diff --git a/.editorconfig b/.editorconfig index b8b05b40aa..e056e37813 100644 --- a/.editorconfig +++ b/.editorconfig @@ -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 \ No newline at end of file diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index e52ede4ef6..470401c082 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -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 diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 6f923b9d16..15698ef646 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -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 diff --git a/.github/workflows/snapshot.yml b/.github/workflows/deploy.yml similarity index 94% rename from .github/workflows/snapshot.yml rename to .github/workflows/deploy.yml index bb1b4127a3..87bf39656b 100644 --- a/.github/workflows/snapshot.yml +++ b/.github/workflows/deploy.yml @@ -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 diff --git a/.github/workflows/duck.yml b/.github/workflows/duck.yml index f0af541969..007d7b6e14 100644 --- a/.github/workflows/duck.yml +++ b/.github/workflows/duck.yml @@ -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 diff --git a/.github/workflows/transifex.yml b/.github/workflows/transifex.yml index 4390bbd85c..68ec73fc3e 100644 --- a/.github/workflows/transifex.yml +++ b/.github/workflows/transifex.yml @@ -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/ diff --git a/CHANGELOG.md b/CHANGELOG.md index bf840cb983..37c331b15c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -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) diff --git a/Cyberduck.xcodeproj/project.pbxproj b/Cyberduck.xcodeproj/project.pbxproj index cb200e0cb4..b955a0fdac 100644 --- a/Cyberduck.xcodeproj/project.pbxproj +++ b/Cyberduck.xcodeproj/project.pbxproj @@ -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 = ""; }; 47D6FFE61CCFA54A005B01C5 /* CDToolbarItem.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = CDToolbarItem.m; path = osx/src/main/objc/CDToolbarItem.m; sourceTree = ""; }; 47D6FFE71CCFA54A005B01C5 /* CDToolbarItem.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = CDToolbarItem.h; path = osx/src/main/objc/CDToolbarItem.h; sourceTree = ""; }; + 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 = ""; }; 47DE47892598F2D300B18BCB /* preferences-ftp.pdf */ = {isa = PBXFileReference; lastKnownFileType = image.pdf; name = "preferences-ftp.pdf"; path = "toolbar/preferences-ftp.pdf"; sourceTree = ""; }; 47DE478A2598F2D300B18BCB /* preferences-connection.pdf */ = {isa = PBXFileReference; lastKnownFileType = image.pdf; name = "preferences-connection.pdf"; path = "toolbar/preferences-connection.pdf"; sourceTree = ""; }; 47DE478B2598F2D300B18BCB /* preferences-browser.pdf */ = {isa = PBXFileReference; lastKnownFileType = image.pdf; name = "preferences-browser.pdf"; path = "toolbar/preferences-browser.pdf"; sourceTree = ""; }; @@ -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 = ""; @@ -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 = ""; }; 4741639206C4F58D00AC0BD2 /* Resources */ = { @@ -2097,6 +2122,15 @@ name = toolbar; sourceTree = ""; }; + 47D9E28D2DF9BB140088B042 /* docktile */ = { + isa = PBXGroup; + children = ( + 47D9E28C2DF9BB140088B042 /* main.swift */, + ); + name = docktile; + path = osx/docktile; + sourceTree = ""; + }; 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 */; diff --git a/Directory.Build.props b/Directory.Build.props index 7cd498cbc8..f237c34ee0 100644 --- a/Directory.Build.props +++ b/Directory.Build.props @@ -23,4 +23,12 @@ + + $(PATH) + + + + + + diff --git a/Directory.Build.targets b/Directory.Build.targets index bb8c9469a1..da8965315f 100644 --- a/Directory.Build.targets +++ b/Directory.Build.targets @@ -18,4 +18,44 @@ + + <_SignToolArgs Condition="'$(SignTool)'=='cng'">_SignToolArgsCNG + <_SignToolArgs Condition="'$(_SignToolArgs)'==''">_SignToolArgsCertificateStore + + $(_SignToolArgs);SignToolExecutablePath + + + + + SignTool.exe + $(WindowsSdk_ExecutablePath);$(SignToolExecutablePath) + + + + + + sign /d "Cyberduck" /fd sha256 /tr "http://timestamp.acs.microsoft.com" /td "sha256" /a + + + + + + + "$(SignToolExecutable)" $(SignToolArgsBase) + + + + + + + + $(SignToolArgsBase) /sm /n "iterate GmbH" + + + + + $(SignToolArgsBase) /f "$(CyberduckDir)setup\cert\certificate.crt" /csp "$(SignToolCSP)" /kc "$(SignToolKC)" + + + \ No newline at end of file diff --git a/Directory.Packages.props b/Directory.Packages.props index b1a9795c8d..a53498443a 100644 --- a/Directory.Packages.props +++ b/Directory.Packages.props @@ -23,7 +23,7 @@ - + @@ -46,7 +46,11 @@ - + + + + + diff --git a/Packages.props b/Packages.props deleted file mode 100644 index 0ca713fed6..0000000000 --- a/Packages.props +++ /dev/null @@ -1,46 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/README.md b/README.md index 2206048659..69fb3159dc 100644 --- a/README.md +++ b/README.md @@ -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..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 diff --git a/azure/pom.xml b/azure/pom.xml index cd018873dc..55c36b94dc 100644 --- a/azure/pom.xml +++ b/azure/pom.xml @@ -18,11 +18,11 @@ ch.cyberduck parent - 9.2.0-SNAPSHOT + 9.4.0-SNAPSHOT azure - 8.6.6 + 12.32.0 @@ -39,8 +39,8 @@ ${project.version} - com.microsoft.azure - azure-storage + com.azure + azure-storage-blob ${azure-storage-version} diff --git a/azure/src/main/java/ch/cyberduck/core/azure/AzureAclPermissionFeature.java b/azure/src/main/java/ch/cyberduck/core/azure/AzureAclPermissionFeature.java index d75ef6e1e8..4e868d0036 100644 --- a/azure/src/main/java/ch/cyberduck/core/azure/AzureAclPermissionFeature.java +++ b/azure/src/main/java/ch/cyberduck/core/azure/AzureAclPermissionFeature.java @@ -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: *

- * 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. *

- * 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. *

* 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 getAvailableAclUsers() { - return new ArrayList(Collections.singletonList( + public List getAvailableAclUsers(final List 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)) { - acl.addAll(new Acl.GroupUser(Acl.GroupUser.EVERYONE, false), new Acl.Role(Acl.Role.READ)); + 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); } } diff --git a/azure/src/main/java/ch/cyberduck/core/azure/AzureAttributesFinderFeature.java b/azure/src/main/java/ch/cyberduck/core/azure/AzureAttributesFinderFeature.java index 1295519de8..3bab27870a 100644 --- a/azure/src/main/java/ch/cyberduck/core/azure/AzureAttributesFinderFeature.java +++ b/azure/src/main/java/ch/cyberduck/core/azure/AzureAttributesFinderFeature.java @@ -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 { +public class AzureAttributesFinderFeature implements AttributesFinder, AttributesAdapter { + 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 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; + } } diff --git a/azure/src/main/java/ch/cyberduck/core/azure/AzureContainerListService.java b/azure/src/main/java/ch/cyberduck/core/azure/AzureContainerListService.java index 1e33c4a01b..45f5dc4421 100644 --- a/azure/src/main/java/ch/cyberduck/core/azure/AzureContainerListService.java +++ b/azure/src/main/java/ch/cyberduck/core/azure/AzureContainerListService.java @@ -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 list(final Path directory, final ListProgressListener listener) throws BackgroundException { - ResultSegment result; - ResultContinuation token = null; try { final AttributedList 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); } } diff --git a/azure/src/main/java/ch/cyberduck/core/azure/AzureCopyFeature.java b/azure/src/main/java/ch/cyberduck/core/azure/AzureCopyFeature.java index e7891df1c5..f0598d3140 100644 --- a/azure/src/main/java/ch/cyberduck/core/azure/AzureCopyFeature.java +++ b/azure/src/main/java/ch/cyberduck/core/azure/AzureCopyFeature.java @@ -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 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 diff --git a/azure/src/main/java/ch/cyberduck/core/azure/AzureDeleteFeature.java b/azure/src/main/java/ch/cyberduck/core/azure/AzureDeleteFeature.java index 4ef935d4b4..8774cbd6fa 100644 --- a/azure/src/main/java/ch/cyberduck/core/azure/AzureDeleteFeature.java +++ b/azure/src/main/java/ch/cyberduck/core/azure/AzureDeleteFeature.java @@ -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 files, final PasswordCallback prompt, final Callback callback) throws BackgroundException { - final List containers = new ArrayList(); + final List 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); - } } } } diff --git a/azure/src/main/java/ch/cyberduck/core/azure/AzureDirectoryFeature.java b/azure/src/main/java/ch/cyberduck/core/azure/AzureDirectoryFeature.java index 56a3172a8e..6507acf7e0 100644 --- a/azure/src/main/java/ch/cyberduck/core/azure/AzureDirectoryFeature.java +++ b/azure/src/main/java/ch/cyberduck/core/azure/AzureDirectoryFeature.java @@ -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 { + private final AzureSession session; private final PathContainerService containerService = new DirectoryDelimiterPathContainerService(); - private final AzureSession session; - private final OperationContext context; - - private Write 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 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 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 { } return true; } - - @Override - public Directory withWriter(final Write writer) { - this.writer = writer; - return this; - } } diff --git a/azure/src/main/java/ch/cyberduck/core/azure/AzureExceptionMappingService.java b/azure/src/main/java/ch/cyberduck/core/azure/AzureExceptionMappingService.java index 98cccb44c0..17ec33295a 100644 --- a/azure/src/main/java/ch/cyberduck/core/azure/AzureExceptionMappingService.java +++ b/azure/src/main/java/ch/cyberduck/core/azure/AzureExceptionMappingService.java @@ -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 { - private static final Logger log = LogManager.getLogger(AzureExceptionMappingService.class); +public class AzureExceptionMappingService extends AbstractExceptionMappingService { @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()); } } diff --git a/azure/src/main/java/ch/cyberduck/core/azure/AzureFindFeature.java b/azure/src/main/java/ch/cyberduck/core/azure/AzureFindFeature.java index 5bab17ff57..f7d3bf5aa2 100644 --- a/azure/src/main/java/ch/cyberduck/core/azure/AzureFindFeature.java +++ b/azure/src/main/java/ch/cyberduck/core/azure/AzureFindFeature.java @@ -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,46 +45,12 @@ public class AzureFindFeature implements Find { return true; } try { - try { - if(containerService.isContainer(file)) { - final CloudBlobContainer container = session.getClient().getContainerReference(containerService.getContainer(file).getName()); - return container.exists(null, null, context); - } - 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()); - 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; + final boolean found; + if(containerService.isContainer(file)) { + return session.getClient().getBlobContainerClient(containerService.getContainer(file).getName()).exists(); } + attributes.find(file, listener); + return true; } catch(NotfoundException e) { return false; diff --git a/azure/src/main/java/ch/cyberduck/core/azure/AzureListService.java b/azure/src/main/java/ch/cyberduck/core/azure/AzureListService.java index ee61da4c84..6b5f180eda 100644 --- a/azure/src/main/java/ch/cyberduck/core/azure/AzureListService.java +++ b/azure/src/main/java/ch/cyberduck/core/azure/AzureListService.java @@ -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 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); } } } diff --git a/azure/src/main/java/ch/cyberduck/core/azure/AzureLoggingFeature.java b/azure/src/main/java/ch/cyberduck/core/azure/AzureLoggingFeature.java index 0015e23242..41673226c3 100644 --- a/azure/src/main/java/ch/cyberduck/core/azure/AzureLoggingFeature.java +++ b/azure/src/main/java/ch/cyberduck/core/azure/AzureLoggingFeature.java @@ -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,19 +59,18 @@ 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)); - } - else { - l.setLogOperationTypes(EnumSet.noneOf(LoggingOperations.class)); - } - properties.setLogging(l); - session.getClient().uploadServiceProperties(properties, null, context); + 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); } - catch(StorageException e) { + catch(HttpResponseException e) { throw new AzureExceptionMappingService().map("Failure to write attributes of {0}", e, container); } } -} \ No newline at end of file +} diff --git a/azure/src/main/java/ch/cyberduck/core/azure/AzureMetadataFeature.java b/azure/src/main/java/ch/cyberduck/core/azure/AzureMetadataFeature.java index 98869d7fd9..2665711fd3 100644 --- a/azure/src/main/java/ch/cyberduck/core/azure/AzureMetadataFeature.java +++ b/azure/src/main/java/ch/cyberduck/core/azure/AzureMetadataFeature.java @@ -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 getDefault() { + public Map getDefault(final Path file) { return HostPreferencesFactory.get(session.getHost()).getMap("azure.metadata.default"); } @@ -65,30 +54,23 @@ public class AzureMetadataFeature implements Headers { public Map 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 metadata = new HashMap(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 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 pruned = new HashMap<>(); for(Map.Entry 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); } } diff --git a/azure/src/main/java/ch/cyberduck/core/azure/AzureMoveFeature.java b/azure/src/main/java/ch/cyberduck/core/azure/AzureMoveFeature.java index 71307c045b..a887c5bcfa 100644 --- a/azure/src/main/java/ch/cyberduck/core/azure/AzureMoveFeature.java +++ b/azure/src/main/java/ch/cyberduck/core/azure/AzureMoveFeature.java @@ -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; } diff --git a/azure/src/main/java/ch/cyberduck/core/azure/AzureObjectListService.java b/azure/src/main/java/ch/cyberduck/core/azure/AzureObjectListService.java index 50a2248087..2fbf35819f 100644 --- a/azure/src/main/java/ch/cyberduck/core/azure/AzureObjectListService.java +++ b/azure/src/main/java/ch/cyberduck/core/azure/AzureObjectListService.java @@ -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 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 children = new AttributedList<>(); - ResultContinuation token = null; - ResultSegment result; + PagedIterable 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 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 types = object instanceof CloudBlobDirectory + final EnumSet 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); - } } } diff --git a/azure/src/main/java/ch/cyberduck/core/azure/AzureProtocol.java b/azure/src/main/java/ch/cyberduck/core/azure/AzureProtocol.java index ad8e937637..cbd9a6e5ae 100644 --- a/azure/src/main/java/ch/cyberduck/core/azure/AzureProtocol.java +++ b/azure/src/main/java/ch/cyberduck/core/azure/AzureProtocol.java @@ -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); } } diff --git a/azure/src/main/java/ch/cyberduck/core/azure/AzureReadFeature.java b/azure/src/main/java/ch/cyberduck/core/azure/AzureReadFeature.java index 8116bb80e8..e0068ea066 100644 --- a/azure/src/main/java/ch/cyberduck/core/azure/AzureReadFeature.java +++ b/azure/src/main/java/ch/cyberduck/core/azure/AzureReadFeature.java @@ -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()); - } - catch(IndexOutOfBoundsException e) { - // If offset is invalid - throw new DefaultExceptionMappingService().map("Download {0} failed", e, file); - } + return session.getClient().getBlobContainerClient(containerService.getContainer(file).getName()) + .getBlobClient(containerService.getKey(file)).openInputStream(new BlobRange(status.getOffset()), new BlobRequestConditions()); + } + 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); - } } } diff --git a/azure/src/main/java/ch/cyberduck/core/azure/AzureSession.java b/azure/src/main/java/ch/cyberduck/core/azure/AzureSession.java index 6d2b7a8068..85e56639e4 100644 --- a/azure/src/main/java/ch/cyberduck/core/azure/AzureSession.java +++ b/azure/src/main/java/ch/cyberduck/core/azure/AzureSession.java @@ -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 { +public class AzureSession extends HttpSession { private static final Logger log = LogManager.getLogger(AzureSession.class); - private final OperationContext context - = new OperationContext(); - - private StorageEvent 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 { 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(); } - @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()); - } - 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() { - @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; + private static final class CredentialsHttpPipelinePolicy implements HttpPipelinePolicy { + private Credentials credentials = new Credentials(); + + public void setCredentials(final Credentials credentials) { + this.credentials = credentials; } - catch(URISyntaxException e) { - throw new LoginFailureException(e.getMessage(), e); + + @Override + public Mono process(final HttpPipelineCallContext context, final HttpPipelineNextPolicy next) { + if(credentials.isTokenAuthentication()) { + return new AzureSasCredentialPolicy(new AzureSasCredential( + credentials.getToken())).process(context, next); + } + 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 _getFeature(final Class 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 process(final HttpPipelineCallContext context, final HttpPipelineNextPolicy next) { + return next.process(); + } + } } diff --git a/azure/src/main/java/ch/cyberduck/core/azure/AzureTouchFeature.java b/azure/src/main/java/ch/cyberduck/core/azure/AzureTouchFeature.java index da0eb43af0..11869f59a4 100644 --- a/azure/src/main/java/ch/cyberduck/core/azure/AzureTouchFeature.java +++ b/azure/src/main/java/ch/cyberduck/core/azure/AzureTouchFeature.java @@ -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 { - 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 { } @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 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))); } } diff --git a/azure/src/main/java/ch/cyberduck/core/azure/AzureUploadFeature.java b/azure/src/main/java/ch/cyberduck/core/azure/AzureUploadFeature.java index 39d8f6af62..b787d8e0f3 100644 --- a/azure/src/main/java/ch/cyberduck/core/azure/AzureUploadFeature.java +++ b/azure/src/main/java/ch/cyberduck/core/azure/AzureUploadFeature.java @@ -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 { 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; } diff --git a/azure/src/main/java/ch/cyberduck/core/azure/AzureUrlProvider.java b/azure/src/main/java/ch/cyberduck/core/azure/AzureUrlProvider.java index 7ba78b3152..a24edb2e62 100644 --- a/azure/src/main/java/ch/cyberduck/core/azure/AzureUrlProvider.java +++ b/azure/src/main/java/ch/cyberduck/core/azure/AzureUrlProvider.java @@ -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,46 +29,58 @@ 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 types) { final DescriptiveUrlBag list = new DescriptiveUrlBag(); - // In one hour - list.add(this.toSignedUrl(file, (int) TimeUnit.HOURS.toSeconds(1))); - // Default signed URL expiring in 24 hours. - list.add(this.toSignedUrl(file, (int) TimeUnit.SECONDS.toSeconds( - HostPreferencesFactory.get(session.getHost()).getInteger("s3.url.expire.seconds")))); - // 1 Week - list.add(this.toSignedUrl(file, (int) TimeUnit.DAYS.toSeconds(7))); - // 1 Month - list.add(this.toSignedUrl(file, (int) TimeUnit.DAYS.toSeconds(30))); - // 1 Year - list.add(this.toSignedUrl(file, (int) TimeUnit.DAYS.toSeconds(365))); + 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. + list.add(this.toSignedUrl(file, (int) TimeUnit.SECONDS.toSeconds( + HostPreferencesFactory.get(session.getHost()).getInteger("s3.url.expire.seconds")))); + // 1 Week + list.add(this.toSignedUrl(file, (int) TimeUnit.DAYS.toSeconds(7))); + // 1 Month + 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()) { - return DescriptiveUrl.EMPTY.getUrl(); + 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"); } - 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); - } - 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; + 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); } @Override public String getHelp() { return MessageFormat.format(LocaleFactory.localizedString("Expires {0}", "S3"), - UserDateFormatterFactory.get().getMediumFormat(expiry.getTimeInMillis())); + UserDateFormatterFactory.get().getMediumFormat(expiry)); } @Override diff --git a/azure/src/main/java/ch/cyberduck/core/azure/AzureWriteFeature.java b/azure/src/main/java/ch/cyberduck/core/azure/AzureWriteFeature.java index 9a50ffbe55..163ca66ea8 100644 --- a/azure/src/main/java/ch/cyberduck/core/azure/AzureWriteFeature.java +++ b/azure/src/main/java/ch/cyberduck/core/azure/AzureWriteFeature.java @@ -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 { 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 { @Override public StatusOutputStream 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 metadata = new HashMap<>(status.getMetadata()); + for(final Map.Entry 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)); + 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: - 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 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); + } + 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 features(final Path file) { - return EnumSet.of(Flags.checksum, Flags.mime); } } diff --git a/azure/src/main/java/ch/cyberduck/core/azure/apache/ApacheHttpClient.java b/azure/src/main/java/ch/cyberduck/core/azure/apache/ApacheHttpClient.java new file mode 100644 index 0000000000..1e2fae41b6 --- /dev/null +++ b/azure/src/main/java/ch/cyberduck/core/azure/apache/ApacheHttpClient.java @@ -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 send(final HttpRequest azureRequest) { + try { + ApacheHttpRequest apacheRequest = new ApacheHttpRequest(azureRequest.getHttpMethod(), azureRequest.getUrl(), + azureRequest.getHeaders()); + + Mono 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()); + } + } + } +} diff --git a/azure/src/main/java/ch/cyberduck/core/azure/apache/ApacheHttpResponse.java b/azure/src/main/java/ch/cyberduck/core/azure/apache/ApacheHttpResponse.java new file mode 100644 index 0000000000..0ef4ffe3cf --- /dev/null +++ b/azure/src/main/java/ch/cyberduck/core/azure/apache/ApacheHttpResponse.java @@ -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 getBody() { + return getBodyAsByteArray().map(ByteBuffer::wrap).flux(); + } + + public Mono getBodyAsByteArray() { + if (null == entity) { + return Mono.empty(); + } + try { + return Mono.just(EntityUtils.toByteArray(entity)); + } catch (IOException e) { + return Mono.error(e); + } + } + + public Mono getBodyAsString() { + return getBodyAsByteArray().map(String::new); + } + + public Mono getBodyAsString(Charset charset) { + return getBodyAsByteArray().map(bytes -> new String(bytes, charset)); + } +} diff --git a/azure/src/test/java/ch/cyberduck/core/azure/AbstractAzureTest.java b/azure/src/test/java/ch/cyberduck/core/azure/AbstractAzureTest.java index 9911084e37..cd7876e88e 100644 --- a/azure/src/test/java/ch/cyberduck/core/azure/AbstractAzureTest.java +++ b/azure/src/test/java/ch/cyberduck/core/azure/AbstractAzureTest.java @@ -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()); } } diff --git a/azure/src/test/java/ch/cyberduck/core/azure/AzureAclPermissionFeatureTest.java b/azure/src/test/java/ch/cyberduck/core/azure/AzureAclPermissionFeatureTest.java index cd37888d15..a3090920c6 100644 --- a/azure/src/test/java/ch/cyberduck/core/azure/AzureAclPermissionFeatureTest.java +++ b/azure/src/test/java/ch/cyberduck/core/azure/AzureAclPermissionFeatureTest.java @@ -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)); + } + } } diff --git a/azure/src/test/java/ch/cyberduck/core/azure/AzureAttributesFinderFeatureTest.java b/azure/src/test/java/ch/cyberduck/core/azure/AzureAttributesFinderFeatureTest.java index f862660d1c..e929d11655 100644 --- a/azure/src/test/java/ch/cyberduck/core/azure/AzureAttributesFinderFeatureTest.java +++ b/azure/src/test/java/ch/cyberduck/core/azure/AzureAttributesFinderFeatureTest.java @@ -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()); } } diff --git a/azure/src/test/java/ch/cyberduck/core/azure/AzureCopyFeatureTest.java b/azure/src/test/java/ch/cyberduck/core/azure/AzureCopyFeatureTest.java index e45b49d5cd..3a217ca299 100644 --- a/azure/src/test/java/ch/cyberduck/core/azure/AzureCopyFeatureTest.java +++ b/azure/src/test/java/ch/cyberduck/core/azure/AzureCopyFeatureTest.java @@ -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()); } } diff --git a/azure/src/test/java/ch/cyberduck/core/azure/AzureDeleteFeatureTest.java b/azure/src/test/java/ch/cyberduck/core/azure/AzureDeleteFeatureTest.java index ddaa7a9994..cc54d1dc93 100644 --- a/azure/src/test/java/ch/cyberduck/core/azure/AzureDeleteFeatureTest.java +++ b/azure/src/test/java/ch/cyberduck/core/azure/AzureDeleteFeatureTest.java @@ -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)); } } diff --git a/azure/src/test/java/ch/cyberduck/core/azure/AzureDirectoryFeatureTest.java b/azure/src/test/java/ch/cyberduck/core/azure/AzureDirectoryFeatureTest.java index d10a7ed4fe..6012559a95 100644 --- a/azure/src/test/java/ch/cyberduck/core/azure/AzureDirectoryFeatureTest.java +++ b/azure/src/test/java/ch/cyberduck/core/azure/AzureDirectoryFeatureTest.java @@ -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()); } } diff --git a/azure/src/test/java/ch/cyberduck/core/azure/AzureFindFeatureTest.java b/azure/src/test/java/ch/cyberduck/core/azure/AzureFindFeatureTest.java index e253ecf22d..00689d7497 100644 --- a/azure/src/test/java/ch/cyberduck/core/azure/AzureFindFeatureTest.java +++ b/azure/src/test/java/ch/cyberduck/core/azure/AzureFindFeatureTest.java @@ -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)))); } } \ No newline at end of file diff --git a/azure/src/test/java/ch/cyberduck/core/azure/AzureLoggingFeatureTest.java b/azure/src/test/java/ch/cyberduck/core/azure/AzureLoggingFeatureTest.java index 1dca5e2df5..39abb86cf5 100644 --- a/azure/src/test/java/ch/cyberduck/core/azure/AzureLoggingFeatureTest.java +++ b/azure/src/test/java/ch/cyberduck/core/azure/AzureLoggingFeatureTest.java @@ -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)); diff --git a/azure/src/test/java/ch/cyberduck/core/azure/AzureMetadataFeatureTest.java b/azure/src/test/java/ch/cyberduck/core/azure/AzureMetadataFeatureTest.java index 726b8ad40a..5dbf88b37f 100644 --- a/azure/src/test/java/ch/cyberduck/core/azure/AzureMetadataFeatureTest.java +++ b/azure/src/test/java/ch/cyberduck/core/azure/AzureMetadataFeatureTest.java @@ -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 metadata = feature.getMetadata(test); + new AzureMetadataFeature(session).setMetadata(test, Collections.singletonMap("Test", v)); + final Map 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 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()); } } diff --git a/azure/src/test/java/ch/cyberduck/core/azure/AzureMoveFeatureTest.java b/azure/src/test/java/ch/cyberduck/core/azure/AzureMoveFeatureTest.java index 16f2b54cbb..250d2edc19 100644 --- a/azure/src/test/java/ch/cyberduck/core/azure/AzureMoveFeatureTest.java +++ b/azure/src/test/java/ch/cyberduck/core/azure/AzureMoveFeatureTest.java @@ -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.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.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))))); } } diff --git a/azure/src/test/java/ch/cyberduck/core/azure/AzureObjectListServiceTest.java b/azure/src/test/java/ch/cyberduck/core/azure/AzureObjectListServiceTest.java index da56dc923b..7451fbe504 100644 --- a/azure/src/test/java/ch/cyberduck/core/azure/AzureObjectListServiceTest.java +++ b/azure/src/test/java/ch/cyberduck/core/azure/AzureObjectListServiceTest.java @@ -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 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 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 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 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 list = new AzureObjectListService(session, null).list(directory, new IndexedListProgressListener() { + final AttributedList 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()); } } diff --git a/azure/src/test/java/ch/cyberduck/core/azure/AzureReadFeatureTest.java b/azure/src/test/java/ch/cyberduck/core/azure/AzureReadFeatureTest.java index aa8184ff6d..e17b063b81 100644 --- a/azure/src/test/java/ch/cyberduck/core/azure/AzureReadFeatureTest.java +++ b/azure/src/test/java/ch/cyberduck/core/azure/AzureReadFeatureTest.java @@ -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()); } } diff --git a/azure/src/test/java/ch/cyberduck/core/azure/AzureSessionTest.java b/azure/src/test/java/ch/cyberduck/core/azure/AzureSessionTest.java index 384b28259e..358b8d3320 100644 --- a/azure/src/test/java/ch/cyberduck/core/azure/AzureSessionTest.java +++ b/azure/src/test/java/ch/cyberduck/core/azure/AzureSessionTest.java @@ -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(), diff --git a/azure/src/test/java/ch/cyberduck/core/azure/AzureTouchFeatureTest.java b/azure/src/test/java/ch/cyberduck/core/azure/AzureTouchFeatureTest.java index d337a3b0f5..7513cd1fa4 100644 --- a/azure/src/test/java/ch/cyberduck/core/azure/AzureTouchFeatureTest.java +++ b/azure/src/test/java/ch/cyberduck/core/azure/AzureTouchFeatureTest.java @@ -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())); diff --git a/azure/src/test/java/ch/cyberduck/core/azure/AzureUrlProviderTest.java b/azure/src/test/java/ch/cyberduck/core/azure/AzureUrlProviderTest.java index 913e5121e8..9bb27abacb 100644 --- a/azure/src/test/java/ch/cyberduck/core/azure/AzureUrlProviderTest.java +++ b/azure/src/test/java/ch/cyberduck/core/azure/AzureUrlProviderTest.java @@ -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()); - } - - @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()); + 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"); + } + }).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()); } } diff --git a/azure/src/test/java/ch/cyberduck/core/azure/AzureWriteFeatureTest.java b/azure/src/test/java/ch/cyberduck/core/azure/AzureWriteFeatureTest.java index 4ae8e1faef..6155eee928 100644 --- a/azure/src/test/java/ch/cyberduck/core/azure/AzureWriteFeatureTest.java +++ b/azure/src/test/java/ch/cyberduck/core/azure/AzureWriteFeatureTest.java @@ -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 metadata = new AzureMetadataFeature(session, context).getMetadata(test); + final Map 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 metadata = new AzureMetadataFeature(session, context).getMetadata(test); + final Map 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()); } } diff --git a/azure/src/test/java/ch/cyberduck/core/cryptomator/AzureDirectoryFeatureTest.java b/azure/src/test/java/ch/cyberduck/core/cryptomator/AzureDirectoryFeatureTest.java index 441bb645c9..72f478660b 100644 --- a/azure/src/test/java/ch/cyberduck/core/cryptomator/AzureDirectoryFeatureTest.java +++ b/azure/src/test/java/ch/cyberduck/core/cryptomator/AzureDirectoryFeatureTest.java @@ -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()); } } diff --git a/azure/src/test/java/ch/cyberduck/core/cryptomator/AzureListServiceTest.java b/azure/src/test/java/ch/cyberduck/core/cryptomator/AzureListServiceTest.java index 559ba25e8e..174e34b260 100644 --- a/azure/src/test/java/ch/cyberduck/core/cryptomator/AzureListServiceTest.java +++ b/azure/src/test/java/ch/cyberduck/core/cryptomator/AzureListServiceTest.java @@ -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 list, final Optional e) { - assertEquals(vault, directory); - for(Path f : list) { - assertTrue(f.getType().contains(Path.Type.decrypted)); - } - } - - @Override - public void chunk(final Path directory, final AttributedList list) { - assertEquals(vault, directory); - for(Path f : list) { - assertTrue(f.getType().contains(Path.Type.decrypted)); - } - } - }).get(0)); - { - final Cache 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 cache = new PathCache(1); - assertTrue(new CachingFindFeature(session, cache, new CryptoFindFeature(session, new DefaultFindFeature(session), cryptomator)).find(test)); - assertTrue(cache.isCached(vault)); - final AttributedList 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(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()); } } diff --git a/azure/src/test/java/ch/cyberduck/core/cryptomator/AzureMoveFeatureTest.java b/azure/src/test/java/ch/cyberduck/core/cryptomator/AzureMoveFeatureTest.java index 7ddc360307..3aa606d75f 100644 --- a/azure/src/test/java/ch/cyberduck/core/cryptomator/AzureMoveFeatureTest.java +++ b/azure/src/test/java/ch/cyberduck/core/cryptomator/AzureMoveFeatureTest.java @@ -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(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()); } } diff --git a/azure/src/test/java/ch/cyberduck/core/cryptomator/AzureTouchFeatureTest.java b/azure/src/test/java/ch/cyberduck/core/cryptomator/AzureTouchFeatureTest.java index 2a00e512e7..b8a257cd4d 100644 --- a/azure/src/test/java/ch/cyberduck/core/cryptomator/AzureTouchFeatureTest.java +++ b/azure/src/test/java/ch/cyberduck/core/cryptomator/AzureTouchFeatureTest.java @@ -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()); } } diff --git a/azure/src/test/java/ch/cyberduck/core/cryptomator/AzureWriteFeatureTest.java b/azure/src/test/java/ch/cyberduck/core/cryptomator/AzureWriteFeatureTest.java index 910c30a75d..a11d3355b9 100644 --- a/azure/src/test/java/ch/cyberduck/core/cryptomator/AzureWriteFeatureTest.java +++ b/azure/src/test/java/ch/cyberduck/core/cryptomator/AzureWriteFeatureTest.java @@ -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 writer = new CryptoWriteFeature<>(session, new AzureWriteFeature(session, null), cryptomator); + final CryptoWriteFeature 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()); } } diff --git a/azure/src/test/java/ch/cyberduck/core/cryptomator/CopyWorkerTest.java b/azure/src/test/java/ch/cyberduck/core/cryptomator/CopyWorkerTest.java index 5c63dea021..1e3bc02b56 100644 --- a/azure/src/test/java/ch/cyberduck/core/cryptomator/CopyWorkerTest.java +++ b/azure/src/test/java/ch/cyberduck/core/cryptomator/CopyWorkerTest.java @@ -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(session, new DefaultTouchFeature(new AzureWriteFeature(session, null)), new AzureWriteFeature(session, null), cryptomator).touch(source, new TransferStatus()); + new CryptoTouchFeature<>(session, new DefaultTouchFeature(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(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(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(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(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); } diff --git a/azure/src/test/java/ch/cyberduck/core/cryptomator/CryptoAzureSingleTransferWorkerTest.java b/azure/src/test/java/ch/cyberduck/core/cryptomator/CryptoAzureSingleTransferWorkerTest.java index 22c656ca57..4ac361ed8c 100644 --- a/azure/src/test/java/ch/cyberduck/core/cryptomator/CryptoAzureSingleTransferWorkerTest.java +++ b/azure/src/test/java/ch/cyberduck/core/cryptomator/CryptoAzureSingleTransferWorkerTest.java @@ -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(); diff --git a/azure/src/test/java/ch/cyberduck/core/worker/AzureSingleTransferWorkerTest.java b/azure/src/test/java/ch/cyberduck/core/worker/AzureSingleTransferWorkerTest.java index 2860fc5dd8..78cdd6bf7d 100644 --- a/azure/src/test/java/ch/cyberduck/core/worker/AzureSingleTransferWorkerTest.java +++ b/azure/src/test/java/ch/cyberduck/core/worker/AzureSingleTransferWorkerTest.java @@ -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 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 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(); } } diff --git a/azure/src/test/java/ch/cyberduck/core/worker/CopyWorkerTest.java b/azure/src/test/java/ch/cyberduck/core/worker/CopyWorkerTest.java index 7f077ec46b..8d2fc567ba 100644 --- a/azure/src/test/java/ch/cyberduck/core/worker/CopyWorkerTest.java +++ b/azure/src/test/java/ch/cyberduck/core/worker/CopyWorkerTest.java @@ -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); } } diff --git a/backblaze/pom.xml b/backblaze/pom.xml index adcfeda634..1b29c741a4 100644 --- a/backblaze/pom.xml +++ b/backblaze/pom.xml @@ -19,13 +19,13 @@ parent ch.cyberduck - 9.2.0-SNAPSHOT + 9.4.0-SNAPSHOT backblaze jar - 2.1.0 + 2.2.0 @@ -33,7 +33,7 @@ org.json json - 20250517 + 20251224 diff --git a/backblaze/src/main/java/ch/cyberduck/core/b2/B2AttributesFinderFeature.java b/backblaze/src/main/java/ch/cyberduck/core/b2/B2AttributesFinderFeature.java index f547c1c361..c578c4536a 100644 --- a/backblaze/src/main/java/ch/cyberduck/core/b2/B2AttributesFinderFeature.java +++ b/backblaze/src/main/java/ch/cyberduck/core/b2/B2AttributesFinderFeature.java @@ -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))); diff --git a/backblaze/src/main/java/ch/cyberduck/core/b2/B2BucketTypeAclFeature.java b/backblaze/src/main/java/ch/cyberduck/core/b2/B2BucketTypeAclFeature.java new file mode 100644 index 0000000000..afcc968c98 --- /dev/null +++ b/backblaze/src/main/java/ch/cyberduck/core/b2/B2BucketTypeAclFeature.java @@ -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 getAvailableAclUsers(final List files) { + return new ArrayList<>(Collections.singletonList( + new Acl.GroupUser(Acl.GroupUser.EVERYONE, false)) + ); + } + + @Override + public List getAvailableAclRoles(final List files) { + return Collections.singletonList( + new Acl.Role(Permission.PERMISSION_READ.toString())); + } +} diff --git a/backblaze/src/main/java/ch/cyberduck/core/b2/B2BucketTypeFeature.java b/backblaze/src/main/java/ch/cyberduck/core/b2/B2BucketTypeFeature.java index c4c9a2d3ef..580b2bc877 100644 --- a/backblaze/src/main/java/ch/cyberduck/core/b2/B2BucketTypeFeature.java +++ b/backblaze/src/main/java/ch/cyberduck/core/b2/B2BucketTypeFeature.java @@ -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 getAvailableAclUsers() { - return new ArrayList<>(Collections.singletonList( - new Acl.GroupUser(Acl.GroupUser.EVERYONE, false)) - ); - } - - @Override - public List getAvailableAclRoles(final List 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 getLocations() { + public Set getLocations(final Path file) { final Set types = new LinkedHashSet<>(); types.add(new B2BucketTypeName(BucketType.allPrivate)); types.add(new B2BucketTypeName(BucketType.allPublic)); diff --git a/backblaze/src/main/java/ch/cyberduck/core/b2/B2DirectoryFeature.java b/backblaze/src/main/java/ch/cyberduck/core/b2/B2DirectoryFeature.java index 5eb019be63..5dfa61b2b7 100644 --- a/backblaze/src/main/java/ch/cyberduck/core/b2/B2DirectoryFeature.java +++ b/backblaze/src/main/java/ch/cyberduck/core/b2/B2DirectoryFeature.java @@ -46,24 +46,18 @@ public class B2DirectoryFeature implements Directory { private final B2Session session; private final B2VersionIdProvider fileid; - private Write 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 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 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 { else { final EnumSet 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 { } } } - - @Override - public B2DirectoryFeature withWriter(final Write writer) { - this.writer = writer; - return this; - } } diff --git a/backblaze/src/main/java/ch/cyberduck/core/b2/B2LargeCopyFeature.java b/backblaze/src/main/java/ch/cyberduck/core/b2/B2LargeCopyFeature.java index 9e660a402b..2df657ff8b 100644 --- a/backblaze/src/main/java/ch/cyberduck/core/b2/B2LargeCopyFeature.java +++ b/backblaze/src/main/java/ch/cyberduck/core/b2/B2LargeCopyFeature.java @@ -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); diff --git a/backblaze/src/main/java/ch/cyberduck/core/b2/B2LargeUploadService.java b/backblaze/src/main/java/ch/cyberduck/core/b2/B2LargeUploadService.java index c08f460563..c69873f8da 100644 --- a/backblaze/src/main/java/ch/cyberduck/core/b2/B2LargeUploadService.java +++ b/backblaze/src/main/java/ch/cyberduck/core/b2/B2LargeUploadService.java @@ -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 writer; - - public B2LargeUploadService(final B2Session session, final B2VersionIdProvider fileid, final Write 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 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 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 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 submit(final ThreadPool pool, final Path file, final Local local, + private Future submit(final ThreadPool pool, final Write 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 withWriter(final Write writer) { - this.writer = writer; - return super.withWriter(writer); - } } diff --git a/backblaze/src/main/java/ch/cyberduck/core/b2/B2ListService.java b/backblaze/src/main/java/ch/cyberduck/core/b2/B2ListService.java index 02daf2c041..547e8a027e 100644 --- a/backblaze/src/main/java/ch/cyberduck/core/b2/B2ListService.java +++ b/backblaze/src/main/java/ch/cyberduck/core/b2/B2ListService.java @@ -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 buckets; public B2ListService(final B2Session session, final B2VersionIdProvider fileid) { + this(session, fileid, AttributedList.EMPTY); + } + + public B2ListService(final B2Session session, final B2VersionIdProvider fileid, final AttributedList buckets) { this.session = session; this.fileid = fileid; + this.buckets = buckets; } @Override public AttributedList list(final Path directory, final ListProgressListener listener) throws BackgroundException { if(directory.isRoot()) { - if(bucket != null) { - final AttributedList buckets = new AttributedList<>(Collections.singleton(bucket)); - listener.chunk(directory, buckets); - return buckets; + if(buckets.isEmpty()) { + return new B2BucketListService(session, fileid).list(directory, listener); } - return new B2BucketListService(session, fileid).list(directory, listener); + listener.chunk(directory, buckets); + return buckets; } 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; - } } diff --git a/backblaze/src/main/java/ch/cyberduck/core/b2/B2MetadataFeature.java b/backblaze/src/main/java/ch/cyberduck/core/b2/B2MetadataFeature.java index befe4be549..2a7d550f8a 100644 --- a/backblaze/src/main/java/ch/cyberduck/core/b2/B2MetadataFeature.java +++ b/backblaze/src/main/java/ch/cyberduck/core/b2/B2MetadataFeature.java @@ -38,7 +38,7 @@ public class B2MetadataFeature implements Headers { } @Override - public Map getDefault() { + public Map getDefault(final Path file) { return HostPreferencesFactory.get(session.getHost()).getMap("b2.metadata.default"); } diff --git a/backblaze/src/main/java/ch/cyberduck/core/b2/B2MoveFeature.java b/backblaze/src/main/java/ch/cyberduck/core/b2/B2MoveFeature.java index ee402fbeec..f7536e1f46 100644 --- a/backblaze/src/main/java/ch/cyberduck/core/b2/B2MoveFeature.java +++ b/backblaze/src/main/java/ch/cyberduck/core/b2/B2MoveFeature.java @@ -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; } diff --git a/backblaze/src/main/java/ch/cyberduck/core/b2/B2ObjectListService.java b/backblaze/src/main/java/ch/cyberduck/core/b2/B2ObjectListService.java index 032447a661..196c03d85c 100644 --- a/backblaze/src/main/java/ch/cyberduck/core/b2/B2ObjectListService.java +++ b/backblaze/src/main/java/ch/cyberduck/core/b2/B2ObjectListService.java @@ -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; diff --git a/backblaze/src/main/java/ch/cyberduck/core/b2/B2Protocol.java b/backblaze/src/main/java/ch/cyberduck/core/b2/B2Protocol.java index 56fea99787..5ef12cd843 100644 --- a/backblaze/src/main/java/ch/cyberduck/core/b2/B2Protocol.java +++ b/backblaze/src/main/java/ch/cyberduck/core/b2/B2Protocol.java @@ -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); } diff --git a/backblaze/src/main/java/ch/cyberduck/core/b2/B2SearchFeature.java b/backblaze/src/main/java/ch/cyberduck/core/b2/B2SearchFeature.java index 425061d0fa..3e3e3d5704 100644 --- a/backblaze/src/main/java/ch/cyberduck/core/b2/B2SearchFeature.java +++ b/backblaze/src/main/java/ch/cyberduck/core/b2/B2SearchFeature.java @@ -91,7 +91,7 @@ public class B2SearchFeature implements Search { } @Override - public EnumSet features() { + public EnumSet features(final Path workdir) { return EnumSet.of(Flags.recursive); } } diff --git a/backblaze/src/main/java/ch/cyberduck/core/b2/B2Session.java b/backblaze/src/main/java/ch/cyberduck/core/b2/B2Session.java index a44ab018f7..e4acbe840a 100644 --- a/backblaze/src/main/java/ch/cyberduck/core/b2/B2Session.java +++ b/backblaze/src/main/java/ch/cyberduck/core/b2/B2Session.java @@ -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 { 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 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 { 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 { // 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 { @SuppressWarnings("unchecked") public T _getFeature(final Class 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 { 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); diff --git a/backblaze/src/main/java/ch/cyberduck/core/b2/B2SingleUploadService.java b/backblaze/src/main/java/ch/cyberduck/core/b2/B2SingleUploadService.java index c7c5fe526c..7129e48ccf 100644 --- a/backblaze/src/main/java/ch/cyberduck/core/b2/B2SingleUploadService.java +++ b/backblaze/src/main/java/ch/cyberduck/core/b2/B2SingleUploadService.java @@ -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 writer) { - super(writer); + public B2SingleUploadService(final B2Session session) { this.session = session; } diff --git a/backblaze/src/main/java/ch/cyberduck/core/b2/B2ThresholdUploadService.java b/backblaze/src/main/java/ch/cyberduck/core/b2/B2ThresholdUploadService.java index fd666bdee9..b7bfdcaf68 100644 --- a/backblaze/src/main/java/ch/cyberduck/core/b2/B2ThresholdUploadService.java +++ b/backblaze/src/main/java/ch/cyberduck/core/b2/B2ThresholdUploadService.java @@ -37,7 +37,6 @@ public class B2ThresholdUploadService implements Upload { private final B2Session session; private final B2VersionIdProvider fileid; - private Write writer; private final Long threshold; public B2ThresholdUploadService(final B2Session session, final B2VersionIdProvider fileid) { @@ -47,35 +46,28 @@ public class B2ThresholdUploadService implements Upload { 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 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 withWriter(final Write 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")) { diff --git a/backblaze/src/main/java/ch/cyberduck/core/b2/B2TouchFeature.java b/backblaze/src/main/java/ch/cyberduck/core/b2/B2TouchFeature.java index cb88113e1b..f20a84a429 100644 --- a/backblaze/src/main/java/ch/cyberduck/core/b2/B2TouchFeature.java +++ b/backblaze/src/main/java/ch/cyberduck/core/b2/B2TouchFeature.java @@ -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 { 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 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 diff --git a/backblaze/src/main/java/ch/cyberduck/core/b2/B2VersioningFeature.java b/backblaze/src/main/java/ch/cyberduck/core/b2/B2VersioningFeature.java index 219a38d7fc..fce6303e02 100644 --- a/backblaze/src/main/java/ch/cyberduck/core/b2/B2VersioningFeature.java +++ b/backblaze/src/main/java/ch/cyberduck/core/b2/B2VersioningFeature.java @@ -77,7 +77,7 @@ public class B2VersioningFeature implements Versioning { return new B2ObjectListService(session, fileid).list(file, listener).filter(new NullFilter() { @Override public boolean accept(final Path f) { - return f.attributes().isDuplicate(); + return f.attributes().isDuplicate() || f.attributes().isTrashed(); } }); } diff --git a/backblaze/src/test/java/ch/cyberduck/core/b2/B2AttributesFinderFeatureTest.java b/backblaze/src/test/java/ch/cyberduck/core/b2/B2AttributesFinderFeatureTest.java index c818032659..8584603fc2 100644 --- a/backblaze/src/test/java/ch/cyberduck/core/b2/B2AttributesFinderFeatureTest.java +++ b/backblaze/src/test/java/ch/cyberduck/core/b2/B2AttributesFinderFeatureTest.java @@ -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 diff --git a/backblaze/src/test/java/ch/cyberduck/core/b2/B2AuthorizedUrlProviderTest.java b/backblaze/src/test/java/ch/cyberduck/core/b2/B2AuthorizedUrlProviderTest.java index 27c2cc6b62..ba66da23d9 100644 --- a/backblaze/src/test/java/ch/cyberduck/core/b2/B2AuthorizedUrlProviderTest.java +++ b/backblaze/src/test/java/ch/cyberduck/core/b2/B2AuthorizedUrlProviderTest.java @@ -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()); diff --git a/backblaze/src/test/java/ch/cyberduck/core/b2/B2BucketTypeFeatureTest.java b/backblaze/src/test/java/ch/cyberduck/core/b2/B2BucketTypeFeatureTest.java index ae3f8fc810..391b60f5c8 100644 --- a/backblaze/src/test/java/ch/cyberduck/core/b2/B2BucketTypeFeatureTest.java +++ b/backblaze/src/test/java/ch/cyberduck/core/b2/B2BucketTypeFeatureTest.java @@ -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()); diff --git a/backblaze/src/test/java/ch/cyberduck/core/b2/B2CopyFeatureTest.java b/backblaze/src/test/java/ch/cyberduck/core/b2/B2CopyFeatureTest.java index 79ef6a24bb..77e288c830 100644 --- a/backblaze/src/test/java/ch/cyberduck/core/b2/B2CopyFeatureTest.java +++ b/backblaze/src/test/java/ch/cyberduck/core/b2/B2CopyFeatureTest.java @@ -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()); diff --git a/backblaze/src/test/java/ch/cyberduck/core/b2/B2DeleteFeatureTest.java b/backblaze/src/test/java/ch/cyberduck/core/b2/B2DeleteFeatureTest.java index ee3767229b..ef70e9ec5a 100644 --- a/backblaze/src/test/java/ch/cyberduck/core/b2/B2DeleteFeatureTest.java +++ b/backblaze/src/test/java/ch/cyberduck/core/b2/B2DeleteFeatureTest.java @@ -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)); diff --git a/backblaze/src/test/java/ch/cyberduck/core/b2/B2DirectoryFeatureTest.java b/backblaze/src/test/java/ch/cyberduck/core/b2/B2DirectoryFeatureTest.java index 060ab8610b..ca8ddf144e 100644 --- a/backblaze/src/test/java/ch/cyberduck/core/b2/B2DirectoryFeatureTest.java +++ b/backblaze/src/test/java/ch/cyberduck/core/b2/B2DirectoryFeatureTest.java @@ -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()); } diff --git a/backblaze/src/test/java/ch/cyberduck/core/b2/B2FindFeatureTest.java b/backblaze/src/test/java/ch/cyberduck/core/b2/B2FindFeatureTest.java index b4c3e873a1..ab67eca2d0 100644 --- a/backblaze/src/test/java/ch/cyberduck/core/b2/B2FindFeatureTest.java +++ b/backblaze/src/test/java/ch/cyberduck/core/b2/B2FindFeatureTest.java @@ -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)); diff --git a/backblaze/src/test/java/ch/cyberduck/core/b2/B2LargeCopyFeatureTest.java b/backblaze/src/test/java/ch/cyberduck/core/b2/B2LargeCopyFeatureTest.java index 0db8bd39c9..c7d2904fa7 100644 --- a/backblaze/src/test/java/ch/cyberduck/core/b2/B2LargeCopyFeatureTest.java +++ b/backblaze/src/test/java/ch/cyberduck/core/b2/B2LargeCopyFeatureTest.java @@ -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, diff --git a/backblaze/src/test/java/ch/cyberduck/core/b2/B2LargeUploadServiceTest.java b/backblaze/src/test/java/ch/cyberduck/core/b2/B2LargeUploadServiceTest.java index 59bf614d06..1019eb0d75 100644 --- a/backblaze/src/test/java/ch/cyberduck/core/b2/B2LargeUploadServiceTest.java +++ b/backblaze/src/test/java/ch/cyberduck/core/b2/B2LargeUploadServiceTest.java @@ -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 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 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()); diff --git a/backblaze/src/test/java/ch/cyberduck/core/b2/B2LifecycleFeatureTest.java b/backblaze/src/test/java/ch/cyberduck/core/b2/B2LifecycleFeatureTest.java index 09bfe52441..c26638163d 100644 --- a/backblaze/src/test/java/ch/cyberduck/core/b2/B2LifecycleFeatureTest.java +++ b/backblaze/src/test/java/ch/cyberduck/core/b2/B2LifecycleFeatureTest.java @@ -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); diff --git a/backblaze/src/test/java/ch/cyberduck/core/b2/B2MoveFeatureTest.java b/backblaze/src/test/java/ch/cyberduck/core/b2/B2MoveFeatureTest.java index 20fd6f1b73..2180d19e64 100644 --- a/backblaze/src/test/java/ch/cyberduck/core/b2/B2MoveFeatureTest.java +++ b/backblaze/src/test/java/ch/cyberduck/core/b2/B2MoveFeatureTest.java @@ -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.singletonList(target), new DisabledLoginCallback(), new Delete.DisabledCallback()); } } diff --git a/backblaze/src/test/java/ch/cyberduck/core/b2/B2ObjectListServiceTest.java b/backblaze/src/test/java/ch/cyberduck/core/b2/B2ObjectListServiceTest.java index b9b1efb02b..4233aea04b 100644 --- a/backblaze/src/test/java/ch/cyberduck/core/b2/B2ObjectListServiceTest.java +++ b/backblaze/src/test/java/ch/cyberduck/core/b2/B2ObjectListServiceTest.java @@ -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 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 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 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 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 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 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 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 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 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 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 list = new B2ObjectListService(session, fileid).list(directory, new IndexedListProgressListener() { diff --git a/backblaze/src/test/java/ch/cyberduck/core/b2/B2ReadFeatureTest.java b/backblaze/src/test/java/ch/cyberduck/core/b2/B2ReadFeatureTest.java index 702c9445e8..f95eb2da40 100644 --- a/backblaze/src/test/java/ch/cyberduck/core/b2/B2ReadFeatureTest.java +++ b/backblaze/src/test/java/ch/cyberduck/core/b2/B2ReadFeatureTest.java @@ -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 diff --git a/backblaze/src/test/java/ch/cyberduck/core/b2/B2SearchFeatureTest.java b/backblaze/src/test/java/ch/cyberduck/core/b2/B2SearchFeatureTest.java index 3b778dc1df..333889cbe2 100644 --- a/backblaze/src/test/java/ch/cyberduck/core/b2/B2SearchFeatureTest.java +++ b/backblaze/src/test/java/ch/cyberduck/core/b2/B2SearchFeatureTest.java @@ -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 result = feature.search(workdir, new SearchFilter(filesubdir.getName()), new DisabledListProgressListener()); assertNotNull(result.find(new SimplePathPredicate(filesubdir))); diff --git a/backblaze/src/test/java/ch/cyberduck/core/b2/B2SingleUploadServiceTest.java b/backblaze/src/test/java/ch/cyberduck/core/b2/B2SingleUploadServiceTest.java index 7b05c43cff..f25c486469 100644 --- a/backblaze/src/test/java/ch/cyberduck/core/b2/B2SingleUploadServiceTest.java +++ b/backblaze/src/test/java/ch/cyberduck/core/b2/B2SingleUploadServiceTest.java @@ -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(); diff --git a/backblaze/src/test/java/ch/cyberduck/core/b2/B2UrlProviderTest.java b/backblaze/src/test/java/ch/cyberduck/core/b2/B2UrlProviderTest.java index fe5e090226..2a594bb963 100644 --- a/backblaze/src/test/java/ch/cyberduck/core/b2/B2UrlProviderTest.java +++ b/backblaze/src/test/java/ch/cyberduck/core/b2/B2UrlProviderTest.java @@ -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()); diff --git a/backblaze/src/test/java/ch/cyberduck/core/b2/B2VersionIdProviderTest.java b/backblaze/src/test/java/ch/cyberduck/core/b2/B2VersionIdProviderTest.java index 2b80ae2022..e36199cc3b 100644 --- a/backblaze/src/test/java/ch/cyberduck/core/b2/B2VersionIdProviderTest.java +++ b/backblaze/src/test/java/ch/cyberduck/core/b2/B2VersionIdProviderTest.java @@ -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()); diff --git a/backblaze/src/test/java/ch/cyberduck/core/b2/B2VersioningFeatureTest.java b/backblaze/src/test/java/ch/cyberduck/core/b2/B2VersioningFeatureTest.java index 2bf787c0b7..2e70078b91 100644 --- a/backblaze/src/test/java/ch/cyberduck/core/b2/B2VersioningFeatureTest.java +++ b/backblaze/src/test/java/ch/cyberduck/core/b2/B2VersioningFeatureTest.java @@ -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(); diff --git a/backblaze/src/test/java/ch/cyberduck/core/cryptomator/B2DirectoryFeatureTest.java b/backblaze/src/test/java/ch/cyberduck/core/cryptomator/B2DirectoryFeatureTest.java index bdefc316aa..c3207396c4 100644 --- a/backblaze/src/test/java/ch/cyberduck/core/cryptomator/B2DirectoryFeatureTest.java +++ b/backblaze/src/test/java/ch/cyberduck/core/cryptomator/B2DirectoryFeatureTest.java @@ -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()); } diff --git a/backblaze/src/test/java/ch/cyberduck/core/cryptomator/B2LargeUploadServiceTest.java b/backblaze/src/test/java/ch/cyberduck/core/cryptomator/B2LargeUploadServiceTest.java index 891f6ea1cf..868c5e95af 100644 --- a/backblaze/src/test/java/ch/cyberduck/core/cryptomator/B2LargeUploadServiceTest.java +++ b/backblaze/src/test/java/ch/cyberduck/core/cryptomator/B2LargeUploadServiceTest.java @@ -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 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> bulk = new CryptoBulkFeature<>(session, new DisabledBulkFeature(), new B2DeleteFeature(session, fileid), cryptomator); + final CryptoBulkFeature> 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 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)); diff --git a/backblaze/src/test/java/ch/cyberduck/core/cryptomator/B2ListServiceTest.java b/backblaze/src/test/java/ch/cyberduck/core/cryptomator/B2ListServiceTest.java index aad78b76fe..4e67fcf8ad 100644 --- a/backblaze/src/test/java/ch/cyberduck/core/cryptomator/B2ListServiceTest.java +++ b/backblaze/src/test/java/ch/cyberduck/core/cryptomator/B2ListServiceTest.java @@ -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(session, new DefaultTouchFeature(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( + 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()); diff --git a/backblaze/src/test/java/ch/cyberduck/core/cryptomator/B2TouchFeatureTest.java b/backblaze/src/test/java/ch/cyberduck/core/cryptomator/B2TouchFeatureTest.java index 215c66c593..c2744bcfc2 100644 --- a/backblaze/src/test/java/ch/cyberduck/core/cryptomator/B2TouchFeatureTest.java +++ b/backblaze/src/test/java/ch/cyberduck/core/cryptomator/B2TouchFeatureTest.java @@ -27,6 +27,7 @@ import ch.cyberduck.core.b2.B2TouchFeature; import ch.cyberduck.core.b2.B2VersionIdProvider; import ch.cyberduck.core.b2.B2WriteFeature; import ch.cyberduck.core.cryptomator.features.CryptoTouchFeature; +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; @@ -46,6 +47,8 @@ import java.util.Arrays; import java.util.EnumSet; import java.util.UUID; +import synapticloop.b2.response.BaseB2Response; + import static org.junit.Assert.*; import static org.junit.Assume.assumeTrue; @@ -62,8 +65,8 @@ public class B2TouchFeatureTest extends AbstractB2Test { session.withRegistry(new DefaultVaultRegistry(new DisabledPasswordCallback(), cryptomator)); final B2VersionIdProvider fileid = new B2VersionIdProvider(session); final TransferStatus status = new TransferStatus(); - final Path test = new CryptoTouchFeature<>(session, new B2TouchFeature(session, fileid), new B2WriteFeature(session, fileid), cryptomator).touch( - new Path(vault, UUID.randomUUID().toString(), EnumSet.of(Path.Type.file)), status); + final Path test = new CryptoTouchFeature<>(session, new B2TouchFeature(session, fileid), cryptomator).touch( + new CryptoWriteFeature<>(session, new B2WriteFeature(session, fileid), cryptomator), new Path(vault, UUID.randomUUID().toString(), EnumSet.of(Path.Type.file)), status); assertEquals(0L, test.attributes().getSize()); assertEquals(0L, status.getResponse().getSize()); assertNotNull(test.attributes().getVersionId()); @@ -82,8 +85,8 @@ public class B2TouchFeatureTest extends AbstractB2Test { session.withRegistry(new DefaultVaultRegistry(new DisabledPasswordCallback(), cryptomator)); final B2VersionIdProvider fileid = new B2VersionIdProvider(session); final TransferStatus status = new TransferStatus(); - final Path test = new CryptoTouchFeature<>(session, new B2TouchFeature(session, fileid), new B2WriteFeature(session, fileid), cryptomator).touch( - new Path(vault, new AlphanumericRandomStringService(130).random(), EnumSet.of(Path.Type.file)), status); + final Path test = new CryptoTouchFeature<>(session, new B2TouchFeature(session, fileid), cryptomator).touch( + new CryptoWriteFeature<>(session, new B2WriteFeature(session, fileid), cryptomator), new Path(vault, new AlphanumericRandomStringService(130).random(), EnumSet.of(Path.Type.file)), status); assertEquals(0L, test.attributes().getSize()); assertEquals(0L, status.getResponse().getSize()); assertTrue(cryptomator.getFeature(session, Find.class, new B2FindFeature(session, fileid)).find(test)); @@ -99,8 +102,8 @@ public class B2TouchFeatureTest extends AbstractB2Test { session.withRegistry(new DefaultVaultRegistry(new DisabledPasswordCallback(), cryptomator)); final B2VersionIdProvider fileid = new B2VersionIdProvider(session); final TransferStatus status = new TransferStatus(); - final Path test = new CryptoTouchFeature<>(session, new DefaultTouchFeature<>(new B2WriteFeature(session, fileid)), new B2WriteFeature(session, fileid), cryptomator).touch( - new Path(vault, UUID.randomUUID().toString(), EnumSet.of(Path.Type.file)), status); + final Path test = new CryptoTouchFeature<>(session, new DefaultTouchFeature(session), cryptomator).touch( + new CryptoWriteFeature<>(session, new B2WriteFeature(session, fileid), cryptomator), new Path(vault, UUID.randomUUID().toString(), EnumSet.of(Path.Type.file)), status); assertEquals(0L, test.attributes().getSize()); assertEquals(0L, status.getResponse().getSize()); assertNotNull(test.attributes().getVersionId()); diff --git a/backblaze/src/test/java/ch/cyberduck/core/cryptomator/CopyWorkerTest.java b/backblaze/src/test/java/ch/cyberduck/core/cryptomator/CopyWorkerTest.java index 32bf74ace1..d54dede840 100644 --- a/backblaze/src/test/java/ch/cyberduck/core/cryptomator/CopyWorkerTest.java +++ b/backblaze/src/test/java/ch/cyberduck/core/cryptomator/CopyWorkerTest.java @@ -23,7 +23,6 @@ import ch.cyberduck.core.DisabledProgressListener; import ch.cyberduck.core.Path; import ch.cyberduck.core.PathCache; import ch.cyberduck.core.b2.AbstractB2Test; -import ch.cyberduck.core.b2.B2DeleteFeature; import ch.cyberduck.core.b2.B2DirectoryFeature; import ch.cyberduck.core.b2.B2FindFeature; import ch.cyberduck.core.b2.B2ReadFeature; @@ -36,6 +35,7 @@ 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.StreamCopier; import ch.cyberduck.core.pool.SessionPool; import ch.cyberduck.core.shared.DefaultFindFeature; @@ -63,6 +63,8 @@ import java.util.Arrays; import java.util.Collections; import java.util.EnumSet; +import synapticloop.b2.response.BaseB2Response; + import static org.junit.Assert.*; import static org.junit.Assume.assumeTrue; @@ -83,7 +85,7 @@ public class CopyWorkerTest extends AbstractB2Test { final byte[] content = RandomUtils.nextBytes(40500); final TransferStatus status = new TransferStatus(); final B2VersionIdProvider fileid = new B2VersionIdProvider(session); - new CryptoBulkFeature<>(session, new DisabledBulkFeature(), new B2DeleteFeature(session, fileid), cryptomator).pre(Transfer.Type.upload, Collections.singletonMap(new TransferItem(source), status), new DisabledConnectionCallback()); + new CryptoBulkFeature<>(session, new DisabledBulkFeature(), 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 B2WriteFeature(session, fileid), 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()); @@ -108,10 +110,11 @@ public class CopyWorkerTest extends AbstractB2Test { final DefaultVaultRegistry registry = new DefaultVaultRegistry(new DisabledPasswordCallback(), cryptomator); session.withRegistry(registry); final B2VersionIdProvider fileid = new B2VersionIdProvider(session); - new CryptoTouchFeature<>(session, new DefaultTouchFeature<>(new B2WriteFeature(session, fileid) - ), new B2WriteFeature(session, fileid), cryptomator).touch(source, new TransferStatus()); + new CryptoTouchFeature<>(session, new DefaultTouchFeature( + session), cryptomator).touch(new CryptoWriteFeature<>(session, new B2WriteFeature(session, fileid), cryptomator), source, new TransferStatus()); assertTrue(cryptomator.getFeature(session, Find.class, new DefaultFindFeature(session)).find(source)); - cryptomator.getFeature(session, Directory.class, new B2DirectoryFeature(session, fileid)).mkdir(targetFolder, new TransferStatus()); + cryptomator.getFeature(session, Directory.class, new B2DirectoryFeature(session, fileid)).mkdir( + cryptomator.getFeature(session, Write.class, new B2WriteFeature(session, fileid)), 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); @@ -134,10 +137,11 @@ public class CopyWorkerTest extends AbstractB2Test { final DefaultVaultRegistry registry = new DefaultVaultRegistry(new DisabledPasswordCallback(), cryptomator); session.withRegistry(registry); final B2VersionIdProvider fileid = new B2VersionIdProvider(session); - new CryptoTouchFeature<>(session, new DefaultTouchFeature<>(new B2WriteFeature(session, fileid) - ), new B2WriteFeature(session, fileid), cryptomator).touch(source, new TransferStatus()); + new CryptoTouchFeature<>(session, new DefaultTouchFeature( + session), cryptomator).touch(new CryptoWriteFeature<>(session, new B2WriteFeature(session, fileid), cryptomator), source, new TransferStatus()); assertTrue(cryptomator.getFeature(session, Find.class, new DefaultFindFeature(session)).find(source)); - cryptomator.getFeature(session, Directory.class, new B2DirectoryFeature(session, fileid)).mkdir(targetFolder, new TransferStatus()); + cryptomator.getFeature(session, Directory.class, new B2DirectoryFeature(session, fileid)).mkdir( + cryptomator.getFeature(session, Write.class, new B2WriteFeature(session, fileid)), 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); @@ -158,10 +162,11 @@ public class CopyWorkerTest extends AbstractB2Test { final DefaultVaultRegistry registry = new DefaultVaultRegistry(new DisabledPasswordCallback(), cryptomator); session.withRegistry(registry); final B2VersionIdProvider fileid = new B2VersionIdProvider(session); - cryptomator.getFeature(session, Directory.class, new B2DirectoryFeature(session, fileid)).mkdir(folder, new TransferStatus()); + cryptomator.getFeature(session, Directory.class, new B2DirectoryFeature(session, fileid)).mkdir( + cryptomator.getFeature(session, Write.class, new B2WriteFeature(session, fileid)), folder, new TransferStatus()); assertTrue(cryptomator.getFeature(session, Find.class, new DefaultFindFeature(session)).find(folder)); - new CryptoTouchFeature<>(session, new DefaultTouchFeature<>(new B2WriteFeature(session, fileid) - ), new B2WriteFeature(session, fileid), cryptomator).touch(file, new TransferStatus()); + new CryptoTouchFeature<>(session, new DefaultTouchFeature( + session), cryptomator).touch(new CryptoWriteFeature<>(session, new B2WriteFeature(session, fileid), 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)); @@ -185,7 +190,7 @@ public class CopyWorkerTest extends AbstractB2Test { 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 B2VersionIdProvider fileid = new B2VersionIdProvider(session); - new B2TouchFeature(session, fileid).touch(cleartextFile, new TransferStatus()); + new B2TouchFeature(session, fileid).touch(new B2WriteFeature(session, fileid), cleartextFile, new TransferStatus()); assertTrue(new B2FindFeature(session, fileid).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)); @@ -193,7 +198,8 @@ public class CopyWorkerTest extends AbstractB2Test { cryptomator.create(session, new VaultCredentials("test"), vaultVersion); final DefaultVaultRegistry registry = new DefaultVaultRegistry(new DisabledPasswordCallback(), cryptomator); session.withRegistry(registry); - cryptomator.getFeature(session, Directory.class, new B2DirectoryFeature(session, fileid)).mkdir(encryptedFolder, new TransferStatus()); + cryptomator.getFeature(session, Directory.class, new B2DirectoryFeature(session, fileid)).mkdir( + cryptomator.getFeature(session, Write.class, new B2WriteFeature(session, fileid)), 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()); @@ -211,8 +217,8 @@ public class CopyWorkerTest extends AbstractB2Test { 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)); final B2VersionIdProvider fileid = new B2VersionIdProvider(session); - new B2DirectoryFeature(session, fileid).mkdir(cleartextFolder, new TransferStatus()); - new B2TouchFeature(session, fileid).touch(cleartextFile, new TransferStatus()); + new B2DirectoryFeature(session, fileid).mkdir(new B2WriteFeature(session, fileid), cleartextFolder, new TransferStatus()); + new B2TouchFeature(session, fileid).touch(new B2WriteFeature(session, fileid), cleartextFile, new TransferStatus()); assertTrue(new B2FindFeature(session, fileid).find(cleartextFolder)); assertTrue(new B2FindFeature(session, fileid).find(cleartextFile)); final CryptoVault cryptomator = new CryptoVault(vault); @@ -238,17 +244,18 @@ public class CopyWorkerTest extends AbstractB2Test { 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)); final B2VersionIdProvider fileid = new B2VersionIdProvider(session); - new B2DirectoryFeature(session, fileid).mkdir(clearFolder, new TransferStatus()); + new B2DirectoryFeature(session, fileid).mkdir(new B2WriteFeature(session, fileid), 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 B2DirectoryFeature(session, fileid)).mkdir(encryptedFolder, new TransferStatus()); + cryptomator.getFeature(session, Directory.class, new B2DirectoryFeature(session, fileid)).mkdir( + cryptomator.getFeature(session, Write.class, new B2WriteFeature(session, fileid)), encryptedFolder, new TransferStatus()); assertTrue(cryptomator.getFeature(session, Find.class, new DefaultFindFeature(session)).find(encryptedFolder)); - new CryptoTouchFeature<>(session, new DefaultTouchFeature<>(new B2WriteFeature(session, fileid) - ), new B2WriteFeature(session, fileid), cryptomator).touch(encryptedFile, new TransferStatus()); + new CryptoTouchFeature<>(session, new DefaultTouchFeature( + session), cryptomator).touch(new CryptoWriteFeature<>(session, new B2WriteFeature(session, fileid), 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)); @@ -271,10 +278,11 @@ public class CopyWorkerTest extends AbstractB2Test { final DefaultVaultRegistry registry = new DefaultVaultRegistry(new DisabledPasswordCallback(), cryptomator); session.withRegistry(registry); final B2VersionIdProvider fileid = new B2VersionIdProvider(session); - cryptomator.getFeature(session, Directory.class, new B2DirectoryFeature(session, fileid)).mkdir(encryptedFolder, new TransferStatus()); + cryptomator.getFeature(session, Directory.class, new B2DirectoryFeature(session, fileid)).mkdir( + cryptomator.getFeature(session, Write.class, new B2WriteFeature(session, fileid)), encryptedFolder, new TransferStatus()); assertTrue(cryptomator.getFeature(session, Find.class, new DefaultFindFeature(session)).find(encryptedFolder)); - new CryptoTouchFeature<>(session, new DefaultTouchFeature<>(new B2WriteFeature(session, fileid) - ), new B2WriteFeature(session, fileid), cryptomator).touch(encryptedFile, new TransferStatus()); + new CryptoTouchFeature<>(session, new DefaultTouchFeature( + session), cryptomator).touch(new CryptoWriteFeature<>(session, new B2WriteFeature(session, fileid), 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)); diff --git a/backblaze/src/test/java/ch/cyberduck/core/cryptomator/MoveWorkerTest.java b/backblaze/src/test/java/ch/cyberduck/core/cryptomator/MoveWorkerTest.java index 9de5074a9b..031bac4fa1 100644 --- a/backblaze/src/test/java/ch/cyberduck/core/cryptomator/MoveWorkerTest.java +++ b/backblaze/src/test/java/ch/cyberduck/core/cryptomator/MoveWorkerTest.java @@ -24,6 +24,7 @@ import ch.cyberduck.core.PathCache; import ch.cyberduck.core.b2.AbstractB2Test; import ch.cyberduck.core.b2.B2DeleteFeature; import ch.cyberduck.core.b2.B2DirectoryFeature; +import ch.cyberduck.core.b2.B2FindFeature; import ch.cyberduck.core.b2.B2TouchFeature; import ch.cyberduck.core.b2.B2VersionIdProvider; import ch.cyberduck.core.b2.B2WriteFeature; @@ -32,6 +33,8 @@ import ch.cyberduck.core.exception.NotfoundException; import ch.cyberduck.core.features.Delete; import ch.cyberduck.core.features.Directory; import ch.cyberduck.core.features.Find; +import ch.cyberduck.core.features.Touch; +import ch.cyberduck.core.features.Write; import ch.cyberduck.core.pool.SessionPool; import ch.cyberduck.core.shared.DefaultFindFeature; import ch.cyberduck.core.transfer.TransferStatus; @@ -61,7 +64,7 @@ public class MoveWorkerTest extends AbstractB2Test { final Path vault = new Path(home, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory)); final Path clearFile = new Path(home, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)); final B2VersionIdProvider fileid = new B2VersionIdProvider(session); - new B2TouchFeature(session, fileid).touch(clearFile, new TransferStatus()); + new B2TouchFeature(session, fileid).touch(new B2WriteFeature(session, fileid), clearFile, new TransferStatus()); assertTrue(new DefaultFindFeature(session).find(clearFile)); 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)); @@ -69,7 +72,8 @@ public class MoveWorkerTest extends AbstractB2Test { cryptomator.create(session, new VaultCredentials("test"), vaultVersion); final DefaultVaultRegistry registry = new DefaultVaultRegistry(new DisabledPasswordCallback(), cryptomator); session.withRegistry(registry); - cryptomator.getFeature(session, Directory.class, new B2DirectoryFeature(session, fileid)).mkdir(encryptedFolder, new TransferStatus()); + cryptomator.getFeature(session, Directory.class, new B2DirectoryFeature(session, fileid)).mkdir( + cryptomator.getFeature(session, Write.class, new B2WriteFeature(session, fileid)), encryptedFolder, new TransferStatus()); assertTrue(cryptomator.getFeature(session, Find.class, new DefaultFindFeature(session)).find(encryptedFolder)); // move file into vault final MoveWorker worker = new MoveWorker(Collections.singletonMap(clearFile, encryptedFile), new SessionPool.SingleSessionPool(session), PathCache.empty(), new DisabledProgressListener(), new DisabledLoginCallback()); @@ -87,8 +91,8 @@ public class MoveWorkerTest extends AbstractB2Test { final Path clearFolder = new Path(home, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory)); final Path clearFile = new Path(clearFolder, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)); final B2VersionIdProvider fileid = new B2VersionIdProvider(session); - new B2DirectoryFeature(session, fileid).mkdir(clearFolder, new TransferStatus()); - new B2TouchFeature(session, fileid).touch(clearFile, new TransferStatus()); + new B2DirectoryFeature(session, fileid).mkdir(new B2WriteFeature(session, fileid), clearFolder, new TransferStatus()); + new B2TouchFeature(session, fileid).touch(new B2WriteFeature(session, fileid), clearFile, new TransferStatus()); assertTrue(new DefaultFindFeature(session).find(clearFolder)); assertTrue(new DefaultFindFeature(session).find(clearFile)); final CryptoVault cryptomator = new CryptoVault(vault); @@ -114,16 +118,18 @@ public class MoveWorkerTest extends AbstractB2Test { 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)); final B2VersionIdProvider fileid = new B2VersionIdProvider(session); - new B2DirectoryFeature(session, fileid).mkdir(clearFolder, new TransferStatus()); + new B2DirectoryFeature(session, fileid).mkdir(new B2WriteFeature(session, fileid), 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 B2DirectoryFeature(session, fileid)).mkdir(encryptedFolder, new TransferStatus()); + cryptomator.getFeature(session, Directory.class, new B2DirectoryFeature(session, fileid)).mkdir( + cryptomator.getFeature(session, Write.class, new B2WriteFeature(session, fileid)), encryptedFolder, new TransferStatus()); assertTrue(cryptomator.getFeature(session, Find.class, new DefaultFindFeature(session)).find(encryptedFolder)); - new CryptoTouchFeature<>(session, new B2TouchFeature(session, fileid), new B2WriteFeature(session, fileid), cryptomator).touch(encryptedFile, new TransferStatus()); + cryptomator.getFeature(session, Touch.class, new B2TouchFeature(session, fileid)).touch( + cryptomator.getFeature(session, Write.class, new B2WriteFeature(session, fileid)), encryptedFile, new TransferStatus()); assertTrue(cryptomator.getFeature(session, Find.class, new DefaultFindFeature(session)).find(encryptedFile)); // move file outside vault final Path fileRenamed = new Path(clearFolder, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)); @@ -147,19 +153,21 @@ public class MoveWorkerTest extends AbstractB2Test { final DefaultVaultRegistry registry = new DefaultVaultRegistry(new DisabledPasswordCallback(), cryptomator); session.withRegistry(registry); final B2VersionIdProvider fileid = new B2VersionIdProvider(session); - cryptomator.getFeature(session, Directory.class, new B2DirectoryFeature(session, fileid)).mkdir(encryptedFolder, new TransferStatus()); + cryptomator.getFeature(session, Directory.class, new B2DirectoryFeature(session, fileid)).mkdir( + cryptomator.getFeature(session, Write.class, new B2WriteFeature(session, fileid)), encryptedFolder, new TransferStatus()); assertTrue(cryptomator.getFeature(session, Find.class, new DefaultFindFeature(session)).find(encryptedFolder)); - new CryptoTouchFeature<>(session, new B2TouchFeature(session, fileid), new B2WriteFeature(session, fileid), cryptomator).touch(encryptedFile, new TransferStatus()); + new CryptoTouchFeature<>(session, new B2TouchFeature(session, fileid), cryptomator).touch( + cryptomator.getFeature(session, Write.class, new B2WriteFeature(session, fileid)), encryptedFile, new TransferStatus()); assertTrue(cryptomator.getFeature(session, Find.class, new DefaultFindFeature(session)).find(encryptedFile)); // move directory outside vault final Path directoryRenamed = new Path(home, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory)); final MoveWorker worker = new MoveWorker(Collections.singletonMap(encryptedFolder, directoryRenamed), new SessionPool.SingleSessionPool(session), PathCache.empty(), new DisabledProgressListener(), new DisabledLoginCallback()); worker.run(session); - assertFalse(cryptomator.getFeature(session, Find.class, new DefaultFindFeature(session)).find(encryptedFolder)); - assertThrows(NotfoundException.class, () -> cryptomator.getFeature(session, Find.class, new DefaultFindFeature(session)).find(encryptedFile)); - assertTrue(new DefaultFindFeature(session).find(directoryRenamed)); + assertFalse(cryptomator.getFeature(session, Find.class, new B2FindFeature(session, fileid)).find(encryptedFolder)); + assertFalse(cryptomator.getFeature(session, Find.class, new B2FindFeature(session, fileid)).find(encryptedFile)); + assertTrue(new B2FindFeature(session, fileid).find(directoryRenamed)); final Path fileRenamed = new Path(directoryRenamed, encryptedFile.getName(), EnumSet.of(Path.Type.file)); - assertTrue(new DefaultFindFeature(session).find(fileRenamed)); + assertTrue(new B2FindFeature(session, fileid).find(fileRenamed)); cryptomator.getFeature(session, Delete.class, new B2DeleteFeature(session, fileid)).delete(Collections.singletonList(vault), new DisabledLoginCallback(), new Delete.DisabledCallback()); new B2DeleteFeature(session, fileid).delete(Arrays.asList(fileRenamed, directoryRenamed), new DisabledLoginCallback(), new Delete.DisabledCallback()); registry.clear(); diff --git a/backblaze/src/test/java/ch/cyberduck/core/shared/CachingAttributesFinderFeatureTest.java b/backblaze/src/test/java/ch/cyberduck/core/shared/CachingAttributesFinderFeatureTest.java index 5942a16880..338dc2d4d3 100644 --- a/backblaze/src/test/java/ch/cyberduck/core/shared/CachingAttributesFinderFeatureTest.java +++ b/backblaze/src/test/java/ch/cyberduck/core/shared/CachingAttributesFinderFeatureTest.java @@ -26,6 +26,7 @@ import ch.cyberduck.core.PathCache; import ch.cyberduck.core.b2.AbstractB2Test; import ch.cyberduck.core.b2.B2DeleteFeature; import ch.cyberduck.core.b2.B2VersionIdProvider; +import ch.cyberduck.core.b2.B2WriteFeature; import ch.cyberduck.core.exception.NotfoundException; import ch.cyberduck.core.features.AttributesFinder; import ch.cyberduck.core.features.Delete; @@ -68,7 +69,7 @@ public class CachingAttributesFinderFeatureTest extends AbstractB2Test { final Path bucket = new Path("test-cyberduck", EnumSet.of(Path.Type.directory, Path.Type.volume)); final String name = new AlphanumericRandomStringService().random(); final Path file = new Path(bucket, name, EnumSet.of(Path.Type.file)); - session.getFeature(Touch.class).touch(file, new TransferStatus()); + session.getFeature(Touch.class).touch(new B2WriteFeature(session, fileid), file, new TransferStatus()); final Attributes lookup = f.find(file); assertEquals(0L, lookup.getSize()); // Test cache diff --git a/backblaze/src/test/java/ch/cyberduck/core/shared/CachingFindFeatureTest.java b/backblaze/src/test/java/ch/cyberduck/core/shared/CachingFindFeatureTest.java index ec26cd3912..9fc34a9c00 100644 --- a/backblaze/src/test/java/ch/cyberduck/core/shared/CachingFindFeatureTest.java +++ b/backblaze/src/test/java/ch/cyberduck/core/shared/CachingFindFeatureTest.java @@ -28,6 +28,7 @@ import ch.cyberduck.core.b2.B2DeleteFeature; import ch.cyberduck.core.b2.B2DirectoryFeature; import ch.cyberduck.core.b2.B2TouchFeature; import ch.cyberduck.core.b2.B2VersionIdProvider; +import ch.cyberduck.core.b2.B2WriteFeature; import ch.cyberduck.core.features.Delete; import ch.cyberduck.core.features.Find; import ch.cyberduck.core.transfer.TransferStatus; @@ -52,7 +53,7 @@ public class CachingFindFeatureTest extends AbstractB2Test { final String name = new AlphanumericRandomStringService().random(); final CachingFindFeature f = new CachingFindFeature(session, cache, new DefaultFindFeature(session)); assertFalse(f.find(new Path(bucket, name, EnumSet.of(Path.Type.file)))); - final Path test = new B2TouchFeature(session, fileid).touch(new Path(bucket, name, EnumSet.of(Path.Type.file)), new TransferStatus()); + final Path test = new B2TouchFeature(session, fileid).touch(new B2WriteFeature(session, fileid), new Path(bucket, name, EnumSet.of(Path.Type.file)), new TransferStatus()); assertFalse(f.find(test)); cache.clear(); assertTrue(f.find(test)); @@ -66,10 +67,10 @@ public class CachingFindFeatureTest extends AbstractB2Test { final PathCache cache = new PathCache(1); 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()); + 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(bucket, name, EnumSet.of(Path.Type.file)), new TransferStatus()); + new B2WriteFeature(session, fileid), new Path(bucket, name, EnumSet.of(Path.Type.file)), new TransferStatus()); final CachingFindFeature f = new CachingFindFeature(session, cache, new DefaultFindFeature(session)); // Find without version id set in attributes assertTrue(f.find(test)); diff --git a/backblaze/src/test/java/ch/cyberduck/core/shared/DefaultAttributesFinderFeatureTest.java b/backblaze/src/test/java/ch/cyberduck/core/shared/DefaultAttributesFinderFeatureTest.java index 03dfa22905..a77aa8c536 100644 --- a/backblaze/src/test/java/ch/cyberduck/core/shared/DefaultAttributesFinderFeatureTest.java +++ b/backblaze/src/test/java/ch/cyberduck/core/shared/DefaultAttributesFinderFeatureTest.java @@ -23,6 +23,7 @@ import ch.cyberduck.core.b2.B2DeleteFeature; import ch.cyberduck.core.b2.B2FindFeature; import ch.cyberduck.core.b2.B2TouchFeature; import ch.cyberduck.core.b2.B2VersionIdProvider; +import ch.cyberduck.core.b2.B2WriteFeature; import ch.cyberduck.core.features.Delete; import ch.cyberduck.core.transfer.TransferStatus; import ch.cyberduck.test.IntegrationTest; @@ -46,7 +47,7 @@ public class DefaultAttributesFinderFeatureTest 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()); // Find without version id set in attributes assertNotNull(new DefaultAttributesFinderFeature(session).find(file).getVersionId()); new B2DeleteFeature(session, fileid).delete(Collections.singletonList(file), new DisabledLoginCallback(), new Delete.DisabledCallback()); diff --git a/backblaze/src/test/java/ch/cyberduck/core/shared/DefaultDownloadFeatureTest.java b/backblaze/src/test/java/ch/cyberduck/core/shared/DefaultDownloadFeatureTest.java index 3b34b1cd65..3f99b2b782 100644 --- a/backblaze/src/test/java/ch/cyberduck/core/shared/DefaultDownloadFeatureTest.java +++ b/backblaze/src/test/java/ch/cyberduck/core/shared/DefaultDownloadFeatureTest.java @@ -60,7 +60,7 @@ public class DefaultDownloadFeatureTest extends AbstractB2Test { final Path bucket = new Path("test-cyberduck", EnumSet.of(Path.Type.directory, Path.Type.volume)); final B2VersionIdProvider fileid = new B2VersionIdProvider(session); 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(bucket, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)), new TransferStatus()); final byte[] content = new byte[39864]; new Random().nextBytes(content); { @@ -73,8 +73,8 @@ public class DefaultDownloadFeatureTest extends AbstractB2Test { } final Local local = new Local(System.getProperty("java.io.tmpdir"), UUID.randomUUID().toString()); final TransferStatus status = new TransferStatus().setLength(content.length); - new DefaultDownloadFeature(new B2ReadFeature(session, fileid)).download( - test, local, new BandwidthThrottle(BandwidthThrottle.UNLIMITED), new DisabledStreamListener(), + new DefaultDownloadFeature(session).download( + new B2ReadFeature(session, fileid), test, local, new BandwidthThrottle(BandwidthThrottle.UNLIMITED), new DisabledStreamListener(), status, new DisabledConnectionCallback()); final byte[] buffer = new byte[content.length]; @@ -90,7 +90,7 @@ public class DefaultDownloadFeatureTest 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 byte[] content = new byte[1]; new Random().nextBytes(content); { @@ -103,8 +103,8 @@ public class DefaultDownloadFeatureTest extends AbstractB2Test { final Local local = new Local(System.getProperty("java.io.tmpdir"), UUID.randomUUID().toString()); { final TransferStatus status = new TransferStatus().setLength(-1L); - new DefaultDownloadFeature(new B2ReadFeature(session, fileid)).download( - test, local, new BandwidthThrottle(BandwidthThrottle.UNLIMITED), new DisabledStreamListener(), + new DefaultDownloadFeature(session).download( + new B2ReadFeature(session, fileid), test, local, new BandwidthThrottle(BandwidthThrottle.UNLIMITED), new DisabledStreamListener(), status, new DisabledConnectionCallback()); } diff --git a/backblaze/src/test/java/ch/cyberduck/core/shared/DefaultFindFeatureTest.java b/backblaze/src/test/java/ch/cyberduck/core/shared/DefaultFindFeatureTest.java index 42fea14b99..90af944f2b 100644 --- a/backblaze/src/test/java/ch/cyberduck/core/shared/DefaultFindFeatureTest.java +++ b/backblaze/src/test/java/ch/cyberduck/core/shared/DefaultFindFeatureTest.java @@ -24,6 +24,7 @@ import ch.cyberduck.core.b2.B2DeleteFeature; import ch.cyberduck.core.b2.B2LargeUploadWriteFeature; import ch.cyberduck.core.b2.B2TouchFeature; import ch.cyberduck.core.b2.B2VersionIdProvider; +import ch.cyberduck.core.b2.B2WriteFeature; import ch.cyberduck.core.features.Delete; import ch.cyberduck.core.io.StatusOutputStream; import ch.cyberduck.core.transfer.TransferStatus; @@ -50,7 +51,7 @@ public class DefaultFindFeatureTest 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); - final Path test = new B2TouchFeature(session, fileid).touch(file, new TransferStatus()); + final Path test = new B2TouchFeature(session, fileid).touch(new B2WriteFeature(session, fileid), file, new TransferStatus()); // Find without version id set in attributes assertTrue(new DefaultFindFeature(session).find(file)); assertTrue(new DefaultFindFeature(session).find(test)); diff --git a/backblaze/src/test/java/ch/cyberduck/core/worker/B2ConcurrentTransferWorkerTest.java b/backblaze/src/test/java/ch/cyberduck/core/worker/B2ConcurrentTransferWorkerTest.java index 60ca02f27d..ccf0fb5e97 100644 --- a/backblaze/src/test/java/ch/cyberduck/core/worker/B2ConcurrentTransferWorkerTest.java +++ b/backblaze/src/test/java/ch/cyberduck/core/worker/B2ConcurrentTransferWorkerTest.java @@ -23,7 +23,6 @@ import ch.cyberduck.core.b2.B2LargeUploadService; import ch.cyberduck.core.b2.B2Protocol; import ch.cyberduck.core.b2.B2Session; import ch.cyberduck.core.b2.B2VersionIdProvider; -import ch.cyberduck.core.b2.B2WriteFeature; import ch.cyberduck.core.exception.BackgroundException; import ch.cyberduck.core.features.Delete; import ch.cyberduck.core.features.Upload; @@ -108,11 +107,11 @@ public class B2ConcurrentTransferWorkerTest extends AbstractB2Test { host, new DefaultVaultRegistry(new DisabledPasswordCallback())) { @Override public Session create() { - return new B2Session(host.withCredentials(new Credentials( + return new B2Session(host.setCredentials(new Credentials( PROPERTIES.get("b2.user"), PROPERTIES.get("b2.password") )), new DefaultX509TrustManager(), new DefaultX509KeyManager()) { - final B2LargeUploadService upload = new B2LargeUploadService(this, new B2VersionIdProvider(this), - new B2WriteFeature(this, new B2VersionIdProvider(this))) { + final B2LargeUploadService upload = new B2LargeUploadService(this, new B2VersionIdProvider(this) + ) { @Override protected InputStream decorate(final InputStream in, final MessageDigest digest) { if(failed.get()) { diff --git a/backblaze/src/test/java/ch/cyberduck/core/worker/B2SingleTransferWorkerTest.java b/backblaze/src/test/java/ch/cyberduck/core/worker/B2SingleTransferWorkerTest.java index 454c605acc..ee4e9eb40a 100644 --- a/backblaze/src/test/java/ch/cyberduck/core/worker/B2SingleTransferWorkerTest.java +++ b/backblaze/src/test/java/ch/cyberduck/core/worker/B2SingleTransferWorkerTest.java @@ -34,7 +34,6 @@ import ch.cyberduck.core.b2.B2LargeUploadService; import ch.cyberduck.core.b2.B2Protocol; import ch.cyberduck.core.b2.B2Session; import ch.cyberduck.core.b2.B2VersionIdProvider; -import ch.cyberduck.core.b2.B2WriteFeature; import ch.cyberduck.core.features.Delete; import ch.cyberduck.core.features.Upload; import ch.cyberduck.core.io.Checksum; @@ -105,11 +104,11 @@ public class B2SingleTransferWorkerTest extends VaultTest { return super.getProperty(key); } }; - final B2Session session = new B2Session(host.withCredentials(new Credentials( + final B2Session session = new B2Session(host.setCredentials(new Credentials( PROPERTIES.get("b2.user"), PROPERTIES.get("b2.password") )), new DefaultX509TrustManager(), new DefaultX509KeyManager()) { - final B2LargeUploadService upload = new B2LargeUploadService(this, new B2VersionIdProvider(this), - new B2WriteFeature(this, new B2VersionIdProvider(this))) { + final B2LargeUploadService upload = new B2LargeUploadService(this, new B2VersionIdProvider(this) + ) { @Override protected InputStream decorate(final InputStream in, final MessageDigest digest) { if(failed.get()) { diff --git a/binding/pom.xml b/binding/pom.xml index 9b232acf1d..d15e1b32cb 100644 --- a/binding/pom.xml +++ b/binding/pom.xml @@ -19,7 +19,7 @@ ch.cyberduck parent - 9.2.0-SNAPSHOT + 9.4.0-SNAPSHOT binding jar diff --git a/binding/src/main/java/ch/cyberduck/binding/AlertController.java b/binding/src/main/java/ch/cyberduck/binding/AlertController.java index cba8af55ba..b69ba756a4 100644 --- a/binding/src/main/java/ch/cyberduck/binding/AlertController.java +++ b/binding/src/main/java/ch/cyberduck/binding/AlertController.java @@ -66,6 +66,10 @@ public abstract class AlertController extends SheetController implements InputVa public abstract NSAlert loadAlert(); + public NSAlert alert() { + return alert; + } + @Override public void loadBundle() { alert = this.loadAlert(); @@ -83,6 +87,12 @@ public abstract class AlertController extends SheetController implements InputVa this.focus(alert); } + @Override + public void setWindow(final NSWindow window) { + super.setWindow(window); + window.setReleasedWhenClosed(false); + } + protected void focus(final NSAlert alert) { log.debug("Focus alert {}", alert); final NSEnumerator buttons = alert.buttons().objectEnumerator(); diff --git a/binding/src/main/java/ch/cyberduck/binding/BundleController.java b/binding/src/main/java/ch/cyberduck/binding/BundleController.java index f48e17fce3..1dc589b15e 100644 --- a/binding/src/main/java/ch/cyberduck/binding/BundleController.java +++ b/binding/src/main/java/ch/cyberduck/binding/BundleController.java @@ -143,7 +143,7 @@ public abstract class BundleController extends ProxyController { /** * @return The top level view object or null if unknown */ - protected NSView view() { + public NSView view() { return null; } diff --git a/binding/src/main/java/ch/cyberduck/binding/ProxyController.java b/binding/src/main/java/ch/cyberduck/binding/ProxyController.java index ad7dd393e8..21cc4f4125 100644 --- a/binding/src/main/java/ch/cyberduck/binding/ProxyController.java +++ b/binding/src/main/java/ch/cyberduck/binding/ProxyController.java @@ -163,6 +163,7 @@ public class ProxyController extends AbstractController { * @return Selected alert option by user */ public int alert(final SheetController sheet, final SheetCallback callback, final AlertRunner runner, final CountDownLatch signal) { + log.debug("Alert with runner {} and callback {}", runner, callback); alerts.add(runner); final AtomicInteger option = new AtomicInteger(SheetCallback.CANCEL_OPTION); final CountDownLatch state = new CountDownLatch(1); @@ -198,17 +199,16 @@ public class ProxyController extends AbstractController { } protected AlertRunner alertFor(final SheetController sheet) { - final ModalWindowAlertRunner runner = new ModalWindowAlertRunner(sheet); - sheet.addHandler(runner); - return runner; + return new ModalWindowAlertRunner(sheet); } public static class RegularWindowAlertRunner implements AlertRunner, AlertRunner.CloseHandler { - private final WindowController controller; + private final SheetController controller; private final AtomicInteger option = new AtomicInteger(SheetCallback.CANCEL_OPTION); - public RegularWindowAlertRunner(final WindowController controller) { + public RegularWindowAlertRunner(final SheetController controller) { this.controller = controller; + this.controller.addHandler(this); } @Override @@ -244,8 +244,8 @@ public class ProxyController extends AbstractController { * Floating window ordered front */ public static class FloatingWindowAlertRunner extends RegularWindowAlertRunner { - public FloatingWindowAlertRunner(final WindowController sheet) { - super(sheet); + public FloatingWindowAlertRunner(final SheetController controller) { + super(controller); } @Override @@ -262,17 +262,17 @@ public class ProxyController extends AbstractController { * Floating window in modal run loop */ public static class ModalWindowAlertRunner extends FloatingWindowAlertRunner implements AlertRunner.CloseHandler { - public ModalWindowAlertRunner(final WindowController sheet) { - super(sheet); + public ModalWindowAlertRunner(final SheetController controller) { + super(controller); } @Override public void closed(final NSWindow sheet, final int returncode) { - // Close window - super.closed(sheet, returncode); log.debug("Stop modal with return code {}", returncode); // The result code you want returned from the runModalForWindow: NSApplication.sharedApplication().stopModalWithCode(returncode); + // Close window + sheet.close(); } /** @@ -286,11 +286,10 @@ public class ProxyController extends AbstractController { public void alert(final NSWindow sheet, final SheetCallback callback) { sheet.setPreventsApplicationTerminationWhenModal(false); sheet.setLevel(NSWindow.NSWindowLevel.NSModalPanelWindowLevel); - sheet.center(); - super.alert(sheet, callback); // This method runs a modal event loop for the specified window synchronously. It displays the specified window, makes it key, // starts the run loop, and processes events for that window. log.debug("Run modal for window {} with callback {}", sheet, callback); + // You do not need to show the window yourself callback.callback(NSApplication.sharedApplication().runModalForWindow(sheet).intValue()); } } diff --git a/binding/src/main/java/ch/cyberduck/binding/SheetController.java b/binding/src/main/java/ch/cyberduck/binding/SheetController.java index 71e48b4276..a804f4dfaa 100644 --- a/binding/src/main/java/ch/cyberduck/binding/SheetController.java +++ b/binding/src/main/java/ch/cyberduck/binding/SheetController.java @@ -83,10 +83,11 @@ public abstract class SheetController extends WindowController implements InputV } } handlers.forEach(h -> h.closed(window, option)); - if(handlers.isEmpty()) { - log.warn("No close handlers for {}", this); - window.performClose(null); - } + } + + @Override + public void invalidate() { + super.invalidate(); handlers.clear(); } @@ -99,23 +100,16 @@ public abstract class SheetController extends WindowController implements InputV log.warn("Return code {} not handled", returncode); } - @Delegate - // Handle keyboard esc event when not running as sheet - public void cancel(ID sender) { - this.closeSheetWithOption(SheetCallback.CANCEL_OPTION); - } - /** * Implementation with no bundle loaded but window reference only */ public static class NoBundleSheetController extends SheetController { - public NoBundleSheetController(final NSWindow window) { - this(window, InputValidator.disabled); + public NoBundleSheetController() { + this(InputValidator.disabled); } - public NoBundleSheetController(final NSWindow window, final InputValidator callback) { + public NoBundleSheetController(final InputValidator callback) { super(callback); - this.setWindow(window); } @Override diff --git a/binding/src/main/java/ch/cyberduck/binding/SystemAlertController.java b/binding/src/main/java/ch/cyberduck/binding/SystemAlertController.java index 94af3a606b..28fe5a831a 100644 --- a/binding/src/main/java/ch/cyberduck/binding/SystemAlertController.java +++ b/binding/src/main/java/ch/cyberduck/binding/SystemAlertController.java @@ -20,7 +20,7 @@ import ch.cyberduck.binding.application.NSAlert; public class SystemAlertController extends AlertController { @Outlet - protected NSAlert alert; + private NSAlert alert; public SystemAlertController(final NSAlert alert) { this.alert = alert; @@ -30,4 +30,9 @@ public class SystemAlertController extends AlertController { public NSAlert loadAlert() { return alert; } + + @Override + public NSAlert alert() { + return alert; + } } diff --git a/binding/src/main/java/ch/cyberduck/binding/ToolbarWindowController.java b/binding/src/main/java/ch/cyberduck/binding/ToolbarWindowController.java index b515df6f6e..005073a86c 100644 --- a/binding/src/main/java/ch/cyberduck/binding/ToolbarWindowController.java +++ b/binding/src/main/java/ch/cyberduck/binding/ToolbarWindowController.java @@ -26,7 +26,6 @@ import ch.cyberduck.binding.foundation.FoundationKitFunctions; import ch.cyberduck.binding.foundation.NSArray; import ch.cyberduck.binding.foundation.NSEnumerator; import ch.cyberduck.binding.foundation.NSMutableArray; -import ch.cyberduck.binding.foundation.NSNotification; import ch.cyberduck.binding.foundation.NSObject; import ch.cyberduck.core.preferences.Preferences; import ch.cyberduck.core.preferences.PreferencesFactory; @@ -66,12 +65,6 @@ public abstract class ToolbarWindowController extends WindowController implement */ protected abstract Map getPanels(); - @Override - public void windowDidBecomeKey(NSNotification notification) { - this.resize(); - super.windowDidBecomeKey(notification); - } - protected static class Label { public String identifier; public String label; diff --git a/binding/src/main/java/ch/cyberduck/binding/WindowController.java b/binding/src/main/java/ch/cyberduck/binding/WindowController.java index d748f21732..3f40880b89 100644 --- a/binding/src/main/java/ch/cyberduck/binding/WindowController.java +++ b/binding/src/main/java/ch/cyberduck/binding/WindowController.java @@ -57,11 +57,6 @@ public abstract class WindowController extends BundleController implements NSWin @Outlet protected NSWindow window; - /** - * Main content view of window - */ - protected NSView view; - public WindowController() { super(); } @@ -91,16 +86,22 @@ public abstract class WindowController extends BundleController implements NSWin public void setWindow(final NSWindow window) { this.window = window; - this.view = window.contentView(); this.window.recalculateKeyViewLoop(); this.window.setReleasedWhenClosed(true); this.window.setDelegate(this.id()); + this.window.setCollectionBehavior(window.collectionBehavior() + | NSWindow.NSWindowCollectionBehavior.NSWindowCollectionBehaviorTransient); } public NSWindow window() { return window; } + @Override + public NSView view() { + return window.contentView(); + } + /** * Order front window */ @@ -247,14 +248,14 @@ public abstract class WindowController extends BundleController implements NSWin protected double toolbarHeightForWindow() { final NSRect windowFrame = NSWindow.contentRectForFrameRect_styleMask(window.frame(), window.styleMask()); - return windowFrame.size.height.doubleValue() - view.frame().size.height.doubleValue(); + return windowFrame.size.height.doubleValue() - window.contentView().frame().size.height.doubleValue(); } /** * @return Minimum size to fit content view of currently selected tab. */ protected NSRect getContentRect() { - return view.frame(); + return window.contentView().frame(); } /** @@ -295,9 +296,7 @@ public abstract class WindowController extends BundleController implements NSWin @Override protected AlertRunner alertFor(final SheetController sheet) { - final SheetAlertRunner handler = new SheetAlertRunner(window, sheet); - sheet.addHandler(handler); - return handler; + return new SheetAlertRunner(window, sheet); } /** @@ -315,6 +314,7 @@ public abstract class WindowController extends BundleController implements NSWin public SheetAlertRunner(final NSWindow window, final SheetController controller) { this.window = window; this.controller = controller; + this.controller.addHandler(this); } @Override diff --git a/binding/src/main/java/ch/cyberduck/binding/application/AlertSheetReturnCodeMapper.java b/binding/src/main/java/ch/cyberduck/binding/application/AlertSheetReturnCodeMapper.java index 1a437b62f5..a06da0df59 100644 --- a/binding/src/main/java/ch/cyberduck/binding/application/AlertSheetReturnCodeMapper.java +++ b/binding/src/main/java/ch/cyberduck/binding/application/AlertSheetReturnCodeMapper.java @@ -38,17 +38,20 @@ public final class AlertSheetReturnCodeMapper { public int getOption(final int option) { log.debug("Map selected tag {}", option); switch(option) { - case NSAlert.NSAlertFirstButtonReturn: case NSPanel.NSOKButton: return SheetCallback.DEFAULT_OPTION; - case NSAlert.NSAlertSecondButtonReturn: case NSPanel.NSCancelButton: return SheetCallback.CANCEL_OPTION; + } + switch(option) { + case NSAlert.NSAlertFirstButtonReturn: + return SheetCallback.DEFAULT_OPTION; + case NSAlert.NSAlertSecondButtonReturn: + return SheetCallback.CANCEL_OPTION; case NSAlert.NSAlertThirdButtonReturn: return SheetCallback.ALTERNATE_OPTION; } log.warn("Unknown return code {}", option); - return SheetCallback.DEFAULT_OPTION; + return option; } - } diff --git a/binding/src/main/java/ch/cyberduck/binding/application/NSApplication.java b/binding/src/main/java/ch/cyberduck/binding/application/NSApplication.java index 431fc13dd0..9745d48af6 100644 --- a/binding/src/main/java/ch/cyberduck/binding/application/NSApplication.java +++ b/binding/src/main/java/ch/cyberduck/binding/application/NSApplication.java @@ -562,8 +562,11 @@ public abstract class NSApplication extends NSObject { public abstract NSMenu mainMenu(); /** - * Original signature : void setApplicationIconImage(NSImage*)
- * native declaration : :179 + * Assign an image to this property when you want to temporarily change the app icon in the dock app tile. The image + * you provide is scaled as needed so that it fits in the tile. To restore your app’s original icon, set this + * property to nil. + * + * @param image The image used for the app’s icon. */ public abstract void setApplicationIconImage(NSImage image); diff --git a/binding/src/main/java/ch/cyberduck/binding/application/NSDockTile.java b/binding/src/main/java/ch/cyberduck/binding/application/NSDockTile.java index 9c34140613..d150d5d6e3 100644 --- a/binding/src/main/java/ch/cyberduck/binding/application/NSDockTile.java +++ b/binding/src/main/java/ch/cyberduck/binding/application/NSDockTile.java @@ -85,15 +85,6 @@ public abstract class NSDockTile extends NSObject { */ public abstract boolean showsApplicationBadge(); - /** - * Assign an image to this property when you want to temporarily change the app icon in the dock app tile. The image - * you provide is scaled as needed so that it fits in the tile. To restore your app’s original icon, set this - * property to nil. - * - * @param image The image used for the app’s icon. - */ - public abstract void setApplicationIconImage(NSImage image); - /** * Badge the dock icon with a localized string. The badge appearance is system defined. This is often used to show * an unread count in the application dock icon.
Original signature : -(void)setBadgeLabel:(NSString*)
diff --git a/binding/src/main/java/ch/cyberduck/binding/application/NSWindow.java b/binding/src/main/java/ch/cyberduck/binding/application/NSWindow.java index 8e1abdc053..2bc8e69fc5 100644 --- a/binding/src/main/java/ch/cyberduck/binding/application/NSWindow.java +++ b/binding/src/main/java/ch/cyberduck/binding/application/NSWindow.java @@ -176,18 +176,37 @@ public abstract class NSWindow extends NSResponder { /// enum values public interface NSWindowCollectionBehavior { - int NSWindowCollectionBehaviorManaged = 1 << 2; // participates in spaces, exposé. Default behavior if windowLevel == NSNormalWindowLevel - int NSWindowCollectionBehaviorTransient = 1 << 3; // floats in spaces, hidden by exposé. Default behavior if windowLevel != NSNormalWindowLevel - int NSWindowCollectionBehaviorStationary = 1 << 4; // unaffected by exposé. Stays visible and stationary, like desktop window - - int NSWindowCollectionBehaviorParticipatesInCycle = 1 << 5; // default behavior if windowLevel == NSNormalWindowLevel - int NSWindowCollectionBehaviorIgnoresCycle = 1 << 6; // default behavior if windowLevel != NSNormalWindowLevel - - int NSWindowNumberListAllApplications = 1 << 0; - int NSWindowNumberListAllSpaces = 1 << 4; - - int NSWindowCollectionBehaviorFullScreenPrimary = 1 << 7; // the frontmost window with this collection behavior will be the fullscreen window. - int NSWindowCollectionBehaviorFullScreenAuxiliary = 1 << 8; // windows with this collection behavior can be shown with the fullscreen window. + int NSWindowCollectionBehaviorDefault = 0; + int NSWindowCollectionBehaviorCanJoinAllSpaces = 1 << 0; + int NSWindowCollectionBehaviorMoveToActiveSpace = 1 << 1; + int NSWindowCollectionBehaviorManaged = 1 << 2; + int NSWindowCollectionBehaviorTransient = 1 << 3; + int NSWindowCollectionBehaviorStationary = 1 << 4; + int NSWindowCollectionBehaviorParticipatesInCycle = 1 << 5; + int NSWindowCollectionBehaviorIgnoresCycle = 1 << 6; + int NSWindowCollectionBehaviorFullScreenPrimary = 1 << 7; + int NSWindowCollectionBehaviorFullScreenAuxiliary = 1 << 8; + int NSWindowCollectionBehaviorFullScreenNone = 1 << 9; + /** + * @since macOS 10.11 + */ + int NSWindowCollectionBehaviorFullScreenAllowsTiling = 1 << 11; + /** + * @since macOS 10.11 + */ + int NSWindowCollectionBehaviorFullScreenDisallowsTiling = 1 << 12; + /** + * @since macOS 13.0 + */ + int NSWindowCollectionBehaviorPrimary = 1 << 16; + /** + * @since macOS 13.0 + */ + int NSWindowCollectionBehaviorAuxiliary = 1 << 17; + /** + * @since macOS 13.0 + */ + int NSWindowCollectionBehaviorCanJoinAllApplications = 1 << 18; } /// enum values diff --git a/bonjour/dll/pom.xml b/bonjour/dll/pom.xml index bcff55db9a..a6f84ff14c 100644 --- a/bonjour/dll/pom.xml +++ b/bonjour/dll/pom.xml @@ -5,7 +5,7 @@ ch.cyberduck parent ../../pom.xml - 9.2.0-SNAPSHOT + 9.4.0-SNAPSHOT Cyberduck.Bonjour pom diff --git a/bonjour/native/pom.xml b/bonjour/native/pom.xml index b5960de96b..b52acc745e 100644 --- a/bonjour/native/pom.xml +++ b/bonjour/native/pom.xml @@ -5,7 +5,7 @@ ch.cyberduck parent ../../pom.xml - 9.2.0-SNAPSHOT + 9.4.0-SNAPSHOT Cyberduck.Bonjour.Native pom diff --git a/bonjour/pom.xml b/bonjour/pom.xml index dd55d72a4a..7e5f8dc544 100644 --- a/bonjour/pom.xml +++ b/bonjour/pom.xml @@ -18,7 +18,7 @@ parent ch.cyberduck - 9.2.0-SNAPSHOT + 9.4.0-SNAPSHOT 4.0.0 diff --git a/box/pom.xml b/box/pom.xml index da471c2369..19f10d2cc7 100644 --- a/box/pom.xml +++ b/box/pom.xml @@ -19,7 +19,7 @@ parent ch.cyberduck - 9.2.0-SNAPSHOT + 9.4.0-SNAPSHOT box diff --git a/box/src/main/java/ch/cyberduck/core/box/BoxAttributesFinderFeature.java b/box/src/main/java/ch/cyberduck/core/box/BoxAttributesFinderFeature.java index 0176931f92..30846dc71a 100644 --- a/box/src/main/java/ch/cyberduck/core/box/BoxAttributesFinderFeature.java +++ b/box/src/main/java/ch/cyberduck/core/box/BoxAttributesFinderFeature.java @@ -15,6 +15,7 @@ package ch.cyberduck.core.box; * GNU General Public License for more details. */ +import ch.cyberduck.core.DefaultPathAttributes; import ch.cyberduck.core.ListProgressListener; import ch.cyberduck.core.Path; import ch.cyberduck.core.PathAttributes; @@ -64,7 +65,7 @@ public class BoxAttributesFinderFeature implements AttributesFinder, AttributesA @Override public PathAttributes toAttributes(final File f) { - final PathAttributes attrs = new PathAttributes(); + final PathAttributes attrs = new DefaultPathAttributes(); if(null != f.getContentModifiedAt()) { attrs.setModificationDate(f.getContentModifiedAt().getMillis()); } @@ -81,7 +82,7 @@ public class BoxAttributesFinderFeature implements AttributesFinder, AttributesA } protected PathAttributes toAttributes(final Folder f) { - final PathAttributes attrs = new PathAttributes(); + final PathAttributes attrs = new DefaultPathAttributes(); if(null != f.getContentModifiedAt()) { attrs.setModificationDate(f.getContentModifiedAt().getMillis()); } diff --git a/box/src/main/java/ch/cyberduck/core/box/BoxChunkedWriteFeature.java b/box/src/main/java/ch/cyberduck/core/box/BoxChunkedWriteFeature.java deleted file mode 100644 index 85848c573d..0000000000 --- a/box/src/main/java/ch/cyberduck/core/box/BoxChunkedWriteFeature.java +++ /dev/null @@ -1,111 +0,0 @@ -package ch.cyberduck.core.box; - -/* - * Copyright (c) 2002-2021 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 ch.cyberduck.core.ConnectionCallback; -import ch.cyberduck.core.DefaultIOExceptionMappingService; -import ch.cyberduck.core.Path; -import ch.cyberduck.core.box.io.swagger.client.JSON; -import ch.cyberduck.core.box.io.swagger.client.model.File; -import ch.cyberduck.core.box.io.swagger.client.model.UploadPart; -import ch.cyberduck.core.box.io.swagger.client.model.UploadedPart; -import ch.cyberduck.core.exception.BackgroundException; -import ch.cyberduck.core.http.AbstractHttpWriteFeature; -import ch.cyberduck.core.http.DefaultHttpResponseExceptionMappingService; -import ch.cyberduck.core.http.DelayedHttpEntityCallable; -import ch.cyberduck.core.http.HttpRange; -import ch.cyberduck.core.http.HttpResponseOutputStream; -import ch.cyberduck.core.io.ChecksumCompute; -import ch.cyberduck.core.io.SHA1ChecksumCompute; -import ch.cyberduck.core.transfer.TransferStatus; - -import org.apache.http.HttpEntity; -import org.apache.http.HttpHeaders; -import org.apache.http.client.HttpResponseException; -import org.apache.http.client.methods.HttpPut; -import org.apache.http.message.BasicHeader; -import org.apache.logging.log4j.LogManager; -import org.apache.logging.log4j.Logger; - -import java.io.IOException; -import java.util.EnumSet; - -public class BoxChunkedWriteFeature extends AbstractHttpWriteFeature { - private static final Logger log = LogManager.getLogger(BoxChunkedWriteFeature.class); - - private final BoxSession session; - private final BoxApiClient client; - - public BoxChunkedWriteFeature(final BoxSession session, final BoxFileidProvider fileid) { - super(new BoxAttributesFinderFeature(session, fileid)); - this.session = session; - this.client = new BoxApiClient(session.getClient()); - this.client.setBasePath("https://upload.box.com/api/2.0"); - } - - @Override - public HttpResponseOutputStream write(final Path file, final TransferStatus status, final ConnectionCallback callback) throws BackgroundException { - final DelayedHttpEntityCallable command = new DelayedHttpEntityCallable(file) { - @Override - public File call(final HttpEntity entity) throws BackgroundException { - try { - final HttpRange range = HttpRange.withStatus(new TransferStatus() - .setLength(status.getLength()) - .setOffset(status.getOffset())); - final String uploadSessionId = status.getParameters().get(BoxLargeUploadService.UPLOAD_SESSION_ID); - final String overall_length = status.getParameters().get(BoxLargeUploadService.OVERALL_LENGTH); - log.debug("Send range {} for file {}", range, file); - final HttpPut request = new HttpPut(String.format("%s/files/upload_sessions/%s", client.getBasePath(), uploadSessionId)); - // Must not overlap with the range of a part already uploaded this session. - request.addHeader(new BasicHeader(HttpHeaders.CONTENT_RANGE, String.format("bytes %d-%d/%d", range.getStart(), range.getEnd(), - Long.valueOf(overall_length)))); - request.addHeader(new BasicHeader("Digest", String.format("sha=%s", status.getChecksum().base64))); - request.setEntity(entity); - final UploadPart response = session.getClient().execute(request, new BoxClientErrorResponseHandler() { - @Override - public UploadedPart handleEntity(final HttpEntity entity1) throws IOException { - return new JSON().getContext(null).readValue(entity1.getContent(), UploadedPart.class); - } - }).getPart(); - log.debug("Received response {} for upload of {}", response, file); - return new File().size(response.getSize()).sha1(response.getSha1()).id(response.getPartId()); - } - catch(HttpResponseException e) { - throw new DefaultHttpResponseExceptionMappingService().map("Upload {0} failed", e, file); - } - catch(IOException e) { - throw new DefaultIOExceptionMappingService().map("Upload {0} failed", e, file); - } - } - - @Override - public long getContentLength() { - return -1L; - } - }; - return this.write(file, status, command); - } - - @Override - public ChecksumCompute checksum(final Path file, final TransferStatus status) { - return new SHA1ChecksumCompute(); - } - - @Override - public EnumSet features(final Path file) { - return EnumSet.of(Flags.checksum); - } -} diff --git a/box/src/main/java/ch/cyberduck/core/box/BoxDeleteFeature.java b/box/src/main/java/ch/cyberduck/core/box/BoxDeleteFeature.java index 96da2c7d1b..94fdfc7b4b 100644 --- a/box/src/main/java/ch/cyberduck/core/box/BoxDeleteFeature.java +++ b/box/src/main/java/ch/cyberduck/core/box/BoxDeleteFeature.java @@ -55,7 +55,7 @@ public class BoxDeleteFeature implements Delete { } @Override - public EnumSet features() { + public EnumSet features(final Path file) { return EnumSet.of(Flags.recursive); } } diff --git a/box/src/main/java/ch/cyberduck/core/box/BoxDirectoryFeature.java b/box/src/main/java/ch/cyberduck/core/box/BoxDirectoryFeature.java index e1235c72e2..faa6cce946 100644 --- a/box/src/main/java/ch/cyberduck/core/box/BoxDirectoryFeature.java +++ b/box/src/main/java/ch/cyberduck/core/box/BoxDirectoryFeature.java @@ -19,17 +19,19 @@ import ch.cyberduck.core.LocaleFactory; import ch.cyberduck.core.Path; import ch.cyberduck.core.box.io.swagger.client.ApiException; import ch.cyberduck.core.box.io.swagger.client.api.FoldersApi; +import ch.cyberduck.core.box.io.swagger.client.model.File; import ch.cyberduck.core.box.io.swagger.client.model.FoldersBody; import ch.cyberduck.core.box.io.swagger.client.model.FoldersParent; import ch.cyberduck.core.exception.BackgroundException; import ch.cyberduck.core.exception.InvalidFilenameException; import ch.cyberduck.core.features.Directory; +import ch.cyberduck.core.features.Write; import ch.cyberduck.core.transfer.TransferStatus; import java.text.MessageFormat; import java.util.Collections; -public class BoxDirectoryFeature implements Directory { +public class BoxDirectoryFeature implements Directory { private final BoxSession session; private final BoxFileidProvider fileid; @@ -40,7 +42,7 @@ public class BoxDirectoryFeature implements Directory { } @Override - public Path mkdir(final Path folder, final TransferStatus status) throws BackgroundException { + public Path mkdir(final Write writer, final Path folder, final TransferStatus status) throws BackgroundException { try { return new Path(folder).withAttributes(new BoxAttributesFinderFeature(session, fileid).toAttributes( new FoldersApi(new BoxApiClient(session.getClient())).postFolders(new FoldersBody() diff --git a/box/src/main/java/ch/cyberduck/core/box/BoxLargeUploadService.java b/box/src/main/java/ch/cyberduck/core/box/BoxLargeUploadService.java index ed9828feda..9dd7f0d7cf 100644 --- a/box/src/main/java/ch/cyberduck/core/box/BoxLargeUploadService.java +++ b/box/src/main/java/ch/cyberduck/core/box/BoxLargeUploadService.java @@ -28,7 +28,6 @@ import ch.cyberduck.core.box.io.swagger.client.model.UploadSession; import ch.cyberduck.core.concurrency.Interruptibles; import ch.cyberduck.core.exception.BackgroundException; import ch.cyberduck.core.exception.NotfoundException; -import ch.cyberduck.core.features.Upload; import ch.cyberduck.core.features.Write; import ch.cyberduck.core.http.HttpUploadFeature; import ch.cyberduck.core.io.BandwidthThrottle; @@ -65,23 +64,19 @@ public class BoxLargeUploadService extends HttpUploadFeature writer; - - public BoxLargeUploadService(final BoxSession session, final BoxFileidProvider fileid, final Write writer) { - this(session, fileid, writer, + public BoxLargeUploadService(final BoxSession session, final BoxFileidProvider fileid) { + this(session, fileid, HostPreferencesFactory.get(session.getHost()).getInteger("box.upload.multipart.concurrency")); } - public BoxLargeUploadService(final BoxSession session, final BoxFileidProvider fileid, final Write writer, final Integer concurrency) { - super(writer); + public BoxLargeUploadService(final BoxSession session, final BoxFileidProvider fileid, final Integer concurrency) { this.session = session; - this.writer = writer; this.concurrency = concurrency; this.fileid = fileid; } @Override - public File upload(final Path file, final Local local, final BandwidthThrottle throttle, final ProgressListener progress, final StreamListener streamListener, + public File upload(final Write 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 ThreadPool pool = ThreadPoolFactory.get("multipart", concurrency); try { @@ -95,7 +90,7 @@ public class BoxLargeUploadService extends HttpUploadFeature 0; partNumber++) { final long length = Math.min(uploadSession.getPartSize(), remaining); - parts.add(this.submit(pool, file, local, throttle, streamListener, status, + parts.add(this.submit(pool, write, file, local, throttle, streamListener, status, uploadSession.getId(), partNumber, offset, length, callback)); remaining -= length; offset += length; @@ -122,7 +117,7 @@ public class BoxLargeUploadService extends HttpUploadFeature submit(final ThreadPool pool, final Path file, final Local local, + private Future submit(final ThreadPool pool, final Write write, final Path file, final Local local, final BandwidthThrottle throttle, final StreamListener listener, final TransferStatus overall, final String uploadSessionId, final int partNumber, final long offset, final long length, final ConnectionCallback callback) { log.info("Submit {} to queue with offset {} and length {}", file, offset, length); @@ -137,13 +132,13 @@ public class BoxLargeUploadService extends HttpUploadFeature parameters = new HashMap<>(); parameters.put(UPLOAD_SESSION_ID, uploadSessionId); parameters.put(OVERALL_LENGTH, String.valueOf(overall.getLength())); status.setParameters(parameters); final File response = BoxLargeUploadService.this.upload( - file, local, throttle, listener, status, overall, status, callback); + write, file, local, throttle, listener, status, overall, status, callback); log.info("Received response {} for part {}", response, partNumber); return new Part(response, status); } @@ -160,9 +155,4 @@ public class BoxLargeUploadService extends HttpUploadFeature withWriter(final Write writer) { - this.writer = writer; - return super.withWriter(writer); - } } diff --git a/box/src/main/java/ch/cyberduck/core/box/BoxMultipartWriteFeature.java b/box/src/main/java/ch/cyberduck/core/box/BoxMultipartWriteFeature.java deleted file mode 100644 index ffa27deaba..0000000000 --- a/box/src/main/java/ch/cyberduck/core/box/BoxMultipartWriteFeature.java +++ /dev/null @@ -1,162 +0,0 @@ -package ch.cyberduck.core.box; - -/* - * Copyright (c) 2002-2021 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 ch.cyberduck.core.BytecountStreamListener; -import ch.cyberduck.core.ConnectionCallback; -import ch.cyberduck.core.Path; -import ch.cyberduck.core.box.io.swagger.client.JSON; -import ch.cyberduck.core.box.io.swagger.client.model.File; -import ch.cyberduck.core.box.io.swagger.client.model.Files; -import ch.cyberduck.core.box.io.swagger.client.model.UploadSession; -import ch.cyberduck.core.box.io.swagger.client.model.UploadedPart; -import ch.cyberduck.core.exception.BackgroundException; -import ch.cyberduck.core.features.Write; -import ch.cyberduck.core.http.HttpRange; -import ch.cyberduck.core.http.HttpResponseOutputStream; -import ch.cyberduck.core.io.ChecksumCompute; -import ch.cyberduck.core.io.MemorySegementingOutputStream; -import ch.cyberduck.core.io.SHA1ChecksumCompute; -import ch.cyberduck.core.transfer.TransferStatus; - -import org.apache.http.HttpEntity; -import org.apache.http.HttpHeaders; -import org.apache.http.client.methods.HttpPut; -import org.apache.http.entity.ByteArrayEntity; -import org.apache.http.message.BasicHeader; -import org.apache.logging.log4j.LogManager; -import org.apache.logging.log4j.Logger; - -import java.io.ByteArrayInputStream; -import java.io.IOException; -import java.io.OutputStream; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.List; -import java.util.concurrent.atomic.AtomicBoolean; -import java.util.concurrent.atomic.AtomicReference; -import java.util.stream.Collectors; - -public class BoxMultipartWriteFeature implements Write { - private static final Logger log = LogManager.getLogger(BoxMultipartWriteFeature.class); - - private final BoxSession session; - private final BoxFileidProvider fileid; - - public BoxMultipartWriteFeature(final BoxSession session, final BoxFileidProvider fileid) { - this.session = session; - this.fileid = fileid; - } - - @Override - public HttpResponseOutputStream write(final Path file, final TransferStatus status, final ConnectionCallback callback) throws BackgroundException { - final UploadSession uploadSession = new BoxUploadHelper(session, fileid).createUploadSession(status, file); - log.debug("Obtained session {} for file {}", uploadSession, file); - final BoxOutputStream proxy = new BoxOutputStream(file, uploadSession, status); - return new HttpResponseOutputStream(new MemorySegementingOutputStream(proxy, - uploadSession.getPartSize().intValue()), new BoxAttributesFinderFeature(session, fileid), status) { - @Override - public File getStatus() { - return proxy.getResult(); - } - }; - } - - private final class BoxOutputStream extends OutputStream { - private final Path file; - private final UploadSession uploadSession; - private final TransferStatus overall; - private final List checksums = new ArrayList<>(); - private final AtomicBoolean close = new AtomicBoolean(); - private final BytecountStreamListener byteCounter = new BytecountStreamListener(); - private final AtomicReference result = new AtomicReference<>(); - - public BoxOutputStream(final Path file, final UploadSession uploadSession, final TransferStatus status) { - this.file = file; - this.uploadSession = uploadSession; - this.overall = status; - } - - @Override - public void write(final int value) throws IOException { - throw new IOException(new UnsupportedOperationException()); - } - - @Override - public void write(byte[] buffer) throws IOException { - this.write(buffer, 0, buffer.length); - } - - @Override - public void write(final byte[] b, final int off, final int len) throws IOException { - final byte[] content = Arrays.copyOfRange(b, off, len); - try { - final HttpRange range = HttpRange.withStatus(new TransferStatus() - .setLength(content.length) - .setOffset(byteCounter.getSent())); - log.debug("Send range {} for file {}", range, file); - final HttpPut request = new HttpPut(String.format("https://upload.box.com/api/2.0/files/upload_sessions/%s", uploadSession.getId())); - // Must not overlap with the range of a part already uploaded this session. - request.addHeader(new BasicHeader(HttpHeaders.CONTENT_RANGE, String.format("bytes %d-%d/%d", range.getStart(), range.getEnd(), - overall.getOffset() + overall.getLength()))); - request.addHeader(new BasicHeader("Digest", String.format("sha=%s", - new SHA1ChecksumCompute().compute(new ByteArrayInputStream(content), overall).base64))); - request.setEntity(new ByteArrayEntity(content)); - checksums.add(session.getClient().execute(request, new BoxClientErrorResponseHandler() { - @Override - public UploadedPart handleEntity(final HttpEntity entity) throws IOException { - return new JSON().getContext(null).readValue(entity.getContent(), UploadedPart.class); - } - })); - byteCounter.sent(len); - } - catch(BackgroundException e) { - throw new IOException(e); - } - } - - @Override - public void close() throws IOException { - if(close.get()) { - log.warn("Skip double close of stream {}", this); - return; - } - try { - super.close(); - final Files files = new BoxUploadHelper(session, fileid).commitUploadSession(file, - uploadSession.getId(), overall, checksums.stream().map(UploadedPart::getPart).collect(Collectors.toList())); - if(files.getEntries().stream().findFirst().isPresent()) { - result.set(files.getEntries().stream().findFirst().get()); - } - } - catch(BackgroundException e) { - throw new IOException(e); - } - finally { - close.set(true); - } - } - - public File getResult() { - return result.get(); - } - } - - @Override - public ChecksumCompute checksum(final Path file, final TransferStatus status) { - return new SHA1ChecksumCompute(); - } -} diff --git a/box/src/main/java/ch/cyberduck/core/box/BoxProtocol.java b/box/src/main/java/ch/cyberduck/core/box/BoxProtocol.java index 71bd72cae0..d830e1ffec 100644 --- a/box/src/main/java/ch/cyberduck/core/box/BoxProtocol.java +++ b/box/src/main/java/ch/cyberduck/core/box/BoxProtocol.java @@ -18,9 +18,10 @@ package ch.cyberduck.core.box; import ch.cyberduck.core.AbstractProtocol; 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.ETagComparisonService; + import com.google.auto.service.AutoService; @AutoService(Protocol.class) @@ -80,7 +81,7 @@ public class BoxProtocol extends AbstractProtocol { @SuppressWarnings("unchecked") public T getFeature(final Class type) { if(type == ComparisonService.class) { - return (T) new DefaultComparisonService(new ETagComparisonService(), ComparisonService.disabled); + return (T) new DefaultComparisonService(new ChecksumComparisonService(), ComparisonService.disabled); } return super.getFeature(type); } diff --git a/box/src/main/java/ch/cyberduck/core/box/BoxSession.java b/box/src/main/java/ch/cyberduck/core/box/BoxSession.java index dda4388eef..bb55bb5608 100644 --- a/box/src/main/java/ch/cyberduck/core/box/BoxSession.java +++ b/box/src/main/java/ch/cyberduck/core/box/BoxSession.java @@ -38,7 +38,6 @@ import ch.cyberduck.core.features.Touch; import ch.cyberduck.core.features.Upload; import ch.cyberduck.core.features.Write; import ch.cyberduck.core.http.CustomServiceUnavailableRetryStrategy; -import ch.cyberduck.core.http.ExecutionCountServiceUnavailableRetryStrategy; import ch.cyberduck.core.http.HttpSession; import ch.cyberduck.core.oauth.OAuth2ErrorResponseInterceptor; import ch.cyberduck.core.oauth.OAuth2RequestInterceptor; @@ -71,14 +70,15 @@ public class BoxSession extends HttpSession { .withRedirectUri(host.getProtocol().getOAuthRedirectUrl()); configuration.addInterceptorLast(authorizationService); configuration.setServiceUnavailableRetryStrategy(new CustomServiceUnavailableRetryStrategy(host, - new ExecutionCountServiceUnavailableRetryStrategy(new OAuth2ErrorResponseInterceptor(host, authorizationService)))); + new OAuth2ErrorResponseInterceptor(host, authorizationService))); return configuration.build(); } @Override public void login(final LoginCallback prompt, final CancelCallback cancel) throws BackgroundException { + final Credentials credentials = host.getCredentials(); + credentials.setOauth(authorizationService.validate(credentials.getOauth())); try { - final Credentials credentials = authorizationService.validate(); credentials.setUsername(new UsersApi(new BoxApiClient(client)).getUsersMe(Collections.emptyList()).getLogin()); } catch(ApiException e) { @@ -87,15 +87,16 @@ public class BoxSession extends HttpSession { } @Override - protected 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(); } } diff --git a/box/src/main/java/ch/cyberduck/core/box/BoxSmallUploadService.java b/box/src/main/java/ch/cyberduck/core/box/BoxSmallUploadService.java index 7c5ff2b2d4..9fc312d39b 100644 --- a/box/src/main/java/ch/cyberduck/core/box/BoxSmallUploadService.java +++ b/box/src/main/java/ch/cyberduck/core/box/BoxSmallUploadService.java @@ -16,14 +16,9 @@ package ch.cyberduck.core.box; */ import ch.cyberduck.core.box.io.swagger.client.model.File; -import ch.cyberduck.core.features.Write; import ch.cyberduck.core.http.HttpUploadFeature; import java.security.MessageDigest; public class BoxSmallUploadService extends HttpUploadFeature { - - public BoxSmallUploadService(final BoxSession session, final BoxFileidProvider fileid, final Write writer) { - super(writer); - } } diff --git a/box/src/main/java/ch/cyberduck/core/box/BoxThresholdUploadService.java b/box/src/main/java/ch/cyberduck/core/box/BoxThresholdUploadService.java index e50ffb1b9f..46bea05e2f 100644 --- a/box/src/main/java/ch/cyberduck/core/box/BoxThresholdUploadService.java +++ b/box/src/main/java/ch/cyberduck/core/box/BoxThresholdUploadService.java @@ -36,31 +36,22 @@ public class BoxThresholdUploadService implements Upload { private final BoxFileidProvider fileid; private final VaultRegistry registry; - private Write writer; - public BoxThresholdUploadService(final BoxSession session, final BoxFileidProvider fileid, final VaultRegistry registry) { this.session = session; this.fileid = fileid; this.registry = registry; - this.writer = new BoxWriteFeature(session, fileid); } @Override - public File upload(final Path file, final Local local, final BandwidthThrottle throttle, final ProgressListener progress, final StreamListener streamListener, + public File upload(final Write 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.getLength())) { if(Vault.DISABLED == registry.find(session, file)) { - return new BoxLargeUploadService(session, fileid, new BoxChunkedWriteFeature(session, fileid)).upload(file, local, throttle, progress, streamListener, status, callback); + return new BoxLargeUploadService(session, fileid).upload(write, file, local, throttle, progress, streamListener, status, callback); } // Cannot comply with chunk size requirement from server } - return new BoxSmallUploadService(session, fileid, writer).upload(file, local, throttle, progress, streamListener, status, callback); - } - - @Override - public Upload withWriter(final Write writer) { - this.writer = writer; - return this; + return new BoxSmallUploadService().upload(write, file, local, throttle, progress, streamListener, status, callback); } protected boolean threshold(final Long length) { diff --git a/box/src/main/java/ch/cyberduck/core/box/BoxTouchFeature.java b/box/src/main/java/ch/cyberduck/core/box/BoxTouchFeature.java index 6925b44a78..daff6bda08 100644 --- a/box/src/main/java/ch/cyberduck/core/box/BoxTouchFeature.java +++ b/box/src/main/java/ch/cyberduck/core/box/BoxTouchFeature.java @@ -29,7 +29,7 @@ import java.text.MessageFormat; public class BoxTouchFeature extends DefaultTouchFeature { public BoxTouchFeature(final BoxSession session, final BoxFileidProvider fileid) { - super(new BoxWriteFeature(session, fileid)); + super(session); } @Override diff --git a/box/src/main/java/ch/cyberduck/core/box/BoxWriteFeature.java b/box/src/main/java/ch/cyberduck/core/box/BoxWriteFeature.java index c7b698053e..9dae6314ae 100644 --- a/box/src/main/java/ch/cyberduck/core/box/BoxWriteFeature.java +++ b/box/src/main/java/ch/cyberduck/core/box/BoxWriteFeature.java @@ -23,11 +23,14 @@ import ch.cyberduck.core.box.io.swagger.client.model.File; import ch.cyberduck.core.box.io.swagger.client.model.Files; import ch.cyberduck.core.box.io.swagger.client.model.FilescontentAttributes; import ch.cyberduck.core.box.io.swagger.client.model.FilescontentAttributesParent; +import ch.cyberduck.core.box.io.swagger.client.model.UploadPart; +import ch.cyberduck.core.box.io.swagger.client.model.UploadedPart; import ch.cyberduck.core.exception.BackgroundException; import ch.cyberduck.core.exception.NotfoundException; import ch.cyberduck.core.http.AbstractHttpWriteFeature; import ch.cyberduck.core.http.DefaultHttpResponseExceptionMappingService; import ch.cyberduck.core.http.DelayedHttpEntityCallable; +import ch.cyberduck.core.http.HttpRange; import ch.cyberduck.core.http.HttpResponseOutputStream; import ch.cyberduck.core.io.Checksum; import ch.cyberduck.core.io.ChecksumCompute; @@ -39,6 +42,7 @@ import org.apache.http.HttpEntity; import org.apache.http.HttpHeaders; import org.apache.http.client.HttpResponseException; import org.apache.http.client.methods.HttpPost; +import org.apache.http.client.methods.HttpPut; import org.apache.http.entity.ContentType; import org.apache.http.entity.mime.MultipartEntityBuilder; import org.apache.http.message.BasicHeader; @@ -67,74 +71,13 @@ public class BoxWriteFeature extends AbstractHttpWriteFeature { @Override public HttpResponseOutputStream write(final Path file, final TransferStatus status, final ConnectionCallback callback) throws BackgroundException { - final DelayedHttpEntityCallable command = new DelayedHttpEntityCallable(file) { - @Override - public File call(final HttpEntity entity) throws BackgroundException { - try { - final HttpPost request; - if(status.isExists()) { - request = new HttpPost(String.format("%s/files/%s/content?fields=%s", client.getBasePath(), - fileid.getFileId(file), - String.join(",", BoxAttributesFinderFeature.DEFAULT_FIELDS))); - } - else { - request = new HttpPost(String.format("%s/files/content?fields=%s", client.getBasePath(), - String.join(",", BoxAttributesFinderFeature.DEFAULT_FIELDS))); - } - final Checksum checksum = status.getChecksum(); - if(Checksum.NONE != checksum) { - switch(checksum.algorithm) { - case sha1: - request.addHeader(HttpHeaders.CONTENT_MD5, checksum.hash); - } - } - final ByteArrayOutputStream content = new ByteArrayOutputStream(); - new JSON().getContext(null).writeValue(content, new FilescontentAttributes() - .name(file.getName()) - .parent(new FilescontentAttributesParent().id(fileid.getFileId(file.getParent()))) - .contentCreatedAt(status.getCreated() != null ? new DateTime(status.getCreated()) : null) - .contentModifiedAt(status.getModified() != null ? new DateTime(status.getModified()) : null) - ); - final MultipartEntityBuilder multipart = MultipartEntityBuilder.create(); - multipart.addBinaryBody("attributes", content.toByteArray()); - final ByteArrayOutputStream out = new ByteArrayOutputStream(); - entity.writeTo(out); - multipart.addBinaryBody("file", out.toByteArray(), - null == status.getMime() ? ContentType.APPLICATION_OCTET_STREAM : ContentType.create(status.getMime()), file.getName()); - request.setEntity(multipart.build()); - if(status.isExists()) { - if(StringUtils.isNotBlank(status.getRemote().getETag())) { - request.addHeader(new BasicHeader(HttpHeaders.IF_MATCH, status.getRemote().getETag())); - } - else { - log.warn("Missing remote attributes in transfer status to read current ETag for {}", file); - } - } - final Files files = session.getClient().execute(request, new BoxClientErrorResponseHandler() { - @Override - public Files handleEntity(final HttpEntity entity) throws IOException { - return new JSON().getContext(null).readValue(entity.getContent(), Files.class); - } - }); - log.debug("Received response {} for upload of {}", files, file); - if(files.getEntries().stream().findFirst().isPresent()) { - return files.getEntries().stream().findFirst().get(); - } - throw new NotfoundException(file.getAbsolute()); - } - catch(HttpResponseException e) { - throw new DefaultHttpResponseExceptionMappingService().map("Upload {0} failed", e, file); - } - catch(IOException e) { - throw new DefaultIOExceptionMappingService().map("Upload {0} failed", e, file); - } - } - - @Override - public long getContentLength() { - return -1L; - } - }; + final DelayedHttpEntityCallable command; + if(status.isSegment()) { + command = new ChunkDelayedHttpEntityCallable(file, status); + } + else { + command = new MultipartDelayedHttpEntityCallable(file, status); + } return this.write(file, status, command); } @@ -147,4 +90,130 @@ public class BoxWriteFeature extends AbstractHttpWriteFeature { public EnumSet features(final Path file) { return EnumSet.of(Flags.timestamp, Flags.checksum, Flags.mime); } + + private class MultipartDelayedHttpEntityCallable extends DelayedHttpEntityCallable { + private final Path file; + private final TransferStatus status; + + public MultipartDelayedHttpEntityCallable(final Path file, final TransferStatus status) { + super(file); + this.file = file; + this.status = status; + } + + @Override + public File call(final HttpEntity entity) throws BackgroundException { + try { + final HttpPost request; + if(status.isExists()) { + request = new HttpPost(String.format("%s/files/%s/content?fields=%s", client.getBasePath(), + fileid.getFileId(file), + String.join(",", BoxAttributesFinderFeature.DEFAULT_FIELDS))); + } + else { + request = new HttpPost(String.format("%s/files/content?fields=%s", client.getBasePath(), + String.join(",", BoxAttributesFinderFeature.DEFAULT_FIELDS))); + } + final Checksum checksum = status.getChecksum(); + if(Checksum.NONE != checksum) { + switch(checksum.algorithm) { + case sha1: + request.addHeader(HttpHeaders.CONTENT_MD5, checksum.hash); + } + } + final ByteArrayOutputStream content = new ByteArrayOutputStream(); + new JSON().getContext(null).writeValue(content, new FilescontentAttributes() + .name(file.getName()) + .parent(new FilescontentAttributesParent().id(fileid.getFileId(file.getParent()))) + .contentCreatedAt(status.getCreated() != null ? new DateTime(status.getCreated()) : null) + .contentModifiedAt(status.getModified() != null ? new DateTime(status.getModified()) : null) + ); + final MultipartEntityBuilder multipart = MultipartEntityBuilder.create(); + multipart.addBinaryBody("attributes", content.toByteArray()); + final ByteArrayOutputStream out = new ByteArrayOutputStream(); + entity.writeTo(out); + multipart.addBinaryBody("file", out.toByteArray(), + null == status.getMime() ? ContentType.APPLICATION_OCTET_STREAM : ContentType.create(status.getMime()), file.getName()); + request.setEntity(multipart.build()); + if(status.isExists()) { + if(StringUtils.isNotBlank(status.getRemote().getETag())) { + request.addHeader(new BasicHeader(HttpHeaders.IF_MATCH, status.getRemote().getETag())); + } + else { + log.warn("Missing remote attributes in transfer status to read current ETag for {}", file); + } + } + final Files files = session.getClient().execute(request, new BoxClientErrorResponseHandler() { + @Override + public Files handleEntity(final HttpEntity entity) throws IOException { + return new JSON().getContext(null).readValue(entity.getContent(), Files.class); + } + }); + log.debug("Received response {} for upload of {}", files, file); + if(files.getEntries().stream().findFirst().isPresent()) { + return files.getEntries().stream().findFirst().get(); + } + throw new NotfoundException(file.getAbsolute()); + } + catch(HttpResponseException e) { + throw new DefaultHttpResponseExceptionMappingService().map("Upload {0} failed", e, file); + } + catch(IOException e) { + throw new DefaultIOExceptionMappingService().map("Upload {0} failed", e, file); + } + } + + @Override + public long getContentLength() { + return -1L; + } + } + + private class ChunkDelayedHttpEntityCallable extends DelayedHttpEntityCallable { + private final Path file; + private final TransferStatus status; + + public ChunkDelayedHttpEntityCallable(final Path file, final TransferStatus status) { + super(file); + this.file = file; + this.status = status; + } + + @Override + public File call(final HttpEntity entity) throws BackgroundException { + try { + final HttpRange range = HttpRange.withStatus(new TransferStatus() + .setLength(status.getLength()) + .setOffset(status.getOffset())); + final String uploadSessionId = status.getParameters().get(BoxLargeUploadService.UPLOAD_SESSION_ID); + final String overall_length = status.getParameters().get(BoxLargeUploadService.OVERALL_LENGTH); + log.debug("Send range {} for file {}", range, file); + final HttpPut request = new HttpPut(String.format("%s/files/upload_sessions/%s", client.getBasePath(), uploadSessionId)); + // Must not overlap with the range of a part already uploaded this session. + request.addHeader(new BasicHeader(HttpHeaders.CONTENT_RANGE, String.format("bytes %d-%d/%d", range.getStart(), range.getEnd(), + Long.valueOf(overall_length)))); + request.addHeader(new BasicHeader("Digest", String.format("sha=%s", status.getChecksum().base64))); + request.setEntity(entity); + final UploadPart response = session.getClient().execute(request, new BoxClientErrorResponseHandler() { + @Override + public UploadedPart handleEntity(final HttpEntity entity1) throws IOException { + return new JSON().getContext(null).readValue(entity1.getContent(), UploadedPart.class); + } + }).getPart(); + log.debug("Received response {} for upload of {}", response, file); + return new File().size(response.getSize()).sha1(response.getSha1()).id(response.getPartId()); + } + catch(HttpResponseException e) { + throw new DefaultHttpResponseExceptionMappingService().map("Upload {0} failed", e, file); + } + catch(IOException e) { + throw new DefaultIOExceptionMappingService().map("Upload {0} failed", e, file); + } + } + + @Override + public long getContentLength() { + return -1L; + } + } } diff --git a/box/src/test/java/ch/cyberduck/core/box/BoxAttributesFinderFeatureTest.java b/box/src/test/java/ch/cyberduck/core/box/BoxAttributesFinderFeatureTest.java index ff99da9162..cd39885e45 100644 --- a/box/src/test/java/ch/cyberduck/core/box/BoxAttributesFinderFeatureTest.java +++ b/box/src/test/java/ch/cyberduck/core/box/BoxAttributesFinderFeatureTest.java @@ -39,7 +39,7 @@ public class BoxAttributesFinderFeatureTest extends AbstractBoxTest { public void testFindNotFound() throws Exception { final BoxFileidProvider fileid = new BoxFileidProvider(session); final Path folder = new BoxDirectoryFeature(session, fileid).mkdir( - new Path(new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory)), new TransferStatus()); + new BoxWriteFeature(session, fileid), new Path(new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory)), new TransferStatus()); final Path test = new Path(folder, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)); final BoxAttributesFinderFeature f = new BoxAttributesFinderFeature(session, fileid); try { @@ -62,10 +62,10 @@ public class BoxAttributesFinderFeatureTest extends AbstractBoxTest { public void testFindFile() throws Exception { final BoxFileidProvider fileid = new BoxFileidProvider(session); final Path folder = new BoxDirectoryFeature(session, fileid).mkdir( - new Path(new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory)), new TransferStatus()); + new BoxWriteFeature(session, fileid), new Path(new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory)), new TransferStatus()); final long folderModification = new BoxAttributesFinderFeature(session, fileid).find(folder).getModificationDate(); final Path test = new BoxTouchFeature(session, fileid) - .touch(new Path(folder, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)), new TransferStatus().setLength(0L)); + .touch(new BoxWriteFeature(session, fileid), new Path(folder, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)), new TransferStatus().setLength(0L)); assertEquals(folderModification, new BoxAttributesFinderFeature(session, fileid).find(folder).getModificationDate(), 0L); final BoxAttributesFinderFeature f = new BoxAttributesFinderFeature(session, fileid); final PathAttributes attributes = f.find(test); @@ -91,8 +91,8 @@ public class BoxAttributesFinderFeatureTest extends AbstractBoxTest { public void testFindDirectory() throws Exception { final BoxFileidProvider fileid = new BoxFileidProvider(session); final Path folder = new BoxDirectoryFeature(session, fileid).mkdir( - new Path(new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory)), new TransferStatus()); - final Path test = new BoxDirectoryFeature(session, fileid).mkdir(new Path(folder, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory)), new TransferStatus()); + new BoxWriteFeature(session, fileid), new Path(new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory)), new TransferStatus()); + final Path test = new BoxDirectoryFeature(session, fileid).mkdir(new BoxWriteFeature(session, fileid), new Path(folder, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory)), new TransferStatus()); final BoxAttributesFinderFeature f = new BoxAttributesFinderFeature(session, fileid); final PathAttributes attributes = f.find(test); assertNotEquals(-1L, attributes.getSize()); diff --git a/box/src/test/java/ch/cyberduck/core/box/BoxCopyFeatureTest.java b/box/src/test/java/ch/cyberduck/core/box/BoxCopyFeatureTest.java index cc35c91640..aa6aa824c2 100644 --- a/box/src/test/java/ch/cyberduck/core/box/BoxCopyFeatureTest.java +++ b/box/src/test/java/ch/cyberduck/core/box/BoxCopyFeatureTest.java @@ -45,7 +45,7 @@ public class BoxCopyFeatureTest extends AbstractBoxTest { public void testCopyFile() throws Exception { final BoxFileidProvider fileid = new BoxFileidProvider(session); final Path test = new Path(new DefaultHomeFinderService(session).find(), new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)); - new BoxTouchFeature(session, fileid).touch(test, new TransferStatus()); + new BoxTouchFeature(session, fileid).touch(new BoxWriteFeature(session, fileid), test, new TransferStatus()); final Path copy = new Path(new DefaultHomeFinderService(session).find(), new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)); new BoxCopyFeature(session, fileid).copy(test, copy, new TransferStatus(), new DisabledConnectionCallback(), new DisabledStreamListener()); assertTrue(new BoxFindFeature(session, fileid).find(test.withAttributes(PathAttributes.EMPTY))); @@ -58,11 +58,11 @@ public class BoxCopyFeatureTest extends AbstractBoxTest { public void testCopyOverride() throws Exception { final BoxFileidProvider fileid = new BoxFileidProvider(session); final Path folder = new Path(new DefaultHomeFinderService(session).find(), new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory)); - new BoxDirectoryFeature(session, fileid).mkdir(folder, new TransferStatus()); + new BoxDirectoryFeature(session, fileid).mkdir(new BoxWriteFeature(session, fileid), folder, new TransferStatus()); final Path test = new Path(folder, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)); - new BoxTouchFeature(session, fileid).touch(test, new TransferStatus()); + new BoxTouchFeature(session, fileid).touch(new BoxWriteFeature(session, fileid), test, new TransferStatus()); final Path copy = new Path(folder, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)); - final Path existing = new BoxTouchFeature(session, fileid).touch(copy, new TransferStatus()); + final Path existing = new BoxTouchFeature(session, fileid).touch(new BoxWriteFeature(session, fileid), copy, new TransferStatus()); new BoxCopyFeature(session, fileid).copy(test, copy, new TransferStatus().setExists(true).setRemote(existing.attributes()), new DisabledConnectionCallback(), new DisabledStreamListener()); final Find find = new DefaultFindFeature(session); assertTrue(find.find(test)); @@ -73,10 +73,10 @@ public class BoxCopyFeatureTest extends AbstractBoxTest { @Test public void testCopyDirectory() throws Exception { final BoxFileidProvider fileid = new BoxFileidProvider(session); - final Path directory = new BoxDirectoryFeature(session, fileid).mkdir(new Path(new DefaultHomeFinderService(session).find(), + final Path directory = new BoxDirectoryFeature(session, fileid).mkdir(new BoxWriteFeature(session, fileid), new Path(new DefaultHomeFinderService(session).find(), new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory)), new TransferStatus()); final String name = new AlphanumericRandomStringService().random(); - final Path file = new BoxTouchFeature(session, fileid).touch(new Path(directory, name, EnumSet.of(Path.Type.file)), new TransferStatus()); + final Path file = new BoxTouchFeature(session, fileid).touch(new BoxWriteFeature(session, fileid), new Path(directory, name, EnumSet.of(Path.Type.file)), new TransferStatus()); final Path copy = new Path(new DefaultHomeFinderService(session).find(), new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory)); new BoxCopyFeature(session, fileid).copy(directory, copy, new TransferStatus(), new DisabledConnectionCallback(), new DisabledStreamListener()); assertTrue(new BoxFindFeature(session, fileid).find(file)); diff --git a/box/src/test/java/ch/cyberduck/core/box/BoxDeleteFeatureTest.java b/box/src/test/java/ch/cyberduck/core/box/BoxDeleteFeatureTest.java index d71152c2c4..5a13240bd1 100644 --- a/box/src/test/java/ch/cyberduck/core/box/BoxDeleteFeatureTest.java +++ b/box/src/test/java/ch/cyberduck/core/box/BoxDeleteFeatureTest.java @@ -50,7 +50,7 @@ public class BoxDeleteFeatureTest extends AbstractBoxTest { @Test public void testDeleteFolder() throws Exception { final BoxFileidProvider fileid = new BoxFileidProvider(session); - final Path directory = new BoxDirectoryFeature(session, fileid).mkdir(new Path( + final Path directory = new BoxDirectoryFeature(session, fileid).mkdir(new BoxWriteFeature(session, fileid), new Path( new AlphanumericRandomStringService().random(), EnumSet.of(AbstractPath.Type.directory)), new TransferStatus()); assertTrue(new BoxFindFeature(session, fileid).find(directory, new DisabledListProgressListener())); new BoxDeleteFeature(session, fileid).delete(Collections.singletonList(directory), new DisabledLoginCallback(), new Delete.DisabledCallback()); @@ -60,9 +60,9 @@ public class BoxDeleteFeatureTest extends AbstractBoxTest { @Test public void testDeleteMultipleFiles() throws Exception { final BoxFileidProvider fileid = new BoxFileidProvider(session); - final Path folder = new BoxDirectoryFeature(session, fileid).mkdir(new Path(new AlphanumericRandomStringService().random(), EnumSet.of(AbstractPath.Type.directory)), new TransferStatus()); - final Path file1 = new BoxTouchFeature(session, fileid).touch(new Path(folder, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)), new TransferStatus()); - final Path file2 = new BoxTouchFeature(session, fileid).touch(new Path(folder, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)), new TransferStatus()); + final Path folder = new BoxDirectoryFeature(session, fileid).mkdir(new BoxWriteFeature(session, fileid), new Path(new AlphanumericRandomStringService().random(), EnumSet.of(AbstractPath.Type.directory)), new TransferStatus()); + final Path file1 = new BoxTouchFeature(session, fileid).touch(new BoxWriteFeature(session, fileid), new Path(folder, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)), new TransferStatus()); + final Path file2 = new BoxTouchFeature(session, fileid).touch(new BoxWriteFeature(session, fileid), new Path(folder, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)), new TransferStatus()); assertTrue(new BoxFindFeature(session, fileid).find(file1)); assertTrue(new BoxFindFeature(session, fileid).find(file2)); new BoxDeleteFeature(session, fileid).delete(Arrays.asList(file1, file2), new DisabledLoginCallback(), new Delete.DisabledCallback()); diff --git a/box/src/test/java/ch/cyberduck/core/box/BoxDirectoryFeatureTest.java b/box/src/test/java/ch/cyberduck/core/box/BoxDirectoryFeatureTest.java index e37c03b7c6..f489a38d66 100644 --- a/box/src/test/java/ch/cyberduck/core/box/BoxDirectoryFeatureTest.java +++ b/box/src/test/java/ch/cyberduck/core/box/BoxDirectoryFeatureTest.java @@ -40,12 +40,12 @@ public class BoxDirectoryFeatureTest extends AbstractBoxTest { public void testCreateDirectory() throws Exception { final BoxFileidProvider fileid = new BoxFileidProvider(session); final Path folder = new BoxDirectoryFeature(session, fileid).mkdir( - new Path(new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory)), new TransferStatus()); + new BoxWriteFeature(session, fileid), new Path(new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory)), new TransferStatus()); assertTrue(new DefaultFindFeature(session).find(folder)); assertTrue(new BoxFindFeature(session, fileid).find(folder)); assertEquals(0L, folder.attributes().getSize()); assertNotEquals(-1L, folder.attributes().getModificationDate()); - assertThrows(ConflictException.class, () -> new BoxDirectoryFeature(session, fileid).mkdir(folder, new TransferStatus())); + assertThrows(ConflictException.class, () -> new BoxDirectoryFeature(session, fileid).mkdir(new BoxWriteFeature(session, fileid), folder, new TransferStatus())); new BoxDeleteFeature(session, fileid).delete(Collections.singletonList(folder), new DisabledLoginCallback(), new Delete.DisabledCallback()); assertFalse(new DefaultFindFeature(session).find(folder)); } diff --git a/box/src/test/java/ch/cyberduck/core/box/BoxFileidProviderTest.java b/box/src/test/java/ch/cyberduck/core/box/BoxFileidProviderTest.java index 771504fb30..7f7c36bfac 100644 --- a/box/src/test/java/ch/cyberduck/core/box/BoxFileidProviderTest.java +++ b/box/src/test/java/ch/cyberduck/core/box/BoxFileidProviderTest.java @@ -48,7 +48,7 @@ public class BoxFileidProviderTest extends AbstractBoxTest { final BoxFileidProvider nodeid = new BoxFileidProvider(session); final Path home = new DefaultHomeFinderService(session).find(); final String name = String.format("%s%s", new AlphanumericRandomStringService().random(), new AlphanumericRandomStringService().random()); - final Path file = new BoxTouchFeature(session, nodeid).touch(new Path(home, name, EnumSet.of(Path.Type.file)), new TransferStatus()); + final Path file = new BoxTouchFeature(session, nodeid).touch(new BoxWriteFeature(session, nodeid), new Path(home, name, EnumSet.of(Path.Type.file)), new TransferStatus()); nodeid.clear(); final String nodeId = nodeid.getFileId(new Path(home, name, EnumSet.of(Path.Type.file))); assertNotNull(nodeId); diff --git a/box/src/test/java/ch/cyberduck/core/box/BoxFindFeatureTest.java b/box/src/test/java/ch/cyberduck/core/box/BoxFindFeatureTest.java index 7491d5445c..591a57ee18 100644 --- a/box/src/test/java/ch/cyberduck/core/box/BoxFindFeatureTest.java +++ b/box/src/test/java/ch/cyberduck/core/box/BoxFindFeatureTest.java @@ -51,7 +51,7 @@ public class BoxFindFeatureTest extends AbstractBoxTest { public void testFindDirectory() throws Exception { final BoxFileidProvider fileid = new BoxFileidProvider(session); final Path folder = new BoxDirectoryFeature(session, fileid).mkdir( - new Path(new DefaultHomeFinderService(session).find(), new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory)), new TransferStatus()); + new BoxWriteFeature(session, fileid), new Path(new DefaultHomeFinderService(session).find(), new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory)), new TransferStatus()); assertTrue(new BoxFindFeature(session, fileid).find(folder)); assertFalse(new BoxFindFeature(session, fileid).find(new Path(folder.getAbsolute(), EnumSet.of(Path.Type.file)))); new BoxDeleteFeature(session, fileid).delete(Collections.singletonList(folder), new DisabledLoginCallback(), new Delete.DisabledCallback()); @@ -61,7 +61,7 @@ public class BoxFindFeatureTest extends AbstractBoxTest { public void testFindFile() throws Exception { final BoxFileidProvider fileid = new BoxFileidProvider(session); final Path file = new Path(new DefaultHomeFinderService(session).find(), new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)); - new BoxTouchFeature(session, fileid).touch(file, new TransferStatus()); + new BoxTouchFeature(session, fileid).touch(new BoxWriteFeature(session, fileid), file, new TransferStatus()); assertTrue(new BoxFindFeature(session, fileid).find(file)); assertFalse(new BoxFindFeature(session, fileid).find(new Path(file.getAbsolute(), EnumSet.of(Path.Type.directory)))); new BoxDeleteFeature(session, fileid).delete(Collections.singletonList(file), new DisabledLoginCallback(), new Delete.DisabledCallback()); diff --git a/box/src/test/java/ch/cyberduck/core/box/BoxLargeUploadServiceTest.java b/box/src/test/java/ch/cyberduck/core/box/BoxLargeUploadServiceTest.java index 6422077425..2112d9f445 100644 --- a/box/src/test/java/ch/cyberduck/core/box/BoxLargeUploadServiceTest.java +++ b/box/src/test/java/ch/cyberduck/core/box/BoxLargeUploadServiceTest.java @@ -46,8 +46,8 @@ public class BoxLargeUploadServiceTest extends AbstractBoxTest { @Test public void testUploadLargeFileInChunks() throws Exception { final BoxFileidProvider fileid = new BoxFileidProvider(session); - final BoxLargeUploadService s = new BoxLargeUploadService(session, fileid, new BoxChunkedWriteFeature(session, fileid)); - final Path container = new BoxDirectoryFeature(session, fileid).mkdir(new Path( + final BoxLargeUploadService s = new BoxLargeUploadService(session, fileid); + final Path container = new BoxDirectoryFeature(session, fileid).mkdir(new BoxWriteFeature(session, fileid), new Path( new AlphanumericRandomStringService().random(), EnumSet.of(AbstractPath.Type.directory)), new TransferStatus()); final String name = new AlphanumericRandomStringService().random(); final Path file = new Path(container, name, EnumSet.of(Path.Type.file)); @@ -58,7 +58,7 @@ public class BoxLargeUploadServiceTest extends AbstractBoxTest { status.setChecksum(new SHA1ChecksumCompute().compute(local.getInputStream(), new TransferStatus())); status.setLength(content.length); final BytecountStreamListener count = new BytecountStreamListener(); - final File response = s.upload(file, local, new BandwidthThrottle(BandwidthThrottle.UNLIMITED), new DisabledProgressListener(), count, status, new DisabledConnectionCallback()); + final File response = s.upload(new BoxWriteFeature(session, fileid), file, local, new BandwidthThrottle(BandwidthThrottle.UNLIMITED), new DisabledProgressListener(), count, status, new DisabledConnectionCallback()); assertTrue(status.isComplete()); assertNotNull(response.getSha1()); assertEquals(content.length, count.getSent()); diff --git a/box/src/test/java/ch/cyberduck/core/box/BoxMoveFeatureTest.java b/box/src/test/java/ch/cyberduck/core/box/BoxMoveFeatureTest.java index 51b7ebc662..766f9b61ff 100644 --- a/box/src/test/java/ch/cyberduck/core/box/BoxMoveFeatureTest.java +++ b/box/src/test/java/ch/cyberduck/core/box/BoxMoveFeatureTest.java @@ -43,7 +43,7 @@ public class BoxMoveFeatureTest extends AbstractBoxTest { @Test public void testMove() throws Exception { final BoxFileidProvider fileid = new BoxFileidProvider(session); - final Path test = new BoxTouchFeature(session, fileid).touch(new Path(new DefaultHomeFinderService(session).find(), new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)), new TransferStatus()); + final Path test = new BoxTouchFeature(session, fileid).touch(new BoxWriteFeature(session, fileid), new Path(new DefaultHomeFinderService(session).find(), new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)), new TransferStatus()); assertNotNull(test.attributes().getFileId()); assertEquals(0L, test.attributes().getSize()); assertNotEquals(-1L, test.attributes().getModificationDate()); @@ -53,7 +53,9 @@ public class BoxMoveFeatureTest extends AbstractBoxTest { assertTrue(new BoxFindFeature(session, fileid).find(target)); assertEquals(test.attributes().getModificationDate(), target.attributes().getModificationDate()); assertEquals(test.attributes().getChecksum(), target.attributes().getChecksum()); - assertEquals(Comparison.equal, session.getHost().getProtocol().getFeature(ComparisonService.class).compare(Path.Type.file, target.attributes(), new BoxAttributesFinderFeature(session, fileid).find(target))); + assertNotEquals(test.attributes().getETag(), target.attributes().getETag()); + assertEquals(target.attributes(), new BoxAttributesFinderFeature(session, fileid).find(target)); + assertEquals(Comparison.equal, session.getHost().getProtocol().getFeature(ComparisonService.class).compare(Path.Type.file, test.attributes(), target.attributes())); new BoxDeleteFeature(session, fileid).delete(Collections.singletonList(target), new DisabledLoginCallback(), new Delete.DisabledCallback()); } @@ -61,7 +63,7 @@ public class BoxMoveFeatureTest extends AbstractBoxTest { public void testMoveDirectory() throws Exception { final BoxFileidProvider fileid = new BoxFileidProvider(session); final Path test = new Path(new DefaultHomeFinderService(session).find(), new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory)); - new BoxDirectoryFeature(session, fileid).mkdir(test, new TransferStatus()); + new BoxDirectoryFeature(session, fileid).mkdir(new BoxWriteFeature(session, fileid), test, new TransferStatus()); final Path target = new Path(new DefaultHomeFinderService(session).find(), new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory)); new BoxMoveFeature(session, fileid).move(test, target, new TransferStatus(), new Delete.DisabledCallback(), new DisabledConnectionCallback()); assertFalse(new BoxFindFeature(session, fileid).find(test.withAttributes(PathAttributes.EMPTY))); @@ -73,9 +75,9 @@ public class BoxMoveFeatureTest extends AbstractBoxTest { public void testMoveOverride() throws Exception { final BoxFileidProvider fileid = new BoxFileidProvider(session); final Path test = new BoxTouchFeature(session, fileid).touch( - new Path(new DefaultHomeFinderService(session).find(), new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)), new TransferStatus()); + new BoxWriteFeature(session, fileid), new Path(new DefaultHomeFinderService(session).find(), new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)), new TransferStatus()); final Path target = new BoxTouchFeature(session, fileid).touch( - new Path(new DefaultHomeFinderService(session).find(), new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)), new TransferStatus()); + new BoxWriteFeature(session, fileid), new Path(new DefaultHomeFinderService(session).find(), new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)), new TransferStatus()); final Path override = new BoxMoveFeature(session, fileid).move(test, target, new TransferStatus().setExists(true).setRemote(target.attributes()), new Delete.DisabledCallback(), new DisabledConnectionCallback()); assertFalse(new BoxFindFeature(session, fileid).find(test)); @@ -96,7 +98,7 @@ public class BoxMoveFeatureTest extends AbstractBoxTest { public void testRenameCaseOnly() throws Exception { final BoxFileidProvider fileid = new BoxFileidProvider(session); final String name = new AlphanumericRandomStringService().random(); - final Path file = new BoxTouchFeature(session, fileid).touch(new Path(new DefaultHomeFinderService(session).find(), StringUtils.capitalize(name), EnumSet.of(Path.Type.file)), new TransferStatus()); + final Path file = new BoxTouchFeature(session, fileid).touch(new BoxWriteFeature(session, fileid), new Path(new DefaultHomeFinderService(session).find(), StringUtils.capitalize(name), EnumSet.of(Path.Type.file)), new TransferStatus()); final Path rename = new Path(new DefaultHomeFinderService(session).find(), StringUtils.lowerCase(name), EnumSet.of(Path.Type.file)); new BoxMoveFeature(session, fileid).move(file, rename, new TransferStatus().setExists(true), new Delete.DisabledCallback(), new DisabledConnectionCallback()); new BoxDeleteFeature(session, fileid).delete(Collections.singletonList(rename), new DisabledLoginCallback(), new Delete.DisabledCallback()); diff --git a/box/src/test/java/ch/cyberduck/core/box/BoxMultipartWriteFeatureTest.java b/box/src/test/java/ch/cyberduck/core/box/BoxMultipartWriteFeatureTest.java deleted file mode 100644 index 41c2721a5b..0000000000 --- a/box/src/test/java/ch/cyberduck/core/box/BoxMultipartWriteFeatureTest.java +++ /dev/null @@ -1,114 +0,0 @@ -package ch.cyberduck.core.box; - -/* - * Copyright (c) 2002-2021 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 ch.cyberduck.core.AlphanumericRandomStringService; -import ch.cyberduck.core.BytecountStreamListener; -import ch.cyberduck.core.DisabledConnectionCallback; -import ch.cyberduck.core.DisabledLoginCallback; -import ch.cyberduck.core.Path; -import ch.cyberduck.core.PathAttributes; -import ch.cyberduck.core.box.io.swagger.client.model.File; -import ch.cyberduck.core.features.Delete; -import ch.cyberduck.core.features.Home; -import ch.cyberduck.core.http.HttpResponseOutputStream; -import ch.cyberduck.core.io.StreamCopier; -import ch.cyberduck.core.shared.DefaultFindFeature; -import ch.cyberduck.core.transfer.TransferStatus; -import ch.cyberduck.test.IntegrationTest; - -import org.apache.commons.io.IOUtils; -import org.apache.commons.lang3.RandomUtils; -import org.junit.Test; -import org.junit.experimental.categories.Category; - -import java.io.ByteArrayInputStream; -import java.io.InputStream; -import java.util.Collections; -import java.util.EnumSet; - -import static org.junit.Assert.*; - -@Category(IntegrationTest.class) -public class BoxMultipartWriteFeatureTest extends AbstractBoxTest { - - @Test - public void testWriteMultiplePartsExisting() throws Exception { - final BoxFileidProvider fileid = new BoxFileidProvider(session); - final BoxMultipartWriteFeature feature = new BoxMultipartWriteFeature(session, fileid); - // Makes sure to test overwrite - final Path file = new BoxTouchFeature(session, fileid).touch( - new Path(Home.root(), new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)), new TransferStatus()); - final byte[] content = RandomUtils.nextBytes(21 * 1024 * 1024); - final TransferStatus status = new TransferStatus() - .setRemote(file.attributes()) - .setExists(true) - .setChecksum(feature.checksum(file, new TransferStatus()).compute(new ByteArrayInputStream(content), new TransferStatus())) - .setLength(content.length); - final HttpResponseOutputStream out = feature.write(file, status, new DisabledConnectionCallback()); - final ByteArrayInputStream in = new ByteArrayInputStream(content); - final TransferStatus progress = new TransferStatus(); - final BytecountStreamListener count = new BytecountStreamListener(); - new StreamCopier(new TransferStatus(), progress).withListener(count).transfer(in, out); - assertEquals(content.length, count.getSent()); - in.close(); - out.close(); - assertNotNull(out.getStatus()); - assertTrue(new DefaultFindFeature(session).find(file)); - assertTrue(new BoxFindFeature(session, fileid).find(file)); - final PathAttributes attributes = new BoxAttributesFinderFeature(session, fileid).find(file); - assertEquals(content.length, attributes.getSize()); - final byte[] compare = new byte[content.length]; - final InputStream stream = new BoxReadFeature(session, fileid).read(file, new TransferStatus().setLength(content.length), new DisabledConnectionCallback()); - IOUtils.readFully(stream, compare); - stream.close(); - assertArrayEquals(content, compare); - new BoxDeleteFeature(session, fileid).delete(Collections.singletonList(file), new DisabledLoginCallback(), new Delete.DisabledCallback()); - } - - @Test - public void testWriteMultipleParts() throws Exception { - final BoxFileidProvider fileid = new BoxFileidProvider(session); - final BoxMultipartWriteFeature feature = new BoxMultipartWriteFeature(session, fileid); - // Makes sure to test overwrite - final Path file = new Path(Home.root(), new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)); - final byte[] content = RandomUtils.nextBytes(21 * 1024 * 1024); - final TransferStatus status = new TransferStatus() - .setRemote(file.attributes()) - .setExists(false) - .setChecksum(feature.checksum(file, new TransferStatus()).compute(new ByteArrayInputStream(content), new TransferStatus())) - .setLength(content.length); - final HttpResponseOutputStream out = feature.write(file, status, new DisabledConnectionCallback()); - final ByteArrayInputStream in = new ByteArrayInputStream(content); - final TransferStatus progress = new TransferStatus(); - final BytecountStreamListener count = new BytecountStreamListener(); - new StreamCopier(new TransferStatus(), progress).withListener(count).transfer(in, out); - assertEquals(content.length, count.getSent()); - in.close(); - out.close(); - assertNotNull(out.getStatus()); - assertTrue(new DefaultFindFeature(session).find(file)); - assertTrue(new BoxFindFeature(session, fileid).find(file)); - final PathAttributes attributes = new BoxAttributesFinderFeature(session, fileid).find(file); - assertEquals(content.length, attributes.getSize()); - final byte[] compare = new byte[content.length]; - final InputStream stream = new BoxReadFeature(session, fileid).read(file, new TransferStatus().setLength(content.length), new DisabledConnectionCallback()); - IOUtils.readFully(stream, compare); - stream.close(); - assertArrayEquals(content, compare); - new BoxDeleteFeature(session, fileid).delete(Collections.singletonList(file), new DisabledLoginCallback(), new Delete.DisabledCallback()); - } -} diff --git a/box/src/test/java/ch/cyberduck/core/box/BoxReadFeatureTest.java b/box/src/test/java/ch/cyberduck/core/box/BoxReadFeatureTest.java index db516a4114..d9c1e67aa5 100644 --- a/box/src/test/java/ch/cyberduck/core/box/BoxReadFeatureTest.java +++ b/box/src/test/java/ch/cyberduck/core/box/BoxReadFeatureTest.java @@ -39,7 +39,7 @@ public class BoxReadFeatureTest extends AbstractBoxTest { public void testReadZeroLength() throws Exception { final BoxFileidProvider fileid = new BoxFileidProvider(session); final Path test = new Path(new DefaultHomeFinderService(session).find(), new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)); - new BoxTouchFeature(session, fileid).touch(test, new TransferStatus()); + new BoxTouchFeature(session, fileid).touch(new BoxWriteFeature(session, fileid), test, new TransferStatus()); final InputStream in = new BoxReadFeature(session, fileid).read(test, new TransferStatus().setLength(0L), new DisabledConnectionCallback()); assertNotNull(in); in.close(); diff --git a/box/src/test/java/ch/cyberduck/core/box/BoxShareFeatureTest.java b/box/src/test/java/ch/cyberduck/core/box/BoxShareFeatureTest.java index 4f99f97d09..bfea9d07b1 100644 --- a/box/src/test/java/ch/cyberduck/core/box/BoxShareFeatureTest.java +++ b/box/src/test/java/ch/cyberduck/core/box/BoxShareFeatureTest.java @@ -38,7 +38,7 @@ public class BoxShareFeatureTest extends AbstractBoxTest { @Test public void testFolder() throws Exception { final BoxFileidProvider fileid = new BoxFileidProvider(session); - final Path directory = new BoxDirectoryFeature(session, fileid).mkdir(new Path(new AlphanumericRandomStringService().random(), + final Path directory = new BoxDirectoryFeature(session, fileid).mkdir(new BoxWriteFeature(session, fileid), new Path(new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory)), new TransferStatus()); final BoxShareFeature feature = new BoxShareFeature(session, fileid); assertTrue(feature.isSupported(directory, Share.Type.download)); @@ -51,7 +51,7 @@ public class BoxShareFeatureTest extends AbstractBoxTest { public void testFile() throws Exception { final BoxFileidProvider fileid = new BoxFileidProvider(session); final Path test = new BoxTouchFeature(session, fileid).touch( - new Path(Home.root(), new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)), new TransferStatus()); + new BoxWriteFeature(session, fileid), new Path(Home.root(), new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)), new TransferStatus()); final BoxShareFeature feature = new BoxShareFeature(session, fileid); assertTrue(feature.isSupported(test, Share.Type.download)); assertFalse(feature.isSupported(test, Share.Type.upload)); diff --git a/box/src/test/java/ch/cyberduck/core/box/BoxWriteFeatureTest.java b/box/src/test/java/ch/cyberduck/core/box/BoxWriteFeatureTest.java index 7ce5dabf25..e7d16ec195 100644 --- a/box/src/test/java/ch/cyberduck/core/box/BoxWriteFeatureTest.java +++ b/box/src/test/java/ch/cyberduck/core/box/BoxWriteFeatureTest.java @@ -51,12 +51,12 @@ public class BoxWriteFeatureTest extends AbstractBoxTest { final BoxFileidProvider fileid = new BoxFileidProvider(session); final BoxWriteFeature feature = new BoxWriteFeature(session, fileid); final Path folder = new BoxDirectoryFeature(session, fileid).mkdir( - new Path(Home.root(), new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory)), new TransferStatus()); + new BoxWriteFeature(session, fileid), new Path(Home.root(), new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory)), new TransferStatus()); final long folderModification = new BoxAttributesFinderFeature(session, fileid).find(folder).getModificationDate(); assertEquals(folderModification, folder.attributes().getModificationDate()); // Makes sure to test overwrite final Path file = new BoxTouchFeature(session, fileid).touch( - new Path(folder, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)), new TransferStatus().setCreated(1503654615002L)); + new BoxWriteFeature(session, fileid), new Path(folder, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)), new TransferStatus().setCreated(1503654615002L)); final byte[] content = RandomUtils.nextBytes(2047); final TransferStatus status = new TransferStatus(); status.setModified(1503654614004L); //GMT: Friday, 25. August 2017 09:50:14.004 diff --git a/box/src/test/java/ch/cyberduck/core/cryptomator/BoxThresholdUploadServiceTest.java b/box/src/test/java/ch/cyberduck/core/cryptomator/BoxThresholdUploadServiceTest.java index 837fa1cb36..1c604e5f50 100644 --- a/box/src/test/java/ch/cyberduck/core/cryptomator/BoxThresholdUploadServiceTest.java +++ b/box/src/test/java/ch/cyberduck/core/cryptomator/BoxThresholdUploadServiceTest.java @@ -74,7 +74,7 @@ public class BoxThresholdUploadServiceTest extends AbstractBoxTest { @Test public void testUploadVaultWithBulkFeature() throws Exception { final BoxFileidProvider fileid = new BoxFileidProvider(session); - final Path container = new BoxDirectoryFeature(session, fileid).mkdir(new Path(new AlphanumericRandomStringService().random(), EnumSet.of(AbstractPath.Type.directory)), new TransferStatus().setLength(0L)); + final Path container = new BoxDirectoryFeature(session, fileid).mkdir(new BoxWriteFeature(session, fileid), new Path(new AlphanumericRandomStringService().random(), EnumSet.of(AbstractPath.Type.directory)), new TransferStatus().setLength(0L)); final Path vault = new Path(container, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory)); final Path test = new Path(vault, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)); final CryptoVault cryptomator = new CryptoVault(vault); @@ -88,13 +88,13 @@ public class BoxThresholdUploadServiceTest extends AbstractBoxTest { final FileHeader header = cryptomator.getFileHeaderCryptor().create(); writeStatus.setHeader(cryptomator.getFileHeaderCryptor().encryptHeader(header)); writeStatus.setLength(content.length); - final CryptoBulkFeature> bulk = new CryptoBulkFeature<>(session, new DisabledBulkFeature(), new BoxDeleteFeature(session, fileid), cryptomator); + final CryptoBulkFeature> bulk = new CryptoBulkFeature<>(session, new DisabledBulkFeature(), cryptomator); bulk.pre(Transfer.Type.upload, Collections.singletonMap(new TransferItem(test), writeStatus), new DisabledConnectionCallback()); final BytecountStreamListener count = new BytecountStreamListener(); final CryptoUploadFeature feature = new CryptoUploadFeature<>(session, new BoxThresholdUploadService(session, fileid, registry), - new BoxWriteFeature(session, fileid), cryptomator); - feature.upload(test, local, new BandwidthThrottle(BandwidthThrottle.UNLIMITED), new DisabledProgressListener(), count, writeStatus, new DisabledConnectionCallback()); + cryptomator); + feature.upload(new BoxWriteFeature(session, fileid), test, local, new BandwidthThrottle(BandwidthThrottle.UNLIMITED), new DisabledProgressListener(), count, writeStatus, new DisabledConnectionCallback()); assertEquals(content.length, count.getSent()); assertTrue(writeStatus.isComplete()); assertTrue(cryptomator.getFeature(session, Find.class, new BoxFindFeature(session, fileid)).find(test)); diff --git a/box/src/test/java/ch/cyberduck/core/cryptomator/BoxWriteFeatureTest.java b/box/src/test/java/ch/cyberduck/core/cryptomator/BoxWriteFeatureTest.java index 5290c45246..daa3c8cec1 100644 --- a/box/src/test/java/ch/cyberduck/core/cryptomator/BoxWriteFeatureTest.java +++ b/box/src/test/java/ch/cyberduck/core/cryptomator/BoxWriteFeatureTest.java @@ -63,7 +63,7 @@ public class BoxWriteFeatureTest extends AbstractBoxTest { @Test public void testWriteVault() throws Exception { final BoxFileidProvider fileid = new BoxFileidProvider(session); - final Path container = new BoxDirectoryFeature(session, fileid).mkdir(new Path(new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory)), new TransferStatus()); + final Path container = new BoxDirectoryFeature(session, fileid).mkdir(new BoxWriteFeature(session, fileid), new Path(new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory)), new TransferStatus()); final Path vault = new Path(container, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory)); final Path test = new Path(vault, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)); final CryptoVault cryptomator = new CryptoVault(vault); @@ -96,7 +96,7 @@ public class BoxWriteFeatureTest extends AbstractBoxTest { @Test public void testWriteVaultWithTimeStamp() throws Exception { final BoxFileidProvider fileid = new BoxFileidProvider(session); - final Path container = new BoxDirectoryFeature(session, fileid).mkdir(new Path(new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory)), new TransferStatus()); + final Path container = new BoxDirectoryFeature(session, fileid).mkdir(new BoxWriteFeature(session, fileid), new Path(new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory)), new TransferStatus()); final Path vault = new Path(container, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory)); final Path test = new Path(vault, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)); final CryptoVault cryptomator = new CryptoVault(vault); diff --git a/box/src/test/java/ch/cyberduck/core/cryptomator/BufferWriteFeatureTest.java b/box/src/test/java/ch/cyberduck/core/cryptomator/BufferWriteFeatureTest.java index 17bfc97690..0f36353221 100644 --- a/box/src/test/java/ch/cyberduck/core/cryptomator/BufferWriteFeatureTest.java +++ b/box/src/test/java/ch/cyberduck/core/cryptomator/BufferWriteFeatureTest.java @@ -27,6 +27,7 @@ import ch.cyberduck.core.box.BoxDirectoryFeature; import ch.cyberduck.core.box.BoxFileidProvider; import ch.cyberduck.core.box.BoxFindFeature; import ch.cyberduck.core.box.BoxReadFeature; +import ch.cyberduck.core.box.BoxWriteFeature; import ch.cyberduck.core.cryptomator.features.CryptoReadFeature; import ch.cyberduck.core.cryptomator.features.CryptoWriteFeature; import ch.cyberduck.core.cryptomator.random.RandomNonceGenerator; @@ -62,7 +63,7 @@ public class BufferWriteFeatureTest extends AbstractBoxTest { @Test public void testWriteVault() throws Exception { final BoxFileidProvider fileid = new BoxFileidProvider(session); - final Path container = new BoxDirectoryFeature(session, fileid).mkdir(new Path(new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory)), new TransferStatus()); + final Path container = new BoxDirectoryFeature(session, fileid).mkdir(new BoxWriteFeature(session, fileid), new Path(new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory)), new TransferStatus()); final Path vault = new Path(container, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory)); final Path test = new Path(vault, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)); final CryptoVault cryptomator = new CryptoVault(vault); diff --git a/brick/pom.xml b/brick/pom.xml index 15c2f73275..0f2bd2ab37 100644 --- a/brick/pom.xml +++ b/brick/pom.xml @@ -18,7 +18,7 @@ ch.cyberduck parent - 9.2.0-SNAPSHOT + 9.4.0-SNAPSHOT brick jar diff --git a/brick/src/main/java/ch/cyberduck/core/brick/BrickApiClient.java b/brick/src/main/java/ch/cyberduck/core/brick/BrickApiClient.java index 196137a6d9..543db5af16 100644 --- a/brick/src/main/java/ch/cyberduck/core/brick/BrickApiClient.java +++ b/brick/src/main/java/ch/cyberduck/core/brick/BrickApiClient.java @@ -16,6 +16,7 @@ package ch.cyberduck.core.brick; */ import ch.cyberduck.core.ConnectionTimeoutFactory; +import ch.cyberduck.core.Host; import ch.cyberduck.core.HostUrlProvider; import ch.cyberduck.core.PreferencesUseragentProvider; import ch.cyberduck.core.brick.io.swagger.client.ApiClient; @@ -24,6 +25,7 @@ import ch.cyberduck.core.brick.io.swagger.client.JSON; import ch.cyberduck.core.brick.io.swagger.client.Pair; import ch.cyberduck.core.jersey.HttpComponentsProvider; +import org.apache.http.impl.client.CloseableHttpClient; import org.glassfish.jersey.client.ClientConfig; import org.glassfish.jersey.jackson.JacksonFeature; import org.glassfish.jersey.media.multipart.MultiPartFeature; @@ -43,24 +45,30 @@ public class BrickApiClient extends ApiClient { Logger.getLogger("org.glassfish.jersey.client.ClientExecutorProvidersConfigurator").setLevel(java.util.logging.Level.SEVERE); } - private final BrickSession session; + private final Host host; + private final CloseableHttpClient client; - public BrickApiClient(final BrickSession session) { - this.session = session; + public BrickApiClient(final Host host, final CloseableHttpClient client) { + this.host = host; + this.client = client; this.setHttpClient(ClientBuilder.newClient(new ClientConfig() .register(new InputStreamProvider()) .register(MultiPartFeature.class) .register(new JSON()) .register(JacksonFeature.class) - .connectorProvider(new HttpComponentsProvider(session.getClient()))) + .connectorProvider(new HttpComponentsProvider(client))) ); - final int timeout = ConnectionTimeoutFactory.get().getTimeout() * 1000; + final int timeout = ConnectionTimeoutFactory.get(host).getTimeout() * 1000; this.setConnectTimeout(timeout); this.setReadTimeout(timeout); this.setUserAgent(new PreferencesUseragentProvider().get()); this.setBasePath("https://app.files.com/api/rest/v1"); } + public CloseableHttpClient getClient() { + return client; + } + @Override protected Client buildHttpClient(final boolean debugging) { // No need to build default client @@ -70,7 +78,7 @@ public class BrickApiClient extends ApiClient { @Override public T invokeAPI(final String path, final String method, final List queryParams, final Object body, final Map headerParams, final Map formParams, final String accept, final String contentType, final String[] authNames, final GenericType returnType) throws ApiException { try { - this.setBasePath(String.format("%s/api/rest/v1", new HostUrlProvider().withUsername(false).get(session.getHost()))); + this.setBasePath(String.format("%s/api/rest/v1", new HostUrlProvider().withUsername(false).get(host))); return super.invokeAPI(path, method, queryParams, body, headerParams, formParams, accept, contentType, authNames, returnType); } catch(ProcessingException e) { diff --git a/brick/src/main/java/ch/cyberduck/core/brick/BrickAttributesFinderFeature.java b/brick/src/main/java/ch/cyberduck/core/brick/BrickAttributesFinderFeature.java index 38aa6f26cb..6cfede0cae 100644 --- a/brick/src/main/java/ch/cyberduck/core/brick/BrickAttributesFinderFeature.java +++ b/brick/src/main/java/ch/cyberduck/core/brick/BrickAttributesFinderFeature.java @@ -15,6 +15,7 @@ package ch.cyberduck.core.brick; * GNU General Public License for more details. */ +import ch.cyberduck.core.DefaultPathAttributes; import ch.cyberduck.core.ListProgressListener; import ch.cyberduck.core.Path; import ch.cyberduck.core.PathAttributes; @@ -41,7 +42,7 @@ public class BrickAttributesFinderFeature implements AttributesFinder, Attribute @Override public PathAttributes find(final Path file, final ListProgressListener listener) throws BackgroundException { try { - final FileEntity entity = new FilesApi(new BrickApiClient(session)) + final FileEntity entity = new FilesApi(session.getClient()) .download(StringUtils.removeStart(file.getAbsolute(), String.valueOf(Path.DELIMITER)), "stat", null, false, false); switch(entity.getType()) { @@ -64,7 +65,7 @@ public class BrickAttributesFinderFeature implements AttributesFinder, Attribute @Override public PathAttributes toAttributes(final FileEntity entity) { - final PathAttributes attr = new PathAttributes(); + final PathAttributes attr = new DefaultPathAttributes(); attr.setChecksum(Checksum.parse(entity.getMd5())); attr.setRegion(entity.getRegion()); if(entity.getSize() != null) { diff --git a/brick/src/main/java/ch/cyberduck/core/brick/BrickCopyFeature.java b/brick/src/main/java/ch/cyberduck/core/brick/BrickCopyFeature.java index 22d6f51b14..9d0babdf85 100644 --- a/brick/src/main/java/ch/cyberduck/core/brick/BrickCopyFeature.java +++ b/brick/src/main/java/ch/cyberduck/core/brick/BrickCopyFeature.java @@ -16,6 +16,7 @@ package ch.cyberduck.core.brick; */ import ch.cyberduck.core.ConnectionCallback; +import ch.cyberduck.core.DefaultPathAttributes; import ch.cyberduck.core.Path; import ch.cyberduck.core.brick.io.swagger.client.ApiException; import ch.cyberduck.core.brick.io.swagger.client.api.FileActionsApi; @@ -49,7 +50,7 @@ public class BrickCopyFeature extends BrickFileMigrationFeature implements Copy @Override public Path copy(final Path file, final Path target, final TransferStatus status, final ConnectionCallback callback, final StreamListener listener) throws BackgroundException { try { - final BrickApiClient client = new BrickApiClient(session); + final BrickApiClient client = session.getClient(); if(status.isExists()) { log.warn("Delete file {} to be replaced with {}", target, file); new BrickDeleteFeature(session).delete(Collections.singletonList(target), callback, new Delete.DisabledCallback()); @@ -61,7 +62,7 @@ public class BrickCopyFeature extends BrickFileMigrationFeature implements Copy if(entity.getFileMigrationId() != null) { this.poll(client, entity); } - return new Path(target).withAttributes(file.attributes()); + return new Path(target).withAttributes(new DefaultPathAttributes(file.attributes()).setVault(null)); } catch(ApiException e) { throw new BrickExceptionMappingService().map("Cannot copy {0}", e, file); diff --git a/brick/src/main/java/ch/cyberduck/core/brick/BrickCredentialsConfigurator.java b/brick/src/main/java/ch/cyberduck/core/brick/BrickCredentialsConfigurator.java index ace87eb735..22baa5ba54 100644 --- a/brick/src/main/java/ch/cyberduck/core/brick/BrickCredentialsConfigurator.java +++ b/brick/src/main/java/ch/cyberduck/core/brick/BrickCredentialsConfigurator.java @@ -21,7 +21,6 @@ import ch.cyberduck.core.CredentialsConfigurator; import ch.cyberduck.core.Host; import ch.cyberduck.core.exception.LoginCanceledException; -import org.apache.commons.lang3.StringUtils; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; @@ -30,13 +29,14 @@ public class BrickCredentialsConfigurator implements CredentialsConfigurator { @Override public Credentials configure(final Host host) { - if(StringUtils.isBlank(host.getCredentials().getToken())) { - final Credentials credentials = new Credentials(host.getCredentials()); - log.debug("Set new random token for {}", host); - credentials.setToken(new AlphanumericRandomStringService().random()); + final Credentials credentials = new Credentials(host.getCredentials()); + if(credentials.isTokenAuthentication()) { + log.warn("Skip auto configuration of credentials for {}", host); return credentials; } - return CredentialsConfigurator.DISABLED.configure(host); + log.debug("Set new random token for {}", host); + credentials.setToken(new AlphanumericRandomStringService().random()); + return credentials; } @Override diff --git a/brick/src/main/java/ch/cyberduck/core/brick/BrickDeleteFeature.java b/brick/src/main/java/ch/cyberduck/core/brick/BrickDeleteFeature.java index 3727ff3c46..f4246c8cfc 100644 --- a/brick/src/main/java/ch/cyberduck/core/brick/BrickDeleteFeature.java +++ b/brick/src/main/java/ch/cyberduck/core/brick/BrickDeleteFeature.java @@ -41,7 +41,7 @@ public class BrickDeleteFeature implements Delete { public void delete(final Map files, final PasswordCallback prompt, final Callback callback) throws BackgroundException { for(Path f : files.keySet()) { try { - new FilesApi(new BrickApiClient(session)).deleteFilesPath( + new FilesApi(session.getClient()).deleteFilesPath( StringUtils.removeStart(f.getAbsolute(), String.valueOf(Path.DELIMITER)), f.isDirectory()); } catch(ApiException e) { @@ -51,7 +51,7 @@ public class BrickDeleteFeature implements Delete { } @Override - public EnumSet features() { + public EnumSet features(final Path file) { return EnumSet.of(Flags.recursive); } } diff --git a/brick/src/main/java/ch/cyberduck/core/brick/BrickDirectoryFeature.java b/brick/src/main/java/ch/cyberduck/core/brick/BrickDirectoryFeature.java index d041862b45..7efa0f4acb 100644 --- a/brick/src/main/java/ch/cyberduck/core/brick/BrickDirectoryFeature.java +++ b/brick/src/main/java/ch/cyberduck/core/brick/BrickDirectoryFeature.java @@ -21,6 +21,7 @@ import ch.cyberduck.core.brick.io.swagger.client.api.FoldersApi; import ch.cyberduck.core.brick.io.swagger.client.model.FileEntity; import ch.cyberduck.core.exception.BackgroundException; import ch.cyberduck.core.features.Directory; +import ch.cyberduck.core.features.Write; import ch.cyberduck.core.transfer.TransferStatus; import org.apache.commons.lang3.StringUtils; @@ -34,10 +35,10 @@ public class BrickDirectoryFeature implements Directory { } @Override - public Path mkdir(final Path folder, final TransferStatus status) throws BackgroundException { + public Path mkdir(final Write writer, final Path folder, final TransferStatus status) throws BackgroundException { try { return new Path(folder).withAttributes( - new BrickAttributesFinderFeature(session).toAttributes(new FoldersApi(new BrickApiClient(session)) + new BrickAttributesFinderFeature(session).toAttributes(new FoldersApi(session.getClient()) .postFoldersPath(StringUtils.removeStart(folder.getAbsolute(), String.valueOf(Path.DELIMITER))))); } catch(ApiException e) { diff --git a/brick/src/main/java/ch/cyberduck/core/brick/BrickListService.java b/brick/src/main/java/ch/cyberduck/core/brick/BrickListService.java index cb0830b43a..c59fbcfdd9 100644 --- a/brick/src/main/java/ch/cyberduck/core/brick/BrickListService.java +++ b/brick/src/main/java/ch/cyberduck/core/brick/BrickListService.java @@ -51,7 +51,7 @@ public class BrickListService implements ListService { final AttributedList children = new AttributedList<>(); String cursor = null; List response; - final BrickApiClient client = new BrickApiClient(session); + final BrickApiClient client = session.getClient(); do { response = new FoldersApi(client).foldersListForPath(StringUtils.removeStart(directory.getAbsolute(), String.valueOf(Path.DELIMITER)), cursor, chunksize, null, null, null, null, null, null); diff --git a/brick/src/main/java/ch/cyberduck/core/brick/BrickLockFeature.java b/brick/src/main/java/ch/cyberduck/core/brick/BrickLockFeature.java index d5f9702d50..2b8a5607a7 100644 --- a/brick/src/main/java/ch/cyberduck/core/brick/BrickLockFeature.java +++ b/brick/src/main/java/ch/cyberduck/core/brick/BrickLockFeature.java @@ -35,7 +35,7 @@ public class BrickLockFeature implements Lock { @Override public String lock(final Path file) throws BackgroundException { try { - return new LocksApi(new BrickApiClient(session)) + return new LocksApi(session.getClient()) .postLocksPath(StringUtils.removeStart(file.getAbsolute(), String.valueOf(Path.DELIMITER)), new LocksPathBody().exclusive(true).allowAccessByAnyUser(true)).getToken(); } @@ -47,7 +47,7 @@ public class BrickLockFeature implements Lock { @Override public void unlock(final Path file, final String token) throws BackgroundException { try { - new LocksApi(new BrickApiClient(session)) + new LocksApi(session.getClient()) .deleteLocksPath(StringUtils.removeStart(file.getAbsolute(), String.valueOf(Path.DELIMITER)), token); } catch(ApiException e) { diff --git a/brick/src/main/java/ch/cyberduck/core/brick/BrickMoveFeature.java b/brick/src/main/java/ch/cyberduck/core/brick/BrickMoveFeature.java index c103e977f5..c89869da55 100644 --- a/brick/src/main/java/ch/cyberduck/core/brick/BrickMoveFeature.java +++ b/brick/src/main/java/ch/cyberduck/core/brick/BrickMoveFeature.java @@ -17,6 +17,7 @@ package ch.cyberduck.core.brick; import ch.cyberduck.core.CaseInsensitivePathPredicate; import ch.cyberduck.core.ConnectionCallback; +import ch.cyberduck.core.DefaultPathAttributes; import ch.cyberduck.core.Path; import ch.cyberduck.core.brick.io.swagger.client.ApiException; import ch.cyberduck.core.brick.io.swagger.client.api.FileActionsApi; @@ -46,7 +47,7 @@ public class BrickMoveFeature extends BrickFileMigrationFeature implements Move @Override public Path move(final Path file, final Path target, final TransferStatus status, final Delete.Callback delete, final ConnectionCallback callback) throws BackgroundException { try { - final BrickApiClient client = new BrickApiClient(session); + final BrickApiClient client = session.getClient(); if(status.isExists()) { if(!new CaseInsensitivePathPredicate(file).test(target)) { log.warn("Delete file {} to be replaced with {}", target, file); @@ -59,7 +60,7 @@ public class BrickMoveFeature extends BrickFileMigrationFeature implements Move if(entity.getFileMigrationId() != null) { this.poll(client, entity); } - return new Path(target).withAttributes(file.attributes()); + return new Path(target).withAttributes(new DefaultPathAttributes(file.attributes()).setVault(null)); } catch(ApiException e) { throw new BrickExceptionMappingService().map("Cannot rename {0}", e, file); diff --git a/brick/src/main/java/ch/cyberduck/core/brick/BrickMultipartWriteFeature.java b/brick/src/main/java/ch/cyberduck/core/brick/BrickMultipartWriteFeature.java index e7a4f32713..6841482e0e 100644 --- a/brick/src/main/java/ch/cyberduck/core/brick/BrickMultipartWriteFeature.java +++ b/brick/src/main/java/ch/cyberduck/core/brick/BrickMultipartWriteFeature.java @@ -117,7 +117,7 @@ public class BrickMultipartWriteFeature implements MultipartWrite { public TransferStatus call() throws BackgroundException { final List uploadPartEntities; try { - uploadPartEntities = new FileActionsApi(new BrickApiClient(session)) + uploadPartEntities = new FileActionsApi(session.getClient()) .beginUpload(StringUtils.removeStart(file.getAbsolute(), String.valueOf(Path.DELIMITER)), new BeginUploadPathBody().ref(ref).part(partNumber)); } catch(ApiException e) { @@ -173,11 +173,11 @@ public class BrickMultipartWriteFeature implements MultipartWrite { return; } if(null == ref) { - new BrickTouchFeature(session).touch(file, new TransferStatus()); + new BrickTouchFeature(session).touch(writer, file, new TransferStatus()); } else { try { - response.set(new FilesApi(new BrickApiClient(session)).postFilesPath(new FilesPathBody() + response.set(new FilesApi(session.getClient()).postFilesPath(new FilesPathBody() .providedMtime(null != overall.getModified() ? new DateTime(overall.getModified()) : null) .etagsEtag(checksums.stream().map(s -> s.getChecksum().hash).collect(Collectors.toList())) .etagsPart(checksums.stream().map(TransferStatus::getPart).collect(Collectors.toList())) diff --git a/brick/src/main/java/ch/cyberduck/core/brick/BrickPairingSchedulerFeature.java b/brick/src/main/java/ch/cyberduck/core/brick/BrickPairingSchedulerFeature.java index ff68ee426e..93f9cce0fd 100644 --- a/brick/src/main/java/ch/cyberduck/core/brick/BrickPairingSchedulerFeature.java +++ b/brick/src/main/java/ch/cyberduck/core/brick/BrickPairingSchedulerFeature.java @@ -105,7 +105,8 @@ public class BrickPairingSchedulerFeature { resource.setHeader(HttpHeaders.ACCEPT, "application/json"); resource.setHeader(HttpHeaders.CONTENT_TYPE, "application/json"); log.info("Fetch credentials for paring key {} from {}", token, resource); - final JsonObject json = session.getClient().execute(resource, new AbstractResponseHandler() { + final BrickApiClient client = session.getClient(); + final JsonObject json = client.getClient().execute(resource, new AbstractResponseHandler() { @Override public JsonObject handleEntity(final HttpEntity entity) throws IOException { final ByteArrayOutputStream out = new ByteArrayOutputStream(); diff --git a/brick/src/main/java/ch/cyberduck/core/brick/BrickReadFeature.java b/brick/src/main/java/ch/cyberduck/core/brick/BrickReadFeature.java index 27390eb85e..0c7ee205e8 100644 --- a/brick/src/main/java/ch/cyberduck/core/brick/BrickReadFeature.java +++ b/brick/src/main/java/ch/cyberduck/core/brick/BrickReadFeature.java @@ -54,7 +54,8 @@ public class BrickReadFeature implements Read { @Override public InputStream read(final Path file, final TransferStatus status, final ConnectionCallback callback) throws BackgroundException { try { - final FileEntity entity = new FilesApi(new BrickApiClient(session)) + final BrickApiClient client = session.getClient(); + final FileEntity entity = new FilesApi(client) .download(StringUtils.removeStart(file.getAbsolute(), String.valueOf(Path.DELIMITER)), null, null, null, null); final HttpUriRequest request = new HttpGet(entity.getDownloadUri()); @@ -72,7 +73,7 @@ public class BrickReadFeature implements Read { // Disable compression request.addHeader(new BasicHeader(HttpHeaders.ACCEPT_ENCODING, "identity")); } - final HttpResponse response = session.getClient().execute(request); + final HttpResponse response = client.getClient().execute(request); switch(response.getStatusLine().getStatusCode()) { case HttpStatus.SC_OK: case HttpStatus.SC_PARTIAL_CONTENT: diff --git a/brick/src/main/java/ch/cyberduck/core/brick/BrickSession.java b/brick/src/main/java/ch/cyberduck/core/brick/BrickSession.java index 22c0757464..b7e7c0b9a2 100644 --- a/brick/src/main/java/ch/cyberduck/core/brick/BrickSession.java +++ b/brick/src/main/java/ch/cyberduck/core/brick/BrickSession.java @@ -17,7 +17,6 @@ package ch.cyberduck.core.brick; import ch.cyberduck.core.ConnectionCallback; import ch.cyberduck.core.Credentials; -import ch.cyberduck.core.DefaultIOExceptionMappingService; import ch.cyberduck.core.DisabledConnectionCallback; import ch.cyberduck.core.Host; import ch.cyberduck.core.HostKeyCallback; @@ -48,7 +47,6 @@ import ch.cyberduck.core.features.Touch; import ch.cyberduck.core.features.Upload; import ch.cyberduck.core.features.Write; import ch.cyberduck.core.http.CustomServiceUnavailableRetryStrategy; -import ch.cyberduck.core.http.ExecutionCountServiceUnavailableRetryStrategy; import ch.cyberduck.core.http.HttpSession; import ch.cyberduck.core.local.BrowserLauncher; import ch.cyberduck.core.local.BrowserLauncherFactory; @@ -59,17 +57,15 @@ import ch.cyberduck.core.ssl.X509TrustManager; import ch.cyberduck.core.threading.CancelCallback; import org.apache.commons.lang3.StringUtils; -import org.apache.http.impl.client.CloseableHttpClient; 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.net.InetAddress; import java.net.UnknownHostException; import java.util.concurrent.CountDownLatch; -public class BrickSession extends HttpSession { +public class BrickSession extends HttpSession { private static final Logger log = LogManager.getLogger(BrickSession.class); private BrickUnauthorizedRetryStrategy retryHandler; @@ -79,13 +75,13 @@ public class BrickSession extends HttpSession { } @Override - protected CloseableHttpClient connect(final ProxyFinder proxy, final HostKeyCallback key, final LoginCallback prompt, final CancelCallback cancel) { + protected BrickApiClient 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 BrickUnauthorizedRetryStrategy(this, prompt, cancel)))); + retryHandler = new BrickUnauthorizedRetryStrategy(this, prompt, cancel))); configuration.addInterceptorLast(retryHandler); configuration.addInterceptorLast(new BrickPreferencesRequestInterceptor()); - return configuration.build(); + return new BrickApiClient(host, configuration.build()); } @Override @@ -112,12 +108,14 @@ public class BrickSession extends HttpSession { } @Override - protected void logout() throws BackgroundException { + public void disconnect() throws BackgroundException { try { - client.close(); + if(client != null) { + client.getHttpClient().close(); + } } - catch(IOException e) { - throw new DefaultIOExceptionMappingService().map(e); + finally { + super.disconnect(); } } diff --git a/brick/src/main/java/ch/cyberduck/core/brick/BrickShareFeature.java b/brick/src/main/java/ch/cyberduck/core/brick/BrickShareFeature.java index ad379bb524..90d193fdc1 100644 --- a/brick/src/main/java/ch/cyberduck/core/brick/BrickShareFeature.java +++ b/brick/src/main/java/ch/cyberduck/core/brick/BrickShareFeature.java @@ -42,6 +42,9 @@ public class BrickShareFeature implements Share { @Override public boolean isSupported(final Path file, final Type type) { + if(file.isRoot()) { + return false; + } return type == Type.download; } @@ -52,7 +55,7 @@ public class BrickShareFeature implements Share { LocaleFactory.localizedString("Passphrase", "Cryptomator"), MessageFormat.format(LocaleFactory.localizedString("Create a passphrase required to access {0}", "Credentials"), file.getName()), new LoginOptions().anonymous(true).keychain(false).icon(session.getHost().getProtocol().disk())); - return new DescriptiveUrl(new BundlesApi(new BrickApiClient(session)) + return new DescriptiveUrl(new BundlesApi(session.getClient()) .postBundles(new BundlesBody().password(password.isPasswordAuthentication() ? password.getPassword() : null).paths(Collections.singletonList( StringUtils.removeStart(file.getAbsolute(), String.valueOf(Path.DELIMITER))))).getUrl(), DescriptiveUrl.Type.signed); } diff --git a/brick/src/main/java/ch/cyberduck/core/brick/BrickThresholdUploadFeature.java b/brick/src/main/java/ch/cyberduck/core/brick/BrickThresholdUploadFeature.java index 7a81b0adf0..4fc8435a52 100644 --- a/brick/src/main/java/ch/cyberduck/core/brick/BrickThresholdUploadFeature.java +++ b/brick/src/main/java/ch/cyberduck/core/brick/BrickThresholdUploadFeature.java @@ -31,32 +31,23 @@ public class BrickThresholdUploadFeature implements Upload { private final BrickSession session; - private Write writer; - public BrickThresholdUploadFeature(final BrickSession session) { this.session = session; - this.writer = new BrickWriteFeature(session); } @Override - public FileEntity upload(final Path file, final Local local, final BandwidthThrottle throttle, final ProgressListener progress, final StreamListener streamListener, final TransferStatus status, final ConnectionCallback callback) throws BackgroundException { + public FileEntity upload(final Write 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(status.getLength() > 0) { - return new BrickUploadFeature(session, writer).upload(file, local, throttle, progress, streamListener, status, callback); + return new BrickUploadFeature(session).upload(write, file, local, throttle, progress, streamListener, status, callback); } else { - new BrickTouchFeature(session).touch(file, status); + new BrickTouchFeature(session).touch(write, file, status); return null; } } @Override public Write.Append append(final Path file, final TransferStatus status) throws BackgroundException { - return new BrickUploadFeature(session, writer).append(file, status); - } - - @Override - public Upload withWriter(final Write writer) { - this.writer = writer; - return this; + return new BrickUploadFeature(session).append(file, status); } } diff --git a/brick/src/main/java/ch/cyberduck/core/brick/BrickTimestampFeature.java b/brick/src/main/java/ch/cyberduck/core/brick/BrickTimestampFeature.java index 1b78063ed2..221a0a5f1e 100644 --- a/brick/src/main/java/ch/cyberduck/core/brick/BrickTimestampFeature.java +++ b/brick/src/main/java/ch/cyberduck/core/brick/BrickTimestampFeature.java @@ -39,7 +39,7 @@ public class BrickTimestampFeature implements Timestamp { public void setTimestamp(final Path file, final TransferStatus status) throws BackgroundException { try { if(null != status.getModified()) { - final FileEntity response = new FilesApi(new BrickApiClient(session)) + final FileEntity response = new FilesApi(session.getClient()) .patchFilesPath(StringUtils.removeStart(file.getAbsolute(), String.valueOf(Path.DELIMITER)), new FilesPathBody().providedMtime(status.getModified() != null ? new DateTime(status.getModified()) : null)); status.setResponse(new BrickAttributesFinderFeature(session).toAttributes(response)); diff --git a/brick/src/main/java/ch/cyberduck/core/brick/BrickTouchFeature.java b/brick/src/main/java/ch/cyberduck/core/brick/BrickTouchFeature.java index c417d83bb9..c003f0b873 100644 --- a/brick/src/main/java/ch/cyberduck/core/brick/BrickTouchFeature.java +++ b/brick/src/main/java/ch/cyberduck/core/brick/BrickTouchFeature.java @@ -22,6 +22,7 @@ import ch.cyberduck.core.brick.io.swagger.client.model.FileEntity; import ch.cyberduck.core.brick.io.swagger.client.model.FileUploadPartEntity; import ch.cyberduck.core.exception.BackgroundException; import ch.cyberduck.core.features.Touch; +import ch.cyberduck.core.features.Write; import ch.cyberduck.core.transfer.TransferStatus; import java.io.IOException; @@ -36,9 +37,9 @@ public class BrickTouchFeature implements Touch { } @Override - public Path touch(final Path file, final TransferStatus status) throws BackgroundException { + public Path touch(final Write writer, final Path file, final TransferStatus status) throws BackgroundException { try { - final BrickUploadFeature upload = new BrickUploadFeature(session, new BrickWriteFeature(session)); + final BrickUploadFeature upload = new BrickUploadFeature(session); final FileUploadPartEntity uploadPartEntity = upload.startUpload(file); status.setLength(0L).setOffset(0L); status.setUrl(uploadPartEntity.getUploadUri()); diff --git a/brick/src/main/java/ch/cyberduck/core/brick/BrickUploadFeature.java b/brick/src/main/java/ch/cyberduck/core/brick/BrickUploadFeature.java index 83ab3f020f..3e2743b5d5 100644 --- a/brick/src/main/java/ch/cyberduck/core/brick/BrickUploadFeature.java +++ b/brick/src/main/java/ch/cyberduck/core/brick/BrickUploadFeature.java @@ -63,25 +63,22 @@ public class BrickUploadFeature extends HttpUploadFeature writer; private final Long partsize; private final Integer concurrency; - public BrickUploadFeature(final BrickSession session, final Write writer) { - this(session, writer, PreferencesFactory.get().getLong("brick.upload.multipart.size"), + public BrickUploadFeature(final BrickSession session) { + this(session, PreferencesFactory.get().getLong("brick.upload.multipart.size"), PreferencesFactory.get().getInteger("brick.upload.multipart.concurrency")); } - public BrickUploadFeature(final BrickSession session, final Write writer, final Long partsize, final Integer concurrency) { - super(writer); + public BrickUploadFeature(final BrickSession session, final Long partsize, final Integer concurrency) { this.session = session; - this.writer = writer; this.partsize = partsize; this.concurrency = concurrency; } @Override - public FileEntity upload(final Path file, final Local local, final BandwidthThrottle throttle, final ProgressListener progress, final StreamListener streamListener, + public FileEntity upload(final Write 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 ThreadPool pool = ThreadPoolFactory.get("multipart", concurrency); try { @@ -100,7 +97,7 @@ public class BrickUploadFeature extends HttpUploadFeature uploadPartEntities; try { - uploadPartEntities = new FileActionsApi(new BrickApiClient(session)) + uploadPartEntities = new FileActionsApi(session.getClient()) .beginUpload(StringUtils.removeStart(file.getAbsolute(), String.valueOf(Path.DELIMITER)), new BeginUploadPathBody().ref(ref).part(partNumber)); } catch(ApiException e) { @@ -140,7 +137,7 @@ public class BrickUploadFeature extends HttpUploadFeature checksums) throws BackgroundException { try { - return new FilesApi(new BrickApiClient(session)).postFilesPath(new FilesPathBody() + return new FilesApi(session.getClient()).postFilesPath(new FilesPathBody() .etagsEtag(checksums.stream().map(s -> s.getChecksum().hash).collect(Collectors.toList())) .etagsPart(checksums.stream().map(TransferStatus::getPart).collect(Collectors.toList())) .providedMtime(null != status.getModified() ? new DateTime(status.getModified()) : null) @@ -152,7 +149,7 @@ public class BrickUploadFeature extends HttpUploadFeature submit(final ThreadPool pool, final Path file, final Local local, + private Future submit(final ThreadPool pool, final Write write, final Path file, final Local local, final BandwidthThrottle throttle, final StreamListener listener, final TransferStatus overall, final String url, final Integer partNumber, final long offset, final long length, final ConnectionCallback callback) throws ConnectionCanceledException { @@ -167,12 +164,12 @@ public class BrickUploadFeature extends HttpUploadFeature { final String uploadUri; FileUploadPartEntity uploadPartEntity = null; if(StringUtils.isBlank(status.getUrl())) { - uploadPartEntity = new BrickUploadFeature(session, this).startUpload(file); + uploadPartEntity = new BrickUploadFeature(session).startUpload(file); uploadUri = uploadPartEntity.getUploadUri(); } else { @@ -81,7 +81,8 @@ public class BrickWriteFeature extends AbstractHttpWriteFeature { final HttpPut request = new HttpPut(uploadUri); request.setEntity(entity); request.setHeader(HttpHeaders.CONTENT_TYPE, MimeTypeService.DEFAULT_CONTENT_TYPE); - final HttpResponse response = session.getClient().execute(request); + final BrickApiClient client = session.getClient(); + final HttpResponse response = client.getClient().execute(request); // Validate response try { switch(response.getStatusLine().getStatusCode()) { @@ -149,7 +150,7 @@ public class BrickWriteFeature extends AbstractHttpWriteFeature { } super.close(); try { - new BrickUploadFeature(session, BrickWriteFeature.this) + new BrickUploadFeature(session) .completeUpload(file, ref, status, Collections.singletonList(status)); } catch(BackgroundException e) { diff --git a/brick/src/test/java/ch/cyberduck/core/brick/BrickAttributesFinderFeatureTest.java b/brick/src/test/java/ch/cyberduck/core/brick/BrickAttributesFinderFeatureTest.java index da388e5dcd..b93887c50d 100644 --- a/brick/src/test/java/ch/cyberduck/core/brick/BrickAttributesFinderFeatureTest.java +++ b/brick/src/test/java/ch/cyberduck/core/brick/BrickAttributesFinderFeatureTest.java @@ -39,7 +39,7 @@ public class BrickAttributesFinderFeatureTest extends AbstractBrickTest { @Test(expected = NotfoundException.class) public void testFindNotFound() throws Exception { final Path folder = new BrickDirectoryFeature(session).mkdir( - new Path(new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory)), new TransferStatus()); + new BrickWriteFeature(session), new Path(new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory)), new TransferStatus()); final Path test = new Path(folder, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)); final BrickAttributesFinderFeature f = new BrickAttributesFinderFeature(session); try { @@ -63,9 +63,9 @@ public class BrickAttributesFinderFeatureTest extends AbstractBrickTest { final Path root = new Path("/", EnumSet.of(Path.Type.volume, Path.Type.directory)); final PathAttributes rootAttributes = f.find(root); final Path folder = new BrickDirectoryFeature(session).mkdir( - new Path(new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory)), new TransferStatus()); + new BrickWriteFeature(session), new Path(new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory)), new TransferStatus()); final long folderTimestamp = f.find(folder).getModificationDate(); - final Path test = new BrickTouchFeature(session).touch(new Path(folder, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)), + final Path test = new BrickTouchFeature(session).touch(new BrickWriteFeature(session), new Path(folder, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)), new TransferStatus().setModified(1719480508000L)); assertEquals(Protocol.DirectoryTimestamp.explicit, session.getHost().getProtocol().getDirectoryTimestamp()); assertNotEquals(folderTimestamp, f.find(folder).getModificationDate()); @@ -90,8 +90,8 @@ public class BrickAttributesFinderFeatureTest extends AbstractBrickTest { @Test public void testFindDirectory() throws Exception { final Path folder = new BrickDirectoryFeature(session).mkdir( - new Path(new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory)), new TransferStatus()); - final Path test = new BrickDirectoryFeature(session).mkdir(new Path(folder, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory)), new TransferStatus()); + new BrickWriteFeature(session), new Path(new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory)), new TransferStatus()); + final Path test = new BrickDirectoryFeature(session).mkdir(new BrickWriteFeature(session), new Path(folder, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory)), new TransferStatus()); final BrickAttributesFinderFeature f = new BrickAttributesFinderFeature(session); final PathAttributes attributes = f.find(test); assertEquals(-1L, attributes.getSize()); diff --git a/brick/src/test/java/ch/cyberduck/core/brick/BrickCopyFeatureTest.java b/brick/src/test/java/ch/cyberduck/core/brick/BrickCopyFeatureTest.java index 69d26ec6d2..bf7a5ebf11 100644 --- a/brick/src/test/java/ch/cyberduck/core/brick/BrickCopyFeatureTest.java +++ b/brick/src/test/java/ch/cyberduck/core/brick/BrickCopyFeatureTest.java @@ -47,7 +47,7 @@ public class BrickCopyFeatureTest extends AbstractBrickTest { @Test public void testCopyEmptyFile() throws Exception { final Path test = new Path(new DefaultHomeFinderService(session).find(), new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)); - new BrickTouchFeature(session).touch(test, new TransferStatus()); + new BrickTouchFeature(session).touch(new BrickWriteFeature(session), test, new TransferStatus()); final Path copy = new Path(new DefaultHomeFinderService(session).find(), new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)); new BrickCopyFeature(session).copy(test, copy, new TransferStatus(), new DisabledConnectionCallback(), new DisabledStreamListener()); assertTrue(new BrickFindFeature(session).find(test)); @@ -63,7 +63,7 @@ public class BrickCopyFeatureTest extends AbstractBrickTest { final byte[] random = RandomUtils.nextBytes(2547); IOUtils.write(random, local.getOutputStream(false)); final TransferStatus status = new TransferStatus().setLength(random.length); - new BrickUploadFeature(session, new BrickWriteFeature(session)).upload(test, local, new BandwidthThrottle(BandwidthThrottle.UNLIMITED), + new BrickUploadFeature(session).upload(new BrickWriteFeature(session), test, local, new BandwidthThrottle(BandwidthThrottle.UNLIMITED), new DisabledProgressListener(), new DisabledStreamListener(), status, new DisabledLoginCallback()); local.delete(); assertTrue(new BrickFindFeature(session).find(test)); @@ -78,18 +78,18 @@ public class BrickCopyFeatureTest extends AbstractBrickTest { @Test public void testCopyToExistingFile() throws Exception { final Path folder = new Path(new DefaultHomeFinderService(session).find(), new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory)); - new BrickDirectoryFeature(session).mkdir(folder, new TransferStatus()); + new BrickDirectoryFeature(session).mkdir(new BrickWriteFeature(session), folder, new TransferStatus()); final Path test = new Path(folder, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)); final Local local = new Local(System.getProperty("java.io.tmpdir"), test.getName()); final byte[] random = RandomUtils.nextBytes(2547); IOUtils.write(random, local.getOutputStream(false)); final TransferStatus status = new TransferStatus().setLength(random.length); - new BrickUploadFeature(session, new BrickWriteFeature(session)).upload(test, local, new BandwidthThrottle(BandwidthThrottle.UNLIMITED), + new BrickUploadFeature(session).upload(new BrickWriteFeature(session), test, local, new BandwidthThrottle(BandwidthThrottle.UNLIMITED), new DisabledProgressListener(), new DisabledStreamListener(), status, new DisabledLoginCallback()); local.delete(); assertTrue(new BrickFindFeature(session).find(test)); final Path copy = new Path(folder, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)); - new BrickTouchFeature(session).touch(copy, new TransferStatus()); + new BrickTouchFeature(session).touch(new BrickWriteFeature(session), copy, new TransferStatus()); new BrickCopyFeature(session).copy(test, copy, new TransferStatus().setExists(true), new DisabledConnectionCallback(), new DisabledStreamListener()); final Find find = new DefaultFindFeature(session); assertTrue(find.find(test)); @@ -102,12 +102,12 @@ public class BrickCopyFeatureTest extends AbstractBrickTest { final Path directory = new Path(new DefaultHomeFinderService(session).find(), new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory)); final String name = new AlphanumericRandomStringService().random(); final Path file = new Path(directory, name, EnumSet.of(Path.Type.file)); - new BrickDirectoryFeature(session).mkdir(directory, new TransferStatus()); + new BrickDirectoryFeature(session).mkdir(new BrickWriteFeature(session), directory, new TransferStatus()); final Local local = new Local(System.getProperty("java.io.tmpdir"), file.getName()); final byte[] random = RandomUtils.nextBytes(2547); IOUtils.write(random, local.getOutputStream(false)); final TransferStatus status = new TransferStatus().setLength(random.length); - new BrickUploadFeature(session, new BrickWriteFeature(session)).upload(file, local, new BandwidthThrottle(BandwidthThrottle.UNLIMITED), + new BrickUploadFeature(session).upload(new BrickWriteFeature(session), file, local, new BandwidthThrottle(BandwidthThrottle.UNLIMITED), new DisabledProgressListener(), new DisabledStreamListener(), status, new DisabledLoginCallback()); local.delete(); assertTrue(new BrickFindFeature(session).find(file)); diff --git a/brick/src/test/java/ch/cyberduck/core/brick/BrickDeleteFeatureTest.java b/brick/src/test/java/ch/cyberduck/core/brick/BrickDeleteFeatureTest.java index 8440e133c3..1b1aa02c0c 100644 --- a/brick/src/test/java/ch/cyberduck/core/brick/BrickDeleteFeatureTest.java +++ b/brick/src/test/java/ch/cyberduck/core/brick/BrickDeleteFeatureTest.java @@ -50,7 +50,7 @@ public class BrickDeleteFeatureTest extends AbstractBrickTest { final byte[] random = RandomUtils.nextBytes(2547); IOUtils.write(random, local.getOutputStream(false)); final TransferStatus status = new TransferStatus().setLength(random.length); - new BrickUploadFeature(session, new BrickWriteFeature(session)).upload(test, local, new BandwidthThrottle(BandwidthThrottle.UNLIMITED), + new BrickUploadFeature(session).upload(new BrickWriteFeature(session), test, local, new BandwidthThrottle(BandwidthThrottle.UNLIMITED), new DisabledProgressListener(), new DisabledStreamListener(), status, new DisabledLoginCallback()); local.delete(); final String lock = new BrickLockFeature(session).lock(test); @@ -61,13 +61,13 @@ public class BrickDeleteFeatureTest extends AbstractBrickTest { @Test @Ignore public void testDeleteRecursively() throws Exception { - final Path room = new BrickDirectoryFeature(session).mkdir(new Path( + final Path room = new BrickDirectoryFeature(session).mkdir(new BrickWriteFeature(session), new Path( new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory, Path.Type.volume)), new TransferStatus()); - final Path folder = new BrickDirectoryFeature(session).mkdir(new Path(room, + final Path folder = new BrickDirectoryFeature(session).mkdir(new BrickWriteFeature(session), new Path(room, new AlphanumericRandomStringService().random().toLowerCase(), EnumSet.of(Path.Type.directory)), new TransferStatus()); assertTrue(new BrickFindFeature(session).find(folder)); final Path file = new Path(folder, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)); - new BrickTouchFeature(session).touch(file, new TransferStatus()); + new BrickTouchFeature(session).touch(new BrickWriteFeature(session), file, new TransferStatus()); assertTrue(new BrickFindFeature(session).find(file)); new BrickDeleteFeature(session).delete(Collections.singletonList(folder), new DisabledLoginCallback(), new Delete.DisabledCallback()); assertFalse(new BrickFindFeature(session).find(folder)); diff --git a/brick/src/test/java/ch/cyberduck/core/brick/BrickDirectoryFeatureTest.java b/brick/src/test/java/ch/cyberduck/core/brick/BrickDirectoryFeatureTest.java index db8ade3c21..992c18f724 100644 --- a/brick/src/test/java/ch/cyberduck/core/brick/BrickDirectoryFeatureTest.java +++ b/brick/src/test/java/ch/cyberduck/core/brick/BrickDirectoryFeatureTest.java @@ -37,8 +37,8 @@ public class BrickDirectoryFeatureTest extends AbstractBrickTest { @Test public void testMakeDirectory() throws Exception { final Path directory = new BrickDirectoryFeature(session).mkdir( - new Path(new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory)), new TransferStatus()); - assertThrows(ConflictException.class, () -> new BrickDirectoryFeature(session).mkdir(directory, new TransferStatus())); + new BrickWriteFeature(session), new Path(new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory)), new TransferStatus()); + assertThrows(ConflictException.class, () -> new BrickDirectoryFeature(session).mkdir(new BrickWriteFeature(session), directory, new TransferStatus())); new BrickDeleteFeature(session).delete(Collections.singletonList(directory), new DisabledLoginCallback(), new Delete.DisabledCallback()); } } \ No newline at end of file diff --git a/brick/src/test/java/ch/cyberduck/core/brick/BrickFindFeatureTest.java b/brick/src/test/java/ch/cyberduck/core/brick/BrickFindFeatureTest.java index 8cf491aa93..bee2e00a48 100644 --- a/brick/src/test/java/ch/cyberduck/core/brick/BrickFindFeatureTest.java +++ b/brick/src/test/java/ch/cyberduck/core/brick/BrickFindFeatureTest.java @@ -29,7 +29,8 @@ import org.junit.experimental.categories.Category; import java.util.Collections; import java.util.EnumSet; -import static org.junit.Assert.*; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; @Category(IntegrationTest.class) public class BrickFindFeatureTest extends AbstractBrickTest { @@ -47,7 +48,7 @@ public class BrickFindFeatureTest extends AbstractBrickTest { @Test public void testFindDirectory() throws Exception { final Path folder = new BrickDirectoryFeature(session).mkdir( - new Path(new DefaultHomeFinderService(session).find(), new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory)), new TransferStatus()); + new BrickWriteFeature(session), new Path(new DefaultHomeFinderService(session).find(), new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory)), new TransferStatus()); assertTrue(new BrickFindFeature(session).find(folder)); assertFalse(new BrickFindFeature(session).find(new Path(folder.getAbsolute(), EnumSet.of(Path.Type.file)))); new BrickDeleteFeature(session).delete(Collections.singletonList(folder), new DisabledLoginCallback(), new Delete.DisabledCallback()); @@ -56,7 +57,7 @@ public class BrickFindFeatureTest extends AbstractBrickTest { @Test public void testFindFile() throws Exception { final Path file = new Path(new DefaultHomeFinderService(session).find(), new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)); - new BrickTouchFeature(session).touch(file, new TransferStatus()); + new BrickTouchFeature(session).touch(new BrickWriteFeature(session), file, new TransferStatus()); assertTrue(new BrickFindFeature(session).find(file)); assertFalse(new BrickFindFeature(session).find(new Path(file.getAbsolute(), EnumSet.of(Path.Type.directory)))); new BrickDeleteFeature(session).delete(Collections.singletonList(file), new DisabledLoginCallback(), new Delete.DisabledCallback()); diff --git a/brick/src/test/java/ch/cyberduck/core/brick/BrickListServiceTest.java b/brick/src/test/java/ch/cyberduck/core/brick/BrickListServiceTest.java index c32ba4689c..7a61b5f9e6 100644 --- a/brick/src/test/java/ch/cyberduck/core/brick/BrickListServiceTest.java +++ b/brick/src/test/java/ch/cyberduck/core/brick/BrickListServiceTest.java @@ -45,7 +45,7 @@ public class BrickListServiceTest extends AbstractBrickTest { @Test public void testListEmptyDirectory() throws Exception { - final Path directory = new BrickDirectoryFeature(session).mkdir(new Path(new AlphanumericRandomStringService().random(), EnumSet.of(AbstractPath.Type.directory)), new TransferStatus()); + final Path directory = new BrickDirectoryFeature(session).mkdir(new BrickWriteFeature(session), new Path(new AlphanumericRandomStringService().random(), EnumSet.of(AbstractPath.Type.directory)), new TransferStatus()); final AttributedList list = new BrickListService(session).list(directory, new DisabledListProgressListener()); assertNotSame(AttributedList.emptyList(), list); assertTrue(list.isEmpty()); @@ -54,9 +54,9 @@ public class BrickListServiceTest extends AbstractBrickTest { @Test public void testListEqualChunkSize() throws Exception { - final Path directory = new BrickDirectoryFeature(session).mkdir(new Path(new AlphanumericRandomStringService().random(), EnumSet.of(AbstractPath.Type.directory)), new TransferStatus()); - final Path f1 = new BrickTouchFeature(session).touch(new Path(directory, new AlphanumericRandomStringService().random(), EnumSet.of(AbstractPath.Type.file)), new TransferStatus()); - final Path f2 = new BrickTouchFeature(session).touch(new Path(directory, new AlphanumericRandomStringService().random(), EnumSet.of(AbstractPath.Type.file)), new TransferStatus()); + final Path directory = new BrickDirectoryFeature(session).mkdir(new BrickWriteFeature(session), new Path(new AlphanumericRandomStringService().random(), EnumSet.of(AbstractPath.Type.directory)), new TransferStatus()); + final Path f1 = new BrickTouchFeature(session).touch(new BrickWriteFeature(session), new Path(directory, new AlphanumericRandomStringService().random(), EnumSet.of(AbstractPath.Type.file)), new TransferStatus()); + final Path f2 = new BrickTouchFeature(session).touch(new BrickWriteFeature(session), new Path(directory, new AlphanumericRandomStringService().random(), EnumSet.of(AbstractPath.Type.file)), new TransferStatus()); final AttributedList list = new BrickListService(session).list(directory, new DisabledListProgressListener(), 2); assertNotSame(AttributedList.emptyList(), list); assertFalse(list.isEmpty()); @@ -71,9 +71,9 @@ public class BrickListServiceTest extends AbstractBrickTest { @Test public void testListChunking() throws Exception { - final Path directory = new BrickDirectoryFeature(session).mkdir(new Path(new AlphanumericRandomStringService().random(), EnumSet.of(AbstractPath.Type.directory)), new TransferStatus()); - final Path f1 = new BrickTouchFeature(session).touch(new Path(directory, new AlphanumericRandomStringService().random(), EnumSet.of(AbstractPath.Type.file)), new TransferStatus()); - final Path f2 = new BrickTouchFeature(session).touch(new Path(directory, new AlphanumericRandomStringService().random(), EnumSet.of(AbstractPath.Type.file)), new TransferStatus()); + final Path directory = new BrickDirectoryFeature(session).mkdir(new BrickWriteFeature(session), new Path(new AlphanumericRandomStringService().random(), EnumSet.of(AbstractPath.Type.directory)), new TransferStatus()); + final Path f1 = new BrickTouchFeature(session).touch(new BrickWriteFeature(session), new Path(directory, new AlphanumericRandomStringService().random(), EnumSet.of(AbstractPath.Type.file)), new TransferStatus()); + final Path f2 = new BrickTouchFeature(session).touch(new BrickWriteFeature(session), new Path(directory, new AlphanumericRandomStringService().random(), EnumSet.of(AbstractPath.Type.file)), new TransferStatus()); final AttributedList list = new BrickListService(session).list(directory, new DisabledListProgressListener(), 1); assertNotSame(AttributedList.emptyList(), list); assertFalse(list.isEmpty()); diff --git a/brick/src/test/java/ch/cyberduck/core/brick/BrickLockFeatureTest.java b/brick/src/test/java/ch/cyberduck/core/brick/BrickLockFeatureTest.java index 926fc9718a..89a980fb9f 100644 --- a/brick/src/test/java/ch/cyberduck/core/brick/BrickLockFeatureTest.java +++ b/brick/src/test/java/ch/cyberduck/core/brick/BrickLockFeatureTest.java @@ -54,8 +54,8 @@ public class BrickLockFeatureTest extends AbstractBrickTest { out.close(); status.setLength(content.length); final Path test = new Path(new DefaultHomeFinderService(session).find(), new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)); - final BrickUploadFeature upload = new BrickUploadFeature(session, new BrickWriteFeature(session)); - upload.upload(test, local, new BandwidthThrottle(BandwidthThrottle.UNLIMITED), + final BrickUploadFeature upload = new BrickUploadFeature(session); + upload.upload(new BrickWriteFeature(session), test, local, new BandwidthThrottle(BandwidthThrottle.UNLIMITED), new DisabledProgressListener(), new DisabledStreamListener(), status, new DisabledConnectionCallback()); assertTrue(new BrickFindFeature(session).find(test)); final BrickLockFeature feature = new BrickLockFeature(session); diff --git a/brick/src/test/java/ch/cyberduck/core/brick/BrickMoveFeatureTest.java b/brick/src/test/java/ch/cyberduck/core/brick/BrickMoveFeatureTest.java index 21b8793a49..260ed83dc3 100644 --- a/brick/src/test/java/ch/cyberduck/core/brick/BrickMoveFeatureTest.java +++ b/brick/src/test/java/ch/cyberduck/core/brick/BrickMoveFeatureTest.java @@ -48,7 +48,7 @@ public class BrickMoveFeatureTest extends AbstractBrickTest { @Test public void testMove() throws Exception { - final Path test = new BrickTouchFeature(session).touch(new Path(new DefaultHomeFinderService(session).find(), new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)), new TransferStatus()); + final Path test = new BrickTouchFeature(session).touch(new BrickWriteFeature(session), new Path(new DefaultHomeFinderService(session).find(), new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)), new TransferStatus()); assertEquals(0L, test.attributes().getSize()); final Path target = new BrickMoveFeature(session).move(test, new Path(new DefaultHomeFinderService(session).find(), new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)), new TransferStatus(), new Delete.DisabledCallback(), new DisabledConnectionCallback()); @@ -56,6 +56,7 @@ public class BrickMoveFeatureTest extends AbstractBrickTest { assertTrue(new BrickFindFeature(session).find(target)); assertEquals(test.attributes(), target.attributes()); final PathAttributes targetAttr = new BrickAttributesFinderFeature(session).find(target); + assertEquals(target.attributes(), targetAttr); assertEquals(test.attributes().getModificationDate(), targetAttr.getModificationDate()); assertEquals(Comparison.equal, session.getHost().getProtocol().getFeature(ComparisonService.class).compare(Path.Type.file, test.attributes(), targetAttr)); assertEquals(Comparison.equal, session.getHost().getProtocol().getFeature(ComparisonService.class).compare(Path.Type.file, target.attributes(), targetAttr)); @@ -64,7 +65,7 @@ public class BrickMoveFeatureTest extends AbstractBrickTest { @Test public void testRename() throws Exception { - final Path test = new BrickTouchFeature(session).touch(new Path(new DefaultHomeFinderService(session).find(), + final Path test = new BrickTouchFeature(session).touch(new BrickWriteFeature(session), new Path(new DefaultHomeFinderService(session).find(), new AlphanumericRandomStringService().random().toLowerCase(), EnumSet.of(Path.Type.file)), new TransferStatus()); final Path target = new BrickMoveFeature(session).move(test, new Path(new DefaultHomeFinderService(session).find(), new AlphanumericRandomStringService().random().toLowerCase(), EnumSet.of(Path.Type.file)), new TransferStatus(), new Delete.DisabledCallback(), new DisabledConnectionCallback()); @@ -77,7 +78,7 @@ public class BrickMoveFeatureTest extends AbstractBrickTest { @Test public void testRenameCaseOnly() throws Exception { final String name = new AlphanumericRandomStringService().random(); - final Path test = new BrickTouchFeature(session).touch(new Path(new DefaultHomeFinderService(session).find(), + final Path test = new BrickTouchFeature(session).touch(new BrickWriteFeature(session), new Path(new DefaultHomeFinderService(session).find(), StringUtils.capitalize(name), EnumSet.of(Path.Type.file)), new TransferStatus()); final Path target = new BrickMoveFeature(session).move(test, new Path(new DefaultHomeFinderService(session).find(), StringUtils.lowerCase(name), EnumSet.of(Path.Type.file)), new TransferStatus().setExists(true), new Delete.DisabledCallback(), new DisabledConnectionCallback()); @@ -94,7 +95,7 @@ public class BrickMoveFeatureTest extends AbstractBrickTest { final byte[] random = RandomUtils.nextBytes(2547); IOUtils.write(random, local.getOutputStream(false)); final TransferStatus status = new TransferStatus().setLength(random.length); - new BrickUploadFeature(session, new BrickWriteFeature(session)).upload(test, local, new BandwidthThrottle(BandwidthThrottle.UNLIMITED), + new BrickUploadFeature(session).upload(new BrickWriteFeature(session), test, local, new BandwidthThrottle(BandwidthThrottle.UNLIMITED), new DisabledProgressListener(), new DisabledStreamListener(), status, new DisabledLoginCallback()); local.delete(); final String lock = new BrickLockFeature(session).lock(test); @@ -109,9 +110,9 @@ public class BrickMoveFeatureTest extends AbstractBrickTest { @Test public void testMoveDirectory() throws Exception { final Path test = new Path(new DefaultHomeFinderService(session).find(), new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory)); - new BrickDirectoryFeature(session).mkdir(test, new TransferStatus()); + new BrickDirectoryFeature(session).mkdir(new BrickWriteFeature(session), test, new TransferStatus()); final Path target = new Path(new DefaultHomeFinderService(session).find(), new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory)); - final Path sourceFile = new BrickTouchFeature(session).touch(new Path(test, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)), new TransferStatus()); + final Path sourceFile = new BrickTouchFeature(session).touch(new BrickWriteFeature(session), new Path(test, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)), new TransferStatus()); final Path targetFile = new Path(target, sourceFile.getName(), EnumSet.of(Path.Type.file)); new BrickMoveFeature(session).move(test, target, new TransferStatus(), new Delete.DisabledCallback(), new DisabledConnectionCallback()); assertFalse(new BrickFindFeature(session).find(test)); @@ -123,9 +124,9 @@ public class BrickMoveFeatureTest extends AbstractBrickTest { @Test public void testMoveOverride() throws Exception { final Path test = new Path(new DefaultHomeFinderService(session).find(), new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)); - new BrickTouchFeature(session).touch(test, new TransferStatus()); + new BrickTouchFeature(session).touch(new BrickWriteFeature(session), test, new TransferStatus()); final Path target = new Path(new DefaultHomeFinderService(session).find(), new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)); - new BrickTouchFeature(session).touch(target, new TransferStatus()); + new BrickTouchFeature(session).touch(new BrickWriteFeature(session), target, new TransferStatus()); new BrickMoveFeature(session).move(test, target, new TransferStatus().setExists(true), new Delete.DisabledCallback(), new DisabledConnectionCallback()); assertFalse(new BrickFindFeature(session).find(test)); assertTrue(new BrickFindFeature(session).find(target)); diff --git a/brick/src/test/java/ch/cyberduck/core/brick/BrickReadFeatureTest.java b/brick/src/test/java/ch/cyberduck/core/brick/BrickReadFeatureTest.java index 8aa5626de2..ac0201acfd 100644 --- a/brick/src/test/java/ch/cyberduck/core/brick/BrickReadFeatureTest.java +++ b/brick/src/test/java/ch/cyberduck/core/brick/BrickReadFeatureTest.java @@ -53,7 +53,7 @@ public class BrickReadFeatureTest extends AbstractBrickTest { public void testReadNotFound() throws Exception { final TransferStatus status = new TransferStatus(); final Path room = new BrickDirectoryFeature(session).mkdir( - new Path(new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory, Path.Type.volume)), new TransferStatus()); + new BrickWriteFeature(session), new Path(new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory, Path.Type.volume)), new TransferStatus()); try { new BrickReadFeature(session).read(new Path(room, "nosuchname", EnumSet.of(Path.Type.file)), status, new DisabledConnectionCallback()); } @@ -68,7 +68,7 @@ public class BrickReadFeatureTest extends AbstractBrickTest { final TransferStatus writeStatus = new TransferStatus(); writeStatus.setLength(content.length); final Path room = new BrickDirectoryFeature(session).mkdir( - new Path(new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory, Path.Type.volume)), new TransferStatus()); + new BrickWriteFeature(session), new Path(new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory, Path.Type.volume)), new TransferStatus()); final Path test = new Path(room, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)); final BrickMultipartWriteFeature writer = new BrickMultipartWriteFeature(session); final HttpResponseOutputStream out = writer.write(test, writeStatus, new DisabledConnectionCallback()); @@ -93,9 +93,9 @@ public class BrickReadFeatureTest extends AbstractBrickTest { @Test public void testReadRange() throws Exception { final Path room = new BrickDirectoryFeature(session).mkdir( - new Path(new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory, Path.Type.volume)), new TransferStatus()); + new BrickWriteFeature(session), new Path(new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory, Path.Type.volume)), new TransferStatus()); final Path test = new Path(room, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)); - new BrickTouchFeature(session).touch(test, new TransferStatus()); + new BrickTouchFeature(session).touch(new BrickWriteFeature(session), test, new TransferStatus()); final Local local = new Local(System.getProperty("java.io.tmpdir"), new AlphanumericRandomStringService().random()); final byte[] content = RandomUtils.nextBytes(1023); final OutputStream out = local.getOutputStream(false); @@ -104,8 +104,8 @@ public class BrickReadFeatureTest extends AbstractBrickTest { out.close(); final TransferStatus upload = new TransferStatus().setLength(content.length); upload.setExists(true); - new BrickUploadFeature(session, new BrickWriteFeature(session)).upload( - test, local, new BandwidthThrottle(BandwidthThrottle.UNLIMITED), new DisabledProgressListener(), new DisabledStreamListener(), upload, + new BrickUploadFeature(session).upload( + new BrickWriteFeature(session), test, local, new BandwidthThrottle(BandwidthThrottle.UNLIMITED), new DisabledProgressListener(), new DisabledStreamListener(), upload, new DisabledConnectionCallback()); final TransferStatus status = new TransferStatus(); status.setLength(content.length); @@ -125,9 +125,9 @@ public class BrickReadFeatureTest extends AbstractBrickTest { @Test public void testReadRangeUnknownLength() throws Exception { final Path room = new BrickDirectoryFeature(session).mkdir( - new Path(new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory, Path.Type.volume)), new TransferStatus()); + new BrickWriteFeature(session), new Path(new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory, Path.Type.volume)), new TransferStatus()); final Path test = new Path(room, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)); - new BrickTouchFeature(session).touch(test, new TransferStatus()); + new BrickTouchFeature(session).touch(new BrickWriteFeature(session), test, new TransferStatus()); final Local local = new Local(System.getProperty("java.io.tmpdir"), new AlphanumericRandomStringService().random()); final byte[] content = RandomUtils.nextBytes(1000); final OutputStream out = local.getOutputStream(false); @@ -136,8 +136,8 @@ public class BrickReadFeatureTest extends AbstractBrickTest { out.close(); final TransferStatus upload = new TransferStatus().setLength(content.length); upload.setExists(true); - new BrickUploadFeature(session, new BrickWriteFeature(session)).upload( - test, local, new BandwidthThrottle(BandwidthThrottle.UNLIMITED), new DisabledProgressListener(), new DisabledStreamListener(), upload, + new BrickUploadFeature(session).upload( + new BrickWriteFeature(session), test, local, new BandwidthThrottle(BandwidthThrottle.UNLIMITED), new DisabledProgressListener(), new DisabledStreamListener(), upload, new DisabledConnectionCallback()); final TransferStatus status = new TransferStatus(); status.setLength(-1L); @@ -161,7 +161,7 @@ public class BrickReadFeatureTest extends AbstractBrickTest { final TransferStatus writeStatus = new TransferStatus(); writeStatus.setLength(content.length); final Path room = new BrickDirectoryFeature(session).mkdir( - new Path(new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory, Path.Type.volume)), new TransferStatus()); + new BrickWriteFeature(session), new Path(new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory, Path.Type.volume)), new TransferStatus()); final Path test = new Path(room, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)); final BrickMultipartWriteFeature writer = new BrickMultipartWriteFeature(session); final HttpResponseOutputStream out = writer.write(test, writeStatus, new DisabledConnectionCallback()); diff --git a/brick/src/test/java/ch/cyberduck/core/brick/BrickShareFeatureTest.java b/brick/src/test/java/ch/cyberduck/core/brick/BrickShareFeatureTest.java index 2b2377d683..d1c9d42d60 100644 --- a/brick/src/test/java/ch/cyberduck/core/brick/BrickShareFeatureTest.java +++ b/brick/src/test/java/ch/cyberduck/core/brick/BrickShareFeatureTest.java @@ -36,10 +36,10 @@ public class BrickShareFeatureTest extends AbstractBrickTest { @Test public void toDownloadUrl() throws Exception { - final Path directory = new BrickDirectoryFeature(session).mkdir(new Path(new AlphanumericRandomStringService().random(), + final Path directory = new BrickDirectoryFeature(session).mkdir(new BrickWriteFeature(session), new Path(new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory, Path.Type.volume)), new TransferStatus()); final Path test = new BrickTouchFeature(session).touch( - new Path(directory, String.format("%s", new AlphanumericRandomStringService().random()), EnumSet.of(Path.Type.file)), new TransferStatus()); + new BrickWriteFeature(session), new Path(directory, String.format("%s", new AlphanumericRandomStringService().random()), EnumSet.of(Path.Type.file)), new TransferStatus()); assertNotNull(new BrickShareFeature(session).toDownloadUrl(test, Share.Sharee.world, null, new DisabledPasswordCallback()).getUrl()); new BrickDeleteFeature(session).delete(Collections.singletonList(directory), new DisabledPasswordCallback(), new Delete.DisabledCallback()); } diff --git a/brick/src/test/java/ch/cyberduck/core/brick/BrickThresholdUploadFeatureTest.java b/brick/src/test/java/ch/cyberduck/core/brick/BrickThresholdUploadFeatureTest.java index e9394b6dd8..74fe3975b4 100644 --- a/brick/src/test/java/ch/cyberduck/core/brick/BrickThresholdUploadFeatureTest.java +++ b/brick/src/test/java/ch/cyberduck/core/brick/BrickThresholdUploadFeatureTest.java @@ -54,7 +54,7 @@ public class BrickThresholdUploadFeatureTest extends AbstractBrickTest { final TransferStatus status = new TransferStatus(); status.setLength(content.length); final BytecountStreamListener count = new BytecountStreamListener(); - feature.upload(test, local, new BandwidthThrottle(BandwidthThrottle.UNLIMITED), + feature.upload(new BrickWriteFeature(session), test, local, new BandwidthThrottle(BandwidthThrottle.UNLIMITED), new DisabledProgressListener(), count, status, new DisabledLoginCallback()); assertEquals(content.length, count.getSent()); assertTrue(new BrickFindFeature(session).find(test)); @@ -80,7 +80,7 @@ public class BrickThresholdUploadFeatureTest extends AbstractBrickTest { final TransferStatus status = new TransferStatus(); status.setLength(content.length); final BytecountStreamListener count = new BytecountStreamListener(); - feature.upload(test, local, new BandwidthThrottle(BandwidthThrottle.UNLIMITED), + feature.upload(new BrickWriteFeature(session), test, local, new BandwidthThrottle(BandwidthThrottle.UNLIMITED), new DisabledProgressListener(), count, status, new DisabledLoginCallback()); assertEquals(content.length, count.getSent()); assertTrue(status.isComplete()); diff --git a/brick/src/test/java/ch/cyberduck/core/brick/BrickTimestampFeatureTest.java b/brick/src/test/java/ch/cyberduck/core/brick/BrickTimestampFeatureTest.java index 46ae60a771..8fac18dfc7 100644 --- a/brick/src/test/java/ch/cyberduck/core/brick/BrickTimestampFeatureTest.java +++ b/brick/src/test/java/ch/cyberduck/core/brick/BrickTimestampFeatureTest.java @@ -39,7 +39,7 @@ public class BrickTimestampFeatureTest extends AbstractBrickTest { @Test public void testSetTimestampFile() throws Exception { - final Path file = new BrickTouchFeature(session).touch(new Path(new DefaultHomeFinderService(session).find(), + final Path file = new BrickTouchFeature(session).touch(new BrickWriteFeature(session), new Path(new DefaultHomeFinderService(session).find(), new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)), new TransferStatus()); final TransferStatus status = new TransferStatus().setModified(5000L); new BrickTimestampFeature(session).setTimestamp(file, status); @@ -52,7 +52,7 @@ public class BrickTimestampFeatureTest extends AbstractBrickTest { @Test public void testSetTimestampDirectory() throws Exception { - final Path file = new BrickDirectoryFeature(session).mkdir(new Path(new DefaultHomeFinderService(session).find(), + final Path file = new BrickDirectoryFeature(session).mkdir(new BrickWriteFeature(session), new Path(new DefaultHomeFinderService(session).find(), new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory)), new TransferStatus()); new BrickTimestampFeature(session).setTimestamp(file, 5000L); assertEquals(5000L, new BrickAttributesFinderFeature(session).find(file).getModificationDate()); diff --git a/brick/src/test/java/ch/cyberduck/core/brick/BrickTouchFeatureTest.java b/brick/src/test/java/ch/cyberduck/core/brick/BrickTouchFeatureTest.java index 2589036bc9..7f9cc098a4 100644 --- a/brick/src/test/java/ch/cyberduck/core/brick/BrickTouchFeatureTest.java +++ b/brick/src/test/java/ch/cyberduck/core/brick/BrickTouchFeatureTest.java @@ -38,13 +38,13 @@ public class BrickTouchFeatureTest extends AbstractBrickTest { @Test public void testCaseSensitivity() throws Exception { - final Path container = new BrickDirectoryFeature(session).mkdir(new Path( + final Path container = new BrickDirectoryFeature(session).mkdir(new BrickWriteFeature(session), new Path( new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory, Path.Type.volume)), new TransferStatus()); final String filename = StringUtils.lowerCase(new AlphanumericRandomStringService().random()); final Path lowerCase = new BrickTouchFeature(session) - .touch(new Path(container, filename, EnumSet.of(Path.Type.file)), new TransferStatus().setLength(0L)); + .touch(new BrickWriteFeature(session), new Path(container, filename, EnumSet.of(Path.Type.file)), new TransferStatus().setLength(0L)); final Path upperCase = new BrickTouchFeature(session) - .touch(new Path(container, StringUtils.capitalize(filename), EnumSet.of(Path.Type.file)), new TransferStatus().setLength(0L)); + .touch(new BrickWriteFeature(session), new Path(container, StringUtils.capitalize(filename), EnumSet.of(Path.Type.file)), new TransferStatus().setLength(0L)); assertTrue(new BrickFindFeature(session).find(lowerCase)); assertTrue(new BrickFindFeature(session).find(upperCase)); assertEquals(1, new BrickListService(session).list(container, new DisabledListProgressListener()).size()); diff --git a/brick/src/test/java/ch/cyberduck/core/brick/BrickUploadFeatureTest.java b/brick/src/test/java/ch/cyberduck/core/brick/BrickUploadFeatureTest.java index 0068deb211..71b2310482 100644 --- a/brick/src/test/java/ch/cyberduck/core/brick/BrickUploadFeatureTest.java +++ b/brick/src/test/java/ch/cyberduck/core/brick/BrickUploadFeatureTest.java @@ -44,7 +44,7 @@ public class BrickUploadFeatureTest extends AbstractBrickTest { @Test public void testUploadSmallPart() throws Exception { - final BrickUploadFeature feature = new BrickUploadFeature(session, new BrickWriteFeature(session), 5 * 1024L * 1024L, 2); + final BrickUploadFeature feature = new BrickUploadFeature(session, 5 * 1024L * 1024L, 2); final Path root = new Path("/", EnumSet.of(Path.Type.directory, Path.Type.volume)); final String name = new AlphanumericRandomStringService().random(); final Path test = new Path(root, name, EnumSet.of(Path.Type.file)); @@ -56,7 +56,7 @@ public class BrickUploadFeatureTest extends AbstractBrickTest { status.setLength(content.length); status.setMime("text/plain"); final BytecountStreamListener count = new BytecountStreamListener(); - feature.upload(test, local, new BandwidthThrottle(BandwidthThrottle.UNLIMITED), + feature.upload(new BrickWriteFeature(session), test, local, new BandwidthThrottle(BandwidthThrottle.UNLIMITED), new DisabledProgressListener(), count, status, new DisabledLoginCallback()); assertEquals(content.length, count.getSent()); assertTrue(status.isComplete()); @@ -73,7 +73,7 @@ public class BrickUploadFeatureTest extends AbstractBrickTest { @Test public void testUploadSinglePart() throws Exception { - final BrickUploadFeature feature = new BrickUploadFeature(session, new BrickWriteFeature(session), 5 * 1024L * 1024L, 2); + final BrickUploadFeature feature = new BrickUploadFeature(session, 5 * 1024L * 1024L, 2); final Path root = new Path("/", EnumSet.of(Path.Type.directory, Path.Type.volume)); final String name = new AlphanumericRandomStringService().random(); final Path test = new Path(root, name, EnumSet.of(Path.Type.file)); @@ -85,7 +85,7 @@ public class BrickUploadFeatureTest extends AbstractBrickTest { status.setLength(content.length); status.setMime("text/plain"); final BytecountStreamListener count = new BytecountStreamListener(); - feature.upload(test, local, new BandwidthThrottle(BandwidthThrottle.UNLIMITED), + feature.upload(new BrickWriteFeature(session), test, local, new BandwidthThrottle(BandwidthThrottle.UNLIMITED), new DisabledProgressListener(), count, status, new DisabledLoginCallback()); assertEquals(content.length, count.getSent()); assertTrue(status.isComplete()); @@ -103,7 +103,7 @@ public class BrickUploadFeatureTest extends AbstractBrickTest { @Test public void testUploadMultipleParts() throws Exception { // 5L * 1024L * 1024L - final BrickUploadFeature feature = new BrickUploadFeature(session, new BrickWriteFeature(session), 5 * 1024L * 1024L, 5); + final BrickUploadFeature feature = new BrickUploadFeature(session, 5 * 1024L * 1024L, 5); final Path root = new Path("/", EnumSet.of(Path.Type.directory, Path.Type.volume)); final Path test = new Path(root, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)); final Local local = new Local(System.getProperty("java.io.tmpdir"), UUID.randomUUID().toString()); @@ -113,7 +113,7 @@ public class BrickUploadFeatureTest extends AbstractBrickTest { final TransferStatus status = new TransferStatus(); status.setLength(content.length); final BytecountStreamListener count = new BytecountStreamListener(); - feature.upload(test, local, new BandwidthThrottle(BandwidthThrottle.UNLIMITED), new DisabledProgressListener(), count, status, new DisabledConnectionCallback()); + feature.upload(new BrickWriteFeature(session), test, local, new BandwidthThrottle(BandwidthThrottle.UNLIMITED), new DisabledProgressListener(), count, status, new DisabledConnectionCallback()); assertEquals(content.length, count.getSent()); assertTrue(status.isComplete()); assertNotSame(PathAttributes.EMPTY, status.getResponse()); diff --git a/brick/src/test/java/ch/cyberduck/core/brick/cryptomator/BrickListServiceTest.java b/brick/src/test/java/ch/cyberduck/core/brick/cryptomator/BrickListServiceTest.java index ea3b8c9f55..54f85e1b23 100644 --- a/brick/src/test/java/ch/cyberduck/core/brick/cryptomator/BrickListServiceTest.java +++ b/brick/src/test/java/ch/cyberduck/core/brick/cryptomator/BrickListServiceTest.java @@ -25,9 +25,11 @@ import ch.cyberduck.core.brick.BrickDeleteFeature; import ch.cyberduck.core.brick.BrickDirectoryFeature; import ch.cyberduck.core.brick.BrickListService; import ch.cyberduck.core.brick.BrickWriteFeature; +import ch.cyberduck.core.brick.io.swagger.client.model.FileEntity; import ch.cyberduck.core.cryptomator.CryptoVault; import ch.cyberduck.core.cryptomator.features.CryptoListService; import ch.cyberduck.core.cryptomator.features.CryptoTouchFeature; +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; @@ -52,7 +54,7 @@ public class BrickListServiceTest extends AbstractBrickTest { @Test public void testListCryptomator() throws Exception { - final Path home = new BrickDirectoryFeature(session).mkdir(new Path(new AlphanumericRandomStringService().random(), + final Path home = new BrickDirectoryFeature(session).mkdir(new BrickWriteFeature(session), new Path(new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory, Path.Type.volume)), new TransferStatus()); final Path vault = new Path(home, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory)); final Path test = new Path(vault, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)); @@ -60,7 +62,8 @@ public class BrickListServiceTest extends AbstractBrickTest { cryptomator.create(session, new VaultCredentials("test"), vaultVersion); session.withRegistry(new DefaultVaultRegistry(new DisabledPasswordCallback(), cryptomator)); assertTrue(new CryptoListService(session, new BrickListService(session), cryptomator).list(vault, new DisabledListProgressListener()).isEmpty()); - new CryptoTouchFeature<>(session, new CryptoTouchFeature<>(session, new DefaultTouchFeature<>(new BrickWriteFeature(session)), new BrickWriteFeature(session), cryptomator), new BrickWriteFeature(session), cryptomator).touch(test, new TransferStatus()); + new CryptoTouchFeature<>(session, new DefaultTouchFeature(session), cryptomator).touch( + new CryptoWriteFeature<>(session, new BrickWriteFeature(session), cryptomator), test, new TransferStatus()); Assert.assertEquals(test, new CryptoListService(session, new BrickListService(session), cryptomator).list(vault, new DisabledListProgressListener()).get(0)); cryptomator.getFeature(session, Delete.class, new BrickDeleteFeature(session)).delete(Arrays.asList(test, vault), new DisabledLoginCallback(), new Delete.DisabledCallback()); } diff --git a/brick/src/test/java/ch/cyberduck/core/cryptomator/DefaultTouchFeatureTest.java b/brick/src/test/java/ch/cyberduck/core/cryptomator/DefaultTouchFeatureTest.java index 82919bf1cf..6e518e70b3 100644 --- a/brick/src/test/java/ch/cyberduck/core/cryptomator/DefaultTouchFeatureTest.java +++ b/brick/src/test/java/ch/cyberduck/core/cryptomator/DefaultTouchFeatureTest.java @@ -23,7 +23,9 @@ import ch.cyberduck.core.brick.AbstractBrickTest; import ch.cyberduck.core.brick.BrickDeleteFeature; import ch.cyberduck.core.brick.BrickFindFeature; import ch.cyberduck.core.brick.BrickWriteFeature; +import ch.cyberduck.core.brick.io.swagger.client.model.FileEntity; 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,8 +60,8 @@ public class DefaultTouchFeatureTest extends AbstractBrickTest { 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 DefaultTouchFeature(new BrickWriteFeature(session)), new BrickWriteFeature(session), cryptomator).touch( - new Path(vault, new AlphanumericRandomStringService(130).random(), EnumSet.of(Path.Type.file)), status); + final Path test = new CryptoTouchFeature<>(session, new DefaultTouchFeature(session), cryptomator).touch( + new CryptoWriteFeature<>(session, new BrickWriteFeature(session), cryptomator), new Path(vault, new AlphanumericRandomStringService(130).random(), EnumSet.of(Path.Type.file)), status); assertEquals(TransferStatus.UNKNOWN_LENGTH, status.getResponse().getSize()); assertTrue(cryptomator.getFeature(session, Find.class, new BrickFindFeature(session)).find(test)); cryptomator.getFeature(session, Delete.class, new BrickDeleteFeature(session)).delete(Arrays.asList(test, vault), new DisabledLoginCallback(), new Delete.DisabledCallback()); @@ -74,8 +76,8 @@ public class DefaultTouchFeatureTest extends AbstractBrickTest { 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 DefaultTouchFeature(new BrickWriteFeature(session)), new BrickWriteFeature(session), cryptomator).touch( - new Path(vault, new AlphanumericRandomStringService(130).random(), EnumSet.of(Path.Type.file)), status); + final Path test = new CryptoTouchFeature<>(session, new DefaultTouchFeature(session), cryptomator).touch( + new CryptoWriteFeature<>(session, new BrickWriteFeature(session), cryptomator), new Path(vault, new AlphanumericRandomStringService(130).random(), EnumSet.of(Path.Type.file)), status); assertEquals(TransferStatus.UNKNOWN_LENGTH, status.getResponse().getSize()); assertTrue(cryptomator.getFeature(session, Find.class, new DefaultFindFeature(session)).find(test)); cryptomator.getFeature(session, Delete.class, new BrickDeleteFeature(session)).delete(Arrays.asList(test, vault), new DisabledLoginCallback(), new Delete.DisabledCallback()); diff --git a/brick/src/test/java/ch/cyberduck/core/worker/CopyWorkerTest.java b/brick/src/test/java/ch/cyberduck/core/worker/CopyWorkerTest.java index ac15dd5a14..ab54439a44 100644 --- a/brick/src/test/java/ch/cyberduck/core/worker/CopyWorkerTest.java +++ b/brick/src/test/java/ch/cyberduck/core/worker/CopyWorkerTest.java @@ -58,7 +58,7 @@ public class CopyWorkerTest extends AbstractBrickTest { final byte[] random = RandomUtils.nextBytes(3247); IOUtils.write(random, local.getOutputStream(false)); final TransferStatus status = new TransferStatus().setLength(random.length); - new BrickUploadFeature(session, new BrickWriteFeature(session)).upload(source, local, new BandwidthThrottle(BandwidthThrottle.UNLIMITED), + new BrickUploadFeature(session).upload(new BrickWriteFeature(session), source, local, new BandwidthThrottle(BandwidthThrottle.UNLIMITED), new DisabledProgressListener(), new DisabledStreamListener(), status, new DisabledLoginCallback()); assertTrue(new BrickFindFeature(session).find(source)); final CopyWorker worker = new CopyWorker(Collections.singletonMap(source, target), new SessionPool.SingleSessionPool(session), PathCache.empty(), new DisabledProgressListener(), new DisabledConnectionCallback()); @@ -78,12 +78,12 @@ public class CopyWorkerTest extends AbstractBrickTest { final byte[] random = RandomUtils.nextBytes(3247); IOUtils.write(random, local.getOutputStream(false)); final TransferStatus status = new TransferStatus().setLength(random.length); - new BrickUploadFeature(session, new BrickWriteFeature(session)).upload(sourceFile, local, new BandwidthThrottle(BandwidthThrottle.UNLIMITED), + new BrickUploadFeature(session).upload(new BrickWriteFeature(session), sourceFile, local, new BandwidthThrottle(BandwidthThrottle.UNLIMITED), new DisabledProgressListener(), new DisabledStreamListener(), status, new DisabledLoginCallback()); assertTrue(new BrickFindFeature(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 BrickDirectoryFeature(session).mkdir(targetFolder, new TransferStatus()); + new BrickDirectoryFeature(session).mkdir(new BrickWriteFeature(session), targetFolder, new TransferStatus()); assertTrue(new BrickFindFeature(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()); @@ -98,13 +98,13 @@ public class CopyWorkerTest extends AbstractBrickTest { public void testCopyDirectory() throws Exception { final Path home = new DefaultHomeFinderService(session).find(); - final Path folder = new BrickDirectoryFeature(session).mkdir(new Path(home, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory)), new TransferStatus()); + final Path folder = new BrickDirectoryFeature(session).mkdir(new BrickWriteFeature(session), new Path(home, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory)), new TransferStatus()); final Path sourceFile = new Path(folder, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)); final Local local = new Local(System.getProperty("java.io.tmpdir"), sourceFile.getName()); final byte[] random = RandomUtils.nextBytes(3247); IOUtils.write(random, local.getOutputStream(false)); final TransferStatus status = new TransferStatus().setLength(random.length); - new BrickUploadFeature(session, new BrickWriteFeature(session)).upload(sourceFile, local, new BandwidthThrottle(BandwidthThrottle.UNLIMITED), + new BrickUploadFeature(session).upload(new BrickWriteFeature(session), sourceFile, local, new BandwidthThrottle(BandwidthThrottle.UNLIMITED), new DisabledProgressListener(), new DisabledStreamListener(), status, new DisabledLoginCallback()); assertTrue(new BrickFindFeature(session).find(folder)); assertTrue(new BrickFindFeature(session).find(sourceFile)); diff --git a/build.xml b/build.xml index f4c0e516f8..a5984b2466 100644 --- a/build.xml +++ b/build.xml @@ -136,6 +136,10 @@ + + + + @@ -147,7 +151,9 @@ - + + + @@ -166,4 +172,4 @@ - \ No newline at end of file + diff --git a/cli/dll/pom.xml b/cli/dll/pom.xml index b0eafac577..238d1da6e1 100644 --- a/cli/dll/pom.xml +++ b/cli/dll/pom.xml @@ -20,7 +20,7 @@ ch.cyberduck parent ../../pom.xml - 9.2.0-SNAPSHOT + 9.4.0-SNAPSHOT Cyberduck.Cli pom diff --git a/cli/linux/pom.xml b/cli/linux/pom.xml index 6148029136..ecf0488b1d 100644 --- a/cli/linux/pom.xml +++ b/cli/linux/pom.xml @@ -20,7 +20,7 @@ ch.cyberduck parent ../../pom.xml - 9.2.0-SNAPSHOT + 9.4.0-SNAPSHOT cli-linux Cyberduck CLI Linux diff --git a/cli/osx/pom.xml b/cli/osx/pom.xml index a1fb6580e2..899171914b 100644 --- a/cli/osx/pom.xml +++ b/cli/osx/pom.xml @@ -20,7 +20,7 @@ ch.cyberduck parent ../../pom.xml - 9.2.0-SNAPSHOT + 9.4.0-SNAPSHOT cli-osx Cyberduck CLI Mac @@ -117,7 +117,7 @@ net.temurin jdk zip - 21.0.7 + 21.0.9 ${project.build.directory} diff --git a/cli/pom.xml b/cli/pom.xml index f57a0171c4..b6c545f203 100644 --- a/cli/pom.xml +++ b/cli/pom.xml @@ -19,7 +19,7 @@ ch.cyberduck parent - 9.2.0-SNAPSHOT + 9.4.0-SNAPSHOT cli @@ -75,7 +75,7 @@ commons-cli commons-cli - 1.9.0 + 1.11.0 org.fusesource.jansi diff --git a/cli/src/main/java/ch/cyberduck/cli/CommandLineUriParser.java b/cli/src/main/java/ch/cyberduck/cli/CommandLineUriParser.java index dd4b6a1275..c7ffc2eae2 100644 --- a/cli/src/main/java/ch/cyberduck/cli/CommandLineUriParser.java +++ b/cli/src/main/java/ch/cyberduck/cli/CommandLineUriParser.java @@ -56,7 +56,7 @@ public class CommandLineUriParser { host.setDefaultPath(directory.getParent().getAbsolute()); } if(input.hasOption(TerminalOptionsBuilder.Params.udt.name())) { - host.setTransfer(Host.TransferType.udt); + host.setTransferType(Host.TransferType.udt); } log.debug("Parsed {} as {}", uri, host); return host; diff --git a/cli/src/main/java/ch/cyberduck/cli/Terminal.java b/cli/src/main/java/ch/cyberduck/cli/Terminal.java index c7f002840c..93bb5c2c53 100644 --- a/cli/src/main/java/ch/cyberduck/cli/Terminal.java +++ b/cli/src/main/java/ch/cyberduck/cli/Terminal.java @@ -379,10 +379,10 @@ public class Terminal { final TerminalTransferErrorCallback error; final Host host = transfer.getSource(); if(input.hasOption(TerminalOptionsBuilder.Params.parallel.name())) { - host.setTransfer(Host.TransferType.concurrent); + host.setTransferType(Host.TransferType.concurrent); } else { - host.setTransfer(Host.TransferType.newconnection); + host.setTransferType(Host.TransferType.newconnection); } if(input.hasOption(TerminalOptionsBuilder.Params.quiet.name())) { error = new TerminalTransferErrorCallback(reader -> false); diff --git a/cli/src/main/java/ch/cyberduck/cli/TerminalAlertCallback.java b/cli/src/main/java/ch/cyberduck/cli/TerminalAlertCallback.java index 9eb2f7ce5c..0dd57b6a66 100644 --- a/cli/src/main/java/ch/cyberduck/cli/TerminalAlertCallback.java +++ b/cli/src/main/java/ch/cyberduck/cli/TerminalAlertCallback.java @@ -24,12 +24,17 @@ import ch.cyberduck.core.exception.BackgroundException; import ch.cyberduck.core.threading.AlertCallback; import ch.cyberduck.core.threading.DefaultFailureDiagnostics; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; + public class TerminalAlertCallback implements AlertCallback { + private static final Logger log = LogManager.getLogger(TerminalAlertCallback.class); private final Console console = new Console(); @Override public boolean alert(final Host host, final BackgroundException failure) { + log.warn("Notify for failure {}", failure.toString()); this.print(failure); // Never repeat return false; diff --git a/cli/src/main/java/ch/cyberduck/cli/TerminalAppender.java b/cli/src/main/java/ch/cyberduck/cli/TerminalAppender.java index 5d107b6823..5590be155a 100644 --- a/cli/src/main/java/ch/cyberduck/cli/TerminalAppender.java +++ b/cli/src/main/java/ch/cyberduck/cli/TerminalAppender.java @@ -15,14 +15,21 @@ package ch.cyberduck.cli; * GNU General Public License for more details. */ +import org.apache.logging.log4j.core.Appender; +import org.apache.logging.log4j.core.Core; import org.apache.logging.log4j.core.Layout; import org.apache.logging.log4j.core.LogEvent; import org.apache.logging.log4j.core.appender.AbstractAppender; import org.apache.logging.log4j.core.config.Property; +import org.apache.logging.log4j.core.config.plugins.Plugin; import org.fusesource.jansi.Ansi; import java.nio.charset.StandardCharsets; +@Plugin( + name = "Terminal", + category = Core.CATEGORY_NAME, + elementType = Appender.ELEMENT_TYPE) public class TerminalAppender extends AbstractAppender { private final Console console = new Console(); @@ -39,10 +46,10 @@ public class TerminalAppender extends AbstractAppender { final StringBuilder buffer = new StringBuilder(); buffer.append(new String(getLayout().toByteArray(event), StandardCharsets.UTF_8)); console.printf("\r%s%s%s", Ansi.ansi() - .fg(Ansi.Color.YELLOW) - .saveCursorPosition() - .eraseLine(Ansi.Erase.ALL) - .restoreCursorPosition(), buffer.toString(), - Ansi.ansi().reset()); + .fg(Ansi.Color.YELLOW) + .saveCursorPosition() + .eraseLine(Ansi.Erase.ALL) + .restoreCursorPosition(), buffer.toString(), + Ansi.ansi().reset()); } } diff --git a/cli/src/main/java/ch/cyberduck/cli/TerminalLoggingAppender.java b/cli/src/main/java/ch/cyberduck/cli/TerminalLoggingAppender.java deleted file mode 100644 index 6ae802570c..0000000000 --- a/cli/src/main/java/ch/cyberduck/cli/TerminalLoggingAppender.java +++ /dev/null @@ -1,60 +0,0 @@ -package ch.cyberduck.cli; - -/* - * Copyright (c) 2002-2015 David Kocher. All rights reserved. - * http://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. - * - * Bug fixes, suggestions and comments should be sent to: - * feedback@cyberduck.io - */ - -import org.apache.commons.lang3.exception.ExceptionUtils; -import org.apache.logging.log4j.core.Layout; -import org.apache.logging.log4j.core.LogEvent; -import org.apache.logging.log4j.core.appender.AbstractAppender; -import org.apache.logging.log4j.core.config.Property; -import org.apache.logging.log4j.util.Strings; -import org.fusesource.jansi.Ansi; - -import java.nio.charset.StandardCharsets; - -public class TerminalLoggingAppender extends AbstractAppender { - - private final Console console = new Console(); - - public TerminalLoggingAppender(final Layout layout) { - super(TerminalAppender.class.getName(), null, layout, true, Property.EMPTY_ARRAY); - } - - @Override - public void append(final LogEvent event) { - final StringBuilder buffer = new StringBuilder(); - buffer.append(new String(getLayout().toByteArray(event), StandardCharsets.UTF_8)); - if(ignoreExceptions()) { - final Throwable thrown = event.getThrown(); - if(thrown != null) { - buffer.append(Strings.LINE_SEPARATOR); - final String[] trace = ExceptionUtils.getStackFrames(thrown); - for(final String t : trace) { - buffer.append(t).append(Strings.LINE_SEPARATOR); - } - } - } - console.printf("\r%s%s%s", Ansi.ansi() - .saveCursorPosition() - .eraseLine(Ansi.Erase.ALL) - .fg(Ansi.Color.MAGENTA) - .restoreCursorPosition(), - buffer.toString(), Ansi.ansi().reset()); - } -} diff --git a/cli/src/main/java/ch/cyberduck/cli/TerminalLoginService.java b/cli/src/main/java/ch/cyberduck/cli/TerminalLoginService.java index 1b1e0a4f0a..2c50d1030f 100644 --- a/cli/src/main/java/ch/cyberduck/cli/TerminalLoginService.java +++ b/cli/src/main/java/ch/cyberduck/cli/TerminalLoginService.java @@ -27,9 +27,9 @@ import ch.cyberduck.core.LoginCallback; import ch.cyberduck.core.LoginOptions; import ch.cyberduck.core.PasswordStoreFactory; import ch.cyberduck.core.exception.ConnectionCanceledException; -import ch.cyberduck.core.exception.LoginCanceledException; import ch.cyberduck.core.exception.LoginFailureException; import ch.cyberduck.core.preferences.PreferencesFactory; +import ch.cyberduck.core.ssl.X509KeyManager; import org.apache.commons.cli.CommandLine; import org.apache.commons.lang3.StringUtils; @@ -44,7 +44,7 @@ public class TerminalLoginService extends KeychainLoginService { } @Override - public void validate(final Host bookmark, final LoginCallback prompt, final LoginOptions options) throws ConnectionCanceledException, LoginFailureException { + public void validate(final Host bookmark, final X509KeyManager keys, final LoginCallback prompt, final LoginOptions options) throws ConnectionCanceledException, LoginFailureException { final Credentials credentials = bookmark.getCredentials(); if(input.hasOption(TerminalOptionsBuilder.Params.anonymous.name())) { credentials.setUsername(PreferencesFactory.get().getProperty("connection.login.anon.name")); @@ -61,6 +61,6 @@ public class TerminalLoginService extends KeychainLoginService { if(StringUtils.isNotBlank(credentials.getUsername()) && StringUtils.isNotBlank(credentials.getPassword())) { return; } - super.validate(bookmark, prompt, options); + super.validate(bookmark, keys, prompt, options); } } diff --git a/cli/src/main/java/ch/cyberduck/cli/TerminalPreferences.java b/cli/src/main/java/ch/cyberduck/cli/TerminalPreferences.java index dd13d2bfc4..ca4bbc5ece 100644 --- a/cli/src/main/java/ch/cyberduck/cli/TerminalPreferences.java +++ b/cli/src/main/java/ch/cyberduck/cli/TerminalPreferences.java @@ -81,7 +81,7 @@ public class TerminalPreferences extends Preferences { this.setDefault("logging", "fatal"); this.setDefault("website.home", "https://duck.sh/"); - this.setDefault("website.help", "https://help.duck.sh/"); + this.setDefault("website.help", "https://docs.duck.sh/"); System.setProperty("jna.library.path", this.getProperty("java.library.path")); diff --git a/cli/src/main/java/ch/cyberduck/cli/TerminalTranscriptListener.java b/cli/src/main/java/ch/cyberduck/cli/TerminalTranscriptListener.java index 5e519f2905..bff50cba15 100644 --- a/cli/src/main/java/ch/cyberduck/cli/TerminalTranscriptListener.java +++ b/cli/src/main/java/ch/cyberduck/cli/TerminalTranscriptListener.java @@ -30,9 +30,11 @@ public class TerminalTranscriptListener implements TranscriptListener { public void log(final Type request, final String message) { switch(request) { case request: + case requestheader: console.printf("%n%s> %s%s", Ansi.ansi().fg(Ansi.Color.GREEN), message, Ansi.ansi().reset()); break; case response: + case responseheader: console.printf("%n%s< %s%s", Ansi.ansi().fg(Ansi.Color.RED), message, Ansi.ansi().reset()); break; } diff --git a/cli/src/main/wix/Bootstrapper/duck.bootstrapper.wixproj b/cli/src/main/wix/Bootstrapper/duck.bootstrapper.wixproj index c6d00922eb..8c2f66cd38 100644 --- a/cli/src/main/wix/Bootstrapper/duck.bootstrapper.wixproj +++ b/cli/src/main/wix/Bootstrapper/duck.bootstrapper.wixproj @@ -11,4 +11,4 @@ - \ No newline at end of file + diff --git a/cli/src/main/wix/Bundle/Cyberduck CLI-WiX.wxs b/cli/src/main/wix/Bundle/Cyberduck CLI-WiX.wxs index cb13f9ddfc..747ba9d212 100644 --- a/cli/src/main/wix/Bundle/Cyberduck CLI-WiX.wxs +++ b/cli/src/main/wix/Bundle/Cyberduck CLI-WiX.wxs @@ -26,4 +26,4 @@ - \ No newline at end of file + diff --git a/cli/src/main/wix/Bundle/duck.bundle.wixproj b/cli/src/main/wix/Bundle/duck.bundle.wixproj index 709eca5b97..5cd9e85252 100644 --- a/cli/src/main/wix/Bundle/duck.bundle.wixproj +++ b/cli/src/main/wix/Bundle/duck.bundle.wixproj @@ -9,4 +9,4 @@ - \ No newline at end of file + diff --git a/cli/src/main/wix/Directory.Build.props b/cli/src/main/wix/Directory.Build.props index 41cf1d2014..aaea473a94 100644 --- a/cli/src/main/wix/Directory.Build.props +++ b/cli/src/main/wix/Directory.Build.props @@ -25,4 +25,4 @@ $(CyberduckDir)setup\wix\ - \ No newline at end of file + diff --git a/cli/windows/pom.xml b/cli/windows/pom.xml index 215b9482e3..90b9f33209 100644 --- a/cli/windows/pom.xml +++ b/cli/windows/pom.xml @@ -20,7 +20,7 @@ ch.cyberduck parent ../../pom.xml - 9.2.0-SNAPSHOT + 9.4.0-SNAPSHOT cli-windows Cyberduck CLI Windows diff --git a/codesign.xml b/codesign.xml index 65cef030c9..31e69aa81f 100644 --- a/codesign.xml +++ b/codesign.xml @@ -23,25 +23,29 @@ + else="${user.home}/Library/Keychains/login.keychain-db"> + else="${user.home}/Library/Keychains/login.keychain-db"> + + + + + diff --git a/core/dll/pom.xml b/core/dll/pom.xml index 8df60430c3..91bb2a3232 100644 --- a/core/dll/pom.xml +++ b/core/dll/pom.xml @@ -5,7 +5,7 @@ ch.cyberduck parent ../../pom.xml - 9.2.0-SNAPSHOT + 9.4.0-SNAPSHOT Cyberduck.Core pom @@ -116,4 +116,4 @@ provided - \ No newline at end of file + diff --git a/core/dylib/pom.xml b/core/dylib/pom.xml index 003de7809a..5fb4c02bac 100644 --- a/core/dylib/pom.xml +++ b/core/dylib/pom.xml @@ -5,7 +5,7 @@ ch.cyberduck parent ../../pom.xml - 9.2.0-SNAPSHOT + 9.4.0-SNAPSHOT libcore diff --git a/core/dylib/src/main/java/ch/cyberduck/core/KeychainPasswordStore.java b/core/dylib/src/main/java/ch/cyberduck/core/KeychainPasswordStore.java index 05a0d3434c..193d2a510b 100644 --- a/core/dylib/src/main/java/ch/cyberduck/core/KeychainPasswordStore.java +++ b/core/dylib/src/main/java/ch/cyberduck/core/KeychainPasswordStore.java @@ -145,6 +145,9 @@ public final class KeychainPasswordStore extends DefaultHostPasswordStore implem if(errSecItemNotFound == err) { return null; } + if(errSecUserCanceled == err) { + return null; + } log.error("Failure reading credentials for {} in keychain", serviceName); throw new LocalAccessDeniedException(SecurityFunctions.library.SecCopyErrorMessageString(err, null)); } diff --git a/core/dylib/src/main/java/ch/cyberduck/core/diagnostics/SystemConfigurationReachability.java b/core/dylib/src/main/java/ch/cyberduck/core/diagnostics/SystemConfigurationReachability.java index 7c1dfc165e..506189b778 100644 --- a/core/dylib/src/main/java/ch/cyberduck/core/diagnostics/SystemConfigurationReachability.java +++ b/core/dylib/src/main/java/ch/cyberduck/core/diagnostics/SystemConfigurationReachability.java @@ -19,6 +19,7 @@ package ch.cyberduck.core.diagnostics; * dkocher@cyberduck.ch */ +import ch.cyberduck.binding.Delegate; import ch.cyberduck.binding.Proxy; import ch.cyberduck.binding.foundation.NSNotification; import ch.cyberduck.binding.foundation.NSNotificationCenter; @@ -28,6 +29,8 @@ import ch.cyberduck.core.Host; import ch.cyberduck.core.HostnameConfiguratorFactory; import ch.cyberduck.core.exception.BackgroundException; import ch.cyberduck.core.idna.PunycodeConverter; +import ch.cyberduck.core.threading.DefaultThreadPool; +import ch.cyberduck.core.threading.ThreadPool; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; @@ -42,16 +45,25 @@ public class SystemConfigurationReachability implements Reachability { private final NSNotificationCenter notificationCenter = NSNotificationCenter.defaultCenter(); - private static final class NotificationFilterCallback extends Proxy { + private static final class ThreadedNotificationSelector extends Proxy { private final Callback proxy; + private final ThreadPool executor = new DefaultThreadPool("reachability", 1); - public NotificationFilterCallback(final Callback proxy) { + public ThreadedNotificationSelector(final Callback proxy) { this.proxy = proxy; } + @Delegate public void notify(final NSNotification notification) { log.debug("Received notification {}", notification); - proxy.change(); + executor.execute(() -> { + proxy.change(); + return null; + }); + } + + public void shutdown() { + executor.shutdown(true); } } @@ -60,7 +72,7 @@ public class SystemConfigurationReachability implements Reachability { final String url = toURL(bookmark); return new Reachability.Monitor() { private final SystemConfigurationReachability.Native monitor = SystemConfigurationReachability.Native.monitorForUrl(url); - private final NotificationFilterCallback listener = new NotificationFilterCallback(callback); + private final ThreadedNotificationSelector listener = new ThreadedNotificationSelector(callback); @Override public Monitor start() { @@ -76,6 +88,7 @@ public class SystemConfigurationReachability implements Reachability { if(monitor.stopReachabilityMonitor()) { notificationCenter.removeObserver(listener.id()); } + listener.shutdown(); return this; } }; diff --git a/core/dylib/src/main/java/ch/cyberduck/core/keychain/SecurityFunctions.java b/core/dylib/src/main/java/ch/cyberduck/core/keychain/SecurityFunctions.java index f1ba6c0855..d4d1f558b2 100644 --- a/core/dylib/src/main/java/ch/cyberduck/core/keychain/SecurityFunctions.java +++ b/core/dylib/src/main/java/ch/cyberduck/core/keychain/SecurityFunctions.java @@ -511,6 +511,7 @@ public interface SecurityFunctions extends Library { * The item cannot be found. */ int errSecItemNotFound = -25300; + int errSecUserCanceled = -128; /** * No error. */ diff --git a/core/dylib/src/main/java/ch/cyberduck/core/local/FinderLocal.java b/core/dylib/src/main/java/ch/cyberduck/core/local/FinderLocal.java index 475e2c5043..4ffe645fb2 100644 --- a/core/dylib/src/main/java/ch/cyberduck/core/local/FinderLocal.java +++ b/core/dylib/src/main/java/ch/cyberduck/core/local/FinderLocal.java @@ -25,9 +25,7 @@ import ch.cyberduck.core.Filter; import ch.cyberduck.core.Local; import ch.cyberduck.core.exception.AccessDeniedException; import ch.cyberduck.core.exception.LocalAccessDeniedException; -import ch.cyberduck.core.library.Native; import ch.cyberduck.core.preferences.BundleApplicationResourcesFinder; -import ch.cyberduck.core.preferences.PreferencesFactory; import ch.cyberduck.core.preferences.SecurityApplicationGroupSupportDirectoryFinder; import ch.cyberduck.core.preferences.TemporarySupportDirectoryFinder; @@ -50,13 +48,6 @@ public class FinderLocal extends Local { private static final Local TEMPORARY = new TemporarySupportDirectoryFinder().find(); private static final Local GROUP_CONTAINER = new SecurityApplicationGroupSupportDirectoryFinder().find(); - static { - Native.load("core"); - } - - private static final FilesystemBookmarkResolver resolver - = FilesystemBookmarkResolverFactory.get(); - public FinderLocal(final Local parent, final String name) { super(parent, name); } @@ -156,7 +147,7 @@ public class FinderLocal extends Local { */ @Override public NSURL lock(final boolean interactive) throws AccessDeniedException { - return this.lock(interactive, resolver); + return this.lock(interactive, FilesystemBookmarkResolverFactory.get()); } protected NSURL lock(final boolean interactive, final FilesystemBookmarkResolver resolver) throws AccessDeniedException { @@ -238,19 +229,6 @@ public class FinderLocal extends Local { return new LockReleaseProxyInputStream(super.getInputStream(resolved.path()), resolved); } - private static String resolveAlias(final String absolute) { - if(PreferencesFactory.get().getBoolean("local.alias.resolve")) { - return resolveAliasNative(absolute); - } - return absolute; - } - - /** - * @param absolute The absolute path of the alias file. - * @return The absolute path this alias is pointing to. - */ - private static native String resolveAliasNative(String absolute); - @Override public FinderLocalAttributes attributes() { return new FinderLocalAttributes(this); diff --git a/core/dylib/src/main/java/ch/cyberduck/core/local/FinderLocalAttributes.java b/core/dylib/src/main/java/ch/cyberduck/core/local/FinderLocalAttributes.java index f3df0e2264..9c1e9c25bf 100644 --- a/core/dylib/src/main/java/ch/cyberduck/core/local/FinderLocalAttributes.java +++ b/core/dylib/src/main/java/ch/cyberduck/core/local/FinderLocalAttributes.java @@ -82,7 +82,11 @@ public class FinderLocalAttributes extends LocalAttributes { } final NSDictionary dict = this.getNativeAttributes(path); // Returns an entry’s value given its key, or null if no value is associated with key. - return dict.objectForKey(name); + final NSObject value = dict.objectForKey(name); + if(null == value) { + throw new NotfoundException(name); + } + return value; } finally { local.release(resolved); @@ -92,6 +96,9 @@ public class FinderLocalAttributes extends LocalAttributes { @Override public long getSize() { try { + if(local.isDirectory()) { + return -1L; + } final NSObject object = this.getNativeAttribute(NSFileManager.NSFileSize); if(object.isKindOfClass(Rococoa.createClass("NSNumber", NSNumber._Class.class))) { final NSNumber number = Rococoa.cast(object, NSNumber.class); @@ -123,6 +130,20 @@ public class FinderLocalAttributes extends LocalAttributes { } } + @Override + public long getModificationDate() { + try { + final NSObject object = this.getNativeAttribute(NSFileManager.NSFileModificationDate); + if(object.isKindOfClass(Rococoa.createClass("NSDate", NSDate._Class.class))) { + return (long) (Rococoa.cast(object, NSDate.class).timeIntervalSince1970() * 1000); + } + return -1; + } + catch(AccessDeniedException | NotfoundException e) { + return -1; + } + } + @Override public String getOwner() { try { diff --git a/core/dylib/src/main/java/ch/cyberduck/core/logging/SystemLogAppender.java b/core/dylib/src/main/java/ch/cyberduck/core/logging/SystemLogAppender.java index c54ebe6ffd..724f01bc95 100644 --- a/core/dylib/src/main/java/ch/cyberduck/core/logging/SystemLogAppender.java +++ b/core/dylib/src/main/java/ch/cyberduck/core/logging/SystemLogAppender.java @@ -21,10 +21,13 @@ package ch.cyberduck.core.logging; import ch.cyberduck.binding.foundation.FoundationKitFunctions; import org.apache.commons.lang3.exception.ExceptionUtils; +import org.apache.logging.log4j.core.Appender; +import org.apache.logging.log4j.core.Core; import org.apache.logging.log4j.core.Layout; import org.apache.logging.log4j.core.LogEvent; import org.apache.logging.log4j.core.appender.AbstractAppender; import org.apache.logging.log4j.core.config.Property; +import org.apache.logging.log4j.core.config.plugins.Plugin; import org.apache.logging.log4j.util.Strings; import org.rococoa.internal.RococoaTypeMapper; @@ -37,10 +40,14 @@ import com.sun.jna.Native; /** * Redirect to NSLog(). Logs an error message to the Apple System Log facility. */ +@Plugin( + name = "SystemLog", + category = Core.CATEGORY_NAME, + elementType = Appender.ELEMENT_TYPE) public class SystemLogAppender extends AbstractAppender { private static final FoundationKitFunctions library = Native.load( - "Foundation", FoundationKitFunctions.class, Collections.singletonMap(Library.OPTION_TYPE_MAPPER, new RococoaTypeMapper())); + "Foundation", FoundationKitFunctions.class, Collections.singletonMap(Library.OPTION_TYPE_MAPPER, new RococoaTypeMapper())); public SystemLogAppender(final Layout layout) { super(SystemLogAppender.class.getName(), null, layout, true, Property.EMPTY_ARRAY); diff --git a/core/dylib/src/main/java/ch/cyberduck/core/logging/UnifiedSystemLogAppender.java b/core/dylib/src/main/java/ch/cyberduck/core/logging/UnifiedSystemLogAppender.java index 38942b4879..b7f6d6a5f3 100644 --- a/core/dylib/src/main/java/ch/cyberduck/core/logging/UnifiedSystemLogAppender.java +++ b/core/dylib/src/main/java/ch/cyberduck/core/logging/UnifiedSystemLogAppender.java @@ -19,13 +19,20 @@ import ch.cyberduck.core.library.Native; import org.apache.commons.lang3.exception.ExceptionUtils; import org.apache.logging.log4j.Level; +import org.apache.logging.log4j.core.Appender; +import org.apache.logging.log4j.core.Core; import org.apache.logging.log4j.core.LogEvent; import org.apache.logging.log4j.core.appender.AbstractAppender; import org.apache.logging.log4j.core.config.Property; +import org.apache.logging.log4j.core.config.plugins.Plugin; /** * Redirect to NSLog(). Logs an error message to the Apple System Log facility. */ +@Plugin( + name = "UnifiedSystemLog", + category = Core.CATEGORY_NAME, + elementType = Appender.ELEMENT_TYPE) public class UnifiedSystemLogAppender extends AbstractAppender { static { diff --git a/core/dylib/src/main/java/ch/cyberduck/core/logging/UnifiedSystemLogTranscriptListener.java b/core/dylib/src/main/java/ch/cyberduck/core/logging/UnifiedSystemLogTranscriptListener.java index a62285e4f7..562d00ca33 100644 --- a/core/dylib/src/main/java/ch/cyberduck/core/logging/UnifiedSystemLogTranscriptListener.java +++ b/core/dylib/src/main/java/ch/cyberduck/core/logging/UnifiedSystemLogTranscriptListener.java @@ -23,11 +23,6 @@ public class UnifiedSystemLogTranscriptListener implements TranscriptListener { @Override public void log(final Type type, final String message) { - switch(type) { - case request: - case response: - appender.log(UnifiedSystemLogAppender.OS_LOG_TYPE_INFO, "transcript", message); - break; - } + appender.log(UnifiedSystemLogAppender.OS_LOG_TYPE_INFO, "transcript", message); } } diff --git a/core/dylib/src/main/java/ch/cyberduck/core/preferences/ApplicationPreferences.java b/core/dylib/src/main/java/ch/cyberduck/core/preferences/ApplicationPreferences.java index 614826f0a3..8455f5e8df 100644 --- a/core/dylib/src/main/java/ch/cyberduck/core/preferences/ApplicationPreferences.java +++ b/core/dylib/src/main/java/ch/cyberduck/core/preferences/ApplicationPreferences.java @@ -15,7 +15,6 @@ package ch.cyberduck.core.preferences; * GNU General Public License for more details. */ -import ch.cyberduck.binding.foundation.NSBundle; import ch.cyberduck.core.ApplescriptTerminalService; import ch.cyberduck.core.Factory; import ch.cyberduck.core.IOKitSleepPreventer; @@ -128,10 +127,9 @@ public class ApplicationPreferences extends UserDefaultsPreferences { @Override protected void setDefaults() { super.setDefaults(); - if(null != NSBundle.mainBundle().appStoreReceiptURL()) { - if(null != NSBundle.mainBundle().appStoreReceiptURL().fileReferenceURL()) { - this.setDefault("factory.licensefactory.class", ReceiptFactory.class.getName()); - } + + if(null == this.getDefault("SUExpectsDSASignature")) { + this.setDefault("factory.licensefactory.class", ReceiptFactory.class.getName()); } } } diff --git a/core/dylib/src/main/java/ch/cyberduck/core/preferences/SMAppServiceApplicationLoginRegistry.java b/core/dylib/src/main/java/ch/cyberduck/core/preferences/SMAppServiceApplicationLoginRegistry.java index 7290520164..c08e85585c 100644 --- a/core/dylib/src/main/java/ch/cyberduck/core/preferences/SMAppServiceApplicationLoginRegistry.java +++ b/core/dylib/src/main/java/ch/cyberduck/core/preferences/SMAppServiceApplicationLoginRegistry.java @@ -26,11 +26,17 @@ public class SMAppServiceApplicationLoginRegistry implements ApplicationLoginReg @Override public boolean register(final Application application) { + if(application.equals(new Application(PreferencesFactory.get().getProperty("application.identifier")))) { + return SMAppService.mainAppService().registerAndReturnError(null); + } return SMAppService.loginItemServiceWithIdentifier(application.getIdentifier()).registerAndReturnError(null); } @Override public boolean unregister(final Application application) { + if(application.equals(new Application(PreferencesFactory.get().getProperty("application.identifier")))) { + return SMAppService.mainAppService().unregisterAndReturnError(null); + } return SMAppService.loginItemServiceWithIdentifier(application.getIdentifier()).unregisterAndReturnError(null); } } diff --git a/core/dylib/src/main/java/ch/cyberduck/core/sparkle/Sandbox.java b/core/dylib/src/main/java/ch/cyberduck/core/sparkle/Sandbox.java index 8e3bd019e4..e21f3937bb 100644 --- a/core/dylib/src/main/java/ch/cyberduck/core/sparkle/Sandbox.java +++ b/core/dylib/src/main/java/ch/cyberduck/core/sparkle/Sandbox.java @@ -20,18 +20,13 @@ package ch.cyberduck.core.sparkle; import ch.cyberduck.core.library.Native; -import org.apache.logging.log4j.LogManager; -import org.apache.logging.log4j.Logger; - public final class Sandbox { - private static final Logger log = LogManager.getLogger(Sandbox.class); static { Native.load("core"); } public static Sandbox get() { - log.debug("Obtain sandbox environment with requirement com.apple.security.app-sandbox"); return new Sandbox(); } diff --git a/core/dylib/src/test/java/ch/cyberduck/core/local/FinderLocalAttributesFinderTest.java b/core/dylib/src/test/java/ch/cyberduck/core/local/FinderLocalAttributesFinderTest.java index adf680ebaf..e7f73ebeb0 100644 --- a/core/dylib/src/test/java/ch/cyberduck/core/local/FinderLocalAttributesFinderTest.java +++ b/core/dylib/src/test/java/ch/cyberduck/core/local/FinderLocalAttributesFinderTest.java @@ -36,6 +36,16 @@ public class FinderLocalAttributesFinderTest { f.delete(); } + @Test + public void testGetSizeDirectory() throws Exception { + assertEquals(-1, new FinderLocalAttributes(new FinderLocal(UUID.randomUUID().toString())).getSize()); + final File f = new File(UUID.randomUUID().toString()); + f.mkdir(); + FinderLocalAttributes a = new FinderLocalAttributes(new FinderLocal(f.getAbsolutePath())); + assertEquals(-1, a.getSize()); + f.delete(); + } + @Test public void testGetPermission() { assertEquals(Permission.EMPTY, new FinderLocalAttributes(new FinderLocal(UUID.randomUUID().toString())).getPermission()); diff --git a/core/native/pom.xml b/core/native/pom.xml index 5cb612c9f4..83b08d2339 100644 --- a/core/native/pom.xml +++ b/core/native/pom.xml @@ -5,7 +5,7 @@ ch.cyberduck parent ../../pom.xml - 9.2.0-SNAPSHOT + 9.4.0-SNAPSHOT Cyberduck.Core.Native pom diff --git a/core/native/refresh/pom.xml b/core/native/refresh/pom.xml index 26a47b8d7d..df8878b280 100644 --- a/core/native/refresh/pom.xml +++ b/core/native/refresh/pom.xml @@ -5,7 +5,7 @@ ch.cyberduck Cyberduck.Core.Native ../pom.xml - 9.2.0-SNAPSHOT + 9.4.0-SNAPSHOT Cyberduck.Core.Refresh pom diff --git a/core/pom.xml b/core/pom.xml index 0e4e80d682..f5193cf994 100644 --- a/core/pom.xml +++ b/core/pom.xml @@ -18,7 +18,7 @@ ch.cyberduck parent - 9.2.0-SNAPSHOT + 9.4.0-SNAPSHOT core jar diff --git a/core/src/main/csharp/Cyberduck.Core.Native.csproj b/core/src/main/csharp/Cyberduck.Core.Native.csproj index 4faf4585f7..68c217be6f 100644 --- a/core/src/main/csharp/Cyberduck.Core.Native.csproj +++ b/core/src/main/csharp/Cyberduck.Core.Native.csproj @@ -17,7 +17,7 @@ net472;net8.0-windows10.0.22621.0 - 10.0.14393.0 + 10.0.14393.0 x64;arm64 true @@ -75,4 +75,4 @@ Ch.Cyberduck.Properties - \ No newline at end of file + diff --git a/core/src/main/csharp/NativeMethods.txt b/core/src/main/csharp/NativeMethods.txt index 4725549a25..cf16beab90 100644 --- a/core/src/main/csharp/NativeMethods.txt +++ b/core/src/main/csharp/NativeMethods.txt @@ -2,6 +2,7 @@ BHID_DataObject CLSID_QueryAssociations CoTaskMemFree +CreateFile CRED_FLAGS CRED_PERSIST CRED_TYPE @@ -11,8 +12,11 @@ CredRead CredWrite DefWindowProc ExtractIconEx +FILE_ID_INFO FOLDERID_Downloads GetCurrentPackageFullName +GetFileInformationByHandleEx +GetFinalPathNameByHandle GetTokenInformation GetWindowLong GetWindowLongPtr @@ -20,15 +24,22 @@ HWND_TOPMOST IApplicationAssociationRegistration IAssocHandler IAssocHandlerInvoker -IDataObject IExtractIconW +ILClone ILCreateFromPath +ILFindLastID +ILRemoveLastID IQueryAssociations IShellItem KNOWN_FOLDER_FLAG LoadLibrary LoadString MESSAGEBOX_RESULT +OpenFileById +PATHCCH_ALLOW_LONG_PATHS +PATHCCH_MAX_CCH +PathCchCanonicalizeEx +PathCchStripPrefix PathParseIconLocation PBST_ERROR PBST_NORMAL @@ -43,6 +54,7 @@ SetWindowPos SetWindowText SHAssocEnumHandlers SHCreateAssociationRegistration +SHCreateDataObject SHCreateItemFromIDList Shell_GetCachedImageIndex ShellExecuteEx @@ -69,4 +81,4 @@ WINDOW_EX_STYLE WINDOW_STYLE WM_APP WM_SETICON -WM_USER \ No newline at end of file +WM_USER diff --git a/core/src/main/csharp/System/IO/StringWriterPolyfills.cs b/core/src/main/csharp/System/IO/StringWriterPolyfills.net472.cs similarity index 82% rename from core/src/main/csharp/System/IO/StringWriterPolyfills.cs rename to core/src/main/csharp/System/IO/StringWriterPolyfills.net472.cs index 55f0dd2740..84b5691353 100644 --- a/core/src/main/csharp/System/IO/StringWriterPolyfills.cs +++ b/core/src/main/csharp/System/IO/StringWriterPolyfills.net472.cs @@ -1,6 +1,4 @@ -#if NETFRAMEWORK - -namespace System.IO; +namespace System.IO; public static class StringWriterPolyfills { @@ -12,5 +10,3 @@ public static class StringWriterPolyfills } } } - -#endif diff --git a/core/src/main/csharp/Windows/Win32/CorePInvoke.cs b/core/src/main/csharp/Windows/Win32/CorePInvoke.cs index 013a7531f4..1958761a04 100644 --- a/core/src/main/csharp/Windows/Win32/CorePInvoke.cs +++ b/core/src/main/csharp/Windows/Win32/CorePInvoke.cs @@ -1,8 +1,12 @@ using System; using System.Runtime.InteropServices; +using Microsoft.Win32.SafeHandles; using Windows.Win32.Foundation; +using Windows.Win32.Security; using Windows.Win32.Security.Credentials; using Windows.Win32.Storage.FileSystem; +using Windows.Win32.System.Com; +using Windows.Win32.System.SystemServices; using Windows.Win32.UI.Shell; using Windows.Win32.UI.Shell.Common; @@ -22,6 +26,48 @@ public unsafe partial class CorePInvoke } } + /// + public static unsafe SafeFileHandle CreateFile( + in ReadOnlySpan lpFileName, + uint dwDesiredAccess, + FILE_SHARE_MODE dwShareMode, + SECURITY_ATTRIBUTES? lpSecurityAttributes, + FILE_CREATION_DISPOSITION dwCreationDisposition, + FILE_FLAGS_AND_ATTRIBUTES dwFlagsAndAttributes, + SafeHandle hTemplateFile) + { + bool hTemplateFileAddRef = false; + try + { + fixed (char* lpFileNameLocal = lpFileName) + { + SECURITY_ATTRIBUTES lpSecurityAttributesLocal = lpSecurityAttributes ?? default(SECURITY_ATTRIBUTES); + HANDLE hTemplateFileLocal; + if (hTemplateFile is object) + { + hTemplateFile.DangerousAddRef(ref hTemplateFileAddRef); + hTemplateFileLocal = (HANDLE)hTemplateFile.DangerousGetHandle(); + } + else + hTemplateFileLocal = (HANDLE)new IntPtr(0L); + HANDLE __result = CorePInvoke.CreateFile( + lpFileName: lpFileNameLocal, + dwDesiredAccess: dwDesiredAccess, + dwShareMode: dwShareMode, + lpSecurityAttributes: lpSecurityAttributes.HasValue ? &lpSecurityAttributesLocal : null, + dwCreationDisposition: dwCreationDisposition, + dwFlagsAndAttributes: dwFlagsAndAttributes, + hTemplateFile: hTemplateFileLocal); + return new SafeFileHandle(__result, ownsHandle: true); + } + } + finally + { + if (hTemplateFileAddRef) + hTemplateFile.DangerousRelease(); + } + } + /// public static unsafe bool CredDelete(string TargetName, CRED_TYPE type, CRED_FLAGS flags) { @@ -43,6 +89,139 @@ public unsafe partial class CorePInvoke return new((nint)credential, true); } + /// + public static unsafe BOOL GetFileInformationByHandleEx(SafeHandle hFile, FILE_INFO_BY_HANDLE_CLASS FileInformationClass, out T value) where T : unmanaged + { + fixed (T* valueLocal = &value) + { + return GetFileInformationByHandleEx(hFile, FileInformationClass, valueLocal, (uint)Marshal.SizeOf()); + } + } + + /// + public static unsafe partial uint GetFinalPathNameByHandle(SafeHandle hFile, Span lpszFilePath, GETFINALPATHNAMEBYHANDLE_FLAGS dwFlags) + { + fixed (char* lpszFilePathLocal = lpszFilePath) + { + return GetFinalPathNameByHandle(hFile, lpszFilePathLocal, (uint)lpszFilePath.Length, dwFlags); + } + } + + /// + public static unsafe SafeITEMIDLISTHandle ILClone(in SafeITEMIDLISTHandle pidl, bool leaveOpen = false) + { + bool pidlAddRef = false; + try + { + ITEMIDLIST* pidlLocal; + if (pidl is not null) + { + pidl.DangerousAddRef(ref pidlAddRef); + pidlLocal = (ITEMIDLIST*)pidl.DangerousGetHandle(); + } + else + { + throw new ArgumentNullException(nameof(pidl)); + } + + return new((nint)ILClone(pidlLocal), true); + } + finally + { + if (pidlAddRef) + { + pidl.DangerousRelease(); + + if (!leaveOpen) + { + pidl.Dispose(); + } + } + } + } + + /// + public static unsafe SafeITEMIDLISTHandle ILCreateFromPathSafe(string pszPath) + { + fixed (char* pszPathLocal = pszPath) + { +#pragma warning disable RS0030 + ITEMIDLIST* __result = ILCreateFromPath(pszPathLocal); +#pragma warning restore RS0030 + return new((nint)__result, true); + } + } + + /// + public static unsafe SafeITEMIDLISTHandle ILFindLastID(in SafeITEMIDLISTHandle pidl) + { + bool pidlAddRef = false; + try + { + ITEMIDLIST* pidlLocal; + if (pidl is not null) + { + pidl.DangerousAddRef(ref pidlAddRef); + pidlLocal = (ITEMIDLIST*)pidl.DangerousGetHandle(); + } + else + { + throw new ArgumentNullException(nameof(pidl)); + } + + return new((nint)ILFindLastID(pidlLocal), false); + } + finally + { + if (pidlAddRef) + { + pidl.DangerousRelease(); + } + } + } + + /// + public static unsafe BOOL ILRemoveLastID(SafeITEMIDLISTHandle pidl) + { + bool pidlAddRef = false; + try + { + ITEMIDLIST* pidlLocal; + if (pidl is not null) + { + pidl.DangerousAddRef(ref pidlAddRef); + pidlLocal = (ITEMIDLIST*)pidl.DangerousGetHandle(); + } + else + { + throw new ArgumentNullException(nameof(pidl)); + } + + return ILRemoveLastID(pidlLocal); + } + finally + { + if (pidlAddRef) + { + pidl.DangerousRelease(); + } + } + } + + /// + public static unsafe HRESULT PathCchCanonicalizeEx(ref Span pszPathOut, string pszPathIn, PATHCCH_OPTIONS dwFlags) + { + fixed (char* ppszPathOut = pszPathOut) + { + PWSTR wstrpszPathOut = ppszPathOut; +#pragma warning disable RS0030 + HRESULT __result = CorePInvoke.PathCchCanonicalizeEx(wstrpszPathOut, (nuint)pszPathOut.Length, pszPathIn, dwFlags); +#pragma warning restore RS0030 + pszPathOut = pszPathOut.Slice(0, wstrpszPathOut.Length); + return __result; + } + } + /// public static unsafe HRESULT SHCreateAssociationRegistration(out T ppv) where T : class { @@ -60,13 +239,174 @@ public unsafe partial class CorePInvoke public static unsafe LRESULT SendMessage(HWND hWnd, uint Msg, WPARAM wParam, PCWSTR lParam) => SendMessage(hWnd, Msg, wParam, (nint)lParam.Value); - /// - public static unsafe SafeITEMIDLISTHandle ILCreateFromPathSafe(string pszPath) + /// + public static unsafe HRESULT SHParseDisplayName(string pszName, IBindCtx pbc, SFGAO_FLAGS sfgaoIn, out SafeITEMIDLISTHandle ppidl, out SFGAO_FLAGS sfgaoOut) { + fixed (SFGAO_FLAGS* sfgaoOutLocal = &sfgaoOut) + { + var __result = SHParseDisplayName(pszName, pbc, out var ppidlLocal, (uint)sfgaoIn, (uint*)sfgaoOutLocal); + ppidl = new((nint)ppidlLocal, true); + return __result; + } + } + + /// + public static unsafe HRESULT SHOpenFolderAndSelectItems(SafeITEMIDLISTHandle pidlFolder, SafeITEMIDLISTHandle apidl) + { + bool pidlFolderAddRef = false; + bool apidlAddRef = false; + try + { + ITEMIDLIST* pidlFolderLocal; + if (pidlFolder is not null) + { + pidlFolder.DangerousAddRef(ref pidlFolderAddRef); + pidlFolderLocal = (ITEMIDLIST*)pidlFolder.DangerousGetHandle(); + } + else + { + throw new ArgumentNullException(nameof(pidlFolder)); + } + + ITEMIDLIST* apidlLocal; + if (apidl is not null) + { + apidl.DangerousAddRef(ref apidlAddRef); + apidlLocal = (ITEMIDLIST*)apidl.DangerousGetHandle(); + } + else + { + throw new ArgumentNullException(nameof(apidl)); + } + + return SHOpenFolderAndSelectItems(pidlFolderLocal, 1, &apidlLocal, 0); + } + finally + { + if (apidlAddRef) + { + apidl.DangerousRelease(); + } + + if (pidlFolderAddRef) + { + pidlFolder.DangerousRelease(); + } + } + } + + /// + public static unsafe string SHGetKnownFolderPath(in Guid rfid, KNOWN_FOLDER_FLAG dwFlags, HANDLE hToken) + { + fixed (Guid* rfidLocal = &rfid) + { + PWSTR pszPath = default; + try + { + HRESULT __result = SHGetKnownFolderPath(rfidLocal, dwFlags, hToken, &pszPath); + __result.ThrowOnFailure(); + return pszPath.ToString(); + } + finally + { + CoTaskMemFree(pszPath); + } + } + } + + /// + public static unsafe nuint SHGetFileInfo(string pszPath, FILE_FLAGS_AND_ATTRIBUTES dwFileAttributes, in SHFILEINFOW sfi, SHGFI_FLAGS uFlags) + { + fixed (SHFILEINFOW* psfiLocal = &sfi) fixed (char* pszPathLocal = pszPath) { - ITEMIDLIST* __result = ILCreateFromPath(pszPathLocal); - return new((nint)__result, true); + return SHGetFileInfo(pszPathLocal, dwFileAttributes, psfiLocal, (uint)Marshal.SizeOf(), uFlags); + } + } + + /// + public static unsafe HRESULT SHCreateDataObject(SafeITEMIDLISTHandle pidlFolder, SafeITEMIDLISTHandle apidl, out IDataObject ppv) + { + bool pidlFolderAddRef = false; + bool apidlAddRef = false; + try + { + ITEMIDLIST* pidlFolderLocal; + if (pidlFolder is not null) + { + pidlFolder.DangerousAddRef(ref pidlFolderAddRef); + pidlFolderLocal = (ITEMIDLIST*)pidlFolder.DangerousGetHandle(); + } + else + { + throw new ArgumentNullException(nameof(pidlFolder)); + } + + ITEMIDLIST* apidlLocal; + if (apidl is not null) + { + apidl.DangerousAddRef(ref apidlAddRef); + apidlLocal = (ITEMIDLIST*)apidl.DangerousGetHandle(); + } + else + { + throw new ArgumentNullException(nameof(apidl)); + } + + return LocalExternFunction(pidlFolderLocal, 1, &apidlLocal, null, typeof(IDataObject).GUID, out ppv); + } + finally + { + if (apidlAddRef) + { + apidl.DangerousRelease(); + } + + if (pidlFolderAddRef) + { + pidlFolder.DangerousRelease(); + } + } + + [DllImport("SHELL32.dll", ExactSpelling = true, EntryPoint = "SHCreateDataObject")] + [DefaultDllImportSearchPaths(DllImportSearchPath.System32)] + static extern unsafe HRESULT LocalExternFunction( + [Optional] ITEMIDLIST* pidlFolder, + uint cidl, + [Optional] ITEMIDLIST** apidl, + IDataObject pdtInner, + in Guid riid, + [MarshalAs(UnmanagedType.Interface)] out IDataObject ppv); + } + + /// + public static unsafe HRESULT SHCreateItemFromIDList(in SafeITEMIDLISTHandle pidl, out T ppv) where T : class + { + bool hPidlAddRef = false; + try + { + ITEMIDLIST* pidlLocal; + if (pidl is not null) + { + pidl.DangerousAddRef(ref hPidlAddRef); + pidlLocal = (ITEMIDLIST*)pidl.DangerousGetHandle(); + } + else + { + pidlLocal = (ITEMIDLIST*)IntPtr.Zero; + } + + Guid riid = typeof(T).GUID; + HRESULT __result = SHCreateItemFromIDList(pidlLocal, &riid, out var ppvLocal); + ppv = (T)ppvLocal; + return __result; + } + finally + { + if (hPidlAddRef) + { + pidl.DangerousRelease(); + } } } @@ -101,64 +441,4 @@ public unsafe partial class CorePInvoke } } } - - /// - public static unsafe string SHGetKnownFolderPath(in Guid rfid, KNOWN_FOLDER_FLAG dwFlags, HANDLE hToken) - { - fixed (Guid* rfidLocal = &rfid) - { - PWSTR pszPath = default; - try - { - HRESULT __result = SHGetKnownFolderPath(rfidLocal, dwFlags, hToken, &pszPath); - __result.ThrowOnFailure(); - return pszPath.ToString(); - } - finally - { - CoTaskMemFree(pszPath); - } - } - } - - /// - public static unsafe HRESULT SHCreateItemFromIDList(in SafeITEMIDLISTHandle pidl, out T ppv) where T : class - { - bool hPidlAddRef = false; - try - { - ITEMIDLIST* pidlLocal; - if (pidl is not null) - { - pidl.DangerousAddRef(ref hPidlAddRef); - pidlLocal = (ITEMIDLIST*)pidl.DangerousGetHandle(); - } - else - { - pidlLocal = (ITEMIDLIST*)IntPtr.Zero; - } - - Guid riid = typeof(T).GUID; - HRESULT __result = SHCreateItemFromIDList(pidlLocal, &riid, out var ppvLocal); - ppv = (T)ppvLocal; - return __result; - } - finally - { - if (hPidlAddRef) - { - pidl.DangerousRelease(); - } - } - } - - /// - public static unsafe nuint SHGetFileInfo(string pszPath, FILE_FLAGS_AND_ATTRIBUTES dwFileAttributes, in SHFILEINFOW sfi, SHGFI_FLAGS uFlags) - { - fixed (SHFILEINFOW* psfiLocal = &sfi) - fixed (char* pszPathLocal = pszPath) - { - return SHGetFileInfo(pszPathLocal, dwFileAttributes, psfiLocal, (uint)Marshal.SizeOf(), uFlags); - } - } } diff --git a/core/src/main/csharp/Windows/Win32/CorePInvoke.net472.cs b/core/src/main/csharp/Windows/Win32/CorePInvoke.net472.cs new file mode 100644 index 0000000000..46125f08e5 --- /dev/null +++ b/core/src/main/csharp/Windows/Win32/CorePInvoke.net472.cs @@ -0,0 +1,10 @@ +using System; +using System.Runtime.InteropServices; +using Windows.Win32.Storage.FileSystem; + +namespace Windows.Win32; + +partial class CorePInvoke +{ + public static unsafe partial uint GetFinalPathNameByHandle(SafeHandle hFile, Span lpszFilePath, GETFINALPATHNAMEBYHANDLE_FLAGS dwFlags); +} diff --git a/core/src/main/csharp/Windows/Win32/CorePInvoke.net8.0.cs b/core/src/main/csharp/Windows/Win32/CorePInvoke.net8.0.cs new file mode 100644 index 0000000000..7a319f8869 --- /dev/null +++ b/core/src/main/csharp/Windows/Win32/CorePInvoke.net8.0.cs @@ -0,0 +1,12 @@ +using System; +using System.Runtime.InteropServices; +using System.Runtime.Versioning; +using Windows.Win32.Storage.FileSystem; + +namespace Windows.Win32; + +partial class CorePInvoke +{ + [SupportedOSPlatform("windows6.0.6000")] + public static unsafe partial uint GetFinalPathNameByHandle(SafeHandle hFile, Span lpszFilePath, GETFINALPATHNAMEBYHANDLE_FLAGS dwFlags); +} diff --git a/core/src/main/csharp/ch/cyberduck/core/CredentialManagerPasswordStore.cs b/core/src/main/csharp/ch/cyberduck/core/CredentialManagerPasswordStore.cs index ed08cface4..03bcb482aa 100644 --- a/core/src/main/csharp/ch/cyberduck/core/CredentialManagerPasswordStore.cs +++ b/core/src/main/csharp/ch/cyberduck/core/CredentialManagerPasswordStore.cs @@ -194,7 +194,10 @@ namespace Ch.Cyberduck.Core } if (credential.isTokenAuthentication()) { - winCred.Attributes["Token"] = credential.getToken(); + if (credential.getToken() != null) + { + winCred.Attributes["Token"] = credential.getToken(); + } } if (credential.isOAuthAuthentication()) { diff --git a/core/src/main/csharp/ch/cyberduck/core/EnvironmentInfo.cs b/core/src/main/csharp/ch/cyberduck/core/EnvironmentInfo.cs index 45f0d76df5..f74238d5df 100644 --- a/core/src/main/csharp/ch/cyberduck/core/EnvironmentInfo.cs +++ b/core/src/main/csharp/ch/cyberduck/core/EnvironmentInfo.cs @@ -69,7 +69,7 @@ namespace Ch.Cyberduck.Core } [MethodImpl(MethodImplOptions.NoInlining)] - static string PackagePath() => Package.Current.InstalledPath; + static string PackagePath() => Package.Current.InstalledLocation.Path; } public static void AssemblyInfo() diff --git a/core/src/main/csharp/ch/cyberduck/core/LocalExtensions.cs b/core/src/main/csharp/ch/cyberduck/core/LocalExtensions.cs new file mode 100644 index 0000000000..c866e255ab --- /dev/null +++ b/core/src/main/csharp/ch/cyberduck/core/LocalExtensions.cs @@ -0,0 +1,14 @@ +using System.ComponentModel; +using Ch.Cyberduck.Core.Local; +using CoreLocal = ch.cyberduck.core.Local; + +namespace Ch.Cyberduck.Core; + +public static class LocalExtensions +{ + /// + public static string NativePath(this CoreLocal local) => PlatformLocalSupport.ToNativePath(local); + + /// + public static string PlatformPath(this CoreLocal local) => PlatformLocalSupport.ToPlatformPath(local); +} diff --git a/core/src/main/csharp/ch/cyberduck/core/aquaticprime/WindowsStoreLicense.cs b/core/src/main/csharp/ch/cyberduck/core/aquaticprime/WindowsStoreLicense.cs index e4c558af15..39015f1557 100644 --- a/core/src/main/csharp/ch/cyberduck/core/aquaticprime/WindowsStoreLicense.cs +++ b/core/src/main/csharp/ch/cyberduck/core/aquaticprime/WindowsStoreLicense.cs @@ -74,7 +74,7 @@ namespace Ch.Cyberduck.Core.AquaticPrime public string getValue(string str) { - return LocaleFactory.localizedString("Unknown"); + return null; } public bool isReceipt() diff --git a/core/src/main/csharp/ch/cyberduck/core/editor/SystemWatchEditor.cs b/core/src/main/csharp/ch/cyberduck/core/editor/SystemWatchEditor.cs index b57b2d4b40..efadda823f 100644 --- a/core/src/main/csharp/ch/cyberduck/core/editor/SystemWatchEditor.cs +++ b/core/src/main/csharp/ch/cyberduck/core/editor/SystemWatchEditor.cs @@ -40,7 +40,7 @@ namespace Ch.Cyberduck.Core.Editor protected override void watch(Application application, ch.cyberduck.core.Local temporary, FileWatcherListener listener, ApplicationQuitCallback quit) { - _watcher.Path = temporary.getParent().getAbsolute(); + _watcher.Path = temporary.getParent().PlatformPath(); _watcher.Filter = temporary.getName(); _watcher.NotifyFilter = NotifyFilters.LastAccess | NotifyFilters.LastWrite | NotifyFilters.FileName | NotifyFilters.DirectoryName; diff --git a/core/src/main/csharp/ch/cyberduck/core/local/ExplorerRevealService.cs b/core/src/main/csharp/ch/cyberduck/core/local/ExplorerRevealService.cs index bd0d126ae2..b8e14982ef 100644 --- a/core/src/main/csharp/ch/cyberduck/core/local/ExplorerRevealService.cs +++ b/core/src/main/csharp/ch/cyberduck/core/local/ExplorerRevealService.cs @@ -17,41 +17,59 @@ // using ch.cyberduck.core.local; -using System; +using org.apache.logging.log4j; using Windows.Win32; using static Windows.Win32.CorePInvoke; +using CoreLocal = ch.cyberduck.core.Local; -namespace Ch.Cyberduck.Core.Local +namespace Ch.Cyberduck.Core.Local; + +public sealed class ExplorerRevealService : RevealService { - public sealed class ExplorerRevealService : RevealService + private static readonly Logger Log = LogManager.getLogger(typeof(ExplorerRevealService)); + + public unsafe bool reveal(CoreLocal l, bool select) { - public unsafe bool reveal(ch.cyberduck.core.Local l, bool select) + if (!select) { - if (!select) - { - return ApplicationLauncherFactory.get().open(l); - } - - using var nativeFolder = ILCreateFromPathSafe(l.getParent().getAbsolute()); - if (nativeFolder.IsInvalid) - { - return false; - } - - using var nativeFile = ILCreateFromPathSafe(l.getAbsolute()); - if (nativeFile.IsInvalid) - { - return false; - } - - ReadOnlySpan target = select ? [nativeFile.Value] : []; - SHOpenFolderAndSelectItems(nativeFolder, target, 0); - return true; + return ApplicationLauncherFactory.get().open(l); } - public bool reveal(ch.cyberduck.core.Local file) + SafeITEMIDLISTHandle selectItem = null; + SafeITEMIDLISTHandle parent = null; + try { - return reveal(file, true); + if (Shell.ItemIdListFromLocal(l, out selectItem) is { } error) + { + if (Log.isDebugEnabled()) + { + Log.debug($"Create IdList {l}", error); + } + + return false; + } + + if (!Shell.GetParent(ref selectItem, out parent)) + { + if (Log.isDebugEnabled()) + { + Log.debug($"Could not get parent IDL of {l}"); + } + + return false; + } + + return SHOpenFolderAndSelectItems(parent, selectItem).Succeeded; + } + finally + { + parent?.Dispose(); + selectItem?.Dispose(); } } + + public bool reveal(CoreLocal file) + { + return reveal(file, true); + } } diff --git a/core/src/main/csharp/ch/cyberduck/core/local/NTFSFilesystemBookmarkResolver.cs b/core/src/main/csharp/ch/cyberduck/core/local/NTFSFilesystemBookmarkResolver.cs new file mode 100644 index 0000000000..fd696746b8 --- /dev/null +++ b/core/src/main/csharp/ch/cyberduck/core/local/NTFSFilesystemBookmarkResolver.cs @@ -0,0 +1,182 @@ +// Copyright (c) 2010-2025 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. + +using System; +using System.Globalization; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; +using ch.cyberduck.core.exception; +using ch.cyberduck.core.local; +using Microsoft.Win32.SafeHandles; +using org.apache.logging.log4j; +using Windows.Win32; +using Windows.Win32.Storage.FileSystem; +using CoreLocal = ch.cyberduck.core.Local; + +namespace Ch.Cyberduck.Core.Local +{ + public class NTFSFilesystemBookmarkResolver(CoreLocal local) : FilesystemBookmarkResolver + { + private static readonly Logger Log = LogManager.getLogger(typeof(NTFSFilesystemBookmarkResolver).FullName); + + public string create(CoreLocal file) => FilesystemBookmarkResolver.__DefaultMethods.create(this, file); + + public string create(CoreLocal file, bool prompt) + { + FILE_ID_INFO info; + using (var handle = CorePInvoke.CreateFile( + lpFileName: file.NativePath(), + dwDesiredAccess: 0, + dwShareMode: (FILE_SHARE_MODE)7, + lpSecurityAttributes: null, + dwCreationDisposition: FILE_CREATION_DISPOSITION.OPEN_EXISTING, + dwFlagsAndAttributes: FILE_FLAGS_AND_ATTRIBUTES.FILE_FLAG_BACKUP_SEMANTICS, + hTemplateFile: null)) + { + if (handle.IsInvalid) + { + goto error; + } + + if (!CorePInvoke.GetFileInformationByHandleEx(handle, FILE_INFO_BY_HANDLE_CLASS.FileIdInfo, out info)) + { + goto error; + } + } + + return Unsafe.As(ref info.FileId).ToString("X16"); + + error: + return null; + } + + public object resolve(string bookmark) + { + if (!ToFileId(bookmark, out var fileId)) + { + throw new LocalAccessDeniedException(bookmark); + } + + SafeFileHandle fileHandle = null; + try + { + SafeFileHandle rootHandle = null; + try + { + if (!TryFindRoot(local, out rootHandle)) + { + throw new LocalAccessDeniedException($"Cannot find root for \"{local}\""); + } + + FILE_ID_DESCRIPTOR fileDescriptor = new() + { + dwSize = (uint)Marshal.SizeOf(), + Type = FILE_ID_TYPE.FileIdType, + Anonymous = + { + FileId = fileId + } + }; + + fileHandle = CorePInvoke.OpenFileById( + hVolumeHint: rootHandle, + lpFileId: fileDescriptor, + dwDesiredAccess: 0, + dwShareMode: (FILE_SHARE_MODE)7, + lpSecurityAttributes: null, + dwFlagsAndAttributes: FILE_FLAGS_AND_ATTRIBUTES.FILE_FLAG_BACKUP_SEMANTICS); + if (fileHandle.IsInvalid) + { + var errorCode = Marshal.GetHRForLastWin32Error(); + Log.warn( + $"Opening file {local.getAbsolute()} with id {bookmark} ({errorCode:X8})"); + throw new LocalAccessDeniedException(bookmark); + } + } + finally + { + rootHandle?.Dispose(); + } + + // Allocate enough space to store 32768-wchars. + Span finalNameBuffer = new char[32 * 1024 + 1]; + var length = CorePInvoke.GetFinalPathNameByHandle( + hFile: fileHandle, + lpszFilePath: finalNameBuffer, + dwFlags: GETFINALPATHNAMEBYHANDLE_FLAGS.VOLUME_NAME_DOS | + GETFINALPATHNAMEBYHANDLE_FLAGS.FILE_NAME_NORMALIZED); + if (length == 0) + { + var errorCode = Marshal.GetHRForLastWin32Error(); + Log.warn( + $"Get final path name for {fileId} originally {local.getAbsolute()} ({errorCode:X8})"); + throw new LocalAccessDeniedException(bookmark); + } + + return new SystemLocal(finalNameBuffer.Slice(0, (int)length).ToString()).setBookmark(bookmark); + } + finally + { + fileHandle?.Dispose(); + } + } + + public static bool ToFileId(string bookmark, out long fileId) + { + long fileIdResult = 0; + try + { + return bookmark?.Length == 16 && + long.TryParse(bookmark, NumberStyles.HexNumber, null, out fileIdResult); + } + finally + { + fileId = fileIdResult; + } + } + + private static bool TryFindRoot(CoreLocal local, out SafeFileHandle handle) + { + while (!local.isRoot()) + { + local = local.getParent(); + SafeFileHandle result = null; + try + { + result = CorePInvoke.CreateFile( + lpFileName: local.NativePath(), + dwDesiredAccess: 0, + dwShareMode: (FILE_SHARE_MODE)7, + lpSecurityAttributes: null, dwCreationDisposition: FILE_CREATION_DISPOSITION.OPEN_EXISTING, + dwFlagsAndAttributes: FILE_FLAGS_AND_ATTRIBUTES.FILE_FLAG_BACKUP_SEMANTICS, + hTemplateFile: null); + if (result.IsInvalid) + { + continue; + } + + handle = result; + result = null; + return true; + } + finally + { + result?.Dispose(); + } + } + + handle = null; + return false; + } + } +} diff --git a/core/src/main/csharp/ch/cyberduck/core/local/PlatformLocalSupport.cs b/core/src/main/csharp/ch/cyberduck/core/local/PlatformLocalSupport.cs new file mode 100644 index 0000000000..be742aa626 --- /dev/null +++ b/core/src/main/csharp/ch/cyberduck/core/local/PlatformLocalSupport.cs @@ -0,0 +1,103 @@ +using System; +using System.ComponentModel; +using System.IO; +using System.Runtime.CompilerServices; +using CoreLocal = ch.cyberduck.core.Local; + +namespace Ch.Cyberduck.Core.Local; + +// This class must never appear in any stack traces. +// IKVM doesn't support `ReadOnlySpan<>` in method signatures. + +internal static class PlatformLocalSupport +{ + private const string UncPathPrefix = @"\\?\"; + + ///

+ /// Returns a path that is usable for .NET IO. + /// + /// A path, optionally prefixed with \\?\ when needed. + [Browsable(false), EditorBrowsable(EditorBrowsableState.Never)] + public static string ToPlatformPath(CoreLocal local) + { +#if NETCOREAPP + // .NET Core automatically handles long paths. + return local.getAbsolute(); +#else + // .NET Framework doesn't automatically prefix long paths. + // Assume same semantics as Win32 paths. + return ToNativePath(local); +#endif + } + + /// + /// Returns a path usable for Win32 IO, e.g. CreateFile. + /// + /// A path, optionally prefixed with \\?\ when needed. + [Browsable(false), EditorBrowsable(EditorBrowsableState.Never)] + public static string ToNativePath(CoreLocal local) + { + // TODO There is RtlAreLongPathsEnabled, but this is undocumented. + // Manifest | LongPathsEnable | RtlAreLongPathsEnabled + // True | False | False + // False | False | False + // False | True | False + // True | True | True + // Just prefix every long path. + + var path = local.getAbsolute(); + bool unc = path.Length > 2 + && IsDirectorySeparator(path[0]) + && IsDirectorySeparator(path[1]); + bool extended = unc && path.Length > 3 + && path[2] is '?' or '.'; + + if (!extended && path.Length > 248 /*MaxShortDirectoryName*/) + { + if (unc) + { + // When storing to a UNC drive, make \\?\UNC\X from \\X + return path.Insert(2, @"?\UNC\"); + } + else + { + // Create \\?\X from X + return UncPathPrefix + path; + } + } + + return path; + } + + internal static bool IsDirectorySeparator(char sep) => + sep == Path.DirectorySeparatorChar || sep == Path.AltDirectorySeparatorChar; + + [MethodImpl(MethodImplOptions.AggressiveInlining)] +#if NETCOREAPP + internal static ReadOnlySpan PathRoot(scoped in ReadOnlySpan path) + { + return Path.GetPathRoot(path); + } +#else + internal static ReadOnlySpan PathRoot(string path) + { + return Path.GetPathRoot(path).AsSpan(); + } +#endif + + internal static void WriteNormalized(in ReadOnlySpan path, StringWriter writer) + { + writer.GetStringBuilder().EnsureCapacity(path.Length); + foreach (ref readonly var c in path) + { + if (c == Path.AltDirectorySeparatorChar) + { + writer.Write(Path.DirectorySeparatorChar); + } + else + { + writer.Write(c); + } + } + } +} diff --git a/core/src/main/csharp/ch/cyberduck/core/local/RecycleLocalTrashFeature.cs b/core/src/main/csharp/ch/cyberduck/core/local/RecycleLocalTrashFeature.cs index 0ab3aae461..e4faa42d4d 100644 --- a/core/src/main/csharp/ch/cyberduck/core/local/RecycleLocalTrashFeature.cs +++ b/core/src/main/csharp/ch/cyberduck/core/local/RecycleLocalTrashFeature.cs @@ -31,11 +31,11 @@ namespace Ch.Cyberduck.Core.Local try { if (file.isFile()) { - FileSystem.DeleteFile(file.getAbsolute(), UIOption.OnlyErrorDialogs, RecycleOption.SendToRecycleBin); + FileSystem.DeleteFile(file.PlatformPath(), UIOption.OnlyErrorDialogs, RecycleOption.SendToRecycleBin); } else if (file.isDirectory()) { - FileSystem.DeleteDirectory(file.getAbsolute(), UIOption.OnlyErrorDialogs, RecycleOption.SendToRecycleBin); + FileSystem.DeleteDirectory(file.PlatformPath(), UIOption.OnlyErrorDialogs, RecycleOption.SendToRecycleBin); } } catch(System.Exception e) { diff --git a/core/src/main/csharp/ch/cyberduck/core/local/Shell.Shlobj.cs b/core/src/main/csharp/ch/cyberduck/core/local/Shell.Shlobj.cs new file mode 100644 index 0000000000..dc13a640cc --- /dev/null +++ b/core/src/main/csharp/ch/cyberduck/core/local/Shell.Shlobj.cs @@ -0,0 +1,43 @@ +using System; +using System.Runtime.InteropServices; +using Windows.Win32; +using Windows.Win32.System.SystemServices; +using CoreLocal = ch.cyberduck.core.Local; + +namespace Ch.Cyberduck.Core.Local; + +public static partial class Shell +{ + /// + /// Tries to extract the last item id in a PIDLIST_ABSOLUTE into , and copies the remainder into . + /// + /// A IDLIST_ABSOLUTE, which receives the last ID on return. + /// Receives the original IDLIST, with the last item zeroed. + /// True on success, and false on any failure. + /// Callers are responsible to cleanup resources after returning. + public static bool GetParent(ref SafeITEMIDLISTHandle child, out SafeITEMIDLISTHandle parent) + { + parent = child; + if ((child = CorePInvoke.ILFindLastID(parent)).IsInvalid) + { + return false; + } + + if ((child = CorePInvoke.ILClone(child)).IsInvalid) + { + return false; + } + + return CorePInvoke.ILRemoveLastID(parent); + } + + public static Exception ItemIdListFromDisplayName(string displayName, out SafeITEMIDLISTHandle handle) + { + return Marshal.GetExceptionForHR(CorePInvoke.SHParseDisplayName(displayName, null, SFGAO_FLAGS.SFGAO_FILESYSTEM, out handle, out _).Value); + } + + public static Exception ItemIdListFromLocal(CoreLocal local, out SafeITEMIDLISTHandle handle) + { + return ItemIdListFromDisplayName(local.getAbsolute(), out handle); + } +} diff --git a/core/src/main/csharp/ch/cyberduck/core/local/ShellApplicationFinder.cs b/core/src/main/csharp/ch/cyberduck/core/local/ShellApplicationFinder.cs index f95fa0ca23..a2b4816cfb 100644 --- a/core/src/main/csharp/ch/cyberduck/core/local/ShellApplicationFinder.cs +++ b/core/src/main/csharp/ch/cyberduck/core/local/ShellApplicationFinder.cs @@ -16,23 +16,22 @@ // feedback@cyberduck.io // -using ch.cyberduck.core.cache; -using ch.cyberduck.core.local; -using Ch.Cyberduck.Core.I18n; -using java.util; -using org.apache.commons.io; -using org.apache.logging.log4j; using System; using System.Collections.Generic; using System.Diagnostics; using System.IO; using System.Runtime.InteropServices; using System.Threading; +using ch.cyberduck.core.cache; +using ch.cyberduck.core.local; +using Ch.Cyberduck.Core.I18n; +using java.util; +using org.apache.commons.io; +using org.apache.logging.log4j; using Windows.Win32; using Windows.Win32.Foundation; using Windows.Win32.System.Com; using Windows.Win32.UI.Shell; -using Windows.Win32.UI.Shell.Common; using static Windows.Win32.CorePInvoke; using static Windows.Win32.UI.Shell.ASSOC_FILTER; using static Windows.Win32.UI.Shell.ASSOCIATIONLEVEL; @@ -249,7 +248,7 @@ namespace Ch.Cyberduck.Core.Local lpClass = PCWSTR.DangerousFromString(getIdentifier()), fMask = SEE_MASK_CLASSNAME | SEE_MASK_NOASYNC, lpVerb = PCWSTR.DangerousFromString("open"), - lpFile = PCWSTR.DangerousFromString(local.getAbsolute()) + lpFile = PCWSTR.DangerousFromString(local.NativePath()) }; ShellExecuteEx(ref info); } @@ -257,6 +256,8 @@ namespace Ch.Cyberduck.Core.Local public class ShellApplication : Application, IInvokeApplication, WindowsApplicationLauncher.IInvokeApplication { + private static readonly Logger Log = LogManager.getLogger(typeof(ShellApplication)); + private readonly IAssocHandler handler; private readonly int iconIndex; private readonly string iconPath; @@ -280,19 +281,69 @@ namespace Ch.Cyberduck.Core.Local { if (SynchronizationContext.Current == null) { - sync.Send(d => Launch(local), null); + sync.Send(d => Launch((ch.cyberduck.core.Local)d), local); return; } - using var pidl = ILCreateFromPathSafe(local.getAbsolute()); - if (pidl.IsInvalid) + IDataObject pdo = null; + try { - return; - } + SafeITEMIDLISTHandle item = null; + SafeITEMIDLISTHandle folder = null; + try + { + if (Shell.ItemIdListFromLocal(local, out item) is { } shellError) + { + if (Log.isDebugEnabled()) + { + Log.debug($"{this}: Creating ItemIdList from {local}", shellError); + } - SHCreateItemFromIDList(pidl, out IShellItem ppv); - ppv.BindToHandler(null, BHID_DataObject, out IDataObject pdo); - handler.Invoke(pdo); + return; + } + + if (!Shell.GetParent(ref item, out folder)) + { + if (Log.isDebugEnabled()) + { + Log.debug($"{this}: Did not find IDL parent for {local}"); + } + + return; + } + + if (SHCreateDataObject(folder, item, out pdo) is { Failed: true, Value: { } dataObjectError }) + { + if (Log.isDebugEnabled()) + { + Log.debug($"{this}: Create IDataObject for Handler", Marshal.GetExceptionForHR(dataObjectError)); + } + + return; + } + } + finally + { + folder?.Dispose(); + item?.Dispose(); + } + + try + { + handler.Invoke(pdo); + } + catch (Exception e) + { + Log.warn($"{this}: Failed invoking handler for {local}", e); + } + } + finally + { + if (pdo != null) + { + Marshal.FinalReleaseComObject(pdo); + } + } } } @@ -331,7 +382,7 @@ namespace Ch.Cyberduck.Core.Local OPENASINFO info = new() { oaifInFlags = OAIF_EXEC, - pcszFile = PCWSTR.DangerousFromString(local.getAbsolute()) + pcszFile = PCWSTR.DangerousFromString(local.NativePath()) }; SHOpenWithDialog(default, info); } diff --git a/core/src/main/csharp/ch/cyberduck/core/local/SystemLocal.cs b/core/src/main/csharp/ch/cyberduck/core/local/SystemLocal.cs index 7c37f37041..92de2c0368 100644 --- a/core/src/main/csharp/ch/cyberduck/core/local/SystemLocal.cs +++ b/core/src/main/csharp/ch/cyberduck/core/local/SystemLocal.cs @@ -1,38 +1,46 @@ -// -// Copyright (c) 2010-2018 Yves Langisch. All rights reserved. -// http://cyberduck.io/ -// +// Copyright (c) 2010-2025 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. -// -// Bug fixes, suggestions and comments should be sent to: -// feedback@cyberduck.io -// -using ch.cyberduck.core; -using org.apache.logging.log4j; using System; using System.IO; +using ch.cyberduck.core; +using ch.cyberduck.core.exception; +using java.io; +using org.apache.logging.log4j; +using static Ch.Cyberduck.Core.Local.PlatformLocalSupport; using CoreLocal = ch.cyberduck.core.Local; +using File = System.IO.File; using Path = System.IO.Path; +using StringWriter = System.IO.StringWriter; namespace Ch.Cyberduck.Core.Local { public class SystemLocal : CoreLocal { - private static readonly char[] INVALID_CHARS = Path.GetInvalidFileNameChars(); private static readonly Logger Log = LogManager.getLogger(typeof(SystemLocal).FullName); - private static readonly char[] PATH_SEPARATORS = new[] { Path.AltDirectorySeparatorChar, Path.DirectorySeparatorChar }; + + private static readonly char[] INVALID_CHARS = Path.GetInvalidFileNameChars(); + private static readonly char[] PATH_SEPARATORS = new[] + { Path.AltDirectorySeparatorChar, Path.DirectorySeparatorChar }; public SystemLocal(string parent, string name) - : this(Join(parent, Sanitize(name, true))) + : this( +#if NETCOREAPP + Path.Join(parent, name) +#else + Join(parent, name) +#endif + ) { } @@ -42,7 +50,7 @@ namespace Ch.Cyberduck.Core.Local } public SystemLocal(string path) - : base(Sanitize(path)) + : base(Canonicalize(path)) { } @@ -51,19 +59,63 @@ namespace Ch.Cyberduck.Core.Local { } + public CoreLocal Resolve() + { + if (null == bookmark) + { + return this; + } + + try + { + return (CoreLocal)new NTFSFilesystemBookmarkResolver(this).resolve(bookmark); + } + catch (LocalAccessDeniedException e) + { + Log.warn($"Failure resolving bookmark for {this}", e); + return this; + } + } + + public override AttributedList list(Filter filter) + { + return base.list(Resolve().getAbsolute(), filter); + } + + public override OutputStream getOutputStream(bool append) + { + return base.getOutputStream(Resolve().getAbsolute(), append); + } + + public override InputStream getInputStream() + { + return base.getInputStream(Resolve().getAbsolute()); + } + public override bool exists() { - string path = getAbsolute(); + var resolved = Resolve(); + string path = resolved.PlatformPath(); +#if NETCOREAPP + return Path.Exists(path); +#else if (File.Exists(path)) { return true; } - bool directory = Directory.Exists(path); - if (directory) + + if (Directory.Exists(path)) { return true; } + return false; +#endif + } + + public override LocalAttributes attributes() + { + return new SystemLocalAttributes(this); } public override String getAbbreviatedPath() @@ -85,120 +137,122 @@ namespace Ch.Cyberduck.Core.Local return false; } - private static string Join(string root, string path) - { - // Path.Join doesn't exist in .NET Framework, need to replicate - bool hasDirectorySeparator = IsDirectorySeparator(root[root.Length - 1]) || IsDirectorySeparator(path[0]); - return hasDirectorySeparator - ? string.Concat(root, path) - : string.Concat(root, Path.DirectorySeparatorChar, path); - - static bool IsDirectorySeparator(char sep) => sep == Path.DirectorySeparatorChar || sep == Path.AltDirectorySeparatorChar; - } - - private static string Sanitize(string name, bool makeUnc = false) + private static string Canonicalize(string name) { if (string.IsNullOrWhiteSpace(name)) { return ""; } - using StringWriter writer = new(); - var namespan = name.AsSpan(); - int? leadingSeparators = 0; - bool hasUnc = false, nextDriveLetter = true; - for (int lastSegment = 0, index = 0; index != -1; lastSegment = index + 1) + int start = 0; + if (name[0] is '/') { - index = name.IndexOfAny(PATH_SEPARATORS, lastSegment); - ReadOnlySpan segment = (index switch - { - -1 => namespan.Slice(lastSegment), - _ => namespan.Slice(lastSegment, index - lastSegment) - }).Trim(); + // LocalFactory.get(Path.getAbsolute()) always retains '/' at the beginning. + // Need a Path-Local translation, which removes this. + // Adjust offset. + start = 1; + } - if (segment.IsEmpty && leadingSeparators is int lead) + var pathRoot = +#if NETCOREAPP + PathRoot(name.AsSpan(start)); +#else + PathRoot(name.Substring(start)); +#endif + + bool deviceSyntax = pathRoot.Length > 3 + && pathRoot[2] is '?' or '.' + && IsDirectorySeparator(pathRoot[0]) + && IsDirectorySeparator(pathRoot[1]) + && IsDirectorySeparator(pathRoot[3]); + bool deviceUnc = deviceSyntax && pathRoot.Length > 7 + && pathRoot[4] is 'U' + && pathRoot[5] is 'N' + && pathRoot[6] is 'C' + && IsDirectorySeparator(pathRoot[7]); + + using StringWriter writer = new(); + var buffer = writer.GetStringBuilder(); + + if (deviceUnc) + { + // Is \\?\UNC\X or \\.\UNC\X, write \\X + buffer.EnsureCapacity(pathRoot.Length - 5); + writer.Write(Path.DirectorySeparatorChar); + // Include separator after UNC. + WriteNormalized(pathRoot.Slice(7), writer); + } + else if (deviceSyntax && pathRoot[2] is '?') + { + // Only if we've got a real long-path (\\?\) + // do we remove it. + WriteNormalized(pathRoot.Slice(4), writer); + } + else + { + // Keep \\.\-prefixes (for e.g. Pipes), + // and don't bother working out how `\\SHARE`-paths work. + WriteNormalized(pathRoot, writer); + } + + start += pathRoot.Length; + + // TODO: Replace following when we have ch.cyberduck.core.Path to ch.cyberduck.core.Local translation + // e.g. on Windows when converting a Path to Local the leading slash has to be stripped, this + // translation would also be responsible for cleaning up bad file and path names (blocked chars). + var path = name.AsSpan(start); + var skipped = pathRoot.Length > 0 && IsDirectorySeparator(pathRoot[pathRoot.Length - 1]) ? 1 : 0; + for (int lastSegment = 0, index = 0; index != -1; lastSegment += index + 1) + { + var segment = path.Slice(lastSegment); + if ((index = segment.IndexOfAny(PATH_SEPARATORS)) != -1) { - // handles up to first two leading separators, that is "\" and "\\" - leadingSeparators = ++lead; - if (lead == 2) - { - // in the case this is "\\" continue with assuming UNC - // thus a drive letter _must not_ follow - hasUnc = true; - nextDriveLetter = false; - leadingSeparators = null; - writer.Write(Path.DirectorySeparatorChar); - writer.Write(Path.DirectorySeparatorChar); - } - // hereafter (after "\\" has been read) every empty segment is skipped "\\\" -> "\" - // or after anything except "\\" has been read, every empty segment is skipped, "\\" -> "\" + segment = segment.Slice(0, index); } - else if (!segment.IsEmpty) + + if (skipped++ == 0) { - var firstChanceDriveLetter = nextDriveLetter; - nextDriveLetter = false; - if (hasUnc) - { - // ignore UNC, whatever is in as first value segment is passed-through as is - // there is no need to validate hostnames here, would bail out somewhere else - // handles all cases of "\\*\" - // including, but not limited to: wsl$, wsl.localhost, \\?\ (MAX_PATH bypass), any network share - writer.Write(segment); + writer.Write(Path.DirectorySeparatorChar); + } - nextDriveLetter = segment.Length == 1 && (segment[0] == '?' || segment[0] == '.'); - } - else if (firstChanceDriveLetter && segment.Length == 2 && segment[1] == Path.VolumeSeparatorChar) + if (!segment.IsEmpty) + { + // pass through segment sanitized from path invalid characters + foreach (ref readonly var c in segment) { - // _only_ if there is a two-letter segment, that is ending in ':' (VolumeSeparatorChar) - // is this thing here run. - // If there is _anything_ wrong (that is not "[A-Z]:") return empty value - - /// - var letter = (segment[0] | 0x20) - 'a'; - if (letter < 0 || letter > 25) + writer.Write(Array.IndexOf(INVALID_CHARS, c) switch { - // letter is not in range A to Z. - return ""; - } - // check above is simplified only, this passes raw input through - // check is 'a' but segment is 'A:', then 'A:' is written to output - writer.Write(segment[0]); - writer.Write(makeUnc ? '$' : ':'); - // additionally, this strips away all leading separator characters before the drive letter - // "/C:" becomes "C:". + -1 => c, + _ => '_' + }); } - else - { - if (leadingSeparators > 0) - { - // workaround. - // there may be input that is leading with one separator, but contains no - writer.Write(Path.DirectorySeparatorChar); - } - // pass through segment sanitized from path invalid characters - foreach (ref readonly var c in segment) - { - writer.Write(Array.IndexOf(INVALID_CHARS, c) switch - { - -1 => c, - _ => '_' - }); - } - } - hasUnc = false; - leadingSeparators = null; - - if (index != -1) - { - // allow for input of "C:\Abc" and "C:\Abc\", preserve trailing separators, where - // (1) return "C:\Abc" - // (2) return "C:\Abc\" - writer.Write(Path.DirectorySeparatorChar); - } + skipped = 0; } } + return writer.ToString(); } + +#if NETFRAMEWORK + private static string Join(string root, string path) + { + if (string.IsNullOrEmpty(root)) + { + return path; + } + + if (string.IsNullOrEmpty(path)) + { + return root; + } + + // Path.Join doesn't exist in .NET Framework, need to replicate + bool hasDirectorySeparator = IsDirectorySeparator(root[root.Length - 1]) || IsDirectorySeparator(path[0]); + return hasDirectorySeparator + ? string.Concat(root, path) + : string.Concat(root, Path.DirectorySeparatorChar, path); + } +#endif } } diff --git a/core/src/main/csharp/ch/cyberduck/core/local/SystemLocalAttributes.cs b/core/src/main/csharp/ch/cyberduck/core/local/SystemLocalAttributes.cs new file mode 100644 index 0000000000..7bcb385d3f --- /dev/null +++ b/core/src/main/csharp/ch/cyberduck/core/local/SystemLocalAttributes.cs @@ -0,0 +1,97 @@ +// Copyright (c) 2010-2025 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. + +using System; +using System.IO; +using ch.cyberduck.core; +using java.nio.file; +using java.nio.file.attribute; +using org.apache.logging.log4j; + +namespace Ch.Cyberduck.Core.Local +{ + public class SystemLocalAttributes : LocalAttributes + { + private static readonly Logger Log = LogManager.getLogger(typeof(SystemLocalAttributes).FullName); + + private readonly SystemLocal local; + + public SystemLocalAttributes(SystemLocal local) : base(local.getAbsolute()) + { + this.local = local; + } + + public override long getSize() + { + var resolved = local.Resolve(); + try + { + if (local.isDirectory()) { + return -1L; + } + return new FileInfo(resolved.PlatformPath()).Length; + } + catch (Exception e) + { + Log.warn($"Failure getting size of {resolved}. {e.Message}"); + return -1L; + } + } + + public override long getModificationDate() + { + var resolved = local.Resolve(); + try + { + return Files.getLastModifiedTime(Paths.get(resolved.getAbsolute())).toMillis(); + } + catch (Exception e) + { + Log.warn($"Failure getting timestamp of {resolved}. {e.Message}"); + return -1L; + } + } + + public override long getCreationDate() + { + var resolved = local.Resolve(); + try + { + return Files + .readAttributes(Paths.get(resolved.getAbsolute()), typeof(BasicFileAttributes)).creationTime() + .toMillis(); + } + catch (Exception e) + { + Log.warn($"Failure getting timestamp of {resolved}. {e.Message}"); + return -1L; + } + } + + public override long getAccessedDate() + { + var resolved = local.Resolve(); + try + { + return Files + .readAttributes(Paths.get(resolved.getAbsolute()), typeof(BasicFileAttributes)).lastAccessTime() + .toMillis(); + } + catch (Exception e) + { + Log.warn($"Failure getting timestamp of {resolved}. {e.Message}"); + return -1L; + } + } + } +} diff --git a/core/src/main/csharp/ch/cyberduck/core/notifications/AbstractDesktopNotificationService.cs b/core/src/main/csharp/ch/cyberduck/core/notifications/AbstractDesktopNotificationService.cs index 41e583b95f..f72d4650b7 100644 --- a/core/src/main/csharp/ch/cyberduck/core/notifications/AbstractDesktopNotificationService.cs +++ b/core/src/main/csharp/ch/cyberduck/core/notifications/AbstractDesktopNotificationService.cs @@ -181,6 +181,8 @@ namespace Ch.Cyberduck.Core.Notifications } notifier.Show(toast); + GC.KeepAlive(notifier); + GC.KeepAlive(toast); } private void Toast_Activated(ToastNotification sender, object args) diff --git a/core/src/main/csharp/ch/cyberduck/core/preferences/ApplicationPreferences.cs b/core/src/main/csharp/ch/cyberduck/core/preferences/ApplicationPreferences.cs index 75300e13ce..dd35f30991 100644 --- a/core/src/main/csharp/ch/cyberduck/core/preferences/ApplicationPreferences.cs +++ b/core/src/main/csharp/ch/cyberduck/core/preferences/ApplicationPreferences.cs @@ -52,6 +52,7 @@ public class ApplicationPreferences : DefaultPreferences public override void deleteProperty(string property) { propertyStore.DeleteProperty(property); + propertyStore.Save(); } public string GetDefaultLanguage() @@ -126,6 +127,7 @@ public class ApplicationPreferences : DefaultPreferences public override void setProperty(string property, string value) { propertyStore[property] = value; + propertyStore.Save(); } public override List systemLocales() => locales.systemLocales(); diff --git a/core/src/main/csharp/ch/cyberduck/core/preferences/LocalSharedFileSettingsProvider.cs b/core/src/main/csharp/ch/cyberduck/core/preferences/LocalSharedFileSettingsProvider.cs index 5ad48bcdc1..440d6ab25f 100644 --- a/core/src/main/csharp/ch/cyberduck/core/preferences/LocalSharedFileSettingsProvider.cs +++ b/core/src/main/csharp/ch/cyberduck/core/preferences/LocalSharedFileSettingsProvider.cs @@ -1,4 +1,5 @@ -using System; +using org.apache.logging.log4j; +using System; using System.Collections.Specialized; using System.Configuration; using System.IO; @@ -10,6 +11,8 @@ namespace Ch.Cyberduck.Core.Preferences; public class LocalSharedFileSettingsProvider : SettingsProvider { + private static readonly Logger Logger = LogManager.getLogger(typeof(LocalSharedFileSettingsProvider)); + private const string APPLICATIONSETTINGS_ELEMENT_NAME = "applicationSettings"; private const string CONFIGURATION_ELEMENT_NAME = "configuration"; private const string NAME_ATTRIBUTE_NAME = "name"; @@ -27,11 +30,30 @@ public class LocalSharedFileSettingsProvider : SettingsProvider { // store in Packaged cache folder (to ensure clearing after uninstall) // store in roaming app data, if not packaged - var configDirectory = EnvironmentInfo.Packaged - ? ApplicationData.Current.LocalCacheFolder.Path - : Path.Combine(EnvironmentInfo.AppDataPath, EnvironmentInfo.DataFolderName); - - userConfig = new(Path.Combine(configDirectory, $"{EnvironmentInfo.ProductName}.user.config")); + FileInfo userConfigFile = new(Path.Combine(EnvironmentInfo.AppDataPath, EnvironmentInfo.DataFolderName, $"{EnvironmentInfo.ProductName}.user.config")); + if (EnvironmentInfo.Packaged) + { + var sharedUserConfigFile = userConfigFile; + userConfigFile = new(Path.Combine(ApplicationData.Current.LocalCacheFolder.Path, userConfigFile.Name)); + try + { + if (!userConfigFile.Exists && sharedUserConfigFile.Exists) + { + userConfigFile.Directory.Create(); + sharedUserConfigFile.CopyTo(userConfigFile.FullName); + } + } + catch (Exception e) + { + Logger.error("Copying user.config", e); + } + finally + { + userConfigFile.Refresh(); + } + } + + userConfig = userConfigFile; } public override SettingsPropertyValueCollection GetPropertyValues(SettingsContext context, SettingsPropertyCollection collection) diff --git a/core/src/main/java/ch/cyberduck/core/AbstractController.java b/core/src/main/java/ch/cyberduck/core/AbstractController.java index a7b78e5389..507da0a2d4 100644 --- a/core/src/main/java/ch/cyberduck/core/AbstractController.java +++ b/core/src/main/java/ch/cyberduck/core/AbstractController.java @@ -80,17 +80,17 @@ public abstract class AbstractController implements Controller { } @Override - public void start(final BackgroundAction action) { + public void start(final BackgroundAction action) { log.debug("Start action {}", action); } @Override - public void cancel(final BackgroundAction action) { + public void cancel(final BackgroundAction action) { log.debug("Cancel action {}", action); } @Override - public void stop(final BackgroundAction action) { + public void stop(final BackgroundAction action) { log.debug("Stop action {}", action); } diff --git a/core/src/main/java/ch/cyberduck/core/AbstractProtocol.java b/core/src/main/java/ch/cyberduck/core/AbstractProtocol.java index 382b2b204d..7d5475bf5f 100644 --- a/core/src/main/java/ch/cyberduck/core/AbstractProtocol.java +++ b/core/src/main/java/ch/cyberduck/core/AbstractProtocol.java @@ -35,7 +35,6 @@ import java.util.LinkedHashSet; import java.util.List; import java.util.Locale; import java.util.Map; -import java.util.Objects; import java.util.Set; import java.util.stream.Collectors; @@ -292,6 +291,16 @@ public abstract class AbstractProtocol implements Protocol { return false; } + @Override + public boolean isRoleConfigurable() { + return false; + } + + @Override + public boolean isMultiFactorConfigurable() { + return false; + } + @Override public boolean isUTCTimezone() { return true; @@ -445,23 +454,27 @@ public abstract class AbstractProtocol implements Protocol { } } if(options.password) { + if(credentials.isAnonymousLogin()) { + return true; + } switch(this.getType()) { case ftp: case dav: - return Objects.nonNull(credentials.getPassword()); + // Allow blank password + return credentials.isPasswordAuthentication(true); case sftp: // SFTP agent auth requires no password and no private key selection return true; default: - return StringUtils.isNotBlank(credentials.getPassword()); + return credentials.isPasswordAuthentication(); } } if(options.oauth) { - // Always refresh tokens in login + // Always authentication with no tokens preset return true; } if(options.token) { - return StringUtils.isNotBlank(credentials.getToken()); + return credentials.isTokenAuthentication(); } return true; } diff --git a/core/src/main/java/ch/cyberduck/core/AnonymousConnectionService.java b/core/src/main/java/ch/cyberduck/core/AnonymousConnectionService.java deleted file mode 100644 index 7cc5274d55..0000000000 --- a/core/src/main/java/ch/cyberduck/core/AnonymousConnectionService.java +++ /dev/null @@ -1,44 +0,0 @@ -package ch.cyberduck.core; - -/* - * Copyright (c) 2002-2021 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 ch.cyberduck.core.exception.BackgroundException; -import ch.cyberduck.core.proxy.ProxyFactory; -import ch.cyberduck.core.threading.CancelCallback; - -public class AnonymousConnectionService implements ConnectionService { - - @Override - public boolean check(final Session session, final CancelCallback callback) throws BackgroundException { - if(session.isConnected()) { - // Connection already open - return false; - } - this.connect(session, callback); - return true; - } - - @Override - public void connect(final Session session, final CancelCallback cancel) throws BackgroundException { - session.open(ProxyFactory.get(), - new DisabledHostKeyCallback(), new DisabledLoginCallback(), cancel); - } - - @Override - public void close(final Session session) throws BackgroundException { - session.close(); - } -} diff --git a/core/src/main/java/ch/cyberduck/core/Attributes.java b/core/src/main/java/ch/cyberduck/core/Attributes.java index f22eb2aa66..b8cc78fcf6 100644 --- a/core/src/main/java/ch/cyberduck/core/Attributes.java +++ b/core/src/main/java/ch/cyberduck/core/Attributes.java @@ -18,34 +18,34 @@ package ch.cyberduck.core; * dkocher@cyberduck.ch */ -public abstract class Attributes { +public interface Attributes { /** * @return The length of the file */ - public abstract long getSize(); + long getSize(); /** * @return The time the file was last modified in millis UTC or -1 if unknown */ - public abstract long getModificationDate(); + long getModificationDate(); /** * @return The time the file was created in millis UTC or -1 if unknown */ - public abstract long getCreationDate(); + long getCreationDate(); /** * @return The time the file was last accessed in millis UTC or -1 if unknown */ - public abstract long getAccessedDate(); + long getAccessedDate(); /** * @return The file permission mask or null if unknown */ - public abstract Permission getPermission(); + Permission getPermission(); - public abstract String getOwner(); + String getOwner(); - public abstract String getGroup(); + String getGroup(); } \ No newline at end of file diff --git a/core/src/main/java/ch/cyberduck/core/CachingVersionIdProvider.java b/core/src/main/java/ch/cyberduck/core/CachingVersionIdProvider.java index d83e2a793b..eb5b58f87b 100644 --- a/core/src/main/java/ch/cyberduck/core/CachingVersionIdProvider.java +++ b/core/src/main/java/ch/cyberduck/core/CachingVersionIdProvider.java @@ -71,6 +71,10 @@ public abstract class CachingVersionIdProvider implements VersionIdProvider { log.warn("Skip caching for previous version {}", file); return id; } + if(file.attributes().isTrashed()) { + log.warn("Skip caching for trashed version {}", file); + return id; + } cache.put(this.toPredicate(file), id); file.attributes().setVersionId(id); } diff --git a/core/src/main/java/ch/cyberduck/core/Credentials.java b/core/src/main/java/ch/cyberduck/core/Credentials.java index 3e81762840..349e266d41 100644 --- a/core/src/main/java/ch/cyberduck/core/Credentials.java +++ b/core/src/main/java/ch/cyberduck/core/Credentials.java @@ -22,12 +22,14 @@ import ch.cyberduck.core.preferences.PreferencesFactory; import org.apache.commons.lang3.StringUtils; +import java.util.HashMap; +import java.util.Map; import java.util.Objects; /** * Stores the login credentials */ -public class Credentials implements Comparable { +public class Credentials implements CredentialsHolder, Comparable { /** * The login name @@ -38,13 +40,26 @@ public class Credentials implements Comparable { * The login password */ private String password = StringUtils.EMPTY; + /** + * Temporary access tokens + */ private TemporaryAccessTokens tokens = TemporaryAccessTokens.EMPTY; + /** + * OIDC tokens + */ private OAuthTokens oauth = OAuthTokens.EMPTY; + /** + * Custom protocol dependent properties related to authentication + */ + private final Map properties = new HashMap<>(); /** * Private key identity for SSH public key authentication. */ private Local identity; + /** + * Passphrase for private key identity for SSH public key authentication. + */ private String identityPassphrase = StringUtils.EMPTY; /** @@ -67,8 +82,9 @@ public class Credentials implements Comparable { public Credentials(final Credentials copy) { this.user = copy.user; this.password = copy.password; - this.tokens = copy.tokens; - this.oauth = copy.oauth; + this.tokens = TemporaryAccessTokens.EMPTY == copy.tokens ? TemporaryAccessTokens.EMPTY : new TemporaryAccessTokens(copy.tokens); + this.oauth = OAuthTokens.EMPTY == copy.oauth ? OAuthTokens.EMPTY : new OAuthTokens(copy.oauth); + this.properties.putAll(copy.properties); this.identity = copy.identity; this.identityPassphrase = copy.identityPassphrase; this.certificate = copy.certificate; @@ -97,22 +113,21 @@ public class Credentials implements Comparable { /** * @return The login identification */ + @Override public String getUsername() { return user; } - public void setUsername(final String user) { + @Override + public Credentials setUsername(final String user) { this.user = user; - } - - public Credentials withUsername(final String user) { - this.setUsername((user)); return this; } /** * @return The login secret */ + @Override public String getPassword() { if(StringUtils.isEmpty(password)) { if(this.isAnonymousLogin()) { @@ -122,57 +137,49 @@ public class Credentials implements Comparable { return password; } - public void setPassword(final String password) { + @Override + public Credentials setPassword(final String password) { this.password = password; - } - - public Credentials withPassword(final String password) { - this.setPassword(password); return this; } + @Override public String getToken() { return tokens.getSessionToken(); } - public void setToken(final String token) { + @Override + public Credentials setToken(final String token) { this.tokens = new TemporaryAccessTokens(token); - } - - public Credentials withToken(final String token) { - this.setToken(token); return this; } + @Override public TemporaryAccessTokens getTokens() { return tokens; } - public void setTokens(final TemporaryAccessTokens tokens) { + @Override + public Credentials setTokens(final TemporaryAccessTokens tokens) { this.tokens = tokens; - } - - public Credentials withTokens(final TemporaryAccessTokens tokens) { - this.setTokens(tokens); return this; } + @Override public OAuthTokens getOauth() { return oauth; } - public void setOauth(final OAuthTokens oauth) { + @Override + public Credentials setOauth(final OAuthTokens oauth) { this.oauth = oauth; - } - - public Credentials withOauth(final OAuthTokens oauth) { - this.setOauth(oauth); return this; } /** * @return true if the password will be added to the system keychain when logged in successfully */ + @Override public boolean isSaved() { return saved; } @@ -182,32 +189,45 @@ public class Credentials implements Comparable { * * @param saved If true, the password of the login is added to the keychain upon successful login */ - public void setSaved(final boolean saved) { + @Override + public Credentials setSaved(final boolean saved) { this.saved = saved; - } - - public Credentials withSaved(final boolean saved) { - this.setSaved(saved); return this; } /** * @return true if the username is anonymous. */ + @Override public boolean isAnonymousLogin() { return StringUtils.equals(user, PreferencesFactory.get().getProperty("connection.login.anon.name")); } + @Override public boolean isPasswordAuthentication() { + return this.isPasswordAuthentication(false); + } + + /** + * @param allowblank Allow blank password + */ + @Override + public boolean isPasswordAuthentication(final boolean allowblank) { + if(allowblank) { + // Allow blank password + return Objects.nonNull(password); + } return StringUtils.isNotBlank(password); } + @Override public boolean isTokenAuthentication() { - return StringUtils.isNotBlank(tokens.getSessionToken()); + return tokens != TemporaryAccessTokens.EMPTY; } + @Override public boolean isOAuthAuthentication() { - return oauth.validate(); + return oauth != OAuthTokens.EMPTY; } /** @@ -217,6 +237,7 @@ public class Credentials implements Comparable { * specified * @see #setIdentity */ + @Override public boolean isPublicKeyAuthentication() { if(null == identity) { return false; @@ -224,14 +245,10 @@ public class Credentials implements Comparable { return identity.exists(); } - public Credentials withIdentity(final Local file) { - this.identity = file; - return this; - } - /** * @return The path to the private key file to use for public key authentication */ + @Override public Local getIdentity() { return identity; } @@ -241,31 +258,35 @@ public class Credentials implements Comparable { * * @param file Private key file */ - public void setIdentity(final Local file) { + @Override + public Credentials setIdentity(final Local file) { this.identity = file; + return this; } + @Override public String getIdentityPassphrase() { return identityPassphrase; } - public void setIdentityPassphrase(final String identityPassphrase) { - this.identityPassphrase = identityPassphrase; - } - - public Credentials withIdentityPassphrase(final String identityPassphrase) { + @Override + public Credentials setIdentityPassphrase(final String identityPassphrase) { this.identityPassphrase = identityPassphrase; return this; } + @Override public String getCertificate() { return certificate; } - public void setCertificate(final String certificate) { + @Override + public Credentials setCertificate(final String certificate) { this.certificate = certificate; + return this; } + @Override public boolean isCertificateAuthentication() { if(null == certificate) { return false; @@ -273,11 +294,23 @@ public class Credentials implements Comparable { return true; } + @Override + public Credentials setProperty(final String key, final String value) { + properties.put(key, value); + return this; + } + + @Override + public String getProperty(final String key) { + return properties.get(key); + } + /** * @param protocol The protocol to verify against. * @param options Options * @return True if the login credential are valid for the given protocol. */ + @Override public boolean validate(final Protocol protocol, final LoginOptions options) { return protocol.validate(this, options); } @@ -285,6 +318,7 @@ public class Credentials implements Comparable { /** * Clear secrets in memory */ + @Override public void reset() { this.setPassword(StringUtils.EMPTY); this.setToken(StringUtils.EMPTY); @@ -337,6 +371,7 @@ public class Credentials implements Comparable { sb.append(", tokens='").append(tokens).append('\''); sb.append(", oauth='").append(oauth).append('\''); sb.append(", identity=").append(identity); + sb.append(", properties=").append(properties); sb.append('}'); return sb.toString(); } diff --git a/core/src/main/java/ch/cyberduck/core/CredentialsHolder.java b/core/src/main/java/ch/cyberduck/core/CredentialsHolder.java new file mode 100644 index 0000000000..47c35158d9 --- /dev/null +++ b/core/src/main/java/ch/cyberduck/core/CredentialsHolder.java @@ -0,0 +1,78 @@ +package ch.cyberduck.core; + +/* + * Copyright (c) 2002-2025 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 ch.cyberduck.core.preferences.PreferencesReader; + +public interface CredentialsHolder extends PreferencesReader { + String getUsername(); + + Credentials setUsername(String user); + + String getPassword(); + + Credentials setPassword(String password); + + String getToken(); + + Credentials setToken(String token); + + TemporaryAccessTokens getTokens(); + + Credentials setTokens(TemporaryAccessTokens tokens); + + OAuthTokens getOauth(); + + Credentials setOauth(OAuthTokens oauth); + + boolean isSaved(); + + Credentials setSaved(boolean saved); + + boolean isAnonymousLogin(); + + boolean isPasswordAuthentication(); + + boolean isPasswordAuthentication(boolean allowblank); + + boolean isTokenAuthentication(); + + boolean isOAuthAuthentication(); + + boolean isPublicKeyAuthentication(); + + Local getIdentity(); + + Credentials setIdentity(Local file); + + String getIdentityPassphrase(); + + Credentials setIdentityPassphrase(String identityPassphrase); + + String getCertificate(); + + Credentials setCertificate(String certificate); + + boolean isCertificateAuthentication(); + + Credentials setProperty(String key, String value); + + String getProperty(String key); + + boolean validate(Protocol protocol, LoginOptions options); + + void reset(); +} diff --git a/core/src/main/java/ch/cyberduck/core/DefaultHostPasswordStore.java b/core/src/main/java/ch/cyberduck/core/DefaultHostPasswordStore.java index 2edd557189..7d900526c5 100644 --- a/core/src/main/java/ch/cyberduck/core/DefaultHostPasswordStore.java +++ b/core/src/main/java/ch/cyberduck/core/DefaultHostPasswordStore.java @@ -17,13 +17,15 @@ package ch.cyberduck.core; * Bug fixes, suggestions and comments should be sent to feedback@cyberduck.ch */ -import ch.cyberduck.core.exception.LocalAccessDeniedException; +import ch.cyberduck.core.exception.AccessDeniedException; import org.apache.commons.lang3.StringUtils; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; import java.net.URI; +import java.util.LinkedHashSet; +import java.util.Set; public abstract class DefaultHostPasswordStore implements HostPasswordStore { private static final Logger log = LogManager.getLogger(DefaultHostPasswordStore.class); @@ -41,17 +43,14 @@ public abstract class DefaultHostPasswordStore implements HostPasswordStore { return null; } final Credentials credentials = bookmark.getCredentials(); - if(StringUtils.isEmpty(credentials.getUsername())) { - log.warn("Missing username in {}", bookmark); - return null; - } log.info("Fetching login password from keychain for {}", bookmark); final String password; try { password = this.getPassword(bookmark.getProtocol().getScheme(), bookmark.getPort(), - bookmark.getHostname(), credentials.getUsername()); + bookmark.getHostname(), StringUtils.isEmpty(credentials.getUsername()) ? + bookmark.getProtocol().getPasswordPlaceholder() : credentials.getUsername()); } - catch(LocalAccessDeniedException e) { + catch(AccessDeniedException e) { log.warn("Failure {} searching in keychain", e.getMessage()); return null; } @@ -74,9 +73,10 @@ public abstract class DefaultHostPasswordStore implements HostPasswordStore { try { token = this.getPassword(bookmark.getProtocol().getScheme(), bookmark.getPort(), bookmark.getHostname(), StringUtils.isEmpty(credentials.getUsername()) ? - bookmark.getProtocol().getTokenPlaceholder() : String.format("%s (%s)", bookmark.getProtocol().getTokenPlaceholder(), credentials.getUsername())); + bookmark.getProtocol().getTokenPlaceholder() : + String.format("%s (%s)", bookmark.getProtocol().getTokenPlaceholder(), credentials.getUsername())); } - catch(LocalAccessDeniedException e) { + catch(AccessDeniedException e) { log.warn("Failure {} searching in keychain", e.getMessage()); return null; } @@ -121,7 +121,7 @@ public abstract class DefaultHostPasswordStore implements HostPasswordStore { } return passphrase; } - catch(LocalAccessDeniedException e) { + catch(AccessDeniedException e) { log.warn("Failure {} searching in keychain", e.getMessage()); return null; } @@ -134,13 +134,15 @@ public abstract class DefaultHostPasswordStore implements HostPasswordStore { @Override public OAuthTokens findOAuthTokens(final Host bookmark) { log.info("Fetching OAuth tokens from keychain for {}", bookmark); - final String[] descriptors = getOAuthPrefix(bookmark); - for(String prefix : descriptors) { + for(String prefix : getOAuthPrefix(bookmark)) { log.debug("Search with prefix {}", prefix); final String hostname = getOAuthHostname(bookmark); + if(StringUtils.isBlank(hostname)) { + continue; + } log.debug("Search with hostname {}", hostname); try { - final String expiry = this.getPassword(getOAuthHostname(bookmark), String.format("%s OAuth2 Token Expiry", prefix)); + final String expiry = this.getPassword(hostname, String.format("%s OAuth2 Token Expiry", prefix)); final OAuthTokens tokens = new OAuthTokens( this.getPassword(getOAuthScheme(bookmark), getOAuthPort(bookmark), hostname, String.format("%s OAuth2 Access Token", prefix)), @@ -155,7 +157,7 @@ public abstract class DefaultHostPasswordStore implements HostPasswordStore { } // Continue with deprecated descriptors } - catch(LocalAccessDeniedException e) { + catch(AccessDeniedException e) { log.warn("Failure {} searching in keychain", e.getMessage()); return OAuthTokens.EMPTY; } @@ -164,44 +166,62 @@ public abstract class DefaultHostPasswordStore implements HostPasswordStore { } protected static Scheme getOAuthScheme(final Host bookmark) { - final URI uri = URI.create(bookmark.getProtocol().getOAuthTokenUrl()); - if(null == uri.getScheme()) { - return bookmark.getProtocol().getScheme(); + if(null != bookmark.getProtocol().getOAuthTokenUrl()) { + final URI uri = URI.create(bookmark.getProtocol().getOAuthTokenUrl()); + if(null == uri.getScheme()) { + return bookmark.getProtocol().getScheme(); + } + return Scheme.valueOf(uri.getScheme()); } - return Scheme.valueOf(uri.getScheme()); + return bookmark.getProtocol().getScheme(); } protected static String getOAuthHostname(final Host bookmark) { - final URI uri = URI.create(bookmark.getProtocol().getOAuthTokenUrl()); - if(StringUtils.isNotBlank(uri.getHost())) { - return uri.getHost(); + if(null != bookmark.getProtocol().getOAuthTokenUrl()) { + final URI uri = URI.create(bookmark.getProtocol().getOAuthTokenUrl()); + if(StringUtils.isNotBlank(uri.getHost())) { + return uri.getHost(); + } + return bookmark.getHostname(); } return bookmark.getHostname(); } protected static int getOAuthPort(final Host bookmark) { - final URI uri = URI.create(bookmark.getProtocol().getOAuthTokenUrl()); - if(-1 != uri.getPort()) { - return uri.getPort(); + if(null != bookmark.getProtocol().getOAuthTokenUrl()) { + final URI uri = URI.create(bookmark.getProtocol().getOAuthTokenUrl()); + if(-1 != uri.getPort()) { + return uri.getPort(); + } } return getOAuthScheme(bookmark).getPort(); } - protected static String[] getOAuthPrefix(final Host bookmark) { + protected static Set getOAuthPrefix(final Host bookmark) { + final Set prefix = new LinkedHashSet<>(); if(StringUtils.isNotBlank(bookmark.getCredentials().getUsername())) { - return new String[]{ - String.format("%s (%s)", bookmark.getProtocol().getOAuthClientId(), bookmark.getCredentials().getUsername()), - String.format("%s (%s)", bookmark.getProtocol().getDescription(), bookmark.getCredentials().getUsername()) - }; + if(StringUtils.isNotBlank(bookmark.getProtocol().getOAuthClientId())) { + prefix.add(String.format("%s (%s)", bookmark.getProtocol().getOAuthClientId(), bookmark.getCredentials().getUsername())); + } + if(StringUtils.isNotBlank(bookmark.getProperty(Profile.OAUTH_CLIENT_ID_KEY))) { + prefix.add(String.format("%s (%s)", bookmark.getProperty(Profile.OAUTH_CLIENT_ID_KEY), bookmark.getCredentials().getUsername())); + } + prefix.add(String.format("%s (%s)", bookmark.getProtocol().getDescription(), bookmark.getCredentials().getUsername())); } - return new String[]{ - bookmark.getProtocol().getOAuthClientId(), - bookmark.getProtocol().getDescription() - }; + else { + if(StringUtils.isNotBlank(bookmark.getProtocol().getOAuthClientId())) { + prefix.add(bookmark.getProtocol().getOAuthClientId()); + } + if(StringUtils.isNotBlank(bookmark.getProperty(Profile.OAUTH_CLIENT_ID_KEY))) { + prefix.add(bookmark.getProperty(Profile.OAUTH_CLIENT_ID_KEY)); + } + prefix.add(bookmark.getProtocol().getDescription()); + } + return prefix; } @Override - public void save(final Host bookmark) throws LocalAccessDeniedException { + public void save(final Host bookmark) throws AccessDeniedException { if(StringUtils.isEmpty(bookmark.getHostname())) { log.warn("No hostname given"); return; @@ -214,44 +234,46 @@ public abstract class DefaultHostPasswordStore implements HostPasswordStore { credentials.getIdentityPassphrase()); } if(credentials.isPasswordAuthentication()) { - if(StringUtils.isEmpty(credentials.getUsername())) { - log.warn("No username in credentials for bookmark {}", bookmark.getHostname()); - return; - } if(StringUtils.isEmpty(credentials.getPassword())) { log.warn("No password in credentials for bookmark {}", bookmark.getHostname()); return; } this.addPassword(protocol.getScheme(), bookmark.getPort(), - bookmark.getHostname(), credentials.getUsername(), credentials.getPassword()); + bookmark.getHostname(), StringUtils.isEmpty(credentials.getUsername()) ? + protocol.getPasswordPlaceholder() : credentials.getUsername(), credentials.getPassword()); } if(credentials.isTokenAuthentication()) { - this.addPassword(protocol.getScheme(), bookmark.getPort(), - bookmark.getHostname(), StringUtils.isEmpty(credentials.getUsername()) ? - protocol.getTokenPlaceholder() : String.format("%s (%s)", protocol.getTokenPlaceholder(), credentials.getUsername()), - credentials.getToken()); + if(StringUtils.isNotBlank(credentials.getToken())) { + this.addPassword(protocol.getScheme(), bookmark.getPort(), + bookmark.getHostname(), StringUtils.isEmpty(credentials.getUsername()) ? + protocol.getTokenPlaceholder() : String.format("%s (%s)", protocol.getTokenPlaceholder(), credentials.getUsername()), + credentials.getToken()); + } } if(credentials.isOAuthAuthentication()) { - final String[] descriptors = getOAuthPrefix(bookmark); - for(String prefix : descriptors) { + for(String prefix : getOAuthPrefix(bookmark)) { + final String hostname = getOAuthHostname(bookmark); + if(StringUtils.isBlank(hostname)) { + continue; + } if(StringUtils.isNotBlank(credentials.getOauth().getAccessToken())) { this.addPassword(getOAuthScheme(bookmark), - getOAuthPort(bookmark), getOAuthHostname(bookmark), + getOAuthPort(bookmark), hostname, String.format("%s OAuth2 Access Token", prefix), credentials.getOauth().getAccessToken()); } if(StringUtils.isNotBlank(credentials.getOauth().getRefreshToken())) { this.addPassword(getOAuthScheme(bookmark), - getOAuthPort(bookmark), getOAuthHostname(bookmark), + getOAuthPort(bookmark), hostname, String.format("%s OAuth2 Refresh Token", prefix), credentials.getOauth().getRefreshToken()); } // Save expiry if(credentials.getOauth().getExpiryInMilliseconds() != null) { - this.addPassword(getOAuthHostname(bookmark), String.format("%s OAuth2 Token Expiry", prefix), + this.addPassword(hostname, String.format("%s OAuth2 Token Expiry", prefix), String.valueOf(credentials.getOauth().getExpiryInMilliseconds())); } if(StringUtils.isNotBlank(credentials.getOauth().getIdToken())) { this.addPassword(getOAuthScheme(bookmark), - getOAuthPort(bookmark), getOAuthHostname(bookmark), + getOAuthPort(bookmark), hostname, String.format("%s OIDC Id Token", prefix), credentials.getOauth().getIdToken()); } break; @@ -260,7 +282,7 @@ public abstract class DefaultHostPasswordStore implements HostPasswordStore { } @Override - public void delete(final Host bookmark) throws LocalAccessDeniedException { + public void delete(final Host bookmark) throws AccessDeniedException { log.info("Delete password for bookmark {}", bookmark); final Credentials credentials = bookmark.getCredentials(); final Protocol protocol = bookmark.getProtocol(); @@ -268,12 +290,9 @@ public abstract class DefaultHostPasswordStore implements HostPasswordStore { this.deletePassword(bookmark.getHostname(), credentials.getIdentity().getAbbreviatedPath()); } if(protocol.isPasswordConfigurable()) { - if(StringUtils.isEmpty(credentials.getUsername())) { - log.warn("No username in credentials for bookmark {}", bookmark.getHostname()); - return; - } this.deletePassword(protocol.getScheme(), bookmark.getPort(), bookmark.getHostname(), - credentials.getUsername()); + StringUtils.isEmpty(credentials.getUsername()) ? + bookmark.getProtocol().getPasswordPlaceholder() : credentials.getUsername()); } if(protocol.isTokenConfigurable()) { this.deletePassword(protocol.getScheme(), bookmark.getPort(), bookmark.getHostname(), @@ -281,22 +300,25 @@ public abstract class DefaultHostPasswordStore implements HostPasswordStore { protocol.getTokenPlaceholder() : String.format("%s (%s)", protocol.getTokenPlaceholder(), credentials.getUsername())); } if(protocol.isOAuthConfigurable()) { - final String[] descriptors = getOAuthPrefix(bookmark); - for(String prefix : descriptors) { + for(String prefix : getOAuthPrefix(bookmark)) { + final String hostname = getOAuthHostname(bookmark); + if(StringUtils.isBlank(hostname)) { + continue; + } if(StringUtils.isNotBlank(credentials.getOauth().getAccessToken())) { - this.deletePassword(getOAuthScheme(bookmark), getOAuthPort(bookmark), getOAuthHostname(bookmark), + this.deletePassword(getOAuthScheme(bookmark), getOAuthPort(bookmark), hostname, String.format("%s OAuth2 Access Token", prefix)); } if(StringUtils.isNotBlank(credentials.getOauth().getRefreshToken())) { - this.deletePassword(getOAuthScheme(bookmark), getOAuthPort(bookmark), getOAuthHostname(bookmark), + this.deletePassword(getOAuthScheme(bookmark), getOAuthPort(bookmark), hostname, String.format("%s OAuth2 Refresh Token", prefix)); } // Save expiry if(credentials.getOauth().getExpiryInMilliseconds() != null) { - this.deletePassword(getOAuthHostname(bookmark), String.format("%s OAuth2 Token Expiry", prefix)); + this.deletePassword(hostname, String.format("%s OAuth2 Token Expiry", prefix)); } if(StringUtils.isNotBlank(credentials.getOauth().getIdToken())) { - this.deletePassword(getOAuthScheme(bookmark), getOAuthPort(bookmark), getOAuthHostname(bookmark), + this.deletePassword(getOAuthScheme(bookmark), getOAuthPort(bookmark), hostname, String.format("%s OIDC Id Token", prefix)); } } diff --git a/core/src/main/java/ch/cyberduck/core/DefaultPathAttributes.java b/core/src/main/java/ch/cyberduck/core/DefaultPathAttributes.java new file mode 100644 index 0000000000..d69b4a69a7 --- /dev/null +++ b/core/src/main/java/ch/cyberduck/core/DefaultPathAttributes.java @@ -0,0 +1,756 @@ +package ch.cyberduck.core; + +/* + * Copyright (c) 2005 David Kocher. All rights reserved. + * http://cyberduck.ch/ + * + * 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. + * + * Bug fixes, suggestions and comments should be sent to: + * dkocher@cyberduck.ch + */ + +import ch.cyberduck.core.features.Encryption; +import ch.cyberduck.core.features.Quota; +import ch.cyberduck.core.io.Checksum; +import ch.cyberduck.core.serializer.Serializer; +import ch.cyberduck.core.transfer.TransferStatus; + +import org.apache.commons.lang3.StringUtils; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; + +import java.util.Collections; +import java.util.HashMap; +import java.util.Map; +import java.util.Objects; + +/** + * Attributes of a remote directory or file. + */ +public class DefaultPathAttributes implements PathAttributes, Attributes, Serializable { + private static final Logger log = LogManager.getLogger(DefaultPathAttributes.class); + + /** + * The file length + */ + private long size = TransferStatus.UNKNOWN_LENGTH; + + /** + * Quota of folder + */ + private Quota.Space quota = Quota.unknown; + + /** + * The file modification date in milliseconds + */ + private long modified = -1; + /** + * Last accessed timestamp in milliseconds + */ + private long accessed = -1; + /** + * When this file was originally created in milliseconds + */ + private long created = -1; + + private String owner; + private String group; + + private Permission permission = Permission.EMPTY; + + private Acl acl = Acl.EMPTY; + + /** + * MD5 checksum + */ + private Checksum checksum = Checksum.NONE; + + /** + * ETag header in HTTP + */ + private String etag; + + /** + * Redundancy level if available + */ + private String storageClass; + + /** + * Server side encryption (SSE) algorithm and key or null + */ + private Encryption.Algorithm encryption = Encryption.Algorithm.NONE; + + /** + * Unique identifier for a given file. Must remain constant even after updating the file. + */ + private String fileId; + + /** + * Unique identifier for a given version of a file + */ + private String versionId; + + /** + * Lock id + */ + private String lockId; + + /** + * Should be hidden in the browser by default + */ + private Boolean duplicate; + + /** + * Hidden flag set on server + */ + private Boolean hidden; + + /** + * Trashed + */ + private Boolean trashed; + + /** + * Revision number + */ + private Long revision; + + /** + * Geographical location + */ + private String region; + + /** + * + */ + private String displayname; + + private DescriptiveUrl link = DescriptiveUrl.EMPTY; + + /** + * HTTP headers + */ + private Map metadata = Collections.emptyMap(); + + /** + * Cryptomator vault + */ + private Path vault; + /** + * Cryptomator decrypted path + */ + private Path decrypted; + /** + * Cryptomator encrypted path. + */ + private Path encrypted; + /** + * Unique identifier for cryptomator + */ + private String directoryId; + + private Map custom = Collections.emptyMap(); + + private Verdict verdict; + + public DefaultPathAttributes() { + } + + public DefaultPathAttributes(final PathAttributes copy) { + size = copy.getSize(); + quota = copy.getQuota(); + modified = copy.getModificationDate(); + accessed = copy.getAccessedDate(); + created = copy.getCreationDate(); + owner = copy.getOwner(); + group = copy.getGroup(); + permission = Permission.EMPTY == copy.getPermission() ? Permission.EMPTY : new Permission(copy.getPermission()); + acl = Acl.EMPTY == copy.getAcl() ? Acl.EMPTY : new Acl(copy.getAcl()); + checksum = Checksum.NONE == copy.getChecksum() ? Checksum.NONE : new Checksum(copy.getChecksum()); + etag = copy.getETag(); + storageClass = copy.getStorageClass(); + encryption = copy.getEncryption(); + fileId = copy.getFileId(); + versionId = copy.getVersionId(); + lockId = copy.getLockId(); + duplicate = copy.isDuplicate(); + hidden = copy.isHidden(); + trashed = copy.isTrashed(); + revision = copy.getRevision(); + region = copy.getRegion(); + displayname = copy.getDisplayname(); + link = DescriptiveUrl.EMPTY == copy.getLink() ? DescriptiveUrl.EMPTY : new DescriptiveUrl(copy.getLink()); + metadata = new HashMap<>(copy.getMetadata()); + custom = new HashMap<>(copy.getCustom()); + verdict = copy.getVerdict(); + vault = copy.getVault(); + decrypted = copy.getDecrypted(); + encrypted = copy.getEncrypted(); + directoryId = copy.getDirectoryId(); + } + + @Override + public T serialize(final Serializer dict) { + if(size != -1) { + dict.setStringForKey(String.valueOf(size), "Size"); + } + if(quota != Quota.unknown) { + // Set remaining quota + dict.setStringForKey(String.valueOf(quota.available), "Quota"); + } + if(modified != -1) { + dict.setStringForKey(String.valueOf(modified), "Modified"); + } + if(created != -1) { + dict.setStringForKey(String.valueOf(created), "Created"); + } + if(revision != null) { + dict.setStringForKey(String.valueOf(revision), "Revision"); + } + if(etag != null) { + dict.setStringForKey(etag, "ETag"); + } + if(permission != Permission.EMPTY) { + dict.setObjectForKey(permission, "Permission"); + } + if(owner != null) { + dict.setStringForKey(owner, "Owner"); + } + if(group != null) { + dict.setStringForKey(group, "Group"); + } + if(acl != Acl.EMPTY) { + dict.setObjectForKey(acl, "Acl"); + } + if(link != DescriptiveUrl.EMPTY) { + final Map wrapper = new HashMap<>(); + wrapper.put("Url", link.getUrl()); + wrapper.put("Type", link.getType().name()); + dict.setMapForKey(wrapper, "Link"); + } + if(checksum != Checksum.NONE) { + final Map wrapper = new HashMap<>(); + wrapper.put("Algorithm", checksum.algorithm.name()); + wrapper.put("Hash", checksum.hash); + if(null != checksum.base64) { + wrapper.put("Base64", checksum.base64); + } + dict.setMapForKey(wrapper, "Checksum"); + } + if(StringUtils.isNotBlank(versionId)) { + dict.setStringForKey(versionId, "Version"); + } + if(StringUtils.isNotBlank(fileId)) { + dict.setStringForKey(fileId, "File Id"); + } + if(StringUtils.isNotBlank(displayname)) { + dict.setStringForKey(displayname, "Display Name"); + } + if(StringUtils.isNotBlank(lockId)) { + dict.setStringForKey(lockId, "Lock Id"); + } + if(duplicate != null) { + dict.setStringForKey(String.valueOf(duplicate), "Duplicate"); + } + if(hidden != null) { + dict.setStringForKey(String.valueOf(hidden), "Hidden"); + } + if(trashed != null) { + dict.setStringForKey(String.valueOf(trashed), "Trashed"); + } + if(StringUtils.isNotBlank(region)) { + dict.setStringForKey(region, "Region"); + } + if(StringUtils.isNotBlank(storageClass)) { + dict.setStringForKey(storageClass, "Storage Class"); + } + if(vault != null) { + if(vault.attributes() == this) { + log.debug("Skip serializing vault attribute {} to avoid recursion", vault); + } + else { + dict.setObjectForKey(vault, "Vault"); + } + } + if(!custom.isEmpty()) { + dict.setMapForKey(custom, "Custom"); + } + if(verdict != null) { + dict.setStringForKey(verdict.name(), "Verdict"); + } + return dict.getSerialized(); + } + + /** + * @return length the size of file in bytes. + */ + @Override + public long getSize() { + return size; + } + + /** + * @param size the size of file in bytes. + */ + @Override + public PathAttributes setSize(final long size) { + this.size = size; + return this; + } + + @Override + public Quota.Space getQuota() { + return quota; + } + + @Override + public PathAttributes setQuota(final Quota.Space quota) { + this.quota = quota; + return this; + } + + @Override + public long getModificationDate() { + return modified; + } + + @Override + public PathAttributes setModificationDate(final long millis) { + this.modified = millis; + return this; + } + + @Override + public long getCreationDate() { + return created; + } + + @Override + public PathAttributes setCreationDate(final long millis) { + this.created = millis; + return this; + } + + @Override + public long getAccessedDate() { + return accessed; + } + + @Override + public PathAttributes setAccessedDate(final long millis) { + this.accessed = millis; + return this; + } + + /** + * @return UNIX permissions + */ + @Override + public Permission getPermission() { + return permission; + } + + /** + * @param p UNIX permissions + */ + @Override + public PathAttributes setPermission(final Permission p) { + this.permission = p; + return this; + } + + @Override + public Acl getAcl() { + return acl; + } + + @Override + public PathAttributes setAcl(final Acl acl) { + this.acl = acl; + return this; + } + + @Override + public String getOwner() { + return owner; + } + + @Override + public PathAttributes setOwner(final String o) { + this.owner = o; + return this; + } + + @Override + public String getGroup() { + return group; + } + + @Override + public PathAttributes setGroup(final String g) { + this.group = g; + return this; + } + + @Override + public Checksum getChecksum() { + return checksum; + } + + @Override + public PathAttributes setChecksum(final Checksum checksum) { + this.checksum = checksum; + return this; + } + + @Override + public String getETag() { + return etag; + } + + @Override + public PathAttributes setETag(final String etag) { + this.etag = etag; + return this; + } + + /** + * @return Storage redundancy identifier. + */ + @Override + public String getStorageClass() { + return storageClass; + } + + /** + * @param storageClass Storage redundancy identifier. + */ + @Override + public PathAttributes setStorageClass(final String storageClass) { + this.storageClass = storageClass; + return this; + } + + @Override + public Encryption.Algorithm getEncryption() { + return encryption; + } + + @Override + public PathAttributes setEncryption(final Encryption.Algorithm encryption) { + this.encryption = encryption; + return this; + } + + /** + * A version identifying a particular revision of a file with the same path. + * + * @return Version Identifier or null if not versioned. + */ + @Override + public String getVersionId() { + return versionId; + } + + /** + * Set a unique version identifier for the revision of a file. + * + * @param versionId Revision + */ + @Override + public PathAttributes setVersionId(final String versionId) { + this.versionId = versionId; + return this; + } + + /** + * A unique identifier for a file with the same path. Remains constant over its lifetime. + * + * @return Identifier or null if there is no such concept + */ + @Override + public String getFileId() { + return fileId; + } + + @Override + public PathAttributes setFileId(final String fileId) { + this.fileId = fileId; + return this; + } + + @Override + public String getLockId() { + return lockId; + } + + @Override + public PathAttributes setLockId(final String lockId) { + this.lockId = lockId; + return this; + } + + @Override + public String getDirectoryId() { + return directoryId; + } + + @Override + public PathAttributes setDirectoryId(final String directoryId) { + this.directoryId = directoryId; + return this; + } + + @Override + public Long getRevision() { + return revision; + } + + @Override + public PathAttributes setRevision(final Long revision) { + this.revision = revision; + return this; + } + + @Override + public Path getDecrypted() { + return decrypted; + } + + @Override + public PathAttributes setDecrypted(final Path decrypted) { + this.decrypted = decrypted; + return this; + } + + @Override + public Path getEncrypted() { + return encrypted; + } + + @Override + public PathAttributes setEncrypted(final Path encrypted) { + this.encrypted = encrypted; + return this; + } + + @Override + public PathAttributes setVault(final Path vault) { + this.vault = vault; + return this; + } + + @Override + public Path getVault() { + return vault; + } + + @Override + public boolean isDuplicate() { + return duplicate != null && duplicate; + } + + /** + * Attribute to mark a file as hidden by default in addition to a filename convention. + * + * @param duplicate Flag + */ + @Override + public PathAttributes setDuplicate(final boolean duplicate) { + this.duplicate = duplicate; + return this; + } + + @Override + public Boolean isHidden() { + return hidden != null && hidden; + } + + @Override + public PathAttributes setHidden(final boolean hidden) { + this.hidden = hidden; + return this; + } + + @Override + public Boolean isTrashed() { + return trashed != null && trashed; + } + + @Override + public PathAttributes setTrashed(final boolean trashed) { + this.trashed = trashed; + return this; + } + + @Override + public Map getMetadata() { + return metadata; + } + + @Override + public PathAttributes setMetadata(final Map metadata) { + this.metadata = metadata; + return this; + } + + @Override + public String getRegion() { + return region; + } + + @Override + public PathAttributes setRegion(final String region) { + this.region = region; + return this; + } + + @Override + public String getDisplayname() { + return displayname; + } + + @Override + public PathAttributes setDisplayname(final String displayname) { + this.displayname = displayname; + return this; + } + + @Override + public DescriptiveUrl getLink() { + return link; + } + + @Override + public PathAttributes setLink(final DescriptiveUrl link) { + this.link = link; + return this; + } + + @Override + public Map getCustom() { + return custom; + } + + @Override + public PathAttributes setCustom(final Map custom) { + this.custom = custom; + return this; + } + + @Override + public PathAttributes setCustom(final String key, final String value) { + custom = new HashMap<>(custom); + custom.put(key, value); + return this; + } + + @Override + public Verdict getVerdict() { + return verdict; + } + + @Override + public PathAttributes setVerdict(final Verdict verdict) { + this.verdict = verdict; + return this; + } + + @Override + public boolean equals(final Object o) { + if(this == o) { + return true; + } + if(!(o instanceof DefaultPathAttributes)) { + return false; + } + final DefaultPathAttributes that = (DefaultPathAttributes) o; + if(modified != that.modified) { + return false; + } + if(size != that.size) { + return false; + } + if(!Objects.equals(checksum, that.checksum)) { + return false; + } + if(!Objects.equals(permission, that.permission)) { + return false; + } + if(!Objects.equals(acl, that.acl)) { + return false; + } + if(!Objects.equals(versionId, that.versionId)) { + return false; + } + if(!Objects.equals(fileId, that.fileId)) { + return false; + } + if(!Objects.equals(revision, that.revision)) { + return false; + } + if(!Objects.equals(vault, that.vault)) { + return false; + } + if(!Objects.equals(lockId, that.lockId)) { + return false; + } + return true; + } + + @Override + public int hashCode() { + int result = (int) (size ^ (size >>> 32)); + result = 31 * result + (int) (modified ^ (modified >>> 32)); + result = 31 * result + (permission != null ? permission.hashCode() : 0); + result = 31 * result + (acl != null ? acl.hashCode() : 0); + result = 31 * result + (checksum != null ? checksum.hashCode() : 0); + result = 31 * result + (versionId != null ? versionId.hashCode() : 0); + result = 31 * result + (fileId != null ? fileId.hashCode() : 0); + result = 31 * result + (revision != null ? revision.hashCode() : 0); + result = 31 * result + (verdict != null ? verdict.hashCode() : 0); + result = 31 * result + (vault != null ? vault.hashCode() : 0); + result = 31 * result + (lockId != null ? lockId.hashCode() : 0); + return result; + } + + @Override + public String toString() { + final StringBuilder sb = new StringBuilder("PathAttributes{"); + sb.append("accessed=").append(accessed); + sb.append(", size=").append(size); + sb.append(", modified=").append(modified); + sb.append(", created=").append(created); + sb.append(", owner='").append(owner).append('\''); + sb.append(", group='").append(group).append('\''); + sb.append(", permission=").append(permission); + sb.append(", acl=").append(acl); + sb.append(", checksum='").append(checksum).append('\''); + sb.append(", etag='").append(etag).append('\''); + sb.append(", storageClass='").append(storageClass).append('\''); + sb.append(", encryption='").append(encryption).append('\''); + sb.append(", versionId='").append(versionId).append('\''); + sb.append(", fileId='").append(fileId).append('\''); + sb.append(", lockId='").append(lockId).append('\''); + sb.append(", duplicate=").append(duplicate); + sb.append(", hidden=").append(hidden); + sb.append(", trashed=").append(trashed); + sb.append(", revision=").append(revision); + sb.append(", region='").append(region).append('\''); + sb.append(", metadata=").append(metadata).append('\''); + sb.append(", custom=").append(custom).append('\''); + sb.append(", verdict=").append(verdict).append('\''); + sb.append('}'); + return sb.toString(); + } + +} diff --git a/core/src/main/java/ch/cyberduck/core/DisabledPasswordStore.java b/core/src/main/java/ch/cyberduck/core/DisabledPasswordStore.java index adb3827f64..9a7cdfc004 100644 --- a/core/src/main/java/ch/cyberduck/core/DisabledPasswordStore.java +++ b/core/src/main/java/ch/cyberduck/core/DisabledPasswordStore.java @@ -17,8 +17,6 @@ package ch.cyberduck.core; * Bug fixes, suggestions and comments should be sent to feedback@cyberduck.ch */ -import ch.cyberduck.core.exception.LocalAccessDeniedException; - public class DisabledPasswordStore extends DefaultHostPasswordStore { @Override @@ -42,12 +40,12 @@ public class DisabledPasswordStore extends DefaultHostPasswordStore { } @Override - public void deletePassword(final String serviceName, final String user) throws LocalAccessDeniedException { + public void deletePassword(final String serviceName, final String user) { } @Override - public void deletePassword(final Scheme scheme, final int port, final String hostname, final String user) throws LocalAccessDeniedException { + public void deletePassword(final Scheme scheme, final int port, final String hostname, final String user) { } } diff --git a/core/src/main/java/ch/cyberduck/core/Host.java b/core/src/main/java/ch/cyberduck/core/Host.java index c43b28cf5b..e1b1ae9c56 100644 --- a/core/src/main/java/ch/cyberduck/core/Host.java +++ b/core/src/main/java/ch/cyberduck/core/Host.java @@ -20,6 +20,7 @@ package ch.cyberduck.core; import ch.cyberduck.core.ftp.FTPConnectMode; import ch.cyberduck.core.preferences.PreferencesFactory; +import ch.cyberduck.core.preferences.PreferencesReader; import ch.cyberduck.core.serializer.Serializer; import org.apache.commons.lang3.StringUtils; @@ -31,12 +32,16 @@ import java.util.Map; import java.util.Set; import java.util.TimeZone; -public class Host implements Serializable, Comparable { +public class Host implements Serializable, Comparable, PreferencesReader { /** * The credentials to authenticate with for the CDN */ private final Credentials cloudfront = new Credentials(); + /** + * Jump host configuration for SSH bastion host connections + */ + private Host jumphost; /** * The protocol identifier. */ @@ -223,6 +228,7 @@ public class Host implements Serializable, Comparable { this.port = other.port; this.hostname = other.hostname; this.credentials = new Credentials(other.credentials); + this.jumphost = other.jumphost; this.uuid = other.uuid; this.nickname = other.nickname; this.defaultpath = other.defaultpath; @@ -330,31 +336,44 @@ public class Host implements Serializable, Comparable { /** * @param defaultpath The path to change the working directory to upon connecting */ - public void setDefaultPath(final String defaultpath) { + public Host setDefaultPath(final String defaultpath) { this.defaultpath = defaultpath; + return this; } public Path getWorkdir() { return workdir; } - public void setWorkdir(final Path workdir) { + public Host setWorkdir(final Path workdir) { this.workdir = workdir; + return this; } public Credentials getCredentials() { return credentials; } - public void setCredentials(final Credentials credentials) { - this.credentials = credentials; - } - - public Host withCredentials(final Credentials credentials) { + public Host setCredentials(final Credentials credentials) { + log.debug("Setting credentials for {} to {}", this, credentials); this.credentials = credentials; return this; } + /** + * @return Jump host configuration for SSH bastion host connections + */ + public Host getJumphost() { + return jumphost; + } + + /** + * @param jumphost Jump host configuration for SSH bastion host connections + */ + public void setJumphost(final Host jumphost) { + this.jumphost = jumphost; + } + /** * @return Credentials to modify CDN configuration */ @@ -369,8 +388,9 @@ public class Host implements Serializable, Comparable { /** * @param protocol Connection profile */ - public void setProtocol(final Protocol protocol) { + public Host setProtocol(final Protocol protocol) { this.protocol = protocol; + return this; } public String getUuid() { @@ -380,8 +400,9 @@ public class Host implements Serializable, Comparable { return uuid; } - public void setUuid(String uuid) { + public Host setUuid(String uuid) { this.uuid = uuid; + return this; } /** @@ -398,8 +419,9 @@ public class Host implements Serializable, Comparable { * * @param nickname Custom name */ - public void setNickname(String nickname) { + public Host setNickname(String nickname) { this.nickname = nickname; + return this; } /** @@ -412,8 +434,9 @@ public class Host implements Serializable, Comparable { /** * @param hostname Server */ - public void setHostname(final String hostname) { + public Host setHostname(final String hostname) { this.hostname = StringUtils.trim(hostname); + return this; } /** @@ -426,8 +449,9 @@ public class Host implements Serializable, Comparable { /** * @param port The port number to connect to or -1 to use the default port for this protocol */ - public void setPort(final int port) { + public Host setPort(final int port) { this.port = -1 == port ? protocol.getDefaultPort() : port; + return this; } /** @@ -446,8 +470,9 @@ public class Host implements Serializable, Comparable { * * @param encoding Control connection encoding */ - public void setEncoding(final String encoding) { + public Host setEncoding(final String encoding) { this.encoding = encoding; + return this; } /** @@ -458,8 +483,9 @@ public class Host implements Serializable, Comparable { return connectMode; } - public void setFTPConnectMode(final FTPConnectMode connectMode) { + public Host setFTPConnectMode(final FTPConnectMode connectMode) { this.connectMode = connectMode; + return this; } /** @@ -474,8 +500,9 @@ public class Host implements Serializable, Comparable { * * @param transfer null to use the default value or -1 if no limit */ - public void setTransfer(final TransferType transfer) { + public Host setTransferType(final TransferType transfer) { this.transfer = transfer; + return this; } /** @@ -492,16 +519,18 @@ public class Host implements Serializable, Comparable { * * @param folder Absolute path */ - public void setDownloadFolder(final Local folder) { + public Host setDownloadFolder(final Local folder) { downloadFolder = folder; + return this; } public Local getUploadFolder() { return uploadFolder; } - public void setUploadFolder(final Local folder) { + public Host setUploadFolder(final Local folder) { this.uploadFolder = folder; + return this; } /** @@ -517,8 +546,9 @@ public class Host implements Serializable, Comparable { * * @param timezone Timezone of server */ - public void setTimezone(final TimeZone timezone) { + public Host setTimezone(final TimeZone timezone) { this.timezone = timezone; + return this; } /** @@ -527,18 +557,22 @@ public class Host implements Serializable, Comparable { * @param key Property name * @return Value for property key */ + @Override public String getProperty(final String key) { final Map overrides = this.getCustom(); if(overrides.containsKey(key)) { return overrides.get(key); } + if(credentials.getProperty(key) != null) { + return credentials.getProperty(key); + } return protocol.getProperties().get(key); } - public void setProperty(final String key, final String value) { + public Host setProperty(final String key, final String value) { final Map overrides = new HashMap<>(this.getCustom()); overrides.put(key, value); - this.setCustom(overrides); + return this.setCustom(overrides); } public String getRegion() { @@ -548,12 +582,8 @@ public class Host implements Serializable, Comparable { return region; } - public void setRegion(final String region) { + public Host setRegion(final String region) { this.region = region; - } - - public Host withRegion(final String region) { - this.setRegion(region); return this; } @@ -567,8 +597,9 @@ public class Host implements Serializable, Comparable { /** * @param comment Notice */ - public void setComment(final String comment) { + public Host setComment(final String comment) { this.comment = comment; + return this; } /** @@ -578,12 +609,8 @@ public class Host implements Serializable, Comparable { return webURL; } - public void setWebURL(final String url) { + public Host setWebURL(final String url) { webURL = url; - } - - public Host withWebURL(final String url) { - this.setWebURL(url); return this; } @@ -599,40 +626,45 @@ public class Host implements Serializable, Comparable { * * @param timestamp Date and time */ - public void setTimestamp(Date timestamp) { + public Host setTimestamp(Date timestamp) { this.timestamp = timestamp; + return this; } public Local getVolume() { return volume; } - public void setVolume(final Local volume) { + public Host setVolume(final Local volume) { this.volume = volume; + return this; } public Boolean getReadonly() { return readonly; } - public void setReadonly(final Boolean readonly) { + public Host setReadonly(final Boolean readonly) { this.readonly = readonly; + return this; } public Map getCustom() { return null == custom ? Collections.emptyMap() : custom; } - public void setCustom(final Map custom) { + public Host setCustom(final Map custom) { this.custom = custom; + return this; } public Set getLabels() { return null == labels ? Collections.emptySet() : labels; } - public void setLabels(final Set labels) { + public Host setLabels(final Set labels) { this.labels = labels; + return this; } @Override diff --git a/core/src/main/java/ch/cyberduck/core/HostPasswordStore.java b/core/src/main/java/ch/cyberduck/core/HostPasswordStore.java index 7c1b082e60..347bf9ca5b 100644 --- a/core/src/main/java/ch/cyberduck/core/HostPasswordStore.java +++ b/core/src/main/java/ch/cyberduck/core/HostPasswordStore.java @@ -15,6 +15,7 @@ package ch.cyberduck.core; * GNU General Public License for more details. */ +import ch.cyberduck.core.exception.AccessDeniedException; import ch.cyberduck.core.exception.LocalAccessDeniedException; public interface HostPasswordStore extends PasswordStore { @@ -33,7 +34,7 @@ public interface HostPasswordStore extends PasswordStore { * @throws LocalAccessDeniedException Failure accessing store * @see ch.cyberduck.core.Host#getCredentials() */ - void save(Host bookmark) throws LocalAccessDeniedException; + void save(Host bookmark) throws AccessDeniedException; /** * Delete password in login keychain if any @@ -41,5 +42,5 @@ public interface HostPasswordStore extends PasswordStore { * @param bookmark Hostname * @throws LocalAccessDeniedException Failure accessing store */ - void delete(Host bookmark) throws LocalAccessDeniedException; + void delete(Host bookmark) throws AccessDeniedException; } diff --git a/core/src/main/java/ch/cyberduck/core/KeychainLoginService.java b/core/src/main/java/ch/cyberduck/core/KeychainLoginService.java index d97698e4d4..874472cc53 100644 --- a/core/src/main/java/ch/cyberduck/core/KeychainLoginService.java +++ b/core/src/main/java/ch/cyberduck/core/KeychainLoginService.java @@ -17,12 +17,12 @@ package ch.cyberduck.core; * Bug fixes, suggestions and comments should be sent to feedback@cyberduck.ch */ +import ch.cyberduck.core.exception.AccessDeniedException; import ch.cyberduck.core.exception.BackgroundException; import ch.cyberduck.core.exception.ConnectionCanceledException; -import ch.cyberduck.core.exception.LocalAccessDeniedException; import ch.cyberduck.core.exception.LoginCanceledException; import ch.cyberduck.core.exception.LoginFailureException; -import ch.cyberduck.core.proxy.ProxyFinder; +import ch.cyberduck.core.ssl.X509KeyManager; import ch.cyberduck.core.threading.CancelCallback; import org.apache.commons.lang3.StringUtils; @@ -36,18 +36,18 @@ public class KeychainLoginService implements LoginService { private final HostPasswordStore keychain; - public KeychainLoginService() { - this(PasswordStoreFactory.get()); - } - public KeychainLoginService(final HostPasswordStore keychain) { this.keychain = keychain; } @Override - public void validate(final Host bookmark, final LoginCallback prompt, final LoginOptions options) throws ConnectionCanceledException, LoginFailureException { - log.debug("Validate login credentials for {}", bookmark); - final Credentials credentials = bookmark.getCredentials(); + public void validate(final Host host, final X509KeyManager keys, final LoginCallback prompt, final LoginOptions options) throws ConnectionCanceledException, LoginFailureException { + log.debug("Validate login credentials for {}", host); + final Host jumphost = host.getJumphost(); + if(null != jumphost) { + this.validate(jumphost, keys, prompt, new LoginOptions(jumphost.getProtocol())); + } + final Credentials credentials = host.getCredentials(); if(credentials.isPublicKeyAuthentication()) { if(!credentials.getIdentity().attributes().getPermission().isReadable()) { log.warn("Prompt to select identity file not readable {}", credentials.getIdentity()); @@ -55,65 +55,78 @@ public class KeychainLoginService implements LoginService { } } if(options.keychain) { + log.debug("Lookup credentials in keychain for {}", host); if(options.password) { if(StringUtils.isBlank(credentials.getPassword())) { - final String password = keychain.findLoginPassword(bookmark); + final String password = keychain.findLoginPassword(host); if(StringUtils.isNotBlank(password)) { - log.info("Fetched password from keychain for {}", bookmark); + log.info("Fetched password from keychain for {}", host); // No need to reinsert found password to the keychain. - credentials.setSaved(false); - credentials.setPassword(password); + credentials.setPassword(password).setSaved(false); } } } if(options.token) { if(StringUtils.isBlank(credentials.getToken())) { - final String token = keychain.findLoginToken(bookmark); + final String token = keychain.findLoginToken(host); if(StringUtils.isNotBlank(token)) { - log.info("Fetched token from keychain for {}", bookmark); + log.info("Fetched token from keychain for {}", host); // No need to reinsert found token to the keychain. - credentials.setSaved(false); - credentials.setToken(token); + credentials.setToken(token).setSaved(false); } } } if(options.publickey) { - final String passphrase = keychain.findPrivateKeyPassphrase(bookmark); + final String passphrase = keychain.findPrivateKeyPassphrase(host); if(StringUtils.isNotBlank(passphrase)) { - log.info("Fetched private key passphrase from keychain for {}", bookmark); + log.info("Fetched private key passphrase from keychain for {}", host); // No need to reinsert found token to the keychain. - credentials.setSaved(false); - credentials.setIdentityPassphrase(passphrase); + credentials.setIdentityPassphrase(passphrase).setSaved(false); } } if(options.oauth) { - final OAuthTokens tokens = keychain.findOAuthTokens(bookmark); + final OAuthTokens tokens = keychain.findOAuthTokens(host); if(tokens.validate()) { - log.info("Fetched OAuth token from keychain for {}", bookmark); + log.info("Fetched OAuth tokens {} from keychain for {}", tokens, host); // No need to reinsert found token to the keychain. - credentials.setSaved(tokens.isExpired()); - credentials.setOauth(tokens); + credentials.setOauth(tokens).setSaved(tokens.isExpired()); + } + } + if(options.certificate) { + final String alias = host.getCredentials().getCertificate(); + if(StringUtils.isNotBlank(alias)) { + if(keys != null) { + if(null == keys.getPrivateKey(alias)) { + log.warn("No private key found for alias {} in keychain", alias); + throw new LoginFailureException(LocaleFactory.localizedString("Provide additional login credentials", "Credentials")); + } + } } } } - if(!credentials.validate(bookmark.getProtocol(), options)) { - final CredentialsConfigurator configurator = bookmark.getProtocol().getFeature(CredentialsConfigurator.class); + if(!credentials.validate(host.getProtocol(), options)) { + log.warn("Failed validation of credentials {} with options {}", credentials, options); + final CredentialsConfigurator configurator = CredentialsConfiguratorFactory.get(host.getProtocol()); log.debug("Auto configure credentials with {}", configurator); - bookmark.setCredentials(configurator.configure(bookmark)); - } - if(!credentials.validate(bookmark.getProtocol(), options)) { + final Credentials configuration = configurator.configure(host); + if(configuration.validate(host.getProtocol(), options)) { + host.setCredentials(configuration); + log.info("Auto configured credentials {} for {}", configuration, host); + return; + } final StringAppender message = new StringAppender(); if(options.password) { message.append(MessageFormat.format(LocaleFactory.localizedString( - "Login {0} with username and password", "Credentials"), BookmarkNameProvider.toString(bookmark))); + "Login {0} with username and password", "Credentials"), BookmarkNameProvider.toString(host))); } if(options.publickey) { message.append(LocaleFactory.localizedString( "Select the private key in PEM or PuTTY format", "Credentials")); } message.append(LocaleFactory.localizedString("No login credentials could be found in the Keychain", "Credentials")); - this.prompt(bookmark, message.toString(), prompt, options); + this.prompt(host, message.toString(), prompt, options); } + log.debug("Validated credentials {} with options {}", credentials, options); } /** @@ -157,7 +170,7 @@ public class KeychainLoginService implements LoginService { } @Override - public boolean authenticate(final ProxyFinder proxy, final Session session, final ProgressListener listener, + public boolean authenticate(final Session session, final ProgressListener listener, final LoginCallback prompt, final CancelCallback cancel) throws BackgroundException { final Host bookmark = session.getHost(); final Credentials credentials = bookmark.getCredentials(); @@ -187,7 +200,7 @@ public class KeychainLoginService implements LoginService { c.initCause(e); } catch(IllegalArgumentException | IllegalStateException r) { - log.warn("Ignore error {} initializing faiulre {} with cause {}", r, e, c); + log.warn("Ignore error {} initializing failure {} with cause {}", r, e, c); } throw c; } @@ -204,22 +217,19 @@ public class KeychainLoginService implements LoginService { public void save(final Host bookmark) { final Credentials credentials = bookmark.getCredentials(); if(credentials.isSaved()) { - // Write credentials to keychain + // Write credentials to the password store try { keychain.save(bookmark); + if(bookmark.getJumphost() != null) { + keychain.save(bookmark.getJumphost()); + } } - catch(LocalAccessDeniedException e) { + catch(AccessDeniedException e) { log.error("Failure saving credentials for {} in keychain. {}", bookmark, e); } } else { log.info("Skip writing credentials for bookmark {}", bookmark.getHostname()); } - // Nullify password and tokens - log.debug("Reset credentials for {}", bookmark); - switch(bookmark.getProtocol().getStatefulness()) { - case stateless: - credentials.reset(); - } } } diff --git a/core/src/main/java/ch/cyberduck/core/ListProgressListener.java b/core/src/main/java/ch/cyberduck/core/ListProgressListener.java index 5cb8cc77a5..cc78009a74 100644 --- a/core/src/main/java/ch/cyberduck/core/ListProgressListener.java +++ b/core/src/main/java/ch/cyberduck/core/ListProgressListener.java @@ -43,6 +43,11 @@ public interface ListProgressListener extends ProgressListener { // } + @Override + default void message(String message) { + // + } + /** * Reset listener status for reuse * diff --git a/core/src/main/java/ch/cyberduck/core/ListService.java b/core/src/main/java/ch/cyberduck/core/ListService.java index ec33fe3236..e6939cf65c 100644 --- a/core/src/main/java/ch/cyberduck/core/ListService.java +++ b/core/src/main/java/ch/cyberduck/core/ListService.java @@ -17,12 +17,9 @@ package ch.cyberduck.core; * Bug fixes, suggestions and comments should be sent to feedback@cyberduck.ch */ -import ch.cyberduck.core.exception.AccessDeniedException; import ch.cyberduck.core.exception.BackgroundException; import ch.cyberduck.core.features.Required; -import java.text.MessageFormat; - @Required public interface ListService { default AttributedList list(Path directory) throws BackgroundException { @@ -32,9 +29,6 @@ public interface ListService { AttributedList list(Path directory, ListProgressListener listener) throws BackgroundException; default void preflight(final Path directory) throws BackgroundException { - if(!directory.attributes().getPermission().isExecutable() || !directory.attributes().getPermission().isReadable()) { - throw new AccessDeniedException(MessageFormat.format(LocaleFactory.localizedString("Listing directory {0} failed", "Error"), - directory.getName())).withFile(directory); - } + // } } diff --git a/core/src/main/java/ch/cyberduck/core/Local.java b/core/src/main/java/ch/cyberduck/core/Local.java index ea81711c8a..a5c6acc74d 100644 --- a/core/src/main/java/ch/cyberduck/core/Local.java +++ b/core/src/main/java/ch/cyberduck/core/Local.java @@ -230,6 +230,10 @@ public class Local extends AbstractPath implements Referenceable, Serializable { } } + public AttributedList list() throws AccessDeniedException { + return this.list(new NullFilter<>()); + } + public AttributedList list(final Filter filter) throws AccessDeniedException { return this.list(path, filter); } @@ -261,10 +265,6 @@ public class Local extends AbstractPath implements Referenceable, Serializable { return children; } - public AttributedList list() throws AccessDeniedException { - return this.list(new NullFilter<>()); - } - @Override public String getAbsolute() { return path; diff --git a/core/src/main/java/ch/cyberduck/core/LocalAttributes.java b/core/src/main/java/ch/cyberduck/core/LocalAttributes.java index 7d6d8ee710..8e955b264d 100644 --- a/core/src/main/java/ch/cyberduck/core/LocalAttributes.java +++ b/core/src/main/java/ch/cyberduck/core/LocalAttributes.java @@ -27,13 +27,15 @@ import java.io.IOException; import java.nio.file.FileSystems; import java.nio.file.Files; import java.nio.file.LinkOption; +import java.nio.file.Path; import java.nio.file.Paths; +import java.nio.file.attribute.BasicFileAttributes; import java.nio.file.attribute.FileTime; import java.nio.file.attribute.PosixFileAttributes; import java.nio.file.attribute.PosixFilePermissions; import java.util.Objects; -public class LocalAttributes extends Attributes { +public class LocalAttributes implements Attributes { private static final Logger log = LogManager.getLogger(LocalAttributes.class); private final String path; @@ -42,13 +44,27 @@ public class LocalAttributes extends Attributes { this.path = path; } + private static BasicFileAttributes readAttributes(final String path) throws IOException { + return readAttributes(path, BasicFileAttributes.class); + } + + private static T readAttributes(final String path, final Class type) throws IOException { + try { + return Files.readAttributes(Paths.get(path), type, LinkOption.NOFOLLOW_LINKS); + } + catch(UnsupportedOperationException e) { + log.warn("Failure {} retrieving attributes of {}", e, path); + throw new IOException(e); + } + } + @Override public long getModificationDate() { try { - return Files.getLastModifiedTime(Paths.get(path)).toMillis(); + return readAttributes(path).lastModifiedTime().toMillis(); } catch(IOException e) { - log.warn("Failure getting timestamp of {}. {}", path, e.getMessage()); + log.warn("Failure {} getting timestamp of {}", e, path); return -1L; } } @@ -58,7 +74,13 @@ public class LocalAttributes extends Attributes { */ @Override public long getCreationDate() { - return this.getModificationDate(); + try { + return readAttributes(path).creationTime().toMillis(); + } + catch(IOException e) { + log.warn("Failure {} getting timestamp of {}", e, path); + return -1L; + } } /** @@ -66,7 +88,13 @@ public class LocalAttributes extends Attributes { */ @Override public long getAccessedDate() { - return this.getModificationDate(); + try { + return readAttributes(path).lastAccessTime().toMillis(); + } + catch(IOException e) { + log.warn("Failure {} getting timestamp of {}", e, path); + return -1L; + } } public void setModificationDate(final long timestamp) throws AccessDeniedException { @@ -96,13 +124,13 @@ public class LocalAttributes extends Attributes { public Permission getPermission() { if(FileSystems.getDefault().supportedFileAttributeViews().contains("posix")) { try { - return new LocalPermission(PosixFilePermissions.toString(Files.readAttributes(Paths.get(path), PosixFileAttributes.class, LinkOption.NOFOLLOW_LINKS).permissions())); + return new LocalPermission(PosixFilePermissions.toString(readAttributes(path, PosixFileAttributes.class).permissions())); } catch(IOException e) { return Permission.EMPTY; } } - return Permission.EMPTY; + return new LocalPermission(); } public void setPermission(final Permission permission) throws AccessDeniedException { @@ -125,17 +153,48 @@ public class LocalAttributes extends Attributes { @Override public String getOwner() { + if(FileSystems.getDefault().supportedFileAttributeViews().contains("posix")) { + try { + return readAttributes(path, PosixFileAttributes.class).owner().getName(); + } + catch(IOException e) { + return null; + } + } return null; } @Override public String getGroup() { + if(FileSystems.getDefault().supportedFileAttributeViews().contains("posix")) { + try { + return readAttributes(path, PosixFileAttributes.class).group().getName(); + } + catch(IOException e) { + return null; + } + } return null; } + private static Permission.Action toAction(final String path) { + Permission.Action actions = Permission.Action.none; + final Path p = Paths.get(path); + if(Files.isReadable(p)) { + actions = actions.or(Permission.Action.read); + } + if(Files.isWritable(p)) { + actions = actions.or(Permission.Action.write); + } + if(Files.isExecutable(p)) { + actions = actions.or(Permission.Action.execute); + } + return actions; + } + protected class LocalPermission extends Permission { public LocalPermission() { - // + super(toAction(path), Action.none, Action.none); } public LocalPermission(final String mode) { diff --git a/core/src/main/java/ch/cyberduck/core/LoggingTranscriptListener.java b/core/src/main/java/ch/cyberduck/core/LoggingTranscriptListener.java index fced2aa81d..5c82250258 100644 --- a/core/src/main/java/ch/cyberduck/core/LoggingTranscriptListener.java +++ b/core/src/main/java/ch/cyberduck/core/LoggingTranscriptListener.java @@ -19,27 +19,18 @@ import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; public class LoggingTranscriptListener implements TranscriptListener { - - private final Logger request; - private final Logger response; - - public LoggingTranscriptListener() { - this(LogManager.getLogger("ch.cyberduck.transcript.request"), LogManager.getLogger("ch.cyberduck.transcript.response")); - } - - public LoggingTranscriptListener(final Logger request, final Logger response) { - this.request = request; - this.response = response; - } + private static final Logger log = LogManager.getLogger("transcript"); + private static final Logger audit = LogManager.getLogger("audit.transcript"); @Override public void log(final Type type, final String message) { switch(type) { - case request: - request.info(message); + case requestheader: + case responseheader: + log.debug(message); break; - case response: - response.info(message); + default: + audit.info(message); break; } } diff --git a/core/src/main/java/ch/cyberduck/core/LoginConnectionService.java b/core/src/main/java/ch/cyberduck/core/LoginConnectionService.java index c97f30c510..b710595995 100644 --- a/core/src/main/java/ch/cyberduck/core/LoginConnectionService.java +++ b/core/src/main/java/ch/cyberduck/core/LoginConnectionService.java @@ -24,6 +24,7 @@ import ch.cyberduck.core.proxy.Proxy; import ch.cyberduck.core.proxy.ProxyFactory; import ch.cyberduck.core.proxy.ProxyFinder; import ch.cyberduck.core.proxy.ProxyHostUrlProvider; +import ch.cyberduck.core.ssl.X509KeyManager; import ch.cyberduck.core.threading.CancelCallback; import org.apache.commons.lang3.StringUtils; @@ -88,12 +89,17 @@ public class LoginConnectionService implements ConnectionService { } if(session.isConnected()) { log.debug("Skip opening connection for session {}", session); - // Connection already open + // Connection is already open return false; } - // Obtain password from keychain or prompt + final Host jumphost = JumpHostConfiguratorFactory.get(bookmark.getProtocol()).getJumphost(bookmark.getHostname()); + if(null != jumphost) { + log.debug("Configure with jump host {}", jumphost); + bookmark.setJumphost(jumphost); + } + // Get password from the password store or prompt synchronized(login) { - login.validate(bookmark, prompt, new LoginOptions(bookmark.getProtocol())); + login.validate(bookmark, session.getFeature(X509KeyManager.class), prompt, new LoginOptions(bookmark.getProtocol())); } this.connect(session, callback); return true; @@ -102,7 +108,7 @@ public class LoginConnectionService implements ConnectionService { @Override public void close(final Session session) throws BackgroundException { listener.message(MessageFormat.format(LocaleFactory.localizedString("Disconnecting {0}", "Status"), - session.getHost().getHostname())); + session.getHost().getHostname())); // Close the underlying socket first session.interrupt(); } @@ -130,28 +136,28 @@ public class LoginConnectionService implements ConnectionService { } } listener.message(MessageFormat.format(LocaleFactory.localizedString("Opening {0} connection to {1}", "Status"), - bookmark.getProtocol().getName(), hostname)); + bookmark.getProtocol().getName(), hostname)); // The IP address could successfully be determined session.open(proxy, key, prompt, cancel); listener.message(MessageFormat.format(LocaleFactory.localizedString("{0} connection opened", "Status"), - bookmark.getProtocol().getName())); + bookmark.getProtocol().getName())); // Update last accessed timestamp bookmark.setTimestamp(new Date()); // Warning about insecure connection prior authenticating if(session.alert(prompt)) { // Warning if credentials are sent plaintext. prompt.warn(bookmark, MessageFormat.format(LocaleFactory.localizedString("Unsecured {0} connection", "Credentials"), - bookmark.getProtocol().getName()), - MessageFormat.format("{0} {1}.", MessageFormat.format(LocaleFactory.localizedString("{0} will be sent in plaintext.", "Credentials"), - bookmark.getProtocol().getPasswordPlaceholder()), - LocaleFactory.localizedString("Please contact your web hosting service provider for assistance", "Support")), - LocaleFactory.localizedString("Continue", "Credentials"), - LocaleFactory.localizedString("Disconnect", "Credentials"), - String.format("connection.unsecure.%s", bookmark.getHostname())); + bookmark.getProtocol().getName()), + MessageFormat.format("{0} {1}.", MessageFormat.format(LocaleFactory.localizedString("{0} will be sent in plaintext.", "Credentials"), + bookmark.getProtocol().getPasswordPlaceholder()), + LocaleFactory.localizedString("Please contact your web hosting service provider for assistance", "Support")), + LocaleFactory.localizedString("Continue", "Credentials"), + LocaleFactory.localizedString("Disconnect", "Credentials"), + String.format("connection.unsecure.%s", bookmark.getHostname())); } // Login try { - this.authenticate(proxy, session, cancel); + this.authenticate(session, cancel); } catch(BackgroundException e) { this.close(session); @@ -159,11 +165,11 @@ public class LoginConnectionService implements ConnectionService { } } - private void authenticate(final ProxyFinder proxy, final Session session, final CancelCallback callback) throws BackgroundException { - if(!login.authenticate(proxy, session, listener, prompt, callback)) { + private void authenticate(final Session session, final CancelCallback callback) throws BackgroundException { + if(!login.authenticate(session, listener, prompt, callback)) { if(session.isConnected()) { - // Next attempt with updated credentials but cancel when prompt is dismissed - this.authenticate(proxy, session, callback); + // Next attempt with updated credentials but cancel when the prompt is dismissed + this.authenticate(session, callback); } else { // Reconnect and next attempt with updated credentials diff --git a/core/src/main/java/ch/cyberduck/core/LoginService.java b/core/src/main/java/ch/cyberduck/core/LoginService.java index aa3b9d50f6..e74b772b75 100644 --- a/core/src/main/java/ch/cyberduck/core/LoginService.java +++ b/core/src/main/java/ch/cyberduck/core/LoginService.java @@ -21,30 +21,29 @@ import ch.cyberduck.core.exception.BackgroundException; import ch.cyberduck.core.exception.ConnectionCanceledException; import ch.cyberduck.core.exception.LoginCanceledException; import ch.cyberduck.core.exception.LoginFailureException; -import ch.cyberduck.core.proxy.ProxyFinder; +import ch.cyberduck.core.ssl.X509KeyManager; import ch.cyberduck.core.threading.CancelCallback; public interface LoginService { /** - * Obtain password from keychain or prompt panel + * Obtain password from password store or prompt user for input * * @param bookmark Credentials - * @param pompt Login prompt - * @param options Login mechanism features + * @param prompt Login prompt + * @param options Login mechanism features */ - void validate(Host bookmark, LoginCallback pompt, LoginOptions options) throws ConnectionCanceledException, LoginFailureException; + void validate(Host bookmark, X509KeyManager keys, LoginCallback prompt, LoginOptions options) throws ConnectionCanceledException, LoginFailureException; /** * Login and prompt on failure * - * @param proxy Proxy configuration * @param session Session * @param listener Authentication message callback - * @param prompt Login prompt + * @param prompt Login prompt * @param cancel Cancel callback while authentication is in progress * @return False if authentication fails * @throws LoginCanceledException Login prompt canceled by user * @throws LoginFailureException Login attempt failed */ - boolean authenticate(ProxyFinder proxy, Session session, ProgressListener listener, LoginCallback prompt, CancelCallback cancel) throws BackgroundException; + boolean authenticate(Session session, ProgressListener listener, LoginCallback prompt, CancelCallback cancel) throws BackgroundException; } diff --git a/core/src/main/java/ch/cyberduck/core/OAuthTokens.java b/core/src/main/java/ch/cyberduck/core/OAuthTokens.java index 21687df8f1..f3f8bdfd5e 100644 --- a/core/src/main/java/ch/cyberduck/core/OAuthTokens.java +++ b/core/src/main/java/ch/cyberduck/core/OAuthTokens.java @@ -20,7 +20,7 @@ import org.apache.commons.lang3.StringUtils; import java.util.Objects; public final class OAuthTokens { - public static final OAuthTokens EMPTY = new OAuthTokens(null, null, Long.MAX_VALUE, null); + public static final OAuthTokens EMPTY = new OAuthTokens(null, null, -1L, null); private final String accessToken; private final String refreshToken; diff --git a/core/src/main/java/ch/cyberduck/core/PasswordStore.java b/core/src/main/java/ch/cyberduck/core/PasswordStore.java index 09be67e2c6..9030aa4713 100644 --- a/core/src/main/java/ch/cyberduck/core/PasswordStore.java +++ b/core/src/main/java/ch/cyberduck/core/PasswordStore.java @@ -17,6 +17,7 @@ package ch.cyberduck.core; * Bug fixes, suggestions and comments should be sent to feedback@cyberduck.ch */ +import ch.cyberduck.core.exception.AccessDeniedException; import ch.cyberduck.core.exception.LocalAccessDeniedException; public interface PasswordStore { @@ -28,7 +29,7 @@ public interface PasswordStore { * @param accountName Account * @return Password if found or null otherwise */ - String getPassword(String serviceName, String accountName) throws LocalAccessDeniedException; + String getPassword(String serviceName, String accountName) throws AccessDeniedException; /** * Add generic password for application @@ -38,7 +39,7 @@ public interface PasswordStore { * @param password Password to save for service * @throws LocalAccessDeniedException Failure accessing keychain */ - void addPassword(String serviceName, String accountName, String password) throws LocalAccessDeniedException; + void addPassword(String serviceName, String accountName, String password) throws AccessDeniedException; /** * Find internet password @@ -48,7 +49,7 @@ public interface PasswordStore { * @param hostname Hostname * @param user Credentials @return Password if found or null otherwise */ - String getPassword(Scheme scheme, int port, String hostname, String user) throws LocalAccessDeniedException; + String getPassword(Scheme scheme, int port, String hostname, String user) throws AccessDeniedException; /** * Save internet password @@ -60,14 +61,14 @@ public interface PasswordStore { * @param password Password to save for service * @throws LocalAccessDeniedException Failure accessing keychain */ - void addPassword(Scheme scheme, int port, String hostname, String user, String password) throws LocalAccessDeniedException; + void addPassword(Scheme scheme, int port, String hostname, String user, String password) throws AccessDeniedException; /** * @param serviceName Service * @param user Credentials * @throws LocalAccessDeniedException Failure accessing keychain */ - void deletePassword(String serviceName, String user) throws LocalAccessDeniedException; + void deletePassword(String serviceName, String user) throws AccessDeniedException; /** * @param scheme Protocol scheme @@ -76,5 +77,5 @@ public interface PasswordStore { * @param user Credentials * @throws LocalAccessDeniedException Failure accessing keychain */ - void deletePassword(Scheme scheme, int port, String hostname, String user) throws LocalAccessDeniedException; + void deletePassword(Scheme scheme, int port, String hostname, String user) throws AccessDeniedException; } diff --git a/core/src/main/java/ch/cyberduck/core/Path.java b/core/src/main/java/ch/cyberduck/core/Path.java index b7ffe103ed..435a326137 100644 --- a/core/src/main/java/ch/cyberduck/core/Path.java +++ b/core/src/main/java/ch/cyberduck/core/Path.java @@ -60,7 +60,7 @@ public class Path extends AbstractPath implements Referenceable, Serializable { this.path = copy.path; this.symlink = null == copy.symlink ? null : new Path(copy.symlink); this.type = EnumSet.copyOf(copy.type); - this.attributes = new PathAttributes(copy.attributes); + this.attributes = new DefaultPathAttributes(copy.attributes); this.alias = copy.alias; } @@ -71,7 +71,7 @@ public class Path extends AbstractPath implements Referenceable, Serializable { */ public Path(final Path parent, final String name, final EnumSet type) { this.type = type; - this.attributes = new PathAttributes(); + this.attributes = new DefaultPathAttributes(); this.attributes.setRegion(parent.attributes.getRegion()); this._setPath(parent, name); } @@ -82,7 +82,7 @@ public class Path extends AbstractPath implements Referenceable, Serializable { */ public Path(final String absolute, final EnumSet type) { this.type = type; - this.attributes = new PathAttributes(); + this.attributes = new DefaultPathAttributes(); this.setPath(absolute); } diff --git a/core/src/main/java/ch/cyberduck/core/PathAttributes.java b/core/src/main/java/ch/cyberduck/core/PathAttributes.java index 95e6f43465..b2e5eb5f30 100644 --- a/core/src/main/java/ch/cyberduck/core/PathAttributes.java +++ b/core/src/main/java/ch/cyberduck/core/PathAttributes.java @@ -22,700 +22,14 @@ import ch.cyberduck.core.features.Encryption; import ch.cyberduck.core.features.Quota; import ch.cyberduck.core.io.Checksum; import ch.cyberduck.core.serializer.Serializer; -import ch.cyberduck.core.transfer.TransferStatus; -import org.apache.commons.lang3.StringUtils; -import org.apache.logging.log4j.LogManager; -import org.apache.logging.log4j.Logger; - -import java.util.Collections; -import java.util.HashMap; import java.util.Map; -import java.util.Objects; /** - * Attributes of a remote directory or file. + * Interface for path attributes operations. */ -public class PathAttributes extends Attributes implements Serializable { - private static final Logger log = LogManager.getLogger(PathAttributes.class); - - /** - * The file length - */ - private long size = TransferStatus.UNKNOWN_LENGTH; - - /** - * Quota of folder - */ - private Quota.Space quota = Quota.unknown; - - /** - * The file modification date in milliseconds - */ - private long modified = -1; - /** - * Last accessed timestamp in milliseconds - */ - private long accessed = -1; - /** - * When this file was originally created in milliseconds - */ - private long created = -1; - - private String owner; - private String group; - - private Permission permission = Permission.EMPTY; - - private Acl acl = Acl.EMPTY; - - /** - * MD5 checksum - */ - private Checksum checksum = Checksum.NONE; - - /** - * ETag header in HTTP - */ - private String etag; - - /** - * Redundancy level if available - */ - private String storageClass; - - /** - * Server side encryption (SSE) algorithm and key or null - */ - private Encryption.Algorithm encryption = Encryption.Algorithm.NONE; - - /** - * Unique identifier for a given file. Must remain constant even after updating the file. - */ - private String fileId; - - /** - * Unique identifier for a given version of a file - */ - private String versionId; - - /** - * Lock id - */ - private String lockId; - - /** - * Should be hidden in the browser by default - */ - private Boolean duplicate; - - /** - * Hidden flag set on server - */ - private Boolean hidden; - - /** - * Trashed - */ - private Boolean trashed; - - /** - * Revision number - */ - private Long revision; - - /** - * Geographical location - */ - private String region; - - /** - * - */ - private String displayname; - - private DescriptiveUrl link = DescriptiveUrl.EMPTY; - - /** - * HTTP headers - */ - private Map metadata = Collections.emptyMap(); - - /** - * Cryptomator vault - */ - private Path vault; - /** - * Cryptomator decrypted path - */ - private Path decrypted; - /** - * Cryptomator encrypted path. - */ - private Path encrypted; - /** - * Unique identifier for cryptomator - */ - private String directoryId; - - private Map custom = Collections.emptyMap(); - - private Verdict verdict; - - public enum Verdict { - pending, - clean, - malicious - } - - public PathAttributes() { - } - - public PathAttributes(final PathAttributes copy) { - size = copy.size; - quota = copy.quota; - modified = copy.modified; - accessed = copy.accessed; - created = copy.created; - owner = copy.owner; - group = copy.group; - permission = Permission.EMPTY == copy.permission ? Permission.EMPTY : new Permission(copy.permission); - acl = Acl.EMPTY == copy.acl ? Acl.EMPTY : new Acl(copy.acl); - checksum = Checksum.NONE == copy.checksum ? Checksum.NONE : new Checksum(copy.checksum); - etag = copy.etag; - storageClass = copy.storageClass; - encryption = copy.encryption; - fileId = copy.fileId; - versionId = copy.versionId; - lockId = copy.lockId; - duplicate = copy.duplicate; - hidden = copy.hidden; - trashed = copy.trashed; - revision = copy.revision; - region = copy.region; - displayname = copy.displayname; - link = DescriptiveUrl.EMPTY == copy.link ? DescriptiveUrl.EMPTY : new DescriptiveUrl(copy.link); - metadata = new HashMap<>(copy.metadata); - custom = new HashMap<>(copy.custom); - verdict = copy.verdict; - vault = copy.vault; - decrypted = copy.decrypted; - encrypted = copy.encrypted; - directoryId = copy.directoryId; - } - - @Override - public T serialize(final Serializer dict) { - if(size != -1) { - dict.setStringForKey(String.valueOf(size), "Size"); - } - if(quota != Quota.unknown) { - // Set remaining quota - dict.setStringForKey(String.valueOf(quota.available), "Quota"); - } - if(modified != -1) { - dict.setStringForKey(String.valueOf(modified), "Modified"); - } - if(created != -1) { - dict.setStringForKey(String.valueOf(created), "Created"); - } - if(revision != null) { - dict.setStringForKey(String.valueOf(revision), "Revision"); - } - if(etag != null) { - dict.setStringForKey(etag, "ETag"); - } - if(permission != Permission.EMPTY) { - dict.setObjectForKey(permission, "Permission"); - } - if(owner != null) { - dict.setStringForKey(owner, "Owner"); - } - if(group != null) { - dict.setStringForKey(group, "Group"); - } - if(acl != Acl.EMPTY) { - dict.setObjectForKey(acl, "Acl"); - } - if(link != DescriptiveUrl.EMPTY) { - final Map wrapper = new HashMap<>(); - wrapper.put("Url", link.getUrl()); - wrapper.put("Type", link.getType().name()); - dict.setMapForKey(wrapper, "Link"); - } - if(checksum != Checksum.NONE) { - final Map wrapper = new HashMap<>(); - wrapper.put("Algorithm", checksum.algorithm.name()); - wrapper.put("Hash", checksum.hash); - if(null != checksum.base64) { - wrapper.put("Base64", checksum.base64); - } - dict.setMapForKey(wrapper, "Checksum"); - } - if(StringUtils.isNotBlank(versionId)) { - dict.setStringForKey(versionId, "Version"); - } - if(StringUtils.isNotBlank(fileId)) { - dict.setStringForKey(fileId, "File Id"); - } - if(StringUtils.isNotBlank(displayname)) { - dict.setStringForKey(displayname, "Display Name"); - } - if(StringUtils.isNotBlank(lockId)) { - dict.setStringForKey(lockId, "Lock Id"); - } - if(duplicate != null) { - dict.setStringForKey(String.valueOf(duplicate), "Duplicate"); - } - if(hidden != null) { - dict.setStringForKey(String.valueOf(hidden), "Hidden"); - } - if(trashed != null) { - dict.setStringForKey(String.valueOf(trashed), "Trashed"); - } - if(StringUtils.isNotBlank(region)) { - dict.setStringForKey(region, "Region"); - } - if(StringUtils.isNotBlank(storageClass)) { - dict.setStringForKey(storageClass, "Storage Class"); - } - if(vault != null) { - if(vault.attributes() == this) { - log.debug("Skip serializing vault attribute {} to avoid recursion", vault); - } - else { - dict.setObjectForKey(vault, "Vault"); - } - } - if(!custom.isEmpty()) { - dict.setMapForKey(custom, "Custom"); - } - if(verdict != null) { - dict.setStringForKey(verdict.name(), "Verdict"); - } - return dict.getSerialized(); - } - - /** - * @return length the size of file in bytes. - */ - @Override - public long getSize() { - return size; - } - - /** - * @param size the size of file in bytes. - */ - public PathAttributes setSize(final long size) { - this.size = size; - return this; - } - - public Quota.Space getQuota() { - return quota; - } - - public PathAttributes setQuota(final Quota.Space quota) { - this.quota = quota; - return this; - } - - @Override - public long getModificationDate() { - return modified; - } - - public PathAttributes setModificationDate(final long millis) { - this.modified = millis; - return this; - } - - @Override - public long getCreationDate() { - return created; - } - - public PathAttributes setCreationDate(final long millis) { - this.created = millis; - return this; - } - - @Override - public long getAccessedDate() { - return accessed; - } - - public PathAttributes setAccessedDate(final long millis) { - this.accessed = millis; - return this; - } - - /** - * @return UNIX permissions - */ - @Override - public Permission getPermission() { - return permission; - } - - /** - * @param p UNIX permissions - */ - public PathAttributes setPermission(final Permission p) { - this.permission = p; - return this; - } - - public Acl getAcl() { - return acl; - } - - public PathAttributes setAcl(final Acl acl) { - this.acl = acl; - return this; - } - - @Override - public String getOwner() { - return owner; - } - - public PathAttributes setOwner(final String o) { - this.owner = o; - return this; - } - - @Override - public String getGroup() { - return group; - } - - public PathAttributes setGroup(final String g) { - this.group = g; - return this; - } - - public Checksum getChecksum() { - return checksum; - } - - public PathAttributes setChecksum(final Checksum checksum) { - this.checksum = checksum; - return this; - } - - public String getETag() { - return etag; - } - - public PathAttributes setETag(final String etag) { - this.etag = etag; - return this; - } - - /** - * @return Storage redundancy identifier. - */ - public String getStorageClass() { - return storageClass; - } - - /** - * @param storageClass Storage redundancy identifier. - */ - public PathAttributes setStorageClass(final String storageClass) { - this.storageClass = storageClass; - return this; - } - - public Encryption.Algorithm getEncryption() { - return encryption; - } - - public PathAttributes setEncryption(final Encryption.Algorithm encryption) { - this.encryption = encryption; - return this; - } - - /** - * A version identifying a particular revision of a file with the same path. - * - * @return Version Identifier or null if not versioned. - */ - public String getVersionId() { - return versionId; - } - - /** - * Set a unique version identifier for the revision of a file. - * - * @param versionId Revision - */ - public PathAttributes setVersionId(final String versionId) { - this.versionId = versionId; - return this; - } - - /** - * A unique identifier for a file with the same path. Remains constant over its lifetime. - * - * @return Identifier or null if there is no such concept - */ - public String getFileId() { - return fileId; - } - - public PathAttributes setFileId(final String fileId) { - this.fileId = fileId; - return this; - } - - public String getLockId() { - return lockId; - } - - public PathAttributes setLockId(final String lockId) { - this.lockId = lockId; - return this; - } - - public String getDirectoryId() { - return directoryId; - } - - public PathAttributes setDirectoryId(final String directoryId) { - this.directoryId = directoryId; - return this; - } - - /** - * @return The incrementing revision number of the file or null if not versioned. - */ - public Long getRevision() { - return revision; - } - - public PathAttributes setRevision(final Long revision) { - this.revision = revision; - return this; - } - - /** - * @return Null if path is missing flag encrypted - */ - public Path getDecrypted() { - return decrypted; - } - - public PathAttributes setDecrypted(final Path decrypted) { - this.decrypted = decrypted; - return this; - } - - /** - * @return Null if path is missing flag decrypted - */ - public Path getEncrypted() { - return encrypted; - } - - public PathAttributes setEncrypted(final Path encrypted) { - this.encrypted = encrypted; - return this; - } - - public PathAttributes setVault(final Path vault) { - this.vault = vault; - return this; - } - - public Path getVault() { - return vault; - } - - /** - * If the path should not be displayed in a browser by default unless the user explicitly chooses to show hidden - * files. - * - * @return True if hidden by default. - */ - public boolean isDuplicate() { - return duplicate != null && duplicate; - } - - /** - * Attribute to mark a file as hidden by default in addition to a filename convention. - * - * @param duplicate Flag - */ - public PathAttributes setDuplicate(final boolean duplicate) { - this.duplicate = duplicate; - return this; - } - - public Boolean isHidden() { - return hidden != null && hidden; - } - - public PathAttributes setHidden(final boolean hidden) { - this.hidden = hidden; - return this; - } - - public Boolean isTrashed() { - return trashed != null && trashed; - } - - public PathAttributes setTrashed(final boolean trashed) { - this.trashed = trashed; - return this; - } - - public Map getMetadata() { - return metadata; - } - - public PathAttributes setMetadata(final Map metadata) { - this.metadata = metadata; - return this; - } - - public String getRegion() { - return region; - } - - public PathAttributes setRegion(final String region) { - this.region = region; - return this; - } - - public String getDisplayname() { - return displayname; - } - - public PathAttributes setDisplayname(final String displayname) { - this.displayname = displayname; - return this; - } - - public DescriptiveUrl getLink() { - return link; - } - - public PathAttributes setLink(final DescriptiveUrl link) { - this.link = link; - return this; - } - - public Map getCustom() { - return custom; - } - - public PathAttributes setCustom(final Map custom) { - this.custom = custom; - return this; - } - - public PathAttributes setCustom(final String key, final String value) { - custom = new HashMap<>(custom); - custom.put(key, value); - return this; - } - - public Verdict getVerdict() { - return verdict; - } - - public PathAttributes setVerdict(final Verdict verdict) { - this.verdict = verdict; - return this; - } - - @Override - public boolean equals(final Object o) { - if(this == o) { - return true; - } - if(!(o instanceof PathAttributes)) { - return false; - } - final PathAttributes that = (PathAttributes) o; - if(modified != that.modified) { - return false; - } - if(size != that.size) { - return false; - } - if(!Objects.equals(checksum, that.checksum)) { - return false; - } - if(!Objects.equals(permission, that.permission)) { - return false; - } - if(!Objects.equals(acl, that.acl)) { - return false; - } - if(!Objects.equals(versionId, that.versionId)) { - return false; - } - if(!Objects.equals(fileId, that.fileId)) { - return false; - } - if(!Objects.equals(revision, that.revision)) { - return false; - } - if(!Objects.equals(verdict, that.verdict)) { - return false; - } - return true; - } - - @Override - public int hashCode() { - int result = (int) (size ^ (size >>> 32)); - result = 31 * result + (int) (modified ^ (modified >>> 32)); - result = 31 * result + (permission != null ? permission.hashCode() : 0); - result = 31 * result + (acl != null ? acl.hashCode() : 0); - result = 31 * result + (checksum != null ? checksum.hashCode() : 0); - result = 31 * result + (versionId != null ? versionId.hashCode() : 0); - result = 31 * result + (fileId != null ? fileId.hashCode() : 0); - result = 31 * result + (revision != null ? revision.hashCode() : 0); - result = 31 * result + (verdict != null ? verdict.hashCode() : 0); - return result; - } - - @Override - public String toString() { - final StringBuilder sb = new StringBuilder("PathAttributes{"); - sb.append("accessed=").append(accessed); - sb.append(", size=").append(size); - sb.append(", modified=").append(modified); - sb.append(", created=").append(created); - sb.append(", owner='").append(owner).append('\''); - sb.append(", group='").append(group).append('\''); - sb.append(", permission=").append(permission); - sb.append(", acl=").append(acl); - sb.append(", checksum='").append(checksum).append('\''); - sb.append(", etag='").append(etag).append('\''); - sb.append(", storageClass='").append(storageClass).append('\''); - sb.append(", encryption='").append(encryption).append('\''); - sb.append(", versionId='").append(versionId).append('\''); - sb.append(", fileId='").append(fileId).append('\''); - sb.append(", lockId='").append(lockId).append('\''); - sb.append(", duplicate=").append(duplicate); - sb.append(", hidden=").append(hidden); - sb.append(", trashed=").append(trashed); - sb.append(", revision=").append(revision); - sb.append(", region='").append(region).append('\''); - sb.append(", metadata=").append(metadata).append('\''); - sb.append(", custom=").append(custom).append('\''); - sb.append(", verdict=").append(verdict).append('\''); - sb.append('}'); - return sb.toString(); - } - - public static final PathAttributes EMPTY = new PathAttributes() { +public interface PathAttributes extends Attributes, Serializable { + PathAttributes EMPTY = new DefaultPathAttributes() { @Override public T serialize(final Serializer dict) { return super.serialize(dict); @@ -876,4 +190,168 @@ public class PathAttributes extends Attributes implements Serializable { return this; } }; + + /** + * @return length the size of file in bytes. + */ + /** + * @param size the size of file in bytes. + */ + PathAttributes setSize(long size); + + Quota.Space getQuota(); + + PathAttributes setQuota(Quota.Space quota); + + PathAttributes setModificationDate(long millis); + + PathAttributes setCreationDate(long millis); + + PathAttributes setAccessedDate(long millis); + + /** + * @param p UNIX permissions + */ + PathAttributes setPermission(Permission p); + + Acl getAcl(); + + PathAttributes setAcl(Acl acl); + + PathAttributes setOwner(String o); + + PathAttributes setGroup(String g); + + Checksum getChecksum(); + + PathAttributes setChecksum(Checksum checksum); + + String getETag(); + + PathAttributes setETag(String etag); + + /** + * @return Storage redundancy identifier. + */ + String getStorageClass(); + + /** + * @param storageClass Storage redundancy identifier. + */ + PathAttributes setStorageClass(String storageClass); + + Encryption.Algorithm getEncryption(); + + PathAttributes setEncryption(Encryption.Algorithm encryption); + + /** + * A version identifying a particular revision of a file with the same path. + * + * @return Version Identifier or null if not versioned. + */ + String getVersionId(); + + /** + * Set a unique version identifier for the revision of a file. + * + * @param versionId Revision + */ + PathAttributes setVersionId(String versionId); + + /** + * A unique identifier for a file with the same path. Remains constant over its lifetime. + * + * @return Identifier or null if there is no such concept + */ + String getFileId(); + + PathAttributes setFileId(String fileId); + + String getLockId(); + + PathAttributes setLockId(String lockId); + + String getDirectoryId(); + + PathAttributes setDirectoryId(String directoryId); + + /** + * @return The incrementing revision number of the file or null if not versioned. + */ + Long getRevision(); + + PathAttributes setRevision(Long revision); + + /** + * @return Null if path is missing flag encrypted + */ + Path getDecrypted(); + + PathAttributes setDecrypted(Path decrypted); + + /** + * @return Null if path is missing flag decrypted + */ + Path getEncrypted(); + + PathAttributes setEncrypted(Path encrypted); + + PathAttributes setVault(Path vault); + + Path getVault(); + + /** + * If the path should not be displayed in a browser by default unless the user explicitly chooses to show hidden + * files. + * + * @return True if hidden by default. + */ + boolean isDuplicate(); + + /** + * Attribute to mark a file as hidden by default in addition to a filename convention. + * + * @param duplicate Flag + */ + PathAttributes setDuplicate(boolean duplicate); + + Boolean isHidden(); + + PathAttributes setHidden(boolean hidden); + + Boolean isTrashed(); + + PathAttributes setTrashed(boolean trashed); + + Map getMetadata(); + + PathAttributes setMetadata(Map metadata); + + String getRegion(); + + PathAttributes setRegion(String region); + + String getDisplayname(); + + PathAttributes setDisplayname(String displayname); + + DescriptiveUrl getLink(); + + PathAttributes setLink(DescriptiveUrl link); + + Map getCustom(); + + PathAttributes setCustom(Map custom); + + PathAttributes setCustom(String key, String value); + + Verdict getVerdict(); + + PathAttributes setVerdict(Verdict verdict); + + enum Verdict { + pending, + clean, + malicious + } } diff --git a/core/src/main/java/ch/cyberduck/core/PreferencesUseragentProvider.java b/core/src/main/java/ch/cyberduck/core/PreferencesUseragentProvider.java index 1a6a37f047..b466db6035 100644 --- a/core/src/main/java/ch/cyberduck/core/PreferencesUseragentProvider.java +++ b/core/src/main/java/ch/cyberduck/core/PreferencesUseragentProvider.java @@ -21,18 +21,44 @@ package ch.cyberduck.core; import ch.cyberduck.core.preferences.Preferences; import ch.cyberduck.core.preferences.PreferencesFactory; +import org.apache.commons.lang3.ArrayUtils; +import org.apache.commons.lang3.StringUtils; + public class PreferencesUseragentProvider implements UseragentProvider { - private static final Preferences preferences - = PreferencesFactory.get(); + private static final String ua = buildUserAgent(); - private static final String ua = String.format("%s/%s.%s (%s/%s) (%s)", - preferences.getProperty("application.name"), - preferences.getProperty("application.version"), - preferences.getProperty("application.revision"), - preferences.getProperty("os.name"), - preferences.getProperty("os.version"), - preferences.getProperty("os.arch")); + private static String buildUserAgent() { + final Preferences preferences = PreferencesFactory.get(); + return String.format("%s/%s.%s (%s/%s) (%s)", + sanitizeApplicationName(preferences.getProperty("application.name")), + preferences.getProperty("application.version"), + preferences.getProperty("application.revision"), + preferences.getProperty("os.name"), + preferences.getProperty("os.version"), + preferences.getProperty("os.arch")); + } + + private static String sanitizeApplicationName(final String application) { + final char[] httpHeaderTokenSeparators = new char[]{ + ' ', '\t', '"', '(', ')', + ',', '/', ':', ';', '<', + '=', '>', '?', '@', '[', + '\\', ']', '{', '}' + }; + if(StringUtils.indexOfAny(application, httpHeaderTokenSeparators) == -1) { + return application; + } + final StringBuilder builder = new StringBuilder(); + for(int i = 0; i < application.length(); i++) { + final char token = application.charAt(i); + if(ArrayUtils.indexOf(httpHeaderTokenSeparators, token) != -1) { + continue; + } + builder.append(token); + } + return builder.toString(); + } @Override public String get() { diff --git a/core/src/main/java/ch/cyberduck/core/Profile.java b/core/src/main/java/ch/cyberduck/core/Profile.java index 92ad329cbd..c2f723ba19 100644 --- a/core/src/main/java/ch/cyberduck/core/Profile.java +++ b/core/src/main/java/ch/cyberduck/core/Profile.java @@ -65,6 +65,19 @@ public class Profile implements Protocol { public static final String OAUTH_PKCE_KEY = "OAuth PKCE"; public static final String SCOPES_KEY = "Scopes"; public static final String STS_ENDPOINT_KEY = "STS Endpoint"; + /** + * A constant key used to define the Amazon Resource Name (ARN) for AWS Security Token Service (STS) + */ + public static final String STS_ROLE_ARN_PROPERTY_KEY = "role_arn"; + public static final String STS_TAGS_PROPERTY_KEY = "tags"; + public static final String STS_ROLE_SESSION_NAME_PROPERTY_KEY = "role_session_name"; + public static final String STS_DURATION_SECONDS_PROPERTY_KEY = "duration_seconds"; + /** + * A constant key used to define the Amazon Resource Name (ARN) for Multi-Factor Authentication (MFA) + * in AWS Security Token Service (STS) profiles. This key is typically utilized to specify or retrieve + * the ARN for an MFA device required during authentication processes involving STS. + */ + public static final String STS_MFA_ARN_PROPERTY_KEY = "mfa_serial"; public static final String DISK_KEY = "Disk"; public static final String ICON_KEY = "Icon"; @@ -101,6 +114,8 @@ public class Profile implements Protocol { public static final String OAUTH_CONFIGURABLE_KEY = "OAuth Configurable"; public static final String CERTIFICATE_CONFIGURABLE_KEY = "Certificate Configurable"; public static final String PRIVATE_KEY_CONFIGURABLE_KEY = "Private Key Configurable"; + public static final String ROLE_KEY_CONFIGURABLE_KEY = "Role Configurable"; + public static final String MULTIFACTOR_KEY_CONFIGURABLE_KEY = "Multi Factor Configurable"; public static final String PROPERTIES_KEY = "Properties"; public static final String DEPRECATED_KEY = "Deprecated"; @@ -544,6 +559,24 @@ public class Profile implements Protocol { return v; } + @Override + public boolean isRoleConfigurable() { + final Boolean v = this.bool(ROLE_KEY_CONFIGURABLE_KEY); + if(null == v) { + return parent.isRoleConfigurable(); + } + return v; + } + + @Override + public boolean isMultiFactorConfigurable() { + final Boolean v = this.bool(MULTIFACTOR_KEY_CONFIGURABLE_KEY); + if(null == v) { + return parent.isMultiFactorConfigurable(); + } + return v; + } + @Override public boolean isHostnameConfigurable() { final Boolean v = this.bool(HOSTNAME_CONFIGURABLE_KEY); @@ -652,6 +685,9 @@ public class Profile implements Protocol { property -> StringUtils.contains(property, '=') ? substitutor.replace(StringUtils.substringAfter(property, '=')) : StringUtils.EMPTY))); // In profile as dict properties.putAll(this.map(PROPERTIES_KEY)); + if(null != this.getContext()) { + properties.put(CONTEXT_KEY, this.getContext()); + } return properties; } diff --git a/core/src/main/java/ch/cyberduck/core/Protocol.java b/core/src/main/java/ch/cyberduck/core/Protocol.java index d36ae62555..b67947b911 100644 --- a/core/src/main/java/ch/cyberduck/core/Protocol.java +++ b/core/src/main/java/ch/cyberduck/core/Protocol.java @@ -81,6 +81,10 @@ public interface Protocol extends FeatureFactory, Comparable, Serializ boolean isPrivateKeyConfigurable(); + boolean isRoleConfigurable(); + + boolean isMultiFactorConfigurable(); + /** * @return False if the hostname to connect is static. */ diff --git a/core/src/main/java/ch/cyberduck/core/ProxyPathAttributes.java b/core/src/main/java/ch/cyberduck/core/ProxyPathAttributes.java new file mode 100644 index 0000000000..f7847a5ddc --- /dev/null +++ b/core/src/main/java/ch/cyberduck/core/ProxyPathAttributes.java @@ -0,0 +1,342 @@ +package ch.cyberduck.core; + +/* + * Copyright (c) 2002-2025 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 ch.cyberduck.core.features.Encryption; +import ch.cyberduck.core.features.Quota; +import ch.cyberduck.core.io.Checksum; +import ch.cyberduck.core.serializer.Serializer; + +import java.util.Map; + +public class ProxyPathAttributes implements PathAttributes { + + private final PathAttributes proxy; + + public ProxyPathAttributes(final PathAttributes proxy) { + this.proxy = proxy; + } + + @Override + public PathAttributes setSize(final long size) { + return proxy.setSize(size); + } + + @Override + public Quota.Space getQuota() { + return proxy.getQuota(); + } + + @Override + public PathAttributes setQuota(final Quota.Space quota) { + return proxy.setQuota(quota); + } + + @Override + public PathAttributes setModificationDate(final long millis) { + return proxy.setModificationDate(millis); + } + + @Override + public PathAttributes setCreationDate(final long millis) { + return proxy.setCreationDate(millis); + } + + @Override + public PathAttributes setAccessedDate(final long millis) { + return proxy.setAccessedDate(millis); + } + + @Override + public PathAttributes setPermission(final Permission p) { + return proxy.setPermission(p); + } + + @Override + public Acl getAcl() { + return proxy.getAcl(); + } + + @Override + public PathAttributes setAcl(final Acl acl) { + return proxy.setAcl(acl); + } + + @Override + public PathAttributes setOwner(final String o) { + return proxy.setOwner(o); + } + + @Override + public PathAttributes setGroup(final String g) { + return proxy.setGroup(g); + } + + @Override + public Checksum getChecksum() { + return proxy.getChecksum(); + } + + @Override + public PathAttributes setChecksum(final Checksum checksum) { + return proxy.setChecksum(checksum); + } + + @Override + public String getETag() { + return proxy.getETag(); + } + + @Override + public PathAttributes setETag(final String etag) { + return proxy.setETag(etag); + } + + @Override + public String getStorageClass() { + return proxy.getStorageClass(); + } + + @Override + public PathAttributes setStorageClass(final String storageClass) { + return proxy.setStorageClass(storageClass); + } + + @Override + public Encryption.Algorithm getEncryption() { + return proxy.getEncryption(); + } + + @Override + public PathAttributes setEncryption(final Encryption.Algorithm encryption) { + return proxy.setEncryption(encryption); + } + + @Override + public String getVersionId() { + return proxy.getVersionId(); + } + + @Override + public PathAttributes setVersionId(final String versionId) { + return proxy.setVersionId(versionId); + } + + @Override + public String getFileId() { + return proxy.getFileId(); + } + + @Override + public PathAttributes setFileId(final String fileId) { + return proxy.setFileId(fileId); + } + + @Override + public String getLockId() { + return proxy.getLockId(); + } + + @Override + public PathAttributes setLockId(final String lockId) { + return proxy.setLockId(lockId); + } + + @Override + public String getDirectoryId() { + return proxy.getDirectoryId(); + } + + @Override + public PathAttributes setDirectoryId(final String directoryId) { + return proxy.setDirectoryId(directoryId); + } + + @Override + public Long getRevision() { + return proxy.getRevision(); + } + + @Override + public PathAttributes setRevision(final Long revision) { + return proxy.setRevision(revision); + } + + @Override + public Path getDecrypted() { + return proxy.getDecrypted(); + } + + @Override + public PathAttributes setDecrypted(final Path decrypted) { + return proxy.setDecrypted(decrypted); + } + + @Override + public Path getEncrypted() { + return proxy.getEncrypted(); + } + + @Override + public PathAttributes setEncrypted(final Path encrypted) { + return proxy.setEncrypted(encrypted); + } + + @Override + public PathAttributes setVault(final Path vault) { + return proxy.setVault(vault); + } + + @Override + public Path getVault() { + return proxy.getVault(); + } + + @Override + public boolean isDuplicate() { + return proxy.isDuplicate(); + } + + @Override + public PathAttributes setDuplicate(final boolean duplicate) { + return proxy.setDuplicate(duplicate); + } + + @Override + public Boolean isHidden() { + return proxy.isHidden(); + } + + @Override + public PathAttributes setHidden(final boolean hidden) { + return proxy.setHidden(hidden); + } + + @Override + public Boolean isTrashed() { + return proxy.isTrashed(); + } + + @Override + public PathAttributes setTrashed(final boolean trashed) { + return proxy.setTrashed(trashed); + } + + @Override + public Map getMetadata() { + return proxy.getMetadata(); + } + + @Override + public PathAttributes setMetadata(final Map metadata) { + return proxy.setMetadata(metadata); + } + + @Override + public String getRegion() { + return proxy.getRegion(); + } + + @Override + public PathAttributes setRegion(final String region) { + return proxy.setRegion(region); + } + + @Override + public String getDisplayname() { + return proxy.getDisplayname(); + } + + @Override + public PathAttributes setDisplayname(final String displayname) { + return proxy.setDisplayname(displayname); + } + + @Override + public DescriptiveUrl getLink() { + return proxy.getLink(); + } + + @Override + public PathAttributes setLink(final DescriptiveUrl link) { + return proxy.setLink(link); + } + + @Override + public Map getCustom() { + return proxy.getCustom(); + } + + @Override + public PathAttributes setCustom(final Map custom) { + return proxy.setCustom(custom); + } + + @Override + public PathAttributes setCustom(final String key, final String value) { + return proxy.setCustom(key, value); + } + + @Override + public Verdict getVerdict() { + return proxy.getVerdict(); + } + + @Override + public PathAttributes setVerdict(final Verdict verdict) { + return proxy.setVerdict(verdict); + } + + @Override + public long getSize() { + return proxy.getSize(); + } + + @Override + public long getModificationDate() { + return proxy.getModificationDate(); + } + + @Override + public long getCreationDate() { + return proxy.getCreationDate(); + } + + @Override + public long getAccessedDate() { + return proxy.getAccessedDate(); + } + + @Override + public Permission getPermission() { + return proxy.getPermission(); + } + + @Override + public String getOwner() { + return proxy.getOwner(); + } + + @Override + public String getGroup() { + return proxy.getGroup(); + } + + @Override + public T serialize(final Serializer dict) { + return proxy.serialize(dict); + } +} diff --git a/core/src/main/java/ch/cyberduck/core/Session.java b/core/src/main/java/ch/cyberduck/core/Session.java index e872b2f33c..b0068fefa4 100644 --- a/core/src/main/java/ch/cyberduck/core/Session.java +++ b/core/src/main/java/ch/cyberduck/core/Session.java @@ -27,12 +27,11 @@ import ch.cyberduck.core.features.Find; import ch.cyberduck.core.features.Home; import ch.cyberduck.core.features.Move; import ch.cyberduck.core.features.Quota; -import ch.cyberduck.core.features.Read; import ch.cyberduck.core.features.Search; import ch.cyberduck.core.features.Share; import ch.cyberduck.core.features.Upload; import ch.cyberduck.core.features.Versioning; -import ch.cyberduck.core.features.Write; +import ch.cyberduck.core.preferences.HostPreferences; import ch.cyberduck.core.preferences.HostPreferencesFactory; import ch.cyberduck.core.preferences.PreferencesReader; import ch.cyberduck.core.proxy.ProxyFinder; @@ -59,6 +58,7 @@ public abstract class Session implements FeatureFactory, TranscriptListener { * Encapsulating all the information of the remote host */ protected final Host host; + protected final HostPreferences preferences; private Metrics metrics = new DisabledMetrics(); @@ -106,6 +106,10 @@ public abstract class Session implements FeatureFactory, TranscriptListener { return this; } + public VaultRegistry getRegistry() { + return registry; + } + public enum State { opening, open, @@ -115,6 +119,7 @@ public abstract class Session implements FeatureFactory, TranscriptListener { protected Session(final Host h) { this.host = h; + this.preferences = HostPreferencesFactory.get(host); } /** @@ -175,6 +180,7 @@ public abstract class Session implements FeatureFactory, TranscriptListener { } } finally { + preferences.clear(); state = State.closed; log.debug("Connection did close to {}", host); } @@ -194,12 +200,15 @@ public abstract class Session implements FeatureFactory, TranscriptListener { } } - protected abstract void logout() throws BackgroundException; + protected void logout() throws BackgroundException { + log.debug("Reset credentials for {}", host); + host.getCredentials().reset(); + } /** * Close the connection to the remote host. Subsequent calls to #getClient() must return null. */ - protected void disconnect() { + protected void disconnect() throws BackgroundException { state = State.closed; listeners.clear(); } @@ -282,10 +291,10 @@ public abstract class Session implements FeatureFactory, TranscriptListener { @SuppressWarnings("unchecked") public T _getFeature(final Class type) { if(type == Upload.class) { - return (T) new DefaultUploadFeature(this.getFeature(Write.class)); + return (T) new DefaultUploadFeature<>(this); } if(type == Download.class) { - return (T) new DefaultDownloadFeature(this.getFeature(Read.class)); + return (T) new DefaultDownloadFeature(this); } if(type == Move.class) { return (T) new DisabledMoveFeature(); diff --git a/core/src/main/java/ch/cyberduck/core/SessionPoolFactory.java b/core/src/main/java/ch/cyberduck/core/SessionPoolFactory.java index 60a97c45c4..6608a38efb 100644 --- a/core/src/main/java/ch/cyberduck/core/SessionPoolFactory.java +++ b/core/src/main/java/ch/cyberduck/core/SessionPoolFactory.java @@ -88,7 +88,7 @@ public class SessionPoolFactory { case stateful: if(Arrays.asList(usage).contains(Usage.browser)) { log.info("Create new stateful connection pool for {}", bookmark); - final Session session = SessionFactory.create(new Host(bookmark).withCredentials(new Credentials(bookmark.getCredentials())), trust, key); + final Session session = SessionFactory.create(new Host(bookmark).setCredentials(new Credentials(bookmark.getCredentials())), trust, key); return new StatefulSessionPool(connect, session, transcript, registry); } // Break through to default pool diff --git a/core/src/main/java/ch/cyberduck/core/TemporaryAccessTokens.java b/core/src/main/java/ch/cyberduck/core/TemporaryAccessTokens.java index 00d13f2788..ea6ce2d20e 100644 --- a/core/src/main/java/ch/cyberduck/core/TemporaryAccessTokens.java +++ b/core/src/main/java/ch/cyberduck/core/TemporaryAccessTokens.java @@ -25,7 +25,7 @@ import java.util.Objects; public final class TemporaryAccessTokens { public static final TemporaryAccessTokens EMPTY - = new TemporaryAccessTokens(null, null, null, Long.MAX_VALUE); + = new TemporaryAccessTokens(null, null, null); private final String accessKeyId; private final String secretAccessKey; @@ -33,7 +33,15 @@ public final class TemporaryAccessTokens { private final Long expiryInMilliseconds; public TemporaryAccessTokens(final String sessionToken) { - this(null, null, sessionToken, -1L); + this(null, null, sessionToken); + } + + public TemporaryAccessTokens(final String accessKeyId, final String secretAccessKey) { + this(accessKeyId, secretAccessKey, null); + } + + public TemporaryAccessTokens(final String accessKeyId, final String secretAccessKey, final String sessionToken) { + this(accessKeyId, secretAccessKey, sessionToken, -1L); } public TemporaryAccessTokens(final String accessKeyId, final String secretAccessKey, final String sessionToken, final Long expiryInMilliseconds) { @@ -43,6 +51,13 @@ public final class TemporaryAccessTokens { this.expiryInMilliseconds = expiryInMilliseconds; } + public TemporaryAccessTokens(final TemporaryAccessTokens tokens) { + this.accessKeyId = tokens.accessKeyId; + this.secretAccessKey = tokens.secretAccessKey; + this.sessionToken = tokens.sessionToken; + this.expiryInMilliseconds = tokens.expiryInMilliseconds; + } + public boolean validate() { return StringUtils.isNotEmpty(sessionToken); } diff --git a/core/src/main/java/ch/cyberduck/core/TranscriptListener.java b/core/src/main/java/ch/cyberduck/core/TranscriptListener.java index 0484b55af3..a7ed20fccd 100644 --- a/core/src/main/java/ch/cyberduck/core/TranscriptListener.java +++ b/core/src/main/java/ch/cyberduck/core/TranscriptListener.java @@ -22,7 +22,9 @@ public interface TranscriptListener { enum Type { request, - response + requestheader, + response, + responseheader } /** diff --git a/core/src/main/java/ch/cyberduck/core/URIEncoder.java b/core/src/main/java/ch/cyberduck/core/URIEncoder.java index 0157c5f9a4..079fb9f99b 100644 --- a/core/src/main/java/ch/cyberduck/core/URIEncoder.java +++ b/core/src/main/java/ch/cyberduck/core/URIEncoder.java @@ -18,6 +18,8 @@ package ch.cyberduck.core; * dkocher@cyberduck.ch */ +import ch.cyberduck.core.preferences.PreferencesFactory; + import org.apache.commons.lang3.StringUtils; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; @@ -26,6 +28,7 @@ import java.io.UnsupportedEncodingException; import java.net.URLDecoder; import java.net.URLEncoder; import java.nio.charset.StandardCharsets; +import java.util.Map; import java.util.StringTokenizer; public final class URIEncoder { @@ -63,9 +66,11 @@ public final class URIEncoder { } // Because URLEncoder uses application/x-www-form-urlencoded we have to replace these // for proper URI percented encoding. + final Map mapping = PreferencesFactory.get().getMap("http.uriencode.mapping"); return StringUtils.replaceEach(b.toString(), - new String[]{"+", "*", "%7E", "%40"}, - new String[]{"%20", "%2A", "~", "@"}); + mapping.keySet().stream().toArray(String[]::new), + mapping.values().stream().toArray(String[]::new) + ); } catch(UnsupportedEncodingException e) { log.warn("Failure {} encoding input {}", e, input); diff --git a/core/src/main/java/ch/cyberduck/core/UnsecureHostPasswordStore.java b/core/src/main/java/ch/cyberduck/core/UnsecureHostPasswordStore.java index 813cc48d94..f76a01c06c 100644 --- a/core/src/main/java/ch/cyberduck/core/UnsecureHostPasswordStore.java +++ b/core/src/main/java/ch/cyberduck/core/UnsecureHostPasswordStore.java @@ -1,6 +1,7 @@ package ch.cyberduck.core; import ch.cyberduck.core.exception.AccessDeniedException; +import ch.cyberduck.core.exception.LocalAccessDeniedException; import ch.cyberduck.core.local.DefaultLocalDirectoryFeature; import ch.cyberduck.core.preferences.SupportDirectoryFinderFactory; @@ -17,72 +18,67 @@ public class UnsecureHostPasswordStore extends DefaultHostPasswordStore { private final Local file = LocalFactory.get(SupportDirectoryFinderFactory.get().find(), "credentials"); - private Properties load() { + private Properties load() throws AccessDeniedException { final Properties properties = new Properties(); - try { - new DefaultLocalDirectoryFeature().mkdir(file.getParent()); - } - catch(AccessDeniedException e) { - log.warn("Failure saving credentials to {}. {}", file.getAbsolute(), e); - } + new DefaultLocalDirectoryFeature().mkdir(file.getParent()); if(file.exists()) { try { - try (InputStream in = file.getInputStream()) { + try(InputStream in = file.getInputStream()) { properties.load(in); } } catch(IllegalArgumentException | AccessDeniedException | IOException e) { log.warn("Failure reading credentials from {}. {}", file.getAbsolute(), e); + throw new LocalAccessDeniedException(e.getMessage(), e); } } return properties; } - private void save(final Properties properties) { - try (OutputStream out = file.getOutputStream(false)) { + private void save(final Properties properties) throws AccessDeniedException { + try(OutputStream out = file.getOutputStream(false)) { properties.store(out, "Credentials"); } - catch(AccessDeniedException e) { - log.warn("Failure saving credentials to {}. {}", file.getAbsolute(), e); - } catch(IOException e) { log.warn("Failure saving credentials to {}. {}", file.getAbsolute(), e.getMessage()); + throw new LocalAccessDeniedException(e.getMessage(), e); } + file.attributes().setPermission(new Permission(600)); } @Override - public String getPassword(final String serviceName, final String accountName) { + public String getPassword(final String serviceName, final String accountName) throws AccessDeniedException { return this.load().getProperty(String.format("%s@%s", accountName, serviceName), null); } @Override - public void addPassword(final String serviceName, final String accountName, final String password) { + public void addPassword(final String serviceName, final String accountName, final String password) throws AccessDeniedException { final Properties properties = this.load(); properties.setProperty(String.format("%s@%s", accountName, serviceName), password); this.save(properties); } @Override - public String getPassword(final Scheme scheme, final int port, final String hostname, final String user) { + public String getPassword(final Scheme scheme, final int port, final String hostname, final String user) throws AccessDeniedException { return this.load().getProperty(String.format("%s://%s@%s:%d", scheme, user, hostname, port), null); } @Override - public void addPassword(final Scheme scheme, final int port, final String hostname, final String user, final String password) { + public void addPassword(final Scheme scheme, final int port, final String hostname, final String user, final String password) throws AccessDeniedException { final Properties properties = this.load(); properties.setProperty(String.format("%s://%s@%s:%d", scheme, user, hostname, port), password); this.save(properties); } @Override - public void deletePassword(final String serviceName, final String accountName) { + public void deletePassword(final String serviceName, final String accountName) throws AccessDeniedException { final Properties properties = this.load(); properties.remove(String.format("%s@%s", accountName, serviceName)); this.save(properties); } @Override - public void deletePassword(final Scheme scheme, final int port, final String hostname, final String user) { + public void deletePassword(final Scheme scheme, final int port, final String hostname, final String user) throws AccessDeniedException { final Properties properties = this.load(); properties.remove(String.format("%s://%s@%s:%d", scheme, user, hostname, port)); this.save(properties); diff --git a/core/src/main/java/ch/cyberduck/core/WindowsIntegratedCredentialsConfigurator.java b/core/src/main/java/ch/cyberduck/core/WindowsIntegratedCredentialsConfigurator.java index 69f5c0ba9c..eccc4c07a3 100644 --- a/core/src/main/java/ch/cyberduck/core/WindowsIntegratedCredentialsConfigurator.java +++ b/core/src/main/java/ch/cyberduck/core/WindowsIntegratedCredentialsConfigurator.java @@ -38,18 +38,18 @@ public class WindowsIntegratedCredentialsConfigurator implements CredentialsConf @Override public Credentials configure(final Host host) { + final Credentials credentials = new Credentials(host.getCredentials()); if(!WinHttpClients.isWinAuthAvailable()) { log.warn("No Windows authentication available"); - return CredentialsConfigurator.DISABLED.configure(host); + return credentials; } - if(host.getCredentials().validate(host.getProtocol(), new LoginOptions(host.getProtocol()).password(false))) { + if(credentials.validate(host.getProtocol(), new LoginOptions(host.getProtocol()).password(false))) { log.warn("Skip auto configuration of credentials for {}", host); - return CredentialsConfigurator.DISABLED.configure(host); + return credentials; } // No username preset final String nameSamCompatible = CurrentWindowsCredentials.INSTANCE.getName(); - final Credentials credentials = new Credentials(host.getCredentials()) - .withPassword(CurrentWindowsCredentials.INSTANCE.getPassword()); + credentials.setPassword(CurrentWindowsCredentials.INSTANCE.getPassword()); if(!includeDomain && StringUtils.contains(nameSamCompatible, '\\')) { credentials.setUsername(StringUtils.split(nameSamCompatible, '\\')[1]); } diff --git a/core/src/main/java/ch/cyberduck/core/aquaticprime/InvalidLicenseException.java b/core/src/main/java/ch/cyberduck/core/aquaticprime/InvalidLicenseException.java index 69220e7983..231b40be50 100644 --- a/core/src/main/java/ch/cyberduck/core/aquaticprime/InvalidLicenseException.java +++ b/core/src/main/java/ch/cyberduck/core/aquaticprime/InvalidLicenseException.java @@ -21,10 +21,14 @@ import ch.cyberduck.core.exception.BackgroundException; public class InvalidLicenseException extends BackgroundException { public InvalidLicenseException() { - super(LocaleFactory.localizedString("Not a valid registration key", "License"), (String) null); + this(null); } public InvalidLicenseException(final String detail) { - super(LocaleFactory.localizedString("Not a valid registration key", "License"), detail); + this(LocaleFactory.localizedString("Not a valid registration key", "License"), detail); + } + + public InvalidLicenseException(final String message, final String detail) { + super(message, detail); } } diff --git a/core/src/main/java/ch/cyberduck/core/aquaticprime/LicenseFactory.java b/core/src/main/java/ch/cyberduck/core/aquaticprime/LicenseFactory.java index e16e9c9056..10d6481c92 100644 --- a/core/src/main/java/ch/cyberduck/core/aquaticprime/LicenseFactory.java +++ b/core/src/main/java/ch/cyberduck/core/aquaticprime/LicenseFactory.java @@ -156,6 +156,7 @@ public abstract class LicenseFactory extends Factory { private static final License EMPTY_LICENSE = new License() { @Override public boolean verify(final LicenseVerifierCallback callback) { + callback.failure(new InvalidLicenseException()); return false; } diff --git a/core/src/main/java/ch/cyberduck/core/aquaticprime/Receipt.java b/core/src/main/java/ch/cyberduck/core/aquaticprime/Receipt.java index 78177ab681..548ec109bf 100644 --- a/core/src/main/java/ch/cyberduck/core/aquaticprime/Receipt.java +++ b/core/src/main/java/ch/cyberduck/core/aquaticprime/Receipt.java @@ -51,7 +51,7 @@ public class Receipt extends AbstractLicense { @Override public String getValue(final String property) { - return LocaleFactory.localizedString("Unknown"); + return null; } @Override diff --git a/core/src/main/java/ch/cyberduck/core/aquaticprime/ReceiptFactory.java b/core/src/main/java/ch/cyberduck/core/aquaticprime/ReceiptFactory.java index 1fff7bcfda..19e30e649f 100644 --- a/core/src/main/java/ch/cyberduck/core/aquaticprime/ReceiptFactory.java +++ b/core/src/main/java/ch/cyberduck/core/aquaticprime/ReceiptFactory.java @@ -50,6 +50,7 @@ public class ReceiptFactory extends LicenseFactory { private static final License EMPTY_LICENSE = new License() { @Override public boolean verify(final LicenseVerifierCallback callback) { + callback.failure(new InvalidLicenseException()); return false; } diff --git a/core/src/main/java/ch/cyberduck/core/features/AclPermission.java b/core/src/main/java/ch/cyberduck/core/features/AclPermission.java index 85cbb10957..f1d3fc71d3 100644 --- a/core/src/main/java/ch/cyberduck/core/features/AclPermission.java +++ b/core/src/main/java/ch/cyberduck/core/features/AclPermission.java @@ -54,7 +54,7 @@ public interface AclPermission { /** * @return List of known ACL users */ - List getAvailableAclUsers(); + List getAvailableAclUsers(List files); /** * Roles available for users in a configurable ACL. @@ -70,9 +70,9 @@ public interface AclPermission { * @param file Remote file * @return Default ACL to set for file */ - default Acl getDefault(final Path type) throws BackgroundException { + default Acl getDefault(final Path file) throws BackgroundException { if(preferences.getBoolean("queue.upload.permissions.default")) { - if(type.getType().contains(Path.Type.file)) { + if(file.getType().contains(Path.Type.file)) { return toAcl(new Permission(preferences.getInteger("queue.upload.permissions.file.default"))); } else { diff --git a/core/src/main/java/ch/cyberduck/core/features/Bulk.java b/core/src/main/java/ch/cyberduck/core/features/Bulk.java index 832e6f68b9..db8be813b6 100644 --- a/core/src/main/java/ch/cyberduck/core/features/Bulk.java +++ b/core/src/main/java/ch/cyberduck/core/features/Bulk.java @@ -46,6 +46,4 @@ public interface Bulk { * @param callback Callback to user */ void post(Transfer.Type type, Map files, ConnectionCallback callback) throws BackgroundException; - - Bulk withDelete(Delete delete); } diff --git a/core/src/main/java/ch/cyberduck/core/features/Delete.java b/core/src/main/java/ch/cyberduck/core/features/Delete.java index 0828041dc0..9a4d90e7c9 100644 --- a/core/src/main/java/ch/cyberduck/core/features/Delete.java +++ b/core/src/main/java/ch/cyberduck/core/features/Delete.java @@ -66,13 +66,6 @@ public interface Delete { } } - /** - * @return True if the implementation supports deleting folders recursively - */ - default boolean isRecursive() { - return this.features().contains(Flags.recursive); - } - /** * Callback for every file deleted */ @@ -94,7 +87,7 @@ public interface Delete { /** * @return Supported features */ - default EnumSet features() { + default EnumSet features(final Path file) { return EnumSet.noneOf(Flags.class); } diff --git a/core/src/main/java/ch/cyberduck/core/features/Directory.java b/core/src/main/java/ch/cyberduck/core/features/Directory.java index f670e86f67..ae81df8ee1 100644 --- a/core/src/main/java/ch/cyberduck/core/features/Directory.java +++ b/core/src/main/java/ch/cyberduck/core/features/Directory.java @@ -34,14 +34,16 @@ public interface Directory { /** * Create new folder on server * + * @param writer Write feature used in default implementations for placeholder files * @param folder Directory * @param status Transfer status + * @return File including latest attributes from server */ - Path mkdir(Path folder, TransferStatus status) throws BackgroundException; + Path mkdir(Write writer, Path folder, TransferStatus status) throws BackgroundException; /** - * @param workdir Working directory in browser - * @param filename Folder name or null if unknown + * @param workdir Working directory in browser + * @param filename Folder name or null if unknown * @return True if creating directory is supported in the working directory */ default boolean isSupported(final Path workdir, final String filename) { @@ -54,13 +56,6 @@ public interface Directory { } } - /** - * Retrieve write implementation for implementations using placeholder files for folders - */ - default Directory withWriter(Write writer) { - return this; - } - /** * @throws AccessDeniedException Permission failure for target directory */ diff --git a/core/src/main/java/ch/cyberduck/core/features/Download.java b/core/src/main/java/ch/cyberduck/core/features/Download.java index 02164d046a..7c346bc8af 100644 --- a/core/src/main/java/ch/cyberduck/core/features/Download.java +++ b/core/src/main/java/ch/cyberduck/core/features/Download.java @@ -29,6 +29,7 @@ public interface Download { /** * Copy file from server to disk * + * @param read Feature * @param file File on server * @param local File on local disk * @param throttle Bandwidth management @@ -36,7 +37,7 @@ public interface Download { * @param status Transfer status holder * @param callback Prompt */ - void download(Path file, Local local, BandwidthThrottle throttle, StreamListener listener, + void download(Read read, Path file, Local local, BandwidthThrottle throttle, StreamListener listener, TransferStatus status, ConnectionCallback callback) throws BackgroundException; /** @@ -46,6 +47,4 @@ public interface Download { * @return True if reading with offset is supported */ boolean offset(Path file) throws BackgroundException; - - Download withReader(Read reader); } diff --git a/core/src/main/java/ch/cyberduck/core/features/Encryptor.java b/core/src/main/java/ch/cyberduck/core/features/Encryptor.java deleted file mode 100644 index 1d588820c2..0000000000 --- a/core/src/main/java/ch/cyberduck/core/features/Encryptor.java +++ /dev/null @@ -1,36 +0,0 @@ -package ch.cyberduck.core.features; - -/* - * Copyright (c) 2002-2022 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 ch.cyberduck.core.Path; -import ch.cyberduck.core.exception.BackgroundException; -import ch.cyberduck.core.transfer.TransferStatus; - -import java.io.InputStream; - -@Optional -public interface Encryptor { - - /** - * Wrap proxy with stream encrypting content on the fly - * - * @param file File - * @param proxy File input stream - * @param status Encryption properties - * @return Wrapped stream - */ - InputStream encrypt(Path file, InputStream proxy, TransferStatus status) throws BackgroundException; -} diff --git a/core/src/main/java/ch/cyberduck/core/features/Location.java b/core/src/main/java/ch/cyberduck/core/features/Location.java index aa89dfce20..ba26ee2d93 100644 --- a/core/src/main/java/ch/cyberduck/core/features/Location.java +++ b/core/src/main/java/ch/cyberduck/core/features/Location.java @@ -32,12 +32,12 @@ public interface Location { /** * @return Default region for new containers */ - Name getDefault(); + Name getDefault(Path file); /** * @return Available regions */ - Set getLocations(); + Set getLocations(Path file); /** * @param file File or folder @@ -91,12 +91,12 @@ public interface Location { Location disabled = new Location() { @Override - public Name getDefault() { + public Name getDefault(final Path file) { return unknown; } @Override - public Set getLocations() { + public Set getLocations(final Path file) { return Collections.emptySet(); } diff --git a/core/src/main/java/ch/cyberduck/core/features/Metadata.java b/core/src/main/java/ch/cyberduck/core/features/Metadata.java index d3eaeb5e19..8111440d62 100644 --- a/core/src/main/java/ch/cyberduck/core/features/Metadata.java +++ b/core/src/main/java/ch/cyberduck/core/features/Metadata.java @@ -23,7 +23,7 @@ import java.util.Map; @Optional public interface Metadata { - Map getDefault(); + Map getDefault(Path file); Map getMetadata(Path file) throws BackgroundException; diff --git a/core/src/main/java/ch/cyberduck/core/features/Redundancy.java b/core/src/main/java/ch/cyberduck/core/features/Redundancy.java index bfaa758832..d517fe40a0 100644 --- a/core/src/main/java/ch/cyberduck/core/features/Redundancy.java +++ b/core/src/main/java/ch/cyberduck/core/features/Redundancy.java @@ -29,12 +29,13 @@ public interface Redundancy { /** * @return Default storage class for new files */ - String getDefault(); + String getDefault(final Path file); /** + * @param file File * @return List of supported redundancy settings */ - Set getClasses(); + Set getClasses(Path file); /** * @param file File diff --git a/core/src/main/java/ch/cyberduck/core/features/Search.java b/core/src/main/java/ch/cyberduck/core/features/Search.java index ad7be7d730..220d3b18a1 100644 --- a/core/src/main/java/ch/cyberduck/core/features/Search.java +++ b/core/src/main/java/ch/cyberduck/core/features/Search.java @@ -33,18 +33,10 @@ public interface Search { * @return List of files found or empty list */ AttributedList search(Path workdir, Filter regex, ListProgressListener listener) throws BackgroundException; - - /** - * @return True if search is capable of recursively searching in folders - */ - default boolean isRecursive() { - return this.features().contains(Flags.recursive); - } - /** * @return Supported features */ - default EnumSet features() { + default EnumSet features(final Path workdir) { return EnumSet.noneOf(Flags.class); } diff --git a/core/src/main/java/ch/cyberduck/core/features/Touch.java b/core/src/main/java/ch/cyberduck/core/features/Touch.java index aa7e7f5b61..655a1584fd 100644 --- a/core/src/main/java/ch/cyberduck/core/features/Touch.java +++ b/core/src/main/java/ch/cyberduck/core/features/Touch.java @@ -26,7 +26,14 @@ import java.text.MessageFormat; @Optional public interface Touch { - Path touch(Path file, TransferStatus status) throws BackgroundException; + /** + * + * @param writer Write feature used in default implementations for placeholder files + * @param file File + * @param status Transfer status + * @return File including latest attributes from server + */ + Path touch(Write writer, Path file, TransferStatus status) throws BackgroundException; /** * @param workdir Working directory @@ -43,10 +50,6 @@ public interface Touch { } } - default Touch withWriter(Write writer) { - return this; - } - /** * @throws AccessDeniedException Permission failure for target directory */ diff --git a/core/src/main/java/ch/cyberduck/core/features/UnixPermission.java b/core/src/main/java/ch/cyberduck/core/features/UnixPermission.java index 93758c9c66..6d692e7248 100644 --- a/core/src/main/java/ch/cyberduck/core/features/UnixPermission.java +++ b/core/src/main/java/ch/cyberduck/core/features/UnixPermission.java @@ -41,11 +41,13 @@ public interface UnixPermission { Preferences preferences = PreferencesFactory.get(); + /** - * @param type File or folder + * @param workdir Parent folder + * @param type File or folder * @return Default mask for new file or folder */ - default Permission getDefault(final EnumSet type) { + default Permission getDefault(final Path workdir, final EnumSet type) { if(preferences.getBoolean("queue.upload.permissions.default")) { if(type.contains(Path.Type.file)) { return new Permission(preferences.getInteger("queue.upload.permissions.file.default")); diff --git a/core/src/main/java/ch/cyberduck/core/features/Upload.java b/core/src/main/java/ch/cyberduck/core/features/Upload.java index 063c7ac276..eb1f86afc4 100644 --- a/core/src/main/java/ch/cyberduck/core/features/Upload.java +++ b/core/src/main/java/ch/cyberduck/core/features/Upload.java @@ -30,6 +30,7 @@ public interface Upload { /** * Copy file on disk to server * + * @param write Write feature * @param file File on server * @param local File on local disk * @param throttle Bandwidth management @@ -39,7 +40,7 @@ public interface Upload { * @param callback Prompt * @see AttributesAdapter#toAttributes(Reply) */ - Reply upload(Path file, Local local, BandwidthThrottle throttle, final ProgressListener progress, StreamListener streamListener, + Reply upload(Write write, Path file, Local local, BandwidthThrottle throttle, final ProgressListener progress, StreamListener streamListener, TransferStatus status, ConnectionCallback callback) throws BackgroundException; /** @@ -52,6 +53,4 @@ public interface Upload { default Write.Append append(Path file, TransferStatus status) throws BackgroundException { return new Write.Append(false).withStatus(status); } - - Upload withWriter(Write writer); } diff --git a/core/src/main/java/ch/cyberduck/core/features/Vault.java b/core/src/main/java/ch/cyberduck/core/features/Vault.java index 58b7b9e5c4..2324350040 100644 --- a/core/src/main/java/ch/cyberduck/core/features/Vault.java +++ b/core/src/main/java/ch/cyberduck/core/features/Vault.java @@ -21,6 +21,7 @@ import ch.cyberduck.core.Session; import ch.cyberduck.core.exception.BackgroundException; import ch.cyberduck.core.exception.LoginCanceledException; import ch.cyberduck.core.exception.NotfoundException; +import ch.cyberduck.core.exception.UnsupportedException; import ch.cyberduck.core.vault.DisabledVault; import ch.cyberduck.core.vault.VaultCredentials; @@ -80,13 +81,25 @@ public interface Vault { long toCleartextSize(final long cleartextFileOffset, long ciphertextFileSize) throws BackgroundException; + /** + * Wrap feature with encryption + * + * @param session Connection + * @param type Feature type to wrap + * @param delegate Actual feature to wrap + * @return Wrapped feature + * @throws UnsupportedException Unsupported feature + */ @SuppressWarnings("unchecked") - T getFeature(Session session, Class type, T delegate); + T getFeature(Session session, Class type, T delegate) throws UnsupportedException; Vault DISABLED = new DisabledVault(); State getState(); + /** + * @return Root directory of vault + */ Path getHome(); enum State { diff --git a/core/src/main/java/ch/cyberduck/core/http/CustomHttpRequestExecutor.java b/core/src/main/java/ch/cyberduck/core/http/CustomHttpRequestExecutor.java index 44cf9f34b4..c84467fdc1 100644 --- a/core/src/main/java/ch/cyberduck/core/http/CustomHttpRequestExecutor.java +++ b/core/src/main/java/ch/cyberduck/core/http/CustomHttpRequestExecutor.java @@ -77,11 +77,11 @@ public class CustomHttpRequestExecutor extends HttpRequestExecutor { case "X-Auth-Key": case "X-Auth-Token": case "X-FilesAPI-Key": - listener.log(TranscriptListener.Type.request, String.format("%s: %s", header.getName(), + listener.log(TranscriptListener.Type.requestheader, String.format("%s: %s", header.getName(), StringUtils.repeat("*", Integer.min(8, StringUtils.length(header.getValue()))))); break; default: - listener.log(TranscriptListener.Type.request, header.toString()); + listener.log(TranscriptListener.Type.requestheader, header.toString()); break; } } @@ -98,9 +98,9 @@ public class CustomHttpRequestExecutor extends HttpRequestExecutor { case HttpPut.METHOD_NAME: case HttpPost.METHOD_NAME: case HttpPatch.METHOD_NAME: - if(Arrays.asList(response.getAllHeaders()).stream() + if(Arrays.stream(response.getAllHeaders()) .filter(header -> HttpHeaders.WWW_AUTHENTICATE.equals(header.getName())) - .filter(header -> "NTLM".equals(header.getValue())).findAny().isPresent()) { + .anyMatch(header -> "NTLM".equals(header.getValue()))) { // Unauthenticated connection cannot proceed with PUT final HttpResponseException preflight = new HttpResponseException(response.getStatusLine().getStatusCode(), response.getStatusLine().getReasonPhrase()); preflight.initCause(new RetriableAccessDeniedException(String.format("Authentication cannot proceed for %s", @@ -126,7 +126,7 @@ public class CustomHttpRequestExecutor extends HttpRequestExecutor { synchronized(listener) { listener.log(TranscriptListener.Type.response, response.getStatusLine().toString()); for(Header header : response.getAllHeaders()) { - listener.log(TranscriptListener.Type.response, header.toString()); + listener.log(TranscriptListener.Type.responseheader, header.toString()); } } } diff --git a/core/src/main/java/ch/cyberduck/core/http/CustomServiceUnavailableRetryStrategy.java b/core/src/main/java/ch/cyberduck/core/http/CustomServiceUnavailableRetryStrategy.java index 843f46745c..6ae262d859 100644 --- a/core/src/main/java/ch/cyberduck/core/http/CustomServiceUnavailableRetryStrategy.java +++ b/core/src/main/java/ch/cyberduck/core/http/CustomServiceUnavailableRetryStrategy.java @@ -38,8 +38,8 @@ public class CustomServiceUnavailableRetryStrategy extends ChainedServiceUnavail } public CustomServiceUnavailableRetryStrategy(final Host host, final int executionCount, final ServiceUnavailableRetryStrategy... chain) { - super(new ChainedServiceUnavailableRetryStrategy(new ExecutionCountServiceUnavailableRetryStrategy( - executionCount, new DefaultServiceUnavailableRetryStrategy(host)), new ChainedServiceUnavailableRetryStrategy(chain))); + super(new ChainedServiceUnavailableRetryStrategy(new ExecutionCountServiceUnavailableRetryStrategy(executionCount, new DefaultServiceUnavailableRetryStrategy(host)), + new ExecutionCountServiceUnavailableRetryStrategy(executionCount, new ChainedServiceUnavailableRetryStrategy(chain)))); this.host = host; } diff --git a/core/src/main/java/ch/cyberduck/core/http/DelayedHttpEntity.java b/core/src/main/java/ch/cyberduck/core/http/DelayedHttpEntity.java index 3c5e10f158..d06fa18ac6 100644 --- a/core/src/main/java/ch/cyberduck/core/http/DelayedHttpEntity.java +++ b/core/src/main/java/ch/cyberduck/core/http/DelayedHttpEntity.java @@ -87,7 +87,7 @@ public abstract class DelayedHttpEntity extends AbstractHttpEntity { public OutputStream getStream() { if(null == stream) { // Nothing to write - return NullOutputStream.NULL_OUTPUT_STREAM; + return NullOutputStream.INSTANCE; } return stream; } diff --git a/core/src/main/java/ch/cyberduck/core/http/DelayedHttpMultipartEntity.java b/core/src/main/java/ch/cyberduck/core/http/DelayedHttpMultipartEntity.java index 8c876aff26..015cf145fc 100644 --- a/core/src/main/java/ch/cyberduck/core/http/DelayedHttpMultipartEntity.java +++ b/core/src/main/java/ch/cyberduck/core/http/DelayedHttpMultipartEntity.java @@ -104,7 +104,7 @@ public class DelayedHttpMultipartEntity extends DelayedHttpEntity { public OutputStream getStream() { if(null == stream) { // Nothing to write - return NullOutputStream.NULL_OUTPUT_STREAM; + return NullOutputStream.INSTANCE; } return stream; } diff --git a/core/src/main/java/ch/cyberduck/core/http/HttpResponseOutputStream.java b/core/src/main/java/ch/cyberduck/core/http/HttpResponseOutputStream.java index 627632ae08..0748f4317a 100644 --- a/core/src/main/java/ch/cyberduck/core/http/HttpResponseOutputStream.java +++ b/core/src/main/java/ch/cyberduck/core/http/HttpResponseOutputStream.java @@ -31,7 +31,7 @@ import java.io.IOException; import java.io.OutputStream; public abstract class HttpResponseOutputStream extends StatusOutputStream { - private static final Logger log = LogManager.getLogger(StatusOutputStream.class); + private static final Logger log = LogManager.getLogger(HttpResponseOutputStream.class); private final AttributesAdapter attributes; private final TransferStatus status; diff --git a/core/src/main/java/ch/cyberduck/core/http/HttpUploadFeature.java b/core/src/main/java/ch/cyberduck/core/http/HttpUploadFeature.java index 6716017112..a29579acb2 100644 --- a/core/src/main/java/ch/cyberduck/core/http/HttpUploadFeature.java +++ b/core/src/main/java/ch/cyberduck/core/http/HttpUploadFeature.java @@ -50,26 +50,20 @@ import java.text.MessageFormat; public class HttpUploadFeature implements Upload { private static final Logger log = LogManager.getLogger(HttpUploadFeature.class); - private Write writer; - - public HttpUploadFeature(final Write writer) { - this.writer = writer; - } - @Override - public Reply upload(final Path file, final Local local, final BandwidthThrottle throttle, + public Reply upload(final Write 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 Reply response = this.upload(file, local, throttle, streamListener, status, status, status, callback); + final Reply response = this.upload(write, file, local, throttle, streamListener, status, status, status, callback); log.debug("Received response {}", response); return response; } - public Reply upload(final Path file, final Local local, final BandwidthThrottle throttle, + public Reply upload(final Write 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 { try { final Digest digest = this.digest(); - final Reply response = this.transfer(file, local, throttle, listener, status, cancel, progress, callback, digest); + final Reply response = this.transfer(write, file, local, throttle, listener, status, cancel, progress, callback, digest); this.post(file, digest, response); return response; } @@ -81,12 +75,12 @@ public class HttpUploadFeature implements Upload { } } - protected Reply transfer(final Path file, final Local local, final BandwidthThrottle throttle, final StreamListener listener, + protected Reply transfer(final Write 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, final Digest digest) throws IOException, BackgroundException { // Wrap with digest stream if available final InputStream in = this.decorate(local.getInputStream(), digest); - final StatusOutputStream out = writer.write(file, status, callback); + final StatusOutputStream out = write.write(file, status, callback); new StreamCopier(cancel, progress) .withOffset(status.getOffset()) .withLimit(status.getLength()) @@ -131,10 +125,4 @@ public class HttpUploadFeature implements Upload { } } } - - @Override - public Upload withWriter(final Write writer) { - this.writer = writer; - return this; - } } diff --git a/core/src/main/java/ch/cyberduck/core/io/BufferOutputStream.java b/core/src/main/java/ch/cyberduck/core/io/BufferOutputStream.java index a03c51f384..9c77075f04 100644 --- a/core/src/main/java/ch/cyberduck/core/io/BufferOutputStream.java +++ b/core/src/main/java/ch/cyberduck/core/io/BufferOutputStream.java @@ -31,11 +31,11 @@ public class BufferOutputStream extends ProxyOutputStream { private Long offset; public BufferOutputStream(final Buffer buffer) { - this(NullOutputStream.NULL_OUTPUT_STREAM, buffer, 0L); + this(NullOutputStream.INSTANCE, buffer, 0L); } public BufferOutputStream(final Buffer buffer, final Long offset) { - this(NullOutputStream.NULL_OUTPUT_STREAM, buffer, offset); + this(NullOutputStream.INSTANCE, buffer, offset); } public BufferOutputStream(final OutputStream proxy, final Buffer buffer, final Long offset) { diff --git a/core/src/main/java/ch/cyberduck/core/io/BufferSegmentingOutputStream.java b/core/src/main/java/ch/cyberduck/core/io/BufferSegmentingOutputStream.java index bd6f1b07fd..d6f2872a1f 100644 --- a/core/src/main/java/ch/cyberduck/core/io/BufferSegmentingOutputStream.java +++ b/core/src/main/java/ch/cyberduck/core/io/BufferSegmentingOutputStream.java @@ -30,7 +30,7 @@ public class BufferSegmentingOutputStream extends SegmentingOutputStream { private final Buffer buffer; public BufferSegmentingOutputStream(final OutputStream proxy, final Long threshold, final Buffer buffer) { - super(NullOutputStream.NULL_OUTPUT_STREAM, threshold, new BufferOutputStream(buffer)); + super(NullOutputStream.INSTANCE, threshold, new BufferOutputStream(buffer)); this.proxy = proxy; this.buffer = buffer; } diff --git a/core/src/main/java/ch/cyberduck/core/io/CRC32ChecksumCompute.java b/core/src/main/java/ch/cyberduck/core/io/CRC32ChecksumCompute.java index d8daadb792..7e28601e4e 100644 --- a/core/src/main/java/ch/cyberduck/core/io/CRC32ChecksumCompute.java +++ b/core/src/main/java/ch/cyberduck/core/io/CRC32ChecksumCompute.java @@ -35,6 +35,7 @@ public class CRC32ChecksumCompute extends AbstractChecksumCompute { byte[] buffer = new byte[16384]; int bytesRead; while((bytesRead = normalized.read(buffer, 0, buffer.length)) != -1) { + status.validate(); crc32.update(buffer, 0, bytesRead); } } @@ -44,6 +45,6 @@ public class CRC32ChecksumCompute extends AbstractChecksumCompute { finally { IOUtils.closeQuietly(normalized); } - return new Checksum(HashAlgorithm.crc32, Long.toHexString(crc32.getValue())); + return new Checksum(HashAlgorithm.crc32, String.format("%08x", crc32.getValue())); } } diff --git a/core/src/main/java/ch/cyberduck/core/io/MD5FastChecksumCompute.java b/core/src/main/java/ch/cyberduck/core/io/MD5FastChecksumCompute.java index b6ad041a28..d6026606a6 100644 --- a/core/src/main/java/ch/cyberduck/core/io/MD5FastChecksumCompute.java +++ b/core/src/main/java/ch/cyberduck/core/io/MD5FastChecksumCompute.java @@ -18,6 +18,7 @@ package ch.cyberduck.core.io; import ch.cyberduck.core.exception.BackgroundException; import ch.cyberduck.core.exception.ChecksumCanceledException; import ch.cyberduck.core.exception.ChecksumException; +import ch.cyberduck.core.exception.ConnectionCanceledException; import ch.cyberduck.core.transfer.TransferStatus; import org.apache.commons.io.IOUtils; @@ -41,12 +42,13 @@ public class MD5FastChecksumCompute extends AbstractChecksumCompute { this.normalize(in, status), status)); } - protected byte[] digest(final String algorithm, final InputStream in, final StreamCancelation cancelation) throws ChecksumException, ChecksumCanceledException { + protected byte[] digest(final String algorithm, final InputStream in, final StreamCancelation cancelation) throws ChecksumException, ConnectionCanceledException { final MD5 md = new MD5(); try { byte[] buffer = new byte[16384]; int bytesRead; while((bytesRead = in.read(buffer, 0, buffer.length)) != -1) { + cancelation.validate(); md.Update(buffer, 0, bytesRead); } } diff --git a/core/src/main/java/ch/cyberduck/core/logging/LoggerPrintStream.java b/core/src/main/java/ch/cyberduck/core/logging/LoggerPrintStream.java index f1fce77e31..2f1847586a 100644 --- a/core/src/main/java/ch/cyberduck/core/logging/LoggerPrintStream.java +++ b/core/src/main/java/ch/cyberduck/core/logging/LoggerPrintStream.java @@ -34,7 +34,7 @@ public class LoggerPrintStream extends PrintStream { private final Level level; public LoggerPrintStream() { - this(NullOutputStream.NULL_OUTPUT_STREAM); + this(NullOutputStream.INSTANCE); } public LoggerPrintStream(final OutputStream out) { diff --git a/core/src/main/java/ch/cyberduck/core/notification/NotificationAlertCallback.java b/core/src/main/java/ch/cyberduck/core/notification/NotificationAlertCallback.java index 669525b3fa..ff114d3917 100644 --- a/core/src/main/java/ch/cyberduck/core/notification/NotificationAlertCallback.java +++ b/core/src/main/java/ch/cyberduck/core/notification/NotificationAlertCallback.java @@ -23,12 +23,17 @@ import ch.cyberduck.core.Host; import ch.cyberduck.core.exception.BackgroundException; import ch.cyberduck.core.threading.AlertCallback; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; + public class NotificationAlertCallback implements AlertCallback { + private static final Logger log = LogManager.getLogger(NotificationAlertCallback.class); private final NotificationService notification = NotificationServiceFactory.get(); @Override public boolean alert(final Host bookmark, final BackgroundException failure) { + log.warn("Notify for failure {}", failure.toString()); notification.notify(BookmarkNameProvider.toString(bookmark), bookmark.getUuid(), failure.getMessage(), BookmarkNameProvider.toString(bookmark)); return false; } diff --git a/core/src/main/java/ch/cyberduck/core/preferences/HostPreferences.java b/core/src/main/java/ch/cyberduck/core/preferences/HostPreferences.java index 2901f10e0c..69c606e4f4 100644 --- a/core/src/main/java/ch/cyberduck/core/preferences/HostPreferences.java +++ b/core/src/main/java/ch/cyberduck/core/preferences/HostPreferences.java @@ -20,8 +20,6 @@ import ch.cyberduck.core.cache.LRUCache; import org.apache.commons.lang3.StringUtils; -import java.util.List; - public class HostPreferences implements PreferencesReader { private final LRUCache cache = LRUCache.usingLoader(this::loadProperty, 1000); @@ -40,6 +38,13 @@ public class HostPreferences implements PreferencesReader { this.proxy = proxy; } + /** + * Delete cached properties. + */ + public void clear() { + cache.clear(); + } + @Override public String getProperty(final String key) { final String value = cache.get(key); @@ -54,61 +59,7 @@ public class HostPreferences implements PreferencesReader { bookmark.setProperty(key, value); } - @Override - public List getList(final String key) { - final String value = cache.get(key); - if(StringUtils.equals(MISSING_PROPERTY, value)) { - return proxy.getList(key); - } - return PreferencesReader.toList(value); - } - - @Override - public int getInteger(final String key) { - final String value = cache.get(key); - if(StringUtils.equals(MISSING_PROPERTY, value)) { - return proxy.getInteger(key); - } - return PreferencesReader.toInteger(value); - } - - @Override - public float getFloat(final String key) { - final String value = cache.get(key); - if(StringUtils.equals(MISSING_PROPERTY, value)) { - return proxy.getFloat(key); - } - return PreferencesReader.toFloat(value); - } - - @Override - public long getLong(final String key) { - final String value = cache.get(key); - if(StringUtils.equals(MISSING_PROPERTY, value)) { - return proxy.getLong(key); - } - return PreferencesReader.toLong(value); - } - - @Override - public double getDouble(final String key) { - final String value = cache.get(key); - if(StringUtils.equals(MISSING_PROPERTY, value)) { - return proxy.getDouble(key); - } - return PreferencesReader.toDouble(value); - } - - @Override - public boolean getBoolean(final String key) { - final String value = cache.get(key); - if(StringUtils.equals(MISSING_PROPERTY, value)) { - return proxy.getBoolean(key); - } - return PreferencesReader.toBoolean(value); - } - - private String loadProperty(String key) { + private String loadProperty(final String key) { final String value = bookmark.getProperty(key); if(null == value) { return MISSING_PROPERTY; diff --git a/core/src/main/java/ch/cyberduck/core/preferences/Preferences.java b/core/src/main/java/ch/cyberduck/core/preferences/Preferences.java index 33c49d69ef..c9d7e3acb9 100755 --- a/core/src/main/java/ch/cyberduck/core/preferences/Preferences.java +++ b/core/src/main/java/ch/cyberduck/core/preferences/Preferences.java @@ -242,7 +242,7 @@ public abstract class Preferences implements Locales, PreferencesReader { if(defaults.exists()) { log.debug("Load defaults from {}", defaults); final Properties props = new Properties(); - try (final InputStream in = defaults.getInputStream()) { + try(final InputStream in = defaults.getInputStream()) { props.load(new InputStreamReader(in, StandardCharsets.UTF_8)); } catch(IllegalArgumentException | AccessDeniedException | IOException e) { @@ -411,74 +411,66 @@ public abstract class Preferences implements Locales, PreferencesReader { } protected void configureAppenders(final String level) { + final LoggerContext ctx = (LoggerContext) LogManager.getContext(false); + final Configuration config = ctx.getConfiguration(); + final Appender defaultAppender = this.getDefaultAppender(config, level); + defaultAppender.start(); + config.addAppender(defaultAppender); + config.getRootLogger().addAppender(defaultAppender, null, null); + final Appender auditAppender = this.getAuditAppender(config, level); + auditAppender.start(); + for(LoggerConfig logger : config.getLoggers().values()) { + if(StringUtils.startsWith(logger.getName(), "audit")) { + logger.addAppender(auditAppender, null, null); + } + } + ctx.updateLoggers(); + } + + private Appender getDefaultAppender(final Configuration config, final String level) { final String logfolder = LogDirectoryFinderFactory.get().find().getAbsolute(); final String appname = StringUtils.replaceChars(StringUtils.lowerCase(this.getProperty("application.name")), StringUtils.SPACE, StringUtils.EMPTY); final Local active = LocalFactory.get(logfolder, String.format("%s.log", appname)); final Local archives = LocalFactory.get(logfolder, String.format("%s-%%d{yyyy-MM-dd'T'HHmmss}.log.zip", appname)); - final LoggerContext ctx = (LoggerContext) LogManager.getContext(false); - final Configuration config = ctx.getConfiguration(); final DeleteAction deleteAction = DeleteAction.createDeleteAction(logfolder, false, 1, false, PathSortByModificationTime.createSorter(true), new PathCondition[]{ IfFileName.createNameCondition(String.format("%s-*.log.zip", appname), null, IfAccumulatedFileCount.createFileCountCondition(this.getInteger("logging.archives"))) }, null, new NullConfiguration()); - final Appender appender = RollingFileAppender.newBuilder() + return RollingFileAppender.newBuilder() .setName(RollingFileAppender.class.getName()) .withFileName(active.getAbsolute()) .withFilePattern(archives.getAbsolute()) .withPolicy(Level.DEBUG.toString().equals(level) ? SizeBasedTriggeringPolicy.createPolicy("100MB") : SizeBasedTriggeringPolicy.createPolicy("10MB")) .withStrategy(DefaultRolloverStrategy.newBuilder(). withCompressionLevelStr(String.valueOf(Deflater.BEST_COMPRESSION)). - withCustomActions(new Action[]{new AbstractAction() { - @Override - public boolean execute() { - log.info("Running version {}", getVersion()); - return true; - } - }, deleteAction}).build()) + withCustomActions(new Action[]{new ApplicationVersionAction(this), deleteAction}).build()) .setLayout(PatternLayout.newBuilder().withConfiguration(config).withPattern("%d [%t] %-5p %c - %m%n").withCharset(StandardCharsets.UTF_8).build()) .build(); - appender.start(); - config.addAppender(appender); - config.getRootLogger().addAppender(appender, null, null); - ctx.updateLoggers(); } - @Override - public List getList(final String property) { - final String value = this.getProperty(property); - return PreferencesReader.toList(value); - } - - @Override - public int getInteger(final String key) { - final String v = this.getProperty(key); - return PreferencesReader.toInteger(v); - } - - @Override - public float getFloat(final String key) { - final String v = this.getProperty(key); - return PreferencesReader.toFloat(v); - } - - @Override - public long getLong(final String key) { - final String v = this.getProperty(key); - return PreferencesReader.toLong(v); - } - - @Override - public double getDouble(final String key) { - final String v = this.getProperty(key); - return PreferencesReader.toDouble(v); - } - - @Override - public boolean getBoolean(final String key) { - final String v = this.getProperty(key); - return PreferencesReader.toBoolean(v); + private Appender getAuditAppender(final Configuration config, final String level) { + final String logfolder = LogDirectoryFinderFactory.get().find().getAbsolute(); + final String appname = StringUtils.replaceChars(StringUtils.lowerCase(this.getProperty("application.name")), StringUtils.SPACE, StringUtils.EMPTY); + final Local active = LocalFactory.get(logfolder, String.format("%s-audit.log", appname)); + final Local archives = LocalFactory.get(logfolder, String.format("%s-audit-%%d{yyyy-MM-dd'T'HHmmss}.log.zip", appname)); + final DeleteAction deleteAction = DeleteAction.createDeleteAction(logfolder, false, 1, false, + PathSortByModificationTime.createSorter(true), + new PathCondition[]{ + IfFileName.createNameCondition(String.format("%s-audit-*.log.zip", appname), null, IfAccumulatedFileCount.createFileCountCondition(this.getInteger("logging.archives"))) + }, + null, new NullConfiguration()); + return RollingFileAppender.newBuilder() + .setName(RollingFileAppender.class.getName()) + .withFileName(active.getAbsolute()) + .withFilePattern(archives.getAbsolute()) + .withPolicy(SizeBasedTriggeringPolicy.createPolicy("50MB")) + .withStrategy(DefaultRolloverStrategy.newBuilder(). + withCompressionLevelStr(String.valueOf(Deflater.BEST_COMPRESSION)). + withCustomActions(new Action[]{new ApplicationVersionAction(this), deleteAction}).build()) + .setLayout(PatternLayout.newBuilder().withConfiguration(config).withPattern("%d [%t] %-5p %c - %m%n").withCharset(StandardCharsets.UTF_8).build()) + .build(); } protected void setFactories() { @@ -548,7 +540,6 @@ public abstract class Preferences implements Locales, PreferencesReader { this.setDefault("factory.connectiontimeout.class", DefaultConnectionTimeout.class.getName()); this.setDefault("factory.hardwareaddress.class", NetworkInterfaceHardwareAddress.class.getName()); this.setDefault("factory.authorizationcodeprovider.class", "ch.cyberduck.core.oauth.BrowserOAuth2AuthorizationCodeProvider"); - this.setDefault("factory.s3.pathcontainerservice.class", "ch.cyberduck.core.s3.S3PathContainerService"); } /** @@ -603,4 +594,25 @@ public abstract class Preferences implements Locales, PreferencesReader { this.getProperty("application.revision"), this.getProperty("application.hash")); } + + public String getSystem() { + return String.format("%s %s (%s)", + this.getProperty("os.name"), + this.getProperty("os.version"), + this.getProperty("os.arch")); + } + + private static final class ApplicationVersionAction extends AbstractAction { + private final Preferences preferences; + + public ApplicationVersionAction(final Preferences preferences) { + this.preferences = preferences; + } + + @Override + public boolean execute() { + log.info("Running version {} on {}", preferences.getVersion(), preferences.getSystem()); + return true; + } + } } diff --git a/core/src/main/java/ch/cyberduck/core/preferences/PreferencesFactory.java b/core/src/main/java/ch/cyberduck/core/preferences/PreferencesFactory.java index ecaf39ead7..a27fed14fb 100644 --- a/core/src/main/java/ch/cyberduck/core/preferences/PreferencesFactory.java +++ b/core/src/main/java/ch/cyberduck/core/preferences/PreferencesFactory.java @@ -41,7 +41,7 @@ public final class PreferencesFactory { preferences.setDefaults(LocalFactory.get(SupportDirectoryFinderFactory.get().find(), "default.properties")); preferences.configureLogging(preferences.getProperty("logging")); final Logger log = LogManager.getLogger(PreferencesFactory.class); - log.info("Running version {}", preferences.getVersion()); + log.info("Running version {} on {}", preferences.getVersion(), preferences.getSystem()); } public static synchronized Preferences get() { diff --git a/core/src/main/java/ch/cyberduck/core/preferences/PreferencesReader.java b/core/src/main/java/ch/cyberduck/core/preferences/PreferencesReader.java index 48e52f706e..8ebc3feabf 100644 --- a/core/src/main/java/ch/cyberduck/core/preferences/PreferencesReader.java +++ b/core/src/main/java/ch/cyberduck/core/preferences/PreferencesReader.java @@ -28,6 +28,17 @@ import java.util.Map; public interface PreferencesReader { Logger log = LogManager.getLogger(PreferencesReader.class); + default String getProperty(final String... keys) { + for(String key : keys) { + final String value = this.getProperty(key); + if(null == value) { + continue; + } + return value; + } + return null; + } + /** * Give value in user settings or default value if not customized. * @@ -36,11 +47,30 @@ public interface PreferencesReader { */ String getProperty(String key); - /** - * @param property The property to query. - * @return The configured values determined by a whitespace separator. - */ - List getList(String property); + default List getList(final String key) { + return PreferencesReader.toList(this.getProperty(key)); + } + + default int getInteger(final String key) { + return PreferencesReader.toInteger(this.getProperty(key)); + } + + default float getFloat(final String key) { + return PreferencesReader.toFloat(this.getProperty(key)); + } + + default long getLong(final String key) { + return PreferencesReader.toLong(this.getProperty(key)); + } + + default double getDouble(final String key) { + return PreferencesReader.toDouble(this.getProperty(key)); + } + + default boolean getBoolean(final String key) { + return PreferencesReader.toBoolean(this.getProperty(key)); + } + default Map getMap(final String property) { final List list = this.getList(property); @@ -76,8 +106,6 @@ public interface PreferencesReader { return Arrays.asList(value.split("(? description; /** * Download and parse profile @@ -46,8 +41,4 @@ public interface ProfilesFinder { return description; }; } - - default void cleanup() { - // - } } diff --git a/core/src/main/java/ch/cyberduck/core/profiles/ProfilesWorkerBackgroundAction.java b/core/src/main/java/ch/cyberduck/core/profiles/ProfilesWorkerBackgroundAction.java index 4a8cc9e83f..d49959dac1 100644 --- a/core/src/main/java/ch/cyberduck/core/profiles/ProfilesWorkerBackgroundAction.java +++ b/core/src/main/java/ch/cyberduck/core/profiles/ProfilesWorkerBackgroundAction.java @@ -29,7 +29,7 @@ public class ProfilesWorkerBackgroundAction extends WorkerBackgroundAction match = matcher.compare(available, l); if(match.isPresent()) { // Found matching checksum for profile in remote list which is not marked as latest version - log.warn("Override {} with latest profile verison {}", l, match); - // Remove previous version - l.getProfile().ifPresent(registry::unregister); - // Register updated profile by copying temporary file to application support - match.get().getFile().ifPresent(value -> { - final Local copy = registry.register(value); - if(null != copy) { - final LocalProfileDescription d = new LocalProfileDescription(registry, copy); - log.debug("Add synched profile {}", d); - result.add(d); - visitor.visit(d); - } - }); + final ProfileDescription latest = match.get(); + if(latest.isLatest()) { + log.warn("Override {} with latest profile version {}", l, latest); + // Remove previous version + l.getProfile().ifPresent(registry::unregister); + // Register updated profile by copying temporary file to application support + latest.getFile().ifPresent(value -> { + final Local copy = registry.register(value); + if(null != copy) { + final LocalProfileDescription d = new LocalProfileDescription(registry, copy); + log.debug("Add synched profile {}", d); + result.add(d); + visitor.visit(d); + } + }); + } } else { log.debug("Add local only profile {}", l); @@ -113,8 +116,6 @@ public class ProtocolFactoryProfilesSynchronizer implements ProfilesSynchronizer } } }); - local.cleanup(); - remote.cleanup(); return result; } } diff --git a/core/src/main/java/ch/cyberduck/core/profiles/RemoteProfilesFinder.java b/core/src/main/java/ch/cyberduck/core/profiles/RemoteProfilesFinder.java index d6a16fc8ed..c8cf0cd38f 100644 --- a/core/src/main/java/ch/cyberduck/core/profiles/RemoteProfilesFinder.java +++ b/core/src/main/java/ch/cyberduck/core/profiles/RemoteProfilesFinder.java @@ -16,20 +16,21 @@ package ch.cyberduck.core.profiles; */ import ch.cyberduck.core.AttributedList; +import ch.cyberduck.core.DefaultPathAttributes; import ch.cyberduck.core.DisabledConnectionCallback; import ch.cyberduck.core.DisabledListProgressListener; import ch.cyberduck.core.DisabledProgressListener; import ch.cyberduck.core.Filter; import ch.cyberduck.core.ListService; import ch.cyberduck.core.Local; +import ch.cyberduck.core.LocalFactory; import ch.cyberduck.core.Path; import ch.cyberduck.core.PathAttributes; import ch.cyberduck.core.ProtocolFactory; import ch.cyberduck.core.Session; import ch.cyberduck.core.exception.BackgroundException; import ch.cyberduck.core.features.Read; -import ch.cyberduck.core.local.TemporaryFileService; -import ch.cyberduck.core.local.TemporaryFileServiceFactory; +import ch.cyberduck.core.preferences.TemporaryApplicationResourcesFinder; import ch.cyberduck.core.shared.DefaultPathHomeFeature; import ch.cyberduck.core.shared.DelegatingHomeFeature; import ch.cyberduck.core.transfer.TransferPathFilter; @@ -53,11 +54,11 @@ import java.util.stream.Collectors; public class RemoteProfilesFinder implements ProfilesFinder { private static final Logger log = LogManager.getLogger(RemoteProfilesFinder.class); - private final TemporaryFileService temp = TemporaryFileServiceFactory.get(); private final ProtocolFactory protocols; private final Session session; private final TransferPathFilter comparison; private final Filter filter; + private final Local temporary; public RemoteProfilesFinder(final Session session) { this(ProtocolFactory.get(), session); @@ -78,11 +79,13 @@ public class RemoteProfilesFinder implements ProfilesFinder { this.session = session; this.comparison = comparison; this.filter = filter; + this.temporary = LocalFactory.get(new TemporaryApplicationResourcesFinder().find(), "profiles"); } @Override public Set find(final Visitor visitor) throws BackgroundException { log.info("Fetch profiles from {}", session.getHost()); + temporary.mkdir(); final AttributedList list = session.getFeature(ListService.class).list(new DelegatingHomeFeature( new DefaultPathHomeFeature(session.getHost())).find(), new DisabledListProgressListener()); return list.filter(filter).toStream().map(file -> visitor.visit(new RemoteProfileDescription(protocols, file, @@ -90,17 +93,18 @@ public class RemoteProfilesFinder implements ProfilesFinder { @Override protected Local initialize() throws ConcurrentException { try { - final Local local = temp.create("profiles", file); + final Local local = LocalFactory.get(temporary, file.getName()); if(comparison.accept(file, local, new TransferStatus().setExists(true), new DisabledProgressListener())) { final Read read = session.getFeature(Read.class); log.info("Download profile {}", file); // Read latest version - try (InputStream in = read.read(file.withAttributes(new PathAttributes(file.attributes()) + try(InputStream in = read.read(file.withAttributes(new DefaultPathAttributes(file.attributes()) // Read latest version .setVersionId(null)), new TransferStatus().setLength(TransferStatus.UNKNOWN_LENGTH), new DisabledConnectionCallback()); OutputStream out = local.getOutputStream(false)) { IOUtils.copy(in, out); } } + // Skip download if previously cached return local; } catch(BackgroundException | IOException e) { diff --git a/core/src/main/java/ch/cyberduck/core/proxy/ProxySocketFactory.java b/core/src/main/java/ch/cyberduck/core/proxy/ProxySocketFactory.java index da2cf0022c..faffa3aeaf 100644 --- a/core/src/main/java/ch/cyberduck/core/proxy/ProxySocketFactory.java +++ b/core/src/main/java/ch/cyberduck/core/proxy/ProxySocketFactory.java @@ -17,6 +17,7 @@ package ch.cyberduck.core.proxy; * Bug fixes, suggestions and comments should be sent to feedback@cyberduck.ch */ +import ch.cyberduck.core.ConnectionTimeoutFactory; import ch.cyberduck.core.Host; import ch.cyberduck.core.socket.DefaultSocketConfigurator; import ch.cyberduck.core.socket.HttpProxySocketFactory; @@ -47,7 +48,7 @@ public class ProxySocketFactory extends SocketFactory { Arrays.asList(Proxy.Type.DIRECT, Proxy.Type.SOCKS, Proxy.Type.HTTP, Proxy.Type.HTTPS)); public ProxySocketFactory(final Host host) { - this(host, new DefaultSocketConfigurator()); + this(host, new DefaultSocketConfigurator(ConnectionTimeoutFactory.get(host))); } public ProxySocketFactory(final Host host, @@ -57,7 +58,7 @@ public class ProxySocketFactory extends SocketFactory { public ProxySocketFactory(final Host host, final ProxyFinder proxyFinder) { - this(host, new DefaultSocketConfigurator(), proxyFinder); + this(host, new DefaultSocketConfigurator(ConnectionTimeoutFactory.get(host)), proxyFinder); } public ProxySocketFactory(final Host host, @@ -107,7 +108,10 @@ public class ProxySocketFactory extends SocketFactory { } private IOException failure(final String target, final IllegalArgumentException e) { - return new ConnectException(String.format("Unsupported proxy type for target %s", target)); + log.error("Failed to create socket for target {}", target, e); + final ConnectException exception = new ConnectException(String.format("Unsupported proxy type for target %s", target)); + exception.initCause(e); + return exception; } @Override diff --git a/core/src/main/java/ch/cyberduck/core/serializer/HostDictionary.java b/core/src/main/java/ch/cyberduck/core/serializer/HostDictionary.java index 733c8b63a9..8d038c94df 100644 --- a/core/src/main/java/ch/cyberduck/core/serializer/HostDictionary.java +++ b/core/src/main/java/ch/cyberduck/core/serializer/HostDictionary.java @@ -139,7 +139,7 @@ public class HostDictionary { if(transferObj != null) { final Host.TransferType transfer = Host.TransferType.valueOf(transferObj.toString()); if(PreferencesFactory.get().getList("queue.transfer.type.enabled").contains(transfer.name())) { - bookmark.setTransfer(transfer); + bookmark.setTransferType(transfer); } } else { @@ -147,7 +147,7 @@ public class HostDictionary { Object connObj = dict.stringForKey("Maximum Connections"); if(connObj != null) { if(1 == Integer.parseInt(connObj.toString())) { - bookmark.setTransfer(Host.TransferType.browser); + bookmark.setTransferType(Host.TransferType.browser); } } } diff --git a/core/src/main/java/ch/cyberduck/core/serializer/PathAttributesDictionary.java b/core/src/main/java/ch/cyberduck/core/serializer/PathAttributesDictionary.java index 0ad7f805a2..a55f7ed81a 100644 --- a/core/src/main/java/ch/cyberduck/core/serializer/PathAttributesDictionary.java +++ b/core/src/main/java/ch/cyberduck/core/serializer/PathAttributesDictionary.java @@ -18,6 +18,7 @@ package ch.cyberduck.core.serializer; * feedback@cyberduck.ch */ +import ch.cyberduck.core.DefaultPathAttributes; import ch.cyberduck.core.DescriptiveUrl; import ch.cyberduck.core.DeserializerFactory; import ch.cyberduck.core.PathAttributes; @@ -42,7 +43,7 @@ public class PathAttributesDictionary { public PathAttributes deserialize(final T serialized) { final Deserializer dict = factory.create(serialized); - final PathAttributes attributes = new PathAttributes(); + final PathAttributes attributes = new DefaultPathAttributes(); final String sizeObj = dict.stringForKey("Size"); if(sizeObj != null) { attributes.setSize(Long.parseLong(sizeObj)); diff --git a/core/src/main/java/ch/cyberduck/core/shared/BufferWriteFeature.java b/core/src/main/java/ch/cyberduck/core/shared/BufferWriteFeature.java index ac9f48e424..30283e5f87 100644 --- a/core/src/main/java/ch/cyberduck/core/shared/BufferWriteFeature.java +++ b/core/src/main/java/ch/cyberduck/core/shared/BufferWriteFeature.java @@ -74,7 +74,7 @@ public class BufferWriteFeature implements MultipartWrite { // through StreamCopier when writing to buffer final TransferStatus range = new TransferStatus(status).setLength(buffer.length()).setAppend(false); if(0L == buffer.length()) { - session._getFeature(Touch.class).touch(file, new TransferStatus()); + session._getFeature(Touch.class).touch(session._getFeature(Write.class), file, new TransferStatus()); } else { final StatusOutputStream out = session._getFeature(Write.class).write(file, range, callback); diff --git a/core/src/main/java/ch/cyberduck/core/shared/DefaultCopyFeature.java b/core/src/main/java/ch/cyberduck/core/shared/DefaultCopyFeature.java index d4fdb5a5b0..937f98d6fe 100644 --- a/core/src/main/java/ch/cyberduck/core/shared/DefaultCopyFeature.java +++ b/core/src/main/java/ch/cyberduck/core/shared/DefaultCopyFeature.java @@ -16,12 +16,10 @@ package ch.cyberduck.core.shared; */ import ch.cyberduck.core.ConnectionCallback; -import ch.cyberduck.core.LocaleFactory; import ch.cyberduck.core.Path; import ch.cyberduck.core.PathAttributes; import ch.cyberduck.core.Session; import ch.cyberduck.core.exception.BackgroundException; -import ch.cyberduck.core.exception.UnsupportedException; import ch.cyberduck.core.features.Copy; import ch.cyberduck.core.features.MultipartWrite; import ch.cyberduck.core.features.Read; @@ -35,8 +33,6 @@ import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; import java.io.InputStream; -import java.text.MessageFormat; -import java.util.Optional; public class DefaultCopyFeature implements Copy { private static final Logger log = LogManager.getLogger(DefaultCopyFeature.class); @@ -69,21 +65,6 @@ public class DefaultCopyFeature implements Copy { return target; } - @Override - public void preflight(final Path source, final Optional optional) throws BackgroundException { - Copy.super.preflight(source, optional); - if(source.isDirectory()) { - throw new UnsupportedException(MessageFormat.format(LocaleFactory.localizedString("Cannot copy {0}", "Error"), - source.getName())).withFile(source); - } - if(optional.isPresent()) { - if(optional.get().isDirectory()) { - throw new UnsupportedException(MessageFormat.format(LocaleFactory.localizedString("Cannot copy {0}", "Error"), - source.getName())).withFile(source); - } - } - } - @Override public DefaultCopyFeature withTarget(final Session session) { to = session; diff --git a/core/src/main/java/ch/cyberduck/core/shared/DefaultDownloadFeature.java b/core/src/main/java/ch/cyberduck/core/shared/DefaultDownloadFeature.java index d48de1fd78..f551fea154 100644 --- a/core/src/main/java/ch/cyberduck/core/shared/DefaultDownloadFeature.java +++ b/core/src/main/java/ch/cyberduck/core/shared/DefaultDownloadFeature.java @@ -20,6 +20,7 @@ package ch.cyberduck.core.shared; import ch.cyberduck.core.ConnectionCallback; import ch.cyberduck.core.Local; import ch.cyberduck.core.Path; +import ch.cyberduck.core.Session; import ch.cyberduck.core.exception.BackgroundException; import ch.cyberduck.core.features.Download; import ch.cyberduck.core.features.Read; @@ -34,16 +35,16 @@ import java.io.OutputStream; public class DefaultDownloadFeature implements Download { - private Read reader; + private final Session session; - public DefaultDownloadFeature(final Read reader) { - this.reader = reader; + public DefaultDownloadFeature(final Session session) { + this.session = session; } @Override - public void download(final Path file, final Local local, final BandwidthThrottle throttle, final StreamListener listener, + public void download(final Read read, final Path file, final Local local, final BandwidthThrottle throttle, final StreamListener listener, final TransferStatus status, final ConnectionCallback callback) throws BackgroundException { - final InputStream in = reader.read(file, status, callback); + final InputStream in = read.read(file, status, callback); final OutputStream out = local.getOutputStream(!status.isSegment() && status.isAppend()); new StreamCopier(status, status) .withOffset(0L) @@ -54,12 +55,6 @@ public class DefaultDownloadFeature implements Download { @Override public boolean offset(final Path file) throws BackgroundException { - return reader.offset(file); - } - - @Override - public Download withReader(final Read reader) { - this.reader = reader; - return this; + return session.getFeature(Read.class).offset(file); } } diff --git a/core/src/main/java/ch/cyberduck/core/shared/DefaultTouchFeature.java b/core/src/main/java/ch/cyberduck/core/shared/DefaultTouchFeature.java index 9a2eff282b..e7fa1593b1 100644 --- a/core/src/main/java/ch/cyberduck/core/shared/DefaultTouchFeature.java +++ b/core/src/main/java/ch/cyberduck/core/shared/DefaultTouchFeature.java @@ -22,6 +22,7 @@ import ch.cyberduck.core.DefaultIOExceptionMappingService; import ch.cyberduck.core.DisabledConnectionCallback; import ch.cyberduck.core.Path; import ch.cyberduck.core.PathAttributes; +import ch.cyberduck.core.Session; import ch.cyberduck.core.exception.BackgroundException; import ch.cyberduck.core.features.Touch; import ch.cyberduck.core.features.Write; @@ -36,33 +37,24 @@ import java.io.IOException; public class DefaultTouchFeature implements Touch { private static final Logger log = LogManager.getLogger(DefaultTouchFeature.class); - protected Write write; - - public DefaultTouchFeature(final Write writer) { - this.write = writer; + public DefaultTouchFeature(final Session session) { } @Override - public Path touch(final Path file, final TransferStatus status) throws BackgroundException { + public Path touch(final Write writer, final Path file, final TransferStatus status) throws BackgroundException { try { - final StatusOutputStream writer = write.write(file, status.setLength( + final StatusOutputStream out = writer.write(file, status.setLength( TransferStatus.UNKNOWN_LENGTH == status.getLength() ? 0L : status.getLength()), new DisabledConnectionCallback()); - writer.close(); + out.close(); if(!PathAttributes.EMPTY.equals(status.getResponse())) { log.debug("Received reply {} for creating file {}", status.getResponse(), file); return new Path(file).withAttributes(status.getResponse()); } - log.warn("Missing status from writer {}", writer); + log.warn("Missing status from writer {}", out); return file; } catch(IOException e) { throw new DefaultIOExceptionMappingService().map("Cannot create {0}", e, file); } } - - @Override - public DefaultTouchFeature withWriter(final Write write) { - this.write = write; - return this; - } } diff --git a/core/src/main/java/ch/cyberduck/core/shared/DefaultUploadFeature.java b/core/src/main/java/ch/cyberduck/core/shared/DefaultUploadFeature.java index 4ab897db73..7cc67bec12 100644 --- a/core/src/main/java/ch/cyberduck/core/shared/DefaultUploadFeature.java +++ b/core/src/main/java/ch/cyberduck/core/shared/DefaultUploadFeature.java @@ -22,6 +22,7 @@ import ch.cyberduck.core.ConnectionCallback; import ch.cyberduck.core.Local; import ch.cyberduck.core.Path; import ch.cyberduck.core.ProgressListener; +import ch.cyberduck.core.Session; import ch.cyberduck.core.exception.BackgroundException; import ch.cyberduck.core.features.Upload; import ch.cyberduck.core.features.Write; @@ -36,18 +37,18 @@ import java.io.InputStream; public class DefaultUploadFeature implements Upload { - private Write writer; + private final Session session; - public DefaultUploadFeature(final Write writer) { - this.writer = writer; + public DefaultUploadFeature(final Session session) { + this.session = session; } @Override - public Reply upload(final Path file, final Local local, final BandwidthThrottle throttle, + public Reply upload(final Write 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 InputStream in = local.getInputStream(); - final StatusOutputStream out = writer.write(file, status, callback); + final StatusOutputStream out = write.write(file, status, callback); new StreamCopier(status, status) .withOffset(status.getOffset()) .withLimit(status.getLength()) @@ -56,9 +57,4 @@ public class DefaultUploadFeature implements Upload { return out.getStatus(); } - @Override - public Upload withWriter(final Write writer) { - this.writer = writer; - return this; - } } diff --git a/core/src/main/java/ch/cyberduck/core/shared/DefaultVersioningFeature.java b/core/src/main/java/ch/cyberduck/core/shared/DefaultVersioningFeature.java index bc9d40476e..258d89ed7b 100644 --- a/core/src/main/java/ch/cyberduck/core/shared/DefaultVersioningFeature.java +++ b/core/src/main/java/ch/cyberduck/core/shared/DefaultVersioningFeature.java @@ -35,6 +35,7 @@ import ch.cyberduck.core.features.Directory; import ch.cyberduck.core.features.Find; import ch.cyberduck.core.features.Move; import ch.cyberduck.core.features.Versioning; +import ch.cyberduck.core.features.Write; import ch.cyberduck.core.preferences.HostPreferencesFactory; import ch.cyberduck.core.transfer.TransferStatus; import ch.cyberduck.ui.comparator.FilenameComparator; @@ -110,7 +111,7 @@ public class DefaultVersioningFeature implements Versioning { final Path directory = version.getParent(); if(!session.getFeature(Find.class).find(directory)) { log.debug("Create directory {} for versions", directory); - session.getFeature(Directory.class).mkdir(directory, new TransferStatus()); + session.getFeature(Directory.class).mkdir(session.getFeature(Write.class), directory, new TransferStatus()); } log.debug("Rename existing file {} to {}", file, version); feature.move(file, version, @@ -153,7 +154,7 @@ public class DefaultVersioningFeature implements Versioning { public void cleanup(final Path file, final ConnectionCallback callback) throws BackgroundException { final Delete delete = session.getFeature(Delete.class); if(file.isDirectory()) { - if(!delete.isRecursive()) { + if(!delete.features(file).contains(Delete.Flags.recursive)) { return; } } diff --git a/core/src/main/java/ch/cyberduck/core/shared/DisabledBulkFeature.java b/core/src/main/java/ch/cyberduck/core/shared/DisabledBulkFeature.java index e196b38318..3bd671d088 100644 --- a/core/src/main/java/ch/cyberduck/core/shared/DisabledBulkFeature.java +++ b/core/src/main/java/ch/cyberduck/core/shared/DisabledBulkFeature.java @@ -18,7 +18,6 @@ package ch.cyberduck.core.shared; import ch.cyberduck.core.ConnectionCallback; import ch.cyberduck.core.exception.BackgroundException; import ch.cyberduck.core.features.Bulk; -import ch.cyberduck.core.features.Delete; import ch.cyberduck.core.transfer.Transfer; import ch.cyberduck.core.transfer.TransferItem; import ch.cyberduck.core.transfer.TransferStatus; @@ -31,11 +30,6 @@ public class DisabledBulkFeature implements Bulk> withDelete(final Delete delete) { - return this; - } - @Override public void post(final Transfer.Type type, final Map files, final ConnectionCallback callback) throws BackgroundException { // diff --git a/core/src/main/java/ch/cyberduck/core/shared/DisabledHeadersFeature.java b/core/src/main/java/ch/cyberduck/core/shared/DisabledHeadersFeature.java index 8d931d40ca..cd1ea4cb2c 100644 --- a/core/src/main/java/ch/cyberduck/core/shared/DisabledHeadersFeature.java +++ b/core/src/main/java/ch/cyberduck/core/shared/DisabledHeadersFeature.java @@ -24,7 +24,7 @@ import java.util.Map; public class DisabledHeadersFeature implements Headers { @Override - public Map getDefault() { + public Map getDefault(final Path file) { return Collections.emptyMap(); } diff --git a/core/src/main/java/ch/cyberduck/core/socket/DefaultSocketConfigurator.java b/core/src/main/java/ch/cyberduck/core/socket/DefaultSocketConfigurator.java index a64eb0a813..8849d4a8b4 100644 --- a/core/src/main/java/ch/cyberduck/core/socket/DefaultSocketConfigurator.java +++ b/core/src/main/java/ch/cyberduck/core/socket/DefaultSocketConfigurator.java @@ -57,7 +57,7 @@ public class DefaultSocketConfigurator implements SocketConfigurator { socket.setSoTimeout(timeout * 1000); if(preferences.getBoolean("connection.socket.linger")) { // The setting only affects socket close. Make sure closing SSL socket does not hang because close_notify cannot be sent. - socket.setSoLinger(true, timeout); + socket.setSoLinger(true, preferences.getInteger("connection.socket.linger.timeout")); } if(preferences.getBoolean("connection.socket.keepalive")) { socket.setKeepAlive(true); diff --git a/core/src/main/java/ch/cyberduck/core/ssl/X509KeyManager.java b/core/src/main/java/ch/cyberduck/core/ssl/X509KeyManager.java index 5c3e52b988..eb8fca8812 100644 --- a/core/src/main/java/ch/cyberduck/core/ssl/X509KeyManager.java +++ b/core/src/main/java/ch/cyberduck/core/ssl/X509KeyManager.java @@ -19,6 +19,7 @@ package ch.cyberduck.core.ssl; */ import java.security.Principal; +import java.security.PrivateKey; import java.security.cert.X509Certificate; import java.util.List; @@ -40,4 +41,13 @@ public interface X509KeyManager extends javax.net.ssl.X509KeyManager { * @param issuers Acceptable CA issuer subject names or null if it does not matter which issuers are used */ X509Certificate getCertificate(String alias, String[] keyTypes, Principal[] issuers); + + /** + * Find private key for certificate to use for authentication with mutual TLS + * + * @param alias Certificate alias + * @return Null when not found + */ + @Override + PrivateKey getPrivateKey(String alias); } diff --git a/core/src/main/java/ch/cyberduck/core/synchronization/DefaultComparePathFilter.java b/core/src/main/java/ch/cyberduck/core/synchronization/DefaultComparePathFilter.java index 7d034c3edd..156a00e1f2 100644 --- a/core/src/main/java/ch/cyberduck/core/synchronization/DefaultComparePathFilter.java +++ b/core/src/main/java/ch/cyberduck/core/synchronization/DefaultComparePathFilter.java @@ -18,6 +18,7 @@ package ch.cyberduck.core.synchronization; * dkocher@cyberduck.ch */ +import ch.cyberduck.core.DefaultPathAttributes; import ch.cyberduck.core.Local; import ch.cyberduck.core.LocaleFactory; import ch.cyberduck.core.Path; @@ -67,7 +68,7 @@ public class DefaultComparePathFilter implements ComparePathFilter { return Comparison.equal; } final PathAttributes remote = attribute.find(file); - final PathAttributes current = new PathAttributes() + final PathAttributes current = new DefaultPathAttributes() .setModificationDate(local.attributes().getModificationDate()) .setSize(local.attributes().getSize()); // We must always compare the size because the download filter will have already created a temporary 0 byte file diff --git a/core/src/main/java/ch/cyberduck/core/threading/BackgroundActionListener.java b/core/src/main/java/ch/cyberduck/core/threading/BackgroundActionListener.java index 64dab90320..5eb4c00985 100644 --- a/core/src/main/java/ch/cyberduck/core/threading/BackgroundActionListener.java +++ b/core/src/main/java/ch/cyberduck/core/threading/BackgroundActionListener.java @@ -18,9 +18,9 @@ package ch.cyberduck.core.threading; */ public interface BackgroundActionListener { - void start(BackgroundAction action); + void start(BackgroundAction action); - void cancel(BackgroundAction action); + void cancel(BackgroundAction action); - void stop(BackgroundAction action); + void stop(BackgroundAction action); } diff --git a/core/src/main/java/ch/cyberduck/core/threading/BackgroundActionRegistry.java b/core/src/main/java/ch/cyberduck/core/threading/BackgroundActionRegistry.java index d705db0e52..fe68788063 100644 --- a/core/src/main/java/ch/cyberduck/core/threading/BackgroundActionRegistry.java +++ b/core/src/main/java/ch/cyberduck/core/threading/BackgroundActionRegistry.java @@ -28,7 +28,7 @@ import java.util.LinkedHashSet; import java.util.Objects; import java.util.Set; -public final class BackgroundActionRegistry extends Collection implements BackgroundActionListener { +public final class BackgroundActionRegistry extends Collection> implements BackgroundActionListener { private static final Logger log = LogManager.getLogger(BackgroundActionRegistry.class); private static BackgroundActionRegistry global = null; @@ -44,7 +44,7 @@ public final class BackgroundActionRegistry extends Collection } } - private final Set running = new LinkedHashSet<>(); + private final Set> running = new LinkedHashSet<>(); public BackgroundActionRegistry() { // @@ -55,8 +55,8 @@ public final class BackgroundActionRegistry extends Collection /** * @return The currently running background action. Null if none is currently running. */ - public synchronized BackgroundAction getCurrent() { - final Iterator iter = running.iterator(); + public synchronized BackgroundAction getCurrent() { + final Iterator> iter = running.iterator(); if(iter.hasNext()) { return iter.next(); } @@ -64,17 +64,17 @@ public final class BackgroundActionRegistry extends Collection } @Override - public synchronized void start(final BackgroundAction action) { + public synchronized void start(final BackgroundAction action) { running.add(action); } @Override - public synchronized void stop(final BackgroundAction action) { + public synchronized void stop(final BackgroundAction action) { this.remove(action); } @Override - public synchronized void cancel(final BackgroundAction action) { + public synchronized void cancel(final BackgroundAction action) { if(action.isRunning()) { log.debug("Skip removing action {} currently running", action); } @@ -90,7 +90,7 @@ public final class BackgroundActionRegistry extends Collection * @return True */ @Override - public synchronized boolean add(final BackgroundAction action) { + public synchronized boolean add(final BackgroundAction action) { action.addListener(this); return super.add(action); } @@ -102,7 +102,7 @@ public final class BackgroundActionRegistry extends Collection log.warn("Failure finding action {} in running tasks", action); } if(super.remove(action)) { - ((BackgroundAction) action).removeListener(this); + ((BackgroundAction) action).removeListener(this); } return true; } diff --git a/core/src/main/java/ch/cyberduck/core/threading/DisabledAlertCallback.java b/core/src/main/java/ch/cyberduck/core/threading/DisabledAlertCallback.java index a7bdec88e3..b8764b9d63 100644 --- a/core/src/main/java/ch/cyberduck/core/threading/DisabledAlertCallback.java +++ b/core/src/main/java/ch/cyberduck/core/threading/DisabledAlertCallback.java @@ -29,7 +29,7 @@ public class DisabledAlertCallback implements AlertCallback { @Override public boolean alert(final Host host, final BackgroundException failure) { - log.warn("Ignore failure {}", failure.getMessage()); + log.warn("Ignore failure {}", failure.toString()); return false; } } diff --git a/core/src/main/java/ch/cyberduck/core/transfer/CopyTransfer.java b/core/src/main/java/ch/cyberduck/core/transfer/CopyTransfer.java index fd29294d67..3ed2d099e9 100644 --- a/core/src/main/java/ch/cyberduck/core/transfer/CopyTransfer.java +++ b/core/src/main/java/ch/cyberduck/core/transfer/CopyTransfer.java @@ -24,6 +24,7 @@ import ch.cyberduck.core.features.Bulk; import ch.cyberduck.core.features.Copy; import ch.cyberduck.core.features.Directory; import ch.cyberduck.core.features.Find; +import ch.cyberduck.core.features.Write; import ch.cyberduck.core.io.BandwidthThrottle; import ch.cyberduck.core.io.StreamListener; import ch.cyberduck.core.preferences.PreferencesFactory; @@ -237,7 +238,7 @@ public class CopyTransfer extends Transfer { if(source.isDirectory()) { if(!segment.isExists()) { final Directory feature = destination.getFeature(Directory.class); - feature.mkdir(mapping.get(source), segment); + feature.mkdir(destination.getFeature(Write.class), mapping.get(source), segment); segment.setComplete(); } } diff --git a/core/src/main/java/ch/cyberduck/core/transfer/DownloadTransfer.java b/core/src/main/java/ch/cyberduck/core/transfer/DownloadTransfer.java index 39c5f86d29..3a27f29254 100644 --- a/core/src/main/java/ch/cyberduck/core/transfer/DownloadTransfer.java +++ b/core/src/main/java/ch/cyberduck/core/transfer/DownloadTransfer.java @@ -24,6 +24,7 @@ import ch.cyberduck.core.exception.TransferCanceledException; import ch.cyberduck.core.features.AttributesFinder; import ch.cyberduck.core.features.Bulk; import ch.cyberduck.core.features.Download; +import ch.cyberduck.core.features.Read; import ch.cyberduck.core.filter.DownloadFilter; import ch.cyberduck.core.io.BandwidthThrottle; import ch.cyberduck.core.io.StreamListener; @@ -296,8 +297,8 @@ public class DownloadTransfer extends Transfer { new DefaultLocalDirectoryFeature().mkdir(folder); } // Transfer - final Download download = source.getFeature(Download.class); - download.download(file, local, bandwidth, listener, segment, prompt); + source.getFeature(Download.class).download(source.getFeature(Read.class), + file, local, bandwidth, listener, segment, prompt); } } diff --git a/core/src/main/java/ch/cyberduck/core/transfer/Transfer.java b/core/src/main/java/ch/cyberduck/core/transfer/Transfer.java index 6d6ad909dc..b185944a94 100644 --- a/core/src/main/java/ch/cyberduck/core/transfer/Transfer.java +++ b/core/src/main/java/ch/cyberduck/core/transfer/Transfer.java @@ -168,7 +168,7 @@ public abstract class Transfer implements Serializable { * @param roots List of files to add to transfer */ public Transfer(final Host host, final List roots, final BandwidthThrottle bandwidth) { - this.host = host; + this.host = new Host(host); this.roots.addAll(roots); this.bandwidth = bandwidth; } diff --git a/core/src/main/java/ch/cyberduck/core/transfer/UploadTransfer.java b/core/src/main/java/ch/cyberduck/core/transfer/UploadTransfer.java index e5c738d031..61844e7e38 100644 --- a/core/src/main/java/ch/cyberduck/core/transfer/UploadTransfer.java +++ b/core/src/main/java/ch/cyberduck/core/transfer/UploadTransfer.java @@ -39,6 +39,7 @@ import ch.cyberduck.core.features.Find; import ch.cyberduck.core.features.Symlink; import ch.cyberduck.core.features.Upload; import ch.cyberduck.core.features.Versioning; +import ch.cyberduck.core.features.Write; import ch.cyberduck.core.filter.UploadFilter; import ch.cyberduck.core.io.BandwidthThrottle; import ch.cyberduck.core.io.StreamListener; @@ -241,7 +242,7 @@ public class UploadTransfer extends Transfer { status.validate(); progress.message(MessageFormat.format(LocaleFactory.localizedString("Making directory {0}", "Status"), file.getName())); try { - mkdir.mkdir(file, status); + mkdir.mkdir(source.getFeature(Write.class), file, status); // Post process of file filter.complete( status.getRename().remote != null ? status.getRename().remote : entry.getKey().remote, @@ -320,8 +321,8 @@ public class UploadTransfer extends Transfer { progress.message(MessageFormat.format(LocaleFactory.localizedString("Uploading {0}", "Status"), file.getName())); // Transfer - final Upload upload = source.getFeature(Upload.class); - final Object reply = upload.upload(file, local, bandwidth, progress, listener, segment, prompt); + source.getFeature(Upload.class).upload(source.getFeature(Write.class), + file, local, bandwidth, progress, listener, segment, prompt); } } diff --git a/core/src/main/java/ch/cyberduck/core/transfer/copy/AbstractCopyFilter.java b/core/src/main/java/ch/cyberduck/core/transfer/copy/AbstractCopyFilter.java index 297cdd70ff..ea6704c5bc 100644 --- a/core/src/main/java/ch/cyberduck/core/transfer/copy/AbstractCopyFilter.java +++ b/core/src/main/java/ch/cyberduck/core/transfer/copy/AbstractCopyFilter.java @@ -182,14 +182,14 @@ public abstract class AbstractCopyFilter implements TransferPathFilter { catch(NotfoundException | AccessDeniedException | InteroperabilityException e) { final Redundancy targetFeature = destination.getFeature(Redundancy.class); if(targetFeature != null) { - status.setStorageClass(targetFeature.getDefault()); + status.setStorageClass(targetFeature.getDefault(file)); } } } else { final Redundancy targetFeature = destination.getFeature(Redundancy.class); if(targetFeature != null) { - status.setStorageClass(targetFeature.getDefault()); + status.setStorageClass(targetFeature.getDefault(file)); } } } diff --git a/core/src/main/java/ch/cyberduck/core/transfer/download/TrashFilter.java b/core/src/main/java/ch/cyberduck/core/transfer/download/TrashFilter.java index 610499aab4..a2ed75171d 100644 --- a/core/src/main/java/ch/cyberduck/core/transfer/download/TrashFilter.java +++ b/core/src/main/java/ch/cyberduck/core/transfer/download/TrashFilter.java @@ -33,7 +33,7 @@ import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; public class TrashFilter extends AbstractDownloadFilter { - private static final Logger log = LogManager.getLogger(SkipFilter.class); + private static final Logger log = LogManager.getLogger(TrashFilter.class); private final Trash feature = LocalTrashFactory.get(); diff --git a/core/src/main/java/ch/cyberduck/core/transfer/upload/AbstractUploadFilter.java b/core/src/main/java/ch/cyberduck/core/transfer/upload/AbstractUploadFilter.java index edca883132..775a74fddb 100644 --- a/core/src/main/java/ch/cyberduck/core/transfer/upload/AbstractUploadFilter.java +++ b/core/src/main/java/ch/cyberduck/core/transfer/upload/AbstractUploadFilter.java @@ -171,7 +171,7 @@ public abstract class AbstractUploadFilter implements TransferPathFilter { } else { if(HostPreferencesFactory.get(session.getHost()).getBoolean("queue.upload.permissions.default")) { - status.setPermission(feature.getDefault(file.getType())); + status.setPermission(feature.getDefault(file.getParent(), file.getType())); } else { // Read permissions from local file @@ -224,11 +224,11 @@ public abstract class AbstractUploadFilter implements TransferPathFilter { status.setMetadata(feature.getMetadata(file)); } catch(NotfoundException | AccessDeniedException | InteroperabilityException e) { - status.setMetadata(feature.getDefault()); + status.setMetadata(feature.getDefault(file)); } } else { - status.setMetadata(feature.getDefault()); + status.setMetadata(feature.getDefault(file)); } } } @@ -261,11 +261,11 @@ public abstract class AbstractUploadFilter implements TransferPathFilter { status.setStorageClass(feature.getClass(file)); } catch(NotfoundException | AccessDeniedException | InteroperabilityException e) { - status.setStorageClass(feature.getDefault()); + status.setStorageClass(feature.getDefault(file)); } } else { - status.setStorageClass(feature.getDefault()); + status.setStorageClass(feature.getDefault(file)); } } } diff --git a/core/src/main/java/ch/cyberduck/core/transfer/upload/CompareFilter.java b/core/src/main/java/ch/cyberduck/core/transfer/upload/CompareFilter.java index 9fe1479128..57267f9cd8 100644 --- a/core/src/main/java/ch/cyberduck/core/transfer/upload/CompareFilter.java +++ b/core/src/main/java/ch/cyberduck/core/transfer/upload/CompareFilter.java @@ -37,7 +37,7 @@ import org.apache.logging.log4j.Logger; public class CompareFilter extends AbstractUploadFilter { private static final Logger log = LogManager.getLogger(CompareFilter.class); - private final ComparePathFilter comparisonService; + private final ComparePathFilter filter; public CompareFilter(final SymlinkResolver symlinkResolver, final Session session) { this(symlinkResolver, session, new UploadFilterOptions(session.getHost())); @@ -48,23 +48,23 @@ public class CompareFilter extends AbstractUploadFilter { } public CompareFilter(final SymlinkResolver symlinkResolver, final Session session, - final DefaultComparePathFilter comparisonService, final UploadFilterOptions options) { - this(symlinkResolver, session, session.getFeature(Find.class), session.getFeature(AttributesFinder.class), comparisonService, options); + final DefaultComparePathFilter filter, final UploadFilterOptions options) { + this(symlinkResolver, session, session.getFeature(Find.class), session.getFeature(AttributesFinder.class), filter, options); } public CompareFilter(final SymlinkResolver symlinkResolver, final Session session, final Find find, final AttributesFinder attribute, final UploadFilterOptions options) { this(symlinkResolver, session, find, attribute, new DefaultComparePathFilter(find, attribute, session.getFeature(ComparisonService.class)), options); } - public CompareFilter(final SymlinkResolver symlinkResolver, final Session session, final Find find, final AttributesFinder attribute, final DefaultComparePathFilter comparisonService, final UploadFilterOptions options) { + public CompareFilter(final SymlinkResolver symlinkResolver, final Session session, final Find find, final AttributesFinder attribute, final DefaultComparePathFilter filter, final UploadFilterOptions options) { super(symlinkResolver, session, find, attribute, options); - this.comparisonService = comparisonService; + this.filter = filter; } @Override public boolean accept(final Path file, final Local local, final TransferStatus parent, final ProgressListener progress) throws BackgroundException { if(super.accept(file, local, parent, progress)) { - final Comparison comparison = comparisonService.compare(file, local, progress); + final Comparison comparison = filter.compare(file, local, progress); switch(comparison) { case local: return true; diff --git a/core/src/main/java/ch/cyberduck/core/updater/NotificationServiceUpdateHandler.java b/core/src/main/java/ch/cyberduck/core/updater/NotificationServiceUpdateHandler.java index d7e366be70..c67670d3e1 100644 --- a/core/src/main/java/ch/cyberduck/core/updater/NotificationServiceUpdateHandler.java +++ b/core/src/main/java/ch/cyberduck/core/updater/NotificationServiceUpdateHandler.java @@ -40,11 +40,15 @@ public class NotificationServiceUpdateHandler implements Handler, NotificationSe @Override public boolean handle(final UpdateChecker.Update item) { notifications.notify(item.getRevision(), "Updater", LocaleFactory.localizedString("Software Update", "Updater"), - MessageFormat.format(LocaleFactory.localizedString("Version {0} is now available", "Updater"), item.getDisplayVersionString()), + this.toMessage(item), String.format("%s…", LocaleFactory.localizedString("Install and Relaunch", "Updater"))); return false; } + protected String toMessage(final UpdateChecker.Update item) { + return MessageFormat.format(LocaleFactory.localizedString("Version {0} is now available", "Updater"), item.getDisplayVersionString()); + } + @Override public void callback(final String identifier) { // If the notificaton is clicked on, make sure we bring the update in focus diff --git a/core/src/main/java/ch/cyberduck/core/vault/DefaultVaultRegistry.java b/core/src/main/java/ch/cyberduck/core/vault/DefaultVaultRegistry.java index 0501da8de6..9550e607bb 100644 --- a/core/src/main/java/ch/cyberduck/core/vault/DefaultVaultRegistry.java +++ b/core/src/main/java/ch/cyberduck/core/vault/DefaultVaultRegistry.java @@ -18,6 +18,7 @@ package ch.cyberduck.core.vault; import ch.cyberduck.core.ListService; import ch.cyberduck.core.PasswordCallback; import ch.cyberduck.core.Path; +import ch.cyberduck.core.PathContainerService; import ch.cyberduck.core.Session; import ch.cyberduck.core.SimplePathPredicate; import ch.cyberduck.core.UrlProvider; @@ -56,29 +57,34 @@ public class DefaultVaultRegistry extends CopyOnWriteArraySet implements @Override public boolean add(final Vault vault) { + log.debug("Add vault {} to registry", vault); return super.add(vault); } @Override public boolean close(final Path directory) { - return this.removeIf(vault -> { - if(new SimplePathPredicate(vault.getHome()).test(directory)) { - vault.close(); - directory.attributes().setVault(null); - return true; - } - return false; - }); + try { + return this.removeIf(vault -> { + if(new SimplePathPredicate(vault.getHome()).test(directory)) { + vault.close(); + return true; + } + return false; + }); + } + finally { + directory.attributes().setVault(null); + } } @Override - public boolean contains(final Path directory) { + public boolean contains(final Path directory, final boolean recursive) { for(Vault vault : this) { - if(directory.equals(vault.getHome())) { + if(new SimplePathPredicate(vault.getHome()).test(directory)) { return true; } - if(directory.isChild(vault.getHome())) { - return true; + if(recursive) { + return vault.contains(directory); } } return false; @@ -92,14 +98,14 @@ public class DefaultVaultRegistry extends CopyOnWriteArraySet implements } @Override - public Vault find(final Session session, final Path file, final boolean unlock) throws VaultUnlockCancelException { + public Vault find(final Session session, final Path file, final boolean autoload) throws VaultUnlockCancelException { for(Vault vault : this) { if(vault.contains(file)) { log.debug("Found vault {} for file {}", vault, file); return vault; } } - if(unlock) { + if(autoload) { final LoadingVaultLookupListener listener = new LoadingVaultLookupListener(this, prompt); if(file.attributes().getVault() != null) { return listener.load(session, file.attributes().getVault(), @@ -128,6 +134,7 @@ public class DefaultVaultRegistry extends CopyOnWriteArraySet implements return this._getFeature(session, type, proxy); } + @SuppressWarnings("unchecked") protected T _getFeature(final Session session, final Class type, final T proxy) { if(type == ListService.class) { return (T) new VaultRegistryListService(session, (ListService) proxy, this, @@ -230,6 +237,9 @@ public class DefaultVaultRegistry extends CopyOnWriteArraySet implements if(type == Versioning.class) { return (T) new VaultRegistryVersioningFeature(session, (Versioning) proxy, this); } + if(type == PathContainerService.class) { + return (T) new VaultRegistryPathContainerService(session, (PathContainerService) proxy, this); + } return proxy; } } diff --git a/core/src/main/java/ch/cyberduck/core/vault/VaultCredentials.java b/core/src/main/java/ch/cyberduck/core/vault/VaultCredentials.java index c99229a3dd..d2d5d84860 100644 --- a/core/src/main/java/ch/cyberduck/core/vault/VaultCredentials.java +++ b/core/src/main/java/ch/cyberduck/core/vault/VaultCredentials.java @@ -24,8 +24,8 @@ public class VaultCredentials extends Credentials { } @Override - public VaultCredentials withSaved(final boolean saved) { - super.withSaved(saved); + public VaultCredentials setSaved(final boolean saved) { + super.setSaved(saved); return this; } } diff --git a/core/src/main/java/ch/cyberduck/core/vault/VaultRegistry.java b/core/src/main/java/ch/cyberduck/core/vault/VaultRegistry.java index 24bbab79db..e8bf347ea8 100644 --- a/core/src/main/java/ch/cyberduck/core/vault/VaultRegistry.java +++ b/core/src/main/java/ch/cyberduck/core/vault/VaultRegistry.java @@ -30,18 +30,18 @@ public interface VaultRegistry { * @throws VaultUnlockCancelException Attempt to unlock vault was canceled by user * @see Vault#DISABLED */ - default Vault find(final Session session, final Path file) throws VaultUnlockCancelException { + default Vault find(final Session session, final Path file) throws VaultUnlockCancelException { return this.find(session, file, HostPreferencesFactory.get(session.getHost()).getBoolean("cryptomator.vault.autoload")); } /** * @param session Connection * @param file File - * @param unlock Attempt to unlock vault when not already registered but file has vault referenced in attributes + * @param autoload Attempt to unlock vault when not already registered but file has vault referenced in attributes * @return Vault for file or disabled vault if file is not inside a vault * @throws VaultUnlockCancelException Attempt to unlock vault was canceled by user */ - Vault find(Session session, Path file, boolean unlock) throws VaultUnlockCancelException; + Vault find(Session session, Path file, boolean autoload) throws VaultUnlockCancelException; /** * Add vault to registry @@ -77,11 +77,20 @@ public interface VaultRegistry { /** * @return True if directory is registered as vault or is contained in vault */ - boolean contains(Path directory); + default boolean contains(Path directory) { + return this.contains(directory, true); + } + + /** + * @param directory Directory to check + * @param recursive When false only return true if directory is registered as vault root + * @return True if directory is registered as vault or is contained in vault + */ + boolean contains(Path directory, boolean recursive); class DisabledVaultRegistry implements VaultRegistry { @Override - public Vault find(final Session session, final Path file, final boolean unlock) throws VaultUnlockCancelException { + public Vault find(final Session session, final Path file, final boolean autoload) throws VaultUnlockCancelException { return Vault.DISABLED; } @@ -96,7 +105,7 @@ public interface VaultRegistry { } @Override - public boolean contains(final Path vault) { + public boolean contains(final Path directory, final boolean recursive) { return false; } diff --git a/core/src/main/java/ch/cyberduck/core/vault/registry/VaultRegistryAclPermissionFeature.java b/core/src/main/java/ch/cyberduck/core/vault/registry/VaultRegistryAclPermissionFeature.java index 92b8c709cf..a7c4489281 100644 --- a/core/src/main/java/ch/cyberduck/core/vault/registry/VaultRegistryAclPermissionFeature.java +++ b/core/src/main/java/ch/cyberduck/core/vault/registry/VaultRegistryAclPermissionFeature.java @@ -15,17 +15,21 @@ package ch.cyberduck.core.vault.registry; * GNU General Public License for more details. */ -import ch.cyberduck.core.AbstractPath; import ch.cyberduck.core.Acl; import ch.cyberduck.core.Path; import ch.cyberduck.core.Session; import ch.cyberduck.core.exception.BackgroundException; +import ch.cyberduck.core.exception.UnsupportedException; import ch.cyberduck.core.features.AclPermission; import ch.cyberduck.core.transfer.TransferStatus; import ch.cyberduck.core.vault.VaultRegistry; +import ch.cyberduck.core.vault.VaultUnlockCancelException; -import java.util.EnumSet; +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashSet; import java.util.List; +import java.util.Set; public class VaultRegistryAclPermissionFeature implements AclPermission { @@ -50,18 +54,42 @@ public class VaultRegistryAclPermissionFeature implements AclPermission { } @Override - public List getAvailableAclUsers() { - return proxy.getAvailableAclUsers(); + public List getAvailableAclUsers(final List files) { + try { + final Set users = new HashSet<>(); + for(Path file : files) { + users.addAll(registry.find(session, file).getFeature(session, AclPermission.class, proxy).getAvailableAclUsers(Collections.singletonList(file))); + } + return new ArrayList<>(users); + } + catch(VaultUnlockCancelException e) { + return proxy.getAvailableAclUsers(files); + } + catch(UnsupportedException e) { + return Collections.emptyList(); + } } @Override public List getAvailableAclRoles(final List files) { - return proxy.getAvailableAclRoles(files); + try { + final Set users = new HashSet<>(); + for(Path file : files) { + users.addAll(registry.find(session, file).getFeature(session, AclPermission.class, proxy).getAvailableAclRoles(Collections.singletonList(file))); + } + return new ArrayList<>(users); + } + catch(VaultUnlockCancelException e) { + return proxy.getAvailableAclRoles(files); + } + catch(UnsupportedException e) { + return Collections.emptyList(); + } } @Override public Acl getDefault(final Path file) throws BackgroundException { - return proxy.getDefault(file); + return registry.find(session, file).getFeature(session, AclPermission.class, proxy).getDefault(file); } @Override diff --git a/core/src/main/java/ch/cyberduck/core/vault/registry/VaultRegistryBulkFeature.java b/core/src/main/java/ch/cyberduck/core/vault/registry/VaultRegistryBulkFeature.java index cec693eb98..e2b6553226 100644 --- a/core/src/main/java/ch/cyberduck/core/vault/registry/VaultRegistryBulkFeature.java +++ b/core/src/main/java/ch/cyberduck/core/vault/registry/VaultRegistryBulkFeature.java @@ -19,7 +19,6 @@ import ch.cyberduck.core.ConnectionCallback; import ch.cyberduck.core.Session; import ch.cyberduck.core.exception.BackgroundException; import ch.cyberduck.core.features.Bulk; -import ch.cyberduck.core.features.Delete; import ch.cyberduck.core.transfer.Transfer; import ch.cyberduck.core.transfer.TransferItem; import ch.cyberduck.core.transfer.TransferStatus; @@ -49,12 +48,6 @@ public class VaultRegistryBulkFeature implements Bulk { return null; } - @Override - public Bulk withDelete(final Delete delete) { - proxy.withDelete(delete); - return this; - } - @Override public void post(final Transfer.Type type, final Map files, final ConnectionCallback callback) throws BackgroundException { for(Map.Entry file : files.entrySet()) { diff --git a/core/src/main/java/ch/cyberduck/core/vault/registry/VaultRegistryCopyFeature.java b/core/src/main/java/ch/cyberduck/core/vault/registry/VaultRegistryCopyFeature.java index 7b988b095d..de2a79b6aa 100644 --- a/core/src/main/java/ch/cyberduck/core/vault/registry/VaultRegistryCopyFeature.java +++ b/core/src/main/java/ch/cyberduck/core/vault/registry/VaultRegistryCopyFeature.java @@ -19,6 +19,7 @@ import ch.cyberduck.core.ConnectionCallback; import ch.cyberduck.core.Path; import ch.cyberduck.core.Session; import ch.cyberduck.core.exception.BackgroundException; +import ch.cyberduck.core.exception.UnsupportedException; import ch.cyberduck.core.features.Copy; import ch.cyberduck.core.features.Vault; import ch.cyberduck.core.io.StreamListener; @@ -72,6 +73,9 @@ public class VaultRegistryCopyFeature implements Copy { catch(VaultUnlockCancelException e) { return proxy.features(source, copy); } + catch(UnsupportedException e) { + return EnumSet.noneOf(Flags.class); + } } @Override diff --git a/core/src/main/java/ch/cyberduck/core/vault/registry/VaultRegistryDeleteFeature.java b/core/src/main/java/ch/cyberduck/core/vault/registry/VaultRegistryDeleteFeature.java index e627fb6549..1825e3c3b8 100644 --- a/core/src/main/java/ch/cyberduck/core/vault/registry/VaultRegistryDeleteFeature.java +++ b/core/src/main/java/ch/cyberduck/core/vault/registry/VaultRegistryDeleteFeature.java @@ -19,6 +19,7 @@ import ch.cyberduck.core.PasswordCallback; import ch.cyberduck.core.Path; import ch.cyberduck.core.Session; import ch.cyberduck.core.exception.BackgroundException; +import ch.cyberduck.core.exception.UnsupportedException; import ch.cyberduck.core.features.Delete; import ch.cyberduck.core.features.Vault; import ch.cyberduck.core.transfer.TransferStatus; @@ -75,8 +76,16 @@ public class VaultRegistryDeleteFeature implements Delete { } @Override - public EnumSet features() { - return proxy.features(); + public EnumSet features(final Path file) { + try { + return registry.find(session, file).getFeature(session, Delete.class, proxy).features(file); + } + catch(VaultUnlockCancelException e) { + return proxy.features(file); + } + catch(UnsupportedException e) { + return EnumSet.noneOf(Flags.class); + } } @Override diff --git a/core/src/main/java/ch/cyberduck/core/vault/registry/VaultRegistryDirectoryFeature.java b/core/src/main/java/ch/cyberduck/core/vault/registry/VaultRegistryDirectoryFeature.java index b2e3e2e9a0..0c24a239a9 100644 --- a/core/src/main/java/ch/cyberduck/core/vault/registry/VaultRegistryDirectoryFeature.java +++ b/core/src/main/java/ch/cyberduck/core/vault/registry/VaultRegistryDirectoryFeature.java @@ -38,8 +38,8 @@ public class VaultRegistryDirectoryFeature implements Directory { } @Override - public Path mkdir(final Path folder, final TransferStatus status) throws BackgroundException { - return registry.find(session, folder).getFeature(session, Directory.class, proxy).mkdir(folder, status); + public Path mkdir(final Write writer, final Path folder, final TransferStatus status) throws BackgroundException { + return registry.find(session, folder).getFeature(session, Directory.class, proxy).mkdir(writer, folder, status); } @Override @@ -52,11 +52,6 @@ public class VaultRegistryDirectoryFeature implements Directory { } } - @Override - public Directory withWriter(final Write writer) { - return proxy.withWriter(writer); - } - @Override public String toString() { final StringBuilder sb = new StringBuilder("VaultRegistryDirectoryFeature{"); diff --git a/core/src/main/java/ch/cyberduck/core/vault/registry/VaultRegistryDownloadFeature.java b/core/src/main/java/ch/cyberduck/core/vault/registry/VaultRegistryDownloadFeature.java index 7efba47087..49b68c1e1d 100644 --- a/core/src/main/java/ch/cyberduck/core/vault/registry/VaultRegistryDownloadFeature.java +++ b/core/src/main/java/ch/cyberduck/core/vault/registry/VaultRegistryDownloadFeature.java @@ -40,8 +40,9 @@ public class VaultRegistryDownloadFeature implements Download { } @Override - public void download(final Path file, final Local local, final BandwidthThrottle throttle, final StreamListener listener, final TransferStatus status, final ConnectionCallback callback) throws BackgroundException { - registry.find(session, file).getFeature(session, Download.class, proxy).download(file, local, throttle, listener, status, callback); + public void download(final Read read, final Path file, final Local local, final BandwidthThrottle throttle, final StreamListener listener, final TransferStatus status, final ConnectionCallback callback) throws BackgroundException { + registry.find(session, file).getFeature(session, Download.class, proxy).download( + read, file, local, throttle, listener, status, callback); } @Override @@ -49,12 +50,6 @@ public class VaultRegistryDownloadFeature implements Download { return registry.find(session, file).getFeature(session, Download.class, proxy).offset(file); } - @Override - public Download withReader(final Read reader) { - proxy.withReader(reader); - return this; - } - @Override public String toString() { final StringBuilder sb = new StringBuilder("VaultRegistryDownloadFeature{"); diff --git a/core/src/main/java/ch/cyberduck/core/vault/registry/VaultRegistryFileIdProvider.java b/core/src/main/java/ch/cyberduck/core/vault/registry/VaultRegistryFileIdProvider.java index 0f43550de1..0020883fb5 100644 --- a/core/src/main/java/ch/cyberduck/core/vault/registry/VaultRegistryFileIdProvider.java +++ b/core/src/main/java/ch/cyberduck/core/vault/registry/VaultRegistryFileIdProvider.java @@ -38,6 +38,11 @@ public class VaultRegistryFileIdProvider implements FileIdProvider { return registry.find(session, file).getFeature(session, FileIdProvider.class, proxy).getFileId(file); } + @Override + public void clear() { + proxy.clear(); + } + @Override public String toString() { final StringBuilder sb = new StringBuilder("VaultRegistryFileIdProvider{"); diff --git a/core/src/main/java/ch/cyberduck/core/vault/registry/VaultRegistryFindFeature.java b/core/src/main/java/ch/cyberduck/core/vault/registry/VaultRegistryFindFeature.java index 979a26a46a..90570bc7c3 100644 --- a/core/src/main/java/ch/cyberduck/core/vault/registry/VaultRegistryFindFeature.java +++ b/core/src/main/java/ch/cyberduck/core/vault/registry/VaultRegistryFindFeature.java @@ -39,8 +39,7 @@ public class VaultRegistryFindFeature implements Find { private final Find proxy; private final VaultRegistry registry; private final VaultLookupListener lookup; - - private boolean autodetect; + private final boolean autodetect; public VaultRegistryFindFeature(final Session session, final Find proxy, final VaultRegistry registry, final VaultLookupListener lookup) { this.session = session; @@ -81,11 +80,6 @@ public class VaultRegistryFindFeature implements Find { return vault.getFeature(session, Find.class, proxy).find(file, listener); } - public VaultRegistryFindFeature withAutodetect(final boolean autodetect) { - this.autodetect = autodetect && HostPreferencesFactory.get(session.getHost()).getBoolean("cryptomator.enable"); - return this; - } - @Override public String toString() { final StringBuilder sb = new StringBuilder("VaultRegistryFindFeature{"); diff --git a/core/src/main/java/ch/cyberduck/core/vault/registry/VaultRegistryHeadersFeature.java b/core/src/main/java/ch/cyberduck/core/vault/registry/VaultRegistryHeadersFeature.java index 55264df8dc..b64a52a04a 100644 --- a/core/src/main/java/ch/cyberduck/core/vault/registry/VaultRegistryHeadersFeature.java +++ b/core/src/main/java/ch/cyberduck/core/vault/registry/VaultRegistryHeadersFeature.java @@ -18,10 +18,13 @@ package ch.cyberduck.core.vault.registry; import ch.cyberduck.core.Path; import ch.cyberduck.core.Session; import ch.cyberduck.core.exception.BackgroundException; +import ch.cyberduck.core.exception.UnsupportedException; import ch.cyberduck.core.features.Headers; import ch.cyberduck.core.transfer.TransferStatus; import ch.cyberduck.core.vault.VaultRegistry; +import ch.cyberduck.core.vault.VaultUnlockCancelException; +import java.util.Collections; import java.util.Map; public class VaultRegistryHeadersFeature implements Headers { @@ -37,8 +40,16 @@ public class VaultRegistryHeadersFeature implements Headers { } @Override - public Map getDefault() { - return proxy.getDefault(); + public Map getDefault(final Path file) { + try { + return registry.find(session, file).getFeature(session, Headers.class, proxy).getDefault(file); + } + catch(VaultUnlockCancelException e) { + return proxy.getDefault(file); + } + catch(UnsupportedException e) { + return Collections.emptyMap(); + } } @Override diff --git a/core/src/main/java/ch/cyberduck/core/vault/registry/VaultRegistryListService.java b/core/src/main/java/ch/cyberduck/core/vault/registry/VaultRegistryListService.java index 61ddcdb113..81bf38b77e 100644 --- a/core/src/main/java/ch/cyberduck/core/vault/registry/VaultRegistryListService.java +++ b/core/src/main/java/ch/cyberduck/core/vault/registry/VaultRegistryListService.java @@ -79,6 +79,6 @@ public class VaultRegistryListService implements ListService { @Override public void preflight(final Path directory) throws BackgroundException { - proxy.preflight(directory); + registry.find(session, directory).getFeature(session, ListService.class, proxy).preflight(directory); } } diff --git a/core/src/main/java/ch/cyberduck/core/vault/registry/VaultRegistryLocationFeature.java b/core/src/main/java/ch/cyberduck/core/vault/registry/VaultRegistryLocationFeature.java index 29ec5a14b0..56cf55387e 100644 --- a/core/src/main/java/ch/cyberduck/core/vault/registry/VaultRegistryLocationFeature.java +++ b/core/src/main/java/ch/cyberduck/core/vault/registry/VaultRegistryLocationFeature.java @@ -18,9 +18,12 @@ package ch.cyberduck.core.vault.registry; import ch.cyberduck.core.Path; import ch.cyberduck.core.Session; import ch.cyberduck.core.exception.BackgroundException; +import ch.cyberduck.core.exception.UnsupportedException; import ch.cyberduck.core.features.Location; import ch.cyberduck.core.vault.VaultRegistry; +import ch.cyberduck.core.vault.VaultUnlockCancelException; +import java.util.Collections; import java.util.Set; public class VaultRegistryLocationFeature implements Location { @@ -36,13 +39,29 @@ public class VaultRegistryLocationFeature implements Location { } @Override - public Name getDefault() { - return proxy.getDefault(); + public Name getDefault(final Path file) { + try { + return registry.find(session, file).getFeature(session, Location.class, proxy).getDefault(file); + } + catch(VaultUnlockCancelException e) { + return proxy.getDefault(file); + } + catch(UnsupportedException e) { + return Location.unknown; + } } @Override - public Set getLocations() { - return proxy.getLocations(); + public Set getLocations(final Path file) { + try { + return registry.find(session, file).getFeature(session, Location.class, proxy).getLocations(file); + } + catch(VaultUnlockCancelException e) { + return proxy.getLocations(file); + } + catch(UnsupportedException e) { + return Collections.emptySet(); + } } @Override diff --git a/core/src/main/java/ch/cyberduck/core/vault/registry/VaultRegistryMoveFeature.java b/core/src/main/java/ch/cyberduck/core/vault/registry/VaultRegistryMoveFeature.java index 569ad6df5d..6023215522 100644 --- a/core/src/main/java/ch/cyberduck/core/vault/registry/VaultRegistryMoveFeature.java +++ b/core/src/main/java/ch/cyberduck/core/vault/registry/VaultRegistryMoveFeature.java @@ -19,6 +19,7 @@ import ch.cyberduck.core.ConnectionCallback; import ch.cyberduck.core.Path; import ch.cyberduck.core.Session; import ch.cyberduck.core.exception.BackgroundException; +import ch.cyberduck.core.exception.UnsupportedException; import ch.cyberduck.core.features.Copy; import ch.cyberduck.core.features.Delete; import ch.cyberduck.core.features.Move; @@ -81,6 +82,9 @@ public class VaultRegistryMoveFeature implements Move { catch(VaultUnlockCancelException e) { return proxy.features(source, target); } + catch(UnsupportedException e) { + return EnumSet.noneOf(Flags.class); + } } @Override diff --git a/core/src/main/java/ch/cyberduck/core/vault/registry/VaultRegistryPathContainerService.java b/core/src/main/java/ch/cyberduck/core/vault/registry/VaultRegistryPathContainerService.java new file mode 100644 index 0000000000..2560ceb607 --- /dev/null +++ b/core/src/main/java/ch/cyberduck/core/vault/registry/VaultRegistryPathContainerService.java @@ -0,0 +1,76 @@ +package ch.cyberduck.core.vault.registry; + +/* + * Copyright (c) 2002-2025 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 ch.cyberduck.core.Path; +import ch.cyberduck.core.PathContainerService; +import ch.cyberduck.core.Session; +import ch.cyberduck.core.exception.UnsupportedException; +import ch.cyberduck.core.vault.VaultRegistry; +import ch.cyberduck.core.vault.VaultUnlockCancelException; + +public class VaultRegistryPathContainerService implements PathContainerService { + + private final Session session; + private final PathContainerService proxy; + private final VaultRegistry registry; + + public VaultRegistryPathContainerService(final Session session, final PathContainerService proxy, final VaultRegistry registry) { + this.session = session; + this.proxy = proxy; + this.registry = registry; + } + + @Override + public Path getRoot(final Path file) { + try { + return registry.find(session, file).getFeature(session, PathContainerService.class, proxy).getRoot(file); + } + catch(UnsupportedException | VaultUnlockCancelException e) { + return proxy.getRoot(file); + } + } + + @Override + public boolean isContainer(final Path file) { + try { + return registry.find(session, file).getFeature(session, PathContainerService.class, proxy).isContainer(file); + } + catch(UnsupportedException | VaultUnlockCancelException e) { + return proxy.isContainer(file); + } + } + + @Override + public Path getContainer(final Path file) { + try { + return registry.find(session, file).getFeature(session, PathContainerService.class, proxy).getContainer(file); + } + catch(UnsupportedException | VaultUnlockCancelException e) { + return proxy.getContainer(file); + } + } + + @Override + public String getKey(final Path file) { + try { + return registry.find(session, file).getFeature(session, PathContainerService.class, proxy).getKey(file); + } + catch(UnsupportedException | VaultUnlockCancelException e) { + return proxy.getKey(file); + } + } +} diff --git a/core/src/main/java/ch/cyberduck/core/vault/registry/VaultRegistryRedundancyFeature.java b/core/src/main/java/ch/cyberduck/core/vault/registry/VaultRegistryRedundancyFeature.java index 4b68f203f9..33d707d2f6 100644 --- a/core/src/main/java/ch/cyberduck/core/vault/registry/VaultRegistryRedundancyFeature.java +++ b/core/src/main/java/ch/cyberduck/core/vault/registry/VaultRegistryRedundancyFeature.java @@ -18,9 +18,12 @@ package ch.cyberduck.core.vault.registry; import ch.cyberduck.core.Path; import ch.cyberduck.core.Session; import ch.cyberduck.core.exception.BackgroundException; +import ch.cyberduck.core.exception.UnsupportedException; import ch.cyberduck.core.features.Redundancy; import ch.cyberduck.core.vault.VaultRegistry; +import ch.cyberduck.core.vault.VaultUnlockCancelException; +import java.util.Collections; import java.util.Set; public class VaultRegistryRedundancyFeature implements Redundancy { @@ -36,13 +39,29 @@ public class VaultRegistryRedundancyFeature implements Redundancy { } @Override - public String getDefault() { - return proxy.getDefault(); + public String getDefault(final Path file) { + try { + return registry.find(session, file).getFeature(session, Redundancy.class, proxy).getDefault(file); + } + catch(VaultUnlockCancelException e) { + return proxy.getDefault(file); + } + catch(UnsupportedException e) { + return null; + } } @Override - public Set getClasses() { - return proxy.getClasses(); + public Set getClasses(final Path file) { + try { + return registry.find(session, file).getFeature(session, Redundancy.class, proxy).getClasses(file); + } + catch(VaultUnlockCancelException e) { + return proxy.getClasses(file); + } + catch(UnsupportedException e) { + return Collections.emptySet(); + } } @Override diff --git a/core/src/main/java/ch/cyberduck/core/vault/registry/VaultRegistrySearchFeature.java b/core/src/main/java/ch/cyberduck/core/vault/registry/VaultRegistrySearchFeature.java index 4866652942..eec1320de8 100644 --- a/core/src/main/java/ch/cyberduck/core/vault/registry/VaultRegistrySearchFeature.java +++ b/core/src/main/java/ch/cyberduck/core/vault/registry/VaultRegistrySearchFeature.java @@ -21,8 +21,10 @@ import ch.cyberduck.core.ListProgressListener; import ch.cyberduck.core.Path; import ch.cyberduck.core.Session; import ch.cyberduck.core.exception.BackgroundException; +import ch.cyberduck.core.exception.UnsupportedException; import ch.cyberduck.core.features.Search; import ch.cyberduck.core.vault.VaultRegistry; +import ch.cyberduck.core.vault.VaultUnlockCancelException; import java.util.EnumSet; @@ -44,8 +46,16 @@ public class VaultRegistrySearchFeature implements Search { } @Override - public EnumSet features() { - return proxy.features(); + public EnumSet features(final Path workdir) { + try { + return registry.find(session, workdir).getFeature(session, Search.class, proxy).features(workdir); + } + catch(VaultUnlockCancelException e) { + return proxy.features(workdir); + } + catch(UnsupportedException e) { + return EnumSet.noneOf(Flags.class); + } } @Override diff --git a/core/src/main/java/ch/cyberduck/core/vault/registry/VaultRegistryTouchFeature.java b/core/src/main/java/ch/cyberduck/core/vault/registry/VaultRegistryTouchFeature.java index 0047b60e0d..00c27982ea 100644 --- a/core/src/main/java/ch/cyberduck/core/vault/registry/VaultRegistryTouchFeature.java +++ b/core/src/main/java/ch/cyberduck/core/vault/registry/VaultRegistryTouchFeature.java @@ -37,8 +37,8 @@ public class VaultRegistryTouchFeature implements Touch { } @Override - public Path touch(final Path file, final TransferStatus status) throws BackgroundException { - return registry.find(session, file).getFeature(session, Touch.class, proxy).touch(file, status); + public Path touch(final Write writer, final Path file, final TransferStatus status) throws BackgroundException { + return registry.find(session, file).getFeature(session, Touch.class, proxy).touch(writer, file, status); } @Override @@ -51,12 +51,6 @@ public class VaultRegistryTouchFeature implements Touch { } } - @Override - public Touch withWriter(final Write writer) { - proxy.withWriter(writer); - return this; - } - @Override public String toString() { final StringBuilder sb = new StringBuilder("VaultRegistryTouchFeature{"); diff --git a/core/src/main/java/ch/cyberduck/core/vault/registry/VaultRegistryTrashFeature.java b/core/src/main/java/ch/cyberduck/core/vault/registry/VaultRegistryTrashFeature.java index 4d89787c20..aa8c755b4f 100644 --- a/core/src/main/java/ch/cyberduck/core/vault/registry/VaultRegistryTrashFeature.java +++ b/core/src/main/java/ch/cyberduck/core/vault/registry/VaultRegistryTrashFeature.java @@ -19,7 +19,7 @@ import ch.cyberduck.core.PasswordCallback; import ch.cyberduck.core.Path; import ch.cyberduck.core.Session; import ch.cyberduck.core.exception.BackgroundException; -import ch.cyberduck.core.features.Delete; +import ch.cyberduck.core.exception.UnsupportedException; import ch.cyberduck.core.features.Trash; import ch.cyberduck.core.features.Vault; import ch.cyberduck.core.transfer.TransferStatus; @@ -68,7 +68,7 @@ public class VaultRegistryTrashFeature implements Trash { @Override public void preflight(final Path file) throws BackgroundException { try { - registry.find(session, file, false).getFeature(session, Delete.class, proxy).preflight(file); + registry.find(session, file, false).getFeature(session, Trash.class, proxy).preflight(file); } catch(VaultUnlockCancelException e) { proxy.preflight(file); @@ -76,8 +76,16 @@ public class VaultRegistryTrashFeature implements Trash { } @Override - public EnumSet features() { - return proxy.features(); + public EnumSet features(final Path file) { + try { + return registry.find(session, file).getFeature(session, Trash.class, proxy).features(file); + } + catch(VaultUnlockCancelException e) { + return proxy.features(file); + } + catch(UnsupportedException e) { + return EnumSet.noneOf(Flags.class); + } } @Override diff --git a/core/src/main/java/ch/cyberduck/core/vault/registry/VaultRegistryUnixPermissionFeature.java b/core/src/main/java/ch/cyberduck/core/vault/registry/VaultRegistryUnixPermissionFeature.java index f9a304a632..077e13a27d 100644 --- a/core/src/main/java/ch/cyberduck/core/vault/registry/VaultRegistryUnixPermissionFeature.java +++ b/core/src/main/java/ch/cyberduck/core/vault/registry/VaultRegistryUnixPermissionFeature.java @@ -19,9 +19,11 @@ import ch.cyberduck.core.Path; import ch.cyberduck.core.Permission; import ch.cyberduck.core.Session; import ch.cyberduck.core.exception.BackgroundException; +import ch.cyberduck.core.exception.UnsupportedException; import ch.cyberduck.core.features.UnixPermission; import ch.cyberduck.core.transfer.TransferStatus; import ch.cyberduck.core.vault.VaultRegistry; +import ch.cyberduck.core.vault.VaultUnlockCancelException; import java.util.EnumSet; @@ -58,8 +60,16 @@ public class VaultRegistryUnixPermissionFeature implements UnixPermission { } @Override - public Permission getDefault(final EnumSet type) { - return proxy.getDefault(type); + public Permission getDefault(final Path workdir, final EnumSet type) { + try { + return registry.find(session, workdir).getFeature(session, UnixPermission.class, proxy).getDefault(workdir, type); + } + catch(VaultUnlockCancelException e) { + return proxy.getDefault(workdir, type); + } + catch(UnsupportedException e) { + return Permission.EMPTY; + } } @Override diff --git a/core/src/main/java/ch/cyberduck/core/vault/registry/VaultRegistryUploadFeature.java b/core/src/main/java/ch/cyberduck/core/vault/registry/VaultRegistryUploadFeature.java index a5f94cccc9..eb5a5e99de 100644 --- a/core/src/main/java/ch/cyberduck/core/vault/registry/VaultRegistryUploadFeature.java +++ b/core/src/main/java/ch/cyberduck/core/vault/registry/VaultRegistryUploadFeature.java @@ -42,8 +42,9 @@ public class VaultRegistryUploadFeature implements Upload { @Override @SuppressWarnings("unchecked") - public Output upload(final Path file, final Local local, final BandwidthThrottle throttle, final ProgressListener progress, final StreamListener streamListener, final TransferStatus status, final ConnectionCallback callback) throws BackgroundException { - return (Output) registry.find(session, file).getFeature(session, Upload.class, proxy).upload(file, local, throttle, progress, streamListener, status, callback); + public Output upload(final Write write, final Path file, final Local local, final BandwidthThrottle throttle, final ProgressListener progress, final StreamListener streamListener, final TransferStatus status, final ConnectionCallback callback) throws BackgroundException { + return (Output) registry.find(session, file).getFeature(session, Upload.class, proxy).upload( + write, file, local, throttle, progress, streamListener, status, callback); } @Override @@ -51,12 +52,6 @@ public class VaultRegistryUploadFeature implements Upload { return registry.find(session, file).getFeature(session, Upload.class, proxy).append(file, status); } - @Override - public Upload withWriter(final Write writer) { - proxy.withWriter(writer); - return this; - } - @Override public String toString() { final StringBuilder sb = new StringBuilder("VaultRegistryUploadFeature{"); diff --git a/core/src/main/java/ch/cyberduck/core/vault/registry/VaultRegistryUrlProvider.java b/core/src/main/java/ch/cyberduck/core/vault/registry/VaultRegistryUrlProvider.java index 007a7cfe12..b90802ed07 100644 --- a/core/src/main/java/ch/cyberduck/core/vault/registry/VaultRegistryUrlProvider.java +++ b/core/src/main/java/ch/cyberduck/core/vault/registry/VaultRegistryUrlProvider.java @@ -20,6 +20,7 @@ import ch.cyberduck.core.DescriptiveUrlBag; import ch.cyberduck.core.Path; import ch.cyberduck.core.Session; import ch.cyberduck.core.UrlProvider; +import ch.cyberduck.core.exception.UnsupportedException; import ch.cyberduck.core.vault.VaultRegistry; import ch.cyberduck.core.vault.VaultUnlockCancelException; @@ -45,6 +46,9 @@ public class VaultRegistryUrlProvider implements UrlProvider { catch(VaultUnlockCancelException e) { return proxy.toUrl(file, types); } + catch(UnsupportedException e) { + return DescriptiveUrlBag.empty(); + } } @Override diff --git a/core/src/main/java/ch/cyberduck/core/vault/registry/VaultRegistryVersionIdProvider.java b/core/src/main/java/ch/cyberduck/core/vault/registry/VaultRegistryVersionIdProvider.java index 14f91e47ca..f15c516179 100644 --- a/core/src/main/java/ch/cyberduck/core/vault/registry/VaultRegistryVersionIdProvider.java +++ b/core/src/main/java/ch/cyberduck/core/vault/registry/VaultRegistryVersionIdProvider.java @@ -40,7 +40,7 @@ public class VaultRegistryVersionIdProvider implements VersionIdProvider { @Override public void clear() { - // + proxy.clear(); } @Override diff --git a/core/src/main/java/ch/cyberduck/core/vault/registry/VaultRegistryVersioningFeature.java b/core/src/main/java/ch/cyberduck/core/vault/registry/VaultRegistryVersioningFeature.java index 31eca0af30..d54a4ef004 100644 --- a/core/src/main/java/ch/cyberduck/core/vault/registry/VaultRegistryVersioningFeature.java +++ b/core/src/main/java/ch/cyberduck/core/vault/registry/VaultRegistryVersioningFeature.java @@ -22,6 +22,7 @@ import ch.cyberduck.core.Path; import ch.cyberduck.core.Session; import ch.cyberduck.core.VersioningConfiguration; import ch.cyberduck.core.exception.BackgroundException; +import ch.cyberduck.core.exception.UnsupportedException; import ch.cyberduck.core.features.Versioning; import ch.cyberduck.core.vault.VaultRegistry; import ch.cyberduck.core.vault.VaultUnlockCancelException; @@ -56,6 +57,9 @@ public class VaultRegistryVersioningFeature implements Versioning { return registry.find(session, file, false).getFeature(session, Versioning.class, proxy).features(file); } catch(VaultUnlockCancelException e) { + return proxy.features(file); + } + catch(UnsupportedException e) { return EnumSet.noneOf(Flags.class); } } diff --git a/core/src/main/java/ch/cyberduck/core/vault/registry/VaultRegistryWriteFeature.java b/core/src/main/java/ch/cyberduck/core/vault/registry/VaultRegistryWriteFeature.java index 32e03824d1..0add51e7c1 100644 --- a/core/src/main/java/ch/cyberduck/core/vault/registry/VaultRegistryWriteFeature.java +++ b/core/src/main/java/ch/cyberduck/core/vault/registry/VaultRegistryWriteFeature.java @@ -19,8 +19,10 @@ import ch.cyberduck.core.ConnectionCallback; import ch.cyberduck.core.Path; import ch.cyberduck.core.Session; import ch.cyberduck.core.exception.BackgroundException; +import ch.cyberduck.core.exception.UnsupportedException; import ch.cyberduck.core.features.Write; import ch.cyberduck.core.io.ChecksumCompute; +import ch.cyberduck.core.io.DisabledChecksumCompute; import ch.cyberduck.core.io.StatusOutputStream; import ch.cyberduck.core.transfer.TransferStatus; import ch.cyberduck.core.vault.VaultRegistry; @@ -31,10 +33,10 @@ import java.util.EnumSet; public class VaultRegistryWriteFeature implements Write { private final Session session; - private final Write proxy; + private final Write proxy; private final VaultRegistry registry; - public VaultRegistryWriteFeature(final Session session, final Write proxy, final VaultRegistry registry) { + public VaultRegistryWriteFeature(final Session session, final Write proxy, final VaultRegistry registry) { this.session = session; this.proxy = proxy; this.registry = registry; @@ -47,7 +49,15 @@ public class VaultRegistryWriteFeature implements Write { @Override public EnumSet features(final Path file) { - return proxy.features(file); + try { + return registry.find(session, file).getFeature(session, Write.class, proxy).features(file); + } + catch(VaultUnlockCancelException e) { + return proxy.features(file); + } + catch(UnsupportedException e) { + return EnumSet.noneOf(Flags.class); + } } @Override @@ -58,6 +68,9 @@ public class VaultRegistryWriteFeature implements Write { catch(VaultUnlockCancelException e) { return proxy.checksum(file, status); } + catch(UnsupportedException e) { + return new DisabledChecksumCompute(); + } } @Override diff --git a/core/src/main/java/ch/cyberduck/core/worker/AbstractTransferWorker.java b/core/src/main/java/ch/cyberduck/core/worker/AbstractTransferWorker.java index 54d275150d..92b387580b 100644 --- a/core/src/main/java/ch/cyberduck/core/worker/AbstractTransferWorker.java +++ b/core/src/main/java/ch/cyberduck/core/worker/AbstractTransferWorker.java @@ -147,7 +147,7 @@ public abstract class AbstractTransferWorker extends TransferWorker { /** * Release session from pool for transfer */ - protected abstract void release(Session session, Connection type, BackgroundException failure); + protected abstract void release(Session session, Connection type, BackgroundException failure); @Override public Boolean initialize() { @@ -200,7 +200,7 @@ public abstract class AbstractTransferWorker extends TransferWorker { for(TransferItem next : transfer.getRoots()) { // Check if parent directory is found in set to determine status this.prepare(next.remote, next.local, new TransferStatus() - .setExists(!transfer.getRoots().stream().anyMatch(f -> next.remote.isChild(f.remote))), action); + .setExists(transfer.getRoots().stream().noneMatch(f -> next.remote.isChild(f.remote))), action); } this.await(); meter.reset(); diff --git a/core/src/main/java/ch/cyberduck/core/worker/ConcurrentTransferWorker.java b/core/src/main/java/ch/cyberduck/core/worker/ConcurrentTransferWorker.java index a665b1da62..1eaa3d508c 100644 --- a/core/src/main/java/ch/cyberduck/core/worker/ConcurrentTransferWorker.java +++ b/core/src/main/java/ch/cyberduck/core/worker/ConcurrentTransferWorker.java @@ -129,7 +129,7 @@ public class ConcurrentTransferWorker extends AbstractTransferWorker { } @Override - protected void release(final Session session, final Connection type, final BackgroundException failure) { + protected void release(final Session session, final Connection type, final BackgroundException failure) { switch(type) { case source: source.release(session, failure); @@ -176,7 +176,7 @@ public class ConcurrentTransferWorker extends AbstractTransferWorker { @Override protected void shutdown() { - // Always shutdown gracefully allowing the threads to return after checking transfer status + // Always shutdown gracefully, allowing the threads to return after checking transfer status pool.shutdown(true); } diff --git a/core/src/main/java/ch/cyberduck/core/worker/CopyWorker.java b/core/src/main/java/ch/cyberduck/core/worker/CopyWorker.java index 9b9ef65080..d28d704c0f 100644 --- a/core/src/main/java/ch/cyberduck/core/worker/CopyWorker.java +++ b/core/src/main/java/ch/cyberduck/core/worker/CopyWorker.java @@ -31,6 +31,7 @@ import ch.cyberduck.core.exception.ConnectionCanceledException; import ch.cyberduck.core.features.AttributesFinder; import ch.cyberduck.core.features.Copy; import ch.cyberduck.core.features.Directory; +import ch.cyberduck.core.features.Write; import ch.cyberduck.core.io.DisabledStreamListener; import ch.cyberduck.core.pool.SessionPool; import ch.cyberduck.core.threading.BackgroundActionState; @@ -86,12 +87,15 @@ public class CopyWorker extends Worker> { if(r.getKey().isDirectory() && !copy.isRecursive(r.getKey(), r.getValue())) { // Create directory unless copy implementation is recursive final Directory directory = session.getFeature(Directory.class); - result.put(r.getKey(), directory.mkdir(r.getValue(), + result.put(r.getKey(), directory.mkdir(session.getFeature(Write.class), r.getValue(), new TransferStatus().setLength(0L).setRegion(r.getKey().attributes().getRegion()))); } else { final TransferStatus status = new TransferStatus() .setMime(new MappingMimeTypeService().getMime(r.getValue().getName())) + .setRegion(r.getKey().attributes().getRegion()) + .setModified(r.getKey().attributes().getModificationDate()) + .setCreated(r.getKey().attributes().getCreationDate()) .setAcl(r.getKey().attributes().getAcl()) .setPermission(r.getKey().attributes().getPermission()) .setEncryption(r.getKey().attributes().getEncryption()) diff --git a/core/src/main/java/ch/cyberduck/core/worker/CreateDirectoryWorker.java b/core/src/main/java/ch/cyberduck/core/worker/CreateDirectoryWorker.java index b5579cfe1a..9389eaf5d3 100644 --- a/core/src/main/java/ch/cyberduck/core/worker/CreateDirectoryWorker.java +++ b/core/src/main/java/ch/cyberduck/core/worker/CreateDirectoryWorker.java @@ -20,6 +20,7 @@ package ch.cyberduck.core.worker; import ch.cyberduck.core.LocaleFactory; import ch.cyberduck.core.Path; import ch.cyberduck.core.PathAttributes; +import ch.cyberduck.core.PathContainerService; import ch.cyberduck.core.Session; import ch.cyberduck.core.SimplePathPredicate; import ch.cyberduck.core.exception.BackgroundException; @@ -28,6 +29,7 @@ import ch.cyberduck.core.features.AttributesFinder; import ch.cyberduck.core.features.Directory; import ch.cyberduck.core.features.Encryption; import ch.cyberduck.core.features.UnixPermission; +import ch.cyberduck.core.features.Write; import ch.cyberduck.core.preferences.PreferencesFactory; import ch.cyberduck.core.transfer.TransferStatus; @@ -54,23 +56,24 @@ public class CreateDirectoryWorker extends Worker { final Directory feature = session.getFeature(Directory.class); log.debug("Run with feature {}", feature); final TransferStatus status = new TransferStatus().setLength(0L); + final Path container = session.getFeature(PathContainerService.class).getContainer(folder); final Encryption encryption = session.getFeature(Encryption.class); if(encryption != null) { - status.setEncryption(encryption.getDefault(folder)); + status.setEncryption(encryption.getDefault(container)); } status.setModified(System.currentTimeMillis()); if(PreferencesFactory.get().getBoolean("touch.permissions.change")) { final UnixPermission permission = session.getFeature(UnixPermission.class); if(permission != null) { - status.setPermission(permission.getDefault(EnumSet.of(Path.Type.directory))); + status.setPermission(permission.getDefault(folder.getParent(), EnumSet.of(Path.Type.directory))); } final AclPermission acl = session.getFeature(AclPermission.class); if(acl != null) { - status.setAcl(acl.getDefault(folder)); + status.setAcl(acl.getDefault(container)); } } status.setRegion(region); - final Path result = feature.mkdir(folder, status); + final Path result = feature.mkdir(session.getFeature(Write.class), folder, status); if(PathAttributes.EMPTY.equals(result.attributes())) { return new Path(folder).withAttributes(session.getFeature(AttributesFinder.class).find(result)); } diff --git a/core/src/main/java/ch/cyberduck/core/worker/DeleteWorker.java b/core/src/main/java/ch/cyberduck/core/worker/DeleteWorker.java index d50aa77841..7cfe893a8d 100644 --- a/core/src/main/java/ch/cyberduck/core/worker/DeleteWorker.java +++ b/core/src/main/java/ch/cyberduck/core/worker/DeleteWorker.java @@ -117,8 +117,15 @@ public class DeleteWorker extends Worker> { recursive.putAll(this.compile(delete, list, new WorkerListProgressListener(this, listener), file)); } // Iterate again to delete any files that can be omitted when recursive operation is supported - if(delete.isRecursive()) { - recursive.keySet().removeIf(f -> !f.getType().contains(Path.Type.decrypted) && recursive.keySet().stream().anyMatch(f::isChild)); + for(Iterator it = recursive.keySet().iterator(); it.hasNext(); ) { + final Path file = it.next(); + if(delete.features(file).contains(Delete.Flags.recursive)) { + if(!file.getType().contains(Path.Type.decrypted)) { + if(recursive.keySet().stream().anyMatch(file::isChild)) { + it.remove(); + } + } + } } final HostPreferences preferences = HostPreferencesFactory.get(session.getHost()); if(preferences.getBoolean("versioning.enable") && preferences.getBoolean("versioning.delete.enable")) { @@ -145,7 +152,7 @@ public class DeleteWorker extends Worker> { listener.message(MessageFormat.format(LocaleFactory.localizedString("Deleting {0}", "Status"), file.getName())); callback.delete(file); if(file.isDirectory()) { - if(delete.isRecursive()) { + if(delete.features(file).contains(Delete.Flags.recursive)) { files.stream().filter(f -> f.isChild(file)).forEach(callback::delete); } } @@ -160,7 +167,7 @@ public class DeleteWorker extends Worker> { final Map recursive = new LinkedHashMap<>(); if(file.isFile() || file.isSymbolicLink()) { if(null != file.attributes().getVersionId()) { - if(file.attributes().isDuplicate()) { + if(file.attributes().isDuplicate() || file.attributes().isTrashed()) { // Delete previous versions or pending upload log.warn("Delete version {}", file); } @@ -178,7 +185,7 @@ public class DeleteWorker extends Worker> { recursive.put(file, new TransferStatus().setLockId(this.getLockId(file))); } else if(file.isDirectory()) { - if(!delete.isRecursive() || file.getType().contains(Path.Type.decrypted)) { + if(!delete.features(file).contains(Delete.Flags.recursive) || file.getType().contains(Path.Type.decrypted)) { for(Path child : list.list(file, listener).filter(filter)) { if(this.isCanceled()) { throw new ConnectionCanceledException(); diff --git a/core/src/main/java/ch/cyberduck/core/worker/MoveWorker.java b/core/src/main/java/ch/cyberduck/core/worker/MoveWorker.java index ce23c42ede..c9629f145b 100644 --- a/core/src/main/java/ch/cyberduck/core/worker/MoveWorker.java +++ b/core/src/main/java/ch/cyberduck/core/worker/MoveWorker.java @@ -37,6 +37,7 @@ import ch.cyberduck.core.features.Delete; import ch.cyberduck.core.features.Directory; import ch.cyberduck.core.features.Move; import ch.cyberduck.core.features.Versioning; +import ch.cyberduck.core.features.Write; import ch.cyberduck.core.pool.SessionPool; import ch.cyberduck.core.preferences.HostPreferences; import ch.cyberduck.core.preferences.HostPreferencesFactory; @@ -108,13 +109,16 @@ public class MoveWorker extends Worker> { if(r.getKey().isDirectory() && !feature.isRecursive(r.getKey(), r.getValue())) { log.warn("Move operation is not recursive. Create directory {}", r.getValue()); // Create directory unless copy implementation is recursive - result.put(r.getKey(), session.getFeature(Directory.class).mkdir(r.getValue(), + result.put(r.getKey(), session.getFeature(Directory.class).mkdir(session.getFeature(Write.class), r.getValue(), new TransferStatus().setLength(0L).setRegion(r.getKey().attributes().getRegion()))); } else { final TransferStatus status = new TransferStatus() .setLockId(this.getLockId(r.getKey())) .setMime(new MappingMimeTypeService().getMime(r.getValue().getName())) + .setRegion(r.getKey().attributes().getRegion()) + .setModified(r.getKey().attributes().getModificationDate()) + .setCreated(r.getKey().attributes().getCreationDate()) .setAcl(r.getKey().attributes().getAcl()) .setPermission(r.getKey().attributes().getPermission()) .setEncryption(r.getKey().attributes().getEncryption()) @@ -153,7 +157,7 @@ public class MoveWorker extends Worker> { final Path directory = target.getParent(); if(!new CachingFindFeature(session, cache).find(directory)) { log.debug("Create directory {} for versions", directory); - session.getFeature(Directory.class).mkdir(directory, new TransferStatus()); + session.getFeature(Directory.class).mkdir(session.getFeature(Write.class), directory, new TransferStatus()); } if(version.isDirectory()) { if(!session.getFeature(Move.class).isRecursive(version, target)) { diff --git a/core/src/main/java/ch/cyberduck/core/worker/SearchWorker.java b/core/src/main/java/ch/cyberduck/core/worker/SearchWorker.java index dffb0b387d..0c647e535c 100644 --- a/core/src/main/java/ch/cyberduck/core/worker/SearchWorker.java +++ b/core/src/main/java/ch/cyberduck/core/worker/SearchWorker.java @@ -31,6 +31,7 @@ import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; import java.text.MessageFormat; +import java.util.EnumSet; import java.util.HashSet; import java.util.Objects; import java.util.Set; @@ -64,18 +65,19 @@ public class SearchWorker extends Worker> { throw new ConnectionCanceledException(); } final AttributedList list; - if(!search.isRecursive() && cache.isCached(workdir)) { + final EnumSet features = search.features(workdir); + if(!features.contains(Search.Flags.recursive) && cache.isCached(workdir)) { list = new AttributedList<>(cache.get(workdir)); } else { // Get filtered list from search list = search.search(workdir, new RecursiveSearchFilter(filter), new WorkerListProgressListener(this, listener)); - if(!search.isRecursive()) { + if(!features.contains(Search.Flags.recursive)) { cache.put(workdir, new AttributedList<>(list)); } } final Set removal = new HashSet<>(); - if(search.isRecursive()) { + if(features.contains(Search.Flags.recursive)) { for(Path directory : list) { if(directory.isDirectory()) { if(!list.toStream().filter(f -> f.isChild(directory)).findAny().isPresent()) { diff --git a/core/src/main/java/ch/cyberduck/core/worker/SingleTransferWorker.java b/core/src/main/java/ch/cyberduck/core/worker/SingleTransferWorker.java index 7ecf088244..6d56fe4c5f 100644 --- a/core/src/main/java/ch/cyberduck/core/worker/SingleTransferWorker.java +++ b/core/src/main/java/ch/cyberduck/core/worker/SingleTransferWorker.java @@ -37,10 +37,10 @@ import java.util.concurrent.Future; public class SingleTransferWorker extends AbstractTransferWorker { - private final Session source; - private final Session destination; + private final Session source; + private final Session destination; - public SingleTransferWorker(final Session source, final Session destination, final Transfer transfer, final TransferOptions options, + public SingleTransferWorker(final Session source, final Session destination, final Transfer transfer, final TransferOptions options, final TransferSpeedometer meter, final TransferPrompt prompt, final TransferErrorCallback error, final ProgressListener listener, final StreamListener streamListener, @@ -51,7 +51,7 @@ public class SingleTransferWorker extends AbstractTransferWorker { } @Override - public Session borrow(final Connection type) { + public Session borrow(final Connection type) { switch(type) { case source: return source; @@ -62,7 +62,7 @@ public class SingleTransferWorker extends AbstractTransferWorker { } @Override - protected void release(final Session session, final Connection type, final BackgroundException failure) { + protected void release(final Session session, final Connection type, final BackgroundException failure) { // } diff --git a/core/src/main/java/ch/cyberduck/core/worker/TouchWorker.java b/core/src/main/java/ch/cyberduck/core/worker/TouchWorker.java index c3ac5180b5..dfe504f899 100644 --- a/core/src/main/java/ch/cyberduck/core/worker/TouchWorker.java +++ b/core/src/main/java/ch/cyberduck/core/worker/TouchWorker.java @@ -21,6 +21,7 @@ import ch.cyberduck.core.LocaleFactory; import ch.cyberduck.core.MappingMimeTypeService; import ch.cyberduck.core.Path; import ch.cyberduck.core.PathAttributes; +import ch.cyberduck.core.PathContainerService; import ch.cyberduck.core.Session; import ch.cyberduck.core.SimplePathPredicate; import ch.cyberduck.core.exception.BackgroundException; @@ -30,6 +31,7 @@ import ch.cyberduck.core.features.Encryption; import ch.cyberduck.core.features.Redundancy; import ch.cyberduck.core.features.Touch; import ch.cyberduck.core.features.UnixPermission; +import ch.cyberduck.core.features.Write; import ch.cyberduck.core.preferences.PreferencesFactory; import ch.cyberduck.core.transfer.TransferStatus; import ch.cyberduck.ui.browser.SearchFilterFactory; @@ -61,26 +63,27 @@ public class TouchWorker extends Worker { .setLength(0L) .setMime(new MappingMimeTypeService().getMime(file.getName())) .setLockId(this.getLockId(file)); + final Path container = session.getFeature(PathContainerService.class).getContainer(file); final Encryption encryption = session.getFeature(Encryption.class); if(encryption != null) { - status.setEncryption(encryption.getDefault(file)); + status.setEncryption(encryption.getDefault(container)); } final Redundancy redundancy = session.getFeature(Redundancy.class); if(redundancy != null) { - status.setStorageClass(redundancy.getDefault()); + status.setStorageClass(redundancy.getDefault(container)); } status.setModified(System.currentTimeMillis()); if(PreferencesFactory.get().getBoolean("touch.permissions.change")) { final UnixPermission permission = session.getFeature(UnixPermission.class); if(permission != null) { - status.setPermission(permission.getDefault(EnumSet.of(Path.Type.file))); + status.setPermission(permission.getDefault(file.getParent(), EnumSet.of(Path.Type.file))); } final AclPermission acl = session.getFeature(AclPermission.class); if(acl != null) { - status.setAcl(acl.getDefault(file)); + status.setAcl(acl.getDefault(container)); } } - final Path result = feature.touch(file, status); + final Path result = feature.touch(session.getFeature(Write.class), file, status); if(PathAttributes.EMPTY.equals(result.attributes())) { return result.withAttributes(session.getFeature(AttributesFinder.class).find(result)); } diff --git a/core/src/main/objc/FinderLocal.h b/core/src/main/objc/FinderLocal.h deleted file mode 100755 index 504f5096cc..0000000000 --- a/core/src/main/objc/FinderLocal.h +++ /dev/null @@ -1,29 +0,0 @@ -/* DO NOT EDIT THIS FILE - it is machine generated */ -#include -/* Header for class ch_cyberduck_core_local_FinderLocal */ - -#ifndef _Included_ch_cyberduck_core_local_FinderLocal -#define _Included_ch_cyberduck_core_local_FinderLocal -#ifdef __cplusplus -extern "C" { -#endif -#undef ch_cyberduck_core_local_FinderLocal_FILE_TYPE -#define ch_cyberduck_core_local_FinderLocal_FILE_TYPE 1L -#undef ch_cyberduck_core_local_FinderLocal_DIRECTORY_TYPE -#define ch_cyberduck_core_local_FinderLocal_DIRECTORY_TYPE 2L -#undef ch_cyberduck_core_local_FinderLocal_SYMBOLIC_LINK_TYPE -#define ch_cyberduck_core_local_FinderLocal_SYMBOLIC_LINK_TYPE 4L -#undef ch_cyberduck_core_local_FinderLocal_VOLUME_TYPE -#define ch_cyberduck_core_local_FinderLocal_VOLUME_TYPE 8L -/* - * Class: ch_cyberduck_core_local_FinderLocal - * Method: resolveAliasNative - * Signature: (Ljava/lang/String;)Ljava/lang/String; - */ -JNIEXPORT jstring JNICALL Java_ch_cyberduck_core_local_FinderLocal_resolveAliasNative - (JNIEnv *, jobject, jstring); - -#ifdef __cplusplus -} -#endif -#endif diff --git a/core/src/main/objc/FinderLocal.m b/core/src/main/objc/FinderLocal.m deleted file mode 100644 index dd6e85eacc..0000000000 --- a/core/src/main/objc/FinderLocal.m +++ /dev/null @@ -1,60 +0,0 @@ -/* - * Copyright (c) 2005 David Kocher. All rights reserved. - * http://cyberduck.ch/ - * - * 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. - * - * Bug fixes, suggestions and comments should be sent to: - * dkocher@cyberduck.ch - */ - -#include - -#import -#import -#import -#import - -JNIEXPORT jstring JNICALL Java_ch_cyberduck_core_local_FinderLocal_resolveAliasNative(JNIEnv *env, jobject this, jstring absolute) -{ -JNF_COCOA_ENTER(env); - NSString *path = JNFJavaToNSString(env, absolute); - NSString *resolvedPath = nil; - - CFURLRef url = CFURLCreateWithFileSystemPath - (kCFAllocatorDefault, (CFStringRef)path, kCFURLPOSIXPathStyle, NO); - if (url != NULL) - { - FSRef fsRef; - if (CFURLGetFSRef(url, &fsRef)) - { - Boolean targetIsFolder, wasAliased; - OSErr err = FSResolveAliasFile (&fsRef, true, &targetIsFolder, &wasAliased); - if ((err == noErr) && wasAliased) - { - CFURLRef resolvedUrl = CFURLCreateFromFSRef(kCFAllocatorDefault, &fsRef); - if (resolvedUrl != NULL) - { - resolvedPath = (NSString*) CFURLCopyFileSystemPath(resolvedUrl, kCFURLPOSIXPathStyle); - CFRelease(resolvedUrl); - } - } - } - CFRelease(url); - } - - if (resolvedPath == nil) - { - resolvedPath = [NSString stringWithString:path]; - } - return JNFNSToJavaString(env, resolvedPath); -JNF_COCOA_EXIT(env); -} diff --git a/core/src/test/csharp/Cyberduck.Core.Test.csproj b/core/src/test/csharp/Cyberduck.Core.Test.csproj index 3b01caa9fd..59a5c5e1af 100644 --- a/core/src/test/csharp/Cyberduck.Core.Test.csproj +++ b/core/src/test/csharp/Cyberduck.Core.Test.csproj @@ -18,6 +18,7 @@ + diff --git a/core/src/test/csharp/NativeMethods.txt b/core/src/test/csharp/NativeMethods.txt new file mode 100644 index 0000000000..147640d7b3 --- /dev/null +++ b/core/src/test/csharp/NativeMethods.txt @@ -0,0 +1 @@ +PathCchCanonicalizeEx diff --git a/core/src/test/csharp/ch/cyberduck/core/local/ExplorerRevealTest.cs b/core/src/test/csharp/ch/cyberduck/core/local/ExplorerRevealTest.cs new file mode 100644 index 0000000000..6fff574cd5 --- /dev/null +++ b/core/src/test/csharp/ch/cyberduck/core/local/ExplorerRevealTest.cs @@ -0,0 +1,18 @@ +using System.IO; +using ch.cyberduck.core.local; +using NUnit.Framework; + +namespace Ch.Cyberduck.Core.Local; + +[TestFixture(Explicit = true)] +public class ExplorerRevealTest +{ + [Test] + public void TestReveal() + { + SystemLocal temp = new(Path.GetTempPath()); + SystemLocal file = new(temp, Path.GetRandomFileName()); + new DefaultLocalTouchFeature().touch(file); + Assert.That(new ExplorerRevealService().reveal(file, true), Is.True); + } +} diff --git a/core/src/test/csharp/ch/cyberduck/core/local/NTFSFilesystemBookmarkResolverTest.cs b/core/src/test/csharp/ch/cyberduck/core/local/NTFSFilesystemBookmarkResolverTest.cs new file mode 100644 index 0000000000..4ef9fe0eee --- /dev/null +++ b/core/src/test/csharp/ch/cyberduck/core/local/NTFSFilesystemBookmarkResolverTest.cs @@ -0,0 +1,24 @@ +using ch.cyberduck.core.local; +using NUnit.Framework; +using NUnit.Framework.Constraints; +using CoreLocal = ch.cyberduck.core.Local; +using NetPath = System.IO.Path; + +namespace Ch.Cyberduck.Core.Local; + +[TestFixture] +public class NTFSFilesystemBookmarkResolverTest +{ + [Test] + public void EnsureRoundtrip() + { + SystemLocal temp = new(NetPath.GetTempPath()); + SystemLocal file = new(temp, NetPath.GetRandomFileName()); + new DefaultLocalTouchFeature().touch(file); + NTFSFilesystemBookmarkResolver resolver = new(file); + var bookmark = resolver.create(file); + Assert.That(bookmark, Is.Not.Null); + CoreLocal resolved = (CoreLocal)resolver.resolve(bookmark); + Assert.That(resolved, Is.EqualTo(file)); + } +} diff --git a/core/src/test/csharp/ch/cyberduck/core/local/SystemLocalTest.cs b/core/src/test/csharp/ch/cyberduck/core/local/SystemLocalTest.cs index 5eb2e7be26..020472c293 100644 --- a/core/src/test/csharp/ch/cyberduck/core/local/SystemLocalTest.cs +++ b/core/src/test/csharp/ch/cyberduck/core/local/SystemLocalTest.cs @@ -1,7 +1,12 @@ -using ch.cyberduck.core; +using System.Runtime.InteropServices; +using System.Text; +using ch.cyberduck.core; using java.nio.file; using java.util; using NUnit.Framework; +using Windows.Win32; +using Windows.Win32.Foundation; +using Windows.Win32.UI.Shell; using CorePath = ch.cyberduck.core.Path; using Path = System.IO.Path; @@ -38,31 +43,28 @@ namespace Ch.Cyberduck.Core.Local } [Test] - public void TestBadDriveLetter([Values(@"#:\", @"\#:\", @"\\.\#:\")] string path) + public void TestBadDriveLetter([Values(@"#:\", @"\\.\#:\")] string path) { - // Sanitized empty - Assert.That(new SystemLocal(path).getAbsolute(), Is.Empty); + // Bad paths are copied verbatim. + Assert.That(new SystemLocal(path).getAbsolute(), Is.EqualTo(path)); } [Test, Sequential] - public void TestPathSanitize( - [Values( - /* 00 */ "C:\\C:" - )] string path, - [Values( - /* 00 */ "C:\\C_" - )] string expected) + public void TestPathCanonicalize( + [Values([ + /* 00 */ "C:\\C:", + /* 01 */ @"\\?\C:", + /* 02 */ @"\\?\C:\", + ])] string path, + [Values([ + /* 00 */ "C:\\C_", + /* 01 */ "C:\\", + /* 02 */ "C:\\", + ])] string expected) { Assert.That(new SystemLocal(path).getAbsolute(), Is.EqualTo(expected)); } - [Test] - public void TestConvertToDirectorySeparator() - { - SystemLocal path = new(PIPE_NAME.Replace('\\', '/')); - Assert.That(path.getAbsolute(), Is.EqualTo(PIPE_NAME)); - } - [Test] public void TestDirectorySeparators([ValueSource(nameof(TestDirectorySeparatorsValues))] char sep) { @@ -77,29 +79,6 @@ namespace Ch.Cyberduck.Core.Local Assert.That(new SystemLocal("").getAbsolute(), Is.Empty); } - /// - /// - /// - [Test] - public void TestFileFormats() - { - string[] filenames = - [ - @"c:\temp\test-file.txt", - @"\\127.0.0.1\c$\temp\test-file.txt", - @"\\LOCALHOST\c$\temp\test-file.txt", - @"\\.\c:\temp\test-file.txt", - @"\\?\c:\temp\test-file.txt", - @"\\.\UNC\LOCALHOST\c$\temp\test-file.txt", - @"\\127.0.0.1\c$\temp\test-file.txt" - ]; - foreach (var item in filenames) - { - // Local passes through invalid paths, and logs an error - Assert.That(new SystemLocal(item).getAbsolute(), Is.EqualTo(item)); - } - } - [Test] public void TestPathToLocal() { @@ -112,7 +91,6 @@ namespace Ch.Cyberduck.Core.Local [Test] public void TestAbsoluteEquality([Values( PIPE_NAME, - @"\\?\C:\ÄÖÜßßäöü", WSL_PATH, @"\Volumes\System\Test", @"C:\Directory\File.ext", @@ -126,5 +104,63 @@ namespace Ch.Cyberduck.Core.Local { Assert.That(new SystemLocal("~/.ssh/known_hosts").getAbsolute(), Is.Not.Empty); } + + /// + /// + /// + [Test] + public unsafe void TestPathCchCanonicalize([Values( + @"c:\temp\test-file.txt", + @"\\127.0.0.1\c$\temp\test-file.txt", + @"\\LOCALHOST\c$\temp\test-file.txt", + @"\\?\UNC\LOCALHOST\c$\temp\test-file.txt", + @"\\?\c:\temp\test-file.txt", + @"\\127.0.0.1\c$\temp\test-file.txt", + @"\\?\C:\Temp", + //@"\\?\C:\Temp\", // Paths.get() removes trailing separator + @"\\?\C:\ Leading Trailing Whitespace \", // Paths.get() keeps trailing separator + @"\Test" + )] string path) + { + SystemLocal local = new(path); + var absolute = local.getAbsolute(); + string canonical; + fixed (char* canonicalLocal = new char[CorePInvoke.PATHCCH_MAX_CCH]) + { + fixed (char* pathLocal = path) + { + if (PInvoke.PathCchCanonicalizeEx(canonicalLocal, CorePInvoke.PATHCCH_MAX_CCH, pathLocal, PATHCCH_OPTIONS.PATHCCH_ALLOW_LONG_PATHS | PATHCCH_OPTIONS.PATHCCH_FORCE_ENABLE_LONG_NAME_PROCESS) is + { + Failed: true, + Value: { } canonicalizeError + }) + { + throw Marshal.GetExceptionForHR(canonicalizeError); + } + } + + canonical = ((PCWSTR)canonicalLocal).ToString(); + } + + Assert.That(absolute, Is.EqualTo(canonical)); + } + + [Test] + public void EnsurePrefixPlatform() + { + StringBuilder builder = new((int)CorePInvoke.PATHCCH_MAX_CCH); + builder.Append("C:"); + while (builder.Length < 260) + { + builder.Append('\\'); + builder.Append(Path.GetTempFileName()); + } + SystemLocal local = new(builder.ToString()); + Assert.That(local.getAbsolute(), Is.Not.SubPathOf(@"\\?\")); + Assert.That(local.NativePath(), Is.SubPathOf(@"\\?\")); +#if NETFRAMEWORK + Assert.That(local.PlatformPath(), Is.SubPathOf(@"\\?\")); +#endif + } } } diff --git a/core/src/test/java/ch/cyberduck/core/DefaultHostPasswordStoreTest.java b/core/src/test/java/ch/cyberduck/core/DefaultHostPasswordStoreTest.java index d4eeb09608..6e0d9e90f7 100644 --- a/core/src/test/java/ch/cyberduck/core/DefaultHostPasswordStoreTest.java +++ b/core/src/test/java/ch/cyberduck/core/DefaultHostPasswordStoreTest.java @@ -38,7 +38,7 @@ public class DefaultHostPasswordStoreTest { public String getOAuthRedirectUrl() { return "x-cyberduck-action:oauth"; } - })); + })).toArray(new String[0]); assertEquals("clientid", prefix[0]); assertEquals("Test", prefix[1]); } @@ -60,7 +60,7 @@ public class DefaultHostPasswordStoreTest { public String getOAuthRedirectUrl() { return "x-cyberduck-action:oauth"; } - }).withCredentials(new Credentials("user"))); + }).setCredentials(new Credentials("user"))).toArray(new String[0]); assertEquals("clientid (user)", prefix[0]); assertEquals("Test (user)", prefix[1]); } diff --git a/core/src/test/java/ch/cyberduck/core/DefaultPathPredicateTest.java b/core/src/test/java/ch/cyberduck/core/DefaultPathPredicateTest.java index c9d8896c99..3aa4b971bf 100644 --- a/core/src/test/java/ch/cyberduck/core/DefaultPathPredicateTest.java +++ b/core/src/test/java/ch/cyberduck/core/DefaultPathPredicateTest.java @@ -76,34 +76,34 @@ public class DefaultPathPredicateTest { @Test public void testPredicateVersionIdFile() { - final Path t = new Path("/f", EnumSet.of(Path.Type.file), new PathAttributes().setVersionId("1")); + final Path t = new Path("/f", EnumSet.of(Path.Type.file), new DefaultPathAttributes().setVersionId("1")); assertTrue(new DefaultPathPredicate(t).test(t)); - assertTrue(new DefaultPathPredicate(t).test(new Path("/f", EnumSet.of(Path.Type.file), new PathAttributes().setVersionId("1")))); - assertFalse(new DefaultPathPredicate(t).test(new Path("/f", EnumSet.of(Path.Type.file), new PathAttributes().setVersionId("2")))); + assertTrue(new DefaultPathPredicate(t).test(new Path("/f", EnumSet.of(Path.Type.file), new DefaultPathAttributes().setVersionId("1")))); + assertFalse(new DefaultPathPredicate(t).test(new Path("/f", EnumSet.of(Path.Type.file), new DefaultPathAttributes().setVersionId("2")))); } @Test public void testPredicateFileIdFile() { - final Path t = new Path("/f", EnumSet.of(Path.Type.file), new PathAttributes().setFileId("1")); + final Path t = new Path("/f", EnumSet.of(Path.Type.file), new DefaultPathAttributes().setFileId("1")); assertTrue(new DefaultPathPredicate(t).test(t)); - assertTrue(new DefaultPathPredicate(t).test(new Path("/f", EnumSet.of(Path.Type.file), new PathAttributes().setFileId("1")))); - assertFalse(new DefaultPathPredicate(t).test(new Path("/f", EnumSet.of(Path.Type.file), new PathAttributes().setFileId("2")))); + assertTrue(new DefaultPathPredicate(t).test(new Path("/f", EnumSet.of(Path.Type.file), new DefaultPathAttributes().setFileId("1")))); + assertFalse(new DefaultPathPredicate(t).test(new Path("/f", EnumSet.of(Path.Type.file), new DefaultPathAttributes().setFileId("2")))); } @Test public void testPredicateVersionIdDirectory() { - final Path t = new Path("/f", EnumSet.of(Path.Type.directory), new PathAttributes().setVersionId("1")); + final Path t = new Path("/f", EnumSet.of(Path.Type.directory), new DefaultPathAttributes().setVersionId("1")); assertTrue(new DefaultPathPredicate(t).test(t)); - assertTrue(new DefaultPathPredicate(t).test(new Path("/f", EnumSet.of(Path.Type.directory), new PathAttributes().setVersionId("1")))); - assertTrue(new DefaultPathPredicate(t).test(new Path("/f", EnumSet.of(Path.Type.directory), new PathAttributes().setVersionId("2")))); + assertTrue(new DefaultPathPredicate(t).test(new Path("/f", EnumSet.of(Path.Type.directory), new DefaultPathAttributes().setVersionId("1")))); + assertTrue(new DefaultPathPredicate(t).test(new Path("/f", EnumSet.of(Path.Type.directory), new DefaultPathAttributes().setVersionId("2")))); } @Test public void testPredicateFileIdDirectory() { - final Path t = new Path("/f", EnumSet.of(Path.Type.directory), new PathAttributes().setFileId("1")); + final Path t = new Path("/f", EnumSet.of(Path.Type.directory), new DefaultPathAttributes().setFileId("1")); assertTrue(new DefaultPathPredicate(t).test(t)); - assertTrue(new DefaultPathPredicate(t).test(new Path("/f", EnumSet.of(Path.Type.directory), new PathAttributes().setFileId("1")))); - assertFalse(new DefaultPathPredicate(t).test(new Path("/f", EnumSet.of(Path.Type.directory), new PathAttributes().setFileId("2")))); + assertTrue(new DefaultPathPredicate(t).test(new Path("/f", EnumSet.of(Path.Type.directory), new DefaultPathAttributes().setFileId("1")))); + assertFalse(new DefaultPathPredicate(t).test(new Path("/f", EnumSet.of(Path.Type.directory), new DefaultPathAttributes().setFileId("2")))); } @Test diff --git a/core/src/test/java/ch/cyberduck/core/KeychainLoginServiceTest.java b/core/src/test/java/ch/cyberduck/core/KeychainLoginServiceTest.java index 76ad43b13e..1393628f8d 100644 --- a/core/src/test/java/ch/cyberduck/core/KeychainLoginServiceTest.java +++ b/core/src/test/java/ch/cyberduck/core/KeychainLoginServiceTest.java @@ -4,6 +4,7 @@ import ch.cyberduck.core.exception.BackgroundException; import ch.cyberduck.core.exception.LoginCanceledException; import ch.cyberduck.core.preferences.PreferencesFactory; import ch.cyberduck.core.proxy.DisabledProxyFinder; +import ch.cyberduck.core.ssl.DefaultX509KeyManager; import ch.cyberduck.core.threading.CancelCallback; import org.junit.Test; @@ -28,7 +29,7 @@ public class KeychainLoginServiceTest { }; session.open(new DisabledProxyFinder(), new DisabledHostKeyCallback(), new DisabledLoginCallback(), new DisabledCancelCallback()); LoginService l = new KeychainLoginService(new DisabledPasswordStore()); - l.authenticate(new DisabledProxyFinder(), session, new ProgressListener() { + l.authenticate(session, new ProgressListener() { int i = 0; @Override @@ -51,7 +52,8 @@ public class KeychainLoginServiceTest { @Test(expected = LoginCanceledException.class) public void testCancel() throws Exception { LoginService l = new KeychainLoginService(new DisabledPasswordStore()); - l.validate(new Host(new TestProtocol(), "h"), new DisabledLoginCallback(), new LoginOptions()); + l.validate(new Host(new TestProtocol(), "h"), + new DefaultX509KeyManager(), new DisabledLoginCallback(), new LoginOptions()); } @Test @@ -68,7 +70,7 @@ public class KeychainLoginServiceTest { final Credentials credentials = new Credentials(); credentials.setUsername("u"); final Host host = new Host(new TestProtocol(), "test.cyberduck.ch", credentials); - l.validate(host, new DisabledLoginCallback(), new LoginOptions(host.getProtocol())); + l.validate(host, new DefaultX509KeyManager(), new DisabledLoginCallback(), new LoginOptions(host.getProtocol())); assertTrue(keychain.get()); assertFalse(host.getCredentials().isSaved()); assertEquals("P", host.getCredentials().getPassword()); diff --git a/core/src/test/java/ch/cyberduck/core/LocalAttributesTest.java b/core/src/test/java/ch/cyberduck/core/LocalAttributesTest.java index 481effda0d..f48759fe0a 100644 --- a/core/src/test/java/ch/cyberduck/core/LocalAttributesTest.java +++ b/core/src/test/java/ch/cyberduck/core/LocalAttributesTest.java @@ -15,13 +15,12 @@ package ch.cyberduck.core; * GNU General Public License for more details. */ -import org.junit.Assert; import org.junit.Test; import java.io.File; import java.util.UUID; -import static org.junit.Assert.assertEquals; +import static org.junit.Assert.*; public class LocalAttributesTest { @@ -35,6 +34,54 @@ public class LocalAttributesTest { f.delete(); } + @Test + public void testGetCreationDate() throws Exception { + assertEquals(-1, new LocalAttributes(UUID.randomUUID().toString()).getCreationDate()); + final File f = new File(UUID.randomUUID().toString()); + f.createNewFile(); + LocalAttributes a = new LocalAttributes(f.getAbsolutePath()); + assertTrue(a.getCreationDate() > 0); + f.delete(); + } + + @Test + public void testGetAccessedDate() throws Exception { + assertEquals(-1, new LocalAttributes(UUID.randomUUID().toString()).getAccessedDate()); + final File f = new File(UUID.randomUUID().toString()); + f.createNewFile(); + LocalAttributes a = new LocalAttributes(f.getAbsolutePath()); + assertTrue(a.getAccessedDate() > 0); + f.delete(); + } + + @Test + public void getGetModificationDate() throws Exception { + assertEquals(-1, new LocalAttributes(UUID.randomUUID().toString()).getModificationDate()); + final File f = new File(UUID.randomUUID().toString()); + f.createNewFile(); + LocalAttributes a = new LocalAttributes(f.getAbsolutePath()); + assertTrue(a.getModificationDate() > 0); + f.delete(); + } + + @Test + public void testGetOwner() { + LocalAttributes a = new LocalAttributes(UUID.randomUUID().toString()); + assertNull(a.getOwner()); + } + + @Test + public void testGetGroup() { + LocalAttributes a = new LocalAttributes(UUID.randomUUID().toString()); + assertNull(a.getGroup()); + } + + @Test + public void testIsBundle() { + LocalAttributes a = new LocalAttributes(UUID.randomUUID().toString()); + assertFalse(a.isBundle()); + } + @Test public void testGetPermissionNotFound() { assertEquals(Permission.EMPTY, new LocalAttributes(UUID.randomUUID().toString()).getPermission()); @@ -46,7 +93,7 @@ public class LocalAttributesTest { final File f = new File(UUID.randomUUID().toString()); f.createNewFile(); LocalAttributes a = new LocalAttributes(f.getAbsolutePath()); - Assert.assertTrue(a.getModificationDate() > 0); + assertTrue(a.getModificationDate() > 0); f.delete(); } } diff --git a/core/src/test/java/ch/cyberduck/core/NullDirectoryFeature.java b/core/src/test/java/ch/cyberduck/core/NullDirectoryFeature.java index dbe95f19b2..9673c7a5b3 100644 --- a/core/src/test/java/ch/cyberduck/core/NullDirectoryFeature.java +++ b/core/src/test/java/ch/cyberduck/core/NullDirectoryFeature.java @@ -17,11 +17,12 @@ package ch.cyberduck.core; import ch.cyberduck.core.exception.BackgroundException; import ch.cyberduck.core.features.Directory; +import ch.cyberduck.core.features.Write; import ch.cyberduck.core.transfer.TransferStatus; public class NullDirectoryFeature implements Directory { @Override - public Path mkdir(final Path folder, final TransferStatus status) throws BackgroundException { + public Path mkdir(final Write writer, final Path folder, final TransferStatus status) throws BackgroundException { return folder; } } diff --git a/core/src/test/java/ch/cyberduck/core/NullLocal.java b/core/src/test/java/ch/cyberduck/core/NullLocal.java index 2596323238..01da7412bc 100755 --- a/core/src/test/java/ch/cyberduck/core/NullLocal.java +++ b/core/src/test/java/ch/cyberduck/core/NullLocal.java @@ -37,6 +37,6 @@ public class NullLocal extends Local { @Override public OutputStream getOutputStream(boolean append) { - return NullOutputStream.NULL_OUTPUT_STREAM; + return NullOutputStream.INSTANCE; } } diff --git a/core/src/test/java/ch/cyberduck/core/NullSession.java b/core/src/test/java/ch/cyberduck/core/NullSession.java index 0f135a58c8..4b3c547b6a 100644 --- a/core/src/test/java/ch/cyberduck/core/NullSession.java +++ b/core/src/test/java/ch/cyberduck/core/NullSession.java @@ -33,11 +33,6 @@ public class NullSession extends Session implements ListService { throw new LoginCanceledException(); } - @Override - protected void logout() { - // - } - @Override public AttributedList list(final Path folder, final ListProgressListener listener) throws BackgroundException { listener.chunk(folder, AttributedList.emptyList()); diff --git a/core/src/test/java/ch/cyberduck/core/NullUploadFeature.java b/core/src/test/java/ch/cyberduck/core/NullUploadFeature.java index fe77e31187..cf392e963f 100644 --- a/core/src/test/java/ch/cyberduck/core/NullUploadFeature.java +++ b/core/src/test/java/ch/cyberduck/core/NullUploadFeature.java @@ -23,16 +23,11 @@ import ch.cyberduck.core.transfer.TransferStatus; public class NullUploadFeature implements Upload { @Override - public Void upload(final Path file, final Local local, final BandwidthThrottle throttle, final ProgressListener progress, final StreamListener streamListener, + public Void upload(final Write write, final Path file, final Local local, final BandwidthThrottle throttle, final ProgressListener progress, final StreamListener streamListener, final TransferStatus status, final ConnectionCallback callback) throws BackgroundException { return null; } - @Override - public Upload withWriter(final Write writer) { - return this; - } - @Override public Write.Append append(final Path file, final TransferStatus status) throws BackgroundException { return new Write.Append(true).withStatus(status); diff --git a/core/src/test/java/ch/cyberduck/core/NullWriteFeature.java b/core/src/test/java/ch/cyberduck/core/NullWriteFeature.java index da26294946..56f56ca7aa 100644 --- a/core/src/test/java/ch/cyberduck/core/NullWriteFeature.java +++ b/core/src/test/java/ch/cyberduck/core/NullWriteFeature.java @@ -26,6 +26,6 @@ public class NullWriteFeature implements Write { @Override public StatusOutputStream write(final Path file, final TransferStatus status, final ConnectionCallback callback) { - return new VoidStatusOutputStream(NullOutputStream.NULL_OUTPUT_STREAM); + return new VoidStatusOutputStream(NullOutputStream.INSTANCE); } } diff --git a/core/src/test/java/ch/cyberduck/core/PathAttributesTest.java b/core/src/test/java/ch/cyberduck/core/PathAttributesTest.java index d6c8215cad..35da8f4d8f 100644 --- a/core/src/test/java/ch/cyberduck/core/PathAttributesTest.java +++ b/core/src/test/java/ch/cyberduck/core/PathAttributesTest.java @@ -17,7 +17,7 @@ public class PathAttributesTest { @Test public void testCopy() { - final PathAttributes attributes = new PathAttributes(); + final PathAttributes attributes = new DefaultPathAttributes(); attributes.setSize(1L); attributes.setQuota(new Quota.Space(1L, 10L)); attributes.setModificationDate(System.currentTimeMillis()); @@ -31,7 +31,7 @@ public class PathAttributesTest { attributes.setVerdict(PathAttributes.Verdict.pending); attributes.setTrashed(true); attributes.setHidden(true); - final PathAttributes clone = new PathAttributes(attributes); + final PathAttributes clone = new DefaultPathAttributes(attributes); assertEquals(clone.getPermission(), attributes.getPermission()); assertEquals(clone.getModificationDate(), attributes.getModificationDate()); assertEquals(clone, attributes); @@ -49,7 +49,7 @@ public class PathAttributesTest { @Test public void testPermissions() { - final PathAttributes attributes = new PathAttributes(); + final PathAttributes attributes = new DefaultPathAttributes(); assertNull(attributes.getOwner()); assertNull(attributes.getGroup()); assertNotNull(attributes.getPermission()); @@ -59,17 +59,17 @@ public class PathAttributesTest { @Test public void testEquals() { - assertEquals(new PathAttributes(), new PathAttributes()); - final PathAttributes r1 = new PathAttributes(); + assertEquals(new DefaultPathAttributes(), new DefaultPathAttributes()); + final PathAttributes r1 = new DefaultPathAttributes(); r1.setVersionId("r1"); - final PathAttributes r2 = new PathAttributes(); + final PathAttributes r2 = new DefaultPathAttributes(); r2.setVersionId("r2"); assertNotEquals(r1, r2); } @Test public void testSerialize() { - final PathAttributes attributes = new PathAttributes(); + final PathAttributes attributes = new DefaultPathAttributes(); attributes.setSize(100); attributes.setModificationDate(System.currentTimeMillis()); attributes.setPermission(new Permission("644")); diff --git a/core/src/test/java/ch/cyberduck/core/PathCacheTest.java b/core/src/test/java/ch/cyberduck/core/PathCacheTest.java index e1ced7d977..50bfd7a48f 100644 --- a/core/src/test/java/ch/cyberduck/core/PathCacheTest.java +++ b/core/src/test/java/ch/cyberduck/core/PathCacheTest.java @@ -69,7 +69,7 @@ public class PathCacheTest { @Test public void testDirectoryWithFileIdDefaultPredicate() { final PathCache cache = new PathCache(1); - final Path f = new Path("/", EnumSet.of(Path.Type.directory)).withAttributes(new PathAttributes().setFileId("1")); + final Path f = new Path("/", EnumSet.of(Path.Type.directory)).withAttributes(new DefaultPathAttributes().setFileId("1")); assertFalse(cache.containsKey(f)); cache.put(f, new AttributedList<>()); assertTrue(cache.containsKey(f)); @@ -86,7 +86,7 @@ public class PathCacheTest { return new SimplePathPredicate(file); } }; - final Path f = new Path("/", EnumSet.of(Path.Type.directory)).withAttributes(new PathAttributes().setFileId("1")); + final Path f = new Path("/", EnumSet.of(Path.Type.directory)).withAttributes(new DefaultPathAttributes().setFileId("1")); assertFalse(cache.containsKey(f)); cache.put(f, new AttributedList<>()); assertTrue(cache.containsKey(f)); diff --git a/core/src/test/java/ch/cyberduck/core/PermissionTest.java b/core/src/test/java/ch/cyberduck/core/PermissionTest.java index 5850953733..57eefae622 100644 --- a/core/src/test/java/ch/cyberduck/core/PermissionTest.java +++ b/core/src/test/java/ch/cyberduck/core/PermissionTest.java @@ -152,6 +152,11 @@ public class PermissionTest { assertEquals(new Permission(4100), new Permission("--s------")); } + @Test + public void testInitMode() { + assertEquals(new Permission("rwxr-x---"), new Permission("0750")); + } + @Test public void testImplies() { assertTrue(new Permission("r--------").getUser().implies(Permission.Action.read)); diff --git a/core/src/test/java/ch/cyberduck/core/TestPermissionAttributes.java b/core/src/test/java/ch/cyberduck/core/TestPermissionAttributes.java index 9cc8670ead..7b568ab210 100644 --- a/core/src/test/java/ch/cyberduck/core/TestPermissionAttributes.java +++ b/core/src/test/java/ch/cyberduck/core/TestPermissionAttributes.java @@ -1,6 +1,6 @@ package ch.cyberduck.core; -public class TestPermissionAttributes extends PathAttributes { +public class TestPermissionAttributes extends DefaultPathAttributes { public TestPermissionAttributes(Permission.Action action) { this(action, action); } diff --git a/core/src/test/java/ch/cyberduck/core/WindowsIntegratedCredentialsConfiguratorTest.java b/core/src/test/java/ch/cyberduck/core/WindowsIntegratedCredentialsConfiguratorTest.java index 25452bbc4c..c91274f07a 100644 --- a/core/src/test/java/ch/cyberduck/core/WindowsIntegratedCredentialsConfiguratorTest.java +++ b/core/src/test/java/ch/cyberduck/core/WindowsIntegratedCredentialsConfiguratorTest.java @@ -18,15 +18,16 @@ package ch.cyberduck.core; import org.junit.Test; import static org.junit.Assert.*; +import static org.junit.Assume.assumeFalse; import static org.junit.Assume.assumeTrue; public class WindowsIntegratedCredentialsConfiguratorTest { @Test public void testConfigure() { - assumeTrue(Factory.Platform.getDefault().equals(Factory.Platform.Name.linux)); + assumeFalse(Factory.Platform.getDefault().equals(Factory.Platform.Name.windows)); final Host bookmark = new Host(new TestProtocol()); - assertSame(bookmark.getCredentials(), new WindowsIntegratedCredentialsConfigurator().configure(bookmark)); + assertEquals(bookmark.getCredentials(), new WindowsIntegratedCredentialsConfigurator().configure(bookmark)); } @Test diff --git a/core/src/test/java/ch/cyberduck/core/http/HttpResponseOutputStreamTest.java b/core/src/test/java/ch/cyberduck/core/http/HttpResponseOutputStreamTest.java index 7f6457d296..69fb8baa87 100644 --- a/core/src/test/java/ch/cyberduck/core/http/HttpResponseOutputStreamTest.java +++ b/core/src/test/java/ch/cyberduck/core/http/HttpResponseOutputStreamTest.java @@ -32,7 +32,7 @@ public class HttpResponseOutputStreamTest { @Test(expected = IOException.class) public void testClose() throws Exception { try { - new HttpResponseOutputStream(NullOutputStream.NULL_OUTPUT_STREAM, new VoidAttributesAdapter(), new TransferStatus()) { + new HttpResponseOutputStream(NullOutputStream.INSTANCE, new VoidAttributesAdapter(), new TransferStatus()) { @Override public Void getStatus() throws BackgroundException { throw new InteroperabilityException("d"); diff --git a/core/src/test/java/ch/cyberduck/core/io/CRC32ChecksumComputeTest.java b/core/src/test/java/ch/cyberduck/core/io/CRC32ChecksumComputeTest.java index 799f376e40..4c080aaf76 100644 --- a/core/src/test/java/ch/cyberduck/core/io/CRC32ChecksumComputeTest.java +++ b/core/src/test/java/ch/cyberduck/core/io/CRC32ChecksumComputeTest.java @@ -26,8 +26,8 @@ public class CRC32ChecksumComputeTest { @Test public void testCompute() throws Exception { - assertEquals("0", - new CRC32ChecksumCompute().compute(new NullInputStream(0), new TransferStatus()).hash); + assertEquals("00000000", + new CRC32ChecksumCompute().compute(new NullInputStream(0L), new TransferStatus()).hash); assertEquals("d202ef8d", new CRC32ChecksumCompute().compute(new NullInputStream(1L), new TransferStatus()).hash); } diff --git a/core/src/test/java/ch/cyberduck/core/io/DefaultStreamCloserTest.java b/core/src/test/java/ch/cyberduck/core/io/DefaultStreamCloserTest.java index 9068ee6494..096410eea5 100644 --- a/core/src/test/java/ch/cyberduck/core/io/DefaultStreamCloserTest.java +++ b/core/src/test/java/ch/cyberduck/core/io/DefaultStreamCloserTest.java @@ -28,7 +28,7 @@ public class DefaultStreamCloserTest { @Test(expected = InteroperabilityException.class) public void testClose() throws Exception { - new DefaultStreamCloser().close(new HttpResponseOutputStream(NullOutputStream.NULL_OUTPUT_STREAM, + new DefaultStreamCloser().close(new HttpResponseOutputStream(NullOutputStream.INSTANCE, new VoidAttributesAdapter(), new TransferStatus()) { @Override diff --git a/core/src/test/java/ch/cyberduck/core/io/StreamCopierTest.java b/core/src/test/java/ch/cyberduck/core/io/StreamCopierTest.java index a96f21718c..c2c4bb7e32 100644 --- a/core/src/test/java/ch/cyberduck/core/io/StreamCopierTest.java +++ b/core/src/test/java/ch/cyberduck/core/io/StreamCopierTest.java @@ -54,7 +54,7 @@ public class StreamCopierTest { assertTrue(this.getRecv() > this.getSent()); } }; - new StreamCopier(status, status).withListener(count).transfer(new NullInputStream(432768L), NullOutputStream.NULL_OUTPUT_STREAM); + new StreamCopier(status, status).withListener(count).transfer(new NullInputStream(432768L), NullOutputStream.INSTANCE); assertTrue(status.isComplete()); assertEquals(0L, status.getOffset()); assertEquals(432768L, count.getSent()); @@ -98,7 +98,7 @@ public class StreamCopierTest { public void testTransferFixedLength() throws Exception { final TransferStatus status = new TransferStatus().setLength(432768L); final BytecountStreamListener count = new BytecountStreamListener(); - new StreamCopier(status, status).withLimit(432767L).withListener(count).transfer(new NullInputStream(432768L), NullOutputStream.NULL_OUTPUT_STREAM); + new StreamCopier(status, status).withLimit(432767L).withListener(count).transfer(new NullInputStream(432768L), NullOutputStream.INSTANCE); assertEquals(0L, status.getOffset()); assertEquals(432767L, count.getSent()); assertEquals(432767L, count.getRecv()); @@ -109,7 +109,7 @@ public class StreamCopierTest { public void testReadNoEndofStream() throws Exception { final TransferStatus status = new TransferStatus().setLength(432768L); final BytecountStreamListener count = new BytecountStreamListener(); - new StreamCopier(status, status).withLimit(432768L).withListener(count).transfer(new NullInputStream(432770L), NullOutputStream.NULL_OUTPUT_STREAM); + new StreamCopier(status, status).withLimit(432768L).withListener(count).transfer(new NullInputStream(432770L), NullOutputStream.INSTANCE); assertEquals(432768L, count.getRecv()); assertEquals(432768L, count.getSent()); assertEquals(0L, status.getOffset()); @@ -121,7 +121,7 @@ public class StreamCopierTest { { final TransferStatus status = new TransferStatus(); final BytecountStreamListener count = new BytecountStreamListener(); - new StreamCopier(status, status).withOffset(1L).withListener(count).transfer(new NullInputStream(432768L), NullOutputStream.NULL_OUTPUT_STREAM); + new StreamCopier(status, status).withOffset(1L).withListener(count).transfer(new NullInputStream(432768L), NullOutputStream.INSTANCE); assertEquals(432767L, count.getSent()); assertEquals(432767L, count.getRecv()); assertEquals(0L, status.getOffset()); @@ -130,7 +130,7 @@ public class StreamCopierTest { { final TransferStatus status = new TransferStatus(); final BytecountStreamListener count = new BytecountStreamListener(); - new StreamCopier(status, status).withOffset(1L).withListener(count).transfer(new NullInputStream(432768L), NullOutputStream.NULL_OUTPUT_STREAM); + new StreamCopier(status, status).withOffset(1L).withListener(count).transfer(new NullInputStream(432768L), NullOutputStream.INSTANCE); assertEquals(432767L, count.getSent()); assertEquals(432767L, count.getRecv()); assertEquals(0L, status.getOffset()); @@ -161,7 +161,7 @@ public class StreamCopierTest { @Override public void run() { try { - new StreamCopier(status, status).withListener(count).transfer(new NullInputStream(status.getLength()), NullOutputStream.NULL_OUTPUT_STREAM); + new StreamCopier(status, status).withListener(count).transfer(new NullInputStream(status.getLength()), NullOutputStream.INSTANCE); } catch(BackgroundException e) { assertTrue(e instanceof ConnectionCanceledException); diff --git a/core/src/test/java/ch/cyberduck/core/local/DefaultTemporaryFileServiceTest.java b/core/src/test/java/ch/cyberduck/core/local/DefaultTemporaryFileServiceTest.java index 46d4a38b90..955d4cebad 100755 --- a/core/src/test/java/ch/cyberduck/core/local/DefaultTemporaryFileServiceTest.java +++ b/core/src/test/java/ch/cyberduck/core/local/DefaultTemporaryFileServiceTest.java @@ -9,6 +9,7 @@ import org.apache.commons.lang3.StringUtils; import org.junit.Test; import java.io.File; +import java.nio.file.FileSystems; import java.util.EnumSet; import static org.junit.Assert.*; @@ -36,31 +37,31 @@ public class DefaultTemporaryFileServiceTest { @Test public void testCreateFile() { final String temp = StringUtils.removeEnd(System.getProperty("java.io.tmpdir"), File.separator); - final String s = System.getProperty("file.separator"); - assertEquals(String.format("%s%su%sp%s1742810335%sf", temp, s, s, s, s), + final String s = FileSystems.getDefault().getSeparator(); + assertEquals(String.format("%s%su%sp%s-196513505%sf", temp, s, s, s, s), new DefaultTemporaryFileService().create("u", new Path("/p/f", EnumSet.of(Path.Type.file))).getAbsolute()); final Path file = new Path("/p/f", EnumSet.of(Path.Type.file)); file.attributes().setRegion("region"); - assertEquals(String.format("%s%su%sp%s1742810335%sf", temp, s, s, s, s), + assertEquals(String.format("%s%su%sp%s-196513505%sf", temp, s, s, s, s), new DefaultTemporaryFileService().create("u", file).getAbsolute()); } @Test public void testVersion() { final String temp = StringUtils.removeEnd(System.getProperty("java.io.tmpdir"), File.separator); - final String s = System.getProperty("file.separator"); + final String s = FileSystems.getDefault().getSeparator(); { final Path file = new Path("/p/f", EnumSet.of(Path.Type.file)); file.attributes().setRegion("region"); file.attributes().setVersionId("2"); - assertEquals(String.format("%s%su%sp%s1744299885%sf", temp, s, s, s, s), + assertEquals(String.format("%s%su%sp%s1234944045%sf", temp, s, s, s, s), new DefaultTemporaryFileService().create("u", file).getAbsolute()); } { final Path file = new Path("/p", EnumSet.of(Path.Type.directory)); file.attributes().setRegion("region"); file.attributes().setVersionId("2"); - assertEquals(String.format("%s%su%s1744299885%sp", temp, s, s, s), + assertEquals(String.format("%s%su%s1234944045%sp", temp, s, s, s), new DefaultTemporaryFileService().create("u", file).getAbsolute()); } } @@ -68,10 +69,10 @@ public class DefaultTemporaryFileServiceTest { @Test public void testCreateContainer() { final String temp = StringUtils.removeEnd(System.getProperty("java.io.tmpdir"), File.separator); - final String s = System.getProperty("file.separator"); + final String s = FileSystems.getDefault().getSeparator(); final Path file = new Path("/container", EnumSet.of(Path.Type.directory)); file.attributes().setRegion("region"); - assertEquals(String.format("%s%su%s1742810335%scontainer", temp, s, s, s), + assertEquals(String.format("%s%su%s-196513505%scontainer", temp, s, s, s), new DefaultTemporaryFileService().create("u", file).getAbsolute()); } @@ -87,8 +88,8 @@ public class DefaultTemporaryFileServiceTest { file.attributes().setVersionId("2"); final Local local = new DefaultTemporaryFileService().create("UID", file); final String localFile = local.getAbsolute(); - assertNotEquals(String.format("%s/%s%s/1744299885/%s", temp, "UID", testPathDirectory, testPathFile).replace('/', File.separatorChar), localFile); - assertEquals(String.format("%s/%s/%s/1744299885/%s", temp, "UID", testPathMD5, testPathFile).replace('/', File.separatorChar), localFile); + assertNotEquals(String.format("%s/%s%s/1234944045/%s", temp, "UID", testPathDirectory, testPathFile).replace('/', File.separatorChar), localFile); + assertEquals(String.format("%s/%s/%s/1234944045/%s", temp, "UID", testPathMD5, testPathFile).replace('/', File.separatorChar), localFile); } @Test @@ -103,8 +104,8 @@ public class DefaultTemporaryFileServiceTest { file.attributes().setVersionId("2"); final Local local = new DefaultTemporaryFileService().create("UID", file); final String localFile = local.getAbsolute(); - assertEquals(String.format("%s/%s%s/1744299885/%s", temp, "UID", testPathDirectory, testPathFile).replace('/', File.separatorChar), localFile); - assertNotEquals(String.format("%s/%s%s/1744299885/%s", temp, "UID", testPathMD5, testPathFile).replace('/', File.separatorChar), localFile); + assertEquals(String.format("%s/%s%s/1234944045/%s", temp, "UID", testPathDirectory, testPathFile).replace('/', File.separatorChar), localFile); + assertNotEquals(String.format("%s/%s%s/1234944045/%s", temp, "UID", testPathMD5, testPathFile).replace('/', File.separatorChar), localFile); } @Test @@ -115,7 +116,7 @@ public class DefaultTemporaryFileServiceTest { final Local local = new DefaultTemporaryFileService().create(file); assertEquals("t.txt", file.getName()); assertEquals("t.txt", local.getName()); - assertEquals("1744270094", local.getParent().getName()); + assertEquals("1206314894", local.getParent().getName()); assertEquals("f2", local.getParent().getParent().getName()); assertEquals("f1", local.getParent().getParent().getParent().getName()); } diff --git a/core/src/test/java/ch/cyberduck/core/local/FlatTemporaryFileServiceTest.java b/core/src/test/java/ch/cyberduck/core/local/FlatTemporaryFileServiceTest.java index c8634e15e4..369839ed94 100644 --- a/core/src/test/java/ch/cyberduck/core/local/FlatTemporaryFileServiceTest.java +++ b/core/src/test/java/ch/cyberduck/core/local/FlatTemporaryFileServiceTest.java @@ -26,6 +26,7 @@ import org.apache.commons.lang3.StringUtils; import org.junit.Test; import java.io.File; +import java.nio.file.FileSystems; import java.util.EnumSet; import static org.junit.Assert.*; @@ -52,22 +53,22 @@ public class FlatTemporaryFileServiceTest { @Test public void testCreateFile() { final String temp = StringUtils.removeEnd(System.getProperty("java.io.tmpdir"), File.separator); - final String s = System.getProperty("file.separator"); - assertEquals(String.format("%s%su%s1742810335-f", temp, s, s), + final String s = FileSystems.getDefault().getSeparator(); + assertEquals(String.format("%s%su%s-196513505-f", temp, s, s), new FlatTemporaryFileService().create("u", new Path("/p/f", EnumSet.of(Path.Type.file))).getAbsolute()); final Path file = new Path("/p/f", EnumSet.of(Path.Type.file)); file.attributes().setRegion("region"); - assertEquals(String.format("%s%su%s1742810335-f", temp, s, s), + assertEquals(String.format("%s%su%s-196513505-f", temp, s, s), new FlatTemporaryFileService().create("u", file).getAbsolute()); } @Test public void testCreateContainer() { final String temp = StringUtils.removeEnd(System.getProperty("java.io.tmpdir"), File.separator); - final String s = System.getProperty("file.separator"); + final String s = FileSystems.getDefault().getSeparator(); final Path file = new Path("/container", EnumSet.of(Path.Type.directory)); file.attributes().setRegion("region"); - assertEquals(String.format("%s%su%s1742810335-container", temp, s, s), + assertEquals(String.format("%s%su%s-196513505-container", temp, s, s), new FlatTemporaryFileService().create("u", file).getAbsolute()); } @@ -99,8 +100,8 @@ public class FlatTemporaryFileServiceTest { final Local local = new FlatTemporaryFileService().create("UID", file); assertTrue(local.getParent().exists()); final String localFile = local.getAbsolute(); - assertEquals(String.format("%s/%s/1744299885-%s", temp, "UID", testPathFile).replace('/', File.separatorChar), localFile); - assertNotEquals(String.format("%s/%s%s/2/1744299885-%s", temp, "UID", testPathMD5, testPathFile).replace('/', File.separatorChar), localFile); + assertEquals(String.format("%s/%s/1234944045-%s", temp, "UID", testPathFile).replace('/', File.separatorChar), localFile); + assertNotEquals(String.format("%s/%s%s/2/1234944045-%s", temp, "UID", testPathMD5, testPathFile).replace('/', File.separatorChar), localFile); } @Test diff --git a/core/src/test/java/ch/cyberduck/core/local/LocalAttributesFinderTest.java b/core/src/test/java/ch/cyberduck/core/local/LocalAttributesFinderTest.java deleted file mode 100644 index 73dd6d8ed0..0000000000 --- a/core/src/test/java/ch/cyberduck/core/local/LocalAttributesFinderTest.java +++ /dev/null @@ -1,80 +0,0 @@ -package ch.cyberduck.core.local; - -import ch.cyberduck.core.LocalAttributes; -import ch.cyberduck.core.Permission; - -import org.junit.Test; - -import java.io.File; -import java.util.UUID; - -import static org.junit.Assert.*; - -public class LocalAttributesFinderTest { - - @Test - public void testGetSize() throws Exception { - assertEquals(-1, new LocalAttributes(UUID.randomUUID().toString()).getSize()); - final File f = new File(UUID.randomUUID().toString()); - f.createNewFile(); - LocalAttributes a = new LocalAttributes(f.getAbsolutePath()); - assertEquals(0, a.getSize()); - f.delete(); - } - - @Test - public void testGetPermission() { - final LocalAttributes a = new LocalAttributes(UUID.randomUUID().toString()); - assertNotNull(a.getPermission()); - assertEquals(Permission.EMPTY, a.getPermission()); - assertSame(a.getPermission(), a.getPermission()); - } - - @Test - public void testGetCreationDate() throws Exception { - assertEquals(-1, new LocalAttributes(UUID.randomUUID().toString()).getCreationDate()); - final File f = new File(UUID.randomUUID().toString()); - f.createNewFile(); - LocalAttributes a = new LocalAttributes(f.getAbsolutePath()); - assertTrue(a.getCreationDate() > 0); - f.delete(); - } - - @Test - public void testGetAccessedDate() throws Exception { - assertEquals(-1, new LocalAttributes(UUID.randomUUID().toString()).getAccessedDate()); - final File f = new File(UUID.randomUUID().toString()); - f.createNewFile(); - LocalAttributes a = new LocalAttributes(f.getAbsolutePath()); - assertTrue(a.getAccessedDate() > 0); - f.delete(); - } - - @Test - public void getGetModificationDate() throws Exception { - assertEquals(-1, new LocalAttributes(UUID.randomUUID().toString()).getModificationDate()); - final File f = new File(UUID.randomUUID().toString()); - f.createNewFile(); - LocalAttributes a = new LocalAttributes(f.getAbsolutePath()); - assertTrue(a.getModificationDate() > 0); - f.delete(); - } - - @Test - public void testGetOwner() { - LocalAttributes a = new LocalAttributes(UUID.randomUUID().toString()); - assertNull(a.getOwner()); - } - - @Test - public void testGetGroup() { - LocalAttributes a = new LocalAttributes(UUID.randomUUID().toString()); - assertNull(a.getGroup()); - } - - @Test - public void testIsBundle() { - LocalAttributes a = new LocalAttributes(UUID.randomUUID().toString()); - assertFalse(a.isBundle()); - } -} diff --git a/core/src/test/java/ch/cyberduck/core/serializer/PathAttributesDictionaryTest.java b/core/src/test/java/ch/cyberduck/core/serializer/PathAttributesDictionaryTest.java index acd669b5ff..1e4d1a91d4 100644 --- a/core/src/test/java/ch/cyberduck/core/serializer/PathAttributesDictionaryTest.java +++ b/core/src/test/java/ch/cyberduck/core/serializer/PathAttributesDictionaryTest.java @@ -15,6 +15,7 @@ package ch.cyberduck.core.serializer; * GNU General Public License for more details. */ +import ch.cyberduck.core.DefaultPathAttributes; import ch.cyberduck.core.DescriptiveUrl; import ch.cyberduck.core.PathAttributes; import ch.cyberduck.core.Permission; @@ -29,7 +30,7 @@ public class PathAttributesDictionaryTest { @Test public void testSerialize() { - PathAttributes attributes = new PathAttributes(); + PathAttributes attributes = new DefaultPathAttributes(); attributes.setOwner("u"); attributes.setGroup("g"); attributes.setTrashed(true); @@ -48,7 +49,7 @@ public class PathAttributesDictionaryTest { @Test public void testGetAsDictionary() { - PathAttributes attributes = new PathAttributes(); + PathAttributes attributes = new DefaultPathAttributes(); attributes.setSize(3L); attributes.setChecksum(Checksum.parse("da39a3ee5e6b4b0d3255bfef95601890afd80709")); attributes.setModificationDate(5343L); @@ -62,7 +63,7 @@ public class PathAttributesDictionaryTest { @Test public void testSerializeHashCode() { - PathAttributes attributes = new PathAttributes(); + PathAttributes attributes = new DefaultPathAttributes(); attributes.setPermission(new Permission(644)); attributes.setDuplicate(true); attributes.setVersionId("v-1"); diff --git a/core/src/test/java/ch/cyberduck/core/shared/ListFilteringFeatureTest.java b/core/src/test/java/ch/cyberduck/core/shared/ListFilteringFeatureTest.java index 8a7dc3da17..a23aaef5e7 100644 --- a/core/src/test/java/ch/cyberduck/core/shared/ListFilteringFeatureTest.java +++ b/core/src/test/java/ch/cyberduck/core/shared/ListFilteringFeatureTest.java @@ -15,6 +15,7 @@ package ch.cyberduck.core.shared; * GNU General Public License for more details. */ +import ch.cyberduck.core.DefaultPathAttributes; import ch.cyberduck.core.Path; import ch.cyberduck.core.PathAttributes; import ch.cyberduck.core.Protocol; @@ -37,32 +38,32 @@ public class ListFilteringFeatureTest { assertTrue(new ListFilteringFeature.ListFilteringPredicate(Protocol.Case.insensitive, new Path(Home.root(), "f", EnumSet.of(Path.Type.file))).test( new Path(Home.root(), "F", EnumSet.of(Path.Type.file)) )); - assertTrue(new ListFilteringFeature.ListFilteringPredicate(Protocol.Case.insensitive, new Path(Home.root(), "f", EnumSet.of(Path.Type.file)).withAttributes(new PathAttributes().setVersionId("v1"))).test( - new Path(Home.root(), "f", EnumSet.of(Path.Type.file)).withAttributes(new PathAttributes().setVersionId("v1")) + assertTrue(new ListFilteringFeature.ListFilteringPredicate(Protocol.Case.insensitive, new Path(Home.root(), "f", EnumSet.of(Path.Type.file)).withAttributes(new DefaultPathAttributes().setVersionId("v1"))).test( + new Path(Home.root(), "f", EnumSet.of(Path.Type.file)).withAttributes(new DefaultPathAttributes().setVersionId("v1")) )); - assertFalse(new ListFilteringFeature.ListFilteringPredicate(Protocol.Case.insensitive, new Path(Home.root(), "f", EnumSet.of(Path.Type.file)).withAttributes(new PathAttributes().setVersionId("v1"))).test( - new Path(Home.root(), "f", EnumSet.of(Path.Type.file)).withAttributes(new PathAttributes().setVersionId("v2")) + assertFalse(new ListFilteringFeature.ListFilteringPredicate(Protocol.Case.insensitive, new Path(Home.root(), "f", EnumSet.of(Path.Type.file)).withAttributes(new DefaultPathAttributes().setVersionId("v1"))).test( + new Path(Home.root(), "f", EnumSet.of(Path.Type.file)).withAttributes(new DefaultPathAttributes().setVersionId("v2")) )); - assertTrue(new ListFilteringFeature.ListFilteringPredicate(Protocol.Case.insensitive, new Path(Home.root(), "f", EnumSet.of(Path.Type.directory)).withAttributes(new PathAttributes().setVersionId("v1"))).test( - new Path(Home.root(), "f", EnumSet.of(Path.Type.directory)).withAttributes(new PathAttributes().setVersionId("v2")) + assertTrue(new ListFilteringFeature.ListFilteringPredicate(Protocol.Case.insensitive, new Path(Home.root(), "f", EnumSet.of(Path.Type.directory)).withAttributes(new DefaultPathAttributes().setVersionId("v1"))).test( + new Path(Home.root(), "f", EnumSet.of(Path.Type.directory)).withAttributes(new DefaultPathAttributes().setVersionId("v2")) )); - assertTrue(new ListFilteringFeature.ListFilteringPredicate(Protocol.Case.insensitive, new Path(Home.root(), "f", EnumSet.of(Path.Type.file)).withAttributes(new PathAttributes().setFileId("v1"))).test( - new Path(Home.root(), "f", EnumSet.of(Path.Type.file)).withAttributes(new PathAttributes().setFileId("v1")) + assertTrue(new ListFilteringFeature.ListFilteringPredicate(Protocol.Case.insensitive, new Path(Home.root(), "f", EnumSet.of(Path.Type.file)).withAttributes(new DefaultPathAttributes().setFileId("v1"))).test( + new Path(Home.root(), "f", EnumSet.of(Path.Type.file)).withAttributes(new DefaultPathAttributes().setFileId("v1")) )); - assertFalse(new ListFilteringFeature.ListFilteringPredicate(Protocol.Case.insensitive, new Path(Home.root(), "f", EnumSet.of(Path.Type.file)).withAttributes(new PathAttributes().setFileId("v1"))).test( - new Path(Home.root(), "f", EnumSet.of(Path.Type.file)).withAttributes(new PathAttributes().setFileId("v2")) + assertFalse(new ListFilteringFeature.ListFilteringPredicate(Protocol.Case.insensitive, new Path(Home.root(), "f", EnumSet.of(Path.Type.file)).withAttributes(new DefaultPathAttributes().setFileId("v1"))).test( + new Path(Home.root(), "f", EnumSet.of(Path.Type.file)).withAttributes(new DefaultPathAttributes().setFileId("v2")) )); - assertFalse(new ListFilteringFeature.ListFilteringPredicate(Protocol.Case.insensitive, new Path(Home.root(), "f", EnumSet.of(Path.Type.directory)).withAttributes(new PathAttributes().setFileId("v1"))).test( - new Path(Home.root(), "f", EnumSet.of(Path.Type.directory)).withAttributes(new PathAttributes().setFileId("v2")) + assertFalse(new ListFilteringFeature.ListFilteringPredicate(Protocol.Case.insensitive, new Path(Home.root(), "f", EnumSet.of(Path.Type.directory)).withAttributes(new DefaultPathAttributes().setFileId("v1"))).test( + new Path(Home.root(), "f", EnumSet.of(Path.Type.directory)).withAttributes(new DefaultPathAttributes().setFileId("v2")) )); assertTrue(new ListFilteringFeature.ListFilteringPredicate(Protocol.Case.insensitive, new Path(Home.root(), "f", EnumSet.of(Path.Type.file))).test( - new Path(Home.root(), "f", EnumSet.of(Path.Type.file)).withAttributes(new PathAttributes().setVersionId("v1")) + new Path(Home.root(), "f", EnumSet.of(Path.Type.file)).withAttributes(new DefaultPathAttributes().setVersionId("v1")) )); - assertFalse(new ListFilteringFeature.ListFilteringPredicate(Protocol.Case.insensitive, new Path(Home.root(), "f", EnumSet.of(Path.Type.file)).withAttributes(new PathAttributes().setVersionId("v1"))).test( - new Path(Home.root(), "f", EnumSet.of(Path.Type.file)).withAttributes(new PathAttributes()) + assertFalse(new ListFilteringFeature.ListFilteringPredicate(Protocol.Case.insensitive, new Path(Home.root(), "f", EnumSet.of(Path.Type.file)).withAttributes(new DefaultPathAttributes().setVersionId("v1"))).test( + new Path(Home.root(), "f", EnumSet.of(Path.Type.file)).withAttributes(new DefaultPathAttributes()) )); assertFalse(new ListFilteringFeature.ListFilteringPredicate(Protocol.Case.insensitive, new Path(Home.root(), "f", EnumSet.of(Path.Type.file))).test( - new Path(Home.root(), "f", EnumSet.of(Path.Type.file)).withAttributes(new PathAttributes() { + new Path(Home.root(), "f", EnumSet.of(Path.Type.file)).withAttributes(new DefaultPathAttributes() { @Override public boolean isDuplicate() { return true; diff --git a/core/src/test/java/ch/cyberduck/core/synchronization/ChainedComparisonServiceTest.java b/core/src/test/java/ch/cyberduck/core/synchronization/ChainedComparisonServiceTest.java index dd937b1e43..2c8989ed1d 100644 --- a/core/src/test/java/ch/cyberduck/core/synchronization/ChainedComparisonServiceTest.java +++ b/core/src/test/java/ch/cyberduck/core/synchronization/ChainedComparisonServiceTest.java @@ -15,6 +15,7 @@ package ch.cyberduck.core.synchronization; * GNU General Public License for more details. */ +import ch.cyberduck.core.DefaultPathAttributes; import ch.cyberduck.core.Path; import ch.cyberduck.core.PathAttributes; @@ -30,31 +31,31 @@ public class ChainedComparisonServiceTest { @Test public void testCompare() { assertEquals(Comparison.equal, new ChainedComparisonService(new TimestampComparisonService(), new SizeComparisonService()).compare( - Path.Type.file, new PathAttributes().setModificationDate(1000L), new PathAttributes().setModificationDate(1000L) + Path.Type.file, new DefaultPathAttributes().setModificationDate(1000L), new DefaultPathAttributes().setModificationDate(1000L) )); assertEquals(Comparison.equal, new ChainedComparisonService(new TimestampComparisonService(), new SizeComparisonService()).compare( - Path.Type.file, new PathAttributes().setModificationDate(1000L).setSize(1000L), new PathAttributes().setModificationDate(1000L).setSize(1000L) + Path.Type.file, new DefaultPathAttributes().setModificationDate(1000L).setSize(1000L), new DefaultPathAttributes().setModificationDate(1000L).setSize(1000L) )); assertEquals(Comparison.remote, new ChainedComparisonService(new TimestampComparisonService(), new SizeComparisonService()).compare( - Path.Type.file, new PathAttributes().setModificationDate(1000L), new PathAttributes().setModificationDate(2000L) + Path.Type.file, new DefaultPathAttributes().setModificationDate(1000L), new DefaultPathAttributes().setModificationDate(2000L) )); assertEquals(Comparison.remote, new ChainedComparisonService(new TimestampComparisonService(), new SizeComparisonService()).compare( - Path.Type.file, new PathAttributes().setModificationDate(1000L).setSize(1000L), new PathAttributes().setModificationDate(2000L).setSize(1000L) + Path.Type.file, new DefaultPathAttributes().setModificationDate(1000L).setSize(1000L), new DefaultPathAttributes().setModificationDate(2000L).setSize(1000L) )); assertEquals(Comparison.unknown, new ChainedComparisonService(new TimestampComparisonService(), new SizeComparisonService()).compare( - Path.Type.file, new PathAttributes(), new PathAttributes() + Path.Type.file, new DefaultPathAttributes(), new DefaultPathAttributes() )); assertEquals(Comparison.equal, new ChainedComparisonService(new TimestampComparisonService(), new SizeComparisonService()).compare( - Path.Type.file, new PathAttributes().setModificationDate(1000L).setSize(1000L), new PathAttributes().setModificationDate(1000L).setSize(2000L) + Path.Type.file, new DefaultPathAttributes().setModificationDate(1000L).setSize(1000L), new DefaultPathAttributes().setModificationDate(1000L).setSize(2000L) )); assertEquals(Comparison.notequal, new ChainedComparisonService(EnumSet.of(Comparison.unknown, Comparison.equal), new TimestampComparisonService(), new SizeComparisonService()).compare( - Path.Type.file, new PathAttributes().setModificationDate(1000L).setSize(1000L), new PathAttributes().setModificationDate(1000L).setSize(2000L) + Path.Type.file, new DefaultPathAttributes().setModificationDate(1000L).setSize(1000L), new DefaultPathAttributes().setModificationDate(1000L).setSize(2000L) )); } @Test public void testHashCode() { - final PathAttributes attr = new PathAttributes().setModificationDate(1000L).setSize(2L); + final PathAttributes attr = new DefaultPathAttributes().setModificationDate(1000L).setSize(2L); assertEquals(0, new ChainedComparisonService(new TimestampComparisonService()).hashCode(Path.Type.file, PathAttributes.EMPTY)); assertNotEquals(0, new ChainedComparisonService(new TimestampComparisonService()).hashCode(Path.Type.file, attr)); assertEquals(new ChainedComparisonService(new TimestampComparisonService()).hashCode(Path.Type.file, attr), diff --git a/core/src/test/java/ch/cyberduck/core/synchronization/ChecksumComparisonServiceTest.java b/core/src/test/java/ch/cyberduck/core/synchronization/ChecksumComparisonServiceTest.java index b5b2749c6c..6a9153a03b 100644 --- a/core/src/test/java/ch/cyberduck/core/synchronization/ChecksumComparisonServiceTest.java +++ b/core/src/test/java/ch/cyberduck/core/synchronization/ChecksumComparisonServiceTest.java @@ -1,5 +1,6 @@ package ch.cyberduck.core.synchronization; +import ch.cyberduck.core.DefaultPathAttributes; import ch.cyberduck.core.Path; import ch.cyberduck.core.PathAttributes; import ch.cyberduck.core.io.Checksum; @@ -14,26 +15,26 @@ public class ChecksumComparisonServiceTest { @Test public void testCompare() { ComparisonService s = new ChecksumComparisonService(); - assertEquals(Comparison.equal, s.compare(Path.Type.file, new PathAttributes() { + assertEquals(Comparison.equal, s.compare(Path.Type.file, new DefaultPathAttributes() { @Override public Checksum getChecksum() { return new Checksum(HashAlgorithm.md5, "a"); } - }, new PathAttributes().setChecksum(new Checksum(HashAlgorithm.md5, "a")) + }, new DefaultPathAttributes().setChecksum(new Checksum(HashAlgorithm.md5, "a")) )); - assertEquals(Comparison.notequal, s.compare(Path.Type.file, new PathAttributes() { + assertEquals(Comparison.notequal, s.compare(Path.Type.file, new DefaultPathAttributes() { @Override public Checksum getChecksum() { return new Checksum(HashAlgorithm.md5, "b"); } - }, new PathAttributes().setChecksum(new Checksum(HashAlgorithm.md5, "a")))); + }, new DefaultPathAttributes().setChecksum(new Checksum(HashAlgorithm.md5, "a")))); } @Test public void testDirectory() { ComparisonService s = new ChecksumComparisonService(); - assertEquals(Comparison.unknown, s.compare(Path.Type.directory, new PathAttributes(), - new PathAttributes())); + assertEquals(Comparison.unknown, s.compare(Path.Type.directory, new DefaultPathAttributes(), + new DefaultPathAttributes())); } } diff --git a/core/src/test/java/ch/cyberduck/core/synchronization/DefaultComparePathFilterTest.java b/core/src/test/java/ch/cyberduck/core/synchronization/DefaultComparePathFilterTest.java index 5edaaaedd5..d2cab680de 100644 --- a/core/src/test/java/ch/cyberduck/core/synchronization/DefaultComparePathFilterTest.java +++ b/core/src/test/java/ch/cyberduck/core/synchronization/DefaultComparePathFilterTest.java @@ -1,6 +1,7 @@ package ch.cyberduck.core.synchronization; import ch.cyberduck.core.AlphanumericRandomStringService; +import ch.cyberduck.core.DefaultPathAttributes; import ch.cyberduck.core.DisabledProgressListener; import ch.cyberduck.core.Host; import ch.cyberduck.core.ListProgressListener; @@ -36,7 +37,7 @@ public class DefaultComparePathFilterTest { @Override public PathAttributes find(final Path file, final ListProgressListener listener) { attr.set(true); - return new PathAttributes() { + return new DefaultPathAttributes() { @Override public Checksum getChecksum() { return new Checksum(HashAlgorithm.md5, "a"); @@ -163,7 +164,7 @@ public class DefaultComparePathFilterTest { @Override public PathAttributes find(final Path file, final ListProgressListener listener) { attr.set(true); - return new PathAttributes() { + return new DefaultPathAttributes() { @Override public Checksum getChecksum() { return new Checksum(HashAlgorithm.md5, "b"); diff --git a/core/src/test/java/ch/cyberduck/core/synchronization/DefaultComparisonServiceTest.java b/core/src/test/java/ch/cyberduck/core/synchronization/DefaultComparisonServiceTest.java index 04e380ad0b..b99a44043e 100644 --- a/core/src/test/java/ch/cyberduck/core/synchronization/DefaultComparisonServiceTest.java +++ b/core/src/test/java/ch/cyberduck/core/synchronization/DefaultComparisonServiceTest.java @@ -15,6 +15,7 @@ package ch.cyberduck.core.synchronization; * GNU General Public License for more details. */ +import ch.cyberduck.core.DefaultPathAttributes; import ch.cyberduck.core.Path; import ch.cyberduck.core.PathAttributes; import ch.cyberduck.core.TestProtocol; @@ -28,14 +29,14 @@ public class DefaultComparisonServiceTest { @Test public void testCompareFile() { final DefaultComparisonService c = new DefaultComparisonService(new TestProtocol()); - assertEquals(Comparison.equal, c.compare(Path.Type.file, new PathAttributes().setETag("1"), new PathAttributes().setETag("1"))); - assertEquals(1519, c.hashCode(Path.Type.file, new PathAttributes().setETag("1"))); - assertEquals(Comparison.unknown, c.compare(Path.Type.file, new PathAttributes().setETag("1"), new PathAttributes().setETag("2"))); - assertEquals(Comparison.equal, c.compare(Path.Type.file, new PathAttributes().setETag("1").setSize(1000L), new PathAttributes().setETag("2").setSize(1000L))); - assertEquals(Comparison.equal, c.compare(Path.Type.file, new PathAttributes().setETag("1").setSize(1000L).setModificationDate(1680879106939L), new PathAttributes().setETag("2").setSize(1000L).setModificationDate(1680879106939L))); - assertEquals(709040760, c.hashCode(Path.Type.file, new PathAttributes().setETag("1").setModificationDate(1680879106939L))); - assertEquals(709040760, c.hashCode(Path.Type.file, new PathAttributes().setETag("1").setModificationDate(1680879106000L))); - assertEquals(Comparison.local, c.compare(Path.Type.file, new PathAttributes().setETag("1").setSize(1000L).setModificationDate(1680879107939L), new PathAttributes().setETag("2").setSize(1000L).setModificationDate(1680879106939L))); + assertEquals(Comparison.equal, c.compare(Path.Type.file, new DefaultPathAttributes().setETag("1"), new DefaultPathAttributes().setETag("1"))); + assertEquals(1519, c.hashCode(Path.Type.file, new DefaultPathAttributes().setETag("1"))); + assertEquals(Comparison.unknown, c.compare(Path.Type.file, new DefaultPathAttributes().setETag("1"), new DefaultPathAttributes().setETag("2"))); + assertEquals(Comparison.equal, c.compare(Path.Type.file, new DefaultPathAttributes().setETag("1").setSize(1000L), new DefaultPathAttributes().setETag("2").setSize(1000L))); + assertEquals(Comparison.equal, c.compare(Path.Type.file, new DefaultPathAttributes().setETag("1").setSize(1000L).setModificationDate(1680879106939L), new DefaultPathAttributes().setETag("2").setSize(1000L).setModificationDate(1680879106939L))); + assertEquals(709040760, c.hashCode(Path.Type.file, new DefaultPathAttributes().setETag("1").setModificationDate(1680879106939L))); + assertEquals(709040760, c.hashCode(Path.Type.file, new DefaultPathAttributes().setETag("1").setModificationDate(1680879106000L))); + assertEquals(Comparison.local, c.compare(Path.Type.file, new DefaultPathAttributes().setETag("1").setSize(1000L).setModificationDate(1680879107939L), new DefaultPathAttributes().setETag("2").setSize(1000L).setModificationDate(1680879106939L))); } @Test @@ -47,13 +48,13 @@ public class DefaultComparisonServiceTest { return DirectoryTimestamp.implicit; } }); - assertEquals(Comparison.equal, c.compare(Path.Type.directory, new PathAttributes().setETag("1"), new PathAttributes().setETag("1"))); - assertEquals(1519, c.hashCode(Path.Type.directory, new PathAttributes().setETag("1"))); - assertEquals(Comparison.notequal, c.compare(Path.Type.directory, new PathAttributes().setETag("1"), new PathAttributes().setETag("2"))); - assertEquals(Comparison.equal, c.compare(Path.Type.directory, new PathAttributes().setModificationDate(1680879106939L), new PathAttributes().setModificationDate(1680879106939L))); - assertEquals(1546892887, c.hashCode(Path.Type.directory, new PathAttributes().setModificationDate(1680879106939L))); - assertEquals(1546892887, c.hashCode(Path.Type.directory, new PathAttributes().setModificationDate(1680879106000L))); - assertEquals(Comparison.local, c.compare(Path.Type.directory, new PathAttributes().setModificationDate(1680879107939L), new PathAttributes().setModificationDate(1680879106939L))); + assertEquals(Comparison.equal, c.compare(Path.Type.directory, new DefaultPathAttributes().setETag("1"), new DefaultPathAttributes().setETag("1"))); + assertEquals(1519, c.hashCode(Path.Type.directory, new DefaultPathAttributes().setETag("1"))); + assertEquals(Comparison.notequal, c.compare(Path.Type.directory, new DefaultPathAttributes().setETag("1"), new DefaultPathAttributes().setETag("2"))); + assertEquals(Comparison.equal, c.compare(Path.Type.directory, new DefaultPathAttributes().setModificationDate(1680879106939L), new DefaultPathAttributes().setModificationDate(1680879106939L))); + assertEquals(1546892887, c.hashCode(Path.Type.directory, new DefaultPathAttributes().setModificationDate(1680879106939L))); + assertEquals(1546892887, c.hashCode(Path.Type.directory, new DefaultPathAttributes().setModificationDate(1680879106000L))); + assertEquals(Comparison.local, c.compare(Path.Type.directory, new DefaultPathAttributes().setModificationDate(1680879107939L), new DefaultPathAttributes().setModificationDate(1680879106939L))); } { final DefaultComparisonService c = new DefaultComparisonService(new TestProtocol() { @@ -62,20 +63,20 @@ public class DefaultComparisonServiceTest { return DirectoryTimestamp.explicit; } }); - assertEquals(0, c.hashCode(Path.Type.directory, new PathAttributes())); - assertEquals(Comparison.equal, c.compare(Path.Type.directory, new PathAttributes().setETag("1"), new PathAttributes().setETag("1"))); - assertEquals(1519, c.hashCode(Path.Type.directory, new PathAttributes().setETag("1"))); - assertEquals(Comparison.notequal, c.compare(Path.Type.directory, new PathAttributes().setETag("1"), new PathAttributes().setETag("2"))); - assertEquals(Comparison.unknown, c.compare(Path.Type.directory, new PathAttributes().setModificationDate(1680879106939L), new PathAttributes().setModificationDate(1680879106939L))); - assertEquals(0, c.hashCode(Path.Type.directory, new PathAttributes().setModificationDate(1680879106939L))); - assertEquals(Comparison.unknown, c.compare(Path.Type.directory, new PathAttributes().setModificationDate(1680879107939L), new PathAttributes().setModificationDate(1680879106939L))); + assertEquals(0, c.hashCode(Path.Type.directory, new DefaultPathAttributes())); + assertEquals(Comparison.equal, c.compare(Path.Type.directory, new DefaultPathAttributes().setETag("1"), new DefaultPathAttributes().setETag("1"))); + assertEquals(1519, c.hashCode(Path.Type.directory, new DefaultPathAttributes().setETag("1"))); + assertEquals(Comparison.notequal, c.compare(Path.Type.directory, new DefaultPathAttributes().setETag("1"), new DefaultPathAttributes().setETag("2"))); + assertEquals(Comparison.unknown, c.compare(Path.Type.directory, new DefaultPathAttributes().setModificationDate(1680879106939L), new DefaultPathAttributes().setModificationDate(1680879106939L))); + assertEquals(0, c.hashCode(Path.Type.directory, new DefaultPathAttributes().setModificationDate(1680879106939L))); + assertEquals(Comparison.unknown, c.compare(Path.Type.directory, new DefaultPathAttributes().setModificationDate(1680879107939L), new DefaultPathAttributes().setModificationDate(1680879106939L))); } } @Test public void testCompareSymlink() { final DefaultComparisonService c = new DefaultComparisonService(new TestProtocol()); - assertEquals(Comparison.equal, c.compare(Path.Type.symboliclink, new PathAttributes().setModificationDate(1680879106939L), new PathAttributes().setModificationDate(1680879106939L))); - assertEquals(Comparison.local, c.compare(Path.Type.symboliclink, new PathAttributes().setModificationDate(1680879107939L), new PathAttributes().setModificationDate(1680879106939L))); + assertEquals(Comparison.equal, c.compare(Path.Type.symboliclink, new DefaultPathAttributes().setModificationDate(1680879106939L), new DefaultPathAttributes().setModificationDate(1680879106939L))); + assertEquals(Comparison.local, c.compare(Path.Type.symboliclink, new DefaultPathAttributes().setModificationDate(1680879107939L), new DefaultPathAttributes().setModificationDate(1680879106939L))); } } \ No newline at end of file diff --git a/core/src/test/java/ch/cyberduck/core/synchronization/SizeComparisonServiceTest.java b/core/src/test/java/ch/cyberduck/core/synchronization/SizeComparisonServiceTest.java index 2024a6f12c..d411f34dc4 100644 --- a/core/src/test/java/ch/cyberduck/core/synchronization/SizeComparisonServiceTest.java +++ b/core/src/test/java/ch/cyberduck/core/synchronization/SizeComparisonServiceTest.java @@ -1,5 +1,6 @@ package ch.cyberduck.core.synchronization; +import ch.cyberduck.core.DefaultPathAttributes; import ch.cyberduck.core.Path; import ch.cyberduck.core.PathAttributes; @@ -12,9 +13,9 @@ public class SizeComparisonServiceTest { @Test public void testCompare() { ComparisonService s = new SizeComparisonService(); - assertEquals(Comparison.remote, s.compare(Path.Type.file, new PathAttributes().setSize(0L), new PathAttributes().setSize(1L))); - assertEquals(Comparison.local, s.compare(Path.Type.file, new PathAttributes().setSize(1L), new PathAttributes().setSize(0L))); - assertEquals(Comparison.equal, s.compare(Path.Type.file, new PathAttributes().setSize(1L), new PathAttributes().setSize(1L))); - assertEquals(Comparison.notequal, s.compare(Path.Type.file, new PathAttributes().setSize(2L), new PathAttributes().setSize(1L))); + assertEquals(Comparison.remote, s.compare(Path.Type.file, new DefaultPathAttributes().setSize(0L), new DefaultPathAttributes().setSize(1L))); + assertEquals(Comparison.local, s.compare(Path.Type.file, new DefaultPathAttributes().setSize(1L), new DefaultPathAttributes().setSize(0L))); + assertEquals(Comparison.equal, s.compare(Path.Type.file, new DefaultPathAttributes().setSize(1L), new DefaultPathAttributes().setSize(1L))); + assertEquals(Comparison.notequal, s.compare(Path.Type.file, new DefaultPathAttributes().setSize(2L), new DefaultPathAttributes().setSize(1L))); } } diff --git a/core/src/test/java/ch/cyberduck/core/synchronization/TimestampComparisonServiceTest.java b/core/src/test/java/ch/cyberduck/core/synchronization/TimestampComparisonServiceTest.java index 22e8fbdc2b..40962dc033 100644 --- a/core/src/test/java/ch/cyberduck/core/synchronization/TimestampComparisonServiceTest.java +++ b/core/src/test/java/ch/cyberduck/core/synchronization/TimestampComparisonServiceTest.java @@ -1,5 +1,6 @@ package ch.cyberduck.core.synchronization; +import ch.cyberduck.core.DefaultPathAttributes; import ch.cyberduck.core.Path; import ch.cyberduck.core.PathAttributes; @@ -17,12 +18,12 @@ public class TimestampComparisonServiceTest { public void testCompareEqual() { TimestampComparisonService s = new TimestampComparisonService(); final long timestamp = System.currentTimeMillis(); - assertEquals(Comparison.equal, s.compare(Path.Type.file, new PathAttributes().setModificationDate(timestamp), new PathAttributes().setModificationDate(timestamp))); - final int hashCode = s.hashCode(Path.Type.file, new PathAttributes().setModificationDate(timestamp)); - assertEquals(Comparison.equal, s.compare(Path.Type.file, new PathAttributes().setModificationDate(timestamp), new PathAttributes().setModificationDate( + assertEquals(Comparison.equal, s.compare(Path.Type.file, new DefaultPathAttributes().setModificationDate(timestamp), new DefaultPathAttributes().setModificationDate(timestamp))); + final int hashCode = s.hashCode(Path.Type.file, new DefaultPathAttributes().setModificationDate(timestamp)); + assertEquals(Comparison.equal, s.compare(Path.Type.file, new DefaultPathAttributes().setModificationDate(timestamp), new DefaultPathAttributes().setModificationDate( TimeUnit.SECONDS.toMillis(TimeUnit.MILLISECONDS.toSeconds(timestamp)) ))); - assertEquals(hashCode, s.hashCode(Path.Type.file, new PathAttributes().setModificationDate( + assertEquals(hashCode, s.hashCode(Path.Type.file, new DefaultPathAttributes().setModificationDate( TimeUnit.SECONDS.toMillis(TimeUnit.MILLISECONDS.toSeconds(timestamp)) ))); } @@ -32,8 +33,9 @@ public class TimestampComparisonServiceTest { TimestampComparisonService s = new TimestampComparisonService(); final long timestamp = Calendar.getInstance(TimeZone.getDefault()).getTimeInMillis(); assertEquals(Comparison.local, s.compare(Path.Type.file, - new PathAttributes().setModificationDate(timestamp), - new PathAttributes() { + new DefaultPathAttributes().setModificationDate(timestamp), + new + DefaultPathAttributes() { @Override public long getModificationDate() { final Calendar c = Calendar.getInstance(TimeZone.getDefault()); diff --git a/core/src/test/java/ch/cyberduck/core/threading/TransferBackgroundActionTest.java b/core/src/test/java/ch/cyberduck/core/threading/TransferBackgroundActionTest.java index f4c0908a75..cbacece564 100644 --- a/core/src/test/java/ch/cyberduck/core/threading/TransferBackgroundActionTest.java +++ b/core/src/test/java/ch/cyberduck/core/threading/TransferBackgroundActionTest.java @@ -71,7 +71,7 @@ public class TransferBackgroundActionTest { } }; final Host host = new Host(new TestProtocol(), "l"); - host.setTransfer(Host.TransferType.concurrent); + host.setTransferType(Host.TransferType.concurrent); assertEquals(ConcurrentTransferWorker.class, new TransferBackgroundAction(controller, new StatelessSessionPool( new TestLoginConnectionService(), new NullSession(host), new DisabledTranscriptListener(), new DefaultVaultRegistry(new DisabledPasswordCallback())), SessionPool.DISCONNECTED, diff --git a/core/src/test/java/ch/cyberduck/core/transfer/DownloadTransferTest.java b/core/src/test/java/ch/cyberduck/core/transfer/DownloadTransferTest.java index 6d99ffc4da..cbaf6fc170 100755 --- a/core/src/test/java/ch/cyberduck/core/transfer/DownloadTransferTest.java +++ b/core/src/test/java/ch/cyberduck/core/transfer/DownloadTransferTest.java @@ -1,7 +1,6 @@ package ch.cyberduck.core.transfer; import ch.cyberduck.core.*; -import ch.cyberduck.core.features.Read; import ch.cyberduck.core.filter.DownloadRegexFilter; import ch.cyberduck.core.io.DisabledStreamListener; import ch.cyberduck.core.local.LocalTouchFactory; @@ -179,7 +178,7 @@ public class DownloadTransferTest { @Override public AbstractDownloadFilter filter(final Session source, final Session d, final TransferAction action, final ProgressListener listener) { return new ResumeFilter(new DownloadSymlinkResolver(Collections.singletonList(new TransferItem(test))), - new NullTransferSession(new Host(new TestProtocol())), new DefaultDownloadFeature(source.getFeature(Read.class)) { + new NullTransferSession(new Host(new TestProtocol())), new DefaultDownloadFeature(session) { @Override public boolean offset(final Path file) { return true; diff --git a/core/src/test/java/ch/cyberduck/core/transfer/SyncTransferTest.java b/core/src/test/java/ch/cyberduck/core/transfer/SyncTransferTest.java index db4f27f0a8..f3266c09c0 100644 --- a/core/src/test/java/ch/cyberduck/core/transfer/SyncTransferTest.java +++ b/core/src/test/java/ch/cyberduck/core/transfer/SyncTransferTest.java @@ -18,20 +18,7 @@ package ch.cyberduck.core.transfer; * dkocher@cyberduck.ch */ -import ch.cyberduck.core.AttributedList; -import ch.cyberduck.core.DisabledListProgressListener; -import ch.cyberduck.core.DisabledProgressListener; -import ch.cyberduck.core.Host; -import ch.cyberduck.core.ListProgressListener; -import ch.cyberduck.core.Local; -import ch.cyberduck.core.LocalAttributes; -import ch.cyberduck.core.NullLocal; -import ch.cyberduck.core.NullSession; -import ch.cyberduck.core.NullTransferSession; -import ch.cyberduck.core.Path; -import ch.cyberduck.core.PathAttributes; -import ch.cyberduck.core.PathCache; -import ch.cyberduck.core.TestProtocol; +import ch.cyberduck.core.*; import ch.cyberduck.core.local.DefaultLocalDirectoryFeature; import ch.cyberduck.core.synchronization.Comparison; @@ -118,8 +105,8 @@ public class SyncTransferTest { @Test public void testFilterMirror() throws Exception { final Path p = new Path("t", EnumSet.of(Path.Type.directory)); - final Path a = new Path(p, "a", EnumSet.of(Path.Type.file)).withAttributes(new PathAttributes().setSize(2L)); - final Path b = new Path(p, "b", EnumSet.of(Path.Type.file)).withAttributes(new PathAttributes().setSize(1L)); + final Path a = new Path(p, "a", EnumSet.of(Path.Type.file)).withAttributes(new DefaultPathAttributes().setSize(2L)); + final Path b = new Path(p, "b", EnumSet.of(Path.Type.file)).withAttributes(new DefaultPathAttributes().setSize(1L)); final PathCache cache = new PathCache(1); cache.put(p, new AttributedList<>(Arrays.asList(a, b))); SyncTransfer t = new SyncTransfer(new Host(new TestProtocol()), new TransferItem(p, new NullLocal(System.getProperty("java.io.tmpdir"), "t"))); diff --git a/core/src/test/java/ch/cyberduck/core/transfer/UploadTransferTest.java b/core/src/test/java/ch/cyberduck/core/transfer/UploadTransferTest.java index 97a053aac6..a7fd694fbb 100755 --- a/core/src/test/java/ch/cyberduck/core/transfer/UploadTransferTest.java +++ b/core/src/test/java/ch/cyberduck/core/transfer/UploadTransferTest.java @@ -393,7 +393,7 @@ public class UploadTransferTest { return (T) new AttributesFinder() { @Override public PathAttributes find(final Path file, final ListProgressListener listener) { - return new PathAttributes(); + return new DefaultPathAttributes(); } }; } diff --git a/core/src/test/java/ch/cyberduck/core/transfer/download/ResumeFilterTest.java b/core/src/test/java/ch/cyberduck/core/transfer/download/ResumeFilterTest.java index 56005ae156..5daa292180 100644 --- a/core/src/test/java/ch/cyberduck/core/transfer/download/ResumeFilterTest.java +++ b/core/src/test/java/ch/cyberduck/core/transfer/download/ResumeFilterTest.java @@ -8,7 +8,6 @@ import ch.cyberduck.core.NullSession; import ch.cyberduck.core.NullTransferSession; import ch.cyberduck.core.Path; import ch.cyberduck.core.TestProtocol; -import ch.cyberduck.core.features.Read; import ch.cyberduck.core.shared.DefaultDownloadFeature; import ch.cyberduck.core.transfer.TransferStatus; import ch.cyberduck.core.transfer.symlink.DisabledDownloadSymlinkResolver; @@ -56,7 +55,7 @@ public class ResumeFilterTest { final Host host = new Host(new TestProtocol()); final NullSession session = new NullTransferSession(host); ResumeFilter f = new ResumeFilter(new DisabledDownloadSymlinkResolver(), session, - new DefaultDownloadFeature(session.getFeature(Read.class)) { + new DefaultDownloadFeature(session) { @Override public boolean offset(final Path file) { return true; @@ -96,7 +95,7 @@ public class ResumeFilterTest { final Host host = new Host(new TestProtocol()); final NullSession session = new NullTransferSession(host); ResumeFilter f = new ResumeFilter(new DisabledDownloadSymlinkResolver(), session, - new DefaultDownloadFeature(session.getFeature(Read.class)) { + new DefaultDownloadFeature(session) { @Override public boolean offset(final Path file) { return true; @@ -128,7 +127,7 @@ public class ResumeFilterTest { final Host host = new Host(new TestProtocol()); final NullSession session = new NullTransferSession(host); ResumeFilter f = new ResumeFilter(new DisabledDownloadSymlinkResolver(), session, - new DefaultDownloadFeature(session.getFeature(Read.class)) { + new DefaultDownloadFeature(session) { @Override public boolean offset(final Path file) { return true; diff --git a/core/src/test/java/ch/cyberduck/core/transfer/upload/OverwriteFilterTest.java b/core/src/test/java/ch/cyberduck/core/transfer/upload/OverwriteFilterTest.java index ace7c02764..df16df913a 100644 --- a/core/src/test/java/ch/cyberduck/core/transfer/upload/OverwriteFilterTest.java +++ b/core/src/test/java/ch/cyberduck/core/transfer/upload/OverwriteFilterTest.java @@ -1,6 +1,7 @@ package ch.cyberduck.core.transfer.upload; import ch.cyberduck.core.Acl; +import ch.cyberduck.core.DefaultPathAttributes; import ch.cyberduck.core.DisabledProgressListener; import ch.cyberduck.core.Host; import ch.cyberduck.core.ListProgressListener; @@ -144,7 +145,7 @@ public class OverwriteFilterTest { final AttributesFinder attributes = new AttributesFinder() { @Override public PathAttributes find(final Path file, final ListProgressListener listener) { - return new PathAttributes(); + return new DefaultPathAttributes(); } }; final Find find = new Find() { diff --git a/core/src/test/java/ch/cyberduck/core/transfer/upload/RenameExistingFilterTest.java b/core/src/test/java/ch/cyberduck/core/transfer/upload/RenameExistingFilterTest.java index 2ed4339a9a..0dc13a1827 100755 --- a/core/src/test/java/ch/cyberduck/core/transfer/upload/RenameExistingFilterTest.java +++ b/core/src/test/java/ch/cyberduck/core/transfer/upload/RenameExistingFilterTest.java @@ -2,6 +2,7 @@ package ch.cyberduck.core.transfer.upload; import ch.cyberduck.core.AttributedList; import ch.cyberduck.core.ConnectionCallback; +import ch.cyberduck.core.DefaultPathAttributes; import ch.cyberduck.core.DisabledProgressListener; import ch.cyberduck.core.Host; import ch.cyberduck.core.ListProgressListener; @@ -106,7 +107,7 @@ public class RenameExistingFilterTest { final AttributesFinder attributes = new AttributesFinder() { @Override public PathAttributes find(final Path file, final ListProgressListener listener) { - return new PathAttributes(); + return new DefaultPathAttributes(); } }; final Host host = new Host(new TestProtocol()); @@ -189,7 +190,7 @@ public class RenameExistingFilterTest { final AttributesFinder attributes = new AttributesFinder() { @Override public PathAttributes find(final Path file, final ListProgressListener listener) { - return new PathAttributes(); + return new DefaultPathAttributes(); } }; final Host host = new Host(new TestProtocol()); diff --git a/core/src/test/java/ch/cyberduck/core/transfer/upload/RenameFilterTest.java b/core/src/test/java/ch/cyberduck/core/transfer/upload/RenameFilterTest.java index 9312ae229d..d5d664cb0e 100644 --- a/core/src/test/java/ch/cyberduck/core/transfer/upload/RenameFilterTest.java +++ b/core/src/test/java/ch/cyberduck/core/transfer/upload/RenameFilterTest.java @@ -1,5 +1,6 @@ package ch.cyberduck.core.transfer.upload; +import ch.cyberduck.core.DefaultPathAttributes; import ch.cyberduck.core.DisabledProgressListener; import ch.cyberduck.core.Host; import ch.cyberduck.core.ListProgressListener; @@ -37,7 +38,7 @@ public class RenameFilterTest { final AttributesFinder attributes = new AttributesFinder() { @Override public PathAttributes find(final Path file, final ListProgressListener listener) { - return new PathAttributes(); + return new DefaultPathAttributes(); } }; final Find find = new Find() { @@ -78,7 +79,7 @@ public class RenameFilterTest { final AttributesFinder attributes = new AttributesFinder() { @Override public PathAttributes find(final Path file, final ListProgressListener listener) { - return new PathAttributes(); + return new DefaultPathAttributes(); } }; final Find find = new Find() { diff --git a/core/src/test/java/ch/cyberduck/core/transfer/upload/ResumeFilterTest.java b/core/src/test/java/ch/cyberduck/core/transfer/upload/ResumeFilterTest.java index 063ccc3e05..96a8a5ad78 100644 --- a/core/src/test/java/ch/cyberduck/core/transfer/upload/ResumeFilterTest.java +++ b/core/src/test/java/ch/cyberduck/core/transfer/upload/ResumeFilterTest.java @@ -1,6 +1,7 @@ package ch.cyberduck.core.transfer.upload; import ch.cyberduck.core.AttributedList; +import ch.cyberduck.core.DefaultPathAttributes; import ch.cyberduck.core.DisabledProgressListener; import ch.cyberduck.core.Host; import ch.cyberduck.core.ListProgressListener; @@ -8,7 +9,6 @@ import ch.cyberduck.core.LocalAttributes; import ch.cyberduck.core.NullLocal; import ch.cyberduck.core.NullSession; import ch.cyberduck.core.NullUploadFeature; -import ch.cyberduck.core.NullWriteFeature; import ch.cyberduck.core.Path; import ch.cyberduck.core.PathAttributes; import ch.cyberduck.core.TestProtocol; @@ -151,7 +151,7 @@ public class ResumeFilterTest { @Override public AttributedList list(final Path folder, final ListProgressListener listener) throws BackgroundException { final AttributedList list = new AttributedList<>(Collections.singletonList(new Path(folder, "t", EnumSet.of(Path.Type.file)) - .withAttributes(new PathAttributes().setSize(3L)))); + .withAttributes(new DefaultPathAttributes().setSize(3L)))); listener.chunk(folder, list); return list; } @@ -190,7 +190,7 @@ public class ResumeFilterTest { @Override public AttributedList list(final Path folder, final ListProgressListener listener) throws BackgroundException { final AttributedList list = new AttributedList<>(Collections.singletonList(new Path(folder, "t", EnumSet.of(Path.Type.file)) - .withAttributes(new PathAttributes().setSize(2L)))); + .withAttributes(new DefaultPathAttributes().setSize(2L)))); listener.chunk(folder, list); return list; } @@ -235,13 +235,13 @@ public class ResumeFilterTest { @Override public AttributedList list(final Path folder, final ListProgressListener listener) throws BackgroundException { final AttributedList list = new AttributedList<>(Collections.singletonList(new Path(folder, "t", EnumSet.of(Path.Type.file)) - .withAttributes(new PathAttributes().setSize(4L)))); + .withAttributes(new DefaultPathAttributes().setSize(4L)))); listener.chunk(folder, list); return list; } }; final ResumeFilter f = new ResumeFilter(new DisabledUploadSymlinkResolver(), session, - new DefaultUploadFeature<>(new NullWriteFeature()), new UploadFilterOptions(host).withTemporary(true)); + new DefaultUploadFeature<>(session), new UploadFilterOptions(host).withTemporary(true)); final long size = 3L; final Path t = new Path("t", EnumSet.of(Path.Type.file)); final NullLocal l = new NullLocal("t") { diff --git a/core/src/test/java/ch/cyberduck/core/worker/DeleteWorkerTest.java b/core/src/test/java/ch/cyberduck/core/worker/DeleteWorkerTest.java index 6fc5cef1a4..832e83e828 100644 --- a/core/src/test/java/ch/cyberduck/core/worker/DeleteWorkerTest.java +++ b/core/src/test/java/ch/cyberduck/core/worker/DeleteWorkerTest.java @@ -87,8 +87,8 @@ public class DeleteWorkerTest { } @Override - public boolean isRecursive() { - return true; + public EnumSet features(final Path file) { + return EnumSet.of(Flags.recursive); } }; } @@ -133,8 +133,8 @@ public class DeleteWorkerTest { } @Override - public boolean isRecursive() { - return true; + public EnumSet features(final Path file) { + return EnumSet.of(Flags.recursive); } }; } @@ -182,11 +182,6 @@ public class DeleteWorkerTest { public void delete(final Map files, final PasswordCallback prompt, final Callback callback) { assertEquals(new Path("/s", EnumSet.of(Path.Type.directory, AbstractPath.Type.symboliclink)), new ArrayList<>(files.keySet()).get(0)); } - - @Override - public boolean isRecursive() { - return false; - } }; } return super._getFeature(type); diff --git a/core/src/test/java/ch/cyberduck/core/worker/MoveWorkerTest.java b/core/src/test/java/ch/cyberduck/core/worker/MoveWorkerTest.java index 682c9abee9..07e8a3856f 100644 --- a/core/src/test/java/ch/cyberduck/core/worker/MoveWorkerTest.java +++ b/core/src/test/java/ch/cyberduck/core/worker/MoveWorkerTest.java @@ -17,6 +17,7 @@ package ch.cyberduck.core.worker; import ch.cyberduck.core.AttributedList; import ch.cyberduck.core.ConnectionCallback; +import ch.cyberduck.core.DefaultPathAttributes; import ch.cyberduck.core.DisabledLoginCallback; import ch.cyberduck.core.DisabledProgressListener; import ch.cyberduck.core.Host; @@ -31,6 +32,7 @@ import ch.cyberduck.core.TestProtocol; import ch.cyberduck.core.features.Delete; import ch.cyberduck.core.features.Directory; import ch.cyberduck.core.features.Move; +import ch.cyberduck.core.features.Write; import ch.cyberduck.core.pool.SessionPool; import ch.cyberduck.core.transfer.TransferStatus; @@ -69,18 +71,13 @@ public class MoveWorkerTest { count.incrementAndGet(); } } - - @Override - public boolean isRecursive() { - return false; - } }; } if(type == Directory.class) { return (T) new Directory() { @Override - public Path mkdir(final Path folder, final TransferStatus status) { + public Path mkdir(final Write writer, final Path folder, final TransferStatus status) { return folder; } }; @@ -88,7 +85,7 @@ public class MoveWorkerTest { if(type == Move.class) { return (T) new Move() { private final AtomicInteger count = new AtomicInteger(); - private final PathAttributes attr = new PathAttributes().setSize(1L); + private final PathAttributes attr = new DefaultPathAttributes().setSize(1L); @Override public Path move(final Path file, final Path renamed, final TransferStatus status, final Delete.Callback callback, final ConnectionCallback connectionCallback) { diff --git a/core/src/test/java/ch/cyberduck/core/worker/ReadAclWorkerTest.java b/core/src/test/java/ch/cyberduck/core/worker/ReadAclWorkerTest.java index 0c13412c03..d3e173b70b 100644 --- a/core/src/test/java/ch/cyberduck/core/worker/ReadAclWorkerTest.java +++ b/core/src/test/java/ch/cyberduck/core/worker/ReadAclWorkerTest.java @@ -60,7 +60,7 @@ public class ReadAclWorkerTest { } @Override - public List getAvailableAclUsers() { + public List getAvailableAclUsers(final List files) { throw new UnsupportedOperationException(); } diff --git a/core/src/test/java/ch/cyberduck/core/worker/ReadMetadataWorkerTest.java b/core/src/test/java/ch/cyberduck/core/worker/ReadMetadataWorkerTest.java index edbf85af1a..beb9c3b7d3 100644 --- a/core/src/test/java/ch/cyberduck/core/worker/ReadMetadataWorkerTest.java +++ b/core/src/test/java/ch/cyberduck/core/worker/ReadMetadataWorkerTest.java @@ -36,7 +36,7 @@ public class ReadMetadataWorkerTest { if(type == Metadata.class) { return (T) new Metadata() { @Override - public Map getDefault() { + public Map getDefault(final Path file) { return Collections.emptyMap(); } @@ -75,7 +75,7 @@ public class ReadMetadataWorkerTest { if(type == Metadata.class) { return (T) new Metadata() { @Override - public Map getDefault() { + public Map getDefault(final Path file) { return Collections.emptyMap(); } @@ -129,7 +129,7 @@ public class ReadMetadataWorkerTest { if(type == Metadata.class) { return (T) new Metadata() { @Override - public Map getDefault() { + public Map getDefault(final Path file) { return Collections.emptyMap(); } diff --git a/core/src/test/java/ch/cyberduck/core/worker/ReadWriteMetadataWorkerTest.java b/core/src/test/java/ch/cyberduck/core/worker/ReadWriteMetadataWorkerTest.java index dc84ba762a..5369562927 100644 --- a/core/src/test/java/ch/cyberduck/core/worker/ReadWriteMetadataWorkerTest.java +++ b/core/src/test/java/ch/cyberduck/core/worker/ReadWriteMetadataWorkerTest.java @@ -121,7 +121,7 @@ public class ReadWriteMetadataWorkerTest { return (T) new Metadata() { @Override - public Map getDefault() { + public Map getDefault(final Path file) { return Collections.emptyMap(); } diff --git a/core/src/test/java/ch/cyberduck/core/worker/WriteAclWorkerTest.java b/core/src/test/java/ch/cyberduck/core/worker/WriteAclWorkerTest.java index fba467ffae..6014e4f332 100644 --- a/core/src/test/java/ch/cyberduck/core/worker/WriteAclWorkerTest.java +++ b/core/src/test/java/ch/cyberduck/core/worker/WriteAclWorkerTest.java @@ -47,7 +47,7 @@ public class WriteAclWorkerTest { } @Override - public List getAvailableAclUsers() { + public List getAvailableAclUsers(final List files) { throw new UnsupportedOperationException(); } @@ -90,7 +90,7 @@ public class WriteAclWorkerTest { } @Override - public List getAvailableAclUsers() { + public List getAvailableAclUsers(final List files) { throw new UnsupportedOperationException(); } @@ -135,7 +135,7 @@ public class WriteAclWorkerTest { } @Override - public List getAvailableAclUsers() { + public List getAvailableAclUsers(final List files) { throw new UnsupportedOperationException(); } diff --git a/core/src/test/java/ch/cyberduck/core/worker/WriteMetadataWorkerTest.java b/core/src/test/java/ch/cyberduck/core/worker/WriteMetadataWorkerTest.java index ddd0ace3a5..23b95e2611 100644 --- a/core/src/test/java/ch/cyberduck/core/worker/WriteMetadataWorkerTest.java +++ b/core/src/test/java/ch/cyberduck/core/worker/WriteMetadataWorkerTest.java @@ -1,5 +1,6 @@ package ch.cyberduck.core.worker; +import ch.cyberduck.core.DefaultPathAttributes; import ch.cyberduck.core.DisabledProgressListener; import ch.cyberduck.core.Host; import ch.cyberduck.core.NullSession; @@ -40,7 +41,7 @@ public class WriteMetadataWorkerTest { if(type == Metadata.class) { return (T) new Metadata() { @Override - public Map getDefault() { + public Map getDefault(final Path file) { return Collections.emptyMap(); } @@ -85,7 +86,7 @@ public class WriteMetadataWorkerTest { if(type == Metadata.class) { return (T) new Metadata() { @Override - public Map getDefault() { + public Map getDefault(final Path file) { return Collections.emptyMap(); } @@ -132,7 +133,7 @@ public class WriteMetadataWorkerTest { if(type == Metadata.class) { return (T) new Metadata() { @Override - public Map getDefault() { + public Map getDefault(final Path file) { return Collections.emptyMap(); } @@ -183,7 +184,7 @@ public class WriteMetadataWorkerTest { if(type == Metadata.class) { return (T) new Metadata() { @Override - public Map getDefault() { + public Map getDefault(final Path file) { return Collections.emptyMap(); } @@ -210,7 +211,8 @@ public class WriteMetadataWorkerTest { @Test public void testRunDifferent() throws Exception { - final PathAttributes attributesA = new PathAttributes(); + final PathAttributes attributesA = new + DefaultPathAttributes(); { final Map map = new HashMap<>(); map.put("equal", "equal"); @@ -218,7 +220,7 @@ public class WriteMetadataWorkerTest { map.put("unique", "unique"); attributesA.setMetadata(map); } - final PathAttributes attributesB = new PathAttributes(); + final PathAttributes attributesB = new DefaultPathAttributes(); { final Map map = new HashMap<>(); map.put("equal", "equal"); @@ -250,7 +252,7 @@ public class WriteMetadataWorkerTest { if(type == Metadata.class) { return (T) new Metadata() { @Override - public Map getDefault() { + public Map getDefault(final Path file) { return Collections.emptyMap(); } diff --git a/core/src/test/java/ch/cyberduck/ui/browser/RecursiveSearchFilterTest.java b/core/src/test/java/ch/cyberduck/ui/browser/RecursiveSearchFilterTest.java index d9af34cc84..9270292cb1 100644 --- a/core/src/test/java/ch/cyberduck/ui/browser/RecursiveSearchFilterTest.java +++ b/core/src/test/java/ch/cyberduck/ui/browser/RecursiveSearchFilterTest.java @@ -16,6 +16,7 @@ package ch.cyberduck.ui.browser; */ import ch.cyberduck.core.AttributedList; +import ch.cyberduck.core.DefaultPathAttributes; import ch.cyberduck.core.Path; import ch.cyberduck.core.PathAttributes; @@ -62,6 +63,6 @@ public class RecursiveSearchFilterTest { @Test public void testAcceptFileVersions() { final RecursiveSearchFilter f = new RecursiveSearchFilter(new AttributedList<>(Arrays.asList(new Path("/f", EnumSet.of(Path.Type.file))))); - assertTrue(f.accept(new Path("/f", EnumSet.of(Path.Type.file), new PathAttributes().setVersionId("1")))); + assertTrue(f.accept(new Path("/f", EnumSet.of(Path.Type.file), new DefaultPathAttributes().setVersionId("1")))); } } diff --git a/core/src/test/java/ch/cyberduck/ui/browser/SizeTooltipServiceTest.java b/core/src/test/java/ch/cyberduck/ui/browser/SizeTooltipServiceTest.java index 6be936c7ee..e42eaee103 100644 --- a/core/src/test/java/ch/cyberduck/ui/browser/SizeTooltipServiceTest.java +++ b/core/src/test/java/ch/cyberduck/ui/browser/SizeTooltipServiceTest.java @@ -15,6 +15,7 @@ package ch.cyberduck.ui.browser; * GNU General Public License for more details. */ +import ch.cyberduck.core.DefaultPathAttributes; import ch.cyberduck.core.Path; import ch.cyberduck.core.PathAttributes; @@ -31,7 +32,7 @@ public class SizeTooltipServiceTest { @Test public void testGetTooltip() { final SizeTooltipService s = new SizeTooltipService(); - final PathAttributes attr = new PathAttributes(); + final PathAttributes attr = new DefaultPathAttributes(); attr.setSize(-1L); assertEquals("--", s.getTooltip(new Path("/p", EnumSet.of(Path.Type.file), attr))); attr.setSize(0L); diff --git a/cryptomator/dll/pom.xml b/cryptomator/dll/pom.xml index 11b2d6059b..96c9da43ee 100644 --- a/cryptomator/dll/pom.xml +++ b/cryptomator/dll/pom.xml @@ -20,7 +20,7 @@ ch.cyberduck parent ../../pom.xml - 9.2.0-SNAPSHOT + 9.4.0-SNAPSHOT Cyberduck.Cryptomator pom diff --git a/cryptomator/pom.xml b/cryptomator/pom.xml index 4036ba6fa6..881960cdf4 100644 --- a/cryptomator/pom.xml +++ b/cryptomator/pom.xml @@ -19,7 +19,7 @@ parent ch.cyberduck - 9.2.0-SNAPSHOT + 9.4.0-SNAPSHOT cryptomator jar diff --git a/cryptomator/src/main/java/ch/cyberduck/core/cryptomator/ContentWriter.java b/cryptomator/src/main/java/ch/cyberduck/core/cryptomator/ContentWriter.java index 0536c64502..e3674bd9de 100644 --- a/cryptomator/src/main/java/ch/cyberduck/core/cryptomator/ContentWriter.java +++ b/cryptomator/src/main/java/ch/cyberduck/core/cryptomator/ContentWriter.java @@ -47,7 +47,7 @@ public class ContentWriter { final Write write = session._getFeature(Write.class); status.setLength(content.length); status.setChecksum(write.checksum(file, status).compute(new ByteArrayInputStream(content), status)); - final Encryption encryption = session.getFeature(Encryption.class); + final Encryption encryption = session._getFeature(Encryption.class); if(encryption != null) { status.setEncryption(encryption.getDefault(file)); } diff --git a/cryptomator/src/main/java/ch/cyberduck/core/cryptomator/CryptoAclPermission.java b/cryptomator/src/main/java/ch/cyberduck/core/cryptomator/CryptoAclPermission.java index eb7c1799ef..4b31674db9 100644 --- a/cryptomator/src/main/java/ch/cyberduck/core/cryptomator/CryptoAclPermission.java +++ b/cryptomator/src/main/java/ch/cyberduck/core/cryptomator/CryptoAclPermission.java @@ -49,8 +49,8 @@ public class CryptoAclPermission implements AclPermission { } @Override - public List getAvailableAclUsers() { - return delegate.getAvailableAclUsers(); + public List getAvailableAclUsers(final List files) { + return delegate.getAvailableAclUsers(files); } @Override diff --git a/cryptomator/src/main/java/ch/cyberduck/core/cryptomator/CryptoTransferStatus.java b/cryptomator/src/main/java/ch/cyberduck/core/cryptomator/CryptoTransferStatus.java index b4564acc14..bcb52f32f5 100644 --- a/cryptomator/src/main/java/ch/cyberduck/core/cryptomator/CryptoTransferStatus.java +++ b/cryptomator/src/main/java/ch/cyberduck/core/cryptomator/CryptoTransferStatus.java @@ -41,7 +41,9 @@ public class CryptoTransferStatus extends ProxyTransferStatus implements StreamC @Override public TransferStatus setResponse(final PathAttributes attributes) { try { - super.setResponse(attributes.setSize(vault.toCleartextSize(0L, attributes.getSize()))); + attributes.setSize(vault.toCleartextSize(0L, attributes.getSize())); + attributes.setVault(vault.getHome()); + super.setResponse(attributes); } catch(CryptoInvalidFilesizeException e) { log.warn("Failure {} translating file size from response {}", e, attributes); diff --git a/cryptomator/src/main/java/ch/cyberduck/core/cryptomator/CryptoVault.java b/cryptomator/src/main/java/ch/cyberduck/core/cryptomator/CryptoVault.java index 11a2e3b2fa..5aa7f44d01 100644 --- a/cryptomator/src/main/java/ch/cyberduck/core/cryptomator/CryptoVault.java +++ b/cryptomator/src/main/java/ch/cyberduck/core/cryptomator/CryptoVault.java @@ -126,10 +126,10 @@ public class CryptoVault implements Vault { final EnumSet type = EnumSet.copyOf(home.getType()); type.add(Path.Type.vault); if(home.isRoot()) { - this.vault = new Path(home.getAbsolute(), type, new PathAttributes(home.attributes())); + this.vault = new Path(home.getAbsolute(), type, new DefaultPathAttributes(home.attributes())); } else { - this.vault = new Path(home.getParent(), home.getName(), type, new PathAttributes(home.attributes())); + this.vault = new Path(home.getParent(), home.getName(), type, new DefaultPathAttributes(home.attributes())); } } @@ -164,11 +164,11 @@ public class CryptoVault implements Vault { // Obtain non encrypted directory writer final Directory directory = session._getFeature(Directory.class); final TransferStatus status = new TransferStatus().setRegion(region); - final Encryption encryption = session.getFeature(Encryption.class); + final Encryption encryption = session._getFeature(Encryption.class); if(encryption != null) { status.setEncryption(encryption.getDefault(home)); } - final Path vault = directory.mkdir(home, status); + final Path vault = directory.mkdir(session._getFeature(Write.class), home, status); new ContentWriter(session).write(masterkey, mkArray.toByteArray()); if(VAULT_VERSION == version) { // Create vaultconfig.cryptomator @@ -191,9 +191,9 @@ public class CryptoVault implements Vault { final Path firstLevel = secondLevel.getParent(); final Path dataDir = firstLevel.getParent(); log.debug("Create vault root directory at {}", secondLevel); - directory.mkdir(dataDir, status); - directory.mkdir(firstLevel, status); - directory.mkdir(secondLevel, status); + directory.mkdir(session._getFeature(Write.class), dataDir, status); + directory.mkdir(session._getFeature(Write.class), firstLevel, status); + directory.mkdir(session._getFeature(Write.class), secondLevel, status); return vault; } @@ -220,23 +220,28 @@ public class CryptoVault implements Vault { } private VaultConfig readVaultConfig(final Session session) throws BackgroundException { + final MasterkeyFile masterkeyFile = this.readMasterkeyFile(session, masterkey); try { - final String token = new ContentReader(session).read(config); - return parseVaultConfigFromJWT(token).withMasterkeyFile(this.readMasterkeyFile(session, masterkey)); + return parseVaultConfigFromJWT(new ContentReader(session).read(config)) + .withMasterkeyFile(masterkeyFile); } catch(NotfoundException e) { - log.debug("Ignore failure reading {}", config); - final MasterkeyFile mkfile = this.readMasterkeyFile(session, masterkey); - return new VaultConfig(mkfile.version, - mkfile.version == VAULT_VERSION_DEPRECATED ? - CryptoFilenameV6Provider.DEFAULT_NAME_SHORTENING_THRESHOLD : - CryptoFilenameV7Provider.DEFAULT_NAME_SHORTENING_THRESHOLD, - CryptorProvider.Scheme.SIV_CTRMAC, null, null).withMasterkeyFile(mkfile); + log.debug("Ignore failure reading vault configuration {}", config); + return parseVaultConfigFromMasterKey(masterkeyFile) + .withMasterkeyFile(masterkeyFile); } } + private static VaultConfig parseVaultConfigFromMasterKey(final MasterkeyFile masterkeyFile) { + return new VaultConfig(masterkeyFile.version, + masterkeyFile.version == VAULT_VERSION_DEPRECATED ? + CryptoFilenameV6Provider.DEFAULT_NAME_SHORTENING_THRESHOLD : + CryptoFilenameV7Provider.DEFAULT_NAME_SHORTENING_THRESHOLD, + CryptorProvider.Scheme.SIV_CTRMAC, null, null); + } - public static VaultConfig parseVaultConfigFromJWT(final String token) { + + private static VaultConfig parseVaultConfigFromJWT(final String token) { final DecodedJWT decoded = JWT.decode(token); return new VaultConfig( decoded.getClaim(JSON_KEY_VAULTVERSION).asInt(), @@ -245,13 +250,13 @@ public class CryptoVault implements Vault { decoded.getAlgorithm(), decoded); } - private MasterkeyFile readMasterkeyFile(final Session session, final Path masterkey) throws BackgroundException { - log.debug("Read master key {}", masterkey); - try (Reader reader = new ContentReader(session).getReader(masterkey)) { + private MasterkeyFile readMasterkeyFile(final Session session, final Path file) throws BackgroundException { + log.debug("Read master key {}", file); + try(Reader reader = new ContentReader(session).getReader(file)) { return MasterkeyFile.read(reader); } catch(JsonParseException | IllegalArgumentException | IllegalStateException | IOException e) { - throw new VaultException(String.format("Failure reading vault master key file %s", masterkey.getName()), e); + throw new VaultException(String.format("Failure reading vault master key file %s", file.getName()), e); } } @@ -281,7 +286,7 @@ public class CryptoVault implements Vault { } } else { - credentials = new VaultCredentials(passphrase).withSaved(false); + credentials = new VaultCredentials(passphrase).setSaved(false); } try { this.open(vaultConfig, credentials.getPassword()); @@ -387,10 +392,7 @@ public class CryptoVault implements Vault { @Override public boolean contains(final Path file) { - if(this.isUnlocked()) { - return new SimplePathPredicate(file).test(home) || file.isChild(home); - } - return false; + return new SimplePathPredicate(file).test(home) || file.isChild(home); } @Override @@ -425,7 +427,7 @@ public class CryptoVault implements Vault { parent = directoryProvider.toEncrypted(session, file.getParent().attributes().getDirectoryId(), file.getParent()); filename = directoryProvider.toEncrypted(session, parent.attributes().getDirectoryId(), file.getName(), file.getType()); } - final PathAttributes attributes = new PathAttributes(file.attributes()); + final PathAttributes attributes = new DefaultPathAttributes(file.attributes()); attributes.setDirectoryId(null); if(!file.isFile() && !metadata) { // The directory is different from the metadata file used to resolve the actual folder @@ -458,8 +460,10 @@ public class CryptoVault implements Vault { encrypted.attributes().setDecrypted(file); } // Add reference for vault - file.attributes().setVault(home); - encrypted.attributes().setVault(home); + if(!new SimplePathPredicate(file).test(home)) { + file.attributes().setVault(home); + encrypted.attributes().setVault(home); + } return encrypted; } @@ -482,7 +486,7 @@ public class CryptoVault implements Vault { final String cleartextFilename = fileNameCryptor.decryptFilename( vaultVersion == VAULT_VERSION_DEPRECATED ? BaseEncoding.base32() : BaseEncoding.base64Url(), ciphertext, file.getParent().attributes().getDirectoryId().getBytes(StandardCharsets.UTF_8)); - final PathAttributes attributes = new PathAttributes(file.attributes()); + final PathAttributes attributes = new DefaultPathAttributes(file.attributes()); if(this.isDirectory(inflated)) { if(Permission.EMPTY != attributes.getPermission()) { final Permission permission = new Permission(attributes.getPermission()); @@ -502,8 +506,6 @@ public class CryptoVault implements Vault { } // Add reference to encrypted file attributes.setEncrypted(file); - // Add reference for vault - attributes.setVault(home); final EnumSet type = EnumSet.copyOf(file.getType()); type.remove(this.isDirectory(inflated) ? Path.Type.file : Path.Type.directory); type.add(this.isDirectory(inflated) ? Path.Type.directory : Path.Type.file); @@ -513,6 +515,10 @@ public class CryptoVault implements Vault { if(type.contains(Path.Type.symboliclink)) { decrypted.setSymlinkTarget(file.getSymlinkTarget()); } + if(!new SimplePathPredicate(decrypted).test(home)) { + // Add reference for vault + attributes.setVault(home); + } return decrypted; } catch(AuthenticationFailedException e) { @@ -630,19 +636,19 @@ public class CryptoVault implements Vault { } if(type == Touch.class) { // Use default touch feature because touch with remote implementation will not add encrypted file header - return (T) new CryptoTouchFeature(session, new DefaultTouchFeature(session._getFeature(Write.class)), session._getFeature(Write.class), this); + return (T) new CryptoTouchFeature(session, new DefaultTouchFeature(session), this); } if(type == Directory.class) { return (T) (vaultVersion == VAULT_VERSION_DEPRECATED ? - new CryptoDirectoryV6Feature(session, (Directory) delegate, session._getFeature(Write.class), this) : - new CryptoDirectoryV7Feature(session, (Directory) delegate, session._getFeature(Write.class), this) + new CryptoDirectoryV6Feature(session, (Directory) delegate, this) : + new CryptoDirectoryV7Feature(session, (Directory) delegate, this) ); } if(type == Upload.class) { - return (T) new CryptoUploadFeature(session, (Upload) delegate, session._getFeature(Write.class), this); + return (T) new CryptoUploadFeature(session, (Upload) delegate, this); } if(type == Download.class) { - return (T) new CryptoDownloadFeature(session, (Download) delegate, session._getFeature(Read.class), this); + return (T) new CryptoDownloadFeature(session, (Download) delegate, this); } if(type == Read.class) { return (T) new CryptoReadFeature(session, (Read) delegate, this); @@ -694,7 +700,7 @@ public class CryptoVault implements Vault { return (T) new CryptoCompressFeature(session, (Compress) delegate, this); } if(type == Bulk.class) { - return (T) new CryptoBulkFeature(session, (Bulk) delegate, session._getFeature(Delete.class), this); + return (T) new CryptoBulkFeature(session, (Bulk) delegate, this); } if(type == UnixPermission.class) { return (T) new CryptoUnixPermission(session, (UnixPermission) delegate, this); diff --git a/cryptomator/src/main/java/ch/cyberduck/core/cryptomator/features/CryptoAttributesFeature.java b/cryptomator/src/main/java/ch/cyberduck/core/cryptomator/features/CryptoAttributesFeature.java index 9b77f8c979..cb2ac2eb71 100644 --- a/cryptomator/src/main/java/ch/cyberduck/core/cryptomator/features/CryptoAttributesFeature.java +++ b/cryptomator/src/main/java/ch/cyberduck/core/cryptomator/features/CryptoAttributesFeature.java @@ -15,6 +15,7 @@ package ch.cyberduck.core.cryptomator.features; * GNU General Public License for more details. */ +import ch.cyberduck.core.DefaultPathAttributes; import ch.cyberduck.core.ListProgressListener; import ch.cyberduck.core.MemoryListProgressListener; import ch.cyberduck.core.Path; @@ -41,7 +42,7 @@ public class CryptoAttributesFeature implements AttributesFinder { public PathAttributes find(final Path file, final ListProgressListener listener) throws BackgroundException { final MemoryListProgressListener memory = new MemoryListProgressListener(); // Fetch with any directory listing stored in memory encrypted - final PathAttributes attributes = new PathAttributes(delegate.find(vault.encrypt(session, file, true), memory)); + final PathAttributes attributes = new DefaultPathAttributes(delegate.find(vault.encrypt(session, file, true), memory)); final Path directory = file.getParent(); // Decrypt directory listing and forward to proxy new DecryptingListProgressListener(session, vault, directory, listener).chunk(directory, memory.getContents()); @@ -51,6 +52,7 @@ public class CryptoAttributesFeature implements AttributesFinder { if(file.isDirectory()) { attributes.setSize(-1L); } + attributes.setVault(vault.getHome()); return attributes; } diff --git a/cryptomator/src/main/java/ch/cyberduck/core/cryptomator/features/CryptoBulkFeature.java b/cryptomator/src/main/java/ch/cyberduck/core/cryptomator/features/CryptoBulkFeature.java index c16210fc64..90f7be910c 100644 --- a/cryptomator/src/main/java/ch/cyberduck/core/cryptomator/features/CryptoBulkFeature.java +++ b/cryptomator/src/main/java/ch/cyberduck/core/cryptomator/features/CryptoBulkFeature.java @@ -26,7 +26,6 @@ import ch.cyberduck.core.cryptomator.random.RandomNonceGenerator; import ch.cyberduck.core.cryptomator.random.RotatingNonceGenerator; import ch.cyberduck.core.exception.BackgroundException; import ch.cyberduck.core.features.Bulk; -import ch.cyberduck.core.features.Delete; import ch.cyberduck.core.transfer.Transfer; import ch.cyberduck.core.transfer.TransferItem; import ch.cyberduck.core.transfer.TransferStatus; @@ -48,9 +47,9 @@ public class CryptoBulkFeature implements Bulk { private final Bulk delegate; private final CryptoVault cryptomator; - public CryptoBulkFeature(final Session session, final Bulk delegate, final Delete delete, final CryptoVault cryptomator) { + public CryptoBulkFeature(final Session session, final Bulk delegate, final CryptoVault cryptomator) { this.session = session; - this.delegate = delegate.withDelete(cryptomator.getFeature(session, Delete.class, delete)); + this.delegate = delegate; this.cryptomator = cryptomator; } @@ -103,12 +102,6 @@ public class CryptoBulkFeature implements Bulk { return delegate.pre(type, encrypted, callback); } - @Override - public Bulk withDelete(final Delete delete) { - delegate.withDelete(cryptomator.getFeature(session, Delete.class, delete)); - return this; - } - @Override public void post(final Transfer.Type type, final Map files, final ConnectionCallback callback) throws BackgroundException { final Map encrypted = new HashMap<>(files.size()); diff --git a/cryptomator/src/main/java/ch/cyberduck/core/cryptomator/features/CryptoCopyFeature.java b/cryptomator/src/main/java/ch/cyberduck/core/cryptomator/features/CryptoCopyFeature.java index 83d536d17b..03cbd6a03b 100644 --- a/cryptomator/src/main/java/ch/cyberduck/core/cryptomator/features/CryptoCopyFeature.java +++ b/cryptomator/src/main/java/ch/cyberduck/core/cryptomator/features/CryptoCopyFeature.java @@ -16,6 +16,7 @@ package ch.cyberduck.core.cryptomator.features; */ import ch.cyberduck.core.ConnectionCallback; +import ch.cyberduck.core.DefaultPathAttributes; import ch.cyberduck.core.Path; import ch.cyberduck.core.PathAttributes; import ch.cyberduck.core.Session; @@ -73,7 +74,7 @@ public class CryptoCopyFeature implements Copy { public TransferStatus setResponse(final PathAttributes attributes) { status.setResponse(attributes); // Will be converted back to clear text when decrypting file below set in default copy feature implementation using writer. - super.setResponse(new PathAttributes(attributes).setSize(vault.toCiphertextSize(0L, attributes.getSize()))); + super.setResponse(new DefaultPathAttributes(attributes).setSize(vault.toCiphertextSize(0L, attributes.getSize()))); return this; } } : status, diff --git a/cryptomator/src/main/java/ch/cyberduck/core/cryptomator/features/CryptoDeleteV6Feature.java b/cryptomator/src/main/java/ch/cyberduck/core/cryptomator/features/CryptoDeleteV6Feature.java index 01a3ce06cf..cbdda42cf1 100644 --- a/cryptomator/src/main/java/ch/cyberduck/core/cryptomator/features/CryptoDeleteV6Feature.java +++ b/cryptomator/src/main/java/ch/cyberduck/core/cryptomator/features/CryptoDeleteV6Feature.java @@ -93,7 +93,7 @@ public class CryptoDeleteV6Feature implements Delete, Trash { if(f.equals(vault.getHome())) { log.warn("Recursively delete vault {}", f); final List metadata = new ArrayList<>(); - if(!proxy.isRecursive()) { + if(!proxy.features(f).contains(Delete.Flags.recursive)) { final Find find = session._getFeature(Find.class); final Path dataRoot = new Path(f, "d", f.getType()); if(find.find(dataRoot)) { @@ -128,8 +128,8 @@ public class CryptoDeleteV6Feature implements Delete, Trash { } @Override - public EnumSet features() { - return proxy.features(); + public EnumSet features(final Path file) { + return proxy.features(file); } @Override diff --git a/cryptomator/src/main/java/ch/cyberduck/core/cryptomator/features/CryptoDeleteV7Feature.java b/cryptomator/src/main/java/ch/cyberduck/core/cryptomator/features/CryptoDeleteV7Feature.java index 8d8570ef47..ab1d6b34b7 100644 --- a/cryptomator/src/main/java/ch/cyberduck/core/cryptomator/features/CryptoDeleteV7Feature.java +++ b/cryptomator/src/main/java/ch/cyberduck/core/cryptomator/features/CryptoDeleteV7Feature.java @@ -114,7 +114,7 @@ public class CryptoDeleteV7Feature implements Delete, Trash { if(f.equals(vault.getHome())) { log.warn("Recursively delete vault {}", f); final List metadata = new ArrayList<>(); - if(!proxy.isRecursive()) { + if(!proxy.features(f).contains(Delete.Flags.recursive)) { final Find find = session._getFeature(Find.class); final Path dataRoot = new Path(f, "d", f.getType()); if(find.find(dataRoot)) { @@ -143,8 +143,8 @@ public class CryptoDeleteV7Feature implements Delete, Trash { } @Override - public EnumSet features() { - return proxy.features(); + public EnumSet features(final Path file) { + return proxy.features(file); } @Override diff --git a/cryptomator/src/main/java/ch/cyberduck/core/cryptomator/features/CryptoDirectoryV6Feature.java b/cryptomator/src/main/java/ch/cyberduck/core/cryptomator/features/CryptoDirectoryV6Feature.java index 1e9e7866a8..1f92916e92 100644 --- a/cryptomator/src/main/java/ch/cyberduck/core/cryptomator/features/CryptoDirectoryV6Feature.java +++ b/cryptomator/src/main/java/ch/cyberduck/core/cryptomator/features/CryptoDirectoryV6Feature.java @@ -38,21 +38,18 @@ public class CryptoDirectoryV6Feature implements Directory { private static final Logger log = LogManager.getLogger(CryptoDirectoryV6Feature.class); private final Session session; - private final Write writer; private final Directory delegate; private final CryptoVault vault; private final RandomStringService random = new UUIDRandomStringService(); - public CryptoDirectoryV6Feature(final Session session, final Directory delegate, - final Write writer, final CryptoVault cryptomator) { + public CryptoDirectoryV6Feature(final Session session, final Directory delegate, final CryptoVault cryptomator) { this.session = session; - this.writer = writer; this.delegate = delegate; this.vault = cryptomator; } @Override - public Path mkdir(final Path folder, final TransferStatus status) throws BackgroundException { + public Path mkdir(final Write writer, final Path folder, final TransferStatus status) throws BackgroundException { final Path encrypt = vault.encrypt(session, folder, random.random(), false); final String directoryId = encrypt.attributes().getDirectoryId(); // Create metadata file for directory @@ -61,13 +58,13 @@ public class CryptoDirectoryV6Feature implements Directory { new ContentWriter(session).write(directoryMetadataFile, directoryId.getBytes(StandardCharsets.UTF_8)); final Path intermediate = encrypt.getParent(); if(!session._getFeature(Find.class).find(intermediate)) { - session._getFeature(Directory.class).mkdir(intermediate, new TransferStatus().setRegion(status.getRegion())); + session._getFeature(Directory.class).mkdir(session._getFeature(Write.class), intermediate, new TransferStatus().setRegion(status.getRegion())); } // Write header final FileHeader header = vault.getFileHeaderCryptor().create(); status.setHeader(vault.getFileHeaderCryptor().encryptHeader(header)); status.setNonces(new RandomNonceGenerator(vault.getNonceSize())); - final Path target = delegate.withWriter(new CryptoWriteFeature<>(session, writer, vault)).mkdir(encrypt, status); + final Path target = delegate.mkdir(writer, encrypt, status); // Implementation may return new copy of attributes without encryption attributes target.attributes().setDirectoryId(directoryId); target.attributes().setDecrypted(folder); diff --git a/cryptomator/src/main/java/ch/cyberduck/core/cryptomator/features/CryptoDirectoryV7Feature.java b/cryptomator/src/main/java/ch/cyberduck/core/cryptomator/features/CryptoDirectoryV7Feature.java index fb08437293..b32f8eb773 100644 --- a/cryptomator/src/main/java/ch/cyberduck/core/cryptomator/features/CryptoDirectoryV7Feature.java +++ b/cryptomator/src/main/java/ch/cyberduck/core/cryptomator/features/CryptoDirectoryV7Feature.java @@ -40,25 +40,23 @@ public class CryptoDirectoryV7Feature implements Directory { private static final Logger log = LogManager.getLogger(CryptoDirectoryV7Feature.class); private final Session session; - private final Write writer; private final Directory delegate; private final CryptoVault vault; private final RandomStringService random = new UUIDRandomStringService(); - public CryptoDirectoryV7Feature(final Session session, final Directory delegate, - final Write writer, final CryptoVault cryptomator) { + public CryptoDirectoryV7Feature(final Session session, final Directory delegate, final CryptoVault cryptomator) { this.session = session; - this.writer = writer; this.delegate = delegate; this.vault = cryptomator; } @Override - public Path mkdir(final Path folder, final TransferStatus status) throws BackgroundException { + public Path mkdir(final Write writer, final Path folder, final TransferStatus status) throws BackgroundException { final Path encrypt = vault.encrypt(session, folder, random.random(), false); final String directoryId = encrypt.attributes().getDirectoryId(); // Create metadata file for directory - final Path directoryMetadataFolder = session._getFeature(Directory.class).mkdir(vault.encrypt(session, folder, true), + final Path directoryMetadataFolder = session._getFeature(Directory.class).mkdir( + session._getFeature(Write.class), vault.encrypt(session, folder, true), new TransferStatus().setRegion(status.getRegion())); final Path directoryMetadataFile = new Path(directoryMetadataFolder, CryptoDirectoryV7Provider.DIRECTORY_METADATAFILE, @@ -67,13 +65,13 @@ public class CryptoDirectoryV7Feature implements Directory { new ContentWriter(session).write(directoryMetadataFile, directoryId.getBytes(StandardCharsets.UTF_8)); final Path intermediate = encrypt.getParent(); if(!session._getFeature(Find.class).find(intermediate)) { - session._getFeature(Directory.class).mkdir(intermediate, new TransferStatus().setRegion(status.getRegion())); + session._getFeature(Directory.class).mkdir(session._getFeature(Write.class), intermediate, new TransferStatus().setRegion(status.getRegion())); } // Write header final FileHeader header = vault.getFileHeaderCryptor().create(); status.setHeader(vault.getFileHeaderCryptor().encryptHeader(header)); status.setNonces(new RandomNonceGenerator(vault.getNonceSize())); - final Path target = delegate.withWriter(new CryptoWriteFeature<>(session, writer, vault)).mkdir(encrypt, status); + final Path target = delegate.mkdir(writer, encrypt, status); // Implementation may return new copy of attributes without encryption attributes target.attributes().setDirectoryId(directoryId); target.attributes().setDecrypted(folder); diff --git a/cryptomator/src/main/java/ch/cyberduck/core/cryptomator/features/CryptoDownloadFeature.java b/cryptomator/src/main/java/ch/cyberduck/core/cryptomator/features/CryptoDownloadFeature.java index dc803e4c91..e3dceec4da 100644 --- a/cryptomator/src/main/java/ch/cyberduck/core/cryptomator/features/CryptoDownloadFeature.java +++ b/cryptomator/src/main/java/ch/cyberduck/core/cryptomator/features/CryptoDownloadFeature.java @@ -35,15 +35,15 @@ public class CryptoDownloadFeature implements Download { private final Download proxy; private final Vault vault; - public CryptoDownloadFeature(final Session session, final Download proxy, final Read reader, final CryptoVault vault) { + public CryptoDownloadFeature(final Session session, final Download proxy, final CryptoVault vault) { this.session = session; - this.proxy = proxy.withReader(new CryptoReadFeature(session, reader, vault)); + this.proxy = proxy; this.vault = vault; } @Override - public void download(final Path file, final Local local, final BandwidthThrottle throttle, final StreamListener listener, final TransferStatus status, final ConnectionCallback callback) throws BackgroundException { - proxy.download(vault.encrypt(session, file), local, throttle, listener, status, callback); + public void download(final Read read, final Path file, final Local local, final BandwidthThrottle throttle, final StreamListener listener, final TransferStatus status, final ConnectionCallback callback) throws BackgroundException { + proxy.download(read, vault.encrypt(session, file), local, throttle, listener, status, callback); } @Override @@ -56,11 +56,6 @@ public class CryptoDownloadFeature implements Download { } } - @Override - public Download withReader(final Read reader) { - return this; - } - @Override public String toString() { final StringBuilder sb = new StringBuilder("CryptoDownloadFeature{"); diff --git a/cryptomator/src/main/java/ch/cyberduck/core/cryptomator/features/CryptoHeadersFeature.java b/cryptomator/src/main/java/ch/cyberduck/core/cryptomator/features/CryptoHeadersFeature.java index 28122eeb00..f1be01f03a 100644 --- a/cryptomator/src/main/java/ch/cyberduck/core/cryptomator/features/CryptoHeadersFeature.java +++ b/cryptomator/src/main/java/ch/cyberduck/core/cryptomator/features/CryptoHeadersFeature.java @@ -37,8 +37,8 @@ public class CryptoHeadersFeature implements Headers { } @Override - public Map getDefault() { - return delegate.getDefault(); + public Map getDefault(final Path file) { + return delegate.getDefault(file); } @Override diff --git a/cryptomator/src/main/java/ch/cyberduck/core/cryptomator/features/CryptoLocationFeature.java b/cryptomator/src/main/java/ch/cyberduck/core/cryptomator/features/CryptoLocationFeature.java index fe61e581da..760ee17841 100644 --- a/cryptomator/src/main/java/ch/cyberduck/core/cryptomator/features/CryptoLocationFeature.java +++ b/cryptomator/src/main/java/ch/cyberduck/core/cryptomator/features/CryptoLocationFeature.java @@ -36,13 +36,13 @@ public class CryptoLocationFeature implements Location { } @Override - public Name getDefault() { - return delegate.getDefault(); + public Name getDefault(final Path file) { + return delegate.getDefault(file); } @Override - public Set getLocations() { - return delegate.getLocations(); + public Set getLocations(final Path file) { + return delegate.getLocations(file); } @Override diff --git a/cryptomator/src/main/java/ch/cyberduck/core/cryptomator/features/CryptoRedundancyFeature.java b/cryptomator/src/main/java/ch/cyberduck/core/cryptomator/features/CryptoRedundancyFeature.java index 73d2206be7..ddb2df6bec 100644 --- a/cryptomator/src/main/java/ch/cyberduck/core/cryptomator/features/CryptoRedundancyFeature.java +++ b/cryptomator/src/main/java/ch/cyberduck/core/cryptomator/features/CryptoRedundancyFeature.java @@ -36,13 +36,13 @@ public class CryptoRedundancyFeature implements Redundancy { } @Override - public String getDefault() { - return delegate.getDefault(); + public String getDefault(final Path file) { + return delegate.getDefault(file); } @Override - public Set getClasses() { - return delegate.getClasses(); + public Set getClasses(final Path file) { + return delegate.getClasses(file); } @Override diff --git a/cryptomator/src/main/java/ch/cyberduck/core/cryptomator/features/CryptoSearchFeature.java b/cryptomator/src/main/java/ch/cyberduck/core/cryptomator/features/CryptoSearchFeature.java index 39fbbde53c..a20c9a8fe7 100644 --- a/cryptomator/src/main/java/ch/cyberduck/core/cryptomator/features/CryptoSearchFeature.java +++ b/cryptomator/src/main/java/ch/cyberduck/core/cryptomator/features/CryptoSearchFeature.java @@ -44,8 +44,8 @@ public class CryptoSearchFeature implements Search { } @Override - public EnumSet features() { - return delegate.features(); + public EnumSet features(final Path workdir) { + return delegate.features(workdir); } @Override diff --git a/cryptomator/src/main/java/ch/cyberduck/core/cryptomator/features/CryptoTouchFeature.java b/cryptomator/src/main/java/ch/cyberduck/core/cryptomator/features/CryptoTouchFeature.java index b4abd64f12..592f036709 100644 --- a/cryptomator/src/main/java/ch/cyberduck/core/cryptomator/features/CryptoTouchFeature.java +++ b/cryptomator/src/main/java/ch/cyberduck/core/cryptomator/features/CryptoTouchFeature.java @@ -15,6 +15,7 @@ package ch.cyberduck.core.cryptomator.features; * GNU General Public License for more details. */ +import ch.cyberduck.core.DefaultPathAttributes; import ch.cyberduck.core.LocaleFactory; import ch.cyberduck.core.Path; import ch.cyberduck.core.PathAttributes; @@ -37,24 +38,24 @@ public class CryptoTouchFeature implements Touch { private final Touch proxy; private final CryptoVault vault; - public CryptoTouchFeature(final Session session, final Touch proxy, final Write writer, final CryptoVault cryptomator) { + public CryptoTouchFeature(final Session session, final Touch proxy, final CryptoVault cryptomator) { this.session = session; - this.proxy = proxy.withWriter(new CryptoWriteFeature<>(session, writer, cryptomator)); + this.proxy = proxy; this.vault = cryptomator; } @Override - public Path touch(final Path file, final TransferStatus status) throws BackgroundException { + public Path touch(final Write writer, final Path file, final TransferStatus status) throws BackgroundException { // Write header final FileHeader header = vault.getFileHeaderCryptor().create(); status.setHeader(vault.getFileHeaderCryptor().encryptHeader(header)); status.setNonces(new RandomNonceGenerator(vault.getNonceSize())); - final Path target = proxy.touch(vault.encrypt(session, file), new TransferStatus(status) { + final Path target = proxy.touch(writer, vault.encrypt(session, file), new TransferStatus(status) { @Override public TransferStatus setResponse(final PathAttributes attributes) { status.setResponse(attributes); // Will be converted back to clear text when decrypting file below set in default touch feature implementation using writer. - super.setResponse(new PathAttributes(attributes).setSize(vault.toCiphertextSize(0L, attributes.getSize()))); + super.setResponse(new DefaultPathAttributes(attributes).setSize(vault.toCiphertextSize(0L, attributes.getSize()))); return this; } }); diff --git a/cryptomator/src/main/java/ch/cyberduck/core/cryptomator/features/CryptoUnixPermission.java b/cryptomator/src/main/java/ch/cyberduck/core/cryptomator/features/CryptoUnixPermission.java index 1a86c0711c..ef92fce3c1 100644 --- a/cryptomator/src/main/java/ch/cyberduck/core/cryptomator/features/CryptoUnixPermission.java +++ b/cryptomator/src/main/java/ch/cyberduck/core/cryptomator/features/CryptoUnixPermission.java @@ -43,8 +43,8 @@ public class CryptoUnixPermission implements UnixPermission { } @Override - public Permission getDefault(final EnumSet type) { - return delegate.getDefault(type); + public Permission getDefault(final Path workdir, final EnumSet type) { + return delegate.getDefault(workdir, type); } @Override diff --git a/cryptomator/src/main/java/ch/cyberduck/core/cryptomator/features/CryptoUploadFeature.java b/cryptomator/src/main/java/ch/cyberduck/core/cryptomator/features/CryptoUploadFeature.java index 848f8e26fe..2f2c131733 100644 --- a/cryptomator/src/main/java/ch/cyberduck/core/cryptomator/features/CryptoUploadFeature.java +++ b/cryptomator/src/main/java/ch/cyberduck/core/cryptomator/features/CryptoUploadFeature.java @@ -35,15 +35,15 @@ public class CryptoUploadFeature implements Upload { private final Upload proxy; private final CryptoVault vault; - public CryptoUploadFeature(final Session session, final Upload delegate, final Write writer, final CryptoVault vault) { + public CryptoUploadFeature(final Session session, final Upload delegate, final CryptoVault vault) { this.session = session; - this.proxy = delegate.withWriter(new CryptoWriteFeature<>(session, writer, vault)); + this.proxy = delegate; this.vault = vault; } @Override - public Reply upload(final Path file, final Local local, final BandwidthThrottle throttle, final ProgressListener progress, final StreamListener streamListener, final TransferStatus status, final ConnectionCallback callback) throws BackgroundException { - return proxy.upload(vault.encrypt(session, file), local, throttle, progress, streamListener, status.setDestinationLength(new CryptoTransferStatus(vault, status).getLength()), callback); + public Reply upload(final Write write, final Path file, final Local local, final BandwidthThrottle throttle, final ProgressListener progress, final StreamListener streamListener, final TransferStatus status, final ConnectionCallback callback) throws BackgroundException { + return proxy.upload(write, vault.encrypt(session, file), local, throttle, progress, streamListener, status.setDestinationLength(new CryptoTransferStatus(vault, status).getLength()), callback); } @Override @@ -51,11 +51,6 @@ public class CryptoUploadFeature implements Upload { return proxy.append(vault.encrypt(session, file), status.setDestinationLength(new CryptoTransferStatus(vault, status).getLength())); } - @Override - public Upload withWriter(final Write writer) { - return this; - } - @Override public String toString() { final StringBuilder sb = new StringBuilder("CryptoUploadFeature{"); diff --git a/cryptomator/src/main/java/ch/cyberduck/core/cryptomator/features/CryptoUrlProvider.java b/cryptomator/src/main/java/ch/cyberduck/core/cryptomator/features/CryptoUrlProvider.java index 0d58732488..f62302a68f 100644 --- a/cryptomator/src/main/java/ch/cyberduck/core/cryptomator/features/CryptoUrlProvider.java +++ b/cryptomator/src/main/java/ch/cyberduck/core/cryptomator/features/CryptoUrlProvider.java @@ -34,7 +34,7 @@ import java.text.MessageFormat; import java.util.EnumSet; public class CryptoUrlProvider implements UrlProvider { - private static final Logger log = LogManager.getLogger(DecryptingListProgressListener.class); + private static final Logger log = LogManager.getLogger(CryptoUrlProvider.class); private final Session session; private final UrlProvider delegate; diff --git a/cryptomator/src/main/java/ch/cyberduck/core/cryptomator/impl/CryptoDirectoryV6Provider.java b/cryptomator/src/main/java/ch/cyberduck/core/cryptomator/impl/CryptoDirectoryV6Provider.java index bf7222072a..519f4f4bcf 100644 --- a/cryptomator/src/main/java/ch/cyberduck/core/cryptomator/impl/CryptoDirectoryV6Provider.java +++ b/cryptomator/src/main/java/ch/cyberduck/core/cryptomator/impl/CryptoDirectoryV6Provider.java @@ -16,6 +16,7 @@ package ch.cyberduck.core.cryptomator.impl; */ import ch.cyberduck.core.CacheReference; +import ch.cyberduck.core.DefaultPathAttributes; import ch.cyberduck.core.Path; import ch.cyberduck.core.PathAttributes; import ch.cyberduck.core.RandomStringService; @@ -37,8 +38,8 @@ import org.apache.logging.log4j.Logger; import java.nio.charset.StandardCharsets; import java.util.EnumSet; -import java.util.concurrent.locks.Lock; -import java.util.concurrent.locks.ReentrantLock; +import java.util.concurrent.locks.ReadWriteLock; +import java.util.concurrent.locks.ReentrantReadWriteLock; public class CryptoDirectoryV6Provider implements CryptoDirectory { private static final Logger log = LogManager.getLogger(CryptoDirectoryV6Provider.class); @@ -51,12 +52,12 @@ public class CryptoDirectoryV6Provider implements CryptoDirectory { private final CryptoVault cryptomator; private final RandomStringService random - = new UUIDRandomStringService(); + = new UUIDRandomStringService(); - private final Lock lock = new ReentrantLock(); + private final ReadWriteLock lock = new ReentrantReadWriteLock(); private final LRUCache, String> cache = LRUCache.build( - PreferencesFactory.get().getInteger("cryptomator.cache.size")); + PreferencesFactory.get().getInteger("cryptomator.cache.size")); public CryptoDirectoryV6Provider(final Path vault, final CryptoVault cryptomator) { this.home = vault; @@ -78,7 +79,7 @@ public class CryptoDirectoryV6Provider implements CryptoDirectory { throw new NotfoundException(directory.getAbsolute()); } if(new SimplePathPredicate(directory).test(home) || directory.isChild(home)) { - final PathAttributes attributes = new PathAttributes(directory.attributes()); + final PathAttributes attributes = new DefaultPathAttributes(directory.attributes()); // The root of the vault is a different target directory and file ids always correspond to the metadata file attributes.setVersionId(null); attributes.setFileId(null); @@ -103,31 +104,31 @@ public class CryptoDirectoryV6Provider implements CryptoDirectory { if(new SimplePathPredicate(home).test(directory)) { return ROOT_DIR_ID; } - if(StringUtils.isBlank(directoryId)) { + try { + lock.readLock().lock(); if(cache.contains(new SimplePathPredicate(directory))) { - return cache.get(new SimplePathPredicate(directory)); - } - try { - log.debug("Acquire lock for {}", directory); - lock.lock(); - final String id = this.load(session, directory); - cache.put(new SimplePathPredicate(directory), id); - return id; - } - finally { - lock.unlock(); + final String existing = cache.get(new SimplePathPredicate(directory)); + if(StringUtils.isNotBlank(directoryId)) { + if(!existing.equals(directoryId)) { + log.warn("Do not override already cached id {} with {}", existing, directoryId); + } + } + return existing; } } - if(!cache.contains(new SimplePathPredicate(directory))) { - cache.put(new SimplePathPredicate(directory), directoryId); + finally { + lock.readLock().unlock(); } - else { - final String existing = cache.get(new SimplePathPredicate(directory)); - if(!existing.equals(directoryId)) { - log.warn("Do not override already cached id {} with {}", existing, directoryId); - } + try { + log.debug("Acquire lock for {}", directory); + lock.writeLock().lock(); + final String id = StringUtils.isBlank(directoryId) ? this.load(session, directory) : directoryId; + cache.put(new SimplePathPredicate(directory), id); + return id; + } + finally { + lock.writeLock().unlock(); } - return cache.get(new SimplePathPredicate(directory)); } protected String load(final Session session, final Path directory) throws BackgroundException { @@ -147,11 +148,23 @@ public class CryptoDirectoryV6Provider implements CryptoDirectory { } public void delete(final Path directory) { - cache.remove(new SimplePathPredicate(directory)); + try { + lock.writeLock().lock(); + cache.remove(new SimplePathPredicate(directory)); + } + finally { + lock.writeLock().unlock(); + } } @Override public void destroy() { - cache.clear(); + try { + lock.writeLock().lock(); + cache.clear(); + } + finally { + lock.writeLock().unlock(); + } } } diff --git a/cryptomator/src/main/java/ch/cyberduck/core/cryptomator/impl/CryptoFilenameV6Provider.java b/cryptomator/src/main/java/ch/cyberduck/core/cryptomator/impl/CryptoFilenameV6Provider.java index 61ffb8488a..6cb8efa916 100644 --- a/cryptomator/src/main/java/ch/cyberduck/core/cryptomator/impl/CryptoFilenameV6Provider.java +++ b/cryptomator/src/main/java/ch/cyberduck/core/cryptomator/impl/CryptoFilenameV6Provider.java @@ -24,6 +24,7 @@ import ch.cyberduck.core.cryptomator.CryptoFilename; import ch.cyberduck.core.exception.BackgroundException; import ch.cyberduck.core.features.Directory; import ch.cyberduck.core.features.Find; +import ch.cyberduck.core.features.Write; import ch.cyberduck.core.preferences.PreferencesFactory; import ch.cyberduck.core.transfer.TransferStatus; @@ -93,13 +94,13 @@ public class CryptoFilenameV6Provider implements CryptoFilename { final Directory mkdir = session._getFeature(Directory.class); final Find find = session._getFeature(Find.class); if(!find.find(metadataRoot)) { - mkdir.mkdir(metadataRoot, new TransferStatus()); + mkdir.mkdir(session._getFeature(Write.class), metadataRoot, new TransferStatus()); } if(!find.find(firstLevel)) { - mkdir.mkdir(firstLevel, new TransferStatus()); + mkdir.mkdir(session._getFeature(Write.class), firstLevel, new TransferStatus()); } if(!find.find(secondLevel)) { - mkdir.mkdir(secondLevel, new TransferStatus()); + mkdir.mkdir(session._getFeature(Write.class), secondLevel, new TransferStatus()); } if(!find.find(metadataFile)) { new ContentWriter(session).write(metadataFile, longFileNameBytes); diff --git a/cryptomator/src/main/java/ch/cyberduck/core/cryptomator/impl/CryptoFilenameV7Provider.java b/cryptomator/src/main/java/ch/cyberduck/core/cryptomator/impl/CryptoFilenameV7Provider.java index ca1f6faebc..91e9172454 100644 --- a/cryptomator/src/main/java/ch/cyberduck/core/cryptomator/impl/CryptoFilenameV7Provider.java +++ b/cryptomator/src/main/java/ch/cyberduck/core/cryptomator/impl/CryptoFilenameV7Provider.java @@ -54,7 +54,8 @@ public class CryptoFilenameV7Provider implements CryptoFilename { if(this.isBelowThreshold(filename)) { return filename; } - throw new CryptoInvalidFilenameException(String.format("Filename length %d exceeds maximum length %d", filename.length(), DEFAULT_NAME_SHORTENING_THRESHOLD)); + throw new CryptoInvalidFilenameException(String.format("Filename %s with length %d exceeds maximum of %d characters", + filename, filename.length(), DEFAULT_NAME_SHORTENING_THRESHOLD)); } @Override diff --git a/cryptomator/src/test/java/ch/cyberduck/core/cryptomator/CryptoChecksumComputeTest.java b/cryptomator/src/test/java/ch/cyberduck/core/cryptomator/CryptoChecksumComputeTest.java index 1a3aec1cc7..a539352f90 100644 --- a/cryptomator/src/test/java/ch/cyberduck/core/cryptomator/CryptoChecksumComputeTest.java +++ b/cryptomator/src/test/java/ch/cyberduck/core/cryptomator/CryptoChecksumComputeTest.java @@ -22,6 +22,7 @@ import ch.cyberduck.core.TestProtocol; import ch.cyberduck.core.cryptomator.features.CryptoChecksumCompute; import ch.cyberduck.core.cryptomator.random.RandomNonceGenerator; import ch.cyberduck.core.features.Directory; +import ch.cyberduck.core.features.Write; import ch.cyberduck.core.io.SHA256ChecksumCompute; import ch.cyberduck.core.transfer.TransferStatus; import ch.cyberduck.core.vault.VaultCredentials; @@ -47,7 +48,7 @@ public class CryptoChecksumComputeTest { return (T) new Directory() { @Override - public Path mkdir(final Path folder, final TransferStatus status) { + public Path mkdir(final Write writer, final Path folder, final TransferStatus status) { assertTrue(folder.equals(vault) || folder.isChild(vault)); return folder; } diff --git a/cryptomator/src/test/java/ch/cyberduck/core/cryptomator/CryptoOutputStreamTest.java b/cryptomator/src/test/java/ch/cyberduck/core/cryptomator/CryptoOutputStreamTest.java index 40c16fe647..eff5c2e279 100644 --- a/cryptomator/src/test/java/ch/cyberduck/core/cryptomator/CryptoOutputStreamTest.java +++ b/cryptomator/src/test/java/ch/cyberduck/core/cryptomator/CryptoOutputStreamTest.java @@ -21,6 +21,7 @@ import ch.cyberduck.core.Path; import ch.cyberduck.core.TestProtocol; import ch.cyberduck.core.cryptomator.random.RandomNonceGenerator; import ch.cyberduck.core.features.Directory; +import ch.cyberduck.core.features.Write; import ch.cyberduck.core.transfer.TransferStatus; import ch.cyberduck.core.vault.VaultCredentials; @@ -49,7 +50,7 @@ public class CryptoOutputStreamTest { return (T) new Directory() { @Override - public Path mkdir(final Path folder, final TransferStatus status) { + public Path mkdir(final Write writer, final Path folder, final TransferStatus status) { assertTrue(folder.equals(home) || folder.isChild(home)); return folder; } diff --git a/cryptomator/src/test/java/ch/cyberduck/core/cryptomator/CryptoVaultTest.java b/cryptomator/src/test/java/ch/cyberduck/core/cryptomator/CryptoVaultTest.java index 4bcfc9c8d1..0a9361a6de 100644 --- a/cryptomator/src/test/java/ch/cyberduck/core/cryptomator/CryptoVaultTest.java +++ b/cryptomator/src/test/java/ch/cyberduck/core/cryptomator/CryptoVaultTest.java @@ -33,6 +33,7 @@ import ch.cyberduck.core.exception.NotfoundException; import ch.cyberduck.core.features.Directory; import ch.cyberduck.core.features.Read; import ch.cyberduck.core.features.Vault; +import ch.cyberduck.core.features.Write; import ch.cyberduck.core.preferences.PreferencesFactory; import ch.cyberduck.core.serializer.PathDictionary; import ch.cyberduck.core.transfer.TransferStatus; @@ -419,7 +420,7 @@ public class CryptoVaultTest { return (T) new Directory() { @Override - public Path mkdir(final Path folder, final TransferStatus status) { + public Path mkdir(final Write writer, final Path folder, final TransferStatus status) { assertTrue(folder.equals(home) || folder.isChild(home)); return folder; } @@ -443,7 +444,7 @@ public class CryptoVaultTest { return (T) new Directory() { @Override - public Path mkdir(final Path folder, final TransferStatus status) { + public Path mkdir(final Write writer, final Path folder, final TransferStatus status) { assertTrue(folder.equals(home) || folder.isChild(home)); return folder; } @@ -488,7 +489,7 @@ public class CryptoVaultTest { return (T) new Directory() { @Override - public Path mkdir(final Path folder, final TransferStatus status) { + public Path mkdir(final Write writer, final Path folder, final TransferStatus status) { assertTrue(folder.equals(home) || folder.isChild(home)); return folder; } @@ -533,7 +534,7 @@ public class CryptoVaultTest { return (T) new Directory() { @Override - public Path mkdir(final Path folder, final TransferStatus status) { + public Path mkdir(final Write writer, final Path folder, final TransferStatus status) { assertTrue(folder.equals(home) || folder.isChild(home)); return folder; } diff --git a/cryptomator/src/test/java/ch/cyberduck/core/cryptomator/CryptoWriteFeatureTest.java b/cryptomator/src/test/java/ch/cyberduck/core/cryptomator/CryptoWriteFeatureTest.java index c27c6df290..ecacf7650c 100644 --- a/cryptomator/src/test/java/ch/cyberduck/core/cryptomator/CryptoWriteFeatureTest.java +++ b/cryptomator/src/test/java/ch/cyberduck/core/cryptomator/CryptoWriteFeatureTest.java @@ -20,6 +20,7 @@ import ch.cyberduck.core.NullSession; import ch.cyberduck.core.Path; import ch.cyberduck.core.TestProtocol; import ch.cyberduck.core.features.Directory; +import ch.cyberduck.core.features.Write; import ch.cyberduck.core.transfer.TransferStatus; import ch.cyberduck.core.vault.VaultCredentials; @@ -43,7 +44,7 @@ public class CryptoWriteFeatureTest { return (T) new Directory() { @Override - public Path mkdir(final Path folder, final TransferStatus status) { + public Path mkdir(final Write writer, final Path folder, final TransferStatus status) { assertTrue(folder.equals(home) || folder.isChild(home)); return folder; } @@ -80,7 +81,7 @@ public class CryptoWriteFeatureTest { return (T) new Directory() { @Override - public Path mkdir(final Path folder, final TransferStatus status) { + public Path mkdir(final Write writer, final Path folder, final TransferStatus status) { assertTrue(folder.equals(home) || folder.isChild(home)); return folder; } diff --git a/cryptomator/src/test/java/ch/cyberduck/core/cryptomator/features/CryptoBulkFeatureTest.java b/cryptomator/src/test/java/ch/cyberduck/core/cryptomator/features/CryptoBulkFeatureTest.java index 68d6d0d474..2bde499b60 100644 --- a/cryptomator/src/test/java/ch/cyberduck/core/cryptomator/features/CryptoBulkFeatureTest.java +++ b/cryptomator/src/test/java/ch/cyberduck/core/cryptomator/features/CryptoBulkFeatureTest.java @@ -20,13 +20,12 @@ import ch.cyberduck.core.DisabledConnectionCallback; import ch.cyberduck.core.Host; import ch.cyberduck.core.Local; import ch.cyberduck.core.NullSession; -import ch.cyberduck.core.PasswordCallback; import ch.cyberduck.core.Path; import ch.cyberduck.core.TestProtocol; import ch.cyberduck.core.cryptomator.CryptoVault; import ch.cyberduck.core.features.Bulk; -import ch.cyberduck.core.features.Delete; import ch.cyberduck.core.features.Directory; +import ch.cyberduck.core.features.Write; import ch.cyberduck.core.transfer.Transfer; import ch.cyberduck.core.transfer.TransferItem; import ch.cyberduck.core.transfer.TransferStatus; @@ -55,7 +54,7 @@ public class CryptoBulkFeatureTest { if(type == Directory.class) { return (T) new Directory() { @Override - public Path mkdir(final Path folder, final TransferStatus status) { + public Path mkdir(final Write writer, final Path folder, final TransferStatus status) { return folder; } }; @@ -75,22 +74,6 @@ public class CryptoBulkFeatureTest { public void post(final Transfer.Type type, final Map files, final ConnectionCallback callback) { // } - - @Override - public Bulk> withDelete(final Delete delete) { - return this; - } - - }, new Delete() { - @Override - public void delete(final Map files, final PasswordCallback prompt, final Callback callback) { - throw new UnsupportedOperationException(); - } - - @Override - public boolean isRecursive() { - throw new UnsupportedOperationException(); - } }, cryptomator); final HashMap files = new HashMap<>(); final Path directory = new Path("/vault/directory", EnumSet.of(Path.Type.directory)); @@ -128,7 +111,7 @@ public class CryptoBulkFeatureTest { if(type == Directory.class) { return (T) new Directory() { @Override - public Path mkdir(final Path folder, final TransferStatus status) { + public Path mkdir(final Write writer, final Path folder, final TransferStatus status) { return folder; } }; @@ -148,22 +131,6 @@ public class CryptoBulkFeatureTest { public void post(final Transfer.Type type, final Map files, final ConnectionCallback callback) { // } - - @Override - public Bulk> withDelete(final Delete delete) { - return this; - } - - }, new Delete() { - @Override - public void delete(final Map files, final PasswordCallback prompt, final Callback callback) { - throw new UnsupportedOperationException(); - } - - @Override - public boolean isRecursive() { - throw new UnsupportedOperationException(); - } }, cryptomator); final HashMap files = new HashMap<>(); final Path directory = new Path("/vault/directory", EnumSet.of(Path.Type.directory)); diff --git a/ctera/pom.xml b/ctera/pom.xml index f2620ae35e..8da69ba1bc 100644 --- a/ctera/pom.xml +++ b/ctera/pom.xml @@ -18,7 +18,7 @@ ch.cyberduck parent - 9.2.0-SNAPSHOT + 9.4.0-SNAPSHOT ctera jar diff --git a/ctera/src/main/java/ch/cyberduck/core/ctera/CteraAttributesFinderFeature.java b/ctera/src/main/java/ch/cyberduck/core/ctera/CteraAttributesFinderFeature.java index 1e17527477..2b7b61d4bd 100644 --- a/ctera/src/main/java/ch/cyberduck/core/ctera/CteraAttributesFinderFeature.java +++ b/ctera/src/main/java/ch/cyberduck/core/ctera/CteraAttributesFinderFeature.java @@ -166,7 +166,13 @@ public class CteraAttributesFinderFeature extends DAVAttributesFinderFeature { final Acl acl = toAcl(customProps); final PathAttributes attributes = super.toAttributes(resource); if(customProps.containsKey(CTERA_GUID)) { - attributes.setFileId(customProps.get(CTERA_GUID)); + final String guid = customProps.get(CTERA_GUID); + if("null:0".equals(guid)) { + log.warn("Skip setting GUID for {}", guid); + } + else { + attributes.setFileId(guid); + } } if(customProps.containsKey(CTERA_FILEID)) { attributes.setVersionId(customProps.get(CTERA_FILEID)); diff --git a/ctera/src/main/java/ch/cyberduck/core/ctera/CteraAuthenticationHandler.java b/ctera/src/main/java/ch/cyberduck/core/ctera/CteraAuthenticationHandler.java index 4a5ea8dde1..ec9d726ef7 100644 --- a/ctera/src/main/java/ch/cyberduck/core/ctera/CteraAuthenticationHandler.java +++ b/ctera/src/main/java/ch/cyberduck/core/ctera/CteraAuthenticationHandler.java @@ -150,13 +150,13 @@ public class CteraAuthenticationHandler implements ServiceUnavailableRetryStrate * * @return Same tokens saved */ - public CteraTokens save(final CteraTokens tokens) throws LocalAccessDeniedException { + public CteraTokens save(final CteraTokens tokens) throws AccessDeniedException { log.debug("Save new tokens {} for {}", tokens, host); final Credentials credentials = host.getCredentials(); credentials - .withToken(String.format("%s:%s", tokens.getDeviceId(), tokens.getSharedSecret())) - .withPassword(password) - .withSaved(new LoginOptions().save); + .setToken(String.format("%s:%s", tokens.getDeviceId(), tokens.getSharedSecret())) + .setPassword(password) + .setSaved(new LoginOptions().save); if(credentials.isSaved()) { store.save(host); } diff --git a/ctera/src/main/java/ch/cyberduck/core/ctera/CteraBulkFeature.java b/ctera/src/main/java/ch/cyberduck/core/ctera/CteraBulkFeature.java index 766b311f92..ff1648d0de 100644 --- a/ctera/src/main/java/ch/cyberduck/core/ctera/CteraBulkFeature.java +++ b/ctera/src/main/java/ch/cyberduck/core/ctera/CteraBulkFeature.java @@ -20,7 +20,6 @@ import ch.cyberduck.core.HostUrlProvider; import ch.cyberduck.core.Path; import ch.cyberduck.core.ctera.model.DirectIO; import ch.cyberduck.core.exception.BackgroundException; -import ch.cyberduck.core.exception.TransferCanceledException; import ch.cyberduck.core.features.VersionIdProvider; import ch.cyberduck.core.shared.DisabledBulkFeature; import ch.cyberduck.core.transfer.Transfer; @@ -80,19 +79,20 @@ public class CteraBulkFeature extends DisabledBulkFeature { } segment.setUrl(metadata.chunks.get(i).url); final Map parameters = new HashMap<>(segment.getParameters()); - parameters.put("wrapped_key", metadata.wrapped_key); + parameters.put(CteraDirectIOReadFeature.CTERA_WRAPPEDKEY, metadata.encrypt_info.wrapped_key); segment.setParameters(parameters); } } else { - throw new TransferCanceledException(String.format("Mismatch between number of segments and chunks for %s", file.getKey().remote)); + log.error("Mismatch between number of segments ({}) and chunks ({}) for {}", + segments.size(), metadata.chunks.size(), file.getKey().remote); } } else { if(0L == status.getOffset()) { status.setUrl(metadata.chunks.get(0).url); final Map parameters = new HashMap<>(status.getParameters()); - parameters.put("wrapped_key", metadata.wrapped_key); + parameters.put(CteraDirectIOReadFeature.CTERA_WRAPPEDKEY, metadata.encrypt_info.wrapped_key); status.setParameters(parameters); } else { @@ -106,7 +106,7 @@ public class CteraBulkFeature extends DisabledBulkFeature { } private DirectIO getMetadata(final Path file) throws IOException, BackgroundException { - final HttpGet request = new HttpGet(String.format("%s%s%s", new HostUrlProvider().withPath(false) + final HttpGet request = new HttpGet(String.format("%s%s%s", new HostUrlProvider().withUsername(false).withPath(false) .get(session.getHost()), CteraDirectIOInterceptor.DIRECTIO_PATH, versionid.getVersionId(file))); return session.getClient().getClient().execute(request, new AbstractResponseHandler() { @Override diff --git a/ctera/src/main/java/ch/cyberduck/core/ctera/CteraDirectIOReadFeature.java b/ctera/src/main/java/ch/cyberduck/core/ctera/CteraDirectIOReadFeature.java index a9dde86389..a8d3897231 100644 --- a/ctera/src/main/java/ch/cyberduck/core/ctera/CteraDirectIOReadFeature.java +++ b/ctera/src/main/java/ch/cyberduck/core/ctera/CteraDirectIOReadFeature.java @@ -42,6 +42,8 @@ import java.util.List; public class CteraDirectIOReadFeature implements Read { private static final Logger log = LogManager.getLogger(CteraDirectIOReadFeature.class); + public static final String CTERA_WRAPPEDKEY = "wrapped_key"; + private final CteraSession session; public CteraDirectIOReadFeature(final CteraSession session) { @@ -51,7 +53,7 @@ public class CteraDirectIOReadFeature implements Read { @Override public InputStream read(final Path file, final TransferStatus status, final ConnectionCallback callback) throws BackgroundException { try { - final EncryptInfo key = new EncryptInfo(status.getParameters().get("wrapped_key"), session.getOrCreateAPIKeys().secretKey); + final EncryptInfo key = new EncryptInfo(status.getParameters().get(CTERA_WRAPPEDKEY), session.getOrCreateAPIKeys().secretKey); final List chunks = new ArrayList<>(); final DirectIO.Chunk chunk = new DirectIO.Chunk(); chunk.url = status.getUrl(); diff --git a/ctera/src/main/java/ch/cyberduck/core/ctera/CteraDirectoryFeature.java b/ctera/src/main/java/ch/cyberduck/core/ctera/CteraDirectoryFeature.java index b40651430f..002247f084 100644 --- a/ctera/src/main/java/ch/cyberduck/core/ctera/CteraDirectoryFeature.java +++ b/ctera/src/main/java/ch/cyberduck/core/ctera/CteraDirectoryFeature.java @@ -30,7 +30,7 @@ import static ch.cyberduck.core.ctera.CteraTouchFeature.validate; public class CteraDirectoryFeature extends DAVDirectoryFeature { public CteraDirectoryFeature(final CteraSession session) { - super(session, new CteraAttributesFinderFeature(session)); + super(session); } @Override diff --git a/ctera/src/main/java/ch/cyberduck/core/ctera/CteraSession.java b/ctera/src/main/java/ch/cyberduck/core/ctera/CteraSession.java index 3a3eb60008..389a1fb8f1 100644 --- a/ctera/src/main/java/ch/cyberduck/core/ctera/CteraSession.java +++ b/ctera/src/main/java/ch/cyberduck/core/ctera/CteraSession.java @@ -26,9 +26,7 @@ import ch.cyberduck.core.exception.BackgroundException; import ch.cyberduck.core.features.*; import ch.cyberduck.core.http.CustomServiceUnavailableRetryStrategy; import ch.cyberduck.core.http.DefaultHttpResponseExceptionMappingService; -import ch.cyberduck.core.http.ExecutionCountServiceUnavailableRetryStrategy; import ch.cyberduck.core.http.HttpExceptionMappingService; -import ch.cyberduck.core.preferences.HostPreferencesFactory; import ch.cyberduck.core.proxy.ProxyFinder; import ch.cyberduck.core.shared.DefaultUrlProvider; import ch.cyberduck.core.shared.DisabledBulkFeature; @@ -84,15 +82,14 @@ public class CteraSession extends DAVSession { protected DAVClient connect(final ProxyFinder proxy, final HostKeyCallback key, final LoginCallback prompt, final CancelCallback cancel) throws BackgroundException { final HttpClientBuilder configuration = builder.build(proxy, this, prompt); configuration.disableRedirectHandling(); - if(HostPreferencesFactory.get(host).getBoolean("ctera.download.directio.enable")) { + if(preferences.getBoolean("ctera.download.directio.enable")) { configuration.setServiceUnavailableRetryStrategy(new CustomServiceUnavailableRetryStrategy(host, - new ExecutionCountServiceUnavailableRetryStrategy(authentication = new CteraAuthenticationHandler(this, prompt), - directio = new CteraDirectIOInterceptor(this)))); + authentication = new CteraAuthenticationHandler(this, prompt), directio = new CteraDirectIOInterceptor(this))); configuration.addInterceptorFirst(directio); } else { configuration.setServiceUnavailableRetryStrategy(new CustomServiceUnavailableRetryStrategy(host, - new ExecutionCountServiceUnavailableRetryStrategy(authentication = new CteraAuthenticationHandler(this, prompt)))); + authentication = new CteraAuthenticationHandler(this, prompt))); } configuration.addInterceptorFirst(new CteraCookieInterceptor()); final DAVClient client = new DAVClient(new HostUrlProvider().withUsername(false).get(host), configuration); @@ -231,15 +228,20 @@ public class CteraSession extends DAVSession { } finally { super.logout(); - versionid.clear(); } } + @Override + public void disconnect() throws BackgroundException { + versionid.clear(); + super.disconnect(); + } + @Override @SuppressWarnings("unchecked") public T _getFeature(final Class type) { if(type == VersionIdProvider.class) { - if(HostPreferencesFactory.get(host).getBoolean("ctera.download.directio.enable")) { + if(preferences.getBoolean("ctera.download.directio.enable")) { return (T) versionid; } return null; @@ -275,7 +277,7 @@ public class CteraSession extends DAVSession { return (T) new CteraListService(this); } if(type == Read.class) { - if(HostPreferencesFactory.get(host).getBoolean("ctera.download.directio.enable")) { + if(preferences.getBoolean("ctera.download.directio.enable")) { return (T) new CteraDelegatingReadFeature(this); } return (T) new CteraReadFeature(this); @@ -293,7 +295,7 @@ public class CteraSession extends DAVSession { return (T) new DisabledQuotaFeature(); } if(type == Bulk.class) { - if(HostPreferencesFactory.get(host).getBoolean("ctera.download.directio.enable")) { + if(preferences.getBoolean("ctera.download.directio.enable")) { return (T) new CteraBulkFeature(this, versionid); } return (T) new DisabledBulkFeature(); diff --git a/ctera/src/main/java/ch/cyberduck/core/ctera/CteraTouchFeature.java b/ctera/src/main/java/ch/cyberduck/core/ctera/CteraTouchFeature.java index d53a42e6ee..a46bb64b93 100644 --- a/ctera/src/main/java/ch/cyberduck/core/ctera/CteraTouchFeature.java +++ b/ctera/src/main/java/ch/cyberduck/core/ctera/CteraTouchFeature.java @@ -35,7 +35,7 @@ public class CteraTouchFeature extends DAVTouchFeature { private static final Logger log = LogManager.getLogger(CteraTouchFeature.class); public CteraTouchFeature(final CteraSession session) { - super(new CteraWriteFeature(session)); + super(session); } @Override diff --git a/ctera/src/main/java/ch/cyberduck/core/ctera/model/DirectIO.java b/ctera/src/main/java/ch/cyberduck/core/ctera/model/DirectIO.java index b5a2f1ed3c..19621f2bd1 100644 --- a/ctera/src/main/java/ch/cyberduck/core/ctera/model/DirectIO.java +++ b/ctera/src/main/java/ch/cyberduck/core/ctera/model/DirectIO.java @@ -22,8 +22,7 @@ import com.fasterxml.jackson.annotation.JsonIgnoreProperties; @JsonIgnoreProperties(ignoreUnknown = true) public final class DirectIO { - public String wrapped_key; - + public EncryptInfo encrypt_info; public List chunks; public static final class Chunk { @@ -40,10 +39,26 @@ public final class DirectIO { } } + public static class EncryptInfo { + + public String wrapped_key; + public boolean data_encrypted; + + @Override + public String toString() { + final StringBuilder sb = new StringBuilder("EncryptInfo{"); + sb.append("wrapped_key='").append(wrapped_key).append('\''); + sb.append(", data_encrypted=").append(data_encrypted); + sb.append('}'); + return sb.toString(); + } + } + @Override public String toString() { final StringBuilder sb = new StringBuilder("DirectIO{"); - sb.append("chunks=").append(chunks); + sb.append("encrypt_info=").append(encrypt_info); + sb.append(", chunks=").append(chunks); sb.append('}'); return sb.toString(); } diff --git a/ctera/src/test/java/ch/cyberduck/core/ctera/CteraAttributesFinderFeatureTest.java b/ctera/src/test/java/ch/cyberduck/core/ctera/CteraAttributesFinderFeatureTest.java index 4d4f2b6f82..1acadef593 100644 --- a/ctera/src/test/java/ch/cyberduck/core/ctera/CteraAttributesFinderFeatureTest.java +++ b/ctera/src/test/java/ch/cyberduck/core/ctera/CteraAttributesFinderFeatureTest.java @@ -67,13 +67,13 @@ public class CteraAttributesFinderFeatureTest extends AbstractCteraTest { final String rootEtag = f.find(root).getETag(); // No milliseconds precision Thread.sleep(1000L); - final Path folder = new CteraDirectoryFeature(session).mkdir(new Path(root, + final Path folder = new CteraDirectoryFeature(session).mkdir(new CteraWriteFeature(session), new Path(root, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory)), new TransferStatus()); assertNotEquals(rootTimestamp, f.find(root).getModificationDate()); assertNotEquals(rootEtag, f.find(root).getETag()); final long folderTimestamp = f.find(folder).getModificationDate(); final String folderEtag = f.find(folder).getETag(); - final Path test = new CteraTouchFeature(session).touch(new Path(folder, + final Path test = new CteraTouchFeature(session).touch(new CteraWriteFeature(session), new Path(folder, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)), new TransferStatus()); assertEquals(folderTimestamp, f.find(folder).getModificationDate()); assertEquals(folderEtag, f.find(folder).getETag()); @@ -94,7 +94,7 @@ public class CteraAttributesFinderFeatureTest extends AbstractCteraTest { @Test public void testFindDirectory() throws Exception { final Path home = new DefaultHomeFinderService(session).find(); - final Path test = new CteraDirectoryFeature(session).mkdir(new Path(home, + final Path test = new CteraDirectoryFeature(session).mkdir(new CteraWriteFeature(session), new Path(home, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory)), new TransferStatus()); final DAVAttributesFinderFeature f = new CteraAttributesFinderFeature(session); final PathAttributes attributes = f.find(test); diff --git a/ctera/src/test/java/ch/cyberduck/core/ctera/CteraConcurrentTransferWorkerTest.java b/ctera/src/test/java/ch/cyberduck/core/ctera/CteraConcurrentTransferWorkerTest.java index dc59fb0f77..1b68a14520 100644 --- a/ctera/src/test/java/ch/cyberduck/core/ctera/CteraConcurrentTransferWorkerTest.java +++ b/ctera/src/test/java/ch/cyberduck/core/ctera/CteraConcurrentTransferWorkerTest.java @@ -15,7 +15,16 @@ package ch.cyberduck.core.ctera; * GNU General Public License for more details. */ -import ch.cyberduck.core.*; +import ch.cyberduck.core.AlphanumericRandomStringService; +import ch.cyberduck.core.BytecountStreamListener; +import ch.cyberduck.core.DisabledConnectionCallback; +import ch.cyberduck.core.DisabledLoginCallback; +import ch.cyberduck.core.DisabledProgressListener; +import ch.cyberduck.core.Host; +import ch.cyberduck.core.Local; +import ch.cyberduck.core.NullFilter; +import ch.cyberduck.core.Path; +import ch.cyberduck.core.TestProtocol; import ch.cyberduck.core.dav.DAVAttributesFinderFeature; import ch.cyberduck.core.dav.DAVUploadFeature; import ch.cyberduck.core.features.Delete; @@ -25,9 +34,18 @@ import ch.cyberduck.core.local.DefaultTemporaryFileService; import ch.cyberduck.core.notification.DisabledNotificationService; import ch.cyberduck.core.pool.SessionPool; import ch.cyberduck.core.shared.DefaultHomeFinderService; -import ch.cyberduck.core.transfer.*; +import ch.cyberduck.core.transfer.DisabledTransferErrorCallback; +import ch.cyberduck.core.transfer.DisabledTransferPrompt; +import ch.cyberduck.core.transfer.DownloadTransfer; +import ch.cyberduck.core.transfer.Transfer; +import ch.cyberduck.core.transfer.TransferAction; +import ch.cyberduck.core.transfer.TransferItem; +import ch.cyberduck.core.transfer.TransferOptions; +import ch.cyberduck.core.transfer.TransferSpeedometer; +import ch.cyberduck.core.transfer.TransferStatus; import ch.cyberduck.core.worker.ConcurrentTransferWorker; import ch.cyberduck.test.IntegrationTest; + import org.apache.commons.io.IOUtils; import org.apache.commons.lang3.RandomUtils; import org.junit.Test; @@ -51,7 +69,7 @@ public class CteraConcurrentTransferWorkerTest extends AbstractCteraDirectIOTest assertNotNull(out); IOUtils.write(content, out); out.close(); - new DAVUploadFeature(session).upload(test, local, new BandwidthThrottle(BandwidthThrottle.UNLIMITED), new DisabledProgressListener(), new DisabledStreamListener(), new TransferStatus().setLength(content.length), new DisabledConnectionCallback()); + new DAVUploadFeature(session).upload(new CteraWriteFeature(session), test, local, new BandwidthThrottle(BandwidthThrottle.UNLIMITED), new DisabledProgressListener(), new DisabledStreamListener(), new TransferStatus().setLength(content.length), new DisabledConnectionCallback()); assertEquals(content.length, new DAVAttributesFinderFeature(session).find(test).getSize()); final Local localFile = new DefaultTemporaryFileService().create(test.getName()); final Transfer download = new DownloadTransfer(new Host(new TestProtocol()), Collections.singletonList(new TransferItem(test, localFile)), new NullFilter<>()); @@ -79,7 +97,7 @@ public class CteraConcurrentTransferWorkerTest extends AbstractCteraDirectIOTest assertNotNull(out); IOUtils.write(content, out); out.close(); - new DAVUploadFeature(session).upload(test, local, new BandwidthThrottle(BandwidthThrottle.UNLIMITED), new DisabledProgressListener(), new DisabledStreamListener(), new TransferStatus().setLength(content.length), new DisabledConnectionCallback()); + new DAVUploadFeature(session).upload(new CteraWriteFeature(session), test, local, new BandwidthThrottle(BandwidthThrottle.UNLIMITED), new DisabledProgressListener(), new DisabledStreamListener(), new TransferStatus().setLength(content.length), new DisabledConnectionCallback()); assertEquals(content.length, new DAVAttributesFinderFeature(session).find(test).getSize()); final Local localFile = new DefaultTemporaryFileService().create(test.getName()); final Transfer download = new DownloadTransfer(new Host(new TestProtocol()), Collections.singletonList(new TransferItem(test, localFile)), new NullFilter<>()); diff --git a/ctera/src/test/java/ch/cyberduck/core/ctera/CteraCopyFeatureTest.java b/ctera/src/test/java/ch/cyberduck/core/ctera/CteraCopyFeatureTest.java index dda1c97024..03898013a1 100644 --- a/ctera/src/test/java/ch/cyberduck/core/ctera/CteraCopyFeatureTest.java +++ b/ctera/src/test/java/ch/cyberduck/core/ctera/CteraCopyFeatureTest.java @@ -17,6 +17,7 @@ package ch.cyberduck.core.ctera; import ch.cyberduck.core.Acl; import ch.cyberduck.core.AlphanumericRandomStringService; +import ch.cyberduck.core.DefaultPathAttributes; import ch.cyberduck.core.LocaleFactory; import ch.cyberduck.core.Path; import ch.cyberduck.core.PathAttributes; @@ -77,7 +78,7 @@ public class CteraCopyFeatureTest extends AbstractCteraTest { target.getParent().setAttributes(target.getParent().attributes().setAcl(new Acl(new Acl.CanonicalUser(), CteraAttributesFinderFeature.CREATEDIRECTORIESPERMISSION))); final CteraAttributesFinderFeature mock = mock(CteraAttributesFinderFeature.class); // target exists and not writable - when(mock.find(eq(target))).thenReturn(new PathAttributes().setAcl(new Acl(new Acl.CanonicalUser()))); + when(mock.find(eq(target))).thenReturn(new DefaultPathAttributes().setAcl(new Acl(new Acl.CanonicalUser()))); final AccessDeniedException accessDeniedException = assertThrows(AccessDeniedException.class, () -> new CteraCopyFeature(session).preflight(source, Optional.of(target))); assertTrue(accessDeniedException.getDetail().contains(MessageFormat.format(LocaleFactory.localizedString("Upload {0} failed", "Error"), target.getName()))); } @@ -92,7 +93,7 @@ public class CteraCopyFeatureTest extends AbstractCteraTest { target.getParent().setAttributes(target.getParent().attributes().setAcl(new Acl(new Acl.CanonicalUser()))); final CteraAttributesFinderFeature mock = mock(CteraAttributesFinderFeature.class); // target exists and not writable - when(mock.find(eq(target))).thenReturn(new PathAttributes().setAcl(new Acl(new Acl.CanonicalUser()))); + when(mock.find(eq(target))).thenReturn(new DefaultPathAttributes().setAcl(new Acl(new Acl.CanonicalUser()))); final AccessDeniedException accessDeniedException = assertThrows(AccessDeniedException.class, () -> new CteraCopyFeature(session).preflight(source, Optional.of(target))); assertTrue(accessDeniedException.getDetail().contains(MessageFormat.format(LocaleFactory.localizedString("Upload {0} failed", "Error"), target.getName()))); } diff --git a/ctera/src/test/java/ch/cyberduck/core/ctera/CteraDeleteFeatureTest.java b/ctera/src/test/java/ch/cyberduck/core/ctera/CteraDeleteFeatureTest.java index 9462129ce4..efa2206f7c 100644 --- a/ctera/src/test/java/ch/cyberduck/core/ctera/CteraDeleteFeatureTest.java +++ b/ctera/src/test/java/ch/cyberduck/core/ctera/CteraDeleteFeatureTest.java @@ -31,7 +31,7 @@ public class CteraDeleteFeatureTest extends AbstractCteraTest { @Test public void testDeleteFile() throws Exception { final Path test = new Path(new DefaultHomeFinderService(session).find(), new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)); - new CteraTouchFeature(session).touch(test, new TransferStatus()); + new CteraTouchFeature(session).touch(new CteraWriteFeature(session), test, new TransferStatus()); assertTrue(new DAVFindFeature(session).find(test)); new CteraDeleteFeature(session).delete(Collections.singletonMap(test, new TransferStatus()), new DisabledLoginCallback(), new Delete.DisabledCallback()); assertFalse(new DAVFindFeature(session).find(test)); @@ -40,7 +40,7 @@ public class CteraDeleteFeatureTest extends AbstractCteraTest { @Test(expected = RetriableAccessDeniedException.class) public void testDeleteFileWithLock() throws Exception { final Path test = new Path(new DefaultHomeFinderService(session).find(), new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)); - new CteraTouchFeature(session).touch(test, new TransferStatus()); + new CteraTouchFeature(session).touch(new CteraWriteFeature(session), test, new TransferStatus()); String lock = null; try { lock = new DAVLockFeature(session).lock(test); @@ -56,9 +56,9 @@ public class CteraDeleteFeatureTest extends AbstractCteraTest { @Test public void testDeleteDirectory() throws Exception { final Path test = new Path(new DefaultHomeFinderService(session).find(), new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory)); - new CteraDirectoryFeature(session).mkdir(test, new TransferStatus()); + new CteraDirectoryFeature(session).mkdir(new CteraWriteFeature(session), test, new TransferStatus()); assertTrue(new DAVFindFeature(session).find(test)); - new CteraTouchFeature(session).touch(new Path(test, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)), new TransferStatus()); + new CteraTouchFeature(session).touch(new CteraWriteFeature(session), new Path(test, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)), new TransferStatus()); new CteraDeleteFeature(session).delete(Collections.singletonMap(test, new TransferStatus()), new DisabledLoginCallback(), new Delete.DisabledCallback()); assertFalse(new DAVFindFeature(session).find(test)); } diff --git a/ctera/src/test/java/ch/cyberduck/core/ctera/CteraDirectIOReadFeatureTest.java b/ctera/src/test/java/ch/cyberduck/core/ctera/CteraDirectIOReadFeatureTest.java index 7239ec7475..54fe13f55d 100644 --- a/ctera/src/test/java/ch/cyberduck/core/ctera/CteraDirectIOReadFeatureTest.java +++ b/ctera/src/test/java/ch/cyberduck/core/ctera/CteraDirectIOReadFeatureTest.java @@ -52,7 +52,7 @@ public class CteraDirectIOReadFeatureTest extends AbstractCteraDirectIOTest { @Test public void testReadChunk() throws Exception { - final Path test = new CteraTouchFeature(session).touch(new Path(new DefaultHomeFinderService(session).find(), new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)), new TransferStatus()); + final Path test = new CteraTouchFeature(session).touch(new CteraWriteFeature(session), new Path(new DefaultHomeFinderService(session).find(), new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)), new TransferStatus()); final Local local = new Local(System.getProperty("java.io.tmpdir"), new AlphanumericRandomStringService().random()); final byte[] content = RandomUtils.nextBytes(65536); final OutputStream out = local.getOutputStream(false); @@ -60,7 +60,7 @@ public class CteraDirectIOReadFeatureTest extends AbstractCteraDirectIOTest { IOUtils.write(content, out); out.close(); new DAVUploadFeature(session).upload( - test, local, new BandwidthThrottle(BandwidthThrottle.UNLIMITED), new DisabledProgressListener(), new DisabledStreamListener(), + new CteraWriteFeature(session), test, local, new BandwidthThrottle(BandwidthThrottle.UNLIMITED), new DisabledProgressListener(), new DisabledStreamListener(), new TransferStatus().setLength(content.length), new DisabledConnectionCallback()); final TransferStatus status = new TransferStatus(); diff --git a/ctera/src/test/java/ch/cyberduck/core/ctera/CteraDirectoryFeatureTest.java b/ctera/src/test/java/ch/cyberduck/core/ctera/CteraDirectoryFeatureTest.java index aacf5ac4b4..a180b0837b 100644 --- a/ctera/src/test/java/ch/cyberduck/core/ctera/CteraDirectoryFeatureTest.java +++ b/ctera/src/test/java/ch/cyberduck/core/ctera/CteraDirectoryFeatureTest.java @@ -43,9 +43,9 @@ public class CteraDirectoryFeatureTest extends AbstractCteraTest { @Test public void testMakeDirectory() throws Exception { final Path test = new Path(new DefaultHomeFinderService(session).find(), new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory)); - final Path result = new CteraDirectoryFeature(session).mkdir(test, new TransferStatus()); + final Path result = new CteraDirectoryFeature(session).mkdir(new CteraWriteFeature(session), test, new TransferStatus()); assertTrue(session.getFeature(Find.class).find(test)); - assertThrows(ConflictException.class, () -> new CteraDirectoryFeature(session).mkdir(test, new TransferStatus())); + assertThrows(ConflictException.class, () -> new CteraDirectoryFeature(session).mkdir(new CteraWriteFeature(session), test, new TransferStatus())); new CteraDeleteFeature(session).delete(Collections.singletonList(test), new DisabledLoginCallback(), new Delete.DisabledCallback()); assertFalse(session.getFeature(Find.class).find(test)); } diff --git a/ctera/src/test/java/ch/cyberduck/core/ctera/CteraMoveFeatureTest.java b/ctera/src/test/java/ch/cyberduck/core/ctera/CteraMoveFeatureTest.java index 64721c439b..6ef2b7c8fc 100644 --- a/ctera/src/test/java/ch/cyberduck/core/ctera/CteraMoveFeatureTest.java +++ b/ctera/src/test/java/ch/cyberduck/core/ctera/CteraMoveFeatureTest.java @@ -19,6 +19,7 @@ package ch.cyberduck.core.ctera; import ch.cyberduck.core.Acl; import ch.cyberduck.core.AlphanumericRandomStringService; +import ch.cyberduck.core.DefaultPathAttributes; import ch.cyberduck.core.DisabledConnectionCallback; import ch.cyberduck.core.DisabledLoginCallback; import ch.cyberduck.core.LocaleFactory; @@ -58,7 +59,7 @@ public class CteraMoveFeatureTest extends AbstractCteraTest { @Test public void testMove() throws Exception { - final Path test = new CteraTouchFeature(session).touch(new Path(new DefaultHomeFinderService(session).find(), new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)), new TransferStatus()); + final Path test = new CteraTouchFeature(session).touch(new CteraWriteFeature(session), new Path(new DefaultHomeFinderService(session).find(), new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)), new TransferStatus()); assertEquals(PathAttributes.EMPTY, test.attributes()); final TransferStatus status = new TransferStatus(); new DAVTimestampFeature(session).setTimestamp(test, status.setModified(5000L)); @@ -78,7 +79,7 @@ public class CteraMoveFeatureTest extends AbstractCteraTest { @Test public void testMoveDirectory() throws Exception { final Path folder = new Path(new DefaultHomeFinderService(session).find(), new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory)); - new CteraDirectoryFeature(session).mkdir(folder, new TransferStatus()); + new CteraDirectoryFeature(session).mkdir(new CteraWriteFeature(session), folder, new TransferStatus()); final Path test = new Path(folder, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)); { final byte[] content = RandomUtils.nextBytes(3547); @@ -107,11 +108,11 @@ public class CteraMoveFeatureTest extends AbstractCteraTest { @Test public void testMoveOverride() throws Exception { final Path test = new Path(new DefaultHomeFinderService(session).find(), new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)); - new CteraTouchFeature(session).touch(test, new TransferStatus()); + new CteraTouchFeature(session).touch(new CteraWriteFeature(session), test, new TransferStatus()); final PathAttributes testAttributes = new CteraAttributesFinderFeature(session).find(test); final Path target = new Path(new DefaultHomeFinderService(session).find(), new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)); Thread.sleep(1000L); - new CteraTouchFeature(session).touch(target, new TransferStatus()); + new CteraTouchFeature(session).touch(new CteraWriteFeature(session), target, new TransferStatus()); final PathAttributes targetAttributes = new CteraAttributesFinderFeature(session).find(target); assertThrows(ConflictException.class, () -> new CteraMoveFeature(session).move(test, target, new TransferStatus().setExists(false), new Delete.DisabledCallback(), new DisabledConnectionCallback())); Thread.sleep(1000L); @@ -178,7 +179,7 @@ public class CteraMoveFeatureTest extends AbstractCteraTest { target.getParent().setAttributes(target.getParent().attributes().setAcl(new Acl(new Acl.CanonicalUser(), CteraAttributesFinderFeature.CREATEDIRECTORIESPERMISSION))); final CteraAttributesFinderFeature mock = mock(CteraAttributesFinderFeature.class); // target exists and not writable - when(mock.find(eq(target))).thenReturn(new PathAttributes().setAcl(new Acl(new Acl.CanonicalUser()))); + when(mock.find(eq(target))).thenReturn(new DefaultPathAttributes().setAcl(new Acl(new Acl.CanonicalUser()))); final AccessDeniedException accessDeniedException = assertThrows(AccessDeniedException.class, () -> new CteraMoveFeature(session).preflight(source, Optional.of(target))); assertTrue(accessDeniedException.getDetail().contains(MessageFormat.format(LocaleFactory.localizedString("Upload {0} failed", "Error"), target.getName()))); } @@ -204,7 +205,7 @@ public class CteraMoveFeatureTest extends AbstractCteraTest { target.getParent().setAttributes(target.getParent().attributes().setAcl(new Acl(new Acl.CanonicalUser()))); final CteraAttributesFinderFeature mock = mock(CteraAttributesFinderFeature.class); // target exists and not writable - when(mock.find(eq(target))).thenReturn(new PathAttributes().setAcl(new Acl(new Acl.CanonicalUser()))); + when(mock.find(eq(target))).thenReturn(new DefaultPathAttributes().setAcl(new Acl(new Acl.CanonicalUser()))); final AccessDeniedException accessDeniedException = assertThrows(AccessDeniedException.class, () -> new CteraMoveFeature(session).preflight(source, Optional.of(target))); assertTrue(accessDeniedException.getDetail().contains(MessageFormat.format(LocaleFactory.localizedString("Upload {0} failed", "Error"), target.getName()))); } diff --git a/ctera/src/test/java/ch/cyberduck/core/ctera/CteraReadFeatureTest.java b/ctera/src/test/java/ch/cyberduck/core/ctera/CteraReadFeatureTest.java index 9ff3e64e54..3265ae51a2 100644 --- a/ctera/src/test/java/ch/cyberduck/core/ctera/CteraReadFeatureTest.java +++ b/ctera/src/test/java/ch/cyberduck/core/ctera/CteraReadFeatureTest.java @@ -76,7 +76,7 @@ public class CteraReadFeatureTest extends AbstractCteraTest { IOUtils.write(content, out); out.close(); new DAVUploadFeature(session).upload( - test, local, new BandwidthThrottle(BandwidthThrottle.UNLIMITED), new DisabledProgressListener(), new DisabledStreamListener(), + new CteraWriteFeature(session), test, local, new BandwidthThrottle(BandwidthThrottle.UNLIMITED), new DisabledProgressListener(), new DisabledStreamListener(), new TransferStatus().setLength(content.length), new DisabledConnectionCallback()); // Unknown length in status @@ -88,7 +88,7 @@ public class CteraReadFeatureTest extends AbstractCteraTest { return this; } }; - new DefaultDownloadFeature(new CteraReadFeature(session)).download(test, local, new BandwidthThrottle(BandwidthThrottle.UNLIMITED), + new DefaultDownloadFeature(session).download(new CteraReadFeature(session), test, local, new BandwidthThrottle(BandwidthThrottle.UNLIMITED), new DisabledStreamListener(), status, new DisabledLoginCallback()); assertEquals(923L, local.attributes().getSize()); new CteraDeleteFeature(session).delete(Collections.singletonList(test), new DisabledLoginCallback(), new Delete.DisabledCallback()); @@ -96,7 +96,7 @@ public class CteraReadFeatureTest extends AbstractCteraTest { @Test public void testReadInterrupt() throws Exception { - final Path test = new CteraTouchFeature(session).touch(new Path(new DefaultHomeFinderService(session).find(), + final Path test = new CteraTouchFeature(session).touch(new CteraWriteFeature(session), new Path(new DefaultHomeFinderService(session).find(), new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)), new TransferStatus()); // Unknown length in status final TransferStatus status = new TransferStatus(); @@ -124,7 +124,7 @@ public class CteraReadFeatureTest extends AbstractCteraTest { IOUtils.write(content, out); out.close(); new DAVUploadFeature(session).upload( - test, local, new BandwidthThrottle(BandwidthThrottle.UNLIMITED), new DisabledProgressListener(), new DisabledStreamListener(), + new CteraWriteFeature(session), test, local, new BandwidthThrottle(BandwidthThrottle.UNLIMITED), new DisabledProgressListener(), new DisabledStreamListener(), new TransferStatus().setLength(content.length), new DisabledConnectionCallback()); final TransferStatus status = new TransferStatus(); @@ -144,7 +144,7 @@ public class CteraReadFeatureTest extends AbstractCteraTest { @Test public void testReadRangeUnknownLength() throws Exception { - final Path test = new CteraTouchFeature(session).touch(new Path(new DefaultHomeFinderService(session).find(), new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)), new TransferStatus()); + final Path test = new CteraTouchFeature(session).touch(new CteraWriteFeature(session), new Path(new DefaultHomeFinderService(session).find(), new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)), new TransferStatus()); final Local local = new Local(System.getProperty("java.io.tmpdir"), new AlphanumericRandomStringService().random()); final byte[] content = RandomUtils.nextBytes(1023); final OutputStream out = local.getOutputStream(false); @@ -152,7 +152,7 @@ public class CteraReadFeatureTest extends AbstractCteraTest { IOUtils.write(content, out); out.close(); new DAVUploadFeature(session).upload( - test, local, new BandwidthThrottle(BandwidthThrottle.UNLIMITED), new DisabledProgressListener(), new DisabledStreamListener(), + new CteraWriteFeature(session), test, local, new BandwidthThrottle(BandwidthThrottle.UNLIMITED), new DisabledProgressListener(), new DisabledStreamListener(), new TransferStatus().setLength(content.length), new DisabledConnectionCallback()); final TransferStatus status = new TransferStatus(); @@ -172,7 +172,7 @@ public class CteraReadFeatureTest extends AbstractCteraTest { @Test public void testReadCloseReleaseEntity() throws Exception { - final Path test = new CteraTouchFeature(session).touch(new Path(new DefaultHomeFinderService(session).find(), + final Path test = new CteraTouchFeature(session).touch(new CteraWriteFeature(session), new Path(new DefaultHomeFinderService(session).find(), new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)), new TransferStatus()); final TransferStatus status = new TransferStatus(); final CountingInputStream in = new CountingInputStream(new CteraReadFeature(session).read(test, status, new DisabledConnectionCallback())); diff --git a/ctera/src/test/java/ch/cyberduck/core/ctera/CteraWriteFeatureTest.java b/ctera/src/test/java/ch/cyberduck/core/ctera/CteraWriteFeatureTest.java index 4345f855cc..91def1b10b 100644 --- a/ctera/src/test/java/ch/cyberduck/core/ctera/CteraWriteFeatureTest.java +++ b/ctera/src/test/java/ch/cyberduck/core/ctera/CteraWriteFeatureTest.java @@ -45,7 +45,7 @@ public class CteraWriteFeatureTest extends AbstractCteraTest { final String rootEtag = new CteraAttributesFinderFeature(session).find(root).getETag(); // ETag is not constant for "My Files" // assertEquals(rootEtag, new CteraAttributesFinderFeature(session).find(root).getETag()); - final Path folder = new CteraDirectoryFeature(session).mkdir(new Path(root, + final Path folder = new CteraDirectoryFeature(session).mkdir(new CteraWriteFeature(session), new Path(root, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory)), new TransferStatus()); final String folderEtag = new CteraAttributesFinderFeature(session).find(folder).getETag(); assertEquals(folderEtag, new CteraAttributesFinderFeature(session).find(folder).getETag()); @@ -58,7 +58,7 @@ public class CteraWriteFeatureTest extends AbstractCteraTest { status.setLength(content.length); final Path test = new Path(folder, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)); final HttpUploadFeature upload = new DAVUploadFeature(session); - upload.upload(test, local, new BandwidthThrottle(BandwidthThrottle.UNLIMITED), + upload.upload(new CteraWriteFeature(session), test, local, new BandwidthThrottle(BandwidthThrottle.UNLIMITED), new DisabledProgressListener(), new DisabledStreamListener(), status, new DisabledConnectionCallback()); assertTrue(session.getFeature(Find.class).find(test)); assertEquals(content.length, new CteraListService(session).list(test.getParent(), new DisabledListProgressListener()) @@ -86,7 +86,7 @@ public class CteraWriteFeatureTest extends AbstractCteraTest { @Test public void testReplaceContent() throws Exception { final Path root = new DefaultHomeFinderService(session).find(); - final Path folder = new CteraDirectoryFeature(session).mkdir(new Path(root, + final Path folder = new CteraDirectoryFeature(session).mkdir(new CteraWriteFeature(session), new Path(root, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory)), new TransferStatus()); final Local local = new Local(System.getProperty("java.io.tmpdir"), new AlphanumericRandomStringService().random()); final Path test = new Path(folder, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)); @@ -98,7 +98,7 @@ public class CteraWriteFeatureTest extends AbstractCteraTest { out.close(); final TransferStatus status = new TransferStatus(); status.setLength(content.length); - upload.upload(test, local, new BandwidthThrottle(BandwidthThrottle.UNLIMITED), + upload.upload(new CteraWriteFeature(session), test, local, new BandwidthThrottle(BandwidthThrottle.UNLIMITED), new DisabledProgressListener(), new DisabledStreamListener(), status, new DisabledConnectionCallback()); } final PathAttributes attr1 = new CteraAttributesFinderFeature(session).find(test); @@ -110,7 +110,7 @@ public class CteraWriteFeatureTest extends AbstractCteraTest { out.close(); final TransferStatus status = new TransferStatus(); status.setLength(content.length); - upload.upload(test, local, new BandwidthThrottle(BandwidthThrottle.UNLIMITED), + upload.upload(new CteraWriteFeature(session), test, local, new BandwidthThrottle(BandwidthThrottle.UNLIMITED), new DisabledProgressListener(), new DisabledStreamListener(), status, new DisabledConnectionCallback()); } final PathAttributes attr2 = new CteraAttributesFinderFeature(session).find(test); diff --git a/ctera/src/test/java/ch/cyberduck/core/ctera/model/DirectIOTest.java b/ctera/src/test/java/ch/cyberduck/core/ctera/model/DirectIOTest.java index 71f790fa02..1a13057483 100644 --- a/ctera/src/test/java/ch/cyberduck/core/ctera/model/DirectIOTest.java +++ b/ctera/src/test/java/ch/cyberduck/core/ctera/model/DirectIOTest.java @@ -23,31 +23,52 @@ import java.nio.charset.StandardCharsets; import com.fasterxml.jackson.databind.ObjectMapper; import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; public class DirectIOTest { @Test public void testParse() throws Exception { final String response = "{\n" + - " \"wrapped_key\": \"CeuGXbS1O0pK8S8jReooFaPhxWC02NemFwxhC36hyP/0yIAXV6D2khDSSoUl7+1I\",\n" + - " \"chunks\": [\n" + + " \"encrypt_info\": {\n" + + " \"wrapped_key\": \"mWPomzFfgsRg0xYHV4qwN6vQhBotAKK8d7nHUvhvFi7RWLM8MY/JNk7VH4z/793B\",\n" + + " \"data_encrypted\": true\n" + + " },\n" + + " \"actual_blocks_range\": {\n" + + " \"file_size\": 4071685,\n" + + " \"range\": \"1048576-2097152\"\n" + + " },\n" + + " \"chunks\" : [\n" + " {\n" + - " \"url\": \"https://alexb-dcdirectio.s3.eu-west-1.amazonaws.com/blocks/0000000f/81eba990102c37a1f68da388f6cd19028fac2f68-1c?X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Date=20250227T081312Z&X-Amz-SignedHeaders=host&X-Amz-Expires=86400&X-Amz-Credential=AKIAZYQPDYEO5UVSMPFE%2F20250227%2Feu-west-1%2Fs3%2Faws4_request&X-Amz-Signature=9614791651e6b52c16639f728c62b85024776f51052cdcbbbc6cb06b61a7beab\",\n" + - " \"len\": 4194304\n" + + " \"url\": \"https://noa-media.s3.eu-west-1.amazonaws.com/blocks/00000012/1460a01b6d372268897cf7247bb74af26027e070-1q?X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Date=20240815T115508Z&X-Amz-SignedHeaders=host&X-Amz-Expires=86399&X-Amz-Credential=AKIAZ3NHRJ7YVOIYLQXM%2F20240815%2Feu-west-1%2Fs3%2Faws4_request&X-Amz-Signature=f85f12b0ebac60c51b5bb3ac7406a8937d286c29e7d064a7749ad369da3e3230\",\n" + + " \"len\": 262144\n" + " },\n" + " {\n" + - " \"url\": \"https://alexb-dcdirectio.s3.eu-west-1.amazonaws.com/blocks/0000000f/d0422bac1f3b9c07607788addbe96e1e2f9658cb-1d?X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Date=20250227T081312Z&X-Amz-SignedHeaders=host&X-Amz-Expires=86400&X-Amz-Credential=AKIAZYQPDYEO5UVSMPFE%2F20250227%2Feu-west-1%2Fs3%2Faws4_request&X-Amz-Signature=fa4ef3ff8b32e63b095d9ff1d5f5ed819ee9b70e67de2eb77a99647a6b7f8f32\",\n" + - " \"len\": 462848\n" + + " \"url\": \"https://noa-media.s3.eu-west-1.amazonaws.com/blocks/00000012/f6e13a35a9cc3fd9286b26faa2702cc6c042403b-1t?X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Date=20240815T115508Z&X-Amz-SignedHeaders=host&X-Amz-Expires=86400&X-Amz-Credential=AKIAZ3NHRJ7YVOIYLQXM%2F20240815%2Feu-west-1%2Fs3%2Faws4_request&X-Amz-Signature=5157fe8fce895d5159cd7be6410976ab5f8aaa536bd88a99527c08209fd396dc\",\n" + + " \"len\": 262144\n" + + " },\n" + + " {\n" + + " \"url\": \"https://noa-media.s3.eu-west-1.amazonaws.com/blocks/00000012/392193f6f81aca7d6599603eb48085767d7887a2-1r?X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Date=20240815T115508Z&X-Amz-SignedHeaders=host&X-Amz-Expires=86400&X-Amz-Credential=AKIAZ3NHRJ7YVOIYLQXM%2F20240815%2Feu-west-1%2Fs3%2Faws4_request&X-Amz-Signature=5b01760bf578f2867c381fa7908bc6dca76ad84e254e8bb2f79e9459cf734bbc\",\n" + + " \"len\": 262144\n" + + " },\n" + + " {\n" + + " \"url\": \"https://noa-media.s3.eu-west-1.amazonaws.com/blocks/00000012/65b55099ec0c13b13d5844b2ab7b3d3a19fb54df-1s?X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Date=20240815T115508Z&X-Amz-SignedHeaders=host&X-Amz-Expires=86400&X-Amz-Credential=AKIAZ3NHRJ7YVOIYLQXM%2F20240815%2Feu-west-1%2Fs3%2Faws4_request&X-Amz-Signature=10f67947fcedbc5f562a7a368f785fd52830de9592ab9988d67ce5fa8efd4153\",\n" + + " \"len\": 262144\n" + " }\n" + - " ]\n" + + " ] \n" + "}"; ObjectMapper mapper = new ObjectMapper(); final DirectIO directio = mapper.readValue(new ByteArrayInputStream(response.getBytes(StandardCharsets.UTF_8)), DirectIO.class); - assertEquals("CeuGXbS1O0pK8S8jReooFaPhxWC02NemFwxhC36hyP/0yIAXV6D2khDSSoUl7+1I", directio.wrapped_key); - assertEquals(2, directio.chunks.size()); - assertEquals(4194304, directio.chunks.get(0).len); - assertEquals(462848, directio.chunks.get(1).len); - assertEquals("https://alexb-dcdirectio.s3.eu-west-1.amazonaws.com/blocks/0000000f/81eba990102c37a1f68da388f6cd19028fac2f68-1c?X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Date=20250227T081312Z&X-Amz-SignedHeaders=host&X-Amz-Expires=86400&X-Amz-Credential=AKIAZYQPDYEO5UVSMPFE%2F20250227%2Feu-west-1%2Fs3%2Faws4_request&X-Amz-Signature=9614791651e6b52c16639f728c62b85024776f51052cdcbbbc6cb06b61a7beab", directio.chunks.get(0).url); - assertEquals("https://alexb-dcdirectio.s3.eu-west-1.amazonaws.com/blocks/0000000f/d0422bac1f3b9c07607788addbe96e1e2f9658cb-1d?X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Date=20250227T081312Z&X-Amz-SignedHeaders=host&X-Amz-Expires=86400&X-Amz-Credential=AKIAZYQPDYEO5UVSMPFE%2F20250227%2Feu-west-1%2Fs3%2Faws4_request&X-Amz-Signature=fa4ef3ff8b32e63b095d9ff1d5f5ed819ee9b70e67de2eb77a99647a6b7f8f32", directio.chunks.get(1).url); + assertEquals("mWPomzFfgsRg0xYHV4qwN6vQhBotAKK8d7nHUvhvFi7RWLM8MY/JNk7VH4z/793B", directio.encrypt_info.wrapped_key); + assertTrue(directio.encrypt_info.data_encrypted); + assertEquals(4, directio.chunks.size()); + assertEquals(262144, directio.chunks.get(0).len); + assertEquals(262144, directio.chunks.get(1).len); + assertEquals(262144, directio.chunks.get(2).len); + assertEquals(262144, directio.chunks.get(3).len); + assertEquals("https://noa-media.s3.eu-west-1.amazonaws.com/blocks/00000012/1460a01b6d372268897cf7247bb74af26027e070-1q?X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Date=20240815T115508Z&X-Amz-SignedHeaders=host&X-Amz-Expires=86399&X-Amz-Credential=AKIAZ3NHRJ7YVOIYLQXM%2F20240815%2Feu-west-1%2Fs3%2Faws4_request&X-Amz-Signature=f85f12b0ebac60c51b5bb3ac7406a8937d286c29e7d064a7749ad369da3e3230", directio.chunks.get(0).url); + assertEquals("https://noa-media.s3.eu-west-1.amazonaws.com/blocks/00000012/f6e13a35a9cc3fd9286b26faa2702cc6c042403b-1t?X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Date=20240815T115508Z&X-Amz-SignedHeaders=host&X-Amz-Expires=86400&X-Amz-Credential=AKIAZ3NHRJ7YVOIYLQXM%2F20240815%2Feu-west-1%2Fs3%2Faws4_request&X-Amz-Signature=5157fe8fce895d5159cd7be6410976ab5f8aaa536bd88a99527c08209fd396dc", directio.chunks.get(1).url); + assertEquals("https://noa-media.s3.eu-west-1.amazonaws.com/blocks/00000012/392193f6f81aca7d6599603eb48085767d7887a2-1r?X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Date=20240815T115508Z&X-Amz-SignedHeaders=host&X-Amz-Expires=86400&X-Amz-Credential=AKIAZ3NHRJ7YVOIYLQXM%2F20240815%2Feu-west-1%2Fs3%2Faws4_request&X-Amz-Signature=5b01760bf578f2867c381fa7908bc6dca76ad84e254e8bb2f79e9459cf734bbc", directio.chunks.get(2).url); + assertEquals("https://noa-media.s3.eu-west-1.amazonaws.com/blocks/00000012/65b55099ec0c13b13d5844b2ab7b3d3a19fb54df-1s?X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Date=20240815T115508Z&X-Amz-SignedHeaders=host&X-Amz-Expires=86400&X-Amz-Credential=AKIAZ3NHRJ7YVOIYLQXM%2F20240815%2Feu-west-1%2Fs3%2Faws4_request&X-Amz-Signature=10f67947fcedbc5f562a7a368f785fd52830de9592ab9988d67ce5fa8efd4153", directio.chunks.get(3).url); } } \ No newline at end of file diff --git a/deepbox/pom.xml b/deepbox/pom.xml index 7c6b681c60..be41560921 100644 --- a/deepbox/pom.xml +++ b/deepbox/pom.xml @@ -19,7 +19,7 @@ parent ch.cyberduck - 9.2.0-SNAPSHOT + 9.4.0-SNAPSHOT deepbox diff --git a/deepbox/src/main/java/ch/cyberduck/core/deepbox/DeepboxAttributesFinderFeature.java b/deepbox/src/main/java/ch/cyberduck/core/deepbox/DeepboxAttributesFinderFeature.java index a44eda8b4e..18627eb0b7 100644 --- a/deepbox/src/main/java/ch/cyberduck/core/deepbox/DeepboxAttributesFinderFeature.java +++ b/deepbox/src/main/java/ch/cyberduck/core/deepbox/DeepboxAttributesFinderFeature.java @@ -16,6 +16,7 @@ package ch.cyberduck.core.deepbox; */ import ch.cyberduck.core.Acl; +import ch.cyberduck.core.DefaultPathAttributes; import ch.cyberduck.core.ListProgressListener; import ch.cyberduck.core.Path; import ch.cyberduck.core.PathAttributes; @@ -146,7 +147,7 @@ public class DeepboxAttributesFinderFeature implements AttributesFinder, Attribu final String boxNodeId = fileid.getBoxNodeId(file); // map BoxAccessPolicy to CANLISTCHILDREN and CANADDCHILDREN for third level final Box box = new BoxRestControllerApi(session.getClient()).getBox(deepBoxNodeId, boxNodeId); - final PathAttributes attr = new PathAttributes().setFileId(fileId); + final PathAttributes attr = new DefaultPathAttributes().setFileId(fileId); final Acl acl = new Acl(new Acl.CanonicalUser()); final BoxAccessPolicy boxPolicy = box.getBoxPolicy(); if(containerService.isInbox(file)) { @@ -200,26 +201,26 @@ public class DeepboxAttributesFinderFeature implements AttributesFinder, Attribu } public PathAttributes toAttributes(final Box box) { - final PathAttributes attrs = new PathAttributes(); + final PathAttributes attrs = new DefaultPathAttributes(); attrs.setFileId(box.getBoxNodeId()); return attrs; } public PathAttributes toAttributes(final DeepBox deepBox) { - final PathAttributes attrs = new PathAttributes(); + final PathAttributes attrs = new DefaultPathAttributes(); attrs.setFileId(deepBox.getDeepBoxNodeId()); return attrs; } public PathAttributes toAttributes(final CompanyRoles company) { - final PathAttributes attrs = new PathAttributes(); + final PathAttributes attrs = new DefaultPathAttributes(); attrs.setFileId(company.getGroupId()); return attrs; } @Override public PathAttributes toAttributes(final Node node) { - final PathAttributes attrs = new PathAttributes(); + final PathAttributes attrs = new DefaultPathAttributes(); attrs.setFileId(node.getNodeId()); attrs.setCreationDate(node.getCreated().getTime().getMillis()); attrs.setModificationDate(node.getModified().getTime().getMillis()); diff --git a/deepbox/src/main/java/ch/cyberduck/core/deepbox/DeepboxDirectoryFeature.java b/deepbox/src/main/java/ch/cyberduck/core/deepbox/DeepboxDirectoryFeature.java index b3c16a45b1..8cca83fab6 100644 --- a/deepbox/src/main/java/ch/cyberduck/core/deepbox/DeepboxDirectoryFeature.java +++ b/deepbox/src/main/java/ch/cyberduck/core/deepbox/DeepboxDirectoryFeature.java @@ -17,15 +17,16 @@ package ch.cyberduck.core.deepbox; import ch.cyberduck.core.LocaleFactory; import ch.cyberduck.core.Path; -import ch.cyberduck.core.VersionId; import ch.cyberduck.core.deepbox.io.swagger.client.ApiException; import ch.cyberduck.core.deepbox.io.swagger.client.api.PathRestControllerApi; import ch.cyberduck.core.deepbox.io.swagger.client.model.Folder; import ch.cyberduck.core.deepbox.io.swagger.client.model.FolderAdded; +import ch.cyberduck.core.deepbox.io.swagger.client.model.Node; import ch.cyberduck.core.exception.AccessDeniedException; import ch.cyberduck.core.exception.BackgroundException; import ch.cyberduck.core.exception.ConflictException; import ch.cyberduck.core.features.Directory; +import ch.cyberduck.core.features.Write; import ch.cyberduck.core.transfer.TransferStatus; import org.apache.logging.log4j.LogManager; @@ -34,7 +35,7 @@ import org.apache.logging.log4j.Logger; import java.util.Collections; import java.util.List; -public class DeepboxDirectoryFeature implements Directory { +public class DeepboxDirectoryFeature implements Directory { private static final Logger log = LogManager.getLogger(DeepboxDirectoryFeature.class); private final DeepboxSession session; @@ -48,7 +49,7 @@ public class DeepboxDirectoryFeature implements Directory { } @Override - public Path mkdir(final Path folder, final TransferStatus status) throws BackgroundException { + public Path mkdir(final Write writer, final Path folder, final TransferStatus status) throws BackgroundException { try { if(new DeepboxFindFeature(session, fileid).find(folder)) { throw new ConflictException(folder.getAbsolute()); diff --git a/deepbox/src/main/java/ch/cyberduck/core/deepbox/DeepboxIdProvider.java b/deepbox/src/main/java/ch/cyberduck/core/deepbox/DeepboxIdProvider.java index 1804cecdfb..7e32ae8e50 100644 --- a/deepbox/src/main/java/ch/cyberduck/core/deepbox/DeepboxIdProvider.java +++ b/deepbox/src/main/java/ch/cyberduck/core/deepbox/DeepboxIdProvider.java @@ -16,6 +16,7 @@ package ch.cyberduck.core.deepbox; */ import ch.cyberduck.core.CachingFileIdProvider; +import ch.cyberduck.core.DefaultPathAttributes; import ch.cyberduck.core.Path; import ch.cyberduck.core.PathAttributes; import ch.cyberduck.core.deepbox.io.swagger.client.ApiException; @@ -135,7 +136,7 @@ public class DeepboxIdProvider extends CachingFileIdProvider implements FileIdPr return file; } } - final Path deepbox = new Path(result, deepboxName, type, new PathAttributes(combined.attributes()).setFileId(null)); + final Path deepbox = new Path(result, deepboxName, type, new DefaultPathAttributes(combined.attributes()).setFileId(null)); result = new Path(deepbox, boxName, type, segment.attributes()); } else { diff --git a/deepbox/src/main/java/ch/cyberduck/core/deepbox/DeepboxListService.java b/deepbox/src/main/java/ch/cyberduck/core/deepbox/DeepboxListService.java index d5d566481a..46327ede04 100644 --- a/deepbox/src/main/java/ch/cyberduck/core/deepbox/DeepboxListService.java +++ b/deepbox/src/main/java/ch/cyberduck/core/deepbox/DeepboxListService.java @@ -17,6 +17,8 @@ package ch.cyberduck.core.deepbox; import ch.cyberduck.core.Acl; import ch.cyberduck.core.AttributedList; +import ch.cyberduck.core.DefaultPathAttributes; +import ch.cyberduck.core.DisabledListProgressListener; import ch.cyberduck.core.ListProgressListener; import ch.cyberduck.core.ListService; import ch.cyberduck.core.LocaleFactory; @@ -37,9 +39,7 @@ import ch.cyberduck.core.deepbox.io.swagger.client.model.Overview; import ch.cyberduck.core.deepcloud.DeepcloudExceptionMappingService; import ch.cyberduck.core.deepcloud.io.swagger.client.api.UsersApi; import ch.cyberduck.core.deepcloud.io.swagger.client.model.CompanyRoles; -import ch.cyberduck.core.deepcloud.io.swagger.client.model.StructureEnum; import ch.cyberduck.core.deepcloud.io.swagger.client.model.UserFull; -import ch.cyberduck.core.deepcloud.io.swagger.client.model.VerificationStateEnum; import ch.cyberduck.core.exception.AccessDeniedException; import ch.cyberduck.core.exception.BackgroundException; import ch.cyberduck.core.preferences.HostPreferencesFactory; @@ -330,7 +330,7 @@ public class DeepboxListService implements ListService { } while(offset < size); final Path shared = new Path(directory, containerService.getPinnedLocalization(SHARED), EnumSet.of(Path.Type.directory, Path.Type.volume)); - if(!new SharedWithMeListService(companyId).list(shared, listener).isEmpty()) { + if(!new SharedWithMeListService(companyId).list(shared, new DisabledListProgressListener()).isEmpty()) { list.add(shared); listener.chunk(directory, list); } @@ -360,7 +360,7 @@ public class DeepboxListService implements ListService { list.add(new Path(directory, String.format("%s (%s)", DeepboxPathNormalizer.name(box.getCompany().getDisplayName()), DeepboxPathNormalizer.name(box.getBoxName())), EnumSet.of(Path.Type.directory, Path.Type.volume), - new PathAttributes().setFileId(box.getBoxNodeId()).setCustom(DeepboxIdProvider.DEEPBOX_NAME_PROEPRTY_KEY, box.getDeepBoxName())) + new DefaultPathAttributes().setFileId(box.getBoxNodeId()).setCustom(DeepboxIdProvider.DEEPBOX_NAME_PROEPRTY_KEY, box.getDeepBoxName())) ); } // Mark duplicates @@ -382,11 +382,6 @@ public class DeepboxListService implements ListService { final UsersApi rest = new UsersApi(session.getDeepcloudClient()); final UserFull user = rest.usersMeList(); for(CompanyRoles company : user.getCompanies()) { - if(company.getStructure() == StructureEnum.PERSONAL) { - if(company.getVerificationState() == VerificationStateEnum.NONE) { - continue; - } - } list.add(new Path(directory, DeepboxPathNormalizer.name(company.getDisplayName()), EnumSet.of(Path.Type.directory, Path.Type.volume), attributes.toAttributes(company)) ); diff --git a/deepbox/src/main/java/ch/cyberduck/core/deepbox/DeepboxMoveFeature.java b/deepbox/src/main/java/ch/cyberduck/core/deepbox/DeepboxMoveFeature.java index fd77782e5e..79411906a5 100644 --- a/deepbox/src/main/java/ch/cyberduck/core/deepbox/DeepboxMoveFeature.java +++ b/deepbox/src/main/java/ch/cyberduck/core/deepbox/DeepboxMoveFeature.java @@ -17,6 +17,7 @@ package ch.cyberduck.core.deepbox; import ch.cyberduck.core.Acl; import ch.cyberduck.core.ConnectionCallback; +import ch.cyberduck.core.DefaultPathAttributes; import ch.cyberduck.core.LocaleFactory; import ch.cyberduck.core.Path; import ch.cyberduck.core.PathAttributes; @@ -74,7 +75,7 @@ public class DeepboxMoveFeature implements Move { } fileid.cache(file, null); fileid.cache(renamed, sourceId); - return new Path(renamed).withAttributes(new PathAttributes(file.attributes()).setFileId(sourceId)); + return new Path(renamed).withAttributes(new DefaultPathAttributes(file.attributes()).setFileId(sourceId)); } catch(ApiException e) { throw new DeepboxExceptionMappingService(fileid).map("Cannot rename {0}", e, file); diff --git a/deepbox/src/main/java/ch/cyberduck/core/deepbox/DeepboxReadFeature.java b/deepbox/src/main/java/ch/cyberduck/core/deepbox/DeepboxReadFeature.java index 82912e22c6..4ca05bd7b9 100644 --- a/deepbox/src/main/java/ch/cyberduck/core/deepbox/DeepboxReadFeature.java +++ b/deepbox/src/main/java/ch/cyberduck/core/deepbox/DeepboxReadFeature.java @@ -52,11 +52,15 @@ import java.io.InputStream; import java.net.URI; import java.text.MessageFormat; import java.time.Duration; +import java.util.Map; +import java.util.Optional; +import java.util.TreeMap; import java.util.concurrent.CountDownLatch; import java.util.concurrent.ExecutionException; import java.util.concurrent.ScheduledFuture; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicReference; +import java.util.stream.Collectors; import com.google.common.base.Throwables; import com.google.common.util.concurrent.Uninterruptibles; @@ -68,10 +72,16 @@ public class DeepboxReadFeature implements Read { private final DeepboxSession session; private final DeepboxIdProvider fileid; + private final Map delays; public DeepboxReadFeature(final DeepboxSession session, final DeepboxIdProvider fileid) { this.session = session; this.fileid = fileid; + this.delays = new TreeMap<>(HostPreferencesFactory.get(session.getHost()).getMap("deepbox.download.interval.ms") + .entrySet() + .stream() + .collect(Collectors.toMap(entry -> Long.parseLong(entry.getKey()), + entry -> Long.parseLong(entry.getValue())))).descendingMap(); } protected void poll(final String downloadId) throws BackgroundException { @@ -88,32 +98,37 @@ public class DeepboxReadFeature implements Read { final long timeout = HostPreferencesFactory.get(session.getHost()).getLong("deepbox.download.interrupt.ms"); final long start = System.currentTimeMillis(); try { - final ScheduledFuture f = scheduler.repeat(() -> { - try { - if(System.currentTimeMillis() - start > timeout) { - failure.set(new ConnectionCanceledException(String.format("Interrupt polling for download URL after %d", timeout))); - signal.countDown(); - return; - } - // Poll status - final DownloadRestControllerApi rest = new DownloadRestControllerApi(session.getClient()); - final Download.StatusEnum status = rest.downloadStatus(downloadId, null).getStatus(); - switch(status) { - case READY: - case READY_WITH_ISSUES: + final ScheduledFuture f = scheduler.schedule(new Runnable() { + @Override + public void run() { + try { + if(System.currentTimeMillis() - start > timeout) { + failure.set(new ConnectionCanceledException(String.format("Interrupt polling for download URL after %d", timeout))); signal.countDown(); return; - default: - log.warn("Wait for download URL to become ready with current status {}", status); - break; + } + // Poll status + final DownloadRestControllerApi rest = new DownloadRestControllerApi(session.getClient()); + final Download.StatusEnum status = rest.downloadStatus(downloadId, null).getStatus(); + switch(status) { + case READY: + case READY_WITH_ISSUES: + signal.countDown(); + return; + default: + log.warn("Wait for download URL to become ready with current status {}", status); + break; + } + // Reschedule with custom delay + scheduler.schedule(this, getDelay(System.currentTimeMillis() - start), TimeUnit.MILLISECONDS); + } + catch(ApiException e) { + log.warn(String.format("Failure processing scheduled task. %s", e.getMessage())); + failure.set(new DeepboxExceptionMappingService(fileid).map(e)); + signal.countDown(); } } - catch(ApiException e) { - log.warn(String.format("Failure processing scheduled task. %s", e.getMessage())); - failure.set(new DeepboxExceptionMappingService(fileid).map(e)); - signal.countDown(); - } - }, HostPreferencesFactory.get(session.getHost()).getLong("deepbox.download.interval.ms"), TimeUnit.MILLISECONDS); + }, getDelay(0), TimeUnit.MILLISECONDS); while(!Uninterruptibles.awaitUninterruptibly(signal, Duration.ofSeconds(1))) { try { if(f.isDone()) { @@ -134,6 +149,14 @@ public class DeepboxReadFeature implements Read { } } + private Long getDelay(final long msPassed) { + final Optional d = delays.entrySet().stream() + .filter(e -> e.getKey() <= msPassed) + .map(Map.Entry::getValue) + .findFirst(); + return d.isPresent() ? d.get() : 0; + } + @Override public InputStream read(final Path file, final TransferStatus status, final ConnectionCallback callback) throws BackgroundException { try { diff --git a/deepbox/src/main/java/ch/cyberduck/core/deepbox/DeepboxSession.java b/deepbox/src/main/java/ch/cyberduck/core/deepbox/DeepboxSession.java index f739b08a18..057cb55d1f 100644 --- a/deepbox/src/main/java/ch/cyberduck/core/deepbox/DeepboxSession.java +++ b/deepbox/src/main/java/ch/cyberduck/core/deepbox/DeepboxSession.java @@ -46,14 +46,11 @@ import ch.cyberduck.core.features.Touch; import ch.cyberduck.core.features.Trash; import ch.cyberduck.core.features.Write; import ch.cyberduck.core.http.CustomServiceUnavailableRetryStrategy; -import ch.cyberduck.core.http.ExecutionCountServiceUnavailableRetryStrategy; import ch.cyberduck.core.http.HttpSession; import ch.cyberduck.core.jersey.HttpComponentsProvider; import ch.cyberduck.core.oauth.OAuth2AuthorizationService; import ch.cyberduck.core.oauth.OAuth2ErrorResponseInterceptor; import ch.cyberduck.core.oauth.OAuth2RequestInterceptor; -import ch.cyberduck.core.preferences.HostPreferences; -import ch.cyberduck.core.preferences.HostPreferencesFactory; import ch.cyberduck.core.preferences.PreferencesFactory; import ch.cyberduck.core.proxy.ProxyFinder; import ch.cyberduck.core.ssl.X509KeyManager; @@ -81,7 +78,6 @@ import java.io.IOException; public class DeepboxSession extends HttpSession { private static final Logger log = LogManager.getLogger(DeepboxSession.class); - private final HostPreferences preferences = HostPreferencesFactory.get(host); private final DeepboxIdProvider fileid = new DeepboxIdProvider(this); private DeepcloudApiClient deepcloudClient; @@ -115,7 +111,7 @@ public class DeepboxSession extends HttpSession { .withRedirectUri(host.getProtocol().getOAuthRedirectUrl() ); configuration.setServiceUnavailableRetryStrategy(new CustomServiceUnavailableRetryStrategy(host, - new ExecutionCountServiceUnavailableRetryStrategy(new OAuth2ErrorResponseInterceptor(host, authorizationService)))); + new OAuth2ErrorResponseInterceptor(host, authorizationService))); configuration.addInterceptorLast(authorizationService); final String locale = this.pinLocalization(); configuration.addInterceptorLast((HttpRequestInterceptor) (request, context) -> request.addHeader("Accept-Language", locale)); @@ -169,7 +165,8 @@ public class DeepboxSession extends HttpSession { @Override public void login(final LoginCallback prompt, final CancelCallback cancel) throws BackgroundException { - final Credentials credentials = authorizationService.validate(); + final Credentials credentials = host.getCredentials(); + credentials.setOauth(authorizationService.validate(credentials.getOauth())); try { final Me me = new UserRestControllerApi(client).usersMe(null, null); log.debug("Authenticated for user {}", me); @@ -185,8 +182,15 @@ public class DeepboxSession extends HttpSession { } @Override - protected void logout() { - client.getHttpClient().close(); + public void disconnect() throws BackgroundException { + try { + if(client != null) { + client.getHttpClient().close(); + } + } + finally { + super.disconnect(); + } } @Override @@ -242,8 +246,9 @@ public class DeepboxSession extends HttpSession { public String getStage() { // For now, required for descriptive URL, API forthcoming - // api.[.]deepbox.swiss + // api.[.]deepbox.swiss, defaults to app if no stage is specified String hostname = this.getHost().getHostname(); - return hostname.replaceAll("^api\\.", "").replaceAll("deepbox\\.swiss$", ""); + final String stage = hostname.replaceAll("^api\\.", "").replaceAll("deepbox\\.swiss$", ""); + return StringUtils.isBlank(stage) ? "app." : stage; } } diff --git a/deepbox/src/main/java/ch/cyberduck/core/deepbox/DeepboxTouchFeature.java b/deepbox/src/main/java/ch/cyberduck/core/deepbox/DeepboxTouchFeature.java index c18a24bb8c..15c5d5b835 100644 --- a/deepbox/src/main/java/ch/cyberduck/core/deepbox/DeepboxTouchFeature.java +++ b/deepbox/src/main/java/ch/cyberduck/core/deepbox/DeepboxTouchFeature.java @@ -34,7 +34,7 @@ public class DeepboxTouchFeature extends DefaultTouchFeature { private final DeepboxPathContainerService containerService; public DeepboxTouchFeature(final DeepboxSession session, final DeepboxIdProvider fileid) { - super(new DeepboxWriteFeature(session, fileid)); + super(session); this.containerService = new DeepboxPathContainerService(session, fileid); } diff --git a/deepbox/src/main/java/ch/cyberduck/core/deepbox/DeepboxTrashFeature.java b/deepbox/src/main/java/ch/cyberduck/core/deepbox/DeepboxTrashFeature.java index 2814ce9aa5..10c7665aff 100644 --- a/deepbox/src/main/java/ch/cyberduck/core/deepbox/DeepboxTrashFeature.java +++ b/deepbox/src/main/java/ch/cyberduck/core/deepbox/DeepboxTrashFeature.java @@ -67,7 +67,7 @@ public class DeepboxTrashFeature implements Trash { } @Override - public EnumSet features() { + public EnumSet features(final Path file) { return EnumSet.of(Flags.recursive); } diff --git a/deepbox/src/test/java/ch/cyberduck/core/deepbox/DeepboxCopyFeatureTest.java b/deepbox/src/test/java/ch/cyberduck/core/deepbox/DeepboxCopyFeatureTest.java index 977101eef3..b522b9c4f7 100644 --- a/deepbox/src/test/java/ch/cyberduck/core/deepbox/DeepboxCopyFeatureTest.java +++ b/deepbox/src/test/java/ch/cyberduck/core/deepbox/DeepboxCopyFeatureTest.java @@ -16,6 +16,7 @@ package ch.cyberduck.core.deepbox; */ import ch.cyberduck.core.AlphanumericRandomStringService; +import ch.cyberduck.core.DefaultPathAttributes; import ch.cyberduck.core.DisabledConnectionCallback; import ch.cyberduck.core.DisabledLoginCallback; import ch.cyberduck.core.Path; @@ -44,16 +45,16 @@ public class DeepboxCopyFeatureTest extends AbstractDeepboxTest { final DeepboxIdProvider fileid = new DeepboxIdProvider(session); final Path documents = new Path("/ORG 4 - DeepBox Desktop App/ORG 4 - DeepBox Desktop App/ORG3:Box1/Documents/", EnumSet.of(Path.Type.directory, Path.Type.volume)); final Path test = new Path(documents, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)); - new DeepboxTouchFeature(session, fileid).touch(test, new TransferStatus()); + new DeepboxTouchFeature(session, fileid).touch(new DeepboxWriteFeature(session, fileid), test, new TransferStatus()); final Path copy = new Path(documents, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)); new DeepboxCopyFeature(session, fileid).copy(test, copy, new TransferStatus(), new DisabledConnectionCallback(), new DisabledStreamListener()); try { - assertTrue(new DeepboxFindFeature(session, fileid).find(test.withAttributes(new PathAttributes()))); - assertTrue(new DeepboxFindFeature(session, fileid).find(copy.withAttributes(new PathAttributes()))); + assertTrue(new DeepboxFindFeature(session, fileid).find(test.withAttributes(new DefaultPathAttributes()))); + assertTrue(new DeepboxFindFeature(session, fileid).find(copy.withAttributes(new DefaultPathAttributes()))); } finally { - new DeepboxDeleteFeature(session, fileid).delete(Collections.singletonList(test.withAttributes(new PathAttributes())), new DisabledLoginCallback(), new Delete.DisabledCallback()); - new DeepboxDeleteFeature(session, fileid).delete(Collections.singletonList(copy.withAttributes(new PathAttributes())), new DisabledLoginCallback(), new Delete.DisabledCallback()); + new DeepboxDeleteFeature(session, fileid).delete(Collections.singletonList(test.withAttributes(new DefaultPathAttributes())), new DisabledLoginCallback(), new Delete.DisabledCallback()); + new DeepboxDeleteFeature(session, fileid).delete(Collections.singletonList(copy.withAttributes(new DefaultPathAttributes())), new DisabledLoginCallback(), new Delete.DisabledCallback()); } } @@ -61,11 +62,11 @@ public class DeepboxCopyFeatureTest extends AbstractDeepboxTest { public void testCopyDirectory() throws Exception { final DeepboxIdProvider fileid = new DeepboxIdProvider(session); final Path documents = new Path("/ORG 4 - DeepBox Desktop App/ORG 4 - DeepBox Desktop App/ORG3:Box1/Documents/", EnumSet.of(Path.Type.directory, Path.Type.volume)); - final Path directory = new DeepboxDirectoryFeature(session, fileid).mkdir(new Path(documents, + final Path directory = new DeepboxDirectoryFeature(session, fileid).mkdir(new DeepboxWriteFeature(session, fileid), new Path(documents, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory)), new TransferStatus()); final Path copy = new Path(documents, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory)); assertThrows(UnsupportedException.class, () -> new DeepboxCopyFeature(session, fileid).preflight(directory, Optional.of(copy))); - new DeepboxDeleteFeature(session, fileid).delete(Collections.singletonList(directory.withAttributes(new PathAttributes())), new DisabledLoginCallback(), new Delete.DisabledCallback()); + new DeepboxDeleteFeature(session, fileid).delete(Collections.singletonList(directory.withAttributes(new DefaultPathAttributes())), new DisabledLoginCallback(), new Delete.DisabledCallback()); } @Test @@ -85,26 +86,26 @@ public class DeepboxCopyFeatureTest extends AbstractDeepboxTest { final Path trash = new Path("/ORG 4 - DeepBox Desktop App/ORG 4 - DeepBox Desktop App/ORG3:Box1/Trash", EnumSet.of(Path.Type.directory, Path.Type.volume)); final Path test = new DeepboxTouchFeature(session, fileid).touch( - new Path(documents, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)), new TransferStatus()); + new DeepboxWriteFeature(session, fileid), new Path(documents, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)), new TransferStatus()); final Path target = new DeepboxTouchFeature(session, fileid).touch( - new Path(documents, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)), new TransferStatus()); + new DeepboxWriteFeature(session, fileid), new Path(documents, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)), new TransferStatus()); final Path targetInTrash = new Path(trash, target.getName(), target.getType()); final PathAttributes originalTestAttributes = new DeepboxAttributesFinderFeature(session, fileid).find(test); final PathAttributes originalTargetAttributes = new DeepboxAttributesFinderFeature(session, fileid).find(target); new DeepboxCopyFeature(session, fileid).copy(test, target, new TransferStatus().setExists(true), new DisabledConnectionCallback(), new DisabledStreamListener()); - assertTrue(new DeepboxFindFeature(session, fileid).find(test.withAttributes(new PathAttributes()))); - assertTrue(new DeepboxFindFeature(session, fileid).find(target.withAttributes(new PathAttributes()))); + assertTrue(new DeepboxFindFeature(session, fileid).find(test.withAttributes(new DefaultPathAttributes()))); + assertTrue(new DeepboxFindFeature(session, fileid).find(target.withAttributes(new DefaultPathAttributes()))); assertTrue(new DeepboxFindFeature(session, fileid).find(targetInTrash)); - final PathAttributes overriddenTargetAttributes = new DeepboxAttributesFinderFeature(session, fileid).find(target.withAttributes(new PathAttributes())); + final PathAttributes overriddenTargetAttributes = new DeepboxAttributesFinderFeature(session, fileid).find(target.withAttributes(new DefaultPathAttributes())); assertNotNull(originalTestAttributes.getFileId()); assertNotEquals(originalTestAttributes.getFileId(), overriddenTargetAttributes.getFileId()); assertNotEquals(originalTestAttributes.getModificationDate(), overriddenTargetAttributes.getModificationDate()); assertEquals(originalTestAttributes.getChecksum(), overriddenTargetAttributes.getChecksum()); - final PathAttributes trashedTargetAttributes = new DeepboxAttributesFinderFeature(session, fileid).find(targetInTrash.withAttributes(new PathAttributes())); + final PathAttributes trashedTargetAttributes = new DeepboxAttributesFinderFeature(session, fileid).find(targetInTrash.withAttributes(new DefaultPathAttributes())); assertNotNull(originalTargetAttributes.getFileId()); assertEquals(originalTargetAttributes.getFileId(), trashedTargetAttributes.getFileId()); assertEquals(originalTargetAttributes.getModificationDate(), trashedTargetAttributes.getModificationDate()); diff --git a/deepbox/src/test/java/ch/cyberduck/core/deepbox/DeepboxDirectoryFeatureTest.java b/deepbox/src/test/java/ch/cyberduck/core/deepbox/DeepboxDirectoryFeatureTest.java index be32b3ef4b..1864a6ec3f 100644 --- a/deepbox/src/test/java/ch/cyberduck/core/deepbox/DeepboxDirectoryFeatureTest.java +++ b/deepbox/src/test/java/ch/cyberduck/core/deepbox/DeepboxDirectoryFeatureTest.java @@ -16,6 +16,7 @@ package ch.cyberduck.core.deepbox; */ import ch.cyberduck.core.AlphanumericRandomStringService; +import ch.cyberduck.core.DefaultPathAttributes; import ch.cyberduck.core.DisabledListProgressListener; import ch.cyberduck.core.DisabledLoginCallback; import ch.cyberduck.core.Path; @@ -46,7 +47,7 @@ public class DeepboxDirectoryFeatureTest extends AbstractDeepboxTest { final Path parent = new Path("/", EnumSet.of(Path.Type.directory)); final Path folder = new Path(parent, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory)); assertThrows(AccessDeniedException.class, () -> directory.preflight(parent, folder.getName())); - assertThrows(NotfoundException.class, () -> directory.mkdir(folder, new TransferStatus())); + assertThrows(NotfoundException.class, () -> directory.mkdir(new DeepboxWriteFeature(session, nodeid), folder, new TransferStatus())); } @Test @@ -56,7 +57,7 @@ public class DeepboxDirectoryFeatureTest extends AbstractDeepboxTest { final Path parent = new Path("/ORG 4 - DeepBox Desktop App/", EnumSet.of(Path.Type.directory)); final Path folder = new Path(parent, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory)); assertThrows(AccessDeniedException.class, () -> directory.preflight(parent, folder.getName())); - assertThrows(NotfoundException.class, () -> directory.mkdir(folder, new TransferStatus())); + assertThrows(NotfoundException.class, () -> directory.mkdir(new DeepboxWriteFeature(session, nodeid), folder, new TransferStatus())); } @Test @@ -66,7 +67,7 @@ public class DeepboxDirectoryFeatureTest extends AbstractDeepboxTest { final Path parent = new Path(String.format("/ORG 1 - DeepBox Desktop App/%s", DeepboxListService.SHARED), EnumSet.of(Path.Type.directory)); final Path folder = new Path(parent, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory)); assertThrows(AccessDeniedException.class, () -> directory.preflight(parent, folder.getName())); - assertThrows(NotfoundException.class, () -> directory.mkdir(folder, new TransferStatus())); + assertThrows(NotfoundException.class, () -> directory.mkdir(new DeepboxWriteFeature(session, nodeid), folder, new TransferStatus())); } @Test @@ -76,7 +77,7 @@ public class DeepboxDirectoryFeatureTest extends AbstractDeepboxTest { final Path parent = new Path("/ORG 4 - DeepBox Desktop App/ORG 4 - DeepBox Desktop App/ORG3:Box1", EnumSet.of(Path.Type.directory)); final Path folder = new Path(parent, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory)); assertThrows(AccessDeniedException.class, () -> directory.preflight(parent, folder.getName())); - assertThrows(AccessDeniedException.class, () -> directory.mkdir(folder, new TransferStatus())); + assertThrows(AccessDeniedException.class, () -> directory.mkdir(new DeepboxWriteFeature(session, nodeid), folder, new TransferStatus())); } @Test @@ -86,7 +87,7 @@ public class DeepboxDirectoryFeatureTest extends AbstractDeepboxTest { final Path parent = new Path(String.format("/ORG 1 - DeepBox Desktop App/%s/Demo 1 (1 Christian Gruber)", DeepboxListService.SHARED), EnumSet.of(Path.Type.directory)); final Path folder = new Path(parent, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory)); assertThrows(AccessDeniedException.class, () -> directory.preflight(parent, folder.getName())); - assertThrows(AccessDeniedException.class, () -> directory.mkdir(folder, new TransferStatus())); + assertThrows(AccessDeniedException.class, () -> directory.mkdir(new DeepboxWriteFeature(session, nodeid), folder, new TransferStatus())); } @Test @@ -96,7 +97,7 @@ public class DeepboxDirectoryFeatureTest extends AbstractDeepboxTest { final Path parent = new Path("/ORG 4 - DeepBox Desktop App/ORG 4 - DeepBox Desktop App/ORG3:Box1/Inbox", EnumSet.of(Path.Type.directory)); final Path folder = new Path(parent, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory)); assertThrows(AccessDeniedException.class, () -> directory.preflight(parent, folder.getName())); - assertThrows(InteroperabilityException.class, () -> directory.mkdir(folder, new TransferStatus())); + assertThrows(InteroperabilityException.class, () -> directory.mkdir(new DeepboxWriteFeature(session, nodeid), folder, new TransferStatus())); } @Test @@ -106,7 +107,7 @@ public class DeepboxDirectoryFeatureTest extends AbstractDeepboxTest { final Path parent = new Path(String.format("/ORG 1 - DeepBox Desktop App/%s/Demo 1 (1 Christian Gruber)/Inbox", DeepboxListService.SHARED), EnumSet.of(Path.Type.directory)); final Path folder = new Path(parent, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory)); assertThrows(AccessDeniedException.class, () -> directory.preflight(parent, folder.getName())); - assertThrows(InteroperabilityException.class, () -> directory.mkdir(folder, new TransferStatus())); + assertThrows(InteroperabilityException.class, () -> directory.mkdir(new DeepboxWriteFeature(session, nodeid), folder, new TransferStatus())); } @Test @@ -116,7 +117,7 @@ public class DeepboxDirectoryFeatureTest extends AbstractDeepboxTest { final Path parent = new Path("/ORG 4 - DeepBox Desktop App/ORG 4 - DeepBox Desktop App/ORG3:Box1/Trash", EnumSet.of(Path.Type.directory)); final Path folder = new Path(parent, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory)); assertThrows(AccessDeniedException.class, () -> directory.preflight(parent, folder.getName())); - assertThrows(AccessDeniedException.class, () -> directory.mkdir(folder, new TransferStatus())); + assertThrows(AccessDeniedException.class, () -> directory.mkdir(new DeepboxWriteFeature(session, nodeid), folder, new TransferStatus())); } @Test @@ -125,12 +126,12 @@ public class DeepboxDirectoryFeatureTest extends AbstractDeepboxTest { final DeepboxDirectoryFeature directory = new DeepboxDirectoryFeature(session, nodeid); final Path parent = new Path("/ORG 4 - DeepBox Desktop App/ORG 4 - DeepBox Desktop App/ORG3:Box1/Documents", EnumSet.of(Path.Type.directory)); final Path folder = new Path(parent, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory)); - directory.mkdir(folder, new TransferStatus()); - assertTrue(new DeepboxFindFeature(session, nodeid).find(folder.withAttributes(new PathAttributes()), new DisabledListProgressListener())); + directory.mkdir(new DeepboxWriteFeature(session, nodeid), folder, new TransferStatus()); + assertTrue(new DeepboxFindFeature(session, nodeid).find(folder.withAttributes(new DefaultPathAttributes()), new DisabledListProgressListener())); assertEquals(0, new DeepboxListService(session, nodeid).list(folder, new DisabledListProgressListener()).size()); new DeepboxDeleteFeature(session, nodeid).delete(Collections.singletonList(folder), new DisabledLoginCallback(), new Delete.DisabledCallback()); - assertThrows(NotfoundException.class, () -> nodeid.getFileId(folder.withAttributes(new PathAttributes()))); - assertFalse(new DeepboxFindFeature(session, nodeid).find(folder.withAttributes(new PathAttributes()))); + assertThrows(NotfoundException.class, () -> nodeid.getFileId(folder.withAttributes(new DefaultPathAttributes()))); + assertFalse(new DeepboxFindFeature(session, nodeid).find(folder.withAttributes(new DefaultPathAttributes()))); } @Test @@ -139,7 +140,7 @@ public class DeepboxDirectoryFeatureTest extends AbstractDeepboxTest { final DeepboxDirectoryFeature directory = new DeepboxDirectoryFeature(session, nodeid); final Path parent = new Path("/ORG 4 - DeepBox Desktop App/ORG 4 - DeepBox Desktop App/ORG3:Box1/Documents/Bookkeeping", EnumSet.of(Path.Type.directory)); final Path folder = new Path(parent, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory)); - directory.mkdir(folder, new TransferStatus()); + directory.mkdir(new DeepboxWriteFeature(session, nodeid), folder, new TransferStatus()); assertEquals(0, new DeepboxListService(session, nodeid).list(folder, new DisabledListProgressListener()).size()); new DeepboxDeleteFeature(session, nodeid).delete(Collections.singletonList(folder), new DisabledLoginCallback(), new Delete.DisabledCallback()); assertFalse(new DeepboxFindFeature(session, nodeid).find(folder)); @@ -147,12 +148,12 @@ public class DeepboxDirectoryFeatureTest extends AbstractDeepboxTest { @Test public void testNoDuplicates() throws Exception { - final DeepboxIdProvider fileid = new DeepboxIdProvider(session); + final DeepboxIdProvider nodeid = new DeepboxIdProvider(session); final Path documents = new Path("/ORG 4 - DeepBox Desktop App/ORG 4 - DeepBox Desktop App/ORG3:Box1/Documents/", EnumSet.of(Path.Type.directory, Path.Type.volume)); - final Path test = new DeepboxDirectoryFeature(session, fileid).mkdir(new Path(documents, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory)), new TransferStatus()); - new DeepboxDirectoryFeature(session, fileid).preflight(documents.withAttributes(new DeepboxAttributesFinderFeature(session, fileid).find(documents)), test.getName()); - assertTrue(new DeepboxFindFeature(session, fileid).find(test)); - new DeepboxDeleteFeature(session, fileid).delete(Collections.singletonList(test), new DisabledLoginCallback(), new Delete.DisabledCallback()); + final Path test = new DeepboxDirectoryFeature(session, nodeid).mkdir(new DeepboxWriteFeature(session, nodeid), new Path(documents, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory)), new TransferStatus()); + new DeepboxDirectoryFeature(session, nodeid).preflight(documents.withAttributes(new DeepboxAttributesFinderFeature(session, nodeid).find(documents)), test.getName()); + assertTrue(new DeepboxFindFeature(session, nodeid).find(test)); + new DeepboxDeleteFeature(session, nodeid).delete(Collections.singletonList(test), new DisabledLoginCallback(), new Delete.DisabledCallback()); } @Test @@ -161,9 +162,9 @@ public class DeepboxDirectoryFeatureTest extends AbstractDeepboxTest { final DeepboxDirectoryFeature directory = new DeepboxDirectoryFeature(session, nodeid); final Path parent = new Path("/ORG 4 - DeepBox Desktop App/ORG 4 - DeepBox Desktop App/ORG3:Box1/Documents/Bookkeeping", EnumSet.of(Path.Type.directory)); final Path folder = new Path(parent, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory)); - directory.mkdir(folder, new TransferStatus()); + directory.mkdir(new DeepboxWriteFeature(session, nodeid), folder, new TransferStatus()); assertEquals(0, new DeepboxListService(session, nodeid).list(folder, new DisabledListProgressListener()).size()); - assertThrows(ConflictException.class, () -> directory.mkdir(folder, new TransferStatus())); + assertThrows(ConflictException.class, () -> directory.mkdir(new DeepboxWriteFeature(session, nodeid), folder, new TransferStatus())); new DeepboxDeleteFeature(session, nodeid).delete(Collections.singletonList(folder), new DisabledLoginCallback(), new Delete.DisabledCallback()); assertFalse(new DeepboxFindFeature(session, nodeid).find(folder)); } diff --git a/deepbox/src/test/java/ch/cyberduck/core/deepbox/DeepboxFindFeatureTest.java b/deepbox/src/test/java/ch/cyberduck/core/deepbox/DeepboxFindFeatureTest.java index 118d9efcfd..817aca4a65 100644 --- a/deepbox/src/test/java/ch/cyberduck/core/deepbox/DeepboxFindFeatureTest.java +++ b/deepbox/src/test/java/ch/cyberduck/core/deepbox/DeepboxFindFeatureTest.java @@ -58,7 +58,7 @@ public class DeepboxFindFeatureTest extends AbstractDeepboxTest { final DeepboxIdProvider nodeid = new DeepboxIdProvider(session); final Path box = new Path("/ORG 4 - DeepBox Desktop App/ORG 4 - DeepBox Desktop App/ORG3:Box1/Documents", EnumSet.of(Path.Type.directory, Path.Type.volume)); final Path folder = new DeepboxDirectoryFeature(session, nodeid).mkdir( - new Path(box, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory)), new TransferStatus()); + new DeepboxWriteFeature(session, nodeid), new Path(box, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory)), new TransferStatus()); assertTrue(new DeepboxFindFeature(session, nodeid).find(folder)); assertFalse(new DeepboxFindFeature(session, nodeid).find(new Path(folder.getAbsolute(), EnumSet.of(Path.Type.file)))); new DeepboxDeleteFeature(session, nodeid).delete(Collections.singletonList(folder), new DisabledLoginCallback(), new Delete.DisabledCallback()); @@ -69,7 +69,7 @@ public class DeepboxFindFeatureTest extends AbstractDeepboxTest { final Path box = new Path("/ORG 4 - DeepBox Desktop App/ORG 4 - DeepBox Desktop App/ORG3:Box1/Documents", EnumSet.of(Path.Type.directory, Path.Type.volume)); final Path file = new Path(box, StringUtils.lowerCase(new AlphanumericRandomStringService().random()), EnumSet.of(Path.Type.file)); final DeepboxIdProvider nodeid = new DeepboxIdProvider(session); - new DeepboxTouchFeature(session, nodeid).touch(file, new TransferStatus()); + new DeepboxTouchFeature(session, nodeid).touch(new DeepboxWriteFeature(session, nodeid), file, new TransferStatus()); assertTrue(new DeepboxFindFeature(session, nodeid).find(file)); assertFalse(new DeepboxFindFeature(session, nodeid).find(new Path(box, StringUtils.upperCase(file.getName()), EnumSet.of(Path.Type.file)))); assertFalse(new DeepboxFindFeature(session, nodeid).find(new Path(file.getAbsolute(), EnumSet.of(Path.Type.directory)))); diff --git a/deepbox/src/test/java/ch/cyberduck/core/deepbox/DeepboxListServiceTest.java b/deepbox/src/test/java/ch/cyberduck/core/deepbox/DeepboxListServiceTest.java index de056e92aa..5a80593acd 100644 --- a/deepbox/src/test/java/ch/cyberduck/core/deepbox/DeepboxListServiceTest.java +++ b/deepbox/src/test/java/ch/cyberduck/core/deepbox/DeepboxListServiceTest.java @@ -18,6 +18,7 @@ package ch.cyberduck.core.deepbox; import ch.cyberduck.core.Acl; import ch.cyberduck.core.AlphanumericRandomStringService; import ch.cyberduck.core.AttributedList; +import ch.cyberduck.core.DefaultPathAttributes; import ch.cyberduck.core.DisabledListProgressListener; import ch.cyberduck.core.DisabledLoginCallback; import ch.cyberduck.core.Path; @@ -71,7 +72,7 @@ public class DeepboxListServiceTest extends AbstractDeepboxTest { // no modification/creation date for DeepBoxes assertTrue(f.attributes().getModificationDate() < 0); assertTrue(f.attributes().getCreationDate() < 0); - assertNotNull(nodeid.getFileId(new Path(f).withAttributes(new PathAttributes()))); + assertNotNull(nodeid.getFileId(new Path(f).withAttributes(new DefaultPathAttributes()))); assertEquals(f.attributes(), new DeepboxAttributesFinderFeature(session, nodeid).find(new Path(f.getAbsolute(), f.getType()))); } } @@ -91,7 +92,7 @@ public class DeepboxListServiceTest extends AbstractDeepboxTest { // no modification/creation date for Companies assertTrue(f.attributes().getModificationDate() < 0); assertTrue(f.attributes().getCreationDate() < 0); - assertNotNull(nodeid.getFileId(new Path(f).withAttributes(new PathAttributes()))); + assertNotNull(nodeid.getFileId(new Path(f).withAttributes(new DefaultPathAttributes()))); assertEquals(f.attributes(), new DeepboxAttributesFinderFeature(session, nodeid).find(new Path(f.getAbsolute(), f.getType()))); } } @@ -131,7 +132,7 @@ public class DeepboxListServiceTest extends AbstractDeepboxTest { // no modification/creation date for Boxes assertTrue(f.attributes().getModificationDate() < 0); assertTrue(f.attributes().getCreationDate() < 0); - assertNotNull(nodeid.getFileId(new Path(f).withAttributes(new PathAttributes()))); + assertNotNull(nodeid.getFileId(new Path(f).withAttributes(new DefaultPathAttributes()))); assertEquals(f.attributes(), new DeepboxAttributesFinderFeature(session, nodeid).find(new Path(f.getAbsolute(), f.getType()))); } } @@ -151,7 +152,7 @@ public class DeepboxListServiceTest extends AbstractDeepboxTest { // no modification/creation date for Boxes assertTrue(f.attributes().getModificationDate() < 0); assertTrue(f.attributes().getCreationDate() < 0); - assertNotNull(nodeid.getFileId(new Path(f).withAttributes(new PathAttributes()))); + assertNotNull(nodeid.getFileId(new Path(f).withAttributes(new DefaultPathAttributes()))); assertEquals(f.attributes(), new DeepboxAttributesFinderFeature(session, nodeid).find(new Path(f.getAbsolute(), f.getType()))); } } @@ -198,7 +199,7 @@ public class DeepboxListServiceTest extends AbstractDeepboxTest { assertFalse(f.getName().contains(String.valueOf(Path.DELIMITER))); assertTrue(f.attributes().getModificationDate() > 0); assertTrue(f.attributes().getCreationDate() > 0); - assertNotNull(nodeid.getFileId(new Path(f).withAttributes(new PathAttributes()))); + assertNotNull(nodeid.getFileId(new Path(f).withAttributes(new DefaultPathAttributes()))); assertEquals(f.attributes(), new DeepboxAttributesFinderFeature(session, nodeid).find(f)); } } @@ -215,7 +216,7 @@ public class DeepboxListServiceTest extends AbstractDeepboxTest { assertFalse(f.getName().contains(String.valueOf(Path.DELIMITER))); assertTrue(f.attributes().getModificationDate() > 0); assertTrue(f.attributes().getCreationDate() > 0); - assertNotNull(nodeid.getFileId(new Path(f).withAttributes(new PathAttributes()))); + assertNotNull(nodeid.getFileId(new Path(f).withAttributes(new DefaultPathAttributes()))); assertEquals(f.attributes(), new DeepboxAttributesFinderFeature(session, nodeid).find(new Path(f.getAbsolute(), f.getType()))); } } @@ -245,7 +246,7 @@ public class DeepboxListServiceTest extends AbstractDeepboxTest { assertFalse(f.getName().contains(String.valueOf(Path.DELIMITER))); assertTrue(f.attributes().getModificationDate() > 0); assertTrue(f.attributes().getCreationDate() > 0); - assertNotNull(nodeid.getFileId(new Path(f).withAttributes(new PathAttributes()))); + assertNotNull(nodeid.getFileId(new Path(f).withAttributes(new DefaultPathAttributes()))); assertEquals(f.attributes(), new DeepboxAttributesFinderFeature(session, nodeid).find(f)); } } @@ -258,11 +259,11 @@ public class DeepboxListServiceTest extends AbstractDeepboxTest { final Path receipts = new Path("/ORG 4 - DeepBox Desktop App/ORG 4 - DeepBox Desktop App/ORG3:Box1/Documents/Invoices : Receipts/", EnumSet.of(Path.Type.directory)); final Path folder = new Path(receipts, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory)); - new DeepboxDirectoryFeature(session, nodeid).mkdir(folder, new TransferStatus()); + new DeepboxDirectoryFeature(session, nodeid).mkdir(new DeepboxWriteFeature(session, nodeid), folder, new TransferStatus()); try { final int numFiles = chunkSize * 2; for(int i = 0; i < numFiles; ++i) { - new DeepboxTouchFeature(session, nodeid).touch(new Path(folder, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)), new TransferStatus()); + new DeepboxTouchFeature(session, nodeid).touch(new DeepboxWriteFeature(session, nodeid), new Path(folder, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)), new TransferStatus()); } final AttributedList listing = new DeepboxListService(session, nodeid).list(folder, new DisabledListProgressListener()); assertEquals(numFiles, listing.size()); @@ -280,11 +281,11 @@ public class DeepboxListServiceTest extends AbstractDeepboxTest { final Path receipts = new Path("/ORG 4 - DeepBox Desktop App/ORG 4 - DeepBox Desktop App/ORG3:Box1/Documents/Invoices : Receipts/", EnumSet.of(Path.Type.directory)); final Path folder = new Path(receipts, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory)); - new DeepboxDirectoryFeature(session, nodeid).mkdir(folder, new TransferStatus()); + new DeepboxDirectoryFeature(session, nodeid).mkdir(new DeepboxWriteFeature(session, nodeid), folder, new TransferStatus()); try { final int numFiles = (int) Math.floor(chunkSize * 2.5); for(int i = 0; i < numFiles; ++i) { - new DeepboxTouchFeature(session, nodeid).touch(new Path(folder, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)), new TransferStatus()); + new DeepboxTouchFeature(session, nodeid).touch(new DeepboxWriteFeature(session, nodeid), new Path(folder, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)), new TransferStatus()); } final AttributedList listing = new DeepboxListService(session, nodeid).list(folder, new DisabledListProgressListener()); assertEquals(numFiles, listing.size()); @@ -300,7 +301,7 @@ public class DeepboxListServiceTest extends AbstractDeepboxTest { final Path auditing = new Path("/ORG 4 - DeepBox Desktop App/ORG 4 - DeepBox Desktop App/ORG3:Box1/Documents/Invoices : Receipts/", EnumSet.of(Path.Type.directory)); final Path folder = new Path(auditing, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory)); - new DeepboxDirectoryFeature(session, nodeid).mkdir(folder, new TransferStatus()); + new DeepboxDirectoryFeature(session, nodeid).mkdir(new DeepboxWriteFeature(session, nodeid), folder, new TransferStatus()); final Path file = new Path(folder, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)); for(int i = 0; i < 2; i++) { @@ -338,9 +339,9 @@ public class DeepboxListServiceTest extends AbstractDeepboxTest { final DeepboxIdProvider nodeid = new DeepboxIdProvider(session); final Path virtualFolder = new Path("/ORG 4 - DeepBox Desktop App/ORG 4 - DeepBox Desktop App/ORG3:Box1/Documents/Bookkeeping", EnumSet.of(Path.Type.directory, Path.Type.volume)); final Path folder = new Path(virtualFolder, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory)); - new DeepboxDirectoryFeature(session, nodeid).mkdir(folder, new TransferStatus()); + new DeepboxDirectoryFeature(session, nodeid).mkdir(new DeepboxWriteFeature(session, nodeid), folder, new TransferStatus()); final Path file = new Path(folder, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)); - new DeepboxTouchFeature(session, nodeid).touch(file, new TransferStatus()); + new DeepboxTouchFeature(session, nodeid).touch(new DeepboxWriteFeature(session, nodeid), file, new TransferStatus()); final NodeCopy body = new NodeCopy(); body.setTargetParentNodeId(nodeid.getFileId(folder)); @@ -358,9 +359,9 @@ public class DeepboxListServiceTest extends AbstractDeepboxTest { final DeepboxIdProvider nodeid = new DeepboxIdProvider(session); final Path virtualFolder = new Path("/ORG 4 - DeepBox Desktop App/ORG 4 - DeepBox Desktop App/ORG3:Box1/Documents/Bookkeeping", EnumSet.of(Path.Type.directory, Path.Type.volume)); final Path folder = new Path(virtualFolder, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory)); - new DeepboxDirectoryFeature(session, nodeid).mkdir(folder, new TransferStatus()); + new DeepboxDirectoryFeature(session, nodeid).mkdir(new DeepboxWriteFeature(session, nodeid), folder, new TransferStatus()); final Path test = new Path(folder, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory)); - new DeepboxDirectoryFeature(session, nodeid).mkdir(test, new TransferStatus()); + new DeepboxDirectoryFeature(session, nodeid).mkdir(new DeepboxWriteFeature(session, nodeid), test, new TransferStatus()); // /api/v1/nodes/{nodeId}/copy does not work for folders final Folder body = new Folder(); diff --git a/deepbox/src/test/java/ch/cyberduck/core/deepbox/DeepboxMoveFeatureTest.java b/deepbox/src/test/java/ch/cyberduck/core/deepbox/DeepboxMoveFeatureTest.java index f8f7db271d..bf5c9bca46 100644 --- a/deepbox/src/test/java/ch/cyberduck/core/deepbox/DeepboxMoveFeatureTest.java +++ b/deepbox/src/test/java/ch/cyberduck/core/deepbox/DeepboxMoveFeatureTest.java @@ -17,6 +17,7 @@ package ch.cyberduck.core.deepbox; import ch.cyberduck.core.Acl; import ch.cyberduck.core.AlphanumericRandomStringService; +import ch.cyberduck.core.DefaultPathAttributes; import ch.cyberduck.core.DisabledConnectionCallback; import ch.cyberduck.core.DisabledLoginCallback; import ch.cyberduck.core.DisabledPasswordCallback; @@ -52,23 +53,20 @@ public class DeepboxMoveFeatureTest extends AbstractDeepboxTest { public void testMove() throws Exception { final DeepboxIdProvider fileid = new DeepboxIdProvider(session); final Path documents = new Path("/ORG 4 - DeepBox Desktop App/ORG 4 - DeepBox Desktop App/ORG3:Box1/Documents/", EnumSet.of(Path.Type.directory, Path.Type.volume)); - final Path test = new DeepboxTouchFeature(session, fileid).touch(new Path(documents, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)), new TransferStatus()); + final Path test = new DeepboxTouchFeature(session, fileid).touch(new DeepboxWriteFeature(session, fileid), new Path(documents, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)), new TransferStatus()); final String sourceId = test.attributes().getFileId(); assertNotNull(sourceId); - test.withAttributes(new DeepboxAttributesFinderFeature(session, fileid).find(test)); assertNotEquals(TransferStatus.UNKNOWN_LENGTH, test.attributes().getSize()); assertNotEquals(-1L, test.attributes().getModificationDate()); - + assertEquals(test.attributes(), new DeepboxAttributesFinderFeature(session, fileid).find(test)); final Path moved = new DeepboxMoveFeature(session, fileid).move(test, new Path(documents, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)), new TransferStatus(), new Delete.DisabledCallback(), new DisabledConnectionCallback()); assertEquals(sourceId, moved.attributes().getFileId()); - moved.withAttributes(new DeepboxAttributesFinderFeature(session, fileid).find(moved)); - assertFalse(new DeepboxFindFeature(session, fileid).find(new Path(test).withAttributes(new PathAttributes()))); + assertFalse(new DeepboxFindFeature(session, fileid).find(new Path(test).withAttributes(new DefaultPathAttributes()))); assertTrue(new DeepboxFindFeature(session, fileid).find(moved)); assertEquals(test.attributes().getModificationDate(), moved.attributes().getModificationDate()); assertEquals(test.attributes().getChecksum(), moved.attributes().getChecksum()); - assertEquals(Comparison.equal, session.getHost().getProtocol().getFeature(ComparisonService.class).compare(Path.Type.file, moved.attributes(), new DeepboxAttributesFinderFeature(session, fileid).find(moved))); - + assertEquals(Comparison.equal, session.getHost().getProtocol().getFeature(ComparisonService.class).compare(Path.Type.file, test.attributes(), moved.attributes())); new DeepboxDeleteFeature(session, fileid).delete(Collections.singletonList(moved), new DisabledLoginCallback(), new Delete.DisabledCallback()); } @@ -76,14 +74,14 @@ public class DeepboxMoveFeatureTest extends AbstractDeepboxTest { public void testMoveDirectory() throws Exception { final DeepboxIdProvider fileid = new DeepboxIdProvider(session); final Path documents = new Path("/ORG 4 - DeepBox Desktop App/ORG 4 - DeepBox Desktop App/ORG3:Box1/Documents/", EnumSet.of(Path.Type.directory, Path.Type.volume)); - final Path test = new DeepboxDirectoryFeature(session, fileid).mkdir(new Path(documents, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory)), new TransferStatus()); + final Path test = new DeepboxDirectoryFeature(session, fileid).mkdir(new DeepboxWriteFeature(session, fileid), new Path(documents, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory)), new TransferStatus()); final String sourceId = test.attributes().getFileId(); assertNotNull(sourceId); - new DeepboxTouchFeature(session, fileid).touch(new Path(test, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)), new TransferStatus()); + new DeepboxTouchFeature(session, fileid).touch(new DeepboxWriteFeature(session, fileid), new Path(test, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)), new TransferStatus()); final Path target = new Path(documents, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory)); final Path moved = new DeepboxMoveFeature(session, fileid).move(test, target, new TransferStatus(), new Delete.DisabledCallback(), new DisabledConnectionCallback()); assertEquals(sourceId, moved.attributes().getFileId()); - assertFalse(new DeepboxFindFeature(session, fileid).find(test.withAttributes(new PathAttributes()))); + assertFalse(new DeepboxFindFeature(session, fileid).find(test.withAttributes(new DefaultPathAttributes()))); assertTrue(new DeepboxFindFeature(session, fileid).find(target)); new DeepboxDeleteFeature(session, fileid).delete(Collections.singletonList(target), new DisabledLoginCallback(), new Delete.DisabledCallback()); @@ -96,7 +94,7 @@ public class DeepboxMoveFeatureTest extends AbstractDeepboxTest { final Path trash = new Path("/ORG 4 - DeepBox Desktop App/ORG 4 - DeepBox Desktop App/ORG3:Box1/Trash", EnumSet.of(Path.Type.directory, Path.Type.volume)); final Path test = new DeepboxTouchFeature(session, fileid).touch( - new Path(documents, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)), new TransferStatus()); + new DeepboxWriteFeature(session, fileid), new Path(documents, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)), new TransferStatus()); { final byte[] content = RandomUtils.nextBytes(124); new StreamCopier(StreamCancelation.noop, StreamProgress.noop).transfer(new ByteArrayInputStream(content), @@ -104,7 +102,7 @@ public class DeepboxMoveFeatureTest extends AbstractDeepboxTest { test.withAttributes(new DeepboxAttributesFinderFeature(session, fileid).find(test)); } final Path target = new DeepboxTouchFeature(session, fileid).touch( - new Path(documents, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)), new TransferStatus()); + new DeepboxWriteFeature(session, fileid), new Path(documents, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)), new TransferStatus()); { final byte[] content = RandomUtils.nextBytes(165); new StreamCopier(StreamCancelation.noop, StreamProgress.noop).transfer(new ByteArrayInputStream(content), @@ -153,9 +151,9 @@ public class DeepboxMoveFeatureTest extends AbstractDeepboxTest { final Path trash = new Path("/ORG 4 - DeepBox Desktop App/ORG 4 - DeepBox Desktop App/ORG3:Box1/Trash", EnumSet.of(Path.Type.directory, Path.Type.volume)); final Path test = new DeepboxDirectoryFeature(session, fileid).mkdir( - new Path(documents, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory)), new TransferStatus()); + new DeepboxWriteFeature(session, fileid), new Path(documents, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory)), new TransferStatus()); final Path target = new DeepboxDirectoryFeature(session, fileid).mkdir( - new Path(documents, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory)), new TransferStatus()); + new DeepboxWriteFeature(session, fileid), new Path(documents, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory)), new TransferStatus()); final Path targetInTrash = new Path(trash, target.getName(), target.getType()); final PathAttributes originalTestAttributes = new DeepboxAttributesFinderFeature(session, fileid).find(test); @@ -259,7 +257,7 @@ public class DeepboxMoveFeatureTest extends AbstractDeepboxTest { public void testNoMoveRenameFileFromTrash() throws Exception { final DeepboxIdProvider nodeid = new DeepboxIdProvider(session); final Path documents = new Path("/ORG 4 - DeepBox Desktop App/ORG 4 - DeepBox Desktop App/ORG3:Box1/Documents", EnumSet.of(Path.Type.directory, Path.Type.volume)); - final Path file = new DeepboxTouchFeature(session, nodeid).touch(new Path(documents, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)), new TransferStatus()); + final Path file = new DeepboxTouchFeature(session, nodeid).touch(new DeepboxWriteFeature(session, nodeid), new Path(documents, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)), new TransferStatus()); new DeepboxTrashFeature(session, nodeid).delete(Collections.singletonList(file), new DisabledPasswordCallback(), new Delete.DisabledCallback()); final Path trash = new Path("/ORG 4 - DeepBox Desktop App/ORG 4 - DeepBox Desktop App/ORG3:Box1/Trash", EnumSet.of(Path.Type.directory, Path.Type.volume)); final Path fileInTrash = new Path(trash, file.getName(), EnumSet.of(Path.Type.file)); @@ -278,7 +276,7 @@ public class DeepboxMoveFeatureTest extends AbstractDeepboxTest { public void testNoMoveFileToTrash() throws Exception { final DeepboxIdProvider nodeid = new DeepboxIdProvider(session); final Path documents = new Path("/ORG 4 - DeepBox Desktop App/ORG 4 - DeepBox Desktop App/ORG3:Box1/Documents", EnumSet.of(Path.Type.directory, Path.Type.volume)); - final Path file = new DeepboxTouchFeature(session, nodeid).touch(new Path(documents, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)), new TransferStatus()); + final Path file = new DeepboxTouchFeature(session, nodeid).touch(new DeepboxWriteFeature(session, nodeid), new Path(documents, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)), new TransferStatus()); final Path trash = new Path("/ORG 4 - DeepBox Desktop App/ORG 4 - DeepBox Desktop App/ORG3:Box1/Trash", EnumSet.of(Path.Type.directory, Path.Type.volume)); final Path fileInTrash = new Path(trash, file.getName(), EnumSet.of(Path.Type.file)); final PathAttributes attributes = new DeepboxAttributesFinderFeature(session, nodeid).find(file); diff --git a/deepbox/src/test/java/ch/cyberduck/core/deepbox/DeepboxReadFeatureTest.java b/deepbox/src/test/java/ch/cyberduck/core/deepbox/DeepboxReadFeatureTest.java index e9d9e32ed4..f3dadc28bf 100644 --- a/deepbox/src/test/java/ch/cyberduck/core/deepbox/DeepboxReadFeatureTest.java +++ b/deepbox/src/test/java/ch/cyberduck/core/deepbox/DeepboxReadFeatureTest.java @@ -60,7 +60,7 @@ public class DeepboxReadFeatureTest extends AbstractDeepboxTest { final DeepboxIdProvider nodeid = new DeepboxIdProvider(session); final Path documents = new Path("/ORG 4 - DeepBox Desktop App/ORG 4 - DeepBox Desktop App/ORG3:Box1/Documents/", EnumSet.of(Path.Type.directory, Path.Type.volume)); final Path test = new DeepboxDirectoryFeature(session, nodeid).mkdir( - new Path(documents, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory)), new TransferStatus()); + new DeepboxWriteFeature(session, nodeid), new Path(documents, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory)), new TransferStatus()); assertThrows(NotfoundException.class, () -> new DeepboxReadFeature(session, nodeid).read(new Path(test, "nosuchname", EnumSet.of(Path.Type.file)), status, new DisabledConnectionCallback())); new DeepboxDeleteFeature(session, nodeid).delete(Collections.singletonList(test), new DisabledLoginCallback(), new Delete.DisabledCallback()); } diff --git a/deepbox/src/test/java/ch/cyberduck/core/deepbox/DeepboxRestoreFeatureTest.java b/deepbox/src/test/java/ch/cyberduck/core/deepbox/DeepboxRestoreFeatureTest.java index 4695d4a3d2..a791a40881 100644 --- a/deepbox/src/test/java/ch/cyberduck/core/deepbox/DeepboxRestoreFeatureTest.java +++ b/deepbox/src/test/java/ch/cyberduck/core/deepbox/DeepboxRestoreFeatureTest.java @@ -16,6 +16,7 @@ package ch.cyberduck.core.deepbox; */ import ch.cyberduck.core.AlphanumericRandomStringService; +import ch.cyberduck.core.DefaultPathAttributes; import ch.cyberduck.core.DisabledLoginCallback; import ch.cyberduck.core.Path; import ch.cyberduck.core.PathAttributes; @@ -45,27 +46,27 @@ public class DeepboxRestoreFeatureTest extends AbstractDeepboxTest { final Path test = new Path(documents, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)); final Path testInTrash = new Path(trash, test.getName(), test.getType()); - new DeepboxTouchFeature(session, fileid).touch(test, new TransferStatus()); + new DeepboxTouchFeature(session, fileid).touch(new DeepboxWriteFeature(session, fileid), test, new TransferStatus()); final String nodeId = new DeepboxAttributesFinderFeature(session, fileid).find(test).getFileId(); - assertTrue(new DeepboxFindFeature(session, fileid).find(test.withAttributes(new PathAttributes()))); - assertFalse(new DeepboxFindFeature(session, fileid).find(testInTrash.withAttributes(new PathAttributes()))); + assertTrue(new DeepboxFindFeature(session, fileid).find(test.withAttributes(new DefaultPathAttributes()))); + assertFalse(new DeepboxFindFeature(session, fileid).find(testInTrash.withAttributes(new DefaultPathAttributes()))); new DeepboxTrashFeature(session, fileid).delete(Collections.singletonList(test), new DisabledLoginCallback(), new Delete.DisabledCallback()); - assertFalse(new DeepboxFindFeature(session, fileid).find(test.withAttributes(new PathAttributes()))); - assertTrue(new DeepboxFindFeature(session, fileid).find(testInTrash.withAttributes(new PathAttributes()))); - assertEquals(nodeId, new DeepboxAttributesFinderFeature(session, fileid).find(testInTrash.withAttributes(new PathAttributes())).getFileId()); - assertThrows(NotfoundException.class, () -> new DeepboxAttributesFinderFeature(session, fileid).find(test.withAttributes(new PathAttributes()))); - assertEquals(nodeId, new DeepboxAttributesFinderFeature(session, fileid).find(testInTrash.withAttributes(new PathAttributes())).getFileId()); + assertFalse(new DeepboxFindFeature(session, fileid).find(test.withAttributes(new DefaultPathAttributes()))); + assertTrue(new DeepboxFindFeature(session, fileid).find(testInTrash.withAttributes(new DefaultPathAttributes()))); + assertEquals(nodeId, new DeepboxAttributesFinderFeature(session, fileid).find(testInTrash.withAttributes(new DefaultPathAttributes())).getFileId()); + assertThrows(NotfoundException.class, () -> new DeepboxAttributesFinderFeature(session, fileid).find(test.withAttributes(new DefaultPathAttributes()))); + assertEquals(nodeId, new DeepboxAttributesFinderFeature(session, fileid).find(testInTrash.withAttributes(new DefaultPathAttributes())).getFileId()); final DeepboxRestoreFeature restore = new DeepboxRestoreFeature(session, fileid); restore.restore(testInTrash, new DisabledLoginCallback()); - assertTrue(new DeepboxFindFeature(session, fileid).find(test.withAttributes(new PathAttributes()))); - assertFalse(new DeepboxFindFeature(session, fileid).find(testInTrash.withAttributes(new PathAttributes()))); - assertEquals(nodeId, new DeepboxAttributesFinderFeature(session, fileid).find(test.withAttributes(new PathAttributes())).getFileId()); - assertThrows(NotfoundException.class, () -> new DeepboxAttributesFinderFeature(session, fileid).find(testInTrash.withAttributes(new PathAttributes()))); + assertTrue(new DeepboxFindFeature(session, fileid).find(test.withAttributes(new DefaultPathAttributes()))); + assertFalse(new DeepboxFindFeature(session, fileid).find(testInTrash.withAttributes(new DefaultPathAttributes()))); + assertEquals(nodeId, new DeepboxAttributesFinderFeature(session, fileid).find(test.withAttributes(new DefaultPathAttributes())).getFileId()); + assertThrows(NotfoundException.class, () -> new DeepboxAttributesFinderFeature(session, fileid).find(testInTrash.withAttributes(new DefaultPathAttributes()))); - new DeepboxDeleteFeature(session, fileid).delete(Collections.singletonList(test.withAttributes(new PathAttributes())), new DisabledLoginCallback(), new Delete.DisabledCallback()); + new DeepboxDeleteFeature(session, fileid).delete(Collections.singletonList(test.withAttributes(new DefaultPathAttributes())), new DisabledLoginCallback(), new Delete.DisabledCallback()); } @Test @@ -81,50 +82,50 @@ public class DeepboxRestoreFeatureTest extends AbstractDeepboxTest { final Path subfolderWithContentInTrash = new Path(folderInTrash, subfolderWithContent.getName(), EnumSet.of(Path.Type.directory)); final Path fileInTrash = new Path(subfolderWithContentInTrash, file.getName(), EnumSet.of(Path.Type.file)); - new DeepboxDirectoryFeature(session, fileid).mkdir(folder, new TransferStatus()); + new DeepboxDirectoryFeature(session, fileid).mkdir(new DeepboxWriteFeature(session, fileid), folder, new TransferStatus()); final String folderId = new DeepboxAttributesFinderFeature(session, fileid).find(folder).getFileId(); assertTrue(new DeepboxFindFeature(session, fileid).find(folder)); new CoreRestControllerApi(session.getClient()).getNodeInfo(folderId, null, null, null); // assert no fail - final String subFolderId = new DeepboxDirectoryFeature(session, fileid).mkdir(subfolderWithContent, new TransferStatus()).attributes().getFileId(); + final String subFolderId = new DeepboxDirectoryFeature(session, fileid).mkdir(new DeepboxWriteFeature(session, fileid), subfolderWithContent, new TransferStatus()).attributes().getFileId(); assertTrue(new DeepboxFindFeature(session, fileid).find(subfolderWithContent)); - new DeepboxTouchFeature(session, fileid).touch(file, new TransferStatus()); + new DeepboxTouchFeature(session, fileid).touch(new DeepboxWriteFeature(session, fileid), file, new TransferStatus()); final String nodeId = new DeepboxAttributesFinderFeature(session, fileid).find(file).getFileId(); - assertTrue(new DeepboxFindFeature(session, fileid).find(folder.withAttributes(new PathAttributes()))); - assertTrue(new DeepboxFindFeature(session, fileid).find(subfolderWithContent.withAttributes(new PathAttributes()))); + assertTrue(new DeepboxFindFeature(session, fileid).find(folder.withAttributes(new DefaultPathAttributes()))); + assertTrue(new DeepboxFindFeature(session, fileid).find(subfolderWithContent.withAttributes(new DefaultPathAttributes()))); assertTrue(new DeepboxFindFeature(session, fileid).find(file)); - assertFalse(new DeepboxFindFeature(session, fileid).find(folderInTrash.withAttributes(new PathAttributes()))); - assertFalse(new DeepboxFindFeature(session, fileid).find(subfolderWithContentInTrash.withAttributes(new PathAttributes()))); - assertFalse(new DeepboxFindFeature(session, fileid).find(fileInTrash.withAttributes(new PathAttributes()))); + assertFalse(new DeepboxFindFeature(session, fileid).find(folderInTrash.withAttributes(new DefaultPathAttributes()))); + assertFalse(new DeepboxFindFeature(session, fileid).find(subfolderWithContentInTrash.withAttributes(new DefaultPathAttributes()))); + assertFalse(new DeepboxFindFeature(session, fileid).find(fileInTrash.withAttributes(new DefaultPathAttributes()))); new DeepboxTrashFeature(session, fileid).delete(Collections.singletonList(folder), new DisabledLoginCallback(), new Delete.DisabledCallback()); - assertThrows(NotfoundException.class, () -> fileid.getFileId(folder.withAttributes(new PathAttributes()))); - assertThrows(NotfoundException.class, () -> fileid.getFileId(subfolderWithContent.withAttributes(new PathAttributes()))); - assertThrows(NotfoundException.class, () -> fileid.getFileId(file.withAttributes(new PathAttributes()))); - assertFalse(new DeepboxFindFeature(session, fileid).find(folder.withAttributes(new PathAttributes()))); - assertFalse(new DeepboxFindFeature(session, fileid).find(subfolderWithContent.withAttributes(new PathAttributes()))); - assertFalse(new DeepboxFindFeature(session, fileid).find(file.withAttributes(new PathAttributes()))); - assertTrue(new DeepboxFindFeature(session, fileid).find(folderInTrash.withAttributes(new PathAttributes()))); - assertTrue(new DeepboxFindFeature(session, fileid).find(subfolderWithContentInTrash.withAttributes(new PathAttributes()))); - assertTrue(new DeepboxFindFeature(session, fileid).find(fileInTrash.withAttributes(new PathAttributes()))); - assertEquals(folderId, new DeepboxAttributesFinderFeature(session, fileid).find(folderInTrash.withAttributes(new PathAttributes())).getFileId()); - assertEquals(subFolderId, new DeepboxAttributesFinderFeature(session, fileid).find(subfolderWithContentInTrash.withAttributes(new PathAttributes())).getFileId()); - assertEquals(nodeId, new DeepboxAttributesFinderFeature(session, fileid).find(fileInTrash.withAttributes(new PathAttributes())).getFileId()); + assertThrows(NotfoundException.class, () -> fileid.getFileId(folder.withAttributes(new DefaultPathAttributes()))); + assertThrows(NotfoundException.class, () -> fileid.getFileId(subfolderWithContent.withAttributes(new DefaultPathAttributes()))); + assertThrows(NotfoundException.class, () -> fileid.getFileId(file.withAttributes(new DefaultPathAttributes()))); + assertFalse(new DeepboxFindFeature(session, fileid).find(folder.withAttributes(new DefaultPathAttributes()))); + assertFalse(new DeepboxFindFeature(session, fileid).find(subfolderWithContent.withAttributes(new DefaultPathAttributes()))); + assertFalse(new DeepboxFindFeature(session, fileid).find(file.withAttributes(new DefaultPathAttributes()))); + assertTrue(new DeepboxFindFeature(session, fileid).find(folderInTrash.withAttributes(new DefaultPathAttributes()))); + assertTrue(new DeepboxFindFeature(session, fileid).find(subfolderWithContentInTrash.withAttributes(new DefaultPathAttributes()))); + assertTrue(new DeepboxFindFeature(session, fileid).find(fileInTrash.withAttributes(new DefaultPathAttributes()))); + assertEquals(folderId, new DeepboxAttributesFinderFeature(session, fileid).find(folderInTrash.withAttributes(new DefaultPathAttributes())).getFileId()); + assertEquals(subFolderId, new DeepboxAttributesFinderFeature(session, fileid).find(subfolderWithContentInTrash.withAttributes(new DefaultPathAttributes())).getFileId()); + assertEquals(nodeId, new DeepboxAttributesFinderFeature(session, fileid).find(fileInTrash.withAttributes(new DefaultPathAttributes())).getFileId()); final DeepboxRestoreFeature restore = new DeepboxRestoreFeature(session, fileid); restore.restore(folderInTrash, new DisabledLoginCallback()); - assertTrue(new DeepboxFindFeature(session, fileid).find(folder.withAttributes(new PathAttributes()))); - assertTrue(new DeepboxFindFeature(session, fileid).find(subfolderWithContent.withAttributes(new PathAttributes()))); - assertTrue(new DeepboxFindFeature(session, fileid).find(file.withAttributes(new PathAttributes()))); - assertFalse(new DeepboxFindFeature(session, fileid).find(folderInTrash.withAttributes(new PathAttributes()))); - assertFalse(new DeepboxFindFeature(session, fileid).find(subfolderWithContentInTrash.withAttributes(new PathAttributes()))); - assertFalse(new DeepboxFindFeature(session, fileid).find(fileInTrash.withAttributes(new PathAttributes()))); - assertEquals(folderId, new DeepboxAttributesFinderFeature(session, fileid).find(folder.withAttributes(new PathAttributes())).getFileId()); - assertEquals(subFolderId, new DeepboxAttributesFinderFeature(session, fileid).find(subfolderWithContent.withAttributes(new PathAttributes())).getFileId()); - assertEquals(nodeId, new DeepboxAttributesFinderFeature(session, fileid).find(file.withAttributes(new PathAttributes())).getFileId()); + assertTrue(new DeepboxFindFeature(session, fileid).find(folder.withAttributes(new DefaultPathAttributes()))); + assertTrue(new DeepboxFindFeature(session, fileid).find(subfolderWithContent.withAttributes(new DefaultPathAttributes()))); + assertTrue(new DeepboxFindFeature(session, fileid).find(file.withAttributes(new DefaultPathAttributes()))); + assertFalse(new DeepboxFindFeature(session, fileid).find(folderInTrash.withAttributes(new DefaultPathAttributes()))); + assertFalse(new DeepboxFindFeature(session, fileid).find(subfolderWithContentInTrash.withAttributes(new DefaultPathAttributes()))); + assertFalse(new DeepboxFindFeature(session, fileid).find(fileInTrash.withAttributes(new DefaultPathAttributes()))); + assertEquals(folderId, new DeepboxAttributesFinderFeature(session, fileid).find(folder.withAttributes(new DefaultPathAttributes())).getFileId()); + assertEquals(subFolderId, new DeepboxAttributesFinderFeature(session, fileid).find(subfolderWithContent.withAttributes(new DefaultPathAttributes())).getFileId()); + assertEquals(nodeId, new DeepboxAttributesFinderFeature(session, fileid).find(file.withAttributes(new DefaultPathAttributes())).getFileId()); - new DeepboxDeleteFeature(session, fileid).delete(Collections.singletonList(folder.withAttributes(new PathAttributes())), new DisabledLoginCallback(), new Delete.DisabledCallback()); + new DeepboxDeleteFeature(session, fileid).delete(Collections.singletonList(folder.withAttributes(new DefaultPathAttributes())), new DisabledLoginCallback(), new Delete.DisabledCallback()); } } \ No newline at end of file diff --git a/deepbox/src/test/java/ch/cyberduck/core/deepbox/DeepboxSessionTest.java b/deepbox/src/test/java/ch/cyberduck/core/deepbox/DeepboxSessionTest.java index 72a3faaded..a362548105 100644 --- a/deepbox/src/test/java/ch/cyberduck/core/deepbox/DeepboxSessionTest.java +++ b/deepbox/src/test/java/ch/cyberduck/core/deepbox/DeepboxSessionTest.java @@ -27,15 +27,15 @@ import static org.junit.jupiter.api.Assertions.assertEquals; public class DeepboxSessionTest { - private static Stream provideStringsForIsBlank() { + private static Stream provideStringsForGetStage() { return Stream.of( Arguments.of("api.int.deepbox.swiss", "int."), - Arguments.of("api.deepbox.swiss", "") + Arguments.of("api.deepbox.swiss", "app.") ); } @ParameterizedTest - @MethodSource("provideStringsForIsBlank") + @MethodSource("provideStringsForGetStage") public void getStage(final String hostname, final String expectedStage) { final DeepboxSession session = new DeepboxSession(new Host(new DeepboxProtocol(), hostname), null, null); assertEquals(expectedStage, session.getStage()); diff --git a/deepbox/src/test/java/ch/cyberduck/core/deepbox/DeepboxShareFeatureTest.java b/deepbox/src/test/java/ch/cyberduck/core/deepbox/DeepboxShareFeatureTest.java index 3f356019f1..605d1a0b64 100644 --- a/deepbox/src/test/java/ch/cyberduck/core/deepbox/DeepboxShareFeatureTest.java +++ b/deepbox/src/test/java/ch/cyberduck/core/deepbox/DeepboxShareFeatureTest.java @@ -40,7 +40,7 @@ public class DeepboxShareFeatureTest extends AbstractDeepboxTest { final DeepboxIdProvider fileid = new DeepboxIdProvider(session); final Path documents = new Path("/ORG 4 - DeepBox Desktop App/ORG 4 - DeepBox Desktop App/ORG3:Box1/Documents/", EnumSet.of(Path.Type.directory, Path.Type.volume)); final Path test = new DeepboxTouchFeature(session, fileid).touch( - new Path(documents, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)), new TransferStatus()); + new DeepboxWriteFeature(session, fileid), new Path(documents, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)), new TransferStatus()); try { final DeepboxShareFeature feature = new DeepboxShareFeature(session, fileid); assertTrue(feature.isSupported(test, Share.Type.download)); diff --git a/deepbox/src/test/java/ch/cyberduck/core/deepbox/DeepboxTouchFeatureTest.java b/deepbox/src/test/java/ch/cyberduck/core/deepbox/DeepboxTouchFeatureTest.java index 904d7cc4c9..12e4f90dcc 100644 --- a/deepbox/src/test/java/ch/cyberduck/core/deepbox/DeepboxTouchFeatureTest.java +++ b/deepbox/src/test/java/ch/cyberduck/core/deepbox/DeepboxTouchFeatureTest.java @@ -45,14 +45,14 @@ public class DeepboxTouchFeatureTest extends AbstractDeepboxTest { public void testTouchRoot() throws Exception { final DeepboxIdProvider fileid = new DeepboxIdProvider(session); assertThrows(AccessDeniedException.class, () -> new DeepboxTouchFeature(session, fileid).preflight(Home.root(), new AlphanumericRandomStringService().random())); - assertThrows(NotfoundException.class, () -> new DeepboxTouchFeature(session, fileid).touch(new Path(new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)), new TransferStatus())); + assertThrows(NotfoundException.class, () -> new DeepboxTouchFeature(session, fileid).touch(new DeepboxWriteFeature(session, fileid), new Path(new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)), new TransferStatus())); } @Test public void testNoDuplicates() throws Exception { final DeepboxIdProvider fileid = new DeepboxIdProvider(session); final Path documents = new Path("/ORG 4 - DeepBox Desktop App/ORG 4 - DeepBox Desktop App/ORG3:Box1/Documents/", EnumSet.of(Path.Type.directory, Path.Type.volume)); - final Path test = new DeepboxTouchFeature(session, fileid).touch(new Path(documents, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)), new TransferStatus()); + final Path test = new DeepboxTouchFeature(session, fileid).touch(new DeepboxWriteFeature(session, fileid), new Path(documents, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)), new TransferStatus()); new DeepboxTouchFeature(session, fileid).preflight(documents.withAttributes(new DeepboxAttributesFinderFeature(session, fileid).find(documents)), test.getName()); assertTrue(new DeepboxFindFeature(session, fileid).find(test)); new DeepboxDeleteFeature(session, fileid).delete(Collections.singletonList(test), new DisabledLoginCallback(), new Delete.DisabledCallback()); @@ -62,7 +62,7 @@ public class DeepboxTouchFeatureTest extends AbstractDeepboxTest { public void testAccents() throws Exception { final DeepboxIdProvider fileid = new DeepboxIdProvider(session); final Path documents = new Path("/ORG 4 - DeepBox Desktop App/ORG 4 - DeepBox Desktop App/ORG3:Box1/Documents/Insurance", EnumSet.of(Path.Type.directory, Path.Type.volume)); - final Path test = new DeepboxTouchFeature(session, fileid).touch(new Path(documents, new AlphanumericRandomStringService().random() + "éf", EnumSet.of(Path.Type.file)), new TransferStatus()); + final Path test = new DeepboxTouchFeature(session, fileid).touch(new DeepboxWriteFeature(session, fileid), new Path(documents, new AlphanumericRandomStringService().random() + "éf", EnumSet.of(Path.Type.file)), new TransferStatus()); assertTrue(new DeepboxFindFeature(session, fileid).find(documents)); new DeepboxDeleteFeature(session, fileid).delete(Collections.singletonList(test), new DisabledLoginCallback(), new Delete.DisabledCallback()); } @@ -193,7 +193,7 @@ public class DeepboxTouchFeatureTest extends AbstractDeepboxTest { final Path parent = new Path("/ORG 4 - DeepBox Desktop App/ORG 4 - DeepBox Desktop App/ORG3:Box1/Trash", EnumSet.of(Path.Type.directory)); final Path folder = new Path(parent, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory)); assertThrows(AccessDeniedException.class, () -> new DeepboxTouchFeature(session, nodeid).preflight(parent, folder.getName())); - assertThrows(AccessDeniedException.class, () -> new DeepboxTouchFeature(session, nodeid).touch(folder, new TransferStatus())); + assertThrows(AccessDeniedException.class, () -> new DeepboxTouchFeature(session, nodeid).touch(new DeepboxWriteFeature(session, nodeid), folder, new TransferStatus())); } @Test @@ -202,6 +202,6 @@ public class DeepboxTouchFeatureTest extends AbstractDeepboxTest { final Path parent = new Path(String.format("/ORG 1 - DeepBox Desktop App/%s", DeepboxListService.SHARED), EnumSet.of(Path.Type.directory)); final Path file = new Path(parent, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory)); assertThrows(AccessDeniedException.class, () -> new DeepboxTouchFeature(session, nodeid).preflight(parent, file.getName())); - assertThrows(NotfoundException.class, () -> new DeepboxTouchFeature(session, nodeid).touch(file, new TransferStatus())); + assertThrows(NotfoundException.class, () -> new DeepboxTouchFeature(session, nodeid).touch(new DeepboxWriteFeature(session, nodeid), file, new TransferStatus())); } } \ No newline at end of file diff --git a/deepbox/src/test/java/ch/cyberduck/core/deepbox/DeepboxTrashFeatureTest.java b/deepbox/src/test/java/ch/cyberduck/core/deepbox/DeepboxTrashFeatureTest.java index 8f6b42ed24..f732e503d9 100644 --- a/deepbox/src/test/java/ch/cyberduck/core/deepbox/DeepboxTrashFeatureTest.java +++ b/deepbox/src/test/java/ch/cyberduck/core/deepbox/DeepboxTrashFeatureTest.java @@ -17,6 +17,7 @@ package ch.cyberduck.core.deepbox; import ch.cyberduck.core.Acl; 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; @@ -48,7 +49,7 @@ public class DeepboxTrashFeatureTest extends AbstractDeepboxTest { final Path documents = new Path("/ORG 4 - DeepBox Desktop App/ORG 4 - DeepBox Desktop App/ORG3:Box1/Documents/", EnumSet.of(Path.Type.directory, Path.Type.volume)); final Path trash = new Path("/ORG 4 - DeepBox Desktop App/ORG 4 - DeepBox Desktop App/ORG3:Box1/Trash/", EnumSet.of(Path.Type.directory, Path.Type.volume)); final Path file = new Path(documents, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)); - new DeepboxTouchFeature(session, nodeid).touch(file, new TransferStatus()); + new DeepboxTouchFeature(session, nodeid).touch(new DeepboxWriteFeature(session, nodeid), file, new TransferStatus()); final String nodeId = new DeepboxAttributesFinderFeature(session, nodeid).find(file).getFileId(); new CoreRestControllerApi(session.getClient()).getNodeInfo(nodeId, null, null, null); // assert no fail assertTrue(new DeepboxFindFeature(session, nodeid).find(file)); @@ -75,20 +76,20 @@ public class DeepboxTrashFeatureTest extends AbstractDeepboxTest { final DeepboxIdProvider nodeid = new DeepboxIdProvider(session); final Path documents = new Path("/ORG 4 - DeepBox Desktop App/ORG 4 - DeepBox Desktop App/ORG3:Box1/Documents/", EnumSet.of(Path.Type.directory, Path.Type.volume)); final Path trash = new Path("/ORG 4 - DeepBox Desktop App/ORG 4 - DeepBox Desktop App/ORG3:Box1/Trash/", EnumSet.of(Path.Type.directory, Path.Type.volume)); - final Path folder = new DeepboxDirectoryFeature(session, nodeid).mkdir(new Path(documents, String.format(new AlphanumericRandomStringService().random()), EnumSet.of(Path.Type.directory, Path.Type.volume)), new TransferStatus()); + final Path folder = new DeepboxDirectoryFeature(session, nodeid).mkdir(new DeepboxWriteFeature(session, nodeid), new Path(documents, String.format(new AlphanumericRandomStringService().random()), EnumSet.of(Path.Type.directory, Path.Type.volume)), new TransferStatus()); final String nodeId = nodeid.getFileId(folder); new CoreRestControllerApi(session.getClient()).getNodeInfo(nodeId, null, null, null); // assert no fail - final Path subfolderWithContent = new DeepboxDirectoryFeature(session, nodeid).mkdir(new Path(folder, new AlphanumericRandomStringService().random().toLowerCase(), EnumSet.of(Path.Type.directory)), new TransferStatus()); + final Path subfolderWithContent = new DeepboxDirectoryFeature(session, nodeid).mkdir(new DeepboxWriteFeature(session, nodeid), new Path(folder, new AlphanumericRandomStringService().random().toLowerCase(), EnumSet.of(Path.Type.directory)), new TransferStatus()); assertTrue(new DeepboxFindFeature(session, nodeid).find(subfolderWithContent)); final Path file = new Path(subfolderWithContent, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)); - new DeepboxTouchFeature(session, nodeid).touch(file, new TransferStatus()); + new DeepboxTouchFeature(session, nodeid).touch(new DeepboxWriteFeature(session, nodeid), file, new TransferStatus()); assertTrue(new DeepboxFindFeature(session, nodeid).find(file)); new DeepboxTrashFeature(session, nodeid).delete(Collections.singletonList(folder), new DisabledLoginCallback(), new Delete.DisabledCallback()); - assertFalse(new DeepboxFindFeature(session, nodeid).find(folder.withAttributes(new PathAttributes()))); + assertFalse(new DeepboxFindFeature(session, nodeid).find(folder.withAttributes(new DefaultPathAttributes()))); assertFalse(new DefaultFindFeature(session).find(folder)); - assertFalse(new DeepboxFindFeature(session, nodeid).find(subfolderWithContent.withAttributes(new PathAttributes()))); + assertFalse(new DeepboxFindFeature(session, nodeid).find(subfolderWithContent.withAttributes(new DefaultPathAttributes()))); assertThrows(NotfoundException.class, () -> new DefaultFindFeature(session).find(subfolderWithContent)); - assertFalse(new DeepboxFindFeature(session, nodeid).find(file.withAttributes(new PathAttributes()))); + assertFalse(new DeepboxFindFeature(session, nodeid).find(file.withAttributes(new DefaultPathAttributes()))); // file not in trash is deleted but not purged (i.e. moved to the trash) final Path folderInTrash = new Path(trash, folder.getName(), EnumSet.of(Path.Type.directory)); assertTrue(new DeepboxFindFeature(session, nodeid).find(folderInTrash)); @@ -197,37 +198,37 @@ public class DeepboxTrashFeatureTest extends AbstractDeepboxTest { final Path test = new Path(parentFolder, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)); final Path testInTrash = new Path(trash, test.getName(), EnumSet.of(Path.Type.file)); - new DeepboxTouchFeature(session, nodeid).touch(test, new TransferStatus()); - assertTrue(new DeepboxFindFeature(session, nodeid).find(test.withAttributes(new PathAttributes()))); - assertFalse(new DeepboxFindFeature(session, nodeid).find(testInTrash.withAttributes(new PathAttributes()))); + new DeepboxTouchFeature(session, nodeid).touch(new DeepboxWriteFeature(session, nodeid), test, new TransferStatus()); + assertTrue(new DeepboxFindFeature(session, nodeid).find(test.withAttributes(new DefaultPathAttributes()))); + assertFalse(new DeepboxFindFeature(session, nodeid).find(testInTrash.withAttributes(new DefaultPathAttributes()))); assertTrue(new DeepboxAttributesFinderFeature(session, nodeid).find(test).getAcl().get(new Acl.CanonicalUser()).contains(CANDELETE)); assertTrue(new DeepboxAttributesFinderFeature(session, nodeid).find(test).getAcl().get(new Acl.CanonicalUser()).contains(CANPURGE)); assertFalse(new DeepboxAttributesFinderFeature(session, nodeid).find(test).getAcl().get(new Acl.CanonicalUser()).contains(CANREVERT)); - new DeepboxTrashFeature(session, nodeid).delete(Collections.singletonList(test.withAttributes(new PathAttributes())), new DisabledPasswordCallback(), new Delete.DisabledCallback()); - assertFalse(new DeepboxFindFeature(session, nodeid).find(test.withAttributes(new PathAttributes()))); - assertTrue(new DeepboxFindFeature(session, nodeid).find(testInTrash.withAttributes(new PathAttributes()))); + new DeepboxTrashFeature(session, nodeid).delete(Collections.singletonList(test.withAttributes(new DefaultPathAttributes())), new DisabledPasswordCallback(), new Delete.DisabledCallback()); + assertFalse(new DeepboxFindFeature(session, nodeid).find(test.withAttributes(new DefaultPathAttributes()))); + assertTrue(new DeepboxFindFeature(session, nodeid).find(testInTrash.withAttributes(new DefaultPathAttributes()))); assertFalse(new DeepboxAttributesFinderFeature(session, nodeid).find(testInTrash).getAcl().get(new Acl.CanonicalUser()).contains(CANDELETE)); assertTrue(new DeepboxAttributesFinderFeature(session, nodeid).find(testInTrash).getAcl().get(new Acl.CanonicalUser()).contains(CANPURGE)); assertTrue(new DeepboxAttributesFinderFeature(session, nodeid).find(testInTrash).getAcl().get(new Acl.CanonicalUser()).contains(CANREVERT)); - new DeepboxRestoreFeature(session, nodeid).restore(testInTrash.withAttributes(new PathAttributes()), new DisabledLoginCallback()); - assertTrue(new DeepboxFindFeature(session, nodeid).find(test.withAttributes(new PathAttributes()))); - assertFalse(new DeepboxFindFeature(session, nodeid).find(testInTrash.withAttributes(new PathAttributes()))); + new DeepboxRestoreFeature(session, nodeid).restore(testInTrash.withAttributes(new DefaultPathAttributes()), new DisabledLoginCallback()); + assertTrue(new DeepboxFindFeature(session, nodeid).find(test.withAttributes(new DefaultPathAttributes()))); + assertFalse(new DeepboxFindFeature(session, nodeid).find(testInTrash.withAttributes(new DefaultPathAttributes()))); assertTrue(new DeepboxAttributesFinderFeature(session, nodeid).find(test).getAcl().get(new Acl.CanonicalUser()).contains(CANDELETE)); assertTrue(new DeepboxAttributesFinderFeature(session, nodeid).find(test).getAcl().get(new Acl.CanonicalUser()).contains(CANPURGE)); assertFalse(new DeepboxAttributesFinderFeature(session, nodeid).find(test).getAcl().get(new Acl.CanonicalUser()).contains(CANREVERT)); - new DeepboxTrashFeature(session, nodeid).delete(Collections.singletonList(test.withAttributes(new PathAttributes())), new DisabledPasswordCallback(), new Delete.DisabledCallback()); - assertFalse(new DeepboxFindFeature(session, nodeid).find(test.withAttributes(new PathAttributes()))); - assertTrue(new DeepboxFindFeature(session, nodeid).find(testInTrash.withAttributes(new PathAttributes()))); + new DeepboxTrashFeature(session, nodeid).delete(Collections.singletonList(test.withAttributes(new DefaultPathAttributes())), new DisabledPasswordCallback(), new Delete.DisabledCallback()); + assertFalse(new DeepboxFindFeature(session, nodeid).find(test.withAttributes(new DefaultPathAttributes()))); + assertTrue(new DeepboxFindFeature(session, nodeid).find(testInTrash.withAttributes(new DefaultPathAttributes()))); assertFalse(new DeepboxAttributesFinderFeature(session, nodeid).find(testInTrash).getAcl().get(new Acl.CanonicalUser()).contains(CANDELETE)); assertTrue(new DeepboxAttributesFinderFeature(session, nodeid).find(testInTrash).getAcl().get(new Acl.CanonicalUser()).contains(CANPURGE)); assertTrue(new DeepboxAttributesFinderFeature(session, nodeid).find(testInTrash).getAcl().get(new Acl.CanonicalUser()).contains(CANREVERT)); - new DeepboxTrashFeature(session, nodeid).delete(Collections.singletonList(testInTrash.withAttributes(new PathAttributes())), new DisabledPasswordCallback(), new Delete.DisabledCallback()); - assertFalse(new DeepboxFindFeature(session, nodeid).find(test.withAttributes(new PathAttributes()))); - assertFalse(new DeepboxFindFeature(session, nodeid).find(testInTrash.withAttributes(new PathAttributes()))); + new DeepboxTrashFeature(session, nodeid).delete(Collections.singletonList(testInTrash.withAttributes(new DefaultPathAttributes())), new DisabledPasswordCallback(), new Delete.DisabledCallback()); + assertFalse(new DeepboxFindFeature(session, nodeid).find(test.withAttributes(new DefaultPathAttributes()))); + assertFalse(new DeepboxFindFeature(session, nodeid).find(testInTrash.withAttributes(new DefaultPathAttributes()))); } @Test @@ -238,38 +239,38 @@ public class DeepboxTrashFeatureTest extends AbstractDeepboxTest { final Path test = new Path(parentFolder, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory)); final Path testInTrash = new Path(trash, test.getName(), EnumSet.of(Path.Type.directory)); - new DeepboxDirectoryFeature(session, nodeid).mkdir(test, new TransferStatus()); - assertTrue(new DeepboxFindFeature(session, nodeid).find(test.withAttributes(new PathAttributes()))); - assertFalse(new DeepboxFindFeature(session, nodeid).find(testInTrash.withAttributes(new PathAttributes()))); + new DeepboxDirectoryFeature(session, nodeid).mkdir(new DeepboxWriteFeature(session, nodeid), test, new TransferStatus()); + assertTrue(new DeepboxFindFeature(session, nodeid).find(test.withAttributes(new DefaultPathAttributes()))); + assertFalse(new DeepboxFindFeature(session, nodeid).find(testInTrash.withAttributes(new DefaultPathAttributes()))); assertTrue(new DeepboxAttributesFinderFeature(session, nodeid).find(test).getAcl().get(new Acl.CanonicalUser()).contains(CANDELETE)); assertTrue(new DeepboxAttributesFinderFeature(session, nodeid).find(test).getAcl().get(new Acl.CanonicalUser()).contains(CANPURGE)); assertFalse(new DeepboxAttributesFinderFeature(session, nodeid).find(test).getAcl().get(new Acl.CanonicalUser()).contains(CANREVERT)); - new DeepboxTrashFeature(session, nodeid).delete(Collections.singletonList(test.withAttributes(new PathAttributes())), new DisabledPasswordCallback(), new Delete.DisabledCallback()); - assertFalse(new DeepboxFindFeature(session, nodeid).find(test.withAttributes(new PathAttributes()))); - assertTrue(new DeepboxFindFeature(session, nodeid).find(testInTrash.withAttributes(new PathAttributes()))); + new DeepboxTrashFeature(session, nodeid).delete(Collections.singletonList(test.withAttributes(new DefaultPathAttributes())), new DisabledPasswordCallback(), new Delete.DisabledCallback()); + assertFalse(new DeepboxFindFeature(session, nodeid).find(test.withAttributes(new DefaultPathAttributes()))); + assertTrue(new DeepboxFindFeature(session, nodeid).find(testInTrash.withAttributes(new DefaultPathAttributes()))); assertFalse(new DeepboxAttributesFinderFeature(session, nodeid).find(testInTrash).getAcl().get(new Acl.CanonicalUser()).contains(CANDELETE)); assertTrue(new DeepboxAttributesFinderFeature(session, nodeid).find(testInTrash).getAcl().get(new Acl.CanonicalUser()).contains(CANPURGE)); assertTrue(new DeepboxAttributesFinderFeature(session, nodeid).find(testInTrash).getAcl().get(new Acl.CanonicalUser()).contains(CANREVERT)); - new DeepboxRestoreFeature(session, nodeid).restore(testInTrash.withAttributes(new PathAttributes()), new DisabledLoginCallback()); - assertTrue(new DeepboxFindFeature(session, nodeid).find(test.withAttributes(new PathAttributes()))); - assertFalse(new DeepboxFindFeature(session, nodeid).find(testInTrash.withAttributes(new PathAttributes()))); + new DeepboxRestoreFeature(session, nodeid).restore(testInTrash.withAttributes(new DefaultPathAttributes()), new DisabledLoginCallback()); + assertTrue(new DeepboxFindFeature(session, nodeid).find(test.withAttributes(new DefaultPathAttributes()))); + assertFalse(new DeepboxFindFeature(session, nodeid).find(testInTrash.withAttributes(new DefaultPathAttributes()))); assertTrue(new DeepboxAttributesFinderFeature(session, nodeid).find(test).getAcl().get(new Acl.CanonicalUser()).contains(CANDELETE)); assertTrue(new DeepboxAttributesFinderFeature(session, nodeid).find(test).getAcl().get(new Acl.CanonicalUser()).contains(CANPURGE)); assertFalse(new DeepboxAttributesFinderFeature(session, nodeid).find(test).getAcl().get(new Acl.CanonicalUser()).contains(CANREVERT)); - new DeepboxTrashFeature(session, nodeid).delete(Collections.singletonList(test.withAttributes(new PathAttributes())), new DisabledPasswordCallback(), new Delete.DisabledCallback()); - assertFalse(new DeepboxFindFeature(session, nodeid).find(test.withAttributes(new PathAttributes()))); - assertTrue(new DeepboxFindFeature(session, nodeid).find(testInTrash.withAttributes(new PathAttributes()))); + new DeepboxTrashFeature(session, nodeid).delete(Collections.singletonList(test.withAttributes(new DefaultPathAttributes())), new DisabledPasswordCallback(), new Delete.DisabledCallback()); + assertFalse(new DeepboxFindFeature(session, nodeid).find(test.withAttributes(new DefaultPathAttributes()))); + assertTrue(new DeepboxFindFeature(session, nodeid).find(testInTrash.withAttributes(new DefaultPathAttributes()))); assertFalse(new DeepboxAttributesFinderFeature(session, nodeid).find(testInTrash).getAcl().get(new Acl.CanonicalUser()).contains(CANDELETE)); assertTrue(new DeepboxAttributesFinderFeature(session, nodeid).find(testInTrash).getAcl().get(new Acl.CanonicalUser()).contains(CANPURGE)); assertTrue(new DeepboxAttributesFinderFeature(session, nodeid).find(testInTrash).getAcl().get(new Acl.CanonicalUser()).contains(CANREVERT)); - new DeepboxTrashFeature(session, nodeid).delete(Collections.singletonList(testInTrash.withAttributes(new PathAttributes())), new DisabledPasswordCallback(), new Delete.DisabledCallback()); - assertFalse(new DeepboxFindFeature(session, nodeid).find(test.withAttributes(new PathAttributes()))); - assertFalse(new DeepboxFindFeature(session, nodeid).find(testInTrash.withAttributes(new PathAttributes()))); + new DeepboxTrashFeature(session, nodeid).delete(Collections.singletonList(testInTrash.withAttributes(new DefaultPathAttributes())), new DisabledPasswordCallback(), new Delete.DisabledCallback()); + assertFalse(new DeepboxFindFeature(session, nodeid).find(test.withAttributes(new DefaultPathAttributes()))); + assertFalse(new DeepboxFindFeature(session, nodeid).find(testInTrash.withAttributes(new DefaultPathAttributes()))); } @Test @@ -281,16 +282,16 @@ public class DeepboxTrashFeatureTest extends AbstractDeepboxTest { final Path test = new Path(parentFolder, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)); final Path testInTrash = new Path(trash, test.getName(), EnumSet.of(Path.Type.file)); - new DeepboxTouchFeature(session, nodeid).touch(test, new TransferStatus()); - assertTrue(new DeepboxFindFeature(session, nodeid).find(test.withAttributes(new PathAttributes()))); - assertFalse(new DeepboxFindFeature(session, nodeid).find(testInTrash.withAttributes(new PathAttributes()))); + new DeepboxTouchFeature(session, nodeid).touch(new DeepboxWriteFeature(session, nodeid), test, new TransferStatus()); + assertTrue(new DeepboxFindFeature(session, nodeid).find(test.withAttributes(new DefaultPathAttributes()))); + assertFalse(new DeepboxFindFeature(session, nodeid).find(testInTrash.withAttributes(new DefaultPathAttributes()))); assertTrue(new DeepboxAttributesFinderFeature(session, nodeid).find(test).getAcl().get(new Acl.CanonicalUser()).contains(CANDELETE)); assertTrue(new DeepboxAttributesFinderFeature(session, nodeid).find(test).getAcl().get(new Acl.CanonicalUser()).contains(CANPURGE)); assertFalse(new DeepboxAttributesFinderFeature(session, nodeid).find(test).getAcl().get(new Acl.CanonicalUser()).contains(CANREVERT)); new DeepboxTrashFeature(session, nodeid).delete(Collections.singletonList(test), new DisabledPasswordCallback(), new Delete.DisabledCallback()); - assertFalse(new DeepboxFindFeature(session, nodeid).find(test.withAttributes(new PathAttributes()))); - assertFalse(new DeepboxFindFeature(session, nodeid).find(testInTrash.withAttributes(new PathAttributes()))); + assertFalse(new DeepboxFindFeature(session, nodeid).find(test.withAttributes(new DefaultPathAttributes()))); + assertFalse(new DeepboxFindFeature(session, nodeid).find(testInTrash.withAttributes(new DefaultPathAttributes()))); } @Test @@ -302,15 +303,15 @@ public class DeepboxTrashFeatureTest extends AbstractDeepboxTest { final Path test = new Path(parentFolder, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory)); final Path testInTrash = new Path(trash, test.getName(), EnumSet.of(Path.Type.directory)); - new DeepboxDirectoryFeature(session, nodeid).mkdir(test, new TransferStatus()); - assertTrue(new DeepboxFindFeature(session, nodeid).find(test.withAttributes(new PathAttributes()))); - assertFalse(new DeepboxFindFeature(session, nodeid).find(testInTrash.withAttributes(new PathAttributes()))); + new DeepboxDirectoryFeature(session, nodeid).mkdir(new DeepboxWriteFeature(session, nodeid), test, new TransferStatus()); + assertTrue(new DeepboxFindFeature(session, nodeid).find(test.withAttributes(new DefaultPathAttributes()))); + assertFalse(new DeepboxFindFeature(session, nodeid).find(testInTrash.withAttributes(new DefaultPathAttributes()))); assertTrue(new DeepboxAttributesFinderFeature(session, nodeid).find(test).getAcl().get(new Acl.CanonicalUser()).contains(CANDELETE)); assertTrue(new DeepboxAttributesFinderFeature(session, nodeid).find(test).getAcl().get(new Acl.CanonicalUser()).contains(CANPURGE)); assertFalse(new DeepboxAttributesFinderFeature(session, nodeid).find(test).getAcl().get(new Acl.CanonicalUser()).contains(CANREVERT)); new DeepboxTrashFeature(session, nodeid).delete(Collections.singletonList(test), new DisabledPasswordCallback(), new Delete.DisabledCallback()); - assertFalse(new DeepboxFindFeature(session, nodeid).find(test.withAttributes(new PathAttributes()))); - assertFalse(new DeepboxFindFeature(session, nodeid).find(testInTrash.withAttributes(new PathAttributes()))); + assertFalse(new DeepboxFindFeature(session, nodeid).find(test.withAttributes(new DefaultPathAttributes()))); + assertFalse(new DeepboxFindFeature(session, nodeid).find(testInTrash.withAttributes(new DefaultPathAttributes()))); } } diff --git a/deepbox/src/test/java/ch/cyberduck/core/deepbox/DeepboxWriteFeatureTest.java b/deepbox/src/test/java/ch/cyberduck/core/deepbox/DeepboxWriteFeatureTest.java index ea371838e4..4254a8b398 100644 --- a/deepbox/src/test/java/ch/cyberduck/core/deepbox/DeepboxWriteFeatureTest.java +++ b/deepbox/src/test/java/ch/cyberduck/core/deepbox/DeepboxWriteFeatureTest.java @@ -52,7 +52,7 @@ public class DeepboxWriteFeatureTest extends AbstractDeepboxTest { final DeepboxIdProvider nodeid = new DeepboxIdProvider(session); final Path documents = new Path("/ORG 4 - DeepBox Desktop App/ORG 4 - DeepBox Desktop App/ORG3:Box1/Documents/", EnumSet.of(Path.Type.directory, Path.Type.volume)); final Path file = new Path(documents, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)); - new DeepboxTouchFeature(session, nodeid).touch(file, new TransferStatus()); + new DeepboxTouchFeature(session, nodeid).touch(new DeepboxWriteFeature(session, nodeid), file, new TransferStatus()); assertTrue(new DefaultFindFeature(session).find(file)); try { final byte[] content = RandomUtils.nextBytes(2047); @@ -123,7 +123,7 @@ public class DeepboxWriteFeatureTest extends AbstractDeepboxTest { final DeepboxIdProvider nodeid = new DeepboxIdProvider(session); final Path documents = new Path("/ORG 4 - DeepBox Desktop App/ORG 4 - DeepBox Desktop App/ORG3:Box1/Documents/", EnumSet.of(Path.Type.directory, Path.Type.volume)); final Path room = new DeepboxDirectoryFeature(session, nodeid).mkdir( - new Path(documents, + new DeepboxWriteFeature(session, nodeid), new Path(documents, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory)), new TransferStatus()); final long folderTimestamp = new DeepboxAttributesFinderFeature(session, nodeid).find(room).getModificationDate(); final byte[] content = RandomUtils.nextBytes(32769); @@ -173,7 +173,7 @@ public class DeepboxWriteFeatureTest extends AbstractDeepboxTest { final DeepboxWriteFeature feature = new DeepboxWriteFeature(session, nodeid); final Path documents = new Path("/ORG 4 - DeepBox Desktop App/ORG 4 - DeepBox Desktop App/ORG3:Box1/Documents/", EnumSet.of(Path.Type.directory, Path.Type.volume)); final Path room = new DeepboxDirectoryFeature(session, nodeid).mkdir( - new Path(documents, new AlphanumericRandomStringService().random(), + new DeepboxWriteFeature(session, nodeid), new Path(documents, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory, Path.Type.volume)), new TransferStatus()); final byte[] content = RandomUtils.nextBytes(1); final TransferStatus status = new TransferStatus(); @@ -199,7 +199,7 @@ public class DeepboxWriteFeatureTest extends AbstractDeepboxTest { final DeepboxIdProvider nodeid = new DeepboxIdProvider(session); final Path documents = new Path("/ORG 4 - DeepBox Desktop App/ORG 4 - DeepBox Desktop App/ORG3:Box1/Documents/", EnumSet.of(Path.Type.directory, Path.Type.volume)); final Path room = new DeepboxDirectoryFeature(session, nodeid).mkdir( - new Path(documents, new AlphanumericRandomStringService().random(), + new DeepboxWriteFeature(session, nodeid), new Path(documents, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory, Path.Type.volume)), new TransferStatus()); final byte[] content = RandomUtils.nextBytes(32769); diff --git a/defaults/pom.xml b/defaults/pom.xml index f0c256c49b..a714637720 100644 --- a/defaults/pom.xml +++ b/defaults/pom.xml @@ -19,7 +19,7 @@ parent ch.cyberduck - 9.2.0-SNAPSHOT + 9.4.0-SNAPSHOT defaults diff --git a/defaults/src/main/resources/debug/log4j.xml b/defaults/src/main/resources/debug/log4j.xml index ce4787cd17..5d525a2b65 100644 --- a/defaults/src/main/resources/debug/log4j.xml +++ b/defaults/src/main/resources/debug/log4j.xml @@ -13,17 +13,18 @@ ~ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ~ GNU General Public License for more details. --> - + - - - - + + + + diff --git a/defaults/src/main/resources/default.properties b/defaults/src/main/resources/default.properties index c49c1166d5..f1ef70f9a7 100644 --- a/defaults/src/main/resources/default.properties +++ b/defaults/src/main/resources/default.properties @@ -43,7 +43,6 @@ growl.enable=true path.symboliclink.resolve=false -local.alias.resolve=true local.symboliclink.resolve=false local.normalize.prefix=false local.normalize.unicode=true @@ -103,9 +102,9 @@ browser.disconnect.bookmarks.show=false # Display only one info panel and change information according to selection in browser browser.info.inspector=true browser.sort.ascending=true -browser.alternatingRows=false +browser.alternatingRows=true browser.verticalLines=false -browser.horizontalLines=true +browser.horizontalLines=false # Show hidden files in browser by default browser.showHidden=false browser.charset.encoding=UTF-8 @@ -253,6 +252,8 @@ http.request.uri.normalize=false http.request.entity.buffer.limit=5242880 request.unauthorized.ntlm.preflight=false +http.uriencode.mapping=+=%20 *=%2A %7E=~ %40=@ + # Enable or disable verification that the remote host taking part # of a data connection is the same as the host to which the control # connection is attached. @@ -303,9 +304,13 @@ s3.endpoint.format.ipv6=s3.dualstack.%s.amazonaws.com s3.acl.default=private # STS Assume Role request parameters +# Defaults to 3600 seconds #s3.assumerole.durationseconds= +# Inline policy for assume role #s3.assumerole.policy= +# Role ARN #s3.assumerole.rolearn= +# Specifying a role session name for easier auditing #s3.assumerole.rolesessionname= # Default redundancy level @@ -576,8 +581,14 @@ connection.port.default=21 connection.protocol.default=ftp # SO_KEEPALIVE connection.socket.keepalive=true -# SO_LINGER Specify a linger-on-close timeout. This option disables/ enables immediate return from a close() of a TCP Socket. +# SO_LINGER Specify a linger-on-close timeout. This option disables/enables immediate +# return from a close() of a TCP Socket. Enabling this option with a non-zero Integer timeout means that a close() will +# block pending the transmission and acknowledgement of all data written to the peer, at which point the socket is +# closed gracefully. Upon reaching the linger timeout, the socket is closed forcefully, with a TCP RST. Enabling the +# option with a timeout of zero does a forceful close immediately. connection.socket.linger=true +# In seconds +connection.socket.linger.timeout=30 # Socket timeout connection.timeout.seconds=30 connection.timeout.min.seconds=5 @@ -776,4 +787,4 @@ deepbox.listing.box.documents=true deepbox.listing.box.trash=false # 1 min deepbox.download.interrupt.ms=60000 -deepbox.download.interval.ms=50 +deepbox.download.interval.ms=0=50 2=200 5=500 15=2000 diff --git a/defaults/src/main/resources/default/log4j.xml b/defaults/src/main/resources/default/log4j.xml index a02f90d3f1..ccefbb8402 100644 --- a/defaults/src/main/resources/default/log4j.xml +++ b/defaults/src/main/resources/default/log4j.xml @@ -13,14 +13,16 @@ ~ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ~ GNU General Public License for more details. --> - + - - - + + + + diff --git a/dracoon/pom.xml b/dracoon/pom.xml index 37536884c8..c6dc970304 100644 --- a/dracoon/pom.xml +++ b/dracoon/pom.xml @@ -19,7 +19,7 @@ parent ch.cyberduck - 9.2.0-SNAPSHOT + 9.4.0-SNAPSHOT dracoon diff --git a/dracoon/src/main/java/ch/cyberduck/core/sds/MultipartUploadTokenOutputStream.java b/dracoon/src/main/java/ch/cyberduck/core/sds/MultipartUploadTokenOutputStream.java index 4d4469d330..9a11fa76af 100644 --- a/dracoon/src/main/java/ch/cyberduck/core/sds/MultipartUploadTokenOutputStream.java +++ b/dracoon/src/main/java/ch/cyberduck/core/sds/MultipartUploadTokenOutputStream.java @@ -85,7 +85,6 @@ public class MultipartUploadTokenOutputStream extends OutputStream { final HttpPost request = new HttpPost(uploadUrl); request.setEntity(entity); request.setHeader(HttpHeaders.CONTENT_TYPE, MimeTypeService.DEFAULT_CONTENT_TYPE); - request.setHeader(SDSSession.SDS_AUTH_TOKEN_HEADER, StringUtils.EMPTY); if(0L != overall.getLength() && 0 != content.length) { final HttpRange range = HttpRange.byLength(offset, content.length); final String header; diff --git a/dracoon/src/main/java/ch/cyberduck/core/sds/SDSAttributesAdapter.java b/dracoon/src/main/java/ch/cyberduck/core/sds/SDSAttributesAdapter.java index a968e0c5da..c83d4464c3 100644 --- a/dracoon/src/main/java/ch/cyberduck/core/sds/SDSAttributesAdapter.java +++ b/dracoon/src/main/java/ch/cyberduck/core/sds/SDSAttributesAdapter.java @@ -16,6 +16,7 @@ package ch.cyberduck.core.sds; */ import ch.cyberduck.core.Acl; +import ch.cyberduck.core.DefaultPathAttributes; import ch.cyberduck.core.Path; import ch.cyberduck.core.PathAttributes; import ch.cyberduck.core.Permission; @@ -36,7 +37,7 @@ import java.util.Map; import static ch.cyberduck.core.sds.SDSAttributesFinderFeature.*; public class SDSAttributesAdapter implements AttributesAdapter { - private static final Logger log = LogManager.getLogger(SDSAttributesFinderFeature.class); + private static final Logger log = LogManager.getLogger(SDSAttributesAdapter.class); private final SDSSession session; @@ -46,7 +47,7 @@ public class SDSAttributesAdapter implements AttributesAdapter { @Override public PathAttributes toAttributes(final Node node) { - final PathAttributes attributes = new PathAttributes(); + final PathAttributes attributes = new DefaultPathAttributes(); attributes.setVersionId(String.valueOf(node.getId())); attributes.setRevision(node.getBranchVersion()); if(node.isIsEncrypted() != null && !node.isIsEncrypted()) { @@ -107,7 +108,7 @@ public class SDSAttributesAdapter implements AttributesAdapter { } public PathAttributes toAttributes(final DeletedNode node) { - final PathAttributes attributes = new PathAttributes(); + final PathAttributes attributes = new DefaultPathAttributes(); attributes.setDuplicate(true); attributes.setVersionId(String.valueOf(node.getId())); attributes.setCreationDate(node.getCreatedAt() != null ? node.getCreatedAt().getMillis() : -1L); diff --git a/dracoon/src/main/java/ch/cyberduck/core/sds/SDSAttributesFinderFeature.java b/dracoon/src/main/java/ch/cyberduck/core/sds/SDSAttributesFinderFeature.java index 866e82a8fc..f6da2a3ab5 100644 --- a/dracoon/src/main/java/ch/cyberduck/core/sds/SDSAttributesFinderFeature.java +++ b/dracoon/src/main/java/ch/cyberduck/core/sds/SDSAttributesFinderFeature.java @@ -16,6 +16,7 @@ package ch.cyberduck.core.sds; */ import ch.cyberduck.core.Acl; +import ch.cyberduck.core.DefaultPathAttributes; import ch.cyberduck.core.ListProgressListener; import ch.cyberduck.core.Path; import ch.cyberduck.core.PathAttributes; @@ -53,7 +54,7 @@ public class SDSAttributesFinderFeature implements AttributesFinder { public PathAttributes find(final Path file, final ListProgressListener listener) throws BackgroundException { if(file.isRoot()) { // {"code":400,"message":"Bad Request","debugInfo":"Node ID must be positive.","errorCode":-80001} - final PathAttributes attributes = new PathAttributes(); + final PathAttributes attributes = new DefaultPathAttributes(); if(session.userAccount().isUserInRole(SDSPermissionsFeature.ROOM_MANAGER_ROLE)) { // We need to map user roles to ACLs in order to decide if creating a top-level room is allowed final Acl acl = new Acl(); diff --git a/dracoon/src/main/java/ch/cyberduck/core/sds/SDSBatchDeleteFeature.java b/dracoon/src/main/java/ch/cyberduck/core/sds/SDSBatchDeleteFeature.java index 62fffe9006..a64792a323 100644 --- a/dracoon/src/main/java/ch/cyberduck/core/sds/SDSBatchDeleteFeature.java +++ b/dracoon/src/main/java/ch/cyberduck/core/sds/SDSBatchDeleteFeature.java @@ -89,7 +89,7 @@ public class SDSBatchDeleteFeature implements Delete { } @Override - public EnumSet features() { + public EnumSet features(final Path file) { return EnumSet.of(Flags.recursive); } } diff --git a/dracoon/src/main/java/ch/cyberduck/core/sds/SDSDelegatingCopyFeature.java b/dracoon/src/main/java/ch/cyberduck/core/sds/SDSDelegatingCopyFeature.java index 1503176f46..71717a2a30 100644 --- a/dracoon/src/main/java/ch/cyberduck/core/sds/SDSDelegatingCopyFeature.java +++ b/dracoon/src/main/java/ch/cyberduck/core/sds/SDSDelegatingCopyFeature.java @@ -62,13 +62,18 @@ public class SDSDelegatingCopyFeature implements Copy { @Override public void preflight(final Path source, final Optional target) throws BackgroundException { if(proxy.isSupported(source, target)) { - return; + proxy.preflight(source, target); + } + else { + copy.preflight(source, target); } - copy.preflight(source, target); } @Override public EnumSet features(final Path source, final Path target) { - return proxy.features(source, target); + if(proxy.isSupported(source, Optional.of(target))) { + return proxy.features(source, target); + } + return copy.features(source, target); } } diff --git a/dracoon/src/main/java/ch/cyberduck/core/sds/SDSDeleteFeature.java b/dracoon/src/main/java/ch/cyberduck/core/sds/SDSDeleteFeature.java index 2ed34ff4e5..394681cd18 100644 --- a/dracoon/src/main/java/ch/cyberduck/core/sds/SDSDeleteFeature.java +++ b/dracoon/src/main/java/ch/cyberduck/core/sds/SDSDeleteFeature.java @@ -74,7 +74,7 @@ public class SDSDeleteFeature implements Delete { } @Override - public EnumSet features() { + public EnumSet features(final Path file) { return EnumSet.of(Flags.recursive); } } diff --git a/dracoon/src/main/java/ch/cyberduck/core/sds/SDSDirectS3MultipartWriteFeature.java b/dracoon/src/main/java/ch/cyberduck/core/sds/SDSDirectS3MultipartWriteFeature.java index e46748223a..25b5995f84 100644 --- a/dracoon/src/main/java/ch/cyberduck/core/sds/SDSDirectS3MultipartWriteFeature.java +++ b/dracoon/src/main/java/ch/cyberduck/core/sds/SDSDirectS3MultipartWriteFeature.java @@ -105,7 +105,7 @@ public class SDSDirectS3MultipartWriteFeature extends AbstractHttpWriteFeature(new MemorySegementingOutputStream(proxy, partsize), new SDSAttributesAdapter(session), status) { diff --git a/dracoon/src/main/java/ch/cyberduck/core/sds/SDSDirectS3UploadFeature.java b/dracoon/src/main/java/ch/cyberduck/core/sds/SDSDirectS3UploadFeature.java index 50c9f0ce81..b867eb88c2 100644 --- a/dracoon/src/main/java/ch/cyberduck/core/sds/SDSDirectS3UploadFeature.java +++ b/dracoon/src/main/java/ch/cyberduck/core/sds/SDSDirectS3UploadFeature.java @@ -50,6 +50,7 @@ import ch.cyberduck.core.sds.io.swagger.client.model.Node; import ch.cyberduck.core.sds.io.swagger.client.model.PresignedUrl; import ch.cyberduck.core.sds.io.swagger.client.model.S3FileUploadPart; import ch.cyberduck.core.sds.triplecrypt.TripleCryptConverter; +import ch.cyberduck.core.sds.triplecrypt.TripleCryptEncryptingInputStream; import ch.cyberduck.core.sds.triplecrypt.TripleCryptExceptionMappingService; import ch.cyberduck.core.threading.BackgroundExceptionCallable; import ch.cyberduck.core.threading.ThreadPool; @@ -97,13 +98,12 @@ public class SDSDirectS3UploadFeature extends HttpUploadFeature writer) { - this(session, nodeid, writer, HostPreferencesFactory.get(session.getHost()).getLong("s3.upload.multipart.size"), + public SDSDirectS3UploadFeature(final SDSSession session, final SDSNodeIdProvider nodeid) { + this(session, nodeid, HostPreferencesFactory.get(session.getHost()).getLong("s3.upload.multipart.size"), HostPreferencesFactory.get(session.getHost()).getInteger("s3.upload.multipart.concurrency")); } - public SDSDirectS3UploadFeature(final SDSSession session, final SDSNodeIdProvider nodeid, final Write writer, final Long partsize, final Integer concurrency) { - super(writer); + public SDSDirectS3UploadFeature(final SDSSession session, final SDSNodeIdProvider nodeid, final Long partsize, final Integer concurrency) { this.session = session; this.nodeid = nodeid; this.partsize = partsize; @@ -111,13 +111,16 @@ public class SDSDirectS3UploadFeature extends HttpUploadFeature 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 ThreadPool pool = ThreadPoolFactory.get("multipart", concurrency); try { final InputStream in; - if(new SDSTripleCryptEncryptorFeature(session, nodeid).isEncrypted(containerService.getContainer(file))) { - in = new SDSTripleCryptEncryptorFeature(session, nodeid).encrypt(file, local.getInputStream(), status); + if(status.getFilekey() != null) { + final ObjectReader reader = session.getClient().getJSON().getContext(null).readerFor(FileKey.class); + final FileKey fileKey = reader.readValue(status.getFilekey().array()); + in = new TripleCryptEncryptingInputStream(session, local.getInputStream(), + Crypto.createFileEncryptionCipher(TripleCryptConverter.toCryptoPlainFileKey(fileKey)), status); } else { in = local.getInputStream(); @@ -131,7 +134,7 @@ public class SDSDirectS3UploadFeature extends HttpUploadFeature etags = new HashMap<>(); final List presignedUrls = this.retrievePresignedUrls(createFileUploadResponse, status); final List> parts = new ArrayList<>(); @@ -144,17 +147,17 @@ public class SDSDirectS3UploadFeature extends HttpUploadFeature= 0; partNumber++) { final long length = Math.min(Math.max((size / (MAXIMUM_UPLOAD_PARTS - 1)), partsize), remaining); final PresignedUrl presignedUrl = presignedUrls.get(partNumber - 1); - if(new SDSTripleCryptEncryptorFeature(session, nodeid).isEncrypted(containerService.getContainer(file))) { + if(status.getFilekey() != null) { final Local temporary = temp.create(String.format("%s-%d", random, partNumber)); log.debug("Encrypted contents for part {} to {}", partNumber, temporary); final FileBuffer buffer = new FileBuffer(temporary); new StreamCopier(status, StreamProgress.noop).withAutoclose(false).withLimit(length) .transfer(in, new BufferOutputStream(buffer)); - parts.add(this.submit(pool, file, temporary, buffer, throttle, streamListener, status, + parts.add(this.submit(pool, new SDSDirectS3WriteFeature(session, nodeid), file, temporary, buffer, throttle, streamListener, status, presignedUrl.getUrl(), presignedUrl.getPartNumber(), 0L, length, callback)); } else { - parts.add(this.submit(pool, file, local, Buffer.noop, throttle, streamListener, status, + parts.add(this.submit(pool, write, file, local, Buffer.noop, throttle, streamListener, status, presignedUrl.getUrl(), presignedUrl.getPartNumber(), offset, length, callback)); } remaining -= length; @@ -235,7 +238,7 @@ public class SDSDirectS3UploadFeature extends HttpUploadFeature submit(final ThreadPool pool, final Path file, final Local local, + private Future submit(final ThreadPool pool, final Write write, final Path file, final Local local, final Buffer buffer, final BandwidthThrottle throttle, final StreamListener listener, final TransferStatus overall, final String url, final Integer partNumber, final long offset, final long length, final ConnectionCallback callback) throws ConnectionCanceledException { @@ -255,7 +258,7 @@ public class SDSDirectS3UploadFeature extends HttpUploadFeature { +public class SDSDirectoryFeature implements Directory { private static final Logger log = LogManager.getLogger(SDSDirectoryFeature.class); private final SDSSession session; @@ -54,7 +54,7 @@ public class SDSDirectoryFeature implements Directory { } @Override - public Path mkdir(final Path folder, final TransferStatus status) throws BackgroundException { + public Path mkdir(final Write writer, final Path folder, final TransferStatus status) throws BackgroundException { try { if(containerService.isContainer(folder)) { return this.createRoom(folder, HostPreferencesFactory.get(session.getHost()).getBoolean("sds.create.dataroom.encrypt")); diff --git a/dracoon/src/main/java/ch/cyberduck/core/sds/SDSEncryptionBulkFeature.java b/dracoon/src/main/java/ch/cyberduck/core/sds/SDSEncryptionBulkFeature.java index 06248136cd..fcec397320 100644 --- a/dracoon/src/main/java/ch/cyberduck/core/sds/SDSEncryptionBulkFeature.java +++ b/dracoon/src/main/java/ch/cyberduck/core/sds/SDSEncryptionBulkFeature.java @@ -20,9 +20,9 @@ import ch.cyberduck.core.Path; import ch.cyberduck.core.PathContainerService; import ch.cyberduck.core.exception.BackgroundException; import ch.cyberduck.core.features.Bulk; -import ch.cyberduck.core.features.Delete; import ch.cyberduck.core.features.Scheduler; import ch.cyberduck.core.preferences.HostPreferencesFactory; +import ch.cyberduck.core.sds.io.swagger.client.model.UserFileKeySetRequest; import ch.cyberduck.core.transfer.Transfer; import ch.cyberduck.core.transfer.TransferItem; import ch.cyberduck.core.transfer.TransferStatus; @@ -31,6 +31,7 @@ import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; import java.util.HashMap; +import java.util.List; import java.util.Map; public class SDSEncryptionBulkFeature implements Bulk { @@ -92,7 +93,7 @@ public class SDSEncryptionBulkFeature implements Bulk { default: if(HostPreferencesFactory.get(session.getHost()).getBoolean("sds.encryption.missingkeys.upload")) { if(session.userAccount().isEncryptionEnabled()) { - final Scheduler scheduler = session.getFeature(Scheduler.class); + final Scheduler> scheduler = session.getFeature(Scheduler.class); final Map rooms = this.getRoomEncryptionStatus(files); for(Map.Entry entry : files.entrySet()) { final Path file = entry.getKey().remote; @@ -108,9 +109,4 @@ public class SDSEncryptionBulkFeature implements Bulk { } } } - - @Override - public Bulk withDelete(final Delete delete) { - return this; - } } diff --git a/dracoon/src/main/java/ch/cyberduck/core/sds/SDSMoveFeature.java b/dracoon/src/main/java/ch/cyberduck/core/sds/SDSMoveFeature.java index 0a7042293f..0684c1734f 100644 --- a/dracoon/src/main/java/ch/cyberduck/core/sds/SDSMoveFeature.java +++ b/dracoon/src/main/java/ch/cyberduck/core/sds/SDSMoveFeature.java @@ -16,6 +16,7 @@ package ch.cyberduck.core.sds; */ import ch.cyberduck.core.ConnectionCallback; +import ch.cyberduck.core.DefaultPathAttributes; import ch.cyberduck.core.LocaleFactory; import ch.cyberduck.core.Path; import ch.cyberduck.core.PathAttributes; @@ -102,7 +103,7 @@ public class SDSMoveFeature implements Move { nodeid.cache(renamed, file.attributes().getVersionId()); nodeid.cache(file, null); // Copy original file attributes - return new Path(renamed).withAttributes(new PathAttributes(file.attributes()).setVersionId(String.valueOf(nodeId))); + return new Path(renamed).withAttributes(new DefaultPathAttributes(file.attributes()).setVersionId(String.valueOf(nodeId))); } } catch(ApiException e) { diff --git a/dracoon/src/main/java/ch/cyberduck/core/sds/SDSPermissionsFeature.java b/dracoon/src/main/java/ch/cyberduck/core/sds/SDSPermissionsFeature.java index c9a6f92188..51fad3c5a8 100644 --- a/dracoon/src/main/java/ch/cyberduck/core/sds/SDSPermissionsFeature.java +++ b/dracoon/src/main/java/ch/cyberduck/core/sds/SDSPermissionsFeature.java @@ -69,7 +69,7 @@ public class SDSPermissionsFeature implements AclPermission { } @Override - public List getAvailableAclUsers() { + public List getAvailableAclUsers(final List files) { return Collections.singletonList(new Acl.CanonicalUser()); } diff --git a/dracoon/src/main/java/ch/cyberduck/core/sds/SDSProtocol.java b/dracoon/src/main/java/ch/cyberduck/core/sds/SDSProtocol.java index 99121f3c1a..df9f0dfbc8 100644 --- a/dracoon/src/main/java/ch/cyberduck/core/sds/SDSProtocol.java +++ b/dracoon/src/main/java/ch/cyberduck/core/sds/SDSProtocol.java @@ -112,10 +112,10 @@ public class SDSProtocol extends AbstractProtocol { } public enum Authorization { - sql, - radius, - active_directory, oauth, + /** + * OAuth Password Flow + */ password } diff --git a/dracoon/src/main/java/ch/cyberduck/core/sds/SDSSearchFeature.java b/dracoon/src/main/java/ch/cyberduck/core/sds/SDSSearchFeature.java index e6ef3d496c..95d0c030b4 100644 --- a/dracoon/src/main/java/ch/cyberduck/core/sds/SDSSearchFeature.java +++ b/dracoon/src/main/java/ch/cyberduck/core/sds/SDSSearchFeature.java @@ -88,7 +88,7 @@ public class SDSSearchFeature implements Search { } @Override - public EnumSet features() { + public EnumSet features(final Path workdir) { return EnumSet.of(Flags.recursive); } diff --git a/dracoon/src/main/java/ch/cyberduck/core/sds/SDSSession.java b/dracoon/src/main/java/ch/cyberduck/core/sds/SDSSession.java index f091eb6cb5..8436c46b15 100644 --- a/dracoon/src/main/java/ch/cyberduck/core/sds/SDSSession.java +++ b/dracoon/src/main/java/ch/cyberduck/core/sds/SDSSession.java @@ -23,14 +23,12 @@ import ch.cyberduck.core.exception.LoginCanceledException; import ch.cyberduck.core.features.*; import ch.cyberduck.core.http.CustomServiceUnavailableRetryStrategy; import ch.cyberduck.core.http.DefaultHttpRateLimiter; -import ch.cyberduck.core.http.ExecutionCountServiceUnavailableRetryStrategy; import ch.cyberduck.core.http.HttpSession; import ch.cyberduck.core.http.RateLimitingHttpRequestInterceptor; import ch.cyberduck.core.jersey.HttpComponentsProvider; import ch.cyberduck.core.oauth.OAuth2AuthorizationService; import ch.cyberduck.core.oauth.OAuth2ErrorResponseInterceptor; import ch.cyberduck.core.oauth.OAuth2RequestInterceptor; -import ch.cyberduck.core.preferences.HostPreferences; import ch.cyberduck.core.preferences.HostPreferencesFactory; import ch.cyberduck.core.proxy.ProxyFinder; import ch.cyberduck.core.sds.io.swagger.client.ApiException; @@ -43,6 +41,7 @@ import ch.cyberduck.core.sds.io.swagger.client.model.AlgorithmVersionInfoList; import ch.cyberduck.core.sds.io.swagger.client.model.ClassificationPoliciesConfig; import ch.cyberduck.core.sds.io.swagger.client.model.CreateKeyPairRequest; import ch.cyberduck.core.sds.io.swagger.client.model.GeneralSettingsInfo; +import ch.cyberduck.core.sds.io.swagger.client.model.Node; import ch.cyberduck.core.sds.io.swagger.client.model.SoftwareVersionData; import ch.cyberduck.core.sds.io.swagger.client.model.SystemDefaults; import ch.cyberduck.core.sds.io.swagger.client.model.UserAccount; @@ -98,15 +97,12 @@ import static ch.cyberduck.core.oauth.OAuth2AuthorizationService.CYBERDUCK_REDIR public class SDSSession extends HttpSession { private static final Logger log = LogManager.getLogger(SDSSession.class); - public static final String SDS_AUTH_TOKEN_HEADER = "X-Sds-Auth-Token"; public static final int DEFAULT_CHUNKSIZE = 16; public static final String VERSION_REGEX = "(([0-9]+)\\.([0-9]+)\\.([0-9]+)).*"; private OAuth2RequestInterceptor authorizationService; - private final HostPreferences preferences = HostPreferencesFactory.get(host); - private final ExpiringObjectHolder userAccount = new ExpiringObjectHolder<>(preferences.getLong("sds.useracount.ttl")); @@ -151,27 +147,16 @@ public class SDSSession extends HttpSession { @Override protected SDSApiClient connect(final ProxyFinder proxy, final HostKeyCallback key, final LoginCallback prompt, final CancelCallback cancel) throws BackgroundException { final HttpClientBuilder configuration = builder.build(proxy, this, prompt); - authorizationService = new OAuth2RequestInterceptor(configuration.addInterceptorLast(new HttpRequestInterceptor() { - @Override - public void process(final HttpRequest request, final HttpContext context) { - if(request instanceof HttpRequestWrapper) { - final HttpRequestWrapper wrapper = (HttpRequestWrapper) request; - if(null != wrapper.getTarget()) { - if(StringUtils.equals(wrapper.getTarget().getHostName(), host.getHostname())) { - request.addHeader(HttpHeaders.AUTHORIZATION, - String.format("Basic %s", Base64.getEncoder().encodeToString(String.format("%s:%s", host.getProtocol().getOAuthClientId(), host.getProtocol().getOAuthClientSecret()).getBytes(StandardCharsets.UTF_8)))); - } - } - } - } - }).build(), host, prompt) { + authorizationService = new OAuth2RequestInterceptor(configuration.build(), host, prompt) { @Override public void process(final HttpRequest request, final HttpContext context) throws HttpException, IOException { if(request instanceof HttpRequestWrapper) { final HttpRequestWrapper wrapper = (HttpRequestWrapper) request; if(null != wrapper.getTarget()) { if(StringUtils.equals(wrapper.getTarget().getHostName(), host.getHostname())) { - super.process(request, context); + if(!StringUtils.contains(wrapper.getRequestLine().getUri(), "/api/v4/public")) { + super.process(request, context); + } } } } @@ -190,21 +175,14 @@ public class SDSSession extends HttpSession { throw new DefaultIOExceptionMappingService().map(e); } configuration.setServiceUnavailableRetryStrategy(new CustomServiceUnavailableRetryStrategy(host, - - new ExecutionCountServiceUnavailableRetryStrategy(new PreconditionFailedResponseInterceptor(host, authorizationService, prompt), - new OAuth2ErrorResponseInterceptor(host, authorizationService)))); + new PreconditionFailedResponseInterceptor(host, authorizationService, prompt), + new OAuth2ErrorResponseInterceptor(host, authorizationService))); if(HostPreferencesFactory.get(host).getBoolean("sds.limit.requests.enable")) { configuration.addInterceptorLast(new RateLimitingHttpRequestInterceptor(new DefaultHttpRateLimiter( HostPreferencesFactory.get(host).getInteger("sds.limit.requests.second") ))); } configuration.addInterceptorLast(authorizationService); - configuration.addInterceptorLast(new HttpRequestInterceptor() { - @Override - public void process(final HttpRequest request, final HttpContext context) { - request.removeHeaders(SDSSession.SDS_AUTH_TOKEN_HEADER); - } - }); final CloseableHttpClient apache = configuration.build(); final SDSApiClient client = new SDSApiClient(apache); client.setBasePath(new HostUrlProvider().withUsername(false).withPath(true).get(host.getProtocol().getScheme(), host.getPort(), @@ -234,25 +212,13 @@ public class SDSSession extends HttpSession { LocaleFactory.localizedString("Your DRACOON environment is outdated and no longer works with this application. Please contact your administrator.", "SDS")); } } - final Credentials credentials; + final Credentials credentials = host.getCredentials(); // The provided token is valid for two hours, every usage resets this period to two full hours again. Logging off invalidates the token. switch(SDSProtocol.Authorization.valueOf(host.getProtocol().getAuthorization())) { case oauth: case password: - if("x-dracoon-action:oauth".equals(CYBERDUCK_REDIRECT_URI)) { - if(matcher.matches()) { - if(new Version(matcher.group(1)).compareTo(new Version("4.15.0")) >= 0) { - authorizationService.withRedirectUri(CYBERDUCK_REDIRECT_URI); - } - } - else { - log.warn("Failure to parse software version {}", version); - } - } - credentials = authorizationService.validate(); + credentials.setOauth(authorizationService.validate(credentials.getOauth())); break; - default: - credentials = host.getCredentials(); } final UserAccount account; try { @@ -586,10 +552,22 @@ public class SDSSession extends HttpSession { } @Override - protected void logout() { + protected void logout() throws BackgroundException { scheduler.shutdown(false); - client.getHttpClient().close(); - nodeid.clear(); + super.logout(); + } + + @Override + public void disconnect() throws BackgroundException { + try { + nodeid.clear(); + if(client != null) { + client.getHttpClient().close(); + } + } + finally { + super.disconnect(); + } } @Override @@ -603,16 +581,22 @@ public class SDSSession extends HttpSession { } if(type == Upload.class) { if(HostPreferencesFactory.get(host).getBoolean("sds.upload.s3.enable")) { - return (T) new SDSDirectS3UploadFeature(this, nodeid, new SDSDirectS3WriteFeature(this, nodeid)); + return (T) new SDSDirectS3UploadFeature(this, nodeid); } - return (T) new DefaultUploadFeature(new SDSDelegatingWriteFeature(this, nodeid, new SDSMultipartWriteFeature(this, nodeid))); + return (T) new DefaultUploadFeature(this); } - if(type == Write.class || type == MultipartWrite.class) { + if(type == MultipartWrite.class) { if(HostPreferencesFactory.get(host).getBoolean("sds.upload.s3.enable")) { return (T) new SDSDelegatingWriteFeature(this, nodeid, new SDSDirectS3MultipartWriteFeature(this, nodeid)); } return (T) new SDSDelegatingWriteFeature(this, nodeid, new SDSMultipartWriteFeature(this, nodeid)); } + if(type == Write.class) { + if(HostPreferencesFactory.get(host).getBoolean("sds.upload.s3.enable")) { + return (T) new SDSDelegatingWriteFeature(this, nodeid, new SDSDirectS3WriteFeature(this, nodeid)); + } + return (T) new SDSDelegatingWriteFeature(this, nodeid, new SDSMultipartWriteFeature(this, nodeid)); + } if(type == Directory.class) { return (T) new SDSDirectoryFeature(this, nodeid); } @@ -658,9 +642,6 @@ public class SDSSession extends HttpSession { if(type == Versioning.class) { return (T) new SDSVersioningFeature(this, nodeid); } - if(type == Encryptor.class) { - return (T) new SDSTripleCryptEncryptorFeature(this, nodeid); - } if(type == Scheduler.class) { return (T) scheduler; } diff --git a/dracoon/src/main/java/ch/cyberduck/core/sds/SDSThresholdDeleteFeature.java b/dracoon/src/main/java/ch/cyberduck/core/sds/SDSThresholdDeleteFeature.java index c4697bc618..0ee5a33232 100644 --- a/dracoon/src/main/java/ch/cyberduck/core/sds/SDSThresholdDeleteFeature.java +++ b/dracoon/src/main/java/ch/cyberduck/core/sds/SDSThresholdDeleteFeature.java @@ -52,7 +52,7 @@ public class SDSThresholdDeleteFeature implements Delete { } @Override - public EnumSet features() { + public EnumSet features(final Path file) { return EnumSet.of(Flags.recursive); } diff --git a/dracoon/src/main/java/ch/cyberduck/core/sds/SDSTouchFeature.java b/dracoon/src/main/java/ch/cyberduck/core/sds/SDSTouchFeature.java index ef333598e8..9b858c142d 100644 --- a/dracoon/src/main/java/ch/cyberduck/core/sds/SDSTouchFeature.java +++ b/dracoon/src/main/java/ch/cyberduck/core/sds/SDSTouchFeature.java @@ -22,6 +22,7 @@ import ch.cyberduck.core.exception.AccessDeniedException; import ch.cyberduck.core.exception.BackgroundException; import ch.cyberduck.core.exception.InvalidFilenameException; import ch.cyberduck.core.exception.QuotaException; +import ch.cyberduck.core.features.Write; import ch.cyberduck.core.preferences.HostPreferencesFactory; import ch.cyberduck.core.sds.io.swagger.client.model.Node; import ch.cyberduck.core.shared.DefaultTouchFeature; @@ -44,19 +45,17 @@ public class SDSTouchFeature extends DefaultTouchFeature { public SDSTouchFeature(final SDSSession session, final SDSNodeIdProvider nodeid) { - super(new SDSDelegatingWriteFeature(session, nodeid, - HostPreferencesFactory.get(session.getHost()).getBoolean("sds.upload.s3.enable") ? - new SDSDirectS3MultipartWriteFeature(session, nodeid) : new SDSMultipartWriteFeature(session, nodeid))); + super(session); this.session = session; this.nodeid = nodeid; } @Override - public Path touch(final Path file, final TransferStatus status) throws BackgroundException { + public Path touch(final Write writer, final Path file, final TransferStatus status) throws BackgroundException { if(new SDSTripleCryptEncryptorFeature(session, nodeid).isEncrypted(containerService.getContainer(file))) { status.setFilekey(SDSTripleCryptEncryptorFeature.generateFileKey()); } - return super.touch(file, status); + return super.touch(writer, file, status); } @Override diff --git a/dracoon/src/main/java/ch/cyberduck/core/sds/SDSTripleCryptEncryptorFeature.java b/dracoon/src/main/java/ch/cyberduck/core/sds/SDSTripleCryptEncryptorFeature.java index 7d41647769..a58fdba9d9 100644 --- a/dracoon/src/main/java/ch/cyberduck/core/sds/SDSTripleCryptEncryptorFeature.java +++ b/dracoon/src/main/java/ch/cyberduck/core/sds/SDSTripleCryptEncryptorFeature.java @@ -19,31 +19,19 @@ import ch.cyberduck.core.DefaultIOExceptionMappingService; import ch.cyberduck.core.Path; import ch.cyberduck.core.PathAttributes; import ch.cyberduck.core.exception.BackgroundException; -import ch.cyberduck.core.features.Encryptor; import ch.cyberduck.core.sds.io.swagger.client.JSON; import ch.cyberduck.core.sds.io.swagger.client.model.FileKey; import ch.cyberduck.core.sds.triplecrypt.TripleCryptConverter; -import ch.cyberduck.core.sds.triplecrypt.TripleCryptEncryptingInputStream; -import ch.cyberduck.core.sds.triplecrypt.TripleCryptExceptionMappingService; -import ch.cyberduck.core.transfer.TransferStatus; - -import org.apache.logging.log4j.LogManager; -import org.apache.logging.log4j.Logger; import java.io.ByteArrayOutputStream; import java.io.IOException; -import java.io.InputStream; import java.nio.ByteBuffer; import com.dracoon.sdk.crypto.Crypto; -import com.dracoon.sdk.crypto.error.CryptoSystemException; -import com.dracoon.sdk.crypto.error.UnknownVersionException; import com.dracoon.sdk.crypto.model.PlainFileKey; -import com.fasterxml.jackson.databind.ObjectReader; import com.fasterxml.jackson.databind.ObjectWriter; -public class SDSTripleCryptEncryptorFeature implements Encryptor { - private static final Logger log = LogManager.getLogger(SDSTripleCryptEncryptorFeature.class); +public class SDSTripleCryptEncryptorFeature { private final SDSSession session; private final SDSNodeIdProvider nodeid; @@ -69,26 +57,6 @@ public class SDSTripleCryptEncryptorFeature implements Encryptor { this.nodeid = nodeid; } - @Override - public InputStream encrypt(final Path file, final InputStream proxy, final TransferStatus status) throws BackgroundException { - try { - final ObjectReader reader = session.getClient().getJSON().getContext(null).readerFor(FileKey.class); - log.debug("Read file key for file {}", file); - if(null == status.getFilekey()) { - status.setFilekey(generateFileKey()); - } - final FileKey fileKey = reader.readValue(status.getFilekey().array()); - return new TripleCryptEncryptingInputStream(session, proxy, - Crypto.createFileEncryptionCipher(TripleCryptConverter.toCryptoPlainFileKey(fileKey)), status); - } - catch(IOException e) { - throw new DefaultIOExceptionMappingService().map("Upload {0} failed", e, file); - } - catch(CryptoSystemException | UnknownVersionException e) { - throw new TripleCryptExceptionMappingService().map("Upload {0} failed", e, file); - } - } - public boolean isEncrypted(final Path file) throws BackgroundException { final PathAttributes attr = file.attributes(); if(attr.getCustom().containsKey(SDSAttributesFinderFeature.KEY_ENCRYPTED)) { diff --git a/dracoon/src/main/java/ch/cyberduck/core/sds/SDSVersioningFeature.java b/dracoon/src/main/java/ch/cyberduck/core/sds/SDSVersioningFeature.java index c3a45c4023..392c44a656 100644 --- a/dracoon/src/main/java/ch/cyberduck/core/sds/SDSVersioningFeature.java +++ b/dracoon/src/main/java/ch/cyberduck/core/sds/SDSVersioningFeature.java @@ -62,7 +62,7 @@ public class SDSVersioningFeature implements Versioning { .resolutionStrategy(RestoreDeletedNodesRequest.ResolutionStrategyEnum.OVERWRITE) .keepShareLinks(HostPreferencesFactory.get(session.getHost()).getBoolean("sds.upload.sharelinks.keep")) .addDeletedNodeIdsItem(Long.parseLong(nodeid.getVersionId(file))) - .parentId(Long.parseLong(nodeid.getVersionId(file.getParent()))), StringUtils.EMPTY);//todo + .parentId(Long.parseLong(nodeid.getVersionId(file.getParent()))), StringUtils.EMPTY); } catch(ApiException e) { throw new SDSExceptionMappingService(nodeid).map("Failure to write attributes of {0}", e, file); diff --git a/dracoon/src/main/java/ch/cyberduck/core/sds/triplecrypt/TripleCryptKeyPair.java b/dracoon/src/main/java/ch/cyberduck/core/sds/triplecrypt/TripleCryptKeyPair.java index 3f90764d55..5fd4ebba2e 100644 --- a/dracoon/src/main/java/ch/cyberduck/core/sds/triplecrypt/TripleCryptKeyPair.java +++ b/dracoon/src/main/java/ch/cyberduck/core/sds/triplecrypt/TripleCryptKeyPair.java @@ -24,8 +24,8 @@ import ch.cyberduck.core.LoginOptions; import ch.cyberduck.core.PasswordCallback; import ch.cyberduck.core.PasswordStoreFactory; import ch.cyberduck.core.Path; +import ch.cyberduck.core.exception.AccessDeniedException; import ch.cyberduck.core.exception.BackgroundException; -import ch.cyberduck.core.exception.LocalAccessDeniedException; import ch.cyberduck.core.exception.LoginCanceledException; import ch.cyberduck.core.shared.DefaultUrlProvider; import ch.cyberduck.core.vault.VaultCredentials; @@ -66,7 +66,7 @@ public class TripleCryptKeyPair { } } else { - credentials = new VaultCredentials(passphrase).withSaved(false); + credentials = new VaultCredentials(passphrase).setSaved(false); } if(!Crypto.checkUserKeyPair(keypair, credentials.getPassword().toCharArray())) { return this.unlock(callback, bookmark, keypair, null, String.format("%s. %s", LocaleFactory.localizedString("Invalid passphrase", "Credentials"), LocaleFactory.localizedString("Enter your decryption password to access encrypted data rooms.", "SDS"))); @@ -78,7 +78,7 @@ public class TripleCryptKeyPair { keychain.addPassword(toServiceName(bookmark, keypair.getUserPublicKey().getVersion()), toAccountName(bookmark), credentials.getPassword()); } - catch(LocalAccessDeniedException e) { + catch(AccessDeniedException e) { log.error("Failure {} saving credentials for {} in password store", e, bookmark); } } diff --git a/dracoon/src/test/java/ch/cyberduck/core/sds/SDSAttributesFinderFeatureTest.java b/dracoon/src/test/java/ch/cyberduck/core/sds/SDSAttributesFinderFeatureTest.java index 880d82f1f6..0916118b7b 100644 --- a/dracoon/src/test/java/ch/cyberduck/core/sds/SDSAttributesFinderFeatureTest.java +++ b/dracoon/src/test/java/ch/cyberduck/core/sds/SDSAttributesFinderFeatureTest.java @@ -48,7 +48,7 @@ public class SDSAttributesFinderFeatureTest extends AbstractSDSTest { public void testFindNotFound() throws Exception { final SDSNodeIdProvider nodeid = new SDSNodeIdProvider(session); final Path room = new SDSDirectoryFeature(session, nodeid).mkdir( - new Path(new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory, Path.Type.volume)), new TransferStatus()); + new SDSDirectS3MultipartWriteFeature(session, nodeid), new Path(new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory, Path.Type.volume)), new TransferStatus()); final Path test = new Path(room, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)); final SDSAttributesFinderFeature f = new SDSAttributesFinderFeature(session, nodeid); try { @@ -72,8 +72,8 @@ public class SDSAttributesFinderFeatureTest extends AbstractSDSTest { public void testFindFile() throws Exception { final SDSNodeIdProvider nodeid = new SDSNodeIdProvider(session); final Path room = new SDSDirectoryFeature(session, nodeid).mkdir( - new Path(new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory, Path.Type.volume)), new TransferStatus()); - final Path test = new SDSTouchFeature(session, nodeid).touch(new Path(room, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)), new TransferStatus()); + new SDSDirectS3MultipartWriteFeature(session, nodeid), new Path(new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory, Path.Type.volume)), new TransferStatus()); + final Path test = new SDSTouchFeature(session, nodeid).touch(new SDSDirectS3MultipartWriteFeature(session, nodeid), new Path(room, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)), new TransferStatus()); final SDSAttributesFinderFeature f = new SDSAttributesFinderFeature(session, nodeid); final PathAttributes attributes = f.find(test); assertNotNull(attributes.getRevision()); @@ -99,8 +99,8 @@ public class SDSAttributesFinderFeatureTest extends AbstractSDSTest { public void testFindDirectory() throws Exception { final SDSNodeIdProvider nodeid = new SDSNodeIdProvider(session); final Path room = new SDSDirectoryFeature(session, nodeid).mkdir( - new Path(new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory, Path.Type.volume)), new TransferStatus()); - final Path test = new SDSDirectoryFeature(session, nodeid).mkdir(new Path(room, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory)), new TransferStatus()); + new SDSDirectS3MultipartWriteFeature(session, nodeid), new Path(new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory, Path.Type.volume)), new TransferStatus()); + final Path test = new SDSDirectoryFeature(session, nodeid).mkdir(new SDSDirectS3MultipartWriteFeature(session, nodeid), new Path(room, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory)), new TransferStatus()); final SDSAttributesFinderFeature f = new SDSAttributesFinderFeature(session, nodeid); final PathAttributes attributes = f.find(test); assertEquals(0L, attributes.getSize()); @@ -124,7 +124,7 @@ public class SDSAttributesFinderFeatureTest extends AbstractSDSTest { public void testFindRoom() throws Exception { final SDSNodeIdProvider nodeid = new SDSNodeIdProvider(session); final Path room = new SDSDirectoryFeature(session, nodeid).mkdir( - new Path(new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory, Path.Type.volume)), new TransferStatus()); + new SDSDirectS3MultipartWriteFeature(session, nodeid), new Path(new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory, Path.Type.volume)), new TransferStatus()); final PathAttributes attributes = new SDSAttributesFinderFeature(session, nodeid).find(room); assertNotEquals(PathAttributes.EMPTY, attributes); assertEquals(0L, attributes.getSize()); @@ -140,12 +140,12 @@ public class SDSAttributesFinderFeatureTest extends AbstractSDSTest { public void testVersioning() throws Exception { final SDSNodeIdProvider nodeid = new SDSNodeIdProvider(session); final Path room = new SDSDirectoryFeature(session, nodeid).mkdir( - new Path(new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory, Path.Type.volume)), new TransferStatus()); - final Path folder = new SDSDirectoryFeature(session, nodeid).mkdir(new Path(room, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory)), new TransferStatus()); + new SDSDirectS3MultipartWriteFeature(session, nodeid), new Path(new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory, Path.Type.volume)), new TransferStatus()); + final Path folder = new SDSDirectoryFeature(session, nodeid).mkdir(new SDSDirectS3MultipartWriteFeature(session, nodeid), new Path(room, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory)), new TransferStatus()); final SDSAttributesFinderFeature f = new SDSAttributesFinderFeature(session, nodeid); final PathAttributes previous = f.find(folder, new DisabledListProgressListener()); assertNotEquals(-1L, previous.getRevision().longValue()); - final Path test = new SDSTouchFeature(session, nodeid).touch(new Path(folder, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)), new TransferStatus()); + final Path test = new SDSTouchFeature(session, nodeid).touch(new SDSDirectS3MultipartWriteFeature(session, nodeid), new Path(folder, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)), new TransferStatus()); final String initialVersion = test.attributes().getVersionId(); assertEquals(test.getParent(), folder); assertTrue(new SDSFindFeature(session, nodeid).find(test)); @@ -173,8 +173,8 @@ public class SDSAttributesFinderFeatureTest extends AbstractSDSTest { public void testChangedNodeId() throws Exception { final SDSNodeIdProvider nodeid = new SDSNodeIdProvider(session); final Path room = new SDSDirectoryFeature(session, nodeid).mkdir( - new Path(new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory, Path.Type.volume)), new TransferStatus()); - final Path test = new SDSTouchFeature(session, nodeid).touch(new Path(room, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)), new TransferStatus()); + new SDSDirectS3MultipartWriteFeature(session, nodeid), new Path(new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory, Path.Type.volume)), new TransferStatus()); + final Path test = new SDSTouchFeature(session, nodeid).touch(new SDSDirectS3MultipartWriteFeature(session, nodeid), new Path(room, 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 diff --git a/dracoon/src/test/java/ch/cyberduck/core/sds/SDSBatchDeleteFeatureTest.java b/dracoon/src/test/java/ch/cyberduck/core/sds/SDSBatchDeleteFeatureTest.java index 232954ad34..edc994bc08 100644 --- a/dracoon/src/test/java/ch/cyberduck/core/sds/SDSBatchDeleteFeatureTest.java +++ b/dracoon/src/test/java/ch/cyberduck/core/sds/SDSBatchDeleteFeatureTest.java @@ -21,6 +21,7 @@ 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.features.Home; import ch.cyberduck.core.transfer.TransferStatus; import ch.cyberduck.test.IntegrationTest; @@ -39,12 +40,12 @@ public class SDSBatchDeleteFeatureTest extends AbstractSDSTest { @Test public void testDeleteFiles() throws Exception { final SDSNodeIdProvider nodeid = new SDSNodeIdProvider(session); - final Path room = new SDSDirectoryFeature(session, nodeid).mkdir(new Path( + final Path room = new SDSDirectoryFeature(session, nodeid).mkdir(new SDSDirectS3MultipartWriteFeature(session, nodeid), new Path( new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory, Path.Type.volume)), new TransferStatus()); final Path file1 = new Path(room, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)); final Path file2 = new Path(room, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)); - new SDSTouchFeature(session, nodeid).touch(file1, new TransferStatus()); - new SDSTouchFeature(session, nodeid).touch(file2, new TransferStatus()); + new SDSTouchFeature(session, nodeid).touch(new SDSDirectS3MultipartWriteFeature(session, nodeid), file1, new TransferStatus()); + new SDSTouchFeature(session, nodeid).touch(new SDSDirectS3MultipartWriteFeature(session, nodeid), file2, new TransferStatus()); new SDSBatchDeleteFeature(session, nodeid).delete(Arrays.asList(file1, file2), new DisabledLoginCallback(), new Delete.DisabledCallback()); assertFalse(new SDSFindFeature(session, nodeid).find(file1)); assertFalse(new SDSFindFeature(session, nodeid).find(file2)); @@ -55,11 +56,11 @@ public class SDSBatchDeleteFeatureTest extends AbstractSDSTest { public void testDeleteRecursively() throws Exception { final SDSNodeIdProvider nodeid = new SDSNodeIdProvider(session); final Path room = new SDSDirectoryFeature(session, nodeid).mkdir( - new Path(new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory, Path.Type.volume)), new TransferStatus()); + new SDSDirectS3MultipartWriteFeature(session, nodeid), new Path(new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory, Path.Type.volume)), new TransferStatus()); final Path folder = new SDSDirectoryFeature(session, nodeid).mkdir( - new Path(room, new AlphanumericRandomStringService().random(), EnumSet.of(AbstractPath.Type.directory)), new TransferStatus()); + new SDSDirectS3MultipartWriteFeature(session, nodeid), new Path(room, new AlphanumericRandomStringService().random(), EnumSet.of(AbstractPath.Type.directory)), new TransferStatus()); final Path file = new SDSTouchFeature(session, nodeid).touch( - new Path(folder, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)), new TransferStatus()); + new SDSDirectS3MultipartWriteFeature(session, nodeid), new Path(folder, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)), new TransferStatus()); assertTrue(new SDSFindFeature(session, nodeid).find(file)); assertNotNull(nodeid.getVersionId(file)); new SDSBatchDeleteFeature(session, nodeid).delete(Collections.singletonList(room), new DisabledLoginCallback(), new Delete.DisabledCallback()); @@ -84,13 +85,13 @@ public class SDSBatchDeleteFeatureTest extends AbstractSDSTest { @Test public void testDeleteFolderRoomWithContent() throws Exception { final SDSNodeIdProvider nodeid = new SDSNodeIdProvider(session); - final Path room = new SDSDirectoryFeature(session, nodeid).mkdir(new Path( + final Path room = new SDSDirectoryFeature(session, nodeid).mkdir(new SDSDirectS3MultipartWriteFeature(session, nodeid), new Path( new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory, Path.Type.volume)), new TransferStatus()); - final Path folder = new SDSDirectoryFeature(session, nodeid).mkdir(new Path(room, + final Path folder = new SDSDirectoryFeature(session, nodeid).mkdir(new SDSDirectS3MultipartWriteFeature(session, nodeid), new Path(room, new AlphanumericRandomStringService().random().toLowerCase(), EnumSet.of(Path.Type.directory)), new TransferStatus()); assertTrue(new SDSFindFeature(session, nodeid).find(folder)); final Path file = new Path(folder, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)); - new SDSTouchFeature(session, nodeid).touch(file, new TransferStatus()); + new SDSTouchFeature(session, nodeid).touch(new SDSDirectS3MultipartWriteFeature(session, nodeid), file, new TransferStatus()); assertTrue(new SDSFindFeature(session, nodeid).find(file)); new SDSBatchDeleteFeature(session, nodeid).delete(Collections.singletonList(folder), new DisabledLoginCallback(), new Delete.DisabledCallback()); assertFalse(new SDSFindFeature(session, nodeid).find(folder)); @@ -101,6 +102,6 @@ public class SDSBatchDeleteFeatureTest extends AbstractSDSTest { @Test public void testIsRecursive() { final SDSNodeIdProvider nodeid = new SDSNodeIdProvider(session); - assertTrue(new SDSBatchDeleteFeature(session, nodeid).isRecursive()); + assertTrue(new SDSBatchDeleteFeature(session, nodeid).features(Home.root()).contains(Delete.Flags.recursive)); } } \ No newline at end of file diff --git a/dracoon/src/test/java/ch/cyberduck/core/sds/SDSDelegatingCopyFeatureTest.java b/dracoon/src/test/java/ch/cyberduck/core/sds/SDSDelegatingCopyFeatureTest.java index 569180ea52..fd3992e319 100644 --- a/dracoon/src/test/java/ch/cyberduck/core/sds/SDSDelegatingCopyFeatureTest.java +++ b/dracoon/src/test/java/ch/cyberduck/core/sds/SDSDelegatingCopyFeatureTest.java @@ -66,10 +66,10 @@ public class SDSDelegatingCopyFeatureTest extends AbstractSDSTest { public void testCopyFileServerSide() throws Exception { final SDSNodeIdProvider nodeid = new SDSNodeIdProvider(session); final Path room = new SDSDirectoryFeature(session, nodeid).mkdir( - new Path(new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory, Path.Type.volume)), new TransferStatus()); - final Path test = new SDSTouchFeature(session, nodeid).touch(new Path(room, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)), new TransferStatus()); - final Path copy = new Path(new SDSDirectoryFeature(session, nodeid).mkdir(new Path(room, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory)), new TransferStatus()), test.getName(), EnumSet.of(Path.Type.file)); - new SDSTouchFeature(session, nodeid).touch(copy, new TransferStatus()); + new SDSDirectS3MultipartWriteFeature(session, nodeid), new Path(new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory, Path.Type.volume)), new TransferStatus()); + final Path test = new SDSTouchFeature(session, nodeid).touch(new SDSDirectS3MultipartWriteFeature(session, nodeid), new Path(room, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)), new TransferStatus()); + final Path copy = new Path(new SDSDirectoryFeature(session, nodeid).mkdir(new SDSDirectS3MultipartWriteFeature(session, nodeid), new Path(room, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory)), new TransferStatus()), test.getName(), EnumSet.of(Path.Type.file)); + new SDSTouchFeature(session, nodeid).touch(new SDSDirectS3MultipartWriteFeature(session, nodeid), copy, new TransferStatus()); final SDSCopyFeature proxy = new SDSCopyFeature(session, nodeid); assertThrows(UnsupportedException.class, () -> proxy.preflight(room, Optional.of(test))); try { @@ -93,9 +93,9 @@ public class SDSDelegatingCopyFeatureTest extends AbstractSDSTest { public void testCopyFileWithRename() throws Exception { final SDSNodeIdProvider nodeid = new SDSNodeIdProvider(session); final Path room = new SDSDirectoryFeature(session, nodeid).mkdir( - new Path(new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory, Path.Type.volume)), new TransferStatus()); - final Path test = new SDSTouchFeature(session, nodeid).touch(new Path(room, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)), new TransferStatus()); - final Path copy = new Path(new SDSDirectoryFeature(session, nodeid).mkdir(new Path(room, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory)), new TransferStatus()), new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)); + new SDSDirectS3MultipartWriteFeature(session, nodeid), new Path(new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory, Path.Type.volume)), new TransferStatus()); + final Path test = new SDSTouchFeature(session, nodeid).touch(new SDSDirectS3MultipartWriteFeature(session, nodeid), new Path(room, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)), new TransferStatus()); + final Path copy = new Path(new SDSDirectoryFeature(session, nodeid).mkdir(new SDSDirectS3MultipartWriteFeature(session, nodeid), new Path(room, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory)), new TransferStatus()), new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)); final SDSCopyFeature proxy = new SDSCopyFeature(session, nodeid); final SDSDelegatingCopyFeature feature = new SDSDelegatingCopyFeature(session, nodeid, proxy); assertFalse(proxy.isSupported(test, Optional.of(copy))); @@ -112,15 +112,15 @@ public class SDSDelegatingCopyFeatureTest extends AbstractSDSTest { public void testCopyServerSideToExistingFile() throws Exception { final SDSNodeIdProvider nodeid = new SDSNodeIdProvider(session); final Path room = new SDSDirectoryFeature(session, nodeid).mkdir( - new Path(new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory, Path.Type.volume)), new TransferStatus()); + new SDSDirectS3MultipartWriteFeature(session, nodeid), new Path(new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory, Path.Type.volume)), new TransferStatus()); final Path sourceFolder = new Path(room, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory)); final Path targetFolder = new Path(room, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory)); - new SDSDirectoryFeature(session, nodeid).mkdir(sourceFolder, new TransferStatus()); - new SDSDirectoryFeature(session, nodeid).mkdir(targetFolder, new TransferStatus()); + new SDSDirectoryFeature(session, nodeid).mkdir(new SDSDirectS3MultipartWriteFeature(session, nodeid), sourceFolder, new TransferStatus()); + new SDSDirectoryFeature(session, nodeid).mkdir(new SDSDirectS3MultipartWriteFeature(session, nodeid), targetFolder, new TransferStatus()); final Path test = new Path(sourceFolder, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)); - new SDSTouchFeature(session, nodeid).touch(test, new TransferStatus()); + new SDSTouchFeature(session, nodeid).touch(new SDSDirectS3MultipartWriteFeature(session, nodeid), test, new TransferStatus()); final Path copy = new Path(targetFolder, test.getName(), EnumSet.of(Path.Type.file)); - new SDSTouchFeature(session, nodeid).touch(copy, new TransferStatus()); + new SDSTouchFeature(session, nodeid).touch(new SDSDirectS3MultipartWriteFeature(session, nodeid), copy, new TransferStatus()); final SDSDelegatingCopyFeature feature = new SDSDelegatingCopyFeature(session, nodeid, new SDSCopyFeature(session, nodeid)); assertTrue(feature.isSupported(test, Optional.of(copy))); final Path target = feature.copy(test, copy, new TransferStatus().setExists(true), new DisabledConnectionCallback(), new DisabledStreamListener()); @@ -136,11 +136,11 @@ public class SDSDelegatingCopyFeatureTest extends AbstractSDSTest { public void testCopyWithRenameToExistingFile() throws Exception { final SDSNodeIdProvider nodeid = new SDSNodeIdProvider(session); final Path room = new SDSDirectoryFeature(session, nodeid).mkdir( - new Path(new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory, Path.Type.volume)), new TransferStatus()); + new SDSDirectS3MultipartWriteFeature(session, nodeid), new Path(new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory, Path.Type.volume)), new TransferStatus()); final Path folder = new Path(room, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory)); - new SDSDirectoryFeature(session, nodeid).mkdir(folder, new TransferStatus()); - final Path test = new SDSTouchFeature(session, nodeid).touch(new Path(folder, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)), new TransferStatus()); - final Path copy = new SDSTouchFeature(session, nodeid).touch(new Path(folder, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)), new TransferStatus()); + new SDSDirectoryFeature(session, nodeid).mkdir(new SDSDirectS3MultipartWriteFeature(session, nodeid), folder, new TransferStatus()); + final Path test = new SDSTouchFeature(session, nodeid).touch(new SDSDirectS3MultipartWriteFeature(session, nodeid), new Path(folder, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)), new TransferStatus()); + final Path copy = new SDSTouchFeature(session, nodeid).touch(new SDSDirectS3MultipartWriteFeature(session, nodeid), new Path(folder, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)), new TransferStatus()); final SDSCopyFeature proxy = new SDSCopyFeature(session, nodeid); final SDSDelegatingCopyFeature feature = new SDSDelegatingCopyFeature(session, nodeid, proxy); assertFalse(proxy.isSupported(test, Optional.of(copy))); @@ -155,14 +155,13 @@ public class SDSDelegatingCopyFeatureTest extends AbstractSDSTest { public void testCopyDirectoryServerSide() throws Exception { final SDSNodeIdProvider nodeid = new SDSNodeIdProvider(session); final Path room = new SDSDirectoryFeature(session, nodeid).mkdir( - new Path(new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory, Path.Type.volume)), new TransferStatus()); - final Path directory = new SDSDirectoryFeature(session, nodeid).mkdir(new Path(room, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory)), new TransferStatus()); + new SDSDirectS3MultipartWriteFeature(session, nodeid), new Path(new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory, Path.Type.volume)), new TransferStatus()); + final Path directory = new SDSDirectoryFeature(session, nodeid).mkdir(new SDSDirectS3MultipartWriteFeature(session, nodeid), new Path(room, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory)), new TransferStatus()); final String name = new AlphanumericRandomStringService().random(); - final Path file = new SDSTouchFeature(session, nodeid).touch(new Path(directory, name, EnumSet.of(Path.Type.file)), new TransferStatus()); - final Path target_parent = new SDSDirectoryFeature(session, nodeid).mkdir(new Path(room, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory)), new TransferStatus()); + final Path file = new SDSTouchFeature(session, nodeid).touch(new SDSDirectS3MultipartWriteFeature(session, nodeid), new Path(directory, name, EnumSet.of(Path.Type.file)), new TransferStatus()); + final Path target_parent = new SDSDirectoryFeature(session, nodeid).mkdir(new SDSDirectS3MultipartWriteFeature(session, nodeid), new Path(room, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory)), new TransferStatus()); final Path target = new Path(target_parent, directory.getName(), EnumSet.of(Path.Type.directory)); final SDSDelegatingCopyFeature feature = new SDSDelegatingCopyFeature(session, nodeid, new SDSCopyFeature(session, nodeid)); - assertFalse(feature.isSupported(directory, Optional.of(target_parent))); assertTrue(feature.isSupported(directory, Optional.of(target))); final Path copy = feature.copy(directory, target, new TransferStatus(), new DisabledConnectionCallback(), new DisabledStreamListener()); assertNotNull(copy.attributes().getVersionId()); @@ -175,12 +174,12 @@ public class SDSDelegatingCopyFeatureTest extends AbstractSDSTest { @Test public void testCopyFileToDifferentDataRoom() throws Exception { final SDSNodeIdProvider nodeid = new SDSNodeIdProvider(session); - final Path room1 = new SDSDirectoryFeature(session, nodeid).mkdir(new Path( + final Path room1 = new SDSDirectoryFeature(session, nodeid).mkdir(new SDSDirectS3MultipartWriteFeature(session, nodeid), new Path( new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory, Path.Type.volume)), new TransferStatus()); - final Path room2 = new SDSDirectoryFeature(session, nodeid).mkdir(new Path( + final Path room2 = new SDSDirectoryFeature(session, nodeid).mkdir(new SDSDirectS3MultipartWriteFeature(session, nodeid), new Path( new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory, Path.Type.volume)), new TransferStatus()); - final Path source = new SDSTouchFeature(session, nodeid).touch(new Path(room1, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)), new TransferStatus()); - final Path target = new SDSTouchFeature(session, nodeid).touch(new Path(room2, source.getName(), EnumSet.of(Path.Type.file)), new TransferStatus()); + final Path source = new SDSTouchFeature(session, nodeid).touch(new SDSDirectS3MultipartWriteFeature(session, nodeid), new Path(room1, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)), new TransferStatus()); + final Path target = new SDSTouchFeature(session, nodeid).touch(new SDSDirectS3MultipartWriteFeature(session, nodeid), new Path(room2, source.getName(), EnumSet.of(Path.Type.file)), new TransferStatus()); final SDSDelegatingCopyFeature feature = new SDSDelegatingCopyFeature(session, nodeid, new SDSCopyFeature(session, nodeid)); assertTrue(feature.isSupported(source, Optional.of(target))); assertNotNull(feature.copy(source, target, new TransferStatus(), new DisabledConnectionCallback(), new DisabledStreamListener()).attributes().getVersionId()); @@ -194,7 +193,7 @@ public class SDSDelegatingCopyFeatureTest extends AbstractSDSTest { final SDSNodeIdProvider nodeid = new SDSNodeIdProvider(session); final Path room1 = new SDSDirectoryFeature(session, nodeid).createRoom( new Path(new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory, Path.Type.volume)), true); - final Path room2 = new SDSDirectoryFeature(session, nodeid).mkdir(new Path( + final Path room2 = new SDSDirectoryFeature(session, nodeid).mkdir(new SDSDirectS3MultipartWriteFeature(session, nodeid), new Path( new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory, Path.Type.volume)), new TransferStatus()); final byte[] content = RandomUtils.nextBytes(32769); final TransferStatus status = new TransferStatus(); @@ -249,7 +248,7 @@ public class SDSDelegatingCopyFeatureTest extends AbstractSDSTest { final SDSNodeIdProvider nodeid = new SDSNodeIdProvider(session); final Path room1 = new SDSDirectoryFeature(session, nodeid).createRoom( new Path(new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory, Path.Type.volume)), true); - final Path room2 = new SDSDirectoryFeature(session, nodeid).mkdir(new Path( + final Path room2 = new SDSDirectoryFeature(session, nodeid).mkdir(new SDSDirectS3MultipartWriteFeature(session, nodeid), new Path( new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory, Path.Type.volume)), new TransferStatus()); final byte[] content = RandomUtils.nextBytes(32769); final TransferStatus status = new TransferStatus(); @@ -291,7 +290,7 @@ public class SDSDelegatingCopyFeatureTest extends AbstractSDSTest { final Path room1 = new SDSDirectoryFeature(session, nodeid).createRoom( new Path(new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory, Path.Type.volume)), true); room1.attributes().getAcl().addAll(new Acl.EmailUser(System.getProperties().getProperty("dracoon.user")), SDSPermissionsFeature.DELETE_ROLE); - final Path room2 = new SDSDirectoryFeature(session, nodeid).mkdir(new Path( + final Path room2 = new SDSDirectoryFeature(session, nodeid).mkdir(new SDSDirectS3MultipartWriteFeature(session, nodeid), new Path( new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory, Path.Type.volume)), new TransferStatus()); final EncryptRoomRequest encrypt = new EncryptRoomRequest().isEncrypted(true); new NodesApi(session.getClient()).encryptRoom(encrypt, Long.parseLong(new SDSNodeIdProvider(session).getVersionId(room2)), StringUtils.EMPTY, null); @@ -348,7 +347,7 @@ public class SDSDelegatingCopyFeatureTest extends AbstractSDSTest { final Path room1 = new SDSDirectoryFeature(session, nodeid).createRoom( new Path(new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory, Path.Type.volume)), true); room1.attributes().getAcl().addAll(new Acl.EmailUser(System.getProperties().getProperty("dracoon.user")), SDSPermissionsFeature.DELETE_ROLE); - final Path room2 = new SDSDirectoryFeature(session, nodeid).mkdir(new Path( + final Path room2 = new SDSDirectoryFeature(session, nodeid).mkdir(new SDSDirectS3MultipartWriteFeature(session, nodeid), new Path( new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory, Path.Type.volume)), new TransferStatus()); final EncryptRoomRequest encrypt = new EncryptRoomRequest().isEncrypted(true); new NodesApi(session.getClient()).encryptRoom(encrypt, Long.parseLong(new SDSNodeIdProvider(session).getVersionId(room2)), StringUtils.EMPTY, null); diff --git a/dracoon/src/test/java/ch/cyberduck/core/sds/SDSDelegatingMoveFeatureTest.java b/dracoon/src/test/java/ch/cyberduck/core/sds/SDSDelegatingMoveFeatureTest.java index 5989ae10bb..f9492ea5d7 100644 --- a/dracoon/src/test/java/ch/cyberduck/core/sds/SDSDelegatingMoveFeatureTest.java +++ b/dracoon/src/test/java/ch/cyberduck/core/sds/SDSDelegatingMoveFeatureTest.java @@ -64,9 +64,9 @@ public class SDSDelegatingMoveFeatureTest extends AbstractSDSTest { @Test public void testMove() throws Exception { final SDSNodeIdProvider nodeid = new SDSNodeIdProvider(session); - final Path room = new SDSDirectoryFeature(session, nodeid).mkdir(new Path( + final Path room = new SDSDirectoryFeature(session, nodeid).mkdir(new SDSDirectS3MultipartWriteFeature(session, nodeid), new Path( new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory, Path.Type.volume)), new TransferStatus()); - final Path test = new SDSTouchFeature(session, nodeid).touch(new Path(room, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)), new TransferStatus()); + final Path test = new SDSTouchFeature(session, nodeid).touch(new SDSDirectS3MultipartWriteFeature(session, nodeid), new Path(room, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)), new TransferStatus()); final String versionId = test.attributes().getVersionId(); final Path target = new SDSDelegatingMoveFeature(session, nodeid, new SDSMoveFeature(session, nodeid)).move(test, new Path(room, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)), new TransferStatus(), new Delete.DisabledCallback(), new DisabledConnectionCallback()); @@ -83,9 +83,9 @@ public class SDSDelegatingMoveFeatureTest extends AbstractSDSTest { @Test public void testRenameCaseChangeOnly() throws Exception { final SDSNodeIdProvider nodeid = new SDSNodeIdProvider(session); - final Path room = new SDSDirectoryFeature(session, nodeid).mkdir(new Path( + final Path room = new SDSDirectoryFeature(session, nodeid).mkdir(new SDSDirectS3MultipartWriteFeature(session, nodeid), new Path( new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory, Path.Type.volume)), new TransferStatus()); - final Path test = new SDSTouchFeature(session, nodeid).touch(new Path(room, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)), new TransferStatus()); + final Path test = new SDSTouchFeature(session, nodeid).touch(new SDSDirectS3MultipartWriteFeature(session, nodeid), new Path(room, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)), new TransferStatus()); final String versionId = test.attributes().getVersionId(); final Path target = new Path(room, test.getName().toUpperCase(), EnumSet.of(Path.Type.file)); final Path result = new SDSDelegatingMoveFeature(session, nodeid, new SDSMoveFeature(session, nodeid)).move(test, target, @@ -98,12 +98,12 @@ public class SDSDelegatingMoveFeatureTest extends AbstractSDSTest { @Test public void testMoveDifferentDataRoom() throws Exception { final SDSNodeIdProvider nodeid = new SDSNodeIdProvider(session); - final Path room1 = new SDSDirectoryFeature(session, nodeid).mkdir(new Path( + final Path room1 = new SDSDirectoryFeature(session, nodeid).mkdir(new SDSDirectS3MultipartWriteFeature(session, nodeid), new Path( new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory, Path.Type.volume)), new TransferStatus()); - final Path room2 = new SDSDirectoryFeature(session, nodeid).mkdir(new Path( + final Path room2 = new SDSDirectoryFeature(session, nodeid).mkdir(new SDSDirectS3MultipartWriteFeature(session, nodeid), new Path( new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory, Path.Type.volume)), new TransferStatus()); final Path test = new Path(room1, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)); - new SDSTouchFeature(session, nodeid).touch(test, new TransferStatus()); + new SDSTouchFeature(session, nodeid).touch(new SDSDirectS3MultipartWriteFeature(session, nodeid), test, new TransferStatus()); final Path target = new Path(room2, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)); new SDSDelegatingMoveFeature(session, nodeid, new SDSMoveFeature(session, nodeid)).move(test, target, new TransferStatus(), new Delete.DisabledCallback(), new DisabledConnectionCallback()); test.attributes().setVersionId(null); @@ -116,14 +116,14 @@ public class SDSDelegatingMoveFeatureTest extends AbstractSDSTest { @Test public void testMoveWithRenameDifferentDataRoomSameFilename() throws Exception { final SDSNodeIdProvider nodeid = new SDSNodeIdProvider(session); - final Path room1 = new SDSDirectoryFeature(session, nodeid).mkdir(new Path( + final Path room1 = new SDSDirectoryFeature(session, nodeid).mkdir(new SDSDirectS3MultipartWriteFeature(session, nodeid), new Path( new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory, Path.Type.volume)), new TransferStatus()); - final Path room2 = new SDSDirectoryFeature(session, nodeid).mkdir(new Path( + final Path room2 = new SDSDirectoryFeature(session, nodeid).mkdir(new SDSDirectS3MultipartWriteFeature(session, nodeid), new Path( new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory, Path.Type.volume)), new TransferStatus()); final Path test1 = new Path(room1, "A", EnumSet.of(Path.Type.file)); - new SDSTouchFeature(session, nodeid).touch(test1, new TransferStatus()); + new SDSTouchFeature(session, nodeid).touch(new SDSDirectS3MultipartWriteFeature(session, nodeid), test1, new TransferStatus()); final Path test2 = new Path(room2, "A", EnumSet.of(Path.Type.file)); - new SDSTouchFeature(session, nodeid).touch(test2, new TransferStatus()); + new SDSTouchFeature(session, nodeid).touch(new SDSDirectS3MultipartWriteFeature(session, nodeid), test2, new TransferStatus()); final Path target = new Path(room2, "A (2)", EnumSet.of(Path.Type.file)); new SDSDelegatingMoveFeature(session, nodeid, new SDSMoveFeature(session, nodeid)).move(test1, target, new TransferStatus(), new Delete.DisabledCallback(), new DisabledConnectionCallback()); test1.attributes().setVersionId(null); @@ -140,7 +140,7 @@ public class SDSDelegatingMoveFeatureTest extends AbstractSDSTest { final Path room1 = new SDSDirectoryFeature(session, nodeid).createRoom( new Path(new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory, Path.Type.volume)), true); room1.setAttributes(new SDSAttributesFinderFeature(session, nodeid).find(room1)); - final Path room2 = new SDSDirectoryFeature(session, nodeid).mkdir(new Path( + final Path room2 = new SDSDirectoryFeature(session, nodeid).mkdir(new SDSDirectS3MultipartWriteFeature(session, nodeid), new Path( new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory, Path.Type.volume)), new TransferStatus()); room2.setAttributes(new SDSAttributesFinderFeature(session, nodeid).find(room2)); final byte[] content = RandomUtils.nextBytes(32769); @@ -191,7 +191,7 @@ public class SDSDelegatingMoveFeatureTest extends AbstractSDSTest { final Path room1 = new SDSDirectoryFeature(session, nodeid).createRoom( new Path(new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory, Path.Type.volume)), true); room1.setAttributes(new SDSAttributesFinderFeature(session, nodeid).find(room1)); - final Path room2 = new SDSDirectoryFeature(session, nodeid).mkdir(new Path( + final Path room2 = new SDSDirectoryFeature(session, nodeid).mkdir(new SDSDirectS3MultipartWriteFeature(session, nodeid), new Path( new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory, Path.Type.volume)), new TransferStatus()); room2.setAttributes(new SDSAttributesFinderFeature(session, nodeid).find(room2)); final byte[] content = RandomUtils.nextBytes(32769); @@ -229,7 +229,7 @@ public class SDSDelegatingMoveFeatureTest extends AbstractSDSTest { final Path room1 = new SDSDirectoryFeature(session, nodeid).createRoom( new Path(new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory, Path.Type.volume)), true); room1.setAttributes(new SDSAttributesFinderFeature(session, nodeid).find(room1)); - final Path room2 = new SDSDirectoryFeature(session, nodeid).mkdir(new Path( + final Path room2 = new SDSDirectoryFeature(session, nodeid).mkdir(new SDSDirectS3MultipartWriteFeature(session, nodeid), new Path( new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory, Path.Type.volume)), new TransferStatus()); final EncryptRoomRequest encrypt = new EncryptRoomRequest().isEncrypted(true); new NodesApi(session.getClient()).encryptRoom(encrypt, Long.parseLong(new SDSNodeIdProvider(session).getVersionId(room2)), StringUtils.EMPTY, null); @@ -280,14 +280,14 @@ public class SDSDelegatingMoveFeatureTest extends AbstractSDSTest { public void testMoveEncryptedDataRoom() throws Exception { final SDSNodeIdProvider nodeid = new SDSNodeIdProvider(session); final String roomName = new AlphanumericRandomStringService().random(); - final Path room = new SDSDirectoryFeature(session, nodeid).mkdir(new Path(roomName, EnumSet.of(Path.Type.directory, Path.Type.volume)), new TransferStatus()); + final Path room = new SDSDirectoryFeature(session, nodeid).mkdir(new SDSDirectS3MultipartWriteFeature(session, nodeid), new Path(roomName, EnumSet.of(Path.Type.directory, Path.Type.volume)), new TransferStatus()); final EncryptRoomRequest encrypt = new EncryptRoomRequest().isEncrypted(true); new NodesApi(session.getClient()).encryptRoom(encrypt, Long.parseLong(new SDSNodeIdProvider(session).getVersionId(room)), StringUtils.EMPTY, null); final AttributedList list = new SDSListService(session, nodeid).list(room.getParent(), new DisabledListProgressListener()); final Path encrypted = list.get(room); // create file inside encrypted room final Path folder = new SDSDirectoryFeature(session, nodeid).mkdir( - new Path(encrypted, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory)), new TransferStatus()); + new SDSDirectS3MultipartWriteFeature(session, nodeid), new Path(encrypted, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory)), new TransferStatus()); final Path renamed = new Path(new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory, Path.Type.volume)); new SDSDelegatingMoveFeature(session, nodeid, new SDSMoveFeature(session, nodeid)).move(encrypted, renamed, new TransferStatus(), new Delete.DisabledCallback(), new DisabledConnectionCallback()); assertEquals(0, session.getMetrics().get(Copy.class)); diff --git a/dracoon/src/test/java/ch/cyberduck/core/sds/SDSDeleteFeatureTest.java b/dracoon/src/test/java/ch/cyberduck/core/sds/SDSDeleteFeatureTest.java index 0d4e895625..2f1448b23e 100644 --- a/dracoon/src/test/java/ch/cyberduck/core/sds/SDSDeleteFeatureTest.java +++ b/dracoon/src/test/java/ch/cyberduck/core/sds/SDSDeleteFeatureTest.java @@ -17,6 +17,7 @@ package ch.cyberduck.core.sds; import ch.cyberduck.core.AbstractPath; import ch.cyberduck.core.AlphanumericRandomStringService; +import ch.cyberduck.core.DefaultPathAttributes; import ch.cyberduck.core.DisabledLoginCallback; import ch.cyberduck.core.DisabledProgressListener; import ch.cyberduck.core.Local; @@ -25,6 +26,7 @@ import ch.cyberduck.core.PathAttributes; import ch.cyberduck.core.exception.AntiVirusAccessDeniedException; import ch.cyberduck.core.exception.NotfoundException; import ch.cyberduck.core.features.Delete; +import ch.cyberduck.core.features.Home; import ch.cyberduck.core.io.BandwidthThrottle; import ch.cyberduck.core.io.DisabledStreamListener; import ch.cyberduck.core.sds.io.swagger.client.api.NodesApi; @@ -53,14 +55,14 @@ public class SDSDeleteFeatureTest extends AbstractSDSTest { @Test public void testDeleteNotFound() throws Exception { final SDSNodeIdProvider nodeid = new SDSNodeIdProvider(session); - final Path room = new SDSDirectoryFeature(session, nodeid).mkdir(new Path( + final Path room = new SDSDirectoryFeature(session, nodeid).mkdir(new SDSDirectS3MultipartWriteFeature(session, nodeid), new Path( new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory, Path.Type.volume)), new TransferStatus()); - final Path file = new SDSTouchFeature(session, nodeid).touch(new Path(room, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)), new TransferStatus()); + final Path file = new SDSTouchFeature(session, nodeid).touch(new SDSDirectS3MultipartWriteFeature(session, nodeid), new Path(room, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)), new TransferStatus()); final String versionId = file.attributes().getVersionId(); assertNotNull(versionId); new SDSDeleteFeature(session, nodeid).delete(Collections.singletonList(file), new DisabledLoginCallback(), new Delete.DisabledCallback()); try { - new SDSDeleteFeature(session, nodeid).delete(Collections.singletonList(file.withAttributes(new PathAttributes().setVersionId(versionId))), new DisabledLoginCallback(), new Delete.DisabledCallback()); + new SDSDeleteFeature(session, nodeid).delete(Collections.singletonList(file.withAttributes(new DefaultPathAttributes().setVersionId(versionId))), new DisabledLoginCallback(), new Delete.DisabledCallback()); fail(); } catch(NotfoundException e) { @@ -72,10 +74,10 @@ public class SDSDeleteFeatureTest extends AbstractSDSTest { @Test public void testDeleteFile() throws Exception { final SDSNodeIdProvider nodeid = new SDSNodeIdProvider(session); - final Path room = new SDSDirectoryFeature(session, nodeid).mkdir(new Path( + final Path room = new SDSDirectoryFeature(session, nodeid).mkdir(new SDSDirectS3MultipartWriteFeature(session, nodeid), new Path( new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory, Path.Type.volume)), new TransferStatus()); final Path fileInRoom = new Path(room, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)); - new SDSTouchFeature(session, nodeid).touch(fileInRoom, new TransferStatus()); + new SDSTouchFeature(session, nodeid).touch(new SDSDirectS3MultipartWriteFeature(session, nodeid), fileInRoom, new TransferStatus()); assertTrue(new DefaultFindFeature(session).find(fileInRoom)); new SDSDeleteFeature(session, nodeid).delete(Collections.singletonList(fileInRoom), new DisabledLoginCallback(), new Delete.DisabledCallback()); assertFalse(new DefaultFindFeature(session).find(fileInRoom)); @@ -86,11 +88,11 @@ public class SDSDeleteFeatureTest extends AbstractSDSTest { public void testDeleteRecursively() throws Exception { final SDSNodeIdProvider nodeid = new SDSNodeIdProvider(session); final Path room = new SDSDirectoryFeature(session, nodeid).mkdir( - new Path(new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory, Path.Type.volume)), new TransferStatus()); + new SDSDirectS3MultipartWriteFeature(session, nodeid), new Path(new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory, Path.Type.volume)), new TransferStatus()); final Path folder = new SDSDirectoryFeature(session, nodeid).mkdir( - new Path(room, new AlphanumericRandomStringService().random(), EnumSet.of(AbstractPath.Type.directory)), new TransferStatus()); + new SDSDirectS3MultipartWriteFeature(session, nodeid), new Path(room, new AlphanumericRandomStringService().random(), EnumSet.of(AbstractPath.Type.directory)), new TransferStatus()); final Path file = new SDSTouchFeature(session, nodeid).touch( - new Path(folder, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)), new TransferStatus()); + new SDSDirectS3MultipartWriteFeature(session, nodeid), new Path(folder, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)), new TransferStatus()); assertTrue(new SDSFindFeature(session, nodeid).find(file)); assertNotNull(nodeid.getVersionId(file)); new SDSDeleteFeature(session, nodeid).delete(Collections.singletonList(room), new DisabledLoginCallback(), new Delete.DisabledCallback()); @@ -115,13 +117,13 @@ public class SDSDeleteFeatureTest extends AbstractSDSTest { @Test public void testDeleteFolderRoomWithContent() throws Exception { final SDSNodeIdProvider nodeid = new SDSNodeIdProvider(session); - final Path room = new SDSDirectoryFeature(session, nodeid).mkdir(new Path( + final Path room = new SDSDirectoryFeature(session, nodeid).mkdir(new SDSDirectS3MultipartWriteFeature(session, nodeid), new Path( new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory, Path.Type.volume)), new TransferStatus()); - final Path folder = new SDSDirectoryFeature(session, nodeid).mkdir(new Path(room, + final Path folder = new SDSDirectoryFeature(session, nodeid).mkdir(new SDSDirectS3MultipartWriteFeature(session, nodeid), new Path(room, new AlphanumericRandomStringService().random().toLowerCase(), EnumSet.of(Path.Type.directory)), new TransferStatus()); assertTrue(new DefaultFindFeature(session).find(folder)); final Path file = new Path(folder, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)); - new SDSTouchFeature(session, nodeid).touch(file, new TransferStatus()); + new SDSTouchFeature(session, nodeid).touch(new SDSDirectS3MultipartWriteFeature(session, nodeid), file, new TransferStatus()); assertTrue(new DefaultFindFeature(session).find(file)); new SDSDeleteFeature(session, nodeid).delete(Collections.singletonList(folder), new DisabledLoginCallback(), new Delete.DisabledCallback()); assertFalse(new DefaultFindFeature(session).find(folder)); @@ -132,14 +134,14 @@ public class SDSDeleteFeatureTest extends AbstractSDSTest { @Test public void testIsRecursive() { final SDSNodeIdProvider nodeid = new SDSNodeIdProvider(session); - assertTrue(new SDSDeleteFeature(session, nodeid).isRecursive()); + assertTrue(new SDSDeleteFeature(session, nodeid).features(Home.root()).contains(Delete.Flags.recursive)); } @Test @Ignore public void testDeleteEicar() throws Exception { final SDSNodeIdProvider nodeid = new SDSNodeIdProvider(session); - final Path room = new SDSDirectoryFeature(session, nodeid).mkdir(new Path( + final Path room = new SDSDirectoryFeature(session, nodeid).mkdir(new SDSDirectS3MultipartWriteFeature(session, nodeid), new Path( new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory, Path.Type.volume)), new TransferStatus()); new NodesApi(session.getClient()).setRoomPolicies(new RoomPoliciesRequest().virusProtectionEnabled(true), Long.valueOf(nodeid.getVersionId(room)), StringUtils.EMPTY); @@ -151,8 +153,8 @@ public class SDSDeleteFeatureTest extends AbstractSDSTest { out.close(); final TransferStatus status = new TransferStatus(); status.setLength(eicar.length); - final SDSDirectS3UploadFeature feature = new SDSDirectS3UploadFeature(session, nodeid, new SDSDelegatingWriteFeature(session, nodeid, new SDSDirectS3WriteFeature(session, nodeid))); - final Node node = feature.upload(test, local, new BandwidthThrottle(BandwidthThrottle.UNLIMITED), + final SDSDirectS3UploadFeature feature = new SDSDirectS3UploadFeature(session, nodeid); + final Node node = feature.upload(new SDSDelegatingWriteFeature(session, nodeid, new SDSDirectS3WriteFeature(session, nodeid)), test, local, new BandwidthThrottle(BandwidthThrottle.UNLIMITED), new DisabledProgressListener(), new DisabledStreamListener(), status, new DisabledLoginCallback()); assertTrue(new SDSFindFeature(session, nodeid).find(test)); final PathAttributes attributes = new SDSAttributesFinderFeature(session, nodeid).find(test); diff --git a/dracoon/src/test/java/ch/cyberduck/core/sds/SDSDirectS3MultipartWriteFeatureTest.java b/dracoon/src/test/java/ch/cyberduck/core/sds/SDSDirectS3MultipartWriteFeatureTest.java index 3cf3dee498..895d7e2adb 100644 --- a/dracoon/src/test/java/ch/cyberduck/core/sds/SDSDirectS3MultipartWriteFeatureTest.java +++ b/dracoon/src/test/java/ch/cyberduck/core/sds/SDSDirectS3MultipartWriteFeatureTest.java @@ -59,7 +59,7 @@ public class SDSDirectS3MultipartWriteFeatureTest extends AbstractSDSTest { public void testWriteZeroLength() throws Exception { final SDSNodeIdProvider nodeid = new SDSNodeIdProvider(session); final Path room = new SDSDirectoryFeature(session, nodeid).mkdir( - new Path(new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory, Path.Type.volume)), new TransferStatus()); + new SDSDirectS3MultipartWriteFeature(session, nodeid), new Path(new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory, Path.Type.volume)), new TransferStatus()); final TransferStatus status = new TransferStatus(); final Path test = new Path(room, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)); final SDSDirectS3MultipartWriteFeature writer = new SDSDirectS3MultipartWriteFeature(session, nodeid); diff --git a/dracoon/src/test/java/ch/cyberduck/core/sds/SDSDirectS3UploadFeatureTest.java b/dracoon/src/test/java/ch/cyberduck/core/sds/SDSDirectS3UploadFeatureTest.java index e32e76dcd0..0a2b1a7029 100644 --- a/dracoon/src/test/java/ch/cyberduck/core/sds/SDSDirectS3UploadFeatureTest.java +++ b/dracoon/src/test/java/ch/cyberduck/core/sds/SDSDirectS3UploadFeatureTest.java @@ -30,12 +30,14 @@ import ch.cyberduck.core.exception.BackgroundException; import ch.cyberduck.core.exception.ConnectionCanceledException; import ch.cyberduck.core.exception.NotfoundException; import ch.cyberduck.core.features.Delete; +import ch.cyberduck.core.features.Write; import ch.cyberduck.core.http.HttpResponseOutputStream; import ch.cyberduck.core.io.BandwidthThrottle; import ch.cyberduck.core.io.DisabledStreamListener; import ch.cyberduck.core.local.DefaultLocalTouchFeature; import ch.cyberduck.core.sds.io.swagger.client.model.Node; import ch.cyberduck.core.sds.triplecrypt.TripleCryptReadFeature; +import ch.cyberduck.core.sds.triplecrypt.TripleCryptWriteFeature; import ch.cyberduck.core.transfer.Transfer; import ch.cyberduck.core.transfer.TransferItem; import ch.cyberduck.core.transfer.TransferStatus; @@ -61,17 +63,9 @@ public class SDSDirectS3UploadFeatureTest extends AbstractSDSTest { @Test public void testUploadInterrupt() throws Exception { final SDSNodeIdProvider nodeid = new SDSNodeIdProvider(session); - final SDSDirectS3UploadFeature feature = new SDSDirectS3UploadFeature(session, nodeid, new SDSDelegatingWriteFeature(session, nodeid, new SDSDirectS3WriteFeature(session, nodeid) { - @Override - public HttpResponseOutputStream write(final Path file, final TransferStatus status, final ConnectionCallback callback) throws BackgroundException { - if(status.getPart() == 2) { - throw new ConnectionCanceledException(); - } - return super.write(file, status, callback); - } - }), 5L * 1024 * 1024, 5); + final SDSDirectS3UploadFeature feature = new SDSDirectS3UploadFeature(session, nodeid, 5L * 1024 * 1024, 5); final Path room = new SDSDirectoryFeature(session, nodeid).mkdir( - new Path(new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory, Path.Type.volume)), new TransferStatus()); + session.getFeature(Write.class), new Path(new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory, Path.Type.volume)), new TransferStatus()); final Path test = new Path(room, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)); final Local local = new Local(System.getProperty("java.io.tmpdir"), UUID.randomUUID().toString()); final byte[] random = RandomUtils.nextBytes(6 * 1024 * 1024); @@ -81,7 +75,15 @@ public class SDSDirectS3UploadFeatureTest extends AbstractSDSTest { final TransferStatus status = new TransferStatus(); status.setLength(random.length); try { - feature.upload(test, local, new BandwidthThrottle(BandwidthThrottle.UNLIMITED), + feature.upload(new SDSDirectS3WriteFeature(session, nodeid) { + @Override + public HttpResponseOutputStream write(final Path file, final TransferStatus status, final ConnectionCallback callback) throws BackgroundException { + if(status.getPart() == 2) { + throw new ConnectionCanceledException(); + } + return super.write(file, status, callback); + } + }, test, local, new BandwidthThrottle(BandwidthThrottle.UNLIMITED), new DisabledProgressListener(), new DisabledStreamListener(), status, new DisabledLoginCallback()); fail(); } @@ -98,9 +100,9 @@ public class SDSDirectS3UploadFeatureTest extends AbstractSDSTest { @Test public void testUploadZeroByteFile() throws Exception { final SDSNodeIdProvider nodeid = new SDSNodeIdProvider(session); - final SDSDirectS3UploadFeature feature = new SDSDirectS3UploadFeature(session, nodeid, new SDSDelegatingWriteFeature(session, nodeid, new SDSDirectS3WriteFeature(session, nodeid))); + final SDSDirectS3UploadFeature feature = new SDSDirectS3UploadFeature(session, nodeid); final Path room = new SDSDirectoryFeature(session, nodeid).mkdir( - new Path(new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory, Path.Type.volume)), new TransferStatus()); + session.getFeature(Write.class), new Path(new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory, Path.Type.volume)), new TransferStatus()); final Path test = new Path(room, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)); final Local local = new Local(System.getProperty("java.io.tmpdir"), UUID.randomUUID().toString()); final byte[] random = RandomUtils.nextBytes(0); @@ -109,7 +111,7 @@ public class SDSDirectS3UploadFeatureTest extends AbstractSDSTest { out.close(); final TransferStatus status = new TransferStatus(); status.setLength(random.length); - feature.upload(test, local, new BandwidthThrottle(BandwidthThrottle.UNLIMITED), + feature.upload(new SDSDirectS3WriteFeature(session, nodeid), test, local, new BandwidthThrottle(BandwidthThrottle.UNLIMITED), new DisabledProgressListener(), new DisabledStreamListener(), status, new DisabledLoginCallback()); assertTrue(new SDSFindFeature(session, nodeid).find(test)); final PathAttributes attributes = new SDSAttributesFinderFeature(session, nodeid).find(test); @@ -121,15 +123,15 @@ public class SDSDirectS3UploadFeatureTest extends AbstractSDSTest { @Test public void testUploadMissingTargetDirectory() throws Exception { final SDSNodeIdProvider nodeid = new SDSNodeIdProvider(session); - final SDSDirectS3UploadFeature feature = new SDSDirectS3UploadFeature(session, nodeid, new SDSDelegatingWriteFeature(session, nodeid, new SDSDirectS3WriteFeature(session, nodeid))); + final SDSDirectS3UploadFeature feature = new SDSDirectS3UploadFeature(session, nodeid); final Path room = new SDSDirectoryFeature(session, nodeid).mkdir( - new Path(new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory, Path.Type.volume)), new TransferStatus()); + session.getFeature(Write.class), new Path(new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory, Path.Type.volume)), new TransferStatus()); final Path directory = new Path(room, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)); final Path test = new Path(directory, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)); final Local local = new Local(System.getProperty("java.io.tmpdir"), UUID.randomUUID().toString()); new DefaultLocalTouchFeature().touch(local); final TransferStatus status = new TransferStatus(); - assertThrows(NotfoundException.class, () -> feature.upload(test, local, new BandwidthThrottle(BandwidthThrottle.UNLIMITED), + assertThrows(NotfoundException.class, () -> feature.upload(new SDSDirectS3WriteFeature(session, nodeid), test, local, new BandwidthThrottle(BandwidthThrottle.UNLIMITED), new DisabledProgressListener(), new DisabledStreamListener(), status, new DisabledLoginCallback())); local.delete(); } @@ -137,9 +139,9 @@ public class SDSDirectS3UploadFeatureTest extends AbstractSDSTest { @Test public void testUploadBelowMultipartSize() throws Exception { final SDSNodeIdProvider nodeid = new SDSNodeIdProvider(session); - final SDSDirectS3UploadFeature feature = new SDSDirectS3UploadFeature(session, nodeid, new SDSDelegatingWriteFeature(session, nodeid, new SDSDirectS3WriteFeature(session, nodeid))); + final SDSDirectS3UploadFeature feature = new SDSDirectS3UploadFeature(session, nodeid); final Path room = new SDSDirectoryFeature(session, nodeid).mkdir( - new Path(new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory, Path.Type.volume)), new TransferStatus()); + session.getFeature(Write.class), new Path(new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory, Path.Type.volume)), new TransferStatus()); final Path test = new Path(room, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)); final Local local = new Local(System.getProperty("java.io.tmpdir"), UUID.randomUUID().toString()); final byte[] random = RandomUtils.nextBytes(578); @@ -148,7 +150,7 @@ public class SDSDirectS3UploadFeatureTest extends AbstractSDSTest { out.close(); final TransferStatus status = new TransferStatus(); status.setLength(random.length); - final Node node = feature.upload(test, local, new BandwidthThrottle(BandwidthThrottle.UNLIMITED), + final Node node = feature.upload(new SDSDirectS3WriteFeature(session, nodeid), test, local, new BandwidthThrottle(BandwidthThrottle.UNLIMITED), new DisabledProgressListener(), new DisabledStreamListener(), status, new DisabledLoginCallback()); assertTrue(new SDSFindFeature(session, nodeid).find(test)); final PathAttributes attributes = new SDSAttributesFinderFeature(session, nodeid).find(test); @@ -161,9 +163,9 @@ public class SDSDirectS3UploadFeatureTest extends AbstractSDSTest { @Test public void testUploadExactMultipartSize() throws Exception { final SDSNodeIdProvider nodeid = new SDSNodeIdProvider(session); - final SDSDirectS3UploadFeature feature = new SDSDirectS3UploadFeature(session, nodeid, new SDSDelegatingWriteFeature(session, nodeid, new SDSDirectS3WriteFeature(session, nodeid))); + final SDSDirectS3UploadFeature feature = new SDSDirectS3UploadFeature(session, nodeid); final Path room = new SDSDirectoryFeature(session, nodeid).mkdir( - new Path(new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory, Path.Type.volume)), new TransferStatus()); + session.getFeature(Write.class), new Path(new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory, Path.Type.volume)), new TransferStatus()); final Path test = new Path(room, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)); final Local local = new Local(System.getProperty("java.io.tmpdir"), UUID.randomUUID().toString()); final byte[] random = RandomUtils.nextBytes(10 * 1024 * 1024); @@ -172,7 +174,7 @@ public class SDSDirectS3UploadFeatureTest extends AbstractSDSTest { out.close(); final TransferStatus status = new TransferStatus(); status.setLength(random.length); - final Node node = feature.upload(test, local, new BandwidthThrottle(BandwidthThrottle.UNLIMITED), + final Node node = feature.upload(new SDSDirectS3WriteFeature(session, nodeid), test, local, new BandwidthThrottle(BandwidthThrottle.UNLIMITED), new DisabledProgressListener(), new DisabledStreamListener(), status, new DisabledLoginCallback()); assertTrue(status.isComplete()); assertNotSame(PathAttributes.EMPTY, status.getResponse()); @@ -187,9 +189,9 @@ public class SDSDirectS3UploadFeatureTest extends AbstractSDSTest { @Test public void testUploadMultipleParts() throws Exception { final SDSNodeIdProvider nodeid = new SDSNodeIdProvider(session); - final SDSDirectS3UploadFeature feature = new SDSDirectS3UploadFeature(session, nodeid, new SDSDelegatingWriteFeature(session, nodeid, new SDSDirectS3WriteFeature(session, nodeid))); + final SDSDirectS3UploadFeature feature = new SDSDirectS3UploadFeature(session, nodeid); final Path room = new SDSDirectoryFeature(session, nodeid).mkdir( - new Path(new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory, Path.Type.volume)), new TransferStatus()); + session.getFeature(Write.class), new Path(new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory, Path.Type.volume)), new TransferStatus()); final Path test = new Path(room, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)); final Local local = new Local(System.getProperty("java.io.tmpdir"), UUID.randomUUID().toString()); final byte[] random = RandomUtils.nextBytes(21 * 1024 * 1024); @@ -198,7 +200,7 @@ public class SDSDirectS3UploadFeatureTest extends AbstractSDSTest { out.close(); final TransferStatus status = new TransferStatus(); status.setLength(random.length); - final Node node = feature.upload(test, local, new BandwidthThrottle(BandwidthThrottle.UNLIMITED), + final Node node = feature.upload(new SDSDirectS3WriteFeature(session, nodeid), test, local, new BandwidthThrottle(BandwidthThrottle.UNLIMITED), new DisabledProgressListener(), new DisabledStreamListener(), status, new DisabledLoginCallback()); assertTrue(status.isComplete()); assertNotSame(PathAttributes.EMPTY, status.getResponse()); @@ -213,7 +215,7 @@ public class SDSDirectS3UploadFeatureTest extends AbstractSDSTest { @Test public void testTripleCryptUploadBelowMultipartSize() throws Exception { final SDSNodeIdProvider nodeid = new SDSNodeIdProvider(session); - final SDSDirectS3UploadFeature feature = new SDSDirectS3UploadFeature(session, nodeid, new SDSDirectS3WriteFeature(session, nodeid)); + final SDSDirectS3UploadFeature feature = new SDSDirectS3UploadFeature(session, nodeid); final Path room = new SDSDirectoryFeature(session, nodeid).createRoom( new Path(new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory, Path.Type.volume)), true); final Path test = new Path(room, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)); @@ -227,7 +229,7 @@ public class SDSDirectS3UploadFeatureTest extends AbstractSDSTest { status.setLength(random.length); final SDSEncryptionBulkFeature bulk = new SDSEncryptionBulkFeature(session, nodeid); bulk.pre(Transfer.Type.upload, Collections.singletonMap(new TransferItem(test, local), status), new DisabledConnectionCallback()); - final Node node = feature.upload(test, local, new BandwidthThrottle(BandwidthThrottle.UNLIMITED), + final Node node = feature.upload(new TripleCryptWriteFeature(session, nodeid, new SDSDirectS3WriteFeature(session, nodeid)), test, local, new BandwidthThrottle(BandwidthThrottle.UNLIMITED), new DisabledProgressListener(), new DisabledStreamListener(), status, new DisabledLoginCallback()); assertTrue(status.isComplete()); assertNotSame(PathAttributes.EMPTY, status.getResponse()); @@ -252,7 +254,7 @@ public class SDSDirectS3UploadFeatureTest extends AbstractSDSTest { @Test public void testTripleCryptUploadExactMultipartSize() throws Exception { final SDSNodeIdProvider nodeid = new SDSNodeIdProvider(session); - final SDSDirectS3UploadFeature feature = new SDSDirectS3UploadFeature(session, nodeid, new SDSDirectS3WriteFeature(session, nodeid)); + final SDSDirectS3UploadFeature feature = new SDSDirectS3UploadFeature(session, nodeid); final Path room = new SDSDirectoryFeature(session, nodeid).createRoom( new Path(new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory, Path.Type.volume)), true); final Path test = new Path(room, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)); @@ -266,7 +268,7 @@ public class SDSDirectS3UploadFeatureTest extends AbstractSDSTest { status.setLength(random.length); final SDSEncryptionBulkFeature bulk = new SDSEncryptionBulkFeature(session, nodeid); bulk.pre(Transfer.Type.upload, Collections.singletonMap(new TransferItem(test, local), status), new DisabledConnectionCallback()); - final Node node = feature.upload(test, local, new BandwidthThrottle(BandwidthThrottle.UNLIMITED), + final Node node = feature.upload(new TripleCryptWriteFeature(session, nodeid, new SDSDirectS3WriteFeature(session, nodeid)), test, local, new BandwidthThrottle(BandwidthThrottle.UNLIMITED), new DisabledProgressListener(), new DisabledStreamListener(), status, new DisabledLoginCallback()); assertTrue(status.isComplete()); assertNotSame(PathAttributes.EMPTY, status.getResponse()); @@ -291,7 +293,7 @@ public class SDSDirectS3UploadFeatureTest extends AbstractSDSTest { @Test public void testTripleCryptUploadMultipleParts() throws Exception { final SDSNodeIdProvider nodeid = new SDSNodeIdProvider(session); - final SDSDirectS3UploadFeature feature = new SDSDirectS3UploadFeature(session, nodeid, new SDSDirectS3WriteFeature(session, nodeid)); + final SDSDirectS3UploadFeature feature = new SDSDirectS3UploadFeature(session, nodeid); final Path room = new SDSDirectoryFeature(session, nodeid).createRoom( new Path(new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory, Path.Type.volume)), true); final Path test = new Path(room, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)); @@ -305,7 +307,7 @@ public class SDSDirectS3UploadFeatureTest extends AbstractSDSTest { status.setLength(random.length); final SDSEncryptionBulkFeature bulk = new SDSEncryptionBulkFeature(session, nodeid); bulk.pre(Transfer.Type.upload, Collections.singletonMap(new TransferItem(test, local), status), new DisabledConnectionCallback()); - final Node node = feature.upload(test, local, new BandwidthThrottle(BandwidthThrottle.UNLIMITED), + final Node node = feature.upload(new TripleCryptWriteFeature(session, nodeid, new SDSDirectS3WriteFeature(session, nodeid)), test, local, new BandwidthThrottle(BandwidthThrottle.UNLIMITED), new DisabledProgressListener(), new DisabledStreamListener(), status, new DisabledLoginCallback()); assertTrue(status.isComplete()); assertNotSame(PathAttributes.EMPTY, status.getResponse()); diff --git a/dracoon/src/test/java/ch/cyberduck/core/sds/SDSDirectoryFeatureTest.java b/dracoon/src/test/java/ch/cyberduck/core/sds/SDSDirectoryFeatureTest.java index 7ebe32120d..07d315551f 100644 --- a/dracoon/src/test/java/ch/cyberduck/core/sds/SDSDirectoryFeatureTest.java +++ b/dracoon/src/test/java/ch/cyberduck/core/sds/SDSDirectoryFeatureTest.java @@ -44,10 +44,10 @@ public class SDSDirectoryFeatureTest extends AbstractSDSTest { public void testCreateDirectory() throws Exception { final SDSNodeIdProvider nodeid = new SDSNodeIdProvider(session); final Path room = new SDSDirectoryFeature(session, nodeid).mkdir( - new Path(new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory, Path.Type.volume)), new TransferStatus()); - assertThrows(ConflictException.class, () -> new SDSDirectoryFeature(session, nodeid).mkdir(room, new TransferStatus())); - final Path test = new SDSDirectoryFeature(session, nodeid).mkdir(new Path(room, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory)), new TransferStatus()); - assertThrows(ConflictException.class, () -> new SDSDirectoryFeature(session, nodeid).mkdir(test, new TransferStatus())); + new SDSDirectS3MultipartWriteFeature(session, nodeid), new Path(new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory, Path.Type.volume)), new TransferStatus()); + assertThrows(ConflictException.class, () -> new SDSDirectoryFeature(session, nodeid).mkdir(new SDSDirectS3MultipartWriteFeature(session, nodeid), room, new TransferStatus())); + final Path test = new SDSDirectoryFeature(session, nodeid).mkdir(new SDSDirectS3MultipartWriteFeature(session, nodeid), new Path(room, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory)), new TransferStatus()); + assertThrows(ConflictException.class, () -> new SDSDirectoryFeature(session, nodeid).mkdir(new SDSDirectS3MultipartWriteFeature(session, nodeid), test, new TransferStatus())); assertNotNull(test.attributes().getVersionId()); assertTrue(new DefaultFindFeature(session).find(test)); // Replace directory on server with same name @@ -59,7 +59,7 @@ public class SDSDirectoryFeatureTest extends AbstractSDSTest { final Node node = new NodesApi(session.getClient()).createFolder(folderRequest, StringUtils.EMPTY, null); assertNotEquals(test.attributes().getVersionId(), node.getId().toString()); // Attempt to create subdirectory referencing previous node id - final Path subdir = new SDSDirectoryFeature(session, nodeid).mkdir(new Path(test, + final Path subdir = new SDSDirectoryFeature(session, nodeid).mkdir(new SDSDirectS3MultipartWriteFeature(session, nodeid), new Path(test, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory)), new TransferStatus()); assertEquals(node.getId().toString(), nodeid.getVersionId(test)); assertEquals(test.attributes().getVersionId(), node.getId().toString()); @@ -70,7 +70,7 @@ public class SDSDirectoryFeatureTest extends AbstractSDSTest { public void testCreateDataRoom() throws Exception { final SDSNodeIdProvider nodeid = new SDSNodeIdProvider(session); final Path room = new SDSDirectoryFeature(session, nodeid).mkdir( - new Path(new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory, Path.Type.volume)), new TransferStatus()); + new SDSDirectS3MultipartWriteFeature(session, nodeid), new Path(new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory, Path.Type.volume)), new TransferStatus()); assertNotNull(room.attributes().getVersionId()); assertTrue(new DefaultFindFeature(session).find(room)); new SDSDeleteFeature(session, nodeid).delete(Collections.singletonList(room), new DisabledLoginCallback(), new Delete.DisabledCallback()); diff --git a/dracoon/src/test/java/ch/cyberduck/core/sds/SDSEncryptionBulkFeatureTest.java b/dracoon/src/test/java/ch/cyberduck/core/sds/SDSEncryptionBulkFeatureTest.java index 3c8ca14eee..729c182c64 100644 --- a/dracoon/src/test/java/ch/cyberduck/core/sds/SDSEncryptionBulkFeatureTest.java +++ b/dracoon/src/test/java/ch/cyberduck/core/sds/SDSEncryptionBulkFeatureTest.java @@ -40,7 +40,7 @@ public class SDSEncryptionBulkFeatureTest extends AbstractSDSTest { @Test public void testMissingEncryptionStatus() throws Exception { final SDSNodeIdProvider nodeid = new SDSNodeIdProvider(session); - final Path room = new SDSDirectoryFeature(session, nodeid).mkdir(new Path( + final Path room = new SDSDirectoryFeature(session, nodeid).mkdir(new SDSDirectS3MultipartWriteFeature(session, nodeid), new Path( new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory, Path.Type.volume)), new TransferStatus()); final EncryptRoomRequest encrypt = new EncryptRoomRequest().isEncrypted(true); new NodesApi(session.getClient()).encryptRoom(encrypt, Long.parseLong(new SDSNodeIdProvider(session).getVersionId(room)), StringUtils.EMPTY, null); diff --git a/dracoon/src/test/java/ch/cyberduck/core/sds/SDSFindFeatureTest.java b/dracoon/src/test/java/ch/cyberduck/core/sds/SDSFindFeatureTest.java index 73cf6313af..7241e24149 100644 --- a/dracoon/src/test/java/ch/cyberduck/core/sds/SDSFindFeatureTest.java +++ b/dracoon/src/test/java/ch/cyberduck/core/sds/SDSFindFeatureTest.java @@ -40,7 +40,7 @@ public class SDSFindFeatureTest extends AbstractSDSTest { public void testFindDirectory() throws Exception { final SDSNodeIdProvider nodeid = new SDSNodeIdProvider(session); final Path folder = new SDSDirectoryFeature(session, nodeid).mkdir( - new Path(new DefaultHomeFinderService(session).find(), new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory)), new TransferStatus()); + new SDSDirectS3MultipartWriteFeature(session, nodeid), new Path(new DefaultHomeFinderService(session).find(), new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory)), new TransferStatus()); assertTrue(new SDSFindFeature(session, nodeid).find(folder)); assertFalse(new SDSFindFeature(session, nodeid).find(new Path(folder.getAbsolute(), EnumSet.of(Path.Type.file)))); new SDSDeleteFeature(session, nodeid).delete(Collections.singletonList(folder), new DisabledLoginCallback(), new Delete.DisabledCallback()); @@ -50,9 +50,9 @@ public class SDSFindFeatureTest extends AbstractSDSTest { public void testFindFile() throws Exception { final SDSNodeIdProvider nodeid = new SDSNodeIdProvider(session); final Path room = new SDSDirectoryFeature(session, nodeid).mkdir( - new Path(new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory, Path.Type.volume)), new TransferStatus()); + new SDSDirectS3MultipartWriteFeature(session, nodeid), new Path(new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory, Path.Type.volume)), new TransferStatus()); final Path file = new Path(room, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)); - new SDSTouchFeature(session, nodeid).touch(file, new TransferStatus()); + new SDSTouchFeature(session, nodeid).touch(new SDSDirectS3MultipartWriteFeature(session, nodeid), file, new TransferStatus()); assertTrue(new SDSFindFeature(session, nodeid).find(file)); assertFalse(new SDSFindFeature(session, nodeid).find(new Path(file.getAbsolute(), EnumSet.of(Path.Type.directory)))); new SDSDeleteFeature(session, nodeid).delete(Collections.singletonList(room), new DisabledLoginCallback(), new Delete.DisabledCallback()); diff --git a/dracoon/src/test/java/ch/cyberduck/core/sds/SDSListServiceTest.java b/dracoon/src/test/java/ch/cyberduck/core/sds/SDSListServiceTest.java index 56828898a1..e63cee46b8 100644 --- a/dracoon/src/test/java/ch/cyberduck/core/sds/SDSListServiceTest.java +++ b/dracoon/src/test/java/ch/cyberduck/core/sds/SDSListServiceTest.java @@ -57,7 +57,7 @@ public class SDSListServiceTest extends AbstractSDSTest { public void testList() throws Exception { final SDSNodeIdProvider nodeid = new SDSNodeIdProvider(session); final Path room = new SDSDirectoryFeature(session, nodeid).mkdir( - new Path(new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory, Path.Type.volume)), new TransferStatus()); + new SDSDirectS3MultipartWriteFeature(session, nodeid), new Path(new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory, Path.Type.volume)), new TransferStatus()); final AtomicBoolean callback = new AtomicBoolean(); assertTrue(new SDSListService(session, nodeid).list(room, new DisabledListProgressListener() { @Override @@ -68,16 +68,16 @@ public class SDSListServiceTest extends AbstractSDSTest { }).isEmpty()); assertTrue(callback.get()); final String filename = String.format("%s%s", new AlphanumericRandomStringService().random(), new NFDNormalizer().normalize("ä")); - final Path file = new SDSTouchFeature(session, nodeid).touch(new Path(room, filename, EnumSet.of(Path.Type.file)), new TransferStatus()); + final Path file = new SDSTouchFeature(session, nodeid).touch(new SDSDirectS3MultipartWriteFeature(session, nodeid), new Path(room, filename, EnumSet.of(Path.Type.file)), new TransferStatus()); final AttributedList list = new SDSListService(session, nodeid).list(room, new DisabledListProgressListener(), 1); assertEquals(1, (list.size())); assertNotNull(list.find(new DefaultPathPredicate(file))); // Not preserving Unicode normalization assertNotEquals(filename, list.find(new DefaultPathPredicate(file)).getName()); - new SDSTouchFeature(session, nodeid).touch(new Path(room, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)), new TransferStatus()); + new SDSTouchFeature(session, nodeid).touch(new SDSDirectS3MultipartWriteFeature(session, nodeid), new Path(room, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)), new TransferStatus()); assertEquals(2, (new SDSListService(session, nodeid).list(room, new DisabledListProgressListener(), 1).size())); assertEquals(2, (new SDSListService(session, nodeid).list(room, new DisabledListProgressListener()).size())); - new SDSTouchFeature(session, nodeid).touch(new Path(room, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)), new TransferStatus()); + new SDSTouchFeature(session, nodeid).touch(new SDSDirectS3MultipartWriteFeature(session, nodeid), new Path(room, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)), new TransferStatus()); assertEquals(3, (new SDSListService(session, nodeid).list(room, new DisabledListProgressListener(), 1).size())); assertEquals(3, (new SDSListService(session, nodeid).list(room, new DisabledListProgressListener()).size())); new SDSDeleteFeature(session, nodeid).delete(Collections.singletonList(room), new DisabledLoginCallback(), new Delete.DisabledCallback()); @@ -87,11 +87,11 @@ public class SDSListServiceTest extends AbstractSDSTest { public void testListAlphanumeric() throws Exception { final SDSNodeIdProvider nodeid = new SDSNodeIdProvider(session); final Path room = new SDSDirectoryFeature(session, nodeid).mkdir( - new Path(new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory, Path.Type.volume)), new TransferStatus()); + new SDSDirectS3MultipartWriteFeature(session, nodeid), new Path(new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory, Path.Type.volume)), new TransferStatus()); assertTrue(new SDSListService(session, nodeid).list(room, new DisabledListProgressListener()).isEmpty()); - new SDSTouchFeature(session, nodeid).touch(new Path(room, "aa", EnumSet.of(Path.Type.file)), new TransferStatus()); - new SDSTouchFeature(session, nodeid).touch(new Path(room, "0a", EnumSet.of(Path.Type.file)), new TransferStatus()); - new SDSTouchFeature(session, nodeid).touch(new Path(room, "a", EnumSet.of(Path.Type.file)), new TransferStatus()); + new SDSTouchFeature(session, nodeid).touch(new SDSDirectS3MultipartWriteFeature(session, nodeid), new Path(room, "aa", EnumSet.of(Path.Type.file)), new TransferStatus()); + new SDSTouchFeature(session, nodeid).touch(new SDSDirectS3MultipartWriteFeature(session, nodeid), new Path(room, "0a", EnumSet.of(Path.Type.file)), new TransferStatus()); + new SDSTouchFeature(session, nodeid).touch(new SDSDirectS3MultipartWriteFeature(session, nodeid), new Path(room, "a", EnumSet.of(Path.Type.file)), new TransferStatus()); final AttributedList list = new SDSListService(session, nodeid).list(room, new DisabledListProgressListener()); assertEquals(3, list.size()); assertEquals("0a", list.get(0).getName()); diff --git a/dracoon/src/test/java/ch/cyberduck/core/sds/SDSMissingFileKeysSchedulerFeatureTest.java b/dracoon/src/test/java/ch/cyberduck/core/sds/SDSMissingFileKeysSchedulerFeatureTest.java index 3caf561fc2..74b825d780 100644 --- a/dracoon/src/test/java/ch/cyberduck/core/sds/SDSMissingFileKeysSchedulerFeatureTest.java +++ b/dracoon/src/test/java/ch/cyberduck/core/sds/SDSMissingFileKeysSchedulerFeatureTest.java @@ -49,6 +49,7 @@ import ch.cyberduck.test.IntegrationTest; import org.apache.commons.lang3.RandomUtils; import org.apache.commons.lang3.StringUtils; +import org.junit.Ignore; import org.junit.Test; import org.junit.experimental.categories.Category; @@ -75,7 +76,7 @@ public class SDSMissingFileKeysSchedulerFeatureTest extends AbstractSDSTest { @Test public void testMissingKeys() throws Exception { final SDSNodeIdProvider nodeid = new SDSNodeIdProvider(session); - final Path room = new SDSDirectoryFeature(session, nodeid).mkdir(new Path( + final Path room = new SDSDirectoryFeature(session, nodeid).mkdir(new SDSDirectS3MultipartWriteFeature(session, nodeid), new Path( new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory, Path.Type.volume)), new TransferStatus()); final EncryptRoomRequest encrypt = new EncryptRoomRequest().isEncrypted(true); final Node node = new NodesApi(session.getClient()).encryptRoom(encrypt, Long.parseLong(new SDSNodeIdProvider(session).getVersionId(room)), StringUtils.EMPTY, null); @@ -127,6 +128,7 @@ public class SDSMissingFileKeysSchedulerFeatureTest extends AbstractSDSTest { } @Test + @Ignore public void testFileKeyMigration() throws Exception { final UserApi userApi = new UserApi(session.getClient()); this.removeKeyPairs(userApi); @@ -190,7 +192,7 @@ public class SDSMissingFileKeysSchedulerFeatureTest extends AbstractSDSTest { @Test(expected = LoginCanceledException.class) public void testWrongPassword() throws Exception { final SDSNodeIdProvider nodeid = new SDSNodeIdProvider(session); - final Path room = new SDSDirectoryFeature(session, nodeid).mkdir(new Path( + final Path room = new SDSDirectoryFeature(session, nodeid).mkdir(new SDSDirectS3MultipartWriteFeature(session, nodeid), new Path( new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory, Path.Type.volume)), new TransferStatus()); final EncryptRoomRequest encrypt = new EncryptRoomRequest().isEncrypted(true); final Node node = new NodesApi(session.getClient()).encryptRoom(encrypt, Long.parseLong(new SDSNodeIdProvider(session).getVersionId(room)), StringUtils.EMPTY, null); diff --git a/dracoon/src/test/java/ch/cyberduck/core/sds/SDSMoveFeatureTest.java b/dracoon/src/test/java/ch/cyberduck/core/sds/SDSMoveFeatureTest.java index de8f14d97a..0a3bd5d251 100644 --- a/dracoon/src/test/java/ch/cyberduck/core/sds/SDSMoveFeatureTest.java +++ b/dracoon/src/test/java/ch/cyberduck/core/sds/SDSMoveFeatureTest.java @@ -41,10 +41,10 @@ public class SDSMoveFeatureTest extends AbstractSDSTest { @Test public void testMoveDirectory() throws Exception { final SDSNodeIdProvider nodeid = new SDSNodeIdProvider(session); - final Path room = new SDSDirectoryFeature(session, nodeid).mkdir(new Path( + final Path room = new SDSDirectoryFeature(session, nodeid).mkdir(new SDSDirectS3MultipartWriteFeature(session, nodeid), new Path( new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory, Path.Type.volume)), new TransferStatus()); final String foldername = new AlphanumericRandomStringService().random(); - final Path test = new SDSDirectoryFeature(session, nodeid).mkdir(new Path(room, foldername, EnumSet.of(Path.Type.directory)), new TransferStatus()); + final Path test = new SDSDirectoryFeature(session, nodeid).mkdir(new SDSDirectS3MultipartWriteFeature(session, nodeid), new Path(room, foldername, EnumSet.of(Path.Type.directory)), new TransferStatus()); final Path target = new Path(room, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory)); new SDSMoveFeature(session, nodeid).move(test, target, new TransferStatus(), new Delete.DisabledCallback(), new DisabledConnectionCallback()); assertThrows(NotfoundException.class, () -> new SDSMoveFeature(session, nodeid).move(test, target, new TransferStatus(), new Delete.DisabledCallback(), new DisabledConnectionCallback())); @@ -57,9 +57,9 @@ public class SDSMoveFeatureTest extends AbstractSDSTest { @Test(expected = InteroperabilityException.class) public void testMoveDirectoryToRoot() throws Exception { final SDSNodeIdProvider nodeid = new SDSNodeIdProvider(session); - final Path room = new SDSDirectoryFeature(session, nodeid).mkdir(new Path( + final Path room = new SDSDirectoryFeature(session, nodeid).mkdir(new SDSDirectS3MultipartWriteFeature(session, nodeid), new Path( new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory, Path.Type.volume)), new TransferStatus()); - final Path test = new SDSDirectoryFeature(session, nodeid).mkdir(new Path(room, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory)), new TransferStatus()); + final Path test = new SDSDirectoryFeature(session, nodeid).mkdir(new SDSDirectS3MultipartWriteFeature(session, nodeid), new Path(room, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory)), new TransferStatus()); final Path target = new Path(new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory)); final SDSMoveFeature move = new SDSMoveFeature(session, nodeid); assertFalse(move.isSupported(test, Optional.of(target))); @@ -73,10 +73,10 @@ public class SDSMoveFeatureTest extends AbstractSDSTest { @Test public void testMoveRoomToDirectory() throws Exception { final SDSNodeIdProvider nodeid = new SDSNodeIdProvider(session); - final Path room = new SDSDirectoryFeature(session, nodeid).mkdir(new Path( + final Path room = new SDSDirectoryFeature(session, nodeid).mkdir(new SDSDirectS3MultipartWriteFeature(session, nodeid), new Path( new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory, Path.Type.volume)), new TransferStatus()); final Path target = new Path(room, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory)); - final Path test = new SDSDirectoryFeature(session, nodeid).mkdir(new Path( + final Path test = new SDSDirectoryFeature(session, nodeid).mkdir(new SDSDirectS3MultipartWriteFeature(session, nodeid), new Path( new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory, Path.Type.volume)), new TransferStatus()); final SDSMoveFeature move = new SDSMoveFeature(session, nodeid); assertFalse(move.isSupported(test, Optional.of(target))); @@ -85,10 +85,10 @@ public class SDSMoveFeatureTest extends AbstractSDSTest { @Test public void testMoveSubroom() throws Exception { final SDSNodeIdProvider nodeid = new SDSNodeIdProvider(session); - final Path room = new SDSDirectoryFeature(session, nodeid).mkdir(new Path( + final Path room = new SDSDirectoryFeature(session, nodeid).mkdir(new SDSDirectS3MultipartWriteFeature(session, nodeid), new Path( new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory, Path.Type.volume)), new TransferStatus()); final Path target = new Path(room, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory)); - final Path subroom = new SDSDirectoryFeature(session, nodeid).mkdir(new Path(room, + final Path subroom = new SDSDirectoryFeature(session, nodeid).mkdir(new SDSDirectS3MultipartWriteFeature(session, nodeid), new Path(room, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory, Path.Type.volume)), new TransferStatus()); final SDSMoveFeature move = new SDSMoveFeature(session, nodeid); assertTrue(move.isSupported(subroom, Optional.of(target))); @@ -104,7 +104,7 @@ public class SDSMoveFeatureTest extends AbstractSDSTest { public void testMoveDataRoom() throws Exception { final SDSNodeIdProvider nodeid = new SDSNodeIdProvider(session); final String directoryname = new AlphanumericRandomStringService().random(); - final Path test = new SDSDirectoryFeature(session, nodeid).mkdir(new Path(directoryname, EnumSet.of(Path.Type.directory, Path.Type.volume)), new TransferStatus()); + final Path test = new SDSDirectoryFeature(session, nodeid).mkdir(new SDSDirectS3MultipartWriteFeature(session, nodeid), new Path(directoryname, EnumSet.of(Path.Type.directory, Path.Type.volume)), new TransferStatus()); final Path target = new Path(new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory, Path.Type.volume)); new SDSMoveFeature(session, nodeid).move(test, target, new TransferStatus(), new Delete.DisabledCallback(), new DisabledConnectionCallback()); assertEquals(0, session.getMetrics().get(Copy.class)); @@ -117,11 +117,11 @@ public class SDSMoveFeatureTest extends AbstractSDSTest { @Test public void testMoveOverride() throws Exception { final SDSNodeIdProvider nodeid = new SDSNodeIdProvider(session); - final Path room = new SDSDirectoryFeature(session, nodeid).mkdir(new Path( + final Path room = new SDSDirectoryFeature(session, nodeid).mkdir(new SDSDirectS3MultipartWriteFeature(session, nodeid), new Path( new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory, Path.Type.volume)), new TransferStatus()); final String filename = new AlphanumericRandomStringService().random(); - final Path test = new SDSTouchFeature(session, nodeid).touch(new Path(room, filename, EnumSet.of(Path.Type.file)), new TransferStatus()); - final Path target = new SDSTouchFeature(session, nodeid).touch(new Path(room, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)), new TransferStatus()); + final Path test = new SDSTouchFeature(session, nodeid).touch(new SDSDirectS3MultipartWriteFeature(session, nodeid), new Path(room, filename, EnumSet.of(Path.Type.file)), new TransferStatus()); + final Path target = new SDSTouchFeature(session, nodeid).touch(new SDSDirectS3MultipartWriteFeature(session, nodeid), new Path(room, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)), new TransferStatus()); new SDSMoveFeature(session, nodeid).move(test, target, new TransferStatus().setExists(true), new Delete.DisabledCallback(), new DisabledConnectionCallback()); assertFalse(new SDSFindFeature(session, nodeid).find(new Path(room, filename, EnumSet.of(Path.Type.file)))); assertTrue(new SDSFindFeature(session, nodeid).find(target)); @@ -131,11 +131,11 @@ public class SDSMoveFeatureTest extends AbstractSDSTest { @Test public void testMoveToDifferentParentAndRename() throws Exception { final SDSNodeIdProvider nodeid = new SDSNodeIdProvider(session); - final Path room = new SDSDirectoryFeature(session, nodeid).mkdir(new Path( + final Path room = new SDSDirectoryFeature(session, nodeid).mkdir(new SDSDirectS3MultipartWriteFeature(session, nodeid), new Path( new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory, Path.Type.volume)), new TransferStatus()); final String filename = new AlphanumericRandomStringService().random(); - final Path test = new SDSTouchFeature(session, nodeid).touch(new Path(room, filename, EnumSet.of(Path.Type.file)), new TransferStatus()); - final Path target = new SDSTouchFeature(session, nodeid).touch(new Path(room, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)), new TransferStatus()); + final Path test = new SDSTouchFeature(session, nodeid).touch(new SDSDirectS3MultipartWriteFeature(session, nodeid), new Path(room, filename, EnumSet.of(Path.Type.file)), new TransferStatus()); + final Path target = new SDSTouchFeature(session, nodeid).touch(new SDSDirectS3MultipartWriteFeature(session, nodeid), new Path(room, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)), new TransferStatus()); new SDSMoveFeature(session, nodeid).move(test, target, new TransferStatus().setExists(true), new Delete.DisabledCallback(), new DisabledConnectionCallback()); assertFalse(new SDSFindFeature(session, nodeid).find(new Path(room, filename, EnumSet.of(Path.Type.file)))); assertTrue(new SDSFindFeature(session, nodeid).find(target)); diff --git a/dracoon/src/test/java/ch/cyberduck/core/sds/SDSMultipartWriteFeatureTest.java b/dracoon/src/test/java/ch/cyberduck/core/sds/SDSMultipartWriteFeatureTest.java index 7e5bd3ebe2..08b2f07f6c 100644 --- a/dracoon/src/test/java/ch/cyberduck/core/sds/SDSMultipartWriteFeatureTest.java +++ b/dracoon/src/test/java/ch/cyberduck/core/sds/SDSMultipartWriteFeatureTest.java @@ -59,7 +59,7 @@ public class SDSMultipartWriteFeatureTest extends AbstractSDSTest { public void testWriteUnknownContentLength() throws Exception { final SDSNodeIdProvider nodeid = new SDSNodeIdProvider(session); final Path room = new SDSDirectoryFeature(session, nodeid).mkdir( - new Path(new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory, Path.Type.volume)), new TransferStatus()); + new SDSDirectS3MultipartWriteFeature(session, nodeid), new Path(new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory, Path.Type.volume)), new TransferStatus()); final byte[] content = RandomUtils.nextBytes(32769); final TransferStatus status = new TransferStatus(); status.setLength(-1L); @@ -84,7 +84,7 @@ public class SDSMultipartWriteFeatureTest extends AbstractSDSTest { public void testReadWrite() throws Exception { final SDSNodeIdProvider nodeid = new SDSNodeIdProvider(session); final Path room = new SDSDirectoryFeature(session, nodeid).mkdir( - new Path(new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory, Path.Type.volume)), new TransferStatus()); + new SDSDirectS3MultipartWriteFeature(session, nodeid), new Path(new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory, Path.Type.volume)), new TransferStatus()); final byte[] content = RandomUtils.nextBytes(32769); final Path test = new Path(room, new NFDNormalizer().normalize(String.format("ä%s", new AlphanumericRandomStringService().random())).toString(), EnumSet.of(Path.Type.file)); { @@ -151,7 +151,7 @@ public class SDSMultipartWriteFeatureTest extends AbstractSDSTest { public void testWriteCancel() throws Exception { final SDSNodeIdProvider nodeid = new SDSNodeIdProvider(session); final Path room = new SDSDirectoryFeature(session, nodeid).mkdir( - new Path(new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory, Path.Type.volume)), new TransferStatus()); + new SDSDirectS3MultipartWriteFeature(session, nodeid), new Path(new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory, Path.Type.volume)), new TransferStatus()); final byte[] content = RandomUtils.nextBytes(32769); final Path test = new Path(room, String.format("{%s", new AlphanumericRandomStringService().random()), EnumSet.of(Path.Type.file)); final BytecountStreamListener count = new BytecountStreamListener(); @@ -178,13 +178,13 @@ public class SDSMultipartWriteFeatureTest extends AbstractSDSTest { final SDSNodeIdProvider nodeid = new SDSNodeIdProvider(session); final String rommname = new AlphanumericRandomStringService().random(); final Path room = new SDSDirectoryFeature(session, nodeid).mkdir( - new Path(rommname, EnumSet.of(Path.Type.directory, Path.Type.volume)), new TransferStatus()); + new SDSDirectS3MultipartWriteFeature(session, nodeid), new Path(rommname, EnumSet.of(Path.Type.directory, Path.Type.volume)), new TransferStatus()); final String fileid = nodeid.getNodeId(room, 1); assertEquals(fileid, room.attributes().getVersionId()); final Path test = new Path(room, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)); new SDSDeleteFeature(session, nodeid).delete(Collections.singletonList(room), new DisabledLoginCallback(), new Delete.DisabledCallback()); final Path roomNew = new SDSDirectoryFeature(session, nodeid).mkdir( - new Path(rommname, EnumSet.of(Path.Type.directory, Path.Type.volume)), new TransferStatus()); + new SDSDirectS3MultipartWriteFeature(session, nodeid), new Path(rommname, EnumSet.of(Path.Type.directory, Path.Type.volume)), new TransferStatus()); assertNotEquals(fileid, roomNew.attributes().getVersionId()); final TransferStatus status = new TransferStatus(); status.setLength(0L); @@ -208,7 +208,7 @@ public class SDSMultipartWriteFeatureTest extends AbstractSDSTest { public void testWriteZeroSingleByte() throws Exception { final SDSNodeIdProvider nodeid = new SDSNodeIdProvider(session); final Path room = new SDSDirectoryFeature(session, nodeid).mkdir( - new Path(new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory, Path.Type.volume)), new TransferStatus()); + new SDSMultipartWriteFeature(session, nodeid), new Path(new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory, Path.Type.volume)), new TransferStatus()); final byte[] content = RandomUtils.nextBytes(2); final TransferStatus status = new TransferStatus().setLength(content.length); final Path test = new Path(room, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)); @@ -225,7 +225,7 @@ public class SDSMultipartWriteFeatureTest extends AbstractSDSTest { public void testWriteZeroLength() throws Exception { final SDSNodeIdProvider nodeid = new SDSNodeIdProvider(session); final Path room = new SDSDirectoryFeature(session, nodeid).mkdir( - new Path(new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory, Path.Type.volume)), new TransferStatus()); + new SDSMultipartWriteFeature(session, nodeid), new Path(new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory, Path.Type.volume)), new TransferStatus()); final TransferStatus status = new TransferStatus(); final Path test = new Path(room, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)); final SDSMultipartWriteFeature writer = new SDSMultipartWriteFeature(session, nodeid); diff --git a/dracoon/src/test/java/ch/cyberduck/core/sds/SDSNodeIdProviderTest.java b/dracoon/src/test/java/ch/cyberduck/core/sds/SDSNodeIdProviderTest.java index dc361ce41d..a790f49b95 100644 --- a/dracoon/src/test/java/ch/cyberduck/core/sds/SDSNodeIdProviderTest.java +++ b/dracoon/src/test/java/ch/cyberduck/core/sds/SDSNodeIdProviderTest.java @@ -46,9 +46,9 @@ public class SDSNodeIdProviderTest extends AbstractSDSTest { @Test public void getFileIdFile() throws Exception { final SDSNodeIdProvider nodeid = new SDSNodeIdProvider(session); - final Path room = new SDSDirectoryFeature(session, nodeid).mkdir(new Path(new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory, Path.Type.volume)), new TransferStatus()); + final Path room = new SDSDirectoryFeature(session, nodeid).mkdir(new SDSDirectS3MultipartWriteFeature(session, nodeid), new Path(new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory, Path.Type.volume)), new TransferStatus()); final String name = String.format("%s%s", new AlphanumericRandomStringService().random(), new AlphanumericRandomStringService().random()); - final Path file = new SDSTouchFeature(session, nodeid).touch(new Path(room, name, EnumSet.of(Path.Type.file)), new TransferStatus()); + final Path file = new SDSTouchFeature(session, nodeid).touch(new SDSDirectS3MultipartWriteFeature(session, nodeid), new Path(room, name, EnumSet.of(Path.Type.file)), new TransferStatus()); nodeid.clear(); final String nodeId = nodeid.getNodeId(new Path(room, name, EnumSet.of(Path.Type.file)), 1); assertNotNull(nodeId); @@ -75,9 +75,9 @@ public class SDSNodeIdProviderTest extends AbstractSDSTest { @Test public void getFileIdFileVersions() throws Exception { final SDSNodeIdProvider nodeid = new SDSNodeIdProvider(session); - final Path room = new SDSDirectoryFeature(session, nodeid).mkdir(new Path(new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory, Path.Type.volume)), new TransferStatus()); + final Path room = new SDSDirectoryFeature(session, nodeid).mkdir(new SDSDirectS3MultipartWriteFeature(session, nodeid), new Path(new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory, Path.Type.volume)), new TransferStatus()); final String name = new AlphanumericRandomStringService().random(); - final Path file = new SDSTouchFeature(session, nodeid).touch(new Path(room, name, EnumSet.of(Path.Type.file)), new TransferStatus()); + final Path file = new SDSTouchFeature(session, nodeid).touch(new SDSDirectS3MultipartWriteFeature(session, nodeid), new Path(room, name, EnumSet.of(Path.Type.file)), new TransferStatus()); final String versionIdTouch = file.attributes().getVersionId(); nodeid.clear(); assertEquals(versionIdTouch, nodeid.getNodeId(new Path(room, name, EnumSet.of(Path.Type.file)), 1)); @@ -99,11 +99,11 @@ public class SDSNodeIdProviderTest extends AbstractSDSTest { @Test public void getFileIdDirectory() throws Exception { final SDSNodeIdProvider nodeid = new SDSNodeIdProvider(session); - final Path room = new SDSDirectoryFeature(session, nodeid).mkdir(new Path(new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory, Path.Type.volume)), new TransferStatus()); + final Path room = new SDSDirectoryFeature(session, nodeid).mkdir(new SDSDirectS3MultipartWriteFeature(session, nodeid), new Path(new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory, Path.Type.volume)), new TransferStatus()); final String name = new AlphanumericRandomStringService().random(); - final Path folder = new SDSDirectoryFeature(session, nodeid).mkdir(new Path(room, name, EnumSet.of(Path.Type.directory)), new TransferStatus()); + final Path folder = new SDSDirectoryFeature(session, nodeid).mkdir(new SDSDirectS3MultipartWriteFeature(session, nodeid), new Path(room, name, EnumSet.of(Path.Type.directory)), new TransferStatus()); assertNotNull(folder.attributes().getVersionId()); - final Path file = new SDSTouchFeature(session, nodeid).touch(new Path(folder, name, EnumSet.of(Path.Type.file)), new TransferStatus()); + final Path file = new SDSTouchFeature(session, nodeid).touch(new SDSDirectS3MultipartWriteFeature(session, nodeid), new Path(folder, name, EnumSet.of(Path.Type.file)), new TransferStatus()); assertNotNull(file.attributes().getVersionId()); nodeid.clear(); assertEquals(folder.attributes().getVersionId(), nodeid.getNodeId(new Path(room, name, EnumSet.of(Path.Type.directory)), 1)); @@ -122,10 +122,10 @@ public class SDSNodeIdProviderTest extends AbstractSDSTest { public void getFileIdRoom() throws Exception { final SDSNodeIdProvider nodeid = new SDSNodeIdProvider(session); final String roomname = new AlphanumericRandomStringService().random(); - final Path room = new SDSDirectoryFeature(session, nodeid).mkdir(new Path(roomname, EnumSet.of(Path.Type.directory, Path.Type.volume)), new TransferStatus()); + final Path room = new SDSDirectoryFeature(session, nodeid).mkdir(new SDSDirectS3MultipartWriteFeature(session, nodeid), new Path(roomname, EnumSet.of(Path.Type.directory, Path.Type.volume)), new TransferStatus()); assertNotNull(room.attributes().getVersionId()); final String subroomname = new AlphanumericRandomStringService().random(); - final Path subroom = new SDSDirectoryFeature(session, nodeid).mkdir(new Path(room, subroomname, EnumSet.of(Path.Type.directory, Path.Type.volume)), new TransferStatus()); + final Path subroom = new SDSDirectoryFeature(session, nodeid).mkdir(new SDSDirectS3MultipartWriteFeature(session, nodeid), new Path(room, subroomname, EnumSet.of(Path.Type.directory, Path.Type.volume)), new TransferStatus()); assertNotNull(subroom.attributes().getVersionId()); nodeid.clear(); assertEquals(room.attributes().getVersionId(), nodeid.getNodeId(new Path(roomname, EnumSet.of(Path.Type.directory)), 1)); diff --git a/dracoon/src/test/java/ch/cyberduck/core/sds/SDSQuotaFeatureTest.java b/dracoon/src/test/java/ch/cyberduck/core/sds/SDSQuotaFeatureTest.java index 6cd30bbf9d..826270b47c 100644 --- a/dracoon/src/test/java/ch/cyberduck/core/sds/SDSQuotaFeatureTest.java +++ b/dracoon/src/test/java/ch/cyberduck/core/sds/SDSQuotaFeatureTest.java @@ -46,7 +46,7 @@ public class SDSQuotaFeatureTest extends AbstractSDSTest { public void testRoom() throws Exception { final SDSNodeIdProvider nodeid = new SDSNodeIdProvider(session); final Path room = new SDSDirectoryFeature(session, nodeid).mkdir( - new Path(new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory, Path.Type.volume)), new TransferStatus()); + new SDSDirectS3MultipartWriteFeature(session, nodeid), new Path(new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory, Path.Type.volume)), new TransferStatus()); final Quota.Space quota = new SDSQuotaFeature(session, nodeid).get(); assertNotNull(quota.available); assertNotNull(quota.used); diff --git a/dracoon/src/test/java/ch/cyberduck/core/sds/SDSReadFeatureTest.java b/dracoon/src/test/java/ch/cyberduck/core/sds/SDSReadFeatureTest.java index 7cc225c186..a6a8028f2f 100644 --- a/dracoon/src/test/java/ch/cyberduck/core/sds/SDSReadFeatureTest.java +++ b/dracoon/src/test/java/ch/cyberduck/core/sds/SDSReadFeatureTest.java @@ -56,7 +56,7 @@ public class SDSReadFeatureTest extends AbstractSDSTest { final TransferStatus status = new TransferStatus(); final SDSNodeIdProvider nodeid = new SDSNodeIdProvider(session); final Path room = new SDSDirectoryFeature(session, nodeid).mkdir( - new Path(new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory, Path.Type.volume)), new TransferStatus()); + new SDSDirectS3MultipartWriteFeature(session, nodeid), new Path(new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory, Path.Type.volume)), new TransferStatus()); try { new SDSReadFeature(session, nodeid).read(new Path(room, "nosuchname", EnumSet.of(Path.Type.file)), status, new DisabledConnectionCallback()); } @@ -72,7 +72,7 @@ public class SDSReadFeatureTest extends AbstractSDSTest { writeStatus.setLength(content.length); final SDSNodeIdProvider nodeid = new SDSNodeIdProvider(session); final Path room = new SDSDirectoryFeature(session, nodeid).mkdir( - new Path(new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory, Path.Type.volume)), new TransferStatus()); + new SDSDirectS3MultipartWriteFeature(session, nodeid), new Path(new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory, Path.Type.volume)), new TransferStatus()); final Path test = new Path(room, UUID.randomUUID().toString(), EnumSet.of(Path.Type.file)); final SDSDirectS3MultipartWriteFeature writer = new SDSDirectS3MultipartWriteFeature(session, nodeid); final HttpResponseOutputStream out = writer.write(test, writeStatus, new DisabledConnectionCallback()); @@ -98,9 +98,9 @@ public class SDSReadFeatureTest extends AbstractSDSTest { public void testReadRange() throws Exception { final SDSNodeIdProvider nodeid = new SDSNodeIdProvider(session); final Path room = new SDSDirectoryFeature(session, nodeid).mkdir( - new Path(new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory, Path.Type.volume)), new TransferStatus()); + new SDSDirectS3MultipartWriteFeature(session, nodeid), new Path(new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory, Path.Type.volume)), new TransferStatus()); final Path test = new Path(room, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)); - new SDSTouchFeature(session, nodeid).touch(test, new TransferStatus()); + new SDSTouchFeature(session, nodeid).touch(new SDSDirectS3MultipartWriteFeature(session, nodeid), test, new TransferStatus()); final Local local = new Local(System.getProperty("java.io.tmpdir"), new AlphanumericRandomStringService().random()); final byte[] content = RandomUtils.nextBytes(1023); final OutputStream out = local.getOutputStream(false); @@ -109,8 +109,8 @@ public class SDSReadFeatureTest extends AbstractSDSTest { out.close(); final TransferStatus upload = new TransferStatus().setLength(content.length); upload.setExists(true); - new DefaultUploadFeature<>(new SDSDirectS3MultipartWriteFeature(session, nodeid)).upload( - test, local, new BandwidthThrottle(BandwidthThrottle.UNLIMITED), new DisabledProgressListener(), new DisabledStreamListener(), upload, + new DefaultUploadFeature(session).upload( + new SDSDirectS3MultipartWriteFeature(session, nodeid), test, local, new BandwidthThrottle(BandwidthThrottle.UNLIMITED), new DisabledProgressListener(), new DisabledStreamListener(), upload, new DisabledConnectionCallback()); final TransferStatus status = new TransferStatus(); status.setLength(content.length); @@ -131,9 +131,9 @@ public class SDSReadFeatureTest extends AbstractSDSTest { public void testReadRangeUnknownLength() throws Exception { final SDSNodeIdProvider nodeid = new SDSNodeIdProvider(session); final Path room = new SDSDirectoryFeature(session, nodeid).mkdir( - new Path(new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory, Path.Type.volume)), new TransferStatus()); + new SDSDirectS3MultipartWriteFeature(session, nodeid), new Path(new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory, Path.Type.volume)), new TransferStatus()); final Path test = new Path(room, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)); - new SDSTouchFeature(session, nodeid).touch(test, new TransferStatus()); + new SDSTouchFeature(session, nodeid).touch(new SDSDirectS3MultipartWriteFeature(session, nodeid), test, new TransferStatus()); final Local local = new Local(System.getProperty("java.io.tmpdir"), new AlphanumericRandomStringService().random()); final byte[] content = RandomUtils.nextBytes(1000); final OutputStream out = local.getOutputStream(false); @@ -142,8 +142,8 @@ public class SDSReadFeatureTest extends AbstractSDSTest { out.close(); final TransferStatus upload = new TransferStatus().setLength(content.length); upload.setExists(true); - new DefaultUploadFeature<>(new SDSDirectS3MultipartWriteFeature(session, nodeid)).upload( - test, local, new BandwidthThrottle(BandwidthThrottle.UNLIMITED), new DisabledProgressListener(), new DisabledStreamListener(), upload, + new DefaultUploadFeature(session).upload( + new SDSDirectS3MultipartWriteFeature(session, nodeid), test, local, new BandwidthThrottle(BandwidthThrottle.UNLIMITED), new DisabledProgressListener(), new DisabledStreamListener(), upload, new DisabledConnectionCallback()); final TransferStatus status = new TransferStatus(); status.setLength(-1L); @@ -168,7 +168,7 @@ public class SDSReadFeatureTest extends AbstractSDSTest { writeStatus.setLength(content.length); final SDSNodeIdProvider nodeid = new SDSNodeIdProvider(session); final Path room = new SDSDirectoryFeature(session, nodeid).mkdir( - new Path(new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory, Path.Type.volume)), new TransferStatus()); + new SDSDirectS3MultipartWriteFeature(session, nodeid), new Path(new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory, Path.Type.volume)), new TransferStatus()); final Path test = new Path(room, UUID.randomUUID().toString(), EnumSet.of(Path.Type.file)); final SDSDirectS3MultipartWriteFeature writer = new SDSDirectS3MultipartWriteFeature(session, nodeid); final HttpResponseOutputStream out = writer.write(test, writeStatus, new DisabledConnectionCallback()); @@ -184,8 +184,8 @@ public class SDSReadFeatureTest extends AbstractSDSTest { public void testChangedNodeId() throws Exception { final SDSNodeIdProvider nodeid = new SDSNodeIdProvider(session); final Path room = new SDSDirectoryFeature(session, nodeid).mkdir( - new Path(new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory, Path.Type.volume)), new TransferStatus()); - final Path test = new SDSTouchFeature(session, nodeid).touch(new Path(room, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)), new TransferStatus()); + new SDSDirectS3MultipartWriteFeature(session, nodeid), new Path(new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory, Path.Type.volume)), new TransferStatus()); + final Path test = new SDSTouchFeature(session, nodeid).touch(new SDSDirectS3MultipartWriteFeature(session, nodeid), new Path(room, 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 diff --git a/dracoon/src/test/java/ch/cyberduck/core/sds/SDSSearchFeatureTest.java b/dracoon/src/test/java/ch/cyberduck/core/sds/SDSSearchFeatureTest.java index de60cc9be7..e67eb01928 100644 --- a/dracoon/src/test/java/ch/cyberduck/core/sds/SDSSearchFeatureTest.java +++ b/dracoon/src/test/java/ch/cyberduck/core/sds/SDSSearchFeatureTest.java @@ -45,9 +45,9 @@ public class SDSSearchFeatureTest extends AbstractSDSTest { final String name = new NFDNormalizer().normalize(String.format("ä%s", new AlphanumericRandomStringService().random())).toString(); final SDSNodeIdProvider nodeid = new SDSNodeIdProvider(session); final Path room = new SDSDirectoryFeature(session, nodeid).mkdir( - new Path(new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory, Path.Type.volume)), new TransferStatus()); - final Path directory = new SDSDirectoryFeature(session, nodeid).mkdir(new Path(room, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory)), new TransferStatus()); - final Path file = new SDSTouchFeature(session, nodeid).touch(new Path(directory, name, EnumSet.of(Path.Type.file)), new TransferStatus()); + new SDSDirectS3MultipartWriteFeature(session, nodeid), new Path(new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory, Path.Type.volume)), new TransferStatus()); + final Path directory = new SDSDirectoryFeature(session, nodeid).mkdir(new SDSDirectS3MultipartWriteFeature(session, nodeid), new Path(room, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory)), new TransferStatus()); + final Path file = new SDSTouchFeature(session, nodeid).touch(new SDSDirectS3MultipartWriteFeature(session, nodeid), new Path(directory, name, EnumSet.of(Path.Type.file)), new TransferStatus()); final SDSSearchFeature feature = new SDSSearchFeature(session, nodeid); assertTrue(feature.search(room, new SearchFilter(name), new DisabledListProgressListener(), 1).contains(file)); assertTrue(feature.search(room, new SearchFilter(StringUtils.substring(name, 2)), new DisabledListProgressListener(), 1).contains(file)); @@ -60,9 +60,9 @@ public class SDSSearchFeatureTest extends AbstractSDSTest { catch(NotfoundException e) { // } - final Path subdir = new SDSDirectoryFeature(session, nodeid).mkdir(new Path(directory, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory)), new TransferStatus()); + final Path subdir = new SDSDirectoryFeature(session, nodeid).mkdir(new SDSDirectS3MultipartWriteFeature(session, nodeid), new Path(directory, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory)), new TransferStatus()); assertNull(feature.search(subdir, new SearchFilter(name), new DisabledListProgressListener(), 1).find(new SimplePathPredicate(file))); - final Path filesubdir = new SDSTouchFeature(session, nodeid).touch(new Path(subdir, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)), new TransferStatus()); + final Path filesubdir = new SDSTouchFeature(session, nodeid).touch(new SDSDirectS3MultipartWriteFeature(session, nodeid), new Path(subdir, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)), new TransferStatus()); { final AttributedList result = feature.search(directory, new SearchFilter(filesubdir.getName()), new DisabledListProgressListener(), 1); assertNotNull(result.find(new SimplePathPredicate(filesubdir))); diff --git a/dracoon/src/test/java/ch/cyberduck/core/sds/SDSSessionTest.java b/dracoon/src/test/java/ch/cyberduck/core/sds/SDSSessionTest.java index 9f7bb7ecc0..af809867b2 100644 --- a/dracoon/src/test/java/ch/cyberduck/core/sds/SDSSessionTest.java +++ b/dracoon/src/test/java/ch/cyberduck/core/sds/SDSSessionTest.java @@ -85,7 +85,7 @@ public class SDSSessionTest extends AbstractSDSTest { final Profile profile = new ProfilePlistReader(factory).read( this.getClass().getResourceAsStream("/DRACOON (CLI).cyberduckprofile")); final Host host = new Host(profile, "duck.dracoon.com", new Credentials( - System.getProperties().getProperty("dracoon.user"), System.getProperties().getProperty("dracoon.key") + PROPERTIES.get("dracoon.user"), PROPERTIES.get("dracoon.key") )); final SDSSession session = new SDSSession(host, new DisabledX509TrustManager(), new DefaultX509KeyManager()); final LoginConnectionService c = new LoginConnectionService( @@ -110,7 +110,7 @@ public class SDSSessionTest extends AbstractSDSTest { final Profile profile = new ProfilePlistReader(factory).read( this.getClass().getResourceAsStream("/DRACOON (CLI).cyberduckprofile")); final Host host = new Host(profile, "duck.dracoon.com", new Credentials( - System.getProperties().getProperty("dracoon.user"), System.getProperties().getProperty("dracoon.key") + PROPERTIES.get("dracoon.user"), PROPERTIES.get("dracoon.key") )); final SDSSession session = new SDSSession(host, new DefaultX509TrustManager(), new KeychainX509KeyManager(new DisabledCertificateIdentityCallback(), host, new DisabledCertificateStore())) { @@ -136,6 +136,7 @@ public class SDSSessionTest extends AbstractSDSTest { } @Test + @Ignore public void testKeyPairMigration() throws Exception { final UserApi userApi = new UserApi(session.getClient()); try { diff --git a/dracoon/src/test/java/ch/cyberduck/core/sds/SDSShareFeatureTest.java b/dracoon/src/test/java/ch/cyberduck/core/sds/SDSShareFeatureTest.java index 2a6c5d4c6f..cd18c27994 100644 --- a/dracoon/src/test/java/ch/cyberduck/core/sds/SDSShareFeatureTest.java +++ b/dracoon/src/test/java/ch/cyberduck/core/sds/SDSShareFeatureTest.java @@ -30,6 +30,7 @@ import ch.cyberduck.core.features.Share; import ch.cyberduck.core.sds.io.swagger.client.model.CreateDownloadShareRequest; import ch.cyberduck.core.sds.io.swagger.client.model.CreateUploadShareRequest; import ch.cyberduck.core.sds.io.swagger.client.model.ObjectExpiration; +import ch.cyberduck.core.sds.triplecrypt.TripleCryptWriteFeature; import ch.cyberduck.core.transfer.TransferStatus; import ch.cyberduck.core.vault.VaultCredentials; import ch.cyberduck.test.IntegrationTest; @@ -49,8 +50,8 @@ public class SDSShareFeatureTest extends AbstractSDSTest { @Test public void testShareFile() throws Exception { final SDSNodeIdProvider nodeid = new SDSNodeIdProvider(session); - final Path room = new SDSDirectoryFeature(session, nodeid).mkdir(new Path(new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory, Path.Type.volume)), new TransferStatus()); - final Path test = new SDSTouchFeature(session, nodeid).touch(new Path(room, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)), new TransferStatus()); + final Path room = new SDSDirectoryFeature(session, nodeid).mkdir(new SDSDirectS3MultipartWriteFeature(session, nodeid), new Path(new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory, Path.Type.volume)), new TransferStatus()); + final Path test = new SDSTouchFeature(session, nodeid).touch(new SDSDirectS3MultipartWriteFeature(session, nodeid), new Path(room, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)), new TransferStatus()); final DescriptiveUrl url = new SDSShareFeature(session, nodeid).toDownloadUrl(test, Share.Sharee.world, new CreateDownloadShareRequest() .expiration(new ObjectExpiration().enableExpiration(false)) @@ -71,7 +72,7 @@ public class SDSShareFeatureTest extends AbstractSDSTest { @Test public void testShareTopLevelRoom() throws Exception { final SDSNodeIdProvider nodeid = new SDSNodeIdProvider(session); - final Path room = new SDSDirectoryFeature(session, nodeid).mkdir(new Path(new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory, Path.Type.volume)), new TransferStatus()); + final Path room = new SDSDirectoryFeature(session, nodeid).mkdir(new SDSDirectS3MultipartWriteFeature(session, nodeid), new Path(new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory, Path.Type.volume)), new TransferStatus()); final DescriptiveUrl url = new SDSShareFeature(session, nodeid).toDownloadUrl(room, Share.Sharee.world, new CreateDownloadShareRequest() .expiration(new ObjectExpiration().enableExpiration(false)) @@ -92,8 +93,8 @@ public class SDSShareFeatureTest extends AbstractSDSTest { @Test public void testShareSubRoom() throws Exception { final SDSNodeIdProvider nodeid = new SDSNodeIdProvider(session); - final Path room = new SDSDirectoryFeature(session, nodeid).mkdir(new Path(new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory, Path.Type.volume)), new TransferStatus()); - final Path test = new SDSDirectoryFeature(session, nodeid).mkdir(new Path(room, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory)), new TransferStatus()); + final Path room = new SDSDirectoryFeature(session, nodeid).mkdir(new SDSDirectS3MultipartWriteFeature(session, nodeid), new Path(new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory, Path.Type.volume)), new TransferStatus()); + final Path test = new SDSDirectoryFeature(session, nodeid).mkdir(new SDSDirectS3MultipartWriteFeature(session, nodeid), new Path(room, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory)), new TransferStatus()); final DescriptiveUrl url = new SDSShareFeature(session, nodeid).toDownloadUrl(test, Share.Sharee.world, new CreateDownloadShareRequest() .expiration(new ObjectExpiration().enableExpiration(false)) @@ -114,8 +115,8 @@ public class SDSShareFeatureTest extends AbstractSDSTest { @Test(expected = InteroperabilityException.class) public void testToUrlMissingEmailRecipients() throws Exception { final SDSNodeIdProvider nodeid = new SDSNodeIdProvider(session); - final Path room = new SDSDirectoryFeature(session, nodeid).mkdir(new Path(new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory, Path.Type.volume)), new TransferStatus()); - final Path test = new SDSTouchFeature(session, nodeid).touch(new Path(room, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)), new TransferStatus()); + final Path room = new SDSDirectoryFeature(session, nodeid).mkdir(new SDSDirectS3MultipartWriteFeature(session, nodeid), new Path(new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory, Path.Type.volume)), new TransferStatus()); + final Path test = new SDSTouchFeature(session, nodeid).touch(new SDSDirectS3MultipartWriteFeature(session, nodeid), new Path(room, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)), new TransferStatus()); try { final DescriptiveUrl url = new SDSShareFeature(session, nodeid).toDownloadUrl(test, Share.Sharee.world, new CreateDownloadShareRequest() @@ -137,8 +138,8 @@ public class SDSShareFeatureTest extends AbstractSDSTest { @Test(expected = InteroperabilityException.class) public void testToUrlInvalidSMSRecipients() throws Exception { final SDSNodeIdProvider nodeid = new SDSNodeIdProvider(session); - final Path room = new SDSDirectoryFeature(session, nodeid).mkdir(new Path(new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory, Path.Type.volume)), new TransferStatus()); - final Path test = new SDSTouchFeature(session, nodeid).touch(new Path(room, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)), new TransferStatus()); + final Path room = new SDSDirectoryFeature(session, nodeid).mkdir(new SDSDirectS3MultipartWriteFeature(session, nodeid), new Path(new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory, Path.Type.volume)), new TransferStatus()); + final Path test = new SDSTouchFeature(session, nodeid).touch(new SDSDirectS3MultipartWriteFeature(session, nodeid), new Path(room, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)), new TransferStatus()); try { final DescriptiveUrl url = new SDSShareFeature(session, nodeid).toDownloadUrl(test, Share.Sharee.world, new CreateDownloadShareRequest() @@ -161,8 +162,8 @@ public class SDSShareFeatureTest extends AbstractSDSTest { @Test(expected = InteroperabilityException.class) public void testToUrlWeakPassword() throws Exception { final SDSNodeIdProvider nodeid = new SDSNodeIdProvider(session); - final Path room = new SDSDirectoryFeature(session, nodeid).mkdir(new Path(new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory, Path.Type.volume)), new TransferStatus()); - final Path test = new SDSTouchFeature(session, nodeid).touch(new Path(room, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)), new TransferStatus()); + final Path room = new SDSDirectoryFeature(session, nodeid).mkdir(new SDSDirectS3MultipartWriteFeature(session, nodeid), new Path(new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory, Path.Type.volume)), new TransferStatus()); + final Path test = new SDSTouchFeature(session, nodeid).touch(new SDSDirectS3MultipartWriteFeature(session, nodeid), new Path(room, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)), new TransferStatus()); try { final DescriptiveUrl url = new SDSShareFeature(session, nodeid).toDownloadUrl(test, Share.Sharee.world, new CreateDownloadShareRequest() @@ -184,8 +185,8 @@ public class SDSShareFeatureTest extends AbstractSDSTest { @Test(expected = InteroperabilityException.class) public void testToUrlInvalidEmail() throws Exception { final SDSNodeIdProvider nodeid = new SDSNodeIdProvider(session); - final Path room = new SDSDirectoryFeature(session, nodeid).mkdir(new Path(new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory, Path.Type.volume)), new TransferStatus()); - final Path test = new SDSTouchFeature(session, nodeid).touch(new Path(room, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)), new TransferStatus()); + final Path room = new SDSDirectoryFeature(session, nodeid).mkdir(new SDSDirectS3MultipartWriteFeature(session, nodeid), new Path(new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory, Path.Type.volume)), new TransferStatus()); + final Path test = new SDSTouchFeature(session, nodeid).touch(new SDSDirectS3MultipartWriteFeature(session, nodeid), new Path(room, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)), new TransferStatus()); try { final DescriptiveUrl url = new SDSShareFeature(session, nodeid).toDownloadUrl(test, Share.Sharee.world, new CreateDownloadShareRequest() @@ -206,8 +207,8 @@ public class SDSShareFeatureTest extends AbstractSDSTest { @Test public void testToUrlExpiry() throws Exception { final SDSNodeIdProvider nodeid = new SDSNodeIdProvider(session); - final Path room = new SDSDirectoryFeature(session, nodeid).mkdir(new Path(new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory, Path.Type.volume)), new TransferStatus()); - final Path test = new SDSTouchFeature(session, nodeid).touch(new Path(room, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)), new TransferStatus()); + final Path room = new SDSDirectoryFeature(session, nodeid).mkdir(new SDSDirectS3MultipartWriteFeature(session, nodeid), new Path(new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory, Path.Type.volume)), new TransferStatus()); + final Path test = new SDSTouchFeature(session, nodeid).touch(new SDSDirectS3MultipartWriteFeature(session, nodeid), new Path(room, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)), new TransferStatus()); final DescriptiveUrl url = new SDSShareFeature(session, nodeid).toDownloadUrl(test, Share.Sharee.world, new CreateDownloadShareRequest() .expiration(new ObjectExpiration().enableExpiration(true).expireAt(new DateTime(1955400800000L))) @@ -228,8 +229,8 @@ public class SDSShareFeatureTest extends AbstractSDSTest { @Test(expected = InteroperabilityException.class) public void testToUrlExpiryInvalidDate() throws Exception { final SDSNodeIdProvider nodeid = new SDSNodeIdProvider(session); - final Path room = new SDSDirectoryFeature(session, nodeid).mkdir(new Path(new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory, Path.Type.volume)), new TransferStatus()); - final Path test = new SDSTouchFeature(session, nodeid).touch(new Path(room, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)), new TransferStatus()); + final Path room = new SDSDirectoryFeature(session, nodeid).mkdir(new SDSDirectS3MultipartWriteFeature(session, nodeid), new Path(new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory, Path.Type.volume)), new TransferStatus()); + final Path test = new SDSTouchFeature(session, nodeid).touch(new SDSDirectS3MultipartWriteFeature(session, nodeid), new Path(room, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)), new TransferStatus()); final DescriptiveUrl url = new SDSShareFeature(session, nodeid).toDownloadUrl(test, Share.Sharee.world, new CreateDownloadShareRequest() .expiration(new ObjectExpiration().enableExpiration(true).expireAt(new DateTime(17443L))) @@ -252,7 +253,8 @@ public class SDSShareFeatureTest extends AbstractSDSTest { final SDSNodeIdProvider nodeid = new SDSNodeIdProvider(session); final Path room = new SDSDirectoryFeature(session, nodeid).createRoom( new Path(new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory, Path.Type.volume)), true); - final Path test = new SDSTouchFeature(session, nodeid).touch(new Path(room, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)), new TransferStatus()); + final Path test = new SDSTouchFeature(session, nodeid).touch( + new TripleCryptWriteFeature(session, nodeid, new SDSDirectS3MultipartWriteFeature(session, nodeid)), new Path(room, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)), new TransferStatus()); final DescriptiveUrl url = new SDSShareFeature(session, nodeid).toDownloadUrl(test, Share.Sharee.world, new CreateDownloadShareRequest() .expiration(new ObjectExpiration().enableExpiration(false)) @@ -280,7 +282,8 @@ public class SDSShareFeatureTest extends AbstractSDSTest { final SDSNodeIdProvider nodeid = new SDSNodeIdProvider(session); final Path room = new SDSDirectoryFeature(session, nodeid).createRoom( new Path(new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory, Path.Type.volume)), true); - final Path test = new SDSTouchFeature(session, nodeid).touch(new Path(room, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)), new TransferStatus()); + final Path test = new SDSTouchFeature(session, nodeid).touch( + new TripleCryptWriteFeature(session, nodeid, new SDSDirectS3MultipartWriteFeature(session, nodeid)), new Path(room, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)), new TransferStatus()); try { final DescriptiveUrl url = new SDSShareFeature(session, nodeid).toDownloadUrl(test, Share.Sharee.world, new CreateDownloadShareRequest() @@ -302,7 +305,7 @@ public class SDSShareFeatureTest extends AbstractSDSTest { @Test public void testUploadAccountTopLevelRoom() throws Exception { final SDSNodeIdProvider nodeid = new SDSNodeIdProvider(session); - final Path room = new SDSDirectoryFeature(session, nodeid).mkdir(new Path(new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory, Path.Type.volume)), new TransferStatus()); + final Path room = new SDSDirectoryFeature(session, nodeid).mkdir(new SDSDirectS3MultipartWriteFeature(session, nodeid), new Path(new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory, Path.Type.volume)), new TransferStatus()); final DescriptiveUrl url = new SDSShareFeature(session, nodeid).toUploadUrl(room, Share.Sharee.world, new CreateUploadShareRequest() .name(new AlphanumericRandomStringService().random()) @@ -329,7 +332,7 @@ public class SDSShareFeatureTest extends AbstractSDSTest { final SDSNodeIdProvider nodeid = new SDSNodeIdProvider(session); final Path room = new SDSDirectoryFeature(session, nodeid).createRoom( new Path(new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory, Path.Type.volume)), true); - final Path folder = new SDSDirectoryFeature(session, nodeid).mkdir(new Path(room, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory)), new TransferStatus()); + final Path folder = new SDSDirectoryFeature(session, nodeid).mkdir(new SDSDirectS3MultipartWriteFeature(session, nodeid), new Path(room, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory)), new TransferStatus()); final DescriptiveUrl url = new SDSShareFeature(session, nodeid).toUploadUrl(folder, Share.Sharee.world, new CreateUploadShareRequest() .name(new AlphanumericRandomStringService().random()) @@ -354,8 +357,8 @@ public class SDSShareFeatureTest extends AbstractSDSTest { @Test public void testUploadAccountSubRoom() throws Exception { final SDSNodeIdProvider nodeid = new SDSNodeIdProvider(session); - final Path room = new SDSDirectoryFeature(session, nodeid).mkdir(new Path(new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory, Path.Type.volume)), new TransferStatus()); - final Path test = new SDSDirectoryFeature(session, nodeid).mkdir(new Path(room, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory)), new TransferStatus()); + final Path room = new SDSDirectoryFeature(session, nodeid).mkdir(new SDSDirectS3MultipartWriteFeature(session, nodeid), new Path(new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory, Path.Type.volume)), new TransferStatus()); + final Path test = new SDSDirectoryFeature(session, nodeid).mkdir(new SDSDirectS3MultipartWriteFeature(session, nodeid), new Path(room, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory)), new TransferStatus()); final DescriptiveUrl url = new SDSShareFeature(session, nodeid).toUploadUrl(test, Share.Sharee.world, new CreateUploadShareRequest() .name(new AlphanumericRandomStringService().random()) diff --git a/dracoon/src/test/java/ch/cyberduck/core/sds/SDSThresholdDeleteFeatureTest.java b/dracoon/src/test/java/ch/cyberduck/core/sds/SDSThresholdDeleteFeatureTest.java index 9a29ad82b7..afa1ae19b4 100644 --- a/dracoon/src/test/java/ch/cyberduck/core/sds/SDSThresholdDeleteFeatureTest.java +++ b/dracoon/src/test/java/ch/cyberduck/core/sds/SDSThresholdDeleteFeatureTest.java @@ -15,6 +15,8 @@ package ch.cyberduck.core.sds; * GNU General Public License for more details. */ +import ch.cyberduck.core.features.Delete; +import ch.cyberduck.core.features.Home; import ch.cyberduck.test.IntegrationTest; import org.junit.Test; @@ -28,6 +30,6 @@ public class SDSThresholdDeleteFeatureTest extends AbstractSDSTest { @Test public void testIsRecursive() { final SDSNodeIdProvider nodeid = new SDSNodeIdProvider(session); - assertTrue(new SDSThresholdDeleteFeature(session, nodeid).isRecursive()); + assertTrue(new SDSThresholdDeleteFeature(session, nodeid).features(Home.root()).contains(Delete.Flags.recursive)); } } \ No newline at end of file diff --git a/dracoon/src/test/java/ch/cyberduck/core/sds/SDSTimestampFeatureTest.java b/dracoon/src/test/java/ch/cyberduck/core/sds/SDSTimestampFeatureTest.java index 6ae85941e4..c7cb7c8c77 100644 --- a/dracoon/src/test/java/ch/cyberduck/core/sds/SDSTimestampFeatureTest.java +++ b/dracoon/src/test/java/ch/cyberduck/core/sds/SDSTimestampFeatureTest.java @@ -38,8 +38,8 @@ public class SDSTimestampFeatureTest extends AbstractSDSTest { public void testWriteTimestampFile() throws Exception { final SDSNodeIdProvider nodeid = new SDSNodeIdProvider(session); final Path room = new SDSDirectoryFeature(session, nodeid).mkdir( - new Path(new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory, Path.Type.volume)), new TransferStatus()); - final Path test = new SDSTouchFeature(session, nodeid).touch(new Path(room, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)), new TransferStatus()); + new SDSDirectS3MultipartWriteFeature(session, nodeid), new Path(new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory, Path.Type.volume)), new TransferStatus()); + final Path test = new SDSTouchFeature(session, nodeid).touch(new SDSDirectS3MultipartWriteFeature(session, nodeid), new Path(room, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)), new TransferStatus()); final Long revision = test.attributes().getRevision(); assertNotNull(revision); final TransferStatus status = new TransferStatus().setModified(1599047952805L); @@ -56,8 +56,8 @@ public class SDSTimestampFeatureTest extends AbstractSDSTest { public void testWriteTimestampFolder() throws Exception { final SDSNodeIdProvider nodeid = new SDSNodeIdProvider(session); final Path room = new SDSDirectoryFeature(session, nodeid).mkdir( - new Path(new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory, Path.Type.volume)), new TransferStatus()); - final Path test = new SDSDirectoryFeature(session, nodeid).mkdir(new Path(room, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory)), new TransferStatus()); + new SDSDirectS3MultipartWriteFeature(session, nodeid), new Path(new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory, Path.Type.volume)), new TransferStatus()); + final Path test = new SDSDirectoryFeature(session, nodeid).mkdir(new SDSDirectS3MultipartWriteFeature(session, nodeid), new Path(room, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory)), new TransferStatus()); new SDSTimestampFeature(session, nodeid).setTimestamp(test, 1599047952805L); final SDSAttributesFinderFeature f = new SDSAttributesFinderFeature(session, nodeid); final PathAttributes attributes = f.find(test); @@ -69,7 +69,7 @@ public class SDSTimestampFeatureTest extends AbstractSDSTest { public void testWriteTimestampRoom() throws Exception { final SDSNodeIdProvider nodeid = new SDSNodeIdProvider(session); final Path room = new SDSDirectoryFeature(session, nodeid).mkdir( - new Path(new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory, Path.Type.volume)), new TransferStatus()); + new SDSDirectS3MultipartWriteFeature(session, nodeid), new Path(new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory, Path.Type.volume)), new TransferStatus()); new SDSTimestampFeature(session, nodeid).setTimestamp(room, 1599047952805L); final SDSAttributesFinderFeature f = new SDSAttributesFinderFeature(session, nodeid); final PathAttributes attributes = f.find(room); diff --git a/dracoon/src/test/java/ch/cyberduck/core/sds/SDSTouchFeatureTest.java b/dracoon/src/test/java/ch/cyberduck/core/sds/SDSTouchFeatureTest.java index 4c7cd2cfb8..4c9e737e34 100644 --- a/dracoon/src/test/java/ch/cyberduck/core/sds/SDSTouchFeatureTest.java +++ b/dracoon/src/test/java/ch/cyberduck/core/sds/SDSTouchFeatureTest.java @@ -62,7 +62,7 @@ public class SDSTouchFeatureTest extends AbstractSDSTest { public void testTouchFileRoot() throws Exception { try { final SDSNodeIdProvider nodeid = new SDSNodeIdProvider(session); - new SDSTouchFeature(session, nodeid).touch(new Path(new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)), new TransferStatus()); + new SDSTouchFeature(session, nodeid).touch(new SDSDirectS3MultipartWriteFeature(session, nodeid), new Path(new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)), new TransferStatus()); } catch(InteroperabilityException e) { assertEquals("Error -80001. Parent ID must be positive. Please contact your web hosting service provider for assistance.", e.getDetail()); @@ -74,12 +74,12 @@ public class SDSTouchFeatureTest extends AbstractSDSTest { public void testInvalidName() throws Exception { final SDSNodeIdProvider nodeid = new SDSNodeIdProvider(session); final Path room = new SDSDirectoryFeature(session, nodeid).mkdir( - new Path(new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory, Path.Type.volume)), new TransferStatus()); + new SDSDirectS3MultipartWriteFeature(session, nodeid), new Path(new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory, Path.Type.volume)), new TransferStatus()); try { final SDSTouchFeature feature = new SDSTouchFeature(session, nodeid); assertFalse(feature.isSupported(room, "?")); assertThrows(InvalidFilenameException.class, () -> feature.preflight(room, "?")); - feature.touch(new Path(room, "CON", EnumSet.of(Path.Type.file)), new TransferStatus()); + feature.touch(new SDSDirectS3MultipartWriteFeature(session, nodeid), new Path(room, "CON", EnumSet.of(Path.Type.file)), new TransferStatus()); } catch(InteroperabilityException e) { assertEquals("Error -40755. Illegal file name='CON'. Please contact your web hosting service provider for assistance.", e.getDetail()); @@ -94,12 +94,12 @@ public class SDSTouchFeatureTest extends AbstractSDSTest { public void testInvalidCharacter() throws Exception { final SDSNodeIdProvider nodeid = new SDSNodeIdProvider(session); final Path room = new SDSDirectoryFeature(session, nodeid).mkdir( - new Path(new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory, Path.Type.volume)), new TransferStatus()); + new SDSDirectS3MultipartWriteFeature(session, nodeid), new Path(new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory, Path.Type.volume)), new TransferStatus()); try { final SDSTouchFeature feature = new SDSTouchFeature(session, nodeid); assertFalse(feature.isSupported(room, "?")); assertThrows(InvalidFilenameException.class, () -> feature.preflight(room, "?")); - feature.touch(new Path(room, "?", EnumSet.of(Path.Type.file)), new TransferStatus()); + feature.touch(new SDSDirectS3MultipartWriteFeature(session, nodeid), new Path(room, "?", EnumSet.of(Path.Type.file)), new TransferStatus()); } catch(InteroperabilityException e) { assertEquals("Error -40755. Illegal file name='?'. Please contact your web hosting service provider for assistance.", e.getDetail()); @@ -114,9 +114,9 @@ public class SDSTouchFeatureTest extends AbstractSDSTest { public void testTouch() throws Exception { final SDSNodeIdProvider nodeid = new SDSNodeIdProvider(session); final Path room = new SDSDirectoryFeature(session, nodeid).mkdir( - new Path(new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory, Path.Type.volume)), new TransferStatus()); + new SDSDirectS3MultipartWriteFeature(session, nodeid), new Path(new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory, Path.Type.volume)), new TransferStatus()); final Path test = new SDSTouchFeature(session, nodeid).touch( - new Path(room, new AsciiRandomStringService().random(), EnumSet.of(Path.Type.file)), new TransferStatus().setMime("text/plain")); + new SDSDirectS3MultipartWriteFeature(session, nodeid), new Path(room, new AsciiRandomStringService().random(), EnumSet.of(Path.Type.file)), new TransferStatus().setMime("text/plain")); assertNotNull(test.attributes().getVersionId()); assertTrue(new SDSFindFeature(session, nodeid).find(test)); assertEquals(test.attributes().getVersionId(), new SDSAttributesFinderFeature(session, nodeid).find(test).getVersionId()); @@ -127,7 +127,7 @@ public class SDSTouchFeatureTest extends AbstractSDSTest { public void testQuota() throws Exception { final SDSNodeIdProvider nodeid = new SDSNodeIdProvider(session); final Path room = new SDSDirectoryFeature(session, nodeid).mkdir( - new Path(new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory, Path.Type.volume)), new TransferStatus()); + new SDSDirectS3MultipartWriteFeature(session, nodeid), new Path(new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory, Path.Type.volume)), new TransferStatus()); final UpdateRoomRequest updateRoomRequest = new UpdateRoomRequest(); final long quota = 1L + PreferencesFactory.get().getInteger("sds.upload.multipart.chunksize"); updateRoomRequest.setQuota(quota); diff --git a/dracoon/src/test/java/ch/cyberduck/core/sds/SDSVersioningFeatureTest.java b/dracoon/src/test/java/ch/cyberduck/core/sds/SDSVersioningFeatureTest.java index d41e555e7c..e6ee4e859e 100644 --- a/dracoon/src/test/java/ch/cyberduck/core/sds/SDSVersioningFeatureTest.java +++ b/dracoon/src/test/java/ch/cyberduck/core/sds/SDSVersioningFeatureTest.java @@ -17,6 +17,7 @@ package ch.cyberduck.core.sds; import ch.cyberduck.core.AlphanumericRandomStringService; import ch.cyberduck.core.AttributedList; +import ch.cyberduck.core.DefaultPathAttributes; import ch.cyberduck.core.DefaultPathPredicate; import ch.cyberduck.core.DisabledConnectionCallback; import ch.cyberduck.core.DisabledListProgressListener; @@ -48,9 +49,9 @@ public class SDSVersioningFeatureTest extends AbstractSDSTest { public void testRevert() throws Exception { final SDSNodeIdProvider nodeid = new SDSNodeIdProvider(session); final Path room = new SDSDirectoryFeature(session, nodeid).mkdir( - new Path(new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory, Path.Type.volume)), new TransferStatus()); - final Path test = new SDSTouchFeature(session, nodeid).touch(new Path(room, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)), new TransferStatus()); - final PathAttributes initialAttributes = new PathAttributes(test.attributes()); + new SDSDirectS3MultipartWriteFeature(session, nodeid), new Path(new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory, Path.Type.volume)), new TransferStatus()); + final Path test = new SDSTouchFeature(session, nodeid).touch(new SDSDirectS3MultipartWriteFeature(session, nodeid), new Path(room, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)), new TransferStatus()); + final PathAttributes initialAttributes = new DefaultPathAttributes(test.attributes()); final String initialVersion = test.attributes().getVersionId(); final SDSVersioningFeature feature = new SDSVersioningFeature(session, nodeid); { diff --git a/dracoon/src/test/java/ch/cyberduck/core/sds/triplecrypt/TripleCryptReadFeatureTest.java b/dracoon/src/test/java/ch/cyberduck/core/sds/triplecrypt/TripleCryptReadFeatureTest.java index 29c480018b..9eef070286 100644 --- a/dracoon/src/test/java/ch/cyberduck/core/sds/triplecrypt/TripleCryptReadFeatureTest.java +++ b/dracoon/src/test/java/ch/cyberduck/core/sds/triplecrypt/TripleCryptReadFeatureTest.java @@ -67,7 +67,7 @@ public class TripleCryptReadFeatureTest extends AbstractSDSTest { @Test public void testPartialRead() throws Exception { final SDSNodeIdProvider nodeid = new SDSNodeIdProvider(session); - final Path room = new SDSDirectoryFeature(session, nodeid).mkdir(new Path( + final Path room = new SDSDirectoryFeature(session, nodeid).mkdir(new SDSDirectS3MultipartWriteFeature(session, nodeid), new Path( new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory, Path.Type.volume)), new TransferStatus()); final EncryptRoomRequest encrypt = new EncryptRoomRequest().isEncrypted(true); new NodesApi(session.getClient()).encryptRoom(encrypt, Long.parseLong(new SDSNodeIdProvider(session).getVersionId(room)), StringUtils.EMPTY, null); diff --git a/dracoon/src/test/java/ch/cyberduck/core/sds/triplecrypt/TripleCryptWriteFeatureTest.java b/dracoon/src/test/java/ch/cyberduck/core/sds/triplecrypt/TripleCryptWriteFeatureTest.java index 5ae1046c9b..181d2e694f 100644 --- a/dracoon/src/test/java/ch/cyberduck/core/sds/triplecrypt/TripleCryptWriteFeatureTest.java +++ b/dracoon/src/test/java/ch/cyberduck/core/sds/triplecrypt/TripleCryptWriteFeatureTest.java @@ -65,7 +65,7 @@ public class TripleCryptWriteFeatureTest extends AbstractSDSTest { @Test public void testWrite() throws Exception { final SDSNodeIdProvider nodeid = new SDSNodeIdProvider(session); - final Path room = new SDSDirectoryFeature(session, nodeid).mkdir(new Path( + final Path room = new SDSDirectoryFeature(session, nodeid).mkdir(new SDSDirectS3MultipartWriteFeature(session, nodeid), new Path( new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory, Path.Type.volume)), new TransferStatus()); final EncryptRoomRequest encrypt = new EncryptRoomRequest().isEncrypted(true); new NodesApi(session.getClient()).encryptRoom(encrypt, Long.parseLong(new SDSNodeIdProvider(session).getVersionId(room)), StringUtils.EMPTY, null); @@ -106,7 +106,7 @@ public class TripleCryptWriteFeatureTest extends AbstractSDSTest { @Test public void testWriteMultipart() throws Exception { final SDSNodeIdProvider nodeid = new SDSNodeIdProvider(session); - final Path room = new SDSDirectoryFeature(session, nodeid).mkdir(new Path( + final Path room = new SDSDirectoryFeature(session, nodeid).mkdir(new SDSDirectS3MultipartWriteFeature(session, nodeid), new Path( new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory, Path.Type.volume)), new TransferStatus()); final EncryptRoomRequest encrypt = new EncryptRoomRequest().isEncrypted(true); new NodesApi(session.getClient()).encryptRoom(encrypt, Long.parseLong(new SDSNodeIdProvider(session).getVersionId(room)), StringUtils.EMPTY, null); diff --git a/dracoon/src/test/java/ch/cyberduck/core/shared/DefaultAttributesFinderFeatureTest.java b/dracoon/src/test/java/ch/cyberduck/core/shared/DefaultAttributesFinderFeatureTest.java index c1eae4eaba..b2edb92283 100644 --- a/dracoon/src/test/java/ch/cyberduck/core/shared/DefaultAttributesFinderFeatureTest.java +++ b/dracoon/src/test/java/ch/cyberduck/core/shared/DefaultAttributesFinderFeatureTest.java @@ -26,6 +26,7 @@ import ch.cyberduck.core.features.AttributesFinder; import ch.cyberduck.core.features.Delete; import ch.cyberduck.core.sds.AbstractSDSTest; import ch.cyberduck.core.sds.SDSDeleteFeature; +import ch.cyberduck.core.sds.SDSDirectS3MultipartWriteFeature; import ch.cyberduck.core.sds.SDSDirectoryFeature; import ch.cyberduck.core.sds.SDSNodeIdProvider; import ch.cyberduck.core.sds.SDSTouchFeature; @@ -54,9 +55,9 @@ public class DefaultAttributesFinderFeatureTest extends AbstractSDSTest { final PathCache cache = new PathCache(1); final AttributesFinder f = new CachingAttributesFinderFeature(session, cache, new DefaultAttributesFinderFeature(session)); final SDSNodeIdProvider nodeid = new SDSNodeIdProvider(session); - final Path room = new SDSDirectoryFeature(session, nodeid).mkdir(new Path(new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory, Path.Type.volume)), new TransferStatus()); + final Path room = new SDSDirectoryFeature(session, nodeid).mkdir(new SDSDirectS3MultipartWriteFeature(session, nodeid), new Path(new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory, Path.Type.volume)), new TransferStatus()); final Path file = new Path(room, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)); - new SDSTouchFeature(session, nodeid).touch(file, new TransferStatus()); + new SDSTouchFeature(session, nodeid).touch(new SDSDirectS3MultipartWriteFeature(session, nodeid), file, new TransferStatus()); final Attributes attributes = f.find(file); assertEquals(0L, attributes.getSize()); // Test cache diff --git a/dracoon/src/test/java/ch/cyberduck/core/shared/DefaultCopyFeatureTest.java b/dracoon/src/test/java/ch/cyberduck/core/shared/DefaultCopyFeatureTest.java index 4edf3f48e5..e21e1b818e 100644 --- a/dracoon/src/test/java/ch/cyberduck/core/shared/DefaultCopyFeatureTest.java +++ b/dracoon/src/test/java/ch/cyberduck/core/shared/DefaultCopyFeatureTest.java @@ -26,6 +26,7 @@ import ch.cyberduck.core.io.StreamCopier; import ch.cyberduck.core.sds.AbstractSDSTest; import ch.cyberduck.core.sds.SDSDeleteFeature; import ch.cyberduck.core.sds.SDSDirectS3MultipartWriteFeature; +import ch.cyberduck.core.sds.SDSDirectS3WriteFeature; import ch.cyberduck.core.sds.SDSDirectoryFeature; import ch.cyberduck.core.sds.SDSNodeIdProvider; import ch.cyberduck.core.sds.SDSTouchFeature; @@ -57,10 +58,10 @@ public class DefaultCopyFeatureTest extends AbstractSDSTest { @Test public void testCopy() throws Exception { final SDSNodeIdProvider nodeid = new SDSNodeIdProvider(session); - final Path room = new SDSDirectoryFeature(session, nodeid).mkdir(new Path(new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory, Path.Type.volume)), new TransferStatus()); + final Path room = new SDSDirectoryFeature(session, nodeid).mkdir(new SDSDirectS3WriteFeature(session, nodeid), new Path(new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory, Path.Type.volume)), new TransferStatus()); final Path source = new Path(room, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)); final Path target = new Path(room, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)); - new SDSTouchFeature(session, nodeid).touch(source, new TransferStatus()); + new SDSTouchFeature(session, nodeid).touch(new SDSDirectS3MultipartWriteFeature(session, nodeid), source, new TransferStatus()); final byte[] content = RandomUtils.nextBytes(524); final TransferStatus status = new TransferStatus().setLength(content.length); status.setExists(true); diff --git a/dracoon/src/test/java/ch/cyberduck/core/shared/DefaultDownloadFeatureTest.java b/dracoon/src/test/java/ch/cyberduck/core/shared/DefaultDownloadFeatureTest.java index 19a1dc87ee..184fd6ab69 100644 --- a/dracoon/src/test/java/ch/cyberduck/core/shared/DefaultDownloadFeatureTest.java +++ b/dracoon/src/test/java/ch/cyberduck/core/shared/DefaultDownloadFeatureTest.java @@ -56,9 +56,9 @@ public class DefaultDownloadFeatureTest extends AbstractSDSTest { @Test public void testTransferAppend() throws Exception { final SDSNodeIdProvider nodeid = new SDSNodeIdProvider(session); - final Path room = new SDSDirectoryFeature(session, nodeid).mkdir(new Path(new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory, Path.Type.volume)), new TransferStatus()); + final Path room = new SDSDirectoryFeature(session, nodeid).mkdir(new SDSDirectS3MultipartWriteFeature(session, nodeid), new Path(new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory, Path.Type.volume)), new TransferStatus()); final Path test = new SDSTouchFeature(session, nodeid).touch( - new Path(room, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)), new TransferStatus()); + new SDSDirectS3MultipartWriteFeature(session, nodeid), new Path(room, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)), new TransferStatus()); final byte[] content = new byte[39864]; new Random().nextBytes(content); { @@ -71,15 +71,15 @@ public class DefaultDownloadFeatureTest extends AbstractSDSTest { final Local local = new Local(System.getProperty("java.io.tmpdir"), UUID.randomUUID().toString()); { final TransferStatus status = new TransferStatus().setLength(content.length / 2); - new DefaultDownloadFeature(new SDSReadFeature(session, nodeid)).download( - test, local, new BandwidthThrottle(BandwidthThrottle.UNLIMITED), new DisabledStreamListener(), + new DefaultDownloadFeature(session).download( + new SDSReadFeature(session, nodeid), test, local, new BandwidthThrottle(BandwidthThrottle.UNLIMITED), new DisabledStreamListener(), status, new DisabledConnectionCallback()); } { final TransferStatus status = new TransferStatus().setLength(content.length / 2).setOffset(content.length / 2).setAppend(true).setExists(true); - new DefaultDownloadFeature(new SDSReadFeature(session, nodeid)).download( - test, local, new BandwidthThrottle(BandwidthThrottle.UNLIMITED), new DisabledStreamListener(), + new DefaultDownloadFeature(session).download( + new SDSReadFeature(session, nodeid), test, local, new BandwidthThrottle(BandwidthThrottle.UNLIMITED), new DisabledStreamListener(), status, new DisabledConnectionCallback()); } @@ -94,9 +94,9 @@ public class DefaultDownloadFeatureTest extends AbstractSDSTest { @Test public void testTransferUnknownSize() throws Exception { final SDSNodeIdProvider nodeid = new SDSNodeIdProvider(session); - final Path room = new SDSDirectoryFeature(session, nodeid).mkdir(new Path(new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory, Path.Type.volume)), new TransferStatus()); + final Path room = new SDSDirectoryFeature(session, nodeid).mkdir(new SDSDirectS3MultipartWriteFeature(session, nodeid), new Path(new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory, Path.Type.volume)), new TransferStatus()); final Path test = new Path(room, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)); - new SDSTouchFeature(session, nodeid).touch(test, new TransferStatus()); + new SDSTouchFeature(session, nodeid).touch(new SDSDirectS3MultipartWriteFeature(session, nodeid), test, new TransferStatus()); final byte[] content = new byte[1]; new Random().nextBytes(content); { @@ -111,8 +111,8 @@ public class DefaultDownloadFeatureTest extends AbstractSDSTest { { final TransferStatus status = new TransferStatus().setLength(-1L); status.setExists(true); - new DefaultDownloadFeature(new SDSReadFeature(session, nodeid)).download( - test, local, new BandwidthThrottle(BandwidthThrottle.UNLIMITED), new DisabledStreamListener(), + new DefaultDownloadFeature(session).download( + new SDSReadFeature(session, nodeid), test, local, new BandwidthThrottle(BandwidthThrottle.UNLIMITED), new DisabledStreamListener(), status, new DisabledConnectionCallback()); } diff --git a/dracoon/src/test/java/ch/cyberduck/core/worker/CopyWorkerTest.java b/dracoon/src/test/java/ch/cyberduck/core/worker/CopyWorkerTest.java index 614357cd72..5f935e3093 100644 --- a/dracoon/src/test/java/ch/cyberduck/core/worker/CopyWorkerTest.java +++ b/dracoon/src/test/java/ch/cyberduck/core/worker/CopyWorkerTest.java @@ -23,6 +23,7 @@ import ch.cyberduck.core.Path; import ch.cyberduck.core.PathCache; import ch.cyberduck.core.pool.SessionPool; import ch.cyberduck.core.sds.AbstractSDSTest; +import ch.cyberduck.core.sds.SDSDirectS3MultipartWriteFeature; import ch.cyberduck.core.sds.SDSDirectoryFeature; import ch.cyberduck.core.sds.SDSFindFeature; import ch.cyberduck.core.sds.SDSNodeIdProvider; @@ -44,11 +45,11 @@ public class CopyWorkerTest extends AbstractSDSTest { @Test public void testCopyFile() throws Exception { final SDSNodeIdProvider nodeid = new SDSNodeIdProvider(session); - final Path room = new SDSDirectoryFeature(session, nodeid).mkdir(new Path( + final Path room = new SDSDirectoryFeature(session, nodeid).mkdir(new SDSDirectS3MultipartWriteFeature(session, nodeid), new Path( new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory, Path.Type.volume)), new TransferStatus()); final Path source = new Path(room, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)); final Path target = new Path(room, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)); - new SDSTouchFeature(session, nodeid).touch(source, new TransferStatus()); + new SDSTouchFeature(session, nodeid).touch(new SDSDirectS3MultipartWriteFeature(session, nodeid), source, new TransferStatus()); assertTrue(new SDSFindFeature(session, nodeid).find(source)); final CopyWorker worker = new CopyWorker(Collections.singletonMap(source, target), new SessionPool.SingleSessionPool(session), PathCache.empty(), new DisabledProgressListener(), new DisabledConnectionCallback()); worker.run(session); @@ -60,14 +61,14 @@ public class CopyWorkerTest extends AbstractSDSTest { @Test public void testCopyFileToDirectory() throws Exception { final SDSNodeIdProvider nodeid = new SDSNodeIdProvider(session); - final Path room = new SDSDirectoryFeature(session, nodeid).mkdir(new Path( + final Path room = new SDSDirectoryFeature(session, nodeid).mkdir(new SDSDirectS3MultipartWriteFeature(session, nodeid), new Path( new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory, Path.Type.volume)), new TransferStatus()); final Path sourceFile = new Path(room, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)); - new SDSTouchFeature(session, nodeid).touch(sourceFile, new TransferStatus()); + new SDSTouchFeature(session, nodeid).touch(new SDSDirectS3MultipartWriteFeature(session, nodeid), sourceFile, new TransferStatus()); assertTrue(new SDSFindFeature(session, nodeid).find(sourceFile)); final Path targetFolder = new Path(room, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory)); final Path targetFile = new Path(targetFolder, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)); - new SDSDirectoryFeature(session, nodeid).mkdir(targetFolder, new TransferStatus()); + new SDSDirectoryFeature(session, nodeid).mkdir(new SDSDirectS3MultipartWriteFeature(session, nodeid), targetFolder, new TransferStatus()); assertTrue(new SDSFindFeature(session, nodeid).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()); @@ -80,10 +81,10 @@ public class CopyWorkerTest extends AbstractSDSTest { @Test public void testCopyDirectory() throws Exception { final SDSNodeIdProvider nodeid = new SDSNodeIdProvider(session); - final Path room = new SDSDirectoryFeature(session, nodeid).mkdir(new Path( + final Path room = new SDSDirectoryFeature(session, nodeid).mkdir(new SDSDirectS3MultipartWriteFeature(session, nodeid), new Path( new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory, Path.Type.volume)), new TransferStatus()); - final Path folder = new SDSDirectoryFeature(session, nodeid).mkdir(new Path(room, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory)), new TransferStatus()); - final Path sourceFile = new SDSTouchFeature(session, nodeid).touch(new Path(folder, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)), new TransferStatus()); + final Path folder = new SDSDirectoryFeature(session, nodeid).mkdir(new SDSDirectS3MultipartWriteFeature(session, nodeid), new Path(room, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory)), new TransferStatus()); + final Path sourceFile = new SDSTouchFeature(session, nodeid).touch(new SDSDirectS3MultipartWriteFeature(session, nodeid), new Path(folder, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)), new TransferStatus()); assertTrue(new SDSFindFeature(session, nodeid).find(folder)); assertTrue(new SDSFindFeature(session, nodeid).find(sourceFile)); final Path targetFolder = new Path(room, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory)); diff --git a/dracoon/src/test/java/ch/cyberduck/core/worker/DeleteWorkerTest.java b/dracoon/src/test/java/ch/cyberduck/core/worker/DeleteWorkerTest.java index 2788b8b250..347335bed1 100644 --- a/dracoon/src/test/java/ch/cyberduck/core/worker/DeleteWorkerTest.java +++ b/dracoon/src/test/java/ch/cyberduck/core/worker/DeleteWorkerTest.java @@ -25,6 +25,7 @@ import ch.cyberduck.core.exception.NotfoundException; import ch.cyberduck.core.features.Delete; import ch.cyberduck.core.sds.AbstractSDSTest; import ch.cyberduck.core.sds.SDSDeleteFeature; +import ch.cyberduck.core.sds.SDSDirectS3MultipartWriteFeature; import ch.cyberduck.core.sds.SDSDirectoryFeature; import ch.cyberduck.core.sds.SDSFindFeature; import ch.cyberduck.core.sds.SDSNodeIdProvider; @@ -47,13 +48,13 @@ public class DeleteWorkerTest extends AbstractSDSTest { @Test public void testDelete() throws Exception { final SDSNodeIdProvider nodeid = new SDSNodeIdProvider(session); - final Path room = new SDSDirectoryFeature(session, nodeid).mkdir(new Path( + final Path room = new SDSDirectoryFeature(session, nodeid).mkdir(new SDSDirectS3MultipartWriteFeature(session, nodeid), new Path( new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory, Path.Type.volume)), new TransferStatus()); final Path folder = new SDSDirectoryFeature(session, nodeid).mkdir( - new Path(room, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory)), new TransferStatus()); + new SDSDirectS3MultipartWriteFeature(session, nodeid), new Path(room, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory)), new TransferStatus()); assertTrue(new SDSFindFeature(session, nodeid).find(folder)); final Path file = new SDSTouchFeature(session, nodeid).touch( - new Path(folder, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)), new TransferStatus()); + new SDSDirectS3MultipartWriteFeature(session, nodeid), new Path(folder, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)), new TransferStatus()); assertTrue(new SDSFindFeature(session, nodeid).find(file)); final DeleteWorker worker = new DeleteWorker(new DisabledLoginCallback(), Collections.singletonList(folder), new DisabledProgressListener()); int hashCode = worker.hashCode(); diff --git a/dracoon/src/test/java/ch/cyberduck/core/worker/SDSSingleTransferWorkerTest.java b/dracoon/src/test/java/ch/cyberduck/core/worker/SDSSingleTransferWorkerTest.java index c3149242ce..a9a6480634 100644 --- a/dracoon/src/test/java/ch/cyberduck/core/worker/SDSSingleTransferWorkerTest.java +++ b/dracoon/src/test/java/ch/cyberduck/core/worker/SDSSingleTransferWorkerTest.java @@ -37,6 +37,7 @@ import ch.cyberduck.core.sds.AbstractSDSTest; import ch.cyberduck.core.sds.SDSAttributesFinderFeature; import ch.cyberduck.core.sds.SDSDeleteFeature; import ch.cyberduck.core.sds.SDSDirectS3MultipartWriteFeature; +import ch.cyberduck.core.sds.SDSDirectS3WriteFeature; import ch.cyberduck.core.sds.SDSDirectoryFeature; import ch.cyberduck.core.sds.SDSFindFeature; import ch.cyberduck.core.sds.SDSNodeIdProvider; @@ -73,7 +74,7 @@ public class SDSSingleTransferWorkerTest extends AbstractSDSTest { @Test public void testDownloadVersioned() throws Exception { final SDSNodeIdProvider fileid = new SDSNodeIdProvider(session); - final Path room = new SDSDirectoryFeature(session, fileid).mkdir(new Path( + final Path room = new SDSDirectoryFeature(session, fileid).mkdir(new SDSDirectS3WriteFeature(session, fileid), new Path( new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory, Path.Type.volume)), new TransferStatus()); final Path test = new Path(room, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)); final Local localFile = new DefaultTemporaryFileService().create(test.getName()); @@ -117,7 +118,7 @@ public class SDSSingleTransferWorkerTest extends AbstractSDSTest { final SDSNodeIdProvider fileid = new SDSNodeIdProvider(session); final Local folder = new Local(System.getProperty("java.io.tmpdir"), UUID.randomUUID().toString()); new DefaultLocalDirectoryFeature().mkdir(folder); - final Path room = new SDSDirectoryFeature(session, fileid).mkdir(new Path( + final Path room = new SDSDirectoryFeature(session, fileid).mkdir(new SDSDirectS3WriteFeature(session, fileid), new Path( new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory, Path.Type.volume)), new TransferStatus()); final Transfer t = new UploadTransfer(session.getHost(), room, folder); final BytecountStreamListener counter = new BytecountStreamListener(); diff --git a/dropbox/pom.xml b/dropbox/pom.xml index dedfccdad2..83271eaadd 100644 --- a/dropbox/pom.xml +++ b/dropbox/pom.xml @@ -18,7 +18,7 @@ parent ch.cyberduck - 9.2.0-SNAPSHOT + 9.4.0-SNAPSHOT 4.0.0 dropbox diff --git a/dropbox/src/main/java/ch/cyberduck/core/dropbox/DropboxAttributesFinderFeature.java b/dropbox/src/main/java/ch/cyberduck/core/dropbox/DropboxAttributesFinderFeature.java index 7eb465883a..bda814fa05 100644 --- a/dropbox/src/main/java/ch/cyberduck/core/dropbox/DropboxAttributesFinderFeature.java +++ b/dropbox/src/main/java/ch/cyberduck/core/dropbox/DropboxAttributesFinderFeature.java @@ -15,6 +15,7 @@ package ch.cyberduck.core.dropbox; * GNU General Public License for more details. */ +import ch.cyberduck.core.DefaultPathAttributes; import ch.cyberduck.core.ListProgressListener; import ch.cyberduck.core.Path; import ch.cyberduck.core.PathAttributes; @@ -56,7 +57,7 @@ public class DropboxAttributesFinderFeature implements AttributesFinder, Attribu // Retrieve the namespace ID for a users home folder and team root folder final FullAccount account = new DbxUserUsersRequests(session.getClient()).getCurrentAccount(); log.debug("Set root namespace {}", account.getRootInfo().getRootNamespaceId()); - return new PathAttributes().setFileId(account.getRootInfo().getRootNamespaceId()); + return new DefaultPathAttributes().setFileId(account.getRootInfo().getRootNamespaceId()); } final Metadata metadata = new DbxUserFilesRequests(session.getClient(file)).getMetadata(containerService.getKey(file)); if(metadata instanceof FileMetadata) { @@ -78,7 +79,7 @@ public class DropboxAttributesFinderFeature implements AttributesFinder, Attribu @Override public PathAttributes toAttributes(final Metadata metadata) { - final PathAttributes attributes = new PathAttributes(); + final PathAttributes attributes = new DefaultPathAttributes(); if(metadata instanceof FileMetadata) { final FileMetadata file = (FileMetadata) metadata; attributes.setSize(file.getSize()); diff --git a/dropbox/src/main/java/ch/cyberduck/core/dropbox/DropboxBatchDeleteFeature.java b/dropbox/src/main/java/ch/cyberduck/core/dropbox/DropboxBatchDeleteFeature.java index bfed8eacbe..b695d962e8 100644 --- a/dropbox/src/main/java/ch/cyberduck/core/dropbox/DropboxBatchDeleteFeature.java +++ b/dropbox/src/main/java/ch/cyberduck/core/dropbox/DropboxBatchDeleteFeature.java @@ -148,7 +148,7 @@ public class DropboxBatchDeleteFeature implements Delete { } @Override - public EnumSet features() { + public EnumSet features(final Path file) { return EnumSet.of(Flags.recursive); } diff --git a/dropbox/src/main/java/ch/cyberduck/core/dropbox/DropboxDeleteFeature.java b/dropbox/src/main/java/ch/cyberduck/core/dropbox/DropboxDeleteFeature.java index c98ad3dfa9..2474108d00 100644 --- a/dropbox/src/main/java/ch/cyberduck/core/dropbox/DropboxDeleteFeature.java +++ b/dropbox/src/main/java/ch/cyberduck/core/dropbox/DropboxDeleteFeature.java @@ -62,7 +62,7 @@ public class DropboxDeleteFeature implements Delete { } @Override - public EnumSet features() { + public EnumSet features(final Path file) { return EnumSet.of(Flags.recursive); } diff --git a/dropbox/src/main/java/ch/cyberduck/core/dropbox/DropboxDirectoryFeature.java b/dropbox/src/main/java/ch/cyberduck/core/dropbox/DropboxDirectoryFeature.java index 025a60f905..0df75ae462 100644 --- a/dropbox/src/main/java/ch/cyberduck/core/dropbox/DropboxDirectoryFeature.java +++ b/dropbox/src/main/java/ch/cyberduck/core/dropbox/DropboxDirectoryFeature.java @@ -21,6 +21,7 @@ import ch.cyberduck.core.PathContainerService; import ch.cyberduck.core.exception.BackgroundException; import ch.cyberduck.core.exception.InvalidFilenameException; import ch.cyberduck.core.features.Directory; +import ch.cyberduck.core.features.Write; import ch.cyberduck.core.transfer.TransferStatus; import java.text.MessageFormat; @@ -28,8 +29,9 @@ import java.text.MessageFormat; import com.dropbox.core.DbxException; import com.dropbox.core.v2.files.CreateFolderResult; import com.dropbox.core.v2.files.DbxUserFilesRequests; +import com.dropbox.core.v2.files.Metadata; -public class DropboxDirectoryFeature implements Directory { +public class DropboxDirectoryFeature implements Directory { private final DropboxSession session; private final PathContainerService containerService; @@ -40,7 +42,7 @@ public class DropboxDirectoryFeature implements Directory { } @Override - public Path mkdir(final Path folder, final TransferStatus status) throws BackgroundException { + public Path mkdir(final Write writer, final Path folder, final TransferStatus status) throws BackgroundException { try { final CreateFolderResult result = new DbxUserFilesRequests(session.getClient(folder.getParent())) .createFolderV2(containerService.getKey(folder), false); diff --git a/dropbox/src/main/java/ch/cyberduck/core/dropbox/DropboxLockFeature.java b/dropbox/src/main/java/ch/cyberduck/core/dropbox/DropboxLockFeature.java index 12c9a7dccd..1e46337450 100644 --- a/dropbox/src/main/java/ch/cyberduck/core/dropbox/DropboxLockFeature.java +++ b/dropbox/src/main/java/ch/cyberduck/core/dropbox/DropboxLockFeature.java @@ -37,7 +37,7 @@ import com.dropbox.core.v2.files.LockFileResultEntry; import com.dropbox.core.v2.files.UnlockFileArg; public class DropboxLockFeature implements Lock { - private static final Logger log = LogManager.getLogger(DropboxSession.class); + private static final Logger log = LogManager.getLogger(DropboxLockFeature.class); private final DropboxSession session; private final PathContainerService containerService; diff --git a/dropbox/src/main/java/ch/cyberduck/core/dropbox/DropboxProtocol.java b/dropbox/src/main/java/ch/cyberduck/core/dropbox/DropboxProtocol.java index dc797c011d..d743f32727 100644 --- a/dropbox/src/main/java/ch/cyberduck/core/dropbox/DropboxProtocol.java +++ b/dropbox/src/main/java/ch/cyberduck/core/dropbox/DropboxProtocol.java @@ -19,6 +19,9 @@ import ch.cyberduck.core.AbstractProtocol; import ch.cyberduck.core.LocaleFactory; 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 com.google.auto.service.AutoService; @@ -90,4 +93,13 @@ public class DropboxProtocol extends AbstractProtocol { public Case getCaseSensitivity() { return Case.insensitive; } + + @Override + @SuppressWarnings("unchecked") + public T getFeature(final Class type) { + if(type == ComparisonService.class) { + return (T) new DefaultComparisonService(new ChecksumComparisonService(), ComparisonService.disabled); + } + return super.getFeature(type); + } } diff --git a/dropbox/src/main/java/ch/cyberduck/core/dropbox/DropboxSearchFeature.java b/dropbox/src/main/java/ch/cyberduck/core/dropbox/DropboxSearchFeature.java index 7697d0e012..5e72c64230 100644 --- a/dropbox/src/main/java/ch/cyberduck/core/dropbox/DropboxSearchFeature.java +++ b/dropbox/src/main/java/ch/cyberduck/core/dropbox/DropboxSearchFeature.java @@ -94,7 +94,7 @@ public class DropboxSearchFeature implements Search { } @Override - public EnumSet features() { + public EnumSet features(final Path workdir) { return EnumSet.of(Flags.recursive); } diff --git a/dropbox/src/main/java/ch/cyberduck/core/dropbox/DropboxSession.java b/dropbox/src/main/java/ch/cyberduck/core/dropbox/DropboxSession.java index a774eb1bf7..fc008c25ec 100644 --- a/dropbox/src/main/java/ch/cyberduck/core/dropbox/DropboxSession.java +++ b/dropbox/src/main/java/ch/cyberduck/core/dropbox/DropboxSession.java @@ -31,12 +31,10 @@ import ch.cyberduck.core.exception.ConnectionCanceledException; import ch.cyberduck.core.features.*; import ch.cyberduck.core.http.CustomServiceUnavailableRetryStrategy; import ch.cyberduck.core.http.DefaultHttpRateLimiter; -import ch.cyberduck.core.http.ExecutionCountServiceUnavailableRetryStrategy; import ch.cyberduck.core.http.HttpSession; import ch.cyberduck.core.http.RateLimitingHttpRequestInterceptor; import ch.cyberduck.core.oauth.OAuth2ErrorResponseInterceptor; import ch.cyberduck.core.oauth.OAuth2RequestInterceptor; -import ch.cyberduck.core.preferences.HostPreferencesFactory; import ch.cyberduck.core.proxy.ProxyFinder; import ch.cyberduck.core.ssl.X509KeyManager; import ch.cyberduck.core.ssl.X509TrustManager; @@ -82,10 +80,10 @@ public class DropboxSession extends HttpSession { .withParameter("token_access_type", "offline"); configuration.addInterceptorLast(authorizationService); configuration.setServiceUnavailableRetryStrategy(new CustomServiceUnavailableRetryStrategy(host, - new ExecutionCountServiceUnavailableRetryStrategy(new OAuth2ErrorResponseInterceptor(host, authorizationService)))); - if(HostPreferencesFactory.get(host).getBoolean("dropbox.limit.requests.enable")) { + new OAuth2ErrorResponseInterceptor(host, authorizationService))); + if(preferences.getBoolean("dropbox.limit.requests.enable")) { configuration.addInterceptorLast(new RateLimitingHttpRequestInterceptor(new DefaultHttpRateLimiter( - HostPreferencesFactory.get(host).getInteger("dropbox.limit.requests.second") + preferences.getInteger("dropbox.limit.requests.second") ))); } final CloseableHttpClient client = configuration.build(); @@ -97,8 +95,9 @@ public class DropboxSession extends HttpSession { @Override public void login(final LoginCallback prompt, final CancelCallback cancel) throws BackgroundException { + final Credentials credentials = host.getCredentials(); + credentials.setOauth(authorizationService.validate(credentials.getOauth())); try { - final Credentials credentials = authorizationService.validate(); final FullAccount account = new DbxUserUsersRequests(client).getCurrentAccount(); log.debug("Authenticated as user {}", account); credentials.setUsername(account.getEmail()); @@ -125,13 +124,16 @@ public class DropboxSession extends HttpSession { } @Override - protected void logout() throws BackgroundException { + public void disconnect() throws BackgroundException { try { ((DropboxCommonsHttpRequestExecutor) client.getRequestConfig().getHttpRequestor()).close(); } catch(IOException e) { throw new DefaultIOExceptionMappingService().map(e); } + finally { + super.disconnect(); + } } @Override @@ -147,7 +149,7 @@ public class DropboxSession extends HttpSession { return (T) new DropboxWriteFeature(this); } if(type == Upload.class) { - return (T) new DropboxUploadFeature(this, new DropboxWriteFeature(this)); + return (T) new DropboxUploadFeature(this); } if(type == Directory.class) { return (T) new DropboxDirectoryFeature(this); diff --git a/dropbox/src/main/java/ch/cyberduck/core/dropbox/DropboxSharedFoldersListService.java b/dropbox/src/main/java/ch/cyberduck/core/dropbox/DropboxSharedFoldersListService.java index b811274ea6..990551746b 100644 --- a/dropbox/src/main/java/ch/cyberduck/core/dropbox/DropboxSharedFoldersListService.java +++ b/dropbox/src/main/java/ch/cyberduck/core/dropbox/DropboxSharedFoldersListService.java @@ -16,6 +16,7 @@ package ch.cyberduck.core.dropbox; */ import ch.cyberduck.core.AttributedList; +import ch.cyberduck.core.DefaultPathAttributes; import ch.cyberduck.core.ListProgressListener; import ch.cyberduck.core.ListService; import ch.cyberduck.core.Path; @@ -67,7 +68,7 @@ public class DropboxSharedFoldersListService implements ListService { } protected Path parse(final Path directory, final SharedFolderMetadata metadata) { - final PathAttributes attr = new PathAttributes(); + final PathAttributes attr = new DefaultPathAttributes(); return new Path(directory, PathNormalizer.name(metadata.getName()), EnumSet.of(Path.Type.directory, Path.Type.volume, Path.Type.shared), attr); } diff --git a/dropbox/src/main/java/ch/cyberduck/core/dropbox/DropboxThresholdDeleteFeature.java b/dropbox/src/main/java/ch/cyberduck/core/dropbox/DropboxThresholdDeleteFeature.java index caf98c6b6d..f4e80efe9c 100644 --- a/dropbox/src/main/java/ch/cyberduck/core/dropbox/DropboxThresholdDeleteFeature.java +++ b/dropbox/src/main/java/ch/cyberduck/core/dropbox/DropboxThresholdDeleteFeature.java @@ -46,7 +46,7 @@ public class DropboxThresholdDeleteFeature implements Delete { } @Override - public EnumSet features() { + public EnumSet features(final Path file) { return EnumSet.of(Flags.recursive); } diff --git a/dropbox/src/main/java/ch/cyberduck/core/dropbox/DropboxTouchFeature.java b/dropbox/src/main/java/ch/cyberduck/core/dropbox/DropboxTouchFeature.java index 5abfbe3353..7fd223c4b2 100644 --- a/dropbox/src/main/java/ch/cyberduck/core/dropbox/DropboxTouchFeature.java +++ b/dropbox/src/main/java/ch/cyberduck/core/dropbox/DropboxTouchFeature.java @@ -30,7 +30,7 @@ import com.dropbox.core.v2.files.Metadata; public class DropboxTouchFeature extends DefaultTouchFeature { public DropboxTouchFeature(final DropboxSession session) { - super(new DropboxWriteFeature(session)); + super(session); } /** diff --git a/dropbox/src/main/java/ch/cyberduck/core/dropbox/DropboxUploadFeature.java b/dropbox/src/main/java/ch/cyberduck/core/dropbox/DropboxUploadFeature.java index e3986ba4db..15c90469a9 100644 --- a/dropbox/src/main/java/ch/cyberduck/core/dropbox/DropboxUploadFeature.java +++ b/dropbox/src/main/java/ch/cyberduck/core/dropbox/DropboxUploadFeature.java @@ -39,8 +39,7 @@ public class DropboxUploadFeature extends HttpUploadFeature(session, new DisabledBulkFeature(), new DropboxDeleteFeature(session), cryptomator).pre(Transfer.Type.upload, Collections.singletonMap(new TransferItem(source), status), new DisabledConnectionCallback()); + new CryptoBulkFeature<>(session, new DisabledBulkFeature(), 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 DropboxWriteFeature(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()); @@ -106,10 +108,11 @@ public class CopyWorkerTest extends AbstractDropboxTest { cryptomator.create(session, new VaultCredentials("test"), vaultVersion); final DefaultVaultRegistry registry = new DefaultVaultRegistry(new DisabledPasswordCallback(), cryptomator); session.withRegistry(registry); - new CryptoTouchFeature<>(session, new DefaultTouchFeature<>(new DropboxWriteFeature(session) - ), new DropboxWriteFeature(session), cryptomator).touch(source, new TransferStatus()); + new CryptoTouchFeature<>(session, new DefaultTouchFeature( + session), cryptomator).touch(new CryptoWriteFeature<>(session, new DropboxWriteFeature(session), cryptomator), source, new TransferStatus()); assertTrue(cryptomator.getFeature(session, Find.class, new DefaultFindFeature(session)).find(source)); - cryptomator.getFeature(session, Directory.class, new DropboxDirectoryFeature(session)).mkdir(targetFolder, new TransferStatus()); + cryptomator.getFeature(session, Directory.class, new DropboxDirectoryFeature(session)).mkdir( + new CryptoWriteFeature<>(session, new DropboxWriteFeature(session), cryptomator), 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); @@ -130,10 +133,11 @@ public class CopyWorkerTest extends AbstractDropboxTest { cryptomator.create(session, new VaultCredentials("test"), vaultVersion); final DefaultVaultRegistry registry = new DefaultVaultRegistry(new DisabledPasswordCallback(), cryptomator); session.withRegistry(registry); - new CryptoTouchFeature<>(session, new DefaultTouchFeature<>(new DropboxWriteFeature(session) - ), new DropboxWriteFeature(session), cryptomator).touch(source, new TransferStatus()); + new CryptoTouchFeature<>(session, new DefaultTouchFeature( + session), cryptomator).touch(new CryptoWriteFeature<>(session, new DropboxWriteFeature(session), cryptomator), source, new TransferStatus()); assertTrue(cryptomator.getFeature(session, Find.class, new DefaultFindFeature(session)).find(source)); - cryptomator.getFeature(session, Directory.class, new DropboxDirectoryFeature(session)).mkdir(targetFolder, new TransferStatus()); + cryptomator.getFeature(session, Directory.class, new DropboxDirectoryFeature(session)).mkdir( + new CryptoWriteFeature<>(session, new DropboxWriteFeature(session), cryptomator), 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); @@ -152,10 +156,11 @@ public class CopyWorkerTest extends AbstractDropboxTest { cryptomator.create(session, new VaultCredentials("test"), vaultVersion); final DefaultVaultRegistry registry = new DefaultVaultRegistry(new DisabledPasswordCallback(), cryptomator); session.withRegistry(registry); - cryptomator.getFeature(session, Directory.class, new DropboxDirectoryFeature(session)).mkdir(folder, new TransferStatus()); + cryptomator.getFeature(session, Directory.class, new DropboxDirectoryFeature(session)).mkdir( + new CryptoWriteFeature<>(session, new DropboxWriteFeature(session), cryptomator), folder, new TransferStatus()); assertTrue(cryptomator.getFeature(session, Find.class, new DefaultFindFeature(session)).find(folder)); - new CryptoTouchFeature<>(session, new DefaultTouchFeature<>(new DropboxWriteFeature(session) - ), new DropboxWriteFeature(session), cryptomator).touch(file, new TransferStatus()); + new CryptoTouchFeature<>(session, new DefaultTouchFeature( + session), cryptomator).touch(new CryptoWriteFeature<>(session, new DropboxWriteFeature(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)); @@ -178,7 +183,7 @@ public class CopyWorkerTest extends AbstractDropboxTest { final Path home = new DefaultHomeFinderService(session).find(); 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)); - new DropboxTouchFeature(session).touch(cleartextFile, new TransferStatus()); + new DropboxTouchFeature(session).touch(new DropboxWriteFeature(session), cleartextFile, new TransferStatus()); assertTrue(new DropboxFindFeature(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)); @@ -186,7 +191,8 @@ public class CopyWorkerTest extends AbstractDropboxTest { cryptomator.create(session, new VaultCredentials("test"), vaultVersion); final DefaultVaultRegistry registry = new DefaultVaultRegistry(new DisabledPasswordCallback(), cryptomator); session.withRegistry(registry); - cryptomator.getFeature(session, Directory.class, new DropboxDirectoryFeature(session)).mkdir(encryptedFolder, new TransferStatus()); + cryptomator.getFeature(session, Directory.class, new DropboxDirectoryFeature(session)).mkdir( + cryptomator.getFeature(session, Write.class, new DropboxWriteFeature(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()); @@ -203,8 +209,8 @@ public class CopyWorkerTest extends AbstractDropboxTest { 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 DropboxDirectoryFeature(session).mkdir(cleartextFolder, new TransferStatus()); - new DropboxTouchFeature(session).touch(cleartextFile, new TransferStatus()); + new DropboxDirectoryFeature(session).mkdir(new DropboxWriteFeature(session), cleartextFolder, new TransferStatus()); + new DropboxTouchFeature(session).touch(new DropboxWriteFeature(session), cleartextFile, new TransferStatus()); assertTrue(new DropboxFindFeature(session).find(cleartextFolder)); assertTrue(new DropboxFindFeature(session).find(cleartextFile)); final CryptoVault cryptomator = new CryptoVault(vault); @@ -229,17 +235,18 @@ public class CopyWorkerTest extends AbstractDropboxTest { final Path home = new DefaultHomeFinderService(session).find(); 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 DropboxDirectoryFeature(session).mkdir(clearFolder, new TransferStatus()); + new DropboxDirectoryFeature(session).mkdir(new DropboxWriteFeature(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 DropboxDirectoryFeature(session)).mkdir(encryptedFolder, new TransferStatus()); + cryptomator.getFeature(session, Directory.class, new DropboxDirectoryFeature(session)).mkdir( + cryptomator.getFeature(session, Write.class, new DropboxWriteFeature(session)), encryptedFolder, new TransferStatus()); assertTrue(cryptomator.getFeature(session, Find.class, new DefaultFindFeature(session)).find(encryptedFolder)); - new CryptoTouchFeature<>(session, new DefaultTouchFeature<>(new DropboxWriteFeature(session) - ), new DropboxWriteFeature(session), cryptomator).touch(encryptedFile, new TransferStatus()); + new CryptoTouchFeature<>(session, new DefaultTouchFeature( + session), cryptomator).touch(new CryptoWriteFeature<>(session, new DropboxWriteFeature(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)); @@ -261,10 +268,11 @@ public class CopyWorkerTest extends AbstractDropboxTest { cryptomator.create(session, new VaultCredentials("test"), vaultVersion); final DefaultVaultRegistry registry = new DefaultVaultRegistry(new DisabledPasswordCallback(), cryptomator); session.withRegistry(registry); - cryptomator.getFeature(session, Directory.class, new DropboxDirectoryFeature(session)).mkdir(encryptedFolder, new TransferStatus()); + cryptomator.getFeature(session, Directory.class, new DropboxDirectoryFeature(session)).mkdir( + cryptomator.getFeature(session, Write.class, new DropboxWriteFeature(session)), encryptedFolder, new TransferStatus()); assertTrue(cryptomator.getFeature(session, Find.class, new DefaultFindFeature(session)).find(encryptedFolder)); - new CryptoTouchFeature<>(session, new DefaultTouchFeature<>(new DropboxWriteFeature(session) - ), new DropboxWriteFeature(session), cryptomator).touch(encryptedFile, new TransferStatus()); + new CryptoTouchFeature<>(session, new DefaultTouchFeature( + session), cryptomator).touch(new CryptoWriteFeature<>(session, new DropboxWriteFeature(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)); diff --git a/dropbox/src/test/java/ch/cyberduck/core/cryptomator/DropboxDirectoryFeatureTest.java b/dropbox/src/test/java/ch/cyberduck/core/cryptomator/DropboxDirectoryFeatureTest.java index 438d6d8c03..3b6fa9869f 100644 --- a/dropbox/src/test/java/ch/cyberduck/core/cryptomator/DropboxDirectoryFeatureTest.java +++ b/dropbox/src/test/java/ch/cyberduck/core/cryptomator/DropboxDirectoryFeatureTest.java @@ -24,10 +24,12 @@ import ch.cyberduck.core.PathAttributes; import ch.cyberduck.core.dropbox.DropboxAttributesFinderFeature; import ch.cyberduck.core.dropbox.DropboxDeleteFeature; import ch.cyberduck.core.dropbox.DropboxDirectoryFeature; +import ch.cyberduck.core.dropbox.DropboxWriteFeature; 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.shared.DefaultHomeFinderService; import ch.cyberduck.core.transfer.TransferStatus; @@ -59,7 +61,7 @@ public class DropboxDirectoryFeatureTest extends AbstractDropboxTest { cryptomator.create(session, new VaultCredentials("test"), vaultVersion); session.withRegistry(new DefaultVaultRegistry(new DisabledPasswordCallback(), cryptomator)); final Path test = cryptomator.getFeature(session, Directory.class, new DropboxDirectoryFeature(session)).mkdir( - new Path(vault, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory)), new TransferStatus()); + cryptomator.getFeature(session, Write.class, new DropboxWriteFeature(session)), new Path(vault, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory)), new TransferStatus()); assertTrue(cryptomator.getFeature(session, Find.class, new DefaultFindFeature(session)).find(test)); final PathAttributes attributes = cryptomator.getFeature(session, AttributesFinder.class, new DropboxAttributesFinderFeature(session)).find(test); assertEquals(test.attributes().getSize(), attributes.getSize()); @@ -75,7 +77,8 @@ public class DropboxDirectoryFeatureTest extends AbstractDropboxTest { final CryptoVault cryptomator = new CryptoVault(vault); cryptomator.create(session, new VaultCredentials("test"), vaultVersion); session.withRegistry(new DefaultVaultRegistry(new DisabledPasswordCallback(), cryptomator)); - cryptomator.getFeature(session, Directory.class, new DropboxDirectoryFeature(session)).mkdir(test, new TransferStatus()); + cryptomator.getFeature(session, Directory.class, new DropboxDirectoryFeature(session)).mkdir( + cryptomator.getFeature(session, Write.class, new DropboxWriteFeature(session)), test, new TransferStatus()); assertTrue(cryptomator.getFeature(session, Find.class, new DefaultFindFeature(session)).find(test)); cryptomator.getFeature(session, Delete.class, new DropboxDeleteFeature(session)).delete(Arrays.asList(test, vault), new DisabledLoginCallback(), new Delete.DisabledCallback()); } diff --git a/dropbox/src/test/java/ch/cyberduck/core/cryptomator/DropboxListServiceTest.java b/dropbox/src/test/java/ch/cyberduck/core/cryptomator/DropboxListServiceTest.java index 09fb57ab86..ef2ce97f56 100644 --- a/dropbox/src/test/java/ch/cyberduck/core/cryptomator/DropboxListServiceTest.java +++ b/dropbox/src/test/java/ch/cyberduck/core/cryptomator/DropboxListServiceTest.java @@ -23,6 +23,7 @@ import ch.cyberduck.core.DisabledPasswordCallback; import ch.cyberduck.core.Path; import ch.cyberduck.core.cryptomator.features.CryptoListService; import ch.cyberduck.core.cryptomator.features.CryptoTouchFeature; +import ch.cyberduck.core.cryptomator.features.CryptoWriteFeature; import ch.cyberduck.core.dropbox.DropboxDeleteFeature; import ch.cyberduck.core.dropbox.DropboxListService; import ch.cyberduck.core.dropbox.DropboxWriteFeature; @@ -42,6 +43,8 @@ import org.junit.runners.Parameterized; import java.util.Arrays; import java.util.EnumSet; +import com.dropbox.core.v2.files.Metadata; + import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertTrue; @@ -57,8 +60,9 @@ public class DropboxListServiceTest extends AbstractDropboxTest { cryptomator.create(session, new VaultCredentials("test"), vaultVersion); session.withRegistry(new DefaultVaultRegistry(new DisabledPasswordCallback(), cryptomator)); assertTrue(new CryptoListService(session, new DropboxListService(session), cryptomator).list(vault, new DisabledListProgressListener()).isEmpty()); - final Path test = new CryptoTouchFeature<>(session, new DefaultTouchFeature<>(new DropboxWriteFeature(session) - ), new DropboxWriteFeature(session), cryptomator).touch(new Path(vault, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)), new TransferStatus()); + final Path test = new CryptoTouchFeature<>(session, new DefaultTouchFeature( + session), cryptomator).touch( + new CryptoWriteFeature<>(session, new DropboxWriteFeature(session), cryptomator), new Path(vault, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)), new TransferStatus()); assertEquals(test, new CryptoListService(session, new DropboxListService(session), cryptomator).list(vault, new DisabledListProgressListener()).get(0)); cryptomator.getFeature(session, Delete.class, new DropboxDeleteFeature(session)).delete(Arrays.asList(test, vault), new DisabledLoginCallback(), new Delete.DisabledCallback()); } diff --git a/dropbox/src/test/java/ch/cyberduck/core/cryptomator/DropboxMoveFeatureTest.java b/dropbox/src/test/java/ch/cyberduck/core/cryptomator/DropboxMoveFeatureTest.java index c188f3f344..d40ecf497a 100644 --- a/dropbox/src/test/java/ch/cyberduck/core/cryptomator/DropboxMoveFeatureTest.java +++ b/dropbox/src/test/java/ch/cyberduck/core/cryptomator/DropboxMoveFeatureTest.java @@ -22,6 +22,7 @@ import ch.cyberduck.core.DisabledLoginCallback; import ch.cyberduck.core.DisabledPasswordCallback; import ch.cyberduck.core.Path; import ch.cyberduck.core.cryptomator.features.CryptoTouchFeature; +import ch.cyberduck.core.cryptomator.features.CryptoWriteFeature; import ch.cyberduck.core.dropbox.DropboxDeleteFeature; import ch.cyberduck.core.dropbox.DropboxDirectoryFeature; import ch.cyberduck.core.dropbox.DropboxFindFeature; @@ -31,6 +32,7 @@ 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.shared.DefaultHomeFinderService; import ch.cyberduck.core.shared.DefaultTouchFeature; @@ -47,6 +49,8 @@ import org.junit.runners.Parameterized; import java.util.Arrays; import java.util.EnumSet; +import com.dropbox.core.v2.files.Metadata; + import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertTrue; @@ -63,9 +67,10 @@ public class DropboxMoveFeatureTest extends AbstractDropboxTest { final CryptoVault cryptomator = new CryptoVault(vault); cryptomator.create(session, new VaultCredentials("test"), vaultVersion); session.withRegistry(new DefaultVaultRegistry(new DisabledPasswordCallback(), cryptomator)); - cryptomator.getFeature(session, Directory.class, new DropboxDirectoryFeature(session)).mkdir(folder, new TransferStatus()); - new CryptoTouchFeature<>(session, new DefaultTouchFeature<>(new DropboxWriteFeature(session) - ), new DropboxWriteFeature(session), cryptomator).touch(file, new TransferStatus()); + cryptomator.getFeature(session, Directory.class, new DropboxDirectoryFeature(session)).mkdir( + cryptomator.getFeature(session, Write.class, new DropboxWriteFeature(session)), folder, new TransferStatus()); + new CryptoTouchFeature<>(session, new DefaultTouchFeature( + session), cryptomator).touch(new CryptoWriteFeature<>(session, new DropboxWriteFeature(session), cryptomator), file, new TransferStatus()); assertTrue(cryptomator.getFeature(session, Find.class, new DefaultFindFeature(session)).find(file)); final Move move = cryptomator.getFeature(session, Move.class, new DropboxMoveFeature(session)); // rename file diff --git a/dropbox/src/test/java/ch/cyberduck/core/cryptomator/DropboxTouchFeatureTest.java b/dropbox/src/test/java/ch/cyberduck/core/cryptomator/DropboxTouchFeatureTest.java index 5551e0417b..1c7bfd7cc3 100644 --- a/dropbox/src/test/java/ch/cyberduck/core/cryptomator/DropboxTouchFeatureTest.java +++ b/dropbox/src/test/java/ch/cyberduck/core/cryptomator/DropboxTouchFeatureTest.java @@ -21,6 +21,7 @@ import ch.cyberduck.core.DisabledLoginCallback; import ch.cyberduck.core.DisabledPasswordCallback; import ch.cyberduck.core.Path; import ch.cyberduck.core.cryptomator.features.CryptoTouchFeature; +import ch.cyberduck.core.cryptomator.features.CryptoWriteFeature; import ch.cyberduck.core.dropbox.DropboxAttributesFinderFeature; import ch.cyberduck.core.dropbox.DropboxDeleteFeature; import ch.cyberduck.core.dropbox.DropboxFindFeature; @@ -44,6 +45,8 @@ import org.junit.runners.Parameterized; import java.util.Arrays; import java.util.EnumSet; +import com.dropbox.core.v2.files.Metadata; + import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertTrue; import static org.junit.Assume.assumeTrue; @@ -61,8 +64,8 @@ public class DropboxTouchFeatureTest extends AbstractDropboxTest { 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 DefaultTouchFeature<>(new DropboxWriteFeature(session)), new DropboxWriteFeature(session), cryptomator).touch( - new Path(vault, new AlphanumericRandomStringService(130).random(), EnumSet.of(Path.Type.file)), status); + final Path test = new CryptoTouchFeature<>(session, new DefaultTouchFeature(session), cryptomator).touch( + new CryptoWriteFeature<>(session, new DropboxWriteFeature(session), cryptomator), new Path(vault, new AlphanumericRandomStringService(130).random(), EnumSet.of(Path.Type.file)), status); assertEquals(0L, test.attributes().getSize()); assertEquals(0L, status.getResponse().getSize()); assertTrue(cryptomator.getFeature(session, Find.class, new DropboxFindFeature(session)).find(test)); @@ -79,8 +82,8 @@ public class DropboxTouchFeatureTest extends AbstractDropboxTest { 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 DefaultTouchFeature<>(new DropboxWriteFeature(session)), new DropboxWriteFeature(session), cryptomator).touch( - new Path(vault, new AlphanumericRandomStringService(130).random(), EnumSet.of(Path.Type.file)), status); + final Path test = new CryptoTouchFeature<>(session, new DefaultTouchFeature(session), cryptomator).touch( + new CryptoWriteFeature<>(session, new DropboxWriteFeature(session), cryptomator), new Path(vault, new AlphanumericRandomStringService(130).random(), EnumSet.of(Path.Type.file)), status); assertEquals(0L, test.attributes().getSize()); assertEquals(0L, status.getResponse().getSize()); assertTrue(cryptomator.getFeature(session, Find.class, new DefaultFindFeature(session)).find(test)); diff --git a/dropbox/src/test/java/ch/cyberduck/core/cryptomator/MoveWorkerTest.java b/dropbox/src/test/java/ch/cyberduck/core/cryptomator/MoveWorkerTest.java index c5cf50d505..47f1208e21 100644 --- a/dropbox/src/test/java/ch/cyberduck/core/cryptomator/MoveWorkerTest.java +++ b/dropbox/src/test/java/ch/cyberduck/core/cryptomator/MoveWorkerTest.java @@ -25,6 +25,7 @@ import ch.cyberduck.core.Path; import ch.cyberduck.core.PathCache; import ch.cyberduck.core.cryptomator.features.CryptoListService; import ch.cyberduck.core.cryptomator.features.CryptoTouchFeature; +import ch.cyberduck.core.cryptomator.features.CryptoWriteFeature; import ch.cyberduck.core.dropbox.DropboxDeleteFeature; import ch.cyberduck.core.dropbox.DropboxDirectoryFeature; import ch.cyberduck.core.dropbox.DropboxListService; @@ -34,6 +35,7 @@ import ch.cyberduck.core.exception.NotfoundException; 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.pool.SessionPool; import ch.cyberduck.core.shared.DefaultFindFeature; import ch.cyberduck.core.shared.DefaultHomeFinderService; @@ -68,7 +70,8 @@ public class MoveWorkerTest extends AbstractDropboxTest { final CryptoVault cryptomator = new CryptoVault(vault); cryptomator.create(session, new VaultCredentials("test"), vaultVersion); session.withRegistry(new DefaultVaultRegistry(new DisabledPasswordCallback(), cryptomator)); - new CryptoTouchFeature<>(session, new DropboxTouchFeature(session), new DropboxWriteFeature(session), cryptomator).touch(source, new TransferStatus()); + new CryptoTouchFeature<>(session, new DropboxTouchFeature(session), cryptomator).touch( + new CryptoWriteFeature<>(session, new DropboxWriteFeature(session), cryptomator), source, new TransferStatus()); assertTrue(cryptomator.getFeature(session, Find.class, new DefaultFindFeature(session)).find(source)); final MoveWorker worker = new MoveWorker(Collections.singletonMap(source, target), new SessionPool.SingleSessionPool(session), PathCache.empty(), new DisabledProgressListener(), new DisabledLoginCallback()); worker.run(session); @@ -87,9 +90,11 @@ public class MoveWorkerTest extends AbstractDropboxTest { final CryptoVault cryptomator = new CryptoVault(vault); cryptomator.create(session, new VaultCredentials("test"), vaultVersion); session.withRegistry(new DefaultVaultRegistry(new DisabledPasswordCallback(), cryptomator)); - new CryptoTouchFeature<>(session, new DropboxTouchFeature(session), new DropboxWriteFeature(session), cryptomator).touch(source, new TransferStatus()); + new CryptoTouchFeature<>(session, new DropboxTouchFeature(session), cryptomator).touch( + new CryptoWriteFeature<>(session, new DropboxWriteFeature(session), cryptomator), source, new TransferStatus()); assertTrue(cryptomator.getFeature(session, Find.class, new DefaultFindFeature(session)).find(source)); - cryptomator.getFeature(session, Directory.class, new DropboxDirectoryFeature(session)).mkdir(targetFolder, new TransferStatus()); + cryptomator.getFeature(session, Directory.class, new DropboxDirectoryFeature(session)).mkdir( + cryptomator.getFeature(session, Write.class, new DropboxWriteFeature(session)), targetFolder, new TransferStatus()); assertTrue(cryptomator.getFeature(session, Find.class, new DefaultFindFeature(session)).find(targetFolder)); final MoveWorker worker = new MoveWorker(Collections.singletonMap(source, target), new SessionPool.SingleSessionPool(session), PathCache.empty(), new DisabledProgressListener(), new DisabledLoginCallback()); worker.run(session); @@ -109,9 +114,11 @@ public class MoveWorkerTest extends AbstractDropboxTest { final CryptoVault cryptomator = new CryptoVault(vault); cryptomator.create(session, new VaultCredentials("test"), vaultVersion); session.withRegistry(new DefaultVaultRegistry(new DisabledPasswordCallback(), cryptomator)); - new CryptoTouchFeature<>(session, new DropboxTouchFeature(session), new DropboxWriteFeature(session), cryptomator).touch(source, new TransferStatus()); + new CryptoTouchFeature<>(session, new DropboxTouchFeature(session), cryptomator).touch( + new CryptoWriteFeature<>(session, new DropboxWriteFeature(session), cryptomator), source, new TransferStatus()); assertTrue(cryptomator.getFeature(session, Find.class, new DefaultFindFeature(session)).find(source)); - cryptomator.getFeature(session, Directory.class, new DropboxDirectoryFeature(session)).mkdir(targetFolder, new TransferStatus()); + cryptomator.getFeature(session, Directory.class, new DropboxDirectoryFeature(session)).mkdir( + cryptomator.getFeature(session, Write.class, new DropboxWriteFeature(session)), targetFolder, new TransferStatus()); assertTrue(cryptomator.getFeature(session, Find.class, new DefaultFindFeature(session)).find(targetFolder)); final MoveWorker worker = new MoveWorker(Collections.singletonMap(source, target), new SessionPool.SingleSessionPool(session), PathCache.empty(), new DisabledProgressListener(), new DisabledLoginCallback()); worker.run(session); @@ -129,9 +136,11 @@ public class MoveWorkerTest extends AbstractDropboxTest { final CryptoVault cryptomator = new CryptoVault(vault); cryptomator.create(session, new VaultCredentials("test"), vaultVersion); session.withRegistry(new DefaultVaultRegistry(new DisabledPasswordCallback(), cryptomator)); - cryptomator.getFeature(session, Directory.class, new DropboxDirectoryFeature(session)).mkdir(folder, new TransferStatus()); + cryptomator.getFeature(session, Directory.class, new DropboxDirectoryFeature(session)).mkdir( + cryptomator.getFeature(session, Write.class, new DropboxWriteFeature(session)), folder, new TransferStatus()); assertTrue(cryptomator.getFeature(session, Find.class, new DefaultFindFeature(session)).find(folder)); - new CryptoTouchFeature<>(session, new DropboxTouchFeature(session), new DropboxWriteFeature(session), cryptomator).touch(file, new TransferStatus()); + new CryptoTouchFeature<>(session, new DropboxTouchFeature(session), cryptomator).touch( + new CryptoWriteFeature<>(session, new DropboxWriteFeature(session), cryptomator), file, new TransferStatus()); assertTrue(cryptomator.getFeature(session, Find.class, new DefaultFindFeature(session)).find(file)); // rename file final Path fileRenamed = new Path(folder, "f1", EnumSet.of(Path.Type.file)); @@ -160,7 +169,7 @@ public class MoveWorkerTest extends AbstractDropboxTest { final Path home = new DefaultHomeFinderService(session).find(); final Path vault = new Path(home, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory)); final Path clearFile = new Path(home, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)); - new DropboxTouchFeature(session).touch(clearFile, new TransferStatus()); + new DropboxTouchFeature(session).touch(new DropboxWriteFeature(session), clearFile, new TransferStatus()); assertTrue(new DefaultFindFeature(session).find(clearFile)); 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)); @@ -168,7 +177,8 @@ public class MoveWorkerTest extends AbstractDropboxTest { cryptomator.create(session, new VaultCredentials("test"), vaultVersion); final DefaultVaultRegistry registry = new DefaultVaultRegistry(new DisabledPasswordCallback(), cryptomator); session.withRegistry(registry); - cryptomator.getFeature(session, Directory.class, new DropboxDirectoryFeature(session)).mkdir(encryptedFolder, new TransferStatus()); + cryptomator.getFeature(session, Directory.class, new DropboxDirectoryFeature(session)).mkdir( + cryptomator.getFeature(session, Write.class, new DropboxWriteFeature(session)), encryptedFolder, new TransferStatus()); assertTrue(cryptomator.getFeature(session, Find.class, new DefaultFindFeature(session)).find(encryptedFolder)); // move file into vault final MoveWorker worker = new MoveWorker(Collections.singletonMap(clearFile, encryptedFile), new SessionPool.SingleSessionPool(session), PathCache.empty(), new DisabledProgressListener(), new DisabledLoginCallback()); @@ -185,8 +195,8 @@ public class MoveWorkerTest extends AbstractDropboxTest { 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)); final Path clearFile = new Path(clearFolder, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)); - new DropboxDirectoryFeature(session).mkdir(clearFolder, new TransferStatus()); - new DropboxTouchFeature(session).touch(clearFile, new TransferStatus()); + new DropboxDirectoryFeature(session).mkdir(new DropboxWriteFeature(session), clearFolder, new TransferStatus()); + new DropboxTouchFeature(session).touch(new DropboxWriteFeature(session), clearFile, new TransferStatus()); assertTrue(new DefaultFindFeature(session).find(clearFolder)); assertTrue(new DefaultFindFeature(session).find(clearFile)); final CryptoVault cryptomator = new CryptoVault(vault); @@ -211,16 +221,18 @@ public class MoveWorkerTest extends AbstractDropboxTest { final Path home = new DefaultHomeFinderService(session).find(); 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 DropboxDirectoryFeature(session).mkdir(clearFolder, new TransferStatus()); + new DropboxDirectoryFeature(session).mkdir(new DropboxWriteFeature(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 DropboxDirectoryFeature(session)).mkdir(encryptedFolder, new TransferStatus()); + cryptomator.getFeature(session, Directory.class, new DropboxDirectoryFeature(session)).mkdir( + cryptomator.getFeature(session, Write.class, new DropboxWriteFeature(session)), encryptedFolder, new TransferStatus()); assertTrue(cryptomator.getFeature(session, Find.class, new DefaultFindFeature(session)).find(encryptedFolder)); - new CryptoTouchFeature<>(session, new DropboxTouchFeature(session), new DropboxWriteFeature(session), cryptomator).touch(encryptedFile, new TransferStatus()); + new CryptoTouchFeature<>(session, new DropboxTouchFeature(session), cryptomator).touch( + new CryptoWriteFeature<>(session, new DropboxWriteFeature(session), cryptomator), encryptedFile, new TransferStatus()); assertTrue(cryptomator.getFeature(session, Find.class, new DefaultFindFeature(session)).find(encryptedFile)); // move file outside vault final Path fileRenamed = new Path(clearFolder, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)); @@ -243,9 +255,11 @@ public class MoveWorkerTest extends AbstractDropboxTest { cryptomator.create(session, new VaultCredentials("test"), vaultVersion); final DefaultVaultRegistry registry = new DefaultVaultRegistry(new DisabledPasswordCallback(), cryptomator); session.withRegistry(registry); - cryptomator.getFeature(session, Directory.class, new DropboxDirectoryFeature(session)).mkdir(encryptedFolder, new TransferStatus()); + cryptomator.getFeature(session, Directory.class, new DropboxDirectoryFeature(session)).mkdir( + cryptomator.getFeature(session, Write.class, new DropboxWriteFeature(session)), encryptedFolder, new TransferStatus()); assertTrue(cryptomator.getFeature(session, Find.class, new DefaultFindFeature(session)).find(encryptedFolder)); - new CryptoTouchFeature<>(session, new DropboxTouchFeature(session), new DropboxWriteFeature(session), cryptomator).touch(encryptedFile, new TransferStatus()); + new CryptoTouchFeature<>(session, new DropboxTouchFeature(session), cryptomator).touch( + new CryptoWriteFeature<>(session, new DropboxWriteFeature(session), cryptomator), encryptedFile, new TransferStatus()); assertTrue(cryptomator.getFeature(session, Find.class, new DefaultFindFeature(session)).find(encryptedFile)); // move directory outside vault final Path directoryRenamed = new Path(home, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory)); diff --git a/dropbox/src/test/java/ch/cyberduck/core/dropbox/DropboxAttributesFinderFeatureTest.java b/dropbox/src/test/java/ch/cyberduck/core/dropbox/DropboxAttributesFinderFeatureTest.java index 7bf7af5f17..bc4b629159 100644 --- a/dropbox/src/test/java/ch/cyberduck/core/dropbox/DropboxAttributesFinderFeatureTest.java +++ b/dropbox/src/test/java/ch/cyberduck/core/dropbox/DropboxAttributesFinderFeatureTest.java @@ -39,12 +39,12 @@ public class DropboxAttributesFinderFeatureTest extends AbstractDropboxTest { @Test public void testFindFile() throws Exception { final Path root = new DefaultHomeFinderService(session).find(); - final Path folder = new DropboxDirectoryFeature(session).mkdir(new Path(root, + final Path folder = new DropboxDirectoryFeature(session).mkdir(new DropboxWriteFeature(session), new Path(root, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory)), null); final DropboxAttributesFinderFeature f = new DropboxAttributesFinderFeature(session); assertEquals(-1L, f.find(folder).getModificationDate()); final Path file = new Path(folder, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)); - new DropboxTouchFeature(session).touch(file, new TransferStatus()); + new DropboxTouchFeature(session).touch(new DropboxWriteFeature(session), file, new TransferStatus()); final PathAttributes attr = f.find(file); assertEquals("e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", attr.getChecksum().hash); assertNotEquals(-1L, attr.getModificationDate()); diff --git a/dropbox/src/test/java/ch/cyberduck/core/dropbox/DropboxBatchDeleteFeatureTest.java b/dropbox/src/test/java/ch/cyberduck/core/dropbox/DropboxBatchDeleteFeatureTest.java index 7af56f30ed..4d55c3da97 100644 --- a/dropbox/src/test/java/ch/cyberduck/core/dropbox/DropboxBatchDeleteFeatureTest.java +++ b/dropbox/src/test/java/ch/cyberduck/core/dropbox/DropboxBatchDeleteFeatureTest.java @@ -46,9 +46,9 @@ public class DropboxBatchDeleteFeatureTest extends AbstractDropboxTest { @Test public void testDeleteFiles() throws Exception { final Path file1 = new DropboxTouchFeature(session).touch( - new Path(new DefaultHomeFinderService(session).find(), new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)), new TransferStatus()); + new DropboxWriteFeature(session), new Path(new DefaultHomeFinderService(session).find(), new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)), new TransferStatus()); final Path file2 = new DropboxTouchFeature(session).touch( - new Path(new DefaultHomeFinderService(session).find(), new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)), new TransferStatus()); + new DropboxWriteFeature(session), new Path(new DefaultHomeFinderService(session).find(), new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)), new TransferStatus()); new DropboxBatchDeleteFeature(session).delete(Arrays.asList(file1, file2), new DisabledLoginCallback(), new Delete.DisabledCallback()); assertFalse(new DropboxFindFeature(session).find(file1)); assertFalse(new DropboxFindFeature(session).find(file2)); @@ -58,11 +58,11 @@ public class DropboxBatchDeleteFeatureTest extends AbstractDropboxTest { @Test public void testDeleteDirectory() throws Exception { final Path folder = new DropboxDirectoryFeature(session).mkdir( - new Path(new DefaultHomeFinderService(session).find(), new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.volume, Path.Type.directory)), new TransferStatus()); + new DropboxWriteFeature(session), new Path(new DefaultHomeFinderService(session).find(), new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.volume, Path.Type.directory)), new TransferStatus()); final Path file1 = new DropboxTouchFeature(session).touch( - new Path(folder, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)), new TransferStatus()); + new DropboxWriteFeature(session), new Path(folder, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)), new TransferStatus()); final Path file2 = new DropboxTouchFeature(session).touch( - new Path(folder, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)), new TransferStatus()); + new DropboxWriteFeature(session), new Path(folder, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)), new TransferStatus()); new DropboxBatchDeleteFeature(session).delete(Collections.singletonList(folder), new DisabledLoginCallback(), new Delete.DisabledCallback()); assertFalse(new DropboxFindFeature(session).find(file1)); assertFalse(new DropboxFindFeature(session).find(file2)); diff --git a/dropbox/src/test/java/ch/cyberduck/core/dropbox/DropboxCopyFeatureTest.java b/dropbox/src/test/java/ch/cyberduck/core/dropbox/DropboxCopyFeatureTest.java index 8f57a7399a..45d77f56e6 100644 --- a/dropbox/src/test/java/ch/cyberduck/core/dropbox/DropboxCopyFeatureTest.java +++ b/dropbox/src/test/java/ch/cyberduck/core/dropbox/DropboxCopyFeatureTest.java @@ -50,7 +50,7 @@ public class DropboxCopyFeatureTest extends AbstractDropboxTest { public void testCopyFile() throws Exception { final Path file = new Path(new DefaultHomeFinderService(session).find(), new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)); final Path target = new Path(new DefaultHomeFinderService(session).find(), new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)); - new DropboxTouchFeature(session).touch(file, new TransferStatus()); + new DropboxTouchFeature(session).touch(new DropboxWriteFeature(session), file, new TransferStatus()); assertTrue(new DropboxFindFeature(session).find(file)); final Path copy = new DropboxCopyFeature(session).copy(file, target, new TransferStatus(), new DisabledConnectionCallback(), new DisabledStreamListener()); assertNotEquals(PathAttributes.EMPTY, copy.attributes()); @@ -62,11 +62,11 @@ public class DropboxCopyFeatureTest extends AbstractDropboxTest { @Test public void testCopyToExistingFile() throws Exception { final Path folder = new DropboxDirectoryFeature(session).mkdir( - new Path(new DefaultHomeFinderService(session).find(), new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory)), new TransferStatus()); + new DropboxWriteFeature(session), new Path(new DefaultHomeFinderService(session).find(), new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory)), new TransferStatus()); final Path test = new DropboxTouchFeature(session).touch( - new Path(folder, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)), new TransferStatus()); + new DropboxWriteFeature(session), new Path(folder, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)), new TransferStatus()); final Path copy = new Path(folder, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)); - new DropboxTouchFeature(session).touch(copy, new TransferStatus()); + new DropboxTouchFeature(session).touch(new DropboxWriteFeature(session), copy, new TransferStatus()); new DropboxCopyFeature(session).copy(test, copy, new TransferStatus().setExists(true), new DisabledConnectionCallback(), new DisabledStreamListener()); final Find find = new DefaultFindFeature(session); assertTrue(find.find(test)); @@ -77,10 +77,10 @@ public class DropboxCopyFeatureTest extends AbstractDropboxTest { @Test public void testCopyDirectory() throws Exception { final Path directory = new DropboxDirectoryFeature(session).mkdir( - new Path(new DefaultHomeFinderService(session).find(), new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory)), new TransferStatus()); + new DropboxWriteFeature(session), new Path(new DefaultHomeFinderService(session).find(), new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory)), new TransferStatus()); final String name = new AlphanumericRandomStringService().random(); final Path file = new DropboxTouchFeature(session).touch( - new Path(directory, name, EnumSet.of(Path.Type.file)), new TransferStatus()); + new DropboxWriteFeature(session), new Path(directory, name, EnumSet.of(Path.Type.file)), new TransferStatus()); assertTrue(new DropboxFindFeature(session).find(file)); final Path copy = new Path(new DefaultHomeFinderService(session).find(), new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory)); new DropboxCopyFeature(session).copy(directory, copy, new TransferStatus(), new DisabledConnectionCallback(), new DisabledStreamListener()); @@ -94,7 +94,7 @@ public class DropboxCopyFeatureTest extends AbstractDropboxTest { public void testMoveInvalidFilename() throws Exception { final DropboxCopyFeature feature = new DropboxCopyFeature(session); final Path home = new DefaultHomeFinderService(session).find(); - final Path file = new DropboxTouchFeature(session).touch(new Path(home, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)), new TransferStatus()); + final Path file = new DropboxTouchFeature(session).touch(new DropboxWriteFeature(session), new Path(home, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)), new TransferStatus()); final Path target = new Path(home, "~$f", EnumSet.of(Path.Type.file)); assertThrows(InvalidFilenameException.class, () -> feature.preflight(file, Optional.of(target))); assertThrows(AccessDeniedException.class, () -> feature.copy(file, target, new TransferStatus(), new DisabledConnectionCallback(), new DisabledStreamListener())); diff --git a/dropbox/src/test/java/ch/cyberduck/core/dropbox/DropboxDeleteFeatureTest.java b/dropbox/src/test/java/ch/cyberduck/core/dropbox/DropboxDeleteFeatureTest.java index ce2e1f9386..0c8864d66f 100644 --- a/dropbox/src/test/java/ch/cyberduck/core/dropbox/DropboxDeleteFeatureTest.java +++ b/dropbox/src/test/java/ch/cyberduck/core/dropbox/DropboxDeleteFeatureTest.java @@ -45,16 +45,16 @@ public class DropboxDeleteFeatureTest extends AbstractDropboxTest { @Test public void testDeleteFile() throws Exception { final Path file = new DropboxTouchFeature(session).touch( - new Path(new DefaultHomeFinderService(session).find(), new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)), new TransferStatus()); + new DropboxWriteFeature(session), new Path(new DefaultHomeFinderService(session).find(), new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)), new TransferStatus()); new DropboxDeleteFeature(session).delete(Collections.singletonList(file), new DisabledLoginCallback(), new Delete.DisabledCallback()); } @Test public void testDeleteDirectory() throws Exception { final Path folder = new DropboxDirectoryFeature(session).mkdir( - new Path(new DefaultHomeFinderService(session).find(), new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.volume, Path.Type.directory)), new TransferStatus()); + new DropboxWriteFeature(session), new Path(new DefaultHomeFinderService(session).find(), new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.volume, Path.Type.directory)), new TransferStatus()); final Path file = new DropboxTouchFeature(session).touch( - new Path(folder, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)), new TransferStatus()); + new DropboxWriteFeature(session), new Path(folder, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)), new TransferStatus()); new DropboxDeleteFeature(session).delete(Collections.singletonList(folder), new DisabledLoginCallback(), new Delete.DisabledCallback()); assertFalse(new DropboxFindFeature(session).find(file)); } diff --git a/dropbox/src/test/java/ch/cyberduck/core/dropbox/DropboxDirectoryFeatureTest.java b/dropbox/src/test/java/ch/cyberduck/core/dropbox/DropboxDirectoryFeatureTest.java index 0bc0417d71..66b56f6bcd 100644 --- a/dropbox/src/test/java/ch/cyberduck/core/dropbox/DropboxDirectoryFeatureTest.java +++ b/dropbox/src/test/java/ch/cyberduck/core/dropbox/DropboxDirectoryFeatureTest.java @@ -41,12 +41,12 @@ public class DropboxDirectoryFeatureTest extends AbstractDropboxTest { @Test public void testDirectory() throws Exception { final Path home = new DefaultHomeFinderService(session).find(); - final Path level1 = new DropboxDirectoryFeature(session).mkdir(new Path(home, + final Path level1 = new DropboxDirectoryFeature(session).mkdir(new DropboxWriteFeature(session), new Path(home, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory)), null); assertTrue(new DefaultFindFeature(session).find(level1)); assertTrue(new DropboxFindFeature(session).find(level1)); - assertThrows(ConflictException.class, () -> new DropboxDirectoryFeature(session).mkdir(level1, new TransferStatus())); - final Path level2 = new DropboxDirectoryFeature(session).mkdir(new Path(level1, + assertThrows(ConflictException.class, () -> new DropboxDirectoryFeature(session).mkdir(new DropboxWriteFeature(session), level1, new TransferStatus())); + final Path level2 = new DropboxDirectoryFeature(session).mkdir(new DropboxWriteFeature(session), new Path(level1, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory)), null); assertTrue(new DefaultFindFeature(session).find(level2)); assertTrue(new DropboxFindFeature(session).find(level2)); diff --git a/dropbox/src/test/java/ch/cyberduck/core/dropbox/DropboxFindFeatureTest.java b/dropbox/src/test/java/ch/cyberduck/core/dropbox/DropboxFindFeatureTest.java index 5fca9f439d..7245be161d 100644 --- a/dropbox/src/test/java/ch/cyberduck/core/dropbox/DropboxFindFeatureTest.java +++ b/dropbox/src/test/java/ch/cyberduck/core/dropbox/DropboxFindFeatureTest.java @@ -49,7 +49,7 @@ public class DropboxFindFeatureTest extends AbstractDropboxTest { @Test public void testFindDirectory() throws Exception { final Path folder = new DropboxDirectoryFeature(session).mkdir( - new Path(new DefaultHomeFinderService(session).find(), new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory)), new TransferStatus()); + new DropboxWriteFeature(session), new Path(new DefaultHomeFinderService(session).find(), new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory)), new TransferStatus()); assertTrue(new DropboxFindFeature(session).find(folder)); assertFalse(new DropboxFindFeature(session).find(new Path(folder.getAbsolute(), EnumSet.of(Path.Type.file)))); new DropboxDeleteFeature(session).delete(Collections.singletonList(folder), new DisabledLoginCallback(), new Delete.DisabledCallback()); @@ -58,7 +58,7 @@ public class DropboxFindFeatureTest extends AbstractDropboxTest { @Test public void testFindFile() throws Exception { final Path file = new Path(new DefaultHomeFinderService(session).find(), new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)); - new DropboxTouchFeature(session).touch(file, new TransferStatus()); + new DropboxTouchFeature(session).touch(new DropboxWriteFeature(session), file, new TransferStatus()); assertTrue(new DropboxFindFeature(session).find(file)); assertFalse(new DropboxFindFeature(session).find(new Path(file.getAbsolute(), EnumSet.of(Path.Type.directory)))); new DropboxDeleteFeature(session).delete(Collections.singletonList(file), new DisabledLoginCallback(), new Delete.DisabledCallback()); diff --git a/dropbox/src/test/java/ch/cyberduck/core/dropbox/DropboxListServiceTest.java b/dropbox/src/test/java/ch/cyberduck/core/dropbox/DropboxListServiceTest.java index 7fee38060c..efbdebe21f 100644 --- a/dropbox/src/test/java/ch/cyberduck/core/dropbox/DropboxListServiceTest.java +++ b/dropbox/src/test/java/ch/cyberduck/core/dropbox/DropboxListServiceTest.java @@ -49,8 +49,8 @@ public class DropboxListServiceTest extends AbstractDropboxTest { @Test public void testFilenameColon() throws Exception { final Path home = new DefaultHomeFinderService(session).find(); - final Path file = new DropboxTouchFeature(session).touch(new Path(home, String.format("%s:name", new AlphanumericRandomStringService().random()), EnumSet.of(Path.Type.file)), new TransferStatus()); - final Path folder = new DropboxDirectoryFeature(session).mkdir(new Path(home, String.format("%s:name", new AlphanumericRandomStringService().random()), EnumSet.of(Path.Type.directory)), new TransferStatus()); + final Path file = new DropboxTouchFeature(session).touch(new DropboxWriteFeature(session), new Path(home, String.format("%s:name", new AlphanumericRandomStringService().random()), EnumSet.of(Path.Type.file)), new TransferStatus()); + final Path folder = new DropboxDirectoryFeature(session).mkdir(new DropboxWriteFeature(session), new Path(home, String.format("%s:name", new AlphanumericRandomStringService().random()), EnumSet.of(Path.Type.directory)), new TransferStatus()); final AttributedList list = new DropboxListService(session).list(home, new DisabledListProgressListener()); assertNotSame(AttributedList.emptyList(), list); assertFalse(list.isEmpty()); diff --git a/dropbox/src/test/java/ch/cyberduck/core/dropbox/DropboxLockFeatureTest.java b/dropbox/src/test/java/ch/cyberduck/core/dropbox/DropboxLockFeatureTest.java index 299d5f468d..1e73f2ba7e 100644 --- a/dropbox/src/test/java/ch/cyberduck/core/dropbox/DropboxLockFeatureTest.java +++ b/dropbox/src/test/java/ch/cyberduck/core/dropbox/DropboxLockFeatureTest.java @@ -17,6 +17,7 @@ package ch.cyberduck.core.dropbox; import ch.cyberduck.core.AbstractDropboxTest; import ch.cyberduck.core.AlphanumericRandomStringService; +import ch.cyberduck.core.DefaultPathAttributes; import ch.cyberduck.core.DisabledLoginCallback; import ch.cyberduck.core.Path; import ch.cyberduck.core.PathAttributes; @@ -43,7 +44,7 @@ public class DropboxLockFeatureTest extends AbstractDropboxTest { @Test public void testLockNotShared() throws Exception { final DropboxTouchFeature touch = new DropboxTouchFeature(session); - final Path file = touch.touch(new Path(new DefaultHomeFinderService(session).find(), new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)), new TransferStatus()); + final Path file = touch.touch(new DropboxWriteFeature(session), new Path(new DefaultHomeFinderService(session).find(), new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)), new TransferStatus()); final DropboxLockFeature f = new DropboxLockFeature(session); try { final String lock = f.lock(file); @@ -58,7 +59,7 @@ public class DropboxLockFeatureTest extends AbstractDropboxTest { @Test(expected = InteroperabilityException.class) public void testLock() throws Exception { final DropboxTouchFeature touch = new DropboxTouchFeature(session); - final Path file = touch.touch(new Path(new Path(new DefaultHomeFinderService(session).find(), "Projects", EnumSet.of(Path.Type.directory, Path.Type.volume, Path.Type.shared)).withAttributes(new PathAttributes().setFileId("7581509952")), + final Path file = touch.touch(new DropboxWriteFeature(session), new Path(new Path(new DefaultHomeFinderService(session).find(), "Projects", EnumSet.of(Path.Type.directory, Path.Type.volume, Path.Type.shared)).withAttributes(new DefaultPathAttributes().setFileId("7581509952")), new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)), new TransferStatus()); final DropboxLockFeature f = new DropboxLockFeature(session); final String lock = f.lock(file); @@ -71,7 +72,7 @@ public class DropboxLockFeatureTest extends AbstractDropboxTest { @Ignore @Test(expected = NotfoundException.class) public void testLockNoSuchFile() throws Exception { - final Path file = new Path(new Path(new DefaultHomeFinderService(session).find(), "Projects", EnumSet.of(Path.Type.directory, Path.Type.volume, Path.Type.shared)).withAttributes(new PathAttributes().setFileId("7581509952")), + final Path file = new Path(new Path(new DefaultHomeFinderService(session).find(), "Projects", EnumSet.of(Path.Type.directory, Path.Type.volume, Path.Type.shared)).withAttributes(new DefaultPathAttributes().setFileId("7581509952")), new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)); final DropboxLockFeature f = new DropboxLockFeature(session); f.lock(file); @@ -80,7 +81,7 @@ public class DropboxLockFeatureTest extends AbstractDropboxTest { @Test public void testLockNotfound() throws Exception { final DropboxTouchFeature touch = new DropboxTouchFeature(session); - final Path file = touch.touch(new Path(new Path(new DefaultHomeFinderService(session).find(), "Projects", EnumSet.of(Path.Type.directory, Path.Type.shared)).withAttributes(new PathAttributes().setFileId("7581509952")), + final Path file = touch.touch(new DropboxWriteFeature(session), new Path(new Path(new DefaultHomeFinderService(session).find(), "Projects", EnumSet.of(Path.Type.directory, Path.Type.shared)).withAttributes(new DefaultPathAttributes().setFileId("7581509952")), new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)), new TransferStatus()); final DropboxLockFeature f = new DropboxLockFeature(session); f.unlock(file, "l"); diff --git a/dropbox/src/test/java/ch/cyberduck/core/dropbox/DropboxMoveFeatureTest.java b/dropbox/src/test/java/ch/cyberduck/core/dropbox/DropboxMoveFeatureTest.java index ae647098ea..62b0fe5cf8 100644 --- a/dropbox/src/test/java/ch/cyberduck/core/dropbox/DropboxMoveFeatureTest.java +++ b/dropbox/src/test/java/ch/cyberduck/core/dropbox/DropboxMoveFeatureTest.java @@ -53,7 +53,7 @@ public class DropboxMoveFeatureTest extends AbstractDropboxTest { @Test public void testMoveFile() throws Exception { final Path home = new DefaultHomeFinderService(session).find(); - final Path file = new DropboxTouchFeature(session).touch(new Path(home, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)), new TransferStatus()); + final Path file = new DropboxTouchFeature(session).touch(new DropboxWriteFeature(session), new Path(home, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)), new TransferStatus()); assertTrue(new DropboxFindFeature(session).find(file)); assertTrue(new DefaultFindFeature(session).find(file)); final Path target = new DropboxMoveFeature(session).move(file, new Path(home, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)), new TransferStatus(), new Delete.DisabledCallback(), new DisabledConnectionCallback()); @@ -63,6 +63,8 @@ public class DropboxMoveFeatureTest extends AbstractDropboxTest { assertNotEquals(target.attributes().getVersionId(), file.attributes().getVersionId()); assertEquals(target.attributes().getModificationDate(), file.attributes().getModificationDate()); final PathAttributes targetAttributes = new DropboxAttributesFinderFeature(session).find(target); + assertEquals(file.attributes().getChecksum(), target.attributes().getChecksum()); + assertEquals(target.attributes(), new DropboxAttributesFinderFeature(session).find(target)); assertEquals(Comparison.equal, session.getHost().getProtocol().getFeature(ComparisonService.class).compare(Path.Type.file, file.attributes(), targetAttributes)); assertEquals(target.attributes(), targetAttributes); new DropboxDeleteFeature(session).delete(Collections.singletonList(target), new DisabledLoginCallback(), new Delete.DisabledCallback()); @@ -72,9 +74,9 @@ public class DropboxMoveFeatureTest extends AbstractDropboxTest { public void testMoveOverride() throws Exception { final Path home = new DefaultHomeFinderService(session).find(); final Path test = new Path(home, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)); - new DropboxTouchFeature(session).touch(test, new TransferStatus()); + new DropboxTouchFeature(session).touch(new DropboxWriteFeature(session), test, new TransferStatus()); final Path target = new Path(home, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)); - new DropboxTouchFeature(session).touch(target, new TransferStatus()); + new DropboxTouchFeature(session).touch(new DropboxWriteFeature(session), target, new TransferStatus()); assertThrows(ConflictException.class, () -> new DropboxMoveFeature(session).move(test, target, new TransferStatus().setExists(false), new Delete.DisabledCallback(), new DisabledConnectionCallback())); new DropboxMoveFeature(session).move(test, target, new TransferStatus().setExists(true), new Delete.DisabledCallback(), new DisabledConnectionCallback()); assertFalse(new DropboxFindFeature(session).find(test)); @@ -85,7 +87,7 @@ public class DropboxMoveFeatureTest extends AbstractDropboxTest { @Test public void testMoveDirectory() throws Exception { final Path home = new DefaultHomeFinderService(session).find(); - final Path directory = new DropboxDirectoryFeature(session).mkdir(new Path(home, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory)), new TransferStatus()); + final Path directory = new DropboxDirectoryFeature(session).mkdir(new DropboxWriteFeature(session), new Path(home, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory)), new TransferStatus()); assertTrue(new DropboxFindFeature(session).find(directory)); assertTrue(new DefaultFindFeature(session).find(directory)); final Path target = new DropboxMoveFeature(session).move(directory, new Path(home, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory)), new TransferStatus(), new Delete.DisabledCallback(), new DisabledConnectionCallback()); @@ -100,13 +102,13 @@ public class DropboxMoveFeatureTest extends AbstractDropboxTest { public void testMoveToExistingFile() throws Exception { final Path home = new DefaultHomeFinderService(session).find(); final Path folder = new DropboxDirectoryFeature(session).mkdir( - new Path(home, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory)), new TransferStatus()); + new DropboxWriteFeature(session), new Path(home, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory)), new TransferStatus()); assertTrue(new DefaultFindFeature(session).find(folder)); final Path test = new DropboxTouchFeature(session).touch( - new Path(folder, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)), new TransferStatus()); + new DropboxWriteFeature(session), new Path(folder, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)), new TransferStatus()); assertTrue(new DefaultFindFeature(session).find(test)); final Path temp = new DropboxTouchFeature(session).touch( - new Path(folder, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)), new TransferStatus()); + new DropboxWriteFeature(session), new Path(folder, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)), new TransferStatus()); assertTrue(new DefaultFindFeature(session).find(temp)); new DropboxMoveFeature(session).move(temp, test, new TransferStatus().setExists(true), new Delete.DisabledCallback(), new DisabledConnectionCallback()); final AttributedList files = new DropboxListService(session).list(folder, new DisabledListProgressListener()); @@ -120,13 +122,13 @@ public class DropboxMoveFeatureTest extends AbstractDropboxTest { public void testMoveCaseSensitive() throws Exception { final Path home = new DefaultHomeFinderService(session).find(); final Path folder = new DropboxDirectoryFeature(session).mkdir( - new Path(home, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory)), new TransferStatus()); + new DropboxWriteFeature(session), new Path(home, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory)), new TransferStatus()); assertTrue(new DefaultFindFeature(session).find(folder)); final Path test = new DropboxTouchFeature(session).touch( - new Path(folder, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)), new TransferStatus()); + new DropboxWriteFeature(session), new Path(folder, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)), new TransferStatus()); assertTrue(new DefaultFindFeature(session).find(test)); final Path temp = new DropboxTouchFeature(session).touch( - new Path(folder, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)), new TransferStatus()); + new DropboxWriteFeature(session), new Path(folder, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)), new TransferStatus()); assertTrue(new DefaultFindFeature(session).find(temp)); new DropboxMoveFeature(session).move(temp, new Path(folder, StringUtils.upperCase(test.getName()), EnumSet.of(Path.Type.file)), new TransferStatus().setExists(true), new Delete.DisabledCallback(), new DisabledConnectionCallback()); final AttributedList list = new DropboxListService(session).list(folder, new DisabledListProgressListener()); @@ -140,7 +142,7 @@ public class DropboxMoveFeatureTest extends AbstractDropboxTest { public void testMoveInvalidFilename() throws Exception { final DropboxMoveFeature feature = new DropboxMoveFeature(session); final Path home = new DefaultHomeFinderService(session).find(); - final Path file = new DropboxTouchFeature(session).touch(new Path(home, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)), new TransferStatus()); + final Path file = new DropboxTouchFeature(session).touch(new DropboxWriteFeature(session), new Path(home, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)), new TransferStatus()); final Path target = new Path(home, "~$f", EnumSet.of(Path.Type.file)); assertThrows(InvalidFilenameException.class, () -> feature.preflight(file, Optional.of(target))); assertThrows(AccessDeniedException.class, () -> feature.move(file, target, new TransferStatus(), new Delete.DisabledCallback(), new DisabledConnectionCallback())); @@ -159,7 +161,7 @@ public class DropboxMoveFeatureTest extends AbstractDropboxTest { public void testRenameCaseOnly() throws Exception { final Path home = new DefaultHomeFinderService(session).find(); final String name = new AlphanumericRandomStringService().random(); - final Path file = new DropboxTouchFeature(session).touch(new Path(home, StringUtils.upperCase(name), EnumSet.of(Path.Type.file)), new TransferStatus()); + final Path file = new DropboxTouchFeature(session).touch(new DropboxWriteFeature(session), new Path(home, StringUtils.upperCase(name), EnumSet.of(Path.Type.file)), new TransferStatus()); final Path rename = new Path(home, StringUtils.lowerCase(name), EnumSet.of(Path.Type.file)); assertThrows(InvalidFilenameException.class, () -> new DropboxMoveFeature(session).preflight(file, Optional.of(rename))); assertThrows(ConflictException.class, () -> new DropboxMoveFeature(session).move(file, rename, new TransferStatus().setExists(true), new Delete.DisabledCallback(), new DisabledConnectionCallback())); diff --git a/dropbox/src/test/java/ch/cyberduck/core/dropbox/DropboxPasswordShareUrlProviderTest.java b/dropbox/src/test/java/ch/cyberduck/core/dropbox/DropboxPasswordShareUrlProviderTest.java index 7dc73a695d..95843b1327 100644 --- a/dropbox/src/test/java/ch/cyberduck/core/dropbox/DropboxPasswordShareUrlProviderTest.java +++ b/dropbox/src/test/java/ch/cyberduck/core/dropbox/DropboxPasswordShareUrlProviderTest.java @@ -47,19 +47,19 @@ public class DropboxPasswordShareUrlProviderTest extends AbstractDropboxTest { @Ignore public void testSharePasswordProtected() throws Exception { final Path file = new DropboxTouchFeature(session).touch( - new Path(new DefaultHomeFinderService(session).find(), new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)), new TransferStatus()); + new DropboxWriteFeature(session), new Path(new DefaultHomeFinderService(session).find(), new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)), new TransferStatus()); final DropboxPasswordShareFeature provider = new DropboxPasswordShareFeature(session); final DescriptiveUrl url = provider.toDownloadUrl(file, Share.Sharee.world, null, new DisabledPasswordCallback() { @Override public Credentials prompt(final Host bookmark, final String title, final String reason, final LoginOptions options) { - return new Credentials().withPassword(new AlphanumericRandomStringService().random()); + return new Credentials().setPassword(new AlphanumericRandomStringService().random()); } }); assertNotEquals(DescriptiveUrl.EMPTY, url); assertEquals(url, provider.toDownloadUrl(file, Share.Sharee.world, null, new DisabledPasswordCallback() { @Override public Credentials prompt(final Host bookmark, final String title, final String reason, final LoginOptions options) { - return new Credentials().withPassword(new AlphanumericRandomStringService().random()); + return new Credentials().setPassword(new AlphanumericRandomStringService().random()); } })); new DropboxDeleteFeature(session).delete(Collections.singletonList(file), new DisabledPasswordCallback(), new Delete.DisabledCallback()); @@ -68,7 +68,7 @@ public class DropboxPasswordShareUrlProviderTest extends AbstractDropboxTest { @Test public void testShareFileDownloadPublic() throws Exception { final Path file = new DropboxTouchFeature(session).touch( - new Path(new DefaultHomeFinderService(session).find(), new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)), new TransferStatus()); + new DropboxWriteFeature(session), new Path(new DefaultHomeFinderService(session).find(), new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)), new TransferStatus()); final DropboxPasswordShareFeature provider = new DropboxPasswordShareFeature(session); final DescriptiveUrl url = provider.toDownloadUrl(file, Share.Sharee.world, null, new DisabledPasswordCallback() { @Override @@ -89,12 +89,12 @@ public class DropboxPasswordShareUrlProviderTest extends AbstractDropboxTest { @Test public void testShareFileDownloadPassword() throws Exception { final Path file = new DropboxTouchFeature(session).touch( - new Path(new DefaultHomeFinderService(session).find(), new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)), new TransferStatus()); + new DropboxWriteFeature(session), new Path(new DefaultHomeFinderService(session).find(), new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)), new TransferStatus()); final DropboxPasswordShareFeature provider = new DropboxPasswordShareFeature(session); assertThrows(InteroperabilityException.class, () -> provider.toDownloadUrl(file, Share.Sharee.world, null, new DisabledPasswordCallback() { @Override public Credentials prompt(final Host bookmark, final String title, final String reason, final LoginOptions options) throws LoginCanceledException { - return new Credentials().withPassword(new AlphanumericRandomStringService().random()); + return new Credentials().setPassword(new AlphanumericRandomStringService().random()); } })); new DropboxDeleteFeature(session).delete(Collections.singletonList(file), new DisabledPasswordCallback(), new Delete.DisabledCallback()); @@ -103,7 +103,7 @@ public class DropboxPasswordShareUrlProviderTest extends AbstractDropboxTest { @Test public void testShareDownloadFolderPublic() throws Exception { final Path folder = new DropboxDirectoryFeature(session).mkdir( - new Path(new DefaultHomeFinderService(session).find(), new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)), new TransferStatus()); + new DropboxWriteFeature(session), new Path(new DefaultHomeFinderService(session).find(), new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)), new TransferStatus()); final DropboxPasswordShareFeature provider = new DropboxPasswordShareFeature(session); final DescriptiveUrl url = provider.toDownloadUrl(folder, Share.Sharee.world, null, new DisabledPasswordCallback() { @Override @@ -126,7 +126,7 @@ public class DropboxPasswordShareUrlProviderTest extends AbstractDropboxTest { final Path root = new DefaultHomeFinderService(session).find(); assertFalse(new DropboxPasswordShareFeature(session).isSupported(root, Share.Type.upload)); final Path folder = new DropboxDirectoryFeature(session).mkdir( - new Path(root, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)), new TransferStatus()); + new DropboxWriteFeature(session), new Path(root, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)), new TransferStatus()); final DropboxPasswordShareFeature provider = new DropboxPasswordShareFeature(session); assertNotEquals(DescriptiveUrl.EMPTY, provider.toUploadUrl(folder, Share.Sharee.world, null, new DisabledPasswordCallback() { @Override diff --git a/dropbox/src/test/java/ch/cyberduck/core/dropbox/DropboxReadFeatureTest.java b/dropbox/src/test/java/ch/cyberduck/core/dropbox/DropboxReadFeatureTest.java index e9c77a28a4..8ffbb93e7b 100644 --- a/dropbox/src/test/java/ch/cyberduck/core/dropbox/DropboxReadFeatureTest.java +++ b/dropbox/src/test/java/ch/cyberduck/core/dropbox/DropboxReadFeatureTest.java @@ -84,16 +84,15 @@ public class DropboxReadFeatureTest extends AbstractDropboxTest { public void testReadRange() throws Exception { final Path drive = new DefaultHomeFinderService(session).find(); final Path test = new Path(drive, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)); - new DropboxTouchFeature(session).touch(test, new TransferStatus()); - + new DropboxTouchFeature(session).touch(new DropboxWriteFeature(session), test, new TransferStatus()); final Local local = new Local(System.getProperty("java.io.tmpdir"), new AlphanumericRandomStringService().random()); final byte[] content = RandomUtils.nextBytes(1000); final OutputStream out = local.getOutputStream(false); assertNotNull(out); IOUtils.write(content, out); out.close(); - new DefaultUploadFeature<>(new DropboxWriteFeature(session)).upload( - test, local, new BandwidthThrottle(BandwidthThrottle.UNLIMITED), new DisabledProgressListener(), new DisabledStreamListener(), + new DefaultUploadFeature(session).upload( + new DropboxWriteFeature(session), test, local, new BandwidthThrottle(BandwidthThrottle.UNLIMITED), new DisabledProgressListener(), new DisabledStreamListener(), new TransferStatus().setLength(content.length), new DisabledConnectionCallback()); final TransferStatus status = new TransferStatus(); @@ -120,7 +119,7 @@ public class DropboxReadFeatureTest extends AbstractDropboxTest { final TransferStatus writeStatus = new TransferStatus(); writeStatus.setLength(content.length); final Path directory = new DropboxDirectoryFeature(session).mkdir( - new Path(new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory, Path.Type.volume)), new TransferStatus()); + new DropboxWriteFeature(session), new Path(new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory, Path.Type.volume)), new TransferStatus()); final Path test = new Path(directory, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)); final DropboxWriteFeature writer = new DropboxWriteFeature(session); final HttpResponseOutputStream out = writer.write(test, writeStatus, new DisabledConnectionCallback()); @@ -137,7 +136,7 @@ public class DropboxReadFeatureTest extends AbstractDropboxTest { final byte[] content = RandomUtils.nextBytes(1645); final TransferStatus status = new TransferStatus().setLength(content.length); final Path directory = new DropboxDirectoryFeature(session).mkdir( - new Path(new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory, Path.Type.volume)), new TransferStatus()); + new DropboxWriteFeature(session), new Path(new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory, Path.Type.volume)), new TransferStatus()); final Path test = new Path(directory, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)); final DropboxWriteFeature writer = new DropboxWriteFeature(session); final HttpResponseOutputStream out = writer.write(test, status, new DisabledConnectionCallback()); diff --git a/dropbox/src/test/java/ch/cyberduck/core/dropbox/DropboxSearchFeatureTest.java b/dropbox/src/test/java/ch/cyberduck/core/dropbox/DropboxSearchFeatureTest.java index 3da0efe76d..42ba07496f 100644 --- a/dropbox/src/test/java/ch/cyberduck/core/dropbox/DropboxSearchFeatureTest.java +++ b/dropbox/src/test/java/ch/cyberduck/core/dropbox/DropboxSearchFeatureTest.java @@ -48,7 +48,7 @@ public class DropboxSearchFeatureTest extends AbstractDropboxTest { final String name = new AlphanumericRandomStringService().random(); final Path workdir = new DefaultHomeFinderService(session).find(); final Path file = new Path(workdir, name, EnumSet.of(Path.Type.file)); - new DropboxTouchFeature(session).touch(file, new TransferStatus()); + new DropboxTouchFeature(session).touch(new DropboxWriteFeature(session), file, new TransferStatus()); final DropboxSearchFeature feature = new DropboxSearchFeature(session); assertTrue(feature.search(workdir, new SearchFilter(name), new DisabledListProgressListener()).contains(file)); // Supports prefix matching only @@ -62,9 +62,9 @@ public class DropboxSearchFeatureTest extends AbstractDropboxTest { // } final Path subdir = new Path(workdir, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory)); - new DropboxDirectoryFeature(session).mkdir(subdir, new TransferStatus()); + new DropboxDirectoryFeature(session).mkdir(new DropboxWriteFeature(session), subdir, new TransferStatus()); assertFalse(feature.search(subdir, new SearchFilter(name), new DisabledListProgressListener()).contains(file)); - final Path filesubdir = new DropboxTouchFeature(session).touch(new Path(subdir, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)), new TransferStatus()); + final Path filesubdir = new DropboxTouchFeature(session).touch(new DropboxWriteFeature(session), new Path(subdir, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)), new TransferStatus()); { final AttributedList result = feature.search(workdir, new SearchFilter(filesubdir.getName()), new DisabledListProgressListener()); assertTrue(result.contains(filesubdir)); diff --git a/dropbox/src/test/java/ch/cyberduck/core/dropbox/DropboxShareFeatureTest.java b/dropbox/src/test/java/ch/cyberduck/core/dropbox/DropboxShareFeatureTest.java new file mode 100644 index 0000000000..ae1210eba3 --- /dev/null +++ b/dropbox/src/test/java/ch/cyberduck/core/dropbox/DropboxShareFeatureTest.java @@ -0,0 +1,73 @@ +package ch.cyberduck.core.dropbox; + +/* + * Copyright (c) 2002-2026 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 ch.cyberduck.core.AbstractDropboxTest; +import ch.cyberduck.core.AlphanumericRandomStringService; +import ch.cyberduck.core.DescriptiveUrl; +import ch.cyberduck.core.DisabledPasswordCallback; +import ch.cyberduck.core.Path; +import ch.cyberduck.core.features.Delete; +import ch.cyberduck.core.features.Share; +import ch.cyberduck.core.shared.DefaultHomeFinderService; +import ch.cyberduck.core.transfer.TransferStatus; +import ch.cyberduck.test.IntegrationTest; + +import org.junit.Test; +import org.junit.experimental.categories.Category; + +import java.util.Arrays; +import java.util.Collections; +import java.util.EnumSet; + +import static org.junit.Assert.*; + +@Category(IntegrationTest.class) +public class DropboxShareFeatureTest extends AbstractDropboxTest { + + @Test + public void toDownloadUrl() throws Exception { + final Path root = new DefaultHomeFinderService(session).find(); + final Path folder = new DropboxDirectoryFeature(session).mkdir(new DropboxWriteFeature(session), new Path(root, + new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory)), null); + { + final DescriptiveUrl url = new DropboxShareFeature(session).toDownloadUrl(folder, Share.Sharee.world, null, new DisabledPasswordCallback()); + assertNotNull(url.getUrl()); + assertEquals(url, new DropboxShareFeature(session).toDownloadUrl(folder, Share.Sharee.world, null, new DisabledPasswordCallback())); + } + final Path file = new Path(folder, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)); + new DropboxTouchFeature(session).touch(new DropboxWriteFeature(session), file, new TransferStatus()); + { + final DescriptiveUrl url = new DropboxShareFeature(session).toDownloadUrl(file, Share.Sharee.world, null, new DisabledPasswordCallback()); + assertNotNull(url.getUrl()); + assertEquals(url, new DropboxShareFeature(session).toDownloadUrl(file, Share.Sharee.world, null, new DisabledPasswordCallback())); + } + new DropboxDeleteFeature(session).delete(Arrays.asList(file, folder), new DisabledPasswordCallback(), new Delete.DisabledCallback()); + } + + @Test + public void toUploadUrl() throws Exception { + final Path root = new DefaultHomeFinderService(session).find(); + final Path folder = new DropboxDirectoryFeature(session).mkdir(new DropboxWriteFeature(session), new Path(root, + new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory)), null); + { + final DescriptiveUrl url = new DropboxShareFeature(session).toUploadUrl(folder, Share.Sharee.world, null, new DisabledPasswordCallback()); + assertNotNull(url.getUrl()); + assertNotEquals(url, new DropboxShareFeature(session).toUploadUrl(folder, Share.Sharee.world, null, new DisabledPasswordCallback())); + } + new DropboxDeleteFeature(session).delete(Collections.singletonList(folder), new DisabledPasswordCallback(), new Delete.DisabledCallback()); + } +} \ No newline at end of file diff --git a/dropbox/src/test/java/ch/cyberduck/core/dropbox/DropboxTemporaryUrlProviderTest.java b/dropbox/src/test/java/ch/cyberduck/core/dropbox/DropboxTemporaryUrlProviderTest.java index f39940ce62..2391e1b86e 100644 --- a/dropbox/src/test/java/ch/cyberduck/core/dropbox/DropboxTemporaryUrlProviderTest.java +++ b/dropbox/src/test/java/ch/cyberduck/core/dropbox/DropboxTemporaryUrlProviderTest.java @@ -41,7 +41,7 @@ public class DropboxTemporaryUrlProviderTest extends AbstractDropboxTest { public void testToUrl() throws Exception { final DropboxTemporaryUrlProvider provider = new DropboxTemporaryUrlProvider(session); final Path file = new Path(new DefaultHomeFinderService(session).find(), new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)); - new DropboxTouchFeature(session).touch(file, new TransferStatus()); + new DropboxTouchFeature(session).touch(new DropboxWriteFeature(session), file, new TransferStatus()); assertNotNull(provider.toDownloadUrl(file, Share.Sharee.world, null, new DisabledPasswordCallback()).getUrl()); new DropboxDeleteFeature(session).delete(Collections.singletonList(file), new DisabledLoginCallback(), new Delete.DisabledCallback()); } diff --git a/dropbox/src/test/java/ch/cyberduck/core/dropbox/DropboxTouchFeatureTest.java b/dropbox/src/test/java/ch/cyberduck/core/dropbox/DropboxTouchFeatureTest.java index 958f987659..b904484003 100644 --- a/dropbox/src/test/java/ch/cyberduck/core/dropbox/DropboxTouchFeatureTest.java +++ b/dropbox/src/test/java/ch/cyberduck/core/dropbox/DropboxTouchFeatureTest.java @@ -52,13 +52,13 @@ public class DropboxTouchFeatureTest extends AbstractDropboxTest { final DropboxTouchFeature touch = new DropboxTouchFeature(session); final Path file = new Path(new DefaultHomeFinderService(session).find(), String.format("~%s.tmp", new AlphanumericRandomStringService().random()), EnumSet.of(Path.Type.file)); assertFalse(touch.isSupported(new DefaultHomeFinderService(session).find(), file.getName())); - touch.touch(file, new TransferStatus()); + touch.touch(new DropboxWriteFeature(session), file, new TransferStatus()); } @Test public void testFindFile() throws Exception { final Path file = new Path(new DefaultHomeFinderService(session).find(), new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)); - new DropboxTouchFeature(session).touch(file, new TransferStatus()); + new DropboxTouchFeature(session).touch(new DropboxWriteFeature(session), file, new TransferStatus()); assertTrue(new DropboxFindFeature(session).find(file)); assertTrue(new DefaultFindFeature(session).find(file)); new DropboxDeleteFeature(session).delete(Collections.singletonList(file), new DisabledLoginCallback(), new Delete.DisabledCallback()); @@ -69,9 +69,9 @@ public class DropboxTouchFeatureTest extends AbstractDropboxTest { final Path container = new DefaultHomeFinderService(session).find(); final String filename = StringUtils.lowerCase(new AlphanumericRandomStringService().random()); final Path file = new DropboxTouchFeature(session) - .touch(new Path(container, filename, EnumSet.of(Path.Type.file)), new TransferStatus().setLength(0L)); + .touch(new DropboxWriteFeature(session), new Path(container, filename, EnumSet.of(Path.Type.file)), new TransferStatus().setLength(0L)); new DropboxTouchFeature(session) - .touch(new Path(container, StringUtils.upperCase(filename), EnumSet.of(Path.Type.file)), new TransferStatus().setLength(0L)); + .touch(new DropboxWriteFeature(session), new Path(container, StringUtils.upperCase(filename), EnumSet.of(Path.Type.file)), new TransferStatus().setLength(0L)); final byte[] content = RandomUtils.nextBytes(254); final TransferStatus status = new TransferStatus(); status.setLength(content.length); diff --git a/dropbox/src/test/java/ch/cyberduck/core/dropbox/DropboxUploadFeatureTest.java b/dropbox/src/test/java/ch/cyberduck/core/dropbox/DropboxUploadFeatureTest.java index 3d45164632..bcbeb01f10 100644 --- a/dropbox/src/test/java/ch/cyberduck/core/dropbox/DropboxUploadFeatureTest.java +++ b/dropbox/src/test/java/ch/cyberduck/core/dropbox/DropboxUploadFeatureTest.java @@ -47,7 +47,7 @@ public class DropboxUploadFeatureTest extends AbstractDropboxTest { @Test public void testUploadSmall() throws Exception { - final DropboxUploadFeature feature = new DropboxUploadFeature(session, new DropboxWriteFeature(session)); + final DropboxUploadFeature feature = new DropboxUploadFeature(session); final Path root = new Path("/", EnumSet.of(Path.Type.directory, Path.Type.volume)); final String name = new AlphanumericRandomStringService().random(); final Path test = new Path(root, name, EnumSet.of(Path.Type.file)); @@ -60,7 +60,7 @@ public class DropboxUploadFeatureTest extends AbstractDropboxTest { status.setLength(content.length); status.setMime("text/plain"); final BytecountStreamListener count = new BytecountStreamListener(); - final Metadata metadata = feature.upload(test, local, new BandwidthThrottle(BandwidthThrottle.UNLIMITED), + final Metadata metadata = feature.upload(new DropboxWriteFeature(session), test, local, new BandwidthThrottle(BandwidthThrottle.UNLIMITED), new DisabledProgressListener(), count, status, new DisabledLoginCallback()); assertEquals(content.length, count.getSent()); assertTrue(status.isComplete()); @@ -76,6 +76,6 @@ public class DropboxUploadFeatureTest extends AbstractDropboxTest { @Test public void testDecorate() throws Exception { final NullInputStream n = new NullInputStream(1L); - assertSame(NullInputStream.class, new DropboxUploadFeature(session, new DropboxWriteFeature(session)).decorate(n, null).getClass()); + assertSame(NullInputStream.class, new DropboxUploadFeature(session).decorate(n, null).getClass()); } } \ No newline at end of file diff --git a/dropbox/src/test/java/ch/cyberduck/core/dropbox/DropboxVersioningFeatureTest.java b/dropbox/src/test/java/ch/cyberduck/core/dropbox/DropboxVersioningFeatureTest.java index 127a28a216..053a67d12e 100644 --- a/dropbox/src/test/java/ch/cyberduck/core/dropbox/DropboxVersioningFeatureTest.java +++ b/dropbox/src/test/java/ch/cyberduck/core/dropbox/DropboxVersioningFeatureTest.java @@ -18,6 +18,7 @@ package ch.cyberduck.core.dropbox; import ch.cyberduck.core.AbstractDropboxTest; 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; @@ -50,13 +51,13 @@ public class DropboxVersioningFeatureTest extends AbstractDropboxTest { @Test public void testRevert() throws Exception { final Path directory = new DropboxDirectoryFeature(session).mkdir( - new Path(new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory, Path.Type.volume)), new TransferStatus()); + new DropboxWriteFeature(session), new Path(new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory, Path.Type.volume)), new TransferStatus()); final DropboxAttributesFinderFeature f = new DropboxAttributesFinderFeature(session); - final Path test = new DropboxTouchFeature(session).touch(new Path(directory, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)), new TransferStatus()); + final Path test = new DropboxTouchFeature(session).touch(new DropboxWriteFeature(session), new Path(directory, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)), new TransferStatus()); assertEquals(test.attributes().getVersionId(), new DropboxAttributesFinderFeature(session).find(test).getVersionId()); final DropboxVersioningFeature feature = new DropboxVersioningFeature(session); assertEquals(0, feature.list(test, new DisabledListProgressListener()).size()); - 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(); diff --git a/eue/pom.xml b/eue/pom.xml index 3674b8a72d..44bb48dd38 100644 --- a/eue/pom.xml +++ b/eue/pom.xml @@ -19,7 +19,7 @@ parent ch.cyberduck - 9.2.0-SNAPSHOT + 9.4.0-SNAPSHOT eue jar diff --git a/eue/src/main/java/ch/cyberduck/core/eue/EueAttributesFinderFeature.java b/eue/src/main/java/ch/cyberduck/core/eue/EueAttributesFinderFeature.java index 1ef7a489c6..e0e6e1ed0c 100644 --- a/eue/src/main/java/ch/cyberduck/core/eue/EueAttributesFinderFeature.java +++ b/eue/src/main/java/ch/cyberduck/core/eue/EueAttributesFinderFeature.java @@ -15,6 +15,7 @@ package ch.cyberduck.core.eue; * GNU General Public License for more details. */ +import ch.cyberduck.core.DefaultPathAttributes; import ch.cyberduck.core.ListProgressListener; import ch.cyberduck.core.Path; import ch.cyberduck.core.PathAttributes; @@ -103,7 +104,7 @@ public class EueAttributesFinderFeature implements AttributesFinder { protected PathAttributes toAttributes(final Uifs entity, final UiWin32 uiwin32, final ShareCreationResponseEntity share) { - final PathAttributes attr = new PathAttributes(); + final PathAttributes attr = new DefaultPathAttributes(); attr.setDisplayname(entity.getName()); // Matches ETag response header attr.setETag(StringUtils.remove(entity.getMetaETag(), '"')); diff --git a/eue/src/main/java/ch/cyberduck/core/eue/EueDirectoryFeature.java b/eue/src/main/java/ch/cyberduck/core/eue/EueDirectoryFeature.java index a400b91d48..267298c742 100644 --- a/eue/src/main/java/ch/cyberduck/core/eue/EueDirectoryFeature.java +++ b/eue/src/main/java/ch/cyberduck/core/eue/EueDirectoryFeature.java @@ -28,6 +28,7 @@ import ch.cyberduck.core.exception.ConflictException; 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; import org.apache.commons.lang3.StringUtils; @@ -50,7 +51,7 @@ public class EueDirectoryFeature implements Directory { } @Override - public Path mkdir(final Path folder, final TransferStatus status) throws BackgroundException { + public Path mkdir(final Write writer, final Path folder, final TransferStatus status) throws BackgroundException { try { final ResourceCreationRepresentationArrayInner resourceCreationRepresentation = new ResourceCreationRepresentationArrayInner(); final String path = StringUtils.removeStart(folder.getAbsolute(), String.valueOf(Path.DELIMITER)); diff --git a/eue/src/main/java/ch/cyberduck/core/eue/EueLargeUploadService.java b/eue/src/main/java/ch/cyberduck/core/eue/EueLargeUploadService.java index 4e836c6b11..1a6dc5c838 100644 --- a/eue/src/main/java/ch/cyberduck/core/eue/EueLargeUploadService.java +++ b/eue/src/main/java/ch/cyberduck/core/eue/EueLargeUploadService.java @@ -27,7 +27,6 @@ import ch.cyberduck.core.eue.io.swagger.client.model.UploadType; import ch.cyberduck.core.exception.BackgroundException; import ch.cyberduck.core.exception.ChecksumException; 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; @@ -65,24 +64,20 @@ public class EueLargeUploadService extends HttpUploadFeature writer; - - public EueLargeUploadService(final EueSession session, final EueResourceIdProvider fileid, final Write writer) { - this(session, fileid, writer, HostPreferencesFactory.get(session.getHost()).getLong("eue.upload.multipart.size"), + public EueLargeUploadService(final EueSession session, final EueResourceIdProvider fileid) { + this(session, fileid, HostPreferencesFactory.get(session.getHost()).getLong("eue.upload.multipart.size"), HostPreferencesFactory.get(session.getHost()).getInteger("eue.upload.multipart.concurrency")); } - public EueLargeUploadService(final EueSession session, final EueResourceIdProvider fileid, final Write writer, final Long chunksize, final Integer concurrency) { - super(writer); + public EueLargeUploadService(final EueSession session, final EueResourceIdProvider fileid, final Long chunksize, final Integer concurrency) { this.session = session; - this.writer = writer; this.chunksize = chunksize; this.concurrency = concurrency; this.fileid = fileid; } @Override - public EueWriteFeature.Chunk upload(final Path file, final Local local, final BandwidthThrottle throttle, final ProgressListener progress, final StreamListener streamListener, + public EueWriteFeature.Chunk upload(final Write 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 ThreadPool pool = ThreadPoolFactory.get("multipart", concurrency); try { @@ -102,9 +97,10 @@ public class EueLargeUploadService extends HttpUploadFeature 0; partNumber++) { final long length = Math.min(chunksize, remaining); - parts.add(this.submit(pool, file, local, throttle, streamListener, status, + parts.add(this.submit(pool, write, file, local, throttle, streamListener, status, uploadUri, resourceId, partNumber, offset, length, callback)); remaining -= length; offset += length; @@ -150,7 +146,7 @@ public class EueLargeUploadService extends HttpUploadFeature submit(final ThreadPool pool, final Path file, final Local local, + private Future submit(final ThreadPool pool, final Write write, final Path file, final Local local, final BandwidthThrottle throttle, final StreamListener listener, final TransferStatus overall, final String url, final String resourceId, final int partNumber, final long offset, final long length, final ConnectionCallback callback) throws ConnectionCanceledException { @@ -170,19 +166,14 @@ public class EueLargeUploadService extends HttpUploadFeature withWriter(final Write writer) { - this.writer = writer; - return super.withWriter(writer); - } } diff --git a/eue/src/main/java/ch/cyberduck/core/eue/EueSession.java b/eue/src/main/java/ch/cyberduck/core/eue/EueSession.java index 7a6d90d5d1..5c13792f17 100644 --- a/eue/src/main/java/ch/cyberduck/core/eue/EueSession.java +++ b/eue/src/main/java/ch/cyberduck/core/eue/EueSession.java @@ -33,13 +33,10 @@ import ch.cyberduck.core.features.*; import ch.cyberduck.core.http.CustomServiceUnavailableRetryStrategy; import ch.cyberduck.core.http.DefaultHttpRateLimiter; import ch.cyberduck.core.http.DefaultHttpResponseExceptionMappingService; -import ch.cyberduck.core.http.ExecutionCountServiceUnavailableRetryStrategy; import ch.cyberduck.core.http.HttpSession; import ch.cyberduck.core.http.RateLimitingHttpRequestInterceptor; import ch.cyberduck.core.oauth.OAuth2ErrorResponseInterceptor; import ch.cyberduck.core.oauth.OAuth2RequestInterceptor; -import ch.cyberduck.core.preferences.HostPreferences; -import ch.cyberduck.core.preferences.HostPreferencesFactory; import ch.cyberduck.core.preferences.PreferencesFactory; import ch.cyberduck.core.proxy.ProxyFinder; import ch.cyberduck.core.ssl.X509KeyManager; @@ -82,8 +79,6 @@ import com.google.gson.JsonParser; public class EueSession extends HttpSession { private static final Logger log = LogManager.getLogger(EueSession.class); - private final HostPreferences preferences = HostPreferencesFactory.get(host); - private OAuth2RequestInterceptor authorizationService; private String basePath; @@ -110,7 +105,7 @@ public class EueSession extends HttpSession { .withRedirectUri(host.getProtocol().getOAuthRedirectUrl() ); configuration.setServiceUnavailableRetryStrategy(new CustomServiceUnavailableRetryStrategy(host, - new ExecutionCountServiceUnavailableRetryStrategy(new OAuth2ErrorResponseInterceptor(host, authorizationService)))); + new OAuth2ErrorResponseInterceptor(host, authorizationService))); configuration.addInterceptorLast(authorizationService); configuration.addInterceptorLast(new HttpRequestInterceptor() { @Override @@ -166,7 +161,8 @@ public class EueSession extends HttpSession { @Override public void login(final LoginCallback prompt, final CancelCallback cancel) throws BackgroundException { - final Credentials credentials = authorizationService.validate(); + final Credentials credentials = host.getCredentials(); + credentials.setOauth(authorizationService.validate(credentials.getOauth())); try { final StringBuilder url = new StringBuilder(); url.append(host.getProtocol().getScheme().toString()).append("://"); @@ -218,15 +214,18 @@ public class EueSession extends HttpSession { } @Override - protected void logout() throws BackgroundException { + public void disconnect() throws BackgroundException { try { - client.close(); + resourceid.clear(); + if(client != null) { + client.close(); + } } catch(IOException e) { throw new DefaultIOExceptionMappingService().map(e); } finally { - resourceid.clear(); + super.disconnect(); } } @@ -263,7 +262,7 @@ public class EueSession extends HttpSession { return (T) new EueReadFeature(this, resourceid); } if(type == Write.class) { - return (T) new EueThresholdWriteFeature(this, resourceid); + return (T) new EueWriteFeature(this, resourceid); } if(type == MultipartWrite.class) { return (T) new EueMultipartWriteFeature(this, resourceid); diff --git a/eue/src/main/java/ch/cyberduck/core/eue/EueShareFeature.java b/eue/src/main/java/ch/cyberduck/core/eue/EueShareFeature.java index 44b974fbbd..f3f663cf06 100644 --- a/eue/src/main/java/ch/cyberduck/core/eue/EueShareFeature.java +++ b/eue/src/main/java/ch/cyberduck/core/eue/EueShareFeature.java @@ -62,6 +62,9 @@ public class EueShareFeature implements Share writer; - - public EueSingleUploadService(final EueSession session, final EueResourceIdProvider fileid, final Write writer) { - super(writer); + public EueSingleUploadService(final EueSession session, final EueResourceIdProvider fileid) { this.session = session; this.fileid = fileid; - this.writer = writer; } @Override - public EueWriteFeature.Chunk upload(final Path file, final Local local, final BandwidthThrottle throttle, final ProgressListener progress, final StreamListener streamListener, + public EueWriteFeature.Chunk upload(final Write 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 String uploadUri; final String resourceId; @@ -66,13 +61,8 @@ public class EueSingleUploadService extends HttpUploadFeature withWriter(final Write writer) { - this.writer = writer; - return super.withWriter(writer); - } } diff --git a/eue/src/main/java/ch/cyberduck/core/eue/EueThresholdUploadService.java b/eue/src/main/java/ch/cyberduck/core/eue/EueThresholdUploadService.java index a57a2f31fe..d759e0d5e3 100644 --- a/eue/src/main/java/ch/cyberduck/core/eue/EueThresholdUploadService.java +++ b/eue/src/main/java/ch/cyberduck/core/eue/EueThresholdUploadService.java @@ -39,8 +39,6 @@ public class EueThresholdUploadService implements Upload private final Long threshold; private final EueResourceIdProvider fileid; - private Write writer; - public EueThresholdUploadService(final EueSession session, final EueResourceIdProvider fileid, final VaultRegistry registry) { this(session, fileid, registry, HostPreferencesFactory.get(session.getHost()).getLong("eue.upload.multipart.threshold")); } @@ -50,27 +48,20 @@ public class EueThresholdUploadService implements Upload this.registry = registry; this.threshold = threshold; this.fileid = fileid; - this.writer = new EueWriteFeature(session, fileid); } @Override - public EueWriteFeature.Chunk upload(final Path file, Local local, final BandwidthThrottle throttle, final ProgressListener progress, final StreamListener streamListener, + public EueWriteFeature.Chunk upload(final Write write, final Path file, Local local, final BandwidthThrottle throttle, final ProgressListener progress, final StreamListener streamListener, final TransferStatus status, final ConnectionCallback prompt) throws BackgroundException { if(status.getLength() >= threshold) { if(Vault.DISABLED == registry.find(session, file)) { // Only allow concurrent write of chunks when not uploading to vault. Write with default feature multiple 4MB chunks in parallel - return new EueLargeUploadService(session, fileid, writer).upload(file, local, throttle, progress, streamListener, status, prompt); + return new EueLargeUploadService(session, fileid).upload(write, file, local, throttle, progress, streamListener, status, prompt); } // Write with multipart write feature for known file length sequentially 4MB chunks - return new EueUploadService(session, fileid, writer).upload(file, local, throttle, progress, streamListener, status, prompt); + return new EueUploadService(session).upload(write, file, local, throttle, progress, streamListener, status, prompt); } // Write single chunk smaller than threshold - return new EueSingleUploadService(session, fileid, writer).upload(file, local, throttle, progress, streamListener, status, prompt); - } - - @Override - public Upload withWriter(final Write writer) { - this.writer = writer; - return this; + return new EueSingleUploadService(session, fileid).upload(write, file, local, throttle, progress, streamListener, status, prompt); } } diff --git a/eue/src/main/java/ch/cyberduck/core/eue/EueThresholdWriteFeature.java b/eue/src/main/java/ch/cyberduck/core/eue/EueThresholdWriteFeature.java deleted file mode 100644 index 7300478315..0000000000 --- a/eue/src/main/java/ch/cyberduck/core/eue/EueThresholdWriteFeature.java +++ /dev/null @@ -1,63 +0,0 @@ -package ch.cyberduck.core.eue; - -/* - * Copyright (c) 2002-2021 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 ch.cyberduck.core.ConnectionCallback; -import ch.cyberduck.core.Path; -import ch.cyberduck.core.exception.BackgroundException; -import ch.cyberduck.core.features.Write; -import ch.cyberduck.core.io.ChecksumCompute; -import ch.cyberduck.core.io.SHA256ChecksumCompute; -import ch.cyberduck.core.io.StatusOutputStream; -import ch.cyberduck.core.preferences.HostPreferencesFactory; -import ch.cyberduck.core.transfer.TransferStatus; - -import java.util.EnumSet; - -public class EueThresholdWriteFeature implements Write { - - private final EueSession session; - private final EueResourceIdProvider fileid; - private final Long threshold; - - public EueThresholdWriteFeature(final EueSession session, final EueResourceIdProvider fileid) { - this(session, fileid, HostPreferencesFactory.get(session.getHost()).getLong("eue.upload.multipart.threshold")); - } - - public EueThresholdWriteFeature(final EueSession session, final EueResourceIdProvider fileid, final Long threshold) { - this.session = session; - this.fileid = fileid; - this.threshold = threshold; - } - - @Override - public StatusOutputStream write(final Path file, final TransferStatus status, final ConnectionCallback callback) throws BackgroundException { - if(status.getLength() >= threshold) { - return new EueMultipartWriteFeature(session, fileid).write(file, status, callback); - } - return new EueWriteFeature(session, fileid).write(file, status, callback); - } - - @Override - public ChecksumCompute checksum(final Path file, final TransferStatus status) { - return new SHA256ChecksumCompute(); - } - - @Override - public EnumSet features(final Path file) { - return EnumSet.of(Flags.timestamp); - } -} diff --git a/eue/src/main/java/ch/cyberduck/core/eue/EueTouchFeature.java b/eue/src/main/java/ch/cyberduck/core/eue/EueTouchFeature.java index d5d4020b9d..71a790c8cf 100644 --- a/eue/src/main/java/ch/cyberduck/core/eue/EueTouchFeature.java +++ b/eue/src/main/java/ch/cyberduck/core/eue/EueTouchFeature.java @@ -28,7 +28,7 @@ import java.text.MessageFormat; public class EueTouchFeature extends DefaultTouchFeature { public EueTouchFeature(final EueSession session, final EueResourceIdProvider fileid) { - super(new EueWriteFeature(session, fileid)); + super(session); } @Override diff --git a/eue/src/main/java/ch/cyberduck/core/eue/EueTrashFeature.java b/eue/src/main/java/ch/cyberduck/core/eue/EueTrashFeature.java index 97d94a0da2..f7d85bf4bc 100644 --- a/eue/src/main/java/ch/cyberduck/core/eue/EueTrashFeature.java +++ b/eue/src/main/java/ch/cyberduck/core/eue/EueTrashFeature.java @@ -122,7 +122,7 @@ public class EueTrashFeature implements Trash { } @Override - public EnumSet features() { + public EnumSet features(final Path file) { return EnumSet.of(Flags.recursive); } } diff --git a/eue/src/main/java/ch/cyberduck/core/eue/EueUploadService.java b/eue/src/main/java/ch/cyberduck/core/eue/EueUploadService.java index 1df44af72a..236f3cc696 100644 --- a/eue/src/main/java/ch/cyberduck/core/eue/EueUploadService.java +++ b/eue/src/main/java/ch/cyberduck/core/eue/EueUploadService.java @@ -15,14 +15,13 @@ package ch.cyberduck.core.eue; * GNU General Public License for more details. */ -import ch.cyberduck.core.features.Write; import ch.cyberduck.core.http.HttpUploadFeature; import java.security.MessageDigest; public class EueUploadService extends HttpUploadFeature { - public EueUploadService(final EueSession session, final EueResourceIdProvider fileid, final Write writer) { - super(writer); + public EueUploadService(final EueSession session) { + } } diff --git a/eue/src/test/java/ch/cyberduck/core/cryptomator/EueSingleUploadServiceTest.java b/eue/src/test/java/ch/cyberduck/core/cryptomator/EueSingleUploadServiceTest.java index 9d5d45c7d5..883a1996c0 100644 --- a/eue/src/test/java/ch/cyberduck/core/cryptomator/EueSingleUploadServiceTest.java +++ b/eue/src/test/java/ch/cyberduck/core/cryptomator/EueSingleUploadServiceTest.java @@ -26,6 +26,7 @@ import ch.cyberduck.core.Local; import ch.cyberduck.core.Path; 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.eue.AbstractEueSessionTest; import ch.cyberduck.core.eue.EueAttributesFinderFeature; import ch.cyberduck.core.eue.EueDeleteFeature; @@ -68,7 +69,7 @@ public class EueSingleUploadServiceTest extends AbstractEueSessionTest { @Test public void testUploadVault() throws Exception { final EueResourceIdProvider fileid = new EueResourceIdProvider(session); - final Path container = new EueDirectoryFeature(session, fileid).mkdir(new Path(new AlphanumericRandomStringService().random(), EnumSet.of(AbstractPath.Type.directory)), new TransferStatus().setLength(0L)); + final Path container = new EueDirectoryFeature(session, fileid).mkdir(new EueWriteFeature(session, fileid), new Path(new AlphanumericRandomStringService().random(), EnumSet.of(AbstractPath.Type.directory)), new TransferStatus().setLength(0L)); final Path vault = new Path(container, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory)); final Path test = new Path(vault, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)); final CryptoVault cryptomator = new CryptoVault(vault); @@ -82,10 +83,10 @@ public class EueSingleUploadServiceTest extends AbstractEueSessionTest { writeStatus.setHeader(cryptomator.getFileHeaderCryptor().encryptHeader(header)); writeStatus.setLength(content.length); final BytecountStreamListener count = new BytecountStreamListener(); - final CryptoUploadFeature feature = new CryptoUploadFeature<>(session, - new EueSingleUploadService(session, fileid, new EueWriteFeature(session, fileid)), - new EueWriteFeature(session, fileid), cryptomator); - feature.upload(test, local, new BandwidthThrottle(BandwidthThrottle.UNLIMITED), new DisabledProgressListener(), count, writeStatus, null); + final CryptoUploadFeature feature = new CryptoUploadFeature<>(session, + new EueSingleUploadService(session, fileid), + cryptomator); + feature.upload(new CryptoWriteFeature<>(session, new EueWriteFeature(session, fileid), cryptomator), test, local, new BandwidthThrottle(BandwidthThrottle.UNLIMITED), new DisabledProgressListener(), count, writeStatus, null); assertEquals(content.length, count.getSent()); assertTrue(writeStatus.isComplete()); assertTrue(cryptomator.getFeature(session, Find.class, new EueFindFeature(session, fileid)).find(test)); diff --git a/eue/src/test/java/ch/cyberduck/core/cryptomator/EueThresholdUploadServiceTest.java b/eue/src/test/java/ch/cyberduck/core/cryptomator/EueThresholdUploadServiceTest.java index 0db0ee2cf1..9855b7e9cd 100644 --- a/eue/src/test/java/ch/cyberduck/core/cryptomator/EueThresholdUploadServiceTest.java +++ b/eue/src/test/java/ch/cyberduck/core/cryptomator/EueThresholdUploadServiceTest.java @@ -26,6 +26,7 @@ import ch.cyberduck.core.Path; 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.eue.AbstractEueSessionTest; import ch.cyberduck.core.eue.EueAttributesFinderFeature; import ch.cyberduck.core.eue.EueDeleteFeature; @@ -73,7 +74,7 @@ public class EueThresholdUploadServiceTest extends AbstractEueSessionTest { @Test public void testUploadVault() throws Exception { final EueResourceIdProvider fileid = new EueResourceIdProvider(session); - final Path container = new EueDirectoryFeature(session, fileid).mkdir(new Path(new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory)), new TransferStatus().setLength(0L)); + final Path container = new EueDirectoryFeature(session, fileid).mkdir(new EueWriteFeature(session, fileid), new Path(new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory)), new TransferStatus().setLength(0L)); final Path vault = new Path(container, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory)); final Path test = new Path(vault, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)); final CryptoVault cryptomator = new CryptoVault(vault); @@ -88,10 +89,10 @@ public class EueThresholdUploadServiceTest extends AbstractEueSessionTest { writeStatus.setHeader(cryptomator.getFileHeaderCryptor().encryptHeader(header)); writeStatus.setLength(content.length); final BytecountStreamListener count = new BytecountStreamListener(); - final CryptoUploadFeature feature = new CryptoUploadFeature<>(session, + final CryptoUploadFeature feature = new CryptoUploadFeature<>(session, new EueThresholdUploadService(session, fileid, registry), - new EueWriteFeature(session, fileid), cryptomator); - feature.upload(test, local, new BandwidthThrottle(BandwidthThrottle.UNLIMITED), new DisabledProgressListener(), count, writeStatus, new DisabledConnectionCallback()); + cryptomator); + feature.upload(new CryptoWriteFeature<>(session, new EueWriteFeature(session, fileid), cryptomator), test, local, new BandwidthThrottle(BandwidthThrottle.UNLIMITED), new DisabledProgressListener(), count, writeStatus, new DisabledConnectionCallback()); assertEquals(content.length, count.getSent()); assertTrue(writeStatus.isComplete()); assertTrue(cryptomator.getFeature(session, Find.class, new EueFindFeature(session, fileid)).find(test)); @@ -108,7 +109,7 @@ public class EueThresholdUploadServiceTest extends AbstractEueSessionTest { @Test public void testUploadVaultWithBulkFeature() throws Exception { final EueResourceIdProvider fileid = new EueResourceIdProvider(session); - final Path container = new EueDirectoryFeature(session, fileid).mkdir(new Path(new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory)), new TransferStatus().setLength(0L)); + final Path container = new EueDirectoryFeature(session, fileid).mkdir(new EueWriteFeature(session, fileid), new Path(new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory)), new TransferStatus().setLength(0L)); final Path vault = new Path(container, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory)); final Path test = new Path(vault, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)); final CryptoVault cryptomator = new CryptoVault(vault); @@ -122,13 +123,13 @@ public class EueThresholdUploadServiceTest extends AbstractEueSessionTest { final FileHeader header = cryptomator.getFileHeaderCryptor().create(); writeStatus.setHeader(cryptomator.getFileHeaderCryptor().encryptHeader(header)); writeStatus.setLength(content.length); - final CryptoBulkFeature> bulk = new CryptoBulkFeature<>(session, new DisabledBulkFeature(), new EueDeleteFeature(session, fileid), cryptomator); + final CryptoBulkFeature> bulk = new CryptoBulkFeature<>(session, new DisabledBulkFeature(), cryptomator); bulk.pre(Transfer.Type.upload, Collections.singletonMap(new TransferItem(test), writeStatus), new DisabledConnectionCallback()); final BytecountStreamListener count = new BytecountStreamListener(); - final CryptoUploadFeature feature = new CryptoUploadFeature<>(session, + final CryptoUploadFeature feature = new CryptoUploadFeature<>(session, new EueThresholdUploadService(session, fileid, registry), - new EueWriteFeature(session, fileid), cryptomator); - feature.upload(test, local, new BandwidthThrottle(BandwidthThrottle.UNLIMITED), new DisabledProgressListener(), count, writeStatus, new DisabledConnectionCallback()); + cryptomator); + feature.upload(new CryptoWriteFeature<>(session, new EueWriteFeature(session, fileid), cryptomator), test, local, new BandwidthThrottle(BandwidthThrottle.UNLIMITED), new DisabledProgressListener(), count, writeStatus, new DisabledConnectionCallback()); assertEquals(content.length, count.getSent()); assertTrue(writeStatus.isComplete()); assertTrue(cryptomator.getFeature(session, Find.class, new EueFindFeature(session, fileid)).find(test)); diff --git a/eue/src/test/java/ch/cyberduck/core/cryptomator/EueUploadServiceTest.java b/eue/src/test/java/ch/cyberduck/core/cryptomator/EueUploadServiceTest.java index ed4e0a25bb..27ecf04980 100644 --- a/eue/src/test/java/ch/cyberduck/core/cryptomator/EueUploadServiceTest.java +++ b/eue/src/test/java/ch/cyberduck/core/cryptomator/EueUploadServiceTest.java @@ -27,6 +27,7 @@ import ch.cyberduck.core.Path; 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.eue.AbstractEueSessionTest; import ch.cyberduck.core.eue.EueAttributesFinderFeature; import ch.cyberduck.core.eue.EueDeleteFeature; @@ -36,6 +37,7 @@ import ch.cyberduck.core.eue.EueMultipartWriteFeature; import ch.cyberduck.core.eue.EueReadFeature; import ch.cyberduck.core.eue.EueResourceIdProvider; import ch.cyberduck.core.eue.EueUploadService; +import ch.cyberduck.core.eue.EueWriteFeature; import ch.cyberduck.core.features.AttributesFinder; import ch.cyberduck.core.features.Delete; import ch.cyberduck.core.features.Find; @@ -74,7 +76,7 @@ public class EueUploadServiceTest extends AbstractEueSessionTest { @Test public void testUploadVault() throws Exception { final EueResourceIdProvider fileid = new EueResourceIdProvider(session); - final Path container = new EueDirectoryFeature(session, fileid).mkdir(new Path(new AlphanumericRandomStringService().random(), EnumSet.of(AbstractPath.Type.directory)), new TransferStatus().setLength(0L)); + final Path container = new EueDirectoryFeature(session, fileid).mkdir(new EueWriteFeature(session, fileid), new Path(new AlphanumericRandomStringService().random(), EnumSet.of(AbstractPath.Type.directory)), new TransferStatus().setLength(0L)); final Path vault = new Path(container, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory)); final Path test = new Path(vault, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)); final CryptoVault cryptomator = new CryptoVault(vault); @@ -88,10 +90,10 @@ public class EueUploadServiceTest extends AbstractEueSessionTest { writeStatus.setHeader(cryptomator.getFileHeaderCryptor().encryptHeader(header)); writeStatus.setLength(content.length); final BytecountStreamListener count = new BytecountStreamListener(); - final CryptoUploadFeature feature = new CryptoUploadFeature<>(session, - new EueUploadService(session, fileid, new EueMultipartWriteFeature(session, fileid)), - new EueMultipartWriteFeature(session, fileid), cryptomator); - feature.upload(test, local, new BandwidthThrottle(BandwidthThrottle.UNLIMITED), new DisabledProgressListener(), count, writeStatus, new DisabledConnectionCallback()); + final CryptoUploadFeature feature = new CryptoUploadFeature<>(session, + new EueUploadService(session), + cryptomator); + feature.upload(new CryptoWriteFeature<>(session, new EueMultipartWriteFeature(session, fileid), cryptomator), test, local, new BandwidthThrottle(BandwidthThrottle.UNLIMITED), new DisabledProgressListener(), count, writeStatus, new DisabledConnectionCallback()); assertEquals(content.length, count.getSent()); assertTrue(writeStatus.isComplete()); assertTrue(cryptomator.getFeature(session, Find.class, new EueFindFeature(session, fileid)).find(test)); @@ -108,7 +110,7 @@ public class EueUploadServiceTest extends AbstractEueSessionTest { @Test public void testUploadVaultWithBulkFeature() throws Exception { final EueResourceIdProvider fileid = new EueResourceIdProvider(session); - final Path container = new EueDirectoryFeature(session, fileid).mkdir(new Path(new AlphanumericRandomStringService().random(), EnumSet.of(AbstractPath.Type.directory)), new TransferStatus().setLength(0L)); + final Path container = new EueDirectoryFeature(session, fileid).mkdir(new EueWriteFeature(session, fileid), new Path(new AlphanumericRandomStringService().random(), EnumSet.of(AbstractPath.Type.directory)), new TransferStatus().setLength(0L)); final Path vault = new Path(container, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory)); final Path test = new Path(vault, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)); final CryptoVault cryptomator = new CryptoVault(vault); @@ -121,13 +123,13 @@ public class EueUploadServiceTest extends AbstractEueSessionTest { final FileHeader header = cryptomator.getFileHeaderCryptor().create(); writeStatus.setHeader(cryptomator.getFileHeaderCryptor().encryptHeader(header)); writeStatus.setLength(content.length); - final CryptoBulkFeature> bulk = new CryptoBulkFeature<>(session, new DisabledBulkFeature(), new EueDeleteFeature(session, fileid), cryptomator); + final CryptoBulkFeature> bulk = new CryptoBulkFeature<>(session, new DisabledBulkFeature(), cryptomator); bulk.pre(Transfer.Type.upload, Collections.singletonMap(new TransferItem(test), writeStatus), new DisabledConnectionCallback()); final BytecountStreamListener count = new BytecountStreamListener(); - final CryptoUploadFeature feature = new CryptoUploadFeature<>(session, - new EueUploadService(session, fileid, new EueMultipartWriteFeature(session, fileid)), - new EueMultipartWriteFeature(session, fileid), cryptomator); - feature.upload(test, local, new BandwidthThrottle(BandwidthThrottle.UNLIMITED), new DisabledProgressListener(), count, writeStatus, new DisabledConnectionCallback()); + final CryptoUploadFeature feature = new CryptoUploadFeature<>(session, + new EueUploadService(session), + cryptomator); + feature.upload(new CryptoWriteFeature<>(session, new EueMultipartWriteFeature(session, fileid), cryptomator), test, local, new BandwidthThrottle(BandwidthThrottle.UNLIMITED), new DisabledProgressListener(), count, writeStatus, new DisabledConnectionCallback()); assertEquals(content.length, count.getSent()); assertTrue(writeStatus.isComplete()); assertTrue(cryptomator.getFeature(session, Find.class, new EueFindFeature(session, fileid)).find(test)); diff --git a/eue/src/test/java/ch/cyberduck/core/eue/EueAttributesFinderFeatureTest.java b/eue/src/test/java/ch/cyberduck/core/eue/EueAttributesFinderFeatureTest.java index 5512d6541f..58b3a6fdf3 100644 --- a/eue/src/test/java/ch/cyberduck/core/eue/EueAttributesFinderFeatureTest.java +++ b/eue/src/test/java/ch/cyberduck/core/eue/EueAttributesFinderFeatureTest.java @@ -60,7 +60,7 @@ public class EueAttributesFinderFeatureTest extends AbstractEueSessionTest { public void testFindIfNoneMatch() throws Exception { final EueResourceIdProvider fileid = new EueResourceIdProvider(session); final EueWriteFeature writer = new EueWriteFeature(session, fileid); - final Path container = new EueDirectoryFeature(session, fileid).mkdir(new Path(new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory)), new TransferStatus()); + final Path container = new EueDirectoryFeature(session, fileid).mkdir(new EueWriteFeature(session, fileid), new Path(new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory)), new TransferStatus()); final EueAttributesFinderFeature feature = new EueAttributesFinderFeature(session, fileid); final long ts = feature.find(container).getModificationDate(); final Path file = new Path(container, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)); @@ -104,18 +104,18 @@ public class EueAttributesFinderFeatureTest extends AbstractEueSessionTest { final String rootEtag = feature.find(new Path("/", EnumSet.of(Path.Type.directory))).getETag(); assertNotNull(rootEtag); final long rootModificationDate = feature.find(new Path("/", EnumSet.of(Path.Type.directory))).getModificationDate(); - final Path firstlevel = new EueDirectoryFeature(session, fileid).mkdir(new Path(new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory)), new TransferStatus()); + final Path firstlevel = new EueDirectoryFeature(session, fileid).mkdir(new EueWriteFeature(session, fileid), new Path(new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory)), new TransferStatus()); final String firstLevelEtag = feature.find(firstlevel).getETag(); final Long firstLevelRevision = feature.find(firstlevel).getRevision(); assertNull(firstLevelRevision); final long firstLevelModificationDate = feature.find(firstlevel).getModificationDate(); assertNotNull(firstLevelEtag); - final Path secondlevel = new EueDirectoryFeature(session, fileid).mkdir(new Path(firstlevel, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory)), new TransferStatus()); + final Path secondlevel = new EueDirectoryFeature(session, fileid).mkdir(new EueWriteFeature(session, fileid), new Path(firstlevel, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory)), new TransferStatus()); final String secondLevelEtag = feature.find(secondlevel).getETag(); assertNotNull(secondLevelEtag); - final Path secondlevelSibling = new EueDirectoryFeature(session, fileid).mkdir(new Path(firstlevel, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory)), new TransferStatus()); + final Path secondlevelSibling = new EueDirectoryFeature(session, fileid).mkdir(new EueWriteFeature(session, fileid), new Path(firstlevel, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory)), new TransferStatus()); assertNotNull(secondlevelSibling); - final Path file = new EueTouchFeature(session, fileid).touch(new Path(secondlevel, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)), new TransferStatus()); + final Path file = new EueTouchFeature(session, fileid).touch(new EueWriteFeature(session, fileid), new Path(secondlevel, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)), new TransferStatus()); final String secondLevelSiblingEtag = feature.find(secondlevelSibling).getETag(); assertNotEquals(secondLevelEtag, feature.find(secondlevel).getETag()); assertNotEquals(firstLevelEtag, feature.find(firstlevel).getETag()); @@ -129,7 +129,7 @@ public class EueAttributesFinderFeatureTest extends AbstractEueSessionTest { @Test public void testFindFeatureForSharedFolder() throws Exception { final EueResourceIdProvider fileid = new EueResourceIdProvider(session); - final Path folder = new EueDirectoryFeature(session, fileid).mkdir(new Path(new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory)), new TransferStatus()); + final Path folder = new EueDirectoryFeature(session, fileid).mkdir(new EueWriteFeature(session, fileid), new Path(new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory)), new TransferStatus()); final ShareCreationResponseEntry shareCreationResponseEntry = createShare(fileid, folder); final String shareName = shareCreationResponseEntry.getEntity().getName(); final EueAttributesFinderFeature feature = new EueAttributesFinderFeature(session, fileid); @@ -142,7 +142,7 @@ public class EueAttributesFinderFeatureTest extends AbstractEueSessionTest { @Test public void testFindFeatureForSharedFile() throws Exception { final EueResourceIdProvider fileid = new EueResourceIdProvider(session); - final Path sourceFolder = new EueDirectoryFeature(session, fileid).mkdir(new Path(new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory)), new TransferStatus()); + final Path sourceFolder = new EueDirectoryFeature(session, fileid).mkdir(new EueWriteFeature(session, fileid), new Path(new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory)), new TransferStatus()); final Path file = new Path(sourceFolder, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)); createFile(fileid, file, RandomUtils.nextBytes(0)); assertTrue(new EueFindFeature(session, fileid).find(file)); diff --git a/eue/src/test/java/ch/cyberduck/core/eue/EueCopyFeatureTest.java b/eue/src/test/java/ch/cyberduck/core/eue/EueCopyFeatureTest.java index 52923fccc8..c23175252a 100644 --- a/eue/src/test/java/ch/cyberduck/core/eue/EueCopyFeatureTest.java +++ b/eue/src/test/java/ch/cyberduck/core/eue/EueCopyFeatureTest.java @@ -51,11 +51,11 @@ public class EueCopyFeatureTest extends AbstractEueSessionTest { final EueResourceIdProvider fileid = new EueResourceIdProvider(session); final Path sourceFolder = new Path(new AlphanumericRandomStringService().random(), EnumSet.of(AbstractPath.Type.directory)); final Path sourceFile = new Path(sourceFolder, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)); - new EueDirectoryFeature(session, fileid).mkdir(sourceFolder, new TransferStatus()); + new EueDirectoryFeature(session, fileid).mkdir(new EueWriteFeature(session, fileid), sourceFolder, new TransferStatus()); createFile(fileid, sourceFile, RandomUtils.nextBytes(1023)); assertTrue(new EueFindFeature(session, fileid).find(sourceFile)); final Path targetFolder = new EueDirectoryFeature(session, fileid).mkdir( - new Path(new AlphanumericRandomStringService().random(), EnumSet.of(AbstractPath.Type.directory)), new TransferStatus()); + new EueWriteFeature(session, fileid), new Path(new AlphanumericRandomStringService().random(), EnumSet.of(AbstractPath.Type.directory)), new TransferStatus()); final Path targetFile = new Path(targetFolder, sourceFile.getName(), EnumSet.of(AbstractPath.Type.file)); final Path copy = new EueCopyFeature(session, fileid).copy(sourceFile, targetFile, new TransferStatus(), new DisabledConnectionCallback(), new DisabledStreamListener()); assertTrue(new EueFindFeature(session, fileid).find(sourceFile)); @@ -75,9 +75,9 @@ public class EueCopyFeatureTest extends AbstractEueSessionTest { public void testCopyRecursive() throws Exception { final EueResourceIdProvider fileid = new EueResourceIdProvider(session); final Path testFolder = new EueDirectoryFeature(session, fileid).mkdir( - new Path(new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory)), new TransferStatus()); + new EueWriteFeature(session, fileid), new Path(new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory)), new TransferStatus()); final Path sourceFolder = new EueDirectoryFeature(session, fileid).mkdir( - new Path(testFolder, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory)), new TransferStatus()); + new EueWriteFeature(session, fileid), new Path(testFolder, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory)), new TransferStatus()); final Path sourceFile = new Path(sourceFolder, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)); createFile(fileid, sourceFile, RandomUtils.nextBytes(541)); final Path targetFolder = new Path(testFolder, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory)); @@ -98,7 +98,7 @@ public class EueCopyFeatureTest extends AbstractEueSessionTest { public void testCopyRecursiveToRoot() throws Exception { final EueResourceIdProvider fileid = new EueResourceIdProvider(session); final Path sourceFolder = new EueDirectoryFeature(session, fileid).mkdir( - new Path(new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory)), new TransferStatus()); + new EueWriteFeature(session, fileid), new Path(new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory)), new TransferStatus()); final Path sourceFile = new Path(sourceFolder, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)); createFile(fileid, sourceFile, RandomUtils.nextBytes(541)); final Path targetFolder = new Path(new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory)); @@ -119,7 +119,7 @@ public class EueCopyFeatureTest extends AbstractEueSessionTest { public void testCopyFileToRoot() throws Exception { final EueResourceIdProvider fileid = new EueResourceIdProvider(session); final Path sourceFolder = new EueDirectoryFeature(session, fileid).mkdir( - new Path(new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory)), new TransferStatus()); + new EueWriteFeature(session, fileid), new Path(new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory)), new TransferStatus()); final Path sourceFile = new Path(sourceFolder, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)); createFile(fileid, sourceFile, RandomUtils.nextBytes(541)); final PathAttributes sourceAttr = new EueAttributesFinderFeature(session, fileid).find(sourceFile); @@ -143,11 +143,11 @@ public class EueCopyFeatureTest extends AbstractEueSessionTest { final EueResourceIdProvider fileid = new EueResourceIdProvider(session); final Path sourceFolder = new Path(new AlphanumericRandomStringService().random(), EnumSet.of(AbstractPath.Type.directory)); final Path sourceFile = new Path(sourceFolder, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)); - new EueDirectoryFeature(session, fileid).mkdir(sourceFolder, new TransferStatus()); + new EueDirectoryFeature(session, fileid).mkdir(new EueWriteFeature(session, fileid), sourceFolder, new TransferStatus()); createFile(fileid, sourceFile, RandomUtils.nextBytes(1023)); assertTrue(new EueFindFeature(session, fileid).find(sourceFile)); final Path targetFolder = new EueDirectoryFeature(session, fileid).mkdir( - new Path(new AlphanumericRandomStringService().random(), EnumSet.of(AbstractPath.Type.directory)), new TransferStatus()); + new EueWriteFeature(session, fileid), new Path(new AlphanumericRandomStringService().random(), EnumSet.of(AbstractPath.Type.directory)), new TransferStatus()); final Path targetFile = new Path(targetFolder, new AlphanumericRandomStringService().random(), EnumSet.of(AbstractPath.Type.file)); final Path copy = new EueCopyFeature(session, fileid).copy(sourceFile, targetFile, new TransferStatus(), new DisabledConnectionCallback(), new DisabledStreamListener()); assertTrue(new EueFindFeature(session, fileid).find(sourceFile)); @@ -167,19 +167,20 @@ public class EueCopyFeatureTest extends AbstractEueSessionTest { public void testCopyToExistingFile() throws Exception { final EueResourceIdProvider fileid = new EueResourceIdProvider(session); final Path folder = new Path(new DefaultHomeFinderService(session).find(), new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory)); - new EueDirectoryFeature(session, fileid).mkdir(folder, new TransferStatus()); + new EueDirectoryFeature(session, fileid).mkdir(new EueWriteFeature(session, fileid), folder, new TransferStatus()); final Path test = new Path(folder, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)); final Local local = new Local(System.getProperty("java.io.tmpdir"), test.getName()); final byte[] random = RandomUtils.nextBytes(2547); IOUtils.write(random, local.getOutputStream(false)); final TransferStatus status = new TransferStatus().setLength(random.length); - final EueWriteFeature.Chunk upload = new EueSingleUploadService(session, fileid, new EueWriteFeature(session, fileid)).upload(test, local, new BandwidthThrottle(BandwidthThrottle.UNLIMITED), + final EueWriteFeature.Chunk upload = new EueSingleUploadService(session, fileid).upload(new EueWriteFeature(session, fileid), + test, local, new BandwidthThrottle(BandwidthThrottle.UNLIMITED), new DisabledProgressListener(), new DisabledStreamListener(), status, new DisabledLoginCallback()); assertNotNull(upload.getResourceId()); local.delete(); assertTrue(new EueFindFeature(session, fileid).find(test)); final Path copy = new Path(folder, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)); - new EueTouchFeature(session, fileid).touch(copy, new TransferStatus().setLength(0L)); + new EueTouchFeature(session, fileid).touch(new EueWriteFeature(session, fileid), copy, new TransferStatus().setLength(0L)); new EueCopyFeature(session, fileid).copy(test, copy, new TransferStatus().setExists(true), new DisabledConnectionCallback(), new DisabledStreamListener()); final Find find = new DefaultFindFeature(session); assertTrue(find.find(test)); diff --git a/eue/src/test/java/ch/cyberduck/core/eue/EueDeleteFeatureTest.java b/eue/src/test/java/ch/cyberduck/core/eue/EueDeleteFeatureTest.java index 6d698c21b2..3b522d5c02 100644 --- a/eue/src/test/java/ch/cyberduck/core/eue/EueDeleteFeatureTest.java +++ b/eue/src/test/java/ch/cyberduck/core/eue/EueDeleteFeatureTest.java @@ -17,6 +17,7 @@ package ch.cyberduck.core.eue; import ch.cyberduck.core.AbstractPath; import ch.cyberduck.core.AlphanumericRandomStringService; +import ch.cyberduck.core.DefaultPathAttributes; import ch.cyberduck.core.DisabledListProgressListener; import ch.cyberduck.core.DisabledLoginCallback; import ch.cyberduck.core.Path; @@ -44,7 +45,7 @@ public class EueDeleteFeatureTest extends AbstractEueSessionTest { @Test public void testDeleteFolder() throws Exception { final EueResourceIdProvider fileid = new EueResourceIdProvider(session); - final Path directory = new EueDirectoryFeature(session, fileid).mkdir(new Path( + final Path directory = new EueDirectoryFeature(session, fileid).mkdir(new EueWriteFeature(session, fileid), new Path( new AlphanumericRandomStringService().random(), EnumSet.of(AbstractPath.Type.directory)), new TransferStatus()); assertTrue(new EueFindFeature(session, fileid).find(directory, new DisabledListProgressListener())); new EueDeleteFeature(session, fileid).delete(Collections.singletonList(directory), new DisabledLoginCallback(), new Delete.DisabledCallback()); @@ -57,7 +58,7 @@ public class EueDeleteFeatureTest extends AbstractEueSessionTest { final Path folder = new Path(new AlphanumericRandomStringService().random(), EnumSet.of(AbstractPath.Type.directory)); final Path file1 = new Path(folder, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)); final Path file2 = new Path(folder, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)); - new EueDirectoryFeature(session, fileid).mkdir(folder, new TransferStatus()); + new EueDirectoryFeature(session, fileid).mkdir(new EueWriteFeature(session, fileid), folder, new TransferStatus()); createFile(fileid, file1, RandomUtils.nextBytes(511)); createFile(fileid, file2, RandomUtils.nextBytes(214)); assertTrue(new EueFindFeature(session, fileid).find(file1)); @@ -74,7 +75,7 @@ public class EueDeleteFeatureTest extends AbstractEueSessionTest { public void testDeleteLockOwnerFile() throws Exception { final EueResourceIdProvider fileid = new EueResourceIdProvider(session); final Path folder = new EueDirectoryFeature(session, fileid).mkdir( - new Path(new AlphanumericRandomStringService().random(), EnumSet.of(AbstractPath.Type.directory)), new TransferStatus()); + new EueWriteFeature(session, fileid), new Path(new AlphanumericRandomStringService().random(), EnumSet.of(AbstractPath.Type.directory)), new TransferStatus()); final String filename = String.format("~$%s.docx", new AlphanumericRandomStringService().random()); { final Path file1 = new Path(folder, filename, EnumSet.of(Path.Type.file)); @@ -115,12 +116,12 @@ public class EueDeleteFeatureTest extends AbstractEueSessionTest { @Test(expected = NotfoundException.class) public void testDoubleDelete() throws Exception { final EueResourceIdProvider fileid = new EueResourceIdProvider(session); - final Path file = new EueTouchFeature(session, fileid).touch(new Path(new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)), new TransferStatus()); + final Path file = new EueTouchFeature(session, fileid).touch(new EueWriteFeature(session, fileid), new Path(new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)), new TransferStatus()); final String resourceId = file.attributes().getFileId(); new EueDeleteFeature(session, fileid).delete(Collections.singletonList(file), new DisabledLoginCallback(), new Delete.DisabledCallback()); try { new EueDeleteFeature(session, fileid).delete(Collections.singletonList( - file.withAttributes(new PathAttributes().setFileId(resourceId))), new DisabledLoginCallback(), new Delete.DisabledCallback()); + file.withAttributes(new DefaultPathAttributes().setFileId(resourceId))), new DisabledLoginCallback(), new Delete.DisabledCallback()); fail(); } catch(NotfoundException e) { diff --git a/eue/src/test/java/ch/cyberduck/core/eue/EueDirectoryFeatureTest.java b/eue/src/test/java/ch/cyberduck/core/eue/EueDirectoryFeatureTest.java index bf375d32a9..9de0b7b4b3 100644 --- a/eue/src/test/java/ch/cyberduck/core/eue/EueDirectoryFeatureTest.java +++ b/eue/src/test/java/ch/cyberduck/core/eue/EueDirectoryFeatureTest.java @@ -41,8 +41,8 @@ public class EueDirectoryFeatureTest extends AbstractEueSessionTest { public void testAttributes() throws Exception { final EueResourceIdProvider fileid = new EueResourceIdProvider(session); final TransferStatus status = new TransferStatus(); - final Path directory = new EueDirectoryFeature(session, fileid).mkdir(new Path(new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory)), status); - assertThrows(ConflictException.class, () -> new EueDirectoryFeature(session, fileid).mkdir(directory, new TransferStatus())); + final Path directory = new EueDirectoryFeature(session, fileid).mkdir(new EueWriteFeature(session, fileid), new Path(new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory)), status); + assertThrows(ConflictException.class, () -> new EueDirectoryFeature(session, fileid).mkdir(new EueWriteFeature(session, fileid), directory, new TransferStatus())); assertEquals(new EueAttributesFinderFeature(session, fileid).find(directory).getFileId(), directory.attributes().getFileId()); new EueDeleteFeature(session, fileid).delete(Collections.singletonList(directory), new DisabledLoginCallback(), new Delete.DisabledCallback()); } @@ -51,7 +51,7 @@ public class EueDirectoryFeatureTest extends AbstractEueSessionTest { public void testProhibitedName() throws Exception { final EueResourceIdProvider fileid = new EueResourceIdProvider(session); try { - new EueDirectoryFeature(session, fileid).mkdir(new Path(String.format("%s.", new AlphanumericRandomStringService().random()), EnumSet.of(Path.Type.directory)), new TransferStatus()); + new EueDirectoryFeature(session, fileid).mkdir(new EueWriteFeature(session, fileid), new Path(String.format("%s.", new AlphanumericRandomStringService().random()), EnumSet.of(Path.Type.directory)), new TransferStatus()); } catch(InteroperabilityException e) { assertEquals("Paths may not end with a . Please contact your web hosting service provider for assistance.", e.getDetail()); @@ -63,8 +63,8 @@ public class EueDirectoryFeatureTest extends AbstractEueSessionTest { public void testCaseSensitivity() throws Exception { final EueResourceIdProvider fileid = new EueResourceIdProvider(session); final String filename = new AlphanumericRandomStringService().random(); - new EueDirectoryFeature(session, fileid).mkdir(new Path(StringUtils.capitalize(filename), EnumSet.of(Path.Type.directory)), new TransferStatus()); - assertThrows(ConflictException.class, () -> new EueDirectoryFeature(session, fileid).mkdir(new Path(StringUtils.lowerCase(filename), EnumSet.of(Path.Type.directory)), new TransferStatus())); + new EueDirectoryFeature(session, fileid).mkdir(new EueWriteFeature(session, fileid), new Path(StringUtils.capitalize(filename), EnumSet.of(Path.Type.directory)), new TransferStatus()); + assertThrows(ConflictException.class, () -> new EueDirectoryFeature(session, fileid).mkdir(new EueWriteFeature(session, fileid), new Path(StringUtils.lowerCase(filename), EnumSet.of(Path.Type.directory)), new TransferStatus())); new EueDeleteFeature(session, fileid).delete(Collections.singletonList(new Path(StringUtils.capitalize(filename), EnumSet.of(Path.Type.directory))), new DisabledLoginCallback(), new Delete.DisabledCallback()); } } \ No newline at end of file diff --git a/eue/src/test/java/ch/cyberduck/core/eue/EueFindFeatureTest.java b/eue/src/test/java/ch/cyberduck/core/eue/EueFindFeatureTest.java index 301197b873..21c3a8cf04 100644 --- a/eue/src/test/java/ch/cyberduck/core/eue/EueFindFeatureTest.java +++ b/eue/src/test/java/ch/cyberduck/core/eue/EueFindFeatureTest.java @@ -64,7 +64,7 @@ public class EueFindFeatureTest extends AbstractEueSessionTest { public void testFindDirectory() throws Exception { final EueResourceIdProvider fileid = new EueResourceIdProvider(session); final Path folder = new EueDirectoryFeature(session, fileid).mkdir( - new Path(new DefaultHomeFinderService(session).find(), new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory)), new TransferStatus()); + new EueWriteFeature(session, fileid), new Path(new DefaultHomeFinderService(session).find(), new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory)), new TransferStatus()); assertTrue(new EueFindFeature(session, fileid).find(folder)); assertFalse(new EueFindFeature(session, fileid).find(new Path(folder.getAbsolute(), EnumSet.of(Path.Type.file)))); new EueDeleteFeature(session, fileid).delete(Collections.singletonList(folder), new DisabledLoginCallback(), new Delete.DisabledCallback()); @@ -74,7 +74,7 @@ public class EueFindFeatureTest extends AbstractEueSessionTest { public void testFindFile() throws Exception { final EueResourceIdProvider fileid = new EueResourceIdProvider(session); final Path file = new Path(new DefaultHomeFinderService(session).find(), new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)); - new EueTouchFeature(session, fileid).touch(file, new TransferStatus()); + new EueTouchFeature(session, fileid).touch(new EueWriteFeature(session, fileid), file, new TransferStatus()); assertTrue(new EueFindFeature(session, fileid).find(file)); assertFalse(new EueFindFeature(session, fileid).find(new Path(file.getAbsolute(), EnumSet.of(Path.Type.directory)))); new EueDeleteFeature(session, fileid).delete(Collections.singletonList(file), new DisabledLoginCallback(), new Delete.DisabledCallback()); @@ -83,18 +83,18 @@ public class EueFindFeatureTest extends AbstractEueSessionTest { @Test public void testFind() throws Exception { final EueResourceIdProvider fileid = new EueResourceIdProvider(session); - final Path folder1 = new EueDirectoryFeature(session, fileid).mkdir(new Path( + final Path folder1 = new EueDirectoryFeature(session, fileid).mkdir(new EueWriteFeature(session, fileid), new Path( new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory)), new TransferStatus()); assertTrue(new EueFindFeature(session, fileid).find(folder1, new DisabledListProgressListener())); // Test case insensitivity assertTrue(new EueFindFeature(session, fileid).find(new Path(StringUtils.lowerCase(folder1.getName()), EnumSet.of(Path.Type.directory)), new DisabledListProgressListener())); assertTrue(new EueFindFeature(session, fileid).find(new Path(StringUtils.upperCase(folder1.getName()), EnumSet.of(Path.Type.directory)), new DisabledListProgressListener())); assertTrue(new DefaultFindFeature(session).find(folder1, new DisabledListProgressListener())); - final Path folder1Folder2 = new EueDirectoryFeature(session, fileid).mkdir(new Path(folder1, + final Path folder1Folder2 = new EueDirectoryFeature(session, fileid).mkdir(new EueWriteFeature(session, fileid), new Path(folder1, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory)), new TransferStatus()); assertTrue(new EueFindFeature(session, fileid).find(folder1Folder2, new DisabledListProgressListener())); assertTrue(new DefaultFindFeature(session).find(folder1Folder2, new DisabledListProgressListener())); - final Path folder1Folder2Folder3 = new EueDirectoryFeature(session, fileid).mkdir(new Path(folder1Folder2, + final Path folder1Folder2Folder3 = new EueDirectoryFeature(session, fileid).mkdir(new EueWriteFeature(session, fileid), new Path(folder1Folder2, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory)), new TransferStatus()); assertEquals(folder1Folder2Folder3.attributes().getFileId(), new EueResourceIdProvider(session).getFileId(folder1Folder2Folder3)); assertTrue(new EueFindFeature(session, fileid).find(folder1Folder2Folder3, new DisabledListProgressListener())); diff --git a/eue/src/test/java/ch/cyberduck/core/eue/EueLargeUploadServiceTest.java b/eue/src/test/java/ch/cyberduck/core/eue/EueLargeUploadServiceTest.java index 74564725be..c0a5992bc8 100644 --- a/eue/src/test/java/ch/cyberduck/core/eue/EueLargeUploadServiceTest.java +++ b/eue/src/test/java/ch/cyberduck/core/eue/EueLargeUploadServiceTest.java @@ -45,8 +45,8 @@ public class EueLargeUploadServiceTest extends AbstractEueSessionTest { @Test public void testUploadLargeFileInChunks() throws Exception { final EueResourceIdProvider fileid = new EueResourceIdProvider(session); - final EueLargeUploadService s = new EueLargeUploadService(session, fileid, new EueWriteFeature(session, fileid)); - final Path container = new EueDirectoryFeature(session, fileid).mkdir(new Path( + final EueLargeUploadService s = new EueLargeUploadService(session, fileid); + final Path container = new EueDirectoryFeature(session, fileid).mkdir(new EueWriteFeature(session, fileid), new Path( new AlphanumericRandomStringService().random(), EnumSet.of(AbstractPath.Type.directory)), new TransferStatus()); final String name = new AlphanumericRandomStringService().random(); final Path file = new Path(container, name, EnumSet.of(Path.Type.file)); @@ -56,7 +56,7 @@ public class EueLargeUploadServiceTest extends AbstractEueSessionTest { final TransferStatus status = new TransferStatus(); status.setLength(content.length); final BytecountStreamListener count = new BytecountStreamListener(); - final EueWriteFeature.Chunk uploadResponse = s.upload(file, local, new BandwidthThrottle(BandwidthThrottle.UNLIMITED), new DisabledProgressListener(), count, status, new DisabledConnectionCallback()); + final EueWriteFeature.Chunk uploadResponse = s.upload(new EueWriteFeature(session, fileid), file, local, new BandwidthThrottle(BandwidthThrottle.UNLIMITED), new DisabledProgressListener(), count, status, new DisabledConnectionCallback()); assertNotNull(uploadResponse.getCdash64()); assertEquals(content.length, count.getSent()); assertEquals(PathAttributes.EMPTY, status.getResponse()); diff --git a/eue/src/test/java/ch/cyberduck/core/eue/EueListServiceTest.java b/eue/src/test/java/ch/cyberduck/core/eue/EueListServiceTest.java index 00b7db7232..54568e7212 100644 --- a/eue/src/test/java/ch/cyberduck/core/eue/EueListServiceTest.java +++ b/eue/src/test/java/ch/cyberduck/core/eue/EueListServiceTest.java @@ -17,6 +17,7 @@ package ch.cyberduck.core.eue; import ch.cyberduck.core.AlphanumericRandomStringService; import ch.cyberduck.core.AttributedList; +import ch.cyberduck.core.DefaultPathAttributes; import ch.cyberduck.core.DefaultPathPredicate; import ch.cyberduck.core.DescriptiveUrl; import ch.cyberduck.core.DisabledListProgressListener; @@ -51,14 +52,14 @@ public class EueListServiceTest extends AbstractEueSessionTest { final EueResourceIdProvider fileid = new EueResourceIdProvider(session); final Path root = new Path("/", EnumSet.of(directory)); final Path folder = new EueDirectoryFeature(session, fileid).mkdir( - new Path(root, new AlphanumericRandomStringService().random(), EnumSet.of(directory)), new TransferStatus()); + new EueWriteFeature(session, fileid), new Path(root, new AlphanumericRandomStringService().random(), EnumSet.of(directory)), new TransferStatus()); final AttributedList list = new EueListService(session, fileid).list(folder.getParent(), new DisabledListProgressListener()); assertNotNull(list.find(new SimplePathPredicate(folder))); for(Path bucket : list) { assertEquals(bucket.attributes(), new EueAttributesFinderFeature(session, fileid).find(bucket, new DisabledListProgressListener())); } assertNotNull(list.find(f -> f.attributes().getFileId().equals(EueResourceIdProvider.TRASH))); - assertTrue(list.contains(new Path("Gelöschte Dateien", EnumSet.of(directory)).withAttributes(new PathAttributes().setFileId("TRASH")))); + assertTrue(list.contains(new Path("Gelöschte Dateien", EnumSet.of(directory)).withAttributes(new DefaultPathAttributes().setFileId("TRASH")))); assertEquals(folder.attributes().getFileId(), list.find(new SimplePathPredicate(folder)).attributes().getFileId()); assertSame(root, list.find(new SimplePathPredicate(folder)).getParent()); new EueDeleteFeature(session, fileid).delete(Collections.singletonList(folder), new DisabledLoginCallback(), new Delete.DisabledCallback()); @@ -74,7 +75,7 @@ public class EueListServiceTest extends AbstractEueSessionTest { @Test public void testListForSharedFile() throws Exception { final EueResourceIdProvider fileid = new EueResourceIdProvider(session); - final Path sourceFolder = new EueDirectoryFeature(session, fileid).mkdir(new Path(new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory)), new TransferStatus()); + final Path sourceFolder = new EueDirectoryFeature(session, fileid).mkdir(new EueWriteFeature(session, fileid), new Path(new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory)), new TransferStatus()); final Path file = new Path(sourceFolder, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)); createFile(fileid, file, RandomUtils.nextBytes(0)); assertTrue(new EueFindFeature(session, fileid).find(file)); @@ -89,8 +90,8 @@ public class EueListServiceTest extends AbstractEueSessionTest { @Test public void testListForSharedFolder() throws Exception { final EueResourceIdProvider fileid = new EueResourceIdProvider(session); - final Path sourceFolder = new EueDirectoryFeature(session, fileid).mkdir(new Path(new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory)), new TransferStatus()); - final Path folder2 = new EueDirectoryFeature(session, fileid).mkdir(new Path(sourceFolder, new AlphanumericRandomStringService().random(), EnumSet.of(directory)), new TransferStatus()); + final Path sourceFolder = new EueDirectoryFeature(session, fileid).mkdir(new EueWriteFeature(session, fileid), new Path(new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory)), new TransferStatus()); + final Path folder2 = new EueDirectoryFeature(session, fileid).mkdir(new EueWriteFeature(session, fileid), new Path(sourceFolder, new AlphanumericRandomStringService().random(), EnumSet.of(directory)), new TransferStatus()); assertTrue(new EueFindFeature(session, fileid).find(folder2)); final ShareCreationResponseEntry shareCreationResponseEntry = createShare(fileid, folder2); final String shareName = shareCreationResponseEntry.getEntity().getName(); @@ -104,7 +105,7 @@ public class EueListServiceTest extends AbstractEueSessionTest { public void testListContainingFolder() throws Exception { final EueResourceIdProvider fileid = new EueResourceIdProvider(session); final Path folder = new EueDirectoryFeature(session, fileid).mkdir( - new Path(new AlphanumericRandomStringService().random(), EnumSet.of(directory)), new TransferStatus()); + new EueWriteFeature(session, fileid), new Path(new AlphanumericRandomStringService().random(), EnumSet.of(directory)), new TransferStatus()); final AtomicBoolean callback = new AtomicBoolean(); assertTrue(new EueListService(session, fileid).list(folder, new DisabledListProgressListener() { @Override @@ -115,7 +116,7 @@ public class EueListServiceTest extends AbstractEueSessionTest { }).isEmpty()); assertTrue(callback.get()); final Path subfolder = new EueDirectoryFeature(session, fileid).mkdir( - new Path(folder, new AlphanumericRandomStringService().random(), EnumSet.of(directory)), new TransferStatus()); + new EueWriteFeature(session, fileid), new Path(folder, new AlphanumericRandomStringService().random(), EnumSet.of(directory)), new TransferStatus()); assertTrue(new EueListService(session, fileid).list(subfolder, new DisabledListProgressListener()).isEmpty()); final AttributedList list = new EueListService(session, fileid).list(folder, new DisabledListProgressListener()); assertFalse(list.isEmpty()); @@ -131,11 +132,11 @@ public class EueListServiceTest extends AbstractEueSessionTest { public void testListContainingFile() throws Exception { final EueResourceIdProvider fileid = new EueResourceIdProvider(session); final Path folder = new EueDirectoryFeature(session, fileid).mkdir( - new Path(new AlphanumericRandomStringService().random(), EnumSet.of(directory)), new TransferStatus()); + new EueWriteFeature(session, fileid), new Path(new AlphanumericRandomStringService().random(), EnumSet.of(directory)), new TransferStatus()); assertTrue(new EueListService(session, fileid).list(folder, new DisabledListProgressListener()).isEmpty()); final String filename = String.format("%s%s", new AlphanumericRandomStringService().random(), new NFDNormalizer().normalize("ä")); final Path file = new EueTouchFeature(session, fileid) - .touch(new Path(folder, filename, EnumSet.of(Path.Type.file)), new TransferStatus().setLength(0L)); + .touch(new EueWriteFeature(session, fileid), new Path(folder, filename, EnumSet.of(Path.Type.file)), new TransferStatus().setLength(0L)); final AttributedList list = new EueListService(session, fileid).list(folder, new DisabledListProgressListener()); assertFalse(list.isEmpty()); assertNotNull(list.find(new DefaultPathPredicate(file))); diff --git a/eue/src/test/java/ch/cyberduck/core/eue/EueMoveFeatureTest.java b/eue/src/test/java/ch/cyberduck/core/eue/EueMoveFeatureTest.java index 76d3ed5e41..bc945f957c 100644 --- a/eue/src/test/java/ch/cyberduck/core/eue/EueMoveFeatureTest.java +++ b/eue/src/test/java/ch/cyberduck/core/eue/EueMoveFeatureTest.java @@ -16,6 +16,7 @@ package ch.cyberduck.core.eue; */ import ch.cyberduck.core.AlphanumericRandomStringService; +import ch.cyberduck.core.DefaultPathAttributes; import ch.cyberduck.core.DisabledConnectionCallback; import ch.cyberduck.core.DisabledLoginCallback; import ch.cyberduck.core.Path; @@ -44,13 +45,13 @@ public class EueMoveFeatureTest extends AbstractEueSessionTest { public void testMoveFile() throws Exception { final EueResourceIdProvider fileid = new EueResourceIdProvider(session); final Path sourceFolder = new EueDirectoryFeature(session, fileid).mkdir( - new Path(new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory)), new TransferStatus()); + new EueWriteFeature(session, fileid), new Path(new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory)), new TransferStatus()); final Path sourceFile = new Path(sourceFolder, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)); createFile(fileid, sourceFile, RandomUtils.nextBytes(541)); final PathAttributes sourceAttr = new EueAttributesFinderFeature(session, fileid).find(sourceFile); assertTrue(new EueFindFeature(session, fileid).find(sourceFile)); final Path targetFolder = new EueDirectoryFeature(session, fileid).mkdir( - new Path(new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory)), new TransferStatus()); + new EueWriteFeature(session, fileid), new Path(new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory)), new TransferStatus()); final Path targetFile = new EueMoveFeature(session, fileid).move(sourceFile, new Path(targetFolder, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)), new TransferStatus(), new Delete.DisabledCallback(), new DisabledConnectionCallback()); assertFalse(new EueFindFeature(session, fileid).find(sourceFile)); @@ -68,7 +69,7 @@ public class EueMoveFeatureTest extends AbstractEueSessionTest { public void testMoveFileOverride() throws Exception { final EueResourceIdProvider fileid = new EueResourceIdProvider(session); final Path folder = new EueDirectoryFeature(session, fileid).mkdir( - new Path(new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory)), new TransferStatus()); + new EueWriteFeature(session, fileid), new Path(new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory)), new TransferStatus()); final Path sourceFile = new Path(folder, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)); createFile(fileid, sourceFile, RandomUtils.nextBytes(48)); final Path targetFile = new Path(folder, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)); @@ -83,7 +84,7 @@ public class EueMoveFeatureTest extends AbstractEueSessionTest { public void testMoveRecursive() throws Exception { final EueResourceIdProvider fileid = new EueResourceIdProvider(session); final Path sourceFolder = new EueDirectoryFeature(session, fileid).mkdir( - new Path(new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory)), new TransferStatus()); + new EueWriteFeature(session, fileid), new Path(new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory)), new TransferStatus()); final Path sourceFile = new Path(sourceFolder, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)); createFile(fileid, sourceFile, RandomUtils.nextBytes(541)); final Path targetFolder = new Path(new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory)); @@ -103,7 +104,7 @@ public class EueMoveFeatureTest extends AbstractEueSessionTest { public void testMoveFileToRoot() throws Exception { final EueResourceIdProvider fileid = new EueResourceIdProvider(session); final Path sourceFolder = new EueDirectoryFeature(session, fileid).mkdir( - new Path(new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory)), new TransferStatus()); + new EueWriteFeature(session, fileid), new Path(new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory)), new TransferStatus()); final Path sourceFile = new Path(sourceFolder, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)); createFile(fileid, sourceFile, RandomUtils.nextBytes(541)); final PathAttributes sourceAttr = new EueAttributesFinderFeature(session, fileid).find(sourceFile); @@ -135,12 +136,12 @@ public class EueMoveFeatureTest extends AbstractEueSessionTest { public void testMoveInvalidResourceId() throws Exception { final EueResourceIdProvider fileid = new EueResourceIdProvider(session); final Path folder = new EueDirectoryFeature(session, fileid).mkdir( - new Path(new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory)), new TransferStatus()); - final Path file = new EueTouchFeature(session, fileid).touch(new Path(folder, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)), new TransferStatus()); + new EueWriteFeature(session, fileid), new Path(new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory)), new TransferStatus()); + final Path file = new EueTouchFeature(session, fileid).touch(new EueWriteFeature(session, fileid), new Path(folder, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)), new TransferStatus()); final String resourceId = file.attributes().getFileId(); new EueDeleteFeature(session, fileid).delete(Collections.singletonList(file), new DisabledLoginCallback(), new Delete.DisabledCallback()); try { - new EueMoveFeature(session, fileid).move(file.withAttributes(new PathAttributes().setFileId(resourceId)), + new EueMoveFeature(session, fileid).move(file.withAttributes(new DefaultPathAttributes().setFileId(resourceId)), new Path(new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)), new TransferStatus(), new Delete.DisabledCallback(), new DisabledConnectionCallback()); fail(); } @@ -152,10 +153,10 @@ public class EueMoveFeatureTest extends AbstractEueSessionTest { @Test(expected = NotfoundException.class) public void testRenameInvalidResourceId() throws Exception { final EueResourceIdProvider fileid = new EueResourceIdProvider(session); - final Path file = new EueTouchFeature(session, fileid).touch(new Path(new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)), new TransferStatus()); + final Path file = new EueTouchFeature(session, fileid).touch(new EueWriteFeature(session, fileid), new Path(new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)), new TransferStatus()); final String resourceId = file.attributes().getFileId(); new EueDeleteFeature(session, fileid).delete(Collections.singletonList(file), new DisabledLoginCallback(), new Delete.DisabledCallback()); - new EueMoveFeature(session, fileid).move(file.withAttributes(new PathAttributes().setFileId(resourceId)), + new EueMoveFeature(session, fileid).move(file.withAttributes(new DefaultPathAttributes().setFileId(resourceId)), new Path(new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)), new TransferStatus(), new Delete.DisabledCallback(), new DisabledConnectionCallback()); } @@ -163,7 +164,7 @@ public class EueMoveFeatureTest extends AbstractEueSessionTest { public void testRenameCaseOnly() throws Exception { final EueResourceIdProvider fileid = new EueResourceIdProvider(session); final String name = new AlphanumericRandomStringService().random(); - final Path file = new EueTouchFeature(session, fileid).touch(new Path(StringUtils.capitalize(name), EnumSet.of(Path.Type.file)), new TransferStatus()); + final Path file = new EueTouchFeature(session, fileid).touch(new EueWriteFeature(session, fileid), new Path(StringUtils.capitalize(name), EnumSet.of(Path.Type.file)), new TransferStatus()); final Path rename = new Path(StringUtils.lowerCase(name), EnumSet.of(Path.Type.file)); new EueMoveFeature(session, fileid).move(file, rename, new TransferStatus().setExists(true), new Delete.DisabledCallback(), new DisabledConnectionCallback()); new EueDeleteFeature(session, fileid).delete(Collections.singletonList(rename), new DisabledLoginCallback(), new Delete.DisabledCallback()); diff --git a/eue/src/test/java/ch/cyberduck/core/eue/EueMultipartWriteFeatureTest.java b/eue/src/test/java/ch/cyberduck/core/eue/EueMultipartWriteFeatureTest.java index 2303dd0b4d..8535e087dd 100644 --- a/eue/src/test/java/ch/cyberduck/core/eue/EueMultipartWriteFeatureTest.java +++ b/eue/src/test/java/ch/cyberduck/core/eue/EueMultipartWriteFeatureTest.java @@ -49,7 +49,7 @@ public class EueMultipartWriteFeatureTest extends AbstractEueSessionTest { public void testWriteZeroLength() throws Exception { final EueResourceIdProvider fileid = new EueResourceIdProvider(session); final EueMultipartWriteFeature feature = new EueMultipartWriteFeature(session, fileid); - final Path container = new EueDirectoryFeature(session, fileid).mkdir(new Path(new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory)), new TransferStatus()); + final Path container = new EueDirectoryFeature(session, fileid).mkdir(new EueWriteFeature(session, fileid), new Path(new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory)), new TransferStatus()); final byte[] content = RandomUtils.nextBytes(0); final TransferStatus status = new TransferStatus().setLength(-1L); final Path file = new Path(container, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)); @@ -72,7 +72,7 @@ public class EueMultipartWriteFeatureTest extends AbstractEueSessionTest { // Uploading a file via the Upload Resource, using the chunked upload method, is only allowed for documents bigger than the chunksize (4MiB) final EueResourceIdProvider fileid = new EueResourceIdProvider(session); final EueMultipartWriteFeature feature = new EueMultipartWriteFeature(session, fileid); - final Path container = new EueDirectoryFeature(session, fileid).mkdir(new Path(new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory)), new TransferStatus()); + final Path container = new EueDirectoryFeature(session, fileid).mkdir(new EueWriteFeature(session, fileid), new Path(new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory)), new TransferStatus()); final Path file = new Path(container, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)); final byte[] content = RandomUtils.nextBytes(100072); // Multipart @@ -102,7 +102,7 @@ public class EueMultipartWriteFeatureTest extends AbstractEueSessionTest { // Uploading a file via the Upload Resource, using the chunked upload method, is only allowed for documents bigger than the chunksize (4MiB) final EueResourceIdProvider fileid = new EueResourceIdProvider(session); final EueMultipartWriteFeature feature = new EueMultipartWriteFeature(session, fileid); - final Path container = new EueDirectoryFeature(session, fileid).mkdir(new Path(new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory)), new TransferStatus()); + final Path container = new EueDirectoryFeature(session, fileid).mkdir(new EueWriteFeature(session, fileid), new Path(new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory)), new TransferStatus()); final Path file = new Path(container, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)); final byte[] content = RandomUtils.nextBytes(512000); final long ts = System.currentTimeMillis(); @@ -134,7 +134,7 @@ public class EueMultipartWriteFeatureTest extends AbstractEueSessionTest { public void testMultipartWrite() throws Exception { final EueResourceIdProvider fileid = new EueResourceIdProvider(session); final EueMultipartWriteFeature feature = new EueMultipartWriteFeature(session, fileid); - final Path container = new EueDirectoryFeature(session, fileid).mkdir(new Path(new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory)), new TransferStatus()); + final Path container = new EueDirectoryFeature(session, fileid).mkdir(new EueWriteFeature(session, fileid), new Path(new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory)), new TransferStatus()); final Path file = new Path(container, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)); final long timestamp = System.currentTimeMillis(); { diff --git a/eue/src/test/java/ch/cyberduck/core/eue/EueReadFeatureTest.java b/eue/src/test/java/ch/cyberduck/core/eue/EueReadFeatureTest.java index 05a95f5fbf..a19a80c7f9 100644 --- a/eue/src/test/java/ch/cyberduck/core/eue/EueReadFeatureTest.java +++ b/eue/src/test/java/ch/cyberduck/core/eue/EueReadFeatureTest.java @@ -45,7 +45,7 @@ public class EueReadFeatureTest extends AbstractEueSessionTest { @Test public void testRead() throws Exception { final EueResourceIdProvider fileid = new EueResourceIdProvider(session); - final Path container = new EueDirectoryFeature(session, fileid).mkdir(new Path(new AlphanumericRandomStringService().random(), EnumSet.of(AbstractPath.Type.directory)), new TransferStatus()); + final Path container = new EueDirectoryFeature(session, fileid).mkdir(new EueWriteFeature(session, fileid), new Path(new AlphanumericRandomStringService().random(), EnumSet.of(AbstractPath.Type.directory)), new TransferStatus()); final Path file = new Path(container, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)); final byte[] content = RandomUtils.nextBytes(5423); createFile(fileid, file, content); @@ -64,7 +64,7 @@ public class EueReadFeatureTest extends AbstractEueSessionTest { public void testReadRange() throws Exception { final EueResourceIdProvider fileid = new EueResourceIdProvider(session); final Path container = new EueDirectoryFeature(session, fileid).mkdir( - new Path(new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory, Path.Type.volume)), new TransferStatus()); + new EueWriteFeature(session, fileid), new Path(new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory, Path.Type.volume)), new TransferStatus()); final byte[] content = RandomUtils.nextBytes(1000); final Path test = createFile(fileid, new Path(container, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)), content); final TransferStatus status = new TransferStatus(); @@ -87,7 +87,7 @@ public class EueReadFeatureTest extends AbstractEueSessionTest { final EueResourceIdProvider fileid = new EueResourceIdProvider(session); final byte[] content = RandomUtils.nextBytes(32769); final Path container = new EueDirectoryFeature(session, fileid).mkdir( - new Path(new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory, Path.Type.volume)), new TransferStatus()); + new EueWriteFeature(session, fileid), new Path(new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory, Path.Type.volume)), new TransferStatus()); final Path test = createFile(fileid, new Path(container, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)), content); // Unknown length in status final TransferStatus readStatus = new TransferStatus(); diff --git a/eue/src/test/java/ch/cyberduck/core/eue/EueResourceIdProviderTest.java b/eue/src/test/java/ch/cyberduck/core/eue/EueResourceIdProviderTest.java index 42096e7667..fd295b76e0 100644 --- a/eue/src/test/java/ch/cyberduck/core/eue/EueResourceIdProviderTest.java +++ b/eue/src/test/java/ch/cyberduck/core/eue/EueResourceIdProviderTest.java @@ -57,7 +57,7 @@ public class EueResourceIdProviderTest extends AbstractEueSessionTest { @Test public void testFindCaseInsensitive() throws Exception { final EueResourceIdProvider fileid = new EueResourceIdProvider(session); - final Path folder = new EueDirectoryFeature(session, fileid).mkdir(new Path(new AlphanumericRandomStringService().random(), EnumSet.of(AbstractPath.Type.directory)), new TransferStatus()); + final Path folder = new EueDirectoryFeature(session, fileid).mkdir(new EueWriteFeature(session, fileid), new Path(new AlphanumericRandomStringService().random(), EnumSet.of(AbstractPath.Type.directory)), new TransferStatus()); folder.withAttributes(new EueAttributesFinderFeature(session, fileid).find(folder)); assertEquals(folder.attributes().getFileId(), fileid.getFileId(folder)); assertEquals(folder.attributes().getFileId(), fileid.getFileId(new Path(StringUtils.lowerCase(folder.getAbsolute()), folder.getType()))); diff --git a/eue/src/test/java/ch/cyberduck/core/eue/EueShareFeatureTest.java b/eue/src/test/java/ch/cyberduck/core/eue/EueShareFeatureTest.java index ae6a1608e9..3b2fa276b0 100644 --- a/eue/src/test/java/ch/cyberduck/core/eue/EueShareFeatureTest.java +++ b/eue/src/test/java/ch/cyberduck/core/eue/EueShareFeatureTest.java @@ -43,7 +43,7 @@ public class EueShareFeatureTest extends AbstractEueSessionTest { @Test(expected = InteroperabilityException.class) public void testInvalidPin() throws Exception { final EueResourceIdProvider fileid = new EueResourceIdProvider(session); - final Path sourceFolder = new EueDirectoryFeature(session, fileid).mkdir(new Path(new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory)), new TransferStatus()); + final Path sourceFolder = new EueDirectoryFeature(session, fileid).mkdir(new EueWriteFeature(session, fileid), new Path(new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory)), new TransferStatus()); final EueShareFeature feature = new EueShareFeature(session, fileid); try { feature.toDownloadUrl(sourceFolder, Share.Sharee.world, null, new DisabledPasswordCallback() { @@ -65,7 +65,7 @@ public class EueShareFeatureTest extends AbstractEueSessionTest { @Test public void testDownloadUrlForContainer() throws Exception { final EueResourceIdProvider fileid = new EueResourceIdProvider(session); - final Path sourceFolder = new EueDirectoryFeature(session, fileid).mkdir(new Path(new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory)), new TransferStatus()); + final Path sourceFolder = new EueDirectoryFeature(session, fileid).mkdir(new EueWriteFeature(session, fileid), new Path(new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory)), new TransferStatus()); final EueShareFeature feature = new EueShareFeature(session, fileid); final DescriptiveUrl url = feature.toDownloadUrl(sourceFolder, Share.Sharee.world, null, new DisabledPasswordCallback() { @Override @@ -88,7 +88,7 @@ public class EueShareFeatureTest extends AbstractEueSessionTest { @Test public void testDownloadUrlForFile() throws Exception { final EueResourceIdProvider fileid = new EueResourceIdProvider(session); - final Path sourceFolder = new EueDirectoryFeature(session, fileid).mkdir(new Path(new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory)), new TransferStatus()); + final Path sourceFolder = new EueDirectoryFeature(session, fileid).mkdir(new EueWriteFeature(session, fileid), new Path(new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory)), new TransferStatus()); final Path file = new Path(sourceFolder, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)); createFile(fileid, file, RandomUtils.nextBytes(0)); assertTrue(new EueFindFeature(session, fileid).find(file)); @@ -114,7 +114,7 @@ public class EueShareFeatureTest extends AbstractEueSessionTest { @Test public void testUploadUrlForContainer() throws Exception { final EueResourceIdProvider fileid = new EueResourceIdProvider(session); - final Path sourceFolder = new EueDirectoryFeature(session, fileid).mkdir(new Path(new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory)), new TransferStatus()); + final Path sourceFolder = new EueDirectoryFeature(session, fileid).mkdir(new EueWriteFeature(session, fileid), new Path(new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory)), new TransferStatus()); final EueShareFeature feature = new EueShareFeature(session, fileid); final DescriptiveUrl url = feature.toUploadUrl(sourceFolder, Share.Sharee.world, null, new DisabledPasswordCallback() { @Override diff --git a/eue/src/test/java/ch/cyberduck/core/eue/EueSingleUploadServiceTest.java b/eue/src/test/java/ch/cyberduck/core/eue/EueSingleUploadServiceTest.java index 4f76e17da0..1736da5c84 100644 --- a/eue/src/test/java/ch/cyberduck/core/eue/EueSingleUploadServiceTest.java +++ b/eue/src/test/java/ch/cyberduck/core/eue/EueSingleUploadServiceTest.java @@ -44,8 +44,8 @@ public class EueSingleUploadServiceTest extends AbstractEueSessionTest { @Test public void testUploadSimpleFile() throws Exception { final EueResourceIdProvider fileid = new EueResourceIdProvider(session); - final EueSingleUploadService service = new EueSingleUploadService(session, fileid, new EueWriteFeature(session, fileid)); - final Path container = new EueDirectoryFeature(session, fileid).mkdir(new Path(new AlphanumericRandomStringService().random(), EnumSet.of(AbstractPath.Type.directory)), new TransferStatus().setLength(0L)); + final EueSingleUploadService service = new EueSingleUploadService(session, fileid); + final Path container = new EueDirectoryFeature(session, fileid).mkdir(new EueWriteFeature(session, fileid), new Path(new AlphanumericRandomStringService().random(), EnumSet.of(AbstractPath.Type.directory)), new TransferStatus().setLength(0L)); final String name = new AlphanumericRandomStringService().random(); final Path file = new Path(container, name, EnumSet.of(Path.Type.file)); final Local local = new Local(System.getProperty("java.io.tmpdir"), name); @@ -54,7 +54,7 @@ public class EueSingleUploadServiceTest extends AbstractEueSessionTest { IOUtils.write(content, local.getOutputStream(false)); final TransferStatus status = new TransferStatus().setLength(content.length); final BytecountStreamListener count = new BytecountStreamListener(); - service.upload(file, local, new BandwidthThrottle(BandwidthThrottle.UNLIMITED), new DisabledProgressListener(), count, status, new DisabledConnectionCallback()); + service.upload(new EueWriteFeature(session, fileid), file, local, new BandwidthThrottle(BandwidthThrottle.UNLIMITED), new DisabledProgressListener(), count, status, new DisabledConnectionCallback()); assertEquals(content.length, count.getSent()); assertTrue(status.isComplete()); assertTrue(new EueFindFeature(session, fileid).find(file)); @@ -68,7 +68,7 @@ public class EueSingleUploadServiceTest extends AbstractEueSessionTest { IOUtils.write(content, local.getOutputStream(false)); final TransferStatus status = new TransferStatus().setLength(content.length).setExists(true); final BytecountStreamListener count = new BytecountStreamListener(); - service.upload(file, local, new BandwidthThrottle(BandwidthThrottle.UNLIMITED), new DisabledProgressListener(), count, status, new DisabledConnectionCallback()); + service.upload(new EueWriteFeature(session, fileid), file, local, new BandwidthThrottle(BandwidthThrottle.UNLIMITED), new DisabledProgressListener(), count, status, new DisabledConnectionCallback()); assertEquals(content.length, count.getSent()); assertTrue(status.isComplete()); assertTrue(new EueFindFeature(session, fileid).find(file)); diff --git a/eue/src/test/java/ch/cyberduck/core/eue/EueThresholdUploadServiceTest.java b/eue/src/test/java/ch/cyberduck/core/eue/EueThresholdUploadServiceTest.java index 3430f8fd91..26357f821e 100644 --- a/eue/src/test/java/ch/cyberduck/core/eue/EueThresholdUploadServiceTest.java +++ b/eue/src/test/java/ch/cyberduck/core/eue/EueThresholdUploadServiceTest.java @@ -46,7 +46,7 @@ public class EueThresholdUploadServiceTest extends AbstractEueSessionTest { public void testUploadSimpleFile() throws Exception { final EueResourceIdProvider fileid = new EueResourceIdProvider(session); final EueThresholdUploadService service = new EueThresholdUploadService(session, fileid, VaultRegistry.DISABLED); - final Path container = new EueDirectoryFeature(session, fileid).mkdir(new Path(new AlphanumericRandomStringService().random(), EnumSet.of(AbstractPath.Type.directory)), new TransferStatus()); + final Path container = new EueDirectoryFeature(session, fileid).mkdir(new EueWriteFeature(session, fileid), new Path(new AlphanumericRandomStringService().random(), EnumSet.of(AbstractPath.Type.directory)), new TransferStatus()); final String name = new AlphanumericRandomStringService().random(); final Path file = new Path(container, name, EnumSet.of(Path.Type.file)); final Local local = new Local(System.getProperty("java.io.tmpdir"), name); @@ -56,7 +56,7 @@ public class EueThresholdUploadServiceTest extends AbstractEueSessionTest { final TransferStatus status = new TransferStatus(); status.setLength(content.length); final BytecountStreamListener count = new BytecountStreamListener(); - service.upload(file, local, new BandwidthThrottle(BandwidthThrottle.UNLIMITED), new DisabledProgressListener(), count, status, new DisabledConnectionCallback()); + service.upload(new EueWriteFeature(session, fileid), file, local, new BandwidthThrottle(BandwidthThrottle.UNLIMITED), new DisabledProgressListener(), count, status, new DisabledConnectionCallback()); assertEquals(content.length, count.getSent()); assertTrue(status.isComplete()); assertTrue(new EueFindFeature(session, fileid).find(file)); @@ -72,7 +72,7 @@ public class EueThresholdUploadServiceTest extends AbstractEueSessionTest { public void testUploadLargeFileInChunks() throws Exception { final EueResourceIdProvider fileid = new EueResourceIdProvider(session); final EueThresholdUploadService service = new EueThresholdUploadService(session, fileid, VaultRegistry.DISABLED); - final Path container = new EueDirectoryFeature(session, fileid).mkdir(new Path(new AlphanumericRandomStringService().random(), EnumSet.of(AbstractPath.Type.directory)), new TransferStatus()); + final Path container = new EueDirectoryFeature(session, fileid).mkdir(new EueWriteFeature(session, fileid), new Path(new AlphanumericRandomStringService().random(), EnumSet.of(AbstractPath.Type.directory)), new TransferStatus()); final String name = new AlphanumericRandomStringService().random(); final Path file = new Path(container, name, EnumSet.of(Path.Type.file)); final Local local = new Local(System.getProperty("java.io.tmpdir"), name); @@ -82,7 +82,7 @@ public class EueThresholdUploadServiceTest extends AbstractEueSessionTest { final TransferStatus status = new TransferStatus(); status.setLength(content.length); final BytecountStreamListener count = new BytecountStreamListener(); - service.upload(file, local, new BandwidthThrottle(BandwidthThrottle.UNLIMITED), new DisabledProgressListener(), count, status, new DisabledConnectionCallback()); + service.upload(new EueWriteFeature(session, fileid), file, local, new BandwidthThrottle(BandwidthThrottle.UNLIMITED), new DisabledProgressListener(), count, status, new DisabledConnectionCallback()); assertEquals(content.length, count.getSent()); assertTrue(status.isComplete()); assertTrue(new EueFindFeature(session, fileid).find(file)); diff --git a/eue/src/test/java/ch/cyberduck/core/eue/EueTimestampFeatureTest.java b/eue/src/test/java/ch/cyberduck/core/eue/EueTimestampFeatureTest.java index ae35e63f8d..c074d87dee 100644 --- a/eue/src/test/java/ch/cyberduck/core/eue/EueTimestampFeatureTest.java +++ b/eue/src/test/java/ch/cyberduck/core/eue/EueTimestampFeatureTest.java @@ -46,10 +46,10 @@ public class EueTimestampFeatureTest extends AbstractEueSessionTest { @Test public void testSetTimestampFile() throws Exception { final EueResourceIdProvider fileid = new EueResourceIdProvider(session); - final Path container = new EueDirectoryFeature(session, fileid).mkdir(new Path( + final Path container = new EueDirectoryFeature(session, fileid).mkdir(new EueWriteFeature(session, fileid), new Path( new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory, Path.Type.volume)), new TransferStatus()); final Path file = new EueTouchFeature(session, fileid) - .touch(new Path(container, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)), new TransferStatus().setLength(0L)); + .touch(new EueWriteFeature(session, fileid), new Path(container, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)), new TransferStatus().setLength(0L)); final PathAttributes attr = new EueAttributesFinderFeature(session, fileid).find(file); assertNotEquals(PathAttributes.EMPTY, attr); assertNotNull(attr.getETag()); @@ -66,17 +66,17 @@ public class EueTimestampFeatureTest extends AbstractEueSessionTest { @Test public void testSetTimestampDirectory() throws Exception { final EueResourceIdProvider fileid = new EueResourceIdProvider(session); - final Path container = new EueDirectoryFeature(session, fileid).mkdir(new Path( + final Path container = new EueDirectoryFeature(session, fileid).mkdir(new EueWriteFeature(session, fileid), new Path( new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory, Path.Type.volume)), new TransferStatus()); final long containerModification = new EueAttributesFinderFeature(session, fileid).find(container).getModificationDate(); - final Path folder = new EueDirectoryFeature(session, fileid).mkdir(new Path(container, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory)), null); + final Path folder = new EueDirectoryFeature(session, fileid).mkdir(new EueWriteFeature(session, fileid), new Path(container, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory)), null); final long folderModification = new EueAttributesFinderFeature(session, fileid).find(folder).getModificationDate(); assertNotNull(new EueAttributesFinderFeature(session, fileid).find(folder)); final long modified = Instant.now().minusSeconds(5 * 24 * 60 * 60).getEpochSecond() * 1000; new EueTimestampFeature(session, fileid).setTimestamp(folder, modified); assertEquals(modified, new EueAttributesFinderFeature(session, fileid).find(folder).getModificationDate()); // Write file to directory and see if timestamp changes - final Path file = new EueTouchFeature(session, fileid).touch(new Path(folder, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)), new TransferStatus()); + final Path file = new EueTouchFeature(session, fileid).touch(new EueWriteFeature(session, fileid), new Path(folder, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)), new TransferStatus()); assertEquals(modified, new EueAttributesFinderFeature(session, fileid).find(folder).getModificationDate()); assertEquals(containerModification, new EueAttributesFinderFeature(session, fileid).find(container).getModificationDate()); final byte[] content = RandomUtils.nextBytes(8235); diff --git a/eue/src/test/java/ch/cyberduck/core/eue/EueTouchFeatureTest.java b/eue/src/test/java/ch/cyberduck/core/eue/EueTouchFeatureTest.java index b912f17a01..ec079c0b80 100644 --- a/eue/src/test/java/ch/cyberduck/core/eue/EueTouchFeatureTest.java +++ b/eue/src/test/java/ch/cyberduck/core/eue/EueTouchFeatureTest.java @@ -51,25 +51,25 @@ public class EueTouchFeatureTest extends AbstractEueSessionTest { @Test public void testConflict() throws Exception { final EueResourceIdProvider fileid = new EueResourceIdProvider(session); - final Path container = new EueDirectoryFeature(session, fileid).mkdir(new Path( + final Path container = new EueDirectoryFeature(session, fileid).mkdir(new EueWriteFeature(session, fileid), new Path( new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory, Path.Type.volume)), new TransferStatus()); - final Path file = new EueTouchFeature(session, fileid).touch(new Path(container, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)), new TransferStatus().setLength(0L)); + final Path file = new EueTouchFeature(session, fileid).touch(new EueWriteFeature(session, fileid), new Path(container, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)), new TransferStatus().setLength(0L)); // Create conflict - assertThrows(ConflictException.class, () -> new EueTouchFeature(session, fileid).touch(file, new TransferStatus().setLength(0L))); + assertThrows(ConflictException.class, () -> new EueTouchFeature(session, fileid).touch(new EueWriteFeature(session, fileid), file, new TransferStatus().setLength(0L))); new EueDeleteFeature(session, fileid).delete(Collections.singletonList(file), new DisabledPasswordCallback(), new Delete.DisabledCallback()); } @Test public void testCaseSensitivity() throws Exception { final EueResourceIdProvider fileid = new EueResourceIdProvider(session); - final Path container = new EueDirectoryFeature(session, fileid).mkdir(new Path( + final Path container = new EueDirectoryFeature(session, fileid).mkdir(new EueWriteFeature(session, fileid), new Path( new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory, Path.Type.volume)), new TransferStatus()); final String filename = StringUtils.lowerCase(new AlphanumericRandomStringService().random()); final Path file = new EueTouchFeature(session, fileid) - .touch(new Path(container, filename, EnumSet.of(Path.Type.file)), new TransferStatus().setLength(0L)); + .touch(new EueWriteFeature(session, fileid), new Path(container, filename, EnumSet.of(Path.Type.file)), new TransferStatus().setLength(0L)); // Create conflict assertThrows(ConflictException.class, () -> new EueTouchFeature(session, fileid) - .touch(new Path(container, StringUtils.capitalize(filename), EnumSet.of(Path.Type.file)), new TransferStatus().setLength(0L))); + .touch(new EueWriteFeature(session, fileid), new Path(container, StringUtils.capitalize(filename), EnumSet.of(Path.Type.file)), new TransferStatus().setLength(0L))); new EueDeleteFeature(session, fileid).delete(Collections.singletonList(file), new DisabledPasswordCallback(), new Delete.DisabledCallback()); } } diff --git a/eue/src/test/java/ch/cyberduck/core/eue/EueTrashFeatureTest.java b/eue/src/test/java/ch/cyberduck/core/eue/EueTrashFeatureTest.java index 13a500f78a..7febe28f6f 100644 --- a/eue/src/test/java/ch/cyberduck/core/eue/EueTrashFeatureTest.java +++ b/eue/src/test/java/ch/cyberduck/core/eue/EueTrashFeatureTest.java @@ -17,6 +17,7 @@ package ch.cyberduck.core.eue; import ch.cyberduck.core.AbstractPath; import ch.cyberduck.core.AlphanumericRandomStringService; +import ch.cyberduck.core.DefaultPathAttributes; import ch.cyberduck.core.DisabledListProgressListener; import ch.cyberduck.core.DisabledLoginCallback; import ch.cyberduck.core.Path; @@ -44,7 +45,7 @@ public class EueTrashFeatureTest extends AbstractEueSessionTest { @Test public void testDeleteFolder() throws Exception { final EueResourceIdProvider fileid = new EueResourceIdProvider(session); - final Path directory = new EueDirectoryFeature(session, fileid).mkdir(new Path( + final Path directory = new EueDirectoryFeature(session, fileid).mkdir(new EueWriteFeature(session, fileid), new Path( new AlphanumericRandomStringService().random(), EnumSet.of(AbstractPath.Type.directory)), new TransferStatus()); assertTrue(new EueFindFeature(session, fileid).find(directory, new DisabledListProgressListener())); new EueTrashFeature(session, fileid).delete(Collections.singletonList(directory), new DisabledLoginCallback(), new Delete.DisabledCallback()); @@ -57,7 +58,7 @@ public class EueTrashFeatureTest extends AbstractEueSessionTest { final Path folder = new Path(new AlphanumericRandomStringService().random(), EnumSet.of(AbstractPath.Type.directory)); final Path file1 = new Path(folder, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)); final Path file2 = new Path(folder, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)); - new EueDirectoryFeature(session, fileid).mkdir(folder, new TransferStatus()); + new EueDirectoryFeature(session, fileid).mkdir(new EueWriteFeature(session, fileid), folder, new TransferStatus()); createFile(fileid, file1, RandomUtils.nextBytes(511)); createFile(fileid, file2, RandomUtils.nextBytes(214)); assertTrue(new EueFindFeature(session, fileid).find(file1)); @@ -75,7 +76,7 @@ public class EueTrashFeatureTest extends AbstractEueSessionTest { final EueResourceIdProvider fileid = new EueResourceIdProvider(session); final Path folder = new Path(new AlphanumericRandomStringService().random(), EnumSet.of(AbstractPath.Type.directory)); final Path file = new Path(folder, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)); - new EueDirectoryFeature(session, fileid).mkdir(folder, new TransferStatus()); + new EueDirectoryFeature(session, fileid).mkdir(new EueWriteFeature(session, fileid), folder, new TransferStatus()); createFile(fileid, file, RandomUtils.nextBytes(511)); assertTrue(new EueFindFeature(session, fileid).find(file)); assertNotNull(fileid.getFileId(file)); @@ -101,7 +102,7 @@ public class EueTrashFeatureTest extends AbstractEueSessionTest { public void testDeleteLockOwnerFile() throws Exception { final EueResourceIdProvider fileid = new EueResourceIdProvider(session); final Path folder = new EueDirectoryFeature(session, fileid).mkdir( - new Path(new AlphanumericRandomStringService().random(), EnumSet.of(AbstractPath.Type.directory)), new TransferStatus()); + new EueWriteFeature(session, fileid), new Path(new AlphanumericRandomStringService().random(), EnumSet.of(AbstractPath.Type.directory)), new TransferStatus()); final String filename = String.format("~$%s.docx", new AlphanumericRandomStringService().random()); { final Path file1 = new Path(folder, filename, EnumSet.of(Path.Type.file)); @@ -142,13 +143,13 @@ public class EueTrashFeatureTest extends AbstractEueSessionTest { @Test public void testDoubleDelete() throws Exception { final EueResourceIdProvider fileid = new EueResourceIdProvider(session); - final Path file = new EueTouchFeature(session, fileid).touch(new Path(new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)), new TransferStatus()); + final Path file = new EueTouchFeature(session, fileid).touch(new EueWriteFeature(session, fileid), new Path(new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)), new TransferStatus()); final String resourceId = file.attributes().getFileId(); new EueTrashFeature(session, fileid).delete(Collections.singletonList(file), new DisabledLoginCallback(), new Delete.DisabledCallback()); try { // Skip file already in trash new EueTrashFeature(session, fileid).delete(Collections.singletonList( - file.withAttributes(new PathAttributes().setFileId(resourceId))), new DisabledLoginCallback(), new Delete.DisabledCallback()); + file.withAttributes(new DefaultPathAttributes().setFileId(resourceId))), new DisabledLoginCallback(), new Delete.DisabledCallback()); } catch(NotfoundException e) { fail(); @@ -158,7 +159,7 @@ public class EueTrashFeatureTest extends AbstractEueSessionTest { @Test public void testDeleteFileInTrash() throws Exception { final EueResourceIdProvider fileid = new EueResourceIdProvider(session); - final Path file = new EueTouchFeature(session, fileid).touch(new Path(new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)), new TransferStatus()); + final Path file = new EueTouchFeature(session, fileid).touch(new EueWriteFeature(session, fileid), new Path(new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)), new TransferStatus()); final String resourceId = file.attributes().getFileId(); final EueTrashFeature feature = new EueTrashFeature(session, fileid); feature.delete(Collections.singletonList(file), new DisabledLoginCallback(), new Delete.DisabledCallback()); diff --git a/eue/src/test/java/ch/cyberduck/core/eue/EueUploadServiceTest.java b/eue/src/test/java/ch/cyberduck/core/eue/EueUploadServiceTest.java index 48203e32b2..64fb6eb21a 100644 --- a/eue/src/test/java/ch/cyberduck/core/eue/EueUploadServiceTest.java +++ b/eue/src/test/java/ch/cyberduck/core/eue/EueUploadServiceTest.java @@ -44,8 +44,8 @@ public class EueUploadServiceTest extends AbstractEueSessionTest { @Test public void testUploadLargeFileInChunks() throws Exception { final EueResourceIdProvider fileid = new EueResourceIdProvider(session); - final EueUploadService s = new EueUploadService(session, fileid, new EueMultipartWriteFeature(session, fileid)); - final Path container = new EueDirectoryFeature(session, fileid).mkdir(new Path( + final EueUploadService s = new EueUploadService(session); + final Path container = new EueDirectoryFeature(session, fileid).mkdir(new EueWriteFeature(session, fileid), new Path( new AlphanumericRandomStringService().random(), EnumSet.of(AbstractPath.Type.directory)), new TransferStatus()); final String name = new AlphanumericRandomStringService().random(); final Path file = new Path(container, name, EnumSet.of(Path.Type.file)); @@ -55,7 +55,7 @@ public class EueUploadServiceTest extends AbstractEueSessionTest { final TransferStatus status = new TransferStatus(); status.setLength(content.length); final BytecountStreamListener count = new BytecountStreamListener(); - final EueWriteFeature.Chunk uploadResponse = s.upload(file, local, new BandwidthThrottle(BandwidthThrottle.UNLIMITED), new DisabledProgressListener(), count, status, new DisabledConnectionCallback()); + final EueWriteFeature.Chunk uploadResponse = s.upload(new EueMultipartWriteFeature(session, fileid), file, local, new BandwidthThrottle(BandwidthThrottle.UNLIMITED), new DisabledProgressListener(), count, status, new DisabledConnectionCallback()); assertNotNull(uploadResponse.getCdash64()); assertEquals(content.length, count.getSent()); assertTrue(status.isComplete()); diff --git a/eue/src/test/java/ch/cyberduck/core/eue/EueWriteFeatureTest.java b/eue/src/test/java/ch/cyberduck/core/eue/EueWriteFeatureTest.java index 4c5d469463..ee27bb4ac8 100644 --- a/eue/src/test/java/ch/cyberduck/core/eue/EueWriteFeatureTest.java +++ b/eue/src/test/java/ch/cyberduck/core/eue/EueWriteFeatureTest.java @@ -114,7 +114,7 @@ public class EueWriteFeatureTest extends AbstractEueSessionTest { public void testWrite() throws Exception { final EueResourceIdProvider fileid = new EueResourceIdProvider(session); final EueWriteFeature feature = new EueWriteFeature(session, fileid); - final Path container = new EueDirectoryFeature(session, fileid).mkdir(new Path(new AlphanumericRandomStringService().random(), EnumSet.of(AbstractPath.Type.directory)), new TransferStatus()); + final Path container = new EueDirectoryFeature(session, fileid).mkdir(new EueWriteFeature(session, fileid), new Path(new AlphanumericRandomStringService().random(), EnumSet.of(AbstractPath.Type.directory)), new TransferStatus()); long containerModification = new EueAttributesFinderFeature(session, fileid).find(container).getModificationDate(); final Path file = new Path(container, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)); String resourceId; @@ -238,7 +238,7 @@ public class EueWriteFeatureTest extends AbstractEueSessionTest { feature.write(file, status, new DisabledConnectionCallback()); } catch(QuotaException e) { - assertEquals("LIMIT_MAX_RESOURCE_SIZE. Please contact your web hosting service provider for assistance.", e.getDetail()); + assertEquals("RESOURCE_SIZE. Please contact your web hosting service provider for assistance.", e.getDetail()); throw e; } } diff --git a/freenet/pom.xml b/freenet/pom.xml index 593212caf5..1c39044e53 100644 --- a/freenet/pom.xml +++ b/freenet/pom.xml @@ -19,7 +19,7 @@ parent ch.cyberduck - 9.2.0-SNAPSHOT + 9.4.0-SNAPSHOT freenet jar diff --git a/freenet/src/test/java/ch/cyberduck/core/freenet/FreenetAttributesFinderFeatureTest.java b/freenet/src/test/java/ch/cyberduck/core/freenet/FreenetAttributesFinderFeatureTest.java index 5b9ab95331..b71384708f 100644 --- a/freenet/src/test/java/ch/cyberduck/core/freenet/FreenetAttributesFinderFeatureTest.java +++ b/freenet/src/test/java/ch/cyberduck/core/freenet/FreenetAttributesFinderFeatureTest.java @@ -8,6 +8,7 @@ import ch.cyberduck.core.dav.DAVAttributesFinderFeature; import ch.cyberduck.core.dav.DAVDeleteFeature; import ch.cyberduck.core.dav.DAVDirectoryFeature; import ch.cyberduck.core.dav.DAVTouchFeature; +import ch.cyberduck.core.dav.DAVWriteFeature; import ch.cyberduck.core.exception.NotfoundException; import ch.cyberduck.core.features.Delete; import ch.cyberduck.core.shared.DefaultHomeFinderService; @@ -40,7 +41,7 @@ public class FreenetAttributesFinderFeatureTest extends AbstractFreenetTest { @Test public void testFindFile() throws Exception { - final Path test = new DAVTouchFeature(session).touch(new Path(new DefaultHomeFinderService(session).find(), + final Path test = new DAVTouchFeature(session).touch(new DAVWriteFeature(session), new Path(new DefaultHomeFinderService(session).find(), new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)), new TransferStatus()); final DAVAttributesFinderFeature f = new DAVAttributesFinderFeature(session); final PathAttributes attributes = f.find(test); @@ -62,7 +63,7 @@ public class FreenetAttributesFinderFeatureTest extends AbstractFreenetTest { @Test public void testFindFolder() throws Exception { - final Path test = new DAVDirectoryFeature(session).mkdir(new Path(new DefaultHomeFinderService(session).find(), + final Path test = new DAVDirectoryFeature(session).mkdir(new DAVWriteFeature(session), new Path(new DefaultHomeFinderService(session).find(), new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory)), new TransferStatus()); final DAVAttributesFinderFeature f = new DAVAttributesFinderFeature(session); final PathAttributes attributes = f.find(test); diff --git a/freenet/src/test/java/ch/cyberduck/core/freenet/FreenetFindFeatureTest.java b/freenet/src/test/java/ch/cyberduck/core/freenet/FreenetFindFeatureTest.java index f0c098ac99..3139ef2bb8 100644 --- a/freenet/src/test/java/ch/cyberduck/core/freenet/FreenetFindFeatureTest.java +++ b/freenet/src/test/java/ch/cyberduck/core/freenet/FreenetFindFeatureTest.java @@ -7,6 +7,7 @@ import ch.cyberduck.core.dav.DAVDeleteFeature; import ch.cyberduck.core.dav.DAVDirectoryFeature; import ch.cyberduck.core.dav.DAVFindFeature; import ch.cyberduck.core.dav.DAVTouchFeature; +import ch.cyberduck.core.dav.DAVWriteFeature; import ch.cyberduck.core.features.Delete; import ch.cyberduck.core.shared.DefaultHomeFinderService; import ch.cyberduck.core.transfer.TransferStatus; @@ -37,7 +38,7 @@ public class FreenetFindFeatureTest extends AbstractFreenetTest { @Test public void testFindFile() throws Exception { - final Path test = new DAVTouchFeature(session).touch(new Path(new DefaultHomeFinderService(session).find(), + final Path test = new DAVTouchFeature(session).touch(new DAVWriteFeature(session), new Path(new DefaultHomeFinderService(session).find(), new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)), new TransferStatus()); assertTrue(new DAVFindFeature(session).find(test)); new DAVDeleteFeature(session).delete(Collections.singletonList(test), new DisabledLoginCallback(), new Delete.DisabledCallback()); @@ -45,7 +46,7 @@ public class FreenetFindFeatureTest extends AbstractFreenetTest { @Test public void testFindFolder() throws Exception { - final Path test = new DAVDirectoryFeature(session).mkdir(new Path(new DefaultHomeFinderService(session).find(), + final Path test = new DAVDirectoryFeature(session).mkdir(new DAVWriteFeature(session), new Path(new DefaultHomeFinderService(session).find(), new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory)), new TransferStatus()); assertTrue(new DAVFindFeature(session).find(test)); new DAVDeleteFeature(session).delete(Collections.singletonList(test), new DisabledLoginCallback(), new Delete.DisabledCallback()); diff --git a/freenet/src/test/java/ch/cyberduck/core/freenet/FreenetTimestampFeatureTest.java b/freenet/src/test/java/ch/cyberduck/core/freenet/FreenetTimestampFeatureTest.java index 12d40a473c..a386943b74 100644 --- a/freenet/src/test/java/ch/cyberduck/core/freenet/FreenetTimestampFeatureTest.java +++ b/freenet/src/test/java/ch/cyberduck/core/freenet/FreenetTimestampFeatureTest.java @@ -22,6 +22,7 @@ import ch.cyberduck.core.dav.DAVAttributesFinderFeature; import ch.cyberduck.core.dav.DAVDeleteFeature; import ch.cyberduck.core.dav.DAVTimestampFeature; import ch.cyberduck.core.dav.DAVTouchFeature; +import ch.cyberduck.core.dav.DAVWriteFeature; import ch.cyberduck.core.features.Delete; import ch.cyberduck.core.shared.DefaultAttributesFinderFeature; import ch.cyberduck.core.shared.DefaultHomeFinderService; @@ -43,7 +44,7 @@ public class FreenetTimestampFeatureTest extends AbstractFreenetTest { @Test @Ignore public void testSetTimestamp() throws Exception { - final Path file = new DAVTouchFeature(session).touch(new Path(new DefaultHomeFinderService(session).find(), new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)), new TransferStatus()); + final Path file = new DAVTouchFeature(session).touch(new DAVWriteFeature(session), new Path(new DefaultHomeFinderService(session).find(), new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)), new TransferStatus()); new DAVTimestampFeature(session).setTimestamp(file, 5000L); assertEquals(5000L, new DAVAttributesFinderFeature(session).find(file).getModificationDate()); assertEquals(5000L, new DefaultAttributesFinderFeature(session).find(file).getModificationDate()); diff --git a/ftp/pom.xml b/ftp/pom.xml index 5ca5b1aa06..d6560ffe94 100644 --- a/ftp/pom.xml +++ b/ftp/pom.xml @@ -18,7 +18,7 @@ ch.cyberduck parent - 9.2.0-SNAPSHOT + 9.4.0-SNAPSHOT ftp jar diff --git a/ftp/src/main/java/ch/cyberduck/core/ftp/FTPClient.java b/ftp/src/main/java/ch/cyberduck/core/ftp/FTPClient.java index 2fcd95d53d..558aa88d30 100644 --- a/ftp/src/main/java/ch/cyberduck/core/ftp/FTPClient.java +++ b/ftp/src/main/java/ch/cyberduck/core/ftp/FTPClient.java @@ -84,7 +84,8 @@ public class FTPClient extends FTPSClient { if(null == socket) { throw new FTPException(this.getReplyCode(), this.getReplyString()); } - return socket; + // Wrap socket to ensure proper TCP shutdown sequence + return new FTPSocket(socket); } @Override diff --git a/ftp/src/main/java/ch/cyberduck/core/ftp/FTPDirectoryFeature.java b/ftp/src/main/java/ch/cyberduck/core/ftp/FTPDirectoryFeature.java index 8e0a726fc1..f2425aab4d 100644 --- a/ftp/src/main/java/ch/cyberduck/core/ftp/FTPDirectoryFeature.java +++ b/ftp/src/main/java/ch/cyberduck/core/ftp/FTPDirectoryFeature.java @@ -21,13 +21,14 @@ import ch.cyberduck.core.Path; import ch.cyberduck.core.exception.BackgroundException; import ch.cyberduck.core.exception.ConflictException; import ch.cyberduck.core.features.Directory; +import ch.cyberduck.core.features.Write; import ch.cyberduck.core.transfer.TransferStatus; import org.apache.commons.net.ftp.FTPReply; import java.io.IOException; -public class FTPDirectoryFeature implements Directory { +public class FTPDirectoryFeature implements Directory { private final FTPSession session; @@ -36,7 +37,7 @@ public class FTPDirectoryFeature implements Directory { } @Override - public Path mkdir(final Path folder, final TransferStatus status) throws BackgroundException { + public Path mkdir(final Write writer, final Path folder, final TransferStatus status) throws BackgroundException { try { if(!session.getClient().makeDirectory(folder.getAbsolute())) { throw new FTPException(session.getClient().getReplyCode(), session.getClient().getReplyString()); diff --git a/ftp/src/main/java/ch/cyberduck/core/ftp/FTPMoveFeature.java b/ftp/src/main/java/ch/cyberduck/core/ftp/FTPMoveFeature.java index 391a062db2..e558b19dd7 100644 --- a/ftp/src/main/java/ch/cyberduck/core/ftp/FTPMoveFeature.java +++ b/ftp/src/main/java/ch/cyberduck/core/ftp/FTPMoveFeature.java @@ -19,7 +19,9 @@ package ch.cyberduck.core.ftp; */ import ch.cyberduck.core.ConnectionCallback; +import ch.cyberduck.core.DefaultPathAttributes; import ch.cyberduck.core.Path; +import ch.cyberduck.core.PathAttributes; import ch.cyberduck.core.exception.BackgroundException; import ch.cyberduck.core.features.Delete; import ch.cyberduck.core.features.Move; @@ -50,7 +52,7 @@ public class FTPMoveFeature implements Move { throw new FTPException(session.getClient().getReplyCode(), session.getClient().getReplyString()); } // Copy original file attributes - return new Path(renamed).withAttributes(file.attributes()); + return new Path(renamed).withAttributes(new DefaultPathAttributes(file.attributes()).setVault(null)); } catch(IOException e) { throw new FTPExceptionMappingService().map("Cannot rename {0}", e, file); diff --git a/ftp/src/main/java/ch/cyberduck/core/ftp/FTPSession.java b/ftp/src/main/java/ch/cyberduck/core/ftp/FTPSession.java index aadc17d0a4..fcf6f6e466 100644 --- a/ftp/src/main/java/ch/cyberduck/core/ftp/FTPSession.java +++ b/ftp/src/main/java/ch/cyberduck/core/ftp/FTPSession.java @@ -34,8 +34,6 @@ import ch.cyberduck.core.exception.LoginCanceledException; import ch.cyberduck.core.features.*; import ch.cyberduck.core.ftp.list.FTPListService; import ch.cyberduck.core.idna.PunycodeConverter; -import ch.cyberduck.core.preferences.HostPreferencesFactory; -import ch.cyberduck.core.preferences.PreferencesReader; import ch.cyberduck.core.proxy.ProxyFinder; import ch.cyberduck.core.proxy.ProxySocketFactory; import ch.cyberduck.core.shared.DefaultCopyFeature; @@ -67,9 +65,6 @@ import java.util.Locale; public class FTPSession extends SSLSession { private static final Logger log = LogManager.getLogger(FTPSession.class); - private final PreferencesReader preferences - = HostPreferencesFactory.get(host); - private final FTPListService list = new FTPListService(this); private final FTPReadFeature read = new FTPReadFeature(this); @@ -105,7 +100,7 @@ public class FTPSession extends SSLSession { } @Override - protected void disconnect() { + protected void disconnect() throws BackgroundException { try { client.disconnect(); } diff --git a/ftp/src/main/java/ch/cyberduck/core/ftp/FTPSocket.java b/ftp/src/main/java/ch/cyberduck/core/ftp/FTPSocket.java new file mode 100644 index 0000000000..c2d44fde77 --- /dev/null +++ b/ftp/src/main/java/ch/cyberduck/core/ftp/FTPSocket.java @@ -0,0 +1,135 @@ +package ch.cyberduck.core.ftp; + +/* + * Copyright (c) 2002-2025 David Kocher. All rights reserved. + * http://cyberduck.ch/ + * + * 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. + * + * Bug fixes, suggestions and comments should be sent to feedback@cyberduck.ch + */ + +import com.amazonaws.internal.DelegateSocket; + +import org.apache.commons.io.input.ProxyInputStream; +import org.apache.commons.io.output.CountingOutputStream; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; + +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.net.Socket; +import java.util.concurrent.CompletableFuture; + +/** + * Socket wrapper that enforces proper TCP shutdown sequence to prevent + * race conditions due to premature socket closure during FTP data connections. + *

+ * This fixes issues where: + * - macOS: Intermittent "426 Connection closed; transfer aborted" errors due to socket closure before server ACKs + * - Windows: Intermittent transfer hanging due to socket closure before client sends FIN with last data packet + *

+ * The proper TCP shutdown sequence is: + * 1. Call shutdownOutput() to send FIN while keeping socket input open for ACKs + * 2. Drain input to wait for ACKs until we receive server FIN + * 3. Close the socket to release resources + */ +public class FTPSocket extends DelegateSocket { + private static final Logger log = LogManager.getLogger(FTPSocket.class); + + private InputStream inputStreamWrapper; + private CountingOutputStream outputStreamWrapper; + + public FTPSocket(final Socket sock) { + super(sock); + } + + @Override + public synchronized void close() throws IOException { + if(sock.isClosed()) { + log.debug("Socket already closed {}", sock); + return; + } + try { + // Only do full TCP shutdown if we have output bytes, otherwise close socket directly + if(outputStreamWrapper != null && outputStreamWrapper.getByteCount() > 0) { + if(sock.isOutputShutdown()) { + log.debug("Socket output already closed {}", sock); + } + else if(!sock.isConnected()) { + log.debug("Socket is already disconnected {}", sock); + } + else { + // Shutdown output to send FIN, but keep socket open to receive ACKs + log.debug("Shutting down output for socket {}", sock); + sock.shutdownOutput(); + + log.debug("Waiting for input to close for socket {}", sock); + int bytesRead = 0; + // Read until EOF (server FIN) or timeout + while(sock.getInputStream().read() != -1) { + bytesRead++; + } + if(bytesRead > 0) { + log.warn("Drained {} bytes from socket {}", bytesRead, sock); + } + } + } + } + finally { + log.debug("Closing socket {}", sock); + // Work around macOS quirk where Java NIO's SocketDispatcher.close0() has a 1,000ms delay + CompletableFuture.runAsync(() -> { + try { + sock.close(); + } + catch(IOException e) { + log.error("Error closing socket {}: {}", sock, e.getMessage()); + } + }); + } + } + + @Override + public synchronized OutputStream getOutputStream() throws IOException { + if(outputStreamWrapper == null) { + outputStreamWrapper = new CountingOutputStream(sock.getOutputStream()) { + @Override + public void close() throws IOException { + // We can't call super.close() as it would call sock.close() + // Therefore, we flush here and close the underlying stream ourselves + try { + super.flush(); + } + finally { + FTPSocket.this.close(); + } + } + }; + } + return outputStreamWrapper; + } + + @Override + public synchronized InputStream getInputStream() throws IOException { + if(inputStreamWrapper == null) { + inputStreamWrapper = new ProxyInputStream(sock.getInputStream()) { + @Override + public void close() throws IOException { + // super.close() will call sock.close(), so override it with ours instead + FTPSocket.this.close(); + } + }; + } + return inputStreamWrapper; + } +} diff --git a/ftp/src/main/java/ch/cyberduck/core/ftp/FTPTouchFeature.java b/ftp/src/main/java/ch/cyberduck/core/ftp/FTPTouchFeature.java index 812ada9fdc..292aecd4bd 100644 --- a/ftp/src/main/java/ch/cyberduck/core/ftp/FTPTouchFeature.java +++ b/ftp/src/main/java/ch/cyberduck/core/ftp/FTPTouchFeature.java @@ -21,7 +21,7 @@ import ch.cyberduck.core.shared.DefaultTouchFeature; public class FTPTouchFeature extends DefaultTouchFeature { public FTPTouchFeature(final FTPSession session) { - super(new FTPWriteFeature(session)); + super(session); } @Override diff --git a/ftp/src/main/java/ch/cyberduck/core/ftp/FTPUploadFeature.java b/ftp/src/main/java/ch/cyberduck/core/ftp/FTPUploadFeature.java index 4993846da8..9de803f8d5 100644 --- a/ftp/src/main/java/ch/cyberduck/core/ftp/FTPUploadFeature.java +++ b/ftp/src/main/java/ch/cyberduck/core/ftp/FTPUploadFeature.java @@ -16,6 +16,7 @@ package ch.cyberduck.core.ftp; */ import ch.cyberduck.core.Path; +import ch.cyberduck.core.Session; import ch.cyberduck.core.exception.BackgroundException; import ch.cyberduck.core.features.Write; import ch.cyberduck.core.shared.DefaultUploadFeature; @@ -23,12 +24,8 @@ import ch.cyberduck.core.transfer.TransferStatus; public class FTPUploadFeature extends DefaultUploadFeature { - public FTPUploadFeature(final FTPSession session) { - super(new FTPWriteFeature(session)); - } - - public FTPUploadFeature(final Write writer) { - super(writer); + public FTPUploadFeature(final Session session) { + super(session); } @Override diff --git a/ftp/src/main/java/ch/cyberduck/core/ftp/LoggingProtocolCommandListener.java b/ftp/src/main/java/ch/cyberduck/core/ftp/LoggingProtocolCommandListener.java index 2aed120e28..991faf86af 100644 --- a/ftp/src/main/java/ch/cyberduck/core/ftp/LoggingProtocolCommandListener.java +++ b/ftp/src/main/java/ch/cyberduck/core/ftp/LoggingProtocolCommandListener.java @@ -38,7 +38,7 @@ public class LoggingProtocolCommandListener implements ProtocolCommandListener, final String message = StringUtils.chomp(event.getMessage()); if(message.startsWith(FTPCmd.PASS.name())) { this.log(Type.request, String.format("%s %s", FTPCmd.PASS.name(), - StringUtils.repeat("*", StringUtils.length(StringUtils.removeStart(message, FTPCmd.PASS.name()))))); + StringUtils.repeat("*", Integer.min(8, StringUtils.length(StringUtils.removeStart(message, FTPCmd.PASS.name())))))); } else { this.log(Type.request, message); diff --git a/ftp/src/main/java/ch/cyberduck/core/ftp/list/FTPListService.java b/ftp/src/main/java/ch/cyberduck/core/ftp/list/FTPListService.java index 015ba4a7ae..b1f5566170 100644 --- a/ftp/src/main/java/ch/cyberduck/core/ftp/list/FTPListService.java +++ b/ftp/src/main/java/ch/cyberduck/core/ftp/list/FTPListService.java @@ -18,6 +18,7 @@ package ch.cyberduck.core.ftp.list; import ch.cyberduck.core.AttributedList; import ch.cyberduck.core.ListProgressListener; import ch.cyberduck.core.ListService; +import ch.cyberduck.core.LocaleFactory; import ch.cyberduck.core.Path; import ch.cyberduck.core.exception.AccessDeniedException; import ch.cyberduck.core.exception.BackgroundException; @@ -38,6 +39,7 @@ import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; import java.io.IOException; +import java.text.MessageFormat; import java.util.ArrayList; import java.util.EnumSet; import java.util.HashMap; @@ -220,4 +222,12 @@ public class FTPListService implements ListService { throw new FTPExceptionMappingService().map("Listing directory {0} failed", e, directory); } } + + @Override + public void preflight(final Path directory) throws BackgroundException { + if(!directory.attributes().getPermission().isExecutable() || !directory.attributes().getPermission().isReadable()) { + throw new AccessDeniedException(MessageFormat.format(LocaleFactory.localizedString("Listing directory {0} failed", "Error"), + directory.getName())).withFile(directory); + } + } } diff --git a/ftp/src/main/java/ch/cyberduck/core/ftp/list/FTPStatListService.java b/ftp/src/main/java/ch/cyberduck/core/ftp/list/FTPStatListService.java index ef68db961b..9f2e592477 100644 --- a/ftp/src/main/java/ch/cyberduck/core/ftp/list/FTPStatListService.java +++ b/ftp/src/main/java/ch/cyberduck/core/ftp/list/FTPStatListService.java @@ -35,7 +35,7 @@ import java.util.ArrayList; import java.util.List; public class FTPStatListService implements ListService { - private static final Logger log = LogManager.getLogger(FTPListService.class); + private static final Logger log = LogManager.getLogger(FTPStatListService.class); private final FTPSession session; private final FTPDataResponseReader reader; diff --git a/ftp/src/test/java/ch/cyberduck/core/cryptomator/CopyWorkerTest.java b/ftp/src/test/java/ch/cyberduck/core/cryptomator/CopyWorkerTest.java index bbc082dfb6..d75edde0d7 100644 --- a/ftp/src/test/java/ch/cyberduck/core/cryptomator/CopyWorkerTest.java +++ b/ftp/src/test/java/ch/cyberduck/core/cryptomator/CopyWorkerTest.java @@ -32,8 +32,8 @@ 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.ftp.AbstractFTPTest; -import ch.cyberduck.core.ftp.FTPDeleteFeature; import ch.cyberduck.core.ftp.FTPDirectoryFeature; import ch.cyberduck.core.ftp.FTPReadFeature; import ch.cyberduck.core.ftp.FTPSession; @@ -87,10 +87,10 @@ public class CopyWorkerTest extends AbstractFTPTest { session.withRegistry(registry); final byte[] content = RandomUtils.nextBytes(40500); final TransferStatus status = new TransferStatus(); - new CryptoBulkFeature<>(session, new DisabledBulkFeature(), new FTPDeleteFeature(session), cryptomator).pre(Transfer.Type.upload, Collections.singletonMap(new TransferItem(source), status), new DisabledConnectionCallback()); + new CryptoBulkFeature<>(session, new DisabledBulkFeature(), 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 FTPWriteFeature(session), cryptomator).write(source, status.setLength(content.length), new DisabledConnectionCallback())); assertTrue(cryptomator.getFeature(session, Find.class, new DefaultFindFeature(session)).find(source)); - final FTPSession copySession = new FTPSession(new Host(session.getHost()).withCredentials(new Credentials("test", "test"))); + final FTPSession copySession = new FTPSession(new Host(session.getHost()).setCredentials(new Credentials("test", "test"))); copySession.open(new DisabledProxyFinder(), new DisabledHostKeyCallback(), new DisabledLoginCallback(), new DisabledCancelCallback()); copySession.login(new DisabledLoginCallback(), new DisabledCancelCallback()); final CopyWorker worker = new CopyWorker(Collections.singletonMap(source, target), new SessionPool.SingleSessionPool(copySession, registry), PathCache.empty(), new DisabledProgressListener(), new DisabledConnectionCallback()); @@ -116,12 +116,13 @@ public class CopyWorkerTest extends AbstractFTPTest { cryptomator.create(session, new VaultCredentials("test"), vaultVersion); final DefaultVaultRegistry registry = new DefaultVaultRegistry(new DisabledPasswordCallback(), cryptomator); session.withRegistry(registry); - new CryptoTouchFeature<>(session, new DefaultTouchFeature<>(new FTPWriteFeature(session) - ), new FTPWriteFeature(session), cryptomator).touch(source, new TransferStatus()); + new CryptoTouchFeature<>(session, new DefaultTouchFeature( + session), cryptomator).touch(new CryptoWriteFeature<>(session, new FTPWriteFeature(session), cryptomator), source, new TransferStatus()); assertTrue(cryptomator.getFeature(session, Find.class, new DefaultFindFeature(session)).find(source)); - cryptomator.getFeature(session, Directory.class, new FTPDirectoryFeature(session)).mkdir(targetFolder, new TransferStatus()); + cryptomator.getFeature(session, Directory.class, new FTPDirectoryFeature(session)).mkdir( + new CryptoWriteFeature<>(session, new FTPWriteFeature(session), cryptomator), targetFolder, new TransferStatus()); assertTrue(cryptomator.getFeature(session, Find.class, new DefaultFindFeature(session)).find(targetFolder)); - final FTPSession copySession = new FTPSession(new Host(session.getHost()).withCredentials(new Credentials("test", "test"))); + final FTPSession copySession = new FTPSession(new Host(session.getHost()).setCredentials(new Credentials("test", "test"))); copySession.open(new DisabledProxyFinder(), new DisabledHostKeyCallback(), new DisabledLoginCallback(), new DisabledCancelCallback()); copySession.login(new DisabledLoginCallback(), new DisabledCancelCallback()); final CopyWorker worker = new CopyWorker(Collections.singletonMap(source, target), new SessionPool.SingleSessionPool(copySession, registry), PathCache.empty(), new DisabledProgressListener(), new DisabledConnectionCallback()); @@ -144,12 +145,13 @@ public class CopyWorkerTest extends AbstractFTPTest { cryptomator.create(session, new VaultCredentials("test"), vaultVersion); final DefaultVaultRegistry registry = new DefaultVaultRegistry(new DisabledPasswordCallback(), cryptomator); session.withRegistry(registry); - new CryptoTouchFeature<>(session, new DefaultTouchFeature<>(new FTPWriteFeature(session) - ), new FTPWriteFeature(session), cryptomator).touch(source, new TransferStatus()); + new CryptoTouchFeature<>(session, new DefaultTouchFeature( + session), cryptomator).touch(new CryptoWriteFeature<>(session, new FTPWriteFeature(session), cryptomator), source, new TransferStatus()); assertTrue(cryptomator.getFeature(session, Find.class, new DefaultFindFeature(session)).find(source)); - cryptomator.getFeature(session, Directory.class, new FTPDirectoryFeature(session)).mkdir(targetFolder, new TransferStatus()); + cryptomator.getFeature(session, Directory.class, new FTPDirectoryFeature(session)).mkdir( + new CryptoWriteFeature<>(session, new FTPWriteFeature(session), cryptomator), targetFolder, new TransferStatus()); assertTrue(cryptomator.getFeature(session, Find.class, new DefaultFindFeature(session)).find(targetFolder)); - final FTPSession copySession = new FTPSession(new Host(session.getHost()).withCredentials(new Credentials("test", "test"))); + final FTPSession copySession = new FTPSession(new Host(session.getHost()).setCredentials(new Credentials("test", "test"))); copySession.open(new DisabledProxyFinder(), new DisabledHostKeyCallback(), new DisabledLoginCallback(), new DisabledCancelCallback()); copySession.login(new DisabledLoginCallback(), new DisabledCancelCallback()); final CopyWorker worker = new CopyWorker(Collections.singletonMap(source, target), new SessionPool.SingleSessionPool(copySession, registry), PathCache.empty(), new DisabledProgressListener(), new DisabledConnectionCallback()); @@ -170,13 +172,15 @@ public class CopyWorkerTest extends AbstractFTPTest { cryptomator.create(session, new VaultCredentials("test"), vaultVersion); final DefaultVaultRegistry registry = new DefaultVaultRegistry(new DisabledPasswordCallback(), cryptomator); session.withRegistry(registry); - cryptomator.getFeature(session, Directory.class, new FTPDirectoryFeature(session)).mkdir(folder, new TransferStatus()); + cryptomator.getFeature(session, Directory.class, new FTPDirectoryFeature(session)).mkdir( + new CryptoWriteFeature<>(session, new FTPWriteFeature(session), cryptomator), folder, new TransferStatus()); assertTrue(cryptomator.getFeature(session, Find.class, new DefaultFindFeature(session)).find(folder)); - new CryptoTouchFeature<>(session, new DefaultTouchFeature<>(new FTPWriteFeature(session)), new FTPWriteFeature(session), cryptomator).touch(file, new TransferStatus()); + new CryptoTouchFeature<>(session, new DefaultTouchFeature(session), cryptomator).touch( + new CryptoWriteFeature<>(session, new FTPWriteFeature(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)); - final FTPSession copySession = new FTPSession(new Host(session.getHost()).withCredentials(new Credentials("test", "test"))); + final FTPSession copySession = new FTPSession(new Host(session.getHost()).setCredentials(new Credentials("test", "test"))); copySession.open(new DisabledProxyFinder(), new DisabledHostKeyCallback(), new DisabledLoginCallback(), new DisabledCancelCallback()); copySession.login(new DisabledLoginCallback(), new DisabledCancelCallback()); new CopyWorker(Collections.singletonMap(file, fileRenamed), new SessionPool.SingleSessionPool(copySession, registry), PathCache.empty(), new DisabledProgressListener(), new DisabledConnectionCallback()).run(session); @@ -198,8 +202,8 @@ public class CopyWorkerTest extends AbstractFTPTest { final Path home = new DefaultHomeFinderService(session).find(); 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)); - new DefaultTouchFeature<>(new FTPWriteFeature(session) - ).touch(cleartextFile, new TransferStatus()); + new DefaultTouchFeature( + session).touch(new FTPWriteFeature(session), cleartextFile, new TransferStatus()); assertTrue(new DefaultFindFeature(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)); @@ -207,10 +211,11 @@ public class CopyWorkerTest extends AbstractFTPTest { cryptomator.create(session, new VaultCredentials("test"), vaultVersion); final DefaultVaultRegistry registry = new DefaultVaultRegistry(new DisabledPasswordCallback(), cryptomator); session.withRegistry(registry); - cryptomator.getFeature(session, Directory.class, new FTPDirectoryFeature(session)).mkdir(encryptedFolder, new TransferStatus()); + cryptomator.getFeature(session, Directory.class, new FTPDirectoryFeature(session)).mkdir( + cryptomator.getFeature(session, Write.class, new FTPWriteFeature(session)), encryptedFolder, new TransferStatus()); assertTrue(cryptomator.getFeature(session, Find.class, new DefaultFindFeature(session)).find(encryptedFolder)); // copy file into vault - final FTPSession copySession = new FTPSession(new Host(session.getHost()).withCredentials(new Credentials("test", "test"))); + final FTPSession copySession = new FTPSession(new Host(session.getHost()).setCredentials(new Credentials("test", "test"))); copySession.open(new DisabledProxyFinder(), new DisabledHostKeyCallback(), new DisabledLoginCallback(), new DisabledCancelCallback()); copySession.login(new DisabledLoginCallback(), new DisabledCancelCallback()); final CopyWorker worker = new CopyWorker(Collections.singletonMap(cleartextFile, encryptedFile), new SessionPool.SingleSessionPool(copySession, registry), PathCache.empty(), new DisabledProgressListener(), new DisabledConnectionCallback()); @@ -227,9 +232,9 @@ public class CopyWorkerTest extends AbstractFTPTest { 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 FTPDirectoryFeature(session).mkdir(cleartextFolder, new TransferStatus()); - new DefaultTouchFeature<>(new FTPWriteFeature(session) - ).touch(cleartextFile, new TransferStatus()); + new FTPDirectoryFeature(session).mkdir(new FTPWriteFeature(session), cleartextFolder, new TransferStatus()); + new DefaultTouchFeature( + session).touch(new FTPWriteFeature(session), cleartextFile, new TransferStatus()); assertTrue(new DefaultFindFeature(session).find(cleartextFolder)); assertTrue(new DefaultFindFeature(session).find(cleartextFile)); final CryptoVault cryptomator = new CryptoVault(vault); @@ -239,7 +244,7 @@ public class CopyWorkerTest extends AbstractFTPTest { // move directory into vault final Path encryptedFolder = new Path(vault, cleartextFolder.getName(), EnumSet.of(Path.Type.directory)); final Path encryptedFile = new Path(encryptedFolder, cleartextFile.getName(), EnumSet.of(Path.Type.file)); - final FTPSession copySession = new FTPSession(new Host(session.getHost()).withCredentials(new Credentials("test", "test"))); + final FTPSession copySession = new FTPSession(new Host(session.getHost()).setCredentials(new Credentials("test", "test"))); copySession.open(new DisabledProxyFinder(), new DisabledHostKeyCallback(), new DisabledLoginCallback(), new DisabledCancelCallback()); copySession.login(new DisabledLoginCallback(), new DisabledCancelCallback()); final CopyWorker worker = new CopyWorker(Collections.singletonMap(cleartextFolder, encryptedFolder), new SessionPool.SingleSessionPool(copySession, registry), PathCache.empty(), new DisabledProgressListener(), new DisabledConnectionCallback()); @@ -257,21 +262,22 @@ public class CopyWorkerTest extends AbstractFTPTest { final Path home = new DefaultHomeFinderService(session).find(); 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 FTPDirectoryFeature(session).mkdir(clearFolder, new TransferStatus()); + new FTPDirectoryFeature(session).mkdir(new FTPWriteFeature(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 FTPDirectoryFeature(session)).mkdir(encryptedFolder, new TransferStatus()); + cryptomator.getFeature(session, Directory.class, new FTPDirectoryFeature(session)).mkdir( + new CryptoWriteFeature<>(session, new FTPWriteFeature(session), cryptomator), encryptedFolder, new TransferStatus()); assertTrue(cryptomator.getFeature(session, Find.class, new DefaultFindFeature(session)).find(encryptedFolder)); - new CryptoTouchFeature<>(session, new DefaultTouchFeature<>(new FTPWriteFeature(session) - ), new FTPWriteFeature(session), cryptomator).touch(encryptedFile, new TransferStatus()); + new CryptoTouchFeature<>(session, new DefaultTouchFeature( + session), cryptomator).touch(new CryptoWriteFeature<>(session, new FTPWriteFeature(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 FTPSession copySession = new FTPSession(new Host(session.getHost()).withCredentials(new Credentials("test", "test"))); + final FTPSession copySession = new FTPSession(new Host(session.getHost()).setCredentials(new Credentials("test", "test"))); copySession.open(new DisabledProxyFinder(), new DisabledHostKeyCallback(), new DisabledLoginCallback(), new DisabledCancelCallback()); copySession.login(new DisabledLoginCallback(), new DisabledCancelCallback()); final CopyWorker worker = new CopyWorker(Collections.singletonMap(encryptedFile, cleartextFile), new SessionPool.SingleSessionPool(copySession, registry), PathCache.empty(), new DisabledProgressListener(), new DisabledConnectionCallback()); @@ -292,14 +298,15 @@ public class CopyWorkerTest extends AbstractFTPTest { cryptomator.create(session, new VaultCredentials("test"), vaultVersion); final DefaultVaultRegistry registry = new DefaultVaultRegistry(new DisabledPasswordCallback(), cryptomator); session.withRegistry(registry); - cryptomator.getFeature(session, Directory.class, new FTPDirectoryFeature(session)).mkdir(encryptedFolder, new TransferStatus()); + cryptomator.getFeature(session, Directory.class, new FTPDirectoryFeature(session)).mkdir( + new CryptoWriteFeature<>(session, new FTPWriteFeature(session), cryptomator), encryptedFolder, new TransferStatus()); assertTrue(cryptomator.getFeature(session, Find.class, new DefaultFindFeature(session)).find(encryptedFolder)); - new CryptoTouchFeature<>(session, new DefaultTouchFeature<>(new FTPWriteFeature(session) - ), new FTPWriteFeature(session), cryptomator).touch(encryptedFile, new TransferStatus()); + new CryptoTouchFeature<>(session, new DefaultTouchFeature( + session), cryptomator).touch(new CryptoWriteFeature<>(session, new FTPWriteFeature(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 FTPSession copySession = new FTPSession(new Host(session.getHost()).withCredentials(new Credentials("test", "test"))); + final FTPSession copySession = new FTPSession(new Host(session.getHost()).setCredentials(new Credentials("test", "test"))); copySession.open(new DisabledProxyFinder(), new DisabledHostKeyCallback(), new DisabledLoginCallback(), new DisabledCancelCallback()); copySession.login(new DisabledLoginCallback(), new DisabledCancelCallback()); final CopyWorker worker = new CopyWorker(Collections.singletonMap(encryptedFolder, cleartextFolder), new SessionPool.SingleSessionPool(copySession, registry), PathCache.empty(), new DisabledProgressListener(), new DisabledConnectionCallback()); diff --git a/ftp/src/test/java/ch/cyberduck/core/cryptomator/FTPDirectoryFeatureTest.java b/ftp/src/test/java/ch/cyberduck/core/cryptomator/FTPDirectoryFeatureTest.java index b0fba49b17..ea4c791fdd 100644 --- a/ftp/src/test/java/ch/cyberduck/core/cryptomator/FTPDirectoryFeatureTest.java +++ b/ftp/src/test/java/ch/cyberduck/core/cryptomator/FTPDirectoryFeatureTest.java @@ -22,9 +22,11 @@ import ch.cyberduck.core.Path; 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.ftp.AbstractFTPTest; import ch.cyberduck.core.ftp.FTPDeleteFeature; import ch.cyberduck.core.ftp.FTPDirectoryFeature; +import ch.cyberduck.core.ftp.FTPWriteFeature; import ch.cyberduck.core.shared.DefaultFindFeature; import ch.cyberduck.core.shared.DefaultHomeFinderService; import ch.cyberduck.core.transfer.TransferStatus; @@ -58,9 +60,11 @@ public class FTPDirectoryFeatureTest extends AbstractFTPTest { final CryptoVault cryptomator = new CryptoVault(vault); cryptomator.create(session, new VaultCredentials("test"), vaultVersion); session.withRegistry(new DefaultVaultRegistry(new DisabledPasswordCallback(), cryptomator)); - cryptomator.getFeature(session, Directory.class, new FTPDirectoryFeature(session)).mkdir(testdirectory, new TransferStatus()); + cryptomator.getFeature(session, Directory.class, new FTPDirectoryFeature(session)).mkdir( + cryptomator.getFeature(session, Write.class, new FTPWriteFeature(session)), testdirectory, new TransferStatus()); assertTrue(cryptomator.getFeature(session, Find.class, new DefaultFindFeature(session)).find(testdirectory)); - cryptomator.getFeature(session, Directory.class, new FTPDirectoryFeature(session)).mkdir(testdirectory2, new TransferStatus()); + cryptomator.getFeature(session, Directory.class, new FTPDirectoryFeature(session)).mkdir( + cryptomator.getFeature(session, Write.class, new FTPWriteFeature(session)), testdirectory2, new TransferStatus()); assertTrue(cryptomator.getFeature(session, Find.class, new DefaultFindFeature(session)).find(testdirectory2)); assertFalse(cryptomator.getFeature(session, Find.class, new DefaultFindFeature(session)).find(testfile2)); cryptomator.getFeature(session, Delete.class, new FTPDeleteFeature(session)).delete(Arrays.asList(testdirectory2, testdirectory), new DisabledLoginCallback(), new Delete.DisabledCallback()); @@ -75,7 +79,8 @@ public class FTPDirectoryFeatureTest extends AbstractFTPTest { final CryptoVault cryptomator = new CryptoVault(vault); cryptomator.create(session, new VaultCredentials("test"), vaultVersion); session.withRegistry(new DefaultVaultRegistry(new DisabledPasswordCallback(), cryptomator)); - cryptomator.getFeature(session, Directory.class, new FTPDirectoryFeature(session)).mkdir(test, new TransferStatus()); + cryptomator.getFeature(session, Directory.class, new FTPDirectoryFeature(session)).mkdir( + cryptomator.getFeature(session, Write.class, new FTPWriteFeature(session)), test, new TransferStatus()); assertTrue(cryptomator.getFeature(session, Find.class, new DefaultFindFeature(session)).find(test)); cryptomator.getFeature(session, Delete.class, new FTPDeleteFeature(session)).delete(Arrays.asList(test, vault), new DisabledLoginCallback(), new Delete.DisabledCallback()); } diff --git a/ftp/src/test/java/ch/cyberduck/core/cryptomator/FTPListServiceTest.java b/ftp/src/test/java/ch/cyberduck/core/cryptomator/FTPListServiceTest.java index e3cd5f698b..8eefa27144 100644 --- a/ftp/src/test/java/ch/cyberduck/core/cryptomator/FTPListServiceTest.java +++ b/ftp/src/test/java/ch/cyberduck/core/cryptomator/FTPListServiceTest.java @@ -27,6 +27,7 @@ import ch.cyberduck.core.PathCache; 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.cryptomator.features.CryptoWriteFeature; import ch.cyberduck.core.exception.BackgroundException; import ch.cyberduck.core.features.Delete; import ch.cyberduck.core.ftp.AbstractFTPTest; @@ -66,8 +67,8 @@ public class FTPListServiceTest extends AbstractFTPTest { cryptomator.create(session, new VaultCredentials("test"), vaultVersion); session.withRegistry(new DefaultVaultRegistry(new DisabledPasswordCallback(), cryptomator)); assertTrue(new CryptoListService(session, new FTPListService(session), cryptomator).list(vault).isEmpty()); - new CryptoTouchFeature<>(session, new DefaultTouchFeature<>(new FTPWriteFeature(session) - ), new FTPWriteFeature(session), cryptomator).touch(test, new TransferStatus()); + new CryptoTouchFeature<>(session, new DefaultTouchFeature( + session), cryptomator).touch(new CryptoWriteFeature<>(session, new FTPWriteFeature(session), cryptomator), test, new TransferStatus()); assertEquals(test, new CryptoListService(session, new FTPListService(session), cryptomator).list(vault, new DisabledListProgressListener() { @Override public void cleanup(final Path directory, final AttributedList list, final Optional e) { diff --git a/ftp/src/test/java/ch/cyberduck/core/cryptomator/FTPMoveFeatureTest.java b/ftp/src/test/java/ch/cyberduck/core/cryptomator/FTPMoveFeatureTest.java index 8b1cfb834c..b5f00a980b 100644 --- a/ftp/src/test/java/ch/cyberduck/core/cryptomator/FTPMoveFeatureTest.java +++ b/ftp/src/test/java/ch/cyberduck/core/cryptomator/FTPMoveFeatureTest.java @@ -21,6 +21,7 @@ import ch.cyberduck.core.DisabledLoginCallback; import ch.cyberduck.core.DisabledPasswordCallback; import ch.cyberduck.core.Path; 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; @@ -62,8 +63,10 @@ public class FTPMoveFeatureTest extends AbstractFTPTest { final CryptoVault cryptomator = new CryptoVault(vault); cryptomator.create(session, new VaultCredentials("test"), vaultVersion); session.withRegistry(new DefaultVaultRegistry(new DisabledPasswordCallback(), cryptomator)); - cryptomator.getFeature(session, Directory.class, new FTPDirectoryFeature(session)).mkdir(folder, new TransferStatus()); - new CryptoTouchFeature<>(session, new DefaultTouchFeature<>(new FTPWriteFeature(session)), new FTPWriteFeature(session), cryptomator).touch(file, new TransferStatus()); + cryptomator.getFeature(session, Directory.class, new FTPDirectoryFeature(session)).mkdir( + new CryptoWriteFeature<>(session, new FTPWriteFeature(session), cryptomator), folder, new TransferStatus()); + new CryptoTouchFeature<>(session, new DefaultTouchFeature(session), cryptomator).touch( + new CryptoWriteFeature<>(session, new FTPWriteFeature(session), cryptomator), file, new TransferStatus()); assertTrue(cryptomator.getFeature(session, Find.class, new DefaultFindFeature(session)).find(file)); final Move move = cryptomator.getFeature(session, Move.class, new FTPMoveFeature(session)); // rename file diff --git a/ftp/src/test/java/ch/cyberduck/core/cryptomator/FTPTouchFeatureTest.java b/ftp/src/test/java/ch/cyberduck/core/cryptomator/FTPTouchFeatureTest.java index 51b6d24409..64d7d458c0 100644 --- a/ftp/src/test/java/ch/cyberduck/core/cryptomator/FTPTouchFeatureTest.java +++ b/ftp/src/test/java/ch/cyberduck/core/cryptomator/FTPTouchFeatureTest.java @@ -20,11 +20,15 @@ import ch.cyberduck.core.DisabledLoginCallback; import ch.cyberduck.core.DisabledPasswordCallback; import ch.cyberduck.core.Path; import ch.cyberduck.core.cryptomator.features.CryptoTouchFeature; +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; import ch.cyberduck.core.ftp.AbstractFTPTest; +import ch.cyberduck.core.ftp.FTPAttributesFinderFeature; import ch.cyberduck.core.ftp.FTPDeleteFeature; import ch.cyberduck.core.ftp.FTPWriteFeature; +import ch.cyberduck.core.shared.DefaultAttributesFinderFeature; import ch.cyberduck.core.shared.DefaultFindFeature; import ch.cyberduck.core.shared.DefaultHomeFinderService; import ch.cyberduck.core.shared.DefaultTouchFeature; @@ -56,12 +60,14 @@ public class FTPTouchFeatureTest extends AbstractFTPTest { final CryptoVault cryptomator = new CryptoVault(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 DefaultTouchFeature<>(new FTPWriteFeature(session)), new FTPWriteFeature(session), cryptomator).touch( - new Path(vault, new AlphanumericRandomStringService(10).random(), EnumSet.of(Path.Type.file)), status); + final TransferStatus status = new TransferStatus().setLength(0L); + final Path test = new CryptoTouchFeature<>(session, new DefaultTouchFeature(session), cryptomator).touch( + new CryptoWriteFeature<>(session, new FTPWriteFeature(session), cryptomator), new Path(vault, new AlphanumericRandomStringService(10).random(), EnumSet.of(Path.Type.file)), status); assertEquals(TransferStatus.UNKNOWN_LENGTH, status.getResponse().getSize()); assertEquals(TransferStatus.UNKNOWN_LENGTH, test.attributes().getSize()); assertTrue(cryptomator.getFeature(session, Find.class, new DefaultFindFeature(session)).find(test)); + assertEquals(0L, cryptomator.getFeature(session, AttributesFinder.class, new FTPAttributesFinderFeature(session)).find(test).getSize()); + assertEquals(0L, cryptomator.getFeature(session, AttributesFinder.class, new DefaultAttributesFinderFeature(session)).find(test).getSize()); cryptomator.getFeature(session, Delete.class, new FTPDeleteFeature(session)).delete(Arrays.asList(test, vault), new DisabledLoginCallback(), new Delete.DisabledCallback()); } @@ -74,8 +80,8 @@ public class FTPTouchFeatureTest extends AbstractFTPTest { 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 DefaultTouchFeature<>(new FTPWriteFeature(session)), new FTPWriteFeature(session), cryptomator).touch( - new Path(vault, new AlphanumericRandomStringService(130).random(), EnumSet.of(Path.Type.file)), status); + final Path test = new CryptoTouchFeature<>(session, new DefaultTouchFeature(session), cryptomator).touch( + new CryptoWriteFeature<>(session, new FTPWriteFeature(session), cryptomator), new Path(vault, new AlphanumericRandomStringService(130).random(), EnumSet.of(Path.Type.file)), status); assertEquals(TransferStatus.UNKNOWN_LENGTH, test.attributes().getSize()); assertTrue(cryptomator.getFeature(session, Find.class, new DefaultFindFeature(session)).find(test)); cryptomator.getFeature(session, Delete.class, new FTPDeleteFeature(session)).delete(Arrays.asList(test, vault), new DisabledLoginCallback(), new Delete.DisabledCallback()); @@ -90,8 +96,8 @@ public class FTPTouchFeatureTest extends AbstractFTPTest { 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 DefaultTouchFeature<>(new FTPWriteFeature(session)), new FTPWriteFeature(session), cryptomator).touch( - new Path(vault, new AlphanumericRandomStringService(130).random(), EnumSet.of(Path.Type.file)), status); + final Path test = new CryptoTouchFeature<>(session, new DefaultTouchFeature(session), cryptomator).touch( + new CryptoWriteFeature<>(session, new FTPWriteFeature(session), cryptomator), new Path(vault, new AlphanumericRandomStringService(130).random(), EnumSet.of(Path.Type.file)), status); assertEquals(TransferStatus.UNKNOWN_LENGTH, test.attributes().getSize()); assertTrue(cryptomator.getFeature(session, Find.class, new DefaultFindFeature(session)).find(test)); cryptomator.getFeature(session, Delete.class, new FTPDeleteFeature(session)).delete(Arrays.asList(test, vault), new DisabledLoginCallback(), new Delete.DisabledCallback()); diff --git a/ftp/src/test/java/ch/cyberduck/core/cryptomator/MoveWorkerTest.java b/ftp/src/test/java/ch/cyberduck/core/cryptomator/MoveWorkerTest.java index 2cfff27fd8..d66b42667d 100644 --- a/ftp/src/test/java/ch/cyberduck/core/cryptomator/MoveWorkerTest.java +++ b/ftp/src/test/java/ch/cyberduck/core/cryptomator/MoveWorkerTest.java @@ -32,6 +32,7 @@ import ch.cyberduck.core.exception.NotfoundException; 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.ftp.AbstractFTPTest; import ch.cyberduck.core.ftp.FTPDeleteFeature; import ch.cyberduck.core.ftp.FTPDirectoryFeature; @@ -75,8 +76,8 @@ public class MoveWorkerTest extends AbstractFTPTest { final CryptoVault cryptomator = new CryptoVault(vault); cryptomator.create(session, new VaultCredentials("test"), vaultVersion); session.withRegistry(new DefaultVaultRegistry(new DisabledPasswordCallback(), cryptomator)); - new CryptoTouchFeature<>(session, new DefaultTouchFeature<>(new FTPWriteFeature(session) - ), new FTPWriteFeature(session), cryptomator).touch(source, new TransferStatus()); + new CryptoTouchFeature<>(session, new DefaultTouchFeature( + session), cryptomator).touch(new FTPWriteFeature(session), source, new TransferStatus()); assertTrue(cryptomator.getFeature(session, Find.class, new DefaultFindFeature(session)).find(source)); final MoveWorker worker = new MoveWorker(Collections.singletonMap(source, target), new SessionPool.SingleSessionPool(session), PathCache.empty(), new DisabledProgressListener(), new DisabledLoginCallback()); worker.run(session); @@ -95,10 +96,11 @@ public class MoveWorkerTest extends AbstractFTPTest { final CryptoVault cryptomator = new CryptoVault(vault); cryptomator.create(session, new VaultCredentials("test"), vaultVersion); session.withRegistry(new DefaultVaultRegistry(new DisabledPasswordCallback(), cryptomator)); - new CryptoTouchFeature<>(session, new DefaultTouchFeature<>(new FTPWriteFeature(session) - ), new FTPWriteFeature(session), cryptomator).touch(source, new TransferStatus()); + new CryptoTouchFeature<>(session, new DefaultTouchFeature( + session), cryptomator).touch(new FTPWriteFeature(session), source, new TransferStatus()); assertTrue(cryptomator.getFeature(session, Find.class, new DefaultFindFeature(session)).find(source)); - cryptomator.getFeature(session, Directory.class, new FTPDirectoryFeature(session)).mkdir(targetFolder, new TransferStatus()); + cryptomator.getFeature(session, Directory.class, new FTPDirectoryFeature(session)).mkdir( + cryptomator.getFeature(session, Write.class, new FTPWriteFeature(session)), targetFolder, new TransferStatus()); assertTrue(cryptomator.getFeature(session, Find.class, new DefaultFindFeature(session)).find(targetFolder)); final MoveWorker worker = new MoveWorker(Collections.singletonMap(source, target), new SessionPool.SingleSessionPool(session) , PathCache.empty(), new DisabledProgressListener(), new DisabledLoginCallback()); @@ -120,11 +122,12 @@ public class MoveWorkerTest extends AbstractFTPTest { final CryptoVault cryptomator = new CryptoVault(vault); cryptomator.create(session, new VaultCredentials("test"), vaultVersion); session.withRegistry(new DefaultVaultRegistry(new DisabledPasswordCallback(), cryptomator)); - cryptomator.getFeature(session, Directory.class, new FTPDirectoryFeature(session)).mkdir(sourceFolder, new TransferStatus()); - new CryptoTouchFeature<>(session, new DefaultTouchFeature<>(new FTPWriteFeature(session) - ), new FTPWriteFeature(session), cryptomator).touch(source, new TransferStatus()); + cryptomator.getFeature(session, Directory.class, new FTPDirectoryFeature(session)).mkdir(cryptomator.getFeature(session, Write.class, new FTPWriteFeature(session)), sourceFolder, new TransferStatus()); + new CryptoTouchFeature<>(session, new DefaultTouchFeature( + session), cryptomator).touch(cryptomator.getFeature(session, Write.class, new FTPWriteFeature(session)), source, new TransferStatus()); assertTrue(cryptomator.getFeature(session, Find.class, new DefaultFindFeature(session)).find(source)); - cryptomator.getFeature(session, Directory.class, new FTPDirectoryFeature(session)).mkdir(targetFolder, new TransferStatus()); + cryptomator.getFeature(session, Directory.class, new FTPDirectoryFeature(session)).mkdir( + cryptomator.getFeature(session, Write.class, new FTPWriteFeature(session)), targetFolder, new TransferStatus()); assertTrue(cryptomator.getFeature(session, Find.class, new DefaultFindFeature(session)).find(targetFolder)); final MoveWorker worker = new MoveWorker(Collections.singletonMap(source, target), new SessionPool.SingleSessionPool(session), PathCache.empty(), new DisabledProgressListener(), new DisabledLoginCallback()); worker.run(session); @@ -142,10 +145,11 @@ public class MoveWorkerTest extends AbstractFTPTest { final CryptoVault cryptomator = new CryptoVault(vault); cryptomator.create(session, new VaultCredentials("test"), vaultVersion); session.withRegistry(new DefaultVaultRegistry(new DisabledPasswordCallback(), cryptomator)); - cryptomator.getFeature(session, Directory.class, new FTPDirectoryFeature(session)).mkdir(folder, new TransferStatus()); + cryptomator.getFeature(session, Directory.class, new FTPDirectoryFeature(session)).mkdir( + cryptomator.getFeature(session, Write.class, new FTPWriteFeature(session)), folder, new TransferStatus()); assertTrue(cryptomator.getFeature(session, Find.class, new DefaultFindFeature(session)).find(folder)); - new CryptoTouchFeature<>(session, new DefaultTouchFeature<>(new FTPWriteFeature(session) - ), new FTPWriteFeature(session), cryptomator).touch(file, new TransferStatus()); + new CryptoTouchFeature<>(session, new DefaultTouchFeature( + session), cryptomator).touch(cryptomator.getFeature(session, Write.class, new FTPWriteFeature(session)), file, new TransferStatus()); assertTrue(cryptomator.getFeature(session, Find.class, new DefaultFindFeature(session)).find(file)); // rename file final Path fileRenamed = new Path(folder, "f1", EnumSet.of(Path.Type.file)); @@ -173,8 +177,8 @@ public class MoveWorkerTest extends AbstractFTPTest { final Path home = new DefaultHomeFinderService(session).find(); final Path vault = new Path(home, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory)); final Path clearFile = new Path(home, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)); - new DefaultTouchFeature<>(new FTPWriteFeature(session) - ).touch(clearFile, new TransferStatus()); + new DefaultTouchFeature( + session).touch(new FTPWriteFeature(session), clearFile, new TransferStatus()); assertTrue(new DefaultFindFeature(session).find(clearFile)); 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)); @@ -182,10 +186,11 @@ public class MoveWorkerTest extends AbstractFTPTest { cryptomator.create(session, new VaultCredentials("test"), vaultVersion); final DefaultVaultRegistry registry = new DefaultVaultRegistry(new DisabledPasswordCallback(), cryptomator); session.withRegistry(registry); - cryptomator.getFeature(session, Directory.class, new FTPDirectoryFeature(session)).mkdir(encryptedFolder, new TransferStatus()); + cryptomator.getFeature(session, Directory.class, new FTPDirectoryFeature(session)).mkdir( + cryptomator.getFeature(session, Write.class, new FTPWriteFeature(session)), encryptedFolder, new TransferStatus()); assertTrue(cryptomator.getFeature(session, Find.class, new DefaultFindFeature(session)).find(encryptedFolder)); // move file into vault - final FTPSession copySession = new FTPSession(new Host(session.getHost()).withCredentials(new Credentials("test", "test"))); + final FTPSession copySession = new FTPSession(new Host(session.getHost()).setCredentials(new Credentials("test", "test"))); copySession.open(new DisabledProxyFinder(), new DisabledHostKeyCallback(), new DisabledLoginCallback(), new DisabledCancelCallback()); copySession.login(new DisabledLoginCallback(), new DisabledCancelCallback()); final MoveWorker worker = new MoveWorker(Collections.singletonMap(clearFile, encryptedFile), new SessionPool.SingleSessionPool(copySession), PathCache.empty(), new DisabledProgressListener(), new DisabledLoginCallback()); @@ -202,8 +207,8 @@ public class MoveWorkerTest extends AbstractFTPTest { 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)); final Path clearFile = new Path(clearFolder, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)); - new FTPDirectoryFeature(session).mkdir(clearFolder, new TransferStatus()); - new DefaultTouchFeature<>(new FTPWriteFeature(session)).touch(clearFile, new TransferStatus()); + new FTPDirectoryFeature(session).mkdir(new FTPWriteFeature(session), clearFolder, new TransferStatus()); + new DefaultTouchFeature(session).touch(new FTPWriteFeature(session), clearFile, new TransferStatus()); assertTrue(new DefaultFindFeature(session).find(clearFolder)); assertTrue(new DefaultFindFeature(session).find(clearFile)); final CryptoVault cryptomator = new CryptoVault(vault); @@ -213,7 +218,7 @@ public class MoveWorkerTest extends AbstractFTPTest { // move directory into vault final Path encryptedFolder = new Path(vault, clearFolder.getName(), EnumSet.of(Path.Type.directory)); final Path encryptedFile = new Path(encryptedFolder, clearFile.getName(), EnumSet.of(Path.Type.file)); - final FTPSession copySession = new FTPSession(new Host(session.getHost()).withCredentials(new Credentials("test", "test"))); + final FTPSession copySession = new FTPSession(new Host(session.getHost()).setCredentials(new Credentials("test", "test"))); copySession.open(new DisabledProxyFinder(), new DisabledHostKeyCallback(), new DisabledLoginCallback(), new DisabledCancelCallback()); copySession.login(new DisabledLoginCallback(), new DisabledCancelCallback()); final MoveWorker worker = new MoveWorker(Collections.singletonMap(clearFolder, encryptedFolder), new SessionPool.SingleSessionPool(copySession), PathCache.empty(), new DisabledProgressListener(), new DisabledLoginCallback()); @@ -231,21 +236,22 @@ public class MoveWorkerTest extends AbstractFTPTest { final Path home = new DefaultHomeFinderService(session).find(); 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 FTPDirectoryFeature(session).mkdir(clearFolder, new TransferStatus()); + new FTPDirectoryFeature(session).mkdir(new FTPWriteFeature(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 FTPDirectoryFeature(session)).mkdir(encryptedFolder, new TransferStatus()); + cryptomator.getFeature(session, Directory.class, new FTPDirectoryFeature(session)).mkdir( + cryptomator.getFeature(session, Write.class, new FTPWriteFeature(session)), encryptedFolder, new TransferStatus()); assertTrue(cryptomator.getFeature(session, Find.class, new DefaultFindFeature(session)).find(encryptedFolder)); - new CryptoTouchFeature<>(session, new DefaultTouchFeature<>(new FTPWriteFeature(session) - ), new FTPWriteFeature(session), cryptomator).touch(encryptedFile, new TransferStatus()); + new CryptoTouchFeature<>(session, new DefaultTouchFeature( + session), cryptomator).touch(cryptomator.getFeature(session, Write.class, new FTPWriteFeature(session)), encryptedFile, new TransferStatus()); assertTrue(cryptomator.getFeature(session, Find.class, new DefaultFindFeature(session)).find(encryptedFile)); // move file outside vault final Path fileRenamed = new Path(clearFolder, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)); - final FTPSession copySession = new FTPSession(new Host(session.getHost()).withCredentials(new Credentials("test", "test"))); + final FTPSession copySession = new FTPSession(new Host(session.getHost()).setCredentials(new Credentials("test", "test"))); copySession.open(new DisabledProxyFinder(), new DisabledHostKeyCallback(), new DisabledLoginCallback(), new DisabledCancelCallback()); copySession.login(new DisabledLoginCallback(), new DisabledCancelCallback()); final MoveWorker worker = new MoveWorker(Collections.singletonMap(encryptedFile, fileRenamed), new SessionPool.SingleSessionPool(copySession), PathCache.empty(), new DisabledProgressListener(), new DisabledLoginCallback()); @@ -267,14 +273,15 @@ public class MoveWorkerTest extends AbstractFTPTest { cryptomator.create(session, new VaultCredentials("test"), vaultVersion); final DefaultVaultRegistry registry = new DefaultVaultRegistry(new DisabledPasswordCallback(), cryptomator); session.withRegistry(registry); - cryptomator.getFeature(session, Directory.class, new FTPDirectoryFeature(session)).mkdir(encryptedFolder, new TransferStatus()); + cryptomator.getFeature(session, Directory.class, new FTPDirectoryFeature(session)).mkdir( + cryptomator.getFeature(session, Write.class, new FTPWriteFeature(session)), encryptedFolder, new TransferStatus()); assertTrue(cryptomator.getFeature(session, Find.class, new DefaultFindFeature(session)).find(encryptedFolder)); - new CryptoTouchFeature<>(session, new DefaultTouchFeature<>(new FTPWriteFeature(session) - ), new FTPWriteFeature(session), cryptomator).touch(encryptedFile, new TransferStatus()); + new CryptoTouchFeature<>(session, new DefaultTouchFeature( + session), cryptomator).touch(cryptomator.getFeature(session, Write.class, new FTPWriteFeature(session)), encryptedFile, new TransferStatus()); assertTrue(cryptomator.getFeature(session, Find.class, new DefaultFindFeature(session)).find(encryptedFile)); // move directory outside vault final Path directoryRenamed = new Path(home, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory)); - final FTPSession copySession = new FTPSession(new Host(session.getHost()).withCredentials(new Credentials("test", "test"))); + final FTPSession copySession = new FTPSession(new Host(session.getHost()).setCredentials(new Credentials("test", "test"))); copySession.open(new DisabledProxyFinder(), new DisabledHostKeyCallback(), new DisabledLoginCallback(), new DisabledCancelCallback()); copySession.login(new DisabledLoginCallback(), new DisabledCancelCallback()); final MoveWorker worker = new MoveWorker(Collections.singletonMap(encryptedFolder, directoryRenamed), new SessionPool.SingleSessionPool(copySession), PathCache.empty(), new DisabledProgressListener(), new DisabledLoginCallback()); diff --git a/ftp/src/test/java/ch/cyberduck/core/ftp/FTPAttributesFinderFeatureTest.java b/ftp/src/test/java/ch/cyberduck/core/ftp/FTPAttributesFinderFeatureTest.java index e604751518..00d78c1736 100644 --- a/ftp/src/test/java/ch/cyberduck/core/ftp/FTPAttributesFinderFeatureTest.java +++ b/ftp/src/test/java/ch/cyberduck/core/ftp/FTPAttributesFinderFeatureTest.java @@ -50,7 +50,7 @@ public class FTPAttributesFinderFeatureTest extends AbstractFTPTest { @Test public void testAttributesWrongFiletype() throws Exception { final FTPAttributesFinderFeature f = new FTPAttributesFinderFeature(session); - final Path file = new FTPTouchFeature(session).touch(new Path(new FTPWorkdirService(session).find(), UUID.randomUUID().toString(), EnumSet.of(Path.Type.file)), new TransferStatus()); + final Path file = new FTPTouchFeature(session).touch(new FTPWriteFeature(session), new Path(new FTPWorkdirService(session).find(), UUID.randomUUID().toString(), EnumSet.of(Path.Type.file)), new TransferStatus()); final Attributes attributes = f.find(file); assertEquals(0L, attributes.getSize()); // Test wrong type diff --git a/ftp/src/test/java/ch/cyberduck/core/ftp/FTPDeleteFeatureTest.java b/ftp/src/test/java/ch/cyberduck/core/ftp/FTPDeleteFeatureTest.java index fcc577d5f1..71cf9bf149 100644 --- a/ftp/src/test/java/ch/cyberduck/core/ftp/FTPDeleteFeatureTest.java +++ b/ftp/src/test/java/ch/cyberduck/core/ftp/FTPDeleteFeatureTest.java @@ -52,7 +52,7 @@ public class FTPDeleteFeatureTest extends AbstractFTPTest { @Test public void testDeleteDirectory() throws Exception { final Path test = new Path(new FTPWorkdirService(session).find(), UUID.randomUUID().toString(), EnumSet.of(Path.Type.directory)); - new FTPDirectoryFeature(session).mkdir(test, new TransferStatus()); + new FTPDirectoryFeature(session).mkdir(new FTPWriteFeature(session), test, new TransferStatus()); new FTPDeleteFeature(session).delete(Collections.singletonList(test), new DisabledLoginCallback(), new Delete.DisabledCallback()); } } diff --git a/ftp/src/test/java/ch/cyberduck/core/ftp/FTPDirectoryFeatureTest.java b/ftp/src/test/java/ch/cyberduck/core/ftp/FTPDirectoryFeatureTest.java index 50b774c6b8..02ee39fe46 100644 --- a/ftp/src/test/java/ch/cyberduck/core/ftp/FTPDirectoryFeatureTest.java +++ b/ftp/src/test/java/ch/cyberduck/core/ftp/FTPDirectoryFeatureTest.java @@ -40,9 +40,9 @@ public class FTPDirectoryFeatureTest extends AbstractFTPTest { @Test public void testMakeDirectory() throws Exception { final Path test = new Path(new FTPWorkdirService(session).find(), UUID.randomUUID().toString(), EnumSet.of(Path.Type.directory)); - new FTPDirectoryFeature(session).mkdir(test, new TransferStatus()); + new FTPDirectoryFeature(session).mkdir(new FTPWriteFeature(session), test, new TransferStatus()); assertTrue(session.getFeature(Find.class).find(test)); - assertThrows(ConflictException.class, () -> new FTPDirectoryFeature(session).mkdir(test, new TransferStatus())); + assertThrows(ConflictException.class, () -> new FTPDirectoryFeature(session).mkdir(new FTPWriteFeature(session), test, new TransferStatus())); new FTPDeleteFeature(session).delete(Collections.singletonList(test), new DisabledLoginCallback(), new Delete.DisabledCallback()); assertFalse(session.getFeature(Find.class).find(test)); } diff --git a/ftp/src/test/java/ch/cyberduck/core/ftp/FTPFindFeatureTest.java b/ftp/src/test/java/ch/cyberduck/core/ftp/FTPFindFeatureTest.java index 4bdc367d95..9f59ef8153 100644 --- a/ftp/src/test/java/ch/cyberduck/core/ftp/FTPFindFeatureTest.java +++ b/ftp/src/test/java/ch/cyberduck/core/ftp/FTPFindFeatureTest.java @@ -30,7 +30,8 @@ import java.util.Collections; import java.util.EnumSet; import java.util.UUID; -import static org.junit.Assert.*; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; @Category(IntegrationTest.class) public class FTPFindFeatureTest extends AbstractFTPTest { @@ -48,7 +49,7 @@ public class FTPFindFeatureTest extends AbstractFTPTest { @Test public void testFindFile() throws Exception { final Path file = new Path(new DefaultHomeFinderService(session).find(), new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)); - new FTPTouchFeature(session).touch(file, new TransferStatus()); + new FTPTouchFeature(session).touch(new FTPWriteFeature(session), file, new TransferStatus()); assertTrue(new FTPFindFeature(session).find(file)); assertFalse(new FTPFindFeature(session).find(new Path(file.getAbsolute(), EnumSet.of(Path.Type.directory)))); new FTPDeleteFeature(session).delete(Collections.singletonList(file), new DisabledLoginCallback(), new Delete.DisabledCallback()); diff --git a/ftp/src/test/java/ch/cyberduck/core/ftp/FTPMFMTTimestampFeatureTest.java b/ftp/src/test/java/ch/cyberduck/core/ftp/FTPMFMTTimestampFeatureTest.java index f6d06da784..41fd053a02 100644 --- a/ftp/src/test/java/ch/cyberduck/core/ftp/FTPMFMTTimestampFeatureTest.java +++ b/ftp/src/test/java/ch/cyberduck/core/ftp/FTPMFMTTimestampFeatureTest.java @@ -42,7 +42,7 @@ public class FTPMFMTTimestampFeatureTest extends AbstractFTPTest { final Path home = new FTPWorkdirService(session).find(); final long modified = System.currentTimeMillis(); final Path test = new Path(new FTPWorkdirService(session).find(), UUID.randomUUID().toString(), EnumSet.of(Path.Type.file)); - new FTPTouchFeature(session).touch(test, new TransferStatus()); + new FTPTouchFeature(session).touch(new FTPWriteFeature(session), test, new TransferStatus()); new FTPMFMTTimestampFeature(session).setTimestamp(test, modified); assertEquals(modified / 1000 * 1000, new FTPListService(session).list(home, new DisabledListProgressListener()).get(test).attributes().getModificationDate()); new FTPDeleteFeature(session).delete(Collections.singletonList(test), new DisabledLoginCallback(), new Delete.DisabledCallback()); diff --git a/ftp/src/test/java/ch/cyberduck/core/ftp/FTPMoveFeatureTest.java b/ftp/src/test/java/ch/cyberduck/core/ftp/FTPMoveFeatureTest.java index 0387b02884..1e3af2bc4b 100644 --- a/ftp/src/test/java/ch/cyberduck/core/ftp/FTPMoveFeatureTest.java +++ b/ftp/src/test/java/ch/cyberduck/core/ftp/FTPMoveFeatureTest.java @@ -44,7 +44,7 @@ public class FTPMoveFeatureTest extends AbstractFTPTest { public void testMove() throws Exception { final FTPWorkdirService workdir = new FTPWorkdirService(session); final Path test = new Path(workdir.find(), UUID.randomUUID().toString(), EnumSet.of(Path.Type.file)); - new FTPTouchFeature(session).touch(test, new TransferStatus()); + new FTPTouchFeature(session).touch(new FTPWriteFeature(session), test, new TransferStatus()); final Path target = new Path(workdir.find(), UUID.randomUUID().toString(), EnumSet.of(Path.Type.file)); new FTPMoveFeature(session).move(test, target, new TransferStatus(), new Delete.DisabledCallback(), new DisabledConnectionCallback()); assertFalse(session.getFeature(Find.class).find(test)); @@ -56,9 +56,9 @@ public class FTPMoveFeatureTest extends AbstractFTPTest { public void testMoveOverride() throws Exception { final Home workdir = new FTPWorkdirService(session); final Path test = new Path(workdir.find(), UUID.randomUUID().toString(), EnumSet.of(Path.Type.file)); - new FTPTouchFeature(session).touch(test, new TransferStatus()); + new FTPTouchFeature(session).touch(new FTPWriteFeature(session), test, new TransferStatus()); final Path target = new Path(workdir.find(), UUID.randomUUID().toString(), EnumSet.of(Path.Type.file)); - new FTPTouchFeature(session).touch(target, new TransferStatus()); + new FTPTouchFeature(session).touch(new FTPWriteFeature(session), target, new TransferStatus()); try { new FTPMoveFeature(session).move(test, target, new TransferStatus().setExists(false), new Delete.DisabledCallback(), new DisabledConnectionCallback()); fail(); diff --git a/ftp/src/test/java/ch/cyberduck/core/ftp/FTPReadFeatureTest.java b/ftp/src/test/java/ch/cyberduck/core/ftp/FTPReadFeatureTest.java index 4cc523679e..1df491e7e9 100644 --- a/ftp/src/test/java/ch/cyberduck/core/ftp/FTPReadFeatureTest.java +++ b/ftp/src/test/java/ch/cyberduck/core/ftp/FTPReadFeatureTest.java @@ -56,7 +56,8 @@ public class FTPReadFeatureTest extends AbstractFTPTest { public void testRead() throws Exception { final Path home = new FTPWorkdirService(session).find(); final Path test = new Path(home, UUID.randomUUID().toString(), EnumSet.of(Path.Type.file)); - new FTPTouchFeature(session).touch(test, new TransferStatus()); + assertThrows(NotfoundException.class, () -> new FTPReadFeature(session).read(test, new TransferStatus(), new DisabledConnectionCallback())); + new FTPTouchFeature(session).touch(new FTPWriteFeature(session), test, new TransferStatus()); final int length = 39865; final byte[] content = RandomUtils.nextBytes(length); { @@ -82,7 +83,7 @@ public class FTPReadFeatureTest extends AbstractFTPTest { @Test public void testReadRange() throws Exception { final Path test = new Path(new FTPWorkdirService(session).find(), UUID.randomUUID().toString(), EnumSet.of(Path.Type.file)); - new FTPTouchFeature(session).touch(test, new TransferStatus()); + new FTPTouchFeature(session).touch(new FTPWriteFeature(session), test, new TransferStatus()); final byte[] content = RandomUtils.nextBytes(2048); final OutputStream out = new FTPWriteFeature(session).write(test, new TransferStatus().setLength(content.length), new DisabledConnectionCallback()); assertNotNull(out); @@ -111,7 +112,7 @@ public class FTPReadFeatureTest extends AbstractFTPTest { status.setLength(5L); final Path workdir = new FTPWorkdirService(session).find(); final Path file = new Path(workdir, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)); - new DefaultTouchFeature<>(new FTPWriteFeature(session)).touch(file, new TransferStatus()); + new DefaultTouchFeature(session).touch(new FTPWriteFeature(session), file, new TransferStatus()); final InputStream in = new FTPReadFeature(session).read(file, status, new DisabledConnectionCallback()); assertNotNull(in); // Send ABOR because stream was not read completly @@ -124,7 +125,7 @@ public class FTPReadFeatureTest extends AbstractFTPTest { @Test public void testAbortPartialRead() throws Exception { final Path test = new Path(new FTPWorkdirService(session).find(), UUID.randomUUID().toString(), EnumSet.of(Path.Type.file)); - new FTPTouchFeature(session).touch(test, new TransferStatus()); + new FTPTouchFeature(session).touch(new FTPWriteFeature(session), test, new TransferStatus()); final OutputStream out = new FTPWriteFeature(session).write(test, new TransferStatus().setLength(20L), new DisabledConnectionCallback()); assertNotNull(out); final byte[] content = RandomUtils.nextBytes(2048); @@ -146,7 +147,7 @@ public class FTPReadFeatureTest extends AbstractFTPTest { @Test public void testDoubleCloseStream() throws Exception { final Path file = new Path(new FTPWorkdirService(session).find(), UUID.randomUUID().toString(), EnumSet.of(Path.Type.file)); - new DefaultTouchFeature<>(new FTPWriteFeature(session)).touch(file, new TransferStatus()); + new DefaultTouchFeature(session).touch(new FTPWriteFeature(session), file, new TransferStatus()); final TransferStatus status = new TransferStatus(); status.setLength(5L); final Path workdir = new FTPWorkdirService(session).find(); diff --git a/ftp/src/test/java/ch/cyberduck/core/ftp/FTPSessionTest.java b/ftp/src/test/java/ch/cyberduck/core/ftp/FTPSessionTest.java index 1211552211..397e4aaf80 100644 --- a/ftp/src/test/java/ch/cyberduck/core/ftp/FTPSessionTest.java +++ b/ftp/src/test/java/ch/cyberduck/core/ftp/FTPSessionTest.java @@ -87,7 +87,7 @@ public class FTPSessionTest extends AbstractFTPTest { @Test public void testTouch() throws Exception { final Path test = new Path(new FTPWorkdirService(session).find(), UUID.randomUUID().toString(), EnumSet.of(Path.Type.file)); - new FTPTouchFeature(session).touch(test, new TransferStatus()); + new FTPTouchFeature(session).touch(new FTPWriteFeature(session), test, new TransferStatus()); assertTrue(session.getFeature(Find.class).find(test)); new FTPDeleteFeature(session).delete(Collections.singletonList(test), new DisabledLoginCallback(), new Delete.DisabledCallback()); assertFalse(session.getFeature(Find.class).find(test)); diff --git a/ftp/src/test/java/ch/cyberduck/core/ftp/FTPUTIMETimestampFeatureTest.java b/ftp/src/test/java/ch/cyberduck/core/ftp/FTPUTIMETimestampFeatureTest.java index 422ff4db00..34430a0a26 100644 --- a/ftp/src/test/java/ch/cyberduck/core/ftp/FTPUTIMETimestampFeatureTest.java +++ b/ftp/src/test/java/ch/cyberduck/core/ftp/FTPUTIMETimestampFeatureTest.java @@ -39,7 +39,7 @@ public class FTPUTIMETimestampFeatureTest extends AbstractFTPTest { final FTPWorkdirService workdir = new FTPWorkdirService(session); final long modified = System.currentTimeMillis(); final Path test = new Path(workdir.find(), UUID.randomUUID().toString(), EnumSet.of(Path.Type.file)); - new FTPTouchFeature(session).touch(test, new TransferStatus()); + new FTPTouchFeature(session).touch(new FTPWriteFeature(session), test, new TransferStatus()); new FTPUTIMETimestampFeature(session).setTimestamp(test, modified); new FTPDeleteFeature(session).delete(Collections.singletonList(test), new DisabledLoginCallback(), new Delete.DisabledCallback()); } diff --git a/ftp/src/test/java/ch/cyberduck/core/ftp/FTPUnixPermissionFeatureTest.java b/ftp/src/test/java/ch/cyberduck/core/ftp/FTPUnixPermissionFeatureTest.java index 767ed3ffba..e7c2d67b49 100644 --- a/ftp/src/test/java/ch/cyberduck/core/ftp/FTPUnixPermissionFeatureTest.java +++ b/ftp/src/test/java/ch/cyberduck/core/ftp/FTPUnixPermissionFeatureTest.java @@ -27,7 +27,7 @@ public class FTPUnixPermissionFeatureTest extends AbstractFTPTest { final FTPWorkdirService workdir = new FTPWorkdirService(session); final Path home = workdir.find(); final Path test = new Path(home, UUID.randomUUID().toString(), EnumSet.of(Path.Type.file)); - new FTPTouchFeature(session).touch(test, new TransferStatus()); + new FTPTouchFeature(session).touch(new FTPWriteFeature(session), test, new TransferStatus()); new FTPUnixPermissionFeature(session).setUnixPermission(test, new Permission(666)); assertEquals("666", new FTPListService(session).list(home, new DisabledListProgressListener()).get(test).attributes().getPermission().getMode()); new FTPDeleteFeature(session).delete(Collections.singletonList(test), new DisabledLoginCallback(), new Delete.DisabledCallback()); diff --git a/ftp/src/test/java/ch/cyberduck/core/ftp/FTPUploadFeatureTest.java b/ftp/src/test/java/ch/cyberduck/core/ftp/FTPUploadFeatureTest.java index 8dfc5b8c38..1fb7a8ceff 100644 --- a/ftp/src/test/java/ch/cyberduck/core/ftp/FTPUploadFeatureTest.java +++ b/ftp/src/test/java/ch/cyberduck/core/ftp/FTPUploadFeatureTest.java @@ -33,7 +33,7 @@ public class FTPUploadFeatureTest extends AbstractFTPTest { @Test public void testAppend() throws Exception { final Path f = new Path(new FTPWorkdirService(session).find(), new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)); - new FTPTouchFeature(session).touch(f, new TransferStatus()); + new FTPTouchFeature(session).touch(new FTPWriteFeature(session), f, new TransferStatus()); assertTrue(new FTPUploadFeature(session).append(f, new TransferStatus().setExists(true).setLength(0L).setRemote(new FTPAttributesFinderFeature(session).find(f))).append); new FTPDeleteFeature(session).delete(Collections.singletonList(f), new DisabledLoginCallback(), new Delete.DisabledCallback()); } diff --git a/ftp/src/test/java/ch/cyberduck/core/ftp/list/FTPDefaultListServiceTest.java b/ftp/src/test/java/ch/cyberduck/core/ftp/list/FTPDefaultListServiceTest.java index 214196aa1b..d2ea373a97 100644 --- a/ftp/src/test/java/ch/cyberduck/core/ftp/list/FTPDefaultListServiceTest.java +++ b/ftp/src/test/java/ch/cyberduck/core/ftp/list/FTPDefaultListServiceTest.java @@ -25,6 +25,7 @@ import ch.cyberduck.core.ftp.AbstractFTPTest; import ch.cyberduck.core.ftp.FTPDeleteFeature; import ch.cyberduck.core.ftp.FTPTouchFeature; import ch.cyberduck.core.ftp.FTPWorkdirService; +import ch.cyberduck.core.ftp.FTPWriteFeature; import ch.cyberduck.core.ftp.parser.CompositeFileEntryParser; import ch.cyberduck.core.transfer.TransferStatus; import ch.cyberduck.test.IntegrationTest; @@ -47,7 +48,7 @@ public class FTPDefaultListServiceTest extends AbstractFTPTest { FTPListService.Command.list); final Path directory = new FTPWorkdirService(session).find(); final Path file = new Path(directory, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)); - new FTPTouchFeature(session).touch(file, new TransferStatus()); + new FTPTouchFeature(session).touch(new FTPWriteFeature(session), file, new TransferStatus()); assertTrue(list.list(directory, new DisabledListProgressListener()).contains(file)); new FTPDeleteFeature(session).delete(Collections.singletonList(file), new DisabledLoginCallback(), new Delete.DisabledCallback()); } @@ -58,7 +59,7 @@ public class FTPDefaultListServiceTest extends AbstractFTPTest { FTPListService.Command.lista); final Path directory = new FTPWorkdirService(session).find(); final Path file = new Path(directory, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)); - new FTPTouchFeature(session).touch(file, new TransferStatus()); + new FTPTouchFeature(session).touch(new FTPWriteFeature(session), file, new TransferStatus()); assertTrue(list.list(directory, new DisabledListProgressListener()).contains(file)); new FTPDeleteFeature(session).delete(Collections.singletonList(file), new DisabledLoginCallback(), new Delete.DisabledCallback()); } diff --git a/ftp/src/test/java/ch/cyberduck/core/ftp/list/FTPListServiceTest.java b/ftp/src/test/java/ch/cyberduck/core/ftp/list/FTPListServiceTest.java index a07ae84095..1bdd5b2db9 100644 --- a/ftp/src/test/java/ch/cyberduck/core/ftp/list/FTPListServiceTest.java +++ b/ftp/src/test/java/ch/cyberduck/core/ftp/list/FTPListServiceTest.java @@ -31,6 +31,7 @@ import ch.cyberduck.core.ftp.FTPDeleteFeature; import ch.cyberduck.core.ftp.FTPDirectoryFeature; import ch.cyberduck.core.ftp.FTPTouchFeature; import ch.cyberduck.core.ftp.FTPWorkdirService; +import ch.cyberduck.core.ftp.FTPWriteFeature; import ch.cyberduck.core.transfer.TransferStatus; import ch.cyberduck.test.IntegrationTest; @@ -53,7 +54,7 @@ public class FTPListServiceTest extends AbstractFTPTest { final ListService service = new FTPListService(session); final Path directory = new FTPWorkdirService(session).find(); final Path file = new Path(directory, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)); - new FTPTouchFeature(session).touch(file, new TransferStatus()); + new FTPTouchFeature(session).touch(new FTPWriteFeature(session), file, new TransferStatus()); final AttributedList list = service.list(directory, new DisabledListProgressListener() { @Override public void chunk(final Path parent, AttributedList list) { @@ -72,7 +73,7 @@ public class FTPListServiceTest extends AbstractFTPTest { service.remove(FTPListService.Command.mlsd); final Path directory = new FTPWorkdirService(session).find(); final Path file = new Path(directory, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)); - new FTPTouchFeature(session).touch(file, new TransferStatus()); + new FTPTouchFeature(session).touch(new FTPWriteFeature(session), file, new TransferStatus()); final AttributedList list = service.list(directory, new DisabledListProgressListener() { @Override public void chunk(final Path parent, AttributedList list) { @@ -91,7 +92,7 @@ public class FTPListServiceTest extends AbstractFTPTest { list.remove(FTPListService.Command.mlsd); final Path home = new FTPWorkdirService(session).find(); final Path directory = new Path(home, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory)); - new FTPDirectoryFeature(session).mkdir(directory, new TransferStatus()); + new FTPDirectoryFeature(session).mkdir(new FTPWriteFeature(session), directory, new TransferStatus()); final AtomicBoolean callback = new AtomicBoolean(); assertTrue(list.list(directory, new DisabledListProgressListener() { @Override diff --git a/ftp/src/test/java/ch/cyberduck/core/ftp/list/FTPMlsdListServiceTest.java b/ftp/src/test/java/ch/cyberduck/core/ftp/list/FTPMlsdListServiceTest.java index 6611165a2a..c682b25af8 100644 --- a/ftp/src/test/java/ch/cyberduck/core/ftp/list/FTPMlsdListServiceTest.java +++ b/ftp/src/test/java/ch/cyberduck/core/ftp/list/FTPMlsdListServiceTest.java @@ -26,18 +26,16 @@ import ch.cyberduck.core.ftp.AbstractFTPTest; import ch.cyberduck.core.ftp.FTPDeleteFeature; import ch.cyberduck.core.ftp.FTPTouchFeature; import ch.cyberduck.core.ftp.FTPWorkdirService; -import ch.cyberduck.core.ftp.parser.CompositeFileEntryParser; +import ch.cyberduck.core.ftp.FTPWriteFeature; import ch.cyberduck.core.transfer.TransferStatus; import ch.cyberduck.test.IntegrationTest; -import org.apache.commons.net.ftp.parser.UnixFTPEntryParser; import org.junit.Test; import org.junit.experimental.categories.Category; import java.util.Collections; import java.util.EnumSet; -import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertTrue; @Category(IntegrationTest.class) @@ -48,7 +46,7 @@ public class FTPMlsdListServiceTest extends AbstractFTPTest { final ListService service = new FTPMlsdListService(session); final Path directory = new FTPWorkdirService(session).find(); final Path file = new Path(directory, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)); - new FTPTouchFeature(session).touch(file, new TransferStatus()); + new FTPTouchFeature(session).touch(new FTPWriteFeature(session), file, new TransferStatus()); final AttributedList list = service.list(directory, new DisabledListProgressListener()); assertTrue(list.contains(file)); new FTPDeleteFeature(session).delete(Collections.singletonList(file), new DisabledLoginCallback(), new Delete.DisabledCallback()); diff --git a/ftp/src/test/java/ch/cyberduck/core/ftp/list/FTPStatListServiceTest.java b/ftp/src/test/java/ch/cyberduck/core/ftp/list/FTPStatListServiceTest.java index 9c30dee716..02121089f7 100644 --- a/ftp/src/test/java/ch/cyberduck/core/ftp/list/FTPStatListServiceTest.java +++ b/ftp/src/test/java/ch/cyberduck/core/ftp/list/FTPStatListServiceTest.java @@ -26,6 +26,7 @@ import ch.cyberduck.core.ftp.AbstractFTPTest; import ch.cyberduck.core.ftp.FTPDeleteFeature; import ch.cyberduck.core.ftp.FTPTouchFeature; import ch.cyberduck.core.ftp.FTPWorkdirService; +import ch.cyberduck.core.ftp.FTPWriteFeature; import ch.cyberduck.core.ftp.parser.CompositeFileEntryParser; import ch.cyberduck.core.ftp.parser.LaxUnixFTPEntryParser; import ch.cyberduck.core.transfer.TransferStatus; @@ -53,7 +54,7 @@ public class FTPStatListServiceTest extends AbstractFTPTest { new CompositeFileEntryParser(Collections.singletonList(new UnixFTPEntryParser()))); final Path directory = new FTPWorkdirService(session).find(); final Path file = new Path(directory, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)); - new FTPTouchFeature(session).touch(file, new TransferStatus()); + new FTPTouchFeature(session).touch(new FTPWriteFeature(session), file, new TransferStatus()); final AttributedList list = service.list(directory, new DisabledListProgressListener()); assertTrue(list.contains(file)); new FTPDeleteFeature(session).delete(Collections.singletonList(file), new DisabledLoginCallback(), new Delete.DisabledCallback()); diff --git a/ftp/src/test/java/ch/cyberduck/core/shared/DefaultDownloadFeatureTest.java b/ftp/src/test/java/ch/cyberduck/core/shared/DefaultDownloadFeatureTest.java index 80f0a6180b..382e5d0044 100644 --- a/ftp/src/test/java/ch/cyberduck/core/shared/DefaultDownloadFeatureTest.java +++ b/ftp/src/test/java/ch/cyberduck/core/shared/DefaultDownloadFeatureTest.java @@ -53,7 +53,7 @@ public class DefaultDownloadFeatureTest extends AbstractFTPTest { @Test public void testTransferAppend() throws Exception { final Path test = new Path(new DefaultHomeFinderService(session).find(), UUID.randomUUID().toString(), EnumSet.of(Path.Type.file)); - new FTPTouchFeature(session).touch(test, new TransferStatus()); + new FTPTouchFeature(session).touch(new FTPWriteFeature(session), test, new TransferStatus()); final byte[] content = new byte[39864]; new Random().nextBytes(content); { @@ -66,15 +66,15 @@ public class DefaultDownloadFeatureTest extends AbstractFTPTest { final Local local = new Local(System.getProperty("java.io.tmpdir"), UUID.randomUUID().toString()); { final TransferStatus status = new TransferStatus().setLength(content.length / 2); - new DefaultDownloadFeature(new FTPReadFeature(session)).download( - test, local, new BandwidthThrottle(BandwidthThrottle.UNLIMITED), new DisabledStreamListener(), + new DefaultDownloadFeature(session).download( + new FTPReadFeature(session), test, local, new BandwidthThrottle(BandwidthThrottle.UNLIMITED), new DisabledStreamListener(), status, new DisabledConnectionCallback()); } { final TransferStatus status = new TransferStatus().setLength(content.length / 2).setOffset(content.length / 2).setAppend(true); - new DefaultDownloadFeature(new FTPReadFeature(session)).download( - test, local, new BandwidthThrottle(BandwidthThrottle.UNLIMITED), new DisabledStreamListener(), + new DefaultDownloadFeature(session).download( + new FTPReadFeature(session), test, local, new BandwidthThrottle(BandwidthThrottle.UNLIMITED), new DisabledStreamListener(), status, new DisabledConnectionCallback()); } diff --git a/ftp/src/test/java/ch/cyberduck/core/shared/DefaultUploadFeatureTest.java b/ftp/src/test/java/ch/cyberduck/core/shared/DefaultUploadFeatureTest.java index 8182a4a5fe..993de99f1e 100644 --- a/ftp/src/test/java/ch/cyberduck/core/shared/DefaultUploadFeatureTest.java +++ b/ftp/src/test/java/ch/cyberduck/core/shared/DefaultUploadFeatureTest.java @@ -58,15 +58,15 @@ public class DefaultUploadFeatureTest extends AbstractFTPTest { final Path test = new Path(new DefaultHomeFinderService(session).find(), UUID.randomUUID().toString(), EnumSet.of(Path.Type.file)); { final TransferStatus status = new TransferStatus().setLength(content.length / 2); - final Void reply = new DefaultUploadFeature<>(new FTPWriteFeature(session)).upload( - test, local, new BandwidthThrottle(BandwidthThrottle.UNLIMITED), new DisabledProgressListener(), new DisabledStreamListener(), + final Void reply = new DefaultUploadFeature(session).upload( + new FTPWriteFeature(session), test, local, new BandwidthThrottle(BandwidthThrottle.UNLIMITED), new DisabledProgressListener(), new DisabledStreamListener(), status, new DisabledConnectionCallback()); } { final TransferStatus status = new TransferStatus().setLength(content.length / 2).setOffset(content.length / 2).setAppend(true); - final Void reply = new DefaultUploadFeature(new FTPWriteFeature(session)).upload( - test, local, new BandwidthThrottle(BandwidthThrottle.UNLIMITED), new DisabledProgressListener(), new DisabledStreamListener(), + final Void reply = new DefaultUploadFeature(session).upload( + new FTPWriteFeature(session), test, local, new BandwidthThrottle(BandwidthThrottle.UNLIMITED), new DisabledProgressListener(), new DisabledStreamListener(), status, new DisabledConnectionCallback()); } diff --git a/ftp/src/test/java/ch/cyberduck/core/worker/CopyWorkerTest.java b/ftp/src/test/java/ch/cyberduck/core/worker/CopyWorkerTest.java index 1aad2ebb0c..bcb537f81a 100644 --- a/ftp/src/test/java/ch/cyberduck/core/worker/CopyWorkerTest.java +++ b/ftp/src/test/java/ch/cyberduck/core/worker/CopyWorkerTest.java @@ -29,6 +29,7 @@ import ch.cyberduck.core.ftp.AbstractFTPTest; import ch.cyberduck.core.ftp.FTPDirectoryFeature; import ch.cyberduck.core.ftp.FTPSession; import ch.cyberduck.core.ftp.FTPTouchFeature; +import ch.cyberduck.core.ftp.FTPWriteFeature; import ch.cyberduck.core.pool.SessionPool; import ch.cyberduck.core.proxy.DisabledProxyFinder; import ch.cyberduck.core.shared.DefaultFindFeature; @@ -53,9 +54,9 @@ public class CopyWorkerTest extends AbstractFTPTest { final Path home = new DefaultHomeFinderService(session).find(); 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 FTPTouchFeature(session).touch(source, new TransferStatus()); + new FTPTouchFeature(session).touch(new FTPWriteFeature(session), source, new TransferStatus()); assertTrue(new DefaultFindFeature(session).find(source)); - final FTPSession copySession = new FTPSession(new Host(session.getHost()).withCredentials(new Credentials("test", "test"))); + final FTPSession copySession = new FTPSession(new Host(session.getHost()).setCredentials(new Credentials("test", "test"))); copySession.open(new DisabledProxyFinder(), new DisabledHostKeyCallback(), new DisabledLoginCallback(), new DisabledCancelCallback()); copySession.login(new DisabledLoginCallback(), new DisabledCancelCallback()); final CopyWorker worker = new CopyWorker(Collections.singletonMap(source, target), new SessionPool.SingleSessionPool(copySession), PathCache.empty(), new DisabledProgressListener(), new DisabledConnectionCallback()); @@ -69,14 +70,14 @@ public class CopyWorkerTest extends AbstractFTPTest { public void testCopyFileToDirectory() throws Exception { final Path home = new DefaultHomeFinderService(session).find(); final Path sourceFile = new Path(home, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)); - new FTPTouchFeature(session).touch(sourceFile, new TransferStatus()); + new FTPTouchFeature(session).touch(new FTPWriteFeature(session), sourceFile, new TransferStatus()); assertTrue(new DefaultFindFeature(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 FTPDirectoryFeature(session).mkdir(targetFolder, new TransferStatus()); + new FTPDirectoryFeature(session).mkdir(new FTPWriteFeature(session), targetFolder, new TransferStatus()); assertTrue(new DefaultFindFeature(session).find(targetFolder)); // copy file into vault - final FTPSession copySession = new FTPSession(new Host(session.getHost()).withCredentials(new Credentials("test", "test"))); + final FTPSession copySession = new FTPSession(new Host(session.getHost()).setCredentials(new Credentials("test", "test"))); copySession.open(new DisabledProxyFinder(), new DisabledHostKeyCallback(), new DisabledLoginCallback(), new DisabledCancelCallback()); copySession.login(new DisabledLoginCallback(), new DisabledCancelCallback()); final CopyWorker worker = new CopyWorker(Collections.singletonMap(sourceFile, targetFile), new SessionPool.SingleSessionPool(copySession), PathCache.empty(), new DisabledProgressListener(), new DisabledConnectionCallback()); @@ -91,14 +92,14 @@ public class CopyWorkerTest extends AbstractFTPTest { final Path home = new DefaultHomeFinderService(session).find(); 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 FTPDirectoryFeature(session).mkdir(folder, new TransferStatus()); - new FTPTouchFeature(session).touch(sourceFile, new TransferStatus()); + new FTPDirectoryFeature(session).mkdir(new FTPWriteFeature(session), folder, new TransferStatus()); + new FTPTouchFeature(session).touch(new FTPWriteFeature(session), sourceFile, new TransferStatus()); assertTrue(new DefaultFindFeature(session).find(folder)); assertTrue(new DefaultFindFeature(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 FTPSession copySession = new FTPSession(new Host(session.getHost()).withCredentials(new Credentials("test", "test"))); + final FTPSession copySession = new FTPSession(new Host(session.getHost()).setCredentials(new Credentials("test", "test"))); copySession.open(new DisabledProxyFinder(), new DisabledHostKeyCallback(), new DisabledLoginCallback(), new DisabledCancelCallback()); copySession.login(new DisabledLoginCallback(), new DisabledCancelCallback()); final CopyWorker worker = new CopyWorker(Collections.singletonMap(folder, targetFolder), new SessionPool.SingleSessionPool(copySession), PathCache.empty(), new DisabledProgressListener(), new DisabledConnectionCallback()); diff --git a/ftp/src/test/java/ch/cyberduck/core/worker/FTPConcurrentTransferWorkerTest.java b/ftp/src/test/java/ch/cyberduck/core/worker/FTPConcurrentTransferWorkerTest.java index 6c16e495ef..bb2947cd29 100644 --- a/ftp/src/test/java/ch/cyberduck/core/worker/FTPConcurrentTransferWorkerTest.java +++ b/ftp/src/test/java/ch/cyberduck/core/worker/FTPConcurrentTransferWorkerTest.java @@ -18,12 +18,11 @@ package ch.cyberduck.core.worker; import ch.cyberduck.core.*; import ch.cyberduck.core.exception.BackgroundException; import ch.cyberduck.core.features.Delete; -import ch.cyberduck.core.features.Upload; +import ch.cyberduck.core.features.Write; import ch.cyberduck.core.ftp.AbstractFTPTest; import ch.cyberduck.core.ftp.FTPAttributesFinderFeature; import ch.cyberduck.core.ftp.FTPDeleteFeature; import ch.cyberduck.core.ftp.FTPSession; -import ch.cyberduck.core.ftp.FTPUploadFeature; import ch.cyberduck.core.ftp.FTPWriteFeature; import ch.cyberduck.core.io.DisabledStreamListener; import ch.cyberduck.core.io.StatusOutputStream; @@ -131,8 +130,8 @@ public class FTPConcurrentTransferWorkerTest extends AbstractFTPTest { @Override @SuppressWarnings("unchecked") public T _getFeature(final Class type) { - if(type == Upload.class) { - return (T) new FTPUploadFeature(write); + if(type == Write.class) { + return (T) write; } return super._getFeature(type); } diff --git a/global.json b/global.json index 3124b4973a..e70bdcde32 100644 --- a/global.json +++ b/global.json @@ -1,6 +1,6 @@ { "msbuild-sdks": { - "IKVM.NET.Sdk": "8.11.2", + "IKVM.NET.Sdk": "8.13.3", "Microsoft.Build.NoTargets": "3.7.56", "Microsoft.Build.Traversal": "4.1.0", "WixToolset.Sdk": "5.0.2" diff --git a/googledrive/pom.xml b/googledrive/pom.xml index 68d593e913..6eed0f41fb 100644 --- a/googledrive/pom.xml +++ b/googledrive/pom.xml @@ -18,12 +18,12 @@ parent ch.cyberduck - 9.2.0-SNAPSHOT + 9.4.0-SNAPSHOT 4.0.0 googledrive - v3-rev20250511-2.0.0 + v3-rev20251210-2.0.0 diff --git a/googledrive/src/main/java/ch/cyberduck/core/googledrive/DriveAttributesFinderFeature.java b/googledrive/src/main/java/ch/cyberduck/core/googledrive/DriveAttributesFinderFeature.java index b356c584f5..d9b6f816e0 100644 --- a/googledrive/src/main/java/ch/cyberduck/core/googledrive/DriveAttributesFinderFeature.java +++ b/googledrive/src/main/java/ch/cyberduck/core/googledrive/DriveAttributesFinderFeature.java @@ -16,6 +16,7 @@ package ch.cyberduck.core.googledrive; */ import ch.cyberduck.core.AttributedList; +import ch.cyberduck.core.DefaultPathAttributes; import ch.cyberduck.core.DefaultPathContainerService; import ch.cyberduck.core.DescriptiveUrl; import ch.cyberduck.core.ListProgressListener; @@ -99,7 +100,7 @@ public class DriveAttributesFinderFeature implements AttributesFinder, Attribute return PathAttributes.EMPTY; } } - final PathAttributes attributes = new PathAttributes(); + final PathAttributes attributes = new DefaultPathAttributes(); attributes.setFileId(f.getId()); if(null != f.getTrashed()) { if(f.getTrashed()) { diff --git a/googledrive/src/main/java/ch/cyberduck/core/googledrive/DriveBatchDeleteFeature.java b/googledrive/src/main/java/ch/cyberduck/core/googledrive/DriveBatchDeleteFeature.java index 1b6681aeed..addf80df62 100644 --- a/googledrive/src/main/java/ch/cyberduck/core/googledrive/DriveBatchDeleteFeature.java +++ b/googledrive/src/main/java/ch/cyberduck/core/googledrive/DriveBatchDeleteFeature.java @@ -125,7 +125,7 @@ public class DriveBatchDeleteFeature implements Delete { } @Override - public EnumSet features() { + public EnumSet features(final Path file) { return EnumSet.of(Flags.recursive); } } diff --git a/googledrive/src/main/java/ch/cyberduck/core/googledrive/DriveBatchTrashFeature.java b/googledrive/src/main/java/ch/cyberduck/core/googledrive/DriveBatchTrashFeature.java index d505c93256..63ddf8e9fd 100644 --- a/googledrive/src/main/java/ch/cyberduck/core/googledrive/DriveBatchTrashFeature.java +++ b/googledrive/src/main/java/ch/cyberduck/core/googledrive/DriveBatchTrashFeature.java @@ -123,7 +123,7 @@ public class DriveBatchTrashFeature implements Trash { } @Override - public EnumSet features() { + public EnumSet features(final Path file) { return EnumSet.of(Flags.recursive); } } \ No newline at end of file diff --git a/googledrive/src/main/java/ch/cyberduck/core/googledrive/DriveDeleteFeature.java b/googledrive/src/main/java/ch/cyberduck/core/googledrive/DriveDeleteFeature.java index 7e4024c599..c7c55d8f71 100644 --- a/googledrive/src/main/java/ch/cyberduck/core/googledrive/DriveDeleteFeature.java +++ b/googledrive/src/main/java/ch/cyberduck/core/googledrive/DriveDeleteFeature.java @@ -73,7 +73,7 @@ public class DriveDeleteFeature implements Delete { } @Override - public EnumSet features() { + public EnumSet features(final Path file) { return EnumSet.of(Flags.recursive); } } diff --git a/googledrive/src/main/java/ch/cyberduck/core/googledrive/DriveDirectoryFeature.java b/googledrive/src/main/java/ch/cyberduck/core/googledrive/DriveDirectoryFeature.java index 084ca9903d..fa5106a272 100644 --- a/googledrive/src/main/java/ch/cyberduck/core/googledrive/DriveDirectoryFeature.java +++ b/googledrive/src/main/java/ch/cyberduck/core/googledrive/DriveDirectoryFeature.java @@ -15,15 +15,16 @@ package ch.cyberduck.core.googledrive; * GNU General Public License for more details. */ +import ch.cyberduck.core.DefaultPathAttributes; import ch.cyberduck.core.Path; import ch.cyberduck.core.PathAttributes; import ch.cyberduck.core.SimplePathPredicate; import ch.cyberduck.core.UUIDRandomStringService; -import ch.cyberduck.core.VersionId; import ch.cyberduck.core.exception.BackgroundException; import ch.cyberduck.core.exception.ConflictException; import ch.cyberduck.core.exception.NotfoundException; import ch.cyberduck.core.features.Directory; +import ch.cyberduck.core.features.Write; import ch.cyberduck.core.preferences.HostPreferencesFactory; import ch.cyberduck.core.transfer.TransferStatus; @@ -34,7 +35,7 @@ import com.google.api.services.drive.Drive; import com.google.api.services.drive.model.File; import com.google.api.services.drive.model.TeamDrive; -public class DriveDirectoryFeature implements Directory { +public class DriveDirectoryFeature implements Directory { private final DriveSession session; private final DriveFileIdProvider fileid; @@ -45,13 +46,13 @@ public class DriveDirectoryFeature implements Directory { } @Override - public Path mkdir(final Path folder, final TransferStatus status) throws BackgroundException { + public Path mkdir(final Write writer, final Path folder, final TransferStatus status) throws BackgroundException { try { if(new SimplePathPredicate(DriveHomeFinderService.SHARED_DRIVES_NAME).test(folder.getParent())) { final TeamDrive execute = session.getClient().teamdrives().create( new UUIDRandomStringService().random(), new TeamDrive().setName(folder.getName()) ).execute(); - return folder.withAttributes(new PathAttributes(folder.attributes()).setFileId(execute.getId())); + return folder.withAttributes(new DefaultPathAttributes(folder.attributes()).setFileId(execute.getId())); } else { try { diff --git a/googledrive/src/main/java/ch/cyberduck/core/googledrive/DriveHomeFinderService.java b/googledrive/src/main/java/ch/cyberduck/core/googledrive/DriveHomeFinderService.java index e8e84ba202..835da2d9d8 100644 --- a/googledrive/src/main/java/ch/cyberduck/core/googledrive/DriveHomeFinderService.java +++ b/googledrive/src/main/java/ch/cyberduck/core/googledrive/DriveHomeFinderService.java @@ -15,6 +15,7 @@ package ch.cyberduck.core.googledrive; * GNU General Public License for more details. */ +import ch.cyberduck.core.DefaultPathAttributes; import ch.cyberduck.core.LocaleFactory; import ch.cyberduck.core.Path; import ch.cyberduck.core.PathAttributes; @@ -30,7 +31,7 @@ public class DriveHomeFinderService extends AbstractHomeFeature { public static final Path MYDRIVE_FOLDER = new Path(PathNormalizer.normalize(LocaleFactory.localizedString("My Drive", "Google Drive")), - EnumSet.of(Path.Type.directory, Path.Type.placeholder, Path.Type.volume), new PathAttributes().setFileId(ROOT_FOLDER_ID)); + EnumSet.of(Path.Type.directory, Path.Type.placeholder, Path.Type.volume), new DefaultPathAttributes().setFileId(ROOT_FOLDER_ID)); public static final Path SHARED_FOLDER_NAME = new Path(PathNormalizer.normalize(LocaleFactory.localizedString("Shared with me", "Google Drive")), diff --git a/googledrive/src/main/java/ch/cyberduck/core/googledrive/DriveMetadataFeature.java b/googledrive/src/main/java/ch/cyberduck/core/googledrive/DriveMetadataFeature.java index 5ba6b87190..a0710b4035 100644 --- a/googledrive/src/main/java/ch/cyberduck/core/googledrive/DriveMetadataFeature.java +++ b/googledrive/src/main/java/ch/cyberduck/core/googledrive/DriveMetadataFeature.java @@ -41,7 +41,7 @@ public class DriveMetadataFeature implements Metadata { } @Override - public Map getDefault() { + public Map getDefault(final Path file) { return Collections.emptyMap(); } diff --git a/googledrive/src/main/java/ch/cyberduck/core/googledrive/DriveProtocol.java b/googledrive/src/main/java/ch/cyberduck/core/googledrive/DriveProtocol.java index b756770b82..615d4586ee 100644 --- a/googledrive/src/main/java/ch/cyberduck/core/googledrive/DriveProtocol.java +++ b/googledrive/src/main/java/ch/cyberduck/core/googledrive/DriveProtocol.java @@ -19,6 +19,9 @@ import ch.cyberduck.core.AbstractProtocol; import ch.cyberduck.core.LocaleFactory; 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 com.google.auto.service.AutoService; @@ -85,4 +88,14 @@ public class DriveProtocol extends AbstractProtocol { public VersioningMode getVersioningMode() { return VersioningMode.storage; } + + + @Override + @SuppressWarnings("unchecked") + public T getFeature(final Class type) { + if(type == ComparisonService.class) { + return (T) new DefaultComparisonService(new ChecksumComparisonService(), ComparisonService.disabled); + } + return super.getFeature(type); + } } diff --git a/googledrive/src/main/java/ch/cyberduck/core/googledrive/DriveSearchFeature.java b/googledrive/src/main/java/ch/cyberduck/core/googledrive/DriveSearchFeature.java index e7f859c71b..65008472dc 100644 --- a/googledrive/src/main/java/ch/cyberduck/core/googledrive/DriveSearchFeature.java +++ b/googledrive/src/main/java/ch/cyberduck/core/googledrive/DriveSearchFeature.java @@ -46,7 +46,7 @@ public class DriveSearchFeature implements Search { } @Override - public EnumSet features() { + public EnumSet features(final Path workdir) { return EnumSet.of(Flags.recursive); } diff --git a/googledrive/src/main/java/ch/cyberduck/core/googledrive/DriveSession.java b/googledrive/src/main/java/ch/cyberduck/core/googledrive/DriveSession.java index 7969ae2131..192a61b3f0 100644 --- a/googledrive/src/main/java/ch/cyberduck/core/googledrive/DriveSession.java +++ b/googledrive/src/main/java/ch/cyberduck/core/googledrive/DriveSession.java @@ -26,17 +26,14 @@ import ch.cyberduck.core.UrlProvider; import ch.cyberduck.core.UseragentProvider; import ch.cyberduck.core.exception.BackgroundException; import ch.cyberduck.core.exception.ConnectionCanceledException; -import ch.cyberduck.core.exception.HostParserException; import ch.cyberduck.core.features.*; import ch.cyberduck.core.http.CustomServiceUnavailableRetryStrategy; import ch.cyberduck.core.http.DefaultHttpRateLimiter; -import ch.cyberduck.core.http.ExecutionCountServiceUnavailableRetryStrategy; import ch.cyberduck.core.http.HttpSession; import ch.cyberduck.core.http.RateLimitingHttpRequestInterceptor; import ch.cyberduck.core.http.UserAgentHttpRequestInitializer; import ch.cyberduck.core.oauth.OAuth2ErrorResponseInterceptor; import ch.cyberduck.core.oauth.OAuth2RequestInterceptor; -import ch.cyberduck.core.preferences.HostPreferencesFactory; import ch.cyberduck.core.proxy.ProxyFinder; import ch.cyberduck.core.ssl.X509KeyManager; import ch.cyberduck.core.ssl.X509TrustManager; @@ -67,16 +64,16 @@ public class DriveSession extends HttpSession { } @Override - protected Drive connect(final ProxyFinder proxy, final HostKeyCallback callback, final LoginCallback prompt, final CancelCallback cancel) throws HostParserException, ConnectionCanceledException { + protected Drive connect(final ProxyFinder proxy, final HostKeyCallback callback, final LoginCallback prompt, final CancelCallback cancel) throws ConnectionCanceledException { final HttpClientBuilder configuration = builder.build(proxy, this, prompt); authorizationService = new OAuth2RequestInterceptor(configuration.build(), host, prompt) .withRedirectUri(host.getProtocol().getOAuthRedirectUrl()); configuration.addInterceptorLast(authorizationService); configuration.setServiceUnavailableRetryStrategy(new CustomServiceUnavailableRetryStrategy(host, - new ExecutionCountServiceUnavailableRetryStrategy(new OAuth2ErrorResponseInterceptor(host, authorizationService)))); - if(HostPreferencesFactory.get(host).getBoolean("googledrive.limit.requests.enable")) { + new OAuth2ErrorResponseInterceptor(host, authorizationService))); + if(preferences.getBoolean("googledrive.limit.requests.enable")) { configuration.addInterceptorLast(new RateLimitingHttpRequestInterceptor(new DefaultHttpRateLimiter( - HostPreferencesFactory.get(host).getInteger("googledrive.limit.requests.second") + preferences.getInteger("googledrive.limit.requests.second") ))); } transport = new ApacheHttpTransport(configuration.build()); @@ -88,7 +85,8 @@ public class DriveSession extends HttpSession { @Override public void login(final LoginCallback prompt, final CancelCallback cancel) throws BackgroundException { - final Credentials credentials = authorizationService.validate(); + final Credentials credentials = host.getCredentials(); + credentials.setOauth(authorizationService.validate(credentials.getOauth())); final About about; try { about = client.about().get().setFields("user").execute(); @@ -101,15 +99,16 @@ public class DriveSession extends HttpSession { } @Override - protected void logout() throws BackgroundException { + public void disconnect() throws BackgroundException { try { + fileid.clear(); transport.shutdown(); } catch(IOException e) { throw new DefaultIOExceptionMappingService().map(e); } finally { - fileid.clear(); + super.disconnect(); } } @@ -130,7 +129,7 @@ public class DriveSession extends HttpSession { return (T) new DriveWriteFeature(this, fileid); } if(type == Upload.class) { - return (T) new DriveUploadFeature(this, fileid); + return (T) new DriveUploadFeature(this); } if(type == Directory.class) { return (T) new DriveDirectoryFeature(this, fileid); diff --git a/googledrive/src/main/java/ch/cyberduck/core/googledrive/DriveTeamDrivesListService.java b/googledrive/src/main/java/ch/cyberduck/core/googledrive/DriveTeamDrivesListService.java index 5662a72e28..582d24c8b5 100644 --- a/googledrive/src/main/java/ch/cyberduck/core/googledrive/DriveTeamDrivesListService.java +++ b/googledrive/src/main/java/ch/cyberduck/core/googledrive/DriveTeamDrivesListService.java @@ -16,6 +16,7 @@ package ch.cyberduck.core.googledrive; */ import ch.cyberduck.core.AttributedList; +import ch.cyberduck.core.DefaultPathAttributes; import ch.cyberduck.core.ListProgressListener; import ch.cyberduck.core.ListService; import ch.cyberduck.core.Path; @@ -60,7 +61,7 @@ public class DriveTeamDrivesListService implements ListService { .execute(); for(TeamDrive f : list.getTeamDrives()) { final Path child = new Path(directory, f.getName(), EnumSet.of(Path.Type.directory, Path.Type.volume), - new PathAttributes().setFileId(f.getId())); + new DefaultPathAttributes().setFileId(f.getId())); children.add(child); } listener.chunk(directory, children); diff --git a/googledrive/src/main/java/ch/cyberduck/core/googledrive/DriveThresholdDeleteFeature.java b/googledrive/src/main/java/ch/cyberduck/core/googledrive/DriveThresholdDeleteFeature.java index e0256bc5f3..880f882800 100644 --- a/googledrive/src/main/java/ch/cyberduck/core/googledrive/DriveThresholdDeleteFeature.java +++ b/googledrive/src/main/java/ch/cyberduck/core/googledrive/DriveThresholdDeleteFeature.java @@ -49,7 +49,7 @@ public class DriveThresholdDeleteFeature implements Delete { } @Override - public EnumSet features() { + public EnumSet features(final Path file) { return EnumSet.of(Flags.recursive); } diff --git a/googledrive/src/main/java/ch/cyberduck/core/googledrive/DriveThresholdTrashFeature.java b/googledrive/src/main/java/ch/cyberduck/core/googledrive/DriveThresholdTrashFeature.java index b57d825252..9d29c6be3a 100644 --- a/googledrive/src/main/java/ch/cyberduck/core/googledrive/DriveThresholdTrashFeature.java +++ b/googledrive/src/main/java/ch/cyberduck/core/googledrive/DriveThresholdTrashFeature.java @@ -45,7 +45,7 @@ public class DriveThresholdTrashFeature implements Trash { } @Override - public EnumSet features() { + public EnumSet features(final Path file) { return EnumSet.of(Flags.recursive); } } diff --git a/googledrive/src/main/java/ch/cyberduck/core/googledrive/DriveTouchFeature.java b/googledrive/src/main/java/ch/cyberduck/core/googledrive/DriveTouchFeature.java index 0b2993a863..c2f3ef49bf 100644 --- a/googledrive/src/main/java/ch/cyberduck/core/googledrive/DriveTouchFeature.java +++ b/googledrive/src/main/java/ch/cyberduck/core/googledrive/DriveTouchFeature.java @@ -18,12 +18,12 @@ package ch.cyberduck.core.googledrive; import ch.cyberduck.core.LocaleFactory; import ch.cyberduck.core.Path; import ch.cyberduck.core.SimplePathPredicate; -import ch.cyberduck.core.VersionId; import ch.cyberduck.core.exception.AccessDeniedException; import ch.cyberduck.core.exception.BackgroundException; import ch.cyberduck.core.exception.ConflictException; import ch.cyberduck.core.exception.NotfoundException; import ch.cyberduck.core.features.Touch; +import ch.cyberduck.core.features.Write; import ch.cyberduck.core.preferences.HostPreferencesFactory; import ch.cyberduck.core.transfer.TransferStatus; @@ -34,7 +34,7 @@ import java.util.Collections; import com.google.api.services.drive.Drive; import com.google.api.services.drive.model.File; -public class DriveTouchFeature implements Touch { +public class DriveTouchFeature implements Touch { private final DriveSession session; private final DriveFileIdProvider fileid; @@ -45,7 +45,7 @@ public class DriveTouchFeature implements Touch { } @Override - public Path touch(final Path file, final TransferStatus status) throws BackgroundException { + public Path touch(final Write writer, final Path file, final TransferStatus status) throws BackgroundException { try { try { if(!new DriveAttributesFinderFeature(session, fileid).find(file).isTrashed()) { diff --git a/googledrive/src/main/java/ch/cyberduck/core/googledrive/DriveTrashFeature.java b/googledrive/src/main/java/ch/cyberduck/core/googledrive/DriveTrashFeature.java index 776e36479c..ebd49af9f5 100644 --- a/googledrive/src/main/java/ch/cyberduck/core/googledrive/DriveTrashFeature.java +++ b/googledrive/src/main/java/ch/cyberduck/core/googledrive/DriveTrashFeature.java @@ -91,7 +91,7 @@ public class DriveTrashFeature implements Trash { } @Override - public EnumSet features() { + public EnumSet features(final Path file) { return EnumSet.of(Flags.recursive); } } diff --git a/googledrive/src/main/java/ch/cyberduck/core/googledrive/DriveUploadFeature.java b/googledrive/src/main/java/ch/cyberduck/core/googledrive/DriveUploadFeature.java index 7b26f8d2de..135ab58472 100644 --- a/googledrive/src/main/java/ch/cyberduck/core/googledrive/DriveUploadFeature.java +++ b/googledrive/src/main/java/ch/cyberduck/core/googledrive/DriveUploadFeature.java @@ -23,7 +23,6 @@ import com.google.api.services.drive.model.File; public class DriveUploadFeature extends HttpUploadFeature { - public DriveUploadFeature(final DriveSession session, final DriveFileIdProvider fileid) { - super(new DriveWriteFeature(session, fileid)); + public DriveUploadFeature(final DriveSession session) { } } diff --git a/googledrive/src/main/java/ch/cyberduck/core/googledrive/DriveVersioningFeature.java b/googledrive/src/main/java/ch/cyberduck/core/googledrive/DriveVersioningFeature.java index 174d77caba..3703d14db9 100644 --- a/googledrive/src/main/java/ch/cyberduck/core/googledrive/DriveVersioningFeature.java +++ b/googledrive/src/main/java/ch/cyberduck/core/googledrive/DriveVersioningFeature.java @@ -16,6 +16,7 @@ package ch.cyberduck.core.googledrive; */ import ch.cyberduck.core.AttributedList; +import ch.cyberduck.core.DefaultPathAttributes; import ch.cyberduck.core.ListProgressListener; import ch.cyberduck.core.PasswordCallback; import ch.cyberduck.core.Path; @@ -107,7 +108,7 @@ public class DriveVersioningFeature implements Versioning { } private PathAttributes toAttributes(final Revision f) { - final PathAttributes attributes = new PathAttributes(); + final PathAttributes attributes = new DefaultPathAttributes(); attributes.setSize(f.getSize()); if(f.getModifiedTime() != null) { attributes.setModificationDate(f.getModifiedTime().getValue()); diff --git a/googledrive/src/test/java/ch/cyberduck/core/cryptomator/CopyWorkerTest.java b/googledrive/src/test/java/ch/cyberduck/core/cryptomator/CopyWorkerTest.java index ff4b37ad71..91df747da3 100644 --- a/googledrive/src/test/java/ch/cyberduck/core/cryptomator/CopyWorkerTest.java +++ b/googledrive/src/test/java/ch/cyberduck/core/cryptomator/CopyWorkerTest.java @@ -28,8 +28,8 @@ 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.googledrive.AbstractDriveTest; -import ch.cyberduck.core.googledrive.DriveDeleteFeature; import ch.cyberduck.core.googledrive.DriveDirectoryFeature; import ch.cyberduck.core.googledrive.DriveFileIdProvider; import ch.cyberduck.core.googledrive.DriveFindFeature; @@ -64,6 +64,8 @@ import java.util.Arrays; import java.util.Collections; import java.util.EnumSet; +import com.google.api.services.drive.model.File; + import static org.junit.Assert.*; import static org.junit.Assume.assumeTrue; @@ -84,7 +86,7 @@ public class CopyWorkerTest extends AbstractDriveTest { final byte[] content = RandomUtils.nextBytes(40500); final TransferStatus status = new TransferStatus(); final DriveFileIdProvider fileid = new DriveFileIdProvider(session); - new CryptoBulkFeature<>(session, new DisabledBulkFeature(), new DriveDeleteFeature(session, fileid), cryptomator).pre(Transfer.Type.upload, Collections.singletonMap(new TransferItem(source), status), new DisabledConnectionCallback()); + new CryptoBulkFeature<>(session, new DisabledBulkFeature(), 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 DriveWriteFeature(session, fileid), 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()); @@ -109,9 +111,11 @@ public class CopyWorkerTest extends AbstractDriveTest { final DefaultVaultRegistry registry = new DefaultVaultRegistry(new DisabledPasswordCallback(), cryptomator); session.withRegistry(registry); final DriveFileIdProvider fileid = new DriveFileIdProvider(session); - new CryptoTouchFeature<>(session, new DefaultTouchFeature<>(new DriveWriteFeature(session, fileid)), new DriveWriteFeature(session, fileid), cryptomator).touch(source, new TransferStatus()); + new CryptoTouchFeature<>(session, new DefaultTouchFeature(session), cryptomator).touch( + cryptomator.getFeature(session, Write.class, new DriveWriteFeature(session, fileid)), source, new TransferStatus()); assertTrue(cryptomator.getFeature(session, Find.class, new DefaultFindFeature(session)).find(source)); - cryptomator.getFeature(session, Directory.class, new DriveDirectoryFeature(session, fileid)).mkdir(targetFolder, new TransferStatus()); + cryptomator.getFeature(session, Directory.class, new DriveDirectoryFeature(session, fileid)).mkdir( + cryptomator.getFeature(session, Write.class, new DriveWriteFeature(session, fileid)), 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); @@ -134,9 +138,11 @@ public class CopyWorkerTest extends AbstractDriveTest { final DefaultVaultRegistry registry = new DefaultVaultRegistry(new DisabledPasswordCallback(), cryptomator); session.withRegistry(registry); final DriveFileIdProvider fileid = new DriveFileIdProvider(session); - new CryptoTouchFeature<>(session, new DefaultTouchFeature<>(new DriveWriteFeature(session, fileid)), new DriveWriteFeature(session, fileid), cryptomator).touch(source, new TransferStatus()); + new CryptoTouchFeature<>(session, new DefaultTouchFeature(session), cryptomator).touch( + cryptomator.getFeature(session, Write.class, new DriveWriteFeature(session, fileid)), source, new TransferStatus()); assertTrue(cryptomator.getFeature(session, Find.class, new DefaultFindFeature(session)).find(source)); - cryptomator.getFeature(session, Directory.class, new DriveDirectoryFeature(session, fileid)).mkdir(targetFolder, new TransferStatus()); + cryptomator.getFeature(session, Directory.class, new DriveDirectoryFeature(session, fileid)).mkdir( + cryptomator.getFeature(session, Write.class, new DriveWriteFeature(session, fileid)), 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); @@ -157,9 +163,11 @@ public class CopyWorkerTest extends AbstractDriveTest { final DefaultVaultRegistry registry = new DefaultVaultRegistry(new DisabledPasswordCallback(), cryptomator); session.withRegistry(registry); final DriveFileIdProvider fileid = new DriveFileIdProvider(session); - cryptomator.getFeature(session, Directory.class, new DriveDirectoryFeature(session, fileid)).mkdir(folder, new TransferStatus()); + cryptomator.getFeature(session, Directory.class, new DriveDirectoryFeature(session, fileid)).mkdir( + cryptomator.getFeature(session, Write.class, new DriveWriteFeature(session, fileid)), folder, new TransferStatus()); assertTrue(cryptomator.getFeature(session, Find.class, new DefaultFindFeature(session)).find(folder)); - new CryptoTouchFeature<>(session, new DefaultTouchFeature<>(new DriveWriteFeature(session, fileid)), new DriveWriteFeature(session, fileid), cryptomator).touch(file, new TransferStatus()); + new CryptoTouchFeature<>(session, new DefaultTouchFeature(session), cryptomator).touch( + cryptomator.getFeature(session, Write.class, new DriveWriteFeature(session, fileid)), 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)); @@ -183,7 +191,7 @@ public class CopyWorkerTest extends AbstractDriveTest { 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 DriveFileIdProvider fileid = new DriveFileIdProvider(session); - new DriveTouchFeature(session, fileid).touch(cleartextFile, new TransferStatus()); + new DriveTouchFeature(session, fileid).touch(new DriveWriteFeature(session, fileid), cleartextFile, new TransferStatus()); assertTrue(new DriveFindFeature(session, fileid).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)); @@ -191,7 +199,8 @@ public class CopyWorkerTest extends AbstractDriveTest { cryptomator.create(session, new VaultCredentials("test"), vaultVersion); final DefaultVaultRegistry registry = new DefaultVaultRegistry(new DisabledPasswordCallback(), cryptomator); session.withRegistry(registry); - cryptomator.getFeature(session, Directory.class, new DriveDirectoryFeature(session, fileid)).mkdir(encryptedFolder, new TransferStatus()); + cryptomator.getFeature(session, Directory.class, new DriveDirectoryFeature(session, fileid)).mkdir( + cryptomator.getFeature(session, Write.class, new DriveWriteFeature(session, fileid)), 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()); @@ -209,8 +218,8 @@ public class CopyWorkerTest extends AbstractDriveTest { 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)); final DriveFileIdProvider fileid = new DriveFileIdProvider(session); - new DriveDirectoryFeature(session, fileid).mkdir(cleartextFolder, new TransferStatus()); - new DriveTouchFeature(session, fileid).touch(cleartextFile, new TransferStatus()); + new DriveDirectoryFeature(session, fileid).mkdir(new DriveWriteFeature(session, fileid), cleartextFolder, new TransferStatus()); + new DriveTouchFeature(session, fileid).touch(new DriveWriteFeature(session, fileid), cleartextFile, new TransferStatus()); assertTrue(new DriveFindFeature(session, fileid).find(cleartextFolder)); assertTrue(new DriveFindFeature(session, fileid).find(cleartextFile)); final CryptoVault cryptomator = new CryptoVault(vault); @@ -236,16 +245,17 @@ public class CopyWorkerTest extends AbstractDriveTest { 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)); final DriveFileIdProvider fileid = new DriveFileIdProvider(session); - new DriveDirectoryFeature(session, fileid).mkdir(clearFolder, new TransferStatus()); + new DriveDirectoryFeature(session, fileid).mkdir(new DriveWriteFeature(session, fileid), 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 DriveDirectoryFeature(session, fileid)).mkdir(encryptedFolder, new TransferStatus()); + cryptomator.getFeature(session, Directory.class, new DriveDirectoryFeature(session, fileid)).mkdir( + cryptomator.getFeature(session, Write.class, new DriveWriteFeature(session, fileid)), encryptedFolder, new TransferStatus()); assertTrue(cryptomator.getFeature(session, Find.class, new DefaultFindFeature(session)).find(encryptedFolder)); - new CryptoTouchFeature<>(session, new DefaultTouchFeature<>(new DriveWriteFeature(session, fileid)), new DriveWriteFeature(session, fileid), cryptomator).touch(encryptedFile, new TransferStatus()); + new CryptoTouchFeature<>(session, new DefaultTouchFeature(session), cryptomator).touch(new DriveWriteFeature(session, fileid), 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)); @@ -268,9 +278,11 @@ public class CopyWorkerTest extends AbstractDriveTest { final DefaultVaultRegistry registry = new DefaultVaultRegistry(new DisabledPasswordCallback(), cryptomator); session.withRegistry(registry); final DriveFileIdProvider fileid = new DriveFileIdProvider(session); - cryptomator.getFeature(session, Directory.class, new DriveDirectoryFeature(session, fileid)).mkdir(encryptedFolder, new TransferStatus()); + cryptomator.getFeature(session, Directory.class, new DriveDirectoryFeature(session, fileid)).mkdir( + cryptomator.getFeature(session, Write.class, new DriveWriteFeature(session, fileid)), encryptedFolder, new TransferStatus()); assertTrue(cryptomator.getFeature(session, Find.class, new DefaultFindFeature(session)).find(encryptedFolder)); - new CryptoTouchFeature<>(session, new DefaultTouchFeature<>(new DriveWriteFeature(session, fileid)), new DriveWriteFeature(session, fileid), cryptomator).touch(encryptedFile, new TransferStatus()); + new CryptoTouchFeature<>(session, new DefaultTouchFeature(session), cryptomator).touch( + cryptomator.getFeature(session, Write.class, new DriveWriteFeature(session, fileid)), 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)); diff --git a/googledrive/src/test/java/ch/cyberduck/core/cryptomator/DriveAttributesFinderFeatureTest.java b/googledrive/src/test/java/ch/cyberduck/core/cryptomator/DriveAttributesFinderFeatureTest.java index 5a6d0ed83c..9b0915f23c 100644 --- a/googledrive/src/test/java/ch/cyberduck/core/cryptomator/DriveAttributesFinderFeatureTest.java +++ b/googledrive/src/test/java/ch/cyberduck/core/cryptomator/DriveAttributesFinderFeatureTest.java @@ -32,6 +32,7 @@ import ch.cyberduck.core.cryptomator.features.CryptoTouchFeature; import ch.cyberduck.core.features.AttributesFinder; import ch.cyberduck.core.features.Delete; import ch.cyberduck.core.features.Directory; +import ch.cyberduck.core.features.Write; import ch.cyberduck.core.googledrive.AbstractDriveTest; import ch.cyberduck.core.googledrive.DriveAttributesFinderFeature; import ch.cyberduck.core.googledrive.DriveDeleteFeature; @@ -55,6 +56,8 @@ import org.junit.runners.Parameterized; import java.util.Arrays; import java.util.EnumSet; +import com.google.api.services.drive.model.File; + import static org.junit.Assert.*; @Category(IntegrationTest.class) @@ -68,14 +71,14 @@ public class DriveAttributesFinderFeatureTest extends AbstractDriveTest { final CryptoVault cryptomator = new CryptoVault(vault); cryptomator.create(session, new VaultCredentials("test"), vaultVersion); session.withRegistry(new DefaultVaultRegistry(new DisabledPasswordCallback(), cryptomator)); - final DriveFileIdProvider idProvider = new DriveFileIdProvider(session); - final Path test = new CryptoTouchFeature<>(session, new DefaultTouchFeature<>(new DriveWriteFeature(session, idProvider)), new DriveWriteFeature(session, idProvider), cryptomator).touch( - new Path(vault, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)), new TransferStatus()); + final DriveFileIdProvider fileid = new DriveFileIdProvider(session); + final Path test = new CryptoTouchFeature<>(session, new DefaultTouchFeature(session), cryptomator).touch( + cryptomator.getFeature(session, Write.class, new DriveWriteFeature(session, fileid)), new Path(vault, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)), new TransferStatus()); test.attributes().setSize(0L); - final PathAttributes attributes = cryptomator.getFeature(session, AttributesFinder.class, new DriveAttributesFinderFeature(session, idProvider)).find(test); + final PathAttributes attributes = cryptomator.getFeature(session, AttributesFinder.class, new DriveAttributesFinderFeature(session, fileid)).find(test); assertNotNull(attributes); assertEquals(0L, attributes.getSize()); - cryptomator.getFeature(session, Delete.class, new DriveDeleteFeature(session, idProvider)).delete(Arrays.asList(test, vault), new DisabledLoginCallback(), new Delete.DisabledCallback()); + cryptomator.getFeature(session, Delete.class, new DriveDeleteFeature(session, fileid)).delete(Arrays.asList(test, vault), new DisabledLoginCallback(), new Delete.DisabledCallback()); } @Test @@ -85,14 +88,14 @@ public class DriveAttributesFinderFeatureTest extends AbstractDriveTest { final CryptoVault cryptomator = new CryptoVault(vault); cryptomator.create(session, new VaultCredentials("test"), vaultVersion); session.withRegistry(new DefaultVaultRegistry(new DisabledPasswordCallback(), cryptomator)); - final DriveFileIdProvider idProvider = new DriveFileIdProvider(session); - final Path test = new CryptoTouchFeature<>(session, new DefaultTouchFeature<>(new DriveWriteFeature(session, idProvider)), new DriveWriteFeature(session, idProvider), cryptomator).touch( - new Path(vault, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)), new TransferStatus()); + final DriveFileIdProvider fileid = new DriveFileIdProvider(session); + final Path test = new CryptoTouchFeature<>(session, new DefaultTouchFeature(session), cryptomator).touch( + cryptomator.getFeature(session, Write.class, new DriveWriteFeature(session, fileid)), new Path(vault, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)), new TransferStatus()); test.attributes().setSize(0L); final PathAttributes attributes = cryptomator.getFeature(session, AttributesFinder.class, new DefaultAttributesFinderFeature(session)).find(test); assertNotNull(attributes); assertEquals(0L, attributes.getSize()); - cryptomator.getFeature(session, Delete.class, new DriveDeleteFeature(session, idProvider)).delete(Arrays.asList(test, vault), new DisabledLoginCallback(), new Delete.DisabledCallback()); + cryptomator.getFeature(session, Delete.class, new DriveDeleteFeature(session, fileid)).delete(Arrays.asList(test, vault), new DisabledLoginCallback(), new Delete.DisabledCallback()); } @Test @@ -102,15 +105,15 @@ public class DriveAttributesFinderFeatureTest extends AbstractDriveTest { final CryptoVault cryptomator = new CryptoVault(vault); cryptomator.create(session, new VaultCredentials("test"), vaultVersion); session.withRegistry(new DefaultVaultRegistry(new DisabledPasswordCallback(), cryptomator)); - final DriveFileIdProvider idProvider = new DriveFileIdProvider(session); - final Path test = cryptomator.getFeature(session, Directory.class, new DriveDirectoryFeature(session, idProvider)).mkdir( - new Path(vault, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory)), new TransferStatus()); + final DriveFileIdProvider fileid = new DriveFileIdProvider(session); + final Path test = cryptomator.getFeature(session, Directory.class, new DriveDirectoryFeature(session, fileid)).mkdir( + cryptomator.getFeature(session, Write.class, new DriveWriteFeature(session, fileid)), new Path(vault, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory)), new TransferStatus()); final String fileId = test.attributes().getFileId(); assertNotNull(fileId); final PathAttributes attributes = cryptomator.getFeature(session, AttributesFinder.class, new DefaultAttributesFinderFeature(session)).find(test); assertEquals(fileId, attributes.getFileId()); assertEquals(fileId, cryptomator.encrypt(session, test, true).attributes().getFileId()); - cryptomator.getFeature(session, Delete.class, new DriveDeleteFeature(session, idProvider)).delete(Arrays.asList(test, vault), new DisabledLoginCallback(), new Delete.DisabledCallback()); + cryptomator.getFeature(session, Delete.class, new DriveDeleteFeature(session, fileid)).delete(Arrays.asList(test, vault), new DisabledLoginCallback(), new Delete.DisabledCallback()); } @Test @@ -120,10 +123,10 @@ public class DriveAttributesFinderFeatureTest extends AbstractDriveTest { final CryptoVault cryptomator = new CryptoVault(vault); cryptomator.create(session, new VaultCredentials("test"), vaultVersion); session.withRegistry(new DefaultVaultRegistry(new DisabledPasswordCallback(), cryptomator)); - final DriveFileIdProvider idProvider = new DriveFileIdProvider(session); - final Path test = new CryptoTouchFeature<>(session, new DefaultTouchFeature<>(new DriveWriteFeature(session, idProvider)), new DriveWriteFeature(session, idProvider), cryptomator).touch( - new Path(vault, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)), new TransferStatus()); - final Path found = new CryptoListService(session, new DriveListService(session, idProvider), cryptomator).list(test.getParent(), new DisabledListProgressListener()).find(new SimplePathPredicate(test)); + final DriveFileIdProvider fileid = new DriveFileIdProvider(session); + final Path test = new CryptoTouchFeature<>(session, new DefaultTouchFeature(session), cryptomator).touch( + cryptomator.getFeature(session, Write.class, new DriveWriteFeature(session, fileid)), new Path(vault, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)), new TransferStatus()); + final Path found = new CryptoListService(session, new DriveListService(session, fileid), cryptomator).list(test.getParent(), new DisabledListProgressListener()).find(new SimplePathPredicate(test)); assertEquals(0L, found.attributes().getSize()); final Cache cache = new PathCache(1); final PathAttributes attributes = new CachingAttributesFinderFeature(session, cache, cryptomator.getFeature(session, AttributesFinder.class, new DefaultAttributesFinderFeature(session))).find(test); @@ -132,7 +135,7 @@ public class DriveAttributesFinderFeatureTest extends AbstractDriveTest { assertTrue(cache.isCached(vault)); assertEquals(test, cache.get(vault).get(0)); assertEquals(0L, cache.get(vault).get(0).attributes().getSize()); - cryptomator.getFeature(session, Delete.class, new DriveDeleteFeature(session, idProvider)).delete(Arrays.asList(test, vault), new DisabledLoginCallback(), new Delete.DisabledCallback()); + cryptomator.getFeature(session, Delete.class, new DriveDeleteFeature(session, fileid)).delete(Arrays.asList(test, vault), new DisabledLoginCallback(), new Delete.DisabledCallback()); } @Test @@ -142,19 +145,19 @@ public class DriveAttributesFinderFeatureTest extends AbstractDriveTest { final CryptoVault cryptomator = new CryptoVault(vault); cryptomator.create(session, new VaultCredentials("test"), vaultVersion); session.withRegistry(new DefaultVaultRegistry(new DisabledPasswordCallback(), cryptomator)); - final DriveFileIdProvider idProvider = new DriveFileIdProvider(session); - final Path test = new CryptoTouchFeature<>(session, new DefaultTouchFeature<>(new DriveWriteFeature(session, idProvider)), new DriveWriteFeature(session, idProvider), cryptomator).touch( - new Path(vault, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)), new TransferStatus()); - final Path found = new CryptoListService(session, new DriveListService(session, idProvider), cryptomator).list(test.getParent(), new DisabledListProgressListener()).find(new SimplePathPredicate(test)); + final DriveFileIdProvider fileid = new DriveFileIdProvider(session); + final Path test = new CryptoTouchFeature<>(session, new DefaultTouchFeature(session), cryptomator).touch( + cryptomator.getFeature(session, Write.class, new DriveWriteFeature(session, fileid)), new Path(vault, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)), new TransferStatus()); + final Path found = new CryptoListService(session, new DriveListService(session, fileid), cryptomator).list(test.getParent(), new DisabledListProgressListener()).find(new SimplePathPredicate(test)); assertEquals(0L, found.attributes().getSize()); final Cache cache = new PathCache(1); - final PathAttributes attributes = new CachingAttributesFinderFeature(session, cache, cryptomator.getFeature(session, AttributesFinder.class, new DriveAttributesFinderFeature(session, idProvider))).find(test); + final PathAttributes attributes = new CachingAttributesFinderFeature(session, cache, cryptomator.getFeature(session, AttributesFinder.class, new DriveAttributesFinderFeature(session, fileid))).find(test); assertNotNull(attributes); assertEquals(0L, attributes.getSize()); assertTrue(cache.isCached(vault)); assertEquals(test, cache.get(vault).get(0)); assertEquals(0L, cache.get(vault).get(0).attributes().getSize()); - cryptomator.getFeature(session, Delete.class, new DriveDeleteFeature(session, idProvider)).delete(Arrays.asList(test, vault), new DisabledLoginCallback(), new Delete.DisabledCallback()); + cryptomator.getFeature(session, Delete.class, new DriveDeleteFeature(session, fileid)).delete(Arrays.asList(test, vault), new DisabledLoginCallback(), new Delete.DisabledCallback()); } @Test @@ -164,22 +167,22 @@ public class DriveAttributesFinderFeatureTest extends AbstractDriveTest { final CryptoVault cryptomator = new CryptoVault(vault); cryptomator.create(session, new VaultCredentials("test"), vaultVersion); session.withRegistry(new DefaultVaultRegistry(new DisabledPasswordCallback(), cryptomator)); - final DriveFileIdProvider idProvider = new DriveFileIdProvider(session); - assertEquals(new CryptoFileIdProvider(session, idProvider, cryptomator).getFileId(vault), - idProvider.getFileId(vault)); - final Path test = cryptomator.getFeature(session, Directory.class, new DriveDirectoryFeature(session, idProvider)).mkdir( - new Path(vault, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory)), new TransferStatus()); + final DriveFileIdProvider fileid = new DriveFileIdProvider(session); + assertEquals(new CryptoFileIdProvider(session, fileid, cryptomator).getFileId(vault), + fileid.getFileId(vault)); + final Path test = cryptomator.getFeature(session, Directory.class, new DriveDirectoryFeature(session, fileid)).mkdir( + cryptomator.getFeature(session, Write.class, new DriveWriteFeature(session, fileid)), new Path(vault, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory)), new TransferStatus()); final String fileId = test.attributes().getFileId(); assertNotNull(fileId); - final Path found = new CryptoListService(session, new DriveListService(session, idProvider), cryptomator).list(test.getParent(), new DisabledListProgressListener()).find(new SimplePathPredicate(test)); + final Path found = new CryptoListService(session, new DriveListService(session, fileid), cryptomator).list(test.getParent(), new DisabledListProgressListener()).find(new SimplePathPredicate(test)); assertNull(found.attributes().getFileId()); - assertEquals(fileId, new CryptoFileIdProvider(session, idProvider, cryptomator).getFileId(found)); + assertEquals(fileId, new CryptoFileIdProvider(session, fileid, cryptomator).getFileId(found)); final Cache cache = new PathCache(1); final AttributedList list = new AttributedList<>(); list.add(test); cache.put(vault, list); final PathAttributes attributes = new CachingAttributesFinderFeature(session, cache, cryptomator.getFeature(session, AttributesFinder.class, new DefaultAttributesFinderFeature(session))).find(test); assertEquals(fileId, attributes.getFileId()); - cryptomator.getFeature(session, Delete.class, new DriveDeleteFeature(session, idProvider)).delete(Arrays.asList(test, vault), new DisabledLoginCallback(), new Delete.DisabledCallback()); + cryptomator.getFeature(session, Delete.class, new DriveDeleteFeature(session, fileid)).delete(Arrays.asList(test, vault), new DisabledLoginCallback(), new Delete.DisabledCallback()); } } diff --git a/googledrive/src/test/java/ch/cyberduck/core/cryptomator/DriveDirectoryFeatureTest.java b/googledrive/src/test/java/ch/cyberduck/core/cryptomator/DriveDirectoryFeatureTest.java index 2aa1b09810..b16f19d348 100644 --- a/googledrive/src/test/java/ch/cyberduck/core/cryptomator/DriveDirectoryFeatureTest.java +++ b/googledrive/src/test/java/ch/cyberduck/core/cryptomator/DriveDirectoryFeatureTest.java @@ -26,6 +26,7 @@ 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.googledrive.AbstractDriveTest; import ch.cyberduck.core.googledrive.DriveAttributesFinderFeature; import ch.cyberduck.core.googledrive.DriveDeleteFeature; @@ -34,6 +35,7 @@ import ch.cyberduck.core.googledrive.DriveFileIdProvider; import ch.cyberduck.core.googledrive.DriveFindFeature; import ch.cyberduck.core.googledrive.DriveHomeFinderService; import ch.cyberduck.core.googledrive.DriveListService; +import ch.cyberduck.core.googledrive.DriveWriteFeature; import ch.cyberduck.core.shared.DefaultAttributesFinderFeature; import ch.cyberduck.core.shared.DefaultFindFeature; import ch.cyberduck.core.transfer.TransferStatus; @@ -67,7 +69,7 @@ public class DriveDirectoryFeatureTest extends AbstractDriveTest { session.withRegistry(new DefaultVaultRegistry(new DisabledPasswordCallback(), cryptomator)); final DriveFileIdProvider fileid = new DriveFileIdProvider(session); final Path test = cryptomator.getFeature(session, Directory.class, new DriveDirectoryFeature(session, fileid)).mkdir( - new Path(vault, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory)), new TransferStatus()); + cryptomator.getFeature(session, Write.class, new DriveWriteFeature(session, fileid)), new Path(vault, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory)), new TransferStatus()); final String versionId = test.attributes().getFileId(); assertNotNull(versionId); // Assert both filename and file id matches @@ -92,7 +94,7 @@ public class DriveDirectoryFeatureTest extends AbstractDriveTest { session.withRegistry(new DefaultVaultRegistry(new DisabledPasswordCallback(), cryptomator)); final DriveFileIdProvider fileid = new DriveFileIdProvider(session); final Path test = cryptomator.getFeature(session, Directory.class, new DriveDirectoryFeature(session, fileid)).mkdir( - new Path(vault, new AlphanumericRandomStringService(130).random(), EnumSet.of(Path.Type.directory)), new TransferStatus()); + cryptomator.getFeature(session, Write.class, new DriveWriteFeature(session, fileid)), new Path(vault, new AlphanumericRandomStringService(130).random(), EnumSet.of(Path.Type.directory)), new TransferStatus()); final String versionId = test.attributes().getFileId(); assertNotNull(versionId); assertTrue(cryptomator.getFeature(session, Find.class, new DefaultFindFeature(session)).find(test)); diff --git a/googledrive/src/test/java/ch/cyberduck/core/cryptomator/DriveListServiceTest.java b/googledrive/src/test/java/ch/cyberduck/core/cryptomator/DriveListServiceTest.java index 9d06da1d57..8737f0f251 100644 --- a/googledrive/src/test/java/ch/cyberduck/core/cryptomator/DriveListServiceTest.java +++ b/googledrive/src/test/java/ch/cyberduck/core/cryptomator/DriveListServiceTest.java @@ -26,6 +26,7 @@ import ch.cyberduck.core.cryptomator.features.CryptoListService; import ch.cyberduck.core.cryptomator.features.CryptoTouchFeature; import ch.cyberduck.core.features.Delete; import ch.cyberduck.core.features.Directory; +import ch.cyberduck.core.features.Write; import ch.cyberduck.core.googledrive.AbstractDriveTest; import ch.cyberduck.core.googledrive.DriveDefaultListService; import ch.cyberduck.core.googledrive.DriveDeleteFeature; @@ -48,6 +49,8 @@ import java.util.Arrays; import java.util.Collections; import java.util.EnumSet; +import com.google.api.services.drive.model.File; + import static org.junit.Assert.*; @Category(IntegrationTest.class) @@ -63,13 +66,13 @@ public class DriveListServiceTest extends AbstractDriveTest { session.withRegistry(new DefaultVaultRegistry(new DisabledPasswordCallback(), cryptomator)); final DriveFileIdProvider fileid = new DriveFileIdProvider(session); assertTrue(new CryptoListService(session, new DriveDefaultListService(session, fileid), cryptomator).list(vault, new DisabledListProgressListener()).isEmpty()); - final Path testFile = new CryptoTouchFeature<>(session, new DefaultTouchFeature<>(new DriveWriteFeature(session, fileid)), new DriveWriteFeature(session, fileid), cryptomator).touch( - new Path(vault, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)), new TransferStatus()); + final Path testFile = new CryptoTouchFeature<>(session, new DefaultTouchFeature(session), cryptomator).touch( + cryptomator.getFeature(session, Write.class, new DriveWriteFeature(session, fileid)), new Path(vault, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)), new TransferStatus()); assertNotNull(testFile.attributes().getFileId()); assertEquals(testFile, new CryptoListService(session, new DriveDefaultListService(session, fileid), cryptomator).list(vault, new DisabledListProgressListener()).get(0)); cryptomator.getFeature(session, Delete.class, new DriveDeleteFeature(session, fileid)).delete(Collections.singletonList(testFile), new DisabledLoginCallback(), new Delete.DisabledCallback()); final Path testDir = cryptomator.getFeature(session, Directory.class, new DriveDirectoryFeature(session, fileid)).mkdir( - new Path(vault, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory)), new TransferStatus()); + cryptomator.getFeature(session, Write.class, new DriveWriteFeature(session, fileid)), new Path(vault, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory)), new TransferStatus()); assertTrue(new CryptoListService(session, new DriveDefaultListService(session, fileid), cryptomator).list(testDir, new DisabledListProgressListener()).isEmpty()); final AttributedList list = new CryptoListService(session, new DriveDefaultListService(session, fileid), cryptomator).list(vault, new DisabledListProgressListener()); assertNotNull(list.find(new SimplePathPredicate(testDir))); diff --git a/googledrive/src/test/java/ch/cyberduck/core/cryptomator/DriveMoveFeatureTest.java b/googledrive/src/test/java/ch/cyberduck/core/cryptomator/DriveMoveFeatureTest.java index c63f975fca..9ee2e402f3 100644 --- a/googledrive/src/test/java/ch/cyberduck/core/cryptomator/DriveMoveFeatureTest.java +++ b/googledrive/src/test/java/ch/cyberduck/core/cryptomator/DriveMoveFeatureTest.java @@ -26,6 +26,7 @@ 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.googledrive.AbstractDriveTest; import ch.cyberduck.core.googledrive.DriveAttributesFinderFeature; import ch.cyberduck.core.googledrive.DriveDeleteFeature; @@ -49,6 +50,8 @@ import org.junit.runners.Parameterized; import java.util.Arrays; import java.util.EnumSet; +import com.google.api.services.drive.model.File; + import static org.junit.Assert.*; @Category(IntegrationTest.class) @@ -64,8 +67,9 @@ public class DriveMoveFeatureTest extends AbstractDriveTest { session.withRegistry(new DefaultVaultRegistry(new DisabledPasswordCallback(), cryptomator)); final DriveFileIdProvider fileid = new DriveFileIdProvider(session); final Path folder = cryptomator.getFeature(session, Directory.class, new DriveDirectoryFeature(session, fileid)).mkdir( - new Path(vault, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory)), new TransferStatus()); - final Path file = new CryptoTouchFeature<>(session, new DefaultTouchFeature<>(new DriveWriteFeature(session, fileid)), new DriveWriteFeature(session, fileid), cryptomator).touch(new Path(folder, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)), new TransferStatus()); + cryptomator.getFeature(session, Write.class, new DriveWriteFeature(session, fileid)), new Path(vault, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory)), new TransferStatus()); + final Path file = new CryptoTouchFeature<>(session, new DefaultTouchFeature(session), cryptomator).touch( + cryptomator.getFeature(session, Write.class, new DriveWriteFeature(session, fileid)), 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 DriveMoveFeature(session, fileid)); // rename file diff --git a/googledrive/src/test/java/ch/cyberduck/core/cryptomator/DriveTouchFeatureTest.java b/googledrive/src/test/java/ch/cyberduck/core/cryptomator/DriveTouchFeatureTest.java index c1b62455f9..65276bcdda 100644 --- a/googledrive/src/test/java/ch/cyberduck/core/cryptomator/DriveTouchFeatureTest.java +++ b/googledrive/src/test/java/ch/cyberduck/core/cryptomator/DriveTouchFeatureTest.java @@ -25,6 +25,7 @@ import ch.cyberduck.core.cryptomator.features.CryptoListService; import ch.cyberduck.core.cryptomator.features.CryptoTouchFeature; import ch.cyberduck.core.features.Delete; import ch.cyberduck.core.features.Find; +import ch.cyberduck.core.features.Write; import ch.cyberduck.core.googledrive.AbstractDriveTest; import ch.cyberduck.core.googledrive.DriveDeleteFeature; import ch.cyberduck.core.googledrive.DriveFileIdProvider; @@ -46,6 +47,8 @@ import org.junit.runners.Parameterized; import java.util.Arrays; import java.util.EnumSet; +import com.google.api.services.drive.model.File; + import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertTrue; import static org.junit.Assume.assumeTrue; @@ -61,14 +64,14 @@ public class DriveTouchFeatureTest extends AbstractDriveTest { 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 DriveFileIdProvider idProvider = new DriveFileIdProvider(session); - final Path test = new CryptoTouchFeature<>(session, new DefaultTouchFeature<>(new DriveWriteFeature(session, idProvider)), new DriveWriteFeature(session, idProvider), cryptomator).touch( - new Path(vault, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)), new TransferStatus()); + final DriveFileIdProvider fileid = new DriveFileIdProvider(session); + final Path test = new CryptoTouchFeature<>(session, new DefaultTouchFeature(session), cryptomator).touch( + cryptomator.getFeature(session, Write.class, new DriveWriteFeature(session, fileid)), new Path(vault, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)), new TransferStatus()); assertTrue(cryptomator.getFeature(session, Find.class, new DefaultFindFeature(session)).find(test)); - final Path found = new CryptoListService(session, new DriveListService(session, idProvider), cryptomator).list(test.getParent(), new DisabledListProgressListener()).find(new SimplePathPredicate(test)); + final Path found = new CryptoListService(session, new DriveListService(session, fileid), cryptomator).list(test.getParent(), new DisabledListProgressListener()).find(new SimplePathPredicate(test)); final String fileId = found.attributes().getFileId(); assertNotNull(fileId); - cryptomator.getFeature(session, Delete.class, new DriveDeleteFeature(session, idProvider)).delete(Arrays.asList(test, vault), new DisabledLoginCallback(), new Delete.DisabledCallback()); + cryptomator.getFeature(session, Delete.class, new DriveDeleteFeature(session, fileid)).delete(Arrays.asList(test, vault), new DisabledLoginCallback(), new Delete.DisabledCallback()); } @Test @@ -79,14 +82,14 @@ public class DriveTouchFeatureTest extends AbstractDriveTest { 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 DriveFileIdProvider idProvider = new DriveFileIdProvider(session); - final Path test = new CryptoTouchFeature<>(session, new DefaultTouchFeature<>(new DriveWriteFeature(session, idProvider)), new DriveWriteFeature(session, idProvider), cryptomator).touch( - new Path(vault, new AlphanumericRandomStringService(130).random(), EnumSet.of(Path.Type.file)), new TransferStatus()); + final DriveFileIdProvider fileid = new DriveFileIdProvider(session); + final Path test = new CryptoTouchFeature<>(session, new DefaultTouchFeature(session), cryptomator).touch( + cryptomator.getFeature(session, Write.class, new DriveWriteFeature(session, fileid)), new Path(vault, new AlphanumericRandomStringService(130).random(), EnumSet.of(Path.Type.file)), new TransferStatus()); assertTrue(cryptomator.getFeature(session, Find.class, new DefaultFindFeature(session)).find(test)); - final Path found = new CryptoListService(session, new DriveListService(session, idProvider), cryptomator).list(test.getParent(), new DisabledListProgressListener()).find(new SimplePathPredicate(test)); + final Path found = new CryptoListService(session, new DriveListService(session, fileid), cryptomator).list(test.getParent(), new DisabledListProgressListener()).find(new SimplePathPredicate(test)); final String fileId = found.attributes().getFileId(); assertNotNull(fileId); - cryptomator.getFeature(session, Delete.class, new DriveDeleteFeature(session, idProvider)).delete(Arrays.asList(test, vault), new DisabledLoginCallback(), new Delete.DisabledCallback()); + cryptomator.getFeature(session, Delete.class, new DriveDeleteFeature(session, fileid)).delete(Arrays.asList(test, vault), new DisabledLoginCallback(), new Delete.DisabledCallback()); } @Test @@ -97,13 +100,13 @@ public class DriveTouchFeatureTest extends AbstractDriveTest { 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 DriveFileIdProvider idProvider = new DriveFileIdProvider(session); - final Path test = new CryptoTouchFeature<>(session, new DefaultTouchFeature<>(new DriveWriteFeature(session, idProvider)), new DriveWriteFeature(session, idProvider), cryptomator).touch( - new Path(vault, new AlphanumericRandomStringService(130).random(), EnumSet.of(Path.Type.file)), new TransferStatus()); + final DriveFileIdProvider fileid = new DriveFileIdProvider(session); + final Path test = new CryptoTouchFeature<>(session, new DefaultTouchFeature(session), cryptomator).touch( + cryptomator.getFeature(session, Write.class, new DriveWriteFeature(session, fileid)), new Path(vault, new AlphanumericRandomStringService(130).random(), EnumSet.of(Path.Type.file)), new TransferStatus()); assertTrue(cryptomator.getFeature(session, Find.class, new DefaultFindFeature(session)).find(test)); - final Path found = new CryptoListService(session, new DriveListService(session, idProvider), cryptomator).list(test.getParent(), new DisabledListProgressListener()).find(new SimplePathPredicate(test)); + final Path found = new CryptoListService(session, new DriveListService(session, fileid), cryptomator).list(test.getParent(), new DisabledListProgressListener()).find(new SimplePathPredicate(test)); final String fileId = found.attributes().getFileId(); assertNotNull(fileId); - cryptomator.getFeature(session, Delete.class, new DriveDeleteFeature(session, idProvider)).delete(Arrays.asList(test, vault), new DisabledLoginCallback(), new Delete.DisabledCallback()); + cryptomator.getFeature(session, Delete.class, new DriveDeleteFeature(session, fileid)).delete(Arrays.asList(test, vault), new DisabledLoginCallback(), new Delete.DisabledCallback()); } } diff --git a/googledrive/src/test/java/ch/cyberduck/core/cryptomator/MoveWorkerTest.java b/googledrive/src/test/java/ch/cyberduck/core/cryptomator/MoveWorkerTest.java index b2c4f8682c..959a53c3df 100644 --- a/googledrive/src/test/java/ch/cyberduck/core/cryptomator/MoveWorkerTest.java +++ b/googledrive/src/test/java/ch/cyberduck/core/cryptomator/MoveWorkerTest.java @@ -31,6 +31,7 @@ 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.Write; import ch.cyberduck.core.googledrive.AbstractDriveTest; import ch.cyberduck.core.googledrive.DriveDeleteFeature; import ch.cyberduck.core.googledrive.DriveDirectoryFeature; @@ -68,6 +69,8 @@ import java.util.Collections; import java.util.EnumSet; import java.util.UUID; +import com.google.api.services.drive.model.File; + import static org.junit.Assert.*; import static org.junit.Assume.assumeTrue; @@ -87,7 +90,7 @@ public class MoveWorkerTest extends AbstractDriveTest { final byte[] content = RandomUtils.nextBytes(40500); final TransferStatus status = new TransferStatus(); final DriveFileIdProvider fileid = new DriveFileIdProvider(session); - new CryptoBulkFeature<>(session, new DisabledBulkFeature(), new DriveDeleteFeature(session, fileid), cryptomator).pre(Transfer.Type.upload, Collections.singletonMap(new TransferItem(source), status), new DisabledConnectionCallback()); + new CryptoBulkFeature<>(session, new DisabledBulkFeature(), 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 DriveWriteFeature(session, fileid), cryptomator).write(source, status.setLength(content.length), new DisabledConnectionCallback())); assertTrue(cryptomator.getFeature(session, Find.class, new DefaultFindFeature(session)).find(source)); final MoveWorker worker = new MoveWorker(Collections.singletonMap(source, target), new SessionPool.SingleSessionPool(session), PathCache.empty(), new DisabledProgressListener(), new DisabledLoginCallback()); @@ -109,10 +112,10 @@ public class MoveWorkerTest extends AbstractDriveTest { final Path source = new Path(vault, UUID.randomUUID().toString(), EnumSet.of(Path.Type.file)); session.withRegistry(new DefaultVaultRegistry(new DisabledPasswordCallback(), cryptomator)); final DriveFileIdProvider fileid = new DriveFileIdProvider(session); - new CryptoTouchFeature<>(session, new DefaultTouchFeature<>(new DriveWriteFeature(session, fileid)), new DriveWriteFeature(session, fileid), cryptomator).touch(source, new TransferStatus()); + new CryptoTouchFeature<>(session, new DefaultTouchFeature(session), cryptomator).touch(new DriveWriteFeature(session, fileid), source, new TransferStatus()); assertTrue(cryptomator.getFeature(session, Find.class, new DefaultFindFeature(session)).find(source)); final Path targetFolder = cryptomator.getFeature(session, Directory.class, new DriveDirectoryFeature(session, fileid)).mkdir( - new Path(vault, UUID.randomUUID().toString(), EnumSet.of(Path.Type.directory)), new TransferStatus()); + cryptomator.getFeature(session, Write.class, new DriveWriteFeature(session, fileid)), new Path(vault, UUID.randomUUID().toString(), EnumSet.of(Path.Type.directory)), new TransferStatus()); final Path target = new Path(targetFolder, UUID.randomUUID().toString(), EnumSet.of(Path.Type.file)); assertTrue(cryptomator.getFeature(session, Find.class, new DefaultFindFeature(session)).find(targetFolder)); final MoveWorker worker = new MoveWorker(Collections.singletonMap(source, target), new SessionPool.SingleSessionPool(session), PathCache.empty(), new DisabledProgressListener(), new DisabledLoginCallback()); @@ -132,10 +135,11 @@ public class MoveWorkerTest extends AbstractDriveTest { final Path source = new Path(vault, new AlphanumericRandomStringService(130).random(), EnumSet.of(Path.Type.file)); session.withRegistry(new DefaultVaultRegistry(new DisabledPasswordCallback(), cryptomator)); final DriveFileIdProvider fileid = new DriveFileIdProvider(session); - new CryptoTouchFeature<>(session, new DefaultTouchFeature<>(new DriveWriteFeature(session, fileid)), new DriveWriteFeature(session, fileid), cryptomator).touch(source, new TransferStatus()); + new CryptoTouchFeature<>(session, new DefaultTouchFeature(session), cryptomator).touch( + cryptomator.getFeature(session, Write.class, new DriveWriteFeature(session, fileid)), source, new TransferStatus()); assertTrue(cryptomator.getFeature(session, Find.class, new DefaultFindFeature(session)).find(source)); final Path targetFolder = cryptomator.getFeature(session, Directory.class, new DriveDirectoryFeature(session, fileid)).mkdir( - new Path(vault, new AlphanumericRandomStringService(130).random(), EnumSet.of(Path.Type.directory)), new TransferStatus()); + cryptomator.getFeature(session, Write.class, new DriveWriteFeature(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(targetFolder)); final Path target = new Path(targetFolder, new AlphanumericRandomStringService(130).random(), EnumSet.of(Path.Type.file)); final MoveWorker worker = new MoveWorker(Collections.singletonMap(source, target), new SessionPool.SingleSessionPool(session), PathCache.empty(), new DisabledProgressListener(), new DisabledLoginCallback()); @@ -154,11 +158,11 @@ public class MoveWorkerTest extends AbstractDriveTest { session.withRegistry(new DefaultVaultRegistry(new DisabledPasswordCallback(), cryptomator)); final DriveFileIdProvider fileid = new DriveFileIdProvider(session); final Path folder = cryptomator.getFeature(session, Directory.class, new DriveDirectoryFeature(session, fileid)).mkdir( - new Path(vault, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory)), new TransferStatus()); + cryptomator.getFeature(session, Write.class, new DriveWriteFeature(session, fileid)), new Path(vault, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory)), new TransferStatus()); assertTrue(cryptomator.getFeature(session, Find.class, new DefaultFindFeature(session)).find(folder)); final String filename = new AlphanumericRandomStringService().random(); - new CryptoTouchFeature<>(session, new DefaultTouchFeature<>(new DriveWriteFeature(session, fileid)), new DriveWriteFeature(session, fileid), cryptomator).touch( - new Path(folder, filename, EnumSet.of(Path.Type.file)), new TransferStatus()); + new CryptoTouchFeature<>(session, new DefaultTouchFeature(session), cryptomator).touch( + cryptomator.getFeature(session, Write.class, new DriveWriteFeature(session, fileid)), new Path(folder, filename, EnumSet.of(Path.Type.file)), new TransferStatus()); assertTrue(cryptomator.getFeature(session, Find.class, new DefaultFindFeature(session)).find( new Path(folder, filename, EnumSet.of(Path.Type.file)))); // rename file @@ -186,7 +190,7 @@ public class MoveWorkerTest extends AbstractDriveTest { final Path home = DriveHomeFinderService.MYDRIVE_FOLDER; final Path clearFile = new Path(home, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)); final DriveFileIdProvider fileid = new DriveFileIdProvider(session); - new DriveTouchFeature(session, fileid).touch(clearFile, new TransferStatus()); + new DriveTouchFeature(session, fileid).touch(new DriveWriteFeature(session, fileid), clearFile, new TransferStatus()); assertTrue(new DefaultFindFeature(session).find(clearFile)); final CryptoVault cryptomator = new CryptoVault( new Path(home, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory))); @@ -194,7 +198,7 @@ public class MoveWorkerTest extends AbstractDriveTest { final DefaultVaultRegistry registry = new DefaultVaultRegistry(new DisabledPasswordCallback(), cryptomator); session.withRegistry(registry); final Path encryptedFolder = cryptomator.getFeature(session, Directory.class, new DriveDirectoryFeature(session, fileid)).mkdir( - new Path(vault, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory)), new TransferStatus()); + cryptomator.getFeature(session, Write.class, new DriveWriteFeature(session, fileid)), new Path(vault, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory)), new TransferStatus()); assertTrue(cryptomator.getFeature(session, Find.class, new DefaultFindFeature(session)).find(encryptedFolder)); // move file into vault final Path encryptedFile = new Path(encryptedFolder, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)); @@ -211,8 +215,8 @@ public class MoveWorkerTest extends AbstractDriveTest { final Path clearFolder = new Path(home, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory)); final Path clearFile = new Path(clearFolder, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)); final DriveFileIdProvider fileid = new DriveFileIdProvider(session); - new DriveDirectoryFeature(session, fileid).mkdir(clearFolder, new TransferStatus()); - new DriveTouchFeature(session, fileid).touch(clearFile, new TransferStatus()); + new DriveDirectoryFeature(session, fileid).mkdir(new DriveWriteFeature(session, fileid), clearFolder, new TransferStatus()); + new DriveTouchFeature(session, fileid).touch(new DriveWriteFeature(session, fileid), clearFile, new TransferStatus()); assertTrue(new DefaultFindFeature(session).find(clearFolder)); assertTrue(new DefaultFindFeature(session).find(clearFile)); final CryptoVault cryptomator = new CryptoVault( @@ -236,17 +240,17 @@ public class MoveWorkerTest extends AbstractDriveTest { final Path home = DriveHomeFinderService.MYDRIVE_FOLDER; final Path clearFolder = new Path(home, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory)); final DriveFileIdProvider fileid = new DriveFileIdProvider(session); - new DriveDirectoryFeature(session, fileid).mkdir(clearFolder, new TransferStatus()); + new DriveDirectoryFeature(session, fileid).mkdir(new DriveWriteFeature(session, fileid), clearFolder, new TransferStatus()); 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); final DefaultVaultRegistry registry = new DefaultVaultRegistry(new DisabledPasswordCallback(), cryptomator); session.withRegistry(registry); final Path encryptedFolder = cryptomator.getFeature(session, Directory.class, new DriveDirectoryFeature(session, fileid)).mkdir( - new Path(vault, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory)), new TransferStatus()); + cryptomator.getFeature(session, Write.class, new DriveWriteFeature(session, fileid)), new Path(vault, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory)), new TransferStatus()); assertTrue(cryptomator.getFeature(session, Find.class, new DefaultFindFeature(session)).find(encryptedFolder)); - final Path encryptedFile = new CryptoTouchFeature<>(session, new DefaultTouchFeature<>(new DriveWriteFeature(session, fileid)), new DriveWriteFeature(session, fileid), cryptomator).touch( - new Path(encryptedFolder, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)), new TransferStatus()); + final Path encryptedFile = new CryptoTouchFeature<>(session, new DefaultTouchFeature(session), cryptomator).touch( + cryptomator.getFeature(session, Write.class, new DriveWriteFeature(session, fileid)), new Path(encryptedFolder, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)), new TransferStatus()); assertTrue(cryptomator.getFeature(session, Find.class, new DefaultFindFeature(session)).find(encryptedFile)); // move file outside vault final Path fileRenamed = new Path(clearFolder, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)); @@ -268,11 +272,11 @@ public class MoveWorkerTest extends AbstractDriveTest { session.withRegistry(registry); final DriveFileIdProvider fileid = new DriveFileIdProvider(session); final Path encryptedFolder = cryptomator.getFeature(session, Directory.class, new DriveDirectoryFeature(session, fileid)).mkdir( - new Path(vault, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory)), new TransferStatus()); + cryptomator.getFeature(session, Write.class, new DriveWriteFeature(session, fileid)), new Path(vault, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory)), new TransferStatus()); assertTrue(cryptomator.getFeature(session, Find.class, new DefaultFindFeature(session)).find(encryptedFolder)); final String filename = new AlphanumericRandomStringService().random(); - final Path encryptedFile = new CryptoTouchFeature<>(session, new DefaultTouchFeature<>(new DriveWriteFeature(session, fileid)), new DriveWriteFeature(session, fileid), cryptomator).touch( - new Path(encryptedFolder, filename, EnumSet.of(Path.Type.file)), new TransferStatus()); + final Path encryptedFile = new CryptoTouchFeature<>(session, new DefaultTouchFeature(session), cryptomator).touch( + cryptomator.getFeature(session, Write.class, new DriveWriteFeature(session, fileid)), new Path(encryptedFolder, filename, EnumSet.of(Path.Type.file)), new TransferStatus()); assertTrue(cryptomator.getFeature(session, Find.class, new DefaultFindFeature(session)).find(encryptedFile)); // move directory outside vault final Path directoryRenamed = new Path(home, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory)); diff --git a/googledrive/src/test/java/ch/cyberduck/core/googledrive/DefaultAttributesFinderFeatureTest.java b/googledrive/src/test/java/ch/cyberduck/core/googledrive/DefaultAttributesFinderFeatureTest.java index 96dc65d1b5..d47e8b7f7d 100644 --- a/googledrive/src/test/java/ch/cyberduck/core/googledrive/DefaultAttributesFinderFeatureTest.java +++ b/googledrive/src/test/java/ch/cyberduck/core/googledrive/DefaultAttributesFinderFeatureTest.java @@ -16,6 +16,7 @@ package ch.cyberduck.core.googledrive; */ import ch.cyberduck.core.AlphanumericRandomStringService; +import ch.cyberduck.core.DefaultPathAttributes; import ch.cyberduck.core.DisabledConnectionCallback; import ch.cyberduck.core.DisabledLoginCallback; import ch.cyberduck.core.Path; @@ -57,7 +58,7 @@ public class DefaultAttributesFinderFeatureTest extends AbstractDriveTest { final DriveFileIdProvider fileid = new DriveFileIdProvider(session); final AttributesFinder f = new DefaultAttributesFinderFeature(session); final String name = new AlphanumericRandomStringService().random(); - final Path file = new DriveTouchFeature(session, fileid).touch(new Path( + final Path file = new DriveTouchFeature(session, fileid).touch(new DriveWriteFeature(session, fileid), new Path( DriveHomeFinderService.MYDRIVE_FOLDER, name, EnumSet.of(Path.Type.file)), new TransferStatus()); final String initialFileid = file.attributes().getFileId(); assertNotNull(initialFileid); @@ -81,10 +82,10 @@ public class DefaultAttributesFinderFeatureTest extends AbstractDriveTest { final HttpResponseOutputStream out = new DriveWriteFeature(session, fileid).write(file, status, new DisabledConnectionCallback()); IOUtils.copy(new ByteArrayInputStream(content), out); out.close(); - assertEquals(initialFileid, f.find(file.withAttributes(new PathAttributes(file.attributes()).setFileId(initialFileid))).getFileId()); + assertEquals(initialFileid, f.find(file.withAttributes(new DefaultPathAttributes(file.attributes()).setFileId(initialFileid))).getFileId()); final String newFileid = out.getStatus().getId(); - assertEquals(newFileid, f.find(file.withAttributes(new PathAttributes(file.attributes()).setFileId(newFileid))).getFileId()); - assertNotEquals(initialFileid, f.find(file.withAttributes(new PathAttributes(file.attributes()).setFileId(newFileid))).getFileId()); + assertEquals(newFileid, f.find(file.withAttributes(new DefaultPathAttributes(file.attributes()).setFileId(newFileid))).getFileId()); + assertNotEquals(initialFileid, f.find(file.withAttributes(new DefaultPathAttributes(file.attributes()).setFileId(newFileid))).getFileId()); assertEquals(out.getStatus().getId(), f.find(file).getFileId()); new DriveDeleteFeature(session, fileid).delete(Collections.singletonList(file), new DisabledLoginCallback(), new Delete.DisabledCallback()); } diff --git a/googledrive/src/test/java/ch/cyberduck/core/googledrive/DriveAttributesFinderFeatureTest.java b/googledrive/src/test/java/ch/cyberduck/core/googledrive/DriveAttributesFinderFeatureTest.java index 28f6ba6b8c..8e851c4aa5 100644 --- a/googledrive/src/test/java/ch/cyberduck/core/googledrive/DriveAttributesFinderFeatureTest.java +++ b/googledrive/src/test/java/ch/cyberduck/core/googledrive/DriveAttributesFinderFeatureTest.java @@ -77,7 +77,7 @@ public class DriveAttributesFinderFeatureTest extends AbstractDriveTest { public void testFindFolderInSharedDriveAsDefaultPath() throws Exception { final Path test = new Path(new Path(DriveHomeFinderService.SHARED_DRIVES_NAME, "iterate", EnumSet.of(Path.Type.directory)), new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)); final DriveFileIdProvider fileid = new DriveFileIdProvider(session); - new DriveTouchFeature(session, fileid).touch(test, new TransferStatus()); + new DriveTouchFeature(session, fileid).touch(new DriveWriteFeature(session, fileid), test, new TransferStatus()); final DriveAttributesFinderFeature f = new DriveAttributesFinderFeature(session, new DriveFileIdProvider(session)); assertNotEquals(PathAttributes.EMPTY, f.find(test)); new DriveDeleteFeature(session, fileid).delete(Collections.singletonList(test), new DisabledLoginCallback(), new Delete.DisabledCallback()); @@ -87,7 +87,7 @@ public class DriveAttributesFinderFeatureTest extends AbstractDriveTest { public void testFind() throws Exception { final Path test = new Path(DriveHomeFinderService.MYDRIVE_FOLDER, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)); final DriveFileIdProvider fileid = new DriveFileIdProvider(session); - new DriveTouchFeature(session, fileid).touch(test, new TransferStatus()); + new DriveTouchFeature(session, fileid).touch(new DriveWriteFeature(session, fileid), test, new TransferStatus()); final DriveAttributesFinderFeature f = new DriveAttributesFinderFeature(session, fileid); final PathAttributes attributes = f.find(test); assertEquals(0L, attributes.getSize()); @@ -100,10 +100,10 @@ public class DriveAttributesFinderFeatureTest extends AbstractDriveTest { public void testDuplicatesWithSameName() throws Exception { final DriveFileIdProvider fileid = new DriveFileIdProvider(session); final Path folder = new DriveDirectoryFeature(session, fileid).mkdir( - new Path(DriveHomeFinderService.MYDRIVE_FOLDER, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory)), new TransferStatus()); + new DriveWriteFeature(session, fileid), new Path(DriveHomeFinderService.MYDRIVE_FOLDER, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory)), new TransferStatus()); final String name = new AlphanumericRandomStringService().random(); final Path version1 = new DriveTouchFeature(session, fileid).touch( - new Path(folder, name, EnumSet.of(Path.Type.file)), new TransferStatus()); + new DriveWriteFeature(session, fileid), new Path(folder, name, EnumSet.of(Path.Type.file)), new TransferStatus()); final DriveAttributesFinderFeature f = new DriveAttributesFinderFeature(session, fileid); assertEquals(version1.attributes(), f.find(version1)); final AttributedList listBeforeDelete = new DriveListService(session, fileid).list(folder, new DisabledListProgressListener()); @@ -114,7 +114,7 @@ public class DriveAttributesFinderFeatureTest extends AbstractDriveTest { assertTrue(listAfterDelete.contains(version1)); assertTrue(listAfterDelete.find(new DefaultPathPredicate(version1)).attributes().isTrashed()); final Path version2 = new DriveTouchFeature(session, fileid).touch( - new Path(folder, name, EnumSet.of(Path.Type.file)), new TransferStatus()); + new DriveWriteFeature(session, fileid), new Path(folder, name, EnumSet.of(Path.Type.file)), new TransferStatus()); assertNotEquals(f.find(version1), f.find(version2)); final AttributedList listAfterReupload = new DriveListService(session, fileid).list(folder, new DisabledListProgressListener()); assertTrue(listAfterReupload.contains(version2)); @@ -126,7 +126,7 @@ public class DriveAttributesFinderFeatureTest extends AbstractDriveTest { public void testFindDirectory() throws Exception { final Path file = new Path(DriveHomeFinderService.MYDRIVE_FOLDER, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory)); final DriveFileIdProvider fileid = new DriveFileIdProvider(session); - new DriveDirectoryFeature(session, fileid).mkdir(file, new TransferStatus()); + new DriveDirectoryFeature(session, fileid).mkdir(new DriveWriteFeature(session, fileid), file, new TransferStatus()); final PathAttributes attributes = new DriveAttributesFinderFeature(session, fileid).find(file); assertNotNull(attributes); assertEquals(-1L, attributes.getSize()); @@ -140,7 +140,7 @@ public class DriveAttributesFinderFeatureTest extends AbstractDriveTest { public void testMissingShortcutTarget() throws Exception { final Path test = new Path(DriveHomeFinderService.MYDRIVE_FOLDER, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)); final DriveFileIdProvider fileid = new DriveFileIdProvider(session); - new DriveTouchFeature(session, fileid).touch(test, new TransferStatus()); + new DriveTouchFeature(session, fileid).touch(new DriveWriteFeature(session, fileid), test, new TransferStatus()); final DriveAttributesFinderFeature f = new DriveAttributesFinderFeature(session, fileid); final PathAttributes attributes = f.find(test); final File shortcut = session.getClient().files().create(new File() @@ -171,8 +171,8 @@ public class DriveAttributesFinderFeatureTest extends AbstractDriveTest { public void testChangedFileId() throws Exception { final DriveFileIdProvider fileid = new DriveFileIdProvider(session); final Path room = new DriveDirectoryFeature(session, fileid).mkdir( - new Path(MYDRIVE_FOLDER, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory)), new TransferStatus()); - final Path test = new DriveTouchFeature(session, fileid).touch(new Path(room, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)), new TransferStatus()); + new DriveWriteFeature(session, fileid), new Path(MYDRIVE_FOLDER, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory)), new TransferStatus()); + final Path test = new DriveTouchFeature(session, fileid).touch(new DriveWriteFeature(session, fileid), new Path(room, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)), new TransferStatus()); final String latestnodeid = test.attributes().getFileId(); assertNotNull(latestnodeid); // Assume previously seen but changed on server diff --git a/googledrive/src/test/java/ch/cyberduck/core/googledrive/DriveBatchDeleteFeatureTest.java b/googledrive/src/test/java/ch/cyberduck/core/googledrive/DriveBatchDeleteFeatureTest.java index 8d104f3a22..116c10a7ff 100644 --- a/googledrive/src/test/java/ch/cyberduck/core/googledrive/DriveBatchDeleteFeatureTest.java +++ b/googledrive/src/test/java/ch/cyberduck/core/googledrive/DriveBatchDeleteFeatureTest.java @@ -48,10 +48,10 @@ public class DriveBatchDeleteFeatureTest extends AbstractDriveTest { @Test public void testDeleteFolder() throws Exception { final DriveFileIdProvider fileid = new DriveFileIdProvider(session); - final Path directory = new DriveDirectoryFeature(session, fileid).mkdir(new Path(DriveHomeFinderService.MYDRIVE_FOLDER, + final Path directory = new DriveDirectoryFeature(session, fileid).mkdir(new DriveWriteFeature(session, fileid), new Path(DriveHomeFinderService.MYDRIVE_FOLDER, new AlphanumericRandomStringService().random(), EnumSet.of(AbstractPath.Type.directory)), new TransferStatus()); assertTrue(new DriveFindFeature(session, fileid).find(directory, new DisabledListProgressListener())); - final Path file = new DriveTouchFeature(session, fileid).touch(new Path(directory, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)), new TransferStatus()); + final Path file = new DriveTouchFeature(session, fileid).touch(new DriveWriteFeature(session, fileid), new Path(directory, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)), new TransferStatus()); assertTrue(new DriveFindFeature(session, fileid).find(file, new DisabledListProgressListener())); new DriveBatchDeleteFeature(session, fileid).delete(Collections.singletonList(directory), new DisabledLoginCallback(), new Delete.DisabledCallback()); assertFalse((new DriveFindFeature(session, fileid).find(directory, new DisabledListProgressListener()))); @@ -61,9 +61,9 @@ public class DriveBatchDeleteFeatureTest extends AbstractDriveTest { @Test public void testDeleteMultipleFiles() throws Exception { final DriveFileIdProvider fileid = new DriveFileIdProvider(session); - final Path folder = new DriveDirectoryFeature(session, fileid).mkdir(new Path(DriveHomeFinderService.MYDRIVE_FOLDER, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory)), new TransferStatus()); - final Path file1 = new DriveTouchFeature(session, fileid).touch(new Path(folder, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)), new TransferStatus()); - final Path file2 = new DriveTouchFeature(session, fileid).touch(new Path(folder, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)), new TransferStatus()); + final Path folder = new DriveDirectoryFeature(session, fileid).mkdir(new DriveWriteFeature(session, fileid), new Path(DriveHomeFinderService.MYDRIVE_FOLDER, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory)), new TransferStatus()); + final Path file1 = new DriveTouchFeature(session, fileid).touch(new DriveWriteFeature(session, fileid), new Path(folder, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)), new TransferStatus()); + final Path file2 = new DriveTouchFeature(session, fileid).touch(new DriveWriteFeature(session, fileid), new Path(folder, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)), new TransferStatus()); assertTrue(new DriveFindFeature(session, fileid).find(file1)); assertTrue(new DriveFindFeature(session, fileid).find(file2)); new DriveBatchDeleteFeature(session, fileid).delete(Arrays.asList(file1, file2), new DisabledLoginCallback(), new Delete.DisabledCallback()); diff --git a/googledrive/src/test/java/ch/cyberduck/core/googledrive/DriveBatchTrashFeatureTest.java b/googledrive/src/test/java/ch/cyberduck/core/googledrive/DriveBatchTrashFeatureTest.java index 1b9d370ae1..54a3b430d7 100644 --- a/googledrive/src/test/java/ch/cyberduck/core/googledrive/DriveBatchTrashFeatureTest.java +++ b/googledrive/src/test/java/ch/cyberduck/core/googledrive/DriveBatchTrashFeatureTest.java @@ -16,6 +16,7 @@ package ch.cyberduck.core.googledrive; */ 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; @@ -48,12 +49,12 @@ public class DriveBatchTrashFeatureTest extends AbstractDriveTest { public void testDeleteFromTrash() throws Exception { final DriveFileIdProvider fileid = new DriveFileIdProvider(session); final Path file = new DriveTouchFeature(session, fileid).touch( - new Path(DriveHomeFinderService.MYDRIVE_FOLDER, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)), new TransferStatus()); + new DriveWriteFeature(session, fileid), new Path(DriveHomeFinderService.MYDRIVE_FOLDER, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)), new TransferStatus()); final String fileId = file.attributes().getFileId(); new DriveBatchTrashFeature(session, fileid).delete(Collections.singletonList(file), new DisabledPasswordCallback(), new Delete.DisabledCallback()); assertFalse(new DriveFindFeature(session, fileid).find(file)); - assertTrue(new DriveFindFeature(session, fileid).find(file.withAttributes(new PathAttributes().setFileId(fileId)))); - final PathAttributes attributesInTrash = new DriveAttributesFinderFeature(session, fileid).find(file.withAttributes(new PathAttributes().setFileId(fileId))); + assertTrue(new DriveFindFeature(session, fileid).find(file.withAttributes(new DefaultPathAttributes().setFileId(fileId)))); + final PathAttributes attributesInTrash = new DriveAttributesFinderFeature(session, fileid).find(file.withAttributes(new DefaultPathAttributes().setFileId(fileId))); assertTrue(attributesInTrash.isTrashed()); new DriveBatchTrashFeature(session, fileid).delete(Collections.singletonList(file.withAttributes(attributesInTrash)), new DisabledPasswordCallback(), new Delete.DisabledCallback()); assertFalse(new DriveFindFeature(session, fileid).find(file.withAttributes(attributesInTrash))); diff --git a/googledrive/src/test/java/ch/cyberduck/core/googledrive/DriveCopyFeatureTest.java b/googledrive/src/test/java/ch/cyberduck/core/googledrive/DriveCopyFeatureTest.java index 79d1e6b59a..5c5e9520a5 100644 --- a/googledrive/src/test/java/ch/cyberduck/core/googledrive/DriveCopyFeatureTest.java +++ b/googledrive/src/test/java/ch/cyberduck/core/googledrive/DriveCopyFeatureTest.java @@ -44,7 +44,7 @@ public class DriveCopyFeatureTest extends AbstractDriveTest { final Path test = new Path(DriveHomeFinderService.MYDRIVE_FOLDER, UUID.randomUUID().toString(), EnumSet.of(Path.Type.file)); final DriveFileIdProvider fileid = new DriveFileIdProvider(session); final TransferStatus status = new TransferStatus(); - new DriveTouchFeature(session, fileid).touch(test, status); + new DriveTouchFeature(session, fileid).touch(new DriveWriteFeature(session, fileid), test, status); final Path copy = new Path(DriveHomeFinderService.MYDRIVE_FOLDER, UUID.randomUUID().toString(), EnumSet.of(Path.Type.file)); final Path target = new DriveCopyFeature(session, fileid).copy(test, copy, new TransferStatus(), new DisabledConnectionCallback(), new DisabledStreamListener()); assertNotEquals(test.attributes().getFileId(), target.attributes().getFileId()); @@ -58,12 +58,12 @@ public class DriveCopyFeatureTest extends AbstractDriveTest { public void testCopyToExistingFile() throws Exception { final Path folder = new Path(DriveHomeFinderService.MYDRIVE_FOLDER, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory)); final DriveFileIdProvider fileid = new DriveFileIdProvider(session); - new DriveDirectoryFeature(session, fileid).mkdir(folder, new TransferStatus()); + new DriveDirectoryFeature(session, fileid).mkdir(new DriveWriteFeature(session, fileid), folder, new TransferStatus()); final Path test = new Path(folder, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)); - new DriveTouchFeature(session, fileid).touch(test, new TransferStatus()); + new DriveTouchFeature(session, fileid).touch(new DriveWriteFeature(session, fileid), test, new TransferStatus()); final Path copy = new Path(folder, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)); final TransferStatus status = new TransferStatus(); - new DriveTouchFeature(session, fileid).touch(copy, status); + new DriveTouchFeature(session, fileid).touch(new DriveWriteFeature(session, fileid), copy, status); final Path target = new DriveCopyFeature(session, fileid).copy(test, copy, new TransferStatus().setExists(true), new DisabledConnectionCallback(), new DisabledStreamListener()); assertNotEquals(test.attributes().getFileId(), target.attributes().getFileId()); final Find find = new DefaultFindFeature(session); diff --git a/googledrive/src/test/java/ch/cyberduck/core/googledrive/DriveDefaultListServiceTest.java b/googledrive/src/test/java/ch/cyberduck/core/googledrive/DriveDefaultListServiceTest.java index e86200d48c..7a082ca8c4 100644 --- a/googledrive/src/test/java/ch/cyberduck/core/googledrive/DriveDefaultListServiceTest.java +++ b/googledrive/src/test/java/ch/cyberduck/core/googledrive/DriveDefaultListServiceTest.java @@ -79,8 +79,8 @@ public class DriveDefaultListServiceTest extends AbstractDriveTest { public void testListSharedDriveFolder() throws Exception { final DriveFileIdProvider fileid = new DriveFileIdProvider(session); final Path directory = new DriveDirectoryFeature(session, fileid).mkdir( - new Path(new Path(SHARED_DRIVES_NAME, "iterate", EnumSet.of(Path.Type.directory)), new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory)), new TransferStatus()); - final Path file = new DriveTouchFeature(session, fileid).touch(new Path(directory, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)), new TransferStatus()); + new DriveWriteFeature(session, fileid), new Path(new Path(SHARED_DRIVES_NAME, "iterate", EnumSet.of(Path.Type.directory)), new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory)), new TransferStatus()); + final Path file = new DriveTouchFeature(session, fileid).touch(new DriveWriteFeature(session, fileid), new Path(directory, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)), new TransferStatus()); final AttributedList list = new DriveDefaultListService(session, fileid).list(directory, new DisabledListProgressListener()); assertFalse(list.isEmpty()); for(Path f : list) { @@ -94,9 +94,9 @@ public class DriveDefaultListServiceTest extends AbstractDriveTest { public void testListMissingFileidOnFolder() throws Exception { final DriveFileIdProvider fileid = new DriveFileIdProvider(session); final Path directory = new DriveDirectoryFeature(session, fileid).mkdir( - new Path(MYDRIVE_FOLDER, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory)), new TransferStatus()); - final Path f2 = new DriveTouchFeature(session, fileid).touch(new Path(directory, "aa", EnumSet.of(Path.Type.file)), new TransferStatus()); - final Path f1 = new DriveTouchFeature(session, fileid).touch(new Path(directory, "a", EnumSet.of(Path.Type.file)), new TransferStatus()); + new DriveWriteFeature(session, fileid), new Path(MYDRIVE_FOLDER, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory)), new TransferStatus()); + final Path f2 = new DriveTouchFeature(session, fileid).touch(new DriveWriteFeature(session, fileid), new Path(directory, "aa", EnumSet.of(Path.Type.file)), new TransferStatus()); + final Path f1 = new DriveTouchFeature(session, fileid).touch(new DriveWriteFeature(session, fileid), new Path(directory, "a", EnumSet.of(Path.Type.file)), new TransferStatus()); fileid.cache(directory, null); final AttributedList list = new DriveDefaultListService(session, fileid).list(directory, new DisabledListProgressListener()); assertEquals(2, list.size()); @@ -109,9 +109,9 @@ public class DriveDefaultListServiceTest extends AbstractDriveTest { public void testListLexicographicallyLetters() throws Exception { final DriveFileIdProvider fileid = new DriveFileIdProvider(session); final Path directory = new DriveDirectoryFeature(session, fileid).mkdir( - new Path(MYDRIVE_FOLDER, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory)), new TransferStatus()); - final Path f2 = new DriveTouchFeature(session, fileid).touch(new Path(directory, "aa", EnumSet.of(Path.Type.file)), new TransferStatus()); - final Path f1 = new DriveTouchFeature(session, fileid).touch(new Path(directory, "a", EnumSet.of(Path.Type.file)), new TransferStatus()); + new DriveWriteFeature(session, fileid), new Path(MYDRIVE_FOLDER, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory)), new TransferStatus()); + final Path f2 = new DriveTouchFeature(session, fileid).touch(new DriveWriteFeature(session, fileid), new Path(directory, "aa", EnumSet.of(Path.Type.file)), new TransferStatus()); + final Path f1 = new DriveTouchFeature(session, fileid).touch(new DriveWriteFeature(session, fileid), new Path(directory, "a", EnumSet.of(Path.Type.file)), new TransferStatus()); assertTrue(f1.getName().compareTo(f2.getName()) < 0); final AttributedList list = new DriveDefaultListService(session, fileid).list(directory, new DisabledListProgressListener()); assertEquals(2, list.size()); @@ -124,9 +124,9 @@ public class DriveDefaultListServiceTest extends AbstractDriveTest { public void testListLexicographicallyNumbers() throws Exception { final DriveFileIdProvider fileid = new DriveFileIdProvider(session); final Path directory = new DriveDirectoryFeature(session, fileid).mkdir( - new Path(MYDRIVE_FOLDER, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory)), new TransferStatus()); - final Path f2 = new DriveTouchFeature(session, fileid).touch(new Path(directory, "103", EnumSet.of(Path.Type.file)), new TransferStatus()); - final Path f1 = new DriveTouchFeature(session, fileid).touch(new Path(directory, "101", EnumSet.of(Path.Type.file)), new TransferStatus()); + new DriveWriteFeature(session, fileid), new Path(MYDRIVE_FOLDER, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory)), new TransferStatus()); + final Path f2 = new DriveTouchFeature(session, fileid).touch(new DriveWriteFeature(session, fileid), new Path(directory, "103", EnumSet.of(Path.Type.file)), new TransferStatus()); + final Path f1 = new DriveTouchFeature(session, fileid).touch(new DriveWriteFeature(session, fileid), new Path(directory, "101", EnumSet.of(Path.Type.file)), new TransferStatus()); assertTrue(f1.getName().compareTo(f2.getName()) < 0); final AttributedList list = new DriveDefaultListService(session, fileid).list(directory, new DisabledListProgressListener()); assertEquals(2, list.size()); @@ -139,8 +139,8 @@ public class DriveDefaultListServiceTest extends AbstractDriveTest { public void testListFilenameSingleQuote() throws Exception { final DriveFileIdProvider fileid = new DriveFileIdProvider(session); final Path directory = new DriveDirectoryFeature(session, fileid).mkdir( - new Path(MYDRIVE_FOLDER, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory)), new TransferStatus()); - final Path f1 = new DriveTouchFeature(session, fileid).touch(new Path(directory, String.format("%s'", new AsciiRandomStringService().random()), EnumSet.of(Path.Type.file)), new TransferStatus()); + new DriveWriteFeature(session, fileid), new Path(MYDRIVE_FOLDER, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory)), new TransferStatus()); + final Path f1 = new DriveTouchFeature(session, fileid).touch(new DriveWriteFeature(session, fileid), new Path(directory, String.format("%s'", new AsciiRandomStringService().random()), EnumSet.of(Path.Type.file)), new TransferStatus()); final AttributedList list = new DriveDefaultListService(session, fileid).list(directory, new DisabledListProgressListener()); assertEquals(1, list.size()); assertEquals(f1, list.get(0)); @@ -151,8 +151,8 @@ public class DriveDefaultListServiceTest extends AbstractDriveTest { public void testListFilenameBackslash() throws Exception { final DriveFileIdProvider fileid = new DriveFileIdProvider(session); final Path directory = new DriveDirectoryFeature(session, fileid).mkdir( - new Path(MYDRIVE_FOLDER, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory)), new TransferStatus()); - final Path f1 = new DriveTouchFeature(session, fileid).touch(new Path(directory, String.format("%s\\", new AsciiRandomStringService().random()), EnumSet.of(Path.Type.file)), new TransferStatus()); + new DriveWriteFeature(session, fileid), new Path(MYDRIVE_FOLDER, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory)), new TransferStatus()); + final Path f1 = new DriveTouchFeature(session, fileid).touch(new DriveWriteFeature(session, fileid), new Path(directory, String.format("%s\\", new AsciiRandomStringService().random()), EnumSet.of(Path.Type.file)), new TransferStatus()); final AttributedList list = new DriveDefaultListService(session, fileid).list(directory, new DisabledListProgressListener()); assertEquals(1, list.size()); assertEquals(f1, list.get(0)); @@ -164,8 +164,8 @@ public class DriveDefaultListServiceTest extends AbstractDriveTest { final Path file = new Path(MYDRIVE_FOLDER, String.format("%s:name", new AlphanumericRandomStringService().random()), EnumSet.of(Path.Type.file)); final Path folder = new Path(MYDRIVE_FOLDER, String.format("%s:name", new AlphanumericRandomStringService().random()), EnumSet.of(Path.Type.directory)); final DriveFileIdProvider fileid = new DriveFileIdProvider(session); - new DriveTouchFeature(session, fileid).touch(file, new TransferStatus()); - new DriveDirectoryFeature(session, fileid).mkdir(folder, new TransferStatus()); + new DriveTouchFeature(session, fileid).touch(new DriveWriteFeature(session, fileid), file, new TransferStatus()); + new DriveDirectoryFeature(session, fileid).mkdir(new DriveWriteFeature(session, fileid), folder, new TransferStatus()); assertTrue(new DefaultFindFeature(session).find(file)); assertTrue(new DefaultFindFeature(session).find(folder)); new DriveDeleteFeature(session, fileid).delete(Arrays.asList(file, folder), new DisabledLoginCallback(), new Delete.DisabledCallback()); @@ -178,15 +178,15 @@ public class DriveDefaultListServiceTest extends AbstractDriveTest { final Path parent = new Path(MYDRIVE_FOLDER, f1, EnumSet.of(Path.Type.directory)); final Path folder = new Path(parent, f2, EnumSet.of(Path.Type.directory)); final DriveFileIdProvider provider = new DriveFileIdProvider(session); - new DriveDirectoryFeature(session, provider).mkdir(parent, new TransferStatus()); - new DriveDirectoryFeature(session, provider).mkdir(folder, new TransferStatus()); + new DriveDirectoryFeature(session, provider).mkdir(new DriveWriteFeature(session, provider), parent, new TransferStatus()); + new DriveDirectoryFeature(session, provider).mkdir(new DriveWriteFeature(session, provider), folder, new TransferStatus()); assertTrue(new DefaultFindFeature(session).find(folder)); assertEquals(1, new DriveDefaultListService(session, provider).list(parent, new DisabledListProgressListener()).size()); final String fileid = provider.getFileId(folder); final File body = new File(); body.set("trashed", true); session.getClient().files().update(fileid, body).execute(); - new DriveDirectoryFeature(session, provider).mkdir(folder, new TransferStatus()); + new DriveDirectoryFeature(session, provider).mkdir(new DriveWriteFeature(session, provider), folder, new TransferStatus()); assertEquals(2, new DriveDefaultListService(session, provider).list(parent, new DisabledListProgressListener()).size()); new DriveDeleteFeature(session, provider).delete(Collections.singletonList(parent), new DisabledLoginCallback(), new Delete.DisabledCallback()); } @@ -195,7 +195,7 @@ public class DriveDefaultListServiceTest extends AbstractDriveTest { public void testListEmptyFolder() throws Exception { final DriveFileIdProvider fileid = new DriveFileIdProvider(session); final Path folder = new DriveDirectoryFeature(session, fileid).mkdir( - new Path(MYDRIVE_FOLDER, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory)), new TransferStatus()); + new DriveWriteFeature(session, fileid), new Path(MYDRIVE_FOLDER, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory)), new TransferStatus()); final AtomicBoolean callback = new AtomicBoolean(); assertTrue(new DriveDefaultListService(session, fileid).list(folder, new DisabledListProgressListener() { @Override diff --git a/googledrive/src/test/java/ch/cyberduck/core/googledrive/DriveDeleteFeatureTest.java b/googledrive/src/test/java/ch/cyberduck/core/googledrive/DriveDeleteFeatureTest.java index 7590ee341f..a44b36ed16 100644 --- a/googledrive/src/test/java/ch/cyberduck/core/googledrive/DriveDeleteFeatureTest.java +++ b/googledrive/src/test/java/ch/cyberduck/core/googledrive/DriveDeleteFeatureTest.java @@ -48,7 +48,7 @@ public class DriveDeleteFeatureTest extends AbstractDriveTest { @Test public void testDeleteFolder() throws Exception { final DriveFileIdProvider fileid = new DriveFileIdProvider(session); - final Path directory = new DriveDirectoryFeature(session, fileid).mkdir(new Path(DriveHomeFinderService.MYDRIVE_FOLDER, + final Path directory = new DriveDirectoryFeature(session, fileid).mkdir(new DriveWriteFeature(session, fileid), new Path(DriveHomeFinderService.MYDRIVE_FOLDER, new AlphanumericRandomStringService().random(), EnumSet.of(AbstractPath.Type.directory)), new TransferStatus()); assertTrue(new DriveFindFeature(session, fileid).find(directory, new DisabledListProgressListener())); new DriveDeleteFeature(session, fileid).delete(Collections.singletonList(directory), new DisabledLoginCallback(), new Delete.DisabledCallback()); @@ -58,9 +58,9 @@ public class DriveDeleteFeatureTest extends AbstractDriveTest { @Test public void testDeleteMultipleFiles() throws Exception { final DriveFileIdProvider fileid = new DriveFileIdProvider(session); - final Path folder = new DriveDirectoryFeature(session, fileid).mkdir(new Path(DriveHomeFinderService.MYDRIVE_FOLDER, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory)), new TransferStatus()); - final Path file1 = new DriveTouchFeature(session, fileid).touch(new Path(folder, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)), new TransferStatus()); - final Path file2 = new DriveTouchFeature(session, fileid).touch(new Path(folder, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)), new TransferStatus()); + final Path folder = new DriveDirectoryFeature(session, fileid).mkdir(new DriveWriteFeature(session, fileid), new Path(DriveHomeFinderService.MYDRIVE_FOLDER, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory)), new TransferStatus()); + final Path file1 = new DriveTouchFeature(session, fileid).touch(new DriveWriteFeature(session, fileid), new Path(folder, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)), new TransferStatus()); + final Path file2 = new DriveTouchFeature(session, fileid).touch(new DriveWriteFeature(session, fileid), new Path(folder, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)), new TransferStatus()); assertTrue(new DriveFindFeature(session, fileid).find(file1)); assertTrue(new DriveFindFeature(session, fileid).find(file2)); new DriveDeleteFeature(session, fileid).delete(Arrays.asList(file1, file2), new DisabledLoginCallback(), new Delete.DisabledCallback()); diff --git a/googledrive/src/test/java/ch/cyberduck/core/googledrive/DriveDirectoryFeatureTest.java b/googledrive/src/test/java/ch/cyberduck/core/googledrive/DriveDirectoryFeatureTest.java index 6edd4ba06f..39cfd8d41f 100644 --- a/googledrive/src/test/java/ch/cyberduck/core/googledrive/DriveDirectoryFeatureTest.java +++ b/googledrive/src/test/java/ch/cyberduck/core/googledrive/DriveDirectoryFeatureTest.java @@ -41,13 +41,13 @@ public class DriveDirectoryFeatureTest extends AbstractDriveTest { public void testMkdir() throws Exception { final DriveFileIdProvider fileid = new DriveFileIdProvider(session); final Path folder = new DriveDirectoryFeature(session, fileid).mkdir( - new Path(DriveHomeFinderService.MYDRIVE_FOLDER, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory)), new TransferStatus()); + new DriveWriteFeature(session, fileid), new Path(DriveHomeFinderService.MYDRIVE_FOLDER, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory)), new TransferStatus()); final Path test = new DriveDirectoryFeature(session, fileid).mkdir( - new Path(folder, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory)), new TransferStatus()); + new DriveWriteFeature(session, fileid), new Path(folder, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory)), new TransferStatus()); final String id = test.attributes().getFileId(); assertNotNull(test.attributes().getFileId()); assertTrue(new DefaultFindFeature(session).find(test)); - assertThrows(ConflictException.class, () -> new DriveDirectoryFeature(session, fileid).mkdir(test, new TransferStatus())); + assertThrows(ConflictException.class, () -> new DriveDirectoryFeature(session, fileid).mkdir(new DriveWriteFeature(session, fileid), test, new TransferStatus())); new DriveTrashFeature(session, fileid).delete(Collections.singletonList(test), new DisabledLoginCallback(), new Delete.DisabledCallback()); assertNull(test.attributes().getFileId()); // Trashed diff --git a/googledrive/src/test/java/ch/cyberduck/core/googledrive/DriveFileIdProviderTest.java b/googledrive/src/test/java/ch/cyberduck/core/googledrive/DriveFileIdProviderTest.java index 230c640138..2825adc83b 100644 --- a/googledrive/src/test/java/ch/cyberduck/core/googledrive/DriveFileIdProviderTest.java +++ b/googledrive/src/test/java/ch/cyberduck/core/googledrive/DriveFileIdProviderTest.java @@ -52,7 +52,7 @@ public class DriveFileIdProviderTest extends AbstractDriveTest { public void testGetFileid() throws Exception { final Path test = new Path(DriveHomeFinderService.MYDRIVE_FOLDER, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)); final DriveFileIdProvider fileid = new DriveFileIdProvider(session); - new DriveTouchFeature(session, fileid).touch(test, new TransferStatus()); + new DriveTouchFeature(session, fileid).touch(new DriveWriteFeature(session, fileid), test, new TransferStatus()); assertNotNull(fileid.getFileId(test)); new DriveDeleteFeature(session, fileid).delete(Collections.singletonList(test), new DisabledLoginCallback(), new Delete.DisabledCallback()); } @@ -61,7 +61,7 @@ public class DriveFileIdProviderTest extends AbstractDriveTest { public void testGetFileidAccentCharacter() throws Exception { final Path test = new Path(DriveHomeFinderService.MYDRIVE_FOLDER, String.format("%sà", new AlphanumericRandomStringService().random()), EnumSet.of(Path.Type.file)); final DriveFileIdProvider fileid = new DriveFileIdProvider(session); - new DriveTouchFeature(session, fileid).touch(test, new TransferStatus()); + new DriveTouchFeature(session, fileid).touch(new DriveWriteFeature(session, fileid), test, new TransferStatus()); assertNotNull(fileid.getFileId(test)); new DriveDeleteFeature(session, fileid).delete(Collections.singletonList(test), new DisabledLoginCallback(), new Delete.DisabledCallback()); } @@ -70,7 +70,7 @@ public class DriveFileIdProviderTest extends AbstractDriveTest { public void testGetFileidSingleQuoteCharacter() throws Exception { final Path test = new Path(DriveHomeFinderService.MYDRIVE_FOLDER, String.format("%s'", new AlphanumericRandomStringService().random()), EnumSet.of(Path.Type.file)); final DriveFileIdProvider driveFileIdProvider = new DriveFileIdProvider(session); - new DriveTouchFeature(session, driveFileIdProvider).touch(test, new TransferStatus()); + new DriveTouchFeature(session, driveFileIdProvider).touch(new DriveWriteFeature(session, driveFileIdProvider), test, new TransferStatus()); assertNotNull(driveFileIdProvider.getFileId(test)); new DriveDeleteFeature(session, driveFileIdProvider).delete(Collections.singletonList(test), new DisabledLoginCallback(), new Delete.DisabledCallback()); } @@ -79,7 +79,7 @@ public class DriveFileIdProviderTest extends AbstractDriveTest { public void testGetFileidBackslashCharacter() throws Exception { final Path test = new Path(DriveHomeFinderService.MYDRIVE_FOLDER, String.format("%s\\", new AlphanumericRandomStringService().random()), EnumSet.of(Path.Type.file)); final DriveFileIdProvider fileid = new DriveFileIdProvider(session); - new DriveTouchFeature(session, fileid).touch(test, new TransferStatus()); + new DriveTouchFeature(session, fileid).touch(new DriveWriteFeature(session, fileid), test, new TransferStatus()); assertNotNull(fileid.getFileId(test)); new DriveDeleteFeature(session, fileid).delete(Collections.singletonList(test), new DisabledLoginCallback(), new Delete.DisabledCallback()); } @@ -88,7 +88,7 @@ public class DriveFileIdProviderTest extends AbstractDriveTest { public void testGetFileidDoubleBackslashCharacter() throws Exception { final Path test = new Path(DriveHomeFinderService.MYDRIVE_FOLDER, String.format("%s\\\\", new AlphanumericRandomStringService().random()), EnumSet.of(Path.Type.file)); final DriveFileIdProvider fileid = new DriveFileIdProvider(session); - new DriveTouchFeature(session, fileid).touch(test, new TransferStatus()); + new DriveTouchFeature(session, fileid).touch(new DriveWriteFeature(session, fileid), test, new TransferStatus()); assertNotNull(fileid.getFileId(test)); new DriveDeleteFeature(session, fileid).delete(Collections.singletonList(test), new DisabledLoginCallback(), new Delete.DisabledCallback()); } @@ -98,11 +98,11 @@ public class DriveFileIdProviderTest extends AbstractDriveTest { final String filename = new AlphanumericRandomStringService().random(); final Path test1 = new Path(DriveHomeFinderService.MYDRIVE_FOLDER, filename, EnumSet.of(Path.Type.file)); final DriveFileIdProvider fileid = new DriveFileIdProvider(session); - final Path p1 = new DriveTouchFeature(session, fileid).touch(test1, new TransferStatus()); + final Path p1 = new DriveTouchFeature(session, fileid).touch(new DriveWriteFeature(session, fileid), test1, new TransferStatus()); assertEquals(p1.attributes().getFileId(), fileid.getFileId(test1)); new DriveDeleteFeature(session, fileid).delete(Collections.singletonList(test1), new DisabledPasswordCallback(), new Delete.DisabledCallback()); final Path test2 = new Path(DriveHomeFinderService.MYDRIVE_FOLDER, filename, EnumSet.of(Path.Type.file)); - final Path p2 = new DriveTouchFeature(session, fileid).touch(test2, new TransferStatus()); + final Path p2 = new DriveTouchFeature(session, fileid).touch(new DriveWriteFeature(session, fileid), test2, new TransferStatus()); assertEquals(p2.attributes().getFileId(), fileid.getFileId(test2)); session.getClient().files().delete(p2.attributes().getFileId()); } @@ -111,15 +111,15 @@ public class DriveFileIdProviderTest extends AbstractDriveTest { public void testFileIdCollision() throws Exception { final DriveFileIdProvider fileid = new DriveFileIdProvider(session); final Path directory = new DriveDirectoryFeature(session, fileid).mkdir( - new Path(DriveHomeFinderService.MYDRIVE_FOLDER, UUID.randomUUID().toString(), EnumSet.of(Path.Type.directory)), new TransferStatus()); + new DriveWriteFeature(session, fileid), new Path(DriveHomeFinderService.MYDRIVE_FOLDER, UUID.randomUUID().toString(), EnumSet.of(Path.Type.directory)), new TransferStatus()); final Path path2R = new Path(directory, "2R", EnumSet.of(Path.Type.directory)); final Path path33 = new Path(directory, "33", EnumSet.of(Path.Type.directory)); final Directory directoryFeature = new DriveDirectoryFeature(session, fileid); - final Path path2RWithId = directoryFeature.mkdir(path2R, new TransferStatus()); + final Path path2RWithId = directoryFeature.mkdir(new DriveWriteFeature(session, fileid), path2R, new TransferStatus()); assertNotNull(path2RWithId.attributes().getFileId()); - final Path path33WithId = directoryFeature.mkdir(path33, new TransferStatus()); + final Path path33WithId = directoryFeature.mkdir(new DriveWriteFeature(session, fileid), path33, new TransferStatus()); assertNotNull(path33WithId.attributes().getFileId()); assertNotEquals(path2RWithId.attributes().getFileId(), path33WithId.attributes().getFileId()); diff --git a/googledrive/src/test/java/ch/cyberduck/core/googledrive/DriveFindFeatureTest.java b/googledrive/src/test/java/ch/cyberduck/core/googledrive/DriveFindFeatureTest.java index 76ad667d14..399cda7ee6 100644 --- a/googledrive/src/test/java/ch/cyberduck/core/googledrive/DriveFindFeatureTest.java +++ b/googledrive/src/test/java/ch/cyberduck/core/googledrive/DriveFindFeatureTest.java @@ -22,7 +22,6 @@ import ch.cyberduck.core.Path; import ch.cyberduck.core.SimplePathPredicate; import ch.cyberduck.core.features.Delete; import ch.cyberduck.core.shared.DefaultFindFeature; -import ch.cyberduck.core.shared.DefaultHomeFinderService; import ch.cyberduck.core.transfer.TransferStatus; import ch.cyberduck.test.IntegrationTest; @@ -49,7 +48,7 @@ public class DriveFindFeatureTest extends AbstractDriveTest { public void testFindDirectory() throws Exception { final DriveFileIdProvider fileid = new DriveFileIdProvider(session); final Path folder = new DriveDirectoryFeature(session, fileid).mkdir( - new Path(DriveHomeFinderService.MYDRIVE_FOLDER, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory)), new TransferStatus()); + new DriveWriteFeature(session, fileid), new Path(DriveHomeFinderService.MYDRIVE_FOLDER, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory)), new TransferStatus()); assertTrue(new DriveFindFeature(session, fileid).find(folder)); assertFalse(new DriveFindFeature(session, fileid).find(new Path(folder.getAbsolute(), EnumSet.of(Path.Type.file)))); new DriveDeleteFeature(session, fileid).delete(Collections.singletonList(folder), new DisabledLoginCallback(), new Delete.DisabledCallback()); @@ -59,7 +58,7 @@ public class DriveFindFeatureTest extends AbstractDriveTest { public void testFindFile() throws Exception { final DriveFileIdProvider fileid = new DriveFileIdProvider(session); final Path file = new Path(DriveHomeFinderService.MYDRIVE_FOLDER, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)); - new DriveTouchFeature(session, fileid).touch(file, new TransferStatus()); + new DriveTouchFeature(session, fileid).touch(new DriveWriteFeature(session, fileid), file, new TransferStatus()); assertTrue(new DriveFindFeature(session, fileid).find(file)); assertFalse(new DriveFindFeature(session, fileid).find(new Path(file.getAbsolute(), EnumSet.of(Path.Type.directory)))); new DriveDeleteFeature(session, fileid).delete(Collections.singletonList(file), new DisabledLoginCallback(), new Delete.DisabledCallback()); @@ -69,9 +68,9 @@ public class DriveFindFeatureTest extends AbstractDriveTest { public void testFind() throws Exception { final DriveFileIdProvider fileid = new DriveFileIdProvider(session); final Path folder = new DriveDirectoryFeature(session, fileid).mkdir( - new Path(DriveHomeFinderService.MYDRIVE_FOLDER, UUID.randomUUID().toString(), EnumSet.of(Path.Type.directory)), new TransferStatus()); + new DriveWriteFeature(session, fileid), new Path(DriveHomeFinderService.MYDRIVE_FOLDER, UUID.randomUUID().toString(), EnumSet.of(Path.Type.directory)), new TransferStatus()); final Path file = new DriveTouchFeature(session, fileid).touch( - new Path(folder, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)), new TransferStatus()); + new DriveWriteFeature(session, fileid), new Path(folder, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)), new TransferStatus()); final String id = file.attributes().getFileId(); assertTrue(new DriveFindFeature(session, fileid).find(file)); new DriveTrashFeature(session, fileid).delete(Collections.singletonList(file), new DisabledLoginCallback(), new Delete.DisabledCallback()); @@ -84,7 +83,7 @@ public class DriveFindFeatureTest extends AbstractDriveTest { assertTrue(new DefaultFindFeature(session).find(trashed)); assertTrue(new DriveFindFeature(session, fileid).find(trashed)); // Recreate file - final Path version2 = new DriveTouchFeature(session, fileid).touch(file, new TransferStatus()); + final Path version2 = new DriveTouchFeature(session, fileid).touch(new DriveWriteFeature(session, fileid), file, new TransferStatus()); assertTrue(new DefaultFindFeature(session).find(version2)); assertTrue(new DriveFindFeature(session, fileid).find(version2)); assertEquals(version2.attributes(), new DriveAttributesFinderFeature(session, fileid).find(version2)); diff --git a/googledrive/src/test/java/ch/cyberduck/core/googledrive/DriveMetadataFeatureTest.java b/googledrive/src/test/java/ch/cyberduck/core/googledrive/DriveMetadataFeatureTest.java index c8a25f1827..2cd06d59a2 100644 --- a/googledrive/src/test/java/ch/cyberduck/core/googledrive/DriveMetadataFeatureTest.java +++ b/googledrive/src/test/java/ch/cyberduck/core/googledrive/DriveMetadataFeatureTest.java @@ -43,7 +43,7 @@ public class DriveMetadataFeatureTest extends AbstractDriveTest { final Path test = new Path(DriveHomeFinderService.MYDRIVE_FOLDER, UUID.randomUUID().toString(), EnumSet.of(Path.Type.file)); final DriveFileIdProvider fileid = new DriveFileIdProvider(session); final TransferStatus status = new TransferStatus(); - new DriveTouchFeature(session, fileid).touch(test, status); + new DriveTouchFeature(session, fileid).touch(new DriveWriteFeature(session, fileid), test, status); final DriveMetadataFeature feature = new DriveMetadataFeature(session, fileid); assertEquals(Collections.emptyMap(), feature.getMetadata(test)); feature.setMetadata(test, Collections.singletonMap("test", "t")); @@ -60,8 +60,8 @@ public class DriveMetadataFeatureTest extends AbstractDriveTest { public void testChangedFileId() throws Exception { final DriveFileIdProvider fileid = new DriveFileIdProvider(session); final Path room = new DriveDirectoryFeature(session, fileid).mkdir( - new Path(MYDRIVE_FOLDER, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory)), new TransferStatus()); - final Path test = new DriveTouchFeature(session, fileid).touch(new Path(room, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)), new TransferStatus()); + new DriveWriteFeature(session, fileid), new Path(MYDRIVE_FOLDER, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory)), new TransferStatus()); + final Path test = new DriveTouchFeature(session, fileid).touch(new DriveWriteFeature(session, fileid), new Path(room, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)), new TransferStatus()); final String latestfileid = test.attributes().getFileId(); assertNotNull(latestfileid); // Assume previously seen but changed on server diff --git a/googledrive/src/test/java/ch/cyberduck/core/googledrive/DriveMoveFeatureTest.java b/googledrive/src/test/java/ch/cyberduck/core/googledrive/DriveMoveFeatureTest.java index 49f0b571d0..bdfc752647 100644 --- a/googledrive/src/test/java/ch/cyberduck/core/googledrive/DriveMoveFeatureTest.java +++ b/googledrive/src/test/java/ch/cyberduck/core/googledrive/DriveMoveFeatureTest.java @@ -17,6 +17,7 @@ package ch.cyberduck.core.googledrive; 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; @@ -56,34 +57,39 @@ public class DriveMoveFeatureTest extends AbstractDriveTest { @Test public void testMoveFile() throws Exception { final DriveFileIdProvider fileid = new DriveFileIdProvider(session); - final Path test = new DriveTouchFeature(session, fileid).touch(new Path(DriveHomeFinderService.MYDRIVE_FOLDER, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)), new TransferStatus()); + final Path test = new DriveTouchFeature(session, fileid).touch(new DriveWriteFeature(session, fileid), new Path(DriveHomeFinderService.MYDRIVE_FOLDER, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)), new TransferStatus()); final String id = test.attributes().getFileId(); - final Path folder = new Path(DriveHomeFinderService.MYDRIVE_FOLDER, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory)); - new DriveDirectoryFeature(session, fileid).mkdir(folder, new TransferStatus()); + final Path folder = new DriveDirectoryFeature(session, fileid).mkdir(new DriveWriteFeature(session, fileid), + new Path(DriveHomeFinderService.MYDRIVE_FOLDER, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory)), new TransferStatus()); + assertEquals(folder.attributes(), new DriveAttributesFinderFeature(session, fileid).find(folder)); final Path target = new DriveMoveFeature(session, fileid).move(test, new Path(folder, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)), new TransferStatus(), new Delete.DisabledCallback(), new DisabledConnectionCallback()); assertEquals(id, target.attributes().getFileId()); + assertEquals(test.attributes().getChecksum(), target.attributes().getChecksum()); + assertNotEquals(test.attributes().getModificationDate(), target.attributes().getModificationDate()); final Find find = new DefaultFindFeature(session); assertFalse(find.find(test)); assertTrue(find.find(target)); + assertEquals(folder.attributes(), new DriveAttributesFinderFeature(session, fileid).find(folder)); final PathAttributes targetAttr = new DriveAttributesFinderFeature(session, fileid).find(target); - assertEquals(Comparison.equal, session.getHost().getProtocol().getFeature(ComparisonService.class).compare(Path.Type.file, target.attributes(), targetAttr)); + assertEquals(target.attributes(), targetAttr); + assertEquals(Comparison.equal, session.getHost().getProtocol().getFeature(ComparisonService.class).compare(Path.Type.file, test.attributes(), targetAttr)); new DriveDeleteFeature(session, fileid).delete(Arrays.asList(target, folder), new DisabledLoginCallback(), new Delete.DisabledCallback()); } @Test public void testMoveToExistingFile() throws Exception { final DriveFileIdProvider fileid = new DriveFileIdProvider(session); - final Path folder = new DriveDirectoryFeature(session, fileid).mkdir(new Path(DriveHomeFinderService.MYDRIVE_FOLDER, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory)), new TransferStatus()); - final Path test = new DriveTouchFeature(session, fileid).touch(new Path(folder, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)), new TransferStatus()); + final Path folder = new DriveDirectoryFeature(session, fileid).mkdir(new DriveWriteFeature(session, fileid), new Path(DriveHomeFinderService.MYDRIVE_FOLDER, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory)), new TransferStatus()); + final Path test = new DriveTouchFeature(session, fileid).touch(new DriveWriteFeature(session, fileid), new Path(folder, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)), new TransferStatus()); final String firstVersion = test.attributes().getFileId(); - final Path temp = new DriveTouchFeature(session, fileid).touch(new Path(folder, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)), new TransferStatus()); + final Path temp = new DriveTouchFeature(session, fileid).touch(new DriveWriteFeature(session, fileid), new Path(folder, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)), new TransferStatus()); final Path target = new DriveMoveFeature(session, fileid).move(temp, test, new TransferStatus().setExists(true), new Delete.DisabledCallback(), new DisabledConnectionCallback()); assertEquals(test.attributes().getFileId(), target.attributes().getFileId()); final Find find = new DefaultFindFeature(session); final AttributedList files = new DriveListService(session, fileid).list(folder, new DisabledListProgressListener()); // Replaced file is trashed assertEquals(2, files.size()); - assertTrue(files.get(new Path(test).withAttributes(new PathAttributes().setFileId(firstVersion))).attributes().isTrashed()); + assertTrue(files.get(new Path(test).withAttributes(new DefaultPathAttributes().setFileId(firstVersion))).attributes().isTrashed()); assertFalse(files.get(target).attributes().isHidden()); assertTrue(find.find(target)); new DriveDeleteFeature(session, fileid).delete(Collections.singletonList(folder), new DisabledLoginCallback(), new Delete.DisabledCallback()); @@ -94,8 +100,8 @@ public class DriveMoveFeatureTest extends AbstractDriveTest { final Path sourceDirectory = new Path(DriveHomeFinderService.MYDRIVE_FOLDER, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory)); final Path targetDirectory = new Path(DriveHomeFinderService.MYDRIVE_FOLDER, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory)); final DriveFileIdProvider fileid = new DriveFileIdProvider(session); - new DriveDirectoryFeature(session, fileid).mkdir(sourceDirectory, new TransferStatus()); - final Path sourceFile = new DriveTouchFeature(session, fileid).touch(new Path(sourceDirectory, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)), new TransferStatus()); + new DriveDirectoryFeature(session, fileid).mkdir(new DriveWriteFeature(session, fileid), sourceDirectory, new TransferStatus()); + final Path sourceFile = new DriveTouchFeature(session, fileid).touch(new DriveWriteFeature(session, fileid), new Path(sourceDirectory, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)), new TransferStatus()); final Path targetFile = new Path(targetDirectory, sourceFile.getName(), EnumSet.of(Path.Type.file)); new DriveMoveFeature(session, fileid).move(sourceDirectory, targetDirectory, new TransferStatus(), new Delete.DisabledCallback(), new DisabledConnectionCallback()); final Find find = new DefaultFindFeature(session); diff --git a/googledrive/src/test/java/ch/cyberduck/core/googledrive/DriveReadFeatureTest.java b/googledrive/src/test/java/ch/cyberduck/core/googledrive/DriveReadFeatureTest.java index b30aa0a2b5..e3da6555d5 100644 --- a/googledrive/src/test/java/ch/cyberduck/core/googledrive/DriveReadFeatureTest.java +++ b/googledrive/src/test/java/ch/cyberduck/core/googledrive/DriveReadFeatureTest.java @@ -76,8 +76,8 @@ public class DriveReadFeatureTest extends AbstractDriveTest { IOUtils.write(content, out); out.close(); final DriveFileIdProvider fileid = new DriveFileIdProvider(session); - new DriveUploadFeature(session, fileid).upload( - test, local, new BandwidthThrottle(BandwidthThrottle.UNLIMITED), new DisabledProgressListener(), new DisabledStreamListener(), + new DriveUploadFeature(session).upload( + new DriveWriteFeature(session, fileid), test, local, new BandwidthThrottle(BandwidthThrottle.UNLIMITED), new DisabledProgressListener(), new DisabledStreamListener(), new TransferStatus().setLength(content.length), new DisabledConnectionCallback()); final TransferStatus status = new TransferStatus(); @@ -98,7 +98,7 @@ public class DriveReadFeatureTest extends AbstractDriveTest { @Test public void testReadWhitespace() throws Exception { final DriveFileIdProvider fileid = new DriveFileIdProvider(session); - final Path file = new DriveTouchFeature(session, fileid).touch(new Path(DriveHomeFinderService.MYDRIVE_FOLDER, String.format("t %s", new AlphanumericRandomStringService().random()), EnumSet.of(Path.Type.file)), new TransferStatus()); + final Path file = new DriveTouchFeature(session, fileid).touch(new DriveWriteFeature(session, fileid), new Path(DriveHomeFinderService.MYDRIVE_FOLDER, String.format("t %s", new AlphanumericRandomStringService().random()), EnumSet.of(Path.Type.file)), new TransferStatus()); assertEquals(0, new DriveAttributesFinderFeature(session, fileid).find(file).getSize()); final CountingInputStream in = new CountingInputStream(new DriveReadFeature(session, fileid).read(file, new TransferStatus(), new DisabledConnectionCallback())); in.close(); @@ -109,8 +109,8 @@ public class DriveReadFeatureTest extends AbstractDriveTest { @Test public void testReadPath() throws Exception { final DriveFileIdProvider fileid = new DriveFileIdProvider(session); - final Path directory = new DriveDirectoryFeature(session, fileid).mkdir(new Path(DriveHomeFinderService.MYDRIVE_FOLDER, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory)), new TransferStatus()); - final Path file = new DriveTouchFeature(session, fileid).touch(new Path(directory, String.format("t %s", new AlphanumericRandomStringService().random()), EnumSet.of(Path.Type.file)), new TransferStatus()); + final Path directory = new DriveDirectoryFeature(session, fileid).mkdir(new DriveWriteFeature(session, fileid), new Path(DriveHomeFinderService.MYDRIVE_FOLDER, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory)), new TransferStatus()); + final Path file = new DriveTouchFeature(session, fileid).touch(new DriveWriteFeature(session, fileid), new Path(directory, String.format("t %s", new AlphanumericRandomStringService().random()), EnumSet.of(Path.Type.file)), new TransferStatus()); assertEquals(0, new DriveAttributesFinderFeature(session, fileid).find(file).getSize()); final CountingInputStream in = new CountingInputStream(new DriveReadFeature(session, fileid).read(file, new TransferStatus(), new DisabledConnectionCallback())); in.close(); @@ -121,8 +121,8 @@ public class DriveReadFeatureTest extends AbstractDriveTest { @Test public void testReadEmpty() throws Exception { final DriveFileIdProvider fileid = new DriveFileIdProvider(session); - final Path directory = new DriveDirectoryFeature(session, fileid).mkdir(new Path(DriveHomeFinderService.MYDRIVE_FOLDER, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory)), new TransferStatus()); - final Path file = new DriveTouchFeature(session, fileid).touch(new Path(directory, String.format("t %s", new AlphanumericRandomStringService().random()), EnumSet.of(Path.Type.file)), new TransferStatus()); + final Path directory = new DriveDirectoryFeature(session, fileid).mkdir(new DriveWriteFeature(session, fileid), new Path(DriveHomeFinderService.MYDRIVE_FOLDER, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory)), new TransferStatus()); + final Path file = new DriveTouchFeature(session, fileid).touch(new DriveWriteFeature(session, fileid), new Path(directory, String.format("t %s", new AlphanumericRandomStringService().random()), EnumSet.of(Path.Type.file)), new TransferStatus()); assertEquals(0, new DriveAttributesFinderFeature(session, fileid).find(file).getSize()); final CountingInputStream in = new CountingInputStream(new DriveReadFeature(session, fileid).read(file, new TransferStatus(), new DisabledConnectionCallback())); in.close(); @@ -138,7 +138,7 @@ public class DriveReadFeatureTest extends AbstractDriveTest { final TransferStatus writeStatus = new TransferStatus(); writeStatus.setLength(content.length); final Path directory = new DriveDirectoryFeature(session, fileid).mkdir( - new Path(MYDRIVE_FOLDER, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory)), new TransferStatus()); + new DriveWriteFeature(session, fileid), new Path(MYDRIVE_FOLDER, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory)), new TransferStatus()); final Path test = new Path(directory, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)); final DriveWriteFeature writer = new DriveWriteFeature(session, fileid); final HttpResponseOutputStream out = writer.write(test, writeStatus, new DisabledConnectionCallback()); @@ -154,7 +154,7 @@ public class DriveReadFeatureTest extends AbstractDriveTest { public void testReadRevision() throws Exception { final DriveFileIdProvider fileid = new DriveFileIdProvider(session); final Path directory = new DriveDirectoryFeature(session, fileid).mkdir( - new Path(MYDRIVE_FOLDER, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory)), new TransferStatus()); + new DriveWriteFeature(session, fileid), new Path(MYDRIVE_FOLDER, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory)), new TransferStatus()); final Path test = new Path(directory, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)); final byte[] content = RandomUtils.nextBytes(1645); { diff --git a/googledrive/src/test/java/ch/cyberduck/core/googledrive/DriveSearchFeatureTest.java b/googledrive/src/test/java/ch/cyberduck/core/googledrive/DriveSearchFeatureTest.java index e22cc0b5ad..e44e0fd42f 100644 --- a/googledrive/src/test/java/ch/cyberduck/core/googledrive/DriveSearchFeatureTest.java +++ b/googledrive/src/test/java/ch/cyberduck/core/googledrive/DriveSearchFeatureTest.java @@ -44,7 +44,7 @@ public class DriveSearchFeatureTest extends AbstractDriveTest { final String name = new AlphanumericRandomStringService().random(); final Path workdir = DriveHomeFinderService.MYDRIVE_FOLDER; final DriveFileIdProvider fileid = new DriveFileIdProvider(session); - final Path file = new DriveTouchFeature(session, fileid).touch(new Path(workdir, name, EnumSet.of(Path.Type.file)), new TransferStatus()); + final Path file = new DriveTouchFeature(session, fileid).touch(new DriveWriteFeature(session, fileid), new Path(workdir, name, EnumSet.of(Path.Type.file)), new TransferStatus()); final DriveSearchFeature feature = new DriveSearchFeature(session, fileid); assertTrue(feature.search(workdir, new SearchFilter(name), new DisabledListProgressListener()).contains(file)); // Supports prefix matching only @@ -59,15 +59,15 @@ public class DriveSearchFeatureTest extends AbstractDriveTest { public void testSearchFolder() throws Exception { final String name = new AlphanumericRandomStringService().random(); final DriveFileIdProvider fileid = new DriveFileIdProvider(session); - final Path workdir = new DriveDirectoryFeature(session, fileid).mkdir(new Path(DriveHomeFinderService.MYDRIVE_FOLDER, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory)), new TransferStatus()); - final Path file = new DriveTouchFeature(session, fileid).touch(new Path(workdir, name, EnumSet.of(Path.Type.file)), new TransferStatus()); + final Path workdir = new DriveDirectoryFeature(session, fileid).mkdir(new DriveWriteFeature(session, fileid), new Path(DriveHomeFinderService.MYDRIVE_FOLDER, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory)), new TransferStatus()); + final Path file = new DriveTouchFeature(session, fileid).touch(new DriveWriteFeature(session, fileid), new Path(workdir, name, EnumSet.of(Path.Type.file)), new TransferStatus()); final DriveSearchFeature feature = new DriveSearchFeature(session, fileid); assertTrue(feature.search(workdir, new SearchFilter(name), new DisabledListProgressListener()).contains(file)); assertTrue(feature.search(workdir, new SearchFilter(StringUtils.substring(name, 2)), new DisabledListProgressListener()).contains(file)); final AttributedList result = feature.search(workdir, new SearchFilter(StringUtils.substring(name, 0, name.length() - 2)), new DisabledListProgressListener()); assertTrue(result.contains(file)); assertEquals(workdir, result.get(result.indexOf(file)).getParent()); - final Path subdir = new DriveDirectoryFeature(session, fileid).mkdir(new Path(workdir, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory)), new TransferStatus()); + final Path subdir = new DriveDirectoryFeature(session, fileid).mkdir(new DriveWriteFeature(session, fileid), new Path(workdir, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory)), new TransferStatus()); assertFalse(feature.search(subdir, new SearchFilter(name), new DisabledListProgressListener()).contains(file)); new DriveDeleteFeature(session, fileid).delete(Arrays.asList(file, subdir, workdir), new DisabledLoginCallback(), new Delete.DisabledCallback()); } @@ -76,8 +76,8 @@ public class DriveSearchFeatureTest extends AbstractDriveTest { public void testSearchFolderRecursively() throws Exception { final String name = new AlphanumericRandomStringService().random(); final DriveFileIdProvider fileid = new DriveFileIdProvider(session); - final Path workdir = new DriveDirectoryFeature(session, fileid).mkdir(new Path(DriveHomeFinderService.MYDRIVE_FOLDER, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory)), new TransferStatus()); - final Path file = new DriveTouchFeature(session, fileid).touch(new Path(workdir, name, EnumSet.of(Path.Type.file)), new TransferStatus()); + final Path workdir = new DriveDirectoryFeature(session, fileid).mkdir(new DriveWriteFeature(session, fileid), new Path(DriveHomeFinderService.MYDRIVE_FOLDER, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory)), new TransferStatus()); + final Path file = new DriveTouchFeature(session, fileid).touch(new DriveWriteFeature(session, fileid), new Path(workdir, name, EnumSet.of(Path.Type.file)), new TransferStatus()); final DriveSearchFeature feature = new DriveSearchFeature(session, fileid); assertTrue(feature.search(DriveHomeFinderService.MYDRIVE_FOLDER, new SearchFilter(name), new DisabledListProgressListener()).contains(file)); new DriveDeleteFeature(session, fileid).delete(Arrays.asList(file, workdir), new DisabledLoginCallback(), new Delete.DisabledCallback()); diff --git a/googledrive/src/test/java/ch/cyberduck/core/googledrive/DriveSearchListServiceTest.java b/googledrive/src/test/java/ch/cyberduck/core/googledrive/DriveSearchListServiceTest.java index 3ad51f4150..74866db2b5 100644 --- a/googledrive/src/test/java/ch/cyberduck/core/googledrive/DriveSearchListServiceTest.java +++ b/googledrive/src/test/java/ch/cyberduck/core/googledrive/DriveSearchListServiceTest.java @@ -43,7 +43,7 @@ public class DriveSearchListServiceTest extends AbstractDriveTest { @Test public void testQuery() throws Exception { final DriveFileIdProvider fileid = new DriveFileIdProvider(session); - final Path directory = new DriveDirectoryFeature(session, fileid).mkdir(new Path(DriveHomeFinderService.MYDRIVE_FOLDER, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory)), new TransferStatus()); + final Path directory = new DriveDirectoryFeature(session, fileid).mkdir(new DriveWriteFeature(session, fileid), new Path(DriveHomeFinderService.MYDRIVE_FOLDER, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory)), new TransferStatus()); final String name = new AlphanumericRandomStringService().random(); final Drive.Files.Create insert = session.getClient().files().create(new File() .setName(name) @@ -52,7 +52,7 @@ public class DriveSearchListServiceTest extends AbstractDriveTest { execute.setVersion(1L); final Path file = new Path(directory, name, EnumSet.of(Path.Type.file), new DriveAttributesFinderFeature(session, fileid).toAttributes(execute)); { - final Path subdirectory = new DriveDirectoryFeature(session, fileid).mkdir(new Path(directory, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory)), new TransferStatus()); + final Path subdirectory = new DriveDirectoryFeature(session, fileid).mkdir(new DriveWriteFeature(session, fileid), new Path(directory, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory)), new TransferStatus()); assertTrue(new DriveSearchListService(session, fileid, name).list(subdirectory, new DisabledListProgressListener()).isEmpty()); new DriveDeleteFeature(session, fileid).delete(Collections.singletonList(subdirectory), new DisabledPasswordCallback(), new Delete.DisabledCallback()); } diff --git a/googledrive/src/test/java/ch/cyberduck/core/googledrive/DriveSharingUrlProviderTest.java b/googledrive/src/test/java/ch/cyberduck/core/googledrive/DriveSharingUrlProviderTest.java index 0599dcd64a..875e4d961a 100644 --- a/googledrive/src/test/java/ch/cyberduck/core/googledrive/DriveSharingUrlProviderTest.java +++ b/googledrive/src/test/java/ch/cyberduck/core/googledrive/DriveSharingUrlProviderTest.java @@ -40,7 +40,7 @@ public class DriveSharingUrlProviderTest extends AbstractDriveTest { public void toDownloadUrl() throws Exception { final DriveFileIdProvider fileid = new DriveFileIdProvider(session); final Path test = new DriveTouchFeature(session, fileid).touch( - new Path(DriveHomeFinderService.MYDRIVE_FOLDER, UUID.randomUUID().toString(), EnumSet.of(Path.Type.file)), new TransferStatus().setMime("x-application/cyberduck")); + new DriveWriteFeature(session, fileid), new Path(DriveHomeFinderService.MYDRIVE_FOLDER, UUID.randomUUID().toString(), EnumSet.of(Path.Type.file)), new TransferStatus().setMime("x-application/cyberduck")); final DriveSharingUrlProvider provider = new DriveSharingUrlProvider(session, fileid); // Set web view link test.setAttributes(new DriveAttributesFinderFeature(session, fileid).find(test)); diff --git a/googledrive/src/test/java/ch/cyberduck/core/googledrive/DriveTimestampFeatureTest.java b/googledrive/src/test/java/ch/cyberduck/core/googledrive/DriveTimestampFeatureTest.java index b3d7c6594b..26927f2dac 100644 --- a/googledrive/src/test/java/ch/cyberduck/core/googledrive/DriveTimestampFeatureTest.java +++ b/googledrive/src/test/java/ch/cyberduck/core/googledrive/DriveTimestampFeatureTest.java @@ -39,7 +39,7 @@ public class DriveTimestampFeatureTest extends AbstractDriveTest { public void testSetTimestamp() throws Exception { final DriveFileIdProvider fileid = new DriveFileIdProvider(session); final Path home = DriveHomeFinderService.MYDRIVE_FOLDER; - final Path test = new DriveTouchFeature(session, fileid).touch(new Path(home, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)), new TransferStatus()); + final Path test = new DriveTouchFeature(session, fileid).touch(new DriveWriteFeature(session, fileid), new Path(home, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)), new TransferStatus()); new DriveMetadataFeature(session, fileid).setMetadata(test, Collections.singletonMap("test", "t")); final long modified = System.currentTimeMillis(); final TransferStatus status = new TransferStatus().setModified(modified); @@ -57,7 +57,7 @@ public class DriveTimestampFeatureTest extends AbstractDriveTest { final DriveFileIdProvider fileid = new DriveFileIdProvider(session); final Path home = DriveHomeFinderService.MYDRIVE_FOLDER; final Path test = new DriveDirectoryFeature(session, fileid).mkdir( - new Path(home, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory)), new TransferStatus()); + new DriveWriteFeature(session, fileid), new Path(home, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory)), new TransferStatus()); final long modified = System.currentTimeMillis(); new DriveTimestampFeature(session, fileid).setTimestamp(test, modified); assertEquals(modified, new DefaultAttributesFinderFeature(session).find(test).getModificationDate()); diff --git a/googledrive/src/test/java/ch/cyberduck/core/googledrive/DriveTouchFeatureTest.java b/googledrive/src/test/java/ch/cyberduck/core/googledrive/DriveTouchFeatureTest.java index 663601b9de..ba8f6994f7 100644 --- a/googledrive/src/test/java/ch/cyberduck/core/googledrive/DriveTouchFeatureTest.java +++ b/googledrive/src/test/java/ch/cyberduck/core/googledrive/DriveTouchFeatureTest.java @@ -39,15 +39,15 @@ public class DriveTouchFeatureTest extends AbstractDriveTest { public void testTouch() throws Exception { final DriveFileIdProvider fileid = new DriveFileIdProvider(session); final Path folder = new DriveDirectoryFeature(session, fileid).mkdir( - new Path(DriveHomeFinderService.MYDRIVE_FOLDER, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory)), new TransferStatus()); + new DriveWriteFeature(session, fileid), new Path(DriveHomeFinderService.MYDRIVE_FOLDER, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory)), new TransferStatus()); final Path test = new DriveTouchFeature(session, fileid).touch( - new Path(folder, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)), new TransferStatus().setMime("x-application/cyberduck")); + new DriveWriteFeature(session, fileid), new Path(folder, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)), new TransferStatus().setMime("x-application/cyberduck")); final String id = test.attributes().getFileId(); assertNotNull(id); assertNull(test.attributes().getVersionId()); assertEquals(test.attributes().getFileId(), new DriveAttributesFinderFeature(session, fileid).find(test).getFileId()); - assertThrows(ConflictException.class, () -> new DriveTouchFeature(session, fileid).touch(test, new TransferStatus())); - assertThrows(ConflictException.class, () -> new DriveDirectoryFeature(session, fileid).mkdir(test, new TransferStatus())); + assertThrows(ConflictException.class, () -> new DriveTouchFeature(session, fileid).touch(new DriveWriteFeature(session, fileid), test, new TransferStatus())); + assertThrows(ConflictException.class, () -> new DriveDirectoryFeature(session, fileid).mkdir(new DriveWriteFeature(session, fileid), test, new TransferStatus())); new DriveTrashFeature(session, fileid).delete(Collections.singletonList(test), new DisabledLoginCallback(), new Delete.DisabledCallback()); assertNull(test.attributes().getFileId()); // Trashed diff --git a/googledrive/src/test/java/ch/cyberduck/core/googledrive/DriveTrashFeatureTest.java b/googledrive/src/test/java/ch/cyberduck/core/googledrive/DriveTrashFeatureTest.java index 5dcca308c8..15fbe4c3f8 100644 --- a/googledrive/src/test/java/ch/cyberduck/core/googledrive/DriveTrashFeatureTest.java +++ b/googledrive/src/test/java/ch/cyberduck/core/googledrive/DriveTrashFeatureTest.java @@ -17,6 +17,7 @@ package ch.cyberduck.core.googledrive; import ch.cyberduck.core.AbstractPath; import ch.cyberduck.core.AlphanumericRandomStringService; +import ch.cyberduck.core.DefaultPathAttributes; import ch.cyberduck.core.DisabledListProgressListener; import ch.cyberduck.core.DisabledLoginCallback; import ch.cyberduck.core.DisabledPasswordCallback; @@ -49,11 +50,11 @@ public class DriveTrashFeatureTest extends AbstractDriveTest { @Test public void testTrashFolder() throws Exception { final DriveFileIdProvider fileid = new DriveFileIdProvider(session); - final Path directory = new DriveDirectoryFeature(session, fileid).mkdir(new Path(DriveHomeFinderService.MYDRIVE_FOLDER, + final Path directory = new DriveDirectoryFeature(session, fileid).mkdir(new DriveWriteFeature(session, fileid), new Path(DriveHomeFinderService.MYDRIVE_FOLDER, new AlphanumericRandomStringService().random(), EnumSet.of(AbstractPath.Type.directory)), new TransferStatus()); assertTrue(new DriveFindFeature(session, fileid).find(directory, new DisabledListProgressListener())); final Path file = new Path(directory, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)); - new DriveTouchFeature(session, fileid).touch(file, new TransferStatus()); + new DriveTouchFeature(session, fileid).touch(new DriveWriteFeature(session, fileid), file, new TransferStatus()); assertTrue(new DriveFindFeature(session, fileid).find(file, new DisabledListProgressListener())); new DriveTrashFeature(session, fileid).delete(Collections.singletonList(directory), new DisabledLoginCallback(), new Delete.DisabledCallback()); assertFalse((new DriveFindFeature(session, fileid).find(directory, new DisabledListProgressListener()))); @@ -63,12 +64,12 @@ public class DriveTrashFeatureTest extends AbstractDriveTest { public void testDeleteFromTrash() throws Exception { final DriveFileIdProvider fileid = new DriveFileIdProvider(session); final Path file = new DriveTouchFeature(session, fileid).touch( - new Path(DriveHomeFinderService.MYDRIVE_FOLDER, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)), new TransferStatus()); + new DriveWriteFeature(session, fileid), new Path(DriveHomeFinderService.MYDRIVE_FOLDER, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)), new TransferStatus()); final String fileId = file.attributes().getFileId(); new DriveTrashFeature(session, fileid).delete(Collections.singletonList(file), new DisabledPasswordCallback(), new Delete.DisabledCallback()); assertFalse(new DriveFindFeature(session, fileid).find(file)); - assertTrue(new DriveFindFeature(session, fileid).find(file.withAttributes(new PathAttributes().setFileId(fileId)))); - final PathAttributes attributesInTrash = new DriveAttributesFinderFeature(session, fileid).find(file.withAttributes(new PathAttributes().setFileId(fileId))); + assertTrue(new DriveFindFeature(session, fileid).find(file.withAttributes(new DefaultPathAttributes().setFileId(fileId)))); + final PathAttributes attributesInTrash = new DriveAttributesFinderFeature(session, fileid).find(file.withAttributes(new DefaultPathAttributes().setFileId(fileId))); assertTrue(attributesInTrash.isTrashed()); new DriveTrashFeature(session, fileid).delete(Collections.singletonList(file.withAttributes(attributesInTrash)), new DisabledPasswordCallback(), new Delete.DisabledCallback()); assertFalse(new DriveFindFeature(session, fileid).find(file.withAttributes(attributesInTrash))); diff --git a/googledrive/src/test/java/ch/cyberduck/core/googledrive/DriveUploadFeatureTest.java b/googledrive/src/test/java/ch/cyberduck/core/googledrive/DriveUploadFeatureTest.java index a09ce26adb..93e6e48974 100644 --- a/googledrive/src/test/java/ch/cyberduck/core/googledrive/DriveUploadFeatureTest.java +++ b/googledrive/src/test/java/ch/cyberduck/core/googledrive/DriveUploadFeatureTest.java @@ -56,8 +56,8 @@ public class DriveUploadFeatureTest extends AbstractDriveTest { status.setLength(content.length); final Path test = new Path(DriveHomeFinderService.MYDRIVE_FOLDER, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)); final DriveFileIdProvider fileid = new DriveFileIdProvider(session); - final DriveUploadFeature upload = new DriveUploadFeature(session, fileid); - upload.upload(test, local, new BandwidthThrottle(BandwidthThrottle.UNLIMITED), new DisabledProgressListener(), new DisabledStreamListener(), + final DriveUploadFeature upload = new DriveUploadFeature(session); + upload.upload(new DriveWriteFeature(session, fileid), test, local, new BandwidthThrottle(BandwidthThrottle.UNLIMITED), new DisabledProgressListener(), new DisabledStreamListener(), status, new DisabledConnectionCallback()); test.attributes().setFileId(fileid.getFileId(test)); assertTrue(session.getFeature(Find.class).find(test)); diff --git a/googledrive/src/test/java/ch/cyberduck/core/googledrive/DriveUrlProviderTest.java b/googledrive/src/test/java/ch/cyberduck/core/googledrive/DriveUrlProviderTest.java index a4cda04ba9..29121f47f7 100644 --- a/googledrive/src/test/java/ch/cyberduck/core/googledrive/DriveUrlProviderTest.java +++ b/googledrive/src/test/java/ch/cyberduck/core/googledrive/DriveUrlProviderTest.java @@ -40,7 +40,7 @@ public class DriveUrlProviderTest extends AbstractDriveTest { final Path test = new Path(DriveHomeFinderService.MYDRIVE_FOLDER, UUID.randomUUID().toString(), EnumSet.of(Path.Type.file)); assertNotNull(provider.toUrl(test)); assertTrue(provider.toUrl(test).isEmpty()); - new DriveTouchFeature(session, new DriveFileIdProvider(session)).touch(test, new TransferStatus()); + new DriveTouchFeature(session, new DriveFileIdProvider(session)).touch(new DriveWriteFeature(session, new DriveFileIdProvider(session)), test, new TransferStatus()); // assertFalse(provider.toDownloadUrl(test).isEmpty()); new DriveDeleteFeature(session, new DriveFileIdProvider(session)).delete(Collections.singletonList(test), new DisabledLoginCallback(), new Delete.DisabledCallback()); } diff --git a/googledrive/src/test/java/ch/cyberduck/core/googledrive/DriveVersioningFeatureTest.java b/googledrive/src/test/java/ch/cyberduck/core/googledrive/DriveVersioningFeatureTest.java index eb9a041497..eedf8b4114 100644 --- a/googledrive/src/test/java/ch/cyberduck/core/googledrive/DriveVersioningFeatureTest.java +++ b/googledrive/src/test/java/ch/cyberduck/core/googledrive/DriveVersioningFeatureTest.java @@ -17,6 +17,7 @@ package ch.cyberduck.core.googledrive; 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; @@ -50,13 +51,13 @@ public class DriveVersioningFeatureTest extends AbstractDriveTest { public void testList() throws Exception { final DriveFileIdProvider fileid = new DriveFileIdProvider(session); final Path room = new DriveDirectoryFeature(session, fileid).mkdir( - new Path(MYDRIVE_FOLDER, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory)), new TransferStatus()); + new DriveWriteFeature(session, fileid), new Path(MYDRIVE_FOLDER, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory)), new TransferStatus()); final DriveAttributesFinderFeature attr = new DriveAttributesFinderFeature(session, fileid); - final Path test = new DriveTouchFeature(session, fileid).touch(new Path(room, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)), new TransferStatus()); + final Path test = new DriveTouchFeature(session, fileid).touch(new DriveWriteFeature(session, fileid), new Path(room, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)), new TransferStatus()); assertEquals(test.attributes().getVersionId(), attr.find(test).getVersionId()); final DriveVersioningFeature feature = new DriveVersioningFeature(session, fileid); assertEquals(0, feature.list(test, new DisabledListProgressListener()).size()); - final PathAttributes initialAttributes = new PathAttributes(test.attributes()); + final PathAttributes initialAttributes = new DefaultPathAttributes(test.attributes()); { final byte[] content = RandomUtils.nextBytes(32769); final TransferStatus status = new TransferStatus(); diff --git a/googledrive/src/test/java/ch/cyberduck/core/googledrive/DriveWriteFeatureTest.java b/googledrive/src/test/java/ch/cyberduck/core/googledrive/DriveWriteFeatureTest.java index 7c8df640be..acccc520ed 100644 --- a/googledrive/src/test/java/ch/cyberduck/core/googledrive/DriveWriteFeatureTest.java +++ b/googledrive/src/test/java/ch/cyberduck/core/googledrive/DriveWriteFeatureTest.java @@ -54,7 +54,9 @@ public class DriveWriteFeatureTest extends AbstractDriveTest { public void testWrite() throws Exception { final DriveFileIdProvider idProvider = new DriveFileIdProvider(session); final Path folder = new DriveDirectoryFeature(session, idProvider).mkdir( - new Path(DriveHomeFinderService.MYDRIVE_FOLDER, UUID.randomUUID().toString(), EnumSet.of(Path.Type.directory)), new TransferStatus()); + new DriveWriteFeature(session, idProvider), new Path(DriveHomeFinderService.MYDRIVE_FOLDER, UUID.randomUUID().toString(), EnumSet.of(Path.Type.directory)), new TransferStatus()); + final PathAttributes folderAttributes = new DriveAttributesFinderFeature(session, idProvider).find(folder); + assertEquals(folderAttributes, folder.attributes()); final Path test = new Path(folder, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)); String fileid; { @@ -83,6 +85,7 @@ public class DriveWriteFeatureTest extends AbstractDriveTest { assertArrayEquals(content, buffer); assertEquals("x-application/cyberduck", session.getClient().files().get(test.attributes().getFileId()).execute().getMimeType()); } + assertEquals(folderAttributes, new DriveAttributesFinderFeature(session, idProvider).find(folder)); { // overwrite final TransferStatus status = new TransferStatus(); @@ -107,7 +110,7 @@ public class DriveWriteFeatureTest extends AbstractDriveTest { public void testWriteSameFilename() throws Exception { final DriveFileIdProvider idProvider = new DriveFileIdProvider(session); final Path folder = new DriveDirectoryFeature(session, idProvider).mkdir( - new Path(DriveHomeFinderService.MYDRIVE_FOLDER, UUID.randomUUID().toString(), EnumSet.of(Path.Type.directory)), new TransferStatus()); + new DriveWriteFeature(session, idProvider), new Path(DriveHomeFinderService.MYDRIVE_FOLDER, UUID.randomUUID().toString(), EnumSet.of(Path.Type.directory)), new TransferStatus()); final Path test = new Path(folder, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)); { final byte[] content = RandomUtils.nextBytes(2048); @@ -179,8 +182,8 @@ public class DriveWriteFeatureTest extends AbstractDriveTest { public void testWritePreviouslyTrashed() throws Exception { final DriveFileIdProvider fileid = new DriveFileIdProvider(session); final Path directory = new DriveDirectoryFeature(session, fileid).mkdir( - new Path(DriveHomeFinderService.MYDRIVE_FOLDER, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory)), new TransferStatus()); - final Path test = new DriveTouchFeature(session, fileid).touch(new Path(directory, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)), new TransferStatus()); + new DriveWriteFeature(session, fileid), new Path(DriveHomeFinderService.MYDRIVE_FOLDER, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory)), new TransferStatus()); + final Path test = new DriveTouchFeature(session, fileid).touch(new DriveWriteFeature(session, fileid), new Path(directory, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)), new TransferStatus()); final String id = test.attributes().getFileId(); assertNotNull(id); new DriveTrashFeature(session, fileid).delete(Collections.singletonList(test), new DisabledPasswordCallback(), new Delete.DisabledCallback()); @@ -192,7 +195,7 @@ public class DriveWriteFeatureTest extends AbstractDriveTest { assertTrue(new DriveAttributesFinderFeature(session, fileid).find(test).isTrashed()); // Files with duplicate flag (trashed) are filtered assertFalse(new DefaultFindFeature(session).find(new Path(test).withAttributes(PathAttributes.EMPTY))); - final Path upload = new DriveTouchFeature(session, fileid).touch(test, new TransferStatus()); + final Path upload = new DriveTouchFeature(session, fileid).touch(new DriveWriteFeature(session, fileid), test, new TransferStatus()); assertEquals(upload.attributes(), new DriveAttributesFinderFeature(session, fileid).find(upload)); new DriveDeleteFeature(session, fileid).delete(Arrays.asList(test, directory), new DisabledPasswordCallback(), new Delete.DisabledCallback()); } diff --git a/googledrive/src/test/java/ch/cyberduck/core/worker/DeleteWorkerTest.java b/googledrive/src/test/java/ch/cyberduck/core/worker/DeleteWorkerTest.java index c31d094aad..29b8f07d68 100644 --- a/googledrive/src/test/java/ch/cyberduck/core/worker/DeleteWorkerTest.java +++ b/googledrive/src/test/java/ch/cyberduck/core/worker/DeleteWorkerTest.java @@ -28,6 +28,7 @@ import ch.cyberduck.core.googledrive.DriveFileIdProvider; import ch.cyberduck.core.googledrive.DriveFindFeature; import ch.cyberduck.core.googledrive.DriveHomeFinderService; import ch.cyberduck.core.googledrive.DriveTouchFeature; +import ch.cyberduck.core.googledrive.DriveWriteFeature; import ch.cyberduck.core.shared.DefaultAttributesFinderFeature; import ch.cyberduck.core.shared.DefaultFindFeature; import ch.cyberduck.core.transfer.TransferStatus; @@ -49,10 +50,10 @@ public class DeleteWorkerTest extends AbstractDriveTest { final Path home = DriveHomeFinderService.MYDRIVE_FOLDER; final DriveFileIdProvider fileid = new DriveFileIdProvider(session); final Path folder = new DriveDirectoryFeature(session, fileid).mkdir( - new Path(home, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory)), new TransferStatus()); + new DriveWriteFeature(session, fileid), new Path(home, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory)), new TransferStatus()); assertTrue(new DriveFindFeature(session, fileid).find(folder)); final Path file = new DriveTouchFeature(session, fileid).touch( - new Path(folder, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)), new TransferStatus()); + new DriveWriteFeature(session, fileid), new Path(folder, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)), new TransferStatus()); assertTrue(new DriveFindFeature(session, fileid).find(file)); final DeleteWorker worker = new DeleteWorker(new DisabledLoginCallback(), Collections.singletonList(folder), new DisabledProgressListener(), true); int hashCode = worker.hashCode(); diff --git a/googledrive/src/test/java/ch/cyberduck/core/worker/ListWorkerTest.java b/googledrive/src/test/java/ch/cyberduck/core/worker/ListWorkerTest.java index 30594be294..6a6e19c178 100644 --- a/googledrive/src/test/java/ch/cyberduck/core/worker/ListWorkerTest.java +++ b/googledrive/src/test/java/ch/cyberduck/core/worker/ListWorkerTest.java @@ -28,6 +28,7 @@ import ch.cyberduck.core.googledrive.DriveDeleteFeature; import ch.cyberduck.core.googledrive.DriveDirectoryFeature; import ch.cyberduck.core.googledrive.DriveFileIdProvider; import ch.cyberduck.core.googledrive.DriveHomeFinderService; +import ch.cyberduck.core.googledrive.DriveWriteFeature; import ch.cyberduck.core.shared.DefaultFindFeature; import ch.cyberduck.core.transfer.TransferStatus; import ch.cyberduck.test.IntegrationTest; @@ -53,9 +54,9 @@ public class ListWorkerTest extends AbstractDriveTest { final String f2 = new AlphanumericRandomStringService().random(); final DriveFileIdProvider fileidProvider = new DriveFileIdProvider(session); final Path parent = new DriveDirectoryFeature(session, fileidProvider).mkdir( - new Path(DriveHomeFinderService.MYDRIVE_FOLDER, f1, EnumSet.of(Path.Type.directory)), new TransferStatus()); + new DriveWriteFeature(session, fileidProvider), new Path(DriveHomeFinderService.MYDRIVE_FOLDER, f1, EnumSet.of(Path.Type.directory)), new TransferStatus()); Path folder = new DriveDirectoryFeature(session, fileidProvider).mkdir( - new Path(parent, f2, EnumSet.of(Path.Type.directory)), new TransferStatus()); + new DriveWriteFeature(session, fileidProvider), new Path(parent, f2, EnumSet.of(Path.Type.directory)), new TransferStatus()); assertTrue(new DefaultFindFeature(session).find(folder)); { // trash folder and recreate it @@ -63,7 +64,7 @@ public class ListWorkerTest extends AbstractDriveTest { final File body = new File(); body.set("trashed", true); session.getClient().files().update(fileid, body).execute(); - folder = new DriveDirectoryFeature(session, fileidProvider).mkdir(folder, new TransferStatus()); + folder = new DriveDirectoryFeature(session, fileidProvider).mkdir(new DriveWriteFeature(session, fileidProvider), folder, new TransferStatus()); final PathCache cache = new PathCache(10); final ListWorker worker = new ListWorker(cache, parent, new CachingListProgressListener(cache, new DisabledListProgressListener())); final AttributedList list = worker.run(session); diff --git a/googledrive/src/test/java/ch/cyberduck/core/worker/MoveWorkerTest.java b/googledrive/src/test/java/ch/cyberduck/core/worker/MoveWorkerTest.java index 47430c1dcc..4094f3bfb8 100644 --- a/googledrive/src/test/java/ch/cyberduck/core/worker/MoveWorkerTest.java +++ b/googledrive/src/test/java/ch/cyberduck/core/worker/MoveWorkerTest.java @@ -27,6 +27,7 @@ import ch.cyberduck.core.googledrive.DriveDirectoryFeature; import ch.cyberduck.core.googledrive.DriveFileIdProvider; import ch.cyberduck.core.googledrive.DriveHomeFinderService; import ch.cyberduck.core.googledrive.DriveTouchFeature; +import ch.cyberduck.core.googledrive.DriveWriteFeature; import ch.cyberduck.core.pool.SessionPool; import ch.cyberduck.core.shared.DefaultFindFeature; import ch.cyberduck.core.transfer.TransferStatus; @@ -50,10 +51,10 @@ public class MoveWorkerTest extends AbstractDriveTest { final Path home = DriveHomeFinderService.MYDRIVE_FOLDER; final DriveFileIdProvider fileid = new DriveFileIdProvider(session); final Path folder = new DriveDirectoryFeature(session, fileid).mkdir( - new Path(home, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory)), new TransferStatus()); + new DriveWriteFeature(session, fileid), new Path(home, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory)), new TransferStatus()); assertTrue(new DefaultFindFeature(session).find(folder)); final Path file = new DriveTouchFeature(session, fileid).touch( - new Path(folder, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)), new TransferStatus()); + new DriveWriteFeature(session, fileid), new Path(folder, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)), new TransferStatus()); assertTrue(new DefaultFindFeature(session).find(file)); // rename file final Path fileRenamed = new Path(folder, "f1", EnumSet.of(Path.Type.file)); diff --git a/googlestorage/pom.xml b/googlestorage/pom.xml index acef0e54dd..c89fe2071f 100644 --- a/googlestorage/pom.xml +++ b/googlestorage/pom.xml @@ -19,11 +19,11 @@ parent ch.cyberduck - 9.2.0-SNAPSHOT + 9.4.0-SNAPSHOT googlestorage - v1-rev20250605-2.0.0 + v1-rev20251118-2.0.0 diff --git a/googlestorage/src/main/java/ch/cyberduck/core/googlestorage/GoogleStorageAccessControlListFeature.java b/googlestorage/src/main/java/ch/cyberduck/core/googlestorage/GoogleStorageAccessControlListFeature.java index 8d8d87d030..5c14142e58 100644 --- a/googlestorage/src/main/java/ch/cyberduck/core/googlestorage/GoogleStorageAccessControlListFeature.java +++ b/googlestorage/src/main/java/ch/cyberduck/core/googlestorage/GoogleStorageAccessControlListFeature.java @@ -318,7 +318,7 @@ public class GoogleStorageAccessControlListFeature implements AclPermission { } @Override - public List getAvailableAclUsers() { + public List getAvailableAclUsers(final List files) { final List users = new ArrayList(Arrays.asList( new Acl.CanonicalUser(), new Acl.GroupUser(Acl.GroupUser.AUTHENTICATED, false) { diff --git a/googlestorage/src/main/java/ch/cyberduck/core/googlestorage/GoogleStorageAttributesFinderFeature.java b/googlestorage/src/main/java/ch/cyberduck/core/googlestorage/GoogleStorageAttributesFinderFeature.java index 0eca396bc6..a48f97a614 100644 --- a/googlestorage/src/main/java/ch/cyberduck/core/googlestorage/GoogleStorageAttributesFinderFeature.java +++ b/googlestorage/src/main/java/ch/cyberduck/core/googlestorage/GoogleStorageAttributesFinderFeature.java @@ -16,6 +16,7 @@ package ch.cyberduck.core.googlestorage; */ import ch.cyberduck.core.CancellingListProgressListener; +import ch.cyberduck.core.DefaultPathAttributes; import ch.cyberduck.core.ListProgressListener; import ch.cyberduck.core.Path; import ch.cyberduck.core.PathAttributes; @@ -142,7 +143,7 @@ public class GoogleStorageAttributesFinderFeature implements AttributesFinder, A } protected PathAttributes toAttributes(final Bucket bucket) { - final PathAttributes attributes = new PathAttributes(); + final PathAttributes attributes = new DefaultPathAttributes(); attributes.setRegion(bucket.getLocation()); attributes.setStorageClass(bucket.getStorageClass()); attributes.setCreationDate(bucket.getTimeCreated().getValue()); @@ -159,7 +160,7 @@ public class GoogleStorageAttributesFinderFeature implements AttributesFinder, A @Override public PathAttributes toAttributes(final StorageObject object) { - final PathAttributes attributes = new PathAttributes(); + final PathAttributes attributes = new DefaultPathAttributes(); if(object.getSize() != null) { attributes.setSize(object.getSize().longValue()); } diff --git a/googlestorage/src/main/java/ch/cyberduck/core/googlestorage/GoogleStorageDirectoryFeature.java b/googlestorage/src/main/java/ch/cyberduck/core/googlestorage/GoogleStorageDirectoryFeature.java index 8c67c2ee5b..2caf0516a5 100644 --- a/googlestorage/src/main/java/ch/cyberduck/core/googlestorage/GoogleStorageDirectoryFeature.java +++ b/googlestorage/src/main/java/ch/cyberduck/core/googlestorage/GoogleStorageDirectoryFeature.java @@ -44,16 +44,13 @@ public class GoogleStorageDirectoryFeature implements Directory { private final PathContainerService containerService; private final GoogleStorageSession session; - private Write writer; - public GoogleStorageDirectoryFeature(final GoogleStorageSession session) { this.session = session; this.containerService = new GoogleStoragePathContainerService(); - this.writer = new GoogleStorageWriteFeature(session); } @Override - public Path mkdir(final Path folder, final TransferStatus status) throws BackgroundException { + public Path mkdir(final Write writer, final Path folder, final TransferStatus status) throws BackgroundException { try { if(containerService.isContainer(folder)) { final Storage.Buckets.Insert request = session.getClient().buckets().insert(session.getHost().getCredentials().getUsername(), @@ -70,7 +67,7 @@ public class GoogleStorageDirectoryFeature implements Directory { final EnumSet type = EnumSet.copyOf(folder.getType()); type.add(Path.Type.placeholder); // Add placeholder object - return new GoogleStorageTouchFeature(session).withWriter(writer).touch(folder.withType(type), + return new GoogleStorageTouchFeature(session).touch(writer, folder.withType(type), status.setMime(MIMETYPE)); } } @@ -96,9 +93,4 @@ public class GoogleStorageDirectoryFeature implements Directory { } } - @Override - public Directory withWriter(final Write writer) { - this.writer = writer; - return this; - } } diff --git a/googlestorage/src/main/java/ch/cyberduck/core/googlestorage/GoogleStorageLocationFeature.java b/googlestorage/src/main/java/ch/cyberduck/core/googlestorage/GoogleStorageLocationFeature.java index d69176c7b0..a9d0eadf9e 100644 --- a/googlestorage/src/main/java/ch/cyberduck/core/googlestorage/GoogleStorageLocationFeature.java +++ b/googlestorage/src/main/java/ch/cyberduck/core/googlestorage/GoogleStorageLocationFeature.java @@ -35,12 +35,12 @@ public class GoogleStorageLocationFeature implements Location { } @Override - public Name getDefault() { + public Name getDefault(final Path file) { return new GoogleStorageRegion(HostPreferencesFactory.get(session.getHost()).getProperty("googlestorage.location")); } @Override - public Set getLocations() { + public Set getLocations(final Path file) { return session.getHost().getProtocol().getRegions(); } diff --git a/googlestorage/src/main/java/ch/cyberduck/core/googlestorage/GoogleStorageMetadataFeature.java b/googlestorage/src/main/java/ch/cyberduck/core/googlestorage/GoogleStorageMetadataFeature.java index 17196ba970..7b78cfad76 100644 --- a/googlestorage/src/main/java/ch/cyberduck/core/googlestorage/GoogleStorageMetadataFeature.java +++ b/googlestorage/src/main/java/ch/cyberduck/core/googlestorage/GoogleStorageMetadataFeature.java @@ -45,7 +45,7 @@ public class GoogleStorageMetadataFeature implements Headers { } @Override - public Map getDefault() { + public Map getDefault(final Path file) { return HostPreferencesFactory.get(session.getHost()).getMap("googlestorage.metadata.default"); } diff --git a/googlestorage/src/main/java/ch/cyberduck/core/googlestorage/GoogleStorageObjectListService.java b/googlestorage/src/main/java/ch/cyberduck/core/googlestorage/GoogleStorageObjectListService.java index 5f6192c152..05016cd5aa 100644 --- a/googlestorage/src/main/java/ch/cyberduck/core/googlestorage/GoogleStorageObjectListService.java +++ b/googlestorage/src/main/java/ch/cyberduck/core/googlestorage/GoogleStorageObjectListService.java @@ -16,6 +16,7 @@ package ch.cyberduck.core.googlestorage; */ import ch.cyberduck.core.AttributedList; +import ch.cyberduck.core.DefaultPathAttributes; import ch.cyberduck.core.ListProgressListener; import ch.cyberduck.core.ListService; import ch.cyberduck.core.Path; @@ -142,7 +143,7 @@ public class GoogleStorageObjectListService implements ListService { if(response.getPrefixes() != null) { final List> folders = new ArrayList<>(); for(String prefix : response.getPrefixes()) { - final String key = StringUtils.chomp(prefix, String.valueOf(Path.DELIMITER)); + final String key = StringUtils.removeEnd(prefix, String.valueOf(Path.DELIMITER)); if(new SimplePathPredicate(PathNormalizer.compose(bucket, key)).test(directory)) { continue; } @@ -151,7 +152,7 @@ public class GoogleStorageObjectListService implements ListService { } else { final Path file; - final PathAttributes attributes = new PathAttributes(); + final PathAttributes attributes = new DefaultPathAttributes(); attributes.setRegion(bucket.attributes().getRegion()); if(null == delimiter) { // When searching for files recursively @@ -206,9 +207,9 @@ public class GoogleStorageObjectListService implements ListService { return pool.execute(new BackgroundExceptionCallable() { @Override public Path call() throws BackgroundException { - final PathAttributes attr = new PathAttributes(); + final PathAttributes attr = new DefaultPathAttributes(); attr.setRegion(bucket.attributes().getRegion()); - final String key = StringUtils.chomp(prefix, String.valueOf(Path.DELIMITER)); + final String key = StringUtils.removeEnd(prefix, String.valueOf(Path.DELIMITER)); try { final Storage.Objects.List list = session.getClient().objects().list(bucket.getName()) .setVersions(true) diff --git a/googlestorage/src/main/java/ch/cyberduck/core/googlestorage/GoogleStorageProtocol.java b/googlestorage/src/main/java/ch/cyberduck/core/googlestorage/GoogleStorageProtocol.java index 65fec9ff42..94353441a9 100644 --- a/googlestorage/src/main/java/ch/cyberduck/core/googlestorage/GoogleStorageProtocol.java +++ b/googlestorage/src/main/java/ch/cyberduck/core/googlestorage/GoogleStorageProtocol.java @@ -24,17 +24,12 @@ import ch.cyberduck.core.PathContainerService; import ch.cyberduck.core.Protocol; import ch.cyberduck.core.Scheme; import ch.cyberduck.core.features.Location; -import ch.cyberduck.core.synchronization.ChainedComparisonService; -import ch.cyberduck.core.synchronization.Comparison; import ch.cyberduck.core.synchronization.ComparisonService; import ch.cyberduck.core.synchronization.DefaultComparisonService; import ch.cyberduck.core.synchronization.ETagComparisonService; -import ch.cyberduck.core.synchronization.SizeComparisonService; -import ch.cyberduck.core.synchronization.TimestampComparisonService; import ch.cyberduck.core.text.DefaultLexicographicOrderComparator; import java.util.Comparator; -import java.util.EnumSet; import java.util.List; import java.util.Set; import java.util.stream.Collectors; @@ -138,10 +133,7 @@ public final class GoogleStorageProtocol extends AbstractProtocol { return (T) new DirectoryDelimiterPathContainerService(); } if(type == ComparisonService.class) { - return (T) new DefaultComparisonService(new ChainedComparisonService(EnumSet.of(Comparison.unknown, Comparison.notequal), - new ETagComparisonService(), - new ChainedComparisonService( - EnumSet.of(Comparison.unknown, Comparison.equal), new TimestampComparisonService(), new SizeComparisonService())), ComparisonService.disabled); + return (T) new DefaultComparisonService(new ETagComparisonService(), ComparisonService.disabled); } return super.getFeature(type); } diff --git a/googlestorage/src/main/java/ch/cyberduck/core/googlestorage/GoogleStorageSearchFeature.java b/googlestorage/src/main/java/ch/cyberduck/core/googlestorage/GoogleStorageSearchFeature.java index d4fb0fb5a7..692b2cfe86 100644 --- a/googlestorage/src/main/java/ch/cyberduck/core/googlestorage/GoogleStorageSearchFeature.java +++ b/googlestorage/src/main/java/ch/cyberduck/core/googlestorage/GoogleStorageSearchFeature.java @@ -66,7 +66,7 @@ public class GoogleStorageSearchFeature implements Search { } @Override - public EnumSet features() { + public EnumSet features(final Path workdir) { return EnumSet.of(Flags.recursive); } } diff --git a/googlestorage/src/main/java/ch/cyberduck/core/googlestorage/GoogleStorageSession.java b/googlestorage/src/main/java/ch/cyberduck/core/googlestorage/GoogleStorageSession.java index c4e37e0f24..15f256188b 100644 --- a/googlestorage/src/main/java/ch/cyberduck/core/googlestorage/GoogleStorageSession.java +++ b/googlestorage/src/main/java/ch/cyberduck/core/googlestorage/GoogleStorageSession.java @@ -15,6 +15,7 @@ package ch.cyberduck.core.googlestorage; * GNU General Public License for more details. */ +import ch.cyberduck.core.Credentials; import ch.cyberduck.core.DefaultIOExceptionMappingService; import ch.cyberduck.core.Host; import ch.cyberduck.core.HostKeyCallback; @@ -28,13 +29,10 @@ import ch.cyberduck.core.exception.BackgroundException; import ch.cyberduck.core.exception.ConnectionCanceledException; 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.http.UserAgentHttpRequestInitializer; import ch.cyberduck.core.oauth.OAuth2ErrorResponseInterceptor; import ch.cyberduck.core.oauth.OAuth2RequestInterceptor; -import ch.cyberduck.core.preferences.HostPreferencesFactory; -import ch.cyberduck.core.preferences.PreferencesReader; import ch.cyberduck.core.proxy.ProxyFinder; import ch.cyberduck.core.ssl.X509KeyManager; import ch.cyberduck.core.ssl.X509TrustManager; @@ -51,9 +49,6 @@ import com.google.api.services.storage.Storage; public class GoogleStorageSession extends HttpSession { - private final PreferencesReader preferences - = HostPreferencesFactory.get(host); - private ApacheHttpTransport transport; private OAuth2RequestInterceptor authorizationService; @@ -71,7 +66,7 @@ public class GoogleStorageSession extends HttpSession { .withRedirectUri(host.getProtocol().getOAuthRedirectUrl()); configuration.addInterceptorLast(authorizationService); configuration.setServiceUnavailableRetryStrategy(new CustomServiceUnavailableRetryStrategy(host, - new ExecutionCountServiceUnavailableRetryStrategy(new OAuth2ErrorResponseInterceptor(host, authorizationService)))); + new OAuth2ErrorResponseInterceptor(host, authorizationService))); transport = new ApacheHttpTransport(configuration.build()); final UseragentProvider ua = new PreferencesUseragentProvider(); return new Storage.Builder(transport, new GsonFactory(), new UserAgentHttpRequestInitializer(ua)) @@ -81,17 +76,21 @@ public class GoogleStorageSession extends HttpSession { @Override public void login(final LoginCallback prompt, final CancelCallback cancel) throws BackgroundException { - authorizationService.validate(); + final Credentials credentials = host.getCredentials(); + credentials.setOauth(authorizationService.validate(credentials.getOauth())); } @Override - protected void logout() throws BackgroundException { + public void disconnect() throws BackgroundException { try { transport.shutdown(); } catch(IOException e) { throw new DefaultIOExceptionMappingService().map(e); } + finally { + super.disconnect(); + } } public HttpClient getHttpClient() { diff --git a/googlestorage/src/main/java/ch/cyberduck/core/googlestorage/GoogleStorageStorageClassFeature.java b/googlestorage/src/main/java/ch/cyberduck/core/googlestorage/GoogleStorageStorageClassFeature.java index 11bddc0634..794343f81f 100644 --- a/googlestorage/src/main/java/ch/cyberduck/core/googlestorage/GoogleStorageStorageClassFeature.java +++ b/googlestorage/src/main/java/ch/cyberduck/core/googlestorage/GoogleStorageStorageClassFeature.java @@ -42,12 +42,12 @@ public class GoogleStorageStorageClassFeature implements Redundancy { } @Override - public String getDefault() { + public String getDefault(final Path file) { return HostPreferencesFactory.get(session.getHost()).getProperty("googlestorage.storage.class"); } @Override - public Set getClasses() { + public Set getClasses(final Path file) { return new LinkedHashSet<>( PreferencesFactory.get().getList("googlestorage.storage.class.options")); } diff --git a/googlestorage/src/main/java/ch/cyberduck/core/googlestorage/GoogleStorageTouchFeature.java b/googlestorage/src/main/java/ch/cyberduck/core/googlestorage/GoogleStorageTouchFeature.java index ca97d7d367..38dd91a90e 100644 --- a/googlestorage/src/main/java/ch/cyberduck/core/googlestorage/GoogleStorageTouchFeature.java +++ b/googlestorage/src/main/java/ch/cyberduck/core/googlestorage/GoogleStorageTouchFeature.java @@ -28,7 +28,7 @@ import com.google.api.services.storage.model.StorageObject; public class GoogleStorageTouchFeature extends DefaultTouchFeature { public GoogleStorageTouchFeature(final GoogleStorageSession session) { - super(new GoogleStorageWriteFeature(session)); + super(session); } @Override diff --git a/googlestorage/src/test/java/ch/cyberduck/core/cryptomator/GoogleStorageListServiceTest.java b/googlestorage/src/test/java/ch/cyberduck/core/cryptomator/GoogleStorageListServiceTest.java index a78b485cdf..2761592831 100644 --- a/googlestorage/src/test/java/ch/cyberduck/core/cryptomator/GoogleStorageListServiceTest.java +++ b/googlestorage/src/test/java/ch/cyberduck/core/cryptomator/GoogleStorageListServiceTest.java @@ -24,6 +24,7 @@ import ch.cyberduck.core.cryptomator.features.CryptoDirectoryV7Feature; import ch.cyberduck.core.cryptomator.features.CryptoListService; import ch.cyberduck.core.cryptomator.features.CryptoTouchFeature; import ch.cyberduck.core.features.Delete; +import ch.cyberduck.core.features.Write; import ch.cyberduck.core.googlestorage.AbstractGoogleStorageTest; import ch.cyberduck.core.googlestorage.GoogleStorageDeleteFeature; import ch.cyberduck.core.googlestorage.GoogleStorageDirectoryFeature; @@ -57,20 +58,19 @@ public class GoogleStorageListServiceTest extends AbstractGoogleStorageTest { final Path vault = cryptomator.create(session, new VaultCredentials("test"), CryptoVault.VAULT_VERSION); session.withRegistry(new DefaultVaultRegistry(new DisabledPasswordCallback(), cryptomator)); assertTrue(new CryptoListService(session, new GoogleStorageObjectListService(session), cryptomator).list(vault).isEmpty()); - final CryptoDirectoryV7Feature mkdir = new CryptoDirectoryV7Feature<>(session, new GoogleStorageDirectoryFeature(session), - new GoogleStorageWriteFeature(session), cryptomator); + final CryptoDirectoryV7Feature mkdir = new CryptoDirectoryV7Feature<>(session, new GoogleStorageDirectoryFeature(session), cryptomator); final Path directory1 = mkdir.mkdir( - new Path(vault, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory)), new TransferStatus()); + cryptomator.getFeature(session, Write.class, new GoogleStorageWriteFeature(session)), new Path(vault, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory)), new TransferStatus()); assertNotNull(new CryptoListService(session, new GoogleStorageObjectListService(session), cryptomator).list(vault) .find(new SimplePathPredicate(directory1))); final CryptoTouchFeature touch = new CryptoTouchFeature<>(session, new GoogleStorageTouchFeature(session), - new GoogleStorageWriteFeature(session), cryptomator); + cryptomator); final Path test = touch.touch( - new Path(directory1, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)), new TransferStatus()); + cryptomator.getFeature(session, Write.class, new GoogleStorageWriteFeature(session)), new Path(directory1, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)), new TransferStatus()); assertNotNull(new CryptoListService(session, new GoogleStorageObjectListService(session), cryptomator).list(directory1) .find(new SimplePathPredicate(test))); final Path directory2 = mkdir.mkdir( - new Path(directory1, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory)), new TransferStatus()); + cryptomator.getFeature(session, Write.class, new GoogleStorageWriteFeature(session)), new Path(directory1, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory)), new TransferStatus()); assertNotNull(new CryptoListService(session, new GoogleStorageObjectListService(session), cryptomator).list(directory1) .find(new SimplePathPredicate(directory2))); cryptomator.getFeature(session, Delete.class, new GoogleStorageDeleteFeature(session)) diff --git a/googlestorage/src/test/java/ch/cyberduck/core/googlestorage/DefaultUploadFeatureTest.java b/googlestorage/src/test/java/ch/cyberduck/core/googlestorage/DefaultUploadFeatureTest.java index 5383d5701a..87b92ff0d0 100644 --- a/googlestorage/src/test/java/ch/cyberduck/core/googlestorage/DefaultUploadFeatureTest.java +++ b/googlestorage/src/test/java/ch/cyberduck/core/googlestorage/DefaultUploadFeatureTest.java @@ -50,7 +50,7 @@ public class DefaultUploadFeatureTest extends AbstractGoogleStorageTest { @Test public void testUpload() throws Exception { - final DefaultUploadFeature m = new DefaultUploadFeature<>(new GoogleStorageWriteFeature(session)); + final DefaultUploadFeature m = new DefaultUploadFeature<>(session); final Path container = new Path("cyberduck-test-eu", EnumSet.of(Path.Type.directory, Path.Type.volume)); final Path test = new Path(container, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)); final Local local = new Local(System.getProperty("java.io.tmpdir"), UUID.randomUUID().toString()); @@ -60,7 +60,7 @@ public class DefaultUploadFeatureTest extends AbstractGoogleStorageTest { out.close(); final TransferStatus status = new TransferStatus(); status.setLength(random.length); - final StorageObject versionId = m.upload(test, local, new BandwidthThrottle(BandwidthThrottle.UNLIMITED), + final StorageObject versionId = m.upload(new GoogleStorageWriteFeature(session), test, local, new BandwidthThrottle(BandwidthThrottle.UNLIMITED), new DisabledProgressListener(), new DisabledStreamListener(), status, new DisabledLoginCallback()); assertTrue(new GoogleStorageFindFeature(session).find(test)); final PathAttributes attributes = new GoogleStorageListService(session).list(container, new DisabledListProgressListener()).find(new SimplePathPredicate(test)).attributes(); diff --git a/googlestorage/src/test/java/ch/cyberduck/core/googlestorage/GoogleStorageAccessControlListFeatureTest.java b/googlestorage/src/test/java/ch/cyberduck/core/googlestorage/GoogleStorageAccessControlListFeatureTest.java index b183066780..f026bb7558 100644 --- a/googlestorage/src/test/java/ch/cyberduck/core/googlestorage/GoogleStorageAccessControlListFeatureTest.java +++ b/googlestorage/src/test/java/ch/cyberduck/core/googlestorage/GoogleStorageAccessControlListFeatureTest.java @@ -41,7 +41,7 @@ public class GoogleStorageAccessControlListFeatureTest extends AbstractGoogleSto public void testWrite() throws Exception { final Path container = new Path("cyberduck-test-eu", EnumSet.of(Path.Type.directory)); final Path test = new Path(container, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)); - new GoogleStorageTouchFeature(session).touch(test, new TransferStatus()); + new GoogleStorageTouchFeature(session).touch(new GoogleStorageWriteFeature(session), test, new TransferStatus()); final GoogleStorageAccessControlListFeature f = new GoogleStorageAccessControlListFeature(session); final Acl acl = f.getPermission(test); acl.addAll(new Acl.GroupUser(Acl.GroupUser.EVERYONE), new Acl.Role(Acl.Role.READ)); @@ -68,7 +68,7 @@ public class GoogleStorageAccessControlListFeatureTest extends AbstractGoogleSto final Acl acl = f.getPermission(container); assertEquals(Acl.EMPTY, acl); assertEquals(Acl.EMPTY, f.getDefault(container)); - final Path test = new GoogleStorageTouchFeature(session).touch(new Path(new Path(container, + final Path test = new GoogleStorageTouchFeature(session).touch(new GoogleStorageWriteFeature(session), new Path(new Path(container, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory)), new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)), new TransferStatus()); assertEquals(Acl.EMPTY, f.getPermission(test)); new GoogleStorageDeleteFeature(session).delete(Collections.singletonList(test), new DisabledLoginCallback(), new Delete.DisabledCallback()); @@ -77,7 +77,7 @@ public class GoogleStorageAccessControlListFeatureTest extends AbstractGoogleSto @Test public void testReadWithDelimiter() throws Exception { final Path container = new Path("cyberduck-test-eu", EnumSet.of(Path.Type.directory)); - final Path test = new GoogleStorageTouchFeature(session).touch(new Path(new Path(container, + final Path test = new GoogleStorageTouchFeature(session).touch(new GoogleStorageWriteFeature(session), new Path(new Path(container, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory)), new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)), new TransferStatus()); final GoogleStorageAccessControlListFeature f = new GoogleStorageAccessControlListFeature(session); assertNotNull(f.getPermission(test)); @@ -87,7 +87,7 @@ public class GoogleStorageAccessControlListFeatureTest extends AbstractGoogleSto @Test public void testReadDirectoryPlaceholder() throws Exception { final Path container = new Path("cyberduck-test-eu", EnumSet.of(Path.Type.directory)); - final Path placeholder = new GoogleStorageDirectoryFeature(session).mkdir(new Path(container, + final Path placeholder = new GoogleStorageDirectoryFeature(session).mkdir(new GoogleStorageWriteFeature(session), new Path(container, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory)), new TransferStatus()); final GoogleStorageAccessControlListFeature f = new GoogleStorageAccessControlListFeature(session); assertNotNull(f.getPermission(placeholder)); @@ -115,10 +115,10 @@ public class GoogleStorageAccessControlListFeatureTest extends AbstractGoogleSto @Test public void testRoles() { final GoogleStorageAccessControlListFeature f = new GoogleStorageAccessControlListFeature(session); - final List users = f.getAvailableAclUsers(); - assertTrue(f.getAvailableAclUsers().stream().filter(user -> user instanceof Acl.CanonicalUser).findAny().isPresent()); - assertTrue(f.getAvailableAclUsers().stream().filter(user -> user instanceof Acl.EmailUser).findAny().isPresent()); - assertTrue(f.getAvailableAclUsers().stream().filter(user -> user instanceof Acl.EmailGroupUser).findAny().isPresent()); - assertTrue(f.getAvailableAclUsers().stream().filter(user -> user instanceof Acl.DomainUser).findAny().isPresent()); + final List users = f.getAvailableAclUsers(Collections.emptyList()); + assertTrue(f.getAvailableAclUsers(Collections.emptyList()).stream().filter(user -> user instanceof Acl.CanonicalUser).findAny().isPresent()); + assertTrue(f.getAvailableAclUsers(Collections.emptyList()).stream().filter(user -> user instanceof Acl.EmailUser).findAny().isPresent()); + assertTrue(f.getAvailableAclUsers(Collections.emptyList()).stream().filter(user -> user instanceof Acl.EmailGroupUser).findAny().isPresent()); + assertTrue(f.getAvailableAclUsers(Collections.emptyList()).stream().filter(user -> user instanceof Acl.DomainUser).findAny().isPresent()); } } diff --git a/googlestorage/src/test/java/ch/cyberduck/core/googlestorage/GoogleStorageAttributesFinderFeatureTest.java b/googlestorage/src/test/java/ch/cyberduck/core/googlestorage/GoogleStorageAttributesFinderFeatureTest.java index ad9ff04cb3..5f82bbcfcd 100644 --- a/googlestorage/src/test/java/ch/cyberduck/core/googlestorage/GoogleStorageAttributesFinderFeatureTest.java +++ b/googlestorage/src/test/java/ch/cyberduck/core/googlestorage/GoogleStorageAttributesFinderFeatureTest.java @@ -18,6 +18,7 @@ package ch.cyberduck.core.googlestorage; 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; @@ -69,7 +70,7 @@ public class GoogleStorageAttributesFinderFeatureTest extends AbstractGoogleStor @Test public void testPreviousVersionReferences() throws Exception { final Path container = new Path("cyberduck-test-eu", EnumSet.of(Path.Type.directory, Path.Type.volume)); - final Path test = new GoogleStorageTouchFeature(session).touch(new Path(container, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)), new TransferStatus()); + final Path test = new GoogleStorageTouchFeature(session).touch(new GoogleStorageWriteFeature(session), new Path(container, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)), new TransferStatus()); final String versionId = new GoogleStorageAttributesFinderFeature(session).find(test).getVersionId(); assertEquals(test.attributes().getVersionId(), versionId); final byte[] content = RandomUtils.nextBytes(512); @@ -80,13 +81,13 @@ public class GoogleStorageAttributesFinderFeatureTest extends AbstractGoogleStor new StreamCopier(status, status).transfer(new ByteArrayInputStream(content), out); out.close(); final Path update = new Path(container, test.getName(), test.getType(), - new PathAttributes().setVersionId(String.valueOf(out.getStatus().getGeneration()))); + new DefaultPathAttributes().setVersionId(String.valueOf(out.getStatus().getGeneration()))); final PathAttributes attributes = new GoogleStorageAttributesFinderFeature(session).find(update); assertFalse(attributes.isDuplicate()); final AttributedList versions = new GoogleStorageVersioningFeature(session).list(update, new DisabledListProgressListener()); assertEquals(1, versions.size()); assertFalse(versions.isEmpty()); - assertEquals(new Path(test).withAttributes(new PathAttributes(test.attributes()).setVersionId(versionId)), versions.get(0)); + assertEquals(new Path(test).withAttributes(new DefaultPathAttributes(test.attributes()).setVersionId(versionId)), versions.get(0)); for(Path version : versions) { assertTrue(version.attributes().isDuplicate()); } @@ -96,7 +97,7 @@ public class GoogleStorageAttributesFinderFeatureTest extends AbstractGoogleStor @Test(expected = NotfoundException.class) public void testDeleted() throws Exception { final Path container = new Path("cyberduck-test-eu", EnumSet.of(Path.Type.directory, Path.Type.volume)); - final Path test = new GoogleStorageTouchFeature(session).touch(new Path(container, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)), new TransferStatus()); + final Path test = new GoogleStorageTouchFeature(session).touch(new GoogleStorageWriteFeature(session), new Path(container, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)), new TransferStatus()); assertNotNull(test.attributes().getVersionId()); assertNotEquals(PathAttributes.EMPTY, new GoogleStorageAttributesFinderFeature(session).find(test)); new GoogleStorageDeleteFeature(session).delete(Collections.singletonList(test), new DisabledPasswordCallback(), new Delete.DisabledCallback()); @@ -112,7 +113,7 @@ public class GoogleStorageAttributesFinderFeatureTest extends AbstractGoogleStor @Test(expected = NotfoundException.class) public void testDeletedWithMarker() throws Exception { final Path container = new Path("cyberduck-test-eu", EnumSet.of(Path.Type.directory, Path.Type.volume)); - final Path test = new GoogleStorageTouchFeature(session).touch(new Path(container, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)), new TransferStatus()); + final Path test = new GoogleStorageTouchFeature(session).touch(new GoogleStorageWriteFeature(session), new Path(container, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)), new TransferStatus()); assertNotNull(test.attributes().getVersionId()); assertNotEquals(PathAttributes.EMPTY, new GoogleStorageAttributesFinderFeature(session).find(test)); // Add delete marker @@ -134,7 +135,7 @@ public class GoogleStorageAttributesFinderFeatureTest extends AbstractGoogleStor assertTrue(new GoogleStorageFindFeature(session).find(container)); final String prefix = new AlphanumericRandomStringService().random(); final Path test = new GoogleStorageTouchFeature(session).touch( - new Path(new Path(container, prefix, EnumSet.of(Path.Type.directory)), + new GoogleStorageWriteFeature(session), new Path(new Path(container, prefix, EnumSet.of(Path.Type.directory)), new AsciiRandomStringService().random(), EnumSet.of(Path.Type.file)), new TransferStatus()); assertNotNull(new GoogleStorageAttributesFinderFeature(session).find(test)); assertNotNull(new GoogleStorageAttributesFinderFeature(session).find(new Path(container, prefix, EnumSet.of(Path.Type.directory)))); diff --git a/googlestorage/src/test/java/ch/cyberduck/core/googlestorage/GoogleStorageCopyFeatureTest.java b/googlestorage/src/test/java/ch/cyberduck/core/googlestorage/GoogleStorageCopyFeatureTest.java index 446ea751f7..ab52f37b82 100644 --- a/googlestorage/src/test/java/ch/cyberduck/core/googlestorage/GoogleStorageCopyFeatureTest.java +++ b/googlestorage/src/test/java/ch/cyberduck/core/googlestorage/GoogleStorageCopyFeatureTest.java @@ -43,7 +43,7 @@ public class GoogleStorageCopyFeatureTest extends AbstractGoogleStorageTest { final Path container = new Path("cyberduck-test-eu", EnumSet.of(Path.Type.directory, Path.Type.volume)); final Path test = new Path(container, new AsciiRandomStringService().random(), EnumSet.of(Path.Type.file)); test.attributes().setSize(0L); - new GoogleStorageTouchFeature(session).touch(test, new TransferStatus().setMime("application/cyberduck").setMetadata(Collections.singletonMap("cyberduck", "set"))); + new GoogleStorageTouchFeature(session).touch(new GoogleStorageWriteFeature(session), test, new TransferStatus().setMime("application/cyberduck").setMetadata(Collections.singletonMap("cyberduck", "set"))); final Path copy = new Path(container, new AsciiRandomStringService().random(), EnumSet.of(Path.Type.file)); final GoogleStorageCopyFeature feature = new GoogleStorageCopyFeature(session); assertTrue(feature.isSupported(test, Optional.of(copy))); @@ -61,7 +61,7 @@ public class GoogleStorageCopyFeatureTest extends AbstractGoogleStorageTest { final Path container = new Path("cyberduck-test-eu", EnumSet.of(Path.Type.directory, Path.Type.volume)); final TransferStatus status = new TransferStatus(); status.setMetadata(Collections.singletonMap("cyberduck", "m")); - final Path test = new GoogleStorageTouchFeature(session).touch(new Path(container, new AsciiRandomStringService().random(), EnumSet.of(Path.Type.file)), status); + final Path test = new GoogleStorageTouchFeature(session).touch(new GoogleStorageWriteFeature(session), new Path(container, new AsciiRandomStringService().random(), EnumSet.of(Path.Type.file)), status); assertNotNull(test.attributes().getVersionId()); final Path copy = new GoogleStorageCopyFeature(session).copy(test, new Path(container, new AsciiRandomStringService().random(), EnumSet.of(Path.Type.file)), new TransferStatus(), new DisabledConnectionCallback(), new DisabledStreamListener()); diff --git a/googlestorage/src/test/java/ch/cyberduck/core/googlestorage/GoogleStorageDeleteFeatureTest.java b/googlestorage/src/test/java/ch/cyberduck/core/googlestorage/GoogleStorageDeleteFeatureTest.java index 6f05963a38..a9cc11ed73 100644 --- a/googlestorage/src/test/java/ch/cyberduck/core/googlestorage/GoogleStorageDeleteFeatureTest.java +++ b/googlestorage/src/test/java/ch/cyberduck/core/googlestorage/GoogleStorageDeleteFeatureTest.java @@ -52,7 +52,7 @@ public class GoogleStorageDeleteFeatureTest extends AbstractGoogleStorageTest { @Test public void testDeleteContainer() throws Exception { final Path container = new Path(new AsciiRandomStringService().random().toLowerCase(Locale.ROOT), EnumSet.of(Path.Type.volume, Path.Type.directory)); - new GoogleStorageDirectoryFeature(session).mkdir(container, new TransferStatus().setRegion("us")); + new GoogleStorageDirectoryFeature(session).mkdir(new GoogleStorageWriteFeature(session), container, new TransferStatus().setRegion("us")); assertTrue(new GoogleStorageFindFeature(session).find(container)); new GoogleStorageDeleteFeature(session).delete(Collections.singletonList(container), new DisabledLoginCallback(), new Delete.DisabledCallback()); assertFalse(new GoogleStorageFindFeature(session).find(container)); @@ -61,9 +61,9 @@ public class GoogleStorageDeleteFeatureTest extends AbstractGoogleStorageTest { @Test public void testDeletedWithMarker() throws Exception { final Path container = new Path("cyberduck-test-eu", EnumSet.of(Path.Type.directory, Path.Type.volume)); - final Path directory = new GoogleStorageDirectoryFeature(session).mkdir(new Path(container, + final Path directory = new GoogleStorageDirectoryFeature(session).mkdir(new GoogleStorageWriteFeature(session), new Path(container, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory)), new TransferStatus()); - final Path test = new GoogleStorageTouchFeature(session).touch(new Path(directory, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)), new TransferStatus()); + final Path test = new GoogleStorageTouchFeature(session).touch(new GoogleStorageWriteFeature(session), new Path(directory, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)), new TransferStatus()); assertNotNull(test.attributes().getVersionId()); assertNotEquals(PathAttributes.EMPTY, new GoogleStorageAttributesFinderFeature(session).find(test)); // Add delete marker diff --git a/googlestorage/src/test/java/ch/cyberduck/core/googlestorage/GoogleStorageDirectoryFeatureTest.java b/googlestorage/src/test/java/ch/cyberduck/core/googlestorage/GoogleStorageDirectoryFeatureTest.java index 49f9962071..1bafc6dc00 100644 --- a/googlestorage/src/test/java/ch/cyberduck/core/googlestorage/GoogleStorageDirectoryFeatureTest.java +++ b/googlestorage/src/test/java/ch/cyberduck/core/googlestorage/GoogleStorageDirectoryFeatureTest.java @@ -46,18 +46,18 @@ public class GoogleStorageDirectoryFeatureTest extends AbstractGoogleStorageTest public void testMakeBucket() throws Exception { final Path test = new Path(new DefaultHomeFinderService(session).find(), new AsciiRandomStringService().random().toLowerCase(Locale.ROOT), EnumSet.of(Path.Type.directory, Path.Type.volume)); - new GoogleStorageDirectoryFeature(session).mkdir(test, new TransferStatus().setRegion("us")); + new GoogleStorageDirectoryFeature(session).mkdir(new GoogleStorageWriteFeature(session), test, new TransferStatus().setRegion("us")); assertTrue(new GoogleStorageFindFeature(session).find(test)); - assertThrows(ConflictException.class, () -> new GoogleStorageDirectoryFeature(session).mkdir(test, new TransferStatus())); + assertThrows(ConflictException.class, () -> new GoogleStorageDirectoryFeature(session).mkdir(new GoogleStorageWriteFeature(session), test, new TransferStatus())); new GoogleStorageDeleteFeature(session).delete(Collections.singletonList(test), new DisabledLoginCallback(), new Delete.DisabledCallback()); } @Test public void testDirectoryDeleteWithVersioning() throws Exception { final Path bucket = new Path("cyberduck-test-eu", EnumSet.of(Path.Type.directory, Path.Type.volume)); - final Path parent = new GoogleStorageDirectoryFeature(session).mkdir(new Path(bucket, + final Path parent = new GoogleStorageDirectoryFeature(session).mkdir(new GoogleStorageWriteFeature(session), new Path(bucket, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory)), new TransferStatus()); - final Path test = new GoogleStorageDirectoryFeature(session).mkdir(new Path(parent, + final Path test = new GoogleStorageDirectoryFeature(session).mkdir(new GoogleStorageWriteFeature(session), new Path(parent, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory)), new TransferStatus()); assertNotNull(test.attributes().getVersionId()); // Only placeholder is found in list output with no version id set @@ -78,7 +78,7 @@ public class GoogleStorageDirectoryFeatureTest extends AbstractGoogleStorageTest @Test public void testDirectoryWhitespace() throws Exception { final Path bucket = new Path("cyberduck-test-eu", EnumSet.of(Path.Type.directory, Path.Type.volume)); - final Path test = new GoogleStorageDirectoryFeature(session).mkdir(new Path(bucket, + final Path test = new GoogleStorageDirectoryFeature(session).mkdir(new GoogleStorageWriteFeature(session), new Path(bucket, String.format("%s %s", new AlphanumericRandomStringService().random(), new AlphanumericRandomStringService().random()), EnumSet.of(Path.Type.directory)), new TransferStatus()); assertTrue(new GoogleStorageFindFeature(session).find(test)); assertTrue(new DefaultFindFeature(session).find(test)); @@ -88,7 +88,7 @@ public class GoogleStorageDirectoryFeatureTest extends AbstractGoogleStorageTest @Test public void testCreatePlaceholderVersioningDelete() throws Exception { final Path bucket = new Path("cyberduck-test-eu", EnumSet.of(Path.Type.directory, Path.Type.volume)); - final Path test = new GoogleStorageDirectoryFeature(session).mkdir(new Path(bucket, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory)), new TransferStatus()); + final Path test = new GoogleStorageDirectoryFeature(session).mkdir(new GoogleStorageWriteFeature(session), new Path(bucket, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory)), new TransferStatus()); assertTrue(test.getType().contains(Path.Type.placeholder)); assertTrue(new GoogleStorageFindFeature(session).find(test)); assertTrue(new GoogleStorageObjectListService(session).list(bucket, new DisabledListProgressListener()).contains(test)); @@ -101,7 +101,7 @@ public class GoogleStorageDirectoryFeatureTest extends AbstractGoogleStorageTest @Test public void testCreatePlaceholderVersioningDeleteWithMarker() throws Exception { final Path bucket = new Path("cyberduck-test-eu", EnumSet.of(Path.Type.directory, Path.Type.volume)); - final Path test = new GoogleStorageDirectoryFeature(session).mkdir(new Path(bucket, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory)), new TransferStatus()); + final Path test = new GoogleStorageDirectoryFeature(session).mkdir(new GoogleStorageWriteFeature(session), new Path(bucket, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory)), new TransferStatus()); assertTrue(test.getType().contains(Path.Type.placeholder)); assertTrue(new GoogleStorageFindFeature(session).find(test)); assertTrue(new GoogleStorageObjectListService(session).list(bucket, new DisabledListProgressListener()).contains(test)); diff --git a/googlestorage/src/test/java/ch/cyberduck/core/googlestorage/GoogleStorageFindFeatureTest.java b/googlestorage/src/test/java/ch/cyberduck/core/googlestorage/GoogleStorageFindFeatureTest.java index fc4302071e..c61f97593f 100644 --- a/googlestorage/src/test/java/ch/cyberduck/core/googlestorage/GoogleStorageFindFeatureTest.java +++ b/googlestorage/src/test/java/ch/cyberduck/core/googlestorage/GoogleStorageFindFeatureTest.java @@ -51,7 +51,7 @@ public class GoogleStorageFindFeatureTest extends AbstractGoogleStorageTest { public void testFindDirectory() throws Exception { final Path container = new Path("cyberduck-test-eu", EnumSet.of(Path.Type.directory, Path.Type.volume)); final Path folder = new GoogleStorageDirectoryFeature(session).mkdir( - new Path(container, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory)), new TransferStatus()); + new GoogleStorageWriteFeature(session), new Path(container, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory)), new TransferStatus()); assertTrue(new GoogleStorageFindFeature(session).find(folder)); assertFalse(new GoogleStorageFindFeature(session).find(new Path(folder.getAbsolute(), EnumSet.of(Path.Type.file)))); new GoogleStorageDeleteFeature(session).delete(Collections.singletonList(folder), new DisabledLoginCallback(), new Delete.DisabledCallback()); @@ -61,7 +61,7 @@ public class GoogleStorageFindFeatureTest extends AbstractGoogleStorageTest { public void testFindFile() throws Exception { final Path container = new Path("cyberduck-test-eu", EnumSet.of(Path.Type.directory, Path.Type.volume)); final Path file = new Path(container, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)); - new GoogleStorageTouchFeature(session).touch(file, new TransferStatus()); + new GoogleStorageTouchFeature(session).touch(new GoogleStorageWriteFeature(session), file, new TransferStatus()); assertTrue(new GoogleStorageFindFeature(session).find(file)); assertFalse(new GoogleStorageFindFeature(session).find(new Path(file.getAbsolute(), EnumSet.of(Path.Type.directory)))); new GoogleStorageDeleteFeature(session).delete(Collections.singletonList(file), new DisabledLoginCallback(), new Delete.DisabledCallback()); @@ -70,7 +70,7 @@ public class GoogleStorageFindFeatureTest extends AbstractGoogleStorageTest { @Test public void testDeleted() throws Exception { final Path container = new Path("cyberduck-test-eu", EnumSet.of(Path.Type.directory, Path.Type.volume)); - final Path test = new GoogleStorageTouchFeature(session).touch(new Path(container, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)), new TransferStatus()); + final Path test = new GoogleStorageTouchFeature(session).touch(new GoogleStorageWriteFeature(session), new Path(container, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)), new TransferStatus()); assertNotNull(test.attributes().getVersionId()); assertTrue(new GoogleStorageFindFeature(session).find(test)); new GoogleStorageDeleteFeature(session).delete(Collections.singletonList(test), new DisabledPasswordCallback(), new Delete.DisabledCallback()); @@ -83,7 +83,7 @@ public class GoogleStorageFindFeatureTest extends AbstractGoogleStorageTest { assertTrue(new GoogleStorageFindFeature(session).find(container)); final String prefix = new AlphanumericRandomStringService().random(); final Path test = new GoogleStorageTouchFeature(session).touch( - new Path(new Path(container, prefix, EnumSet.of(Path.Type.directory)), + new GoogleStorageWriteFeature(session), new Path(new Path(container, prefix, EnumSet.of(Path.Type.directory)), new AsciiRandomStringService().random(), EnumSet.of(Path.Type.file)), new TransferStatus()); assertTrue(new GoogleStorageFindFeature(session).find(test)); assertFalse(new GoogleStorageFindFeature(session).find(new Path(test.getAbsolute(), EnumSet.of(Path.Type.directory)))); diff --git a/googlestorage/src/test/java/ch/cyberduck/core/googlestorage/GoogleStorageLifecycleFeatureTest.java b/googlestorage/src/test/java/ch/cyberduck/core/googlestorage/GoogleStorageLifecycleFeatureTest.java index d3f7f88f12..7073493846 100644 --- a/googlestorage/src/test/java/ch/cyberduck/core/googlestorage/GoogleStorageLifecycleFeatureTest.java +++ b/googlestorage/src/test/java/ch/cyberduck/core/googlestorage/GoogleStorageLifecycleFeatureTest.java @@ -40,7 +40,7 @@ public class GoogleStorageLifecycleFeatureTest extends AbstractGoogleStorageTest public void testSetConfiguration() throws Exception { final Path test = new Path(new DefaultHomeFinderService(session).find(), new AsciiRandomStringService().random().toLowerCase(Locale.ROOT), EnumSet.of(Path.Type.directory, Path.Type.volume)); - new GoogleStorageDirectoryFeature(session).mkdir(test, new TransferStatus()); + new GoogleStorageDirectoryFeature(session).mkdir(new GoogleStorageWriteFeature(session), test, new TransferStatus()); final GoogleStorageLifecycleFeature feature = new GoogleStorageLifecycleFeature(session); assertEquals(LifecycleConfiguration.empty(), feature.getConfiguration(test)); feature.setConfiguration(test, new LifecycleConfiguration(1, 2)); diff --git a/googlestorage/src/test/java/ch/cyberduck/core/googlestorage/GoogleStorageMetadataFeatureTest.java b/googlestorage/src/test/java/ch/cyberduck/core/googlestorage/GoogleStorageMetadataFeatureTest.java index 32a94a7381..99572279a1 100644 --- a/googlestorage/src/test/java/ch/cyberduck/core/googlestorage/GoogleStorageMetadataFeatureTest.java +++ b/googlestorage/src/test/java/ch/cyberduck/core/googlestorage/GoogleStorageMetadataFeatureTest.java @@ -47,7 +47,7 @@ public class GoogleStorageMetadataFeatureTest extends AbstractGoogleStorageTest final Path bucket = new Path("cyberduck-test-eu", EnumSet.of(Path.Type.directory, Path.Type.volume)); final TransferStatus status = new TransferStatus(); final Path test = new GoogleStorageTouchFeature(session).touch( - new Path(bucket, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)), status.setMime("text/plain")); + new GoogleStorageWriteFeature(session), new Path(bucket, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)), status.setMime("text/plain")); final GoogleStorageMetadataFeature feature = new GoogleStorageMetadataFeature(session); assertTrue(feature.getMetadata(test).isEmpty()); final Map set = Collections.singletonMap("k", "v"); @@ -65,7 +65,7 @@ public class GoogleStorageMetadataFeatureTest extends AbstractGoogleStorageTest @Test public void testSetMetadataFileLeaveOtherFeatures() throws Exception { final Path bucket = new Path("cyberduck-test-eu", EnumSet.of(Path.Type.directory, Path.Type.volume)); - final Path test = new GoogleStorageTouchFeature(session).touch(new Path(bucket, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)), new TransferStatus()); + final Path test = new GoogleStorageTouchFeature(session).touch(new GoogleStorageWriteFeature(session), new Path(bucket, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)), new TransferStatus()); final String v = UUID.randomUUID().toString(); final GoogleStorageStorageClassFeature storage = new GoogleStorageStorageClassFeature(session); storage.setClass(test, "NEARLINE"); diff --git a/googlestorage/src/test/java/ch/cyberduck/core/googlestorage/GoogleStorageMoveFeatureTest.java b/googlestorage/src/test/java/ch/cyberduck/core/googlestorage/GoogleStorageMoveFeatureTest.java index 56f389c08d..e8df5bfc22 100644 --- a/googlestorage/src/test/java/ch/cyberduck/core/googlestorage/GoogleStorageMoveFeatureTest.java +++ b/googlestorage/src/test/java/ch/cyberduck/core/googlestorage/GoogleStorageMoveFeatureTest.java @@ -46,7 +46,7 @@ public class GoogleStorageMoveFeatureTest extends AbstractGoogleStorageTest { public void testMove() throws Exception { final Path bucket = new Path("cyberduck-test-eu", EnumSet.of(Path.Type.directory, Path.Type.volume)); final Path test = new GoogleStorageTouchFeature(session).touch( - new Path(bucket, new AsciiRandomStringService().random(), EnumSet.of(Path.Type.file)), new TransferStatus().setMetadata(Collections.singletonMap("cyberduck", "set"))); + new GoogleStorageWriteFeature(session), new Path(bucket, new AsciiRandomStringService().random(), EnumSet.of(Path.Type.file)), new TransferStatus().setMetadata(Collections.singletonMap("cyberduck", "set"))); assertTrue(new GoogleStorageFindFeature(session).find(test)); assertFalse(new GoogleStorageMetadataFeature(session).getMetadata(test).isEmpty()); final Path renamed = new Path(bucket, new AsciiRandomStringService().random(), EnumSet.of(Path.Type.file)); @@ -69,7 +69,7 @@ public class GoogleStorageMoveFeatureTest extends AbstractGoogleStorageTest { final Path container = new Path("cyberduck-test-eu", EnumSet.of(Path.Type.directory, Path.Type.volume)); final Path placeholder = new Path(container, new AsciiRandomStringService().random(), EnumSet.of(Path.Type.directory)); final Path test = new Path(placeholder, new AsciiRandomStringService().random(), EnumSet.of(Path.Type.file)); - new GoogleStorageTouchFeature(session).touch(test, new TransferStatus()); + new GoogleStorageTouchFeature(session).touch(new GoogleStorageWriteFeature(session), test, new TransferStatus()); final Path renamed = new Path(placeholder, new AsciiRandomStringService().random(), EnumSet.of(Path.Type.file)); new GoogleStorageMoveFeature(session).move(test, renamed, new TransferStatus(), new Delete.DisabledCallback(), new DisabledConnectionCallback()); assertFalse(new GoogleStorageFindFeature(session).find(test)); @@ -92,7 +92,7 @@ public class GoogleStorageMoveFeatureTest extends AbstractGoogleStorageTest { final GoogleStorageTouchFeature touch = new GoogleStorageTouchFeature(session); final TransferStatus status = new TransferStatus(); status.setEncryption(new Encryption.Algorithm("AES256", null)); - touch.touch(test, status); + touch.touch(new GoogleStorageWriteFeature(session), test, status); assertTrue(new GoogleStorageFindFeature(session).find(test)); final Path renamed = new Path(container, new AsciiRandomStringService().random(), EnumSet.of(Path.Type.file)); new GoogleStorageMoveFeature(session).move(test, renamed, new TransferStatus(), new Delete.DisabledCallback(), new DisabledConnectionCallback()); diff --git a/googlestorage/src/test/java/ch/cyberduck/core/googlestorage/GoogleStorageObjectListServiceTest.java b/googlestorage/src/test/java/ch/cyberduck/core/googlestorage/GoogleStorageObjectListServiceTest.java index 47d3ec86d8..55e8500063 100644 --- a/googlestorage/src/test/java/ch/cyberduck/core/googlestorage/GoogleStorageObjectListServiceTest.java +++ b/googlestorage/src/test/java/ch/cyberduck/core/googlestorage/GoogleStorageObjectListServiceTest.java @@ -48,7 +48,6 @@ public class GoogleStorageObjectListServiceTest extends AbstractGoogleStorageTes for(Path p : list) { assertSame(container, p.getParent()); if(p.isFile()) { - assertNotEquals(-1L, p.attributes().getModificationDate()); assertNotEquals(-1L, p.attributes().getSize()); assertNotNull(p.attributes().getETag()); } @@ -59,17 +58,17 @@ public class GoogleStorageObjectListServiceTest extends AbstractGoogleStorageTes public void testListLexicographicSortOrderAssumption() throws Exception { final Path container = new Path("cyberduck-test-eu", EnumSet.of(Path.Type.directory, Path.Type.volume)); final Path directory = new GoogleStorageDirectoryFeature(session).mkdir( - new Path(container, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory)), new TransferStatus()); + new GoogleStorageWriteFeature(session), new Path(container, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory)), new TransferStatus()); assertTrue(new GoogleStorageObjectListService(session).list(directory, new DisabledListProgressListener()).isEmpty()); final List files = new ArrayList<>(Arrays.asList( "Z", "aa", "0a", "a", "AAA", "B", "~$a", ".c" )); for(String f : files) { - new GoogleStorageTouchFeature(session).touch(new Path(directory, f, EnumSet.of(Path.Type.file)), new TransferStatus()); + new GoogleStorageTouchFeature(session).touch(new GoogleStorageWriteFeature(session), new Path(directory, f, EnumSet.of(Path.Type.file)), new TransferStatus()); } final List folders = Arrays.asList("b", "BB"); for(String f : folders) { - new GoogleStorageDirectoryFeature(session).mkdir(new Path(directory, f, EnumSet.of(Path.Type.directory)), new TransferStatus()); + new GoogleStorageDirectoryFeature(session).mkdir(new GoogleStorageWriteFeature(session), new Path(directory, f, EnumSet.of(Path.Type.directory)), new TransferStatus()); } files.addAll(folders); files.sort(session.getHost().getProtocol().getListComparator()); @@ -96,7 +95,7 @@ public class GoogleStorageObjectListServiceTest extends AbstractGoogleStorageTes final Path container = new Path("cyberduck-test-eu", EnumSet.of(Path.Type.directory, Path.Type.volume)); container.attributes().setRegion("us-east-1"); final Path placeholder = new GoogleStorageTouchFeature(session).touch( - new Path(container, String.format("^<%%%s", new AlphanumericRandomStringService().random()), EnumSet.of(Path.Type.file)), new TransferStatus()); + new GoogleStorageWriteFeature(session), new Path(container, String.format("^<%%%s", new AlphanumericRandomStringService().random()), EnumSet.of(Path.Type.file)), new TransferStatus()); assertTrue(new GoogleStorageObjectListService(session).list(container, new DisabledListProgressListener()).contains(placeholder)); new GoogleStorageDeleteFeature(session).delete(Collections.singletonList(placeholder), new DisabledLoginCallback(), new Delete.DisabledCallback()); } @@ -106,7 +105,7 @@ public class GoogleStorageObjectListServiceTest extends AbstractGoogleStorageTes final Path container = new Path("cyberduck-test-eu", EnumSet.of(Path.Type.directory, Path.Type.volume)); container.attributes().setRegion("us-east-1"); final Path placeholder = new GoogleStorageDirectoryFeature(session).mkdir( - new Path(container, String.format("%s +", new AlphanumericRandomStringService().random()), EnumSet.of(Path.Type.directory)), new TransferStatus()); + new GoogleStorageWriteFeature(session), new Path(container, String.format("%s +", new AlphanumericRandomStringService().random()), EnumSet.of(Path.Type.directory)), new TransferStatus()); assertTrue(new GoogleStorageObjectListService(session).list(container, new DisabledListProgressListener()).contains(placeholder)); new GoogleStorageDeleteFeature(session).delete(Collections.singletonList(placeholder), new DisabledLoginCallback(), new Delete.DisabledCallback()); } @@ -116,7 +115,7 @@ public class GoogleStorageObjectListServiceTest extends AbstractGoogleStorageTes final Path container = new Path("cyberduck-test-eu", EnumSet.of(Path.Type.directory, Path.Type.volume)); container.attributes().setRegion("us-east-1"); final Path placeholder = new GoogleStorageDirectoryFeature(session).mkdir( - new Path(container, String.format("%s +", new AlphanumericRandomStringService().random()), EnumSet.of(Path.Type.directory)), new TransferStatus()); + new GoogleStorageWriteFeature(session), new Path(container, String.format("%s +", new AlphanumericRandomStringService().random()), EnumSet.of(Path.Type.directory)), new TransferStatus()); assertTrue(new GoogleStorageObjectListService(session).list(container, new DisabledListProgressListener(), String.valueOf(Path.DELIMITER), HostPreferencesFactory.get(session.getHost()).getInteger("googlestorage.listing.chunksize"), VersioningConfiguration.empty()).contains(placeholder)); new GoogleStorageDeleteFeature(session).delete(Collections.singletonList(placeholder), new DisabledLoginCallback(), new Delete.DisabledCallback()); @@ -127,7 +126,7 @@ public class GoogleStorageObjectListServiceTest extends AbstractGoogleStorageTes final Path container = new Path("cyberduck-test-eu", EnumSet.of(Path.Type.directory, Path.Type.volume)); container.attributes().setRegion("us-east-1"); final Path placeholder = new GoogleStorageTouchFeature(session).touch( - new Path(container, String.format("test-\u001F-%s", new AlphanumericRandomStringService().random()), EnumSet.of(Path.Type.file)), new TransferStatus()); + new GoogleStorageWriteFeature(session), new Path(container, String.format("test-\u001F-%s", new AlphanumericRandomStringService().random()), EnumSet.of(Path.Type.file)), new TransferStatus()); assertTrue(new GoogleStorageObjectListService(session).list(container, new DisabledListProgressListener()).contains(placeholder)); new GoogleStorageDeleteFeature(session).delete(Collections.singletonList(placeholder), new DisabledLoginCallback(), new Delete.DisabledCallback()); } @@ -137,7 +136,7 @@ public class GoogleStorageObjectListServiceTest extends AbstractGoogleStorageTes final Path container = new Path("cyberduck-test-eu", EnumSet.of(Path.Type.directory, Path.Type.volume)); container.attributes().setRegion("us-east-1"); final Path placeholder = new GoogleStorageTouchFeature(session).touch( - new Path(container, String.format("test+%s", new AlphanumericRandomStringService().random()), EnumSet.of(Path.Type.file)), new TransferStatus()); + new GoogleStorageWriteFeature(session), new Path(container, String.format("test+%s", new AlphanumericRandomStringService().random()), EnumSet.of(Path.Type.file)), new TransferStatus()); assertTrue(new GoogleStorageObjectListService(session).list(container, new DisabledListProgressListener()).contains(placeholder)); new GoogleStorageDeleteFeature(session).delete(Collections.singletonList(placeholder), new DisabledLoginCallback(), new Delete.DisabledCallback()); } @@ -147,9 +146,9 @@ public class GoogleStorageObjectListServiceTest extends AbstractGoogleStorageTes final Path container = new Path("cyberduck-test-eu", EnumSet.of(Path.Type.directory, Path.Type.volume)); container.attributes().setRegion("us-east-1"); final Path directory = new GoogleStorageDirectoryFeature(session).mkdir( - new Path(container, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory)), new TransferStatus()); + new GoogleStorageWriteFeature(session), new Path(container, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory)), new TransferStatus()); final Path placeholder = new GoogleStorageDirectoryFeature(session).mkdir( - new Path(directory, String.format("test+%s", new AlphanumericRandomStringService().random()), EnumSet.of(Path.Type.directory)), new TransferStatus()); + new GoogleStorageWriteFeature(session), new Path(directory, String.format("test+%s", new AlphanumericRandomStringService().random()), EnumSet.of(Path.Type.directory)), new TransferStatus()); assertTrue(new GoogleStorageObjectListService(session).list(directory, new DisabledListProgressListener()).contains(placeholder)); assertTrue(new GoogleStorageObjectListService(session).list(placeholder, new DisabledListProgressListener()).isEmpty()); new GoogleStorageDeleteFeature(session).delete(Arrays.asList(placeholder, directory), new DisabledLoginCallback(), new Delete.DisabledCallback()); @@ -159,7 +158,7 @@ public class GoogleStorageObjectListServiceTest extends AbstractGoogleStorageTes public void testListPlaceholderDot() throws Exception { final Path container = new Path("cyberduck-test-eu", EnumSet.of(Path.Type.directory, Path.Type.volume)); final Path placeholder = new GoogleStorageDirectoryFeature(session).mkdir( - new Path(container, ".", EnumSet.of(Path.Type.directory)), new TransferStatus()); + new GoogleStorageWriteFeature(session), new Path(container, ".", EnumSet.of(Path.Type.directory)), new TransferStatus()); assertTrue(new GoogleStorageObjectListService(session).list(container, new DisabledListProgressListener()).contains(placeholder)); new GoogleStorageDeleteFeature(session).delete(Collections.singletonList(placeholder), new DisabledLoginCallback(), new Delete.DisabledCallback()); } diff --git a/googlestorage/src/test/java/ch/cyberduck/core/googlestorage/GoogleStorageReadFeatureTest.java b/googlestorage/src/test/java/ch/cyberduck/core/googlestorage/GoogleStorageReadFeatureTest.java index 8782aceeaa..6735711276 100644 --- a/googlestorage/src/test/java/ch/cyberduck/core/googlestorage/GoogleStorageReadFeatureTest.java +++ b/googlestorage/src/test/java/ch/cyberduck/core/googlestorage/GoogleStorageReadFeatureTest.java @@ -17,10 +17,10 @@ package ch.cyberduck.core.googlestorage; import ch.cyberduck.core.AlphanumericRandomStringService; import ch.cyberduck.core.BytecountStreamListener; +import ch.cyberduck.core.DefaultPathAttributes; 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.NotfoundException; import ch.cyberduck.core.features.Delete; import ch.cyberduck.core.http.HttpResponseOutputStream; @@ -63,7 +63,7 @@ public class GoogleStorageReadFeatureTest extends AbstractGoogleStorageTest { public void testReadRangeUnknownLength() throws Exception { final Path container = new Path("cyberduck-test-eu", EnumSet.of(Path.Type.directory, Path.Type.volume)); final Path test = new Path(container, String.format("%s %s", new AlphanumericRandomStringService().random(), new AlphanumericRandomStringService().random()), EnumSet.of(Path.Type.file)); - new GoogleStorageTouchFeature(session).touch(test, new TransferStatus()); + new GoogleStorageTouchFeature(session).touch(new GoogleStorageWriteFeature(session), test, new TransferStatus()); final byte[] content = RandomUtils.nextBytes(1023); final TransferStatus status = new TransferStatus().setLength(content.length); status.setChecksum(new SHA256ChecksumCompute().compute(new ByteArrayInputStream(content), status)); @@ -98,7 +98,7 @@ public class GoogleStorageReadFeatureTest extends AbstractGoogleStorageTest { final InputStream in = new GoogleStorageReadFeature(session).read(file, status, new DisabledConnectionCallback()); assertNotNull(in); final BytecountStreamListener count = new BytecountStreamListener(); - new StreamCopier(status, status).withListener(count).transfer(in, NullOutputStream.NULL_OUTPUT_STREAM); + new StreamCopier(status, status).withListener(count).transfer(in, NullOutputStream.INSTANCE); assertEquals(content.length, count.getRecv()); assertEquals(content.length, status.getLength()); in.close(); @@ -143,7 +143,7 @@ public class GoogleStorageReadFeatureTest extends AbstractGoogleStorageTest { final int length = 47; final byte[] content = RandomUtils.nextBytes(length); final Path container = new Path("cyberduck-test-eu", EnumSet.of(Path.Type.directory, Path.Type.volume)); - final Path directory = new GoogleStorageDirectoryFeature(session).mkdir(new Path(container, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory)), new TransferStatus()); + final Path directory = new GoogleStorageDirectoryFeature(session).mkdir(new GoogleStorageWriteFeature(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)); final TransferStatus status = new TransferStatus().setLength(content.length); status.setChecksum(new SHA256ChecksumCompute().compute(new ByteArrayInputStream(content), status)); @@ -159,8 +159,8 @@ public class GoogleStorageReadFeatureTest extends AbstractGoogleStorageTest { @Test public void testReadEmpty() throws Exception { final Path container = new Path("cyberduck-test-eu", EnumSet.of(Path.Type.directory, Path.Type.volume)); - final Path directory = new GoogleStorageDirectoryFeature(session).mkdir(new Path(container, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory)), new TransferStatus()); - final Path file = new GoogleStorageTouchFeature(session).touch(new Path(directory, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)), new TransferStatus()); + final Path directory = new GoogleStorageDirectoryFeature(session).mkdir(new GoogleStorageWriteFeature(session), new Path(container, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory)), new TransferStatus()); + final Path file = new GoogleStorageTouchFeature(session).touch(new GoogleStorageWriteFeature(session), new Path(directory, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)), new TransferStatus()); assertEquals(0, new GoogleStorageAttributesFinderFeature(session).find(file).getSize()); final CountingInputStream in = new CountingInputStream(new GoogleStorageReadFeature(session).read(file, new TransferStatus(), new DisabledConnectionCallback())); in.close(); @@ -174,13 +174,13 @@ public class GoogleStorageReadFeatureTest extends AbstractGoogleStorageTest { final byte[] content = RandomUtils.nextBytes(length); final Path container = new Path("cyberduck-test-eu", EnumSet.of(Path.Type.directory, Path.Type.volume)); assertTrue(new GoogleStorageVersioningFeature(session).getConfiguration(container).isEnabled()); - final Path file = new GoogleStorageTouchFeature(session).touch(new Path(container, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)), new TransferStatus()); + final Path file = new GoogleStorageTouchFeature(session).touch(new GoogleStorageWriteFeature(session), new Path(container, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)), new TransferStatus()); final String initialVersion = file.attributes().getVersionId(); final TransferStatus status = new TransferStatus().setLength(content.length); status.setChecksum(new SHA256ChecksumCompute().compute(new ByteArrayInputStream(content), status)); final HttpResponseOutputStream out = new GoogleStorageWriteFeature(session).write(file, status, new DisabledConnectionCallback()); new StreamCopier(new TransferStatus(), new TransferStatus()).transfer(new ByteArrayInputStream(content), out); - assertEquals(0L, new GoogleStorageAttributesFinderFeature(session).find(file.withAttributes(new PathAttributes(file.attributes()).setVersionId(initialVersion))).getSize()); + assertEquals(0L, new GoogleStorageAttributesFinderFeature(session).find(file.withAttributes(new DefaultPathAttributes(file.attributes()).setVersionId(initialVersion))).getSize()); // Read previous version status.setLength(0L); final InputStream in = new GoogleStorageReadFeature(session).read(file, status, new DisabledConnectionCallback()); diff --git a/googlestorage/src/test/java/ch/cyberduck/core/googlestorage/GoogleStorageSearchFeatureTest.java b/googlestorage/src/test/java/ch/cyberduck/core/googlestorage/GoogleStorageSearchFeatureTest.java index cb996d718b..622d02ad2c 100644 --- a/googlestorage/src/test/java/ch/cyberduck/core/googlestorage/GoogleStorageSearchFeatureTest.java +++ b/googlestorage/src/test/java/ch/cyberduck/core/googlestorage/GoogleStorageSearchFeatureTest.java @@ -43,7 +43,7 @@ public class GoogleStorageSearchFeatureTest extends AbstractGoogleStorageTest { public void testSearchInBucket() throws Exception { final String name = new AlphanumericRandomStringService().random(); final Path bucket = new Path("cyberduck-test-eu", EnumSet.of(Path.Type.directory, Path.Type.volume)); - final Path file = new GoogleStorageTouchFeature(session).touch(new Path(bucket, name, EnumSet.of(Path.Type.file)), new TransferStatus()); + final Path file = new GoogleStorageTouchFeature(session).touch(new GoogleStorageWriteFeature(session), new Path(bucket, name, EnumSet.of(Path.Type.file)), new TransferStatus()); final GoogleStorageSearchFeature feature = new GoogleStorageSearchFeature(session); 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))); @@ -58,7 +58,7 @@ public class GoogleStorageSearchFeatureTest extends AbstractGoogleStorageTest { public void testSearchInDirectory() throws Exception { final Path bucket = new Path("cyberduck-test-eu", EnumSet.of(Path.Type.directory, Path.Type.volume)); final String name = new AlphanumericRandomStringService().random(); - final Path file = new GoogleStorageTouchFeature(session).touch(new Path(bucket, name, EnumSet.of(Path.Type.file)), new TransferStatus()); + final Path file = new GoogleStorageTouchFeature(session).touch(new GoogleStorageWriteFeature(session), new Path(bucket, name, EnumSet.of(Path.Type.file)), new TransferStatus()); final GoogleStorageSearchFeature feature = new GoogleStorageSearchFeature(session); assertTrue(feature.search(bucket, new SearchFilter(name), new DisabledListProgressListener()).contains(file)); assertTrue(feature.search(bucket, new SearchFilter(StringUtils.substring(name, 2)), new DisabledListProgressListener()).contains(file)); @@ -67,9 +67,9 @@ public class GoogleStorageSearchFeatureTest extends AbstractGoogleStorageTest { assertTrue(result.contains(file)); } assertFalse(feature.search(new Path(bucket, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory)), new SearchFilter(name), new DisabledListProgressListener()).contains(file)); - final Path subdir = new GoogleStorageDirectoryFeature(session).mkdir(new Path(bucket, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory)), new TransferStatus()); + final Path subdir = new GoogleStorageDirectoryFeature(session).mkdir(new GoogleStorageWriteFeature(session), new Path(bucket, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory)), new TransferStatus()); assertFalse(feature.search(subdir, new SearchFilter(name), new DisabledListProgressListener()).contains(file)); - final Path filesubdir = new GoogleStorageTouchFeature(session).touch(new Path(subdir, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)), new TransferStatus()); + final Path filesubdir = new GoogleStorageTouchFeature(session).touch(new GoogleStorageWriteFeature(session), new Path(subdir, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)), new TransferStatus()); { final AttributedList result = feature.search(bucket, new SearchFilter(filesubdir.getName()), new DisabledListProgressListener()); assertNotNull(result.find(new SimplePathPredicate(filesubdir))); diff --git a/googlestorage/src/test/java/ch/cyberduck/core/googlestorage/GoogleStorageSessionTest.java b/googlestorage/src/test/java/ch/cyberduck/core/googlestorage/GoogleStorageSessionTest.java index 790fbccd24..1bf7156d23 100644 --- a/googlestorage/src/test/java/ch/cyberduck/core/googlestorage/GoogleStorageSessionTest.java +++ b/googlestorage/src/test/java/ch/cyberduck/core/googlestorage/GoogleStorageSessionTest.java @@ -75,7 +75,7 @@ public class GoogleStorageSessionTest extends AbstractGoogleStorageTest { return "a"; } if(user.equals("Google Cloud Storage (api-project-408246103372) OAuth2 Refresh Token")) { - return System.getProperties().getProperty("googlestorage.refreshtoken"); + return PROPERTIES.get("googlestorage.refreshtoken"); } return null; } diff --git a/googlestorage/src/test/java/ch/cyberduck/core/googlestorage/GoogleStorageStorageClassFeatureTest.java b/googlestorage/src/test/java/ch/cyberduck/core/googlestorage/GoogleStorageStorageClassFeatureTest.java index c1126b7039..3aa9057099 100644 --- a/googlestorage/src/test/java/ch/cyberduck/core/googlestorage/GoogleStorageStorageClassFeatureTest.java +++ b/googlestorage/src/test/java/ch/cyberduck/core/googlestorage/GoogleStorageStorageClassFeatureTest.java @@ -21,6 +21,7 @@ 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.features.Home; import ch.cyberduck.core.transfer.TransferStatus; import ch.cyberduck.test.IntegrationTest; @@ -47,7 +48,7 @@ public class GoogleStorageStorageClassFeatureTest extends AbstractGoogleStorageT "NEARLINE", "COLDLINE", "ARCHIVE").toArray(), - new GoogleStorageStorageClassFeature(session).getClasses().toArray()); + new GoogleStorageStorageClassFeature(session).getClasses(Home.root()).toArray()); } @Test(expected = NotfoundException.class) @@ -62,7 +63,7 @@ public class GoogleStorageStorageClassFeatureTest extends AbstractGoogleStorageT public void testSetClassBucket() throws Exception { final TransferStatus status = new TransferStatus(); status.setStorageClass("MULTI_REGIONAL"); - final Path test = new GoogleStorageDirectoryFeature(session).mkdir(new Path(new AsciiRandomStringService().random().toLowerCase(Locale.ROOT), EnumSet.of(Path.Type.directory)), + final Path test = new GoogleStorageDirectoryFeature(session).mkdir(new GoogleStorageWriteFeature(session), new Path(new AsciiRandomStringService().random().toLowerCase(Locale.ROOT), EnumSet.of(Path.Type.directory)), status); final GoogleStorageStorageClassFeature feature = new GoogleStorageStorageClassFeature(session); assertEquals("MULTI_REGIONAL", feature.getClass(test)); @@ -79,7 +80,7 @@ public class GoogleStorageStorageClassFeatureTest extends AbstractGoogleStorageT @Test public void testSetClassObject() throws Exception { final Path bucket = new Path("cyberduck-test-eu", EnumSet.of(Path.Type.directory)); - final Path test = new GoogleStorageTouchFeature(session).touch(new Path(bucket, + final Path test = new GoogleStorageTouchFeature(session).touch(new GoogleStorageWriteFeature(session), new Path(bucket, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)), new TransferStatus()); final GoogleStorageStorageClassFeature feature = new GoogleStorageStorageClassFeature(session); assertEquals("STANDARD", feature.getClass(test)); diff --git a/googlestorage/src/test/java/ch/cyberduck/core/googlestorage/GoogleStorageTimestampFeatureTest.java b/googlestorage/src/test/java/ch/cyberduck/core/googlestorage/GoogleStorageTimestampFeatureTest.java index 8dc46784c9..f67981d9ed 100644 --- a/googlestorage/src/test/java/ch/cyberduck/core/googlestorage/GoogleStorageTimestampFeatureTest.java +++ b/googlestorage/src/test/java/ch/cyberduck/core/googlestorage/GoogleStorageTimestampFeatureTest.java @@ -44,7 +44,7 @@ public class GoogleStorageTimestampFeatureTest extends AbstractGoogleStorageTest @Test public void testFindTimesteamp() throws Exception { final Path bucket = new Path("cyberduck-test-eu", EnumSet.of(Path.Type.directory, Path.Type.volume)); - final Path test = new GoogleStorageTouchFeature(session).touch(new Path(bucket, + final Path test = new GoogleStorageTouchFeature(session).touch(new GoogleStorageWriteFeature(session), new Path(bucket, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)), new TransferStatus().setModified(1530305150672L)); assertEquals(1530305150672L, new GoogleStorageAttributesFinderFeature(session).find(test).getModificationDate()); final TransferStatus status = new TransferStatus(); diff --git a/googlestorage/src/test/java/ch/cyberduck/core/googlestorage/GoogleStorageTouchFeatureTest.java b/googlestorage/src/test/java/ch/cyberduck/core/googlestorage/GoogleStorageTouchFeatureTest.java index fd39a69fd4..d0cfb46dc0 100644 --- a/googlestorage/src/test/java/ch/cyberduck/core/googlestorage/GoogleStorageTouchFeatureTest.java +++ b/googlestorage/src/test/java/ch/cyberduck/core/googlestorage/GoogleStorageTouchFeatureTest.java @@ -37,7 +37,7 @@ public class GoogleStorageTouchFeatureTest extends AbstractGoogleStorageTest { public void testTouch() throws Exception { final Path bucket = new Path("cyberduck-test-eu", EnumSet.of(Path.Type.directory, Path.Type.volume)); final Path test = new GoogleStorageTouchFeature(session).touch( - new Path(bucket, String.format("%s %s", new AlphanumericRandomStringService().random(), new AlphanumericRandomStringService().random()), EnumSet.of(Path.Type.file)), new TransferStatus().setMime("text/plain")); + new GoogleStorageWriteFeature(session), new Path(bucket, String.format("%s %s", new AlphanumericRandomStringService().random(), new AlphanumericRandomStringService().random()), EnumSet.of(Path.Type.file)), new TransferStatus().setMime("text/plain")); assertTrue(new GoogleStorageFindFeature(session).find(test)); assertEquals(test.attributes().getVersionId(), new GoogleStorageAttributesFinderFeature(session).find(test).getVersionId()); new GoogleStorageDeleteFeature(session).delete(Collections.singletonList(test), new DisabledLoginCallback(), new Delete.DisabledCallback()); diff --git a/googlestorage/src/test/java/ch/cyberduck/core/googlestorage/GoogleStorageVersioningFeatureTest.java b/googlestorage/src/test/java/ch/cyberduck/core/googlestorage/GoogleStorageVersioningFeatureTest.java index d4af170f95..c629a6c7df 100644 --- a/googlestorage/src/test/java/ch/cyberduck/core/googlestorage/GoogleStorageVersioningFeatureTest.java +++ b/googlestorage/src/test/java/ch/cyberduck/core/googlestorage/GoogleStorageVersioningFeatureTest.java @@ -41,7 +41,7 @@ public class GoogleStorageVersioningFeatureTest extends AbstractGoogleStorageTes @Test public void testSetConfiguration() throws Exception { final Path container = new Path(new AsciiRandomStringService().random().toLowerCase(Locale.ROOT), EnumSet.of(Path.Type.directory, Path.Type.volume)); - new GoogleStorageDirectoryFeature(session).mkdir(container, new TransferStatus()); + new GoogleStorageDirectoryFeature(session).mkdir(new GoogleStorageWriteFeature(session), container, new TransferStatus()); final Versioning feature = new GoogleStorageVersioningFeature(session); feature.setConfiguration(container, new DisabledLoginCallback(), new VersioningConfiguration(true, false)); assertTrue(feature.getConfiguration(container).isEnabled()); diff --git a/googlestorage/src/test/java/ch/cyberduck/core/googlestorage/GoogleStorageWebsiteDistributionConfigurationTest.java b/googlestorage/src/test/java/ch/cyberduck/core/googlestorage/GoogleStorageWebsiteDistributionConfigurationTest.java index ae2f7dca77..4cf1447bfc 100644 --- a/googlestorage/src/test/java/ch/cyberduck/core/googlestorage/GoogleStorageWebsiteDistributionConfigurationTest.java +++ b/googlestorage/src/test/java/ch/cyberduck/core/googlestorage/GoogleStorageWebsiteDistributionConfigurationTest.java @@ -50,7 +50,7 @@ public class GoogleStorageWebsiteDistributionConfigurationTest extends AbstractG public void testWrite() throws Exception { final DistributionConfiguration configuration = new GoogleStorageWebsiteDistributionConfiguration(session); final Path bucket = new Path(new AsciiRandomStringService().random().toLowerCase(Locale.ROOT), EnumSet.of(Path.Type.directory, Path.Type.volume)); - new GoogleStorageDirectoryFeature(session).mkdir(bucket, new TransferStatus()); + new GoogleStorageDirectoryFeature(session).mkdir(new GoogleStorageWriteFeature(session), bucket, new TransferStatus()); configuration.write(bucket, new Distribution(Distribution.WEBSITE, null, true), new DisabledLoginCallback()); final Distribution distribution = configuration.read(bucket, Distribution.WEBSITE, new DisabledLoginCallback()); assertTrue(distribution.isEnabled()); diff --git a/googlestorage/src/test/java/ch/cyberduck/core/worker/CopyWorkerTest.java b/googlestorage/src/test/java/ch/cyberduck/core/worker/CopyWorkerTest.java index 7e0b4c9247..e1b154bad5 100644 --- a/googlestorage/src/test/java/ch/cyberduck/core/worker/CopyWorkerTest.java +++ b/googlestorage/src/test/java/ch/cyberduck/core/worker/CopyWorkerTest.java @@ -25,6 +25,7 @@ import ch.cyberduck.core.googlestorage.AbstractGoogleStorageTest; import ch.cyberduck.core.googlestorage.GoogleStorageDirectoryFeature; import ch.cyberduck.core.googlestorage.GoogleStorageFindFeature; import ch.cyberduck.core.googlestorage.GoogleStorageTouchFeature; +import ch.cyberduck.core.googlestorage.GoogleStorageWriteFeature; import ch.cyberduck.core.pool.SessionPool; import ch.cyberduck.core.transfer.TransferStatus; import ch.cyberduck.test.IntegrationTest; @@ -46,7 +47,7 @@ public class CopyWorkerTest extends AbstractGoogleStorageTest { final Path home = new Path("cyberduck-test-eu", 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 GoogleStorageTouchFeature(session).touch(source, new TransferStatus()); + new GoogleStorageTouchFeature(session).touch(new GoogleStorageWriteFeature(session), source, new TransferStatus()); assertTrue(new GoogleStorageFindFeature(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); @@ -59,11 +60,11 @@ public class CopyWorkerTest extends AbstractGoogleStorageTest { public void testCopyFileToDirectory() throws Exception { final Path home = new Path("cyberduck-test-eu", EnumSet.of(Path.Type.directory, Path.Type.volume)); final Path sourceFile = new Path(home, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)); - new GoogleStorageTouchFeature(session).touch(sourceFile, new TransferStatus()); + new GoogleStorageTouchFeature(session).touch(new GoogleStorageWriteFeature(session), sourceFile, new TransferStatus()); assertTrue(new GoogleStorageFindFeature(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 GoogleStorageDirectoryFeature(session).mkdir(targetFolder, new TransferStatus()); + new GoogleStorageDirectoryFeature(session).mkdir(new GoogleStorageWriteFeature(session), targetFolder, new TransferStatus()); assertTrue(new GoogleStorageFindFeature(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()); @@ -78,8 +79,8 @@ public class CopyWorkerTest extends AbstractGoogleStorageTest { final Path home = new Path("cyberduck-test-eu", 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 GoogleStorageDirectoryFeature(session).mkdir(folder, new TransferStatus()); - new GoogleStorageTouchFeature(session).touch(sourceFile, new TransferStatus()); + new GoogleStorageDirectoryFeature(session).mkdir(new GoogleStorageWriteFeature(session), folder, new TransferStatus()); + new GoogleStorageTouchFeature(session).touch(new GoogleStorageWriteFeature(session), sourceFile, new TransferStatus()); assertTrue(new GoogleStorageFindFeature(session).find(folder)); assertTrue(new GoogleStorageFindFeature(session).find(sourceFile)); // move directory into vault diff --git a/googlestorage/src/test/java/ch/cyberduck/core/worker/DeleteWorkerTest.java b/googlestorage/src/test/java/ch/cyberduck/core/worker/DeleteWorkerTest.java index 8e85aaf854..7647d2fdd8 100644 --- a/googlestorage/src/test/java/ch/cyberduck/core/worker/DeleteWorkerTest.java +++ b/googlestorage/src/test/java/ch/cyberduck/core/worker/DeleteWorkerTest.java @@ -28,6 +28,7 @@ import ch.cyberduck.core.googlestorage.GoogleStorageDeleteFeature; import ch.cyberduck.core.googlestorage.GoogleStorageDirectoryFeature; import ch.cyberduck.core.googlestorage.GoogleStorageFindFeature; import ch.cyberduck.core.googlestorage.GoogleStorageTouchFeature; +import ch.cyberduck.core.googlestorage.GoogleStorageWriteFeature; import ch.cyberduck.core.shared.DefaultFindFeature; import ch.cyberduck.core.transfer.TransferStatus; import ch.cyberduck.test.IntegrationTest; @@ -48,10 +49,10 @@ public class DeleteWorkerTest extends AbstractGoogleStorageTest { public void testDelete() throws Exception { final Path home = new Path("cyberduck-test-eu", EnumSet.of(Path.Type.directory, Path.Type.volume)); final Path folder = new GoogleStorageDirectoryFeature(session).mkdir( - new Path(home, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory)), new TransferStatus()); + new GoogleStorageWriteFeature(session), new Path(home, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory)), new TransferStatus()); assertTrue(new GoogleStorageFindFeature(session).find(folder)); final Path file = new GoogleStorageTouchFeature(session).touch( - new Path(folder, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)), new TransferStatus()); + new GoogleStorageWriteFeature(session), new Path(folder, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)), new TransferStatus()); assertNotNull(file.attributes().getVersionId()); assertTrue(new GoogleStorageFindFeature(session).find(file)); final DeleteWorker worker = new DeleteWorker(new DisabledLoginCallback(), Collections.singletonList(folder), new DisabledProgressListener()); diff --git a/hubic/pom.xml b/hubic/pom.xml index 94a5b27b58..e13ef899de 100644 --- a/hubic/pom.xml +++ b/hubic/pom.xml @@ -19,7 +19,7 @@ parent ch.cyberduck - 9.2.0-SNAPSHOT + 9.4.0-SNAPSHOT hubic jar diff --git a/hubic/src/main/java/ch/cyberduck/core/hubic/HubicProtocol.java b/hubic/src/main/java/ch/cyberduck/core/hubic/HubicProtocol.java index 15c08ac3f5..e837d45575 100644 --- a/hubic/src/main/java/ch/cyberduck/core/hubic/HubicProtocol.java +++ b/hubic/src/main/java/ch/cyberduck/core/hubic/HubicProtocol.java @@ -15,16 +15,14 @@ package ch.cyberduck.core.hubic; * GNU General Public License for more details. */ -import ch.cyberduck.core.AbstractProtocol; import ch.cyberduck.core.LocaleFactory; import ch.cyberduck.core.Protocol; -import ch.cyberduck.core.Scheme; import ch.cyberduck.core.openstack.SwiftProtocol; import com.google.auto.service.AutoService; @AutoService(Protocol.class) -public class HubicProtocol extends AbstractProtocol { +public class HubicProtocol extends SwiftProtocol { @Override public String getIdentifier() { @@ -36,11 +34,6 @@ public class HubicProtocol extends AbstractProtocol { return "hubiC"; } - @Override - public Scheme getScheme() { - return Scheme.https; - } - @Override public Type getType() { return Type.swift; @@ -76,11 +69,6 @@ public class HubicProtocol extends AbstractProtocol { return LocaleFactory.localizedString("Authorization code", "Credentials"); } - @Override - public DirectoryTimestamp getDirectoryTimestamp() { - return DirectoryTimestamp.explicit; - } - @Override public boolean isUsernameConfigurable() { return false; diff --git a/hubic/src/main/java/ch/cyberduck/core/hubic/HubicSession.java b/hubic/src/main/java/ch/cyberduck/core/hubic/HubicSession.java index c93a311f76..efb74860de 100644 --- a/hubic/src/main/java/ch/cyberduck/core/hubic/HubicSession.java +++ b/hubic/src/main/java/ch/cyberduck/core/hubic/HubicSession.java @@ -24,7 +24,6 @@ import ch.cyberduck.core.cdn.DistributionConfiguration; import ch.cyberduck.core.exception.BackgroundException; import ch.cyberduck.core.exception.ConnectionCanceledException; import ch.cyberduck.core.http.CustomServiceUnavailableRetryStrategy; -import ch.cyberduck.core.http.ExecutionCountServiceUnavailableRetryStrategy; import ch.cyberduck.core.oauth.OAuth2ErrorResponseInterceptor; import ch.cyberduck.core.oauth.OAuth2RequestInterceptor; import ch.cyberduck.core.openstack.SwiftExceptionMappingService; @@ -59,13 +58,14 @@ public class HubicSession extends SwiftSession { .withRedirectUri(host.getProtocol().getOAuthRedirectUrl()); configuration.addInterceptorLast(authorizationService); configuration.setServiceUnavailableRetryStrategy(new CustomServiceUnavailableRetryStrategy(host, - new ExecutionCountServiceUnavailableRetryStrategy(new OAuth2ErrorResponseInterceptor(host, authorizationService)))); + new OAuth2ErrorResponseInterceptor(host, authorizationService))); return new Client(configuration.build()); } @Override public void login(final LoginCallback prompt, final CancelCallback cancel) throws BackgroundException { - final Credentials credentials = authorizationService.validate(); + final Credentials credentials = host.getCredentials(); + credentials.setOauth(authorizationService.validate(credentials.getOauth())); try { log.info("Attempt authentication with {}", credentials.getOauth()); client.authenticate(new HubicAuthenticationRequest(credentials.getOauth().getAccessToken()), new HubicAuthenticationResponseHandler()); diff --git a/i18n/pom.xml b/i18n/pom.xml index 7662211bed..2c582beeea 100644 --- a/i18n/pom.xml +++ b/i18n/pom.xml @@ -18,7 +18,7 @@ parent ch.cyberduck - 9.2.0-SNAPSHOT + 9.4.0-SNAPSHOT 4.0.0 jar diff --git a/i18n/src/main/resources/ar.lproj/Bookmark.xib b/i18n/src/main/resources/ar.lproj/Bookmark.xib index 66247561a7..e1036b0f90 100644 --- a/i18n/src/main/resources/ar.lproj/Bookmark.xib +++ b/i18n/src/main/resources/ar.lproj/Bookmark.xib @@ -1,7 +1,7 @@ - + - + @@ -92,7 +92,7 @@ - +

@@ -489,95 +489,95 @@ YnBsaXN0MDDUAQIDBAUGBwpYJHZlcnNpb25ZJGFyY2hpdmVyVCR0b3BYJG9iamVjdHMSAAGGoF8QD05T -S2V5ZWRBcmNoaXZlctEICVRyb290gAGuCwwZGh8UJCkqMTQ3PUBVJG51bGzWDQ4PEBESExQVFhcYVk5T -U2l6ZV5OU1Jlc2l6aW5nTW9kZVYkY2xhc3NcTlNJbWFnZUZsYWdzVk5TUmVwc1dOU0NvbG9ygAIQAIAN -EiDDAACAA4ALVnsxLCAxfdIbDxweWk5TLm9iamVjdHOhHYAEgArSGw8gI6IhIoAFgAaACdMPJSYnKBRf -EBROU1RJRkZSZXByZXNlbnRhdGlvbl8QGU5TSW50ZXJuYWxMYXlvdXREaXJlY3Rpb26ACIAHTxESbE1N -ACoAAAAKAAAAEAEAAAMAAAABAAEAAAEBAAMAAAABAAEAAAECAAMAAAACAAgACAEDAAMAAAABAAEAAAEG -AAMAAAABAAEAAAEKAAMAAAABAAEAAAERAAQAAAABAAAACAESAAMAAAABAAEAAAEVAAMAAAABAAIAAAEW -AAMAAAABAAEAAAEXAAQAAAABAAAAAgEcAAMAAAABAAEAAAEoAAMAAAABAAIAAAFSAAMAAAABAAEAAAFT -AAMAAAACAAEAAYdzAAcAABGcAAAA0AAAAAAAABGcYXBwbAIAAABtbnRyR1JBWVhZWiAH3AAIABcADwAu -AA9hY3NwQVBQTAAAAABub25lAAAAAAAAAAAAAAAAAAAAAAAA9tYAAQAAAADTLWFwcGwAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAVkZXNjAAAAwAAAAHlkc2NtAAABPAAA -CBpjcHJ0AAAJWAAAACN3dHB0AAAJfAAAABRrVFJDAAAJkAAACAxkZXNjAAAAAAAAAB9HZW5lcmljIEdy -YXkgR2FtbWEgMi4yIFByb2ZpbGUAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAbWx1YwAAAAAAAAAfAAAADHNr -U0sAAAAuAAABhGRhREsAAAA6AAABsmNhRVMAAAA4AAAB7HZpVk4AAABAAAACJHB0QlIAAABKAAACZHVr -VUEAAAAsAAACrmZyRlUAAAA+AAAC2mh1SFUAAAA0AAADGHpoVFcAAAAaAAADTGtvS1IAAAAiAAADZm5i -Tk8AAAA6AAADiGNzQ1oAAAAoAAADwmhlSUwAAAAkAAAD6nJvUk8AAAAqAAAEDmRlREUAAABOAAAEOGl0 -SVQAAABOAAAEhnN2U0UAAAA4AAAE1HpoQ04AAAAaAAAFDGphSlAAAAAmAAAFJmVsR1IAAAAqAAAFTHB0 -UE8AAABSAAAFdm5sTkwAAABAAAAFyGVzRVMAAABMAAAGCHRoVEgAAAAyAAAGVHRyVFIAAAAkAAAGhmZp -RkkAAABGAAAGqmhySFIAAAA+AAAG8HBsUEwAAABKAAAHLmFyRUcAAAAsAAAHeHJ1UlUAAAA6AAAHpGVu -VVMAAAA8AAAH3gBWAWEAZQBvAGIAZQBjAG4A4QAgAHMAaQB2AOEAIABnAGEAbQBhACAAMgAsADIARwBl -AG4AZQByAGkAcwBrACAAZwByAOUAIAAyACwAMgAgAGcAYQBtAG0AYQAtAHAAcgBvAGYAaQBsAEcAYQBt -AG0AYQAgAGQAZQAgAGcAcgBpAHMAbwBzACAAZwBlAG4A6AByAGkAYwBhACAAMgAuADIAQx6lAHUAIABo -AOwAbgBoACAATQDgAHUAIAB4AOEAbQAgAEMAaAB1AG4AZwAgAEcAYQBtAG0AYQAgADIALgAyAFAAZQBy -AGYAaQBsACAARwBlAG4A6QByAGkAYwBvACAAZABhACAARwBhAG0AYQAgAGQAZQAgAEMAaQBuAHoAYQBz -ACAAMgAsADIEFwQwBDMEMAQ7BEwEPQQwACAARwByAGEAeQAtBDMEMAQ8BDAAIAAyAC4AMgBQAHIAbwBm -AGkAbAAgAGcA6QBuAOkAcgBpAHEAdQBlACAAZwByAGkAcwAgAGcAYQBtAG0AYQAgADIALAAyAMEAbAB0 -AGEAbADhAG4AbwBzACAAcwB6APwAcgBrAGUAIABnAGEAbQBtAGEAIAAyAC4AMpAadShwcJaOUUlepgAy -AC4AMoJyX2ljz4/wx3y8GAAg1ozAyQAgrBC5yAAgADIALgAyACDVBLhc0wzHfABHAGUAbgBlAHIAaQBz -AGsAIABnAHIA5QAgAGcAYQBtAG0AYQAgADIALAAyAC0AcAByAG8AZgBpAGwATwBiAGUAYwBuAOEAIAFh -AGUAZADhACAAZwBhAG0AYQAgADIALgAyBdIF0AXeBdQAIAXQBeQF1QXoACAF2wXcBdwF2QAgADIALgAy -AEcAYQBtAGEAIABnAHIAaQAgAGcAZQBuAGUAcgBpAGMBAwAgADIALAAyAEEAbABsAGcAZQBtAGUAaQBu -AGUAcwAgAEcAcgBhAHUAcwB0AHUAZgBlAG4ALQBQAHIAbwBmAGkAbAAgAEcAYQBtAG0AYQAgADIALAAy -AFAAcgBvAGYAaQBsAG8AIABnAHIAaQBnAGkAbwAgAGcAZQBuAGUAcgBpAGMAbwAgAGQAZQBsAGwAYQAg -AGcAYQBtAG0AYQAgADIALAAyAEcAZQBuAGUAcgBpAHMAawAgAGcAcgDlACAAMgAsADIAIABnAGEAbQBt -AGEAcAByAG8AZgBpAGxmbpAacHBepnz7ZXAAMgAuADJjz4/wZYdO9k4AgiwwsDDsMKQwrDDzMN4AIAAy -AC4AMgAgMNcw7TDVMKEwpDDrA5MDtQO9A7kDugPMACADkwO6A8EDuQAgA5MDrAO8A7wDsQAgADIALgAy -AFAAZQByAGYAaQBsACAAZwBlAG4A6QByAGkAYwBvACAAZABlACAAYwBpAG4AegBlAG4AdABvAHMAIABk -AGEAIABHAGEAbQBtAGEAIAAyACwAMgBBAGwAZwBlAG0AZQBlAG4AIABnAHIAaQBqAHMAIABnAGEAbQBt -AGEAIAAyACwAMgAtAHAAcgBvAGYAaQBlAGwAUABlAHIAZgBpAGwAIABnAGUAbgDpAHIAaQBjAG8AIABk -AGUAIABnAGEAbQBtAGEAIABkAGUAIABnAHIAaQBzAGUAcwAgADIALAAyDiMOMQ4HDioONQ5BDgEOIQ4h -DjIOQA4BDiMOIg5MDhcOMQ5IDicORA4bACAAMgAuADIARwBlAG4AZQBsACAARwByAGkAIABHAGEAbQBh -ACAAMgAsADIAWQBsAGUAaQBuAGUAbgAgAGgAYQByAG0AYQBhAG4AIABnAGEAbQBtAGEAIAAyACwAMgAg -AC0AcAByAG8AZgBpAGkAbABpAEcAZQBuAGUAcgBpAQ0AawBpACAARwByAGEAeQAgAEcAYQBtAG0AYQAg -ADIALgAyACAAcAByAG8AZgBpAGwAVQBuAGkAdwBlAHIAcwBhAGwAbgB5ACAAcAByAG8AZgBpAGwAIABz -AHoAYQByAG8BWwBjAGkAIABnAGEAbQBtAGEAIAAyACwAMgY6BicGRQYnACAAMgAuADIAIAZEBkgGRgAg -BjEGRQYnBi8GSgAgBjkGJwZFBB4EMQRJBDAETwAgBEEENQRABDAETwAgBDMEMAQ8BDwEMAAgADIALAAy -AC0EPwRABD4ERAQ4BDsETABHAGUAbgBlAHIAaQBjACAARwByAGEAeQAgAEcAYQBtAG0AYQAgADIALgAy -ACAAUAByAG8AZgBpAGwAZQAAdGV4dAAAAABDb3B5cmlnaHQgQXBwbGUgSW5jLiwgMjAxMgAAWFlaIAAA -AAAAAPNRAAEAAAABFsxjdXJ2AAAAAAAABAAAAAAFAAoADwAUABkAHgAjACgALQAyADcAOwBAAEUASgBP -AFQAWQBeAGMAaABtAHIAdwB8AIEAhgCLAJAAlQCaAJ8ApACpAK4AsgC3ALwAwQDGAMsA0ADVANsA4ADl -AOsA8AD2APsBAQEHAQ0BEwEZAR8BJQErATIBOAE+AUUBTAFSAVkBYAFnAW4BdQF8AYMBiwGSAZoBoQGp -AbEBuQHBAckB0QHZAeEB6QHyAfoCAwIMAhQCHQImAi8COAJBAksCVAJdAmcCcQJ6AoQCjgKYAqICrAK2 -AsECywLVAuAC6wL1AwADCwMWAyEDLQM4A0MDTwNaA2YDcgN+A4oDlgOiA64DugPHA9MD4APsA/kEBgQT -BCAELQQ7BEgEVQRjBHEEfgSMBJoEqAS2BMQE0wThBPAE/gUNBRwFKwU6BUkFWAVnBXcFhgWWBaYFtQXF -BdUF5QX2BgYGFgYnBjcGSAZZBmoGewaMBp0GrwbABtEG4wb1BwcHGQcrBz0HTwdhB3QHhgeZB6wHvwfS -B+UH+AgLCB8IMghGCFoIbgiCCJYIqgi+CNII5wj7CRAJJQk6CU8JZAl5CY8JpAm6Cc8J5Qn7ChEKJwo9 -ClQKagqBCpgKrgrFCtwK8wsLCyILOQtRC2kLgAuYC7ALyAvhC/kMEgwqDEMMXAx1DI4MpwzADNkM8w0N -DSYNQA1aDXQNjg2pDcMN3g34DhMOLg5JDmQOfw6bDrYO0g7uDwkPJQ9BD14Peg+WD7MPzw/sEAkQJhBD -EGEQfhCbELkQ1xD1ERMRMRFPEW0RjBGqEckR6BIHEiYSRRJkEoQSoxLDEuMTAxMjE0MTYxODE6QTxRPl -FAYUJxRJFGoUixStFM4U8BUSFTQVVhV4FZsVvRXgFgMWJhZJFmwWjxayFtYW+hcdF0EXZReJF64X0hf3 -GBsYQBhlGIoYrxjVGPoZIBlFGWsZkRm3Gd0aBBoqGlEadxqeGsUa7BsUGzsbYxuKG7Ib2hwCHCocUhx7 -HKMczBz1HR4dRx1wHZkdwx3sHhYeQB5qHpQevh7pHxMfPh9pH5Qfvx/qIBUgQSBsIJggxCDwIRwhSCF1 -IaEhziH7IiciVSKCIq8i3SMKIzgjZiOUI8Ij8CQfJE0kfCSrJNolCSU4JWgllyXHJfcmJyZXJocmtybo -JxgnSSd6J6sn3CgNKD8ocSiiKNQpBik4KWspnSnQKgIqNSpoKpsqzysCKzYraSudK9EsBSw5LG4soizX -LQwtQS12Last4S4WLkwugi63Lu4vJC9aL5Evxy/+MDUwbDCkMNsxEjFKMYIxujHyMioyYzKbMtQzDTNG -M38zuDPxNCs0ZTSeNNg1EzVNNYc1wjX9Njc2cjauNuk3JDdgN5w31zgUOFA4jDjIOQU5Qjl/Obw5+To2 -OnQ6sjrvOy07azuqO+g8JzxlPKQ84z0iPWE9oT3gPiA+YD6gPuA/IT9hP6I/4kAjQGRApkDnQSlBakGs -Qe5CMEJyQrVC90M6Q31DwEQDREdEikTORRJFVUWaRd5GIkZnRqtG8Ec1R3tHwEgFSEtIkUjXSR1JY0mp -SfBKN0p9SsRLDEtTS5pL4kwqTHJMuk0CTUpNk03cTiVObk63TwBPSU+TT91QJ1BxULtRBlFQUZtR5lIx -UnxSx1MTU19TqlP2VEJUj1TbVShVdVXCVg9WXFapVvdXRFeSV+BYL1h9WMtZGllpWbhaB1pWWqZa9VtF -W5Vb5Vw1XIZc1l0nXXhdyV4aXmxevV8PX2Ffs2AFYFdgqmD8YU9homH1YklinGLwY0Njl2PrZEBklGTp -ZT1lkmXnZj1mkmboZz1nk2fpaD9olmjsaUNpmmnxakhqn2r3a09rp2v/bFdsr20IbWBtuW4SbmtuxG8e -b3hv0XArcIZw4HE6cZVx8HJLcqZzAXNdc7h0FHRwdMx1KHWFdeF2Pnabdvh3VnezeBF4bnjMeSp5iXnn -ekZ6pXsEe2N7wnwhfIF84X1BfaF+AX5ifsJ/I3+Ef+WAR4CogQqBa4HNgjCCkoL0g1eDuoQdhICE44VH -hauGDoZyhteHO4efiASIaYjOiTOJmYn+imSKyoswi5aL/IxjjMqNMY2Yjf+OZo7OjzaPnpAGkG6Q1pE/ -kaiSEZJ6kuOTTZO2lCCUipT0lV+VyZY0lp+XCpd1l+CYTJi4mSSZkJn8mmia1ZtCm6+cHJyJnPedZJ3S -nkCerp8dn4uf+qBpoNihR6G2oiailqMGo3aj5qRWpMelOKWpphqmi6b9p26n4KhSqMSpN6mpqhyqj6sC -q3Wr6axcrNCtRK24ri2uoa8Wr4uwALB1sOqxYLHWskuywrM4s660JbSctRO1irYBtnm28Ldot+C4WbjR -uUq5wro7urW7LrunvCG8m70VvY++Cr6Evv+/er/1wHDA7MFnwePCX8Lbw1jD1MRRxM7FS8XIxkbGw8dB -x7/IPci8yTrJuco4yrfLNsu2zDXMtc01zbXONs62zzfPuNA50LrRPNG+0j/SwdNE08bUSdTL1U7V0dZV -1tjXXNfg2GTY6Nls2fHadtr724DcBdyK3RDdlt4c3qLfKd+v4DbgveFE4cziU+Lb42Pj6+Rz5PzlhOYN -5pbnH+ep6DLovOlG6dDqW+rl63Dr++yG7RHtnO4o7rTvQO/M8Fjw5fFy8f/yjPMZ86f0NPTC9VD13vZt -9vv3ivgZ+Kj5OPnH+lf65/t3/Af8mP0p/br+S/7c/23//9IrLC0uWiRjbGFzc25hbWVYJGNsYXNzZXNf -EBBOU0JpdG1hcEltYWdlUmVwoy0vMFpOU0ltYWdlUmVwWE5TT2JqZWN00issMjNXTlNBcnJheaIyMNIr -LDU2Xk5TTXV0YWJsZUFycmF5ozUyMNM4OQ86OzxXTlNXaGl0ZVxOU0NvbG9yU3BhY2VEMCAwABADgAzS -Kyw+P1dOU0NvbG9yoj4w0issQUJXTlNJbWFnZaJBMAAIABEAGgAkACkAMgA3AEkATABRAFMAYgBoAHUA -fACLAJIAnwCmAK4AsACyALQAuQC7AL0AxADJANQA1gDYANoA3wDiAOQA5gDoAO8BBgEiASQBJhOWE5sT -phOvE8ITxhPRE9oT3xPnE+oT7xP+FAIUCRQRFB4UIxQlFCcULBQ0FDcUPBREAAAAAAAAAgEAAAAAAAAA -QwAAAAAAAAAAAAAAAAAAFEc +S2V5ZWRBcmNoaXZlctEICVRyb290gAGuCwwbHCEaJissMzY5P0JVJG51bGzXDQ4PEBESExQVFhcYGRpW +TlNTaXplViRjbGFzc1xOU0ltYWdlRmxhZ3NWTlNSZXBzW05TVGludENvbG9yV05TQ29sb3JeTlNSZXNp +emluZ01vZGWAAoANEiDDAACAA4AAgAsQAFZ7MSwgMX3SHQ4eIFpOUy5vYmplY3RzoR+ABIAK0h0OIiWi +IySABYAGgAnTDicoKSoaXxAUTlNUSUZGUmVwcmVzZW50YXRpb25fEBlOU0ludGVybmFsTGF5b3V0RGly +ZWN0aW9ugAiAB08REmxNTQAqAAAACgAAABABAAADAAAAAQABAAABAQADAAAAAQABAAABAgADAAAAAgAI +AAgBAwADAAAAAQABAAABBgADAAAAAQABAAABCgADAAAAAQABAAABEQAEAAAAAQAAAAgBEgADAAAAAQAB +AAABFQADAAAAAQACAAABFgADAAAAAQABAAABFwAEAAAAAQAAAAIBHAADAAAAAQABAAABKAADAAAAAQAC +AAABUgADAAAAAQABAAABUwADAAAAAgABAAGHcwAHAAARnAAAANAAAAAAAAARnGFwcGwCAAAAbW50ckdS +QVlYWVogB9wACAAXAA8ALgAPYWNzcEFQUEwAAAAAbm9uZQAAAAAAAAAAAAAAAAAAAAAAAPbWAAEAAAAA +0y1hcHBsAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFZGVzYwAA +AMAAAAB5ZHNjbQAAATwAAAgaY3BydAAACVgAAAAjd3RwdAAACXwAAAAUa1RSQwAACZAAAAgMZGVzYwAA +AAAAAAAfR2VuZXJpYyBHcmF5IEdhbW1hIDIuMiBQcm9maWxlAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAG1s +dWMAAAAAAAAAHwAAAAxza1NLAAAALgAAAYRkYURLAAAAOgAAAbJjYUVTAAAAOAAAAex2aVZOAAAAQAAA +AiRwdEJSAAAASgAAAmR1a1VBAAAALAAAAq5mckZVAAAAPgAAAtpodUhVAAAANAAAAxh6aFRXAAAAGgAA +A0xrb0tSAAAAIgAAA2ZuYk5PAAAAOgAAA4hjc0NaAAAAKAAAA8JoZUlMAAAAJAAAA+pyb1JPAAAAKgAA +BA5kZURFAAAATgAABDhpdElUAAAATgAABIZzdlNFAAAAOAAABNR6aENOAAAAGgAABQxqYUpQAAAAJgAA +BSZlbEdSAAAAKgAABUxwdFBPAAAAUgAABXZubE5MAAAAQAAABchlc0VTAAAATAAABgh0aFRIAAAAMgAA +BlR0clRSAAAAJAAABoZmaUZJAAAARgAABqpockhSAAAAPgAABvBwbFBMAAAASgAABy5hckVHAAAALAAA +B3hydVJVAAAAOgAAB6RlblVTAAAAPAAAB94AVgFhAGUAbwBiAGUAYwBuAOEAIABzAGkAdgDhACAAZwBh +AG0AYQAgADIALAAyAEcAZQBuAGUAcgBpAHMAawAgAGcAcgDlACAAMgAsADIAIABnAGEAbQBtAGEALQBw +AHIAbwBmAGkAbABHAGEAbQBtAGEAIABkAGUAIABnAHIAaQBzAG8AcwAgAGcAZQBuAOgAcgBpAGMAYQAg +ADIALgAyAEMepQB1ACAAaADsAG4AaAAgAE0A4AB1ACAAeADhAG0AIABDAGgAdQBuAGcAIABHAGEAbQBt +AGEAIAAyAC4AMgBQAGUAcgBmAGkAbAAgAEcAZQBuAOkAcgBpAGMAbwAgAGQAYQAgAEcAYQBtAGEAIABk +AGUAIABDAGkAbgB6AGEAcwAgADIALAAyBBcEMAQzBDAEOwRMBD0EMAAgAEcAcgBhAHkALQQzBDAEPAQw +ACAAMgAuADIAUAByAG8AZgBpAGwAIABnAOkAbgDpAHIAaQBxAHUAZQAgAGcAcgBpAHMAIABnAGEAbQBt +AGEAIAAyACwAMgDBAGwAdABhAGwA4QBuAG8AcwAgAHMAegD8AHIAawBlACAAZwBhAG0AbQBhACAAMgAu +ADKQGnUocHCWjlFJXqYAMgAuADKCcl9pY8+P8Md8vBgAINaMwMkAIKwQucgAIAAyAC4AMgAg1QS4XNMM +x3wARwBlAG4AZQByAGkAcwBrACAAZwByAOUAIABnAGEAbQBtAGEAIAAyACwAMgAtAHAAcgBvAGYAaQBs +AE8AYgBlAGMAbgDhACABYQBlAGQA4QAgAGcAYQBtAGEAIAAyAC4AMgXSBdAF3gXUACAF0AXkBdUF6AAg +BdsF3AXcBdkAIAAyAC4AMgBHAGEAbQBhACAAZwByAGkAIABnAGUAbgBlAHIAaQBjAQMAIAAyACwAMgBB +AGwAbABnAGUAbQBlAGkAbgBlAHMAIABHAHIAYQB1AHMAdAB1AGYAZQBuAC0AUAByAG8AZgBpAGwAIABH +AGEAbQBtAGEAIAAyACwAMgBQAHIAbwBmAGkAbABvACAAZwByAGkAZwBpAG8AIABnAGUAbgBlAHIAaQBj +AG8AIABkAGUAbABsAGEAIABnAGEAbQBtAGEAIAAyACwAMgBHAGUAbgBlAHIAaQBzAGsAIABnAHIA5QAg +ADIALAAyACAAZwBhAG0AbQBhAHAAcgBvAGYAaQBsZm6QGnBwXqZ8+2VwADIALgAyY8+P8GWHTvZOAIIs +MLAw7DCkMKww8zDeACAAMgAuADIAIDDXMO0w1TChMKQw6wOTA7UDvQO5A7oDzAAgA5MDugPBA7kAIAOT +A6wDvAO8A7EAIAAyAC4AMgBQAGUAcgBmAGkAbAAgAGcAZQBuAOkAcgBpAGMAbwAgAGQAZQAgAGMAaQBu +AHoAZQBuAHQAbwBzACAAZABhACAARwBhAG0AbQBhACAAMgAsADIAQQBsAGcAZQBtAGUAZQBuACAAZwBy +AGkAagBzACAAZwBhAG0AbQBhACAAMgAsADIALQBwAHIAbwBmAGkAZQBsAFAAZQByAGYAaQBsACAAZwBl +AG4A6QByAGkAYwBvACAAZABlACAAZwBhAG0AbQBhACAAZABlACAAZwByAGkAcwBlAHMAIAAyACwAMg4j +DjEOBw4qDjUOQQ4BDiEOIQ4yDkAOAQ4jDiIOTA4XDjEOSA4nDkQOGwAgADIALgAyAEcAZQBuAGUAbAAg +AEcAcgBpACAARwBhAG0AYQAgADIALAAyAFkAbABlAGkAbgBlAG4AIABoAGEAcgBtAGEAYQBuACAAZwBh +AG0AbQBhACAAMgAsADIAIAAtAHAAcgBvAGYAaQBpAGwAaQBHAGUAbgBlAHIAaQENAGsAaQAgAEcAcgBh +AHkAIABHAGEAbQBtAGEAIAAyAC4AMgAgAHAAcgBvAGYAaQBsAFUAbgBpAHcAZQByAHMAYQBsAG4AeQAg +AHAAcgBvAGYAaQBsACAAcwB6AGEAcgBvAVsAYwBpACAAZwBhAG0AbQBhACAAMgAsADIGOgYnBkUGJwAg +ADIALgAyACAGRAZIBkYAIAYxBkUGJwYvBkoAIAY5BicGRQQeBDEESQQwBE8AIARBBDUEQAQwBE8AIAQz +BDAEPAQ8BDAAIAAyACwAMgAtBD8EQAQ+BEQEOAQ7BEwARwBlAG4AZQByAGkAYwAgAEcAcgBhAHkAIABH +AGEAbQBtAGEAIAAyAC4AMgAgAFAAcgBvAGYAaQBsAGUAAHRleHQAAAAAQ29weXJpZ2h0IEFwcGxlIElu +Yy4sIDIwMTIAAFhZWiAAAAAAAADzUQABAAAAARbMY3VydgAAAAAAAAQAAAAABQAKAA8AFAAZAB4AIwAo +AC0AMgA3ADsAQABFAEoATwBUAFkAXgBjAGgAbQByAHcAfACBAIYAiwCQAJUAmgCfAKQAqQCuALIAtwC8 +AMEAxgDLANAA1QDbAOAA5QDrAPAA9gD7AQEBBwENARMBGQEfASUBKwEyATgBPgFFAUwBUgFZAWABZwFu +AXUBfAGDAYsBkgGaAaEBqQGxAbkBwQHJAdEB2QHhAekB8gH6AgMCDAIUAh0CJgIvAjgCQQJLAlQCXQJn +AnECegKEAo4CmAKiAqwCtgLBAssC1QLgAusC9QMAAwsDFgMhAy0DOANDA08DWgNmA3IDfgOKA5YDogOu +A7oDxwPTA+AD7AP5BAYEEwQgBC0EOwRIBFUEYwRxBH4EjASaBKgEtgTEBNME4QTwBP4FDQUcBSsFOgVJ +BVgFZwV3BYYFlgWmBbUFxQXVBeUF9gYGBhYGJwY3BkgGWQZqBnsGjAadBq8GwAbRBuMG9QcHBxkHKwc9 +B08HYQd0B4YHmQesB78H0gflB/gICwgfCDIIRghaCG4IggiWCKoIvgjSCOcI+wkQCSUJOglPCWQJeQmP +CaQJugnPCeUJ+woRCicKPQpUCmoKgQqYCq4KxQrcCvMLCwsiCzkLUQtpC4ALmAuwC8gL4Qv5DBIMKgxD +DFwMdQyODKcMwAzZDPMNDQ0mDUANWg10DY4NqQ3DDd4N+A4TDi4OSQ5kDn8Omw62DtIO7g8JDyUPQQ9e +D3oPlg+zD88P7BAJECYQQxBhEH4QmxC5ENcQ9RETETERTxFtEYwRqhHJEegSBxImEkUSZBKEEqMSwxLj +EwMTIxNDE2MTgxOkE8UT5RQGFCcUSRRqFIsUrRTOFPAVEhU0FVYVeBWbFb0V4BYDFiYWSRZsFo8WshbW +FvoXHRdBF2UXiReuF9IX9xgbGEAYZRiKGK8Y1Rj6GSAZRRlrGZEZtxndGgQaKhpRGncanhrFGuwbFBs7 +G2MbihuyG9ocAhwqHFIcexyjHMwc9R0eHUcdcB2ZHcMd7B4WHkAeah6UHr4e6R8THz4faR+UH78f6iAV +IEEgbCCYIMQg8CEcIUghdSGhIc4h+yInIlUigiKvIt0jCiM4I2YjlCPCI/AkHyRNJHwkqyTaJQklOCVo +JZclxyX3JicmVyaHJrcm6CcYJ0kneierJ9woDSg/KHEooijUKQYpOClrKZ0p0CoCKjUqaCqbKs8rAis2 +K2krnSvRLAUsOSxuLKIs1y0MLUEtdi2rLeEuFi5MLoIuty7uLyQvWi+RL8cv/jA1MGwwpDDbMRIxSjGC +Mbox8jIqMmMymzLUMw0zRjN/M7gz8TQrNGU0njTYNRM1TTWHNcI1/TY3NnI2rjbpNyQ3YDecN9c4FDhQ +OIw4yDkFOUI5fzm8Ofk6Njp0OrI67zstO2s7qjvoPCc8ZTykPOM9Ij1hPaE94D4gPmA+oD7gPyE/YT+i +P+JAI0BkQKZA50EpQWpBrEHuQjBCckK1QvdDOkN9Q8BEA0RHRIpEzkUSRVVFmkXeRiJGZ0arRvBHNUd7 +R8BIBUhLSJFI10kdSWNJqUnwSjdKfUrESwxLU0uaS+JMKkxyTLpNAk1KTZNN3E4lTm5Ot08AT0lPk0/d +UCdQcVC7UQZRUFGbUeZSMVJ8UsdTE1NfU6pT9lRCVI9U21UoVXVVwlYPVlxWqVb3V0RXklfgWC9YfVjL +WRpZaVm4WgdaVlqmWvVbRVuVW+VcNVyGXNZdJ114XcleGl5sXr1fD19hX7NgBWBXYKpg/GFPYaJh9WJJ +Ypxi8GNDY5dj62RAZJRk6WU9ZZJl52Y9ZpJm6Gc9Z5Nn6Wg/aJZo7GlDaZpp8WpIap9q92tPa6dr/2xX +bK9tCG1gbbluEm5rbsRvHm94b9FwK3CGcOBxOnGVcfByS3KmcwFzXXO4dBR0cHTMdSh1hXXhdj52m3b4 +d1Z3s3gReG54zHkqeYl553pGeqV7BHtje8J8IXyBfOF9QX2hfgF+Yn7CfyN/hH/lgEeAqIEKgWuBzYIw +gpKC9INXg7qEHYSAhOOFR4Wrhg6GcobXhzuHn4gEiGmIzokziZmJ/opkisqLMIuWi/yMY4zKjTGNmI3/ +jmaOzo82j56QBpBukNaRP5GokhGSepLjk02TtpQglIqU9JVflcmWNJaflwqXdZfgmEyYuJkkmZCZ/Jpo +mtWbQpuvnByciZz3nWSd0p5Anq6fHZ+Ln/qgaaDYoUehtqImopajBqN2o+akVqTHpTilqaYapoum/adu +p+CoUqjEqTepqaocqo+rAqt1q+msXKzQrUStuK4trqGvFq+LsACwdbDqsWCx1rJLssKzOLOutCW0nLUT +tYq2AbZ5tvC3aLfguFm40blKucK6O7q1uy67p7whvJu9Fb2Pvgq+hL7/v3q/9cBwwOzBZ8Hjwl/C28NY +w9TEUcTOxUvFyMZGxsPHQce/yD3IvMk6ybnKOMq3yzbLtsw1zLXNNc21zjbOts83z7jQOdC60TzRvtI/ +0sHTRNPG1EnUy9VO1dHWVdbY11zX4Nhk2OjZbNnx2nba+9uA3AXcit0Q3ZbeHN6i3ynfr+A24L3hROHM +4lPi2+Nj4+vkc+T85YTmDeaW5x/nqegy6LzpRunQ6lvq5etw6/vshu0R7ZzuKO6070DvzPBY8OXxcvH/ +8ozzGfOn9DT0wvVQ9d72bfb794r4Gfio+Tj5x/pX+uf7d/wH/Jj9Kf26/kv+3P9t///SLS4vMFokY2xh +c3NuYW1lWCRjbGFzc2VzXxAQTlNCaXRtYXBJbWFnZVJlcKMvMTJaTlNJbWFnZVJlcFhOU09iamVjdNIt +LjQ1V05TQXJyYXmiNDLSLS43OF5OU011dGFibGVBcnJheaM3NDLTOjsOPD0+V05TV2hpdGVcTlNDb2xv +clNwYWNlRDAgMAAQA4AM0i0uQEFXTlNDb2xvcqJAMtItLkNEV05TSW1hZ2WiQzIACAARABoAJAApADIA +NwBJAEwAUQBTAGIAaAB3AH4AhQCSAJkApQCtALwAvgDAAMUAxwDJAMsAzQDUANkA5ADmAOgA6gDvAPIA +9AD2APgA/wEWATIBNAE2E6YTqxO2E78T0hPWE+ET6hPvE/cT+hP/FA4UEhQZFCEULhQzFDUUNxQ8FEQU +RxRMFFQAAAAAAAACAQAAAAAAAABFAAAAAAAAAAAAAAAAAAAUVw diff --git a/i18n/src/main/resources/ar.lproj/Browser.xib b/i18n/src/main/resources/ar.lproj/Browser.xib index e1cc423c37..8e956b2b4b 100644 --- a/i18n/src/main/resources/ar.lproj/Browser.xib +++ b/i18n/src/main/resources/ar.lproj/Browser.xib @@ -1,7 +1,7 @@ - + - + @@ -38,7 +38,7 @@ - + @@ -62,27 +62,27 @@ @@ -256,7 +256,7 @@ - + @@ -553,95 +553,95 @@ DQ YnBsaXN0MDDUAQIDBAUGBwpYJHZlcnNpb25ZJGFyY2hpdmVyVCR0b3BYJG9iamVjdHMSAAGGoF8QD05T -S2V5ZWRBcmNoaXZlctEICVRyb290gAGuCwwZGh8UJCkqMTQ3PUBVJG51bGzWDQ4PEBESExQVFhcYVk5T -U2l6ZV5OU1Jlc2l6aW5nTW9kZVYkY2xhc3NcTlNJbWFnZUZsYWdzVk5TUmVwc1dOU0NvbG9ygAIQAIAN -EiDDAACAA4ALVnsxLCAxfdIbDxweWk5TLm9iamVjdHOhHYAEgArSGw8gI6IhIoAFgAaACdMPJSYnKBRf -EBROU1RJRkZSZXByZXNlbnRhdGlvbl8QGU5TSW50ZXJuYWxMYXlvdXREaXJlY3Rpb26ACIAHTxESbE1N -ACoAAAAKAAAAEAEAAAMAAAABAAEAAAEBAAMAAAABAAEAAAECAAMAAAACAAgACAEDAAMAAAABAAEAAAEG -AAMAAAABAAEAAAEKAAMAAAABAAEAAAERAAQAAAABAAAACAESAAMAAAABAAEAAAEVAAMAAAABAAIAAAEW -AAMAAAABAAEAAAEXAAQAAAABAAAAAgEcAAMAAAABAAEAAAEoAAMAAAABAAIAAAFSAAMAAAABAAEAAAFT -AAMAAAACAAEAAYdzAAcAABGcAAAA0AAAAAAAABGcYXBwbAIAAABtbnRyR1JBWVhZWiAH3AAIABcADwAu -AA9hY3NwQVBQTAAAAABub25lAAAAAAAAAAAAAAAAAAAAAAAA9tYAAQAAAADTLWFwcGwAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAVkZXNjAAAAwAAAAHlkc2NtAAABPAAA -CBpjcHJ0AAAJWAAAACN3dHB0AAAJfAAAABRrVFJDAAAJkAAACAxkZXNjAAAAAAAAAB9HZW5lcmljIEdy -YXkgR2FtbWEgMi4yIFByb2ZpbGUAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAbWx1YwAAAAAAAAAfAAAADHNr -U0sAAAAuAAABhGRhREsAAAA6AAABsmNhRVMAAAA4AAAB7HZpVk4AAABAAAACJHB0QlIAAABKAAACZHVr -VUEAAAAsAAACrmZyRlUAAAA+AAAC2mh1SFUAAAA0AAADGHpoVFcAAAAaAAADTGtvS1IAAAAiAAADZm5i -Tk8AAAA6AAADiGNzQ1oAAAAoAAADwmhlSUwAAAAkAAAD6nJvUk8AAAAqAAAEDmRlREUAAABOAAAEOGl0 -SVQAAABOAAAEhnN2U0UAAAA4AAAE1HpoQ04AAAAaAAAFDGphSlAAAAAmAAAFJmVsR1IAAAAqAAAFTHB0 -UE8AAABSAAAFdm5sTkwAAABAAAAFyGVzRVMAAABMAAAGCHRoVEgAAAAyAAAGVHRyVFIAAAAkAAAGhmZp -RkkAAABGAAAGqmhySFIAAAA+AAAG8HBsUEwAAABKAAAHLmFyRUcAAAAsAAAHeHJ1UlUAAAA6AAAHpGVu -VVMAAAA8AAAH3gBWAWEAZQBvAGIAZQBjAG4A4QAgAHMAaQB2AOEAIABnAGEAbQBhACAAMgAsADIARwBl -AG4AZQByAGkAcwBrACAAZwByAOUAIAAyACwAMgAgAGcAYQBtAG0AYQAtAHAAcgBvAGYAaQBsAEcAYQBt -AG0AYQAgAGQAZQAgAGcAcgBpAHMAbwBzACAAZwBlAG4A6AByAGkAYwBhACAAMgAuADIAQx6lAHUAIABo -AOwAbgBoACAATQDgAHUAIAB4AOEAbQAgAEMAaAB1AG4AZwAgAEcAYQBtAG0AYQAgADIALgAyAFAAZQBy -AGYAaQBsACAARwBlAG4A6QByAGkAYwBvACAAZABhACAARwBhAG0AYQAgAGQAZQAgAEMAaQBuAHoAYQBz -ACAAMgAsADIEFwQwBDMEMAQ7BEwEPQQwACAARwByAGEAeQAtBDMEMAQ8BDAAIAAyAC4AMgBQAHIAbwBm -AGkAbAAgAGcA6QBuAOkAcgBpAHEAdQBlACAAZwByAGkAcwAgAGcAYQBtAG0AYQAgADIALAAyAMEAbAB0 -AGEAbADhAG4AbwBzACAAcwB6APwAcgBrAGUAIABnAGEAbQBtAGEAIAAyAC4AMpAadShwcJaOUUlepgAy -AC4AMoJyX2ljz4/wx3y8GAAg1ozAyQAgrBC5yAAgADIALgAyACDVBLhc0wzHfABHAGUAbgBlAHIAaQBz -AGsAIABnAHIA5QAgAGcAYQBtAG0AYQAgADIALAAyAC0AcAByAG8AZgBpAGwATwBiAGUAYwBuAOEAIAFh -AGUAZADhACAAZwBhAG0AYQAgADIALgAyBdIF0AXeBdQAIAXQBeQF1QXoACAF2wXcBdwF2QAgADIALgAy -AEcAYQBtAGEAIABnAHIAaQAgAGcAZQBuAGUAcgBpAGMBAwAgADIALAAyAEEAbABsAGcAZQBtAGUAaQBu -AGUAcwAgAEcAcgBhAHUAcwB0AHUAZgBlAG4ALQBQAHIAbwBmAGkAbAAgAEcAYQBtAG0AYQAgADIALAAy -AFAAcgBvAGYAaQBsAG8AIABnAHIAaQBnAGkAbwAgAGcAZQBuAGUAcgBpAGMAbwAgAGQAZQBsAGwAYQAg -AGcAYQBtAG0AYQAgADIALAAyAEcAZQBuAGUAcgBpAHMAawAgAGcAcgDlACAAMgAsADIAIABnAGEAbQBt -AGEAcAByAG8AZgBpAGxmbpAacHBepnz7ZXAAMgAuADJjz4/wZYdO9k4AgiwwsDDsMKQwrDDzMN4AIAAy -AC4AMgAgMNcw7TDVMKEwpDDrA5MDtQO9A7kDugPMACADkwO6A8EDuQAgA5MDrAO8A7wDsQAgADIALgAy -AFAAZQByAGYAaQBsACAAZwBlAG4A6QByAGkAYwBvACAAZABlACAAYwBpAG4AegBlAG4AdABvAHMAIABk -AGEAIABHAGEAbQBtAGEAIAAyACwAMgBBAGwAZwBlAG0AZQBlAG4AIABnAHIAaQBqAHMAIABnAGEAbQBt -AGEAIAAyACwAMgAtAHAAcgBvAGYAaQBlAGwAUABlAHIAZgBpAGwAIABnAGUAbgDpAHIAaQBjAG8AIABk -AGUAIABnAGEAbQBtAGEAIABkAGUAIABnAHIAaQBzAGUAcwAgADIALAAyDiMOMQ4HDioONQ5BDgEOIQ4h -DjIOQA4BDiMOIg5MDhcOMQ5IDicORA4bACAAMgAuADIARwBlAG4AZQBsACAARwByAGkAIABHAGEAbQBh -ACAAMgAsADIAWQBsAGUAaQBuAGUAbgAgAGgAYQByAG0AYQBhAG4AIABnAGEAbQBtAGEAIAAyACwAMgAg -AC0AcAByAG8AZgBpAGkAbABpAEcAZQBuAGUAcgBpAQ0AawBpACAARwByAGEAeQAgAEcAYQBtAG0AYQAg -ADIALgAyACAAcAByAG8AZgBpAGwAVQBuAGkAdwBlAHIAcwBhAGwAbgB5ACAAcAByAG8AZgBpAGwAIABz -AHoAYQByAG8BWwBjAGkAIABnAGEAbQBtAGEAIAAyACwAMgY6BicGRQYnACAAMgAuADIAIAZEBkgGRgAg -BjEGRQYnBi8GSgAgBjkGJwZFBB4EMQRJBDAETwAgBEEENQRABDAETwAgBDMEMAQ8BDwEMAAgADIALAAy -AC0EPwRABD4ERAQ4BDsETABHAGUAbgBlAHIAaQBjACAARwByAGEAeQAgAEcAYQBtAG0AYQAgADIALgAy -ACAAUAByAG8AZgBpAGwAZQAAdGV4dAAAAABDb3B5cmlnaHQgQXBwbGUgSW5jLiwgMjAxMgAAWFlaIAAA -AAAAAPNRAAEAAAABFsxjdXJ2AAAAAAAABAAAAAAFAAoADwAUABkAHgAjACgALQAyADcAOwBAAEUASgBP -AFQAWQBeAGMAaABtAHIAdwB8AIEAhgCLAJAAlQCaAJ8ApACpAK4AsgC3ALwAwQDGAMsA0ADVANsA4ADl -AOsA8AD2APsBAQEHAQ0BEwEZAR8BJQErATIBOAE+AUUBTAFSAVkBYAFnAW4BdQF8AYMBiwGSAZoBoQGp -AbEBuQHBAckB0QHZAeEB6QHyAfoCAwIMAhQCHQImAi8COAJBAksCVAJdAmcCcQJ6AoQCjgKYAqICrAK2 -AsECywLVAuAC6wL1AwADCwMWAyEDLQM4A0MDTwNaA2YDcgN+A4oDlgOiA64DugPHA9MD4APsA/kEBgQT -BCAELQQ7BEgEVQRjBHEEfgSMBJoEqAS2BMQE0wThBPAE/gUNBRwFKwU6BUkFWAVnBXcFhgWWBaYFtQXF -BdUF5QX2BgYGFgYnBjcGSAZZBmoGewaMBp0GrwbABtEG4wb1BwcHGQcrBz0HTwdhB3QHhgeZB6wHvwfS -B+UH+AgLCB8IMghGCFoIbgiCCJYIqgi+CNII5wj7CRAJJQk6CU8JZAl5CY8JpAm6Cc8J5Qn7ChEKJwo9 -ClQKagqBCpgKrgrFCtwK8wsLCyILOQtRC2kLgAuYC7ALyAvhC/kMEgwqDEMMXAx1DI4MpwzADNkM8w0N -DSYNQA1aDXQNjg2pDcMN3g34DhMOLg5JDmQOfw6bDrYO0g7uDwkPJQ9BD14Peg+WD7MPzw/sEAkQJhBD -EGEQfhCbELkQ1xD1ERMRMRFPEW0RjBGqEckR6BIHEiYSRRJkEoQSoxLDEuMTAxMjE0MTYxODE6QTxRPl -FAYUJxRJFGoUixStFM4U8BUSFTQVVhV4FZsVvRXgFgMWJhZJFmwWjxayFtYW+hcdF0EXZReJF64X0hf3 -GBsYQBhlGIoYrxjVGPoZIBlFGWsZkRm3Gd0aBBoqGlEadxqeGsUa7BsUGzsbYxuKG7Ib2hwCHCocUhx7 -HKMczBz1HR4dRx1wHZkdwx3sHhYeQB5qHpQevh7pHxMfPh9pH5Qfvx/qIBUgQSBsIJggxCDwIRwhSCF1 -IaEhziH7IiciVSKCIq8i3SMKIzgjZiOUI8Ij8CQfJE0kfCSrJNolCSU4JWgllyXHJfcmJyZXJocmtybo -JxgnSSd6J6sn3CgNKD8ocSiiKNQpBik4KWspnSnQKgIqNSpoKpsqzysCKzYraSudK9EsBSw5LG4soizX -LQwtQS12Last4S4WLkwugi63Lu4vJC9aL5Evxy/+MDUwbDCkMNsxEjFKMYIxujHyMioyYzKbMtQzDTNG -M38zuDPxNCs0ZTSeNNg1EzVNNYc1wjX9Njc2cjauNuk3JDdgN5w31zgUOFA4jDjIOQU5Qjl/Obw5+To2 -OnQ6sjrvOy07azuqO+g8JzxlPKQ84z0iPWE9oT3gPiA+YD6gPuA/IT9hP6I/4kAjQGRApkDnQSlBakGs -Qe5CMEJyQrVC90M6Q31DwEQDREdEikTORRJFVUWaRd5GIkZnRqtG8Ec1R3tHwEgFSEtIkUjXSR1JY0mp -SfBKN0p9SsRLDEtTS5pL4kwqTHJMuk0CTUpNk03cTiVObk63TwBPSU+TT91QJ1BxULtRBlFQUZtR5lIx -UnxSx1MTU19TqlP2VEJUj1TbVShVdVXCVg9WXFapVvdXRFeSV+BYL1h9WMtZGllpWbhaB1pWWqZa9VtF -W5Vb5Vw1XIZc1l0nXXhdyV4aXmxevV8PX2Ffs2AFYFdgqmD8YU9homH1YklinGLwY0Njl2PrZEBklGTp -ZT1lkmXnZj1mkmboZz1nk2fpaD9olmjsaUNpmmnxakhqn2r3a09rp2v/bFdsr20IbWBtuW4SbmtuxG8e -b3hv0XArcIZw4HE6cZVx8HJLcqZzAXNdc7h0FHRwdMx1KHWFdeF2Pnabdvh3VnezeBF4bnjMeSp5iXnn -ekZ6pXsEe2N7wnwhfIF84X1BfaF+AX5ifsJ/I3+Ef+WAR4CogQqBa4HNgjCCkoL0g1eDuoQdhICE44VH -hauGDoZyhteHO4efiASIaYjOiTOJmYn+imSKyoswi5aL/IxjjMqNMY2Yjf+OZo7OjzaPnpAGkG6Q1pE/ -kaiSEZJ6kuOTTZO2lCCUipT0lV+VyZY0lp+XCpd1l+CYTJi4mSSZkJn8mmia1ZtCm6+cHJyJnPedZJ3S -nkCerp8dn4uf+qBpoNihR6G2oiailqMGo3aj5qRWpMelOKWpphqmi6b9p26n4KhSqMSpN6mpqhyqj6sC -q3Wr6axcrNCtRK24ri2uoa8Wr4uwALB1sOqxYLHWskuywrM4s660JbSctRO1irYBtnm28Ldot+C4WbjR -uUq5wro7urW7LrunvCG8m70VvY++Cr6Evv+/er/1wHDA7MFnwePCX8Lbw1jD1MRRxM7FS8XIxkbGw8dB -x7/IPci8yTrJuco4yrfLNsu2zDXMtc01zbXONs62zzfPuNA50LrRPNG+0j/SwdNE08bUSdTL1U7V0dZV -1tjXXNfg2GTY6Nls2fHadtr724DcBdyK3RDdlt4c3qLfKd+v4DbgveFE4cziU+Lb42Pj6+Rz5PzlhOYN -5pbnH+ep6DLovOlG6dDqW+rl63Dr++yG7RHtnO4o7rTvQO/M8Fjw5fFy8f/yjPMZ86f0NPTC9VD13vZt -9vv3ivgZ+Kj5OPnH+lf65/t3/Af8mP0p/br+S/7c/23//9IrLC0uWiRjbGFzc25hbWVYJGNsYXNzZXNf -EBBOU0JpdG1hcEltYWdlUmVwoy0vMFpOU0ltYWdlUmVwWE5TT2JqZWN00issMjNXTlNBcnJheaIyMNIr -LDU2Xk5TTXV0YWJsZUFycmF5ozUyMNM4OQ86OzxXTlNXaGl0ZVxOU0NvbG9yU3BhY2VEMCAwABADgAzS -Kyw+P1dOU0NvbG9yoj4w0issQUJXTlNJbWFnZaJBMAAIABEAGgAkACkAMgA3AEkATABRAFMAYgBoAHUA -fACLAJIAnwCmAK4AsACyALQAuQC7AL0AxADJANQA1gDYANoA3wDiAOQA5gDoAO8BBgEiASQBJhOWE5sT -phOvE8ITxhPRE9oT3xPnE+oT7xP+FAIUCRQRFB4UIxQlFCcULBQ0FDcUPBREAAAAAAAAAgEAAAAAAAAA -QwAAAAAAAAAAAAAAAAAAFEc +S2V5ZWRBcmNoaXZlctEICVRyb290gAGuCwwbHCEaJissMzY5P0JVJG51bGzXDQ4PEBESExQVFhcYGRpW +TlNTaXplViRjbGFzc1xOU0ltYWdlRmxhZ3NWTlNSZXBzW05TVGludENvbG9yV05TQ29sb3JeTlNSZXNp +emluZ01vZGWAAoANEiDDAACAA4AAgAsQAFZ7MSwgMX3SHQ4eIFpOUy5vYmplY3RzoR+ABIAK0h0OIiWi +IySABYAGgAnTDicoKSoaXxAUTlNUSUZGUmVwcmVzZW50YXRpb25fEBlOU0ludGVybmFsTGF5b3V0RGly +ZWN0aW9ugAiAB08REmxNTQAqAAAACgAAABABAAADAAAAAQABAAABAQADAAAAAQABAAABAgADAAAAAgAI +AAgBAwADAAAAAQABAAABBgADAAAAAQABAAABCgADAAAAAQABAAABEQAEAAAAAQAAAAgBEgADAAAAAQAB +AAABFQADAAAAAQACAAABFgADAAAAAQABAAABFwAEAAAAAQAAAAIBHAADAAAAAQABAAABKAADAAAAAQAC +AAABUgADAAAAAQABAAABUwADAAAAAgABAAGHcwAHAAARnAAAANAAAAAAAAARnGFwcGwCAAAAbW50ckdS +QVlYWVogB9wACAAXAA8ALgAPYWNzcEFQUEwAAAAAbm9uZQAAAAAAAAAAAAAAAAAAAAAAAPbWAAEAAAAA +0y1hcHBsAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFZGVzYwAA +AMAAAAB5ZHNjbQAAATwAAAgaY3BydAAACVgAAAAjd3RwdAAACXwAAAAUa1RSQwAACZAAAAgMZGVzYwAA +AAAAAAAfR2VuZXJpYyBHcmF5IEdhbW1hIDIuMiBQcm9maWxlAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAG1s +dWMAAAAAAAAAHwAAAAxza1NLAAAALgAAAYRkYURLAAAAOgAAAbJjYUVTAAAAOAAAAex2aVZOAAAAQAAA +AiRwdEJSAAAASgAAAmR1a1VBAAAALAAAAq5mckZVAAAAPgAAAtpodUhVAAAANAAAAxh6aFRXAAAAGgAA +A0xrb0tSAAAAIgAAA2ZuYk5PAAAAOgAAA4hjc0NaAAAAKAAAA8JoZUlMAAAAJAAAA+pyb1JPAAAAKgAA +BA5kZURFAAAATgAABDhpdElUAAAATgAABIZzdlNFAAAAOAAABNR6aENOAAAAGgAABQxqYUpQAAAAJgAA +BSZlbEdSAAAAKgAABUxwdFBPAAAAUgAABXZubE5MAAAAQAAABchlc0VTAAAATAAABgh0aFRIAAAAMgAA +BlR0clRSAAAAJAAABoZmaUZJAAAARgAABqpockhSAAAAPgAABvBwbFBMAAAASgAABy5hckVHAAAALAAA +B3hydVJVAAAAOgAAB6RlblVTAAAAPAAAB94AVgFhAGUAbwBiAGUAYwBuAOEAIABzAGkAdgDhACAAZwBh +AG0AYQAgADIALAAyAEcAZQBuAGUAcgBpAHMAawAgAGcAcgDlACAAMgAsADIAIABnAGEAbQBtAGEALQBw +AHIAbwBmAGkAbABHAGEAbQBtAGEAIABkAGUAIABnAHIAaQBzAG8AcwAgAGcAZQBuAOgAcgBpAGMAYQAg +ADIALgAyAEMepQB1ACAAaADsAG4AaAAgAE0A4AB1ACAAeADhAG0AIABDAGgAdQBuAGcAIABHAGEAbQBt +AGEAIAAyAC4AMgBQAGUAcgBmAGkAbAAgAEcAZQBuAOkAcgBpAGMAbwAgAGQAYQAgAEcAYQBtAGEAIABk +AGUAIABDAGkAbgB6AGEAcwAgADIALAAyBBcEMAQzBDAEOwRMBD0EMAAgAEcAcgBhAHkALQQzBDAEPAQw +ACAAMgAuADIAUAByAG8AZgBpAGwAIABnAOkAbgDpAHIAaQBxAHUAZQAgAGcAcgBpAHMAIABnAGEAbQBt +AGEAIAAyACwAMgDBAGwAdABhAGwA4QBuAG8AcwAgAHMAegD8AHIAawBlACAAZwBhAG0AbQBhACAAMgAu +ADKQGnUocHCWjlFJXqYAMgAuADKCcl9pY8+P8Md8vBgAINaMwMkAIKwQucgAIAAyAC4AMgAg1QS4XNMM +x3wARwBlAG4AZQByAGkAcwBrACAAZwByAOUAIABnAGEAbQBtAGEAIAAyACwAMgAtAHAAcgBvAGYAaQBs +AE8AYgBlAGMAbgDhACABYQBlAGQA4QAgAGcAYQBtAGEAIAAyAC4AMgXSBdAF3gXUACAF0AXkBdUF6AAg +BdsF3AXcBdkAIAAyAC4AMgBHAGEAbQBhACAAZwByAGkAIABnAGUAbgBlAHIAaQBjAQMAIAAyACwAMgBB +AGwAbABnAGUAbQBlAGkAbgBlAHMAIABHAHIAYQB1AHMAdAB1AGYAZQBuAC0AUAByAG8AZgBpAGwAIABH +AGEAbQBtAGEAIAAyACwAMgBQAHIAbwBmAGkAbABvACAAZwByAGkAZwBpAG8AIABnAGUAbgBlAHIAaQBj +AG8AIABkAGUAbABsAGEAIABnAGEAbQBtAGEAIAAyACwAMgBHAGUAbgBlAHIAaQBzAGsAIABnAHIA5QAg +ADIALAAyACAAZwBhAG0AbQBhAHAAcgBvAGYAaQBsZm6QGnBwXqZ8+2VwADIALgAyY8+P8GWHTvZOAIIs +MLAw7DCkMKww8zDeACAAMgAuADIAIDDXMO0w1TChMKQw6wOTA7UDvQO5A7oDzAAgA5MDugPBA7kAIAOT +A6wDvAO8A7EAIAAyAC4AMgBQAGUAcgBmAGkAbAAgAGcAZQBuAOkAcgBpAGMAbwAgAGQAZQAgAGMAaQBu +AHoAZQBuAHQAbwBzACAAZABhACAARwBhAG0AbQBhACAAMgAsADIAQQBsAGcAZQBtAGUAZQBuACAAZwBy +AGkAagBzACAAZwBhAG0AbQBhACAAMgAsADIALQBwAHIAbwBmAGkAZQBsAFAAZQByAGYAaQBsACAAZwBl +AG4A6QByAGkAYwBvACAAZABlACAAZwBhAG0AbQBhACAAZABlACAAZwByAGkAcwBlAHMAIAAyACwAMg4j +DjEOBw4qDjUOQQ4BDiEOIQ4yDkAOAQ4jDiIOTA4XDjEOSA4nDkQOGwAgADIALgAyAEcAZQBuAGUAbAAg +AEcAcgBpACAARwBhAG0AYQAgADIALAAyAFkAbABlAGkAbgBlAG4AIABoAGEAcgBtAGEAYQBuACAAZwBh +AG0AbQBhACAAMgAsADIAIAAtAHAAcgBvAGYAaQBpAGwAaQBHAGUAbgBlAHIAaQENAGsAaQAgAEcAcgBh +AHkAIABHAGEAbQBtAGEAIAAyAC4AMgAgAHAAcgBvAGYAaQBsAFUAbgBpAHcAZQByAHMAYQBsAG4AeQAg +AHAAcgBvAGYAaQBsACAAcwB6AGEAcgBvAVsAYwBpACAAZwBhAG0AbQBhACAAMgAsADIGOgYnBkUGJwAg +ADIALgAyACAGRAZIBkYAIAYxBkUGJwYvBkoAIAY5BicGRQQeBDEESQQwBE8AIARBBDUEQAQwBE8AIAQz +BDAEPAQ8BDAAIAAyACwAMgAtBD8EQAQ+BEQEOAQ7BEwARwBlAG4AZQByAGkAYwAgAEcAcgBhAHkAIABH +AGEAbQBtAGEAIAAyAC4AMgAgAFAAcgBvAGYAaQBsAGUAAHRleHQAAAAAQ29weXJpZ2h0IEFwcGxlIElu +Yy4sIDIwMTIAAFhZWiAAAAAAAADzUQABAAAAARbMY3VydgAAAAAAAAQAAAAABQAKAA8AFAAZAB4AIwAo +AC0AMgA3ADsAQABFAEoATwBUAFkAXgBjAGgAbQByAHcAfACBAIYAiwCQAJUAmgCfAKQAqQCuALIAtwC8 +AMEAxgDLANAA1QDbAOAA5QDrAPAA9gD7AQEBBwENARMBGQEfASUBKwEyATgBPgFFAUwBUgFZAWABZwFu +AXUBfAGDAYsBkgGaAaEBqQGxAbkBwQHJAdEB2QHhAekB8gH6AgMCDAIUAh0CJgIvAjgCQQJLAlQCXQJn +AnECegKEAo4CmAKiAqwCtgLBAssC1QLgAusC9QMAAwsDFgMhAy0DOANDA08DWgNmA3IDfgOKA5YDogOu +A7oDxwPTA+AD7AP5BAYEEwQgBC0EOwRIBFUEYwRxBH4EjASaBKgEtgTEBNME4QTwBP4FDQUcBSsFOgVJ +BVgFZwV3BYYFlgWmBbUFxQXVBeUF9gYGBhYGJwY3BkgGWQZqBnsGjAadBq8GwAbRBuMG9QcHBxkHKwc9 +B08HYQd0B4YHmQesB78H0gflB/gICwgfCDIIRghaCG4IggiWCKoIvgjSCOcI+wkQCSUJOglPCWQJeQmP +CaQJugnPCeUJ+woRCicKPQpUCmoKgQqYCq4KxQrcCvMLCwsiCzkLUQtpC4ALmAuwC8gL4Qv5DBIMKgxD +DFwMdQyODKcMwAzZDPMNDQ0mDUANWg10DY4NqQ3DDd4N+A4TDi4OSQ5kDn8Omw62DtIO7g8JDyUPQQ9e +D3oPlg+zD88P7BAJECYQQxBhEH4QmxC5ENcQ9RETETERTxFtEYwRqhHJEegSBxImEkUSZBKEEqMSwxLj +EwMTIxNDE2MTgxOkE8UT5RQGFCcUSRRqFIsUrRTOFPAVEhU0FVYVeBWbFb0V4BYDFiYWSRZsFo8WshbW +FvoXHRdBF2UXiReuF9IX9xgbGEAYZRiKGK8Y1Rj6GSAZRRlrGZEZtxndGgQaKhpRGncanhrFGuwbFBs7 +G2MbihuyG9ocAhwqHFIcexyjHMwc9R0eHUcdcB2ZHcMd7B4WHkAeah6UHr4e6R8THz4faR+UH78f6iAV +IEEgbCCYIMQg8CEcIUghdSGhIc4h+yInIlUigiKvIt0jCiM4I2YjlCPCI/AkHyRNJHwkqyTaJQklOCVo +JZclxyX3JicmVyaHJrcm6CcYJ0kneierJ9woDSg/KHEooijUKQYpOClrKZ0p0CoCKjUqaCqbKs8rAis2 +K2krnSvRLAUsOSxuLKIs1y0MLUEtdi2rLeEuFi5MLoIuty7uLyQvWi+RL8cv/jA1MGwwpDDbMRIxSjGC +Mbox8jIqMmMymzLUMw0zRjN/M7gz8TQrNGU0njTYNRM1TTWHNcI1/TY3NnI2rjbpNyQ3YDecN9c4FDhQ +OIw4yDkFOUI5fzm8Ofk6Njp0OrI67zstO2s7qjvoPCc8ZTykPOM9Ij1hPaE94D4gPmA+oD7gPyE/YT+i +P+JAI0BkQKZA50EpQWpBrEHuQjBCckK1QvdDOkN9Q8BEA0RHRIpEzkUSRVVFmkXeRiJGZ0arRvBHNUd7 +R8BIBUhLSJFI10kdSWNJqUnwSjdKfUrESwxLU0uaS+JMKkxyTLpNAk1KTZNN3E4lTm5Ot08AT0lPk0/d +UCdQcVC7UQZRUFGbUeZSMVJ8UsdTE1NfU6pT9lRCVI9U21UoVXVVwlYPVlxWqVb3V0RXklfgWC9YfVjL +WRpZaVm4WgdaVlqmWvVbRVuVW+VcNVyGXNZdJ114XcleGl5sXr1fD19hX7NgBWBXYKpg/GFPYaJh9WJJ +Ypxi8GNDY5dj62RAZJRk6WU9ZZJl52Y9ZpJm6Gc9Z5Nn6Wg/aJZo7GlDaZpp8WpIap9q92tPa6dr/2xX +bK9tCG1gbbluEm5rbsRvHm94b9FwK3CGcOBxOnGVcfByS3KmcwFzXXO4dBR0cHTMdSh1hXXhdj52m3b4 +d1Z3s3gReG54zHkqeYl553pGeqV7BHtje8J8IXyBfOF9QX2hfgF+Yn7CfyN/hH/lgEeAqIEKgWuBzYIw +gpKC9INXg7qEHYSAhOOFR4Wrhg6GcobXhzuHn4gEiGmIzokziZmJ/opkisqLMIuWi/yMY4zKjTGNmI3/ +jmaOzo82j56QBpBukNaRP5GokhGSepLjk02TtpQglIqU9JVflcmWNJaflwqXdZfgmEyYuJkkmZCZ/Jpo +mtWbQpuvnByciZz3nWSd0p5Anq6fHZ+Ln/qgaaDYoUehtqImopajBqN2o+akVqTHpTilqaYapoum/adu +p+CoUqjEqTepqaocqo+rAqt1q+msXKzQrUStuK4trqGvFq+LsACwdbDqsWCx1rJLssKzOLOutCW0nLUT +tYq2AbZ5tvC3aLfguFm40blKucK6O7q1uy67p7whvJu9Fb2Pvgq+hL7/v3q/9cBwwOzBZ8Hjwl/C28NY +w9TEUcTOxUvFyMZGxsPHQce/yD3IvMk6ybnKOMq3yzbLtsw1zLXNNc21zjbOts83z7jQOdC60TzRvtI/ +0sHTRNPG1EnUy9VO1dHWVdbY11zX4Nhk2OjZbNnx2nba+9uA3AXcit0Q3ZbeHN6i3ynfr+A24L3hROHM +4lPi2+Nj4+vkc+T85YTmDeaW5x/nqegy6LzpRunQ6lvq5etw6/vshu0R7ZzuKO6070DvzPBY8OXxcvH/ +8ozzGfOn9DT0wvVQ9d72bfb794r4Gfio+Tj5x/pX+uf7d/wH/Jj9Kf26/kv+3P9t///SLS4vMFokY2xh +c3NuYW1lWCRjbGFzc2VzXxAQTlNCaXRtYXBJbWFnZVJlcKMvMTJaTlNJbWFnZVJlcFhOU09iamVjdNIt +LjQ1V05TQXJyYXmiNDLSLS43OF5OU011dGFibGVBcnJheaM3NDLTOjsOPD0+V05TV2hpdGVcTlNDb2xv +clNwYWNlRDAgMAAQA4AM0i0uQEFXTlNDb2xvcqJAMtItLkNEV05TSW1hZ2WiQzIACAARABoAJAApADIA +NwBJAEwAUQBTAGIAaAB3AH4AhQCSAJkApQCtALwAvgDAAMUAxwDJAMsAzQDUANkA5ADmAOgA6gDvAPIA +9AD2APgA/wEWATIBNAE2E6YTqxO2E78T0hPWE+ET6hPvE/cT+hP/FA4UEhQZFCEULhQzFDUUNxQ8FEQU +RxRMFFQAAAAAAAACAQAAAAAAAABFAAAAAAAAAAAAAAAAAAAUVw diff --git a/i18n/src/main/resources/ar.lproj/Command.xib b/i18n/src/main/resources/ar.lproj/Command.xib index 66d8c3ce68..26f3513fab 100644 --- a/i18n/src/main/resources/ar.lproj/Command.xib +++ b/i18n/src/main/resources/ar.lproj/Command.xib @@ -1,7 +1,7 @@ - + - + diff --git a/i18n/src/main/resources/ar.lproj/Connection.xib b/i18n/src/main/resources/ar.lproj/Connection.xib index 486c09735d..c25cefe91d 100644 --- a/i18n/src/main/resources/ar.lproj/Connection.xib +++ b/i18n/src/main/resources/ar.lproj/Connection.xib @@ -1,7 +1,7 @@ - + - + @@ -76,7 +76,7 @@ - + diff --git a/i18n/src/main/resources/ar.lproj/Info.xib b/i18n/src/main/resources/ar.lproj/Info.xib index 59dbf861b4..63428417eb 100644 --- a/i18n/src/main/resources/ar.lproj/Info.xib +++ b/i18n/src/main/resources/ar.lproj/Info.xib @@ -1,7 +1,7 @@ - + - + @@ -89,7 +89,7 @@ - + @@ -1017,21 +1017,21 @@ - + - + - + @@ -1121,21 +1121,21 @@ - + - + - + @@ -1250,27 +1250,27 @@ diff --git a/i18n/src/main/resources/ar.lproj/Login.strings b/i18n/src/main/resources/ar.lproj/Login.strings index 7d4cb5b0a8..421bf073b3 100644 Binary files a/i18n/src/main/resources/ar.lproj/Login.strings and b/i18n/src/main/resources/ar.lproj/Login.strings differ diff --git a/i18n/src/main/resources/ar.lproj/Main.xib b/i18n/src/main/resources/ar.lproj/Main.xib index b9f69cd53e..378d61abd2 100644 --- a/i18n/src/main/resources/ar.lproj/Main.xib +++ b/i18n/src/main/resources/ar.lproj/Main.xib @@ -1,7 +1,7 @@ - + - + @@ -159,19 +159,19 @@ - + - + - + @@ -321,12 +321,12 @@ CA - + - + @@ -566,6 +566,10 @@ CA - + + + + + diff --git a/i18n/src/main/resources/ar.lproj/Preferences.xib b/i18n/src/main/resources/ar.lproj/Preferences.xib index 18df7164b6..c8c1350fe1 100644 --- a/i18n/src/main/resources/ar.lproj/Preferences.xib +++ b/i18n/src/main/resources/ar.lproj/Preferences.xib @@ -1,7 +1,7 @@ - + - + @@ -266,7 +266,7 @@ - + @@ -307,7 +307,7 @@ - + @@ -538,7 +538,7 @@ - + @@ -578,7 +578,7 @@ - + @@ -613,7 +613,7 @@ - + @@ -642,7 +642,7 @@ - + @@ -1218,7 +1218,7 @@ - + @@ -1250,7 +1250,7 @@ - + @@ -1290,7 +1290,7 @@ - + @@ -1363,7 +1363,7 @@ - + @@ -1558,7 +1558,7 @@ - + @@ -1618,7 +1618,7 @@ - + @@ -1643,7 +1643,7 @@ - + @@ -1685,7 +1685,7 @@ - + @@ -1713,7 +1713,7 @@ - + @@ -1741,7 +1741,7 @@ - + @@ -1770,7 +1770,7 @@ - + @@ -1805,7 +1805,7 @@ - + @@ -1888,7 +1888,7 @@ - + @@ -1947,7 +1947,7 @@ - + @@ -1975,7 +1975,7 @@ - + @@ -2004,7 +2004,7 @@ - + diff --git a/i18n/src/main/resources/ar.lproj/Profiles.xib b/i18n/src/main/resources/ar.lproj/Profiles.xib index 0664670947..f3f89fd684 100644 --- a/i18n/src/main/resources/ar.lproj/Profiles.xib +++ b/i18n/src/main/resources/ar.lproj/Profiles.xib @@ -1,7 +1,7 @@ - + - + diff --git a/i18n/src/main/resources/ar.lproj/Prompt.xib b/i18n/src/main/resources/ar.lproj/Prompt.xib index c8323a3bf3..e8ba13e427 100644 --- a/i18n/src/main/resources/ar.lproj/Prompt.xib +++ b/i18n/src/main/resources/ar.lproj/Prompt.xib @@ -1,7 +1,7 @@ - + - + @@ -95,7 +95,7 @@ Gw - + diff --git a/i18n/src/main/resources/ar.lproj/S3.strings b/i18n/src/main/resources/ar.lproj/S3.strings index 496867e939..c4fa0ca3ad 100644 Binary files a/i18n/src/main/resources/ar.lproj/S3.strings and b/i18n/src/main/resources/ar.lproj/S3.strings differ diff --git a/i18n/src/main/resources/ar.lproj/Updater.strings b/i18n/src/main/resources/ar.lproj/Updater.strings index f67cbd6375..085d559596 100644 Binary files a/i18n/src/main/resources/ar.lproj/Updater.strings and b/i18n/src/main/resources/ar.lproj/Updater.strings differ diff --git a/i18n/src/main/resources/bg.lproj/Bookmark.xib b/i18n/src/main/resources/bg.lproj/Bookmark.xib index 76d42b70d2..38be43ca94 100644 --- a/i18n/src/main/resources/bg.lproj/Bookmark.xib +++ b/i18n/src/main/resources/bg.lproj/Bookmark.xib @@ -1,7 +1,7 @@ - + - + @@ -92,7 +92,7 @@ - + @@ -489,95 +489,95 @@ YnBsaXN0MDDUAQIDBAUGBwpYJHZlcnNpb25ZJGFyY2hpdmVyVCR0b3BYJG9iamVjdHMSAAGGoF8QD05T -S2V5ZWRBcmNoaXZlctEICVRyb290gAGuCwwZGh8UJCkqMTQ3PUBVJG51bGzWDQ4PEBESExQVFhcYVk5T -U2l6ZV5OU1Jlc2l6aW5nTW9kZVYkY2xhc3NcTlNJbWFnZUZsYWdzVk5TUmVwc1dOU0NvbG9ygAIQAIAN -EiDDAACAA4ALVnsxLCAxfdIbDxweWk5TLm9iamVjdHOhHYAEgArSGw8gI6IhIoAFgAaACdMPJSYnKBRf -EBROU1RJRkZSZXByZXNlbnRhdGlvbl8QGU5TSW50ZXJuYWxMYXlvdXREaXJlY3Rpb26ACIAHTxESbE1N -ACoAAAAKAAAAEAEAAAMAAAABAAEAAAEBAAMAAAABAAEAAAECAAMAAAACAAgACAEDAAMAAAABAAEAAAEG -AAMAAAABAAEAAAEKAAMAAAABAAEAAAERAAQAAAABAAAACAESAAMAAAABAAEAAAEVAAMAAAABAAIAAAEW -AAMAAAABAAEAAAEXAAQAAAABAAAAAgEcAAMAAAABAAEAAAEoAAMAAAABAAIAAAFSAAMAAAABAAEAAAFT -AAMAAAACAAEAAYdzAAcAABGcAAAA0AAAAAAAABGcYXBwbAIAAABtbnRyR1JBWVhZWiAH3AAIABcADwAu -AA9hY3NwQVBQTAAAAABub25lAAAAAAAAAAAAAAAAAAAAAAAA9tYAAQAAAADTLWFwcGwAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAVkZXNjAAAAwAAAAHlkc2NtAAABPAAA -CBpjcHJ0AAAJWAAAACN3dHB0AAAJfAAAABRrVFJDAAAJkAAACAxkZXNjAAAAAAAAAB9HZW5lcmljIEdy -YXkgR2FtbWEgMi4yIFByb2ZpbGUAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAbWx1YwAAAAAAAAAfAAAADHNr -U0sAAAAuAAABhGRhREsAAAA6AAABsmNhRVMAAAA4AAAB7HZpVk4AAABAAAACJHB0QlIAAABKAAACZHVr -VUEAAAAsAAACrmZyRlUAAAA+AAAC2mh1SFUAAAA0AAADGHpoVFcAAAAaAAADTGtvS1IAAAAiAAADZm5i -Tk8AAAA6AAADiGNzQ1oAAAAoAAADwmhlSUwAAAAkAAAD6nJvUk8AAAAqAAAEDmRlREUAAABOAAAEOGl0 -SVQAAABOAAAEhnN2U0UAAAA4AAAE1HpoQ04AAAAaAAAFDGphSlAAAAAmAAAFJmVsR1IAAAAqAAAFTHB0 -UE8AAABSAAAFdm5sTkwAAABAAAAFyGVzRVMAAABMAAAGCHRoVEgAAAAyAAAGVHRyVFIAAAAkAAAGhmZp -RkkAAABGAAAGqmhySFIAAAA+AAAG8HBsUEwAAABKAAAHLmFyRUcAAAAsAAAHeHJ1UlUAAAA6AAAHpGVu -VVMAAAA8AAAH3gBWAWEAZQBvAGIAZQBjAG4A4QAgAHMAaQB2AOEAIABnAGEAbQBhACAAMgAsADIARwBl -AG4AZQByAGkAcwBrACAAZwByAOUAIAAyACwAMgAgAGcAYQBtAG0AYQAtAHAAcgBvAGYAaQBsAEcAYQBt -AG0AYQAgAGQAZQAgAGcAcgBpAHMAbwBzACAAZwBlAG4A6AByAGkAYwBhACAAMgAuADIAQx6lAHUAIABo -AOwAbgBoACAATQDgAHUAIAB4AOEAbQAgAEMAaAB1AG4AZwAgAEcAYQBtAG0AYQAgADIALgAyAFAAZQBy -AGYAaQBsACAARwBlAG4A6QByAGkAYwBvACAAZABhACAARwBhAG0AYQAgAGQAZQAgAEMAaQBuAHoAYQBz -ACAAMgAsADIEFwQwBDMEMAQ7BEwEPQQwACAARwByAGEAeQAtBDMEMAQ8BDAAIAAyAC4AMgBQAHIAbwBm -AGkAbAAgAGcA6QBuAOkAcgBpAHEAdQBlACAAZwByAGkAcwAgAGcAYQBtAG0AYQAgADIALAAyAMEAbAB0 -AGEAbADhAG4AbwBzACAAcwB6APwAcgBrAGUAIABnAGEAbQBtAGEAIAAyAC4AMpAadShwcJaOUUlepgAy -AC4AMoJyX2ljz4/wx3y8GAAg1ozAyQAgrBC5yAAgADIALgAyACDVBLhc0wzHfABHAGUAbgBlAHIAaQBz -AGsAIABnAHIA5QAgAGcAYQBtAG0AYQAgADIALAAyAC0AcAByAG8AZgBpAGwATwBiAGUAYwBuAOEAIAFh -AGUAZADhACAAZwBhAG0AYQAgADIALgAyBdIF0AXeBdQAIAXQBeQF1QXoACAF2wXcBdwF2QAgADIALgAy -AEcAYQBtAGEAIABnAHIAaQAgAGcAZQBuAGUAcgBpAGMBAwAgADIALAAyAEEAbABsAGcAZQBtAGUAaQBu -AGUAcwAgAEcAcgBhAHUAcwB0AHUAZgBlAG4ALQBQAHIAbwBmAGkAbAAgAEcAYQBtAG0AYQAgADIALAAy -AFAAcgBvAGYAaQBsAG8AIABnAHIAaQBnAGkAbwAgAGcAZQBuAGUAcgBpAGMAbwAgAGQAZQBsAGwAYQAg -AGcAYQBtAG0AYQAgADIALAAyAEcAZQBuAGUAcgBpAHMAawAgAGcAcgDlACAAMgAsADIAIABnAGEAbQBt -AGEAcAByAG8AZgBpAGxmbpAacHBepnz7ZXAAMgAuADJjz4/wZYdO9k4AgiwwsDDsMKQwrDDzMN4AIAAy -AC4AMgAgMNcw7TDVMKEwpDDrA5MDtQO9A7kDugPMACADkwO6A8EDuQAgA5MDrAO8A7wDsQAgADIALgAy -AFAAZQByAGYAaQBsACAAZwBlAG4A6QByAGkAYwBvACAAZABlACAAYwBpAG4AegBlAG4AdABvAHMAIABk -AGEAIABHAGEAbQBtAGEAIAAyACwAMgBBAGwAZwBlAG0AZQBlAG4AIABnAHIAaQBqAHMAIABnAGEAbQBt -AGEAIAAyACwAMgAtAHAAcgBvAGYAaQBlAGwAUABlAHIAZgBpAGwAIABnAGUAbgDpAHIAaQBjAG8AIABk -AGUAIABnAGEAbQBtAGEAIABkAGUAIABnAHIAaQBzAGUAcwAgADIALAAyDiMOMQ4HDioONQ5BDgEOIQ4h -DjIOQA4BDiMOIg5MDhcOMQ5IDicORA4bACAAMgAuADIARwBlAG4AZQBsACAARwByAGkAIABHAGEAbQBh -ACAAMgAsADIAWQBsAGUAaQBuAGUAbgAgAGgAYQByAG0AYQBhAG4AIABnAGEAbQBtAGEAIAAyACwAMgAg -AC0AcAByAG8AZgBpAGkAbABpAEcAZQBuAGUAcgBpAQ0AawBpACAARwByAGEAeQAgAEcAYQBtAG0AYQAg -ADIALgAyACAAcAByAG8AZgBpAGwAVQBuAGkAdwBlAHIAcwBhAGwAbgB5ACAAcAByAG8AZgBpAGwAIABz -AHoAYQByAG8BWwBjAGkAIABnAGEAbQBtAGEAIAAyACwAMgY6BicGRQYnACAAMgAuADIAIAZEBkgGRgAg -BjEGRQYnBi8GSgAgBjkGJwZFBB4EMQRJBDAETwAgBEEENQRABDAETwAgBDMEMAQ8BDwEMAAgADIALAAy -AC0EPwRABD4ERAQ4BDsETABHAGUAbgBlAHIAaQBjACAARwByAGEAeQAgAEcAYQBtAG0AYQAgADIALgAy -ACAAUAByAG8AZgBpAGwAZQAAdGV4dAAAAABDb3B5cmlnaHQgQXBwbGUgSW5jLiwgMjAxMgAAWFlaIAAA -AAAAAPNRAAEAAAABFsxjdXJ2AAAAAAAABAAAAAAFAAoADwAUABkAHgAjACgALQAyADcAOwBAAEUASgBP -AFQAWQBeAGMAaABtAHIAdwB8AIEAhgCLAJAAlQCaAJ8ApACpAK4AsgC3ALwAwQDGAMsA0ADVANsA4ADl -AOsA8AD2APsBAQEHAQ0BEwEZAR8BJQErATIBOAE+AUUBTAFSAVkBYAFnAW4BdQF8AYMBiwGSAZoBoQGp -AbEBuQHBAckB0QHZAeEB6QHyAfoCAwIMAhQCHQImAi8COAJBAksCVAJdAmcCcQJ6AoQCjgKYAqICrAK2 -AsECywLVAuAC6wL1AwADCwMWAyEDLQM4A0MDTwNaA2YDcgN+A4oDlgOiA64DugPHA9MD4APsA/kEBgQT -BCAELQQ7BEgEVQRjBHEEfgSMBJoEqAS2BMQE0wThBPAE/gUNBRwFKwU6BUkFWAVnBXcFhgWWBaYFtQXF -BdUF5QX2BgYGFgYnBjcGSAZZBmoGewaMBp0GrwbABtEG4wb1BwcHGQcrBz0HTwdhB3QHhgeZB6wHvwfS -B+UH+AgLCB8IMghGCFoIbgiCCJYIqgi+CNII5wj7CRAJJQk6CU8JZAl5CY8JpAm6Cc8J5Qn7ChEKJwo9 -ClQKagqBCpgKrgrFCtwK8wsLCyILOQtRC2kLgAuYC7ALyAvhC/kMEgwqDEMMXAx1DI4MpwzADNkM8w0N -DSYNQA1aDXQNjg2pDcMN3g34DhMOLg5JDmQOfw6bDrYO0g7uDwkPJQ9BD14Peg+WD7MPzw/sEAkQJhBD -EGEQfhCbELkQ1xD1ERMRMRFPEW0RjBGqEckR6BIHEiYSRRJkEoQSoxLDEuMTAxMjE0MTYxODE6QTxRPl -FAYUJxRJFGoUixStFM4U8BUSFTQVVhV4FZsVvRXgFgMWJhZJFmwWjxayFtYW+hcdF0EXZReJF64X0hf3 -GBsYQBhlGIoYrxjVGPoZIBlFGWsZkRm3Gd0aBBoqGlEadxqeGsUa7BsUGzsbYxuKG7Ib2hwCHCocUhx7 -HKMczBz1HR4dRx1wHZkdwx3sHhYeQB5qHpQevh7pHxMfPh9pH5Qfvx/qIBUgQSBsIJggxCDwIRwhSCF1 -IaEhziH7IiciVSKCIq8i3SMKIzgjZiOUI8Ij8CQfJE0kfCSrJNolCSU4JWgllyXHJfcmJyZXJocmtybo -JxgnSSd6J6sn3CgNKD8ocSiiKNQpBik4KWspnSnQKgIqNSpoKpsqzysCKzYraSudK9EsBSw5LG4soizX -LQwtQS12Last4S4WLkwugi63Lu4vJC9aL5Evxy/+MDUwbDCkMNsxEjFKMYIxujHyMioyYzKbMtQzDTNG -M38zuDPxNCs0ZTSeNNg1EzVNNYc1wjX9Njc2cjauNuk3JDdgN5w31zgUOFA4jDjIOQU5Qjl/Obw5+To2 -OnQ6sjrvOy07azuqO+g8JzxlPKQ84z0iPWE9oT3gPiA+YD6gPuA/IT9hP6I/4kAjQGRApkDnQSlBakGs -Qe5CMEJyQrVC90M6Q31DwEQDREdEikTORRJFVUWaRd5GIkZnRqtG8Ec1R3tHwEgFSEtIkUjXSR1JY0mp -SfBKN0p9SsRLDEtTS5pL4kwqTHJMuk0CTUpNk03cTiVObk63TwBPSU+TT91QJ1BxULtRBlFQUZtR5lIx -UnxSx1MTU19TqlP2VEJUj1TbVShVdVXCVg9WXFapVvdXRFeSV+BYL1h9WMtZGllpWbhaB1pWWqZa9VtF -W5Vb5Vw1XIZc1l0nXXhdyV4aXmxevV8PX2Ffs2AFYFdgqmD8YU9homH1YklinGLwY0Njl2PrZEBklGTp -ZT1lkmXnZj1mkmboZz1nk2fpaD9olmjsaUNpmmnxakhqn2r3a09rp2v/bFdsr20IbWBtuW4SbmtuxG8e -b3hv0XArcIZw4HE6cZVx8HJLcqZzAXNdc7h0FHRwdMx1KHWFdeF2Pnabdvh3VnezeBF4bnjMeSp5iXnn -ekZ6pXsEe2N7wnwhfIF84X1BfaF+AX5ifsJ/I3+Ef+WAR4CogQqBa4HNgjCCkoL0g1eDuoQdhICE44VH -hauGDoZyhteHO4efiASIaYjOiTOJmYn+imSKyoswi5aL/IxjjMqNMY2Yjf+OZo7OjzaPnpAGkG6Q1pE/ -kaiSEZJ6kuOTTZO2lCCUipT0lV+VyZY0lp+XCpd1l+CYTJi4mSSZkJn8mmia1ZtCm6+cHJyJnPedZJ3S -nkCerp8dn4uf+qBpoNihR6G2oiailqMGo3aj5qRWpMelOKWpphqmi6b9p26n4KhSqMSpN6mpqhyqj6sC -q3Wr6axcrNCtRK24ri2uoa8Wr4uwALB1sOqxYLHWskuywrM4s660JbSctRO1irYBtnm28Ldot+C4WbjR -uUq5wro7urW7LrunvCG8m70VvY++Cr6Evv+/er/1wHDA7MFnwePCX8Lbw1jD1MRRxM7FS8XIxkbGw8dB -x7/IPci8yTrJuco4yrfLNsu2zDXMtc01zbXONs62zzfPuNA50LrRPNG+0j/SwdNE08bUSdTL1U7V0dZV -1tjXXNfg2GTY6Nls2fHadtr724DcBdyK3RDdlt4c3qLfKd+v4DbgveFE4cziU+Lb42Pj6+Rz5PzlhOYN -5pbnH+ep6DLovOlG6dDqW+rl63Dr++yG7RHtnO4o7rTvQO/M8Fjw5fFy8f/yjPMZ86f0NPTC9VD13vZt -9vv3ivgZ+Kj5OPnH+lf65/t3/Af8mP0p/br+S/7c/23//9IrLC0uWiRjbGFzc25hbWVYJGNsYXNzZXNf -EBBOU0JpdG1hcEltYWdlUmVwoy0vMFpOU0ltYWdlUmVwWE5TT2JqZWN00issMjNXTlNBcnJheaIyMNIr -LDU2Xk5TTXV0YWJsZUFycmF5ozUyMNM4OQ86OzxXTlNXaGl0ZVxOU0NvbG9yU3BhY2VEMCAwABADgAzS -Kyw+P1dOU0NvbG9yoj4w0issQUJXTlNJbWFnZaJBMAAIABEAGgAkACkAMgA3AEkATABRAFMAYgBoAHUA -fACLAJIAnwCmAK4AsACyALQAuQC7AL0AxADJANQA1gDYANoA3wDiAOQA5gDoAO8BBgEiASQBJhOWE5sT -phOvE8ITxhPRE9oT3xPnE+oT7xP+FAIUCRQRFB4UIxQlFCcULBQ0FDcUPBREAAAAAAAAAgEAAAAAAAAA -QwAAAAAAAAAAAAAAAAAAFEc +S2V5ZWRBcmNoaXZlctEICVRyb290gAGuCwwbHCEaJissMzY5P0JVJG51bGzXDQ4PEBESExQVFhcYGRpW +TlNTaXplViRjbGFzc1xOU0ltYWdlRmxhZ3NWTlNSZXBzW05TVGludENvbG9yV05TQ29sb3JeTlNSZXNp +emluZ01vZGWAAoANEiDDAACAA4AAgAsQAFZ7MSwgMX3SHQ4eIFpOUy5vYmplY3RzoR+ABIAK0h0OIiWi +IySABYAGgAnTDicoKSoaXxAUTlNUSUZGUmVwcmVzZW50YXRpb25fEBlOU0ludGVybmFsTGF5b3V0RGly +ZWN0aW9ugAiAB08REmxNTQAqAAAACgAAABABAAADAAAAAQABAAABAQADAAAAAQABAAABAgADAAAAAgAI +AAgBAwADAAAAAQABAAABBgADAAAAAQABAAABCgADAAAAAQABAAABEQAEAAAAAQAAAAgBEgADAAAAAQAB +AAABFQADAAAAAQACAAABFgADAAAAAQABAAABFwAEAAAAAQAAAAIBHAADAAAAAQABAAABKAADAAAAAQAC +AAABUgADAAAAAQABAAABUwADAAAAAgABAAGHcwAHAAARnAAAANAAAAAAAAARnGFwcGwCAAAAbW50ckdS +QVlYWVogB9wACAAXAA8ALgAPYWNzcEFQUEwAAAAAbm9uZQAAAAAAAAAAAAAAAAAAAAAAAPbWAAEAAAAA +0y1hcHBsAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFZGVzYwAA +AMAAAAB5ZHNjbQAAATwAAAgaY3BydAAACVgAAAAjd3RwdAAACXwAAAAUa1RSQwAACZAAAAgMZGVzYwAA +AAAAAAAfR2VuZXJpYyBHcmF5IEdhbW1hIDIuMiBQcm9maWxlAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAG1s +dWMAAAAAAAAAHwAAAAxza1NLAAAALgAAAYRkYURLAAAAOgAAAbJjYUVTAAAAOAAAAex2aVZOAAAAQAAA +AiRwdEJSAAAASgAAAmR1a1VBAAAALAAAAq5mckZVAAAAPgAAAtpodUhVAAAANAAAAxh6aFRXAAAAGgAA +A0xrb0tSAAAAIgAAA2ZuYk5PAAAAOgAAA4hjc0NaAAAAKAAAA8JoZUlMAAAAJAAAA+pyb1JPAAAAKgAA +BA5kZURFAAAATgAABDhpdElUAAAATgAABIZzdlNFAAAAOAAABNR6aENOAAAAGgAABQxqYUpQAAAAJgAA +BSZlbEdSAAAAKgAABUxwdFBPAAAAUgAABXZubE5MAAAAQAAABchlc0VTAAAATAAABgh0aFRIAAAAMgAA +BlR0clRSAAAAJAAABoZmaUZJAAAARgAABqpockhSAAAAPgAABvBwbFBMAAAASgAABy5hckVHAAAALAAA +B3hydVJVAAAAOgAAB6RlblVTAAAAPAAAB94AVgFhAGUAbwBiAGUAYwBuAOEAIABzAGkAdgDhACAAZwBh +AG0AYQAgADIALAAyAEcAZQBuAGUAcgBpAHMAawAgAGcAcgDlACAAMgAsADIAIABnAGEAbQBtAGEALQBw +AHIAbwBmAGkAbABHAGEAbQBtAGEAIABkAGUAIABnAHIAaQBzAG8AcwAgAGcAZQBuAOgAcgBpAGMAYQAg +ADIALgAyAEMepQB1ACAAaADsAG4AaAAgAE0A4AB1ACAAeADhAG0AIABDAGgAdQBuAGcAIABHAGEAbQBt +AGEAIAAyAC4AMgBQAGUAcgBmAGkAbAAgAEcAZQBuAOkAcgBpAGMAbwAgAGQAYQAgAEcAYQBtAGEAIABk +AGUAIABDAGkAbgB6AGEAcwAgADIALAAyBBcEMAQzBDAEOwRMBD0EMAAgAEcAcgBhAHkALQQzBDAEPAQw +ACAAMgAuADIAUAByAG8AZgBpAGwAIABnAOkAbgDpAHIAaQBxAHUAZQAgAGcAcgBpAHMAIABnAGEAbQBt +AGEAIAAyACwAMgDBAGwAdABhAGwA4QBuAG8AcwAgAHMAegD8AHIAawBlACAAZwBhAG0AbQBhACAAMgAu +ADKQGnUocHCWjlFJXqYAMgAuADKCcl9pY8+P8Md8vBgAINaMwMkAIKwQucgAIAAyAC4AMgAg1QS4XNMM +x3wARwBlAG4AZQByAGkAcwBrACAAZwByAOUAIABnAGEAbQBtAGEAIAAyACwAMgAtAHAAcgBvAGYAaQBs +AE8AYgBlAGMAbgDhACABYQBlAGQA4QAgAGcAYQBtAGEAIAAyAC4AMgXSBdAF3gXUACAF0AXkBdUF6AAg +BdsF3AXcBdkAIAAyAC4AMgBHAGEAbQBhACAAZwByAGkAIABnAGUAbgBlAHIAaQBjAQMAIAAyACwAMgBB +AGwAbABnAGUAbQBlAGkAbgBlAHMAIABHAHIAYQB1AHMAdAB1AGYAZQBuAC0AUAByAG8AZgBpAGwAIABH +AGEAbQBtAGEAIAAyACwAMgBQAHIAbwBmAGkAbABvACAAZwByAGkAZwBpAG8AIABnAGUAbgBlAHIAaQBj +AG8AIABkAGUAbABsAGEAIABnAGEAbQBtAGEAIAAyACwAMgBHAGUAbgBlAHIAaQBzAGsAIABnAHIA5QAg +ADIALAAyACAAZwBhAG0AbQBhAHAAcgBvAGYAaQBsZm6QGnBwXqZ8+2VwADIALgAyY8+P8GWHTvZOAIIs +MLAw7DCkMKww8zDeACAAMgAuADIAIDDXMO0w1TChMKQw6wOTA7UDvQO5A7oDzAAgA5MDugPBA7kAIAOT +A6wDvAO8A7EAIAAyAC4AMgBQAGUAcgBmAGkAbAAgAGcAZQBuAOkAcgBpAGMAbwAgAGQAZQAgAGMAaQBu +AHoAZQBuAHQAbwBzACAAZABhACAARwBhAG0AbQBhACAAMgAsADIAQQBsAGcAZQBtAGUAZQBuACAAZwBy +AGkAagBzACAAZwBhAG0AbQBhACAAMgAsADIALQBwAHIAbwBmAGkAZQBsAFAAZQByAGYAaQBsACAAZwBl +AG4A6QByAGkAYwBvACAAZABlACAAZwBhAG0AbQBhACAAZABlACAAZwByAGkAcwBlAHMAIAAyACwAMg4j +DjEOBw4qDjUOQQ4BDiEOIQ4yDkAOAQ4jDiIOTA4XDjEOSA4nDkQOGwAgADIALgAyAEcAZQBuAGUAbAAg +AEcAcgBpACAARwBhAG0AYQAgADIALAAyAFkAbABlAGkAbgBlAG4AIABoAGEAcgBtAGEAYQBuACAAZwBh +AG0AbQBhACAAMgAsADIAIAAtAHAAcgBvAGYAaQBpAGwAaQBHAGUAbgBlAHIAaQENAGsAaQAgAEcAcgBh +AHkAIABHAGEAbQBtAGEAIAAyAC4AMgAgAHAAcgBvAGYAaQBsAFUAbgBpAHcAZQByAHMAYQBsAG4AeQAg +AHAAcgBvAGYAaQBsACAAcwB6AGEAcgBvAVsAYwBpACAAZwBhAG0AbQBhACAAMgAsADIGOgYnBkUGJwAg +ADIALgAyACAGRAZIBkYAIAYxBkUGJwYvBkoAIAY5BicGRQQeBDEESQQwBE8AIARBBDUEQAQwBE8AIAQz +BDAEPAQ8BDAAIAAyACwAMgAtBD8EQAQ+BEQEOAQ7BEwARwBlAG4AZQByAGkAYwAgAEcAcgBhAHkAIABH +AGEAbQBtAGEAIAAyAC4AMgAgAFAAcgBvAGYAaQBsAGUAAHRleHQAAAAAQ29weXJpZ2h0IEFwcGxlIElu +Yy4sIDIwMTIAAFhZWiAAAAAAAADzUQABAAAAARbMY3VydgAAAAAAAAQAAAAABQAKAA8AFAAZAB4AIwAo +AC0AMgA3ADsAQABFAEoATwBUAFkAXgBjAGgAbQByAHcAfACBAIYAiwCQAJUAmgCfAKQAqQCuALIAtwC8 +AMEAxgDLANAA1QDbAOAA5QDrAPAA9gD7AQEBBwENARMBGQEfASUBKwEyATgBPgFFAUwBUgFZAWABZwFu +AXUBfAGDAYsBkgGaAaEBqQGxAbkBwQHJAdEB2QHhAekB8gH6AgMCDAIUAh0CJgIvAjgCQQJLAlQCXQJn +AnECegKEAo4CmAKiAqwCtgLBAssC1QLgAusC9QMAAwsDFgMhAy0DOANDA08DWgNmA3IDfgOKA5YDogOu +A7oDxwPTA+AD7AP5BAYEEwQgBC0EOwRIBFUEYwRxBH4EjASaBKgEtgTEBNME4QTwBP4FDQUcBSsFOgVJ +BVgFZwV3BYYFlgWmBbUFxQXVBeUF9gYGBhYGJwY3BkgGWQZqBnsGjAadBq8GwAbRBuMG9QcHBxkHKwc9 +B08HYQd0B4YHmQesB78H0gflB/gICwgfCDIIRghaCG4IggiWCKoIvgjSCOcI+wkQCSUJOglPCWQJeQmP +CaQJugnPCeUJ+woRCicKPQpUCmoKgQqYCq4KxQrcCvMLCwsiCzkLUQtpC4ALmAuwC8gL4Qv5DBIMKgxD +DFwMdQyODKcMwAzZDPMNDQ0mDUANWg10DY4NqQ3DDd4N+A4TDi4OSQ5kDn8Omw62DtIO7g8JDyUPQQ9e +D3oPlg+zD88P7BAJECYQQxBhEH4QmxC5ENcQ9RETETERTxFtEYwRqhHJEegSBxImEkUSZBKEEqMSwxLj +EwMTIxNDE2MTgxOkE8UT5RQGFCcUSRRqFIsUrRTOFPAVEhU0FVYVeBWbFb0V4BYDFiYWSRZsFo8WshbW +FvoXHRdBF2UXiReuF9IX9xgbGEAYZRiKGK8Y1Rj6GSAZRRlrGZEZtxndGgQaKhpRGncanhrFGuwbFBs7 +G2MbihuyG9ocAhwqHFIcexyjHMwc9R0eHUcdcB2ZHcMd7B4WHkAeah6UHr4e6R8THz4faR+UH78f6iAV +IEEgbCCYIMQg8CEcIUghdSGhIc4h+yInIlUigiKvIt0jCiM4I2YjlCPCI/AkHyRNJHwkqyTaJQklOCVo +JZclxyX3JicmVyaHJrcm6CcYJ0kneierJ9woDSg/KHEooijUKQYpOClrKZ0p0CoCKjUqaCqbKs8rAis2 +K2krnSvRLAUsOSxuLKIs1y0MLUEtdi2rLeEuFi5MLoIuty7uLyQvWi+RL8cv/jA1MGwwpDDbMRIxSjGC +Mbox8jIqMmMymzLUMw0zRjN/M7gz8TQrNGU0njTYNRM1TTWHNcI1/TY3NnI2rjbpNyQ3YDecN9c4FDhQ +OIw4yDkFOUI5fzm8Ofk6Njp0OrI67zstO2s7qjvoPCc8ZTykPOM9Ij1hPaE94D4gPmA+oD7gPyE/YT+i +P+JAI0BkQKZA50EpQWpBrEHuQjBCckK1QvdDOkN9Q8BEA0RHRIpEzkUSRVVFmkXeRiJGZ0arRvBHNUd7 +R8BIBUhLSJFI10kdSWNJqUnwSjdKfUrESwxLU0uaS+JMKkxyTLpNAk1KTZNN3E4lTm5Ot08AT0lPk0/d +UCdQcVC7UQZRUFGbUeZSMVJ8UsdTE1NfU6pT9lRCVI9U21UoVXVVwlYPVlxWqVb3V0RXklfgWC9YfVjL +WRpZaVm4WgdaVlqmWvVbRVuVW+VcNVyGXNZdJ114XcleGl5sXr1fD19hX7NgBWBXYKpg/GFPYaJh9WJJ +Ypxi8GNDY5dj62RAZJRk6WU9ZZJl52Y9ZpJm6Gc9Z5Nn6Wg/aJZo7GlDaZpp8WpIap9q92tPa6dr/2xX +bK9tCG1gbbluEm5rbsRvHm94b9FwK3CGcOBxOnGVcfByS3KmcwFzXXO4dBR0cHTMdSh1hXXhdj52m3b4 +d1Z3s3gReG54zHkqeYl553pGeqV7BHtje8J8IXyBfOF9QX2hfgF+Yn7CfyN/hH/lgEeAqIEKgWuBzYIw +gpKC9INXg7qEHYSAhOOFR4Wrhg6GcobXhzuHn4gEiGmIzokziZmJ/opkisqLMIuWi/yMY4zKjTGNmI3/ +jmaOzo82j56QBpBukNaRP5GokhGSepLjk02TtpQglIqU9JVflcmWNJaflwqXdZfgmEyYuJkkmZCZ/Jpo +mtWbQpuvnByciZz3nWSd0p5Anq6fHZ+Ln/qgaaDYoUehtqImopajBqN2o+akVqTHpTilqaYapoum/adu +p+CoUqjEqTepqaocqo+rAqt1q+msXKzQrUStuK4trqGvFq+LsACwdbDqsWCx1rJLssKzOLOutCW0nLUT +tYq2AbZ5tvC3aLfguFm40blKucK6O7q1uy67p7whvJu9Fb2Pvgq+hL7/v3q/9cBwwOzBZ8Hjwl/C28NY +w9TEUcTOxUvFyMZGxsPHQce/yD3IvMk6ybnKOMq3yzbLtsw1zLXNNc21zjbOts83z7jQOdC60TzRvtI/ +0sHTRNPG1EnUy9VO1dHWVdbY11zX4Nhk2OjZbNnx2nba+9uA3AXcit0Q3ZbeHN6i3ynfr+A24L3hROHM +4lPi2+Nj4+vkc+T85YTmDeaW5x/nqegy6LzpRunQ6lvq5etw6/vshu0R7ZzuKO6070DvzPBY8OXxcvH/ +8ozzGfOn9DT0wvVQ9d72bfb794r4Gfio+Tj5x/pX+uf7d/wH/Jj9Kf26/kv+3P9t///SLS4vMFokY2xh +c3NuYW1lWCRjbGFzc2VzXxAQTlNCaXRtYXBJbWFnZVJlcKMvMTJaTlNJbWFnZVJlcFhOU09iamVjdNIt +LjQ1V05TQXJyYXmiNDLSLS43OF5OU011dGFibGVBcnJheaM3NDLTOjsOPD0+V05TV2hpdGVcTlNDb2xv +clNwYWNlRDAgMAAQA4AM0i0uQEFXTlNDb2xvcqJAMtItLkNEV05TSW1hZ2WiQzIACAARABoAJAApADIA +NwBJAEwAUQBTAGIAaAB3AH4AhQCSAJkApQCtALwAvgDAAMUAxwDJAMsAzQDUANkA5ADmAOgA6gDvAPIA +9AD2APgA/wEWATIBNAE2E6YTqxO2E78T0hPWE+ET6hPvE/cT+hP/FA4UEhQZFCEULhQzFDUUNxQ8FEQU +RxRMFFQAAAAAAAACAQAAAAAAAABFAAAAAAAAAAAAAAAAAAAUVw diff --git a/i18n/src/main/resources/bg.lproj/Browser.xib b/i18n/src/main/resources/bg.lproj/Browser.xib index e359956899..3268f9c61c 100644 --- a/i18n/src/main/resources/bg.lproj/Browser.xib +++ b/i18n/src/main/resources/bg.lproj/Browser.xib @@ -1,7 +1,7 @@ - + - + @@ -38,7 +38,7 @@ - + @@ -62,27 +62,27 @@ @@ -256,7 +256,7 @@ - + @@ -553,95 +553,95 @@ DQ YnBsaXN0MDDUAQIDBAUGBwpYJHZlcnNpb25ZJGFyY2hpdmVyVCR0b3BYJG9iamVjdHMSAAGGoF8QD05T -S2V5ZWRBcmNoaXZlctEICVRyb290gAGuCwwZGh8UJCkqMTQ3PUBVJG51bGzWDQ4PEBESExQVFhcYVk5T -U2l6ZV5OU1Jlc2l6aW5nTW9kZVYkY2xhc3NcTlNJbWFnZUZsYWdzVk5TUmVwc1dOU0NvbG9ygAIQAIAN -EiDDAACAA4ALVnsxLCAxfdIbDxweWk5TLm9iamVjdHOhHYAEgArSGw8gI6IhIoAFgAaACdMPJSYnKBRf -EBROU1RJRkZSZXByZXNlbnRhdGlvbl8QGU5TSW50ZXJuYWxMYXlvdXREaXJlY3Rpb26ACIAHTxESbE1N -ACoAAAAKAAAAEAEAAAMAAAABAAEAAAEBAAMAAAABAAEAAAECAAMAAAACAAgACAEDAAMAAAABAAEAAAEG -AAMAAAABAAEAAAEKAAMAAAABAAEAAAERAAQAAAABAAAACAESAAMAAAABAAEAAAEVAAMAAAABAAIAAAEW -AAMAAAABAAEAAAEXAAQAAAABAAAAAgEcAAMAAAABAAEAAAEoAAMAAAABAAIAAAFSAAMAAAABAAEAAAFT -AAMAAAACAAEAAYdzAAcAABGcAAAA0AAAAAAAABGcYXBwbAIAAABtbnRyR1JBWVhZWiAH3AAIABcADwAu -AA9hY3NwQVBQTAAAAABub25lAAAAAAAAAAAAAAAAAAAAAAAA9tYAAQAAAADTLWFwcGwAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAVkZXNjAAAAwAAAAHlkc2NtAAABPAAA -CBpjcHJ0AAAJWAAAACN3dHB0AAAJfAAAABRrVFJDAAAJkAAACAxkZXNjAAAAAAAAAB9HZW5lcmljIEdy -YXkgR2FtbWEgMi4yIFByb2ZpbGUAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAbWx1YwAAAAAAAAAfAAAADHNr -U0sAAAAuAAABhGRhREsAAAA6AAABsmNhRVMAAAA4AAAB7HZpVk4AAABAAAACJHB0QlIAAABKAAACZHVr -VUEAAAAsAAACrmZyRlUAAAA+AAAC2mh1SFUAAAA0AAADGHpoVFcAAAAaAAADTGtvS1IAAAAiAAADZm5i -Tk8AAAA6AAADiGNzQ1oAAAAoAAADwmhlSUwAAAAkAAAD6nJvUk8AAAAqAAAEDmRlREUAAABOAAAEOGl0 -SVQAAABOAAAEhnN2U0UAAAA4AAAE1HpoQ04AAAAaAAAFDGphSlAAAAAmAAAFJmVsR1IAAAAqAAAFTHB0 -UE8AAABSAAAFdm5sTkwAAABAAAAFyGVzRVMAAABMAAAGCHRoVEgAAAAyAAAGVHRyVFIAAAAkAAAGhmZp -RkkAAABGAAAGqmhySFIAAAA+AAAG8HBsUEwAAABKAAAHLmFyRUcAAAAsAAAHeHJ1UlUAAAA6AAAHpGVu -VVMAAAA8AAAH3gBWAWEAZQBvAGIAZQBjAG4A4QAgAHMAaQB2AOEAIABnAGEAbQBhACAAMgAsADIARwBl -AG4AZQByAGkAcwBrACAAZwByAOUAIAAyACwAMgAgAGcAYQBtAG0AYQAtAHAAcgBvAGYAaQBsAEcAYQBt -AG0AYQAgAGQAZQAgAGcAcgBpAHMAbwBzACAAZwBlAG4A6AByAGkAYwBhACAAMgAuADIAQx6lAHUAIABo -AOwAbgBoACAATQDgAHUAIAB4AOEAbQAgAEMAaAB1AG4AZwAgAEcAYQBtAG0AYQAgADIALgAyAFAAZQBy -AGYAaQBsACAARwBlAG4A6QByAGkAYwBvACAAZABhACAARwBhAG0AYQAgAGQAZQAgAEMAaQBuAHoAYQBz -ACAAMgAsADIEFwQwBDMEMAQ7BEwEPQQwACAARwByAGEAeQAtBDMEMAQ8BDAAIAAyAC4AMgBQAHIAbwBm -AGkAbAAgAGcA6QBuAOkAcgBpAHEAdQBlACAAZwByAGkAcwAgAGcAYQBtAG0AYQAgADIALAAyAMEAbAB0 -AGEAbADhAG4AbwBzACAAcwB6APwAcgBrAGUAIABnAGEAbQBtAGEAIAAyAC4AMpAadShwcJaOUUlepgAy -AC4AMoJyX2ljz4/wx3y8GAAg1ozAyQAgrBC5yAAgADIALgAyACDVBLhc0wzHfABHAGUAbgBlAHIAaQBz -AGsAIABnAHIA5QAgAGcAYQBtAG0AYQAgADIALAAyAC0AcAByAG8AZgBpAGwATwBiAGUAYwBuAOEAIAFh -AGUAZADhACAAZwBhAG0AYQAgADIALgAyBdIF0AXeBdQAIAXQBeQF1QXoACAF2wXcBdwF2QAgADIALgAy -AEcAYQBtAGEAIABnAHIAaQAgAGcAZQBuAGUAcgBpAGMBAwAgADIALAAyAEEAbABsAGcAZQBtAGUAaQBu -AGUAcwAgAEcAcgBhAHUAcwB0AHUAZgBlAG4ALQBQAHIAbwBmAGkAbAAgAEcAYQBtAG0AYQAgADIALAAy -AFAAcgBvAGYAaQBsAG8AIABnAHIAaQBnAGkAbwAgAGcAZQBuAGUAcgBpAGMAbwAgAGQAZQBsAGwAYQAg -AGcAYQBtAG0AYQAgADIALAAyAEcAZQBuAGUAcgBpAHMAawAgAGcAcgDlACAAMgAsADIAIABnAGEAbQBt -AGEAcAByAG8AZgBpAGxmbpAacHBepnz7ZXAAMgAuADJjz4/wZYdO9k4AgiwwsDDsMKQwrDDzMN4AIAAy -AC4AMgAgMNcw7TDVMKEwpDDrA5MDtQO9A7kDugPMACADkwO6A8EDuQAgA5MDrAO8A7wDsQAgADIALgAy -AFAAZQByAGYAaQBsACAAZwBlAG4A6QByAGkAYwBvACAAZABlACAAYwBpAG4AegBlAG4AdABvAHMAIABk -AGEAIABHAGEAbQBtAGEAIAAyACwAMgBBAGwAZwBlAG0AZQBlAG4AIABnAHIAaQBqAHMAIABnAGEAbQBt -AGEAIAAyACwAMgAtAHAAcgBvAGYAaQBlAGwAUABlAHIAZgBpAGwAIABnAGUAbgDpAHIAaQBjAG8AIABk -AGUAIABnAGEAbQBtAGEAIABkAGUAIABnAHIAaQBzAGUAcwAgADIALAAyDiMOMQ4HDioONQ5BDgEOIQ4h -DjIOQA4BDiMOIg5MDhcOMQ5IDicORA4bACAAMgAuADIARwBlAG4AZQBsACAARwByAGkAIABHAGEAbQBh -ACAAMgAsADIAWQBsAGUAaQBuAGUAbgAgAGgAYQByAG0AYQBhAG4AIABnAGEAbQBtAGEAIAAyACwAMgAg -AC0AcAByAG8AZgBpAGkAbABpAEcAZQBuAGUAcgBpAQ0AawBpACAARwByAGEAeQAgAEcAYQBtAG0AYQAg -ADIALgAyACAAcAByAG8AZgBpAGwAVQBuAGkAdwBlAHIAcwBhAGwAbgB5ACAAcAByAG8AZgBpAGwAIABz -AHoAYQByAG8BWwBjAGkAIABnAGEAbQBtAGEAIAAyACwAMgY6BicGRQYnACAAMgAuADIAIAZEBkgGRgAg -BjEGRQYnBi8GSgAgBjkGJwZFBB4EMQRJBDAETwAgBEEENQRABDAETwAgBDMEMAQ8BDwEMAAgADIALAAy -AC0EPwRABD4ERAQ4BDsETABHAGUAbgBlAHIAaQBjACAARwByAGEAeQAgAEcAYQBtAG0AYQAgADIALgAy -ACAAUAByAG8AZgBpAGwAZQAAdGV4dAAAAABDb3B5cmlnaHQgQXBwbGUgSW5jLiwgMjAxMgAAWFlaIAAA -AAAAAPNRAAEAAAABFsxjdXJ2AAAAAAAABAAAAAAFAAoADwAUABkAHgAjACgALQAyADcAOwBAAEUASgBP -AFQAWQBeAGMAaABtAHIAdwB8AIEAhgCLAJAAlQCaAJ8ApACpAK4AsgC3ALwAwQDGAMsA0ADVANsA4ADl -AOsA8AD2APsBAQEHAQ0BEwEZAR8BJQErATIBOAE+AUUBTAFSAVkBYAFnAW4BdQF8AYMBiwGSAZoBoQGp -AbEBuQHBAckB0QHZAeEB6QHyAfoCAwIMAhQCHQImAi8COAJBAksCVAJdAmcCcQJ6AoQCjgKYAqICrAK2 -AsECywLVAuAC6wL1AwADCwMWAyEDLQM4A0MDTwNaA2YDcgN+A4oDlgOiA64DugPHA9MD4APsA/kEBgQT -BCAELQQ7BEgEVQRjBHEEfgSMBJoEqAS2BMQE0wThBPAE/gUNBRwFKwU6BUkFWAVnBXcFhgWWBaYFtQXF -BdUF5QX2BgYGFgYnBjcGSAZZBmoGewaMBp0GrwbABtEG4wb1BwcHGQcrBz0HTwdhB3QHhgeZB6wHvwfS -B+UH+AgLCB8IMghGCFoIbgiCCJYIqgi+CNII5wj7CRAJJQk6CU8JZAl5CY8JpAm6Cc8J5Qn7ChEKJwo9 -ClQKagqBCpgKrgrFCtwK8wsLCyILOQtRC2kLgAuYC7ALyAvhC/kMEgwqDEMMXAx1DI4MpwzADNkM8w0N -DSYNQA1aDXQNjg2pDcMN3g34DhMOLg5JDmQOfw6bDrYO0g7uDwkPJQ9BD14Peg+WD7MPzw/sEAkQJhBD -EGEQfhCbELkQ1xD1ERMRMRFPEW0RjBGqEckR6BIHEiYSRRJkEoQSoxLDEuMTAxMjE0MTYxODE6QTxRPl -FAYUJxRJFGoUixStFM4U8BUSFTQVVhV4FZsVvRXgFgMWJhZJFmwWjxayFtYW+hcdF0EXZReJF64X0hf3 -GBsYQBhlGIoYrxjVGPoZIBlFGWsZkRm3Gd0aBBoqGlEadxqeGsUa7BsUGzsbYxuKG7Ib2hwCHCocUhx7 -HKMczBz1HR4dRx1wHZkdwx3sHhYeQB5qHpQevh7pHxMfPh9pH5Qfvx/qIBUgQSBsIJggxCDwIRwhSCF1 -IaEhziH7IiciVSKCIq8i3SMKIzgjZiOUI8Ij8CQfJE0kfCSrJNolCSU4JWgllyXHJfcmJyZXJocmtybo -JxgnSSd6J6sn3CgNKD8ocSiiKNQpBik4KWspnSnQKgIqNSpoKpsqzysCKzYraSudK9EsBSw5LG4soizX -LQwtQS12Last4S4WLkwugi63Lu4vJC9aL5Evxy/+MDUwbDCkMNsxEjFKMYIxujHyMioyYzKbMtQzDTNG -M38zuDPxNCs0ZTSeNNg1EzVNNYc1wjX9Njc2cjauNuk3JDdgN5w31zgUOFA4jDjIOQU5Qjl/Obw5+To2 -OnQ6sjrvOy07azuqO+g8JzxlPKQ84z0iPWE9oT3gPiA+YD6gPuA/IT9hP6I/4kAjQGRApkDnQSlBakGs -Qe5CMEJyQrVC90M6Q31DwEQDREdEikTORRJFVUWaRd5GIkZnRqtG8Ec1R3tHwEgFSEtIkUjXSR1JY0mp -SfBKN0p9SsRLDEtTS5pL4kwqTHJMuk0CTUpNk03cTiVObk63TwBPSU+TT91QJ1BxULtRBlFQUZtR5lIx -UnxSx1MTU19TqlP2VEJUj1TbVShVdVXCVg9WXFapVvdXRFeSV+BYL1h9WMtZGllpWbhaB1pWWqZa9VtF -W5Vb5Vw1XIZc1l0nXXhdyV4aXmxevV8PX2Ffs2AFYFdgqmD8YU9homH1YklinGLwY0Njl2PrZEBklGTp -ZT1lkmXnZj1mkmboZz1nk2fpaD9olmjsaUNpmmnxakhqn2r3a09rp2v/bFdsr20IbWBtuW4SbmtuxG8e -b3hv0XArcIZw4HE6cZVx8HJLcqZzAXNdc7h0FHRwdMx1KHWFdeF2Pnabdvh3VnezeBF4bnjMeSp5iXnn -ekZ6pXsEe2N7wnwhfIF84X1BfaF+AX5ifsJ/I3+Ef+WAR4CogQqBa4HNgjCCkoL0g1eDuoQdhICE44VH -hauGDoZyhteHO4efiASIaYjOiTOJmYn+imSKyoswi5aL/IxjjMqNMY2Yjf+OZo7OjzaPnpAGkG6Q1pE/ -kaiSEZJ6kuOTTZO2lCCUipT0lV+VyZY0lp+XCpd1l+CYTJi4mSSZkJn8mmia1ZtCm6+cHJyJnPedZJ3S -nkCerp8dn4uf+qBpoNihR6G2oiailqMGo3aj5qRWpMelOKWpphqmi6b9p26n4KhSqMSpN6mpqhyqj6sC -q3Wr6axcrNCtRK24ri2uoa8Wr4uwALB1sOqxYLHWskuywrM4s660JbSctRO1irYBtnm28Ldot+C4WbjR -uUq5wro7urW7LrunvCG8m70VvY++Cr6Evv+/er/1wHDA7MFnwePCX8Lbw1jD1MRRxM7FS8XIxkbGw8dB -x7/IPci8yTrJuco4yrfLNsu2zDXMtc01zbXONs62zzfPuNA50LrRPNG+0j/SwdNE08bUSdTL1U7V0dZV -1tjXXNfg2GTY6Nls2fHadtr724DcBdyK3RDdlt4c3qLfKd+v4DbgveFE4cziU+Lb42Pj6+Rz5PzlhOYN -5pbnH+ep6DLovOlG6dDqW+rl63Dr++yG7RHtnO4o7rTvQO/M8Fjw5fFy8f/yjPMZ86f0NPTC9VD13vZt -9vv3ivgZ+Kj5OPnH+lf65/t3/Af8mP0p/br+S/7c/23//9IrLC0uWiRjbGFzc25hbWVYJGNsYXNzZXNf -EBBOU0JpdG1hcEltYWdlUmVwoy0vMFpOU0ltYWdlUmVwWE5TT2JqZWN00issMjNXTlNBcnJheaIyMNIr -LDU2Xk5TTXV0YWJsZUFycmF5ozUyMNM4OQ86OzxXTlNXaGl0ZVxOU0NvbG9yU3BhY2VEMCAwABADgAzS -Kyw+P1dOU0NvbG9yoj4w0issQUJXTlNJbWFnZaJBMAAIABEAGgAkACkAMgA3AEkATABRAFMAYgBoAHUA -fACLAJIAnwCmAK4AsACyALQAuQC7AL0AxADJANQA1gDYANoA3wDiAOQA5gDoAO8BBgEiASQBJhOWE5sT -phOvE8ITxhPRE9oT3xPnE+oT7xP+FAIUCRQRFB4UIxQlFCcULBQ0FDcUPBREAAAAAAAAAgEAAAAAAAAA -QwAAAAAAAAAAAAAAAAAAFEc +S2V5ZWRBcmNoaXZlctEICVRyb290gAGuCwwbHCEaJissMzY5P0JVJG51bGzXDQ4PEBESExQVFhcYGRpW +TlNTaXplViRjbGFzc1xOU0ltYWdlRmxhZ3NWTlNSZXBzW05TVGludENvbG9yV05TQ29sb3JeTlNSZXNp +emluZ01vZGWAAoANEiDDAACAA4AAgAsQAFZ7MSwgMX3SHQ4eIFpOUy5vYmplY3RzoR+ABIAK0h0OIiWi +IySABYAGgAnTDicoKSoaXxAUTlNUSUZGUmVwcmVzZW50YXRpb25fEBlOU0ludGVybmFsTGF5b3V0RGly +ZWN0aW9ugAiAB08REmxNTQAqAAAACgAAABABAAADAAAAAQABAAABAQADAAAAAQABAAABAgADAAAAAgAI +AAgBAwADAAAAAQABAAABBgADAAAAAQABAAABCgADAAAAAQABAAABEQAEAAAAAQAAAAgBEgADAAAAAQAB +AAABFQADAAAAAQACAAABFgADAAAAAQABAAABFwAEAAAAAQAAAAIBHAADAAAAAQABAAABKAADAAAAAQAC +AAABUgADAAAAAQABAAABUwADAAAAAgABAAGHcwAHAAARnAAAANAAAAAAAAARnGFwcGwCAAAAbW50ckdS +QVlYWVogB9wACAAXAA8ALgAPYWNzcEFQUEwAAAAAbm9uZQAAAAAAAAAAAAAAAAAAAAAAAPbWAAEAAAAA +0y1hcHBsAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFZGVzYwAA +AMAAAAB5ZHNjbQAAATwAAAgaY3BydAAACVgAAAAjd3RwdAAACXwAAAAUa1RSQwAACZAAAAgMZGVzYwAA +AAAAAAAfR2VuZXJpYyBHcmF5IEdhbW1hIDIuMiBQcm9maWxlAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAG1s +dWMAAAAAAAAAHwAAAAxza1NLAAAALgAAAYRkYURLAAAAOgAAAbJjYUVTAAAAOAAAAex2aVZOAAAAQAAA +AiRwdEJSAAAASgAAAmR1a1VBAAAALAAAAq5mckZVAAAAPgAAAtpodUhVAAAANAAAAxh6aFRXAAAAGgAA +A0xrb0tSAAAAIgAAA2ZuYk5PAAAAOgAAA4hjc0NaAAAAKAAAA8JoZUlMAAAAJAAAA+pyb1JPAAAAKgAA +BA5kZURFAAAATgAABDhpdElUAAAATgAABIZzdlNFAAAAOAAABNR6aENOAAAAGgAABQxqYUpQAAAAJgAA +BSZlbEdSAAAAKgAABUxwdFBPAAAAUgAABXZubE5MAAAAQAAABchlc0VTAAAATAAABgh0aFRIAAAAMgAA +BlR0clRSAAAAJAAABoZmaUZJAAAARgAABqpockhSAAAAPgAABvBwbFBMAAAASgAABy5hckVHAAAALAAA +B3hydVJVAAAAOgAAB6RlblVTAAAAPAAAB94AVgFhAGUAbwBiAGUAYwBuAOEAIABzAGkAdgDhACAAZwBh +AG0AYQAgADIALAAyAEcAZQBuAGUAcgBpAHMAawAgAGcAcgDlACAAMgAsADIAIABnAGEAbQBtAGEALQBw +AHIAbwBmAGkAbABHAGEAbQBtAGEAIABkAGUAIABnAHIAaQBzAG8AcwAgAGcAZQBuAOgAcgBpAGMAYQAg +ADIALgAyAEMepQB1ACAAaADsAG4AaAAgAE0A4AB1ACAAeADhAG0AIABDAGgAdQBuAGcAIABHAGEAbQBt +AGEAIAAyAC4AMgBQAGUAcgBmAGkAbAAgAEcAZQBuAOkAcgBpAGMAbwAgAGQAYQAgAEcAYQBtAGEAIABk +AGUAIABDAGkAbgB6AGEAcwAgADIALAAyBBcEMAQzBDAEOwRMBD0EMAAgAEcAcgBhAHkALQQzBDAEPAQw +ACAAMgAuADIAUAByAG8AZgBpAGwAIABnAOkAbgDpAHIAaQBxAHUAZQAgAGcAcgBpAHMAIABnAGEAbQBt +AGEAIAAyACwAMgDBAGwAdABhAGwA4QBuAG8AcwAgAHMAegD8AHIAawBlACAAZwBhAG0AbQBhACAAMgAu +ADKQGnUocHCWjlFJXqYAMgAuADKCcl9pY8+P8Md8vBgAINaMwMkAIKwQucgAIAAyAC4AMgAg1QS4XNMM +x3wARwBlAG4AZQByAGkAcwBrACAAZwByAOUAIABnAGEAbQBtAGEAIAAyACwAMgAtAHAAcgBvAGYAaQBs +AE8AYgBlAGMAbgDhACABYQBlAGQA4QAgAGcAYQBtAGEAIAAyAC4AMgXSBdAF3gXUACAF0AXkBdUF6AAg +BdsF3AXcBdkAIAAyAC4AMgBHAGEAbQBhACAAZwByAGkAIABnAGUAbgBlAHIAaQBjAQMAIAAyACwAMgBB +AGwAbABnAGUAbQBlAGkAbgBlAHMAIABHAHIAYQB1AHMAdAB1AGYAZQBuAC0AUAByAG8AZgBpAGwAIABH +AGEAbQBtAGEAIAAyACwAMgBQAHIAbwBmAGkAbABvACAAZwByAGkAZwBpAG8AIABnAGUAbgBlAHIAaQBj +AG8AIABkAGUAbABsAGEAIABnAGEAbQBtAGEAIAAyACwAMgBHAGUAbgBlAHIAaQBzAGsAIABnAHIA5QAg +ADIALAAyACAAZwBhAG0AbQBhAHAAcgBvAGYAaQBsZm6QGnBwXqZ8+2VwADIALgAyY8+P8GWHTvZOAIIs +MLAw7DCkMKww8zDeACAAMgAuADIAIDDXMO0w1TChMKQw6wOTA7UDvQO5A7oDzAAgA5MDugPBA7kAIAOT +A6wDvAO8A7EAIAAyAC4AMgBQAGUAcgBmAGkAbAAgAGcAZQBuAOkAcgBpAGMAbwAgAGQAZQAgAGMAaQBu +AHoAZQBuAHQAbwBzACAAZABhACAARwBhAG0AbQBhACAAMgAsADIAQQBsAGcAZQBtAGUAZQBuACAAZwBy +AGkAagBzACAAZwBhAG0AbQBhACAAMgAsADIALQBwAHIAbwBmAGkAZQBsAFAAZQByAGYAaQBsACAAZwBl +AG4A6QByAGkAYwBvACAAZABlACAAZwBhAG0AbQBhACAAZABlACAAZwByAGkAcwBlAHMAIAAyACwAMg4j +DjEOBw4qDjUOQQ4BDiEOIQ4yDkAOAQ4jDiIOTA4XDjEOSA4nDkQOGwAgADIALgAyAEcAZQBuAGUAbAAg +AEcAcgBpACAARwBhAG0AYQAgADIALAAyAFkAbABlAGkAbgBlAG4AIABoAGEAcgBtAGEAYQBuACAAZwBh +AG0AbQBhACAAMgAsADIAIAAtAHAAcgBvAGYAaQBpAGwAaQBHAGUAbgBlAHIAaQENAGsAaQAgAEcAcgBh +AHkAIABHAGEAbQBtAGEAIAAyAC4AMgAgAHAAcgBvAGYAaQBsAFUAbgBpAHcAZQByAHMAYQBsAG4AeQAg +AHAAcgBvAGYAaQBsACAAcwB6AGEAcgBvAVsAYwBpACAAZwBhAG0AbQBhACAAMgAsADIGOgYnBkUGJwAg +ADIALgAyACAGRAZIBkYAIAYxBkUGJwYvBkoAIAY5BicGRQQeBDEESQQwBE8AIARBBDUEQAQwBE8AIAQz +BDAEPAQ8BDAAIAAyACwAMgAtBD8EQAQ+BEQEOAQ7BEwARwBlAG4AZQByAGkAYwAgAEcAcgBhAHkAIABH +AGEAbQBtAGEAIAAyAC4AMgAgAFAAcgBvAGYAaQBsAGUAAHRleHQAAAAAQ29weXJpZ2h0IEFwcGxlIElu +Yy4sIDIwMTIAAFhZWiAAAAAAAADzUQABAAAAARbMY3VydgAAAAAAAAQAAAAABQAKAA8AFAAZAB4AIwAo +AC0AMgA3ADsAQABFAEoATwBUAFkAXgBjAGgAbQByAHcAfACBAIYAiwCQAJUAmgCfAKQAqQCuALIAtwC8 +AMEAxgDLANAA1QDbAOAA5QDrAPAA9gD7AQEBBwENARMBGQEfASUBKwEyATgBPgFFAUwBUgFZAWABZwFu +AXUBfAGDAYsBkgGaAaEBqQGxAbkBwQHJAdEB2QHhAekB8gH6AgMCDAIUAh0CJgIvAjgCQQJLAlQCXQJn +AnECegKEAo4CmAKiAqwCtgLBAssC1QLgAusC9QMAAwsDFgMhAy0DOANDA08DWgNmA3IDfgOKA5YDogOu +A7oDxwPTA+AD7AP5BAYEEwQgBC0EOwRIBFUEYwRxBH4EjASaBKgEtgTEBNME4QTwBP4FDQUcBSsFOgVJ +BVgFZwV3BYYFlgWmBbUFxQXVBeUF9gYGBhYGJwY3BkgGWQZqBnsGjAadBq8GwAbRBuMG9QcHBxkHKwc9 +B08HYQd0B4YHmQesB78H0gflB/gICwgfCDIIRghaCG4IggiWCKoIvgjSCOcI+wkQCSUJOglPCWQJeQmP +CaQJugnPCeUJ+woRCicKPQpUCmoKgQqYCq4KxQrcCvMLCwsiCzkLUQtpC4ALmAuwC8gL4Qv5DBIMKgxD +DFwMdQyODKcMwAzZDPMNDQ0mDUANWg10DY4NqQ3DDd4N+A4TDi4OSQ5kDn8Omw62DtIO7g8JDyUPQQ9e +D3oPlg+zD88P7BAJECYQQxBhEH4QmxC5ENcQ9RETETERTxFtEYwRqhHJEegSBxImEkUSZBKEEqMSwxLj +EwMTIxNDE2MTgxOkE8UT5RQGFCcUSRRqFIsUrRTOFPAVEhU0FVYVeBWbFb0V4BYDFiYWSRZsFo8WshbW +FvoXHRdBF2UXiReuF9IX9xgbGEAYZRiKGK8Y1Rj6GSAZRRlrGZEZtxndGgQaKhpRGncanhrFGuwbFBs7 +G2MbihuyG9ocAhwqHFIcexyjHMwc9R0eHUcdcB2ZHcMd7B4WHkAeah6UHr4e6R8THz4faR+UH78f6iAV +IEEgbCCYIMQg8CEcIUghdSGhIc4h+yInIlUigiKvIt0jCiM4I2YjlCPCI/AkHyRNJHwkqyTaJQklOCVo +JZclxyX3JicmVyaHJrcm6CcYJ0kneierJ9woDSg/KHEooijUKQYpOClrKZ0p0CoCKjUqaCqbKs8rAis2 +K2krnSvRLAUsOSxuLKIs1y0MLUEtdi2rLeEuFi5MLoIuty7uLyQvWi+RL8cv/jA1MGwwpDDbMRIxSjGC +Mbox8jIqMmMymzLUMw0zRjN/M7gz8TQrNGU0njTYNRM1TTWHNcI1/TY3NnI2rjbpNyQ3YDecN9c4FDhQ +OIw4yDkFOUI5fzm8Ofk6Njp0OrI67zstO2s7qjvoPCc8ZTykPOM9Ij1hPaE94D4gPmA+oD7gPyE/YT+i +P+JAI0BkQKZA50EpQWpBrEHuQjBCckK1QvdDOkN9Q8BEA0RHRIpEzkUSRVVFmkXeRiJGZ0arRvBHNUd7 +R8BIBUhLSJFI10kdSWNJqUnwSjdKfUrESwxLU0uaS+JMKkxyTLpNAk1KTZNN3E4lTm5Ot08AT0lPk0/d +UCdQcVC7UQZRUFGbUeZSMVJ8UsdTE1NfU6pT9lRCVI9U21UoVXVVwlYPVlxWqVb3V0RXklfgWC9YfVjL +WRpZaVm4WgdaVlqmWvVbRVuVW+VcNVyGXNZdJ114XcleGl5sXr1fD19hX7NgBWBXYKpg/GFPYaJh9WJJ +Ypxi8GNDY5dj62RAZJRk6WU9ZZJl52Y9ZpJm6Gc9Z5Nn6Wg/aJZo7GlDaZpp8WpIap9q92tPa6dr/2xX +bK9tCG1gbbluEm5rbsRvHm94b9FwK3CGcOBxOnGVcfByS3KmcwFzXXO4dBR0cHTMdSh1hXXhdj52m3b4 +d1Z3s3gReG54zHkqeYl553pGeqV7BHtje8J8IXyBfOF9QX2hfgF+Yn7CfyN/hH/lgEeAqIEKgWuBzYIw +gpKC9INXg7qEHYSAhOOFR4Wrhg6GcobXhzuHn4gEiGmIzokziZmJ/opkisqLMIuWi/yMY4zKjTGNmI3/ +jmaOzo82j56QBpBukNaRP5GokhGSepLjk02TtpQglIqU9JVflcmWNJaflwqXdZfgmEyYuJkkmZCZ/Jpo +mtWbQpuvnByciZz3nWSd0p5Anq6fHZ+Ln/qgaaDYoUehtqImopajBqN2o+akVqTHpTilqaYapoum/adu +p+CoUqjEqTepqaocqo+rAqt1q+msXKzQrUStuK4trqGvFq+LsACwdbDqsWCx1rJLssKzOLOutCW0nLUT +tYq2AbZ5tvC3aLfguFm40blKucK6O7q1uy67p7whvJu9Fb2Pvgq+hL7/v3q/9cBwwOzBZ8Hjwl/C28NY +w9TEUcTOxUvFyMZGxsPHQce/yD3IvMk6ybnKOMq3yzbLtsw1zLXNNc21zjbOts83z7jQOdC60TzRvtI/ +0sHTRNPG1EnUy9VO1dHWVdbY11zX4Nhk2OjZbNnx2nba+9uA3AXcit0Q3ZbeHN6i3ynfr+A24L3hROHM +4lPi2+Nj4+vkc+T85YTmDeaW5x/nqegy6LzpRunQ6lvq5etw6/vshu0R7ZzuKO6070DvzPBY8OXxcvH/ +8ozzGfOn9DT0wvVQ9d72bfb794r4Gfio+Tj5x/pX+uf7d/wH/Jj9Kf26/kv+3P9t///SLS4vMFokY2xh +c3NuYW1lWCRjbGFzc2VzXxAQTlNCaXRtYXBJbWFnZVJlcKMvMTJaTlNJbWFnZVJlcFhOU09iamVjdNIt +LjQ1V05TQXJyYXmiNDLSLS43OF5OU011dGFibGVBcnJheaM3NDLTOjsOPD0+V05TV2hpdGVcTlNDb2xv +clNwYWNlRDAgMAAQA4AM0i0uQEFXTlNDb2xvcqJAMtItLkNEV05TSW1hZ2WiQzIACAARABoAJAApADIA +NwBJAEwAUQBTAGIAaAB3AH4AhQCSAJkApQCtALwAvgDAAMUAxwDJAMsAzQDUANkA5ADmAOgA6gDvAPIA +9AD2APgA/wEWATIBNAE2E6YTqxO2E78T0hPWE+ET6hPvE/cT+hP/FA4UEhQZFCEULhQzFDUUNxQ8FEQU +RxRMFFQAAAAAAAACAQAAAAAAAABFAAAAAAAAAAAAAAAAAAAUVw diff --git a/i18n/src/main/resources/bg.lproj/Command.xib b/i18n/src/main/resources/bg.lproj/Command.xib index cf8eac9085..c18056229b 100644 --- a/i18n/src/main/resources/bg.lproj/Command.xib +++ b/i18n/src/main/resources/bg.lproj/Command.xib @@ -1,7 +1,7 @@ - + - + diff --git a/i18n/src/main/resources/bg.lproj/Connection.xib b/i18n/src/main/resources/bg.lproj/Connection.xib index b8c8949ddc..e8ba2e5317 100644 --- a/i18n/src/main/resources/bg.lproj/Connection.xib +++ b/i18n/src/main/resources/bg.lproj/Connection.xib @@ -1,7 +1,7 @@ - + - + @@ -76,7 +76,7 @@ - + diff --git a/i18n/src/main/resources/bg.lproj/Info.xib b/i18n/src/main/resources/bg.lproj/Info.xib index 5fc0254b34..895d5b6755 100644 --- a/i18n/src/main/resources/bg.lproj/Info.xib +++ b/i18n/src/main/resources/bg.lproj/Info.xib @@ -1,7 +1,7 @@ - + - + @@ -89,7 +89,7 @@ - + @@ -1017,21 +1017,21 @@ - + - + - + @@ -1121,21 +1121,21 @@ - + - + - + @@ -1250,27 +1250,27 @@ diff --git a/i18n/src/main/resources/bg.lproj/Login.strings b/i18n/src/main/resources/bg.lproj/Login.strings index d80e964ad2..33052e6320 100644 Binary files a/i18n/src/main/resources/bg.lproj/Login.strings and b/i18n/src/main/resources/bg.lproj/Login.strings differ diff --git a/i18n/src/main/resources/bg.lproj/Main.xib b/i18n/src/main/resources/bg.lproj/Main.xib index a1902574f2..9287a3c8dc 100644 --- a/i18n/src/main/resources/bg.lproj/Main.xib +++ b/i18n/src/main/resources/bg.lproj/Main.xib @@ -1,7 +1,7 @@ - + - + @@ -159,19 +159,19 @@ - + - + - + @@ -321,12 +321,12 @@ CA - + - + @@ -566,6 +566,10 @@ CA - + + + + + diff --git a/i18n/src/main/resources/bg.lproj/Preferences.xib b/i18n/src/main/resources/bg.lproj/Preferences.xib index 47779ac84e..8dedc7d642 100644 --- a/i18n/src/main/resources/bg.lproj/Preferences.xib +++ b/i18n/src/main/resources/bg.lproj/Preferences.xib @@ -1,7 +1,7 @@ - + - + @@ -266,7 +266,7 @@ - + @@ -307,7 +307,7 @@ - + @@ -538,7 +538,7 @@ - + @@ -578,7 +578,7 @@ - + @@ -613,7 +613,7 @@ - + @@ -642,7 +642,7 @@ - + @@ -1218,7 +1218,7 @@ - + @@ -1250,7 +1250,7 @@ - + @@ -1290,7 +1290,7 @@ - + @@ -1363,7 +1363,7 @@ - + @@ -1558,7 +1558,7 @@ - + @@ -1618,7 +1618,7 @@ - + @@ -1643,7 +1643,7 @@ - + @@ -1685,7 +1685,7 @@ - + @@ -1713,7 +1713,7 @@ - + @@ -1741,7 +1741,7 @@ - + @@ -1770,7 +1770,7 @@ - + @@ -1805,7 +1805,7 @@ - + @@ -1889,7 +1889,7 @@ - + @@ -1948,7 +1948,7 @@ - + @@ -1976,7 +1976,7 @@ - + @@ -2005,7 +2005,7 @@ - + diff --git a/i18n/src/main/resources/bg.lproj/Profiles.xib b/i18n/src/main/resources/bg.lproj/Profiles.xib index 18af5a58f4..20e5ce0f36 100644 --- a/i18n/src/main/resources/bg.lproj/Profiles.xib +++ b/i18n/src/main/resources/bg.lproj/Profiles.xib @@ -1,7 +1,7 @@ - + - + diff --git a/i18n/src/main/resources/bg.lproj/Prompt.xib b/i18n/src/main/resources/bg.lproj/Prompt.xib index e867bbe8f5..5b02d32ddd 100644 --- a/i18n/src/main/resources/bg.lproj/Prompt.xib +++ b/i18n/src/main/resources/bg.lproj/Prompt.xib @@ -1,7 +1,7 @@ - + - + @@ -95,7 +95,7 @@ Gw - + diff --git a/i18n/src/main/resources/bg.lproj/S3.strings b/i18n/src/main/resources/bg.lproj/S3.strings index af62532508..cba51ba817 100644 Binary files a/i18n/src/main/resources/bg.lproj/S3.strings and b/i18n/src/main/resources/bg.lproj/S3.strings differ diff --git a/i18n/src/main/resources/bg.lproj/Updater.strings b/i18n/src/main/resources/bg.lproj/Updater.strings index a9c71e14f8..09b65b375d 100644 Binary files a/i18n/src/main/resources/bg.lproj/Updater.strings and b/i18n/src/main/resources/bg.lproj/Updater.strings differ diff --git a/i18n/src/main/resources/ca.lproj/Bookmark.xib b/i18n/src/main/resources/ca.lproj/Bookmark.xib index 3121a5b3aa..9e5f0cfbe4 100644 --- a/i18n/src/main/resources/ca.lproj/Bookmark.xib +++ b/i18n/src/main/resources/ca.lproj/Bookmark.xib @@ -1,7 +1,7 @@ - + - + @@ -92,7 +92,7 @@ - + @@ -489,95 +489,95 @@ YnBsaXN0MDDUAQIDBAUGBwpYJHZlcnNpb25ZJGFyY2hpdmVyVCR0b3BYJG9iamVjdHMSAAGGoF8QD05T -S2V5ZWRBcmNoaXZlctEICVRyb290gAGuCwwZGh8UJCkqMTQ3PUBVJG51bGzWDQ4PEBESExQVFhcYVk5T -U2l6ZV5OU1Jlc2l6aW5nTW9kZVYkY2xhc3NcTlNJbWFnZUZsYWdzVk5TUmVwc1dOU0NvbG9ygAIQAIAN -EiDDAACAA4ALVnsxLCAxfdIbDxweWk5TLm9iamVjdHOhHYAEgArSGw8gI6IhIoAFgAaACdMPJSYnKBRf -EBROU1RJRkZSZXByZXNlbnRhdGlvbl8QGU5TSW50ZXJuYWxMYXlvdXREaXJlY3Rpb26ACIAHTxESbE1N -ACoAAAAKAAAAEAEAAAMAAAABAAEAAAEBAAMAAAABAAEAAAECAAMAAAACAAgACAEDAAMAAAABAAEAAAEG -AAMAAAABAAEAAAEKAAMAAAABAAEAAAERAAQAAAABAAAACAESAAMAAAABAAEAAAEVAAMAAAABAAIAAAEW -AAMAAAABAAEAAAEXAAQAAAABAAAAAgEcAAMAAAABAAEAAAEoAAMAAAABAAIAAAFSAAMAAAABAAEAAAFT -AAMAAAACAAEAAYdzAAcAABGcAAAA0AAAAAAAABGcYXBwbAIAAABtbnRyR1JBWVhZWiAH3AAIABcADwAu -AA9hY3NwQVBQTAAAAABub25lAAAAAAAAAAAAAAAAAAAAAAAA9tYAAQAAAADTLWFwcGwAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAVkZXNjAAAAwAAAAHlkc2NtAAABPAAA -CBpjcHJ0AAAJWAAAACN3dHB0AAAJfAAAABRrVFJDAAAJkAAACAxkZXNjAAAAAAAAAB9HZW5lcmljIEdy -YXkgR2FtbWEgMi4yIFByb2ZpbGUAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAbWx1YwAAAAAAAAAfAAAADHNr -U0sAAAAuAAABhGRhREsAAAA6AAABsmNhRVMAAAA4AAAB7HZpVk4AAABAAAACJHB0QlIAAABKAAACZHVr -VUEAAAAsAAACrmZyRlUAAAA+AAAC2mh1SFUAAAA0AAADGHpoVFcAAAAaAAADTGtvS1IAAAAiAAADZm5i -Tk8AAAA6AAADiGNzQ1oAAAAoAAADwmhlSUwAAAAkAAAD6nJvUk8AAAAqAAAEDmRlREUAAABOAAAEOGl0 -SVQAAABOAAAEhnN2U0UAAAA4AAAE1HpoQ04AAAAaAAAFDGphSlAAAAAmAAAFJmVsR1IAAAAqAAAFTHB0 -UE8AAABSAAAFdm5sTkwAAABAAAAFyGVzRVMAAABMAAAGCHRoVEgAAAAyAAAGVHRyVFIAAAAkAAAGhmZp -RkkAAABGAAAGqmhySFIAAAA+AAAG8HBsUEwAAABKAAAHLmFyRUcAAAAsAAAHeHJ1UlUAAAA6AAAHpGVu -VVMAAAA8AAAH3gBWAWEAZQBvAGIAZQBjAG4A4QAgAHMAaQB2AOEAIABnAGEAbQBhACAAMgAsADIARwBl -AG4AZQByAGkAcwBrACAAZwByAOUAIAAyACwAMgAgAGcAYQBtAG0AYQAtAHAAcgBvAGYAaQBsAEcAYQBt -AG0AYQAgAGQAZQAgAGcAcgBpAHMAbwBzACAAZwBlAG4A6AByAGkAYwBhACAAMgAuADIAQx6lAHUAIABo -AOwAbgBoACAATQDgAHUAIAB4AOEAbQAgAEMAaAB1AG4AZwAgAEcAYQBtAG0AYQAgADIALgAyAFAAZQBy -AGYAaQBsACAARwBlAG4A6QByAGkAYwBvACAAZABhACAARwBhAG0AYQAgAGQAZQAgAEMAaQBuAHoAYQBz -ACAAMgAsADIEFwQwBDMEMAQ7BEwEPQQwACAARwByAGEAeQAtBDMEMAQ8BDAAIAAyAC4AMgBQAHIAbwBm -AGkAbAAgAGcA6QBuAOkAcgBpAHEAdQBlACAAZwByAGkAcwAgAGcAYQBtAG0AYQAgADIALAAyAMEAbAB0 -AGEAbADhAG4AbwBzACAAcwB6APwAcgBrAGUAIABnAGEAbQBtAGEAIAAyAC4AMpAadShwcJaOUUlepgAy -AC4AMoJyX2ljz4/wx3y8GAAg1ozAyQAgrBC5yAAgADIALgAyACDVBLhc0wzHfABHAGUAbgBlAHIAaQBz -AGsAIABnAHIA5QAgAGcAYQBtAG0AYQAgADIALAAyAC0AcAByAG8AZgBpAGwATwBiAGUAYwBuAOEAIAFh -AGUAZADhACAAZwBhAG0AYQAgADIALgAyBdIF0AXeBdQAIAXQBeQF1QXoACAF2wXcBdwF2QAgADIALgAy -AEcAYQBtAGEAIABnAHIAaQAgAGcAZQBuAGUAcgBpAGMBAwAgADIALAAyAEEAbABsAGcAZQBtAGUAaQBu -AGUAcwAgAEcAcgBhAHUAcwB0AHUAZgBlAG4ALQBQAHIAbwBmAGkAbAAgAEcAYQBtAG0AYQAgADIALAAy -AFAAcgBvAGYAaQBsAG8AIABnAHIAaQBnAGkAbwAgAGcAZQBuAGUAcgBpAGMAbwAgAGQAZQBsAGwAYQAg -AGcAYQBtAG0AYQAgADIALAAyAEcAZQBuAGUAcgBpAHMAawAgAGcAcgDlACAAMgAsADIAIABnAGEAbQBt -AGEAcAByAG8AZgBpAGxmbpAacHBepnz7ZXAAMgAuADJjz4/wZYdO9k4AgiwwsDDsMKQwrDDzMN4AIAAy -AC4AMgAgMNcw7TDVMKEwpDDrA5MDtQO9A7kDugPMACADkwO6A8EDuQAgA5MDrAO8A7wDsQAgADIALgAy -AFAAZQByAGYAaQBsACAAZwBlAG4A6QByAGkAYwBvACAAZABlACAAYwBpAG4AegBlAG4AdABvAHMAIABk -AGEAIABHAGEAbQBtAGEAIAAyACwAMgBBAGwAZwBlAG0AZQBlAG4AIABnAHIAaQBqAHMAIABnAGEAbQBt -AGEAIAAyACwAMgAtAHAAcgBvAGYAaQBlAGwAUABlAHIAZgBpAGwAIABnAGUAbgDpAHIAaQBjAG8AIABk -AGUAIABnAGEAbQBtAGEAIABkAGUAIABnAHIAaQBzAGUAcwAgADIALAAyDiMOMQ4HDioONQ5BDgEOIQ4h -DjIOQA4BDiMOIg5MDhcOMQ5IDicORA4bACAAMgAuADIARwBlAG4AZQBsACAARwByAGkAIABHAGEAbQBh -ACAAMgAsADIAWQBsAGUAaQBuAGUAbgAgAGgAYQByAG0AYQBhAG4AIABnAGEAbQBtAGEAIAAyACwAMgAg -AC0AcAByAG8AZgBpAGkAbABpAEcAZQBuAGUAcgBpAQ0AawBpACAARwByAGEAeQAgAEcAYQBtAG0AYQAg -ADIALgAyACAAcAByAG8AZgBpAGwAVQBuAGkAdwBlAHIAcwBhAGwAbgB5ACAAcAByAG8AZgBpAGwAIABz -AHoAYQByAG8BWwBjAGkAIABnAGEAbQBtAGEAIAAyACwAMgY6BicGRQYnACAAMgAuADIAIAZEBkgGRgAg -BjEGRQYnBi8GSgAgBjkGJwZFBB4EMQRJBDAETwAgBEEENQRABDAETwAgBDMEMAQ8BDwEMAAgADIALAAy -AC0EPwRABD4ERAQ4BDsETABHAGUAbgBlAHIAaQBjACAARwByAGEAeQAgAEcAYQBtAG0AYQAgADIALgAy -ACAAUAByAG8AZgBpAGwAZQAAdGV4dAAAAABDb3B5cmlnaHQgQXBwbGUgSW5jLiwgMjAxMgAAWFlaIAAA -AAAAAPNRAAEAAAABFsxjdXJ2AAAAAAAABAAAAAAFAAoADwAUABkAHgAjACgALQAyADcAOwBAAEUASgBP -AFQAWQBeAGMAaABtAHIAdwB8AIEAhgCLAJAAlQCaAJ8ApACpAK4AsgC3ALwAwQDGAMsA0ADVANsA4ADl -AOsA8AD2APsBAQEHAQ0BEwEZAR8BJQErATIBOAE+AUUBTAFSAVkBYAFnAW4BdQF8AYMBiwGSAZoBoQGp -AbEBuQHBAckB0QHZAeEB6QHyAfoCAwIMAhQCHQImAi8COAJBAksCVAJdAmcCcQJ6AoQCjgKYAqICrAK2 -AsECywLVAuAC6wL1AwADCwMWAyEDLQM4A0MDTwNaA2YDcgN+A4oDlgOiA64DugPHA9MD4APsA/kEBgQT -BCAELQQ7BEgEVQRjBHEEfgSMBJoEqAS2BMQE0wThBPAE/gUNBRwFKwU6BUkFWAVnBXcFhgWWBaYFtQXF -BdUF5QX2BgYGFgYnBjcGSAZZBmoGewaMBp0GrwbABtEG4wb1BwcHGQcrBz0HTwdhB3QHhgeZB6wHvwfS -B+UH+AgLCB8IMghGCFoIbgiCCJYIqgi+CNII5wj7CRAJJQk6CU8JZAl5CY8JpAm6Cc8J5Qn7ChEKJwo9 -ClQKagqBCpgKrgrFCtwK8wsLCyILOQtRC2kLgAuYC7ALyAvhC/kMEgwqDEMMXAx1DI4MpwzADNkM8w0N -DSYNQA1aDXQNjg2pDcMN3g34DhMOLg5JDmQOfw6bDrYO0g7uDwkPJQ9BD14Peg+WD7MPzw/sEAkQJhBD -EGEQfhCbELkQ1xD1ERMRMRFPEW0RjBGqEckR6BIHEiYSRRJkEoQSoxLDEuMTAxMjE0MTYxODE6QTxRPl -FAYUJxRJFGoUixStFM4U8BUSFTQVVhV4FZsVvRXgFgMWJhZJFmwWjxayFtYW+hcdF0EXZReJF64X0hf3 -GBsYQBhlGIoYrxjVGPoZIBlFGWsZkRm3Gd0aBBoqGlEadxqeGsUa7BsUGzsbYxuKG7Ib2hwCHCocUhx7 -HKMczBz1HR4dRx1wHZkdwx3sHhYeQB5qHpQevh7pHxMfPh9pH5Qfvx/qIBUgQSBsIJggxCDwIRwhSCF1 -IaEhziH7IiciVSKCIq8i3SMKIzgjZiOUI8Ij8CQfJE0kfCSrJNolCSU4JWgllyXHJfcmJyZXJocmtybo -JxgnSSd6J6sn3CgNKD8ocSiiKNQpBik4KWspnSnQKgIqNSpoKpsqzysCKzYraSudK9EsBSw5LG4soizX -LQwtQS12Last4S4WLkwugi63Lu4vJC9aL5Evxy/+MDUwbDCkMNsxEjFKMYIxujHyMioyYzKbMtQzDTNG -M38zuDPxNCs0ZTSeNNg1EzVNNYc1wjX9Njc2cjauNuk3JDdgN5w31zgUOFA4jDjIOQU5Qjl/Obw5+To2 -OnQ6sjrvOy07azuqO+g8JzxlPKQ84z0iPWE9oT3gPiA+YD6gPuA/IT9hP6I/4kAjQGRApkDnQSlBakGs -Qe5CMEJyQrVC90M6Q31DwEQDREdEikTORRJFVUWaRd5GIkZnRqtG8Ec1R3tHwEgFSEtIkUjXSR1JY0mp -SfBKN0p9SsRLDEtTS5pL4kwqTHJMuk0CTUpNk03cTiVObk63TwBPSU+TT91QJ1BxULtRBlFQUZtR5lIx -UnxSx1MTU19TqlP2VEJUj1TbVShVdVXCVg9WXFapVvdXRFeSV+BYL1h9WMtZGllpWbhaB1pWWqZa9VtF -W5Vb5Vw1XIZc1l0nXXhdyV4aXmxevV8PX2Ffs2AFYFdgqmD8YU9homH1YklinGLwY0Njl2PrZEBklGTp -ZT1lkmXnZj1mkmboZz1nk2fpaD9olmjsaUNpmmnxakhqn2r3a09rp2v/bFdsr20IbWBtuW4SbmtuxG8e -b3hv0XArcIZw4HE6cZVx8HJLcqZzAXNdc7h0FHRwdMx1KHWFdeF2Pnabdvh3VnezeBF4bnjMeSp5iXnn -ekZ6pXsEe2N7wnwhfIF84X1BfaF+AX5ifsJ/I3+Ef+WAR4CogQqBa4HNgjCCkoL0g1eDuoQdhICE44VH -hauGDoZyhteHO4efiASIaYjOiTOJmYn+imSKyoswi5aL/IxjjMqNMY2Yjf+OZo7OjzaPnpAGkG6Q1pE/ -kaiSEZJ6kuOTTZO2lCCUipT0lV+VyZY0lp+XCpd1l+CYTJi4mSSZkJn8mmia1ZtCm6+cHJyJnPedZJ3S -nkCerp8dn4uf+qBpoNihR6G2oiailqMGo3aj5qRWpMelOKWpphqmi6b9p26n4KhSqMSpN6mpqhyqj6sC -q3Wr6axcrNCtRK24ri2uoa8Wr4uwALB1sOqxYLHWskuywrM4s660JbSctRO1irYBtnm28Ldot+C4WbjR -uUq5wro7urW7LrunvCG8m70VvY++Cr6Evv+/er/1wHDA7MFnwePCX8Lbw1jD1MRRxM7FS8XIxkbGw8dB -x7/IPci8yTrJuco4yrfLNsu2zDXMtc01zbXONs62zzfPuNA50LrRPNG+0j/SwdNE08bUSdTL1U7V0dZV -1tjXXNfg2GTY6Nls2fHadtr724DcBdyK3RDdlt4c3qLfKd+v4DbgveFE4cziU+Lb42Pj6+Rz5PzlhOYN -5pbnH+ep6DLovOlG6dDqW+rl63Dr++yG7RHtnO4o7rTvQO/M8Fjw5fFy8f/yjPMZ86f0NPTC9VD13vZt -9vv3ivgZ+Kj5OPnH+lf65/t3/Af8mP0p/br+S/7c/23//9IrLC0uWiRjbGFzc25hbWVYJGNsYXNzZXNf -EBBOU0JpdG1hcEltYWdlUmVwoy0vMFpOU0ltYWdlUmVwWE5TT2JqZWN00issMjNXTlNBcnJheaIyMNIr -LDU2Xk5TTXV0YWJsZUFycmF5ozUyMNM4OQ86OzxXTlNXaGl0ZVxOU0NvbG9yU3BhY2VEMCAwABADgAzS -Kyw+P1dOU0NvbG9yoj4w0issQUJXTlNJbWFnZaJBMAAIABEAGgAkACkAMgA3AEkATABRAFMAYgBoAHUA -fACLAJIAnwCmAK4AsACyALQAuQC7AL0AxADJANQA1gDYANoA3wDiAOQA5gDoAO8BBgEiASQBJhOWE5sT -phOvE8ITxhPRE9oT3xPnE+oT7xP+FAIUCRQRFB4UIxQlFCcULBQ0FDcUPBREAAAAAAAAAgEAAAAAAAAA -QwAAAAAAAAAAAAAAAAAAFEc +S2V5ZWRBcmNoaXZlctEICVRyb290gAGuCwwbHCEaJissMzY5P0JVJG51bGzXDQ4PEBESExQVFhcYGRpW +TlNTaXplViRjbGFzc1xOU0ltYWdlRmxhZ3NWTlNSZXBzW05TVGludENvbG9yV05TQ29sb3JeTlNSZXNp +emluZ01vZGWAAoANEiDDAACAA4AAgAsQAFZ7MSwgMX3SHQ4eIFpOUy5vYmplY3RzoR+ABIAK0h0OIiWi +IySABYAGgAnTDicoKSoaXxAUTlNUSUZGUmVwcmVzZW50YXRpb25fEBlOU0ludGVybmFsTGF5b3V0RGly +ZWN0aW9ugAiAB08REmxNTQAqAAAACgAAABABAAADAAAAAQABAAABAQADAAAAAQABAAABAgADAAAAAgAI +AAgBAwADAAAAAQABAAABBgADAAAAAQABAAABCgADAAAAAQABAAABEQAEAAAAAQAAAAgBEgADAAAAAQAB +AAABFQADAAAAAQACAAABFgADAAAAAQABAAABFwAEAAAAAQAAAAIBHAADAAAAAQABAAABKAADAAAAAQAC +AAABUgADAAAAAQABAAABUwADAAAAAgABAAGHcwAHAAARnAAAANAAAAAAAAARnGFwcGwCAAAAbW50ckdS +QVlYWVogB9wACAAXAA8ALgAPYWNzcEFQUEwAAAAAbm9uZQAAAAAAAAAAAAAAAAAAAAAAAPbWAAEAAAAA +0y1hcHBsAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFZGVzYwAA +AMAAAAB5ZHNjbQAAATwAAAgaY3BydAAACVgAAAAjd3RwdAAACXwAAAAUa1RSQwAACZAAAAgMZGVzYwAA +AAAAAAAfR2VuZXJpYyBHcmF5IEdhbW1hIDIuMiBQcm9maWxlAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAG1s +dWMAAAAAAAAAHwAAAAxza1NLAAAALgAAAYRkYURLAAAAOgAAAbJjYUVTAAAAOAAAAex2aVZOAAAAQAAA +AiRwdEJSAAAASgAAAmR1a1VBAAAALAAAAq5mckZVAAAAPgAAAtpodUhVAAAANAAAAxh6aFRXAAAAGgAA +A0xrb0tSAAAAIgAAA2ZuYk5PAAAAOgAAA4hjc0NaAAAAKAAAA8JoZUlMAAAAJAAAA+pyb1JPAAAAKgAA +BA5kZURFAAAATgAABDhpdElUAAAATgAABIZzdlNFAAAAOAAABNR6aENOAAAAGgAABQxqYUpQAAAAJgAA +BSZlbEdSAAAAKgAABUxwdFBPAAAAUgAABXZubE5MAAAAQAAABchlc0VTAAAATAAABgh0aFRIAAAAMgAA +BlR0clRSAAAAJAAABoZmaUZJAAAARgAABqpockhSAAAAPgAABvBwbFBMAAAASgAABy5hckVHAAAALAAA +B3hydVJVAAAAOgAAB6RlblVTAAAAPAAAB94AVgFhAGUAbwBiAGUAYwBuAOEAIABzAGkAdgDhACAAZwBh +AG0AYQAgADIALAAyAEcAZQBuAGUAcgBpAHMAawAgAGcAcgDlACAAMgAsADIAIABnAGEAbQBtAGEALQBw +AHIAbwBmAGkAbABHAGEAbQBtAGEAIABkAGUAIABnAHIAaQBzAG8AcwAgAGcAZQBuAOgAcgBpAGMAYQAg +ADIALgAyAEMepQB1ACAAaADsAG4AaAAgAE0A4AB1ACAAeADhAG0AIABDAGgAdQBuAGcAIABHAGEAbQBt +AGEAIAAyAC4AMgBQAGUAcgBmAGkAbAAgAEcAZQBuAOkAcgBpAGMAbwAgAGQAYQAgAEcAYQBtAGEAIABk +AGUAIABDAGkAbgB6AGEAcwAgADIALAAyBBcEMAQzBDAEOwRMBD0EMAAgAEcAcgBhAHkALQQzBDAEPAQw +ACAAMgAuADIAUAByAG8AZgBpAGwAIABnAOkAbgDpAHIAaQBxAHUAZQAgAGcAcgBpAHMAIABnAGEAbQBt +AGEAIAAyACwAMgDBAGwAdABhAGwA4QBuAG8AcwAgAHMAegD8AHIAawBlACAAZwBhAG0AbQBhACAAMgAu +ADKQGnUocHCWjlFJXqYAMgAuADKCcl9pY8+P8Md8vBgAINaMwMkAIKwQucgAIAAyAC4AMgAg1QS4XNMM +x3wARwBlAG4AZQByAGkAcwBrACAAZwByAOUAIABnAGEAbQBtAGEAIAAyACwAMgAtAHAAcgBvAGYAaQBs +AE8AYgBlAGMAbgDhACABYQBlAGQA4QAgAGcAYQBtAGEAIAAyAC4AMgXSBdAF3gXUACAF0AXkBdUF6AAg +BdsF3AXcBdkAIAAyAC4AMgBHAGEAbQBhACAAZwByAGkAIABnAGUAbgBlAHIAaQBjAQMAIAAyACwAMgBB +AGwAbABnAGUAbQBlAGkAbgBlAHMAIABHAHIAYQB1AHMAdAB1AGYAZQBuAC0AUAByAG8AZgBpAGwAIABH +AGEAbQBtAGEAIAAyACwAMgBQAHIAbwBmAGkAbABvACAAZwByAGkAZwBpAG8AIABnAGUAbgBlAHIAaQBj +AG8AIABkAGUAbABsAGEAIABnAGEAbQBtAGEAIAAyACwAMgBHAGUAbgBlAHIAaQBzAGsAIABnAHIA5QAg +ADIALAAyACAAZwBhAG0AbQBhAHAAcgBvAGYAaQBsZm6QGnBwXqZ8+2VwADIALgAyY8+P8GWHTvZOAIIs +MLAw7DCkMKww8zDeACAAMgAuADIAIDDXMO0w1TChMKQw6wOTA7UDvQO5A7oDzAAgA5MDugPBA7kAIAOT +A6wDvAO8A7EAIAAyAC4AMgBQAGUAcgBmAGkAbAAgAGcAZQBuAOkAcgBpAGMAbwAgAGQAZQAgAGMAaQBu +AHoAZQBuAHQAbwBzACAAZABhACAARwBhAG0AbQBhACAAMgAsADIAQQBsAGcAZQBtAGUAZQBuACAAZwBy +AGkAagBzACAAZwBhAG0AbQBhACAAMgAsADIALQBwAHIAbwBmAGkAZQBsAFAAZQByAGYAaQBsACAAZwBl +AG4A6QByAGkAYwBvACAAZABlACAAZwBhAG0AbQBhACAAZABlACAAZwByAGkAcwBlAHMAIAAyACwAMg4j +DjEOBw4qDjUOQQ4BDiEOIQ4yDkAOAQ4jDiIOTA4XDjEOSA4nDkQOGwAgADIALgAyAEcAZQBuAGUAbAAg +AEcAcgBpACAARwBhAG0AYQAgADIALAAyAFkAbABlAGkAbgBlAG4AIABoAGEAcgBtAGEAYQBuACAAZwBh +AG0AbQBhACAAMgAsADIAIAAtAHAAcgBvAGYAaQBpAGwAaQBHAGUAbgBlAHIAaQENAGsAaQAgAEcAcgBh +AHkAIABHAGEAbQBtAGEAIAAyAC4AMgAgAHAAcgBvAGYAaQBsAFUAbgBpAHcAZQByAHMAYQBsAG4AeQAg +AHAAcgBvAGYAaQBsACAAcwB6AGEAcgBvAVsAYwBpACAAZwBhAG0AbQBhACAAMgAsADIGOgYnBkUGJwAg +ADIALgAyACAGRAZIBkYAIAYxBkUGJwYvBkoAIAY5BicGRQQeBDEESQQwBE8AIARBBDUEQAQwBE8AIAQz +BDAEPAQ8BDAAIAAyACwAMgAtBD8EQAQ+BEQEOAQ7BEwARwBlAG4AZQByAGkAYwAgAEcAcgBhAHkAIABH +AGEAbQBtAGEAIAAyAC4AMgAgAFAAcgBvAGYAaQBsAGUAAHRleHQAAAAAQ29weXJpZ2h0IEFwcGxlIElu +Yy4sIDIwMTIAAFhZWiAAAAAAAADzUQABAAAAARbMY3VydgAAAAAAAAQAAAAABQAKAA8AFAAZAB4AIwAo +AC0AMgA3ADsAQABFAEoATwBUAFkAXgBjAGgAbQByAHcAfACBAIYAiwCQAJUAmgCfAKQAqQCuALIAtwC8 +AMEAxgDLANAA1QDbAOAA5QDrAPAA9gD7AQEBBwENARMBGQEfASUBKwEyATgBPgFFAUwBUgFZAWABZwFu +AXUBfAGDAYsBkgGaAaEBqQGxAbkBwQHJAdEB2QHhAekB8gH6AgMCDAIUAh0CJgIvAjgCQQJLAlQCXQJn +AnECegKEAo4CmAKiAqwCtgLBAssC1QLgAusC9QMAAwsDFgMhAy0DOANDA08DWgNmA3IDfgOKA5YDogOu +A7oDxwPTA+AD7AP5BAYEEwQgBC0EOwRIBFUEYwRxBH4EjASaBKgEtgTEBNME4QTwBP4FDQUcBSsFOgVJ +BVgFZwV3BYYFlgWmBbUFxQXVBeUF9gYGBhYGJwY3BkgGWQZqBnsGjAadBq8GwAbRBuMG9QcHBxkHKwc9 +B08HYQd0B4YHmQesB78H0gflB/gICwgfCDIIRghaCG4IggiWCKoIvgjSCOcI+wkQCSUJOglPCWQJeQmP +CaQJugnPCeUJ+woRCicKPQpUCmoKgQqYCq4KxQrcCvMLCwsiCzkLUQtpC4ALmAuwC8gL4Qv5DBIMKgxD +DFwMdQyODKcMwAzZDPMNDQ0mDUANWg10DY4NqQ3DDd4N+A4TDi4OSQ5kDn8Omw62DtIO7g8JDyUPQQ9e +D3oPlg+zD88P7BAJECYQQxBhEH4QmxC5ENcQ9RETETERTxFtEYwRqhHJEegSBxImEkUSZBKEEqMSwxLj +EwMTIxNDE2MTgxOkE8UT5RQGFCcUSRRqFIsUrRTOFPAVEhU0FVYVeBWbFb0V4BYDFiYWSRZsFo8WshbW +FvoXHRdBF2UXiReuF9IX9xgbGEAYZRiKGK8Y1Rj6GSAZRRlrGZEZtxndGgQaKhpRGncanhrFGuwbFBs7 +G2MbihuyG9ocAhwqHFIcexyjHMwc9R0eHUcdcB2ZHcMd7B4WHkAeah6UHr4e6R8THz4faR+UH78f6iAV +IEEgbCCYIMQg8CEcIUghdSGhIc4h+yInIlUigiKvIt0jCiM4I2YjlCPCI/AkHyRNJHwkqyTaJQklOCVo +JZclxyX3JicmVyaHJrcm6CcYJ0kneierJ9woDSg/KHEooijUKQYpOClrKZ0p0CoCKjUqaCqbKs8rAis2 +K2krnSvRLAUsOSxuLKIs1y0MLUEtdi2rLeEuFi5MLoIuty7uLyQvWi+RL8cv/jA1MGwwpDDbMRIxSjGC +Mbox8jIqMmMymzLUMw0zRjN/M7gz8TQrNGU0njTYNRM1TTWHNcI1/TY3NnI2rjbpNyQ3YDecN9c4FDhQ +OIw4yDkFOUI5fzm8Ofk6Njp0OrI67zstO2s7qjvoPCc8ZTykPOM9Ij1hPaE94D4gPmA+oD7gPyE/YT+i +P+JAI0BkQKZA50EpQWpBrEHuQjBCckK1QvdDOkN9Q8BEA0RHRIpEzkUSRVVFmkXeRiJGZ0arRvBHNUd7 +R8BIBUhLSJFI10kdSWNJqUnwSjdKfUrESwxLU0uaS+JMKkxyTLpNAk1KTZNN3E4lTm5Ot08AT0lPk0/d +UCdQcVC7UQZRUFGbUeZSMVJ8UsdTE1NfU6pT9lRCVI9U21UoVXVVwlYPVlxWqVb3V0RXklfgWC9YfVjL +WRpZaVm4WgdaVlqmWvVbRVuVW+VcNVyGXNZdJ114XcleGl5sXr1fD19hX7NgBWBXYKpg/GFPYaJh9WJJ +Ypxi8GNDY5dj62RAZJRk6WU9ZZJl52Y9ZpJm6Gc9Z5Nn6Wg/aJZo7GlDaZpp8WpIap9q92tPa6dr/2xX +bK9tCG1gbbluEm5rbsRvHm94b9FwK3CGcOBxOnGVcfByS3KmcwFzXXO4dBR0cHTMdSh1hXXhdj52m3b4 +d1Z3s3gReG54zHkqeYl553pGeqV7BHtje8J8IXyBfOF9QX2hfgF+Yn7CfyN/hH/lgEeAqIEKgWuBzYIw +gpKC9INXg7qEHYSAhOOFR4Wrhg6GcobXhzuHn4gEiGmIzokziZmJ/opkisqLMIuWi/yMY4zKjTGNmI3/ +jmaOzo82j56QBpBukNaRP5GokhGSepLjk02TtpQglIqU9JVflcmWNJaflwqXdZfgmEyYuJkkmZCZ/Jpo +mtWbQpuvnByciZz3nWSd0p5Anq6fHZ+Ln/qgaaDYoUehtqImopajBqN2o+akVqTHpTilqaYapoum/adu +p+CoUqjEqTepqaocqo+rAqt1q+msXKzQrUStuK4trqGvFq+LsACwdbDqsWCx1rJLssKzOLOutCW0nLUT +tYq2AbZ5tvC3aLfguFm40blKucK6O7q1uy67p7whvJu9Fb2Pvgq+hL7/v3q/9cBwwOzBZ8Hjwl/C28NY +w9TEUcTOxUvFyMZGxsPHQce/yD3IvMk6ybnKOMq3yzbLtsw1zLXNNc21zjbOts83z7jQOdC60TzRvtI/ +0sHTRNPG1EnUy9VO1dHWVdbY11zX4Nhk2OjZbNnx2nba+9uA3AXcit0Q3ZbeHN6i3ynfr+A24L3hROHM +4lPi2+Nj4+vkc+T85YTmDeaW5x/nqegy6LzpRunQ6lvq5etw6/vshu0R7ZzuKO6070DvzPBY8OXxcvH/ +8ozzGfOn9DT0wvVQ9d72bfb794r4Gfio+Tj5x/pX+uf7d/wH/Jj9Kf26/kv+3P9t///SLS4vMFokY2xh +c3NuYW1lWCRjbGFzc2VzXxAQTlNCaXRtYXBJbWFnZVJlcKMvMTJaTlNJbWFnZVJlcFhOU09iamVjdNIt +LjQ1V05TQXJyYXmiNDLSLS43OF5OU011dGFibGVBcnJheaM3NDLTOjsOPD0+V05TV2hpdGVcTlNDb2xv +clNwYWNlRDAgMAAQA4AM0i0uQEFXTlNDb2xvcqJAMtItLkNEV05TSW1hZ2WiQzIACAARABoAJAApADIA +NwBJAEwAUQBTAGIAaAB3AH4AhQCSAJkApQCtALwAvgDAAMUAxwDJAMsAzQDUANkA5ADmAOgA6gDvAPIA +9AD2APgA/wEWATIBNAE2E6YTqxO2E78T0hPWE+ET6hPvE/cT+hP/FA4UEhQZFCEULhQzFDUUNxQ8FEQU +RxRMFFQAAAAAAAACAQAAAAAAAABFAAAAAAAAAAAAAAAAAAAUVw diff --git a/i18n/src/main/resources/ca.lproj/Browser.xib b/i18n/src/main/resources/ca.lproj/Browser.xib index 82bc8a57b1..63af930c3e 100644 --- a/i18n/src/main/resources/ca.lproj/Browser.xib +++ b/i18n/src/main/resources/ca.lproj/Browser.xib @@ -1,7 +1,7 @@ - + - + @@ -38,7 +38,7 @@ - + @@ -62,27 +62,27 @@ @@ -256,7 +256,7 @@ - + @@ -553,95 +553,95 @@ DQ YnBsaXN0MDDUAQIDBAUGBwpYJHZlcnNpb25ZJGFyY2hpdmVyVCR0b3BYJG9iamVjdHMSAAGGoF8QD05T -S2V5ZWRBcmNoaXZlctEICVRyb290gAGuCwwZGh8UJCkqMTQ3PUBVJG51bGzWDQ4PEBESExQVFhcYVk5T -U2l6ZV5OU1Jlc2l6aW5nTW9kZVYkY2xhc3NcTlNJbWFnZUZsYWdzVk5TUmVwc1dOU0NvbG9ygAIQAIAN -EiDDAACAA4ALVnsxLCAxfdIbDxweWk5TLm9iamVjdHOhHYAEgArSGw8gI6IhIoAFgAaACdMPJSYnKBRf -EBROU1RJRkZSZXByZXNlbnRhdGlvbl8QGU5TSW50ZXJuYWxMYXlvdXREaXJlY3Rpb26ACIAHTxESbE1N -ACoAAAAKAAAAEAEAAAMAAAABAAEAAAEBAAMAAAABAAEAAAECAAMAAAACAAgACAEDAAMAAAABAAEAAAEG -AAMAAAABAAEAAAEKAAMAAAABAAEAAAERAAQAAAABAAAACAESAAMAAAABAAEAAAEVAAMAAAABAAIAAAEW -AAMAAAABAAEAAAEXAAQAAAABAAAAAgEcAAMAAAABAAEAAAEoAAMAAAABAAIAAAFSAAMAAAABAAEAAAFT -AAMAAAACAAEAAYdzAAcAABGcAAAA0AAAAAAAABGcYXBwbAIAAABtbnRyR1JBWVhZWiAH3AAIABcADwAu -AA9hY3NwQVBQTAAAAABub25lAAAAAAAAAAAAAAAAAAAAAAAA9tYAAQAAAADTLWFwcGwAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAVkZXNjAAAAwAAAAHlkc2NtAAABPAAA -CBpjcHJ0AAAJWAAAACN3dHB0AAAJfAAAABRrVFJDAAAJkAAACAxkZXNjAAAAAAAAAB9HZW5lcmljIEdy -YXkgR2FtbWEgMi4yIFByb2ZpbGUAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAbWx1YwAAAAAAAAAfAAAADHNr -U0sAAAAuAAABhGRhREsAAAA6AAABsmNhRVMAAAA4AAAB7HZpVk4AAABAAAACJHB0QlIAAABKAAACZHVr -VUEAAAAsAAACrmZyRlUAAAA+AAAC2mh1SFUAAAA0AAADGHpoVFcAAAAaAAADTGtvS1IAAAAiAAADZm5i -Tk8AAAA6AAADiGNzQ1oAAAAoAAADwmhlSUwAAAAkAAAD6nJvUk8AAAAqAAAEDmRlREUAAABOAAAEOGl0 -SVQAAABOAAAEhnN2U0UAAAA4AAAE1HpoQ04AAAAaAAAFDGphSlAAAAAmAAAFJmVsR1IAAAAqAAAFTHB0 -UE8AAABSAAAFdm5sTkwAAABAAAAFyGVzRVMAAABMAAAGCHRoVEgAAAAyAAAGVHRyVFIAAAAkAAAGhmZp -RkkAAABGAAAGqmhySFIAAAA+AAAG8HBsUEwAAABKAAAHLmFyRUcAAAAsAAAHeHJ1UlUAAAA6AAAHpGVu -VVMAAAA8AAAH3gBWAWEAZQBvAGIAZQBjAG4A4QAgAHMAaQB2AOEAIABnAGEAbQBhACAAMgAsADIARwBl -AG4AZQByAGkAcwBrACAAZwByAOUAIAAyACwAMgAgAGcAYQBtAG0AYQAtAHAAcgBvAGYAaQBsAEcAYQBt -AG0AYQAgAGQAZQAgAGcAcgBpAHMAbwBzACAAZwBlAG4A6AByAGkAYwBhACAAMgAuADIAQx6lAHUAIABo -AOwAbgBoACAATQDgAHUAIAB4AOEAbQAgAEMAaAB1AG4AZwAgAEcAYQBtAG0AYQAgADIALgAyAFAAZQBy -AGYAaQBsACAARwBlAG4A6QByAGkAYwBvACAAZABhACAARwBhAG0AYQAgAGQAZQAgAEMAaQBuAHoAYQBz -ACAAMgAsADIEFwQwBDMEMAQ7BEwEPQQwACAARwByAGEAeQAtBDMEMAQ8BDAAIAAyAC4AMgBQAHIAbwBm -AGkAbAAgAGcA6QBuAOkAcgBpAHEAdQBlACAAZwByAGkAcwAgAGcAYQBtAG0AYQAgADIALAAyAMEAbAB0 -AGEAbADhAG4AbwBzACAAcwB6APwAcgBrAGUAIABnAGEAbQBtAGEAIAAyAC4AMpAadShwcJaOUUlepgAy -AC4AMoJyX2ljz4/wx3y8GAAg1ozAyQAgrBC5yAAgADIALgAyACDVBLhc0wzHfABHAGUAbgBlAHIAaQBz -AGsAIABnAHIA5QAgAGcAYQBtAG0AYQAgADIALAAyAC0AcAByAG8AZgBpAGwATwBiAGUAYwBuAOEAIAFh -AGUAZADhACAAZwBhAG0AYQAgADIALgAyBdIF0AXeBdQAIAXQBeQF1QXoACAF2wXcBdwF2QAgADIALgAy -AEcAYQBtAGEAIABnAHIAaQAgAGcAZQBuAGUAcgBpAGMBAwAgADIALAAyAEEAbABsAGcAZQBtAGUAaQBu -AGUAcwAgAEcAcgBhAHUAcwB0AHUAZgBlAG4ALQBQAHIAbwBmAGkAbAAgAEcAYQBtAG0AYQAgADIALAAy -AFAAcgBvAGYAaQBsAG8AIABnAHIAaQBnAGkAbwAgAGcAZQBuAGUAcgBpAGMAbwAgAGQAZQBsAGwAYQAg -AGcAYQBtAG0AYQAgADIALAAyAEcAZQBuAGUAcgBpAHMAawAgAGcAcgDlACAAMgAsADIAIABnAGEAbQBt -AGEAcAByAG8AZgBpAGxmbpAacHBepnz7ZXAAMgAuADJjz4/wZYdO9k4AgiwwsDDsMKQwrDDzMN4AIAAy -AC4AMgAgMNcw7TDVMKEwpDDrA5MDtQO9A7kDugPMACADkwO6A8EDuQAgA5MDrAO8A7wDsQAgADIALgAy -AFAAZQByAGYAaQBsACAAZwBlAG4A6QByAGkAYwBvACAAZABlACAAYwBpAG4AegBlAG4AdABvAHMAIABk -AGEAIABHAGEAbQBtAGEAIAAyACwAMgBBAGwAZwBlAG0AZQBlAG4AIABnAHIAaQBqAHMAIABnAGEAbQBt -AGEAIAAyACwAMgAtAHAAcgBvAGYAaQBlAGwAUABlAHIAZgBpAGwAIABnAGUAbgDpAHIAaQBjAG8AIABk -AGUAIABnAGEAbQBtAGEAIABkAGUAIABnAHIAaQBzAGUAcwAgADIALAAyDiMOMQ4HDioONQ5BDgEOIQ4h -DjIOQA4BDiMOIg5MDhcOMQ5IDicORA4bACAAMgAuADIARwBlAG4AZQBsACAARwByAGkAIABHAGEAbQBh -ACAAMgAsADIAWQBsAGUAaQBuAGUAbgAgAGgAYQByAG0AYQBhAG4AIABnAGEAbQBtAGEAIAAyACwAMgAg -AC0AcAByAG8AZgBpAGkAbABpAEcAZQBuAGUAcgBpAQ0AawBpACAARwByAGEAeQAgAEcAYQBtAG0AYQAg -ADIALgAyACAAcAByAG8AZgBpAGwAVQBuAGkAdwBlAHIAcwBhAGwAbgB5ACAAcAByAG8AZgBpAGwAIABz -AHoAYQByAG8BWwBjAGkAIABnAGEAbQBtAGEAIAAyACwAMgY6BicGRQYnACAAMgAuADIAIAZEBkgGRgAg -BjEGRQYnBi8GSgAgBjkGJwZFBB4EMQRJBDAETwAgBEEENQRABDAETwAgBDMEMAQ8BDwEMAAgADIALAAy -AC0EPwRABD4ERAQ4BDsETABHAGUAbgBlAHIAaQBjACAARwByAGEAeQAgAEcAYQBtAG0AYQAgADIALgAy -ACAAUAByAG8AZgBpAGwAZQAAdGV4dAAAAABDb3B5cmlnaHQgQXBwbGUgSW5jLiwgMjAxMgAAWFlaIAAA -AAAAAPNRAAEAAAABFsxjdXJ2AAAAAAAABAAAAAAFAAoADwAUABkAHgAjACgALQAyADcAOwBAAEUASgBP -AFQAWQBeAGMAaABtAHIAdwB8AIEAhgCLAJAAlQCaAJ8ApACpAK4AsgC3ALwAwQDGAMsA0ADVANsA4ADl -AOsA8AD2APsBAQEHAQ0BEwEZAR8BJQErATIBOAE+AUUBTAFSAVkBYAFnAW4BdQF8AYMBiwGSAZoBoQGp -AbEBuQHBAckB0QHZAeEB6QHyAfoCAwIMAhQCHQImAi8COAJBAksCVAJdAmcCcQJ6AoQCjgKYAqICrAK2 -AsECywLVAuAC6wL1AwADCwMWAyEDLQM4A0MDTwNaA2YDcgN+A4oDlgOiA64DugPHA9MD4APsA/kEBgQT -BCAELQQ7BEgEVQRjBHEEfgSMBJoEqAS2BMQE0wThBPAE/gUNBRwFKwU6BUkFWAVnBXcFhgWWBaYFtQXF -BdUF5QX2BgYGFgYnBjcGSAZZBmoGewaMBp0GrwbABtEG4wb1BwcHGQcrBz0HTwdhB3QHhgeZB6wHvwfS -B+UH+AgLCB8IMghGCFoIbgiCCJYIqgi+CNII5wj7CRAJJQk6CU8JZAl5CY8JpAm6Cc8J5Qn7ChEKJwo9 -ClQKagqBCpgKrgrFCtwK8wsLCyILOQtRC2kLgAuYC7ALyAvhC/kMEgwqDEMMXAx1DI4MpwzADNkM8w0N -DSYNQA1aDXQNjg2pDcMN3g34DhMOLg5JDmQOfw6bDrYO0g7uDwkPJQ9BD14Peg+WD7MPzw/sEAkQJhBD -EGEQfhCbELkQ1xD1ERMRMRFPEW0RjBGqEckR6BIHEiYSRRJkEoQSoxLDEuMTAxMjE0MTYxODE6QTxRPl -FAYUJxRJFGoUixStFM4U8BUSFTQVVhV4FZsVvRXgFgMWJhZJFmwWjxayFtYW+hcdF0EXZReJF64X0hf3 -GBsYQBhlGIoYrxjVGPoZIBlFGWsZkRm3Gd0aBBoqGlEadxqeGsUa7BsUGzsbYxuKG7Ib2hwCHCocUhx7 -HKMczBz1HR4dRx1wHZkdwx3sHhYeQB5qHpQevh7pHxMfPh9pH5Qfvx/qIBUgQSBsIJggxCDwIRwhSCF1 -IaEhziH7IiciVSKCIq8i3SMKIzgjZiOUI8Ij8CQfJE0kfCSrJNolCSU4JWgllyXHJfcmJyZXJocmtybo -JxgnSSd6J6sn3CgNKD8ocSiiKNQpBik4KWspnSnQKgIqNSpoKpsqzysCKzYraSudK9EsBSw5LG4soizX -LQwtQS12Last4S4WLkwugi63Lu4vJC9aL5Evxy/+MDUwbDCkMNsxEjFKMYIxujHyMioyYzKbMtQzDTNG -M38zuDPxNCs0ZTSeNNg1EzVNNYc1wjX9Njc2cjauNuk3JDdgN5w31zgUOFA4jDjIOQU5Qjl/Obw5+To2 -OnQ6sjrvOy07azuqO+g8JzxlPKQ84z0iPWE9oT3gPiA+YD6gPuA/IT9hP6I/4kAjQGRApkDnQSlBakGs -Qe5CMEJyQrVC90M6Q31DwEQDREdEikTORRJFVUWaRd5GIkZnRqtG8Ec1R3tHwEgFSEtIkUjXSR1JY0mp -SfBKN0p9SsRLDEtTS5pL4kwqTHJMuk0CTUpNk03cTiVObk63TwBPSU+TT91QJ1BxULtRBlFQUZtR5lIx -UnxSx1MTU19TqlP2VEJUj1TbVShVdVXCVg9WXFapVvdXRFeSV+BYL1h9WMtZGllpWbhaB1pWWqZa9VtF -W5Vb5Vw1XIZc1l0nXXhdyV4aXmxevV8PX2Ffs2AFYFdgqmD8YU9homH1YklinGLwY0Njl2PrZEBklGTp -ZT1lkmXnZj1mkmboZz1nk2fpaD9olmjsaUNpmmnxakhqn2r3a09rp2v/bFdsr20IbWBtuW4SbmtuxG8e -b3hv0XArcIZw4HE6cZVx8HJLcqZzAXNdc7h0FHRwdMx1KHWFdeF2Pnabdvh3VnezeBF4bnjMeSp5iXnn -ekZ6pXsEe2N7wnwhfIF84X1BfaF+AX5ifsJ/I3+Ef+WAR4CogQqBa4HNgjCCkoL0g1eDuoQdhICE44VH -hauGDoZyhteHO4efiASIaYjOiTOJmYn+imSKyoswi5aL/IxjjMqNMY2Yjf+OZo7OjzaPnpAGkG6Q1pE/ -kaiSEZJ6kuOTTZO2lCCUipT0lV+VyZY0lp+XCpd1l+CYTJi4mSSZkJn8mmia1ZtCm6+cHJyJnPedZJ3S -nkCerp8dn4uf+qBpoNihR6G2oiailqMGo3aj5qRWpMelOKWpphqmi6b9p26n4KhSqMSpN6mpqhyqj6sC -q3Wr6axcrNCtRK24ri2uoa8Wr4uwALB1sOqxYLHWskuywrM4s660JbSctRO1irYBtnm28Ldot+C4WbjR -uUq5wro7urW7LrunvCG8m70VvY++Cr6Evv+/er/1wHDA7MFnwePCX8Lbw1jD1MRRxM7FS8XIxkbGw8dB -x7/IPci8yTrJuco4yrfLNsu2zDXMtc01zbXONs62zzfPuNA50LrRPNG+0j/SwdNE08bUSdTL1U7V0dZV -1tjXXNfg2GTY6Nls2fHadtr724DcBdyK3RDdlt4c3qLfKd+v4DbgveFE4cziU+Lb42Pj6+Rz5PzlhOYN -5pbnH+ep6DLovOlG6dDqW+rl63Dr++yG7RHtnO4o7rTvQO/M8Fjw5fFy8f/yjPMZ86f0NPTC9VD13vZt -9vv3ivgZ+Kj5OPnH+lf65/t3/Af8mP0p/br+S/7c/23//9IrLC0uWiRjbGFzc25hbWVYJGNsYXNzZXNf -EBBOU0JpdG1hcEltYWdlUmVwoy0vMFpOU0ltYWdlUmVwWE5TT2JqZWN00issMjNXTlNBcnJheaIyMNIr -LDU2Xk5TTXV0YWJsZUFycmF5ozUyMNM4OQ86OzxXTlNXaGl0ZVxOU0NvbG9yU3BhY2VEMCAwABADgAzS -Kyw+P1dOU0NvbG9yoj4w0issQUJXTlNJbWFnZaJBMAAIABEAGgAkACkAMgA3AEkATABRAFMAYgBoAHUA -fACLAJIAnwCmAK4AsACyALQAuQC7AL0AxADJANQA1gDYANoA3wDiAOQA5gDoAO8BBgEiASQBJhOWE5sT -phOvE8ITxhPRE9oT3xPnE+oT7xP+FAIUCRQRFB4UIxQlFCcULBQ0FDcUPBREAAAAAAAAAgEAAAAAAAAA -QwAAAAAAAAAAAAAAAAAAFEc +S2V5ZWRBcmNoaXZlctEICVRyb290gAGuCwwbHCEaJissMzY5P0JVJG51bGzXDQ4PEBESExQVFhcYGRpW +TlNTaXplViRjbGFzc1xOU0ltYWdlRmxhZ3NWTlNSZXBzW05TVGludENvbG9yV05TQ29sb3JeTlNSZXNp +emluZ01vZGWAAoANEiDDAACAA4AAgAsQAFZ7MSwgMX3SHQ4eIFpOUy5vYmplY3RzoR+ABIAK0h0OIiWi +IySABYAGgAnTDicoKSoaXxAUTlNUSUZGUmVwcmVzZW50YXRpb25fEBlOU0ludGVybmFsTGF5b3V0RGly +ZWN0aW9ugAiAB08REmxNTQAqAAAACgAAABABAAADAAAAAQABAAABAQADAAAAAQABAAABAgADAAAAAgAI +AAgBAwADAAAAAQABAAABBgADAAAAAQABAAABCgADAAAAAQABAAABEQAEAAAAAQAAAAgBEgADAAAAAQAB +AAABFQADAAAAAQACAAABFgADAAAAAQABAAABFwAEAAAAAQAAAAIBHAADAAAAAQABAAABKAADAAAAAQAC +AAABUgADAAAAAQABAAABUwADAAAAAgABAAGHcwAHAAARnAAAANAAAAAAAAARnGFwcGwCAAAAbW50ckdS +QVlYWVogB9wACAAXAA8ALgAPYWNzcEFQUEwAAAAAbm9uZQAAAAAAAAAAAAAAAAAAAAAAAPbWAAEAAAAA +0y1hcHBsAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFZGVzYwAA +AMAAAAB5ZHNjbQAAATwAAAgaY3BydAAACVgAAAAjd3RwdAAACXwAAAAUa1RSQwAACZAAAAgMZGVzYwAA +AAAAAAAfR2VuZXJpYyBHcmF5IEdhbW1hIDIuMiBQcm9maWxlAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAG1s +dWMAAAAAAAAAHwAAAAxza1NLAAAALgAAAYRkYURLAAAAOgAAAbJjYUVTAAAAOAAAAex2aVZOAAAAQAAA +AiRwdEJSAAAASgAAAmR1a1VBAAAALAAAAq5mckZVAAAAPgAAAtpodUhVAAAANAAAAxh6aFRXAAAAGgAA +A0xrb0tSAAAAIgAAA2ZuYk5PAAAAOgAAA4hjc0NaAAAAKAAAA8JoZUlMAAAAJAAAA+pyb1JPAAAAKgAA +BA5kZURFAAAATgAABDhpdElUAAAATgAABIZzdlNFAAAAOAAABNR6aENOAAAAGgAABQxqYUpQAAAAJgAA +BSZlbEdSAAAAKgAABUxwdFBPAAAAUgAABXZubE5MAAAAQAAABchlc0VTAAAATAAABgh0aFRIAAAAMgAA +BlR0clRSAAAAJAAABoZmaUZJAAAARgAABqpockhSAAAAPgAABvBwbFBMAAAASgAABy5hckVHAAAALAAA +B3hydVJVAAAAOgAAB6RlblVTAAAAPAAAB94AVgFhAGUAbwBiAGUAYwBuAOEAIABzAGkAdgDhACAAZwBh +AG0AYQAgADIALAAyAEcAZQBuAGUAcgBpAHMAawAgAGcAcgDlACAAMgAsADIAIABnAGEAbQBtAGEALQBw +AHIAbwBmAGkAbABHAGEAbQBtAGEAIABkAGUAIABnAHIAaQBzAG8AcwAgAGcAZQBuAOgAcgBpAGMAYQAg +ADIALgAyAEMepQB1ACAAaADsAG4AaAAgAE0A4AB1ACAAeADhAG0AIABDAGgAdQBuAGcAIABHAGEAbQBt +AGEAIAAyAC4AMgBQAGUAcgBmAGkAbAAgAEcAZQBuAOkAcgBpAGMAbwAgAGQAYQAgAEcAYQBtAGEAIABk +AGUAIABDAGkAbgB6AGEAcwAgADIALAAyBBcEMAQzBDAEOwRMBD0EMAAgAEcAcgBhAHkALQQzBDAEPAQw +ACAAMgAuADIAUAByAG8AZgBpAGwAIABnAOkAbgDpAHIAaQBxAHUAZQAgAGcAcgBpAHMAIABnAGEAbQBt +AGEAIAAyACwAMgDBAGwAdABhAGwA4QBuAG8AcwAgAHMAegD8AHIAawBlACAAZwBhAG0AbQBhACAAMgAu +ADKQGnUocHCWjlFJXqYAMgAuADKCcl9pY8+P8Md8vBgAINaMwMkAIKwQucgAIAAyAC4AMgAg1QS4XNMM +x3wARwBlAG4AZQByAGkAcwBrACAAZwByAOUAIABnAGEAbQBtAGEAIAAyACwAMgAtAHAAcgBvAGYAaQBs +AE8AYgBlAGMAbgDhACABYQBlAGQA4QAgAGcAYQBtAGEAIAAyAC4AMgXSBdAF3gXUACAF0AXkBdUF6AAg +BdsF3AXcBdkAIAAyAC4AMgBHAGEAbQBhACAAZwByAGkAIABnAGUAbgBlAHIAaQBjAQMAIAAyACwAMgBB +AGwAbABnAGUAbQBlAGkAbgBlAHMAIABHAHIAYQB1AHMAdAB1AGYAZQBuAC0AUAByAG8AZgBpAGwAIABH +AGEAbQBtAGEAIAAyACwAMgBQAHIAbwBmAGkAbABvACAAZwByAGkAZwBpAG8AIABnAGUAbgBlAHIAaQBj +AG8AIABkAGUAbABsAGEAIABnAGEAbQBtAGEAIAAyACwAMgBHAGUAbgBlAHIAaQBzAGsAIABnAHIA5QAg +ADIALAAyACAAZwBhAG0AbQBhAHAAcgBvAGYAaQBsZm6QGnBwXqZ8+2VwADIALgAyY8+P8GWHTvZOAIIs +MLAw7DCkMKww8zDeACAAMgAuADIAIDDXMO0w1TChMKQw6wOTA7UDvQO5A7oDzAAgA5MDugPBA7kAIAOT +A6wDvAO8A7EAIAAyAC4AMgBQAGUAcgBmAGkAbAAgAGcAZQBuAOkAcgBpAGMAbwAgAGQAZQAgAGMAaQBu +AHoAZQBuAHQAbwBzACAAZABhACAARwBhAG0AbQBhACAAMgAsADIAQQBsAGcAZQBtAGUAZQBuACAAZwBy +AGkAagBzACAAZwBhAG0AbQBhACAAMgAsADIALQBwAHIAbwBmAGkAZQBsAFAAZQByAGYAaQBsACAAZwBl +AG4A6QByAGkAYwBvACAAZABlACAAZwBhAG0AbQBhACAAZABlACAAZwByAGkAcwBlAHMAIAAyACwAMg4j +DjEOBw4qDjUOQQ4BDiEOIQ4yDkAOAQ4jDiIOTA4XDjEOSA4nDkQOGwAgADIALgAyAEcAZQBuAGUAbAAg +AEcAcgBpACAARwBhAG0AYQAgADIALAAyAFkAbABlAGkAbgBlAG4AIABoAGEAcgBtAGEAYQBuACAAZwBh +AG0AbQBhACAAMgAsADIAIAAtAHAAcgBvAGYAaQBpAGwAaQBHAGUAbgBlAHIAaQENAGsAaQAgAEcAcgBh +AHkAIABHAGEAbQBtAGEAIAAyAC4AMgAgAHAAcgBvAGYAaQBsAFUAbgBpAHcAZQByAHMAYQBsAG4AeQAg +AHAAcgBvAGYAaQBsACAAcwB6AGEAcgBvAVsAYwBpACAAZwBhAG0AbQBhACAAMgAsADIGOgYnBkUGJwAg +ADIALgAyACAGRAZIBkYAIAYxBkUGJwYvBkoAIAY5BicGRQQeBDEESQQwBE8AIARBBDUEQAQwBE8AIAQz +BDAEPAQ8BDAAIAAyACwAMgAtBD8EQAQ+BEQEOAQ7BEwARwBlAG4AZQByAGkAYwAgAEcAcgBhAHkAIABH +AGEAbQBtAGEAIAAyAC4AMgAgAFAAcgBvAGYAaQBsAGUAAHRleHQAAAAAQ29weXJpZ2h0IEFwcGxlIElu +Yy4sIDIwMTIAAFhZWiAAAAAAAADzUQABAAAAARbMY3VydgAAAAAAAAQAAAAABQAKAA8AFAAZAB4AIwAo +AC0AMgA3ADsAQABFAEoATwBUAFkAXgBjAGgAbQByAHcAfACBAIYAiwCQAJUAmgCfAKQAqQCuALIAtwC8 +AMEAxgDLANAA1QDbAOAA5QDrAPAA9gD7AQEBBwENARMBGQEfASUBKwEyATgBPgFFAUwBUgFZAWABZwFu +AXUBfAGDAYsBkgGaAaEBqQGxAbkBwQHJAdEB2QHhAekB8gH6AgMCDAIUAh0CJgIvAjgCQQJLAlQCXQJn +AnECegKEAo4CmAKiAqwCtgLBAssC1QLgAusC9QMAAwsDFgMhAy0DOANDA08DWgNmA3IDfgOKA5YDogOu +A7oDxwPTA+AD7AP5BAYEEwQgBC0EOwRIBFUEYwRxBH4EjASaBKgEtgTEBNME4QTwBP4FDQUcBSsFOgVJ +BVgFZwV3BYYFlgWmBbUFxQXVBeUF9gYGBhYGJwY3BkgGWQZqBnsGjAadBq8GwAbRBuMG9QcHBxkHKwc9 +B08HYQd0B4YHmQesB78H0gflB/gICwgfCDIIRghaCG4IggiWCKoIvgjSCOcI+wkQCSUJOglPCWQJeQmP +CaQJugnPCeUJ+woRCicKPQpUCmoKgQqYCq4KxQrcCvMLCwsiCzkLUQtpC4ALmAuwC8gL4Qv5DBIMKgxD +DFwMdQyODKcMwAzZDPMNDQ0mDUANWg10DY4NqQ3DDd4N+A4TDi4OSQ5kDn8Omw62DtIO7g8JDyUPQQ9e +D3oPlg+zD88P7BAJECYQQxBhEH4QmxC5ENcQ9RETETERTxFtEYwRqhHJEegSBxImEkUSZBKEEqMSwxLj +EwMTIxNDE2MTgxOkE8UT5RQGFCcUSRRqFIsUrRTOFPAVEhU0FVYVeBWbFb0V4BYDFiYWSRZsFo8WshbW +FvoXHRdBF2UXiReuF9IX9xgbGEAYZRiKGK8Y1Rj6GSAZRRlrGZEZtxndGgQaKhpRGncanhrFGuwbFBs7 +G2MbihuyG9ocAhwqHFIcexyjHMwc9R0eHUcdcB2ZHcMd7B4WHkAeah6UHr4e6R8THz4faR+UH78f6iAV +IEEgbCCYIMQg8CEcIUghdSGhIc4h+yInIlUigiKvIt0jCiM4I2YjlCPCI/AkHyRNJHwkqyTaJQklOCVo +JZclxyX3JicmVyaHJrcm6CcYJ0kneierJ9woDSg/KHEooijUKQYpOClrKZ0p0CoCKjUqaCqbKs8rAis2 +K2krnSvRLAUsOSxuLKIs1y0MLUEtdi2rLeEuFi5MLoIuty7uLyQvWi+RL8cv/jA1MGwwpDDbMRIxSjGC +Mbox8jIqMmMymzLUMw0zRjN/M7gz8TQrNGU0njTYNRM1TTWHNcI1/TY3NnI2rjbpNyQ3YDecN9c4FDhQ +OIw4yDkFOUI5fzm8Ofk6Njp0OrI67zstO2s7qjvoPCc8ZTykPOM9Ij1hPaE94D4gPmA+oD7gPyE/YT+i +P+JAI0BkQKZA50EpQWpBrEHuQjBCckK1QvdDOkN9Q8BEA0RHRIpEzkUSRVVFmkXeRiJGZ0arRvBHNUd7 +R8BIBUhLSJFI10kdSWNJqUnwSjdKfUrESwxLU0uaS+JMKkxyTLpNAk1KTZNN3E4lTm5Ot08AT0lPk0/d +UCdQcVC7UQZRUFGbUeZSMVJ8UsdTE1NfU6pT9lRCVI9U21UoVXVVwlYPVlxWqVb3V0RXklfgWC9YfVjL +WRpZaVm4WgdaVlqmWvVbRVuVW+VcNVyGXNZdJ114XcleGl5sXr1fD19hX7NgBWBXYKpg/GFPYaJh9WJJ +Ypxi8GNDY5dj62RAZJRk6WU9ZZJl52Y9ZpJm6Gc9Z5Nn6Wg/aJZo7GlDaZpp8WpIap9q92tPa6dr/2xX +bK9tCG1gbbluEm5rbsRvHm94b9FwK3CGcOBxOnGVcfByS3KmcwFzXXO4dBR0cHTMdSh1hXXhdj52m3b4 +d1Z3s3gReG54zHkqeYl553pGeqV7BHtje8J8IXyBfOF9QX2hfgF+Yn7CfyN/hH/lgEeAqIEKgWuBzYIw +gpKC9INXg7qEHYSAhOOFR4Wrhg6GcobXhzuHn4gEiGmIzokziZmJ/opkisqLMIuWi/yMY4zKjTGNmI3/ +jmaOzo82j56QBpBukNaRP5GokhGSepLjk02TtpQglIqU9JVflcmWNJaflwqXdZfgmEyYuJkkmZCZ/Jpo +mtWbQpuvnByciZz3nWSd0p5Anq6fHZ+Ln/qgaaDYoUehtqImopajBqN2o+akVqTHpTilqaYapoum/adu +p+CoUqjEqTepqaocqo+rAqt1q+msXKzQrUStuK4trqGvFq+LsACwdbDqsWCx1rJLssKzOLOutCW0nLUT +tYq2AbZ5tvC3aLfguFm40blKucK6O7q1uy67p7whvJu9Fb2Pvgq+hL7/v3q/9cBwwOzBZ8Hjwl/C28NY +w9TEUcTOxUvFyMZGxsPHQce/yD3IvMk6ybnKOMq3yzbLtsw1zLXNNc21zjbOts83z7jQOdC60TzRvtI/ +0sHTRNPG1EnUy9VO1dHWVdbY11zX4Nhk2OjZbNnx2nba+9uA3AXcit0Q3ZbeHN6i3ynfr+A24L3hROHM +4lPi2+Nj4+vkc+T85YTmDeaW5x/nqegy6LzpRunQ6lvq5etw6/vshu0R7ZzuKO6070DvzPBY8OXxcvH/ +8ozzGfOn9DT0wvVQ9d72bfb794r4Gfio+Tj5x/pX+uf7d/wH/Jj9Kf26/kv+3P9t///SLS4vMFokY2xh +c3NuYW1lWCRjbGFzc2VzXxAQTlNCaXRtYXBJbWFnZVJlcKMvMTJaTlNJbWFnZVJlcFhOU09iamVjdNIt +LjQ1V05TQXJyYXmiNDLSLS43OF5OU011dGFibGVBcnJheaM3NDLTOjsOPD0+V05TV2hpdGVcTlNDb2xv +clNwYWNlRDAgMAAQA4AM0i0uQEFXTlNDb2xvcqJAMtItLkNEV05TSW1hZ2WiQzIACAARABoAJAApADIA +NwBJAEwAUQBTAGIAaAB3AH4AhQCSAJkApQCtALwAvgDAAMUAxwDJAMsAzQDUANkA5ADmAOgA6gDvAPIA +9AD2APgA/wEWATIBNAE2E6YTqxO2E78T0hPWE+ET6hPvE/cT+hP/FA4UEhQZFCEULhQzFDUUNxQ8FEQU +RxRMFFQAAAAAAAACAQAAAAAAAABFAAAAAAAAAAAAAAAAAAAUVw diff --git a/i18n/src/main/resources/ca.lproj/Command.xib b/i18n/src/main/resources/ca.lproj/Command.xib index a526a3b00f..f908e82f69 100644 --- a/i18n/src/main/resources/ca.lproj/Command.xib +++ b/i18n/src/main/resources/ca.lproj/Command.xib @@ -1,7 +1,7 @@ - + - + diff --git a/i18n/src/main/resources/ca.lproj/Connection.xib b/i18n/src/main/resources/ca.lproj/Connection.xib index 3e082a7b2b..9ae0cc65b7 100644 --- a/i18n/src/main/resources/ca.lproj/Connection.xib +++ b/i18n/src/main/resources/ca.lproj/Connection.xib @@ -1,7 +1,7 @@ - + - + @@ -76,7 +76,7 @@ - + diff --git a/i18n/src/main/resources/ca.lproj/Info.xib b/i18n/src/main/resources/ca.lproj/Info.xib index a5a463685b..ff1ea5707f 100644 --- a/i18n/src/main/resources/ca.lproj/Info.xib +++ b/i18n/src/main/resources/ca.lproj/Info.xib @@ -1,7 +1,7 @@ - + - + @@ -89,7 +89,7 @@ - + @@ -1017,21 +1017,21 @@ - + - + - + @@ -1121,21 +1121,21 @@ - + - + - + @@ -1250,27 +1250,27 @@ diff --git a/i18n/src/main/resources/ca.lproj/Login.strings b/i18n/src/main/resources/ca.lproj/Login.strings index eee314abac..8e6953bb5d 100644 Binary files a/i18n/src/main/resources/ca.lproj/Login.strings and b/i18n/src/main/resources/ca.lproj/Login.strings differ diff --git a/i18n/src/main/resources/ca.lproj/Main.xib b/i18n/src/main/resources/ca.lproj/Main.xib index 886264ebaf..cb67111989 100644 --- a/i18n/src/main/resources/ca.lproj/Main.xib +++ b/i18n/src/main/resources/ca.lproj/Main.xib @@ -1,7 +1,7 @@ - + - + @@ -159,19 +159,19 @@ - + - + - + @@ -321,12 +321,12 @@ CA - + - + @@ -566,6 +566,10 @@ CA - + + + + + diff --git a/i18n/src/main/resources/ca.lproj/Preferences.xib b/i18n/src/main/resources/ca.lproj/Preferences.xib index df4ec14e58..b4e78524d5 100644 --- a/i18n/src/main/resources/ca.lproj/Preferences.xib +++ b/i18n/src/main/resources/ca.lproj/Preferences.xib @@ -1,7 +1,7 @@ - + - + @@ -266,7 +266,7 @@ - + @@ -307,7 +307,7 @@ - + @@ -538,7 +538,7 @@ - + @@ -578,7 +578,7 @@ - + @@ -613,7 +613,7 @@ - + @@ -642,7 +642,7 @@ - + @@ -1218,7 +1218,7 @@ - + @@ -1250,7 +1250,7 @@ - + @@ -1290,7 +1290,7 @@ - + @@ -1363,7 +1363,7 @@ - + @@ -1558,7 +1558,7 @@ - + @@ -1618,7 +1618,7 @@ - + @@ -1643,7 +1643,7 @@ - + @@ -1685,7 +1685,7 @@ - + @@ -1713,7 +1713,7 @@ - + @@ -1741,7 +1741,7 @@ - + @@ -1770,7 +1770,7 @@ - + @@ -1805,7 +1805,7 @@ - + @@ -1888,7 +1888,7 @@ - + @@ -1947,7 +1947,7 @@ - + @@ -1975,7 +1975,7 @@ - + @@ -2004,7 +2004,7 @@ - + diff --git a/i18n/src/main/resources/ca.lproj/Profiles.xib b/i18n/src/main/resources/ca.lproj/Profiles.xib index 18af5a58f4..20e5ce0f36 100644 --- a/i18n/src/main/resources/ca.lproj/Profiles.xib +++ b/i18n/src/main/resources/ca.lproj/Profiles.xib @@ -1,7 +1,7 @@ - + - + diff --git a/i18n/src/main/resources/ca.lproj/Prompt.xib b/i18n/src/main/resources/ca.lproj/Prompt.xib index a42c0861e1..411864ec29 100644 --- a/i18n/src/main/resources/ca.lproj/Prompt.xib +++ b/i18n/src/main/resources/ca.lproj/Prompt.xib @@ -1,7 +1,7 @@ - + - + @@ -95,7 +95,7 @@ Gw - + diff --git a/i18n/src/main/resources/ca.lproj/S3.strings b/i18n/src/main/resources/ca.lproj/S3.strings index a67b354725..31ffdfeb44 100644 Binary files a/i18n/src/main/resources/ca.lproj/S3.strings and b/i18n/src/main/resources/ca.lproj/S3.strings differ diff --git a/i18n/src/main/resources/ca.lproj/Updater.strings b/i18n/src/main/resources/ca.lproj/Updater.strings index 2b8126f855..de886224f2 100644 Binary files a/i18n/src/main/resources/ca.lproj/Updater.strings and b/i18n/src/main/resources/ca.lproj/Updater.strings differ diff --git a/i18n/src/main/resources/cs.lproj/Bookmark.xib b/i18n/src/main/resources/cs.lproj/Bookmark.xib index 3c6c9c53a6..de1863cc40 100644 --- a/i18n/src/main/resources/cs.lproj/Bookmark.xib +++ b/i18n/src/main/resources/cs.lproj/Bookmark.xib @@ -1,7 +1,7 @@ - + - + @@ -92,7 +92,7 @@ - + @@ -489,95 +489,95 @@ YnBsaXN0MDDUAQIDBAUGBwpYJHZlcnNpb25ZJGFyY2hpdmVyVCR0b3BYJG9iamVjdHMSAAGGoF8QD05T -S2V5ZWRBcmNoaXZlctEICVRyb290gAGuCwwZGh8UJCkqMTQ3PUBVJG51bGzWDQ4PEBESExQVFhcYVk5T -U2l6ZV5OU1Jlc2l6aW5nTW9kZVYkY2xhc3NcTlNJbWFnZUZsYWdzVk5TUmVwc1dOU0NvbG9ygAIQAIAN -EiDDAACAA4ALVnsxLCAxfdIbDxweWk5TLm9iamVjdHOhHYAEgArSGw8gI6IhIoAFgAaACdMPJSYnKBRf -EBROU1RJRkZSZXByZXNlbnRhdGlvbl8QGU5TSW50ZXJuYWxMYXlvdXREaXJlY3Rpb26ACIAHTxESbE1N -ACoAAAAKAAAAEAEAAAMAAAABAAEAAAEBAAMAAAABAAEAAAECAAMAAAACAAgACAEDAAMAAAABAAEAAAEG -AAMAAAABAAEAAAEKAAMAAAABAAEAAAERAAQAAAABAAAACAESAAMAAAABAAEAAAEVAAMAAAABAAIAAAEW -AAMAAAABAAEAAAEXAAQAAAABAAAAAgEcAAMAAAABAAEAAAEoAAMAAAABAAIAAAFSAAMAAAABAAEAAAFT -AAMAAAACAAEAAYdzAAcAABGcAAAA0AAAAAAAABGcYXBwbAIAAABtbnRyR1JBWVhZWiAH3AAIABcADwAu -AA9hY3NwQVBQTAAAAABub25lAAAAAAAAAAAAAAAAAAAAAAAA9tYAAQAAAADTLWFwcGwAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAVkZXNjAAAAwAAAAHlkc2NtAAABPAAA -CBpjcHJ0AAAJWAAAACN3dHB0AAAJfAAAABRrVFJDAAAJkAAACAxkZXNjAAAAAAAAAB9HZW5lcmljIEdy -YXkgR2FtbWEgMi4yIFByb2ZpbGUAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAbWx1YwAAAAAAAAAfAAAADHNr -U0sAAAAuAAABhGRhREsAAAA6AAABsmNhRVMAAAA4AAAB7HZpVk4AAABAAAACJHB0QlIAAABKAAACZHVr -VUEAAAAsAAACrmZyRlUAAAA+AAAC2mh1SFUAAAA0AAADGHpoVFcAAAAaAAADTGtvS1IAAAAiAAADZm5i -Tk8AAAA6AAADiGNzQ1oAAAAoAAADwmhlSUwAAAAkAAAD6nJvUk8AAAAqAAAEDmRlREUAAABOAAAEOGl0 -SVQAAABOAAAEhnN2U0UAAAA4AAAE1HpoQ04AAAAaAAAFDGphSlAAAAAmAAAFJmVsR1IAAAAqAAAFTHB0 -UE8AAABSAAAFdm5sTkwAAABAAAAFyGVzRVMAAABMAAAGCHRoVEgAAAAyAAAGVHRyVFIAAAAkAAAGhmZp -RkkAAABGAAAGqmhySFIAAAA+AAAG8HBsUEwAAABKAAAHLmFyRUcAAAAsAAAHeHJ1UlUAAAA6AAAHpGVu -VVMAAAA8AAAH3gBWAWEAZQBvAGIAZQBjAG4A4QAgAHMAaQB2AOEAIABnAGEAbQBhACAAMgAsADIARwBl -AG4AZQByAGkAcwBrACAAZwByAOUAIAAyACwAMgAgAGcAYQBtAG0AYQAtAHAAcgBvAGYAaQBsAEcAYQBt -AG0AYQAgAGQAZQAgAGcAcgBpAHMAbwBzACAAZwBlAG4A6AByAGkAYwBhACAAMgAuADIAQx6lAHUAIABo -AOwAbgBoACAATQDgAHUAIAB4AOEAbQAgAEMAaAB1AG4AZwAgAEcAYQBtAG0AYQAgADIALgAyAFAAZQBy -AGYAaQBsACAARwBlAG4A6QByAGkAYwBvACAAZABhACAARwBhAG0AYQAgAGQAZQAgAEMAaQBuAHoAYQBz -ACAAMgAsADIEFwQwBDMEMAQ7BEwEPQQwACAARwByAGEAeQAtBDMEMAQ8BDAAIAAyAC4AMgBQAHIAbwBm -AGkAbAAgAGcA6QBuAOkAcgBpAHEAdQBlACAAZwByAGkAcwAgAGcAYQBtAG0AYQAgADIALAAyAMEAbAB0 -AGEAbADhAG4AbwBzACAAcwB6APwAcgBrAGUAIABnAGEAbQBtAGEAIAAyAC4AMpAadShwcJaOUUlepgAy -AC4AMoJyX2ljz4/wx3y8GAAg1ozAyQAgrBC5yAAgADIALgAyACDVBLhc0wzHfABHAGUAbgBlAHIAaQBz -AGsAIABnAHIA5QAgAGcAYQBtAG0AYQAgADIALAAyAC0AcAByAG8AZgBpAGwATwBiAGUAYwBuAOEAIAFh -AGUAZADhACAAZwBhAG0AYQAgADIALgAyBdIF0AXeBdQAIAXQBeQF1QXoACAF2wXcBdwF2QAgADIALgAy -AEcAYQBtAGEAIABnAHIAaQAgAGcAZQBuAGUAcgBpAGMBAwAgADIALAAyAEEAbABsAGcAZQBtAGUAaQBu -AGUAcwAgAEcAcgBhAHUAcwB0AHUAZgBlAG4ALQBQAHIAbwBmAGkAbAAgAEcAYQBtAG0AYQAgADIALAAy -AFAAcgBvAGYAaQBsAG8AIABnAHIAaQBnAGkAbwAgAGcAZQBuAGUAcgBpAGMAbwAgAGQAZQBsAGwAYQAg -AGcAYQBtAG0AYQAgADIALAAyAEcAZQBuAGUAcgBpAHMAawAgAGcAcgDlACAAMgAsADIAIABnAGEAbQBt -AGEAcAByAG8AZgBpAGxmbpAacHBepnz7ZXAAMgAuADJjz4/wZYdO9k4AgiwwsDDsMKQwrDDzMN4AIAAy -AC4AMgAgMNcw7TDVMKEwpDDrA5MDtQO9A7kDugPMACADkwO6A8EDuQAgA5MDrAO8A7wDsQAgADIALgAy -AFAAZQByAGYAaQBsACAAZwBlAG4A6QByAGkAYwBvACAAZABlACAAYwBpAG4AegBlAG4AdABvAHMAIABk -AGEAIABHAGEAbQBtAGEAIAAyACwAMgBBAGwAZwBlAG0AZQBlAG4AIABnAHIAaQBqAHMAIABnAGEAbQBt -AGEAIAAyACwAMgAtAHAAcgBvAGYAaQBlAGwAUABlAHIAZgBpAGwAIABnAGUAbgDpAHIAaQBjAG8AIABk -AGUAIABnAGEAbQBtAGEAIABkAGUAIABnAHIAaQBzAGUAcwAgADIALAAyDiMOMQ4HDioONQ5BDgEOIQ4h -DjIOQA4BDiMOIg5MDhcOMQ5IDicORA4bACAAMgAuADIARwBlAG4AZQBsACAARwByAGkAIABHAGEAbQBh -ACAAMgAsADIAWQBsAGUAaQBuAGUAbgAgAGgAYQByAG0AYQBhAG4AIABnAGEAbQBtAGEAIAAyACwAMgAg -AC0AcAByAG8AZgBpAGkAbABpAEcAZQBuAGUAcgBpAQ0AawBpACAARwByAGEAeQAgAEcAYQBtAG0AYQAg -ADIALgAyACAAcAByAG8AZgBpAGwAVQBuAGkAdwBlAHIAcwBhAGwAbgB5ACAAcAByAG8AZgBpAGwAIABz -AHoAYQByAG8BWwBjAGkAIABnAGEAbQBtAGEAIAAyACwAMgY6BicGRQYnACAAMgAuADIAIAZEBkgGRgAg -BjEGRQYnBi8GSgAgBjkGJwZFBB4EMQRJBDAETwAgBEEENQRABDAETwAgBDMEMAQ8BDwEMAAgADIALAAy -AC0EPwRABD4ERAQ4BDsETABHAGUAbgBlAHIAaQBjACAARwByAGEAeQAgAEcAYQBtAG0AYQAgADIALgAy -ACAAUAByAG8AZgBpAGwAZQAAdGV4dAAAAABDb3B5cmlnaHQgQXBwbGUgSW5jLiwgMjAxMgAAWFlaIAAA -AAAAAPNRAAEAAAABFsxjdXJ2AAAAAAAABAAAAAAFAAoADwAUABkAHgAjACgALQAyADcAOwBAAEUASgBP -AFQAWQBeAGMAaABtAHIAdwB8AIEAhgCLAJAAlQCaAJ8ApACpAK4AsgC3ALwAwQDGAMsA0ADVANsA4ADl -AOsA8AD2APsBAQEHAQ0BEwEZAR8BJQErATIBOAE+AUUBTAFSAVkBYAFnAW4BdQF8AYMBiwGSAZoBoQGp -AbEBuQHBAckB0QHZAeEB6QHyAfoCAwIMAhQCHQImAi8COAJBAksCVAJdAmcCcQJ6AoQCjgKYAqICrAK2 -AsECywLVAuAC6wL1AwADCwMWAyEDLQM4A0MDTwNaA2YDcgN+A4oDlgOiA64DugPHA9MD4APsA/kEBgQT -BCAELQQ7BEgEVQRjBHEEfgSMBJoEqAS2BMQE0wThBPAE/gUNBRwFKwU6BUkFWAVnBXcFhgWWBaYFtQXF -BdUF5QX2BgYGFgYnBjcGSAZZBmoGewaMBp0GrwbABtEG4wb1BwcHGQcrBz0HTwdhB3QHhgeZB6wHvwfS -B+UH+AgLCB8IMghGCFoIbgiCCJYIqgi+CNII5wj7CRAJJQk6CU8JZAl5CY8JpAm6Cc8J5Qn7ChEKJwo9 -ClQKagqBCpgKrgrFCtwK8wsLCyILOQtRC2kLgAuYC7ALyAvhC/kMEgwqDEMMXAx1DI4MpwzADNkM8w0N -DSYNQA1aDXQNjg2pDcMN3g34DhMOLg5JDmQOfw6bDrYO0g7uDwkPJQ9BD14Peg+WD7MPzw/sEAkQJhBD -EGEQfhCbELkQ1xD1ERMRMRFPEW0RjBGqEckR6BIHEiYSRRJkEoQSoxLDEuMTAxMjE0MTYxODE6QTxRPl -FAYUJxRJFGoUixStFM4U8BUSFTQVVhV4FZsVvRXgFgMWJhZJFmwWjxayFtYW+hcdF0EXZReJF64X0hf3 -GBsYQBhlGIoYrxjVGPoZIBlFGWsZkRm3Gd0aBBoqGlEadxqeGsUa7BsUGzsbYxuKG7Ib2hwCHCocUhx7 -HKMczBz1HR4dRx1wHZkdwx3sHhYeQB5qHpQevh7pHxMfPh9pH5Qfvx/qIBUgQSBsIJggxCDwIRwhSCF1 -IaEhziH7IiciVSKCIq8i3SMKIzgjZiOUI8Ij8CQfJE0kfCSrJNolCSU4JWgllyXHJfcmJyZXJocmtybo -JxgnSSd6J6sn3CgNKD8ocSiiKNQpBik4KWspnSnQKgIqNSpoKpsqzysCKzYraSudK9EsBSw5LG4soizX -LQwtQS12Last4S4WLkwugi63Lu4vJC9aL5Evxy/+MDUwbDCkMNsxEjFKMYIxujHyMioyYzKbMtQzDTNG -M38zuDPxNCs0ZTSeNNg1EzVNNYc1wjX9Njc2cjauNuk3JDdgN5w31zgUOFA4jDjIOQU5Qjl/Obw5+To2 -OnQ6sjrvOy07azuqO+g8JzxlPKQ84z0iPWE9oT3gPiA+YD6gPuA/IT9hP6I/4kAjQGRApkDnQSlBakGs -Qe5CMEJyQrVC90M6Q31DwEQDREdEikTORRJFVUWaRd5GIkZnRqtG8Ec1R3tHwEgFSEtIkUjXSR1JY0mp -SfBKN0p9SsRLDEtTS5pL4kwqTHJMuk0CTUpNk03cTiVObk63TwBPSU+TT91QJ1BxULtRBlFQUZtR5lIx -UnxSx1MTU19TqlP2VEJUj1TbVShVdVXCVg9WXFapVvdXRFeSV+BYL1h9WMtZGllpWbhaB1pWWqZa9VtF -W5Vb5Vw1XIZc1l0nXXhdyV4aXmxevV8PX2Ffs2AFYFdgqmD8YU9homH1YklinGLwY0Njl2PrZEBklGTp -ZT1lkmXnZj1mkmboZz1nk2fpaD9olmjsaUNpmmnxakhqn2r3a09rp2v/bFdsr20IbWBtuW4SbmtuxG8e -b3hv0XArcIZw4HE6cZVx8HJLcqZzAXNdc7h0FHRwdMx1KHWFdeF2Pnabdvh3VnezeBF4bnjMeSp5iXnn -ekZ6pXsEe2N7wnwhfIF84X1BfaF+AX5ifsJ/I3+Ef+WAR4CogQqBa4HNgjCCkoL0g1eDuoQdhICE44VH -hauGDoZyhteHO4efiASIaYjOiTOJmYn+imSKyoswi5aL/IxjjMqNMY2Yjf+OZo7OjzaPnpAGkG6Q1pE/ -kaiSEZJ6kuOTTZO2lCCUipT0lV+VyZY0lp+XCpd1l+CYTJi4mSSZkJn8mmia1ZtCm6+cHJyJnPedZJ3S -nkCerp8dn4uf+qBpoNihR6G2oiailqMGo3aj5qRWpMelOKWpphqmi6b9p26n4KhSqMSpN6mpqhyqj6sC -q3Wr6axcrNCtRK24ri2uoa8Wr4uwALB1sOqxYLHWskuywrM4s660JbSctRO1irYBtnm28Ldot+C4WbjR -uUq5wro7urW7LrunvCG8m70VvY++Cr6Evv+/er/1wHDA7MFnwePCX8Lbw1jD1MRRxM7FS8XIxkbGw8dB -x7/IPci8yTrJuco4yrfLNsu2zDXMtc01zbXONs62zzfPuNA50LrRPNG+0j/SwdNE08bUSdTL1U7V0dZV -1tjXXNfg2GTY6Nls2fHadtr724DcBdyK3RDdlt4c3qLfKd+v4DbgveFE4cziU+Lb42Pj6+Rz5PzlhOYN -5pbnH+ep6DLovOlG6dDqW+rl63Dr++yG7RHtnO4o7rTvQO/M8Fjw5fFy8f/yjPMZ86f0NPTC9VD13vZt -9vv3ivgZ+Kj5OPnH+lf65/t3/Af8mP0p/br+S/7c/23//9IrLC0uWiRjbGFzc25hbWVYJGNsYXNzZXNf -EBBOU0JpdG1hcEltYWdlUmVwoy0vMFpOU0ltYWdlUmVwWE5TT2JqZWN00issMjNXTlNBcnJheaIyMNIr -LDU2Xk5TTXV0YWJsZUFycmF5ozUyMNM4OQ86OzxXTlNXaGl0ZVxOU0NvbG9yU3BhY2VEMCAwABADgAzS -Kyw+P1dOU0NvbG9yoj4w0issQUJXTlNJbWFnZaJBMAAIABEAGgAkACkAMgA3AEkATABRAFMAYgBoAHUA -fACLAJIAnwCmAK4AsACyALQAuQC7AL0AxADJANQA1gDYANoA3wDiAOQA5gDoAO8BBgEiASQBJhOWE5sT -phOvE8ITxhPRE9oT3xPnE+oT7xP+FAIUCRQRFB4UIxQlFCcULBQ0FDcUPBREAAAAAAAAAgEAAAAAAAAA -QwAAAAAAAAAAAAAAAAAAFEc +S2V5ZWRBcmNoaXZlctEICVRyb290gAGuCwwbHCEaJissMzY5P0JVJG51bGzXDQ4PEBESExQVFhcYGRpW +TlNTaXplViRjbGFzc1xOU0ltYWdlRmxhZ3NWTlNSZXBzW05TVGludENvbG9yV05TQ29sb3JeTlNSZXNp +emluZ01vZGWAAoANEiDDAACAA4AAgAsQAFZ7MSwgMX3SHQ4eIFpOUy5vYmplY3RzoR+ABIAK0h0OIiWi +IySABYAGgAnTDicoKSoaXxAUTlNUSUZGUmVwcmVzZW50YXRpb25fEBlOU0ludGVybmFsTGF5b3V0RGly +ZWN0aW9ugAiAB08REmxNTQAqAAAACgAAABABAAADAAAAAQABAAABAQADAAAAAQABAAABAgADAAAAAgAI +AAgBAwADAAAAAQABAAABBgADAAAAAQABAAABCgADAAAAAQABAAABEQAEAAAAAQAAAAgBEgADAAAAAQAB +AAABFQADAAAAAQACAAABFgADAAAAAQABAAABFwAEAAAAAQAAAAIBHAADAAAAAQABAAABKAADAAAAAQAC +AAABUgADAAAAAQABAAABUwADAAAAAgABAAGHcwAHAAARnAAAANAAAAAAAAARnGFwcGwCAAAAbW50ckdS +QVlYWVogB9wACAAXAA8ALgAPYWNzcEFQUEwAAAAAbm9uZQAAAAAAAAAAAAAAAAAAAAAAAPbWAAEAAAAA +0y1hcHBsAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFZGVzYwAA +AMAAAAB5ZHNjbQAAATwAAAgaY3BydAAACVgAAAAjd3RwdAAACXwAAAAUa1RSQwAACZAAAAgMZGVzYwAA +AAAAAAAfR2VuZXJpYyBHcmF5IEdhbW1hIDIuMiBQcm9maWxlAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAG1s +dWMAAAAAAAAAHwAAAAxza1NLAAAALgAAAYRkYURLAAAAOgAAAbJjYUVTAAAAOAAAAex2aVZOAAAAQAAA +AiRwdEJSAAAASgAAAmR1a1VBAAAALAAAAq5mckZVAAAAPgAAAtpodUhVAAAANAAAAxh6aFRXAAAAGgAA +A0xrb0tSAAAAIgAAA2ZuYk5PAAAAOgAAA4hjc0NaAAAAKAAAA8JoZUlMAAAAJAAAA+pyb1JPAAAAKgAA +BA5kZURFAAAATgAABDhpdElUAAAATgAABIZzdlNFAAAAOAAABNR6aENOAAAAGgAABQxqYUpQAAAAJgAA +BSZlbEdSAAAAKgAABUxwdFBPAAAAUgAABXZubE5MAAAAQAAABchlc0VTAAAATAAABgh0aFRIAAAAMgAA +BlR0clRSAAAAJAAABoZmaUZJAAAARgAABqpockhSAAAAPgAABvBwbFBMAAAASgAABy5hckVHAAAALAAA +B3hydVJVAAAAOgAAB6RlblVTAAAAPAAAB94AVgFhAGUAbwBiAGUAYwBuAOEAIABzAGkAdgDhACAAZwBh +AG0AYQAgADIALAAyAEcAZQBuAGUAcgBpAHMAawAgAGcAcgDlACAAMgAsADIAIABnAGEAbQBtAGEALQBw +AHIAbwBmAGkAbABHAGEAbQBtAGEAIABkAGUAIABnAHIAaQBzAG8AcwAgAGcAZQBuAOgAcgBpAGMAYQAg +ADIALgAyAEMepQB1ACAAaADsAG4AaAAgAE0A4AB1ACAAeADhAG0AIABDAGgAdQBuAGcAIABHAGEAbQBt +AGEAIAAyAC4AMgBQAGUAcgBmAGkAbAAgAEcAZQBuAOkAcgBpAGMAbwAgAGQAYQAgAEcAYQBtAGEAIABk +AGUAIABDAGkAbgB6AGEAcwAgADIALAAyBBcEMAQzBDAEOwRMBD0EMAAgAEcAcgBhAHkALQQzBDAEPAQw +ACAAMgAuADIAUAByAG8AZgBpAGwAIABnAOkAbgDpAHIAaQBxAHUAZQAgAGcAcgBpAHMAIABnAGEAbQBt +AGEAIAAyACwAMgDBAGwAdABhAGwA4QBuAG8AcwAgAHMAegD8AHIAawBlACAAZwBhAG0AbQBhACAAMgAu +ADKQGnUocHCWjlFJXqYAMgAuADKCcl9pY8+P8Md8vBgAINaMwMkAIKwQucgAIAAyAC4AMgAg1QS4XNMM +x3wARwBlAG4AZQByAGkAcwBrACAAZwByAOUAIABnAGEAbQBtAGEAIAAyACwAMgAtAHAAcgBvAGYAaQBs +AE8AYgBlAGMAbgDhACABYQBlAGQA4QAgAGcAYQBtAGEAIAAyAC4AMgXSBdAF3gXUACAF0AXkBdUF6AAg +BdsF3AXcBdkAIAAyAC4AMgBHAGEAbQBhACAAZwByAGkAIABnAGUAbgBlAHIAaQBjAQMAIAAyACwAMgBB +AGwAbABnAGUAbQBlAGkAbgBlAHMAIABHAHIAYQB1AHMAdAB1AGYAZQBuAC0AUAByAG8AZgBpAGwAIABH +AGEAbQBtAGEAIAAyACwAMgBQAHIAbwBmAGkAbABvACAAZwByAGkAZwBpAG8AIABnAGUAbgBlAHIAaQBj +AG8AIABkAGUAbABsAGEAIABnAGEAbQBtAGEAIAAyACwAMgBHAGUAbgBlAHIAaQBzAGsAIABnAHIA5QAg +ADIALAAyACAAZwBhAG0AbQBhAHAAcgBvAGYAaQBsZm6QGnBwXqZ8+2VwADIALgAyY8+P8GWHTvZOAIIs +MLAw7DCkMKww8zDeACAAMgAuADIAIDDXMO0w1TChMKQw6wOTA7UDvQO5A7oDzAAgA5MDugPBA7kAIAOT +A6wDvAO8A7EAIAAyAC4AMgBQAGUAcgBmAGkAbAAgAGcAZQBuAOkAcgBpAGMAbwAgAGQAZQAgAGMAaQBu +AHoAZQBuAHQAbwBzACAAZABhACAARwBhAG0AbQBhACAAMgAsADIAQQBsAGcAZQBtAGUAZQBuACAAZwBy +AGkAagBzACAAZwBhAG0AbQBhACAAMgAsADIALQBwAHIAbwBmAGkAZQBsAFAAZQByAGYAaQBsACAAZwBl +AG4A6QByAGkAYwBvACAAZABlACAAZwBhAG0AbQBhACAAZABlACAAZwByAGkAcwBlAHMAIAAyACwAMg4j +DjEOBw4qDjUOQQ4BDiEOIQ4yDkAOAQ4jDiIOTA4XDjEOSA4nDkQOGwAgADIALgAyAEcAZQBuAGUAbAAg +AEcAcgBpACAARwBhAG0AYQAgADIALAAyAFkAbABlAGkAbgBlAG4AIABoAGEAcgBtAGEAYQBuACAAZwBh +AG0AbQBhACAAMgAsADIAIAAtAHAAcgBvAGYAaQBpAGwAaQBHAGUAbgBlAHIAaQENAGsAaQAgAEcAcgBh +AHkAIABHAGEAbQBtAGEAIAAyAC4AMgAgAHAAcgBvAGYAaQBsAFUAbgBpAHcAZQByAHMAYQBsAG4AeQAg +AHAAcgBvAGYAaQBsACAAcwB6AGEAcgBvAVsAYwBpACAAZwBhAG0AbQBhACAAMgAsADIGOgYnBkUGJwAg +ADIALgAyACAGRAZIBkYAIAYxBkUGJwYvBkoAIAY5BicGRQQeBDEESQQwBE8AIARBBDUEQAQwBE8AIAQz +BDAEPAQ8BDAAIAAyACwAMgAtBD8EQAQ+BEQEOAQ7BEwARwBlAG4AZQByAGkAYwAgAEcAcgBhAHkAIABH +AGEAbQBtAGEAIAAyAC4AMgAgAFAAcgBvAGYAaQBsAGUAAHRleHQAAAAAQ29weXJpZ2h0IEFwcGxlIElu +Yy4sIDIwMTIAAFhZWiAAAAAAAADzUQABAAAAARbMY3VydgAAAAAAAAQAAAAABQAKAA8AFAAZAB4AIwAo +AC0AMgA3ADsAQABFAEoATwBUAFkAXgBjAGgAbQByAHcAfACBAIYAiwCQAJUAmgCfAKQAqQCuALIAtwC8 +AMEAxgDLANAA1QDbAOAA5QDrAPAA9gD7AQEBBwENARMBGQEfASUBKwEyATgBPgFFAUwBUgFZAWABZwFu +AXUBfAGDAYsBkgGaAaEBqQGxAbkBwQHJAdEB2QHhAekB8gH6AgMCDAIUAh0CJgIvAjgCQQJLAlQCXQJn +AnECegKEAo4CmAKiAqwCtgLBAssC1QLgAusC9QMAAwsDFgMhAy0DOANDA08DWgNmA3IDfgOKA5YDogOu +A7oDxwPTA+AD7AP5BAYEEwQgBC0EOwRIBFUEYwRxBH4EjASaBKgEtgTEBNME4QTwBP4FDQUcBSsFOgVJ +BVgFZwV3BYYFlgWmBbUFxQXVBeUF9gYGBhYGJwY3BkgGWQZqBnsGjAadBq8GwAbRBuMG9QcHBxkHKwc9 +B08HYQd0B4YHmQesB78H0gflB/gICwgfCDIIRghaCG4IggiWCKoIvgjSCOcI+wkQCSUJOglPCWQJeQmP +CaQJugnPCeUJ+woRCicKPQpUCmoKgQqYCq4KxQrcCvMLCwsiCzkLUQtpC4ALmAuwC8gL4Qv5DBIMKgxD +DFwMdQyODKcMwAzZDPMNDQ0mDUANWg10DY4NqQ3DDd4N+A4TDi4OSQ5kDn8Omw62DtIO7g8JDyUPQQ9e +D3oPlg+zD88P7BAJECYQQxBhEH4QmxC5ENcQ9RETETERTxFtEYwRqhHJEegSBxImEkUSZBKEEqMSwxLj +EwMTIxNDE2MTgxOkE8UT5RQGFCcUSRRqFIsUrRTOFPAVEhU0FVYVeBWbFb0V4BYDFiYWSRZsFo8WshbW +FvoXHRdBF2UXiReuF9IX9xgbGEAYZRiKGK8Y1Rj6GSAZRRlrGZEZtxndGgQaKhpRGncanhrFGuwbFBs7 +G2MbihuyG9ocAhwqHFIcexyjHMwc9R0eHUcdcB2ZHcMd7B4WHkAeah6UHr4e6R8THz4faR+UH78f6iAV +IEEgbCCYIMQg8CEcIUghdSGhIc4h+yInIlUigiKvIt0jCiM4I2YjlCPCI/AkHyRNJHwkqyTaJQklOCVo +JZclxyX3JicmVyaHJrcm6CcYJ0kneierJ9woDSg/KHEooijUKQYpOClrKZ0p0CoCKjUqaCqbKs8rAis2 +K2krnSvRLAUsOSxuLKIs1y0MLUEtdi2rLeEuFi5MLoIuty7uLyQvWi+RL8cv/jA1MGwwpDDbMRIxSjGC +Mbox8jIqMmMymzLUMw0zRjN/M7gz8TQrNGU0njTYNRM1TTWHNcI1/TY3NnI2rjbpNyQ3YDecN9c4FDhQ +OIw4yDkFOUI5fzm8Ofk6Njp0OrI67zstO2s7qjvoPCc8ZTykPOM9Ij1hPaE94D4gPmA+oD7gPyE/YT+i +P+JAI0BkQKZA50EpQWpBrEHuQjBCckK1QvdDOkN9Q8BEA0RHRIpEzkUSRVVFmkXeRiJGZ0arRvBHNUd7 +R8BIBUhLSJFI10kdSWNJqUnwSjdKfUrESwxLU0uaS+JMKkxyTLpNAk1KTZNN3E4lTm5Ot08AT0lPk0/d +UCdQcVC7UQZRUFGbUeZSMVJ8UsdTE1NfU6pT9lRCVI9U21UoVXVVwlYPVlxWqVb3V0RXklfgWC9YfVjL +WRpZaVm4WgdaVlqmWvVbRVuVW+VcNVyGXNZdJ114XcleGl5sXr1fD19hX7NgBWBXYKpg/GFPYaJh9WJJ +Ypxi8GNDY5dj62RAZJRk6WU9ZZJl52Y9ZpJm6Gc9Z5Nn6Wg/aJZo7GlDaZpp8WpIap9q92tPa6dr/2xX +bK9tCG1gbbluEm5rbsRvHm94b9FwK3CGcOBxOnGVcfByS3KmcwFzXXO4dBR0cHTMdSh1hXXhdj52m3b4 +d1Z3s3gReG54zHkqeYl553pGeqV7BHtje8J8IXyBfOF9QX2hfgF+Yn7CfyN/hH/lgEeAqIEKgWuBzYIw +gpKC9INXg7qEHYSAhOOFR4Wrhg6GcobXhzuHn4gEiGmIzokziZmJ/opkisqLMIuWi/yMY4zKjTGNmI3/ +jmaOzo82j56QBpBukNaRP5GokhGSepLjk02TtpQglIqU9JVflcmWNJaflwqXdZfgmEyYuJkkmZCZ/Jpo +mtWbQpuvnByciZz3nWSd0p5Anq6fHZ+Ln/qgaaDYoUehtqImopajBqN2o+akVqTHpTilqaYapoum/adu +p+CoUqjEqTepqaocqo+rAqt1q+msXKzQrUStuK4trqGvFq+LsACwdbDqsWCx1rJLssKzOLOutCW0nLUT +tYq2AbZ5tvC3aLfguFm40blKucK6O7q1uy67p7whvJu9Fb2Pvgq+hL7/v3q/9cBwwOzBZ8Hjwl/C28NY +w9TEUcTOxUvFyMZGxsPHQce/yD3IvMk6ybnKOMq3yzbLtsw1zLXNNc21zjbOts83z7jQOdC60TzRvtI/ +0sHTRNPG1EnUy9VO1dHWVdbY11zX4Nhk2OjZbNnx2nba+9uA3AXcit0Q3ZbeHN6i3ynfr+A24L3hROHM +4lPi2+Nj4+vkc+T85YTmDeaW5x/nqegy6LzpRunQ6lvq5etw6/vshu0R7ZzuKO6070DvzPBY8OXxcvH/ +8ozzGfOn9DT0wvVQ9d72bfb794r4Gfio+Tj5x/pX+uf7d/wH/Jj9Kf26/kv+3P9t///SLS4vMFokY2xh +c3NuYW1lWCRjbGFzc2VzXxAQTlNCaXRtYXBJbWFnZVJlcKMvMTJaTlNJbWFnZVJlcFhOU09iamVjdNIt +LjQ1V05TQXJyYXmiNDLSLS43OF5OU011dGFibGVBcnJheaM3NDLTOjsOPD0+V05TV2hpdGVcTlNDb2xv +clNwYWNlRDAgMAAQA4AM0i0uQEFXTlNDb2xvcqJAMtItLkNEV05TSW1hZ2WiQzIACAARABoAJAApADIA +NwBJAEwAUQBTAGIAaAB3AH4AhQCSAJkApQCtALwAvgDAAMUAxwDJAMsAzQDUANkA5ADmAOgA6gDvAPIA +9AD2APgA/wEWATIBNAE2E6YTqxO2E78T0hPWE+ET6hPvE/cT+hP/FA4UEhQZFCEULhQzFDUUNxQ8FEQU +RxRMFFQAAAAAAAACAQAAAAAAAABFAAAAAAAAAAAAAAAAAAAUVw diff --git a/i18n/src/main/resources/cs.lproj/Browser.xib b/i18n/src/main/resources/cs.lproj/Browser.xib index 782a87e9d7..cc2b8a4043 100644 --- a/i18n/src/main/resources/cs.lproj/Browser.xib +++ b/i18n/src/main/resources/cs.lproj/Browser.xib @@ -1,7 +1,7 @@ - + - + @@ -38,7 +38,7 @@ - + @@ -62,27 +62,27 @@ @@ -256,7 +256,7 @@ - + @@ -553,95 +553,95 @@ DQ YnBsaXN0MDDUAQIDBAUGBwpYJHZlcnNpb25ZJGFyY2hpdmVyVCR0b3BYJG9iamVjdHMSAAGGoF8QD05T -S2V5ZWRBcmNoaXZlctEICVRyb290gAGuCwwZGh8UJCkqMTQ3PUBVJG51bGzWDQ4PEBESExQVFhcYVk5T -U2l6ZV5OU1Jlc2l6aW5nTW9kZVYkY2xhc3NcTlNJbWFnZUZsYWdzVk5TUmVwc1dOU0NvbG9ygAIQAIAN -EiDDAACAA4ALVnsxLCAxfdIbDxweWk5TLm9iamVjdHOhHYAEgArSGw8gI6IhIoAFgAaACdMPJSYnKBRf -EBROU1RJRkZSZXByZXNlbnRhdGlvbl8QGU5TSW50ZXJuYWxMYXlvdXREaXJlY3Rpb26ACIAHTxESbE1N -ACoAAAAKAAAAEAEAAAMAAAABAAEAAAEBAAMAAAABAAEAAAECAAMAAAACAAgACAEDAAMAAAABAAEAAAEG -AAMAAAABAAEAAAEKAAMAAAABAAEAAAERAAQAAAABAAAACAESAAMAAAABAAEAAAEVAAMAAAABAAIAAAEW -AAMAAAABAAEAAAEXAAQAAAABAAAAAgEcAAMAAAABAAEAAAEoAAMAAAABAAIAAAFSAAMAAAABAAEAAAFT -AAMAAAACAAEAAYdzAAcAABGcAAAA0AAAAAAAABGcYXBwbAIAAABtbnRyR1JBWVhZWiAH3AAIABcADwAu -AA9hY3NwQVBQTAAAAABub25lAAAAAAAAAAAAAAAAAAAAAAAA9tYAAQAAAADTLWFwcGwAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAVkZXNjAAAAwAAAAHlkc2NtAAABPAAA -CBpjcHJ0AAAJWAAAACN3dHB0AAAJfAAAABRrVFJDAAAJkAAACAxkZXNjAAAAAAAAAB9HZW5lcmljIEdy -YXkgR2FtbWEgMi4yIFByb2ZpbGUAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAbWx1YwAAAAAAAAAfAAAADHNr -U0sAAAAuAAABhGRhREsAAAA6AAABsmNhRVMAAAA4AAAB7HZpVk4AAABAAAACJHB0QlIAAABKAAACZHVr -VUEAAAAsAAACrmZyRlUAAAA+AAAC2mh1SFUAAAA0AAADGHpoVFcAAAAaAAADTGtvS1IAAAAiAAADZm5i -Tk8AAAA6AAADiGNzQ1oAAAAoAAADwmhlSUwAAAAkAAAD6nJvUk8AAAAqAAAEDmRlREUAAABOAAAEOGl0 -SVQAAABOAAAEhnN2U0UAAAA4AAAE1HpoQ04AAAAaAAAFDGphSlAAAAAmAAAFJmVsR1IAAAAqAAAFTHB0 -UE8AAABSAAAFdm5sTkwAAABAAAAFyGVzRVMAAABMAAAGCHRoVEgAAAAyAAAGVHRyVFIAAAAkAAAGhmZp -RkkAAABGAAAGqmhySFIAAAA+AAAG8HBsUEwAAABKAAAHLmFyRUcAAAAsAAAHeHJ1UlUAAAA6AAAHpGVu -VVMAAAA8AAAH3gBWAWEAZQBvAGIAZQBjAG4A4QAgAHMAaQB2AOEAIABnAGEAbQBhACAAMgAsADIARwBl -AG4AZQByAGkAcwBrACAAZwByAOUAIAAyACwAMgAgAGcAYQBtAG0AYQAtAHAAcgBvAGYAaQBsAEcAYQBt -AG0AYQAgAGQAZQAgAGcAcgBpAHMAbwBzACAAZwBlAG4A6AByAGkAYwBhACAAMgAuADIAQx6lAHUAIABo -AOwAbgBoACAATQDgAHUAIAB4AOEAbQAgAEMAaAB1AG4AZwAgAEcAYQBtAG0AYQAgADIALgAyAFAAZQBy -AGYAaQBsACAARwBlAG4A6QByAGkAYwBvACAAZABhACAARwBhAG0AYQAgAGQAZQAgAEMAaQBuAHoAYQBz -ACAAMgAsADIEFwQwBDMEMAQ7BEwEPQQwACAARwByAGEAeQAtBDMEMAQ8BDAAIAAyAC4AMgBQAHIAbwBm -AGkAbAAgAGcA6QBuAOkAcgBpAHEAdQBlACAAZwByAGkAcwAgAGcAYQBtAG0AYQAgADIALAAyAMEAbAB0 -AGEAbADhAG4AbwBzACAAcwB6APwAcgBrAGUAIABnAGEAbQBtAGEAIAAyAC4AMpAadShwcJaOUUlepgAy -AC4AMoJyX2ljz4/wx3y8GAAg1ozAyQAgrBC5yAAgADIALgAyACDVBLhc0wzHfABHAGUAbgBlAHIAaQBz -AGsAIABnAHIA5QAgAGcAYQBtAG0AYQAgADIALAAyAC0AcAByAG8AZgBpAGwATwBiAGUAYwBuAOEAIAFh -AGUAZADhACAAZwBhAG0AYQAgADIALgAyBdIF0AXeBdQAIAXQBeQF1QXoACAF2wXcBdwF2QAgADIALgAy -AEcAYQBtAGEAIABnAHIAaQAgAGcAZQBuAGUAcgBpAGMBAwAgADIALAAyAEEAbABsAGcAZQBtAGUAaQBu -AGUAcwAgAEcAcgBhAHUAcwB0AHUAZgBlAG4ALQBQAHIAbwBmAGkAbAAgAEcAYQBtAG0AYQAgADIALAAy -AFAAcgBvAGYAaQBsAG8AIABnAHIAaQBnAGkAbwAgAGcAZQBuAGUAcgBpAGMAbwAgAGQAZQBsAGwAYQAg -AGcAYQBtAG0AYQAgADIALAAyAEcAZQBuAGUAcgBpAHMAawAgAGcAcgDlACAAMgAsADIAIABnAGEAbQBt -AGEAcAByAG8AZgBpAGxmbpAacHBepnz7ZXAAMgAuADJjz4/wZYdO9k4AgiwwsDDsMKQwrDDzMN4AIAAy -AC4AMgAgMNcw7TDVMKEwpDDrA5MDtQO9A7kDugPMACADkwO6A8EDuQAgA5MDrAO8A7wDsQAgADIALgAy -AFAAZQByAGYAaQBsACAAZwBlAG4A6QByAGkAYwBvACAAZABlACAAYwBpAG4AegBlAG4AdABvAHMAIABk -AGEAIABHAGEAbQBtAGEAIAAyACwAMgBBAGwAZwBlAG0AZQBlAG4AIABnAHIAaQBqAHMAIABnAGEAbQBt -AGEAIAAyACwAMgAtAHAAcgBvAGYAaQBlAGwAUABlAHIAZgBpAGwAIABnAGUAbgDpAHIAaQBjAG8AIABk -AGUAIABnAGEAbQBtAGEAIABkAGUAIABnAHIAaQBzAGUAcwAgADIALAAyDiMOMQ4HDioONQ5BDgEOIQ4h -DjIOQA4BDiMOIg5MDhcOMQ5IDicORA4bACAAMgAuADIARwBlAG4AZQBsACAARwByAGkAIABHAGEAbQBh -ACAAMgAsADIAWQBsAGUAaQBuAGUAbgAgAGgAYQByAG0AYQBhAG4AIABnAGEAbQBtAGEAIAAyACwAMgAg -AC0AcAByAG8AZgBpAGkAbABpAEcAZQBuAGUAcgBpAQ0AawBpACAARwByAGEAeQAgAEcAYQBtAG0AYQAg -ADIALgAyACAAcAByAG8AZgBpAGwAVQBuAGkAdwBlAHIAcwBhAGwAbgB5ACAAcAByAG8AZgBpAGwAIABz -AHoAYQByAG8BWwBjAGkAIABnAGEAbQBtAGEAIAAyACwAMgY6BicGRQYnACAAMgAuADIAIAZEBkgGRgAg -BjEGRQYnBi8GSgAgBjkGJwZFBB4EMQRJBDAETwAgBEEENQRABDAETwAgBDMEMAQ8BDwEMAAgADIALAAy -AC0EPwRABD4ERAQ4BDsETABHAGUAbgBlAHIAaQBjACAARwByAGEAeQAgAEcAYQBtAG0AYQAgADIALgAy -ACAAUAByAG8AZgBpAGwAZQAAdGV4dAAAAABDb3B5cmlnaHQgQXBwbGUgSW5jLiwgMjAxMgAAWFlaIAAA -AAAAAPNRAAEAAAABFsxjdXJ2AAAAAAAABAAAAAAFAAoADwAUABkAHgAjACgALQAyADcAOwBAAEUASgBP -AFQAWQBeAGMAaABtAHIAdwB8AIEAhgCLAJAAlQCaAJ8ApACpAK4AsgC3ALwAwQDGAMsA0ADVANsA4ADl -AOsA8AD2APsBAQEHAQ0BEwEZAR8BJQErATIBOAE+AUUBTAFSAVkBYAFnAW4BdQF8AYMBiwGSAZoBoQGp -AbEBuQHBAckB0QHZAeEB6QHyAfoCAwIMAhQCHQImAi8COAJBAksCVAJdAmcCcQJ6AoQCjgKYAqICrAK2 -AsECywLVAuAC6wL1AwADCwMWAyEDLQM4A0MDTwNaA2YDcgN+A4oDlgOiA64DugPHA9MD4APsA/kEBgQT -BCAELQQ7BEgEVQRjBHEEfgSMBJoEqAS2BMQE0wThBPAE/gUNBRwFKwU6BUkFWAVnBXcFhgWWBaYFtQXF -BdUF5QX2BgYGFgYnBjcGSAZZBmoGewaMBp0GrwbABtEG4wb1BwcHGQcrBz0HTwdhB3QHhgeZB6wHvwfS -B+UH+AgLCB8IMghGCFoIbgiCCJYIqgi+CNII5wj7CRAJJQk6CU8JZAl5CY8JpAm6Cc8J5Qn7ChEKJwo9 -ClQKagqBCpgKrgrFCtwK8wsLCyILOQtRC2kLgAuYC7ALyAvhC/kMEgwqDEMMXAx1DI4MpwzADNkM8w0N -DSYNQA1aDXQNjg2pDcMN3g34DhMOLg5JDmQOfw6bDrYO0g7uDwkPJQ9BD14Peg+WD7MPzw/sEAkQJhBD -EGEQfhCbELkQ1xD1ERMRMRFPEW0RjBGqEckR6BIHEiYSRRJkEoQSoxLDEuMTAxMjE0MTYxODE6QTxRPl -FAYUJxRJFGoUixStFM4U8BUSFTQVVhV4FZsVvRXgFgMWJhZJFmwWjxayFtYW+hcdF0EXZReJF64X0hf3 -GBsYQBhlGIoYrxjVGPoZIBlFGWsZkRm3Gd0aBBoqGlEadxqeGsUa7BsUGzsbYxuKG7Ib2hwCHCocUhx7 -HKMczBz1HR4dRx1wHZkdwx3sHhYeQB5qHpQevh7pHxMfPh9pH5Qfvx/qIBUgQSBsIJggxCDwIRwhSCF1 -IaEhziH7IiciVSKCIq8i3SMKIzgjZiOUI8Ij8CQfJE0kfCSrJNolCSU4JWgllyXHJfcmJyZXJocmtybo -JxgnSSd6J6sn3CgNKD8ocSiiKNQpBik4KWspnSnQKgIqNSpoKpsqzysCKzYraSudK9EsBSw5LG4soizX -LQwtQS12Last4S4WLkwugi63Lu4vJC9aL5Evxy/+MDUwbDCkMNsxEjFKMYIxujHyMioyYzKbMtQzDTNG -M38zuDPxNCs0ZTSeNNg1EzVNNYc1wjX9Njc2cjauNuk3JDdgN5w31zgUOFA4jDjIOQU5Qjl/Obw5+To2 -OnQ6sjrvOy07azuqO+g8JzxlPKQ84z0iPWE9oT3gPiA+YD6gPuA/IT9hP6I/4kAjQGRApkDnQSlBakGs -Qe5CMEJyQrVC90M6Q31DwEQDREdEikTORRJFVUWaRd5GIkZnRqtG8Ec1R3tHwEgFSEtIkUjXSR1JY0mp -SfBKN0p9SsRLDEtTS5pL4kwqTHJMuk0CTUpNk03cTiVObk63TwBPSU+TT91QJ1BxULtRBlFQUZtR5lIx -UnxSx1MTU19TqlP2VEJUj1TbVShVdVXCVg9WXFapVvdXRFeSV+BYL1h9WMtZGllpWbhaB1pWWqZa9VtF -W5Vb5Vw1XIZc1l0nXXhdyV4aXmxevV8PX2Ffs2AFYFdgqmD8YU9homH1YklinGLwY0Njl2PrZEBklGTp -ZT1lkmXnZj1mkmboZz1nk2fpaD9olmjsaUNpmmnxakhqn2r3a09rp2v/bFdsr20IbWBtuW4SbmtuxG8e -b3hv0XArcIZw4HE6cZVx8HJLcqZzAXNdc7h0FHRwdMx1KHWFdeF2Pnabdvh3VnezeBF4bnjMeSp5iXnn -ekZ6pXsEe2N7wnwhfIF84X1BfaF+AX5ifsJ/I3+Ef+WAR4CogQqBa4HNgjCCkoL0g1eDuoQdhICE44VH -hauGDoZyhteHO4efiASIaYjOiTOJmYn+imSKyoswi5aL/IxjjMqNMY2Yjf+OZo7OjzaPnpAGkG6Q1pE/ -kaiSEZJ6kuOTTZO2lCCUipT0lV+VyZY0lp+XCpd1l+CYTJi4mSSZkJn8mmia1ZtCm6+cHJyJnPedZJ3S -nkCerp8dn4uf+qBpoNihR6G2oiailqMGo3aj5qRWpMelOKWpphqmi6b9p26n4KhSqMSpN6mpqhyqj6sC -q3Wr6axcrNCtRK24ri2uoa8Wr4uwALB1sOqxYLHWskuywrM4s660JbSctRO1irYBtnm28Ldot+C4WbjR -uUq5wro7urW7LrunvCG8m70VvY++Cr6Evv+/er/1wHDA7MFnwePCX8Lbw1jD1MRRxM7FS8XIxkbGw8dB -x7/IPci8yTrJuco4yrfLNsu2zDXMtc01zbXONs62zzfPuNA50LrRPNG+0j/SwdNE08bUSdTL1U7V0dZV -1tjXXNfg2GTY6Nls2fHadtr724DcBdyK3RDdlt4c3qLfKd+v4DbgveFE4cziU+Lb42Pj6+Rz5PzlhOYN -5pbnH+ep6DLovOlG6dDqW+rl63Dr++yG7RHtnO4o7rTvQO/M8Fjw5fFy8f/yjPMZ86f0NPTC9VD13vZt -9vv3ivgZ+Kj5OPnH+lf65/t3/Af8mP0p/br+S/7c/23//9IrLC0uWiRjbGFzc25hbWVYJGNsYXNzZXNf -EBBOU0JpdG1hcEltYWdlUmVwoy0vMFpOU0ltYWdlUmVwWE5TT2JqZWN00issMjNXTlNBcnJheaIyMNIr -LDU2Xk5TTXV0YWJsZUFycmF5ozUyMNM4OQ86OzxXTlNXaGl0ZVxOU0NvbG9yU3BhY2VEMCAwABADgAzS -Kyw+P1dOU0NvbG9yoj4w0issQUJXTlNJbWFnZaJBMAAIABEAGgAkACkAMgA3AEkATABRAFMAYgBoAHUA -fACLAJIAnwCmAK4AsACyALQAuQC7AL0AxADJANQA1gDYANoA3wDiAOQA5gDoAO8BBgEiASQBJhOWE5sT -phOvE8ITxhPRE9oT3xPnE+oT7xP+FAIUCRQRFB4UIxQlFCcULBQ0FDcUPBREAAAAAAAAAgEAAAAAAAAA -QwAAAAAAAAAAAAAAAAAAFEc +S2V5ZWRBcmNoaXZlctEICVRyb290gAGuCwwbHCEaJissMzY5P0JVJG51bGzXDQ4PEBESExQVFhcYGRpW +TlNTaXplViRjbGFzc1xOU0ltYWdlRmxhZ3NWTlNSZXBzW05TVGludENvbG9yV05TQ29sb3JeTlNSZXNp +emluZ01vZGWAAoANEiDDAACAA4AAgAsQAFZ7MSwgMX3SHQ4eIFpOUy5vYmplY3RzoR+ABIAK0h0OIiWi +IySABYAGgAnTDicoKSoaXxAUTlNUSUZGUmVwcmVzZW50YXRpb25fEBlOU0ludGVybmFsTGF5b3V0RGly +ZWN0aW9ugAiAB08REmxNTQAqAAAACgAAABABAAADAAAAAQABAAABAQADAAAAAQABAAABAgADAAAAAgAI +AAgBAwADAAAAAQABAAABBgADAAAAAQABAAABCgADAAAAAQABAAABEQAEAAAAAQAAAAgBEgADAAAAAQAB +AAABFQADAAAAAQACAAABFgADAAAAAQABAAABFwAEAAAAAQAAAAIBHAADAAAAAQABAAABKAADAAAAAQAC +AAABUgADAAAAAQABAAABUwADAAAAAgABAAGHcwAHAAARnAAAANAAAAAAAAARnGFwcGwCAAAAbW50ckdS +QVlYWVogB9wACAAXAA8ALgAPYWNzcEFQUEwAAAAAbm9uZQAAAAAAAAAAAAAAAAAAAAAAAPbWAAEAAAAA +0y1hcHBsAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFZGVzYwAA +AMAAAAB5ZHNjbQAAATwAAAgaY3BydAAACVgAAAAjd3RwdAAACXwAAAAUa1RSQwAACZAAAAgMZGVzYwAA +AAAAAAAfR2VuZXJpYyBHcmF5IEdhbW1hIDIuMiBQcm9maWxlAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAG1s +dWMAAAAAAAAAHwAAAAxza1NLAAAALgAAAYRkYURLAAAAOgAAAbJjYUVTAAAAOAAAAex2aVZOAAAAQAAA +AiRwdEJSAAAASgAAAmR1a1VBAAAALAAAAq5mckZVAAAAPgAAAtpodUhVAAAANAAAAxh6aFRXAAAAGgAA +A0xrb0tSAAAAIgAAA2ZuYk5PAAAAOgAAA4hjc0NaAAAAKAAAA8JoZUlMAAAAJAAAA+pyb1JPAAAAKgAA +BA5kZURFAAAATgAABDhpdElUAAAATgAABIZzdlNFAAAAOAAABNR6aENOAAAAGgAABQxqYUpQAAAAJgAA +BSZlbEdSAAAAKgAABUxwdFBPAAAAUgAABXZubE5MAAAAQAAABchlc0VTAAAATAAABgh0aFRIAAAAMgAA +BlR0clRSAAAAJAAABoZmaUZJAAAARgAABqpockhSAAAAPgAABvBwbFBMAAAASgAABy5hckVHAAAALAAA +B3hydVJVAAAAOgAAB6RlblVTAAAAPAAAB94AVgFhAGUAbwBiAGUAYwBuAOEAIABzAGkAdgDhACAAZwBh +AG0AYQAgADIALAAyAEcAZQBuAGUAcgBpAHMAawAgAGcAcgDlACAAMgAsADIAIABnAGEAbQBtAGEALQBw +AHIAbwBmAGkAbABHAGEAbQBtAGEAIABkAGUAIABnAHIAaQBzAG8AcwAgAGcAZQBuAOgAcgBpAGMAYQAg +ADIALgAyAEMepQB1ACAAaADsAG4AaAAgAE0A4AB1ACAAeADhAG0AIABDAGgAdQBuAGcAIABHAGEAbQBt +AGEAIAAyAC4AMgBQAGUAcgBmAGkAbAAgAEcAZQBuAOkAcgBpAGMAbwAgAGQAYQAgAEcAYQBtAGEAIABk +AGUAIABDAGkAbgB6AGEAcwAgADIALAAyBBcEMAQzBDAEOwRMBD0EMAAgAEcAcgBhAHkALQQzBDAEPAQw +ACAAMgAuADIAUAByAG8AZgBpAGwAIABnAOkAbgDpAHIAaQBxAHUAZQAgAGcAcgBpAHMAIABnAGEAbQBt +AGEAIAAyACwAMgDBAGwAdABhAGwA4QBuAG8AcwAgAHMAegD8AHIAawBlACAAZwBhAG0AbQBhACAAMgAu +ADKQGnUocHCWjlFJXqYAMgAuADKCcl9pY8+P8Md8vBgAINaMwMkAIKwQucgAIAAyAC4AMgAg1QS4XNMM +x3wARwBlAG4AZQByAGkAcwBrACAAZwByAOUAIABnAGEAbQBtAGEAIAAyACwAMgAtAHAAcgBvAGYAaQBs +AE8AYgBlAGMAbgDhACABYQBlAGQA4QAgAGcAYQBtAGEAIAAyAC4AMgXSBdAF3gXUACAF0AXkBdUF6AAg +BdsF3AXcBdkAIAAyAC4AMgBHAGEAbQBhACAAZwByAGkAIABnAGUAbgBlAHIAaQBjAQMAIAAyACwAMgBB +AGwAbABnAGUAbQBlAGkAbgBlAHMAIABHAHIAYQB1AHMAdAB1AGYAZQBuAC0AUAByAG8AZgBpAGwAIABH +AGEAbQBtAGEAIAAyACwAMgBQAHIAbwBmAGkAbABvACAAZwByAGkAZwBpAG8AIABnAGUAbgBlAHIAaQBj +AG8AIABkAGUAbABsAGEAIABnAGEAbQBtAGEAIAAyACwAMgBHAGUAbgBlAHIAaQBzAGsAIABnAHIA5QAg +ADIALAAyACAAZwBhAG0AbQBhAHAAcgBvAGYAaQBsZm6QGnBwXqZ8+2VwADIALgAyY8+P8GWHTvZOAIIs +MLAw7DCkMKww8zDeACAAMgAuADIAIDDXMO0w1TChMKQw6wOTA7UDvQO5A7oDzAAgA5MDugPBA7kAIAOT +A6wDvAO8A7EAIAAyAC4AMgBQAGUAcgBmAGkAbAAgAGcAZQBuAOkAcgBpAGMAbwAgAGQAZQAgAGMAaQBu +AHoAZQBuAHQAbwBzACAAZABhACAARwBhAG0AbQBhACAAMgAsADIAQQBsAGcAZQBtAGUAZQBuACAAZwBy +AGkAagBzACAAZwBhAG0AbQBhACAAMgAsADIALQBwAHIAbwBmAGkAZQBsAFAAZQByAGYAaQBsACAAZwBl +AG4A6QByAGkAYwBvACAAZABlACAAZwBhAG0AbQBhACAAZABlACAAZwByAGkAcwBlAHMAIAAyACwAMg4j +DjEOBw4qDjUOQQ4BDiEOIQ4yDkAOAQ4jDiIOTA4XDjEOSA4nDkQOGwAgADIALgAyAEcAZQBuAGUAbAAg +AEcAcgBpACAARwBhAG0AYQAgADIALAAyAFkAbABlAGkAbgBlAG4AIABoAGEAcgBtAGEAYQBuACAAZwBh +AG0AbQBhACAAMgAsADIAIAAtAHAAcgBvAGYAaQBpAGwAaQBHAGUAbgBlAHIAaQENAGsAaQAgAEcAcgBh +AHkAIABHAGEAbQBtAGEAIAAyAC4AMgAgAHAAcgBvAGYAaQBsAFUAbgBpAHcAZQByAHMAYQBsAG4AeQAg +AHAAcgBvAGYAaQBsACAAcwB6AGEAcgBvAVsAYwBpACAAZwBhAG0AbQBhACAAMgAsADIGOgYnBkUGJwAg +ADIALgAyACAGRAZIBkYAIAYxBkUGJwYvBkoAIAY5BicGRQQeBDEESQQwBE8AIARBBDUEQAQwBE8AIAQz +BDAEPAQ8BDAAIAAyACwAMgAtBD8EQAQ+BEQEOAQ7BEwARwBlAG4AZQByAGkAYwAgAEcAcgBhAHkAIABH +AGEAbQBtAGEAIAAyAC4AMgAgAFAAcgBvAGYAaQBsAGUAAHRleHQAAAAAQ29weXJpZ2h0IEFwcGxlIElu +Yy4sIDIwMTIAAFhZWiAAAAAAAADzUQABAAAAARbMY3VydgAAAAAAAAQAAAAABQAKAA8AFAAZAB4AIwAo +AC0AMgA3ADsAQABFAEoATwBUAFkAXgBjAGgAbQByAHcAfACBAIYAiwCQAJUAmgCfAKQAqQCuALIAtwC8 +AMEAxgDLANAA1QDbAOAA5QDrAPAA9gD7AQEBBwENARMBGQEfASUBKwEyATgBPgFFAUwBUgFZAWABZwFu +AXUBfAGDAYsBkgGaAaEBqQGxAbkBwQHJAdEB2QHhAekB8gH6AgMCDAIUAh0CJgIvAjgCQQJLAlQCXQJn +AnECegKEAo4CmAKiAqwCtgLBAssC1QLgAusC9QMAAwsDFgMhAy0DOANDA08DWgNmA3IDfgOKA5YDogOu +A7oDxwPTA+AD7AP5BAYEEwQgBC0EOwRIBFUEYwRxBH4EjASaBKgEtgTEBNME4QTwBP4FDQUcBSsFOgVJ +BVgFZwV3BYYFlgWmBbUFxQXVBeUF9gYGBhYGJwY3BkgGWQZqBnsGjAadBq8GwAbRBuMG9QcHBxkHKwc9 +B08HYQd0B4YHmQesB78H0gflB/gICwgfCDIIRghaCG4IggiWCKoIvgjSCOcI+wkQCSUJOglPCWQJeQmP +CaQJugnPCeUJ+woRCicKPQpUCmoKgQqYCq4KxQrcCvMLCwsiCzkLUQtpC4ALmAuwC8gL4Qv5DBIMKgxD +DFwMdQyODKcMwAzZDPMNDQ0mDUANWg10DY4NqQ3DDd4N+A4TDi4OSQ5kDn8Omw62DtIO7g8JDyUPQQ9e +D3oPlg+zD88P7BAJECYQQxBhEH4QmxC5ENcQ9RETETERTxFtEYwRqhHJEegSBxImEkUSZBKEEqMSwxLj +EwMTIxNDE2MTgxOkE8UT5RQGFCcUSRRqFIsUrRTOFPAVEhU0FVYVeBWbFb0V4BYDFiYWSRZsFo8WshbW +FvoXHRdBF2UXiReuF9IX9xgbGEAYZRiKGK8Y1Rj6GSAZRRlrGZEZtxndGgQaKhpRGncanhrFGuwbFBs7 +G2MbihuyG9ocAhwqHFIcexyjHMwc9R0eHUcdcB2ZHcMd7B4WHkAeah6UHr4e6R8THz4faR+UH78f6iAV +IEEgbCCYIMQg8CEcIUghdSGhIc4h+yInIlUigiKvIt0jCiM4I2YjlCPCI/AkHyRNJHwkqyTaJQklOCVo +JZclxyX3JicmVyaHJrcm6CcYJ0kneierJ9woDSg/KHEooijUKQYpOClrKZ0p0CoCKjUqaCqbKs8rAis2 +K2krnSvRLAUsOSxuLKIs1y0MLUEtdi2rLeEuFi5MLoIuty7uLyQvWi+RL8cv/jA1MGwwpDDbMRIxSjGC +Mbox8jIqMmMymzLUMw0zRjN/M7gz8TQrNGU0njTYNRM1TTWHNcI1/TY3NnI2rjbpNyQ3YDecN9c4FDhQ +OIw4yDkFOUI5fzm8Ofk6Njp0OrI67zstO2s7qjvoPCc8ZTykPOM9Ij1hPaE94D4gPmA+oD7gPyE/YT+i +P+JAI0BkQKZA50EpQWpBrEHuQjBCckK1QvdDOkN9Q8BEA0RHRIpEzkUSRVVFmkXeRiJGZ0arRvBHNUd7 +R8BIBUhLSJFI10kdSWNJqUnwSjdKfUrESwxLU0uaS+JMKkxyTLpNAk1KTZNN3E4lTm5Ot08AT0lPk0/d +UCdQcVC7UQZRUFGbUeZSMVJ8UsdTE1NfU6pT9lRCVI9U21UoVXVVwlYPVlxWqVb3V0RXklfgWC9YfVjL +WRpZaVm4WgdaVlqmWvVbRVuVW+VcNVyGXNZdJ114XcleGl5sXr1fD19hX7NgBWBXYKpg/GFPYaJh9WJJ +Ypxi8GNDY5dj62RAZJRk6WU9ZZJl52Y9ZpJm6Gc9Z5Nn6Wg/aJZo7GlDaZpp8WpIap9q92tPa6dr/2xX +bK9tCG1gbbluEm5rbsRvHm94b9FwK3CGcOBxOnGVcfByS3KmcwFzXXO4dBR0cHTMdSh1hXXhdj52m3b4 +d1Z3s3gReG54zHkqeYl553pGeqV7BHtje8J8IXyBfOF9QX2hfgF+Yn7CfyN/hH/lgEeAqIEKgWuBzYIw +gpKC9INXg7qEHYSAhOOFR4Wrhg6GcobXhzuHn4gEiGmIzokziZmJ/opkisqLMIuWi/yMY4zKjTGNmI3/ +jmaOzo82j56QBpBukNaRP5GokhGSepLjk02TtpQglIqU9JVflcmWNJaflwqXdZfgmEyYuJkkmZCZ/Jpo +mtWbQpuvnByciZz3nWSd0p5Anq6fHZ+Ln/qgaaDYoUehtqImopajBqN2o+akVqTHpTilqaYapoum/adu +p+CoUqjEqTepqaocqo+rAqt1q+msXKzQrUStuK4trqGvFq+LsACwdbDqsWCx1rJLssKzOLOutCW0nLUT +tYq2AbZ5tvC3aLfguFm40blKucK6O7q1uy67p7whvJu9Fb2Pvgq+hL7/v3q/9cBwwOzBZ8Hjwl/C28NY +w9TEUcTOxUvFyMZGxsPHQce/yD3IvMk6ybnKOMq3yzbLtsw1zLXNNc21zjbOts83z7jQOdC60TzRvtI/ +0sHTRNPG1EnUy9VO1dHWVdbY11zX4Nhk2OjZbNnx2nba+9uA3AXcit0Q3ZbeHN6i3ynfr+A24L3hROHM +4lPi2+Nj4+vkc+T85YTmDeaW5x/nqegy6LzpRunQ6lvq5etw6/vshu0R7ZzuKO6070DvzPBY8OXxcvH/ +8ozzGfOn9DT0wvVQ9d72bfb794r4Gfio+Tj5x/pX+uf7d/wH/Jj9Kf26/kv+3P9t///SLS4vMFokY2xh +c3NuYW1lWCRjbGFzc2VzXxAQTlNCaXRtYXBJbWFnZVJlcKMvMTJaTlNJbWFnZVJlcFhOU09iamVjdNIt +LjQ1V05TQXJyYXmiNDLSLS43OF5OU011dGFibGVBcnJheaM3NDLTOjsOPD0+V05TV2hpdGVcTlNDb2xv +clNwYWNlRDAgMAAQA4AM0i0uQEFXTlNDb2xvcqJAMtItLkNEV05TSW1hZ2WiQzIACAARABoAJAApADIA +NwBJAEwAUQBTAGIAaAB3AH4AhQCSAJkApQCtALwAvgDAAMUAxwDJAMsAzQDUANkA5ADmAOgA6gDvAPIA +9AD2APgA/wEWATIBNAE2E6YTqxO2E78T0hPWE+ET6hPvE/cT+hP/FA4UEhQZFCEULhQzFDUUNxQ8FEQU +RxRMFFQAAAAAAAACAQAAAAAAAABFAAAAAAAAAAAAAAAAAAAUVw diff --git a/i18n/src/main/resources/cs.lproj/Command.xib b/i18n/src/main/resources/cs.lproj/Command.xib index 0b006b2618..493d00efd6 100644 --- a/i18n/src/main/resources/cs.lproj/Command.xib +++ b/i18n/src/main/resources/cs.lproj/Command.xib @@ -1,7 +1,7 @@ - + - + diff --git a/i18n/src/main/resources/cs.lproj/Connection.xib b/i18n/src/main/resources/cs.lproj/Connection.xib index 3e9f7acccb..394a88dbfc 100644 --- a/i18n/src/main/resources/cs.lproj/Connection.xib +++ b/i18n/src/main/resources/cs.lproj/Connection.xib @@ -1,7 +1,7 @@ - + - + @@ -76,7 +76,7 @@ - + diff --git a/i18n/src/main/resources/cs.lproj/Info.xib b/i18n/src/main/resources/cs.lproj/Info.xib index 8e052acf0e..0e3152434c 100644 --- a/i18n/src/main/resources/cs.lproj/Info.xib +++ b/i18n/src/main/resources/cs.lproj/Info.xib @@ -1,7 +1,7 @@ - + - + @@ -89,7 +89,7 @@ - + @@ -1017,21 +1017,21 @@ - + - + - + @@ -1121,21 +1121,21 @@ - + - + - + @@ -1250,27 +1250,27 @@ diff --git a/i18n/src/main/resources/cs.lproj/Login.strings b/i18n/src/main/resources/cs.lproj/Login.strings index edfbdde235..35dee43227 100644 Binary files a/i18n/src/main/resources/cs.lproj/Login.strings and b/i18n/src/main/resources/cs.lproj/Login.strings differ diff --git a/i18n/src/main/resources/cs.lproj/Main.xib b/i18n/src/main/resources/cs.lproj/Main.xib index 36503ae17d..7fa023ec98 100644 --- a/i18n/src/main/resources/cs.lproj/Main.xib +++ b/i18n/src/main/resources/cs.lproj/Main.xib @@ -1,7 +1,7 @@ - + - + @@ -94,7 +94,7 @@ - + @@ -159,19 +159,19 @@ - + - + - + @@ -321,12 +321,12 @@ CA - + - + @@ -566,6 +566,10 @@ CA - + + + + + diff --git a/i18n/src/main/resources/cs.lproj/Preferences.xib b/i18n/src/main/resources/cs.lproj/Preferences.xib index 426f1e3b06..4ef0e49f5c 100644 --- a/i18n/src/main/resources/cs.lproj/Preferences.xib +++ b/i18n/src/main/resources/cs.lproj/Preferences.xib @@ -1,7 +1,7 @@ - + - + @@ -266,7 +266,7 @@ - + @@ -307,7 +307,7 @@ - + @@ -538,7 +538,7 @@ - + @@ -578,7 +578,7 @@ - + @@ -613,7 +613,7 @@ - + @@ -642,7 +642,7 @@ - + @@ -1218,7 +1218,7 @@ - + @@ -1250,7 +1250,7 @@ - + @@ -1290,7 +1290,7 @@ - + @@ -1363,7 +1363,7 @@ - + @@ -1558,7 +1558,7 @@ - + @@ -1618,7 +1618,7 @@ - + @@ -1643,7 +1643,7 @@ - + @@ -1685,7 +1685,7 @@ - + @@ -1713,7 +1713,7 @@ - + @@ -1741,7 +1741,7 @@ - + @@ -1769,7 +1769,7 @@ - + @@ -1804,7 +1804,7 @@ - + @@ -1887,7 +1887,7 @@ - + @@ -1946,7 +1946,7 @@ - + @@ -1974,7 +1974,7 @@ - + @@ -2002,7 +2002,7 @@ - + diff --git a/i18n/src/main/resources/cs.lproj/Profiles.xib b/i18n/src/main/resources/cs.lproj/Profiles.xib index 9312daab81..938e421287 100644 --- a/i18n/src/main/resources/cs.lproj/Profiles.xib +++ b/i18n/src/main/resources/cs.lproj/Profiles.xib @@ -1,7 +1,7 @@ - + - + diff --git a/i18n/src/main/resources/cs.lproj/Prompt.xib b/i18n/src/main/resources/cs.lproj/Prompt.xib index 25f4b23e2f..e6f2e6a75d 100644 --- a/i18n/src/main/resources/cs.lproj/Prompt.xib +++ b/i18n/src/main/resources/cs.lproj/Prompt.xib @@ -1,7 +1,7 @@ - + - + @@ -95,7 +95,7 @@ Gw - + diff --git a/i18n/src/main/resources/cs.lproj/S3.strings b/i18n/src/main/resources/cs.lproj/S3.strings index 18c2d59e62..f0f02bb762 100644 Binary files a/i18n/src/main/resources/cs.lproj/S3.strings and b/i18n/src/main/resources/cs.lproj/S3.strings differ diff --git a/i18n/src/main/resources/cs.lproj/Updater.strings b/i18n/src/main/resources/cs.lproj/Updater.strings index 2730936fa5..35dbb2cbb5 100644 Binary files a/i18n/src/main/resources/cs.lproj/Updater.strings and b/i18n/src/main/resources/cs.lproj/Updater.strings differ diff --git a/i18n/src/main/resources/cy.lproj/Bookmark.xib b/i18n/src/main/resources/cy.lproj/Bookmark.xib index 1dd9507ed5..b53b3957ac 100644 --- a/i18n/src/main/resources/cy.lproj/Bookmark.xib +++ b/i18n/src/main/resources/cy.lproj/Bookmark.xib @@ -1,7 +1,7 @@ - + - + @@ -92,7 +92,7 @@ - + @@ -489,95 +489,95 @@ YnBsaXN0MDDUAQIDBAUGBwpYJHZlcnNpb25ZJGFyY2hpdmVyVCR0b3BYJG9iamVjdHMSAAGGoF8QD05T -S2V5ZWRBcmNoaXZlctEICVRyb290gAGuCwwZGh8UJCkqMTQ3PUBVJG51bGzWDQ4PEBESExQVFhcYVk5T -U2l6ZV5OU1Jlc2l6aW5nTW9kZVYkY2xhc3NcTlNJbWFnZUZsYWdzVk5TUmVwc1dOU0NvbG9ygAIQAIAN -EiDDAACAA4ALVnsxLCAxfdIbDxweWk5TLm9iamVjdHOhHYAEgArSGw8gI6IhIoAFgAaACdMPJSYnKBRf -EBROU1RJRkZSZXByZXNlbnRhdGlvbl8QGU5TSW50ZXJuYWxMYXlvdXREaXJlY3Rpb26ACIAHTxESbE1N -ACoAAAAKAAAAEAEAAAMAAAABAAEAAAEBAAMAAAABAAEAAAECAAMAAAACAAgACAEDAAMAAAABAAEAAAEG -AAMAAAABAAEAAAEKAAMAAAABAAEAAAERAAQAAAABAAAACAESAAMAAAABAAEAAAEVAAMAAAABAAIAAAEW -AAMAAAABAAEAAAEXAAQAAAABAAAAAgEcAAMAAAABAAEAAAEoAAMAAAABAAIAAAFSAAMAAAABAAEAAAFT -AAMAAAACAAEAAYdzAAcAABGcAAAA0AAAAAAAABGcYXBwbAIAAABtbnRyR1JBWVhZWiAH3AAIABcADwAu -AA9hY3NwQVBQTAAAAABub25lAAAAAAAAAAAAAAAAAAAAAAAA9tYAAQAAAADTLWFwcGwAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAVkZXNjAAAAwAAAAHlkc2NtAAABPAAA -CBpjcHJ0AAAJWAAAACN3dHB0AAAJfAAAABRrVFJDAAAJkAAACAxkZXNjAAAAAAAAAB9HZW5lcmljIEdy -YXkgR2FtbWEgMi4yIFByb2ZpbGUAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAbWx1YwAAAAAAAAAfAAAADHNr -U0sAAAAuAAABhGRhREsAAAA6AAABsmNhRVMAAAA4AAAB7HZpVk4AAABAAAACJHB0QlIAAABKAAACZHVr -VUEAAAAsAAACrmZyRlUAAAA+AAAC2mh1SFUAAAA0AAADGHpoVFcAAAAaAAADTGtvS1IAAAAiAAADZm5i -Tk8AAAA6AAADiGNzQ1oAAAAoAAADwmhlSUwAAAAkAAAD6nJvUk8AAAAqAAAEDmRlREUAAABOAAAEOGl0 -SVQAAABOAAAEhnN2U0UAAAA4AAAE1HpoQ04AAAAaAAAFDGphSlAAAAAmAAAFJmVsR1IAAAAqAAAFTHB0 -UE8AAABSAAAFdm5sTkwAAABAAAAFyGVzRVMAAABMAAAGCHRoVEgAAAAyAAAGVHRyVFIAAAAkAAAGhmZp -RkkAAABGAAAGqmhySFIAAAA+AAAG8HBsUEwAAABKAAAHLmFyRUcAAAAsAAAHeHJ1UlUAAAA6AAAHpGVu -VVMAAAA8AAAH3gBWAWEAZQBvAGIAZQBjAG4A4QAgAHMAaQB2AOEAIABnAGEAbQBhACAAMgAsADIARwBl -AG4AZQByAGkAcwBrACAAZwByAOUAIAAyACwAMgAgAGcAYQBtAG0AYQAtAHAAcgBvAGYAaQBsAEcAYQBt -AG0AYQAgAGQAZQAgAGcAcgBpAHMAbwBzACAAZwBlAG4A6AByAGkAYwBhACAAMgAuADIAQx6lAHUAIABo -AOwAbgBoACAATQDgAHUAIAB4AOEAbQAgAEMAaAB1AG4AZwAgAEcAYQBtAG0AYQAgADIALgAyAFAAZQBy -AGYAaQBsACAARwBlAG4A6QByAGkAYwBvACAAZABhACAARwBhAG0AYQAgAGQAZQAgAEMAaQBuAHoAYQBz -ACAAMgAsADIEFwQwBDMEMAQ7BEwEPQQwACAARwByAGEAeQAtBDMEMAQ8BDAAIAAyAC4AMgBQAHIAbwBm -AGkAbAAgAGcA6QBuAOkAcgBpAHEAdQBlACAAZwByAGkAcwAgAGcAYQBtAG0AYQAgADIALAAyAMEAbAB0 -AGEAbADhAG4AbwBzACAAcwB6APwAcgBrAGUAIABnAGEAbQBtAGEAIAAyAC4AMpAadShwcJaOUUlepgAy -AC4AMoJyX2ljz4/wx3y8GAAg1ozAyQAgrBC5yAAgADIALgAyACDVBLhc0wzHfABHAGUAbgBlAHIAaQBz -AGsAIABnAHIA5QAgAGcAYQBtAG0AYQAgADIALAAyAC0AcAByAG8AZgBpAGwATwBiAGUAYwBuAOEAIAFh -AGUAZADhACAAZwBhAG0AYQAgADIALgAyBdIF0AXeBdQAIAXQBeQF1QXoACAF2wXcBdwF2QAgADIALgAy -AEcAYQBtAGEAIABnAHIAaQAgAGcAZQBuAGUAcgBpAGMBAwAgADIALAAyAEEAbABsAGcAZQBtAGUAaQBu -AGUAcwAgAEcAcgBhAHUAcwB0AHUAZgBlAG4ALQBQAHIAbwBmAGkAbAAgAEcAYQBtAG0AYQAgADIALAAy -AFAAcgBvAGYAaQBsAG8AIABnAHIAaQBnAGkAbwAgAGcAZQBuAGUAcgBpAGMAbwAgAGQAZQBsAGwAYQAg -AGcAYQBtAG0AYQAgADIALAAyAEcAZQBuAGUAcgBpAHMAawAgAGcAcgDlACAAMgAsADIAIABnAGEAbQBt -AGEAcAByAG8AZgBpAGxmbpAacHBepnz7ZXAAMgAuADJjz4/wZYdO9k4AgiwwsDDsMKQwrDDzMN4AIAAy -AC4AMgAgMNcw7TDVMKEwpDDrA5MDtQO9A7kDugPMACADkwO6A8EDuQAgA5MDrAO8A7wDsQAgADIALgAy -AFAAZQByAGYAaQBsACAAZwBlAG4A6QByAGkAYwBvACAAZABlACAAYwBpAG4AegBlAG4AdABvAHMAIABk -AGEAIABHAGEAbQBtAGEAIAAyACwAMgBBAGwAZwBlAG0AZQBlAG4AIABnAHIAaQBqAHMAIABnAGEAbQBt -AGEAIAAyACwAMgAtAHAAcgBvAGYAaQBlAGwAUABlAHIAZgBpAGwAIABnAGUAbgDpAHIAaQBjAG8AIABk -AGUAIABnAGEAbQBtAGEAIABkAGUAIABnAHIAaQBzAGUAcwAgADIALAAyDiMOMQ4HDioONQ5BDgEOIQ4h -DjIOQA4BDiMOIg5MDhcOMQ5IDicORA4bACAAMgAuADIARwBlAG4AZQBsACAARwByAGkAIABHAGEAbQBh -ACAAMgAsADIAWQBsAGUAaQBuAGUAbgAgAGgAYQByAG0AYQBhAG4AIABnAGEAbQBtAGEAIAAyACwAMgAg -AC0AcAByAG8AZgBpAGkAbABpAEcAZQBuAGUAcgBpAQ0AawBpACAARwByAGEAeQAgAEcAYQBtAG0AYQAg -ADIALgAyACAAcAByAG8AZgBpAGwAVQBuAGkAdwBlAHIAcwBhAGwAbgB5ACAAcAByAG8AZgBpAGwAIABz -AHoAYQByAG8BWwBjAGkAIABnAGEAbQBtAGEAIAAyACwAMgY6BicGRQYnACAAMgAuADIAIAZEBkgGRgAg -BjEGRQYnBi8GSgAgBjkGJwZFBB4EMQRJBDAETwAgBEEENQRABDAETwAgBDMEMAQ8BDwEMAAgADIALAAy -AC0EPwRABD4ERAQ4BDsETABHAGUAbgBlAHIAaQBjACAARwByAGEAeQAgAEcAYQBtAG0AYQAgADIALgAy -ACAAUAByAG8AZgBpAGwAZQAAdGV4dAAAAABDb3B5cmlnaHQgQXBwbGUgSW5jLiwgMjAxMgAAWFlaIAAA -AAAAAPNRAAEAAAABFsxjdXJ2AAAAAAAABAAAAAAFAAoADwAUABkAHgAjACgALQAyADcAOwBAAEUASgBP -AFQAWQBeAGMAaABtAHIAdwB8AIEAhgCLAJAAlQCaAJ8ApACpAK4AsgC3ALwAwQDGAMsA0ADVANsA4ADl -AOsA8AD2APsBAQEHAQ0BEwEZAR8BJQErATIBOAE+AUUBTAFSAVkBYAFnAW4BdQF8AYMBiwGSAZoBoQGp -AbEBuQHBAckB0QHZAeEB6QHyAfoCAwIMAhQCHQImAi8COAJBAksCVAJdAmcCcQJ6AoQCjgKYAqICrAK2 -AsECywLVAuAC6wL1AwADCwMWAyEDLQM4A0MDTwNaA2YDcgN+A4oDlgOiA64DugPHA9MD4APsA/kEBgQT -BCAELQQ7BEgEVQRjBHEEfgSMBJoEqAS2BMQE0wThBPAE/gUNBRwFKwU6BUkFWAVnBXcFhgWWBaYFtQXF -BdUF5QX2BgYGFgYnBjcGSAZZBmoGewaMBp0GrwbABtEG4wb1BwcHGQcrBz0HTwdhB3QHhgeZB6wHvwfS -B+UH+AgLCB8IMghGCFoIbgiCCJYIqgi+CNII5wj7CRAJJQk6CU8JZAl5CY8JpAm6Cc8J5Qn7ChEKJwo9 -ClQKagqBCpgKrgrFCtwK8wsLCyILOQtRC2kLgAuYC7ALyAvhC/kMEgwqDEMMXAx1DI4MpwzADNkM8w0N -DSYNQA1aDXQNjg2pDcMN3g34DhMOLg5JDmQOfw6bDrYO0g7uDwkPJQ9BD14Peg+WD7MPzw/sEAkQJhBD -EGEQfhCbELkQ1xD1ERMRMRFPEW0RjBGqEckR6BIHEiYSRRJkEoQSoxLDEuMTAxMjE0MTYxODE6QTxRPl -FAYUJxRJFGoUixStFM4U8BUSFTQVVhV4FZsVvRXgFgMWJhZJFmwWjxayFtYW+hcdF0EXZReJF64X0hf3 -GBsYQBhlGIoYrxjVGPoZIBlFGWsZkRm3Gd0aBBoqGlEadxqeGsUa7BsUGzsbYxuKG7Ib2hwCHCocUhx7 -HKMczBz1HR4dRx1wHZkdwx3sHhYeQB5qHpQevh7pHxMfPh9pH5Qfvx/qIBUgQSBsIJggxCDwIRwhSCF1 -IaEhziH7IiciVSKCIq8i3SMKIzgjZiOUI8Ij8CQfJE0kfCSrJNolCSU4JWgllyXHJfcmJyZXJocmtybo -JxgnSSd6J6sn3CgNKD8ocSiiKNQpBik4KWspnSnQKgIqNSpoKpsqzysCKzYraSudK9EsBSw5LG4soizX -LQwtQS12Last4S4WLkwugi63Lu4vJC9aL5Evxy/+MDUwbDCkMNsxEjFKMYIxujHyMioyYzKbMtQzDTNG -M38zuDPxNCs0ZTSeNNg1EzVNNYc1wjX9Njc2cjauNuk3JDdgN5w31zgUOFA4jDjIOQU5Qjl/Obw5+To2 -OnQ6sjrvOy07azuqO+g8JzxlPKQ84z0iPWE9oT3gPiA+YD6gPuA/IT9hP6I/4kAjQGRApkDnQSlBakGs -Qe5CMEJyQrVC90M6Q31DwEQDREdEikTORRJFVUWaRd5GIkZnRqtG8Ec1R3tHwEgFSEtIkUjXSR1JY0mp -SfBKN0p9SsRLDEtTS5pL4kwqTHJMuk0CTUpNk03cTiVObk63TwBPSU+TT91QJ1BxULtRBlFQUZtR5lIx -UnxSx1MTU19TqlP2VEJUj1TbVShVdVXCVg9WXFapVvdXRFeSV+BYL1h9WMtZGllpWbhaB1pWWqZa9VtF -W5Vb5Vw1XIZc1l0nXXhdyV4aXmxevV8PX2Ffs2AFYFdgqmD8YU9homH1YklinGLwY0Njl2PrZEBklGTp -ZT1lkmXnZj1mkmboZz1nk2fpaD9olmjsaUNpmmnxakhqn2r3a09rp2v/bFdsr20IbWBtuW4SbmtuxG8e -b3hv0XArcIZw4HE6cZVx8HJLcqZzAXNdc7h0FHRwdMx1KHWFdeF2Pnabdvh3VnezeBF4bnjMeSp5iXnn -ekZ6pXsEe2N7wnwhfIF84X1BfaF+AX5ifsJ/I3+Ef+WAR4CogQqBa4HNgjCCkoL0g1eDuoQdhICE44VH -hauGDoZyhteHO4efiASIaYjOiTOJmYn+imSKyoswi5aL/IxjjMqNMY2Yjf+OZo7OjzaPnpAGkG6Q1pE/ -kaiSEZJ6kuOTTZO2lCCUipT0lV+VyZY0lp+XCpd1l+CYTJi4mSSZkJn8mmia1ZtCm6+cHJyJnPedZJ3S -nkCerp8dn4uf+qBpoNihR6G2oiailqMGo3aj5qRWpMelOKWpphqmi6b9p26n4KhSqMSpN6mpqhyqj6sC -q3Wr6axcrNCtRK24ri2uoa8Wr4uwALB1sOqxYLHWskuywrM4s660JbSctRO1irYBtnm28Ldot+C4WbjR -uUq5wro7urW7LrunvCG8m70VvY++Cr6Evv+/er/1wHDA7MFnwePCX8Lbw1jD1MRRxM7FS8XIxkbGw8dB -x7/IPci8yTrJuco4yrfLNsu2zDXMtc01zbXONs62zzfPuNA50LrRPNG+0j/SwdNE08bUSdTL1U7V0dZV -1tjXXNfg2GTY6Nls2fHadtr724DcBdyK3RDdlt4c3qLfKd+v4DbgveFE4cziU+Lb42Pj6+Rz5PzlhOYN -5pbnH+ep6DLovOlG6dDqW+rl63Dr++yG7RHtnO4o7rTvQO/M8Fjw5fFy8f/yjPMZ86f0NPTC9VD13vZt -9vv3ivgZ+Kj5OPnH+lf65/t3/Af8mP0p/br+S/7c/23//9IrLC0uWiRjbGFzc25hbWVYJGNsYXNzZXNf -EBBOU0JpdG1hcEltYWdlUmVwoy0vMFpOU0ltYWdlUmVwWE5TT2JqZWN00issMjNXTlNBcnJheaIyMNIr -LDU2Xk5TTXV0YWJsZUFycmF5ozUyMNM4OQ86OzxXTlNXaGl0ZVxOU0NvbG9yU3BhY2VEMCAwABADgAzS -Kyw+P1dOU0NvbG9yoj4w0issQUJXTlNJbWFnZaJBMAAIABEAGgAkACkAMgA3AEkATABRAFMAYgBoAHUA -fACLAJIAnwCmAK4AsACyALQAuQC7AL0AxADJANQA1gDYANoA3wDiAOQA5gDoAO8BBgEiASQBJhOWE5sT -phOvE8ITxhPRE9oT3xPnE+oT7xP+FAIUCRQRFB4UIxQlFCcULBQ0FDcUPBREAAAAAAAAAgEAAAAAAAAA -QwAAAAAAAAAAAAAAAAAAFEc +S2V5ZWRBcmNoaXZlctEICVRyb290gAGuCwwbHCEaJissMzY5P0JVJG51bGzXDQ4PEBESExQVFhcYGRpW +TlNTaXplViRjbGFzc1xOU0ltYWdlRmxhZ3NWTlNSZXBzW05TVGludENvbG9yV05TQ29sb3JeTlNSZXNp +emluZ01vZGWAAoANEiDDAACAA4AAgAsQAFZ7MSwgMX3SHQ4eIFpOUy5vYmplY3RzoR+ABIAK0h0OIiWi +IySABYAGgAnTDicoKSoaXxAUTlNUSUZGUmVwcmVzZW50YXRpb25fEBlOU0ludGVybmFsTGF5b3V0RGly +ZWN0aW9ugAiAB08REmxNTQAqAAAACgAAABABAAADAAAAAQABAAABAQADAAAAAQABAAABAgADAAAAAgAI +AAgBAwADAAAAAQABAAABBgADAAAAAQABAAABCgADAAAAAQABAAABEQAEAAAAAQAAAAgBEgADAAAAAQAB +AAABFQADAAAAAQACAAABFgADAAAAAQABAAABFwAEAAAAAQAAAAIBHAADAAAAAQABAAABKAADAAAAAQAC +AAABUgADAAAAAQABAAABUwADAAAAAgABAAGHcwAHAAARnAAAANAAAAAAAAARnGFwcGwCAAAAbW50ckdS +QVlYWVogB9wACAAXAA8ALgAPYWNzcEFQUEwAAAAAbm9uZQAAAAAAAAAAAAAAAAAAAAAAAPbWAAEAAAAA +0y1hcHBsAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFZGVzYwAA +AMAAAAB5ZHNjbQAAATwAAAgaY3BydAAACVgAAAAjd3RwdAAACXwAAAAUa1RSQwAACZAAAAgMZGVzYwAA +AAAAAAAfR2VuZXJpYyBHcmF5IEdhbW1hIDIuMiBQcm9maWxlAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAG1s +dWMAAAAAAAAAHwAAAAxza1NLAAAALgAAAYRkYURLAAAAOgAAAbJjYUVTAAAAOAAAAex2aVZOAAAAQAAA +AiRwdEJSAAAASgAAAmR1a1VBAAAALAAAAq5mckZVAAAAPgAAAtpodUhVAAAANAAAAxh6aFRXAAAAGgAA +A0xrb0tSAAAAIgAAA2ZuYk5PAAAAOgAAA4hjc0NaAAAAKAAAA8JoZUlMAAAAJAAAA+pyb1JPAAAAKgAA +BA5kZURFAAAATgAABDhpdElUAAAATgAABIZzdlNFAAAAOAAABNR6aENOAAAAGgAABQxqYUpQAAAAJgAA +BSZlbEdSAAAAKgAABUxwdFBPAAAAUgAABXZubE5MAAAAQAAABchlc0VTAAAATAAABgh0aFRIAAAAMgAA +BlR0clRSAAAAJAAABoZmaUZJAAAARgAABqpockhSAAAAPgAABvBwbFBMAAAASgAABy5hckVHAAAALAAA +B3hydVJVAAAAOgAAB6RlblVTAAAAPAAAB94AVgFhAGUAbwBiAGUAYwBuAOEAIABzAGkAdgDhACAAZwBh +AG0AYQAgADIALAAyAEcAZQBuAGUAcgBpAHMAawAgAGcAcgDlACAAMgAsADIAIABnAGEAbQBtAGEALQBw +AHIAbwBmAGkAbABHAGEAbQBtAGEAIABkAGUAIABnAHIAaQBzAG8AcwAgAGcAZQBuAOgAcgBpAGMAYQAg +ADIALgAyAEMepQB1ACAAaADsAG4AaAAgAE0A4AB1ACAAeADhAG0AIABDAGgAdQBuAGcAIABHAGEAbQBt +AGEAIAAyAC4AMgBQAGUAcgBmAGkAbAAgAEcAZQBuAOkAcgBpAGMAbwAgAGQAYQAgAEcAYQBtAGEAIABk +AGUAIABDAGkAbgB6AGEAcwAgADIALAAyBBcEMAQzBDAEOwRMBD0EMAAgAEcAcgBhAHkALQQzBDAEPAQw +ACAAMgAuADIAUAByAG8AZgBpAGwAIABnAOkAbgDpAHIAaQBxAHUAZQAgAGcAcgBpAHMAIABnAGEAbQBt +AGEAIAAyACwAMgDBAGwAdABhAGwA4QBuAG8AcwAgAHMAegD8AHIAawBlACAAZwBhAG0AbQBhACAAMgAu +ADKQGnUocHCWjlFJXqYAMgAuADKCcl9pY8+P8Md8vBgAINaMwMkAIKwQucgAIAAyAC4AMgAg1QS4XNMM +x3wARwBlAG4AZQByAGkAcwBrACAAZwByAOUAIABnAGEAbQBtAGEAIAAyACwAMgAtAHAAcgBvAGYAaQBs +AE8AYgBlAGMAbgDhACABYQBlAGQA4QAgAGcAYQBtAGEAIAAyAC4AMgXSBdAF3gXUACAF0AXkBdUF6AAg +BdsF3AXcBdkAIAAyAC4AMgBHAGEAbQBhACAAZwByAGkAIABnAGUAbgBlAHIAaQBjAQMAIAAyACwAMgBB +AGwAbABnAGUAbQBlAGkAbgBlAHMAIABHAHIAYQB1AHMAdAB1AGYAZQBuAC0AUAByAG8AZgBpAGwAIABH +AGEAbQBtAGEAIAAyACwAMgBQAHIAbwBmAGkAbABvACAAZwByAGkAZwBpAG8AIABnAGUAbgBlAHIAaQBj +AG8AIABkAGUAbABsAGEAIABnAGEAbQBtAGEAIAAyACwAMgBHAGUAbgBlAHIAaQBzAGsAIABnAHIA5QAg +ADIALAAyACAAZwBhAG0AbQBhAHAAcgBvAGYAaQBsZm6QGnBwXqZ8+2VwADIALgAyY8+P8GWHTvZOAIIs +MLAw7DCkMKww8zDeACAAMgAuADIAIDDXMO0w1TChMKQw6wOTA7UDvQO5A7oDzAAgA5MDugPBA7kAIAOT +A6wDvAO8A7EAIAAyAC4AMgBQAGUAcgBmAGkAbAAgAGcAZQBuAOkAcgBpAGMAbwAgAGQAZQAgAGMAaQBu +AHoAZQBuAHQAbwBzACAAZABhACAARwBhAG0AbQBhACAAMgAsADIAQQBsAGcAZQBtAGUAZQBuACAAZwBy +AGkAagBzACAAZwBhAG0AbQBhACAAMgAsADIALQBwAHIAbwBmAGkAZQBsAFAAZQByAGYAaQBsACAAZwBl +AG4A6QByAGkAYwBvACAAZABlACAAZwBhAG0AbQBhACAAZABlACAAZwByAGkAcwBlAHMAIAAyACwAMg4j +DjEOBw4qDjUOQQ4BDiEOIQ4yDkAOAQ4jDiIOTA4XDjEOSA4nDkQOGwAgADIALgAyAEcAZQBuAGUAbAAg +AEcAcgBpACAARwBhAG0AYQAgADIALAAyAFkAbABlAGkAbgBlAG4AIABoAGEAcgBtAGEAYQBuACAAZwBh +AG0AbQBhACAAMgAsADIAIAAtAHAAcgBvAGYAaQBpAGwAaQBHAGUAbgBlAHIAaQENAGsAaQAgAEcAcgBh +AHkAIABHAGEAbQBtAGEAIAAyAC4AMgAgAHAAcgBvAGYAaQBsAFUAbgBpAHcAZQByAHMAYQBsAG4AeQAg +AHAAcgBvAGYAaQBsACAAcwB6AGEAcgBvAVsAYwBpACAAZwBhAG0AbQBhACAAMgAsADIGOgYnBkUGJwAg +ADIALgAyACAGRAZIBkYAIAYxBkUGJwYvBkoAIAY5BicGRQQeBDEESQQwBE8AIARBBDUEQAQwBE8AIAQz +BDAEPAQ8BDAAIAAyACwAMgAtBD8EQAQ+BEQEOAQ7BEwARwBlAG4AZQByAGkAYwAgAEcAcgBhAHkAIABH +AGEAbQBtAGEAIAAyAC4AMgAgAFAAcgBvAGYAaQBsAGUAAHRleHQAAAAAQ29weXJpZ2h0IEFwcGxlIElu +Yy4sIDIwMTIAAFhZWiAAAAAAAADzUQABAAAAARbMY3VydgAAAAAAAAQAAAAABQAKAA8AFAAZAB4AIwAo +AC0AMgA3ADsAQABFAEoATwBUAFkAXgBjAGgAbQByAHcAfACBAIYAiwCQAJUAmgCfAKQAqQCuALIAtwC8 +AMEAxgDLANAA1QDbAOAA5QDrAPAA9gD7AQEBBwENARMBGQEfASUBKwEyATgBPgFFAUwBUgFZAWABZwFu +AXUBfAGDAYsBkgGaAaEBqQGxAbkBwQHJAdEB2QHhAekB8gH6AgMCDAIUAh0CJgIvAjgCQQJLAlQCXQJn +AnECegKEAo4CmAKiAqwCtgLBAssC1QLgAusC9QMAAwsDFgMhAy0DOANDA08DWgNmA3IDfgOKA5YDogOu +A7oDxwPTA+AD7AP5BAYEEwQgBC0EOwRIBFUEYwRxBH4EjASaBKgEtgTEBNME4QTwBP4FDQUcBSsFOgVJ +BVgFZwV3BYYFlgWmBbUFxQXVBeUF9gYGBhYGJwY3BkgGWQZqBnsGjAadBq8GwAbRBuMG9QcHBxkHKwc9 +B08HYQd0B4YHmQesB78H0gflB/gICwgfCDIIRghaCG4IggiWCKoIvgjSCOcI+wkQCSUJOglPCWQJeQmP +CaQJugnPCeUJ+woRCicKPQpUCmoKgQqYCq4KxQrcCvMLCwsiCzkLUQtpC4ALmAuwC8gL4Qv5DBIMKgxD +DFwMdQyODKcMwAzZDPMNDQ0mDUANWg10DY4NqQ3DDd4N+A4TDi4OSQ5kDn8Omw62DtIO7g8JDyUPQQ9e +D3oPlg+zD88P7BAJECYQQxBhEH4QmxC5ENcQ9RETETERTxFtEYwRqhHJEegSBxImEkUSZBKEEqMSwxLj +EwMTIxNDE2MTgxOkE8UT5RQGFCcUSRRqFIsUrRTOFPAVEhU0FVYVeBWbFb0V4BYDFiYWSRZsFo8WshbW +FvoXHRdBF2UXiReuF9IX9xgbGEAYZRiKGK8Y1Rj6GSAZRRlrGZEZtxndGgQaKhpRGncanhrFGuwbFBs7 +G2MbihuyG9ocAhwqHFIcexyjHMwc9R0eHUcdcB2ZHcMd7B4WHkAeah6UHr4e6R8THz4faR+UH78f6iAV +IEEgbCCYIMQg8CEcIUghdSGhIc4h+yInIlUigiKvIt0jCiM4I2YjlCPCI/AkHyRNJHwkqyTaJQklOCVo +JZclxyX3JicmVyaHJrcm6CcYJ0kneierJ9woDSg/KHEooijUKQYpOClrKZ0p0CoCKjUqaCqbKs8rAis2 +K2krnSvRLAUsOSxuLKIs1y0MLUEtdi2rLeEuFi5MLoIuty7uLyQvWi+RL8cv/jA1MGwwpDDbMRIxSjGC +Mbox8jIqMmMymzLUMw0zRjN/M7gz8TQrNGU0njTYNRM1TTWHNcI1/TY3NnI2rjbpNyQ3YDecN9c4FDhQ +OIw4yDkFOUI5fzm8Ofk6Njp0OrI67zstO2s7qjvoPCc8ZTykPOM9Ij1hPaE94D4gPmA+oD7gPyE/YT+i +P+JAI0BkQKZA50EpQWpBrEHuQjBCckK1QvdDOkN9Q8BEA0RHRIpEzkUSRVVFmkXeRiJGZ0arRvBHNUd7 +R8BIBUhLSJFI10kdSWNJqUnwSjdKfUrESwxLU0uaS+JMKkxyTLpNAk1KTZNN3E4lTm5Ot08AT0lPk0/d +UCdQcVC7UQZRUFGbUeZSMVJ8UsdTE1NfU6pT9lRCVI9U21UoVXVVwlYPVlxWqVb3V0RXklfgWC9YfVjL +WRpZaVm4WgdaVlqmWvVbRVuVW+VcNVyGXNZdJ114XcleGl5sXr1fD19hX7NgBWBXYKpg/GFPYaJh9WJJ +Ypxi8GNDY5dj62RAZJRk6WU9ZZJl52Y9ZpJm6Gc9Z5Nn6Wg/aJZo7GlDaZpp8WpIap9q92tPa6dr/2xX +bK9tCG1gbbluEm5rbsRvHm94b9FwK3CGcOBxOnGVcfByS3KmcwFzXXO4dBR0cHTMdSh1hXXhdj52m3b4 +d1Z3s3gReG54zHkqeYl553pGeqV7BHtje8J8IXyBfOF9QX2hfgF+Yn7CfyN/hH/lgEeAqIEKgWuBzYIw +gpKC9INXg7qEHYSAhOOFR4Wrhg6GcobXhzuHn4gEiGmIzokziZmJ/opkisqLMIuWi/yMY4zKjTGNmI3/ +jmaOzo82j56QBpBukNaRP5GokhGSepLjk02TtpQglIqU9JVflcmWNJaflwqXdZfgmEyYuJkkmZCZ/Jpo +mtWbQpuvnByciZz3nWSd0p5Anq6fHZ+Ln/qgaaDYoUehtqImopajBqN2o+akVqTHpTilqaYapoum/adu +p+CoUqjEqTepqaocqo+rAqt1q+msXKzQrUStuK4trqGvFq+LsACwdbDqsWCx1rJLssKzOLOutCW0nLUT +tYq2AbZ5tvC3aLfguFm40blKucK6O7q1uy67p7whvJu9Fb2Pvgq+hL7/v3q/9cBwwOzBZ8Hjwl/C28NY +w9TEUcTOxUvFyMZGxsPHQce/yD3IvMk6ybnKOMq3yzbLtsw1zLXNNc21zjbOts83z7jQOdC60TzRvtI/ +0sHTRNPG1EnUy9VO1dHWVdbY11zX4Nhk2OjZbNnx2nba+9uA3AXcit0Q3ZbeHN6i3ynfr+A24L3hROHM +4lPi2+Nj4+vkc+T85YTmDeaW5x/nqegy6LzpRunQ6lvq5etw6/vshu0R7ZzuKO6070DvzPBY8OXxcvH/ +8ozzGfOn9DT0wvVQ9d72bfb794r4Gfio+Tj5x/pX+uf7d/wH/Jj9Kf26/kv+3P9t///SLS4vMFokY2xh +c3NuYW1lWCRjbGFzc2VzXxAQTlNCaXRtYXBJbWFnZVJlcKMvMTJaTlNJbWFnZVJlcFhOU09iamVjdNIt +LjQ1V05TQXJyYXmiNDLSLS43OF5OU011dGFibGVBcnJheaM3NDLTOjsOPD0+V05TV2hpdGVcTlNDb2xv +clNwYWNlRDAgMAAQA4AM0i0uQEFXTlNDb2xvcqJAMtItLkNEV05TSW1hZ2WiQzIACAARABoAJAApADIA +NwBJAEwAUQBTAGIAaAB3AH4AhQCSAJkApQCtALwAvgDAAMUAxwDJAMsAzQDUANkA5ADmAOgA6gDvAPIA +9AD2APgA/wEWATIBNAE2E6YTqxO2E78T0hPWE+ET6hPvE/cT+hP/FA4UEhQZFCEULhQzFDUUNxQ8FEQU +RxRMFFQAAAAAAAACAQAAAAAAAABFAAAAAAAAAAAAAAAAAAAUVw diff --git a/i18n/src/main/resources/cy.lproj/Browser.xib b/i18n/src/main/resources/cy.lproj/Browser.xib index d6932636d6..ded330b74b 100644 --- a/i18n/src/main/resources/cy.lproj/Browser.xib +++ b/i18n/src/main/resources/cy.lproj/Browser.xib @@ -1,7 +1,7 @@ - + - + @@ -38,7 +38,7 @@ - + @@ -62,27 +62,27 @@ @@ -256,7 +256,7 @@ - + @@ -553,95 +553,95 @@ DQ YnBsaXN0MDDUAQIDBAUGBwpYJHZlcnNpb25ZJGFyY2hpdmVyVCR0b3BYJG9iamVjdHMSAAGGoF8QD05T -S2V5ZWRBcmNoaXZlctEICVRyb290gAGuCwwZGh8UJCkqMTQ3PUBVJG51bGzWDQ4PEBESExQVFhcYVk5T -U2l6ZV5OU1Jlc2l6aW5nTW9kZVYkY2xhc3NcTlNJbWFnZUZsYWdzVk5TUmVwc1dOU0NvbG9ygAIQAIAN -EiDDAACAA4ALVnsxLCAxfdIbDxweWk5TLm9iamVjdHOhHYAEgArSGw8gI6IhIoAFgAaACdMPJSYnKBRf -EBROU1RJRkZSZXByZXNlbnRhdGlvbl8QGU5TSW50ZXJuYWxMYXlvdXREaXJlY3Rpb26ACIAHTxESbE1N -ACoAAAAKAAAAEAEAAAMAAAABAAEAAAEBAAMAAAABAAEAAAECAAMAAAACAAgACAEDAAMAAAABAAEAAAEG -AAMAAAABAAEAAAEKAAMAAAABAAEAAAERAAQAAAABAAAACAESAAMAAAABAAEAAAEVAAMAAAABAAIAAAEW -AAMAAAABAAEAAAEXAAQAAAABAAAAAgEcAAMAAAABAAEAAAEoAAMAAAABAAIAAAFSAAMAAAABAAEAAAFT -AAMAAAACAAEAAYdzAAcAABGcAAAA0AAAAAAAABGcYXBwbAIAAABtbnRyR1JBWVhZWiAH3AAIABcADwAu -AA9hY3NwQVBQTAAAAABub25lAAAAAAAAAAAAAAAAAAAAAAAA9tYAAQAAAADTLWFwcGwAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAVkZXNjAAAAwAAAAHlkc2NtAAABPAAA -CBpjcHJ0AAAJWAAAACN3dHB0AAAJfAAAABRrVFJDAAAJkAAACAxkZXNjAAAAAAAAAB9HZW5lcmljIEdy -YXkgR2FtbWEgMi4yIFByb2ZpbGUAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAbWx1YwAAAAAAAAAfAAAADHNr -U0sAAAAuAAABhGRhREsAAAA6AAABsmNhRVMAAAA4AAAB7HZpVk4AAABAAAACJHB0QlIAAABKAAACZHVr -VUEAAAAsAAACrmZyRlUAAAA+AAAC2mh1SFUAAAA0AAADGHpoVFcAAAAaAAADTGtvS1IAAAAiAAADZm5i -Tk8AAAA6AAADiGNzQ1oAAAAoAAADwmhlSUwAAAAkAAAD6nJvUk8AAAAqAAAEDmRlREUAAABOAAAEOGl0 -SVQAAABOAAAEhnN2U0UAAAA4AAAE1HpoQ04AAAAaAAAFDGphSlAAAAAmAAAFJmVsR1IAAAAqAAAFTHB0 -UE8AAABSAAAFdm5sTkwAAABAAAAFyGVzRVMAAABMAAAGCHRoVEgAAAAyAAAGVHRyVFIAAAAkAAAGhmZp -RkkAAABGAAAGqmhySFIAAAA+AAAG8HBsUEwAAABKAAAHLmFyRUcAAAAsAAAHeHJ1UlUAAAA6AAAHpGVu -VVMAAAA8AAAH3gBWAWEAZQBvAGIAZQBjAG4A4QAgAHMAaQB2AOEAIABnAGEAbQBhACAAMgAsADIARwBl -AG4AZQByAGkAcwBrACAAZwByAOUAIAAyACwAMgAgAGcAYQBtAG0AYQAtAHAAcgBvAGYAaQBsAEcAYQBt -AG0AYQAgAGQAZQAgAGcAcgBpAHMAbwBzACAAZwBlAG4A6AByAGkAYwBhACAAMgAuADIAQx6lAHUAIABo -AOwAbgBoACAATQDgAHUAIAB4AOEAbQAgAEMAaAB1AG4AZwAgAEcAYQBtAG0AYQAgADIALgAyAFAAZQBy -AGYAaQBsACAARwBlAG4A6QByAGkAYwBvACAAZABhACAARwBhAG0AYQAgAGQAZQAgAEMAaQBuAHoAYQBz -ACAAMgAsADIEFwQwBDMEMAQ7BEwEPQQwACAARwByAGEAeQAtBDMEMAQ8BDAAIAAyAC4AMgBQAHIAbwBm -AGkAbAAgAGcA6QBuAOkAcgBpAHEAdQBlACAAZwByAGkAcwAgAGcAYQBtAG0AYQAgADIALAAyAMEAbAB0 -AGEAbADhAG4AbwBzACAAcwB6APwAcgBrAGUAIABnAGEAbQBtAGEAIAAyAC4AMpAadShwcJaOUUlepgAy -AC4AMoJyX2ljz4/wx3y8GAAg1ozAyQAgrBC5yAAgADIALgAyACDVBLhc0wzHfABHAGUAbgBlAHIAaQBz -AGsAIABnAHIA5QAgAGcAYQBtAG0AYQAgADIALAAyAC0AcAByAG8AZgBpAGwATwBiAGUAYwBuAOEAIAFh -AGUAZADhACAAZwBhAG0AYQAgADIALgAyBdIF0AXeBdQAIAXQBeQF1QXoACAF2wXcBdwF2QAgADIALgAy -AEcAYQBtAGEAIABnAHIAaQAgAGcAZQBuAGUAcgBpAGMBAwAgADIALAAyAEEAbABsAGcAZQBtAGUAaQBu -AGUAcwAgAEcAcgBhAHUAcwB0AHUAZgBlAG4ALQBQAHIAbwBmAGkAbAAgAEcAYQBtAG0AYQAgADIALAAy -AFAAcgBvAGYAaQBsAG8AIABnAHIAaQBnAGkAbwAgAGcAZQBuAGUAcgBpAGMAbwAgAGQAZQBsAGwAYQAg -AGcAYQBtAG0AYQAgADIALAAyAEcAZQBuAGUAcgBpAHMAawAgAGcAcgDlACAAMgAsADIAIABnAGEAbQBt -AGEAcAByAG8AZgBpAGxmbpAacHBepnz7ZXAAMgAuADJjz4/wZYdO9k4AgiwwsDDsMKQwrDDzMN4AIAAy -AC4AMgAgMNcw7TDVMKEwpDDrA5MDtQO9A7kDugPMACADkwO6A8EDuQAgA5MDrAO8A7wDsQAgADIALgAy -AFAAZQByAGYAaQBsACAAZwBlAG4A6QByAGkAYwBvACAAZABlACAAYwBpAG4AegBlAG4AdABvAHMAIABk -AGEAIABHAGEAbQBtAGEAIAAyACwAMgBBAGwAZwBlAG0AZQBlAG4AIABnAHIAaQBqAHMAIABnAGEAbQBt -AGEAIAAyACwAMgAtAHAAcgBvAGYAaQBlAGwAUABlAHIAZgBpAGwAIABnAGUAbgDpAHIAaQBjAG8AIABk -AGUAIABnAGEAbQBtAGEAIABkAGUAIABnAHIAaQBzAGUAcwAgADIALAAyDiMOMQ4HDioONQ5BDgEOIQ4h -DjIOQA4BDiMOIg5MDhcOMQ5IDicORA4bACAAMgAuADIARwBlAG4AZQBsACAARwByAGkAIABHAGEAbQBh -ACAAMgAsADIAWQBsAGUAaQBuAGUAbgAgAGgAYQByAG0AYQBhAG4AIABnAGEAbQBtAGEAIAAyACwAMgAg -AC0AcAByAG8AZgBpAGkAbABpAEcAZQBuAGUAcgBpAQ0AawBpACAARwByAGEAeQAgAEcAYQBtAG0AYQAg -ADIALgAyACAAcAByAG8AZgBpAGwAVQBuAGkAdwBlAHIAcwBhAGwAbgB5ACAAcAByAG8AZgBpAGwAIABz -AHoAYQByAG8BWwBjAGkAIABnAGEAbQBtAGEAIAAyACwAMgY6BicGRQYnACAAMgAuADIAIAZEBkgGRgAg -BjEGRQYnBi8GSgAgBjkGJwZFBB4EMQRJBDAETwAgBEEENQRABDAETwAgBDMEMAQ8BDwEMAAgADIALAAy -AC0EPwRABD4ERAQ4BDsETABHAGUAbgBlAHIAaQBjACAARwByAGEAeQAgAEcAYQBtAG0AYQAgADIALgAy -ACAAUAByAG8AZgBpAGwAZQAAdGV4dAAAAABDb3B5cmlnaHQgQXBwbGUgSW5jLiwgMjAxMgAAWFlaIAAA -AAAAAPNRAAEAAAABFsxjdXJ2AAAAAAAABAAAAAAFAAoADwAUABkAHgAjACgALQAyADcAOwBAAEUASgBP -AFQAWQBeAGMAaABtAHIAdwB8AIEAhgCLAJAAlQCaAJ8ApACpAK4AsgC3ALwAwQDGAMsA0ADVANsA4ADl -AOsA8AD2APsBAQEHAQ0BEwEZAR8BJQErATIBOAE+AUUBTAFSAVkBYAFnAW4BdQF8AYMBiwGSAZoBoQGp -AbEBuQHBAckB0QHZAeEB6QHyAfoCAwIMAhQCHQImAi8COAJBAksCVAJdAmcCcQJ6AoQCjgKYAqICrAK2 -AsECywLVAuAC6wL1AwADCwMWAyEDLQM4A0MDTwNaA2YDcgN+A4oDlgOiA64DugPHA9MD4APsA/kEBgQT -BCAELQQ7BEgEVQRjBHEEfgSMBJoEqAS2BMQE0wThBPAE/gUNBRwFKwU6BUkFWAVnBXcFhgWWBaYFtQXF -BdUF5QX2BgYGFgYnBjcGSAZZBmoGewaMBp0GrwbABtEG4wb1BwcHGQcrBz0HTwdhB3QHhgeZB6wHvwfS -B+UH+AgLCB8IMghGCFoIbgiCCJYIqgi+CNII5wj7CRAJJQk6CU8JZAl5CY8JpAm6Cc8J5Qn7ChEKJwo9 -ClQKagqBCpgKrgrFCtwK8wsLCyILOQtRC2kLgAuYC7ALyAvhC/kMEgwqDEMMXAx1DI4MpwzADNkM8w0N -DSYNQA1aDXQNjg2pDcMN3g34DhMOLg5JDmQOfw6bDrYO0g7uDwkPJQ9BD14Peg+WD7MPzw/sEAkQJhBD -EGEQfhCbELkQ1xD1ERMRMRFPEW0RjBGqEckR6BIHEiYSRRJkEoQSoxLDEuMTAxMjE0MTYxODE6QTxRPl -FAYUJxRJFGoUixStFM4U8BUSFTQVVhV4FZsVvRXgFgMWJhZJFmwWjxayFtYW+hcdF0EXZReJF64X0hf3 -GBsYQBhlGIoYrxjVGPoZIBlFGWsZkRm3Gd0aBBoqGlEadxqeGsUa7BsUGzsbYxuKG7Ib2hwCHCocUhx7 -HKMczBz1HR4dRx1wHZkdwx3sHhYeQB5qHpQevh7pHxMfPh9pH5Qfvx/qIBUgQSBsIJggxCDwIRwhSCF1 -IaEhziH7IiciVSKCIq8i3SMKIzgjZiOUI8Ij8CQfJE0kfCSrJNolCSU4JWgllyXHJfcmJyZXJocmtybo -JxgnSSd6J6sn3CgNKD8ocSiiKNQpBik4KWspnSnQKgIqNSpoKpsqzysCKzYraSudK9EsBSw5LG4soizX -LQwtQS12Last4S4WLkwugi63Lu4vJC9aL5Evxy/+MDUwbDCkMNsxEjFKMYIxujHyMioyYzKbMtQzDTNG -M38zuDPxNCs0ZTSeNNg1EzVNNYc1wjX9Njc2cjauNuk3JDdgN5w31zgUOFA4jDjIOQU5Qjl/Obw5+To2 -OnQ6sjrvOy07azuqO+g8JzxlPKQ84z0iPWE9oT3gPiA+YD6gPuA/IT9hP6I/4kAjQGRApkDnQSlBakGs -Qe5CMEJyQrVC90M6Q31DwEQDREdEikTORRJFVUWaRd5GIkZnRqtG8Ec1R3tHwEgFSEtIkUjXSR1JY0mp -SfBKN0p9SsRLDEtTS5pL4kwqTHJMuk0CTUpNk03cTiVObk63TwBPSU+TT91QJ1BxULtRBlFQUZtR5lIx -UnxSx1MTU19TqlP2VEJUj1TbVShVdVXCVg9WXFapVvdXRFeSV+BYL1h9WMtZGllpWbhaB1pWWqZa9VtF -W5Vb5Vw1XIZc1l0nXXhdyV4aXmxevV8PX2Ffs2AFYFdgqmD8YU9homH1YklinGLwY0Njl2PrZEBklGTp -ZT1lkmXnZj1mkmboZz1nk2fpaD9olmjsaUNpmmnxakhqn2r3a09rp2v/bFdsr20IbWBtuW4SbmtuxG8e -b3hv0XArcIZw4HE6cZVx8HJLcqZzAXNdc7h0FHRwdMx1KHWFdeF2Pnabdvh3VnezeBF4bnjMeSp5iXnn -ekZ6pXsEe2N7wnwhfIF84X1BfaF+AX5ifsJ/I3+Ef+WAR4CogQqBa4HNgjCCkoL0g1eDuoQdhICE44VH -hauGDoZyhteHO4efiASIaYjOiTOJmYn+imSKyoswi5aL/IxjjMqNMY2Yjf+OZo7OjzaPnpAGkG6Q1pE/ -kaiSEZJ6kuOTTZO2lCCUipT0lV+VyZY0lp+XCpd1l+CYTJi4mSSZkJn8mmia1ZtCm6+cHJyJnPedZJ3S -nkCerp8dn4uf+qBpoNihR6G2oiailqMGo3aj5qRWpMelOKWpphqmi6b9p26n4KhSqMSpN6mpqhyqj6sC -q3Wr6axcrNCtRK24ri2uoa8Wr4uwALB1sOqxYLHWskuywrM4s660JbSctRO1irYBtnm28Ldot+C4WbjR -uUq5wro7urW7LrunvCG8m70VvY++Cr6Evv+/er/1wHDA7MFnwePCX8Lbw1jD1MRRxM7FS8XIxkbGw8dB -x7/IPci8yTrJuco4yrfLNsu2zDXMtc01zbXONs62zzfPuNA50LrRPNG+0j/SwdNE08bUSdTL1U7V0dZV -1tjXXNfg2GTY6Nls2fHadtr724DcBdyK3RDdlt4c3qLfKd+v4DbgveFE4cziU+Lb42Pj6+Rz5PzlhOYN -5pbnH+ep6DLovOlG6dDqW+rl63Dr++yG7RHtnO4o7rTvQO/M8Fjw5fFy8f/yjPMZ86f0NPTC9VD13vZt -9vv3ivgZ+Kj5OPnH+lf65/t3/Af8mP0p/br+S/7c/23//9IrLC0uWiRjbGFzc25hbWVYJGNsYXNzZXNf -EBBOU0JpdG1hcEltYWdlUmVwoy0vMFpOU0ltYWdlUmVwWE5TT2JqZWN00issMjNXTlNBcnJheaIyMNIr -LDU2Xk5TTXV0YWJsZUFycmF5ozUyMNM4OQ86OzxXTlNXaGl0ZVxOU0NvbG9yU3BhY2VEMCAwABADgAzS -Kyw+P1dOU0NvbG9yoj4w0issQUJXTlNJbWFnZaJBMAAIABEAGgAkACkAMgA3AEkATABRAFMAYgBoAHUA -fACLAJIAnwCmAK4AsACyALQAuQC7AL0AxADJANQA1gDYANoA3wDiAOQA5gDoAO8BBgEiASQBJhOWE5sT -phOvE8ITxhPRE9oT3xPnE+oT7xP+FAIUCRQRFB4UIxQlFCcULBQ0FDcUPBREAAAAAAAAAgEAAAAAAAAA -QwAAAAAAAAAAAAAAAAAAFEc +S2V5ZWRBcmNoaXZlctEICVRyb290gAGuCwwbHCEaJissMzY5P0JVJG51bGzXDQ4PEBESExQVFhcYGRpW +TlNTaXplViRjbGFzc1xOU0ltYWdlRmxhZ3NWTlNSZXBzW05TVGludENvbG9yV05TQ29sb3JeTlNSZXNp +emluZ01vZGWAAoANEiDDAACAA4AAgAsQAFZ7MSwgMX3SHQ4eIFpOUy5vYmplY3RzoR+ABIAK0h0OIiWi +IySABYAGgAnTDicoKSoaXxAUTlNUSUZGUmVwcmVzZW50YXRpb25fEBlOU0ludGVybmFsTGF5b3V0RGly +ZWN0aW9ugAiAB08REmxNTQAqAAAACgAAABABAAADAAAAAQABAAABAQADAAAAAQABAAABAgADAAAAAgAI +AAgBAwADAAAAAQABAAABBgADAAAAAQABAAABCgADAAAAAQABAAABEQAEAAAAAQAAAAgBEgADAAAAAQAB +AAABFQADAAAAAQACAAABFgADAAAAAQABAAABFwAEAAAAAQAAAAIBHAADAAAAAQABAAABKAADAAAAAQAC +AAABUgADAAAAAQABAAABUwADAAAAAgABAAGHcwAHAAARnAAAANAAAAAAAAARnGFwcGwCAAAAbW50ckdS +QVlYWVogB9wACAAXAA8ALgAPYWNzcEFQUEwAAAAAbm9uZQAAAAAAAAAAAAAAAAAAAAAAAPbWAAEAAAAA +0y1hcHBsAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFZGVzYwAA +AMAAAAB5ZHNjbQAAATwAAAgaY3BydAAACVgAAAAjd3RwdAAACXwAAAAUa1RSQwAACZAAAAgMZGVzYwAA +AAAAAAAfR2VuZXJpYyBHcmF5IEdhbW1hIDIuMiBQcm9maWxlAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAG1s +dWMAAAAAAAAAHwAAAAxza1NLAAAALgAAAYRkYURLAAAAOgAAAbJjYUVTAAAAOAAAAex2aVZOAAAAQAAA +AiRwdEJSAAAASgAAAmR1a1VBAAAALAAAAq5mckZVAAAAPgAAAtpodUhVAAAANAAAAxh6aFRXAAAAGgAA +A0xrb0tSAAAAIgAAA2ZuYk5PAAAAOgAAA4hjc0NaAAAAKAAAA8JoZUlMAAAAJAAAA+pyb1JPAAAAKgAA +BA5kZURFAAAATgAABDhpdElUAAAATgAABIZzdlNFAAAAOAAABNR6aENOAAAAGgAABQxqYUpQAAAAJgAA +BSZlbEdSAAAAKgAABUxwdFBPAAAAUgAABXZubE5MAAAAQAAABchlc0VTAAAATAAABgh0aFRIAAAAMgAA +BlR0clRSAAAAJAAABoZmaUZJAAAARgAABqpockhSAAAAPgAABvBwbFBMAAAASgAABy5hckVHAAAALAAA +B3hydVJVAAAAOgAAB6RlblVTAAAAPAAAB94AVgFhAGUAbwBiAGUAYwBuAOEAIABzAGkAdgDhACAAZwBh +AG0AYQAgADIALAAyAEcAZQBuAGUAcgBpAHMAawAgAGcAcgDlACAAMgAsADIAIABnAGEAbQBtAGEALQBw +AHIAbwBmAGkAbABHAGEAbQBtAGEAIABkAGUAIABnAHIAaQBzAG8AcwAgAGcAZQBuAOgAcgBpAGMAYQAg +ADIALgAyAEMepQB1ACAAaADsAG4AaAAgAE0A4AB1ACAAeADhAG0AIABDAGgAdQBuAGcAIABHAGEAbQBt +AGEAIAAyAC4AMgBQAGUAcgBmAGkAbAAgAEcAZQBuAOkAcgBpAGMAbwAgAGQAYQAgAEcAYQBtAGEAIABk +AGUAIABDAGkAbgB6AGEAcwAgADIALAAyBBcEMAQzBDAEOwRMBD0EMAAgAEcAcgBhAHkALQQzBDAEPAQw +ACAAMgAuADIAUAByAG8AZgBpAGwAIABnAOkAbgDpAHIAaQBxAHUAZQAgAGcAcgBpAHMAIABnAGEAbQBt +AGEAIAAyACwAMgDBAGwAdABhAGwA4QBuAG8AcwAgAHMAegD8AHIAawBlACAAZwBhAG0AbQBhACAAMgAu +ADKQGnUocHCWjlFJXqYAMgAuADKCcl9pY8+P8Md8vBgAINaMwMkAIKwQucgAIAAyAC4AMgAg1QS4XNMM +x3wARwBlAG4AZQByAGkAcwBrACAAZwByAOUAIABnAGEAbQBtAGEAIAAyACwAMgAtAHAAcgBvAGYAaQBs +AE8AYgBlAGMAbgDhACABYQBlAGQA4QAgAGcAYQBtAGEAIAAyAC4AMgXSBdAF3gXUACAF0AXkBdUF6AAg +BdsF3AXcBdkAIAAyAC4AMgBHAGEAbQBhACAAZwByAGkAIABnAGUAbgBlAHIAaQBjAQMAIAAyACwAMgBB +AGwAbABnAGUAbQBlAGkAbgBlAHMAIABHAHIAYQB1AHMAdAB1AGYAZQBuAC0AUAByAG8AZgBpAGwAIABH +AGEAbQBtAGEAIAAyACwAMgBQAHIAbwBmAGkAbABvACAAZwByAGkAZwBpAG8AIABnAGUAbgBlAHIAaQBj +AG8AIABkAGUAbABsAGEAIABnAGEAbQBtAGEAIAAyACwAMgBHAGUAbgBlAHIAaQBzAGsAIABnAHIA5QAg +ADIALAAyACAAZwBhAG0AbQBhAHAAcgBvAGYAaQBsZm6QGnBwXqZ8+2VwADIALgAyY8+P8GWHTvZOAIIs +MLAw7DCkMKww8zDeACAAMgAuADIAIDDXMO0w1TChMKQw6wOTA7UDvQO5A7oDzAAgA5MDugPBA7kAIAOT +A6wDvAO8A7EAIAAyAC4AMgBQAGUAcgBmAGkAbAAgAGcAZQBuAOkAcgBpAGMAbwAgAGQAZQAgAGMAaQBu +AHoAZQBuAHQAbwBzACAAZABhACAARwBhAG0AbQBhACAAMgAsADIAQQBsAGcAZQBtAGUAZQBuACAAZwBy +AGkAagBzACAAZwBhAG0AbQBhACAAMgAsADIALQBwAHIAbwBmAGkAZQBsAFAAZQByAGYAaQBsACAAZwBl +AG4A6QByAGkAYwBvACAAZABlACAAZwBhAG0AbQBhACAAZABlACAAZwByAGkAcwBlAHMAIAAyACwAMg4j +DjEOBw4qDjUOQQ4BDiEOIQ4yDkAOAQ4jDiIOTA4XDjEOSA4nDkQOGwAgADIALgAyAEcAZQBuAGUAbAAg +AEcAcgBpACAARwBhAG0AYQAgADIALAAyAFkAbABlAGkAbgBlAG4AIABoAGEAcgBtAGEAYQBuACAAZwBh +AG0AbQBhACAAMgAsADIAIAAtAHAAcgBvAGYAaQBpAGwAaQBHAGUAbgBlAHIAaQENAGsAaQAgAEcAcgBh +AHkAIABHAGEAbQBtAGEAIAAyAC4AMgAgAHAAcgBvAGYAaQBsAFUAbgBpAHcAZQByAHMAYQBsAG4AeQAg +AHAAcgBvAGYAaQBsACAAcwB6AGEAcgBvAVsAYwBpACAAZwBhAG0AbQBhACAAMgAsADIGOgYnBkUGJwAg +ADIALgAyACAGRAZIBkYAIAYxBkUGJwYvBkoAIAY5BicGRQQeBDEESQQwBE8AIARBBDUEQAQwBE8AIAQz +BDAEPAQ8BDAAIAAyACwAMgAtBD8EQAQ+BEQEOAQ7BEwARwBlAG4AZQByAGkAYwAgAEcAcgBhAHkAIABH +AGEAbQBtAGEAIAAyAC4AMgAgAFAAcgBvAGYAaQBsAGUAAHRleHQAAAAAQ29weXJpZ2h0IEFwcGxlIElu +Yy4sIDIwMTIAAFhZWiAAAAAAAADzUQABAAAAARbMY3VydgAAAAAAAAQAAAAABQAKAA8AFAAZAB4AIwAo +AC0AMgA3ADsAQABFAEoATwBUAFkAXgBjAGgAbQByAHcAfACBAIYAiwCQAJUAmgCfAKQAqQCuALIAtwC8 +AMEAxgDLANAA1QDbAOAA5QDrAPAA9gD7AQEBBwENARMBGQEfASUBKwEyATgBPgFFAUwBUgFZAWABZwFu +AXUBfAGDAYsBkgGaAaEBqQGxAbkBwQHJAdEB2QHhAekB8gH6AgMCDAIUAh0CJgIvAjgCQQJLAlQCXQJn +AnECegKEAo4CmAKiAqwCtgLBAssC1QLgAusC9QMAAwsDFgMhAy0DOANDA08DWgNmA3IDfgOKA5YDogOu +A7oDxwPTA+AD7AP5BAYEEwQgBC0EOwRIBFUEYwRxBH4EjASaBKgEtgTEBNME4QTwBP4FDQUcBSsFOgVJ +BVgFZwV3BYYFlgWmBbUFxQXVBeUF9gYGBhYGJwY3BkgGWQZqBnsGjAadBq8GwAbRBuMG9QcHBxkHKwc9 +B08HYQd0B4YHmQesB78H0gflB/gICwgfCDIIRghaCG4IggiWCKoIvgjSCOcI+wkQCSUJOglPCWQJeQmP +CaQJugnPCeUJ+woRCicKPQpUCmoKgQqYCq4KxQrcCvMLCwsiCzkLUQtpC4ALmAuwC8gL4Qv5DBIMKgxD +DFwMdQyODKcMwAzZDPMNDQ0mDUANWg10DY4NqQ3DDd4N+A4TDi4OSQ5kDn8Omw62DtIO7g8JDyUPQQ9e +D3oPlg+zD88P7BAJECYQQxBhEH4QmxC5ENcQ9RETETERTxFtEYwRqhHJEegSBxImEkUSZBKEEqMSwxLj +EwMTIxNDE2MTgxOkE8UT5RQGFCcUSRRqFIsUrRTOFPAVEhU0FVYVeBWbFb0V4BYDFiYWSRZsFo8WshbW +FvoXHRdBF2UXiReuF9IX9xgbGEAYZRiKGK8Y1Rj6GSAZRRlrGZEZtxndGgQaKhpRGncanhrFGuwbFBs7 +G2MbihuyG9ocAhwqHFIcexyjHMwc9R0eHUcdcB2ZHcMd7B4WHkAeah6UHr4e6R8THz4faR+UH78f6iAV +IEEgbCCYIMQg8CEcIUghdSGhIc4h+yInIlUigiKvIt0jCiM4I2YjlCPCI/AkHyRNJHwkqyTaJQklOCVo +JZclxyX3JicmVyaHJrcm6CcYJ0kneierJ9woDSg/KHEooijUKQYpOClrKZ0p0CoCKjUqaCqbKs8rAis2 +K2krnSvRLAUsOSxuLKIs1y0MLUEtdi2rLeEuFi5MLoIuty7uLyQvWi+RL8cv/jA1MGwwpDDbMRIxSjGC +Mbox8jIqMmMymzLUMw0zRjN/M7gz8TQrNGU0njTYNRM1TTWHNcI1/TY3NnI2rjbpNyQ3YDecN9c4FDhQ +OIw4yDkFOUI5fzm8Ofk6Njp0OrI67zstO2s7qjvoPCc8ZTykPOM9Ij1hPaE94D4gPmA+oD7gPyE/YT+i +P+JAI0BkQKZA50EpQWpBrEHuQjBCckK1QvdDOkN9Q8BEA0RHRIpEzkUSRVVFmkXeRiJGZ0arRvBHNUd7 +R8BIBUhLSJFI10kdSWNJqUnwSjdKfUrESwxLU0uaS+JMKkxyTLpNAk1KTZNN3E4lTm5Ot08AT0lPk0/d +UCdQcVC7UQZRUFGbUeZSMVJ8UsdTE1NfU6pT9lRCVI9U21UoVXVVwlYPVlxWqVb3V0RXklfgWC9YfVjL +WRpZaVm4WgdaVlqmWvVbRVuVW+VcNVyGXNZdJ114XcleGl5sXr1fD19hX7NgBWBXYKpg/GFPYaJh9WJJ +Ypxi8GNDY5dj62RAZJRk6WU9ZZJl52Y9ZpJm6Gc9Z5Nn6Wg/aJZo7GlDaZpp8WpIap9q92tPa6dr/2xX +bK9tCG1gbbluEm5rbsRvHm94b9FwK3CGcOBxOnGVcfByS3KmcwFzXXO4dBR0cHTMdSh1hXXhdj52m3b4 +d1Z3s3gReG54zHkqeYl553pGeqV7BHtje8J8IXyBfOF9QX2hfgF+Yn7CfyN/hH/lgEeAqIEKgWuBzYIw +gpKC9INXg7qEHYSAhOOFR4Wrhg6GcobXhzuHn4gEiGmIzokziZmJ/opkisqLMIuWi/yMY4zKjTGNmI3/ +jmaOzo82j56QBpBukNaRP5GokhGSepLjk02TtpQglIqU9JVflcmWNJaflwqXdZfgmEyYuJkkmZCZ/Jpo +mtWbQpuvnByciZz3nWSd0p5Anq6fHZ+Ln/qgaaDYoUehtqImopajBqN2o+akVqTHpTilqaYapoum/adu +p+CoUqjEqTepqaocqo+rAqt1q+msXKzQrUStuK4trqGvFq+LsACwdbDqsWCx1rJLssKzOLOutCW0nLUT +tYq2AbZ5tvC3aLfguFm40blKucK6O7q1uy67p7whvJu9Fb2Pvgq+hL7/v3q/9cBwwOzBZ8Hjwl/C28NY +w9TEUcTOxUvFyMZGxsPHQce/yD3IvMk6ybnKOMq3yzbLtsw1zLXNNc21zjbOts83z7jQOdC60TzRvtI/ +0sHTRNPG1EnUy9VO1dHWVdbY11zX4Nhk2OjZbNnx2nba+9uA3AXcit0Q3ZbeHN6i3ynfr+A24L3hROHM +4lPi2+Nj4+vkc+T85YTmDeaW5x/nqegy6LzpRunQ6lvq5etw6/vshu0R7ZzuKO6070DvzPBY8OXxcvH/ +8ozzGfOn9DT0wvVQ9d72bfb794r4Gfio+Tj5x/pX+uf7d/wH/Jj9Kf26/kv+3P9t///SLS4vMFokY2xh +c3NuYW1lWCRjbGFzc2VzXxAQTlNCaXRtYXBJbWFnZVJlcKMvMTJaTlNJbWFnZVJlcFhOU09iamVjdNIt +LjQ1V05TQXJyYXmiNDLSLS43OF5OU011dGFibGVBcnJheaM3NDLTOjsOPD0+V05TV2hpdGVcTlNDb2xv +clNwYWNlRDAgMAAQA4AM0i0uQEFXTlNDb2xvcqJAMtItLkNEV05TSW1hZ2WiQzIACAARABoAJAApADIA +NwBJAEwAUQBTAGIAaAB3AH4AhQCSAJkApQCtALwAvgDAAMUAxwDJAMsAzQDUANkA5ADmAOgA6gDvAPIA +9AD2APgA/wEWATIBNAE2E6YTqxO2E78T0hPWE+ET6hPvE/cT+hP/FA4UEhQZFCEULhQzFDUUNxQ8FEQU +RxRMFFQAAAAAAAACAQAAAAAAAABFAAAAAAAAAAAAAAAAAAAUVw diff --git a/i18n/src/main/resources/cy.lproj/Command.xib b/i18n/src/main/resources/cy.lproj/Command.xib index fd040a3c1a..40c94a26cb 100644 --- a/i18n/src/main/resources/cy.lproj/Command.xib +++ b/i18n/src/main/resources/cy.lproj/Command.xib @@ -1,7 +1,7 @@ - + - + diff --git a/i18n/src/main/resources/cy.lproj/Connection.xib b/i18n/src/main/resources/cy.lproj/Connection.xib index 1f1b0ffbca..cb535359b2 100644 --- a/i18n/src/main/resources/cy.lproj/Connection.xib +++ b/i18n/src/main/resources/cy.lproj/Connection.xib @@ -1,7 +1,7 @@ - + - + @@ -76,7 +76,7 @@ - + diff --git a/i18n/src/main/resources/cy.lproj/Info.xib b/i18n/src/main/resources/cy.lproj/Info.xib index 427ece4d7f..7315fb30b2 100644 --- a/i18n/src/main/resources/cy.lproj/Info.xib +++ b/i18n/src/main/resources/cy.lproj/Info.xib @@ -1,7 +1,7 @@ - + - + @@ -89,7 +89,7 @@ - + @@ -1017,21 +1017,21 @@ - + - + - + @@ -1121,21 +1121,21 @@ - + - + - + @@ -1250,27 +1250,27 @@ diff --git a/i18n/src/main/resources/cy.lproj/Login.strings b/i18n/src/main/resources/cy.lproj/Login.strings index 5a29b7dbd2..5d6ae0e059 100644 Binary files a/i18n/src/main/resources/cy.lproj/Login.strings and b/i18n/src/main/resources/cy.lproj/Login.strings differ diff --git a/i18n/src/main/resources/cy.lproj/Main.xib b/i18n/src/main/resources/cy.lproj/Main.xib index 116bc0ce1a..f49a181ed7 100644 --- a/i18n/src/main/resources/cy.lproj/Main.xib +++ b/i18n/src/main/resources/cy.lproj/Main.xib @@ -1,7 +1,7 @@ - + - + @@ -159,19 +159,19 @@ - + - + - + @@ -321,12 +321,12 @@ CA - + - + @@ -566,6 +566,10 @@ CA - + + + + + diff --git a/i18n/src/main/resources/cy.lproj/Preferences.xib b/i18n/src/main/resources/cy.lproj/Preferences.xib index d1f7e40b55..add92c4c8b 100644 --- a/i18n/src/main/resources/cy.lproj/Preferences.xib +++ b/i18n/src/main/resources/cy.lproj/Preferences.xib @@ -1,7 +1,7 @@ - + - + @@ -266,7 +266,7 @@ - + @@ -307,7 +307,7 @@ - + @@ -538,7 +538,7 @@ - + @@ -578,7 +578,7 @@ - + @@ -613,7 +613,7 @@ - + @@ -642,7 +642,7 @@ - + @@ -1218,7 +1218,7 @@ - + @@ -1250,7 +1250,7 @@ - + @@ -1290,7 +1290,7 @@ - + @@ -1363,7 +1363,7 @@ - + @@ -1558,7 +1558,7 @@ - + @@ -1618,7 +1618,7 @@ - + @@ -1643,7 +1643,7 @@ - + @@ -1685,7 +1685,7 @@ - + @@ -1713,7 +1713,7 @@ - + @@ -1741,7 +1741,7 @@ - + @@ -1770,7 +1770,7 @@ - + @@ -1805,7 +1805,7 @@ - + @@ -1888,7 +1888,7 @@ - + @@ -1947,7 +1947,7 @@ - + @@ -1975,7 +1975,7 @@ - + @@ -2004,7 +2004,7 @@ - + diff --git a/i18n/src/main/resources/cy.lproj/Profiles.xib b/i18n/src/main/resources/cy.lproj/Profiles.xib index 18af5a58f4..20e5ce0f36 100644 --- a/i18n/src/main/resources/cy.lproj/Profiles.xib +++ b/i18n/src/main/resources/cy.lproj/Profiles.xib @@ -1,7 +1,7 @@ - + - + diff --git a/i18n/src/main/resources/cy.lproj/Prompt.xib b/i18n/src/main/resources/cy.lproj/Prompt.xib index 5985378e7f..a08ee44fff 100644 --- a/i18n/src/main/resources/cy.lproj/Prompt.xib +++ b/i18n/src/main/resources/cy.lproj/Prompt.xib @@ -1,7 +1,7 @@ - + - + @@ -95,7 +95,7 @@ Gw - + diff --git a/i18n/src/main/resources/cy.lproj/S3.strings b/i18n/src/main/resources/cy.lproj/S3.strings index 44f7c3a53b..648da7203f 100644 Binary files a/i18n/src/main/resources/cy.lproj/S3.strings and b/i18n/src/main/resources/cy.lproj/S3.strings differ diff --git a/i18n/src/main/resources/cy.lproj/Updater.strings b/i18n/src/main/resources/cy.lproj/Updater.strings index 7d8fe63687..dadc9e95fd 100644 Binary files a/i18n/src/main/resources/cy.lproj/Updater.strings and b/i18n/src/main/resources/cy.lproj/Updater.strings differ diff --git a/i18n/src/main/resources/da.lproj/Bookmark.xib b/i18n/src/main/resources/da.lproj/Bookmark.xib index 1d52608ac0..c65f5c4fec 100644 --- a/i18n/src/main/resources/da.lproj/Bookmark.xib +++ b/i18n/src/main/resources/da.lproj/Bookmark.xib @@ -1,7 +1,7 @@ - + - + @@ -92,7 +92,7 @@ - + @@ -489,95 +489,95 @@ YnBsaXN0MDDUAQIDBAUGBwpYJHZlcnNpb25ZJGFyY2hpdmVyVCR0b3BYJG9iamVjdHMSAAGGoF8QD05T -S2V5ZWRBcmNoaXZlctEICVRyb290gAGuCwwZGh8UJCkqMTQ3PUBVJG51bGzWDQ4PEBESExQVFhcYVk5T -U2l6ZV5OU1Jlc2l6aW5nTW9kZVYkY2xhc3NcTlNJbWFnZUZsYWdzVk5TUmVwc1dOU0NvbG9ygAIQAIAN -EiDDAACAA4ALVnsxLCAxfdIbDxweWk5TLm9iamVjdHOhHYAEgArSGw8gI6IhIoAFgAaACdMPJSYnKBRf -EBROU1RJRkZSZXByZXNlbnRhdGlvbl8QGU5TSW50ZXJuYWxMYXlvdXREaXJlY3Rpb26ACIAHTxESbE1N -ACoAAAAKAAAAEAEAAAMAAAABAAEAAAEBAAMAAAABAAEAAAECAAMAAAACAAgACAEDAAMAAAABAAEAAAEG -AAMAAAABAAEAAAEKAAMAAAABAAEAAAERAAQAAAABAAAACAESAAMAAAABAAEAAAEVAAMAAAABAAIAAAEW -AAMAAAABAAEAAAEXAAQAAAABAAAAAgEcAAMAAAABAAEAAAEoAAMAAAABAAIAAAFSAAMAAAABAAEAAAFT -AAMAAAACAAEAAYdzAAcAABGcAAAA0AAAAAAAABGcYXBwbAIAAABtbnRyR1JBWVhZWiAH3AAIABcADwAu -AA9hY3NwQVBQTAAAAABub25lAAAAAAAAAAAAAAAAAAAAAAAA9tYAAQAAAADTLWFwcGwAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAVkZXNjAAAAwAAAAHlkc2NtAAABPAAA -CBpjcHJ0AAAJWAAAACN3dHB0AAAJfAAAABRrVFJDAAAJkAAACAxkZXNjAAAAAAAAAB9HZW5lcmljIEdy -YXkgR2FtbWEgMi4yIFByb2ZpbGUAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAbWx1YwAAAAAAAAAfAAAADHNr -U0sAAAAuAAABhGRhREsAAAA6AAABsmNhRVMAAAA4AAAB7HZpVk4AAABAAAACJHB0QlIAAABKAAACZHVr -VUEAAAAsAAACrmZyRlUAAAA+AAAC2mh1SFUAAAA0AAADGHpoVFcAAAAaAAADTGtvS1IAAAAiAAADZm5i -Tk8AAAA6AAADiGNzQ1oAAAAoAAADwmhlSUwAAAAkAAAD6nJvUk8AAAAqAAAEDmRlREUAAABOAAAEOGl0 -SVQAAABOAAAEhnN2U0UAAAA4AAAE1HpoQ04AAAAaAAAFDGphSlAAAAAmAAAFJmVsR1IAAAAqAAAFTHB0 -UE8AAABSAAAFdm5sTkwAAABAAAAFyGVzRVMAAABMAAAGCHRoVEgAAAAyAAAGVHRyVFIAAAAkAAAGhmZp -RkkAAABGAAAGqmhySFIAAAA+AAAG8HBsUEwAAABKAAAHLmFyRUcAAAAsAAAHeHJ1UlUAAAA6AAAHpGVu -VVMAAAA8AAAH3gBWAWEAZQBvAGIAZQBjAG4A4QAgAHMAaQB2AOEAIABnAGEAbQBhACAAMgAsADIARwBl -AG4AZQByAGkAcwBrACAAZwByAOUAIAAyACwAMgAgAGcAYQBtAG0AYQAtAHAAcgBvAGYAaQBsAEcAYQBt -AG0AYQAgAGQAZQAgAGcAcgBpAHMAbwBzACAAZwBlAG4A6AByAGkAYwBhACAAMgAuADIAQx6lAHUAIABo -AOwAbgBoACAATQDgAHUAIAB4AOEAbQAgAEMAaAB1AG4AZwAgAEcAYQBtAG0AYQAgADIALgAyAFAAZQBy -AGYAaQBsACAARwBlAG4A6QByAGkAYwBvACAAZABhACAARwBhAG0AYQAgAGQAZQAgAEMAaQBuAHoAYQBz -ACAAMgAsADIEFwQwBDMEMAQ7BEwEPQQwACAARwByAGEAeQAtBDMEMAQ8BDAAIAAyAC4AMgBQAHIAbwBm -AGkAbAAgAGcA6QBuAOkAcgBpAHEAdQBlACAAZwByAGkAcwAgAGcAYQBtAG0AYQAgADIALAAyAMEAbAB0 -AGEAbADhAG4AbwBzACAAcwB6APwAcgBrAGUAIABnAGEAbQBtAGEAIAAyAC4AMpAadShwcJaOUUlepgAy -AC4AMoJyX2ljz4/wx3y8GAAg1ozAyQAgrBC5yAAgADIALgAyACDVBLhc0wzHfABHAGUAbgBlAHIAaQBz -AGsAIABnAHIA5QAgAGcAYQBtAG0AYQAgADIALAAyAC0AcAByAG8AZgBpAGwATwBiAGUAYwBuAOEAIAFh -AGUAZADhACAAZwBhAG0AYQAgADIALgAyBdIF0AXeBdQAIAXQBeQF1QXoACAF2wXcBdwF2QAgADIALgAy -AEcAYQBtAGEAIABnAHIAaQAgAGcAZQBuAGUAcgBpAGMBAwAgADIALAAyAEEAbABsAGcAZQBtAGUAaQBu -AGUAcwAgAEcAcgBhAHUAcwB0AHUAZgBlAG4ALQBQAHIAbwBmAGkAbAAgAEcAYQBtAG0AYQAgADIALAAy -AFAAcgBvAGYAaQBsAG8AIABnAHIAaQBnAGkAbwAgAGcAZQBuAGUAcgBpAGMAbwAgAGQAZQBsAGwAYQAg -AGcAYQBtAG0AYQAgADIALAAyAEcAZQBuAGUAcgBpAHMAawAgAGcAcgDlACAAMgAsADIAIABnAGEAbQBt -AGEAcAByAG8AZgBpAGxmbpAacHBepnz7ZXAAMgAuADJjz4/wZYdO9k4AgiwwsDDsMKQwrDDzMN4AIAAy -AC4AMgAgMNcw7TDVMKEwpDDrA5MDtQO9A7kDugPMACADkwO6A8EDuQAgA5MDrAO8A7wDsQAgADIALgAy -AFAAZQByAGYAaQBsACAAZwBlAG4A6QByAGkAYwBvACAAZABlACAAYwBpAG4AegBlAG4AdABvAHMAIABk -AGEAIABHAGEAbQBtAGEAIAAyACwAMgBBAGwAZwBlAG0AZQBlAG4AIABnAHIAaQBqAHMAIABnAGEAbQBt -AGEAIAAyACwAMgAtAHAAcgBvAGYAaQBlAGwAUABlAHIAZgBpAGwAIABnAGUAbgDpAHIAaQBjAG8AIABk -AGUAIABnAGEAbQBtAGEAIABkAGUAIABnAHIAaQBzAGUAcwAgADIALAAyDiMOMQ4HDioONQ5BDgEOIQ4h -DjIOQA4BDiMOIg5MDhcOMQ5IDicORA4bACAAMgAuADIARwBlAG4AZQBsACAARwByAGkAIABHAGEAbQBh -ACAAMgAsADIAWQBsAGUAaQBuAGUAbgAgAGgAYQByAG0AYQBhAG4AIABnAGEAbQBtAGEAIAAyACwAMgAg -AC0AcAByAG8AZgBpAGkAbABpAEcAZQBuAGUAcgBpAQ0AawBpACAARwByAGEAeQAgAEcAYQBtAG0AYQAg -ADIALgAyACAAcAByAG8AZgBpAGwAVQBuAGkAdwBlAHIAcwBhAGwAbgB5ACAAcAByAG8AZgBpAGwAIABz -AHoAYQByAG8BWwBjAGkAIABnAGEAbQBtAGEAIAAyACwAMgY6BicGRQYnACAAMgAuADIAIAZEBkgGRgAg -BjEGRQYnBi8GSgAgBjkGJwZFBB4EMQRJBDAETwAgBEEENQRABDAETwAgBDMEMAQ8BDwEMAAgADIALAAy -AC0EPwRABD4ERAQ4BDsETABHAGUAbgBlAHIAaQBjACAARwByAGEAeQAgAEcAYQBtAG0AYQAgADIALgAy -ACAAUAByAG8AZgBpAGwAZQAAdGV4dAAAAABDb3B5cmlnaHQgQXBwbGUgSW5jLiwgMjAxMgAAWFlaIAAA -AAAAAPNRAAEAAAABFsxjdXJ2AAAAAAAABAAAAAAFAAoADwAUABkAHgAjACgALQAyADcAOwBAAEUASgBP -AFQAWQBeAGMAaABtAHIAdwB8AIEAhgCLAJAAlQCaAJ8ApACpAK4AsgC3ALwAwQDGAMsA0ADVANsA4ADl -AOsA8AD2APsBAQEHAQ0BEwEZAR8BJQErATIBOAE+AUUBTAFSAVkBYAFnAW4BdQF8AYMBiwGSAZoBoQGp -AbEBuQHBAckB0QHZAeEB6QHyAfoCAwIMAhQCHQImAi8COAJBAksCVAJdAmcCcQJ6AoQCjgKYAqICrAK2 -AsECywLVAuAC6wL1AwADCwMWAyEDLQM4A0MDTwNaA2YDcgN+A4oDlgOiA64DugPHA9MD4APsA/kEBgQT -BCAELQQ7BEgEVQRjBHEEfgSMBJoEqAS2BMQE0wThBPAE/gUNBRwFKwU6BUkFWAVnBXcFhgWWBaYFtQXF -BdUF5QX2BgYGFgYnBjcGSAZZBmoGewaMBp0GrwbABtEG4wb1BwcHGQcrBz0HTwdhB3QHhgeZB6wHvwfS -B+UH+AgLCB8IMghGCFoIbgiCCJYIqgi+CNII5wj7CRAJJQk6CU8JZAl5CY8JpAm6Cc8J5Qn7ChEKJwo9 -ClQKagqBCpgKrgrFCtwK8wsLCyILOQtRC2kLgAuYC7ALyAvhC/kMEgwqDEMMXAx1DI4MpwzADNkM8w0N -DSYNQA1aDXQNjg2pDcMN3g34DhMOLg5JDmQOfw6bDrYO0g7uDwkPJQ9BD14Peg+WD7MPzw/sEAkQJhBD -EGEQfhCbELkQ1xD1ERMRMRFPEW0RjBGqEckR6BIHEiYSRRJkEoQSoxLDEuMTAxMjE0MTYxODE6QTxRPl -FAYUJxRJFGoUixStFM4U8BUSFTQVVhV4FZsVvRXgFgMWJhZJFmwWjxayFtYW+hcdF0EXZReJF64X0hf3 -GBsYQBhlGIoYrxjVGPoZIBlFGWsZkRm3Gd0aBBoqGlEadxqeGsUa7BsUGzsbYxuKG7Ib2hwCHCocUhx7 -HKMczBz1HR4dRx1wHZkdwx3sHhYeQB5qHpQevh7pHxMfPh9pH5Qfvx/qIBUgQSBsIJggxCDwIRwhSCF1 -IaEhziH7IiciVSKCIq8i3SMKIzgjZiOUI8Ij8CQfJE0kfCSrJNolCSU4JWgllyXHJfcmJyZXJocmtybo -JxgnSSd6J6sn3CgNKD8ocSiiKNQpBik4KWspnSnQKgIqNSpoKpsqzysCKzYraSudK9EsBSw5LG4soizX -LQwtQS12Last4S4WLkwugi63Lu4vJC9aL5Evxy/+MDUwbDCkMNsxEjFKMYIxujHyMioyYzKbMtQzDTNG -M38zuDPxNCs0ZTSeNNg1EzVNNYc1wjX9Njc2cjauNuk3JDdgN5w31zgUOFA4jDjIOQU5Qjl/Obw5+To2 -OnQ6sjrvOy07azuqO+g8JzxlPKQ84z0iPWE9oT3gPiA+YD6gPuA/IT9hP6I/4kAjQGRApkDnQSlBakGs -Qe5CMEJyQrVC90M6Q31DwEQDREdEikTORRJFVUWaRd5GIkZnRqtG8Ec1R3tHwEgFSEtIkUjXSR1JY0mp -SfBKN0p9SsRLDEtTS5pL4kwqTHJMuk0CTUpNk03cTiVObk63TwBPSU+TT91QJ1BxULtRBlFQUZtR5lIx -UnxSx1MTU19TqlP2VEJUj1TbVShVdVXCVg9WXFapVvdXRFeSV+BYL1h9WMtZGllpWbhaB1pWWqZa9VtF -W5Vb5Vw1XIZc1l0nXXhdyV4aXmxevV8PX2Ffs2AFYFdgqmD8YU9homH1YklinGLwY0Njl2PrZEBklGTp -ZT1lkmXnZj1mkmboZz1nk2fpaD9olmjsaUNpmmnxakhqn2r3a09rp2v/bFdsr20IbWBtuW4SbmtuxG8e -b3hv0XArcIZw4HE6cZVx8HJLcqZzAXNdc7h0FHRwdMx1KHWFdeF2Pnabdvh3VnezeBF4bnjMeSp5iXnn -ekZ6pXsEe2N7wnwhfIF84X1BfaF+AX5ifsJ/I3+Ef+WAR4CogQqBa4HNgjCCkoL0g1eDuoQdhICE44VH -hauGDoZyhteHO4efiASIaYjOiTOJmYn+imSKyoswi5aL/IxjjMqNMY2Yjf+OZo7OjzaPnpAGkG6Q1pE/ -kaiSEZJ6kuOTTZO2lCCUipT0lV+VyZY0lp+XCpd1l+CYTJi4mSSZkJn8mmia1ZtCm6+cHJyJnPedZJ3S -nkCerp8dn4uf+qBpoNihR6G2oiailqMGo3aj5qRWpMelOKWpphqmi6b9p26n4KhSqMSpN6mpqhyqj6sC -q3Wr6axcrNCtRK24ri2uoa8Wr4uwALB1sOqxYLHWskuywrM4s660JbSctRO1irYBtnm28Ldot+C4WbjR -uUq5wro7urW7LrunvCG8m70VvY++Cr6Evv+/er/1wHDA7MFnwePCX8Lbw1jD1MRRxM7FS8XIxkbGw8dB -x7/IPci8yTrJuco4yrfLNsu2zDXMtc01zbXONs62zzfPuNA50LrRPNG+0j/SwdNE08bUSdTL1U7V0dZV -1tjXXNfg2GTY6Nls2fHadtr724DcBdyK3RDdlt4c3qLfKd+v4DbgveFE4cziU+Lb42Pj6+Rz5PzlhOYN -5pbnH+ep6DLovOlG6dDqW+rl63Dr++yG7RHtnO4o7rTvQO/M8Fjw5fFy8f/yjPMZ86f0NPTC9VD13vZt -9vv3ivgZ+Kj5OPnH+lf65/t3/Af8mP0p/br+S/7c/23//9IrLC0uWiRjbGFzc25hbWVYJGNsYXNzZXNf -EBBOU0JpdG1hcEltYWdlUmVwoy0vMFpOU0ltYWdlUmVwWE5TT2JqZWN00issMjNXTlNBcnJheaIyMNIr -LDU2Xk5TTXV0YWJsZUFycmF5ozUyMNM4OQ86OzxXTlNXaGl0ZVxOU0NvbG9yU3BhY2VEMCAwABADgAzS -Kyw+P1dOU0NvbG9yoj4w0issQUJXTlNJbWFnZaJBMAAIABEAGgAkACkAMgA3AEkATABRAFMAYgBoAHUA -fACLAJIAnwCmAK4AsACyALQAuQC7AL0AxADJANQA1gDYANoA3wDiAOQA5gDoAO8BBgEiASQBJhOWE5sT -phOvE8ITxhPRE9oT3xPnE+oT7xP+FAIUCRQRFB4UIxQlFCcULBQ0FDcUPBREAAAAAAAAAgEAAAAAAAAA -QwAAAAAAAAAAAAAAAAAAFEc +S2V5ZWRBcmNoaXZlctEICVRyb290gAGuCwwbHCEaJissMzY5P0JVJG51bGzXDQ4PEBESExQVFhcYGRpW +TlNTaXplViRjbGFzc1xOU0ltYWdlRmxhZ3NWTlNSZXBzW05TVGludENvbG9yV05TQ29sb3JeTlNSZXNp +emluZ01vZGWAAoANEiDDAACAA4AAgAsQAFZ7MSwgMX3SHQ4eIFpOUy5vYmplY3RzoR+ABIAK0h0OIiWi +IySABYAGgAnTDicoKSoaXxAUTlNUSUZGUmVwcmVzZW50YXRpb25fEBlOU0ludGVybmFsTGF5b3V0RGly +ZWN0aW9ugAiAB08REmxNTQAqAAAACgAAABABAAADAAAAAQABAAABAQADAAAAAQABAAABAgADAAAAAgAI +AAgBAwADAAAAAQABAAABBgADAAAAAQABAAABCgADAAAAAQABAAABEQAEAAAAAQAAAAgBEgADAAAAAQAB +AAABFQADAAAAAQACAAABFgADAAAAAQABAAABFwAEAAAAAQAAAAIBHAADAAAAAQABAAABKAADAAAAAQAC +AAABUgADAAAAAQABAAABUwADAAAAAgABAAGHcwAHAAARnAAAANAAAAAAAAARnGFwcGwCAAAAbW50ckdS +QVlYWVogB9wACAAXAA8ALgAPYWNzcEFQUEwAAAAAbm9uZQAAAAAAAAAAAAAAAAAAAAAAAPbWAAEAAAAA +0y1hcHBsAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFZGVzYwAA +AMAAAAB5ZHNjbQAAATwAAAgaY3BydAAACVgAAAAjd3RwdAAACXwAAAAUa1RSQwAACZAAAAgMZGVzYwAA +AAAAAAAfR2VuZXJpYyBHcmF5IEdhbW1hIDIuMiBQcm9maWxlAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAG1s +dWMAAAAAAAAAHwAAAAxza1NLAAAALgAAAYRkYURLAAAAOgAAAbJjYUVTAAAAOAAAAex2aVZOAAAAQAAA +AiRwdEJSAAAASgAAAmR1a1VBAAAALAAAAq5mckZVAAAAPgAAAtpodUhVAAAANAAAAxh6aFRXAAAAGgAA +A0xrb0tSAAAAIgAAA2ZuYk5PAAAAOgAAA4hjc0NaAAAAKAAAA8JoZUlMAAAAJAAAA+pyb1JPAAAAKgAA +BA5kZURFAAAATgAABDhpdElUAAAATgAABIZzdlNFAAAAOAAABNR6aENOAAAAGgAABQxqYUpQAAAAJgAA +BSZlbEdSAAAAKgAABUxwdFBPAAAAUgAABXZubE5MAAAAQAAABchlc0VTAAAATAAABgh0aFRIAAAAMgAA +BlR0clRSAAAAJAAABoZmaUZJAAAARgAABqpockhSAAAAPgAABvBwbFBMAAAASgAABy5hckVHAAAALAAA +B3hydVJVAAAAOgAAB6RlblVTAAAAPAAAB94AVgFhAGUAbwBiAGUAYwBuAOEAIABzAGkAdgDhACAAZwBh +AG0AYQAgADIALAAyAEcAZQBuAGUAcgBpAHMAawAgAGcAcgDlACAAMgAsADIAIABnAGEAbQBtAGEALQBw +AHIAbwBmAGkAbABHAGEAbQBtAGEAIABkAGUAIABnAHIAaQBzAG8AcwAgAGcAZQBuAOgAcgBpAGMAYQAg +ADIALgAyAEMepQB1ACAAaADsAG4AaAAgAE0A4AB1ACAAeADhAG0AIABDAGgAdQBuAGcAIABHAGEAbQBt +AGEAIAAyAC4AMgBQAGUAcgBmAGkAbAAgAEcAZQBuAOkAcgBpAGMAbwAgAGQAYQAgAEcAYQBtAGEAIABk +AGUAIABDAGkAbgB6AGEAcwAgADIALAAyBBcEMAQzBDAEOwRMBD0EMAAgAEcAcgBhAHkALQQzBDAEPAQw +ACAAMgAuADIAUAByAG8AZgBpAGwAIABnAOkAbgDpAHIAaQBxAHUAZQAgAGcAcgBpAHMAIABnAGEAbQBt +AGEAIAAyACwAMgDBAGwAdABhAGwA4QBuAG8AcwAgAHMAegD8AHIAawBlACAAZwBhAG0AbQBhACAAMgAu +ADKQGnUocHCWjlFJXqYAMgAuADKCcl9pY8+P8Md8vBgAINaMwMkAIKwQucgAIAAyAC4AMgAg1QS4XNMM +x3wARwBlAG4AZQByAGkAcwBrACAAZwByAOUAIABnAGEAbQBtAGEAIAAyACwAMgAtAHAAcgBvAGYAaQBs +AE8AYgBlAGMAbgDhACABYQBlAGQA4QAgAGcAYQBtAGEAIAAyAC4AMgXSBdAF3gXUACAF0AXkBdUF6AAg +BdsF3AXcBdkAIAAyAC4AMgBHAGEAbQBhACAAZwByAGkAIABnAGUAbgBlAHIAaQBjAQMAIAAyACwAMgBB +AGwAbABnAGUAbQBlAGkAbgBlAHMAIABHAHIAYQB1AHMAdAB1AGYAZQBuAC0AUAByAG8AZgBpAGwAIABH +AGEAbQBtAGEAIAAyACwAMgBQAHIAbwBmAGkAbABvACAAZwByAGkAZwBpAG8AIABnAGUAbgBlAHIAaQBj +AG8AIABkAGUAbABsAGEAIABnAGEAbQBtAGEAIAAyACwAMgBHAGUAbgBlAHIAaQBzAGsAIABnAHIA5QAg +ADIALAAyACAAZwBhAG0AbQBhAHAAcgBvAGYAaQBsZm6QGnBwXqZ8+2VwADIALgAyY8+P8GWHTvZOAIIs +MLAw7DCkMKww8zDeACAAMgAuADIAIDDXMO0w1TChMKQw6wOTA7UDvQO5A7oDzAAgA5MDugPBA7kAIAOT +A6wDvAO8A7EAIAAyAC4AMgBQAGUAcgBmAGkAbAAgAGcAZQBuAOkAcgBpAGMAbwAgAGQAZQAgAGMAaQBu +AHoAZQBuAHQAbwBzACAAZABhACAARwBhAG0AbQBhACAAMgAsADIAQQBsAGcAZQBtAGUAZQBuACAAZwBy +AGkAagBzACAAZwBhAG0AbQBhACAAMgAsADIALQBwAHIAbwBmAGkAZQBsAFAAZQByAGYAaQBsACAAZwBl +AG4A6QByAGkAYwBvACAAZABlACAAZwBhAG0AbQBhACAAZABlACAAZwByAGkAcwBlAHMAIAAyACwAMg4j +DjEOBw4qDjUOQQ4BDiEOIQ4yDkAOAQ4jDiIOTA4XDjEOSA4nDkQOGwAgADIALgAyAEcAZQBuAGUAbAAg +AEcAcgBpACAARwBhAG0AYQAgADIALAAyAFkAbABlAGkAbgBlAG4AIABoAGEAcgBtAGEAYQBuACAAZwBh +AG0AbQBhACAAMgAsADIAIAAtAHAAcgBvAGYAaQBpAGwAaQBHAGUAbgBlAHIAaQENAGsAaQAgAEcAcgBh +AHkAIABHAGEAbQBtAGEAIAAyAC4AMgAgAHAAcgBvAGYAaQBsAFUAbgBpAHcAZQByAHMAYQBsAG4AeQAg +AHAAcgBvAGYAaQBsACAAcwB6AGEAcgBvAVsAYwBpACAAZwBhAG0AbQBhACAAMgAsADIGOgYnBkUGJwAg +ADIALgAyACAGRAZIBkYAIAYxBkUGJwYvBkoAIAY5BicGRQQeBDEESQQwBE8AIARBBDUEQAQwBE8AIAQz +BDAEPAQ8BDAAIAAyACwAMgAtBD8EQAQ+BEQEOAQ7BEwARwBlAG4AZQByAGkAYwAgAEcAcgBhAHkAIABH +AGEAbQBtAGEAIAAyAC4AMgAgAFAAcgBvAGYAaQBsAGUAAHRleHQAAAAAQ29weXJpZ2h0IEFwcGxlIElu +Yy4sIDIwMTIAAFhZWiAAAAAAAADzUQABAAAAARbMY3VydgAAAAAAAAQAAAAABQAKAA8AFAAZAB4AIwAo +AC0AMgA3ADsAQABFAEoATwBUAFkAXgBjAGgAbQByAHcAfACBAIYAiwCQAJUAmgCfAKQAqQCuALIAtwC8 +AMEAxgDLANAA1QDbAOAA5QDrAPAA9gD7AQEBBwENARMBGQEfASUBKwEyATgBPgFFAUwBUgFZAWABZwFu +AXUBfAGDAYsBkgGaAaEBqQGxAbkBwQHJAdEB2QHhAekB8gH6AgMCDAIUAh0CJgIvAjgCQQJLAlQCXQJn +AnECegKEAo4CmAKiAqwCtgLBAssC1QLgAusC9QMAAwsDFgMhAy0DOANDA08DWgNmA3IDfgOKA5YDogOu +A7oDxwPTA+AD7AP5BAYEEwQgBC0EOwRIBFUEYwRxBH4EjASaBKgEtgTEBNME4QTwBP4FDQUcBSsFOgVJ +BVgFZwV3BYYFlgWmBbUFxQXVBeUF9gYGBhYGJwY3BkgGWQZqBnsGjAadBq8GwAbRBuMG9QcHBxkHKwc9 +B08HYQd0B4YHmQesB78H0gflB/gICwgfCDIIRghaCG4IggiWCKoIvgjSCOcI+wkQCSUJOglPCWQJeQmP +CaQJugnPCeUJ+woRCicKPQpUCmoKgQqYCq4KxQrcCvMLCwsiCzkLUQtpC4ALmAuwC8gL4Qv5DBIMKgxD +DFwMdQyODKcMwAzZDPMNDQ0mDUANWg10DY4NqQ3DDd4N+A4TDi4OSQ5kDn8Omw62DtIO7g8JDyUPQQ9e +D3oPlg+zD88P7BAJECYQQxBhEH4QmxC5ENcQ9RETETERTxFtEYwRqhHJEegSBxImEkUSZBKEEqMSwxLj +EwMTIxNDE2MTgxOkE8UT5RQGFCcUSRRqFIsUrRTOFPAVEhU0FVYVeBWbFb0V4BYDFiYWSRZsFo8WshbW +FvoXHRdBF2UXiReuF9IX9xgbGEAYZRiKGK8Y1Rj6GSAZRRlrGZEZtxndGgQaKhpRGncanhrFGuwbFBs7 +G2MbihuyG9ocAhwqHFIcexyjHMwc9R0eHUcdcB2ZHcMd7B4WHkAeah6UHr4e6R8THz4faR+UH78f6iAV +IEEgbCCYIMQg8CEcIUghdSGhIc4h+yInIlUigiKvIt0jCiM4I2YjlCPCI/AkHyRNJHwkqyTaJQklOCVo +JZclxyX3JicmVyaHJrcm6CcYJ0kneierJ9woDSg/KHEooijUKQYpOClrKZ0p0CoCKjUqaCqbKs8rAis2 +K2krnSvRLAUsOSxuLKIs1y0MLUEtdi2rLeEuFi5MLoIuty7uLyQvWi+RL8cv/jA1MGwwpDDbMRIxSjGC +Mbox8jIqMmMymzLUMw0zRjN/M7gz8TQrNGU0njTYNRM1TTWHNcI1/TY3NnI2rjbpNyQ3YDecN9c4FDhQ +OIw4yDkFOUI5fzm8Ofk6Njp0OrI67zstO2s7qjvoPCc8ZTykPOM9Ij1hPaE94D4gPmA+oD7gPyE/YT+i +P+JAI0BkQKZA50EpQWpBrEHuQjBCckK1QvdDOkN9Q8BEA0RHRIpEzkUSRVVFmkXeRiJGZ0arRvBHNUd7 +R8BIBUhLSJFI10kdSWNJqUnwSjdKfUrESwxLU0uaS+JMKkxyTLpNAk1KTZNN3E4lTm5Ot08AT0lPk0/d +UCdQcVC7UQZRUFGbUeZSMVJ8UsdTE1NfU6pT9lRCVI9U21UoVXVVwlYPVlxWqVb3V0RXklfgWC9YfVjL +WRpZaVm4WgdaVlqmWvVbRVuVW+VcNVyGXNZdJ114XcleGl5sXr1fD19hX7NgBWBXYKpg/GFPYaJh9WJJ +Ypxi8GNDY5dj62RAZJRk6WU9ZZJl52Y9ZpJm6Gc9Z5Nn6Wg/aJZo7GlDaZpp8WpIap9q92tPa6dr/2xX +bK9tCG1gbbluEm5rbsRvHm94b9FwK3CGcOBxOnGVcfByS3KmcwFzXXO4dBR0cHTMdSh1hXXhdj52m3b4 +d1Z3s3gReG54zHkqeYl553pGeqV7BHtje8J8IXyBfOF9QX2hfgF+Yn7CfyN/hH/lgEeAqIEKgWuBzYIw +gpKC9INXg7qEHYSAhOOFR4Wrhg6GcobXhzuHn4gEiGmIzokziZmJ/opkisqLMIuWi/yMY4zKjTGNmI3/ +jmaOzo82j56QBpBukNaRP5GokhGSepLjk02TtpQglIqU9JVflcmWNJaflwqXdZfgmEyYuJkkmZCZ/Jpo +mtWbQpuvnByciZz3nWSd0p5Anq6fHZ+Ln/qgaaDYoUehtqImopajBqN2o+akVqTHpTilqaYapoum/adu +p+CoUqjEqTepqaocqo+rAqt1q+msXKzQrUStuK4trqGvFq+LsACwdbDqsWCx1rJLssKzOLOutCW0nLUT +tYq2AbZ5tvC3aLfguFm40blKucK6O7q1uy67p7whvJu9Fb2Pvgq+hL7/v3q/9cBwwOzBZ8Hjwl/C28NY +w9TEUcTOxUvFyMZGxsPHQce/yD3IvMk6ybnKOMq3yzbLtsw1zLXNNc21zjbOts83z7jQOdC60TzRvtI/ +0sHTRNPG1EnUy9VO1dHWVdbY11zX4Nhk2OjZbNnx2nba+9uA3AXcit0Q3ZbeHN6i3ynfr+A24L3hROHM +4lPi2+Nj4+vkc+T85YTmDeaW5x/nqegy6LzpRunQ6lvq5etw6/vshu0R7ZzuKO6070DvzPBY8OXxcvH/ +8ozzGfOn9DT0wvVQ9d72bfb794r4Gfio+Tj5x/pX+uf7d/wH/Jj9Kf26/kv+3P9t///SLS4vMFokY2xh +c3NuYW1lWCRjbGFzc2VzXxAQTlNCaXRtYXBJbWFnZVJlcKMvMTJaTlNJbWFnZVJlcFhOU09iamVjdNIt +LjQ1V05TQXJyYXmiNDLSLS43OF5OU011dGFibGVBcnJheaM3NDLTOjsOPD0+V05TV2hpdGVcTlNDb2xv +clNwYWNlRDAgMAAQA4AM0i0uQEFXTlNDb2xvcqJAMtItLkNEV05TSW1hZ2WiQzIACAARABoAJAApADIA +NwBJAEwAUQBTAGIAaAB3AH4AhQCSAJkApQCtALwAvgDAAMUAxwDJAMsAzQDUANkA5ADmAOgA6gDvAPIA +9AD2APgA/wEWATIBNAE2E6YTqxO2E78T0hPWE+ET6hPvE/cT+hP/FA4UEhQZFCEULhQzFDUUNxQ8FEQU +RxRMFFQAAAAAAAACAQAAAAAAAABFAAAAAAAAAAAAAAAAAAAUVw diff --git a/i18n/src/main/resources/da.lproj/Browser.xib b/i18n/src/main/resources/da.lproj/Browser.xib index 99045cb68a..34b9d76cc4 100644 --- a/i18n/src/main/resources/da.lproj/Browser.xib +++ b/i18n/src/main/resources/da.lproj/Browser.xib @@ -1,7 +1,7 @@ - + - + @@ -38,7 +38,7 @@ - + @@ -62,27 +62,27 @@ @@ -256,7 +256,7 @@ - + @@ -553,95 +553,95 @@ DQ YnBsaXN0MDDUAQIDBAUGBwpYJHZlcnNpb25ZJGFyY2hpdmVyVCR0b3BYJG9iamVjdHMSAAGGoF8QD05T -S2V5ZWRBcmNoaXZlctEICVRyb290gAGuCwwZGh8UJCkqMTQ3PUBVJG51bGzWDQ4PEBESExQVFhcYVk5T -U2l6ZV5OU1Jlc2l6aW5nTW9kZVYkY2xhc3NcTlNJbWFnZUZsYWdzVk5TUmVwc1dOU0NvbG9ygAIQAIAN -EiDDAACAA4ALVnsxLCAxfdIbDxweWk5TLm9iamVjdHOhHYAEgArSGw8gI6IhIoAFgAaACdMPJSYnKBRf -EBROU1RJRkZSZXByZXNlbnRhdGlvbl8QGU5TSW50ZXJuYWxMYXlvdXREaXJlY3Rpb26ACIAHTxESbE1N -ACoAAAAKAAAAEAEAAAMAAAABAAEAAAEBAAMAAAABAAEAAAECAAMAAAACAAgACAEDAAMAAAABAAEAAAEG -AAMAAAABAAEAAAEKAAMAAAABAAEAAAERAAQAAAABAAAACAESAAMAAAABAAEAAAEVAAMAAAABAAIAAAEW -AAMAAAABAAEAAAEXAAQAAAABAAAAAgEcAAMAAAABAAEAAAEoAAMAAAABAAIAAAFSAAMAAAABAAEAAAFT -AAMAAAACAAEAAYdzAAcAABGcAAAA0AAAAAAAABGcYXBwbAIAAABtbnRyR1JBWVhZWiAH3AAIABcADwAu -AA9hY3NwQVBQTAAAAABub25lAAAAAAAAAAAAAAAAAAAAAAAA9tYAAQAAAADTLWFwcGwAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAVkZXNjAAAAwAAAAHlkc2NtAAABPAAA -CBpjcHJ0AAAJWAAAACN3dHB0AAAJfAAAABRrVFJDAAAJkAAACAxkZXNjAAAAAAAAAB9HZW5lcmljIEdy -YXkgR2FtbWEgMi4yIFByb2ZpbGUAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAbWx1YwAAAAAAAAAfAAAADHNr -U0sAAAAuAAABhGRhREsAAAA6AAABsmNhRVMAAAA4AAAB7HZpVk4AAABAAAACJHB0QlIAAABKAAACZHVr -VUEAAAAsAAACrmZyRlUAAAA+AAAC2mh1SFUAAAA0AAADGHpoVFcAAAAaAAADTGtvS1IAAAAiAAADZm5i -Tk8AAAA6AAADiGNzQ1oAAAAoAAADwmhlSUwAAAAkAAAD6nJvUk8AAAAqAAAEDmRlREUAAABOAAAEOGl0 -SVQAAABOAAAEhnN2U0UAAAA4AAAE1HpoQ04AAAAaAAAFDGphSlAAAAAmAAAFJmVsR1IAAAAqAAAFTHB0 -UE8AAABSAAAFdm5sTkwAAABAAAAFyGVzRVMAAABMAAAGCHRoVEgAAAAyAAAGVHRyVFIAAAAkAAAGhmZp -RkkAAABGAAAGqmhySFIAAAA+AAAG8HBsUEwAAABKAAAHLmFyRUcAAAAsAAAHeHJ1UlUAAAA6AAAHpGVu -VVMAAAA8AAAH3gBWAWEAZQBvAGIAZQBjAG4A4QAgAHMAaQB2AOEAIABnAGEAbQBhACAAMgAsADIARwBl -AG4AZQByAGkAcwBrACAAZwByAOUAIAAyACwAMgAgAGcAYQBtAG0AYQAtAHAAcgBvAGYAaQBsAEcAYQBt -AG0AYQAgAGQAZQAgAGcAcgBpAHMAbwBzACAAZwBlAG4A6AByAGkAYwBhACAAMgAuADIAQx6lAHUAIABo -AOwAbgBoACAATQDgAHUAIAB4AOEAbQAgAEMAaAB1AG4AZwAgAEcAYQBtAG0AYQAgADIALgAyAFAAZQBy -AGYAaQBsACAARwBlAG4A6QByAGkAYwBvACAAZABhACAARwBhAG0AYQAgAGQAZQAgAEMAaQBuAHoAYQBz -ACAAMgAsADIEFwQwBDMEMAQ7BEwEPQQwACAARwByAGEAeQAtBDMEMAQ8BDAAIAAyAC4AMgBQAHIAbwBm -AGkAbAAgAGcA6QBuAOkAcgBpAHEAdQBlACAAZwByAGkAcwAgAGcAYQBtAG0AYQAgADIALAAyAMEAbAB0 -AGEAbADhAG4AbwBzACAAcwB6APwAcgBrAGUAIABnAGEAbQBtAGEAIAAyAC4AMpAadShwcJaOUUlepgAy -AC4AMoJyX2ljz4/wx3y8GAAg1ozAyQAgrBC5yAAgADIALgAyACDVBLhc0wzHfABHAGUAbgBlAHIAaQBz -AGsAIABnAHIA5QAgAGcAYQBtAG0AYQAgADIALAAyAC0AcAByAG8AZgBpAGwATwBiAGUAYwBuAOEAIAFh -AGUAZADhACAAZwBhAG0AYQAgADIALgAyBdIF0AXeBdQAIAXQBeQF1QXoACAF2wXcBdwF2QAgADIALgAy -AEcAYQBtAGEAIABnAHIAaQAgAGcAZQBuAGUAcgBpAGMBAwAgADIALAAyAEEAbABsAGcAZQBtAGUAaQBu -AGUAcwAgAEcAcgBhAHUAcwB0AHUAZgBlAG4ALQBQAHIAbwBmAGkAbAAgAEcAYQBtAG0AYQAgADIALAAy -AFAAcgBvAGYAaQBsAG8AIABnAHIAaQBnAGkAbwAgAGcAZQBuAGUAcgBpAGMAbwAgAGQAZQBsAGwAYQAg -AGcAYQBtAG0AYQAgADIALAAyAEcAZQBuAGUAcgBpAHMAawAgAGcAcgDlACAAMgAsADIAIABnAGEAbQBt -AGEAcAByAG8AZgBpAGxmbpAacHBepnz7ZXAAMgAuADJjz4/wZYdO9k4AgiwwsDDsMKQwrDDzMN4AIAAy -AC4AMgAgMNcw7TDVMKEwpDDrA5MDtQO9A7kDugPMACADkwO6A8EDuQAgA5MDrAO8A7wDsQAgADIALgAy -AFAAZQByAGYAaQBsACAAZwBlAG4A6QByAGkAYwBvACAAZABlACAAYwBpAG4AegBlAG4AdABvAHMAIABk -AGEAIABHAGEAbQBtAGEAIAAyACwAMgBBAGwAZwBlAG0AZQBlAG4AIABnAHIAaQBqAHMAIABnAGEAbQBt -AGEAIAAyACwAMgAtAHAAcgBvAGYAaQBlAGwAUABlAHIAZgBpAGwAIABnAGUAbgDpAHIAaQBjAG8AIABk -AGUAIABnAGEAbQBtAGEAIABkAGUAIABnAHIAaQBzAGUAcwAgADIALAAyDiMOMQ4HDioONQ5BDgEOIQ4h -DjIOQA4BDiMOIg5MDhcOMQ5IDicORA4bACAAMgAuADIARwBlAG4AZQBsACAARwByAGkAIABHAGEAbQBh -ACAAMgAsADIAWQBsAGUAaQBuAGUAbgAgAGgAYQByAG0AYQBhAG4AIABnAGEAbQBtAGEAIAAyACwAMgAg -AC0AcAByAG8AZgBpAGkAbABpAEcAZQBuAGUAcgBpAQ0AawBpACAARwByAGEAeQAgAEcAYQBtAG0AYQAg -ADIALgAyACAAcAByAG8AZgBpAGwAVQBuAGkAdwBlAHIAcwBhAGwAbgB5ACAAcAByAG8AZgBpAGwAIABz -AHoAYQByAG8BWwBjAGkAIABnAGEAbQBtAGEAIAAyACwAMgY6BicGRQYnACAAMgAuADIAIAZEBkgGRgAg -BjEGRQYnBi8GSgAgBjkGJwZFBB4EMQRJBDAETwAgBEEENQRABDAETwAgBDMEMAQ8BDwEMAAgADIALAAy -AC0EPwRABD4ERAQ4BDsETABHAGUAbgBlAHIAaQBjACAARwByAGEAeQAgAEcAYQBtAG0AYQAgADIALgAy -ACAAUAByAG8AZgBpAGwAZQAAdGV4dAAAAABDb3B5cmlnaHQgQXBwbGUgSW5jLiwgMjAxMgAAWFlaIAAA -AAAAAPNRAAEAAAABFsxjdXJ2AAAAAAAABAAAAAAFAAoADwAUABkAHgAjACgALQAyADcAOwBAAEUASgBP -AFQAWQBeAGMAaABtAHIAdwB8AIEAhgCLAJAAlQCaAJ8ApACpAK4AsgC3ALwAwQDGAMsA0ADVANsA4ADl -AOsA8AD2APsBAQEHAQ0BEwEZAR8BJQErATIBOAE+AUUBTAFSAVkBYAFnAW4BdQF8AYMBiwGSAZoBoQGp -AbEBuQHBAckB0QHZAeEB6QHyAfoCAwIMAhQCHQImAi8COAJBAksCVAJdAmcCcQJ6AoQCjgKYAqICrAK2 -AsECywLVAuAC6wL1AwADCwMWAyEDLQM4A0MDTwNaA2YDcgN+A4oDlgOiA64DugPHA9MD4APsA/kEBgQT -BCAELQQ7BEgEVQRjBHEEfgSMBJoEqAS2BMQE0wThBPAE/gUNBRwFKwU6BUkFWAVnBXcFhgWWBaYFtQXF -BdUF5QX2BgYGFgYnBjcGSAZZBmoGewaMBp0GrwbABtEG4wb1BwcHGQcrBz0HTwdhB3QHhgeZB6wHvwfS -B+UH+AgLCB8IMghGCFoIbgiCCJYIqgi+CNII5wj7CRAJJQk6CU8JZAl5CY8JpAm6Cc8J5Qn7ChEKJwo9 -ClQKagqBCpgKrgrFCtwK8wsLCyILOQtRC2kLgAuYC7ALyAvhC/kMEgwqDEMMXAx1DI4MpwzADNkM8w0N -DSYNQA1aDXQNjg2pDcMN3g34DhMOLg5JDmQOfw6bDrYO0g7uDwkPJQ9BD14Peg+WD7MPzw/sEAkQJhBD -EGEQfhCbELkQ1xD1ERMRMRFPEW0RjBGqEckR6BIHEiYSRRJkEoQSoxLDEuMTAxMjE0MTYxODE6QTxRPl -FAYUJxRJFGoUixStFM4U8BUSFTQVVhV4FZsVvRXgFgMWJhZJFmwWjxayFtYW+hcdF0EXZReJF64X0hf3 -GBsYQBhlGIoYrxjVGPoZIBlFGWsZkRm3Gd0aBBoqGlEadxqeGsUa7BsUGzsbYxuKG7Ib2hwCHCocUhx7 -HKMczBz1HR4dRx1wHZkdwx3sHhYeQB5qHpQevh7pHxMfPh9pH5Qfvx/qIBUgQSBsIJggxCDwIRwhSCF1 -IaEhziH7IiciVSKCIq8i3SMKIzgjZiOUI8Ij8CQfJE0kfCSrJNolCSU4JWgllyXHJfcmJyZXJocmtybo -JxgnSSd6J6sn3CgNKD8ocSiiKNQpBik4KWspnSnQKgIqNSpoKpsqzysCKzYraSudK9EsBSw5LG4soizX -LQwtQS12Last4S4WLkwugi63Lu4vJC9aL5Evxy/+MDUwbDCkMNsxEjFKMYIxujHyMioyYzKbMtQzDTNG -M38zuDPxNCs0ZTSeNNg1EzVNNYc1wjX9Njc2cjauNuk3JDdgN5w31zgUOFA4jDjIOQU5Qjl/Obw5+To2 -OnQ6sjrvOy07azuqO+g8JzxlPKQ84z0iPWE9oT3gPiA+YD6gPuA/IT9hP6I/4kAjQGRApkDnQSlBakGs -Qe5CMEJyQrVC90M6Q31DwEQDREdEikTORRJFVUWaRd5GIkZnRqtG8Ec1R3tHwEgFSEtIkUjXSR1JY0mp -SfBKN0p9SsRLDEtTS5pL4kwqTHJMuk0CTUpNk03cTiVObk63TwBPSU+TT91QJ1BxULtRBlFQUZtR5lIx -UnxSx1MTU19TqlP2VEJUj1TbVShVdVXCVg9WXFapVvdXRFeSV+BYL1h9WMtZGllpWbhaB1pWWqZa9VtF -W5Vb5Vw1XIZc1l0nXXhdyV4aXmxevV8PX2Ffs2AFYFdgqmD8YU9homH1YklinGLwY0Njl2PrZEBklGTp -ZT1lkmXnZj1mkmboZz1nk2fpaD9olmjsaUNpmmnxakhqn2r3a09rp2v/bFdsr20IbWBtuW4SbmtuxG8e -b3hv0XArcIZw4HE6cZVx8HJLcqZzAXNdc7h0FHRwdMx1KHWFdeF2Pnabdvh3VnezeBF4bnjMeSp5iXnn -ekZ6pXsEe2N7wnwhfIF84X1BfaF+AX5ifsJ/I3+Ef+WAR4CogQqBa4HNgjCCkoL0g1eDuoQdhICE44VH -hauGDoZyhteHO4efiASIaYjOiTOJmYn+imSKyoswi5aL/IxjjMqNMY2Yjf+OZo7OjzaPnpAGkG6Q1pE/ -kaiSEZJ6kuOTTZO2lCCUipT0lV+VyZY0lp+XCpd1l+CYTJi4mSSZkJn8mmia1ZtCm6+cHJyJnPedZJ3S -nkCerp8dn4uf+qBpoNihR6G2oiailqMGo3aj5qRWpMelOKWpphqmi6b9p26n4KhSqMSpN6mpqhyqj6sC -q3Wr6axcrNCtRK24ri2uoa8Wr4uwALB1sOqxYLHWskuywrM4s660JbSctRO1irYBtnm28Ldot+C4WbjR -uUq5wro7urW7LrunvCG8m70VvY++Cr6Evv+/er/1wHDA7MFnwePCX8Lbw1jD1MRRxM7FS8XIxkbGw8dB -x7/IPci8yTrJuco4yrfLNsu2zDXMtc01zbXONs62zzfPuNA50LrRPNG+0j/SwdNE08bUSdTL1U7V0dZV -1tjXXNfg2GTY6Nls2fHadtr724DcBdyK3RDdlt4c3qLfKd+v4DbgveFE4cziU+Lb42Pj6+Rz5PzlhOYN -5pbnH+ep6DLovOlG6dDqW+rl63Dr++yG7RHtnO4o7rTvQO/M8Fjw5fFy8f/yjPMZ86f0NPTC9VD13vZt -9vv3ivgZ+Kj5OPnH+lf65/t3/Af8mP0p/br+S/7c/23//9IrLC0uWiRjbGFzc25hbWVYJGNsYXNzZXNf -EBBOU0JpdG1hcEltYWdlUmVwoy0vMFpOU0ltYWdlUmVwWE5TT2JqZWN00issMjNXTlNBcnJheaIyMNIr -LDU2Xk5TTXV0YWJsZUFycmF5ozUyMNM4OQ86OzxXTlNXaGl0ZVxOU0NvbG9yU3BhY2VEMCAwABADgAzS -Kyw+P1dOU0NvbG9yoj4w0issQUJXTlNJbWFnZaJBMAAIABEAGgAkACkAMgA3AEkATABRAFMAYgBoAHUA -fACLAJIAnwCmAK4AsACyALQAuQC7AL0AxADJANQA1gDYANoA3wDiAOQA5gDoAO8BBgEiASQBJhOWE5sT -phOvE8ITxhPRE9oT3xPnE+oT7xP+FAIUCRQRFB4UIxQlFCcULBQ0FDcUPBREAAAAAAAAAgEAAAAAAAAA -QwAAAAAAAAAAAAAAAAAAFEc +S2V5ZWRBcmNoaXZlctEICVRyb290gAGuCwwbHCEaJissMzY5P0JVJG51bGzXDQ4PEBESExQVFhcYGRpW +TlNTaXplViRjbGFzc1xOU0ltYWdlRmxhZ3NWTlNSZXBzW05TVGludENvbG9yV05TQ29sb3JeTlNSZXNp +emluZ01vZGWAAoANEiDDAACAA4AAgAsQAFZ7MSwgMX3SHQ4eIFpOUy5vYmplY3RzoR+ABIAK0h0OIiWi +IySABYAGgAnTDicoKSoaXxAUTlNUSUZGUmVwcmVzZW50YXRpb25fEBlOU0ludGVybmFsTGF5b3V0RGly +ZWN0aW9ugAiAB08REmxNTQAqAAAACgAAABABAAADAAAAAQABAAABAQADAAAAAQABAAABAgADAAAAAgAI +AAgBAwADAAAAAQABAAABBgADAAAAAQABAAABCgADAAAAAQABAAABEQAEAAAAAQAAAAgBEgADAAAAAQAB +AAABFQADAAAAAQACAAABFgADAAAAAQABAAABFwAEAAAAAQAAAAIBHAADAAAAAQABAAABKAADAAAAAQAC +AAABUgADAAAAAQABAAABUwADAAAAAgABAAGHcwAHAAARnAAAANAAAAAAAAARnGFwcGwCAAAAbW50ckdS +QVlYWVogB9wACAAXAA8ALgAPYWNzcEFQUEwAAAAAbm9uZQAAAAAAAAAAAAAAAAAAAAAAAPbWAAEAAAAA +0y1hcHBsAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFZGVzYwAA +AMAAAAB5ZHNjbQAAATwAAAgaY3BydAAACVgAAAAjd3RwdAAACXwAAAAUa1RSQwAACZAAAAgMZGVzYwAA +AAAAAAAfR2VuZXJpYyBHcmF5IEdhbW1hIDIuMiBQcm9maWxlAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAG1s +dWMAAAAAAAAAHwAAAAxza1NLAAAALgAAAYRkYURLAAAAOgAAAbJjYUVTAAAAOAAAAex2aVZOAAAAQAAA +AiRwdEJSAAAASgAAAmR1a1VBAAAALAAAAq5mckZVAAAAPgAAAtpodUhVAAAANAAAAxh6aFRXAAAAGgAA +A0xrb0tSAAAAIgAAA2ZuYk5PAAAAOgAAA4hjc0NaAAAAKAAAA8JoZUlMAAAAJAAAA+pyb1JPAAAAKgAA +BA5kZURFAAAATgAABDhpdElUAAAATgAABIZzdlNFAAAAOAAABNR6aENOAAAAGgAABQxqYUpQAAAAJgAA +BSZlbEdSAAAAKgAABUxwdFBPAAAAUgAABXZubE5MAAAAQAAABchlc0VTAAAATAAABgh0aFRIAAAAMgAA +BlR0clRSAAAAJAAABoZmaUZJAAAARgAABqpockhSAAAAPgAABvBwbFBMAAAASgAABy5hckVHAAAALAAA +B3hydVJVAAAAOgAAB6RlblVTAAAAPAAAB94AVgFhAGUAbwBiAGUAYwBuAOEAIABzAGkAdgDhACAAZwBh +AG0AYQAgADIALAAyAEcAZQBuAGUAcgBpAHMAawAgAGcAcgDlACAAMgAsADIAIABnAGEAbQBtAGEALQBw +AHIAbwBmAGkAbABHAGEAbQBtAGEAIABkAGUAIABnAHIAaQBzAG8AcwAgAGcAZQBuAOgAcgBpAGMAYQAg +ADIALgAyAEMepQB1ACAAaADsAG4AaAAgAE0A4AB1ACAAeADhAG0AIABDAGgAdQBuAGcAIABHAGEAbQBt +AGEAIAAyAC4AMgBQAGUAcgBmAGkAbAAgAEcAZQBuAOkAcgBpAGMAbwAgAGQAYQAgAEcAYQBtAGEAIABk +AGUAIABDAGkAbgB6AGEAcwAgADIALAAyBBcEMAQzBDAEOwRMBD0EMAAgAEcAcgBhAHkALQQzBDAEPAQw +ACAAMgAuADIAUAByAG8AZgBpAGwAIABnAOkAbgDpAHIAaQBxAHUAZQAgAGcAcgBpAHMAIABnAGEAbQBt +AGEAIAAyACwAMgDBAGwAdABhAGwA4QBuAG8AcwAgAHMAegD8AHIAawBlACAAZwBhAG0AbQBhACAAMgAu +ADKQGnUocHCWjlFJXqYAMgAuADKCcl9pY8+P8Md8vBgAINaMwMkAIKwQucgAIAAyAC4AMgAg1QS4XNMM +x3wARwBlAG4AZQByAGkAcwBrACAAZwByAOUAIABnAGEAbQBtAGEAIAAyACwAMgAtAHAAcgBvAGYAaQBs +AE8AYgBlAGMAbgDhACABYQBlAGQA4QAgAGcAYQBtAGEAIAAyAC4AMgXSBdAF3gXUACAF0AXkBdUF6AAg +BdsF3AXcBdkAIAAyAC4AMgBHAGEAbQBhACAAZwByAGkAIABnAGUAbgBlAHIAaQBjAQMAIAAyACwAMgBB +AGwAbABnAGUAbQBlAGkAbgBlAHMAIABHAHIAYQB1AHMAdAB1AGYAZQBuAC0AUAByAG8AZgBpAGwAIABH +AGEAbQBtAGEAIAAyACwAMgBQAHIAbwBmAGkAbABvACAAZwByAGkAZwBpAG8AIABnAGUAbgBlAHIAaQBj +AG8AIABkAGUAbABsAGEAIABnAGEAbQBtAGEAIAAyACwAMgBHAGUAbgBlAHIAaQBzAGsAIABnAHIA5QAg +ADIALAAyACAAZwBhAG0AbQBhAHAAcgBvAGYAaQBsZm6QGnBwXqZ8+2VwADIALgAyY8+P8GWHTvZOAIIs +MLAw7DCkMKww8zDeACAAMgAuADIAIDDXMO0w1TChMKQw6wOTA7UDvQO5A7oDzAAgA5MDugPBA7kAIAOT +A6wDvAO8A7EAIAAyAC4AMgBQAGUAcgBmAGkAbAAgAGcAZQBuAOkAcgBpAGMAbwAgAGQAZQAgAGMAaQBu +AHoAZQBuAHQAbwBzACAAZABhACAARwBhAG0AbQBhACAAMgAsADIAQQBsAGcAZQBtAGUAZQBuACAAZwBy +AGkAagBzACAAZwBhAG0AbQBhACAAMgAsADIALQBwAHIAbwBmAGkAZQBsAFAAZQByAGYAaQBsACAAZwBl +AG4A6QByAGkAYwBvACAAZABlACAAZwBhAG0AbQBhACAAZABlACAAZwByAGkAcwBlAHMAIAAyACwAMg4j +DjEOBw4qDjUOQQ4BDiEOIQ4yDkAOAQ4jDiIOTA4XDjEOSA4nDkQOGwAgADIALgAyAEcAZQBuAGUAbAAg +AEcAcgBpACAARwBhAG0AYQAgADIALAAyAFkAbABlAGkAbgBlAG4AIABoAGEAcgBtAGEAYQBuACAAZwBh +AG0AbQBhACAAMgAsADIAIAAtAHAAcgBvAGYAaQBpAGwAaQBHAGUAbgBlAHIAaQENAGsAaQAgAEcAcgBh +AHkAIABHAGEAbQBtAGEAIAAyAC4AMgAgAHAAcgBvAGYAaQBsAFUAbgBpAHcAZQByAHMAYQBsAG4AeQAg +AHAAcgBvAGYAaQBsACAAcwB6AGEAcgBvAVsAYwBpACAAZwBhAG0AbQBhACAAMgAsADIGOgYnBkUGJwAg +ADIALgAyACAGRAZIBkYAIAYxBkUGJwYvBkoAIAY5BicGRQQeBDEESQQwBE8AIARBBDUEQAQwBE8AIAQz +BDAEPAQ8BDAAIAAyACwAMgAtBD8EQAQ+BEQEOAQ7BEwARwBlAG4AZQByAGkAYwAgAEcAcgBhAHkAIABH +AGEAbQBtAGEAIAAyAC4AMgAgAFAAcgBvAGYAaQBsAGUAAHRleHQAAAAAQ29weXJpZ2h0IEFwcGxlIElu +Yy4sIDIwMTIAAFhZWiAAAAAAAADzUQABAAAAARbMY3VydgAAAAAAAAQAAAAABQAKAA8AFAAZAB4AIwAo +AC0AMgA3ADsAQABFAEoATwBUAFkAXgBjAGgAbQByAHcAfACBAIYAiwCQAJUAmgCfAKQAqQCuALIAtwC8 +AMEAxgDLANAA1QDbAOAA5QDrAPAA9gD7AQEBBwENARMBGQEfASUBKwEyATgBPgFFAUwBUgFZAWABZwFu +AXUBfAGDAYsBkgGaAaEBqQGxAbkBwQHJAdEB2QHhAekB8gH6AgMCDAIUAh0CJgIvAjgCQQJLAlQCXQJn +AnECegKEAo4CmAKiAqwCtgLBAssC1QLgAusC9QMAAwsDFgMhAy0DOANDA08DWgNmA3IDfgOKA5YDogOu +A7oDxwPTA+AD7AP5BAYEEwQgBC0EOwRIBFUEYwRxBH4EjASaBKgEtgTEBNME4QTwBP4FDQUcBSsFOgVJ +BVgFZwV3BYYFlgWmBbUFxQXVBeUF9gYGBhYGJwY3BkgGWQZqBnsGjAadBq8GwAbRBuMG9QcHBxkHKwc9 +B08HYQd0B4YHmQesB78H0gflB/gICwgfCDIIRghaCG4IggiWCKoIvgjSCOcI+wkQCSUJOglPCWQJeQmP +CaQJugnPCeUJ+woRCicKPQpUCmoKgQqYCq4KxQrcCvMLCwsiCzkLUQtpC4ALmAuwC8gL4Qv5DBIMKgxD +DFwMdQyODKcMwAzZDPMNDQ0mDUANWg10DY4NqQ3DDd4N+A4TDi4OSQ5kDn8Omw62DtIO7g8JDyUPQQ9e +D3oPlg+zD88P7BAJECYQQxBhEH4QmxC5ENcQ9RETETERTxFtEYwRqhHJEegSBxImEkUSZBKEEqMSwxLj +EwMTIxNDE2MTgxOkE8UT5RQGFCcUSRRqFIsUrRTOFPAVEhU0FVYVeBWbFb0V4BYDFiYWSRZsFo8WshbW +FvoXHRdBF2UXiReuF9IX9xgbGEAYZRiKGK8Y1Rj6GSAZRRlrGZEZtxndGgQaKhpRGncanhrFGuwbFBs7 +G2MbihuyG9ocAhwqHFIcexyjHMwc9R0eHUcdcB2ZHcMd7B4WHkAeah6UHr4e6R8THz4faR+UH78f6iAV +IEEgbCCYIMQg8CEcIUghdSGhIc4h+yInIlUigiKvIt0jCiM4I2YjlCPCI/AkHyRNJHwkqyTaJQklOCVo +JZclxyX3JicmVyaHJrcm6CcYJ0kneierJ9woDSg/KHEooijUKQYpOClrKZ0p0CoCKjUqaCqbKs8rAis2 +K2krnSvRLAUsOSxuLKIs1y0MLUEtdi2rLeEuFi5MLoIuty7uLyQvWi+RL8cv/jA1MGwwpDDbMRIxSjGC +Mbox8jIqMmMymzLUMw0zRjN/M7gz8TQrNGU0njTYNRM1TTWHNcI1/TY3NnI2rjbpNyQ3YDecN9c4FDhQ +OIw4yDkFOUI5fzm8Ofk6Njp0OrI67zstO2s7qjvoPCc8ZTykPOM9Ij1hPaE94D4gPmA+oD7gPyE/YT+i +P+JAI0BkQKZA50EpQWpBrEHuQjBCckK1QvdDOkN9Q8BEA0RHRIpEzkUSRVVFmkXeRiJGZ0arRvBHNUd7 +R8BIBUhLSJFI10kdSWNJqUnwSjdKfUrESwxLU0uaS+JMKkxyTLpNAk1KTZNN3E4lTm5Ot08AT0lPk0/d +UCdQcVC7UQZRUFGbUeZSMVJ8UsdTE1NfU6pT9lRCVI9U21UoVXVVwlYPVlxWqVb3V0RXklfgWC9YfVjL +WRpZaVm4WgdaVlqmWvVbRVuVW+VcNVyGXNZdJ114XcleGl5sXr1fD19hX7NgBWBXYKpg/GFPYaJh9WJJ +Ypxi8GNDY5dj62RAZJRk6WU9ZZJl52Y9ZpJm6Gc9Z5Nn6Wg/aJZo7GlDaZpp8WpIap9q92tPa6dr/2xX +bK9tCG1gbbluEm5rbsRvHm94b9FwK3CGcOBxOnGVcfByS3KmcwFzXXO4dBR0cHTMdSh1hXXhdj52m3b4 +d1Z3s3gReG54zHkqeYl553pGeqV7BHtje8J8IXyBfOF9QX2hfgF+Yn7CfyN/hH/lgEeAqIEKgWuBzYIw +gpKC9INXg7qEHYSAhOOFR4Wrhg6GcobXhzuHn4gEiGmIzokziZmJ/opkisqLMIuWi/yMY4zKjTGNmI3/ +jmaOzo82j56QBpBukNaRP5GokhGSepLjk02TtpQglIqU9JVflcmWNJaflwqXdZfgmEyYuJkkmZCZ/Jpo +mtWbQpuvnByciZz3nWSd0p5Anq6fHZ+Ln/qgaaDYoUehtqImopajBqN2o+akVqTHpTilqaYapoum/adu +p+CoUqjEqTepqaocqo+rAqt1q+msXKzQrUStuK4trqGvFq+LsACwdbDqsWCx1rJLssKzOLOutCW0nLUT +tYq2AbZ5tvC3aLfguFm40blKucK6O7q1uy67p7whvJu9Fb2Pvgq+hL7/v3q/9cBwwOzBZ8Hjwl/C28NY +w9TEUcTOxUvFyMZGxsPHQce/yD3IvMk6ybnKOMq3yzbLtsw1zLXNNc21zjbOts83z7jQOdC60TzRvtI/ +0sHTRNPG1EnUy9VO1dHWVdbY11zX4Nhk2OjZbNnx2nba+9uA3AXcit0Q3ZbeHN6i3ynfr+A24L3hROHM +4lPi2+Nj4+vkc+T85YTmDeaW5x/nqegy6LzpRunQ6lvq5etw6/vshu0R7ZzuKO6070DvzPBY8OXxcvH/ +8ozzGfOn9DT0wvVQ9d72bfb794r4Gfio+Tj5x/pX+uf7d/wH/Jj9Kf26/kv+3P9t///SLS4vMFokY2xh +c3NuYW1lWCRjbGFzc2VzXxAQTlNCaXRtYXBJbWFnZVJlcKMvMTJaTlNJbWFnZVJlcFhOU09iamVjdNIt +LjQ1V05TQXJyYXmiNDLSLS43OF5OU011dGFibGVBcnJheaM3NDLTOjsOPD0+V05TV2hpdGVcTlNDb2xv +clNwYWNlRDAgMAAQA4AM0i0uQEFXTlNDb2xvcqJAMtItLkNEV05TSW1hZ2WiQzIACAARABoAJAApADIA +NwBJAEwAUQBTAGIAaAB3AH4AhQCSAJkApQCtALwAvgDAAMUAxwDJAMsAzQDUANkA5ADmAOgA6gDvAPIA +9AD2APgA/wEWATIBNAE2E6YTqxO2E78T0hPWE+ET6hPvE/cT+hP/FA4UEhQZFCEULhQzFDUUNxQ8FEQU +RxRMFFQAAAAAAAACAQAAAAAAAABFAAAAAAAAAAAAAAAAAAAUVw diff --git a/i18n/src/main/resources/da.lproj/Command.xib b/i18n/src/main/resources/da.lproj/Command.xib index 6e19e64ac0..dd1bf28941 100644 --- a/i18n/src/main/resources/da.lproj/Command.xib +++ b/i18n/src/main/resources/da.lproj/Command.xib @@ -1,7 +1,7 @@ - + - + diff --git a/i18n/src/main/resources/da.lproj/Connection.xib b/i18n/src/main/resources/da.lproj/Connection.xib index 5959e86ee3..ac46ffec99 100644 --- a/i18n/src/main/resources/da.lproj/Connection.xib +++ b/i18n/src/main/resources/da.lproj/Connection.xib @@ -1,7 +1,7 @@ - + - + @@ -76,7 +76,7 @@ - + diff --git a/i18n/src/main/resources/da.lproj/Info.xib b/i18n/src/main/resources/da.lproj/Info.xib index 3fe91e6aca..6c3e27d6e0 100644 --- a/i18n/src/main/resources/da.lproj/Info.xib +++ b/i18n/src/main/resources/da.lproj/Info.xib @@ -1,7 +1,7 @@ - + - + @@ -89,7 +89,7 @@ - + @@ -1017,21 +1017,21 @@ - + - + - + @@ -1121,21 +1121,21 @@ - + - + - + @@ -1250,27 +1250,27 @@ diff --git a/i18n/src/main/resources/da.lproj/Login.strings b/i18n/src/main/resources/da.lproj/Login.strings index 1c5269dae4..85cdaa5191 100644 Binary files a/i18n/src/main/resources/da.lproj/Login.strings and b/i18n/src/main/resources/da.lproj/Login.strings differ diff --git a/i18n/src/main/resources/da.lproj/Main.xib b/i18n/src/main/resources/da.lproj/Main.xib index a37f14fea8..dde264338b 100644 --- a/i18n/src/main/resources/da.lproj/Main.xib +++ b/i18n/src/main/resources/da.lproj/Main.xib @@ -1,7 +1,7 @@ - + - + @@ -94,7 +94,7 @@ - + @@ -159,19 +159,19 @@ - + - + - + @@ -321,12 +321,12 @@ CA - + - + @@ -566,6 +566,10 @@ CA - + + + + + diff --git a/i18n/src/main/resources/da.lproj/Preferences.strings b/i18n/src/main/resources/da.lproj/Preferences.strings index 98e6217556..e8cab83fa3 100644 Binary files a/i18n/src/main/resources/da.lproj/Preferences.strings and b/i18n/src/main/resources/da.lproj/Preferences.strings differ diff --git a/i18n/src/main/resources/da.lproj/Preferences.strings.1 b/i18n/src/main/resources/da.lproj/Preferences.strings.1 index 71d3e513f4..9044ada69e 100644 Binary files a/i18n/src/main/resources/da.lproj/Preferences.strings.1 and b/i18n/src/main/resources/da.lproj/Preferences.strings.1 differ diff --git a/i18n/src/main/resources/da.lproj/Preferences.xib b/i18n/src/main/resources/da.lproj/Preferences.xib index 7c68933b40..f65dfda5d4 100644 --- a/i18n/src/main/resources/da.lproj/Preferences.xib +++ b/i18n/src/main/resources/da.lproj/Preferences.xib @@ -1,7 +1,7 @@ - + - + @@ -245,7 +245,7 @@ - + @@ -266,7 +266,7 @@ - + @@ -307,7 +307,7 @@ - + @@ -538,7 +538,7 @@ - + @@ -578,7 +578,7 @@ - + @@ -613,7 +613,7 @@ - + @@ -642,7 +642,7 @@ - + @@ -1218,7 +1218,7 @@ - + @@ -1250,7 +1250,7 @@ - + @@ -1290,7 +1290,7 @@ - + @@ -1343,7 +1343,7 @@ - + @@ -1363,7 +1363,7 @@ - + @@ -1374,7 +1374,7 @@ - + @@ -1558,7 +1558,7 @@ - + @@ -1618,7 +1618,7 @@ - + @@ -1643,7 +1643,7 @@ - + @@ -1685,7 +1685,7 @@ - + @@ -1713,7 +1713,7 @@ - + @@ -1741,7 +1741,7 @@ - + @@ -1770,7 +1770,7 @@ - + @@ -1793,7 +1793,7 @@ - + @@ -1805,7 +1805,7 @@ - + @@ -1889,7 +1889,7 @@ - + @@ -1948,7 +1948,7 @@ - + @@ -1976,7 +1976,7 @@ - + @@ -2005,7 +2005,7 @@ - + diff --git a/i18n/src/main/resources/da.lproj/Profiles.xib b/i18n/src/main/resources/da.lproj/Profiles.xib index 32fb2d7fec..be424ac541 100644 --- a/i18n/src/main/resources/da.lproj/Profiles.xib +++ b/i18n/src/main/resources/da.lproj/Profiles.xib @@ -1,7 +1,7 @@ - + - + diff --git a/i18n/src/main/resources/da.lproj/Prompt.xib b/i18n/src/main/resources/da.lproj/Prompt.xib index 61d017f00f..b695aaffed 100644 --- a/i18n/src/main/resources/da.lproj/Prompt.xib +++ b/i18n/src/main/resources/da.lproj/Prompt.xib @@ -1,7 +1,7 @@ - + - + @@ -95,7 +95,7 @@ Gw - + diff --git a/i18n/src/main/resources/da.lproj/S3.strings b/i18n/src/main/resources/da.lproj/S3.strings index aa8c4d433d..5fa06ff4ad 100644 Binary files a/i18n/src/main/resources/da.lproj/S3.strings and b/i18n/src/main/resources/da.lproj/S3.strings differ diff --git a/i18n/src/main/resources/da.lproj/SDS.strings b/i18n/src/main/resources/da.lproj/SDS.strings index 15fc0aa0cb..5687cd7706 100644 Binary files a/i18n/src/main/resources/da.lproj/SDS.strings and b/i18n/src/main/resources/da.lproj/SDS.strings differ diff --git a/i18n/src/main/resources/da.lproj/Status.strings b/i18n/src/main/resources/da.lproj/Status.strings index b69ae2cca1..9a5ffb2ab2 100644 Binary files a/i18n/src/main/resources/da.lproj/Status.strings and b/i18n/src/main/resources/da.lproj/Status.strings differ diff --git a/i18n/src/main/resources/da.lproj/Updater.strings b/i18n/src/main/resources/da.lproj/Updater.strings index c87203efbb..2ef8cc9d9a 100644 Binary files a/i18n/src/main/resources/da.lproj/Updater.strings and b/i18n/src/main/resources/da.lproj/Updater.strings differ diff --git a/i18n/src/main/resources/de.lproj/Bookmark.xib b/i18n/src/main/resources/de.lproj/Bookmark.xib index 4d6393a836..6204d0381e 100644 --- a/i18n/src/main/resources/de.lproj/Bookmark.xib +++ b/i18n/src/main/resources/de.lproj/Bookmark.xib @@ -1,7 +1,7 @@ - + - + @@ -92,7 +92,7 @@ - + @@ -489,95 +489,95 @@ YnBsaXN0MDDUAQIDBAUGBwpYJHZlcnNpb25ZJGFyY2hpdmVyVCR0b3BYJG9iamVjdHMSAAGGoF8QD05T -S2V5ZWRBcmNoaXZlctEICVRyb290gAGuCwwZGh8UJCkqMTQ3PUBVJG51bGzWDQ4PEBESExQVFhcYVk5T -U2l6ZV5OU1Jlc2l6aW5nTW9kZVYkY2xhc3NcTlNJbWFnZUZsYWdzVk5TUmVwc1dOU0NvbG9ygAIQAIAN -EiDDAACAA4ALVnsxLCAxfdIbDxweWk5TLm9iamVjdHOhHYAEgArSGw8gI6IhIoAFgAaACdMPJSYnKBRf -EBROU1RJRkZSZXByZXNlbnRhdGlvbl8QGU5TSW50ZXJuYWxMYXlvdXREaXJlY3Rpb26ACIAHTxESbE1N -ACoAAAAKAAAAEAEAAAMAAAABAAEAAAEBAAMAAAABAAEAAAECAAMAAAACAAgACAEDAAMAAAABAAEAAAEG -AAMAAAABAAEAAAEKAAMAAAABAAEAAAERAAQAAAABAAAACAESAAMAAAABAAEAAAEVAAMAAAABAAIAAAEW -AAMAAAABAAEAAAEXAAQAAAABAAAAAgEcAAMAAAABAAEAAAEoAAMAAAABAAIAAAFSAAMAAAABAAEAAAFT -AAMAAAACAAEAAYdzAAcAABGcAAAA0AAAAAAAABGcYXBwbAIAAABtbnRyR1JBWVhZWiAH3AAIABcADwAu -AA9hY3NwQVBQTAAAAABub25lAAAAAAAAAAAAAAAAAAAAAAAA9tYAAQAAAADTLWFwcGwAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAVkZXNjAAAAwAAAAHlkc2NtAAABPAAA -CBpjcHJ0AAAJWAAAACN3dHB0AAAJfAAAABRrVFJDAAAJkAAACAxkZXNjAAAAAAAAAB9HZW5lcmljIEdy -YXkgR2FtbWEgMi4yIFByb2ZpbGUAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAbWx1YwAAAAAAAAAfAAAADHNr -U0sAAAAuAAABhGRhREsAAAA6AAABsmNhRVMAAAA4AAAB7HZpVk4AAABAAAACJHB0QlIAAABKAAACZHVr -VUEAAAAsAAACrmZyRlUAAAA+AAAC2mh1SFUAAAA0AAADGHpoVFcAAAAaAAADTGtvS1IAAAAiAAADZm5i -Tk8AAAA6AAADiGNzQ1oAAAAoAAADwmhlSUwAAAAkAAAD6nJvUk8AAAAqAAAEDmRlREUAAABOAAAEOGl0 -SVQAAABOAAAEhnN2U0UAAAA4AAAE1HpoQ04AAAAaAAAFDGphSlAAAAAmAAAFJmVsR1IAAAAqAAAFTHB0 -UE8AAABSAAAFdm5sTkwAAABAAAAFyGVzRVMAAABMAAAGCHRoVEgAAAAyAAAGVHRyVFIAAAAkAAAGhmZp -RkkAAABGAAAGqmhySFIAAAA+AAAG8HBsUEwAAABKAAAHLmFyRUcAAAAsAAAHeHJ1UlUAAAA6AAAHpGVu -VVMAAAA8AAAH3gBWAWEAZQBvAGIAZQBjAG4A4QAgAHMAaQB2AOEAIABnAGEAbQBhACAAMgAsADIARwBl -AG4AZQByAGkAcwBrACAAZwByAOUAIAAyACwAMgAgAGcAYQBtAG0AYQAtAHAAcgBvAGYAaQBsAEcAYQBt -AG0AYQAgAGQAZQAgAGcAcgBpAHMAbwBzACAAZwBlAG4A6AByAGkAYwBhACAAMgAuADIAQx6lAHUAIABo -AOwAbgBoACAATQDgAHUAIAB4AOEAbQAgAEMAaAB1AG4AZwAgAEcAYQBtAG0AYQAgADIALgAyAFAAZQBy -AGYAaQBsACAARwBlAG4A6QByAGkAYwBvACAAZABhACAARwBhAG0AYQAgAGQAZQAgAEMAaQBuAHoAYQBz -ACAAMgAsADIEFwQwBDMEMAQ7BEwEPQQwACAARwByAGEAeQAtBDMEMAQ8BDAAIAAyAC4AMgBQAHIAbwBm -AGkAbAAgAGcA6QBuAOkAcgBpAHEAdQBlACAAZwByAGkAcwAgAGcAYQBtAG0AYQAgADIALAAyAMEAbAB0 -AGEAbADhAG4AbwBzACAAcwB6APwAcgBrAGUAIABnAGEAbQBtAGEAIAAyAC4AMpAadShwcJaOUUlepgAy -AC4AMoJyX2ljz4/wx3y8GAAg1ozAyQAgrBC5yAAgADIALgAyACDVBLhc0wzHfABHAGUAbgBlAHIAaQBz -AGsAIABnAHIA5QAgAGcAYQBtAG0AYQAgADIALAAyAC0AcAByAG8AZgBpAGwATwBiAGUAYwBuAOEAIAFh -AGUAZADhACAAZwBhAG0AYQAgADIALgAyBdIF0AXeBdQAIAXQBeQF1QXoACAF2wXcBdwF2QAgADIALgAy -AEcAYQBtAGEAIABnAHIAaQAgAGcAZQBuAGUAcgBpAGMBAwAgADIALAAyAEEAbABsAGcAZQBtAGUAaQBu -AGUAcwAgAEcAcgBhAHUAcwB0AHUAZgBlAG4ALQBQAHIAbwBmAGkAbAAgAEcAYQBtAG0AYQAgADIALAAy -AFAAcgBvAGYAaQBsAG8AIABnAHIAaQBnAGkAbwAgAGcAZQBuAGUAcgBpAGMAbwAgAGQAZQBsAGwAYQAg -AGcAYQBtAG0AYQAgADIALAAyAEcAZQBuAGUAcgBpAHMAawAgAGcAcgDlACAAMgAsADIAIABnAGEAbQBt -AGEAcAByAG8AZgBpAGxmbpAacHBepnz7ZXAAMgAuADJjz4/wZYdO9k4AgiwwsDDsMKQwrDDzMN4AIAAy -AC4AMgAgMNcw7TDVMKEwpDDrA5MDtQO9A7kDugPMACADkwO6A8EDuQAgA5MDrAO8A7wDsQAgADIALgAy -AFAAZQByAGYAaQBsACAAZwBlAG4A6QByAGkAYwBvACAAZABlACAAYwBpAG4AegBlAG4AdABvAHMAIABk -AGEAIABHAGEAbQBtAGEAIAAyACwAMgBBAGwAZwBlAG0AZQBlAG4AIABnAHIAaQBqAHMAIABnAGEAbQBt -AGEAIAAyACwAMgAtAHAAcgBvAGYAaQBlAGwAUABlAHIAZgBpAGwAIABnAGUAbgDpAHIAaQBjAG8AIABk -AGUAIABnAGEAbQBtAGEAIABkAGUAIABnAHIAaQBzAGUAcwAgADIALAAyDiMOMQ4HDioONQ5BDgEOIQ4h -DjIOQA4BDiMOIg5MDhcOMQ5IDicORA4bACAAMgAuADIARwBlAG4AZQBsACAARwByAGkAIABHAGEAbQBh -ACAAMgAsADIAWQBsAGUAaQBuAGUAbgAgAGgAYQByAG0AYQBhAG4AIABnAGEAbQBtAGEAIAAyACwAMgAg -AC0AcAByAG8AZgBpAGkAbABpAEcAZQBuAGUAcgBpAQ0AawBpACAARwByAGEAeQAgAEcAYQBtAG0AYQAg -ADIALgAyACAAcAByAG8AZgBpAGwAVQBuAGkAdwBlAHIAcwBhAGwAbgB5ACAAcAByAG8AZgBpAGwAIABz -AHoAYQByAG8BWwBjAGkAIABnAGEAbQBtAGEAIAAyACwAMgY6BicGRQYnACAAMgAuADIAIAZEBkgGRgAg -BjEGRQYnBi8GSgAgBjkGJwZFBB4EMQRJBDAETwAgBEEENQRABDAETwAgBDMEMAQ8BDwEMAAgADIALAAy -AC0EPwRABD4ERAQ4BDsETABHAGUAbgBlAHIAaQBjACAARwByAGEAeQAgAEcAYQBtAG0AYQAgADIALgAy -ACAAUAByAG8AZgBpAGwAZQAAdGV4dAAAAABDb3B5cmlnaHQgQXBwbGUgSW5jLiwgMjAxMgAAWFlaIAAA -AAAAAPNRAAEAAAABFsxjdXJ2AAAAAAAABAAAAAAFAAoADwAUABkAHgAjACgALQAyADcAOwBAAEUASgBP -AFQAWQBeAGMAaABtAHIAdwB8AIEAhgCLAJAAlQCaAJ8ApACpAK4AsgC3ALwAwQDGAMsA0ADVANsA4ADl -AOsA8AD2APsBAQEHAQ0BEwEZAR8BJQErATIBOAE+AUUBTAFSAVkBYAFnAW4BdQF8AYMBiwGSAZoBoQGp -AbEBuQHBAckB0QHZAeEB6QHyAfoCAwIMAhQCHQImAi8COAJBAksCVAJdAmcCcQJ6AoQCjgKYAqICrAK2 -AsECywLVAuAC6wL1AwADCwMWAyEDLQM4A0MDTwNaA2YDcgN+A4oDlgOiA64DugPHA9MD4APsA/kEBgQT -BCAELQQ7BEgEVQRjBHEEfgSMBJoEqAS2BMQE0wThBPAE/gUNBRwFKwU6BUkFWAVnBXcFhgWWBaYFtQXF -BdUF5QX2BgYGFgYnBjcGSAZZBmoGewaMBp0GrwbABtEG4wb1BwcHGQcrBz0HTwdhB3QHhgeZB6wHvwfS -B+UH+AgLCB8IMghGCFoIbgiCCJYIqgi+CNII5wj7CRAJJQk6CU8JZAl5CY8JpAm6Cc8J5Qn7ChEKJwo9 -ClQKagqBCpgKrgrFCtwK8wsLCyILOQtRC2kLgAuYC7ALyAvhC/kMEgwqDEMMXAx1DI4MpwzADNkM8w0N -DSYNQA1aDXQNjg2pDcMN3g34DhMOLg5JDmQOfw6bDrYO0g7uDwkPJQ9BD14Peg+WD7MPzw/sEAkQJhBD -EGEQfhCbELkQ1xD1ERMRMRFPEW0RjBGqEckR6BIHEiYSRRJkEoQSoxLDEuMTAxMjE0MTYxODE6QTxRPl -FAYUJxRJFGoUixStFM4U8BUSFTQVVhV4FZsVvRXgFgMWJhZJFmwWjxayFtYW+hcdF0EXZReJF64X0hf3 -GBsYQBhlGIoYrxjVGPoZIBlFGWsZkRm3Gd0aBBoqGlEadxqeGsUa7BsUGzsbYxuKG7Ib2hwCHCocUhx7 -HKMczBz1HR4dRx1wHZkdwx3sHhYeQB5qHpQevh7pHxMfPh9pH5Qfvx/qIBUgQSBsIJggxCDwIRwhSCF1 -IaEhziH7IiciVSKCIq8i3SMKIzgjZiOUI8Ij8CQfJE0kfCSrJNolCSU4JWgllyXHJfcmJyZXJocmtybo -JxgnSSd6J6sn3CgNKD8ocSiiKNQpBik4KWspnSnQKgIqNSpoKpsqzysCKzYraSudK9EsBSw5LG4soizX -LQwtQS12Last4S4WLkwugi63Lu4vJC9aL5Evxy/+MDUwbDCkMNsxEjFKMYIxujHyMioyYzKbMtQzDTNG -M38zuDPxNCs0ZTSeNNg1EzVNNYc1wjX9Njc2cjauNuk3JDdgN5w31zgUOFA4jDjIOQU5Qjl/Obw5+To2 -OnQ6sjrvOy07azuqO+g8JzxlPKQ84z0iPWE9oT3gPiA+YD6gPuA/IT9hP6I/4kAjQGRApkDnQSlBakGs -Qe5CMEJyQrVC90M6Q31DwEQDREdEikTORRJFVUWaRd5GIkZnRqtG8Ec1R3tHwEgFSEtIkUjXSR1JY0mp -SfBKN0p9SsRLDEtTS5pL4kwqTHJMuk0CTUpNk03cTiVObk63TwBPSU+TT91QJ1BxULtRBlFQUZtR5lIx -UnxSx1MTU19TqlP2VEJUj1TbVShVdVXCVg9WXFapVvdXRFeSV+BYL1h9WMtZGllpWbhaB1pWWqZa9VtF -W5Vb5Vw1XIZc1l0nXXhdyV4aXmxevV8PX2Ffs2AFYFdgqmD8YU9homH1YklinGLwY0Njl2PrZEBklGTp -ZT1lkmXnZj1mkmboZz1nk2fpaD9olmjsaUNpmmnxakhqn2r3a09rp2v/bFdsr20IbWBtuW4SbmtuxG8e -b3hv0XArcIZw4HE6cZVx8HJLcqZzAXNdc7h0FHRwdMx1KHWFdeF2Pnabdvh3VnezeBF4bnjMeSp5iXnn -ekZ6pXsEe2N7wnwhfIF84X1BfaF+AX5ifsJ/I3+Ef+WAR4CogQqBa4HNgjCCkoL0g1eDuoQdhICE44VH -hauGDoZyhteHO4efiASIaYjOiTOJmYn+imSKyoswi5aL/IxjjMqNMY2Yjf+OZo7OjzaPnpAGkG6Q1pE/ -kaiSEZJ6kuOTTZO2lCCUipT0lV+VyZY0lp+XCpd1l+CYTJi4mSSZkJn8mmia1ZtCm6+cHJyJnPedZJ3S -nkCerp8dn4uf+qBpoNihR6G2oiailqMGo3aj5qRWpMelOKWpphqmi6b9p26n4KhSqMSpN6mpqhyqj6sC -q3Wr6axcrNCtRK24ri2uoa8Wr4uwALB1sOqxYLHWskuywrM4s660JbSctRO1irYBtnm28Ldot+C4WbjR -uUq5wro7urW7LrunvCG8m70VvY++Cr6Evv+/er/1wHDA7MFnwePCX8Lbw1jD1MRRxM7FS8XIxkbGw8dB -x7/IPci8yTrJuco4yrfLNsu2zDXMtc01zbXONs62zzfPuNA50LrRPNG+0j/SwdNE08bUSdTL1U7V0dZV -1tjXXNfg2GTY6Nls2fHadtr724DcBdyK3RDdlt4c3qLfKd+v4DbgveFE4cziU+Lb42Pj6+Rz5PzlhOYN -5pbnH+ep6DLovOlG6dDqW+rl63Dr++yG7RHtnO4o7rTvQO/M8Fjw5fFy8f/yjPMZ86f0NPTC9VD13vZt -9vv3ivgZ+Kj5OPnH+lf65/t3/Af8mP0p/br+S/7c/23//9IrLC0uWiRjbGFzc25hbWVYJGNsYXNzZXNf -EBBOU0JpdG1hcEltYWdlUmVwoy0vMFpOU0ltYWdlUmVwWE5TT2JqZWN00issMjNXTlNBcnJheaIyMNIr -LDU2Xk5TTXV0YWJsZUFycmF5ozUyMNM4OQ86OzxXTlNXaGl0ZVxOU0NvbG9yU3BhY2VEMCAwABADgAzS -Kyw+P1dOU0NvbG9yoj4w0issQUJXTlNJbWFnZaJBMAAIABEAGgAkACkAMgA3AEkATABRAFMAYgBoAHUA -fACLAJIAnwCmAK4AsACyALQAuQC7AL0AxADJANQA1gDYANoA3wDiAOQA5gDoAO8BBgEiASQBJhOWE5sT -phOvE8ITxhPRE9oT3xPnE+oT7xP+FAIUCRQRFB4UIxQlFCcULBQ0FDcUPBREAAAAAAAAAgEAAAAAAAAA -QwAAAAAAAAAAAAAAAAAAFEc +S2V5ZWRBcmNoaXZlctEICVRyb290gAGuCwwbHCEaJissMzY5P0JVJG51bGzXDQ4PEBESExQVFhcYGRpW +TlNTaXplViRjbGFzc1xOU0ltYWdlRmxhZ3NWTlNSZXBzW05TVGludENvbG9yV05TQ29sb3JeTlNSZXNp +emluZ01vZGWAAoANEiDDAACAA4AAgAsQAFZ7MSwgMX3SHQ4eIFpOUy5vYmplY3RzoR+ABIAK0h0OIiWi +IySABYAGgAnTDicoKSoaXxAUTlNUSUZGUmVwcmVzZW50YXRpb25fEBlOU0ludGVybmFsTGF5b3V0RGly +ZWN0aW9ugAiAB08REmxNTQAqAAAACgAAABABAAADAAAAAQABAAABAQADAAAAAQABAAABAgADAAAAAgAI +AAgBAwADAAAAAQABAAABBgADAAAAAQABAAABCgADAAAAAQABAAABEQAEAAAAAQAAAAgBEgADAAAAAQAB +AAABFQADAAAAAQACAAABFgADAAAAAQABAAABFwAEAAAAAQAAAAIBHAADAAAAAQABAAABKAADAAAAAQAC +AAABUgADAAAAAQABAAABUwADAAAAAgABAAGHcwAHAAARnAAAANAAAAAAAAARnGFwcGwCAAAAbW50ckdS +QVlYWVogB9wACAAXAA8ALgAPYWNzcEFQUEwAAAAAbm9uZQAAAAAAAAAAAAAAAAAAAAAAAPbWAAEAAAAA +0y1hcHBsAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFZGVzYwAA +AMAAAAB5ZHNjbQAAATwAAAgaY3BydAAACVgAAAAjd3RwdAAACXwAAAAUa1RSQwAACZAAAAgMZGVzYwAA +AAAAAAAfR2VuZXJpYyBHcmF5IEdhbW1hIDIuMiBQcm9maWxlAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAG1s +dWMAAAAAAAAAHwAAAAxza1NLAAAALgAAAYRkYURLAAAAOgAAAbJjYUVTAAAAOAAAAex2aVZOAAAAQAAA +AiRwdEJSAAAASgAAAmR1a1VBAAAALAAAAq5mckZVAAAAPgAAAtpodUhVAAAANAAAAxh6aFRXAAAAGgAA +A0xrb0tSAAAAIgAAA2ZuYk5PAAAAOgAAA4hjc0NaAAAAKAAAA8JoZUlMAAAAJAAAA+pyb1JPAAAAKgAA +BA5kZURFAAAATgAABDhpdElUAAAATgAABIZzdlNFAAAAOAAABNR6aENOAAAAGgAABQxqYUpQAAAAJgAA +BSZlbEdSAAAAKgAABUxwdFBPAAAAUgAABXZubE5MAAAAQAAABchlc0VTAAAATAAABgh0aFRIAAAAMgAA +BlR0clRSAAAAJAAABoZmaUZJAAAARgAABqpockhSAAAAPgAABvBwbFBMAAAASgAABy5hckVHAAAALAAA +B3hydVJVAAAAOgAAB6RlblVTAAAAPAAAB94AVgFhAGUAbwBiAGUAYwBuAOEAIABzAGkAdgDhACAAZwBh +AG0AYQAgADIALAAyAEcAZQBuAGUAcgBpAHMAawAgAGcAcgDlACAAMgAsADIAIABnAGEAbQBtAGEALQBw +AHIAbwBmAGkAbABHAGEAbQBtAGEAIABkAGUAIABnAHIAaQBzAG8AcwAgAGcAZQBuAOgAcgBpAGMAYQAg +ADIALgAyAEMepQB1ACAAaADsAG4AaAAgAE0A4AB1ACAAeADhAG0AIABDAGgAdQBuAGcAIABHAGEAbQBt +AGEAIAAyAC4AMgBQAGUAcgBmAGkAbAAgAEcAZQBuAOkAcgBpAGMAbwAgAGQAYQAgAEcAYQBtAGEAIABk +AGUAIABDAGkAbgB6AGEAcwAgADIALAAyBBcEMAQzBDAEOwRMBD0EMAAgAEcAcgBhAHkALQQzBDAEPAQw +ACAAMgAuADIAUAByAG8AZgBpAGwAIABnAOkAbgDpAHIAaQBxAHUAZQAgAGcAcgBpAHMAIABnAGEAbQBt +AGEAIAAyACwAMgDBAGwAdABhAGwA4QBuAG8AcwAgAHMAegD8AHIAawBlACAAZwBhAG0AbQBhACAAMgAu +ADKQGnUocHCWjlFJXqYAMgAuADKCcl9pY8+P8Md8vBgAINaMwMkAIKwQucgAIAAyAC4AMgAg1QS4XNMM +x3wARwBlAG4AZQByAGkAcwBrACAAZwByAOUAIABnAGEAbQBtAGEAIAAyACwAMgAtAHAAcgBvAGYAaQBs +AE8AYgBlAGMAbgDhACABYQBlAGQA4QAgAGcAYQBtAGEAIAAyAC4AMgXSBdAF3gXUACAF0AXkBdUF6AAg +BdsF3AXcBdkAIAAyAC4AMgBHAGEAbQBhACAAZwByAGkAIABnAGUAbgBlAHIAaQBjAQMAIAAyACwAMgBB +AGwAbABnAGUAbQBlAGkAbgBlAHMAIABHAHIAYQB1AHMAdAB1AGYAZQBuAC0AUAByAG8AZgBpAGwAIABH +AGEAbQBtAGEAIAAyACwAMgBQAHIAbwBmAGkAbABvACAAZwByAGkAZwBpAG8AIABnAGUAbgBlAHIAaQBj +AG8AIABkAGUAbABsAGEAIABnAGEAbQBtAGEAIAAyACwAMgBHAGUAbgBlAHIAaQBzAGsAIABnAHIA5QAg +ADIALAAyACAAZwBhAG0AbQBhAHAAcgBvAGYAaQBsZm6QGnBwXqZ8+2VwADIALgAyY8+P8GWHTvZOAIIs +MLAw7DCkMKww8zDeACAAMgAuADIAIDDXMO0w1TChMKQw6wOTA7UDvQO5A7oDzAAgA5MDugPBA7kAIAOT +A6wDvAO8A7EAIAAyAC4AMgBQAGUAcgBmAGkAbAAgAGcAZQBuAOkAcgBpAGMAbwAgAGQAZQAgAGMAaQBu +AHoAZQBuAHQAbwBzACAAZABhACAARwBhAG0AbQBhACAAMgAsADIAQQBsAGcAZQBtAGUAZQBuACAAZwBy +AGkAagBzACAAZwBhAG0AbQBhACAAMgAsADIALQBwAHIAbwBmAGkAZQBsAFAAZQByAGYAaQBsACAAZwBl +AG4A6QByAGkAYwBvACAAZABlACAAZwBhAG0AbQBhACAAZABlACAAZwByAGkAcwBlAHMAIAAyACwAMg4j +DjEOBw4qDjUOQQ4BDiEOIQ4yDkAOAQ4jDiIOTA4XDjEOSA4nDkQOGwAgADIALgAyAEcAZQBuAGUAbAAg +AEcAcgBpACAARwBhAG0AYQAgADIALAAyAFkAbABlAGkAbgBlAG4AIABoAGEAcgBtAGEAYQBuACAAZwBh +AG0AbQBhACAAMgAsADIAIAAtAHAAcgBvAGYAaQBpAGwAaQBHAGUAbgBlAHIAaQENAGsAaQAgAEcAcgBh +AHkAIABHAGEAbQBtAGEAIAAyAC4AMgAgAHAAcgBvAGYAaQBsAFUAbgBpAHcAZQByAHMAYQBsAG4AeQAg +AHAAcgBvAGYAaQBsACAAcwB6AGEAcgBvAVsAYwBpACAAZwBhAG0AbQBhACAAMgAsADIGOgYnBkUGJwAg +ADIALgAyACAGRAZIBkYAIAYxBkUGJwYvBkoAIAY5BicGRQQeBDEESQQwBE8AIARBBDUEQAQwBE8AIAQz +BDAEPAQ8BDAAIAAyACwAMgAtBD8EQAQ+BEQEOAQ7BEwARwBlAG4AZQByAGkAYwAgAEcAcgBhAHkAIABH +AGEAbQBtAGEAIAAyAC4AMgAgAFAAcgBvAGYAaQBsAGUAAHRleHQAAAAAQ29weXJpZ2h0IEFwcGxlIElu +Yy4sIDIwMTIAAFhZWiAAAAAAAADzUQABAAAAARbMY3VydgAAAAAAAAQAAAAABQAKAA8AFAAZAB4AIwAo +AC0AMgA3ADsAQABFAEoATwBUAFkAXgBjAGgAbQByAHcAfACBAIYAiwCQAJUAmgCfAKQAqQCuALIAtwC8 +AMEAxgDLANAA1QDbAOAA5QDrAPAA9gD7AQEBBwENARMBGQEfASUBKwEyATgBPgFFAUwBUgFZAWABZwFu +AXUBfAGDAYsBkgGaAaEBqQGxAbkBwQHJAdEB2QHhAekB8gH6AgMCDAIUAh0CJgIvAjgCQQJLAlQCXQJn +AnECegKEAo4CmAKiAqwCtgLBAssC1QLgAusC9QMAAwsDFgMhAy0DOANDA08DWgNmA3IDfgOKA5YDogOu +A7oDxwPTA+AD7AP5BAYEEwQgBC0EOwRIBFUEYwRxBH4EjASaBKgEtgTEBNME4QTwBP4FDQUcBSsFOgVJ +BVgFZwV3BYYFlgWmBbUFxQXVBeUF9gYGBhYGJwY3BkgGWQZqBnsGjAadBq8GwAbRBuMG9QcHBxkHKwc9 +B08HYQd0B4YHmQesB78H0gflB/gICwgfCDIIRghaCG4IggiWCKoIvgjSCOcI+wkQCSUJOglPCWQJeQmP +CaQJugnPCeUJ+woRCicKPQpUCmoKgQqYCq4KxQrcCvMLCwsiCzkLUQtpC4ALmAuwC8gL4Qv5DBIMKgxD +DFwMdQyODKcMwAzZDPMNDQ0mDUANWg10DY4NqQ3DDd4N+A4TDi4OSQ5kDn8Omw62DtIO7g8JDyUPQQ9e +D3oPlg+zD88P7BAJECYQQxBhEH4QmxC5ENcQ9RETETERTxFtEYwRqhHJEegSBxImEkUSZBKEEqMSwxLj +EwMTIxNDE2MTgxOkE8UT5RQGFCcUSRRqFIsUrRTOFPAVEhU0FVYVeBWbFb0V4BYDFiYWSRZsFo8WshbW +FvoXHRdBF2UXiReuF9IX9xgbGEAYZRiKGK8Y1Rj6GSAZRRlrGZEZtxndGgQaKhpRGncanhrFGuwbFBs7 +G2MbihuyG9ocAhwqHFIcexyjHMwc9R0eHUcdcB2ZHcMd7B4WHkAeah6UHr4e6R8THz4faR+UH78f6iAV +IEEgbCCYIMQg8CEcIUghdSGhIc4h+yInIlUigiKvIt0jCiM4I2YjlCPCI/AkHyRNJHwkqyTaJQklOCVo +JZclxyX3JicmVyaHJrcm6CcYJ0kneierJ9woDSg/KHEooijUKQYpOClrKZ0p0CoCKjUqaCqbKs8rAis2 +K2krnSvRLAUsOSxuLKIs1y0MLUEtdi2rLeEuFi5MLoIuty7uLyQvWi+RL8cv/jA1MGwwpDDbMRIxSjGC +Mbox8jIqMmMymzLUMw0zRjN/M7gz8TQrNGU0njTYNRM1TTWHNcI1/TY3NnI2rjbpNyQ3YDecN9c4FDhQ +OIw4yDkFOUI5fzm8Ofk6Njp0OrI67zstO2s7qjvoPCc8ZTykPOM9Ij1hPaE94D4gPmA+oD7gPyE/YT+i +P+JAI0BkQKZA50EpQWpBrEHuQjBCckK1QvdDOkN9Q8BEA0RHRIpEzkUSRVVFmkXeRiJGZ0arRvBHNUd7 +R8BIBUhLSJFI10kdSWNJqUnwSjdKfUrESwxLU0uaS+JMKkxyTLpNAk1KTZNN3E4lTm5Ot08AT0lPk0/d +UCdQcVC7UQZRUFGbUeZSMVJ8UsdTE1NfU6pT9lRCVI9U21UoVXVVwlYPVlxWqVb3V0RXklfgWC9YfVjL +WRpZaVm4WgdaVlqmWvVbRVuVW+VcNVyGXNZdJ114XcleGl5sXr1fD19hX7NgBWBXYKpg/GFPYaJh9WJJ +Ypxi8GNDY5dj62RAZJRk6WU9ZZJl52Y9ZpJm6Gc9Z5Nn6Wg/aJZo7GlDaZpp8WpIap9q92tPa6dr/2xX +bK9tCG1gbbluEm5rbsRvHm94b9FwK3CGcOBxOnGVcfByS3KmcwFzXXO4dBR0cHTMdSh1hXXhdj52m3b4 +d1Z3s3gReG54zHkqeYl553pGeqV7BHtje8J8IXyBfOF9QX2hfgF+Yn7CfyN/hH/lgEeAqIEKgWuBzYIw +gpKC9INXg7qEHYSAhOOFR4Wrhg6GcobXhzuHn4gEiGmIzokziZmJ/opkisqLMIuWi/yMY4zKjTGNmI3/ +jmaOzo82j56QBpBukNaRP5GokhGSepLjk02TtpQglIqU9JVflcmWNJaflwqXdZfgmEyYuJkkmZCZ/Jpo +mtWbQpuvnByciZz3nWSd0p5Anq6fHZ+Ln/qgaaDYoUehtqImopajBqN2o+akVqTHpTilqaYapoum/adu +p+CoUqjEqTepqaocqo+rAqt1q+msXKzQrUStuK4trqGvFq+LsACwdbDqsWCx1rJLssKzOLOutCW0nLUT +tYq2AbZ5tvC3aLfguFm40blKucK6O7q1uy67p7whvJu9Fb2Pvgq+hL7/v3q/9cBwwOzBZ8Hjwl/C28NY +w9TEUcTOxUvFyMZGxsPHQce/yD3IvMk6ybnKOMq3yzbLtsw1zLXNNc21zjbOts83z7jQOdC60TzRvtI/ +0sHTRNPG1EnUy9VO1dHWVdbY11zX4Nhk2OjZbNnx2nba+9uA3AXcit0Q3ZbeHN6i3ynfr+A24L3hROHM +4lPi2+Nj4+vkc+T85YTmDeaW5x/nqegy6LzpRunQ6lvq5etw6/vshu0R7ZzuKO6070DvzPBY8OXxcvH/ +8ozzGfOn9DT0wvVQ9d72bfb794r4Gfio+Tj5x/pX+uf7d/wH/Jj9Kf26/kv+3P9t///SLS4vMFokY2xh +c3NuYW1lWCRjbGFzc2VzXxAQTlNCaXRtYXBJbWFnZVJlcKMvMTJaTlNJbWFnZVJlcFhOU09iamVjdNIt +LjQ1V05TQXJyYXmiNDLSLS43OF5OU011dGFibGVBcnJheaM3NDLTOjsOPD0+V05TV2hpdGVcTlNDb2xv +clNwYWNlRDAgMAAQA4AM0i0uQEFXTlNDb2xvcqJAMtItLkNEV05TSW1hZ2WiQzIACAARABoAJAApADIA +NwBJAEwAUQBTAGIAaAB3AH4AhQCSAJkApQCtALwAvgDAAMUAxwDJAMsAzQDUANkA5ADmAOgA6gDvAPIA +9AD2APgA/wEWATIBNAE2E6YTqxO2E78T0hPWE+ET6hPvE/cT+hP/FA4UEhQZFCEULhQzFDUUNxQ8FEQU +RxRMFFQAAAAAAAACAQAAAAAAAABFAAAAAAAAAAAAAAAAAAAUVw diff --git a/i18n/src/main/resources/de.lproj/Browser.xib b/i18n/src/main/resources/de.lproj/Browser.xib index e933ae07ff..fdc41f137d 100644 --- a/i18n/src/main/resources/de.lproj/Browser.xib +++ b/i18n/src/main/resources/de.lproj/Browser.xib @@ -1,7 +1,7 @@ - + - + @@ -38,7 +38,7 @@ - + @@ -62,27 +62,27 @@ @@ -256,7 +256,7 @@ - + @@ -553,95 +553,95 @@ DQ YnBsaXN0MDDUAQIDBAUGBwpYJHZlcnNpb25ZJGFyY2hpdmVyVCR0b3BYJG9iamVjdHMSAAGGoF8QD05T -S2V5ZWRBcmNoaXZlctEICVRyb290gAGuCwwZGh8UJCkqMTQ3PUBVJG51bGzWDQ4PEBESExQVFhcYVk5T -U2l6ZV5OU1Jlc2l6aW5nTW9kZVYkY2xhc3NcTlNJbWFnZUZsYWdzVk5TUmVwc1dOU0NvbG9ygAIQAIAN -EiDDAACAA4ALVnsxLCAxfdIbDxweWk5TLm9iamVjdHOhHYAEgArSGw8gI6IhIoAFgAaACdMPJSYnKBRf -EBROU1RJRkZSZXByZXNlbnRhdGlvbl8QGU5TSW50ZXJuYWxMYXlvdXREaXJlY3Rpb26ACIAHTxESbE1N -ACoAAAAKAAAAEAEAAAMAAAABAAEAAAEBAAMAAAABAAEAAAECAAMAAAACAAgACAEDAAMAAAABAAEAAAEG -AAMAAAABAAEAAAEKAAMAAAABAAEAAAERAAQAAAABAAAACAESAAMAAAABAAEAAAEVAAMAAAABAAIAAAEW -AAMAAAABAAEAAAEXAAQAAAABAAAAAgEcAAMAAAABAAEAAAEoAAMAAAABAAIAAAFSAAMAAAABAAEAAAFT -AAMAAAACAAEAAYdzAAcAABGcAAAA0AAAAAAAABGcYXBwbAIAAABtbnRyR1JBWVhZWiAH3AAIABcADwAu -AA9hY3NwQVBQTAAAAABub25lAAAAAAAAAAAAAAAAAAAAAAAA9tYAAQAAAADTLWFwcGwAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAVkZXNjAAAAwAAAAHlkc2NtAAABPAAA -CBpjcHJ0AAAJWAAAACN3dHB0AAAJfAAAABRrVFJDAAAJkAAACAxkZXNjAAAAAAAAAB9HZW5lcmljIEdy -YXkgR2FtbWEgMi4yIFByb2ZpbGUAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAbWx1YwAAAAAAAAAfAAAADHNr -U0sAAAAuAAABhGRhREsAAAA6AAABsmNhRVMAAAA4AAAB7HZpVk4AAABAAAACJHB0QlIAAABKAAACZHVr -VUEAAAAsAAACrmZyRlUAAAA+AAAC2mh1SFUAAAA0AAADGHpoVFcAAAAaAAADTGtvS1IAAAAiAAADZm5i -Tk8AAAA6AAADiGNzQ1oAAAAoAAADwmhlSUwAAAAkAAAD6nJvUk8AAAAqAAAEDmRlREUAAABOAAAEOGl0 -SVQAAABOAAAEhnN2U0UAAAA4AAAE1HpoQ04AAAAaAAAFDGphSlAAAAAmAAAFJmVsR1IAAAAqAAAFTHB0 -UE8AAABSAAAFdm5sTkwAAABAAAAFyGVzRVMAAABMAAAGCHRoVEgAAAAyAAAGVHRyVFIAAAAkAAAGhmZp -RkkAAABGAAAGqmhySFIAAAA+AAAG8HBsUEwAAABKAAAHLmFyRUcAAAAsAAAHeHJ1UlUAAAA6AAAHpGVu -VVMAAAA8AAAH3gBWAWEAZQBvAGIAZQBjAG4A4QAgAHMAaQB2AOEAIABnAGEAbQBhACAAMgAsADIARwBl -AG4AZQByAGkAcwBrACAAZwByAOUAIAAyACwAMgAgAGcAYQBtAG0AYQAtAHAAcgBvAGYAaQBsAEcAYQBt -AG0AYQAgAGQAZQAgAGcAcgBpAHMAbwBzACAAZwBlAG4A6AByAGkAYwBhACAAMgAuADIAQx6lAHUAIABo -AOwAbgBoACAATQDgAHUAIAB4AOEAbQAgAEMAaAB1AG4AZwAgAEcAYQBtAG0AYQAgADIALgAyAFAAZQBy -AGYAaQBsACAARwBlAG4A6QByAGkAYwBvACAAZABhACAARwBhAG0AYQAgAGQAZQAgAEMAaQBuAHoAYQBz -ACAAMgAsADIEFwQwBDMEMAQ7BEwEPQQwACAARwByAGEAeQAtBDMEMAQ8BDAAIAAyAC4AMgBQAHIAbwBm -AGkAbAAgAGcA6QBuAOkAcgBpAHEAdQBlACAAZwByAGkAcwAgAGcAYQBtAG0AYQAgADIALAAyAMEAbAB0 -AGEAbADhAG4AbwBzACAAcwB6APwAcgBrAGUAIABnAGEAbQBtAGEAIAAyAC4AMpAadShwcJaOUUlepgAy -AC4AMoJyX2ljz4/wx3y8GAAg1ozAyQAgrBC5yAAgADIALgAyACDVBLhc0wzHfABHAGUAbgBlAHIAaQBz -AGsAIABnAHIA5QAgAGcAYQBtAG0AYQAgADIALAAyAC0AcAByAG8AZgBpAGwATwBiAGUAYwBuAOEAIAFh -AGUAZADhACAAZwBhAG0AYQAgADIALgAyBdIF0AXeBdQAIAXQBeQF1QXoACAF2wXcBdwF2QAgADIALgAy -AEcAYQBtAGEAIABnAHIAaQAgAGcAZQBuAGUAcgBpAGMBAwAgADIALAAyAEEAbABsAGcAZQBtAGUAaQBu -AGUAcwAgAEcAcgBhAHUAcwB0AHUAZgBlAG4ALQBQAHIAbwBmAGkAbAAgAEcAYQBtAG0AYQAgADIALAAy -AFAAcgBvAGYAaQBsAG8AIABnAHIAaQBnAGkAbwAgAGcAZQBuAGUAcgBpAGMAbwAgAGQAZQBsAGwAYQAg -AGcAYQBtAG0AYQAgADIALAAyAEcAZQBuAGUAcgBpAHMAawAgAGcAcgDlACAAMgAsADIAIABnAGEAbQBt -AGEAcAByAG8AZgBpAGxmbpAacHBepnz7ZXAAMgAuADJjz4/wZYdO9k4AgiwwsDDsMKQwrDDzMN4AIAAy -AC4AMgAgMNcw7TDVMKEwpDDrA5MDtQO9A7kDugPMACADkwO6A8EDuQAgA5MDrAO8A7wDsQAgADIALgAy -AFAAZQByAGYAaQBsACAAZwBlAG4A6QByAGkAYwBvACAAZABlACAAYwBpAG4AegBlAG4AdABvAHMAIABk -AGEAIABHAGEAbQBtAGEAIAAyACwAMgBBAGwAZwBlAG0AZQBlAG4AIABnAHIAaQBqAHMAIABnAGEAbQBt -AGEAIAAyACwAMgAtAHAAcgBvAGYAaQBlAGwAUABlAHIAZgBpAGwAIABnAGUAbgDpAHIAaQBjAG8AIABk -AGUAIABnAGEAbQBtAGEAIABkAGUAIABnAHIAaQBzAGUAcwAgADIALAAyDiMOMQ4HDioONQ5BDgEOIQ4h -DjIOQA4BDiMOIg5MDhcOMQ5IDicORA4bACAAMgAuADIARwBlAG4AZQBsACAARwByAGkAIABHAGEAbQBh -ACAAMgAsADIAWQBsAGUAaQBuAGUAbgAgAGgAYQByAG0AYQBhAG4AIABnAGEAbQBtAGEAIAAyACwAMgAg -AC0AcAByAG8AZgBpAGkAbABpAEcAZQBuAGUAcgBpAQ0AawBpACAARwByAGEAeQAgAEcAYQBtAG0AYQAg -ADIALgAyACAAcAByAG8AZgBpAGwAVQBuAGkAdwBlAHIAcwBhAGwAbgB5ACAAcAByAG8AZgBpAGwAIABz -AHoAYQByAG8BWwBjAGkAIABnAGEAbQBtAGEAIAAyACwAMgY6BicGRQYnACAAMgAuADIAIAZEBkgGRgAg -BjEGRQYnBi8GSgAgBjkGJwZFBB4EMQRJBDAETwAgBEEENQRABDAETwAgBDMEMAQ8BDwEMAAgADIALAAy -AC0EPwRABD4ERAQ4BDsETABHAGUAbgBlAHIAaQBjACAARwByAGEAeQAgAEcAYQBtAG0AYQAgADIALgAy -ACAAUAByAG8AZgBpAGwAZQAAdGV4dAAAAABDb3B5cmlnaHQgQXBwbGUgSW5jLiwgMjAxMgAAWFlaIAAA -AAAAAPNRAAEAAAABFsxjdXJ2AAAAAAAABAAAAAAFAAoADwAUABkAHgAjACgALQAyADcAOwBAAEUASgBP -AFQAWQBeAGMAaABtAHIAdwB8AIEAhgCLAJAAlQCaAJ8ApACpAK4AsgC3ALwAwQDGAMsA0ADVANsA4ADl -AOsA8AD2APsBAQEHAQ0BEwEZAR8BJQErATIBOAE+AUUBTAFSAVkBYAFnAW4BdQF8AYMBiwGSAZoBoQGp -AbEBuQHBAckB0QHZAeEB6QHyAfoCAwIMAhQCHQImAi8COAJBAksCVAJdAmcCcQJ6AoQCjgKYAqICrAK2 -AsECywLVAuAC6wL1AwADCwMWAyEDLQM4A0MDTwNaA2YDcgN+A4oDlgOiA64DugPHA9MD4APsA/kEBgQT -BCAELQQ7BEgEVQRjBHEEfgSMBJoEqAS2BMQE0wThBPAE/gUNBRwFKwU6BUkFWAVnBXcFhgWWBaYFtQXF -BdUF5QX2BgYGFgYnBjcGSAZZBmoGewaMBp0GrwbABtEG4wb1BwcHGQcrBz0HTwdhB3QHhgeZB6wHvwfS -B+UH+AgLCB8IMghGCFoIbgiCCJYIqgi+CNII5wj7CRAJJQk6CU8JZAl5CY8JpAm6Cc8J5Qn7ChEKJwo9 -ClQKagqBCpgKrgrFCtwK8wsLCyILOQtRC2kLgAuYC7ALyAvhC/kMEgwqDEMMXAx1DI4MpwzADNkM8w0N -DSYNQA1aDXQNjg2pDcMN3g34DhMOLg5JDmQOfw6bDrYO0g7uDwkPJQ9BD14Peg+WD7MPzw/sEAkQJhBD -EGEQfhCbELkQ1xD1ERMRMRFPEW0RjBGqEckR6BIHEiYSRRJkEoQSoxLDEuMTAxMjE0MTYxODE6QTxRPl -FAYUJxRJFGoUixStFM4U8BUSFTQVVhV4FZsVvRXgFgMWJhZJFmwWjxayFtYW+hcdF0EXZReJF64X0hf3 -GBsYQBhlGIoYrxjVGPoZIBlFGWsZkRm3Gd0aBBoqGlEadxqeGsUa7BsUGzsbYxuKG7Ib2hwCHCocUhx7 -HKMczBz1HR4dRx1wHZkdwx3sHhYeQB5qHpQevh7pHxMfPh9pH5Qfvx/qIBUgQSBsIJggxCDwIRwhSCF1 -IaEhziH7IiciVSKCIq8i3SMKIzgjZiOUI8Ij8CQfJE0kfCSrJNolCSU4JWgllyXHJfcmJyZXJocmtybo -JxgnSSd6J6sn3CgNKD8ocSiiKNQpBik4KWspnSnQKgIqNSpoKpsqzysCKzYraSudK9EsBSw5LG4soizX -LQwtQS12Last4S4WLkwugi63Lu4vJC9aL5Evxy/+MDUwbDCkMNsxEjFKMYIxujHyMioyYzKbMtQzDTNG -M38zuDPxNCs0ZTSeNNg1EzVNNYc1wjX9Njc2cjauNuk3JDdgN5w31zgUOFA4jDjIOQU5Qjl/Obw5+To2 -OnQ6sjrvOy07azuqO+g8JzxlPKQ84z0iPWE9oT3gPiA+YD6gPuA/IT9hP6I/4kAjQGRApkDnQSlBakGs -Qe5CMEJyQrVC90M6Q31DwEQDREdEikTORRJFVUWaRd5GIkZnRqtG8Ec1R3tHwEgFSEtIkUjXSR1JY0mp -SfBKN0p9SsRLDEtTS5pL4kwqTHJMuk0CTUpNk03cTiVObk63TwBPSU+TT91QJ1BxULtRBlFQUZtR5lIx -UnxSx1MTU19TqlP2VEJUj1TbVShVdVXCVg9WXFapVvdXRFeSV+BYL1h9WMtZGllpWbhaB1pWWqZa9VtF -W5Vb5Vw1XIZc1l0nXXhdyV4aXmxevV8PX2Ffs2AFYFdgqmD8YU9homH1YklinGLwY0Njl2PrZEBklGTp -ZT1lkmXnZj1mkmboZz1nk2fpaD9olmjsaUNpmmnxakhqn2r3a09rp2v/bFdsr20IbWBtuW4SbmtuxG8e -b3hv0XArcIZw4HE6cZVx8HJLcqZzAXNdc7h0FHRwdMx1KHWFdeF2Pnabdvh3VnezeBF4bnjMeSp5iXnn -ekZ6pXsEe2N7wnwhfIF84X1BfaF+AX5ifsJ/I3+Ef+WAR4CogQqBa4HNgjCCkoL0g1eDuoQdhICE44VH -hauGDoZyhteHO4efiASIaYjOiTOJmYn+imSKyoswi5aL/IxjjMqNMY2Yjf+OZo7OjzaPnpAGkG6Q1pE/ -kaiSEZJ6kuOTTZO2lCCUipT0lV+VyZY0lp+XCpd1l+CYTJi4mSSZkJn8mmia1ZtCm6+cHJyJnPedZJ3S -nkCerp8dn4uf+qBpoNihR6G2oiailqMGo3aj5qRWpMelOKWpphqmi6b9p26n4KhSqMSpN6mpqhyqj6sC -q3Wr6axcrNCtRK24ri2uoa8Wr4uwALB1sOqxYLHWskuywrM4s660JbSctRO1irYBtnm28Ldot+C4WbjR -uUq5wro7urW7LrunvCG8m70VvY++Cr6Evv+/er/1wHDA7MFnwePCX8Lbw1jD1MRRxM7FS8XIxkbGw8dB -x7/IPci8yTrJuco4yrfLNsu2zDXMtc01zbXONs62zzfPuNA50LrRPNG+0j/SwdNE08bUSdTL1U7V0dZV -1tjXXNfg2GTY6Nls2fHadtr724DcBdyK3RDdlt4c3qLfKd+v4DbgveFE4cziU+Lb42Pj6+Rz5PzlhOYN -5pbnH+ep6DLovOlG6dDqW+rl63Dr++yG7RHtnO4o7rTvQO/M8Fjw5fFy8f/yjPMZ86f0NPTC9VD13vZt -9vv3ivgZ+Kj5OPnH+lf65/t3/Af8mP0p/br+S/7c/23//9IrLC0uWiRjbGFzc25hbWVYJGNsYXNzZXNf -EBBOU0JpdG1hcEltYWdlUmVwoy0vMFpOU0ltYWdlUmVwWE5TT2JqZWN00issMjNXTlNBcnJheaIyMNIr -LDU2Xk5TTXV0YWJsZUFycmF5ozUyMNM4OQ86OzxXTlNXaGl0ZVxOU0NvbG9yU3BhY2VEMCAwABADgAzS -Kyw+P1dOU0NvbG9yoj4w0issQUJXTlNJbWFnZaJBMAAIABEAGgAkACkAMgA3AEkATABRAFMAYgBoAHUA -fACLAJIAnwCmAK4AsACyALQAuQC7AL0AxADJANQA1gDYANoA3wDiAOQA5gDoAO8BBgEiASQBJhOWE5sT -phOvE8ITxhPRE9oT3xPnE+oT7xP+FAIUCRQRFB4UIxQlFCcULBQ0FDcUPBREAAAAAAAAAgEAAAAAAAAA -QwAAAAAAAAAAAAAAAAAAFEc +S2V5ZWRBcmNoaXZlctEICVRyb290gAGuCwwbHCEaJissMzY5P0JVJG51bGzXDQ4PEBESExQVFhcYGRpW +TlNTaXplViRjbGFzc1xOU0ltYWdlRmxhZ3NWTlNSZXBzW05TVGludENvbG9yV05TQ29sb3JeTlNSZXNp +emluZ01vZGWAAoANEiDDAACAA4AAgAsQAFZ7MSwgMX3SHQ4eIFpOUy5vYmplY3RzoR+ABIAK0h0OIiWi +IySABYAGgAnTDicoKSoaXxAUTlNUSUZGUmVwcmVzZW50YXRpb25fEBlOU0ludGVybmFsTGF5b3V0RGly +ZWN0aW9ugAiAB08REmxNTQAqAAAACgAAABABAAADAAAAAQABAAABAQADAAAAAQABAAABAgADAAAAAgAI +AAgBAwADAAAAAQABAAABBgADAAAAAQABAAABCgADAAAAAQABAAABEQAEAAAAAQAAAAgBEgADAAAAAQAB +AAABFQADAAAAAQACAAABFgADAAAAAQABAAABFwAEAAAAAQAAAAIBHAADAAAAAQABAAABKAADAAAAAQAC +AAABUgADAAAAAQABAAABUwADAAAAAgABAAGHcwAHAAARnAAAANAAAAAAAAARnGFwcGwCAAAAbW50ckdS +QVlYWVogB9wACAAXAA8ALgAPYWNzcEFQUEwAAAAAbm9uZQAAAAAAAAAAAAAAAAAAAAAAAPbWAAEAAAAA +0y1hcHBsAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFZGVzYwAA +AMAAAAB5ZHNjbQAAATwAAAgaY3BydAAACVgAAAAjd3RwdAAACXwAAAAUa1RSQwAACZAAAAgMZGVzYwAA +AAAAAAAfR2VuZXJpYyBHcmF5IEdhbW1hIDIuMiBQcm9maWxlAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAG1s +dWMAAAAAAAAAHwAAAAxza1NLAAAALgAAAYRkYURLAAAAOgAAAbJjYUVTAAAAOAAAAex2aVZOAAAAQAAA +AiRwdEJSAAAASgAAAmR1a1VBAAAALAAAAq5mckZVAAAAPgAAAtpodUhVAAAANAAAAxh6aFRXAAAAGgAA +A0xrb0tSAAAAIgAAA2ZuYk5PAAAAOgAAA4hjc0NaAAAAKAAAA8JoZUlMAAAAJAAAA+pyb1JPAAAAKgAA +BA5kZURFAAAATgAABDhpdElUAAAATgAABIZzdlNFAAAAOAAABNR6aENOAAAAGgAABQxqYUpQAAAAJgAA +BSZlbEdSAAAAKgAABUxwdFBPAAAAUgAABXZubE5MAAAAQAAABchlc0VTAAAATAAABgh0aFRIAAAAMgAA +BlR0clRSAAAAJAAABoZmaUZJAAAARgAABqpockhSAAAAPgAABvBwbFBMAAAASgAABy5hckVHAAAALAAA +B3hydVJVAAAAOgAAB6RlblVTAAAAPAAAB94AVgFhAGUAbwBiAGUAYwBuAOEAIABzAGkAdgDhACAAZwBh +AG0AYQAgADIALAAyAEcAZQBuAGUAcgBpAHMAawAgAGcAcgDlACAAMgAsADIAIABnAGEAbQBtAGEALQBw +AHIAbwBmAGkAbABHAGEAbQBtAGEAIABkAGUAIABnAHIAaQBzAG8AcwAgAGcAZQBuAOgAcgBpAGMAYQAg +ADIALgAyAEMepQB1ACAAaADsAG4AaAAgAE0A4AB1ACAAeADhAG0AIABDAGgAdQBuAGcAIABHAGEAbQBt +AGEAIAAyAC4AMgBQAGUAcgBmAGkAbAAgAEcAZQBuAOkAcgBpAGMAbwAgAGQAYQAgAEcAYQBtAGEAIABk +AGUAIABDAGkAbgB6AGEAcwAgADIALAAyBBcEMAQzBDAEOwRMBD0EMAAgAEcAcgBhAHkALQQzBDAEPAQw +ACAAMgAuADIAUAByAG8AZgBpAGwAIABnAOkAbgDpAHIAaQBxAHUAZQAgAGcAcgBpAHMAIABnAGEAbQBt +AGEAIAAyACwAMgDBAGwAdABhAGwA4QBuAG8AcwAgAHMAegD8AHIAawBlACAAZwBhAG0AbQBhACAAMgAu +ADKQGnUocHCWjlFJXqYAMgAuADKCcl9pY8+P8Md8vBgAINaMwMkAIKwQucgAIAAyAC4AMgAg1QS4XNMM +x3wARwBlAG4AZQByAGkAcwBrACAAZwByAOUAIABnAGEAbQBtAGEAIAAyACwAMgAtAHAAcgBvAGYAaQBs +AE8AYgBlAGMAbgDhACABYQBlAGQA4QAgAGcAYQBtAGEAIAAyAC4AMgXSBdAF3gXUACAF0AXkBdUF6AAg +BdsF3AXcBdkAIAAyAC4AMgBHAGEAbQBhACAAZwByAGkAIABnAGUAbgBlAHIAaQBjAQMAIAAyACwAMgBB +AGwAbABnAGUAbQBlAGkAbgBlAHMAIABHAHIAYQB1AHMAdAB1AGYAZQBuAC0AUAByAG8AZgBpAGwAIABH +AGEAbQBtAGEAIAAyACwAMgBQAHIAbwBmAGkAbABvACAAZwByAGkAZwBpAG8AIABnAGUAbgBlAHIAaQBj +AG8AIABkAGUAbABsAGEAIABnAGEAbQBtAGEAIAAyACwAMgBHAGUAbgBlAHIAaQBzAGsAIABnAHIA5QAg +ADIALAAyACAAZwBhAG0AbQBhAHAAcgBvAGYAaQBsZm6QGnBwXqZ8+2VwADIALgAyY8+P8GWHTvZOAIIs +MLAw7DCkMKww8zDeACAAMgAuADIAIDDXMO0w1TChMKQw6wOTA7UDvQO5A7oDzAAgA5MDugPBA7kAIAOT +A6wDvAO8A7EAIAAyAC4AMgBQAGUAcgBmAGkAbAAgAGcAZQBuAOkAcgBpAGMAbwAgAGQAZQAgAGMAaQBu +AHoAZQBuAHQAbwBzACAAZABhACAARwBhAG0AbQBhACAAMgAsADIAQQBsAGcAZQBtAGUAZQBuACAAZwBy +AGkAagBzACAAZwBhAG0AbQBhACAAMgAsADIALQBwAHIAbwBmAGkAZQBsAFAAZQByAGYAaQBsACAAZwBl +AG4A6QByAGkAYwBvACAAZABlACAAZwBhAG0AbQBhACAAZABlACAAZwByAGkAcwBlAHMAIAAyACwAMg4j +DjEOBw4qDjUOQQ4BDiEOIQ4yDkAOAQ4jDiIOTA4XDjEOSA4nDkQOGwAgADIALgAyAEcAZQBuAGUAbAAg +AEcAcgBpACAARwBhAG0AYQAgADIALAAyAFkAbABlAGkAbgBlAG4AIABoAGEAcgBtAGEAYQBuACAAZwBh +AG0AbQBhACAAMgAsADIAIAAtAHAAcgBvAGYAaQBpAGwAaQBHAGUAbgBlAHIAaQENAGsAaQAgAEcAcgBh +AHkAIABHAGEAbQBtAGEAIAAyAC4AMgAgAHAAcgBvAGYAaQBsAFUAbgBpAHcAZQByAHMAYQBsAG4AeQAg +AHAAcgBvAGYAaQBsACAAcwB6AGEAcgBvAVsAYwBpACAAZwBhAG0AbQBhACAAMgAsADIGOgYnBkUGJwAg +ADIALgAyACAGRAZIBkYAIAYxBkUGJwYvBkoAIAY5BicGRQQeBDEESQQwBE8AIARBBDUEQAQwBE8AIAQz +BDAEPAQ8BDAAIAAyACwAMgAtBD8EQAQ+BEQEOAQ7BEwARwBlAG4AZQByAGkAYwAgAEcAcgBhAHkAIABH +AGEAbQBtAGEAIAAyAC4AMgAgAFAAcgBvAGYAaQBsAGUAAHRleHQAAAAAQ29weXJpZ2h0IEFwcGxlIElu +Yy4sIDIwMTIAAFhZWiAAAAAAAADzUQABAAAAARbMY3VydgAAAAAAAAQAAAAABQAKAA8AFAAZAB4AIwAo +AC0AMgA3ADsAQABFAEoATwBUAFkAXgBjAGgAbQByAHcAfACBAIYAiwCQAJUAmgCfAKQAqQCuALIAtwC8 +AMEAxgDLANAA1QDbAOAA5QDrAPAA9gD7AQEBBwENARMBGQEfASUBKwEyATgBPgFFAUwBUgFZAWABZwFu +AXUBfAGDAYsBkgGaAaEBqQGxAbkBwQHJAdEB2QHhAekB8gH6AgMCDAIUAh0CJgIvAjgCQQJLAlQCXQJn +AnECegKEAo4CmAKiAqwCtgLBAssC1QLgAusC9QMAAwsDFgMhAy0DOANDA08DWgNmA3IDfgOKA5YDogOu +A7oDxwPTA+AD7AP5BAYEEwQgBC0EOwRIBFUEYwRxBH4EjASaBKgEtgTEBNME4QTwBP4FDQUcBSsFOgVJ +BVgFZwV3BYYFlgWmBbUFxQXVBeUF9gYGBhYGJwY3BkgGWQZqBnsGjAadBq8GwAbRBuMG9QcHBxkHKwc9 +B08HYQd0B4YHmQesB78H0gflB/gICwgfCDIIRghaCG4IggiWCKoIvgjSCOcI+wkQCSUJOglPCWQJeQmP +CaQJugnPCeUJ+woRCicKPQpUCmoKgQqYCq4KxQrcCvMLCwsiCzkLUQtpC4ALmAuwC8gL4Qv5DBIMKgxD +DFwMdQyODKcMwAzZDPMNDQ0mDUANWg10DY4NqQ3DDd4N+A4TDi4OSQ5kDn8Omw62DtIO7g8JDyUPQQ9e +D3oPlg+zD88P7BAJECYQQxBhEH4QmxC5ENcQ9RETETERTxFtEYwRqhHJEegSBxImEkUSZBKEEqMSwxLj +EwMTIxNDE2MTgxOkE8UT5RQGFCcUSRRqFIsUrRTOFPAVEhU0FVYVeBWbFb0V4BYDFiYWSRZsFo8WshbW +FvoXHRdBF2UXiReuF9IX9xgbGEAYZRiKGK8Y1Rj6GSAZRRlrGZEZtxndGgQaKhpRGncanhrFGuwbFBs7 +G2MbihuyG9ocAhwqHFIcexyjHMwc9R0eHUcdcB2ZHcMd7B4WHkAeah6UHr4e6R8THz4faR+UH78f6iAV +IEEgbCCYIMQg8CEcIUghdSGhIc4h+yInIlUigiKvIt0jCiM4I2YjlCPCI/AkHyRNJHwkqyTaJQklOCVo +JZclxyX3JicmVyaHJrcm6CcYJ0kneierJ9woDSg/KHEooijUKQYpOClrKZ0p0CoCKjUqaCqbKs8rAis2 +K2krnSvRLAUsOSxuLKIs1y0MLUEtdi2rLeEuFi5MLoIuty7uLyQvWi+RL8cv/jA1MGwwpDDbMRIxSjGC +Mbox8jIqMmMymzLUMw0zRjN/M7gz8TQrNGU0njTYNRM1TTWHNcI1/TY3NnI2rjbpNyQ3YDecN9c4FDhQ +OIw4yDkFOUI5fzm8Ofk6Njp0OrI67zstO2s7qjvoPCc8ZTykPOM9Ij1hPaE94D4gPmA+oD7gPyE/YT+i +P+JAI0BkQKZA50EpQWpBrEHuQjBCckK1QvdDOkN9Q8BEA0RHRIpEzkUSRVVFmkXeRiJGZ0arRvBHNUd7 +R8BIBUhLSJFI10kdSWNJqUnwSjdKfUrESwxLU0uaS+JMKkxyTLpNAk1KTZNN3E4lTm5Ot08AT0lPk0/d +UCdQcVC7UQZRUFGbUeZSMVJ8UsdTE1NfU6pT9lRCVI9U21UoVXVVwlYPVlxWqVb3V0RXklfgWC9YfVjL +WRpZaVm4WgdaVlqmWvVbRVuVW+VcNVyGXNZdJ114XcleGl5sXr1fD19hX7NgBWBXYKpg/GFPYaJh9WJJ +Ypxi8GNDY5dj62RAZJRk6WU9ZZJl52Y9ZpJm6Gc9Z5Nn6Wg/aJZo7GlDaZpp8WpIap9q92tPa6dr/2xX +bK9tCG1gbbluEm5rbsRvHm94b9FwK3CGcOBxOnGVcfByS3KmcwFzXXO4dBR0cHTMdSh1hXXhdj52m3b4 +d1Z3s3gReG54zHkqeYl553pGeqV7BHtje8J8IXyBfOF9QX2hfgF+Yn7CfyN/hH/lgEeAqIEKgWuBzYIw +gpKC9INXg7qEHYSAhOOFR4Wrhg6GcobXhzuHn4gEiGmIzokziZmJ/opkisqLMIuWi/yMY4zKjTGNmI3/ +jmaOzo82j56QBpBukNaRP5GokhGSepLjk02TtpQglIqU9JVflcmWNJaflwqXdZfgmEyYuJkkmZCZ/Jpo +mtWbQpuvnByciZz3nWSd0p5Anq6fHZ+Ln/qgaaDYoUehtqImopajBqN2o+akVqTHpTilqaYapoum/adu +p+CoUqjEqTepqaocqo+rAqt1q+msXKzQrUStuK4trqGvFq+LsACwdbDqsWCx1rJLssKzOLOutCW0nLUT +tYq2AbZ5tvC3aLfguFm40blKucK6O7q1uy67p7whvJu9Fb2Pvgq+hL7/v3q/9cBwwOzBZ8Hjwl/C28NY +w9TEUcTOxUvFyMZGxsPHQce/yD3IvMk6ybnKOMq3yzbLtsw1zLXNNc21zjbOts83z7jQOdC60TzRvtI/ +0sHTRNPG1EnUy9VO1dHWVdbY11zX4Nhk2OjZbNnx2nba+9uA3AXcit0Q3ZbeHN6i3ynfr+A24L3hROHM +4lPi2+Nj4+vkc+T85YTmDeaW5x/nqegy6LzpRunQ6lvq5etw6/vshu0R7ZzuKO6070DvzPBY8OXxcvH/ +8ozzGfOn9DT0wvVQ9d72bfb794r4Gfio+Tj5x/pX+uf7d/wH/Jj9Kf26/kv+3P9t///SLS4vMFokY2xh +c3NuYW1lWCRjbGFzc2VzXxAQTlNCaXRtYXBJbWFnZVJlcKMvMTJaTlNJbWFnZVJlcFhOU09iamVjdNIt +LjQ1V05TQXJyYXmiNDLSLS43OF5OU011dGFibGVBcnJheaM3NDLTOjsOPD0+V05TV2hpdGVcTlNDb2xv +clNwYWNlRDAgMAAQA4AM0i0uQEFXTlNDb2xvcqJAMtItLkNEV05TSW1hZ2WiQzIACAARABoAJAApADIA +NwBJAEwAUQBTAGIAaAB3AH4AhQCSAJkApQCtALwAvgDAAMUAxwDJAMsAzQDUANkA5ADmAOgA6gDvAPIA +9AD2APgA/wEWATIBNAE2E6YTqxO2E78T0hPWE+ET6hPvE/cT+hP/FA4UEhQZFCEULhQzFDUUNxQ8FEQU +RxRMFFQAAAAAAAACAQAAAAAAAABFAAAAAAAAAAAAAAAAAAAUVw diff --git a/i18n/src/main/resources/de.lproj/Command.xib b/i18n/src/main/resources/de.lproj/Command.xib index bfb5deabab..2f57ffa50f 100644 --- a/i18n/src/main/resources/de.lproj/Command.xib +++ b/i18n/src/main/resources/de.lproj/Command.xib @@ -1,7 +1,7 @@ - + - + diff --git a/i18n/src/main/resources/de.lproj/Connection.xib b/i18n/src/main/resources/de.lproj/Connection.xib index 1e6b790d59..b2bdb4bec3 100644 --- a/i18n/src/main/resources/de.lproj/Connection.xib +++ b/i18n/src/main/resources/de.lproj/Connection.xib @@ -1,7 +1,7 @@ - + - + @@ -76,7 +76,7 @@ - + diff --git a/i18n/src/main/resources/de.lproj/Info.xib b/i18n/src/main/resources/de.lproj/Info.xib index 564a4fdf5b..b52a7b2713 100644 --- a/i18n/src/main/resources/de.lproj/Info.xib +++ b/i18n/src/main/resources/de.lproj/Info.xib @@ -1,7 +1,7 @@ - + - + @@ -89,7 +89,7 @@ - + @@ -1017,21 +1017,21 @@ - + - + - + @@ -1121,21 +1121,21 @@ - + - + - + @@ -1250,27 +1250,27 @@ diff --git a/i18n/src/main/resources/de.lproj/Login.strings b/i18n/src/main/resources/de.lproj/Login.strings index 425db0ec50..6cbd89ec72 100644 Binary files a/i18n/src/main/resources/de.lproj/Login.strings and b/i18n/src/main/resources/de.lproj/Login.strings differ diff --git a/i18n/src/main/resources/de.lproj/Main.xib b/i18n/src/main/resources/de.lproj/Main.xib index 2d50f44600..422707bdd3 100644 --- a/i18n/src/main/resources/de.lproj/Main.xib +++ b/i18n/src/main/resources/de.lproj/Main.xib @@ -1,7 +1,7 @@ - + - + @@ -159,19 +159,19 @@ - + - + - + @@ -321,12 +321,12 @@ CA - + - + @@ -566,6 +566,10 @@ CA - + + + + + diff --git a/i18n/src/main/resources/de.lproj/Preferences.xib b/i18n/src/main/resources/de.lproj/Preferences.xib index 023e92e1a3..acd02a6c1a 100644 --- a/i18n/src/main/resources/de.lproj/Preferences.xib +++ b/i18n/src/main/resources/de.lproj/Preferences.xib @@ -1,7 +1,7 @@ - + - + @@ -266,7 +266,7 @@ - + @@ -307,7 +307,7 @@ - + @@ -538,7 +538,7 @@ - + @@ -578,7 +578,7 @@ - + @@ -613,7 +613,7 @@ - + @@ -642,7 +642,7 @@ - + @@ -1218,7 +1218,7 @@ - + @@ -1250,7 +1250,7 @@ - + @@ -1290,7 +1290,7 @@ - + @@ -1363,7 +1363,7 @@ - + @@ -1558,7 +1558,7 @@ - + @@ -1618,7 +1618,7 @@ - + @@ -1643,7 +1643,7 @@ - + @@ -1685,7 +1685,7 @@ - + @@ -1713,7 +1713,7 @@ - + @@ -1741,7 +1741,7 @@ - + @@ -1769,7 +1769,7 @@ - + @@ -1804,7 +1804,7 @@ - + @@ -1887,7 +1887,7 @@ - + @@ -1946,7 +1946,7 @@ - + @@ -1974,7 +1974,7 @@ - + @@ -2002,7 +2002,7 @@ - + diff --git a/i18n/src/main/resources/de.lproj/Profiles.xib b/i18n/src/main/resources/de.lproj/Profiles.xib index 0997f7ba6e..a33cdb7df5 100644 --- a/i18n/src/main/resources/de.lproj/Profiles.xib +++ b/i18n/src/main/resources/de.lproj/Profiles.xib @@ -1,7 +1,7 @@ - + - + diff --git a/i18n/src/main/resources/de.lproj/Prompt.xib b/i18n/src/main/resources/de.lproj/Prompt.xib index 0d09dfcc16..55505d2838 100644 --- a/i18n/src/main/resources/de.lproj/Prompt.xib +++ b/i18n/src/main/resources/de.lproj/Prompt.xib @@ -1,7 +1,7 @@ - + - + @@ -95,7 +95,7 @@ Gw - + diff --git a/i18n/src/main/resources/de.lproj/S3.strings b/i18n/src/main/resources/de.lproj/S3.strings index a82a6a03af..c6f73a7734 100644 Binary files a/i18n/src/main/resources/de.lproj/S3.strings and b/i18n/src/main/resources/de.lproj/S3.strings differ diff --git a/i18n/src/main/resources/de.lproj/Updater.strings b/i18n/src/main/resources/de.lproj/Updater.strings index 22a6ec71fc..f2231cf081 100644 Binary files a/i18n/src/main/resources/de.lproj/Updater.strings and b/i18n/src/main/resources/de.lproj/Updater.strings differ diff --git a/i18n/src/main/resources/el.lproj/Bookmark.xib b/i18n/src/main/resources/el.lproj/Bookmark.xib index 323dee8083..534bf88a3e 100644 --- a/i18n/src/main/resources/el.lproj/Bookmark.xib +++ b/i18n/src/main/resources/el.lproj/Bookmark.xib @@ -1,7 +1,7 @@ - + - + @@ -92,7 +92,7 @@ - + @@ -489,95 +489,95 @@ YnBsaXN0MDDUAQIDBAUGBwpYJHZlcnNpb25ZJGFyY2hpdmVyVCR0b3BYJG9iamVjdHMSAAGGoF8QD05T -S2V5ZWRBcmNoaXZlctEICVRyb290gAGuCwwZGh8UJCkqMTQ3PUBVJG51bGzWDQ4PEBESExQVFhcYVk5T -U2l6ZV5OU1Jlc2l6aW5nTW9kZVYkY2xhc3NcTlNJbWFnZUZsYWdzVk5TUmVwc1dOU0NvbG9ygAIQAIAN -EiDDAACAA4ALVnsxLCAxfdIbDxweWk5TLm9iamVjdHOhHYAEgArSGw8gI6IhIoAFgAaACdMPJSYnKBRf -EBROU1RJRkZSZXByZXNlbnRhdGlvbl8QGU5TSW50ZXJuYWxMYXlvdXREaXJlY3Rpb26ACIAHTxESbE1N -ACoAAAAKAAAAEAEAAAMAAAABAAEAAAEBAAMAAAABAAEAAAECAAMAAAACAAgACAEDAAMAAAABAAEAAAEG -AAMAAAABAAEAAAEKAAMAAAABAAEAAAERAAQAAAABAAAACAESAAMAAAABAAEAAAEVAAMAAAABAAIAAAEW -AAMAAAABAAEAAAEXAAQAAAABAAAAAgEcAAMAAAABAAEAAAEoAAMAAAABAAIAAAFSAAMAAAABAAEAAAFT -AAMAAAACAAEAAYdzAAcAABGcAAAA0AAAAAAAABGcYXBwbAIAAABtbnRyR1JBWVhZWiAH3AAIABcADwAu -AA9hY3NwQVBQTAAAAABub25lAAAAAAAAAAAAAAAAAAAAAAAA9tYAAQAAAADTLWFwcGwAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAVkZXNjAAAAwAAAAHlkc2NtAAABPAAA -CBpjcHJ0AAAJWAAAACN3dHB0AAAJfAAAABRrVFJDAAAJkAAACAxkZXNjAAAAAAAAAB9HZW5lcmljIEdy -YXkgR2FtbWEgMi4yIFByb2ZpbGUAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAbWx1YwAAAAAAAAAfAAAADHNr -U0sAAAAuAAABhGRhREsAAAA6AAABsmNhRVMAAAA4AAAB7HZpVk4AAABAAAACJHB0QlIAAABKAAACZHVr -VUEAAAAsAAACrmZyRlUAAAA+AAAC2mh1SFUAAAA0AAADGHpoVFcAAAAaAAADTGtvS1IAAAAiAAADZm5i -Tk8AAAA6AAADiGNzQ1oAAAAoAAADwmhlSUwAAAAkAAAD6nJvUk8AAAAqAAAEDmRlREUAAABOAAAEOGl0 -SVQAAABOAAAEhnN2U0UAAAA4AAAE1HpoQ04AAAAaAAAFDGphSlAAAAAmAAAFJmVsR1IAAAAqAAAFTHB0 -UE8AAABSAAAFdm5sTkwAAABAAAAFyGVzRVMAAABMAAAGCHRoVEgAAAAyAAAGVHRyVFIAAAAkAAAGhmZp -RkkAAABGAAAGqmhySFIAAAA+AAAG8HBsUEwAAABKAAAHLmFyRUcAAAAsAAAHeHJ1UlUAAAA6AAAHpGVu -VVMAAAA8AAAH3gBWAWEAZQBvAGIAZQBjAG4A4QAgAHMAaQB2AOEAIABnAGEAbQBhACAAMgAsADIARwBl -AG4AZQByAGkAcwBrACAAZwByAOUAIAAyACwAMgAgAGcAYQBtAG0AYQAtAHAAcgBvAGYAaQBsAEcAYQBt -AG0AYQAgAGQAZQAgAGcAcgBpAHMAbwBzACAAZwBlAG4A6AByAGkAYwBhACAAMgAuADIAQx6lAHUAIABo -AOwAbgBoACAATQDgAHUAIAB4AOEAbQAgAEMAaAB1AG4AZwAgAEcAYQBtAG0AYQAgADIALgAyAFAAZQBy -AGYAaQBsACAARwBlAG4A6QByAGkAYwBvACAAZABhACAARwBhAG0AYQAgAGQAZQAgAEMAaQBuAHoAYQBz -ACAAMgAsADIEFwQwBDMEMAQ7BEwEPQQwACAARwByAGEAeQAtBDMEMAQ8BDAAIAAyAC4AMgBQAHIAbwBm -AGkAbAAgAGcA6QBuAOkAcgBpAHEAdQBlACAAZwByAGkAcwAgAGcAYQBtAG0AYQAgADIALAAyAMEAbAB0 -AGEAbADhAG4AbwBzACAAcwB6APwAcgBrAGUAIABnAGEAbQBtAGEAIAAyAC4AMpAadShwcJaOUUlepgAy -AC4AMoJyX2ljz4/wx3y8GAAg1ozAyQAgrBC5yAAgADIALgAyACDVBLhc0wzHfABHAGUAbgBlAHIAaQBz -AGsAIABnAHIA5QAgAGcAYQBtAG0AYQAgADIALAAyAC0AcAByAG8AZgBpAGwATwBiAGUAYwBuAOEAIAFh -AGUAZADhACAAZwBhAG0AYQAgADIALgAyBdIF0AXeBdQAIAXQBeQF1QXoACAF2wXcBdwF2QAgADIALgAy -AEcAYQBtAGEAIABnAHIAaQAgAGcAZQBuAGUAcgBpAGMBAwAgADIALAAyAEEAbABsAGcAZQBtAGUAaQBu -AGUAcwAgAEcAcgBhAHUAcwB0AHUAZgBlAG4ALQBQAHIAbwBmAGkAbAAgAEcAYQBtAG0AYQAgADIALAAy -AFAAcgBvAGYAaQBsAG8AIABnAHIAaQBnAGkAbwAgAGcAZQBuAGUAcgBpAGMAbwAgAGQAZQBsAGwAYQAg -AGcAYQBtAG0AYQAgADIALAAyAEcAZQBuAGUAcgBpAHMAawAgAGcAcgDlACAAMgAsADIAIABnAGEAbQBt -AGEAcAByAG8AZgBpAGxmbpAacHBepnz7ZXAAMgAuADJjz4/wZYdO9k4AgiwwsDDsMKQwrDDzMN4AIAAy -AC4AMgAgMNcw7TDVMKEwpDDrA5MDtQO9A7kDugPMACADkwO6A8EDuQAgA5MDrAO8A7wDsQAgADIALgAy -AFAAZQByAGYAaQBsACAAZwBlAG4A6QByAGkAYwBvACAAZABlACAAYwBpAG4AegBlAG4AdABvAHMAIABk -AGEAIABHAGEAbQBtAGEAIAAyACwAMgBBAGwAZwBlAG0AZQBlAG4AIABnAHIAaQBqAHMAIABnAGEAbQBt -AGEAIAAyACwAMgAtAHAAcgBvAGYAaQBlAGwAUABlAHIAZgBpAGwAIABnAGUAbgDpAHIAaQBjAG8AIABk -AGUAIABnAGEAbQBtAGEAIABkAGUAIABnAHIAaQBzAGUAcwAgADIALAAyDiMOMQ4HDioONQ5BDgEOIQ4h -DjIOQA4BDiMOIg5MDhcOMQ5IDicORA4bACAAMgAuADIARwBlAG4AZQBsACAARwByAGkAIABHAGEAbQBh -ACAAMgAsADIAWQBsAGUAaQBuAGUAbgAgAGgAYQByAG0AYQBhAG4AIABnAGEAbQBtAGEAIAAyACwAMgAg -AC0AcAByAG8AZgBpAGkAbABpAEcAZQBuAGUAcgBpAQ0AawBpACAARwByAGEAeQAgAEcAYQBtAG0AYQAg -ADIALgAyACAAcAByAG8AZgBpAGwAVQBuAGkAdwBlAHIAcwBhAGwAbgB5ACAAcAByAG8AZgBpAGwAIABz -AHoAYQByAG8BWwBjAGkAIABnAGEAbQBtAGEAIAAyACwAMgY6BicGRQYnACAAMgAuADIAIAZEBkgGRgAg -BjEGRQYnBi8GSgAgBjkGJwZFBB4EMQRJBDAETwAgBEEENQRABDAETwAgBDMEMAQ8BDwEMAAgADIALAAy -AC0EPwRABD4ERAQ4BDsETABHAGUAbgBlAHIAaQBjACAARwByAGEAeQAgAEcAYQBtAG0AYQAgADIALgAy -ACAAUAByAG8AZgBpAGwAZQAAdGV4dAAAAABDb3B5cmlnaHQgQXBwbGUgSW5jLiwgMjAxMgAAWFlaIAAA -AAAAAPNRAAEAAAABFsxjdXJ2AAAAAAAABAAAAAAFAAoADwAUABkAHgAjACgALQAyADcAOwBAAEUASgBP -AFQAWQBeAGMAaABtAHIAdwB8AIEAhgCLAJAAlQCaAJ8ApACpAK4AsgC3ALwAwQDGAMsA0ADVANsA4ADl -AOsA8AD2APsBAQEHAQ0BEwEZAR8BJQErATIBOAE+AUUBTAFSAVkBYAFnAW4BdQF8AYMBiwGSAZoBoQGp -AbEBuQHBAckB0QHZAeEB6QHyAfoCAwIMAhQCHQImAi8COAJBAksCVAJdAmcCcQJ6AoQCjgKYAqICrAK2 -AsECywLVAuAC6wL1AwADCwMWAyEDLQM4A0MDTwNaA2YDcgN+A4oDlgOiA64DugPHA9MD4APsA/kEBgQT -BCAELQQ7BEgEVQRjBHEEfgSMBJoEqAS2BMQE0wThBPAE/gUNBRwFKwU6BUkFWAVnBXcFhgWWBaYFtQXF -BdUF5QX2BgYGFgYnBjcGSAZZBmoGewaMBp0GrwbABtEG4wb1BwcHGQcrBz0HTwdhB3QHhgeZB6wHvwfS -B+UH+AgLCB8IMghGCFoIbgiCCJYIqgi+CNII5wj7CRAJJQk6CU8JZAl5CY8JpAm6Cc8J5Qn7ChEKJwo9 -ClQKagqBCpgKrgrFCtwK8wsLCyILOQtRC2kLgAuYC7ALyAvhC/kMEgwqDEMMXAx1DI4MpwzADNkM8w0N -DSYNQA1aDXQNjg2pDcMN3g34DhMOLg5JDmQOfw6bDrYO0g7uDwkPJQ9BD14Peg+WD7MPzw/sEAkQJhBD -EGEQfhCbELkQ1xD1ERMRMRFPEW0RjBGqEckR6BIHEiYSRRJkEoQSoxLDEuMTAxMjE0MTYxODE6QTxRPl -FAYUJxRJFGoUixStFM4U8BUSFTQVVhV4FZsVvRXgFgMWJhZJFmwWjxayFtYW+hcdF0EXZReJF64X0hf3 -GBsYQBhlGIoYrxjVGPoZIBlFGWsZkRm3Gd0aBBoqGlEadxqeGsUa7BsUGzsbYxuKG7Ib2hwCHCocUhx7 -HKMczBz1HR4dRx1wHZkdwx3sHhYeQB5qHpQevh7pHxMfPh9pH5Qfvx/qIBUgQSBsIJggxCDwIRwhSCF1 -IaEhziH7IiciVSKCIq8i3SMKIzgjZiOUI8Ij8CQfJE0kfCSrJNolCSU4JWgllyXHJfcmJyZXJocmtybo -JxgnSSd6J6sn3CgNKD8ocSiiKNQpBik4KWspnSnQKgIqNSpoKpsqzysCKzYraSudK9EsBSw5LG4soizX -LQwtQS12Last4S4WLkwugi63Lu4vJC9aL5Evxy/+MDUwbDCkMNsxEjFKMYIxujHyMioyYzKbMtQzDTNG -M38zuDPxNCs0ZTSeNNg1EzVNNYc1wjX9Njc2cjauNuk3JDdgN5w31zgUOFA4jDjIOQU5Qjl/Obw5+To2 -OnQ6sjrvOy07azuqO+g8JzxlPKQ84z0iPWE9oT3gPiA+YD6gPuA/IT9hP6I/4kAjQGRApkDnQSlBakGs -Qe5CMEJyQrVC90M6Q31DwEQDREdEikTORRJFVUWaRd5GIkZnRqtG8Ec1R3tHwEgFSEtIkUjXSR1JY0mp -SfBKN0p9SsRLDEtTS5pL4kwqTHJMuk0CTUpNk03cTiVObk63TwBPSU+TT91QJ1BxULtRBlFQUZtR5lIx -UnxSx1MTU19TqlP2VEJUj1TbVShVdVXCVg9WXFapVvdXRFeSV+BYL1h9WMtZGllpWbhaB1pWWqZa9VtF -W5Vb5Vw1XIZc1l0nXXhdyV4aXmxevV8PX2Ffs2AFYFdgqmD8YU9homH1YklinGLwY0Njl2PrZEBklGTp -ZT1lkmXnZj1mkmboZz1nk2fpaD9olmjsaUNpmmnxakhqn2r3a09rp2v/bFdsr20IbWBtuW4SbmtuxG8e -b3hv0XArcIZw4HE6cZVx8HJLcqZzAXNdc7h0FHRwdMx1KHWFdeF2Pnabdvh3VnezeBF4bnjMeSp5iXnn -ekZ6pXsEe2N7wnwhfIF84X1BfaF+AX5ifsJ/I3+Ef+WAR4CogQqBa4HNgjCCkoL0g1eDuoQdhICE44VH -hauGDoZyhteHO4efiASIaYjOiTOJmYn+imSKyoswi5aL/IxjjMqNMY2Yjf+OZo7OjzaPnpAGkG6Q1pE/ -kaiSEZJ6kuOTTZO2lCCUipT0lV+VyZY0lp+XCpd1l+CYTJi4mSSZkJn8mmia1ZtCm6+cHJyJnPedZJ3S -nkCerp8dn4uf+qBpoNihR6G2oiailqMGo3aj5qRWpMelOKWpphqmi6b9p26n4KhSqMSpN6mpqhyqj6sC -q3Wr6axcrNCtRK24ri2uoa8Wr4uwALB1sOqxYLHWskuywrM4s660JbSctRO1irYBtnm28Ldot+C4WbjR -uUq5wro7urW7LrunvCG8m70VvY++Cr6Evv+/er/1wHDA7MFnwePCX8Lbw1jD1MRRxM7FS8XIxkbGw8dB -x7/IPci8yTrJuco4yrfLNsu2zDXMtc01zbXONs62zzfPuNA50LrRPNG+0j/SwdNE08bUSdTL1U7V0dZV -1tjXXNfg2GTY6Nls2fHadtr724DcBdyK3RDdlt4c3qLfKd+v4DbgveFE4cziU+Lb42Pj6+Rz5PzlhOYN -5pbnH+ep6DLovOlG6dDqW+rl63Dr++yG7RHtnO4o7rTvQO/M8Fjw5fFy8f/yjPMZ86f0NPTC9VD13vZt -9vv3ivgZ+Kj5OPnH+lf65/t3/Af8mP0p/br+S/7c/23//9IrLC0uWiRjbGFzc25hbWVYJGNsYXNzZXNf -EBBOU0JpdG1hcEltYWdlUmVwoy0vMFpOU0ltYWdlUmVwWE5TT2JqZWN00issMjNXTlNBcnJheaIyMNIr -LDU2Xk5TTXV0YWJsZUFycmF5ozUyMNM4OQ86OzxXTlNXaGl0ZVxOU0NvbG9yU3BhY2VEMCAwABADgAzS -Kyw+P1dOU0NvbG9yoj4w0issQUJXTlNJbWFnZaJBMAAIABEAGgAkACkAMgA3AEkATABRAFMAYgBoAHUA -fACLAJIAnwCmAK4AsACyALQAuQC7AL0AxADJANQA1gDYANoA3wDiAOQA5gDoAO8BBgEiASQBJhOWE5sT -phOvE8ITxhPRE9oT3xPnE+oT7xP+FAIUCRQRFB4UIxQlFCcULBQ0FDcUPBREAAAAAAAAAgEAAAAAAAAA -QwAAAAAAAAAAAAAAAAAAFEc +S2V5ZWRBcmNoaXZlctEICVRyb290gAGuCwwbHCEaJissMzY5P0JVJG51bGzXDQ4PEBESExQVFhcYGRpW +TlNTaXplViRjbGFzc1xOU0ltYWdlRmxhZ3NWTlNSZXBzW05TVGludENvbG9yV05TQ29sb3JeTlNSZXNp +emluZ01vZGWAAoANEiDDAACAA4AAgAsQAFZ7MSwgMX3SHQ4eIFpOUy5vYmplY3RzoR+ABIAK0h0OIiWi +IySABYAGgAnTDicoKSoaXxAUTlNUSUZGUmVwcmVzZW50YXRpb25fEBlOU0ludGVybmFsTGF5b3V0RGly +ZWN0aW9ugAiAB08REmxNTQAqAAAACgAAABABAAADAAAAAQABAAABAQADAAAAAQABAAABAgADAAAAAgAI +AAgBAwADAAAAAQABAAABBgADAAAAAQABAAABCgADAAAAAQABAAABEQAEAAAAAQAAAAgBEgADAAAAAQAB +AAABFQADAAAAAQACAAABFgADAAAAAQABAAABFwAEAAAAAQAAAAIBHAADAAAAAQABAAABKAADAAAAAQAC +AAABUgADAAAAAQABAAABUwADAAAAAgABAAGHcwAHAAARnAAAANAAAAAAAAARnGFwcGwCAAAAbW50ckdS +QVlYWVogB9wACAAXAA8ALgAPYWNzcEFQUEwAAAAAbm9uZQAAAAAAAAAAAAAAAAAAAAAAAPbWAAEAAAAA +0y1hcHBsAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFZGVzYwAA +AMAAAAB5ZHNjbQAAATwAAAgaY3BydAAACVgAAAAjd3RwdAAACXwAAAAUa1RSQwAACZAAAAgMZGVzYwAA +AAAAAAAfR2VuZXJpYyBHcmF5IEdhbW1hIDIuMiBQcm9maWxlAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAG1s +dWMAAAAAAAAAHwAAAAxza1NLAAAALgAAAYRkYURLAAAAOgAAAbJjYUVTAAAAOAAAAex2aVZOAAAAQAAA +AiRwdEJSAAAASgAAAmR1a1VBAAAALAAAAq5mckZVAAAAPgAAAtpodUhVAAAANAAAAxh6aFRXAAAAGgAA +A0xrb0tSAAAAIgAAA2ZuYk5PAAAAOgAAA4hjc0NaAAAAKAAAA8JoZUlMAAAAJAAAA+pyb1JPAAAAKgAA +BA5kZURFAAAATgAABDhpdElUAAAATgAABIZzdlNFAAAAOAAABNR6aENOAAAAGgAABQxqYUpQAAAAJgAA +BSZlbEdSAAAAKgAABUxwdFBPAAAAUgAABXZubE5MAAAAQAAABchlc0VTAAAATAAABgh0aFRIAAAAMgAA +BlR0clRSAAAAJAAABoZmaUZJAAAARgAABqpockhSAAAAPgAABvBwbFBMAAAASgAABy5hckVHAAAALAAA +B3hydVJVAAAAOgAAB6RlblVTAAAAPAAAB94AVgFhAGUAbwBiAGUAYwBuAOEAIABzAGkAdgDhACAAZwBh +AG0AYQAgADIALAAyAEcAZQBuAGUAcgBpAHMAawAgAGcAcgDlACAAMgAsADIAIABnAGEAbQBtAGEALQBw +AHIAbwBmAGkAbABHAGEAbQBtAGEAIABkAGUAIABnAHIAaQBzAG8AcwAgAGcAZQBuAOgAcgBpAGMAYQAg +ADIALgAyAEMepQB1ACAAaADsAG4AaAAgAE0A4AB1ACAAeADhAG0AIABDAGgAdQBuAGcAIABHAGEAbQBt +AGEAIAAyAC4AMgBQAGUAcgBmAGkAbAAgAEcAZQBuAOkAcgBpAGMAbwAgAGQAYQAgAEcAYQBtAGEAIABk +AGUAIABDAGkAbgB6AGEAcwAgADIALAAyBBcEMAQzBDAEOwRMBD0EMAAgAEcAcgBhAHkALQQzBDAEPAQw +ACAAMgAuADIAUAByAG8AZgBpAGwAIABnAOkAbgDpAHIAaQBxAHUAZQAgAGcAcgBpAHMAIABnAGEAbQBt +AGEAIAAyACwAMgDBAGwAdABhAGwA4QBuAG8AcwAgAHMAegD8AHIAawBlACAAZwBhAG0AbQBhACAAMgAu +ADKQGnUocHCWjlFJXqYAMgAuADKCcl9pY8+P8Md8vBgAINaMwMkAIKwQucgAIAAyAC4AMgAg1QS4XNMM +x3wARwBlAG4AZQByAGkAcwBrACAAZwByAOUAIABnAGEAbQBtAGEAIAAyACwAMgAtAHAAcgBvAGYAaQBs +AE8AYgBlAGMAbgDhACABYQBlAGQA4QAgAGcAYQBtAGEAIAAyAC4AMgXSBdAF3gXUACAF0AXkBdUF6AAg +BdsF3AXcBdkAIAAyAC4AMgBHAGEAbQBhACAAZwByAGkAIABnAGUAbgBlAHIAaQBjAQMAIAAyACwAMgBB +AGwAbABnAGUAbQBlAGkAbgBlAHMAIABHAHIAYQB1AHMAdAB1AGYAZQBuAC0AUAByAG8AZgBpAGwAIABH +AGEAbQBtAGEAIAAyACwAMgBQAHIAbwBmAGkAbABvACAAZwByAGkAZwBpAG8AIABnAGUAbgBlAHIAaQBj +AG8AIABkAGUAbABsAGEAIABnAGEAbQBtAGEAIAAyACwAMgBHAGUAbgBlAHIAaQBzAGsAIABnAHIA5QAg +ADIALAAyACAAZwBhAG0AbQBhAHAAcgBvAGYAaQBsZm6QGnBwXqZ8+2VwADIALgAyY8+P8GWHTvZOAIIs +MLAw7DCkMKww8zDeACAAMgAuADIAIDDXMO0w1TChMKQw6wOTA7UDvQO5A7oDzAAgA5MDugPBA7kAIAOT +A6wDvAO8A7EAIAAyAC4AMgBQAGUAcgBmAGkAbAAgAGcAZQBuAOkAcgBpAGMAbwAgAGQAZQAgAGMAaQBu +AHoAZQBuAHQAbwBzACAAZABhACAARwBhAG0AbQBhACAAMgAsADIAQQBsAGcAZQBtAGUAZQBuACAAZwBy +AGkAagBzACAAZwBhAG0AbQBhACAAMgAsADIALQBwAHIAbwBmAGkAZQBsAFAAZQByAGYAaQBsACAAZwBl +AG4A6QByAGkAYwBvACAAZABlACAAZwBhAG0AbQBhACAAZABlACAAZwByAGkAcwBlAHMAIAAyACwAMg4j +DjEOBw4qDjUOQQ4BDiEOIQ4yDkAOAQ4jDiIOTA4XDjEOSA4nDkQOGwAgADIALgAyAEcAZQBuAGUAbAAg +AEcAcgBpACAARwBhAG0AYQAgADIALAAyAFkAbABlAGkAbgBlAG4AIABoAGEAcgBtAGEAYQBuACAAZwBh +AG0AbQBhACAAMgAsADIAIAAtAHAAcgBvAGYAaQBpAGwAaQBHAGUAbgBlAHIAaQENAGsAaQAgAEcAcgBh +AHkAIABHAGEAbQBtAGEAIAAyAC4AMgAgAHAAcgBvAGYAaQBsAFUAbgBpAHcAZQByAHMAYQBsAG4AeQAg +AHAAcgBvAGYAaQBsACAAcwB6AGEAcgBvAVsAYwBpACAAZwBhAG0AbQBhACAAMgAsADIGOgYnBkUGJwAg +ADIALgAyACAGRAZIBkYAIAYxBkUGJwYvBkoAIAY5BicGRQQeBDEESQQwBE8AIARBBDUEQAQwBE8AIAQz +BDAEPAQ8BDAAIAAyACwAMgAtBD8EQAQ+BEQEOAQ7BEwARwBlAG4AZQByAGkAYwAgAEcAcgBhAHkAIABH +AGEAbQBtAGEAIAAyAC4AMgAgAFAAcgBvAGYAaQBsAGUAAHRleHQAAAAAQ29weXJpZ2h0IEFwcGxlIElu +Yy4sIDIwMTIAAFhZWiAAAAAAAADzUQABAAAAARbMY3VydgAAAAAAAAQAAAAABQAKAA8AFAAZAB4AIwAo +AC0AMgA3ADsAQABFAEoATwBUAFkAXgBjAGgAbQByAHcAfACBAIYAiwCQAJUAmgCfAKQAqQCuALIAtwC8 +AMEAxgDLANAA1QDbAOAA5QDrAPAA9gD7AQEBBwENARMBGQEfASUBKwEyATgBPgFFAUwBUgFZAWABZwFu +AXUBfAGDAYsBkgGaAaEBqQGxAbkBwQHJAdEB2QHhAekB8gH6AgMCDAIUAh0CJgIvAjgCQQJLAlQCXQJn +AnECegKEAo4CmAKiAqwCtgLBAssC1QLgAusC9QMAAwsDFgMhAy0DOANDA08DWgNmA3IDfgOKA5YDogOu +A7oDxwPTA+AD7AP5BAYEEwQgBC0EOwRIBFUEYwRxBH4EjASaBKgEtgTEBNME4QTwBP4FDQUcBSsFOgVJ +BVgFZwV3BYYFlgWmBbUFxQXVBeUF9gYGBhYGJwY3BkgGWQZqBnsGjAadBq8GwAbRBuMG9QcHBxkHKwc9 +B08HYQd0B4YHmQesB78H0gflB/gICwgfCDIIRghaCG4IggiWCKoIvgjSCOcI+wkQCSUJOglPCWQJeQmP +CaQJugnPCeUJ+woRCicKPQpUCmoKgQqYCq4KxQrcCvMLCwsiCzkLUQtpC4ALmAuwC8gL4Qv5DBIMKgxD +DFwMdQyODKcMwAzZDPMNDQ0mDUANWg10DY4NqQ3DDd4N+A4TDi4OSQ5kDn8Omw62DtIO7g8JDyUPQQ9e +D3oPlg+zD88P7BAJECYQQxBhEH4QmxC5ENcQ9RETETERTxFtEYwRqhHJEegSBxImEkUSZBKEEqMSwxLj +EwMTIxNDE2MTgxOkE8UT5RQGFCcUSRRqFIsUrRTOFPAVEhU0FVYVeBWbFb0V4BYDFiYWSRZsFo8WshbW +FvoXHRdBF2UXiReuF9IX9xgbGEAYZRiKGK8Y1Rj6GSAZRRlrGZEZtxndGgQaKhpRGncanhrFGuwbFBs7 +G2MbihuyG9ocAhwqHFIcexyjHMwc9R0eHUcdcB2ZHcMd7B4WHkAeah6UHr4e6R8THz4faR+UH78f6iAV +IEEgbCCYIMQg8CEcIUghdSGhIc4h+yInIlUigiKvIt0jCiM4I2YjlCPCI/AkHyRNJHwkqyTaJQklOCVo +JZclxyX3JicmVyaHJrcm6CcYJ0kneierJ9woDSg/KHEooijUKQYpOClrKZ0p0CoCKjUqaCqbKs8rAis2 +K2krnSvRLAUsOSxuLKIs1y0MLUEtdi2rLeEuFi5MLoIuty7uLyQvWi+RL8cv/jA1MGwwpDDbMRIxSjGC +Mbox8jIqMmMymzLUMw0zRjN/M7gz8TQrNGU0njTYNRM1TTWHNcI1/TY3NnI2rjbpNyQ3YDecN9c4FDhQ +OIw4yDkFOUI5fzm8Ofk6Njp0OrI67zstO2s7qjvoPCc8ZTykPOM9Ij1hPaE94D4gPmA+oD7gPyE/YT+i +P+JAI0BkQKZA50EpQWpBrEHuQjBCckK1QvdDOkN9Q8BEA0RHRIpEzkUSRVVFmkXeRiJGZ0arRvBHNUd7 +R8BIBUhLSJFI10kdSWNJqUnwSjdKfUrESwxLU0uaS+JMKkxyTLpNAk1KTZNN3E4lTm5Ot08AT0lPk0/d +UCdQcVC7UQZRUFGbUeZSMVJ8UsdTE1NfU6pT9lRCVI9U21UoVXVVwlYPVlxWqVb3V0RXklfgWC9YfVjL +WRpZaVm4WgdaVlqmWvVbRVuVW+VcNVyGXNZdJ114XcleGl5sXr1fD19hX7NgBWBXYKpg/GFPYaJh9WJJ +Ypxi8GNDY5dj62RAZJRk6WU9ZZJl52Y9ZpJm6Gc9Z5Nn6Wg/aJZo7GlDaZpp8WpIap9q92tPa6dr/2xX +bK9tCG1gbbluEm5rbsRvHm94b9FwK3CGcOBxOnGVcfByS3KmcwFzXXO4dBR0cHTMdSh1hXXhdj52m3b4 +d1Z3s3gReG54zHkqeYl553pGeqV7BHtje8J8IXyBfOF9QX2hfgF+Yn7CfyN/hH/lgEeAqIEKgWuBzYIw +gpKC9INXg7qEHYSAhOOFR4Wrhg6GcobXhzuHn4gEiGmIzokziZmJ/opkisqLMIuWi/yMY4zKjTGNmI3/ +jmaOzo82j56QBpBukNaRP5GokhGSepLjk02TtpQglIqU9JVflcmWNJaflwqXdZfgmEyYuJkkmZCZ/Jpo +mtWbQpuvnByciZz3nWSd0p5Anq6fHZ+Ln/qgaaDYoUehtqImopajBqN2o+akVqTHpTilqaYapoum/adu +p+CoUqjEqTepqaocqo+rAqt1q+msXKzQrUStuK4trqGvFq+LsACwdbDqsWCx1rJLssKzOLOutCW0nLUT +tYq2AbZ5tvC3aLfguFm40blKucK6O7q1uy67p7whvJu9Fb2Pvgq+hL7/v3q/9cBwwOzBZ8Hjwl/C28NY +w9TEUcTOxUvFyMZGxsPHQce/yD3IvMk6ybnKOMq3yzbLtsw1zLXNNc21zjbOts83z7jQOdC60TzRvtI/ +0sHTRNPG1EnUy9VO1dHWVdbY11zX4Nhk2OjZbNnx2nba+9uA3AXcit0Q3ZbeHN6i3ynfr+A24L3hROHM +4lPi2+Nj4+vkc+T85YTmDeaW5x/nqegy6LzpRunQ6lvq5etw6/vshu0R7ZzuKO6070DvzPBY8OXxcvH/ +8ozzGfOn9DT0wvVQ9d72bfb794r4Gfio+Tj5x/pX+uf7d/wH/Jj9Kf26/kv+3P9t///SLS4vMFokY2xh +c3NuYW1lWCRjbGFzc2VzXxAQTlNCaXRtYXBJbWFnZVJlcKMvMTJaTlNJbWFnZVJlcFhOU09iamVjdNIt +LjQ1V05TQXJyYXmiNDLSLS43OF5OU011dGFibGVBcnJheaM3NDLTOjsOPD0+V05TV2hpdGVcTlNDb2xv +clNwYWNlRDAgMAAQA4AM0i0uQEFXTlNDb2xvcqJAMtItLkNEV05TSW1hZ2WiQzIACAARABoAJAApADIA +NwBJAEwAUQBTAGIAaAB3AH4AhQCSAJkApQCtALwAvgDAAMUAxwDJAMsAzQDUANkA5ADmAOgA6gDvAPIA +9AD2APgA/wEWATIBNAE2E6YTqxO2E78T0hPWE+ET6hPvE/cT+hP/FA4UEhQZFCEULhQzFDUUNxQ8FEQU +RxRMFFQAAAAAAAACAQAAAAAAAABFAAAAAAAAAAAAAAAAAAAUVw diff --git a/i18n/src/main/resources/el.lproj/Browser.xib b/i18n/src/main/resources/el.lproj/Browser.xib index 6e958d3573..cfc1506e04 100644 --- a/i18n/src/main/resources/el.lproj/Browser.xib +++ b/i18n/src/main/resources/el.lproj/Browser.xib @@ -1,7 +1,7 @@ - + - + @@ -38,7 +38,7 @@ - + @@ -62,27 +62,27 @@ @@ -256,7 +256,7 @@ - + @@ -553,95 +553,95 @@ DQ YnBsaXN0MDDUAQIDBAUGBwpYJHZlcnNpb25ZJGFyY2hpdmVyVCR0b3BYJG9iamVjdHMSAAGGoF8QD05T -S2V5ZWRBcmNoaXZlctEICVRyb290gAGuCwwZGh8UJCkqMTQ3PUBVJG51bGzWDQ4PEBESExQVFhcYVk5T -U2l6ZV5OU1Jlc2l6aW5nTW9kZVYkY2xhc3NcTlNJbWFnZUZsYWdzVk5TUmVwc1dOU0NvbG9ygAIQAIAN -EiDDAACAA4ALVnsxLCAxfdIbDxweWk5TLm9iamVjdHOhHYAEgArSGw8gI6IhIoAFgAaACdMPJSYnKBRf -EBROU1RJRkZSZXByZXNlbnRhdGlvbl8QGU5TSW50ZXJuYWxMYXlvdXREaXJlY3Rpb26ACIAHTxESbE1N -ACoAAAAKAAAAEAEAAAMAAAABAAEAAAEBAAMAAAABAAEAAAECAAMAAAACAAgACAEDAAMAAAABAAEAAAEG -AAMAAAABAAEAAAEKAAMAAAABAAEAAAERAAQAAAABAAAACAESAAMAAAABAAEAAAEVAAMAAAABAAIAAAEW -AAMAAAABAAEAAAEXAAQAAAABAAAAAgEcAAMAAAABAAEAAAEoAAMAAAABAAIAAAFSAAMAAAABAAEAAAFT -AAMAAAACAAEAAYdzAAcAABGcAAAA0AAAAAAAABGcYXBwbAIAAABtbnRyR1JBWVhZWiAH3AAIABcADwAu -AA9hY3NwQVBQTAAAAABub25lAAAAAAAAAAAAAAAAAAAAAAAA9tYAAQAAAADTLWFwcGwAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAVkZXNjAAAAwAAAAHlkc2NtAAABPAAA -CBpjcHJ0AAAJWAAAACN3dHB0AAAJfAAAABRrVFJDAAAJkAAACAxkZXNjAAAAAAAAAB9HZW5lcmljIEdy -YXkgR2FtbWEgMi4yIFByb2ZpbGUAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAbWx1YwAAAAAAAAAfAAAADHNr -U0sAAAAuAAABhGRhREsAAAA6AAABsmNhRVMAAAA4AAAB7HZpVk4AAABAAAACJHB0QlIAAABKAAACZHVr -VUEAAAAsAAACrmZyRlUAAAA+AAAC2mh1SFUAAAA0AAADGHpoVFcAAAAaAAADTGtvS1IAAAAiAAADZm5i -Tk8AAAA6AAADiGNzQ1oAAAAoAAADwmhlSUwAAAAkAAAD6nJvUk8AAAAqAAAEDmRlREUAAABOAAAEOGl0 -SVQAAABOAAAEhnN2U0UAAAA4AAAE1HpoQ04AAAAaAAAFDGphSlAAAAAmAAAFJmVsR1IAAAAqAAAFTHB0 -UE8AAABSAAAFdm5sTkwAAABAAAAFyGVzRVMAAABMAAAGCHRoVEgAAAAyAAAGVHRyVFIAAAAkAAAGhmZp -RkkAAABGAAAGqmhySFIAAAA+AAAG8HBsUEwAAABKAAAHLmFyRUcAAAAsAAAHeHJ1UlUAAAA6AAAHpGVu -VVMAAAA8AAAH3gBWAWEAZQBvAGIAZQBjAG4A4QAgAHMAaQB2AOEAIABnAGEAbQBhACAAMgAsADIARwBl -AG4AZQByAGkAcwBrACAAZwByAOUAIAAyACwAMgAgAGcAYQBtAG0AYQAtAHAAcgBvAGYAaQBsAEcAYQBt -AG0AYQAgAGQAZQAgAGcAcgBpAHMAbwBzACAAZwBlAG4A6AByAGkAYwBhACAAMgAuADIAQx6lAHUAIABo -AOwAbgBoACAATQDgAHUAIAB4AOEAbQAgAEMAaAB1AG4AZwAgAEcAYQBtAG0AYQAgADIALgAyAFAAZQBy -AGYAaQBsACAARwBlAG4A6QByAGkAYwBvACAAZABhACAARwBhAG0AYQAgAGQAZQAgAEMAaQBuAHoAYQBz -ACAAMgAsADIEFwQwBDMEMAQ7BEwEPQQwACAARwByAGEAeQAtBDMEMAQ8BDAAIAAyAC4AMgBQAHIAbwBm -AGkAbAAgAGcA6QBuAOkAcgBpAHEAdQBlACAAZwByAGkAcwAgAGcAYQBtAG0AYQAgADIALAAyAMEAbAB0 -AGEAbADhAG4AbwBzACAAcwB6APwAcgBrAGUAIABnAGEAbQBtAGEAIAAyAC4AMpAadShwcJaOUUlepgAy -AC4AMoJyX2ljz4/wx3y8GAAg1ozAyQAgrBC5yAAgADIALgAyACDVBLhc0wzHfABHAGUAbgBlAHIAaQBz -AGsAIABnAHIA5QAgAGcAYQBtAG0AYQAgADIALAAyAC0AcAByAG8AZgBpAGwATwBiAGUAYwBuAOEAIAFh -AGUAZADhACAAZwBhAG0AYQAgADIALgAyBdIF0AXeBdQAIAXQBeQF1QXoACAF2wXcBdwF2QAgADIALgAy -AEcAYQBtAGEAIABnAHIAaQAgAGcAZQBuAGUAcgBpAGMBAwAgADIALAAyAEEAbABsAGcAZQBtAGUAaQBu -AGUAcwAgAEcAcgBhAHUAcwB0AHUAZgBlAG4ALQBQAHIAbwBmAGkAbAAgAEcAYQBtAG0AYQAgADIALAAy -AFAAcgBvAGYAaQBsAG8AIABnAHIAaQBnAGkAbwAgAGcAZQBuAGUAcgBpAGMAbwAgAGQAZQBsAGwAYQAg -AGcAYQBtAG0AYQAgADIALAAyAEcAZQBuAGUAcgBpAHMAawAgAGcAcgDlACAAMgAsADIAIABnAGEAbQBt -AGEAcAByAG8AZgBpAGxmbpAacHBepnz7ZXAAMgAuADJjz4/wZYdO9k4AgiwwsDDsMKQwrDDzMN4AIAAy -AC4AMgAgMNcw7TDVMKEwpDDrA5MDtQO9A7kDugPMACADkwO6A8EDuQAgA5MDrAO8A7wDsQAgADIALgAy -AFAAZQByAGYAaQBsACAAZwBlAG4A6QByAGkAYwBvACAAZABlACAAYwBpAG4AegBlAG4AdABvAHMAIABk -AGEAIABHAGEAbQBtAGEAIAAyACwAMgBBAGwAZwBlAG0AZQBlAG4AIABnAHIAaQBqAHMAIABnAGEAbQBt -AGEAIAAyACwAMgAtAHAAcgBvAGYAaQBlAGwAUABlAHIAZgBpAGwAIABnAGUAbgDpAHIAaQBjAG8AIABk -AGUAIABnAGEAbQBtAGEAIABkAGUAIABnAHIAaQBzAGUAcwAgADIALAAyDiMOMQ4HDioONQ5BDgEOIQ4h -DjIOQA4BDiMOIg5MDhcOMQ5IDicORA4bACAAMgAuADIARwBlAG4AZQBsACAARwByAGkAIABHAGEAbQBh -ACAAMgAsADIAWQBsAGUAaQBuAGUAbgAgAGgAYQByAG0AYQBhAG4AIABnAGEAbQBtAGEAIAAyACwAMgAg -AC0AcAByAG8AZgBpAGkAbABpAEcAZQBuAGUAcgBpAQ0AawBpACAARwByAGEAeQAgAEcAYQBtAG0AYQAg -ADIALgAyACAAcAByAG8AZgBpAGwAVQBuAGkAdwBlAHIAcwBhAGwAbgB5ACAAcAByAG8AZgBpAGwAIABz -AHoAYQByAG8BWwBjAGkAIABnAGEAbQBtAGEAIAAyACwAMgY6BicGRQYnACAAMgAuADIAIAZEBkgGRgAg -BjEGRQYnBi8GSgAgBjkGJwZFBB4EMQRJBDAETwAgBEEENQRABDAETwAgBDMEMAQ8BDwEMAAgADIALAAy -AC0EPwRABD4ERAQ4BDsETABHAGUAbgBlAHIAaQBjACAARwByAGEAeQAgAEcAYQBtAG0AYQAgADIALgAy -ACAAUAByAG8AZgBpAGwAZQAAdGV4dAAAAABDb3B5cmlnaHQgQXBwbGUgSW5jLiwgMjAxMgAAWFlaIAAA -AAAAAPNRAAEAAAABFsxjdXJ2AAAAAAAABAAAAAAFAAoADwAUABkAHgAjACgALQAyADcAOwBAAEUASgBP -AFQAWQBeAGMAaABtAHIAdwB8AIEAhgCLAJAAlQCaAJ8ApACpAK4AsgC3ALwAwQDGAMsA0ADVANsA4ADl -AOsA8AD2APsBAQEHAQ0BEwEZAR8BJQErATIBOAE+AUUBTAFSAVkBYAFnAW4BdQF8AYMBiwGSAZoBoQGp -AbEBuQHBAckB0QHZAeEB6QHyAfoCAwIMAhQCHQImAi8COAJBAksCVAJdAmcCcQJ6AoQCjgKYAqICrAK2 -AsECywLVAuAC6wL1AwADCwMWAyEDLQM4A0MDTwNaA2YDcgN+A4oDlgOiA64DugPHA9MD4APsA/kEBgQT -BCAELQQ7BEgEVQRjBHEEfgSMBJoEqAS2BMQE0wThBPAE/gUNBRwFKwU6BUkFWAVnBXcFhgWWBaYFtQXF -BdUF5QX2BgYGFgYnBjcGSAZZBmoGewaMBp0GrwbABtEG4wb1BwcHGQcrBz0HTwdhB3QHhgeZB6wHvwfS -B+UH+AgLCB8IMghGCFoIbgiCCJYIqgi+CNII5wj7CRAJJQk6CU8JZAl5CY8JpAm6Cc8J5Qn7ChEKJwo9 -ClQKagqBCpgKrgrFCtwK8wsLCyILOQtRC2kLgAuYC7ALyAvhC/kMEgwqDEMMXAx1DI4MpwzADNkM8w0N -DSYNQA1aDXQNjg2pDcMN3g34DhMOLg5JDmQOfw6bDrYO0g7uDwkPJQ9BD14Peg+WD7MPzw/sEAkQJhBD -EGEQfhCbELkQ1xD1ERMRMRFPEW0RjBGqEckR6BIHEiYSRRJkEoQSoxLDEuMTAxMjE0MTYxODE6QTxRPl -FAYUJxRJFGoUixStFM4U8BUSFTQVVhV4FZsVvRXgFgMWJhZJFmwWjxayFtYW+hcdF0EXZReJF64X0hf3 -GBsYQBhlGIoYrxjVGPoZIBlFGWsZkRm3Gd0aBBoqGlEadxqeGsUa7BsUGzsbYxuKG7Ib2hwCHCocUhx7 -HKMczBz1HR4dRx1wHZkdwx3sHhYeQB5qHpQevh7pHxMfPh9pH5Qfvx/qIBUgQSBsIJggxCDwIRwhSCF1 -IaEhziH7IiciVSKCIq8i3SMKIzgjZiOUI8Ij8CQfJE0kfCSrJNolCSU4JWgllyXHJfcmJyZXJocmtybo -JxgnSSd6J6sn3CgNKD8ocSiiKNQpBik4KWspnSnQKgIqNSpoKpsqzysCKzYraSudK9EsBSw5LG4soizX -LQwtQS12Last4S4WLkwugi63Lu4vJC9aL5Evxy/+MDUwbDCkMNsxEjFKMYIxujHyMioyYzKbMtQzDTNG -M38zuDPxNCs0ZTSeNNg1EzVNNYc1wjX9Njc2cjauNuk3JDdgN5w31zgUOFA4jDjIOQU5Qjl/Obw5+To2 -OnQ6sjrvOy07azuqO+g8JzxlPKQ84z0iPWE9oT3gPiA+YD6gPuA/IT9hP6I/4kAjQGRApkDnQSlBakGs -Qe5CMEJyQrVC90M6Q31DwEQDREdEikTORRJFVUWaRd5GIkZnRqtG8Ec1R3tHwEgFSEtIkUjXSR1JY0mp -SfBKN0p9SsRLDEtTS5pL4kwqTHJMuk0CTUpNk03cTiVObk63TwBPSU+TT91QJ1BxULtRBlFQUZtR5lIx -UnxSx1MTU19TqlP2VEJUj1TbVShVdVXCVg9WXFapVvdXRFeSV+BYL1h9WMtZGllpWbhaB1pWWqZa9VtF -W5Vb5Vw1XIZc1l0nXXhdyV4aXmxevV8PX2Ffs2AFYFdgqmD8YU9homH1YklinGLwY0Njl2PrZEBklGTp -ZT1lkmXnZj1mkmboZz1nk2fpaD9olmjsaUNpmmnxakhqn2r3a09rp2v/bFdsr20IbWBtuW4SbmtuxG8e -b3hv0XArcIZw4HE6cZVx8HJLcqZzAXNdc7h0FHRwdMx1KHWFdeF2Pnabdvh3VnezeBF4bnjMeSp5iXnn -ekZ6pXsEe2N7wnwhfIF84X1BfaF+AX5ifsJ/I3+Ef+WAR4CogQqBa4HNgjCCkoL0g1eDuoQdhICE44VH -hauGDoZyhteHO4efiASIaYjOiTOJmYn+imSKyoswi5aL/IxjjMqNMY2Yjf+OZo7OjzaPnpAGkG6Q1pE/ -kaiSEZJ6kuOTTZO2lCCUipT0lV+VyZY0lp+XCpd1l+CYTJi4mSSZkJn8mmia1ZtCm6+cHJyJnPedZJ3S -nkCerp8dn4uf+qBpoNihR6G2oiailqMGo3aj5qRWpMelOKWpphqmi6b9p26n4KhSqMSpN6mpqhyqj6sC -q3Wr6axcrNCtRK24ri2uoa8Wr4uwALB1sOqxYLHWskuywrM4s660JbSctRO1irYBtnm28Ldot+C4WbjR -uUq5wro7urW7LrunvCG8m70VvY++Cr6Evv+/er/1wHDA7MFnwePCX8Lbw1jD1MRRxM7FS8XIxkbGw8dB -x7/IPci8yTrJuco4yrfLNsu2zDXMtc01zbXONs62zzfPuNA50LrRPNG+0j/SwdNE08bUSdTL1U7V0dZV -1tjXXNfg2GTY6Nls2fHadtr724DcBdyK3RDdlt4c3qLfKd+v4DbgveFE4cziU+Lb42Pj6+Rz5PzlhOYN -5pbnH+ep6DLovOlG6dDqW+rl63Dr++yG7RHtnO4o7rTvQO/M8Fjw5fFy8f/yjPMZ86f0NPTC9VD13vZt -9vv3ivgZ+Kj5OPnH+lf65/t3/Af8mP0p/br+S/7c/23//9IrLC0uWiRjbGFzc25hbWVYJGNsYXNzZXNf -EBBOU0JpdG1hcEltYWdlUmVwoy0vMFpOU0ltYWdlUmVwWE5TT2JqZWN00issMjNXTlNBcnJheaIyMNIr -LDU2Xk5TTXV0YWJsZUFycmF5ozUyMNM4OQ86OzxXTlNXaGl0ZVxOU0NvbG9yU3BhY2VEMCAwABADgAzS -Kyw+P1dOU0NvbG9yoj4w0issQUJXTlNJbWFnZaJBMAAIABEAGgAkACkAMgA3AEkATABRAFMAYgBoAHUA -fACLAJIAnwCmAK4AsACyALQAuQC7AL0AxADJANQA1gDYANoA3wDiAOQA5gDoAO8BBgEiASQBJhOWE5sT -phOvE8ITxhPRE9oT3xPnE+oT7xP+FAIUCRQRFB4UIxQlFCcULBQ0FDcUPBREAAAAAAAAAgEAAAAAAAAA -QwAAAAAAAAAAAAAAAAAAFEc +S2V5ZWRBcmNoaXZlctEICVRyb290gAGuCwwbHCEaJissMzY5P0JVJG51bGzXDQ4PEBESExQVFhcYGRpW +TlNTaXplViRjbGFzc1xOU0ltYWdlRmxhZ3NWTlNSZXBzW05TVGludENvbG9yV05TQ29sb3JeTlNSZXNp +emluZ01vZGWAAoANEiDDAACAA4AAgAsQAFZ7MSwgMX3SHQ4eIFpOUy5vYmplY3RzoR+ABIAK0h0OIiWi +IySABYAGgAnTDicoKSoaXxAUTlNUSUZGUmVwcmVzZW50YXRpb25fEBlOU0ludGVybmFsTGF5b3V0RGly +ZWN0aW9ugAiAB08REmxNTQAqAAAACgAAABABAAADAAAAAQABAAABAQADAAAAAQABAAABAgADAAAAAgAI +AAgBAwADAAAAAQABAAABBgADAAAAAQABAAABCgADAAAAAQABAAABEQAEAAAAAQAAAAgBEgADAAAAAQAB +AAABFQADAAAAAQACAAABFgADAAAAAQABAAABFwAEAAAAAQAAAAIBHAADAAAAAQABAAABKAADAAAAAQAC +AAABUgADAAAAAQABAAABUwADAAAAAgABAAGHcwAHAAARnAAAANAAAAAAAAARnGFwcGwCAAAAbW50ckdS +QVlYWVogB9wACAAXAA8ALgAPYWNzcEFQUEwAAAAAbm9uZQAAAAAAAAAAAAAAAAAAAAAAAPbWAAEAAAAA +0y1hcHBsAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFZGVzYwAA +AMAAAAB5ZHNjbQAAATwAAAgaY3BydAAACVgAAAAjd3RwdAAACXwAAAAUa1RSQwAACZAAAAgMZGVzYwAA +AAAAAAAfR2VuZXJpYyBHcmF5IEdhbW1hIDIuMiBQcm9maWxlAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAG1s +dWMAAAAAAAAAHwAAAAxza1NLAAAALgAAAYRkYURLAAAAOgAAAbJjYUVTAAAAOAAAAex2aVZOAAAAQAAA +AiRwdEJSAAAASgAAAmR1a1VBAAAALAAAAq5mckZVAAAAPgAAAtpodUhVAAAANAAAAxh6aFRXAAAAGgAA +A0xrb0tSAAAAIgAAA2ZuYk5PAAAAOgAAA4hjc0NaAAAAKAAAA8JoZUlMAAAAJAAAA+pyb1JPAAAAKgAA +BA5kZURFAAAATgAABDhpdElUAAAATgAABIZzdlNFAAAAOAAABNR6aENOAAAAGgAABQxqYUpQAAAAJgAA +BSZlbEdSAAAAKgAABUxwdFBPAAAAUgAABXZubE5MAAAAQAAABchlc0VTAAAATAAABgh0aFRIAAAAMgAA +BlR0clRSAAAAJAAABoZmaUZJAAAARgAABqpockhSAAAAPgAABvBwbFBMAAAASgAABy5hckVHAAAALAAA +B3hydVJVAAAAOgAAB6RlblVTAAAAPAAAB94AVgFhAGUAbwBiAGUAYwBuAOEAIABzAGkAdgDhACAAZwBh +AG0AYQAgADIALAAyAEcAZQBuAGUAcgBpAHMAawAgAGcAcgDlACAAMgAsADIAIABnAGEAbQBtAGEALQBw +AHIAbwBmAGkAbABHAGEAbQBtAGEAIABkAGUAIABnAHIAaQBzAG8AcwAgAGcAZQBuAOgAcgBpAGMAYQAg +ADIALgAyAEMepQB1ACAAaADsAG4AaAAgAE0A4AB1ACAAeADhAG0AIABDAGgAdQBuAGcAIABHAGEAbQBt +AGEAIAAyAC4AMgBQAGUAcgBmAGkAbAAgAEcAZQBuAOkAcgBpAGMAbwAgAGQAYQAgAEcAYQBtAGEAIABk +AGUAIABDAGkAbgB6AGEAcwAgADIALAAyBBcEMAQzBDAEOwRMBD0EMAAgAEcAcgBhAHkALQQzBDAEPAQw +ACAAMgAuADIAUAByAG8AZgBpAGwAIABnAOkAbgDpAHIAaQBxAHUAZQAgAGcAcgBpAHMAIABnAGEAbQBt +AGEAIAAyACwAMgDBAGwAdABhAGwA4QBuAG8AcwAgAHMAegD8AHIAawBlACAAZwBhAG0AbQBhACAAMgAu +ADKQGnUocHCWjlFJXqYAMgAuADKCcl9pY8+P8Md8vBgAINaMwMkAIKwQucgAIAAyAC4AMgAg1QS4XNMM +x3wARwBlAG4AZQByAGkAcwBrACAAZwByAOUAIABnAGEAbQBtAGEAIAAyACwAMgAtAHAAcgBvAGYAaQBs +AE8AYgBlAGMAbgDhACABYQBlAGQA4QAgAGcAYQBtAGEAIAAyAC4AMgXSBdAF3gXUACAF0AXkBdUF6AAg +BdsF3AXcBdkAIAAyAC4AMgBHAGEAbQBhACAAZwByAGkAIABnAGUAbgBlAHIAaQBjAQMAIAAyACwAMgBB +AGwAbABnAGUAbQBlAGkAbgBlAHMAIABHAHIAYQB1AHMAdAB1AGYAZQBuAC0AUAByAG8AZgBpAGwAIABH +AGEAbQBtAGEAIAAyACwAMgBQAHIAbwBmAGkAbABvACAAZwByAGkAZwBpAG8AIABnAGUAbgBlAHIAaQBj +AG8AIABkAGUAbABsAGEAIABnAGEAbQBtAGEAIAAyACwAMgBHAGUAbgBlAHIAaQBzAGsAIABnAHIA5QAg +ADIALAAyACAAZwBhAG0AbQBhAHAAcgBvAGYAaQBsZm6QGnBwXqZ8+2VwADIALgAyY8+P8GWHTvZOAIIs +MLAw7DCkMKww8zDeACAAMgAuADIAIDDXMO0w1TChMKQw6wOTA7UDvQO5A7oDzAAgA5MDugPBA7kAIAOT +A6wDvAO8A7EAIAAyAC4AMgBQAGUAcgBmAGkAbAAgAGcAZQBuAOkAcgBpAGMAbwAgAGQAZQAgAGMAaQBu +AHoAZQBuAHQAbwBzACAAZABhACAARwBhAG0AbQBhACAAMgAsADIAQQBsAGcAZQBtAGUAZQBuACAAZwBy +AGkAagBzACAAZwBhAG0AbQBhACAAMgAsADIALQBwAHIAbwBmAGkAZQBsAFAAZQByAGYAaQBsACAAZwBl +AG4A6QByAGkAYwBvACAAZABlACAAZwBhAG0AbQBhACAAZABlACAAZwByAGkAcwBlAHMAIAAyACwAMg4j +DjEOBw4qDjUOQQ4BDiEOIQ4yDkAOAQ4jDiIOTA4XDjEOSA4nDkQOGwAgADIALgAyAEcAZQBuAGUAbAAg +AEcAcgBpACAARwBhAG0AYQAgADIALAAyAFkAbABlAGkAbgBlAG4AIABoAGEAcgBtAGEAYQBuACAAZwBh +AG0AbQBhACAAMgAsADIAIAAtAHAAcgBvAGYAaQBpAGwAaQBHAGUAbgBlAHIAaQENAGsAaQAgAEcAcgBh +AHkAIABHAGEAbQBtAGEAIAAyAC4AMgAgAHAAcgBvAGYAaQBsAFUAbgBpAHcAZQByAHMAYQBsAG4AeQAg +AHAAcgBvAGYAaQBsACAAcwB6AGEAcgBvAVsAYwBpACAAZwBhAG0AbQBhACAAMgAsADIGOgYnBkUGJwAg +ADIALgAyACAGRAZIBkYAIAYxBkUGJwYvBkoAIAY5BicGRQQeBDEESQQwBE8AIARBBDUEQAQwBE8AIAQz +BDAEPAQ8BDAAIAAyACwAMgAtBD8EQAQ+BEQEOAQ7BEwARwBlAG4AZQByAGkAYwAgAEcAcgBhAHkAIABH +AGEAbQBtAGEAIAAyAC4AMgAgAFAAcgBvAGYAaQBsAGUAAHRleHQAAAAAQ29weXJpZ2h0IEFwcGxlIElu +Yy4sIDIwMTIAAFhZWiAAAAAAAADzUQABAAAAARbMY3VydgAAAAAAAAQAAAAABQAKAA8AFAAZAB4AIwAo +AC0AMgA3ADsAQABFAEoATwBUAFkAXgBjAGgAbQByAHcAfACBAIYAiwCQAJUAmgCfAKQAqQCuALIAtwC8 +AMEAxgDLANAA1QDbAOAA5QDrAPAA9gD7AQEBBwENARMBGQEfASUBKwEyATgBPgFFAUwBUgFZAWABZwFu +AXUBfAGDAYsBkgGaAaEBqQGxAbkBwQHJAdEB2QHhAekB8gH6AgMCDAIUAh0CJgIvAjgCQQJLAlQCXQJn +AnECegKEAo4CmAKiAqwCtgLBAssC1QLgAusC9QMAAwsDFgMhAy0DOANDA08DWgNmA3IDfgOKA5YDogOu +A7oDxwPTA+AD7AP5BAYEEwQgBC0EOwRIBFUEYwRxBH4EjASaBKgEtgTEBNME4QTwBP4FDQUcBSsFOgVJ +BVgFZwV3BYYFlgWmBbUFxQXVBeUF9gYGBhYGJwY3BkgGWQZqBnsGjAadBq8GwAbRBuMG9QcHBxkHKwc9 +B08HYQd0B4YHmQesB78H0gflB/gICwgfCDIIRghaCG4IggiWCKoIvgjSCOcI+wkQCSUJOglPCWQJeQmP +CaQJugnPCeUJ+woRCicKPQpUCmoKgQqYCq4KxQrcCvMLCwsiCzkLUQtpC4ALmAuwC8gL4Qv5DBIMKgxD +DFwMdQyODKcMwAzZDPMNDQ0mDUANWg10DY4NqQ3DDd4N+A4TDi4OSQ5kDn8Omw62DtIO7g8JDyUPQQ9e +D3oPlg+zD88P7BAJECYQQxBhEH4QmxC5ENcQ9RETETERTxFtEYwRqhHJEegSBxImEkUSZBKEEqMSwxLj +EwMTIxNDE2MTgxOkE8UT5RQGFCcUSRRqFIsUrRTOFPAVEhU0FVYVeBWbFb0V4BYDFiYWSRZsFo8WshbW +FvoXHRdBF2UXiReuF9IX9xgbGEAYZRiKGK8Y1Rj6GSAZRRlrGZEZtxndGgQaKhpRGncanhrFGuwbFBs7 +G2MbihuyG9ocAhwqHFIcexyjHMwc9R0eHUcdcB2ZHcMd7B4WHkAeah6UHr4e6R8THz4faR+UH78f6iAV +IEEgbCCYIMQg8CEcIUghdSGhIc4h+yInIlUigiKvIt0jCiM4I2YjlCPCI/AkHyRNJHwkqyTaJQklOCVo +JZclxyX3JicmVyaHJrcm6CcYJ0kneierJ9woDSg/KHEooijUKQYpOClrKZ0p0CoCKjUqaCqbKs8rAis2 +K2krnSvRLAUsOSxuLKIs1y0MLUEtdi2rLeEuFi5MLoIuty7uLyQvWi+RL8cv/jA1MGwwpDDbMRIxSjGC +Mbox8jIqMmMymzLUMw0zRjN/M7gz8TQrNGU0njTYNRM1TTWHNcI1/TY3NnI2rjbpNyQ3YDecN9c4FDhQ +OIw4yDkFOUI5fzm8Ofk6Njp0OrI67zstO2s7qjvoPCc8ZTykPOM9Ij1hPaE94D4gPmA+oD7gPyE/YT+i +P+JAI0BkQKZA50EpQWpBrEHuQjBCckK1QvdDOkN9Q8BEA0RHRIpEzkUSRVVFmkXeRiJGZ0arRvBHNUd7 +R8BIBUhLSJFI10kdSWNJqUnwSjdKfUrESwxLU0uaS+JMKkxyTLpNAk1KTZNN3E4lTm5Ot08AT0lPk0/d +UCdQcVC7UQZRUFGbUeZSMVJ8UsdTE1NfU6pT9lRCVI9U21UoVXVVwlYPVlxWqVb3V0RXklfgWC9YfVjL +WRpZaVm4WgdaVlqmWvVbRVuVW+VcNVyGXNZdJ114XcleGl5sXr1fD19hX7NgBWBXYKpg/GFPYaJh9WJJ +Ypxi8GNDY5dj62RAZJRk6WU9ZZJl52Y9ZpJm6Gc9Z5Nn6Wg/aJZo7GlDaZpp8WpIap9q92tPa6dr/2xX +bK9tCG1gbbluEm5rbsRvHm94b9FwK3CGcOBxOnGVcfByS3KmcwFzXXO4dBR0cHTMdSh1hXXhdj52m3b4 +d1Z3s3gReG54zHkqeYl553pGeqV7BHtje8J8IXyBfOF9QX2hfgF+Yn7CfyN/hH/lgEeAqIEKgWuBzYIw +gpKC9INXg7qEHYSAhOOFR4Wrhg6GcobXhzuHn4gEiGmIzokziZmJ/opkisqLMIuWi/yMY4zKjTGNmI3/ +jmaOzo82j56QBpBukNaRP5GokhGSepLjk02TtpQglIqU9JVflcmWNJaflwqXdZfgmEyYuJkkmZCZ/Jpo +mtWbQpuvnByciZz3nWSd0p5Anq6fHZ+Ln/qgaaDYoUehtqImopajBqN2o+akVqTHpTilqaYapoum/adu +p+CoUqjEqTepqaocqo+rAqt1q+msXKzQrUStuK4trqGvFq+LsACwdbDqsWCx1rJLssKzOLOutCW0nLUT +tYq2AbZ5tvC3aLfguFm40blKucK6O7q1uy67p7whvJu9Fb2Pvgq+hL7/v3q/9cBwwOzBZ8Hjwl/C28NY +w9TEUcTOxUvFyMZGxsPHQce/yD3IvMk6ybnKOMq3yzbLtsw1zLXNNc21zjbOts83z7jQOdC60TzRvtI/ +0sHTRNPG1EnUy9VO1dHWVdbY11zX4Nhk2OjZbNnx2nba+9uA3AXcit0Q3ZbeHN6i3ynfr+A24L3hROHM +4lPi2+Nj4+vkc+T85YTmDeaW5x/nqegy6LzpRunQ6lvq5etw6/vshu0R7ZzuKO6070DvzPBY8OXxcvH/ +8ozzGfOn9DT0wvVQ9d72bfb794r4Gfio+Tj5x/pX+uf7d/wH/Jj9Kf26/kv+3P9t///SLS4vMFokY2xh +c3NuYW1lWCRjbGFzc2VzXxAQTlNCaXRtYXBJbWFnZVJlcKMvMTJaTlNJbWFnZVJlcFhOU09iamVjdNIt +LjQ1V05TQXJyYXmiNDLSLS43OF5OU011dGFibGVBcnJheaM3NDLTOjsOPD0+V05TV2hpdGVcTlNDb2xv +clNwYWNlRDAgMAAQA4AM0i0uQEFXTlNDb2xvcqJAMtItLkNEV05TSW1hZ2WiQzIACAARABoAJAApADIA +NwBJAEwAUQBTAGIAaAB3AH4AhQCSAJkApQCtALwAvgDAAMUAxwDJAMsAzQDUANkA5ADmAOgA6gDvAPIA +9AD2APgA/wEWATIBNAE2E6YTqxO2E78T0hPWE+ET6hPvE/cT+hP/FA4UEhQZFCEULhQzFDUUNxQ8FEQU +RxRMFFQAAAAAAAACAQAAAAAAAABFAAAAAAAAAAAAAAAAAAAUVw diff --git a/i18n/src/main/resources/el.lproj/Command.xib b/i18n/src/main/resources/el.lproj/Command.xib index d2532eebb9..89be4bd648 100644 --- a/i18n/src/main/resources/el.lproj/Command.xib +++ b/i18n/src/main/resources/el.lproj/Command.xib @@ -1,7 +1,7 @@ - + - + diff --git a/i18n/src/main/resources/el.lproj/Connection.xib b/i18n/src/main/resources/el.lproj/Connection.xib index 1544963975..909617149f 100644 --- a/i18n/src/main/resources/el.lproj/Connection.xib +++ b/i18n/src/main/resources/el.lproj/Connection.xib @@ -1,7 +1,7 @@ - + - + @@ -76,7 +76,7 @@ - + diff --git a/i18n/src/main/resources/el.lproj/Info.xib b/i18n/src/main/resources/el.lproj/Info.xib index 610ccf4c19..da6c052312 100644 --- a/i18n/src/main/resources/el.lproj/Info.xib +++ b/i18n/src/main/resources/el.lproj/Info.xib @@ -1,7 +1,7 @@ - + - + @@ -89,7 +89,7 @@ - + @@ -1017,21 +1017,21 @@ - + - + - + @@ -1121,21 +1121,21 @@ - + - + - + @@ -1250,27 +1250,27 @@ diff --git a/i18n/src/main/resources/el.lproj/Login.strings b/i18n/src/main/resources/el.lproj/Login.strings index 0270de6605..eb2cf19019 100644 Binary files a/i18n/src/main/resources/el.lproj/Login.strings and b/i18n/src/main/resources/el.lproj/Login.strings differ diff --git a/i18n/src/main/resources/el.lproj/Main.xib b/i18n/src/main/resources/el.lproj/Main.xib index 288a990f16..edbc3198a3 100644 --- a/i18n/src/main/resources/el.lproj/Main.xib +++ b/i18n/src/main/resources/el.lproj/Main.xib @@ -1,7 +1,7 @@ - + - + @@ -159,19 +159,19 @@ - + - + - + @@ -321,12 +321,12 @@ CA - + - + @@ -566,6 +566,10 @@ CA - + + + + + diff --git a/i18n/src/main/resources/el.lproj/Preferences.xib b/i18n/src/main/resources/el.lproj/Preferences.xib index 778746a737..507e430816 100644 --- a/i18n/src/main/resources/el.lproj/Preferences.xib +++ b/i18n/src/main/resources/el.lproj/Preferences.xib @@ -1,7 +1,7 @@ - + - + @@ -266,7 +266,7 @@ - + @@ -307,7 +307,7 @@ - + @@ -538,7 +538,7 @@ - + @@ -578,7 +578,7 @@ - + @@ -613,7 +613,7 @@ - + @@ -642,7 +642,7 @@ - + @@ -1218,7 +1218,7 @@ - + @@ -1250,7 +1250,7 @@ - + @@ -1290,7 +1290,7 @@ - + @@ -1363,7 +1363,7 @@ - + @@ -1558,7 +1558,7 @@ - + @@ -1618,7 +1618,7 @@ - + @@ -1643,7 +1643,7 @@ - + @@ -1685,7 +1685,7 @@ - + @@ -1713,7 +1713,7 @@ - + @@ -1741,7 +1741,7 @@ - + @@ -1770,7 +1770,7 @@ - + @@ -1805,7 +1805,7 @@ - + @@ -1890,7 +1890,7 @@ - + @@ -1949,7 +1949,7 @@ - + @@ -1977,7 +1977,7 @@ - + @@ -2006,7 +2006,7 @@ - + diff --git a/i18n/src/main/resources/el.lproj/Profiles.xib b/i18n/src/main/resources/el.lproj/Profiles.xib index 18af5a58f4..20e5ce0f36 100644 --- a/i18n/src/main/resources/el.lproj/Profiles.xib +++ b/i18n/src/main/resources/el.lproj/Profiles.xib @@ -1,7 +1,7 @@ - + - + diff --git a/i18n/src/main/resources/el.lproj/Prompt.xib b/i18n/src/main/resources/el.lproj/Prompt.xib index 13f48140e2..1558354749 100644 --- a/i18n/src/main/resources/el.lproj/Prompt.xib +++ b/i18n/src/main/resources/el.lproj/Prompt.xib @@ -1,7 +1,7 @@ - + - + @@ -95,7 +95,7 @@ Gw - + diff --git a/i18n/src/main/resources/el.lproj/S3.strings b/i18n/src/main/resources/el.lproj/S3.strings index 96980b6659..55013f5f15 100644 Binary files a/i18n/src/main/resources/el.lproj/S3.strings and b/i18n/src/main/resources/el.lproj/S3.strings differ diff --git a/i18n/src/main/resources/el.lproj/Updater.strings b/i18n/src/main/resources/el.lproj/Updater.strings index 29e552dc25..c2ee1ca275 100644 Binary files a/i18n/src/main/resources/el.lproj/Updater.strings and b/i18n/src/main/resources/el.lproj/Updater.strings differ diff --git a/i18n/src/main/resources/en.lproj/Browser.xib b/i18n/src/main/resources/en.lproj/Browser.xib index e08c3da8b1..874870bd14 100644 --- a/i18n/src/main/resources/en.lproj/Browser.xib +++ b/i18n/src/main/resources/en.lproj/Browser.xib @@ -1,7 +1,7 @@ - + - + @@ -38,7 +38,7 @@ - + @@ -58,31 +58,31 @@ - + @@ -256,7 +256,7 @@ - + @@ -551,101 +551,101 @@ DQ - - - + + + YnBsaXN0MDDUAQIDBAUGBwpYJHZlcnNpb25ZJGFyY2hpdmVyVCR0b3BYJG9iamVjdHMSAAGGoF8QD05T -S2V5ZWRBcmNoaXZlctEICVRyb290gAGuCwwZGh8UJCkqMTQ3PUBVJG51bGzWDQ4PEBESExQVFhcYVk5T -U2l6ZV5OU1Jlc2l6aW5nTW9kZVYkY2xhc3NcTlNJbWFnZUZsYWdzVk5TUmVwc1dOU0NvbG9ygAIQAIAN -EiDDAACAA4ALVnsxLCAxfdIbDxweWk5TLm9iamVjdHOhHYAEgArSGw8gI6IhIoAFgAaACdMPJSYnKBRf -EBROU1RJRkZSZXByZXNlbnRhdGlvbl8QGU5TSW50ZXJuYWxMYXlvdXREaXJlY3Rpb26ACIAHTxESbE1N -ACoAAAAKAAAAEAEAAAMAAAABAAEAAAEBAAMAAAABAAEAAAECAAMAAAACAAgACAEDAAMAAAABAAEAAAEG -AAMAAAABAAEAAAEKAAMAAAABAAEAAAERAAQAAAABAAAACAESAAMAAAABAAEAAAEVAAMAAAABAAIAAAEW -AAMAAAABAAEAAAEXAAQAAAABAAAAAgEcAAMAAAABAAEAAAEoAAMAAAABAAIAAAFSAAMAAAABAAEAAAFT -AAMAAAACAAEAAYdzAAcAABGcAAAA0AAAAAAAABGcYXBwbAIAAABtbnRyR1JBWVhZWiAH3AAIABcADwAu -AA9hY3NwQVBQTAAAAABub25lAAAAAAAAAAAAAAAAAAAAAAAA9tYAAQAAAADTLWFwcGwAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAVkZXNjAAAAwAAAAHlkc2NtAAABPAAA -CBpjcHJ0AAAJWAAAACN3dHB0AAAJfAAAABRrVFJDAAAJkAAACAxkZXNjAAAAAAAAAB9HZW5lcmljIEdy -YXkgR2FtbWEgMi4yIFByb2ZpbGUAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAbWx1YwAAAAAAAAAfAAAADHNr -U0sAAAAuAAABhGRhREsAAAA6AAABsmNhRVMAAAA4AAAB7HZpVk4AAABAAAACJHB0QlIAAABKAAACZHVr -VUEAAAAsAAACrmZyRlUAAAA+AAAC2mh1SFUAAAA0AAADGHpoVFcAAAAaAAADTGtvS1IAAAAiAAADZm5i -Tk8AAAA6AAADiGNzQ1oAAAAoAAADwmhlSUwAAAAkAAAD6nJvUk8AAAAqAAAEDmRlREUAAABOAAAEOGl0 -SVQAAABOAAAEhnN2U0UAAAA4AAAE1HpoQ04AAAAaAAAFDGphSlAAAAAmAAAFJmVsR1IAAAAqAAAFTHB0 -UE8AAABSAAAFdm5sTkwAAABAAAAFyGVzRVMAAABMAAAGCHRoVEgAAAAyAAAGVHRyVFIAAAAkAAAGhmZp -RkkAAABGAAAGqmhySFIAAAA+AAAG8HBsUEwAAABKAAAHLmFyRUcAAAAsAAAHeHJ1UlUAAAA6AAAHpGVu -VVMAAAA8AAAH3gBWAWEAZQBvAGIAZQBjAG4A4QAgAHMAaQB2AOEAIABnAGEAbQBhACAAMgAsADIARwBl -AG4AZQByAGkAcwBrACAAZwByAOUAIAAyACwAMgAgAGcAYQBtAG0AYQAtAHAAcgBvAGYAaQBsAEcAYQBt -AG0AYQAgAGQAZQAgAGcAcgBpAHMAbwBzACAAZwBlAG4A6AByAGkAYwBhACAAMgAuADIAQx6lAHUAIABo -AOwAbgBoACAATQDgAHUAIAB4AOEAbQAgAEMAaAB1AG4AZwAgAEcAYQBtAG0AYQAgADIALgAyAFAAZQBy -AGYAaQBsACAARwBlAG4A6QByAGkAYwBvACAAZABhACAARwBhAG0AYQAgAGQAZQAgAEMAaQBuAHoAYQBz -ACAAMgAsADIEFwQwBDMEMAQ7BEwEPQQwACAARwByAGEAeQAtBDMEMAQ8BDAAIAAyAC4AMgBQAHIAbwBm -AGkAbAAgAGcA6QBuAOkAcgBpAHEAdQBlACAAZwByAGkAcwAgAGcAYQBtAG0AYQAgADIALAAyAMEAbAB0 -AGEAbADhAG4AbwBzACAAcwB6APwAcgBrAGUAIABnAGEAbQBtAGEAIAAyAC4AMpAadShwcJaOUUlepgAy -AC4AMoJyX2ljz4/wx3y8GAAg1ozAyQAgrBC5yAAgADIALgAyACDVBLhc0wzHfABHAGUAbgBlAHIAaQBz -AGsAIABnAHIA5QAgAGcAYQBtAG0AYQAgADIALAAyAC0AcAByAG8AZgBpAGwATwBiAGUAYwBuAOEAIAFh -AGUAZADhACAAZwBhAG0AYQAgADIALgAyBdIF0AXeBdQAIAXQBeQF1QXoACAF2wXcBdwF2QAgADIALgAy -AEcAYQBtAGEAIABnAHIAaQAgAGcAZQBuAGUAcgBpAGMBAwAgADIALAAyAEEAbABsAGcAZQBtAGUAaQBu -AGUAcwAgAEcAcgBhAHUAcwB0AHUAZgBlAG4ALQBQAHIAbwBmAGkAbAAgAEcAYQBtAG0AYQAgADIALAAy -AFAAcgBvAGYAaQBsAG8AIABnAHIAaQBnAGkAbwAgAGcAZQBuAGUAcgBpAGMAbwAgAGQAZQBsAGwAYQAg -AGcAYQBtAG0AYQAgADIALAAyAEcAZQBuAGUAcgBpAHMAawAgAGcAcgDlACAAMgAsADIAIABnAGEAbQBt -AGEAcAByAG8AZgBpAGxmbpAacHBepnz7ZXAAMgAuADJjz4/wZYdO9k4AgiwwsDDsMKQwrDDzMN4AIAAy -AC4AMgAgMNcw7TDVMKEwpDDrA5MDtQO9A7kDugPMACADkwO6A8EDuQAgA5MDrAO8A7wDsQAgADIALgAy -AFAAZQByAGYAaQBsACAAZwBlAG4A6QByAGkAYwBvACAAZABlACAAYwBpAG4AegBlAG4AdABvAHMAIABk -AGEAIABHAGEAbQBtAGEAIAAyACwAMgBBAGwAZwBlAG0AZQBlAG4AIABnAHIAaQBqAHMAIABnAGEAbQBt -AGEAIAAyACwAMgAtAHAAcgBvAGYAaQBlAGwAUABlAHIAZgBpAGwAIABnAGUAbgDpAHIAaQBjAG8AIABk -AGUAIABnAGEAbQBtAGEAIABkAGUAIABnAHIAaQBzAGUAcwAgADIALAAyDiMOMQ4HDioONQ5BDgEOIQ4h -DjIOQA4BDiMOIg5MDhcOMQ5IDicORA4bACAAMgAuADIARwBlAG4AZQBsACAARwByAGkAIABHAGEAbQBh -ACAAMgAsADIAWQBsAGUAaQBuAGUAbgAgAGgAYQByAG0AYQBhAG4AIABnAGEAbQBtAGEAIAAyACwAMgAg -AC0AcAByAG8AZgBpAGkAbABpAEcAZQBuAGUAcgBpAQ0AawBpACAARwByAGEAeQAgAEcAYQBtAG0AYQAg -ADIALgAyACAAcAByAG8AZgBpAGwAVQBuAGkAdwBlAHIAcwBhAGwAbgB5ACAAcAByAG8AZgBpAGwAIABz -AHoAYQByAG8BWwBjAGkAIABnAGEAbQBtAGEAIAAyACwAMgY6BicGRQYnACAAMgAuADIAIAZEBkgGRgAg -BjEGRQYnBi8GSgAgBjkGJwZFBB4EMQRJBDAETwAgBEEENQRABDAETwAgBDMEMAQ8BDwEMAAgADIALAAy -AC0EPwRABD4ERAQ4BDsETABHAGUAbgBlAHIAaQBjACAARwByAGEAeQAgAEcAYQBtAG0AYQAgADIALgAy -ACAAUAByAG8AZgBpAGwAZQAAdGV4dAAAAABDb3B5cmlnaHQgQXBwbGUgSW5jLiwgMjAxMgAAWFlaIAAA -AAAAAPNRAAEAAAABFsxjdXJ2AAAAAAAABAAAAAAFAAoADwAUABkAHgAjACgALQAyADcAOwBAAEUASgBP -AFQAWQBeAGMAaABtAHIAdwB8AIEAhgCLAJAAlQCaAJ8ApACpAK4AsgC3ALwAwQDGAMsA0ADVANsA4ADl -AOsA8AD2APsBAQEHAQ0BEwEZAR8BJQErATIBOAE+AUUBTAFSAVkBYAFnAW4BdQF8AYMBiwGSAZoBoQGp -AbEBuQHBAckB0QHZAeEB6QHyAfoCAwIMAhQCHQImAi8COAJBAksCVAJdAmcCcQJ6AoQCjgKYAqICrAK2 -AsECywLVAuAC6wL1AwADCwMWAyEDLQM4A0MDTwNaA2YDcgN+A4oDlgOiA64DugPHA9MD4APsA/kEBgQT -BCAELQQ7BEgEVQRjBHEEfgSMBJoEqAS2BMQE0wThBPAE/gUNBRwFKwU6BUkFWAVnBXcFhgWWBaYFtQXF -BdUF5QX2BgYGFgYnBjcGSAZZBmoGewaMBp0GrwbABtEG4wb1BwcHGQcrBz0HTwdhB3QHhgeZB6wHvwfS -B+UH+AgLCB8IMghGCFoIbgiCCJYIqgi+CNII5wj7CRAJJQk6CU8JZAl5CY8JpAm6Cc8J5Qn7ChEKJwo9 -ClQKagqBCpgKrgrFCtwK8wsLCyILOQtRC2kLgAuYC7ALyAvhC/kMEgwqDEMMXAx1DI4MpwzADNkM8w0N -DSYNQA1aDXQNjg2pDcMN3g34DhMOLg5JDmQOfw6bDrYO0g7uDwkPJQ9BD14Peg+WD7MPzw/sEAkQJhBD -EGEQfhCbELkQ1xD1ERMRMRFPEW0RjBGqEckR6BIHEiYSRRJkEoQSoxLDEuMTAxMjE0MTYxODE6QTxRPl -FAYUJxRJFGoUixStFM4U8BUSFTQVVhV4FZsVvRXgFgMWJhZJFmwWjxayFtYW+hcdF0EXZReJF64X0hf3 -GBsYQBhlGIoYrxjVGPoZIBlFGWsZkRm3Gd0aBBoqGlEadxqeGsUa7BsUGzsbYxuKG7Ib2hwCHCocUhx7 -HKMczBz1HR4dRx1wHZkdwx3sHhYeQB5qHpQevh7pHxMfPh9pH5Qfvx/qIBUgQSBsIJggxCDwIRwhSCF1 -IaEhziH7IiciVSKCIq8i3SMKIzgjZiOUI8Ij8CQfJE0kfCSrJNolCSU4JWgllyXHJfcmJyZXJocmtybo -JxgnSSd6J6sn3CgNKD8ocSiiKNQpBik4KWspnSnQKgIqNSpoKpsqzysCKzYraSudK9EsBSw5LG4soizX -LQwtQS12Last4S4WLkwugi63Lu4vJC9aL5Evxy/+MDUwbDCkMNsxEjFKMYIxujHyMioyYzKbMtQzDTNG -M38zuDPxNCs0ZTSeNNg1EzVNNYc1wjX9Njc2cjauNuk3JDdgN5w31zgUOFA4jDjIOQU5Qjl/Obw5+To2 -OnQ6sjrvOy07azuqO+g8JzxlPKQ84z0iPWE9oT3gPiA+YD6gPuA/IT9hP6I/4kAjQGRApkDnQSlBakGs -Qe5CMEJyQrVC90M6Q31DwEQDREdEikTORRJFVUWaRd5GIkZnRqtG8Ec1R3tHwEgFSEtIkUjXSR1JY0mp -SfBKN0p9SsRLDEtTS5pL4kwqTHJMuk0CTUpNk03cTiVObk63TwBPSU+TT91QJ1BxULtRBlFQUZtR5lIx -UnxSx1MTU19TqlP2VEJUj1TbVShVdVXCVg9WXFapVvdXRFeSV+BYL1h9WMtZGllpWbhaB1pWWqZa9VtF -W5Vb5Vw1XIZc1l0nXXhdyV4aXmxevV8PX2Ffs2AFYFdgqmD8YU9homH1YklinGLwY0Njl2PrZEBklGTp -ZT1lkmXnZj1mkmboZz1nk2fpaD9olmjsaUNpmmnxakhqn2r3a09rp2v/bFdsr20IbWBtuW4SbmtuxG8e -b3hv0XArcIZw4HE6cZVx8HJLcqZzAXNdc7h0FHRwdMx1KHWFdeF2Pnabdvh3VnezeBF4bnjMeSp5iXnn -ekZ6pXsEe2N7wnwhfIF84X1BfaF+AX5ifsJ/I3+Ef+WAR4CogQqBa4HNgjCCkoL0g1eDuoQdhICE44VH -hauGDoZyhteHO4efiASIaYjOiTOJmYn+imSKyoswi5aL/IxjjMqNMY2Yjf+OZo7OjzaPnpAGkG6Q1pE/ -kaiSEZJ6kuOTTZO2lCCUipT0lV+VyZY0lp+XCpd1l+CYTJi4mSSZkJn8mmia1ZtCm6+cHJyJnPedZJ3S -nkCerp8dn4uf+qBpoNihR6G2oiailqMGo3aj5qRWpMelOKWpphqmi6b9p26n4KhSqMSpN6mpqhyqj6sC -q3Wr6axcrNCtRK24ri2uoa8Wr4uwALB1sOqxYLHWskuywrM4s660JbSctRO1irYBtnm28Ldot+C4WbjR -uUq5wro7urW7LrunvCG8m70VvY++Cr6Evv+/er/1wHDA7MFnwePCX8Lbw1jD1MRRxM7FS8XIxkbGw8dB -x7/IPci8yTrJuco4yrfLNsu2zDXMtc01zbXONs62zzfPuNA50LrRPNG+0j/SwdNE08bUSdTL1U7V0dZV -1tjXXNfg2GTY6Nls2fHadtr724DcBdyK3RDdlt4c3qLfKd+v4DbgveFE4cziU+Lb42Pj6+Rz5PzlhOYN -5pbnH+ep6DLovOlG6dDqW+rl63Dr++yG7RHtnO4o7rTvQO/M8Fjw5fFy8f/yjPMZ86f0NPTC9VD13vZt -9vv3ivgZ+Kj5OPnH+lf65/t3/Af8mP0p/br+S/7c/23//9IrLC0uWiRjbGFzc25hbWVYJGNsYXNzZXNf -EBBOU0JpdG1hcEltYWdlUmVwoy0vMFpOU0ltYWdlUmVwWE5TT2JqZWN00issMjNXTlNBcnJheaIyMNIr -LDU2Xk5TTXV0YWJsZUFycmF5ozUyMNM4OQ86OzxXTlNXaGl0ZVxOU0NvbG9yU3BhY2VEMCAwABADgAzS -Kyw+P1dOU0NvbG9yoj4w0issQUJXTlNJbWFnZaJBMAAIABEAGgAkACkAMgA3AEkATABRAFMAYgBoAHUA -fACLAJIAnwCmAK4AsACyALQAuQC7AL0AxADJANQA1gDYANoA3wDiAOQA5gDoAO8BBgEiASQBJhOWE5sT -phOvE8ITxhPRE9oT3xPnE+oT7xP+FAIUCRQRFB4UIxQlFCcULBQ0FDcUPBREAAAAAAAAAgEAAAAAAAAA -QwAAAAAAAAAAAAAAAAAAFEc +S2V5ZWRBcmNoaXZlctEICVRyb290gAGuCwwbHCEaJissMzY5P0JVJG51bGzXDQ4PEBESExQVFhcYGRpW +TlNTaXplViRjbGFzc1xOU0ltYWdlRmxhZ3NWTlNSZXBzW05TVGludENvbG9yV05TQ29sb3JeTlNSZXNp +emluZ01vZGWAAoANEiDDAACAA4AAgAsQAFZ7MSwgMX3SHQ4eIFpOUy5vYmplY3RzoR+ABIAK0h0OIiWi +IySABYAGgAnTDicoKSoaXxAUTlNUSUZGUmVwcmVzZW50YXRpb25fEBlOU0ludGVybmFsTGF5b3V0RGly +ZWN0aW9ugAiAB08REmxNTQAqAAAACgAAABABAAADAAAAAQABAAABAQADAAAAAQABAAABAgADAAAAAgAI +AAgBAwADAAAAAQABAAABBgADAAAAAQABAAABCgADAAAAAQABAAABEQAEAAAAAQAAAAgBEgADAAAAAQAB +AAABFQADAAAAAQACAAABFgADAAAAAQABAAABFwAEAAAAAQAAAAIBHAADAAAAAQABAAABKAADAAAAAQAC +AAABUgADAAAAAQABAAABUwADAAAAAgABAAGHcwAHAAARnAAAANAAAAAAAAARnGFwcGwCAAAAbW50ckdS +QVlYWVogB9wACAAXAA8ALgAPYWNzcEFQUEwAAAAAbm9uZQAAAAAAAAAAAAAAAAAAAAAAAPbWAAEAAAAA +0y1hcHBsAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFZGVzYwAA +AMAAAAB5ZHNjbQAAATwAAAgaY3BydAAACVgAAAAjd3RwdAAACXwAAAAUa1RSQwAACZAAAAgMZGVzYwAA +AAAAAAAfR2VuZXJpYyBHcmF5IEdhbW1hIDIuMiBQcm9maWxlAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAG1s +dWMAAAAAAAAAHwAAAAxza1NLAAAALgAAAYRkYURLAAAAOgAAAbJjYUVTAAAAOAAAAex2aVZOAAAAQAAA +AiRwdEJSAAAASgAAAmR1a1VBAAAALAAAAq5mckZVAAAAPgAAAtpodUhVAAAANAAAAxh6aFRXAAAAGgAA +A0xrb0tSAAAAIgAAA2ZuYk5PAAAAOgAAA4hjc0NaAAAAKAAAA8JoZUlMAAAAJAAAA+pyb1JPAAAAKgAA +BA5kZURFAAAATgAABDhpdElUAAAATgAABIZzdlNFAAAAOAAABNR6aENOAAAAGgAABQxqYUpQAAAAJgAA +BSZlbEdSAAAAKgAABUxwdFBPAAAAUgAABXZubE5MAAAAQAAABchlc0VTAAAATAAABgh0aFRIAAAAMgAA +BlR0clRSAAAAJAAABoZmaUZJAAAARgAABqpockhSAAAAPgAABvBwbFBMAAAASgAABy5hckVHAAAALAAA +B3hydVJVAAAAOgAAB6RlblVTAAAAPAAAB94AVgFhAGUAbwBiAGUAYwBuAOEAIABzAGkAdgDhACAAZwBh +AG0AYQAgADIALAAyAEcAZQBuAGUAcgBpAHMAawAgAGcAcgDlACAAMgAsADIAIABnAGEAbQBtAGEALQBw +AHIAbwBmAGkAbABHAGEAbQBtAGEAIABkAGUAIABnAHIAaQBzAG8AcwAgAGcAZQBuAOgAcgBpAGMAYQAg +ADIALgAyAEMepQB1ACAAaADsAG4AaAAgAE0A4AB1ACAAeADhAG0AIABDAGgAdQBuAGcAIABHAGEAbQBt +AGEAIAAyAC4AMgBQAGUAcgBmAGkAbAAgAEcAZQBuAOkAcgBpAGMAbwAgAGQAYQAgAEcAYQBtAGEAIABk +AGUAIABDAGkAbgB6AGEAcwAgADIALAAyBBcEMAQzBDAEOwRMBD0EMAAgAEcAcgBhAHkALQQzBDAEPAQw +ACAAMgAuADIAUAByAG8AZgBpAGwAIABnAOkAbgDpAHIAaQBxAHUAZQAgAGcAcgBpAHMAIABnAGEAbQBt +AGEAIAAyACwAMgDBAGwAdABhAGwA4QBuAG8AcwAgAHMAegD8AHIAawBlACAAZwBhAG0AbQBhACAAMgAu +ADKQGnUocHCWjlFJXqYAMgAuADKCcl9pY8+P8Md8vBgAINaMwMkAIKwQucgAIAAyAC4AMgAg1QS4XNMM +x3wARwBlAG4AZQByAGkAcwBrACAAZwByAOUAIABnAGEAbQBtAGEAIAAyACwAMgAtAHAAcgBvAGYAaQBs +AE8AYgBlAGMAbgDhACABYQBlAGQA4QAgAGcAYQBtAGEAIAAyAC4AMgXSBdAF3gXUACAF0AXkBdUF6AAg +BdsF3AXcBdkAIAAyAC4AMgBHAGEAbQBhACAAZwByAGkAIABnAGUAbgBlAHIAaQBjAQMAIAAyACwAMgBB +AGwAbABnAGUAbQBlAGkAbgBlAHMAIABHAHIAYQB1AHMAdAB1AGYAZQBuAC0AUAByAG8AZgBpAGwAIABH +AGEAbQBtAGEAIAAyACwAMgBQAHIAbwBmAGkAbABvACAAZwByAGkAZwBpAG8AIABnAGUAbgBlAHIAaQBj +AG8AIABkAGUAbABsAGEAIABnAGEAbQBtAGEAIAAyACwAMgBHAGUAbgBlAHIAaQBzAGsAIABnAHIA5QAg +ADIALAAyACAAZwBhAG0AbQBhAHAAcgBvAGYAaQBsZm6QGnBwXqZ8+2VwADIALgAyY8+P8GWHTvZOAIIs +MLAw7DCkMKww8zDeACAAMgAuADIAIDDXMO0w1TChMKQw6wOTA7UDvQO5A7oDzAAgA5MDugPBA7kAIAOT +A6wDvAO8A7EAIAAyAC4AMgBQAGUAcgBmAGkAbAAgAGcAZQBuAOkAcgBpAGMAbwAgAGQAZQAgAGMAaQBu +AHoAZQBuAHQAbwBzACAAZABhACAARwBhAG0AbQBhACAAMgAsADIAQQBsAGcAZQBtAGUAZQBuACAAZwBy +AGkAagBzACAAZwBhAG0AbQBhACAAMgAsADIALQBwAHIAbwBmAGkAZQBsAFAAZQByAGYAaQBsACAAZwBl +AG4A6QByAGkAYwBvACAAZABlACAAZwBhAG0AbQBhACAAZABlACAAZwByAGkAcwBlAHMAIAAyACwAMg4j +DjEOBw4qDjUOQQ4BDiEOIQ4yDkAOAQ4jDiIOTA4XDjEOSA4nDkQOGwAgADIALgAyAEcAZQBuAGUAbAAg +AEcAcgBpACAARwBhAG0AYQAgADIALAAyAFkAbABlAGkAbgBlAG4AIABoAGEAcgBtAGEAYQBuACAAZwBh +AG0AbQBhACAAMgAsADIAIAAtAHAAcgBvAGYAaQBpAGwAaQBHAGUAbgBlAHIAaQENAGsAaQAgAEcAcgBh +AHkAIABHAGEAbQBtAGEAIAAyAC4AMgAgAHAAcgBvAGYAaQBsAFUAbgBpAHcAZQByAHMAYQBsAG4AeQAg +AHAAcgBvAGYAaQBsACAAcwB6AGEAcgBvAVsAYwBpACAAZwBhAG0AbQBhACAAMgAsADIGOgYnBkUGJwAg +ADIALgAyACAGRAZIBkYAIAYxBkUGJwYvBkoAIAY5BicGRQQeBDEESQQwBE8AIARBBDUEQAQwBE8AIAQz +BDAEPAQ8BDAAIAAyACwAMgAtBD8EQAQ+BEQEOAQ7BEwARwBlAG4AZQByAGkAYwAgAEcAcgBhAHkAIABH +AGEAbQBtAGEAIAAyAC4AMgAgAFAAcgBvAGYAaQBsAGUAAHRleHQAAAAAQ29weXJpZ2h0IEFwcGxlIElu +Yy4sIDIwMTIAAFhZWiAAAAAAAADzUQABAAAAARbMY3VydgAAAAAAAAQAAAAABQAKAA8AFAAZAB4AIwAo +AC0AMgA3ADsAQABFAEoATwBUAFkAXgBjAGgAbQByAHcAfACBAIYAiwCQAJUAmgCfAKQAqQCuALIAtwC8 +AMEAxgDLANAA1QDbAOAA5QDrAPAA9gD7AQEBBwENARMBGQEfASUBKwEyATgBPgFFAUwBUgFZAWABZwFu +AXUBfAGDAYsBkgGaAaEBqQGxAbkBwQHJAdEB2QHhAekB8gH6AgMCDAIUAh0CJgIvAjgCQQJLAlQCXQJn +AnECegKEAo4CmAKiAqwCtgLBAssC1QLgAusC9QMAAwsDFgMhAy0DOANDA08DWgNmA3IDfgOKA5YDogOu +A7oDxwPTA+AD7AP5BAYEEwQgBC0EOwRIBFUEYwRxBH4EjASaBKgEtgTEBNME4QTwBP4FDQUcBSsFOgVJ +BVgFZwV3BYYFlgWmBbUFxQXVBeUF9gYGBhYGJwY3BkgGWQZqBnsGjAadBq8GwAbRBuMG9QcHBxkHKwc9 +B08HYQd0B4YHmQesB78H0gflB/gICwgfCDIIRghaCG4IggiWCKoIvgjSCOcI+wkQCSUJOglPCWQJeQmP +CaQJugnPCeUJ+woRCicKPQpUCmoKgQqYCq4KxQrcCvMLCwsiCzkLUQtpC4ALmAuwC8gL4Qv5DBIMKgxD +DFwMdQyODKcMwAzZDPMNDQ0mDUANWg10DY4NqQ3DDd4N+A4TDi4OSQ5kDn8Omw62DtIO7g8JDyUPQQ9e +D3oPlg+zD88P7BAJECYQQxBhEH4QmxC5ENcQ9RETETERTxFtEYwRqhHJEegSBxImEkUSZBKEEqMSwxLj +EwMTIxNDE2MTgxOkE8UT5RQGFCcUSRRqFIsUrRTOFPAVEhU0FVYVeBWbFb0V4BYDFiYWSRZsFo8WshbW +FvoXHRdBF2UXiReuF9IX9xgbGEAYZRiKGK8Y1Rj6GSAZRRlrGZEZtxndGgQaKhpRGncanhrFGuwbFBs7 +G2MbihuyG9ocAhwqHFIcexyjHMwc9R0eHUcdcB2ZHcMd7B4WHkAeah6UHr4e6R8THz4faR+UH78f6iAV +IEEgbCCYIMQg8CEcIUghdSGhIc4h+yInIlUigiKvIt0jCiM4I2YjlCPCI/AkHyRNJHwkqyTaJQklOCVo +JZclxyX3JicmVyaHJrcm6CcYJ0kneierJ9woDSg/KHEooijUKQYpOClrKZ0p0CoCKjUqaCqbKs8rAis2 +K2krnSvRLAUsOSxuLKIs1y0MLUEtdi2rLeEuFi5MLoIuty7uLyQvWi+RL8cv/jA1MGwwpDDbMRIxSjGC +Mbox8jIqMmMymzLUMw0zRjN/M7gz8TQrNGU0njTYNRM1TTWHNcI1/TY3NnI2rjbpNyQ3YDecN9c4FDhQ +OIw4yDkFOUI5fzm8Ofk6Njp0OrI67zstO2s7qjvoPCc8ZTykPOM9Ij1hPaE94D4gPmA+oD7gPyE/YT+i +P+JAI0BkQKZA50EpQWpBrEHuQjBCckK1QvdDOkN9Q8BEA0RHRIpEzkUSRVVFmkXeRiJGZ0arRvBHNUd7 +R8BIBUhLSJFI10kdSWNJqUnwSjdKfUrESwxLU0uaS+JMKkxyTLpNAk1KTZNN3E4lTm5Ot08AT0lPk0/d +UCdQcVC7UQZRUFGbUeZSMVJ8UsdTE1NfU6pT9lRCVI9U21UoVXVVwlYPVlxWqVb3V0RXklfgWC9YfVjL +WRpZaVm4WgdaVlqmWvVbRVuVW+VcNVyGXNZdJ114XcleGl5sXr1fD19hX7NgBWBXYKpg/GFPYaJh9WJJ +Ypxi8GNDY5dj62RAZJRk6WU9ZZJl52Y9ZpJm6Gc9Z5Nn6Wg/aJZo7GlDaZpp8WpIap9q92tPa6dr/2xX +bK9tCG1gbbluEm5rbsRvHm94b9FwK3CGcOBxOnGVcfByS3KmcwFzXXO4dBR0cHTMdSh1hXXhdj52m3b4 +d1Z3s3gReG54zHkqeYl553pGeqV7BHtje8J8IXyBfOF9QX2hfgF+Yn7CfyN/hH/lgEeAqIEKgWuBzYIw +gpKC9INXg7qEHYSAhOOFR4Wrhg6GcobXhzuHn4gEiGmIzokziZmJ/opkisqLMIuWi/yMY4zKjTGNmI3/ +jmaOzo82j56QBpBukNaRP5GokhGSepLjk02TtpQglIqU9JVflcmWNJaflwqXdZfgmEyYuJkkmZCZ/Jpo +mtWbQpuvnByciZz3nWSd0p5Anq6fHZ+Ln/qgaaDYoUehtqImopajBqN2o+akVqTHpTilqaYapoum/adu +p+CoUqjEqTepqaocqo+rAqt1q+msXKzQrUStuK4trqGvFq+LsACwdbDqsWCx1rJLssKzOLOutCW0nLUT +tYq2AbZ5tvC3aLfguFm40blKucK6O7q1uy67p7whvJu9Fb2Pvgq+hL7/v3q/9cBwwOzBZ8Hjwl/C28NY +w9TEUcTOxUvFyMZGxsPHQce/yD3IvMk6ybnKOMq3yzbLtsw1zLXNNc21zjbOts83z7jQOdC60TzRvtI/ +0sHTRNPG1EnUy9VO1dHWVdbY11zX4Nhk2OjZbNnx2nba+9uA3AXcit0Q3ZbeHN6i3ynfr+A24L3hROHM +4lPi2+Nj4+vkc+T85YTmDeaW5x/nqegy6LzpRunQ6lvq5etw6/vshu0R7ZzuKO6070DvzPBY8OXxcvH/ +8ozzGfOn9DT0wvVQ9d72bfb794r4Gfio+Tj5x/pX+uf7d/wH/Jj9Kf26/kv+3P9t///SLS4vMFokY2xh +c3NuYW1lWCRjbGFzc2VzXxAQTlNCaXRtYXBJbWFnZVJlcKMvMTJaTlNJbWFnZVJlcFhOU09iamVjdNIt +LjQ1V05TQXJyYXmiNDLSLS43OF5OU011dGFibGVBcnJheaM3NDLTOjsOPD0+V05TV2hpdGVcTlNDb2xv +clNwYWNlRDAgMAAQA4AM0i0uQEFXTlNDb2xvcqJAMtItLkNEV05TSW1hZ2WiQzIACAARABoAJAApADIA +NwBJAEwAUQBTAGIAaAB3AH4AhQCSAJkApQCtALwAvgDAAMUAxwDJAMsAzQDUANkA5ADmAOgA6gDvAPIA +9AD2APgA/wEWATIBNAE2E6YTqxO2E78T0hPWE+ET6hPvE/cT+hP/FA4UEhQZFCEULhQzFDUUNxQ8FEQU +RxRMFFQAAAAAAAACAQAAAAAAAABFAAAAAAAAAAAAAAAAAAAUVw diff --git a/i18n/src/main/resources/en.lproj/Info.xib b/i18n/src/main/resources/en.lproj/Info.xib index a8949a0856..92dd1ed026 100644 --- a/i18n/src/main/resources/en.lproj/Info.xib +++ b/i18n/src/main/resources/en.lproj/Info.xib @@ -1,7 +1,7 @@ - + - + @@ -89,7 +89,7 @@ - + @@ -953,7 +953,7 @@ - + @@ -1017,21 +1017,21 @@ - + - + - + @@ -1057,7 +1057,7 @@ - + @@ -1121,21 +1121,21 @@ - + - + - + @@ -1250,27 +1250,27 @@ @@ -1278,8 +1278,8 @@ - - - + + + diff --git a/i18n/src/main/resources/en.lproj/Main.xib b/i18n/src/main/resources/en.lproj/Main.xib index bd1125b830..0e0e507cd3 100644 --- a/i18n/src/main/resources/en.lproj/Main.xib +++ b/i18n/src/main/resources/en.lproj/Main.xib @@ -1,8 +1,7 @@ - + - + @@ -160,19 +159,19 @@ - + - + - + @@ -210,8 +209,8 @@ -CA - + CA + @@ -322,12 +321,12 @@ CA - + - + @@ -567,6 +566,10 @@ CA - + + + + + diff --git a/i18n/src/main/resources/en.lproj/S3.strings b/i18n/src/main/resources/en.lproj/S3.strings index 44f7c3a53b..648da7203f 100644 Binary files a/i18n/src/main/resources/en.lproj/S3.strings and b/i18n/src/main/resources/en.lproj/S3.strings differ diff --git a/i18n/src/main/resources/es.lproj/Bookmark.xib b/i18n/src/main/resources/es.lproj/Bookmark.xib index 1f3d44745b..a9c90c43a7 100644 --- a/i18n/src/main/resources/es.lproj/Bookmark.xib +++ b/i18n/src/main/resources/es.lproj/Bookmark.xib @@ -1,7 +1,7 @@ - + - + @@ -92,7 +92,7 @@ - + @@ -489,95 +489,95 @@ YnBsaXN0MDDUAQIDBAUGBwpYJHZlcnNpb25ZJGFyY2hpdmVyVCR0b3BYJG9iamVjdHMSAAGGoF8QD05T -S2V5ZWRBcmNoaXZlctEICVRyb290gAGuCwwZGh8UJCkqMTQ3PUBVJG51bGzWDQ4PEBESExQVFhcYVk5T -U2l6ZV5OU1Jlc2l6aW5nTW9kZVYkY2xhc3NcTlNJbWFnZUZsYWdzVk5TUmVwc1dOU0NvbG9ygAIQAIAN -EiDDAACAA4ALVnsxLCAxfdIbDxweWk5TLm9iamVjdHOhHYAEgArSGw8gI6IhIoAFgAaACdMPJSYnKBRf -EBROU1RJRkZSZXByZXNlbnRhdGlvbl8QGU5TSW50ZXJuYWxMYXlvdXREaXJlY3Rpb26ACIAHTxESbE1N -ACoAAAAKAAAAEAEAAAMAAAABAAEAAAEBAAMAAAABAAEAAAECAAMAAAACAAgACAEDAAMAAAABAAEAAAEG -AAMAAAABAAEAAAEKAAMAAAABAAEAAAERAAQAAAABAAAACAESAAMAAAABAAEAAAEVAAMAAAABAAIAAAEW -AAMAAAABAAEAAAEXAAQAAAABAAAAAgEcAAMAAAABAAEAAAEoAAMAAAABAAIAAAFSAAMAAAABAAEAAAFT -AAMAAAACAAEAAYdzAAcAABGcAAAA0AAAAAAAABGcYXBwbAIAAABtbnRyR1JBWVhZWiAH3AAIABcADwAu -AA9hY3NwQVBQTAAAAABub25lAAAAAAAAAAAAAAAAAAAAAAAA9tYAAQAAAADTLWFwcGwAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAVkZXNjAAAAwAAAAHlkc2NtAAABPAAA -CBpjcHJ0AAAJWAAAACN3dHB0AAAJfAAAABRrVFJDAAAJkAAACAxkZXNjAAAAAAAAAB9HZW5lcmljIEdy -YXkgR2FtbWEgMi4yIFByb2ZpbGUAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAbWx1YwAAAAAAAAAfAAAADHNr -U0sAAAAuAAABhGRhREsAAAA6AAABsmNhRVMAAAA4AAAB7HZpVk4AAABAAAACJHB0QlIAAABKAAACZHVr -VUEAAAAsAAACrmZyRlUAAAA+AAAC2mh1SFUAAAA0AAADGHpoVFcAAAAaAAADTGtvS1IAAAAiAAADZm5i -Tk8AAAA6AAADiGNzQ1oAAAAoAAADwmhlSUwAAAAkAAAD6nJvUk8AAAAqAAAEDmRlREUAAABOAAAEOGl0 -SVQAAABOAAAEhnN2U0UAAAA4AAAE1HpoQ04AAAAaAAAFDGphSlAAAAAmAAAFJmVsR1IAAAAqAAAFTHB0 -UE8AAABSAAAFdm5sTkwAAABAAAAFyGVzRVMAAABMAAAGCHRoVEgAAAAyAAAGVHRyVFIAAAAkAAAGhmZp -RkkAAABGAAAGqmhySFIAAAA+AAAG8HBsUEwAAABKAAAHLmFyRUcAAAAsAAAHeHJ1UlUAAAA6AAAHpGVu -VVMAAAA8AAAH3gBWAWEAZQBvAGIAZQBjAG4A4QAgAHMAaQB2AOEAIABnAGEAbQBhACAAMgAsADIARwBl -AG4AZQByAGkAcwBrACAAZwByAOUAIAAyACwAMgAgAGcAYQBtAG0AYQAtAHAAcgBvAGYAaQBsAEcAYQBt -AG0AYQAgAGQAZQAgAGcAcgBpAHMAbwBzACAAZwBlAG4A6AByAGkAYwBhACAAMgAuADIAQx6lAHUAIABo -AOwAbgBoACAATQDgAHUAIAB4AOEAbQAgAEMAaAB1AG4AZwAgAEcAYQBtAG0AYQAgADIALgAyAFAAZQBy -AGYAaQBsACAARwBlAG4A6QByAGkAYwBvACAAZABhACAARwBhAG0AYQAgAGQAZQAgAEMAaQBuAHoAYQBz -ACAAMgAsADIEFwQwBDMEMAQ7BEwEPQQwACAARwByAGEAeQAtBDMEMAQ8BDAAIAAyAC4AMgBQAHIAbwBm -AGkAbAAgAGcA6QBuAOkAcgBpAHEAdQBlACAAZwByAGkAcwAgAGcAYQBtAG0AYQAgADIALAAyAMEAbAB0 -AGEAbADhAG4AbwBzACAAcwB6APwAcgBrAGUAIABnAGEAbQBtAGEAIAAyAC4AMpAadShwcJaOUUlepgAy -AC4AMoJyX2ljz4/wx3y8GAAg1ozAyQAgrBC5yAAgADIALgAyACDVBLhc0wzHfABHAGUAbgBlAHIAaQBz -AGsAIABnAHIA5QAgAGcAYQBtAG0AYQAgADIALAAyAC0AcAByAG8AZgBpAGwATwBiAGUAYwBuAOEAIAFh -AGUAZADhACAAZwBhAG0AYQAgADIALgAyBdIF0AXeBdQAIAXQBeQF1QXoACAF2wXcBdwF2QAgADIALgAy -AEcAYQBtAGEAIABnAHIAaQAgAGcAZQBuAGUAcgBpAGMBAwAgADIALAAyAEEAbABsAGcAZQBtAGUAaQBu -AGUAcwAgAEcAcgBhAHUAcwB0AHUAZgBlAG4ALQBQAHIAbwBmAGkAbAAgAEcAYQBtAG0AYQAgADIALAAy -AFAAcgBvAGYAaQBsAG8AIABnAHIAaQBnAGkAbwAgAGcAZQBuAGUAcgBpAGMAbwAgAGQAZQBsAGwAYQAg -AGcAYQBtAG0AYQAgADIALAAyAEcAZQBuAGUAcgBpAHMAawAgAGcAcgDlACAAMgAsADIAIABnAGEAbQBt -AGEAcAByAG8AZgBpAGxmbpAacHBepnz7ZXAAMgAuADJjz4/wZYdO9k4AgiwwsDDsMKQwrDDzMN4AIAAy -AC4AMgAgMNcw7TDVMKEwpDDrA5MDtQO9A7kDugPMACADkwO6A8EDuQAgA5MDrAO8A7wDsQAgADIALgAy -AFAAZQByAGYAaQBsACAAZwBlAG4A6QByAGkAYwBvACAAZABlACAAYwBpAG4AegBlAG4AdABvAHMAIABk -AGEAIABHAGEAbQBtAGEAIAAyACwAMgBBAGwAZwBlAG0AZQBlAG4AIABnAHIAaQBqAHMAIABnAGEAbQBt -AGEAIAAyACwAMgAtAHAAcgBvAGYAaQBlAGwAUABlAHIAZgBpAGwAIABnAGUAbgDpAHIAaQBjAG8AIABk -AGUAIABnAGEAbQBtAGEAIABkAGUAIABnAHIAaQBzAGUAcwAgADIALAAyDiMOMQ4HDioONQ5BDgEOIQ4h -DjIOQA4BDiMOIg5MDhcOMQ5IDicORA4bACAAMgAuADIARwBlAG4AZQBsACAARwByAGkAIABHAGEAbQBh -ACAAMgAsADIAWQBsAGUAaQBuAGUAbgAgAGgAYQByAG0AYQBhAG4AIABnAGEAbQBtAGEAIAAyACwAMgAg -AC0AcAByAG8AZgBpAGkAbABpAEcAZQBuAGUAcgBpAQ0AawBpACAARwByAGEAeQAgAEcAYQBtAG0AYQAg -ADIALgAyACAAcAByAG8AZgBpAGwAVQBuAGkAdwBlAHIAcwBhAGwAbgB5ACAAcAByAG8AZgBpAGwAIABz -AHoAYQByAG8BWwBjAGkAIABnAGEAbQBtAGEAIAAyACwAMgY6BicGRQYnACAAMgAuADIAIAZEBkgGRgAg -BjEGRQYnBi8GSgAgBjkGJwZFBB4EMQRJBDAETwAgBEEENQRABDAETwAgBDMEMAQ8BDwEMAAgADIALAAy -AC0EPwRABD4ERAQ4BDsETABHAGUAbgBlAHIAaQBjACAARwByAGEAeQAgAEcAYQBtAG0AYQAgADIALgAy -ACAAUAByAG8AZgBpAGwAZQAAdGV4dAAAAABDb3B5cmlnaHQgQXBwbGUgSW5jLiwgMjAxMgAAWFlaIAAA -AAAAAPNRAAEAAAABFsxjdXJ2AAAAAAAABAAAAAAFAAoADwAUABkAHgAjACgALQAyADcAOwBAAEUASgBP -AFQAWQBeAGMAaABtAHIAdwB8AIEAhgCLAJAAlQCaAJ8ApACpAK4AsgC3ALwAwQDGAMsA0ADVANsA4ADl -AOsA8AD2APsBAQEHAQ0BEwEZAR8BJQErATIBOAE+AUUBTAFSAVkBYAFnAW4BdQF8AYMBiwGSAZoBoQGp -AbEBuQHBAckB0QHZAeEB6QHyAfoCAwIMAhQCHQImAi8COAJBAksCVAJdAmcCcQJ6AoQCjgKYAqICrAK2 -AsECywLVAuAC6wL1AwADCwMWAyEDLQM4A0MDTwNaA2YDcgN+A4oDlgOiA64DugPHA9MD4APsA/kEBgQT -BCAELQQ7BEgEVQRjBHEEfgSMBJoEqAS2BMQE0wThBPAE/gUNBRwFKwU6BUkFWAVnBXcFhgWWBaYFtQXF -BdUF5QX2BgYGFgYnBjcGSAZZBmoGewaMBp0GrwbABtEG4wb1BwcHGQcrBz0HTwdhB3QHhgeZB6wHvwfS -B+UH+AgLCB8IMghGCFoIbgiCCJYIqgi+CNII5wj7CRAJJQk6CU8JZAl5CY8JpAm6Cc8J5Qn7ChEKJwo9 -ClQKagqBCpgKrgrFCtwK8wsLCyILOQtRC2kLgAuYC7ALyAvhC/kMEgwqDEMMXAx1DI4MpwzADNkM8w0N -DSYNQA1aDXQNjg2pDcMN3g34DhMOLg5JDmQOfw6bDrYO0g7uDwkPJQ9BD14Peg+WD7MPzw/sEAkQJhBD -EGEQfhCbELkQ1xD1ERMRMRFPEW0RjBGqEckR6BIHEiYSRRJkEoQSoxLDEuMTAxMjE0MTYxODE6QTxRPl -FAYUJxRJFGoUixStFM4U8BUSFTQVVhV4FZsVvRXgFgMWJhZJFmwWjxayFtYW+hcdF0EXZReJF64X0hf3 -GBsYQBhlGIoYrxjVGPoZIBlFGWsZkRm3Gd0aBBoqGlEadxqeGsUa7BsUGzsbYxuKG7Ib2hwCHCocUhx7 -HKMczBz1HR4dRx1wHZkdwx3sHhYeQB5qHpQevh7pHxMfPh9pH5Qfvx/qIBUgQSBsIJggxCDwIRwhSCF1 -IaEhziH7IiciVSKCIq8i3SMKIzgjZiOUI8Ij8CQfJE0kfCSrJNolCSU4JWgllyXHJfcmJyZXJocmtybo -JxgnSSd6J6sn3CgNKD8ocSiiKNQpBik4KWspnSnQKgIqNSpoKpsqzysCKzYraSudK9EsBSw5LG4soizX -LQwtQS12Last4S4WLkwugi63Lu4vJC9aL5Evxy/+MDUwbDCkMNsxEjFKMYIxujHyMioyYzKbMtQzDTNG -M38zuDPxNCs0ZTSeNNg1EzVNNYc1wjX9Njc2cjauNuk3JDdgN5w31zgUOFA4jDjIOQU5Qjl/Obw5+To2 -OnQ6sjrvOy07azuqO+g8JzxlPKQ84z0iPWE9oT3gPiA+YD6gPuA/IT9hP6I/4kAjQGRApkDnQSlBakGs -Qe5CMEJyQrVC90M6Q31DwEQDREdEikTORRJFVUWaRd5GIkZnRqtG8Ec1R3tHwEgFSEtIkUjXSR1JY0mp -SfBKN0p9SsRLDEtTS5pL4kwqTHJMuk0CTUpNk03cTiVObk63TwBPSU+TT91QJ1BxULtRBlFQUZtR5lIx -UnxSx1MTU19TqlP2VEJUj1TbVShVdVXCVg9WXFapVvdXRFeSV+BYL1h9WMtZGllpWbhaB1pWWqZa9VtF -W5Vb5Vw1XIZc1l0nXXhdyV4aXmxevV8PX2Ffs2AFYFdgqmD8YU9homH1YklinGLwY0Njl2PrZEBklGTp -ZT1lkmXnZj1mkmboZz1nk2fpaD9olmjsaUNpmmnxakhqn2r3a09rp2v/bFdsr20IbWBtuW4SbmtuxG8e -b3hv0XArcIZw4HE6cZVx8HJLcqZzAXNdc7h0FHRwdMx1KHWFdeF2Pnabdvh3VnezeBF4bnjMeSp5iXnn -ekZ6pXsEe2N7wnwhfIF84X1BfaF+AX5ifsJ/I3+Ef+WAR4CogQqBa4HNgjCCkoL0g1eDuoQdhICE44VH -hauGDoZyhteHO4efiASIaYjOiTOJmYn+imSKyoswi5aL/IxjjMqNMY2Yjf+OZo7OjzaPnpAGkG6Q1pE/ -kaiSEZJ6kuOTTZO2lCCUipT0lV+VyZY0lp+XCpd1l+CYTJi4mSSZkJn8mmia1ZtCm6+cHJyJnPedZJ3S -nkCerp8dn4uf+qBpoNihR6G2oiailqMGo3aj5qRWpMelOKWpphqmi6b9p26n4KhSqMSpN6mpqhyqj6sC -q3Wr6axcrNCtRK24ri2uoa8Wr4uwALB1sOqxYLHWskuywrM4s660JbSctRO1irYBtnm28Ldot+C4WbjR -uUq5wro7urW7LrunvCG8m70VvY++Cr6Evv+/er/1wHDA7MFnwePCX8Lbw1jD1MRRxM7FS8XIxkbGw8dB -x7/IPci8yTrJuco4yrfLNsu2zDXMtc01zbXONs62zzfPuNA50LrRPNG+0j/SwdNE08bUSdTL1U7V0dZV -1tjXXNfg2GTY6Nls2fHadtr724DcBdyK3RDdlt4c3qLfKd+v4DbgveFE4cziU+Lb42Pj6+Rz5PzlhOYN -5pbnH+ep6DLovOlG6dDqW+rl63Dr++yG7RHtnO4o7rTvQO/M8Fjw5fFy8f/yjPMZ86f0NPTC9VD13vZt -9vv3ivgZ+Kj5OPnH+lf65/t3/Af8mP0p/br+S/7c/23//9IrLC0uWiRjbGFzc25hbWVYJGNsYXNzZXNf -EBBOU0JpdG1hcEltYWdlUmVwoy0vMFpOU0ltYWdlUmVwWE5TT2JqZWN00issMjNXTlNBcnJheaIyMNIr -LDU2Xk5TTXV0YWJsZUFycmF5ozUyMNM4OQ86OzxXTlNXaGl0ZVxOU0NvbG9yU3BhY2VEMCAwABADgAzS -Kyw+P1dOU0NvbG9yoj4w0issQUJXTlNJbWFnZaJBMAAIABEAGgAkACkAMgA3AEkATABRAFMAYgBoAHUA -fACLAJIAnwCmAK4AsACyALQAuQC7AL0AxADJANQA1gDYANoA3wDiAOQA5gDoAO8BBgEiASQBJhOWE5sT -phOvE8ITxhPRE9oT3xPnE+oT7xP+FAIUCRQRFB4UIxQlFCcULBQ0FDcUPBREAAAAAAAAAgEAAAAAAAAA -QwAAAAAAAAAAAAAAAAAAFEc +S2V5ZWRBcmNoaXZlctEICVRyb290gAGuCwwbHCEaJissMzY5P0JVJG51bGzXDQ4PEBESExQVFhcYGRpW +TlNTaXplViRjbGFzc1xOU0ltYWdlRmxhZ3NWTlNSZXBzW05TVGludENvbG9yV05TQ29sb3JeTlNSZXNp +emluZ01vZGWAAoANEiDDAACAA4AAgAsQAFZ7MSwgMX3SHQ4eIFpOUy5vYmplY3RzoR+ABIAK0h0OIiWi +IySABYAGgAnTDicoKSoaXxAUTlNUSUZGUmVwcmVzZW50YXRpb25fEBlOU0ludGVybmFsTGF5b3V0RGly +ZWN0aW9ugAiAB08REmxNTQAqAAAACgAAABABAAADAAAAAQABAAABAQADAAAAAQABAAABAgADAAAAAgAI +AAgBAwADAAAAAQABAAABBgADAAAAAQABAAABCgADAAAAAQABAAABEQAEAAAAAQAAAAgBEgADAAAAAQAB +AAABFQADAAAAAQACAAABFgADAAAAAQABAAABFwAEAAAAAQAAAAIBHAADAAAAAQABAAABKAADAAAAAQAC +AAABUgADAAAAAQABAAABUwADAAAAAgABAAGHcwAHAAARnAAAANAAAAAAAAARnGFwcGwCAAAAbW50ckdS +QVlYWVogB9wACAAXAA8ALgAPYWNzcEFQUEwAAAAAbm9uZQAAAAAAAAAAAAAAAAAAAAAAAPbWAAEAAAAA +0y1hcHBsAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFZGVzYwAA +AMAAAAB5ZHNjbQAAATwAAAgaY3BydAAACVgAAAAjd3RwdAAACXwAAAAUa1RSQwAACZAAAAgMZGVzYwAA +AAAAAAAfR2VuZXJpYyBHcmF5IEdhbW1hIDIuMiBQcm9maWxlAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAG1s +dWMAAAAAAAAAHwAAAAxza1NLAAAALgAAAYRkYURLAAAAOgAAAbJjYUVTAAAAOAAAAex2aVZOAAAAQAAA +AiRwdEJSAAAASgAAAmR1a1VBAAAALAAAAq5mckZVAAAAPgAAAtpodUhVAAAANAAAAxh6aFRXAAAAGgAA +A0xrb0tSAAAAIgAAA2ZuYk5PAAAAOgAAA4hjc0NaAAAAKAAAA8JoZUlMAAAAJAAAA+pyb1JPAAAAKgAA +BA5kZURFAAAATgAABDhpdElUAAAATgAABIZzdlNFAAAAOAAABNR6aENOAAAAGgAABQxqYUpQAAAAJgAA +BSZlbEdSAAAAKgAABUxwdFBPAAAAUgAABXZubE5MAAAAQAAABchlc0VTAAAATAAABgh0aFRIAAAAMgAA +BlR0clRSAAAAJAAABoZmaUZJAAAARgAABqpockhSAAAAPgAABvBwbFBMAAAASgAABy5hckVHAAAALAAA +B3hydVJVAAAAOgAAB6RlblVTAAAAPAAAB94AVgFhAGUAbwBiAGUAYwBuAOEAIABzAGkAdgDhACAAZwBh +AG0AYQAgADIALAAyAEcAZQBuAGUAcgBpAHMAawAgAGcAcgDlACAAMgAsADIAIABnAGEAbQBtAGEALQBw +AHIAbwBmAGkAbABHAGEAbQBtAGEAIABkAGUAIABnAHIAaQBzAG8AcwAgAGcAZQBuAOgAcgBpAGMAYQAg +ADIALgAyAEMepQB1ACAAaADsAG4AaAAgAE0A4AB1ACAAeADhAG0AIABDAGgAdQBuAGcAIABHAGEAbQBt +AGEAIAAyAC4AMgBQAGUAcgBmAGkAbAAgAEcAZQBuAOkAcgBpAGMAbwAgAGQAYQAgAEcAYQBtAGEAIABk +AGUAIABDAGkAbgB6AGEAcwAgADIALAAyBBcEMAQzBDAEOwRMBD0EMAAgAEcAcgBhAHkALQQzBDAEPAQw +ACAAMgAuADIAUAByAG8AZgBpAGwAIABnAOkAbgDpAHIAaQBxAHUAZQAgAGcAcgBpAHMAIABnAGEAbQBt +AGEAIAAyACwAMgDBAGwAdABhAGwA4QBuAG8AcwAgAHMAegD8AHIAawBlACAAZwBhAG0AbQBhACAAMgAu +ADKQGnUocHCWjlFJXqYAMgAuADKCcl9pY8+P8Md8vBgAINaMwMkAIKwQucgAIAAyAC4AMgAg1QS4XNMM +x3wARwBlAG4AZQByAGkAcwBrACAAZwByAOUAIABnAGEAbQBtAGEAIAAyACwAMgAtAHAAcgBvAGYAaQBs +AE8AYgBlAGMAbgDhACABYQBlAGQA4QAgAGcAYQBtAGEAIAAyAC4AMgXSBdAF3gXUACAF0AXkBdUF6AAg +BdsF3AXcBdkAIAAyAC4AMgBHAGEAbQBhACAAZwByAGkAIABnAGUAbgBlAHIAaQBjAQMAIAAyACwAMgBB +AGwAbABnAGUAbQBlAGkAbgBlAHMAIABHAHIAYQB1AHMAdAB1AGYAZQBuAC0AUAByAG8AZgBpAGwAIABH +AGEAbQBtAGEAIAAyACwAMgBQAHIAbwBmAGkAbABvACAAZwByAGkAZwBpAG8AIABnAGUAbgBlAHIAaQBj +AG8AIABkAGUAbABsAGEAIABnAGEAbQBtAGEAIAAyACwAMgBHAGUAbgBlAHIAaQBzAGsAIABnAHIA5QAg +ADIALAAyACAAZwBhAG0AbQBhAHAAcgBvAGYAaQBsZm6QGnBwXqZ8+2VwADIALgAyY8+P8GWHTvZOAIIs +MLAw7DCkMKww8zDeACAAMgAuADIAIDDXMO0w1TChMKQw6wOTA7UDvQO5A7oDzAAgA5MDugPBA7kAIAOT +A6wDvAO8A7EAIAAyAC4AMgBQAGUAcgBmAGkAbAAgAGcAZQBuAOkAcgBpAGMAbwAgAGQAZQAgAGMAaQBu +AHoAZQBuAHQAbwBzACAAZABhACAARwBhAG0AbQBhACAAMgAsADIAQQBsAGcAZQBtAGUAZQBuACAAZwBy +AGkAagBzACAAZwBhAG0AbQBhACAAMgAsADIALQBwAHIAbwBmAGkAZQBsAFAAZQByAGYAaQBsACAAZwBl +AG4A6QByAGkAYwBvACAAZABlACAAZwBhAG0AbQBhACAAZABlACAAZwByAGkAcwBlAHMAIAAyACwAMg4j +DjEOBw4qDjUOQQ4BDiEOIQ4yDkAOAQ4jDiIOTA4XDjEOSA4nDkQOGwAgADIALgAyAEcAZQBuAGUAbAAg +AEcAcgBpACAARwBhAG0AYQAgADIALAAyAFkAbABlAGkAbgBlAG4AIABoAGEAcgBtAGEAYQBuACAAZwBh +AG0AbQBhACAAMgAsADIAIAAtAHAAcgBvAGYAaQBpAGwAaQBHAGUAbgBlAHIAaQENAGsAaQAgAEcAcgBh +AHkAIABHAGEAbQBtAGEAIAAyAC4AMgAgAHAAcgBvAGYAaQBsAFUAbgBpAHcAZQByAHMAYQBsAG4AeQAg +AHAAcgBvAGYAaQBsACAAcwB6AGEAcgBvAVsAYwBpACAAZwBhAG0AbQBhACAAMgAsADIGOgYnBkUGJwAg +ADIALgAyACAGRAZIBkYAIAYxBkUGJwYvBkoAIAY5BicGRQQeBDEESQQwBE8AIARBBDUEQAQwBE8AIAQz +BDAEPAQ8BDAAIAAyACwAMgAtBD8EQAQ+BEQEOAQ7BEwARwBlAG4AZQByAGkAYwAgAEcAcgBhAHkAIABH +AGEAbQBtAGEAIAAyAC4AMgAgAFAAcgBvAGYAaQBsAGUAAHRleHQAAAAAQ29weXJpZ2h0IEFwcGxlIElu +Yy4sIDIwMTIAAFhZWiAAAAAAAADzUQABAAAAARbMY3VydgAAAAAAAAQAAAAABQAKAA8AFAAZAB4AIwAo +AC0AMgA3ADsAQABFAEoATwBUAFkAXgBjAGgAbQByAHcAfACBAIYAiwCQAJUAmgCfAKQAqQCuALIAtwC8 +AMEAxgDLANAA1QDbAOAA5QDrAPAA9gD7AQEBBwENARMBGQEfASUBKwEyATgBPgFFAUwBUgFZAWABZwFu +AXUBfAGDAYsBkgGaAaEBqQGxAbkBwQHJAdEB2QHhAekB8gH6AgMCDAIUAh0CJgIvAjgCQQJLAlQCXQJn +AnECegKEAo4CmAKiAqwCtgLBAssC1QLgAusC9QMAAwsDFgMhAy0DOANDA08DWgNmA3IDfgOKA5YDogOu +A7oDxwPTA+AD7AP5BAYEEwQgBC0EOwRIBFUEYwRxBH4EjASaBKgEtgTEBNME4QTwBP4FDQUcBSsFOgVJ +BVgFZwV3BYYFlgWmBbUFxQXVBeUF9gYGBhYGJwY3BkgGWQZqBnsGjAadBq8GwAbRBuMG9QcHBxkHKwc9 +B08HYQd0B4YHmQesB78H0gflB/gICwgfCDIIRghaCG4IggiWCKoIvgjSCOcI+wkQCSUJOglPCWQJeQmP +CaQJugnPCeUJ+woRCicKPQpUCmoKgQqYCq4KxQrcCvMLCwsiCzkLUQtpC4ALmAuwC8gL4Qv5DBIMKgxD +DFwMdQyODKcMwAzZDPMNDQ0mDUANWg10DY4NqQ3DDd4N+A4TDi4OSQ5kDn8Omw62DtIO7g8JDyUPQQ9e +D3oPlg+zD88P7BAJECYQQxBhEH4QmxC5ENcQ9RETETERTxFtEYwRqhHJEegSBxImEkUSZBKEEqMSwxLj +EwMTIxNDE2MTgxOkE8UT5RQGFCcUSRRqFIsUrRTOFPAVEhU0FVYVeBWbFb0V4BYDFiYWSRZsFo8WshbW +FvoXHRdBF2UXiReuF9IX9xgbGEAYZRiKGK8Y1Rj6GSAZRRlrGZEZtxndGgQaKhpRGncanhrFGuwbFBs7 +G2MbihuyG9ocAhwqHFIcexyjHMwc9R0eHUcdcB2ZHcMd7B4WHkAeah6UHr4e6R8THz4faR+UH78f6iAV +IEEgbCCYIMQg8CEcIUghdSGhIc4h+yInIlUigiKvIt0jCiM4I2YjlCPCI/AkHyRNJHwkqyTaJQklOCVo +JZclxyX3JicmVyaHJrcm6CcYJ0kneierJ9woDSg/KHEooijUKQYpOClrKZ0p0CoCKjUqaCqbKs8rAis2 +K2krnSvRLAUsOSxuLKIs1y0MLUEtdi2rLeEuFi5MLoIuty7uLyQvWi+RL8cv/jA1MGwwpDDbMRIxSjGC +Mbox8jIqMmMymzLUMw0zRjN/M7gz8TQrNGU0njTYNRM1TTWHNcI1/TY3NnI2rjbpNyQ3YDecN9c4FDhQ +OIw4yDkFOUI5fzm8Ofk6Njp0OrI67zstO2s7qjvoPCc8ZTykPOM9Ij1hPaE94D4gPmA+oD7gPyE/YT+i +P+JAI0BkQKZA50EpQWpBrEHuQjBCckK1QvdDOkN9Q8BEA0RHRIpEzkUSRVVFmkXeRiJGZ0arRvBHNUd7 +R8BIBUhLSJFI10kdSWNJqUnwSjdKfUrESwxLU0uaS+JMKkxyTLpNAk1KTZNN3E4lTm5Ot08AT0lPk0/d +UCdQcVC7UQZRUFGbUeZSMVJ8UsdTE1NfU6pT9lRCVI9U21UoVXVVwlYPVlxWqVb3V0RXklfgWC9YfVjL +WRpZaVm4WgdaVlqmWvVbRVuVW+VcNVyGXNZdJ114XcleGl5sXr1fD19hX7NgBWBXYKpg/GFPYaJh9WJJ +Ypxi8GNDY5dj62RAZJRk6WU9ZZJl52Y9ZpJm6Gc9Z5Nn6Wg/aJZo7GlDaZpp8WpIap9q92tPa6dr/2xX +bK9tCG1gbbluEm5rbsRvHm94b9FwK3CGcOBxOnGVcfByS3KmcwFzXXO4dBR0cHTMdSh1hXXhdj52m3b4 +d1Z3s3gReG54zHkqeYl553pGeqV7BHtje8J8IXyBfOF9QX2hfgF+Yn7CfyN/hH/lgEeAqIEKgWuBzYIw +gpKC9INXg7qEHYSAhOOFR4Wrhg6GcobXhzuHn4gEiGmIzokziZmJ/opkisqLMIuWi/yMY4zKjTGNmI3/ +jmaOzo82j56QBpBukNaRP5GokhGSepLjk02TtpQglIqU9JVflcmWNJaflwqXdZfgmEyYuJkkmZCZ/Jpo +mtWbQpuvnByciZz3nWSd0p5Anq6fHZ+Ln/qgaaDYoUehtqImopajBqN2o+akVqTHpTilqaYapoum/adu +p+CoUqjEqTepqaocqo+rAqt1q+msXKzQrUStuK4trqGvFq+LsACwdbDqsWCx1rJLssKzOLOutCW0nLUT +tYq2AbZ5tvC3aLfguFm40blKucK6O7q1uy67p7whvJu9Fb2Pvgq+hL7/v3q/9cBwwOzBZ8Hjwl/C28NY +w9TEUcTOxUvFyMZGxsPHQce/yD3IvMk6ybnKOMq3yzbLtsw1zLXNNc21zjbOts83z7jQOdC60TzRvtI/ +0sHTRNPG1EnUy9VO1dHWVdbY11zX4Nhk2OjZbNnx2nba+9uA3AXcit0Q3ZbeHN6i3ynfr+A24L3hROHM +4lPi2+Nj4+vkc+T85YTmDeaW5x/nqegy6LzpRunQ6lvq5etw6/vshu0R7ZzuKO6070DvzPBY8OXxcvH/ +8ozzGfOn9DT0wvVQ9d72bfb794r4Gfio+Tj5x/pX+uf7d/wH/Jj9Kf26/kv+3P9t///SLS4vMFokY2xh +c3NuYW1lWCRjbGFzc2VzXxAQTlNCaXRtYXBJbWFnZVJlcKMvMTJaTlNJbWFnZVJlcFhOU09iamVjdNIt +LjQ1V05TQXJyYXmiNDLSLS43OF5OU011dGFibGVBcnJheaM3NDLTOjsOPD0+V05TV2hpdGVcTlNDb2xv +clNwYWNlRDAgMAAQA4AM0i0uQEFXTlNDb2xvcqJAMtItLkNEV05TSW1hZ2WiQzIACAARABoAJAApADIA +NwBJAEwAUQBTAGIAaAB3AH4AhQCSAJkApQCtALwAvgDAAMUAxwDJAMsAzQDUANkA5ADmAOgA6gDvAPIA +9AD2APgA/wEWATIBNAE2E6YTqxO2E78T0hPWE+ET6hPvE/cT+hP/FA4UEhQZFCEULhQzFDUUNxQ8FEQU +RxRMFFQAAAAAAAACAQAAAAAAAABFAAAAAAAAAAAAAAAAAAAUVw diff --git a/i18n/src/main/resources/es.lproj/Browser.xib b/i18n/src/main/resources/es.lproj/Browser.xib index 7cdbce44c9..a26d64224c 100644 --- a/i18n/src/main/resources/es.lproj/Browser.xib +++ b/i18n/src/main/resources/es.lproj/Browser.xib @@ -1,7 +1,7 @@ - + - + @@ -38,7 +38,7 @@ - + @@ -62,27 +62,27 @@ @@ -256,7 +256,7 @@ - + @@ -553,95 +553,95 @@ DQ YnBsaXN0MDDUAQIDBAUGBwpYJHZlcnNpb25ZJGFyY2hpdmVyVCR0b3BYJG9iamVjdHMSAAGGoF8QD05T -S2V5ZWRBcmNoaXZlctEICVRyb290gAGuCwwZGh8UJCkqMTQ3PUBVJG51bGzWDQ4PEBESExQVFhcYVk5T -U2l6ZV5OU1Jlc2l6aW5nTW9kZVYkY2xhc3NcTlNJbWFnZUZsYWdzVk5TUmVwc1dOU0NvbG9ygAIQAIAN -EiDDAACAA4ALVnsxLCAxfdIbDxweWk5TLm9iamVjdHOhHYAEgArSGw8gI6IhIoAFgAaACdMPJSYnKBRf -EBROU1RJRkZSZXByZXNlbnRhdGlvbl8QGU5TSW50ZXJuYWxMYXlvdXREaXJlY3Rpb26ACIAHTxESbE1N -ACoAAAAKAAAAEAEAAAMAAAABAAEAAAEBAAMAAAABAAEAAAECAAMAAAACAAgACAEDAAMAAAABAAEAAAEG -AAMAAAABAAEAAAEKAAMAAAABAAEAAAERAAQAAAABAAAACAESAAMAAAABAAEAAAEVAAMAAAABAAIAAAEW -AAMAAAABAAEAAAEXAAQAAAABAAAAAgEcAAMAAAABAAEAAAEoAAMAAAABAAIAAAFSAAMAAAABAAEAAAFT -AAMAAAACAAEAAYdzAAcAABGcAAAA0AAAAAAAABGcYXBwbAIAAABtbnRyR1JBWVhZWiAH3AAIABcADwAu -AA9hY3NwQVBQTAAAAABub25lAAAAAAAAAAAAAAAAAAAAAAAA9tYAAQAAAADTLWFwcGwAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAVkZXNjAAAAwAAAAHlkc2NtAAABPAAA -CBpjcHJ0AAAJWAAAACN3dHB0AAAJfAAAABRrVFJDAAAJkAAACAxkZXNjAAAAAAAAAB9HZW5lcmljIEdy -YXkgR2FtbWEgMi4yIFByb2ZpbGUAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAbWx1YwAAAAAAAAAfAAAADHNr -U0sAAAAuAAABhGRhREsAAAA6AAABsmNhRVMAAAA4AAAB7HZpVk4AAABAAAACJHB0QlIAAABKAAACZHVr -VUEAAAAsAAACrmZyRlUAAAA+AAAC2mh1SFUAAAA0AAADGHpoVFcAAAAaAAADTGtvS1IAAAAiAAADZm5i -Tk8AAAA6AAADiGNzQ1oAAAAoAAADwmhlSUwAAAAkAAAD6nJvUk8AAAAqAAAEDmRlREUAAABOAAAEOGl0 -SVQAAABOAAAEhnN2U0UAAAA4AAAE1HpoQ04AAAAaAAAFDGphSlAAAAAmAAAFJmVsR1IAAAAqAAAFTHB0 -UE8AAABSAAAFdm5sTkwAAABAAAAFyGVzRVMAAABMAAAGCHRoVEgAAAAyAAAGVHRyVFIAAAAkAAAGhmZp -RkkAAABGAAAGqmhySFIAAAA+AAAG8HBsUEwAAABKAAAHLmFyRUcAAAAsAAAHeHJ1UlUAAAA6AAAHpGVu -VVMAAAA8AAAH3gBWAWEAZQBvAGIAZQBjAG4A4QAgAHMAaQB2AOEAIABnAGEAbQBhACAAMgAsADIARwBl -AG4AZQByAGkAcwBrACAAZwByAOUAIAAyACwAMgAgAGcAYQBtAG0AYQAtAHAAcgBvAGYAaQBsAEcAYQBt -AG0AYQAgAGQAZQAgAGcAcgBpAHMAbwBzACAAZwBlAG4A6AByAGkAYwBhACAAMgAuADIAQx6lAHUAIABo -AOwAbgBoACAATQDgAHUAIAB4AOEAbQAgAEMAaAB1AG4AZwAgAEcAYQBtAG0AYQAgADIALgAyAFAAZQBy -AGYAaQBsACAARwBlAG4A6QByAGkAYwBvACAAZABhACAARwBhAG0AYQAgAGQAZQAgAEMAaQBuAHoAYQBz -ACAAMgAsADIEFwQwBDMEMAQ7BEwEPQQwACAARwByAGEAeQAtBDMEMAQ8BDAAIAAyAC4AMgBQAHIAbwBm -AGkAbAAgAGcA6QBuAOkAcgBpAHEAdQBlACAAZwByAGkAcwAgAGcAYQBtAG0AYQAgADIALAAyAMEAbAB0 -AGEAbADhAG4AbwBzACAAcwB6APwAcgBrAGUAIABnAGEAbQBtAGEAIAAyAC4AMpAadShwcJaOUUlepgAy -AC4AMoJyX2ljz4/wx3y8GAAg1ozAyQAgrBC5yAAgADIALgAyACDVBLhc0wzHfABHAGUAbgBlAHIAaQBz -AGsAIABnAHIA5QAgAGcAYQBtAG0AYQAgADIALAAyAC0AcAByAG8AZgBpAGwATwBiAGUAYwBuAOEAIAFh -AGUAZADhACAAZwBhAG0AYQAgADIALgAyBdIF0AXeBdQAIAXQBeQF1QXoACAF2wXcBdwF2QAgADIALgAy -AEcAYQBtAGEAIABnAHIAaQAgAGcAZQBuAGUAcgBpAGMBAwAgADIALAAyAEEAbABsAGcAZQBtAGUAaQBu -AGUAcwAgAEcAcgBhAHUAcwB0AHUAZgBlAG4ALQBQAHIAbwBmAGkAbAAgAEcAYQBtAG0AYQAgADIALAAy -AFAAcgBvAGYAaQBsAG8AIABnAHIAaQBnAGkAbwAgAGcAZQBuAGUAcgBpAGMAbwAgAGQAZQBsAGwAYQAg -AGcAYQBtAG0AYQAgADIALAAyAEcAZQBuAGUAcgBpAHMAawAgAGcAcgDlACAAMgAsADIAIABnAGEAbQBt -AGEAcAByAG8AZgBpAGxmbpAacHBepnz7ZXAAMgAuADJjz4/wZYdO9k4AgiwwsDDsMKQwrDDzMN4AIAAy -AC4AMgAgMNcw7TDVMKEwpDDrA5MDtQO9A7kDugPMACADkwO6A8EDuQAgA5MDrAO8A7wDsQAgADIALgAy -AFAAZQByAGYAaQBsACAAZwBlAG4A6QByAGkAYwBvACAAZABlACAAYwBpAG4AegBlAG4AdABvAHMAIABk -AGEAIABHAGEAbQBtAGEAIAAyACwAMgBBAGwAZwBlAG0AZQBlAG4AIABnAHIAaQBqAHMAIABnAGEAbQBt -AGEAIAAyACwAMgAtAHAAcgBvAGYAaQBlAGwAUABlAHIAZgBpAGwAIABnAGUAbgDpAHIAaQBjAG8AIABk -AGUAIABnAGEAbQBtAGEAIABkAGUAIABnAHIAaQBzAGUAcwAgADIALAAyDiMOMQ4HDioONQ5BDgEOIQ4h -DjIOQA4BDiMOIg5MDhcOMQ5IDicORA4bACAAMgAuADIARwBlAG4AZQBsACAARwByAGkAIABHAGEAbQBh -ACAAMgAsADIAWQBsAGUAaQBuAGUAbgAgAGgAYQByAG0AYQBhAG4AIABnAGEAbQBtAGEAIAAyACwAMgAg -AC0AcAByAG8AZgBpAGkAbABpAEcAZQBuAGUAcgBpAQ0AawBpACAARwByAGEAeQAgAEcAYQBtAG0AYQAg -ADIALgAyACAAcAByAG8AZgBpAGwAVQBuAGkAdwBlAHIAcwBhAGwAbgB5ACAAcAByAG8AZgBpAGwAIABz -AHoAYQByAG8BWwBjAGkAIABnAGEAbQBtAGEAIAAyACwAMgY6BicGRQYnACAAMgAuADIAIAZEBkgGRgAg -BjEGRQYnBi8GSgAgBjkGJwZFBB4EMQRJBDAETwAgBEEENQRABDAETwAgBDMEMAQ8BDwEMAAgADIALAAy -AC0EPwRABD4ERAQ4BDsETABHAGUAbgBlAHIAaQBjACAARwByAGEAeQAgAEcAYQBtAG0AYQAgADIALgAy -ACAAUAByAG8AZgBpAGwAZQAAdGV4dAAAAABDb3B5cmlnaHQgQXBwbGUgSW5jLiwgMjAxMgAAWFlaIAAA -AAAAAPNRAAEAAAABFsxjdXJ2AAAAAAAABAAAAAAFAAoADwAUABkAHgAjACgALQAyADcAOwBAAEUASgBP -AFQAWQBeAGMAaABtAHIAdwB8AIEAhgCLAJAAlQCaAJ8ApACpAK4AsgC3ALwAwQDGAMsA0ADVANsA4ADl -AOsA8AD2APsBAQEHAQ0BEwEZAR8BJQErATIBOAE+AUUBTAFSAVkBYAFnAW4BdQF8AYMBiwGSAZoBoQGp -AbEBuQHBAckB0QHZAeEB6QHyAfoCAwIMAhQCHQImAi8COAJBAksCVAJdAmcCcQJ6AoQCjgKYAqICrAK2 -AsECywLVAuAC6wL1AwADCwMWAyEDLQM4A0MDTwNaA2YDcgN+A4oDlgOiA64DugPHA9MD4APsA/kEBgQT -BCAELQQ7BEgEVQRjBHEEfgSMBJoEqAS2BMQE0wThBPAE/gUNBRwFKwU6BUkFWAVnBXcFhgWWBaYFtQXF -BdUF5QX2BgYGFgYnBjcGSAZZBmoGewaMBp0GrwbABtEG4wb1BwcHGQcrBz0HTwdhB3QHhgeZB6wHvwfS -B+UH+AgLCB8IMghGCFoIbgiCCJYIqgi+CNII5wj7CRAJJQk6CU8JZAl5CY8JpAm6Cc8J5Qn7ChEKJwo9 -ClQKagqBCpgKrgrFCtwK8wsLCyILOQtRC2kLgAuYC7ALyAvhC/kMEgwqDEMMXAx1DI4MpwzADNkM8w0N -DSYNQA1aDXQNjg2pDcMN3g34DhMOLg5JDmQOfw6bDrYO0g7uDwkPJQ9BD14Peg+WD7MPzw/sEAkQJhBD -EGEQfhCbELkQ1xD1ERMRMRFPEW0RjBGqEckR6BIHEiYSRRJkEoQSoxLDEuMTAxMjE0MTYxODE6QTxRPl -FAYUJxRJFGoUixStFM4U8BUSFTQVVhV4FZsVvRXgFgMWJhZJFmwWjxayFtYW+hcdF0EXZReJF64X0hf3 -GBsYQBhlGIoYrxjVGPoZIBlFGWsZkRm3Gd0aBBoqGlEadxqeGsUa7BsUGzsbYxuKG7Ib2hwCHCocUhx7 -HKMczBz1HR4dRx1wHZkdwx3sHhYeQB5qHpQevh7pHxMfPh9pH5Qfvx/qIBUgQSBsIJggxCDwIRwhSCF1 -IaEhziH7IiciVSKCIq8i3SMKIzgjZiOUI8Ij8CQfJE0kfCSrJNolCSU4JWgllyXHJfcmJyZXJocmtybo -JxgnSSd6J6sn3CgNKD8ocSiiKNQpBik4KWspnSnQKgIqNSpoKpsqzysCKzYraSudK9EsBSw5LG4soizX -LQwtQS12Last4S4WLkwugi63Lu4vJC9aL5Evxy/+MDUwbDCkMNsxEjFKMYIxujHyMioyYzKbMtQzDTNG -M38zuDPxNCs0ZTSeNNg1EzVNNYc1wjX9Njc2cjauNuk3JDdgN5w31zgUOFA4jDjIOQU5Qjl/Obw5+To2 -OnQ6sjrvOy07azuqO+g8JzxlPKQ84z0iPWE9oT3gPiA+YD6gPuA/IT9hP6I/4kAjQGRApkDnQSlBakGs -Qe5CMEJyQrVC90M6Q31DwEQDREdEikTORRJFVUWaRd5GIkZnRqtG8Ec1R3tHwEgFSEtIkUjXSR1JY0mp -SfBKN0p9SsRLDEtTS5pL4kwqTHJMuk0CTUpNk03cTiVObk63TwBPSU+TT91QJ1BxULtRBlFQUZtR5lIx -UnxSx1MTU19TqlP2VEJUj1TbVShVdVXCVg9WXFapVvdXRFeSV+BYL1h9WMtZGllpWbhaB1pWWqZa9VtF -W5Vb5Vw1XIZc1l0nXXhdyV4aXmxevV8PX2Ffs2AFYFdgqmD8YU9homH1YklinGLwY0Njl2PrZEBklGTp -ZT1lkmXnZj1mkmboZz1nk2fpaD9olmjsaUNpmmnxakhqn2r3a09rp2v/bFdsr20IbWBtuW4SbmtuxG8e -b3hv0XArcIZw4HE6cZVx8HJLcqZzAXNdc7h0FHRwdMx1KHWFdeF2Pnabdvh3VnezeBF4bnjMeSp5iXnn -ekZ6pXsEe2N7wnwhfIF84X1BfaF+AX5ifsJ/I3+Ef+WAR4CogQqBa4HNgjCCkoL0g1eDuoQdhICE44VH -hauGDoZyhteHO4efiASIaYjOiTOJmYn+imSKyoswi5aL/IxjjMqNMY2Yjf+OZo7OjzaPnpAGkG6Q1pE/ -kaiSEZJ6kuOTTZO2lCCUipT0lV+VyZY0lp+XCpd1l+CYTJi4mSSZkJn8mmia1ZtCm6+cHJyJnPedZJ3S -nkCerp8dn4uf+qBpoNihR6G2oiailqMGo3aj5qRWpMelOKWpphqmi6b9p26n4KhSqMSpN6mpqhyqj6sC -q3Wr6axcrNCtRK24ri2uoa8Wr4uwALB1sOqxYLHWskuywrM4s660JbSctRO1irYBtnm28Ldot+C4WbjR -uUq5wro7urW7LrunvCG8m70VvY++Cr6Evv+/er/1wHDA7MFnwePCX8Lbw1jD1MRRxM7FS8XIxkbGw8dB -x7/IPci8yTrJuco4yrfLNsu2zDXMtc01zbXONs62zzfPuNA50LrRPNG+0j/SwdNE08bUSdTL1U7V0dZV -1tjXXNfg2GTY6Nls2fHadtr724DcBdyK3RDdlt4c3qLfKd+v4DbgveFE4cziU+Lb42Pj6+Rz5PzlhOYN -5pbnH+ep6DLovOlG6dDqW+rl63Dr++yG7RHtnO4o7rTvQO/M8Fjw5fFy8f/yjPMZ86f0NPTC9VD13vZt -9vv3ivgZ+Kj5OPnH+lf65/t3/Af8mP0p/br+S/7c/23//9IrLC0uWiRjbGFzc25hbWVYJGNsYXNzZXNf -EBBOU0JpdG1hcEltYWdlUmVwoy0vMFpOU0ltYWdlUmVwWE5TT2JqZWN00issMjNXTlNBcnJheaIyMNIr -LDU2Xk5TTXV0YWJsZUFycmF5ozUyMNM4OQ86OzxXTlNXaGl0ZVxOU0NvbG9yU3BhY2VEMCAwABADgAzS -Kyw+P1dOU0NvbG9yoj4w0issQUJXTlNJbWFnZaJBMAAIABEAGgAkACkAMgA3AEkATABRAFMAYgBoAHUA -fACLAJIAnwCmAK4AsACyALQAuQC7AL0AxADJANQA1gDYANoA3wDiAOQA5gDoAO8BBgEiASQBJhOWE5sT -phOvE8ITxhPRE9oT3xPnE+oT7xP+FAIUCRQRFB4UIxQlFCcULBQ0FDcUPBREAAAAAAAAAgEAAAAAAAAA -QwAAAAAAAAAAAAAAAAAAFEc +S2V5ZWRBcmNoaXZlctEICVRyb290gAGuCwwbHCEaJissMzY5P0JVJG51bGzXDQ4PEBESExQVFhcYGRpW +TlNTaXplViRjbGFzc1xOU0ltYWdlRmxhZ3NWTlNSZXBzW05TVGludENvbG9yV05TQ29sb3JeTlNSZXNp +emluZ01vZGWAAoANEiDDAACAA4AAgAsQAFZ7MSwgMX3SHQ4eIFpOUy5vYmplY3RzoR+ABIAK0h0OIiWi +IySABYAGgAnTDicoKSoaXxAUTlNUSUZGUmVwcmVzZW50YXRpb25fEBlOU0ludGVybmFsTGF5b3V0RGly +ZWN0aW9ugAiAB08REmxNTQAqAAAACgAAABABAAADAAAAAQABAAABAQADAAAAAQABAAABAgADAAAAAgAI +AAgBAwADAAAAAQABAAABBgADAAAAAQABAAABCgADAAAAAQABAAABEQAEAAAAAQAAAAgBEgADAAAAAQAB +AAABFQADAAAAAQACAAABFgADAAAAAQABAAABFwAEAAAAAQAAAAIBHAADAAAAAQABAAABKAADAAAAAQAC +AAABUgADAAAAAQABAAABUwADAAAAAgABAAGHcwAHAAARnAAAANAAAAAAAAARnGFwcGwCAAAAbW50ckdS +QVlYWVogB9wACAAXAA8ALgAPYWNzcEFQUEwAAAAAbm9uZQAAAAAAAAAAAAAAAAAAAAAAAPbWAAEAAAAA +0y1hcHBsAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFZGVzYwAA +AMAAAAB5ZHNjbQAAATwAAAgaY3BydAAACVgAAAAjd3RwdAAACXwAAAAUa1RSQwAACZAAAAgMZGVzYwAA +AAAAAAAfR2VuZXJpYyBHcmF5IEdhbW1hIDIuMiBQcm9maWxlAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAG1s +dWMAAAAAAAAAHwAAAAxza1NLAAAALgAAAYRkYURLAAAAOgAAAbJjYUVTAAAAOAAAAex2aVZOAAAAQAAA +AiRwdEJSAAAASgAAAmR1a1VBAAAALAAAAq5mckZVAAAAPgAAAtpodUhVAAAANAAAAxh6aFRXAAAAGgAA +A0xrb0tSAAAAIgAAA2ZuYk5PAAAAOgAAA4hjc0NaAAAAKAAAA8JoZUlMAAAAJAAAA+pyb1JPAAAAKgAA +BA5kZURFAAAATgAABDhpdElUAAAATgAABIZzdlNFAAAAOAAABNR6aENOAAAAGgAABQxqYUpQAAAAJgAA +BSZlbEdSAAAAKgAABUxwdFBPAAAAUgAABXZubE5MAAAAQAAABchlc0VTAAAATAAABgh0aFRIAAAAMgAA +BlR0clRSAAAAJAAABoZmaUZJAAAARgAABqpockhSAAAAPgAABvBwbFBMAAAASgAABy5hckVHAAAALAAA +B3hydVJVAAAAOgAAB6RlblVTAAAAPAAAB94AVgFhAGUAbwBiAGUAYwBuAOEAIABzAGkAdgDhACAAZwBh +AG0AYQAgADIALAAyAEcAZQBuAGUAcgBpAHMAawAgAGcAcgDlACAAMgAsADIAIABnAGEAbQBtAGEALQBw +AHIAbwBmAGkAbABHAGEAbQBtAGEAIABkAGUAIABnAHIAaQBzAG8AcwAgAGcAZQBuAOgAcgBpAGMAYQAg +ADIALgAyAEMepQB1ACAAaADsAG4AaAAgAE0A4AB1ACAAeADhAG0AIABDAGgAdQBuAGcAIABHAGEAbQBt +AGEAIAAyAC4AMgBQAGUAcgBmAGkAbAAgAEcAZQBuAOkAcgBpAGMAbwAgAGQAYQAgAEcAYQBtAGEAIABk +AGUAIABDAGkAbgB6AGEAcwAgADIALAAyBBcEMAQzBDAEOwRMBD0EMAAgAEcAcgBhAHkALQQzBDAEPAQw +ACAAMgAuADIAUAByAG8AZgBpAGwAIABnAOkAbgDpAHIAaQBxAHUAZQAgAGcAcgBpAHMAIABnAGEAbQBt +AGEAIAAyACwAMgDBAGwAdABhAGwA4QBuAG8AcwAgAHMAegD8AHIAawBlACAAZwBhAG0AbQBhACAAMgAu +ADKQGnUocHCWjlFJXqYAMgAuADKCcl9pY8+P8Md8vBgAINaMwMkAIKwQucgAIAAyAC4AMgAg1QS4XNMM +x3wARwBlAG4AZQByAGkAcwBrACAAZwByAOUAIABnAGEAbQBtAGEAIAAyACwAMgAtAHAAcgBvAGYAaQBs +AE8AYgBlAGMAbgDhACABYQBlAGQA4QAgAGcAYQBtAGEAIAAyAC4AMgXSBdAF3gXUACAF0AXkBdUF6AAg +BdsF3AXcBdkAIAAyAC4AMgBHAGEAbQBhACAAZwByAGkAIABnAGUAbgBlAHIAaQBjAQMAIAAyACwAMgBB +AGwAbABnAGUAbQBlAGkAbgBlAHMAIABHAHIAYQB1AHMAdAB1AGYAZQBuAC0AUAByAG8AZgBpAGwAIABH +AGEAbQBtAGEAIAAyACwAMgBQAHIAbwBmAGkAbABvACAAZwByAGkAZwBpAG8AIABnAGUAbgBlAHIAaQBj +AG8AIABkAGUAbABsAGEAIABnAGEAbQBtAGEAIAAyACwAMgBHAGUAbgBlAHIAaQBzAGsAIABnAHIA5QAg +ADIALAAyACAAZwBhAG0AbQBhAHAAcgBvAGYAaQBsZm6QGnBwXqZ8+2VwADIALgAyY8+P8GWHTvZOAIIs +MLAw7DCkMKww8zDeACAAMgAuADIAIDDXMO0w1TChMKQw6wOTA7UDvQO5A7oDzAAgA5MDugPBA7kAIAOT +A6wDvAO8A7EAIAAyAC4AMgBQAGUAcgBmAGkAbAAgAGcAZQBuAOkAcgBpAGMAbwAgAGQAZQAgAGMAaQBu +AHoAZQBuAHQAbwBzACAAZABhACAARwBhAG0AbQBhACAAMgAsADIAQQBsAGcAZQBtAGUAZQBuACAAZwBy +AGkAagBzACAAZwBhAG0AbQBhACAAMgAsADIALQBwAHIAbwBmAGkAZQBsAFAAZQByAGYAaQBsACAAZwBl +AG4A6QByAGkAYwBvACAAZABlACAAZwBhAG0AbQBhACAAZABlACAAZwByAGkAcwBlAHMAIAAyACwAMg4j +DjEOBw4qDjUOQQ4BDiEOIQ4yDkAOAQ4jDiIOTA4XDjEOSA4nDkQOGwAgADIALgAyAEcAZQBuAGUAbAAg +AEcAcgBpACAARwBhAG0AYQAgADIALAAyAFkAbABlAGkAbgBlAG4AIABoAGEAcgBtAGEAYQBuACAAZwBh +AG0AbQBhACAAMgAsADIAIAAtAHAAcgBvAGYAaQBpAGwAaQBHAGUAbgBlAHIAaQENAGsAaQAgAEcAcgBh +AHkAIABHAGEAbQBtAGEAIAAyAC4AMgAgAHAAcgBvAGYAaQBsAFUAbgBpAHcAZQByAHMAYQBsAG4AeQAg +AHAAcgBvAGYAaQBsACAAcwB6AGEAcgBvAVsAYwBpACAAZwBhAG0AbQBhACAAMgAsADIGOgYnBkUGJwAg +ADIALgAyACAGRAZIBkYAIAYxBkUGJwYvBkoAIAY5BicGRQQeBDEESQQwBE8AIARBBDUEQAQwBE8AIAQz +BDAEPAQ8BDAAIAAyACwAMgAtBD8EQAQ+BEQEOAQ7BEwARwBlAG4AZQByAGkAYwAgAEcAcgBhAHkAIABH +AGEAbQBtAGEAIAAyAC4AMgAgAFAAcgBvAGYAaQBsAGUAAHRleHQAAAAAQ29weXJpZ2h0IEFwcGxlIElu +Yy4sIDIwMTIAAFhZWiAAAAAAAADzUQABAAAAARbMY3VydgAAAAAAAAQAAAAABQAKAA8AFAAZAB4AIwAo +AC0AMgA3ADsAQABFAEoATwBUAFkAXgBjAGgAbQByAHcAfACBAIYAiwCQAJUAmgCfAKQAqQCuALIAtwC8 +AMEAxgDLANAA1QDbAOAA5QDrAPAA9gD7AQEBBwENARMBGQEfASUBKwEyATgBPgFFAUwBUgFZAWABZwFu +AXUBfAGDAYsBkgGaAaEBqQGxAbkBwQHJAdEB2QHhAekB8gH6AgMCDAIUAh0CJgIvAjgCQQJLAlQCXQJn +AnECegKEAo4CmAKiAqwCtgLBAssC1QLgAusC9QMAAwsDFgMhAy0DOANDA08DWgNmA3IDfgOKA5YDogOu +A7oDxwPTA+AD7AP5BAYEEwQgBC0EOwRIBFUEYwRxBH4EjASaBKgEtgTEBNME4QTwBP4FDQUcBSsFOgVJ +BVgFZwV3BYYFlgWmBbUFxQXVBeUF9gYGBhYGJwY3BkgGWQZqBnsGjAadBq8GwAbRBuMG9QcHBxkHKwc9 +B08HYQd0B4YHmQesB78H0gflB/gICwgfCDIIRghaCG4IggiWCKoIvgjSCOcI+wkQCSUJOglPCWQJeQmP +CaQJugnPCeUJ+woRCicKPQpUCmoKgQqYCq4KxQrcCvMLCwsiCzkLUQtpC4ALmAuwC8gL4Qv5DBIMKgxD +DFwMdQyODKcMwAzZDPMNDQ0mDUANWg10DY4NqQ3DDd4N+A4TDi4OSQ5kDn8Omw62DtIO7g8JDyUPQQ9e +D3oPlg+zD88P7BAJECYQQxBhEH4QmxC5ENcQ9RETETERTxFtEYwRqhHJEegSBxImEkUSZBKEEqMSwxLj +EwMTIxNDE2MTgxOkE8UT5RQGFCcUSRRqFIsUrRTOFPAVEhU0FVYVeBWbFb0V4BYDFiYWSRZsFo8WshbW +FvoXHRdBF2UXiReuF9IX9xgbGEAYZRiKGK8Y1Rj6GSAZRRlrGZEZtxndGgQaKhpRGncanhrFGuwbFBs7 +G2MbihuyG9ocAhwqHFIcexyjHMwc9R0eHUcdcB2ZHcMd7B4WHkAeah6UHr4e6R8THz4faR+UH78f6iAV +IEEgbCCYIMQg8CEcIUghdSGhIc4h+yInIlUigiKvIt0jCiM4I2YjlCPCI/AkHyRNJHwkqyTaJQklOCVo +JZclxyX3JicmVyaHJrcm6CcYJ0kneierJ9woDSg/KHEooijUKQYpOClrKZ0p0CoCKjUqaCqbKs8rAis2 +K2krnSvRLAUsOSxuLKIs1y0MLUEtdi2rLeEuFi5MLoIuty7uLyQvWi+RL8cv/jA1MGwwpDDbMRIxSjGC +Mbox8jIqMmMymzLUMw0zRjN/M7gz8TQrNGU0njTYNRM1TTWHNcI1/TY3NnI2rjbpNyQ3YDecN9c4FDhQ +OIw4yDkFOUI5fzm8Ofk6Njp0OrI67zstO2s7qjvoPCc8ZTykPOM9Ij1hPaE94D4gPmA+oD7gPyE/YT+i +P+JAI0BkQKZA50EpQWpBrEHuQjBCckK1QvdDOkN9Q8BEA0RHRIpEzkUSRVVFmkXeRiJGZ0arRvBHNUd7 +R8BIBUhLSJFI10kdSWNJqUnwSjdKfUrESwxLU0uaS+JMKkxyTLpNAk1KTZNN3E4lTm5Ot08AT0lPk0/d +UCdQcVC7UQZRUFGbUeZSMVJ8UsdTE1NfU6pT9lRCVI9U21UoVXVVwlYPVlxWqVb3V0RXklfgWC9YfVjL +WRpZaVm4WgdaVlqmWvVbRVuVW+VcNVyGXNZdJ114XcleGl5sXr1fD19hX7NgBWBXYKpg/GFPYaJh9WJJ +Ypxi8GNDY5dj62RAZJRk6WU9ZZJl52Y9ZpJm6Gc9Z5Nn6Wg/aJZo7GlDaZpp8WpIap9q92tPa6dr/2xX +bK9tCG1gbbluEm5rbsRvHm94b9FwK3CGcOBxOnGVcfByS3KmcwFzXXO4dBR0cHTMdSh1hXXhdj52m3b4 +d1Z3s3gReG54zHkqeYl553pGeqV7BHtje8J8IXyBfOF9QX2hfgF+Yn7CfyN/hH/lgEeAqIEKgWuBzYIw +gpKC9INXg7qEHYSAhOOFR4Wrhg6GcobXhzuHn4gEiGmIzokziZmJ/opkisqLMIuWi/yMY4zKjTGNmI3/ +jmaOzo82j56QBpBukNaRP5GokhGSepLjk02TtpQglIqU9JVflcmWNJaflwqXdZfgmEyYuJkkmZCZ/Jpo +mtWbQpuvnByciZz3nWSd0p5Anq6fHZ+Ln/qgaaDYoUehtqImopajBqN2o+akVqTHpTilqaYapoum/adu +p+CoUqjEqTepqaocqo+rAqt1q+msXKzQrUStuK4trqGvFq+LsACwdbDqsWCx1rJLssKzOLOutCW0nLUT +tYq2AbZ5tvC3aLfguFm40blKucK6O7q1uy67p7whvJu9Fb2Pvgq+hL7/v3q/9cBwwOzBZ8Hjwl/C28NY +w9TEUcTOxUvFyMZGxsPHQce/yD3IvMk6ybnKOMq3yzbLtsw1zLXNNc21zjbOts83z7jQOdC60TzRvtI/ +0sHTRNPG1EnUy9VO1dHWVdbY11zX4Nhk2OjZbNnx2nba+9uA3AXcit0Q3ZbeHN6i3ynfr+A24L3hROHM +4lPi2+Nj4+vkc+T85YTmDeaW5x/nqegy6LzpRunQ6lvq5etw6/vshu0R7ZzuKO6070DvzPBY8OXxcvH/ +8ozzGfOn9DT0wvVQ9d72bfb794r4Gfio+Tj5x/pX+uf7d/wH/Jj9Kf26/kv+3P9t///SLS4vMFokY2xh +c3NuYW1lWCRjbGFzc2VzXxAQTlNCaXRtYXBJbWFnZVJlcKMvMTJaTlNJbWFnZVJlcFhOU09iamVjdNIt +LjQ1V05TQXJyYXmiNDLSLS43OF5OU011dGFibGVBcnJheaM3NDLTOjsOPD0+V05TV2hpdGVcTlNDb2xv +clNwYWNlRDAgMAAQA4AM0i0uQEFXTlNDb2xvcqJAMtItLkNEV05TSW1hZ2WiQzIACAARABoAJAApADIA +NwBJAEwAUQBTAGIAaAB3AH4AhQCSAJkApQCtALwAvgDAAMUAxwDJAMsAzQDUANkA5ADmAOgA6gDvAPIA +9AD2APgA/wEWATIBNAE2E6YTqxO2E78T0hPWE+ET6hPvE/cT+hP/FA4UEhQZFCEULhQzFDUUNxQ8FEQU +RxRMFFQAAAAAAAACAQAAAAAAAABFAAAAAAAAAAAAAAAAAAAUVw diff --git a/i18n/src/main/resources/es.lproj/Command.xib b/i18n/src/main/resources/es.lproj/Command.xib index 3596865097..817e7fa894 100644 --- a/i18n/src/main/resources/es.lproj/Command.xib +++ b/i18n/src/main/resources/es.lproj/Command.xib @@ -1,7 +1,7 @@ - + - + diff --git a/i18n/src/main/resources/es.lproj/Connection.xib b/i18n/src/main/resources/es.lproj/Connection.xib index e81a1383c2..194e59b418 100644 --- a/i18n/src/main/resources/es.lproj/Connection.xib +++ b/i18n/src/main/resources/es.lproj/Connection.xib @@ -1,7 +1,7 @@ - + - + @@ -76,7 +76,7 @@ - + diff --git a/i18n/src/main/resources/es.lproj/Info.xib b/i18n/src/main/resources/es.lproj/Info.xib index a53a4fccdf..b094af217c 100644 --- a/i18n/src/main/resources/es.lproj/Info.xib +++ b/i18n/src/main/resources/es.lproj/Info.xib @@ -1,7 +1,7 @@ - + - + @@ -89,7 +89,7 @@ - + @@ -1017,21 +1017,21 @@ - + - + - + @@ -1121,21 +1121,21 @@ - + - + - + @@ -1250,27 +1250,27 @@ diff --git a/i18n/src/main/resources/es.lproj/Login.strings b/i18n/src/main/resources/es.lproj/Login.strings index f8c32253fc..16e23224d4 100644 Binary files a/i18n/src/main/resources/es.lproj/Login.strings and b/i18n/src/main/resources/es.lproj/Login.strings differ diff --git a/i18n/src/main/resources/es.lproj/Main.xib b/i18n/src/main/resources/es.lproj/Main.xib index 5dcf18f3c8..b1d84d397c 100644 --- a/i18n/src/main/resources/es.lproj/Main.xib +++ b/i18n/src/main/resources/es.lproj/Main.xib @@ -1,7 +1,7 @@ - + - + @@ -159,19 +159,19 @@ - + - + - + @@ -321,12 +321,12 @@ CA - + - + @@ -566,6 +566,10 @@ CA - + + + + + diff --git a/i18n/src/main/resources/es.lproj/Preferences.xib b/i18n/src/main/resources/es.lproj/Preferences.xib index a677610d7d..ca34015eba 100644 --- a/i18n/src/main/resources/es.lproj/Preferences.xib +++ b/i18n/src/main/resources/es.lproj/Preferences.xib @@ -1,7 +1,7 @@ - + - + @@ -266,7 +266,7 @@ - + @@ -307,7 +307,7 @@ - + @@ -538,7 +538,7 @@ - + @@ -578,7 +578,7 @@ - + @@ -613,7 +613,7 @@ - + @@ -642,7 +642,7 @@ - + @@ -1218,7 +1218,7 @@ - + @@ -1250,7 +1250,7 @@ - + @@ -1290,7 +1290,7 @@ - + @@ -1363,7 +1363,7 @@ - + @@ -1558,7 +1558,7 @@ - + @@ -1618,7 +1618,7 @@ - + @@ -1643,7 +1643,7 @@ - + @@ -1685,7 +1685,7 @@ - + @@ -1713,7 +1713,7 @@ - + @@ -1741,7 +1741,7 @@ - + @@ -1770,7 +1770,7 @@ - + @@ -1805,7 +1805,7 @@ - + @@ -1888,7 +1888,7 @@ - + @@ -1947,7 +1947,7 @@ - + @@ -1975,7 +1975,7 @@ - + @@ -2004,7 +2004,7 @@ - + diff --git a/i18n/src/main/resources/es.lproj/Profiles.xib b/i18n/src/main/resources/es.lproj/Profiles.xib index 715b58825f..59a0d0a751 100644 --- a/i18n/src/main/resources/es.lproj/Profiles.xib +++ b/i18n/src/main/resources/es.lproj/Profiles.xib @@ -1,7 +1,7 @@ - + - + diff --git a/i18n/src/main/resources/es.lproj/Prompt.xib b/i18n/src/main/resources/es.lproj/Prompt.xib index c939dff501..c11c9c827a 100644 --- a/i18n/src/main/resources/es.lproj/Prompt.xib +++ b/i18n/src/main/resources/es.lproj/Prompt.xib @@ -1,7 +1,7 @@ - + - + @@ -95,7 +95,7 @@ Gw - + diff --git a/i18n/src/main/resources/es.lproj/S3.strings b/i18n/src/main/resources/es.lproj/S3.strings index 83054214ce..b9800e0dce 100644 Binary files a/i18n/src/main/resources/es.lproj/S3.strings and b/i18n/src/main/resources/es.lproj/S3.strings differ diff --git a/i18n/src/main/resources/es.lproj/Updater.strings b/i18n/src/main/resources/es.lproj/Updater.strings index 52f37189e6..ae0fd6f3ce 100644 Binary files a/i18n/src/main/resources/es.lproj/Updater.strings and b/i18n/src/main/resources/es.lproj/Updater.strings differ diff --git a/i18n/src/main/resources/et.lproj/Bookmark.xib b/i18n/src/main/resources/et.lproj/Bookmark.xib index c16885debb..1bc011409b 100644 --- a/i18n/src/main/resources/et.lproj/Bookmark.xib +++ b/i18n/src/main/resources/et.lproj/Bookmark.xib @@ -1,7 +1,7 @@ - + - + @@ -92,7 +92,7 @@ - + @@ -489,95 +489,95 @@ YnBsaXN0MDDUAQIDBAUGBwpYJHZlcnNpb25ZJGFyY2hpdmVyVCR0b3BYJG9iamVjdHMSAAGGoF8QD05T -S2V5ZWRBcmNoaXZlctEICVRyb290gAGuCwwZGh8UJCkqMTQ3PUBVJG51bGzWDQ4PEBESExQVFhcYVk5T -U2l6ZV5OU1Jlc2l6aW5nTW9kZVYkY2xhc3NcTlNJbWFnZUZsYWdzVk5TUmVwc1dOU0NvbG9ygAIQAIAN -EiDDAACAA4ALVnsxLCAxfdIbDxweWk5TLm9iamVjdHOhHYAEgArSGw8gI6IhIoAFgAaACdMPJSYnKBRf -EBROU1RJRkZSZXByZXNlbnRhdGlvbl8QGU5TSW50ZXJuYWxMYXlvdXREaXJlY3Rpb26ACIAHTxESbE1N -ACoAAAAKAAAAEAEAAAMAAAABAAEAAAEBAAMAAAABAAEAAAECAAMAAAACAAgACAEDAAMAAAABAAEAAAEG -AAMAAAABAAEAAAEKAAMAAAABAAEAAAERAAQAAAABAAAACAESAAMAAAABAAEAAAEVAAMAAAABAAIAAAEW -AAMAAAABAAEAAAEXAAQAAAABAAAAAgEcAAMAAAABAAEAAAEoAAMAAAABAAIAAAFSAAMAAAABAAEAAAFT -AAMAAAACAAEAAYdzAAcAABGcAAAA0AAAAAAAABGcYXBwbAIAAABtbnRyR1JBWVhZWiAH3AAIABcADwAu -AA9hY3NwQVBQTAAAAABub25lAAAAAAAAAAAAAAAAAAAAAAAA9tYAAQAAAADTLWFwcGwAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAVkZXNjAAAAwAAAAHlkc2NtAAABPAAA -CBpjcHJ0AAAJWAAAACN3dHB0AAAJfAAAABRrVFJDAAAJkAAACAxkZXNjAAAAAAAAAB9HZW5lcmljIEdy -YXkgR2FtbWEgMi4yIFByb2ZpbGUAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAbWx1YwAAAAAAAAAfAAAADHNr -U0sAAAAuAAABhGRhREsAAAA6AAABsmNhRVMAAAA4AAAB7HZpVk4AAABAAAACJHB0QlIAAABKAAACZHVr -VUEAAAAsAAACrmZyRlUAAAA+AAAC2mh1SFUAAAA0AAADGHpoVFcAAAAaAAADTGtvS1IAAAAiAAADZm5i -Tk8AAAA6AAADiGNzQ1oAAAAoAAADwmhlSUwAAAAkAAAD6nJvUk8AAAAqAAAEDmRlREUAAABOAAAEOGl0 -SVQAAABOAAAEhnN2U0UAAAA4AAAE1HpoQ04AAAAaAAAFDGphSlAAAAAmAAAFJmVsR1IAAAAqAAAFTHB0 -UE8AAABSAAAFdm5sTkwAAABAAAAFyGVzRVMAAABMAAAGCHRoVEgAAAAyAAAGVHRyVFIAAAAkAAAGhmZp -RkkAAABGAAAGqmhySFIAAAA+AAAG8HBsUEwAAABKAAAHLmFyRUcAAAAsAAAHeHJ1UlUAAAA6AAAHpGVu -VVMAAAA8AAAH3gBWAWEAZQBvAGIAZQBjAG4A4QAgAHMAaQB2AOEAIABnAGEAbQBhACAAMgAsADIARwBl -AG4AZQByAGkAcwBrACAAZwByAOUAIAAyACwAMgAgAGcAYQBtAG0AYQAtAHAAcgBvAGYAaQBsAEcAYQBt -AG0AYQAgAGQAZQAgAGcAcgBpAHMAbwBzACAAZwBlAG4A6AByAGkAYwBhACAAMgAuADIAQx6lAHUAIABo -AOwAbgBoACAATQDgAHUAIAB4AOEAbQAgAEMAaAB1AG4AZwAgAEcAYQBtAG0AYQAgADIALgAyAFAAZQBy -AGYAaQBsACAARwBlAG4A6QByAGkAYwBvACAAZABhACAARwBhAG0AYQAgAGQAZQAgAEMAaQBuAHoAYQBz -ACAAMgAsADIEFwQwBDMEMAQ7BEwEPQQwACAARwByAGEAeQAtBDMEMAQ8BDAAIAAyAC4AMgBQAHIAbwBm -AGkAbAAgAGcA6QBuAOkAcgBpAHEAdQBlACAAZwByAGkAcwAgAGcAYQBtAG0AYQAgADIALAAyAMEAbAB0 -AGEAbADhAG4AbwBzACAAcwB6APwAcgBrAGUAIABnAGEAbQBtAGEAIAAyAC4AMpAadShwcJaOUUlepgAy -AC4AMoJyX2ljz4/wx3y8GAAg1ozAyQAgrBC5yAAgADIALgAyACDVBLhc0wzHfABHAGUAbgBlAHIAaQBz -AGsAIABnAHIA5QAgAGcAYQBtAG0AYQAgADIALAAyAC0AcAByAG8AZgBpAGwATwBiAGUAYwBuAOEAIAFh -AGUAZADhACAAZwBhAG0AYQAgADIALgAyBdIF0AXeBdQAIAXQBeQF1QXoACAF2wXcBdwF2QAgADIALgAy -AEcAYQBtAGEAIABnAHIAaQAgAGcAZQBuAGUAcgBpAGMBAwAgADIALAAyAEEAbABsAGcAZQBtAGUAaQBu -AGUAcwAgAEcAcgBhAHUAcwB0AHUAZgBlAG4ALQBQAHIAbwBmAGkAbAAgAEcAYQBtAG0AYQAgADIALAAy -AFAAcgBvAGYAaQBsAG8AIABnAHIAaQBnAGkAbwAgAGcAZQBuAGUAcgBpAGMAbwAgAGQAZQBsAGwAYQAg -AGcAYQBtAG0AYQAgADIALAAyAEcAZQBuAGUAcgBpAHMAawAgAGcAcgDlACAAMgAsADIAIABnAGEAbQBt -AGEAcAByAG8AZgBpAGxmbpAacHBepnz7ZXAAMgAuADJjz4/wZYdO9k4AgiwwsDDsMKQwrDDzMN4AIAAy -AC4AMgAgMNcw7TDVMKEwpDDrA5MDtQO9A7kDugPMACADkwO6A8EDuQAgA5MDrAO8A7wDsQAgADIALgAy -AFAAZQByAGYAaQBsACAAZwBlAG4A6QByAGkAYwBvACAAZABlACAAYwBpAG4AegBlAG4AdABvAHMAIABk -AGEAIABHAGEAbQBtAGEAIAAyACwAMgBBAGwAZwBlAG0AZQBlAG4AIABnAHIAaQBqAHMAIABnAGEAbQBt -AGEAIAAyACwAMgAtAHAAcgBvAGYAaQBlAGwAUABlAHIAZgBpAGwAIABnAGUAbgDpAHIAaQBjAG8AIABk -AGUAIABnAGEAbQBtAGEAIABkAGUAIABnAHIAaQBzAGUAcwAgADIALAAyDiMOMQ4HDioONQ5BDgEOIQ4h -DjIOQA4BDiMOIg5MDhcOMQ5IDicORA4bACAAMgAuADIARwBlAG4AZQBsACAARwByAGkAIABHAGEAbQBh -ACAAMgAsADIAWQBsAGUAaQBuAGUAbgAgAGgAYQByAG0AYQBhAG4AIABnAGEAbQBtAGEAIAAyACwAMgAg -AC0AcAByAG8AZgBpAGkAbABpAEcAZQBuAGUAcgBpAQ0AawBpACAARwByAGEAeQAgAEcAYQBtAG0AYQAg -ADIALgAyACAAcAByAG8AZgBpAGwAVQBuAGkAdwBlAHIAcwBhAGwAbgB5ACAAcAByAG8AZgBpAGwAIABz -AHoAYQByAG8BWwBjAGkAIABnAGEAbQBtAGEAIAAyACwAMgY6BicGRQYnACAAMgAuADIAIAZEBkgGRgAg -BjEGRQYnBi8GSgAgBjkGJwZFBB4EMQRJBDAETwAgBEEENQRABDAETwAgBDMEMAQ8BDwEMAAgADIALAAy -AC0EPwRABD4ERAQ4BDsETABHAGUAbgBlAHIAaQBjACAARwByAGEAeQAgAEcAYQBtAG0AYQAgADIALgAy -ACAAUAByAG8AZgBpAGwAZQAAdGV4dAAAAABDb3B5cmlnaHQgQXBwbGUgSW5jLiwgMjAxMgAAWFlaIAAA -AAAAAPNRAAEAAAABFsxjdXJ2AAAAAAAABAAAAAAFAAoADwAUABkAHgAjACgALQAyADcAOwBAAEUASgBP -AFQAWQBeAGMAaABtAHIAdwB8AIEAhgCLAJAAlQCaAJ8ApACpAK4AsgC3ALwAwQDGAMsA0ADVANsA4ADl -AOsA8AD2APsBAQEHAQ0BEwEZAR8BJQErATIBOAE+AUUBTAFSAVkBYAFnAW4BdQF8AYMBiwGSAZoBoQGp -AbEBuQHBAckB0QHZAeEB6QHyAfoCAwIMAhQCHQImAi8COAJBAksCVAJdAmcCcQJ6AoQCjgKYAqICrAK2 -AsECywLVAuAC6wL1AwADCwMWAyEDLQM4A0MDTwNaA2YDcgN+A4oDlgOiA64DugPHA9MD4APsA/kEBgQT -BCAELQQ7BEgEVQRjBHEEfgSMBJoEqAS2BMQE0wThBPAE/gUNBRwFKwU6BUkFWAVnBXcFhgWWBaYFtQXF -BdUF5QX2BgYGFgYnBjcGSAZZBmoGewaMBp0GrwbABtEG4wb1BwcHGQcrBz0HTwdhB3QHhgeZB6wHvwfS -B+UH+AgLCB8IMghGCFoIbgiCCJYIqgi+CNII5wj7CRAJJQk6CU8JZAl5CY8JpAm6Cc8J5Qn7ChEKJwo9 -ClQKagqBCpgKrgrFCtwK8wsLCyILOQtRC2kLgAuYC7ALyAvhC/kMEgwqDEMMXAx1DI4MpwzADNkM8w0N -DSYNQA1aDXQNjg2pDcMN3g34DhMOLg5JDmQOfw6bDrYO0g7uDwkPJQ9BD14Peg+WD7MPzw/sEAkQJhBD -EGEQfhCbELkQ1xD1ERMRMRFPEW0RjBGqEckR6BIHEiYSRRJkEoQSoxLDEuMTAxMjE0MTYxODE6QTxRPl -FAYUJxRJFGoUixStFM4U8BUSFTQVVhV4FZsVvRXgFgMWJhZJFmwWjxayFtYW+hcdF0EXZReJF64X0hf3 -GBsYQBhlGIoYrxjVGPoZIBlFGWsZkRm3Gd0aBBoqGlEadxqeGsUa7BsUGzsbYxuKG7Ib2hwCHCocUhx7 -HKMczBz1HR4dRx1wHZkdwx3sHhYeQB5qHpQevh7pHxMfPh9pH5Qfvx/qIBUgQSBsIJggxCDwIRwhSCF1 -IaEhziH7IiciVSKCIq8i3SMKIzgjZiOUI8Ij8CQfJE0kfCSrJNolCSU4JWgllyXHJfcmJyZXJocmtybo -JxgnSSd6J6sn3CgNKD8ocSiiKNQpBik4KWspnSnQKgIqNSpoKpsqzysCKzYraSudK9EsBSw5LG4soizX -LQwtQS12Last4S4WLkwugi63Lu4vJC9aL5Evxy/+MDUwbDCkMNsxEjFKMYIxujHyMioyYzKbMtQzDTNG -M38zuDPxNCs0ZTSeNNg1EzVNNYc1wjX9Njc2cjauNuk3JDdgN5w31zgUOFA4jDjIOQU5Qjl/Obw5+To2 -OnQ6sjrvOy07azuqO+g8JzxlPKQ84z0iPWE9oT3gPiA+YD6gPuA/IT9hP6I/4kAjQGRApkDnQSlBakGs -Qe5CMEJyQrVC90M6Q31DwEQDREdEikTORRJFVUWaRd5GIkZnRqtG8Ec1R3tHwEgFSEtIkUjXSR1JY0mp -SfBKN0p9SsRLDEtTS5pL4kwqTHJMuk0CTUpNk03cTiVObk63TwBPSU+TT91QJ1BxULtRBlFQUZtR5lIx -UnxSx1MTU19TqlP2VEJUj1TbVShVdVXCVg9WXFapVvdXRFeSV+BYL1h9WMtZGllpWbhaB1pWWqZa9VtF -W5Vb5Vw1XIZc1l0nXXhdyV4aXmxevV8PX2Ffs2AFYFdgqmD8YU9homH1YklinGLwY0Njl2PrZEBklGTp -ZT1lkmXnZj1mkmboZz1nk2fpaD9olmjsaUNpmmnxakhqn2r3a09rp2v/bFdsr20IbWBtuW4SbmtuxG8e -b3hv0XArcIZw4HE6cZVx8HJLcqZzAXNdc7h0FHRwdMx1KHWFdeF2Pnabdvh3VnezeBF4bnjMeSp5iXnn -ekZ6pXsEe2N7wnwhfIF84X1BfaF+AX5ifsJ/I3+Ef+WAR4CogQqBa4HNgjCCkoL0g1eDuoQdhICE44VH -hauGDoZyhteHO4efiASIaYjOiTOJmYn+imSKyoswi5aL/IxjjMqNMY2Yjf+OZo7OjzaPnpAGkG6Q1pE/ -kaiSEZJ6kuOTTZO2lCCUipT0lV+VyZY0lp+XCpd1l+CYTJi4mSSZkJn8mmia1ZtCm6+cHJyJnPedZJ3S -nkCerp8dn4uf+qBpoNihR6G2oiailqMGo3aj5qRWpMelOKWpphqmi6b9p26n4KhSqMSpN6mpqhyqj6sC -q3Wr6axcrNCtRK24ri2uoa8Wr4uwALB1sOqxYLHWskuywrM4s660JbSctRO1irYBtnm28Ldot+C4WbjR -uUq5wro7urW7LrunvCG8m70VvY++Cr6Evv+/er/1wHDA7MFnwePCX8Lbw1jD1MRRxM7FS8XIxkbGw8dB -x7/IPci8yTrJuco4yrfLNsu2zDXMtc01zbXONs62zzfPuNA50LrRPNG+0j/SwdNE08bUSdTL1U7V0dZV -1tjXXNfg2GTY6Nls2fHadtr724DcBdyK3RDdlt4c3qLfKd+v4DbgveFE4cziU+Lb42Pj6+Rz5PzlhOYN -5pbnH+ep6DLovOlG6dDqW+rl63Dr++yG7RHtnO4o7rTvQO/M8Fjw5fFy8f/yjPMZ86f0NPTC9VD13vZt -9vv3ivgZ+Kj5OPnH+lf65/t3/Af8mP0p/br+S/7c/23//9IrLC0uWiRjbGFzc25hbWVYJGNsYXNzZXNf -EBBOU0JpdG1hcEltYWdlUmVwoy0vMFpOU0ltYWdlUmVwWE5TT2JqZWN00issMjNXTlNBcnJheaIyMNIr -LDU2Xk5TTXV0YWJsZUFycmF5ozUyMNM4OQ86OzxXTlNXaGl0ZVxOU0NvbG9yU3BhY2VEMCAwABADgAzS -Kyw+P1dOU0NvbG9yoj4w0issQUJXTlNJbWFnZaJBMAAIABEAGgAkACkAMgA3AEkATABRAFMAYgBoAHUA -fACLAJIAnwCmAK4AsACyALQAuQC7AL0AxADJANQA1gDYANoA3wDiAOQA5gDoAO8BBgEiASQBJhOWE5sT -phOvE8ITxhPRE9oT3xPnE+oT7xP+FAIUCRQRFB4UIxQlFCcULBQ0FDcUPBREAAAAAAAAAgEAAAAAAAAA -QwAAAAAAAAAAAAAAAAAAFEc +S2V5ZWRBcmNoaXZlctEICVRyb290gAGuCwwbHCEaJissMzY5P0JVJG51bGzXDQ4PEBESExQVFhcYGRpW +TlNTaXplViRjbGFzc1xOU0ltYWdlRmxhZ3NWTlNSZXBzW05TVGludENvbG9yV05TQ29sb3JeTlNSZXNp +emluZ01vZGWAAoANEiDDAACAA4AAgAsQAFZ7MSwgMX3SHQ4eIFpOUy5vYmplY3RzoR+ABIAK0h0OIiWi +IySABYAGgAnTDicoKSoaXxAUTlNUSUZGUmVwcmVzZW50YXRpb25fEBlOU0ludGVybmFsTGF5b3V0RGly +ZWN0aW9ugAiAB08REmxNTQAqAAAACgAAABABAAADAAAAAQABAAABAQADAAAAAQABAAABAgADAAAAAgAI +AAgBAwADAAAAAQABAAABBgADAAAAAQABAAABCgADAAAAAQABAAABEQAEAAAAAQAAAAgBEgADAAAAAQAB +AAABFQADAAAAAQACAAABFgADAAAAAQABAAABFwAEAAAAAQAAAAIBHAADAAAAAQABAAABKAADAAAAAQAC +AAABUgADAAAAAQABAAABUwADAAAAAgABAAGHcwAHAAARnAAAANAAAAAAAAARnGFwcGwCAAAAbW50ckdS +QVlYWVogB9wACAAXAA8ALgAPYWNzcEFQUEwAAAAAbm9uZQAAAAAAAAAAAAAAAAAAAAAAAPbWAAEAAAAA +0y1hcHBsAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFZGVzYwAA +AMAAAAB5ZHNjbQAAATwAAAgaY3BydAAACVgAAAAjd3RwdAAACXwAAAAUa1RSQwAACZAAAAgMZGVzYwAA +AAAAAAAfR2VuZXJpYyBHcmF5IEdhbW1hIDIuMiBQcm9maWxlAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAG1s +dWMAAAAAAAAAHwAAAAxza1NLAAAALgAAAYRkYURLAAAAOgAAAbJjYUVTAAAAOAAAAex2aVZOAAAAQAAA +AiRwdEJSAAAASgAAAmR1a1VBAAAALAAAAq5mckZVAAAAPgAAAtpodUhVAAAANAAAAxh6aFRXAAAAGgAA +A0xrb0tSAAAAIgAAA2ZuYk5PAAAAOgAAA4hjc0NaAAAAKAAAA8JoZUlMAAAAJAAAA+pyb1JPAAAAKgAA +BA5kZURFAAAATgAABDhpdElUAAAATgAABIZzdlNFAAAAOAAABNR6aENOAAAAGgAABQxqYUpQAAAAJgAA +BSZlbEdSAAAAKgAABUxwdFBPAAAAUgAABXZubE5MAAAAQAAABchlc0VTAAAATAAABgh0aFRIAAAAMgAA +BlR0clRSAAAAJAAABoZmaUZJAAAARgAABqpockhSAAAAPgAABvBwbFBMAAAASgAABy5hckVHAAAALAAA +B3hydVJVAAAAOgAAB6RlblVTAAAAPAAAB94AVgFhAGUAbwBiAGUAYwBuAOEAIABzAGkAdgDhACAAZwBh +AG0AYQAgADIALAAyAEcAZQBuAGUAcgBpAHMAawAgAGcAcgDlACAAMgAsADIAIABnAGEAbQBtAGEALQBw +AHIAbwBmAGkAbABHAGEAbQBtAGEAIABkAGUAIABnAHIAaQBzAG8AcwAgAGcAZQBuAOgAcgBpAGMAYQAg +ADIALgAyAEMepQB1ACAAaADsAG4AaAAgAE0A4AB1ACAAeADhAG0AIABDAGgAdQBuAGcAIABHAGEAbQBt +AGEAIAAyAC4AMgBQAGUAcgBmAGkAbAAgAEcAZQBuAOkAcgBpAGMAbwAgAGQAYQAgAEcAYQBtAGEAIABk +AGUAIABDAGkAbgB6AGEAcwAgADIALAAyBBcEMAQzBDAEOwRMBD0EMAAgAEcAcgBhAHkALQQzBDAEPAQw +ACAAMgAuADIAUAByAG8AZgBpAGwAIABnAOkAbgDpAHIAaQBxAHUAZQAgAGcAcgBpAHMAIABnAGEAbQBt +AGEAIAAyACwAMgDBAGwAdABhAGwA4QBuAG8AcwAgAHMAegD8AHIAawBlACAAZwBhAG0AbQBhACAAMgAu +ADKQGnUocHCWjlFJXqYAMgAuADKCcl9pY8+P8Md8vBgAINaMwMkAIKwQucgAIAAyAC4AMgAg1QS4XNMM +x3wARwBlAG4AZQByAGkAcwBrACAAZwByAOUAIABnAGEAbQBtAGEAIAAyACwAMgAtAHAAcgBvAGYAaQBs +AE8AYgBlAGMAbgDhACABYQBlAGQA4QAgAGcAYQBtAGEAIAAyAC4AMgXSBdAF3gXUACAF0AXkBdUF6AAg +BdsF3AXcBdkAIAAyAC4AMgBHAGEAbQBhACAAZwByAGkAIABnAGUAbgBlAHIAaQBjAQMAIAAyACwAMgBB +AGwAbABnAGUAbQBlAGkAbgBlAHMAIABHAHIAYQB1AHMAdAB1AGYAZQBuAC0AUAByAG8AZgBpAGwAIABH +AGEAbQBtAGEAIAAyACwAMgBQAHIAbwBmAGkAbABvACAAZwByAGkAZwBpAG8AIABnAGUAbgBlAHIAaQBj +AG8AIABkAGUAbABsAGEAIABnAGEAbQBtAGEAIAAyACwAMgBHAGUAbgBlAHIAaQBzAGsAIABnAHIA5QAg +ADIALAAyACAAZwBhAG0AbQBhAHAAcgBvAGYAaQBsZm6QGnBwXqZ8+2VwADIALgAyY8+P8GWHTvZOAIIs +MLAw7DCkMKww8zDeACAAMgAuADIAIDDXMO0w1TChMKQw6wOTA7UDvQO5A7oDzAAgA5MDugPBA7kAIAOT +A6wDvAO8A7EAIAAyAC4AMgBQAGUAcgBmAGkAbAAgAGcAZQBuAOkAcgBpAGMAbwAgAGQAZQAgAGMAaQBu +AHoAZQBuAHQAbwBzACAAZABhACAARwBhAG0AbQBhACAAMgAsADIAQQBsAGcAZQBtAGUAZQBuACAAZwBy +AGkAagBzACAAZwBhAG0AbQBhACAAMgAsADIALQBwAHIAbwBmAGkAZQBsAFAAZQByAGYAaQBsACAAZwBl +AG4A6QByAGkAYwBvACAAZABlACAAZwBhAG0AbQBhACAAZABlACAAZwByAGkAcwBlAHMAIAAyACwAMg4j +DjEOBw4qDjUOQQ4BDiEOIQ4yDkAOAQ4jDiIOTA4XDjEOSA4nDkQOGwAgADIALgAyAEcAZQBuAGUAbAAg +AEcAcgBpACAARwBhAG0AYQAgADIALAAyAFkAbABlAGkAbgBlAG4AIABoAGEAcgBtAGEAYQBuACAAZwBh +AG0AbQBhACAAMgAsADIAIAAtAHAAcgBvAGYAaQBpAGwAaQBHAGUAbgBlAHIAaQENAGsAaQAgAEcAcgBh +AHkAIABHAGEAbQBtAGEAIAAyAC4AMgAgAHAAcgBvAGYAaQBsAFUAbgBpAHcAZQByAHMAYQBsAG4AeQAg +AHAAcgBvAGYAaQBsACAAcwB6AGEAcgBvAVsAYwBpACAAZwBhAG0AbQBhACAAMgAsADIGOgYnBkUGJwAg +ADIALgAyACAGRAZIBkYAIAYxBkUGJwYvBkoAIAY5BicGRQQeBDEESQQwBE8AIARBBDUEQAQwBE8AIAQz +BDAEPAQ8BDAAIAAyACwAMgAtBD8EQAQ+BEQEOAQ7BEwARwBlAG4AZQByAGkAYwAgAEcAcgBhAHkAIABH +AGEAbQBtAGEAIAAyAC4AMgAgAFAAcgBvAGYAaQBsAGUAAHRleHQAAAAAQ29weXJpZ2h0IEFwcGxlIElu +Yy4sIDIwMTIAAFhZWiAAAAAAAADzUQABAAAAARbMY3VydgAAAAAAAAQAAAAABQAKAA8AFAAZAB4AIwAo +AC0AMgA3ADsAQABFAEoATwBUAFkAXgBjAGgAbQByAHcAfACBAIYAiwCQAJUAmgCfAKQAqQCuALIAtwC8 +AMEAxgDLANAA1QDbAOAA5QDrAPAA9gD7AQEBBwENARMBGQEfASUBKwEyATgBPgFFAUwBUgFZAWABZwFu +AXUBfAGDAYsBkgGaAaEBqQGxAbkBwQHJAdEB2QHhAekB8gH6AgMCDAIUAh0CJgIvAjgCQQJLAlQCXQJn +AnECegKEAo4CmAKiAqwCtgLBAssC1QLgAusC9QMAAwsDFgMhAy0DOANDA08DWgNmA3IDfgOKA5YDogOu +A7oDxwPTA+AD7AP5BAYEEwQgBC0EOwRIBFUEYwRxBH4EjASaBKgEtgTEBNME4QTwBP4FDQUcBSsFOgVJ +BVgFZwV3BYYFlgWmBbUFxQXVBeUF9gYGBhYGJwY3BkgGWQZqBnsGjAadBq8GwAbRBuMG9QcHBxkHKwc9 +B08HYQd0B4YHmQesB78H0gflB/gICwgfCDIIRghaCG4IggiWCKoIvgjSCOcI+wkQCSUJOglPCWQJeQmP +CaQJugnPCeUJ+woRCicKPQpUCmoKgQqYCq4KxQrcCvMLCwsiCzkLUQtpC4ALmAuwC8gL4Qv5DBIMKgxD +DFwMdQyODKcMwAzZDPMNDQ0mDUANWg10DY4NqQ3DDd4N+A4TDi4OSQ5kDn8Omw62DtIO7g8JDyUPQQ9e +D3oPlg+zD88P7BAJECYQQxBhEH4QmxC5ENcQ9RETETERTxFtEYwRqhHJEegSBxImEkUSZBKEEqMSwxLj +EwMTIxNDE2MTgxOkE8UT5RQGFCcUSRRqFIsUrRTOFPAVEhU0FVYVeBWbFb0V4BYDFiYWSRZsFo8WshbW +FvoXHRdBF2UXiReuF9IX9xgbGEAYZRiKGK8Y1Rj6GSAZRRlrGZEZtxndGgQaKhpRGncanhrFGuwbFBs7 +G2MbihuyG9ocAhwqHFIcexyjHMwc9R0eHUcdcB2ZHcMd7B4WHkAeah6UHr4e6R8THz4faR+UH78f6iAV +IEEgbCCYIMQg8CEcIUghdSGhIc4h+yInIlUigiKvIt0jCiM4I2YjlCPCI/AkHyRNJHwkqyTaJQklOCVo +JZclxyX3JicmVyaHJrcm6CcYJ0kneierJ9woDSg/KHEooijUKQYpOClrKZ0p0CoCKjUqaCqbKs8rAis2 +K2krnSvRLAUsOSxuLKIs1y0MLUEtdi2rLeEuFi5MLoIuty7uLyQvWi+RL8cv/jA1MGwwpDDbMRIxSjGC +Mbox8jIqMmMymzLUMw0zRjN/M7gz8TQrNGU0njTYNRM1TTWHNcI1/TY3NnI2rjbpNyQ3YDecN9c4FDhQ +OIw4yDkFOUI5fzm8Ofk6Njp0OrI67zstO2s7qjvoPCc8ZTykPOM9Ij1hPaE94D4gPmA+oD7gPyE/YT+i +P+JAI0BkQKZA50EpQWpBrEHuQjBCckK1QvdDOkN9Q8BEA0RHRIpEzkUSRVVFmkXeRiJGZ0arRvBHNUd7 +R8BIBUhLSJFI10kdSWNJqUnwSjdKfUrESwxLU0uaS+JMKkxyTLpNAk1KTZNN3E4lTm5Ot08AT0lPk0/d +UCdQcVC7UQZRUFGbUeZSMVJ8UsdTE1NfU6pT9lRCVI9U21UoVXVVwlYPVlxWqVb3V0RXklfgWC9YfVjL +WRpZaVm4WgdaVlqmWvVbRVuVW+VcNVyGXNZdJ114XcleGl5sXr1fD19hX7NgBWBXYKpg/GFPYaJh9WJJ +Ypxi8GNDY5dj62RAZJRk6WU9ZZJl52Y9ZpJm6Gc9Z5Nn6Wg/aJZo7GlDaZpp8WpIap9q92tPa6dr/2xX +bK9tCG1gbbluEm5rbsRvHm94b9FwK3CGcOBxOnGVcfByS3KmcwFzXXO4dBR0cHTMdSh1hXXhdj52m3b4 +d1Z3s3gReG54zHkqeYl553pGeqV7BHtje8J8IXyBfOF9QX2hfgF+Yn7CfyN/hH/lgEeAqIEKgWuBzYIw +gpKC9INXg7qEHYSAhOOFR4Wrhg6GcobXhzuHn4gEiGmIzokziZmJ/opkisqLMIuWi/yMY4zKjTGNmI3/ +jmaOzo82j56QBpBukNaRP5GokhGSepLjk02TtpQglIqU9JVflcmWNJaflwqXdZfgmEyYuJkkmZCZ/Jpo +mtWbQpuvnByciZz3nWSd0p5Anq6fHZ+Ln/qgaaDYoUehtqImopajBqN2o+akVqTHpTilqaYapoum/adu +p+CoUqjEqTepqaocqo+rAqt1q+msXKzQrUStuK4trqGvFq+LsACwdbDqsWCx1rJLssKzOLOutCW0nLUT +tYq2AbZ5tvC3aLfguFm40blKucK6O7q1uy67p7whvJu9Fb2Pvgq+hL7/v3q/9cBwwOzBZ8Hjwl/C28NY +w9TEUcTOxUvFyMZGxsPHQce/yD3IvMk6ybnKOMq3yzbLtsw1zLXNNc21zjbOts83z7jQOdC60TzRvtI/ +0sHTRNPG1EnUy9VO1dHWVdbY11zX4Nhk2OjZbNnx2nba+9uA3AXcit0Q3ZbeHN6i3ynfr+A24L3hROHM +4lPi2+Nj4+vkc+T85YTmDeaW5x/nqegy6LzpRunQ6lvq5etw6/vshu0R7ZzuKO6070DvzPBY8OXxcvH/ +8ozzGfOn9DT0wvVQ9d72bfb794r4Gfio+Tj5x/pX+uf7d/wH/Jj9Kf26/kv+3P9t///SLS4vMFokY2xh +c3NuYW1lWCRjbGFzc2VzXxAQTlNCaXRtYXBJbWFnZVJlcKMvMTJaTlNJbWFnZVJlcFhOU09iamVjdNIt +LjQ1V05TQXJyYXmiNDLSLS43OF5OU011dGFibGVBcnJheaM3NDLTOjsOPD0+V05TV2hpdGVcTlNDb2xv +clNwYWNlRDAgMAAQA4AM0i0uQEFXTlNDb2xvcqJAMtItLkNEV05TSW1hZ2WiQzIACAARABoAJAApADIA +NwBJAEwAUQBTAGIAaAB3AH4AhQCSAJkApQCtALwAvgDAAMUAxwDJAMsAzQDUANkA5ADmAOgA6gDvAPIA +9AD2APgA/wEWATIBNAE2E6YTqxO2E78T0hPWE+ET6hPvE/cT+hP/FA4UEhQZFCEULhQzFDUUNxQ8FEQU +RxRMFFQAAAAAAAACAQAAAAAAAABFAAAAAAAAAAAAAAAAAAAUVw diff --git a/i18n/src/main/resources/et.lproj/Browser.xib b/i18n/src/main/resources/et.lproj/Browser.xib index 68fca24b12..317c5b6e6e 100644 --- a/i18n/src/main/resources/et.lproj/Browser.xib +++ b/i18n/src/main/resources/et.lproj/Browser.xib @@ -1,7 +1,7 @@ - + - + @@ -38,7 +38,7 @@ - + @@ -62,27 +62,27 @@ @@ -256,7 +256,7 @@ - + @@ -553,95 +553,95 @@ DQ YnBsaXN0MDDUAQIDBAUGBwpYJHZlcnNpb25ZJGFyY2hpdmVyVCR0b3BYJG9iamVjdHMSAAGGoF8QD05T -S2V5ZWRBcmNoaXZlctEICVRyb290gAGuCwwZGh8UJCkqMTQ3PUBVJG51bGzWDQ4PEBESExQVFhcYVk5T -U2l6ZV5OU1Jlc2l6aW5nTW9kZVYkY2xhc3NcTlNJbWFnZUZsYWdzVk5TUmVwc1dOU0NvbG9ygAIQAIAN -EiDDAACAA4ALVnsxLCAxfdIbDxweWk5TLm9iamVjdHOhHYAEgArSGw8gI6IhIoAFgAaACdMPJSYnKBRf -EBROU1RJRkZSZXByZXNlbnRhdGlvbl8QGU5TSW50ZXJuYWxMYXlvdXREaXJlY3Rpb26ACIAHTxESbE1N -ACoAAAAKAAAAEAEAAAMAAAABAAEAAAEBAAMAAAABAAEAAAECAAMAAAACAAgACAEDAAMAAAABAAEAAAEG -AAMAAAABAAEAAAEKAAMAAAABAAEAAAERAAQAAAABAAAACAESAAMAAAABAAEAAAEVAAMAAAABAAIAAAEW -AAMAAAABAAEAAAEXAAQAAAABAAAAAgEcAAMAAAABAAEAAAEoAAMAAAABAAIAAAFSAAMAAAABAAEAAAFT -AAMAAAACAAEAAYdzAAcAABGcAAAA0AAAAAAAABGcYXBwbAIAAABtbnRyR1JBWVhZWiAH3AAIABcADwAu -AA9hY3NwQVBQTAAAAABub25lAAAAAAAAAAAAAAAAAAAAAAAA9tYAAQAAAADTLWFwcGwAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAVkZXNjAAAAwAAAAHlkc2NtAAABPAAA -CBpjcHJ0AAAJWAAAACN3dHB0AAAJfAAAABRrVFJDAAAJkAAACAxkZXNjAAAAAAAAAB9HZW5lcmljIEdy -YXkgR2FtbWEgMi4yIFByb2ZpbGUAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAbWx1YwAAAAAAAAAfAAAADHNr -U0sAAAAuAAABhGRhREsAAAA6AAABsmNhRVMAAAA4AAAB7HZpVk4AAABAAAACJHB0QlIAAABKAAACZHVr -VUEAAAAsAAACrmZyRlUAAAA+AAAC2mh1SFUAAAA0AAADGHpoVFcAAAAaAAADTGtvS1IAAAAiAAADZm5i -Tk8AAAA6AAADiGNzQ1oAAAAoAAADwmhlSUwAAAAkAAAD6nJvUk8AAAAqAAAEDmRlREUAAABOAAAEOGl0 -SVQAAABOAAAEhnN2U0UAAAA4AAAE1HpoQ04AAAAaAAAFDGphSlAAAAAmAAAFJmVsR1IAAAAqAAAFTHB0 -UE8AAABSAAAFdm5sTkwAAABAAAAFyGVzRVMAAABMAAAGCHRoVEgAAAAyAAAGVHRyVFIAAAAkAAAGhmZp -RkkAAABGAAAGqmhySFIAAAA+AAAG8HBsUEwAAABKAAAHLmFyRUcAAAAsAAAHeHJ1UlUAAAA6AAAHpGVu -VVMAAAA8AAAH3gBWAWEAZQBvAGIAZQBjAG4A4QAgAHMAaQB2AOEAIABnAGEAbQBhACAAMgAsADIARwBl -AG4AZQByAGkAcwBrACAAZwByAOUAIAAyACwAMgAgAGcAYQBtAG0AYQAtAHAAcgBvAGYAaQBsAEcAYQBt -AG0AYQAgAGQAZQAgAGcAcgBpAHMAbwBzACAAZwBlAG4A6AByAGkAYwBhACAAMgAuADIAQx6lAHUAIABo -AOwAbgBoACAATQDgAHUAIAB4AOEAbQAgAEMAaAB1AG4AZwAgAEcAYQBtAG0AYQAgADIALgAyAFAAZQBy -AGYAaQBsACAARwBlAG4A6QByAGkAYwBvACAAZABhACAARwBhAG0AYQAgAGQAZQAgAEMAaQBuAHoAYQBz -ACAAMgAsADIEFwQwBDMEMAQ7BEwEPQQwACAARwByAGEAeQAtBDMEMAQ8BDAAIAAyAC4AMgBQAHIAbwBm -AGkAbAAgAGcA6QBuAOkAcgBpAHEAdQBlACAAZwByAGkAcwAgAGcAYQBtAG0AYQAgADIALAAyAMEAbAB0 -AGEAbADhAG4AbwBzACAAcwB6APwAcgBrAGUAIABnAGEAbQBtAGEAIAAyAC4AMpAadShwcJaOUUlepgAy -AC4AMoJyX2ljz4/wx3y8GAAg1ozAyQAgrBC5yAAgADIALgAyACDVBLhc0wzHfABHAGUAbgBlAHIAaQBz -AGsAIABnAHIA5QAgAGcAYQBtAG0AYQAgADIALAAyAC0AcAByAG8AZgBpAGwATwBiAGUAYwBuAOEAIAFh -AGUAZADhACAAZwBhAG0AYQAgADIALgAyBdIF0AXeBdQAIAXQBeQF1QXoACAF2wXcBdwF2QAgADIALgAy -AEcAYQBtAGEAIABnAHIAaQAgAGcAZQBuAGUAcgBpAGMBAwAgADIALAAyAEEAbABsAGcAZQBtAGUAaQBu -AGUAcwAgAEcAcgBhAHUAcwB0AHUAZgBlAG4ALQBQAHIAbwBmAGkAbAAgAEcAYQBtAG0AYQAgADIALAAy -AFAAcgBvAGYAaQBsAG8AIABnAHIAaQBnAGkAbwAgAGcAZQBuAGUAcgBpAGMAbwAgAGQAZQBsAGwAYQAg -AGcAYQBtAG0AYQAgADIALAAyAEcAZQBuAGUAcgBpAHMAawAgAGcAcgDlACAAMgAsADIAIABnAGEAbQBt -AGEAcAByAG8AZgBpAGxmbpAacHBepnz7ZXAAMgAuADJjz4/wZYdO9k4AgiwwsDDsMKQwrDDzMN4AIAAy -AC4AMgAgMNcw7TDVMKEwpDDrA5MDtQO9A7kDugPMACADkwO6A8EDuQAgA5MDrAO8A7wDsQAgADIALgAy -AFAAZQByAGYAaQBsACAAZwBlAG4A6QByAGkAYwBvACAAZABlACAAYwBpAG4AegBlAG4AdABvAHMAIABk -AGEAIABHAGEAbQBtAGEAIAAyACwAMgBBAGwAZwBlAG0AZQBlAG4AIABnAHIAaQBqAHMAIABnAGEAbQBt -AGEAIAAyACwAMgAtAHAAcgBvAGYAaQBlAGwAUABlAHIAZgBpAGwAIABnAGUAbgDpAHIAaQBjAG8AIABk -AGUAIABnAGEAbQBtAGEAIABkAGUAIABnAHIAaQBzAGUAcwAgADIALAAyDiMOMQ4HDioONQ5BDgEOIQ4h -DjIOQA4BDiMOIg5MDhcOMQ5IDicORA4bACAAMgAuADIARwBlAG4AZQBsACAARwByAGkAIABHAGEAbQBh -ACAAMgAsADIAWQBsAGUAaQBuAGUAbgAgAGgAYQByAG0AYQBhAG4AIABnAGEAbQBtAGEAIAAyACwAMgAg -AC0AcAByAG8AZgBpAGkAbABpAEcAZQBuAGUAcgBpAQ0AawBpACAARwByAGEAeQAgAEcAYQBtAG0AYQAg -ADIALgAyACAAcAByAG8AZgBpAGwAVQBuAGkAdwBlAHIAcwBhAGwAbgB5ACAAcAByAG8AZgBpAGwAIABz -AHoAYQByAG8BWwBjAGkAIABnAGEAbQBtAGEAIAAyACwAMgY6BicGRQYnACAAMgAuADIAIAZEBkgGRgAg -BjEGRQYnBi8GSgAgBjkGJwZFBB4EMQRJBDAETwAgBEEENQRABDAETwAgBDMEMAQ8BDwEMAAgADIALAAy -AC0EPwRABD4ERAQ4BDsETABHAGUAbgBlAHIAaQBjACAARwByAGEAeQAgAEcAYQBtAG0AYQAgADIALgAy -ACAAUAByAG8AZgBpAGwAZQAAdGV4dAAAAABDb3B5cmlnaHQgQXBwbGUgSW5jLiwgMjAxMgAAWFlaIAAA -AAAAAPNRAAEAAAABFsxjdXJ2AAAAAAAABAAAAAAFAAoADwAUABkAHgAjACgALQAyADcAOwBAAEUASgBP -AFQAWQBeAGMAaABtAHIAdwB8AIEAhgCLAJAAlQCaAJ8ApACpAK4AsgC3ALwAwQDGAMsA0ADVANsA4ADl -AOsA8AD2APsBAQEHAQ0BEwEZAR8BJQErATIBOAE+AUUBTAFSAVkBYAFnAW4BdQF8AYMBiwGSAZoBoQGp -AbEBuQHBAckB0QHZAeEB6QHyAfoCAwIMAhQCHQImAi8COAJBAksCVAJdAmcCcQJ6AoQCjgKYAqICrAK2 -AsECywLVAuAC6wL1AwADCwMWAyEDLQM4A0MDTwNaA2YDcgN+A4oDlgOiA64DugPHA9MD4APsA/kEBgQT -BCAELQQ7BEgEVQRjBHEEfgSMBJoEqAS2BMQE0wThBPAE/gUNBRwFKwU6BUkFWAVnBXcFhgWWBaYFtQXF -BdUF5QX2BgYGFgYnBjcGSAZZBmoGewaMBp0GrwbABtEG4wb1BwcHGQcrBz0HTwdhB3QHhgeZB6wHvwfS -B+UH+AgLCB8IMghGCFoIbgiCCJYIqgi+CNII5wj7CRAJJQk6CU8JZAl5CY8JpAm6Cc8J5Qn7ChEKJwo9 -ClQKagqBCpgKrgrFCtwK8wsLCyILOQtRC2kLgAuYC7ALyAvhC/kMEgwqDEMMXAx1DI4MpwzADNkM8w0N -DSYNQA1aDXQNjg2pDcMN3g34DhMOLg5JDmQOfw6bDrYO0g7uDwkPJQ9BD14Peg+WD7MPzw/sEAkQJhBD -EGEQfhCbELkQ1xD1ERMRMRFPEW0RjBGqEckR6BIHEiYSRRJkEoQSoxLDEuMTAxMjE0MTYxODE6QTxRPl -FAYUJxRJFGoUixStFM4U8BUSFTQVVhV4FZsVvRXgFgMWJhZJFmwWjxayFtYW+hcdF0EXZReJF64X0hf3 -GBsYQBhlGIoYrxjVGPoZIBlFGWsZkRm3Gd0aBBoqGlEadxqeGsUa7BsUGzsbYxuKG7Ib2hwCHCocUhx7 -HKMczBz1HR4dRx1wHZkdwx3sHhYeQB5qHpQevh7pHxMfPh9pH5Qfvx/qIBUgQSBsIJggxCDwIRwhSCF1 -IaEhziH7IiciVSKCIq8i3SMKIzgjZiOUI8Ij8CQfJE0kfCSrJNolCSU4JWgllyXHJfcmJyZXJocmtybo -JxgnSSd6J6sn3CgNKD8ocSiiKNQpBik4KWspnSnQKgIqNSpoKpsqzysCKzYraSudK9EsBSw5LG4soizX -LQwtQS12Last4S4WLkwugi63Lu4vJC9aL5Evxy/+MDUwbDCkMNsxEjFKMYIxujHyMioyYzKbMtQzDTNG -M38zuDPxNCs0ZTSeNNg1EzVNNYc1wjX9Njc2cjauNuk3JDdgN5w31zgUOFA4jDjIOQU5Qjl/Obw5+To2 -OnQ6sjrvOy07azuqO+g8JzxlPKQ84z0iPWE9oT3gPiA+YD6gPuA/IT9hP6I/4kAjQGRApkDnQSlBakGs -Qe5CMEJyQrVC90M6Q31DwEQDREdEikTORRJFVUWaRd5GIkZnRqtG8Ec1R3tHwEgFSEtIkUjXSR1JY0mp -SfBKN0p9SsRLDEtTS5pL4kwqTHJMuk0CTUpNk03cTiVObk63TwBPSU+TT91QJ1BxULtRBlFQUZtR5lIx -UnxSx1MTU19TqlP2VEJUj1TbVShVdVXCVg9WXFapVvdXRFeSV+BYL1h9WMtZGllpWbhaB1pWWqZa9VtF -W5Vb5Vw1XIZc1l0nXXhdyV4aXmxevV8PX2Ffs2AFYFdgqmD8YU9homH1YklinGLwY0Njl2PrZEBklGTp -ZT1lkmXnZj1mkmboZz1nk2fpaD9olmjsaUNpmmnxakhqn2r3a09rp2v/bFdsr20IbWBtuW4SbmtuxG8e -b3hv0XArcIZw4HE6cZVx8HJLcqZzAXNdc7h0FHRwdMx1KHWFdeF2Pnabdvh3VnezeBF4bnjMeSp5iXnn -ekZ6pXsEe2N7wnwhfIF84X1BfaF+AX5ifsJ/I3+Ef+WAR4CogQqBa4HNgjCCkoL0g1eDuoQdhICE44VH -hauGDoZyhteHO4efiASIaYjOiTOJmYn+imSKyoswi5aL/IxjjMqNMY2Yjf+OZo7OjzaPnpAGkG6Q1pE/ -kaiSEZJ6kuOTTZO2lCCUipT0lV+VyZY0lp+XCpd1l+CYTJi4mSSZkJn8mmia1ZtCm6+cHJyJnPedZJ3S -nkCerp8dn4uf+qBpoNihR6G2oiailqMGo3aj5qRWpMelOKWpphqmi6b9p26n4KhSqMSpN6mpqhyqj6sC -q3Wr6axcrNCtRK24ri2uoa8Wr4uwALB1sOqxYLHWskuywrM4s660JbSctRO1irYBtnm28Ldot+C4WbjR -uUq5wro7urW7LrunvCG8m70VvY++Cr6Evv+/er/1wHDA7MFnwePCX8Lbw1jD1MRRxM7FS8XIxkbGw8dB -x7/IPci8yTrJuco4yrfLNsu2zDXMtc01zbXONs62zzfPuNA50LrRPNG+0j/SwdNE08bUSdTL1U7V0dZV -1tjXXNfg2GTY6Nls2fHadtr724DcBdyK3RDdlt4c3qLfKd+v4DbgveFE4cziU+Lb42Pj6+Rz5PzlhOYN -5pbnH+ep6DLovOlG6dDqW+rl63Dr++yG7RHtnO4o7rTvQO/M8Fjw5fFy8f/yjPMZ86f0NPTC9VD13vZt -9vv3ivgZ+Kj5OPnH+lf65/t3/Af8mP0p/br+S/7c/23//9IrLC0uWiRjbGFzc25hbWVYJGNsYXNzZXNf -EBBOU0JpdG1hcEltYWdlUmVwoy0vMFpOU0ltYWdlUmVwWE5TT2JqZWN00issMjNXTlNBcnJheaIyMNIr -LDU2Xk5TTXV0YWJsZUFycmF5ozUyMNM4OQ86OzxXTlNXaGl0ZVxOU0NvbG9yU3BhY2VEMCAwABADgAzS -Kyw+P1dOU0NvbG9yoj4w0issQUJXTlNJbWFnZaJBMAAIABEAGgAkACkAMgA3AEkATABRAFMAYgBoAHUA -fACLAJIAnwCmAK4AsACyALQAuQC7AL0AxADJANQA1gDYANoA3wDiAOQA5gDoAO8BBgEiASQBJhOWE5sT -phOvE8ITxhPRE9oT3xPnE+oT7xP+FAIUCRQRFB4UIxQlFCcULBQ0FDcUPBREAAAAAAAAAgEAAAAAAAAA -QwAAAAAAAAAAAAAAAAAAFEc +S2V5ZWRBcmNoaXZlctEICVRyb290gAGuCwwbHCEaJissMzY5P0JVJG51bGzXDQ4PEBESExQVFhcYGRpW +TlNTaXplViRjbGFzc1xOU0ltYWdlRmxhZ3NWTlNSZXBzW05TVGludENvbG9yV05TQ29sb3JeTlNSZXNp +emluZ01vZGWAAoANEiDDAACAA4AAgAsQAFZ7MSwgMX3SHQ4eIFpOUy5vYmplY3RzoR+ABIAK0h0OIiWi +IySABYAGgAnTDicoKSoaXxAUTlNUSUZGUmVwcmVzZW50YXRpb25fEBlOU0ludGVybmFsTGF5b3V0RGly +ZWN0aW9ugAiAB08REmxNTQAqAAAACgAAABABAAADAAAAAQABAAABAQADAAAAAQABAAABAgADAAAAAgAI +AAgBAwADAAAAAQABAAABBgADAAAAAQABAAABCgADAAAAAQABAAABEQAEAAAAAQAAAAgBEgADAAAAAQAB +AAABFQADAAAAAQACAAABFgADAAAAAQABAAABFwAEAAAAAQAAAAIBHAADAAAAAQABAAABKAADAAAAAQAC +AAABUgADAAAAAQABAAABUwADAAAAAgABAAGHcwAHAAARnAAAANAAAAAAAAARnGFwcGwCAAAAbW50ckdS +QVlYWVogB9wACAAXAA8ALgAPYWNzcEFQUEwAAAAAbm9uZQAAAAAAAAAAAAAAAAAAAAAAAPbWAAEAAAAA +0y1hcHBsAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFZGVzYwAA +AMAAAAB5ZHNjbQAAATwAAAgaY3BydAAACVgAAAAjd3RwdAAACXwAAAAUa1RSQwAACZAAAAgMZGVzYwAA +AAAAAAAfR2VuZXJpYyBHcmF5IEdhbW1hIDIuMiBQcm9maWxlAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAG1s +dWMAAAAAAAAAHwAAAAxza1NLAAAALgAAAYRkYURLAAAAOgAAAbJjYUVTAAAAOAAAAex2aVZOAAAAQAAA +AiRwdEJSAAAASgAAAmR1a1VBAAAALAAAAq5mckZVAAAAPgAAAtpodUhVAAAANAAAAxh6aFRXAAAAGgAA +A0xrb0tSAAAAIgAAA2ZuYk5PAAAAOgAAA4hjc0NaAAAAKAAAA8JoZUlMAAAAJAAAA+pyb1JPAAAAKgAA +BA5kZURFAAAATgAABDhpdElUAAAATgAABIZzdlNFAAAAOAAABNR6aENOAAAAGgAABQxqYUpQAAAAJgAA +BSZlbEdSAAAAKgAABUxwdFBPAAAAUgAABXZubE5MAAAAQAAABchlc0VTAAAATAAABgh0aFRIAAAAMgAA +BlR0clRSAAAAJAAABoZmaUZJAAAARgAABqpockhSAAAAPgAABvBwbFBMAAAASgAABy5hckVHAAAALAAA +B3hydVJVAAAAOgAAB6RlblVTAAAAPAAAB94AVgFhAGUAbwBiAGUAYwBuAOEAIABzAGkAdgDhACAAZwBh +AG0AYQAgADIALAAyAEcAZQBuAGUAcgBpAHMAawAgAGcAcgDlACAAMgAsADIAIABnAGEAbQBtAGEALQBw +AHIAbwBmAGkAbABHAGEAbQBtAGEAIABkAGUAIABnAHIAaQBzAG8AcwAgAGcAZQBuAOgAcgBpAGMAYQAg +ADIALgAyAEMepQB1ACAAaADsAG4AaAAgAE0A4AB1ACAAeADhAG0AIABDAGgAdQBuAGcAIABHAGEAbQBt +AGEAIAAyAC4AMgBQAGUAcgBmAGkAbAAgAEcAZQBuAOkAcgBpAGMAbwAgAGQAYQAgAEcAYQBtAGEAIABk +AGUAIABDAGkAbgB6AGEAcwAgADIALAAyBBcEMAQzBDAEOwRMBD0EMAAgAEcAcgBhAHkALQQzBDAEPAQw +ACAAMgAuADIAUAByAG8AZgBpAGwAIABnAOkAbgDpAHIAaQBxAHUAZQAgAGcAcgBpAHMAIABnAGEAbQBt +AGEAIAAyACwAMgDBAGwAdABhAGwA4QBuAG8AcwAgAHMAegD8AHIAawBlACAAZwBhAG0AbQBhACAAMgAu +ADKQGnUocHCWjlFJXqYAMgAuADKCcl9pY8+P8Md8vBgAINaMwMkAIKwQucgAIAAyAC4AMgAg1QS4XNMM +x3wARwBlAG4AZQByAGkAcwBrACAAZwByAOUAIABnAGEAbQBtAGEAIAAyACwAMgAtAHAAcgBvAGYAaQBs +AE8AYgBlAGMAbgDhACABYQBlAGQA4QAgAGcAYQBtAGEAIAAyAC4AMgXSBdAF3gXUACAF0AXkBdUF6AAg +BdsF3AXcBdkAIAAyAC4AMgBHAGEAbQBhACAAZwByAGkAIABnAGUAbgBlAHIAaQBjAQMAIAAyACwAMgBB +AGwAbABnAGUAbQBlAGkAbgBlAHMAIABHAHIAYQB1AHMAdAB1AGYAZQBuAC0AUAByAG8AZgBpAGwAIABH +AGEAbQBtAGEAIAAyACwAMgBQAHIAbwBmAGkAbABvACAAZwByAGkAZwBpAG8AIABnAGUAbgBlAHIAaQBj +AG8AIABkAGUAbABsAGEAIABnAGEAbQBtAGEAIAAyACwAMgBHAGUAbgBlAHIAaQBzAGsAIABnAHIA5QAg +ADIALAAyACAAZwBhAG0AbQBhAHAAcgBvAGYAaQBsZm6QGnBwXqZ8+2VwADIALgAyY8+P8GWHTvZOAIIs +MLAw7DCkMKww8zDeACAAMgAuADIAIDDXMO0w1TChMKQw6wOTA7UDvQO5A7oDzAAgA5MDugPBA7kAIAOT +A6wDvAO8A7EAIAAyAC4AMgBQAGUAcgBmAGkAbAAgAGcAZQBuAOkAcgBpAGMAbwAgAGQAZQAgAGMAaQBu +AHoAZQBuAHQAbwBzACAAZABhACAARwBhAG0AbQBhACAAMgAsADIAQQBsAGcAZQBtAGUAZQBuACAAZwBy +AGkAagBzACAAZwBhAG0AbQBhACAAMgAsADIALQBwAHIAbwBmAGkAZQBsAFAAZQByAGYAaQBsACAAZwBl +AG4A6QByAGkAYwBvACAAZABlACAAZwBhAG0AbQBhACAAZABlACAAZwByAGkAcwBlAHMAIAAyACwAMg4j +DjEOBw4qDjUOQQ4BDiEOIQ4yDkAOAQ4jDiIOTA4XDjEOSA4nDkQOGwAgADIALgAyAEcAZQBuAGUAbAAg +AEcAcgBpACAARwBhAG0AYQAgADIALAAyAFkAbABlAGkAbgBlAG4AIABoAGEAcgBtAGEAYQBuACAAZwBh +AG0AbQBhACAAMgAsADIAIAAtAHAAcgBvAGYAaQBpAGwAaQBHAGUAbgBlAHIAaQENAGsAaQAgAEcAcgBh +AHkAIABHAGEAbQBtAGEAIAAyAC4AMgAgAHAAcgBvAGYAaQBsAFUAbgBpAHcAZQByAHMAYQBsAG4AeQAg +AHAAcgBvAGYAaQBsACAAcwB6AGEAcgBvAVsAYwBpACAAZwBhAG0AbQBhACAAMgAsADIGOgYnBkUGJwAg +ADIALgAyACAGRAZIBkYAIAYxBkUGJwYvBkoAIAY5BicGRQQeBDEESQQwBE8AIARBBDUEQAQwBE8AIAQz +BDAEPAQ8BDAAIAAyACwAMgAtBD8EQAQ+BEQEOAQ7BEwARwBlAG4AZQByAGkAYwAgAEcAcgBhAHkAIABH +AGEAbQBtAGEAIAAyAC4AMgAgAFAAcgBvAGYAaQBsAGUAAHRleHQAAAAAQ29weXJpZ2h0IEFwcGxlIElu +Yy4sIDIwMTIAAFhZWiAAAAAAAADzUQABAAAAARbMY3VydgAAAAAAAAQAAAAABQAKAA8AFAAZAB4AIwAo +AC0AMgA3ADsAQABFAEoATwBUAFkAXgBjAGgAbQByAHcAfACBAIYAiwCQAJUAmgCfAKQAqQCuALIAtwC8 +AMEAxgDLANAA1QDbAOAA5QDrAPAA9gD7AQEBBwENARMBGQEfASUBKwEyATgBPgFFAUwBUgFZAWABZwFu +AXUBfAGDAYsBkgGaAaEBqQGxAbkBwQHJAdEB2QHhAekB8gH6AgMCDAIUAh0CJgIvAjgCQQJLAlQCXQJn +AnECegKEAo4CmAKiAqwCtgLBAssC1QLgAusC9QMAAwsDFgMhAy0DOANDA08DWgNmA3IDfgOKA5YDogOu +A7oDxwPTA+AD7AP5BAYEEwQgBC0EOwRIBFUEYwRxBH4EjASaBKgEtgTEBNME4QTwBP4FDQUcBSsFOgVJ +BVgFZwV3BYYFlgWmBbUFxQXVBeUF9gYGBhYGJwY3BkgGWQZqBnsGjAadBq8GwAbRBuMG9QcHBxkHKwc9 +B08HYQd0B4YHmQesB78H0gflB/gICwgfCDIIRghaCG4IggiWCKoIvgjSCOcI+wkQCSUJOglPCWQJeQmP +CaQJugnPCeUJ+woRCicKPQpUCmoKgQqYCq4KxQrcCvMLCwsiCzkLUQtpC4ALmAuwC8gL4Qv5DBIMKgxD +DFwMdQyODKcMwAzZDPMNDQ0mDUANWg10DY4NqQ3DDd4N+A4TDi4OSQ5kDn8Omw62DtIO7g8JDyUPQQ9e +D3oPlg+zD88P7BAJECYQQxBhEH4QmxC5ENcQ9RETETERTxFtEYwRqhHJEegSBxImEkUSZBKEEqMSwxLj +EwMTIxNDE2MTgxOkE8UT5RQGFCcUSRRqFIsUrRTOFPAVEhU0FVYVeBWbFb0V4BYDFiYWSRZsFo8WshbW +FvoXHRdBF2UXiReuF9IX9xgbGEAYZRiKGK8Y1Rj6GSAZRRlrGZEZtxndGgQaKhpRGncanhrFGuwbFBs7 +G2MbihuyG9ocAhwqHFIcexyjHMwc9R0eHUcdcB2ZHcMd7B4WHkAeah6UHr4e6R8THz4faR+UH78f6iAV +IEEgbCCYIMQg8CEcIUghdSGhIc4h+yInIlUigiKvIt0jCiM4I2YjlCPCI/AkHyRNJHwkqyTaJQklOCVo +JZclxyX3JicmVyaHJrcm6CcYJ0kneierJ9woDSg/KHEooijUKQYpOClrKZ0p0CoCKjUqaCqbKs8rAis2 +K2krnSvRLAUsOSxuLKIs1y0MLUEtdi2rLeEuFi5MLoIuty7uLyQvWi+RL8cv/jA1MGwwpDDbMRIxSjGC +Mbox8jIqMmMymzLUMw0zRjN/M7gz8TQrNGU0njTYNRM1TTWHNcI1/TY3NnI2rjbpNyQ3YDecN9c4FDhQ +OIw4yDkFOUI5fzm8Ofk6Njp0OrI67zstO2s7qjvoPCc8ZTykPOM9Ij1hPaE94D4gPmA+oD7gPyE/YT+i +P+JAI0BkQKZA50EpQWpBrEHuQjBCckK1QvdDOkN9Q8BEA0RHRIpEzkUSRVVFmkXeRiJGZ0arRvBHNUd7 +R8BIBUhLSJFI10kdSWNJqUnwSjdKfUrESwxLU0uaS+JMKkxyTLpNAk1KTZNN3E4lTm5Ot08AT0lPk0/d +UCdQcVC7UQZRUFGbUeZSMVJ8UsdTE1NfU6pT9lRCVI9U21UoVXVVwlYPVlxWqVb3V0RXklfgWC9YfVjL +WRpZaVm4WgdaVlqmWvVbRVuVW+VcNVyGXNZdJ114XcleGl5sXr1fD19hX7NgBWBXYKpg/GFPYaJh9WJJ +Ypxi8GNDY5dj62RAZJRk6WU9ZZJl52Y9ZpJm6Gc9Z5Nn6Wg/aJZo7GlDaZpp8WpIap9q92tPa6dr/2xX +bK9tCG1gbbluEm5rbsRvHm94b9FwK3CGcOBxOnGVcfByS3KmcwFzXXO4dBR0cHTMdSh1hXXhdj52m3b4 +d1Z3s3gReG54zHkqeYl553pGeqV7BHtje8J8IXyBfOF9QX2hfgF+Yn7CfyN/hH/lgEeAqIEKgWuBzYIw +gpKC9INXg7qEHYSAhOOFR4Wrhg6GcobXhzuHn4gEiGmIzokziZmJ/opkisqLMIuWi/yMY4zKjTGNmI3/ +jmaOzo82j56QBpBukNaRP5GokhGSepLjk02TtpQglIqU9JVflcmWNJaflwqXdZfgmEyYuJkkmZCZ/Jpo +mtWbQpuvnByciZz3nWSd0p5Anq6fHZ+Ln/qgaaDYoUehtqImopajBqN2o+akVqTHpTilqaYapoum/adu +p+CoUqjEqTepqaocqo+rAqt1q+msXKzQrUStuK4trqGvFq+LsACwdbDqsWCx1rJLssKzOLOutCW0nLUT +tYq2AbZ5tvC3aLfguFm40blKucK6O7q1uy67p7whvJu9Fb2Pvgq+hL7/v3q/9cBwwOzBZ8Hjwl/C28NY +w9TEUcTOxUvFyMZGxsPHQce/yD3IvMk6ybnKOMq3yzbLtsw1zLXNNc21zjbOts83z7jQOdC60TzRvtI/ +0sHTRNPG1EnUy9VO1dHWVdbY11zX4Nhk2OjZbNnx2nba+9uA3AXcit0Q3ZbeHN6i3ynfr+A24L3hROHM +4lPi2+Nj4+vkc+T85YTmDeaW5x/nqegy6LzpRunQ6lvq5etw6/vshu0R7ZzuKO6070DvzPBY8OXxcvH/ +8ozzGfOn9DT0wvVQ9d72bfb794r4Gfio+Tj5x/pX+uf7d/wH/Jj9Kf26/kv+3P9t///SLS4vMFokY2xh +c3NuYW1lWCRjbGFzc2VzXxAQTlNCaXRtYXBJbWFnZVJlcKMvMTJaTlNJbWFnZVJlcFhOU09iamVjdNIt +LjQ1V05TQXJyYXmiNDLSLS43OF5OU011dGFibGVBcnJheaM3NDLTOjsOPD0+V05TV2hpdGVcTlNDb2xv +clNwYWNlRDAgMAAQA4AM0i0uQEFXTlNDb2xvcqJAMtItLkNEV05TSW1hZ2WiQzIACAARABoAJAApADIA +NwBJAEwAUQBTAGIAaAB3AH4AhQCSAJkApQCtALwAvgDAAMUAxwDJAMsAzQDUANkA5ADmAOgA6gDvAPIA +9AD2APgA/wEWATIBNAE2E6YTqxO2E78T0hPWE+ET6hPvE/cT+hP/FA4UEhQZFCEULhQzFDUUNxQ8FEQU +RxRMFFQAAAAAAAACAQAAAAAAAABFAAAAAAAAAAAAAAAAAAAUVw diff --git a/i18n/src/main/resources/et.lproj/Command.xib b/i18n/src/main/resources/et.lproj/Command.xib index cd6ad07730..3f1b1aa837 100644 --- a/i18n/src/main/resources/et.lproj/Command.xib +++ b/i18n/src/main/resources/et.lproj/Command.xib @@ -1,7 +1,7 @@ - + - + diff --git a/i18n/src/main/resources/et.lproj/Connection.xib b/i18n/src/main/resources/et.lproj/Connection.xib index fd5048f622..91e71045ca 100644 --- a/i18n/src/main/resources/et.lproj/Connection.xib +++ b/i18n/src/main/resources/et.lproj/Connection.xib @@ -1,7 +1,7 @@ - + - + @@ -76,7 +76,7 @@ - + diff --git a/i18n/src/main/resources/et.lproj/Info.xib b/i18n/src/main/resources/et.lproj/Info.xib index ef94b0efdd..6da31d3d59 100644 --- a/i18n/src/main/resources/et.lproj/Info.xib +++ b/i18n/src/main/resources/et.lproj/Info.xib @@ -1,7 +1,7 @@ - + - + @@ -89,7 +89,7 @@ - + @@ -1017,21 +1017,21 @@ - + - + - + @@ -1121,21 +1121,21 @@ - + - + - + @@ -1250,27 +1250,27 @@ diff --git a/i18n/src/main/resources/et.lproj/Login.strings b/i18n/src/main/resources/et.lproj/Login.strings index b421ab45c4..9205eab3b9 100644 Binary files a/i18n/src/main/resources/et.lproj/Login.strings and b/i18n/src/main/resources/et.lproj/Login.strings differ diff --git a/i18n/src/main/resources/et.lproj/Main.xib b/i18n/src/main/resources/et.lproj/Main.xib index a64a066e22..0c6ce9aed2 100644 --- a/i18n/src/main/resources/et.lproj/Main.xib +++ b/i18n/src/main/resources/et.lproj/Main.xib @@ -1,7 +1,7 @@ - + - + @@ -94,7 +94,7 @@ - + @@ -159,19 +159,19 @@ - + - + - + @@ -321,12 +321,12 @@ CA - + - + @@ -566,6 +566,10 @@ CA - + + + + + diff --git a/i18n/src/main/resources/et.lproj/Preferences.xib b/i18n/src/main/resources/et.lproj/Preferences.xib index 216636f73f..2cad4892ec 100644 --- a/i18n/src/main/resources/et.lproj/Preferences.xib +++ b/i18n/src/main/resources/et.lproj/Preferences.xib @@ -1,7 +1,7 @@ - + - + @@ -266,7 +266,7 @@ - + @@ -307,7 +307,7 @@ - + @@ -538,7 +538,7 @@ - + @@ -578,7 +578,7 @@ - + @@ -613,7 +613,7 @@ - + @@ -642,7 +642,7 @@ - + @@ -1218,7 +1218,7 @@ - + @@ -1250,7 +1250,7 @@ - + @@ -1290,7 +1290,7 @@ - + @@ -1363,7 +1363,7 @@ - + @@ -1558,7 +1558,7 @@ - + @@ -1618,7 +1618,7 @@ - + @@ -1643,7 +1643,7 @@ - + @@ -1685,7 +1685,7 @@ - + @@ -1713,7 +1713,7 @@ - + @@ -1741,7 +1741,7 @@ - + @@ -1770,7 +1770,7 @@ - + @@ -1805,7 +1805,7 @@ - + @@ -1888,7 +1888,7 @@ - + @@ -1947,7 +1947,7 @@ - + @@ -1975,7 +1975,7 @@ - + @@ -2004,7 +2004,7 @@ - + diff --git a/i18n/src/main/resources/et.lproj/Profiles.xib b/i18n/src/main/resources/et.lproj/Profiles.xib index 18af5a58f4..20e5ce0f36 100644 --- a/i18n/src/main/resources/et.lproj/Profiles.xib +++ b/i18n/src/main/resources/et.lproj/Profiles.xib @@ -1,7 +1,7 @@ - + - + diff --git a/i18n/src/main/resources/et.lproj/Prompt.xib b/i18n/src/main/resources/et.lproj/Prompt.xib index f6de0241c7..5b5993e53b 100644 --- a/i18n/src/main/resources/et.lproj/Prompt.xib +++ b/i18n/src/main/resources/et.lproj/Prompt.xib @@ -1,7 +1,7 @@ - + - + @@ -95,7 +95,7 @@ Gw - + diff --git a/i18n/src/main/resources/et.lproj/S3.strings b/i18n/src/main/resources/et.lproj/S3.strings index b6c13a3ba9..bade4db501 100644 Binary files a/i18n/src/main/resources/et.lproj/S3.strings and b/i18n/src/main/resources/et.lproj/S3.strings differ diff --git a/i18n/src/main/resources/et.lproj/Updater.strings b/i18n/src/main/resources/et.lproj/Updater.strings index 959c8c18fe..fd1766de20 100644 Binary files a/i18n/src/main/resources/et.lproj/Updater.strings and b/i18n/src/main/resources/et.lproj/Updater.strings differ diff --git a/i18n/src/main/resources/fi.lproj/Bookmark.xib b/i18n/src/main/resources/fi.lproj/Bookmark.xib index 820971bbb0..37bb4fada0 100644 --- a/i18n/src/main/resources/fi.lproj/Bookmark.xib +++ b/i18n/src/main/resources/fi.lproj/Bookmark.xib @@ -1,7 +1,7 @@ - + - + @@ -92,7 +92,7 @@ - + @@ -489,95 +489,95 @@ YnBsaXN0MDDUAQIDBAUGBwpYJHZlcnNpb25ZJGFyY2hpdmVyVCR0b3BYJG9iamVjdHMSAAGGoF8QD05T -S2V5ZWRBcmNoaXZlctEICVRyb290gAGuCwwZGh8UJCkqMTQ3PUBVJG51bGzWDQ4PEBESExQVFhcYVk5T -U2l6ZV5OU1Jlc2l6aW5nTW9kZVYkY2xhc3NcTlNJbWFnZUZsYWdzVk5TUmVwc1dOU0NvbG9ygAIQAIAN -EiDDAACAA4ALVnsxLCAxfdIbDxweWk5TLm9iamVjdHOhHYAEgArSGw8gI6IhIoAFgAaACdMPJSYnKBRf -EBROU1RJRkZSZXByZXNlbnRhdGlvbl8QGU5TSW50ZXJuYWxMYXlvdXREaXJlY3Rpb26ACIAHTxESbE1N -ACoAAAAKAAAAEAEAAAMAAAABAAEAAAEBAAMAAAABAAEAAAECAAMAAAACAAgACAEDAAMAAAABAAEAAAEG -AAMAAAABAAEAAAEKAAMAAAABAAEAAAERAAQAAAABAAAACAESAAMAAAABAAEAAAEVAAMAAAABAAIAAAEW -AAMAAAABAAEAAAEXAAQAAAABAAAAAgEcAAMAAAABAAEAAAEoAAMAAAABAAIAAAFSAAMAAAABAAEAAAFT -AAMAAAACAAEAAYdzAAcAABGcAAAA0AAAAAAAABGcYXBwbAIAAABtbnRyR1JBWVhZWiAH3AAIABcADwAu -AA9hY3NwQVBQTAAAAABub25lAAAAAAAAAAAAAAAAAAAAAAAA9tYAAQAAAADTLWFwcGwAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAVkZXNjAAAAwAAAAHlkc2NtAAABPAAA -CBpjcHJ0AAAJWAAAACN3dHB0AAAJfAAAABRrVFJDAAAJkAAACAxkZXNjAAAAAAAAAB9HZW5lcmljIEdy -YXkgR2FtbWEgMi4yIFByb2ZpbGUAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAbWx1YwAAAAAAAAAfAAAADHNr -U0sAAAAuAAABhGRhREsAAAA6AAABsmNhRVMAAAA4AAAB7HZpVk4AAABAAAACJHB0QlIAAABKAAACZHVr -VUEAAAAsAAACrmZyRlUAAAA+AAAC2mh1SFUAAAA0AAADGHpoVFcAAAAaAAADTGtvS1IAAAAiAAADZm5i -Tk8AAAA6AAADiGNzQ1oAAAAoAAADwmhlSUwAAAAkAAAD6nJvUk8AAAAqAAAEDmRlREUAAABOAAAEOGl0 -SVQAAABOAAAEhnN2U0UAAAA4AAAE1HpoQ04AAAAaAAAFDGphSlAAAAAmAAAFJmVsR1IAAAAqAAAFTHB0 -UE8AAABSAAAFdm5sTkwAAABAAAAFyGVzRVMAAABMAAAGCHRoVEgAAAAyAAAGVHRyVFIAAAAkAAAGhmZp -RkkAAABGAAAGqmhySFIAAAA+AAAG8HBsUEwAAABKAAAHLmFyRUcAAAAsAAAHeHJ1UlUAAAA6AAAHpGVu -VVMAAAA8AAAH3gBWAWEAZQBvAGIAZQBjAG4A4QAgAHMAaQB2AOEAIABnAGEAbQBhACAAMgAsADIARwBl -AG4AZQByAGkAcwBrACAAZwByAOUAIAAyACwAMgAgAGcAYQBtAG0AYQAtAHAAcgBvAGYAaQBsAEcAYQBt -AG0AYQAgAGQAZQAgAGcAcgBpAHMAbwBzACAAZwBlAG4A6AByAGkAYwBhACAAMgAuADIAQx6lAHUAIABo -AOwAbgBoACAATQDgAHUAIAB4AOEAbQAgAEMAaAB1AG4AZwAgAEcAYQBtAG0AYQAgADIALgAyAFAAZQBy -AGYAaQBsACAARwBlAG4A6QByAGkAYwBvACAAZABhACAARwBhAG0AYQAgAGQAZQAgAEMAaQBuAHoAYQBz -ACAAMgAsADIEFwQwBDMEMAQ7BEwEPQQwACAARwByAGEAeQAtBDMEMAQ8BDAAIAAyAC4AMgBQAHIAbwBm -AGkAbAAgAGcA6QBuAOkAcgBpAHEAdQBlACAAZwByAGkAcwAgAGcAYQBtAG0AYQAgADIALAAyAMEAbAB0 -AGEAbADhAG4AbwBzACAAcwB6APwAcgBrAGUAIABnAGEAbQBtAGEAIAAyAC4AMpAadShwcJaOUUlepgAy -AC4AMoJyX2ljz4/wx3y8GAAg1ozAyQAgrBC5yAAgADIALgAyACDVBLhc0wzHfABHAGUAbgBlAHIAaQBz -AGsAIABnAHIA5QAgAGcAYQBtAG0AYQAgADIALAAyAC0AcAByAG8AZgBpAGwATwBiAGUAYwBuAOEAIAFh -AGUAZADhACAAZwBhAG0AYQAgADIALgAyBdIF0AXeBdQAIAXQBeQF1QXoACAF2wXcBdwF2QAgADIALgAy -AEcAYQBtAGEAIABnAHIAaQAgAGcAZQBuAGUAcgBpAGMBAwAgADIALAAyAEEAbABsAGcAZQBtAGUAaQBu -AGUAcwAgAEcAcgBhAHUAcwB0AHUAZgBlAG4ALQBQAHIAbwBmAGkAbAAgAEcAYQBtAG0AYQAgADIALAAy -AFAAcgBvAGYAaQBsAG8AIABnAHIAaQBnAGkAbwAgAGcAZQBuAGUAcgBpAGMAbwAgAGQAZQBsAGwAYQAg -AGcAYQBtAG0AYQAgADIALAAyAEcAZQBuAGUAcgBpAHMAawAgAGcAcgDlACAAMgAsADIAIABnAGEAbQBt -AGEAcAByAG8AZgBpAGxmbpAacHBepnz7ZXAAMgAuADJjz4/wZYdO9k4AgiwwsDDsMKQwrDDzMN4AIAAy -AC4AMgAgMNcw7TDVMKEwpDDrA5MDtQO9A7kDugPMACADkwO6A8EDuQAgA5MDrAO8A7wDsQAgADIALgAy -AFAAZQByAGYAaQBsACAAZwBlAG4A6QByAGkAYwBvACAAZABlACAAYwBpAG4AegBlAG4AdABvAHMAIABk -AGEAIABHAGEAbQBtAGEAIAAyACwAMgBBAGwAZwBlAG0AZQBlAG4AIABnAHIAaQBqAHMAIABnAGEAbQBt -AGEAIAAyACwAMgAtAHAAcgBvAGYAaQBlAGwAUABlAHIAZgBpAGwAIABnAGUAbgDpAHIAaQBjAG8AIABk -AGUAIABnAGEAbQBtAGEAIABkAGUAIABnAHIAaQBzAGUAcwAgADIALAAyDiMOMQ4HDioONQ5BDgEOIQ4h -DjIOQA4BDiMOIg5MDhcOMQ5IDicORA4bACAAMgAuADIARwBlAG4AZQBsACAARwByAGkAIABHAGEAbQBh -ACAAMgAsADIAWQBsAGUAaQBuAGUAbgAgAGgAYQByAG0AYQBhAG4AIABnAGEAbQBtAGEAIAAyACwAMgAg -AC0AcAByAG8AZgBpAGkAbABpAEcAZQBuAGUAcgBpAQ0AawBpACAARwByAGEAeQAgAEcAYQBtAG0AYQAg -ADIALgAyACAAcAByAG8AZgBpAGwAVQBuAGkAdwBlAHIAcwBhAGwAbgB5ACAAcAByAG8AZgBpAGwAIABz -AHoAYQByAG8BWwBjAGkAIABnAGEAbQBtAGEAIAAyACwAMgY6BicGRQYnACAAMgAuADIAIAZEBkgGRgAg -BjEGRQYnBi8GSgAgBjkGJwZFBB4EMQRJBDAETwAgBEEENQRABDAETwAgBDMEMAQ8BDwEMAAgADIALAAy -AC0EPwRABD4ERAQ4BDsETABHAGUAbgBlAHIAaQBjACAARwByAGEAeQAgAEcAYQBtAG0AYQAgADIALgAy -ACAAUAByAG8AZgBpAGwAZQAAdGV4dAAAAABDb3B5cmlnaHQgQXBwbGUgSW5jLiwgMjAxMgAAWFlaIAAA -AAAAAPNRAAEAAAABFsxjdXJ2AAAAAAAABAAAAAAFAAoADwAUABkAHgAjACgALQAyADcAOwBAAEUASgBP -AFQAWQBeAGMAaABtAHIAdwB8AIEAhgCLAJAAlQCaAJ8ApACpAK4AsgC3ALwAwQDGAMsA0ADVANsA4ADl -AOsA8AD2APsBAQEHAQ0BEwEZAR8BJQErATIBOAE+AUUBTAFSAVkBYAFnAW4BdQF8AYMBiwGSAZoBoQGp -AbEBuQHBAckB0QHZAeEB6QHyAfoCAwIMAhQCHQImAi8COAJBAksCVAJdAmcCcQJ6AoQCjgKYAqICrAK2 -AsECywLVAuAC6wL1AwADCwMWAyEDLQM4A0MDTwNaA2YDcgN+A4oDlgOiA64DugPHA9MD4APsA/kEBgQT -BCAELQQ7BEgEVQRjBHEEfgSMBJoEqAS2BMQE0wThBPAE/gUNBRwFKwU6BUkFWAVnBXcFhgWWBaYFtQXF -BdUF5QX2BgYGFgYnBjcGSAZZBmoGewaMBp0GrwbABtEG4wb1BwcHGQcrBz0HTwdhB3QHhgeZB6wHvwfS -B+UH+AgLCB8IMghGCFoIbgiCCJYIqgi+CNII5wj7CRAJJQk6CU8JZAl5CY8JpAm6Cc8J5Qn7ChEKJwo9 -ClQKagqBCpgKrgrFCtwK8wsLCyILOQtRC2kLgAuYC7ALyAvhC/kMEgwqDEMMXAx1DI4MpwzADNkM8w0N -DSYNQA1aDXQNjg2pDcMN3g34DhMOLg5JDmQOfw6bDrYO0g7uDwkPJQ9BD14Peg+WD7MPzw/sEAkQJhBD -EGEQfhCbELkQ1xD1ERMRMRFPEW0RjBGqEckR6BIHEiYSRRJkEoQSoxLDEuMTAxMjE0MTYxODE6QTxRPl -FAYUJxRJFGoUixStFM4U8BUSFTQVVhV4FZsVvRXgFgMWJhZJFmwWjxayFtYW+hcdF0EXZReJF64X0hf3 -GBsYQBhlGIoYrxjVGPoZIBlFGWsZkRm3Gd0aBBoqGlEadxqeGsUa7BsUGzsbYxuKG7Ib2hwCHCocUhx7 -HKMczBz1HR4dRx1wHZkdwx3sHhYeQB5qHpQevh7pHxMfPh9pH5Qfvx/qIBUgQSBsIJggxCDwIRwhSCF1 -IaEhziH7IiciVSKCIq8i3SMKIzgjZiOUI8Ij8CQfJE0kfCSrJNolCSU4JWgllyXHJfcmJyZXJocmtybo -JxgnSSd6J6sn3CgNKD8ocSiiKNQpBik4KWspnSnQKgIqNSpoKpsqzysCKzYraSudK9EsBSw5LG4soizX -LQwtQS12Last4S4WLkwugi63Lu4vJC9aL5Evxy/+MDUwbDCkMNsxEjFKMYIxujHyMioyYzKbMtQzDTNG -M38zuDPxNCs0ZTSeNNg1EzVNNYc1wjX9Njc2cjauNuk3JDdgN5w31zgUOFA4jDjIOQU5Qjl/Obw5+To2 -OnQ6sjrvOy07azuqO+g8JzxlPKQ84z0iPWE9oT3gPiA+YD6gPuA/IT9hP6I/4kAjQGRApkDnQSlBakGs -Qe5CMEJyQrVC90M6Q31DwEQDREdEikTORRJFVUWaRd5GIkZnRqtG8Ec1R3tHwEgFSEtIkUjXSR1JY0mp -SfBKN0p9SsRLDEtTS5pL4kwqTHJMuk0CTUpNk03cTiVObk63TwBPSU+TT91QJ1BxULtRBlFQUZtR5lIx -UnxSx1MTU19TqlP2VEJUj1TbVShVdVXCVg9WXFapVvdXRFeSV+BYL1h9WMtZGllpWbhaB1pWWqZa9VtF -W5Vb5Vw1XIZc1l0nXXhdyV4aXmxevV8PX2Ffs2AFYFdgqmD8YU9homH1YklinGLwY0Njl2PrZEBklGTp -ZT1lkmXnZj1mkmboZz1nk2fpaD9olmjsaUNpmmnxakhqn2r3a09rp2v/bFdsr20IbWBtuW4SbmtuxG8e -b3hv0XArcIZw4HE6cZVx8HJLcqZzAXNdc7h0FHRwdMx1KHWFdeF2Pnabdvh3VnezeBF4bnjMeSp5iXnn -ekZ6pXsEe2N7wnwhfIF84X1BfaF+AX5ifsJ/I3+Ef+WAR4CogQqBa4HNgjCCkoL0g1eDuoQdhICE44VH -hauGDoZyhteHO4efiASIaYjOiTOJmYn+imSKyoswi5aL/IxjjMqNMY2Yjf+OZo7OjzaPnpAGkG6Q1pE/ -kaiSEZJ6kuOTTZO2lCCUipT0lV+VyZY0lp+XCpd1l+CYTJi4mSSZkJn8mmia1ZtCm6+cHJyJnPedZJ3S -nkCerp8dn4uf+qBpoNihR6G2oiailqMGo3aj5qRWpMelOKWpphqmi6b9p26n4KhSqMSpN6mpqhyqj6sC -q3Wr6axcrNCtRK24ri2uoa8Wr4uwALB1sOqxYLHWskuywrM4s660JbSctRO1irYBtnm28Ldot+C4WbjR -uUq5wro7urW7LrunvCG8m70VvY++Cr6Evv+/er/1wHDA7MFnwePCX8Lbw1jD1MRRxM7FS8XIxkbGw8dB -x7/IPci8yTrJuco4yrfLNsu2zDXMtc01zbXONs62zzfPuNA50LrRPNG+0j/SwdNE08bUSdTL1U7V0dZV -1tjXXNfg2GTY6Nls2fHadtr724DcBdyK3RDdlt4c3qLfKd+v4DbgveFE4cziU+Lb42Pj6+Rz5PzlhOYN -5pbnH+ep6DLovOlG6dDqW+rl63Dr++yG7RHtnO4o7rTvQO/M8Fjw5fFy8f/yjPMZ86f0NPTC9VD13vZt -9vv3ivgZ+Kj5OPnH+lf65/t3/Af8mP0p/br+S/7c/23//9IrLC0uWiRjbGFzc25hbWVYJGNsYXNzZXNf -EBBOU0JpdG1hcEltYWdlUmVwoy0vMFpOU0ltYWdlUmVwWE5TT2JqZWN00issMjNXTlNBcnJheaIyMNIr -LDU2Xk5TTXV0YWJsZUFycmF5ozUyMNM4OQ86OzxXTlNXaGl0ZVxOU0NvbG9yU3BhY2VEMCAwABADgAzS -Kyw+P1dOU0NvbG9yoj4w0issQUJXTlNJbWFnZaJBMAAIABEAGgAkACkAMgA3AEkATABRAFMAYgBoAHUA -fACLAJIAnwCmAK4AsACyALQAuQC7AL0AxADJANQA1gDYANoA3wDiAOQA5gDoAO8BBgEiASQBJhOWE5sT -phOvE8ITxhPRE9oT3xPnE+oT7xP+FAIUCRQRFB4UIxQlFCcULBQ0FDcUPBREAAAAAAAAAgEAAAAAAAAA -QwAAAAAAAAAAAAAAAAAAFEc +S2V5ZWRBcmNoaXZlctEICVRyb290gAGuCwwbHCEaJissMzY5P0JVJG51bGzXDQ4PEBESExQVFhcYGRpW +TlNTaXplViRjbGFzc1xOU0ltYWdlRmxhZ3NWTlNSZXBzW05TVGludENvbG9yV05TQ29sb3JeTlNSZXNp +emluZ01vZGWAAoANEiDDAACAA4AAgAsQAFZ7MSwgMX3SHQ4eIFpOUy5vYmplY3RzoR+ABIAK0h0OIiWi +IySABYAGgAnTDicoKSoaXxAUTlNUSUZGUmVwcmVzZW50YXRpb25fEBlOU0ludGVybmFsTGF5b3V0RGly +ZWN0aW9ugAiAB08REmxNTQAqAAAACgAAABABAAADAAAAAQABAAABAQADAAAAAQABAAABAgADAAAAAgAI +AAgBAwADAAAAAQABAAABBgADAAAAAQABAAABCgADAAAAAQABAAABEQAEAAAAAQAAAAgBEgADAAAAAQAB +AAABFQADAAAAAQACAAABFgADAAAAAQABAAABFwAEAAAAAQAAAAIBHAADAAAAAQABAAABKAADAAAAAQAC +AAABUgADAAAAAQABAAABUwADAAAAAgABAAGHcwAHAAARnAAAANAAAAAAAAARnGFwcGwCAAAAbW50ckdS +QVlYWVogB9wACAAXAA8ALgAPYWNzcEFQUEwAAAAAbm9uZQAAAAAAAAAAAAAAAAAAAAAAAPbWAAEAAAAA +0y1hcHBsAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFZGVzYwAA +AMAAAAB5ZHNjbQAAATwAAAgaY3BydAAACVgAAAAjd3RwdAAACXwAAAAUa1RSQwAACZAAAAgMZGVzYwAA +AAAAAAAfR2VuZXJpYyBHcmF5IEdhbW1hIDIuMiBQcm9maWxlAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAG1s +dWMAAAAAAAAAHwAAAAxza1NLAAAALgAAAYRkYURLAAAAOgAAAbJjYUVTAAAAOAAAAex2aVZOAAAAQAAA +AiRwdEJSAAAASgAAAmR1a1VBAAAALAAAAq5mckZVAAAAPgAAAtpodUhVAAAANAAAAxh6aFRXAAAAGgAA +A0xrb0tSAAAAIgAAA2ZuYk5PAAAAOgAAA4hjc0NaAAAAKAAAA8JoZUlMAAAAJAAAA+pyb1JPAAAAKgAA +BA5kZURFAAAATgAABDhpdElUAAAATgAABIZzdlNFAAAAOAAABNR6aENOAAAAGgAABQxqYUpQAAAAJgAA +BSZlbEdSAAAAKgAABUxwdFBPAAAAUgAABXZubE5MAAAAQAAABchlc0VTAAAATAAABgh0aFRIAAAAMgAA +BlR0clRSAAAAJAAABoZmaUZJAAAARgAABqpockhSAAAAPgAABvBwbFBMAAAASgAABy5hckVHAAAALAAA +B3hydVJVAAAAOgAAB6RlblVTAAAAPAAAB94AVgFhAGUAbwBiAGUAYwBuAOEAIABzAGkAdgDhACAAZwBh +AG0AYQAgADIALAAyAEcAZQBuAGUAcgBpAHMAawAgAGcAcgDlACAAMgAsADIAIABnAGEAbQBtAGEALQBw +AHIAbwBmAGkAbABHAGEAbQBtAGEAIABkAGUAIABnAHIAaQBzAG8AcwAgAGcAZQBuAOgAcgBpAGMAYQAg +ADIALgAyAEMepQB1ACAAaADsAG4AaAAgAE0A4AB1ACAAeADhAG0AIABDAGgAdQBuAGcAIABHAGEAbQBt +AGEAIAAyAC4AMgBQAGUAcgBmAGkAbAAgAEcAZQBuAOkAcgBpAGMAbwAgAGQAYQAgAEcAYQBtAGEAIABk +AGUAIABDAGkAbgB6AGEAcwAgADIALAAyBBcEMAQzBDAEOwRMBD0EMAAgAEcAcgBhAHkALQQzBDAEPAQw +ACAAMgAuADIAUAByAG8AZgBpAGwAIABnAOkAbgDpAHIAaQBxAHUAZQAgAGcAcgBpAHMAIABnAGEAbQBt +AGEAIAAyACwAMgDBAGwAdABhAGwA4QBuAG8AcwAgAHMAegD8AHIAawBlACAAZwBhAG0AbQBhACAAMgAu +ADKQGnUocHCWjlFJXqYAMgAuADKCcl9pY8+P8Md8vBgAINaMwMkAIKwQucgAIAAyAC4AMgAg1QS4XNMM +x3wARwBlAG4AZQByAGkAcwBrACAAZwByAOUAIABnAGEAbQBtAGEAIAAyACwAMgAtAHAAcgBvAGYAaQBs +AE8AYgBlAGMAbgDhACABYQBlAGQA4QAgAGcAYQBtAGEAIAAyAC4AMgXSBdAF3gXUACAF0AXkBdUF6AAg +BdsF3AXcBdkAIAAyAC4AMgBHAGEAbQBhACAAZwByAGkAIABnAGUAbgBlAHIAaQBjAQMAIAAyACwAMgBB +AGwAbABnAGUAbQBlAGkAbgBlAHMAIABHAHIAYQB1AHMAdAB1AGYAZQBuAC0AUAByAG8AZgBpAGwAIABH +AGEAbQBtAGEAIAAyACwAMgBQAHIAbwBmAGkAbABvACAAZwByAGkAZwBpAG8AIABnAGUAbgBlAHIAaQBj +AG8AIABkAGUAbABsAGEAIABnAGEAbQBtAGEAIAAyACwAMgBHAGUAbgBlAHIAaQBzAGsAIABnAHIA5QAg +ADIALAAyACAAZwBhAG0AbQBhAHAAcgBvAGYAaQBsZm6QGnBwXqZ8+2VwADIALgAyY8+P8GWHTvZOAIIs +MLAw7DCkMKww8zDeACAAMgAuADIAIDDXMO0w1TChMKQw6wOTA7UDvQO5A7oDzAAgA5MDugPBA7kAIAOT +A6wDvAO8A7EAIAAyAC4AMgBQAGUAcgBmAGkAbAAgAGcAZQBuAOkAcgBpAGMAbwAgAGQAZQAgAGMAaQBu +AHoAZQBuAHQAbwBzACAAZABhACAARwBhAG0AbQBhACAAMgAsADIAQQBsAGcAZQBtAGUAZQBuACAAZwBy +AGkAagBzACAAZwBhAG0AbQBhACAAMgAsADIALQBwAHIAbwBmAGkAZQBsAFAAZQByAGYAaQBsACAAZwBl +AG4A6QByAGkAYwBvACAAZABlACAAZwBhAG0AbQBhACAAZABlACAAZwByAGkAcwBlAHMAIAAyACwAMg4j +DjEOBw4qDjUOQQ4BDiEOIQ4yDkAOAQ4jDiIOTA4XDjEOSA4nDkQOGwAgADIALgAyAEcAZQBuAGUAbAAg +AEcAcgBpACAARwBhAG0AYQAgADIALAAyAFkAbABlAGkAbgBlAG4AIABoAGEAcgBtAGEAYQBuACAAZwBh +AG0AbQBhACAAMgAsADIAIAAtAHAAcgBvAGYAaQBpAGwAaQBHAGUAbgBlAHIAaQENAGsAaQAgAEcAcgBh +AHkAIABHAGEAbQBtAGEAIAAyAC4AMgAgAHAAcgBvAGYAaQBsAFUAbgBpAHcAZQByAHMAYQBsAG4AeQAg +AHAAcgBvAGYAaQBsACAAcwB6AGEAcgBvAVsAYwBpACAAZwBhAG0AbQBhACAAMgAsADIGOgYnBkUGJwAg +ADIALgAyACAGRAZIBkYAIAYxBkUGJwYvBkoAIAY5BicGRQQeBDEESQQwBE8AIARBBDUEQAQwBE8AIAQz +BDAEPAQ8BDAAIAAyACwAMgAtBD8EQAQ+BEQEOAQ7BEwARwBlAG4AZQByAGkAYwAgAEcAcgBhAHkAIABH +AGEAbQBtAGEAIAAyAC4AMgAgAFAAcgBvAGYAaQBsAGUAAHRleHQAAAAAQ29weXJpZ2h0IEFwcGxlIElu +Yy4sIDIwMTIAAFhZWiAAAAAAAADzUQABAAAAARbMY3VydgAAAAAAAAQAAAAABQAKAA8AFAAZAB4AIwAo +AC0AMgA3ADsAQABFAEoATwBUAFkAXgBjAGgAbQByAHcAfACBAIYAiwCQAJUAmgCfAKQAqQCuALIAtwC8 +AMEAxgDLANAA1QDbAOAA5QDrAPAA9gD7AQEBBwENARMBGQEfASUBKwEyATgBPgFFAUwBUgFZAWABZwFu +AXUBfAGDAYsBkgGaAaEBqQGxAbkBwQHJAdEB2QHhAekB8gH6AgMCDAIUAh0CJgIvAjgCQQJLAlQCXQJn +AnECegKEAo4CmAKiAqwCtgLBAssC1QLgAusC9QMAAwsDFgMhAy0DOANDA08DWgNmA3IDfgOKA5YDogOu +A7oDxwPTA+AD7AP5BAYEEwQgBC0EOwRIBFUEYwRxBH4EjASaBKgEtgTEBNME4QTwBP4FDQUcBSsFOgVJ +BVgFZwV3BYYFlgWmBbUFxQXVBeUF9gYGBhYGJwY3BkgGWQZqBnsGjAadBq8GwAbRBuMG9QcHBxkHKwc9 +B08HYQd0B4YHmQesB78H0gflB/gICwgfCDIIRghaCG4IggiWCKoIvgjSCOcI+wkQCSUJOglPCWQJeQmP +CaQJugnPCeUJ+woRCicKPQpUCmoKgQqYCq4KxQrcCvMLCwsiCzkLUQtpC4ALmAuwC8gL4Qv5DBIMKgxD +DFwMdQyODKcMwAzZDPMNDQ0mDUANWg10DY4NqQ3DDd4N+A4TDi4OSQ5kDn8Omw62DtIO7g8JDyUPQQ9e +D3oPlg+zD88P7BAJECYQQxBhEH4QmxC5ENcQ9RETETERTxFtEYwRqhHJEegSBxImEkUSZBKEEqMSwxLj +EwMTIxNDE2MTgxOkE8UT5RQGFCcUSRRqFIsUrRTOFPAVEhU0FVYVeBWbFb0V4BYDFiYWSRZsFo8WshbW +FvoXHRdBF2UXiReuF9IX9xgbGEAYZRiKGK8Y1Rj6GSAZRRlrGZEZtxndGgQaKhpRGncanhrFGuwbFBs7 +G2MbihuyG9ocAhwqHFIcexyjHMwc9R0eHUcdcB2ZHcMd7B4WHkAeah6UHr4e6R8THz4faR+UH78f6iAV +IEEgbCCYIMQg8CEcIUghdSGhIc4h+yInIlUigiKvIt0jCiM4I2YjlCPCI/AkHyRNJHwkqyTaJQklOCVo +JZclxyX3JicmVyaHJrcm6CcYJ0kneierJ9woDSg/KHEooijUKQYpOClrKZ0p0CoCKjUqaCqbKs8rAis2 +K2krnSvRLAUsOSxuLKIs1y0MLUEtdi2rLeEuFi5MLoIuty7uLyQvWi+RL8cv/jA1MGwwpDDbMRIxSjGC +Mbox8jIqMmMymzLUMw0zRjN/M7gz8TQrNGU0njTYNRM1TTWHNcI1/TY3NnI2rjbpNyQ3YDecN9c4FDhQ +OIw4yDkFOUI5fzm8Ofk6Njp0OrI67zstO2s7qjvoPCc8ZTykPOM9Ij1hPaE94D4gPmA+oD7gPyE/YT+i +P+JAI0BkQKZA50EpQWpBrEHuQjBCckK1QvdDOkN9Q8BEA0RHRIpEzkUSRVVFmkXeRiJGZ0arRvBHNUd7 +R8BIBUhLSJFI10kdSWNJqUnwSjdKfUrESwxLU0uaS+JMKkxyTLpNAk1KTZNN3E4lTm5Ot08AT0lPk0/d +UCdQcVC7UQZRUFGbUeZSMVJ8UsdTE1NfU6pT9lRCVI9U21UoVXVVwlYPVlxWqVb3V0RXklfgWC9YfVjL +WRpZaVm4WgdaVlqmWvVbRVuVW+VcNVyGXNZdJ114XcleGl5sXr1fD19hX7NgBWBXYKpg/GFPYaJh9WJJ +Ypxi8GNDY5dj62RAZJRk6WU9ZZJl52Y9ZpJm6Gc9Z5Nn6Wg/aJZo7GlDaZpp8WpIap9q92tPa6dr/2xX +bK9tCG1gbbluEm5rbsRvHm94b9FwK3CGcOBxOnGVcfByS3KmcwFzXXO4dBR0cHTMdSh1hXXhdj52m3b4 +d1Z3s3gReG54zHkqeYl553pGeqV7BHtje8J8IXyBfOF9QX2hfgF+Yn7CfyN/hH/lgEeAqIEKgWuBzYIw +gpKC9INXg7qEHYSAhOOFR4Wrhg6GcobXhzuHn4gEiGmIzokziZmJ/opkisqLMIuWi/yMY4zKjTGNmI3/ +jmaOzo82j56QBpBukNaRP5GokhGSepLjk02TtpQglIqU9JVflcmWNJaflwqXdZfgmEyYuJkkmZCZ/Jpo +mtWbQpuvnByciZz3nWSd0p5Anq6fHZ+Ln/qgaaDYoUehtqImopajBqN2o+akVqTHpTilqaYapoum/adu +p+CoUqjEqTepqaocqo+rAqt1q+msXKzQrUStuK4trqGvFq+LsACwdbDqsWCx1rJLssKzOLOutCW0nLUT +tYq2AbZ5tvC3aLfguFm40blKucK6O7q1uy67p7whvJu9Fb2Pvgq+hL7/v3q/9cBwwOzBZ8Hjwl/C28NY +w9TEUcTOxUvFyMZGxsPHQce/yD3IvMk6ybnKOMq3yzbLtsw1zLXNNc21zjbOts83z7jQOdC60TzRvtI/ +0sHTRNPG1EnUy9VO1dHWVdbY11zX4Nhk2OjZbNnx2nba+9uA3AXcit0Q3ZbeHN6i3ynfr+A24L3hROHM +4lPi2+Nj4+vkc+T85YTmDeaW5x/nqegy6LzpRunQ6lvq5etw6/vshu0R7ZzuKO6070DvzPBY8OXxcvH/ +8ozzGfOn9DT0wvVQ9d72bfb794r4Gfio+Tj5x/pX+uf7d/wH/Jj9Kf26/kv+3P9t///SLS4vMFokY2xh +c3NuYW1lWCRjbGFzc2VzXxAQTlNCaXRtYXBJbWFnZVJlcKMvMTJaTlNJbWFnZVJlcFhOU09iamVjdNIt +LjQ1V05TQXJyYXmiNDLSLS43OF5OU011dGFibGVBcnJheaM3NDLTOjsOPD0+V05TV2hpdGVcTlNDb2xv +clNwYWNlRDAgMAAQA4AM0i0uQEFXTlNDb2xvcqJAMtItLkNEV05TSW1hZ2WiQzIACAARABoAJAApADIA +NwBJAEwAUQBTAGIAaAB3AH4AhQCSAJkApQCtALwAvgDAAMUAxwDJAMsAzQDUANkA5ADmAOgA6gDvAPIA +9AD2APgA/wEWATIBNAE2E6YTqxO2E78T0hPWE+ET6hPvE/cT+hP/FA4UEhQZFCEULhQzFDUUNxQ8FEQU +RxRMFFQAAAAAAAACAQAAAAAAAABFAAAAAAAAAAAAAAAAAAAUVw diff --git a/i18n/src/main/resources/fi.lproj/Browser.xib b/i18n/src/main/resources/fi.lproj/Browser.xib index 2441287481..9ef4948a03 100644 --- a/i18n/src/main/resources/fi.lproj/Browser.xib +++ b/i18n/src/main/resources/fi.lproj/Browser.xib @@ -1,7 +1,7 @@ - + - + @@ -38,7 +38,7 @@ - + @@ -62,27 +62,27 @@ @@ -256,7 +256,7 @@ - + @@ -553,95 +553,95 @@ DQ YnBsaXN0MDDUAQIDBAUGBwpYJHZlcnNpb25ZJGFyY2hpdmVyVCR0b3BYJG9iamVjdHMSAAGGoF8QD05T -S2V5ZWRBcmNoaXZlctEICVRyb290gAGuCwwZGh8UJCkqMTQ3PUBVJG51bGzWDQ4PEBESExQVFhcYVk5T -U2l6ZV5OU1Jlc2l6aW5nTW9kZVYkY2xhc3NcTlNJbWFnZUZsYWdzVk5TUmVwc1dOU0NvbG9ygAIQAIAN -EiDDAACAA4ALVnsxLCAxfdIbDxweWk5TLm9iamVjdHOhHYAEgArSGw8gI6IhIoAFgAaACdMPJSYnKBRf -EBROU1RJRkZSZXByZXNlbnRhdGlvbl8QGU5TSW50ZXJuYWxMYXlvdXREaXJlY3Rpb26ACIAHTxESbE1N -ACoAAAAKAAAAEAEAAAMAAAABAAEAAAEBAAMAAAABAAEAAAECAAMAAAACAAgACAEDAAMAAAABAAEAAAEG -AAMAAAABAAEAAAEKAAMAAAABAAEAAAERAAQAAAABAAAACAESAAMAAAABAAEAAAEVAAMAAAABAAIAAAEW -AAMAAAABAAEAAAEXAAQAAAABAAAAAgEcAAMAAAABAAEAAAEoAAMAAAABAAIAAAFSAAMAAAABAAEAAAFT -AAMAAAACAAEAAYdzAAcAABGcAAAA0AAAAAAAABGcYXBwbAIAAABtbnRyR1JBWVhZWiAH3AAIABcADwAu -AA9hY3NwQVBQTAAAAABub25lAAAAAAAAAAAAAAAAAAAAAAAA9tYAAQAAAADTLWFwcGwAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAVkZXNjAAAAwAAAAHlkc2NtAAABPAAA -CBpjcHJ0AAAJWAAAACN3dHB0AAAJfAAAABRrVFJDAAAJkAAACAxkZXNjAAAAAAAAAB9HZW5lcmljIEdy -YXkgR2FtbWEgMi4yIFByb2ZpbGUAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAbWx1YwAAAAAAAAAfAAAADHNr -U0sAAAAuAAABhGRhREsAAAA6AAABsmNhRVMAAAA4AAAB7HZpVk4AAABAAAACJHB0QlIAAABKAAACZHVr -VUEAAAAsAAACrmZyRlUAAAA+AAAC2mh1SFUAAAA0AAADGHpoVFcAAAAaAAADTGtvS1IAAAAiAAADZm5i -Tk8AAAA6AAADiGNzQ1oAAAAoAAADwmhlSUwAAAAkAAAD6nJvUk8AAAAqAAAEDmRlREUAAABOAAAEOGl0 -SVQAAABOAAAEhnN2U0UAAAA4AAAE1HpoQ04AAAAaAAAFDGphSlAAAAAmAAAFJmVsR1IAAAAqAAAFTHB0 -UE8AAABSAAAFdm5sTkwAAABAAAAFyGVzRVMAAABMAAAGCHRoVEgAAAAyAAAGVHRyVFIAAAAkAAAGhmZp -RkkAAABGAAAGqmhySFIAAAA+AAAG8HBsUEwAAABKAAAHLmFyRUcAAAAsAAAHeHJ1UlUAAAA6AAAHpGVu -VVMAAAA8AAAH3gBWAWEAZQBvAGIAZQBjAG4A4QAgAHMAaQB2AOEAIABnAGEAbQBhACAAMgAsADIARwBl -AG4AZQByAGkAcwBrACAAZwByAOUAIAAyACwAMgAgAGcAYQBtAG0AYQAtAHAAcgBvAGYAaQBsAEcAYQBt -AG0AYQAgAGQAZQAgAGcAcgBpAHMAbwBzACAAZwBlAG4A6AByAGkAYwBhACAAMgAuADIAQx6lAHUAIABo -AOwAbgBoACAATQDgAHUAIAB4AOEAbQAgAEMAaAB1AG4AZwAgAEcAYQBtAG0AYQAgADIALgAyAFAAZQBy -AGYAaQBsACAARwBlAG4A6QByAGkAYwBvACAAZABhACAARwBhAG0AYQAgAGQAZQAgAEMAaQBuAHoAYQBz -ACAAMgAsADIEFwQwBDMEMAQ7BEwEPQQwACAARwByAGEAeQAtBDMEMAQ8BDAAIAAyAC4AMgBQAHIAbwBm -AGkAbAAgAGcA6QBuAOkAcgBpAHEAdQBlACAAZwByAGkAcwAgAGcAYQBtAG0AYQAgADIALAAyAMEAbAB0 -AGEAbADhAG4AbwBzACAAcwB6APwAcgBrAGUAIABnAGEAbQBtAGEAIAAyAC4AMpAadShwcJaOUUlepgAy -AC4AMoJyX2ljz4/wx3y8GAAg1ozAyQAgrBC5yAAgADIALgAyACDVBLhc0wzHfABHAGUAbgBlAHIAaQBz -AGsAIABnAHIA5QAgAGcAYQBtAG0AYQAgADIALAAyAC0AcAByAG8AZgBpAGwATwBiAGUAYwBuAOEAIAFh -AGUAZADhACAAZwBhAG0AYQAgADIALgAyBdIF0AXeBdQAIAXQBeQF1QXoACAF2wXcBdwF2QAgADIALgAy -AEcAYQBtAGEAIABnAHIAaQAgAGcAZQBuAGUAcgBpAGMBAwAgADIALAAyAEEAbABsAGcAZQBtAGUAaQBu -AGUAcwAgAEcAcgBhAHUAcwB0AHUAZgBlAG4ALQBQAHIAbwBmAGkAbAAgAEcAYQBtAG0AYQAgADIALAAy -AFAAcgBvAGYAaQBsAG8AIABnAHIAaQBnAGkAbwAgAGcAZQBuAGUAcgBpAGMAbwAgAGQAZQBsAGwAYQAg -AGcAYQBtAG0AYQAgADIALAAyAEcAZQBuAGUAcgBpAHMAawAgAGcAcgDlACAAMgAsADIAIABnAGEAbQBt -AGEAcAByAG8AZgBpAGxmbpAacHBepnz7ZXAAMgAuADJjz4/wZYdO9k4AgiwwsDDsMKQwrDDzMN4AIAAy -AC4AMgAgMNcw7TDVMKEwpDDrA5MDtQO9A7kDugPMACADkwO6A8EDuQAgA5MDrAO8A7wDsQAgADIALgAy -AFAAZQByAGYAaQBsACAAZwBlAG4A6QByAGkAYwBvACAAZABlACAAYwBpAG4AegBlAG4AdABvAHMAIABk -AGEAIABHAGEAbQBtAGEAIAAyACwAMgBBAGwAZwBlAG0AZQBlAG4AIABnAHIAaQBqAHMAIABnAGEAbQBt -AGEAIAAyACwAMgAtAHAAcgBvAGYAaQBlAGwAUABlAHIAZgBpAGwAIABnAGUAbgDpAHIAaQBjAG8AIABk -AGUAIABnAGEAbQBtAGEAIABkAGUAIABnAHIAaQBzAGUAcwAgADIALAAyDiMOMQ4HDioONQ5BDgEOIQ4h -DjIOQA4BDiMOIg5MDhcOMQ5IDicORA4bACAAMgAuADIARwBlAG4AZQBsACAARwByAGkAIABHAGEAbQBh -ACAAMgAsADIAWQBsAGUAaQBuAGUAbgAgAGgAYQByAG0AYQBhAG4AIABnAGEAbQBtAGEAIAAyACwAMgAg -AC0AcAByAG8AZgBpAGkAbABpAEcAZQBuAGUAcgBpAQ0AawBpACAARwByAGEAeQAgAEcAYQBtAG0AYQAg -ADIALgAyACAAcAByAG8AZgBpAGwAVQBuAGkAdwBlAHIAcwBhAGwAbgB5ACAAcAByAG8AZgBpAGwAIABz -AHoAYQByAG8BWwBjAGkAIABnAGEAbQBtAGEAIAAyACwAMgY6BicGRQYnACAAMgAuADIAIAZEBkgGRgAg -BjEGRQYnBi8GSgAgBjkGJwZFBB4EMQRJBDAETwAgBEEENQRABDAETwAgBDMEMAQ8BDwEMAAgADIALAAy -AC0EPwRABD4ERAQ4BDsETABHAGUAbgBlAHIAaQBjACAARwByAGEAeQAgAEcAYQBtAG0AYQAgADIALgAy -ACAAUAByAG8AZgBpAGwAZQAAdGV4dAAAAABDb3B5cmlnaHQgQXBwbGUgSW5jLiwgMjAxMgAAWFlaIAAA -AAAAAPNRAAEAAAABFsxjdXJ2AAAAAAAABAAAAAAFAAoADwAUABkAHgAjACgALQAyADcAOwBAAEUASgBP -AFQAWQBeAGMAaABtAHIAdwB8AIEAhgCLAJAAlQCaAJ8ApACpAK4AsgC3ALwAwQDGAMsA0ADVANsA4ADl -AOsA8AD2APsBAQEHAQ0BEwEZAR8BJQErATIBOAE+AUUBTAFSAVkBYAFnAW4BdQF8AYMBiwGSAZoBoQGp -AbEBuQHBAckB0QHZAeEB6QHyAfoCAwIMAhQCHQImAi8COAJBAksCVAJdAmcCcQJ6AoQCjgKYAqICrAK2 -AsECywLVAuAC6wL1AwADCwMWAyEDLQM4A0MDTwNaA2YDcgN+A4oDlgOiA64DugPHA9MD4APsA/kEBgQT -BCAELQQ7BEgEVQRjBHEEfgSMBJoEqAS2BMQE0wThBPAE/gUNBRwFKwU6BUkFWAVnBXcFhgWWBaYFtQXF -BdUF5QX2BgYGFgYnBjcGSAZZBmoGewaMBp0GrwbABtEG4wb1BwcHGQcrBz0HTwdhB3QHhgeZB6wHvwfS -B+UH+AgLCB8IMghGCFoIbgiCCJYIqgi+CNII5wj7CRAJJQk6CU8JZAl5CY8JpAm6Cc8J5Qn7ChEKJwo9 -ClQKagqBCpgKrgrFCtwK8wsLCyILOQtRC2kLgAuYC7ALyAvhC/kMEgwqDEMMXAx1DI4MpwzADNkM8w0N -DSYNQA1aDXQNjg2pDcMN3g34DhMOLg5JDmQOfw6bDrYO0g7uDwkPJQ9BD14Peg+WD7MPzw/sEAkQJhBD -EGEQfhCbELkQ1xD1ERMRMRFPEW0RjBGqEckR6BIHEiYSRRJkEoQSoxLDEuMTAxMjE0MTYxODE6QTxRPl -FAYUJxRJFGoUixStFM4U8BUSFTQVVhV4FZsVvRXgFgMWJhZJFmwWjxayFtYW+hcdF0EXZReJF64X0hf3 -GBsYQBhlGIoYrxjVGPoZIBlFGWsZkRm3Gd0aBBoqGlEadxqeGsUa7BsUGzsbYxuKG7Ib2hwCHCocUhx7 -HKMczBz1HR4dRx1wHZkdwx3sHhYeQB5qHpQevh7pHxMfPh9pH5Qfvx/qIBUgQSBsIJggxCDwIRwhSCF1 -IaEhziH7IiciVSKCIq8i3SMKIzgjZiOUI8Ij8CQfJE0kfCSrJNolCSU4JWgllyXHJfcmJyZXJocmtybo -JxgnSSd6J6sn3CgNKD8ocSiiKNQpBik4KWspnSnQKgIqNSpoKpsqzysCKzYraSudK9EsBSw5LG4soizX -LQwtQS12Last4S4WLkwugi63Lu4vJC9aL5Evxy/+MDUwbDCkMNsxEjFKMYIxujHyMioyYzKbMtQzDTNG -M38zuDPxNCs0ZTSeNNg1EzVNNYc1wjX9Njc2cjauNuk3JDdgN5w31zgUOFA4jDjIOQU5Qjl/Obw5+To2 -OnQ6sjrvOy07azuqO+g8JzxlPKQ84z0iPWE9oT3gPiA+YD6gPuA/IT9hP6I/4kAjQGRApkDnQSlBakGs -Qe5CMEJyQrVC90M6Q31DwEQDREdEikTORRJFVUWaRd5GIkZnRqtG8Ec1R3tHwEgFSEtIkUjXSR1JY0mp -SfBKN0p9SsRLDEtTS5pL4kwqTHJMuk0CTUpNk03cTiVObk63TwBPSU+TT91QJ1BxULtRBlFQUZtR5lIx -UnxSx1MTU19TqlP2VEJUj1TbVShVdVXCVg9WXFapVvdXRFeSV+BYL1h9WMtZGllpWbhaB1pWWqZa9VtF -W5Vb5Vw1XIZc1l0nXXhdyV4aXmxevV8PX2Ffs2AFYFdgqmD8YU9homH1YklinGLwY0Njl2PrZEBklGTp -ZT1lkmXnZj1mkmboZz1nk2fpaD9olmjsaUNpmmnxakhqn2r3a09rp2v/bFdsr20IbWBtuW4SbmtuxG8e -b3hv0XArcIZw4HE6cZVx8HJLcqZzAXNdc7h0FHRwdMx1KHWFdeF2Pnabdvh3VnezeBF4bnjMeSp5iXnn -ekZ6pXsEe2N7wnwhfIF84X1BfaF+AX5ifsJ/I3+Ef+WAR4CogQqBa4HNgjCCkoL0g1eDuoQdhICE44VH -hauGDoZyhteHO4efiASIaYjOiTOJmYn+imSKyoswi5aL/IxjjMqNMY2Yjf+OZo7OjzaPnpAGkG6Q1pE/ -kaiSEZJ6kuOTTZO2lCCUipT0lV+VyZY0lp+XCpd1l+CYTJi4mSSZkJn8mmia1ZtCm6+cHJyJnPedZJ3S -nkCerp8dn4uf+qBpoNihR6G2oiailqMGo3aj5qRWpMelOKWpphqmi6b9p26n4KhSqMSpN6mpqhyqj6sC -q3Wr6axcrNCtRK24ri2uoa8Wr4uwALB1sOqxYLHWskuywrM4s660JbSctRO1irYBtnm28Ldot+C4WbjR -uUq5wro7urW7LrunvCG8m70VvY++Cr6Evv+/er/1wHDA7MFnwePCX8Lbw1jD1MRRxM7FS8XIxkbGw8dB -x7/IPci8yTrJuco4yrfLNsu2zDXMtc01zbXONs62zzfPuNA50LrRPNG+0j/SwdNE08bUSdTL1U7V0dZV -1tjXXNfg2GTY6Nls2fHadtr724DcBdyK3RDdlt4c3qLfKd+v4DbgveFE4cziU+Lb42Pj6+Rz5PzlhOYN -5pbnH+ep6DLovOlG6dDqW+rl63Dr++yG7RHtnO4o7rTvQO/M8Fjw5fFy8f/yjPMZ86f0NPTC9VD13vZt -9vv3ivgZ+Kj5OPnH+lf65/t3/Af8mP0p/br+S/7c/23//9IrLC0uWiRjbGFzc25hbWVYJGNsYXNzZXNf -EBBOU0JpdG1hcEltYWdlUmVwoy0vMFpOU0ltYWdlUmVwWE5TT2JqZWN00issMjNXTlNBcnJheaIyMNIr -LDU2Xk5TTXV0YWJsZUFycmF5ozUyMNM4OQ86OzxXTlNXaGl0ZVxOU0NvbG9yU3BhY2VEMCAwABADgAzS -Kyw+P1dOU0NvbG9yoj4w0issQUJXTlNJbWFnZaJBMAAIABEAGgAkACkAMgA3AEkATABRAFMAYgBoAHUA -fACLAJIAnwCmAK4AsACyALQAuQC7AL0AxADJANQA1gDYANoA3wDiAOQA5gDoAO8BBgEiASQBJhOWE5sT -phOvE8ITxhPRE9oT3xPnE+oT7xP+FAIUCRQRFB4UIxQlFCcULBQ0FDcUPBREAAAAAAAAAgEAAAAAAAAA -QwAAAAAAAAAAAAAAAAAAFEc +S2V5ZWRBcmNoaXZlctEICVRyb290gAGuCwwbHCEaJissMzY5P0JVJG51bGzXDQ4PEBESExQVFhcYGRpW +TlNTaXplViRjbGFzc1xOU0ltYWdlRmxhZ3NWTlNSZXBzW05TVGludENvbG9yV05TQ29sb3JeTlNSZXNp +emluZ01vZGWAAoANEiDDAACAA4AAgAsQAFZ7MSwgMX3SHQ4eIFpOUy5vYmplY3RzoR+ABIAK0h0OIiWi +IySABYAGgAnTDicoKSoaXxAUTlNUSUZGUmVwcmVzZW50YXRpb25fEBlOU0ludGVybmFsTGF5b3V0RGly +ZWN0aW9ugAiAB08REmxNTQAqAAAACgAAABABAAADAAAAAQABAAABAQADAAAAAQABAAABAgADAAAAAgAI +AAgBAwADAAAAAQABAAABBgADAAAAAQABAAABCgADAAAAAQABAAABEQAEAAAAAQAAAAgBEgADAAAAAQAB +AAABFQADAAAAAQACAAABFgADAAAAAQABAAABFwAEAAAAAQAAAAIBHAADAAAAAQABAAABKAADAAAAAQAC +AAABUgADAAAAAQABAAABUwADAAAAAgABAAGHcwAHAAARnAAAANAAAAAAAAARnGFwcGwCAAAAbW50ckdS +QVlYWVogB9wACAAXAA8ALgAPYWNzcEFQUEwAAAAAbm9uZQAAAAAAAAAAAAAAAAAAAAAAAPbWAAEAAAAA +0y1hcHBsAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFZGVzYwAA +AMAAAAB5ZHNjbQAAATwAAAgaY3BydAAACVgAAAAjd3RwdAAACXwAAAAUa1RSQwAACZAAAAgMZGVzYwAA +AAAAAAAfR2VuZXJpYyBHcmF5IEdhbW1hIDIuMiBQcm9maWxlAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAG1s +dWMAAAAAAAAAHwAAAAxza1NLAAAALgAAAYRkYURLAAAAOgAAAbJjYUVTAAAAOAAAAex2aVZOAAAAQAAA +AiRwdEJSAAAASgAAAmR1a1VBAAAALAAAAq5mckZVAAAAPgAAAtpodUhVAAAANAAAAxh6aFRXAAAAGgAA +A0xrb0tSAAAAIgAAA2ZuYk5PAAAAOgAAA4hjc0NaAAAAKAAAA8JoZUlMAAAAJAAAA+pyb1JPAAAAKgAA +BA5kZURFAAAATgAABDhpdElUAAAATgAABIZzdlNFAAAAOAAABNR6aENOAAAAGgAABQxqYUpQAAAAJgAA +BSZlbEdSAAAAKgAABUxwdFBPAAAAUgAABXZubE5MAAAAQAAABchlc0VTAAAATAAABgh0aFRIAAAAMgAA +BlR0clRSAAAAJAAABoZmaUZJAAAARgAABqpockhSAAAAPgAABvBwbFBMAAAASgAABy5hckVHAAAALAAA +B3hydVJVAAAAOgAAB6RlblVTAAAAPAAAB94AVgFhAGUAbwBiAGUAYwBuAOEAIABzAGkAdgDhACAAZwBh +AG0AYQAgADIALAAyAEcAZQBuAGUAcgBpAHMAawAgAGcAcgDlACAAMgAsADIAIABnAGEAbQBtAGEALQBw +AHIAbwBmAGkAbABHAGEAbQBtAGEAIABkAGUAIABnAHIAaQBzAG8AcwAgAGcAZQBuAOgAcgBpAGMAYQAg +ADIALgAyAEMepQB1ACAAaADsAG4AaAAgAE0A4AB1ACAAeADhAG0AIABDAGgAdQBuAGcAIABHAGEAbQBt +AGEAIAAyAC4AMgBQAGUAcgBmAGkAbAAgAEcAZQBuAOkAcgBpAGMAbwAgAGQAYQAgAEcAYQBtAGEAIABk +AGUAIABDAGkAbgB6AGEAcwAgADIALAAyBBcEMAQzBDAEOwRMBD0EMAAgAEcAcgBhAHkALQQzBDAEPAQw +ACAAMgAuADIAUAByAG8AZgBpAGwAIABnAOkAbgDpAHIAaQBxAHUAZQAgAGcAcgBpAHMAIABnAGEAbQBt +AGEAIAAyACwAMgDBAGwAdABhAGwA4QBuAG8AcwAgAHMAegD8AHIAawBlACAAZwBhAG0AbQBhACAAMgAu +ADKQGnUocHCWjlFJXqYAMgAuADKCcl9pY8+P8Md8vBgAINaMwMkAIKwQucgAIAAyAC4AMgAg1QS4XNMM +x3wARwBlAG4AZQByAGkAcwBrACAAZwByAOUAIABnAGEAbQBtAGEAIAAyACwAMgAtAHAAcgBvAGYAaQBs +AE8AYgBlAGMAbgDhACABYQBlAGQA4QAgAGcAYQBtAGEAIAAyAC4AMgXSBdAF3gXUACAF0AXkBdUF6AAg +BdsF3AXcBdkAIAAyAC4AMgBHAGEAbQBhACAAZwByAGkAIABnAGUAbgBlAHIAaQBjAQMAIAAyACwAMgBB +AGwAbABnAGUAbQBlAGkAbgBlAHMAIABHAHIAYQB1AHMAdAB1AGYAZQBuAC0AUAByAG8AZgBpAGwAIABH +AGEAbQBtAGEAIAAyACwAMgBQAHIAbwBmAGkAbABvACAAZwByAGkAZwBpAG8AIABnAGUAbgBlAHIAaQBj +AG8AIABkAGUAbABsAGEAIABnAGEAbQBtAGEAIAAyACwAMgBHAGUAbgBlAHIAaQBzAGsAIABnAHIA5QAg +ADIALAAyACAAZwBhAG0AbQBhAHAAcgBvAGYAaQBsZm6QGnBwXqZ8+2VwADIALgAyY8+P8GWHTvZOAIIs +MLAw7DCkMKww8zDeACAAMgAuADIAIDDXMO0w1TChMKQw6wOTA7UDvQO5A7oDzAAgA5MDugPBA7kAIAOT +A6wDvAO8A7EAIAAyAC4AMgBQAGUAcgBmAGkAbAAgAGcAZQBuAOkAcgBpAGMAbwAgAGQAZQAgAGMAaQBu +AHoAZQBuAHQAbwBzACAAZABhACAARwBhAG0AbQBhACAAMgAsADIAQQBsAGcAZQBtAGUAZQBuACAAZwBy +AGkAagBzACAAZwBhAG0AbQBhACAAMgAsADIALQBwAHIAbwBmAGkAZQBsAFAAZQByAGYAaQBsACAAZwBl +AG4A6QByAGkAYwBvACAAZABlACAAZwBhAG0AbQBhACAAZABlACAAZwByAGkAcwBlAHMAIAAyACwAMg4j +DjEOBw4qDjUOQQ4BDiEOIQ4yDkAOAQ4jDiIOTA4XDjEOSA4nDkQOGwAgADIALgAyAEcAZQBuAGUAbAAg +AEcAcgBpACAARwBhAG0AYQAgADIALAAyAFkAbABlAGkAbgBlAG4AIABoAGEAcgBtAGEAYQBuACAAZwBh +AG0AbQBhACAAMgAsADIAIAAtAHAAcgBvAGYAaQBpAGwAaQBHAGUAbgBlAHIAaQENAGsAaQAgAEcAcgBh +AHkAIABHAGEAbQBtAGEAIAAyAC4AMgAgAHAAcgBvAGYAaQBsAFUAbgBpAHcAZQByAHMAYQBsAG4AeQAg +AHAAcgBvAGYAaQBsACAAcwB6AGEAcgBvAVsAYwBpACAAZwBhAG0AbQBhACAAMgAsADIGOgYnBkUGJwAg +ADIALgAyACAGRAZIBkYAIAYxBkUGJwYvBkoAIAY5BicGRQQeBDEESQQwBE8AIARBBDUEQAQwBE8AIAQz +BDAEPAQ8BDAAIAAyACwAMgAtBD8EQAQ+BEQEOAQ7BEwARwBlAG4AZQByAGkAYwAgAEcAcgBhAHkAIABH +AGEAbQBtAGEAIAAyAC4AMgAgAFAAcgBvAGYAaQBsAGUAAHRleHQAAAAAQ29weXJpZ2h0IEFwcGxlIElu +Yy4sIDIwMTIAAFhZWiAAAAAAAADzUQABAAAAARbMY3VydgAAAAAAAAQAAAAABQAKAA8AFAAZAB4AIwAo +AC0AMgA3ADsAQABFAEoATwBUAFkAXgBjAGgAbQByAHcAfACBAIYAiwCQAJUAmgCfAKQAqQCuALIAtwC8 +AMEAxgDLANAA1QDbAOAA5QDrAPAA9gD7AQEBBwENARMBGQEfASUBKwEyATgBPgFFAUwBUgFZAWABZwFu +AXUBfAGDAYsBkgGaAaEBqQGxAbkBwQHJAdEB2QHhAekB8gH6AgMCDAIUAh0CJgIvAjgCQQJLAlQCXQJn +AnECegKEAo4CmAKiAqwCtgLBAssC1QLgAusC9QMAAwsDFgMhAy0DOANDA08DWgNmA3IDfgOKA5YDogOu +A7oDxwPTA+AD7AP5BAYEEwQgBC0EOwRIBFUEYwRxBH4EjASaBKgEtgTEBNME4QTwBP4FDQUcBSsFOgVJ +BVgFZwV3BYYFlgWmBbUFxQXVBeUF9gYGBhYGJwY3BkgGWQZqBnsGjAadBq8GwAbRBuMG9QcHBxkHKwc9 +B08HYQd0B4YHmQesB78H0gflB/gICwgfCDIIRghaCG4IggiWCKoIvgjSCOcI+wkQCSUJOglPCWQJeQmP +CaQJugnPCeUJ+woRCicKPQpUCmoKgQqYCq4KxQrcCvMLCwsiCzkLUQtpC4ALmAuwC8gL4Qv5DBIMKgxD +DFwMdQyODKcMwAzZDPMNDQ0mDUANWg10DY4NqQ3DDd4N+A4TDi4OSQ5kDn8Omw62DtIO7g8JDyUPQQ9e +D3oPlg+zD88P7BAJECYQQxBhEH4QmxC5ENcQ9RETETERTxFtEYwRqhHJEegSBxImEkUSZBKEEqMSwxLj +EwMTIxNDE2MTgxOkE8UT5RQGFCcUSRRqFIsUrRTOFPAVEhU0FVYVeBWbFb0V4BYDFiYWSRZsFo8WshbW +FvoXHRdBF2UXiReuF9IX9xgbGEAYZRiKGK8Y1Rj6GSAZRRlrGZEZtxndGgQaKhpRGncanhrFGuwbFBs7 +G2MbihuyG9ocAhwqHFIcexyjHMwc9R0eHUcdcB2ZHcMd7B4WHkAeah6UHr4e6R8THz4faR+UH78f6iAV +IEEgbCCYIMQg8CEcIUghdSGhIc4h+yInIlUigiKvIt0jCiM4I2YjlCPCI/AkHyRNJHwkqyTaJQklOCVo +JZclxyX3JicmVyaHJrcm6CcYJ0kneierJ9woDSg/KHEooijUKQYpOClrKZ0p0CoCKjUqaCqbKs8rAis2 +K2krnSvRLAUsOSxuLKIs1y0MLUEtdi2rLeEuFi5MLoIuty7uLyQvWi+RL8cv/jA1MGwwpDDbMRIxSjGC +Mbox8jIqMmMymzLUMw0zRjN/M7gz8TQrNGU0njTYNRM1TTWHNcI1/TY3NnI2rjbpNyQ3YDecN9c4FDhQ +OIw4yDkFOUI5fzm8Ofk6Njp0OrI67zstO2s7qjvoPCc8ZTykPOM9Ij1hPaE94D4gPmA+oD7gPyE/YT+i +P+JAI0BkQKZA50EpQWpBrEHuQjBCckK1QvdDOkN9Q8BEA0RHRIpEzkUSRVVFmkXeRiJGZ0arRvBHNUd7 +R8BIBUhLSJFI10kdSWNJqUnwSjdKfUrESwxLU0uaS+JMKkxyTLpNAk1KTZNN3E4lTm5Ot08AT0lPk0/d +UCdQcVC7UQZRUFGbUeZSMVJ8UsdTE1NfU6pT9lRCVI9U21UoVXVVwlYPVlxWqVb3V0RXklfgWC9YfVjL +WRpZaVm4WgdaVlqmWvVbRVuVW+VcNVyGXNZdJ114XcleGl5sXr1fD19hX7NgBWBXYKpg/GFPYaJh9WJJ +Ypxi8GNDY5dj62RAZJRk6WU9ZZJl52Y9ZpJm6Gc9Z5Nn6Wg/aJZo7GlDaZpp8WpIap9q92tPa6dr/2xX +bK9tCG1gbbluEm5rbsRvHm94b9FwK3CGcOBxOnGVcfByS3KmcwFzXXO4dBR0cHTMdSh1hXXhdj52m3b4 +d1Z3s3gReG54zHkqeYl553pGeqV7BHtje8J8IXyBfOF9QX2hfgF+Yn7CfyN/hH/lgEeAqIEKgWuBzYIw +gpKC9INXg7qEHYSAhOOFR4Wrhg6GcobXhzuHn4gEiGmIzokziZmJ/opkisqLMIuWi/yMY4zKjTGNmI3/ +jmaOzo82j56QBpBukNaRP5GokhGSepLjk02TtpQglIqU9JVflcmWNJaflwqXdZfgmEyYuJkkmZCZ/Jpo +mtWbQpuvnByciZz3nWSd0p5Anq6fHZ+Ln/qgaaDYoUehtqImopajBqN2o+akVqTHpTilqaYapoum/adu +p+CoUqjEqTepqaocqo+rAqt1q+msXKzQrUStuK4trqGvFq+LsACwdbDqsWCx1rJLssKzOLOutCW0nLUT +tYq2AbZ5tvC3aLfguFm40blKucK6O7q1uy67p7whvJu9Fb2Pvgq+hL7/v3q/9cBwwOzBZ8Hjwl/C28NY +w9TEUcTOxUvFyMZGxsPHQce/yD3IvMk6ybnKOMq3yzbLtsw1zLXNNc21zjbOts83z7jQOdC60TzRvtI/ +0sHTRNPG1EnUy9VO1dHWVdbY11zX4Nhk2OjZbNnx2nba+9uA3AXcit0Q3ZbeHN6i3ynfr+A24L3hROHM +4lPi2+Nj4+vkc+T85YTmDeaW5x/nqegy6LzpRunQ6lvq5etw6/vshu0R7ZzuKO6070DvzPBY8OXxcvH/ +8ozzGfOn9DT0wvVQ9d72bfb794r4Gfio+Tj5x/pX+uf7d/wH/Jj9Kf26/kv+3P9t///SLS4vMFokY2xh +c3NuYW1lWCRjbGFzc2VzXxAQTlNCaXRtYXBJbWFnZVJlcKMvMTJaTlNJbWFnZVJlcFhOU09iamVjdNIt +LjQ1V05TQXJyYXmiNDLSLS43OF5OU011dGFibGVBcnJheaM3NDLTOjsOPD0+V05TV2hpdGVcTlNDb2xv +clNwYWNlRDAgMAAQA4AM0i0uQEFXTlNDb2xvcqJAMtItLkNEV05TSW1hZ2WiQzIACAARABoAJAApADIA +NwBJAEwAUQBTAGIAaAB3AH4AhQCSAJkApQCtALwAvgDAAMUAxwDJAMsAzQDUANkA5ADmAOgA6gDvAPIA +9AD2APgA/wEWATIBNAE2E6YTqxO2E78T0hPWE+ET6hPvE/cT+hP/FA4UEhQZFCEULhQzFDUUNxQ8FEQU +RxRMFFQAAAAAAAACAQAAAAAAAABFAAAAAAAAAAAAAAAAAAAUVw diff --git a/i18n/src/main/resources/fi.lproj/Command.xib b/i18n/src/main/resources/fi.lproj/Command.xib index dfe96e97d9..67e9a3d6cf 100644 --- a/i18n/src/main/resources/fi.lproj/Command.xib +++ b/i18n/src/main/resources/fi.lproj/Command.xib @@ -1,7 +1,7 @@ - + - + diff --git a/i18n/src/main/resources/fi.lproj/Connection.xib b/i18n/src/main/resources/fi.lproj/Connection.xib index 38edb1aa42..65ba4d74d4 100644 --- a/i18n/src/main/resources/fi.lproj/Connection.xib +++ b/i18n/src/main/resources/fi.lproj/Connection.xib @@ -1,7 +1,7 @@ - + - + @@ -76,7 +76,7 @@ - + diff --git a/i18n/src/main/resources/fi.lproj/Error.strings b/i18n/src/main/resources/fi.lproj/Error.strings index ffa65de26d..3228549d7e 100644 Binary files a/i18n/src/main/resources/fi.lproj/Error.strings and b/i18n/src/main/resources/fi.lproj/Error.strings differ diff --git a/i18n/src/main/resources/fi.lproj/Info.xib b/i18n/src/main/resources/fi.lproj/Info.xib index 998eeae2b5..943fda4190 100644 --- a/i18n/src/main/resources/fi.lproj/Info.xib +++ b/i18n/src/main/resources/fi.lproj/Info.xib @@ -1,7 +1,7 @@ - + - + @@ -89,7 +89,7 @@ - + @@ -1017,21 +1017,21 @@ - + - + - + @@ -1121,21 +1121,21 @@ - + - + - + @@ -1250,27 +1250,27 @@ diff --git a/i18n/src/main/resources/fi.lproj/Login.strings b/i18n/src/main/resources/fi.lproj/Login.strings index 86bcbb9762..b6720e6a46 100644 Binary files a/i18n/src/main/resources/fi.lproj/Login.strings and b/i18n/src/main/resources/fi.lproj/Login.strings differ diff --git a/i18n/src/main/resources/fi.lproj/Main.xib b/i18n/src/main/resources/fi.lproj/Main.xib index 5136eb7816..8d1b638dc6 100644 --- a/i18n/src/main/resources/fi.lproj/Main.xib +++ b/i18n/src/main/resources/fi.lproj/Main.xib @@ -1,7 +1,7 @@ - + - + @@ -159,19 +159,19 @@ - + - + - + @@ -321,12 +321,12 @@ CA - + - + @@ -566,6 +566,10 @@ CA - + + + + + diff --git a/i18n/src/main/resources/fi.lproj/Preferences.strings b/i18n/src/main/resources/fi.lproj/Preferences.strings index f51551070c..72428cf2af 100644 Binary files a/i18n/src/main/resources/fi.lproj/Preferences.strings and b/i18n/src/main/resources/fi.lproj/Preferences.strings differ diff --git a/i18n/src/main/resources/fi.lproj/Preferences.strings.1 b/i18n/src/main/resources/fi.lproj/Preferences.strings.1 index 148d794a51..53d4c2b0f2 100644 Binary files a/i18n/src/main/resources/fi.lproj/Preferences.strings.1 and b/i18n/src/main/resources/fi.lproj/Preferences.strings.1 differ diff --git a/i18n/src/main/resources/fi.lproj/Preferences.xib b/i18n/src/main/resources/fi.lproj/Preferences.xib index f945d516cf..2040d2722f 100644 --- a/i18n/src/main/resources/fi.lproj/Preferences.xib +++ b/i18n/src/main/resources/fi.lproj/Preferences.xib @@ -1,7 +1,7 @@ - + - + @@ -245,7 +245,7 @@ - + @@ -266,7 +266,7 @@ - + @@ -307,7 +307,7 @@ - + @@ -538,7 +538,7 @@ - + @@ -578,7 +578,7 @@ - + @@ -613,7 +613,7 @@ - + @@ -642,7 +642,7 @@ - + @@ -817,7 +817,7 @@ @@ -256,7 +256,7 @@ - + @@ -553,95 +553,95 @@ DQ YnBsaXN0MDDUAQIDBAUGBwpYJHZlcnNpb25ZJGFyY2hpdmVyVCR0b3BYJG9iamVjdHMSAAGGoF8QD05T -S2V5ZWRBcmNoaXZlctEICVRyb290gAGuCwwZGh8UJCkqMTQ3PUBVJG51bGzWDQ4PEBESExQVFhcYVk5T -U2l6ZV5OU1Jlc2l6aW5nTW9kZVYkY2xhc3NcTlNJbWFnZUZsYWdzVk5TUmVwc1dOU0NvbG9ygAIQAIAN -EiDDAACAA4ALVnsxLCAxfdIbDxweWk5TLm9iamVjdHOhHYAEgArSGw8gI6IhIoAFgAaACdMPJSYnKBRf -EBROU1RJRkZSZXByZXNlbnRhdGlvbl8QGU5TSW50ZXJuYWxMYXlvdXREaXJlY3Rpb26ACIAHTxESbE1N -ACoAAAAKAAAAEAEAAAMAAAABAAEAAAEBAAMAAAABAAEAAAECAAMAAAACAAgACAEDAAMAAAABAAEAAAEG -AAMAAAABAAEAAAEKAAMAAAABAAEAAAERAAQAAAABAAAACAESAAMAAAABAAEAAAEVAAMAAAABAAIAAAEW -AAMAAAABAAEAAAEXAAQAAAABAAAAAgEcAAMAAAABAAEAAAEoAAMAAAABAAIAAAFSAAMAAAABAAEAAAFT -AAMAAAACAAEAAYdzAAcAABGcAAAA0AAAAAAAABGcYXBwbAIAAABtbnRyR1JBWVhZWiAH3AAIABcADwAu -AA9hY3NwQVBQTAAAAABub25lAAAAAAAAAAAAAAAAAAAAAAAA9tYAAQAAAADTLWFwcGwAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAVkZXNjAAAAwAAAAHlkc2NtAAABPAAA -CBpjcHJ0AAAJWAAAACN3dHB0AAAJfAAAABRrVFJDAAAJkAAACAxkZXNjAAAAAAAAAB9HZW5lcmljIEdy -YXkgR2FtbWEgMi4yIFByb2ZpbGUAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAbWx1YwAAAAAAAAAfAAAADHNr -U0sAAAAuAAABhGRhREsAAAA6AAABsmNhRVMAAAA4AAAB7HZpVk4AAABAAAACJHB0QlIAAABKAAACZHVr -VUEAAAAsAAACrmZyRlUAAAA+AAAC2mh1SFUAAAA0AAADGHpoVFcAAAAaAAADTGtvS1IAAAAiAAADZm5i -Tk8AAAA6AAADiGNzQ1oAAAAoAAADwmhlSUwAAAAkAAAD6nJvUk8AAAAqAAAEDmRlREUAAABOAAAEOGl0 -SVQAAABOAAAEhnN2U0UAAAA4AAAE1HpoQ04AAAAaAAAFDGphSlAAAAAmAAAFJmVsR1IAAAAqAAAFTHB0 -UE8AAABSAAAFdm5sTkwAAABAAAAFyGVzRVMAAABMAAAGCHRoVEgAAAAyAAAGVHRyVFIAAAAkAAAGhmZp -RkkAAABGAAAGqmhySFIAAAA+AAAG8HBsUEwAAABKAAAHLmFyRUcAAAAsAAAHeHJ1UlUAAAA6AAAHpGVu -VVMAAAA8AAAH3gBWAWEAZQBvAGIAZQBjAG4A4QAgAHMAaQB2AOEAIABnAGEAbQBhACAAMgAsADIARwBl -AG4AZQByAGkAcwBrACAAZwByAOUAIAAyACwAMgAgAGcAYQBtAG0AYQAtAHAAcgBvAGYAaQBsAEcAYQBt -AG0AYQAgAGQAZQAgAGcAcgBpAHMAbwBzACAAZwBlAG4A6AByAGkAYwBhACAAMgAuADIAQx6lAHUAIABo -AOwAbgBoACAATQDgAHUAIAB4AOEAbQAgAEMAaAB1AG4AZwAgAEcAYQBtAG0AYQAgADIALgAyAFAAZQBy -AGYAaQBsACAARwBlAG4A6QByAGkAYwBvACAAZABhACAARwBhAG0AYQAgAGQAZQAgAEMAaQBuAHoAYQBz -ACAAMgAsADIEFwQwBDMEMAQ7BEwEPQQwACAARwByAGEAeQAtBDMEMAQ8BDAAIAAyAC4AMgBQAHIAbwBm -AGkAbAAgAGcA6QBuAOkAcgBpAHEAdQBlACAAZwByAGkAcwAgAGcAYQBtAG0AYQAgADIALAAyAMEAbAB0 -AGEAbADhAG4AbwBzACAAcwB6APwAcgBrAGUAIABnAGEAbQBtAGEAIAAyAC4AMpAadShwcJaOUUlepgAy -AC4AMoJyX2ljz4/wx3y8GAAg1ozAyQAgrBC5yAAgADIALgAyACDVBLhc0wzHfABHAGUAbgBlAHIAaQBz -AGsAIABnAHIA5QAgAGcAYQBtAG0AYQAgADIALAAyAC0AcAByAG8AZgBpAGwATwBiAGUAYwBuAOEAIAFh -AGUAZADhACAAZwBhAG0AYQAgADIALgAyBdIF0AXeBdQAIAXQBeQF1QXoACAF2wXcBdwF2QAgADIALgAy -AEcAYQBtAGEAIABnAHIAaQAgAGcAZQBuAGUAcgBpAGMBAwAgADIALAAyAEEAbABsAGcAZQBtAGUAaQBu -AGUAcwAgAEcAcgBhAHUAcwB0AHUAZgBlAG4ALQBQAHIAbwBmAGkAbAAgAEcAYQBtAG0AYQAgADIALAAy -AFAAcgBvAGYAaQBsAG8AIABnAHIAaQBnAGkAbwAgAGcAZQBuAGUAcgBpAGMAbwAgAGQAZQBsAGwAYQAg -AGcAYQBtAG0AYQAgADIALAAyAEcAZQBuAGUAcgBpAHMAawAgAGcAcgDlACAAMgAsADIAIABnAGEAbQBt -AGEAcAByAG8AZgBpAGxmbpAacHBepnz7ZXAAMgAuADJjz4/wZYdO9k4AgiwwsDDsMKQwrDDzMN4AIAAy -AC4AMgAgMNcw7TDVMKEwpDDrA5MDtQO9A7kDugPMACADkwO6A8EDuQAgA5MDrAO8A7wDsQAgADIALgAy -AFAAZQByAGYAaQBsACAAZwBlAG4A6QByAGkAYwBvACAAZABlACAAYwBpAG4AegBlAG4AdABvAHMAIABk -AGEAIABHAGEAbQBtAGEAIAAyACwAMgBBAGwAZwBlAG0AZQBlAG4AIABnAHIAaQBqAHMAIABnAGEAbQBt -AGEAIAAyACwAMgAtAHAAcgBvAGYAaQBlAGwAUABlAHIAZgBpAGwAIABnAGUAbgDpAHIAaQBjAG8AIABk -AGUAIABnAGEAbQBtAGEAIABkAGUAIABnAHIAaQBzAGUAcwAgADIALAAyDiMOMQ4HDioONQ5BDgEOIQ4h -DjIOQA4BDiMOIg5MDhcOMQ5IDicORA4bACAAMgAuADIARwBlAG4AZQBsACAARwByAGkAIABHAGEAbQBh -ACAAMgAsADIAWQBsAGUAaQBuAGUAbgAgAGgAYQByAG0AYQBhAG4AIABnAGEAbQBtAGEAIAAyACwAMgAg -AC0AcAByAG8AZgBpAGkAbABpAEcAZQBuAGUAcgBpAQ0AawBpACAARwByAGEAeQAgAEcAYQBtAG0AYQAg -ADIALgAyACAAcAByAG8AZgBpAGwAVQBuAGkAdwBlAHIAcwBhAGwAbgB5ACAAcAByAG8AZgBpAGwAIABz -AHoAYQByAG8BWwBjAGkAIABnAGEAbQBtAGEAIAAyACwAMgY6BicGRQYnACAAMgAuADIAIAZEBkgGRgAg -BjEGRQYnBi8GSgAgBjkGJwZFBB4EMQRJBDAETwAgBEEENQRABDAETwAgBDMEMAQ8BDwEMAAgADIALAAy -AC0EPwRABD4ERAQ4BDsETABHAGUAbgBlAHIAaQBjACAARwByAGEAeQAgAEcAYQBtAG0AYQAgADIALgAy -ACAAUAByAG8AZgBpAGwAZQAAdGV4dAAAAABDb3B5cmlnaHQgQXBwbGUgSW5jLiwgMjAxMgAAWFlaIAAA -AAAAAPNRAAEAAAABFsxjdXJ2AAAAAAAABAAAAAAFAAoADwAUABkAHgAjACgALQAyADcAOwBAAEUASgBP -AFQAWQBeAGMAaABtAHIAdwB8AIEAhgCLAJAAlQCaAJ8ApACpAK4AsgC3ALwAwQDGAMsA0ADVANsA4ADl -AOsA8AD2APsBAQEHAQ0BEwEZAR8BJQErATIBOAE+AUUBTAFSAVkBYAFnAW4BdQF8AYMBiwGSAZoBoQGp -AbEBuQHBAckB0QHZAeEB6QHyAfoCAwIMAhQCHQImAi8COAJBAksCVAJdAmcCcQJ6AoQCjgKYAqICrAK2 -AsECywLVAuAC6wL1AwADCwMWAyEDLQM4A0MDTwNaA2YDcgN+A4oDlgOiA64DugPHA9MD4APsA/kEBgQT -BCAELQQ7BEgEVQRjBHEEfgSMBJoEqAS2BMQE0wThBPAE/gUNBRwFKwU6BUkFWAVnBXcFhgWWBaYFtQXF -BdUF5QX2BgYGFgYnBjcGSAZZBmoGewaMBp0GrwbABtEG4wb1BwcHGQcrBz0HTwdhB3QHhgeZB6wHvwfS -B+UH+AgLCB8IMghGCFoIbgiCCJYIqgi+CNII5wj7CRAJJQk6CU8JZAl5CY8JpAm6Cc8J5Qn7ChEKJwo9 -ClQKagqBCpgKrgrFCtwK8wsLCyILOQtRC2kLgAuYC7ALyAvhC/kMEgwqDEMMXAx1DI4MpwzADNkM8w0N -DSYNQA1aDXQNjg2pDcMN3g34DhMOLg5JDmQOfw6bDrYO0g7uDwkPJQ9BD14Peg+WD7MPzw/sEAkQJhBD -EGEQfhCbELkQ1xD1ERMRMRFPEW0RjBGqEckR6BIHEiYSRRJkEoQSoxLDEuMTAxMjE0MTYxODE6QTxRPl -FAYUJxRJFGoUixStFM4U8BUSFTQVVhV4FZsVvRXgFgMWJhZJFmwWjxayFtYW+hcdF0EXZReJF64X0hf3 -GBsYQBhlGIoYrxjVGPoZIBlFGWsZkRm3Gd0aBBoqGlEadxqeGsUa7BsUGzsbYxuKG7Ib2hwCHCocUhx7 -HKMczBz1HR4dRx1wHZkdwx3sHhYeQB5qHpQevh7pHxMfPh9pH5Qfvx/qIBUgQSBsIJggxCDwIRwhSCF1 -IaEhziH7IiciVSKCIq8i3SMKIzgjZiOUI8Ij8CQfJE0kfCSrJNolCSU4JWgllyXHJfcmJyZXJocmtybo -JxgnSSd6J6sn3CgNKD8ocSiiKNQpBik4KWspnSnQKgIqNSpoKpsqzysCKzYraSudK9EsBSw5LG4soizX -LQwtQS12Last4S4WLkwugi63Lu4vJC9aL5Evxy/+MDUwbDCkMNsxEjFKMYIxujHyMioyYzKbMtQzDTNG -M38zuDPxNCs0ZTSeNNg1EzVNNYc1wjX9Njc2cjauNuk3JDdgN5w31zgUOFA4jDjIOQU5Qjl/Obw5+To2 -OnQ6sjrvOy07azuqO+g8JzxlPKQ84z0iPWE9oT3gPiA+YD6gPuA/IT9hP6I/4kAjQGRApkDnQSlBakGs -Qe5CMEJyQrVC90M6Q31DwEQDREdEikTORRJFVUWaRd5GIkZnRqtG8Ec1R3tHwEgFSEtIkUjXSR1JY0mp -SfBKN0p9SsRLDEtTS5pL4kwqTHJMuk0CTUpNk03cTiVObk63TwBPSU+TT91QJ1BxULtRBlFQUZtR5lIx -UnxSx1MTU19TqlP2VEJUj1TbVShVdVXCVg9WXFapVvdXRFeSV+BYL1h9WMtZGllpWbhaB1pWWqZa9VtF -W5Vb5Vw1XIZc1l0nXXhdyV4aXmxevV8PX2Ffs2AFYFdgqmD8YU9homH1YklinGLwY0Njl2PrZEBklGTp -ZT1lkmXnZj1mkmboZz1nk2fpaD9olmjsaUNpmmnxakhqn2r3a09rp2v/bFdsr20IbWBtuW4SbmtuxG8e -b3hv0XArcIZw4HE6cZVx8HJLcqZzAXNdc7h0FHRwdMx1KHWFdeF2Pnabdvh3VnezeBF4bnjMeSp5iXnn -ekZ6pXsEe2N7wnwhfIF84X1BfaF+AX5ifsJ/I3+Ef+WAR4CogQqBa4HNgjCCkoL0g1eDuoQdhICE44VH -hauGDoZyhteHO4efiASIaYjOiTOJmYn+imSKyoswi5aL/IxjjMqNMY2Yjf+OZo7OjzaPnpAGkG6Q1pE/ -kaiSEZJ6kuOTTZO2lCCUipT0lV+VyZY0lp+XCpd1l+CYTJi4mSSZkJn8mmia1ZtCm6+cHJyJnPedZJ3S -nkCerp8dn4uf+qBpoNihR6G2oiailqMGo3aj5qRWpMelOKWpphqmi6b9p26n4KhSqMSpN6mpqhyqj6sC -q3Wr6axcrNCtRK24ri2uoa8Wr4uwALB1sOqxYLHWskuywrM4s660JbSctRO1irYBtnm28Ldot+C4WbjR -uUq5wro7urW7LrunvCG8m70VvY++Cr6Evv+/er/1wHDA7MFnwePCX8Lbw1jD1MRRxM7FS8XIxkbGw8dB -x7/IPci8yTrJuco4yrfLNsu2zDXMtc01zbXONs62zzfPuNA50LrRPNG+0j/SwdNE08bUSdTL1U7V0dZV -1tjXXNfg2GTY6Nls2fHadtr724DcBdyK3RDdlt4c3qLfKd+v4DbgveFE4cziU+Lb42Pj6+Rz5PzlhOYN -5pbnH+ep6DLovOlG6dDqW+rl63Dr++yG7RHtnO4o7rTvQO/M8Fjw5fFy8f/yjPMZ86f0NPTC9VD13vZt -9vv3ivgZ+Kj5OPnH+lf65/t3/Af8mP0p/br+S/7c/23//9IrLC0uWiRjbGFzc25hbWVYJGNsYXNzZXNf -EBBOU0JpdG1hcEltYWdlUmVwoy0vMFpOU0ltYWdlUmVwWE5TT2JqZWN00issMjNXTlNBcnJheaIyMNIr -LDU2Xk5TTXV0YWJsZUFycmF5ozUyMNM4OQ86OzxXTlNXaGl0ZVxOU0NvbG9yU3BhY2VEMCAwABADgAzS -Kyw+P1dOU0NvbG9yoj4w0issQUJXTlNJbWFnZaJBMAAIABEAGgAkACkAMgA3AEkATABRAFMAYgBoAHUA -fACLAJIAnwCmAK4AsACyALQAuQC7AL0AxADJANQA1gDYANoA3wDiAOQA5gDoAO8BBgEiASQBJhOWE5sT -phOvE8ITxhPRE9oT3xPnE+oT7xP+FAIUCRQRFB4UIxQlFCcULBQ0FDcUPBREAAAAAAAAAgEAAAAAAAAA -QwAAAAAAAAAAAAAAAAAAFEc +S2V5ZWRBcmNoaXZlctEICVRyb290gAGuCwwbHCEaJissMzY5P0JVJG51bGzXDQ4PEBESExQVFhcYGRpW +TlNTaXplViRjbGFzc1xOU0ltYWdlRmxhZ3NWTlNSZXBzW05TVGludENvbG9yV05TQ29sb3JeTlNSZXNp +emluZ01vZGWAAoANEiDDAACAA4AAgAsQAFZ7MSwgMX3SHQ4eIFpOUy5vYmplY3RzoR+ABIAK0h0OIiWi +IySABYAGgAnTDicoKSoaXxAUTlNUSUZGUmVwcmVzZW50YXRpb25fEBlOU0ludGVybmFsTGF5b3V0RGly +ZWN0aW9ugAiAB08REmxNTQAqAAAACgAAABABAAADAAAAAQABAAABAQADAAAAAQABAAABAgADAAAAAgAI +AAgBAwADAAAAAQABAAABBgADAAAAAQABAAABCgADAAAAAQABAAABEQAEAAAAAQAAAAgBEgADAAAAAQAB +AAABFQADAAAAAQACAAABFgADAAAAAQABAAABFwAEAAAAAQAAAAIBHAADAAAAAQABAAABKAADAAAAAQAC +AAABUgADAAAAAQABAAABUwADAAAAAgABAAGHcwAHAAARnAAAANAAAAAAAAARnGFwcGwCAAAAbW50ckdS +QVlYWVogB9wACAAXAA8ALgAPYWNzcEFQUEwAAAAAbm9uZQAAAAAAAAAAAAAAAAAAAAAAAPbWAAEAAAAA +0y1hcHBsAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFZGVzYwAA +AMAAAAB5ZHNjbQAAATwAAAgaY3BydAAACVgAAAAjd3RwdAAACXwAAAAUa1RSQwAACZAAAAgMZGVzYwAA +AAAAAAAfR2VuZXJpYyBHcmF5IEdhbW1hIDIuMiBQcm9maWxlAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAG1s +dWMAAAAAAAAAHwAAAAxza1NLAAAALgAAAYRkYURLAAAAOgAAAbJjYUVTAAAAOAAAAex2aVZOAAAAQAAA +AiRwdEJSAAAASgAAAmR1a1VBAAAALAAAAq5mckZVAAAAPgAAAtpodUhVAAAANAAAAxh6aFRXAAAAGgAA +A0xrb0tSAAAAIgAAA2ZuYk5PAAAAOgAAA4hjc0NaAAAAKAAAA8JoZUlMAAAAJAAAA+pyb1JPAAAAKgAA +BA5kZURFAAAATgAABDhpdElUAAAATgAABIZzdlNFAAAAOAAABNR6aENOAAAAGgAABQxqYUpQAAAAJgAA +BSZlbEdSAAAAKgAABUxwdFBPAAAAUgAABXZubE5MAAAAQAAABchlc0VTAAAATAAABgh0aFRIAAAAMgAA +BlR0clRSAAAAJAAABoZmaUZJAAAARgAABqpockhSAAAAPgAABvBwbFBMAAAASgAABy5hckVHAAAALAAA +B3hydVJVAAAAOgAAB6RlblVTAAAAPAAAB94AVgFhAGUAbwBiAGUAYwBuAOEAIABzAGkAdgDhACAAZwBh +AG0AYQAgADIALAAyAEcAZQBuAGUAcgBpAHMAawAgAGcAcgDlACAAMgAsADIAIABnAGEAbQBtAGEALQBw +AHIAbwBmAGkAbABHAGEAbQBtAGEAIABkAGUAIABnAHIAaQBzAG8AcwAgAGcAZQBuAOgAcgBpAGMAYQAg +ADIALgAyAEMepQB1ACAAaADsAG4AaAAgAE0A4AB1ACAAeADhAG0AIABDAGgAdQBuAGcAIABHAGEAbQBt +AGEAIAAyAC4AMgBQAGUAcgBmAGkAbAAgAEcAZQBuAOkAcgBpAGMAbwAgAGQAYQAgAEcAYQBtAGEAIABk +AGUAIABDAGkAbgB6AGEAcwAgADIALAAyBBcEMAQzBDAEOwRMBD0EMAAgAEcAcgBhAHkALQQzBDAEPAQw +ACAAMgAuADIAUAByAG8AZgBpAGwAIABnAOkAbgDpAHIAaQBxAHUAZQAgAGcAcgBpAHMAIABnAGEAbQBt +AGEAIAAyACwAMgDBAGwAdABhAGwA4QBuAG8AcwAgAHMAegD8AHIAawBlACAAZwBhAG0AbQBhACAAMgAu +ADKQGnUocHCWjlFJXqYAMgAuADKCcl9pY8+P8Md8vBgAINaMwMkAIKwQucgAIAAyAC4AMgAg1QS4XNMM +x3wARwBlAG4AZQByAGkAcwBrACAAZwByAOUAIABnAGEAbQBtAGEAIAAyACwAMgAtAHAAcgBvAGYAaQBs +AE8AYgBlAGMAbgDhACABYQBlAGQA4QAgAGcAYQBtAGEAIAAyAC4AMgXSBdAF3gXUACAF0AXkBdUF6AAg +BdsF3AXcBdkAIAAyAC4AMgBHAGEAbQBhACAAZwByAGkAIABnAGUAbgBlAHIAaQBjAQMAIAAyACwAMgBB +AGwAbABnAGUAbQBlAGkAbgBlAHMAIABHAHIAYQB1AHMAdAB1AGYAZQBuAC0AUAByAG8AZgBpAGwAIABH +AGEAbQBtAGEAIAAyACwAMgBQAHIAbwBmAGkAbABvACAAZwByAGkAZwBpAG8AIABnAGUAbgBlAHIAaQBj +AG8AIABkAGUAbABsAGEAIABnAGEAbQBtAGEAIAAyACwAMgBHAGUAbgBlAHIAaQBzAGsAIABnAHIA5QAg +ADIALAAyACAAZwBhAG0AbQBhAHAAcgBvAGYAaQBsZm6QGnBwXqZ8+2VwADIALgAyY8+P8GWHTvZOAIIs +MLAw7DCkMKww8zDeACAAMgAuADIAIDDXMO0w1TChMKQw6wOTA7UDvQO5A7oDzAAgA5MDugPBA7kAIAOT +A6wDvAO8A7EAIAAyAC4AMgBQAGUAcgBmAGkAbAAgAGcAZQBuAOkAcgBpAGMAbwAgAGQAZQAgAGMAaQBu +AHoAZQBuAHQAbwBzACAAZABhACAARwBhAG0AbQBhACAAMgAsADIAQQBsAGcAZQBtAGUAZQBuACAAZwBy +AGkAagBzACAAZwBhAG0AbQBhACAAMgAsADIALQBwAHIAbwBmAGkAZQBsAFAAZQByAGYAaQBsACAAZwBl +AG4A6QByAGkAYwBvACAAZABlACAAZwBhAG0AbQBhACAAZABlACAAZwByAGkAcwBlAHMAIAAyACwAMg4j +DjEOBw4qDjUOQQ4BDiEOIQ4yDkAOAQ4jDiIOTA4XDjEOSA4nDkQOGwAgADIALgAyAEcAZQBuAGUAbAAg +AEcAcgBpACAARwBhAG0AYQAgADIALAAyAFkAbABlAGkAbgBlAG4AIABoAGEAcgBtAGEAYQBuACAAZwBh +AG0AbQBhACAAMgAsADIAIAAtAHAAcgBvAGYAaQBpAGwAaQBHAGUAbgBlAHIAaQENAGsAaQAgAEcAcgBh +AHkAIABHAGEAbQBtAGEAIAAyAC4AMgAgAHAAcgBvAGYAaQBsAFUAbgBpAHcAZQByAHMAYQBsAG4AeQAg +AHAAcgBvAGYAaQBsACAAcwB6AGEAcgBvAVsAYwBpACAAZwBhAG0AbQBhACAAMgAsADIGOgYnBkUGJwAg +ADIALgAyACAGRAZIBkYAIAYxBkUGJwYvBkoAIAY5BicGRQQeBDEESQQwBE8AIARBBDUEQAQwBE8AIAQz +BDAEPAQ8BDAAIAAyACwAMgAtBD8EQAQ+BEQEOAQ7BEwARwBlAG4AZQByAGkAYwAgAEcAcgBhAHkAIABH +AGEAbQBtAGEAIAAyAC4AMgAgAFAAcgBvAGYAaQBsAGUAAHRleHQAAAAAQ29weXJpZ2h0IEFwcGxlIElu +Yy4sIDIwMTIAAFhZWiAAAAAAAADzUQABAAAAARbMY3VydgAAAAAAAAQAAAAABQAKAA8AFAAZAB4AIwAo +AC0AMgA3ADsAQABFAEoATwBUAFkAXgBjAGgAbQByAHcAfACBAIYAiwCQAJUAmgCfAKQAqQCuALIAtwC8 +AMEAxgDLANAA1QDbAOAA5QDrAPAA9gD7AQEBBwENARMBGQEfASUBKwEyATgBPgFFAUwBUgFZAWABZwFu +AXUBfAGDAYsBkgGaAaEBqQGxAbkBwQHJAdEB2QHhAekB8gH6AgMCDAIUAh0CJgIvAjgCQQJLAlQCXQJn +AnECegKEAo4CmAKiAqwCtgLBAssC1QLgAusC9QMAAwsDFgMhAy0DOANDA08DWgNmA3IDfgOKA5YDogOu +A7oDxwPTA+AD7AP5BAYEEwQgBC0EOwRIBFUEYwRxBH4EjASaBKgEtgTEBNME4QTwBP4FDQUcBSsFOgVJ +BVgFZwV3BYYFlgWmBbUFxQXVBeUF9gYGBhYGJwY3BkgGWQZqBnsGjAadBq8GwAbRBuMG9QcHBxkHKwc9 +B08HYQd0B4YHmQesB78H0gflB/gICwgfCDIIRghaCG4IggiWCKoIvgjSCOcI+wkQCSUJOglPCWQJeQmP +CaQJugnPCeUJ+woRCicKPQpUCmoKgQqYCq4KxQrcCvMLCwsiCzkLUQtpC4ALmAuwC8gL4Qv5DBIMKgxD +DFwMdQyODKcMwAzZDPMNDQ0mDUANWg10DY4NqQ3DDd4N+A4TDi4OSQ5kDn8Omw62DtIO7g8JDyUPQQ9e +D3oPlg+zD88P7BAJECYQQxBhEH4QmxC5ENcQ9RETETERTxFtEYwRqhHJEegSBxImEkUSZBKEEqMSwxLj +EwMTIxNDE2MTgxOkE8UT5RQGFCcUSRRqFIsUrRTOFPAVEhU0FVYVeBWbFb0V4BYDFiYWSRZsFo8WshbW +FvoXHRdBF2UXiReuF9IX9xgbGEAYZRiKGK8Y1Rj6GSAZRRlrGZEZtxndGgQaKhpRGncanhrFGuwbFBs7 +G2MbihuyG9ocAhwqHFIcexyjHMwc9R0eHUcdcB2ZHcMd7B4WHkAeah6UHr4e6R8THz4faR+UH78f6iAV +IEEgbCCYIMQg8CEcIUghdSGhIc4h+yInIlUigiKvIt0jCiM4I2YjlCPCI/AkHyRNJHwkqyTaJQklOCVo +JZclxyX3JicmVyaHJrcm6CcYJ0kneierJ9woDSg/KHEooijUKQYpOClrKZ0p0CoCKjUqaCqbKs8rAis2 +K2krnSvRLAUsOSxuLKIs1y0MLUEtdi2rLeEuFi5MLoIuty7uLyQvWi+RL8cv/jA1MGwwpDDbMRIxSjGC +Mbox8jIqMmMymzLUMw0zRjN/M7gz8TQrNGU0njTYNRM1TTWHNcI1/TY3NnI2rjbpNyQ3YDecN9c4FDhQ +OIw4yDkFOUI5fzm8Ofk6Njp0OrI67zstO2s7qjvoPCc8ZTykPOM9Ij1hPaE94D4gPmA+oD7gPyE/YT+i +P+JAI0BkQKZA50EpQWpBrEHuQjBCckK1QvdDOkN9Q8BEA0RHRIpEzkUSRVVFmkXeRiJGZ0arRvBHNUd7 +R8BIBUhLSJFI10kdSWNJqUnwSjdKfUrESwxLU0uaS+JMKkxyTLpNAk1KTZNN3E4lTm5Ot08AT0lPk0/d +UCdQcVC7UQZRUFGbUeZSMVJ8UsdTE1NfU6pT9lRCVI9U21UoVXVVwlYPVlxWqVb3V0RXklfgWC9YfVjL +WRpZaVm4WgdaVlqmWvVbRVuVW+VcNVyGXNZdJ114XcleGl5sXr1fD19hX7NgBWBXYKpg/GFPYaJh9WJJ +Ypxi8GNDY5dj62RAZJRk6WU9ZZJl52Y9ZpJm6Gc9Z5Nn6Wg/aJZo7GlDaZpp8WpIap9q92tPa6dr/2xX +bK9tCG1gbbluEm5rbsRvHm94b9FwK3CGcOBxOnGVcfByS3KmcwFzXXO4dBR0cHTMdSh1hXXhdj52m3b4 +d1Z3s3gReG54zHkqeYl553pGeqV7BHtje8J8IXyBfOF9QX2hfgF+Yn7CfyN/hH/lgEeAqIEKgWuBzYIw +gpKC9INXg7qEHYSAhOOFR4Wrhg6GcobXhzuHn4gEiGmIzokziZmJ/opkisqLMIuWi/yMY4zKjTGNmI3/ +jmaOzo82j56QBpBukNaRP5GokhGSepLjk02TtpQglIqU9JVflcmWNJaflwqXdZfgmEyYuJkkmZCZ/Jpo +mtWbQpuvnByciZz3nWSd0p5Anq6fHZ+Ln/qgaaDYoUehtqImopajBqN2o+akVqTHpTilqaYapoum/adu +p+CoUqjEqTepqaocqo+rAqt1q+msXKzQrUStuK4trqGvFq+LsACwdbDqsWCx1rJLssKzOLOutCW0nLUT +tYq2AbZ5tvC3aLfguFm40blKucK6O7q1uy67p7whvJu9Fb2Pvgq+hL7/v3q/9cBwwOzBZ8Hjwl/C28NY +w9TEUcTOxUvFyMZGxsPHQce/yD3IvMk6ybnKOMq3yzbLtsw1zLXNNc21zjbOts83z7jQOdC60TzRvtI/ +0sHTRNPG1EnUy9VO1dHWVdbY11zX4Nhk2OjZbNnx2nba+9uA3AXcit0Q3ZbeHN6i3ynfr+A24L3hROHM +4lPi2+Nj4+vkc+T85YTmDeaW5x/nqegy6LzpRunQ6lvq5etw6/vshu0R7ZzuKO6070DvzPBY8OXxcvH/ +8ozzGfOn9DT0wvVQ9d72bfb794r4Gfio+Tj5x/pX+uf7d/wH/Jj9Kf26/kv+3P9t///SLS4vMFokY2xh +c3NuYW1lWCRjbGFzc2VzXxAQTlNCaXRtYXBJbWFnZVJlcKMvMTJaTlNJbWFnZVJlcFhOU09iamVjdNIt +LjQ1V05TQXJyYXmiNDLSLS43OF5OU011dGFibGVBcnJheaM3NDLTOjsOPD0+V05TV2hpdGVcTlNDb2xv +clNwYWNlRDAgMAAQA4AM0i0uQEFXTlNDb2xvcqJAMtItLkNEV05TSW1hZ2WiQzIACAARABoAJAApADIA +NwBJAEwAUQBTAGIAaAB3AH4AhQCSAJkApQCtALwAvgDAAMUAxwDJAMsAzQDUANkA5ADmAOgA6gDvAPIA +9AD2APgA/wEWATIBNAE2E6YTqxO2E78T0hPWE+ET6hPvE/cT+hP/FA4UEhQZFCEULhQzFDUUNxQ8FEQU +RxRMFFQAAAAAAAACAQAAAAAAAABFAAAAAAAAAAAAAAAAAAAUVw diff --git a/i18n/src/main/resources/fr.lproj/Command.xib b/i18n/src/main/resources/fr.lproj/Command.xib index 418acff497..a3fd620f90 100644 --- a/i18n/src/main/resources/fr.lproj/Command.xib +++ b/i18n/src/main/resources/fr.lproj/Command.xib @@ -1,7 +1,7 @@ - + - + diff --git a/i18n/src/main/resources/fr.lproj/Connection.xib b/i18n/src/main/resources/fr.lproj/Connection.xib index d0a9fee939..1020577c63 100644 --- a/i18n/src/main/resources/fr.lproj/Connection.xib +++ b/i18n/src/main/resources/fr.lproj/Connection.xib @@ -1,7 +1,7 @@ - + - + @@ -76,7 +76,7 @@ - + diff --git a/i18n/src/main/resources/fr.lproj/Info.xib b/i18n/src/main/resources/fr.lproj/Info.xib index cd49253fe4..1273621c69 100644 --- a/i18n/src/main/resources/fr.lproj/Info.xib +++ b/i18n/src/main/resources/fr.lproj/Info.xib @@ -1,7 +1,7 @@ - + - + @@ -89,7 +89,7 @@ - + @@ -1018,21 +1018,21 @@ - + - + - + @@ -1122,21 +1122,21 @@ - + - + - + @@ -1251,27 +1251,27 @@ diff --git a/i18n/src/main/resources/fr.lproj/Login.strings b/i18n/src/main/resources/fr.lproj/Login.strings index 31ff84c2e7..320b3f4109 100644 Binary files a/i18n/src/main/resources/fr.lproj/Login.strings and b/i18n/src/main/resources/fr.lproj/Login.strings differ diff --git a/i18n/src/main/resources/fr.lproj/Main.xib b/i18n/src/main/resources/fr.lproj/Main.xib index ce52f50507..2ae5c07dad 100644 --- a/i18n/src/main/resources/fr.lproj/Main.xib +++ b/i18n/src/main/resources/fr.lproj/Main.xib @@ -1,7 +1,7 @@ - + - + @@ -159,19 +159,19 @@ - + - + - + @@ -321,12 +321,12 @@ CA - + - + @@ -566,6 +566,10 @@ CA - + + + + + diff --git a/i18n/src/main/resources/fr.lproj/Preferences.xib b/i18n/src/main/resources/fr.lproj/Preferences.xib index ef8c44fdc3..7882ea254e 100644 --- a/i18n/src/main/resources/fr.lproj/Preferences.xib +++ b/i18n/src/main/resources/fr.lproj/Preferences.xib @@ -1,7 +1,7 @@ - + - + @@ -266,7 +266,7 @@ - + @@ -307,7 +307,7 @@ - + @@ -539,7 +539,7 @@ - + @@ -579,7 +579,7 @@ - + @@ -614,7 +614,7 @@ - + @@ -643,7 +643,7 @@ - + @@ -1219,7 +1219,7 @@ - + @@ -1251,7 +1251,7 @@ - + @@ -1291,7 +1291,7 @@ - + @@ -1364,7 +1364,7 @@ - + @@ -1559,7 +1559,7 @@ - + @@ -1619,7 +1619,7 @@ - + @@ -1644,7 +1644,7 @@ - + @@ -1686,7 +1686,7 @@ - + @@ -1714,7 +1714,7 @@ - + @@ -1742,7 +1742,7 @@ - + @@ -1771,7 +1771,7 @@ - + @@ -1806,7 +1806,7 @@ - + @@ -1890,7 +1890,7 @@ - + @@ -1949,7 +1949,7 @@ - + @@ -1977,7 +1977,7 @@ - + @@ -2006,7 +2006,7 @@ - + diff --git a/i18n/src/main/resources/fr.lproj/Profiles.xib b/i18n/src/main/resources/fr.lproj/Profiles.xib index 5bee209243..21cc6f49de 100644 --- a/i18n/src/main/resources/fr.lproj/Profiles.xib +++ b/i18n/src/main/resources/fr.lproj/Profiles.xib @@ -1,7 +1,7 @@ - + - + diff --git a/i18n/src/main/resources/fr.lproj/Prompt.xib b/i18n/src/main/resources/fr.lproj/Prompt.xib index 7c4ba62024..21348bb5f9 100644 --- a/i18n/src/main/resources/fr.lproj/Prompt.xib +++ b/i18n/src/main/resources/fr.lproj/Prompt.xib @@ -1,7 +1,7 @@ - + - + @@ -95,7 +95,7 @@ Gw - + diff --git a/i18n/src/main/resources/fr.lproj/S3.strings b/i18n/src/main/resources/fr.lproj/S3.strings index 304b432abc..35b996afeb 100644 Binary files a/i18n/src/main/resources/fr.lproj/S3.strings and b/i18n/src/main/resources/fr.lproj/S3.strings differ diff --git a/i18n/src/main/resources/fr.lproj/Updater.strings b/i18n/src/main/resources/fr.lproj/Updater.strings index a637e5d4ae..55c93ab79c 100644 Binary files a/i18n/src/main/resources/fr.lproj/Updater.strings and b/i18n/src/main/resources/fr.lproj/Updater.strings differ diff --git a/i18n/src/main/resources/he.lproj/Bookmark.xib b/i18n/src/main/resources/he.lproj/Bookmark.xib index aaacc97d30..493ee854d8 100644 --- a/i18n/src/main/resources/he.lproj/Bookmark.xib +++ b/i18n/src/main/resources/he.lproj/Bookmark.xib @@ -1,7 +1,7 @@ - + - + @@ -92,7 +92,7 @@ - + @@ -489,95 +489,95 @@ YnBsaXN0MDDUAQIDBAUGBwpYJHZlcnNpb25ZJGFyY2hpdmVyVCR0b3BYJG9iamVjdHMSAAGGoF8QD05T -S2V5ZWRBcmNoaXZlctEICVRyb290gAGuCwwZGh8UJCkqMTQ3PUBVJG51bGzWDQ4PEBESExQVFhcYVk5T -U2l6ZV5OU1Jlc2l6aW5nTW9kZVYkY2xhc3NcTlNJbWFnZUZsYWdzVk5TUmVwc1dOU0NvbG9ygAIQAIAN -EiDDAACAA4ALVnsxLCAxfdIbDxweWk5TLm9iamVjdHOhHYAEgArSGw8gI6IhIoAFgAaACdMPJSYnKBRf -EBROU1RJRkZSZXByZXNlbnRhdGlvbl8QGU5TSW50ZXJuYWxMYXlvdXREaXJlY3Rpb26ACIAHTxESbE1N -ACoAAAAKAAAAEAEAAAMAAAABAAEAAAEBAAMAAAABAAEAAAECAAMAAAACAAgACAEDAAMAAAABAAEAAAEG -AAMAAAABAAEAAAEKAAMAAAABAAEAAAERAAQAAAABAAAACAESAAMAAAABAAEAAAEVAAMAAAABAAIAAAEW -AAMAAAABAAEAAAEXAAQAAAABAAAAAgEcAAMAAAABAAEAAAEoAAMAAAABAAIAAAFSAAMAAAABAAEAAAFT -AAMAAAACAAEAAYdzAAcAABGcAAAA0AAAAAAAABGcYXBwbAIAAABtbnRyR1JBWVhZWiAH3AAIABcADwAu -AA9hY3NwQVBQTAAAAABub25lAAAAAAAAAAAAAAAAAAAAAAAA9tYAAQAAAADTLWFwcGwAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAVkZXNjAAAAwAAAAHlkc2NtAAABPAAA -CBpjcHJ0AAAJWAAAACN3dHB0AAAJfAAAABRrVFJDAAAJkAAACAxkZXNjAAAAAAAAAB9HZW5lcmljIEdy -YXkgR2FtbWEgMi4yIFByb2ZpbGUAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAbWx1YwAAAAAAAAAfAAAADHNr -U0sAAAAuAAABhGRhREsAAAA6AAABsmNhRVMAAAA4AAAB7HZpVk4AAABAAAACJHB0QlIAAABKAAACZHVr -VUEAAAAsAAACrmZyRlUAAAA+AAAC2mh1SFUAAAA0AAADGHpoVFcAAAAaAAADTGtvS1IAAAAiAAADZm5i -Tk8AAAA6AAADiGNzQ1oAAAAoAAADwmhlSUwAAAAkAAAD6nJvUk8AAAAqAAAEDmRlREUAAABOAAAEOGl0 -SVQAAABOAAAEhnN2U0UAAAA4AAAE1HpoQ04AAAAaAAAFDGphSlAAAAAmAAAFJmVsR1IAAAAqAAAFTHB0 -UE8AAABSAAAFdm5sTkwAAABAAAAFyGVzRVMAAABMAAAGCHRoVEgAAAAyAAAGVHRyVFIAAAAkAAAGhmZp -RkkAAABGAAAGqmhySFIAAAA+AAAG8HBsUEwAAABKAAAHLmFyRUcAAAAsAAAHeHJ1UlUAAAA6AAAHpGVu -VVMAAAA8AAAH3gBWAWEAZQBvAGIAZQBjAG4A4QAgAHMAaQB2AOEAIABnAGEAbQBhACAAMgAsADIARwBl -AG4AZQByAGkAcwBrACAAZwByAOUAIAAyACwAMgAgAGcAYQBtAG0AYQAtAHAAcgBvAGYAaQBsAEcAYQBt -AG0AYQAgAGQAZQAgAGcAcgBpAHMAbwBzACAAZwBlAG4A6AByAGkAYwBhACAAMgAuADIAQx6lAHUAIABo -AOwAbgBoACAATQDgAHUAIAB4AOEAbQAgAEMAaAB1AG4AZwAgAEcAYQBtAG0AYQAgADIALgAyAFAAZQBy -AGYAaQBsACAARwBlAG4A6QByAGkAYwBvACAAZABhACAARwBhAG0AYQAgAGQAZQAgAEMAaQBuAHoAYQBz -ACAAMgAsADIEFwQwBDMEMAQ7BEwEPQQwACAARwByAGEAeQAtBDMEMAQ8BDAAIAAyAC4AMgBQAHIAbwBm -AGkAbAAgAGcA6QBuAOkAcgBpAHEAdQBlACAAZwByAGkAcwAgAGcAYQBtAG0AYQAgADIALAAyAMEAbAB0 -AGEAbADhAG4AbwBzACAAcwB6APwAcgBrAGUAIABnAGEAbQBtAGEAIAAyAC4AMpAadShwcJaOUUlepgAy -AC4AMoJyX2ljz4/wx3y8GAAg1ozAyQAgrBC5yAAgADIALgAyACDVBLhc0wzHfABHAGUAbgBlAHIAaQBz -AGsAIABnAHIA5QAgAGcAYQBtAG0AYQAgADIALAAyAC0AcAByAG8AZgBpAGwATwBiAGUAYwBuAOEAIAFh -AGUAZADhACAAZwBhAG0AYQAgADIALgAyBdIF0AXeBdQAIAXQBeQF1QXoACAF2wXcBdwF2QAgADIALgAy -AEcAYQBtAGEAIABnAHIAaQAgAGcAZQBuAGUAcgBpAGMBAwAgADIALAAyAEEAbABsAGcAZQBtAGUAaQBu -AGUAcwAgAEcAcgBhAHUAcwB0AHUAZgBlAG4ALQBQAHIAbwBmAGkAbAAgAEcAYQBtAG0AYQAgADIALAAy -AFAAcgBvAGYAaQBsAG8AIABnAHIAaQBnAGkAbwAgAGcAZQBuAGUAcgBpAGMAbwAgAGQAZQBsAGwAYQAg -AGcAYQBtAG0AYQAgADIALAAyAEcAZQBuAGUAcgBpAHMAawAgAGcAcgDlACAAMgAsADIAIABnAGEAbQBt -AGEAcAByAG8AZgBpAGxmbpAacHBepnz7ZXAAMgAuADJjz4/wZYdO9k4AgiwwsDDsMKQwrDDzMN4AIAAy -AC4AMgAgMNcw7TDVMKEwpDDrA5MDtQO9A7kDugPMACADkwO6A8EDuQAgA5MDrAO8A7wDsQAgADIALgAy -AFAAZQByAGYAaQBsACAAZwBlAG4A6QByAGkAYwBvACAAZABlACAAYwBpAG4AegBlAG4AdABvAHMAIABk -AGEAIABHAGEAbQBtAGEAIAAyACwAMgBBAGwAZwBlAG0AZQBlAG4AIABnAHIAaQBqAHMAIABnAGEAbQBt -AGEAIAAyACwAMgAtAHAAcgBvAGYAaQBlAGwAUABlAHIAZgBpAGwAIABnAGUAbgDpAHIAaQBjAG8AIABk -AGUAIABnAGEAbQBtAGEAIABkAGUAIABnAHIAaQBzAGUAcwAgADIALAAyDiMOMQ4HDioONQ5BDgEOIQ4h -DjIOQA4BDiMOIg5MDhcOMQ5IDicORA4bACAAMgAuADIARwBlAG4AZQBsACAARwByAGkAIABHAGEAbQBh -ACAAMgAsADIAWQBsAGUAaQBuAGUAbgAgAGgAYQByAG0AYQBhAG4AIABnAGEAbQBtAGEAIAAyACwAMgAg -AC0AcAByAG8AZgBpAGkAbABpAEcAZQBuAGUAcgBpAQ0AawBpACAARwByAGEAeQAgAEcAYQBtAG0AYQAg -ADIALgAyACAAcAByAG8AZgBpAGwAVQBuAGkAdwBlAHIAcwBhAGwAbgB5ACAAcAByAG8AZgBpAGwAIABz -AHoAYQByAG8BWwBjAGkAIABnAGEAbQBtAGEAIAAyACwAMgY6BicGRQYnACAAMgAuADIAIAZEBkgGRgAg -BjEGRQYnBi8GSgAgBjkGJwZFBB4EMQRJBDAETwAgBEEENQRABDAETwAgBDMEMAQ8BDwEMAAgADIALAAy -AC0EPwRABD4ERAQ4BDsETABHAGUAbgBlAHIAaQBjACAARwByAGEAeQAgAEcAYQBtAG0AYQAgADIALgAy -ACAAUAByAG8AZgBpAGwAZQAAdGV4dAAAAABDb3B5cmlnaHQgQXBwbGUgSW5jLiwgMjAxMgAAWFlaIAAA -AAAAAPNRAAEAAAABFsxjdXJ2AAAAAAAABAAAAAAFAAoADwAUABkAHgAjACgALQAyADcAOwBAAEUASgBP -AFQAWQBeAGMAaABtAHIAdwB8AIEAhgCLAJAAlQCaAJ8ApACpAK4AsgC3ALwAwQDGAMsA0ADVANsA4ADl -AOsA8AD2APsBAQEHAQ0BEwEZAR8BJQErATIBOAE+AUUBTAFSAVkBYAFnAW4BdQF8AYMBiwGSAZoBoQGp -AbEBuQHBAckB0QHZAeEB6QHyAfoCAwIMAhQCHQImAi8COAJBAksCVAJdAmcCcQJ6AoQCjgKYAqICrAK2 -AsECywLVAuAC6wL1AwADCwMWAyEDLQM4A0MDTwNaA2YDcgN+A4oDlgOiA64DugPHA9MD4APsA/kEBgQT -BCAELQQ7BEgEVQRjBHEEfgSMBJoEqAS2BMQE0wThBPAE/gUNBRwFKwU6BUkFWAVnBXcFhgWWBaYFtQXF -BdUF5QX2BgYGFgYnBjcGSAZZBmoGewaMBp0GrwbABtEG4wb1BwcHGQcrBz0HTwdhB3QHhgeZB6wHvwfS -B+UH+AgLCB8IMghGCFoIbgiCCJYIqgi+CNII5wj7CRAJJQk6CU8JZAl5CY8JpAm6Cc8J5Qn7ChEKJwo9 -ClQKagqBCpgKrgrFCtwK8wsLCyILOQtRC2kLgAuYC7ALyAvhC/kMEgwqDEMMXAx1DI4MpwzADNkM8w0N -DSYNQA1aDXQNjg2pDcMN3g34DhMOLg5JDmQOfw6bDrYO0g7uDwkPJQ9BD14Peg+WD7MPzw/sEAkQJhBD -EGEQfhCbELkQ1xD1ERMRMRFPEW0RjBGqEckR6BIHEiYSRRJkEoQSoxLDEuMTAxMjE0MTYxODE6QTxRPl -FAYUJxRJFGoUixStFM4U8BUSFTQVVhV4FZsVvRXgFgMWJhZJFmwWjxayFtYW+hcdF0EXZReJF64X0hf3 -GBsYQBhlGIoYrxjVGPoZIBlFGWsZkRm3Gd0aBBoqGlEadxqeGsUa7BsUGzsbYxuKG7Ib2hwCHCocUhx7 -HKMczBz1HR4dRx1wHZkdwx3sHhYeQB5qHpQevh7pHxMfPh9pH5Qfvx/qIBUgQSBsIJggxCDwIRwhSCF1 -IaEhziH7IiciVSKCIq8i3SMKIzgjZiOUI8Ij8CQfJE0kfCSrJNolCSU4JWgllyXHJfcmJyZXJocmtybo -JxgnSSd6J6sn3CgNKD8ocSiiKNQpBik4KWspnSnQKgIqNSpoKpsqzysCKzYraSudK9EsBSw5LG4soizX -LQwtQS12Last4S4WLkwugi63Lu4vJC9aL5Evxy/+MDUwbDCkMNsxEjFKMYIxujHyMioyYzKbMtQzDTNG -M38zuDPxNCs0ZTSeNNg1EzVNNYc1wjX9Njc2cjauNuk3JDdgN5w31zgUOFA4jDjIOQU5Qjl/Obw5+To2 -OnQ6sjrvOy07azuqO+g8JzxlPKQ84z0iPWE9oT3gPiA+YD6gPuA/IT9hP6I/4kAjQGRApkDnQSlBakGs -Qe5CMEJyQrVC90M6Q31DwEQDREdEikTORRJFVUWaRd5GIkZnRqtG8Ec1R3tHwEgFSEtIkUjXSR1JY0mp -SfBKN0p9SsRLDEtTS5pL4kwqTHJMuk0CTUpNk03cTiVObk63TwBPSU+TT91QJ1BxULtRBlFQUZtR5lIx -UnxSx1MTU19TqlP2VEJUj1TbVShVdVXCVg9WXFapVvdXRFeSV+BYL1h9WMtZGllpWbhaB1pWWqZa9VtF -W5Vb5Vw1XIZc1l0nXXhdyV4aXmxevV8PX2Ffs2AFYFdgqmD8YU9homH1YklinGLwY0Njl2PrZEBklGTp -ZT1lkmXnZj1mkmboZz1nk2fpaD9olmjsaUNpmmnxakhqn2r3a09rp2v/bFdsr20IbWBtuW4SbmtuxG8e -b3hv0XArcIZw4HE6cZVx8HJLcqZzAXNdc7h0FHRwdMx1KHWFdeF2Pnabdvh3VnezeBF4bnjMeSp5iXnn -ekZ6pXsEe2N7wnwhfIF84X1BfaF+AX5ifsJ/I3+Ef+WAR4CogQqBa4HNgjCCkoL0g1eDuoQdhICE44VH -hauGDoZyhteHO4efiASIaYjOiTOJmYn+imSKyoswi5aL/IxjjMqNMY2Yjf+OZo7OjzaPnpAGkG6Q1pE/ -kaiSEZJ6kuOTTZO2lCCUipT0lV+VyZY0lp+XCpd1l+CYTJi4mSSZkJn8mmia1ZtCm6+cHJyJnPedZJ3S -nkCerp8dn4uf+qBpoNihR6G2oiailqMGo3aj5qRWpMelOKWpphqmi6b9p26n4KhSqMSpN6mpqhyqj6sC -q3Wr6axcrNCtRK24ri2uoa8Wr4uwALB1sOqxYLHWskuywrM4s660JbSctRO1irYBtnm28Ldot+C4WbjR -uUq5wro7urW7LrunvCG8m70VvY++Cr6Evv+/er/1wHDA7MFnwePCX8Lbw1jD1MRRxM7FS8XIxkbGw8dB -x7/IPci8yTrJuco4yrfLNsu2zDXMtc01zbXONs62zzfPuNA50LrRPNG+0j/SwdNE08bUSdTL1U7V0dZV -1tjXXNfg2GTY6Nls2fHadtr724DcBdyK3RDdlt4c3qLfKd+v4DbgveFE4cziU+Lb42Pj6+Rz5PzlhOYN -5pbnH+ep6DLovOlG6dDqW+rl63Dr++yG7RHtnO4o7rTvQO/M8Fjw5fFy8f/yjPMZ86f0NPTC9VD13vZt -9vv3ivgZ+Kj5OPnH+lf65/t3/Af8mP0p/br+S/7c/23//9IrLC0uWiRjbGFzc25hbWVYJGNsYXNzZXNf -EBBOU0JpdG1hcEltYWdlUmVwoy0vMFpOU0ltYWdlUmVwWE5TT2JqZWN00issMjNXTlNBcnJheaIyMNIr -LDU2Xk5TTXV0YWJsZUFycmF5ozUyMNM4OQ86OzxXTlNXaGl0ZVxOU0NvbG9yU3BhY2VEMCAwABADgAzS -Kyw+P1dOU0NvbG9yoj4w0issQUJXTlNJbWFnZaJBMAAIABEAGgAkACkAMgA3AEkATABRAFMAYgBoAHUA -fACLAJIAnwCmAK4AsACyALQAuQC7AL0AxADJANQA1gDYANoA3wDiAOQA5gDoAO8BBgEiASQBJhOWE5sT -phOvE8ITxhPRE9oT3xPnE+oT7xP+FAIUCRQRFB4UIxQlFCcULBQ0FDcUPBREAAAAAAAAAgEAAAAAAAAA -QwAAAAAAAAAAAAAAAAAAFEc +S2V5ZWRBcmNoaXZlctEICVRyb290gAGuCwwbHCEaJissMzY5P0JVJG51bGzXDQ4PEBESExQVFhcYGRpW +TlNTaXplViRjbGFzc1xOU0ltYWdlRmxhZ3NWTlNSZXBzW05TVGludENvbG9yV05TQ29sb3JeTlNSZXNp +emluZ01vZGWAAoANEiDDAACAA4AAgAsQAFZ7MSwgMX3SHQ4eIFpOUy5vYmplY3RzoR+ABIAK0h0OIiWi +IySABYAGgAnTDicoKSoaXxAUTlNUSUZGUmVwcmVzZW50YXRpb25fEBlOU0ludGVybmFsTGF5b3V0RGly +ZWN0aW9ugAiAB08REmxNTQAqAAAACgAAABABAAADAAAAAQABAAABAQADAAAAAQABAAABAgADAAAAAgAI +AAgBAwADAAAAAQABAAABBgADAAAAAQABAAABCgADAAAAAQABAAABEQAEAAAAAQAAAAgBEgADAAAAAQAB +AAABFQADAAAAAQACAAABFgADAAAAAQABAAABFwAEAAAAAQAAAAIBHAADAAAAAQABAAABKAADAAAAAQAC +AAABUgADAAAAAQABAAABUwADAAAAAgABAAGHcwAHAAARnAAAANAAAAAAAAARnGFwcGwCAAAAbW50ckdS +QVlYWVogB9wACAAXAA8ALgAPYWNzcEFQUEwAAAAAbm9uZQAAAAAAAAAAAAAAAAAAAAAAAPbWAAEAAAAA +0y1hcHBsAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFZGVzYwAA +AMAAAAB5ZHNjbQAAATwAAAgaY3BydAAACVgAAAAjd3RwdAAACXwAAAAUa1RSQwAACZAAAAgMZGVzYwAA +AAAAAAAfR2VuZXJpYyBHcmF5IEdhbW1hIDIuMiBQcm9maWxlAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAG1s +dWMAAAAAAAAAHwAAAAxza1NLAAAALgAAAYRkYURLAAAAOgAAAbJjYUVTAAAAOAAAAex2aVZOAAAAQAAA +AiRwdEJSAAAASgAAAmR1a1VBAAAALAAAAq5mckZVAAAAPgAAAtpodUhVAAAANAAAAxh6aFRXAAAAGgAA +A0xrb0tSAAAAIgAAA2ZuYk5PAAAAOgAAA4hjc0NaAAAAKAAAA8JoZUlMAAAAJAAAA+pyb1JPAAAAKgAA +BA5kZURFAAAATgAABDhpdElUAAAATgAABIZzdlNFAAAAOAAABNR6aENOAAAAGgAABQxqYUpQAAAAJgAA +BSZlbEdSAAAAKgAABUxwdFBPAAAAUgAABXZubE5MAAAAQAAABchlc0VTAAAATAAABgh0aFRIAAAAMgAA +BlR0clRSAAAAJAAABoZmaUZJAAAARgAABqpockhSAAAAPgAABvBwbFBMAAAASgAABy5hckVHAAAALAAA +B3hydVJVAAAAOgAAB6RlblVTAAAAPAAAB94AVgFhAGUAbwBiAGUAYwBuAOEAIABzAGkAdgDhACAAZwBh +AG0AYQAgADIALAAyAEcAZQBuAGUAcgBpAHMAawAgAGcAcgDlACAAMgAsADIAIABnAGEAbQBtAGEALQBw +AHIAbwBmAGkAbABHAGEAbQBtAGEAIABkAGUAIABnAHIAaQBzAG8AcwAgAGcAZQBuAOgAcgBpAGMAYQAg +ADIALgAyAEMepQB1ACAAaADsAG4AaAAgAE0A4AB1ACAAeADhAG0AIABDAGgAdQBuAGcAIABHAGEAbQBt +AGEAIAAyAC4AMgBQAGUAcgBmAGkAbAAgAEcAZQBuAOkAcgBpAGMAbwAgAGQAYQAgAEcAYQBtAGEAIABk +AGUAIABDAGkAbgB6AGEAcwAgADIALAAyBBcEMAQzBDAEOwRMBD0EMAAgAEcAcgBhAHkALQQzBDAEPAQw +ACAAMgAuADIAUAByAG8AZgBpAGwAIABnAOkAbgDpAHIAaQBxAHUAZQAgAGcAcgBpAHMAIABnAGEAbQBt +AGEAIAAyACwAMgDBAGwAdABhAGwA4QBuAG8AcwAgAHMAegD8AHIAawBlACAAZwBhAG0AbQBhACAAMgAu +ADKQGnUocHCWjlFJXqYAMgAuADKCcl9pY8+P8Md8vBgAINaMwMkAIKwQucgAIAAyAC4AMgAg1QS4XNMM +x3wARwBlAG4AZQByAGkAcwBrACAAZwByAOUAIABnAGEAbQBtAGEAIAAyACwAMgAtAHAAcgBvAGYAaQBs +AE8AYgBlAGMAbgDhACABYQBlAGQA4QAgAGcAYQBtAGEAIAAyAC4AMgXSBdAF3gXUACAF0AXkBdUF6AAg +BdsF3AXcBdkAIAAyAC4AMgBHAGEAbQBhACAAZwByAGkAIABnAGUAbgBlAHIAaQBjAQMAIAAyACwAMgBB +AGwAbABnAGUAbQBlAGkAbgBlAHMAIABHAHIAYQB1AHMAdAB1AGYAZQBuAC0AUAByAG8AZgBpAGwAIABH +AGEAbQBtAGEAIAAyACwAMgBQAHIAbwBmAGkAbABvACAAZwByAGkAZwBpAG8AIABnAGUAbgBlAHIAaQBj +AG8AIABkAGUAbABsAGEAIABnAGEAbQBtAGEAIAAyACwAMgBHAGUAbgBlAHIAaQBzAGsAIABnAHIA5QAg +ADIALAAyACAAZwBhAG0AbQBhAHAAcgBvAGYAaQBsZm6QGnBwXqZ8+2VwADIALgAyY8+P8GWHTvZOAIIs +MLAw7DCkMKww8zDeACAAMgAuADIAIDDXMO0w1TChMKQw6wOTA7UDvQO5A7oDzAAgA5MDugPBA7kAIAOT +A6wDvAO8A7EAIAAyAC4AMgBQAGUAcgBmAGkAbAAgAGcAZQBuAOkAcgBpAGMAbwAgAGQAZQAgAGMAaQBu +AHoAZQBuAHQAbwBzACAAZABhACAARwBhAG0AbQBhACAAMgAsADIAQQBsAGcAZQBtAGUAZQBuACAAZwBy +AGkAagBzACAAZwBhAG0AbQBhACAAMgAsADIALQBwAHIAbwBmAGkAZQBsAFAAZQByAGYAaQBsACAAZwBl +AG4A6QByAGkAYwBvACAAZABlACAAZwBhAG0AbQBhACAAZABlACAAZwByAGkAcwBlAHMAIAAyACwAMg4j +DjEOBw4qDjUOQQ4BDiEOIQ4yDkAOAQ4jDiIOTA4XDjEOSA4nDkQOGwAgADIALgAyAEcAZQBuAGUAbAAg +AEcAcgBpACAARwBhAG0AYQAgADIALAAyAFkAbABlAGkAbgBlAG4AIABoAGEAcgBtAGEAYQBuACAAZwBh +AG0AbQBhACAAMgAsADIAIAAtAHAAcgBvAGYAaQBpAGwAaQBHAGUAbgBlAHIAaQENAGsAaQAgAEcAcgBh +AHkAIABHAGEAbQBtAGEAIAAyAC4AMgAgAHAAcgBvAGYAaQBsAFUAbgBpAHcAZQByAHMAYQBsAG4AeQAg +AHAAcgBvAGYAaQBsACAAcwB6AGEAcgBvAVsAYwBpACAAZwBhAG0AbQBhACAAMgAsADIGOgYnBkUGJwAg +ADIALgAyACAGRAZIBkYAIAYxBkUGJwYvBkoAIAY5BicGRQQeBDEESQQwBE8AIARBBDUEQAQwBE8AIAQz +BDAEPAQ8BDAAIAAyACwAMgAtBD8EQAQ+BEQEOAQ7BEwARwBlAG4AZQByAGkAYwAgAEcAcgBhAHkAIABH +AGEAbQBtAGEAIAAyAC4AMgAgAFAAcgBvAGYAaQBsAGUAAHRleHQAAAAAQ29weXJpZ2h0IEFwcGxlIElu +Yy4sIDIwMTIAAFhZWiAAAAAAAADzUQABAAAAARbMY3VydgAAAAAAAAQAAAAABQAKAA8AFAAZAB4AIwAo +AC0AMgA3ADsAQABFAEoATwBUAFkAXgBjAGgAbQByAHcAfACBAIYAiwCQAJUAmgCfAKQAqQCuALIAtwC8 +AMEAxgDLANAA1QDbAOAA5QDrAPAA9gD7AQEBBwENARMBGQEfASUBKwEyATgBPgFFAUwBUgFZAWABZwFu +AXUBfAGDAYsBkgGaAaEBqQGxAbkBwQHJAdEB2QHhAekB8gH6AgMCDAIUAh0CJgIvAjgCQQJLAlQCXQJn +AnECegKEAo4CmAKiAqwCtgLBAssC1QLgAusC9QMAAwsDFgMhAy0DOANDA08DWgNmA3IDfgOKA5YDogOu +A7oDxwPTA+AD7AP5BAYEEwQgBC0EOwRIBFUEYwRxBH4EjASaBKgEtgTEBNME4QTwBP4FDQUcBSsFOgVJ +BVgFZwV3BYYFlgWmBbUFxQXVBeUF9gYGBhYGJwY3BkgGWQZqBnsGjAadBq8GwAbRBuMG9QcHBxkHKwc9 +B08HYQd0B4YHmQesB78H0gflB/gICwgfCDIIRghaCG4IggiWCKoIvgjSCOcI+wkQCSUJOglPCWQJeQmP +CaQJugnPCeUJ+woRCicKPQpUCmoKgQqYCq4KxQrcCvMLCwsiCzkLUQtpC4ALmAuwC8gL4Qv5DBIMKgxD +DFwMdQyODKcMwAzZDPMNDQ0mDUANWg10DY4NqQ3DDd4N+A4TDi4OSQ5kDn8Omw62DtIO7g8JDyUPQQ9e +D3oPlg+zD88P7BAJECYQQxBhEH4QmxC5ENcQ9RETETERTxFtEYwRqhHJEegSBxImEkUSZBKEEqMSwxLj +EwMTIxNDE2MTgxOkE8UT5RQGFCcUSRRqFIsUrRTOFPAVEhU0FVYVeBWbFb0V4BYDFiYWSRZsFo8WshbW +FvoXHRdBF2UXiReuF9IX9xgbGEAYZRiKGK8Y1Rj6GSAZRRlrGZEZtxndGgQaKhpRGncanhrFGuwbFBs7 +G2MbihuyG9ocAhwqHFIcexyjHMwc9R0eHUcdcB2ZHcMd7B4WHkAeah6UHr4e6R8THz4faR+UH78f6iAV +IEEgbCCYIMQg8CEcIUghdSGhIc4h+yInIlUigiKvIt0jCiM4I2YjlCPCI/AkHyRNJHwkqyTaJQklOCVo +JZclxyX3JicmVyaHJrcm6CcYJ0kneierJ9woDSg/KHEooijUKQYpOClrKZ0p0CoCKjUqaCqbKs8rAis2 +K2krnSvRLAUsOSxuLKIs1y0MLUEtdi2rLeEuFi5MLoIuty7uLyQvWi+RL8cv/jA1MGwwpDDbMRIxSjGC +Mbox8jIqMmMymzLUMw0zRjN/M7gz8TQrNGU0njTYNRM1TTWHNcI1/TY3NnI2rjbpNyQ3YDecN9c4FDhQ +OIw4yDkFOUI5fzm8Ofk6Njp0OrI67zstO2s7qjvoPCc8ZTykPOM9Ij1hPaE94D4gPmA+oD7gPyE/YT+i +P+JAI0BkQKZA50EpQWpBrEHuQjBCckK1QvdDOkN9Q8BEA0RHRIpEzkUSRVVFmkXeRiJGZ0arRvBHNUd7 +R8BIBUhLSJFI10kdSWNJqUnwSjdKfUrESwxLU0uaS+JMKkxyTLpNAk1KTZNN3E4lTm5Ot08AT0lPk0/d +UCdQcVC7UQZRUFGbUeZSMVJ8UsdTE1NfU6pT9lRCVI9U21UoVXVVwlYPVlxWqVb3V0RXklfgWC9YfVjL +WRpZaVm4WgdaVlqmWvVbRVuVW+VcNVyGXNZdJ114XcleGl5sXr1fD19hX7NgBWBXYKpg/GFPYaJh9WJJ +Ypxi8GNDY5dj62RAZJRk6WU9ZZJl52Y9ZpJm6Gc9Z5Nn6Wg/aJZo7GlDaZpp8WpIap9q92tPa6dr/2xX +bK9tCG1gbbluEm5rbsRvHm94b9FwK3CGcOBxOnGVcfByS3KmcwFzXXO4dBR0cHTMdSh1hXXhdj52m3b4 +d1Z3s3gReG54zHkqeYl553pGeqV7BHtje8J8IXyBfOF9QX2hfgF+Yn7CfyN/hH/lgEeAqIEKgWuBzYIw +gpKC9INXg7qEHYSAhOOFR4Wrhg6GcobXhzuHn4gEiGmIzokziZmJ/opkisqLMIuWi/yMY4zKjTGNmI3/ +jmaOzo82j56QBpBukNaRP5GokhGSepLjk02TtpQglIqU9JVflcmWNJaflwqXdZfgmEyYuJkkmZCZ/Jpo +mtWbQpuvnByciZz3nWSd0p5Anq6fHZ+Ln/qgaaDYoUehtqImopajBqN2o+akVqTHpTilqaYapoum/adu +p+CoUqjEqTepqaocqo+rAqt1q+msXKzQrUStuK4trqGvFq+LsACwdbDqsWCx1rJLssKzOLOutCW0nLUT +tYq2AbZ5tvC3aLfguFm40blKucK6O7q1uy67p7whvJu9Fb2Pvgq+hL7/v3q/9cBwwOzBZ8Hjwl/C28NY +w9TEUcTOxUvFyMZGxsPHQce/yD3IvMk6ybnKOMq3yzbLtsw1zLXNNc21zjbOts83z7jQOdC60TzRvtI/ +0sHTRNPG1EnUy9VO1dHWVdbY11zX4Nhk2OjZbNnx2nba+9uA3AXcit0Q3ZbeHN6i3ynfr+A24L3hROHM +4lPi2+Nj4+vkc+T85YTmDeaW5x/nqegy6LzpRunQ6lvq5etw6/vshu0R7ZzuKO6070DvzPBY8OXxcvH/ +8ozzGfOn9DT0wvVQ9d72bfb794r4Gfio+Tj5x/pX+uf7d/wH/Jj9Kf26/kv+3P9t///SLS4vMFokY2xh +c3NuYW1lWCRjbGFzc2VzXxAQTlNCaXRtYXBJbWFnZVJlcKMvMTJaTlNJbWFnZVJlcFhOU09iamVjdNIt +LjQ1V05TQXJyYXmiNDLSLS43OF5OU011dGFibGVBcnJheaM3NDLTOjsOPD0+V05TV2hpdGVcTlNDb2xv +clNwYWNlRDAgMAAQA4AM0i0uQEFXTlNDb2xvcqJAMtItLkNEV05TSW1hZ2WiQzIACAARABoAJAApADIA +NwBJAEwAUQBTAGIAaAB3AH4AhQCSAJkApQCtALwAvgDAAMUAxwDJAMsAzQDUANkA5ADmAOgA6gDvAPIA +9AD2APgA/wEWATIBNAE2E6YTqxO2E78T0hPWE+ET6hPvE/cT+hP/FA4UEhQZFCEULhQzFDUUNxQ8FEQU +RxRMFFQAAAAAAAACAQAAAAAAAABFAAAAAAAAAAAAAAAAAAAUVw diff --git a/i18n/src/main/resources/he.lproj/Browser.xib b/i18n/src/main/resources/he.lproj/Browser.xib index 1486d2ebde..4db2945705 100644 --- a/i18n/src/main/resources/he.lproj/Browser.xib +++ b/i18n/src/main/resources/he.lproj/Browser.xib @@ -1,7 +1,7 @@ - + - + @@ -38,7 +38,7 @@ - + @@ -62,27 +62,27 @@ @@ -256,7 +256,7 @@ - + @@ -553,95 +553,95 @@ DQ YnBsaXN0MDDUAQIDBAUGBwpYJHZlcnNpb25ZJGFyY2hpdmVyVCR0b3BYJG9iamVjdHMSAAGGoF8QD05T -S2V5ZWRBcmNoaXZlctEICVRyb290gAGuCwwZGh8UJCkqMTQ3PUBVJG51bGzWDQ4PEBESExQVFhcYVk5T -U2l6ZV5OU1Jlc2l6aW5nTW9kZVYkY2xhc3NcTlNJbWFnZUZsYWdzVk5TUmVwc1dOU0NvbG9ygAIQAIAN -EiDDAACAA4ALVnsxLCAxfdIbDxweWk5TLm9iamVjdHOhHYAEgArSGw8gI6IhIoAFgAaACdMPJSYnKBRf -EBROU1RJRkZSZXByZXNlbnRhdGlvbl8QGU5TSW50ZXJuYWxMYXlvdXREaXJlY3Rpb26ACIAHTxESbE1N -ACoAAAAKAAAAEAEAAAMAAAABAAEAAAEBAAMAAAABAAEAAAECAAMAAAACAAgACAEDAAMAAAABAAEAAAEG -AAMAAAABAAEAAAEKAAMAAAABAAEAAAERAAQAAAABAAAACAESAAMAAAABAAEAAAEVAAMAAAABAAIAAAEW -AAMAAAABAAEAAAEXAAQAAAABAAAAAgEcAAMAAAABAAEAAAEoAAMAAAABAAIAAAFSAAMAAAABAAEAAAFT -AAMAAAACAAEAAYdzAAcAABGcAAAA0AAAAAAAABGcYXBwbAIAAABtbnRyR1JBWVhZWiAH3AAIABcADwAu -AA9hY3NwQVBQTAAAAABub25lAAAAAAAAAAAAAAAAAAAAAAAA9tYAAQAAAADTLWFwcGwAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAVkZXNjAAAAwAAAAHlkc2NtAAABPAAA -CBpjcHJ0AAAJWAAAACN3dHB0AAAJfAAAABRrVFJDAAAJkAAACAxkZXNjAAAAAAAAAB9HZW5lcmljIEdy -YXkgR2FtbWEgMi4yIFByb2ZpbGUAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAbWx1YwAAAAAAAAAfAAAADHNr -U0sAAAAuAAABhGRhREsAAAA6AAABsmNhRVMAAAA4AAAB7HZpVk4AAABAAAACJHB0QlIAAABKAAACZHVr -VUEAAAAsAAACrmZyRlUAAAA+AAAC2mh1SFUAAAA0AAADGHpoVFcAAAAaAAADTGtvS1IAAAAiAAADZm5i -Tk8AAAA6AAADiGNzQ1oAAAAoAAADwmhlSUwAAAAkAAAD6nJvUk8AAAAqAAAEDmRlREUAAABOAAAEOGl0 -SVQAAABOAAAEhnN2U0UAAAA4AAAE1HpoQ04AAAAaAAAFDGphSlAAAAAmAAAFJmVsR1IAAAAqAAAFTHB0 -UE8AAABSAAAFdm5sTkwAAABAAAAFyGVzRVMAAABMAAAGCHRoVEgAAAAyAAAGVHRyVFIAAAAkAAAGhmZp -RkkAAABGAAAGqmhySFIAAAA+AAAG8HBsUEwAAABKAAAHLmFyRUcAAAAsAAAHeHJ1UlUAAAA6AAAHpGVu -VVMAAAA8AAAH3gBWAWEAZQBvAGIAZQBjAG4A4QAgAHMAaQB2AOEAIABnAGEAbQBhACAAMgAsADIARwBl -AG4AZQByAGkAcwBrACAAZwByAOUAIAAyACwAMgAgAGcAYQBtAG0AYQAtAHAAcgBvAGYAaQBsAEcAYQBt -AG0AYQAgAGQAZQAgAGcAcgBpAHMAbwBzACAAZwBlAG4A6AByAGkAYwBhACAAMgAuADIAQx6lAHUAIABo -AOwAbgBoACAATQDgAHUAIAB4AOEAbQAgAEMAaAB1AG4AZwAgAEcAYQBtAG0AYQAgADIALgAyAFAAZQBy -AGYAaQBsACAARwBlAG4A6QByAGkAYwBvACAAZABhACAARwBhAG0AYQAgAGQAZQAgAEMAaQBuAHoAYQBz -ACAAMgAsADIEFwQwBDMEMAQ7BEwEPQQwACAARwByAGEAeQAtBDMEMAQ8BDAAIAAyAC4AMgBQAHIAbwBm -AGkAbAAgAGcA6QBuAOkAcgBpAHEAdQBlACAAZwByAGkAcwAgAGcAYQBtAG0AYQAgADIALAAyAMEAbAB0 -AGEAbADhAG4AbwBzACAAcwB6APwAcgBrAGUAIABnAGEAbQBtAGEAIAAyAC4AMpAadShwcJaOUUlepgAy -AC4AMoJyX2ljz4/wx3y8GAAg1ozAyQAgrBC5yAAgADIALgAyACDVBLhc0wzHfABHAGUAbgBlAHIAaQBz -AGsAIABnAHIA5QAgAGcAYQBtAG0AYQAgADIALAAyAC0AcAByAG8AZgBpAGwATwBiAGUAYwBuAOEAIAFh -AGUAZADhACAAZwBhAG0AYQAgADIALgAyBdIF0AXeBdQAIAXQBeQF1QXoACAF2wXcBdwF2QAgADIALgAy -AEcAYQBtAGEAIABnAHIAaQAgAGcAZQBuAGUAcgBpAGMBAwAgADIALAAyAEEAbABsAGcAZQBtAGUAaQBu -AGUAcwAgAEcAcgBhAHUAcwB0AHUAZgBlAG4ALQBQAHIAbwBmAGkAbAAgAEcAYQBtAG0AYQAgADIALAAy -AFAAcgBvAGYAaQBsAG8AIABnAHIAaQBnAGkAbwAgAGcAZQBuAGUAcgBpAGMAbwAgAGQAZQBsAGwAYQAg -AGcAYQBtAG0AYQAgADIALAAyAEcAZQBuAGUAcgBpAHMAawAgAGcAcgDlACAAMgAsADIAIABnAGEAbQBt -AGEAcAByAG8AZgBpAGxmbpAacHBepnz7ZXAAMgAuADJjz4/wZYdO9k4AgiwwsDDsMKQwrDDzMN4AIAAy -AC4AMgAgMNcw7TDVMKEwpDDrA5MDtQO9A7kDugPMACADkwO6A8EDuQAgA5MDrAO8A7wDsQAgADIALgAy -AFAAZQByAGYAaQBsACAAZwBlAG4A6QByAGkAYwBvACAAZABlACAAYwBpAG4AegBlAG4AdABvAHMAIABk -AGEAIABHAGEAbQBtAGEAIAAyACwAMgBBAGwAZwBlAG0AZQBlAG4AIABnAHIAaQBqAHMAIABnAGEAbQBt -AGEAIAAyACwAMgAtAHAAcgBvAGYAaQBlAGwAUABlAHIAZgBpAGwAIABnAGUAbgDpAHIAaQBjAG8AIABk -AGUAIABnAGEAbQBtAGEAIABkAGUAIABnAHIAaQBzAGUAcwAgADIALAAyDiMOMQ4HDioONQ5BDgEOIQ4h -DjIOQA4BDiMOIg5MDhcOMQ5IDicORA4bACAAMgAuADIARwBlAG4AZQBsACAARwByAGkAIABHAGEAbQBh -ACAAMgAsADIAWQBsAGUAaQBuAGUAbgAgAGgAYQByAG0AYQBhAG4AIABnAGEAbQBtAGEAIAAyACwAMgAg -AC0AcAByAG8AZgBpAGkAbABpAEcAZQBuAGUAcgBpAQ0AawBpACAARwByAGEAeQAgAEcAYQBtAG0AYQAg -ADIALgAyACAAcAByAG8AZgBpAGwAVQBuAGkAdwBlAHIAcwBhAGwAbgB5ACAAcAByAG8AZgBpAGwAIABz -AHoAYQByAG8BWwBjAGkAIABnAGEAbQBtAGEAIAAyACwAMgY6BicGRQYnACAAMgAuADIAIAZEBkgGRgAg -BjEGRQYnBi8GSgAgBjkGJwZFBB4EMQRJBDAETwAgBEEENQRABDAETwAgBDMEMAQ8BDwEMAAgADIALAAy -AC0EPwRABD4ERAQ4BDsETABHAGUAbgBlAHIAaQBjACAARwByAGEAeQAgAEcAYQBtAG0AYQAgADIALgAy -ACAAUAByAG8AZgBpAGwAZQAAdGV4dAAAAABDb3B5cmlnaHQgQXBwbGUgSW5jLiwgMjAxMgAAWFlaIAAA -AAAAAPNRAAEAAAABFsxjdXJ2AAAAAAAABAAAAAAFAAoADwAUABkAHgAjACgALQAyADcAOwBAAEUASgBP -AFQAWQBeAGMAaABtAHIAdwB8AIEAhgCLAJAAlQCaAJ8ApACpAK4AsgC3ALwAwQDGAMsA0ADVANsA4ADl -AOsA8AD2APsBAQEHAQ0BEwEZAR8BJQErATIBOAE+AUUBTAFSAVkBYAFnAW4BdQF8AYMBiwGSAZoBoQGp -AbEBuQHBAckB0QHZAeEB6QHyAfoCAwIMAhQCHQImAi8COAJBAksCVAJdAmcCcQJ6AoQCjgKYAqICrAK2 -AsECywLVAuAC6wL1AwADCwMWAyEDLQM4A0MDTwNaA2YDcgN+A4oDlgOiA64DugPHA9MD4APsA/kEBgQT -BCAELQQ7BEgEVQRjBHEEfgSMBJoEqAS2BMQE0wThBPAE/gUNBRwFKwU6BUkFWAVnBXcFhgWWBaYFtQXF -BdUF5QX2BgYGFgYnBjcGSAZZBmoGewaMBp0GrwbABtEG4wb1BwcHGQcrBz0HTwdhB3QHhgeZB6wHvwfS -B+UH+AgLCB8IMghGCFoIbgiCCJYIqgi+CNII5wj7CRAJJQk6CU8JZAl5CY8JpAm6Cc8J5Qn7ChEKJwo9 -ClQKagqBCpgKrgrFCtwK8wsLCyILOQtRC2kLgAuYC7ALyAvhC/kMEgwqDEMMXAx1DI4MpwzADNkM8w0N -DSYNQA1aDXQNjg2pDcMN3g34DhMOLg5JDmQOfw6bDrYO0g7uDwkPJQ9BD14Peg+WD7MPzw/sEAkQJhBD -EGEQfhCbELkQ1xD1ERMRMRFPEW0RjBGqEckR6BIHEiYSRRJkEoQSoxLDEuMTAxMjE0MTYxODE6QTxRPl -FAYUJxRJFGoUixStFM4U8BUSFTQVVhV4FZsVvRXgFgMWJhZJFmwWjxayFtYW+hcdF0EXZReJF64X0hf3 -GBsYQBhlGIoYrxjVGPoZIBlFGWsZkRm3Gd0aBBoqGlEadxqeGsUa7BsUGzsbYxuKG7Ib2hwCHCocUhx7 -HKMczBz1HR4dRx1wHZkdwx3sHhYeQB5qHpQevh7pHxMfPh9pH5Qfvx/qIBUgQSBsIJggxCDwIRwhSCF1 -IaEhziH7IiciVSKCIq8i3SMKIzgjZiOUI8Ij8CQfJE0kfCSrJNolCSU4JWgllyXHJfcmJyZXJocmtybo -JxgnSSd6J6sn3CgNKD8ocSiiKNQpBik4KWspnSnQKgIqNSpoKpsqzysCKzYraSudK9EsBSw5LG4soizX -LQwtQS12Last4S4WLkwugi63Lu4vJC9aL5Evxy/+MDUwbDCkMNsxEjFKMYIxujHyMioyYzKbMtQzDTNG -M38zuDPxNCs0ZTSeNNg1EzVNNYc1wjX9Njc2cjauNuk3JDdgN5w31zgUOFA4jDjIOQU5Qjl/Obw5+To2 -OnQ6sjrvOy07azuqO+g8JzxlPKQ84z0iPWE9oT3gPiA+YD6gPuA/IT9hP6I/4kAjQGRApkDnQSlBakGs -Qe5CMEJyQrVC90M6Q31DwEQDREdEikTORRJFVUWaRd5GIkZnRqtG8Ec1R3tHwEgFSEtIkUjXSR1JY0mp -SfBKN0p9SsRLDEtTS5pL4kwqTHJMuk0CTUpNk03cTiVObk63TwBPSU+TT91QJ1BxULtRBlFQUZtR5lIx -UnxSx1MTU19TqlP2VEJUj1TbVShVdVXCVg9WXFapVvdXRFeSV+BYL1h9WMtZGllpWbhaB1pWWqZa9VtF -W5Vb5Vw1XIZc1l0nXXhdyV4aXmxevV8PX2Ffs2AFYFdgqmD8YU9homH1YklinGLwY0Njl2PrZEBklGTp -ZT1lkmXnZj1mkmboZz1nk2fpaD9olmjsaUNpmmnxakhqn2r3a09rp2v/bFdsr20IbWBtuW4SbmtuxG8e -b3hv0XArcIZw4HE6cZVx8HJLcqZzAXNdc7h0FHRwdMx1KHWFdeF2Pnabdvh3VnezeBF4bnjMeSp5iXnn -ekZ6pXsEe2N7wnwhfIF84X1BfaF+AX5ifsJ/I3+Ef+WAR4CogQqBa4HNgjCCkoL0g1eDuoQdhICE44VH -hauGDoZyhteHO4efiASIaYjOiTOJmYn+imSKyoswi5aL/IxjjMqNMY2Yjf+OZo7OjzaPnpAGkG6Q1pE/ -kaiSEZJ6kuOTTZO2lCCUipT0lV+VyZY0lp+XCpd1l+CYTJi4mSSZkJn8mmia1ZtCm6+cHJyJnPedZJ3S -nkCerp8dn4uf+qBpoNihR6G2oiailqMGo3aj5qRWpMelOKWpphqmi6b9p26n4KhSqMSpN6mpqhyqj6sC -q3Wr6axcrNCtRK24ri2uoa8Wr4uwALB1sOqxYLHWskuywrM4s660JbSctRO1irYBtnm28Ldot+C4WbjR -uUq5wro7urW7LrunvCG8m70VvY++Cr6Evv+/er/1wHDA7MFnwePCX8Lbw1jD1MRRxM7FS8XIxkbGw8dB -x7/IPci8yTrJuco4yrfLNsu2zDXMtc01zbXONs62zzfPuNA50LrRPNG+0j/SwdNE08bUSdTL1U7V0dZV -1tjXXNfg2GTY6Nls2fHadtr724DcBdyK3RDdlt4c3qLfKd+v4DbgveFE4cziU+Lb42Pj6+Rz5PzlhOYN -5pbnH+ep6DLovOlG6dDqW+rl63Dr++yG7RHtnO4o7rTvQO/M8Fjw5fFy8f/yjPMZ86f0NPTC9VD13vZt -9vv3ivgZ+Kj5OPnH+lf65/t3/Af8mP0p/br+S/7c/23//9IrLC0uWiRjbGFzc25hbWVYJGNsYXNzZXNf -EBBOU0JpdG1hcEltYWdlUmVwoy0vMFpOU0ltYWdlUmVwWE5TT2JqZWN00issMjNXTlNBcnJheaIyMNIr -LDU2Xk5TTXV0YWJsZUFycmF5ozUyMNM4OQ86OzxXTlNXaGl0ZVxOU0NvbG9yU3BhY2VEMCAwABADgAzS -Kyw+P1dOU0NvbG9yoj4w0issQUJXTlNJbWFnZaJBMAAIABEAGgAkACkAMgA3AEkATABRAFMAYgBoAHUA -fACLAJIAnwCmAK4AsACyALQAuQC7AL0AxADJANQA1gDYANoA3wDiAOQA5gDoAO8BBgEiASQBJhOWE5sT -phOvE8ITxhPRE9oT3xPnE+oT7xP+FAIUCRQRFB4UIxQlFCcULBQ0FDcUPBREAAAAAAAAAgEAAAAAAAAA -QwAAAAAAAAAAAAAAAAAAFEc +S2V5ZWRBcmNoaXZlctEICVRyb290gAGuCwwbHCEaJissMzY5P0JVJG51bGzXDQ4PEBESExQVFhcYGRpW +TlNTaXplViRjbGFzc1xOU0ltYWdlRmxhZ3NWTlNSZXBzW05TVGludENvbG9yV05TQ29sb3JeTlNSZXNp +emluZ01vZGWAAoANEiDDAACAA4AAgAsQAFZ7MSwgMX3SHQ4eIFpOUy5vYmplY3RzoR+ABIAK0h0OIiWi +IySABYAGgAnTDicoKSoaXxAUTlNUSUZGUmVwcmVzZW50YXRpb25fEBlOU0ludGVybmFsTGF5b3V0RGly +ZWN0aW9ugAiAB08REmxNTQAqAAAACgAAABABAAADAAAAAQABAAABAQADAAAAAQABAAABAgADAAAAAgAI +AAgBAwADAAAAAQABAAABBgADAAAAAQABAAABCgADAAAAAQABAAABEQAEAAAAAQAAAAgBEgADAAAAAQAB +AAABFQADAAAAAQACAAABFgADAAAAAQABAAABFwAEAAAAAQAAAAIBHAADAAAAAQABAAABKAADAAAAAQAC +AAABUgADAAAAAQABAAABUwADAAAAAgABAAGHcwAHAAARnAAAANAAAAAAAAARnGFwcGwCAAAAbW50ckdS +QVlYWVogB9wACAAXAA8ALgAPYWNzcEFQUEwAAAAAbm9uZQAAAAAAAAAAAAAAAAAAAAAAAPbWAAEAAAAA +0y1hcHBsAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFZGVzYwAA +AMAAAAB5ZHNjbQAAATwAAAgaY3BydAAACVgAAAAjd3RwdAAACXwAAAAUa1RSQwAACZAAAAgMZGVzYwAA +AAAAAAAfR2VuZXJpYyBHcmF5IEdhbW1hIDIuMiBQcm9maWxlAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAG1s +dWMAAAAAAAAAHwAAAAxza1NLAAAALgAAAYRkYURLAAAAOgAAAbJjYUVTAAAAOAAAAex2aVZOAAAAQAAA +AiRwdEJSAAAASgAAAmR1a1VBAAAALAAAAq5mckZVAAAAPgAAAtpodUhVAAAANAAAAxh6aFRXAAAAGgAA +A0xrb0tSAAAAIgAAA2ZuYk5PAAAAOgAAA4hjc0NaAAAAKAAAA8JoZUlMAAAAJAAAA+pyb1JPAAAAKgAA +BA5kZURFAAAATgAABDhpdElUAAAATgAABIZzdlNFAAAAOAAABNR6aENOAAAAGgAABQxqYUpQAAAAJgAA +BSZlbEdSAAAAKgAABUxwdFBPAAAAUgAABXZubE5MAAAAQAAABchlc0VTAAAATAAABgh0aFRIAAAAMgAA +BlR0clRSAAAAJAAABoZmaUZJAAAARgAABqpockhSAAAAPgAABvBwbFBMAAAASgAABy5hckVHAAAALAAA +B3hydVJVAAAAOgAAB6RlblVTAAAAPAAAB94AVgFhAGUAbwBiAGUAYwBuAOEAIABzAGkAdgDhACAAZwBh +AG0AYQAgADIALAAyAEcAZQBuAGUAcgBpAHMAawAgAGcAcgDlACAAMgAsADIAIABnAGEAbQBtAGEALQBw +AHIAbwBmAGkAbABHAGEAbQBtAGEAIABkAGUAIABnAHIAaQBzAG8AcwAgAGcAZQBuAOgAcgBpAGMAYQAg +ADIALgAyAEMepQB1ACAAaADsAG4AaAAgAE0A4AB1ACAAeADhAG0AIABDAGgAdQBuAGcAIABHAGEAbQBt +AGEAIAAyAC4AMgBQAGUAcgBmAGkAbAAgAEcAZQBuAOkAcgBpAGMAbwAgAGQAYQAgAEcAYQBtAGEAIABk +AGUAIABDAGkAbgB6AGEAcwAgADIALAAyBBcEMAQzBDAEOwRMBD0EMAAgAEcAcgBhAHkALQQzBDAEPAQw +ACAAMgAuADIAUAByAG8AZgBpAGwAIABnAOkAbgDpAHIAaQBxAHUAZQAgAGcAcgBpAHMAIABnAGEAbQBt +AGEAIAAyACwAMgDBAGwAdABhAGwA4QBuAG8AcwAgAHMAegD8AHIAawBlACAAZwBhAG0AbQBhACAAMgAu +ADKQGnUocHCWjlFJXqYAMgAuADKCcl9pY8+P8Md8vBgAINaMwMkAIKwQucgAIAAyAC4AMgAg1QS4XNMM +x3wARwBlAG4AZQByAGkAcwBrACAAZwByAOUAIABnAGEAbQBtAGEAIAAyACwAMgAtAHAAcgBvAGYAaQBs +AE8AYgBlAGMAbgDhACABYQBlAGQA4QAgAGcAYQBtAGEAIAAyAC4AMgXSBdAF3gXUACAF0AXkBdUF6AAg +BdsF3AXcBdkAIAAyAC4AMgBHAGEAbQBhACAAZwByAGkAIABnAGUAbgBlAHIAaQBjAQMAIAAyACwAMgBB +AGwAbABnAGUAbQBlAGkAbgBlAHMAIABHAHIAYQB1AHMAdAB1AGYAZQBuAC0AUAByAG8AZgBpAGwAIABH +AGEAbQBtAGEAIAAyACwAMgBQAHIAbwBmAGkAbABvACAAZwByAGkAZwBpAG8AIABnAGUAbgBlAHIAaQBj +AG8AIABkAGUAbABsAGEAIABnAGEAbQBtAGEAIAAyACwAMgBHAGUAbgBlAHIAaQBzAGsAIABnAHIA5QAg +ADIALAAyACAAZwBhAG0AbQBhAHAAcgBvAGYAaQBsZm6QGnBwXqZ8+2VwADIALgAyY8+P8GWHTvZOAIIs +MLAw7DCkMKww8zDeACAAMgAuADIAIDDXMO0w1TChMKQw6wOTA7UDvQO5A7oDzAAgA5MDugPBA7kAIAOT +A6wDvAO8A7EAIAAyAC4AMgBQAGUAcgBmAGkAbAAgAGcAZQBuAOkAcgBpAGMAbwAgAGQAZQAgAGMAaQBu +AHoAZQBuAHQAbwBzACAAZABhACAARwBhAG0AbQBhACAAMgAsADIAQQBsAGcAZQBtAGUAZQBuACAAZwBy +AGkAagBzACAAZwBhAG0AbQBhACAAMgAsADIALQBwAHIAbwBmAGkAZQBsAFAAZQByAGYAaQBsACAAZwBl +AG4A6QByAGkAYwBvACAAZABlACAAZwBhAG0AbQBhACAAZABlACAAZwByAGkAcwBlAHMAIAAyACwAMg4j +DjEOBw4qDjUOQQ4BDiEOIQ4yDkAOAQ4jDiIOTA4XDjEOSA4nDkQOGwAgADIALgAyAEcAZQBuAGUAbAAg +AEcAcgBpACAARwBhAG0AYQAgADIALAAyAFkAbABlAGkAbgBlAG4AIABoAGEAcgBtAGEAYQBuACAAZwBh +AG0AbQBhACAAMgAsADIAIAAtAHAAcgBvAGYAaQBpAGwAaQBHAGUAbgBlAHIAaQENAGsAaQAgAEcAcgBh +AHkAIABHAGEAbQBtAGEAIAAyAC4AMgAgAHAAcgBvAGYAaQBsAFUAbgBpAHcAZQByAHMAYQBsAG4AeQAg +AHAAcgBvAGYAaQBsACAAcwB6AGEAcgBvAVsAYwBpACAAZwBhAG0AbQBhACAAMgAsADIGOgYnBkUGJwAg +ADIALgAyACAGRAZIBkYAIAYxBkUGJwYvBkoAIAY5BicGRQQeBDEESQQwBE8AIARBBDUEQAQwBE8AIAQz +BDAEPAQ8BDAAIAAyACwAMgAtBD8EQAQ+BEQEOAQ7BEwARwBlAG4AZQByAGkAYwAgAEcAcgBhAHkAIABH +AGEAbQBtAGEAIAAyAC4AMgAgAFAAcgBvAGYAaQBsAGUAAHRleHQAAAAAQ29weXJpZ2h0IEFwcGxlIElu +Yy4sIDIwMTIAAFhZWiAAAAAAAADzUQABAAAAARbMY3VydgAAAAAAAAQAAAAABQAKAA8AFAAZAB4AIwAo +AC0AMgA3ADsAQABFAEoATwBUAFkAXgBjAGgAbQByAHcAfACBAIYAiwCQAJUAmgCfAKQAqQCuALIAtwC8 +AMEAxgDLANAA1QDbAOAA5QDrAPAA9gD7AQEBBwENARMBGQEfASUBKwEyATgBPgFFAUwBUgFZAWABZwFu +AXUBfAGDAYsBkgGaAaEBqQGxAbkBwQHJAdEB2QHhAekB8gH6AgMCDAIUAh0CJgIvAjgCQQJLAlQCXQJn +AnECegKEAo4CmAKiAqwCtgLBAssC1QLgAusC9QMAAwsDFgMhAy0DOANDA08DWgNmA3IDfgOKA5YDogOu +A7oDxwPTA+AD7AP5BAYEEwQgBC0EOwRIBFUEYwRxBH4EjASaBKgEtgTEBNME4QTwBP4FDQUcBSsFOgVJ +BVgFZwV3BYYFlgWmBbUFxQXVBeUF9gYGBhYGJwY3BkgGWQZqBnsGjAadBq8GwAbRBuMG9QcHBxkHKwc9 +B08HYQd0B4YHmQesB78H0gflB/gICwgfCDIIRghaCG4IggiWCKoIvgjSCOcI+wkQCSUJOglPCWQJeQmP +CaQJugnPCeUJ+woRCicKPQpUCmoKgQqYCq4KxQrcCvMLCwsiCzkLUQtpC4ALmAuwC8gL4Qv5DBIMKgxD +DFwMdQyODKcMwAzZDPMNDQ0mDUANWg10DY4NqQ3DDd4N+A4TDi4OSQ5kDn8Omw62DtIO7g8JDyUPQQ9e +D3oPlg+zD88P7BAJECYQQxBhEH4QmxC5ENcQ9RETETERTxFtEYwRqhHJEegSBxImEkUSZBKEEqMSwxLj +EwMTIxNDE2MTgxOkE8UT5RQGFCcUSRRqFIsUrRTOFPAVEhU0FVYVeBWbFb0V4BYDFiYWSRZsFo8WshbW +FvoXHRdBF2UXiReuF9IX9xgbGEAYZRiKGK8Y1Rj6GSAZRRlrGZEZtxndGgQaKhpRGncanhrFGuwbFBs7 +G2MbihuyG9ocAhwqHFIcexyjHMwc9R0eHUcdcB2ZHcMd7B4WHkAeah6UHr4e6R8THz4faR+UH78f6iAV +IEEgbCCYIMQg8CEcIUghdSGhIc4h+yInIlUigiKvIt0jCiM4I2YjlCPCI/AkHyRNJHwkqyTaJQklOCVo +JZclxyX3JicmVyaHJrcm6CcYJ0kneierJ9woDSg/KHEooijUKQYpOClrKZ0p0CoCKjUqaCqbKs8rAis2 +K2krnSvRLAUsOSxuLKIs1y0MLUEtdi2rLeEuFi5MLoIuty7uLyQvWi+RL8cv/jA1MGwwpDDbMRIxSjGC +Mbox8jIqMmMymzLUMw0zRjN/M7gz8TQrNGU0njTYNRM1TTWHNcI1/TY3NnI2rjbpNyQ3YDecN9c4FDhQ +OIw4yDkFOUI5fzm8Ofk6Njp0OrI67zstO2s7qjvoPCc8ZTykPOM9Ij1hPaE94D4gPmA+oD7gPyE/YT+i +P+JAI0BkQKZA50EpQWpBrEHuQjBCckK1QvdDOkN9Q8BEA0RHRIpEzkUSRVVFmkXeRiJGZ0arRvBHNUd7 +R8BIBUhLSJFI10kdSWNJqUnwSjdKfUrESwxLU0uaS+JMKkxyTLpNAk1KTZNN3E4lTm5Ot08AT0lPk0/d +UCdQcVC7UQZRUFGbUeZSMVJ8UsdTE1NfU6pT9lRCVI9U21UoVXVVwlYPVlxWqVb3V0RXklfgWC9YfVjL +WRpZaVm4WgdaVlqmWvVbRVuVW+VcNVyGXNZdJ114XcleGl5sXr1fD19hX7NgBWBXYKpg/GFPYaJh9WJJ +Ypxi8GNDY5dj62RAZJRk6WU9ZZJl52Y9ZpJm6Gc9Z5Nn6Wg/aJZo7GlDaZpp8WpIap9q92tPa6dr/2xX +bK9tCG1gbbluEm5rbsRvHm94b9FwK3CGcOBxOnGVcfByS3KmcwFzXXO4dBR0cHTMdSh1hXXhdj52m3b4 +d1Z3s3gReG54zHkqeYl553pGeqV7BHtje8J8IXyBfOF9QX2hfgF+Yn7CfyN/hH/lgEeAqIEKgWuBzYIw +gpKC9INXg7qEHYSAhOOFR4Wrhg6GcobXhzuHn4gEiGmIzokziZmJ/opkisqLMIuWi/yMY4zKjTGNmI3/ +jmaOzo82j56QBpBukNaRP5GokhGSepLjk02TtpQglIqU9JVflcmWNJaflwqXdZfgmEyYuJkkmZCZ/Jpo +mtWbQpuvnByciZz3nWSd0p5Anq6fHZ+Ln/qgaaDYoUehtqImopajBqN2o+akVqTHpTilqaYapoum/adu +p+CoUqjEqTepqaocqo+rAqt1q+msXKzQrUStuK4trqGvFq+LsACwdbDqsWCx1rJLssKzOLOutCW0nLUT +tYq2AbZ5tvC3aLfguFm40blKucK6O7q1uy67p7whvJu9Fb2Pvgq+hL7/v3q/9cBwwOzBZ8Hjwl/C28NY +w9TEUcTOxUvFyMZGxsPHQce/yD3IvMk6ybnKOMq3yzbLtsw1zLXNNc21zjbOts83z7jQOdC60TzRvtI/ +0sHTRNPG1EnUy9VO1dHWVdbY11zX4Nhk2OjZbNnx2nba+9uA3AXcit0Q3ZbeHN6i3ynfr+A24L3hROHM +4lPi2+Nj4+vkc+T85YTmDeaW5x/nqegy6LzpRunQ6lvq5etw6/vshu0R7ZzuKO6070DvzPBY8OXxcvH/ +8ozzGfOn9DT0wvVQ9d72bfb794r4Gfio+Tj5x/pX+uf7d/wH/Jj9Kf26/kv+3P9t///SLS4vMFokY2xh +c3NuYW1lWCRjbGFzc2VzXxAQTlNCaXRtYXBJbWFnZVJlcKMvMTJaTlNJbWFnZVJlcFhOU09iamVjdNIt +LjQ1V05TQXJyYXmiNDLSLS43OF5OU011dGFibGVBcnJheaM3NDLTOjsOPD0+V05TV2hpdGVcTlNDb2xv +clNwYWNlRDAgMAAQA4AM0i0uQEFXTlNDb2xvcqJAMtItLkNEV05TSW1hZ2WiQzIACAARABoAJAApADIA +NwBJAEwAUQBTAGIAaAB3AH4AhQCSAJkApQCtALwAvgDAAMUAxwDJAMsAzQDUANkA5ADmAOgA6gDvAPIA +9AD2APgA/wEWATIBNAE2E6YTqxO2E78T0hPWE+ET6hPvE/cT+hP/FA4UEhQZFCEULhQzFDUUNxQ8FEQU +RxRMFFQAAAAAAAACAQAAAAAAAABFAAAAAAAAAAAAAAAAAAAUVw diff --git a/i18n/src/main/resources/he.lproj/Command.xib b/i18n/src/main/resources/he.lproj/Command.xib index 18c993a37d..dda629300c 100644 --- a/i18n/src/main/resources/he.lproj/Command.xib +++ b/i18n/src/main/resources/he.lproj/Command.xib @@ -1,7 +1,7 @@ - + - + diff --git a/i18n/src/main/resources/he.lproj/Connection.xib b/i18n/src/main/resources/he.lproj/Connection.xib index abdf0399d3..10bcbfe65b 100644 --- a/i18n/src/main/resources/he.lproj/Connection.xib +++ b/i18n/src/main/resources/he.lproj/Connection.xib @@ -1,7 +1,7 @@ - + - + @@ -76,7 +76,7 @@ - + diff --git a/i18n/src/main/resources/he.lproj/Info.xib b/i18n/src/main/resources/he.lproj/Info.xib index 63ac7e4f90..b5a1cf385a 100644 --- a/i18n/src/main/resources/he.lproj/Info.xib +++ b/i18n/src/main/resources/he.lproj/Info.xib @@ -1,7 +1,7 @@ - + - + @@ -89,7 +89,7 @@ - + @@ -1017,21 +1017,21 @@ - + - + - + @@ -1121,21 +1121,21 @@ - + - + - + @@ -1250,27 +1250,27 @@ diff --git a/i18n/src/main/resources/he.lproj/Login.strings b/i18n/src/main/resources/he.lproj/Login.strings index f146807cfc..660344964a 100644 Binary files a/i18n/src/main/resources/he.lproj/Login.strings and b/i18n/src/main/resources/he.lproj/Login.strings differ diff --git a/i18n/src/main/resources/he.lproj/Main.xib b/i18n/src/main/resources/he.lproj/Main.xib index 2ed7878d7a..aa38671c72 100644 --- a/i18n/src/main/resources/he.lproj/Main.xib +++ b/i18n/src/main/resources/he.lproj/Main.xib @@ -1,7 +1,7 @@ - + - + @@ -159,19 +159,19 @@ - + - + - + @@ -321,12 +321,12 @@ CA - + - + @@ -566,6 +566,10 @@ CA - + + + + + diff --git a/i18n/src/main/resources/he.lproj/Preferences.xib b/i18n/src/main/resources/he.lproj/Preferences.xib index 784523e6f7..2cb1b6d4d8 100644 --- a/i18n/src/main/resources/he.lproj/Preferences.xib +++ b/i18n/src/main/resources/he.lproj/Preferences.xib @@ -1,7 +1,7 @@ - + - + @@ -266,7 +266,7 @@ - + @@ -307,7 +307,7 @@ - + @@ -538,7 +538,7 @@ - + @@ -578,7 +578,7 @@ - + @@ -613,7 +613,7 @@ - + @@ -642,7 +642,7 @@ - + @@ -1218,7 +1218,7 @@ - + @@ -1250,7 +1250,7 @@ - + @@ -1289,7 +1289,7 @@ - + @@ -1362,7 +1362,7 @@ - + @@ -1557,7 +1557,7 @@ - + @@ -1617,7 +1617,7 @@ - + @@ -1642,7 +1642,7 @@ - + @@ -1684,7 +1684,7 @@ - + @@ -1712,7 +1712,7 @@ - + @@ -1740,7 +1740,7 @@ - + @@ -1769,7 +1769,7 @@ - + @@ -1804,7 +1804,7 @@ - + @@ -1887,7 +1887,7 @@ - + @@ -1946,7 +1946,7 @@ - + @@ -1974,7 +1974,7 @@ - + @@ -2003,7 +2003,7 @@ - + diff --git a/i18n/src/main/resources/he.lproj/Profiles.xib b/i18n/src/main/resources/he.lproj/Profiles.xib index 18af5a58f4..20e5ce0f36 100644 --- a/i18n/src/main/resources/he.lproj/Profiles.xib +++ b/i18n/src/main/resources/he.lproj/Profiles.xib @@ -1,7 +1,7 @@ - + - + diff --git a/i18n/src/main/resources/he.lproj/Prompt.xib b/i18n/src/main/resources/he.lproj/Prompt.xib index 3a4711560e..65264d8090 100644 --- a/i18n/src/main/resources/he.lproj/Prompt.xib +++ b/i18n/src/main/resources/he.lproj/Prompt.xib @@ -1,7 +1,7 @@ - + - + @@ -95,7 +95,7 @@ Gw - + diff --git a/i18n/src/main/resources/he.lproj/S3.strings b/i18n/src/main/resources/he.lproj/S3.strings index 44f7c3a53b..648da7203f 100644 Binary files a/i18n/src/main/resources/he.lproj/S3.strings and b/i18n/src/main/resources/he.lproj/S3.strings differ diff --git a/i18n/src/main/resources/he.lproj/Updater.strings b/i18n/src/main/resources/he.lproj/Updater.strings index 23536b83a7..8ba22299ba 100644 Binary files a/i18n/src/main/resources/he.lproj/Updater.strings and b/i18n/src/main/resources/he.lproj/Updater.strings differ diff --git a/i18n/src/main/resources/hr.lproj/Bookmark.xib b/i18n/src/main/resources/hr.lproj/Bookmark.xib index e3f09143d0..3c1ebc8f05 100644 --- a/i18n/src/main/resources/hr.lproj/Bookmark.xib +++ b/i18n/src/main/resources/hr.lproj/Bookmark.xib @@ -1,7 +1,7 @@ - + - + @@ -92,7 +92,7 @@ - + @@ -489,95 +489,95 @@ YnBsaXN0MDDUAQIDBAUGBwpYJHZlcnNpb25ZJGFyY2hpdmVyVCR0b3BYJG9iamVjdHMSAAGGoF8QD05T -S2V5ZWRBcmNoaXZlctEICVRyb290gAGuCwwZGh8UJCkqMTQ3PUBVJG51bGzWDQ4PEBESExQVFhcYVk5T -U2l6ZV5OU1Jlc2l6aW5nTW9kZVYkY2xhc3NcTlNJbWFnZUZsYWdzVk5TUmVwc1dOU0NvbG9ygAIQAIAN -EiDDAACAA4ALVnsxLCAxfdIbDxweWk5TLm9iamVjdHOhHYAEgArSGw8gI6IhIoAFgAaACdMPJSYnKBRf -EBROU1RJRkZSZXByZXNlbnRhdGlvbl8QGU5TSW50ZXJuYWxMYXlvdXREaXJlY3Rpb26ACIAHTxESbE1N -ACoAAAAKAAAAEAEAAAMAAAABAAEAAAEBAAMAAAABAAEAAAECAAMAAAACAAgACAEDAAMAAAABAAEAAAEG -AAMAAAABAAEAAAEKAAMAAAABAAEAAAERAAQAAAABAAAACAESAAMAAAABAAEAAAEVAAMAAAABAAIAAAEW -AAMAAAABAAEAAAEXAAQAAAABAAAAAgEcAAMAAAABAAEAAAEoAAMAAAABAAIAAAFSAAMAAAABAAEAAAFT -AAMAAAACAAEAAYdzAAcAABGcAAAA0AAAAAAAABGcYXBwbAIAAABtbnRyR1JBWVhZWiAH3AAIABcADwAu -AA9hY3NwQVBQTAAAAABub25lAAAAAAAAAAAAAAAAAAAAAAAA9tYAAQAAAADTLWFwcGwAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAVkZXNjAAAAwAAAAHlkc2NtAAABPAAA -CBpjcHJ0AAAJWAAAACN3dHB0AAAJfAAAABRrVFJDAAAJkAAACAxkZXNjAAAAAAAAAB9HZW5lcmljIEdy -YXkgR2FtbWEgMi4yIFByb2ZpbGUAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAbWx1YwAAAAAAAAAfAAAADHNr -U0sAAAAuAAABhGRhREsAAAA6AAABsmNhRVMAAAA4AAAB7HZpVk4AAABAAAACJHB0QlIAAABKAAACZHVr -VUEAAAAsAAACrmZyRlUAAAA+AAAC2mh1SFUAAAA0AAADGHpoVFcAAAAaAAADTGtvS1IAAAAiAAADZm5i -Tk8AAAA6AAADiGNzQ1oAAAAoAAADwmhlSUwAAAAkAAAD6nJvUk8AAAAqAAAEDmRlREUAAABOAAAEOGl0 -SVQAAABOAAAEhnN2U0UAAAA4AAAE1HpoQ04AAAAaAAAFDGphSlAAAAAmAAAFJmVsR1IAAAAqAAAFTHB0 -UE8AAABSAAAFdm5sTkwAAABAAAAFyGVzRVMAAABMAAAGCHRoVEgAAAAyAAAGVHRyVFIAAAAkAAAGhmZp -RkkAAABGAAAGqmhySFIAAAA+AAAG8HBsUEwAAABKAAAHLmFyRUcAAAAsAAAHeHJ1UlUAAAA6AAAHpGVu -VVMAAAA8AAAH3gBWAWEAZQBvAGIAZQBjAG4A4QAgAHMAaQB2AOEAIABnAGEAbQBhACAAMgAsADIARwBl -AG4AZQByAGkAcwBrACAAZwByAOUAIAAyACwAMgAgAGcAYQBtAG0AYQAtAHAAcgBvAGYAaQBsAEcAYQBt -AG0AYQAgAGQAZQAgAGcAcgBpAHMAbwBzACAAZwBlAG4A6AByAGkAYwBhACAAMgAuADIAQx6lAHUAIABo -AOwAbgBoACAATQDgAHUAIAB4AOEAbQAgAEMAaAB1AG4AZwAgAEcAYQBtAG0AYQAgADIALgAyAFAAZQBy -AGYAaQBsACAARwBlAG4A6QByAGkAYwBvACAAZABhACAARwBhAG0AYQAgAGQAZQAgAEMAaQBuAHoAYQBz -ACAAMgAsADIEFwQwBDMEMAQ7BEwEPQQwACAARwByAGEAeQAtBDMEMAQ8BDAAIAAyAC4AMgBQAHIAbwBm -AGkAbAAgAGcA6QBuAOkAcgBpAHEAdQBlACAAZwByAGkAcwAgAGcAYQBtAG0AYQAgADIALAAyAMEAbAB0 -AGEAbADhAG4AbwBzACAAcwB6APwAcgBrAGUAIABnAGEAbQBtAGEAIAAyAC4AMpAadShwcJaOUUlepgAy -AC4AMoJyX2ljz4/wx3y8GAAg1ozAyQAgrBC5yAAgADIALgAyACDVBLhc0wzHfABHAGUAbgBlAHIAaQBz -AGsAIABnAHIA5QAgAGcAYQBtAG0AYQAgADIALAAyAC0AcAByAG8AZgBpAGwATwBiAGUAYwBuAOEAIAFh -AGUAZADhACAAZwBhAG0AYQAgADIALgAyBdIF0AXeBdQAIAXQBeQF1QXoACAF2wXcBdwF2QAgADIALgAy -AEcAYQBtAGEAIABnAHIAaQAgAGcAZQBuAGUAcgBpAGMBAwAgADIALAAyAEEAbABsAGcAZQBtAGUAaQBu -AGUAcwAgAEcAcgBhAHUAcwB0AHUAZgBlAG4ALQBQAHIAbwBmAGkAbAAgAEcAYQBtAG0AYQAgADIALAAy -AFAAcgBvAGYAaQBsAG8AIABnAHIAaQBnAGkAbwAgAGcAZQBuAGUAcgBpAGMAbwAgAGQAZQBsAGwAYQAg -AGcAYQBtAG0AYQAgADIALAAyAEcAZQBuAGUAcgBpAHMAawAgAGcAcgDlACAAMgAsADIAIABnAGEAbQBt -AGEAcAByAG8AZgBpAGxmbpAacHBepnz7ZXAAMgAuADJjz4/wZYdO9k4AgiwwsDDsMKQwrDDzMN4AIAAy -AC4AMgAgMNcw7TDVMKEwpDDrA5MDtQO9A7kDugPMACADkwO6A8EDuQAgA5MDrAO8A7wDsQAgADIALgAy -AFAAZQByAGYAaQBsACAAZwBlAG4A6QByAGkAYwBvACAAZABlACAAYwBpAG4AegBlAG4AdABvAHMAIABk -AGEAIABHAGEAbQBtAGEAIAAyACwAMgBBAGwAZwBlAG0AZQBlAG4AIABnAHIAaQBqAHMAIABnAGEAbQBt -AGEAIAAyACwAMgAtAHAAcgBvAGYAaQBlAGwAUABlAHIAZgBpAGwAIABnAGUAbgDpAHIAaQBjAG8AIABk -AGUAIABnAGEAbQBtAGEAIABkAGUAIABnAHIAaQBzAGUAcwAgADIALAAyDiMOMQ4HDioONQ5BDgEOIQ4h -DjIOQA4BDiMOIg5MDhcOMQ5IDicORA4bACAAMgAuADIARwBlAG4AZQBsACAARwByAGkAIABHAGEAbQBh -ACAAMgAsADIAWQBsAGUAaQBuAGUAbgAgAGgAYQByAG0AYQBhAG4AIABnAGEAbQBtAGEAIAAyACwAMgAg -AC0AcAByAG8AZgBpAGkAbABpAEcAZQBuAGUAcgBpAQ0AawBpACAARwByAGEAeQAgAEcAYQBtAG0AYQAg -ADIALgAyACAAcAByAG8AZgBpAGwAVQBuAGkAdwBlAHIAcwBhAGwAbgB5ACAAcAByAG8AZgBpAGwAIABz -AHoAYQByAG8BWwBjAGkAIABnAGEAbQBtAGEAIAAyACwAMgY6BicGRQYnACAAMgAuADIAIAZEBkgGRgAg -BjEGRQYnBi8GSgAgBjkGJwZFBB4EMQRJBDAETwAgBEEENQRABDAETwAgBDMEMAQ8BDwEMAAgADIALAAy -AC0EPwRABD4ERAQ4BDsETABHAGUAbgBlAHIAaQBjACAARwByAGEAeQAgAEcAYQBtAG0AYQAgADIALgAy -ACAAUAByAG8AZgBpAGwAZQAAdGV4dAAAAABDb3B5cmlnaHQgQXBwbGUgSW5jLiwgMjAxMgAAWFlaIAAA -AAAAAPNRAAEAAAABFsxjdXJ2AAAAAAAABAAAAAAFAAoADwAUABkAHgAjACgALQAyADcAOwBAAEUASgBP -AFQAWQBeAGMAaABtAHIAdwB8AIEAhgCLAJAAlQCaAJ8ApACpAK4AsgC3ALwAwQDGAMsA0ADVANsA4ADl -AOsA8AD2APsBAQEHAQ0BEwEZAR8BJQErATIBOAE+AUUBTAFSAVkBYAFnAW4BdQF8AYMBiwGSAZoBoQGp -AbEBuQHBAckB0QHZAeEB6QHyAfoCAwIMAhQCHQImAi8COAJBAksCVAJdAmcCcQJ6AoQCjgKYAqICrAK2 -AsECywLVAuAC6wL1AwADCwMWAyEDLQM4A0MDTwNaA2YDcgN+A4oDlgOiA64DugPHA9MD4APsA/kEBgQT -BCAELQQ7BEgEVQRjBHEEfgSMBJoEqAS2BMQE0wThBPAE/gUNBRwFKwU6BUkFWAVnBXcFhgWWBaYFtQXF -BdUF5QX2BgYGFgYnBjcGSAZZBmoGewaMBp0GrwbABtEG4wb1BwcHGQcrBz0HTwdhB3QHhgeZB6wHvwfS -B+UH+AgLCB8IMghGCFoIbgiCCJYIqgi+CNII5wj7CRAJJQk6CU8JZAl5CY8JpAm6Cc8J5Qn7ChEKJwo9 -ClQKagqBCpgKrgrFCtwK8wsLCyILOQtRC2kLgAuYC7ALyAvhC/kMEgwqDEMMXAx1DI4MpwzADNkM8w0N -DSYNQA1aDXQNjg2pDcMN3g34DhMOLg5JDmQOfw6bDrYO0g7uDwkPJQ9BD14Peg+WD7MPzw/sEAkQJhBD -EGEQfhCbELkQ1xD1ERMRMRFPEW0RjBGqEckR6BIHEiYSRRJkEoQSoxLDEuMTAxMjE0MTYxODE6QTxRPl -FAYUJxRJFGoUixStFM4U8BUSFTQVVhV4FZsVvRXgFgMWJhZJFmwWjxayFtYW+hcdF0EXZReJF64X0hf3 -GBsYQBhlGIoYrxjVGPoZIBlFGWsZkRm3Gd0aBBoqGlEadxqeGsUa7BsUGzsbYxuKG7Ib2hwCHCocUhx7 -HKMczBz1HR4dRx1wHZkdwx3sHhYeQB5qHpQevh7pHxMfPh9pH5Qfvx/qIBUgQSBsIJggxCDwIRwhSCF1 -IaEhziH7IiciVSKCIq8i3SMKIzgjZiOUI8Ij8CQfJE0kfCSrJNolCSU4JWgllyXHJfcmJyZXJocmtybo -JxgnSSd6J6sn3CgNKD8ocSiiKNQpBik4KWspnSnQKgIqNSpoKpsqzysCKzYraSudK9EsBSw5LG4soizX -LQwtQS12Last4S4WLkwugi63Lu4vJC9aL5Evxy/+MDUwbDCkMNsxEjFKMYIxujHyMioyYzKbMtQzDTNG -M38zuDPxNCs0ZTSeNNg1EzVNNYc1wjX9Njc2cjauNuk3JDdgN5w31zgUOFA4jDjIOQU5Qjl/Obw5+To2 -OnQ6sjrvOy07azuqO+g8JzxlPKQ84z0iPWE9oT3gPiA+YD6gPuA/IT9hP6I/4kAjQGRApkDnQSlBakGs -Qe5CMEJyQrVC90M6Q31DwEQDREdEikTORRJFVUWaRd5GIkZnRqtG8Ec1R3tHwEgFSEtIkUjXSR1JY0mp -SfBKN0p9SsRLDEtTS5pL4kwqTHJMuk0CTUpNk03cTiVObk63TwBPSU+TT91QJ1BxULtRBlFQUZtR5lIx -UnxSx1MTU19TqlP2VEJUj1TbVShVdVXCVg9WXFapVvdXRFeSV+BYL1h9WMtZGllpWbhaB1pWWqZa9VtF -W5Vb5Vw1XIZc1l0nXXhdyV4aXmxevV8PX2Ffs2AFYFdgqmD8YU9homH1YklinGLwY0Njl2PrZEBklGTp -ZT1lkmXnZj1mkmboZz1nk2fpaD9olmjsaUNpmmnxakhqn2r3a09rp2v/bFdsr20IbWBtuW4SbmtuxG8e -b3hv0XArcIZw4HE6cZVx8HJLcqZzAXNdc7h0FHRwdMx1KHWFdeF2Pnabdvh3VnezeBF4bnjMeSp5iXnn -ekZ6pXsEe2N7wnwhfIF84X1BfaF+AX5ifsJ/I3+Ef+WAR4CogQqBa4HNgjCCkoL0g1eDuoQdhICE44VH -hauGDoZyhteHO4efiASIaYjOiTOJmYn+imSKyoswi5aL/IxjjMqNMY2Yjf+OZo7OjzaPnpAGkG6Q1pE/ -kaiSEZJ6kuOTTZO2lCCUipT0lV+VyZY0lp+XCpd1l+CYTJi4mSSZkJn8mmia1ZtCm6+cHJyJnPedZJ3S -nkCerp8dn4uf+qBpoNihR6G2oiailqMGo3aj5qRWpMelOKWpphqmi6b9p26n4KhSqMSpN6mpqhyqj6sC -q3Wr6axcrNCtRK24ri2uoa8Wr4uwALB1sOqxYLHWskuywrM4s660JbSctRO1irYBtnm28Ldot+C4WbjR -uUq5wro7urW7LrunvCG8m70VvY++Cr6Evv+/er/1wHDA7MFnwePCX8Lbw1jD1MRRxM7FS8XIxkbGw8dB -x7/IPci8yTrJuco4yrfLNsu2zDXMtc01zbXONs62zzfPuNA50LrRPNG+0j/SwdNE08bUSdTL1U7V0dZV -1tjXXNfg2GTY6Nls2fHadtr724DcBdyK3RDdlt4c3qLfKd+v4DbgveFE4cziU+Lb42Pj6+Rz5PzlhOYN -5pbnH+ep6DLovOlG6dDqW+rl63Dr++yG7RHtnO4o7rTvQO/M8Fjw5fFy8f/yjPMZ86f0NPTC9VD13vZt -9vv3ivgZ+Kj5OPnH+lf65/t3/Af8mP0p/br+S/7c/23//9IrLC0uWiRjbGFzc25hbWVYJGNsYXNzZXNf -EBBOU0JpdG1hcEltYWdlUmVwoy0vMFpOU0ltYWdlUmVwWE5TT2JqZWN00issMjNXTlNBcnJheaIyMNIr -LDU2Xk5TTXV0YWJsZUFycmF5ozUyMNM4OQ86OzxXTlNXaGl0ZVxOU0NvbG9yU3BhY2VEMCAwABADgAzS -Kyw+P1dOU0NvbG9yoj4w0issQUJXTlNJbWFnZaJBMAAIABEAGgAkACkAMgA3AEkATABRAFMAYgBoAHUA -fACLAJIAnwCmAK4AsACyALQAuQC7AL0AxADJANQA1gDYANoA3wDiAOQA5gDoAO8BBgEiASQBJhOWE5sT -phOvE8ITxhPRE9oT3xPnE+oT7xP+FAIUCRQRFB4UIxQlFCcULBQ0FDcUPBREAAAAAAAAAgEAAAAAAAAA -QwAAAAAAAAAAAAAAAAAAFEc +S2V5ZWRBcmNoaXZlctEICVRyb290gAGuCwwbHCEaJissMzY5P0JVJG51bGzXDQ4PEBESExQVFhcYGRpW +TlNTaXplViRjbGFzc1xOU0ltYWdlRmxhZ3NWTlNSZXBzW05TVGludENvbG9yV05TQ29sb3JeTlNSZXNp +emluZ01vZGWAAoANEiDDAACAA4AAgAsQAFZ7MSwgMX3SHQ4eIFpOUy5vYmplY3RzoR+ABIAK0h0OIiWi +IySABYAGgAnTDicoKSoaXxAUTlNUSUZGUmVwcmVzZW50YXRpb25fEBlOU0ludGVybmFsTGF5b3V0RGly +ZWN0aW9ugAiAB08REmxNTQAqAAAACgAAABABAAADAAAAAQABAAABAQADAAAAAQABAAABAgADAAAAAgAI +AAgBAwADAAAAAQABAAABBgADAAAAAQABAAABCgADAAAAAQABAAABEQAEAAAAAQAAAAgBEgADAAAAAQAB +AAABFQADAAAAAQACAAABFgADAAAAAQABAAABFwAEAAAAAQAAAAIBHAADAAAAAQABAAABKAADAAAAAQAC +AAABUgADAAAAAQABAAABUwADAAAAAgABAAGHcwAHAAARnAAAANAAAAAAAAARnGFwcGwCAAAAbW50ckdS +QVlYWVogB9wACAAXAA8ALgAPYWNzcEFQUEwAAAAAbm9uZQAAAAAAAAAAAAAAAAAAAAAAAPbWAAEAAAAA +0y1hcHBsAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFZGVzYwAA +AMAAAAB5ZHNjbQAAATwAAAgaY3BydAAACVgAAAAjd3RwdAAACXwAAAAUa1RSQwAACZAAAAgMZGVzYwAA +AAAAAAAfR2VuZXJpYyBHcmF5IEdhbW1hIDIuMiBQcm9maWxlAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAG1s +dWMAAAAAAAAAHwAAAAxza1NLAAAALgAAAYRkYURLAAAAOgAAAbJjYUVTAAAAOAAAAex2aVZOAAAAQAAA +AiRwdEJSAAAASgAAAmR1a1VBAAAALAAAAq5mckZVAAAAPgAAAtpodUhVAAAANAAAAxh6aFRXAAAAGgAA +A0xrb0tSAAAAIgAAA2ZuYk5PAAAAOgAAA4hjc0NaAAAAKAAAA8JoZUlMAAAAJAAAA+pyb1JPAAAAKgAA +BA5kZURFAAAATgAABDhpdElUAAAATgAABIZzdlNFAAAAOAAABNR6aENOAAAAGgAABQxqYUpQAAAAJgAA +BSZlbEdSAAAAKgAABUxwdFBPAAAAUgAABXZubE5MAAAAQAAABchlc0VTAAAATAAABgh0aFRIAAAAMgAA +BlR0clRSAAAAJAAABoZmaUZJAAAARgAABqpockhSAAAAPgAABvBwbFBMAAAASgAABy5hckVHAAAALAAA +B3hydVJVAAAAOgAAB6RlblVTAAAAPAAAB94AVgFhAGUAbwBiAGUAYwBuAOEAIABzAGkAdgDhACAAZwBh +AG0AYQAgADIALAAyAEcAZQBuAGUAcgBpAHMAawAgAGcAcgDlACAAMgAsADIAIABnAGEAbQBtAGEALQBw +AHIAbwBmAGkAbABHAGEAbQBtAGEAIABkAGUAIABnAHIAaQBzAG8AcwAgAGcAZQBuAOgAcgBpAGMAYQAg +ADIALgAyAEMepQB1ACAAaADsAG4AaAAgAE0A4AB1ACAAeADhAG0AIABDAGgAdQBuAGcAIABHAGEAbQBt +AGEAIAAyAC4AMgBQAGUAcgBmAGkAbAAgAEcAZQBuAOkAcgBpAGMAbwAgAGQAYQAgAEcAYQBtAGEAIABk +AGUAIABDAGkAbgB6AGEAcwAgADIALAAyBBcEMAQzBDAEOwRMBD0EMAAgAEcAcgBhAHkALQQzBDAEPAQw +ACAAMgAuADIAUAByAG8AZgBpAGwAIABnAOkAbgDpAHIAaQBxAHUAZQAgAGcAcgBpAHMAIABnAGEAbQBt +AGEAIAAyACwAMgDBAGwAdABhAGwA4QBuAG8AcwAgAHMAegD8AHIAawBlACAAZwBhAG0AbQBhACAAMgAu +ADKQGnUocHCWjlFJXqYAMgAuADKCcl9pY8+P8Md8vBgAINaMwMkAIKwQucgAIAAyAC4AMgAg1QS4XNMM +x3wARwBlAG4AZQByAGkAcwBrACAAZwByAOUAIABnAGEAbQBtAGEAIAAyACwAMgAtAHAAcgBvAGYAaQBs +AE8AYgBlAGMAbgDhACABYQBlAGQA4QAgAGcAYQBtAGEAIAAyAC4AMgXSBdAF3gXUACAF0AXkBdUF6AAg +BdsF3AXcBdkAIAAyAC4AMgBHAGEAbQBhACAAZwByAGkAIABnAGUAbgBlAHIAaQBjAQMAIAAyACwAMgBB +AGwAbABnAGUAbQBlAGkAbgBlAHMAIABHAHIAYQB1AHMAdAB1AGYAZQBuAC0AUAByAG8AZgBpAGwAIABH +AGEAbQBtAGEAIAAyACwAMgBQAHIAbwBmAGkAbABvACAAZwByAGkAZwBpAG8AIABnAGUAbgBlAHIAaQBj +AG8AIABkAGUAbABsAGEAIABnAGEAbQBtAGEAIAAyACwAMgBHAGUAbgBlAHIAaQBzAGsAIABnAHIA5QAg +ADIALAAyACAAZwBhAG0AbQBhAHAAcgBvAGYAaQBsZm6QGnBwXqZ8+2VwADIALgAyY8+P8GWHTvZOAIIs +MLAw7DCkMKww8zDeACAAMgAuADIAIDDXMO0w1TChMKQw6wOTA7UDvQO5A7oDzAAgA5MDugPBA7kAIAOT +A6wDvAO8A7EAIAAyAC4AMgBQAGUAcgBmAGkAbAAgAGcAZQBuAOkAcgBpAGMAbwAgAGQAZQAgAGMAaQBu +AHoAZQBuAHQAbwBzACAAZABhACAARwBhAG0AbQBhACAAMgAsADIAQQBsAGcAZQBtAGUAZQBuACAAZwBy +AGkAagBzACAAZwBhAG0AbQBhACAAMgAsADIALQBwAHIAbwBmAGkAZQBsAFAAZQByAGYAaQBsACAAZwBl +AG4A6QByAGkAYwBvACAAZABlACAAZwBhAG0AbQBhACAAZABlACAAZwByAGkAcwBlAHMAIAAyACwAMg4j +DjEOBw4qDjUOQQ4BDiEOIQ4yDkAOAQ4jDiIOTA4XDjEOSA4nDkQOGwAgADIALgAyAEcAZQBuAGUAbAAg +AEcAcgBpACAARwBhAG0AYQAgADIALAAyAFkAbABlAGkAbgBlAG4AIABoAGEAcgBtAGEAYQBuACAAZwBh +AG0AbQBhACAAMgAsADIAIAAtAHAAcgBvAGYAaQBpAGwAaQBHAGUAbgBlAHIAaQENAGsAaQAgAEcAcgBh +AHkAIABHAGEAbQBtAGEAIAAyAC4AMgAgAHAAcgBvAGYAaQBsAFUAbgBpAHcAZQByAHMAYQBsAG4AeQAg +AHAAcgBvAGYAaQBsACAAcwB6AGEAcgBvAVsAYwBpACAAZwBhAG0AbQBhACAAMgAsADIGOgYnBkUGJwAg +ADIALgAyACAGRAZIBkYAIAYxBkUGJwYvBkoAIAY5BicGRQQeBDEESQQwBE8AIARBBDUEQAQwBE8AIAQz +BDAEPAQ8BDAAIAAyACwAMgAtBD8EQAQ+BEQEOAQ7BEwARwBlAG4AZQByAGkAYwAgAEcAcgBhAHkAIABH +AGEAbQBtAGEAIAAyAC4AMgAgAFAAcgBvAGYAaQBsAGUAAHRleHQAAAAAQ29weXJpZ2h0IEFwcGxlIElu +Yy4sIDIwMTIAAFhZWiAAAAAAAADzUQABAAAAARbMY3VydgAAAAAAAAQAAAAABQAKAA8AFAAZAB4AIwAo +AC0AMgA3ADsAQABFAEoATwBUAFkAXgBjAGgAbQByAHcAfACBAIYAiwCQAJUAmgCfAKQAqQCuALIAtwC8 +AMEAxgDLANAA1QDbAOAA5QDrAPAA9gD7AQEBBwENARMBGQEfASUBKwEyATgBPgFFAUwBUgFZAWABZwFu +AXUBfAGDAYsBkgGaAaEBqQGxAbkBwQHJAdEB2QHhAekB8gH6AgMCDAIUAh0CJgIvAjgCQQJLAlQCXQJn +AnECegKEAo4CmAKiAqwCtgLBAssC1QLgAusC9QMAAwsDFgMhAy0DOANDA08DWgNmA3IDfgOKA5YDogOu +A7oDxwPTA+AD7AP5BAYEEwQgBC0EOwRIBFUEYwRxBH4EjASaBKgEtgTEBNME4QTwBP4FDQUcBSsFOgVJ +BVgFZwV3BYYFlgWmBbUFxQXVBeUF9gYGBhYGJwY3BkgGWQZqBnsGjAadBq8GwAbRBuMG9QcHBxkHKwc9 +B08HYQd0B4YHmQesB78H0gflB/gICwgfCDIIRghaCG4IggiWCKoIvgjSCOcI+wkQCSUJOglPCWQJeQmP +CaQJugnPCeUJ+woRCicKPQpUCmoKgQqYCq4KxQrcCvMLCwsiCzkLUQtpC4ALmAuwC8gL4Qv5DBIMKgxD +DFwMdQyODKcMwAzZDPMNDQ0mDUANWg10DY4NqQ3DDd4N+A4TDi4OSQ5kDn8Omw62DtIO7g8JDyUPQQ9e +D3oPlg+zD88P7BAJECYQQxBhEH4QmxC5ENcQ9RETETERTxFtEYwRqhHJEegSBxImEkUSZBKEEqMSwxLj +EwMTIxNDE2MTgxOkE8UT5RQGFCcUSRRqFIsUrRTOFPAVEhU0FVYVeBWbFb0V4BYDFiYWSRZsFo8WshbW +FvoXHRdBF2UXiReuF9IX9xgbGEAYZRiKGK8Y1Rj6GSAZRRlrGZEZtxndGgQaKhpRGncanhrFGuwbFBs7 +G2MbihuyG9ocAhwqHFIcexyjHMwc9R0eHUcdcB2ZHcMd7B4WHkAeah6UHr4e6R8THz4faR+UH78f6iAV +IEEgbCCYIMQg8CEcIUghdSGhIc4h+yInIlUigiKvIt0jCiM4I2YjlCPCI/AkHyRNJHwkqyTaJQklOCVo +JZclxyX3JicmVyaHJrcm6CcYJ0kneierJ9woDSg/KHEooijUKQYpOClrKZ0p0CoCKjUqaCqbKs8rAis2 +K2krnSvRLAUsOSxuLKIs1y0MLUEtdi2rLeEuFi5MLoIuty7uLyQvWi+RL8cv/jA1MGwwpDDbMRIxSjGC +Mbox8jIqMmMymzLUMw0zRjN/M7gz8TQrNGU0njTYNRM1TTWHNcI1/TY3NnI2rjbpNyQ3YDecN9c4FDhQ +OIw4yDkFOUI5fzm8Ofk6Njp0OrI67zstO2s7qjvoPCc8ZTykPOM9Ij1hPaE94D4gPmA+oD7gPyE/YT+i +P+JAI0BkQKZA50EpQWpBrEHuQjBCckK1QvdDOkN9Q8BEA0RHRIpEzkUSRVVFmkXeRiJGZ0arRvBHNUd7 +R8BIBUhLSJFI10kdSWNJqUnwSjdKfUrESwxLU0uaS+JMKkxyTLpNAk1KTZNN3E4lTm5Ot08AT0lPk0/d +UCdQcVC7UQZRUFGbUeZSMVJ8UsdTE1NfU6pT9lRCVI9U21UoVXVVwlYPVlxWqVb3V0RXklfgWC9YfVjL +WRpZaVm4WgdaVlqmWvVbRVuVW+VcNVyGXNZdJ114XcleGl5sXr1fD19hX7NgBWBXYKpg/GFPYaJh9WJJ +Ypxi8GNDY5dj62RAZJRk6WU9ZZJl52Y9ZpJm6Gc9Z5Nn6Wg/aJZo7GlDaZpp8WpIap9q92tPa6dr/2xX +bK9tCG1gbbluEm5rbsRvHm94b9FwK3CGcOBxOnGVcfByS3KmcwFzXXO4dBR0cHTMdSh1hXXhdj52m3b4 +d1Z3s3gReG54zHkqeYl553pGeqV7BHtje8J8IXyBfOF9QX2hfgF+Yn7CfyN/hH/lgEeAqIEKgWuBzYIw +gpKC9INXg7qEHYSAhOOFR4Wrhg6GcobXhzuHn4gEiGmIzokziZmJ/opkisqLMIuWi/yMY4zKjTGNmI3/ +jmaOzo82j56QBpBukNaRP5GokhGSepLjk02TtpQglIqU9JVflcmWNJaflwqXdZfgmEyYuJkkmZCZ/Jpo +mtWbQpuvnByciZz3nWSd0p5Anq6fHZ+Ln/qgaaDYoUehtqImopajBqN2o+akVqTHpTilqaYapoum/adu +p+CoUqjEqTepqaocqo+rAqt1q+msXKzQrUStuK4trqGvFq+LsACwdbDqsWCx1rJLssKzOLOutCW0nLUT +tYq2AbZ5tvC3aLfguFm40blKucK6O7q1uy67p7whvJu9Fb2Pvgq+hL7/v3q/9cBwwOzBZ8Hjwl/C28NY +w9TEUcTOxUvFyMZGxsPHQce/yD3IvMk6ybnKOMq3yzbLtsw1zLXNNc21zjbOts83z7jQOdC60TzRvtI/ +0sHTRNPG1EnUy9VO1dHWVdbY11zX4Nhk2OjZbNnx2nba+9uA3AXcit0Q3ZbeHN6i3ynfr+A24L3hROHM +4lPi2+Nj4+vkc+T85YTmDeaW5x/nqegy6LzpRunQ6lvq5etw6/vshu0R7ZzuKO6070DvzPBY8OXxcvH/ +8ozzGfOn9DT0wvVQ9d72bfb794r4Gfio+Tj5x/pX+uf7d/wH/Jj9Kf26/kv+3P9t///SLS4vMFokY2xh +c3NuYW1lWCRjbGFzc2VzXxAQTlNCaXRtYXBJbWFnZVJlcKMvMTJaTlNJbWFnZVJlcFhOU09iamVjdNIt +LjQ1V05TQXJyYXmiNDLSLS43OF5OU011dGFibGVBcnJheaM3NDLTOjsOPD0+V05TV2hpdGVcTlNDb2xv +clNwYWNlRDAgMAAQA4AM0i0uQEFXTlNDb2xvcqJAMtItLkNEV05TSW1hZ2WiQzIACAARABoAJAApADIA +NwBJAEwAUQBTAGIAaAB3AH4AhQCSAJkApQCtALwAvgDAAMUAxwDJAMsAzQDUANkA5ADmAOgA6gDvAPIA +9AD2APgA/wEWATIBNAE2E6YTqxO2E78T0hPWE+ET6hPvE/cT+hP/FA4UEhQZFCEULhQzFDUUNxQ8FEQU +RxRMFFQAAAAAAAACAQAAAAAAAABFAAAAAAAAAAAAAAAAAAAUVw diff --git a/i18n/src/main/resources/hr.lproj/Browser.xib b/i18n/src/main/resources/hr.lproj/Browser.xib index 1f264d812c..555ba1ce04 100644 --- a/i18n/src/main/resources/hr.lproj/Browser.xib +++ b/i18n/src/main/resources/hr.lproj/Browser.xib @@ -1,7 +1,7 @@ - + - + @@ -38,7 +38,7 @@ - + @@ -62,27 +62,27 @@ @@ -256,7 +256,7 @@ - + @@ -553,95 +553,95 @@ DQ YnBsaXN0MDDUAQIDBAUGBwpYJHZlcnNpb25ZJGFyY2hpdmVyVCR0b3BYJG9iamVjdHMSAAGGoF8QD05T -S2V5ZWRBcmNoaXZlctEICVRyb290gAGuCwwZGh8UJCkqMTQ3PUBVJG51bGzWDQ4PEBESExQVFhcYVk5T -U2l6ZV5OU1Jlc2l6aW5nTW9kZVYkY2xhc3NcTlNJbWFnZUZsYWdzVk5TUmVwc1dOU0NvbG9ygAIQAIAN -EiDDAACAA4ALVnsxLCAxfdIbDxweWk5TLm9iamVjdHOhHYAEgArSGw8gI6IhIoAFgAaACdMPJSYnKBRf -EBROU1RJRkZSZXByZXNlbnRhdGlvbl8QGU5TSW50ZXJuYWxMYXlvdXREaXJlY3Rpb26ACIAHTxESbE1N -ACoAAAAKAAAAEAEAAAMAAAABAAEAAAEBAAMAAAABAAEAAAECAAMAAAACAAgACAEDAAMAAAABAAEAAAEG -AAMAAAABAAEAAAEKAAMAAAABAAEAAAERAAQAAAABAAAACAESAAMAAAABAAEAAAEVAAMAAAABAAIAAAEW -AAMAAAABAAEAAAEXAAQAAAABAAAAAgEcAAMAAAABAAEAAAEoAAMAAAABAAIAAAFSAAMAAAABAAEAAAFT -AAMAAAACAAEAAYdzAAcAABGcAAAA0AAAAAAAABGcYXBwbAIAAABtbnRyR1JBWVhZWiAH3AAIABcADwAu -AA9hY3NwQVBQTAAAAABub25lAAAAAAAAAAAAAAAAAAAAAAAA9tYAAQAAAADTLWFwcGwAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAVkZXNjAAAAwAAAAHlkc2NtAAABPAAA -CBpjcHJ0AAAJWAAAACN3dHB0AAAJfAAAABRrVFJDAAAJkAAACAxkZXNjAAAAAAAAAB9HZW5lcmljIEdy -YXkgR2FtbWEgMi4yIFByb2ZpbGUAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAbWx1YwAAAAAAAAAfAAAADHNr -U0sAAAAuAAABhGRhREsAAAA6AAABsmNhRVMAAAA4AAAB7HZpVk4AAABAAAACJHB0QlIAAABKAAACZHVr -VUEAAAAsAAACrmZyRlUAAAA+AAAC2mh1SFUAAAA0AAADGHpoVFcAAAAaAAADTGtvS1IAAAAiAAADZm5i -Tk8AAAA6AAADiGNzQ1oAAAAoAAADwmhlSUwAAAAkAAAD6nJvUk8AAAAqAAAEDmRlREUAAABOAAAEOGl0 -SVQAAABOAAAEhnN2U0UAAAA4AAAE1HpoQ04AAAAaAAAFDGphSlAAAAAmAAAFJmVsR1IAAAAqAAAFTHB0 -UE8AAABSAAAFdm5sTkwAAABAAAAFyGVzRVMAAABMAAAGCHRoVEgAAAAyAAAGVHRyVFIAAAAkAAAGhmZp -RkkAAABGAAAGqmhySFIAAAA+AAAG8HBsUEwAAABKAAAHLmFyRUcAAAAsAAAHeHJ1UlUAAAA6AAAHpGVu -VVMAAAA8AAAH3gBWAWEAZQBvAGIAZQBjAG4A4QAgAHMAaQB2AOEAIABnAGEAbQBhACAAMgAsADIARwBl -AG4AZQByAGkAcwBrACAAZwByAOUAIAAyACwAMgAgAGcAYQBtAG0AYQAtAHAAcgBvAGYAaQBsAEcAYQBt -AG0AYQAgAGQAZQAgAGcAcgBpAHMAbwBzACAAZwBlAG4A6AByAGkAYwBhACAAMgAuADIAQx6lAHUAIABo -AOwAbgBoACAATQDgAHUAIAB4AOEAbQAgAEMAaAB1AG4AZwAgAEcAYQBtAG0AYQAgADIALgAyAFAAZQBy -AGYAaQBsACAARwBlAG4A6QByAGkAYwBvACAAZABhACAARwBhAG0AYQAgAGQAZQAgAEMAaQBuAHoAYQBz -ACAAMgAsADIEFwQwBDMEMAQ7BEwEPQQwACAARwByAGEAeQAtBDMEMAQ8BDAAIAAyAC4AMgBQAHIAbwBm -AGkAbAAgAGcA6QBuAOkAcgBpAHEAdQBlACAAZwByAGkAcwAgAGcAYQBtAG0AYQAgADIALAAyAMEAbAB0 -AGEAbADhAG4AbwBzACAAcwB6APwAcgBrAGUAIABnAGEAbQBtAGEAIAAyAC4AMpAadShwcJaOUUlepgAy -AC4AMoJyX2ljz4/wx3y8GAAg1ozAyQAgrBC5yAAgADIALgAyACDVBLhc0wzHfABHAGUAbgBlAHIAaQBz -AGsAIABnAHIA5QAgAGcAYQBtAG0AYQAgADIALAAyAC0AcAByAG8AZgBpAGwATwBiAGUAYwBuAOEAIAFh -AGUAZADhACAAZwBhAG0AYQAgADIALgAyBdIF0AXeBdQAIAXQBeQF1QXoACAF2wXcBdwF2QAgADIALgAy -AEcAYQBtAGEAIABnAHIAaQAgAGcAZQBuAGUAcgBpAGMBAwAgADIALAAyAEEAbABsAGcAZQBtAGUAaQBu -AGUAcwAgAEcAcgBhAHUAcwB0AHUAZgBlAG4ALQBQAHIAbwBmAGkAbAAgAEcAYQBtAG0AYQAgADIALAAy -AFAAcgBvAGYAaQBsAG8AIABnAHIAaQBnAGkAbwAgAGcAZQBuAGUAcgBpAGMAbwAgAGQAZQBsAGwAYQAg -AGcAYQBtAG0AYQAgADIALAAyAEcAZQBuAGUAcgBpAHMAawAgAGcAcgDlACAAMgAsADIAIABnAGEAbQBt -AGEAcAByAG8AZgBpAGxmbpAacHBepnz7ZXAAMgAuADJjz4/wZYdO9k4AgiwwsDDsMKQwrDDzMN4AIAAy -AC4AMgAgMNcw7TDVMKEwpDDrA5MDtQO9A7kDugPMACADkwO6A8EDuQAgA5MDrAO8A7wDsQAgADIALgAy -AFAAZQByAGYAaQBsACAAZwBlAG4A6QByAGkAYwBvACAAZABlACAAYwBpAG4AegBlAG4AdABvAHMAIABk -AGEAIABHAGEAbQBtAGEAIAAyACwAMgBBAGwAZwBlAG0AZQBlAG4AIABnAHIAaQBqAHMAIABnAGEAbQBt -AGEAIAAyACwAMgAtAHAAcgBvAGYAaQBlAGwAUABlAHIAZgBpAGwAIABnAGUAbgDpAHIAaQBjAG8AIABk -AGUAIABnAGEAbQBtAGEAIABkAGUAIABnAHIAaQBzAGUAcwAgADIALAAyDiMOMQ4HDioONQ5BDgEOIQ4h -DjIOQA4BDiMOIg5MDhcOMQ5IDicORA4bACAAMgAuADIARwBlAG4AZQBsACAARwByAGkAIABHAGEAbQBh -ACAAMgAsADIAWQBsAGUAaQBuAGUAbgAgAGgAYQByAG0AYQBhAG4AIABnAGEAbQBtAGEAIAAyACwAMgAg -AC0AcAByAG8AZgBpAGkAbABpAEcAZQBuAGUAcgBpAQ0AawBpACAARwByAGEAeQAgAEcAYQBtAG0AYQAg -ADIALgAyACAAcAByAG8AZgBpAGwAVQBuAGkAdwBlAHIAcwBhAGwAbgB5ACAAcAByAG8AZgBpAGwAIABz -AHoAYQByAG8BWwBjAGkAIABnAGEAbQBtAGEAIAAyACwAMgY6BicGRQYnACAAMgAuADIAIAZEBkgGRgAg -BjEGRQYnBi8GSgAgBjkGJwZFBB4EMQRJBDAETwAgBEEENQRABDAETwAgBDMEMAQ8BDwEMAAgADIALAAy -AC0EPwRABD4ERAQ4BDsETABHAGUAbgBlAHIAaQBjACAARwByAGEAeQAgAEcAYQBtAG0AYQAgADIALgAy -ACAAUAByAG8AZgBpAGwAZQAAdGV4dAAAAABDb3B5cmlnaHQgQXBwbGUgSW5jLiwgMjAxMgAAWFlaIAAA -AAAAAPNRAAEAAAABFsxjdXJ2AAAAAAAABAAAAAAFAAoADwAUABkAHgAjACgALQAyADcAOwBAAEUASgBP -AFQAWQBeAGMAaABtAHIAdwB8AIEAhgCLAJAAlQCaAJ8ApACpAK4AsgC3ALwAwQDGAMsA0ADVANsA4ADl -AOsA8AD2APsBAQEHAQ0BEwEZAR8BJQErATIBOAE+AUUBTAFSAVkBYAFnAW4BdQF8AYMBiwGSAZoBoQGp -AbEBuQHBAckB0QHZAeEB6QHyAfoCAwIMAhQCHQImAi8COAJBAksCVAJdAmcCcQJ6AoQCjgKYAqICrAK2 -AsECywLVAuAC6wL1AwADCwMWAyEDLQM4A0MDTwNaA2YDcgN+A4oDlgOiA64DugPHA9MD4APsA/kEBgQT -BCAELQQ7BEgEVQRjBHEEfgSMBJoEqAS2BMQE0wThBPAE/gUNBRwFKwU6BUkFWAVnBXcFhgWWBaYFtQXF -BdUF5QX2BgYGFgYnBjcGSAZZBmoGewaMBp0GrwbABtEG4wb1BwcHGQcrBz0HTwdhB3QHhgeZB6wHvwfS -B+UH+AgLCB8IMghGCFoIbgiCCJYIqgi+CNII5wj7CRAJJQk6CU8JZAl5CY8JpAm6Cc8J5Qn7ChEKJwo9 -ClQKagqBCpgKrgrFCtwK8wsLCyILOQtRC2kLgAuYC7ALyAvhC/kMEgwqDEMMXAx1DI4MpwzADNkM8w0N -DSYNQA1aDXQNjg2pDcMN3g34DhMOLg5JDmQOfw6bDrYO0g7uDwkPJQ9BD14Peg+WD7MPzw/sEAkQJhBD -EGEQfhCbELkQ1xD1ERMRMRFPEW0RjBGqEckR6BIHEiYSRRJkEoQSoxLDEuMTAxMjE0MTYxODE6QTxRPl -FAYUJxRJFGoUixStFM4U8BUSFTQVVhV4FZsVvRXgFgMWJhZJFmwWjxayFtYW+hcdF0EXZReJF64X0hf3 -GBsYQBhlGIoYrxjVGPoZIBlFGWsZkRm3Gd0aBBoqGlEadxqeGsUa7BsUGzsbYxuKG7Ib2hwCHCocUhx7 -HKMczBz1HR4dRx1wHZkdwx3sHhYeQB5qHpQevh7pHxMfPh9pH5Qfvx/qIBUgQSBsIJggxCDwIRwhSCF1 -IaEhziH7IiciVSKCIq8i3SMKIzgjZiOUI8Ij8CQfJE0kfCSrJNolCSU4JWgllyXHJfcmJyZXJocmtybo -JxgnSSd6J6sn3CgNKD8ocSiiKNQpBik4KWspnSnQKgIqNSpoKpsqzysCKzYraSudK9EsBSw5LG4soizX -LQwtQS12Last4S4WLkwugi63Lu4vJC9aL5Evxy/+MDUwbDCkMNsxEjFKMYIxujHyMioyYzKbMtQzDTNG -M38zuDPxNCs0ZTSeNNg1EzVNNYc1wjX9Njc2cjauNuk3JDdgN5w31zgUOFA4jDjIOQU5Qjl/Obw5+To2 -OnQ6sjrvOy07azuqO+g8JzxlPKQ84z0iPWE9oT3gPiA+YD6gPuA/IT9hP6I/4kAjQGRApkDnQSlBakGs -Qe5CMEJyQrVC90M6Q31DwEQDREdEikTORRJFVUWaRd5GIkZnRqtG8Ec1R3tHwEgFSEtIkUjXSR1JY0mp -SfBKN0p9SsRLDEtTS5pL4kwqTHJMuk0CTUpNk03cTiVObk63TwBPSU+TT91QJ1BxULtRBlFQUZtR5lIx -UnxSx1MTU19TqlP2VEJUj1TbVShVdVXCVg9WXFapVvdXRFeSV+BYL1h9WMtZGllpWbhaB1pWWqZa9VtF -W5Vb5Vw1XIZc1l0nXXhdyV4aXmxevV8PX2Ffs2AFYFdgqmD8YU9homH1YklinGLwY0Njl2PrZEBklGTp -ZT1lkmXnZj1mkmboZz1nk2fpaD9olmjsaUNpmmnxakhqn2r3a09rp2v/bFdsr20IbWBtuW4SbmtuxG8e -b3hv0XArcIZw4HE6cZVx8HJLcqZzAXNdc7h0FHRwdMx1KHWFdeF2Pnabdvh3VnezeBF4bnjMeSp5iXnn -ekZ6pXsEe2N7wnwhfIF84X1BfaF+AX5ifsJ/I3+Ef+WAR4CogQqBa4HNgjCCkoL0g1eDuoQdhICE44VH -hauGDoZyhteHO4efiASIaYjOiTOJmYn+imSKyoswi5aL/IxjjMqNMY2Yjf+OZo7OjzaPnpAGkG6Q1pE/ -kaiSEZJ6kuOTTZO2lCCUipT0lV+VyZY0lp+XCpd1l+CYTJi4mSSZkJn8mmia1ZtCm6+cHJyJnPedZJ3S -nkCerp8dn4uf+qBpoNihR6G2oiailqMGo3aj5qRWpMelOKWpphqmi6b9p26n4KhSqMSpN6mpqhyqj6sC -q3Wr6axcrNCtRK24ri2uoa8Wr4uwALB1sOqxYLHWskuywrM4s660JbSctRO1irYBtnm28Ldot+C4WbjR -uUq5wro7urW7LrunvCG8m70VvY++Cr6Evv+/er/1wHDA7MFnwePCX8Lbw1jD1MRRxM7FS8XIxkbGw8dB -x7/IPci8yTrJuco4yrfLNsu2zDXMtc01zbXONs62zzfPuNA50LrRPNG+0j/SwdNE08bUSdTL1U7V0dZV -1tjXXNfg2GTY6Nls2fHadtr724DcBdyK3RDdlt4c3qLfKd+v4DbgveFE4cziU+Lb42Pj6+Rz5PzlhOYN -5pbnH+ep6DLovOlG6dDqW+rl63Dr++yG7RHtnO4o7rTvQO/M8Fjw5fFy8f/yjPMZ86f0NPTC9VD13vZt -9vv3ivgZ+Kj5OPnH+lf65/t3/Af8mP0p/br+S/7c/23//9IrLC0uWiRjbGFzc25hbWVYJGNsYXNzZXNf -EBBOU0JpdG1hcEltYWdlUmVwoy0vMFpOU0ltYWdlUmVwWE5TT2JqZWN00issMjNXTlNBcnJheaIyMNIr -LDU2Xk5TTXV0YWJsZUFycmF5ozUyMNM4OQ86OzxXTlNXaGl0ZVxOU0NvbG9yU3BhY2VEMCAwABADgAzS -Kyw+P1dOU0NvbG9yoj4w0issQUJXTlNJbWFnZaJBMAAIABEAGgAkACkAMgA3AEkATABRAFMAYgBoAHUA -fACLAJIAnwCmAK4AsACyALQAuQC7AL0AxADJANQA1gDYANoA3wDiAOQA5gDoAO8BBgEiASQBJhOWE5sT -phOvE8ITxhPRE9oT3xPnE+oT7xP+FAIUCRQRFB4UIxQlFCcULBQ0FDcUPBREAAAAAAAAAgEAAAAAAAAA -QwAAAAAAAAAAAAAAAAAAFEc +S2V5ZWRBcmNoaXZlctEICVRyb290gAGuCwwbHCEaJissMzY5P0JVJG51bGzXDQ4PEBESExQVFhcYGRpW +TlNTaXplViRjbGFzc1xOU0ltYWdlRmxhZ3NWTlNSZXBzW05TVGludENvbG9yV05TQ29sb3JeTlNSZXNp +emluZ01vZGWAAoANEiDDAACAA4AAgAsQAFZ7MSwgMX3SHQ4eIFpOUy5vYmplY3RzoR+ABIAK0h0OIiWi +IySABYAGgAnTDicoKSoaXxAUTlNUSUZGUmVwcmVzZW50YXRpb25fEBlOU0ludGVybmFsTGF5b3V0RGly +ZWN0aW9ugAiAB08REmxNTQAqAAAACgAAABABAAADAAAAAQABAAABAQADAAAAAQABAAABAgADAAAAAgAI +AAgBAwADAAAAAQABAAABBgADAAAAAQABAAABCgADAAAAAQABAAABEQAEAAAAAQAAAAgBEgADAAAAAQAB +AAABFQADAAAAAQACAAABFgADAAAAAQABAAABFwAEAAAAAQAAAAIBHAADAAAAAQABAAABKAADAAAAAQAC +AAABUgADAAAAAQABAAABUwADAAAAAgABAAGHcwAHAAARnAAAANAAAAAAAAARnGFwcGwCAAAAbW50ckdS +QVlYWVogB9wACAAXAA8ALgAPYWNzcEFQUEwAAAAAbm9uZQAAAAAAAAAAAAAAAAAAAAAAAPbWAAEAAAAA +0y1hcHBsAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFZGVzYwAA +AMAAAAB5ZHNjbQAAATwAAAgaY3BydAAACVgAAAAjd3RwdAAACXwAAAAUa1RSQwAACZAAAAgMZGVzYwAA +AAAAAAAfR2VuZXJpYyBHcmF5IEdhbW1hIDIuMiBQcm9maWxlAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAG1s +dWMAAAAAAAAAHwAAAAxza1NLAAAALgAAAYRkYURLAAAAOgAAAbJjYUVTAAAAOAAAAex2aVZOAAAAQAAA +AiRwdEJSAAAASgAAAmR1a1VBAAAALAAAAq5mckZVAAAAPgAAAtpodUhVAAAANAAAAxh6aFRXAAAAGgAA +A0xrb0tSAAAAIgAAA2ZuYk5PAAAAOgAAA4hjc0NaAAAAKAAAA8JoZUlMAAAAJAAAA+pyb1JPAAAAKgAA +BA5kZURFAAAATgAABDhpdElUAAAATgAABIZzdlNFAAAAOAAABNR6aENOAAAAGgAABQxqYUpQAAAAJgAA +BSZlbEdSAAAAKgAABUxwdFBPAAAAUgAABXZubE5MAAAAQAAABchlc0VTAAAATAAABgh0aFRIAAAAMgAA +BlR0clRSAAAAJAAABoZmaUZJAAAARgAABqpockhSAAAAPgAABvBwbFBMAAAASgAABy5hckVHAAAALAAA +B3hydVJVAAAAOgAAB6RlblVTAAAAPAAAB94AVgFhAGUAbwBiAGUAYwBuAOEAIABzAGkAdgDhACAAZwBh +AG0AYQAgADIALAAyAEcAZQBuAGUAcgBpAHMAawAgAGcAcgDlACAAMgAsADIAIABnAGEAbQBtAGEALQBw +AHIAbwBmAGkAbABHAGEAbQBtAGEAIABkAGUAIABnAHIAaQBzAG8AcwAgAGcAZQBuAOgAcgBpAGMAYQAg +ADIALgAyAEMepQB1ACAAaADsAG4AaAAgAE0A4AB1ACAAeADhAG0AIABDAGgAdQBuAGcAIABHAGEAbQBt +AGEAIAAyAC4AMgBQAGUAcgBmAGkAbAAgAEcAZQBuAOkAcgBpAGMAbwAgAGQAYQAgAEcAYQBtAGEAIABk +AGUAIABDAGkAbgB6AGEAcwAgADIALAAyBBcEMAQzBDAEOwRMBD0EMAAgAEcAcgBhAHkALQQzBDAEPAQw +ACAAMgAuADIAUAByAG8AZgBpAGwAIABnAOkAbgDpAHIAaQBxAHUAZQAgAGcAcgBpAHMAIABnAGEAbQBt +AGEAIAAyACwAMgDBAGwAdABhAGwA4QBuAG8AcwAgAHMAegD8AHIAawBlACAAZwBhAG0AbQBhACAAMgAu +ADKQGnUocHCWjlFJXqYAMgAuADKCcl9pY8+P8Md8vBgAINaMwMkAIKwQucgAIAAyAC4AMgAg1QS4XNMM +x3wARwBlAG4AZQByAGkAcwBrACAAZwByAOUAIABnAGEAbQBtAGEAIAAyACwAMgAtAHAAcgBvAGYAaQBs +AE8AYgBlAGMAbgDhACABYQBlAGQA4QAgAGcAYQBtAGEAIAAyAC4AMgXSBdAF3gXUACAF0AXkBdUF6AAg +BdsF3AXcBdkAIAAyAC4AMgBHAGEAbQBhACAAZwByAGkAIABnAGUAbgBlAHIAaQBjAQMAIAAyACwAMgBB +AGwAbABnAGUAbQBlAGkAbgBlAHMAIABHAHIAYQB1AHMAdAB1AGYAZQBuAC0AUAByAG8AZgBpAGwAIABH +AGEAbQBtAGEAIAAyACwAMgBQAHIAbwBmAGkAbABvACAAZwByAGkAZwBpAG8AIABnAGUAbgBlAHIAaQBj +AG8AIABkAGUAbABsAGEAIABnAGEAbQBtAGEAIAAyACwAMgBHAGUAbgBlAHIAaQBzAGsAIABnAHIA5QAg +ADIALAAyACAAZwBhAG0AbQBhAHAAcgBvAGYAaQBsZm6QGnBwXqZ8+2VwADIALgAyY8+P8GWHTvZOAIIs +MLAw7DCkMKww8zDeACAAMgAuADIAIDDXMO0w1TChMKQw6wOTA7UDvQO5A7oDzAAgA5MDugPBA7kAIAOT +A6wDvAO8A7EAIAAyAC4AMgBQAGUAcgBmAGkAbAAgAGcAZQBuAOkAcgBpAGMAbwAgAGQAZQAgAGMAaQBu +AHoAZQBuAHQAbwBzACAAZABhACAARwBhAG0AbQBhACAAMgAsADIAQQBsAGcAZQBtAGUAZQBuACAAZwBy +AGkAagBzACAAZwBhAG0AbQBhACAAMgAsADIALQBwAHIAbwBmAGkAZQBsAFAAZQByAGYAaQBsACAAZwBl +AG4A6QByAGkAYwBvACAAZABlACAAZwBhAG0AbQBhACAAZABlACAAZwByAGkAcwBlAHMAIAAyACwAMg4j +DjEOBw4qDjUOQQ4BDiEOIQ4yDkAOAQ4jDiIOTA4XDjEOSA4nDkQOGwAgADIALgAyAEcAZQBuAGUAbAAg +AEcAcgBpACAARwBhAG0AYQAgADIALAAyAFkAbABlAGkAbgBlAG4AIABoAGEAcgBtAGEAYQBuACAAZwBh +AG0AbQBhACAAMgAsADIAIAAtAHAAcgBvAGYAaQBpAGwAaQBHAGUAbgBlAHIAaQENAGsAaQAgAEcAcgBh +AHkAIABHAGEAbQBtAGEAIAAyAC4AMgAgAHAAcgBvAGYAaQBsAFUAbgBpAHcAZQByAHMAYQBsAG4AeQAg +AHAAcgBvAGYAaQBsACAAcwB6AGEAcgBvAVsAYwBpACAAZwBhAG0AbQBhACAAMgAsADIGOgYnBkUGJwAg +ADIALgAyACAGRAZIBkYAIAYxBkUGJwYvBkoAIAY5BicGRQQeBDEESQQwBE8AIARBBDUEQAQwBE8AIAQz +BDAEPAQ8BDAAIAAyACwAMgAtBD8EQAQ+BEQEOAQ7BEwARwBlAG4AZQByAGkAYwAgAEcAcgBhAHkAIABH +AGEAbQBtAGEAIAAyAC4AMgAgAFAAcgBvAGYAaQBsAGUAAHRleHQAAAAAQ29weXJpZ2h0IEFwcGxlIElu +Yy4sIDIwMTIAAFhZWiAAAAAAAADzUQABAAAAARbMY3VydgAAAAAAAAQAAAAABQAKAA8AFAAZAB4AIwAo +AC0AMgA3ADsAQABFAEoATwBUAFkAXgBjAGgAbQByAHcAfACBAIYAiwCQAJUAmgCfAKQAqQCuALIAtwC8 +AMEAxgDLANAA1QDbAOAA5QDrAPAA9gD7AQEBBwENARMBGQEfASUBKwEyATgBPgFFAUwBUgFZAWABZwFu +AXUBfAGDAYsBkgGaAaEBqQGxAbkBwQHJAdEB2QHhAekB8gH6AgMCDAIUAh0CJgIvAjgCQQJLAlQCXQJn +AnECegKEAo4CmAKiAqwCtgLBAssC1QLgAusC9QMAAwsDFgMhAy0DOANDA08DWgNmA3IDfgOKA5YDogOu +A7oDxwPTA+AD7AP5BAYEEwQgBC0EOwRIBFUEYwRxBH4EjASaBKgEtgTEBNME4QTwBP4FDQUcBSsFOgVJ +BVgFZwV3BYYFlgWmBbUFxQXVBeUF9gYGBhYGJwY3BkgGWQZqBnsGjAadBq8GwAbRBuMG9QcHBxkHKwc9 +B08HYQd0B4YHmQesB78H0gflB/gICwgfCDIIRghaCG4IggiWCKoIvgjSCOcI+wkQCSUJOglPCWQJeQmP +CaQJugnPCeUJ+woRCicKPQpUCmoKgQqYCq4KxQrcCvMLCwsiCzkLUQtpC4ALmAuwC8gL4Qv5DBIMKgxD +DFwMdQyODKcMwAzZDPMNDQ0mDUANWg10DY4NqQ3DDd4N+A4TDi4OSQ5kDn8Omw62DtIO7g8JDyUPQQ9e +D3oPlg+zD88P7BAJECYQQxBhEH4QmxC5ENcQ9RETETERTxFtEYwRqhHJEegSBxImEkUSZBKEEqMSwxLj +EwMTIxNDE2MTgxOkE8UT5RQGFCcUSRRqFIsUrRTOFPAVEhU0FVYVeBWbFb0V4BYDFiYWSRZsFo8WshbW +FvoXHRdBF2UXiReuF9IX9xgbGEAYZRiKGK8Y1Rj6GSAZRRlrGZEZtxndGgQaKhpRGncanhrFGuwbFBs7 +G2MbihuyG9ocAhwqHFIcexyjHMwc9R0eHUcdcB2ZHcMd7B4WHkAeah6UHr4e6R8THz4faR+UH78f6iAV +IEEgbCCYIMQg8CEcIUghdSGhIc4h+yInIlUigiKvIt0jCiM4I2YjlCPCI/AkHyRNJHwkqyTaJQklOCVo +JZclxyX3JicmVyaHJrcm6CcYJ0kneierJ9woDSg/KHEooijUKQYpOClrKZ0p0CoCKjUqaCqbKs8rAis2 +K2krnSvRLAUsOSxuLKIs1y0MLUEtdi2rLeEuFi5MLoIuty7uLyQvWi+RL8cv/jA1MGwwpDDbMRIxSjGC +Mbox8jIqMmMymzLUMw0zRjN/M7gz8TQrNGU0njTYNRM1TTWHNcI1/TY3NnI2rjbpNyQ3YDecN9c4FDhQ +OIw4yDkFOUI5fzm8Ofk6Njp0OrI67zstO2s7qjvoPCc8ZTykPOM9Ij1hPaE94D4gPmA+oD7gPyE/YT+i +P+JAI0BkQKZA50EpQWpBrEHuQjBCckK1QvdDOkN9Q8BEA0RHRIpEzkUSRVVFmkXeRiJGZ0arRvBHNUd7 +R8BIBUhLSJFI10kdSWNJqUnwSjdKfUrESwxLU0uaS+JMKkxyTLpNAk1KTZNN3E4lTm5Ot08AT0lPk0/d +UCdQcVC7UQZRUFGbUeZSMVJ8UsdTE1NfU6pT9lRCVI9U21UoVXVVwlYPVlxWqVb3V0RXklfgWC9YfVjL +WRpZaVm4WgdaVlqmWvVbRVuVW+VcNVyGXNZdJ114XcleGl5sXr1fD19hX7NgBWBXYKpg/GFPYaJh9WJJ +Ypxi8GNDY5dj62RAZJRk6WU9ZZJl52Y9ZpJm6Gc9Z5Nn6Wg/aJZo7GlDaZpp8WpIap9q92tPa6dr/2xX +bK9tCG1gbbluEm5rbsRvHm94b9FwK3CGcOBxOnGVcfByS3KmcwFzXXO4dBR0cHTMdSh1hXXhdj52m3b4 +d1Z3s3gReG54zHkqeYl553pGeqV7BHtje8J8IXyBfOF9QX2hfgF+Yn7CfyN/hH/lgEeAqIEKgWuBzYIw +gpKC9INXg7qEHYSAhOOFR4Wrhg6GcobXhzuHn4gEiGmIzokziZmJ/opkisqLMIuWi/yMY4zKjTGNmI3/ +jmaOzo82j56QBpBukNaRP5GokhGSepLjk02TtpQglIqU9JVflcmWNJaflwqXdZfgmEyYuJkkmZCZ/Jpo +mtWbQpuvnByciZz3nWSd0p5Anq6fHZ+Ln/qgaaDYoUehtqImopajBqN2o+akVqTHpTilqaYapoum/adu +p+CoUqjEqTepqaocqo+rAqt1q+msXKzQrUStuK4trqGvFq+LsACwdbDqsWCx1rJLssKzOLOutCW0nLUT +tYq2AbZ5tvC3aLfguFm40blKucK6O7q1uy67p7whvJu9Fb2Pvgq+hL7/v3q/9cBwwOzBZ8Hjwl/C28NY +w9TEUcTOxUvFyMZGxsPHQce/yD3IvMk6ybnKOMq3yzbLtsw1zLXNNc21zjbOts83z7jQOdC60TzRvtI/ +0sHTRNPG1EnUy9VO1dHWVdbY11zX4Nhk2OjZbNnx2nba+9uA3AXcit0Q3ZbeHN6i3ynfr+A24L3hROHM +4lPi2+Nj4+vkc+T85YTmDeaW5x/nqegy6LzpRunQ6lvq5etw6/vshu0R7ZzuKO6070DvzPBY8OXxcvH/ +8ozzGfOn9DT0wvVQ9d72bfb794r4Gfio+Tj5x/pX+uf7d/wH/Jj9Kf26/kv+3P9t///SLS4vMFokY2xh +c3NuYW1lWCRjbGFzc2VzXxAQTlNCaXRtYXBJbWFnZVJlcKMvMTJaTlNJbWFnZVJlcFhOU09iamVjdNIt +LjQ1V05TQXJyYXmiNDLSLS43OF5OU011dGFibGVBcnJheaM3NDLTOjsOPD0+V05TV2hpdGVcTlNDb2xv +clNwYWNlRDAgMAAQA4AM0i0uQEFXTlNDb2xvcqJAMtItLkNEV05TSW1hZ2WiQzIACAARABoAJAApADIA +NwBJAEwAUQBTAGIAaAB3AH4AhQCSAJkApQCtALwAvgDAAMUAxwDJAMsAzQDUANkA5ADmAOgA6gDvAPIA +9AD2APgA/wEWATIBNAE2E6YTqxO2E78T0hPWE+ET6hPvE/cT+hP/FA4UEhQZFCEULhQzFDUUNxQ8FEQU +RxRMFFQAAAAAAAACAQAAAAAAAABFAAAAAAAAAAAAAAAAAAAUVw diff --git a/i18n/src/main/resources/hr.lproj/Command.xib b/i18n/src/main/resources/hr.lproj/Command.xib index 526125e086..11ea806cb3 100644 --- a/i18n/src/main/resources/hr.lproj/Command.xib +++ b/i18n/src/main/resources/hr.lproj/Command.xib @@ -1,7 +1,7 @@ - + - + diff --git a/i18n/src/main/resources/hr.lproj/Connection.xib b/i18n/src/main/resources/hr.lproj/Connection.xib index 1f1fdc9a2d..c0ed24a7c1 100644 --- a/i18n/src/main/resources/hr.lproj/Connection.xib +++ b/i18n/src/main/resources/hr.lproj/Connection.xib @@ -1,7 +1,7 @@ - + - + @@ -76,7 +76,7 @@ - + diff --git a/i18n/src/main/resources/hr.lproj/Info.xib b/i18n/src/main/resources/hr.lproj/Info.xib index da82d09b4d..73d58c19bb 100644 --- a/i18n/src/main/resources/hr.lproj/Info.xib +++ b/i18n/src/main/resources/hr.lproj/Info.xib @@ -1,7 +1,7 @@ - + - + @@ -89,7 +89,7 @@ - + @@ -1017,21 +1017,21 @@ - + - + - + @@ -1121,21 +1121,21 @@ - + - + - + @@ -1250,27 +1250,27 @@ diff --git a/i18n/src/main/resources/hr.lproj/Login.strings b/i18n/src/main/resources/hr.lproj/Login.strings index 3c27fe8749..d6e7f01d76 100644 Binary files a/i18n/src/main/resources/hr.lproj/Login.strings and b/i18n/src/main/resources/hr.lproj/Login.strings differ diff --git a/i18n/src/main/resources/hr.lproj/Main.xib b/i18n/src/main/resources/hr.lproj/Main.xib index 520c1d3786..0d99c025a5 100644 --- a/i18n/src/main/resources/hr.lproj/Main.xib +++ b/i18n/src/main/resources/hr.lproj/Main.xib @@ -1,7 +1,7 @@ - + - + @@ -94,7 +94,7 @@ - + @@ -159,19 +159,19 @@ - + - + - + @@ -321,12 +321,12 @@ CA - + - + @@ -566,6 +566,10 @@ CA - + + + + + diff --git a/i18n/src/main/resources/hr.lproj/Preferences.xib b/i18n/src/main/resources/hr.lproj/Preferences.xib index b2ae93b3a0..cd23fd9182 100644 --- a/i18n/src/main/resources/hr.lproj/Preferences.xib +++ b/i18n/src/main/resources/hr.lproj/Preferences.xib @@ -1,7 +1,7 @@ - + - + @@ -266,7 +266,7 @@ - + @@ -307,7 +307,7 @@ - + @@ -538,7 +538,7 @@ - + @@ -578,7 +578,7 @@ - + @@ -613,7 +613,7 @@ - + @@ -642,7 +642,7 @@ - + @@ -1218,7 +1218,7 @@ - + @@ -1250,7 +1250,7 @@ - + @@ -1290,7 +1290,7 @@ - + @@ -1363,7 +1363,7 @@ - + @@ -1558,7 +1558,7 @@ - + @@ -1618,7 +1618,7 @@ - + @@ -1643,7 +1643,7 @@ - + @@ -1685,7 +1685,7 @@ - + @@ -1713,7 +1713,7 @@ - + @@ -1741,7 +1741,7 @@ - + @@ -1770,7 +1770,7 @@ - + @@ -1805,7 +1805,7 @@ - + @@ -1889,7 +1889,7 @@ - + @@ -1948,7 +1948,7 @@ - + @@ -1976,7 +1976,7 @@ - + @@ -2005,7 +2005,7 @@ - + diff --git a/i18n/src/main/resources/hr.lproj/Profiles.xib b/i18n/src/main/resources/hr.lproj/Profiles.xib index 162f9c1fcb..91928e7a3f 100644 --- a/i18n/src/main/resources/hr.lproj/Profiles.xib +++ b/i18n/src/main/resources/hr.lproj/Profiles.xib @@ -1,7 +1,7 @@ - + - + diff --git a/i18n/src/main/resources/hr.lproj/Prompt.xib b/i18n/src/main/resources/hr.lproj/Prompt.xib index 2272a57c43..3e03605e17 100644 --- a/i18n/src/main/resources/hr.lproj/Prompt.xib +++ b/i18n/src/main/resources/hr.lproj/Prompt.xib @@ -1,7 +1,7 @@ - + - + @@ -95,7 +95,7 @@ Gw - + diff --git a/i18n/src/main/resources/hr.lproj/S3.strings b/i18n/src/main/resources/hr.lproj/S3.strings index 7329455196..4f0aa06210 100644 Binary files a/i18n/src/main/resources/hr.lproj/S3.strings and b/i18n/src/main/resources/hr.lproj/S3.strings differ diff --git a/i18n/src/main/resources/hr.lproj/Updater.strings b/i18n/src/main/resources/hr.lproj/Updater.strings index ba6739f1df..54994a7186 100644 Binary files a/i18n/src/main/resources/hr.lproj/Updater.strings and b/i18n/src/main/resources/hr.lproj/Updater.strings differ diff --git a/i18n/src/main/resources/hu.lproj/Bookmark.xib b/i18n/src/main/resources/hu.lproj/Bookmark.xib index 46204460e3..2d6ad17c8e 100644 --- a/i18n/src/main/resources/hu.lproj/Bookmark.xib +++ b/i18n/src/main/resources/hu.lproj/Bookmark.xib @@ -1,7 +1,7 @@ - + - + @@ -92,7 +92,7 @@ - + @@ -489,95 +489,95 @@ YnBsaXN0MDDUAQIDBAUGBwpYJHZlcnNpb25ZJGFyY2hpdmVyVCR0b3BYJG9iamVjdHMSAAGGoF8QD05T -S2V5ZWRBcmNoaXZlctEICVRyb290gAGuCwwZGh8UJCkqMTQ3PUBVJG51bGzWDQ4PEBESExQVFhcYVk5T -U2l6ZV5OU1Jlc2l6aW5nTW9kZVYkY2xhc3NcTlNJbWFnZUZsYWdzVk5TUmVwc1dOU0NvbG9ygAIQAIAN -EiDDAACAA4ALVnsxLCAxfdIbDxweWk5TLm9iamVjdHOhHYAEgArSGw8gI6IhIoAFgAaACdMPJSYnKBRf -EBROU1RJRkZSZXByZXNlbnRhdGlvbl8QGU5TSW50ZXJuYWxMYXlvdXREaXJlY3Rpb26ACIAHTxESbE1N -ACoAAAAKAAAAEAEAAAMAAAABAAEAAAEBAAMAAAABAAEAAAECAAMAAAACAAgACAEDAAMAAAABAAEAAAEG -AAMAAAABAAEAAAEKAAMAAAABAAEAAAERAAQAAAABAAAACAESAAMAAAABAAEAAAEVAAMAAAABAAIAAAEW -AAMAAAABAAEAAAEXAAQAAAABAAAAAgEcAAMAAAABAAEAAAEoAAMAAAABAAIAAAFSAAMAAAABAAEAAAFT -AAMAAAACAAEAAYdzAAcAABGcAAAA0AAAAAAAABGcYXBwbAIAAABtbnRyR1JBWVhZWiAH3AAIABcADwAu -AA9hY3NwQVBQTAAAAABub25lAAAAAAAAAAAAAAAAAAAAAAAA9tYAAQAAAADTLWFwcGwAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAVkZXNjAAAAwAAAAHlkc2NtAAABPAAA -CBpjcHJ0AAAJWAAAACN3dHB0AAAJfAAAABRrVFJDAAAJkAAACAxkZXNjAAAAAAAAAB9HZW5lcmljIEdy -YXkgR2FtbWEgMi4yIFByb2ZpbGUAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAbWx1YwAAAAAAAAAfAAAADHNr -U0sAAAAuAAABhGRhREsAAAA6AAABsmNhRVMAAAA4AAAB7HZpVk4AAABAAAACJHB0QlIAAABKAAACZHVr -VUEAAAAsAAACrmZyRlUAAAA+AAAC2mh1SFUAAAA0AAADGHpoVFcAAAAaAAADTGtvS1IAAAAiAAADZm5i -Tk8AAAA6AAADiGNzQ1oAAAAoAAADwmhlSUwAAAAkAAAD6nJvUk8AAAAqAAAEDmRlREUAAABOAAAEOGl0 -SVQAAABOAAAEhnN2U0UAAAA4AAAE1HpoQ04AAAAaAAAFDGphSlAAAAAmAAAFJmVsR1IAAAAqAAAFTHB0 -UE8AAABSAAAFdm5sTkwAAABAAAAFyGVzRVMAAABMAAAGCHRoVEgAAAAyAAAGVHRyVFIAAAAkAAAGhmZp -RkkAAABGAAAGqmhySFIAAAA+AAAG8HBsUEwAAABKAAAHLmFyRUcAAAAsAAAHeHJ1UlUAAAA6AAAHpGVu -VVMAAAA8AAAH3gBWAWEAZQBvAGIAZQBjAG4A4QAgAHMAaQB2AOEAIABnAGEAbQBhACAAMgAsADIARwBl -AG4AZQByAGkAcwBrACAAZwByAOUAIAAyACwAMgAgAGcAYQBtAG0AYQAtAHAAcgBvAGYAaQBsAEcAYQBt -AG0AYQAgAGQAZQAgAGcAcgBpAHMAbwBzACAAZwBlAG4A6AByAGkAYwBhACAAMgAuADIAQx6lAHUAIABo -AOwAbgBoACAATQDgAHUAIAB4AOEAbQAgAEMAaAB1AG4AZwAgAEcAYQBtAG0AYQAgADIALgAyAFAAZQBy -AGYAaQBsACAARwBlAG4A6QByAGkAYwBvACAAZABhACAARwBhAG0AYQAgAGQAZQAgAEMAaQBuAHoAYQBz -ACAAMgAsADIEFwQwBDMEMAQ7BEwEPQQwACAARwByAGEAeQAtBDMEMAQ8BDAAIAAyAC4AMgBQAHIAbwBm -AGkAbAAgAGcA6QBuAOkAcgBpAHEAdQBlACAAZwByAGkAcwAgAGcAYQBtAG0AYQAgADIALAAyAMEAbAB0 -AGEAbADhAG4AbwBzACAAcwB6APwAcgBrAGUAIABnAGEAbQBtAGEAIAAyAC4AMpAadShwcJaOUUlepgAy -AC4AMoJyX2ljz4/wx3y8GAAg1ozAyQAgrBC5yAAgADIALgAyACDVBLhc0wzHfABHAGUAbgBlAHIAaQBz -AGsAIABnAHIA5QAgAGcAYQBtAG0AYQAgADIALAAyAC0AcAByAG8AZgBpAGwATwBiAGUAYwBuAOEAIAFh -AGUAZADhACAAZwBhAG0AYQAgADIALgAyBdIF0AXeBdQAIAXQBeQF1QXoACAF2wXcBdwF2QAgADIALgAy -AEcAYQBtAGEAIABnAHIAaQAgAGcAZQBuAGUAcgBpAGMBAwAgADIALAAyAEEAbABsAGcAZQBtAGUAaQBu -AGUAcwAgAEcAcgBhAHUAcwB0AHUAZgBlAG4ALQBQAHIAbwBmAGkAbAAgAEcAYQBtAG0AYQAgADIALAAy -AFAAcgBvAGYAaQBsAG8AIABnAHIAaQBnAGkAbwAgAGcAZQBuAGUAcgBpAGMAbwAgAGQAZQBsAGwAYQAg -AGcAYQBtAG0AYQAgADIALAAyAEcAZQBuAGUAcgBpAHMAawAgAGcAcgDlACAAMgAsADIAIABnAGEAbQBt -AGEAcAByAG8AZgBpAGxmbpAacHBepnz7ZXAAMgAuADJjz4/wZYdO9k4AgiwwsDDsMKQwrDDzMN4AIAAy -AC4AMgAgMNcw7TDVMKEwpDDrA5MDtQO9A7kDugPMACADkwO6A8EDuQAgA5MDrAO8A7wDsQAgADIALgAy -AFAAZQByAGYAaQBsACAAZwBlAG4A6QByAGkAYwBvACAAZABlACAAYwBpAG4AegBlAG4AdABvAHMAIABk -AGEAIABHAGEAbQBtAGEAIAAyACwAMgBBAGwAZwBlAG0AZQBlAG4AIABnAHIAaQBqAHMAIABnAGEAbQBt -AGEAIAAyACwAMgAtAHAAcgBvAGYAaQBlAGwAUABlAHIAZgBpAGwAIABnAGUAbgDpAHIAaQBjAG8AIABk -AGUAIABnAGEAbQBtAGEAIABkAGUAIABnAHIAaQBzAGUAcwAgADIALAAyDiMOMQ4HDioONQ5BDgEOIQ4h -DjIOQA4BDiMOIg5MDhcOMQ5IDicORA4bACAAMgAuADIARwBlAG4AZQBsACAARwByAGkAIABHAGEAbQBh -ACAAMgAsADIAWQBsAGUAaQBuAGUAbgAgAGgAYQByAG0AYQBhAG4AIABnAGEAbQBtAGEAIAAyACwAMgAg -AC0AcAByAG8AZgBpAGkAbABpAEcAZQBuAGUAcgBpAQ0AawBpACAARwByAGEAeQAgAEcAYQBtAG0AYQAg -ADIALgAyACAAcAByAG8AZgBpAGwAVQBuAGkAdwBlAHIAcwBhAGwAbgB5ACAAcAByAG8AZgBpAGwAIABz -AHoAYQByAG8BWwBjAGkAIABnAGEAbQBtAGEAIAAyACwAMgY6BicGRQYnACAAMgAuADIAIAZEBkgGRgAg -BjEGRQYnBi8GSgAgBjkGJwZFBB4EMQRJBDAETwAgBEEENQRABDAETwAgBDMEMAQ8BDwEMAAgADIALAAy -AC0EPwRABD4ERAQ4BDsETABHAGUAbgBlAHIAaQBjACAARwByAGEAeQAgAEcAYQBtAG0AYQAgADIALgAy -ACAAUAByAG8AZgBpAGwAZQAAdGV4dAAAAABDb3B5cmlnaHQgQXBwbGUgSW5jLiwgMjAxMgAAWFlaIAAA -AAAAAPNRAAEAAAABFsxjdXJ2AAAAAAAABAAAAAAFAAoADwAUABkAHgAjACgALQAyADcAOwBAAEUASgBP -AFQAWQBeAGMAaABtAHIAdwB8AIEAhgCLAJAAlQCaAJ8ApACpAK4AsgC3ALwAwQDGAMsA0ADVANsA4ADl -AOsA8AD2APsBAQEHAQ0BEwEZAR8BJQErATIBOAE+AUUBTAFSAVkBYAFnAW4BdQF8AYMBiwGSAZoBoQGp -AbEBuQHBAckB0QHZAeEB6QHyAfoCAwIMAhQCHQImAi8COAJBAksCVAJdAmcCcQJ6AoQCjgKYAqICrAK2 -AsECywLVAuAC6wL1AwADCwMWAyEDLQM4A0MDTwNaA2YDcgN+A4oDlgOiA64DugPHA9MD4APsA/kEBgQT -BCAELQQ7BEgEVQRjBHEEfgSMBJoEqAS2BMQE0wThBPAE/gUNBRwFKwU6BUkFWAVnBXcFhgWWBaYFtQXF -BdUF5QX2BgYGFgYnBjcGSAZZBmoGewaMBp0GrwbABtEG4wb1BwcHGQcrBz0HTwdhB3QHhgeZB6wHvwfS -B+UH+AgLCB8IMghGCFoIbgiCCJYIqgi+CNII5wj7CRAJJQk6CU8JZAl5CY8JpAm6Cc8J5Qn7ChEKJwo9 -ClQKagqBCpgKrgrFCtwK8wsLCyILOQtRC2kLgAuYC7ALyAvhC/kMEgwqDEMMXAx1DI4MpwzADNkM8w0N -DSYNQA1aDXQNjg2pDcMN3g34DhMOLg5JDmQOfw6bDrYO0g7uDwkPJQ9BD14Peg+WD7MPzw/sEAkQJhBD -EGEQfhCbELkQ1xD1ERMRMRFPEW0RjBGqEckR6BIHEiYSRRJkEoQSoxLDEuMTAxMjE0MTYxODE6QTxRPl -FAYUJxRJFGoUixStFM4U8BUSFTQVVhV4FZsVvRXgFgMWJhZJFmwWjxayFtYW+hcdF0EXZReJF64X0hf3 -GBsYQBhlGIoYrxjVGPoZIBlFGWsZkRm3Gd0aBBoqGlEadxqeGsUa7BsUGzsbYxuKG7Ib2hwCHCocUhx7 -HKMczBz1HR4dRx1wHZkdwx3sHhYeQB5qHpQevh7pHxMfPh9pH5Qfvx/qIBUgQSBsIJggxCDwIRwhSCF1 -IaEhziH7IiciVSKCIq8i3SMKIzgjZiOUI8Ij8CQfJE0kfCSrJNolCSU4JWgllyXHJfcmJyZXJocmtybo -JxgnSSd6J6sn3CgNKD8ocSiiKNQpBik4KWspnSnQKgIqNSpoKpsqzysCKzYraSudK9EsBSw5LG4soizX -LQwtQS12Last4S4WLkwugi63Lu4vJC9aL5Evxy/+MDUwbDCkMNsxEjFKMYIxujHyMioyYzKbMtQzDTNG -M38zuDPxNCs0ZTSeNNg1EzVNNYc1wjX9Njc2cjauNuk3JDdgN5w31zgUOFA4jDjIOQU5Qjl/Obw5+To2 -OnQ6sjrvOy07azuqO+g8JzxlPKQ84z0iPWE9oT3gPiA+YD6gPuA/IT9hP6I/4kAjQGRApkDnQSlBakGs -Qe5CMEJyQrVC90M6Q31DwEQDREdEikTORRJFVUWaRd5GIkZnRqtG8Ec1R3tHwEgFSEtIkUjXSR1JY0mp -SfBKN0p9SsRLDEtTS5pL4kwqTHJMuk0CTUpNk03cTiVObk63TwBPSU+TT91QJ1BxULtRBlFQUZtR5lIx -UnxSx1MTU19TqlP2VEJUj1TbVShVdVXCVg9WXFapVvdXRFeSV+BYL1h9WMtZGllpWbhaB1pWWqZa9VtF -W5Vb5Vw1XIZc1l0nXXhdyV4aXmxevV8PX2Ffs2AFYFdgqmD8YU9homH1YklinGLwY0Njl2PrZEBklGTp -ZT1lkmXnZj1mkmboZz1nk2fpaD9olmjsaUNpmmnxakhqn2r3a09rp2v/bFdsr20IbWBtuW4SbmtuxG8e -b3hv0XArcIZw4HE6cZVx8HJLcqZzAXNdc7h0FHRwdMx1KHWFdeF2Pnabdvh3VnezeBF4bnjMeSp5iXnn -ekZ6pXsEe2N7wnwhfIF84X1BfaF+AX5ifsJ/I3+Ef+WAR4CogQqBa4HNgjCCkoL0g1eDuoQdhICE44VH -hauGDoZyhteHO4efiASIaYjOiTOJmYn+imSKyoswi5aL/IxjjMqNMY2Yjf+OZo7OjzaPnpAGkG6Q1pE/ -kaiSEZJ6kuOTTZO2lCCUipT0lV+VyZY0lp+XCpd1l+CYTJi4mSSZkJn8mmia1ZtCm6+cHJyJnPedZJ3S -nkCerp8dn4uf+qBpoNihR6G2oiailqMGo3aj5qRWpMelOKWpphqmi6b9p26n4KhSqMSpN6mpqhyqj6sC -q3Wr6axcrNCtRK24ri2uoa8Wr4uwALB1sOqxYLHWskuywrM4s660JbSctRO1irYBtnm28Ldot+C4WbjR -uUq5wro7urW7LrunvCG8m70VvY++Cr6Evv+/er/1wHDA7MFnwePCX8Lbw1jD1MRRxM7FS8XIxkbGw8dB -x7/IPci8yTrJuco4yrfLNsu2zDXMtc01zbXONs62zzfPuNA50LrRPNG+0j/SwdNE08bUSdTL1U7V0dZV -1tjXXNfg2GTY6Nls2fHadtr724DcBdyK3RDdlt4c3qLfKd+v4DbgveFE4cziU+Lb42Pj6+Rz5PzlhOYN -5pbnH+ep6DLovOlG6dDqW+rl63Dr++yG7RHtnO4o7rTvQO/M8Fjw5fFy8f/yjPMZ86f0NPTC9VD13vZt -9vv3ivgZ+Kj5OPnH+lf65/t3/Af8mP0p/br+S/7c/23//9IrLC0uWiRjbGFzc25hbWVYJGNsYXNzZXNf -EBBOU0JpdG1hcEltYWdlUmVwoy0vMFpOU0ltYWdlUmVwWE5TT2JqZWN00issMjNXTlNBcnJheaIyMNIr -LDU2Xk5TTXV0YWJsZUFycmF5ozUyMNM4OQ86OzxXTlNXaGl0ZVxOU0NvbG9yU3BhY2VEMCAwABADgAzS -Kyw+P1dOU0NvbG9yoj4w0issQUJXTlNJbWFnZaJBMAAIABEAGgAkACkAMgA3AEkATABRAFMAYgBoAHUA -fACLAJIAnwCmAK4AsACyALQAuQC7AL0AxADJANQA1gDYANoA3wDiAOQA5gDoAO8BBgEiASQBJhOWE5sT -phOvE8ITxhPRE9oT3xPnE+oT7xP+FAIUCRQRFB4UIxQlFCcULBQ0FDcUPBREAAAAAAAAAgEAAAAAAAAA -QwAAAAAAAAAAAAAAAAAAFEc +S2V5ZWRBcmNoaXZlctEICVRyb290gAGuCwwbHCEaJissMzY5P0JVJG51bGzXDQ4PEBESExQVFhcYGRpW +TlNTaXplViRjbGFzc1xOU0ltYWdlRmxhZ3NWTlNSZXBzW05TVGludENvbG9yV05TQ29sb3JeTlNSZXNp +emluZ01vZGWAAoANEiDDAACAA4AAgAsQAFZ7MSwgMX3SHQ4eIFpOUy5vYmplY3RzoR+ABIAK0h0OIiWi +IySABYAGgAnTDicoKSoaXxAUTlNUSUZGUmVwcmVzZW50YXRpb25fEBlOU0ludGVybmFsTGF5b3V0RGly +ZWN0aW9ugAiAB08REmxNTQAqAAAACgAAABABAAADAAAAAQABAAABAQADAAAAAQABAAABAgADAAAAAgAI +AAgBAwADAAAAAQABAAABBgADAAAAAQABAAABCgADAAAAAQABAAABEQAEAAAAAQAAAAgBEgADAAAAAQAB +AAABFQADAAAAAQACAAABFgADAAAAAQABAAABFwAEAAAAAQAAAAIBHAADAAAAAQABAAABKAADAAAAAQAC +AAABUgADAAAAAQABAAABUwADAAAAAgABAAGHcwAHAAARnAAAANAAAAAAAAARnGFwcGwCAAAAbW50ckdS +QVlYWVogB9wACAAXAA8ALgAPYWNzcEFQUEwAAAAAbm9uZQAAAAAAAAAAAAAAAAAAAAAAAPbWAAEAAAAA +0y1hcHBsAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFZGVzYwAA +AMAAAAB5ZHNjbQAAATwAAAgaY3BydAAACVgAAAAjd3RwdAAACXwAAAAUa1RSQwAACZAAAAgMZGVzYwAA +AAAAAAAfR2VuZXJpYyBHcmF5IEdhbW1hIDIuMiBQcm9maWxlAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAG1s +dWMAAAAAAAAAHwAAAAxza1NLAAAALgAAAYRkYURLAAAAOgAAAbJjYUVTAAAAOAAAAex2aVZOAAAAQAAA +AiRwdEJSAAAASgAAAmR1a1VBAAAALAAAAq5mckZVAAAAPgAAAtpodUhVAAAANAAAAxh6aFRXAAAAGgAA +A0xrb0tSAAAAIgAAA2ZuYk5PAAAAOgAAA4hjc0NaAAAAKAAAA8JoZUlMAAAAJAAAA+pyb1JPAAAAKgAA +BA5kZURFAAAATgAABDhpdElUAAAATgAABIZzdlNFAAAAOAAABNR6aENOAAAAGgAABQxqYUpQAAAAJgAA +BSZlbEdSAAAAKgAABUxwdFBPAAAAUgAABXZubE5MAAAAQAAABchlc0VTAAAATAAABgh0aFRIAAAAMgAA +BlR0clRSAAAAJAAABoZmaUZJAAAARgAABqpockhSAAAAPgAABvBwbFBMAAAASgAABy5hckVHAAAALAAA +B3hydVJVAAAAOgAAB6RlblVTAAAAPAAAB94AVgFhAGUAbwBiAGUAYwBuAOEAIABzAGkAdgDhACAAZwBh +AG0AYQAgADIALAAyAEcAZQBuAGUAcgBpAHMAawAgAGcAcgDlACAAMgAsADIAIABnAGEAbQBtAGEALQBw +AHIAbwBmAGkAbABHAGEAbQBtAGEAIABkAGUAIABnAHIAaQBzAG8AcwAgAGcAZQBuAOgAcgBpAGMAYQAg +ADIALgAyAEMepQB1ACAAaADsAG4AaAAgAE0A4AB1ACAAeADhAG0AIABDAGgAdQBuAGcAIABHAGEAbQBt +AGEAIAAyAC4AMgBQAGUAcgBmAGkAbAAgAEcAZQBuAOkAcgBpAGMAbwAgAGQAYQAgAEcAYQBtAGEAIABk +AGUAIABDAGkAbgB6AGEAcwAgADIALAAyBBcEMAQzBDAEOwRMBD0EMAAgAEcAcgBhAHkALQQzBDAEPAQw +ACAAMgAuADIAUAByAG8AZgBpAGwAIABnAOkAbgDpAHIAaQBxAHUAZQAgAGcAcgBpAHMAIABnAGEAbQBt +AGEAIAAyACwAMgDBAGwAdABhAGwA4QBuAG8AcwAgAHMAegD8AHIAawBlACAAZwBhAG0AbQBhACAAMgAu +ADKQGnUocHCWjlFJXqYAMgAuADKCcl9pY8+P8Md8vBgAINaMwMkAIKwQucgAIAAyAC4AMgAg1QS4XNMM +x3wARwBlAG4AZQByAGkAcwBrACAAZwByAOUAIABnAGEAbQBtAGEAIAAyACwAMgAtAHAAcgBvAGYAaQBs +AE8AYgBlAGMAbgDhACABYQBlAGQA4QAgAGcAYQBtAGEAIAAyAC4AMgXSBdAF3gXUACAF0AXkBdUF6AAg +BdsF3AXcBdkAIAAyAC4AMgBHAGEAbQBhACAAZwByAGkAIABnAGUAbgBlAHIAaQBjAQMAIAAyACwAMgBB +AGwAbABnAGUAbQBlAGkAbgBlAHMAIABHAHIAYQB1AHMAdAB1AGYAZQBuAC0AUAByAG8AZgBpAGwAIABH +AGEAbQBtAGEAIAAyACwAMgBQAHIAbwBmAGkAbABvACAAZwByAGkAZwBpAG8AIABnAGUAbgBlAHIAaQBj +AG8AIABkAGUAbABsAGEAIABnAGEAbQBtAGEAIAAyACwAMgBHAGUAbgBlAHIAaQBzAGsAIABnAHIA5QAg +ADIALAAyACAAZwBhAG0AbQBhAHAAcgBvAGYAaQBsZm6QGnBwXqZ8+2VwADIALgAyY8+P8GWHTvZOAIIs +MLAw7DCkMKww8zDeACAAMgAuADIAIDDXMO0w1TChMKQw6wOTA7UDvQO5A7oDzAAgA5MDugPBA7kAIAOT +A6wDvAO8A7EAIAAyAC4AMgBQAGUAcgBmAGkAbAAgAGcAZQBuAOkAcgBpAGMAbwAgAGQAZQAgAGMAaQBu +AHoAZQBuAHQAbwBzACAAZABhACAARwBhAG0AbQBhACAAMgAsADIAQQBsAGcAZQBtAGUAZQBuACAAZwBy +AGkAagBzACAAZwBhAG0AbQBhACAAMgAsADIALQBwAHIAbwBmAGkAZQBsAFAAZQByAGYAaQBsACAAZwBl +AG4A6QByAGkAYwBvACAAZABlACAAZwBhAG0AbQBhACAAZABlACAAZwByAGkAcwBlAHMAIAAyACwAMg4j +DjEOBw4qDjUOQQ4BDiEOIQ4yDkAOAQ4jDiIOTA4XDjEOSA4nDkQOGwAgADIALgAyAEcAZQBuAGUAbAAg +AEcAcgBpACAARwBhAG0AYQAgADIALAAyAFkAbABlAGkAbgBlAG4AIABoAGEAcgBtAGEAYQBuACAAZwBh +AG0AbQBhACAAMgAsADIAIAAtAHAAcgBvAGYAaQBpAGwAaQBHAGUAbgBlAHIAaQENAGsAaQAgAEcAcgBh +AHkAIABHAGEAbQBtAGEAIAAyAC4AMgAgAHAAcgBvAGYAaQBsAFUAbgBpAHcAZQByAHMAYQBsAG4AeQAg +AHAAcgBvAGYAaQBsACAAcwB6AGEAcgBvAVsAYwBpACAAZwBhAG0AbQBhACAAMgAsADIGOgYnBkUGJwAg +ADIALgAyACAGRAZIBkYAIAYxBkUGJwYvBkoAIAY5BicGRQQeBDEESQQwBE8AIARBBDUEQAQwBE8AIAQz +BDAEPAQ8BDAAIAAyACwAMgAtBD8EQAQ+BEQEOAQ7BEwARwBlAG4AZQByAGkAYwAgAEcAcgBhAHkAIABH +AGEAbQBtAGEAIAAyAC4AMgAgAFAAcgBvAGYAaQBsAGUAAHRleHQAAAAAQ29weXJpZ2h0IEFwcGxlIElu +Yy4sIDIwMTIAAFhZWiAAAAAAAADzUQABAAAAARbMY3VydgAAAAAAAAQAAAAABQAKAA8AFAAZAB4AIwAo +AC0AMgA3ADsAQABFAEoATwBUAFkAXgBjAGgAbQByAHcAfACBAIYAiwCQAJUAmgCfAKQAqQCuALIAtwC8 +AMEAxgDLANAA1QDbAOAA5QDrAPAA9gD7AQEBBwENARMBGQEfASUBKwEyATgBPgFFAUwBUgFZAWABZwFu +AXUBfAGDAYsBkgGaAaEBqQGxAbkBwQHJAdEB2QHhAekB8gH6AgMCDAIUAh0CJgIvAjgCQQJLAlQCXQJn +AnECegKEAo4CmAKiAqwCtgLBAssC1QLgAusC9QMAAwsDFgMhAy0DOANDA08DWgNmA3IDfgOKA5YDogOu +A7oDxwPTA+AD7AP5BAYEEwQgBC0EOwRIBFUEYwRxBH4EjASaBKgEtgTEBNME4QTwBP4FDQUcBSsFOgVJ +BVgFZwV3BYYFlgWmBbUFxQXVBeUF9gYGBhYGJwY3BkgGWQZqBnsGjAadBq8GwAbRBuMG9QcHBxkHKwc9 +B08HYQd0B4YHmQesB78H0gflB/gICwgfCDIIRghaCG4IggiWCKoIvgjSCOcI+wkQCSUJOglPCWQJeQmP +CaQJugnPCeUJ+woRCicKPQpUCmoKgQqYCq4KxQrcCvMLCwsiCzkLUQtpC4ALmAuwC8gL4Qv5DBIMKgxD +DFwMdQyODKcMwAzZDPMNDQ0mDUANWg10DY4NqQ3DDd4N+A4TDi4OSQ5kDn8Omw62DtIO7g8JDyUPQQ9e +D3oPlg+zD88P7BAJECYQQxBhEH4QmxC5ENcQ9RETETERTxFtEYwRqhHJEegSBxImEkUSZBKEEqMSwxLj +EwMTIxNDE2MTgxOkE8UT5RQGFCcUSRRqFIsUrRTOFPAVEhU0FVYVeBWbFb0V4BYDFiYWSRZsFo8WshbW +FvoXHRdBF2UXiReuF9IX9xgbGEAYZRiKGK8Y1Rj6GSAZRRlrGZEZtxndGgQaKhpRGncanhrFGuwbFBs7 +G2MbihuyG9ocAhwqHFIcexyjHMwc9R0eHUcdcB2ZHcMd7B4WHkAeah6UHr4e6R8THz4faR+UH78f6iAV +IEEgbCCYIMQg8CEcIUghdSGhIc4h+yInIlUigiKvIt0jCiM4I2YjlCPCI/AkHyRNJHwkqyTaJQklOCVo +JZclxyX3JicmVyaHJrcm6CcYJ0kneierJ9woDSg/KHEooijUKQYpOClrKZ0p0CoCKjUqaCqbKs8rAis2 +K2krnSvRLAUsOSxuLKIs1y0MLUEtdi2rLeEuFi5MLoIuty7uLyQvWi+RL8cv/jA1MGwwpDDbMRIxSjGC +Mbox8jIqMmMymzLUMw0zRjN/M7gz8TQrNGU0njTYNRM1TTWHNcI1/TY3NnI2rjbpNyQ3YDecN9c4FDhQ +OIw4yDkFOUI5fzm8Ofk6Njp0OrI67zstO2s7qjvoPCc8ZTykPOM9Ij1hPaE94D4gPmA+oD7gPyE/YT+i +P+JAI0BkQKZA50EpQWpBrEHuQjBCckK1QvdDOkN9Q8BEA0RHRIpEzkUSRVVFmkXeRiJGZ0arRvBHNUd7 +R8BIBUhLSJFI10kdSWNJqUnwSjdKfUrESwxLU0uaS+JMKkxyTLpNAk1KTZNN3E4lTm5Ot08AT0lPk0/d +UCdQcVC7UQZRUFGbUeZSMVJ8UsdTE1NfU6pT9lRCVI9U21UoVXVVwlYPVlxWqVb3V0RXklfgWC9YfVjL +WRpZaVm4WgdaVlqmWvVbRVuVW+VcNVyGXNZdJ114XcleGl5sXr1fD19hX7NgBWBXYKpg/GFPYaJh9WJJ +Ypxi8GNDY5dj62RAZJRk6WU9ZZJl52Y9ZpJm6Gc9Z5Nn6Wg/aJZo7GlDaZpp8WpIap9q92tPa6dr/2xX +bK9tCG1gbbluEm5rbsRvHm94b9FwK3CGcOBxOnGVcfByS3KmcwFzXXO4dBR0cHTMdSh1hXXhdj52m3b4 +d1Z3s3gReG54zHkqeYl553pGeqV7BHtje8J8IXyBfOF9QX2hfgF+Yn7CfyN/hH/lgEeAqIEKgWuBzYIw +gpKC9INXg7qEHYSAhOOFR4Wrhg6GcobXhzuHn4gEiGmIzokziZmJ/opkisqLMIuWi/yMY4zKjTGNmI3/ +jmaOzo82j56QBpBukNaRP5GokhGSepLjk02TtpQglIqU9JVflcmWNJaflwqXdZfgmEyYuJkkmZCZ/Jpo +mtWbQpuvnByciZz3nWSd0p5Anq6fHZ+Ln/qgaaDYoUehtqImopajBqN2o+akVqTHpTilqaYapoum/adu +p+CoUqjEqTepqaocqo+rAqt1q+msXKzQrUStuK4trqGvFq+LsACwdbDqsWCx1rJLssKzOLOutCW0nLUT +tYq2AbZ5tvC3aLfguFm40blKucK6O7q1uy67p7whvJu9Fb2Pvgq+hL7/v3q/9cBwwOzBZ8Hjwl/C28NY +w9TEUcTOxUvFyMZGxsPHQce/yD3IvMk6ybnKOMq3yzbLtsw1zLXNNc21zjbOts83z7jQOdC60TzRvtI/ +0sHTRNPG1EnUy9VO1dHWVdbY11zX4Nhk2OjZbNnx2nba+9uA3AXcit0Q3ZbeHN6i3ynfr+A24L3hROHM +4lPi2+Nj4+vkc+T85YTmDeaW5x/nqegy6LzpRunQ6lvq5etw6/vshu0R7ZzuKO6070DvzPBY8OXxcvH/ +8ozzGfOn9DT0wvVQ9d72bfb794r4Gfio+Tj5x/pX+uf7d/wH/Jj9Kf26/kv+3P9t///SLS4vMFokY2xh +c3NuYW1lWCRjbGFzc2VzXxAQTlNCaXRtYXBJbWFnZVJlcKMvMTJaTlNJbWFnZVJlcFhOU09iamVjdNIt +LjQ1V05TQXJyYXmiNDLSLS43OF5OU011dGFibGVBcnJheaM3NDLTOjsOPD0+V05TV2hpdGVcTlNDb2xv +clNwYWNlRDAgMAAQA4AM0i0uQEFXTlNDb2xvcqJAMtItLkNEV05TSW1hZ2WiQzIACAARABoAJAApADIA +NwBJAEwAUQBTAGIAaAB3AH4AhQCSAJkApQCtALwAvgDAAMUAxwDJAMsAzQDUANkA5ADmAOgA6gDvAPIA +9AD2APgA/wEWATIBNAE2E6YTqxO2E78T0hPWE+ET6hPvE/cT+hP/FA4UEhQZFCEULhQzFDUUNxQ8FEQU +RxRMFFQAAAAAAAACAQAAAAAAAABFAAAAAAAAAAAAAAAAAAAUVw diff --git a/i18n/src/main/resources/hu.lproj/Browser.xib b/i18n/src/main/resources/hu.lproj/Browser.xib index 33ee5822c1..14a66b71cf 100644 --- a/i18n/src/main/resources/hu.lproj/Browser.xib +++ b/i18n/src/main/resources/hu.lproj/Browser.xib @@ -1,7 +1,7 @@ - + - + @@ -38,7 +38,7 @@ - + @@ -62,27 +62,27 @@ @@ -256,7 +256,7 @@ - + @@ -553,95 +553,95 @@ DQ YnBsaXN0MDDUAQIDBAUGBwpYJHZlcnNpb25ZJGFyY2hpdmVyVCR0b3BYJG9iamVjdHMSAAGGoF8QD05T -S2V5ZWRBcmNoaXZlctEICVRyb290gAGuCwwZGh8UJCkqMTQ3PUBVJG51bGzWDQ4PEBESExQVFhcYVk5T -U2l6ZV5OU1Jlc2l6aW5nTW9kZVYkY2xhc3NcTlNJbWFnZUZsYWdzVk5TUmVwc1dOU0NvbG9ygAIQAIAN -EiDDAACAA4ALVnsxLCAxfdIbDxweWk5TLm9iamVjdHOhHYAEgArSGw8gI6IhIoAFgAaACdMPJSYnKBRf -EBROU1RJRkZSZXByZXNlbnRhdGlvbl8QGU5TSW50ZXJuYWxMYXlvdXREaXJlY3Rpb26ACIAHTxESbE1N -ACoAAAAKAAAAEAEAAAMAAAABAAEAAAEBAAMAAAABAAEAAAECAAMAAAACAAgACAEDAAMAAAABAAEAAAEG -AAMAAAABAAEAAAEKAAMAAAABAAEAAAERAAQAAAABAAAACAESAAMAAAABAAEAAAEVAAMAAAABAAIAAAEW -AAMAAAABAAEAAAEXAAQAAAABAAAAAgEcAAMAAAABAAEAAAEoAAMAAAABAAIAAAFSAAMAAAABAAEAAAFT -AAMAAAACAAEAAYdzAAcAABGcAAAA0AAAAAAAABGcYXBwbAIAAABtbnRyR1JBWVhZWiAH3AAIABcADwAu -AA9hY3NwQVBQTAAAAABub25lAAAAAAAAAAAAAAAAAAAAAAAA9tYAAQAAAADTLWFwcGwAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAVkZXNjAAAAwAAAAHlkc2NtAAABPAAA -CBpjcHJ0AAAJWAAAACN3dHB0AAAJfAAAABRrVFJDAAAJkAAACAxkZXNjAAAAAAAAAB9HZW5lcmljIEdy -YXkgR2FtbWEgMi4yIFByb2ZpbGUAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAbWx1YwAAAAAAAAAfAAAADHNr -U0sAAAAuAAABhGRhREsAAAA6AAABsmNhRVMAAAA4AAAB7HZpVk4AAABAAAACJHB0QlIAAABKAAACZHVr -VUEAAAAsAAACrmZyRlUAAAA+AAAC2mh1SFUAAAA0AAADGHpoVFcAAAAaAAADTGtvS1IAAAAiAAADZm5i -Tk8AAAA6AAADiGNzQ1oAAAAoAAADwmhlSUwAAAAkAAAD6nJvUk8AAAAqAAAEDmRlREUAAABOAAAEOGl0 -SVQAAABOAAAEhnN2U0UAAAA4AAAE1HpoQ04AAAAaAAAFDGphSlAAAAAmAAAFJmVsR1IAAAAqAAAFTHB0 -UE8AAABSAAAFdm5sTkwAAABAAAAFyGVzRVMAAABMAAAGCHRoVEgAAAAyAAAGVHRyVFIAAAAkAAAGhmZp -RkkAAABGAAAGqmhySFIAAAA+AAAG8HBsUEwAAABKAAAHLmFyRUcAAAAsAAAHeHJ1UlUAAAA6AAAHpGVu -VVMAAAA8AAAH3gBWAWEAZQBvAGIAZQBjAG4A4QAgAHMAaQB2AOEAIABnAGEAbQBhACAAMgAsADIARwBl -AG4AZQByAGkAcwBrACAAZwByAOUAIAAyACwAMgAgAGcAYQBtAG0AYQAtAHAAcgBvAGYAaQBsAEcAYQBt -AG0AYQAgAGQAZQAgAGcAcgBpAHMAbwBzACAAZwBlAG4A6AByAGkAYwBhACAAMgAuADIAQx6lAHUAIABo -AOwAbgBoACAATQDgAHUAIAB4AOEAbQAgAEMAaAB1AG4AZwAgAEcAYQBtAG0AYQAgADIALgAyAFAAZQBy -AGYAaQBsACAARwBlAG4A6QByAGkAYwBvACAAZABhACAARwBhAG0AYQAgAGQAZQAgAEMAaQBuAHoAYQBz -ACAAMgAsADIEFwQwBDMEMAQ7BEwEPQQwACAARwByAGEAeQAtBDMEMAQ8BDAAIAAyAC4AMgBQAHIAbwBm -AGkAbAAgAGcA6QBuAOkAcgBpAHEAdQBlACAAZwByAGkAcwAgAGcAYQBtAG0AYQAgADIALAAyAMEAbAB0 -AGEAbADhAG4AbwBzACAAcwB6APwAcgBrAGUAIABnAGEAbQBtAGEAIAAyAC4AMpAadShwcJaOUUlepgAy -AC4AMoJyX2ljz4/wx3y8GAAg1ozAyQAgrBC5yAAgADIALgAyACDVBLhc0wzHfABHAGUAbgBlAHIAaQBz -AGsAIABnAHIA5QAgAGcAYQBtAG0AYQAgADIALAAyAC0AcAByAG8AZgBpAGwATwBiAGUAYwBuAOEAIAFh -AGUAZADhACAAZwBhAG0AYQAgADIALgAyBdIF0AXeBdQAIAXQBeQF1QXoACAF2wXcBdwF2QAgADIALgAy -AEcAYQBtAGEAIABnAHIAaQAgAGcAZQBuAGUAcgBpAGMBAwAgADIALAAyAEEAbABsAGcAZQBtAGUAaQBu -AGUAcwAgAEcAcgBhAHUAcwB0AHUAZgBlAG4ALQBQAHIAbwBmAGkAbAAgAEcAYQBtAG0AYQAgADIALAAy -AFAAcgBvAGYAaQBsAG8AIABnAHIAaQBnAGkAbwAgAGcAZQBuAGUAcgBpAGMAbwAgAGQAZQBsAGwAYQAg -AGcAYQBtAG0AYQAgADIALAAyAEcAZQBuAGUAcgBpAHMAawAgAGcAcgDlACAAMgAsADIAIABnAGEAbQBt -AGEAcAByAG8AZgBpAGxmbpAacHBepnz7ZXAAMgAuADJjz4/wZYdO9k4AgiwwsDDsMKQwrDDzMN4AIAAy -AC4AMgAgMNcw7TDVMKEwpDDrA5MDtQO9A7kDugPMACADkwO6A8EDuQAgA5MDrAO8A7wDsQAgADIALgAy -AFAAZQByAGYAaQBsACAAZwBlAG4A6QByAGkAYwBvACAAZABlACAAYwBpAG4AegBlAG4AdABvAHMAIABk -AGEAIABHAGEAbQBtAGEAIAAyACwAMgBBAGwAZwBlAG0AZQBlAG4AIABnAHIAaQBqAHMAIABnAGEAbQBt -AGEAIAAyACwAMgAtAHAAcgBvAGYAaQBlAGwAUABlAHIAZgBpAGwAIABnAGUAbgDpAHIAaQBjAG8AIABk -AGUAIABnAGEAbQBtAGEAIABkAGUAIABnAHIAaQBzAGUAcwAgADIALAAyDiMOMQ4HDioONQ5BDgEOIQ4h -DjIOQA4BDiMOIg5MDhcOMQ5IDicORA4bACAAMgAuADIARwBlAG4AZQBsACAARwByAGkAIABHAGEAbQBh -ACAAMgAsADIAWQBsAGUAaQBuAGUAbgAgAGgAYQByAG0AYQBhAG4AIABnAGEAbQBtAGEAIAAyACwAMgAg -AC0AcAByAG8AZgBpAGkAbABpAEcAZQBuAGUAcgBpAQ0AawBpACAARwByAGEAeQAgAEcAYQBtAG0AYQAg -ADIALgAyACAAcAByAG8AZgBpAGwAVQBuAGkAdwBlAHIAcwBhAGwAbgB5ACAAcAByAG8AZgBpAGwAIABz -AHoAYQByAG8BWwBjAGkAIABnAGEAbQBtAGEAIAAyACwAMgY6BicGRQYnACAAMgAuADIAIAZEBkgGRgAg -BjEGRQYnBi8GSgAgBjkGJwZFBB4EMQRJBDAETwAgBEEENQRABDAETwAgBDMEMAQ8BDwEMAAgADIALAAy -AC0EPwRABD4ERAQ4BDsETABHAGUAbgBlAHIAaQBjACAARwByAGEAeQAgAEcAYQBtAG0AYQAgADIALgAy -ACAAUAByAG8AZgBpAGwAZQAAdGV4dAAAAABDb3B5cmlnaHQgQXBwbGUgSW5jLiwgMjAxMgAAWFlaIAAA -AAAAAPNRAAEAAAABFsxjdXJ2AAAAAAAABAAAAAAFAAoADwAUABkAHgAjACgALQAyADcAOwBAAEUASgBP -AFQAWQBeAGMAaABtAHIAdwB8AIEAhgCLAJAAlQCaAJ8ApACpAK4AsgC3ALwAwQDGAMsA0ADVANsA4ADl -AOsA8AD2APsBAQEHAQ0BEwEZAR8BJQErATIBOAE+AUUBTAFSAVkBYAFnAW4BdQF8AYMBiwGSAZoBoQGp -AbEBuQHBAckB0QHZAeEB6QHyAfoCAwIMAhQCHQImAi8COAJBAksCVAJdAmcCcQJ6AoQCjgKYAqICrAK2 -AsECywLVAuAC6wL1AwADCwMWAyEDLQM4A0MDTwNaA2YDcgN+A4oDlgOiA64DugPHA9MD4APsA/kEBgQT -BCAELQQ7BEgEVQRjBHEEfgSMBJoEqAS2BMQE0wThBPAE/gUNBRwFKwU6BUkFWAVnBXcFhgWWBaYFtQXF -BdUF5QX2BgYGFgYnBjcGSAZZBmoGewaMBp0GrwbABtEG4wb1BwcHGQcrBz0HTwdhB3QHhgeZB6wHvwfS -B+UH+AgLCB8IMghGCFoIbgiCCJYIqgi+CNII5wj7CRAJJQk6CU8JZAl5CY8JpAm6Cc8J5Qn7ChEKJwo9 -ClQKagqBCpgKrgrFCtwK8wsLCyILOQtRC2kLgAuYC7ALyAvhC/kMEgwqDEMMXAx1DI4MpwzADNkM8w0N -DSYNQA1aDXQNjg2pDcMN3g34DhMOLg5JDmQOfw6bDrYO0g7uDwkPJQ9BD14Peg+WD7MPzw/sEAkQJhBD -EGEQfhCbELkQ1xD1ERMRMRFPEW0RjBGqEckR6BIHEiYSRRJkEoQSoxLDEuMTAxMjE0MTYxODE6QTxRPl -FAYUJxRJFGoUixStFM4U8BUSFTQVVhV4FZsVvRXgFgMWJhZJFmwWjxayFtYW+hcdF0EXZReJF64X0hf3 -GBsYQBhlGIoYrxjVGPoZIBlFGWsZkRm3Gd0aBBoqGlEadxqeGsUa7BsUGzsbYxuKG7Ib2hwCHCocUhx7 -HKMczBz1HR4dRx1wHZkdwx3sHhYeQB5qHpQevh7pHxMfPh9pH5Qfvx/qIBUgQSBsIJggxCDwIRwhSCF1 -IaEhziH7IiciVSKCIq8i3SMKIzgjZiOUI8Ij8CQfJE0kfCSrJNolCSU4JWgllyXHJfcmJyZXJocmtybo -JxgnSSd6J6sn3CgNKD8ocSiiKNQpBik4KWspnSnQKgIqNSpoKpsqzysCKzYraSudK9EsBSw5LG4soizX -LQwtQS12Last4S4WLkwugi63Lu4vJC9aL5Evxy/+MDUwbDCkMNsxEjFKMYIxujHyMioyYzKbMtQzDTNG -M38zuDPxNCs0ZTSeNNg1EzVNNYc1wjX9Njc2cjauNuk3JDdgN5w31zgUOFA4jDjIOQU5Qjl/Obw5+To2 -OnQ6sjrvOy07azuqO+g8JzxlPKQ84z0iPWE9oT3gPiA+YD6gPuA/IT9hP6I/4kAjQGRApkDnQSlBakGs -Qe5CMEJyQrVC90M6Q31DwEQDREdEikTORRJFVUWaRd5GIkZnRqtG8Ec1R3tHwEgFSEtIkUjXSR1JY0mp -SfBKN0p9SsRLDEtTS5pL4kwqTHJMuk0CTUpNk03cTiVObk63TwBPSU+TT91QJ1BxULtRBlFQUZtR5lIx -UnxSx1MTU19TqlP2VEJUj1TbVShVdVXCVg9WXFapVvdXRFeSV+BYL1h9WMtZGllpWbhaB1pWWqZa9VtF -W5Vb5Vw1XIZc1l0nXXhdyV4aXmxevV8PX2Ffs2AFYFdgqmD8YU9homH1YklinGLwY0Njl2PrZEBklGTp -ZT1lkmXnZj1mkmboZz1nk2fpaD9olmjsaUNpmmnxakhqn2r3a09rp2v/bFdsr20IbWBtuW4SbmtuxG8e -b3hv0XArcIZw4HE6cZVx8HJLcqZzAXNdc7h0FHRwdMx1KHWFdeF2Pnabdvh3VnezeBF4bnjMeSp5iXnn -ekZ6pXsEe2N7wnwhfIF84X1BfaF+AX5ifsJ/I3+Ef+WAR4CogQqBa4HNgjCCkoL0g1eDuoQdhICE44VH -hauGDoZyhteHO4efiASIaYjOiTOJmYn+imSKyoswi5aL/IxjjMqNMY2Yjf+OZo7OjzaPnpAGkG6Q1pE/ -kaiSEZJ6kuOTTZO2lCCUipT0lV+VyZY0lp+XCpd1l+CYTJi4mSSZkJn8mmia1ZtCm6+cHJyJnPedZJ3S -nkCerp8dn4uf+qBpoNihR6G2oiailqMGo3aj5qRWpMelOKWpphqmi6b9p26n4KhSqMSpN6mpqhyqj6sC -q3Wr6axcrNCtRK24ri2uoa8Wr4uwALB1sOqxYLHWskuywrM4s660JbSctRO1irYBtnm28Ldot+C4WbjR -uUq5wro7urW7LrunvCG8m70VvY++Cr6Evv+/er/1wHDA7MFnwePCX8Lbw1jD1MRRxM7FS8XIxkbGw8dB -x7/IPci8yTrJuco4yrfLNsu2zDXMtc01zbXONs62zzfPuNA50LrRPNG+0j/SwdNE08bUSdTL1U7V0dZV -1tjXXNfg2GTY6Nls2fHadtr724DcBdyK3RDdlt4c3qLfKd+v4DbgveFE4cziU+Lb42Pj6+Rz5PzlhOYN -5pbnH+ep6DLovOlG6dDqW+rl63Dr++yG7RHtnO4o7rTvQO/M8Fjw5fFy8f/yjPMZ86f0NPTC9VD13vZt -9vv3ivgZ+Kj5OPnH+lf65/t3/Af8mP0p/br+S/7c/23//9IrLC0uWiRjbGFzc25hbWVYJGNsYXNzZXNf -EBBOU0JpdG1hcEltYWdlUmVwoy0vMFpOU0ltYWdlUmVwWE5TT2JqZWN00issMjNXTlNBcnJheaIyMNIr -LDU2Xk5TTXV0YWJsZUFycmF5ozUyMNM4OQ86OzxXTlNXaGl0ZVxOU0NvbG9yU3BhY2VEMCAwABADgAzS -Kyw+P1dOU0NvbG9yoj4w0issQUJXTlNJbWFnZaJBMAAIABEAGgAkACkAMgA3AEkATABRAFMAYgBoAHUA -fACLAJIAnwCmAK4AsACyALQAuQC7AL0AxADJANQA1gDYANoA3wDiAOQA5gDoAO8BBgEiASQBJhOWE5sT -phOvE8ITxhPRE9oT3xPnE+oT7xP+FAIUCRQRFB4UIxQlFCcULBQ0FDcUPBREAAAAAAAAAgEAAAAAAAAA -QwAAAAAAAAAAAAAAAAAAFEc +S2V5ZWRBcmNoaXZlctEICVRyb290gAGuCwwbHCEaJissMzY5P0JVJG51bGzXDQ4PEBESExQVFhcYGRpW +TlNTaXplViRjbGFzc1xOU0ltYWdlRmxhZ3NWTlNSZXBzW05TVGludENvbG9yV05TQ29sb3JeTlNSZXNp +emluZ01vZGWAAoANEiDDAACAA4AAgAsQAFZ7MSwgMX3SHQ4eIFpOUy5vYmplY3RzoR+ABIAK0h0OIiWi +IySABYAGgAnTDicoKSoaXxAUTlNUSUZGUmVwcmVzZW50YXRpb25fEBlOU0ludGVybmFsTGF5b3V0RGly +ZWN0aW9ugAiAB08REmxNTQAqAAAACgAAABABAAADAAAAAQABAAABAQADAAAAAQABAAABAgADAAAAAgAI +AAgBAwADAAAAAQABAAABBgADAAAAAQABAAABCgADAAAAAQABAAABEQAEAAAAAQAAAAgBEgADAAAAAQAB +AAABFQADAAAAAQACAAABFgADAAAAAQABAAABFwAEAAAAAQAAAAIBHAADAAAAAQABAAABKAADAAAAAQAC +AAABUgADAAAAAQABAAABUwADAAAAAgABAAGHcwAHAAARnAAAANAAAAAAAAARnGFwcGwCAAAAbW50ckdS +QVlYWVogB9wACAAXAA8ALgAPYWNzcEFQUEwAAAAAbm9uZQAAAAAAAAAAAAAAAAAAAAAAAPbWAAEAAAAA +0y1hcHBsAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFZGVzYwAA +AMAAAAB5ZHNjbQAAATwAAAgaY3BydAAACVgAAAAjd3RwdAAACXwAAAAUa1RSQwAACZAAAAgMZGVzYwAA +AAAAAAAfR2VuZXJpYyBHcmF5IEdhbW1hIDIuMiBQcm9maWxlAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAG1s +dWMAAAAAAAAAHwAAAAxza1NLAAAALgAAAYRkYURLAAAAOgAAAbJjYUVTAAAAOAAAAex2aVZOAAAAQAAA +AiRwdEJSAAAASgAAAmR1a1VBAAAALAAAAq5mckZVAAAAPgAAAtpodUhVAAAANAAAAxh6aFRXAAAAGgAA +A0xrb0tSAAAAIgAAA2ZuYk5PAAAAOgAAA4hjc0NaAAAAKAAAA8JoZUlMAAAAJAAAA+pyb1JPAAAAKgAA +BA5kZURFAAAATgAABDhpdElUAAAATgAABIZzdlNFAAAAOAAABNR6aENOAAAAGgAABQxqYUpQAAAAJgAA +BSZlbEdSAAAAKgAABUxwdFBPAAAAUgAABXZubE5MAAAAQAAABchlc0VTAAAATAAABgh0aFRIAAAAMgAA +BlR0clRSAAAAJAAABoZmaUZJAAAARgAABqpockhSAAAAPgAABvBwbFBMAAAASgAABy5hckVHAAAALAAA +B3hydVJVAAAAOgAAB6RlblVTAAAAPAAAB94AVgFhAGUAbwBiAGUAYwBuAOEAIABzAGkAdgDhACAAZwBh +AG0AYQAgADIALAAyAEcAZQBuAGUAcgBpAHMAawAgAGcAcgDlACAAMgAsADIAIABnAGEAbQBtAGEALQBw +AHIAbwBmAGkAbABHAGEAbQBtAGEAIABkAGUAIABnAHIAaQBzAG8AcwAgAGcAZQBuAOgAcgBpAGMAYQAg +ADIALgAyAEMepQB1ACAAaADsAG4AaAAgAE0A4AB1ACAAeADhAG0AIABDAGgAdQBuAGcAIABHAGEAbQBt +AGEAIAAyAC4AMgBQAGUAcgBmAGkAbAAgAEcAZQBuAOkAcgBpAGMAbwAgAGQAYQAgAEcAYQBtAGEAIABk +AGUAIABDAGkAbgB6AGEAcwAgADIALAAyBBcEMAQzBDAEOwRMBD0EMAAgAEcAcgBhAHkALQQzBDAEPAQw +ACAAMgAuADIAUAByAG8AZgBpAGwAIABnAOkAbgDpAHIAaQBxAHUAZQAgAGcAcgBpAHMAIABnAGEAbQBt +AGEAIAAyACwAMgDBAGwAdABhAGwA4QBuAG8AcwAgAHMAegD8AHIAawBlACAAZwBhAG0AbQBhACAAMgAu +ADKQGnUocHCWjlFJXqYAMgAuADKCcl9pY8+P8Md8vBgAINaMwMkAIKwQucgAIAAyAC4AMgAg1QS4XNMM +x3wARwBlAG4AZQByAGkAcwBrACAAZwByAOUAIABnAGEAbQBtAGEAIAAyACwAMgAtAHAAcgBvAGYAaQBs +AE8AYgBlAGMAbgDhACABYQBlAGQA4QAgAGcAYQBtAGEAIAAyAC4AMgXSBdAF3gXUACAF0AXkBdUF6AAg +BdsF3AXcBdkAIAAyAC4AMgBHAGEAbQBhACAAZwByAGkAIABnAGUAbgBlAHIAaQBjAQMAIAAyACwAMgBB +AGwAbABnAGUAbQBlAGkAbgBlAHMAIABHAHIAYQB1AHMAdAB1AGYAZQBuAC0AUAByAG8AZgBpAGwAIABH +AGEAbQBtAGEAIAAyACwAMgBQAHIAbwBmAGkAbABvACAAZwByAGkAZwBpAG8AIABnAGUAbgBlAHIAaQBj +AG8AIABkAGUAbABsAGEAIABnAGEAbQBtAGEAIAAyACwAMgBHAGUAbgBlAHIAaQBzAGsAIABnAHIA5QAg +ADIALAAyACAAZwBhAG0AbQBhAHAAcgBvAGYAaQBsZm6QGnBwXqZ8+2VwADIALgAyY8+P8GWHTvZOAIIs +MLAw7DCkMKww8zDeACAAMgAuADIAIDDXMO0w1TChMKQw6wOTA7UDvQO5A7oDzAAgA5MDugPBA7kAIAOT +A6wDvAO8A7EAIAAyAC4AMgBQAGUAcgBmAGkAbAAgAGcAZQBuAOkAcgBpAGMAbwAgAGQAZQAgAGMAaQBu +AHoAZQBuAHQAbwBzACAAZABhACAARwBhAG0AbQBhACAAMgAsADIAQQBsAGcAZQBtAGUAZQBuACAAZwBy +AGkAagBzACAAZwBhAG0AbQBhACAAMgAsADIALQBwAHIAbwBmAGkAZQBsAFAAZQByAGYAaQBsACAAZwBl +AG4A6QByAGkAYwBvACAAZABlACAAZwBhAG0AbQBhACAAZABlACAAZwByAGkAcwBlAHMAIAAyACwAMg4j +DjEOBw4qDjUOQQ4BDiEOIQ4yDkAOAQ4jDiIOTA4XDjEOSA4nDkQOGwAgADIALgAyAEcAZQBuAGUAbAAg +AEcAcgBpACAARwBhAG0AYQAgADIALAAyAFkAbABlAGkAbgBlAG4AIABoAGEAcgBtAGEAYQBuACAAZwBh +AG0AbQBhACAAMgAsADIAIAAtAHAAcgBvAGYAaQBpAGwAaQBHAGUAbgBlAHIAaQENAGsAaQAgAEcAcgBh +AHkAIABHAGEAbQBtAGEAIAAyAC4AMgAgAHAAcgBvAGYAaQBsAFUAbgBpAHcAZQByAHMAYQBsAG4AeQAg +AHAAcgBvAGYAaQBsACAAcwB6AGEAcgBvAVsAYwBpACAAZwBhAG0AbQBhACAAMgAsADIGOgYnBkUGJwAg +ADIALgAyACAGRAZIBkYAIAYxBkUGJwYvBkoAIAY5BicGRQQeBDEESQQwBE8AIARBBDUEQAQwBE8AIAQz +BDAEPAQ8BDAAIAAyACwAMgAtBD8EQAQ+BEQEOAQ7BEwARwBlAG4AZQByAGkAYwAgAEcAcgBhAHkAIABH +AGEAbQBtAGEAIAAyAC4AMgAgAFAAcgBvAGYAaQBsAGUAAHRleHQAAAAAQ29weXJpZ2h0IEFwcGxlIElu +Yy4sIDIwMTIAAFhZWiAAAAAAAADzUQABAAAAARbMY3VydgAAAAAAAAQAAAAABQAKAA8AFAAZAB4AIwAo +AC0AMgA3ADsAQABFAEoATwBUAFkAXgBjAGgAbQByAHcAfACBAIYAiwCQAJUAmgCfAKQAqQCuALIAtwC8 +AMEAxgDLANAA1QDbAOAA5QDrAPAA9gD7AQEBBwENARMBGQEfASUBKwEyATgBPgFFAUwBUgFZAWABZwFu +AXUBfAGDAYsBkgGaAaEBqQGxAbkBwQHJAdEB2QHhAekB8gH6AgMCDAIUAh0CJgIvAjgCQQJLAlQCXQJn +AnECegKEAo4CmAKiAqwCtgLBAssC1QLgAusC9QMAAwsDFgMhAy0DOANDA08DWgNmA3IDfgOKA5YDogOu +A7oDxwPTA+AD7AP5BAYEEwQgBC0EOwRIBFUEYwRxBH4EjASaBKgEtgTEBNME4QTwBP4FDQUcBSsFOgVJ +BVgFZwV3BYYFlgWmBbUFxQXVBeUF9gYGBhYGJwY3BkgGWQZqBnsGjAadBq8GwAbRBuMG9QcHBxkHKwc9 +B08HYQd0B4YHmQesB78H0gflB/gICwgfCDIIRghaCG4IggiWCKoIvgjSCOcI+wkQCSUJOglPCWQJeQmP +CaQJugnPCeUJ+woRCicKPQpUCmoKgQqYCq4KxQrcCvMLCwsiCzkLUQtpC4ALmAuwC8gL4Qv5DBIMKgxD +DFwMdQyODKcMwAzZDPMNDQ0mDUANWg10DY4NqQ3DDd4N+A4TDi4OSQ5kDn8Omw62DtIO7g8JDyUPQQ9e +D3oPlg+zD88P7BAJECYQQxBhEH4QmxC5ENcQ9RETETERTxFtEYwRqhHJEegSBxImEkUSZBKEEqMSwxLj +EwMTIxNDE2MTgxOkE8UT5RQGFCcUSRRqFIsUrRTOFPAVEhU0FVYVeBWbFb0V4BYDFiYWSRZsFo8WshbW +FvoXHRdBF2UXiReuF9IX9xgbGEAYZRiKGK8Y1Rj6GSAZRRlrGZEZtxndGgQaKhpRGncanhrFGuwbFBs7 +G2MbihuyG9ocAhwqHFIcexyjHMwc9R0eHUcdcB2ZHcMd7B4WHkAeah6UHr4e6R8THz4faR+UH78f6iAV +IEEgbCCYIMQg8CEcIUghdSGhIc4h+yInIlUigiKvIt0jCiM4I2YjlCPCI/AkHyRNJHwkqyTaJQklOCVo +JZclxyX3JicmVyaHJrcm6CcYJ0kneierJ9woDSg/KHEooijUKQYpOClrKZ0p0CoCKjUqaCqbKs8rAis2 +K2krnSvRLAUsOSxuLKIs1y0MLUEtdi2rLeEuFi5MLoIuty7uLyQvWi+RL8cv/jA1MGwwpDDbMRIxSjGC +Mbox8jIqMmMymzLUMw0zRjN/M7gz8TQrNGU0njTYNRM1TTWHNcI1/TY3NnI2rjbpNyQ3YDecN9c4FDhQ +OIw4yDkFOUI5fzm8Ofk6Njp0OrI67zstO2s7qjvoPCc8ZTykPOM9Ij1hPaE94D4gPmA+oD7gPyE/YT+i +P+JAI0BkQKZA50EpQWpBrEHuQjBCckK1QvdDOkN9Q8BEA0RHRIpEzkUSRVVFmkXeRiJGZ0arRvBHNUd7 +R8BIBUhLSJFI10kdSWNJqUnwSjdKfUrESwxLU0uaS+JMKkxyTLpNAk1KTZNN3E4lTm5Ot08AT0lPk0/d +UCdQcVC7UQZRUFGbUeZSMVJ8UsdTE1NfU6pT9lRCVI9U21UoVXVVwlYPVlxWqVb3V0RXklfgWC9YfVjL +WRpZaVm4WgdaVlqmWvVbRVuVW+VcNVyGXNZdJ114XcleGl5sXr1fD19hX7NgBWBXYKpg/GFPYaJh9WJJ +Ypxi8GNDY5dj62RAZJRk6WU9ZZJl52Y9ZpJm6Gc9Z5Nn6Wg/aJZo7GlDaZpp8WpIap9q92tPa6dr/2xX +bK9tCG1gbbluEm5rbsRvHm94b9FwK3CGcOBxOnGVcfByS3KmcwFzXXO4dBR0cHTMdSh1hXXhdj52m3b4 +d1Z3s3gReG54zHkqeYl553pGeqV7BHtje8J8IXyBfOF9QX2hfgF+Yn7CfyN/hH/lgEeAqIEKgWuBzYIw +gpKC9INXg7qEHYSAhOOFR4Wrhg6GcobXhzuHn4gEiGmIzokziZmJ/opkisqLMIuWi/yMY4zKjTGNmI3/ +jmaOzo82j56QBpBukNaRP5GokhGSepLjk02TtpQglIqU9JVflcmWNJaflwqXdZfgmEyYuJkkmZCZ/Jpo +mtWbQpuvnByciZz3nWSd0p5Anq6fHZ+Ln/qgaaDYoUehtqImopajBqN2o+akVqTHpTilqaYapoum/adu +p+CoUqjEqTepqaocqo+rAqt1q+msXKzQrUStuK4trqGvFq+LsACwdbDqsWCx1rJLssKzOLOutCW0nLUT +tYq2AbZ5tvC3aLfguFm40blKucK6O7q1uy67p7whvJu9Fb2Pvgq+hL7/v3q/9cBwwOzBZ8Hjwl/C28NY +w9TEUcTOxUvFyMZGxsPHQce/yD3IvMk6ybnKOMq3yzbLtsw1zLXNNc21zjbOts83z7jQOdC60TzRvtI/ +0sHTRNPG1EnUy9VO1dHWVdbY11zX4Nhk2OjZbNnx2nba+9uA3AXcit0Q3ZbeHN6i3ynfr+A24L3hROHM +4lPi2+Nj4+vkc+T85YTmDeaW5x/nqegy6LzpRunQ6lvq5etw6/vshu0R7ZzuKO6070DvzPBY8OXxcvH/ +8ozzGfOn9DT0wvVQ9d72bfb794r4Gfio+Tj5x/pX+uf7d/wH/Jj9Kf26/kv+3P9t///SLS4vMFokY2xh +c3NuYW1lWCRjbGFzc2VzXxAQTlNCaXRtYXBJbWFnZVJlcKMvMTJaTlNJbWFnZVJlcFhOU09iamVjdNIt +LjQ1V05TQXJyYXmiNDLSLS43OF5OU011dGFibGVBcnJheaM3NDLTOjsOPD0+V05TV2hpdGVcTlNDb2xv +clNwYWNlRDAgMAAQA4AM0i0uQEFXTlNDb2xvcqJAMtItLkNEV05TSW1hZ2WiQzIACAARABoAJAApADIA +NwBJAEwAUQBTAGIAaAB3AH4AhQCSAJkApQCtALwAvgDAAMUAxwDJAMsAzQDUANkA5ADmAOgA6gDvAPIA +9AD2APgA/wEWATIBNAE2E6YTqxO2E78T0hPWE+ET6hPvE/cT+hP/FA4UEhQZFCEULhQzFDUUNxQ8FEQU +RxRMFFQAAAAAAAACAQAAAAAAAABFAAAAAAAAAAAAAAAAAAAUVw diff --git a/i18n/src/main/resources/hu.lproj/Command.xib b/i18n/src/main/resources/hu.lproj/Command.xib index 3d44f95f68..a225add30d 100644 --- a/i18n/src/main/resources/hu.lproj/Command.xib +++ b/i18n/src/main/resources/hu.lproj/Command.xib @@ -1,7 +1,7 @@ - + - + diff --git a/i18n/src/main/resources/hu.lproj/Connection.xib b/i18n/src/main/resources/hu.lproj/Connection.xib index 78b7a17810..f6c517c130 100644 --- a/i18n/src/main/resources/hu.lproj/Connection.xib +++ b/i18n/src/main/resources/hu.lproj/Connection.xib @@ -1,7 +1,7 @@ - + - + @@ -76,7 +76,7 @@ - + diff --git a/i18n/src/main/resources/hu.lproj/Info.xib b/i18n/src/main/resources/hu.lproj/Info.xib index 2c4576d116..950b859a09 100644 --- a/i18n/src/main/resources/hu.lproj/Info.xib +++ b/i18n/src/main/resources/hu.lproj/Info.xib @@ -1,7 +1,7 @@ - + - + @@ -89,7 +89,7 @@ - + @@ -1017,21 +1017,21 @@ - + - + - + @@ -1121,21 +1121,21 @@ - + - + - + @@ -1250,27 +1250,27 @@ diff --git a/i18n/src/main/resources/hu.lproj/Login.strings b/i18n/src/main/resources/hu.lproj/Login.strings index 4e8950d404..12ea598f6d 100644 Binary files a/i18n/src/main/resources/hu.lproj/Login.strings and b/i18n/src/main/resources/hu.lproj/Login.strings differ diff --git a/i18n/src/main/resources/hu.lproj/Main.xib b/i18n/src/main/resources/hu.lproj/Main.xib index 458621f3b3..11f3932c9f 100644 --- a/i18n/src/main/resources/hu.lproj/Main.xib +++ b/i18n/src/main/resources/hu.lproj/Main.xib @@ -1,7 +1,7 @@ - + - + @@ -94,7 +94,7 @@ - + @@ -159,19 +159,19 @@ - + - + - + @@ -321,12 +321,12 @@ CA - + - + @@ -566,6 +566,10 @@ CA - + + + + + diff --git a/i18n/src/main/resources/hu.lproj/Preferences.xib b/i18n/src/main/resources/hu.lproj/Preferences.xib index eb40049c1c..58bb6304f9 100644 --- a/i18n/src/main/resources/hu.lproj/Preferences.xib +++ b/i18n/src/main/resources/hu.lproj/Preferences.xib @@ -1,7 +1,7 @@ - + - + @@ -266,7 +266,7 @@ - + @@ -307,7 +307,7 @@ - + @@ -539,7 +539,7 @@ - + @@ -579,7 +579,7 @@ - + @@ -614,7 +614,7 @@ - + @@ -643,7 +643,7 @@ - + @@ -1219,7 +1219,7 @@ - + @@ -1251,7 +1251,7 @@ - + @@ -1291,7 +1291,7 @@ - + @@ -1364,7 +1364,7 @@ - + @@ -1559,7 +1559,7 @@ - + @@ -1619,7 +1619,7 @@ - + @@ -1644,7 +1644,7 @@ - + @@ -1686,7 +1686,7 @@ - + @@ -1714,7 +1714,7 @@ - + @@ -1742,7 +1742,7 @@ - + @@ -1771,7 +1771,7 @@ - + @@ -1806,7 +1806,7 @@ - + @@ -1890,7 +1890,7 @@ - + @@ -1949,7 +1949,7 @@ - + @@ -1977,7 +1977,7 @@ - + @@ -2006,7 +2006,7 @@ - + diff --git a/i18n/src/main/resources/hu.lproj/Profiles.xib b/i18n/src/main/resources/hu.lproj/Profiles.xib index 63ba3fa7b6..da07e21786 100644 --- a/i18n/src/main/resources/hu.lproj/Profiles.xib +++ b/i18n/src/main/resources/hu.lproj/Profiles.xib @@ -1,7 +1,7 @@ - + - + diff --git a/i18n/src/main/resources/hu.lproj/Prompt.xib b/i18n/src/main/resources/hu.lproj/Prompt.xib index 40ae530ec5..db30080784 100644 --- a/i18n/src/main/resources/hu.lproj/Prompt.xib +++ b/i18n/src/main/resources/hu.lproj/Prompt.xib @@ -1,7 +1,7 @@ - + - + @@ -95,7 +95,7 @@ Gw - + diff --git a/i18n/src/main/resources/hu.lproj/S3.strings b/i18n/src/main/resources/hu.lproj/S3.strings index d8e3e6ee16..4ecd4a62b7 100644 Binary files a/i18n/src/main/resources/hu.lproj/S3.strings and b/i18n/src/main/resources/hu.lproj/S3.strings differ diff --git a/i18n/src/main/resources/hu.lproj/Updater.strings b/i18n/src/main/resources/hu.lproj/Updater.strings index 979da65c02..1a0e5aad11 100644 Binary files a/i18n/src/main/resources/hu.lproj/Updater.strings and b/i18n/src/main/resources/hu.lproj/Updater.strings differ diff --git a/i18n/src/main/resources/it.lproj/Bookmark.xib b/i18n/src/main/resources/it.lproj/Bookmark.xib index 70342feb69..2f030f33a2 100644 --- a/i18n/src/main/resources/it.lproj/Bookmark.xib +++ b/i18n/src/main/resources/it.lproj/Bookmark.xib @@ -1,7 +1,7 @@ - + - + @@ -92,7 +92,7 @@ - + @@ -489,95 +489,95 @@ YnBsaXN0MDDUAQIDBAUGBwpYJHZlcnNpb25ZJGFyY2hpdmVyVCR0b3BYJG9iamVjdHMSAAGGoF8QD05T -S2V5ZWRBcmNoaXZlctEICVRyb290gAGuCwwZGh8UJCkqMTQ3PUBVJG51bGzWDQ4PEBESExQVFhcYVk5T -U2l6ZV5OU1Jlc2l6aW5nTW9kZVYkY2xhc3NcTlNJbWFnZUZsYWdzVk5TUmVwc1dOU0NvbG9ygAIQAIAN -EiDDAACAA4ALVnsxLCAxfdIbDxweWk5TLm9iamVjdHOhHYAEgArSGw8gI6IhIoAFgAaACdMPJSYnKBRf -EBROU1RJRkZSZXByZXNlbnRhdGlvbl8QGU5TSW50ZXJuYWxMYXlvdXREaXJlY3Rpb26ACIAHTxESbE1N -ACoAAAAKAAAAEAEAAAMAAAABAAEAAAEBAAMAAAABAAEAAAECAAMAAAACAAgACAEDAAMAAAABAAEAAAEG -AAMAAAABAAEAAAEKAAMAAAABAAEAAAERAAQAAAABAAAACAESAAMAAAABAAEAAAEVAAMAAAABAAIAAAEW -AAMAAAABAAEAAAEXAAQAAAABAAAAAgEcAAMAAAABAAEAAAEoAAMAAAABAAIAAAFSAAMAAAABAAEAAAFT -AAMAAAACAAEAAYdzAAcAABGcAAAA0AAAAAAAABGcYXBwbAIAAABtbnRyR1JBWVhZWiAH3AAIABcADwAu -AA9hY3NwQVBQTAAAAABub25lAAAAAAAAAAAAAAAAAAAAAAAA9tYAAQAAAADTLWFwcGwAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAVkZXNjAAAAwAAAAHlkc2NtAAABPAAA -CBpjcHJ0AAAJWAAAACN3dHB0AAAJfAAAABRrVFJDAAAJkAAACAxkZXNjAAAAAAAAAB9HZW5lcmljIEdy -YXkgR2FtbWEgMi4yIFByb2ZpbGUAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAbWx1YwAAAAAAAAAfAAAADHNr -U0sAAAAuAAABhGRhREsAAAA6AAABsmNhRVMAAAA4AAAB7HZpVk4AAABAAAACJHB0QlIAAABKAAACZHVr -VUEAAAAsAAACrmZyRlUAAAA+AAAC2mh1SFUAAAA0AAADGHpoVFcAAAAaAAADTGtvS1IAAAAiAAADZm5i -Tk8AAAA6AAADiGNzQ1oAAAAoAAADwmhlSUwAAAAkAAAD6nJvUk8AAAAqAAAEDmRlREUAAABOAAAEOGl0 -SVQAAABOAAAEhnN2U0UAAAA4AAAE1HpoQ04AAAAaAAAFDGphSlAAAAAmAAAFJmVsR1IAAAAqAAAFTHB0 -UE8AAABSAAAFdm5sTkwAAABAAAAFyGVzRVMAAABMAAAGCHRoVEgAAAAyAAAGVHRyVFIAAAAkAAAGhmZp -RkkAAABGAAAGqmhySFIAAAA+AAAG8HBsUEwAAABKAAAHLmFyRUcAAAAsAAAHeHJ1UlUAAAA6AAAHpGVu -VVMAAAA8AAAH3gBWAWEAZQBvAGIAZQBjAG4A4QAgAHMAaQB2AOEAIABnAGEAbQBhACAAMgAsADIARwBl -AG4AZQByAGkAcwBrACAAZwByAOUAIAAyACwAMgAgAGcAYQBtAG0AYQAtAHAAcgBvAGYAaQBsAEcAYQBt -AG0AYQAgAGQAZQAgAGcAcgBpAHMAbwBzACAAZwBlAG4A6AByAGkAYwBhACAAMgAuADIAQx6lAHUAIABo -AOwAbgBoACAATQDgAHUAIAB4AOEAbQAgAEMAaAB1AG4AZwAgAEcAYQBtAG0AYQAgADIALgAyAFAAZQBy -AGYAaQBsACAARwBlAG4A6QByAGkAYwBvACAAZABhACAARwBhAG0AYQAgAGQAZQAgAEMAaQBuAHoAYQBz -ACAAMgAsADIEFwQwBDMEMAQ7BEwEPQQwACAARwByAGEAeQAtBDMEMAQ8BDAAIAAyAC4AMgBQAHIAbwBm -AGkAbAAgAGcA6QBuAOkAcgBpAHEAdQBlACAAZwByAGkAcwAgAGcAYQBtAG0AYQAgADIALAAyAMEAbAB0 -AGEAbADhAG4AbwBzACAAcwB6APwAcgBrAGUAIABnAGEAbQBtAGEAIAAyAC4AMpAadShwcJaOUUlepgAy -AC4AMoJyX2ljz4/wx3y8GAAg1ozAyQAgrBC5yAAgADIALgAyACDVBLhc0wzHfABHAGUAbgBlAHIAaQBz -AGsAIABnAHIA5QAgAGcAYQBtAG0AYQAgADIALAAyAC0AcAByAG8AZgBpAGwATwBiAGUAYwBuAOEAIAFh -AGUAZADhACAAZwBhAG0AYQAgADIALgAyBdIF0AXeBdQAIAXQBeQF1QXoACAF2wXcBdwF2QAgADIALgAy -AEcAYQBtAGEAIABnAHIAaQAgAGcAZQBuAGUAcgBpAGMBAwAgADIALAAyAEEAbABsAGcAZQBtAGUAaQBu -AGUAcwAgAEcAcgBhAHUAcwB0AHUAZgBlAG4ALQBQAHIAbwBmAGkAbAAgAEcAYQBtAG0AYQAgADIALAAy -AFAAcgBvAGYAaQBsAG8AIABnAHIAaQBnAGkAbwAgAGcAZQBuAGUAcgBpAGMAbwAgAGQAZQBsAGwAYQAg -AGcAYQBtAG0AYQAgADIALAAyAEcAZQBuAGUAcgBpAHMAawAgAGcAcgDlACAAMgAsADIAIABnAGEAbQBt -AGEAcAByAG8AZgBpAGxmbpAacHBepnz7ZXAAMgAuADJjz4/wZYdO9k4AgiwwsDDsMKQwrDDzMN4AIAAy -AC4AMgAgMNcw7TDVMKEwpDDrA5MDtQO9A7kDugPMACADkwO6A8EDuQAgA5MDrAO8A7wDsQAgADIALgAy -AFAAZQByAGYAaQBsACAAZwBlAG4A6QByAGkAYwBvACAAZABlACAAYwBpAG4AegBlAG4AdABvAHMAIABk -AGEAIABHAGEAbQBtAGEAIAAyACwAMgBBAGwAZwBlAG0AZQBlAG4AIABnAHIAaQBqAHMAIABnAGEAbQBt -AGEAIAAyACwAMgAtAHAAcgBvAGYAaQBlAGwAUABlAHIAZgBpAGwAIABnAGUAbgDpAHIAaQBjAG8AIABk -AGUAIABnAGEAbQBtAGEAIABkAGUAIABnAHIAaQBzAGUAcwAgADIALAAyDiMOMQ4HDioONQ5BDgEOIQ4h -DjIOQA4BDiMOIg5MDhcOMQ5IDicORA4bACAAMgAuADIARwBlAG4AZQBsACAARwByAGkAIABHAGEAbQBh -ACAAMgAsADIAWQBsAGUAaQBuAGUAbgAgAGgAYQByAG0AYQBhAG4AIABnAGEAbQBtAGEAIAAyACwAMgAg -AC0AcAByAG8AZgBpAGkAbABpAEcAZQBuAGUAcgBpAQ0AawBpACAARwByAGEAeQAgAEcAYQBtAG0AYQAg -ADIALgAyACAAcAByAG8AZgBpAGwAVQBuAGkAdwBlAHIAcwBhAGwAbgB5ACAAcAByAG8AZgBpAGwAIABz -AHoAYQByAG8BWwBjAGkAIABnAGEAbQBtAGEAIAAyACwAMgY6BicGRQYnACAAMgAuADIAIAZEBkgGRgAg -BjEGRQYnBi8GSgAgBjkGJwZFBB4EMQRJBDAETwAgBEEENQRABDAETwAgBDMEMAQ8BDwEMAAgADIALAAy -AC0EPwRABD4ERAQ4BDsETABHAGUAbgBlAHIAaQBjACAARwByAGEAeQAgAEcAYQBtAG0AYQAgADIALgAy -ACAAUAByAG8AZgBpAGwAZQAAdGV4dAAAAABDb3B5cmlnaHQgQXBwbGUgSW5jLiwgMjAxMgAAWFlaIAAA -AAAAAPNRAAEAAAABFsxjdXJ2AAAAAAAABAAAAAAFAAoADwAUABkAHgAjACgALQAyADcAOwBAAEUASgBP -AFQAWQBeAGMAaABtAHIAdwB8AIEAhgCLAJAAlQCaAJ8ApACpAK4AsgC3ALwAwQDGAMsA0ADVANsA4ADl -AOsA8AD2APsBAQEHAQ0BEwEZAR8BJQErATIBOAE+AUUBTAFSAVkBYAFnAW4BdQF8AYMBiwGSAZoBoQGp -AbEBuQHBAckB0QHZAeEB6QHyAfoCAwIMAhQCHQImAi8COAJBAksCVAJdAmcCcQJ6AoQCjgKYAqICrAK2 -AsECywLVAuAC6wL1AwADCwMWAyEDLQM4A0MDTwNaA2YDcgN+A4oDlgOiA64DugPHA9MD4APsA/kEBgQT -BCAELQQ7BEgEVQRjBHEEfgSMBJoEqAS2BMQE0wThBPAE/gUNBRwFKwU6BUkFWAVnBXcFhgWWBaYFtQXF -BdUF5QX2BgYGFgYnBjcGSAZZBmoGewaMBp0GrwbABtEG4wb1BwcHGQcrBz0HTwdhB3QHhgeZB6wHvwfS -B+UH+AgLCB8IMghGCFoIbgiCCJYIqgi+CNII5wj7CRAJJQk6CU8JZAl5CY8JpAm6Cc8J5Qn7ChEKJwo9 -ClQKagqBCpgKrgrFCtwK8wsLCyILOQtRC2kLgAuYC7ALyAvhC/kMEgwqDEMMXAx1DI4MpwzADNkM8w0N -DSYNQA1aDXQNjg2pDcMN3g34DhMOLg5JDmQOfw6bDrYO0g7uDwkPJQ9BD14Peg+WD7MPzw/sEAkQJhBD -EGEQfhCbELkQ1xD1ERMRMRFPEW0RjBGqEckR6BIHEiYSRRJkEoQSoxLDEuMTAxMjE0MTYxODE6QTxRPl -FAYUJxRJFGoUixStFM4U8BUSFTQVVhV4FZsVvRXgFgMWJhZJFmwWjxayFtYW+hcdF0EXZReJF64X0hf3 -GBsYQBhlGIoYrxjVGPoZIBlFGWsZkRm3Gd0aBBoqGlEadxqeGsUa7BsUGzsbYxuKG7Ib2hwCHCocUhx7 -HKMczBz1HR4dRx1wHZkdwx3sHhYeQB5qHpQevh7pHxMfPh9pH5Qfvx/qIBUgQSBsIJggxCDwIRwhSCF1 -IaEhziH7IiciVSKCIq8i3SMKIzgjZiOUI8Ij8CQfJE0kfCSrJNolCSU4JWgllyXHJfcmJyZXJocmtybo -JxgnSSd6J6sn3CgNKD8ocSiiKNQpBik4KWspnSnQKgIqNSpoKpsqzysCKzYraSudK9EsBSw5LG4soizX -LQwtQS12Last4S4WLkwugi63Lu4vJC9aL5Evxy/+MDUwbDCkMNsxEjFKMYIxujHyMioyYzKbMtQzDTNG -M38zuDPxNCs0ZTSeNNg1EzVNNYc1wjX9Njc2cjauNuk3JDdgN5w31zgUOFA4jDjIOQU5Qjl/Obw5+To2 -OnQ6sjrvOy07azuqO+g8JzxlPKQ84z0iPWE9oT3gPiA+YD6gPuA/IT9hP6I/4kAjQGRApkDnQSlBakGs -Qe5CMEJyQrVC90M6Q31DwEQDREdEikTORRJFVUWaRd5GIkZnRqtG8Ec1R3tHwEgFSEtIkUjXSR1JY0mp -SfBKN0p9SsRLDEtTS5pL4kwqTHJMuk0CTUpNk03cTiVObk63TwBPSU+TT91QJ1BxULtRBlFQUZtR5lIx -UnxSx1MTU19TqlP2VEJUj1TbVShVdVXCVg9WXFapVvdXRFeSV+BYL1h9WMtZGllpWbhaB1pWWqZa9VtF -W5Vb5Vw1XIZc1l0nXXhdyV4aXmxevV8PX2Ffs2AFYFdgqmD8YU9homH1YklinGLwY0Njl2PrZEBklGTp -ZT1lkmXnZj1mkmboZz1nk2fpaD9olmjsaUNpmmnxakhqn2r3a09rp2v/bFdsr20IbWBtuW4SbmtuxG8e -b3hv0XArcIZw4HE6cZVx8HJLcqZzAXNdc7h0FHRwdMx1KHWFdeF2Pnabdvh3VnezeBF4bnjMeSp5iXnn -ekZ6pXsEe2N7wnwhfIF84X1BfaF+AX5ifsJ/I3+Ef+WAR4CogQqBa4HNgjCCkoL0g1eDuoQdhICE44VH -hauGDoZyhteHO4efiASIaYjOiTOJmYn+imSKyoswi5aL/IxjjMqNMY2Yjf+OZo7OjzaPnpAGkG6Q1pE/ -kaiSEZJ6kuOTTZO2lCCUipT0lV+VyZY0lp+XCpd1l+CYTJi4mSSZkJn8mmia1ZtCm6+cHJyJnPedZJ3S -nkCerp8dn4uf+qBpoNihR6G2oiailqMGo3aj5qRWpMelOKWpphqmi6b9p26n4KhSqMSpN6mpqhyqj6sC -q3Wr6axcrNCtRK24ri2uoa8Wr4uwALB1sOqxYLHWskuywrM4s660JbSctRO1irYBtnm28Ldot+C4WbjR -uUq5wro7urW7LrunvCG8m70VvY++Cr6Evv+/er/1wHDA7MFnwePCX8Lbw1jD1MRRxM7FS8XIxkbGw8dB -x7/IPci8yTrJuco4yrfLNsu2zDXMtc01zbXONs62zzfPuNA50LrRPNG+0j/SwdNE08bUSdTL1U7V0dZV -1tjXXNfg2GTY6Nls2fHadtr724DcBdyK3RDdlt4c3qLfKd+v4DbgveFE4cziU+Lb42Pj6+Rz5PzlhOYN -5pbnH+ep6DLovOlG6dDqW+rl63Dr++yG7RHtnO4o7rTvQO/M8Fjw5fFy8f/yjPMZ86f0NPTC9VD13vZt -9vv3ivgZ+Kj5OPnH+lf65/t3/Af8mP0p/br+S/7c/23//9IrLC0uWiRjbGFzc25hbWVYJGNsYXNzZXNf -EBBOU0JpdG1hcEltYWdlUmVwoy0vMFpOU0ltYWdlUmVwWE5TT2JqZWN00issMjNXTlNBcnJheaIyMNIr -LDU2Xk5TTXV0YWJsZUFycmF5ozUyMNM4OQ86OzxXTlNXaGl0ZVxOU0NvbG9yU3BhY2VEMCAwABADgAzS -Kyw+P1dOU0NvbG9yoj4w0issQUJXTlNJbWFnZaJBMAAIABEAGgAkACkAMgA3AEkATABRAFMAYgBoAHUA -fACLAJIAnwCmAK4AsACyALQAuQC7AL0AxADJANQA1gDYANoA3wDiAOQA5gDoAO8BBgEiASQBJhOWE5sT -phOvE8ITxhPRE9oT3xPnE+oT7xP+FAIUCRQRFB4UIxQlFCcULBQ0FDcUPBREAAAAAAAAAgEAAAAAAAAA -QwAAAAAAAAAAAAAAAAAAFEc +S2V5ZWRBcmNoaXZlctEICVRyb290gAGuCwwbHCEaJissMzY5P0JVJG51bGzXDQ4PEBESExQVFhcYGRpW +TlNTaXplViRjbGFzc1xOU0ltYWdlRmxhZ3NWTlNSZXBzW05TVGludENvbG9yV05TQ29sb3JeTlNSZXNp +emluZ01vZGWAAoANEiDDAACAA4AAgAsQAFZ7MSwgMX3SHQ4eIFpOUy5vYmplY3RzoR+ABIAK0h0OIiWi +IySABYAGgAnTDicoKSoaXxAUTlNUSUZGUmVwcmVzZW50YXRpb25fEBlOU0ludGVybmFsTGF5b3V0RGly +ZWN0aW9ugAiAB08REmxNTQAqAAAACgAAABABAAADAAAAAQABAAABAQADAAAAAQABAAABAgADAAAAAgAI +AAgBAwADAAAAAQABAAABBgADAAAAAQABAAABCgADAAAAAQABAAABEQAEAAAAAQAAAAgBEgADAAAAAQAB +AAABFQADAAAAAQACAAABFgADAAAAAQABAAABFwAEAAAAAQAAAAIBHAADAAAAAQABAAABKAADAAAAAQAC +AAABUgADAAAAAQABAAABUwADAAAAAgABAAGHcwAHAAARnAAAANAAAAAAAAARnGFwcGwCAAAAbW50ckdS +QVlYWVogB9wACAAXAA8ALgAPYWNzcEFQUEwAAAAAbm9uZQAAAAAAAAAAAAAAAAAAAAAAAPbWAAEAAAAA +0y1hcHBsAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFZGVzYwAA +AMAAAAB5ZHNjbQAAATwAAAgaY3BydAAACVgAAAAjd3RwdAAACXwAAAAUa1RSQwAACZAAAAgMZGVzYwAA +AAAAAAAfR2VuZXJpYyBHcmF5IEdhbW1hIDIuMiBQcm9maWxlAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAG1s +dWMAAAAAAAAAHwAAAAxza1NLAAAALgAAAYRkYURLAAAAOgAAAbJjYUVTAAAAOAAAAex2aVZOAAAAQAAA +AiRwdEJSAAAASgAAAmR1a1VBAAAALAAAAq5mckZVAAAAPgAAAtpodUhVAAAANAAAAxh6aFRXAAAAGgAA +A0xrb0tSAAAAIgAAA2ZuYk5PAAAAOgAAA4hjc0NaAAAAKAAAA8JoZUlMAAAAJAAAA+pyb1JPAAAAKgAA +BA5kZURFAAAATgAABDhpdElUAAAATgAABIZzdlNFAAAAOAAABNR6aENOAAAAGgAABQxqYUpQAAAAJgAA +BSZlbEdSAAAAKgAABUxwdFBPAAAAUgAABXZubE5MAAAAQAAABchlc0VTAAAATAAABgh0aFRIAAAAMgAA +BlR0clRSAAAAJAAABoZmaUZJAAAARgAABqpockhSAAAAPgAABvBwbFBMAAAASgAABy5hckVHAAAALAAA +B3hydVJVAAAAOgAAB6RlblVTAAAAPAAAB94AVgFhAGUAbwBiAGUAYwBuAOEAIABzAGkAdgDhACAAZwBh +AG0AYQAgADIALAAyAEcAZQBuAGUAcgBpAHMAawAgAGcAcgDlACAAMgAsADIAIABnAGEAbQBtAGEALQBw +AHIAbwBmAGkAbABHAGEAbQBtAGEAIABkAGUAIABnAHIAaQBzAG8AcwAgAGcAZQBuAOgAcgBpAGMAYQAg +ADIALgAyAEMepQB1ACAAaADsAG4AaAAgAE0A4AB1ACAAeADhAG0AIABDAGgAdQBuAGcAIABHAGEAbQBt +AGEAIAAyAC4AMgBQAGUAcgBmAGkAbAAgAEcAZQBuAOkAcgBpAGMAbwAgAGQAYQAgAEcAYQBtAGEAIABk +AGUAIABDAGkAbgB6AGEAcwAgADIALAAyBBcEMAQzBDAEOwRMBD0EMAAgAEcAcgBhAHkALQQzBDAEPAQw +ACAAMgAuADIAUAByAG8AZgBpAGwAIABnAOkAbgDpAHIAaQBxAHUAZQAgAGcAcgBpAHMAIABnAGEAbQBt +AGEAIAAyACwAMgDBAGwAdABhAGwA4QBuAG8AcwAgAHMAegD8AHIAawBlACAAZwBhAG0AbQBhACAAMgAu +ADKQGnUocHCWjlFJXqYAMgAuADKCcl9pY8+P8Md8vBgAINaMwMkAIKwQucgAIAAyAC4AMgAg1QS4XNMM +x3wARwBlAG4AZQByAGkAcwBrACAAZwByAOUAIABnAGEAbQBtAGEAIAAyACwAMgAtAHAAcgBvAGYAaQBs +AE8AYgBlAGMAbgDhACABYQBlAGQA4QAgAGcAYQBtAGEAIAAyAC4AMgXSBdAF3gXUACAF0AXkBdUF6AAg +BdsF3AXcBdkAIAAyAC4AMgBHAGEAbQBhACAAZwByAGkAIABnAGUAbgBlAHIAaQBjAQMAIAAyACwAMgBB +AGwAbABnAGUAbQBlAGkAbgBlAHMAIABHAHIAYQB1AHMAdAB1AGYAZQBuAC0AUAByAG8AZgBpAGwAIABH +AGEAbQBtAGEAIAAyACwAMgBQAHIAbwBmAGkAbABvACAAZwByAGkAZwBpAG8AIABnAGUAbgBlAHIAaQBj +AG8AIABkAGUAbABsAGEAIABnAGEAbQBtAGEAIAAyACwAMgBHAGUAbgBlAHIAaQBzAGsAIABnAHIA5QAg +ADIALAAyACAAZwBhAG0AbQBhAHAAcgBvAGYAaQBsZm6QGnBwXqZ8+2VwADIALgAyY8+P8GWHTvZOAIIs +MLAw7DCkMKww8zDeACAAMgAuADIAIDDXMO0w1TChMKQw6wOTA7UDvQO5A7oDzAAgA5MDugPBA7kAIAOT +A6wDvAO8A7EAIAAyAC4AMgBQAGUAcgBmAGkAbAAgAGcAZQBuAOkAcgBpAGMAbwAgAGQAZQAgAGMAaQBu +AHoAZQBuAHQAbwBzACAAZABhACAARwBhAG0AbQBhACAAMgAsADIAQQBsAGcAZQBtAGUAZQBuACAAZwBy +AGkAagBzACAAZwBhAG0AbQBhACAAMgAsADIALQBwAHIAbwBmAGkAZQBsAFAAZQByAGYAaQBsACAAZwBl +AG4A6QByAGkAYwBvACAAZABlACAAZwBhAG0AbQBhACAAZABlACAAZwByAGkAcwBlAHMAIAAyACwAMg4j +DjEOBw4qDjUOQQ4BDiEOIQ4yDkAOAQ4jDiIOTA4XDjEOSA4nDkQOGwAgADIALgAyAEcAZQBuAGUAbAAg +AEcAcgBpACAARwBhAG0AYQAgADIALAAyAFkAbABlAGkAbgBlAG4AIABoAGEAcgBtAGEAYQBuACAAZwBh +AG0AbQBhACAAMgAsADIAIAAtAHAAcgBvAGYAaQBpAGwAaQBHAGUAbgBlAHIAaQENAGsAaQAgAEcAcgBh +AHkAIABHAGEAbQBtAGEAIAAyAC4AMgAgAHAAcgBvAGYAaQBsAFUAbgBpAHcAZQByAHMAYQBsAG4AeQAg +AHAAcgBvAGYAaQBsACAAcwB6AGEAcgBvAVsAYwBpACAAZwBhAG0AbQBhACAAMgAsADIGOgYnBkUGJwAg +ADIALgAyACAGRAZIBkYAIAYxBkUGJwYvBkoAIAY5BicGRQQeBDEESQQwBE8AIARBBDUEQAQwBE8AIAQz +BDAEPAQ8BDAAIAAyACwAMgAtBD8EQAQ+BEQEOAQ7BEwARwBlAG4AZQByAGkAYwAgAEcAcgBhAHkAIABH +AGEAbQBtAGEAIAAyAC4AMgAgAFAAcgBvAGYAaQBsAGUAAHRleHQAAAAAQ29weXJpZ2h0IEFwcGxlIElu +Yy4sIDIwMTIAAFhZWiAAAAAAAADzUQABAAAAARbMY3VydgAAAAAAAAQAAAAABQAKAA8AFAAZAB4AIwAo +AC0AMgA3ADsAQABFAEoATwBUAFkAXgBjAGgAbQByAHcAfACBAIYAiwCQAJUAmgCfAKQAqQCuALIAtwC8 +AMEAxgDLANAA1QDbAOAA5QDrAPAA9gD7AQEBBwENARMBGQEfASUBKwEyATgBPgFFAUwBUgFZAWABZwFu +AXUBfAGDAYsBkgGaAaEBqQGxAbkBwQHJAdEB2QHhAekB8gH6AgMCDAIUAh0CJgIvAjgCQQJLAlQCXQJn +AnECegKEAo4CmAKiAqwCtgLBAssC1QLgAusC9QMAAwsDFgMhAy0DOANDA08DWgNmA3IDfgOKA5YDogOu +A7oDxwPTA+AD7AP5BAYEEwQgBC0EOwRIBFUEYwRxBH4EjASaBKgEtgTEBNME4QTwBP4FDQUcBSsFOgVJ +BVgFZwV3BYYFlgWmBbUFxQXVBeUF9gYGBhYGJwY3BkgGWQZqBnsGjAadBq8GwAbRBuMG9QcHBxkHKwc9 +B08HYQd0B4YHmQesB78H0gflB/gICwgfCDIIRghaCG4IggiWCKoIvgjSCOcI+wkQCSUJOglPCWQJeQmP +CaQJugnPCeUJ+woRCicKPQpUCmoKgQqYCq4KxQrcCvMLCwsiCzkLUQtpC4ALmAuwC8gL4Qv5DBIMKgxD +DFwMdQyODKcMwAzZDPMNDQ0mDUANWg10DY4NqQ3DDd4N+A4TDi4OSQ5kDn8Omw62DtIO7g8JDyUPQQ9e +D3oPlg+zD88P7BAJECYQQxBhEH4QmxC5ENcQ9RETETERTxFtEYwRqhHJEegSBxImEkUSZBKEEqMSwxLj +EwMTIxNDE2MTgxOkE8UT5RQGFCcUSRRqFIsUrRTOFPAVEhU0FVYVeBWbFb0V4BYDFiYWSRZsFo8WshbW +FvoXHRdBF2UXiReuF9IX9xgbGEAYZRiKGK8Y1Rj6GSAZRRlrGZEZtxndGgQaKhpRGncanhrFGuwbFBs7 +G2MbihuyG9ocAhwqHFIcexyjHMwc9R0eHUcdcB2ZHcMd7B4WHkAeah6UHr4e6R8THz4faR+UH78f6iAV +IEEgbCCYIMQg8CEcIUghdSGhIc4h+yInIlUigiKvIt0jCiM4I2YjlCPCI/AkHyRNJHwkqyTaJQklOCVo +JZclxyX3JicmVyaHJrcm6CcYJ0kneierJ9woDSg/KHEooijUKQYpOClrKZ0p0CoCKjUqaCqbKs8rAis2 +K2krnSvRLAUsOSxuLKIs1y0MLUEtdi2rLeEuFi5MLoIuty7uLyQvWi+RL8cv/jA1MGwwpDDbMRIxSjGC +Mbox8jIqMmMymzLUMw0zRjN/M7gz8TQrNGU0njTYNRM1TTWHNcI1/TY3NnI2rjbpNyQ3YDecN9c4FDhQ +OIw4yDkFOUI5fzm8Ofk6Njp0OrI67zstO2s7qjvoPCc8ZTykPOM9Ij1hPaE94D4gPmA+oD7gPyE/YT+i +P+JAI0BkQKZA50EpQWpBrEHuQjBCckK1QvdDOkN9Q8BEA0RHRIpEzkUSRVVFmkXeRiJGZ0arRvBHNUd7 +R8BIBUhLSJFI10kdSWNJqUnwSjdKfUrESwxLU0uaS+JMKkxyTLpNAk1KTZNN3E4lTm5Ot08AT0lPk0/d +UCdQcVC7UQZRUFGbUeZSMVJ8UsdTE1NfU6pT9lRCVI9U21UoVXVVwlYPVlxWqVb3V0RXklfgWC9YfVjL +WRpZaVm4WgdaVlqmWvVbRVuVW+VcNVyGXNZdJ114XcleGl5sXr1fD19hX7NgBWBXYKpg/GFPYaJh9WJJ +Ypxi8GNDY5dj62RAZJRk6WU9ZZJl52Y9ZpJm6Gc9Z5Nn6Wg/aJZo7GlDaZpp8WpIap9q92tPa6dr/2xX +bK9tCG1gbbluEm5rbsRvHm94b9FwK3CGcOBxOnGVcfByS3KmcwFzXXO4dBR0cHTMdSh1hXXhdj52m3b4 +d1Z3s3gReG54zHkqeYl553pGeqV7BHtje8J8IXyBfOF9QX2hfgF+Yn7CfyN/hH/lgEeAqIEKgWuBzYIw +gpKC9INXg7qEHYSAhOOFR4Wrhg6GcobXhzuHn4gEiGmIzokziZmJ/opkisqLMIuWi/yMY4zKjTGNmI3/ +jmaOzo82j56QBpBukNaRP5GokhGSepLjk02TtpQglIqU9JVflcmWNJaflwqXdZfgmEyYuJkkmZCZ/Jpo +mtWbQpuvnByciZz3nWSd0p5Anq6fHZ+Ln/qgaaDYoUehtqImopajBqN2o+akVqTHpTilqaYapoum/adu +p+CoUqjEqTepqaocqo+rAqt1q+msXKzQrUStuK4trqGvFq+LsACwdbDqsWCx1rJLssKzOLOutCW0nLUT +tYq2AbZ5tvC3aLfguFm40blKucK6O7q1uy67p7whvJu9Fb2Pvgq+hL7/v3q/9cBwwOzBZ8Hjwl/C28NY +w9TEUcTOxUvFyMZGxsPHQce/yD3IvMk6ybnKOMq3yzbLtsw1zLXNNc21zjbOts83z7jQOdC60TzRvtI/ +0sHTRNPG1EnUy9VO1dHWVdbY11zX4Nhk2OjZbNnx2nba+9uA3AXcit0Q3ZbeHN6i3ynfr+A24L3hROHM +4lPi2+Nj4+vkc+T85YTmDeaW5x/nqegy6LzpRunQ6lvq5etw6/vshu0R7ZzuKO6070DvzPBY8OXxcvH/ +8ozzGfOn9DT0wvVQ9d72bfb794r4Gfio+Tj5x/pX+uf7d/wH/Jj9Kf26/kv+3P9t///SLS4vMFokY2xh +c3NuYW1lWCRjbGFzc2VzXxAQTlNCaXRtYXBJbWFnZVJlcKMvMTJaTlNJbWFnZVJlcFhOU09iamVjdNIt +LjQ1V05TQXJyYXmiNDLSLS43OF5OU011dGFibGVBcnJheaM3NDLTOjsOPD0+V05TV2hpdGVcTlNDb2xv +clNwYWNlRDAgMAAQA4AM0i0uQEFXTlNDb2xvcqJAMtItLkNEV05TSW1hZ2WiQzIACAARABoAJAApADIA +NwBJAEwAUQBTAGIAaAB3AH4AhQCSAJkApQCtALwAvgDAAMUAxwDJAMsAzQDUANkA5ADmAOgA6gDvAPIA +9AD2APgA/wEWATIBNAE2E6YTqxO2E78T0hPWE+ET6hPvE/cT+hP/FA4UEhQZFCEULhQzFDUUNxQ8FEQU +RxRMFFQAAAAAAAACAQAAAAAAAABFAAAAAAAAAAAAAAAAAAAUVw diff --git a/i18n/src/main/resources/it.lproj/Browser.xib b/i18n/src/main/resources/it.lproj/Browser.xib index 5522f35e9e..d8b69288d5 100644 --- a/i18n/src/main/resources/it.lproj/Browser.xib +++ b/i18n/src/main/resources/it.lproj/Browser.xib @@ -1,7 +1,7 @@ - + - + @@ -38,7 +38,7 @@ - + @@ -62,27 +62,27 @@ @@ -256,7 +256,7 @@ - + @@ -553,95 +553,95 @@ DQ YnBsaXN0MDDUAQIDBAUGBwpYJHZlcnNpb25ZJGFyY2hpdmVyVCR0b3BYJG9iamVjdHMSAAGGoF8QD05T -S2V5ZWRBcmNoaXZlctEICVRyb290gAGuCwwZGh8UJCkqMTQ3PUBVJG51bGzWDQ4PEBESExQVFhcYVk5T -U2l6ZV5OU1Jlc2l6aW5nTW9kZVYkY2xhc3NcTlNJbWFnZUZsYWdzVk5TUmVwc1dOU0NvbG9ygAIQAIAN -EiDDAACAA4ALVnsxLCAxfdIbDxweWk5TLm9iamVjdHOhHYAEgArSGw8gI6IhIoAFgAaACdMPJSYnKBRf -EBROU1RJRkZSZXByZXNlbnRhdGlvbl8QGU5TSW50ZXJuYWxMYXlvdXREaXJlY3Rpb26ACIAHTxESbE1N -ACoAAAAKAAAAEAEAAAMAAAABAAEAAAEBAAMAAAABAAEAAAECAAMAAAACAAgACAEDAAMAAAABAAEAAAEG -AAMAAAABAAEAAAEKAAMAAAABAAEAAAERAAQAAAABAAAACAESAAMAAAABAAEAAAEVAAMAAAABAAIAAAEW -AAMAAAABAAEAAAEXAAQAAAABAAAAAgEcAAMAAAABAAEAAAEoAAMAAAABAAIAAAFSAAMAAAABAAEAAAFT -AAMAAAACAAEAAYdzAAcAABGcAAAA0AAAAAAAABGcYXBwbAIAAABtbnRyR1JBWVhZWiAH3AAIABcADwAu -AA9hY3NwQVBQTAAAAABub25lAAAAAAAAAAAAAAAAAAAAAAAA9tYAAQAAAADTLWFwcGwAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAVkZXNjAAAAwAAAAHlkc2NtAAABPAAA -CBpjcHJ0AAAJWAAAACN3dHB0AAAJfAAAABRrVFJDAAAJkAAACAxkZXNjAAAAAAAAAB9HZW5lcmljIEdy -YXkgR2FtbWEgMi4yIFByb2ZpbGUAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAbWx1YwAAAAAAAAAfAAAADHNr -U0sAAAAuAAABhGRhREsAAAA6AAABsmNhRVMAAAA4AAAB7HZpVk4AAABAAAACJHB0QlIAAABKAAACZHVr -VUEAAAAsAAACrmZyRlUAAAA+AAAC2mh1SFUAAAA0AAADGHpoVFcAAAAaAAADTGtvS1IAAAAiAAADZm5i -Tk8AAAA6AAADiGNzQ1oAAAAoAAADwmhlSUwAAAAkAAAD6nJvUk8AAAAqAAAEDmRlREUAAABOAAAEOGl0 -SVQAAABOAAAEhnN2U0UAAAA4AAAE1HpoQ04AAAAaAAAFDGphSlAAAAAmAAAFJmVsR1IAAAAqAAAFTHB0 -UE8AAABSAAAFdm5sTkwAAABAAAAFyGVzRVMAAABMAAAGCHRoVEgAAAAyAAAGVHRyVFIAAAAkAAAGhmZp -RkkAAABGAAAGqmhySFIAAAA+AAAG8HBsUEwAAABKAAAHLmFyRUcAAAAsAAAHeHJ1UlUAAAA6AAAHpGVu -VVMAAAA8AAAH3gBWAWEAZQBvAGIAZQBjAG4A4QAgAHMAaQB2AOEAIABnAGEAbQBhACAAMgAsADIARwBl -AG4AZQByAGkAcwBrACAAZwByAOUAIAAyACwAMgAgAGcAYQBtAG0AYQAtAHAAcgBvAGYAaQBsAEcAYQBt -AG0AYQAgAGQAZQAgAGcAcgBpAHMAbwBzACAAZwBlAG4A6AByAGkAYwBhACAAMgAuADIAQx6lAHUAIABo -AOwAbgBoACAATQDgAHUAIAB4AOEAbQAgAEMAaAB1AG4AZwAgAEcAYQBtAG0AYQAgADIALgAyAFAAZQBy -AGYAaQBsACAARwBlAG4A6QByAGkAYwBvACAAZABhACAARwBhAG0AYQAgAGQAZQAgAEMAaQBuAHoAYQBz -ACAAMgAsADIEFwQwBDMEMAQ7BEwEPQQwACAARwByAGEAeQAtBDMEMAQ8BDAAIAAyAC4AMgBQAHIAbwBm -AGkAbAAgAGcA6QBuAOkAcgBpAHEAdQBlACAAZwByAGkAcwAgAGcAYQBtAG0AYQAgADIALAAyAMEAbAB0 -AGEAbADhAG4AbwBzACAAcwB6APwAcgBrAGUAIABnAGEAbQBtAGEAIAAyAC4AMpAadShwcJaOUUlepgAy -AC4AMoJyX2ljz4/wx3y8GAAg1ozAyQAgrBC5yAAgADIALgAyACDVBLhc0wzHfABHAGUAbgBlAHIAaQBz -AGsAIABnAHIA5QAgAGcAYQBtAG0AYQAgADIALAAyAC0AcAByAG8AZgBpAGwATwBiAGUAYwBuAOEAIAFh -AGUAZADhACAAZwBhAG0AYQAgADIALgAyBdIF0AXeBdQAIAXQBeQF1QXoACAF2wXcBdwF2QAgADIALgAy -AEcAYQBtAGEAIABnAHIAaQAgAGcAZQBuAGUAcgBpAGMBAwAgADIALAAyAEEAbABsAGcAZQBtAGUAaQBu -AGUAcwAgAEcAcgBhAHUAcwB0AHUAZgBlAG4ALQBQAHIAbwBmAGkAbAAgAEcAYQBtAG0AYQAgADIALAAy -AFAAcgBvAGYAaQBsAG8AIABnAHIAaQBnAGkAbwAgAGcAZQBuAGUAcgBpAGMAbwAgAGQAZQBsAGwAYQAg -AGcAYQBtAG0AYQAgADIALAAyAEcAZQBuAGUAcgBpAHMAawAgAGcAcgDlACAAMgAsADIAIABnAGEAbQBt -AGEAcAByAG8AZgBpAGxmbpAacHBepnz7ZXAAMgAuADJjz4/wZYdO9k4AgiwwsDDsMKQwrDDzMN4AIAAy -AC4AMgAgMNcw7TDVMKEwpDDrA5MDtQO9A7kDugPMACADkwO6A8EDuQAgA5MDrAO8A7wDsQAgADIALgAy -AFAAZQByAGYAaQBsACAAZwBlAG4A6QByAGkAYwBvACAAZABlACAAYwBpAG4AegBlAG4AdABvAHMAIABk -AGEAIABHAGEAbQBtAGEAIAAyACwAMgBBAGwAZwBlAG0AZQBlAG4AIABnAHIAaQBqAHMAIABnAGEAbQBt -AGEAIAAyACwAMgAtAHAAcgBvAGYAaQBlAGwAUABlAHIAZgBpAGwAIABnAGUAbgDpAHIAaQBjAG8AIABk -AGUAIABnAGEAbQBtAGEAIABkAGUAIABnAHIAaQBzAGUAcwAgADIALAAyDiMOMQ4HDioONQ5BDgEOIQ4h -DjIOQA4BDiMOIg5MDhcOMQ5IDicORA4bACAAMgAuADIARwBlAG4AZQBsACAARwByAGkAIABHAGEAbQBh -ACAAMgAsADIAWQBsAGUAaQBuAGUAbgAgAGgAYQByAG0AYQBhAG4AIABnAGEAbQBtAGEAIAAyACwAMgAg -AC0AcAByAG8AZgBpAGkAbABpAEcAZQBuAGUAcgBpAQ0AawBpACAARwByAGEAeQAgAEcAYQBtAG0AYQAg -ADIALgAyACAAcAByAG8AZgBpAGwAVQBuAGkAdwBlAHIAcwBhAGwAbgB5ACAAcAByAG8AZgBpAGwAIABz -AHoAYQByAG8BWwBjAGkAIABnAGEAbQBtAGEAIAAyACwAMgY6BicGRQYnACAAMgAuADIAIAZEBkgGRgAg -BjEGRQYnBi8GSgAgBjkGJwZFBB4EMQRJBDAETwAgBEEENQRABDAETwAgBDMEMAQ8BDwEMAAgADIALAAy -AC0EPwRABD4ERAQ4BDsETABHAGUAbgBlAHIAaQBjACAARwByAGEAeQAgAEcAYQBtAG0AYQAgADIALgAy -ACAAUAByAG8AZgBpAGwAZQAAdGV4dAAAAABDb3B5cmlnaHQgQXBwbGUgSW5jLiwgMjAxMgAAWFlaIAAA -AAAAAPNRAAEAAAABFsxjdXJ2AAAAAAAABAAAAAAFAAoADwAUABkAHgAjACgALQAyADcAOwBAAEUASgBP -AFQAWQBeAGMAaABtAHIAdwB8AIEAhgCLAJAAlQCaAJ8ApACpAK4AsgC3ALwAwQDGAMsA0ADVANsA4ADl -AOsA8AD2APsBAQEHAQ0BEwEZAR8BJQErATIBOAE+AUUBTAFSAVkBYAFnAW4BdQF8AYMBiwGSAZoBoQGp -AbEBuQHBAckB0QHZAeEB6QHyAfoCAwIMAhQCHQImAi8COAJBAksCVAJdAmcCcQJ6AoQCjgKYAqICrAK2 -AsECywLVAuAC6wL1AwADCwMWAyEDLQM4A0MDTwNaA2YDcgN+A4oDlgOiA64DugPHA9MD4APsA/kEBgQT -BCAELQQ7BEgEVQRjBHEEfgSMBJoEqAS2BMQE0wThBPAE/gUNBRwFKwU6BUkFWAVnBXcFhgWWBaYFtQXF -BdUF5QX2BgYGFgYnBjcGSAZZBmoGewaMBp0GrwbABtEG4wb1BwcHGQcrBz0HTwdhB3QHhgeZB6wHvwfS -B+UH+AgLCB8IMghGCFoIbgiCCJYIqgi+CNII5wj7CRAJJQk6CU8JZAl5CY8JpAm6Cc8J5Qn7ChEKJwo9 -ClQKagqBCpgKrgrFCtwK8wsLCyILOQtRC2kLgAuYC7ALyAvhC/kMEgwqDEMMXAx1DI4MpwzADNkM8w0N -DSYNQA1aDXQNjg2pDcMN3g34DhMOLg5JDmQOfw6bDrYO0g7uDwkPJQ9BD14Peg+WD7MPzw/sEAkQJhBD -EGEQfhCbELkQ1xD1ERMRMRFPEW0RjBGqEckR6BIHEiYSRRJkEoQSoxLDEuMTAxMjE0MTYxODE6QTxRPl -FAYUJxRJFGoUixStFM4U8BUSFTQVVhV4FZsVvRXgFgMWJhZJFmwWjxayFtYW+hcdF0EXZReJF64X0hf3 -GBsYQBhlGIoYrxjVGPoZIBlFGWsZkRm3Gd0aBBoqGlEadxqeGsUa7BsUGzsbYxuKG7Ib2hwCHCocUhx7 -HKMczBz1HR4dRx1wHZkdwx3sHhYeQB5qHpQevh7pHxMfPh9pH5Qfvx/qIBUgQSBsIJggxCDwIRwhSCF1 -IaEhziH7IiciVSKCIq8i3SMKIzgjZiOUI8Ij8CQfJE0kfCSrJNolCSU4JWgllyXHJfcmJyZXJocmtybo -JxgnSSd6J6sn3CgNKD8ocSiiKNQpBik4KWspnSnQKgIqNSpoKpsqzysCKzYraSudK9EsBSw5LG4soizX -LQwtQS12Last4S4WLkwugi63Lu4vJC9aL5Evxy/+MDUwbDCkMNsxEjFKMYIxujHyMioyYzKbMtQzDTNG -M38zuDPxNCs0ZTSeNNg1EzVNNYc1wjX9Njc2cjauNuk3JDdgN5w31zgUOFA4jDjIOQU5Qjl/Obw5+To2 -OnQ6sjrvOy07azuqO+g8JzxlPKQ84z0iPWE9oT3gPiA+YD6gPuA/IT9hP6I/4kAjQGRApkDnQSlBakGs -Qe5CMEJyQrVC90M6Q31DwEQDREdEikTORRJFVUWaRd5GIkZnRqtG8Ec1R3tHwEgFSEtIkUjXSR1JY0mp -SfBKN0p9SsRLDEtTS5pL4kwqTHJMuk0CTUpNk03cTiVObk63TwBPSU+TT91QJ1BxULtRBlFQUZtR5lIx -UnxSx1MTU19TqlP2VEJUj1TbVShVdVXCVg9WXFapVvdXRFeSV+BYL1h9WMtZGllpWbhaB1pWWqZa9VtF -W5Vb5Vw1XIZc1l0nXXhdyV4aXmxevV8PX2Ffs2AFYFdgqmD8YU9homH1YklinGLwY0Njl2PrZEBklGTp -ZT1lkmXnZj1mkmboZz1nk2fpaD9olmjsaUNpmmnxakhqn2r3a09rp2v/bFdsr20IbWBtuW4SbmtuxG8e -b3hv0XArcIZw4HE6cZVx8HJLcqZzAXNdc7h0FHRwdMx1KHWFdeF2Pnabdvh3VnezeBF4bnjMeSp5iXnn -ekZ6pXsEe2N7wnwhfIF84X1BfaF+AX5ifsJ/I3+Ef+WAR4CogQqBa4HNgjCCkoL0g1eDuoQdhICE44VH -hauGDoZyhteHO4efiASIaYjOiTOJmYn+imSKyoswi5aL/IxjjMqNMY2Yjf+OZo7OjzaPnpAGkG6Q1pE/ -kaiSEZJ6kuOTTZO2lCCUipT0lV+VyZY0lp+XCpd1l+CYTJi4mSSZkJn8mmia1ZtCm6+cHJyJnPedZJ3S -nkCerp8dn4uf+qBpoNihR6G2oiailqMGo3aj5qRWpMelOKWpphqmi6b9p26n4KhSqMSpN6mpqhyqj6sC -q3Wr6axcrNCtRK24ri2uoa8Wr4uwALB1sOqxYLHWskuywrM4s660JbSctRO1irYBtnm28Ldot+C4WbjR -uUq5wro7urW7LrunvCG8m70VvY++Cr6Evv+/er/1wHDA7MFnwePCX8Lbw1jD1MRRxM7FS8XIxkbGw8dB -x7/IPci8yTrJuco4yrfLNsu2zDXMtc01zbXONs62zzfPuNA50LrRPNG+0j/SwdNE08bUSdTL1U7V0dZV -1tjXXNfg2GTY6Nls2fHadtr724DcBdyK3RDdlt4c3qLfKd+v4DbgveFE4cziU+Lb42Pj6+Rz5PzlhOYN -5pbnH+ep6DLovOlG6dDqW+rl63Dr++yG7RHtnO4o7rTvQO/M8Fjw5fFy8f/yjPMZ86f0NPTC9VD13vZt -9vv3ivgZ+Kj5OPnH+lf65/t3/Af8mP0p/br+S/7c/23//9IrLC0uWiRjbGFzc25hbWVYJGNsYXNzZXNf -EBBOU0JpdG1hcEltYWdlUmVwoy0vMFpOU0ltYWdlUmVwWE5TT2JqZWN00issMjNXTlNBcnJheaIyMNIr -LDU2Xk5TTXV0YWJsZUFycmF5ozUyMNM4OQ86OzxXTlNXaGl0ZVxOU0NvbG9yU3BhY2VEMCAwABADgAzS -Kyw+P1dOU0NvbG9yoj4w0issQUJXTlNJbWFnZaJBMAAIABEAGgAkACkAMgA3AEkATABRAFMAYgBoAHUA -fACLAJIAnwCmAK4AsACyALQAuQC7AL0AxADJANQA1gDYANoA3wDiAOQA5gDoAO8BBgEiASQBJhOWE5sT -phOvE8ITxhPRE9oT3xPnE+oT7xP+FAIUCRQRFB4UIxQlFCcULBQ0FDcUPBREAAAAAAAAAgEAAAAAAAAA -QwAAAAAAAAAAAAAAAAAAFEc +S2V5ZWRBcmNoaXZlctEICVRyb290gAGuCwwbHCEaJissMzY5P0JVJG51bGzXDQ4PEBESExQVFhcYGRpW +TlNTaXplViRjbGFzc1xOU0ltYWdlRmxhZ3NWTlNSZXBzW05TVGludENvbG9yV05TQ29sb3JeTlNSZXNp +emluZ01vZGWAAoANEiDDAACAA4AAgAsQAFZ7MSwgMX3SHQ4eIFpOUy5vYmplY3RzoR+ABIAK0h0OIiWi +IySABYAGgAnTDicoKSoaXxAUTlNUSUZGUmVwcmVzZW50YXRpb25fEBlOU0ludGVybmFsTGF5b3V0RGly +ZWN0aW9ugAiAB08REmxNTQAqAAAACgAAABABAAADAAAAAQABAAABAQADAAAAAQABAAABAgADAAAAAgAI +AAgBAwADAAAAAQABAAABBgADAAAAAQABAAABCgADAAAAAQABAAABEQAEAAAAAQAAAAgBEgADAAAAAQAB +AAABFQADAAAAAQACAAABFgADAAAAAQABAAABFwAEAAAAAQAAAAIBHAADAAAAAQABAAABKAADAAAAAQAC +AAABUgADAAAAAQABAAABUwADAAAAAgABAAGHcwAHAAARnAAAANAAAAAAAAARnGFwcGwCAAAAbW50ckdS +QVlYWVogB9wACAAXAA8ALgAPYWNzcEFQUEwAAAAAbm9uZQAAAAAAAAAAAAAAAAAAAAAAAPbWAAEAAAAA +0y1hcHBsAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFZGVzYwAA +AMAAAAB5ZHNjbQAAATwAAAgaY3BydAAACVgAAAAjd3RwdAAACXwAAAAUa1RSQwAACZAAAAgMZGVzYwAA +AAAAAAAfR2VuZXJpYyBHcmF5IEdhbW1hIDIuMiBQcm9maWxlAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAG1s +dWMAAAAAAAAAHwAAAAxza1NLAAAALgAAAYRkYURLAAAAOgAAAbJjYUVTAAAAOAAAAex2aVZOAAAAQAAA +AiRwdEJSAAAASgAAAmR1a1VBAAAALAAAAq5mckZVAAAAPgAAAtpodUhVAAAANAAAAxh6aFRXAAAAGgAA +A0xrb0tSAAAAIgAAA2ZuYk5PAAAAOgAAA4hjc0NaAAAAKAAAA8JoZUlMAAAAJAAAA+pyb1JPAAAAKgAA +BA5kZURFAAAATgAABDhpdElUAAAATgAABIZzdlNFAAAAOAAABNR6aENOAAAAGgAABQxqYUpQAAAAJgAA +BSZlbEdSAAAAKgAABUxwdFBPAAAAUgAABXZubE5MAAAAQAAABchlc0VTAAAATAAABgh0aFRIAAAAMgAA +BlR0clRSAAAAJAAABoZmaUZJAAAARgAABqpockhSAAAAPgAABvBwbFBMAAAASgAABy5hckVHAAAALAAA +B3hydVJVAAAAOgAAB6RlblVTAAAAPAAAB94AVgFhAGUAbwBiAGUAYwBuAOEAIABzAGkAdgDhACAAZwBh +AG0AYQAgADIALAAyAEcAZQBuAGUAcgBpAHMAawAgAGcAcgDlACAAMgAsADIAIABnAGEAbQBtAGEALQBw +AHIAbwBmAGkAbABHAGEAbQBtAGEAIABkAGUAIABnAHIAaQBzAG8AcwAgAGcAZQBuAOgAcgBpAGMAYQAg +ADIALgAyAEMepQB1ACAAaADsAG4AaAAgAE0A4AB1ACAAeADhAG0AIABDAGgAdQBuAGcAIABHAGEAbQBt +AGEAIAAyAC4AMgBQAGUAcgBmAGkAbAAgAEcAZQBuAOkAcgBpAGMAbwAgAGQAYQAgAEcAYQBtAGEAIABk +AGUAIABDAGkAbgB6AGEAcwAgADIALAAyBBcEMAQzBDAEOwRMBD0EMAAgAEcAcgBhAHkALQQzBDAEPAQw +ACAAMgAuADIAUAByAG8AZgBpAGwAIABnAOkAbgDpAHIAaQBxAHUAZQAgAGcAcgBpAHMAIABnAGEAbQBt +AGEAIAAyACwAMgDBAGwAdABhAGwA4QBuAG8AcwAgAHMAegD8AHIAawBlACAAZwBhAG0AbQBhACAAMgAu +ADKQGnUocHCWjlFJXqYAMgAuADKCcl9pY8+P8Md8vBgAINaMwMkAIKwQucgAIAAyAC4AMgAg1QS4XNMM +x3wARwBlAG4AZQByAGkAcwBrACAAZwByAOUAIABnAGEAbQBtAGEAIAAyACwAMgAtAHAAcgBvAGYAaQBs +AE8AYgBlAGMAbgDhACABYQBlAGQA4QAgAGcAYQBtAGEAIAAyAC4AMgXSBdAF3gXUACAF0AXkBdUF6AAg +BdsF3AXcBdkAIAAyAC4AMgBHAGEAbQBhACAAZwByAGkAIABnAGUAbgBlAHIAaQBjAQMAIAAyACwAMgBB +AGwAbABnAGUAbQBlAGkAbgBlAHMAIABHAHIAYQB1AHMAdAB1AGYAZQBuAC0AUAByAG8AZgBpAGwAIABH +AGEAbQBtAGEAIAAyACwAMgBQAHIAbwBmAGkAbABvACAAZwByAGkAZwBpAG8AIABnAGUAbgBlAHIAaQBj +AG8AIABkAGUAbABsAGEAIABnAGEAbQBtAGEAIAAyACwAMgBHAGUAbgBlAHIAaQBzAGsAIABnAHIA5QAg +ADIALAAyACAAZwBhAG0AbQBhAHAAcgBvAGYAaQBsZm6QGnBwXqZ8+2VwADIALgAyY8+P8GWHTvZOAIIs +MLAw7DCkMKww8zDeACAAMgAuADIAIDDXMO0w1TChMKQw6wOTA7UDvQO5A7oDzAAgA5MDugPBA7kAIAOT +A6wDvAO8A7EAIAAyAC4AMgBQAGUAcgBmAGkAbAAgAGcAZQBuAOkAcgBpAGMAbwAgAGQAZQAgAGMAaQBu +AHoAZQBuAHQAbwBzACAAZABhACAARwBhAG0AbQBhACAAMgAsADIAQQBsAGcAZQBtAGUAZQBuACAAZwBy +AGkAagBzACAAZwBhAG0AbQBhACAAMgAsADIALQBwAHIAbwBmAGkAZQBsAFAAZQByAGYAaQBsACAAZwBl +AG4A6QByAGkAYwBvACAAZABlACAAZwBhAG0AbQBhACAAZABlACAAZwByAGkAcwBlAHMAIAAyACwAMg4j +DjEOBw4qDjUOQQ4BDiEOIQ4yDkAOAQ4jDiIOTA4XDjEOSA4nDkQOGwAgADIALgAyAEcAZQBuAGUAbAAg +AEcAcgBpACAARwBhAG0AYQAgADIALAAyAFkAbABlAGkAbgBlAG4AIABoAGEAcgBtAGEAYQBuACAAZwBh +AG0AbQBhACAAMgAsADIAIAAtAHAAcgBvAGYAaQBpAGwAaQBHAGUAbgBlAHIAaQENAGsAaQAgAEcAcgBh +AHkAIABHAGEAbQBtAGEAIAAyAC4AMgAgAHAAcgBvAGYAaQBsAFUAbgBpAHcAZQByAHMAYQBsAG4AeQAg +AHAAcgBvAGYAaQBsACAAcwB6AGEAcgBvAVsAYwBpACAAZwBhAG0AbQBhACAAMgAsADIGOgYnBkUGJwAg +ADIALgAyACAGRAZIBkYAIAYxBkUGJwYvBkoAIAY5BicGRQQeBDEESQQwBE8AIARBBDUEQAQwBE8AIAQz +BDAEPAQ8BDAAIAAyACwAMgAtBD8EQAQ+BEQEOAQ7BEwARwBlAG4AZQByAGkAYwAgAEcAcgBhAHkAIABH +AGEAbQBtAGEAIAAyAC4AMgAgAFAAcgBvAGYAaQBsAGUAAHRleHQAAAAAQ29weXJpZ2h0IEFwcGxlIElu +Yy4sIDIwMTIAAFhZWiAAAAAAAADzUQABAAAAARbMY3VydgAAAAAAAAQAAAAABQAKAA8AFAAZAB4AIwAo +AC0AMgA3ADsAQABFAEoATwBUAFkAXgBjAGgAbQByAHcAfACBAIYAiwCQAJUAmgCfAKQAqQCuALIAtwC8 +AMEAxgDLANAA1QDbAOAA5QDrAPAA9gD7AQEBBwENARMBGQEfASUBKwEyATgBPgFFAUwBUgFZAWABZwFu +AXUBfAGDAYsBkgGaAaEBqQGxAbkBwQHJAdEB2QHhAekB8gH6AgMCDAIUAh0CJgIvAjgCQQJLAlQCXQJn +AnECegKEAo4CmAKiAqwCtgLBAssC1QLgAusC9QMAAwsDFgMhAy0DOANDA08DWgNmA3IDfgOKA5YDogOu +A7oDxwPTA+AD7AP5BAYEEwQgBC0EOwRIBFUEYwRxBH4EjASaBKgEtgTEBNME4QTwBP4FDQUcBSsFOgVJ +BVgFZwV3BYYFlgWmBbUFxQXVBeUF9gYGBhYGJwY3BkgGWQZqBnsGjAadBq8GwAbRBuMG9QcHBxkHKwc9 +B08HYQd0B4YHmQesB78H0gflB/gICwgfCDIIRghaCG4IggiWCKoIvgjSCOcI+wkQCSUJOglPCWQJeQmP +CaQJugnPCeUJ+woRCicKPQpUCmoKgQqYCq4KxQrcCvMLCwsiCzkLUQtpC4ALmAuwC8gL4Qv5DBIMKgxD +DFwMdQyODKcMwAzZDPMNDQ0mDUANWg10DY4NqQ3DDd4N+A4TDi4OSQ5kDn8Omw62DtIO7g8JDyUPQQ9e +D3oPlg+zD88P7BAJECYQQxBhEH4QmxC5ENcQ9RETETERTxFtEYwRqhHJEegSBxImEkUSZBKEEqMSwxLj +EwMTIxNDE2MTgxOkE8UT5RQGFCcUSRRqFIsUrRTOFPAVEhU0FVYVeBWbFb0V4BYDFiYWSRZsFo8WshbW +FvoXHRdBF2UXiReuF9IX9xgbGEAYZRiKGK8Y1Rj6GSAZRRlrGZEZtxndGgQaKhpRGncanhrFGuwbFBs7 +G2MbihuyG9ocAhwqHFIcexyjHMwc9R0eHUcdcB2ZHcMd7B4WHkAeah6UHr4e6R8THz4faR+UH78f6iAV +IEEgbCCYIMQg8CEcIUghdSGhIc4h+yInIlUigiKvIt0jCiM4I2YjlCPCI/AkHyRNJHwkqyTaJQklOCVo +JZclxyX3JicmVyaHJrcm6CcYJ0kneierJ9woDSg/KHEooijUKQYpOClrKZ0p0CoCKjUqaCqbKs8rAis2 +K2krnSvRLAUsOSxuLKIs1y0MLUEtdi2rLeEuFi5MLoIuty7uLyQvWi+RL8cv/jA1MGwwpDDbMRIxSjGC +Mbox8jIqMmMymzLUMw0zRjN/M7gz8TQrNGU0njTYNRM1TTWHNcI1/TY3NnI2rjbpNyQ3YDecN9c4FDhQ +OIw4yDkFOUI5fzm8Ofk6Njp0OrI67zstO2s7qjvoPCc8ZTykPOM9Ij1hPaE94D4gPmA+oD7gPyE/YT+i +P+JAI0BkQKZA50EpQWpBrEHuQjBCckK1QvdDOkN9Q8BEA0RHRIpEzkUSRVVFmkXeRiJGZ0arRvBHNUd7 +R8BIBUhLSJFI10kdSWNJqUnwSjdKfUrESwxLU0uaS+JMKkxyTLpNAk1KTZNN3E4lTm5Ot08AT0lPk0/d +UCdQcVC7UQZRUFGbUeZSMVJ8UsdTE1NfU6pT9lRCVI9U21UoVXVVwlYPVlxWqVb3V0RXklfgWC9YfVjL +WRpZaVm4WgdaVlqmWvVbRVuVW+VcNVyGXNZdJ114XcleGl5sXr1fD19hX7NgBWBXYKpg/GFPYaJh9WJJ +Ypxi8GNDY5dj62RAZJRk6WU9ZZJl52Y9ZpJm6Gc9Z5Nn6Wg/aJZo7GlDaZpp8WpIap9q92tPa6dr/2xX +bK9tCG1gbbluEm5rbsRvHm94b9FwK3CGcOBxOnGVcfByS3KmcwFzXXO4dBR0cHTMdSh1hXXhdj52m3b4 +d1Z3s3gReG54zHkqeYl553pGeqV7BHtje8J8IXyBfOF9QX2hfgF+Yn7CfyN/hH/lgEeAqIEKgWuBzYIw +gpKC9INXg7qEHYSAhOOFR4Wrhg6GcobXhzuHn4gEiGmIzokziZmJ/opkisqLMIuWi/yMY4zKjTGNmI3/ +jmaOzo82j56QBpBukNaRP5GokhGSepLjk02TtpQglIqU9JVflcmWNJaflwqXdZfgmEyYuJkkmZCZ/Jpo +mtWbQpuvnByciZz3nWSd0p5Anq6fHZ+Ln/qgaaDYoUehtqImopajBqN2o+akVqTHpTilqaYapoum/adu +p+CoUqjEqTepqaocqo+rAqt1q+msXKzQrUStuK4trqGvFq+LsACwdbDqsWCx1rJLssKzOLOutCW0nLUT +tYq2AbZ5tvC3aLfguFm40blKucK6O7q1uy67p7whvJu9Fb2Pvgq+hL7/v3q/9cBwwOzBZ8Hjwl/C28NY +w9TEUcTOxUvFyMZGxsPHQce/yD3IvMk6ybnKOMq3yzbLtsw1zLXNNc21zjbOts83z7jQOdC60TzRvtI/ +0sHTRNPG1EnUy9VO1dHWVdbY11zX4Nhk2OjZbNnx2nba+9uA3AXcit0Q3ZbeHN6i3ynfr+A24L3hROHM +4lPi2+Nj4+vkc+T85YTmDeaW5x/nqegy6LzpRunQ6lvq5etw6/vshu0R7ZzuKO6070DvzPBY8OXxcvH/ +8ozzGfOn9DT0wvVQ9d72bfb794r4Gfio+Tj5x/pX+uf7d/wH/Jj9Kf26/kv+3P9t///SLS4vMFokY2xh +c3NuYW1lWCRjbGFzc2VzXxAQTlNCaXRtYXBJbWFnZVJlcKMvMTJaTlNJbWFnZVJlcFhOU09iamVjdNIt +LjQ1V05TQXJyYXmiNDLSLS43OF5OU011dGFibGVBcnJheaM3NDLTOjsOPD0+V05TV2hpdGVcTlNDb2xv +clNwYWNlRDAgMAAQA4AM0i0uQEFXTlNDb2xvcqJAMtItLkNEV05TSW1hZ2WiQzIACAARABoAJAApADIA +NwBJAEwAUQBTAGIAaAB3AH4AhQCSAJkApQCtALwAvgDAAMUAxwDJAMsAzQDUANkA5ADmAOgA6gDvAPIA +9AD2APgA/wEWATIBNAE2E6YTqxO2E78T0hPWE+ET6hPvE/cT+hP/FA4UEhQZFCEULhQzFDUUNxQ8FEQU +RxRMFFQAAAAAAAACAQAAAAAAAABFAAAAAAAAAAAAAAAAAAAUVw diff --git a/i18n/src/main/resources/it.lproj/Command.xib b/i18n/src/main/resources/it.lproj/Command.xib index 436a5172b4..1d7defde09 100644 --- a/i18n/src/main/resources/it.lproj/Command.xib +++ b/i18n/src/main/resources/it.lproj/Command.xib @@ -1,7 +1,7 @@ - + - + diff --git a/i18n/src/main/resources/it.lproj/Connection.xib b/i18n/src/main/resources/it.lproj/Connection.xib index 8d3f3547bb..0d0576d27f 100644 --- a/i18n/src/main/resources/it.lproj/Connection.xib +++ b/i18n/src/main/resources/it.lproj/Connection.xib @@ -1,7 +1,7 @@ - + - + @@ -76,7 +76,7 @@ - + diff --git a/i18n/src/main/resources/it.lproj/Info.xib b/i18n/src/main/resources/it.lproj/Info.xib index 797167f5ae..8e684531d3 100644 --- a/i18n/src/main/resources/it.lproj/Info.xib +++ b/i18n/src/main/resources/it.lproj/Info.xib @@ -1,7 +1,7 @@ - + - + @@ -89,7 +89,7 @@ - + @@ -1017,21 +1017,21 @@ - + - + - + @@ -1121,21 +1121,21 @@ - + - + - + @@ -1250,27 +1250,27 @@ diff --git a/i18n/src/main/resources/it.lproj/Login.strings b/i18n/src/main/resources/it.lproj/Login.strings index fe4ecc11bd..6f25a4dcdc 100644 Binary files a/i18n/src/main/resources/it.lproj/Login.strings and b/i18n/src/main/resources/it.lproj/Login.strings differ diff --git a/i18n/src/main/resources/it.lproj/Main.xib b/i18n/src/main/resources/it.lproj/Main.xib index e7b1605d85..e5c2ed1325 100644 --- a/i18n/src/main/resources/it.lproj/Main.xib +++ b/i18n/src/main/resources/it.lproj/Main.xib @@ -1,7 +1,7 @@ - + - + @@ -159,19 +159,19 @@ - + - + - + @@ -321,12 +321,12 @@ CA - + - + @@ -566,6 +566,10 @@ CA - + + + + + diff --git a/i18n/src/main/resources/it.lproj/Preferences.xib b/i18n/src/main/resources/it.lproj/Preferences.xib index 4d85252b85..85d3b6f8ce 100644 --- a/i18n/src/main/resources/it.lproj/Preferences.xib +++ b/i18n/src/main/resources/it.lproj/Preferences.xib @@ -1,7 +1,7 @@ - + - + @@ -266,7 +266,7 @@ - + @@ -307,7 +307,7 @@ - + @@ -538,7 +538,7 @@ - + @@ -578,7 +578,7 @@ - + @@ -613,7 +613,7 @@ - + @@ -642,7 +642,7 @@ - + @@ -1218,7 +1218,7 @@ - + @@ -1250,7 +1250,7 @@ - + @@ -1291,7 +1291,7 @@ Questo dovrebbe corrispondere alla codifica testo suata nel server. - + @@ -1364,7 +1364,7 @@ Questo dovrebbe corrispondere alla codifica testo suata nel server. - + @@ -1559,7 +1559,7 @@ Questo dovrebbe corrispondere alla codifica testo suata nel server. - + @@ -1619,7 +1619,7 @@ Questo dovrebbe corrispondere alla codifica testo suata nel server. - + @@ -1644,7 +1644,7 @@ Questo dovrebbe corrispondere alla codifica testo suata nel server. - + @@ -1686,7 +1686,7 @@ Questo dovrebbe corrispondere alla codifica testo suata nel server. - + @@ -1714,7 +1714,7 @@ Questo dovrebbe corrispondere alla codifica testo suata nel server. - + @@ -1742,7 +1742,7 @@ Questo dovrebbe corrispondere alla codifica testo suata nel server. - + @@ -1771,7 +1771,7 @@ Questo dovrebbe corrispondere alla codifica testo suata nel server. - + @@ -1806,7 +1806,7 @@ Questo dovrebbe corrispondere alla codifica testo suata nel server. - + @@ -1891,7 +1891,7 @@ Questo dovrebbe corrispondere alla codifica testo suata nel server. - + @@ -1950,7 +1950,7 @@ Questo dovrebbe corrispondere alla codifica testo suata nel server. - + @@ -1978,7 +1978,7 @@ Questo dovrebbe corrispondere alla codifica testo suata nel server. - + @@ -2007,7 +2007,7 @@ Questo dovrebbe corrispondere alla codifica testo suata nel server. - + diff --git a/i18n/src/main/resources/it.lproj/Profiles.xib b/i18n/src/main/resources/it.lproj/Profiles.xib index f249734cd7..4331e622fd 100644 --- a/i18n/src/main/resources/it.lproj/Profiles.xib +++ b/i18n/src/main/resources/it.lproj/Profiles.xib @@ -1,7 +1,7 @@ - + - + diff --git a/i18n/src/main/resources/it.lproj/Prompt.xib b/i18n/src/main/resources/it.lproj/Prompt.xib index 7efe328c7e..8f2c8d217a 100644 --- a/i18n/src/main/resources/it.lproj/Prompt.xib +++ b/i18n/src/main/resources/it.lproj/Prompt.xib @@ -1,7 +1,7 @@ - + - + @@ -95,7 +95,7 @@ Gw - + diff --git a/i18n/src/main/resources/it.lproj/S3.strings b/i18n/src/main/resources/it.lproj/S3.strings index 14a4fe39b1..80fbb6d135 100644 Binary files a/i18n/src/main/resources/it.lproj/S3.strings and b/i18n/src/main/resources/it.lproj/S3.strings differ diff --git a/i18n/src/main/resources/it.lproj/Updater.strings b/i18n/src/main/resources/it.lproj/Updater.strings index e14b073792..4c3b808771 100644 Binary files a/i18n/src/main/resources/it.lproj/Updater.strings and b/i18n/src/main/resources/it.lproj/Updater.strings differ diff --git a/i18n/src/main/resources/ja.lproj/Bookmark.xib b/i18n/src/main/resources/ja.lproj/Bookmark.xib index d8bea93eb8..efbce95ee0 100644 --- a/i18n/src/main/resources/ja.lproj/Bookmark.xib +++ b/i18n/src/main/resources/ja.lproj/Bookmark.xib @@ -1,7 +1,7 @@ - + - + @@ -92,7 +92,7 @@ - + @@ -489,95 +489,95 @@ YnBsaXN0MDDUAQIDBAUGBwpYJHZlcnNpb25ZJGFyY2hpdmVyVCR0b3BYJG9iamVjdHMSAAGGoF8QD05T -S2V5ZWRBcmNoaXZlctEICVRyb290gAGuCwwZGh8UJCkqMTQ3PUBVJG51bGzWDQ4PEBESExQVFhcYVk5T -U2l6ZV5OU1Jlc2l6aW5nTW9kZVYkY2xhc3NcTlNJbWFnZUZsYWdzVk5TUmVwc1dOU0NvbG9ygAIQAIAN -EiDDAACAA4ALVnsxLCAxfdIbDxweWk5TLm9iamVjdHOhHYAEgArSGw8gI6IhIoAFgAaACdMPJSYnKBRf -EBROU1RJRkZSZXByZXNlbnRhdGlvbl8QGU5TSW50ZXJuYWxMYXlvdXREaXJlY3Rpb26ACIAHTxESbE1N -ACoAAAAKAAAAEAEAAAMAAAABAAEAAAEBAAMAAAABAAEAAAECAAMAAAACAAgACAEDAAMAAAABAAEAAAEG -AAMAAAABAAEAAAEKAAMAAAABAAEAAAERAAQAAAABAAAACAESAAMAAAABAAEAAAEVAAMAAAABAAIAAAEW -AAMAAAABAAEAAAEXAAQAAAABAAAAAgEcAAMAAAABAAEAAAEoAAMAAAABAAIAAAFSAAMAAAABAAEAAAFT -AAMAAAACAAEAAYdzAAcAABGcAAAA0AAAAAAAABGcYXBwbAIAAABtbnRyR1JBWVhZWiAH3AAIABcADwAu -AA9hY3NwQVBQTAAAAABub25lAAAAAAAAAAAAAAAAAAAAAAAA9tYAAQAAAADTLWFwcGwAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAVkZXNjAAAAwAAAAHlkc2NtAAABPAAA -CBpjcHJ0AAAJWAAAACN3dHB0AAAJfAAAABRrVFJDAAAJkAAACAxkZXNjAAAAAAAAAB9HZW5lcmljIEdy -YXkgR2FtbWEgMi4yIFByb2ZpbGUAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAbWx1YwAAAAAAAAAfAAAADHNr -U0sAAAAuAAABhGRhREsAAAA6AAABsmNhRVMAAAA4AAAB7HZpVk4AAABAAAACJHB0QlIAAABKAAACZHVr -VUEAAAAsAAACrmZyRlUAAAA+AAAC2mh1SFUAAAA0AAADGHpoVFcAAAAaAAADTGtvS1IAAAAiAAADZm5i -Tk8AAAA6AAADiGNzQ1oAAAAoAAADwmhlSUwAAAAkAAAD6nJvUk8AAAAqAAAEDmRlREUAAABOAAAEOGl0 -SVQAAABOAAAEhnN2U0UAAAA4AAAE1HpoQ04AAAAaAAAFDGphSlAAAAAmAAAFJmVsR1IAAAAqAAAFTHB0 -UE8AAABSAAAFdm5sTkwAAABAAAAFyGVzRVMAAABMAAAGCHRoVEgAAAAyAAAGVHRyVFIAAAAkAAAGhmZp -RkkAAABGAAAGqmhySFIAAAA+AAAG8HBsUEwAAABKAAAHLmFyRUcAAAAsAAAHeHJ1UlUAAAA6AAAHpGVu -VVMAAAA8AAAH3gBWAWEAZQBvAGIAZQBjAG4A4QAgAHMAaQB2AOEAIABnAGEAbQBhACAAMgAsADIARwBl -AG4AZQByAGkAcwBrACAAZwByAOUAIAAyACwAMgAgAGcAYQBtAG0AYQAtAHAAcgBvAGYAaQBsAEcAYQBt -AG0AYQAgAGQAZQAgAGcAcgBpAHMAbwBzACAAZwBlAG4A6AByAGkAYwBhACAAMgAuADIAQx6lAHUAIABo -AOwAbgBoACAATQDgAHUAIAB4AOEAbQAgAEMAaAB1AG4AZwAgAEcAYQBtAG0AYQAgADIALgAyAFAAZQBy -AGYAaQBsACAARwBlAG4A6QByAGkAYwBvACAAZABhACAARwBhAG0AYQAgAGQAZQAgAEMAaQBuAHoAYQBz -ACAAMgAsADIEFwQwBDMEMAQ7BEwEPQQwACAARwByAGEAeQAtBDMEMAQ8BDAAIAAyAC4AMgBQAHIAbwBm -AGkAbAAgAGcA6QBuAOkAcgBpAHEAdQBlACAAZwByAGkAcwAgAGcAYQBtAG0AYQAgADIALAAyAMEAbAB0 -AGEAbADhAG4AbwBzACAAcwB6APwAcgBrAGUAIABnAGEAbQBtAGEAIAAyAC4AMpAadShwcJaOUUlepgAy -AC4AMoJyX2ljz4/wx3y8GAAg1ozAyQAgrBC5yAAgADIALgAyACDVBLhc0wzHfABHAGUAbgBlAHIAaQBz -AGsAIABnAHIA5QAgAGcAYQBtAG0AYQAgADIALAAyAC0AcAByAG8AZgBpAGwATwBiAGUAYwBuAOEAIAFh -AGUAZADhACAAZwBhAG0AYQAgADIALgAyBdIF0AXeBdQAIAXQBeQF1QXoACAF2wXcBdwF2QAgADIALgAy -AEcAYQBtAGEAIABnAHIAaQAgAGcAZQBuAGUAcgBpAGMBAwAgADIALAAyAEEAbABsAGcAZQBtAGUAaQBu -AGUAcwAgAEcAcgBhAHUAcwB0AHUAZgBlAG4ALQBQAHIAbwBmAGkAbAAgAEcAYQBtAG0AYQAgADIALAAy -AFAAcgBvAGYAaQBsAG8AIABnAHIAaQBnAGkAbwAgAGcAZQBuAGUAcgBpAGMAbwAgAGQAZQBsAGwAYQAg -AGcAYQBtAG0AYQAgADIALAAyAEcAZQBuAGUAcgBpAHMAawAgAGcAcgDlACAAMgAsADIAIABnAGEAbQBt -AGEAcAByAG8AZgBpAGxmbpAacHBepnz7ZXAAMgAuADJjz4/wZYdO9k4AgiwwsDDsMKQwrDDzMN4AIAAy -AC4AMgAgMNcw7TDVMKEwpDDrA5MDtQO9A7kDugPMACADkwO6A8EDuQAgA5MDrAO8A7wDsQAgADIALgAy -AFAAZQByAGYAaQBsACAAZwBlAG4A6QByAGkAYwBvACAAZABlACAAYwBpAG4AegBlAG4AdABvAHMAIABk -AGEAIABHAGEAbQBtAGEAIAAyACwAMgBBAGwAZwBlAG0AZQBlAG4AIABnAHIAaQBqAHMAIABnAGEAbQBt -AGEAIAAyACwAMgAtAHAAcgBvAGYAaQBlAGwAUABlAHIAZgBpAGwAIABnAGUAbgDpAHIAaQBjAG8AIABk -AGUAIABnAGEAbQBtAGEAIABkAGUAIABnAHIAaQBzAGUAcwAgADIALAAyDiMOMQ4HDioONQ5BDgEOIQ4h -DjIOQA4BDiMOIg5MDhcOMQ5IDicORA4bACAAMgAuADIARwBlAG4AZQBsACAARwByAGkAIABHAGEAbQBh -ACAAMgAsADIAWQBsAGUAaQBuAGUAbgAgAGgAYQByAG0AYQBhAG4AIABnAGEAbQBtAGEAIAAyACwAMgAg -AC0AcAByAG8AZgBpAGkAbABpAEcAZQBuAGUAcgBpAQ0AawBpACAARwByAGEAeQAgAEcAYQBtAG0AYQAg -ADIALgAyACAAcAByAG8AZgBpAGwAVQBuAGkAdwBlAHIAcwBhAGwAbgB5ACAAcAByAG8AZgBpAGwAIABz -AHoAYQByAG8BWwBjAGkAIABnAGEAbQBtAGEAIAAyACwAMgY6BicGRQYnACAAMgAuADIAIAZEBkgGRgAg -BjEGRQYnBi8GSgAgBjkGJwZFBB4EMQRJBDAETwAgBEEENQRABDAETwAgBDMEMAQ8BDwEMAAgADIALAAy -AC0EPwRABD4ERAQ4BDsETABHAGUAbgBlAHIAaQBjACAARwByAGEAeQAgAEcAYQBtAG0AYQAgADIALgAy -ACAAUAByAG8AZgBpAGwAZQAAdGV4dAAAAABDb3B5cmlnaHQgQXBwbGUgSW5jLiwgMjAxMgAAWFlaIAAA -AAAAAPNRAAEAAAABFsxjdXJ2AAAAAAAABAAAAAAFAAoADwAUABkAHgAjACgALQAyADcAOwBAAEUASgBP -AFQAWQBeAGMAaABtAHIAdwB8AIEAhgCLAJAAlQCaAJ8ApACpAK4AsgC3ALwAwQDGAMsA0ADVANsA4ADl -AOsA8AD2APsBAQEHAQ0BEwEZAR8BJQErATIBOAE+AUUBTAFSAVkBYAFnAW4BdQF8AYMBiwGSAZoBoQGp -AbEBuQHBAckB0QHZAeEB6QHyAfoCAwIMAhQCHQImAi8COAJBAksCVAJdAmcCcQJ6AoQCjgKYAqICrAK2 -AsECywLVAuAC6wL1AwADCwMWAyEDLQM4A0MDTwNaA2YDcgN+A4oDlgOiA64DugPHA9MD4APsA/kEBgQT -BCAELQQ7BEgEVQRjBHEEfgSMBJoEqAS2BMQE0wThBPAE/gUNBRwFKwU6BUkFWAVnBXcFhgWWBaYFtQXF -BdUF5QX2BgYGFgYnBjcGSAZZBmoGewaMBp0GrwbABtEG4wb1BwcHGQcrBz0HTwdhB3QHhgeZB6wHvwfS -B+UH+AgLCB8IMghGCFoIbgiCCJYIqgi+CNII5wj7CRAJJQk6CU8JZAl5CY8JpAm6Cc8J5Qn7ChEKJwo9 -ClQKagqBCpgKrgrFCtwK8wsLCyILOQtRC2kLgAuYC7ALyAvhC/kMEgwqDEMMXAx1DI4MpwzADNkM8w0N -DSYNQA1aDXQNjg2pDcMN3g34DhMOLg5JDmQOfw6bDrYO0g7uDwkPJQ9BD14Peg+WD7MPzw/sEAkQJhBD -EGEQfhCbELkQ1xD1ERMRMRFPEW0RjBGqEckR6BIHEiYSRRJkEoQSoxLDEuMTAxMjE0MTYxODE6QTxRPl -FAYUJxRJFGoUixStFM4U8BUSFTQVVhV4FZsVvRXgFgMWJhZJFmwWjxayFtYW+hcdF0EXZReJF64X0hf3 -GBsYQBhlGIoYrxjVGPoZIBlFGWsZkRm3Gd0aBBoqGlEadxqeGsUa7BsUGzsbYxuKG7Ib2hwCHCocUhx7 -HKMczBz1HR4dRx1wHZkdwx3sHhYeQB5qHpQevh7pHxMfPh9pH5Qfvx/qIBUgQSBsIJggxCDwIRwhSCF1 -IaEhziH7IiciVSKCIq8i3SMKIzgjZiOUI8Ij8CQfJE0kfCSrJNolCSU4JWgllyXHJfcmJyZXJocmtybo -JxgnSSd6J6sn3CgNKD8ocSiiKNQpBik4KWspnSnQKgIqNSpoKpsqzysCKzYraSudK9EsBSw5LG4soizX -LQwtQS12Last4S4WLkwugi63Lu4vJC9aL5Evxy/+MDUwbDCkMNsxEjFKMYIxujHyMioyYzKbMtQzDTNG -M38zuDPxNCs0ZTSeNNg1EzVNNYc1wjX9Njc2cjauNuk3JDdgN5w31zgUOFA4jDjIOQU5Qjl/Obw5+To2 -OnQ6sjrvOy07azuqO+g8JzxlPKQ84z0iPWE9oT3gPiA+YD6gPuA/IT9hP6I/4kAjQGRApkDnQSlBakGs -Qe5CMEJyQrVC90M6Q31DwEQDREdEikTORRJFVUWaRd5GIkZnRqtG8Ec1R3tHwEgFSEtIkUjXSR1JY0mp -SfBKN0p9SsRLDEtTS5pL4kwqTHJMuk0CTUpNk03cTiVObk63TwBPSU+TT91QJ1BxULtRBlFQUZtR5lIx -UnxSx1MTU19TqlP2VEJUj1TbVShVdVXCVg9WXFapVvdXRFeSV+BYL1h9WMtZGllpWbhaB1pWWqZa9VtF -W5Vb5Vw1XIZc1l0nXXhdyV4aXmxevV8PX2Ffs2AFYFdgqmD8YU9homH1YklinGLwY0Njl2PrZEBklGTp -ZT1lkmXnZj1mkmboZz1nk2fpaD9olmjsaUNpmmnxakhqn2r3a09rp2v/bFdsr20IbWBtuW4SbmtuxG8e -b3hv0XArcIZw4HE6cZVx8HJLcqZzAXNdc7h0FHRwdMx1KHWFdeF2Pnabdvh3VnezeBF4bnjMeSp5iXnn -ekZ6pXsEe2N7wnwhfIF84X1BfaF+AX5ifsJ/I3+Ef+WAR4CogQqBa4HNgjCCkoL0g1eDuoQdhICE44VH -hauGDoZyhteHO4efiASIaYjOiTOJmYn+imSKyoswi5aL/IxjjMqNMY2Yjf+OZo7OjzaPnpAGkG6Q1pE/ -kaiSEZJ6kuOTTZO2lCCUipT0lV+VyZY0lp+XCpd1l+CYTJi4mSSZkJn8mmia1ZtCm6+cHJyJnPedZJ3S -nkCerp8dn4uf+qBpoNihR6G2oiailqMGo3aj5qRWpMelOKWpphqmi6b9p26n4KhSqMSpN6mpqhyqj6sC -q3Wr6axcrNCtRK24ri2uoa8Wr4uwALB1sOqxYLHWskuywrM4s660JbSctRO1irYBtnm28Ldot+C4WbjR -uUq5wro7urW7LrunvCG8m70VvY++Cr6Evv+/er/1wHDA7MFnwePCX8Lbw1jD1MRRxM7FS8XIxkbGw8dB -x7/IPci8yTrJuco4yrfLNsu2zDXMtc01zbXONs62zzfPuNA50LrRPNG+0j/SwdNE08bUSdTL1U7V0dZV -1tjXXNfg2GTY6Nls2fHadtr724DcBdyK3RDdlt4c3qLfKd+v4DbgveFE4cziU+Lb42Pj6+Rz5PzlhOYN -5pbnH+ep6DLovOlG6dDqW+rl63Dr++yG7RHtnO4o7rTvQO/M8Fjw5fFy8f/yjPMZ86f0NPTC9VD13vZt -9vv3ivgZ+Kj5OPnH+lf65/t3/Af8mP0p/br+S/7c/23//9IrLC0uWiRjbGFzc25hbWVYJGNsYXNzZXNf -EBBOU0JpdG1hcEltYWdlUmVwoy0vMFpOU0ltYWdlUmVwWE5TT2JqZWN00issMjNXTlNBcnJheaIyMNIr -LDU2Xk5TTXV0YWJsZUFycmF5ozUyMNM4OQ86OzxXTlNXaGl0ZVxOU0NvbG9yU3BhY2VEMCAwABADgAzS -Kyw+P1dOU0NvbG9yoj4w0issQUJXTlNJbWFnZaJBMAAIABEAGgAkACkAMgA3AEkATABRAFMAYgBoAHUA -fACLAJIAnwCmAK4AsACyALQAuQC7AL0AxADJANQA1gDYANoA3wDiAOQA5gDoAO8BBgEiASQBJhOWE5sT -phOvE8ITxhPRE9oT3xPnE+oT7xP+FAIUCRQRFB4UIxQlFCcULBQ0FDcUPBREAAAAAAAAAgEAAAAAAAAA -QwAAAAAAAAAAAAAAAAAAFEc +S2V5ZWRBcmNoaXZlctEICVRyb290gAGuCwwbHCEaJissMzY5P0JVJG51bGzXDQ4PEBESExQVFhcYGRpW +TlNTaXplViRjbGFzc1xOU0ltYWdlRmxhZ3NWTlNSZXBzW05TVGludENvbG9yV05TQ29sb3JeTlNSZXNp +emluZ01vZGWAAoANEiDDAACAA4AAgAsQAFZ7MSwgMX3SHQ4eIFpOUy5vYmplY3RzoR+ABIAK0h0OIiWi +IySABYAGgAnTDicoKSoaXxAUTlNUSUZGUmVwcmVzZW50YXRpb25fEBlOU0ludGVybmFsTGF5b3V0RGly +ZWN0aW9ugAiAB08REmxNTQAqAAAACgAAABABAAADAAAAAQABAAABAQADAAAAAQABAAABAgADAAAAAgAI +AAgBAwADAAAAAQABAAABBgADAAAAAQABAAABCgADAAAAAQABAAABEQAEAAAAAQAAAAgBEgADAAAAAQAB +AAABFQADAAAAAQACAAABFgADAAAAAQABAAABFwAEAAAAAQAAAAIBHAADAAAAAQABAAABKAADAAAAAQAC +AAABUgADAAAAAQABAAABUwADAAAAAgABAAGHcwAHAAARnAAAANAAAAAAAAARnGFwcGwCAAAAbW50ckdS +QVlYWVogB9wACAAXAA8ALgAPYWNzcEFQUEwAAAAAbm9uZQAAAAAAAAAAAAAAAAAAAAAAAPbWAAEAAAAA +0y1hcHBsAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFZGVzYwAA +AMAAAAB5ZHNjbQAAATwAAAgaY3BydAAACVgAAAAjd3RwdAAACXwAAAAUa1RSQwAACZAAAAgMZGVzYwAA +AAAAAAAfR2VuZXJpYyBHcmF5IEdhbW1hIDIuMiBQcm9maWxlAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAG1s +dWMAAAAAAAAAHwAAAAxza1NLAAAALgAAAYRkYURLAAAAOgAAAbJjYUVTAAAAOAAAAex2aVZOAAAAQAAA +AiRwdEJSAAAASgAAAmR1a1VBAAAALAAAAq5mckZVAAAAPgAAAtpodUhVAAAANAAAAxh6aFRXAAAAGgAA +A0xrb0tSAAAAIgAAA2ZuYk5PAAAAOgAAA4hjc0NaAAAAKAAAA8JoZUlMAAAAJAAAA+pyb1JPAAAAKgAA +BA5kZURFAAAATgAABDhpdElUAAAATgAABIZzdlNFAAAAOAAABNR6aENOAAAAGgAABQxqYUpQAAAAJgAA +BSZlbEdSAAAAKgAABUxwdFBPAAAAUgAABXZubE5MAAAAQAAABchlc0VTAAAATAAABgh0aFRIAAAAMgAA +BlR0clRSAAAAJAAABoZmaUZJAAAARgAABqpockhSAAAAPgAABvBwbFBMAAAASgAABy5hckVHAAAALAAA +B3hydVJVAAAAOgAAB6RlblVTAAAAPAAAB94AVgFhAGUAbwBiAGUAYwBuAOEAIABzAGkAdgDhACAAZwBh +AG0AYQAgADIALAAyAEcAZQBuAGUAcgBpAHMAawAgAGcAcgDlACAAMgAsADIAIABnAGEAbQBtAGEALQBw +AHIAbwBmAGkAbABHAGEAbQBtAGEAIABkAGUAIABnAHIAaQBzAG8AcwAgAGcAZQBuAOgAcgBpAGMAYQAg +ADIALgAyAEMepQB1ACAAaADsAG4AaAAgAE0A4AB1ACAAeADhAG0AIABDAGgAdQBuAGcAIABHAGEAbQBt +AGEAIAAyAC4AMgBQAGUAcgBmAGkAbAAgAEcAZQBuAOkAcgBpAGMAbwAgAGQAYQAgAEcAYQBtAGEAIABk +AGUAIABDAGkAbgB6AGEAcwAgADIALAAyBBcEMAQzBDAEOwRMBD0EMAAgAEcAcgBhAHkALQQzBDAEPAQw +ACAAMgAuADIAUAByAG8AZgBpAGwAIABnAOkAbgDpAHIAaQBxAHUAZQAgAGcAcgBpAHMAIABnAGEAbQBt +AGEAIAAyACwAMgDBAGwAdABhAGwA4QBuAG8AcwAgAHMAegD8AHIAawBlACAAZwBhAG0AbQBhACAAMgAu +ADKQGnUocHCWjlFJXqYAMgAuADKCcl9pY8+P8Md8vBgAINaMwMkAIKwQucgAIAAyAC4AMgAg1QS4XNMM +x3wARwBlAG4AZQByAGkAcwBrACAAZwByAOUAIABnAGEAbQBtAGEAIAAyACwAMgAtAHAAcgBvAGYAaQBs +AE8AYgBlAGMAbgDhACABYQBlAGQA4QAgAGcAYQBtAGEAIAAyAC4AMgXSBdAF3gXUACAF0AXkBdUF6AAg +BdsF3AXcBdkAIAAyAC4AMgBHAGEAbQBhACAAZwByAGkAIABnAGUAbgBlAHIAaQBjAQMAIAAyACwAMgBB +AGwAbABnAGUAbQBlAGkAbgBlAHMAIABHAHIAYQB1AHMAdAB1AGYAZQBuAC0AUAByAG8AZgBpAGwAIABH +AGEAbQBtAGEAIAAyACwAMgBQAHIAbwBmAGkAbABvACAAZwByAGkAZwBpAG8AIABnAGUAbgBlAHIAaQBj +AG8AIABkAGUAbABsAGEAIABnAGEAbQBtAGEAIAAyACwAMgBHAGUAbgBlAHIAaQBzAGsAIABnAHIA5QAg +ADIALAAyACAAZwBhAG0AbQBhAHAAcgBvAGYAaQBsZm6QGnBwXqZ8+2VwADIALgAyY8+P8GWHTvZOAIIs +MLAw7DCkMKww8zDeACAAMgAuADIAIDDXMO0w1TChMKQw6wOTA7UDvQO5A7oDzAAgA5MDugPBA7kAIAOT +A6wDvAO8A7EAIAAyAC4AMgBQAGUAcgBmAGkAbAAgAGcAZQBuAOkAcgBpAGMAbwAgAGQAZQAgAGMAaQBu +AHoAZQBuAHQAbwBzACAAZABhACAARwBhAG0AbQBhACAAMgAsADIAQQBsAGcAZQBtAGUAZQBuACAAZwBy +AGkAagBzACAAZwBhAG0AbQBhACAAMgAsADIALQBwAHIAbwBmAGkAZQBsAFAAZQByAGYAaQBsACAAZwBl +AG4A6QByAGkAYwBvACAAZABlACAAZwBhAG0AbQBhACAAZABlACAAZwByAGkAcwBlAHMAIAAyACwAMg4j +DjEOBw4qDjUOQQ4BDiEOIQ4yDkAOAQ4jDiIOTA4XDjEOSA4nDkQOGwAgADIALgAyAEcAZQBuAGUAbAAg +AEcAcgBpACAARwBhAG0AYQAgADIALAAyAFkAbABlAGkAbgBlAG4AIABoAGEAcgBtAGEAYQBuACAAZwBh +AG0AbQBhACAAMgAsADIAIAAtAHAAcgBvAGYAaQBpAGwAaQBHAGUAbgBlAHIAaQENAGsAaQAgAEcAcgBh +AHkAIABHAGEAbQBtAGEAIAAyAC4AMgAgAHAAcgBvAGYAaQBsAFUAbgBpAHcAZQByAHMAYQBsAG4AeQAg +AHAAcgBvAGYAaQBsACAAcwB6AGEAcgBvAVsAYwBpACAAZwBhAG0AbQBhACAAMgAsADIGOgYnBkUGJwAg +ADIALgAyACAGRAZIBkYAIAYxBkUGJwYvBkoAIAY5BicGRQQeBDEESQQwBE8AIARBBDUEQAQwBE8AIAQz +BDAEPAQ8BDAAIAAyACwAMgAtBD8EQAQ+BEQEOAQ7BEwARwBlAG4AZQByAGkAYwAgAEcAcgBhAHkAIABH +AGEAbQBtAGEAIAAyAC4AMgAgAFAAcgBvAGYAaQBsAGUAAHRleHQAAAAAQ29weXJpZ2h0IEFwcGxlIElu +Yy4sIDIwMTIAAFhZWiAAAAAAAADzUQABAAAAARbMY3VydgAAAAAAAAQAAAAABQAKAA8AFAAZAB4AIwAo +AC0AMgA3ADsAQABFAEoATwBUAFkAXgBjAGgAbQByAHcAfACBAIYAiwCQAJUAmgCfAKQAqQCuALIAtwC8 +AMEAxgDLANAA1QDbAOAA5QDrAPAA9gD7AQEBBwENARMBGQEfASUBKwEyATgBPgFFAUwBUgFZAWABZwFu +AXUBfAGDAYsBkgGaAaEBqQGxAbkBwQHJAdEB2QHhAekB8gH6AgMCDAIUAh0CJgIvAjgCQQJLAlQCXQJn +AnECegKEAo4CmAKiAqwCtgLBAssC1QLgAusC9QMAAwsDFgMhAy0DOANDA08DWgNmA3IDfgOKA5YDogOu +A7oDxwPTA+AD7AP5BAYEEwQgBC0EOwRIBFUEYwRxBH4EjASaBKgEtgTEBNME4QTwBP4FDQUcBSsFOgVJ +BVgFZwV3BYYFlgWmBbUFxQXVBeUF9gYGBhYGJwY3BkgGWQZqBnsGjAadBq8GwAbRBuMG9QcHBxkHKwc9 +B08HYQd0B4YHmQesB78H0gflB/gICwgfCDIIRghaCG4IggiWCKoIvgjSCOcI+wkQCSUJOglPCWQJeQmP +CaQJugnPCeUJ+woRCicKPQpUCmoKgQqYCq4KxQrcCvMLCwsiCzkLUQtpC4ALmAuwC8gL4Qv5DBIMKgxD +DFwMdQyODKcMwAzZDPMNDQ0mDUANWg10DY4NqQ3DDd4N+A4TDi4OSQ5kDn8Omw62DtIO7g8JDyUPQQ9e +D3oPlg+zD88P7BAJECYQQxBhEH4QmxC5ENcQ9RETETERTxFtEYwRqhHJEegSBxImEkUSZBKEEqMSwxLj +EwMTIxNDE2MTgxOkE8UT5RQGFCcUSRRqFIsUrRTOFPAVEhU0FVYVeBWbFb0V4BYDFiYWSRZsFo8WshbW +FvoXHRdBF2UXiReuF9IX9xgbGEAYZRiKGK8Y1Rj6GSAZRRlrGZEZtxndGgQaKhpRGncanhrFGuwbFBs7 +G2MbihuyG9ocAhwqHFIcexyjHMwc9R0eHUcdcB2ZHcMd7B4WHkAeah6UHr4e6R8THz4faR+UH78f6iAV +IEEgbCCYIMQg8CEcIUghdSGhIc4h+yInIlUigiKvIt0jCiM4I2YjlCPCI/AkHyRNJHwkqyTaJQklOCVo +JZclxyX3JicmVyaHJrcm6CcYJ0kneierJ9woDSg/KHEooijUKQYpOClrKZ0p0CoCKjUqaCqbKs8rAis2 +K2krnSvRLAUsOSxuLKIs1y0MLUEtdi2rLeEuFi5MLoIuty7uLyQvWi+RL8cv/jA1MGwwpDDbMRIxSjGC +Mbox8jIqMmMymzLUMw0zRjN/M7gz8TQrNGU0njTYNRM1TTWHNcI1/TY3NnI2rjbpNyQ3YDecN9c4FDhQ +OIw4yDkFOUI5fzm8Ofk6Njp0OrI67zstO2s7qjvoPCc8ZTykPOM9Ij1hPaE94D4gPmA+oD7gPyE/YT+i +P+JAI0BkQKZA50EpQWpBrEHuQjBCckK1QvdDOkN9Q8BEA0RHRIpEzkUSRVVFmkXeRiJGZ0arRvBHNUd7 +R8BIBUhLSJFI10kdSWNJqUnwSjdKfUrESwxLU0uaS+JMKkxyTLpNAk1KTZNN3E4lTm5Ot08AT0lPk0/d +UCdQcVC7UQZRUFGbUeZSMVJ8UsdTE1NfU6pT9lRCVI9U21UoVXVVwlYPVlxWqVb3V0RXklfgWC9YfVjL +WRpZaVm4WgdaVlqmWvVbRVuVW+VcNVyGXNZdJ114XcleGl5sXr1fD19hX7NgBWBXYKpg/GFPYaJh9WJJ +Ypxi8GNDY5dj62RAZJRk6WU9ZZJl52Y9ZpJm6Gc9Z5Nn6Wg/aJZo7GlDaZpp8WpIap9q92tPa6dr/2xX +bK9tCG1gbbluEm5rbsRvHm94b9FwK3CGcOBxOnGVcfByS3KmcwFzXXO4dBR0cHTMdSh1hXXhdj52m3b4 +d1Z3s3gReG54zHkqeYl553pGeqV7BHtje8J8IXyBfOF9QX2hfgF+Yn7CfyN/hH/lgEeAqIEKgWuBzYIw +gpKC9INXg7qEHYSAhOOFR4Wrhg6GcobXhzuHn4gEiGmIzokziZmJ/opkisqLMIuWi/yMY4zKjTGNmI3/ +jmaOzo82j56QBpBukNaRP5GokhGSepLjk02TtpQglIqU9JVflcmWNJaflwqXdZfgmEyYuJkkmZCZ/Jpo +mtWbQpuvnByciZz3nWSd0p5Anq6fHZ+Ln/qgaaDYoUehtqImopajBqN2o+akVqTHpTilqaYapoum/adu +p+CoUqjEqTepqaocqo+rAqt1q+msXKzQrUStuK4trqGvFq+LsACwdbDqsWCx1rJLssKzOLOutCW0nLUT +tYq2AbZ5tvC3aLfguFm40blKucK6O7q1uy67p7whvJu9Fb2Pvgq+hL7/v3q/9cBwwOzBZ8Hjwl/C28NY +w9TEUcTOxUvFyMZGxsPHQce/yD3IvMk6ybnKOMq3yzbLtsw1zLXNNc21zjbOts83z7jQOdC60TzRvtI/ +0sHTRNPG1EnUy9VO1dHWVdbY11zX4Nhk2OjZbNnx2nba+9uA3AXcit0Q3ZbeHN6i3ynfr+A24L3hROHM +4lPi2+Nj4+vkc+T85YTmDeaW5x/nqegy6LzpRunQ6lvq5etw6/vshu0R7ZzuKO6070DvzPBY8OXxcvH/ +8ozzGfOn9DT0wvVQ9d72bfb794r4Gfio+Tj5x/pX+uf7d/wH/Jj9Kf26/kv+3P9t///SLS4vMFokY2xh +c3NuYW1lWCRjbGFzc2VzXxAQTlNCaXRtYXBJbWFnZVJlcKMvMTJaTlNJbWFnZVJlcFhOU09iamVjdNIt +LjQ1V05TQXJyYXmiNDLSLS43OF5OU011dGFibGVBcnJheaM3NDLTOjsOPD0+V05TV2hpdGVcTlNDb2xv +clNwYWNlRDAgMAAQA4AM0i0uQEFXTlNDb2xvcqJAMtItLkNEV05TSW1hZ2WiQzIACAARABoAJAApADIA +NwBJAEwAUQBTAGIAaAB3AH4AhQCSAJkApQCtALwAvgDAAMUAxwDJAMsAzQDUANkA5ADmAOgA6gDvAPIA +9AD2APgA/wEWATIBNAE2E6YTqxO2E78T0hPWE+ET6hPvE/cT+hP/FA4UEhQZFCEULhQzFDUUNxQ8FEQU +RxRMFFQAAAAAAAACAQAAAAAAAABFAAAAAAAAAAAAAAAAAAAUVw diff --git a/i18n/src/main/resources/ja.lproj/Browser.xib b/i18n/src/main/resources/ja.lproj/Browser.xib index cedb8876fc..27e1e4fd9d 100644 --- a/i18n/src/main/resources/ja.lproj/Browser.xib +++ b/i18n/src/main/resources/ja.lproj/Browser.xib @@ -1,7 +1,7 @@ - + - + @@ -38,7 +38,7 @@ - + @@ -62,27 +62,27 @@ @@ -256,7 +256,7 @@ - + @@ -553,95 +553,95 @@ DQ YnBsaXN0MDDUAQIDBAUGBwpYJHZlcnNpb25ZJGFyY2hpdmVyVCR0b3BYJG9iamVjdHMSAAGGoF8QD05T -S2V5ZWRBcmNoaXZlctEICVRyb290gAGuCwwZGh8UJCkqMTQ3PUBVJG51bGzWDQ4PEBESExQVFhcYVk5T -U2l6ZV5OU1Jlc2l6aW5nTW9kZVYkY2xhc3NcTlNJbWFnZUZsYWdzVk5TUmVwc1dOU0NvbG9ygAIQAIAN -EiDDAACAA4ALVnsxLCAxfdIbDxweWk5TLm9iamVjdHOhHYAEgArSGw8gI6IhIoAFgAaACdMPJSYnKBRf -EBROU1RJRkZSZXByZXNlbnRhdGlvbl8QGU5TSW50ZXJuYWxMYXlvdXREaXJlY3Rpb26ACIAHTxESbE1N -ACoAAAAKAAAAEAEAAAMAAAABAAEAAAEBAAMAAAABAAEAAAECAAMAAAACAAgACAEDAAMAAAABAAEAAAEG -AAMAAAABAAEAAAEKAAMAAAABAAEAAAERAAQAAAABAAAACAESAAMAAAABAAEAAAEVAAMAAAABAAIAAAEW -AAMAAAABAAEAAAEXAAQAAAABAAAAAgEcAAMAAAABAAEAAAEoAAMAAAABAAIAAAFSAAMAAAABAAEAAAFT -AAMAAAACAAEAAYdzAAcAABGcAAAA0AAAAAAAABGcYXBwbAIAAABtbnRyR1JBWVhZWiAH3AAIABcADwAu -AA9hY3NwQVBQTAAAAABub25lAAAAAAAAAAAAAAAAAAAAAAAA9tYAAQAAAADTLWFwcGwAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAVkZXNjAAAAwAAAAHlkc2NtAAABPAAA -CBpjcHJ0AAAJWAAAACN3dHB0AAAJfAAAABRrVFJDAAAJkAAACAxkZXNjAAAAAAAAAB9HZW5lcmljIEdy -YXkgR2FtbWEgMi4yIFByb2ZpbGUAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAbWx1YwAAAAAAAAAfAAAADHNr -U0sAAAAuAAABhGRhREsAAAA6AAABsmNhRVMAAAA4AAAB7HZpVk4AAABAAAACJHB0QlIAAABKAAACZHVr -VUEAAAAsAAACrmZyRlUAAAA+AAAC2mh1SFUAAAA0AAADGHpoVFcAAAAaAAADTGtvS1IAAAAiAAADZm5i -Tk8AAAA6AAADiGNzQ1oAAAAoAAADwmhlSUwAAAAkAAAD6nJvUk8AAAAqAAAEDmRlREUAAABOAAAEOGl0 -SVQAAABOAAAEhnN2U0UAAAA4AAAE1HpoQ04AAAAaAAAFDGphSlAAAAAmAAAFJmVsR1IAAAAqAAAFTHB0 -UE8AAABSAAAFdm5sTkwAAABAAAAFyGVzRVMAAABMAAAGCHRoVEgAAAAyAAAGVHRyVFIAAAAkAAAGhmZp -RkkAAABGAAAGqmhySFIAAAA+AAAG8HBsUEwAAABKAAAHLmFyRUcAAAAsAAAHeHJ1UlUAAAA6AAAHpGVu -VVMAAAA8AAAH3gBWAWEAZQBvAGIAZQBjAG4A4QAgAHMAaQB2AOEAIABnAGEAbQBhACAAMgAsADIARwBl -AG4AZQByAGkAcwBrACAAZwByAOUAIAAyACwAMgAgAGcAYQBtAG0AYQAtAHAAcgBvAGYAaQBsAEcAYQBt -AG0AYQAgAGQAZQAgAGcAcgBpAHMAbwBzACAAZwBlAG4A6AByAGkAYwBhACAAMgAuADIAQx6lAHUAIABo -AOwAbgBoACAATQDgAHUAIAB4AOEAbQAgAEMAaAB1AG4AZwAgAEcAYQBtAG0AYQAgADIALgAyAFAAZQBy -AGYAaQBsACAARwBlAG4A6QByAGkAYwBvACAAZABhACAARwBhAG0AYQAgAGQAZQAgAEMAaQBuAHoAYQBz -ACAAMgAsADIEFwQwBDMEMAQ7BEwEPQQwACAARwByAGEAeQAtBDMEMAQ8BDAAIAAyAC4AMgBQAHIAbwBm -AGkAbAAgAGcA6QBuAOkAcgBpAHEAdQBlACAAZwByAGkAcwAgAGcAYQBtAG0AYQAgADIALAAyAMEAbAB0 -AGEAbADhAG4AbwBzACAAcwB6APwAcgBrAGUAIABnAGEAbQBtAGEAIAAyAC4AMpAadShwcJaOUUlepgAy -AC4AMoJyX2ljz4/wx3y8GAAg1ozAyQAgrBC5yAAgADIALgAyACDVBLhc0wzHfABHAGUAbgBlAHIAaQBz -AGsAIABnAHIA5QAgAGcAYQBtAG0AYQAgADIALAAyAC0AcAByAG8AZgBpAGwATwBiAGUAYwBuAOEAIAFh -AGUAZADhACAAZwBhAG0AYQAgADIALgAyBdIF0AXeBdQAIAXQBeQF1QXoACAF2wXcBdwF2QAgADIALgAy -AEcAYQBtAGEAIABnAHIAaQAgAGcAZQBuAGUAcgBpAGMBAwAgADIALAAyAEEAbABsAGcAZQBtAGUAaQBu -AGUAcwAgAEcAcgBhAHUAcwB0AHUAZgBlAG4ALQBQAHIAbwBmAGkAbAAgAEcAYQBtAG0AYQAgADIALAAy -AFAAcgBvAGYAaQBsAG8AIABnAHIAaQBnAGkAbwAgAGcAZQBuAGUAcgBpAGMAbwAgAGQAZQBsAGwAYQAg -AGcAYQBtAG0AYQAgADIALAAyAEcAZQBuAGUAcgBpAHMAawAgAGcAcgDlACAAMgAsADIAIABnAGEAbQBt -AGEAcAByAG8AZgBpAGxmbpAacHBepnz7ZXAAMgAuADJjz4/wZYdO9k4AgiwwsDDsMKQwrDDzMN4AIAAy -AC4AMgAgMNcw7TDVMKEwpDDrA5MDtQO9A7kDugPMACADkwO6A8EDuQAgA5MDrAO8A7wDsQAgADIALgAy -AFAAZQByAGYAaQBsACAAZwBlAG4A6QByAGkAYwBvACAAZABlACAAYwBpAG4AegBlAG4AdABvAHMAIABk -AGEAIABHAGEAbQBtAGEAIAAyACwAMgBBAGwAZwBlAG0AZQBlAG4AIABnAHIAaQBqAHMAIABnAGEAbQBt -AGEAIAAyACwAMgAtAHAAcgBvAGYAaQBlAGwAUABlAHIAZgBpAGwAIABnAGUAbgDpAHIAaQBjAG8AIABk -AGUAIABnAGEAbQBtAGEAIABkAGUAIABnAHIAaQBzAGUAcwAgADIALAAyDiMOMQ4HDioONQ5BDgEOIQ4h -DjIOQA4BDiMOIg5MDhcOMQ5IDicORA4bACAAMgAuADIARwBlAG4AZQBsACAARwByAGkAIABHAGEAbQBh -ACAAMgAsADIAWQBsAGUAaQBuAGUAbgAgAGgAYQByAG0AYQBhAG4AIABnAGEAbQBtAGEAIAAyACwAMgAg -AC0AcAByAG8AZgBpAGkAbABpAEcAZQBuAGUAcgBpAQ0AawBpACAARwByAGEAeQAgAEcAYQBtAG0AYQAg -ADIALgAyACAAcAByAG8AZgBpAGwAVQBuAGkAdwBlAHIAcwBhAGwAbgB5ACAAcAByAG8AZgBpAGwAIABz -AHoAYQByAG8BWwBjAGkAIABnAGEAbQBtAGEAIAAyACwAMgY6BicGRQYnACAAMgAuADIAIAZEBkgGRgAg -BjEGRQYnBi8GSgAgBjkGJwZFBB4EMQRJBDAETwAgBEEENQRABDAETwAgBDMEMAQ8BDwEMAAgADIALAAy -AC0EPwRABD4ERAQ4BDsETABHAGUAbgBlAHIAaQBjACAARwByAGEAeQAgAEcAYQBtAG0AYQAgADIALgAy -ACAAUAByAG8AZgBpAGwAZQAAdGV4dAAAAABDb3B5cmlnaHQgQXBwbGUgSW5jLiwgMjAxMgAAWFlaIAAA -AAAAAPNRAAEAAAABFsxjdXJ2AAAAAAAABAAAAAAFAAoADwAUABkAHgAjACgALQAyADcAOwBAAEUASgBP -AFQAWQBeAGMAaABtAHIAdwB8AIEAhgCLAJAAlQCaAJ8ApACpAK4AsgC3ALwAwQDGAMsA0ADVANsA4ADl -AOsA8AD2APsBAQEHAQ0BEwEZAR8BJQErATIBOAE+AUUBTAFSAVkBYAFnAW4BdQF8AYMBiwGSAZoBoQGp -AbEBuQHBAckB0QHZAeEB6QHyAfoCAwIMAhQCHQImAi8COAJBAksCVAJdAmcCcQJ6AoQCjgKYAqICrAK2 -AsECywLVAuAC6wL1AwADCwMWAyEDLQM4A0MDTwNaA2YDcgN+A4oDlgOiA64DugPHA9MD4APsA/kEBgQT -BCAELQQ7BEgEVQRjBHEEfgSMBJoEqAS2BMQE0wThBPAE/gUNBRwFKwU6BUkFWAVnBXcFhgWWBaYFtQXF -BdUF5QX2BgYGFgYnBjcGSAZZBmoGewaMBp0GrwbABtEG4wb1BwcHGQcrBz0HTwdhB3QHhgeZB6wHvwfS -B+UH+AgLCB8IMghGCFoIbgiCCJYIqgi+CNII5wj7CRAJJQk6CU8JZAl5CY8JpAm6Cc8J5Qn7ChEKJwo9 -ClQKagqBCpgKrgrFCtwK8wsLCyILOQtRC2kLgAuYC7ALyAvhC/kMEgwqDEMMXAx1DI4MpwzADNkM8w0N -DSYNQA1aDXQNjg2pDcMN3g34DhMOLg5JDmQOfw6bDrYO0g7uDwkPJQ9BD14Peg+WD7MPzw/sEAkQJhBD -EGEQfhCbELkQ1xD1ERMRMRFPEW0RjBGqEckR6BIHEiYSRRJkEoQSoxLDEuMTAxMjE0MTYxODE6QTxRPl -FAYUJxRJFGoUixStFM4U8BUSFTQVVhV4FZsVvRXgFgMWJhZJFmwWjxayFtYW+hcdF0EXZReJF64X0hf3 -GBsYQBhlGIoYrxjVGPoZIBlFGWsZkRm3Gd0aBBoqGlEadxqeGsUa7BsUGzsbYxuKG7Ib2hwCHCocUhx7 -HKMczBz1HR4dRx1wHZkdwx3sHhYeQB5qHpQevh7pHxMfPh9pH5Qfvx/qIBUgQSBsIJggxCDwIRwhSCF1 -IaEhziH7IiciVSKCIq8i3SMKIzgjZiOUI8Ij8CQfJE0kfCSrJNolCSU4JWgllyXHJfcmJyZXJocmtybo -JxgnSSd6J6sn3CgNKD8ocSiiKNQpBik4KWspnSnQKgIqNSpoKpsqzysCKzYraSudK9EsBSw5LG4soizX -LQwtQS12Last4S4WLkwugi63Lu4vJC9aL5Evxy/+MDUwbDCkMNsxEjFKMYIxujHyMioyYzKbMtQzDTNG -M38zuDPxNCs0ZTSeNNg1EzVNNYc1wjX9Njc2cjauNuk3JDdgN5w31zgUOFA4jDjIOQU5Qjl/Obw5+To2 -OnQ6sjrvOy07azuqO+g8JzxlPKQ84z0iPWE9oT3gPiA+YD6gPuA/IT9hP6I/4kAjQGRApkDnQSlBakGs -Qe5CMEJyQrVC90M6Q31DwEQDREdEikTORRJFVUWaRd5GIkZnRqtG8Ec1R3tHwEgFSEtIkUjXSR1JY0mp -SfBKN0p9SsRLDEtTS5pL4kwqTHJMuk0CTUpNk03cTiVObk63TwBPSU+TT91QJ1BxULtRBlFQUZtR5lIx -UnxSx1MTU19TqlP2VEJUj1TbVShVdVXCVg9WXFapVvdXRFeSV+BYL1h9WMtZGllpWbhaB1pWWqZa9VtF -W5Vb5Vw1XIZc1l0nXXhdyV4aXmxevV8PX2Ffs2AFYFdgqmD8YU9homH1YklinGLwY0Njl2PrZEBklGTp -ZT1lkmXnZj1mkmboZz1nk2fpaD9olmjsaUNpmmnxakhqn2r3a09rp2v/bFdsr20IbWBtuW4SbmtuxG8e -b3hv0XArcIZw4HE6cZVx8HJLcqZzAXNdc7h0FHRwdMx1KHWFdeF2Pnabdvh3VnezeBF4bnjMeSp5iXnn -ekZ6pXsEe2N7wnwhfIF84X1BfaF+AX5ifsJ/I3+Ef+WAR4CogQqBa4HNgjCCkoL0g1eDuoQdhICE44VH -hauGDoZyhteHO4efiASIaYjOiTOJmYn+imSKyoswi5aL/IxjjMqNMY2Yjf+OZo7OjzaPnpAGkG6Q1pE/ -kaiSEZJ6kuOTTZO2lCCUipT0lV+VyZY0lp+XCpd1l+CYTJi4mSSZkJn8mmia1ZtCm6+cHJyJnPedZJ3S -nkCerp8dn4uf+qBpoNihR6G2oiailqMGo3aj5qRWpMelOKWpphqmi6b9p26n4KhSqMSpN6mpqhyqj6sC -q3Wr6axcrNCtRK24ri2uoa8Wr4uwALB1sOqxYLHWskuywrM4s660JbSctRO1irYBtnm28Ldot+C4WbjR -uUq5wro7urW7LrunvCG8m70VvY++Cr6Evv+/er/1wHDA7MFnwePCX8Lbw1jD1MRRxM7FS8XIxkbGw8dB -x7/IPci8yTrJuco4yrfLNsu2zDXMtc01zbXONs62zzfPuNA50LrRPNG+0j/SwdNE08bUSdTL1U7V0dZV -1tjXXNfg2GTY6Nls2fHadtr724DcBdyK3RDdlt4c3qLfKd+v4DbgveFE4cziU+Lb42Pj6+Rz5PzlhOYN -5pbnH+ep6DLovOlG6dDqW+rl63Dr++yG7RHtnO4o7rTvQO/M8Fjw5fFy8f/yjPMZ86f0NPTC9VD13vZt -9vv3ivgZ+Kj5OPnH+lf65/t3/Af8mP0p/br+S/7c/23//9IrLC0uWiRjbGFzc25hbWVYJGNsYXNzZXNf -EBBOU0JpdG1hcEltYWdlUmVwoy0vMFpOU0ltYWdlUmVwWE5TT2JqZWN00issMjNXTlNBcnJheaIyMNIr -LDU2Xk5TTXV0YWJsZUFycmF5ozUyMNM4OQ86OzxXTlNXaGl0ZVxOU0NvbG9yU3BhY2VEMCAwABADgAzS -Kyw+P1dOU0NvbG9yoj4w0issQUJXTlNJbWFnZaJBMAAIABEAGgAkACkAMgA3AEkATABRAFMAYgBoAHUA -fACLAJIAnwCmAK4AsACyALQAuQC7AL0AxADJANQA1gDYANoA3wDiAOQA5gDoAO8BBgEiASQBJhOWE5sT -phOvE8ITxhPRE9oT3xPnE+oT7xP+FAIUCRQRFB4UIxQlFCcULBQ0FDcUPBREAAAAAAAAAgEAAAAAAAAA -QwAAAAAAAAAAAAAAAAAAFEc +S2V5ZWRBcmNoaXZlctEICVRyb290gAGuCwwbHCEaJissMzY5P0JVJG51bGzXDQ4PEBESExQVFhcYGRpW +TlNTaXplViRjbGFzc1xOU0ltYWdlRmxhZ3NWTlNSZXBzW05TVGludENvbG9yV05TQ29sb3JeTlNSZXNp +emluZ01vZGWAAoANEiDDAACAA4AAgAsQAFZ7MSwgMX3SHQ4eIFpOUy5vYmplY3RzoR+ABIAK0h0OIiWi +IySABYAGgAnTDicoKSoaXxAUTlNUSUZGUmVwcmVzZW50YXRpb25fEBlOU0ludGVybmFsTGF5b3V0RGly +ZWN0aW9ugAiAB08REmxNTQAqAAAACgAAABABAAADAAAAAQABAAABAQADAAAAAQABAAABAgADAAAAAgAI +AAgBAwADAAAAAQABAAABBgADAAAAAQABAAABCgADAAAAAQABAAABEQAEAAAAAQAAAAgBEgADAAAAAQAB +AAABFQADAAAAAQACAAABFgADAAAAAQABAAABFwAEAAAAAQAAAAIBHAADAAAAAQABAAABKAADAAAAAQAC +AAABUgADAAAAAQABAAABUwADAAAAAgABAAGHcwAHAAARnAAAANAAAAAAAAARnGFwcGwCAAAAbW50ckdS +QVlYWVogB9wACAAXAA8ALgAPYWNzcEFQUEwAAAAAbm9uZQAAAAAAAAAAAAAAAAAAAAAAAPbWAAEAAAAA +0y1hcHBsAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFZGVzYwAA +AMAAAAB5ZHNjbQAAATwAAAgaY3BydAAACVgAAAAjd3RwdAAACXwAAAAUa1RSQwAACZAAAAgMZGVzYwAA +AAAAAAAfR2VuZXJpYyBHcmF5IEdhbW1hIDIuMiBQcm9maWxlAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAG1s +dWMAAAAAAAAAHwAAAAxza1NLAAAALgAAAYRkYURLAAAAOgAAAbJjYUVTAAAAOAAAAex2aVZOAAAAQAAA +AiRwdEJSAAAASgAAAmR1a1VBAAAALAAAAq5mckZVAAAAPgAAAtpodUhVAAAANAAAAxh6aFRXAAAAGgAA +A0xrb0tSAAAAIgAAA2ZuYk5PAAAAOgAAA4hjc0NaAAAAKAAAA8JoZUlMAAAAJAAAA+pyb1JPAAAAKgAA +BA5kZURFAAAATgAABDhpdElUAAAATgAABIZzdlNFAAAAOAAABNR6aENOAAAAGgAABQxqYUpQAAAAJgAA +BSZlbEdSAAAAKgAABUxwdFBPAAAAUgAABXZubE5MAAAAQAAABchlc0VTAAAATAAABgh0aFRIAAAAMgAA +BlR0clRSAAAAJAAABoZmaUZJAAAARgAABqpockhSAAAAPgAABvBwbFBMAAAASgAABy5hckVHAAAALAAA +B3hydVJVAAAAOgAAB6RlblVTAAAAPAAAB94AVgFhAGUAbwBiAGUAYwBuAOEAIABzAGkAdgDhACAAZwBh +AG0AYQAgADIALAAyAEcAZQBuAGUAcgBpAHMAawAgAGcAcgDlACAAMgAsADIAIABnAGEAbQBtAGEALQBw +AHIAbwBmAGkAbABHAGEAbQBtAGEAIABkAGUAIABnAHIAaQBzAG8AcwAgAGcAZQBuAOgAcgBpAGMAYQAg +ADIALgAyAEMepQB1ACAAaADsAG4AaAAgAE0A4AB1ACAAeADhAG0AIABDAGgAdQBuAGcAIABHAGEAbQBt +AGEAIAAyAC4AMgBQAGUAcgBmAGkAbAAgAEcAZQBuAOkAcgBpAGMAbwAgAGQAYQAgAEcAYQBtAGEAIABk +AGUAIABDAGkAbgB6AGEAcwAgADIALAAyBBcEMAQzBDAEOwRMBD0EMAAgAEcAcgBhAHkALQQzBDAEPAQw +ACAAMgAuADIAUAByAG8AZgBpAGwAIABnAOkAbgDpAHIAaQBxAHUAZQAgAGcAcgBpAHMAIABnAGEAbQBt +AGEAIAAyACwAMgDBAGwAdABhAGwA4QBuAG8AcwAgAHMAegD8AHIAawBlACAAZwBhAG0AbQBhACAAMgAu +ADKQGnUocHCWjlFJXqYAMgAuADKCcl9pY8+P8Md8vBgAINaMwMkAIKwQucgAIAAyAC4AMgAg1QS4XNMM +x3wARwBlAG4AZQByAGkAcwBrACAAZwByAOUAIABnAGEAbQBtAGEAIAAyACwAMgAtAHAAcgBvAGYAaQBs +AE8AYgBlAGMAbgDhACABYQBlAGQA4QAgAGcAYQBtAGEAIAAyAC4AMgXSBdAF3gXUACAF0AXkBdUF6AAg +BdsF3AXcBdkAIAAyAC4AMgBHAGEAbQBhACAAZwByAGkAIABnAGUAbgBlAHIAaQBjAQMAIAAyACwAMgBB +AGwAbABnAGUAbQBlAGkAbgBlAHMAIABHAHIAYQB1AHMAdAB1AGYAZQBuAC0AUAByAG8AZgBpAGwAIABH +AGEAbQBtAGEAIAAyACwAMgBQAHIAbwBmAGkAbABvACAAZwByAGkAZwBpAG8AIABnAGUAbgBlAHIAaQBj +AG8AIABkAGUAbABsAGEAIABnAGEAbQBtAGEAIAAyACwAMgBHAGUAbgBlAHIAaQBzAGsAIABnAHIA5QAg +ADIALAAyACAAZwBhAG0AbQBhAHAAcgBvAGYAaQBsZm6QGnBwXqZ8+2VwADIALgAyY8+P8GWHTvZOAIIs +MLAw7DCkMKww8zDeACAAMgAuADIAIDDXMO0w1TChMKQw6wOTA7UDvQO5A7oDzAAgA5MDugPBA7kAIAOT +A6wDvAO8A7EAIAAyAC4AMgBQAGUAcgBmAGkAbAAgAGcAZQBuAOkAcgBpAGMAbwAgAGQAZQAgAGMAaQBu +AHoAZQBuAHQAbwBzACAAZABhACAARwBhAG0AbQBhACAAMgAsADIAQQBsAGcAZQBtAGUAZQBuACAAZwBy +AGkAagBzACAAZwBhAG0AbQBhACAAMgAsADIALQBwAHIAbwBmAGkAZQBsAFAAZQByAGYAaQBsACAAZwBl +AG4A6QByAGkAYwBvACAAZABlACAAZwBhAG0AbQBhACAAZABlACAAZwByAGkAcwBlAHMAIAAyACwAMg4j +DjEOBw4qDjUOQQ4BDiEOIQ4yDkAOAQ4jDiIOTA4XDjEOSA4nDkQOGwAgADIALgAyAEcAZQBuAGUAbAAg +AEcAcgBpACAARwBhAG0AYQAgADIALAAyAFkAbABlAGkAbgBlAG4AIABoAGEAcgBtAGEAYQBuACAAZwBh +AG0AbQBhACAAMgAsADIAIAAtAHAAcgBvAGYAaQBpAGwAaQBHAGUAbgBlAHIAaQENAGsAaQAgAEcAcgBh +AHkAIABHAGEAbQBtAGEAIAAyAC4AMgAgAHAAcgBvAGYAaQBsAFUAbgBpAHcAZQByAHMAYQBsAG4AeQAg +AHAAcgBvAGYAaQBsACAAcwB6AGEAcgBvAVsAYwBpACAAZwBhAG0AbQBhACAAMgAsADIGOgYnBkUGJwAg +ADIALgAyACAGRAZIBkYAIAYxBkUGJwYvBkoAIAY5BicGRQQeBDEESQQwBE8AIARBBDUEQAQwBE8AIAQz +BDAEPAQ8BDAAIAAyACwAMgAtBD8EQAQ+BEQEOAQ7BEwARwBlAG4AZQByAGkAYwAgAEcAcgBhAHkAIABH +AGEAbQBtAGEAIAAyAC4AMgAgAFAAcgBvAGYAaQBsAGUAAHRleHQAAAAAQ29weXJpZ2h0IEFwcGxlIElu +Yy4sIDIwMTIAAFhZWiAAAAAAAADzUQABAAAAARbMY3VydgAAAAAAAAQAAAAABQAKAA8AFAAZAB4AIwAo +AC0AMgA3ADsAQABFAEoATwBUAFkAXgBjAGgAbQByAHcAfACBAIYAiwCQAJUAmgCfAKQAqQCuALIAtwC8 +AMEAxgDLANAA1QDbAOAA5QDrAPAA9gD7AQEBBwENARMBGQEfASUBKwEyATgBPgFFAUwBUgFZAWABZwFu +AXUBfAGDAYsBkgGaAaEBqQGxAbkBwQHJAdEB2QHhAekB8gH6AgMCDAIUAh0CJgIvAjgCQQJLAlQCXQJn +AnECegKEAo4CmAKiAqwCtgLBAssC1QLgAusC9QMAAwsDFgMhAy0DOANDA08DWgNmA3IDfgOKA5YDogOu +A7oDxwPTA+AD7AP5BAYEEwQgBC0EOwRIBFUEYwRxBH4EjASaBKgEtgTEBNME4QTwBP4FDQUcBSsFOgVJ +BVgFZwV3BYYFlgWmBbUFxQXVBeUF9gYGBhYGJwY3BkgGWQZqBnsGjAadBq8GwAbRBuMG9QcHBxkHKwc9 +B08HYQd0B4YHmQesB78H0gflB/gICwgfCDIIRghaCG4IggiWCKoIvgjSCOcI+wkQCSUJOglPCWQJeQmP +CaQJugnPCeUJ+woRCicKPQpUCmoKgQqYCq4KxQrcCvMLCwsiCzkLUQtpC4ALmAuwC8gL4Qv5DBIMKgxD +DFwMdQyODKcMwAzZDPMNDQ0mDUANWg10DY4NqQ3DDd4N+A4TDi4OSQ5kDn8Omw62DtIO7g8JDyUPQQ9e +D3oPlg+zD88P7BAJECYQQxBhEH4QmxC5ENcQ9RETETERTxFtEYwRqhHJEegSBxImEkUSZBKEEqMSwxLj +EwMTIxNDE2MTgxOkE8UT5RQGFCcUSRRqFIsUrRTOFPAVEhU0FVYVeBWbFb0V4BYDFiYWSRZsFo8WshbW +FvoXHRdBF2UXiReuF9IX9xgbGEAYZRiKGK8Y1Rj6GSAZRRlrGZEZtxndGgQaKhpRGncanhrFGuwbFBs7 +G2MbihuyG9ocAhwqHFIcexyjHMwc9R0eHUcdcB2ZHcMd7B4WHkAeah6UHr4e6R8THz4faR+UH78f6iAV +IEEgbCCYIMQg8CEcIUghdSGhIc4h+yInIlUigiKvIt0jCiM4I2YjlCPCI/AkHyRNJHwkqyTaJQklOCVo +JZclxyX3JicmVyaHJrcm6CcYJ0kneierJ9woDSg/KHEooijUKQYpOClrKZ0p0CoCKjUqaCqbKs8rAis2 +K2krnSvRLAUsOSxuLKIs1y0MLUEtdi2rLeEuFi5MLoIuty7uLyQvWi+RL8cv/jA1MGwwpDDbMRIxSjGC +Mbox8jIqMmMymzLUMw0zRjN/M7gz8TQrNGU0njTYNRM1TTWHNcI1/TY3NnI2rjbpNyQ3YDecN9c4FDhQ +OIw4yDkFOUI5fzm8Ofk6Njp0OrI67zstO2s7qjvoPCc8ZTykPOM9Ij1hPaE94D4gPmA+oD7gPyE/YT+i +P+JAI0BkQKZA50EpQWpBrEHuQjBCckK1QvdDOkN9Q8BEA0RHRIpEzkUSRVVFmkXeRiJGZ0arRvBHNUd7 +R8BIBUhLSJFI10kdSWNJqUnwSjdKfUrESwxLU0uaS+JMKkxyTLpNAk1KTZNN3E4lTm5Ot08AT0lPk0/d +UCdQcVC7UQZRUFGbUeZSMVJ8UsdTE1NfU6pT9lRCVI9U21UoVXVVwlYPVlxWqVb3V0RXklfgWC9YfVjL +WRpZaVm4WgdaVlqmWvVbRVuVW+VcNVyGXNZdJ114XcleGl5sXr1fD19hX7NgBWBXYKpg/GFPYaJh9WJJ +Ypxi8GNDY5dj62RAZJRk6WU9ZZJl52Y9ZpJm6Gc9Z5Nn6Wg/aJZo7GlDaZpp8WpIap9q92tPa6dr/2xX +bK9tCG1gbbluEm5rbsRvHm94b9FwK3CGcOBxOnGVcfByS3KmcwFzXXO4dBR0cHTMdSh1hXXhdj52m3b4 +d1Z3s3gReG54zHkqeYl553pGeqV7BHtje8J8IXyBfOF9QX2hfgF+Yn7CfyN/hH/lgEeAqIEKgWuBzYIw +gpKC9INXg7qEHYSAhOOFR4Wrhg6GcobXhzuHn4gEiGmIzokziZmJ/opkisqLMIuWi/yMY4zKjTGNmI3/ +jmaOzo82j56QBpBukNaRP5GokhGSepLjk02TtpQglIqU9JVflcmWNJaflwqXdZfgmEyYuJkkmZCZ/Jpo +mtWbQpuvnByciZz3nWSd0p5Anq6fHZ+Ln/qgaaDYoUehtqImopajBqN2o+akVqTHpTilqaYapoum/adu +p+CoUqjEqTepqaocqo+rAqt1q+msXKzQrUStuK4trqGvFq+LsACwdbDqsWCx1rJLssKzOLOutCW0nLUT +tYq2AbZ5tvC3aLfguFm40blKucK6O7q1uy67p7whvJu9Fb2Pvgq+hL7/v3q/9cBwwOzBZ8Hjwl/C28NY +w9TEUcTOxUvFyMZGxsPHQce/yD3IvMk6ybnKOMq3yzbLtsw1zLXNNc21zjbOts83z7jQOdC60TzRvtI/ +0sHTRNPG1EnUy9VO1dHWVdbY11zX4Nhk2OjZbNnx2nba+9uA3AXcit0Q3ZbeHN6i3ynfr+A24L3hROHM +4lPi2+Nj4+vkc+T85YTmDeaW5x/nqegy6LzpRunQ6lvq5etw6/vshu0R7ZzuKO6070DvzPBY8OXxcvH/ +8ozzGfOn9DT0wvVQ9d72bfb794r4Gfio+Tj5x/pX+uf7d/wH/Jj9Kf26/kv+3P9t///SLS4vMFokY2xh +c3NuYW1lWCRjbGFzc2VzXxAQTlNCaXRtYXBJbWFnZVJlcKMvMTJaTlNJbWFnZVJlcFhOU09iamVjdNIt +LjQ1V05TQXJyYXmiNDLSLS43OF5OU011dGFibGVBcnJheaM3NDLTOjsOPD0+V05TV2hpdGVcTlNDb2xv +clNwYWNlRDAgMAAQA4AM0i0uQEFXTlNDb2xvcqJAMtItLkNEV05TSW1hZ2WiQzIACAARABoAJAApADIA +NwBJAEwAUQBTAGIAaAB3AH4AhQCSAJkApQCtALwAvgDAAMUAxwDJAMsAzQDUANkA5ADmAOgA6gDvAPIA +9AD2APgA/wEWATIBNAE2E6YTqxO2E78T0hPWE+ET6hPvE/cT+hP/FA4UEhQZFCEULhQzFDUUNxQ8FEQU +RxRMFFQAAAAAAAACAQAAAAAAAABFAAAAAAAAAAAAAAAAAAAUVw diff --git a/i18n/src/main/resources/ja.lproj/Command.xib b/i18n/src/main/resources/ja.lproj/Command.xib index a8bfdc4862..5544201c08 100644 --- a/i18n/src/main/resources/ja.lproj/Command.xib +++ b/i18n/src/main/resources/ja.lproj/Command.xib @@ -1,7 +1,7 @@ - + - + diff --git a/i18n/src/main/resources/ja.lproj/Connection.xib b/i18n/src/main/resources/ja.lproj/Connection.xib index 34ec30e109..e4f0120fa8 100644 --- a/i18n/src/main/resources/ja.lproj/Connection.xib +++ b/i18n/src/main/resources/ja.lproj/Connection.xib @@ -1,7 +1,7 @@ - + - + @@ -76,7 +76,7 @@ - + diff --git a/i18n/src/main/resources/ja.lproj/Info.xib b/i18n/src/main/resources/ja.lproj/Info.xib index cd4b7f99d6..153573e1f0 100644 --- a/i18n/src/main/resources/ja.lproj/Info.xib +++ b/i18n/src/main/resources/ja.lproj/Info.xib @@ -1,7 +1,7 @@ - + - + @@ -89,7 +89,7 @@ - + @@ -1017,21 +1017,21 @@ - + - + - + @@ -1121,21 +1121,21 @@ - + - + - + @@ -1250,27 +1250,27 @@ diff --git a/i18n/src/main/resources/ja.lproj/Login.strings b/i18n/src/main/resources/ja.lproj/Login.strings index 99abfe7a0c..69a138b227 100644 Binary files a/i18n/src/main/resources/ja.lproj/Login.strings and b/i18n/src/main/resources/ja.lproj/Login.strings differ diff --git a/i18n/src/main/resources/ja.lproj/Main.xib b/i18n/src/main/resources/ja.lproj/Main.xib index a1bbba4687..50911cc6b2 100644 --- a/i18n/src/main/resources/ja.lproj/Main.xib +++ b/i18n/src/main/resources/ja.lproj/Main.xib @@ -1,7 +1,7 @@ - + - + @@ -159,19 +159,19 @@ - + - + - + @@ -321,12 +321,12 @@ CA - + - + @@ -566,6 +566,10 @@ CA - + + + + + diff --git a/i18n/src/main/resources/ja.lproj/Preferences.xib b/i18n/src/main/resources/ja.lproj/Preferences.xib index e1868cf7a8..e09c934171 100644 --- a/i18n/src/main/resources/ja.lproj/Preferences.xib +++ b/i18n/src/main/resources/ja.lproj/Preferences.xib @@ -1,7 +1,7 @@ - + - + @@ -266,7 +266,7 @@ - + @@ -307,7 +307,7 @@ - + @@ -538,7 +538,7 @@ - + @@ -578,7 +578,7 @@ - + @@ -613,7 +613,7 @@ - + @@ -642,7 +642,7 @@ - + @@ -1218,7 +1218,7 @@ - + @@ -1250,7 +1250,7 @@ - + @@ -1289,7 +1289,7 @@ - + @@ -1362,7 +1362,7 @@ - + @@ -1557,7 +1557,7 @@ - + @@ -1617,7 +1617,7 @@ - + @@ -1642,7 +1642,7 @@ - + @@ -1684,7 +1684,7 @@ - + @@ -1712,7 +1712,7 @@ - + @@ -1740,7 +1740,7 @@ - + @@ -1768,7 +1768,7 @@ - + @@ -1803,7 +1803,7 @@ - + @@ -1886,7 +1886,7 @@ - + @@ -1945,7 +1945,7 @@ - + @@ -1973,7 +1973,7 @@ - + @@ -2001,7 +2001,7 @@ - + diff --git a/i18n/src/main/resources/ja.lproj/Profiles.xib b/i18n/src/main/resources/ja.lproj/Profiles.xib index 18af5a58f4..20e5ce0f36 100644 --- a/i18n/src/main/resources/ja.lproj/Profiles.xib +++ b/i18n/src/main/resources/ja.lproj/Profiles.xib @@ -1,7 +1,7 @@ - + - + diff --git a/i18n/src/main/resources/ja.lproj/Prompt.xib b/i18n/src/main/resources/ja.lproj/Prompt.xib index 0d0965c871..2b80cb7558 100644 --- a/i18n/src/main/resources/ja.lproj/Prompt.xib +++ b/i18n/src/main/resources/ja.lproj/Prompt.xib @@ -1,7 +1,7 @@ - + - + @@ -95,7 +95,7 @@ Gw - + diff --git a/i18n/src/main/resources/ja.lproj/S3.strings b/i18n/src/main/resources/ja.lproj/S3.strings index 748b2f15c2..6b2654dab3 100644 Binary files a/i18n/src/main/resources/ja.lproj/S3.strings and b/i18n/src/main/resources/ja.lproj/S3.strings differ diff --git a/i18n/src/main/resources/ja.lproj/Updater.strings b/i18n/src/main/resources/ja.lproj/Updater.strings index f16723a3f9..dc14abc948 100644 Binary files a/i18n/src/main/resources/ja.lproj/Updater.strings and b/i18n/src/main/resources/ja.lproj/Updater.strings differ diff --git a/i18n/src/main/resources/ka.lproj/Bookmark.xib b/i18n/src/main/resources/ka.lproj/Bookmark.xib index 2aa5306b7a..a57c6a84e1 100644 --- a/i18n/src/main/resources/ka.lproj/Bookmark.xib +++ b/i18n/src/main/resources/ka.lproj/Bookmark.xib @@ -1,7 +1,7 @@ - + - + @@ -92,7 +92,7 @@ - + @@ -489,95 +489,95 @@ YnBsaXN0MDDUAQIDBAUGBwpYJHZlcnNpb25ZJGFyY2hpdmVyVCR0b3BYJG9iamVjdHMSAAGGoF8QD05T -S2V5ZWRBcmNoaXZlctEICVRyb290gAGuCwwZGh8UJCkqMTQ3PUBVJG51bGzWDQ4PEBESExQVFhcYVk5T -U2l6ZV5OU1Jlc2l6aW5nTW9kZVYkY2xhc3NcTlNJbWFnZUZsYWdzVk5TUmVwc1dOU0NvbG9ygAIQAIAN -EiDDAACAA4ALVnsxLCAxfdIbDxweWk5TLm9iamVjdHOhHYAEgArSGw8gI6IhIoAFgAaACdMPJSYnKBRf -EBROU1RJRkZSZXByZXNlbnRhdGlvbl8QGU5TSW50ZXJuYWxMYXlvdXREaXJlY3Rpb26ACIAHTxESbE1N -ACoAAAAKAAAAEAEAAAMAAAABAAEAAAEBAAMAAAABAAEAAAECAAMAAAACAAgACAEDAAMAAAABAAEAAAEG -AAMAAAABAAEAAAEKAAMAAAABAAEAAAERAAQAAAABAAAACAESAAMAAAABAAEAAAEVAAMAAAABAAIAAAEW -AAMAAAABAAEAAAEXAAQAAAABAAAAAgEcAAMAAAABAAEAAAEoAAMAAAABAAIAAAFSAAMAAAABAAEAAAFT -AAMAAAACAAEAAYdzAAcAABGcAAAA0AAAAAAAABGcYXBwbAIAAABtbnRyR1JBWVhZWiAH3AAIABcADwAu -AA9hY3NwQVBQTAAAAABub25lAAAAAAAAAAAAAAAAAAAAAAAA9tYAAQAAAADTLWFwcGwAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAVkZXNjAAAAwAAAAHlkc2NtAAABPAAA -CBpjcHJ0AAAJWAAAACN3dHB0AAAJfAAAABRrVFJDAAAJkAAACAxkZXNjAAAAAAAAAB9HZW5lcmljIEdy -YXkgR2FtbWEgMi4yIFByb2ZpbGUAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAbWx1YwAAAAAAAAAfAAAADHNr -U0sAAAAuAAABhGRhREsAAAA6AAABsmNhRVMAAAA4AAAB7HZpVk4AAABAAAACJHB0QlIAAABKAAACZHVr -VUEAAAAsAAACrmZyRlUAAAA+AAAC2mh1SFUAAAA0AAADGHpoVFcAAAAaAAADTGtvS1IAAAAiAAADZm5i -Tk8AAAA6AAADiGNzQ1oAAAAoAAADwmhlSUwAAAAkAAAD6nJvUk8AAAAqAAAEDmRlREUAAABOAAAEOGl0 -SVQAAABOAAAEhnN2U0UAAAA4AAAE1HpoQ04AAAAaAAAFDGphSlAAAAAmAAAFJmVsR1IAAAAqAAAFTHB0 -UE8AAABSAAAFdm5sTkwAAABAAAAFyGVzRVMAAABMAAAGCHRoVEgAAAAyAAAGVHRyVFIAAAAkAAAGhmZp -RkkAAABGAAAGqmhySFIAAAA+AAAG8HBsUEwAAABKAAAHLmFyRUcAAAAsAAAHeHJ1UlUAAAA6AAAHpGVu -VVMAAAA8AAAH3gBWAWEAZQBvAGIAZQBjAG4A4QAgAHMAaQB2AOEAIABnAGEAbQBhACAAMgAsADIARwBl -AG4AZQByAGkAcwBrACAAZwByAOUAIAAyACwAMgAgAGcAYQBtAG0AYQAtAHAAcgBvAGYAaQBsAEcAYQBt -AG0AYQAgAGQAZQAgAGcAcgBpAHMAbwBzACAAZwBlAG4A6AByAGkAYwBhACAAMgAuADIAQx6lAHUAIABo -AOwAbgBoACAATQDgAHUAIAB4AOEAbQAgAEMAaAB1AG4AZwAgAEcAYQBtAG0AYQAgADIALgAyAFAAZQBy -AGYAaQBsACAARwBlAG4A6QByAGkAYwBvACAAZABhACAARwBhAG0AYQAgAGQAZQAgAEMAaQBuAHoAYQBz -ACAAMgAsADIEFwQwBDMEMAQ7BEwEPQQwACAARwByAGEAeQAtBDMEMAQ8BDAAIAAyAC4AMgBQAHIAbwBm -AGkAbAAgAGcA6QBuAOkAcgBpAHEAdQBlACAAZwByAGkAcwAgAGcAYQBtAG0AYQAgADIALAAyAMEAbAB0 -AGEAbADhAG4AbwBzACAAcwB6APwAcgBrAGUAIABnAGEAbQBtAGEAIAAyAC4AMpAadShwcJaOUUlepgAy -AC4AMoJyX2ljz4/wx3y8GAAg1ozAyQAgrBC5yAAgADIALgAyACDVBLhc0wzHfABHAGUAbgBlAHIAaQBz -AGsAIABnAHIA5QAgAGcAYQBtAG0AYQAgADIALAAyAC0AcAByAG8AZgBpAGwATwBiAGUAYwBuAOEAIAFh -AGUAZADhACAAZwBhAG0AYQAgADIALgAyBdIF0AXeBdQAIAXQBeQF1QXoACAF2wXcBdwF2QAgADIALgAy -AEcAYQBtAGEAIABnAHIAaQAgAGcAZQBuAGUAcgBpAGMBAwAgADIALAAyAEEAbABsAGcAZQBtAGUAaQBu -AGUAcwAgAEcAcgBhAHUAcwB0AHUAZgBlAG4ALQBQAHIAbwBmAGkAbAAgAEcAYQBtAG0AYQAgADIALAAy -AFAAcgBvAGYAaQBsAG8AIABnAHIAaQBnAGkAbwAgAGcAZQBuAGUAcgBpAGMAbwAgAGQAZQBsAGwAYQAg -AGcAYQBtAG0AYQAgADIALAAyAEcAZQBuAGUAcgBpAHMAawAgAGcAcgDlACAAMgAsADIAIABnAGEAbQBt -AGEAcAByAG8AZgBpAGxmbpAacHBepnz7ZXAAMgAuADJjz4/wZYdO9k4AgiwwsDDsMKQwrDDzMN4AIAAy -AC4AMgAgMNcw7TDVMKEwpDDrA5MDtQO9A7kDugPMACADkwO6A8EDuQAgA5MDrAO8A7wDsQAgADIALgAy -AFAAZQByAGYAaQBsACAAZwBlAG4A6QByAGkAYwBvACAAZABlACAAYwBpAG4AegBlAG4AdABvAHMAIABk -AGEAIABHAGEAbQBtAGEAIAAyACwAMgBBAGwAZwBlAG0AZQBlAG4AIABnAHIAaQBqAHMAIABnAGEAbQBt -AGEAIAAyACwAMgAtAHAAcgBvAGYAaQBlAGwAUABlAHIAZgBpAGwAIABnAGUAbgDpAHIAaQBjAG8AIABk -AGUAIABnAGEAbQBtAGEAIABkAGUAIABnAHIAaQBzAGUAcwAgADIALAAyDiMOMQ4HDioONQ5BDgEOIQ4h -DjIOQA4BDiMOIg5MDhcOMQ5IDicORA4bACAAMgAuADIARwBlAG4AZQBsACAARwByAGkAIABHAGEAbQBh -ACAAMgAsADIAWQBsAGUAaQBuAGUAbgAgAGgAYQByAG0AYQBhAG4AIABnAGEAbQBtAGEAIAAyACwAMgAg -AC0AcAByAG8AZgBpAGkAbABpAEcAZQBuAGUAcgBpAQ0AawBpACAARwByAGEAeQAgAEcAYQBtAG0AYQAg -ADIALgAyACAAcAByAG8AZgBpAGwAVQBuAGkAdwBlAHIAcwBhAGwAbgB5ACAAcAByAG8AZgBpAGwAIABz -AHoAYQByAG8BWwBjAGkAIABnAGEAbQBtAGEAIAAyACwAMgY6BicGRQYnACAAMgAuADIAIAZEBkgGRgAg -BjEGRQYnBi8GSgAgBjkGJwZFBB4EMQRJBDAETwAgBEEENQRABDAETwAgBDMEMAQ8BDwEMAAgADIALAAy -AC0EPwRABD4ERAQ4BDsETABHAGUAbgBlAHIAaQBjACAARwByAGEAeQAgAEcAYQBtAG0AYQAgADIALgAy -ACAAUAByAG8AZgBpAGwAZQAAdGV4dAAAAABDb3B5cmlnaHQgQXBwbGUgSW5jLiwgMjAxMgAAWFlaIAAA -AAAAAPNRAAEAAAABFsxjdXJ2AAAAAAAABAAAAAAFAAoADwAUABkAHgAjACgALQAyADcAOwBAAEUASgBP -AFQAWQBeAGMAaABtAHIAdwB8AIEAhgCLAJAAlQCaAJ8ApACpAK4AsgC3ALwAwQDGAMsA0ADVANsA4ADl -AOsA8AD2APsBAQEHAQ0BEwEZAR8BJQErATIBOAE+AUUBTAFSAVkBYAFnAW4BdQF8AYMBiwGSAZoBoQGp -AbEBuQHBAckB0QHZAeEB6QHyAfoCAwIMAhQCHQImAi8COAJBAksCVAJdAmcCcQJ6AoQCjgKYAqICrAK2 -AsECywLVAuAC6wL1AwADCwMWAyEDLQM4A0MDTwNaA2YDcgN+A4oDlgOiA64DugPHA9MD4APsA/kEBgQT -BCAELQQ7BEgEVQRjBHEEfgSMBJoEqAS2BMQE0wThBPAE/gUNBRwFKwU6BUkFWAVnBXcFhgWWBaYFtQXF -BdUF5QX2BgYGFgYnBjcGSAZZBmoGewaMBp0GrwbABtEG4wb1BwcHGQcrBz0HTwdhB3QHhgeZB6wHvwfS -B+UH+AgLCB8IMghGCFoIbgiCCJYIqgi+CNII5wj7CRAJJQk6CU8JZAl5CY8JpAm6Cc8J5Qn7ChEKJwo9 -ClQKagqBCpgKrgrFCtwK8wsLCyILOQtRC2kLgAuYC7ALyAvhC/kMEgwqDEMMXAx1DI4MpwzADNkM8w0N -DSYNQA1aDXQNjg2pDcMN3g34DhMOLg5JDmQOfw6bDrYO0g7uDwkPJQ9BD14Peg+WD7MPzw/sEAkQJhBD -EGEQfhCbELkQ1xD1ERMRMRFPEW0RjBGqEckR6BIHEiYSRRJkEoQSoxLDEuMTAxMjE0MTYxODE6QTxRPl -FAYUJxRJFGoUixStFM4U8BUSFTQVVhV4FZsVvRXgFgMWJhZJFmwWjxayFtYW+hcdF0EXZReJF64X0hf3 -GBsYQBhlGIoYrxjVGPoZIBlFGWsZkRm3Gd0aBBoqGlEadxqeGsUa7BsUGzsbYxuKG7Ib2hwCHCocUhx7 -HKMczBz1HR4dRx1wHZkdwx3sHhYeQB5qHpQevh7pHxMfPh9pH5Qfvx/qIBUgQSBsIJggxCDwIRwhSCF1 -IaEhziH7IiciVSKCIq8i3SMKIzgjZiOUI8Ij8CQfJE0kfCSrJNolCSU4JWgllyXHJfcmJyZXJocmtybo -JxgnSSd6J6sn3CgNKD8ocSiiKNQpBik4KWspnSnQKgIqNSpoKpsqzysCKzYraSudK9EsBSw5LG4soizX -LQwtQS12Last4S4WLkwugi63Lu4vJC9aL5Evxy/+MDUwbDCkMNsxEjFKMYIxujHyMioyYzKbMtQzDTNG -M38zuDPxNCs0ZTSeNNg1EzVNNYc1wjX9Njc2cjauNuk3JDdgN5w31zgUOFA4jDjIOQU5Qjl/Obw5+To2 -OnQ6sjrvOy07azuqO+g8JzxlPKQ84z0iPWE9oT3gPiA+YD6gPuA/IT9hP6I/4kAjQGRApkDnQSlBakGs -Qe5CMEJyQrVC90M6Q31DwEQDREdEikTORRJFVUWaRd5GIkZnRqtG8Ec1R3tHwEgFSEtIkUjXSR1JY0mp -SfBKN0p9SsRLDEtTS5pL4kwqTHJMuk0CTUpNk03cTiVObk63TwBPSU+TT91QJ1BxULtRBlFQUZtR5lIx -UnxSx1MTU19TqlP2VEJUj1TbVShVdVXCVg9WXFapVvdXRFeSV+BYL1h9WMtZGllpWbhaB1pWWqZa9VtF -W5Vb5Vw1XIZc1l0nXXhdyV4aXmxevV8PX2Ffs2AFYFdgqmD8YU9homH1YklinGLwY0Njl2PrZEBklGTp -ZT1lkmXnZj1mkmboZz1nk2fpaD9olmjsaUNpmmnxakhqn2r3a09rp2v/bFdsr20IbWBtuW4SbmtuxG8e -b3hv0XArcIZw4HE6cZVx8HJLcqZzAXNdc7h0FHRwdMx1KHWFdeF2Pnabdvh3VnezeBF4bnjMeSp5iXnn -ekZ6pXsEe2N7wnwhfIF84X1BfaF+AX5ifsJ/I3+Ef+WAR4CogQqBa4HNgjCCkoL0g1eDuoQdhICE44VH -hauGDoZyhteHO4efiASIaYjOiTOJmYn+imSKyoswi5aL/IxjjMqNMY2Yjf+OZo7OjzaPnpAGkG6Q1pE/ -kaiSEZJ6kuOTTZO2lCCUipT0lV+VyZY0lp+XCpd1l+CYTJi4mSSZkJn8mmia1ZtCm6+cHJyJnPedZJ3S -nkCerp8dn4uf+qBpoNihR6G2oiailqMGo3aj5qRWpMelOKWpphqmi6b9p26n4KhSqMSpN6mpqhyqj6sC -q3Wr6axcrNCtRK24ri2uoa8Wr4uwALB1sOqxYLHWskuywrM4s660JbSctRO1irYBtnm28Ldot+C4WbjR -uUq5wro7urW7LrunvCG8m70VvY++Cr6Evv+/er/1wHDA7MFnwePCX8Lbw1jD1MRRxM7FS8XIxkbGw8dB -x7/IPci8yTrJuco4yrfLNsu2zDXMtc01zbXONs62zzfPuNA50LrRPNG+0j/SwdNE08bUSdTL1U7V0dZV -1tjXXNfg2GTY6Nls2fHadtr724DcBdyK3RDdlt4c3qLfKd+v4DbgveFE4cziU+Lb42Pj6+Rz5PzlhOYN -5pbnH+ep6DLovOlG6dDqW+rl63Dr++yG7RHtnO4o7rTvQO/M8Fjw5fFy8f/yjPMZ86f0NPTC9VD13vZt -9vv3ivgZ+Kj5OPnH+lf65/t3/Af8mP0p/br+S/7c/23//9IrLC0uWiRjbGFzc25hbWVYJGNsYXNzZXNf -EBBOU0JpdG1hcEltYWdlUmVwoy0vMFpOU0ltYWdlUmVwWE5TT2JqZWN00issMjNXTlNBcnJheaIyMNIr -LDU2Xk5TTXV0YWJsZUFycmF5ozUyMNM4OQ86OzxXTlNXaGl0ZVxOU0NvbG9yU3BhY2VEMCAwABADgAzS -Kyw+P1dOU0NvbG9yoj4w0issQUJXTlNJbWFnZaJBMAAIABEAGgAkACkAMgA3AEkATABRAFMAYgBoAHUA -fACLAJIAnwCmAK4AsACyALQAuQC7AL0AxADJANQA1gDYANoA3wDiAOQA5gDoAO8BBgEiASQBJhOWE5sT -phOvE8ITxhPRE9oT3xPnE+oT7xP+FAIUCRQRFB4UIxQlFCcULBQ0FDcUPBREAAAAAAAAAgEAAAAAAAAA -QwAAAAAAAAAAAAAAAAAAFEc +S2V5ZWRBcmNoaXZlctEICVRyb290gAGuCwwbHCEaJissMzY5P0JVJG51bGzXDQ4PEBESExQVFhcYGRpW +TlNTaXplViRjbGFzc1xOU0ltYWdlRmxhZ3NWTlNSZXBzW05TVGludENvbG9yV05TQ29sb3JeTlNSZXNp +emluZ01vZGWAAoANEiDDAACAA4AAgAsQAFZ7MSwgMX3SHQ4eIFpOUy5vYmplY3RzoR+ABIAK0h0OIiWi +IySABYAGgAnTDicoKSoaXxAUTlNUSUZGUmVwcmVzZW50YXRpb25fEBlOU0ludGVybmFsTGF5b3V0RGly +ZWN0aW9ugAiAB08REmxNTQAqAAAACgAAABABAAADAAAAAQABAAABAQADAAAAAQABAAABAgADAAAAAgAI +AAgBAwADAAAAAQABAAABBgADAAAAAQABAAABCgADAAAAAQABAAABEQAEAAAAAQAAAAgBEgADAAAAAQAB +AAABFQADAAAAAQACAAABFgADAAAAAQABAAABFwAEAAAAAQAAAAIBHAADAAAAAQABAAABKAADAAAAAQAC +AAABUgADAAAAAQABAAABUwADAAAAAgABAAGHcwAHAAARnAAAANAAAAAAAAARnGFwcGwCAAAAbW50ckdS +QVlYWVogB9wACAAXAA8ALgAPYWNzcEFQUEwAAAAAbm9uZQAAAAAAAAAAAAAAAAAAAAAAAPbWAAEAAAAA +0y1hcHBsAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFZGVzYwAA +AMAAAAB5ZHNjbQAAATwAAAgaY3BydAAACVgAAAAjd3RwdAAACXwAAAAUa1RSQwAACZAAAAgMZGVzYwAA +AAAAAAAfR2VuZXJpYyBHcmF5IEdhbW1hIDIuMiBQcm9maWxlAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAG1s +dWMAAAAAAAAAHwAAAAxza1NLAAAALgAAAYRkYURLAAAAOgAAAbJjYUVTAAAAOAAAAex2aVZOAAAAQAAA +AiRwdEJSAAAASgAAAmR1a1VBAAAALAAAAq5mckZVAAAAPgAAAtpodUhVAAAANAAAAxh6aFRXAAAAGgAA +A0xrb0tSAAAAIgAAA2ZuYk5PAAAAOgAAA4hjc0NaAAAAKAAAA8JoZUlMAAAAJAAAA+pyb1JPAAAAKgAA +BA5kZURFAAAATgAABDhpdElUAAAATgAABIZzdlNFAAAAOAAABNR6aENOAAAAGgAABQxqYUpQAAAAJgAA +BSZlbEdSAAAAKgAABUxwdFBPAAAAUgAABXZubE5MAAAAQAAABchlc0VTAAAATAAABgh0aFRIAAAAMgAA +BlR0clRSAAAAJAAABoZmaUZJAAAARgAABqpockhSAAAAPgAABvBwbFBMAAAASgAABy5hckVHAAAALAAA +B3hydVJVAAAAOgAAB6RlblVTAAAAPAAAB94AVgFhAGUAbwBiAGUAYwBuAOEAIABzAGkAdgDhACAAZwBh +AG0AYQAgADIALAAyAEcAZQBuAGUAcgBpAHMAawAgAGcAcgDlACAAMgAsADIAIABnAGEAbQBtAGEALQBw +AHIAbwBmAGkAbABHAGEAbQBtAGEAIABkAGUAIABnAHIAaQBzAG8AcwAgAGcAZQBuAOgAcgBpAGMAYQAg +ADIALgAyAEMepQB1ACAAaADsAG4AaAAgAE0A4AB1ACAAeADhAG0AIABDAGgAdQBuAGcAIABHAGEAbQBt +AGEAIAAyAC4AMgBQAGUAcgBmAGkAbAAgAEcAZQBuAOkAcgBpAGMAbwAgAGQAYQAgAEcAYQBtAGEAIABk +AGUAIABDAGkAbgB6AGEAcwAgADIALAAyBBcEMAQzBDAEOwRMBD0EMAAgAEcAcgBhAHkALQQzBDAEPAQw +ACAAMgAuADIAUAByAG8AZgBpAGwAIABnAOkAbgDpAHIAaQBxAHUAZQAgAGcAcgBpAHMAIABnAGEAbQBt +AGEAIAAyACwAMgDBAGwAdABhAGwA4QBuAG8AcwAgAHMAegD8AHIAawBlACAAZwBhAG0AbQBhACAAMgAu +ADKQGnUocHCWjlFJXqYAMgAuADKCcl9pY8+P8Md8vBgAINaMwMkAIKwQucgAIAAyAC4AMgAg1QS4XNMM +x3wARwBlAG4AZQByAGkAcwBrACAAZwByAOUAIABnAGEAbQBtAGEAIAAyACwAMgAtAHAAcgBvAGYAaQBs +AE8AYgBlAGMAbgDhACABYQBlAGQA4QAgAGcAYQBtAGEAIAAyAC4AMgXSBdAF3gXUACAF0AXkBdUF6AAg +BdsF3AXcBdkAIAAyAC4AMgBHAGEAbQBhACAAZwByAGkAIABnAGUAbgBlAHIAaQBjAQMAIAAyACwAMgBB +AGwAbABnAGUAbQBlAGkAbgBlAHMAIABHAHIAYQB1AHMAdAB1AGYAZQBuAC0AUAByAG8AZgBpAGwAIABH +AGEAbQBtAGEAIAAyACwAMgBQAHIAbwBmAGkAbABvACAAZwByAGkAZwBpAG8AIABnAGUAbgBlAHIAaQBj +AG8AIABkAGUAbABsAGEAIABnAGEAbQBtAGEAIAAyACwAMgBHAGUAbgBlAHIAaQBzAGsAIABnAHIA5QAg +ADIALAAyACAAZwBhAG0AbQBhAHAAcgBvAGYAaQBsZm6QGnBwXqZ8+2VwADIALgAyY8+P8GWHTvZOAIIs +MLAw7DCkMKww8zDeACAAMgAuADIAIDDXMO0w1TChMKQw6wOTA7UDvQO5A7oDzAAgA5MDugPBA7kAIAOT +A6wDvAO8A7EAIAAyAC4AMgBQAGUAcgBmAGkAbAAgAGcAZQBuAOkAcgBpAGMAbwAgAGQAZQAgAGMAaQBu +AHoAZQBuAHQAbwBzACAAZABhACAARwBhAG0AbQBhACAAMgAsADIAQQBsAGcAZQBtAGUAZQBuACAAZwBy +AGkAagBzACAAZwBhAG0AbQBhACAAMgAsADIALQBwAHIAbwBmAGkAZQBsAFAAZQByAGYAaQBsACAAZwBl +AG4A6QByAGkAYwBvACAAZABlACAAZwBhAG0AbQBhACAAZABlACAAZwByAGkAcwBlAHMAIAAyACwAMg4j +DjEOBw4qDjUOQQ4BDiEOIQ4yDkAOAQ4jDiIOTA4XDjEOSA4nDkQOGwAgADIALgAyAEcAZQBuAGUAbAAg +AEcAcgBpACAARwBhAG0AYQAgADIALAAyAFkAbABlAGkAbgBlAG4AIABoAGEAcgBtAGEAYQBuACAAZwBh +AG0AbQBhACAAMgAsADIAIAAtAHAAcgBvAGYAaQBpAGwAaQBHAGUAbgBlAHIAaQENAGsAaQAgAEcAcgBh +AHkAIABHAGEAbQBtAGEAIAAyAC4AMgAgAHAAcgBvAGYAaQBsAFUAbgBpAHcAZQByAHMAYQBsAG4AeQAg +AHAAcgBvAGYAaQBsACAAcwB6AGEAcgBvAVsAYwBpACAAZwBhAG0AbQBhACAAMgAsADIGOgYnBkUGJwAg +ADIALgAyACAGRAZIBkYAIAYxBkUGJwYvBkoAIAY5BicGRQQeBDEESQQwBE8AIARBBDUEQAQwBE8AIAQz +BDAEPAQ8BDAAIAAyACwAMgAtBD8EQAQ+BEQEOAQ7BEwARwBlAG4AZQByAGkAYwAgAEcAcgBhAHkAIABH +AGEAbQBtAGEAIAAyAC4AMgAgAFAAcgBvAGYAaQBsAGUAAHRleHQAAAAAQ29weXJpZ2h0IEFwcGxlIElu +Yy4sIDIwMTIAAFhZWiAAAAAAAADzUQABAAAAARbMY3VydgAAAAAAAAQAAAAABQAKAA8AFAAZAB4AIwAo +AC0AMgA3ADsAQABFAEoATwBUAFkAXgBjAGgAbQByAHcAfACBAIYAiwCQAJUAmgCfAKQAqQCuALIAtwC8 +AMEAxgDLANAA1QDbAOAA5QDrAPAA9gD7AQEBBwENARMBGQEfASUBKwEyATgBPgFFAUwBUgFZAWABZwFu +AXUBfAGDAYsBkgGaAaEBqQGxAbkBwQHJAdEB2QHhAekB8gH6AgMCDAIUAh0CJgIvAjgCQQJLAlQCXQJn +AnECegKEAo4CmAKiAqwCtgLBAssC1QLgAusC9QMAAwsDFgMhAy0DOANDA08DWgNmA3IDfgOKA5YDogOu +A7oDxwPTA+AD7AP5BAYEEwQgBC0EOwRIBFUEYwRxBH4EjASaBKgEtgTEBNME4QTwBP4FDQUcBSsFOgVJ +BVgFZwV3BYYFlgWmBbUFxQXVBeUF9gYGBhYGJwY3BkgGWQZqBnsGjAadBq8GwAbRBuMG9QcHBxkHKwc9 +B08HYQd0B4YHmQesB78H0gflB/gICwgfCDIIRghaCG4IggiWCKoIvgjSCOcI+wkQCSUJOglPCWQJeQmP +CaQJugnPCeUJ+woRCicKPQpUCmoKgQqYCq4KxQrcCvMLCwsiCzkLUQtpC4ALmAuwC8gL4Qv5DBIMKgxD +DFwMdQyODKcMwAzZDPMNDQ0mDUANWg10DY4NqQ3DDd4N+A4TDi4OSQ5kDn8Omw62DtIO7g8JDyUPQQ9e +D3oPlg+zD88P7BAJECYQQxBhEH4QmxC5ENcQ9RETETERTxFtEYwRqhHJEegSBxImEkUSZBKEEqMSwxLj +EwMTIxNDE2MTgxOkE8UT5RQGFCcUSRRqFIsUrRTOFPAVEhU0FVYVeBWbFb0V4BYDFiYWSRZsFo8WshbW +FvoXHRdBF2UXiReuF9IX9xgbGEAYZRiKGK8Y1Rj6GSAZRRlrGZEZtxndGgQaKhpRGncanhrFGuwbFBs7 +G2MbihuyG9ocAhwqHFIcexyjHMwc9R0eHUcdcB2ZHcMd7B4WHkAeah6UHr4e6R8THz4faR+UH78f6iAV +IEEgbCCYIMQg8CEcIUghdSGhIc4h+yInIlUigiKvIt0jCiM4I2YjlCPCI/AkHyRNJHwkqyTaJQklOCVo +JZclxyX3JicmVyaHJrcm6CcYJ0kneierJ9woDSg/KHEooijUKQYpOClrKZ0p0CoCKjUqaCqbKs8rAis2 +K2krnSvRLAUsOSxuLKIs1y0MLUEtdi2rLeEuFi5MLoIuty7uLyQvWi+RL8cv/jA1MGwwpDDbMRIxSjGC +Mbox8jIqMmMymzLUMw0zRjN/M7gz8TQrNGU0njTYNRM1TTWHNcI1/TY3NnI2rjbpNyQ3YDecN9c4FDhQ +OIw4yDkFOUI5fzm8Ofk6Njp0OrI67zstO2s7qjvoPCc8ZTykPOM9Ij1hPaE94D4gPmA+oD7gPyE/YT+i +P+JAI0BkQKZA50EpQWpBrEHuQjBCckK1QvdDOkN9Q8BEA0RHRIpEzkUSRVVFmkXeRiJGZ0arRvBHNUd7 +R8BIBUhLSJFI10kdSWNJqUnwSjdKfUrESwxLU0uaS+JMKkxyTLpNAk1KTZNN3E4lTm5Ot08AT0lPk0/d +UCdQcVC7UQZRUFGbUeZSMVJ8UsdTE1NfU6pT9lRCVI9U21UoVXVVwlYPVlxWqVb3V0RXklfgWC9YfVjL +WRpZaVm4WgdaVlqmWvVbRVuVW+VcNVyGXNZdJ114XcleGl5sXr1fD19hX7NgBWBXYKpg/GFPYaJh9WJJ +Ypxi8GNDY5dj62RAZJRk6WU9ZZJl52Y9ZpJm6Gc9Z5Nn6Wg/aJZo7GlDaZpp8WpIap9q92tPa6dr/2xX +bK9tCG1gbbluEm5rbsRvHm94b9FwK3CGcOBxOnGVcfByS3KmcwFzXXO4dBR0cHTMdSh1hXXhdj52m3b4 +d1Z3s3gReG54zHkqeYl553pGeqV7BHtje8J8IXyBfOF9QX2hfgF+Yn7CfyN/hH/lgEeAqIEKgWuBzYIw +gpKC9INXg7qEHYSAhOOFR4Wrhg6GcobXhzuHn4gEiGmIzokziZmJ/opkisqLMIuWi/yMY4zKjTGNmI3/ +jmaOzo82j56QBpBukNaRP5GokhGSepLjk02TtpQglIqU9JVflcmWNJaflwqXdZfgmEyYuJkkmZCZ/Jpo +mtWbQpuvnByciZz3nWSd0p5Anq6fHZ+Ln/qgaaDYoUehtqImopajBqN2o+akVqTHpTilqaYapoum/adu +p+CoUqjEqTepqaocqo+rAqt1q+msXKzQrUStuK4trqGvFq+LsACwdbDqsWCx1rJLssKzOLOutCW0nLUT +tYq2AbZ5tvC3aLfguFm40blKucK6O7q1uy67p7whvJu9Fb2Pvgq+hL7/v3q/9cBwwOzBZ8Hjwl/C28NY +w9TEUcTOxUvFyMZGxsPHQce/yD3IvMk6ybnKOMq3yzbLtsw1zLXNNc21zjbOts83z7jQOdC60TzRvtI/ +0sHTRNPG1EnUy9VO1dHWVdbY11zX4Nhk2OjZbNnx2nba+9uA3AXcit0Q3ZbeHN6i3ynfr+A24L3hROHM +4lPi2+Nj4+vkc+T85YTmDeaW5x/nqegy6LzpRunQ6lvq5etw6/vshu0R7ZzuKO6070DvzPBY8OXxcvH/ +8ozzGfOn9DT0wvVQ9d72bfb794r4Gfio+Tj5x/pX+uf7d/wH/Jj9Kf26/kv+3P9t///SLS4vMFokY2xh +c3NuYW1lWCRjbGFzc2VzXxAQTlNCaXRtYXBJbWFnZVJlcKMvMTJaTlNJbWFnZVJlcFhOU09iamVjdNIt +LjQ1V05TQXJyYXmiNDLSLS43OF5OU011dGFibGVBcnJheaM3NDLTOjsOPD0+V05TV2hpdGVcTlNDb2xv +clNwYWNlRDAgMAAQA4AM0i0uQEFXTlNDb2xvcqJAMtItLkNEV05TSW1hZ2WiQzIACAARABoAJAApADIA +NwBJAEwAUQBTAGIAaAB3AH4AhQCSAJkApQCtALwAvgDAAMUAxwDJAMsAzQDUANkA5ADmAOgA6gDvAPIA +9AD2APgA/wEWATIBNAE2E6YTqxO2E78T0hPWE+ET6hPvE/cT+hP/FA4UEhQZFCEULhQzFDUUNxQ8FEQU +RxRMFFQAAAAAAAACAQAAAAAAAABFAAAAAAAAAAAAAAAAAAAUVw diff --git a/i18n/src/main/resources/ka.lproj/Browser.xib b/i18n/src/main/resources/ka.lproj/Browser.xib index 4c3dfaf773..03e4cbae7d 100644 --- a/i18n/src/main/resources/ka.lproj/Browser.xib +++ b/i18n/src/main/resources/ka.lproj/Browser.xib @@ -1,7 +1,7 @@ - + - + @@ -38,7 +38,7 @@ - + @@ -62,27 +62,27 @@ @@ -256,7 +256,7 @@ - + @@ -553,95 +553,95 @@ DQ YnBsaXN0MDDUAQIDBAUGBwpYJHZlcnNpb25ZJGFyY2hpdmVyVCR0b3BYJG9iamVjdHMSAAGGoF8QD05T -S2V5ZWRBcmNoaXZlctEICVRyb290gAGuCwwZGh8UJCkqMTQ3PUBVJG51bGzWDQ4PEBESExQVFhcYVk5T -U2l6ZV5OU1Jlc2l6aW5nTW9kZVYkY2xhc3NcTlNJbWFnZUZsYWdzVk5TUmVwc1dOU0NvbG9ygAIQAIAN -EiDDAACAA4ALVnsxLCAxfdIbDxweWk5TLm9iamVjdHOhHYAEgArSGw8gI6IhIoAFgAaACdMPJSYnKBRf -EBROU1RJRkZSZXByZXNlbnRhdGlvbl8QGU5TSW50ZXJuYWxMYXlvdXREaXJlY3Rpb26ACIAHTxESbE1N -ACoAAAAKAAAAEAEAAAMAAAABAAEAAAEBAAMAAAABAAEAAAECAAMAAAACAAgACAEDAAMAAAABAAEAAAEG -AAMAAAABAAEAAAEKAAMAAAABAAEAAAERAAQAAAABAAAACAESAAMAAAABAAEAAAEVAAMAAAABAAIAAAEW -AAMAAAABAAEAAAEXAAQAAAABAAAAAgEcAAMAAAABAAEAAAEoAAMAAAABAAIAAAFSAAMAAAABAAEAAAFT -AAMAAAACAAEAAYdzAAcAABGcAAAA0AAAAAAAABGcYXBwbAIAAABtbnRyR1JBWVhZWiAH3AAIABcADwAu -AA9hY3NwQVBQTAAAAABub25lAAAAAAAAAAAAAAAAAAAAAAAA9tYAAQAAAADTLWFwcGwAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAVkZXNjAAAAwAAAAHlkc2NtAAABPAAA -CBpjcHJ0AAAJWAAAACN3dHB0AAAJfAAAABRrVFJDAAAJkAAACAxkZXNjAAAAAAAAAB9HZW5lcmljIEdy -YXkgR2FtbWEgMi4yIFByb2ZpbGUAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAbWx1YwAAAAAAAAAfAAAADHNr -U0sAAAAuAAABhGRhREsAAAA6AAABsmNhRVMAAAA4AAAB7HZpVk4AAABAAAACJHB0QlIAAABKAAACZHVr -VUEAAAAsAAACrmZyRlUAAAA+AAAC2mh1SFUAAAA0AAADGHpoVFcAAAAaAAADTGtvS1IAAAAiAAADZm5i -Tk8AAAA6AAADiGNzQ1oAAAAoAAADwmhlSUwAAAAkAAAD6nJvUk8AAAAqAAAEDmRlREUAAABOAAAEOGl0 -SVQAAABOAAAEhnN2U0UAAAA4AAAE1HpoQ04AAAAaAAAFDGphSlAAAAAmAAAFJmVsR1IAAAAqAAAFTHB0 -UE8AAABSAAAFdm5sTkwAAABAAAAFyGVzRVMAAABMAAAGCHRoVEgAAAAyAAAGVHRyVFIAAAAkAAAGhmZp -RkkAAABGAAAGqmhySFIAAAA+AAAG8HBsUEwAAABKAAAHLmFyRUcAAAAsAAAHeHJ1UlUAAAA6AAAHpGVu -VVMAAAA8AAAH3gBWAWEAZQBvAGIAZQBjAG4A4QAgAHMAaQB2AOEAIABnAGEAbQBhACAAMgAsADIARwBl -AG4AZQByAGkAcwBrACAAZwByAOUAIAAyACwAMgAgAGcAYQBtAG0AYQAtAHAAcgBvAGYAaQBsAEcAYQBt -AG0AYQAgAGQAZQAgAGcAcgBpAHMAbwBzACAAZwBlAG4A6AByAGkAYwBhACAAMgAuADIAQx6lAHUAIABo -AOwAbgBoACAATQDgAHUAIAB4AOEAbQAgAEMAaAB1AG4AZwAgAEcAYQBtAG0AYQAgADIALgAyAFAAZQBy -AGYAaQBsACAARwBlAG4A6QByAGkAYwBvACAAZABhACAARwBhAG0AYQAgAGQAZQAgAEMAaQBuAHoAYQBz -ACAAMgAsADIEFwQwBDMEMAQ7BEwEPQQwACAARwByAGEAeQAtBDMEMAQ8BDAAIAAyAC4AMgBQAHIAbwBm -AGkAbAAgAGcA6QBuAOkAcgBpAHEAdQBlACAAZwByAGkAcwAgAGcAYQBtAG0AYQAgADIALAAyAMEAbAB0 -AGEAbADhAG4AbwBzACAAcwB6APwAcgBrAGUAIABnAGEAbQBtAGEAIAAyAC4AMpAadShwcJaOUUlepgAy -AC4AMoJyX2ljz4/wx3y8GAAg1ozAyQAgrBC5yAAgADIALgAyACDVBLhc0wzHfABHAGUAbgBlAHIAaQBz -AGsAIABnAHIA5QAgAGcAYQBtAG0AYQAgADIALAAyAC0AcAByAG8AZgBpAGwATwBiAGUAYwBuAOEAIAFh -AGUAZADhACAAZwBhAG0AYQAgADIALgAyBdIF0AXeBdQAIAXQBeQF1QXoACAF2wXcBdwF2QAgADIALgAy -AEcAYQBtAGEAIABnAHIAaQAgAGcAZQBuAGUAcgBpAGMBAwAgADIALAAyAEEAbABsAGcAZQBtAGUAaQBu -AGUAcwAgAEcAcgBhAHUAcwB0AHUAZgBlAG4ALQBQAHIAbwBmAGkAbAAgAEcAYQBtAG0AYQAgADIALAAy -AFAAcgBvAGYAaQBsAG8AIABnAHIAaQBnAGkAbwAgAGcAZQBuAGUAcgBpAGMAbwAgAGQAZQBsAGwAYQAg -AGcAYQBtAG0AYQAgADIALAAyAEcAZQBuAGUAcgBpAHMAawAgAGcAcgDlACAAMgAsADIAIABnAGEAbQBt -AGEAcAByAG8AZgBpAGxmbpAacHBepnz7ZXAAMgAuADJjz4/wZYdO9k4AgiwwsDDsMKQwrDDzMN4AIAAy -AC4AMgAgMNcw7TDVMKEwpDDrA5MDtQO9A7kDugPMACADkwO6A8EDuQAgA5MDrAO8A7wDsQAgADIALgAy -AFAAZQByAGYAaQBsACAAZwBlAG4A6QByAGkAYwBvACAAZABlACAAYwBpAG4AegBlAG4AdABvAHMAIABk -AGEAIABHAGEAbQBtAGEAIAAyACwAMgBBAGwAZwBlAG0AZQBlAG4AIABnAHIAaQBqAHMAIABnAGEAbQBt -AGEAIAAyACwAMgAtAHAAcgBvAGYAaQBlAGwAUABlAHIAZgBpAGwAIABnAGUAbgDpAHIAaQBjAG8AIABk -AGUAIABnAGEAbQBtAGEAIABkAGUAIABnAHIAaQBzAGUAcwAgADIALAAyDiMOMQ4HDioONQ5BDgEOIQ4h -DjIOQA4BDiMOIg5MDhcOMQ5IDicORA4bACAAMgAuADIARwBlAG4AZQBsACAARwByAGkAIABHAGEAbQBh -ACAAMgAsADIAWQBsAGUAaQBuAGUAbgAgAGgAYQByAG0AYQBhAG4AIABnAGEAbQBtAGEAIAAyACwAMgAg -AC0AcAByAG8AZgBpAGkAbABpAEcAZQBuAGUAcgBpAQ0AawBpACAARwByAGEAeQAgAEcAYQBtAG0AYQAg -ADIALgAyACAAcAByAG8AZgBpAGwAVQBuAGkAdwBlAHIAcwBhAGwAbgB5ACAAcAByAG8AZgBpAGwAIABz -AHoAYQByAG8BWwBjAGkAIABnAGEAbQBtAGEAIAAyACwAMgY6BicGRQYnACAAMgAuADIAIAZEBkgGRgAg -BjEGRQYnBi8GSgAgBjkGJwZFBB4EMQRJBDAETwAgBEEENQRABDAETwAgBDMEMAQ8BDwEMAAgADIALAAy -AC0EPwRABD4ERAQ4BDsETABHAGUAbgBlAHIAaQBjACAARwByAGEAeQAgAEcAYQBtAG0AYQAgADIALgAy -ACAAUAByAG8AZgBpAGwAZQAAdGV4dAAAAABDb3B5cmlnaHQgQXBwbGUgSW5jLiwgMjAxMgAAWFlaIAAA -AAAAAPNRAAEAAAABFsxjdXJ2AAAAAAAABAAAAAAFAAoADwAUABkAHgAjACgALQAyADcAOwBAAEUASgBP -AFQAWQBeAGMAaABtAHIAdwB8AIEAhgCLAJAAlQCaAJ8ApACpAK4AsgC3ALwAwQDGAMsA0ADVANsA4ADl -AOsA8AD2APsBAQEHAQ0BEwEZAR8BJQErATIBOAE+AUUBTAFSAVkBYAFnAW4BdQF8AYMBiwGSAZoBoQGp -AbEBuQHBAckB0QHZAeEB6QHyAfoCAwIMAhQCHQImAi8COAJBAksCVAJdAmcCcQJ6AoQCjgKYAqICrAK2 -AsECywLVAuAC6wL1AwADCwMWAyEDLQM4A0MDTwNaA2YDcgN+A4oDlgOiA64DugPHA9MD4APsA/kEBgQT -BCAELQQ7BEgEVQRjBHEEfgSMBJoEqAS2BMQE0wThBPAE/gUNBRwFKwU6BUkFWAVnBXcFhgWWBaYFtQXF -BdUF5QX2BgYGFgYnBjcGSAZZBmoGewaMBp0GrwbABtEG4wb1BwcHGQcrBz0HTwdhB3QHhgeZB6wHvwfS -B+UH+AgLCB8IMghGCFoIbgiCCJYIqgi+CNII5wj7CRAJJQk6CU8JZAl5CY8JpAm6Cc8J5Qn7ChEKJwo9 -ClQKagqBCpgKrgrFCtwK8wsLCyILOQtRC2kLgAuYC7ALyAvhC/kMEgwqDEMMXAx1DI4MpwzADNkM8w0N -DSYNQA1aDXQNjg2pDcMN3g34DhMOLg5JDmQOfw6bDrYO0g7uDwkPJQ9BD14Peg+WD7MPzw/sEAkQJhBD -EGEQfhCbELkQ1xD1ERMRMRFPEW0RjBGqEckR6BIHEiYSRRJkEoQSoxLDEuMTAxMjE0MTYxODE6QTxRPl -FAYUJxRJFGoUixStFM4U8BUSFTQVVhV4FZsVvRXgFgMWJhZJFmwWjxayFtYW+hcdF0EXZReJF64X0hf3 -GBsYQBhlGIoYrxjVGPoZIBlFGWsZkRm3Gd0aBBoqGlEadxqeGsUa7BsUGzsbYxuKG7Ib2hwCHCocUhx7 -HKMczBz1HR4dRx1wHZkdwx3sHhYeQB5qHpQevh7pHxMfPh9pH5Qfvx/qIBUgQSBsIJggxCDwIRwhSCF1 -IaEhziH7IiciVSKCIq8i3SMKIzgjZiOUI8Ij8CQfJE0kfCSrJNolCSU4JWgllyXHJfcmJyZXJocmtybo -JxgnSSd6J6sn3CgNKD8ocSiiKNQpBik4KWspnSnQKgIqNSpoKpsqzysCKzYraSudK9EsBSw5LG4soizX -LQwtQS12Last4S4WLkwugi63Lu4vJC9aL5Evxy/+MDUwbDCkMNsxEjFKMYIxujHyMioyYzKbMtQzDTNG -M38zuDPxNCs0ZTSeNNg1EzVNNYc1wjX9Njc2cjauNuk3JDdgN5w31zgUOFA4jDjIOQU5Qjl/Obw5+To2 -OnQ6sjrvOy07azuqO+g8JzxlPKQ84z0iPWE9oT3gPiA+YD6gPuA/IT9hP6I/4kAjQGRApkDnQSlBakGs -Qe5CMEJyQrVC90M6Q31DwEQDREdEikTORRJFVUWaRd5GIkZnRqtG8Ec1R3tHwEgFSEtIkUjXSR1JY0mp -SfBKN0p9SsRLDEtTS5pL4kwqTHJMuk0CTUpNk03cTiVObk63TwBPSU+TT91QJ1BxULtRBlFQUZtR5lIx -UnxSx1MTU19TqlP2VEJUj1TbVShVdVXCVg9WXFapVvdXRFeSV+BYL1h9WMtZGllpWbhaB1pWWqZa9VtF -W5Vb5Vw1XIZc1l0nXXhdyV4aXmxevV8PX2Ffs2AFYFdgqmD8YU9homH1YklinGLwY0Njl2PrZEBklGTp -ZT1lkmXnZj1mkmboZz1nk2fpaD9olmjsaUNpmmnxakhqn2r3a09rp2v/bFdsr20IbWBtuW4SbmtuxG8e -b3hv0XArcIZw4HE6cZVx8HJLcqZzAXNdc7h0FHRwdMx1KHWFdeF2Pnabdvh3VnezeBF4bnjMeSp5iXnn -ekZ6pXsEe2N7wnwhfIF84X1BfaF+AX5ifsJ/I3+Ef+WAR4CogQqBa4HNgjCCkoL0g1eDuoQdhICE44VH -hauGDoZyhteHO4efiASIaYjOiTOJmYn+imSKyoswi5aL/IxjjMqNMY2Yjf+OZo7OjzaPnpAGkG6Q1pE/ -kaiSEZJ6kuOTTZO2lCCUipT0lV+VyZY0lp+XCpd1l+CYTJi4mSSZkJn8mmia1ZtCm6+cHJyJnPedZJ3S -nkCerp8dn4uf+qBpoNihR6G2oiailqMGo3aj5qRWpMelOKWpphqmi6b9p26n4KhSqMSpN6mpqhyqj6sC -q3Wr6axcrNCtRK24ri2uoa8Wr4uwALB1sOqxYLHWskuywrM4s660JbSctRO1irYBtnm28Ldot+C4WbjR -uUq5wro7urW7LrunvCG8m70VvY++Cr6Evv+/er/1wHDA7MFnwePCX8Lbw1jD1MRRxM7FS8XIxkbGw8dB -x7/IPci8yTrJuco4yrfLNsu2zDXMtc01zbXONs62zzfPuNA50LrRPNG+0j/SwdNE08bUSdTL1U7V0dZV -1tjXXNfg2GTY6Nls2fHadtr724DcBdyK3RDdlt4c3qLfKd+v4DbgveFE4cziU+Lb42Pj6+Rz5PzlhOYN -5pbnH+ep6DLovOlG6dDqW+rl63Dr++yG7RHtnO4o7rTvQO/M8Fjw5fFy8f/yjPMZ86f0NPTC9VD13vZt -9vv3ivgZ+Kj5OPnH+lf65/t3/Af8mP0p/br+S/7c/23//9IrLC0uWiRjbGFzc25hbWVYJGNsYXNzZXNf -EBBOU0JpdG1hcEltYWdlUmVwoy0vMFpOU0ltYWdlUmVwWE5TT2JqZWN00issMjNXTlNBcnJheaIyMNIr -LDU2Xk5TTXV0YWJsZUFycmF5ozUyMNM4OQ86OzxXTlNXaGl0ZVxOU0NvbG9yU3BhY2VEMCAwABADgAzS -Kyw+P1dOU0NvbG9yoj4w0issQUJXTlNJbWFnZaJBMAAIABEAGgAkACkAMgA3AEkATABRAFMAYgBoAHUA -fACLAJIAnwCmAK4AsACyALQAuQC7AL0AxADJANQA1gDYANoA3wDiAOQA5gDoAO8BBgEiASQBJhOWE5sT -phOvE8ITxhPRE9oT3xPnE+oT7xP+FAIUCRQRFB4UIxQlFCcULBQ0FDcUPBREAAAAAAAAAgEAAAAAAAAA -QwAAAAAAAAAAAAAAAAAAFEc +S2V5ZWRBcmNoaXZlctEICVRyb290gAGuCwwbHCEaJissMzY5P0JVJG51bGzXDQ4PEBESExQVFhcYGRpW +TlNTaXplViRjbGFzc1xOU0ltYWdlRmxhZ3NWTlNSZXBzW05TVGludENvbG9yV05TQ29sb3JeTlNSZXNp +emluZ01vZGWAAoANEiDDAACAA4AAgAsQAFZ7MSwgMX3SHQ4eIFpOUy5vYmplY3RzoR+ABIAK0h0OIiWi +IySABYAGgAnTDicoKSoaXxAUTlNUSUZGUmVwcmVzZW50YXRpb25fEBlOU0ludGVybmFsTGF5b3V0RGly +ZWN0aW9ugAiAB08REmxNTQAqAAAACgAAABABAAADAAAAAQABAAABAQADAAAAAQABAAABAgADAAAAAgAI +AAgBAwADAAAAAQABAAABBgADAAAAAQABAAABCgADAAAAAQABAAABEQAEAAAAAQAAAAgBEgADAAAAAQAB +AAABFQADAAAAAQACAAABFgADAAAAAQABAAABFwAEAAAAAQAAAAIBHAADAAAAAQABAAABKAADAAAAAQAC +AAABUgADAAAAAQABAAABUwADAAAAAgABAAGHcwAHAAARnAAAANAAAAAAAAARnGFwcGwCAAAAbW50ckdS +QVlYWVogB9wACAAXAA8ALgAPYWNzcEFQUEwAAAAAbm9uZQAAAAAAAAAAAAAAAAAAAAAAAPbWAAEAAAAA +0y1hcHBsAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFZGVzYwAA +AMAAAAB5ZHNjbQAAATwAAAgaY3BydAAACVgAAAAjd3RwdAAACXwAAAAUa1RSQwAACZAAAAgMZGVzYwAA +AAAAAAAfR2VuZXJpYyBHcmF5IEdhbW1hIDIuMiBQcm9maWxlAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAG1s +dWMAAAAAAAAAHwAAAAxza1NLAAAALgAAAYRkYURLAAAAOgAAAbJjYUVTAAAAOAAAAex2aVZOAAAAQAAA +AiRwdEJSAAAASgAAAmR1a1VBAAAALAAAAq5mckZVAAAAPgAAAtpodUhVAAAANAAAAxh6aFRXAAAAGgAA +A0xrb0tSAAAAIgAAA2ZuYk5PAAAAOgAAA4hjc0NaAAAAKAAAA8JoZUlMAAAAJAAAA+pyb1JPAAAAKgAA +BA5kZURFAAAATgAABDhpdElUAAAATgAABIZzdlNFAAAAOAAABNR6aENOAAAAGgAABQxqYUpQAAAAJgAA +BSZlbEdSAAAAKgAABUxwdFBPAAAAUgAABXZubE5MAAAAQAAABchlc0VTAAAATAAABgh0aFRIAAAAMgAA +BlR0clRSAAAAJAAABoZmaUZJAAAARgAABqpockhSAAAAPgAABvBwbFBMAAAASgAABy5hckVHAAAALAAA +B3hydVJVAAAAOgAAB6RlblVTAAAAPAAAB94AVgFhAGUAbwBiAGUAYwBuAOEAIABzAGkAdgDhACAAZwBh +AG0AYQAgADIALAAyAEcAZQBuAGUAcgBpAHMAawAgAGcAcgDlACAAMgAsADIAIABnAGEAbQBtAGEALQBw +AHIAbwBmAGkAbABHAGEAbQBtAGEAIABkAGUAIABnAHIAaQBzAG8AcwAgAGcAZQBuAOgAcgBpAGMAYQAg +ADIALgAyAEMepQB1ACAAaADsAG4AaAAgAE0A4AB1ACAAeADhAG0AIABDAGgAdQBuAGcAIABHAGEAbQBt +AGEAIAAyAC4AMgBQAGUAcgBmAGkAbAAgAEcAZQBuAOkAcgBpAGMAbwAgAGQAYQAgAEcAYQBtAGEAIABk +AGUAIABDAGkAbgB6AGEAcwAgADIALAAyBBcEMAQzBDAEOwRMBD0EMAAgAEcAcgBhAHkALQQzBDAEPAQw +ACAAMgAuADIAUAByAG8AZgBpAGwAIABnAOkAbgDpAHIAaQBxAHUAZQAgAGcAcgBpAHMAIABnAGEAbQBt +AGEAIAAyACwAMgDBAGwAdABhAGwA4QBuAG8AcwAgAHMAegD8AHIAawBlACAAZwBhAG0AbQBhACAAMgAu +ADKQGnUocHCWjlFJXqYAMgAuADKCcl9pY8+P8Md8vBgAINaMwMkAIKwQucgAIAAyAC4AMgAg1QS4XNMM +x3wARwBlAG4AZQByAGkAcwBrACAAZwByAOUAIABnAGEAbQBtAGEAIAAyACwAMgAtAHAAcgBvAGYAaQBs +AE8AYgBlAGMAbgDhACABYQBlAGQA4QAgAGcAYQBtAGEAIAAyAC4AMgXSBdAF3gXUACAF0AXkBdUF6AAg +BdsF3AXcBdkAIAAyAC4AMgBHAGEAbQBhACAAZwByAGkAIABnAGUAbgBlAHIAaQBjAQMAIAAyACwAMgBB +AGwAbABnAGUAbQBlAGkAbgBlAHMAIABHAHIAYQB1AHMAdAB1AGYAZQBuAC0AUAByAG8AZgBpAGwAIABH +AGEAbQBtAGEAIAAyACwAMgBQAHIAbwBmAGkAbABvACAAZwByAGkAZwBpAG8AIABnAGUAbgBlAHIAaQBj +AG8AIABkAGUAbABsAGEAIABnAGEAbQBtAGEAIAAyACwAMgBHAGUAbgBlAHIAaQBzAGsAIABnAHIA5QAg +ADIALAAyACAAZwBhAG0AbQBhAHAAcgBvAGYAaQBsZm6QGnBwXqZ8+2VwADIALgAyY8+P8GWHTvZOAIIs +MLAw7DCkMKww8zDeACAAMgAuADIAIDDXMO0w1TChMKQw6wOTA7UDvQO5A7oDzAAgA5MDugPBA7kAIAOT +A6wDvAO8A7EAIAAyAC4AMgBQAGUAcgBmAGkAbAAgAGcAZQBuAOkAcgBpAGMAbwAgAGQAZQAgAGMAaQBu +AHoAZQBuAHQAbwBzACAAZABhACAARwBhAG0AbQBhACAAMgAsADIAQQBsAGcAZQBtAGUAZQBuACAAZwBy +AGkAagBzACAAZwBhAG0AbQBhACAAMgAsADIALQBwAHIAbwBmAGkAZQBsAFAAZQByAGYAaQBsACAAZwBl +AG4A6QByAGkAYwBvACAAZABlACAAZwBhAG0AbQBhACAAZABlACAAZwByAGkAcwBlAHMAIAAyACwAMg4j +DjEOBw4qDjUOQQ4BDiEOIQ4yDkAOAQ4jDiIOTA4XDjEOSA4nDkQOGwAgADIALgAyAEcAZQBuAGUAbAAg +AEcAcgBpACAARwBhAG0AYQAgADIALAAyAFkAbABlAGkAbgBlAG4AIABoAGEAcgBtAGEAYQBuACAAZwBh +AG0AbQBhACAAMgAsADIAIAAtAHAAcgBvAGYAaQBpAGwAaQBHAGUAbgBlAHIAaQENAGsAaQAgAEcAcgBh +AHkAIABHAGEAbQBtAGEAIAAyAC4AMgAgAHAAcgBvAGYAaQBsAFUAbgBpAHcAZQByAHMAYQBsAG4AeQAg +AHAAcgBvAGYAaQBsACAAcwB6AGEAcgBvAVsAYwBpACAAZwBhAG0AbQBhACAAMgAsADIGOgYnBkUGJwAg +ADIALgAyACAGRAZIBkYAIAYxBkUGJwYvBkoAIAY5BicGRQQeBDEESQQwBE8AIARBBDUEQAQwBE8AIAQz +BDAEPAQ8BDAAIAAyACwAMgAtBD8EQAQ+BEQEOAQ7BEwARwBlAG4AZQByAGkAYwAgAEcAcgBhAHkAIABH +AGEAbQBtAGEAIAAyAC4AMgAgAFAAcgBvAGYAaQBsAGUAAHRleHQAAAAAQ29weXJpZ2h0IEFwcGxlIElu +Yy4sIDIwMTIAAFhZWiAAAAAAAADzUQABAAAAARbMY3VydgAAAAAAAAQAAAAABQAKAA8AFAAZAB4AIwAo +AC0AMgA3ADsAQABFAEoATwBUAFkAXgBjAGgAbQByAHcAfACBAIYAiwCQAJUAmgCfAKQAqQCuALIAtwC8 +AMEAxgDLANAA1QDbAOAA5QDrAPAA9gD7AQEBBwENARMBGQEfASUBKwEyATgBPgFFAUwBUgFZAWABZwFu +AXUBfAGDAYsBkgGaAaEBqQGxAbkBwQHJAdEB2QHhAekB8gH6AgMCDAIUAh0CJgIvAjgCQQJLAlQCXQJn +AnECegKEAo4CmAKiAqwCtgLBAssC1QLgAusC9QMAAwsDFgMhAy0DOANDA08DWgNmA3IDfgOKA5YDogOu +A7oDxwPTA+AD7AP5BAYEEwQgBC0EOwRIBFUEYwRxBH4EjASaBKgEtgTEBNME4QTwBP4FDQUcBSsFOgVJ +BVgFZwV3BYYFlgWmBbUFxQXVBeUF9gYGBhYGJwY3BkgGWQZqBnsGjAadBq8GwAbRBuMG9QcHBxkHKwc9 +B08HYQd0B4YHmQesB78H0gflB/gICwgfCDIIRghaCG4IggiWCKoIvgjSCOcI+wkQCSUJOglPCWQJeQmP +CaQJugnPCeUJ+woRCicKPQpUCmoKgQqYCq4KxQrcCvMLCwsiCzkLUQtpC4ALmAuwC8gL4Qv5DBIMKgxD +DFwMdQyODKcMwAzZDPMNDQ0mDUANWg10DY4NqQ3DDd4N+A4TDi4OSQ5kDn8Omw62DtIO7g8JDyUPQQ9e +D3oPlg+zD88P7BAJECYQQxBhEH4QmxC5ENcQ9RETETERTxFtEYwRqhHJEegSBxImEkUSZBKEEqMSwxLj +EwMTIxNDE2MTgxOkE8UT5RQGFCcUSRRqFIsUrRTOFPAVEhU0FVYVeBWbFb0V4BYDFiYWSRZsFo8WshbW +FvoXHRdBF2UXiReuF9IX9xgbGEAYZRiKGK8Y1Rj6GSAZRRlrGZEZtxndGgQaKhpRGncanhrFGuwbFBs7 +G2MbihuyG9ocAhwqHFIcexyjHMwc9R0eHUcdcB2ZHcMd7B4WHkAeah6UHr4e6R8THz4faR+UH78f6iAV +IEEgbCCYIMQg8CEcIUghdSGhIc4h+yInIlUigiKvIt0jCiM4I2YjlCPCI/AkHyRNJHwkqyTaJQklOCVo +JZclxyX3JicmVyaHJrcm6CcYJ0kneierJ9woDSg/KHEooijUKQYpOClrKZ0p0CoCKjUqaCqbKs8rAis2 +K2krnSvRLAUsOSxuLKIs1y0MLUEtdi2rLeEuFi5MLoIuty7uLyQvWi+RL8cv/jA1MGwwpDDbMRIxSjGC +Mbox8jIqMmMymzLUMw0zRjN/M7gz8TQrNGU0njTYNRM1TTWHNcI1/TY3NnI2rjbpNyQ3YDecN9c4FDhQ +OIw4yDkFOUI5fzm8Ofk6Njp0OrI67zstO2s7qjvoPCc8ZTykPOM9Ij1hPaE94D4gPmA+oD7gPyE/YT+i +P+JAI0BkQKZA50EpQWpBrEHuQjBCckK1QvdDOkN9Q8BEA0RHRIpEzkUSRVVFmkXeRiJGZ0arRvBHNUd7 +R8BIBUhLSJFI10kdSWNJqUnwSjdKfUrESwxLU0uaS+JMKkxyTLpNAk1KTZNN3E4lTm5Ot08AT0lPk0/d +UCdQcVC7UQZRUFGbUeZSMVJ8UsdTE1NfU6pT9lRCVI9U21UoVXVVwlYPVlxWqVb3V0RXklfgWC9YfVjL +WRpZaVm4WgdaVlqmWvVbRVuVW+VcNVyGXNZdJ114XcleGl5sXr1fD19hX7NgBWBXYKpg/GFPYaJh9WJJ +Ypxi8GNDY5dj62RAZJRk6WU9ZZJl52Y9ZpJm6Gc9Z5Nn6Wg/aJZo7GlDaZpp8WpIap9q92tPa6dr/2xX +bK9tCG1gbbluEm5rbsRvHm94b9FwK3CGcOBxOnGVcfByS3KmcwFzXXO4dBR0cHTMdSh1hXXhdj52m3b4 +d1Z3s3gReG54zHkqeYl553pGeqV7BHtje8J8IXyBfOF9QX2hfgF+Yn7CfyN/hH/lgEeAqIEKgWuBzYIw +gpKC9INXg7qEHYSAhOOFR4Wrhg6GcobXhzuHn4gEiGmIzokziZmJ/opkisqLMIuWi/yMY4zKjTGNmI3/ +jmaOzo82j56QBpBukNaRP5GokhGSepLjk02TtpQglIqU9JVflcmWNJaflwqXdZfgmEyYuJkkmZCZ/Jpo +mtWbQpuvnByciZz3nWSd0p5Anq6fHZ+Ln/qgaaDYoUehtqImopajBqN2o+akVqTHpTilqaYapoum/adu +p+CoUqjEqTepqaocqo+rAqt1q+msXKzQrUStuK4trqGvFq+LsACwdbDqsWCx1rJLssKzOLOutCW0nLUT +tYq2AbZ5tvC3aLfguFm40blKucK6O7q1uy67p7whvJu9Fb2Pvgq+hL7/v3q/9cBwwOzBZ8Hjwl/C28NY +w9TEUcTOxUvFyMZGxsPHQce/yD3IvMk6ybnKOMq3yzbLtsw1zLXNNc21zjbOts83z7jQOdC60TzRvtI/ +0sHTRNPG1EnUy9VO1dHWVdbY11zX4Nhk2OjZbNnx2nba+9uA3AXcit0Q3ZbeHN6i3ynfr+A24L3hROHM +4lPi2+Nj4+vkc+T85YTmDeaW5x/nqegy6LzpRunQ6lvq5etw6/vshu0R7ZzuKO6070DvzPBY8OXxcvH/ +8ozzGfOn9DT0wvVQ9d72bfb794r4Gfio+Tj5x/pX+uf7d/wH/Jj9Kf26/kv+3P9t///SLS4vMFokY2xh +c3NuYW1lWCRjbGFzc2VzXxAQTlNCaXRtYXBJbWFnZVJlcKMvMTJaTlNJbWFnZVJlcFhOU09iamVjdNIt +LjQ1V05TQXJyYXmiNDLSLS43OF5OU011dGFibGVBcnJheaM3NDLTOjsOPD0+V05TV2hpdGVcTlNDb2xv +clNwYWNlRDAgMAAQA4AM0i0uQEFXTlNDb2xvcqJAMtItLkNEV05TSW1hZ2WiQzIACAARABoAJAApADIA +NwBJAEwAUQBTAGIAaAB3AH4AhQCSAJkApQCtALwAvgDAAMUAxwDJAMsAzQDUANkA5ADmAOgA6gDvAPIA +9AD2APgA/wEWATIBNAE2E6YTqxO2E78T0hPWE+ET6hPvE/cT+hP/FA4UEhQZFCEULhQzFDUUNxQ8FEQU +RxRMFFQAAAAAAAACAQAAAAAAAABFAAAAAAAAAAAAAAAAAAAUVw diff --git a/i18n/src/main/resources/ka.lproj/Command.xib b/i18n/src/main/resources/ka.lproj/Command.xib index 99eac9f1bd..4f7ae238c5 100644 --- a/i18n/src/main/resources/ka.lproj/Command.xib +++ b/i18n/src/main/resources/ka.lproj/Command.xib @@ -1,7 +1,7 @@ - + - + diff --git a/i18n/src/main/resources/ka.lproj/Connection.xib b/i18n/src/main/resources/ka.lproj/Connection.xib index ac4c79eff9..f541990de0 100644 --- a/i18n/src/main/resources/ka.lproj/Connection.xib +++ b/i18n/src/main/resources/ka.lproj/Connection.xib @@ -1,7 +1,7 @@ - + - + @@ -76,7 +76,7 @@ - + diff --git a/i18n/src/main/resources/ka.lproj/Info.xib b/i18n/src/main/resources/ka.lproj/Info.xib index a7c403bc90..03f6708e0f 100644 --- a/i18n/src/main/resources/ka.lproj/Info.xib +++ b/i18n/src/main/resources/ka.lproj/Info.xib @@ -1,7 +1,7 @@ - + - + @@ -89,7 +89,7 @@ - + @@ -1017,21 +1017,21 @@ - + - + - + @@ -1121,21 +1121,21 @@ - + - + - + @@ -1250,27 +1250,27 @@ diff --git a/i18n/src/main/resources/ka.lproj/Login.strings b/i18n/src/main/resources/ka.lproj/Login.strings index 9b6cd84a97..9a69426c01 100644 Binary files a/i18n/src/main/resources/ka.lproj/Login.strings and b/i18n/src/main/resources/ka.lproj/Login.strings differ diff --git a/i18n/src/main/resources/ka.lproj/Main.xib b/i18n/src/main/resources/ka.lproj/Main.xib index 8c94395e5f..d4b69cdc78 100644 --- a/i18n/src/main/resources/ka.lproj/Main.xib +++ b/i18n/src/main/resources/ka.lproj/Main.xib @@ -1,7 +1,7 @@ - + - + @@ -159,19 +159,19 @@ - + - + - + @@ -321,12 +321,12 @@ CA - + - + @@ -566,6 +566,10 @@ CA - + + + + + diff --git a/i18n/src/main/resources/ka.lproj/Preferences.xib b/i18n/src/main/resources/ka.lproj/Preferences.xib index dc17faf8a8..6e5dd98704 100644 --- a/i18n/src/main/resources/ka.lproj/Preferences.xib +++ b/i18n/src/main/resources/ka.lproj/Preferences.xib @@ -1,7 +1,7 @@ - + - + @@ -266,7 +266,7 @@ - + @@ -307,7 +307,7 @@ - + @@ -538,7 +538,7 @@ - + @@ -578,7 +578,7 @@ - + @@ -613,7 +613,7 @@ - + @@ -642,7 +642,7 @@ - + @@ -1218,7 +1218,7 @@ - + @@ -1250,7 +1250,7 @@ - + @@ -1290,7 +1290,7 @@ - + @@ -1363,7 +1363,7 @@ - + @@ -1558,7 +1558,7 @@ - + @@ -1618,7 +1618,7 @@ - + @@ -1643,7 +1643,7 @@ - + @@ -1685,7 +1685,7 @@ - + @@ -1713,7 +1713,7 @@ - + @@ -1741,7 +1741,7 @@ - + @@ -1770,7 +1770,7 @@ - + @@ -1805,7 +1805,7 @@ - + @@ -1889,7 +1889,7 @@ - + @@ -1948,7 +1948,7 @@ - + @@ -1976,7 +1976,7 @@ - + @@ -2005,7 +2005,7 @@ - + diff --git a/i18n/src/main/resources/ka.lproj/Profiles.xib b/i18n/src/main/resources/ka.lproj/Profiles.xib index 18af5a58f4..20e5ce0f36 100644 --- a/i18n/src/main/resources/ka.lproj/Profiles.xib +++ b/i18n/src/main/resources/ka.lproj/Profiles.xib @@ -1,7 +1,7 @@ - + - + diff --git a/i18n/src/main/resources/ka.lproj/Prompt.xib b/i18n/src/main/resources/ka.lproj/Prompt.xib index 6dbd7f5d0d..5a64732d78 100644 --- a/i18n/src/main/resources/ka.lproj/Prompt.xib +++ b/i18n/src/main/resources/ka.lproj/Prompt.xib @@ -1,7 +1,7 @@ - + - + @@ -95,7 +95,7 @@ Gw - + diff --git a/i18n/src/main/resources/ka.lproj/S3.strings b/i18n/src/main/resources/ka.lproj/S3.strings index 743f8c403b..7daeaec5e7 100644 Binary files a/i18n/src/main/resources/ka.lproj/S3.strings and b/i18n/src/main/resources/ka.lproj/S3.strings differ diff --git a/i18n/src/main/resources/ka.lproj/Updater.strings b/i18n/src/main/resources/ka.lproj/Updater.strings index f8d28c5d20..1ece10a226 100644 Binary files a/i18n/src/main/resources/ka.lproj/Updater.strings and b/i18n/src/main/resources/ka.lproj/Updater.strings differ diff --git a/i18n/src/main/resources/ko.lproj/Bookmark.xib b/i18n/src/main/resources/ko.lproj/Bookmark.xib index 260a1bf522..0cfaf2e806 100644 --- a/i18n/src/main/resources/ko.lproj/Bookmark.xib +++ b/i18n/src/main/resources/ko.lproj/Bookmark.xib @@ -1,7 +1,7 @@ - + - + @@ -92,7 +92,7 @@ - + @@ -489,95 +489,95 @@ YnBsaXN0MDDUAQIDBAUGBwpYJHZlcnNpb25ZJGFyY2hpdmVyVCR0b3BYJG9iamVjdHMSAAGGoF8QD05T -S2V5ZWRBcmNoaXZlctEICVRyb290gAGuCwwZGh8UJCkqMTQ3PUBVJG51bGzWDQ4PEBESExQVFhcYVk5T -U2l6ZV5OU1Jlc2l6aW5nTW9kZVYkY2xhc3NcTlNJbWFnZUZsYWdzVk5TUmVwc1dOU0NvbG9ygAIQAIAN -EiDDAACAA4ALVnsxLCAxfdIbDxweWk5TLm9iamVjdHOhHYAEgArSGw8gI6IhIoAFgAaACdMPJSYnKBRf -EBROU1RJRkZSZXByZXNlbnRhdGlvbl8QGU5TSW50ZXJuYWxMYXlvdXREaXJlY3Rpb26ACIAHTxESbE1N -ACoAAAAKAAAAEAEAAAMAAAABAAEAAAEBAAMAAAABAAEAAAECAAMAAAACAAgACAEDAAMAAAABAAEAAAEG -AAMAAAABAAEAAAEKAAMAAAABAAEAAAERAAQAAAABAAAACAESAAMAAAABAAEAAAEVAAMAAAABAAIAAAEW -AAMAAAABAAEAAAEXAAQAAAABAAAAAgEcAAMAAAABAAEAAAEoAAMAAAABAAIAAAFSAAMAAAABAAEAAAFT -AAMAAAACAAEAAYdzAAcAABGcAAAA0AAAAAAAABGcYXBwbAIAAABtbnRyR1JBWVhZWiAH3AAIABcADwAu -AA9hY3NwQVBQTAAAAABub25lAAAAAAAAAAAAAAAAAAAAAAAA9tYAAQAAAADTLWFwcGwAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAVkZXNjAAAAwAAAAHlkc2NtAAABPAAA -CBpjcHJ0AAAJWAAAACN3dHB0AAAJfAAAABRrVFJDAAAJkAAACAxkZXNjAAAAAAAAAB9HZW5lcmljIEdy -YXkgR2FtbWEgMi4yIFByb2ZpbGUAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAbWx1YwAAAAAAAAAfAAAADHNr -U0sAAAAuAAABhGRhREsAAAA6AAABsmNhRVMAAAA4AAAB7HZpVk4AAABAAAACJHB0QlIAAABKAAACZHVr -VUEAAAAsAAACrmZyRlUAAAA+AAAC2mh1SFUAAAA0AAADGHpoVFcAAAAaAAADTGtvS1IAAAAiAAADZm5i -Tk8AAAA6AAADiGNzQ1oAAAAoAAADwmhlSUwAAAAkAAAD6nJvUk8AAAAqAAAEDmRlREUAAABOAAAEOGl0 -SVQAAABOAAAEhnN2U0UAAAA4AAAE1HpoQ04AAAAaAAAFDGphSlAAAAAmAAAFJmVsR1IAAAAqAAAFTHB0 -UE8AAABSAAAFdm5sTkwAAABAAAAFyGVzRVMAAABMAAAGCHRoVEgAAAAyAAAGVHRyVFIAAAAkAAAGhmZp -RkkAAABGAAAGqmhySFIAAAA+AAAG8HBsUEwAAABKAAAHLmFyRUcAAAAsAAAHeHJ1UlUAAAA6AAAHpGVu -VVMAAAA8AAAH3gBWAWEAZQBvAGIAZQBjAG4A4QAgAHMAaQB2AOEAIABnAGEAbQBhACAAMgAsADIARwBl -AG4AZQByAGkAcwBrACAAZwByAOUAIAAyACwAMgAgAGcAYQBtAG0AYQAtAHAAcgBvAGYAaQBsAEcAYQBt -AG0AYQAgAGQAZQAgAGcAcgBpAHMAbwBzACAAZwBlAG4A6AByAGkAYwBhACAAMgAuADIAQx6lAHUAIABo -AOwAbgBoACAATQDgAHUAIAB4AOEAbQAgAEMAaAB1AG4AZwAgAEcAYQBtAG0AYQAgADIALgAyAFAAZQBy -AGYAaQBsACAARwBlAG4A6QByAGkAYwBvACAAZABhACAARwBhAG0AYQAgAGQAZQAgAEMAaQBuAHoAYQBz -ACAAMgAsADIEFwQwBDMEMAQ7BEwEPQQwACAARwByAGEAeQAtBDMEMAQ8BDAAIAAyAC4AMgBQAHIAbwBm -AGkAbAAgAGcA6QBuAOkAcgBpAHEAdQBlACAAZwByAGkAcwAgAGcAYQBtAG0AYQAgADIALAAyAMEAbAB0 -AGEAbADhAG4AbwBzACAAcwB6APwAcgBrAGUAIABnAGEAbQBtAGEAIAAyAC4AMpAadShwcJaOUUlepgAy -AC4AMoJyX2ljz4/wx3y8GAAg1ozAyQAgrBC5yAAgADIALgAyACDVBLhc0wzHfABHAGUAbgBlAHIAaQBz -AGsAIABnAHIA5QAgAGcAYQBtAG0AYQAgADIALAAyAC0AcAByAG8AZgBpAGwATwBiAGUAYwBuAOEAIAFh -AGUAZADhACAAZwBhAG0AYQAgADIALgAyBdIF0AXeBdQAIAXQBeQF1QXoACAF2wXcBdwF2QAgADIALgAy -AEcAYQBtAGEAIABnAHIAaQAgAGcAZQBuAGUAcgBpAGMBAwAgADIALAAyAEEAbABsAGcAZQBtAGUAaQBu -AGUAcwAgAEcAcgBhAHUAcwB0AHUAZgBlAG4ALQBQAHIAbwBmAGkAbAAgAEcAYQBtAG0AYQAgADIALAAy -AFAAcgBvAGYAaQBsAG8AIABnAHIAaQBnAGkAbwAgAGcAZQBuAGUAcgBpAGMAbwAgAGQAZQBsAGwAYQAg -AGcAYQBtAG0AYQAgADIALAAyAEcAZQBuAGUAcgBpAHMAawAgAGcAcgDlACAAMgAsADIAIABnAGEAbQBt -AGEAcAByAG8AZgBpAGxmbpAacHBepnz7ZXAAMgAuADJjz4/wZYdO9k4AgiwwsDDsMKQwrDDzMN4AIAAy -AC4AMgAgMNcw7TDVMKEwpDDrA5MDtQO9A7kDugPMACADkwO6A8EDuQAgA5MDrAO8A7wDsQAgADIALgAy -AFAAZQByAGYAaQBsACAAZwBlAG4A6QByAGkAYwBvACAAZABlACAAYwBpAG4AegBlAG4AdABvAHMAIABk -AGEAIABHAGEAbQBtAGEAIAAyACwAMgBBAGwAZwBlAG0AZQBlAG4AIABnAHIAaQBqAHMAIABnAGEAbQBt -AGEAIAAyACwAMgAtAHAAcgBvAGYAaQBlAGwAUABlAHIAZgBpAGwAIABnAGUAbgDpAHIAaQBjAG8AIABk -AGUAIABnAGEAbQBtAGEAIABkAGUAIABnAHIAaQBzAGUAcwAgADIALAAyDiMOMQ4HDioONQ5BDgEOIQ4h -DjIOQA4BDiMOIg5MDhcOMQ5IDicORA4bACAAMgAuADIARwBlAG4AZQBsACAARwByAGkAIABHAGEAbQBh -ACAAMgAsADIAWQBsAGUAaQBuAGUAbgAgAGgAYQByAG0AYQBhAG4AIABnAGEAbQBtAGEAIAAyACwAMgAg -AC0AcAByAG8AZgBpAGkAbABpAEcAZQBuAGUAcgBpAQ0AawBpACAARwByAGEAeQAgAEcAYQBtAG0AYQAg -ADIALgAyACAAcAByAG8AZgBpAGwAVQBuAGkAdwBlAHIAcwBhAGwAbgB5ACAAcAByAG8AZgBpAGwAIABz -AHoAYQByAG8BWwBjAGkAIABnAGEAbQBtAGEAIAAyACwAMgY6BicGRQYnACAAMgAuADIAIAZEBkgGRgAg -BjEGRQYnBi8GSgAgBjkGJwZFBB4EMQRJBDAETwAgBEEENQRABDAETwAgBDMEMAQ8BDwEMAAgADIALAAy -AC0EPwRABD4ERAQ4BDsETABHAGUAbgBlAHIAaQBjACAARwByAGEAeQAgAEcAYQBtAG0AYQAgADIALgAy -ACAAUAByAG8AZgBpAGwAZQAAdGV4dAAAAABDb3B5cmlnaHQgQXBwbGUgSW5jLiwgMjAxMgAAWFlaIAAA -AAAAAPNRAAEAAAABFsxjdXJ2AAAAAAAABAAAAAAFAAoADwAUABkAHgAjACgALQAyADcAOwBAAEUASgBP -AFQAWQBeAGMAaABtAHIAdwB8AIEAhgCLAJAAlQCaAJ8ApACpAK4AsgC3ALwAwQDGAMsA0ADVANsA4ADl -AOsA8AD2APsBAQEHAQ0BEwEZAR8BJQErATIBOAE+AUUBTAFSAVkBYAFnAW4BdQF8AYMBiwGSAZoBoQGp -AbEBuQHBAckB0QHZAeEB6QHyAfoCAwIMAhQCHQImAi8COAJBAksCVAJdAmcCcQJ6AoQCjgKYAqICrAK2 -AsECywLVAuAC6wL1AwADCwMWAyEDLQM4A0MDTwNaA2YDcgN+A4oDlgOiA64DugPHA9MD4APsA/kEBgQT -BCAELQQ7BEgEVQRjBHEEfgSMBJoEqAS2BMQE0wThBPAE/gUNBRwFKwU6BUkFWAVnBXcFhgWWBaYFtQXF -BdUF5QX2BgYGFgYnBjcGSAZZBmoGewaMBp0GrwbABtEG4wb1BwcHGQcrBz0HTwdhB3QHhgeZB6wHvwfS -B+UH+AgLCB8IMghGCFoIbgiCCJYIqgi+CNII5wj7CRAJJQk6CU8JZAl5CY8JpAm6Cc8J5Qn7ChEKJwo9 -ClQKagqBCpgKrgrFCtwK8wsLCyILOQtRC2kLgAuYC7ALyAvhC/kMEgwqDEMMXAx1DI4MpwzADNkM8w0N -DSYNQA1aDXQNjg2pDcMN3g34DhMOLg5JDmQOfw6bDrYO0g7uDwkPJQ9BD14Peg+WD7MPzw/sEAkQJhBD -EGEQfhCbELkQ1xD1ERMRMRFPEW0RjBGqEckR6BIHEiYSRRJkEoQSoxLDEuMTAxMjE0MTYxODE6QTxRPl -FAYUJxRJFGoUixStFM4U8BUSFTQVVhV4FZsVvRXgFgMWJhZJFmwWjxayFtYW+hcdF0EXZReJF64X0hf3 -GBsYQBhlGIoYrxjVGPoZIBlFGWsZkRm3Gd0aBBoqGlEadxqeGsUa7BsUGzsbYxuKG7Ib2hwCHCocUhx7 -HKMczBz1HR4dRx1wHZkdwx3sHhYeQB5qHpQevh7pHxMfPh9pH5Qfvx/qIBUgQSBsIJggxCDwIRwhSCF1 -IaEhziH7IiciVSKCIq8i3SMKIzgjZiOUI8Ij8CQfJE0kfCSrJNolCSU4JWgllyXHJfcmJyZXJocmtybo -JxgnSSd6J6sn3CgNKD8ocSiiKNQpBik4KWspnSnQKgIqNSpoKpsqzysCKzYraSudK9EsBSw5LG4soizX -LQwtQS12Last4S4WLkwugi63Lu4vJC9aL5Evxy/+MDUwbDCkMNsxEjFKMYIxujHyMioyYzKbMtQzDTNG -M38zuDPxNCs0ZTSeNNg1EzVNNYc1wjX9Njc2cjauNuk3JDdgN5w31zgUOFA4jDjIOQU5Qjl/Obw5+To2 -OnQ6sjrvOy07azuqO+g8JzxlPKQ84z0iPWE9oT3gPiA+YD6gPuA/IT9hP6I/4kAjQGRApkDnQSlBakGs -Qe5CMEJyQrVC90M6Q31DwEQDREdEikTORRJFVUWaRd5GIkZnRqtG8Ec1R3tHwEgFSEtIkUjXSR1JY0mp -SfBKN0p9SsRLDEtTS5pL4kwqTHJMuk0CTUpNk03cTiVObk63TwBPSU+TT91QJ1BxULtRBlFQUZtR5lIx -UnxSx1MTU19TqlP2VEJUj1TbVShVdVXCVg9WXFapVvdXRFeSV+BYL1h9WMtZGllpWbhaB1pWWqZa9VtF -W5Vb5Vw1XIZc1l0nXXhdyV4aXmxevV8PX2Ffs2AFYFdgqmD8YU9homH1YklinGLwY0Njl2PrZEBklGTp -ZT1lkmXnZj1mkmboZz1nk2fpaD9olmjsaUNpmmnxakhqn2r3a09rp2v/bFdsr20IbWBtuW4SbmtuxG8e -b3hv0XArcIZw4HE6cZVx8HJLcqZzAXNdc7h0FHRwdMx1KHWFdeF2Pnabdvh3VnezeBF4bnjMeSp5iXnn -ekZ6pXsEe2N7wnwhfIF84X1BfaF+AX5ifsJ/I3+Ef+WAR4CogQqBa4HNgjCCkoL0g1eDuoQdhICE44VH -hauGDoZyhteHO4efiASIaYjOiTOJmYn+imSKyoswi5aL/IxjjMqNMY2Yjf+OZo7OjzaPnpAGkG6Q1pE/ -kaiSEZJ6kuOTTZO2lCCUipT0lV+VyZY0lp+XCpd1l+CYTJi4mSSZkJn8mmia1ZtCm6+cHJyJnPedZJ3S -nkCerp8dn4uf+qBpoNihR6G2oiailqMGo3aj5qRWpMelOKWpphqmi6b9p26n4KhSqMSpN6mpqhyqj6sC -q3Wr6axcrNCtRK24ri2uoa8Wr4uwALB1sOqxYLHWskuywrM4s660JbSctRO1irYBtnm28Ldot+C4WbjR -uUq5wro7urW7LrunvCG8m70VvY++Cr6Evv+/er/1wHDA7MFnwePCX8Lbw1jD1MRRxM7FS8XIxkbGw8dB -x7/IPci8yTrJuco4yrfLNsu2zDXMtc01zbXONs62zzfPuNA50LrRPNG+0j/SwdNE08bUSdTL1U7V0dZV -1tjXXNfg2GTY6Nls2fHadtr724DcBdyK3RDdlt4c3qLfKd+v4DbgveFE4cziU+Lb42Pj6+Rz5PzlhOYN -5pbnH+ep6DLovOlG6dDqW+rl63Dr++yG7RHtnO4o7rTvQO/M8Fjw5fFy8f/yjPMZ86f0NPTC9VD13vZt -9vv3ivgZ+Kj5OPnH+lf65/t3/Af8mP0p/br+S/7c/23//9IrLC0uWiRjbGFzc25hbWVYJGNsYXNzZXNf -EBBOU0JpdG1hcEltYWdlUmVwoy0vMFpOU0ltYWdlUmVwWE5TT2JqZWN00issMjNXTlNBcnJheaIyMNIr -LDU2Xk5TTXV0YWJsZUFycmF5ozUyMNM4OQ86OzxXTlNXaGl0ZVxOU0NvbG9yU3BhY2VEMCAwABADgAzS -Kyw+P1dOU0NvbG9yoj4w0issQUJXTlNJbWFnZaJBMAAIABEAGgAkACkAMgA3AEkATABRAFMAYgBoAHUA -fACLAJIAnwCmAK4AsACyALQAuQC7AL0AxADJANQA1gDYANoA3wDiAOQA5gDoAO8BBgEiASQBJhOWE5sT -phOvE8ITxhPRE9oT3xPnE+oT7xP+FAIUCRQRFB4UIxQlFCcULBQ0FDcUPBREAAAAAAAAAgEAAAAAAAAA -QwAAAAAAAAAAAAAAAAAAFEc +S2V5ZWRBcmNoaXZlctEICVRyb290gAGuCwwbHCEaJissMzY5P0JVJG51bGzXDQ4PEBESExQVFhcYGRpW +TlNTaXplViRjbGFzc1xOU0ltYWdlRmxhZ3NWTlNSZXBzW05TVGludENvbG9yV05TQ29sb3JeTlNSZXNp +emluZ01vZGWAAoANEiDDAACAA4AAgAsQAFZ7MSwgMX3SHQ4eIFpOUy5vYmplY3RzoR+ABIAK0h0OIiWi +IySABYAGgAnTDicoKSoaXxAUTlNUSUZGUmVwcmVzZW50YXRpb25fEBlOU0ludGVybmFsTGF5b3V0RGly +ZWN0aW9ugAiAB08REmxNTQAqAAAACgAAABABAAADAAAAAQABAAABAQADAAAAAQABAAABAgADAAAAAgAI +AAgBAwADAAAAAQABAAABBgADAAAAAQABAAABCgADAAAAAQABAAABEQAEAAAAAQAAAAgBEgADAAAAAQAB +AAABFQADAAAAAQACAAABFgADAAAAAQABAAABFwAEAAAAAQAAAAIBHAADAAAAAQABAAABKAADAAAAAQAC +AAABUgADAAAAAQABAAABUwADAAAAAgABAAGHcwAHAAARnAAAANAAAAAAAAARnGFwcGwCAAAAbW50ckdS +QVlYWVogB9wACAAXAA8ALgAPYWNzcEFQUEwAAAAAbm9uZQAAAAAAAAAAAAAAAAAAAAAAAPbWAAEAAAAA +0y1hcHBsAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFZGVzYwAA +AMAAAAB5ZHNjbQAAATwAAAgaY3BydAAACVgAAAAjd3RwdAAACXwAAAAUa1RSQwAACZAAAAgMZGVzYwAA +AAAAAAAfR2VuZXJpYyBHcmF5IEdhbW1hIDIuMiBQcm9maWxlAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAG1s +dWMAAAAAAAAAHwAAAAxza1NLAAAALgAAAYRkYURLAAAAOgAAAbJjYUVTAAAAOAAAAex2aVZOAAAAQAAA +AiRwdEJSAAAASgAAAmR1a1VBAAAALAAAAq5mckZVAAAAPgAAAtpodUhVAAAANAAAAxh6aFRXAAAAGgAA +A0xrb0tSAAAAIgAAA2ZuYk5PAAAAOgAAA4hjc0NaAAAAKAAAA8JoZUlMAAAAJAAAA+pyb1JPAAAAKgAA +BA5kZURFAAAATgAABDhpdElUAAAATgAABIZzdlNFAAAAOAAABNR6aENOAAAAGgAABQxqYUpQAAAAJgAA +BSZlbEdSAAAAKgAABUxwdFBPAAAAUgAABXZubE5MAAAAQAAABchlc0VTAAAATAAABgh0aFRIAAAAMgAA +BlR0clRSAAAAJAAABoZmaUZJAAAARgAABqpockhSAAAAPgAABvBwbFBMAAAASgAABy5hckVHAAAALAAA +B3hydVJVAAAAOgAAB6RlblVTAAAAPAAAB94AVgFhAGUAbwBiAGUAYwBuAOEAIABzAGkAdgDhACAAZwBh +AG0AYQAgADIALAAyAEcAZQBuAGUAcgBpAHMAawAgAGcAcgDlACAAMgAsADIAIABnAGEAbQBtAGEALQBw +AHIAbwBmAGkAbABHAGEAbQBtAGEAIABkAGUAIABnAHIAaQBzAG8AcwAgAGcAZQBuAOgAcgBpAGMAYQAg +ADIALgAyAEMepQB1ACAAaADsAG4AaAAgAE0A4AB1ACAAeADhAG0AIABDAGgAdQBuAGcAIABHAGEAbQBt +AGEAIAAyAC4AMgBQAGUAcgBmAGkAbAAgAEcAZQBuAOkAcgBpAGMAbwAgAGQAYQAgAEcAYQBtAGEAIABk +AGUAIABDAGkAbgB6AGEAcwAgADIALAAyBBcEMAQzBDAEOwRMBD0EMAAgAEcAcgBhAHkALQQzBDAEPAQw +ACAAMgAuADIAUAByAG8AZgBpAGwAIABnAOkAbgDpAHIAaQBxAHUAZQAgAGcAcgBpAHMAIABnAGEAbQBt +AGEAIAAyACwAMgDBAGwAdABhAGwA4QBuAG8AcwAgAHMAegD8AHIAawBlACAAZwBhAG0AbQBhACAAMgAu +ADKQGnUocHCWjlFJXqYAMgAuADKCcl9pY8+P8Md8vBgAINaMwMkAIKwQucgAIAAyAC4AMgAg1QS4XNMM +x3wARwBlAG4AZQByAGkAcwBrACAAZwByAOUAIABnAGEAbQBtAGEAIAAyACwAMgAtAHAAcgBvAGYAaQBs +AE8AYgBlAGMAbgDhACABYQBlAGQA4QAgAGcAYQBtAGEAIAAyAC4AMgXSBdAF3gXUACAF0AXkBdUF6AAg +BdsF3AXcBdkAIAAyAC4AMgBHAGEAbQBhACAAZwByAGkAIABnAGUAbgBlAHIAaQBjAQMAIAAyACwAMgBB +AGwAbABnAGUAbQBlAGkAbgBlAHMAIABHAHIAYQB1AHMAdAB1AGYAZQBuAC0AUAByAG8AZgBpAGwAIABH +AGEAbQBtAGEAIAAyACwAMgBQAHIAbwBmAGkAbABvACAAZwByAGkAZwBpAG8AIABnAGUAbgBlAHIAaQBj +AG8AIABkAGUAbABsAGEAIABnAGEAbQBtAGEAIAAyACwAMgBHAGUAbgBlAHIAaQBzAGsAIABnAHIA5QAg +ADIALAAyACAAZwBhAG0AbQBhAHAAcgBvAGYAaQBsZm6QGnBwXqZ8+2VwADIALgAyY8+P8GWHTvZOAIIs +MLAw7DCkMKww8zDeACAAMgAuADIAIDDXMO0w1TChMKQw6wOTA7UDvQO5A7oDzAAgA5MDugPBA7kAIAOT +A6wDvAO8A7EAIAAyAC4AMgBQAGUAcgBmAGkAbAAgAGcAZQBuAOkAcgBpAGMAbwAgAGQAZQAgAGMAaQBu +AHoAZQBuAHQAbwBzACAAZABhACAARwBhAG0AbQBhACAAMgAsADIAQQBsAGcAZQBtAGUAZQBuACAAZwBy +AGkAagBzACAAZwBhAG0AbQBhACAAMgAsADIALQBwAHIAbwBmAGkAZQBsAFAAZQByAGYAaQBsACAAZwBl +AG4A6QByAGkAYwBvACAAZABlACAAZwBhAG0AbQBhACAAZABlACAAZwByAGkAcwBlAHMAIAAyACwAMg4j +DjEOBw4qDjUOQQ4BDiEOIQ4yDkAOAQ4jDiIOTA4XDjEOSA4nDkQOGwAgADIALgAyAEcAZQBuAGUAbAAg +AEcAcgBpACAARwBhAG0AYQAgADIALAAyAFkAbABlAGkAbgBlAG4AIABoAGEAcgBtAGEAYQBuACAAZwBh +AG0AbQBhACAAMgAsADIAIAAtAHAAcgBvAGYAaQBpAGwAaQBHAGUAbgBlAHIAaQENAGsAaQAgAEcAcgBh +AHkAIABHAGEAbQBtAGEAIAAyAC4AMgAgAHAAcgBvAGYAaQBsAFUAbgBpAHcAZQByAHMAYQBsAG4AeQAg +AHAAcgBvAGYAaQBsACAAcwB6AGEAcgBvAVsAYwBpACAAZwBhAG0AbQBhACAAMgAsADIGOgYnBkUGJwAg +ADIALgAyACAGRAZIBkYAIAYxBkUGJwYvBkoAIAY5BicGRQQeBDEESQQwBE8AIARBBDUEQAQwBE8AIAQz +BDAEPAQ8BDAAIAAyACwAMgAtBD8EQAQ+BEQEOAQ7BEwARwBlAG4AZQByAGkAYwAgAEcAcgBhAHkAIABH +AGEAbQBtAGEAIAAyAC4AMgAgAFAAcgBvAGYAaQBsAGUAAHRleHQAAAAAQ29weXJpZ2h0IEFwcGxlIElu +Yy4sIDIwMTIAAFhZWiAAAAAAAADzUQABAAAAARbMY3VydgAAAAAAAAQAAAAABQAKAA8AFAAZAB4AIwAo +AC0AMgA3ADsAQABFAEoATwBUAFkAXgBjAGgAbQByAHcAfACBAIYAiwCQAJUAmgCfAKQAqQCuALIAtwC8 +AMEAxgDLANAA1QDbAOAA5QDrAPAA9gD7AQEBBwENARMBGQEfASUBKwEyATgBPgFFAUwBUgFZAWABZwFu +AXUBfAGDAYsBkgGaAaEBqQGxAbkBwQHJAdEB2QHhAekB8gH6AgMCDAIUAh0CJgIvAjgCQQJLAlQCXQJn +AnECegKEAo4CmAKiAqwCtgLBAssC1QLgAusC9QMAAwsDFgMhAy0DOANDA08DWgNmA3IDfgOKA5YDogOu +A7oDxwPTA+AD7AP5BAYEEwQgBC0EOwRIBFUEYwRxBH4EjASaBKgEtgTEBNME4QTwBP4FDQUcBSsFOgVJ +BVgFZwV3BYYFlgWmBbUFxQXVBeUF9gYGBhYGJwY3BkgGWQZqBnsGjAadBq8GwAbRBuMG9QcHBxkHKwc9 +B08HYQd0B4YHmQesB78H0gflB/gICwgfCDIIRghaCG4IggiWCKoIvgjSCOcI+wkQCSUJOglPCWQJeQmP +CaQJugnPCeUJ+woRCicKPQpUCmoKgQqYCq4KxQrcCvMLCwsiCzkLUQtpC4ALmAuwC8gL4Qv5DBIMKgxD +DFwMdQyODKcMwAzZDPMNDQ0mDUANWg10DY4NqQ3DDd4N+A4TDi4OSQ5kDn8Omw62DtIO7g8JDyUPQQ9e +D3oPlg+zD88P7BAJECYQQxBhEH4QmxC5ENcQ9RETETERTxFtEYwRqhHJEegSBxImEkUSZBKEEqMSwxLj +EwMTIxNDE2MTgxOkE8UT5RQGFCcUSRRqFIsUrRTOFPAVEhU0FVYVeBWbFb0V4BYDFiYWSRZsFo8WshbW +FvoXHRdBF2UXiReuF9IX9xgbGEAYZRiKGK8Y1Rj6GSAZRRlrGZEZtxndGgQaKhpRGncanhrFGuwbFBs7 +G2MbihuyG9ocAhwqHFIcexyjHMwc9R0eHUcdcB2ZHcMd7B4WHkAeah6UHr4e6R8THz4faR+UH78f6iAV +IEEgbCCYIMQg8CEcIUghdSGhIc4h+yInIlUigiKvIt0jCiM4I2YjlCPCI/AkHyRNJHwkqyTaJQklOCVo +JZclxyX3JicmVyaHJrcm6CcYJ0kneierJ9woDSg/KHEooijUKQYpOClrKZ0p0CoCKjUqaCqbKs8rAis2 +K2krnSvRLAUsOSxuLKIs1y0MLUEtdi2rLeEuFi5MLoIuty7uLyQvWi+RL8cv/jA1MGwwpDDbMRIxSjGC +Mbox8jIqMmMymzLUMw0zRjN/M7gz8TQrNGU0njTYNRM1TTWHNcI1/TY3NnI2rjbpNyQ3YDecN9c4FDhQ +OIw4yDkFOUI5fzm8Ofk6Njp0OrI67zstO2s7qjvoPCc8ZTykPOM9Ij1hPaE94D4gPmA+oD7gPyE/YT+i +P+JAI0BkQKZA50EpQWpBrEHuQjBCckK1QvdDOkN9Q8BEA0RHRIpEzkUSRVVFmkXeRiJGZ0arRvBHNUd7 +R8BIBUhLSJFI10kdSWNJqUnwSjdKfUrESwxLU0uaS+JMKkxyTLpNAk1KTZNN3E4lTm5Ot08AT0lPk0/d +UCdQcVC7UQZRUFGbUeZSMVJ8UsdTE1NfU6pT9lRCVI9U21UoVXVVwlYPVlxWqVb3V0RXklfgWC9YfVjL +WRpZaVm4WgdaVlqmWvVbRVuVW+VcNVyGXNZdJ114XcleGl5sXr1fD19hX7NgBWBXYKpg/GFPYaJh9WJJ +Ypxi8GNDY5dj62RAZJRk6WU9ZZJl52Y9ZpJm6Gc9Z5Nn6Wg/aJZo7GlDaZpp8WpIap9q92tPa6dr/2xX +bK9tCG1gbbluEm5rbsRvHm94b9FwK3CGcOBxOnGVcfByS3KmcwFzXXO4dBR0cHTMdSh1hXXhdj52m3b4 +d1Z3s3gReG54zHkqeYl553pGeqV7BHtje8J8IXyBfOF9QX2hfgF+Yn7CfyN/hH/lgEeAqIEKgWuBzYIw +gpKC9INXg7qEHYSAhOOFR4Wrhg6GcobXhzuHn4gEiGmIzokziZmJ/opkisqLMIuWi/yMY4zKjTGNmI3/ +jmaOzo82j56QBpBukNaRP5GokhGSepLjk02TtpQglIqU9JVflcmWNJaflwqXdZfgmEyYuJkkmZCZ/Jpo +mtWbQpuvnByciZz3nWSd0p5Anq6fHZ+Ln/qgaaDYoUehtqImopajBqN2o+akVqTHpTilqaYapoum/adu +p+CoUqjEqTepqaocqo+rAqt1q+msXKzQrUStuK4trqGvFq+LsACwdbDqsWCx1rJLssKzOLOutCW0nLUT +tYq2AbZ5tvC3aLfguFm40blKucK6O7q1uy67p7whvJu9Fb2Pvgq+hL7/v3q/9cBwwOzBZ8Hjwl/C28NY +w9TEUcTOxUvFyMZGxsPHQce/yD3IvMk6ybnKOMq3yzbLtsw1zLXNNc21zjbOts83z7jQOdC60TzRvtI/ +0sHTRNPG1EnUy9VO1dHWVdbY11zX4Nhk2OjZbNnx2nba+9uA3AXcit0Q3ZbeHN6i3ynfr+A24L3hROHM +4lPi2+Nj4+vkc+T85YTmDeaW5x/nqegy6LzpRunQ6lvq5etw6/vshu0R7ZzuKO6070DvzPBY8OXxcvH/ +8ozzGfOn9DT0wvVQ9d72bfb794r4Gfio+Tj5x/pX+uf7d/wH/Jj9Kf26/kv+3P9t///SLS4vMFokY2xh +c3NuYW1lWCRjbGFzc2VzXxAQTlNCaXRtYXBJbWFnZVJlcKMvMTJaTlNJbWFnZVJlcFhOU09iamVjdNIt +LjQ1V05TQXJyYXmiNDLSLS43OF5OU011dGFibGVBcnJheaM3NDLTOjsOPD0+V05TV2hpdGVcTlNDb2xv +clNwYWNlRDAgMAAQA4AM0i0uQEFXTlNDb2xvcqJAMtItLkNEV05TSW1hZ2WiQzIACAARABoAJAApADIA +NwBJAEwAUQBTAGIAaAB3AH4AhQCSAJkApQCtALwAvgDAAMUAxwDJAMsAzQDUANkA5ADmAOgA6gDvAPIA +9AD2APgA/wEWATIBNAE2E6YTqxO2E78T0hPWE+ET6hPvE/cT+hP/FA4UEhQZFCEULhQzFDUUNxQ8FEQU +RxRMFFQAAAAAAAACAQAAAAAAAABFAAAAAAAAAAAAAAAAAAAUVw diff --git a/i18n/src/main/resources/ko.lproj/Browser.xib b/i18n/src/main/resources/ko.lproj/Browser.xib index d08de70ff9..8091e9f529 100644 --- a/i18n/src/main/resources/ko.lproj/Browser.xib +++ b/i18n/src/main/resources/ko.lproj/Browser.xib @@ -1,7 +1,7 @@ - + - + @@ -38,7 +38,7 @@ - + @@ -62,27 +62,27 @@ @@ -256,7 +256,7 @@ - + @@ -553,95 +553,95 @@ DQ YnBsaXN0MDDUAQIDBAUGBwpYJHZlcnNpb25ZJGFyY2hpdmVyVCR0b3BYJG9iamVjdHMSAAGGoF8QD05T -S2V5ZWRBcmNoaXZlctEICVRyb290gAGuCwwZGh8UJCkqMTQ3PUBVJG51bGzWDQ4PEBESExQVFhcYVk5T -U2l6ZV5OU1Jlc2l6aW5nTW9kZVYkY2xhc3NcTlNJbWFnZUZsYWdzVk5TUmVwc1dOU0NvbG9ygAIQAIAN -EiDDAACAA4ALVnsxLCAxfdIbDxweWk5TLm9iamVjdHOhHYAEgArSGw8gI6IhIoAFgAaACdMPJSYnKBRf -EBROU1RJRkZSZXByZXNlbnRhdGlvbl8QGU5TSW50ZXJuYWxMYXlvdXREaXJlY3Rpb26ACIAHTxESbE1N -ACoAAAAKAAAAEAEAAAMAAAABAAEAAAEBAAMAAAABAAEAAAECAAMAAAACAAgACAEDAAMAAAABAAEAAAEG -AAMAAAABAAEAAAEKAAMAAAABAAEAAAERAAQAAAABAAAACAESAAMAAAABAAEAAAEVAAMAAAABAAIAAAEW -AAMAAAABAAEAAAEXAAQAAAABAAAAAgEcAAMAAAABAAEAAAEoAAMAAAABAAIAAAFSAAMAAAABAAEAAAFT -AAMAAAACAAEAAYdzAAcAABGcAAAA0AAAAAAAABGcYXBwbAIAAABtbnRyR1JBWVhZWiAH3AAIABcADwAu -AA9hY3NwQVBQTAAAAABub25lAAAAAAAAAAAAAAAAAAAAAAAA9tYAAQAAAADTLWFwcGwAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAVkZXNjAAAAwAAAAHlkc2NtAAABPAAA -CBpjcHJ0AAAJWAAAACN3dHB0AAAJfAAAABRrVFJDAAAJkAAACAxkZXNjAAAAAAAAAB9HZW5lcmljIEdy -YXkgR2FtbWEgMi4yIFByb2ZpbGUAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAbWx1YwAAAAAAAAAfAAAADHNr -U0sAAAAuAAABhGRhREsAAAA6AAABsmNhRVMAAAA4AAAB7HZpVk4AAABAAAACJHB0QlIAAABKAAACZHVr -VUEAAAAsAAACrmZyRlUAAAA+AAAC2mh1SFUAAAA0AAADGHpoVFcAAAAaAAADTGtvS1IAAAAiAAADZm5i -Tk8AAAA6AAADiGNzQ1oAAAAoAAADwmhlSUwAAAAkAAAD6nJvUk8AAAAqAAAEDmRlREUAAABOAAAEOGl0 -SVQAAABOAAAEhnN2U0UAAAA4AAAE1HpoQ04AAAAaAAAFDGphSlAAAAAmAAAFJmVsR1IAAAAqAAAFTHB0 -UE8AAABSAAAFdm5sTkwAAABAAAAFyGVzRVMAAABMAAAGCHRoVEgAAAAyAAAGVHRyVFIAAAAkAAAGhmZp -RkkAAABGAAAGqmhySFIAAAA+AAAG8HBsUEwAAABKAAAHLmFyRUcAAAAsAAAHeHJ1UlUAAAA6AAAHpGVu -VVMAAAA8AAAH3gBWAWEAZQBvAGIAZQBjAG4A4QAgAHMAaQB2AOEAIABnAGEAbQBhACAAMgAsADIARwBl -AG4AZQByAGkAcwBrACAAZwByAOUAIAAyACwAMgAgAGcAYQBtAG0AYQAtAHAAcgBvAGYAaQBsAEcAYQBt -AG0AYQAgAGQAZQAgAGcAcgBpAHMAbwBzACAAZwBlAG4A6AByAGkAYwBhACAAMgAuADIAQx6lAHUAIABo -AOwAbgBoACAATQDgAHUAIAB4AOEAbQAgAEMAaAB1AG4AZwAgAEcAYQBtAG0AYQAgADIALgAyAFAAZQBy -AGYAaQBsACAARwBlAG4A6QByAGkAYwBvACAAZABhACAARwBhAG0AYQAgAGQAZQAgAEMAaQBuAHoAYQBz -ACAAMgAsADIEFwQwBDMEMAQ7BEwEPQQwACAARwByAGEAeQAtBDMEMAQ8BDAAIAAyAC4AMgBQAHIAbwBm -AGkAbAAgAGcA6QBuAOkAcgBpAHEAdQBlACAAZwByAGkAcwAgAGcAYQBtAG0AYQAgADIALAAyAMEAbAB0 -AGEAbADhAG4AbwBzACAAcwB6APwAcgBrAGUAIABnAGEAbQBtAGEAIAAyAC4AMpAadShwcJaOUUlepgAy -AC4AMoJyX2ljz4/wx3y8GAAg1ozAyQAgrBC5yAAgADIALgAyACDVBLhc0wzHfABHAGUAbgBlAHIAaQBz -AGsAIABnAHIA5QAgAGcAYQBtAG0AYQAgADIALAAyAC0AcAByAG8AZgBpAGwATwBiAGUAYwBuAOEAIAFh -AGUAZADhACAAZwBhAG0AYQAgADIALgAyBdIF0AXeBdQAIAXQBeQF1QXoACAF2wXcBdwF2QAgADIALgAy -AEcAYQBtAGEAIABnAHIAaQAgAGcAZQBuAGUAcgBpAGMBAwAgADIALAAyAEEAbABsAGcAZQBtAGUAaQBu -AGUAcwAgAEcAcgBhAHUAcwB0AHUAZgBlAG4ALQBQAHIAbwBmAGkAbAAgAEcAYQBtAG0AYQAgADIALAAy -AFAAcgBvAGYAaQBsAG8AIABnAHIAaQBnAGkAbwAgAGcAZQBuAGUAcgBpAGMAbwAgAGQAZQBsAGwAYQAg -AGcAYQBtAG0AYQAgADIALAAyAEcAZQBuAGUAcgBpAHMAawAgAGcAcgDlACAAMgAsADIAIABnAGEAbQBt -AGEAcAByAG8AZgBpAGxmbpAacHBepnz7ZXAAMgAuADJjz4/wZYdO9k4AgiwwsDDsMKQwrDDzMN4AIAAy -AC4AMgAgMNcw7TDVMKEwpDDrA5MDtQO9A7kDugPMACADkwO6A8EDuQAgA5MDrAO8A7wDsQAgADIALgAy -AFAAZQByAGYAaQBsACAAZwBlAG4A6QByAGkAYwBvACAAZABlACAAYwBpAG4AegBlAG4AdABvAHMAIABk -AGEAIABHAGEAbQBtAGEAIAAyACwAMgBBAGwAZwBlAG0AZQBlAG4AIABnAHIAaQBqAHMAIABnAGEAbQBt -AGEAIAAyACwAMgAtAHAAcgBvAGYAaQBlAGwAUABlAHIAZgBpAGwAIABnAGUAbgDpAHIAaQBjAG8AIABk -AGUAIABnAGEAbQBtAGEAIABkAGUAIABnAHIAaQBzAGUAcwAgADIALAAyDiMOMQ4HDioONQ5BDgEOIQ4h -DjIOQA4BDiMOIg5MDhcOMQ5IDicORA4bACAAMgAuADIARwBlAG4AZQBsACAARwByAGkAIABHAGEAbQBh -ACAAMgAsADIAWQBsAGUAaQBuAGUAbgAgAGgAYQByAG0AYQBhAG4AIABnAGEAbQBtAGEAIAAyACwAMgAg -AC0AcAByAG8AZgBpAGkAbABpAEcAZQBuAGUAcgBpAQ0AawBpACAARwByAGEAeQAgAEcAYQBtAG0AYQAg -ADIALgAyACAAcAByAG8AZgBpAGwAVQBuAGkAdwBlAHIAcwBhAGwAbgB5ACAAcAByAG8AZgBpAGwAIABz -AHoAYQByAG8BWwBjAGkAIABnAGEAbQBtAGEAIAAyACwAMgY6BicGRQYnACAAMgAuADIAIAZEBkgGRgAg -BjEGRQYnBi8GSgAgBjkGJwZFBB4EMQRJBDAETwAgBEEENQRABDAETwAgBDMEMAQ8BDwEMAAgADIALAAy -AC0EPwRABD4ERAQ4BDsETABHAGUAbgBlAHIAaQBjACAARwByAGEAeQAgAEcAYQBtAG0AYQAgADIALgAy -ACAAUAByAG8AZgBpAGwAZQAAdGV4dAAAAABDb3B5cmlnaHQgQXBwbGUgSW5jLiwgMjAxMgAAWFlaIAAA -AAAAAPNRAAEAAAABFsxjdXJ2AAAAAAAABAAAAAAFAAoADwAUABkAHgAjACgALQAyADcAOwBAAEUASgBP -AFQAWQBeAGMAaABtAHIAdwB8AIEAhgCLAJAAlQCaAJ8ApACpAK4AsgC3ALwAwQDGAMsA0ADVANsA4ADl -AOsA8AD2APsBAQEHAQ0BEwEZAR8BJQErATIBOAE+AUUBTAFSAVkBYAFnAW4BdQF8AYMBiwGSAZoBoQGp -AbEBuQHBAckB0QHZAeEB6QHyAfoCAwIMAhQCHQImAi8COAJBAksCVAJdAmcCcQJ6AoQCjgKYAqICrAK2 -AsECywLVAuAC6wL1AwADCwMWAyEDLQM4A0MDTwNaA2YDcgN+A4oDlgOiA64DugPHA9MD4APsA/kEBgQT -BCAELQQ7BEgEVQRjBHEEfgSMBJoEqAS2BMQE0wThBPAE/gUNBRwFKwU6BUkFWAVnBXcFhgWWBaYFtQXF -BdUF5QX2BgYGFgYnBjcGSAZZBmoGewaMBp0GrwbABtEG4wb1BwcHGQcrBz0HTwdhB3QHhgeZB6wHvwfS -B+UH+AgLCB8IMghGCFoIbgiCCJYIqgi+CNII5wj7CRAJJQk6CU8JZAl5CY8JpAm6Cc8J5Qn7ChEKJwo9 -ClQKagqBCpgKrgrFCtwK8wsLCyILOQtRC2kLgAuYC7ALyAvhC/kMEgwqDEMMXAx1DI4MpwzADNkM8w0N -DSYNQA1aDXQNjg2pDcMN3g34DhMOLg5JDmQOfw6bDrYO0g7uDwkPJQ9BD14Peg+WD7MPzw/sEAkQJhBD -EGEQfhCbELkQ1xD1ERMRMRFPEW0RjBGqEckR6BIHEiYSRRJkEoQSoxLDEuMTAxMjE0MTYxODE6QTxRPl -FAYUJxRJFGoUixStFM4U8BUSFTQVVhV4FZsVvRXgFgMWJhZJFmwWjxayFtYW+hcdF0EXZReJF64X0hf3 -GBsYQBhlGIoYrxjVGPoZIBlFGWsZkRm3Gd0aBBoqGlEadxqeGsUa7BsUGzsbYxuKG7Ib2hwCHCocUhx7 -HKMczBz1HR4dRx1wHZkdwx3sHhYeQB5qHpQevh7pHxMfPh9pH5Qfvx/qIBUgQSBsIJggxCDwIRwhSCF1 -IaEhziH7IiciVSKCIq8i3SMKIzgjZiOUI8Ij8CQfJE0kfCSrJNolCSU4JWgllyXHJfcmJyZXJocmtybo -JxgnSSd6J6sn3CgNKD8ocSiiKNQpBik4KWspnSnQKgIqNSpoKpsqzysCKzYraSudK9EsBSw5LG4soizX -LQwtQS12Last4S4WLkwugi63Lu4vJC9aL5Evxy/+MDUwbDCkMNsxEjFKMYIxujHyMioyYzKbMtQzDTNG -M38zuDPxNCs0ZTSeNNg1EzVNNYc1wjX9Njc2cjauNuk3JDdgN5w31zgUOFA4jDjIOQU5Qjl/Obw5+To2 -OnQ6sjrvOy07azuqO+g8JzxlPKQ84z0iPWE9oT3gPiA+YD6gPuA/IT9hP6I/4kAjQGRApkDnQSlBakGs -Qe5CMEJyQrVC90M6Q31DwEQDREdEikTORRJFVUWaRd5GIkZnRqtG8Ec1R3tHwEgFSEtIkUjXSR1JY0mp -SfBKN0p9SsRLDEtTS5pL4kwqTHJMuk0CTUpNk03cTiVObk63TwBPSU+TT91QJ1BxULtRBlFQUZtR5lIx -UnxSx1MTU19TqlP2VEJUj1TbVShVdVXCVg9WXFapVvdXRFeSV+BYL1h9WMtZGllpWbhaB1pWWqZa9VtF -W5Vb5Vw1XIZc1l0nXXhdyV4aXmxevV8PX2Ffs2AFYFdgqmD8YU9homH1YklinGLwY0Njl2PrZEBklGTp -ZT1lkmXnZj1mkmboZz1nk2fpaD9olmjsaUNpmmnxakhqn2r3a09rp2v/bFdsr20IbWBtuW4SbmtuxG8e -b3hv0XArcIZw4HE6cZVx8HJLcqZzAXNdc7h0FHRwdMx1KHWFdeF2Pnabdvh3VnezeBF4bnjMeSp5iXnn -ekZ6pXsEe2N7wnwhfIF84X1BfaF+AX5ifsJ/I3+Ef+WAR4CogQqBa4HNgjCCkoL0g1eDuoQdhICE44VH -hauGDoZyhteHO4efiASIaYjOiTOJmYn+imSKyoswi5aL/IxjjMqNMY2Yjf+OZo7OjzaPnpAGkG6Q1pE/ -kaiSEZJ6kuOTTZO2lCCUipT0lV+VyZY0lp+XCpd1l+CYTJi4mSSZkJn8mmia1ZtCm6+cHJyJnPedZJ3S -nkCerp8dn4uf+qBpoNihR6G2oiailqMGo3aj5qRWpMelOKWpphqmi6b9p26n4KhSqMSpN6mpqhyqj6sC -q3Wr6axcrNCtRK24ri2uoa8Wr4uwALB1sOqxYLHWskuywrM4s660JbSctRO1irYBtnm28Ldot+C4WbjR -uUq5wro7urW7LrunvCG8m70VvY++Cr6Evv+/er/1wHDA7MFnwePCX8Lbw1jD1MRRxM7FS8XIxkbGw8dB -x7/IPci8yTrJuco4yrfLNsu2zDXMtc01zbXONs62zzfPuNA50LrRPNG+0j/SwdNE08bUSdTL1U7V0dZV -1tjXXNfg2GTY6Nls2fHadtr724DcBdyK3RDdlt4c3qLfKd+v4DbgveFE4cziU+Lb42Pj6+Rz5PzlhOYN -5pbnH+ep6DLovOlG6dDqW+rl63Dr++yG7RHtnO4o7rTvQO/M8Fjw5fFy8f/yjPMZ86f0NPTC9VD13vZt -9vv3ivgZ+Kj5OPnH+lf65/t3/Af8mP0p/br+S/7c/23//9IrLC0uWiRjbGFzc25hbWVYJGNsYXNzZXNf -EBBOU0JpdG1hcEltYWdlUmVwoy0vMFpOU0ltYWdlUmVwWE5TT2JqZWN00issMjNXTlNBcnJheaIyMNIr -LDU2Xk5TTXV0YWJsZUFycmF5ozUyMNM4OQ86OzxXTlNXaGl0ZVxOU0NvbG9yU3BhY2VEMCAwABADgAzS -Kyw+P1dOU0NvbG9yoj4w0issQUJXTlNJbWFnZaJBMAAIABEAGgAkACkAMgA3AEkATABRAFMAYgBoAHUA -fACLAJIAnwCmAK4AsACyALQAuQC7AL0AxADJANQA1gDYANoA3wDiAOQA5gDoAO8BBgEiASQBJhOWE5sT -phOvE8ITxhPRE9oT3xPnE+oT7xP+FAIUCRQRFB4UIxQlFCcULBQ0FDcUPBREAAAAAAAAAgEAAAAAAAAA -QwAAAAAAAAAAAAAAAAAAFEc +S2V5ZWRBcmNoaXZlctEICVRyb290gAGuCwwbHCEaJissMzY5P0JVJG51bGzXDQ4PEBESExQVFhcYGRpW +TlNTaXplViRjbGFzc1xOU0ltYWdlRmxhZ3NWTlNSZXBzW05TVGludENvbG9yV05TQ29sb3JeTlNSZXNp +emluZ01vZGWAAoANEiDDAACAA4AAgAsQAFZ7MSwgMX3SHQ4eIFpOUy5vYmplY3RzoR+ABIAK0h0OIiWi +IySABYAGgAnTDicoKSoaXxAUTlNUSUZGUmVwcmVzZW50YXRpb25fEBlOU0ludGVybmFsTGF5b3V0RGly +ZWN0aW9ugAiAB08REmxNTQAqAAAACgAAABABAAADAAAAAQABAAABAQADAAAAAQABAAABAgADAAAAAgAI +AAgBAwADAAAAAQABAAABBgADAAAAAQABAAABCgADAAAAAQABAAABEQAEAAAAAQAAAAgBEgADAAAAAQAB +AAABFQADAAAAAQACAAABFgADAAAAAQABAAABFwAEAAAAAQAAAAIBHAADAAAAAQABAAABKAADAAAAAQAC +AAABUgADAAAAAQABAAABUwADAAAAAgABAAGHcwAHAAARnAAAANAAAAAAAAARnGFwcGwCAAAAbW50ckdS +QVlYWVogB9wACAAXAA8ALgAPYWNzcEFQUEwAAAAAbm9uZQAAAAAAAAAAAAAAAAAAAAAAAPbWAAEAAAAA +0y1hcHBsAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFZGVzYwAA +AMAAAAB5ZHNjbQAAATwAAAgaY3BydAAACVgAAAAjd3RwdAAACXwAAAAUa1RSQwAACZAAAAgMZGVzYwAA +AAAAAAAfR2VuZXJpYyBHcmF5IEdhbW1hIDIuMiBQcm9maWxlAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAG1s +dWMAAAAAAAAAHwAAAAxza1NLAAAALgAAAYRkYURLAAAAOgAAAbJjYUVTAAAAOAAAAex2aVZOAAAAQAAA +AiRwdEJSAAAASgAAAmR1a1VBAAAALAAAAq5mckZVAAAAPgAAAtpodUhVAAAANAAAAxh6aFRXAAAAGgAA +A0xrb0tSAAAAIgAAA2ZuYk5PAAAAOgAAA4hjc0NaAAAAKAAAA8JoZUlMAAAAJAAAA+pyb1JPAAAAKgAA +BA5kZURFAAAATgAABDhpdElUAAAATgAABIZzdlNFAAAAOAAABNR6aENOAAAAGgAABQxqYUpQAAAAJgAA +BSZlbEdSAAAAKgAABUxwdFBPAAAAUgAABXZubE5MAAAAQAAABchlc0VTAAAATAAABgh0aFRIAAAAMgAA +BlR0clRSAAAAJAAABoZmaUZJAAAARgAABqpockhSAAAAPgAABvBwbFBMAAAASgAABy5hckVHAAAALAAA +B3hydVJVAAAAOgAAB6RlblVTAAAAPAAAB94AVgFhAGUAbwBiAGUAYwBuAOEAIABzAGkAdgDhACAAZwBh +AG0AYQAgADIALAAyAEcAZQBuAGUAcgBpAHMAawAgAGcAcgDlACAAMgAsADIAIABnAGEAbQBtAGEALQBw +AHIAbwBmAGkAbABHAGEAbQBtAGEAIABkAGUAIABnAHIAaQBzAG8AcwAgAGcAZQBuAOgAcgBpAGMAYQAg +ADIALgAyAEMepQB1ACAAaADsAG4AaAAgAE0A4AB1ACAAeADhAG0AIABDAGgAdQBuAGcAIABHAGEAbQBt +AGEAIAAyAC4AMgBQAGUAcgBmAGkAbAAgAEcAZQBuAOkAcgBpAGMAbwAgAGQAYQAgAEcAYQBtAGEAIABk +AGUAIABDAGkAbgB6AGEAcwAgADIALAAyBBcEMAQzBDAEOwRMBD0EMAAgAEcAcgBhAHkALQQzBDAEPAQw +ACAAMgAuADIAUAByAG8AZgBpAGwAIABnAOkAbgDpAHIAaQBxAHUAZQAgAGcAcgBpAHMAIABnAGEAbQBt +AGEAIAAyACwAMgDBAGwAdABhAGwA4QBuAG8AcwAgAHMAegD8AHIAawBlACAAZwBhAG0AbQBhACAAMgAu +ADKQGnUocHCWjlFJXqYAMgAuADKCcl9pY8+P8Md8vBgAINaMwMkAIKwQucgAIAAyAC4AMgAg1QS4XNMM +x3wARwBlAG4AZQByAGkAcwBrACAAZwByAOUAIABnAGEAbQBtAGEAIAAyACwAMgAtAHAAcgBvAGYAaQBs +AE8AYgBlAGMAbgDhACABYQBlAGQA4QAgAGcAYQBtAGEAIAAyAC4AMgXSBdAF3gXUACAF0AXkBdUF6AAg +BdsF3AXcBdkAIAAyAC4AMgBHAGEAbQBhACAAZwByAGkAIABnAGUAbgBlAHIAaQBjAQMAIAAyACwAMgBB +AGwAbABnAGUAbQBlAGkAbgBlAHMAIABHAHIAYQB1AHMAdAB1AGYAZQBuAC0AUAByAG8AZgBpAGwAIABH +AGEAbQBtAGEAIAAyACwAMgBQAHIAbwBmAGkAbABvACAAZwByAGkAZwBpAG8AIABnAGUAbgBlAHIAaQBj +AG8AIABkAGUAbABsAGEAIABnAGEAbQBtAGEAIAAyACwAMgBHAGUAbgBlAHIAaQBzAGsAIABnAHIA5QAg +ADIALAAyACAAZwBhAG0AbQBhAHAAcgBvAGYAaQBsZm6QGnBwXqZ8+2VwADIALgAyY8+P8GWHTvZOAIIs +MLAw7DCkMKww8zDeACAAMgAuADIAIDDXMO0w1TChMKQw6wOTA7UDvQO5A7oDzAAgA5MDugPBA7kAIAOT +A6wDvAO8A7EAIAAyAC4AMgBQAGUAcgBmAGkAbAAgAGcAZQBuAOkAcgBpAGMAbwAgAGQAZQAgAGMAaQBu +AHoAZQBuAHQAbwBzACAAZABhACAARwBhAG0AbQBhACAAMgAsADIAQQBsAGcAZQBtAGUAZQBuACAAZwBy +AGkAagBzACAAZwBhAG0AbQBhACAAMgAsADIALQBwAHIAbwBmAGkAZQBsAFAAZQByAGYAaQBsACAAZwBl +AG4A6QByAGkAYwBvACAAZABlACAAZwBhAG0AbQBhACAAZABlACAAZwByAGkAcwBlAHMAIAAyACwAMg4j +DjEOBw4qDjUOQQ4BDiEOIQ4yDkAOAQ4jDiIOTA4XDjEOSA4nDkQOGwAgADIALgAyAEcAZQBuAGUAbAAg +AEcAcgBpACAARwBhAG0AYQAgADIALAAyAFkAbABlAGkAbgBlAG4AIABoAGEAcgBtAGEAYQBuACAAZwBh +AG0AbQBhACAAMgAsADIAIAAtAHAAcgBvAGYAaQBpAGwAaQBHAGUAbgBlAHIAaQENAGsAaQAgAEcAcgBh +AHkAIABHAGEAbQBtAGEAIAAyAC4AMgAgAHAAcgBvAGYAaQBsAFUAbgBpAHcAZQByAHMAYQBsAG4AeQAg +AHAAcgBvAGYAaQBsACAAcwB6AGEAcgBvAVsAYwBpACAAZwBhAG0AbQBhACAAMgAsADIGOgYnBkUGJwAg +ADIALgAyACAGRAZIBkYAIAYxBkUGJwYvBkoAIAY5BicGRQQeBDEESQQwBE8AIARBBDUEQAQwBE8AIAQz +BDAEPAQ8BDAAIAAyACwAMgAtBD8EQAQ+BEQEOAQ7BEwARwBlAG4AZQByAGkAYwAgAEcAcgBhAHkAIABH +AGEAbQBtAGEAIAAyAC4AMgAgAFAAcgBvAGYAaQBsAGUAAHRleHQAAAAAQ29weXJpZ2h0IEFwcGxlIElu +Yy4sIDIwMTIAAFhZWiAAAAAAAADzUQABAAAAARbMY3VydgAAAAAAAAQAAAAABQAKAA8AFAAZAB4AIwAo +AC0AMgA3ADsAQABFAEoATwBUAFkAXgBjAGgAbQByAHcAfACBAIYAiwCQAJUAmgCfAKQAqQCuALIAtwC8 +AMEAxgDLANAA1QDbAOAA5QDrAPAA9gD7AQEBBwENARMBGQEfASUBKwEyATgBPgFFAUwBUgFZAWABZwFu +AXUBfAGDAYsBkgGaAaEBqQGxAbkBwQHJAdEB2QHhAekB8gH6AgMCDAIUAh0CJgIvAjgCQQJLAlQCXQJn +AnECegKEAo4CmAKiAqwCtgLBAssC1QLgAusC9QMAAwsDFgMhAy0DOANDA08DWgNmA3IDfgOKA5YDogOu +A7oDxwPTA+AD7AP5BAYEEwQgBC0EOwRIBFUEYwRxBH4EjASaBKgEtgTEBNME4QTwBP4FDQUcBSsFOgVJ +BVgFZwV3BYYFlgWmBbUFxQXVBeUF9gYGBhYGJwY3BkgGWQZqBnsGjAadBq8GwAbRBuMG9QcHBxkHKwc9 +B08HYQd0B4YHmQesB78H0gflB/gICwgfCDIIRghaCG4IggiWCKoIvgjSCOcI+wkQCSUJOglPCWQJeQmP +CaQJugnPCeUJ+woRCicKPQpUCmoKgQqYCq4KxQrcCvMLCwsiCzkLUQtpC4ALmAuwC8gL4Qv5DBIMKgxD +DFwMdQyODKcMwAzZDPMNDQ0mDUANWg10DY4NqQ3DDd4N+A4TDi4OSQ5kDn8Omw62DtIO7g8JDyUPQQ9e +D3oPlg+zD88P7BAJECYQQxBhEH4QmxC5ENcQ9RETETERTxFtEYwRqhHJEegSBxImEkUSZBKEEqMSwxLj +EwMTIxNDE2MTgxOkE8UT5RQGFCcUSRRqFIsUrRTOFPAVEhU0FVYVeBWbFb0V4BYDFiYWSRZsFo8WshbW +FvoXHRdBF2UXiReuF9IX9xgbGEAYZRiKGK8Y1Rj6GSAZRRlrGZEZtxndGgQaKhpRGncanhrFGuwbFBs7 +G2MbihuyG9ocAhwqHFIcexyjHMwc9R0eHUcdcB2ZHcMd7B4WHkAeah6UHr4e6R8THz4faR+UH78f6iAV +IEEgbCCYIMQg8CEcIUghdSGhIc4h+yInIlUigiKvIt0jCiM4I2YjlCPCI/AkHyRNJHwkqyTaJQklOCVo +JZclxyX3JicmVyaHJrcm6CcYJ0kneierJ9woDSg/KHEooijUKQYpOClrKZ0p0CoCKjUqaCqbKs8rAis2 +K2krnSvRLAUsOSxuLKIs1y0MLUEtdi2rLeEuFi5MLoIuty7uLyQvWi+RL8cv/jA1MGwwpDDbMRIxSjGC +Mbox8jIqMmMymzLUMw0zRjN/M7gz8TQrNGU0njTYNRM1TTWHNcI1/TY3NnI2rjbpNyQ3YDecN9c4FDhQ +OIw4yDkFOUI5fzm8Ofk6Njp0OrI67zstO2s7qjvoPCc8ZTykPOM9Ij1hPaE94D4gPmA+oD7gPyE/YT+i +P+JAI0BkQKZA50EpQWpBrEHuQjBCckK1QvdDOkN9Q8BEA0RHRIpEzkUSRVVFmkXeRiJGZ0arRvBHNUd7 +R8BIBUhLSJFI10kdSWNJqUnwSjdKfUrESwxLU0uaS+JMKkxyTLpNAk1KTZNN3E4lTm5Ot08AT0lPk0/d +UCdQcVC7UQZRUFGbUeZSMVJ8UsdTE1NfU6pT9lRCVI9U21UoVXVVwlYPVlxWqVb3V0RXklfgWC9YfVjL +WRpZaVm4WgdaVlqmWvVbRVuVW+VcNVyGXNZdJ114XcleGl5sXr1fD19hX7NgBWBXYKpg/GFPYaJh9WJJ +Ypxi8GNDY5dj62RAZJRk6WU9ZZJl52Y9ZpJm6Gc9Z5Nn6Wg/aJZo7GlDaZpp8WpIap9q92tPa6dr/2xX +bK9tCG1gbbluEm5rbsRvHm94b9FwK3CGcOBxOnGVcfByS3KmcwFzXXO4dBR0cHTMdSh1hXXhdj52m3b4 +d1Z3s3gReG54zHkqeYl553pGeqV7BHtje8J8IXyBfOF9QX2hfgF+Yn7CfyN/hH/lgEeAqIEKgWuBzYIw +gpKC9INXg7qEHYSAhOOFR4Wrhg6GcobXhzuHn4gEiGmIzokziZmJ/opkisqLMIuWi/yMY4zKjTGNmI3/ +jmaOzo82j56QBpBukNaRP5GokhGSepLjk02TtpQglIqU9JVflcmWNJaflwqXdZfgmEyYuJkkmZCZ/Jpo +mtWbQpuvnByciZz3nWSd0p5Anq6fHZ+Ln/qgaaDYoUehtqImopajBqN2o+akVqTHpTilqaYapoum/adu +p+CoUqjEqTepqaocqo+rAqt1q+msXKzQrUStuK4trqGvFq+LsACwdbDqsWCx1rJLssKzOLOutCW0nLUT +tYq2AbZ5tvC3aLfguFm40blKucK6O7q1uy67p7whvJu9Fb2Pvgq+hL7/v3q/9cBwwOzBZ8Hjwl/C28NY +w9TEUcTOxUvFyMZGxsPHQce/yD3IvMk6ybnKOMq3yzbLtsw1zLXNNc21zjbOts83z7jQOdC60TzRvtI/ +0sHTRNPG1EnUy9VO1dHWVdbY11zX4Nhk2OjZbNnx2nba+9uA3AXcit0Q3ZbeHN6i3ynfr+A24L3hROHM +4lPi2+Nj4+vkc+T85YTmDeaW5x/nqegy6LzpRunQ6lvq5etw6/vshu0R7ZzuKO6070DvzPBY8OXxcvH/ +8ozzGfOn9DT0wvVQ9d72bfb794r4Gfio+Tj5x/pX+uf7d/wH/Jj9Kf26/kv+3P9t///SLS4vMFokY2xh +c3NuYW1lWCRjbGFzc2VzXxAQTlNCaXRtYXBJbWFnZVJlcKMvMTJaTlNJbWFnZVJlcFhOU09iamVjdNIt +LjQ1V05TQXJyYXmiNDLSLS43OF5OU011dGFibGVBcnJheaM3NDLTOjsOPD0+V05TV2hpdGVcTlNDb2xv +clNwYWNlRDAgMAAQA4AM0i0uQEFXTlNDb2xvcqJAMtItLkNEV05TSW1hZ2WiQzIACAARABoAJAApADIA +NwBJAEwAUQBTAGIAaAB3AH4AhQCSAJkApQCtALwAvgDAAMUAxwDJAMsAzQDUANkA5ADmAOgA6gDvAPIA +9AD2APgA/wEWATIBNAE2E6YTqxO2E78T0hPWE+ET6hPvE/cT+hP/FA4UEhQZFCEULhQzFDUUNxQ8FEQU +RxRMFFQAAAAAAAACAQAAAAAAAABFAAAAAAAAAAAAAAAAAAAUVw diff --git a/i18n/src/main/resources/ko.lproj/Command.xib b/i18n/src/main/resources/ko.lproj/Command.xib index 06a614f5e8..25dfed5b62 100644 --- a/i18n/src/main/resources/ko.lproj/Command.xib +++ b/i18n/src/main/resources/ko.lproj/Command.xib @@ -1,7 +1,7 @@ - + - + diff --git a/i18n/src/main/resources/ko.lproj/Connection.xib b/i18n/src/main/resources/ko.lproj/Connection.xib index fb6a5e0171..8d06dcda8a 100644 --- a/i18n/src/main/resources/ko.lproj/Connection.xib +++ b/i18n/src/main/resources/ko.lproj/Connection.xib @@ -1,7 +1,7 @@ - + - + @@ -76,7 +76,7 @@ - + diff --git a/i18n/src/main/resources/ko.lproj/Info.xib b/i18n/src/main/resources/ko.lproj/Info.xib index 2a7d417076..c8936f9611 100644 --- a/i18n/src/main/resources/ko.lproj/Info.xib +++ b/i18n/src/main/resources/ko.lproj/Info.xib @@ -1,7 +1,7 @@ - + - + @@ -89,7 +89,7 @@ - + @@ -1017,21 +1017,21 @@ - + - + - + @@ -1121,21 +1121,21 @@ - + - + - + @@ -1250,27 +1250,27 @@ diff --git a/i18n/src/main/resources/ko.lproj/Login.strings b/i18n/src/main/resources/ko.lproj/Login.strings index 3e5e25733f..d0f916aab4 100644 Binary files a/i18n/src/main/resources/ko.lproj/Login.strings and b/i18n/src/main/resources/ko.lproj/Login.strings differ diff --git a/i18n/src/main/resources/ko.lproj/Main.xib b/i18n/src/main/resources/ko.lproj/Main.xib index 647514962c..23f68d78e6 100644 --- a/i18n/src/main/resources/ko.lproj/Main.xib +++ b/i18n/src/main/resources/ko.lproj/Main.xib @@ -1,7 +1,7 @@ - + - + @@ -159,19 +159,19 @@ - + - + - + @@ -321,12 +321,12 @@ CA - + - + @@ -566,6 +566,10 @@ CA - + + + + + diff --git a/i18n/src/main/resources/ko.lproj/Preferences.xib b/i18n/src/main/resources/ko.lproj/Preferences.xib index ea72840e33..877921bb89 100644 --- a/i18n/src/main/resources/ko.lproj/Preferences.xib +++ b/i18n/src/main/resources/ko.lproj/Preferences.xib @@ -1,7 +1,7 @@ - + - + @@ -266,7 +266,7 @@ - + @@ -307,7 +307,7 @@ - + @@ -538,7 +538,7 @@ - + @@ -578,7 +578,7 @@ - + @@ -613,7 +613,7 @@ - + @@ -642,7 +642,7 @@ - + @@ -1218,7 +1218,7 @@ - + @@ -1250,7 +1250,7 @@ - + @@ -1289,7 +1289,7 @@ - + @@ -1362,7 +1362,7 @@ - + @@ -1557,7 +1557,7 @@ - + @@ -1617,7 +1617,7 @@ - + @@ -1642,7 +1642,7 @@ - + @@ -1684,7 +1684,7 @@ - + @@ -1712,7 +1712,7 @@ - + @@ -1740,7 +1740,7 @@ - + @@ -1769,7 +1769,7 @@ - + @@ -1804,7 +1804,7 @@ - + @@ -1887,7 +1887,7 @@ - + @@ -1946,7 +1946,7 @@ - + @@ -1974,7 +1974,7 @@ - + @@ -2002,7 +2002,7 @@ - + diff --git a/i18n/src/main/resources/ko.lproj/Profiles.xib b/i18n/src/main/resources/ko.lproj/Profiles.xib index d4a1baecb6..c9beaa5b94 100644 --- a/i18n/src/main/resources/ko.lproj/Profiles.xib +++ b/i18n/src/main/resources/ko.lproj/Profiles.xib @@ -1,7 +1,7 @@ - + - + diff --git a/i18n/src/main/resources/ko.lproj/Prompt.xib b/i18n/src/main/resources/ko.lproj/Prompt.xib index a67b232148..697a1fe5b1 100644 --- a/i18n/src/main/resources/ko.lproj/Prompt.xib +++ b/i18n/src/main/resources/ko.lproj/Prompt.xib @@ -1,7 +1,7 @@ - + - + @@ -95,7 +95,7 @@ Gw - + diff --git a/i18n/src/main/resources/ko.lproj/S3.strings b/i18n/src/main/resources/ko.lproj/S3.strings index 9fed752a43..2d5c5fcd83 100644 Binary files a/i18n/src/main/resources/ko.lproj/S3.strings and b/i18n/src/main/resources/ko.lproj/S3.strings differ diff --git a/i18n/src/main/resources/ko.lproj/Updater.strings b/i18n/src/main/resources/ko.lproj/Updater.strings index 6f65e7f143..a7e6bb1817 100644 Binary files a/i18n/src/main/resources/ko.lproj/Updater.strings and b/i18n/src/main/resources/ko.lproj/Updater.strings differ diff --git a/i18n/src/main/resources/lv.lproj/Bookmark.xib b/i18n/src/main/resources/lv.lproj/Bookmark.xib index d9850e8b7b..a4ee262eeb 100644 --- a/i18n/src/main/resources/lv.lproj/Bookmark.xib +++ b/i18n/src/main/resources/lv.lproj/Bookmark.xib @@ -1,7 +1,7 @@ - + - + @@ -92,7 +92,7 @@ - + @@ -489,95 +489,95 @@ YnBsaXN0MDDUAQIDBAUGBwpYJHZlcnNpb25ZJGFyY2hpdmVyVCR0b3BYJG9iamVjdHMSAAGGoF8QD05T -S2V5ZWRBcmNoaXZlctEICVRyb290gAGuCwwZGh8UJCkqMTQ3PUBVJG51bGzWDQ4PEBESExQVFhcYVk5T -U2l6ZV5OU1Jlc2l6aW5nTW9kZVYkY2xhc3NcTlNJbWFnZUZsYWdzVk5TUmVwc1dOU0NvbG9ygAIQAIAN -EiDDAACAA4ALVnsxLCAxfdIbDxweWk5TLm9iamVjdHOhHYAEgArSGw8gI6IhIoAFgAaACdMPJSYnKBRf -EBROU1RJRkZSZXByZXNlbnRhdGlvbl8QGU5TSW50ZXJuYWxMYXlvdXREaXJlY3Rpb26ACIAHTxESbE1N -ACoAAAAKAAAAEAEAAAMAAAABAAEAAAEBAAMAAAABAAEAAAECAAMAAAACAAgACAEDAAMAAAABAAEAAAEG -AAMAAAABAAEAAAEKAAMAAAABAAEAAAERAAQAAAABAAAACAESAAMAAAABAAEAAAEVAAMAAAABAAIAAAEW -AAMAAAABAAEAAAEXAAQAAAABAAAAAgEcAAMAAAABAAEAAAEoAAMAAAABAAIAAAFSAAMAAAABAAEAAAFT -AAMAAAACAAEAAYdzAAcAABGcAAAA0AAAAAAAABGcYXBwbAIAAABtbnRyR1JBWVhZWiAH3AAIABcADwAu -AA9hY3NwQVBQTAAAAABub25lAAAAAAAAAAAAAAAAAAAAAAAA9tYAAQAAAADTLWFwcGwAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAVkZXNjAAAAwAAAAHlkc2NtAAABPAAA -CBpjcHJ0AAAJWAAAACN3dHB0AAAJfAAAABRrVFJDAAAJkAAACAxkZXNjAAAAAAAAAB9HZW5lcmljIEdy -YXkgR2FtbWEgMi4yIFByb2ZpbGUAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAbWx1YwAAAAAAAAAfAAAADHNr -U0sAAAAuAAABhGRhREsAAAA6AAABsmNhRVMAAAA4AAAB7HZpVk4AAABAAAACJHB0QlIAAABKAAACZHVr -VUEAAAAsAAACrmZyRlUAAAA+AAAC2mh1SFUAAAA0AAADGHpoVFcAAAAaAAADTGtvS1IAAAAiAAADZm5i -Tk8AAAA6AAADiGNzQ1oAAAAoAAADwmhlSUwAAAAkAAAD6nJvUk8AAAAqAAAEDmRlREUAAABOAAAEOGl0 -SVQAAABOAAAEhnN2U0UAAAA4AAAE1HpoQ04AAAAaAAAFDGphSlAAAAAmAAAFJmVsR1IAAAAqAAAFTHB0 -UE8AAABSAAAFdm5sTkwAAABAAAAFyGVzRVMAAABMAAAGCHRoVEgAAAAyAAAGVHRyVFIAAAAkAAAGhmZp -RkkAAABGAAAGqmhySFIAAAA+AAAG8HBsUEwAAABKAAAHLmFyRUcAAAAsAAAHeHJ1UlUAAAA6AAAHpGVu -VVMAAAA8AAAH3gBWAWEAZQBvAGIAZQBjAG4A4QAgAHMAaQB2AOEAIABnAGEAbQBhACAAMgAsADIARwBl -AG4AZQByAGkAcwBrACAAZwByAOUAIAAyACwAMgAgAGcAYQBtAG0AYQAtAHAAcgBvAGYAaQBsAEcAYQBt -AG0AYQAgAGQAZQAgAGcAcgBpAHMAbwBzACAAZwBlAG4A6AByAGkAYwBhACAAMgAuADIAQx6lAHUAIABo -AOwAbgBoACAATQDgAHUAIAB4AOEAbQAgAEMAaAB1AG4AZwAgAEcAYQBtAG0AYQAgADIALgAyAFAAZQBy -AGYAaQBsACAARwBlAG4A6QByAGkAYwBvACAAZABhACAARwBhAG0AYQAgAGQAZQAgAEMAaQBuAHoAYQBz -ACAAMgAsADIEFwQwBDMEMAQ7BEwEPQQwACAARwByAGEAeQAtBDMEMAQ8BDAAIAAyAC4AMgBQAHIAbwBm -AGkAbAAgAGcA6QBuAOkAcgBpAHEAdQBlACAAZwByAGkAcwAgAGcAYQBtAG0AYQAgADIALAAyAMEAbAB0 -AGEAbADhAG4AbwBzACAAcwB6APwAcgBrAGUAIABnAGEAbQBtAGEAIAAyAC4AMpAadShwcJaOUUlepgAy -AC4AMoJyX2ljz4/wx3y8GAAg1ozAyQAgrBC5yAAgADIALgAyACDVBLhc0wzHfABHAGUAbgBlAHIAaQBz -AGsAIABnAHIA5QAgAGcAYQBtAG0AYQAgADIALAAyAC0AcAByAG8AZgBpAGwATwBiAGUAYwBuAOEAIAFh -AGUAZADhACAAZwBhAG0AYQAgADIALgAyBdIF0AXeBdQAIAXQBeQF1QXoACAF2wXcBdwF2QAgADIALgAy -AEcAYQBtAGEAIABnAHIAaQAgAGcAZQBuAGUAcgBpAGMBAwAgADIALAAyAEEAbABsAGcAZQBtAGUAaQBu -AGUAcwAgAEcAcgBhAHUAcwB0AHUAZgBlAG4ALQBQAHIAbwBmAGkAbAAgAEcAYQBtAG0AYQAgADIALAAy -AFAAcgBvAGYAaQBsAG8AIABnAHIAaQBnAGkAbwAgAGcAZQBuAGUAcgBpAGMAbwAgAGQAZQBsAGwAYQAg -AGcAYQBtAG0AYQAgADIALAAyAEcAZQBuAGUAcgBpAHMAawAgAGcAcgDlACAAMgAsADIAIABnAGEAbQBt -AGEAcAByAG8AZgBpAGxmbpAacHBepnz7ZXAAMgAuADJjz4/wZYdO9k4AgiwwsDDsMKQwrDDzMN4AIAAy -AC4AMgAgMNcw7TDVMKEwpDDrA5MDtQO9A7kDugPMACADkwO6A8EDuQAgA5MDrAO8A7wDsQAgADIALgAy -AFAAZQByAGYAaQBsACAAZwBlAG4A6QByAGkAYwBvACAAZABlACAAYwBpAG4AegBlAG4AdABvAHMAIABk -AGEAIABHAGEAbQBtAGEAIAAyACwAMgBBAGwAZwBlAG0AZQBlAG4AIABnAHIAaQBqAHMAIABnAGEAbQBt -AGEAIAAyACwAMgAtAHAAcgBvAGYAaQBlAGwAUABlAHIAZgBpAGwAIABnAGUAbgDpAHIAaQBjAG8AIABk -AGUAIABnAGEAbQBtAGEAIABkAGUAIABnAHIAaQBzAGUAcwAgADIALAAyDiMOMQ4HDioONQ5BDgEOIQ4h -DjIOQA4BDiMOIg5MDhcOMQ5IDicORA4bACAAMgAuADIARwBlAG4AZQBsACAARwByAGkAIABHAGEAbQBh -ACAAMgAsADIAWQBsAGUAaQBuAGUAbgAgAGgAYQByAG0AYQBhAG4AIABnAGEAbQBtAGEAIAAyACwAMgAg -AC0AcAByAG8AZgBpAGkAbABpAEcAZQBuAGUAcgBpAQ0AawBpACAARwByAGEAeQAgAEcAYQBtAG0AYQAg -ADIALgAyACAAcAByAG8AZgBpAGwAVQBuAGkAdwBlAHIAcwBhAGwAbgB5ACAAcAByAG8AZgBpAGwAIABz -AHoAYQByAG8BWwBjAGkAIABnAGEAbQBtAGEAIAAyACwAMgY6BicGRQYnACAAMgAuADIAIAZEBkgGRgAg -BjEGRQYnBi8GSgAgBjkGJwZFBB4EMQRJBDAETwAgBEEENQRABDAETwAgBDMEMAQ8BDwEMAAgADIALAAy -AC0EPwRABD4ERAQ4BDsETABHAGUAbgBlAHIAaQBjACAARwByAGEAeQAgAEcAYQBtAG0AYQAgADIALgAy -ACAAUAByAG8AZgBpAGwAZQAAdGV4dAAAAABDb3B5cmlnaHQgQXBwbGUgSW5jLiwgMjAxMgAAWFlaIAAA -AAAAAPNRAAEAAAABFsxjdXJ2AAAAAAAABAAAAAAFAAoADwAUABkAHgAjACgALQAyADcAOwBAAEUASgBP -AFQAWQBeAGMAaABtAHIAdwB8AIEAhgCLAJAAlQCaAJ8ApACpAK4AsgC3ALwAwQDGAMsA0ADVANsA4ADl -AOsA8AD2APsBAQEHAQ0BEwEZAR8BJQErATIBOAE+AUUBTAFSAVkBYAFnAW4BdQF8AYMBiwGSAZoBoQGp -AbEBuQHBAckB0QHZAeEB6QHyAfoCAwIMAhQCHQImAi8COAJBAksCVAJdAmcCcQJ6AoQCjgKYAqICrAK2 -AsECywLVAuAC6wL1AwADCwMWAyEDLQM4A0MDTwNaA2YDcgN+A4oDlgOiA64DugPHA9MD4APsA/kEBgQT -BCAELQQ7BEgEVQRjBHEEfgSMBJoEqAS2BMQE0wThBPAE/gUNBRwFKwU6BUkFWAVnBXcFhgWWBaYFtQXF -BdUF5QX2BgYGFgYnBjcGSAZZBmoGewaMBp0GrwbABtEG4wb1BwcHGQcrBz0HTwdhB3QHhgeZB6wHvwfS -B+UH+AgLCB8IMghGCFoIbgiCCJYIqgi+CNII5wj7CRAJJQk6CU8JZAl5CY8JpAm6Cc8J5Qn7ChEKJwo9 -ClQKagqBCpgKrgrFCtwK8wsLCyILOQtRC2kLgAuYC7ALyAvhC/kMEgwqDEMMXAx1DI4MpwzADNkM8w0N -DSYNQA1aDXQNjg2pDcMN3g34DhMOLg5JDmQOfw6bDrYO0g7uDwkPJQ9BD14Peg+WD7MPzw/sEAkQJhBD -EGEQfhCbELkQ1xD1ERMRMRFPEW0RjBGqEckR6BIHEiYSRRJkEoQSoxLDEuMTAxMjE0MTYxODE6QTxRPl -FAYUJxRJFGoUixStFM4U8BUSFTQVVhV4FZsVvRXgFgMWJhZJFmwWjxayFtYW+hcdF0EXZReJF64X0hf3 -GBsYQBhlGIoYrxjVGPoZIBlFGWsZkRm3Gd0aBBoqGlEadxqeGsUa7BsUGzsbYxuKG7Ib2hwCHCocUhx7 -HKMczBz1HR4dRx1wHZkdwx3sHhYeQB5qHpQevh7pHxMfPh9pH5Qfvx/qIBUgQSBsIJggxCDwIRwhSCF1 -IaEhziH7IiciVSKCIq8i3SMKIzgjZiOUI8Ij8CQfJE0kfCSrJNolCSU4JWgllyXHJfcmJyZXJocmtybo -JxgnSSd6J6sn3CgNKD8ocSiiKNQpBik4KWspnSnQKgIqNSpoKpsqzysCKzYraSudK9EsBSw5LG4soizX -LQwtQS12Last4S4WLkwugi63Lu4vJC9aL5Evxy/+MDUwbDCkMNsxEjFKMYIxujHyMioyYzKbMtQzDTNG -M38zuDPxNCs0ZTSeNNg1EzVNNYc1wjX9Njc2cjauNuk3JDdgN5w31zgUOFA4jDjIOQU5Qjl/Obw5+To2 -OnQ6sjrvOy07azuqO+g8JzxlPKQ84z0iPWE9oT3gPiA+YD6gPuA/IT9hP6I/4kAjQGRApkDnQSlBakGs -Qe5CMEJyQrVC90M6Q31DwEQDREdEikTORRJFVUWaRd5GIkZnRqtG8Ec1R3tHwEgFSEtIkUjXSR1JY0mp -SfBKN0p9SsRLDEtTS5pL4kwqTHJMuk0CTUpNk03cTiVObk63TwBPSU+TT91QJ1BxULtRBlFQUZtR5lIx -UnxSx1MTU19TqlP2VEJUj1TbVShVdVXCVg9WXFapVvdXRFeSV+BYL1h9WMtZGllpWbhaB1pWWqZa9VtF -W5Vb5Vw1XIZc1l0nXXhdyV4aXmxevV8PX2Ffs2AFYFdgqmD8YU9homH1YklinGLwY0Njl2PrZEBklGTp -ZT1lkmXnZj1mkmboZz1nk2fpaD9olmjsaUNpmmnxakhqn2r3a09rp2v/bFdsr20IbWBtuW4SbmtuxG8e -b3hv0XArcIZw4HE6cZVx8HJLcqZzAXNdc7h0FHRwdMx1KHWFdeF2Pnabdvh3VnezeBF4bnjMeSp5iXnn -ekZ6pXsEe2N7wnwhfIF84X1BfaF+AX5ifsJ/I3+Ef+WAR4CogQqBa4HNgjCCkoL0g1eDuoQdhICE44VH -hauGDoZyhteHO4efiASIaYjOiTOJmYn+imSKyoswi5aL/IxjjMqNMY2Yjf+OZo7OjzaPnpAGkG6Q1pE/ -kaiSEZJ6kuOTTZO2lCCUipT0lV+VyZY0lp+XCpd1l+CYTJi4mSSZkJn8mmia1ZtCm6+cHJyJnPedZJ3S -nkCerp8dn4uf+qBpoNihR6G2oiailqMGo3aj5qRWpMelOKWpphqmi6b9p26n4KhSqMSpN6mpqhyqj6sC -q3Wr6axcrNCtRK24ri2uoa8Wr4uwALB1sOqxYLHWskuywrM4s660JbSctRO1irYBtnm28Ldot+C4WbjR -uUq5wro7urW7LrunvCG8m70VvY++Cr6Evv+/er/1wHDA7MFnwePCX8Lbw1jD1MRRxM7FS8XIxkbGw8dB -x7/IPci8yTrJuco4yrfLNsu2zDXMtc01zbXONs62zzfPuNA50LrRPNG+0j/SwdNE08bUSdTL1U7V0dZV -1tjXXNfg2GTY6Nls2fHadtr724DcBdyK3RDdlt4c3qLfKd+v4DbgveFE4cziU+Lb42Pj6+Rz5PzlhOYN -5pbnH+ep6DLovOlG6dDqW+rl63Dr++yG7RHtnO4o7rTvQO/M8Fjw5fFy8f/yjPMZ86f0NPTC9VD13vZt -9vv3ivgZ+Kj5OPnH+lf65/t3/Af8mP0p/br+S/7c/23//9IrLC0uWiRjbGFzc25hbWVYJGNsYXNzZXNf -EBBOU0JpdG1hcEltYWdlUmVwoy0vMFpOU0ltYWdlUmVwWE5TT2JqZWN00issMjNXTlNBcnJheaIyMNIr -LDU2Xk5TTXV0YWJsZUFycmF5ozUyMNM4OQ86OzxXTlNXaGl0ZVxOU0NvbG9yU3BhY2VEMCAwABADgAzS -Kyw+P1dOU0NvbG9yoj4w0issQUJXTlNJbWFnZaJBMAAIABEAGgAkACkAMgA3AEkATABRAFMAYgBoAHUA -fACLAJIAnwCmAK4AsACyALQAuQC7AL0AxADJANQA1gDYANoA3wDiAOQA5gDoAO8BBgEiASQBJhOWE5sT -phOvE8ITxhPRE9oT3xPnE+oT7xP+FAIUCRQRFB4UIxQlFCcULBQ0FDcUPBREAAAAAAAAAgEAAAAAAAAA -QwAAAAAAAAAAAAAAAAAAFEc +S2V5ZWRBcmNoaXZlctEICVRyb290gAGuCwwbHCEaJissMzY5P0JVJG51bGzXDQ4PEBESExQVFhcYGRpW +TlNTaXplViRjbGFzc1xOU0ltYWdlRmxhZ3NWTlNSZXBzW05TVGludENvbG9yV05TQ29sb3JeTlNSZXNp +emluZ01vZGWAAoANEiDDAACAA4AAgAsQAFZ7MSwgMX3SHQ4eIFpOUy5vYmplY3RzoR+ABIAK0h0OIiWi +IySABYAGgAnTDicoKSoaXxAUTlNUSUZGUmVwcmVzZW50YXRpb25fEBlOU0ludGVybmFsTGF5b3V0RGly +ZWN0aW9ugAiAB08REmxNTQAqAAAACgAAABABAAADAAAAAQABAAABAQADAAAAAQABAAABAgADAAAAAgAI +AAgBAwADAAAAAQABAAABBgADAAAAAQABAAABCgADAAAAAQABAAABEQAEAAAAAQAAAAgBEgADAAAAAQAB +AAABFQADAAAAAQACAAABFgADAAAAAQABAAABFwAEAAAAAQAAAAIBHAADAAAAAQABAAABKAADAAAAAQAC +AAABUgADAAAAAQABAAABUwADAAAAAgABAAGHcwAHAAARnAAAANAAAAAAAAARnGFwcGwCAAAAbW50ckdS +QVlYWVogB9wACAAXAA8ALgAPYWNzcEFQUEwAAAAAbm9uZQAAAAAAAAAAAAAAAAAAAAAAAPbWAAEAAAAA +0y1hcHBsAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFZGVzYwAA +AMAAAAB5ZHNjbQAAATwAAAgaY3BydAAACVgAAAAjd3RwdAAACXwAAAAUa1RSQwAACZAAAAgMZGVzYwAA +AAAAAAAfR2VuZXJpYyBHcmF5IEdhbW1hIDIuMiBQcm9maWxlAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAG1s +dWMAAAAAAAAAHwAAAAxza1NLAAAALgAAAYRkYURLAAAAOgAAAbJjYUVTAAAAOAAAAex2aVZOAAAAQAAA +AiRwdEJSAAAASgAAAmR1a1VBAAAALAAAAq5mckZVAAAAPgAAAtpodUhVAAAANAAAAxh6aFRXAAAAGgAA +A0xrb0tSAAAAIgAAA2ZuYk5PAAAAOgAAA4hjc0NaAAAAKAAAA8JoZUlMAAAAJAAAA+pyb1JPAAAAKgAA +BA5kZURFAAAATgAABDhpdElUAAAATgAABIZzdlNFAAAAOAAABNR6aENOAAAAGgAABQxqYUpQAAAAJgAA +BSZlbEdSAAAAKgAABUxwdFBPAAAAUgAABXZubE5MAAAAQAAABchlc0VTAAAATAAABgh0aFRIAAAAMgAA +BlR0clRSAAAAJAAABoZmaUZJAAAARgAABqpockhSAAAAPgAABvBwbFBMAAAASgAABy5hckVHAAAALAAA +B3hydVJVAAAAOgAAB6RlblVTAAAAPAAAB94AVgFhAGUAbwBiAGUAYwBuAOEAIABzAGkAdgDhACAAZwBh +AG0AYQAgADIALAAyAEcAZQBuAGUAcgBpAHMAawAgAGcAcgDlACAAMgAsADIAIABnAGEAbQBtAGEALQBw +AHIAbwBmAGkAbABHAGEAbQBtAGEAIABkAGUAIABnAHIAaQBzAG8AcwAgAGcAZQBuAOgAcgBpAGMAYQAg +ADIALgAyAEMepQB1ACAAaADsAG4AaAAgAE0A4AB1ACAAeADhAG0AIABDAGgAdQBuAGcAIABHAGEAbQBt +AGEAIAAyAC4AMgBQAGUAcgBmAGkAbAAgAEcAZQBuAOkAcgBpAGMAbwAgAGQAYQAgAEcAYQBtAGEAIABk +AGUAIABDAGkAbgB6AGEAcwAgADIALAAyBBcEMAQzBDAEOwRMBD0EMAAgAEcAcgBhAHkALQQzBDAEPAQw +ACAAMgAuADIAUAByAG8AZgBpAGwAIABnAOkAbgDpAHIAaQBxAHUAZQAgAGcAcgBpAHMAIABnAGEAbQBt +AGEAIAAyACwAMgDBAGwAdABhAGwA4QBuAG8AcwAgAHMAegD8AHIAawBlACAAZwBhAG0AbQBhACAAMgAu +ADKQGnUocHCWjlFJXqYAMgAuADKCcl9pY8+P8Md8vBgAINaMwMkAIKwQucgAIAAyAC4AMgAg1QS4XNMM +x3wARwBlAG4AZQByAGkAcwBrACAAZwByAOUAIABnAGEAbQBtAGEAIAAyACwAMgAtAHAAcgBvAGYAaQBs +AE8AYgBlAGMAbgDhACABYQBlAGQA4QAgAGcAYQBtAGEAIAAyAC4AMgXSBdAF3gXUACAF0AXkBdUF6AAg +BdsF3AXcBdkAIAAyAC4AMgBHAGEAbQBhACAAZwByAGkAIABnAGUAbgBlAHIAaQBjAQMAIAAyACwAMgBB +AGwAbABnAGUAbQBlAGkAbgBlAHMAIABHAHIAYQB1AHMAdAB1AGYAZQBuAC0AUAByAG8AZgBpAGwAIABH +AGEAbQBtAGEAIAAyACwAMgBQAHIAbwBmAGkAbABvACAAZwByAGkAZwBpAG8AIABnAGUAbgBlAHIAaQBj +AG8AIABkAGUAbABsAGEAIABnAGEAbQBtAGEAIAAyACwAMgBHAGUAbgBlAHIAaQBzAGsAIABnAHIA5QAg +ADIALAAyACAAZwBhAG0AbQBhAHAAcgBvAGYAaQBsZm6QGnBwXqZ8+2VwADIALgAyY8+P8GWHTvZOAIIs +MLAw7DCkMKww8zDeACAAMgAuADIAIDDXMO0w1TChMKQw6wOTA7UDvQO5A7oDzAAgA5MDugPBA7kAIAOT +A6wDvAO8A7EAIAAyAC4AMgBQAGUAcgBmAGkAbAAgAGcAZQBuAOkAcgBpAGMAbwAgAGQAZQAgAGMAaQBu +AHoAZQBuAHQAbwBzACAAZABhACAARwBhAG0AbQBhACAAMgAsADIAQQBsAGcAZQBtAGUAZQBuACAAZwBy +AGkAagBzACAAZwBhAG0AbQBhACAAMgAsADIALQBwAHIAbwBmAGkAZQBsAFAAZQByAGYAaQBsACAAZwBl +AG4A6QByAGkAYwBvACAAZABlACAAZwBhAG0AbQBhACAAZABlACAAZwByAGkAcwBlAHMAIAAyACwAMg4j +DjEOBw4qDjUOQQ4BDiEOIQ4yDkAOAQ4jDiIOTA4XDjEOSA4nDkQOGwAgADIALgAyAEcAZQBuAGUAbAAg +AEcAcgBpACAARwBhAG0AYQAgADIALAAyAFkAbABlAGkAbgBlAG4AIABoAGEAcgBtAGEAYQBuACAAZwBh +AG0AbQBhACAAMgAsADIAIAAtAHAAcgBvAGYAaQBpAGwAaQBHAGUAbgBlAHIAaQENAGsAaQAgAEcAcgBh +AHkAIABHAGEAbQBtAGEAIAAyAC4AMgAgAHAAcgBvAGYAaQBsAFUAbgBpAHcAZQByAHMAYQBsAG4AeQAg +AHAAcgBvAGYAaQBsACAAcwB6AGEAcgBvAVsAYwBpACAAZwBhAG0AbQBhACAAMgAsADIGOgYnBkUGJwAg +ADIALgAyACAGRAZIBkYAIAYxBkUGJwYvBkoAIAY5BicGRQQeBDEESQQwBE8AIARBBDUEQAQwBE8AIAQz +BDAEPAQ8BDAAIAAyACwAMgAtBD8EQAQ+BEQEOAQ7BEwARwBlAG4AZQByAGkAYwAgAEcAcgBhAHkAIABH +AGEAbQBtAGEAIAAyAC4AMgAgAFAAcgBvAGYAaQBsAGUAAHRleHQAAAAAQ29weXJpZ2h0IEFwcGxlIElu +Yy4sIDIwMTIAAFhZWiAAAAAAAADzUQABAAAAARbMY3VydgAAAAAAAAQAAAAABQAKAA8AFAAZAB4AIwAo +AC0AMgA3ADsAQABFAEoATwBUAFkAXgBjAGgAbQByAHcAfACBAIYAiwCQAJUAmgCfAKQAqQCuALIAtwC8 +AMEAxgDLANAA1QDbAOAA5QDrAPAA9gD7AQEBBwENARMBGQEfASUBKwEyATgBPgFFAUwBUgFZAWABZwFu +AXUBfAGDAYsBkgGaAaEBqQGxAbkBwQHJAdEB2QHhAekB8gH6AgMCDAIUAh0CJgIvAjgCQQJLAlQCXQJn +AnECegKEAo4CmAKiAqwCtgLBAssC1QLgAusC9QMAAwsDFgMhAy0DOANDA08DWgNmA3IDfgOKA5YDogOu +A7oDxwPTA+AD7AP5BAYEEwQgBC0EOwRIBFUEYwRxBH4EjASaBKgEtgTEBNME4QTwBP4FDQUcBSsFOgVJ +BVgFZwV3BYYFlgWmBbUFxQXVBeUF9gYGBhYGJwY3BkgGWQZqBnsGjAadBq8GwAbRBuMG9QcHBxkHKwc9 +B08HYQd0B4YHmQesB78H0gflB/gICwgfCDIIRghaCG4IggiWCKoIvgjSCOcI+wkQCSUJOglPCWQJeQmP +CaQJugnPCeUJ+woRCicKPQpUCmoKgQqYCq4KxQrcCvMLCwsiCzkLUQtpC4ALmAuwC8gL4Qv5DBIMKgxD +DFwMdQyODKcMwAzZDPMNDQ0mDUANWg10DY4NqQ3DDd4N+A4TDi4OSQ5kDn8Omw62DtIO7g8JDyUPQQ9e +D3oPlg+zD88P7BAJECYQQxBhEH4QmxC5ENcQ9RETETERTxFtEYwRqhHJEegSBxImEkUSZBKEEqMSwxLj +EwMTIxNDE2MTgxOkE8UT5RQGFCcUSRRqFIsUrRTOFPAVEhU0FVYVeBWbFb0V4BYDFiYWSRZsFo8WshbW +FvoXHRdBF2UXiReuF9IX9xgbGEAYZRiKGK8Y1Rj6GSAZRRlrGZEZtxndGgQaKhpRGncanhrFGuwbFBs7 +G2MbihuyG9ocAhwqHFIcexyjHMwc9R0eHUcdcB2ZHcMd7B4WHkAeah6UHr4e6R8THz4faR+UH78f6iAV +IEEgbCCYIMQg8CEcIUghdSGhIc4h+yInIlUigiKvIt0jCiM4I2YjlCPCI/AkHyRNJHwkqyTaJQklOCVo +JZclxyX3JicmVyaHJrcm6CcYJ0kneierJ9woDSg/KHEooijUKQYpOClrKZ0p0CoCKjUqaCqbKs8rAis2 +K2krnSvRLAUsOSxuLKIs1y0MLUEtdi2rLeEuFi5MLoIuty7uLyQvWi+RL8cv/jA1MGwwpDDbMRIxSjGC +Mbox8jIqMmMymzLUMw0zRjN/M7gz8TQrNGU0njTYNRM1TTWHNcI1/TY3NnI2rjbpNyQ3YDecN9c4FDhQ +OIw4yDkFOUI5fzm8Ofk6Njp0OrI67zstO2s7qjvoPCc8ZTykPOM9Ij1hPaE94D4gPmA+oD7gPyE/YT+i +P+JAI0BkQKZA50EpQWpBrEHuQjBCckK1QvdDOkN9Q8BEA0RHRIpEzkUSRVVFmkXeRiJGZ0arRvBHNUd7 +R8BIBUhLSJFI10kdSWNJqUnwSjdKfUrESwxLU0uaS+JMKkxyTLpNAk1KTZNN3E4lTm5Ot08AT0lPk0/d +UCdQcVC7UQZRUFGbUeZSMVJ8UsdTE1NfU6pT9lRCVI9U21UoVXVVwlYPVlxWqVb3V0RXklfgWC9YfVjL +WRpZaVm4WgdaVlqmWvVbRVuVW+VcNVyGXNZdJ114XcleGl5sXr1fD19hX7NgBWBXYKpg/GFPYaJh9WJJ +Ypxi8GNDY5dj62RAZJRk6WU9ZZJl52Y9ZpJm6Gc9Z5Nn6Wg/aJZo7GlDaZpp8WpIap9q92tPa6dr/2xX +bK9tCG1gbbluEm5rbsRvHm94b9FwK3CGcOBxOnGVcfByS3KmcwFzXXO4dBR0cHTMdSh1hXXhdj52m3b4 +d1Z3s3gReG54zHkqeYl553pGeqV7BHtje8J8IXyBfOF9QX2hfgF+Yn7CfyN/hH/lgEeAqIEKgWuBzYIw +gpKC9INXg7qEHYSAhOOFR4Wrhg6GcobXhzuHn4gEiGmIzokziZmJ/opkisqLMIuWi/yMY4zKjTGNmI3/ +jmaOzo82j56QBpBukNaRP5GokhGSepLjk02TtpQglIqU9JVflcmWNJaflwqXdZfgmEyYuJkkmZCZ/Jpo +mtWbQpuvnByciZz3nWSd0p5Anq6fHZ+Ln/qgaaDYoUehtqImopajBqN2o+akVqTHpTilqaYapoum/adu +p+CoUqjEqTepqaocqo+rAqt1q+msXKzQrUStuK4trqGvFq+LsACwdbDqsWCx1rJLssKzOLOutCW0nLUT +tYq2AbZ5tvC3aLfguFm40blKucK6O7q1uy67p7whvJu9Fb2Pvgq+hL7/v3q/9cBwwOzBZ8Hjwl/C28NY +w9TEUcTOxUvFyMZGxsPHQce/yD3IvMk6ybnKOMq3yzbLtsw1zLXNNc21zjbOts83z7jQOdC60TzRvtI/ +0sHTRNPG1EnUy9VO1dHWVdbY11zX4Nhk2OjZbNnx2nba+9uA3AXcit0Q3ZbeHN6i3ynfr+A24L3hROHM +4lPi2+Nj4+vkc+T85YTmDeaW5x/nqegy6LzpRunQ6lvq5etw6/vshu0R7ZzuKO6070DvzPBY8OXxcvH/ +8ozzGfOn9DT0wvVQ9d72bfb794r4Gfio+Tj5x/pX+uf7d/wH/Jj9Kf26/kv+3P9t///SLS4vMFokY2xh +c3NuYW1lWCRjbGFzc2VzXxAQTlNCaXRtYXBJbWFnZVJlcKMvMTJaTlNJbWFnZVJlcFhOU09iamVjdNIt +LjQ1V05TQXJyYXmiNDLSLS43OF5OU011dGFibGVBcnJheaM3NDLTOjsOPD0+V05TV2hpdGVcTlNDb2xv +clNwYWNlRDAgMAAQA4AM0i0uQEFXTlNDb2xvcqJAMtItLkNEV05TSW1hZ2WiQzIACAARABoAJAApADIA +NwBJAEwAUQBTAGIAaAB3AH4AhQCSAJkApQCtALwAvgDAAMUAxwDJAMsAzQDUANkA5ADmAOgA6gDvAPIA +9AD2APgA/wEWATIBNAE2E6YTqxO2E78T0hPWE+ET6hPvE/cT+hP/FA4UEhQZFCEULhQzFDUUNxQ8FEQU +RxRMFFQAAAAAAAACAQAAAAAAAABFAAAAAAAAAAAAAAAAAAAUVw diff --git a/i18n/src/main/resources/lv.lproj/Browser.xib b/i18n/src/main/resources/lv.lproj/Browser.xib index af722d6ecd..e646c2db88 100644 --- a/i18n/src/main/resources/lv.lproj/Browser.xib +++ b/i18n/src/main/resources/lv.lproj/Browser.xib @@ -1,7 +1,7 @@ - + - + @@ -38,7 +38,7 @@ - + @@ -62,27 +62,27 @@ @@ -256,7 +256,7 @@ - + @@ -553,95 +553,95 @@ DQ YnBsaXN0MDDUAQIDBAUGBwpYJHZlcnNpb25ZJGFyY2hpdmVyVCR0b3BYJG9iamVjdHMSAAGGoF8QD05T -S2V5ZWRBcmNoaXZlctEICVRyb290gAGuCwwZGh8UJCkqMTQ3PUBVJG51bGzWDQ4PEBESExQVFhcYVk5T -U2l6ZV5OU1Jlc2l6aW5nTW9kZVYkY2xhc3NcTlNJbWFnZUZsYWdzVk5TUmVwc1dOU0NvbG9ygAIQAIAN -EiDDAACAA4ALVnsxLCAxfdIbDxweWk5TLm9iamVjdHOhHYAEgArSGw8gI6IhIoAFgAaACdMPJSYnKBRf -EBROU1RJRkZSZXByZXNlbnRhdGlvbl8QGU5TSW50ZXJuYWxMYXlvdXREaXJlY3Rpb26ACIAHTxESbE1N -ACoAAAAKAAAAEAEAAAMAAAABAAEAAAEBAAMAAAABAAEAAAECAAMAAAACAAgACAEDAAMAAAABAAEAAAEG -AAMAAAABAAEAAAEKAAMAAAABAAEAAAERAAQAAAABAAAACAESAAMAAAABAAEAAAEVAAMAAAABAAIAAAEW -AAMAAAABAAEAAAEXAAQAAAABAAAAAgEcAAMAAAABAAEAAAEoAAMAAAABAAIAAAFSAAMAAAABAAEAAAFT -AAMAAAACAAEAAYdzAAcAABGcAAAA0AAAAAAAABGcYXBwbAIAAABtbnRyR1JBWVhZWiAH3AAIABcADwAu -AA9hY3NwQVBQTAAAAABub25lAAAAAAAAAAAAAAAAAAAAAAAA9tYAAQAAAADTLWFwcGwAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAVkZXNjAAAAwAAAAHlkc2NtAAABPAAA -CBpjcHJ0AAAJWAAAACN3dHB0AAAJfAAAABRrVFJDAAAJkAAACAxkZXNjAAAAAAAAAB9HZW5lcmljIEdy -YXkgR2FtbWEgMi4yIFByb2ZpbGUAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAbWx1YwAAAAAAAAAfAAAADHNr -U0sAAAAuAAABhGRhREsAAAA6AAABsmNhRVMAAAA4AAAB7HZpVk4AAABAAAACJHB0QlIAAABKAAACZHVr -VUEAAAAsAAACrmZyRlUAAAA+AAAC2mh1SFUAAAA0AAADGHpoVFcAAAAaAAADTGtvS1IAAAAiAAADZm5i -Tk8AAAA6AAADiGNzQ1oAAAAoAAADwmhlSUwAAAAkAAAD6nJvUk8AAAAqAAAEDmRlREUAAABOAAAEOGl0 -SVQAAABOAAAEhnN2U0UAAAA4AAAE1HpoQ04AAAAaAAAFDGphSlAAAAAmAAAFJmVsR1IAAAAqAAAFTHB0 -UE8AAABSAAAFdm5sTkwAAABAAAAFyGVzRVMAAABMAAAGCHRoVEgAAAAyAAAGVHRyVFIAAAAkAAAGhmZp -RkkAAABGAAAGqmhySFIAAAA+AAAG8HBsUEwAAABKAAAHLmFyRUcAAAAsAAAHeHJ1UlUAAAA6AAAHpGVu -VVMAAAA8AAAH3gBWAWEAZQBvAGIAZQBjAG4A4QAgAHMAaQB2AOEAIABnAGEAbQBhACAAMgAsADIARwBl -AG4AZQByAGkAcwBrACAAZwByAOUAIAAyACwAMgAgAGcAYQBtAG0AYQAtAHAAcgBvAGYAaQBsAEcAYQBt -AG0AYQAgAGQAZQAgAGcAcgBpAHMAbwBzACAAZwBlAG4A6AByAGkAYwBhACAAMgAuADIAQx6lAHUAIABo -AOwAbgBoACAATQDgAHUAIAB4AOEAbQAgAEMAaAB1AG4AZwAgAEcAYQBtAG0AYQAgADIALgAyAFAAZQBy -AGYAaQBsACAARwBlAG4A6QByAGkAYwBvACAAZABhACAARwBhAG0AYQAgAGQAZQAgAEMAaQBuAHoAYQBz -ACAAMgAsADIEFwQwBDMEMAQ7BEwEPQQwACAARwByAGEAeQAtBDMEMAQ8BDAAIAAyAC4AMgBQAHIAbwBm -AGkAbAAgAGcA6QBuAOkAcgBpAHEAdQBlACAAZwByAGkAcwAgAGcAYQBtAG0AYQAgADIALAAyAMEAbAB0 -AGEAbADhAG4AbwBzACAAcwB6APwAcgBrAGUAIABnAGEAbQBtAGEAIAAyAC4AMpAadShwcJaOUUlepgAy -AC4AMoJyX2ljz4/wx3y8GAAg1ozAyQAgrBC5yAAgADIALgAyACDVBLhc0wzHfABHAGUAbgBlAHIAaQBz -AGsAIABnAHIA5QAgAGcAYQBtAG0AYQAgADIALAAyAC0AcAByAG8AZgBpAGwATwBiAGUAYwBuAOEAIAFh -AGUAZADhACAAZwBhAG0AYQAgADIALgAyBdIF0AXeBdQAIAXQBeQF1QXoACAF2wXcBdwF2QAgADIALgAy -AEcAYQBtAGEAIABnAHIAaQAgAGcAZQBuAGUAcgBpAGMBAwAgADIALAAyAEEAbABsAGcAZQBtAGUAaQBu -AGUAcwAgAEcAcgBhAHUAcwB0AHUAZgBlAG4ALQBQAHIAbwBmAGkAbAAgAEcAYQBtAG0AYQAgADIALAAy -AFAAcgBvAGYAaQBsAG8AIABnAHIAaQBnAGkAbwAgAGcAZQBuAGUAcgBpAGMAbwAgAGQAZQBsAGwAYQAg -AGcAYQBtAG0AYQAgADIALAAyAEcAZQBuAGUAcgBpAHMAawAgAGcAcgDlACAAMgAsADIAIABnAGEAbQBt -AGEAcAByAG8AZgBpAGxmbpAacHBepnz7ZXAAMgAuADJjz4/wZYdO9k4AgiwwsDDsMKQwrDDzMN4AIAAy -AC4AMgAgMNcw7TDVMKEwpDDrA5MDtQO9A7kDugPMACADkwO6A8EDuQAgA5MDrAO8A7wDsQAgADIALgAy -AFAAZQByAGYAaQBsACAAZwBlAG4A6QByAGkAYwBvACAAZABlACAAYwBpAG4AegBlAG4AdABvAHMAIABk -AGEAIABHAGEAbQBtAGEAIAAyACwAMgBBAGwAZwBlAG0AZQBlAG4AIABnAHIAaQBqAHMAIABnAGEAbQBt -AGEAIAAyACwAMgAtAHAAcgBvAGYAaQBlAGwAUABlAHIAZgBpAGwAIABnAGUAbgDpAHIAaQBjAG8AIABk -AGUAIABnAGEAbQBtAGEAIABkAGUAIABnAHIAaQBzAGUAcwAgADIALAAyDiMOMQ4HDioONQ5BDgEOIQ4h -DjIOQA4BDiMOIg5MDhcOMQ5IDicORA4bACAAMgAuADIARwBlAG4AZQBsACAARwByAGkAIABHAGEAbQBh -ACAAMgAsADIAWQBsAGUAaQBuAGUAbgAgAGgAYQByAG0AYQBhAG4AIABnAGEAbQBtAGEAIAAyACwAMgAg -AC0AcAByAG8AZgBpAGkAbABpAEcAZQBuAGUAcgBpAQ0AawBpACAARwByAGEAeQAgAEcAYQBtAG0AYQAg -ADIALgAyACAAcAByAG8AZgBpAGwAVQBuAGkAdwBlAHIAcwBhAGwAbgB5ACAAcAByAG8AZgBpAGwAIABz -AHoAYQByAG8BWwBjAGkAIABnAGEAbQBtAGEAIAAyACwAMgY6BicGRQYnACAAMgAuADIAIAZEBkgGRgAg -BjEGRQYnBi8GSgAgBjkGJwZFBB4EMQRJBDAETwAgBEEENQRABDAETwAgBDMEMAQ8BDwEMAAgADIALAAy -AC0EPwRABD4ERAQ4BDsETABHAGUAbgBlAHIAaQBjACAARwByAGEAeQAgAEcAYQBtAG0AYQAgADIALgAy -ACAAUAByAG8AZgBpAGwAZQAAdGV4dAAAAABDb3B5cmlnaHQgQXBwbGUgSW5jLiwgMjAxMgAAWFlaIAAA -AAAAAPNRAAEAAAABFsxjdXJ2AAAAAAAABAAAAAAFAAoADwAUABkAHgAjACgALQAyADcAOwBAAEUASgBP -AFQAWQBeAGMAaABtAHIAdwB8AIEAhgCLAJAAlQCaAJ8ApACpAK4AsgC3ALwAwQDGAMsA0ADVANsA4ADl -AOsA8AD2APsBAQEHAQ0BEwEZAR8BJQErATIBOAE+AUUBTAFSAVkBYAFnAW4BdQF8AYMBiwGSAZoBoQGp -AbEBuQHBAckB0QHZAeEB6QHyAfoCAwIMAhQCHQImAi8COAJBAksCVAJdAmcCcQJ6AoQCjgKYAqICrAK2 -AsECywLVAuAC6wL1AwADCwMWAyEDLQM4A0MDTwNaA2YDcgN+A4oDlgOiA64DugPHA9MD4APsA/kEBgQT -BCAELQQ7BEgEVQRjBHEEfgSMBJoEqAS2BMQE0wThBPAE/gUNBRwFKwU6BUkFWAVnBXcFhgWWBaYFtQXF -BdUF5QX2BgYGFgYnBjcGSAZZBmoGewaMBp0GrwbABtEG4wb1BwcHGQcrBz0HTwdhB3QHhgeZB6wHvwfS -B+UH+AgLCB8IMghGCFoIbgiCCJYIqgi+CNII5wj7CRAJJQk6CU8JZAl5CY8JpAm6Cc8J5Qn7ChEKJwo9 -ClQKagqBCpgKrgrFCtwK8wsLCyILOQtRC2kLgAuYC7ALyAvhC/kMEgwqDEMMXAx1DI4MpwzADNkM8w0N -DSYNQA1aDXQNjg2pDcMN3g34DhMOLg5JDmQOfw6bDrYO0g7uDwkPJQ9BD14Peg+WD7MPzw/sEAkQJhBD -EGEQfhCbELkQ1xD1ERMRMRFPEW0RjBGqEckR6BIHEiYSRRJkEoQSoxLDEuMTAxMjE0MTYxODE6QTxRPl -FAYUJxRJFGoUixStFM4U8BUSFTQVVhV4FZsVvRXgFgMWJhZJFmwWjxayFtYW+hcdF0EXZReJF64X0hf3 -GBsYQBhlGIoYrxjVGPoZIBlFGWsZkRm3Gd0aBBoqGlEadxqeGsUa7BsUGzsbYxuKG7Ib2hwCHCocUhx7 -HKMczBz1HR4dRx1wHZkdwx3sHhYeQB5qHpQevh7pHxMfPh9pH5Qfvx/qIBUgQSBsIJggxCDwIRwhSCF1 -IaEhziH7IiciVSKCIq8i3SMKIzgjZiOUI8Ij8CQfJE0kfCSrJNolCSU4JWgllyXHJfcmJyZXJocmtybo -JxgnSSd6J6sn3CgNKD8ocSiiKNQpBik4KWspnSnQKgIqNSpoKpsqzysCKzYraSudK9EsBSw5LG4soizX -LQwtQS12Last4S4WLkwugi63Lu4vJC9aL5Evxy/+MDUwbDCkMNsxEjFKMYIxujHyMioyYzKbMtQzDTNG -M38zuDPxNCs0ZTSeNNg1EzVNNYc1wjX9Njc2cjauNuk3JDdgN5w31zgUOFA4jDjIOQU5Qjl/Obw5+To2 -OnQ6sjrvOy07azuqO+g8JzxlPKQ84z0iPWE9oT3gPiA+YD6gPuA/IT9hP6I/4kAjQGRApkDnQSlBakGs -Qe5CMEJyQrVC90M6Q31DwEQDREdEikTORRJFVUWaRd5GIkZnRqtG8Ec1R3tHwEgFSEtIkUjXSR1JY0mp -SfBKN0p9SsRLDEtTS5pL4kwqTHJMuk0CTUpNk03cTiVObk63TwBPSU+TT91QJ1BxULtRBlFQUZtR5lIx -UnxSx1MTU19TqlP2VEJUj1TbVShVdVXCVg9WXFapVvdXRFeSV+BYL1h9WMtZGllpWbhaB1pWWqZa9VtF -W5Vb5Vw1XIZc1l0nXXhdyV4aXmxevV8PX2Ffs2AFYFdgqmD8YU9homH1YklinGLwY0Njl2PrZEBklGTp -ZT1lkmXnZj1mkmboZz1nk2fpaD9olmjsaUNpmmnxakhqn2r3a09rp2v/bFdsr20IbWBtuW4SbmtuxG8e -b3hv0XArcIZw4HE6cZVx8HJLcqZzAXNdc7h0FHRwdMx1KHWFdeF2Pnabdvh3VnezeBF4bnjMeSp5iXnn -ekZ6pXsEe2N7wnwhfIF84X1BfaF+AX5ifsJ/I3+Ef+WAR4CogQqBa4HNgjCCkoL0g1eDuoQdhICE44VH -hauGDoZyhteHO4efiASIaYjOiTOJmYn+imSKyoswi5aL/IxjjMqNMY2Yjf+OZo7OjzaPnpAGkG6Q1pE/ -kaiSEZJ6kuOTTZO2lCCUipT0lV+VyZY0lp+XCpd1l+CYTJi4mSSZkJn8mmia1ZtCm6+cHJyJnPedZJ3S -nkCerp8dn4uf+qBpoNihR6G2oiailqMGo3aj5qRWpMelOKWpphqmi6b9p26n4KhSqMSpN6mpqhyqj6sC -q3Wr6axcrNCtRK24ri2uoa8Wr4uwALB1sOqxYLHWskuywrM4s660JbSctRO1irYBtnm28Ldot+C4WbjR -uUq5wro7urW7LrunvCG8m70VvY++Cr6Evv+/er/1wHDA7MFnwePCX8Lbw1jD1MRRxM7FS8XIxkbGw8dB -x7/IPci8yTrJuco4yrfLNsu2zDXMtc01zbXONs62zzfPuNA50LrRPNG+0j/SwdNE08bUSdTL1U7V0dZV -1tjXXNfg2GTY6Nls2fHadtr724DcBdyK3RDdlt4c3qLfKd+v4DbgveFE4cziU+Lb42Pj6+Rz5PzlhOYN -5pbnH+ep6DLovOlG6dDqW+rl63Dr++yG7RHtnO4o7rTvQO/M8Fjw5fFy8f/yjPMZ86f0NPTC9VD13vZt -9vv3ivgZ+Kj5OPnH+lf65/t3/Af8mP0p/br+S/7c/23//9IrLC0uWiRjbGFzc25hbWVYJGNsYXNzZXNf -EBBOU0JpdG1hcEltYWdlUmVwoy0vMFpOU0ltYWdlUmVwWE5TT2JqZWN00issMjNXTlNBcnJheaIyMNIr -LDU2Xk5TTXV0YWJsZUFycmF5ozUyMNM4OQ86OzxXTlNXaGl0ZVxOU0NvbG9yU3BhY2VEMCAwABADgAzS -Kyw+P1dOU0NvbG9yoj4w0issQUJXTlNJbWFnZaJBMAAIABEAGgAkACkAMgA3AEkATABRAFMAYgBoAHUA -fACLAJIAnwCmAK4AsACyALQAuQC7AL0AxADJANQA1gDYANoA3wDiAOQA5gDoAO8BBgEiASQBJhOWE5sT -phOvE8ITxhPRE9oT3xPnE+oT7xP+FAIUCRQRFB4UIxQlFCcULBQ0FDcUPBREAAAAAAAAAgEAAAAAAAAA -QwAAAAAAAAAAAAAAAAAAFEc +S2V5ZWRBcmNoaXZlctEICVRyb290gAGuCwwbHCEaJissMzY5P0JVJG51bGzXDQ4PEBESExQVFhcYGRpW +TlNTaXplViRjbGFzc1xOU0ltYWdlRmxhZ3NWTlNSZXBzW05TVGludENvbG9yV05TQ29sb3JeTlNSZXNp +emluZ01vZGWAAoANEiDDAACAA4AAgAsQAFZ7MSwgMX3SHQ4eIFpOUy5vYmplY3RzoR+ABIAK0h0OIiWi +IySABYAGgAnTDicoKSoaXxAUTlNUSUZGUmVwcmVzZW50YXRpb25fEBlOU0ludGVybmFsTGF5b3V0RGly +ZWN0aW9ugAiAB08REmxNTQAqAAAACgAAABABAAADAAAAAQABAAABAQADAAAAAQABAAABAgADAAAAAgAI +AAgBAwADAAAAAQABAAABBgADAAAAAQABAAABCgADAAAAAQABAAABEQAEAAAAAQAAAAgBEgADAAAAAQAB +AAABFQADAAAAAQACAAABFgADAAAAAQABAAABFwAEAAAAAQAAAAIBHAADAAAAAQABAAABKAADAAAAAQAC +AAABUgADAAAAAQABAAABUwADAAAAAgABAAGHcwAHAAARnAAAANAAAAAAAAARnGFwcGwCAAAAbW50ckdS +QVlYWVogB9wACAAXAA8ALgAPYWNzcEFQUEwAAAAAbm9uZQAAAAAAAAAAAAAAAAAAAAAAAPbWAAEAAAAA +0y1hcHBsAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFZGVzYwAA +AMAAAAB5ZHNjbQAAATwAAAgaY3BydAAACVgAAAAjd3RwdAAACXwAAAAUa1RSQwAACZAAAAgMZGVzYwAA +AAAAAAAfR2VuZXJpYyBHcmF5IEdhbW1hIDIuMiBQcm9maWxlAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAG1s +dWMAAAAAAAAAHwAAAAxza1NLAAAALgAAAYRkYURLAAAAOgAAAbJjYUVTAAAAOAAAAex2aVZOAAAAQAAA +AiRwdEJSAAAASgAAAmR1a1VBAAAALAAAAq5mckZVAAAAPgAAAtpodUhVAAAANAAAAxh6aFRXAAAAGgAA +A0xrb0tSAAAAIgAAA2ZuYk5PAAAAOgAAA4hjc0NaAAAAKAAAA8JoZUlMAAAAJAAAA+pyb1JPAAAAKgAA +BA5kZURFAAAATgAABDhpdElUAAAATgAABIZzdlNFAAAAOAAABNR6aENOAAAAGgAABQxqYUpQAAAAJgAA +BSZlbEdSAAAAKgAABUxwdFBPAAAAUgAABXZubE5MAAAAQAAABchlc0VTAAAATAAABgh0aFRIAAAAMgAA +BlR0clRSAAAAJAAABoZmaUZJAAAARgAABqpockhSAAAAPgAABvBwbFBMAAAASgAABy5hckVHAAAALAAA +B3hydVJVAAAAOgAAB6RlblVTAAAAPAAAB94AVgFhAGUAbwBiAGUAYwBuAOEAIABzAGkAdgDhACAAZwBh +AG0AYQAgADIALAAyAEcAZQBuAGUAcgBpAHMAawAgAGcAcgDlACAAMgAsADIAIABnAGEAbQBtAGEALQBw +AHIAbwBmAGkAbABHAGEAbQBtAGEAIABkAGUAIABnAHIAaQBzAG8AcwAgAGcAZQBuAOgAcgBpAGMAYQAg +ADIALgAyAEMepQB1ACAAaADsAG4AaAAgAE0A4AB1ACAAeADhAG0AIABDAGgAdQBuAGcAIABHAGEAbQBt +AGEAIAAyAC4AMgBQAGUAcgBmAGkAbAAgAEcAZQBuAOkAcgBpAGMAbwAgAGQAYQAgAEcAYQBtAGEAIABk +AGUAIABDAGkAbgB6AGEAcwAgADIALAAyBBcEMAQzBDAEOwRMBD0EMAAgAEcAcgBhAHkALQQzBDAEPAQw +ACAAMgAuADIAUAByAG8AZgBpAGwAIABnAOkAbgDpAHIAaQBxAHUAZQAgAGcAcgBpAHMAIABnAGEAbQBt +AGEAIAAyACwAMgDBAGwAdABhAGwA4QBuAG8AcwAgAHMAegD8AHIAawBlACAAZwBhAG0AbQBhACAAMgAu +ADKQGnUocHCWjlFJXqYAMgAuADKCcl9pY8+P8Md8vBgAINaMwMkAIKwQucgAIAAyAC4AMgAg1QS4XNMM +x3wARwBlAG4AZQByAGkAcwBrACAAZwByAOUAIABnAGEAbQBtAGEAIAAyACwAMgAtAHAAcgBvAGYAaQBs +AE8AYgBlAGMAbgDhACABYQBlAGQA4QAgAGcAYQBtAGEAIAAyAC4AMgXSBdAF3gXUACAF0AXkBdUF6AAg +BdsF3AXcBdkAIAAyAC4AMgBHAGEAbQBhACAAZwByAGkAIABnAGUAbgBlAHIAaQBjAQMAIAAyACwAMgBB +AGwAbABnAGUAbQBlAGkAbgBlAHMAIABHAHIAYQB1AHMAdAB1AGYAZQBuAC0AUAByAG8AZgBpAGwAIABH +AGEAbQBtAGEAIAAyACwAMgBQAHIAbwBmAGkAbABvACAAZwByAGkAZwBpAG8AIABnAGUAbgBlAHIAaQBj +AG8AIABkAGUAbABsAGEAIABnAGEAbQBtAGEAIAAyACwAMgBHAGUAbgBlAHIAaQBzAGsAIABnAHIA5QAg +ADIALAAyACAAZwBhAG0AbQBhAHAAcgBvAGYAaQBsZm6QGnBwXqZ8+2VwADIALgAyY8+P8GWHTvZOAIIs +MLAw7DCkMKww8zDeACAAMgAuADIAIDDXMO0w1TChMKQw6wOTA7UDvQO5A7oDzAAgA5MDugPBA7kAIAOT +A6wDvAO8A7EAIAAyAC4AMgBQAGUAcgBmAGkAbAAgAGcAZQBuAOkAcgBpAGMAbwAgAGQAZQAgAGMAaQBu +AHoAZQBuAHQAbwBzACAAZABhACAARwBhAG0AbQBhACAAMgAsADIAQQBsAGcAZQBtAGUAZQBuACAAZwBy +AGkAagBzACAAZwBhAG0AbQBhACAAMgAsADIALQBwAHIAbwBmAGkAZQBsAFAAZQByAGYAaQBsACAAZwBl +AG4A6QByAGkAYwBvACAAZABlACAAZwBhAG0AbQBhACAAZABlACAAZwByAGkAcwBlAHMAIAAyACwAMg4j +DjEOBw4qDjUOQQ4BDiEOIQ4yDkAOAQ4jDiIOTA4XDjEOSA4nDkQOGwAgADIALgAyAEcAZQBuAGUAbAAg +AEcAcgBpACAARwBhAG0AYQAgADIALAAyAFkAbABlAGkAbgBlAG4AIABoAGEAcgBtAGEAYQBuACAAZwBh +AG0AbQBhACAAMgAsADIAIAAtAHAAcgBvAGYAaQBpAGwAaQBHAGUAbgBlAHIAaQENAGsAaQAgAEcAcgBh +AHkAIABHAGEAbQBtAGEAIAAyAC4AMgAgAHAAcgBvAGYAaQBsAFUAbgBpAHcAZQByAHMAYQBsAG4AeQAg +AHAAcgBvAGYAaQBsACAAcwB6AGEAcgBvAVsAYwBpACAAZwBhAG0AbQBhACAAMgAsADIGOgYnBkUGJwAg +ADIALgAyACAGRAZIBkYAIAYxBkUGJwYvBkoAIAY5BicGRQQeBDEESQQwBE8AIARBBDUEQAQwBE8AIAQz +BDAEPAQ8BDAAIAAyACwAMgAtBD8EQAQ+BEQEOAQ7BEwARwBlAG4AZQByAGkAYwAgAEcAcgBhAHkAIABH +AGEAbQBtAGEAIAAyAC4AMgAgAFAAcgBvAGYAaQBsAGUAAHRleHQAAAAAQ29weXJpZ2h0IEFwcGxlIElu +Yy4sIDIwMTIAAFhZWiAAAAAAAADzUQABAAAAARbMY3VydgAAAAAAAAQAAAAABQAKAA8AFAAZAB4AIwAo +AC0AMgA3ADsAQABFAEoATwBUAFkAXgBjAGgAbQByAHcAfACBAIYAiwCQAJUAmgCfAKQAqQCuALIAtwC8 +AMEAxgDLANAA1QDbAOAA5QDrAPAA9gD7AQEBBwENARMBGQEfASUBKwEyATgBPgFFAUwBUgFZAWABZwFu +AXUBfAGDAYsBkgGaAaEBqQGxAbkBwQHJAdEB2QHhAekB8gH6AgMCDAIUAh0CJgIvAjgCQQJLAlQCXQJn +AnECegKEAo4CmAKiAqwCtgLBAssC1QLgAusC9QMAAwsDFgMhAy0DOANDA08DWgNmA3IDfgOKA5YDogOu +A7oDxwPTA+AD7AP5BAYEEwQgBC0EOwRIBFUEYwRxBH4EjASaBKgEtgTEBNME4QTwBP4FDQUcBSsFOgVJ +BVgFZwV3BYYFlgWmBbUFxQXVBeUF9gYGBhYGJwY3BkgGWQZqBnsGjAadBq8GwAbRBuMG9QcHBxkHKwc9 +B08HYQd0B4YHmQesB78H0gflB/gICwgfCDIIRghaCG4IggiWCKoIvgjSCOcI+wkQCSUJOglPCWQJeQmP +CaQJugnPCeUJ+woRCicKPQpUCmoKgQqYCq4KxQrcCvMLCwsiCzkLUQtpC4ALmAuwC8gL4Qv5DBIMKgxD +DFwMdQyODKcMwAzZDPMNDQ0mDUANWg10DY4NqQ3DDd4N+A4TDi4OSQ5kDn8Omw62DtIO7g8JDyUPQQ9e +D3oPlg+zD88P7BAJECYQQxBhEH4QmxC5ENcQ9RETETERTxFtEYwRqhHJEegSBxImEkUSZBKEEqMSwxLj +EwMTIxNDE2MTgxOkE8UT5RQGFCcUSRRqFIsUrRTOFPAVEhU0FVYVeBWbFb0V4BYDFiYWSRZsFo8WshbW +FvoXHRdBF2UXiReuF9IX9xgbGEAYZRiKGK8Y1Rj6GSAZRRlrGZEZtxndGgQaKhpRGncanhrFGuwbFBs7 +G2MbihuyG9ocAhwqHFIcexyjHMwc9R0eHUcdcB2ZHcMd7B4WHkAeah6UHr4e6R8THz4faR+UH78f6iAV +IEEgbCCYIMQg8CEcIUghdSGhIc4h+yInIlUigiKvIt0jCiM4I2YjlCPCI/AkHyRNJHwkqyTaJQklOCVo +JZclxyX3JicmVyaHJrcm6CcYJ0kneierJ9woDSg/KHEooijUKQYpOClrKZ0p0CoCKjUqaCqbKs8rAis2 +K2krnSvRLAUsOSxuLKIs1y0MLUEtdi2rLeEuFi5MLoIuty7uLyQvWi+RL8cv/jA1MGwwpDDbMRIxSjGC +Mbox8jIqMmMymzLUMw0zRjN/M7gz8TQrNGU0njTYNRM1TTWHNcI1/TY3NnI2rjbpNyQ3YDecN9c4FDhQ +OIw4yDkFOUI5fzm8Ofk6Njp0OrI67zstO2s7qjvoPCc8ZTykPOM9Ij1hPaE94D4gPmA+oD7gPyE/YT+i +P+JAI0BkQKZA50EpQWpBrEHuQjBCckK1QvdDOkN9Q8BEA0RHRIpEzkUSRVVFmkXeRiJGZ0arRvBHNUd7 +R8BIBUhLSJFI10kdSWNJqUnwSjdKfUrESwxLU0uaS+JMKkxyTLpNAk1KTZNN3E4lTm5Ot08AT0lPk0/d +UCdQcVC7UQZRUFGbUeZSMVJ8UsdTE1NfU6pT9lRCVI9U21UoVXVVwlYPVlxWqVb3V0RXklfgWC9YfVjL +WRpZaVm4WgdaVlqmWvVbRVuVW+VcNVyGXNZdJ114XcleGl5sXr1fD19hX7NgBWBXYKpg/GFPYaJh9WJJ +Ypxi8GNDY5dj62RAZJRk6WU9ZZJl52Y9ZpJm6Gc9Z5Nn6Wg/aJZo7GlDaZpp8WpIap9q92tPa6dr/2xX +bK9tCG1gbbluEm5rbsRvHm94b9FwK3CGcOBxOnGVcfByS3KmcwFzXXO4dBR0cHTMdSh1hXXhdj52m3b4 +d1Z3s3gReG54zHkqeYl553pGeqV7BHtje8J8IXyBfOF9QX2hfgF+Yn7CfyN/hH/lgEeAqIEKgWuBzYIw +gpKC9INXg7qEHYSAhOOFR4Wrhg6GcobXhzuHn4gEiGmIzokziZmJ/opkisqLMIuWi/yMY4zKjTGNmI3/ +jmaOzo82j56QBpBukNaRP5GokhGSepLjk02TtpQglIqU9JVflcmWNJaflwqXdZfgmEyYuJkkmZCZ/Jpo +mtWbQpuvnByciZz3nWSd0p5Anq6fHZ+Ln/qgaaDYoUehtqImopajBqN2o+akVqTHpTilqaYapoum/adu +p+CoUqjEqTepqaocqo+rAqt1q+msXKzQrUStuK4trqGvFq+LsACwdbDqsWCx1rJLssKzOLOutCW0nLUT +tYq2AbZ5tvC3aLfguFm40blKucK6O7q1uy67p7whvJu9Fb2Pvgq+hL7/v3q/9cBwwOzBZ8Hjwl/C28NY +w9TEUcTOxUvFyMZGxsPHQce/yD3IvMk6ybnKOMq3yzbLtsw1zLXNNc21zjbOts83z7jQOdC60TzRvtI/ +0sHTRNPG1EnUy9VO1dHWVdbY11zX4Nhk2OjZbNnx2nba+9uA3AXcit0Q3ZbeHN6i3ynfr+A24L3hROHM +4lPi2+Nj4+vkc+T85YTmDeaW5x/nqegy6LzpRunQ6lvq5etw6/vshu0R7ZzuKO6070DvzPBY8OXxcvH/ +8ozzGfOn9DT0wvVQ9d72bfb794r4Gfio+Tj5x/pX+uf7d/wH/Jj9Kf26/kv+3P9t///SLS4vMFokY2xh +c3NuYW1lWCRjbGFzc2VzXxAQTlNCaXRtYXBJbWFnZVJlcKMvMTJaTlNJbWFnZVJlcFhOU09iamVjdNIt +LjQ1V05TQXJyYXmiNDLSLS43OF5OU011dGFibGVBcnJheaM3NDLTOjsOPD0+V05TV2hpdGVcTlNDb2xv +clNwYWNlRDAgMAAQA4AM0i0uQEFXTlNDb2xvcqJAMtItLkNEV05TSW1hZ2WiQzIACAARABoAJAApADIA +NwBJAEwAUQBTAGIAaAB3AH4AhQCSAJkApQCtALwAvgDAAMUAxwDJAMsAzQDUANkA5ADmAOgA6gDvAPIA +9AD2APgA/wEWATIBNAE2E6YTqxO2E78T0hPWE+ET6hPvE/cT+hP/FA4UEhQZFCEULhQzFDUUNxQ8FEQU +RxRMFFQAAAAAAAACAQAAAAAAAABFAAAAAAAAAAAAAAAAAAAUVw diff --git a/i18n/src/main/resources/lv.lproj/Command.xib b/i18n/src/main/resources/lv.lproj/Command.xib index ffe7e25da3..97eba4a4c2 100644 --- a/i18n/src/main/resources/lv.lproj/Command.xib +++ b/i18n/src/main/resources/lv.lproj/Command.xib @@ -1,7 +1,7 @@ - + - + diff --git a/i18n/src/main/resources/lv.lproj/Connection.xib b/i18n/src/main/resources/lv.lproj/Connection.xib index 12b5a451a8..9cfe65a09f 100644 --- a/i18n/src/main/resources/lv.lproj/Connection.xib +++ b/i18n/src/main/resources/lv.lproj/Connection.xib @@ -1,7 +1,7 @@ - + - + @@ -76,7 +76,7 @@ - + diff --git a/i18n/src/main/resources/lv.lproj/Info.xib b/i18n/src/main/resources/lv.lproj/Info.xib index dfd082f5d0..df29ca9581 100644 --- a/i18n/src/main/resources/lv.lproj/Info.xib +++ b/i18n/src/main/resources/lv.lproj/Info.xib @@ -1,7 +1,7 @@ - + - + @@ -89,7 +89,7 @@ - + @@ -1017,21 +1017,21 @@ - + - + - + @@ -1121,21 +1121,21 @@ - + - + - + @@ -1250,27 +1250,27 @@ diff --git a/i18n/src/main/resources/lv.lproj/Login.strings b/i18n/src/main/resources/lv.lproj/Login.strings index d4741e4c10..3595ea5945 100644 Binary files a/i18n/src/main/resources/lv.lproj/Login.strings and b/i18n/src/main/resources/lv.lproj/Login.strings differ diff --git a/i18n/src/main/resources/lv.lproj/Main.xib b/i18n/src/main/resources/lv.lproj/Main.xib index 3a6664b53b..f635563e8f 100644 --- a/i18n/src/main/resources/lv.lproj/Main.xib +++ b/i18n/src/main/resources/lv.lproj/Main.xib @@ -1,7 +1,7 @@ - + - + @@ -94,7 +94,7 @@ - + @@ -159,19 +159,19 @@ - + - + - + @@ -321,12 +321,12 @@ CA - + - + @@ -566,6 +566,10 @@ CA - + + + + + diff --git a/i18n/src/main/resources/lv.lproj/Preferences.xib b/i18n/src/main/resources/lv.lproj/Preferences.xib index 8a41973a2d..3a41656e19 100644 --- a/i18n/src/main/resources/lv.lproj/Preferences.xib +++ b/i18n/src/main/resources/lv.lproj/Preferences.xib @@ -1,7 +1,7 @@ - + - + @@ -266,7 +266,7 @@ - + @@ -307,7 +307,7 @@ - + @@ -538,7 +538,7 @@ - + @@ -578,7 +578,7 @@ - + @@ -613,7 +613,7 @@ - + @@ -642,7 +642,7 @@ - + @@ -1218,7 +1218,7 @@ - + @@ -1250,7 +1250,7 @@ - + @@ -1290,7 +1290,7 @@ - + @@ -1363,7 +1363,7 @@ - + @@ -1558,7 +1558,7 @@ - + @@ -1618,7 +1618,7 @@ - + @@ -1643,7 +1643,7 @@ - + @@ -1685,7 +1685,7 @@ - + @@ -1713,7 +1713,7 @@ - + @@ -1741,7 +1741,7 @@ - + @@ -1770,7 +1770,7 @@ - + @@ -1805,7 +1805,7 @@ - + @@ -1888,7 +1888,7 @@ - + @@ -1947,7 +1947,7 @@ - + @@ -1975,7 +1975,7 @@ - + @@ -2004,7 +2004,7 @@ - + diff --git a/i18n/src/main/resources/lv.lproj/Profiles.xib b/i18n/src/main/resources/lv.lproj/Profiles.xib index 18af5a58f4..20e5ce0f36 100644 --- a/i18n/src/main/resources/lv.lproj/Profiles.xib +++ b/i18n/src/main/resources/lv.lproj/Profiles.xib @@ -1,7 +1,7 @@ - + - + diff --git a/i18n/src/main/resources/lv.lproj/Prompt.xib b/i18n/src/main/resources/lv.lproj/Prompt.xib index 7dd5c9f2f6..d41d18fe84 100644 --- a/i18n/src/main/resources/lv.lproj/Prompt.xib +++ b/i18n/src/main/resources/lv.lproj/Prompt.xib @@ -1,7 +1,7 @@ - + - + @@ -95,7 +95,7 @@ Gw - + diff --git a/i18n/src/main/resources/lv.lproj/S3.strings b/i18n/src/main/resources/lv.lproj/S3.strings index 4dd2c81245..4c6dc19c79 100644 Binary files a/i18n/src/main/resources/lv.lproj/S3.strings and b/i18n/src/main/resources/lv.lproj/S3.strings differ diff --git a/i18n/src/main/resources/lv.lproj/Updater.strings b/i18n/src/main/resources/lv.lproj/Updater.strings index 0b0c280369..3df64e17ee 100644 Binary files a/i18n/src/main/resources/lv.lproj/Updater.strings and b/i18n/src/main/resources/lv.lproj/Updater.strings differ diff --git a/i18n/src/main/resources/nl.lproj/Bookmark.xib b/i18n/src/main/resources/nl.lproj/Bookmark.xib index 5a0c3fe3c1..39d38ab08c 100644 --- a/i18n/src/main/resources/nl.lproj/Bookmark.xib +++ b/i18n/src/main/resources/nl.lproj/Bookmark.xib @@ -1,7 +1,7 @@ - + - + @@ -92,7 +92,7 @@ - + @@ -489,95 +489,95 @@ YnBsaXN0MDDUAQIDBAUGBwpYJHZlcnNpb25ZJGFyY2hpdmVyVCR0b3BYJG9iamVjdHMSAAGGoF8QD05T -S2V5ZWRBcmNoaXZlctEICVRyb290gAGuCwwZGh8UJCkqMTQ3PUBVJG51bGzWDQ4PEBESExQVFhcYVk5T -U2l6ZV5OU1Jlc2l6aW5nTW9kZVYkY2xhc3NcTlNJbWFnZUZsYWdzVk5TUmVwc1dOU0NvbG9ygAIQAIAN -EiDDAACAA4ALVnsxLCAxfdIbDxweWk5TLm9iamVjdHOhHYAEgArSGw8gI6IhIoAFgAaACdMPJSYnKBRf -EBROU1RJRkZSZXByZXNlbnRhdGlvbl8QGU5TSW50ZXJuYWxMYXlvdXREaXJlY3Rpb26ACIAHTxESbE1N -ACoAAAAKAAAAEAEAAAMAAAABAAEAAAEBAAMAAAABAAEAAAECAAMAAAACAAgACAEDAAMAAAABAAEAAAEG -AAMAAAABAAEAAAEKAAMAAAABAAEAAAERAAQAAAABAAAACAESAAMAAAABAAEAAAEVAAMAAAABAAIAAAEW -AAMAAAABAAEAAAEXAAQAAAABAAAAAgEcAAMAAAABAAEAAAEoAAMAAAABAAIAAAFSAAMAAAABAAEAAAFT -AAMAAAACAAEAAYdzAAcAABGcAAAA0AAAAAAAABGcYXBwbAIAAABtbnRyR1JBWVhZWiAH3AAIABcADwAu -AA9hY3NwQVBQTAAAAABub25lAAAAAAAAAAAAAAAAAAAAAAAA9tYAAQAAAADTLWFwcGwAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAVkZXNjAAAAwAAAAHlkc2NtAAABPAAA -CBpjcHJ0AAAJWAAAACN3dHB0AAAJfAAAABRrVFJDAAAJkAAACAxkZXNjAAAAAAAAAB9HZW5lcmljIEdy -YXkgR2FtbWEgMi4yIFByb2ZpbGUAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAbWx1YwAAAAAAAAAfAAAADHNr -U0sAAAAuAAABhGRhREsAAAA6AAABsmNhRVMAAAA4AAAB7HZpVk4AAABAAAACJHB0QlIAAABKAAACZHVr -VUEAAAAsAAACrmZyRlUAAAA+AAAC2mh1SFUAAAA0AAADGHpoVFcAAAAaAAADTGtvS1IAAAAiAAADZm5i -Tk8AAAA6AAADiGNzQ1oAAAAoAAADwmhlSUwAAAAkAAAD6nJvUk8AAAAqAAAEDmRlREUAAABOAAAEOGl0 -SVQAAABOAAAEhnN2U0UAAAA4AAAE1HpoQ04AAAAaAAAFDGphSlAAAAAmAAAFJmVsR1IAAAAqAAAFTHB0 -UE8AAABSAAAFdm5sTkwAAABAAAAFyGVzRVMAAABMAAAGCHRoVEgAAAAyAAAGVHRyVFIAAAAkAAAGhmZp -RkkAAABGAAAGqmhySFIAAAA+AAAG8HBsUEwAAABKAAAHLmFyRUcAAAAsAAAHeHJ1UlUAAAA6AAAHpGVu -VVMAAAA8AAAH3gBWAWEAZQBvAGIAZQBjAG4A4QAgAHMAaQB2AOEAIABnAGEAbQBhACAAMgAsADIARwBl -AG4AZQByAGkAcwBrACAAZwByAOUAIAAyACwAMgAgAGcAYQBtAG0AYQAtAHAAcgBvAGYAaQBsAEcAYQBt -AG0AYQAgAGQAZQAgAGcAcgBpAHMAbwBzACAAZwBlAG4A6AByAGkAYwBhACAAMgAuADIAQx6lAHUAIABo -AOwAbgBoACAATQDgAHUAIAB4AOEAbQAgAEMAaAB1AG4AZwAgAEcAYQBtAG0AYQAgADIALgAyAFAAZQBy -AGYAaQBsACAARwBlAG4A6QByAGkAYwBvACAAZABhACAARwBhAG0AYQAgAGQAZQAgAEMAaQBuAHoAYQBz -ACAAMgAsADIEFwQwBDMEMAQ7BEwEPQQwACAARwByAGEAeQAtBDMEMAQ8BDAAIAAyAC4AMgBQAHIAbwBm -AGkAbAAgAGcA6QBuAOkAcgBpAHEAdQBlACAAZwByAGkAcwAgAGcAYQBtAG0AYQAgADIALAAyAMEAbAB0 -AGEAbADhAG4AbwBzACAAcwB6APwAcgBrAGUAIABnAGEAbQBtAGEAIAAyAC4AMpAadShwcJaOUUlepgAy -AC4AMoJyX2ljz4/wx3y8GAAg1ozAyQAgrBC5yAAgADIALgAyACDVBLhc0wzHfABHAGUAbgBlAHIAaQBz -AGsAIABnAHIA5QAgAGcAYQBtAG0AYQAgADIALAAyAC0AcAByAG8AZgBpAGwATwBiAGUAYwBuAOEAIAFh -AGUAZADhACAAZwBhAG0AYQAgADIALgAyBdIF0AXeBdQAIAXQBeQF1QXoACAF2wXcBdwF2QAgADIALgAy -AEcAYQBtAGEAIABnAHIAaQAgAGcAZQBuAGUAcgBpAGMBAwAgADIALAAyAEEAbABsAGcAZQBtAGUAaQBu -AGUAcwAgAEcAcgBhAHUAcwB0AHUAZgBlAG4ALQBQAHIAbwBmAGkAbAAgAEcAYQBtAG0AYQAgADIALAAy -AFAAcgBvAGYAaQBsAG8AIABnAHIAaQBnAGkAbwAgAGcAZQBuAGUAcgBpAGMAbwAgAGQAZQBsAGwAYQAg -AGcAYQBtAG0AYQAgADIALAAyAEcAZQBuAGUAcgBpAHMAawAgAGcAcgDlACAAMgAsADIAIABnAGEAbQBt -AGEAcAByAG8AZgBpAGxmbpAacHBepnz7ZXAAMgAuADJjz4/wZYdO9k4AgiwwsDDsMKQwrDDzMN4AIAAy -AC4AMgAgMNcw7TDVMKEwpDDrA5MDtQO9A7kDugPMACADkwO6A8EDuQAgA5MDrAO8A7wDsQAgADIALgAy -AFAAZQByAGYAaQBsACAAZwBlAG4A6QByAGkAYwBvACAAZABlACAAYwBpAG4AegBlAG4AdABvAHMAIABk -AGEAIABHAGEAbQBtAGEAIAAyACwAMgBBAGwAZwBlAG0AZQBlAG4AIABnAHIAaQBqAHMAIABnAGEAbQBt -AGEAIAAyACwAMgAtAHAAcgBvAGYAaQBlAGwAUABlAHIAZgBpAGwAIABnAGUAbgDpAHIAaQBjAG8AIABk -AGUAIABnAGEAbQBtAGEAIABkAGUAIABnAHIAaQBzAGUAcwAgADIALAAyDiMOMQ4HDioONQ5BDgEOIQ4h -DjIOQA4BDiMOIg5MDhcOMQ5IDicORA4bACAAMgAuADIARwBlAG4AZQBsACAARwByAGkAIABHAGEAbQBh -ACAAMgAsADIAWQBsAGUAaQBuAGUAbgAgAGgAYQByAG0AYQBhAG4AIABnAGEAbQBtAGEAIAAyACwAMgAg -AC0AcAByAG8AZgBpAGkAbABpAEcAZQBuAGUAcgBpAQ0AawBpACAARwByAGEAeQAgAEcAYQBtAG0AYQAg -ADIALgAyACAAcAByAG8AZgBpAGwAVQBuAGkAdwBlAHIAcwBhAGwAbgB5ACAAcAByAG8AZgBpAGwAIABz -AHoAYQByAG8BWwBjAGkAIABnAGEAbQBtAGEAIAAyACwAMgY6BicGRQYnACAAMgAuADIAIAZEBkgGRgAg -BjEGRQYnBi8GSgAgBjkGJwZFBB4EMQRJBDAETwAgBEEENQRABDAETwAgBDMEMAQ8BDwEMAAgADIALAAy -AC0EPwRABD4ERAQ4BDsETABHAGUAbgBlAHIAaQBjACAARwByAGEAeQAgAEcAYQBtAG0AYQAgADIALgAy -ACAAUAByAG8AZgBpAGwAZQAAdGV4dAAAAABDb3B5cmlnaHQgQXBwbGUgSW5jLiwgMjAxMgAAWFlaIAAA -AAAAAPNRAAEAAAABFsxjdXJ2AAAAAAAABAAAAAAFAAoADwAUABkAHgAjACgALQAyADcAOwBAAEUASgBP -AFQAWQBeAGMAaABtAHIAdwB8AIEAhgCLAJAAlQCaAJ8ApACpAK4AsgC3ALwAwQDGAMsA0ADVANsA4ADl -AOsA8AD2APsBAQEHAQ0BEwEZAR8BJQErATIBOAE+AUUBTAFSAVkBYAFnAW4BdQF8AYMBiwGSAZoBoQGp -AbEBuQHBAckB0QHZAeEB6QHyAfoCAwIMAhQCHQImAi8COAJBAksCVAJdAmcCcQJ6AoQCjgKYAqICrAK2 -AsECywLVAuAC6wL1AwADCwMWAyEDLQM4A0MDTwNaA2YDcgN+A4oDlgOiA64DugPHA9MD4APsA/kEBgQT -BCAELQQ7BEgEVQRjBHEEfgSMBJoEqAS2BMQE0wThBPAE/gUNBRwFKwU6BUkFWAVnBXcFhgWWBaYFtQXF -BdUF5QX2BgYGFgYnBjcGSAZZBmoGewaMBp0GrwbABtEG4wb1BwcHGQcrBz0HTwdhB3QHhgeZB6wHvwfS -B+UH+AgLCB8IMghGCFoIbgiCCJYIqgi+CNII5wj7CRAJJQk6CU8JZAl5CY8JpAm6Cc8J5Qn7ChEKJwo9 -ClQKagqBCpgKrgrFCtwK8wsLCyILOQtRC2kLgAuYC7ALyAvhC/kMEgwqDEMMXAx1DI4MpwzADNkM8w0N -DSYNQA1aDXQNjg2pDcMN3g34DhMOLg5JDmQOfw6bDrYO0g7uDwkPJQ9BD14Peg+WD7MPzw/sEAkQJhBD -EGEQfhCbELkQ1xD1ERMRMRFPEW0RjBGqEckR6BIHEiYSRRJkEoQSoxLDEuMTAxMjE0MTYxODE6QTxRPl -FAYUJxRJFGoUixStFM4U8BUSFTQVVhV4FZsVvRXgFgMWJhZJFmwWjxayFtYW+hcdF0EXZReJF64X0hf3 -GBsYQBhlGIoYrxjVGPoZIBlFGWsZkRm3Gd0aBBoqGlEadxqeGsUa7BsUGzsbYxuKG7Ib2hwCHCocUhx7 -HKMczBz1HR4dRx1wHZkdwx3sHhYeQB5qHpQevh7pHxMfPh9pH5Qfvx/qIBUgQSBsIJggxCDwIRwhSCF1 -IaEhziH7IiciVSKCIq8i3SMKIzgjZiOUI8Ij8CQfJE0kfCSrJNolCSU4JWgllyXHJfcmJyZXJocmtybo -JxgnSSd6J6sn3CgNKD8ocSiiKNQpBik4KWspnSnQKgIqNSpoKpsqzysCKzYraSudK9EsBSw5LG4soizX -LQwtQS12Last4S4WLkwugi63Lu4vJC9aL5Evxy/+MDUwbDCkMNsxEjFKMYIxujHyMioyYzKbMtQzDTNG -M38zuDPxNCs0ZTSeNNg1EzVNNYc1wjX9Njc2cjauNuk3JDdgN5w31zgUOFA4jDjIOQU5Qjl/Obw5+To2 -OnQ6sjrvOy07azuqO+g8JzxlPKQ84z0iPWE9oT3gPiA+YD6gPuA/IT9hP6I/4kAjQGRApkDnQSlBakGs -Qe5CMEJyQrVC90M6Q31DwEQDREdEikTORRJFVUWaRd5GIkZnRqtG8Ec1R3tHwEgFSEtIkUjXSR1JY0mp -SfBKN0p9SsRLDEtTS5pL4kwqTHJMuk0CTUpNk03cTiVObk63TwBPSU+TT91QJ1BxULtRBlFQUZtR5lIx -UnxSx1MTU19TqlP2VEJUj1TbVShVdVXCVg9WXFapVvdXRFeSV+BYL1h9WMtZGllpWbhaB1pWWqZa9VtF -W5Vb5Vw1XIZc1l0nXXhdyV4aXmxevV8PX2Ffs2AFYFdgqmD8YU9homH1YklinGLwY0Njl2PrZEBklGTp -ZT1lkmXnZj1mkmboZz1nk2fpaD9olmjsaUNpmmnxakhqn2r3a09rp2v/bFdsr20IbWBtuW4SbmtuxG8e -b3hv0XArcIZw4HE6cZVx8HJLcqZzAXNdc7h0FHRwdMx1KHWFdeF2Pnabdvh3VnezeBF4bnjMeSp5iXnn -ekZ6pXsEe2N7wnwhfIF84X1BfaF+AX5ifsJ/I3+Ef+WAR4CogQqBa4HNgjCCkoL0g1eDuoQdhICE44VH -hauGDoZyhteHO4efiASIaYjOiTOJmYn+imSKyoswi5aL/IxjjMqNMY2Yjf+OZo7OjzaPnpAGkG6Q1pE/ -kaiSEZJ6kuOTTZO2lCCUipT0lV+VyZY0lp+XCpd1l+CYTJi4mSSZkJn8mmia1ZtCm6+cHJyJnPedZJ3S -nkCerp8dn4uf+qBpoNihR6G2oiailqMGo3aj5qRWpMelOKWpphqmi6b9p26n4KhSqMSpN6mpqhyqj6sC -q3Wr6axcrNCtRK24ri2uoa8Wr4uwALB1sOqxYLHWskuywrM4s660JbSctRO1irYBtnm28Ldot+C4WbjR -uUq5wro7urW7LrunvCG8m70VvY++Cr6Evv+/er/1wHDA7MFnwePCX8Lbw1jD1MRRxM7FS8XIxkbGw8dB -x7/IPci8yTrJuco4yrfLNsu2zDXMtc01zbXONs62zzfPuNA50LrRPNG+0j/SwdNE08bUSdTL1U7V0dZV -1tjXXNfg2GTY6Nls2fHadtr724DcBdyK3RDdlt4c3qLfKd+v4DbgveFE4cziU+Lb42Pj6+Rz5PzlhOYN -5pbnH+ep6DLovOlG6dDqW+rl63Dr++yG7RHtnO4o7rTvQO/M8Fjw5fFy8f/yjPMZ86f0NPTC9VD13vZt -9vv3ivgZ+Kj5OPnH+lf65/t3/Af8mP0p/br+S/7c/23//9IrLC0uWiRjbGFzc25hbWVYJGNsYXNzZXNf -EBBOU0JpdG1hcEltYWdlUmVwoy0vMFpOU0ltYWdlUmVwWE5TT2JqZWN00issMjNXTlNBcnJheaIyMNIr -LDU2Xk5TTXV0YWJsZUFycmF5ozUyMNM4OQ86OzxXTlNXaGl0ZVxOU0NvbG9yU3BhY2VEMCAwABADgAzS -Kyw+P1dOU0NvbG9yoj4w0issQUJXTlNJbWFnZaJBMAAIABEAGgAkACkAMgA3AEkATABRAFMAYgBoAHUA -fACLAJIAnwCmAK4AsACyALQAuQC7AL0AxADJANQA1gDYANoA3wDiAOQA5gDoAO8BBgEiASQBJhOWE5sT -phOvE8ITxhPRE9oT3xPnE+oT7xP+FAIUCRQRFB4UIxQlFCcULBQ0FDcUPBREAAAAAAAAAgEAAAAAAAAA -QwAAAAAAAAAAAAAAAAAAFEc +S2V5ZWRBcmNoaXZlctEICVRyb290gAGuCwwbHCEaJissMzY5P0JVJG51bGzXDQ4PEBESExQVFhcYGRpW +TlNTaXplViRjbGFzc1xOU0ltYWdlRmxhZ3NWTlNSZXBzW05TVGludENvbG9yV05TQ29sb3JeTlNSZXNp +emluZ01vZGWAAoANEiDDAACAA4AAgAsQAFZ7MSwgMX3SHQ4eIFpOUy5vYmplY3RzoR+ABIAK0h0OIiWi +IySABYAGgAnTDicoKSoaXxAUTlNUSUZGUmVwcmVzZW50YXRpb25fEBlOU0ludGVybmFsTGF5b3V0RGly +ZWN0aW9ugAiAB08REmxNTQAqAAAACgAAABABAAADAAAAAQABAAABAQADAAAAAQABAAABAgADAAAAAgAI +AAgBAwADAAAAAQABAAABBgADAAAAAQABAAABCgADAAAAAQABAAABEQAEAAAAAQAAAAgBEgADAAAAAQAB +AAABFQADAAAAAQACAAABFgADAAAAAQABAAABFwAEAAAAAQAAAAIBHAADAAAAAQABAAABKAADAAAAAQAC +AAABUgADAAAAAQABAAABUwADAAAAAgABAAGHcwAHAAARnAAAANAAAAAAAAARnGFwcGwCAAAAbW50ckdS +QVlYWVogB9wACAAXAA8ALgAPYWNzcEFQUEwAAAAAbm9uZQAAAAAAAAAAAAAAAAAAAAAAAPbWAAEAAAAA +0y1hcHBsAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFZGVzYwAA +AMAAAAB5ZHNjbQAAATwAAAgaY3BydAAACVgAAAAjd3RwdAAACXwAAAAUa1RSQwAACZAAAAgMZGVzYwAA +AAAAAAAfR2VuZXJpYyBHcmF5IEdhbW1hIDIuMiBQcm9maWxlAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAG1s +dWMAAAAAAAAAHwAAAAxza1NLAAAALgAAAYRkYURLAAAAOgAAAbJjYUVTAAAAOAAAAex2aVZOAAAAQAAA +AiRwdEJSAAAASgAAAmR1a1VBAAAALAAAAq5mckZVAAAAPgAAAtpodUhVAAAANAAAAxh6aFRXAAAAGgAA +A0xrb0tSAAAAIgAAA2ZuYk5PAAAAOgAAA4hjc0NaAAAAKAAAA8JoZUlMAAAAJAAAA+pyb1JPAAAAKgAA +BA5kZURFAAAATgAABDhpdElUAAAATgAABIZzdlNFAAAAOAAABNR6aENOAAAAGgAABQxqYUpQAAAAJgAA +BSZlbEdSAAAAKgAABUxwdFBPAAAAUgAABXZubE5MAAAAQAAABchlc0VTAAAATAAABgh0aFRIAAAAMgAA +BlR0clRSAAAAJAAABoZmaUZJAAAARgAABqpockhSAAAAPgAABvBwbFBMAAAASgAABy5hckVHAAAALAAA +B3hydVJVAAAAOgAAB6RlblVTAAAAPAAAB94AVgFhAGUAbwBiAGUAYwBuAOEAIABzAGkAdgDhACAAZwBh +AG0AYQAgADIALAAyAEcAZQBuAGUAcgBpAHMAawAgAGcAcgDlACAAMgAsADIAIABnAGEAbQBtAGEALQBw +AHIAbwBmAGkAbABHAGEAbQBtAGEAIABkAGUAIABnAHIAaQBzAG8AcwAgAGcAZQBuAOgAcgBpAGMAYQAg +ADIALgAyAEMepQB1ACAAaADsAG4AaAAgAE0A4AB1ACAAeADhAG0AIABDAGgAdQBuAGcAIABHAGEAbQBt +AGEAIAAyAC4AMgBQAGUAcgBmAGkAbAAgAEcAZQBuAOkAcgBpAGMAbwAgAGQAYQAgAEcAYQBtAGEAIABk +AGUAIABDAGkAbgB6AGEAcwAgADIALAAyBBcEMAQzBDAEOwRMBD0EMAAgAEcAcgBhAHkALQQzBDAEPAQw +ACAAMgAuADIAUAByAG8AZgBpAGwAIABnAOkAbgDpAHIAaQBxAHUAZQAgAGcAcgBpAHMAIABnAGEAbQBt +AGEAIAAyACwAMgDBAGwAdABhAGwA4QBuAG8AcwAgAHMAegD8AHIAawBlACAAZwBhAG0AbQBhACAAMgAu +ADKQGnUocHCWjlFJXqYAMgAuADKCcl9pY8+P8Md8vBgAINaMwMkAIKwQucgAIAAyAC4AMgAg1QS4XNMM +x3wARwBlAG4AZQByAGkAcwBrACAAZwByAOUAIABnAGEAbQBtAGEAIAAyACwAMgAtAHAAcgBvAGYAaQBs +AE8AYgBlAGMAbgDhACABYQBlAGQA4QAgAGcAYQBtAGEAIAAyAC4AMgXSBdAF3gXUACAF0AXkBdUF6AAg +BdsF3AXcBdkAIAAyAC4AMgBHAGEAbQBhACAAZwByAGkAIABnAGUAbgBlAHIAaQBjAQMAIAAyACwAMgBB +AGwAbABnAGUAbQBlAGkAbgBlAHMAIABHAHIAYQB1AHMAdAB1AGYAZQBuAC0AUAByAG8AZgBpAGwAIABH +AGEAbQBtAGEAIAAyACwAMgBQAHIAbwBmAGkAbABvACAAZwByAGkAZwBpAG8AIABnAGUAbgBlAHIAaQBj +AG8AIABkAGUAbABsAGEAIABnAGEAbQBtAGEAIAAyACwAMgBHAGUAbgBlAHIAaQBzAGsAIABnAHIA5QAg +ADIALAAyACAAZwBhAG0AbQBhAHAAcgBvAGYAaQBsZm6QGnBwXqZ8+2VwADIALgAyY8+P8GWHTvZOAIIs +MLAw7DCkMKww8zDeACAAMgAuADIAIDDXMO0w1TChMKQw6wOTA7UDvQO5A7oDzAAgA5MDugPBA7kAIAOT +A6wDvAO8A7EAIAAyAC4AMgBQAGUAcgBmAGkAbAAgAGcAZQBuAOkAcgBpAGMAbwAgAGQAZQAgAGMAaQBu +AHoAZQBuAHQAbwBzACAAZABhACAARwBhAG0AbQBhACAAMgAsADIAQQBsAGcAZQBtAGUAZQBuACAAZwBy +AGkAagBzACAAZwBhAG0AbQBhACAAMgAsADIALQBwAHIAbwBmAGkAZQBsAFAAZQByAGYAaQBsACAAZwBl +AG4A6QByAGkAYwBvACAAZABlACAAZwBhAG0AbQBhACAAZABlACAAZwByAGkAcwBlAHMAIAAyACwAMg4j +DjEOBw4qDjUOQQ4BDiEOIQ4yDkAOAQ4jDiIOTA4XDjEOSA4nDkQOGwAgADIALgAyAEcAZQBuAGUAbAAg +AEcAcgBpACAARwBhAG0AYQAgADIALAAyAFkAbABlAGkAbgBlAG4AIABoAGEAcgBtAGEAYQBuACAAZwBh +AG0AbQBhACAAMgAsADIAIAAtAHAAcgBvAGYAaQBpAGwAaQBHAGUAbgBlAHIAaQENAGsAaQAgAEcAcgBh +AHkAIABHAGEAbQBtAGEAIAAyAC4AMgAgAHAAcgBvAGYAaQBsAFUAbgBpAHcAZQByAHMAYQBsAG4AeQAg +AHAAcgBvAGYAaQBsACAAcwB6AGEAcgBvAVsAYwBpACAAZwBhAG0AbQBhACAAMgAsADIGOgYnBkUGJwAg +ADIALgAyACAGRAZIBkYAIAYxBkUGJwYvBkoAIAY5BicGRQQeBDEESQQwBE8AIARBBDUEQAQwBE8AIAQz +BDAEPAQ8BDAAIAAyACwAMgAtBD8EQAQ+BEQEOAQ7BEwARwBlAG4AZQByAGkAYwAgAEcAcgBhAHkAIABH +AGEAbQBtAGEAIAAyAC4AMgAgAFAAcgBvAGYAaQBsAGUAAHRleHQAAAAAQ29weXJpZ2h0IEFwcGxlIElu +Yy4sIDIwMTIAAFhZWiAAAAAAAADzUQABAAAAARbMY3VydgAAAAAAAAQAAAAABQAKAA8AFAAZAB4AIwAo +AC0AMgA3ADsAQABFAEoATwBUAFkAXgBjAGgAbQByAHcAfACBAIYAiwCQAJUAmgCfAKQAqQCuALIAtwC8 +AMEAxgDLANAA1QDbAOAA5QDrAPAA9gD7AQEBBwENARMBGQEfASUBKwEyATgBPgFFAUwBUgFZAWABZwFu +AXUBfAGDAYsBkgGaAaEBqQGxAbkBwQHJAdEB2QHhAekB8gH6AgMCDAIUAh0CJgIvAjgCQQJLAlQCXQJn +AnECegKEAo4CmAKiAqwCtgLBAssC1QLgAusC9QMAAwsDFgMhAy0DOANDA08DWgNmA3IDfgOKA5YDogOu +A7oDxwPTA+AD7AP5BAYEEwQgBC0EOwRIBFUEYwRxBH4EjASaBKgEtgTEBNME4QTwBP4FDQUcBSsFOgVJ +BVgFZwV3BYYFlgWmBbUFxQXVBeUF9gYGBhYGJwY3BkgGWQZqBnsGjAadBq8GwAbRBuMG9QcHBxkHKwc9 +B08HYQd0B4YHmQesB78H0gflB/gICwgfCDIIRghaCG4IggiWCKoIvgjSCOcI+wkQCSUJOglPCWQJeQmP +CaQJugnPCeUJ+woRCicKPQpUCmoKgQqYCq4KxQrcCvMLCwsiCzkLUQtpC4ALmAuwC8gL4Qv5DBIMKgxD +DFwMdQyODKcMwAzZDPMNDQ0mDUANWg10DY4NqQ3DDd4N+A4TDi4OSQ5kDn8Omw62DtIO7g8JDyUPQQ9e +D3oPlg+zD88P7BAJECYQQxBhEH4QmxC5ENcQ9RETETERTxFtEYwRqhHJEegSBxImEkUSZBKEEqMSwxLj +EwMTIxNDE2MTgxOkE8UT5RQGFCcUSRRqFIsUrRTOFPAVEhU0FVYVeBWbFb0V4BYDFiYWSRZsFo8WshbW +FvoXHRdBF2UXiReuF9IX9xgbGEAYZRiKGK8Y1Rj6GSAZRRlrGZEZtxndGgQaKhpRGncanhrFGuwbFBs7 +G2MbihuyG9ocAhwqHFIcexyjHMwc9R0eHUcdcB2ZHcMd7B4WHkAeah6UHr4e6R8THz4faR+UH78f6iAV +IEEgbCCYIMQg8CEcIUghdSGhIc4h+yInIlUigiKvIt0jCiM4I2YjlCPCI/AkHyRNJHwkqyTaJQklOCVo +JZclxyX3JicmVyaHJrcm6CcYJ0kneierJ9woDSg/KHEooijUKQYpOClrKZ0p0CoCKjUqaCqbKs8rAis2 +K2krnSvRLAUsOSxuLKIs1y0MLUEtdi2rLeEuFi5MLoIuty7uLyQvWi+RL8cv/jA1MGwwpDDbMRIxSjGC +Mbox8jIqMmMymzLUMw0zRjN/M7gz8TQrNGU0njTYNRM1TTWHNcI1/TY3NnI2rjbpNyQ3YDecN9c4FDhQ +OIw4yDkFOUI5fzm8Ofk6Njp0OrI67zstO2s7qjvoPCc8ZTykPOM9Ij1hPaE94D4gPmA+oD7gPyE/YT+i +P+JAI0BkQKZA50EpQWpBrEHuQjBCckK1QvdDOkN9Q8BEA0RHRIpEzkUSRVVFmkXeRiJGZ0arRvBHNUd7 +R8BIBUhLSJFI10kdSWNJqUnwSjdKfUrESwxLU0uaS+JMKkxyTLpNAk1KTZNN3E4lTm5Ot08AT0lPk0/d +UCdQcVC7UQZRUFGbUeZSMVJ8UsdTE1NfU6pT9lRCVI9U21UoVXVVwlYPVlxWqVb3V0RXklfgWC9YfVjL +WRpZaVm4WgdaVlqmWvVbRVuVW+VcNVyGXNZdJ114XcleGl5sXr1fD19hX7NgBWBXYKpg/GFPYaJh9WJJ +Ypxi8GNDY5dj62RAZJRk6WU9ZZJl52Y9ZpJm6Gc9Z5Nn6Wg/aJZo7GlDaZpp8WpIap9q92tPa6dr/2xX +bK9tCG1gbbluEm5rbsRvHm94b9FwK3CGcOBxOnGVcfByS3KmcwFzXXO4dBR0cHTMdSh1hXXhdj52m3b4 +d1Z3s3gReG54zHkqeYl553pGeqV7BHtje8J8IXyBfOF9QX2hfgF+Yn7CfyN/hH/lgEeAqIEKgWuBzYIw +gpKC9INXg7qEHYSAhOOFR4Wrhg6GcobXhzuHn4gEiGmIzokziZmJ/opkisqLMIuWi/yMY4zKjTGNmI3/ +jmaOzo82j56QBpBukNaRP5GokhGSepLjk02TtpQglIqU9JVflcmWNJaflwqXdZfgmEyYuJkkmZCZ/Jpo +mtWbQpuvnByciZz3nWSd0p5Anq6fHZ+Ln/qgaaDYoUehtqImopajBqN2o+akVqTHpTilqaYapoum/adu +p+CoUqjEqTepqaocqo+rAqt1q+msXKzQrUStuK4trqGvFq+LsACwdbDqsWCx1rJLssKzOLOutCW0nLUT +tYq2AbZ5tvC3aLfguFm40blKucK6O7q1uy67p7whvJu9Fb2Pvgq+hL7/v3q/9cBwwOzBZ8Hjwl/C28NY +w9TEUcTOxUvFyMZGxsPHQce/yD3IvMk6ybnKOMq3yzbLtsw1zLXNNc21zjbOts83z7jQOdC60TzRvtI/ +0sHTRNPG1EnUy9VO1dHWVdbY11zX4Nhk2OjZbNnx2nba+9uA3AXcit0Q3ZbeHN6i3ynfr+A24L3hROHM +4lPi2+Nj4+vkc+T85YTmDeaW5x/nqegy6LzpRunQ6lvq5etw6/vshu0R7ZzuKO6070DvzPBY8OXxcvH/ +8ozzGfOn9DT0wvVQ9d72bfb794r4Gfio+Tj5x/pX+uf7d/wH/Jj9Kf26/kv+3P9t///SLS4vMFokY2xh +c3NuYW1lWCRjbGFzc2VzXxAQTlNCaXRtYXBJbWFnZVJlcKMvMTJaTlNJbWFnZVJlcFhOU09iamVjdNIt +LjQ1V05TQXJyYXmiNDLSLS43OF5OU011dGFibGVBcnJheaM3NDLTOjsOPD0+V05TV2hpdGVcTlNDb2xv +clNwYWNlRDAgMAAQA4AM0i0uQEFXTlNDb2xvcqJAMtItLkNEV05TSW1hZ2WiQzIACAARABoAJAApADIA +NwBJAEwAUQBTAGIAaAB3AH4AhQCSAJkApQCtALwAvgDAAMUAxwDJAMsAzQDUANkA5ADmAOgA6gDvAPIA +9AD2APgA/wEWATIBNAE2E6YTqxO2E78T0hPWE+ET6hPvE/cT+hP/FA4UEhQZFCEULhQzFDUUNxQ8FEQU +RxRMFFQAAAAAAAACAQAAAAAAAABFAAAAAAAAAAAAAAAAAAAUVw diff --git a/i18n/src/main/resources/nl.lproj/Browser.xib b/i18n/src/main/resources/nl.lproj/Browser.xib index 7061a57487..cbe76524ce 100644 --- a/i18n/src/main/resources/nl.lproj/Browser.xib +++ b/i18n/src/main/resources/nl.lproj/Browser.xib @@ -1,7 +1,7 @@ - + - + @@ -38,7 +38,7 @@ - + @@ -62,27 +62,27 @@ @@ -256,7 +256,7 @@ - + @@ -553,95 +553,95 @@ DQ YnBsaXN0MDDUAQIDBAUGBwpYJHZlcnNpb25ZJGFyY2hpdmVyVCR0b3BYJG9iamVjdHMSAAGGoF8QD05T -S2V5ZWRBcmNoaXZlctEICVRyb290gAGuCwwZGh8UJCkqMTQ3PUBVJG51bGzWDQ4PEBESExQVFhcYVk5T -U2l6ZV5OU1Jlc2l6aW5nTW9kZVYkY2xhc3NcTlNJbWFnZUZsYWdzVk5TUmVwc1dOU0NvbG9ygAIQAIAN -EiDDAACAA4ALVnsxLCAxfdIbDxweWk5TLm9iamVjdHOhHYAEgArSGw8gI6IhIoAFgAaACdMPJSYnKBRf -EBROU1RJRkZSZXByZXNlbnRhdGlvbl8QGU5TSW50ZXJuYWxMYXlvdXREaXJlY3Rpb26ACIAHTxESbE1N -ACoAAAAKAAAAEAEAAAMAAAABAAEAAAEBAAMAAAABAAEAAAECAAMAAAACAAgACAEDAAMAAAABAAEAAAEG -AAMAAAABAAEAAAEKAAMAAAABAAEAAAERAAQAAAABAAAACAESAAMAAAABAAEAAAEVAAMAAAABAAIAAAEW -AAMAAAABAAEAAAEXAAQAAAABAAAAAgEcAAMAAAABAAEAAAEoAAMAAAABAAIAAAFSAAMAAAABAAEAAAFT -AAMAAAACAAEAAYdzAAcAABGcAAAA0AAAAAAAABGcYXBwbAIAAABtbnRyR1JBWVhZWiAH3AAIABcADwAu -AA9hY3NwQVBQTAAAAABub25lAAAAAAAAAAAAAAAAAAAAAAAA9tYAAQAAAADTLWFwcGwAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAVkZXNjAAAAwAAAAHlkc2NtAAABPAAA -CBpjcHJ0AAAJWAAAACN3dHB0AAAJfAAAABRrVFJDAAAJkAAACAxkZXNjAAAAAAAAAB9HZW5lcmljIEdy -YXkgR2FtbWEgMi4yIFByb2ZpbGUAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAbWx1YwAAAAAAAAAfAAAADHNr -U0sAAAAuAAABhGRhREsAAAA6AAABsmNhRVMAAAA4AAAB7HZpVk4AAABAAAACJHB0QlIAAABKAAACZHVr -VUEAAAAsAAACrmZyRlUAAAA+AAAC2mh1SFUAAAA0AAADGHpoVFcAAAAaAAADTGtvS1IAAAAiAAADZm5i -Tk8AAAA6AAADiGNzQ1oAAAAoAAADwmhlSUwAAAAkAAAD6nJvUk8AAAAqAAAEDmRlREUAAABOAAAEOGl0 -SVQAAABOAAAEhnN2U0UAAAA4AAAE1HpoQ04AAAAaAAAFDGphSlAAAAAmAAAFJmVsR1IAAAAqAAAFTHB0 -UE8AAABSAAAFdm5sTkwAAABAAAAFyGVzRVMAAABMAAAGCHRoVEgAAAAyAAAGVHRyVFIAAAAkAAAGhmZp -RkkAAABGAAAGqmhySFIAAAA+AAAG8HBsUEwAAABKAAAHLmFyRUcAAAAsAAAHeHJ1UlUAAAA6AAAHpGVu -VVMAAAA8AAAH3gBWAWEAZQBvAGIAZQBjAG4A4QAgAHMAaQB2AOEAIABnAGEAbQBhACAAMgAsADIARwBl -AG4AZQByAGkAcwBrACAAZwByAOUAIAAyACwAMgAgAGcAYQBtAG0AYQAtAHAAcgBvAGYAaQBsAEcAYQBt -AG0AYQAgAGQAZQAgAGcAcgBpAHMAbwBzACAAZwBlAG4A6AByAGkAYwBhACAAMgAuADIAQx6lAHUAIABo -AOwAbgBoACAATQDgAHUAIAB4AOEAbQAgAEMAaAB1AG4AZwAgAEcAYQBtAG0AYQAgADIALgAyAFAAZQBy -AGYAaQBsACAARwBlAG4A6QByAGkAYwBvACAAZABhACAARwBhAG0AYQAgAGQAZQAgAEMAaQBuAHoAYQBz -ACAAMgAsADIEFwQwBDMEMAQ7BEwEPQQwACAARwByAGEAeQAtBDMEMAQ8BDAAIAAyAC4AMgBQAHIAbwBm -AGkAbAAgAGcA6QBuAOkAcgBpAHEAdQBlACAAZwByAGkAcwAgAGcAYQBtAG0AYQAgADIALAAyAMEAbAB0 -AGEAbADhAG4AbwBzACAAcwB6APwAcgBrAGUAIABnAGEAbQBtAGEAIAAyAC4AMpAadShwcJaOUUlepgAy -AC4AMoJyX2ljz4/wx3y8GAAg1ozAyQAgrBC5yAAgADIALgAyACDVBLhc0wzHfABHAGUAbgBlAHIAaQBz -AGsAIABnAHIA5QAgAGcAYQBtAG0AYQAgADIALAAyAC0AcAByAG8AZgBpAGwATwBiAGUAYwBuAOEAIAFh -AGUAZADhACAAZwBhAG0AYQAgADIALgAyBdIF0AXeBdQAIAXQBeQF1QXoACAF2wXcBdwF2QAgADIALgAy -AEcAYQBtAGEAIABnAHIAaQAgAGcAZQBuAGUAcgBpAGMBAwAgADIALAAyAEEAbABsAGcAZQBtAGUAaQBu -AGUAcwAgAEcAcgBhAHUAcwB0AHUAZgBlAG4ALQBQAHIAbwBmAGkAbAAgAEcAYQBtAG0AYQAgADIALAAy -AFAAcgBvAGYAaQBsAG8AIABnAHIAaQBnAGkAbwAgAGcAZQBuAGUAcgBpAGMAbwAgAGQAZQBsAGwAYQAg -AGcAYQBtAG0AYQAgADIALAAyAEcAZQBuAGUAcgBpAHMAawAgAGcAcgDlACAAMgAsADIAIABnAGEAbQBt -AGEAcAByAG8AZgBpAGxmbpAacHBepnz7ZXAAMgAuADJjz4/wZYdO9k4AgiwwsDDsMKQwrDDzMN4AIAAy -AC4AMgAgMNcw7TDVMKEwpDDrA5MDtQO9A7kDugPMACADkwO6A8EDuQAgA5MDrAO8A7wDsQAgADIALgAy -AFAAZQByAGYAaQBsACAAZwBlAG4A6QByAGkAYwBvACAAZABlACAAYwBpAG4AegBlAG4AdABvAHMAIABk -AGEAIABHAGEAbQBtAGEAIAAyACwAMgBBAGwAZwBlAG0AZQBlAG4AIABnAHIAaQBqAHMAIABnAGEAbQBt -AGEAIAAyACwAMgAtAHAAcgBvAGYAaQBlAGwAUABlAHIAZgBpAGwAIABnAGUAbgDpAHIAaQBjAG8AIABk -AGUAIABnAGEAbQBtAGEAIABkAGUAIABnAHIAaQBzAGUAcwAgADIALAAyDiMOMQ4HDioONQ5BDgEOIQ4h -DjIOQA4BDiMOIg5MDhcOMQ5IDicORA4bACAAMgAuADIARwBlAG4AZQBsACAARwByAGkAIABHAGEAbQBh -ACAAMgAsADIAWQBsAGUAaQBuAGUAbgAgAGgAYQByAG0AYQBhAG4AIABnAGEAbQBtAGEAIAAyACwAMgAg -AC0AcAByAG8AZgBpAGkAbABpAEcAZQBuAGUAcgBpAQ0AawBpACAARwByAGEAeQAgAEcAYQBtAG0AYQAg -ADIALgAyACAAcAByAG8AZgBpAGwAVQBuAGkAdwBlAHIAcwBhAGwAbgB5ACAAcAByAG8AZgBpAGwAIABz -AHoAYQByAG8BWwBjAGkAIABnAGEAbQBtAGEAIAAyACwAMgY6BicGRQYnACAAMgAuADIAIAZEBkgGRgAg -BjEGRQYnBi8GSgAgBjkGJwZFBB4EMQRJBDAETwAgBEEENQRABDAETwAgBDMEMAQ8BDwEMAAgADIALAAy -AC0EPwRABD4ERAQ4BDsETABHAGUAbgBlAHIAaQBjACAARwByAGEAeQAgAEcAYQBtAG0AYQAgADIALgAy -ACAAUAByAG8AZgBpAGwAZQAAdGV4dAAAAABDb3B5cmlnaHQgQXBwbGUgSW5jLiwgMjAxMgAAWFlaIAAA -AAAAAPNRAAEAAAABFsxjdXJ2AAAAAAAABAAAAAAFAAoADwAUABkAHgAjACgALQAyADcAOwBAAEUASgBP -AFQAWQBeAGMAaABtAHIAdwB8AIEAhgCLAJAAlQCaAJ8ApACpAK4AsgC3ALwAwQDGAMsA0ADVANsA4ADl -AOsA8AD2APsBAQEHAQ0BEwEZAR8BJQErATIBOAE+AUUBTAFSAVkBYAFnAW4BdQF8AYMBiwGSAZoBoQGp -AbEBuQHBAckB0QHZAeEB6QHyAfoCAwIMAhQCHQImAi8COAJBAksCVAJdAmcCcQJ6AoQCjgKYAqICrAK2 -AsECywLVAuAC6wL1AwADCwMWAyEDLQM4A0MDTwNaA2YDcgN+A4oDlgOiA64DugPHA9MD4APsA/kEBgQT -BCAELQQ7BEgEVQRjBHEEfgSMBJoEqAS2BMQE0wThBPAE/gUNBRwFKwU6BUkFWAVnBXcFhgWWBaYFtQXF -BdUF5QX2BgYGFgYnBjcGSAZZBmoGewaMBp0GrwbABtEG4wb1BwcHGQcrBz0HTwdhB3QHhgeZB6wHvwfS -B+UH+AgLCB8IMghGCFoIbgiCCJYIqgi+CNII5wj7CRAJJQk6CU8JZAl5CY8JpAm6Cc8J5Qn7ChEKJwo9 -ClQKagqBCpgKrgrFCtwK8wsLCyILOQtRC2kLgAuYC7ALyAvhC/kMEgwqDEMMXAx1DI4MpwzADNkM8w0N -DSYNQA1aDXQNjg2pDcMN3g34DhMOLg5JDmQOfw6bDrYO0g7uDwkPJQ9BD14Peg+WD7MPzw/sEAkQJhBD -EGEQfhCbELkQ1xD1ERMRMRFPEW0RjBGqEckR6BIHEiYSRRJkEoQSoxLDEuMTAxMjE0MTYxODE6QTxRPl -FAYUJxRJFGoUixStFM4U8BUSFTQVVhV4FZsVvRXgFgMWJhZJFmwWjxayFtYW+hcdF0EXZReJF64X0hf3 -GBsYQBhlGIoYrxjVGPoZIBlFGWsZkRm3Gd0aBBoqGlEadxqeGsUa7BsUGzsbYxuKG7Ib2hwCHCocUhx7 -HKMczBz1HR4dRx1wHZkdwx3sHhYeQB5qHpQevh7pHxMfPh9pH5Qfvx/qIBUgQSBsIJggxCDwIRwhSCF1 -IaEhziH7IiciVSKCIq8i3SMKIzgjZiOUI8Ij8CQfJE0kfCSrJNolCSU4JWgllyXHJfcmJyZXJocmtybo -JxgnSSd6J6sn3CgNKD8ocSiiKNQpBik4KWspnSnQKgIqNSpoKpsqzysCKzYraSudK9EsBSw5LG4soizX -LQwtQS12Last4S4WLkwugi63Lu4vJC9aL5Evxy/+MDUwbDCkMNsxEjFKMYIxujHyMioyYzKbMtQzDTNG -M38zuDPxNCs0ZTSeNNg1EzVNNYc1wjX9Njc2cjauNuk3JDdgN5w31zgUOFA4jDjIOQU5Qjl/Obw5+To2 -OnQ6sjrvOy07azuqO+g8JzxlPKQ84z0iPWE9oT3gPiA+YD6gPuA/IT9hP6I/4kAjQGRApkDnQSlBakGs -Qe5CMEJyQrVC90M6Q31DwEQDREdEikTORRJFVUWaRd5GIkZnRqtG8Ec1R3tHwEgFSEtIkUjXSR1JY0mp -SfBKN0p9SsRLDEtTS5pL4kwqTHJMuk0CTUpNk03cTiVObk63TwBPSU+TT91QJ1BxULtRBlFQUZtR5lIx -UnxSx1MTU19TqlP2VEJUj1TbVShVdVXCVg9WXFapVvdXRFeSV+BYL1h9WMtZGllpWbhaB1pWWqZa9VtF -W5Vb5Vw1XIZc1l0nXXhdyV4aXmxevV8PX2Ffs2AFYFdgqmD8YU9homH1YklinGLwY0Njl2PrZEBklGTp -ZT1lkmXnZj1mkmboZz1nk2fpaD9olmjsaUNpmmnxakhqn2r3a09rp2v/bFdsr20IbWBtuW4SbmtuxG8e -b3hv0XArcIZw4HE6cZVx8HJLcqZzAXNdc7h0FHRwdMx1KHWFdeF2Pnabdvh3VnezeBF4bnjMeSp5iXnn -ekZ6pXsEe2N7wnwhfIF84X1BfaF+AX5ifsJ/I3+Ef+WAR4CogQqBa4HNgjCCkoL0g1eDuoQdhICE44VH -hauGDoZyhteHO4efiASIaYjOiTOJmYn+imSKyoswi5aL/IxjjMqNMY2Yjf+OZo7OjzaPnpAGkG6Q1pE/ -kaiSEZJ6kuOTTZO2lCCUipT0lV+VyZY0lp+XCpd1l+CYTJi4mSSZkJn8mmia1ZtCm6+cHJyJnPedZJ3S -nkCerp8dn4uf+qBpoNihR6G2oiailqMGo3aj5qRWpMelOKWpphqmi6b9p26n4KhSqMSpN6mpqhyqj6sC -q3Wr6axcrNCtRK24ri2uoa8Wr4uwALB1sOqxYLHWskuywrM4s660JbSctRO1irYBtnm28Ldot+C4WbjR -uUq5wro7urW7LrunvCG8m70VvY++Cr6Evv+/er/1wHDA7MFnwePCX8Lbw1jD1MRRxM7FS8XIxkbGw8dB -x7/IPci8yTrJuco4yrfLNsu2zDXMtc01zbXONs62zzfPuNA50LrRPNG+0j/SwdNE08bUSdTL1U7V0dZV -1tjXXNfg2GTY6Nls2fHadtr724DcBdyK3RDdlt4c3qLfKd+v4DbgveFE4cziU+Lb42Pj6+Rz5PzlhOYN -5pbnH+ep6DLovOlG6dDqW+rl63Dr++yG7RHtnO4o7rTvQO/M8Fjw5fFy8f/yjPMZ86f0NPTC9VD13vZt -9vv3ivgZ+Kj5OPnH+lf65/t3/Af8mP0p/br+S/7c/23//9IrLC0uWiRjbGFzc25hbWVYJGNsYXNzZXNf -EBBOU0JpdG1hcEltYWdlUmVwoy0vMFpOU0ltYWdlUmVwWE5TT2JqZWN00issMjNXTlNBcnJheaIyMNIr -LDU2Xk5TTXV0YWJsZUFycmF5ozUyMNM4OQ86OzxXTlNXaGl0ZVxOU0NvbG9yU3BhY2VEMCAwABADgAzS -Kyw+P1dOU0NvbG9yoj4w0issQUJXTlNJbWFnZaJBMAAIABEAGgAkACkAMgA3AEkATABRAFMAYgBoAHUA -fACLAJIAnwCmAK4AsACyALQAuQC7AL0AxADJANQA1gDYANoA3wDiAOQA5gDoAO8BBgEiASQBJhOWE5sT -phOvE8ITxhPRE9oT3xPnE+oT7xP+FAIUCRQRFB4UIxQlFCcULBQ0FDcUPBREAAAAAAAAAgEAAAAAAAAA -QwAAAAAAAAAAAAAAAAAAFEc +S2V5ZWRBcmNoaXZlctEICVRyb290gAGuCwwbHCEaJissMzY5P0JVJG51bGzXDQ4PEBESExQVFhcYGRpW +TlNTaXplViRjbGFzc1xOU0ltYWdlRmxhZ3NWTlNSZXBzW05TVGludENvbG9yV05TQ29sb3JeTlNSZXNp +emluZ01vZGWAAoANEiDDAACAA4AAgAsQAFZ7MSwgMX3SHQ4eIFpOUy5vYmplY3RzoR+ABIAK0h0OIiWi +IySABYAGgAnTDicoKSoaXxAUTlNUSUZGUmVwcmVzZW50YXRpb25fEBlOU0ludGVybmFsTGF5b3V0RGly +ZWN0aW9ugAiAB08REmxNTQAqAAAACgAAABABAAADAAAAAQABAAABAQADAAAAAQABAAABAgADAAAAAgAI +AAgBAwADAAAAAQABAAABBgADAAAAAQABAAABCgADAAAAAQABAAABEQAEAAAAAQAAAAgBEgADAAAAAQAB +AAABFQADAAAAAQACAAABFgADAAAAAQABAAABFwAEAAAAAQAAAAIBHAADAAAAAQABAAABKAADAAAAAQAC +AAABUgADAAAAAQABAAABUwADAAAAAgABAAGHcwAHAAARnAAAANAAAAAAAAARnGFwcGwCAAAAbW50ckdS +QVlYWVogB9wACAAXAA8ALgAPYWNzcEFQUEwAAAAAbm9uZQAAAAAAAAAAAAAAAAAAAAAAAPbWAAEAAAAA +0y1hcHBsAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFZGVzYwAA +AMAAAAB5ZHNjbQAAATwAAAgaY3BydAAACVgAAAAjd3RwdAAACXwAAAAUa1RSQwAACZAAAAgMZGVzYwAA +AAAAAAAfR2VuZXJpYyBHcmF5IEdhbW1hIDIuMiBQcm9maWxlAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAG1s +dWMAAAAAAAAAHwAAAAxza1NLAAAALgAAAYRkYURLAAAAOgAAAbJjYUVTAAAAOAAAAex2aVZOAAAAQAAA +AiRwdEJSAAAASgAAAmR1a1VBAAAALAAAAq5mckZVAAAAPgAAAtpodUhVAAAANAAAAxh6aFRXAAAAGgAA +A0xrb0tSAAAAIgAAA2ZuYk5PAAAAOgAAA4hjc0NaAAAAKAAAA8JoZUlMAAAAJAAAA+pyb1JPAAAAKgAA +BA5kZURFAAAATgAABDhpdElUAAAATgAABIZzdlNFAAAAOAAABNR6aENOAAAAGgAABQxqYUpQAAAAJgAA +BSZlbEdSAAAAKgAABUxwdFBPAAAAUgAABXZubE5MAAAAQAAABchlc0VTAAAATAAABgh0aFRIAAAAMgAA +BlR0clRSAAAAJAAABoZmaUZJAAAARgAABqpockhSAAAAPgAABvBwbFBMAAAASgAABy5hckVHAAAALAAA +B3hydVJVAAAAOgAAB6RlblVTAAAAPAAAB94AVgFhAGUAbwBiAGUAYwBuAOEAIABzAGkAdgDhACAAZwBh +AG0AYQAgADIALAAyAEcAZQBuAGUAcgBpAHMAawAgAGcAcgDlACAAMgAsADIAIABnAGEAbQBtAGEALQBw +AHIAbwBmAGkAbABHAGEAbQBtAGEAIABkAGUAIABnAHIAaQBzAG8AcwAgAGcAZQBuAOgAcgBpAGMAYQAg +ADIALgAyAEMepQB1ACAAaADsAG4AaAAgAE0A4AB1ACAAeADhAG0AIABDAGgAdQBuAGcAIABHAGEAbQBt +AGEAIAAyAC4AMgBQAGUAcgBmAGkAbAAgAEcAZQBuAOkAcgBpAGMAbwAgAGQAYQAgAEcAYQBtAGEAIABk +AGUAIABDAGkAbgB6AGEAcwAgADIALAAyBBcEMAQzBDAEOwRMBD0EMAAgAEcAcgBhAHkALQQzBDAEPAQw +ACAAMgAuADIAUAByAG8AZgBpAGwAIABnAOkAbgDpAHIAaQBxAHUAZQAgAGcAcgBpAHMAIABnAGEAbQBt +AGEAIAAyACwAMgDBAGwAdABhAGwA4QBuAG8AcwAgAHMAegD8AHIAawBlACAAZwBhAG0AbQBhACAAMgAu +ADKQGnUocHCWjlFJXqYAMgAuADKCcl9pY8+P8Md8vBgAINaMwMkAIKwQucgAIAAyAC4AMgAg1QS4XNMM +x3wARwBlAG4AZQByAGkAcwBrACAAZwByAOUAIABnAGEAbQBtAGEAIAAyACwAMgAtAHAAcgBvAGYAaQBs +AE8AYgBlAGMAbgDhACABYQBlAGQA4QAgAGcAYQBtAGEAIAAyAC4AMgXSBdAF3gXUACAF0AXkBdUF6AAg +BdsF3AXcBdkAIAAyAC4AMgBHAGEAbQBhACAAZwByAGkAIABnAGUAbgBlAHIAaQBjAQMAIAAyACwAMgBB +AGwAbABnAGUAbQBlAGkAbgBlAHMAIABHAHIAYQB1AHMAdAB1AGYAZQBuAC0AUAByAG8AZgBpAGwAIABH +AGEAbQBtAGEAIAAyACwAMgBQAHIAbwBmAGkAbABvACAAZwByAGkAZwBpAG8AIABnAGUAbgBlAHIAaQBj +AG8AIABkAGUAbABsAGEAIABnAGEAbQBtAGEAIAAyACwAMgBHAGUAbgBlAHIAaQBzAGsAIABnAHIA5QAg +ADIALAAyACAAZwBhAG0AbQBhAHAAcgBvAGYAaQBsZm6QGnBwXqZ8+2VwADIALgAyY8+P8GWHTvZOAIIs +MLAw7DCkMKww8zDeACAAMgAuADIAIDDXMO0w1TChMKQw6wOTA7UDvQO5A7oDzAAgA5MDugPBA7kAIAOT +A6wDvAO8A7EAIAAyAC4AMgBQAGUAcgBmAGkAbAAgAGcAZQBuAOkAcgBpAGMAbwAgAGQAZQAgAGMAaQBu +AHoAZQBuAHQAbwBzACAAZABhACAARwBhAG0AbQBhACAAMgAsADIAQQBsAGcAZQBtAGUAZQBuACAAZwBy +AGkAagBzACAAZwBhAG0AbQBhACAAMgAsADIALQBwAHIAbwBmAGkAZQBsAFAAZQByAGYAaQBsACAAZwBl +AG4A6QByAGkAYwBvACAAZABlACAAZwBhAG0AbQBhACAAZABlACAAZwByAGkAcwBlAHMAIAAyACwAMg4j +DjEOBw4qDjUOQQ4BDiEOIQ4yDkAOAQ4jDiIOTA4XDjEOSA4nDkQOGwAgADIALgAyAEcAZQBuAGUAbAAg +AEcAcgBpACAARwBhAG0AYQAgADIALAAyAFkAbABlAGkAbgBlAG4AIABoAGEAcgBtAGEAYQBuACAAZwBh +AG0AbQBhACAAMgAsADIAIAAtAHAAcgBvAGYAaQBpAGwAaQBHAGUAbgBlAHIAaQENAGsAaQAgAEcAcgBh +AHkAIABHAGEAbQBtAGEAIAAyAC4AMgAgAHAAcgBvAGYAaQBsAFUAbgBpAHcAZQByAHMAYQBsAG4AeQAg +AHAAcgBvAGYAaQBsACAAcwB6AGEAcgBvAVsAYwBpACAAZwBhAG0AbQBhACAAMgAsADIGOgYnBkUGJwAg +ADIALgAyACAGRAZIBkYAIAYxBkUGJwYvBkoAIAY5BicGRQQeBDEESQQwBE8AIARBBDUEQAQwBE8AIAQz +BDAEPAQ8BDAAIAAyACwAMgAtBD8EQAQ+BEQEOAQ7BEwARwBlAG4AZQByAGkAYwAgAEcAcgBhAHkAIABH +AGEAbQBtAGEAIAAyAC4AMgAgAFAAcgBvAGYAaQBsAGUAAHRleHQAAAAAQ29weXJpZ2h0IEFwcGxlIElu +Yy4sIDIwMTIAAFhZWiAAAAAAAADzUQABAAAAARbMY3VydgAAAAAAAAQAAAAABQAKAA8AFAAZAB4AIwAo +AC0AMgA3ADsAQABFAEoATwBUAFkAXgBjAGgAbQByAHcAfACBAIYAiwCQAJUAmgCfAKQAqQCuALIAtwC8 +AMEAxgDLANAA1QDbAOAA5QDrAPAA9gD7AQEBBwENARMBGQEfASUBKwEyATgBPgFFAUwBUgFZAWABZwFu +AXUBfAGDAYsBkgGaAaEBqQGxAbkBwQHJAdEB2QHhAekB8gH6AgMCDAIUAh0CJgIvAjgCQQJLAlQCXQJn +AnECegKEAo4CmAKiAqwCtgLBAssC1QLgAusC9QMAAwsDFgMhAy0DOANDA08DWgNmA3IDfgOKA5YDogOu +A7oDxwPTA+AD7AP5BAYEEwQgBC0EOwRIBFUEYwRxBH4EjASaBKgEtgTEBNME4QTwBP4FDQUcBSsFOgVJ +BVgFZwV3BYYFlgWmBbUFxQXVBeUF9gYGBhYGJwY3BkgGWQZqBnsGjAadBq8GwAbRBuMG9QcHBxkHKwc9 +B08HYQd0B4YHmQesB78H0gflB/gICwgfCDIIRghaCG4IggiWCKoIvgjSCOcI+wkQCSUJOglPCWQJeQmP +CaQJugnPCeUJ+woRCicKPQpUCmoKgQqYCq4KxQrcCvMLCwsiCzkLUQtpC4ALmAuwC8gL4Qv5DBIMKgxD +DFwMdQyODKcMwAzZDPMNDQ0mDUANWg10DY4NqQ3DDd4N+A4TDi4OSQ5kDn8Omw62DtIO7g8JDyUPQQ9e +D3oPlg+zD88P7BAJECYQQxBhEH4QmxC5ENcQ9RETETERTxFtEYwRqhHJEegSBxImEkUSZBKEEqMSwxLj +EwMTIxNDE2MTgxOkE8UT5RQGFCcUSRRqFIsUrRTOFPAVEhU0FVYVeBWbFb0V4BYDFiYWSRZsFo8WshbW +FvoXHRdBF2UXiReuF9IX9xgbGEAYZRiKGK8Y1Rj6GSAZRRlrGZEZtxndGgQaKhpRGncanhrFGuwbFBs7 +G2MbihuyG9ocAhwqHFIcexyjHMwc9R0eHUcdcB2ZHcMd7B4WHkAeah6UHr4e6R8THz4faR+UH78f6iAV +IEEgbCCYIMQg8CEcIUghdSGhIc4h+yInIlUigiKvIt0jCiM4I2YjlCPCI/AkHyRNJHwkqyTaJQklOCVo +JZclxyX3JicmVyaHJrcm6CcYJ0kneierJ9woDSg/KHEooijUKQYpOClrKZ0p0CoCKjUqaCqbKs8rAis2 +K2krnSvRLAUsOSxuLKIs1y0MLUEtdi2rLeEuFi5MLoIuty7uLyQvWi+RL8cv/jA1MGwwpDDbMRIxSjGC +Mbox8jIqMmMymzLUMw0zRjN/M7gz8TQrNGU0njTYNRM1TTWHNcI1/TY3NnI2rjbpNyQ3YDecN9c4FDhQ +OIw4yDkFOUI5fzm8Ofk6Njp0OrI67zstO2s7qjvoPCc8ZTykPOM9Ij1hPaE94D4gPmA+oD7gPyE/YT+i +P+JAI0BkQKZA50EpQWpBrEHuQjBCckK1QvdDOkN9Q8BEA0RHRIpEzkUSRVVFmkXeRiJGZ0arRvBHNUd7 +R8BIBUhLSJFI10kdSWNJqUnwSjdKfUrESwxLU0uaS+JMKkxyTLpNAk1KTZNN3E4lTm5Ot08AT0lPk0/d +UCdQcVC7UQZRUFGbUeZSMVJ8UsdTE1NfU6pT9lRCVI9U21UoVXVVwlYPVlxWqVb3V0RXklfgWC9YfVjL +WRpZaVm4WgdaVlqmWvVbRVuVW+VcNVyGXNZdJ114XcleGl5sXr1fD19hX7NgBWBXYKpg/GFPYaJh9WJJ +Ypxi8GNDY5dj62RAZJRk6WU9ZZJl52Y9ZpJm6Gc9Z5Nn6Wg/aJZo7GlDaZpp8WpIap9q92tPa6dr/2xX +bK9tCG1gbbluEm5rbsRvHm94b9FwK3CGcOBxOnGVcfByS3KmcwFzXXO4dBR0cHTMdSh1hXXhdj52m3b4 +d1Z3s3gReG54zHkqeYl553pGeqV7BHtje8J8IXyBfOF9QX2hfgF+Yn7CfyN/hH/lgEeAqIEKgWuBzYIw +gpKC9INXg7qEHYSAhOOFR4Wrhg6GcobXhzuHn4gEiGmIzokziZmJ/opkisqLMIuWi/yMY4zKjTGNmI3/ +jmaOzo82j56QBpBukNaRP5GokhGSepLjk02TtpQglIqU9JVflcmWNJaflwqXdZfgmEyYuJkkmZCZ/Jpo +mtWbQpuvnByciZz3nWSd0p5Anq6fHZ+Ln/qgaaDYoUehtqImopajBqN2o+akVqTHpTilqaYapoum/adu +p+CoUqjEqTepqaocqo+rAqt1q+msXKzQrUStuK4trqGvFq+LsACwdbDqsWCx1rJLssKzOLOutCW0nLUT +tYq2AbZ5tvC3aLfguFm40blKucK6O7q1uy67p7whvJu9Fb2Pvgq+hL7/v3q/9cBwwOzBZ8Hjwl/C28NY +w9TEUcTOxUvFyMZGxsPHQce/yD3IvMk6ybnKOMq3yzbLtsw1zLXNNc21zjbOts83z7jQOdC60TzRvtI/ +0sHTRNPG1EnUy9VO1dHWVdbY11zX4Nhk2OjZbNnx2nba+9uA3AXcit0Q3ZbeHN6i3ynfr+A24L3hROHM +4lPi2+Nj4+vkc+T85YTmDeaW5x/nqegy6LzpRunQ6lvq5etw6/vshu0R7ZzuKO6070DvzPBY8OXxcvH/ +8ozzGfOn9DT0wvVQ9d72bfb794r4Gfio+Tj5x/pX+uf7d/wH/Jj9Kf26/kv+3P9t///SLS4vMFokY2xh +c3NuYW1lWCRjbGFzc2VzXxAQTlNCaXRtYXBJbWFnZVJlcKMvMTJaTlNJbWFnZVJlcFhOU09iamVjdNIt +LjQ1V05TQXJyYXmiNDLSLS43OF5OU011dGFibGVBcnJheaM3NDLTOjsOPD0+V05TV2hpdGVcTlNDb2xv +clNwYWNlRDAgMAAQA4AM0i0uQEFXTlNDb2xvcqJAMtItLkNEV05TSW1hZ2WiQzIACAARABoAJAApADIA +NwBJAEwAUQBTAGIAaAB3AH4AhQCSAJkApQCtALwAvgDAAMUAxwDJAMsAzQDUANkA5ADmAOgA6gDvAPIA +9AD2APgA/wEWATIBNAE2E6YTqxO2E78T0hPWE+ET6hPvE/cT+hP/FA4UEhQZFCEULhQzFDUUNxQ8FEQU +RxRMFFQAAAAAAAACAQAAAAAAAABFAAAAAAAAAAAAAAAAAAAUVw diff --git a/i18n/src/main/resources/nl.lproj/Command.xib b/i18n/src/main/resources/nl.lproj/Command.xib index d0f5980de1..328bc09a01 100644 --- a/i18n/src/main/resources/nl.lproj/Command.xib +++ b/i18n/src/main/resources/nl.lproj/Command.xib @@ -1,7 +1,7 @@ - + - + diff --git a/i18n/src/main/resources/nl.lproj/Connection.xib b/i18n/src/main/resources/nl.lproj/Connection.xib index 555b8bbc73..51533fb4fa 100644 --- a/i18n/src/main/resources/nl.lproj/Connection.xib +++ b/i18n/src/main/resources/nl.lproj/Connection.xib @@ -1,7 +1,7 @@ - + - + @@ -76,7 +76,7 @@ - + diff --git a/i18n/src/main/resources/nl.lproj/Info.xib b/i18n/src/main/resources/nl.lproj/Info.xib index 64c026c5ec..2fc4c91ea2 100644 --- a/i18n/src/main/resources/nl.lproj/Info.xib +++ b/i18n/src/main/resources/nl.lproj/Info.xib @@ -1,7 +1,7 @@ - + - + @@ -89,7 +89,7 @@ - + @@ -1017,21 +1017,21 @@ - + - + - + @@ -1121,21 +1121,21 @@ - + - + - + @@ -1250,27 +1250,27 @@ diff --git a/i18n/src/main/resources/nl.lproj/Login.strings b/i18n/src/main/resources/nl.lproj/Login.strings index 520c9792af..083867c0a0 100644 Binary files a/i18n/src/main/resources/nl.lproj/Login.strings and b/i18n/src/main/resources/nl.lproj/Login.strings differ diff --git a/i18n/src/main/resources/nl.lproj/Main.xib b/i18n/src/main/resources/nl.lproj/Main.xib index 6b61b8e79b..4dc194f14f 100644 --- a/i18n/src/main/resources/nl.lproj/Main.xib +++ b/i18n/src/main/resources/nl.lproj/Main.xib @@ -1,7 +1,7 @@ - + - + @@ -94,7 +94,7 @@ - + @@ -159,19 +159,19 @@ - + - + - + @@ -321,12 +321,12 @@ CA - + - + @@ -566,6 +566,10 @@ CA - + + + + + diff --git a/i18n/src/main/resources/nl.lproj/Preferences.strings b/i18n/src/main/resources/nl.lproj/Preferences.strings index 56720bc486..73589dff22 100644 Binary files a/i18n/src/main/resources/nl.lproj/Preferences.strings and b/i18n/src/main/resources/nl.lproj/Preferences.strings differ diff --git a/i18n/src/main/resources/nl.lproj/Preferences.strings.1 b/i18n/src/main/resources/nl.lproj/Preferences.strings.1 index 46a9767e3c..1972b45e29 100644 Binary files a/i18n/src/main/resources/nl.lproj/Preferences.strings.1 and b/i18n/src/main/resources/nl.lproj/Preferences.strings.1 differ diff --git a/i18n/src/main/resources/nl.lproj/Preferences.xib b/i18n/src/main/resources/nl.lproj/Preferences.xib index 9546a265f7..7c41fb972f 100644 --- a/i18n/src/main/resources/nl.lproj/Preferences.xib +++ b/i18n/src/main/resources/nl.lproj/Preferences.xib @@ -1,7 +1,7 @@ - + - + @@ -266,7 +266,7 @@ - + @@ -307,7 +307,7 @@ - + @@ -538,7 +538,7 @@ - + @@ -578,7 +578,7 @@ - + @@ -613,7 +613,7 @@ - + @@ -642,7 +642,7 @@ - + @@ -1218,7 +1218,7 @@ - + @@ -1250,7 +1250,7 @@ - + @@ -1290,7 +1290,7 @@ - + @@ -1363,7 +1363,7 @@ - + @@ -1558,7 +1558,7 @@ - + @@ -1618,7 +1618,7 @@ - + @@ -1643,7 +1643,7 @@ - + @@ -1685,7 +1685,7 @@ - + @@ -1713,7 +1713,7 @@ - + @@ -1741,7 +1741,7 @@ - + @@ -1770,7 +1770,7 @@ - + @@ -1793,7 +1793,7 @@ - + @@ -1805,7 +1805,7 @@ - + @@ -1888,7 +1888,7 @@ - + @@ -1947,7 +1947,7 @@ - + @@ -1975,7 +1975,7 @@ - + @@ -2004,7 +2004,7 @@ - + diff --git a/i18n/src/main/resources/nl.lproj/Profiles.xib b/i18n/src/main/resources/nl.lproj/Profiles.xib index 30a7bdc0ca..40e992cebb 100644 --- a/i18n/src/main/resources/nl.lproj/Profiles.xib +++ b/i18n/src/main/resources/nl.lproj/Profiles.xib @@ -1,7 +1,7 @@ - + - + diff --git a/i18n/src/main/resources/nl.lproj/Prompt.xib b/i18n/src/main/resources/nl.lproj/Prompt.xib index 360280589d..5c398535b6 100644 --- a/i18n/src/main/resources/nl.lproj/Prompt.xib +++ b/i18n/src/main/resources/nl.lproj/Prompt.xib @@ -1,7 +1,7 @@ - + - + @@ -95,7 +95,7 @@ Gw - + diff --git a/i18n/src/main/resources/nl.lproj/S3.strings b/i18n/src/main/resources/nl.lproj/S3.strings index 24233376bb..c52f1e211d 100644 Binary files a/i18n/src/main/resources/nl.lproj/S3.strings and b/i18n/src/main/resources/nl.lproj/S3.strings differ diff --git a/i18n/src/main/resources/nl.lproj/Status.strings b/i18n/src/main/resources/nl.lproj/Status.strings index 7c81271385..41b8fe1247 100644 Binary files a/i18n/src/main/resources/nl.lproj/Status.strings and b/i18n/src/main/resources/nl.lproj/Status.strings differ diff --git a/i18n/src/main/resources/nl.lproj/Updater.strings b/i18n/src/main/resources/nl.lproj/Updater.strings index cc12d8bc2f..208aa3f1d9 100644 Binary files a/i18n/src/main/resources/nl.lproj/Updater.strings and b/i18n/src/main/resources/nl.lproj/Updater.strings differ diff --git a/i18n/src/main/resources/no.lproj/Bookmark.xib b/i18n/src/main/resources/no.lproj/Bookmark.xib index e26fcc2809..047fe19052 100644 --- a/i18n/src/main/resources/no.lproj/Bookmark.xib +++ b/i18n/src/main/resources/no.lproj/Bookmark.xib @@ -1,7 +1,7 @@ - + - + @@ -92,7 +92,7 @@ - + @@ -489,95 +489,95 @@ YnBsaXN0MDDUAQIDBAUGBwpYJHZlcnNpb25ZJGFyY2hpdmVyVCR0b3BYJG9iamVjdHMSAAGGoF8QD05T -S2V5ZWRBcmNoaXZlctEICVRyb290gAGuCwwZGh8UJCkqMTQ3PUBVJG51bGzWDQ4PEBESExQVFhcYVk5T -U2l6ZV5OU1Jlc2l6aW5nTW9kZVYkY2xhc3NcTlNJbWFnZUZsYWdzVk5TUmVwc1dOU0NvbG9ygAIQAIAN -EiDDAACAA4ALVnsxLCAxfdIbDxweWk5TLm9iamVjdHOhHYAEgArSGw8gI6IhIoAFgAaACdMPJSYnKBRf -EBROU1RJRkZSZXByZXNlbnRhdGlvbl8QGU5TSW50ZXJuYWxMYXlvdXREaXJlY3Rpb26ACIAHTxESbE1N -ACoAAAAKAAAAEAEAAAMAAAABAAEAAAEBAAMAAAABAAEAAAECAAMAAAACAAgACAEDAAMAAAABAAEAAAEG -AAMAAAABAAEAAAEKAAMAAAABAAEAAAERAAQAAAABAAAACAESAAMAAAABAAEAAAEVAAMAAAABAAIAAAEW -AAMAAAABAAEAAAEXAAQAAAABAAAAAgEcAAMAAAABAAEAAAEoAAMAAAABAAIAAAFSAAMAAAABAAEAAAFT -AAMAAAACAAEAAYdzAAcAABGcAAAA0AAAAAAAABGcYXBwbAIAAABtbnRyR1JBWVhZWiAH3AAIABcADwAu -AA9hY3NwQVBQTAAAAABub25lAAAAAAAAAAAAAAAAAAAAAAAA9tYAAQAAAADTLWFwcGwAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAVkZXNjAAAAwAAAAHlkc2NtAAABPAAA -CBpjcHJ0AAAJWAAAACN3dHB0AAAJfAAAABRrVFJDAAAJkAAACAxkZXNjAAAAAAAAAB9HZW5lcmljIEdy -YXkgR2FtbWEgMi4yIFByb2ZpbGUAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAbWx1YwAAAAAAAAAfAAAADHNr -U0sAAAAuAAABhGRhREsAAAA6AAABsmNhRVMAAAA4AAAB7HZpVk4AAABAAAACJHB0QlIAAABKAAACZHVr -VUEAAAAsAAACrmZyRlUAAAA+AAAC2mh1SFUAAAA0AAADGHpoVFcAAAAaAAADTGtvS1IAAAAiAAADZm5i -Tk8AAAA6AAADiGNzQ1oAAAAoAAADwmhlSUwAAAAkAAAD6nJvUk8AAAAqAAAEDmRlREUAAABOAAAEOGl0 -SVQAAABOAAAEhnN2U0UAAAA4AAAE1HpoQ04AAAAaAAAFDGphSlAAAAAmAAAFJmVsR1IAAAAqAAAFTHB0 -UE8AAABSAAAFdm5sTkwAAABAAAAFyGVzRVMAAABMAAAGCHRoVEgAAAAyAAAGVHRyVFIAAAAkAAAGhmZp -RkkAAABGAAAGqmhySFIAAAA+AAAG8HBsUEwAAABKAAAHLmFyRUcAAAAsAAAHeHJ1UlUAAAA6AAAHpGVu -VVMAAAA8AAAH3gBWAWEAZQBvAGIAZQBjAG4A4QAgAHMAaQB2AOEAIABnAGEAbQBhACAAMgAsADIARwBl -AG4AZQByAGkAcwBrACAAZwByAOUAIAAyACwAMgAgAGcAYQBtAG0AYQAtAHAAcgBvAGYAaQBsAEcAYQBt -AG0AYQAgAGQAZQAgAGcAcgBpAHMAbwBzACAAZwBlAG4A6AByAGkAYwBhACAAMgAuADIAQx6lAHUAIABo -AOwAbgBoACAATQDgAHUAIAB4AOEAbQAgAEMAaAB1AG4AZwAgAEcAYQBtAG0AYQAgADIALgAyAFAAZQBy -AGYAaQBsACAARwBlAG4A6QByAGkAYwBvACAAZABhACAARwBhAG0AYQAgAGQAZQAgAEMAaQBuAHoAYQBz -ACAAMgAsADIEFwQwBDMEMAQ7BEwEPQQwACAARwByAGEAeQAtBDMEMAQ8BDAAIAAyAC4AMgBQAHIAbwBm -AGkAbAAgAGcA6QBuAOkAcgBpAHEAdQBlACAAZwByAGkAcwAgAGcAYQBtAG0AYQAgADIALAAyAMEAbAB0 -AGEAbADhAG4AbwBzACAAcwB6APwAcgBrAGUAIABnAGEAbQBtAGEAIAAyAC4AMpAadShwcJaOUUlepgAy -AC4AMoJyX2ljz4/wx3y8GAAg1ozAyQAgrBC5yAAgADIALgAyACDVBLhc0wzHfABHAGUAbgBlAHIAaQBz -AGsAIABnAHIA5QAgAGcAYQBtAG0AYQAgADIALAAyAC0AcAByAG8AZgBpAGwATwBiAGUAYwBuAOEAIAFh -AGUAZADhACAAZwBhAG0AYQAgADIALgAyBdIF0AXeBdQAIAXQBeQF1QXoACAF2wXcBdwF2QAgADIALgAy -AEcAYQBtAGEAIABnAHIAaQAgAGcAZQBuAGUAcgBpAGMBAwAgADIALAAyAEEAbABsAGcAZQBtAGUAaQBu -AGUAcwAgAEcAcgBhAHUAcwB0AHUAZgBlAG4ALQBQAHIAbwBmAGkAbAAgAEcAYQBtAG0AYQAgADIALAAy -AFAAcgBvAGYAaQBsAG8AIABnAHIAaQBnAGkAbwAgAGcAZQBuAGUAcgBpAGMAbwAgAGQAZQBsAGwAYQAg -AGcAYQBtAG0AYQAgADIALAAyAEcAZQBuAGUAcgBpAHMAawAgAGcAcgDlACAAMgAsADIAIABnAGEAbQBt -AGEAcAByAG8AZgBpAGxmbpAacHBepnz7ZXAAMgAuADJjz4/wZYdO9k4AgiwwsDDsMKQwrDDzMN4AIAAy -AC4AMgAgMNcw7TDVMKEwpDDrA5MDtQO9A7kDugPMACADkwO6A8EDuQAgA5MDrAO8A7wDsQAgADIALgAy -AFAAZQByAGYAaQBsACAAZwBlAG4A6QByAGkAYwBvACAAZABlACAAYwBpAG4AegBlAG4AdABvAHMAIABk -AGEAIABHAGEAbQBtAGEAIAAyACwAMgBBAGwAZwBlAG0AZQBlAG4AIABnAHIAaQBqAHMAIABnAGEAbQBt -AGEAIAAyACwAMgAtAHAAcgBvAGYAaQBlAGwAUABlAHIAZgBpAGwAIABnAGUAbgDpAHIAaQBjAG8AIABk -AGUAIABnAGEAbQBtAGEAIABkAGUAIABnAHIAaQBzAGUAcwAgADIALAAyDiMOMQ4HDioONQ5BDgEOIQ4h -DjIOQA4BDiMOIg5MDhcOMQ5IDicORA4bACAAMgAuADIARwBlAG4AZQBsACAARwByAGkAIABHAGEAbQBh -ACAAMgAsADIAWQBsAGUAaQBuAGUAbgAgAGgAYQByAG0AYQBhAG4AIABnAGEAbQBtAGEAIAAyACwAMgAg -AC0AcAByAG8AZgBpAGkAbABpAEcAZQBuAGUAcgBpAQ0AawBpACAARwByAGEAeQAgAEcAYQBtAG0AYQAg -ADIALgAyACAAcAByAG8AZgBpAGwAVQBuAGkAdwBlAHIAcwBhAGwAbgB5ACAAcAByAG8AZgBpAGwAIABz -AHoAYQByAG8BWwBjAGkAIABnAGEAbQBtAGEAIAAyACwAMgY6BicGRQYnACAAMgAuADIAIAZEBkgGRgAg -BjEGRQYnBi8GSgAgBjkGJwZFBB4EMQRJBDAETwAgBEEENQRABDAETwAgBDMEMAQ8BDwEMAAgADIALAAy -AC0EPwRABD4ERAQ4BDsETABHAGUAbgBlAHIAaQBjACAARwByAGEAeQAgAEcAYQBtAG0AYQAgADIALgAy -ACAAUAByAG8AZgBpAGwAZQAAdGV4dAAAAABDb3B5cmlnaHQgQXBwbGUgSW5jLiwgMjAxMgAAWFlaIAAA -AAAAAPNRAAEAAAABFsxjdXJ2AAAAAAAABAAAAAAFAAoADwAUABkAHgAjACgALQAyADcAOwBAAEUASgBP -AFQAWQBeAGMAaABtAHIAdwB8AIEAhgCLAJAAlQCaAJ8ApACpAK4AsgC3ALwAwQDGAMsA0ADVANsA4ADl -AOsA8AD2APsBAQEHAQ0BEwEZAR8BJQErATIBOAE+AUUBTAFSAVkBYAFnAW4BdQF8AYMBiwGSAZoBoQGp -AbEBuQHBAckB0QHZAeEB6QHyAfoCAwIMAhQCHQImAi8COAJBAksCVAJdAmcCcQJ6AoQCjgKYAqICrAK2 -AsECywLVAuAC6wL1AwADCwMWAyEDLQM4A0MDTwNaA2YDcgN+A4oDlgOiA64DugPHA9MD4APsA/kEBgQT -BCAELQQ7BEgEVQRjBHEEfgSMBJoEqAS2BMQE0wThBPAE/gUNBRwFKwU6BUkFWAVnBXcFhgWWBaYFtQXF -BdUF5QX2BgYGFgYnBjcGSAZZBmoGewaMBp0GrwbABtEG4wb1BwcHGQcrBz0HTwdhB3QHhgeZB6wHvwfS -B+UH+AgLCB8IMghGCFoIbgiCCJYIqgi+CNII5wj7CRAJJQk6CU8JZAl5CY8JpAm6Cc8J5Qn7ChEKJwo9 -ClQKagqBCpgKrgrFCtwK8wsLCyILOQtRC2kLgAuYC7ALyAvhC/kMEgwqDEMMXAx1DI4MpwzADNkM8w0N -DSYNQA1aDXQNjg2pDcMN3g34DhMOLg5JDmQOfw6bDrYO0g7uDwkPJQ9BD14Peg+WD7MPzw/sEAkQJhBD -EGEQfhCbELkQ1xD1ERMRMRFPEW0RjBGqEckR6BIHEiYSRRJkEoQSoxLDEuMTAxMjE0MTYxODE6QTxRPl -FAYUJxRJFGoUixStFM4U8BUSFTQVVhV4FZsVvRXgFgMWJhZJFmwWjxayFtYW+hcdF0EXZReJF64X0hf3 -GBsYQBhlGIoYrxjVGPoZIBlFGWsZkRm3Gd0aBBoqGlEadxqeGsUa7BsUGzsbYxuKG7Ib2hwCHCocUhx7 -HKMczBz1HR4dRx1wHZkdwx3sHhYeQB5qHpQevh7pHxMfPh9pH5Qfvx/qIBUgQSBsIJggxCDwIRwhSCF1 -IaEhziH7IiciVSKCIq8i3SMKIzgjZiOUI8Ij8CQfJE0kfCSrJNolCSU4JWgllyXHJfcmJyZXJocmtybo -JxgnSSd6J6sn3CgNKD8ocSiiKNQpBik4KWspnSnQKgIqNSpoKpsqzysCKzYraSudK9EsBSw5LG4soizX -LQwtQS12Last4S4WLkwugi63Lu4vJC9aL5Evxy/+MDUwbDCkMNsxEjFKMYIxujHyMioyYzKbMtQzDTNG -M38zuDPxNCs0ZTSeNNg1EzVNNYc1wjX9Njc2cjauNuk3JDdgN5w31zgUOFA4jDjIOQU5Qjl/Obw5+To2 -OnQ6sjrvOy07azuqO+g8JzxlPKQ84z0iPWE9oT3gPiA+YD6gPuA/IT9hP6I/4kAjQGRApkDnQSlBakGs -Qe5CMEJyQrVC90M6Q31DwEQDREdEikTORRJFVUWaRd5GIkZnRqtG8Ec1R3tHwEgFSEtIkUjXSR1JY0mp -SfBKN0p9SsRLDEtTS5pL4kwqTHJMuk0CTUpNk03cTiVObk63TwBPSU+TT91QJ1BxULtRBlFQUZtR5lIx -UnxSx1MTU19TqlP2VEJUj1TbVShVdVXCVg9WXFapVvdXRFeSV+BYL1h9WMtZGllpWbhaB1pWWqZa9VtF -W5Vb5Vw1XIZc1l0nXXhdyV4aXmxevV8PX2Ffs2AFYFdgqmD8YU9homH1YklinGLwY0Njl2PrZEBklGTp -ZT1lkmXnZj1mkmboZz1nk2fpaD9olmjsaUNpmmnxakhqn2r3a09rp2v/bFdsr20IbWBtuW4SbmtuxG8e -b3hv0XArcIZw4HE6cZVx8HJLcqZzAXNdc7h0FHRwdMx1KHWFdeF2Pnabdvh3VnezeBF4bnjMeSp5iXnn -ekZ6pXsEe2N7wnwhfIF84X1BfaF+AX5ifsJ/I3+Ef+WAR4CogQqBa4HNgjCCkoL0g1eDuoQdhICE44VH -hauGDoZyhteHO4efiASIaYjOiTOJmYn+imSKyoswi5aL/IxjjMqNMY2Yjf+OZo7OjzaPnpAGkG6Q1pE/ -kaiSEZJ6kuOTTZO2lCCUipT0lV+VyZY0lp+XCpd1l+CYTJi4mSSZkJn8mmia1ZtCm6+cHJyJnPedZJ3S -nkCerp8dn4uf+qBpoNihR6G2oiailqMGo3aj5qRWpMelOKWpphqmi6b9p26n4KhSqMSpN6mpqhyqj6sC -q3Wr6axcrNCtRK24ri2uoa8Wr4uwALB1sOqxYLHWskuywrM4s660JbSctRO1irYBtnm28Ldot+C4WbjR -uUq5wro7urW7LrunvCG8m70VvY++Cr6Evv+/er/1wHDA7MFnwePCX8Lbw1jD1MRRxM7FS8XIxkbGw8dB -x7/IPci8yTrJuco4yrfLNsu2zDXMtc01zbXONs62zzfPuNA50LrRPNG+0j/SwdNE08bUSdTL1U7V0dZV -1tjXXNfg2GTY6Nls2fHadtr724DcBdyK3RDdlt4c3qLfKd+v4DbgveFE4cziU+Lb42Pj6+Rz5PzlhOYN -5pbnH+ep6DLovOlG6dDqW+rl63Dr++yG7RHtnO4o7rTvQO/M8Fjw5fFy8f/yjPMZ86f0NPTC9VD13vZt -9vv3ivgZ+Kj5OPnH+lf65/t3/Af8mP0p/br+S/7c/23//9IrLC0uWiRjbGFzc25hbWVYJGNsYXNzZXNf -EBBOU0JpdG1hcEltYWdlUmVwoy0vMFpOU0ltYWdlUmVwWE5TT2JqZWN00issMjNXTlNBcnJheaIyMNIr -LDU2Xk5TTXV0YWJsZUFycmF5ozUyMNM4OQ86OzxXTlNXaGl0ZVxOU0NvbG9yU3BhY2VEMCAwABADgAzS -Kyw+P1dOU0NvbG9yoj4w0issQUJXTlNJbWFnZaJBMAAIABEAGgAkACkAMgA3AEkATABRAFMAYgBoAHUA -fACLAJIAnwCmAK4AsACyALQAuQC7AL0AxADJANQA1gDYANoA3wDiAOQA5gDoAO8BBgEiASQBJhOWE5sT -phOvE8ITxhPRE9oT3xPnE+oT7xP+FAIUCRQRFB4UIxQlFCcULBQ0FDcUPBREAAAAAAAAAgEAAAAAAAAA -QwAAAAAAAAAAAAAAAAAAFEc +S2V5ZWRBcmNoaXZlctEICVRyb290gAGuCwwbHCEaJissMzY5P0JVJG51bGzXDQ4PEBESExQVFhcYGRpW +TlNTaXplViRjbGFzc1xOU0ltYWdlRmxhZ3NWTlNSZXBzW05TVGludENvbG9yV05TQ29sb3JeTlNSZXNp +emluZ01vZGWAAoANEiDDAACAA4AAgAsQAFZ7MSwgMX3SHQ4eIFpOUy5vYmplY3RzoR+ABIAK0h0OIiWi +IySABYAGgAnTDicoKSoaXxAUTlNUSUZGUmVwcmVzZW50YXRpb25fEBlOU0ludGVybmFsTGF5b3V0RGly +ZWN0aW9ugAiAB08REmxNTQAqAAAACgAAABABAAADAAAAAQABAAABAQADAAAAAQABAAABAgADAAAAAgAI +AAgBAwADAAAAAQABAAABBgADAAAAAQABAAABCgADAAAAAQABAAABEQAEAAAAAQAAAAgBEgADAAAAAQAB +AAABFQADAAAAAQACAAABFgADAAAAAQABAAABFwAEAAAAAQAAAAIBHAADAAAAAQABAAABKAADAAAAAQAC +AAABUgADAAAAAQABAAABUwADAAAAAgABAAGHcwAHAAARnAAAANAAAAAAAAARnGFwcGwCAAAAbW50ckdS +QVlYWVogB9wACAAXAA8ALgAPYWNzcEFQUEwAAAAAbm9uZQAAAAAAAAAAAAAAAAAAAAAAAPbWAAEAAAAA +0y1hcHBsAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFZGVzYwAA +AMAAAAB5ZHNjbQAAATwAAAgaY3BydAAACVgAAAAjd3RwdAAACXwAAAAUa1RSQwAACZAAAAgMZGVzYwAA +AAAAAAAfR2VuZXJpYyBHcmF5IEdhbW1hIDIuMiBQcm9maWxlAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAG1s +dWMAAAAAAAAAHwAAAAxza1NLAAAALgAAAYRkYURLAAAAOgAAAbJjYUVTAAAAOAAAAex2aVZOAAAAQAAA +AiRwdEJSAAAASgAAAmR1a1VBAAAALAAAAq5mckZVAAAAPgAAAtpodUhVAAAANAAAAxh6aFRXAAAAGgAA +A0xrb0tSAAAAIgAAA2ZuYk5PAAAAOgAAA4hjc0NaAAAAKAAAA8JoZUlMAAAAJAAAA+pyb1JPAAAAKgAA +BA5kZURFAAAATgAABDhpdElUAAAATgAABIZzdlNFAAAAOAAABNR6aENOAAAAGgAABQxqYUpQAAAAJgAA +BSZlbEdSAAAAKgAABUxwdFBPAAAAUgAABXZubE5MAAAAQAAABchlc0VTAAAATAAABgh0aFRIAAAAMgAA +BlR0clRSAAAAJAAABoZmaUZJAAAARgAABqpockhSAAAAPgAABvBwbFBMAAAASgAABy5hckVHAAAALAAA +B3hydVJVAAAAOgAAB6RlblVTAAAAPAAAB94AVgFhAGUAbwBiAGUAYwBuAOEAIABzAGkAdgDhACAAZwBh +AG0AYQAgADIALAAyAEcAZQBuAGUAcgBpAHMAawAgAGcAcgDlACAAMgAsADIAIABnAGEAbQBtAGEALQBw +AHIAbwBmAGkAbABHAGEAbQBtAGEAIABkAGUAIABnAHIAaQBzAG8AcwAgAGcAZQBuAOgAcgBpAGMAYQAg +ADIALgAyAEMepQB1ACAAaADsAG4AaAAgAE0A4AB1ACAAeADhAG0AIABDAGgAdQBuAGcAIABHAGEAbQBt +AGEAIAAyAC4AMgBQAGUAcgBmAGkAbAAgAEcAZQBuAOkAcgBpAGMAbwAgAGQAYQAgAEcAYQBtAGEAIABk +AGUAIABDAGkAbgB6AGEAcwAgADIALAAyBBcEMAQzBDAEOwRMBD0EMAAgAEcAcgBhAHkALQQzBDAEPAQw +ACAAMgAuADIAUAByAG8AZgBpAGwAIABnAOkAbgDpAHIAaQBxAHUAZQAgAGcAcgBpAHMAIABnAGEAbQBt +AGEAIAAyACwAMgDBAGwAdABhAGwA4QBuAG8AcwAgAHMAegD8AHIAawBlACAAZwBhAG0AbQBhACAAMgAu +ADKQGnUocHCWjlFJXqYAMgAuADKCcl9pY8+P8Md8vBgAINaMwMkAIKwQucgAIAAyAC4AMgAg1QS4XNMM +x3wARwBlAG4AZQByAGkAcwBrACAAZwByAOUAIABnAGEAbQBtAGEAIAAyACwAMgAtAHAAcgBvAGYAaQBs +AE8AYgBlAGMAbgDhACABYQBlAGQA4QAgAGcAYQBtAGEAIAAyAC4AMgXSBdAF3gXUACAF0AXkBdUF6AAg +BdsF3AXcBdkAIAAyAC4AMgBHAGEAbQBhACAAZwByAGkAIABnAGUAbgBlAHIAaQBjAQMAIAAyACwAMgBB +AGwAbABnAGUAbQBlAGkAbgBlAHMAIABHAHIAYQB1AHMAdAB1AGYAZQBuAC0AUAByAG8AZgBpAGwAIABH +AGEAbQBtAGEAIAAyACwAMgBQAHIAbwBmAGkAbABvACAAZwByAGkAZwBpAG8AIABnAGUAbgBlAHIAaQBj +AG8AIABkAGUAbABsAGEAIABnAGEAbQBtAGEAIAAyACwAMgBHAGUAbgBlAHIAaQBzAGsAIABnAHIA5QAg +ADIALAAyACAAZwBhAG0AbQBhAHAAcgBvAGYAaQBsZm6QGnBwXqZ8+2VwADIALgAyY8+P8GWHTvZOAIIs +MLAw7DCkMKww8zDeACAAMgAuADIAIDDXMO0w1TChMKQw6wOTA7UDvQO5A7oDzAAgA5MDugPBA7kAIAOT +A6wDvAO8A7EAIAAyAC4AMgBQAGUAcgBmAGkAbAAgAGcAZQBuAOkAcgBpAGMAbwAgAGQAZQAgAGMAaQBu +AHoAZQBuAHQAbwBzACAAZABhACAARwBhAG0AbQBhACAAMgAsADIAQQBsAGcAZQBtAGUAZQBuACAAZwBy +AGkAagBzACAAZwBhAG0AbQBhACAAMgAsADIALQBwAHIAbwBmAGkAZQBsAFAAZQByAGYAaQBsACAAZwBl +AG4A6QByAGkAYwBvACAAZABlACAAZwBhAG0AbQBhACAAZABlACAAZwByAGkAcwBlAHMAIAAyACwAMg4j +DjEOBw4qDjUOQQ4BDiEOIQ4yDkAOAQ4jDiIOTA4XDjEOSA4nDkQOGwAgADIALgAyAEcAZQBuAGUAbAAg +AEcAcgBpACAARwBhAG0AYQAgADIALAAyAFkAbABlAGkAbgBlAG4AIABoAGEAcgBtAGEAYQBuACAAZwBh +AG0AbQBhACAAMgAsADIAIAAtAHAAcgBvAGYAaQBpAGwAaQBHAGUAbgBlAHIAaQENAGsAaQAgAEcAcgBh +AHkAIABHAGEAbQBtAGEAIAAyAC4AMgAgAHAAcgBvAGYAaQBsAFUAbgBpAHcAZQByAHMAYQBsAG4AeQAg +AHAAcgBvAGYAaQBsACAAcwB6AGEAcgBvAVsAYwBpACAAZwBhAG0AbQBhACAAMgAsADIGOgYnBkUGJwAg +ADIALgAyACAGRAZIBkYAIAYxBkUGJwYvBkoAIAY5BicGRQQeBDEESQQwBE8AIARBBDUEQAQwBE8AIAQz +BDAEPAQ8BDAAIAAyACwAMgAtBD8EQAQ+BEQEOAQ7BEwARwBlAG4AZQByAGkAYwAgAEcAcgBhAHkAIABH +AGEAbQBtAGEAIAAyAC4AMgAgAFAAcgBvAGYAaQBsAGUAAHRleHQAAAAAQ29weXJpZ2h0IEFwcGxlIElu +Yy4sIDIwMTIAAFhZWiAAAAAAAADzUQABAAAAARbMY3VydgAAAAAAAAQAAAAABQAKAA8AFAAZAB4AIwAo +AC0AMgA3ADsAQABFAEoATwBUAFkAXgBjAGgAbQByAHcAfACBAIYAiwCQAJUAmgCfAKQAqQCuALIAtwC8 +AMEAxgDLANAA1QDbAOAA5QDrAPAA9gD7AQEBBwENARMBGQEfASUBKwEyATgBPgFFAUwBUgFZAWABZwFu +AXUBfAGDAYsBkgGaAaEBqQGxAbkBwQHJAdEB2QHhAekB8gH6AgMCDAIUAh0CJgIvAjgCQQJLAlQCXQJn +AnECegKEAo4CmAKiAqwCtgLBAssC1QLgAusC9QMAAwsDFgMhAy0DOANDA08DWgNmA3IDfgOKA5YDogOu +A7oDxwPTA+AD7AP5BAYEEwQgBC0EOwRIBFUEYwRxBH4EjASaBKgEtgTEBNME4QTwBP4FDQUcBSsFOgVJ +BVgFZwV3BYYFlgWmBbUFxQXVBeUF9gYGBhYGJwY3BkgGWQZqBnsGjAadBq8GwAbRBuMG9QcHBxkHKwc9 +B08HYQd0B4YHmQesB78H0gflB/gICwgfCDIIRghaCG4IggiWCKoIvgjSCOcI+wkQCSUJOglPCWQJeQmP +CaQJugnPCeUJ+woRCicKPQpUCmoKgQqYCq4KxQrcCvMLCwsiCzkLUQtpC4ALmAuwC8gL4Qv5DBIMKgxD +DFwMdQyODKcMwAzZDPMNDQ0mDUANWg10DY4NqQ3DDd4N+A4TDi4OSQ5kDn8Omw62DtIO7g8JDyUPQQ9e +D3oPlg+zD88P7BAJECYQQxBhEH4QmxC5ENcQ9RETETERTxFtEYwRqhHJEegSBxImEkUSZBKEEqMSwxLj +EwMTIxNDE2MTgxOkE8UT5RQGFCcUSRRqFIsUrRTOFPAVEhU0FVYVeBWbFb0V4BYDFiYWSRZsFo8WshbW +FvoXHRdBF2UXiReuF9IX9xgbGEAYZRiKGK8Y1Rj6GSAZRRlrGZEZtxndGgQaKhpRGncanhrFGuwbFBs7 +G2MbihuyG9ocAhwqHFIcexyjHMwc9R0eHUcdcB2ZHcMd7B4WHkAeah6UHr4e6R8THz4faR+UH78f6iAV +IEEgbCCYIMQg8CEcIUghdSGhIc4h+yInIlUigiKvIt0jCiM4I2YjlCPCI/AkHyRNJHwkqyTaJQklOCVo +JZclxyX3JicmVyaHJrcm6CcYJ0kneierJ9woDSg/KHEooijUKQYpOClrKZ0p0CoCKjUqaCqbKs8rAis2 +K2krnSvRLAUsOSxuLKIs1y0MLUEtdi2rLeEuFi5MLoIuty7uLyQvWi+RL8cv/jA1MGwwpDDbMRIxSjGC +Mbox8jIqMmMymzLUMw0zRjN/M7gz8TQrNGU0njTYNRM1TTWHNcI1/TY3NnI2rjbpNyQ3YDecN9c4FDhQ +OIw4yDkFOUI5fzm8Ofk6Njp0OrI67zstO2s7qjvoPCc8ZTykPOM9Ij1hPaE94D4gPmA+oD7gPyE/YT+i +P+JAI0BkQKZA50EpQWpBrEHuQjBCckK1QvdDOkN9Q8BEA0RHRIpEzkUSRVVFmkXeRiJGZ0arRvBHNUd7 +R8BIBUhLSJFI10kdSWNJqUnwSjdKfUrESwxLU0uaS+JMKkxyTLpNAk1KTZNN3E4lTm5Ot08AT0lPk0/d +UCdQcVC7UQZRUFGbUeZSMVJ8UsdTE1NfU6pT9lRCVI9U21UoVXVVwlYPVlxWqVb3V0RXklfgWC9YfVjL +WRpZaVm4WgdaVlqmWvVbRVuVW+VcNVyGXNZdJ114XcleGl5sXr1fD19hX7NgBWBXYKpg/GFPYaJh9WJJ +Ypxi8GNDY5dj62RAZJRk6WU9ZZJl52Y9ZpJm6Gc9Z5Nn6Wg/aJZo7GlDaZpp8WpIap9q92tPa6dr/2xX +bK9tCG1gbbluEm5rbsRvHm94b9FwK3CGcOBxOnGVcfByS3KmcwFzXXO4dBR0cHTMdSh1hXXhdj52m3b4 +d1Z3s3gReG54zHkqeYl553pGeqV7BHtje8J8IXyBfOF9QX2hfgF+Yn7CfyN/hH/lgEeAqIEKgWuBzYIw +gpKC9INXg7qEHYSAhOOFR4Wrhg6GcobXhzuHn4gEiGmIzokziZmJ/opkisqLMIuWi/yMY4zKjTGNmI3/ +jmaOzo82j56QBpBukNaRP5GokhGSepLjk02TtpQglIqU9JVflcmWNJaflwqXdZfgmEyYuJkkmZCZ/Jpo +mtWbQpuvnByciZz3nWSd0p5Anq6fHZ+Ln/qgaaDYoUehtqImopajBqN2o+akVqTHpTilqaYapoum/adu +p+CoUqjEqTepqaocqo+rAqt1q+msXKzQrUStuK4trqGvFq+LsACwdbDqsWCx1rJLssKzOLOutCW0nLUT +tYq2AbZ5tvC3aLfguFm40blKucK6O7q1uy67p7whvJu9Fb2Pvgq+hL7/v3q/9cBwwOzBZ8Hjwl/C28NY +w9TEUcTOxUvFyMZGxsPHQce/yD3IvMk6ybnKOMq3yzbLtsw1zLXNNc21zjbOts83z7jQOdC60TzRvtI/ +0sHTRNPG1EnUy9VO1dHWVdbY11zX4Nhk2OjZbNnx2nba+9uA3AXcit0Q3ZbeHN6i3ynfr+A24L3hROHM +4lPi2+Nj4+vkc+T85YTmDeaW5x/nqegy6LzpRunQ6lvq5etw6/vshu0R7ZzuKO6070DvzPBY8OXxcvH/ +8ozzGfOn9DT0wvVQ9d72bfb794r4Gfio+Tj5x/pX+uf7d/wH/Jj9Kf26/kv+3P9t///SLS4vMFokY2xh +c3NuYW1lWCRjbGFzc2VzXxAQTlNCaXRtYXBJbWFnZVJlcKMvMTJaTlNJbWFnZVJlcFhOU09iamVjdNIt +LjQ1V05TQXJyYXmiNDLSLS43OF5OU011dGFibGVBcnJheaM3NDLTOjsOPD0+V05TV2hpdGVcTlNDb2xv +clNwYWNlRDAgMAAQA4AM0i0uQEFXTlNDb2xvcqJAMtItLkNEV05TSW1hZ2WiQzIACAARABoAJAApADIA +NwBJAEwAUQBTAGIAaAB3AH4AhQCSAJkApQCtALwAvgDAAMUAxwDJAMsAzQDUANkA5ADmAOgA6gDvAPIA +9AD2APgA/wEWATIBNAE2E6YTqxO2E78T0hPWE+ET6hPvE/cT+hP/FA4UEhQZFCEULhQzFDUUNxQ8FEQU +RxRMFFQAAAAAAAACAQAAAAAAAABFAAAAAAAAAAAAAAAAAAAUVw diff --git a/i18n/src/main/resources/no.lproj/Browser.xib b/i18n/src/main/resources/no.lproj/Browser.xib index 2450670bd2..e8f09d05a9 100644 --- a/i18n/src/main/resources/no.lproj/Browser.xib +++ b/i18n/src/main/resources/no.lproj/Browser.xib @@ -1,7 +1,7 @@ - + - + @@ -38,7 +38,7 @@ - + @@ -62,27 +62,27 @@ @@ -256,7 +256,7 @@ - + @@ -553,95 +553,95 @@ DQ YnBsaXN0MDDUAQIDBAUGBwpYJHZlcnNpb25ZJGFyY2hpdmVyVCR0b3BYJG9iamVjdHMSAAGGoF8QD05T -S2V5ZWRBcmNoaXZlctEICVRyb290gAGuCwwZGh8UJCkqMTQ3PUBVJG51bGzWDQ4PEBESExQVFhcYVk5T -U2l6ZV5OU1Jlc2l6aW5nTW9kZVYkY2xhc3NcTlNJbWFnZUZsYWdzVk5TUmVwc1dOU0NvbG9ygAIQAIAN -EiDDAACAA4ALVnsxLCAxfdIbDxweWk5TLm9iamVjdHOhHYAEgArSGw8gI6IhIoAFgAaACdMPJSYnKBRf -EBROU1RJRkZSZXByZXNlbnRhdGlvbl8QGU5TSW50ZXJuYWxMYXlvdXREaXJlY3Rpb26ACIAHTxESbE1N -ACoAAAAKAAAAEAEAAAMAAAABAAEAAAEBAAMAAAABAAEAAAECAAMAAAACAAgACAEDAAMAAAABAAEAAAEG -AAMAAAABAAEAAAEKAAMAAAABAAEAAAERAAQAAAABAAAACAESAAMAAAABAAEAAAEVAAMAAAABAAIAAAEW -AAMAAAABAAEAAAEXAAQAAAABAAAAAgEcAAMAAAABAAEAAAEoAAMAAAABAAIAAAFSAAMAAAABAAEAAAFT -AAMAAAACAAEAAYdzAAcAABGcAAAA0AAAAAAAABGcYXBwbAIAAABtbnRyR1JBWVhZWiAH3AAIABcADwAu -AA9hY3NwQVBQTAAAAABub25lAAAAAAAAAAAAAAAAAAAAAAAA9tYAAQAAAADTLWFwcGwAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAVkZXNjAAAAwAAAAHlkc2NtAAABPAAA -CBpjcHJ0AAAJWAAAACN3dHB0AAAJfAAAABRrVFJDAAAJkAAACAxkZXNjAAAAAAAAAB9HZW5lcmljIEdy -YXkgR2FtbWEgMi4yIFByb2ZpbGUAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAbWx1YwAAAAAAAAAfAAAADHNr -U0sAAAAuAAABhGRhREsAAAA6AAABsmNhRVMAAAA4AAAB7HZpVk4AAABAAAACJHB0QlIAAABKAAACZHVr -VUEAAAAsAAACrmZyRlUAAAA+AAAC2mh1SFUAAAA0AAADGHpoVFcAAAAaAAADTGtvS1IAAAAiAAADZm5i -Tk8AAAA6AAADiGNzQ1oAAAAoAAADwmhlSUwAAAAkAAAD6nJvUk8AAAAqAAAEDmRlREUAAABOAAAEOGl0 -SVQAAABOAAAEhnN2U0UAAAA4AAAE1HpoQ04AAAAaAAAFDGphSlAAAAAmAAAFJmVsR1IAAAAqAAAFTHB0 -UE8AAABSAAAFdm5sTkwAAABAAAAFyGVzRVMAAABMAAAGCHRoVEgAAAAyAAAGVHRyVFIAAAAkAAAGhmZp -RkkAAABGAAAGqmhySFIAAAA+AAAG8HBsUEwAAABKAAAHLmFyRUcAAAAsAAAHeHJ1UlUAAAA6AAAHpGVu -VVMAAAA8AAAH3gBWAWEAZQBvAGIAZQBjAG4A4QAgAHMAaQB2AOEAIABnAGEAbQBhACAAMgAsADIARwBl -AG4AZQByAGkAcwBrACAAZwByAOUAIAAyACwAMgAgAGcAYQBtAG0AYQAtAHAAcgBvAGYAaQBsAEcAYQBt -AG0AYQAgAGQAZQAgAGcAcgBpAHMAbwBzACAAZwBlAG4A6AByAGkAYwBhACAAMgAuADIAQx6lAHUAIABo -AOwAbgBoACAATQDgAHUAIAB4AOEAbQAgAEMAaAB1AG4AZwAgAEcAYQBtAG0AYQAgADIALgAyAFAAZQBy -AGYAaQBsACAARwBlAG4A6QByAGkAYwBvACAAZABhACAARwBhAG0AYQAgAGQAZQAgAEMAaQBuAHoAYQBz -ACAAMgAsADIEFwQwBDMEMAQ7BEwEPQQwACAARwByAGEAeQAtBDMEMAQ8BDAAIAAyAC4AMgBQAHIAbwBm -AGkAbAAgAGcA6QBuAOkAcgBpAHEAdQBlACAAZwByAGkAcwAgAGcAYQBtAG0AYQAgADIALAAyAMEAbAB0 -AGEAbADhAG4AbwBzACAAcwB6APwAcgBrAGUAIABnAGEAbQBtAGEAIAAyAC4AMpAadShwcJaOUUlepgAy -AC4AMoJyX2ljz4/wx3y8GAAg1ozAyQAgrBC5yAAgADIALgAyACDVBLhc0wzHfABHAGUAbgBlAHIAaQBz -AGsAIABnAHIA5QAgAGcAYQBtAG0AYQAgADIALAAyAC0AcAByAG8AZgBpAGwATwBiAGUAYwBuAOEAIAFh -AGUAZADhACAAZwBhAG0AYQAgADIALgAyBdIF0AXeBdQAIAXQBeQF1QXoACAF2wXcBdwF2QAgADIALgAy -AEcAYQBtAGEAIABnAHIAaQAgAGcAZQBuAGUAcgBpAGMBAwAgADIALAAyAEEAbABsAGcAZQBtAGUAaQBu -AGUAcwAgAEcAcgBhAHUAcwB0AHUAZgBlAG4ALQBQAHIAbwBmAGkAbAAgAEcAYQBtAG0AYQAgADIALAAy -AFAAcgBvAGYAaQBsAG8AIABnAHIAaQBnAGkAbwAgAGcAZQBuAGUAcgBpAGMAbwAgAGQAZQBsAGwAYQAg -AGcAYQBtAG0AYQAgADIALAAyAEcAZQBuAGUAcgBpAHMAawAgAGcAcgDlACAAMgAsADIAIABnAGEAbQBt -AGEAcAByAG8AZgBpAGxmbpAacHBepnz7ZXAAMgAuADJjz4/wZYdO9k4AgiwwsDDsMKQwrDDzMN4AIAAy -AC4AMgAgMNcw7TDVMKEwpDDrA5MDtQO9A7kDugPMACADkwO6A8EDuQAgA5MDrAO8A7wDsQAgADIALgAy -AFAAZQByAGYAaQBsACAAZwBlAG4A6QByAGkAYwBvACAAZABlACAAYwBpAG4AegBlAG4AdABvAHMAIABk -AGEAIABHAGEAbQBtAGEAIAAyACwAMgBBAGwAZwBlAG0AZQBlAG4AIABnAHIAaQBqAHMAIABnAGEAbQBt -AGEAIAAyACwAMgAtAHAAcgBvAGYAaQBlAGwAUABlAHIAZgBpAGwAIABnAGUAbgDpAHIAaQBjAG8AIABk -AGUAIABnAGEAbQBtAGEAIABkAGUAIABnAHIAaQBzAGUAcwAgADIALAAyDiMOMQ4HDioONQ5BDgEOIQ4h -DjIOQA4BDiMOIg5MDhcOMQ5IDicORA4bACAAMgAuADIARwBlAG4AZQBsACAARwByAGkAIABHAGEAbQBh -ACAAMgAsADIAWQBsAGUAaQBuAGUAbgAgAGgAYQByAG0AYQBhAG4AIABnAGEAbQBtAGEAIAAyACwAMgAg -AC0AcAByAG8AZgBpAGkAbABpAEcAZQBuAGUAcgBpAQ0AawBpACAARwByAGEAeQAgAEcAYQBtAG0AYQAg -ADIALgAyACAAcAByAG8AZgBpAGwAVQBuAGkAdwBlAHIAcwBhAGwAbgB5ACAAcAByAG8AZgBpAGwAIABz -AHoAYQByAG8BWwBjAGkAIABnAGEAbQBtAGEAIAAyACwAMgY6BicGRQYnACAAMgAuADIAIAZEBkgGRgAg -BjEGRQYnBi8GSgAgBjkGJwZFBB4EMQRJBDAETwAgBEEENQRABDAETwAgBDMEMAQ8BDwEMAAgADIALAAy -AC0EPwRABD4ERAQ4BDsETABHAGUAbgBlAHIAaQBjACAARwByAGEAeQAgAEcAYQBtAG0AYQAgADIALgAy -ACAAUAByAG8AZgBpAGwAZQAAdGV4dAAAAABDb3B5cmlnaHQgQXBwbGUgSW5jLiwgMjAxMgAAWFlaIAAA -AAAAAPNRAAEAAAABFsxjdXJ2AAAAAAAABAAAAAAFAAoADwAUABkAHgAjACgALQAyADcAOwBAAEUASgBP -AFQAWQBeAGMAaABtAHIAdwB8AIEAhgCLAJAAlQCaAJ8ApACpAK4AsgC3ALwAwQDGAMsA0ADVANsA4ADl -AOsA8AD2APsBAQEHAQ0BEwEZAR8BJQErATIBOAE+AUUBTAFSAVkBYAFnAW4BdQF8AYMBiwGSAZoBoQGp -AbEBuQHBAckB0QHZAeEB6QHyAfoCAwIMAhQCHQImAi8COAJBAksCVAJdAmcCcQJ6AoQCjgKYAqICrAK2 -AsECywLVAuAC6wL1AwADCwMWAyEDLQM4A0MDTwNaA2YDcgN+A4oDlgOiA64DugPHA9MD4APsA/kEBgQT -BCAELQQ7BEgEVQRjBHEEfgSMBJoEqAS2BMQE0wThBPAE/gUNBRwFKwU6BUkFWAVnBXcFhgWWBaYFtQXF -BdUF5QX2BgYGFgYnBjcGSAZZBmoGewaMBp0GrwbABtEG4wb1BwcHGQcrBz0HTwdhB3QHhgeZB6wHvwfS -B+UH+AgLCB8IMghGCFoIbgiCCJYIqgi+CNII5wj7CRAJJQk6CU8JZAl5CY8JpAm6Cc8J5Qn7ChEKJwo9 -ClQKagqBCpgKrgrFCtwK8wsLCyILOQtRC2kLgAuYC7ALyAvhC/kMEgwqDEMMXAx1DI4MpwzADNkM8w0N -DSYNQA1aDXQNjg2pDcMN3g34DhMOLg5JDmQOfw6bDrYO0g7uDwkPJQ9BD14Peg+WD7MPzw/sEAkQJhBD -EGEQfhCbELkQ1xD1ERMRMRFPEW0RjBGqEckR6BIHEiYSRRJkEoQSoxLDEuMTAxMjE0MTYxODE6QTxRPl -FAYUJxRJFGoUixStFM4U8BUSFTQVVhV4FZsVvRXgFgMWJhZJFmwWjxayFtYW+hcdF0EXZReJF64X0hf3 -GBsYQBhlGIoYrxjVGPoZIBlFGWsZkRm3Gd0aBBoqGlEadxqeGsUa7BsUGzsbYxuKG7Ib2hwCHCocUhx7 -HKMczBz1HR4dRx1wHZkdwx3sHhYeQB5qHpQevh7pHxMfPh9pH5Qfvx/qIBUgQSBsIJggxCDwIRwhSCF1 -IaEhziH7IiciVSKCIq8i3SMKIzgjZiOUI8Ij8CQfJE0kfCSrJNolCSU4JWgllyXHJfcmJyZXJocmtybo -JxgnSSd6J6sn3CgNKD8ocSiiKNQpBik4KWspnSnQKgIqNSpoKpsqzysCKzYraSudK9EsBSw5LG4soizX -LQwtQS12Last4S4WLkwugi63Lu4vJC9aL5Evxy/+MDUwbDCkMNsxEjFKMYIxujHyMioyYzKbMtQzDTNG -M38zuDPxNCs0ZTSeNNg1EzVNNYc1wjX9Njc2cjauNuk3JDdgN5w31zgUOFA4jDjIOQU5Qjl/Obw5+To2 -OnQ6sjrvOy07azuqO+g8JzxlPKQ84z0iPWE9oT3gPiA+YD6gPuA/IT9hP6I/4kAjQGRApkDnQSlBakGs -Qe5CMEJyQrVC90M6Q31DwEQDREdEikTORRJFVUWaRd5GIkZnRqtG8Ec1R3tHwEgFSEtIkUjXSR1JY0mp -SfBKN0p9SsRLDEtTS5pL4kwqTHJMuk0CTUpNk03cTiVObk63TwBPSU+TT91QJ1BxULtRBlFQUZtR5lIx -UnxSx1MTU19TqlP2VEJUj1TbVShVdVXCVg9WXFapVvdXRFeSV+BYL1h9WMtZGllpWbhaB1pWWqZa9VtF -W5Vb5Vw1XIZc1l0nXXhdyV4aXmxevV8PX2Ffs2AFYFdgqmD8YU9homH1YklinGLwY0Njl2PrZEBklGTp -ZT1lkmXnZj1mkmboZz1nk2fpaD9olmjsaUNpmmnxakhqn2r3a09rp2v/bFdsr20IbWBtuW4SbmtuxG8e -b3hv0XArcIZw4HE6cZVx8HJLcqZzAXNdc7h0FHRwdMx1KHWFdeF2Pnabdvh3VnezeBF4bnjMeSp5iXnn -ekZ6pXsEe2N7wnwhfIF84X1BfaF+AX5ifsJ/I3+Ef+WAR4CogQqBa4HNgjCCkoL0g1eDuoQdhICE44VH -hauGDoZyhteHO4efiASIaYjOiTOJmYn+imSKyoswi5aL/IxjjMqNMY2Yjf+OZo7OjzaPnpAGkG6Q1pE/ -kaiSEZJ6kuOTTZO2lCCUipT0lV+VyZY0lp+XCpd1l+CYTJi4mSSZkJn8mmia1ZtCm6+cHJyJnPedZJ3S -nkCerp8dn4uf+qBpoNihR6G2oiailqMGo3aj5qRWpMelOKWpphqmi6b9p26n4KhSqMSpN6mpqhyqj6sC -q3Wr6axcrNCtRK24ri2uoa8Wr4uwALB1sOqxYLHWskuywrM4s660JbSctRO1irYBtnm28Ldot+C4WbjR -uUq5wro7urW7LrunvCG8m70VvY++Cr6Evv+/er/1wHDA7MFnwePCX8Lbw1jD1MRRxM7FS8XIxkbGw8dB -x7/IPci8yTrJuco4yrfLNsu2zDXMtc01zbXONs62zzfPuNA50LrRPNG+0j/SwdNE08bUSdTL1U7V0dZV -1tjXXNfg2GTY6Nls2fHadtr724DcBdyK3RDdlt4c3qLfKd+v4DbgveFE4cziU+Lb42Pj6+Rz5PzlhOYN -5pbnH+ep6DLovOlG6dDqW+rl63Dr++yG7RHtnO4o7rTvQO/M8Fjw5fFy8f/yjPMZ86f0NPTC9VD13vZt -9vv3ivgZ+Kj5OPnH+lf65/t3/Af8mP0p/br+S/7c/23//9IrLC0uWiRjbGFzc25hbWVYJGNsYXNzZXNf -EBBOU0JpdG1hcEltYWdlUmVwoy0vMFpOU0ltYWdlUmVwWE5TT2JqZWN00issMjNXTlNBcnJheaIyMNIr -LDU2Xk5TTXV0YWJsZUFycmF5ozUyMNM4OQ86OzxXTlNXaGl0ZVxOU0NvbG9yU3BhY2VEMCAwABADgAzS -Kyw+P1dOU0NvbG9yoj4w0issQUJXTlNJbWFnZaJBMAAIABEAGgAkACkAMgA3AEkATABRAFMAYgBoAHUA -fACLAJIAnwCmAK4AsACyALQAuQC7AL0AxADJANQA1gDYANoA3wDiAOQA5gDoAO8BBgEiASQBJhOWE5sT -phOvE8ITxhPRE9oT3xPnE+oT7xP+FAIUCRQRFB4UIxQlFCcULBQ0FDcUPBREAAAAAAAAAgEAAAAAAAAA -QwAAAAAAAAAAAAAAAAAAFEc +S2V5ZWRBcmNoaXZlctEICVRyb290gAGuCwwbHCEaJissMzY5P0JVJG51bGzXDQ4PEBESExQVFhcYGRpW +TlNTaXplViRjbGFzc1xOU0ltYWdlRmxhZ3NWTlNSZXBzW05TVGludENvbG9yV05TQ29sb3JeTlNSZXNp +emluZ01vZGWAAoANEiDDAACAA4AAgAsQAFZ7MSwgMX3SHQ4eIFpOUy5vYmplY3RzoR+ABIAK0h0OIiWi +IySABYAGgAnTDicoKSoaXxAUTlNUSUZGUmVwcmVzZW50YXRpb25fEBlOU0ludGVybmFsTGF5b3V0RGly +ZWN0aW9ugAiAB08REmxNTQAqAAAACgAAABABAAADAAAAAQABAAABAQADAAAAAQABAAABAgADAAAAAgAI +AAgBAwADAAAAAQABAAABBgADAAAAAQABAAABCgADAAAAAQABAAABEQAEAAAAAQAAAAgBEgADAAAAAQAB +AAABFQADAAAAAQACAAABFgADAAAAAQABAAABFwAEAAAAAQAAAAIBHAADAAAAAQABAAABKAADAAAAAQAC +AAABUgADAAAAAQABAAABUwADAAAAAgABAAGHcwAHAAARnAAAANAAAAAAAAARnGFwcGwCAAAAbW50ckdS +QVlYWVogB9wACAAXAA8ALgAPYWNzcEFQUEwAAAAAbm9uZQAAAAAAAAAAAAAAAAAAAAAAAPbWAAEAAAAA +0y1hcHBsAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFZGVzYwAA +AMAAAAB5ZHNjbQAAATwAAAgaY3BydAAACVgAAAAjd3RwdAAACXwAAAAUa1RSQwAACZAAAAgMZGVzYwAA +AAAAAAAfR2VuZXJpYyBHcmF5IEdhbW1hIDIuMiBQcm9maWxlAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAG1s +dWMAAAAAAAAAHwAAAAxza1NLAAAALgAAAYRkYURLAAAAOgAAAbJjYUVTAAAAOAAAAex2aVZOAAAAQAAA +AiRwdEJSAAAASgAAAmR1a1VBAAAALAAAAq5mckZVAAAAPgAAAtpodUhVAAAANAAAAxh6aFRXAAAAGgAA +A0xrb0tSAAAAIgAAA2ZuYk5PAAAAOgAAA4hjc0NaAAAAKAAAA8JoZUlMAAAAJAAAA+pyb1JPAAAAKgAA +BA5kZURFAAAATgAABDhpdElUAAAATgAABIZzdlNFAAAAOAAABNR6aENOAAAAGgAABQxqYUpQAAAAJgAA +BSZlbEdSAAAAKgAABUxwdFBPAAAAUgAABXZubE5MAAAAQAAABchlc0VTAAAATAAABgh0aFRIAAAAMgAA +BlR0clRSAAAAJAAABoZmaUZJAAAARgAABqpockhSAAAAPgAABvBwbFBMAAAASgAABy5hckVHAAAALAAA +B3hydVJVAAAAOgAAB6RlblVTAAAAPAAAB94AVgFhAGUAbwBiAGUAYwBuAOEAIABzAGkAdgDhACAAZwBh +AG0AYQAgADIALAAyAEcAZQBuAGUAcgBpAHMAawAgAGcAcgDlACAAMgAsADIAIABnAGEAbQBtAGEALQBw +AHIAbwBmAGkAbABHAGEAbQBtAGEAIABkAGUAIABnAHIAaQBzAG8AcwAgAGcAZQBuAOgAcgBpAGMAYQAg +ADIALgAyAEMepQB1ACAAaADsAG4AaAAgAE0A4AB1ACAAeADhAG0AIABDAGgAdQBuAGcAIABHAGEAbQBt +AGEAIAAyAC4AMgBQAGUAcgBmAGkAbAAgAEcAZQBuAOkAcgBpAGMAbwAgAGQAYQAgAEcAYQBtAGEAIABk +AGUAIABDAGkAbgB6AGEAcwAgADIALAAyBBcEMAQzBDAEOwRMBD0EMAAgAEcAcgBhAHkALQQzBDAEPAQw +ACAAMgAuADIAUAByAG8AZgBpAGwAIABnAOkAbgDpAHIAaQBxAHUAZQAgAGcAcgBpAHMAIABnAGEAbQBt +AGEAIAAyACwAMgDBAGwAdABhAGwA4QBuAG8AcwAgAHMAegD8AHIAawBlACAAZwBhAG0AbQBhACAAMgAu +ADKQGnUocHCWjlFJXqYAMgAuADKCcl9pY8+P8Md8vBgAINaMwMkAIKwQucgAIAAyAC4AMgAg1QS4XNMM +x3wARwBlAG4AZQByAGkAcwBrACAAZwByAOUAIABnAGEAbQBtAGEAIAAyACwAMgAtAHAAcgBvAGYAaQBs +AE8AYgBlAGMAbgDhACABYQBlAGQA4QAgAGcAYQBtAGEAIAAyAC4AMgXSBdAF3gXUACAF0AXkBdUF6AAg +BdsF3AXcBdkAIAAyAC4AMgBHAGEAbQBhACAAZwByAGkAIABnAGUAbgBlAHIAaQBjAQMAIAAyACwAMgBB +AGwAbABnAGUAbQBlAGkAbgBlAHMAIABHAHIAYQB1AHMAdAB1AGYAZQBuAC0AUAByAG8AZgBpAGwAIABH +AGEAbQBtAGEAIAAyACwAMgBQAHIAbwBmAGkAbABvACAAZwByAGkAZwBpAG8AIABnAGUAbgBlAHIAaQBj +AG8AIABkAGUAbABsAGEAIABnAGEAbQBtAGEAIAAyACwAMgBHAGUAbgBlAHIAaQBzAGsAIABnAHIA5QAg +ADIALAAyACAAZwBhAG0AbQBhAHAAcgBvAGYAaQBsZm6QGnBwXqZ8+2VwADIALgAyY8+P8GWHTvZOAIIs +MLAw7DCkMKww8zDeACAAMgAuADIAIDDXMO0w1TChMKQw6wOTA7UDvQO5A7oDzAAgA5MDugPBA7kAIAOT +A6wDvAO8A7EAIAAyAC4AMgBQAGUAcgBmAGkAbAAgAGcAZQBuAOkAcgBpAGMAbwAgAGQAZQAgAGMAaQBu +AHoAZQBuAHQAbwBzACAAZABhACAARwBhAG0AbQBhACAAMgAsADIAQQBsAGcAZQBtAGUAZQBuACAAZwBy +AGkAagBzACAAZwBhAG0AbQBhACAAMgAsADIALQBwAHIAbwBmAGkAZQBsAFAAZQByAGYAaQBsACAAZwBl +AG4A6QByAGkAYwBvACAAZABlACAAZwBhAG0AbQBhACAAZABlACAAZwByAGkAcwBlAHMAIAAyACwAMg4j +DjEOBw4qDjUOQQ4BDiEOIQ4yDkAOAQ4jDiIOTA4XDjEOSA4nDkQOGwAgADIALgAyAEcAZQBuAGUAbAAg +AEcAcgBpACAARwBhAG0AYQAgADIALAAyAFkAbABlAGkAbgBlAG4AIABoAGEAcgBtAGEAYQBuACAAZwBh +AG0AbQBhACAAMgAsADIAIAAtAHAAcgBvAGYAaQBpAGwAaQBHAGUAbgBlAHIAaQENAGsAaQAgAEcAcgBh +AHkAIABHAGEAbQBtAGEAIAAyAC4AMgAgAHAAcgBvAGYAaQBsAFUAbgBpAHcAZQByAHMAYQBsAG4AeQAg +AHAAcgBvAGYAaQBsACAAcwB6AGEAcgBvAVsAYwBpACAAZwBhAG0AbQBhACAAMgAsADIGOgYnBkUGJwAg +ADIALgAyACAGRAZIBkYAIAYxBkUGJwYvBkoAIAY5BicGRQQeBDEESQQwBE8AIARBBDUEQAQwBE8AIAQz +BDAEPAQ8BDAAIAAyACwAMgAtBD8EQAQ+BEQEOAQ7BEwARwBlAG4AZQByAGkAYwAgAEcAcgBhAHkAIABH +AGEAbQBtAGEAIAAyAC4AMgAgAFAAcgBvAGYAaQBsAGUAAHRleHQAAAAAQ29weXJpZ2h0IEFwcGxlIElu +Yy4sIDIwMTIAAFhZWiAAAAAAAADzUQABAAAAARbMY3VydgAAAAAAAAQAAAAABQAKAA8AFAAZAB4AIwAo +AC0AMgA3ADsAQABFAEoATwBUAFkAXgBjAGgAbQByAHcAfACBAIYAiwCQAJUAmgCfAKQAqQCuALIAtwC8 +AMEAxgDLANAA1QDbAOAA5QDrAPAA9gD7AQEBBwENARMBGQEfASUBKwEyATgBPgFFAUwBUgFZAWABZwFu +AXUBfAGDAYsBkgGaAaEBqQGxAbkBwQHJAdEB2QHhAekB8gH6AgMCDAIUAh0CJgIvAjgCQQJLAlQCXQJn +AnECegKEAo4CmAKiAqwCtgLBAssC1QLgAusC9QMAAwsDFgMhAy0DOANDA08DWgNmA3IDfgOKA5YDogOu +A7oDxwPTA+AD7AP5BAYEEwQgBC0EOwRIBFUEYwRxBH4EjASaBKgEtgTEBNME4QTwBP4FDQUcBSsFOgVJ +BVgFZwV3BYYFlgWmBbUFxQXVBeUF9gYGBhYGJwY3BkgGWQZqBnsGjAadBq8GwAbRBuMG9QcHBxkHKwc9 +B08HYQd0B4YHmQesB78H0gflB/gICwgfCDIIRghaCG4IggiWCKoIvgjSCOcI+wkQCSUJOglPCWQJeQmP +CaQJugnPCeUJ+woRCicKPQpUCmoKgQqYCq4KxQrcCvMLCwsiCzkLUQtpC4ALmAuwC8gL4Qv5DBIMKgxD +DFwMdQyODKcMwAzZDPMNDQ0mDUANWg10DY4NqQ3DDd4N+A4TDi4OSQ5kDn8Omw62DtIO7g8JDyUPQQ9e +D3oPlg+zD88P7BAJECYQQxBhEH4QmxC5ENcQ9RETETERTxFtEYwRqhHJEegSBxImEkUSZBKEEqMSwxLj +EwMTIxNDE2MTgxOkE8UT5RQGFCcUSRRqFIsUrRTOFPAVEhU0FVYVeBWbFb0V4BYDFiYWSRZsFo8WshbW +FvoXHRdBF2UXiReuF9IX9xgbGEAYZRiKGK8Y1Rj6GSAZRRlrGZEZtxndGgQaKhpRGncanhrFGuwbFBs7 +G2MbihuyG9ocAhwqHFIcexyjHMwc9R0eHUcdcB2ZHcMd7B4WHkAeah6UHr4e6R8THz4faR+UH78f6iAV +IEEgbCCYIMQg8CEcIUghdSGhIc4h+yInIlUigiKvIt0jCiM4I2YjlCPCI/AkHyRNJHwkqyTaJQklOCVo +JZclxyX3JicmVyaHJrcm6CcYJ0kneierJ9woDSg/KHEooijUKQYpOClrKZ0p0CoCKjUqaCqbKs8rAis2 +K2krnSvRLAUsOSxuLKIs1y0MLUEtdi2rLeEuFi5MLoIuty7uLyQvWi+RL8cv/jA1MGwwpDDbMRIxSjGC +Mbox8jIqMmMymzLUMw0zRjN/M7gz8TQrNGU0njTYNRM1TTWHNcI1/TY3NnI2rjbpNyQ3YDecN9c4FDhQ +OIw4yDkFOUI5fzm8Ofk6Njp0OrI67zstO2s7qjvoPCc8ZTykPOM9Ij1hPaE94D4gPmA+oD7gPyE/YT+i +P+JAI0BkQKZA50EpQWpBrEHuQjBCckK1QvdDOkN9Q8BEA0RHRIpEzkUSRVVFmkXeRiJGZ0arRvBHNUd7 +R8BIBUhLSJFI10kdSWNJqUnwSjdKfUrESwxLU0uaS+JMKkxyTLpNAk1KTZNN3E4lTm5Ot08AT0lPk0/d +UCdQcVC7UQZRUFGbUeZSMVJ8UsdTE1NfU6pT9lRCVI9U21UoVXVVwlYPVlxWqVb3V0RXklfgWC9YfVjL +WRpZaVm4WgdaVlqmWvVbRVuVW+VcNVyGXNZdJ114XcleGl5sXr1fD19hX7NgBWBXYKpg/GFPYaJh9WJJ +Ypxi8GNDY5dj62RAZJRk6WU9ZZJl52Y9ZpJm6Gc9Z5Nn6Wg/aJZo7GlDaZpp8WpIap9q92tPa6dr/2xX +bK9tCG1gbbluEm5rbsRvHm94b9FwK3CGcOBxOnGVcfByS3KmcwFzXXO4dBR0cHTMdSh1hXXhdj52m3b4 +d1Z3s3gReG54zHkqeYl553pGeqV7BHtje8J8IXyBfOF9QX2hfgF+Yn7CfyN/hH/lgEeAqIEKgWuBzYIw +gpKC9INXg7qEHYSAhOOFR4Wrhg6GcobXhzuHn4gEiGmIzokziZmJ/opkisqLMIuWi/yMY4zKjTGNmI3/ +jmaOzo82j56QBpBukNaRP5GokhGSepLjk02TtpQglIqU9JVflcmWNJaflwqXdZfgmEyYuJkkmZCZ/Jpo +mtWbQpuvnByciZz3nWSd0p5Anq6fHZ+Ln/qgaaDYoUehtqImopajBqN2o+akVqTHpTilqaYapoum/adu +p+CoUqjEqTepqaocqo+rAqt1q+msXKzQrUStuK4trqGvFq+LsACwdbDqsWCx1rJLssKzOLOutCW0nLUT +tYq2AbZ5tvC3aLfguFm40blKucK6O7q1uy67p7whvJu9Fb2Pvgq+hL7/v3q/9cBwwOzBZ8Hjwl/C28NY +w9TEUcTOxUvFyMZGxsPHQce/yD3IvMk6ybnKOMq3yzbLtsw1zLXNNc21zjbOts83z7jQOdC60TzRvtI/ +0sHTRNPG1EnUy9VO1dHWVdbY11zX4Nhk2OjZbNnx2nba+9uA3AXcit0Q3ZbeHN6i3ynfr+A24L3hROHM +4lPi2+Nj4+vkc+T85YTmDeaW5x/nqegy6LzpRunQ6lvq5etw6/vshu0R7ZzuKO6070DvzPBY8OXxcvH/ +8ozzGfOn9DT0wvVQ9d72bfb794r4Gfio+Tj5x/pX+uf7d/wH/Jj9Kf26/kv+3P9t///SLS4vMFokY2xh +c3NuYW1lWCRjbGFzc2VzXxAQTlNCaXRtYXBJbWFnZVJlcKMvMTJaTlNJbWFnZVJlcFhOU09iamVjdNIt +LjQ1V05TQXJyYXmiNDLSLS43OF5OU011dGFibGVBcnJheaM3NDLTOjsOPD0+V05TV2hpdGVcTlNDb2xv +clNwYWNlRDAgMAAQA4AM0i0uQEFXTlNDb2xvcqJAMtItLkNEV05TSW1hZ2WiQzIACAARABoAJAApADIA +NwBJAEwAUQBTAGIAaAB3AH4AhQCSAJkApQCtALwAvgDAAMUAxwDJAMsAzQDUANkA5ADmAOgA6gDvAPIA +9AD2APgA/wEWATIBNAE2E6YTqxO2E78T0hPWE+ET6hPvE/cT+hP/FA4UEhQZFCEULhQzFDUUNxQ8FEQU +RxRMFFQAAAAAAAACAQAAAAAAAABFAAAAAAAAAAAAAAAAAAAUVw diff --git a/i18n/src/main/resources/no.lproj/Command.xib b/i18n/src/main/resources/no.lproj/Command.xib index 018c173f01..bd6131ce24 100644 --- a/i18n/src/main/resources/no.lproj/Command.xib +++ b/i18n/src/main/resources/no.lproj/Command.xib @@ -1,7 +1,7 @@ - + - + diff --git a/i18n/src/main/resources/no.lproj/Connection.xib b/i18n/src/main/resources/no.lproj/Connection.xib index e6bd023808..8f21952be9 100644 --- a/i18n/src/main/resources/no.lproj/Connection.xib +++ b/i18n/src/main/resources/no.lproj/Connection.xib @@ -1,7 +1,7 @@ - + - + @@ -76,7 +76,7 @@ - + diff --git a/i18n/src/main/resources/no.lproj/Info.xib b/i18n/src/main/resources/no.lproj/Info.xib index 7fee8a5446..5c4e55600f 100644 --- a/i18n/src/main/resources/no.lproj/Info.xib +++ b/i18n/src/main/resources/no.lproj/Info.xib @@ -1,7 +1,7 @@ - + - + @@ -89,7 +89,7 @@ - + @@ -1017,21 +1017,21 @@ - + - + - + @@ -1121,21 +1121,21 @@ - + - + - + @@ -1250,27 +1250,27 @@ diff --git a/i18n/src/main/resources/no.lproj/Login.strings b/i18n/src/main/resources/no.lproj/Login.strings index 5c65070d79..1bb6c504da 100644 Binary files a/i18n/src/main/resources/no.lproj/Login.strings and b/i18n/src/main/resources/no.lproj/Login.strings differ diff --git a/i18n/src/main/resources/no.lproj/Main.xib b/i18n/src/main/resources/no.lproj/Main.xib index b5b55b7833..22d7fd48eb 100644 --- a/i18n/src/main/resources/no.lproj/Main.xib +++ b/i18n/src/main/resources/no.lproj/Main.xib @@ -1,7 +1,7 @@ - + - + @@ -159,19 +159,19 @@ - + - + - + @@ -321,12 +321,12 @@ CA - + - + @@ -566,6 +566,10 @@ CA - + + + + + diff --git a/i18n/src/main/resources/no.lproj/Preferences.xib b/i18n/src/main/resources/no.lproj/Preferences.xib index b7fa05c2b7..c663d2e887 100644 --- a/i18n/src/main/resources/no.lproj/Preferences.xib +++ b/i18n/src/main/resources/no.lproj/Preferences.xib @@ -1,7 +1,7 @@ - + - + @@ -266,7 +266,7 @@ - + @@ -307,7 +307,7 @@ - + @@ -539,7 +539,7 @@ - + @@ -579,7 +579,7 @@ - + @@ -614,7 +614,7 @@ - + @@ -643,7 +643,7 @@ - + @@ -1219,7 +1219,7 @@ - + @@ -1251,7 +1251,7 @@ - + @@ -1291,7 +1291,7 @@ - + @@ -1364,7 +1364,7 @@ - + @@ -1559,7 +1559,7 @@ - + @@ -1619,7 +1619,7 @@ - + @@ -1644,7 +1644,7 @@ - + @@ -1686,7 +1686,7 @@ - + @@ -1714,7 +1714,7 @@ - + @@ -1742,7 +1742,7 @@ - + @@ -1771,7 +1771,7 @@ - + @@ -1806,7 +1806,7 @@ - + @@ -1890,7 +1890,7 @@ - + @@ -1949,7 +1949,7 @@ - + @@ -1977,7 +1977,7 @@ - + @@ -2006,7 +2006,7 @@ - + diff --git a/i18n/src/main/resources/no.lproj/Profiles.xib b/i18n/src/main/resources/no.lproj/Profiles.xib index f0fa9c52d0..66638a0df1 100644 --- a/i18n/src/main/resources/no.lproj/Profiles.xib +++ b/i18n/src/main/resources/no.lproj/Profiles.xib @@ -1,7 +1,7 @@ - + - + diff --git a/i18n/src/main/resources/no.lproj/Prompt.xib b/i18n/src/main/resources/no.lproj/Prompt.xib index 608e8158e0..99af192e0c 100644 --- a/i18n/src/main/resources/no.lproj/Prompt.xib +++ b/i18n/src/main/resources/no.lproj/Prompt.xib @@ -1,7 +1,7 @@ - + - + @@ -95,7 +95,7 @@ Gw - + diff --git a/i18n/src/main/resources/no.lproj/S3.strings b/i18n/src/main/resources/no.lproj/S3.strings index 33245e29e8..6ecf92efce 100644 Binary files a/i18n/src/main/resources/no.lproj/S3.strings and b/i18n/src/main/resources/no.lproj/S3.strings differ diff --git a/i18n/src/main/resources/no.lproj/Updater.strings b/i18n/src/main/resources/no.lproj/Updater.strings index 49b86e558e..ea2ff9166c 100644 Binary files a/i18n/src/main/resources/no.lproj/Updater.strings and b/i18n/src/main/resources/no.lproj/Updater.strings differ diff --git a/i18n/src/main/resources/pl.lproj/Bookmark.xib b/i18n/src/main/resources/pl.lproj/Bookmark.xib index e613d7c7e8..b911722dc6 100644 --- a/i18n/src/main/resources/pl.lproj/Bookmark.xib +++ b/i18n/src/main/resources/pl.lproj/Bookmark.xib @@ -1,7 +1,7 @@ - + - + @@ -92,7 +92,7 @@ - + @@ -489,95 +489,95 @@ YnBsaXN0MDDUAQIDBAUGBwpYJHZlcnNpb25ZJGFyY2hpdmVyVCR0b3BYJG9iamVjdHMSAAGGoF8QD05T -S2V5ZWRBcmNoaXZlctEICVRyb290gAGuCwwZGh8UJCkqMTQ3PUBVJG51bGzWDQ4PEBESExQVFhcYVk5T -U2l6ZV5OU1Jlc2l6aW5nTW9kZVYkY2xhc3NcTlNJbWFnZUZsYWdzVk5TUmVwc1dOU0NvbG9ygAIQAIAN -EiDDAACAA4ALVnsxLCAxfdIbDxweWk5TLm9iamVjdHOhHYAEgArSGw8gI6IhIoAFgAaACdMPJSYnKBRf -EBROU1RJRkZSZXByZXNlbnRhdGlvbl8QGU5TSW50ZXJuYWxMYXlvdXREaXJlY3Rpb26ACIAHTxESbE1N -ACoAAAAKAAAAEAEAAAMAAAABAAEAAAEBAAMAAAABAAEAAAECAAMAAAACAAgACAEDAAMAAAABAAEAAAEG -AAMAAAABAAEAAAEKAAMAAAABAAEAAAERAAQAAAABAAAACAESAAMAAAABAAEAAAEVAAMAAAABAAIAAAEW -AAMAAAABAAEAAAEXAAQAAAABAAAAAgEcAAMAAAABAAEAAAEoAAMAAAABAAIAAAFSAAMAAAABAAEAAAFT -AAMAAAACAAEAAYdzAAcAABGcAAAA0AAAAAAAABGcYXBwbAIAAABtbnRyR1JBWVhZWiAH3AAIABcADwAu -AA9hY3NwQVBQTAAAAABub25lAAAAAAAAAAAAAAAAAAAAAAAA9tYAAQAAAADTLWFwcGwAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAVkZXNjAAAAwAAAAHlkc2NtAAABPAAA -CBpjcHJ0AAAJWAAAACN3dHB0AAAJfAAAABRrVFJDAAAJkAAACAxkZXNjAAAAAAAAAB9HZW5lcmljIEdy -YXkgR2FtbWEgMi4yIFByb2ZpbGUAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAbWx1YwAAAAAAAAAfAAAADHNr -U0sAAAAuAAABhGRhREsAAAA6AAABsmNhRVMAAAA4AAAB7HZpVk4AAABAAAACJHB0QlIAAABKAAACZHVr -VUEAAAAsAAACrmZyRlUAAAA+AAAC2mh1SFUAAAA0AAADGHpoVFcAAAAaAAADTGtvS1IAAAAiAAADZm5i -Tk8AAAA6AAADiGNzQ1oAAAAoAAADwmhlSUwAAAAkAAAD6nJvUk8AAAAqAAAEDmRlREUAAABOAAAEOGl0 -SVQAAABOAAAEhnN2U0UAAAA4AAAE1HpoQ04AAAAaAAAFDGphSlAAAAAmAAAFJmVsR1IAAAAqAAAFTHB0 -UE8AAABSAAAFdm5sTkwAAABAAAAFyGVzRVMAAABMAAAGCHRoVEgAAAAyAAAGVHRyVFIAAAAkAAAGhmZp -RkkAAABGAAAGqmhySFIAAAA+AAAG8HBsUEwAAABKAAAHLmFyRUcAAAAsAAAHeHJ1UlUAAAA6AAAHpGVu -VVMAAAA8AAAH3gBWAWEAZQBvAGIAZQBjAG4A4QAgAHMAaQB2AOEAIABnAGEAbQBhACAAMgAsADIARwBl -AG4AZQByAGkAcwBrACAAZwByAOUAIAAyACwAMgAgAGcAYQBtAG0AYQAtAHAAcgBvAGYAaQBsAEcAYQBt -AG0AYQAgAGQAZQAgAGcAcgBpAHMAbwBzACAAZwBlAG4A6AByAGkAYwBhACAAMgAuADIAQx6lAHUAIABo -AOwAbgBoACAATQDgAHUAIAB4AOEAbQAgAEMAaAB1AG4AZwAgAEcAYQBtAG0AYQAgADIALgAyAFAAZQBy -AGYAaQBsACAARwBlAG4A6QByAGkAYwBvACAAZABhACAARwBhAG0AYQAgAGQAZQAgAEMAaQBuAHoAYQBz -ACAAMgAsADIEFwQwBDMEMAQ7BEwEPQQwACAARwByAGEAeQAtBDMEMAQ8BDAAIAAyAC4AMgBQAHIAbwBm -AGkAbAAgAGcA6QBuAOkAcgBpAHEAdQBlACAAZwByAGkAcwAgAGcAYQBtAG0AYQAgADIALAAyAMEAbAB0 -AGEAbADhAG4AbwBzACAAcwB6APwAcgBrAGUAIABnAGEAbQBtAGEAIAAyAC4AMpAadShwcJaOUUlepgAy -AC4AMoJyX2ljz4/wx3y8GAAg1ozAyQAgrBC5yAAgADIALgAyACDVBLhc0wzHfABHAGUAbgBlAHIAaQBz -AGsAIABnAHIA5QAgAGcAYQBtAG0AYQAgADIALAAyAC0AcAByAG8AZgBpAGwATwBiAGUAYwBuAOEAIAFh -AGUAZADhACAAZwBhAG0AYQAgADIALgAyBdIF0AXeBdQAIAXQBeQF1QXoACAF2wXcBdwF2QAgADIALgAy -AEcAYQBtAGEAIABnAHIAaQAgAGcAZQBuAGUAcgBpAGMBAwAgADIALAAyAEEAbABsAGcAZQBtAGUAaQBu -AGUAcwAgAEcAcgBhAHUAcwB0AHUAZgBlAG4ALQBQAHIAbwBmAGkAbAAgAEcAYQBtAG0AYQAgADIALAAy -AFAAcgBvAGYAaQBsAG8AIABnAHIAaQBnAGkAbwAgAGcAZQBuAGUAcgBpAGMAbwAgAGQAZQBsAGwAYQAg -AGcAYQBtAG0AYQAgADIALAAyAEcAZQBuAGUAcgBpAHMAawAgAGcAcgDlACAAMgAsADIAIABnAGEAbQBt -AGEAcAByAG8AZgBpAGxmbpAacHBepnz7ZXAAMgAuADJjz4/wZYdO9k4AgiwwsDDsMKQwrDDzMN4AIAAy -AC4AMgAgMNcw7TDVMKEwpDDrA5MDtQO9A7kDugPMACADkwO6A8EDuQAgA5MDrAO8A7wDsQAgADIALgAy -AFAAZQByAGYAaQBsACAAZwBlAG4A6QByAGkAYwBvACAAZABlACAAYwBpAG4AegBlAG4AdABvAHMAIABk -AGEAIABHAGEAbQBtAGEAIAAyACwAMgBBAGwAZwBlAG0AZQBlAG4AIABnAHIAaQBqAHMAIABnAGEAbQBt -AGEAIAAyACwAMgAtAHAAcgBvAGYAaQBlAGwAUABlAHIAZgBpAGwAIABnAGUAbgDpAHIAaQBjAG8AIABk -AGUAIABnAGEAbQBtAGEAIABkAGUAIABnAHIAaQBzAGUAcwAgADIALAAyDiMOMQ4HDioONQ5BDgEOIQ4h -DjIOQA4BDiMOIg5MDhcOMQ5IDicORA4bACAAMgAuADIARwBlAG4AZQBsACAARwByAGkAIABHAGEAbQBh -ACAAMgAsADIAWQBsAGUAaQBuAGUAbgAgAGgAYQByAG0AYQBhAG4AIABnAGEAbQBtAGEAIAAyACwAMgAg -AC0AcAByAG8AZgBpAGkAbABpAEcAZQBuAGUAcgBpAQ0AawBpACAARwByAGEAeQAgAEcAYQBtAG0AYQAg -ADIALgAyACAAcAByAG8AZgBpAGwAVQBuAGkAdwBlAHIAcwBhAGwAbgB5ACAAcAByAG8AZgBpAGwAIABz -AHoAYQByAG8BWwBjAGkAIABnAGEAbQBtAGEAIAAyACwAMgY6BicGRQYnACAAMgAuADIAIAZEBkgGRgAg -BjEGRQYnBi8GSgAgBjkGJwZFBB4EMQRJBDAETwAgBEEENQRABDAETwAgBDMEMAQ8BDwEMAAgADIALAAy -AC0EPwRABD4ERAQ4BDsETABHAGUAbgBlAHIAaQBjACAARwByAGEAeQAgAEcAYQBtAG0AYQAgADIALgAy -ACAAUAByAG8AZgBpAGwAZQAAdGV4dAAAAABDb3B5cmlnaHQgQXBwbGUgSW5jLiwgMjAxMgAAWFlaIAAA -AAAAAPNRAAEAAAABFsxjdXJ2AAAAAAAABAAAAAAFAAoADwAUABkAHgAjACgALQAyADcAOwBAAEUASgBP -AFQAWQBeAGMAaABtAHIAdwB8AIEAhgCLAJAAlQCaAJ8ApACpAK4AsgC3ALwAwQDGAMsA0ADVANsA4ADl -AOsA8AD2APsBAQEHAQ0BEwEZAR8BJQErATIBOAE+AUUBTAFSAVkBYAFnAW4BdQF8AYMBiwGSAZoBoQGp -AbEBuQHBAckB0QHZAeEB6QHyAfoCAwIMAhQCHQImAi8COAJBAksCVAJdAmcCcQJ6AoQCjgKYAqICrAK2 -AsECywLVAuAC6wL1AwADCwMWAyEDLQM4A0MDTwNaA2YDcgN+A4oDlgOiA64DugPHA9MD4APsA/kEBgQT -BCAELQQ7BEgEVQRjBHEEfgSMBJoEqAS2BMQE0wThBPAE/gUNBRwFKwU6BUkFWAVnBXcFhgWWBaYFtQXF -BdUF5QX2BgYGFgYnBjcGSAZZBmoGewaMBp0GrwbABtEG4wb1BwcHGQcrBz0HTwdhB3QHhgeZB6wHvwfS -B+UH+AgLCB8IMghGCFoIbgiCCJYIqgi+CNII5wj7CRAJJQk6CU8JZAl5CY8JpAm6Cc8J5Qn7ChEKJwo9 -ClQKagqBCpgKrgrFCtwK8wsLCyILOQtRC2kLgAuYC7ALyAvhC/kMEgwqDEMMXAx1DI4MpwzADNkM8w0N -DSYNQA1aDXQNjg2pDcMN3g34DhMOLg5JDmQOfw6bDrYO0g7uDwkPJQ9BD14Peg+WD7MPzw/sEAkQJhBD -EGEQfhCbELkQ1xD1ERMRMRFPEW0RjBGqEckR6BIHEiYSRRJkEoQSoxLDEuMTAxMjE0MTYxODE6QTxRPl -FAYUJxRJFGoUixStFM4U8BUSFTQVVhV4FZsVvRXgFgMWJhZJFmwWjxayFtYW+hcdF0EXZReJF64X0hf3 -GBsYQBhlGIoYrxjVGPoZIBlFGWsZkRm3Gd0aBBoqGlEadxqeGsUa7BsUGzsbYxuKG7Ib2hwCHCocUhx7 -HKMczBz1HR4dRx1wHZkdwx3sHhYeQB5qHpQevh7pHxMfPh9pH5Qfvx/qIBUgQSBsIJggxCDwIRwhSCF1 -IaEhziH7IiciVSKCIq8i3SMKIzgjZiOUI8Ij8CQfJE0kfCSrJNolCSU4JWgllyXHJfcmJyZXJocmtybo -JxgnSSd6J6sn3CgNKD8ocSiiKNQpBik4KWspnSnQKgIqNSpoKpsqzysCKzYraSudK9EsBSw5LG4soizX -LQwtQS12Last4S4WLkwugi63Lu4vJC9aL5Evxy/+MDUwbDCkMNsxEjFKMYIxujHyMioyYzKbMtQzDTNG -M38zuDPxNCs0ZTSeNNg1EzVNNYc1wjX9Njc2cjauNuk3JDdgN5w31zgUOFA4jDjIOQU5Qjl/Obw5+To2 -OnQ6sjrvOy07azuqO+g8JzxlPKQ84z0iPWE9oT3gPiA+YD6gPuA/IT9hP6I/4kAjQGRApkDnQSlBakGs -Qe5CMEJyQrVC90M6Q31DwEQDREdEikTORRJFVUWaRd5GIkZnRqtG8Ec1R3tHwEgFSEtIkUjXSR1JY0mp -SfBKN0p9SsRLDEtTS5pL4kwqTHJMuk0CTUpNk03cTiVObk63TwBPSU+TT91QJ1BxULtRBlFQUZtR5lIx -UnxSx1MTU19TqlP2VEJUj1TbVShVdVXCVg9WXFapVvdXRFeSV+BYL1h9WMtZGllpWbhaB1pWWqZa9VtF -W5Vb5Vw1XIZc1l0nXXhdyV4aXmxevV8PX2Ffs2AFYFdgqmD8YU9homH1YklinGLwY0Njl2PrZEBklGTp -ZT1lkmXnZj1mkmboZz1nk2fpaD9olmjsaUNpmmnxakhqn2r3a09rp2v/bFdsr20IbWBtuW4SbmtuxG8e -b3hv0XArcIZw4HE6cZVx8HJLcqZzAXNdc7h0FHRwdMx1KHWFdeF2Pnabdvh3VnezeBF4bnjMeSp5iXnn -ekZ6pXsEe2N7wnwhfIF84X1BfaF+AX5ifsJ/I3+Ef+WAR4CogQqBa4HNgjCCkoL0g1eDuoQdhICE44VH -hauGDoZyhteHO4efiASIaYjOiTOJmYn+imSKyoswi5aL/IxjjMqNMY2Yjf+OZo7OjzaPnpAGkG6Q1pE/ -kaiSEZJ6kuOTTZO2lCCUipT0lV+VyZY0lp+XCpd1l+CYTJi4mSSZkJn8mmia1ZtCm6+cHJyJnPedZJ3S -nkCerp8dn4uf+qBpoNihR6G2oiailqMGo3aj5qRWpMelOKWpphqmi6b9p26n4KhSqMSpN6mpqhyqj6sC -q3Wr6axcrNCtRK24ri2uoa8Wr4uwALB1sOqxYLHWskuywrM4s660JbSctRO1irYBtnm28Ldot+C4WbjR -uUq5wro7urW7LrunvCG8m70VvY++Cr6Evv+/er/1wHDA7MFnwePCX8Lbw1jD1MRRxM7FS8XIxkbGw8dB -x7/IPci8yTrJuco4yrfLNsu2zDXMtc01zbXONs62zzfPuNA50LrRPNG+0j/SwdNE08bUSdTL1U7V0dZV -1tjXXNfg2GTY6Nls2fHadtr724DcBdyK3RDdlt4c3qLfKd+v4DbgveFE4cziU+Lb42Pj6+Rz5PzlhOYN -5pbnH+ep6DLovOlG6dDqW+rl63Dr++yG7RHtnO4o7rTvQO/M8Fjw5fFy8f/yjPMZ86f0NPTC9VD13vZt -9vv3ivgZ+Kj5OPnH+lf65/t3/Af8mP0p/br+S/7c/23//9IrLC0uWiRjbGFzc25hbWVYJGNsYXNzZXNf -EBBOU0JpdG1hcEltYWdlUmVwoy0vMFpOU0ltYWdlUmVwWE5TT2JqZWN00issMjNXTlNBcnJheaIyMNIr -LDU2Xk5TTXV0YWJsZUFycmF5ozUyMNM4OQ86OzxXTlNXaGl0ZVxOU0NvbG9yU3BhY2VEMCAwABADgAzS -Kyw+P1dOU0NvbG9yoj4w0issQUJXTlNJbWFnZaJBMAAIABEAGgAkACkAMgA3AEkATABRAFMAYgBoAHUA -fACLAJIAnwCmAK4AsACyALQAuQC7AL0AxADJANQA1gDYANoA3wDiAOQA5gDoAO8BBgEiASQBJhOWE5sT -phOvE8ITxhPRE9oT3xPnE+oT7xP+FAIUCRQRFB4UIxQlFCcULBQ0FDcUPBREAAAAAAAAAgEAAAAAAAAA -QwAAAAAAAAAAAAAAAAAAFEc +S2V5ZWRBcmNoaXZlctEICVRyb290gAGuCwwbHCEaJissMzY5P0JVJG51bGzXDQ4PEBESExQVFhcYGRpW +TlNTaXplViRjbGFzc1xOU0ltYWdlRmxhZ3NWTlNSZXBzW05TVGludENvbG9yV05TQ29sb3JeTlNSZXNp +emluZ01vZGWAAoANEiDDAACAA4AAgAsQAFZ7MSwgMX3SHQ4eIFpOUy5vYmplY3RzoR+ABIAK0h0OIiWi +IySABYAGgAnTDicoKSoaXxAUTlNUSUZGUmVwcmVzZW50YXRpb25fEBlOU0ludGVybmFsTGF5b3V0RGly +ZWN0aW9ugAiAB08REmxNTQAqAAAACgAAABABAAADAAAAAQABAAABAQADAAAAAQABAAABAgADAAAAAgAI +AAgBAwADAAAAAQABAAABBgADAAAAAQABAAABCgADAAAAAQABAAABEQAEAAAAAQAAAAgBEgADAAAAAQAB +AAABFQADAAAAAQACAAABFgADAAAAAQABAAABFwAEAAAAAQAAAAIBHAADAAAAAQABAAABKAADAAAAAQAC +AAABUgADAAAAAQABAAABUwADAAAAAgABAAGHcwAHAAARnAAAANAAAAAAAAARnGFwcGwCAAAAbW50ckdS +QVlYWVogB9wACAAXAA8ALgAPYWNzcEFQUEwAAAAAbm9uZQAAAAAAAAAAAAAAAAAAAAAAAPbWAAEAAAAA +0y1hcHBsAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFZGVzYwAA +AMAAAAB5ZHNjbQAAATwAAAgaY3BydAAACVgAAAAjd3RwdAAACXwAAAAUa1RSQwAACZAAAAgMZGVzYwAA +AAAAAAAfR2VuZXJpYyBHcmF5IEdhbW1hIDIuMiBQcm9maWxlAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAG1s +dWMAAAAAAAAAHwAAAAxza1NLAAAALgAAAYRkYURLAAAAOgAAAbJjYUVTAAAAOAAAAex2aVZOAAAAQAAA +AiRwdEJSAAAASgAAAmR1a1VBAAAALAAAAq5mckZVAAAAPgAAAtpodUhVAAAANAAAAxh6aFRXAAAAGgAA +A0xrb0tSAAAAIgAAA2ZuYk5PAAAAOgAAA4hjc0NaAAAAKAAAA8JoZUlMAAAAJAAAA+pyb1JPAAAAKgAA +BA5kZURFAAAATgAABDhpdElUAAAATgAABIZzdlNFAAAAOAAABNR6aENOAAAAGgAABQxqYUpQAAAAJgAA +BSZlbEdSAAAAKgAABUxwdFBPAAAAUgAABXZubE5MAAAAQAAABchlc0VTAAAATAAABgh0aFRIAAAAMgAA +BlR0clRSAAAAJAAABoZmaUZJAAAARgAABqpockhSAAAAPgAABvBwbFBMAAAASgAABy5hckVHAAAALAAA +B3hydVJVAAAAOgAAB6RlblVTAAAAPAAAB94AVgFhAGUAbwBiAGUAYwBuAOEAIABzAGkAdgDhACAAZwBh +AG0AYQAgADIALAAyAEcAZQBuAGUAcgBpAHMAawAgAGcAcgDlACAAMgAsADIAIABnAGEAbQBtAGEALQBw +AHIAbwBmAGkAbABHAGEAbQBtAGEAIABkAGUAIABnAHIAaQBzAG8AcwAgAGcAZQBuAOgAcgBpAGMAYQAg +ADIALgAyAEMepQB1ACAAaADsAG4AaAAgAE0A4AB1ACAAeADhAG0AIABDAGgAdQBuAGcAIABHAGEAbQBt +AGEAIAAyAC4AMgBQAGUAcgBmAGkAbAAgAEcAZQBuAOkAcgBpAGMAbwAgAGQAYQAgAEcAYQBtAGEAIABk +AGUAIABDAGkAbgB6AGEAcwAgADIALAAyBBcEMAQzBDAEOwRMBD0EMAAgAEcAcgBhAHkALQQzBDAEPAQw +ACAAMgAuADIAUAByAG8AZgBpAGwAIABnAOkAbgDpAHIAaQBxAHUAZQAgAGcAcgBpAHMAIABnAGEAbQBt +AGEAIAAyACwAMgDBAGwAdABhAGwA4QBuAG8AcwAgAHMAegD8AHIAawBlACAAZwBhAG0AbQBhACAAMgAu +ADKQGnUocHCWjlFJXqYAMgAuADKCcl9pY8+P8Md8vBgAINaMwMkAIKwQucgAIAAyAC4AMgAg1QS4XNMM +x3wARwBlAG4AZQByAGkAcwBrACAAZwByAOUAIABnAGEAbQBtAGEAIAAyACwAMgAtAHAAcgBvAGYAaQBs +AE8AYgBlAGMAbgDhACABYQBlAGQA4QAgAGcAYQBtAGEAIAAyAC4AMgXSBdAF3gXUACAF0AXkBdUF6AAg +BdsF3AXcBdkAIAAyAC4AMgBHAGEAbQBhACAAZwByAGkAIABnAGUAbgBlAHIAaQBjAQMAIAAyACwAMgBB +AGwAbABnAGUAbQBlAGkAbgBlAHMAIABHAHIAYQB1AHMAdAB1AGYAZQBuAC0AUAByAG8AZgBpAGwAIABH +AGEAbQBtAGEAIAAyACwAMgBQAHIAbwBmAGkAbABvACAAZwByAGkAZwBpAG8AIABnAGUAbgBlAHIAaQBj +AG8AIABkAGUAbABsAGEAIABnAGEAbQBtAGEAIAAyACwAMgBHAGUAbgBlAHIAaQBzAGsAIABnAHIA5QAg +ADIALAAyACAAZwBhAG0AbQBhAHAAcgBvAGYAaQBsZm6QGnBwXqZ8+2VwADIALgAyY8+P8GWHTvZOAIIs +MLAw7DCkMKww8zDeACAAMgAuADIAIDDXMO0w1TChMKQw6wOTA7UDvQO5A7oDzAAgA5MDugPBA7kAIAOT +A6wDvAO8A7EAIAAyAC4AMgBQAGUAcgBmAGkAbAAgAGcAZQBuAOkAcgBpAGMAbwAgAGQAZQAgAGMAaQBu +AHoAZQBuAHQAbwBzACAAZABhACAARwBhAG0AbQBhACAAMgAsADIAQQBsAGcAZQBtAGUAZQBuACAAZwBy +AGkAagBzACAAZwBhAG0AbQBhACAAMgAsADIALQBwAHIAbwBmAGkAZQBsAFAAZQByAGYAaQBsACAAZwBl +AG4A6QByAGkAYwBvACAAZABlACAAZwBhAG0AbQBhACAAZABlACAAZwByAGkAcwBlAHMAIAAyACwAMg4j +DjEOBw4qDjUOQQ4BDiEOIQ4yDkAOAQ4jDiIOTA4XDjEOSA4nDkQOGwAgADIALgAyAEcAZQBuAGUAbAAg +AEcAcgBpACAARwBhAG0AYQAgADIALAAyAFkAbABlAGkAbgBlAG4AIABoAGEAcgBtAGEAYQBuACAAZwBh +AG0AbQBhACAAMgAsADIAIAAtAHAAcgBvAGYAaQBpAGwAaQBHAGUAbgBlAHIAaQENAGsAaQAgAEcAcgBh +AHkAIABHAGEAbQBtAGEAIAAyAC4AMgAgAHAAcgBvAGYAaQBsAFUAbgBpAHcAZQByAHMAYQBsAG4AeQAg +AHAAcgBvAGYAaQBsACAAcwB6AGEAcgBvAVsAYwBpACAAZwBhAG0AbQBhACAAMgAsADIGOgYnBkUGJwAg +ADIALgAyACAGRAZIBkYAIAYxBkUGJwYvBkoAIAY5BicGRQQeBDEESQQwBE8AIARBBDUEQAQwBE8AIAQz +BDAEPAQ8BDAAIAAyACwAMgAtBD8EQAQ+BEQEOAQ7BEwARwBlAG4AZQByAGkAYwAgAEcAcgBhAHkAIABH +AGEAbQBtAGEAIAAyAC4AMgAgAFAAcgBvAGYAaQBsAGUAAHRleHQAAAAAQ29weXJpZ2h0IEFwcGxlIElu +Yy4sIDIwMTIAAFhZWiAAAAAAAADzUQABAAAAARbMY3VydgAAAAAAAAQAAAAABQAKAA8AFAAZAB4AIwAo +AC0AMgA3ADsAQABFAEoATwBUAFkAXgBjAGgAbQByAHcAfACBAIYAiwCQAJUAmgCfAKQAqQCuALIAtwC8 +AMEAxgDLANAA1QDbAOAA5QDrAPAA9gD7AQEBBwENARMBGQEfASUBKwEyATgBPgFFAUwBUgFZAWABZwFu +AXUBfAGDAYsBkgGaAaEBqQGxAbkBwQHJAdEB2QHhAekB8gH6AgMCDAIUAh0CJgIvAjgCQQJLAlQCXQJn +AnECegKEAo4CmAKiAqwCtgLBAssC1QLgAusC9QMAAwsDFgMhAy0DOANDA08DWgNmA3IDfgOKA5YDogOu +A7oDxwPTA+AD7AP5BAYEEwQgBC0EOwRIBFUEYwRxBH4EjASaBKgEtgTEBNME4QTwBP4FDQUcBSsFOgVJ +BVgFZwV3BYYFlgWmBbUFxQXVBeUF9gYGBhYGJwY3BkgGWQZqBnsGjAadBq8GwAbRBuMG9QcHBxkHKwc9 +B08HYQd0B4YHmQesB78H0gflB/gICwgfCDIIRghaCG4IggiWCKoIvgjSCOcI+wkQCSUJOglPCWQJeQmP +CaQJugnPCeUJ+woRCicKPQpUCmoKgQqYCq4KxQrcCvMLCwsiCzkLUQtpC4ALmAuwC8gL4Qv5DBIMKgxD +DFwMdQyODKcMwAzZDPMNDQ0mDUANWg10DY4NqQ3DDd4N+A4TDi4OSQ5kDn8Omw62DtIO7g8JDyUPQQ9e +D3oPlg+zD88P7BAJECYQQxBhEH4QmxC5ENcQ9RETETERTxFtEYwRqhHJEegSBxImEkUSZBKEEqMSwxLj +EwMTIxNDE2MTgxOkE8UT5RQGFCcUSRRqFIsUrRTOFPAVEhU0FVYVeBWbFb0V4BYDFiYWSRZsFo8WshbW +FvoXHRdBF2UXiReuF9IX9xgbGEAYZRiKGK8Y1Rj6GSAZRRlrGZEZtxndGgQaKhpRGncanhrFGuwbFBs7 +G2MbihuyG9ocAhwqHFIcexyjHMwc9R0eHUcdcB2ZHcMd7B4WHkAeah6UHr4e6R8THz4faR+UH78f6iAV +IEEgbCCYIMQg8CEcIUghdSGhIc4h+yInIlUigiKvIt0jCiM4I2YjlCPCI/AkHyRNJHwkqyTaJQklOCVo +JZclxyX3JicmVyaHJrcm6CcYJ0kneierJ9woDSg/KHEooijUKQYpOClrKZ0p0CoCKjUqaCqbKs8rAis2 +K2krnSvRLAUsOSxuLKIs1y0MLUEtdi2rLeEuFi5MLoIuty7uLyQvWi+RL8cv/jA1MGwwpDDbMRIxSjGC +Mbox8jIqMmMymzLUMw0zRjN/M7gz8TQrNGU0njTYNRM1TTWHNcI1/TY3NnI2rjbpNyQ3YDecN9c4FDhQ +OIw4yDkFOUI5fzm8Ofk6Njp0OrI67zstO2s7qjvoPCc8ZTykPOM9Ij1hPaE94D4gPmA+oD7gPyE/YT+i +P+JAI0BkQKZA50EpQWpBrEHuQjBCckK1QvdDOkN9Q8BEA0RHRIpEzkUSRVVFmkXeRiJGZ0arRvBHNUd7 +R8BIBUhLSJFI10kdSWNJqUnwSjdKfUrESwxLU0uaS+JMKkxyTLpNAk1KTZNN3E4lTm5Ot08AT0lPk0/d +UCdQcVC7UQZRUFGbUeZSMVJ8UsdTE1NfU6pT9lRCVI9U21UoVXVVwlYPVlxWqVb3V0RXklfgWC9YfVjL +WRpZaVm4WgdaVlqmWvVbRVuVW+VcNVyGXNZdJ114XcleGl5sXr1fD19hX7NgBWBXYKpg/GFPYaJh9WJJ +Ypxi8GNDY5dj62RAZJRk6WU9ZZJl52Y9ZpJm6Gc9Z5Nn6Wg/aJZo7GlDaZpp8WpIap9q92tPa6dr/2xX +bK9tCG1gbbluEm5rbsRvHm94b9FwK3CGcOBxOnGVcfByS3KmcwFzXXO4dBR0cHTMdSh1hXXhdj52m3b4 +d1Z3s3gReG54zHkqeYl553pGeqV7BHtje8J8IXyBfOF9QX2hfgF+Yn7CfyN/hH/lgEeAqIEKgWuBzYIw +gpKC9INXg7qEHYSAhOOFR4Wrhg6GcobXhzuHn4gEiGmIzokziZmJ/opkisqLMIuWi/yMY4zKjTGNmI3/ +jmaOzo82j56QBpBukNaRP5GokhGSepLjk02TtpQglIqU9JVflcmWNJaflwqXdZfgmEyYuJkkmZCZ/Jpo +mtWbQpuvnByciZz3nWSd0p5Anq6fHZ+Ln/qgaaDYoUehtqImopajBqN2o+akVqTHpTilqaYapoum/adu +p+CoUqjEqTepqaocqo+rAqt1q+msXKzQrUStuK4trqGvFq+LsACwdbDqsWCx1rJLssKzOLOutCW0nLUT +tYq2AbZ5tvC3aLfguFm40blKucK6O7q1uy67p7whvJu9Fb2Pvgq+hL7/v3q/9cBwwOzBZ8Hjwl/C28NY +w9TEUcTOxUvFyMZGxsPHQce/yD3IvMk6ybnKOMq3yzbLtsw1zLXNNc21zjbOts83z7jQOdC60TzRvtI/ +0sHTRNPG1EnUy9VO1dHWVdbY11zX4Nhk2OjZbNnx2nba+9uA3AXcit0Q3ZbeHN6i3ynfr+A24L3hROHM +4lPi2+Nj4+vkc+T85YTmDeaW5x/nqegy6LzpRunQ6lvq5etw6/vshu0R7ZzuKO6070DvzPBY8OXxcvH/ +8ozzGfOn9DT0wvVQ9d72bfb794r4Gfio+Tj5x/pX+uf7d/wH/Jj9Kf26/kv+3P9t///SLS4vMFokY2xh +c3NuYW1lWCRjbGFzc2VzXxAQTlNCaXRtYXBJbWFnZVJlcKMvMTJaTlNJbWFnZVJlcFhOU09iamVjdNIt +LjQ1V05TQXJyYXmiNDLSLS43OF5OU011dGFibGVBcnJheaM3NDLTOjsOPD0+V05TV2hpdGVcTlNDb2xv +clNwYWNlRDAgMAAQA4AM0i0uQEFXTlNDb2xvcqJAMtItLkNEV05TSW1hZ2WiQzIACAARABoAJAApADIA +NwBJAEwAUQBTAGIAaAB3AH4AhQCSAJkApQCtALwAvgDAAMUAxwDJAMsAzQDUANkA5ADmAOgA6gDvAPIA +9AD2APgA/wEWATIBNAE2E6YTqxO2E78T0hPWE+ET6hPvE/cT+hP/FA4UEhQZFCEULhQzFDUUNxQ8FEQU +RxRMFFQAAAAAAAACAQAAAAAAAABFAAAAAAAAAAAAAAAAAAAUVw diff --git a/i18n/src/main/resources/pl.lproj/Browser.xib b/i18n/src/main/resources/pl.lproj/Browser.xib index 968cc25e92..098c730b98 100644 --- a/i18n/src/main/resources/pl.lproj/Browser.xib +++ b/i18n/src/main/resources/pl.lproj/Browser.xib @@ -1,7 +1,7 @@ - + - + @@ -38,7 +38,7 @@ - + @@ -62,27 +62,27 @@ @@ -256,7 +256,7 @@ - + @@ -553,95 +553,95 @@ DQ YnBsaXN0MDDUAQIDBAUGBwpYJHZlcnNpb25ZJGFyY2hpdmVyVCR0b3BYJG9iamVjdHMSAAGGoF8QD05T -S2V5ZWRBcmNoaXZlctEICVRyb290gAGuCwwZGh8UJCkqMTQ3PUBVJG51bGzWDQ4PEBESExQVFhcYVk5T -U2l6ZV5OU1Jlc2l6aW5nTW9kZVYkY2xhc3NcTlNJbWFnZUZsYWdzVk5TUmVwc1dOU0NvbG9ygAIQAIAN -EiDDAACAA4ALVnsxLCAxfdIbDxweWk5TLm9iamVjdHOhHYAEgArSGw8gI6IhIoAFgAaACdMPJSYnKBRf -EBROU1RJRkZSZXByZXNlbnRhdGlvbl8QGU5TSW50ZXJuYWxMYXlvdXREaXJlY3Rpb26ACIAHTxESbE1N -ACoAAAAKAAAAEAEAAAMAAAABAAEAAAEBAAMAAAABAAEAAAECAAMAAAACAAgACAEDAAMAAAABAAEAAAEG -AAMAAAABAAEAAAEKAAMAAAABAAEAAAERAAQAAAABAAAACAESAAMAAAABAAEAAAEVAAMAAAABAAIAAAEW -AAMAAAABAAEAAAEXAAQAAAABAAAAAgEcAAMAAAABAAEAAAEoAAMAAAABAAIAAAFSAAMAAAABAAEAAAFT -AAMAAAACAAEAAYdzAAcAABGcAAAA0AAAAAAAABGcYXBwbAIAAABtbnRyR1JBWVhZWiAH3AAIABcADwAu -AA9hY3NwQVBQTAAAAABub25lAAAAAAAAAAAAAAAAAAAAAAAA9tYAAQAAAADTLWFwcGwAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAVkZXNjAAAAwAAAAHlkc2NtAAABPAAA -CBpjcHJ0AAAJWAAAACN3dHB0AAAJfAAAABRrVFJDAAAJkAAACAxkZXNjAAAAAAAAAB9HZW5lcmljIEdy -YXkgR2FtbWEgMi4yIFByb2ZpbGUAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAbWx1YwAAAAAAAAAfAAAADHNr -U0sAAAAuAAABhGRhREsAAAA6AAABsmNhRVMAAAA4AAAB7HZpVk4AAABAAAACJHB0QlIAAABKAAACZHVr -VUEAAAAsAAACrmZyRlUAAAA+AAAC2mh1SFUAAAA0AAADGHpoVFcAAAAaAAADTGtvS1IAAAAiAAADZm5i -Tk8AAAA6AAADiGNzQ1oAAAAoAAADwmhlSUwAAAAkAAAD6nJvUk8AAAAqAAAEDmRlREUAAABOAAAEOGl0 -SVQAAABOAAAEhnN2U0UAAAA4AAAE1HpoQ04AAAAaAAAFDGphSlAAAAAmAAAFJmVsR1IAAAAqAAAFTHB0 -UE8AAABSAAAFdm5sTkwAAABAAAAFyGVzRVMAAABMAAAGCHRoVEgAAAAyAAAGVHRyVFIAAAAkAAAGhmZp -RkkAAABGAAAGqmhySFIAAAA+AAAG8HBsUEwAAABKAAAHLmFyRUcAAAAsAAAHeHJ1UlUAAAA6AAAHpGVu -VVMAAAA8AAAH3gBWAWEAZQBvAGIAZQBjAG4A4QAgAHMAaQB2AOEAIABnAGEAbQBhACAAMgAsADIARwBl -AG4AZQByAGkAcwBrACAAZwByAOUAIAAyACwAMgAgAGcAYQBtAG0AYQAtAHAAcgBvAGYAaQBsAEcAYQBt -AG0AYQAgAGQAZQAgAGcAcgBpAHMAbwBzACAAZwBlAG4A6AByAGkAYwBhACAAMgAuADIAQx6lAHUAIABo -AOwAbgBoACAATQDgAHUAIAB4AOEAbQAgAEMAaAB1AG4AZwAgAEcAYQBtAG0AYQAgADIALgAyAFAAZQBy -AGYAaQBsACAARwBlAG4A6QByAGkAYwBvACAAZABhACAARwBhAG0AYQAgAGQAZQAgAEMAaQBuAHoAYQBz -ACAAMgAsADIEFwQwBDMEMAQ7BEwEPQQwACAARwByAGEAeQAtBDMEMAQ8BDAAIAAyAC4AMgBQAHIAbwBm -AGkAbAAgAGcA6QBuAOkAcgBpAHEAdQBlACAAZwByAGkAcwAgAGcAYQBtAG0AYQAgADIALAAyAMEAbAB0 -AGEAbADhAG4AbwBzACAAcwB6APwAcgBrAGUAIABnAGEAbQBtAGEAIAAyAC4AMpAadShwcJaOUUlepgAy -AC4AMoJyX2ljz4/wx3y8GAAg1ozAyQAgrBC5yAAgADIALgAyACDVBLhc0wzHfABHAGUAbgBlAHIAaQBz -AGsAIABnAHIA5QAgAGcAYQBtAG0AYQAgADIALAAyAC0AcAByAG8AZgBpAGwATwBiAGUAYwBuAOEAIAFh -AGUAZADhACAAZwBhAG0AYQAgADIALgAyBdIF0AXeBdQAIAXQBeQF1QXoACAF2wXcBdwF2QAgADIALgAy -AEcAYQBtAGEAIABnAHIAaQAgAGcAZQBuAGUAcgBpAGMBAwAgADIALAAyAEEAbABsAGcAZQBtAGUAaQBu -AGUAcwAgAEcAcgBhAHUAcwB0AHUAZgBlAG4ALQBQAHIAbwBmAGkAbAAgAEcAYQBtAG0AYQAgADIALAAy -AFAAcgBvAGYAaQBsAG8AIABnAHIAaQBnAGkAbwAgAGcAZQBuAGUAcgBpAGMAbwAgAGQAZQBsAGwAYQAg -AGcAYQBtAG0AYQAgADIALAAyAEcAZQBuAGUAcgBpAHMAawAgAGcAcgDlACAAMgAsADIAIABnAGEAbQBt -AGEAcAByAG8AZgBpAGxmbpAacHBepnz7ZXAAMgAuADJjz4/wZYdO9k4AgiwwsDDsMKQwrDDzMN4AIAAy -AC4AMgAgMNcw7TDVMKEwpDDrA5MDtQO9A7kDugPMACADkwO6A8EDuQAgA5MDrAO8A7wDsQAgADIALgAy -AFAAZQByAGYAaQBsACAAZwBlAG4A6QByAGkAYwBvACAAZABlACAAYwBpAG4AegBlAG4AdABvAHMAIABk -AGEAIABHAGEAbQBtAGEAIAAyACwAMgBBAGwAZwBlAG0AZQBlAG4AIABnAHIAaQBqAHMAIABnAGEAbQBt -AGEAIAAyACwAMgAtAHAAcgBvAGYAaQBlAGwAUABlAHIAZgBpAGwAIABnAGUAbgDpAHIAaQBjAG8AIABk -AGUAIABnAGEAbQBtAGEAIABkAGUAIABnAHIAaQBzAGUAcwAgADIALAAyDiMOMQ4HDioONQ5BDgEOIQ4h -DjIOQA4BDiMOIg5MDhcOMQ5IDicORA4bACAAMgAuADIARwBlAG4AZQBsACAARwByAGkAIABHAGEAbQBh -ACAAMgAsADIAWQBsAGUAaQBuAGUAbgAgAGgAYQByAG0AYQBhAG4AIABnAGEAbQBtAGEAIAAyACwAMgAg -AC0AcAByAG8AZgBpAGkAbABpAEcAZQBuAGUAcgBpAQ0AawBpACAARwByAGEAeQAgAEcAYQBtAG0AYQAg -ADIALgAyACAAcAByAG8AZgBpAGwAVQBuAGkAdwBlAHIAcwBhAGwAbgB5ACAAcAByAG8AZgBpAGwAIABz -AHoAYQByAG8BWwBjAGkAIABnAGEAbQBtAGEAIAAyACwAMgY6BicGRQYnACAAMgAuADIAIAZEBkgGRgAg -BjEGRQYnBi8GSgAgBjkGJwZFBB4EMQRJBDAETwAgBEEENQRABDAETwAgBDMEMAQ8BDwEMAAgADIALAAy -AC0EPwRABD4ERAQ4BDsETABHAGUAbgBlAHIAaQBjACAARwByAGEAeQAgAEcAYQBtAG0AYQAgADIALgAy -ACAAUAByAG8AZgBpAGwAZQAAdGV4dAAAAABDb3B5cmlnaHQgQXBwbGUgSW5jLiwgMjAxMgAAWFlaIAAA -AAAAAPNRAAEAAAABFsxjdXJ2AAAAAAAABAAAAAAFAAoADwAUABkAHgAjACgALQAyADcAOwBAAEUASgBP -AFQAWQBeAGMAaABtAHIAdwB8AIEAhgCLAJAAlQCaAJ8ApACpAK4AsgC3ALwAwQDGAMsA0ADVANsA4ADl -AOsA8AD2APsBAQEHAQ0BEwEZAR8BJQErATIBOAE+AUUBTAFSAVkBYAFnAW4BdQF8AYMBiwGSAZoBoQGp -AbEBuQHBAckB0QHZAeEB6QHyAfoCAwIMAhQCHQImAi8COAJBAksCVAJdAmcCcQJ6AoQCjgKYAqICrAK2 -AsECywLVAuAC6wL1AwADCwMWAyEDLQM4A0MDTwNaA2YDcgN+A4oDlgOiA64DugPHA9MD4APsA/kEBgQT -BCAELQQ7BEgEVQRjBHEEfgSMBJoEqAS2BMQE0wThBPAE/gUNBRwFKwU6BUkFWAVnBXcFhgWWBaYFtQXF -BdUF5QX2BgYGFgYnBjcGSAZZBmoGewaMBp0GrwbABtEG4wb1BwcHGQcrBz0HTwdhB3QHhgeZB6wHvwfS -B+UH+AgLCB8IMghGCFoIbgiCCJYIqgi+CNII5wj7CRAJJQk6CU8JZAl5CY8JpAm6Cc8J5Qn7ChEKJwo9 -ClQKagqBCpgKrgrFCtwK8wsLCyILOQtRC2kLgAuYC7ALyAvhC/kMEgwqDEMMXAx1DI4MpwzADNkM8w0N -DSYNQA1aDXQNjg2pDcMN3g34DhMOLg5JDmQOfw6bDrYO0g7uDwkPJQ9BD14Peg+WD7MPzw/sEAkQJhBD -EGEQfhCbELkQ1xD1ERMRMRFPEW0RjBGqEckR6BIHEiYSRRJkEoQSoxLDEuMTAxMjE0MTYxODE6QTxRPl -FAYUJxRJFGoUixStFM4U8BUSFTQVVhV4FZsVvRXgFgMWJhZJFmwWjxayFtYW+hcdF0EXZReJF64X0hf3 -GBsYQBhlGIoYrxjVGPoZIBlFGWsZkRm3Gd0aBBoqGlEadxqeGsUa7BsUGzsbYxuKG7Ib2hwCHCocUhx7 -HKMczBz1HR4dRx1wHZkdwx3sHhYeQB5qHpQevh7pHxMfPh9pH5Qfvx/qIBUgQSBsIJggxCDwIRwhSCF1 -IaEhziH7IiciVSKCIq8i3SMKIzgjZiOUI8Ij8CQfJE0kfCSrJNolCSU4JWgllyXHJfcmJyZXJocmtybo -JxgnSSd6J6sn3CgNKD8ocSiiKNQpBik4KWspnSnQKgIqNSpoKpsqzysCKzYraSudK9EsBSw5LG4soizX -LQwtQS12Last4S4WLkwugi63Lu4vJC9aL5Evxy/+MDUwbDCkMNsxEjFKMYIxujHyMioyYzKbMtQzDTNG -M38zuDPxNCs0ZTSeNNg1EzVNNYc1wjX9Njc2cjauNuk3JDdgN5w31zgUOFA4jDjIOQU5Qjl/Obw5+To2 -OnQ6sjrvOy07azuqO+g8JzxlPKQ84z0iPWE9oT3gPiA+YD6gPuA/IT9hP6I/4kAjQGRApkDnQSlBakGs -Qe5CMEJyQrVC90M6Q31DwEQDREdEikTORRJFVUWaRd5GIkZnRqtG8Ec1R3tHwEgFSEtIkUjXSR1JY0mp -SfBKN0p9SsRLDEtTS5pL4kwqTHJMuk0CTUpNk03cTiVObk63TwBPSU+TT91QJ1BxULtRBlFQUZtR5lIx -UnxSx1MTU19TqlP2VEJUj1TbVShVdVXCVg9WXFapVvdXRFeSV+BYL1h9WMtZGllpWbhaB1pWWqZa9VtF -W5Vb5Vw1XIZc1l0nXXhdyV4aXmxevV8PX2Ffs2AFYFdgqmD8YU9homH1YklinGLwY0Njl2PrZEBklGTp -ZT1lkmXnZj1mkmboZz1nk2fpaD9olmjsaUNpmmnxakhqn2r3a09rp2v/bFdsr20IbWBtuW4SbmtuxG8e -b3hv0XArcIZw4HE6cZVx8HJLcqZzAXNdc7h0FHRwdMx1KHWFdeF2Pnabdvh3VnezeBF4bnjMeSp5iXnn -ekZ6pXsEe2N7wnwhfIF84X1BfaF+AX5ifsJ/I3+Ef+WAR4CogQqBa4HNgjCCkoL0g1eDuoQdhICE44VH -hauGDoZyhteHO4efiASIaYjOiTOJmYn+imSKyoswi5aL/IxjjMqNMY2Yjf+OZo7OjzaPnpAGkG6Q1pE/ -kaiSEZJ6kuOTTZO2lCCUipT0lV+VyZY0lp+XCpd1l+CYTJi4mSSZkJn8mmia1ZtCm6+cHJyJnPedZJ3S -nkCerp8dn4uf+qBpoNihR6G2oiailqMGo3aj5qRWpMelOKWpphqmi6b9p26n4KhSqMSpN6mpqhyqj6sC -q3Wr6axcrNCtRK24ri2uoa8Wr4uwALB1sOqxYLHWskuywrM4s660JbSctRO1irYBtnm28Ldot+C4WbjR -uUq5wro7urW7LrunvCG8m70VvY++Cr6Evv+/er/1wHDA7MFnwePCX8Lbw1jD1MRRxM7FS8XIxkbGw8dB -x7/IPci8yTrJuco4yrfLNsu2zDXMtc01zbXONs62zzfPuNA50LrRPNG+0j/SwdNE08bUSdTL1U7V0dZV -1tjXXNfg2GTY6Nls2fHadtr724DcBdyK3RDdlt4c3qLfKd+v4DbgveFE4cziU+Lb42Pj6+Rz5PzlhOYN -5pbnH+ep6DLovOlG6dDqW+rl63Dr++yG7RHtnO4o7rTvQO/M8Fjw5fFy8f/yjPMZ86f0NPTC9VD13vZt -9vv3ivgZ+Kj5OPnH+lf65/t3/Af8mP0p/br+S/7c/23//9IrLC0uWiRjbGFzc25hbWVYJGNsYXNzZXNf -EBBOU0JpdG1hcEltYWdlUmVwoy0vMFpOU0ltYWdlUmVwWE5TT2JqZWN00issMjNXTlNBcnJheaIyMNIr -LDU2Xk5TTXV0YWJsZUFycmF5ozUyMNM4OQ86OzxXTlNXaGl0ZVxOU0NvbG9yU3BhY2VEMCAwABADgAzS -Kyw+P1dOU0NvbG9yoj4w0issQUJXTlNJbWFnZaJBMAAIABEAGgAkACkAMgA3AEkATABRAFMAYgBoAHUA -fACLAJIAnwCmAK4AsACyALQAuQC7AL0AxADJANQA1gDYANoA3wDiAOQA5gDoAO8BBgEiASQBJhOWE5sT -phOvE8ITxhPRE9oT3xPnE+oT7xP+FAIUCRQRFB4UIxQlFCcULBQ0FDcUPBREAAAAAAAAAgEAAAAAAAAA -QwAAAAAAAAAAAAAAAAAAFEc +S2V5ZWRBcmNoaXZlctEICVRyb290gAGuCwwbHCEaJissMzY5P0JVJG51bGzXDQ4PEBESExQVFhcYGRpW +TlNTaXplViRjbGFzc1xOU0ltYWdlRmxhZ3NWTlNSZXBzW05TVGludENvbG9yV05TQ29sb3JeTlNSZXNp +emluZ01vZGWAAoANEiDDAACAA4AAgAsQAFZ7MSwgMX3SHQ4eIFpOUy5vYmplY3RzoR+ABIAK0h0OIiWi +IySABYAGgAnTDicoKSoaXxAUTlNUSUZGUmVwcmVzZW50YXRpb25fEBlOU0ludGVybmFsTGF5b3V0RGly +ZWN0aW9ugAiAB08REmxNTQAqAAAACgAAABABAAADAAAAAQABAAABAQADAAAAAQABAAABAgADAAAAAgAI +AAgBAwADAAAAAQABAAABBgADAAAAAQABAAABCgADAAAAAQABAAABEQAEAAAAAQAAAAgBEgADAAAAAQAB +AAABFQADAAAAAQACAAABFgADAAAAAQABAAABFwAEAAAAAQAAAAIBHAADAAAAAQABAAABKAADAAAAAQAC +AAABUgADAAAAAQABAAABUwADAAAAAgABAAGHcwAHAAARnAAAANAAAAAAAAARnGFwcGwCAAAAbW50ckdS +QVlYWVogB9wACAAXAA8ALgAPYWNzcEFQUEwAAAAAbm9uZQAAAAAAAAAAAAAAAAAAAAAAAPbWAAEAAAAA +0y1hcHBsAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFZGVzYwAA +AMAAAAB5ZHNjbQAAATwAAAgaY3BydAAACVgAAAAjd3RwdAAACXwAAAAUa1RSQwAACZAAAAgMZGVzYwAA +AAAAAAAfR2VuZXJpYyBHcmF5IEdhbW1hIDIuMiBQcm9maWxlAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAG1s +dWMAAAAAAAAAHwAAAAxza1NLAAAALgAAAYRkYURLAAAAOgAAAbJjYUVTAAAAOAAAAex2aVZOAAAAQAAA +AiRwdEJSAAAASgAAAmR1a1VBAAAALAAAAq5mckZVAAAAPgAAAtpodUhVAAAANAAAAxh6aFRXAAAAGgAA +A0xrb0tSAAAAIgAAA2ZuYk5PAAAAOgAAA4hjc0NaAAAAKAAAA8JoZUlMAAAAJAAAA+pyb1JPAAAAKgAA +BA5kZURFAAAATgAABDhpdElUAAAATgAABIZzdlNFAAAAOAAABNR6aENOAAAAGgAABQxqYUpQAAAAJgAA +BSZlbEdSAAAAKgAABUxwdFBPAAAAUgAABXZubE5MAAAAQAAABchlc0VTAAAATAAABgh0aFRIAAAAMgAA +BlR0clRSAAAAJAAABoZmaUZJAAAARgAABqpockhSAAAAPgAABvBwbFBMAAAASgAABy5hckVHAAAALAAA +B3hydVJVAAAAOgAAB6RlblVTAAAAPAAAB94AVgFhAGUAbwBiAGUAYwBuAOEAIABzAGkAdgDhACAAZwBh +AG0AYQAgADIALAAyAEcAZQBuAGUAcgBpAHMAawAgAGcAcgDlACAAMgAsADIAIABnAGEAbQBtAGEALQBw +AHIAbwBmAGkAbABHAGEAbQBtAGEAIABkAGUAIABnAHIAaQBzAG8AcwAgAGcAZQBuAOgAcgBpAGMAYQAg +ADIALgAyAEMepQB1ACAAaADsAG4AaAAgAE0A4AB1ACAAeADhAG0AIABDAGgAdQBuAGcAIABHAGEAbQBt +AGEAIAAyAC4AMgBQAGUAcgBmAGkAbAAgAEcAZQBuAOkAcgBpAGMAbwAgAGQAYQAgAEcAYQBtAGEAIABk +AGUAIABDAGkAbgB6AGEAcwAgADIALAAyBBcEMAQzBDAEOwRMBD0EMAAgAEcAcgBhAHkALQQzBDAEPAQw +ACAAMgAuADIAUAByAG8AZgBpAGwAIABnAOkAbgDpAHIAaQBxAHUAZQAgAGcAcgBpAHMAIABnAGEAbQBt +AGEAIAAyACwAMgDBAGwAdABhAGwA4QBuAG8AcwAgAHMAegD8AHIAawBlACAAZwBhAG0AbQBhACAAMgAu +ADKQGnUocHCWjlFJXqYAMgAuADKCcl9pY8+P8Md8vBgAINaMwMkAIKwQucgAIAAyAC4AMgAg1QS4XNMM +x3wARwBlAG4AZQByAGkAcwBrACAAZwByAOUAIABnAGEAbQBtAGEAIAAyACwAMgAtAHAAcgBvAGYAaQBs +AE8AYgBlAGMAbgDhACABYQBlAGQA4QAgAGcAYQBtAGEAIAAyAC4AMgXSBdAF3gXUACAF0AXkBdUF6AAg +BdsF3AXcBdkAIAAyAC4AMgBHAGEAbQBhACAAZwByAGkAIABnAGUAbgBlAHIAaQBjAQMAIAAyACwAMgBB +AGwAbABnAGUAbQBlAGkAbgBlAHMAIABHAHIAYQB1AHMAdAB1AGYAZQBuAC0AUAByAG8AZgBpAGwAIABH +AGEAbQBtAGEAIAAyACwAMgBQAHIAbwBmAGkAbABvACAAZwByAGkAZwBpAG8AIABnAGUAbgBlAHIAaQBj +AG8AIABkAGUAbABsAGEAIABnAGEAbQBtAGEAIAAyACwAMgBHAGUAbgBlAHIAaQBzAGsAIABnAHIA5QAg +ADIALAAyACAAZwBhAG0AbQBhAHAAcgBvAGYAaQBsZm6QGnBwXqZ8+2VwADIALgAyY8+P8GWHTvZOAIIs +MLAw7DCkMKww8zDeACAAMgAuADIAIDDXMO0w1TChMKQw6wOTA7UDvQO5A7oDzAAgA5MDugPBA7kAIAOT +A6wDvAO8A7EAIAAyAC4AMgBQAGUAcgBmAGkAbAAgAGcAZQBuAOkAcgBpAGMAbwAgAGQAZQAgAGMAaQBu +AHoAZQBuAHQAbwBzACAAZABhACAARwBhAG0AbQBhACAAMgAsADIAQQBsAGcAZQBtAGUAZQBuACAAZwBy +AGkAagBzACAAZwBhAG0AbQBhACAAMgAsADIALQBwAHIAbwBmAGkAZQBsAFAAZQByAGYAaQBsACAAZwBl +AG4A6QByAGkAYwBvACAAZABlACAAZwBhAG0AbQBhACAAZABlACAAZwByAGkAcwBlAHMAIAAyACwAMg4j +DjEOBw4qDjUOQQ4BDiEOIQ4yDkAOAQ4jDiIOTA4XDjEOSA4nDkQOGwAgADIALgAyAEcAZQBuAGUAbAAg +AEcAcgBpACAARwBhAG0AYQAgADIALAAyAFkAbABlAGkAbgBlAG4AIABoAGEAcgBtAGEAYQBuACAAZwBh +AG0AbQBhACAAMgAsADIAIAAtAHAAcgBvAGYAaQBpAGwAaQBHAGUAbgBlAHIAaQENAGsAaQAgAEcAcgBh +AHkAIABHAGEAbQBtAGEAIAAyAC4AMgAgAHAAcgBvAGYAaQBsAFUAbgBpAHcAZQByAHMAYQBsAG4AeQAg +AHAAcgBvAGYAaQBsACAAcwB6AGEAcgBvAVsAYwBpACAAZwBhAG0AbQBhACAAMgAsADIGOgYnBkUGJwAg +ADIALgAyACAGRAZIBkYAIAYxBkUGJwYvBkoAIAY5BicGRQQeBDEESQQwBE8AIARBBDUEQAQwBE8AIAQz +BDAEPAQ8BDAAIAAyACwAMgAtBD8EQAQ+BEQEOAQ7BEwARwBlAG4AZQByAGkAYwAgAEcAcgBhAHkAIABH +AGEAbQBtAGEAIAAyAC4AMgAgAFAAcgBvAGYAaQBsAGUAAHRleHQAAAAAQ29weXJpZ2h0IEFwcGxlIElu +Yy4sIDIwMTIAAFhZWiAAAAAAAADzUQABAAAAARbMY3VydgAAAAAAAAQAAAAABQAKAA8AFAAZAB4AIwAo +AC0AMgA3ADsAQABFAEoATwBUAFkAXgBjAGgAbQByAHcAfACBAIYAiwCQAJUAmgCfAKQAqQCuALIAtwC8 +AMEAxgDLANAA1QDbAOAA5QDrAPAA9gD7AQEBBwENARMBGQEfASUBKwEyATgBPgFFAUwBUgFZAWABZwFu +AXUBfAGDAYsBkgGaAaEBqQGxAbkBwQHJAdEB2QHhAekB8gH6AgMCDAIUAh0CJgIvAjgCQQJLAlQCXQJn +AnECegKEAo4CmAKiAqwCtgLBAssC1QLgAusC9QMAAwsDFgMhAy0DOANDA08DWgNmA3IDfgOKA5YDogOu +A7oDxwPTA+AD7AP5BAYEEwQgBC0EOwRIBFUEYwRxBH4EjASaBKgEtgTEBNME4QTwBP4FDQUcBSsFOgVJ +BVgFZwV3BYYFlgWmBbUFxQXVBeUF9gYGBhYGJwY3BkgGWQZqBnsGjAadBq8GwAbRBuMG9QcHBxkHKwc9 +B08HYQd0B4YHmQesB78H0gflB/gICwgfCDIIRghaCG4IggiWCKoIvgjSCOcI+wkQCSUJOglPCWQJeQmP +CaQJugnPCeUJ+woRCicKPQpUCmoKgQqYCq4KxQrcCvMLCwsiCzkLUQtpC4ALmAuwC8gL4Qv5DBIMKgxD +DFwMdQyODKcMwAzZDPMNDQ0mDUANWg10DY4NqQ3DDd4N+A4TDi4OSQ5kDn8Omw62DtIO7g8JDyUPQQ9e +D3oPlg+zD88P7BAJECYQQxBhEH4QmxC5ENcQ9RETETERTxFtEYwRqhHJEegSBxImEkUSZBKEEqMSwxLj +EwMTIxNDE2MTgxOkE8UT5RQGFCcUSRRqFIsUrRTOFPAVEhU0FVYVeBWbFb0V4BYDFiYWSRZsFo8WshbW +FvoXHRdBF2UXiReuF9IX9xgbGEAYZRiKGK8Y1Rj6GSAZRRlrGZEZtxndGgQaKhpRGncanhrFGuwbFBs7 +G2MbihuyG9ocAhwqHFIcexyjHMwc9R0eHUcdcB2ZHcMd7B4WHkAeah6UHr4e6R8THz4faR+UH78f6iAV +IEEgbCCYIMQg8CEcIUghdSGhIc4h+yInIlUigiKvIt0jCiM4I2YjlCPCI/AkHyRNJHwkqyTaJQklOCVo +JZclxyX3JicmVyaHJrcm6CcYJ0kneierJ9woDSg/KHEooijUKQYpOClrKZ0p0CoCKjUqaCqbKs8rAis2 +K2krnSvRLAUsOSxuLKIs1y0MLUEtdi2rLeEuFi5MLoIuty7uLyQvWi+RL8cv/jA1MGwwpDDbMRIxSjGC +Mbox8jIqMmMymzLUMw0zRjN/M7gz8TQrNGU0njTYNRM1TTWHNcI1/TY3NnI2rjbpNyQ3YDecN9c4FDhQ +OIw4yDkFOUI5fzm8Ofk6Njp0OrI67zstO2s7qjvoPCc8ZTykPOM9Ij1hPaE94D4gPmA+oD7gPyE/YT+i +P+JAI0BkQKZA50EpQWpBrEHuQjBCckK1QvdDOkN9Q8BEA0RHRIpEzkUSRVVFmkXeRiJGZ0arRvBHNUd7 +R8BIBUhLSJFI10kdSWNJqUnwSjdKfUrESwxLU0uaS+JMKkxyTLpNAk1KTZNN3E4lTm5Ot08AT0lPk0/d +UCdQcVC7UQZRUFGbUeZSMVJ8UsdTE1NfU6pT9lRCVI9U21UoVXVVwlYPVlxWqVb3V0RXklfgWC9YfVjL +WRpZaVm4WgdaVlqmWvVbRVuVW+VcNVyGXNZdJ114XcleGl5sXr1fD19hX7NgBWBXYKpg/GFPYaJh9WJJ +Ypxi8GNDY5dj62RAZJRk6WU9ZZJl52Y9ZpJm6Gc9Z5Nn6Wg/aJZo7GlDaZpp8WpIap9q92tPa6dr/2xX +bK9tCG1gbbluEm5rbsRvHm94b9FwK3CGcOBxOnGVcfByS3KmcwFzXXO4dBR0cHTMdSh1hXXhdj52m3b4 +d1Z3s3gReG54zHkqeYl553pGeqV7BHtje8J8IXyBfOF9QX2hfgF+Yn7CfyN/hH/lgEeAqIEKgWuBzYIw +gpKC9INXg7qEHYSAhOOFR4Wrhg6GcobXhzuHn4gEiGmIzokziZmJ/opkisqLMIuWi/yMY4zKjTGNmI3/ +jmaOzo82j56QBpBukNaRP5GokhGSepLjk02TtpQglIqU9JVflcmWNJaflwqXdZfgmEyYuJkkmZCZ/Jpo +mtWbQpuvnByciZz3nWSd0p5Anq6fHZ+Ln/qgaaDYoUehtqImopajBqN2o+akVqTHpTilqaYapoum/adu +p+CoUqjEqTepqaocqo+rAqt1q+msXKzQrUStuK4trqGvFq+LsACwdbDqsWCx1rJLssKzOLOutCW0nLUT +tYq2AbZ5tvC3aLfguFm40blKucK6O7q1uy67p7whvJu9Fb2Pvgq+hL7/v3q/9cBwwOzBZ8Hjwl/C28NY +w9TEUcTOxUvFyMZGxsPHQce/yD3IvMk6ybnKOMq3yzbLtsw1zLXNNc21zjbOts83z7jQOdC60TzRvtI/ +0sHTRNPG1EnUy9VO1dHWVdbY11zX4Nhk2OjZbNnx2nba+9uA3AXcit0Q3ZbeHN6i3ynfr+A24L3hROHM +4lPi2+Nj4+vkc+T85YTmDeaW5x/nqegy6LzpRunQ6lvq5etw6/vshu0R7ZzuKO6070DvzPBY8OXxcvH/ +8ozzGfOn9DT0wvVQ9d72bfb794r4Gfio+Tj5x/pX+uf7d/wH/Jj9Kf26/kv+3P9t///SLS4vMFokY2xh +c3NuYW1lWCRjbGFzc2VzXxAQTlNCaXRtYXBJbWFnZVJlcKMvMTJaTlNJbWFnZVJlcFhOU09iamVjdNIt +LjQ1V05TQXJyYXmiNDLSLS43OF5OU011dGFibGVBcnJheaM3NDLTOjsOPD0+V05TV2hpdGVcTlNDb2xv +clNwYWNlRDAgMAAQA4AM0i0uQEFXTlNDb2xvcqJAMtItLkNEV05TSW1hZ2WiQzIACAARABoAJAApADIA +NwBJAEwAUQBTAGIAaAB3AH4AhQCSAJkApQCtALwAvgDAAMUAxwDJAMsAzQDUANkA5ADmAOgA6gDvAPIA +9AD2APgA/wEWATIBNAE2E6YTqxO2E78T0hPWE+ET6hPvE/cT+hP/FA4UEhQZFCEULhQzFDUUNxQ8FEQU +RxRMFFQAAAAAAAACAQAAAAAAAABFAAAAAAAAAAAAAAAAAAAUVw diff --git a/i18n/src/main/resources/pl.lproj/Command.xib b/i18n/src/main/resources/pl.lproj/Command.xib index ccb8c72fd5..83d383a895 100644 --- a/i18n/src/main/resources/pl.lproj/Command.xib +++ b/i18n/src/main/resources/pl.lproj/Command.xib @@ -1,7 +1,7 @@ - + - + diff --git a/i18n/src/main/resources/pl.lproj/Connection.xib b/i18n/src/main/resources/pl.lproj/Connection.xib index da205ca740..4f86db0168 100644 --- a/i18n/src/main/resources/pl.lproj/Connection.xib +++ b/i18n/src/main/resources/pl.lproj/Connection.xib @@ -1,7 +1,7 @@ - + - + @@ -76,7 +76,7 @@ - + diff --git a/i18n/src/main/resources/pl.lproj/Info.xib b/i18n/src/main/resources/pl.lproj/Info.xib index af5f532b59..3d8848121d 100644 --- a/i18n/src/main/resources/pl.lproj/Info.xib +++ b/i18n/src/main/resources/pl.lproj/Info.xib @@ -1,7 +1,7 @@ - + - + @@ -89,7 +89,7 @@ - + @@ -1017,21 +1017,21 @@ - + - + - + @@ -1121,21 +1121,21 @@ - + - + - + @@ -1250,27 +1250,27 @@ diff --git a/i18n/src/main/resources/pl.lproj/Login.strings b/i18n/src/main/resources/pl.lproj/Login.strings index 284209095f..775f4d5541 100644 Binary files a/i18n/src/main/resources/pl.lproj/Login.strings and b/i18n/src/main/resources/pl.lproj/Login.strings differ diff --git a/i18n/src/main/resources/pl.lproj/Main.xib b/i18n/src/main/resources/pl.lproj/Main.xib index e200e446be..5021d7c9cf 100644 --- a/i18n/src/main/resources/pl.lproj/Main.xib +++ b/i18n/src/main/resources/pl.lproj/Main.xib @@ -1,7 +1,7 @@ - + - + @@ -94,7 +94,7 @@ - + @@ -159,19 +159,19 @@ - + - + - + @@ -321,12 +321,12 @@ CA - + - + @@ -566,6 +566,10 @@ CA - + + + + + diff --git a/i18n/src/main/resources/pl.lproj/Preferences.xib b/i18n/src/main/resources/pl.lproj/Preferences.xib index 9819ad9a00..4a1615d600 100644 --- a/i18n/src/main/resources/pl.lproj/Preferences.xib +++ b/i18n/src/main/resources/pl.lproj/Preferences.xib @@ -1,7 +1,7 @@ - + - + @@ -266,7 +266,7 @@ - + @@ -307,7 +307,7 @@ - + @@ -538,7 +538,7 @@ - + @@ -578,7 +578,7 @@ - + @@ -613,7 +613,7 @@ - + @@ -642,7 +642,7 @@ - + @@ -1218,7 +1218,7 @@ - + @@ -1250,7 +1250,7 @@ - + @@ -1290,7 +1290,7 @@ - + @@ -1363,7 +1363,7 @@ - + @@ -1558,7 +1558,7 @@ - + @@ -1618,7 +1618,7 @@ - + @@ -1643,7 +1643,7 @@ - + @@ -1685,7 +1685,7 @@ - + @@ -1713,7 +1713,7 @@ - + @@ -1741,7 +1741,7 @@ - + @@ -1770,7 +1770,7 @@ - + @@ -1805,7 +1805,7 @@ - + @@ -1888,7 +1888,7 @@ - + @@ -1947,7 +1947,7 @@ - + @@ -1975,7 +1975,7 @@ - + @@ -2004,7 +2004,7 @@ - + diff --git a/i18n/src/main/resources/pl.lproj/Profiles.xib b/i18n/src/main/resources/pl.lproj/Profiles.xib index 5a7c574023..70b6231494 100644 --- a/i18n/src/main/resources/pl.lproj/Profiles.xib +++ b/i18n/src/main/resources/pl.lproj/Profiles.xib @@ -1,7 +1,7 @@ - + - + diff --git a/i18n/src/main/resources/pl.lproj/Prompt.xib b/i18n/src/main/resources/pl.lproj/Prompt.xib index 9bc89cf465..191c3cce61 100644 --- a/i18n/src/main/resources/pl.lproj/Prompt.xib +++ b/i18n/src/main/resources/pl.lproj/Prompt.xib @@ -1,7 +1,7 @@ - + - + @@ -95,7 +95,7 @@ Gw - + diff --git a/i18n/src/main/resources/pl.lproj/S3.strings b/i18n/src/main/resources/pl.lproj/S3.strings index be84b80038..3a46c2afbc 100644 Binary files a/i18n/src/main/resources/pl.lproj/S3.strings and b/i18n/src/main/resources/pl.lproj/S3.strings differ diff --git a/i18n/src/main/resources/pl.lproj/Updater.strings b/i18n/src/main/resources/pl.lproj/Updater.strings index 564803f81f..a14f1916a1 100644 Binary files a/i18n/src/main/resources/pl.lproj/Updater.strings and b/i18n/src/main/resources/pl.lproj/Updater.strings differ diff --git a/i18n/src/main/resources/pt_BR.lproj/Bookmark.xib b/i18n/src/main/resources/pt_BR.lproj/Bookmark.xib index 34fe67c8f1..c5ed39ddba 100644 --- a/i18n/src/main/resources/pt_BR.lproj/Bookmark.xib +++ b/i18n/src/main/resources/pt_BR.lproj/Bookmark.xib @@ -1,7 +1,7 @@ - + - + @@ -92,7 +92,7 @@ - + @@ -489,95 +489,95 @@ YnBsaXN0MDDUAQIDBAUGBwpYJHZlcnNpb25ZJGFyY2hpdmVyVCR0b3BYJG9iamVjdHMSAAGGoF8QD05T -S2V5ZWRBcmNoaXZlctEICVRyb290gAGuCwwZGh8UJCkqMTQ3PUBVJG51bGzWDQ4PEBESExQVFhcYVk5T -U2l6ZV5OU1Jlc2l6aW5nTW9kZVYkY2xhc3NcTlNJbWFnZUZsYWdzVk5TUmVwc1dOU0NvbG9ygAIQAIAN -EiDDAACAA4ALVnsxLCAxfdIbDxweWk5TLm9iamVjdHOhHYAEgArSGw8gI6IhIoAFgAaACdMPJSYnKBRf -EBROU1RJRkZSZXByZXNlbnRhdGlvbl8QGU5TSW50ZXJuYWxMYXlvdXREaXJlY3Rpb26ACIAHTxESbE1N -ACoAAAAKAAAAEAEAAAMAAAABAAEAAAEBAAMAAAABAAEAAAECAAMAAAACAAgACAEDAAMAAAABAAEAAAEG -AAMAAAABAAEAAAEKAAMAAAABAAEAAAERAAQAAAABAAAACAESAAMAAAABAAEAAAEVAAMAAAABAAIAAAEW -AAMAAAABAAEAAAEXAAQAAAABAAAAAgEcAAMAAAABAAEAAAEoAAMAAAABAAIAAAFSAAMAAAABAAEAAAFT -AAMAAAACAAEAAYdzAAcAABGcAAAA0AAAAAAAABGcYXBwbAIAAABtbnRyR1JBWVhZWiAH3AAIABcADwAu -AA9hY3NwQVBQTAAAAABub25lAAAAAAAAAAAAAAAAAAAAAAAA9tYAAQAAAADTLWFwcGwAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAVkZXNjAAAAwAAAAHlkc2NtAAABPAAA -CBpjcHJ0AAAJWAAAACN3dHB0AAAJfAAAABRrVFJDAAAJkAAACAxkZXNjAAAAAAAAAB9HZW5lcmljIEdy -YXkgR2FtbWEgMi4yIFByb2ZpbGUAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAbWx1YwAAAAAAAAAfAAAADHNr -U0sAAAAuAAABhGRhREsAAAA6AAABsmNhRVMAAAA4AAAB7HZpVk4AAABAAAACJHB0QlIAAABKAAACZHVr -VUEAAAAsAAACrmZyRlUAAAA+AAAC2mh1SFUAAAA0AAADGHpoVFcAAAAaAAADTGtvS1IAAAAiAAADZm5i -Tk8AAAA6AAADiGNzQ1oAAAAoAAADwmhlSUwAAAAkAAAD6nJvUk8AAAAqAAAEDmRlREUAAABOAAAEOGl0 -SVQAAABOAAAEhnN2U0UAAAA4AAAE1HpoQ04AAAAaAAAFDGphSlAAAAAmAAAFJmVsR1IAAAAqAAAFTHB0 -UE8AAABSAAAFdm5sTkwAAABAAAAFyGVzRVMAAABMAAAGCHRoVEgAAAAyAAAGVHRyVFIAAAAkAAAGhmZp -RkkAAABGAAAGqmhySFIAAAA+AAAG8HBsUEwAAABKAAAHLmFyRUcAAAAsAAAHeHJ1UlUAAAA6AAAHpGVu -VVMAAAA8AAAH3gBWAWEAZQBvAGIAZQBjAG4A4QAgAHMAaQB2AOEAIABnAGEAbQBhACAAMgAsADIARwBl -AG4AZQByAGkAcwBrACAAZwByAOUAIAAyACwAMgAgAGcAYQBtAG0AYQAtAHAAcgBvAGYAaQBsAEcAYQBt -AG0AYQAgAGQAZQAgAGcAcgBpAHMAbwBzACAAZwBlAG4A6AByAGkAYwBhACAAMgAuADIAQx6lAHUAIABo -AOwAbgBoACAATQDgAHUAIAB4AOEAbQAgAEMAaAB1AG4AZwAgAEcAYQBtAG0AYQAgADIALgAyAFAAZQBy -AGYAaQBsACAARwBlAG4A6QByAGkAYwBvACAAZABhACAARwBhAG0AYQAgAGQAZQAgAEMAaQBuAHoAYQBz -ACAAMgAsADIEFwQwBDMEMAQ7BEwEPQQwACAARwByAGEAeQAtBDMEMAQ8BDAAIAAyAC4AMgBQAHIAbwBm -AGkAbAAgAGcA6QBuAOkAcgBpAHEAdQBlACAAZwByAGkAcwAgAGcAYQBtAG0AYQAgADIALAAyAMEAbAB0 -AGEAbADhAG4AbwBzACAAcwB6APwAcgBrAGUAIABnAGEAbQBtAGEAIAAyAC4AMpAadShwcJaOUUlepgAy -AC4AMoJyX2ljz4/wx3y8GAAg1ozAyQAgrBC5yAAgADIALgAyACDVBLhc0wzHfABHAGUAbgBlAHIAaQBz -AGsAIABnAHIA5QAgAGcAYQBtAG0AYQAgADIALAAyAC0AcAByAG8AZgBpAGwATwBiAGUAYwBuAOEAIAFh -AGUAZADhACAAZwBhAG0AYQAgADIALgAyBdIF0AXeBdQAIAXQBeQF1QXoACAF2wXcBdwF2QAgADIALgAy -AEcAYQBtAGEAIABnAHIAaQAgAGcAZQBuAGUAcgBpAGMBAwAgADIALAAyAEEAbABsAGcAZQBtAGUAaQBu -AGUAcwAgAEcAcgBhAHUAcwB0AHUAZgBlAG4ALQBQAHIAbwBmAGkAbAAgAEcAYQBtAG0AYQAgADIALAAy -AFAAcgBvAGYAaQBsAG8AIABnAHIAaQBnAGkAbwAgAGcAZQBuAGUAcgBpAGMAbwAgAGQAZQBsAGwAYQAg -AGcAYQBtAG0AYQAgADIALAAyAEcAZQBuAGUAcgBpAHMAawAgAGcAcgDlACAAMgAsADIAIABnAGEAbQBt -AGEAcAByAG8AZgBpAGxmbpAacHBepnz7ZXAAMgAuADJjz4/wZYdO9k4AgiwwsDDsMKQwrDDzMN4AIAAy -AC4AMgAgMNcw7TDVMKEwpDDrA5MDtQO9A7kDugPMACADkwO6A8EDuQAgA5MDrAO8A7wDsQAgADIALgAy -AFAAZQByAGYAaQBsACAAZwBlAG4A6QByAGkAYwBvACAAZABlACAAYwBpAG4AegBlAG4AdABvAHMAIABk -AGEAIABHAGEAbQBtAGEAIAAyACwAMgBBAGwAZwBlAG0AZQBlAG4AIABnAHIAaQBqAHMAIABnAGEAbQBt -AGEAIAAyACwAMgAtAHAAcgBvAGYAaQBlAGwAUABlAHIAZgBpAGwAIABnAGUAbgDpAHIAaQBjAG8AIABk -AGUAIABnAGEAbQBtAGEAIABkAGUAIABnAHIAaQBzAGUAcwAgADIALAAyDiMOMQ4HDioONQ5BDgEOIQ4h -DjIOQA4BDiMOIg5MDhcOMQ5IDicORA4bACAAMgAuADIARwBlAG4AZQBsACAARwByAGkAIABHAGEAbQBh -ACAAMgAsADIAWQBsAGUAaQBuAGUAbgAgAGgAYQByAG0AYQBhAG4AIABnAGEAbQBtAGEAIAAyACwAMgAg -AC0AcAByAG8AZgBpAGkAbABpAEcAZQBuAGUAcgBpAQ0AawBpACAARwByAGEAeQAgAEcAYQBtAG0AYQAg -ADIALgAyACAAcAByAG8AZgBpAGwAVQBuAGkAdwBlAHIAcwBhAGwAbgB5ACAAcAByAG8AZgBpAGwAIABz -AHoAYQByAG8BWwBjAGkAIABnAGEAbQBtAGEAIAAyACwAMgY6BicGRQYnACAAMgAuADIAIAZEBkgGRgAg -BjEGRQYnBi8GSgAgBjkGJwZFBB4EMQRJBDAETwAgBEEENQRABDAETwAgBDMEMAQ8BDwEMAAgADIALAAy -AC0EPwRABD4ERAQ4BDsETABHAGUAbgBlAHIAaQBjACAARwByAGEAeQAgAEcAYQBtAG0AYQAgADIALgAy -ACAAUAByAG8AZgBpAGwAZQAAdGV4dAAAAABDb3B5cmlnaHQgQXBwbGUgSW5jLiwgMjAxMgAAWFlaIAAA -AAAAAPNRAAEAAAABFsxjdXJ2AAAAAAAABAAAAAAFAAoADwAUABkAHgAjACgALQAyADcAOwBAAEUASgBP -AFQAWQBeAGMAaABtAHIAdwB8AIEAhgCLAJAAlQCaAJ8ApACpAK4AsgC3ALwAwQDGAMsA0ADVANsA4ADl -AOsA8AD2APsBAQEHAQ0BEwEZAR8BJQErATIBOAE+AUUBTAFSAVkBYAFnAW4BdQF8AYMBiwGSAZoBoQGp -AbEBuQHBAckB0QHZAeEB6QHyAfoCAwIMAhQCHQImAi8COAJBAksCVAJdAmcCcQJ6AoQCjgKYAqICrAK2 -AsECywLVAuAC6wL1AwADCwMWAyEDLQM4A0MDTwNaA2YDcgN+A4oDlgOiA64DugPHA9MD4APsA/kEBgQT -BCAELQQ7BEgEVQRjBHEEfgSMBJoEqAS2BMQE0wThBPAE/gUNBRwFKwU6BUkFWAVnBXcFhgWWBaYFtQXF -BdUF5QX2BgYGFgYnBjcGSAZZBmoGewaMBp0GrwbABtEG4wb1BwcHGQcrBz0HTwdhB3QHhgeZB6wHvwfS -B+UH+AgLCB8IMghGCFoIbgiCCJYIqgi+CNII5wj7CRAJJQk6CU8JZAl5CY8JpAm6Cc8J5Qn7ChEKJwo9 -ClQKagqBCpgKrgrFCtwK8wsLCyILOQtRC2kLgAuYC7ALyAvhC/kMEgwqDEMMXAx1DI4MpwzADNkM8w0N -DSYNQA1aDXQNjg2pDcMN3g34DhMOLg5JDmQOfw6bDrYO0g7uDwkPJQ9BD14Peg+WD7MPzw/sEAkQJhBD -EGEQfhCbELkQ1xD1ERMRMRFPEW0RjBGqEckR6BIHEiYSRRJkEoQSoxLDEuMTAxMjE0MTYxODE6QTxRPl -FAYUJxRJFGoUixStFM4U8BUSFTQVVhV4FZsVvRXgFgMWJhZJFmwWjxayFtYW+hcdF0EXZReJF64X0hf3 -GBsYQBhlGIoYrxjVGPoZIBlFGWsZkRm3Gd0aBBoqGlEadxqeGsUa7BsUGzsbYxuKG7Ib2hwCHCocUhx7 -HKMczBz1HR4dRx1wHZkdwx3sHhYeQB5qHpQevh7pHxMfPh9pH5Qfvx/qIBUgQSBsIJggxCDwIRwhSCF1 -IaEhziH7IiciVSKCIq8i3SMKIzgjZiOUI8Ij8CQfJE0kfCSrJNolCSU4JWgllyXHJfcmJyZXJocmtybo -JxgnSSd6J6sn3CgNKD8ocSiiKNQpBik4KWspnSnQKgIqNSpoKpsqzysCKzYraSudK9EsBSw5LG4soizX -LQwtQS12Last4S4WLkwugi63Lu4vJC9aL5Evxy/+MDUwbDCkMNsxEjFKMYIxujHyMioyYzKbMtQzDTNG -M38zuDPxNCs0ZTSeNNg1EzVNNYc1wjX9Njc2cjauNuk3JDdgN5w31zgUOFA4jDjIOQU5Qjl/Obw5+To2 -OnQ6sjrvOy07azuqO+g8JzxlPKQ84z0iPWE9oT3gPiA+YD6gPuA/IT9hP6I/4kAjQGRApkDnQSlBakGs -Qe5CMEJyQrVC90M6Q31DwEQDREdEikTORRJFVUWaRd5GIkZnRqtG8Ec1R3tHwEgFSEtIkUjXSR1JY0mp -SfBKN0p9SsRLDEtTS5pL4kwqTHJMuk0CTUpNk03cTiVObk63TwBPSU+TT91QJ1BxULtRBlFQUZtR5lIx -UnxSx1MTU19TqlP2VEJUj1TbVShVdVXCVg9WXFapVvdXRFeSV+BYL1h9WMtZGllpWbhaB1pWWqZa9VtF -W5Vb5Vw1XIZc1l0nXXhdyV4aXmxevV8PX2Ffs2AFYFdgqmD8YU9homH1YklinGLwY0Njl2PrZEBklGTp -ZT1lkmXnZj1mkmboZz1nk2fpaD9olmjsaUNpmmnxakhqn2r3a09rp2v/bFdsr20IbWBtuW4SbmtuxG8e -b3hv0XArcIZw4HE6cZVx8HJLcqZzAXNdc7h0FHRwdMx1KHWFdeF2Pnabdvh3VnezeBF4bnjMeSp5iXnn -ekZ6pXsEe2N7wnwhfIF84X1BfaF+AX5ifsJ/I3+Ef+WAR4CogQqBa4HNgjCCkoL0g1eDuoQdhICE44VH -hauGDoZyhteHO4efiASIaYjOiTOJmYn+imSKyoswi5aL/IxjjMqNMY2Yjf+OZo7OjzaPnpAGkG6Q1pE/ -kaiSEZJ6kuOTTZO2lCCUipT0lV+VyZY0lp+XCpd1l+CYTJi4mSSZkJn8mmia1ZtCm6+cHJyJnPedZJ3S -nkCerp8dn4uf+qBpoNihR6G2oiailqMGo3aj5qRWpMelOKWpphqmi6b9p26n4KhSqMSpN6mpqhyqj6sC -q3Wr6axcrNCtRK24ri2uoa8Wr4uwALB1sOqxYLHWskuywrM4s660JbSctRO1irYBtnm28Ldot+C4WbjR -uUq5wro7urW7LrunvCG8m70VvY++Cr6Evv+/er/1wHDA7MFnwePCX8Lbw1jD1MRRxM7FS8XIxkbGw8dB -x7/IPci8yTrJuco4yrfLNsu2zDXMtc01zbXONs62zzfPuNA50LrRPNG+0j/SwdNE08bUSdTL1U7V0dZV -1tjXXNfg2GTY6Nls2fHadtr724DcBdyK3RDdlt4c3qLfKd+v4DbgveFE4cziU+Lb42Pj6+Rz5PzlhOYN -5pbnH+ep6DLovOlG6dDqW+rl63Dr++yG7RHtnO4o7rTvQO/M8Fjw5fFy8f/yjPMZ86f0NPTC9VD13vZt -9vv3ivgZ+Kj5OPnH+lf65/t3/Af8mP0p/br+S/7c/23//9IrLC0uWiRjbGFzc25hbWVYJGNsYXNzZXNf -EBBOU0JpdG1hcEltYWdlUmVwoy0vMFpOU0ltYWdlUmVwWE5TT2JqZWN00issMjNXTlNBcnJheaIyMNIr -LDU2Xk5TTXV0YWJsZUFycmF5ozUyMNM4OQ86OzxXTlNXaGl0ZVxOU0NvbG9yU3BhY2VEMCAwABADgAzS -Kyw+P1dOU0NvbG9yoj4w0issQUJXTlNJbWFnZaJBMAAIABEAGgAkACkAMgA3AEkATABRAFMAYgBoAHUA -fACLAJIAnwCmAK4AsACyALQAuQC7AL0AxADJANQA1gDYANoA3wDiAOQA5gDoAO8BBgEiASQBJhOWE5sT -phOvE8ITxhPRE9oT3xPnE+oT7xP+FAIUCRQRFB4UIxQlFCcULBQ0FDcUPBREAAAAAAAAAgEAAAAAAAAA -QwAAAAAAAAAAAAAAAAAAFEc +S2V5ZWRBcmNoaXZlctEICVRyb290gAGuCwwbHCEaJissMzY5P0JVJG51bGzXDQ4PEBESExQVFhcYGRpW +TlNTaXplViRjbGFzc1xOU0ltYWdlRmxhZ3NWTlNSZXBzW05TVGludENvbG9yV05TQ29sb3JeTlNSZXNp +emluZ01vZGWAAoANEiDDAACAA4AAgAsQAFZ7MSwgMX3SHQ4eIFpOUy5vYmplY3RzoR+ABIAK0h0OIiWi +IySABYAGgAnTDicoKSoaXxAUTlNUSUZGUmVwcmVzZW50YXRpb25fEBlOU0ludGVybmFsTGF5b3V0RGly +ZWN0aW9ugAiAB08REmxNTQAqAAAACgAAABABAAADAAAAAQABAAABAQADAAAAAQABAAABAgADAAAAAgAI +AAgBAwADAAAAAQABAAABBgADAAAAAQABAAABCgADAAAAAQABAAABEQAEAAAAAQAAAAgBEgADAAAAAQAB +AAABFQADAAAAAQACAAABFgADAAAAAQABAAABFwAEAAAAAQAAAAIBHAADAAAAAQABAAABKAADAAAAAQAC +AAABUgADAAAAAQABAAABUwADAAAAAgABAAGHcwAHAAARnAAAANAAAAAAAAARnGFwcGwCAAAAbW50ckdS +QVlYWVogB9wACAAXAA8ALgAPYWNzcEFQUEwAAAAAbm9uZQAAAAAAAAAAAAAAAAAAAAAAAPbWAAEAAAAA +0y1hcHBsAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFZGVzYwAA +AMAAAAB5ZHNjbQAAATwAAAgaY3BydAAACVgAAAAjd3RwdAAACXwAAAAUa1RSQwAACZAAAAgMZGVzYwAA +AAAAAAAfR2VuZXJpYyBHcmF5IEdhbW1hIDIuMiBQcm9maWxlAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAG1s +dWMAAAAAAAAAHwAAAAxza1NLAAAALgAAAYRkYURLAAAAOgAAAbJjYUVTAAAAOAAAAex2aVZOAAAAQAAA +AiRwdEJSAAAASgAAAmR1a1VBAAAALAAAAq5mckZVAAAAPgAAAtpodUhVAAAANAAAAxh6aFRXAAAAGgAA +A0xrb0tSAAAAIgAAA2ZuYk5PAAAAOgAAA4hjc0NaAAAAKAAAA8JoZUlMAAAAJAAAA+pyb1JPAAAAKgAA +BA5kZURFAAAATgAABDhpdElUAAAATgAABIZzdlNFAAAAOAAABNR6aENOAAAAGgAABQxqYUpQAAAAJgAA +BSZlbEdSAAAAKgAABUxwdFBPAAAAUgAABXZubE5MAAAAQAAABchlc0VTAAAATAAABgh0aFRIAAAAMgAA +BlR0clRSAAAAJAAABoZmaUZJAAAARgAABqpockhSAAAAPgAABvBwbFBMAAAASgAABy5hckVHAAAALAAA +B3hydVJVAAAAOgAAB6RlblVTAAAAPAAAB94AVgFhAGUAbwBiAGUAYwBuAOEAIABzAGkAdgDhACAAZwBh +AG0AYQAgADIALAAyAEcAZQBuAGUAcgBpAHMAawAgAGcAcgDlACAAMgAsADIAIABnAGEAbQBtAGEALQBw +AHIAbwBmAGkAbABHAGEAbQBtAGEAIABkAGUAIABnAHIAaQBzAG8AcwAgAGcAZQBuAOgAcgBpAGMAYQAg +ADIALgAyAEMepQB1ACAAaADsAG4AaAAgAE0A4AB1ACAAeADhAG0AIABDAGgAdQBuAGcAIABHAGEAbQBt +AGEAIAAyAC4AMgBQAGUAcgBmAGkAbAAgAEcAZQBuAOkAcgBpAGMAbwAgAGQAYQAgAEcAYQBtAGEAIABk +AGUAIABDAGkAbgB6AGEAcwAgADIALAAyBBcEMAQzBDAEOwRMBD0EMAAgAEcAcgBhAHkALQQzBDAEPAQw +ACAAMgAuADIAUAByAG8AZgBpAGwAIABnAOkAbgDpAHIAaQBxAHUAZQAgAGcAcgBpAHMAIABnAGEAbQBt +AGEAIAAyACwAMgDBAGwAdABhAGwA4QBuAG8AcwAgAHMAegD8AHIAawBlACAAZwBhAG0AbQBhACAAMgAu +ADKQGnUocHCWjlFJXqYAMgAuADKCcl9pY8+P8Md8vBgAINaMwMkAIKwQucgAIAAyAC4AMgAg1QS4XNMM +x3wARwBlAG4AZQByAGkAcwBrACAAZwByAOUAIABnAGEAbQBtAGEAIAAyACwAMgAtAHAAcgBvAGYAaQBs +AE8AYgBlAGMAbgDhACABYQBlAGQA4QAgAGcAYQBtAGEAIAAyAC4AMgXSBdAF3gXUACAF0AXkBdUF6AAg +BdsF3AXcBdkAIAAyAC4AMgBHAGEAbQBhACAAZwByAGkAIABnAGUAbgBlAHIAaQBjAQMAIAAyACwAMgBB +AGwAbABnAGUAbQBlAGkAbgBlAHMAIABHAHIAYQB1AHMAdAB1AGYAZQBuAC0AUAByAG8AZgBpAGwAIABH +AGEAbQBtAGEAIAAyACwAMgBQAHIAbwBmAGkAbABvACAAZwByAGkAZwBpAG8AIABnAGUAbgBlAHIAaQBj +AG8AIABkAGUAbABsAGEAIABnAGEAbQBtAGEAIAAyACwAMgBHAGUAbgBlAHIAaQBzAGsAIABnAHIA5QAg +ADIALAAyACAAZwBhAG0AbQBhAHAAcgBvAGYAaQBsZm6QGnBwXqZ8+2VwADIALgAyY8+P8GWHTvZOAIIs +MLAw7DCkMKww8zDeACAAMgAuADIAIDDXMO0w1TChMKQw6wOTA7UDvQO5A7oDzAAgA5MDugPBA7kAIAOT +A6wDvAO8A7EAIAAyAC4AMgBQAGUAcgBmAGkAbAAgAGcAZQBuAOkAcgBpAGMAbwAgAGQAZQAgAGMAaQBu +AHoAZQBuAHQAbwBzACAAZABhACAARwBhAG0AbQBhACAAMgAsADIAQQBsAGcAZQBtAGUAZQBuACAAZwBy +AGkAagBzACAAZwBhAG0AbQBhACAAMgAsADIALQBwAHIAbwBmAGkAZQBsAFAAZQByAGYAaQBsACAAZwBl +AG4A6QByAGkAYwBvACAAZABlACAAZwBhAG0AbQBhACAAZABlACAAZwByAGkAcwBlAHMAIAAyACwAMg4j +DjEOBw4qDjUOQQ4BDiEOIQ4yDkAOAQ4jDiIOTA4XDjEOSA4nDkQOGwAgADIALgAyAEcAZQBuAGUAbAAg +AEcAcgBpACAARwBhAG0AYQAgADIALAAyAFkAbABlAGkAbgBlAG4AIABoAGEAcgBtAGEAYQBuACAAZwBh +AG0AbQBhACAAMgAsADIAIAAtAHAAcgBvAGYAaQBpAGwAaQBHAGUAbgBlAHIAaQENAGsAaQAgAEcAcgBh +AHkAIABHAGEAbQBtAGEAIAAyAC4AMgAgAHAAcgBvAGYAaQBsAFUAbgBpAHcAZQByAHMAYQBsAG4AeQAg +AHAAcgBvAGYAaQBsACAAcwB6AGEAcgBvAVsAYwBpACAAZwBhAG0AbQBhACAAMgAsADIGOgYnBkUGJwAg +ADIALgAyACAGRAZIBkYAIAYxBkUGJwYvBkoAIAY5BicGRQQeBDEESQQwBE8AIARBBDUEQAQwBE8AIAQz +BDAEPAQ8BDAAIAAyACwAMgAtBD8EQAQ+BEQEOAQ7BEwARwBlAG4AZQByAGkAYwAgAEcAcgBhAHkAIABH +AGEAbQBtAGEAIAAyAC4AMgAgAFAAcgBvAGYAaQBsAGUAAHRleHQAAAAAQ29weXJpZ2h0IEFwcGxlIElu +Yy4sIDIwMTIAAFhZWiAAAAAAAADzUQABAAAAARbMY3VydgAAAAAAAAQAAAAABQAKAA8AFAAZAB4AIwAo +AC0AMgA3ADsAQABFAEoATwBUAFkAXgBjAGgAbQByAHcAfACBAIYAiwCQAJUAmgCfAKQAqQCuALIAtwC8 +AMEAxgDLANAA1QDbAOAA5QDrAPAA9gD7AQEBBwENARMBGQEfASUBKwEyATgBPgFFAUwBUgFZAWABZwFu +AXUBfAGDAYsBkgGaAaEBqQGxAbkBwQHJAdEB2QHhAekB8gH6AgMCDAIUAh0CJgIvAjgCQQJLAlQCXQJn +AnECegKEAo4CmAKiAqwCtgLBAssC1QLgAusC9QMAAwsDFgMhAy0DOANDA08DWgNmA3IDfgOKA5YDogOu +A7oDxwPTA+AD7AP5BAYEEwQgBC0EOwRIBFUEYwRxBH4EjASaBKgEtgTEBNME4QTwBP4FDQUcBSsFOgVJ +BVgFZwV3BYYFlgWmBbUFxQXVBeUF9gYGBhYGJwY3BkgGWQZqBnsGjAadBq8GwAbRBuMG9QcHBxkHKwc9 +B08HYQd0B4YHmQesB78H0gflB/gICwgfCDIIRghaCG4IggiWCKoIvgjSCOcI+wkQCSUJOglPCWQJeQmP +CaQJugnPCeUJ+woRCicKPQpUCmoKgQqYCq4KxQrcCvMLCwsiCzkLUQtpC4ALmAuwC8gL4Qv5DBIMKgxD +DFwMdQyODKcMwAzZDPMNDQ0mDUANWg10DY4NqQ3DDd4N+A4TDi4OSQ5kDn8Omw62DtIO7g8JDyUPQQ9e +D3oPlg+zD88P7BAJECYQQxBhEH4QmxC5ENcQ9RETETERTxFtEYwRqhHJEegSBxImEkUSZBKEEqMSwxLj +EwMTIxNDE2MTgxOkE8UT5RQGFCcUSRRqFIsUrRTOFPAVEhU0FVYVeBWbFb0V4BYDFiYWSRZsFo8WshbW +FvoXHRdBF2UXiReuF9IX9xgbGEAYZRiKGK8Y1Rj6GSAZRRlrGZEZtxndGgQaKhpRGncanhrFGuwbFBs7 +G2MbihuyG9ocAhwqHFIcexyjHMwc9R0eHUcdcB2ZHcMd7B4WHkAeah6UHr4e6R8THz4faR+UH78f6iAV +IEEgbCCYIMQg8CEcIUghdSGhIc4h+yInIlUigiKvIt0jCiM4I2YjlCPCI/AkHyRNJHwkqyTaJQklOCVo +JZclxyX3JicmVyaHJrcm6CcYJ0kneierJ9woDSg/KHEooijUKQYpOClrKZ0p0CoCKjUqaCqbKs8rAis2 +K2krnSvRLAUsOSxuLKIs1y0MLUEtdi2rLeEuFi5MLoIuty7uLyQvWi+RL8cv/jA1MGwwpDDbMRIxSjGC +Mbox8jIqMmMymzLUMw0zRjN/M7gz8TQrNGU0njTYNRM1TTWHNcI1/TY3NnI2rjbpNyQ3YDecN9c4FDhQ +OIw4yDkFOUI5fzm8Ofk6Njp0OrI67zstO2s7qjvoPCc8ZTykPOM9Ij1hPaE94D4gPmA+oD7gPyE/YT+i +P+JAI0BkQKZA50EpQWpBrEHuQjBCckK1QvdDOkN9Q8BEA0RHRIpEzkUSRVVFmkXeRiJGZ0arRvBHNUd7 +R8BIBUhLSJFI10kdSWNJqUnwSjdKfUrESwxLU0uaS+JMKkxyTLpNAk1KTZNN3E4lTm5Ot08AT0lPk0/d +UCdQcVC7UQZRUFGbUeZSMVJ8UsdTE1NfU6pT9lRCVI9U21UoVXVVwlYPVlxWqVb3V0RXklfgWC9YfVjL +WRpZaVm4WgdaVlqmWvVbRVuVW+VcNVyGXNZdJ114XcleGl5sXr1fD19hX7NgBWBXYKpg/GFPYaJh9WJJ +Ypxi8GNDY5dj62RAZJRk6WU9ZZJl52Y9ZpJm6Gc9Z5Nn6Wg/aJZo7GlDaZpp8WpIap9q92tPa6dr/2xX +bK9tCG1gbbluEm5rbsRvHm94b9FwK3CGcOBxOnGVcfByS3KmcwFzXXO4dBR0cHTMdSh1hXXhdj52m3b4 +d1Z3s3gReG54zHkqeYl553pGeqV7BHtje8J8IXyBfOF9QX2hfgF+Yn7CfyN/hH/lgEeAqIEKgWuBzYIw +gpKC9INXg7qEHYSAhOOFR4Wrhg6GcobXhzuHn4gEiGmIzokziZmJ/opkisqLMIuWi/yMY4zKjTGNmI3/ +jmaOzo82j56QBpBukNaRP5GokhGSepLjk02TtpQglIqU9JVflcmWNJaflwqXdZfgmEyYuJkkmZCZ/Jpo +mtWbQpuvnByciZz3nWSd0p5Anq6fHZ+Ln/qgaaDYoUehtqImopajBqN2o+akVqTHpTilqaYapoum/adu +p+CoUqjEqTepqaocqo+rAqt1q+msXKzQrUStuK4trqGvFq+LsACwdbDqsWCx1rJLssKzOLOutCW0nLUT +tYq2AbZ5tvC3aLfguFm40blKucK6O7q1uy67p7whvJu9Fb2Pvgq+hL7/v3q/9cBwwOzBZ8Hjwl/C28NY +w9TEUcTOxUvFyMZGxsPHQce/yD3IvMk6ybnKOMq3yzbLtsw1zLXNNc21zjbOts83z7jQOdC60TzRvtI/ +0sHTRNPG1EnUy9VO1dHWVdbY11zX4Nhk2OjZbNnx2nba+9uA3AXcit0Q3ZbeHN6i3ynfr+A24L3hROHM +4lPi2+Nj4+vkc+T85YTmDeaW5x/nqegy6LzpRunQ6lvq5etw6/vshu0R7ZzuKO6070DvzPBY8OXxcvH/ +8ozzGfOn9DT0wvVQ9d72bfb794r4Gfio+Tj5x/pX+uf7d/wH/Jj9Kf26/kv+3P9t///SLS4vMFokY2xh +c3NuYW1lWCRjbGFzc2VzXxAQTlNCaXRtYXBJbWFnZVJlcKMvMTJaTlNJbWFnZVJlcFhOU09iamVjdNIt +LjQ1V05TQXJyYXmiNDLSLS43OF5OU011dGFibGVBcnJheaM3NDLTOjsOPD0+V05TV2hpdGVcTlNDb2xv +clNwYWNlRDAgMAAQA4AM0i0uQEFXTlNDb2xvcqJAMtItLkNEV05TSW1hZ2WiQzIACAARABoAJAApADIA +NwBJAEwAUQBTAGIAaAB3AH4AhQCSAJkApQCtALwAvgDAAMUAxwDJAMsAzQDUANkA5ADmAOgA6gDvAPIA +9AD2APgA/wEWATIBNAE2E6YTqxO2E78T0hPWE+ET6hPvE/cT+hP/FA4UEhQZFCEULhQzFDUUNxQ8FEQU +RxRMFFQAAAAAAAACAQAAAAAAAABFAAAAAAAAAAAAAAAAAAAUVw diff --git a/i18n/src/main/resources/pt_BR.lproj/Browser.xib b/i18n/src/main/resources/pt_BR.lproj/Browser.xib index e70eef78d1..358f04def2 100644 --- a/i18n/src/main/resources/pt_BR.lproj/Browser.xib +++ b/i18n/src/main/resources/pt_BR.lproj/Browser.xib @@ -1,7 +1,7 @@ - + - + @@ -38,7 +38,7 @@ - + @@ -62,27 +62,27 @@ @@ -256,7 +256,7 @@ - + @@ -553,95 +553,95 @@ DQ YnBsaXN0MDDUAQIDBAUGBwpYJHZlcnNpb25ZJGFyY2hpdmVyVCR0b3BYJG9iamVjdHMSAAGGoF8QD05T -S2V5ZWRBcmNoaXZlctEICVRyb290gAGuCwwZGh8UJCkqMTQ3PUBVJG51bGzWDQ4PEBESExQVFhcYVk5T -U2l6ZV5OU1Jlc2l6aW5nTW9kZVYkY2xhc3NcTlNJbWFnZUZsYWdzVk5TUmVwc1dOU0NvbG9ygAIQAIAN -EiDDAACAA4ALVnsxLCAxfdIbDxweWk5TLm9iamVjdHOhHYAEgArSGw8gI6IhIoAFgAaACdMPJSYnKBRf -EBROU1RJRkZSZXByZXNlbnRhdGlvbl8QGU5TSW50ZXJuYWxMYXlvdXREaXJlY3Rpb26ACIAHTxESbE1N -ACoAAAAKAAAAEAEAAAMAAAABAAEAAAEBAAMAAAABAAEAAAECAAMAAAACAAgACAEDAAMAAAABAAEAAAEG -AAMAAAABAAEAAAEKAAMAAAABAAEAAAERAAQAAAABAAAACAESAAMAAAABAAEAAAEVAAMAAAABAAIAAAEW -AAMAAAABAAEAAAEXAAQAAAABAAAAAgEcAAMAAAABAAEAAAEoAAMAAAABAAIAAAFSAAMAAAABAAEAAAFT -AAMAAAACAAEAAYdzAAcAABGcAAAA0AAAAAAAABGcYXBwbAIAAABtbnRyR1JBWVhZWiAH3AAIABcADwAu -AA9hY3NwQVBQTAAAAABub25lAAAAAAAAAAAAAAAAAAAAAAAA9tYAAQAAAADTLWFwcGwAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAVkZXNjAAAAwAAAAHlkc2NtAAABPAAA -CBpjcHJ0AAAJWAAAACN3dHB0AAAJfAAAABRrVFJDAAAJkAAACAxkZXNjAAAAAAAAAB9HZW5lcmljIEdy -YXkgR2FtbWEgMi4yIFByb2ZpbGUAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAbWx1YwAAAAAAAAAfAAAADHNr -U0sAAAAuAAABhGRhREsAAAA6AAABsmNhRVMAAAA4AAAB7HZpVk4AAABAAAACJHB0QlIAAABKAAACZHVr -VUEAAAAsAAACrmZyRlUAAAA+AAAC2mh1SFUAAAA0AAADGHpoVFcAAAAaAAADTGtvS1IAAAAiAAADZm5i -Tk8AAAA6AAADiGNzQ1oAAAAoAAADwmhlSUwAAAAkAAAD6nJvUk8AAAAqAAAEDmRlREUAAABOAAAEOGl0 -SVQAAABOAAAEhnN2U0UAAAA4AAAE1HpoQ04AAAAaAAAFDGphSlAAAAAmAAAFJmVsR1IAAAAqAAAFTHB0 -UE8AAABSAAAFdm5sTkwAAABAAAAFyGVzRVMAAABMAAAGCHRoVEgAAAAyAAAGVHRyVFIAAAAkAAAGhmZp -RkkAAABGAAAGqmhySFIAAAA+AAAG8HBsUEwAAABKAAAHLmFyRUcAAAAsAAAHeHJ1UlUAAAA6AAAHpGVu -VVMAAAA8AAAH3gBWAWEAZQBvAGIAZQBjAG4A4QAgAHMAaQB2AOEAIABnAGEAbQBhACAAMgAsADIARwBl -AG4AZQByAGkAcwBrACAAZwByAOUAIAAyACwAMgAgAGcAYQBtAG0AYQAtAHAAcgBvAGYAaQBsAEcAYQBt -AG0AYQAgAGQAZQAgAGcAcgBpAHMAbwBzACAAZwBlAG4A6AByAGkAYwBhACAAMgAuADIAQx6lAHUAIABo -AOwAbgBoACAATQDgAHUAIAB4AOEAbQAgAEMAaAB1AG4AZwAgAEcAYQBtAG0AYQAgADIALgAyAFAAZQBy -AGYAaQBsACAARwBlAG4A6QByAGkAYwBvACAAZABhACAARwBhAG0AYQAgAGQAZQAgAEMAaQBuAHoAYQBz -ACAAMgAsADIEFwQwBDMEMAQ7BEwEPQQwACAARwByAGEAeQAtBDMEMAQ8BDAAIAAyAC4AMgBQAHIAbwBm -AGkAbAAgAGcA6QBuAOkAcgBpAHEAdQBlACAAZwByAGkAcwAgAGcAYQBtAG0AYQAgADIALAAyAMEAbAB0 -AGEAbADhAG4AbwBzACAAcwB6APwAcgBrAGUAIABnAGEAbQBtAGEAIAAyAC4AMpAadShwcJaOUUlepgAy -AC4AMoJyX2ljz4/wx3y8GAAg1ozAyQAgrBC5yAAgADIALgAyACDVBLhc0wzHfABHAGUAbgBlAHIAaQBz -AGsAIABnAHIA5QAgAGcAYQBtAG0AYQAgADIALAAyAC0AcAByAG8AZgBpAGwATwBiAGUAYwBuAOEAIAFh -AGUAZADhACAAZwBhAG0AYQAgADIALgAyBdIF0AXeBdQAIAXQBeQF1QXoACAF2wXcBdwF2QAgADIALgAy -AEcAYQBtAGEAIABnAHIAaQAgAGcAZQBuAGUAcgBpAGMBAwAgADIALAAyAEEAbABsAGcAZQBtAGUAaQBu -AGUAcwAgAEcAcgBhAHUAcwB0AHUAZgBlAG4ALQBQAHIAbwBmAGkAbAAgAEcAYQBtAG0AYQAgADIALAAy -AFAAcgBvAGYAaQBsAG8AIABnAHIAaQBnAGkAbwAgAGcAZQBuAGUAcgBpAGMAbwAgAGQAZQBsAGwAYQAg -AGcAYQBtAG0AYQAgADIALAAyAEcAZQBuAGUAcgBpAHMAawAgAGcAcgDlACAAMgAsADIAIABnAGEAbQBt -AGEAcAByAG8AZgBpAGxmbpAacHBepnz7ZXAAMgAuADJjz4/wZYdO9k4AgiwwsDDsMKQwrDDzMN4AIAAy -AC4AMgAgMNcw7TDVMKEwpDDrA5MDtQO9A7kDugPMACADkwO6A8EDuQAgA5MDrAO8A7wDsQAgADIALgAy -AFAAZQByAGYAaQBsACAAZwBlAG4A6QByAGkAYwBvACAAZABlACAAYwBpAG4AegBlAG4AdABvAHMAIABk -AGEAIABHAGEAbQBtAGEAIAAyACwAMgBBAGwAZwBlAG0AZQBlAG4AIABnAHIAaQBqAHMAIABnAGEAbQBt -AGEAIAAyACwAMgAtAHAAcgBvAGYAaQBlAGwAUABlAHIAZgBpAGwAIABnAGUAbgDpAHIAaQBjAG8AIABk -AGUAIABnAGEAbQBtAGEAIABkAGUAIABnAHIAaQBzAGUAcwAgADIALAAyDiMOMQ4HDioONQ5BDgEOIQ4h -DjIOQA4BDiMOIg5MDhcOMQ5IDicORA4bACAAMgAuADIARwBlAG4AZQBsACAARwByAGkAIABHAGEAbQBh -ACAAMgAsADIAWQBsAGUAaQBuAGUAbgAgAGgAYQByAG0AYQBhAG4AIABnAGEAbQBtAGEAIAAyACwAMgAg -AC0AcAByAG8AZgBpAGkAbABpAEcAZQBuAGUAcgBpAQ0AawBpACAARwByAGEAeQAgAEcAYQBtAG0AYQAg -ADIALgAyACAAcAByAG8AZgBpAGwAVQBuAGkAdwBlAHIAcwBhAGwAbgB5ACAAcAByAG8AZgBpAGwAIABz -AHoAYQByAG8BWwBjAGkAIABnAGEAbQBtAGEAIAAyACwAMgY6BicGRQYnACAAMgAuADIAIAZEBkgGRgAg -BjEGRQYnBi8GSgAgBjkGJwZFBB4EMQRJBDAETwAgBEEENQRABDAETwAgBDMEMAQ8BDwEMAAgADIALAAy -AC0EPwRABD4ERAQ4BDsETABHAGUAbgBlAHIAaQBjACAARwByAGEAeQAgAEcAYQBtAG0AYQAgADIALgAy -ACAAUAByAG8AZgBpAGwAZQAAdGV4dAAAAABDb3B5cmlnaHQgQXBwbGUgSW5jLiwgMjAxMgAAWFlaIAAA -AAAAAPNRAAEAAAABFsxjdXJ2AAAAAAAABAAAAAAFAAoADwAUABkAHgAjACgALQAyADcAOwBAAEUASgBP -AFQAWQBeAGMAaABtAHIAdwB8AIEAhgCLAJAAlQCaAJ8ApACpAK4AsgC3ALwAwQDGAMsA0ADVANsA4ADl -AOsA8AD2APsBAQEHAQ0BEwEZAR8BJQErATIBOAE+AUUBTAFSAVkBYAFnAW4BdQF8AYMBiwGSAZoBoQGp -AbEBuQHBAckB0QHZAeEB6QHyAfoCAwIMAhQCHQImAi8COAJBAksCVAJdAmcCcQJ6AoQCjgKYAqICrAK2 -AsECywLVAuAC6wL1AwADCwMWAyEDLQM4A0MDTwNaA2YDcgN+A4oDlgOiA64DugPHA9MD4APsA/kEBgQT -BCAELQQ7BEgEVQRjBHEEfgSMBJoEqAS2BMQE0wThBPAE/gUNBRwFKwU6BUkFWAVnBXcFhgWWBaYFtQXF -BdUF5QX2BgYGFgYnBjcGSAZZBmoGewaMBp0GrwbABtEG4wb1BwcHGQcrBz0HTwdhB3QHhgeZB6wHvwfS -B+UH+AgLCB8IMghGCFoIbgiCCJYIqgi+CNII5wj7CRAJJQk6CU8JZAl5CY8JpAm6Cc8J5Qn7ChEKJwo9 -ClQKagqBCpgKrgrFCtwK8wsLCyILOQtRC2kLgAuYC7ALyAvhC/kMEgwqDEMMXAx1DI4MpwzADNkM8w0N -DSYNQA1aDXQNjg2pDcMN3g34DhMOLg5JDmQOfw6bDrYO0g7uDwkPJQ9BD14Peg+WD7MPzw/sEAkQJhBD -EGEQfhCbELkQ1xD1ERMRMRFPEW0RjBGqEckR6BIHEiYSRRJkEoQSoxLDEuMTAxMjE0MTYxODE6QTxRPl -FAYUJxRJFGoUixStFM4U8BUSFTQVVhV4FZsVvRXgFgMWJhZJFmwWjxayFtYW+hcdF0EXZReJF64X0hf3 -GBsYQBhlGIoYrxjVGPoZIBlFGWsZkRm3Gd0aBBoqGlEadxqeGsUa7BsUGzsbYxuKG7Ib2hwCHCocUhx7 -HKMczBz1HR4dRx1wHZkdwx3sHhYeQB5qHpQevh7pHxMfPh9pH5Qfvx/qIBUgQSBsIJggxCDwIRwhSCF1 -IaEhziH7IiciVSKCIq8i3SMKIzgjZiOUI8Ij8CQfJE0kfCSrJNolCSU4JWgllyXHJfcmJyZXJocmtybo -JxgnSSd6J6sn3CgNKD8ocSiiKNQpBik4KWspnSnQKgIqNSpoKpsqzysCKzYraSudK9EsBSw5LG4soizX -LQwtQS12Last4S4WLkwugi63Lu4vJC9aL5Evxy/+MDUwbDCkMNsxEjFKMYIxujHyMioyYzKbMtQzDTNG -M38zuDPxNCs0ZTSeNNg1EzVNNYc1wjX9Njc2cjauNuk3JDdgN5w31zgUOFA4jDjIOQU5Qjl/Obw5+To2 -OnQ6sjrvOy07azuqO+g8JzxlPKQ84z0iPWE9oT3gPiA+YD6gPuA/IT9hP6I/4kAjQGRApkDnQSlBakGs -Qe5CMEJyQrVC90M6Q31DwEQDREdEikTORRJFVUWaRd5GIkZnRqtG8Ec1R3tHwEgFSEtIkUjXSR1JY0mp -SfBKN0p9SsRLDEtTS5pL4kwqTHJMuk0CTUpNk03cTiVObk63TwBPSU+TT91QJ1BxULtRBlFQUZtR5lIx -UnxSx1MTU19TqlP2VEJUj1TbVShVdVXCVg9WXFapVvdXRFeSV+BYL1h9WMtZGllpWbhaB1pWWqZa9VtF -W5Vb5Vw1XIZc1l0nXXhdyV4aXmxevV8PX2Ffs2AFYFdgqmD8YU9homH1YklinGLwY0Njl2PrZEBklGTp -ZT1lkmXnZj1mkmboZz1nk2fpaD9olmjsaUNpmmnxakhqn2r3a09rp2v/bFdsr20IbWBtuW4SbmtuxG8e -b3hv0XArcIZw4HE6cZVx8HJLcqZzAXNdc7h0FHRwdMx1KHWFdeF2Pnabdvh3VnezeBF4bnjMeSp5iXnn -ekZ6pXsEe2N7wnwhfIF84X1BfaF+AX5ifsJ/I3+Ef+WAR4CogQqBa4HNgjCCkoL0g1eDuoQdhICE44VH -hauGDoZyhteHO4efiASIaYjOiTOJmYn+imSKyoswi5aL/IxjjMqNMY2Yjf+OZo7OjzaPnpAGkG6Q1pE/ -kaiSEZJ6kuOTTZO2lCCUipT0lV+VyZY0lp+XCpd1l+CYTJi4mSSZkJn8mmia1ZtCm6+cHJyJnPedZJ3S -nkCerp8dn4uf+qBpoNihR6G2oiailqMGo3aj5qRWpMelOKWpphqmi6b9p26n4KhSqMSpN6mpqhyqj6sC -q3Wr6axcrNCtRK24ri2uoa8Wr4uwALB1sOqxYLHWskuywrM4s660JbSctRO1irYBtnm28Ldot+C4WbjR -uUq5wro7urW7LrunvCG8m70VvY++Cr6Evv+/er/1wHDA7MFnwePCX8Lbw1jD1MRRxM7FS8XIxkbGw8dB -x7/IPci8yTrJuco4yrfLNsu2zDXMtc01zbXONs62zzfPuNA50LrRPNG+0j/SwdNE08bUSdTL1U7V0dZV -1tjXXNfg2GTY6Nls2fHadtr724DcBdyK3RDdlt4c3qLfKd+v4DbgveFE4cziU+Lb42Pj6+Rz5PzlhOYN -5pbnH+ep6DLovOlG6dDqW+rl63Dr++yG7RHtnO4o7rTvQO/M8Fjw5fFy8f/yjPMZ86f0NPTC9VD13vZt -9vv3ivgZ+Kj5OPnH+lf65/t3/Af8mP0p/br+S/7c/23//9IrLC0uWiRjbGFzc25hbWVYJGNsYXNzZXNf -EBBOU0JpdG1hcEltYWdlUmVwoy0vMFpOU0ltYWdlUmVwWE5TT2JqZWN00issMjNXTlNBcnJheaIyMNIr -LDU2Xk5TTXV0YWJsZUFycmF5ozUyMNM4OQ86OzxXTlNXaGl0ZVxOU0NvbG9yU3BhY2VEMCAwABADgAzS -Kyw+P1dOU0NvbG9yoj4w0issQUJXTlNJbWFnZaJBMAAIABEAGgAkACkAMgA3AEkATABRAFMAYgBoAHUA -fACLAJIAnwCmAK4AsACyALQAuQC7AL0AxADJANQA1gDYANoA3wDiAOQA5gDoAO8BBgEiASQBJhOWE5sT -phOvE8ITxhPRE9oT3xPnE+oT7xP+FAIUCRQRFB4UIxQlFCcULBQ0FDcUPBREAAAAAAAAAgEAAAAAAAAA -QwAAAAAAAAAAAAAAAAAAFEc +S2V5ZWRBcmNoaXZlctEICVRyb290gAGuCwwbHCEaJissMzY5P0JVJG51bGzXDQ4PEBESExQVFhcYGRpW +TlNTaXplViRjbGFzc1xOU0ltYWdlRmxhZ3NWTlNSZXBzW05TVGludENvbG9yV05TQ29sb3JeTlNSZXNp +emluZ01vZGWAAoANEiDDAACAA4AAgAsQAFZ7MSwgMX3SHQ4eIFpOUy5vYmplY3RzoR+ABIAK0h0OIiWi +IySABYAGgAnTDicoKSoaXxAUTlNUSUZGUmVwcmVzZW50YXRpb25fEBlOU0ludGVybmFsTGF5b3V0RGly +ZWN0aW9ugAiAB08REmxNTQAqAAAACgAAABABAAADAAAAAQABAAABAQADAAAAAQABAAABAgADAAAAAgAI +AAgBAwADAAAAAQABAAABBgADAAAAAQABAAABCgADAAAAAQABAAABEQAEAAAAAQAAAAgBEgADAAAAAQAB +AAABFQADAAAAAQACAAABFgADAAAAAQABAAABFwAEAAAAAQAAAAIBHAADAAAAAQABAAABKAADAAAAAQAC +AAABUgADAAAAAQABAAABUwADAAAAAgABAAGHcwAHAAARnAAAANAAAAAAAAARnGFwcGwCAAAAbW50ckdS +QVlYWVogB9wACAAXAA8ALgAPYWNzcEFQUEwAAAAAbm9uZQAAAAAAAAAAAAAAAAAAAAAAAPbWAAEAAAAA +0y1hcHBsAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFZGVzYwAA +AMAAAAB5ZHNjbQAAATwAAAgaY3BydAAACVgAAAAjd3RwdAAACXwAAAAUa1RSQwAACZAAAAgMZGVzYwAA +AAAAAAAfR2VuZXJpYyBHcmF5IEdhbW1hIDIuMiBQcm9maWxlAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAG1s +dWMAAAAAAAAAHwAAAAxza1NLAAAALgAAAYRkYURLAAAAOgAAAbJjYUVTAAAAOAAAAex2aVZOAAAAQAAA +AiRwdEJSAAAASgAAAmR1a1VBAAAALAAAAq5mckZVAAAAPgAAAtpodUhVAAAANAAAAxh6aFRXAAAAGgAA +A0xrb0tSAAAAIgAAA2ZuYk5PAAAAOgAAA4hjc0NaAAAAKAAAA8JoZUlMAAAAJAAAA+pyb1JPAAAAKgAA +BA5kZURFAAAATgAABDhpdElUAAAATgAABIZzdlNFAAAAOAAABNR6aENOAAAAGgAABQxqYUpQAAAAJgAA +BSZlbEdSAAAAKgAABUxwdFBPAAAAUgAABXZubE5MAAAAQAAABchlc0VTAAAATAAABgh0aFRIAAAAMgAA +BlR0clRSAAAAJAAABoZmaUZJAAAARgAABqpockhSAAAAPgAABvBwbFBMAAAASgAABy5hckVHAAAALAAA +B3hydVJVAAAAOgAAB6RlblVTAAAAPAAAB94AVgFhAGUAbwBiAGUAYwBuAOEAIABzAGkAdgDhACAAZwBh +AG0AYQAgADIALAAyAEcAZQBuAGUAcgBpAHMAawAgAGcAcgDlACAAMgAsADIAIABnAGEAbQBtAGEALQBw +AHIAbwBmAGkAbABHAGEAbQBtAGEAIABkAGUAIABnAHIAaQBzAG8AcwAgAGcAZQBuAOgAcgBpAGMAYQAg +ADIALgAyAEMepQB1ACAAaADsAG4AaAAgAE0A4AB1ACAAeADhAG0AIABDAGgAdQBuAGcAIABHAGEAbQBt +AGEAIAAyAC4AMgBQAGUAcgBmAGkAbAAgAEcAZQBuAOkAcgBpAGMAbwAgAGQAYQAgAEcAYQBtAGEAIABk +AGUAIABDAGkAbgB6AGEAcwAgADIALAAyBBcEMAQzBDAEOwRMBD0EMAAgAEcAcgBhAHkALQQzBDAEPAQw +ACAAMgAuADIAUAByAG8AZgBpAGwAIABnAOkAbgDpAHIAaQBxAHUAZQAgAGcAcgBpAHMAIABnAGEAbQBt +AGEAIAAyACwAMgDBAGwAdABhAGwA4QBuAG8AcwAgAHMAegD8AHIAawBlACAAZwBhAG0AbQBhACAAMgAu +ADKQGnUocHCWjlFJXqYAMgAuADKCcl9pY8+P8Md8vBgAINaMwMkAIKwQucgAIAAyAC4AMgAg1QS4XNMM +x3wARwBlAG4AZQByAGkAcwBrACAAZwByAOUAIABnAGEAbQBtAGEAIAAyACwAMgAtAHAAcgBvAGYAaQBs +AE8AYgBlAGMAbgDhACABYQBlAGQA4QAgAGcAYQBtAGEAIAAyAC4AMgXSBdAF3gXUACAF0AXkBdUF6AAg +BdsF3AXcBdkAIAAyAC4AMgBHAGEAbQBhACAAZwByAGkAIABnAGUAbgBlAHIAaQBjAQMAIAAyACwAMgBB +AGwAbABnAGUAbQBlAGkAbgBlAHMAIABHAHIAYQB1AHMAdAB1AGYAZQBuAC0AUAByAG8AZgBpAGwAIABH +AGEAbQBtAGEAIAAyACwAMgBQAHIAbwBmAGkAbABvACAAZwByAGkAZwBpAG8AIABnAGUAbgBlAHIAaQBj +AG8AIABkAGUAbABsAGEAIABnAGEAbQBtAGEAIAAyACwAMgBHAGUAbgBlAHIAaQBzAGsAIABnAHIA5QAg +ADIALAAyACAAZwBhAG0AbQBhAHAAcgBvAGYAaQBsZm6QGnBwXqZ8+2VwADIALgAyY8+P8GWHTvZOAIIs +MLAw7DCkMKww8zDeACAAMgAuADIAIDDXMO0w1TChMKQw6wOTA7UDvQO5A7oDzAAgA5MDugPBA7kAIAOT +A6wDvAO8A7EAIAAyAC4AMgBQAGUAcgBmAGkAbAAgAGcAZQBuAOkAcgBpAGMAbwAgAGQAZQAgAGMAaQBu +AHoAZQBuAHQAbwBzACAAZABhACAARwBhAG0AbQBhACAAMgAsADIAQQBsAGcAZQBtAGUAZQBuACAAZwBy +AGkAagBzACAAZwBhAG0AbQBhACAAMgAsADIALQBwAHIAbwBmAGkAZQBsAFAAZQByAGYAaQBsACAAZwBl +AG4A6QByAGkAYwBvACAAZABlACAAZwBhAG0AbQBhACAAZABlACAAZwByAGkAcwBlAHMAIAAyACwAMg4j +DjEOBw4qDjUOQQ4BDiEOIQ4yDkAOAQ4jDiIOTA4XDjEOSA4nDkQOGwAgADIALgAyAEcAZQBuAGUAbAAg +AEcAcgBpACAARwBhAG0AYQAgADIALAAyAFkAbABlAGkAbgBlAG4AIABoAGEAcgBtAGEAYQBuACAAZwBh +AG0AbQBhACAAMgAsADIAIAAtAHAAcgBvAGYAaQBpAGwAaQBHAGUAbgBlAHIAaQENAGsAaQAgAEcAcgBh +AHkAIABHAGEAbQBtAGEAIAAyAC4AMgAgAHAAcgBvAGYAaQBsAFUAbgBpAHcAZQByAHMAYQBsAG4AeQAg +AHAAcgBvAGYAaQBsACAAcwB6AGEAcgBvAVsAYwBpACAAZwBhAG0AbQBhACAAMgAsADIGOgYnBkUGJwAg +ADIALgAyACAGRAZIBkYAIAYxBkUGJwYvBkoAIAY5BicGRQQeBDEESQQwBE8AIARBBDUEQAQwBE8AIAQz +BDAEPAQ8BDAAIAAyACwAMgAtBD8EQAQ+BEQEOAQ7BEwARwBlAG4AZQByAGkAYwAgAEcAcgBhAHkAIABH +AGEAbQBtAGEAIAAyAC4AMgAgAFAAcgBvAGYAaQBsAGUAAHRleHQAAAAAQ29weXJpZ2h0IEFwcGxlIElu +Yy4sIDIwMTIAAFhZWiAAAAAAAADzUQABAAAAARbMY3VydgAAAAAAAAQAAAAABQAKAA8AFAAZAB4AIwAo +AC0AMgA3ADsAQABFAEoATwBUAFkAXgBjAGgAbQByAHcAfACBAIYAiwCQAJUAmgCfAKQAqQCuALIAtwC8 +AMEAxgDLANAA1QDbAOAA5QDrAPAA9gD7AQEBBwENARMBGQEfASUBKwEyATgBPgFFAUwBUgFZAWABZwFu +AXUBfAGDAYsBkgGaAaEBqQGxAbkBwQHJAdEB2QHhAekB8gH6AgMCDAIUAh0CJgIvAjgCQQJLAlQCXQJn +AnECegKEAo4CmAKiAqwCtgLBAssC1QLgAusC9QMAAwsDFgMhAy0DOANDA08DWgNmA3IDfgOKA5YDogOu +A7oDxwPTA+AD7AP5BAYEEwQgBC0EOwRIBFUEYwRxBH4EjASaBKgEtgTEBNME4QTwBP4FDQUcBSsFOgVJ +BVgFZwV3BYYFlgWmBbUFxQXVBeUF9gYGBhYGJwY3BkgGWQZqBnsGjAadBq8GwAbRBuMG9QcHBxkHKwc9 +B08HYQd0B4YHmQesB78H0gflB/gICwgfCDIIRghaCG4IggiWCKoIvgjSCOcI+wkQCSUJOglPCWQJeQmP +CaQJugnPCeUJ+woRCicKPQpUCmoKgQqYCq4KxQrcCvMLCwsiCzkLUQtpC4ALmAuwC8gL4Qv5DBIMKgxD +DFwMdQyODKcMwAzZDPMNDQ0mDUANWg10DY4NqQ3DDd4N+A4TDi4OSQ5kDn8Omw62DtIO7g8JDyUPQQ9e +D3oPlg+zD88P7BAJECYQQxBhEH4QmxC5ENcQ9RETETERTxFtEYwRqhHJEegSBxImEkUSZBKEEqMSwxLj +EwMTIxNDE2MTgxOkE8UT5RQGFCcUSRRqFIsUrRTOFPAVEhU0FVYVeBWbFb0V4BYDFiYWSRZsFo8WshbW +FvoXHRdBF2UXiReuF9IX9xgbGEAYZRiKGK8Y1Rj6GSAZRRlrGZEZtxndGgQaKhpRGncanhrFGuwbFBs7 +G2MbihuyG9ocAhwqHFIcexyjHMwc9R0eHUcdcB2ZHcMd7B4WHkAeah6UHr4e6R8THz4faR+UH78f6iAV +IEEgbCCYIMQg8CEcIUghdSGhIc4h+yInIlUigiKvIt0jCiM4I2YjlCPCI/AkHyRNJHwkqyTaJQklOCVo +JZclxyX3JicmVyaHJrcm6CcYJ0kneierJ9woDSg/KHEooijUKQYpOClrKZ0p0CoCKjUqaCqbKs8rAis2 +K2krnSvRLAUsOSxuLKIs1y0MLUEtdi2rLeEuFi5MLoIuty7uLyQvWi+RL8cv/jA1MGwwpDDbMRIxSjGC +Mbox8jIqMmMymzLUMw0zRjN/M7gz8TQrNGU0njTYNRM1TTWHNcI1/TY3NnI2rjbpNyQ3YDecN9c4FDhQ +OIw4yDkFOUI5fzm8Ofk6Njp0OrI67zstO2s7qjvoPCc8ZTykPOM9Ij1hPaE94D4gPmA+oD7gPyE/YT+i +P+JAI0BkQKZA50EpQWpBrEHuQjBCckK1QvdDOkN9Q8BEA0RHRIpEzkUSRVVFmkXeRiJGZ0arRvBHNUd7 +R8BIBUhLSJFI10kdSWNJqUnwSjdKfUrESwxLU0uaS+JMKkxyTLpNAk1KTZNN3E4lTm5Ot08AT0lPk0/d +UCdQcVC7UQZRUFGbUeZSMVJ8UsdTE1NfU6pT9lRCVI9U21UoVXVVwlYPVlxWqVb3V0RXklfgWC9YfVjL +WRpZaVm4WgdaVlqmWvVbRVuVW+VcNVyGXNZdJ114XcleGl5sXr1fD19hX7NgBWBXYKpg/GFPYaJh9WJJ +Ypxi8GNDY5dj62RAZJRk6WU9ZZJl52Y9ZpJm6Gc9Z5Nn6Wg/aJZo7GlDaZpp8WpIap9q92tPa6dr/2xX +bK9tCG1gbbluEm5rbsRvHm94b9FwK3CGcOBxOnGVcfByS3KmcwFzXXO4dBR0cHTMdSh1hXXhdj52m3b4 +d1Z3s3gReG54zHkqeYl553pGeqV7BHtje8J8IXyBfOF9QX2hfgF+Yn7CfyN/hH/lgEeAqIEKgWuBzYIw +gpKC9INXg7qEHYSAhOOFR4Wrhg6GcobXhzuHn4gEiGmIzokziZmJ/opkisqLMIuWi/yMY4zKjTGNmI3/ +jmaOzo82j56QBpBukNaRP5GokhGSepLjk02TtpQglIqU9JVflcmWNJaflwqXdZfgmEyYuJkkmZCZ/Jpo +mtWbQpuvnByciZz3nWSd0p5Anq6fHZ+Ln/qgaaDYoUehtqImopajBqN2o+akVqTHpTilqaYapoum/adu +p+CoUqjEqTepqaocqo+rAqt1q+msXKzQrUStuK4trqGvFq+LsACwdbDqsWCx1rJLssKzOLOutCW0nLUT +tYq2AbZ5tvC3aLfguFm40blKucK6O7q1uy67p7whvJu9Fb2Pvgq+hL7/v3q/9cBwwOzBZ8Hjwl/C28NY +w9TEUcTOxUvFyMZGxsPHQce/yD3IvMk6ybnKOMq3yzbLtsw1zLXNNc21zjbOts83z7jQOdC60TzRvtI/ +0sHTRNPG1EnUy9VO1dHWVdbY11zX4Nhk2OjZbNnx2nba+9uA3AXcit0Q3ZbeHN6i3ynfr+A24L3hROHM +4lPi2+Nj4+vkc+T85YTmDeaW5x/nqegy6LzpRunQ6lvq5etw6/vshu0R7ZzuKO6070DvzPBY8OXxcvH/ +8ozzGfOn9DT0wvVQ9d72bfb794r4Gfio+Tj5x/pX+uf7d/wH/Jj9Kf26/kv+3P9t///SLS4vMFokY2xh +c3NuYW1lWCRjbGFzc2VzXxAQTlNCaXRtYXBJbWFnZVJlcKMvMTJaTlNJbWFnZVJlcFhOU09iamVjdNIt +LjQ1V05TQXJyYXmiNDLSLS43OF5OU011dGFibGVBcnJheaM3NDLTOjsOPD0+V05TV2hpdGVcTlNDb2xv +clNwYWNlRDAgMAAQA4AM0i0uQEFXTlNDb2xvcqJAMtItLkNEV05TSW1hZ2WiQzIACAARABoAJAApADIA +NwBJAEwAUQBTAGIAaAB3AH4AhQCSAJkApQCtALwAvgDAAMUAxwDJAMsAzQDUANkA5ADmAOgA6gDvAPIA +9AD2APgA/wEWATIBNAE2E6YTqxO2E78T0hPWE+ET6hPvE/cT+hP/FA4UEhQZFCEULhQzFDUUNxQ8FEQU +RxRMFFQAAAAAAAACAQAAAAAAAABFAAAAAAAAAAAAAAAAAAAUVw diff --git a/i18n/src/main/resources/pt_BR.lproj/Command.xib b/i18n/src/main/resources/pt_BR.lproj/Command.xib index 1b19c3eddb..42adc0ecb4 100644 --- a/i18n/src/main/resources/pt_BR.lproj/Command.xib +++ b/i18n/src/main/resources/pt_BR.lproj/Command.xib @@ -1,7 +1,7 @@ - + - + diff --git a/i18n/src/main/resources/pt_BR.lproj/Connection.xib b/i18n/src/main/resources/pt_BR.lproj/Connection.xib index 73dd4bb91d..9cd2757d91 100644 --- a/i18n/src/main/resources/pt_BR.lproj/Connection.xib +++ b/i18n/src/main/resources/pt_BR.lproj/Connection.xib @@ -1,7 +1,7 @@ - + - + @@ -76,7 +76,7 @@ - + diff --git a/i18n/src/main/resources/pt_BR.lproj/Info.xib b/i18n/src/main/resources/pt_BR.lproj/Info.xib index 7a47d93e7d..bf266563d4 100644 --- a/i18n/src/main/resources/pt_BR.lproj/Info.xib +++ b/i18n/src/main/resources/pt_BR.lproj/Info.xib @@ -1,7 +1,7 @@ - + - + @@ -89,7 +89,7 @@ - + @@ -1017,21 +1017,21 @@ - + - + - + @@ -1121,21 +1121,21 @@ - + - + - + @@ -1250,27 +1250,27 @@ diff --git a/i18n/src/main/resources/pt_BR.lproj/Login.strings b/i18n/src/main/resources/pt_BR.lproj/Login.strings index b182ed27b8..476375ebb9 100644 Binary files a/i18n/src/main/resources/pt_BR.lproj/Login.strings and b/i18n/src/main/resources/pt_BR.lproj/Login.strings differ diff --git a/i18n/src/main/resources/pt_BR.lproj/Main.xib b/i18n/src/main/resources/pt_BR.lproj/Main.xib index ba15438ae7..d31651e98f 100644 --- a/i18n/src/main/resources/pt_BR.lproj/Main.xib +++ b/i18n/src/main/resources/pt_BR.lproj/Main.xib @@ -1,7 +1,7 @@ - + - + @@ -159,19 +159,19 @@ - + - + - + @@ -321,12 +321,12 @@ CA - + - + @@ -566,6 +566,10 @@ CA - + + + + + diff --git a/i18n/src/main/resources/pt_BR.lproj/Preferences.xib b/i18n/src/main/resources/pt_BR.lproj/Preferences.xib index 0074c2b74a..25a0a0484e 100644 --- a/i18n/src/main/resources/pt_BR.lproj/Preferences.xib +++ b/i18n/src/main/resources/pt_BR.lproj/Preferences.xib @@ -1,7 +1,7 @@ - + - + @@ -266,7 +266,7 @@ - + @@ -307,7 +307,7 @@ - + @@ -538,7 +538,7 @@ - + @@ -578,7 +578,7 @@ - + @@ -613,7 +613,7 @@ - + @@ -642,7 +642,7 @@ - + @@ -1218,7 +1218,7 @@ - + @@ -1250,7 +1250,7 @@ - + @@ -1290,7 +1290,7 @@ - + @@ -1363,7 +1363,7 @@ - + @@ -1558,7 +1558,7 @@ - + @@ -1618,7 +1618,7 @@ - + @@ -1643,7 +1643,7 @@ - + @@ -1685,7 +1685,7 @@ - + @@ -1713,7 +1713,7 @@ - + @@ -1741,7 +1741,7 @@ - + @@ -1770,7 +1770,7 @@ - + @@ -1805,7 +1805,7 @@ - + @@ -1889,7 +1889,7 @@ - + @@ -1948,7 +1948,7 @@ - + @@ -1976,7 +1976,7 @@ - + @@ -2005,7 +2005,7 @@ - + diff --git a/i18n/src/main/resources/pt_BR.lproj/Profiles.xib b/i18n/src/main/resources/pt_BR.lproj/Profiles.xib index 059c96e63d..a37c540201 100644 --- a/i18n/src/main/resources/pt_BR.lproj/Profiles.xib +++ b/i18n/src/main/resources/pt_BR.lproj/Profiles.xib @@ -1,7 +1,7 @@ - + - + diff --git a/i18n/src/main/resources/pt_BR.lproj/Prompt.xib b/i18n/src/main/resources/pt_BR.lproj/Prompt.xib index 240be9a48b..59c3e3c39e 100644 --- a/i18n/src/main/resources/pt_BR.lproj/Prompt.xib +++ b/i18n/src/main/resources/pt_BR.lproj/Prompt.xib @@ -1,7 +1,7 @@ - + - + @@ -95,7 +95,7 @@ Gw - + diff --git a/i18n/src/main/resources/pt_BR.lproj/S3.strings b/i18n/src/main/resources/pt_BR.lproj/S3.strings index 7e922f9b16..782ce5602d 100644 Binary files a/i18n/src/main/resources/pt_BR.lproj/S3.strings and b/i18n/src/main/resources/pt_BR.lproj/S3.strings differ diff --git a/i18n/src/main/resources/pt_BR.lproj/Updater.strings b/i18n/src/main/resources/pt_BR.lproj/Updater.strings index e50bc42443..1c7862c89e 100644 Binary files a/i18n/src/main/resources/pt_BR.lproj/Updater.strings and b/i18n/src/main/resources/pt_BR.lproj/Updater.strings differ diff --git a/i18n/src/main/resources/pt_PT.lproj/Bookmark.xib b/i18n/src/main/resources/pt_PT.lproj/Bookmark.xib index 36d392b645..6b9eee7ea6 100644 --- a/i18n/src/main/resources/pt_PT.lproj/Bookmark.xib +++ b/i18n/src/main/resources/pt_PT.lproj/Bookmark.xib @@ -1,7 +1,7 @@ - + - + @@ -92,7 +92,7 @@ - + @@ -489,95 +489,95 @@ YnBsaXN0MDDUAQIDBAUGBwpYJHZlcnNpb25ZJGFyY2hpdmVyVCR0b3BYJG9iamVjdHMSAAGGoF8QD05T -S2V5ZWRBcmNoaXZlctEICVRyb290gAGuCwwZGh8UJCkqMTQ3PUBVJG51bGzWDQ4PEBESExQVFhcYVk5T -U2l6ZV5OU1Jlc2l6aW5nTW9kZVYkY2xhc3NcTlNJbWFnZUZsYWdzVk5TUmVwc1dOU0NvbG9ygAIQAIAN -EiDDAACAA4ALVnsxLCAxfdIbDxweWk5TLm9iamVjdHOhHYAEgArSGw8gI6IhIoAFgAaACdMPJSYnKBRf -EBROU1RJRkZSZXByZXNlbnRhdGlvbl8QGU5TSW50ZXJuYWxMYXlvdXREaXJlY3Rpb26ACIAHTxESbE1N -ACoAAAAKAAAAEAEAAAMAAAABAAEAAAEBAAMAAAABAAEAAAECAAMAAAACAAgACAEDAAMAAAABAAEAAAEG -AAMAAAABAAEAAAEKAAMAAAABAAEAAAERAAQAAAABAAAACAESAAMAAAABAAEAAAEVAAMAAAABAAIAAAEW -AAMAAAABAAEAAAEXAAQAAAABAAAAAgEcAAMAAAABAAEAAAEoAAMAAAABAAIAAAFSAAMAAAABAAEAAAFT -AAMAAAACAAEAAYdzAAcAABGcAAAA0AAAAAAAABGcYXBwbAIAAABtbnRyR1JBWVhZWiAH3AAIABcADwAu -AA9hY3NwQVBQTAAAAABub25lAAAAAAAAAAAAAAAAAAAAAAAA9tYAAQAAAADTLWFwcGwAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAVkZXNjAAAAwAAAAHlkc2NtAAABPAAA -CBpjcHJ0AAAJWAAAACN3dHB0AAAJfAAAABRrVFJDAAAJkAAACAxkZXNjAAAAAAAAAB9HZW5lcmljIEdy -YXkgR2FtbWEgMi4yIFByb2ZpbGUAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAbWx1YwAAAAAAAAAfAAAADHNr -U0sAAAAuAAABhGRhREsAAAA6AAABsmNhRVMAAAA4AAAB7HZpVk4AAABAAAACJHB0QlIAAABKAAACZHVr -VUEAAAAsAAACrmZyRlUAAAA+AAAC2mh1SFUAAAA0AAADGHpoVFcAAAAaAAADTGtvS1IAAAAiAAADZm5i -Tk8AAAA6AAADiGNzQ1oAAAAoAAADwmhlSUwAAAAkAAAD6nJvUk8AAAAqAAAEDmRlREUAAABOAAAEOGl0 -SVQAAABOAAAEhnN2U0UAAAA4AAAE1HpoQ04AAAAaAAAFDGphSlAAAAAmAAAFJmVsR1IAAAAqAAAFTHB0 -UE8AAABSAAAFdm5sTkwAAABAAAAFyGVzRVMAAABMAAAGCHRoVEgAAAAyAAAGVHRyVFIAAAAkAAAGhmZp -RkkAAABGAAAGqmhySFIAAAA+AAAG8HBsUEwAAABKAAAHLmFyRUcAAAAsAAAHeHJ1UlUAAAA6AAAHpGVu -VVMAAAA8AAAH3gBWAWEAZQBvAGIAZQBjAG4A4QAgAHMAaQB2AOEAIABnAGEAbQBhACAAMgAsADIARwBl -AG4AZQByAGkAcwBrACAAZwByAOUAIAAyACwAMgAgAGcAYQBtAG0AYQAtAHAAcgBvAGYAaQBsAEcAYQBt -AG0AYQAgAGQAZQAgAGcAcgBpAHMAbwBzACAAZwBlAG4A6AByAGkAYwBhACAAMgAuADIAQx6lAHUAIABo -AOwAbgBoACAATQDgAHUAIAB4AOEAbQAgAEMAaAB1AG4AZwAgAEcAYQBtAG0AYQAgADIALgAyAFAAZQBy -AGYAaQBsACAARwBlAG4A6QByAGkAYwBvACAAZABhACAARwBhAG0AYQAgAGQAZQAgAEMAaQBuAHoAYQBz -ACAAMgAsADIEFwQwBDMEMAQ7BEwEPQQwACAARwByAGEAeQAtBDMEMAQ8BDAAIAAyAC4AMgBQAHIAbwBm -AGkAbAAgAGcA6QBuAOkAcgBpAHEAdQBlACAAZwByAGkAcwAgAGcAYQBtAG0AYQAgADIALAAyAMEAbAB0 -AGEAbADhAG4AbwBzACAAcwB6APwAcgBrAGUAIABnAGEAbQBtAGEAIAAyAC4AMpAadShwcJaOUUlepgAy -AC4AMoJyX2ljz4/wx3y8GAAg1ozAyQAgrBC5yAAgADIALgAyACDVBLhc0wzHfABHAGUAbgBlAHIAaQBz -AGsAIABnAHIA5QAgAGcAYQBtAG0AYQAgADIALAAyAC0AcAByAG8AZgBpAGwATwBiAGUAYwBuAOEAIAFh -AGUAZADhACAAZwBhAG0AYQAgADIALgAyBdIF0AXeBdQAIAXQBeQF1QXoACAF2wXcBdwF2QAgADIALgAy -AEcAYQBtAGEAIABnAHIAaQAgAGcAZQBuAGUAcgBpAGMBAwAgADIALAAyAEEAbABsAGcAZQBtAGUAaQBu -AGUAcwAgAEcAcgBhAHUAcwB0AHUAZgBlAG4ALQBQAHIAbwBmAGkAbAAgAEcAYQBtAG0AYQAgADIALAAy -AFAAcgBvAGYAaQBsAG8AIABnAHIAaQBnAGkAbwAgAGcAZQBuAGUAcgBpAGMAbwAgAGQAZQBsAGwAYQAg -AGcAYQBtAG0AYQAgADIALAAyAEcAZQBuAGUAcgBpAHMAawAgAGcAcgDlACAAMgAsADIAIABnAGEAbQBt -AGEAcAByAG8AZgBpAGxmbpAacHBepnz7ZXAAMgAuADJjz4/wZYdO9k4AgiwwsDDsMKQwrDDzMN4AIAAy -AC4AMgAgMNcw7TDVMKEwpDDrA5MDtQO9A7kDugPMACADkwO6A8EDuQAgA5MDrAO8A7wDsQAgADIALgAy -AFAAZQByAGYAaQBsACAAZwBlAG4A6QByAGkAYwBvACAAZABlACAAYwBpAG4AegBlAG4AdABvAHMAIABk -AGEAIABHAGEAbQBtAGEAIAAyACwAMgBBAGwAZwBlAG0AZQBlAG4AIABnAHIAaQBqAHMAIABnAGEAbQBt -AGEAIAAyACwAMgAtAHAAcgBvAGYAaQBlAGwAUABlAHIAZgBpAGwAIABnAGUAbgDpAHIAaQBjAG8AIABk -AGUAIABnAGEAbQBtAGEAIABkAGUAIABnAHIAaQBzAGUAcwAgADIALAAyDiMOMQ4HDioONQ5BDgEOIQ4h -DjIOQA4BDiMOIg5MDhcOMQ5IDicORA4bACAAMgAuADIARwBlAG4AZQBsACAARwByAGkAIABHAGEAbQBh -ACAAMgAsADIAWQBsAGUAaQBuAGUAbgAgAGgAYQByAG0AYQBhAG4AIABnAGEAbQBtAGEAIAAyACwAMgAg -AC0AcAByAG8AZgBpAGkAbABpAEcAZQBuAGUAcgBpAQ0AawBpACAARwByAGEAeQAgAEcAYQBtAG0AYQAg -ADIALgAyACAAcAByAG8AZgBpAGwAVQBuAGkAdwBlAHIAcwBhAGwAbgB5ACAAcAByAG8AZgBpAGwAIABz -AHoAYQByAG8BWwBjAGkAIABnAGEAbQBtAGEAIAAyACwAMgY6BicGRQYnACAAMgAuADIAIAZEBkgGRgAg -BjEGRQYnBi8GSgAgBjkGJwZFBB4EMQRJBDAETwAgBEEENQRABDAETwAgBDMEMAQ8BDwEMAAgADIALAAy -AC0EPwRABD4ERAQ4BDsETABHAGUAbgBlAHIAaQBjACAARwByAGEAeQAgAEcAYQBtAG0AYQAgADIALgAy -ACAAUAByAG8AZgBpAGwAZQAAdGV4dAAAAABDb3B5cmlnaHQgQXBwbGUgSW5jLiwgMjAxMgAAWFlaIAAA -AAAAAPNRAAEAAAABFsxjdXJ2AAAAAAAABAAAAAAFAAoADwAUABkAHgAjACgALQAyADcAOwBAAEUASgBP -AFQAWQBeAGMAaABtAHIAdwB8AIEAhgCLAJAAlQCaAJ8ApACpAK4AsgC3ALwAwQDGAMsA0ADVANsA4ADl -AOsA8AD2APsBAQEHAQ0BEwEZAR8BJQErATIBOAE+AUUBTAFSAVkBYAFnAW4BdQF8AYMBiwGSAZoBoQGp -AbEBuQHBAckB0QHZAeEB6QHyAfoCAwIMAhQCHQImAi8COAJBAksCVAJdAmcCcQJ6AoQCjgKYAqICrAK2 -AsECywLVAuAC6wL1AwADCwMWAyEDLQM4A0MDTwNaA2YDcgN+A4oDlgOiA64DugPHA9MD4APsA/kEBgQT -BCAELQQ7BEgEVQRjBHEEfgSMBJoEqAS2BMQE0wThBPAE/gUNBRwFKwU6BUkFWAVnBXcFhgWWBaYFtQXF -BdUF5QX2BgYGFgYnBjcGSAZZBmoGewaMBp0GrwbABtEG4wb1BwcHGQcrBz0HTwdhB3QHhgeZB6wHvwfS -B+UH+AgLCB8IMghGCFoIbgiCCJYIqgi+CNII5wj7CRAJJQk6CU8JZAl5CY8JpAm6Cc8J5Qn7ChEKJwo9 -ClQKagqBCpgKrgrFCtwK8wsLCyILOQtRC2kLgAuYC7ALyAvhC/kMEgwqDEMMXAx1DI4MpwzADNkM8w0N -DSYNQA1aDXQNjg2pDcMN3g34DhMOLg5JDmQOfw6bDrYO0g7uDwkPJQ9BD14Peg+WD7MPzw/sEAkQJhBD -EGEQfhCbELkQ1xD1ERMRMRFPEW0RjBGqEckR6BIHEiYSRRJkEoQSoxLDEuMTAxMjE0MTYxODE6QTxRPl -FAYUJxRJFGoUixStFM4U8BUSFTQVVhV4FZsVvRXgFgMWJhZJFmwWjxayFtYW+hcdF0EXZReJF64X0hf3 -GBsYQBhlGIoYrxjVGPoZIBlFGWsZkRm3Gd0aBBoqGlEadxqeGsUa7BsUGzsbYxuKG7Ib2hwCHCocUhx7 -HKMczBz1HR4dRx1wHZkdwx3sHhYeQB5qHpQevh7pHxMfPh9pH5Qfvx/qIBUgQSBsIJggxCDwIRwhSCF1 -IaEhziH7IiciVSKCIq8i3SMKIzgjZiOUI8Ij8CQfJE0kfCSrJNolCSU4JWgllyXHJfcmJyZXJocmtybo -JxgnSSd6J6sn3CgNKD8ocSiiKNQpBik4KWspnSnQKgIqNSpoKpsqzysCKzYraSudK9EsBSw5LG4soizX -LQwtQS12Last4S4WLkwugi63Lu4vJC9aL5Evxy/+MDUwbDCkMNsxEjFKMYIxujHyMioyYzKbMtQzDTNG -M38zuDPxNCs0ZTSeNNg1EzVNNYc1wjX9Njc2cjauNuk3JDdgN5w31zgUOFA4jDjIOQU5Qjl/Obw5+To2 -OnQ6sjrvOy07azuqO+g8JzxlPKQ84z0iPWE9oT3gPiA+YD6gPuA/IT9hP6I/4kAjQGRApkDnQSlBakGs -Qe5CMEJyQrVC90M6Q31DwEQDREdEikTORRJFVUWaRd5GIkZnRqtG8Ec1R3tHwEgFSEtIkUjXSR1JY0mp -SfBKN0p9SsRLDEtTS5pL4kwqTHJMuk0CTUpNk03cTiVObk63TwBPSU+TT91QJ1BxULtRBlFQUZtR5lIx -UnxSx1MTU19TqlP2VEJUj1TbVShVdVXCVg9WXFapVvdXRFeSV+BYL1h9WMtZGllpWbhaB1pWWqZa9VtF -W5Vb5Vw1XIZc1l0nXXhdyV4aXmxevV8PX2Ffs2AFYFdgqmD8YU9homH1YklinGLwY0Njl2PrZEBklGTp -ZT1lkmXnZj1mkmboZz1nk2fpaD9olmjsaUNpmmnxakhqn2r3a09rp2v/bFdsr20IbWBtuW4SbmtuxG8e -b3hv0XArcIZw4HE6cZVx8HJLcqZzAXNdc7h0FHRwdMx1KHWFdeF2Pnabdvh3VnezeBF4bnjMeSp5iXnn -ekZ6pXsEe2N7wnwhfIF84X1BfaF+AX5ifsJ/I3+Ef+WAR4CogQqBa4HNgjCCkoL0g1eDuoQdhICE44VH -hauGDoZyhteHO4efiASIaYjOiTOJmYn+imSKyoswi5aL/IxjjMqNMY2Yjf+OZo7OjzaPnpAGkG6Q1pE/ -kaiSEZJ6kuOTTZO2lCCUipT0lV+VyZY0lp+XCpd1l+CYTJi4mSSZkJn8mmia1ZtCm6+cHJyJnPedZJ3S -nkCerp8dn4uf+qBpoNihR6G2oiailqMGo3aj5qRWpMelOKWpphqmi6b9p26n4KhSqMSpN6mpqhyqj6sC -q3Wr6axcrNCtRK24ri2uoa8Wr4uwALB1sOqxYLHWskuywrM4s660JbSctRO1irYBtnm28Ldot+C4WbjR -uUq5wro7urW7LrunvCG8m70VvY++Cr6Evv+/er/1wHDA7MFnwePCX8Lbw1jD1MRRxM7FS8XIxkbGw8dB -x7/IPci8yTrJuco4yrfLNsu2zDXMtc01zbXONs62zzfPuNA50LrRPNG+0j/SwdNE08bUSdTL1U7V0dZV -1tjXXNfg2GTY6Nls2fHadtr724DcBdyK3RDdlt4c3qLfKd+v4DbgveFE4cziU+Lb42Pj6+Rz5PzlhOYN -5pbnH+ep6DLovOlG6dDqW+rl63Dr++yG7RHtnO4o7rTvQO/M8Fjw5fFy8f/yjPMZ86f0NPTC9VD13vZt -9vv3ivgZ+Kj5OPnH+lf65/t3/Af8mP0p/br+S/7c/23//9IrLC0uWiRjbGFzc25hbWVYJGNsYXNzZXNf -EBBOU0JpdG1hcEltYWdlUmVwoy0vMFpOU0ltYWdlUmVwWE5TT2JqZWN00issMjNXTlNBcnJheaIyMNIr -LDU2Xk5TTXV0YWJsZUFycmF5ozUyMNM4OQ86OzxXTlNXaGl0ZVxOU0NvbG9yU3BhY2VEMCAwABADgAzS -Kyw+P1dOU0NvbG9yoj4w0issQUJXTlNJbWFnZaJBMAAIABEAGgAkACkAMgA3AEkATABRAFMAYgBoAHUA -fACLAJIAnwCmAK4AsACyALQAuQC7AL0AxADJANQA1gDYANoA3wDiAOQA5gDoAO8BBgEiASQBJhOWE5sT -phOvE8ITxhPRE9oT3xPnE+oT7xP+FAIUCRQRFB4UIxQlFCcULBQ0FDcUPBREAAAAAAAAAgEAAAAAAAAA -QwAAAAAAAAAAAAAAAAAAFEc +S2V5ZWRBcmNoaXZlctEICVRyb290gAGuCwwbHCEaJissMzY5P0JVJG51bGzXDQ4PEBESExQVFhcYGRpW +TlNTaXplViRjbGFzc1xOU0ltYWdlRmxhZ3NWTlNSZXBzW05TVGludENvbG9yV05TQ29sb3JeTlNSZXNp +emluZ01vZGWAAoANEiDDAACAA4AAgAsQAFZ7MSwgMX3SHQ4eIFpOUy5vYmplY3RzoR+ABIAK0h0OIiWi +IySABYAGgAnTDicoKSoaXxAUTlNUSUZGUmVwcmVzZW50YXRpb25fEBlOU0ludGVybmFsTGF5b3V0RGly +ZWN0aW9ugAiAB08REmxNTQAqAAAACgAAABABAAADAAAAAQABAAABAQADAAAAAQABAAABAgADAAAAAgAI +AAgBAwADAAAAAQABAAABBgADAAAAAQABAAABCgADAAAAAQABAAABEQAEAAAAAQAAAAgBEgADAAAAAQAB +AAABFQADAAAAAQACAAABFgADAAAAAQABAAABFwAEAAAAAQAAAAIBHAADAAAAAQABAAABKAADAAAAAQAC +AAABUgADAAAAAQABAAABUwADAAAAAgABAAGHcwAHAAARnAAAANAAAAAAAAARnGFwcGwCAAAAbW50ckdS +QVlYWVogB9wACAAXAA8ALgAPYWNzcEFQUEwAAAAAbm9uZQAAAAAAAAAAAAAAAAAAAAAAAPbWAAEAAAAA +0y1hcHBsAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFZGVzYwAA +AMAAAAB5ZHNjbQAAATwAAAgaY3BydAAACVgAAAAjd3RwdAAACXwAAAAUa1RSQwAACZAAAAgMZGVzYwAA +AAAAAAAfR2VuZXJpYyBHcmF5IEdhbW1hIDIuMiBQcm9maWxlAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAG1s +dWMAAAAAAAAAHwAAAAxza1NLAAAALgAAAYRkYURLAAAAOgAAAbJjYUVTAAAAOAAAAex2aVZOAAAAQAAA +AiRwdEJSAAAASgAAAmR1a1VBAAAALAAAAq5mckZVAAAAPgAAAtpodUhVAAAANAAAAxh6aFRXAAAAGgAA +A0xrb0tSAAAAIgAAA2ZuYk5PAAAAOgAAA4hjc0NaAAAAKAAAA8JoZUlMAAAAJAAAA+pyb1JPAAAAKgAA +BA5kZURFAAAATgAABDhpdElUAAAATgAABIZzdlNFAAAAOAAABNR6aENOAAAAGgAABQxqYUpQAAAAJgAA +BSZlbEdSAAAAKgAABUxwdFBPAAAAUgAABXZubE5MAAAAQAAABchlc0VTAAAATAAABgh0aFRIAAAAMgAA +BlR0clRSAAAAJAAABoZmaUZJAAAARgAABqpockhSAAAAPgAABvBwbFBMAAAASgAABy5hckVHAAAALAAA +B3hydVJVAAAAOgAAB6RlblVTAAAAPAAAB94AVgFhAGUAbwBiAGUAYwBuAOEAIABzAGkAdgDhACAAZwBh +AG0AYQAgADIALAAyAEcAZQBuAGUAcgBpAHMAawAgAGcAcgDlACAAMgAsADIAIABnAGEAbQBtAGEALQBw +AHIAbwBmAGkAbABHAGEAbQBtAGEAIABkAGUAIABnAHIAaQBzAG8AcwAgAGcAZQBuAOgAcgBpAGMAYQAg +ADIALgAyAEMepQB1ACAAaADsAG4AaAAgAE0A4AB1ACAAeADhAG0AIABDAGgAdQBuAGcAIABHAGEAbQBt +AGEAIAAyAC4AMgBQAGUAcgBmAGkAbAAgAEcAZQBuAOkAcgBpAGMAbwAgAGQAYQAgAEcAYQBtAGEAIABk +AGUAIABDAGkAbgB6AGEAcwAgADIALAAyBBcEMAQzBDAEOwRMBD0EMAAgAEcAcgBhAHkALQQzBDAEPAQw +ACAAMgAuADIAUAByAG8AZgBpAGwAIABnAOkAbgDpAHIAaQBxAHUAZQAgAGcAcgBpAHMAIABnAGEAbQBt +AGEAIAAyACwAMgDBAGwAdABhAGwA4QBuAG8AcwAgAHMAegD8AHIAawBlACAAZwBhAG0AbQBhACAAMgAu +ADKQGnUocHCWjlFJXqYAMgAuADKCcl9pY8+P8Md8vBgAINaMwMkAIKwQucgAIAAyAC4AMgAg1QS4XNMM +x3wARwBlAG4AZQByAGkAcwBrACAAZwByAOUAIABnAGEAbQBtAGEAIAAyACwAMgAtAHAAcgBvAGYAaQBs +AE8AYgBlAGMAbgDhACABYQBlAGQA4QAgAGcAYQBtAGEAIAAyAC4AMgXSBdAF3gXUACAF0AXkBdUF6AAg +BdsF3AXcBdkAIAAyAC4AMgBHAGEAbQBhACAAZwByAGkAIABnAGUAbgBlAHIAaQBjAQMAIAAyACwAMgBB +AGwAbABnAGUAbQBlAGkAbgBlAHMAIABHAHIAYQB1AHMAdAB1AGYAZQBuAC0AUAByAG8AZgBpAGwAIABH +AGEAbQBtAGEAIAAyACwAMgBQAHIAbwBmAGkAbABvACAAZwByAGkAZwBpAG8AIABnAGUAbgBlAHIAaQBj +AG8AIABkAGUAbABsAGEAIABnAGEAbQBtAGEAIAAyACwAMgBHAGUAbgBlAHIAaQBzAGsAIABnAHIA5QAg +ADIALAAyACAAZwBhAG0AbQBhAHAAcgBvAGYAaQBsZm6QGnBwXqZ8+2VwADIALgAyY8+P8GWHTvZOAIIs +MLAw7DCkMKww8zDeACAAMgAuADIAIDDXMO0w1TChMKQw6wOTA7UDvQO5A7oDzAAgA5MDugPBA7kAIAOT +A6wDvAO8A7EAIAAyAC4AMgBQAGUAcgBmAGkAbAAgAGcAZQBuAOkAcgBpAGMAbwAgAGQAZQAgAGMAaQBu +AHoAZQBuAHQAbwBzACAAZABhACAARwBhAG0AbQBhACAAMgAsADIAQQBsAGcAZQBtAGUAZQBuACAAZwBy +AGkAagBzACAAZwBhAG0AbQBhACAAMgAsADIALQBwAHIAbwBmAGkAZQBsAFAAZQByAGYAaQBsACAAZwBl +AG4A6QByAGkAYwBvACAAZABlACAAZwBhAG0AbQBhACAAZABlACAAZwByAGkAcwBlAHMAIAAyACwAMg4j +DjEOBw4qDjUOQQ4BDiEOIQ4yDkAOAQ4jDiIOTA4XDjEOSA4nDkQOGwAgADIALgAyAEcAZQBuAGUAbAAg +AEcAcgBpACAARwBhAG0AYQAgADIALAAyAFkAbABlAGkAbgBlAG4AIABoAGEAcgBtAGEAYQBuACAAZwBh +AG0AbQBhACAAMgAsADIAIAAtAHAAcgBvAGYAaQBpAGwAaQBHAGUAbgBlAHIAaQENAGsAaQAgAEcAcgBh +AHkAIABHAGEAbQBtAGEAIAAyAC4AMgAgAHAAcgBvAGYAaQBsAFUAbgBpAHcAZQByAHMAYQBsAG4AeQAg +AHAAcgBvAGYAaQBsACAAcwB6AGEAcgBvAVsAYwBpACAAZwBhAG0AbQBhACAAMgAsADIGOgYnBkUGJwAg +ADIALgAyACAGRAZIBkYAIAYxBkUGJwYvBkoAIAY5BicGRQQeBDEESQQwBE8AIARBBDUEQAQwBE8AIAQz +BDAEPAQ8BDAAIAAyACwAMgAtBD8EQAQ+BEQEOAQ7BEwARwBlAG4AZQByAGkAYwAgAEcAcgBhAHkAIABH +AGEAbQBtAGEAIAAyAC4AMgAgAFAAcgBvAGYAaQBsAGUAAHRleHQAAAAAQ29weXJpZ2h0IEFwcGxlIElu +Yy4sIDIwMTIAAFhZWiAAAAAAAADzUQABAAAAARbMY3VydgAAAAAAAAQAAAAABQAKAA8AFAAZAB4AIwAo +AC0AMgA3ADsAQABFAEoATwBUAFkAXgBjAGgAbQByAHcAfACBAIYAiwCQAJUAmgCfAKQAqQCuALIAtwC8 +AMEAxgDLANAA1QDbAOAA5QDrAPAA9gD7AQEBBwENARMBGQEfASUBKwEyATgBPgFFAUwBUgFZAWABZwFu +AXUBfAGDAYsBkgGaAaEBqQGxAbkBwQHJAdEB2QHhAekB8gH6AgMCDAIUAh0CJgIvAjgCQQJLAlQCXQJn +AnECegKEAo4CmAKiAqwCtgLBAssC1QLgAusC9QMAAwsDFgMhAy0DOANDA08DWgNmA3IDfgOKA5YDogOu +A7oDxwPTA+AD7AP5BAYEEwQgBC0EOwRIBFUEYwRxBH4EjASaBKgEtgTEBNME4QTwBP4FDQUcBSsFOgVJ +BVgFZwV3BYYFlgWmBbUFxQXVBeUF9gYGBhYGJwY3BkgGWQZqBnsGjAadBq8GwAbRBuMG9QcHBxkHKwc9 +B08HYQd0B4YHmQesB78H0gflB/gICwgfCDIIRghaCG4IggiWCKoIvgjSCOcI+wkQCSUJOglPCWQJeQmP +CaQJugnPCeUJ+woRCicKPQpUCmoKgQqYCq4KxQrcCvMLCwsiCzkLUQtpC4ALmAuwC8gL4Qv5DBIMKgxD +DFwMdQyODKcMwAzZDPMNDQ0mDUANWg10DY4NqQ3DDd4N+A4TDi4OSQ5kDn8Omw62DtIO7g8JDyUPQQ9e +D3oPlg+zD88P7BAJECYQQxBhEH4QmxC5ENcQ9RETETERTxFtEYwRqhHJEegSBxImEkUSZBKEEqMSwxLj +EwMTIxNDE2MTgxOkE8UT5RQGFCcUSRRqFIsUrRTOFPAVEhU0FVYVeBWbFb0V4BYDFiYWSRZsFo8WshbW +FvoXHRdBF2UXiReuF9IX9xgbGEAYZRiKGK8Y1Rj6GSAZRRlrGZEZtxndGgQaKhpRGncanhrFGuwbFBs7 +G2MbihuyG9ocAhwqHFIcexyjHMwc9R0eHUcdcB2ZHcMd7B4WHkAeah6UHr4e6R8THz4faR+UH78f6iAV +IEEgbCCYIMQg8CEcIUghdSGhIc4h+yInIlUigiKvIt0jCiM4I2YjlCPCI/AkHyRNJHwkqyTaJQklOCVo +JZclxyX3JicmVyaHJrcm6CcYJ0kneierJ9woDSg/KHEooijUKQYpOClrKZ0p0CoCKjUqaCqbKs8rAis2 +K2krnSvRLAUsOSxuLKIs1y0MLUEtdi2rLeEuFi5MLoIuty7uLyQvWi+RL8cv/jA1MGwwpDDbMRIxSjGC +Mbox8jIqMmMymzLUMw0zRjN/M7gz8TQrNGU0njTYNRM1TTWHNcI1/TY3NnI2rjbpNyQ3YDecN9c4FDhQ +OIw4yDkFOUI5fzm8Ofk6Njp0OrI67zstO2s7qjvoPCc8ZTykPOM9Ij1hPaE94D4gPmA+oD7gPyE/YT+i +P+JAI0BkQKZA50EpQWpBrEHuQjBCckK1QvdDOkN9Q8BEA0RHRIpEzkUSRVVFmkXeRiJGZ0arRvBHNUd7 +R8BIBUhLSJFI10kdSWNJqUnwSjdKfUrESwxLU0uaS+JMKkxyTLpNAk1KTZNN3E4lTm5Ot08AT0lPk0/d +UCdQcVC7UQZRUFGbUeZSMVJ8UsdTE1NfU6pT9lRCVI9U21UoVXVVwlYPVlxWqVb3V0RXklfgWC9YfVjL +WRpZaVm4WgdaVlqmWvVbRVuVW+VcNVyGXNZdJ114XcleGl5sXr1fD19hX7NgBWBXYKpg/GFPYaJh9WJJ +Ypxi8GNDY5dj62RAZJRk6WU9ZZJl52Y9ZpJm6Gc9Z5Nn6Wg/aJZo7GlDaZpp8WpIap9q92tPa6dr/2xX +bK9tCG1gbbluEm5rbsRvHm94b9FwK3CGcOBxOnGVcfByS3KmcwFzXXO4dBR0cHTMdSh1hXXhdj52m3b4 +d1Z3s3gReG54zHkqeYl553pGeqV7BHtje8J8IXyBfOF9QX2hfgF+Yn7CfyN/hH/lgEeAqIEKgWuBzYIw +gpKC9INXg7qEHYSAhOOFR4Wrhg6GcobXhzuHn4gEiGmIzokziZmJ/opkisqLMIuWi/yMY4zKjTGNmI3/ +jmaOzo82j56QBpBukNaRP5GokhGSepLjk02TtpQglIqU9JVflcmWNJaflwqXdZfgmEyYuJkkmZCZ/Jpo +mtWbQpuvnByciZz3nWSd0p5Anq6fHZ+Ln/qgaaDYoUehtqImopajBqN2o+akVqTHpTilqaYapoum/adu +p+CoUqjEqTepqaocqo+rAqt1q+msXKzQrUStuK4trqGvFq+LsACwdbDqsWCx1rJLssKzOLOutCW0nLUT +tYq2AbZ5tvC3aLfguFm40blKucK6O7q1uy67p7whvJu9Fb2Pvgq+hL7/v3q/9cBwwOzBZ8Hjwl/C28NY +w9TEUcTOxUvFyMZGxsPHQce/yD3IvMk6ybnKOMq3yzbLtsw1zLXNNc21zjbOts83z7jQOdC60TzRvtI/ +0sHTRNPG1EnUy9VO1dHWVdbY11zX4Nhk2OjZbNnx2nba+9uA3AXcit0Q3ZbeHN6i3ynfr+A24L3hROHM +4lPi2+Nj4+vkc+T85YTmDeaW5x/nqegy6LzpRunQ6lvq5etw6/vshu0R7ZzuKO6070DvzPBY8OXxcvH/ +8ozzGfOn9DT0wvVQ9d72bfb794r4Gfio+Tj5x/pX+uf7d/wH/Jj9Kf26/kv+3P9t///SLS4vMFokY2xh +c3NuYW1lWCRjbGFzc2VzXxAQTlNCaXRtYXBJbWFnZVJlcKMvMTJaTlNJbWFnZVJlcFhOU09iamVjdNIt +LjQ1V05TQXJyYXmiNDLSLS43OF5OU011dGFibGVBcnJheaM3NDLTOjsOPD0+V05TV2hpdGVcTlNDb2xv +clNwYWNlRDAgMAAQA4AM0i0uQEFXTlNDb2xvcqJAMtItLkNEV05TSW1hZ2WiQzIACAARABoAJAApADIA +NwBJAEwAUQBTAGIAaAB3AH4AhQCSAJkApQCtALwAvgDAAMUAxwDJAMsAzQDUANkA5ADmAOgA6gDvAPIA +9AD2APgA/wEWATIBNAE2E6YTqxO2E78T0hPWE+ET6hPvE/cT+hP/FA4UEhQZFCEULhQzFDUUNxQ8FEQU +RxRMFFQAAAAAAAACAQAAAAAAAABFAAAAAAAAAAAAAAAAAAAUVw diff --git a/i18n/src/main/resources/pt_PT.lproj/Browser.xib b/i18n/src/main/resources/pt_PT.lproj/Browser.xib index 3e3a63d725..719e672f34 100644 --- a/i18n/src/main/resources/pt_PT.lproj/Browser.xib +++ b/i18n/src/main/resources/pt_PT.lproj/Browser.xib @@ -1,7 +1,7 @@ - + - + @@ -38,7 +38,7 @@ - + @@ -62,27 +62,27 @@ @@ -256,7 +256,7 @@ - + @@ -553,95 +553,95 @@ DQ YnBsaXN0MDDUAQIDBAUGBwpYJHZlcnNpb25ZJGFyY2hpdmVyVCR0b3BYJG9iamVjdHMSAAGGoF8QD05T -S2V5ZWRBcmNoaXZlctEICVRyb290gAGuCwwZGh8UJCkqMTQ3PUBVJG51bGzWDQ4PEBESExQVFhcYVk5T -U2l6ZV5OU1Jlc2l6aW5nTW9kZVYkY2xhc3NcTlNJbWFnZUZsYWdzVk5TUmVwc1dOU0NvbG9ygAIQAIAN -EiDDAACAA4ALVnsxLCAxfdIbDxweWk5TLm9iamVjdHOhHYAEgArSGw8gI6IhIoAFgAaACdMPJSYnKBRf -EBROU1RJRkZSZXByZXNlbnRhdGlvbl8QGU5TSW50ZXJuYWxMYXlvdXREaXJlY3Rpb26ACIAHTxESbE1N -ACoAAAAKAAAAEAEAAAMAAAABAAEAAAEBAAMAAAABAAEAAAECAAMAAAACAAgACAEDAAMAAAABAAEAAAEG -AAMAAAABAAEAAAEKAAMAAAABAAEAAAERAAQAAAABAAAACAESAAMAAAABAAEAAAEVAAMAAAABAAIAAAEW -AAMAAAABAAEAAAEXAAQAAAABAAAAAgEcAAMAAAABAAEAAAEoAAMAAAABAAIAAAFSAAMAAAABAAEAAAFT -AAMAAAACAAEAAYdzAAcAABGcAAAA0AAAAAAAABGcYXBwbAIAAABtbnRyR1JBWVhZWiAH3AAIABcADwAu -AA9hY3NwQVBQTAAAAABub25lAAAAAAAAAAAAAAAAAAAAAAAA9tYAAQAAAADTLWFwcGwAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAVkZXNjAAAAwAAAAHlkc2NtAAABPAAA -CBpjcHJ0AAAJWAAAACN3dHB0AAAJfAAAABRrVFJDAAAJkAAACAxkZXNjAAAAAAAAAB9HZW5lcmljIEdy -YXkgR2FtbWEgMi4yIFByb2ZpbGUAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAbWx1YwAAAAAAAAAfAAAADHNr -U0sAAAAuAAABhGRhREsAAAA6AAABsmNhRVMAAAA4AAAB7HZpVk4AAABAAAACJHB0QlIAAABKAAACZHVr -VUEAAAAsAAACrmZyRlUAAAA+AAAC2mh1SFUAAAA0AAADGHpoVFcAAAAaAAADTGtvS1IAAAAiAAADZm5i -Tk8AAAA6AAADiGNzQ1oAAAAoAAADwmhlSUwAAAAkAAAD6nJvUk8AAAAqAAAEDmRlREUAAABOAAAEOGl0 -SVQAAABOAAAEhnN2U0UAAAA4AAAE1HpoQ04AAAAaAAAFDGphSlAAAAAmAAAFJmVsR1IAAAAqAAAFTHB0 -UE8AAABSAAAFdm5sTkwAAABAAAAFyGVzRVMAAABMAAAGCHRoVEgAAAAyAAAGVHRyVFIAAAAkAAAGhmZp -RkkAAABGAAAGqmhySFIAAAA+AAAG8HBsUEwAAABKAAAHLmFyRUcAAAAsAAAHeHJ1UlUAAAA6AAAHpGVu -VVMAAAA8AAAH3gBWAWEAZQBvAGIAZQBjAG4A4QAgAHMAaQB2AOEAIABnAGEAbQBhACAAMgAsADIARwBl -AG4AZQByAGkAcwBrACAAZwByAOUAIAAyACwAMgAgAGcAYQBtAG0AYQAtAHAAcgBvAGYAaQBsAEcAYQBt -AG0AYQAgAGQAZQAgAGcAcgBpAHMAbwBzACAAZwBlAG4A6AByAGkAYwBhACAAMgAuADIAQx6lAHUAIABo -AOwAbgBoACAATQDgAHUAIAB4AOEAbQAgAEMAaAB1AG4AZwAgAEcAYQBtAG0AYQAgADIALgAyAFAAZQBy -AGYAaQBsACAARwBlAG4A6QByAGkAYwBvACAAZABhACAARwBhAG0AYQAgAGQAZQAgAEMAaQBuAHoAYQBz -ACAAMgAsADIEFwQwBDMEMAQ7BEwEPQQwACAARwByAGEAeQAtBDMEMAQ8BDAAIAAyAC4AMgBQAHIAbwBm -AGkAbAAgAGcA6QBuAOkAcgBpAHEAdQBlACAAZwByAGkAcwAgAGcAYQBtAG0AYQAgADIALAAyAMEAbAB0 -AGEAbADhAG4AbwBzACAAcwB6APwAcgBrAGUAIABnAGEAbQBtAGEAIAAyAC4AMpAadShwcJaOUUlepgAy -AC4AMoJyX2ljz4/wx3y8GAAg1ozAyQAgrBC5yAAgADIALgAyACDVBLhc0wzHfABHAGUAbgBlAHIAaQBz -AGsAIABnAHIA5QAgAGcAYQBtAG0AYQAgADIALAAyAC0AcAByAG8AZgBpAGwATwBiAGUAYwBuAOEAIAFh -AGUAZADhACAAZwBhAG0AYQAgADIALgAyBdIF0AXeBdQAIAXQBeQF1QXoACAF2wXcBdwF2QAgADIALgAy -AEcAYQBtAGEAIABnAHIAaQAgAGcAZQBuAGUAcgBpAGMBAwAgADIALAAyAEEAbABsAGcAZQBtAGUAaQBu -AGUAcwAgAEcAcgBhAHUAcwB0AHUAZgBlAG4ALQBQAHIAbwBmAGkAbAAgAEcAYQBtAG0AYQAgADIALAAy -AFAAcgBvAGYAaQBsAG8AIABnAHIAaQBnAGkAbwAgAGcAZQBuAGUAcgBpAGMAbwAgAGQAZQBsAGwAYQAg -AGcAYQBtAG0AYQAgADIALAAyAEcAZQBuAGUAcgBpAHMAawAgAGcAcgDlACAAMgAsADIAIABnAGEAbQBt -AGEAcAByAG8AZgBpAGxmbpAacHBepnz7ZXAAMgAuADJjz4/wZYdO9k4AgiwwsDDsMKQwrDDzMN4AIAAy -AC4AMgAgMNcw7TDVMKEwpDDrA5MDtQO9A7kDugPMACADkwO6A8EDuQAgA5MDrAO8A7wDsQAgADIALgAy -AFAAZQByAGYAaQBsACAAZwBlAG4A6QByAGkAYwBvACAAZABlACAAYwBpAG4AegBlAG4AdABvAHMAIABk -AGEAIABHAGEAbQBtAGEAIAAyACwAMgBBAGwAZwBlAG0AZQBlAG4AIABnAHIAaQBqAHMAIABnAGEAbQBt -AGEAIAAyACwAMgAtAHAAcgBvAGYAaQBlAGwAUABlAHIAZgBpAGwAIABnAGUAbgDpAHIAaQBjAG8AIABk -AGUAIABnAGEAbQBtAGEAIABkAGUAIABnAHIAaQBzAGUAcwAgADIALAAyDiMOMQ4HDioONQ5BDgEOIQ4h -DjIOQA4BDiMOIg5MDhcOMQ5IDicORA4bACAAMgAuADIARwBlAG4AZQBsACAARwByAGkAIABHAGEAbQBh -ACAAMgAsADIAWQBsAGUAaQBuAGUAbgAgAGgAYQByAG0AYQBhAG4AIABnAGEAbQBtAGEAIAAyACwAMgAg -AC0AcAByAG8AZgBpAGkAbABpAEcAZQBuAGUAcgBpAQ0AawBpACAARwByAGEAeQAgAEcAYQBtAG0AYQAg -ADIALgAyACAAcAByAG8AZgBpAGwAVQBuAGkAdwBlAHIAcwBhAGwAbgB5ACAAcAByAG8AZgBpAGwAIABz -AHoAYQByAG8BWwBjAGkAIABnAGEAbQBtAGEAIAAyACwAMgY6BicGRQYnACAAMgAuADIAIAZEBkgGRgAg -BjEGRQYnBi8GSgAgBjkGJwZFBB4EMQRJBDAETwAgBEEENQRABDAETwAgBDMEMAQ8BDwEMAAgADIALAAy -AC0EPwRABD4ERAQ4BDsETABHAGUAbgBlAHIAaQBjACAARwByAGEAeQAgAEcAYQBtAG0AYQAgADIALgAy -ACAAUAByAG8AZgBpAGwAZQAAdGV4dAAAAABDb3B5cmlnaHQgQXBwbGUgSW5jLiwgMjAxMgAAWFlaIAAA -AAAAAPNRAAEAAAABFsxjdXJ2AAAAAAAABAAAAAAFAAoADwAUABkAHgAjACgALQAyADcAOwBAAEUASgBP -AFQAWQBeAGMAaABtAHIAdwB8AIEAhgCLAJAAlQCaAJ8ApACpAK4AsgC3ALwAwQDGAMsA0ADVANsA4ADl -AOsA8AD2APsBAQEHAQ0BEwEZAR8BJQErATIBOAE+AUUBTAFSAVkBYAFnAW4BdQF8AYMBiwGSAZoBoQGp -AbEBuQHBAckB0QHZAeEB6QHyAfoCAwIMAhQCHQImAi8COAJBAksCVAJdAmcCcQJ6AoQCjgKYAqICrAK2 -AsECywLVAuAC6wL1AwADCwMWAyEDLQM4A0MDTwNaA2YDcgN+A4oDlgOiA64DugPHA9MD4APsA/kEBgQT -BCAELQQ7BEgEVQRjBHEEfgSMBJoEqAS2BMQE0wThBPAE/gUNBRwFKwU6BUkFWAVnBXcFhgWWBaYFtQXF -BdUF5QX2BgYGFgYnBjcGSAZZBmoGewaMBp0GrwbABtEG4wb1BwcHGQcrBz0HTwdhB3QHhgeZB6wHvwfS -B+UH+AgLCB8IMghGCFoIbgiCCJYIqgi+CNII5wj7CRAJJQk6CU8JZAl5CY8JpAm6Cc8J5Qn7ChEKJwo9 -ClQKagqBCpgKrgrFCtwK8wsLCyILOQtRC2kLgAuYC7ALyAvhC/kMEgwqDEMMXAx1DI4MpwzADNkM8w0N -DSYNQA1aDXQNjg2pDcMN3g34DhMOLg5JDmQOfw6bDrYO0g7uDwkPJQ9BD14Peg+WD7MPzw/sEAkQJhBD -EGEQfhCbELkQ1xD1ERMRMRFPEW0RjBGqEckR6BIHEiYSRRJkEoQSoxLDEuMTAxMjE0MTYxODE6QTxRPl -FAYUJxRJFGoUixStFM4U8BUSFTQVVhV4FZsVvRXgFgMWJhZJFmwWjxayFtYW+hcdF0EXZReJF64X0hf3 -GBsYQBhlGIoYrxjVGPoZIBlFGWsZkRm3Gd0aBBoqGlEadxqeGsUa7BsUGzsbYxuKG7Ib2hwCHCocUhx7 -HKMczBz1HR4dRx1wHZkdwx3sHhYeQB5qHpQevh7pHxMfPh9pH5Qfvx/qIBUgQSBsIJggxCDwIRwhSCF1 -IaEhziH7IiciVSKCIq8i3SMKIzgjZiOUI8Ij8CQfJE0kfCSrJNolCSU4JWgllyXHJfcmJyZXJocmtybo -JxgnSSd6J6sn3CgNKD8ocSiiKNQpBik4KWspnSnQKgIqNSpoKpsqzysCKzYraSudK9EsBSw5LG4soizX -LQwtQS12Last4S4WLkwugi63Lu4vJC9aL5Evxy/+MDUwbDCkMNsxEjFKMYIxujHyMioyYzKbMtQzDTNG -M38zuDPxNCs0ZTSeNNg1EzVNNYc1wjX9Njc2cjauNuk3JDdgN5w31zgUOFA4jDjIOQU5Qjl/Obw5+To2 -OnQ6sjrvOy07azuqO+g8JzxlPKQ84z0iPWE9oT3gPiA+YD6gPuA/IT9hP6I/4kAjQGRApkDnQSlBakGs -Qe5CMEJyQrVC90M6Q31DwEQDREdEikTORRJFVUWaRd5GIkZnRqtG8Ec1R3tHwEgFSEtIkUjXSR1JY0mp -SfBKN0p9SsRLDEtTS5pL4kwqTHJMuk0CTUpNk03cTiVObk63TwBPSU+TT91QJ1BxULtRBlFQUZtR5lIx -UnxSx1MTU19TqlP2VEJUj1TbVShVdVXCVg9WXFapVvdXRFeSV+BYL1h9WMtZGllpWbhaB1pWWqZa9VtF -W5Vb5Vw1XIZc1l0nXXhdyV4aXmxevV8PX2Ffs2AFYFdgqmD8YU9homH1YklinGLwY0Njl2PrZEBklGTp -ZT1lkmXnZj1mkmboZz1nk2fpaD9olmjsaUNpmmnxakhqn2r3a09rp2v/bFdsr20IbWBtuW4SbmtuxG8e -b3hv0XArcIZw4HE6cZVx8HJLcqZzAXNdc7h0FHRwdMx1KHWFdeF2Pnabdvh3VnezeBF4bnjMeSp5iXnn -ekZ6pXsEe2N7wnwhfIF84X1BfaF+AX5ifsJ/I3+Ef+WAR4CogQqBa4HNgjCCkoL0g1eDuoQdhICE44VH -hauGDoZyhteHO4efiASIaYjOiTOJmYn+imSKyoswi5aL/IxjjMqNMY2Yjf+OZo7OjzaPnpAGkG6Q1pE/ -kaiSEZJ6kuOTTZO2lCCUipT0lV+VyZY0lp+XCpd1l+CYTJi4mSSZkJn8mmia1ZtCm6+cHJyJnPedZJ3S -nkCerp8dn4uf+qBpoNihR6G2oiailqMGo3aj5qRWpMelOKWpphqmi6b9p26n4KhSqMSpN6mpqhyqj6sC -q3Wr6axcrNCtRK24ri2uoa8Wr4uwALB1sOqxYLHWskuywrM4s660JbSctRO1irYBtnm28Ldot+C4WbjR -uUq5wro7urW7LrunvCG8m70VvY++Cr6Evv+/er/1wHDA7MFnwePCX8Lbw1jD1MRRxM7FS8XIxkbGw8dB -x7/IPci8yTrJuco4yrfLNsu2zDXMtc01zbXONs62zzfPuNA50LrRPNG+0j/SwdNE08bUSdTL1U7V0dZV -1tjXXNfg2GTY6Nls2fHadtr724DcBdyK3RDdlt4c3qLfKd+v4DbgveFE4cziU+Lb42Pj6+Rz5PzlhOYN -5pbnH+ep6DLovOlG6dDqW+rl63Dr++yG7RHtnO4o7rTvQO/M8Fjw5fFy8f/yjPMZ86f0NPTC9VD13vZt -9vv3ivgZ+Kj5OPnH+lf65/t3/Af8mP0p/br+S/7c/23//9IrLC0uWiRjbGFzc25hbWVYJGNsYXNzZXNf -EBBOU0JpdG1hcEltYWdlUmVwoy0vMFpOU0ltYWdlUmVwWE5TT2JqZWN00issMjNXTlNBcnJheaIyMNIr -LDU2Xk5TTXV0YWJsZUFycmF5ozUyMNM4OQ86OzxXTlNXaGl0ZVxOU0NvbG9yU3BhY2VEMCAwABADgAzS -Kyw+P1dOU0NvbG9yoj4w0issQUJXTlNJbWFnZaJBMAAIABEAGgAkACkAMgA3AEkATABRAFMAYgBoAHUA -fACLAJIAnwCmAK4AsACyALQAuQC7AL0AxADJANQA1gDYANoA3wDiAOQA5gDoAO8BBgEiASQBJhOWE5sT -phOvE8ITxhPRE9oT3xPnE+oT7xP+FAIUCRQRFB4UIxQlFCcULBQ0FDcUPBREAAAAAAAAAgEAAAAAAAAA -QwAAAAAAAAAAAAAAAAAAFEc +S2V5ZWRBcmNoaXZlctEICVRyb290gAGuCwwbHCEaJissMzY5P0JVJG51bGzXDQ4PEBESExQVFhcYGRpW +TlNTaXplViRjbGFzc1xOU0ltYWdlRmxhZ3NWTlNSZXBzW05TVGludENvbG9yV05TQ29sb3JeTlNSZXNp +emluZ01vZGWAAoANEiDDAACAA4AAgAsQAFZ7MSwgMX3SHQ4eIFpOUy5vYmplY3RzoR+ABIAK0h0OIiWi +IySABYAGgAnTDicoKSoaXxAUTlNUSUZGUmVwcmVzZW50YXRpb25fEBlOU0ludGVybmFsTGF5b3V0RGly +ZWN0aW9ugAiAB08REmxNTQAqAAAACgAAABABAAADAAAAAQABAAABAQADAAAAAQABAAABAgADAAAAAgAI +AAgBAwADAAAAAQABAAABBgADAAAAAQABAAABCgADAAAAAQABAAABEQAEAAAAAQAAAAgBEgADAAAAAQAB +AAABFQADAAAAAQACAAABFgADAAAAAQABAAABFwAEAAAAAQAAAAIBHAADAAAAAQABAAABKAADAAAAAQAC +AAABUgADAAAAAQABAAABUwADAAAAAgABAAGHcwAHAAARnAAAANAAAAAAAAARnGFwcGwCAAAAbW50ckdS +QVlYWVogB9wACAAXAA8ALgAPYWNzcEFQUEwAAAAAbm9uZQAAAAAAAAAAAAAAAAAAAAAAAPbWAAEAAAAA +0y1hcHBsAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFZGVzYwAA +AMAAAAB5ZHNjbQAAATwAAAgaY3BydAAACVgAAAAjd3RwdAAACXwAAAAUa1RSQwAACZAAAAgMZGVzYwAA +AAAAAAAfR2VuZXJpYyBHcmF5IEdhbW1hIDIuMiBQcm9maWxlAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAG1s +dWMAAAAAAAAAHwAAAAxza1NLAAAALgAAAYRkYURLAAAAOgAAAbJjYUVTAAAAOAAAAex2aVZOAAAAQAAA +AiRwdEJSAAAASgAAAmR1a1VBAAAALAAAAq5mckZVAAAAPgAAAtpodUhVAAAANAAAAxh6aFRXAAAAGgAA +A0xrb0tSAAAAIgAAA2ZuYk5PAAAAOgAAA4hjc0NaAAAAKAAAA8JoZUlMAAAAJAAAA+pyb1JPAAAAKgAA +BA5kZURFAAAATgAABDhpdElUAAAATgAABIZzdlNFAAAAOAAABNR6aENOAAAAGgAABQxqYUpQAAAAJgAA +BSZlbEdSAAAAKgAABUxwdFBPAAAAUgAABXZubE5MAAAAQAAABchlc0VTAAAATAAABgh0aFRIAAAAMgAA +BlR0clRSAAAAJAAABoZmaUZJAAAARgAABqpockhSAAAAPgAABvBwbFBMAAAASgAABy5hckVHAAAALAAA +B3hydVJVAAAAOgAAB6RlblVTAAAAPAAAB94AVgFhAGUAbwBiAGUAYwBuAOEAIABzAGkAdgDhACAAZwBh +AG0AYQAgADIALAAyAEcAZQBuAGUAcgBpAHMAawAgAGcAcgDlACAAMgAsADIAIABnAGEAbQBtAGEALQBw +AHIAbwBmAGkAbABHAGEAbQBtAGEAIABkAGUAIABnAHIAaQBzAG8AcwAgAGcAZQBuAOgAcgBpAGMAYQAg +ADIALgAyAEMepQB1ACAAaADsAG4AaAAgAE0A4AB1ACAAeADhAG0AIABDAGgAdQBuAGcAIABHAGEAbQBt +AGEAIAAyAC4AMgBQAGUAcgBmAGkAbAAgAEcAZQBuAOkAcgBpAGMAbwAgAGQAYQAgAEcAYQBtAGEAIABk +AGUAIABDAGkAbgB6AGEAcwAgADIALAAyBBcEMAQzBDAEOwRMBD0EMAAgAEcAcgBhAHkALQQzBDAEPAQw +ACAAMgAuADIAUAByAG8AZgBpAGwAIABnAOkAbgDpAHIAaQBxAHUAZQAgAGcAcgBpAHMAIABnAGEAbQBt +AGEAIAAyACwAMgDBAGwAdABhAGwA4QBuAG8AcwAgAHMAegD8AHIAawBlACAAZwBhAG0AbQBhACAAMgAu +ADKQGnUocHCWjlFJXqYAMgAuADKCcl9pY8+P8Md8vBgAINaMwMkAIKwQucgAIAAyAC4AMgAg1QS4XNMM +x3wARwBlAG4AZQByAGkAcwBrACAAZwByAOUAIABnAGEAbQBtAGEAIAAyACwAMgAtAHAAcgBvAGYAaQBs +AE8AYgBlAGMAbgDhACABYQBlAGQA4QAgAGcAYQBtAGEAIAAyAC4AMgXSBdAF3gXUACAF0AXkBdUF6AAg +BdsF3AXcBdkAIAAyAC4AMgBHAGEAbQBhACAAZwByAGkAIABnAGUAbgBlAHIAaQBjAQMAIAAyACwAMgBB +AGwAbABnAGUAbQBlAGkAbgBlAHMAIABHAHIAYQB1AHMAdAB1AGYAZQBuAC0AUAByAG8AZgBpAGwAIABH +AGEAbQBtAGEAIAAyACwAMgBQAHIAbwBmAGkAbABvACAAZwByAGkAZwBpAG8AIABnAGUAbgBlAHIAaQBj +AG8AIABkAGUAbABsAGEAIABnAGEAbQBtAGEAIAAyACwAMgBHAGUAbgBlAHIAaQBzAGsAIABnAHIA5QAg +ADIALAAyACAAZwBhAG0AbQBhAHAAcgBvAGYAaQBsZm6QGnBwXqZ8+2VwADIALgAyY8+P8GWHTvZOAIIs +MLAw7DCkMKww8zDeACAAMgAuADIAIDDXMO0w1TChMKQw6wOTA7UDvQO5A7oDzAAgA5MDugPBA7kAIAOT +A6wDvAO8A7EAIAAyAC4AMgBQAGUAcgBmAGkAbAAgAGcAZQBuAOkAcgBpAGMAbwAgAGQAZQAgAGMAaQBu +AHoAZQBuAHQAbwBzACAAZABhACAARwBhAG0AbQBhACAAMgAsADIAQQBsAGcAZQBtAGUAZQBuACAAZwBy +AGkAagBzACAAZwBhAG0AbQBhACAAMgAsADIALQBwAHIAbwBmAGkAZQBsAFAAZQByAGYAaQBsACAAZwBl +AG4A6QByAGkAYwBvACAAZABlACAAZwBhAG0AbQBhACAAZABlACAAZwByAGkAcwBlAHMAIAAyACwAMg4j +DjEOBw4qDjUOQQ4BDiEOIQ4yDkAOAQ4jDiIOTA4XDjEOSA4nDkQOGwAgADIALgAyAEcAZQBuAGUAbAAg +AEcAcgBpACAARwBhAG0AYQAgADIALAAyAFkAbABlAGkAbgBlAG4AIABoAGEAcgBtAGEAYQBuACAAZwBh +AG0AbQBhACAAMgAsADIAIAAtAHAAcgBvAGYAaQBpAGwAaQBHAGUAbgBlAHIAaQENAGsAaQAgAEcAcgBh +AHkAIABHAGEAbQBtAGEAIAAyAC4AMgAgAHAAcgBvAGYAaQBsAFUAbgBpAHcAZQByAHMAYQBsAG4AeQAg +AHAAcgBvAGYAaQBsACAAcwB6AGEAcgBvAVsAYwBpACAAZwBhAG0AbQBhACAAMgAsADIGOgYnBkUGJwAg +ADIALgAyACAGRAZIBkYAIAYxBkUGJwYvBkoAIAY5BicGRQQeBDEESQQwBE8AIARBBDUEQAQwBE8AIAQz +BDAEPAQ8BDAAIAAyACwAMgAtBD8EQAQ+BEQEOAQ7BEwARwBlAG4AZQByAGkAYwAgAEcAcgBhAHkAIABH +AGEAbQBtAGEAIAAyAC4AMgAgAFAAcgBvAGYAaQBsAGUAAHRleHQAAAAAQ29weXJpZ2h0IEFwcGxlIElu +Yy4sIDIwMTIAAFhZWiAAAAAAAADzUQABAAAAARbMY3VydgAAAAAAAAQAAAAABQAKAA8AFAAZAB4AIwAo +AC0AMgA3ADsAQABFAEoATwBUAFkAXgBjAGgAbQByAHcAfACBAIYAiwCQAJUAmgCfAKQAqQCuALIAtwC8 +AMEAxgDLANAA1QDbAOAA5QDrAPAA9gD7AQEBBwENARMBGQEfASUBKwEyATgBPgFFAUwBUgFZAWABZwFu +AXUBfAGDAYsBkgGaAaEBqQGxAbkBwQHJAdEB2QHhAekB8gH6AgMCDAIUAh0CJgIvAjgCQQJLAlQCXQJn +AnECegKEAo4CmAKiAqwCtgLBAssC1QLgAusC9QMAAwsDFgMhAy0DOANDA08DWgNmA3IDfgOKA5YDogOu +A7oDxwPTA+AD7AP5BAYEEwQgBC0EOwRIBFUEYwRxBH4EjASaBKgEtgTEBNME4QTwBP4FDQUcBSsFOgVJ +BVgFZwV3BYYFlgWmBbUFxQXVBeUF9gYGBhYGJwY3BkgGWQZqBnsGjAadBq8GwAbRBuMG9QcHBxkHKwc9 +B08HYQd0B4YHmQesB78H0gflB/gICwgfCDIIRghaCG4IggiWCKoIvgjSCOcI+wkQCSUJOglPCWQJeQmP +CaQJugnPCeUJ+woRCicKPQpUCmoKgQqYCq4KxQrcCvMLCwsiCzkLUQtpC4ALmAuwC8gL4Qv5DBIMKgxD +DFwMdQyODKcMwAzZDPMNDQ0mDUANWg10DY4NqQ3DDd4N+A4TDi4OSQ5kDn8Omw62DtIO7g8JDyUPQQ9e +D3oPlg+zD88P7BAJECYQQxBhEH4QmxC5ENcQ9RETETERTxFtEYwRqhHJEegSBxImEkUSZBKEEqMSwxLj +EwMTIxNDE2MTgxOkE8UT5RQGFCcUSRRqFIsUrRTOFPAVEhU0FVYVeBWbFb0V4BYDFiYWSRZsFo8WshbW +FvoXHRdBF2UXiReuF9IX9xgbGEAYZRiKGK8Y1Rj6GSAZRRlrGZEZtxndGgQaKhpRGncanhrFGuwbFBs7 +G2MbihuyG9ocAhwqHFIcexyjHMwc9R0eHUcdcB2ZHcMd7B4WHkAeah6UHr4e6R8THz4faR+UH78f6iAV +IEEgbCCYIMQg8CEcIUghdSGhIc4h+yInIlUigiKvIt0jCiM4I2YjlCPCI/AkHyRNJHwkqyTaJQklOCVo +JZclxyX3JicmVyaHJrcm6CcYJ0kneierJ9woDSg/KHEooijUKQYpOClrKZ0p0CoCKjUqaCqbKs8rAis2 +K2krnSvRLAUsOSxuLKIs1y0MLUEtdi2rLeEuFi5MLoIuty7uLyQvWi+RL8cv/jA1MGwwpDDbMRIxSjGC +Mbox8jIqMmMymzLUMw0zRjN/M7gz8TQrNGU0njTYNRM1TTWHNcI1/TY3NnI2rjbpNyQ3YDecN9c4FDhQ +OIw4yDkFOUI5fzm8Ofk6Njp0OrI67zstO2s7qjvoPCc8ZTykPOM9Ij1hPaE94D4gPmA+oD7gPyE/YT+i +P+JAI0BkQKZA50EpQWpBrEHuQjBCckK1QvdDOkN9Q8BEA0RHRIpEzkUSRVVFmkXeRiJGZ0arRvBHNUd7 +R8BIBUhLSJFI10kdSWNJqUnwSjdKfUrESwxLU0uaS+JMKkxyTLpNAk1KTZNN3E4lTm5Ot08AT0lPk0/d +UCdQcVC7UQZRUFGbUeZSMVJ8UsdTE1NfU6pT9lRCVI9U21UoVXVVwlYPVlxWqVb3V0RXklfgWC9YfVjL +WRpZaVm4WgdaVlqmWvVbRVuVW+VcNVyGXNZdJ114XcleGl5sXr1fD19hX7NgBWBXYKpg/GFPYaJh9WJJ +Ypxi8GNDY5dj62RAZJRk6WU9ZZJl52Y9ZpJm6Gc9Z5Nn6Wg/aJZo7GlDaZpp8WpIap9q92tPa6dr/2xX +bK9tCG1gbbluEm5rbsRvHm94b9FwK3CGcOBxOnGVcfByS3KmcwFzXXO4dBR0cHTMdSh1hXXhdj52m3b4 +d1Z3s3gReG54zHkqeYl553pGeqV7BHtje8J8IXyBfOF9QX2hfgF+Yn7CfyN/hH/lgEeAqIEKgWuBzYIw +gpKC9INXg7qEHYSAhOOFR4Wrhg6GcobXhzuHn4gEiGmIzokziZmJ/opkisqLMIuWi/yMY4zKjTGNmI3/ +jmaOzo82j56QBpBukNaRP5GokhGSepLjk02TtpQglIqU9JVflcmWNJaflwqXdZfgmEyYuJkkmZCZ/Jpo +mtWbQpuvnByciZz3nWSd0p5Anq6fHZ+Ln/qgaaDYoUehtqImopajBqN2o+akVqTHpTilqaYapoum/adu +p+CoUqjEqTepqaocqo+rAqt1q+msXKzQrUStuK4trqGvFq+LsACwdbDqsWCx1rJLssKzOLOutCW0nLUT +tYq2AbZ5tvC3aLfguFm40blKucK6O7q1uy67p7whvJu9Fb2Pvgq+hL7/v3q/9cBwwOzBZ8Hjwl/C28NY +w9TEUcTOxUvFyMZGxsPHQce/yD3IvMk6ybnKOMq3yzbLtsw1zLXNNc21zjbOts83z7jQOdC60TzRvtI/ +0sHTRNPG1EnUy9VO1dHWVdbY11zX4Nhk2OjZbNnx2nba+9uA3AXcit0Q3ZbeHN6i3ynfr+A24L3hROHM +4lPi2+Nj4+vkc+T85YTmDeaW5x/nqegy6LzpRunQ6lvq5etw6/vshu0R7ZzuKO6070DvzPBY8OXxcvH/ +8ozzGfOn9DT0wvVQ9d72bfb794r4Gfio+Tj5x/pX+uf7d/wH/Jj9Kf26/kv+3P9t///SLS4vMFokY2xh +c3NuYW1lWCRjbGFzc2VzXxAQTlNCaXRtYXBJbWFnZVJlcKMvMTJaTlNJbWFnZVJlcFhOU09iamVjdNIt +LjQ1V05TQXJyYXmiNDLSLS43OF5OU011dGFibGVBcnJheaM3NDLTOjsOPD0+V05TV2hpdGVcTlNDb2xv +clNwYWNlRDAgMAAQA4AM0i0uQEFXTlNDb2xvcqJAMtItLkNEV05TSW1hZ2WiQzIACAARABoAJAApADIA +NwBJAEwAUQBTAGIAaAB3AH4AhQCSAJkApQCtALwAvgDAAMUAxwDJAMsAzQDUANkA5ADmAOgA6gDvAPIA +9AD2APgA/wEWATIBNAE2E6YTqxO2E78T0hPWE+ET6hPvE/cT+hP/FA4UEhQZFCEULhQzFDUUNxQ8FEQU +RxRMFFQAAAAAAAACAQAAAAAAAABFAAAAAAAAAAAAAAAAAAAUVw diff --git a/i18n/src/main/resources/pt_PT.lproj/Command.xib b/i18n/src/main/resources/pt_PT.lproj/Command.xib index be7f2aaddd..db5c7ef3cb 100644 --- a/i18n/src/main/resources/pt_PT.lproj/Command.xib +++ b/i18n/src/main/resources/pt_PT.lproj/Command.xib @@ -1,7 +1,7 @@ - + - + diff --git a/i18n/src/main/resources/pt_PT.lproj/Connection.xib b/i18n/src/main/resources/pt_PT.lproj/Connection.xib index 2b72037ffc..44331e8a6d 100644 --- a/i18n/src/main/resources/pt_PT.lproj/Connection.xib +++ b/i18n/src/main/resources/pt_PT.lproj/Connection.xib @@ -1,7 +1,7 @@ - + - + @@ -76,7 +76,7 @@ - + diff --git a/i18n/src/main/resources/pt_PT.lproj/Info.xib b/i18n/src/main/resources/pt_PT.lproj/Info.xib index e4fbef1bdb..982a8d9093 100644 --- a/i18n/src/main/resources/pt_PT.lproj/Info.xib +++ b/i18n/src/main/resources/pt_PT.lproj/Info.xib @@ -1,7 +1,7 @@ - + - + @@ -89,7 +89,7 @@ - + @@ -1017,21 +1017,21 @@ - + - + - + @@ -1121,21 +1121,21 @@ - + - + - + @@ -1250,27 +1250,27 @@ diff --git a/i18n/src/main/resources/pt_PT.lproj/Login.strings b/i18n/src/main/resources/pt_PT.lproj/Login.strings index e5a2589921..4c46e4b68d 100644 Binary files a/i18n/src/main/resources/pt_PT.lproj/Login.strings and b/i18n/src/main/resources/pt_PT.lproj/Login.strings differ diff --git a/i18n/src/main/resources/pt_PT.lproj/Main.xib b/i18n/src/main/resources/pt_PT.lproj/Main.xib index ea3a640a64..a9ca5440fa 100644 --- a/i18n/src/main/resources/pt_PT.lproj/Main.xib +++ b/i18n/src/main/resources/pt_PT.lproj/Main.xib @@ -1,7 +1,7 @@ - + - + @@ -159,19 +159,19 @@ - + - + - + @@ -321,12 +321,12 @@ CA - + - + @@ -566,6 +566,10 @@ CA - + + + + + diff --git a/i18n/src/main/resources/pt_PT.lproj/Preferences.xib b/i18n/src/main/resources/pt_PT.lproj/Preferences.xib index 77c9638a18..b6e9c3e46f 100644 --- a/i18n/src/main/resources/pt_PT.lproj/Preferences.xib +++ b/i18n/src/main/resources/pt_PT.lproj/Preferences.xib @@ -1,7 +1,7 @@ - + - + @@ -266,7 +266,7 @@ - + @@ -307,7 +307,7 @@ - + @@ -538,7 +538,7 @@ - + @@ -578,7 +578,7 @@ - + @@ -613,7 +613,7 @@ - + @@ -644,7 +644,7 @@ - + Outras vistas @@ -1222,7 +1222,7 @@ - + @@ -1254,7 +1254,7 @@ - + @@ -1294,7 +1294,7 @@ - + @@ -1367,7 +1367,7 @@ - + @@ -1562,7 +1562,7 @@ - + @@ -1622,7 +1622,7 @@ - + @@ -1647,7 +1647,7 @@ - + @@ -1689,7 +1689,7 @@ - + @@ -1717,7 +1717,7 @@ - + @@ -1745,7 +1745,7 @@ - + @@ -1774,7 +1774,7 @@ - + @@ -1809,7 +1809,7 @@ - + @@ -1893,7 +1893,7 @@ - + @@ -1952,7 +1952,7 @@ - + @@ -1980,7 +1980,7 @@ - + @@ -2009,7 +2009,7 @@ - + diff --git a/i18n/src/main/resources/pt_PT.lproj/Profiles.xib b/i18n/src/main/resources/pt_PT.lproj/Profiles.xib index 059c96e63d..a37c540201 100644 --- a/i18n/src/main/resources/pt_PT.lproj/Profiles.xib +++ b/i18n/src/main/resources/pt_PT.lproj/Profiles.xib @@ -1,7 +1,7 @@ - + - + diff --git a/i18n/src/main/resources/pt_PT.lproj/Prompt.xib b/i18n/src/main/resources/pt_PT.lproj/Prompt.xib index 6223d6b074..59854bdac9 100644 --- a/i18n/src/main/resources/pt_PT.lproj/Prompt.xib +++ b/i18n/src/main/resources/pt_PT.lproj/Prompt.xib @@ -1,7 +1,7 @@ - + - + @@ -95,7 +95,7 @@ Gw - + diff --git a/i18n/src/main/resources/pt_PT.lproj/S3.strings b/i18n/src/main/resources/pt_PT.lproj/S3.strings index c3bf515468..d5596b93d7 100644 Binary files a/i18n/src/main/resources/pt_PT.lproj/S3.strings and b/i18n/src/main/resources/pt_PT.lproj/S3.strings differ diff --git a/i18n/src/main/resources/pt_PT.lproj/Updater.strings b/i18n/src/main/resources/pt_PT.lproj/Updater.strings index 3f1895a2fa..5af0b80d6e 100644 Binary files a/i18n/src/main/resources/pt_PT.lproj/Updater.strings and b/i18n/src/main/resources/pt_PT.lproj/Updater.strings differ diff --git a/i18n/src/main/resources/ro.lproj/Bookmark.xib b/i18n/src/main/resources/ro.lproj/Bookmark.xib index 51ba5aaf7c..b086eefa11 100644 --- a/i18n/src/main/resources/ro.lproj/Bookmark.xib +++ b/i18n/src/main/resources/ro.lproj/Bookmark.xib @@ -1,7 +1,7 @@ - + - + @@ -92,7 +92,7 @@ - + @@ -489,95 +489,95 @@ YnBsaXN0MDDUAQIDBAUGBwpYJHZlcnNpb25ZJGFyY2hpdmVyVCR0b3BYJG9iamVjdHMSAAGGoF8QD05T -S2V5ZWRBcmNoaXZlctEICVRyb290gAGuCwwZGh8UJCkqMTQ3PUBVJG51bGzWDQ4PEBESExQVFhcYVk5T -U2l6ZV5OU1Jlc2l6aW5nTW9kZVYkY2xhc3NcTlNJbWFnZUZsYWdzVk5TUmVwc1dOU0NvbG9ygAIQAIAN -EiDDAACAA4ALVnsxLCAxfdIbDxweWk5TLm9iamVjdHOhHYAEgArSGw8gI6IhIoAFgAaACdMPJSYnKBRf -EBROU1RJRkZSZXByZXNlbnRhdGlvbl8QGU5TSW50ZXJuYWxMYXlvdXREaXJlY3Rpb26ACIAHTxESbE1N -ACoAAAAKAAAAEAEAAAMAAAABAAEAAAEBAAMAAAABAAEAAAECAAMAAAACAAgACAEDAAMAAAABAAEAAAEG -AAMAAAABAAEAAAEKAAMAAAABAAEAAAERAAQAAAABAAAACAESAAMAAAABAAEAAAEVAAMAAAABAAIAAAEW -AAMAAAABAAEAAAEXAAQAAAABAAAAAgEcAAMAAAABAAEAAAEoAAMAAAABAAIAAAFSAAMAAAABAAEAAAFT -AAMAAAACAAEAAYdzAAcAABGcAAAA0AAAAAAAABGcYXBwbAIAAABtbnRyR1JBWVhZWiAH3AAIABcADwAu -AA9hY3NwQVBQTAAAAABub25lAAAAAAAAAAAAAAAAAAAAAAAA9tYAAQAAAADTLWFwcGwAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAVkZXNjAAAAwAAAAHlkc2NtAAABPAAA -CBpjcHJ0AAAJWAAAACN3dHB0AAAJfAAAABRrVFJDAAAJkAAACAxkZXNjAAAAAAAAAB9HZW5lcmljIEdy -YXkgR2FtbWEgMi4yIFByb2ZpbGUAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAbWx1YwAAAAAAAAAfAAAADHNr -U0sAAAAuAAABhGRhREsAAAA6AAABsmNhRVMAAAA4AAAB7HZpVk4AAABAAAACJHB0QlIAAABKAAACZHVr -VUEAAAAsAAACrmZyRlUAAAA+AAAC2mh1SFUAAAA0AAADGHpoVFcAAAAaAAADTGtvS1IAAAAiAAADZm5i -Tk8AAAA6AAADiGNzQ1oAAAAoAAADwmhlSUwAAAAkAAAD6nJvUk8AAAAqAAAEDmRlREUAAABOAAAEOGl0 -SVQAAABOAAAEhnN2U0UAAAA4AAAE1HpoQ04AAAAaAAAFDGphSlAAAAAmAAAFJmVsR1IAAAAqAAAFTHB0 -UE8AAABSAAAFdm5sTkwAAABAAAAFyGVzRVMAAABMAAAGCHRoVEgAAAAyAAAGVHRyVFIAAAAkAAAGhmZp -RkkAAABGAAAGqmhySFIAAAA+AAAG8HBsUEwAAABKAAAHLmFyRUcAAAAsAAAHeHJ1UlUAAAA6AAAHpGVu -VVMAAAA8AAAH3gBWAWEAZQBvAGIAZQBjAG4A4QAgAHMAaQB2AOEAIABnAGEAbQBhACAAMgAsADIARwBl -AG4AZQByAGkAcwBrACAAZwByAOUAIAAyACwAMgAgAGcAYQBtAG0AYQAtAHAAcgBvAGYAaQBsAEcAYQBt -AG0AYQAgAGQAZQAgAGcAcgBpAHMAbwBzACAAZwBlAG4A6AByAGkAYwBhACAAMgAuADIAQx6lAHUAIABo -AOwAbgBoACAATQDgAHUAIAB4AOEAbQAgAEMAaAB1AG4AZwAgAEcAYQBtAG0AYQAgADIALgAyAFAAZQBy -AGYAaQBsACAARwBlAG4A6QByAGkAYwBvACAAZABhACAARwBhAG0AYQAgAGQAZQAgAEMAaQBuAHoAYQBz -ACAAMgAsADIEFwQwBDMEMAQ7BEwEPQQwACAARwByAGEAeQAtBDMEMAQ8BDAAIAAyAC4AMgBQAHIAbwBm -AGkAbAAgAGcA6QBuAOkAcgBpAHEAdQBlACAAZwByAGkAcwAgAGcAYQBtAG0AYQAgADIALAAyAMEAbAB0 -AGEAbADhAG4AbwBzACAAcwB6APwAcgBrAGUAIABnAGEAbQBtAGEAIAAyAC4AMpAadShwcJaOUUlepgAy -AC4AMoJyX2ljz4/wx3y8GAAg1ozAyQAgrBC5yAAgADIALgAyACDVBLhc0wzHfABHAGUAbgBlAHIAaQBz -AGsAIABnAHIA5QAgAGcAYQBtAG0AYQAgADIALAAyAC0AcAByAG8AZgBpAGwATwBiAGUAYwBuAOEAIAFh -AGUAZADhACAAZwBhAG0AYQAgADIALgAyBdIF0AXeBdQAIAXQBeQF1QXoACAF2wXcBdwF2QAgADIALgAy -AEcAYQBtAGEAIABnAHIAaQAgAGcAZQBuAGUAcgBpAGMBAwAgADIALAAyAEEAbABsAGcAZQBtAGUAaQBu -AGUAcwAgAEcAcgBhAHUAcwB0AHUAZgBlAG4ALQBQAHIAbwBmAGkAbAAgAEcAYQBtAG0AYQAgADIALAAy -AFAAcgBvAGYAaQBsAG8AIABnAHIAaQBnAGkAbwAgAGcAZQBuAGUAcgBpAGMAbwAgAGQAZQBsAGwAYQAg -AGcAYQBtAG0AYQAgADIALAAyAEcAZQBuAGUAcgBpAHMAawAgAGcAcgDlACAAMgAsADIAIABnAGEAbQBt -AGEAcAByAG8AZgBpAGxmbpAacHBepnz7ZXAAMgAuADJjz4/wZYdO9k4AgiwwsDDsMKQwrDDzMN4AIAAy -AC4AMgAgMNcw7TDVMKEwpDDrA5MDtQO9A7kDugPMACADkwO6A8EDuQAgA5MDrAO8A7wDsQAgADIALgAy -AFAAZQByAGYAaQBsACAAZwBlAG4A6QByAGkAYwBvACAAZABlACAAYwBpAG4AegBlAG4AdABvAHMAIABk -AGEAIABHAGEAbQBtAGEAIAAyACwAMgBBAGwAZwBlAG0AZQBlAG4AIABnAHIAaQBqAHMAIABnAGEAbQBt -AGEAIAAyACwAMgAtAHAAcgBvAGYAaQBlAGwAUABlAHIAZgBpAGwAIABnAGUAbgDpAHIAaQBjAG8AIABk -AGUAIABnAGEAbQBtAGEAIABkAGUAIABnAHIAaQBzAGUAcwAgADIALAAyDiMOMQ4HDioONQ5BDgEOIQ4h -DjIOQA4BDiMOIg5MDhcOMQ5IDicORA4bACAAMgAuADIARwBlAG4AZQBsACAARwByAGkAIABHAGEAbQBh -ACAAMgAsADIAWQBsAGUAaQBuAGUAbgAgAGgAYQByAG0AYQBhAG4AIABnAGEAbQBtAGEAIAAyACwAMgAg -AC0AcAByAG8AZgBpAGkAbABpAEcAZQBuAGUAcgBpAQ0AawBpACAARwByAGEAeQAgAEcAYQBtAG0AYQAg -ADIALgAyACAAcAByAG8AZgBpAGwAVQBuAGkAdwBlAHIAcwBhAGwAbgB5ACAAcAByAG8AZgBpAGwAIABz -AHoAYQByAG8BWwBjAGkAIABnAGEAbQBtAGEAIAAyACwAMgY6BicGRQYnACAAMgAuADIAIAZEBkgGRgAg -BjEGRQYnBi8GSgAgBjkGJwZFBB4EMQRJBDAETwAgBEEENQRABDAETwAgBDMEMAQ8BDwEMAAgADIALAAy -AC0EPwRABD4ERAQ4BDsETABHAGUAbgBlAHIAaQBjACAARwByAGEAeQAgAEcAYQBtAG0AYQAgADIALgAy -ACAAUAByAG8AZgBpAGwAZQAAdGV4dAAAAABDb3B5cmlnaHQgQXBwbGUgSW5jLiwgMjAxMgAAWFlaIAAA -AAAAAPNRAAEAAAABFsxjdXJ2AAAAAAAABAAAAAAFAAoADwAUABkAHgAjACgALQAyADcAOwBAAEUASgBP -AFQAWQBeAGMAaABtAHIAdwB8AIEAhgCLAJAAlQCaAJ8ApACpAK4AsgC3ALwAwQDGAMsA0ADVANsA4ADl -AOsA8AD2APsBAQEHAQ0BEwEZAR8BJQErATIBOAE+AUUBTAFSAVkBYAFnAW4BdQF8AYMBiwGSAZoBoQGp -AbEBuQHBAckB0QHZAeEB6QHyAfoCAwIMAhQCHQImAi8COAJBAksCVAJdAmcCcQJ6AoQCjgKYAqICrAK2 -AsECywLVAuAC6wL1AwADCwMWAyEDLQM4A0MDTwNaA2YDcgN+A4oDlgOiA64DugPHA9MD4APsA/kEBgQT -BCAELQQ7BEgEVQRjBHEEfgSMBJoEqAS2BMQE0wThBPAE/gUNBRwFKwU6BUkFWAVnBXcFhgWWBaYFtQXF -BdUF5QX2BgYGFgYnBjcGSAZZBmoGewaMBp0GrwbABtEG4wb1BwcHGQcrBz0HTwdhB3QHhgeZB6wHvwfS -B+UH+AgLCB8IMghGCFoIbgiCCJYIqgi+CNII5wj7CRAJJQk6CU8JZAl5CY8JpAm6Cc8J5Qn7ChEKJwo9 -ClQKagqBCpgKrgrFCtwK8wsLCyILOQtRC2kLgAuYC7ALyAvhC/kMEgwqDEMMXAx1DI4MpwzADNkM8w0N -DSYNQA1aDXQNjg2pDcMN3g34DhMOLg5JDmQOfw6bDrYO0g7uDwkPJQ9BD14Peg+WD7MPzw/sEAkQJhBD -EGEQfhCbELkQ1xD1ERMRMRFPEW0RjBGqEckR6BIHEiYSRRJkEoQSoxLDEuMTAxMjE0MTYxODE6QTxRPl -FAYUJxRJFGoUixStFM4U8BUSFTQVVhV4FZsVvRXgFgMWJhZJFmwWjxayFtYW+hcdF0EXZReJF64X0hf3 -GBsYQBhlGIoYrxjVGPoZIBlFGWsZkRm3Gd0aBBoqGlEadxqeGsUa7BsUGzsbYxuKG7Ib2hwCHCocUhx7 -HKMczBz1HR4dRx1wHZkdwx3sHhYeQB5qHpQevh7pHxMfPh9pH5Qfvx/qIBUgQSBsIJggxCDwIRwhSCF1 -IaEhziH7IiciVSKCIq8i3SMKIzgjZiOUI8Ij8CQfJE0kfCSrJNolCSU4JWgllyXHJfcmJyZXJocmtybo -JxgnSSd6J6sn3CgNKD8ocSiiKNQpBik4KWspnSnQKgIqNSpoKpsqzysCKzYraSudK9EsBSw5LG4soizX -LQwtQS12Last4S4WLkwugi63Lu4vJC9aL5Evxy/+MDUwbDCkMNsxEjFKMYIxujHyMioyYzKbMtQzDTNG -M38zuDPxNCs0ZTSeNNg1EzVNNYc1wjX9Njc2cjauNuk3JDdgN5w31zgUOFA4jDjIOQU5Qjl/Obw5+To2 -OnQ6sjrvOy07azuqO+g8JzxlPKQ84z0iPWE9oT3gPiA+YD6gPuA/IT9hP6I/4kAjQGRApkDnQSlBakGs -Qe5CMEJyQrVC90M6Q31DwEQDREdEikTORRJFVUWaRd5GIkZnRqtG8Ec1R3tHwEgFSEtIkUjXSR1JY0mp -SfBKN0p9SsRLDEtTS5pL4kwqTHJMuk0CTUpNk03cTiVObk63TwBPSU+TT91QJ1BxULtRBlFQUZtR5lIx -UnxSx1MTU19TqlP2VEJUj1TbVShVdVXCVg9WXFapVvdXRFeSV+BYL1h9WMtZGllpWbhaB1pWWqZa9VtF -W5Vb5Vw1XIZc1l0nXXhdyV4aXmxevV8PX2Ffs2AFYFdgqmD8YU9homH1YklinGLwY0Njl2PrZEBklGTp -ZT1lkmXnZj1mkmboZz1nk2fpaD9olmjsaUNpmmnxakhqn2r3a09rp2v/bFdsr20IbWBtuW4SbmtuxG8e -b3hv0XArcIZw4HE6cZVx8HJLcqZzAXNdc7h0FHRwdMx1KHWFdeF2Pnabdvh3VnezeBF4bnjMeSp5iXnn -ekZ6pXsEe2N7wnwhfIF84X1BfaF+AX5ifsJ/I3+Ef+WAR4CogQqBa4HNgjCCkoL0g1eDuoQdhICE44VH -hauGDoZyhteHO4efiASIaYjOiTOJmYn+imSKyoswi5aL/IxjjMqNMY2Yjf+OZo7OjzaPnpAGkG6Q1pE/ -kaiSEZJ6kuOTTZO2lCCUipT0lV+VyZY0lp+XCpd1l+CYTJi4mSSZkJn8mmia1ZtCm6+cHJyJnPedZJ3S -nkCerp8dn4uf+qBpoNihR6G2oiailqMGo3aj5qRWpMelOKWpphqmi6b9p26n4KhSqMSpN6mpqhyqj6sC -q3Wr6axcrNCtRK24ri2uoa8Wr4uwALB1sOqxYLHWskuywrM4s660JbSctRO1irYBtnm28Ldot+C4WbjR -uUq5wro7urW7LrunvCG8m70VvY++Cr6Evv+/er/1wHDA7MFnwePCX8Lbw1jD1MRRxM7FS8XIxkbGw8dB -x7/IPci8yTrJuco4yrfLNsu2zDXMtc01zbXONs62zzfPuNA50LrRPNG+0j/SwdNE08bUSdTL1U7V0dZV -1tjXXNfg2GTY6Nls2fHadtr724DcBdyK3RDdlt4c3qLfKd+v4DbgveFE4cziU+Lb42Pj6+Rz5PzlhOYN -5pbnH+ep6DLovOlG6dDqW+rl63Dr++yG7RHtnO4o7rTvQO/M8Fjw5fFy8f/yjPMZ86f0NPTC9VD13vZt -9vv3ivgZ+Kj5OPnH+lf65/t3/Af8mP0p/br+S/7c/23//9IrLC0uWiRjbGFzc25hbWVYJGNsYXNzZXNf -EBBOU0JpdG1hcEltYWdlUmVwoy0vMFpOU0ltYWdlUmVwWE5TT2JqZWN00issMjNXTlNBcnJheaIyMNIr -LDU2Xk5TTXV0YWJsZUFycmF5ozUyMNM4OQ86OzxXTlNXaGl0ZVxOU0NvbG9yU3BhY2VEMCAwABADgAzS -Kyw+P1dOU0NvbG9yoj4w0issQUJXTlNJbWFnZaJBMAAIABEAGgAkACkAMgA3AEkATABRAFMAYgBoAHUA -fACLAJIAnwCmAK4AsACyALQAuQC7AL0AxADJANQA1gDYANoA3wDiAOQA5gDoAO8BBgEiASQBJhOWE5sT -phOvE8ITxhPRE9oT3xPnE+oT7xP+FAIUCRQRFB4UIxQlFCcULBQ0FDcUPBREAAAAAAAAAgEAAAAAAAAA -QwAAAAAAAAAAAAAAAAAAFEc +S2V5ZWRBcmNoaXZlctEICVRyb290gAGuCwwbHCEaJissMzY5P0JVJG51bGzXDQ4PEBESExQVFhcYGRpW +TlNTaXplViRjbGFzc1xOU0ltYWdlRmxhZ3NWTlNSZXBzW05TVGludENvbG9yV05TQ29sb3JeTlNSZXNp +emluZ01vZGWAAoANEiDDAACAA4AAgAsQAFZ7MSwgMX3SHQ4eIFpOUy5vYmplY3RzoR+ABIAK0h0OIiWi +IySABYAGgAnTDicoKSoaXxAUTlNUSUZGUmVwcmVzZW50YXRpb25fEBlOU0ludGVybmFsTGF5b3V0RGly +ZWN0aW9ugAiAB08REmxNTQAqAAAACgAAABABAAADAAAAAQABAAABAQADAAAAAQABAAABAgADAAAAAgAI +AAgBAwADAAAAAQABAAABBgADAAAAAQABAAABCgADAAAAAQABAAABEQAEAAAAAQAAAAgBEgADAAAAAQAB +AAABFQADAAAAAQACAAABFgADAAAAAQABAAABFwAEAAAAAQAAAAIBHAADAAAAAQABAAABKAADAAAAAQAC +AAABUgADAAAAAQABAAABUwADAAAAAgABAAGHcwAHAAARnAAAANAAAAAAAAARnGFwcGwCAAAAbW50ckdS +QVlYWVogB9wACAAXAA8ALgAPYWNzcEFQUEwAAAAAbm9uZQAAAAAAAAAAAAAAAAAAAAAAAPbWAAEAAAAA +0y1hcHBsAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFZGVzYwAA +AMAAAAB5ZHNjbQAAATwAAAgaY3BydAAACVgAAAAjd3RwdAAACXwAAAAUa1RSQwAACZAAAAgMZGVzYwAA +AAAAAAAfR2VuZXJpYyBHcmF5IEdhbW1hIDIuMiBQcm9maWxlAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAG1s +dWMAAAAAAAAAHwAAAAxza1NLAAAALgAAAYRkYURLAAAAOgAAAbJjYUVTAAAAOAAAAex2aVZOAAAAQAAA +AiRwdEJSAAAASgAAAmR1a1VBAAAALAAAAq5mckZVAAAAPgAAAtpodUhVAAAANAAAAxh6aFRXAAAAGgAA +A0xrb0tSAAAAIgAAA2ZuYk5PAAAAOgAAA4hjc0NaAAAAKAAAA8JoZUlMAAAAJAAAA+pyb1JPAAAAKgAA +BA5kZURFAAAATgAABDhpdElUAAAATgAABIZzdlNFAAAAOAAABNR6aENOAAAAGgAABQxqYUpQAAAAJgAA +BSZlbEdSAAAAKgAABUxwdFBPAAAAUgAABXZubE5MAAAAQAAABchlc0VTAAAATAAABgh0aFRIAAAAMgAA +BlR0clRSAAAAJAAABoZmaUZJAAAARgAABqpockhSAAAAPgAABvBwbFBMAAAASgAABy5hckVHAAAALAAA +B3hydVJVAAAAOgAAB6RlblVTAAAAPAAAB94AVgFhAGUAbwBiAGUAYwBuAOEAIABzAGkAdgDhACAAZwBh +AG0AYQAgADIALAAyAEcAZQBuAGUAcgBpAHMAawAgAGcAcgDlACAAMgAsADIAIABnAGEAbQBtAGEALQBw +AHIAbwBmAGkAbABHAGEAbQBtAGEAIABkAGUAIABnAHIAaQBzAG8AcwAgAGcAZQBuAOgAcgBpAGMAYQAg +ADIALgAyAEMepQB1ACAAaADsAG4AaAAgAE0A4AB1ACAAeADhAG0AIABDAGgAdQBuAGcAIABHAGEAbQBt +AGEAIAAyAC4AMgBQAGUAcgBmAGkAbAAgAEcAZQBuAOkAcgBpAGMAbwAgAGQAYQAgAEcAYQBtAGEAIABk +AGUAIABDAGkAbgB6AGEAcwAgADIALAAyBBcEMAQzBDAEOwRMBD0EMAAgAEcAcgBhAHkALQQzBDAEPAQw +ACAAMgAuADIAUAByAG8AZgBpAGwAIABnAOkAbgDpAHIAaQBxAHUAZQAgAGcAcgBpAHMAIABnAGEAbQBt +AGEAIAAyACwAMgDBAGwAdABhAGwA4QBuAG8AcwAgAHMAegD8AHIAawBlACAAZwBhAG0AbQBhACAAMgAu +ADKQGnUocHCWjlFJXqYAMgAuADKCcl9pY8+P8Md8vBgAINaMwMkAIKwQucgAIAAyAC4AMgAg1QS4XNMM +x3wARwBlAG4AZQByAGkAcwBrACAAZwByAOUAIABnAGEAbQBtAGEAIAAyACwAMgAtAHAAcgBvAGYAaQBs +AE8AYgBlAGMAbgDhACABYQBlAGQA4QAgAGcAYQBtAGEAIAAyAC4AMgXSBdAF3gXUACAF0AXkBdUF6AAg +BdsF3AXcBdkAIAAyAC4AMgBHAGEAbQBhACAAZwByAGkAIABnAGUAbgBlAHIAaQBjAQMAIAAyACwAMgBB +AGwAbABnAGUAbQBlAGkAbgBlAHMAIABHAHIAYQB1AHMAdAB1AGYAZQBuAC0AUAByAG8AZgBpAGwAIABH +AGEAbQBtAGEAIAAyACwAMgBQAHIAbwBmAGkAbABvACAAZwByAGkAZwBpAG8AIABnAGUAbgBlAHIAaQBj +AG8AIABkAGUAbABsAGEAIABnAGEAbQBtAGEAIAAyACwAMgBHAGUAbgBlAHIAaQBzAGsAIABnAHIA5QAg +ADIALAAyACAAZwBhAG0AbQBhAHAAcgBvAGYAaQBsZm6QGnBwXqZ8+2VwADIALgAyY8+P8GWHTvZOAIIs +MLAw7DCkMKww8zDeACAAMgAuADIAIDDXMO0w1TChMKQw6wOTA7UDvQO5A7oDzAAgA5MDugPBA7kAIAOT +A6wDvAO8A7EAIAAyAC4AMgBQAGUAcgBmAGkAbAAgAGcAZQBuAOkAcgBpAGMAbwAgAGQAZQAgAGMAaQBu +AHoAZQBuAHQAbwBzACAAZABhACAARwBhAG0AbQBhACAAMgAsADIAQQBsAGcAZQBtAGUAZQBuACAAZwBy +AGkAagBzACAAZwBhAG0AbQBhACAAMgAsADIALQBwAHIAbwBmAGkAZQBsAFAAZQByAGYAaQBsACAAZwBl +AG4A6QByAGkAYwBvACAAZABlACAAZwBhAG0AbQBhACAAZABlACAAZwByAGkAcwBlAHMAIAAyACwAMg4j +DjEOBw4qDjUOQQ4BDiEOIQ4yDkAOAQ4jDiIOTA4XDjEOSA4nDkQOGwAgADIALgAyAEcAZQBuAGUAbAAg +AEcAcgBpACAARwBhAG0AYQAgADIALAAyAFkAbABlAGkAbgBlAG4AIABoAGEAcgBtAGEAYQBuACAAZwBh +AG0AbQBhACAAMgAsADIAIAAtAHAAcgBvAGYAaQBpAGwAaQBHAGUAbgBlAHIAaQENAGsAaQAgAEcAcgBh +AHkAIABHAGEAbQBtAGEAIAAyAC4AMgAgAHAAcgBvAGYAaQBsAFUAbgBpAHcAZQByAHMAYQBsAG4AeQAg +AHAAcgBvAGYAaQBsACAAcwB6AGEAcgBvAVsAYwBpACAAZwBhAG0AbQBhACAAMgAsADIGOgYnBkUGJwAg +ADIALgAyACAGRAZIBkYAIAYxBkUGJwYvBkoAIAY5BicGRQQeBDEESQQwBE8AIARBBDUEQAQwBE8AIAQz +BDAEPAQ8BDAAIAAyACwAMgAtBD8EQAQ+BEQEOAQ7BEwARwBlAG4AZQByAGkAYwAgAEcAcgBhAHkAIABH +AGEAbQBtAGEAIAAyAC4AMgAgAFAAcgBvAGYAaQBsAGUAAHRleHQAAAAAQ29weXJpZ2h0IEFwcGxlIElu +Yy4sIDIwMTIAAFhZWiAAAAAAAADzUQABAAAAARbMY3VydgAAAAAAAAQAAAAABQAKAA8AFAAZAB4AIwAo +AC0AMgA3ADsAQABFAEoATwBUAFkAXgBjAGgAbQByAHcAfACBAIYAiwCQAJUAmgCfAKQAqQCuALIAtwC8 +AMEAxgDLANAA1QDbAOAA5QDrAPAA9gD7AQEBBwENARMBGQEfASUBKwEyATgBPgFFAUwBUgFZAWABZwFu +AXUBfAGDAYsBkgGaAaEBqQGxAbkBwQHJAdEB2QHhAekB8gH6AgMCDAIUAh0CJgIvAjgCQQJLAlQCXQJn +AnECegKEAo4CmAKiAqwCtgLBAssC1QLgAusC9QMAAwsDFgMhAy0DOANDA08DWgNmA3IDfgOKA5YDogOu +A7oDxwPTA+AD7AP5BAYEEwQgBC0EOwRIBFUEYwRxBH4EjASaBKgEtgTEBNME4QTwBP4FDQUcBSsFOgVJ +BVgFZwV3BYYFlgWmBbUFxQXVBeUF9gYGBhYGJwY3BkgGWQZqBnsGjAadBq8GwAbRBuMG9QcHBxkHKwc9 +B08HYQd0B4YHmQesB78H0gflB/gICwgfCDIIRghaCG4IggiWCKoIvgjSCOcI+wkQCSUJOglPCWQJeQmP +CaQJugnPCeUJ+woRCicKPQpUCmoKgQqYCq4KxQrcCvMLCwsiCzkLUQtpC4ALmAuwC8gL4Qv5DBIMKgxD +DFwMdQyODKcMwAzZDPMNDQ0mDUANWg10DY4NqQ3DDd4N+A4TDi4OSQ5kDn8Omw62DtIO7g8JDyUPQQ9e +D3oPlg+zD88P7BAJECYQQxBhEH4QmxC5ENcQ9RETETERTxFtEYwRqhHJEegSBxImEkUSZBKEEqMSwxLj +EwMTIxNDE2MTgxOkE8UT5RQGFCcUSRRqFIsUrRTOFPAVEhU0FVYVeBWbFb0V4BYDFiYWSRZsFo8WshbW +FvoXHRdBF2UXiReuF9IX9xgbGEAYZRiKGK8Y1Rj6GSAZRRlrGZEZtxndGgQaKhpRGncanhrFGuwbFBs7 +G2MbihuyG9ocAhwqHFIcexyjHMwc9R0eHUcdcB2ZHcMd7B4WHkAeah6UHr4e6R8THz4faR+UH78f6iAV +IEEgbCCYIMQg8CEcIUghdSGhIc4h+yInIlUigiKvIt0jCiM4I2YjlCPCI/AkHyRNJHwkqyTaJQklOCVo +JZclxyX3JicmVyaHJrcm6CcYJ0kneierJ9woDSg/KHEooijUKQYpOClrKZ0p0CoCKjUqaCqbKs8rAis2 +K2krnSvRLAUsOSxuLKIs1y0MLUEtdi2rLeEuFi5MLoIuty7uLyQvWi+RL8cv/jA1MGwwpDDbMRIxSjGC +Mbox8jIqMmMymzLUMw0zRjN/M7gz8TQrNGU0njTYNRM1TTWHNcI1/TY3NnI2rjbpNyQ3YDecN9c4FDhQ +OIw4yDkFOUI5fzm8Ofk6Njp0OrI67zstO2s7qjvoPCc8ZTykPOM9Ij1hPaE94D4gPmA+oD7gPyE/YT+i +P+JAI0BkQKZA50EpQWpBrEHuQjBCckK1QvdDOkN9Q8BEA0RHRIpEzkUSRVVFmkXeRiJGZ0arRvBHNUd7 +R8BIBUhLSJFI10kdSWNJqUnwSjdKfUrESwxLU0uaS+JMKkxyTLpNAk1KTZNN3E4lTm5Ot08AT0lPk0/d +UCdQcVC7UQZRUFGbUeZSMVJ8UsdTE1NfU6pT9lRCVI9U21UoVXVVwlYPVlxWqVb3V0RXklfgWC9YfVjL +WRpZaVm4WgdaVlqmWvVbRVuVW+VcNVyGXNZdJ114XcleGl5sXr1fD19hX7NgBWBXYKpg/GFPYaJh9WJJ +Ypxi8GNDY5dj62RAZJRk6WU9ZZJl52Y9ZpJm6Gc9Z5Nn6Wg/aJZo7GlDaZpp8WpIap9q92tPa6dr/2xX +bK9tCG1gbbluEm5rbsRvHm94b9FwK3CGcOBxOnGVcfByS3KmcwFzXXO4dBR0cHTMdSh1hXXhdj52m3b4 +d1Z3s3gReG54zHkqeYl553pGeqV7BHtje8J8IXyBfOF9QX2hfgF+Yn7CfyN/hH/lgEeAqIEKgWuBzYIw +gpKC9INXg7qEHYSAhOOFR4Wrhg6GcobXhzuHn4gEiGmIzokziZmJ/opkisqLMIuWi/yMY4zKjTGNmI3/ +jmaOzo82j56QBpBukNaRP5GokhGSepLjk02TtpQglIqU9JVflcmWNJaflwqXdZfgmEyYuJkkmZCZ/Jpo +mtWbQpuvnByciZz3nWSd0p5Anq6fHZ+Ln/qgaaDYoUehtqImopajBqN2o+akVqTHpTilqaYapoum/adu +p+CoUqjEqTepqaocqo+rAqt1q+msXKzQrUStuK4trqGvFq+LsACwdbDqsWCx1rJLssKzOLOutCW0nLUT +tYq2AbZ5tvC3aLfguFm40blKucK6O7q1uy67p7whvJu9Fb2Pvgq+hL7/v3q/9cBwwOzBZ8Hjwl/C28NY +w9TEUcTOxUvFyMZGxsPHQce/yD3IvMk6ybnKOMq3yzbLtsw1zLXNNc21zjbOts83z7jQOdC60TzRvtI/ +0sHTRNPG1EnUy9VO1dHWVdbY11zX4Nhk2OjZbNnx2nba+9uA3AXcit0Q3ZbeHN6i3ynfr+A24L3hROHM +4lPi2+Nj4+vkc+T85YTmDeaW5x/nqegy6LzpRunQ6lvq5etw6/vshu0R7ZzuKO6070DvzPBY8OXxcvH/ +8ozzGfOn9DT0wvVQ9d72bfb794r4Gfio+Tj5x/pX+uf7d/wH/Jj9Kf26/kv+3P9t///SLS4vMFokY2xh +c3NuYW1lWCRjbGFzc2VzXxAQTlNCaXRtYXBJbWFnZVJlcKMvMTJaTlNJbWFnZVJlcFhOU09iamVjdNIt +LjQ1V05TQXJyYXmiNDLSLS43OF5OU011dGFibGVBcnJheaM3NDLTOjsOPD0+V05TV2hpdGVcTlNDb2xv +clNwYWNlRDAgMAAQA4AM0i0uQEFXTlNDb2xvcqJAMtItLkNEV05TSW1hZ2WiQzIACAARABoAJAApADIA +NwBJAEwAUQBTAGIAaAB3AH4AhQCSAJkApQCtALwAvgDAAMUAxwDJAMsAzQDUANkA5ADmAOgA6gDvAPIA +9AD2APgA/wEWATIBNAE2E6YTqxO2E78T0hPWE+ET6hPvE/cT+hP/FA4UEhQZFCEULhQzFDUUNxQ8FEQU +RxRMFFQAAAAAAAACAQAAAAAAAABFAAAAAAAAAAAAAAAAAAAUVw diff --git a/i18n/src/main/resources/ro.lproj/Browser.xib b/i18n/src/main/resources/ro.lproj/Browser.xib index 8b28781d29..63c6789739 100644 --- a/i18n/src/main/resources/ro.lproj/Browser.xib +++ b/i18n/src/main/resources/ro.lproj/Browser.xib @@ -1,7 +1,7 @@ - + - + @@ -38,7 +38,7 @@ - + @@ -62,27 +62,27 @@ @@ -256,7 +256,7 @@ - + @@ -553,95 +553,95 @@ DQ YnBsaXN0MDDUAQIDBAUGBwpYJHZlcnNpb25ZJGFyY2hpdmVyVCR0b3BYJG9iamVjdHMSAAGGoF8QD05T -S2V5ZWRBcmNoaXZlctEICVRyb290gAGuCwwZGh8UJCkqMTQ3PUBVJG51bGzWDQ4PEBESExQVFhcYVk5T -U2l6ZV5OU1Jlc2l6aW5nTW9kZVYkY2xhc3NcTlNJbWFnZUZsYWdzVk5TUmVwc1dOU0NvbG9ygAIQAIAN -EiDDAACAA4ALVnsxLCAxfdIbDxweWk5TLm9iamVjdHOhHYAEgArSGw8gI6IhIoAFgAaACdMPJSYnKBRf -EBROU1RJRkZSZXByZXNlbnRhdGlvbl8QGU5TSW50ZXJuYWxMYXlvdXREaXJlY3Rpb26ACIAHTxESbE1N -ACoAAAAKAAAAEAEAAAMAAAABAAEAAAEBAAMAAAABAAEAAAECAAMAAAACAAgACAEDAAMAAAABAAEAAAEG -AAMAAAABAAEAAAEKAAMAAAABAAEAAAERAAQAAAABAAAACAESAAMAAAABAAEAAAEVAAMAAAABAAIAAAEW -AAMAAAABAAEAAAEXAAQAAAABAAAAAgEcAAMAAAABAAEAAAEoAAMAAAABAAIAAAFSAAMAAAABAAEAAAFT -AAMAAAACAAEAAYdzAAcAABGcAAAA0AAAAAAAABGcYXBwbAIAAABtbnRyR1JBWVhZWiAH3AAIABcADwAu -AA9hY3NwQVBQTAAAAABub25lAAAAAAAAAAAAAAAAAAAAAAAA9tYAAQAAAADTLWFwcGwAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAVkZXNjAAAAwAAAAHlkc2NtAAABPAAA -CBpjcHJ0AAAJWAAAACN3dHB0AAAJfAAAABRrVFJDAAAJkAAACAxkZXNjAAAAAAAAAB9HZW5lcmljIEdy -YXkgR2FtbWEgMi4yIFByb2ZpbGUAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAbWx1YwAAAAAAAAAfAAAADHNr -U0sAAAAuAAABhGRhREsAAAA6AAABsmNhRVMAAAA4AAAB7HZpVk4AAABAAAACJHB0QlIAAABKAAACZHVr -VUEAAAAsAAACrmZyRlUAAAA+AAAC2mh1SFUAAAA0AAADGHpoVFcAAAAaAAADTGtvS1IAAAAiAAADZm5i -Tk8AAAA6AAADiGNzQ1oAAAAoAAADwmhlSUwAAAAkAAAD6nJvUk8AAAAqAAAEDmRlREUAAABOAAAEOGl0 -SVQAAABOAAAEhnN2U0UAAAA4AAAE1HpoQ04AAAAaAAAFDGphSlAAAAAmAAAFJmVsR1IAAAAqAAAFTHB0 -UE8AAABSAAAFdm5sTkwAAABAAAAFyGVzRVMAAABMAAAGCHRoVEgAAAAyAAAGVHRyVFIAAAAkAAAGhmZp -RkkAAABGAAAGqmhySFIAAAA+AAAG8HBsUEwAAABKAAAHLmFyRUcAAAAsAAAHeHJ1UlUAAAA6AAAHpGVu -VVMAAAA8AAAH3gBWAWEAZQBvAGIAZQBjAG4A4QAgAHMAaQB2AOEAIABnAGEAbQBhACAAMgAsADIARwBl -AG4AZQByAGkAcwBrACAAZwByAOUAIAAyACwAMgAgAGcAYQBtAG0AYQAtAHAAcgBvAGYAaQBsAEcAYQBt -AG0AYQAgAGQAZQAgAGcAcgBpAHMAbwBzACAAZwBlAG4A6AByAGkAYwBhACAAMgAuADIAQx6lAHUAIABo -AOwAbgBoACAATQDgAHUAIAB4AOEAbQAgAEMAaAB1AG4AZwAgAEcAYQBtAG0AYQAgADIALgAyAFAAZQBy -AGYAaQBsACAARwBlAG4A6QByAGkAYwBvACAAZABhACAARwBhAG0AYQAgAGQAZQAgAEMAaQBuAHoAYQBz -ACAAMgAsADIEFwQwBDMEMAQ7BEwEPQQwACAARwByAGEAeQAtBDMEMAQ8BDAAIAAyAC4AMgBQAHIAbwBm -AGkAbAAgAGcA6QBuAOkAcgBpAHEAdQBlACAAZwByAGkAcwAgAGcAYQBtAG0AYQAgADIALAAyAMEAbAB0 -AGEAbADhAG4AbwBzACAAcwB6APwAcgBrAGUAIABnAGEAbQBtAGEAIAAyAC4AMpAadShwcJaOUUlepgAy -AC4AMoJyX2ljz4/wx3y8GAAg1ozAyQAgrBC5yAAgADIALgAyACDVBLhc0wzHfABHAGUAbgBlAHIAaQBz -AGsAIABnAHIA5QAgAGcAYQBtAG0AYQAgADIALAAyAC0AcAByAG8AZgBpAGwATwBiAGUAYwBuAOEAIAFh -AGUAZADhACAAZwBhAG0AYQAgADIALgAyBdIF0AXeBdQAIAXQBeQF1QXoACAF2wXcBdwF2QAgADIALgAy -AEcAYQBtAGEAIABnAHIAaQAgAGcAZQBuAGUAcgBpAGMBAwAgADIALAAyAEEAbABsAGcAZQBtAGUAaQBu -AGUAcwAgAEcAcgBhAHUAcwB0AHUAZgBlAG4ALQBQAHIAbwBmAGkAbAAgAEcAYQBtAG0AYQAgADIALAAy -AFAAcgBvAGYAaQBsAG8AIABnAHIAaQBnAGkAbwAgAGcAZQBuAGUAcgBpAGMAbwAgAGQAZQBsAGwAYQAg -AGcAYQBtAG0AYQAgADIALAAyAEcAZQBuAGUAcgBpAHMAawAgAGcAcgDlACAAMgAsADIAIABnAGEAbQBt -AGEAcAByAG8AZgBpAGxmbpAacHBepnz7ZXAAMgAuADJjz4/wZYdO9k4AgiwwsDDsMKQwrDDzMN4AIAAy -AC4AMgAgMNcw7TDVMKEwpDDrA5MDtQO9A7kDugPMACADkwO6A8EDuQAgA5MDrAO8A7wDsQAgADIALgAy -AFAAZQByAGYAaQBsACAAZwBlAG4A6QByAGkAYwBvACAAZABlACAAYwBpAG4AegBlAG4AdABvAHMAIABk -AGEAIABHAGEAbQBtAGEAIAAyACwAMgBBAGwAZwBlAG0AZQBlAG4AIABnAHIAaQBqAHMAIABnAGEAbQBt -AGEAIAAyACwAMgAtAHAAcgBvAGYAaQBlAGwAUABlAHIAZgBpAGwAIABnAGUAbgDpAHIAaQBjAG8AIABk -AGUAIABnAGEAbQBtAGEAIABkAGUAIABnAHIAaQBzAGUAcwAgADIALAAyDiMOMQ4HDioONQ5BDgEOIQ4h -DjIOQA4BDiMOIg5MDhcOMQ5IDicORA4bACAAMgAuADIARwBlAG4AZQBsACAARwByAGkAIABHAGEAbQBh -ACAAMgAsADIAWQBsAGUAaQBuAGUAbgAgAGgAYQByAG0AYQBhAG4AIABnAGEAbQBtAGEAIAAyACwAMgAg -AC0AcAByAG8AZgBpAGkAbABpAEcAZQBuAGUAcgBpAQ0AawBpACAARwByAGEAeQAgAEcAYQBtAG0AYQAg -ADIALgAyACAAcAByAG8AZgBpAGwAVQBuAGkAdwBlAHIAcwBhAGwAbgB5ACAAcAByAG8AZgBpAGwAIABz -AHoAYQByAG8BWwBjAGkAIABnAGEAbQBtAGEAIAAyACwAMgY6BicGRQYnACAAMgAuADIAIAZEBkgGRgAg -BjEGRQYnBi8GSgAgBjkGJwZFBB4EMQRJBDAETwAgBEEENQRABDAETwAgBDMEMAQ8BDwEMAAgADIALAAy -AC0EPwRABD4ERAQ4BDsETABHAGUAbgBlAHIAaQBjACAARwByAGEAeQAgAEcAYQBtAG0AYQAgADIALgAy -ACAAUAByAG8AZgBpAGwAZQAAdGV4dAAAAABDb3B5cmlnaHQgQXBwbGUgSW5jLiwgMjAxMgAAWFlaIAAA -AAAAAPNRAAEAAAABFsxjdXJ2AAAAAAAABAAAAAAFAAoADwAUABkAHgAjACgALQAyADcAOwBAAEUASgBP -AFQAWQBeAGMAaABtAHIAdwB8AIEAhgCLAJAAlQCaAJ8ApACpAK4AsgC3ALwAwQDGAMsA0ADVANsA4ADl -AOsA8AD2APsBAQEHAQ0BEwEZAR8BJQErATIBOAE+AUUBTAFSAVkBYAFnAW4BdQF8AYMBiwGSAZoBoQGp -AbEBuQHBAckB0QHZAeEB6QHyAfoCAwIMAhQCHQImAi8COAJBAksCVAJdAmcCcQJ6AoQCjgKYAqICrAK2 -AsECywLVAuAC6wL1AwADCwMWAyEDLQM4A0MDTwNaA2YDcgN+A4oDlgOiA64DugPHA9MD4APsA/kEBgQT -BCAELQQ7BEgEVQRjBHEEfgSMBJoEqAS2BMQE0wThBPAE/gUNBRwFKwU6BUkFWAVnBXcFhgWWBaYFtQXF -BdUF5QX2BgYGFgYnBjcGSAZZBmoGewaMBp0GrwbABtEG4wb1BwcHGQcrBz0HTwdhB3QHhgeZB6wHvwfS -B+UH+AgLCB8IMghGCFoIbgiCCJYIqgi+CNII5wj7CRAJJQk6CU8JZAl5CY8JpAm6Cc8J5Qn7ChEKJwo9 -ClQKagqBCpgKrgrFCtwK8wsLCyILOQtRC2kLgAuYC7ALyAvhC/kMEgwqDEMMXAx1DI4MpwzADNkM8w0N -DSYNQA1aDXQNjg2pDcMN3g34DhMOLg5JDmQOfw6bDrYO0g7uDwkPJQ9BD14Peg+WD7MPzw/sEAkQJhBD -EGEQfhCbELkQ1xD1ERMRMRFPEW0RjBGqEckR6BIHEiYSRRJkEoQSoxLDEuMTAxMjE0MTYxODE6QTxRPl -FAYUJxRJFGoUixStFM4U8BUSFTQVVhV4FZsVvRXgFgMWJhZJFmwWjxayFtYW+hcdF0EXZReJF64X0hf3 -GBsYQBhlGIoYrxjVGPoZIBlFGWsZkRm3Gd0aBBoqGlEadxqeGsUa7BsUGzsbYxuKG7Ib2hwCHCocUhx7 -HKMczBz1HR4dRx1wHZkdwx3sHhYeQB5qHpQevh7pHxMfPh9pH5Qfvx/qIBUgQSBsIJggxCDwIRwhSCF1 -IaEhziH7IiciVSKCIq8i3SMKIzgjZiOUI8Ij8CQfJE0kfCSrJNolCSU4JWgllyXHJfcmJyZXJocmtybo -JxgnSSd6J6sn3CgNKD8ocSiiKNQpBik4KWspnSnQKgIqNSpoKpsqzysCKzYraSudK9EsBSw5LG4soizX -LQwtQS12Last4S4WLkwugi63Lu4vJC9aL5Evxy/+MDUwbDCkMNsxEjFKMYIxujHyMioyYzKbMtQzDTNG -M38zuDPxNCs0ZTSeNNg1EzVNNYc1wjX9Njc2cjauNuk3JDdgN5w31zgUOFA4jDjIOQU5Qjl/Obw5+To2 -OnQ6sjrvOy07azuqO+g8JzxlPKQ84z0iPWE9oT3gPiA+YD6gPuA/IT9hP6I/4kAjQGRApkDnQSlBakGs -Qe5CMEJyQrVC90M6Q31DwEQDREdEikTORRJFVUWaRd5GIkZnRqtG8Ec1R3tHwEgFSEtIkUjXSR1JY0mp -SfBKN0p9SsRLDEtTS5pL4kwqTHJMuk0CTUpNk03cTiVObk63TwBPSU+TT91QJ1BxULtRBlFQUZtR5lIx -UnxSx1MTU19TqlP2VEJUj1TbVShVdVXCVg9WXFapVvdXRFeSV+BYL1h9WMtZGllpWbhaB1pWWqZa9VtF -W5Vb5Vw1XIZc1l0nXXhdyV4aXmxevV8PX2Ffs2AFYFdgqmD8YU9homH1YklinGLwY0Njl2PrZEBklGTp -ZT1lkmXnZj1mkmboZz1nk2fpaD9olmjsaUNpmmnxakhqn2r3a09rp2v/bFdsr20IbWBtuW4SbmtuxG8e -b3hv0XArcIZw4HE6cZVx8HJLcqZzAXNdc7h0FHRwdMx1KHWFdeF2Pnabdvh3VnezeBF4bnjMeSp5iXnn -ekZ6pXsEe2N7wnwhfIF84X1BfaF+AX5ifsJ/I3+Ef+WAR4CogQqBa4HNgjCCkoL0g1eDuoQdhICE44VH -hauGDoZyhteHO4efiASIaYjOiTOJmYn+imSKyoswi5aL/IxjjMqNMY2Yjf+OZo7OjzaPnpAGkG6Q1pE/ -kaiSEZJ6kuOTTZO2lCCUipT0lV+VyZY0lp+XCpd1l+CYTJi4mSSZkJn8mmia1ZtCm6+cHJyJnPedZJ3S -nkCerp8dn4uf+qBpoNihR6G2oiailqMGo3aj5qRWpMelOKWpphqmi6b9p26n4KhSqMSpN6mpqhyqj6sC -q3Wr6axcrNCtRK24ri2uoa8Wr4uwALB1sOqxYLHWskuywrM4s660JbSctRO1irYBtnm28Ldot+C4WbjR -uUq5wro7urW7LrunvCG8m70VvY++Cr6Evv+/er/1wHDA7MFnwePCX8Lbw1jD1MRRxM7FS8XIxkbGw8dB -x7/IPci8yTrJuco4yrfLNsu2zDXMtc01zbXONs62zzfPuNA50LrRPNG+0j/SwdNE08bUSdTL1U7V0dZV -1tjXXNfg2GTY6Nls2fHadtr724DcBdyK3RDdlt4c3qLfKd+v4DbgveFE4cziU+Lb42Pj6+Rz5PzlhOYN -5pbnH+ep6DLovOlG6dDqW+rl63Dr++yG7RHtnO4o7rTvQO/M8Fjw5fFy8f/yjPMZ86f0NPTC9VD13vZt -9vv3ivgZ+Kj5OPnH+lf65/t3/Af8mP0p/br+S/7c/23//9IrLC0uWiRjbGFzc25hbWVYJGNsYXNzZXNf -EBBOU0JpdG1hcEltYWdlUmVwoy0vMFpOU0ltYWdlUmVwWE5TT2JqZWN00issMjNXTlNBcnJheaIyMNIr -LDU2Xk5TTXV0YWJsZUFycmF5ozUyMNM4OQ86OzxXTlNXaGl0ZVxOU0NvbG9yU3BhY2VEMCAwABADgAzS -Kyw+P1dOU0NvbG9yoj4w0issQUJXTlNJbWFnZaJBMAAIABEAGgAkACkAMgA3AEkATABRAFMAYgBoAHUA -fACLAJIAnwCmAK4AsACyALQAuQC7AL0AxADJANQA1gDYANoA3wDiAOQA5gDoAO8BBgEiASQBJhOWE5sT -phOvE8ITxhPRE9oT3xPnE+oT7xP+FAIUCRQRFB4UIxQlFCcULBQ0FDcUPBREAAAAAAAAAgEAAAAAAAAA -QwAAAAAAAAAAAAAAAAAAFEc +S2V5ZWRBcmNoaXZlctEICVRyb290gAGuCwwbHCEaJissMzY5P0JVJG51bGzXDQ4PEBESExQVFhcYGRpW +TlNTaXplViRjbGFzc1xOU0ltYWdlRmxhZ3NWTlNSZXBzW05TVGludENvbG9yV05TQ29sb3JeTlNSZXNp +emluZ01vZGWAAoANEiDDAACAA4AAgAsQAFZ7MSwgMX3SHQ4eIFpOUy5vYmplY3RzoR+ABIAK0h0OIiWi +IySABYAGgAnTDicoKSoaXxAUTlNUSUZGUmVwcmVzZW50YXRpb25fEBlOU0ludGVybmFsTGF5b3V0RGly +ZWN0aW9ugAiAB08REmxNTQAqAAAACgAAABABAAADAAAAAQABAAABAQADAAAAAQABAAABAgADAAAAAgAI +AAgBAwADAAAAAQABAAABBgADAAAAAQABAAABCgADAAAAAQABAAABEQAEAAAAAQAAAAgBEgADAAAAAQAB +AAABFQADAAAAAQACAAABFgADAAAAAQABAAABFwAEAAAAAQAAAAIBHAADAAAAAQABAAABKAADAAAAAQAC +AAABUgADAAAAAQABAAABUwADAAAAAgABAAGHcwAHAAARnAAAANAAAAAAAAARnGFwcGwCAAAAbW50ckdS +QVlYWVogB9wACAAXAA8ALgAPYWNzcEFQUEwAAAAAbm9uZQAAAAAAAAAAAAAAAAAAAAAAAPbWAAEAAAAA +0y1hcHBsAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFZGVzYwAA +AMAAAAB5ZHNjbQAAATwAAAgaY3BydAAACVgAAAAjd3RwdAAACXwAAAAUa1RSQwAACZAAAAgMZGVzYwAA +AAAAAAAfR2VuZXJpYyBHcmF5IEdhbW1hIDIuMiBQcm9maWxlAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAG1s +dWMAAAAAAAAAHwAAAAxza1NLAAAALgAAAYRkYURLAAAAOgAAAbJjYUVTAAAAOAAAAex2aVZOAAAAQAAA +AiRwdEJSAAAASgAAAmR1a1VBAAAALAAAAq5mckZVAAAAPgAAAtpodUhVAAAANAAAAxh6aFRXAAAAGgAA +A0xrb0tSAAAAIgAAA2ZuYk5PAAAAOgAAA4hjc0NaAAAAKAAAA8JoZUlMAAAAJAAAA+pyb1JPAAAAKgAA +BA5kZURFAAAATgAABDhpdElUAAAATgAABIZzdlNFAAAAOAAABNR6aENOAAAAGgAABQxqYUpQAAAAJgAA +BSZlbEdSAAAAKgAABUxwdFBPAAAAUgAABXZubE5MAAAAQAAABchlc0VTAAAATAAABgh0aFRIAAAAMgAA +BlR0clRSAAAAJAAABoZmaUZJAAAARgAABqpockhSAAAAPgAABvBwbFBMAAAASgAABy5hckVHAAAALAAA +B3hydVJVAAAAOgAAB6RlblVTAAAAPAAAB94AVgFhAGUAbwBiAGUAYwBuAOEAIABzAGkAdgDhACAAZwBh +AG0AYQAgADIALAAyAEcAZQBuAGUAcgBpAHMAawAgAGcAcgDlACAAMgAsADIAIABnAGEAbQBtAGEALQBw +AHIAbwBmAGkAbABHAGEAbQBtAGEAIABkAGUAIABnAHIAaQBzAG8AcwAgAGcAZQBuAOgAcgBpAGMAYQAg +ADIALgAyAEMepQB1ACAAaADsAG4AaAAgAE0A4AB1ACAAeADhAG0AIABDAGgAdQBuAGcAIABHAGEAbQBt +AGEAIAAyAC4AMgBQAGUAcgBmAGkAbAAgAEcAZQBuAOkAcgBpAGMAbwAgAGQAYQAgAEcAYQBtAGEAIABk +AGUAIABDAGkAbgB6AGEAcwAgADIALAAyBBcEMAQzBDAEOwRMBD0EMAAgAEcAcgBhAHkALQQzBDAEPAQw +ACAAMgAuADIAUAByAG8AZgBpAGwAIABnAOkAbgDpAHIAaQBxAHUAZQAgAGcAcgBpAHMAIABnAGEAbQBt +AGEAIAAyACwAMgDBAGwAdABhAGwA4QBuAG8AcwAgAHMAegD8AHIAawBlACAAZwBhAG0AbQBhACAAMgAu +ADKQGnUocHCWjlFJXqYAMgAuADKCcl9pY8+P8Md8vBgAINaMwMkAIKwQucgAIAAyAC4AMgAg1QS4XNMM +x3wARwBlAG4AZQByAGkAcwBrACAAZwByAOUAIABnAGEAbQBtAGEAIAAyACwAMgAtAHAAcgBvAGYAaQBs +AE8AYgBlAGMAbgDhACABYQBlAGQA4QAgAGcAYQBtAGEAIAAyAC4AMgXSBdAF3gXUACAF0AXkBdUF6AAg +BdsF3AXcBdkAIAAyAC4AMgBHAGEAbQBhACAAZwByAGkAIABnAGUAbgBlAHIAaQBjAQMAIAAyACwAMgBB +AGwAbABnAGUAbQBlAGkAbgBlAHMAIABHAHIAYQB1AHMAdAB1AGYAZQBuAC0AUAByAG8AZgBpAGwAIABH +AGEAbQBtAGEAIAAyACwAMgBQAHIAbwBmAGkAbABvACAAZwByAGkAZwBpAG8AIABnAGUAbgBlAHIAaQBj +AG8AIABkAGUAbABsAGEAIABnAGEAbQBtAGEAIAAyACwAMgBHAGUAbgBlAHIAaQBzAGsAIABnAHIA5QAg +ADIALAAyACAAZwBhAG0AbQBhAHAAcgBvAGYAaQBsZm6QGnBwXqZ8+2VwADIALgAyY8+P8GWHTvZOAIIs +MLAw7DCkMKww8zDeACAAMgAuADIAIDDXMO0w1TChMKQw6wOTA7UDvQO5A7oDzAAgA5MDugPBA7kAIAOT +A6wDvAO8A7EAIAAyAC4AMgBQAGUAcgBmAGkAbAAgAGcAZQBuAOkAcgBpAGMAbwAgAGQAZQAgAGMAaQBu +AHoAZQBuAHQAbwBzACAAZABhACAARwBhAG0AbQBhACAAMgAsADIAQQBsAGcAZQBtAGUAZQBuACAAZwBy +AGkAagBzACAAZwBhAG0AbQBhACAAMgAsADIALQBwAHIAbwBmAGkAZQBsAFAAZQByAGYAaQBsACAAZwBl +AG4A6QByAGkAYwBvACAAZABlACAAZwBhAG0AbQBhACAAZABlACAAZwByAGkAcwBlAHMAIAAyACwAMg4j +DjEOBw4qDjUOQQ4BDiEOIQ4yDkAOAQ4jDiIOTA4XDjEOSA4nDkQOGwAgADIALgAyAEcAZQBuAGUAbAAg +AEcAcgBpACAARwBhAG0AYQAgADIALAAyAFkAbABlAGkAbgBlAG4AIABoAGEAcgBtAGEAYQBuACAAZwBh +AG0AbQBhACAAMgAsADIAIAAtAHAAcgBvAGYAaQBpAGwAaQBHAGUAbgBlAHIAaQENAGsAaQAgAEcAcgBh +AHkAIABHAGEAbQBtAGEAIAAyAC4AMgAgAHAAcgBvAGYAaQBsAFUAbgBpAHcAZQByAHMAYQBsAG4AeQAg +AHAAcgBvAGYAaQBsACAAcwB6AGEAcgBvAVsAYwBpACAAZwBhAG0AbQBhACAAMgAsADIGOgYnBkUGJwAg +ADIALgAyACAGRAZIBkYAIAYxBkUGJwYvBkoAIAY5BicGRQQeBDEESQQwBE8AIARBBDUEQAQwBE8AIAQz +BDAEPAQ8BDAAIAAyACwAMgAtBD8EQAQ+BEQEOAQ7BEwARwBlAG4AZQByAGkAYwAgAEcAcgBhAHkAIABH +AGEAbQBtAGEAIAAyAC4AMgAgAFAAcgBvAGYAaQBsAGUAAHRleHQAAAAAQ29weXJpZ2h0IEFwcGxlIElu +Yy4sIDIwMTIAAFhZWiAAAAAAAADzUQABAAAAARbMY3VydgAAAAAAAAQAAAAABQAKAA8AFAAZAB4AIwAo +AC0AMgA3ADsAQABFAEoATwBUAFkAXgBjAGgAbQByAHcAfACBAIYAiwCQAJUAmgCfAKQAqQCuALIAtwC8 +AMEAxgDLANAA1QDbAOAA5QDrAPAA9gD7AQEBBwENARMBGQEfASUBKwEyATgBPgFFAUwBUgFZAWABZwFu +AXUBfAGDAYsBkgGaAaEBqQGxAbkBwQHJAdEB2QHhAekB8gH6AgMCDAIUAh0CJgIvAjgCQQJLAlQCXQJn +AnECegKEAo4CmAKiAqwCtgLBAssC1QLgAusC9QMAAwsDFgMhAy0DOANDA08DWgNmA3IDfgOKA5YDogOu +A7oDxwPTA+AD7AP5BAYEEwQgBC0EOwRIBFUEYwRxBH4EjASaBKgEtgTEBNME4QTwBP4FDQUcBSsFOgVJ +BVgFZwV3BYYFlgWmBbUFxQXVBeUF9gYGBhYGJwY3BkgGWQZqBnsGjAadBq8GwAbRBuMG9QcHBxkHKwc9 +B08HYQd0B4YHmQesB78H0gflB/gICwgfCDIIRghaCG4IggiWCKoIvgjSCOcI+wkQCSUJOglPCWQJeQmP +CaQJugnPCeUJ+woRCicKPQpUCmoKgQqYCq4KxQrcCvMLCwsiCzkLUQtpC4ALmAuwC8gL4Qv5DBIMKgxD +DFwMdQyODKcMwAzZDPMNDQ0mDUANWg10DY4NqQ3DDd4N+A4TDi4OSQ5kDn8Omw62DtIO7g8JDyUPQQ9e +D3oPlg+zD88P7BAJECYQQxBhEH4QmxC5ENcQ9RETETERTxFtEYwRqhHJEegSBxImEkUSZBKEEqMSwxLj +EwMTIxNDE2MTgxOkE8UT5RQGFCcUSRRqFIsUrRTOFPAVEhU0FVYVeBWbFb0V4BYDFiYWSRZsFo8WshbW +FvoXHRdBF2UXiReuF9IX9xgbGEAYZRiKGK8Y1Rj6GSAZRRlrGZEZtxndGgQaKhpRGncanhrFGuwbFBs7 +G2MbihuyG9ocAhwqHFIcexyjHMwc9R0eHUcdcB2ZHcMd7B4WHkAeah6UHr4e6R8THz4faR+UH78f6iAV +IEEgbCCYIMQg8CEcIUghdSGhIc4h+yInIlUigiKvIt0jCiM4I2YjlCPCI/AkHyRNJHwkqyTaJQklOCVo +JZclxyX3JicmVyaHJrcm6CcYJ0kneierJ9woDSg/KHEooijUKQYpOClrKZ0p0CoCKjUqaCqbKs8rAis2 +K2krnSvRLAUsOSxuLKIs1y0MLUEtdi2rLeEuFi5MLoIuty7uLyQvWi+RL8cv/jA1MGwwpDDbMRIxSjGC +Mbox8jIqMmMymzLUMw0zRjN/M7gz8TQrNGU0njTYNRM1TTWHNcI1/TY3NnI2rjbpNyQ3YDecN9c4FDhQ +OIw4yDkFOUI5fzm8Ofk6Njp0OrI67zstO2s7qjvoPCc8ZTykPOM9Ij1hPaE94D4gPmA+oD7gPyE/YT+i +P+JAI0BkQKZA50EpQWpBrEHuQjBCckK1QvdDOkN9Q8BEA0RHRIpEzkUSRVVFmkXeRiJGZ0arRvBHNUd7 +R8BIBUhLSJFI10kdSWNJqUnwSjdKfUrESwxLU0uaS+JMKkxyTLpNAk1KTZNN3E4lTm5Ot08AT0lPk0/d +UCdQcVC7UQZRUFGbUeZSMVJ8UsdTE1NfU6pT9lRCVI9U21UoVXVVwlYPVlxWqVb3V0RXklfgWC9YfVjL +WRpZaVm4WgdaVlqmWvVbRVuVW+VcNVyGXNZdJ114XcleGl5sXr1fD19hX7NgBWBXYKpg/GFPYaJh9WJJ +Ypxi8GNDY5dj62RAZJRk6WU9ZZJl52Y9ZpJm6Gc9Z5Nn6Wg/aJZo7GlDaZpp8WpIap9q92tPa6dr/2xX +bK9tCG1gbbluEm5rbsRvHm94b9FwK3CGcOBxOnGVcfByS3KmcwFzXXO4dBR0cHTMdSh1hXXhdj52m3b4 +d1Z3s3gReG54zHkqeYl553pGeqV7BHtje8J8IXyBfOF9QX2hfgF+Yn7CfyN/hH/lgEeAqIEKgWuBzYIw +gpKC9INXg7qEHYSAhOOFR4Wrhg6GcobXhzuHn4gEiGmIzokziZmJ/opkisqLMIuWi/yMY4zKjTGNmI3/ +jmaOzo82j56QBpBukNaRP5GokhGSepLjk02TtpQglIqU9JVflcmWNJaflwqXdZfgmEyYuJkkmZCZ/Jpo +mtWbQpuvnByciZz3nWSd0p5Anq6fHZ+Ln/qgaaDYoUehtqImopajBqN2o+akVqTHpTilqaYapoum/adu +p+CoUqjEqTepqaocqo+rAqt1q+msXKzQrUStuK4trqGvFq+LsACwdbDqsWCx1rJLssKzOLOutCW0nLUT +tYq2AbZ5tvC3aLfguFm40blKucK6O7q1uy67p7whvJu9Fb2Pvgq+hL7/v3q/9cBwwOzBZ8Hjwl/C28NY +w9TEUcTOxUvFyMZGxsPHQce/yD3IvMk6ybnKOMq3yzbLtsw1zLXNNc21zjbOts83z7jQOdC60TzRvtI/ +0sHTRNPG1EnUy9VO1dHWVdbY11zX4Nhk2OjZbNnx2nba+9uA3AXcit0Q3ZbeHN6i3ynfr+A24L3hROHM +4lPi2+Nj4+vkc+T85YTmDeaW5x/nqegy6LzpRunQ6lvq5etw6/vshu0R7ZzuKO6070DvzPBY8OXxcvH/ +8ozzGfOn9DT0wvVQ9d72bfb794r4Gfio+Tj5x/pX+uf7d/wH/Jj9Kf26/kv+3P9t///SLS4vMFokY2xh +c3NuYW1lWCRjbGFzc2VzXxAQTlNCaXRtYXBJbWFnZVJlcKMvMTJaTlNJbWFnZVJlcFhOU09iamVjdNIt +LjQ1V05TQXJyYXmiNDLSLS43OF5OU011dGFibGVBcnJheaM3NDLTOjsOPD0+V05TV2hpdGVcTlNDb2xv +clNwYWNlRDAgMAAQA4AM0i0uQEFXTlNDb2xvcqJAMtItLkNEV05TSW1hZ2WiQzIACAARABoAJAApADIA +NwBJAEwAUQBTAGIAaAB3AH4AhQCSAJkApQCtALwAvgDAAMUAxwDJAMsAzQDUANkA5ADmAOgA6gDvAPIA +9AD2APgA/wEWATIBNAE2E6YTqxO2E78T0hPWE+ET6hPvE/cT+hP/FA4UEhQZFCEULhQzFDUUNxQ8FEQU +RxRMFFQAAAAAAAACAQAAAAAAAABFAAAAAAAAAAAAAAAAAAAUVw diff --git a/i18n/src/main/resources/ro.lproj/Command.xib b/i18n/src/main/resources/ro.lproj/Command.xib index 94472129d6..ef7de192e8 100644 --- a/i18n/src/main/resources/ro.lproj/Command.xib +++ b/i18n/src/main/resources/ro.lproj/Command.xib @@ -1,7 +1,7 @@ - + - + diff --git a/i18n/src/main/resources/ro.lproj/Connection.xib b/i18n/src/main/resources/ro.lproj/Connection.xib index cb4b2a6176..bd9662872f 100644 --- a/i18n/src/main/resources/ro.lproj/Connection.xib +++ b/i18n/src/main/resources/ro.lproj/Connection.xib @@ -1,7 +1,7 @@ - + - + @@ -76,7 +76,7 @@ - + diff --git a/i18n/src/main/resources/ro.lproj/Info.xib b/i18n/src/main/resources/ro.lproj/Info.xib index 9a91c4b544..bfcf5e6017 100644 --- a/i18n/src/main/resources/ro.lproj/Info.xib +++ b/i18n/src/main/resources/ro.lproj/Info.xib @@ -1,7 +1,7 @@ - + - + @@ -89,7 +89,7 @@ - + @@ -1017,21 +1017,21 @@ - + - + - + @@ -1121,21 +1121,21 @@ - + - + - + @@ -1250,27 +1250,27 @@ diff --git a/i18n/src/main/resources/ro.lproj/Login.strings b/i18n/src/main/resources/ro.lproj/Login.strings index 339db8960a..0e42b2a85a 100644 Binary files a/i18n/src/main/resources/ro.lproj/Login.strings and b/i18n/src/main/resources/ro.lproj/Login.strings differ diff --git a/i18n/src/main/resources/ro.lproj/Main.xib b/i18n/src/main/resources/ro.lproj/Main.xib index 0bd7691b49..b8d94b49ae 100644 --- a/i18n/src/main/resources/ro.lproj/Main.xib +++ b/i18n/src/main/resources/ro.lproj/Main.xib @@ -1,7 +1,7 @@ - + - + @@ -159,19 +159,19 @@ - + - + - + @@ -321,12 +321,12 @@ CA - + - + @@ -566,6 +566,10 @@ CA - + + + + + diff --git a/i18n/src/main/resources/ro.lproj/Preferences.xib b/i18n/src/main/resources/ro.lproj/Preferences.xib index 5cc9c3f6ba..a1285c0afb 100644 --- a/i18n/src/main/resources/ro.lproj/Preferences.xib +++ b/i18n/src/main/resources/ro.lproj/Preferences.xib @@ -1,7 +1,7 @@ - + - + @@ -266,7 +266,7 @@ - + @@ -307,7 +307,7 @@ - + @@ -538,7 +538,7 @@ - + @@ -578,7 +578,7 @@ - + @@ -613,7 +613,7 @@ - + @@ -642,7 +642,7 @@ - + @@ -1218,7 +1218,7 @@ - + @@ -1250,7 +1250,7 @@ - + @@ -1290,7 +1290,7 @@ - + @@ -1363,7 +1363,7 @@ - + @@ -1558,7 +1558,7 @@ - + @@ -1618,7 +1618,7 @@ - + @@ -1643,7 +1643,7 @@ - + @@ -1685,7 +1685,7 @@ - + @@ -1713,7 +1713,7 @@ - + @@ -1741,7 +1741,7 @@ - + @@ -1770,7 +1770,7 @@ - + @@ -1805,7 +1805,7 @@ - + @@ -1888,7 +1888,7 @@ - + @@ -1947,7 +1947,7 @@ - + @@ -1975,7 +1975,7 @@ - + @@ -2004,7 +2004,7 @@ - + diff --git a/i18n/src/main/resources/ro.lproj/Profiles.xib b/i18n/src/main/resources/ro.lproj/Profiles.xib index 18af5a58f4..20e5ce0f36 100644 --- a/i18n/src/main/resources/ro.lproj/Profiles.xib +++ b/i18n/src/main/resources/ro.lproj/Profiles.xib @@ -1,7 +1,7 @@ - + - + diff --git a/i18n/src/main/resources/ro.lproj/Prompt.xib b/i18n/src/main/resources/ro.lproj/Prompt.xib index 968be8d5d2..50cafe24cf 100644 --- a/i18n/src/main/resources/ro.lproj/Prompt.xib +++ b/i18n/src/main/resources/ro.lproj/Prompt.xib @@ -1,7 +1,7 @@ - + - + @@ -95,7 +95,7 @@ Gw - + diff --git a/i18n/src/main/resources/ro.lproj/S3.strings b/i18n/src/main/resources/ro.lproj/S3.strings index 3e2dad1440..027458d092 100644 Binary files a/i18n/src/main/resources/ro.lproj/S3.strings and b/i18n/src/main/resources/ro.lproj/S3.strings differ diff --git a/i18n/src/main/resources/ro.lproj/Updater.strings b/i18n/src/main/resources/ro.lproj/Updater.strings index 095c3d043f..d79d4833e1 100644 Binary files a/i18n/src/main/resources/ro.lproj/Updater.strings and b/i18n/src/main/resources/ro.lproj/Updater.strings differ diff --git a/i18n/src/main/resources/ru.lproj/Bookmark.xib b/i18n/src/main/resources/ru.lproj/Bookmark.xib index ce1951cf33..0b36fd1e93 100644 --- a/i18n/src/main/resources/ru.lproj/Bookmark.xib +++ b/i18n/src/main/resources/ru.lproj/Bookmark.xib @@ -1,7 +1,7 @@ - + - + @@ -92,7 +92,7 @@ - + @@ -489,95 +489,95 @@ YnBsaXN0MDDUAQIDBAUGBwpYJHZlcnNpb25ZJGFyY2hpdmVyVCR0b3BYJG9iamVjdHMSAAGGoF8QD05T -S2V5ZWRBcmNoaXZlctEICVRyb290gAGuCwwZGh8UJCkqMTQ3PUBVJG51bGzWDQ4PEBESExQVFhcYVk5T -U2l6ZV5OU1Jlc2l6aW5nTW9kZVYkY2xhc3NcTlNJbWFnZUZsYWdzVk5TUmVwc1dOU0NvbG9ygAIQAIAN -EiDDAACAA4ALVnsxLCAxfdIbDxweWk5TLm9iamVjdHOhHYAEgArSGw8gI6IhIoAFgAaACdMPJSYnKBRf -EBROU1RJRkZSZXByZXNlbnRhdGlvbl8QGU5TSW50ZXJuYWxMYXlvdXREaXJlY3Rpb26ACIAHTxESbE1N -ACoAAAAKAAAAEAEAAAMAAAABAAEAAAEBAAMAAAABAAEAAAECAAMAAAACAAgACAEDAAMAAAABAAEAAAEG -AAMAAAABAAEAAAEKAAMAAAABAAEAAAERAAQAAAABAAAACAESAAMAAAABAAEAAAEVAAMAAAABAAIAAAEW -AAMAAAABAAEAAAEXAAQAAAABAAAAAgEcAAMAAAABAAEAAAEoAAMAAAABAAIAAAFSAAMAAAABAAEAAAFT -AAMAAAACAAEAAYdzAAcAABGcAAAA0AAAAAAAABGcYXBwbAIAAABtbnRyR1JBWVhZWiAH3AAIABcADwAu -AA9hY3NwQVBQTAAAAABub25lAAAAAAAAAAAAAAAAAAAAAAAA9tYAAQAAAADTLWFwcGwAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAVkZXNjAAAAwAAAAHlkc2NtAAABPAAA -CBpjcHJ0AAAJWAAAACN3dHB0AAAJfAAAABRrVFJDAAAJkAAACAxkZXNjAAAAAAAAAB9HZW5lcmljIEdy -YXkgR2FtbWEgMi4yIFByb2ZpbGUAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAbWx1YwAAAAAAAAAfAAAADHNr -U0sAAAAuAAABhGRhREsAAAA6AAABsmNhRVMAAAA4AAAB7HZpVk4AAABAAAACJHB0QlIAAABKAAACZHVr -VUEAAAAsAAACrmZyRlUAAAA+AAAC2mh1SFUAAAA0AAADGHpoVFcAAAAaAAADTGtvS1IAAAAiAAADZm5i -Tk8AAAA6AAADiGNzQ1oAAAAoAAADwmhlSUwAAAAkAAAD6nJvUk8AAAAqAAAEDmRlREUAAABOAAAEOGl0 -SVQAAABOAAAEhnN2U0UAAAA4AAAE1HpoQ04AAAAaAAAFDGphSlAAAAAmAAAFJmVsR1IAAAAqAAAFTHB0 -UE8AAABSAAAFdm5sTkwAAABAAAAFyGVzRVMAAABMAAAGCHRoVEgAAAAyAAAGVHRyVFIAAAAkAAAGhmZp -RkkAAABGAAAGqmhySFIAAAA+AAAG8HBsUEwAAABKAAAHLmFyRUcAAAAsAAAHeHJ1UlUAAAA6AAAHpGVu -VVMAAAA8AAAH3gBWAWEAZQBvAGIAZQBjAG4A4QAgAHMAaQB2AOEAIABnAGEAbQBhACAAMgAsADIARwBl -AG4AZQByAGkAcwBrACAAZwByAOUAIAAyACwAMgAgAGcAYQBtAG0AYQAtAHAAcgBvAGYAaQBsAEcAYQBt -AG0AYQAgAGQAZQAgAGcAcgBpAHMAbwBzACAAZwBlAG4A6AByAGkAYwBhACAAMgAuADIAQx6lAHUAIABo -AOwAbgBoACAATQDgAHUAIAB4AOEAbQAgAEMAaAB1AG4AZwAgAEcAYQBtAG0AYQAgADIALgAyAFAAZQBy -AGYAaQBsACAARwBlAG4A6QByAGkAYwBvACAAZABhACAARwBhAG0AYQAgAGQAZQAgAEMAaQBuAHoAYQBz -ACAAMgAsADIEFwQwBDMEMAQ7BEwEPQQwACAARwByAGEAeQAtBDMEMAQ8BDAAIAAyAC4AMgBQAHIAbwBm -AGkAbAAgAGcA6QBuAOkAcgBpAHEAdQBlACAAZwByAGkAcwAgAGcAYQBtAG0AYQAgADIALAAyAMEAbAB0 -AGEAbADhAG4AbwBzACAAcwB6APwAcgBrAGUAIABnAGEAbQBtAGEAIAAyAC4AMpAadShwcJaOUUlepgAy -AC4AMoJyX2ljz4/wx3y8GAAg1ozAyQAgrBC5yAAgADIALgAyACDVBLhc0wzHfABHAGUAbgBlAHIAaQBz -AGsAIABnAHIA5QAgAGcAYQBtAG0AYQAgADIALAAyAC0AcAByAG8AZgBpAGwATwBiAGUAYwBuAOEAIAFh -AGUAZADhACAAZwBhAG0AYQAgADIALgAyBdIF0AXeBdQAIAXQBeQF1QXoACAF2wXcBdwF2QAgADIALgAy -AEcAYQBtAGEAIABnAHIAaQAgAGcAZQBuAGUAcgBpAGMBAwAgADIALAAyAEEAbABsAGcAZQBtAGUAaQBu -AGUAcwAgAEcAcgBhAHUAcwB0AHUAZgBlAG4ALQBQAHIAbwBmAGkAbAAgAEcAYQBtAG0AYQAgADIALAAy -AFAAcgBvAGYAaQBsAG8AIABnAHIAaQBnAGkAbwAgAGcAZQBuAGUAcgBpAGMAbwAgAGQAZQBsAGwAYQAg -AGcAYQBtAG0AYQAgADIALAAyAEcAZQBuAGUAcgBpAHMAawAgAGcAcgDlACAAMgAsADIAIABnAGEAbQBt -AGEAcAByAG8AZgBpAGxmbpAacHBepnz7ZXAAMgAuADJjz4/wZYdO9k4AgiwwsDDsMKQwrDDzMN4AIAAy -AC4AMgAgMNcw7TDVMKEwpDDrA5MDtQO9A7kDugPMACADkwO6A8EDuQAgA5MDrAO8A7wDsQAgADIALgAy -AFAAZQByAGYAaQBsACAAZwBlAG4A6QByAGkAYwBvACAAZABlACAAYwBpAG4AegBlAG4AdABvAHMAIABk -AGEAIABHAGEAbQBtAGEAIAAyACwAMgBBAGwAZwBlAG0AZQBlAG4AIABnAHIAaQBqAHMAIABnAGEAbQBt -AGEAIAAyACwAMgAtAHAAcgBvAGYAaQBlAGwAUABlAHIAZgBpAGwAIABnAGUAbgDpAHIAaQBjAG8AIABk -AGUAIABnAGEAbQBtAGEAIABkAGUAIABnAHIAaQBzAGUAcwAgADIALAAyDiMOMQ4HDioONQ5BDgEOIQ4h -DjIOQA4BDiMOIg5MDhcOMQ5IDicORA4bACAAMgAuADIARwBlAG4AZQBsACAARwByAGkAIABHAGEAbQBh -ACAAMgAsADIAWQBsAGUAaQBuAGUAbgAgAGgAYQByAG0AYQBhAG4AIABnAGEAbQBtAGEAIAAyACwAMgAg -AC0AcAByAG8AZgBpAGkAbABpAEcAZQBuAGUAcgBpAQ0AawBpACAARwByAGEAeQAgAEcAYQBtAG0AYQAg -ADIALgAyACAAcAByAG8AZgBpAGwAVQBuAGkAdwBlAHIAcwBhAGwAbgB5ACAAcAByAG8AZgBpAGwAIABz -AHoAYQByAG8BWwBjAGkAIABnAGEAbQBtAGEAIAAyACwAMgY6BicGRQYnACAAMgAuADIAIAZEBkgGRgAg -BjEGRQYnBi8GSgAgBjkGJwZFBB4EMQRJBDAETwAgBEEENQRABDAETwAgBDMEMAQ8BDwEMAAgADIALAAy -AC0EPwRABD4ERAQ4BDsETABHAGUAbgBlAHIAaQBjACAARwByAGEAeQAgAEcAYQBtAG0AYQAgADIALgAy -ACAAUAByAG8AZgBpAGwAZQAAdGV4dAAAAABDb3B5cmlnaHQgQXBwbGUgSW5jLiwgMjAxMgAAWFlaIAAA -AAAAAPNRAAEAAAABFsxjdXJ2AAAAAAAABAAAAAAFAAoADwAUABkAHgAjACgALQAyADcAOwBAAEUASgBP -AFQAWQBeAGMAaABtAHIAdwB8AIEAhgCLAJAAlQCaAJ8ApACpAK4AsgC3ALwAwQDGAMsA0ADVANsA4ADl -AOsA8AD2APsBAQEHAQ0BEwEZAR8BJQErATIBOAE+AUUBTAFSAVkBYAFnAW4BdQF8AYMBiwGSAZoBoQGp -AbEBuQHBAckB0QHZAeEB6QHyAfoCAwIMAhQCHQImAi8COAJBAksCVAJdAmcCcQJ6AoQCjgKYAqICrAK2 -AsECywLVAuAC6wL1AwADCwMWAyEDLQM4A0MDTwNaA2YDcgN+A4oDlgOiA64DugPHA9MD4APsA/kEBgQT -BCAELQQ7BEgEVQRjBHEEfgSMBJoEqAS2BMQE0wThBPAE/gUNBRwFKwU6BUkFWAVnBXcFhgWWBaYFtQXF -BdUF5QX2BgYGFgYnBjcGSAZZBmoGewaMBp0GrwbABtEG4wb1BwcHGQcrBz0HTwdhB3QHhgeZB6wHvwfS -B+UH+AgLCB8IMghGCFoIbgiCCJYIqgi+CNII5wj7CRAJJQk6CU8JZAl5CY8JpAm6Cc8J5Qn7ChEKJwo9 -ClQKagqBCpgKrgrFCtwK8wsLCyILOQtRC2kLgAuYC7ALyAvhC/kMEgwqDEMMXAx1DI4MpwzADNkM8w0N -DSYNQA1aDXQNjg2pDcMN3g34DhMOLg5JDmQOfw6bDrYO0g7uDwkPJQ9BD14Peg+WD7MPzw/sEAkQJhBD -EGEQfhCbELkQ1xD1ERMRMRFPEW0RjBGqEckR6BIHEiYSRRJkEoQSoxLDEuMTAxMjE0MTYxODE6QTxRPl -FAYUJxRJFGoUixStFM4U8BUSFTQVVhV4FZsVvRXgFgMWJhZJFmwWjxayFtYW+hcdF0EXZReJF64X0hf3 -GBsYQBhlGIoYrxjVGPoZIBlFGWsZkRm3Gd0aBBoqGlEadxqeGsUa7BsUGzsbYxuKG7Ib2hwCHCocUhx7 -HKMczBz1HR4dRx1wHZkdwx3sHhYeQB5qHpQevh7pHxMfPh9pH5Qfvx/qIBUgQSBsIJggxCDwIRwhSCF1 -IaEhziH7IiciVSKCIq8i3SMKIzgjZiOUI8Ij8CQfJE0kfCSrJNolCSU4JWgllyXHJfcmJyZXJocmtybo -JxgnSSd6J6sn3CgNKD8ocSiiKNQpBik4KWspnSnQKgIqNSpoKpsqzysCKzYraSudK9EsBSw5LG4soizX -LQwtQS12Last4S4WLkwugi63Lu4vJC9aL5Evxy/+MDUwbDCkMNsxEjFKMYIxujHyMioyYzKbMtQzDTNG -M38zuDPxNCs0ZTSeNNg1EzVNNYc1wjX9Njc2cjauNuk3JDdgN5w31zgUOFA4jDjIOQU5Qjl/Obw5+To2 -OnQ6sjrvOy07azuqO+g8JzxlPKQ84z0iPWE9oT3gPiA+YD6gPuA/IT9hP6I/4kAjQGRApkDnQSlBakGs -Qe5CMEJyQrVC90M6Q31DwEQDREdEikTORRJFVUWaRd5GIkZnRqtG8Ec1R3tHwEgFSEtIkUjXSR1JY0mp -SfBKN0p9SsRLDEtTS5pL4kwqTHJMuk0CTUpNk03cTiVObk63TwBPSU+TT91QJ1BxULtRBlFQUZtR5lIx -UnxSx1MTU19TqlP2VEJUj1TbVShVdVXCVg9WXFapVvdXRFeSV+BYL1h9WMtZGllpWbhaB1pWWqZa9VtF -W5Vb5Vw1XIZc1l0nXXhdyV4aXmxevV8PX2Ffs2AFYFdgqmD8YU9homH1YklinGLwY0Njl2PrZEBklGTp -ZT1lkmXnZj1mkmboZz1nk2fpaD9olmjsaUNpmmnxakhqn2r3a09rp2v/bFdsr20IbWBtuW4SbmtuxG8e -b3hv0XArcIZw4HE6cZVx8HJLcqZzAXNdc7h0FHRwdMx1KHWFdeF2Pnabdvh3VnezeBF4bnjMeSp5iXnn -ekZ6pXsEe2N7wnwhfIF84X1BfaF+AX5ifsJ/I3+Ef+WAR4CogQqBa4HNgjCCkoL0g1eDuoQdhICE44VH -hauGDoZyhteHO4efiASIaYjOiTOJmYn+imSKyoswi5aL/IxjjMqNMY2Yjf+OZo7OjzaPnpAGkG6Q1pE/ -kaiSEZJ6kuOTTZO2lCCUipT0lV+VyZY0lp+XCpd1l+CYTJi4mSSZkJn8mmia1ZtCm6+cHJyJnPedZJ3S -nkCerp8dn4uf+qBpoNihR6G2oiailqMGo3aj5qRWpMelOKWpphqmi6b9p26n4KhSqMSpN6mpqhyqj6sC -q3Wr6axcrNCtRK24ri2uoa8Wr4uwALB1sOqxYLHWskuywrM4s660JbSctRO1irYBtnm28Ldot+C4WbjR -uUq5wro7urW7LrunvCG8m70VvY++Cr6Evv+/er/1wHDA7MFnwePCX8Lbw1jD1MRRxM7FS8XIxkbGw8dB -x7/IPci8yTrJuco4yrfLNsu2zDXMtc01zbXONs62zzfPuNA50LrRPNG+0j/SwdNE08bUSdTL1U7V0dZV -1tjXXNfg2GTY6Nls2fHadtr724DcBdyK3RDdlt4c3qLfKd+v4DbgveFE4cziU+Lb42Pj6+Rz5PzlhOYN -5pbnH+ep6DLovOlG6dDqW+rl63Dr++yG7RHtnO4o7rTvQO/M8Fjw5fFy8f/yjPMZ86f0NPTC9VD13vZt -9vv3ivgZ+Kj5OPnH+lf65/t3/Af8mP0p/br+S/7c/23//9IrLC0uWiRjbGFzc25hbWVYJGNsYXNzZXNf -EBBOU0JpdG1hcEltYWdlUmVwoy0vMFpOU0ltYWdlUmVwWE5TT2JqZWN00issMjNXTlNBcnJheaIyMNIr -LDU2Xk5TTXV0YWJsZUFycmF5ozUyMNM4OQ86OzxXTlNXaGl0ZVxOU0NvbG9yU3BhY2VEMCAwABADgAzS -Kyw+P1dOU0NvbG9yoj4w0issQUJXTlNJbWFnZaJBMAAIABEAGgAkACkAMgA3AEkATABRAFMAYgBoAHUA -fACLAJIAnwCmAK4AsACyALQAuQC7AL0AxADJANQA1gDYANoA3wDiAOQA5gDoAO8BBgEiASQBJhOWE5sT -phOvE8ITxhPRE9oT3xPnE+oT7xP+FAIUCRQRFB4UIxQlFCcULBQ0FDcUPBREAAAAAAAAAgEAAAAAAAAA -QwAAAAAAAAAAAAAAAAAAFEc +S2V5ZWRBcmNoaXZlctEICVRyb290gAGuCwwbHCEaJissMzY5P0JVJG51bGzXDQ4PEBESExQVFhcYGRpW +TlNTaXplViRjbGFzc1xOU0ltYWdlRmxhZ3NWTlNSZXBzW05TVGludENvbG9yV05TQ29sb3JeTlNSZXNp +emluZ01vZGWAAoANEiDDAACAA4AAgAsQAFZ7MSwgMX3SHQ4eIFpOUy5vYmplY3RzoR+ABIAK0h0OIiWi +IySABYAGgAnTDicoKSoaXxAUTlNUSUZGUmVwcmVzZW50YXRpb25fEBlOU0ludGVybmFsTGF5b3V0RGly +ZWN0aW9ugAiAB08REmxNTQAqAAAACgAAABABAAADAAAAAQABAAABAQADAAAAAQABAAABAgADAAAAAgAI +AAgBAwADAAAAAQABAAABBgADAAAAAQABAAABCgADAAAAAQABAAABEQAEAAAAAQAAAAgBEgADAAAAAQAB +AAABFQADAAAAAQACAAABFgADAAAAAQABAAABFwAEAAAAAQAAAAIBHAADAAAAAQABAAABKAADAAAAAQAC +AAABUgADAAAAAQABAAABUwADAAAAAgABAAGHcwAHAAARnAAAANAAAAAAAAARnGFwcGwCAAAAbW50ckdS +QVlYWVogB9wACAAXAA8ALgAPYWNzcEFQUEwAAAAAbm9uZQAAAAAAAAAAAAAAAAAAAAAAAPbWAAEAAAAA +0y1hcHBsAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFZGVzYwAA +AMAAAAB5ZHNjbQAAATwAAAgaY3BydAAACVgAAAAjd3RwdAAACXwAAAAUa1RSQwAACZAAAAgMZGVzYwAA +AAAAAAAfR2VuZXJpYyBHcmF5IEdhbW1hIDIuMiBQcm9maWxlAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAG1s +dWMAAAAAAAAAHwAAAAxza1NLAAAALgAAAYRkYURLAAAAOgAAAbJjYUVTAAAAOAAAAex2aVZOAAAAQAAA +AiRwdEJSAAAASgAAAmR1a1VBAAAALAAAAq5mckZVAAAAPgAAAtpodUhVAAAANAAAAxh6aFRXAAAAGgAA +A0xrb0tSAAAAIgAAA2ZuYk5PAAAAOgAAA4hjc0NaAAAAKAAAA8JoZUlMAAAAJAAAA+pyb1JPAAAAKgAA +BA5kZURFAAAATgAABDhpdElUAAAATgAABIZzdlNFAAAAOAAABNR6aENOAAAAGgAABQxqYUpQAAAAJgAA +BSZlbEdSAAAAKgAABUxwdFBPAAAAUgAABXZubE5MAAAAQAAABchlc0VTAAAATAAABgh0aFRIAAAAMgAA +BlR0clRSAAAAJAAABoZmaUZJAAAARgAABqpockhSAAAAPgAABvBwbFBMAAAASgAABy5hckVHAAAALAAA +B3hydVJVAAAAOgAAB6RlblVTAAAAPAAAB94AVgFhAGUAbwBiAGUAYwBuAOEAIABzAGkAdgDhACAAZwBh +AG0AYQAgADIALAAyAEcAZQBuAGUAcgBpAHMAawAgAGcAcgDlACAAMgAsADIAIABnAGEAbQBtAGEALQBw +AHIAbwBmAGkAbABHAGEAbQBtAGEAIABkAGUAIABnAHIAaQBzAG8AcwAgAGcAZQBuAOgAcgBpAGMAYQAg +ADIALgAyAEMepQB1ACAAaADsAG4AaAAgAE0A4AB1ACAAeADhAG0AIABDAGgAdQBuAGcAIABHAGEAbQBt +AGEAIAAyAC4AMgBQAGUAcgBmAGkAbAAgAEcAZQBuAOkAcgBpAGMAbwAgAGQAYQAgAEcAYQBtAGEAIABk +AGUAIABDAGkAbgB6AGEAcwAgADIALAAyBBcEMAQzBDAEOwRMBD0EMAAgAEcAcgBhAHkALQQzBDAEPAQw +ACAAMgAuADIAUAByAG8AZgBpAGwAIABnAOkAbgDpAHIAaQBxAHUAZQAgAGcAcgBpAHMAIABnAGEAbQBt +AGEAIAAyACwAMgDBAGwAdABhAGwA4QBuAG8AcwAgAHMAegD8AHIAawBlACAAZwBhAG0AbQBhACAAMgAu +ADKQGnUocHCWjlFJXqYAMgAuADKCcl9pY8+P8Md8vBgAINaMwMkAIKwQucgAIAAyAC4AMgAg1QS4XNMM +x3wARwBlAG4AZQByAGkAcwBrACAAZwByAOUAIABnAGEAbQBtAGEAIAAyACwAMgAtAHAAcgBvAGYAaQBs +AE8AYgBlAGMAbgDhACABYQBlAGQA4QAgAGcAYQBtAGEAIAAyAC4AMgXSBdAF3gXUACAF0AXkBdUF6AAg +BdsF3AXcBdkAIAAyAC4AMgBHAGEAbQBhACAAZwByAGkAIABnAGUAbgBlAHIAaQBjAQMAIAAyACwAMgBB +AGwAbABnAGUAbQBlAGkAbgBlAHMAIABHAHIAYQB1AHMAdAB1AGYAZQBuAC0AUAByAG8AZgBpAGwAIABH +AGEAbQBtAGEAIAAyACwAMgBQAHIAbwBmAGkAbABvACAAZwByAGkAZwBpAG8AIABnAGUAbgBlAHIAaQBj +AG8AIABkAGUAbABsAGEAIABnAGEAbQBtAGEAIAAyACwAMgBHAGUAbgBlAHIAaQBzAGsAIABnAHIA5QAg +ADIALAAyACAAZwBhAG0AbQBhAHAAcgBvAGYAaQBsZm6QGnBwXqZ8+2VwADIALgAyY8+P8GWHTvZOAIIs +MLAw7DCkMKww8zDeACAAMgAuADIAIDDXMO0w1TChMKQw6wOTA7UDvQO5A7oDzAAgA5MDugPBA7kAIAOT +A6wDvAO8A7EAIAAyAC4AMgBQAGUAcgBmAGkAbAAgAGcAZQBuAOkAcgBpAGMAbwAgAGQAZQAgAGMAaQBu +AHoAZQBuAHQAbwBzACAAZABhACAARwBhAG0AbQBhACAAMgAsADIAQQBsAGcAZQBtAGUAZQBuACAAZwBy +AGkAagBzACAAZwBhAG0AbQBhACAAMgAsADIALQBwAHIAbwBmAGkAZQBsAFAAZQByAGYAaQBsACAAZwBl +AG4A6QByAGkAYwBvACAAZABlACAAZwBhAG0AbQBhACAAZABlACAAZwByAGkAcwBlAHMAIAAyACwAMg4j +DjEOBw4qDjUOQQ4BDiEOIQ4yDkAOAQ4jDiIOTA4XDjEOSA4nDkQOGwAgADIALgAyAEcAZQBuAGUAbAAg +AEcAcgBpACAARwBhAG0AYQAgADIALAAyAFkAbABlAGkAbgBlAG4AIABoAGEAcgBtAGEAYQBuACAAZwBh +AG0AbQBhACAAMgAsADIAIAAtAHAAcgBvAGYAaQBpAGwAaQBHAGUAbgBlAHIAaQENAGsAaQAgAEcAcgBh +AHkAIABHAGEAbQBtAGEAIAAyAC4AMgAgAHAAcgBvAGYAaQBsAFUAbgBpAHcAZQByAHMAYQBsAG4AeQAg +AHAAcgBvAGYAaQBsACAAcwB6AGEAcgBvAVsAYwBpACAAZwBhAG0AbQBhACAAMgAsADIGOgYnBkUGJwAg +ADIALgAyACAGRAZIBkYAIAYxBkUGJwYvBkoAIAY5BicGRQQeBDEESQQwBE8AIARBBDUEQAQwBE8AIAQz +BDAEPAQ8BDAAIAAyACwAMgAtBD8EQAQ+BEQEOAQ7BEwARwBlAG4AZQByAGkAYwAgAEcAcgBhAHkAIABH +AGEAbQBtAGEAIAAyAC4AMgAgAFAAcgBvAGYAaQBsAGUAAHRleHQAAAAAQ29weXJpZ2h0IEFwcGxlIElu +Yy4sIDIwMTIAAFhZWiAAAAAAAADzUQABAAAAARbMY3VydgAAAAAAAAQAAAAABQAKAA8AFAAZAB4AIwAo +AC0AMgA3ADsAQABFAEoATwBUAFkAXgBjAGgAbQByAHcAfACBAIYAiwCQAJUAmgCfAKQAqQCuALIAtwC8 +AMEAxgDLANAA1QDbAOAA5QDrAPAA9gD7AQEBBwENARMBGQEfASUBKwEyATgBPgFFAUwBUgFZAWABZwFu +AXUBfAGDAYsBkgGaAaEBqQGxAbkBwQHJAdEB2QHhAekB8gH6AgMCDAIUAh0CJgIvAjgCQQJLAlQCXQJn +AnECegKEAo4CmAKiAqwCtgLBAssC1QLgAusC9QMAAwsDFgMhAy0DOANDA08DWgNmA3IDfgOKA5YDogOu +A7oDxwPTA+AD7AP5BAYEEwQgBC0EOwRIBFUEYwRxBH4EjASaBKgEtgTEBNME4QTwBP4FDQUcBSsFOgVJ +BVgFZwV3BYYFlgWmBbUFxQXVBeUF9gYGBhYGJwY3BkgGWQZqBnsGjAadBq8GwAbRBuMG9QcHBxkHKwc9 +B08HYQd0B4YHmQesB78H0gflB/gICwgfCDIIRghaCG4IggiWCKoIvgjSCOcI+wkQCSUJOglPCWQJeQmP +CaQJugnPCeUJ+woRCicKPQpUCmoKgQqYCq4KxQrcCvMLCwsiCzkLUQtpC4ALmAuwC8gL4Qv5DBIMKgxD +DFwMdQyODKcMwAzZDPMNDQ0mDUANWg10DY4NqQ3DDd4N+A4TDi4OSQ5kDn8Omw62DtIO7g8JDyUPQQ9e +D3oPlg+zD88P7BAJECYQQxBhEH4QmxC5ENcQ9RETETERTxFtEYwRqhHJEegSBxImEkUSZBKEEqMSwxLj +EwMTIxNDE2MTgxOkE8UT5RQGFCcUSRRqFIsUrRTOFPAVEhU0FVYVeBWbFb0V4BYDFiYWSRZsFo8WshbW +FvoXHRdBF2UXiReuF9IX9xgbGEAYZRiKGK8Y1Rj6GSAZRRlrGZEZtxndGgQaKhpRGncanhrFGuwbFBs7 +G2MbihuyG9ocAhwqHFIcexyjHMwc9R0eHUcdcB2ZHcMd7B4WHkAeah6UHr4e6R8THz4faR+UH78f6iAV +IEEgbCCYIMQg8CEcIUghdSGhIc4h+yInIlUigiKvIt0jCiM4I2YjlCPCI/AkHyRNJHwkqyTaJQklOCVo +JZclxyX3JicmVyaHJrcm6CcYJ0kneierJ9woDSg/KHEooijUKQYpOClrKZ0p0CoCKjUqaCqbKs8rAis2 +K2krnSvRLAUsOSxuLKIs1y0MLUEtdi2rLeEuFi5MLoIuty7uLyQvWi+RL8cv/jA1MGwwpDDbMRIxSjGC +Mbox8jIqMmMymzLUMw0zRjN/M7gz8TQrNGU0njTYNRM1TTWHNcI1/TY3NnI2rjbpNyQ3YDecN9c4FDhQ +OIw4yDkFOUI5fzm8Ofk6Njp0OrI67zstO2s7qjvoPCc8ZTykPOM9Ij1hPaE94D4gPmA+oD7gPyE/YT+i +P+JAI0BkQKZA50EpQWpBrEHuQjBCckK1QvdDOkN9Q8BEA0RHRIpEzkUSRVVFmkXeRiJGZ0arRvBHNUd7 +R8BIBUhLSJFI10kdSWNJqUnwSjdKfUrESwxLU0uaS+JMKkxyTLpNAk1KTZNN3E4lTm5Ot08AT0lPk0/d +UCdQcVC7UQZRUFGbUeZSMVJ8UsdTE1NfU6pT9lRCVI9U21UoVXVVwlYPVlxWqVb3V0RXklfgWC9YfVjL +WRpZaVm4WgdaVlqmWvVbRVuVW+VcNVyGXNZdJ114XcleGl5sXr1fD19hX7NgBWBXYKpg/GFPYaJh9WJJ +Ypxi8GNDY5dj62RAZJRk6WU9ZZJl52Y9ZpJm6Gc9Z5Nn6Wg/aJZo7GlDaZpp8WpIap9q92tPa6dr/2xX +bK9tCG1gbbluEm5rbsRvHm94b9FwK3CGcOBxOnGVcfByS3KmcwFzXXO4dBR0cHTMdSh1hXXhdj52m3b4 +d1Z3s3gReG54zHkqeYl553pGeqV7BHtje8J8IXyBfOF9QX2hfgF+Yn7CfyN/hH/lgEeAqIEKgWuBzYIw +gpKC9INXg7qEHYSAhOOFR4Wrhg6GcobXhzuHn4gEiGmIzokziZmJ/opkisqLMIuWi/yMY4zKjTGNmI3/ +jmaOzo82j56QBpBukNaRP5GokhGSepLjk02TtpQglIqU9JVflcmWNJaflwqXdZfgmEyYuJkkmZCZ/Jpo +mtWbQpuvnByciZz3nWSd0p5Anq6fHZ+Ln/qgaaDYoUehtqImopajBqN2o+akVqTHpTilqaYapoum/adu +p+CoUqjEqTepqaocqo+rAqt1q+msXKzQrUStuK4trqGvFq+LsACwdbDqsWCx1rJLssKzOLOutCW0nLUT +tYq2AbZ5tvC3aLfguFm40blKucK6O7q1uy67p7whvJu9Fb2Pvgq+hL7/v3q/9cBwwOzBZ8Hjwl/C28NY +w9TEUcTOxUvFyMZGxsPHQce/yD3IvMk6ybnKOMq3yzbLtsw1zLXNNc21zjbOts83z7jQOdC60TzRvtI/ +0sHTRNPG1EnUy9VO1dHWVdbY11zX4Nhk2OjZbNnx2nba+9uA3AXcit0Q3ZbeHN6i3ynfr+A24L3hROHM +4lPi2+Nj4+vkc+T85YTmDeaW5x/nqegy6LzpRunQ6lvq5etw6/vshu0R7ZzuKO6070DvzPBY8OXxcvH/ +8ozzGfOn9DT0wvVQ9d72bfb794r4Gfio+Tj5x/pX+uf7d/wH/Jj9Kf26/kv+3P9t///SLS4vMFokY2xh +c3NuYW1lWCRjbGFzc2VzXxAQTlNCaXRtYXBJbWFnZVJlcKMvMTJaTlNJbWFnZVJlcFhOU09iamVjdNIt +LjQ1V05TQXJyYXmiNDLSLS43OF5OU011dGFibGVBcnJheaM3NDLTOjsOPD0+V05TV2hpdGVcTlNDb2xv +clNwYWNlRDAgMAAQA4AM0i0uQEFXTlNDb2xvcqJAMtItLkNEV05TSW1hZ2WiQzIACAARABoAJAApADIA +NwBJAEwAUQBTAGIAaAB3AH4AhQCSAJkApQCtALwAvgDAAMUAxwDJAMsAzQDUANkA5ADmAOgA6gDvAPIA +9AD2APgA/wEWATIBNAE2E6YTqxO2E78T0hPWE+ET6hPvE/cT+hP/FA4UEhQZFCEULhQzFDUUNxQ8FEQU +RxRMFFQAAAAAAAACAQAAAAAAAABFAAAAAAAAAAAAAAAAAAAUVw diff --git a/i18n/src/main/resources/ru.lproj/Browser.xib b/i18n/src/main/resources/ru.lproj/Browser.xib index 0293215dc6..b8363f8905 100644 --- a/i18n/src/main/resources/ru.lproj/Browser.xib +++ b/i18n/src/main/resources/ru.lproj/Browser.xib @@ -1,7 +1,7 @@ - + - + @@ -38,7 +38,7 @@ - + @@ -62,27 +62,27 @@ @@ -256,7 +256,7 @@ - + @@ -553,95 +553,95 @@ DQ YnBsaXN0MDDUAQIDBAUGBwpYJHZlcnNpb25ZJGFyY2hpdmVyVCR0b3BYJG9iamVjdHMSAAGGoF8QD05T -S2V5ZWRBcmNoaXZlctEICVRyb290gAGuCwwZGh8UJCkqMTQ3PUBVJG51bGzWDQ4PEBESExQVFhcYVk5T -U2l6ZV5OU1Jlc2l6aW5nTW9kZVYkY2xhc3NcTlNJbWFnZUZsYWdzVk5TUmVwc1dOU0NvbG9ygAIQAIAN -EiDDAACAA4ALVnsxLCAxfdIbDxweWk5TLm9iamVjdHOhHYAEgArSGw8gI6IhIoAFgAaACdMPJSYnKBRf -EBROU1RJRkZSZXByZXNlbnRhdGlvbl8QGU5TSW50ZXJuYWxMYXlvdXREaXJlY3Rpb26ACIAHTxESbE1N -ACoAAAAKAAAAEAEAAAMAAAABAAEAAAEBAAMAAAABAAEAAAECAAMAAAACAAgACAEDAAMAAAABAAEAAAEG -AAMAAAABAAEAAAEKAAMAAAABAAEAAAERAAQAAAABAAAACAESAAMAAAABAAEAAAEVAAMAAAABAAIAAAEW -AAMAAAABAAEAAAEXAAQAAAABAAAAAgEcAAMAAAABAAEAAAEoAAMAAAABAAIAAAFSAAMAAAABAAEAAAFT -AAMAAAACAAEAAYdzAAcAABGcAAAA0AAAAAAAABGcYXBwbAIAAABtbnRyR1JBWVhZWiAH3AAIABcADwAu -AA9hY3NwQVBQTAAAAABub25lAAAAAAAAAAAAAAAAAAAAAAAA9tYAAQAAAADTLWFwcGwAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAVkZXNjAAAAwAAAAHlkc2NtAAABPAAA -CBpjcHJ0AAAJWAAAACN3dHB0AAAJfAAAABRrVFJDAAAJkAAACAxkZXNjAAAAAAAAAB9HZW5lcmljIEdy -YXkgR2FtbWEgMi4yIFByb2ZpbGUAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAbWx1YwAAAAAAAAAfAAAADHNr -U0sAAAAuAAABhGRhREsAAAA6AAABsmNhRVMAAAA4AAAB7HZpVk4AAABAAAACJHB0QlIAAABKAAACZHVr -VUEAAAAsAAACrmZyRlUAAAA+AAAC2mh1SFUAAAA0AAADGHpoVFcAAAAaAAADTGtvS1IAAAAiAAADZm5i -Tk8AAAA6AAADiGNzQ1oAAAAoAAADwmhlSUwAAAAkAAAD6nJvUk8AAAAqAAAEDmRlREUAAABOAAAEOGl0 -SVQAAABOAAAEhnN2U0UAAAA4AAAE1HpoQ04AAAAaAAAFDGphSlAAAAAmAAAFJmVsR1IAAAAqAAAFTHB0 -UE8AAABSAAAFdm5sTkwAAABAAAAFyGVzRVMAAABMAAAGCHRoVEgAAAAyAAAGVHRyVFIAAAAkAAAGhmZp -RkkAAABGAAAGqmhySFIAAAA+AAAG8HBsUEwAAABKAAAHLmFyRUcAAAAsAAAHeHJ1UlUAAAA6AAAHpGVu -VVMAAAA8AAAH3gBWAWEAZQBvAGIAZQBjAG4A4QAgAHMAaQB2AOEAIABnAGEAbQBhACAAMgAsADIARwBl -AG4AZQByAGkAcwBrACAAZwByAOUAIAAyACwAMgAgAGcAYQBtAG0AYQAtAHAAcgBvAGYAaQBsAEcAYQBt -AG0AYQAgAGQAZQAgAGcAcgBpAHMAbwBzACAAZwBlAG4A6AByAGkAYwBhACAAMgAuADIAQx6lAHUAIABo -AOwAbgBoACAATQDgAHUAIAB4AOEAbQAgAEMAaAB1AG4AZwAgAEcAYQBtAG0AYQAgADIALgAyAFAAZQBy -AGYAaQBsACAARwBlAG4A6QByAGkAYwBvACAAZABhACAARwBhAG0AYQAgAGQAZQAgAEMAaQBuAHoAYQBz -ACAAMgAsADIEFwQwBDMEMAQ7BEwEPQQwACAARwByAGEAeQAtBDMEMAQ8BDAAIAAyAC4AMgBQAHIAbwBm -AGkAbAAgAGcA6QBuAOkAcgBpAHEAdQBlACAAZwByAGkAcwAgAGcAYQBtAG0AYQAgADIALAAyAMEAbAB0 -AGEAbADhAG4AbwBzACAAcwB6APwAcgBrAGUAIABnAGEAbQBtAGEAIAAyAC4AMpAadShwcJaOUUlepgAy -AC4AMoJyX2ljz4/wx3y8GAAg1ozAyQAgrBC5yAAgADIALgAyACDVBLhc0wzHfABHAGUAbgBlAHIAaQBz -AGsAIABnAHIA5QAgAGcAYQBtAG0AYQAgADIALAAyAC0AcAByAG8AZgBpAGwATwBiAGUAYwBuAOEAIAFh -AGUAZADhACAAZwBhAG0AYQAgADIALgAyBdIF0AXeBdQAIAXQBeQF1QXoACAF2wXcBdwF2QAgADIALgAy -AEcAYQBtAGEAIABnAHIAaQAgAGcAZQBuAGUAcgBpAGMBAwAgADIALAAyAEEAbABsAGcAZQBtAGUAaQBu -AGUAcwAgAEcAcgBhAHUAcwB0AHUAZgBlAG4ALQBQAHIAbwBmAGkAbAAgAEcAYQBtAG0AYQAgADIALAAy -AFAAcgBvAGYAaQBsAG8AIABnAHIAaQBnAGkAbwAgAGcAZQBuAGUAcgBpAGMAbwAgAGQAZQBsAGwAYQAg -AGcAYQBtAG0AYQAgADIALAAyAEcAZQBuAGUAcgBpAHMAawAgAGcAcgDlACAAMgAsADIAIABnAGEAbQBt -AGEAcAByAG8AZgBpAGxmbpAacHBepnz7ZXAAMgAuADJjz4/wZYdO9k4AgiwwsDDsMKQwrDDzMN4AIAAy -AC4AMgAgMNcw7TDVMKEwpDDrA5MDtQO9A7kDugPMACADkwO6A8EDuQAgA5MDrAO8A7wDsQAgADIALgAy -AFAAZQByAGYAaQBsACAAZwBlAG4A6QByAGkAYwBvACAAZABlACAAYwBpAG4AegBlAG4AdABvAHMAIABk -AGEAIABHAGEAbQBtAGEAIAAyACwAMgBBAGwAZwBlAG0AZQBlAG4AIABnAHIAaQBqAHMAIABnAGEAbQBt -AGEAIAAyACwAMgAtAHAAcgBvAGYAaQBlAGwAUABlAHIAZgBpAGwAIABnAGUAbgDpAHIAaQBjAG8AIABk -AGUAIABnAGEAbQBtAGEAIABkAGUAIABnAHIAaQBzAGUAcwAgADIALAAyDiMOMQ4HDioONQ5BDgEOIQ4h -DjIOQA4BDiMOIg5MDhcOMQ5IDicORA4bACAAMgAuADIARwBlAG4AZQBsACAARwByAGkAIABHAGEAbQBh -ACAAMgAsADIAWQBsAGUAaQBuAGUAbgAgAGgAYQByAG0AYQBhAG4AIABnAGEAbQBtAGEAIAAyACwAMgAg -AC0AcAByAG8AZgBpAGkAbABpAEcAZQBuAGUAcgBpAQ0AawBpACAARwByAGEAeQAgAEcAYQBtAG0AYQAg -ADIALgAyACAAcAByAG8AZgBpAGwAVQBuAGkAdwBlAHIAcwBhAGwAbgB5ACAAcAByAG8AZgBpAGwAIABz -AHoAYQByAG8BWwBjAGkAIABnAGEAbQBtAGEAIAAyACwAMgY6BicGRQYnACAAMgAuADIAIAZEBkgGRgAg -BjEGRQYnBi8GSgAgBjkGJwZFBB4EMQRJBDAETwAgBEEENQRABDAETwAgBDMEMAQ8BDwEMAAgADIALAAy -AC0EPwRABD4ERAQ4BDsETABHAGUAbgBlAHIAaQBjACAARwByAGEAeQAgAEcAYQBtAG0AYQAgADIALgAy -ACAAUAByAG8AZgBpAGwAZQAAdGV4dAAAAABDb3B5cmlnaHQgQXBwbGUgSW5jLiwgMjAxMgAAWFlaIAAA -AAAAAPNRAAEAAAABFsxjdXJ2AAAAAAAABAAAAAAFAAoADwAUABkAHgAjACgALQAyADcAOwBAAEUASgBP -AFQAWQBeAGMAaABtAHIAdwB8AIEAhgCLAJAAlQCaAJ8ApACpAK4AsgC3ALwAwQDGAMsA0ADVANsA4ADl -AOsA8AD2APsBAQEHAQ0BEwEZAR8BJQErATIBOAE+AUUBTAFSAVkBYAFnAW4BdQF8AYMBiwGSAZoBoQGp -AbEBuQHBAckB0QHZAeEB6QHyAfoCAwIMAhQCHQImAi8COAJBAksCVAJdAmcCcQJ6AoQCjgKYAqICrAK2 -AsECywLVAuAC6wL1AwADCwMWAyEDLQM4A0MDTwNaA2YDcgN+A4oDlgOiA64DugPHA9MD4APsA/kEBgQT -BCAELQQ7BEgEVQRjBHEEfgSMBJoEqAS2BMQE0wThBPAE/gUNBRwFKwU6BUkFWAVnBXcFhgWWBaYFtQXF -BdUF5QX2BgYGFgYnBjcGSAZZBmoGewaMBp0GrwbABtEG4wb1BwcHGQcrBz0HTwdhB3QHhgeZB6wHvwfS -B+UH+AgLCB8IMghGCFoIbgiCCJYIqgi+CNII5wj7CRAJJQk6CU8JZAl5CY8JpAm6Cc8J5Qn7ChEKJwo9 -ClQKagqBCpgKrgrFCtwK8wsLCyILOQtRC2kLgAuYC7ALyAvhC/kMEgwqDEMMXAx1DI4MpwzADNkM8w0N -DSYNQA1aDXQNjg2pDcMN3g34DhMOLg5JDmQOfw6bDrYO0g7uDwkPJQ9BD14Peg+WD7MPzw/sEAkQJhBD -EGEQfhCbELkQ1xD1ERMRMRFPEW0RjBGqEckR6BIHEiYSRRJkEoQSoxLDEuMTAxMjE0MTYxODE6QTxRPl -FAYUJxRJFGoUixStFM4U8BUSFTQVVhV4FZsVvRXgFgMWJhZJFmwWjxayFtYW+hcdF0EXZReJF64X0hf3 -GBsYQBhlGIoYrxjVGPoZIBlFGWsZkRm3Gd0aBBoqGlEadxqeGsUa7BsUGzsbYxuKG7Ib2hwCHCocUhx7 -HKMczBz1HR4dRx1wHZkdwx3sHhYeQB5qHpQevh7pHxMfPh9pH5Qfvx/qIBUgQSBsIJggxCDwIRwhSCF1 -IaEhziH7IiciVSKCIq8i3SMKIzgjZiOUI8Ij8CQfJE0kfCSrJNolCSU4JWgllyXHJfcmJyZXJocmtybo -JxgnSSd6J6sn3CgNKD8ocSiiKNQpBik4KWspnSnQKgIqNSpoKpsqzysCKzYraSudK9EsBSw5LG4soizX -LQwtQS12Last4S4WLkwugi63Lu4vJC9aL5Evxy/+MDUwbDCkMNsxEjFKMYIxujHyMioyYzKbMtQzDTNG -M38zuDPxNCs0ZTSeNNg1EzVNNYc1wjX9Njc2cjauNuk3JDdgN5w31zgUOFA4jDjIOQU5Qjl/Obw5+To2 -OnQ6sjrvOy07azuqO+g8JzxlPKQ84z0iPWE9oT3gPiA+YD6gPuA/IT9hP6I/4kAjQGRApkDnQSlBakGs -Qe5CMEJyQrVC90M6Q31DwEQDREdEikTORRJFVUWaRd5GIkZnRqtG8Ec1R3tHwEgFSEtIkUjXSR1JY0mp -SfBKN0p9SsRLDEtTS5pL4kwqTHJMuk0CTUpNk03cTiVObk63TwBPSU+TT91QJ1BxULtRBlFQUZtR5lIx -UnxSx1MTU19TqlP2VEJUj1TbVShVdVXCVg9WXFapVvdXRFeSV+BYL1h9WMtZGllpWbhaB1pWWqZa9VtF -W5Vb5Vw1XIZc1l0nXXhdyV4aXmxevV8PX2Ffs2AFYFdgqmD8YU9homH1YklinGLwY0Njl2PrZEBklGTp -ZT1lkmXnZj1mkmboZz1nk2fpaD9olmjsaUNpmmnxakhqn2r3a09rp2v/bFdsr20IbWBtuW4SbmtuxG8e -b3hv0XArcIZw4HE6cZVx8HJLcqZzAXNdc7h0FHRwdMx1KHWFdeF2Pnabdvh3VnezeBF4bnjMeSp5iXnn -ekZ6pXsEe2N7wnwhfIF84X1BfaF+AX5ifsJ/I3+Ef+WAR4CogQqBa4HNgjCCkoL0g1eDuoQdhICE44VH -hauGDoZyhteHO4efiASIaYjOiTOJmYn+imSKyoswi5aL/IxjjMqNMY2Yjf+OZo7OjzaPnpAGkG6Q1pE/ -kaiSEZJ6kuOTTZO2lCCUipT0lV+VyZY0lp+XCpd1l+CYTJi4mSSZkJn8mmia1ZtCm6+cHJyJnPedZJ3S -nkCerp8dn4uf+qBpoNihR6G2oiailqMGo3aj5qRWpMelOKWpphqmi6b9p26n4KhSqMSpN6mpqhyqj6sC -q3Wr6axcrNCtRK24ri2uoa8Wr4uwALB1sOqxYLHWskuywrM4s660JbSctRO1irYBtnm28Ldot+C4WbjR -uUq5wro7urW7LrunvCG8m70VvY++Cr6Evv+/er/1wHDA7MFnwePCX8Lbw1jD1MRRxM7FS8XIxkbGw8dB -x7/IPci8yTrJuco4yrfLNsu2zDXMtc01zbXONs62zzfPuNA50LrRPNG+0j/SwdNE08bUSdTL1U7V0dZV -1tjXXNfg2GTY6Nls2fHadtr724DcBdyK3RDdlt4c3qLfKd+v4DbgveFE4cziU+Lb42Pj6+Rz5PzlhOYN -5pbnH+ep6DLovOlG6dDqW+rl63Dr++yG7RHtnO4o7rTvQO/M8Fjw5fFy8f/yjPMZ86f0NPTC9VD13vZt -9vv3ivgZ+Kj5OPnH+lf65/t3/Af8mP0p/br+S/7c/23//9IrLC0uWiRjbGFzc25hbWVYJGNsYXNzZXNf -EBBOU0JpdG1hcEltYWdlUmVwoy0vMFpOU0ltYWdlUmVwWE5TT2JqZWN00issMjNXTlNBcnJheaIyMNIr -LDU2Xk5TTXV0YWJsZUFycmF5ozUyMNM4OQ86OzxXTlNXaGl0ZVxOU0NvbG9yU3BhY2VEMCAwABADgAzS -Kyw+P1dOU0NvbG9yoj4w0issQUJXTlNJbWFnZaJBMAAIABEAGgAkACkAMgA3AEkATABRAFMAYgBoAHUA -fACLAJIAnwCmAK4AsACyALQAuQC7AL0AxADJANQA1gDYANoA3wDiAOQA5gDoAO8BBgEiASQBJhOWE5sT -phOvE8ITxhPRE9oT3xPnE+oT7xP+FAIUCRQRFB4UIxQlFCcULBQ0FDcUPBREAAAAAAAAAgEAAAAAAAAA -QwAAAAAAAAAAAAAAAAAAFEc +S2V5ZWRBcmNoaXZlctEICVRyb290gAGuCwwbHCEaJissMzY5P0JVJG51bGzXDQ4PEBESExQVFhcYGRpW +TlNTaXplViRjbGFzc1xOU0ltYWdlRmxhZ3NWTlNSZXBzW05TVGludENvbG9yV05TQ29sb3JeTlNSZXNp +emluZ01vZGWAAoANEiDDAACAA4AAgAsQAFZ7MSwgMX3SHQ4eIFpOUy5vYmplY3RzoR+ABIAK0h0OIiWi +IySABYAGgAnTDicoKSoaXxAUTlNUSUZGUmVwcmVzZW50YXRpb25fEBlOU0ludGVybmFsTGF5b3V0RGly +ZWN0aW9ugAiAB08REmxNTQAqAAAACgAAABABAAADAAAAAQABAAABAQADAAAAAQABAAABAgADAAAAAgAI +AAgBAwADAAAAAQABAAABBgADAAAAAQABAAABCgADAAAAAQABAAABEQAEAAAAAQAAAAgBEgADAAAAAQAB +AAABFQADAAAAAQACAAABFgADAAAAAQABAAABFwAEAAAAAQAAAAIBHAADAAAAAQABAAABKAADAAAAAQAC +AAABUgADAAAAAQABAAABUwADAAAAAgABAAGHcwAHAAARnAAAANAAAAAAAAARnGFwcGwCAAAAbW50ckdS +QVlYWVogB9wACAAXAA8ALgAPYWNzcEFQUEwAAAAAbm9uZQAAAAAAAAAAAAAAAAAAAAAAAPbWAAEAAAAA +0y1hcHBsAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFZGVzYwAA +AMAAAAB5ZHNjbQAAATwAAAgaY3BydAAACVgAAAAjd3RwdAAACXwAAAAUa1RSQwAACZAAAAgMZGVzYwAA +AAAAAAAfR2VuZXJpYyBHcmF5IEdhbW1hIDIuMiBQcm9maWxlAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAG1s +dWMAAAAAAAAAHwAAAAxza1NLAAAALgAAAYRkYURLAAAAOgAAAbJjYUVTAAAAOAAAAex2aVZOAAAAQAAA +AiRwdEJSAAAASgAAAmR1a1VBAAAALAAAAq5mckZVAAAAPgAAAtpodUhVAAAANAAAAxh6aFRXAAAAGgAA +A0xrb0tSAAAAIgAAA2ZuYk5PAAAAOgAAA4hjc0NaAAAAKAAAA8JoZUlMAAAAJAAAA+pyb1JPAAAAKgAA +BA5kZURFAAAATgAABDhpdElUAAAATgAABIZzdlNFAAAAOAAABNR6aENOAAAAGgAABQxqYUpQAAAAJgAA +BSZlbEdSAAAAKgAABUxwdFBPAAAAUgAABXZubE5MAAAAQAAABchlc0VTAAAATAAABgh0aFRIAAAAMgAA +BlR0clRSAAAAJAAABoZmaUZJAAAARgAABqpockhSAAAAPgAABvBwbFBMAAAASgAABy5hckVHAAAALAAA +B3hydVJVAAAAOgAAB6RlblVTAAAAPAAAB94AVgFhAGUAbwBiAGUAYwBuAOEAIABzAGkAdgDhACAAZwBh +AG0AYQAgADIALAAyAEcAZQBuAGUAcgBpAHMAawAgAGcAcgDlACAAMgAsADIAIABnAGEAbQBtAGEALQBw +AHIAbwBmAGkAbABHAGEAbQBtAGEAIABkAGUAIABnAHIAaQBzAG8AcwAgAGcAZQBuAOgAcgBpAGMAYQAg +ADIALgAyAEMepQB1ACAAaADsAG4AaAAgAE0A4AB1ACAAeADhAG0AIABDAGgAdQBuAGcAIABHAGEAbQBt +AGEAIAAyAC4AMgBQAGUAcgBmAGkAbAAgAEcAZQBuAOkAcgBpAGMAbwAgAGQAYQAgAEcAYQBtAGEAIABk +AGUAIABDAGkAbgB6AGEAcwAgADIALAAyBBcEMAQzBDAEOwRMBD0EMAAgAEcAcgBhAHkALQQzBDAEPAQw +ACAAMgAuADIAUAByAG8AZgBpAGwAIABnAOkAbgDpAHIAaQBxAHUAZQAgAGcAcgBpAHMAIABnAGEAbQBt +AGEAIAAyACwAMgDBAGwAdABhAGwA4QBuAG8AcwAgAHMAegD8AHIAawBlACAAZwBhAG0AbQBhACAAMgAu +ADKQGnUocHCWjlFJXqYAMgAuADKCcl9pY8+P8Md8vBgAINaMwMkAIKwQucgAIAAyAC4AMgAg1QS4XNMM +x3wARwBlAG4AZQByAGkAcwBrACAAZwByAOUAIABnAGEAbQBtAGEAIAAyACwAMgAtAHAAcgBvAGYAaQBs +AE8AYgBlAGMAbgDhACABYQBlAGQA4QAgAGcAYQBtAGEAIAAyAC4AMgXSBdAF3gXUACAF0AXkBdUF6AAg +BdsF3AXcBdkAIAAyAC4AMgBHAGEAbQBhACAAZwByAGkAIABnAGUAbgBlAHIAaQBjAQMAIAAyACwAMgBB +AGwAbABnAGUAbQBlAGkAbgBlAHMAIABHAHIAYQB1AHMAdAB1AGYAZQBuAC0AUAByAG8AZgBpAGwAIABH +AGEAbQBtAGEAIAAyACwAMgBQAHIAbwBmAGkAbABvACAAZwByAGkAZwBpAG8AIABnAGUAbgBlAHIAaQBj +AG8AIABkAGUAbABsAGEAIABnAGEAbQBtAGEAIAAyACwAMgBHAGUAbgBlAHIAaQBzAGsAIABnAHIA5QAg +ADIALAAyACAAZwBhAG0AbQBhAHAAcgBvAGYAaQBsZm6QGnBwXqZ8+2VwADIALgAyY8+P8GWHTvZOAIIs +MLAw7DCkMKww8zDeACAAMgAuADIAIDDXMO0w1TChMKQw6wOTA7UDvQO5A7oDzAAgA5MDugPBA7kAIAOT +A6wDvAO8A7EAIAAyAC4AMgBQAGUAcgBmAGkAbAAgAGcAZQBuAOkAcgBpAGMAbwAgAGQAZQAgAGMAaQBu +AHoAZQBuAHQAbwBzACAAZABhACAARwBhAG0AbQBhACAAMgAsADIAQQBsAGcAZQBtAGUAZQBuACAAZwBy +AGkAagBzACAAZwBhAG0AbQBhACAAMgAsADIALQBwAHIAbwBmAGkAZQBsAFAAZQByAGYAaQBsACAAZwBl +AG4A6QByAGkAYwBvACAAZABlACAAZwBhAG0AbQBhACAAZABlACAAZwByAGkAcwBlAHMAIAAyACwAMg4j +DjEOBw4qDjUOQQ4BDiEOIQ4yDkAOAQ4jDiIOTA4XDjEOSA4nDkQOGwAgADIALgAyAEcAZQBuAGUAbAAg +AEcAcgBpACAARwBhAG0AYQAgADIALAAyAFkAbABlAGkAbgBlAG4AIABoAGEAcgBtAGEAYQBuACAAZwBh +AG0AbQBhACAAMgAsADIAIAAtAHAAcgBvAGYAaQBpAGwAaQBHAGUAbgBlAHIAaQENAGsAaQAgAEcAcgBh +AHkAIABHAGEAbQBtAGEAIAAyAC4AMgAgAHAAcgBvAGYAaQBsAFUAbgBpAHcAZQByAHMAYQBsAG4AeQAg +AHAAcgBvAGYAaQBsACAAcwB6AGEAcgBvAVsAYwBpACAAZwBhAG0AbQBhACAAMgAsADIGOgYnBkUGJwAg +ADIALgAyACAGRAZIBkYAIAYxBkUGJwYvBkoAIAY5BicGRQQeBDEESQQwBE8AIARBBDUEQAQwBE8AIAQz +BDAEPAQ8BDAAIAAyACwAMgAtBD8EQAQ+BEQEOAQ7BEwARwBlAG4AZQByAGkAYwAgAEcAcgBhAHkAIABH +AGEAbQBtAGEAIAAyAC4AMgAgAFAAcgBvAGYAaQBsAGUAAHRleHQAAAAAQ29weXJpZ2h0IEFwcGxlIElu +Yy4sIDIwMTIAAFhZWiAAAAAAAADzUQABAAAAARbMY3VydgAAAAAAAAQAAAAABQAKAA8AFAAZAB4AIwAo +AC0AMgA3ADsAQABFAEoATwBUAFkAXgBjAGgAbQByAHcAfACBAIYAiwCQAJUAmgCfAKQAqQCuALIAtwC8 +AMEAxgDLANAA1QDbAOAA5QDrAPAA9gD7AQEBBwENARMBGQEfASUBKwEyATgBPgFFAUwBUgFZAWABZwFu +AXUBfAGDAYsBkgGaAaEBqQGxAbkBwQHJAdEB2QHhAekB8gH6AgMCDAIUAh0CJgIvAjgCQQJLAlQCXQJn +AnECegKEAo4CmAKiAqwCtgLBAssC1QLgAusC9QMAAwsDFgMhAy0DOANDA08DWgNmA3IDfgOKA5YDogOu +A7oDxwPTA+AD7AP5BAYEEwQgBC0EOwRIBFUEYwRxBH4EjASaBKgEtgTEBNME4QTwBP4FDQUcBSsFOgVJ +BVgFZwV3BYYFlgWmBbUFxQXVBeUF9gYGBhYGJwY3BkgGWQZqBnsGjAadBq8GwAbRBuMG9QcHBxkHKwc9 +B08HYQd0B4YHmQesB78H0gflB/gICwgfCDIIRghaCG4IggiWCKoIvgjSCOcI+wkQCSUJOglPCWQJeQmP +CaQJugnPCeUJ+woRCicKPQpUCmoKgQqYCq4KxQrcCvMLCwsiCzkLUQtpC4ALmAuwC8gL4Qv5DBIMKgxD +DFwMdQyODKcMwAzZDPMNDQ0mDUANWg10DY4NqQ3DDd4N+A4TDi4OSQ5kDn8Omw62DtIO7g8JDyUPQQ9e +D3oPlg+zD88P7BAJECYQQxBhEH4QmxC5ENcQ9RETETERTxFtEYwRqhHJEegSBxImEkUSZBKEEqMSwxLj +EwMTIxNDE2MTgxOkE8UT5RQGFCcUSRRqFIsUrRTOFPAVEhU0FVYVeBWbFb0V4BYDFiYWSRZsFo8WshbW +FvoXHRdBF2UXiReuF9IX9xgbGEAYZRiKGK8Y1Rj6GSAZRRlrGZEZtxndGgQaKhpRGncanhrFGuwbFBs7 +G2MbihuyG9ocAhwqHFIcexyjHMwc9R0eHUcdcB2ZHcMd7B4WHkAeah6UHr4e6R8THz4faR+UH78f6iAV +IEEgbCCYIMQg8CEcIUghdSGhIc4h+yInIlUigiKvIt0jCiM4I2YjlCPCI/AkHyRNJHwkqyTaJQklOCVo +JZclxyX3JicmVyaHJrcm6CcYJ0kneierJ9woDSg/KHEooijUKQYpOClrKZ0p0CoCKjUqaCqbKs8rAis2 +K2krnSvRLAUsOSxuLKIs1y0MLUEtdi2rLeEuFi5MLoIuty7uLyQvWi+RL8cv/jA1MGwwpDDbMRIxSjGC +Mbox8jIqMmMymzLUMw0zRjN/M7gz8TQrNGU0njTYNRM1TTWHNcI1/TY3NnI2rjbpNyQ3YDecN9c4FDhQ +OIw4yDkFOUI5fzm8Ofk6Njp0OrI67zstO2s7qjvoPCc8ZTykPOM9Ij1hPaE94D4gPmA+oD7gPyE/YT+i +P+JAI0BkQKZA50EpQWpBrEHuQjBCckK1QvdDOkN9Q8BEA0RHRIpEzkUSRVVFmkXeRiJGZ0arRvBHNUd7 +R8BIBUhLSJFI10kdSWNJqUnwSjdKfUrESwxLU0uaS+JMKkxyTLpNAk1KTZNN3E4lTm5Ot08AT0lPk0/d +UCdQcVC7UQZRUFGbUeZSMVJ8UsdTE1NfU6pT9lRCVI9U21UoVXVVwlYPVlxWqVb3V0RXklfgWC9YfVjL +WRpZaVm4WgdaVlqmWvVbRVuVW+VcNVyGXNZdJ114XcleGl5sXr1fD19hX7NgBWBXYKpg/GFPYaJh9WJJ +Ypxi8GNDY5dj62RAZJRk6WU9ZZJl52Y9ZpJm6Gc9Z5Nn6Wg/aJZo7GlDaZpp8WpIap9q92tPa6dr/2xX +bK9tCG1gbbluEm5rbsRvHm94b9FwK3CGcOBxOnGVcfByS3KmcwFzXXO4dBR0cHTMdSh1hXXhdj52m3b4 +d1Z3s3gReG54zHkqeYl553pGeqV7BHtje8J8IXyBfOF9QX2hfgF+Yn7CfyN/hH/lgEeAqIEKgWuBzYIw +gpKC9INXg7qEHYSAhOOFR4Wrhg6GcobXhzuHn4gEiGmIzokziZmJ/opkisqLMIuWi/yMY4zKjTGNmI3/ +jmaOzo82j56QBpBukNaRP5GokhGSepLjk02TtpQglIqU9JVflcmWNJaflwqXdZfgmEyYuJkkmZCZ/Jpo +mtWbQpuvnByciZz3nWSd0p5Anq6fHZ+Ln/qgaaDYoUehtqImopajBqN2o+akVqTHpTilqaYapoum/adu +p+CoUqjEqTepqaocqo+rAqt1q+msXKzQrUStuK4trqGvFq+LsACwdbDqsWCx1rJLssKzOLOutCW0nLUT +tYq2AbZ5tvC3aLfguFm40blKucK6O7q1uy67p7whvJu9Fb2Pvgq+hL7/v3q/9cBwwOzBZ8Hjwl/C28NY +w9TEUcTOxUvFyMZGxsPHQce/yD3IvMk6ybnKOMq3yzbLtsw1zLXNNc21zjbOts83z7jQOdC60TzRvtI/ +0sHTRNPG1EnUy9VO1dHWVdbY11zX4Nhk2OjZbNnx2nba+9uA3AXcit0Q3ZbeHN6i3ynfr+A24L3hROHM +4lPi2+Nj4+vkc+T85YTmDeaW5x/nqegy6LzpRunQ6lvq5etw6/vshu0R7ZzuKO6070DvzPBY8OXxcvH/ +8ozzGfOn9DT0wvVQ9d72bfb794r4Gfio+Tj5x/pX+uf7d/wH/Jj9Kf26/kv+3P9t///SLS4vMFokY2xh +c3NuYW1lWCRjbGFzc2VzXxAQTlNCaXRtYXBJbWFnZVJlcKMvMTJaTlNJbWFnZVJlcFhOU09iamVjdNIt +LjQ1V05TQXJyYXmiNDLSLS43OF5OU011dGFibGVBcnJheaM3NDLTOjsOPD0+V05TV2hpdGVcTlNDb2xv +clNwYWNlRDAgMAAQA4AM0i0uQEFXTlNDb2xvcqJAMtItLkNEV05TSW1hZ2WiQzIACAARABoAJAApADIA +NwBJAEwAUQBTAGIAaAB3AH4AhQCSAJkApQCtALwAvgDAAMUAxwDJAMsAzQDUANkA5ADmAOgA6gDvAPIA +9AD2APgA/wEWATIBNAE2E6YTqxO2E78T0hPWE+ET6hPvE/cT+hP/FA4UEhQZFCEULhQzFDUUNxQ8FEQU +RxRMFFQAAAAAAAACAQAAAAAAAABFAAAAAAAAAAAAAAAAAAAUVw diff --git a/i18n/src/main/resources/ru.lproj/Command.xib b/i18n/src/main/resources/ru.lproj/Command.xib index 115cedc019..732b555038 100644 --- a/i18n/src/main/resources/ru.lproj/Command.xib +++ b/i18n/src/main/resources/ru.lproj/Command.xib @@ -1,7 +1,7 @@ - + - + diff --git a/i18n/src/main/resources/ru.lproj/Connection.xib b/i18n/src/main/resources/ru.lproj/Connection.xib index 57d185b2af..0db086de06 100644 --- a/i18n/src/main/resources/ru.lproj/Connection.xib +++ b/i18n/src/main/resources/ru.lproj/Connection.xib @@ -1,7 +1,7 @@ - + - + @@ -76,7 +76,7 @@ - + diff --git a/i18n/src/main/resources/ru.lproj/Info.xib b/i18n/src/main/resources/ru.lproj/Info.xib index 249b0b6359..4590cbae9e 100644 --- a/i18n/src/main/resources/ru.lproj/Info.xib +++ b/i18n/src/main/resources/ru.lproj/Info.xib @@ -1,7 +1,7 @@ - + - + @@ -89,7 +89,7 @@ - + @@ -1017,21 +1017,21 @@ - + - + - + @@ -1121,21 +1121,21 @@ - + - + - + @@ -1250,27 +1250,27 @@ diff --git a/i18n/src/main/resources/ru.lproj/Login.strings b/i18n/src/main/resources/ru.lproj/Login.strings index 4040713b03..8c63b36d6a 100644 Binary files a/i18n/src/main/resources/ru.lproj/Login.strings and b/i18n/src/main/resources/ru.lproj/Login.strings differ diff --git a/i18n/src/main/resources/ru.lproj/Main.xib b/i18n/src/main/resources/ru.lproj/Main.xib index 934a085f45..d9a3e4bab8 100644 --- a/i18n/src/main/resources/ru.lproj/Main.xib +++ b/i18n/src/main/resources/ru.lproj/Main.xib @@ -1,7 +1,7 @@ - + - + @@ -94,7 +94,7 @@ - + @@ -159,19 +159,19 @@ - + - + - + @@ -321,12 +321,12 @@ CA - + - + @@ -566,6 +566,10 @@ CA - + + + + + diff --git a/i18n/src/main/resources/ru.lproj/Preferences.xib b/i18n/src/main/resources/ru.lproj/Preferences.xib index 45e0544a2a..23925293b9 100644 --- a/i18n/src/main/resources/ru.lproj/Preferences.xib +++ b/i18n/src/main/resources/ru.lproj/Preferences.xib @@ -1,7 +1,7 @@ - + - + @@ -266,7 +266,7 @@ - + @@ -307,7 +307,7 @@ - + @@ -539,7 +539,7 @@ - + @@ -579,7 +579,7 @@ - + @@ -614,7 +614,7 @@ - + @@ -643,7 +643,7 @@ - + @@ -1219,7 +1219,7 @@ - + @@ -1251,7 +1251,7 @@ - + @@ -1291,7 +1291,7 @@ - + @@ -1364,7 +1364,7 @@ - + @@ -1559,7 +1559,7 @@ - + @@ -1619,7 +1619,7 @@ - + @@ -1644,7 +1644,7 @@ - + @@ -1686,7 +1686,7 @@ - + @@ -1714,7 +1714,7 @@ - + @@ -1742,7 +1742,7 @@ - + @@ -1771,7 +1771,7 @@ - + @@ -1806,7 +1806,7 @@ - + @@ -1890,7 +1890,7 @@ - + @@ -1949,7 +1949,7 @@ - + @@ -1977,7 +1977,7 @@ - + @@ -2006,7 +2006,7 @@ - + diff --git a/i18n/src/main/resources/ru.lproj/Profiles.xib b/i18n/src/main/resources/ru.lproj/Profiles.xib index 906dd12331..91e3d6ce8e 100644 --- a/i18n/src/main/resources/ru.lproj/Profiles.xib +++ b/i18n/src/main/resources/ru.lproj/Profiles.xib @@ -1,7 +1,7 @@ - + - + diff --git a/i18n/src/main/resources/ru.lproj/Prompt.xib b/i18n/src/main/resources/ru.lproj/Prompt.xib index 1fa06f304b..abfe0d7f65 100644 --- a/i18n/src/main/resources/ru.lproj/Prompt.xib +++ b/i18n/src/main/resources/ru.lproj/Prompt.xib @@ -1,7 +1,7 @@ - + - + @@ -95,7 +95,7 @@ Gw - + diff --git a/i18n/src/main/resources/ru.lproj/S3.strings b/i18n/src/main/resources/ru.lproj/S3.strings index 88deeed2dc..f3035d39db 100644 Binary files a/i18n/src/main/resources/ru.lproj/S3.strings and b/i18n/src/main/resources/ru.lproj/S3.strings differ diff --git a/i18n/src/main/resources/ru.lproj/Updater.strings b/i18n/src/main/resources/ru.lproj/Updater.strings index 39db7a89aa..cad7b12c11 100644 Binary files a/i18n/src/main/resources/ru.lproj/Updater.strings and b/i18n/src/main/resources/ru.lproj/Updater.strings differ diff --git a/i18n/src/main/resources/sk.lproj/Bookmark.xib b/i18n/src/main/resources/sk.lproj/Bookmark.xib index ca1d57868f..964a931ca4 100644 --- a/i18n/src/main/resources/sk.lproj/Bookmark.xib +++ b/i18n/src/main/resources/sk.lproj/Bookmark.xib @@ -1,7 +1,7 @@ - + - + @@ -92,7 +92,7 @@ - + @@ -489,95 +489,95 @@ YnBsaXN0MDDUAQIDBAUGBwpYJHZlcnNpb25ZJGFyY2hpdmVyVCR0b3BYJG9iamVjdHMSAAGGoF8QD05T -S2V5ZWRBcmNoaXZlctEICVRyb290gAGuCwwZGh8UJCkqMTQ3PUBVJG51bGzWDQ4PEBESExQVFhcYVk5T -U2l6ZV5OU1Jlc2l6aW5nTW9kZVYkY2xhc3NcTlNJbWFnZUZsYWdzVk5TUmVwc1dOU0NvbG9ygAIQAIAN -EiDDAACAA4ALVnsxLCAxfdIbDxweWk5TLm9iamVjdHOhHYAEgArSGw8gI6IhIoAFgAaACdMPJSYnKBRf -EBROU1RJRkZSZXByZXNlbnRhdGlvbl8QGU5TSW50ZXJuYWxMYXlvdXREaXJlY3Rpb26ACIAHTxESbE1N -ACoAAAAKAAAAEAEAAAMAAAABAAEAAAEBAAMAAAABAAEAAAECAAMAAAACAAgACAEDAAMAAAABAAEAAAEG -AAMAAAABAAEAAAEKAAMAAAABAAEAAAERAAQAAAABAAAACAESAAMAAAABAAEAAAEVAAMAAAABAAIAAAEW -AAMAAAABAAEAAAEXAAQAAAABAAAAAgEcAAMAAAABAAEAAAEoAAMAAAABAAIAAAFSAAMAAAABAAEAAAFT -AAMAAAACAAEAAYdzAAcAABGcAAAA0AAAAAAAABGcYXBwbAIAAABtbnRyR1JBWVhZWiAH3AAIABcADwAu -AA9hY3NwQVBQTAAAAABub25lAAAAAAAAAAAAAAAAAAAAAAAA9tYAAQAAAADTLWFwcGwAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAVkZXNjAAAAwAAAAHlkc2NtAAABPAAA -CBpjcHJ0AAAJWAAAACN3dHB0AAAJfAAAABRrVFJDAAAJkAAACAxkZXNjAAAAAAAAAB9HZW5lcmljIEdy -YXkgR2FtbWEgMi4yIFByb2ZpbGUAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAbWx1YwAAAAAAAAAfAAAADHNr -U0sAAAAuAAABhGRhREsAAAA6AAABsmNhRVMAAAA4AAAB7HZpVk4AAABAAAACJHB0QlIAAABKAAACZHVr -VUEAAAAsAAACrmZyRlUAAAA+AAAC2mh1SFUAAAA0AAADGHpoVFcAAAAaAAADTGtvS1IAAAAiAAADZm5i -Tk8AAAA6AAADiGNzQ1oAAAAoAAADwmhlSUwAAAAkAAAD6nJvUk8AAAAqAAAEDmRlREUAAABOAAAEOGl0 -SVQAAABOAAAEhnN2U0UAAAA4AAAE1HpoQ04AAAAaAAAFDGphSlAAAAAmAAAFJmVsR1IAAAAqAAAFTHB0 -UE8AAABSAAAFdm5sTkwAAABAAAAFyGVzRVMAAABMAAAGCHRoVEgAAAAyAAAGVHRyVFIAAAAkAAAGhmZp -RkkAAABGAAAGqmhySFIAAAA+AAAG8HBsUEwAAABKAAAHLmFyRUcAAAAsAAAHeHJ1UlUAAAA6AAAHpGVu -VVMAAAA8AAAH3gBWAWEAZQBvAGIAZQBjAG4A4QAgAHMAaQB2AOEAIABnAGEAbQBhACAAMgAsADIARwBl -AG4AZQByAGkAcwBrACAAZwByAOUAIAAyACwAMgAgAGcAYQBtAG0AYQAtAHAAcgBvAGYAaQBsAEcAYQBt -AG0AYQAgAGQAZQAgAGcAcgBpAHMAbwBzACAAZwBlAG4A6AByAGkAYwBhACAAMgAuADIAQx6lAHUAIABo -AOwAbgBoACAATQDgAHUAIAB4AOEAbQAgAEMAaAB1AG4AZwAgAEcAYQBtAG0AYQAgADIALgAyAFAAZQBy -AGYAaQBsACAARwBlAG4A6QByAGkAYwBvACAAZABhACAARwBhAG0AYQAgAGQAZQAgAEMAaQBuAHoAYQBz -ACAAMgAsADIEFwQwBDMEMAQ7BEwEPQQwACAARwByAGEAeQAtBDMEMAQ8BDAAIAAyAC4AMgBQAHIAbwBm -AGkAbAAgAGcA6QBuAOkAcgBpAHEAdQBlACAAZwByAGkAcwAgAGcAYQBtAG0AYQAgADIALAAyAMEAbAB0 -AGEAbADhAG4AbwBzACAAcwB6APwAcgBrAGUAIABnAGEAbQBtAGEAIAAyAC4AMpAadShwcJaOUUlepgAy -AC4AMoJyX2ljz4/wx3y8GAAg1ozAyQAgrBC5yAAgADIALgAyACDVBLhc0wzHfABHAGUAbgBlAHIAaQBz -AGsAIABnAHIA5QAgAGcAYQBtAG0AYQAgADIALAAyAC0AcAByAG8AZgBpAGwATwBiAGUAYwBuAOEAIAFh -AGUAZADhACAAZwBhAG0AYQAgADIALgAyBdIF0AXeBdQAIAXQBeQF1QXoACAF2wXcBdwF2QAgADIALgAy -AEcAYQBtAGEAIABnAHIAaQAgAGcAZQBuAGUAcgBpAGMBAwAgADIALAAyAEEAbABsAGcAZQBtAGUAaQBu -AGUAcwAgAEcAcgBhAHUAcwB0AHUAZgBlAG4ALQBQAHIAbwBmAGkAbAAgAEcAYQBtAG0AYQAgADIALAAy -AFAAcgBvAGYAaQBsAG8AIABnAHIAaQBnAGkAbwAgAGcAZQBuAGUAcgBpAGMAbwAgAGQAZQBsAGwAYQAg -AGcAYQBtAG0AYQAgADIALAAyAEcAZQBuAGUAcgBpAHMAawAgAGcAcgDlACAAMgAsADIAIABnAGEAbQBt -AGEAcAByAG8AZgBpAGxmbpAacHBepnz7ZXAAMgAuADJjz4/wZYdO9k4AgiwwsDDsMKQwrDDzMN4AIAAy -AC4AMgAgMNcw7TDVMKEwpDDrA5MDtQO9A7kDugPMACADkwO6A8EDuQAgA5MDrAO8A7wDsQAgADIALgAy -AFAAZQByAGYAaQBsACAAZwBlAG4A6QByAGkAYwBvACAAZABlACAAYwBpAG4AegBlAG4AdABvAHMAIABk -AGEAIABHAGEAbQBtAGEAIAAyACwAMgBBAGwAZwBlAG0AZQBlAG4AIABnAHIAaQBqAHMAIABnAGEAbQBt -AGEAIAAyACwAMgAtAHAAcgBvAGYAaQBlAGwAUABlAHIAZgBpAGwAIABnAGUAbgDpAHIAaQBjAG8AIABk -AGUAIABnAGEAbQBtAGEAIABkAGUAIABnAHIAaQBzAGUAcwAgADIALAAyDiMOMQ4HDioONQ5BDgEOIQ4h -DjIOQA4BDiMOIg5MDhcOMQ5IDicORA4bACAAMgAuADIARwBlAG4AZQBsACAARwByAGkAIABHAGEAbQBh -ACAAMgAsADIAWQBsAGUAaQBuAGUAbgAgAGgAYQByAG0AYQBhAG4AIABnAGEAbQBtAGEAIAAyACwAMgAg -AC0AcAByAG8AZgBpAGkAbABpAEcAZQBuAGUAcgBpAQ0AawBpACAARwByAGEAeQAgAEcAYQBtAG0AYQAg -ADIALgAyACAAcAByAG8AZgBpAGwAVQBuAGkAdwBlAHIAcwBhAGwAbgB5ACAAcAByAG8AZgBpAGwAIABz -AHoAYQByAG8BWwBjAGkAIABnAGEAbQBtAGEAIAAyACwAMgY6BicGRQYnACAAMgAuADIAIAZEBkgGRgAg -BjEGRQYnBi8GSgAgBjkGJwZFBB4EMQRJBDAETwAgBEEENQRABDAETwAgBDMEMAQ8BDwEMAAgADIALAAy -AC0EPwRABD4ERAQ4BDsETABHAGUAbgBlAHIAaQBjACAARwByAGEAeQAgAEcAYQBtAG0AYQAgADIALgAy -ACAAUAByAG8AZgBpAGwAZQAAdGV4dAAAAABDb3B5cmlnaHQgQXBwbGUgSW5jLiwgMjAxMgAAWFlaIAAA -AAAAAPNRAAEAAAABFsxjdXJ2AAAAAAAABAAAAAAFAAoADwAUABkAHgAjACgALQAyADcAOwBAAEUASgBP -AFQAWQBeAGMAaABtAHIAdwB8AIEAhgCLAJAAlQCaAJ8ApACpAK4AsgC3ALwAwQDGAMsA0ADVANsA4ADl -AOsA8AD2APsBAQEHAQ0BEwEZAR8BJQErATIBOAE+AUUBTAFSAVkBYAFnAW4BdQF8AYMBiwGSAZoBoQGp -AbEBuQHBAckB0QHZAeEB6QHyAfoCAwIMAhQCHQImAi8COAJBAksCVAJdAmcCcQJ6AoQCjgKYAqICrAK2 -AsECywLVAuAC6wL1AwADCwMWAyEDLQM4A0MDTwNaA2YDcgN+A4oDlgOiA64DugPHA9MD4APsA/kEBgQT -BCAELQQ7BEgEVQRjBHEEfgSMBJoEqAS2BMQE0wThBPAE/gUNBRwFKwU6BUkFWAVnBXcFhgWWBaYFtQXF -BdUF5QX2BgYGFgYnBjcGSAZZBmoGewaMBp0GrwbABtEG4wb1BwcHGQcrBz0HTwdhB3QHhgeZB6wHvwfS -B+UH+AgLCB8IMghGCFoIbgiCCJYIqgi+CNII5wj7CRAJJQk6CU8JZAl5CY8JpAm6Cc8J5Qn7ChEKJwo9 -ClQKagqBCpgKrgrFCtwK8wsLCyILOQtRC2kLgAuYC7ALyAvhC/kMEgwqDEMMXAx1DI4MpwzADNkM8w0N -DSYNQA1aDXQNjg2pDcMN3g34DhMOLg5JDmQOfw6bDrYO0g7uDwkPJQ9BD14Peg+WD7MPzw/sEAkQJhBD -EGEQfhCbELkQ1xD1ERMRMRFPEW0RjBGqEckR6BIHEiYSRRJkEoQSoxLDEuMTAxMjE0MTYxODE6QTxRPl -FAYUJxRJFGoUixStFM4U8BUSFTQVVhV4FZsVvRXgFgMWJhZJFmwWjxayFtYW+hcdF0EXZReJF64X0hf3 -GBsYQBhlGIoYrxjVGPoZIBlFGWsZkRm3Gd0aBBoqGlEadxqeGsUa7BsUGzsbYxuKG7Ib2hwCHCocUhx7 -HKMczBz1HR4dRx1wHZkdwx3sHhYeQB5qHpQevh7pHxMfPh9pH5Qfvx/qIBUgQSBsIJggxCDwIRwhSCF1 -IaEhziH7IiciVSKCIq8i3SMKIzgjZiOUI8Ij8CQfJE0kfCSrJNolCSU4JWgllyXHJfcmJyZXJocmtybo -JxgnSSd6J6sn3CgNKD8ocSiiKNQpBik4KWspnSnQKgIqNSpoKpsqzysCKzYraSudK9EsBSw5LG4soizX -LQwtQS12Last4S4WLkwugi63Lu4vJC9aL5Evxy/+MDUwbDCkMNsxEjFKMYIxujHyMioyYzKbMtQzDTNG -M38zuDPxNCs0ZTSeNNg1EzVNNYc1wjX9Njc2cjauNuk3JDdgN5w31zgUOFA4jDjIOQU5Qjl/Obw5+To2 -OnQ6sjrvOy07azuqO+g8JzxlPKQ84z0iPWE9oT3gPiA+YD6gPuA/IT9hP6I/4kAjQGRApkDnQSlBakGs -Qe5CMEJyQrVC90M6Q31DwEQDREdEikTORRJFVUWaRd5GIkZnRqtG8Ec1R3tHwEgFSEtIkUjXSR1JY0mp -SfBKN0p9SsRLDEtTS5pL4kwqTHJMuk0CTUpNk03cTiVObk63TwBPSU+TT91QJ1BxULtRBlFQUZtR5lIx -UnxSx1MTU19TqlP2VEJUj1TbVShVdVXCVg9WXFapVvdXRFeSV+BYL1h9WMtZGllpWbhaB1pWWqZa9VtF -W5Vb5Vw1XIZc1l0nXXhdyV4aXmxevV8PX2Ffs2AFYFdgqmD8YU9homH1YklinGLwY0Njl2PrZEBklGTp -ZT1lkmXnZj1mkmboZz1nk2fpaD9olmjsaUNpmmnxakhqn2r3a09rp2v/bFdsr20IbWBtuW4SbmtuxG8e -b3hv0XArcIZw4HE6cZVx8HJLcqZzAXNdc7h0FHRwdMx1KHWFdeF2Pnabdvh3VnezeBF4bnjMeSp5iXnn -ekZ6pXsEe2N7wnwhfIF84X1BfaF+AX5ifsJ/I3+Ef+WAR4CogQqBa4HNgjCCkoL0g1eDuoQdhICE44VH -hauGDoZyhteHO4efiASIaYjOiTOJmYn+imSKyoswi5aL/IxjjMqNMY2Yjf+OZo7OjzaPnpAGkG6Q1pE/ -kaiSEZJ6kuOTTZO2lCCUipT0lV+VyZY0lp+XCpd1l+CYTJi4mSSZkJn8mmia1ZtCm6+cHJyJnPedZJ3S -nkCerp8dn4uf+qBpoNihR6G2oiailqMGo3aj5qRWpMelOKWpphqmi6b9p26n4KhSqMSpN6mpqhyqj6sC -q3Wr6axcrNCtRK24ri2uoa8Wr4uwALB1sOqxYLHWskuywrM4s660JbSctRO1irYBtnm28Ldot+C4WbjR -uUq5wro7urW7LrunvCG8m70VvY++Cr6Evv+/er/1wHDA7MFnwePCX8Lbw1jD1MRRxM7FS8XIxkbGw8dB -x7/IPci8yTrJuco4yrfLNsu2zDXMtc01zbXONs62zzfPuNA50LrRPNG+0j/SwdNE08bUSdTL1U7V0dZV -1tjXXNfg2GTY6Nls2fHadtr724DcBdyK3RDdlt4c3qLfKd+v4DbgveFE4cziU+Lb42Pj6+Rz5PzlhOYN -5pbnH+ep6DLovOlG6dDqW+rl63Dr++yG7RHtnO4o7rTvQO/M8Fjw5fFy8f/yjPMZ86f0NPTC9VD13vZt -9vv3ivgZ+Kj5OPnH+lf65/t3/Af8mP0p/br+S/7c/23//9IrLC0uWiRjbGFzc25hbWVYJGNsYXNzZXNf -EBBOU0JpdG1hcEltYWdlUmVwoy0vMFpOU0ltYWdlUmVwWE5TT2JqZWN00issMjNXTlNBcnJheaIyMNIr -LDU2Xk5TTXV0YWJsZUFycmF5ozUyMNM4OQ86OzxXTlNXaGl0ZVxOU0NvbG9yU3BhY2VEMCAwABADgAzS -Kyw+P1dOU0NvbG9yoj4w0issQUJXTlNJbWFnZaJBMAAIABEAGgAkACkAMgA3AEkATABRAFMAYgBoAHUA -fACLAJIAnwCmAK4AsACyALQAuQC7AL0AxADJANQA1gDYANoA3wDiAOQA5gDoAO8BBgEiASQBJhOWE5sT -phOvE8ITxhPRE9oT3xPnE+oT7xP+FAIUCRQRFB4UIxQlFCcULBQ0FDcUPBREAAAAAAAAAgEAAAAAAAAA -QwAAAAAAAAAAAAAAAAAAFEc +S2V5ZWRBcmNoaXZlctEICVRyb290gAGuCwwbHCEaJissMzY5P0JVJG51bGzXDQ4PEBESExQVFhcYGRpW +TlNTaXplViRjbGFzc1xOU0ltYWdlRmxhZ3NWTlNSZXBzW05TVGludENvbG9yV05TQ29sb3JeTlNSZXNp +emluZ01vZGWAAoANEiDDAACAA4AAgAsQAFZ7MSwgMX3SHQ4eIFpOUy5vYmplY3RzoR+ABIAK0h0OIiWi +IySABYAGgAnTDicoKSoaXxAUTlNUSUZGUmVwcmVzZW50YXRpb25fEBlOU0ludGVybmFsTGF5b3V0RGly +ZWN0aW9ugAiAB08REmxNTQAqAAAACgAAABABAAADAAAAAQABAAABAQADAAAAAQABAAABAgADAAAAAgAI +AAgBAwADAAAAAQABAAABBgADAAAAAQABAAABCgADAAAAAQABAAABEQAEAAAAAQAAAAgBEgADAAAAAQAB +AAABFQADAAAAAQACAAABFgADAAAAAQABAAABFwAEAAAAAQAAAAIBHAADAAAAAQABAAABKAADAAAAAQAC +AAABUgADAAAAAQABAAABUwADAAAAAgABAAGHcwAHAAARnAAAANAAAAAAAAARnGFwcGwCAAAAbW50ckdS +QVlYWVogB9wACAAXAA8ALgAPYWNzcEFQUEwAAAAAbm9uZQAAAAAAAAAAAAAAAAAAAAAAAPbWAAEAAAAA +0y1hcHBsAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFZGVzYwAA +AMAAAAB5ZHNjbQAAATwAAAgaY3BydAAACVgAAAAjd3RwdAAACXwAAAAUa1RSQwAACZAAAAgMZGVzYwAA +AAAAAAAfR2VuZXJpYyBHcmF5IEdhbW1hIDIuMiBQcm9maWxlAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAG1s +dWMAAAAAAAAAHwAAAAxza1NLAAAALgAAAYRkYURLAAAAOgAAAbJjYUVTAAAAOAAAAex2aVZOAAAAQAAA +AiRwdEJSAAAASgAAAmR1a1VBAAAALAAAAq5mckZVAAAAPgAAAtpodUhVAAAANAAAAxh6aFRXAAAAGgAA +A0xrb0tSAAAAIgAAA2ZuYk5PAAAAOgAAA4hjc0NaAAAAKAAAA8JoZUlMAAAAJAAAA+pyb1JPAAAAKgAA +BA5kZURFAAAATgAABDhpdElUAAAATgAABIZzdlNFAAAAOAAABNR6aENOAAAAGgAABQxqYUpQAAAAJgAA +BSZlbEdSAAAAKgAABUxwdFBPAAAAUgAABXZubE5MAAAAQAAABchlc0VTAAAATAAABgh0aFRIAAAAMgAA +BlR0clRSAAAAJAAABoZmaUZJAAAARgAABqpockhSAAAAPgAABvBwbFBMAAAASgAABy5hckVHAAAALAAA +B3hydVJVAAAAOgAAB6RlblVTAAAAPAAAB94AVgFhAGUAbwBiAGUAYwBuAOEAIABzAGkAdgDhACAAZwBh +AG0AYQAgADIALAAyAEcAZQBuAGUAcgBpAHMAawAgAGcAcgDlACAAMgAsADIAIABnAGEAbQBtAGEALQBw +AHIAbwBmAGkAbABHAGEAbQBtAGEAIABkAGUAIABnAHIAaQBzAG8AcwAgAGcAZQBuAOgAcgBpAGMAYQAg +ADIALgAyAEMepQB1ACAAaADsAG4AaAAgAE0A4AB1ACAAeADhAG0AIABDAGgAdQBuAGcAIABHAGEAbQBt +AGEAIAAyAC4AMgBQAGUAcgBmAGkAbAAgAEcAZQBuAOkAcgBpAGMAbwAgAGQAYQAgAEcAYQBtAGEAIABk +AGUAIABDAGkAbgB6AGEAcwAgADIALAAyBBcEMAQzBDAEOwRMBD0EMAAgAEcAcgBhAHkALQQzBDAEPAQw +ACAAMgAuADIAUAByAG8AZgBpAGwAIABnAOkAbgDpAHIAaQBxAHUAZQAgAGcAcgBpAHMAIABnAGEAbQBt +AGEAIAAyACwAMgDBAGwAdABhAGwA4QBuAG8AcwAgAHMAegD8AHIAawBlACAAZwBhAG0AbQBhACAAMgAu +ADKQGnUocHCWjlFJXqYAMgAuADKCcl9pY8+P8Md8vBgAINaMwMkAIKwQucgAIAAyAC4AMgAg1QS4XNMM +x3wARwBlAG4AZQByAGkAcwBrACAAZwByAOUAIABnAGEAbQBtAGEAIAAyACwAMgAtAHAAcgBvAGYAaQBs +AE8AYgBlAGMAbgDhACABYQBlAGQA4QAgAGcAYQBtAGEAIAAyAC4AMgXSBdAF3gXUACAF0AXkBdUF6AAg +BdsF3AXcBdkAIAAyAC4AMgBHAGEAbQBhACAAZwByAGkAIABnAGUAbgBlAHIAaQBjAQMAIAAyACwAMgBB +AGwAbABnAGUAbQBlAGkAbgBlAHMAIABHAHIAYQB1AHMAdAB1AGYAZQBuAC0AUAByAG8AZgBpAGwAIABH +AGEAbQBtAGEAIAAyACwAMgBQAHIAbwBmAGkAbABvACAAZwByAGkAZwBpAG8AIABnAGUAbgBlAHIAaQBj +AG8AIABkAGUAbABsAGEAIABnAGEAbQBtAGEAIAAyACwAMgBHAGUAbgBlAHIAaQBzAGsAIABnAHIA5QAg +ADIALAAyACAAZwBhAG0AbQBhAHAAcgBvAGYAaQBsZm6QGnBwXqZ8+2VwADIALgAyY8+P8GWHTvZOAIIs +MLAw7DCkMKww8zDeACAAMgAuADIAIDDXMO0w1TChMKQw6wOTA7UDvQO5A7oDzAAgA5MDugPBA7kAIAOT +A6wDvAO8A7EAIAAyAC4AMgBQAGUAcgBmAGkAbAAgAGcAZQBuAOkAcgBpAGMAbwAgAGQAZQAgAGMAaQBu +AHoAZQBuAHQAbwBzACAAZABhACAARwBhAG0AbQBhACAAMgAsADIAQQBsAGcAZQBtAGUAZQBuACAAZwBy +AGkAagBzACAAZwBhAG0AbQBhACAAMgAsADIALQBwAHIAbwBmAGkAZQBsAFAAZQByAGYAaQBsACAAZwBl +AG4A6QByAGkAYwBvACAAZABlACAAZwBhAG0AbQBhACAAZABlACAAZwByAGkAcwBlAHMAIAAyACwAMg4j +DjEOBw4qDjUOQQ4BDiEOIQ4yDkAOAQ4jDiIOTA4XDjEOSA4nDkQOGwAgADIALgAyAEcAZQBuAGUAbAAg +AEcAcgBpACAARwBhAG0AYQAgADIALAAyAFkAbABlAGkAbgBlAG4AIABoAGEAcgBtAGEAYQBuACAAZwBh +AG0AbQBhACAAMgAsADIAIAAtAHAAcgBvAGYAaQBpAGwAaQBHAGUAbgBlAHIAaQENAGsAaQAgAEcAcgBh +AHkAIABHAGEAbQBtAGEAIAAyAC4AMgAgAHAAcgBvAGYAaQBsAFUAbgBpAHcAZQByAHMAYQBsAG4AeQAg +AHAAcgBvAGYAaQBsACAAcwB6AGEAcgBvAVsAYwBpACAAZwBhAG0AbQBhACAAMgAsADIGOgYnBkUGJwAg +ADIALgAyACAGRAZIBkYAIAYxBkUGJwYvBkoAIAY5BicGRQQeBDEESQQwBE8AIARBBDUEQAQwBE8AIAQz +BDAEPAQ8BDAAIAAyACwAMgAtBD8EQAQ+BEQEOAQ7BEwARwBlAG4AZQByAGkAYwAgAEcAcgBhAHkAIABH +AGEAbQBtAGEAIAAyAC4AMgAgAFAAcgBvAGYAaQBsAGUAAHRleHQAAAAAQ29weXJpZ2h0IEFwcGxlIElu +Yy4sIDIwMTIAAFhZWiAAAAAAAADzUQABAAAAARbMY3VydgAAAAAAAAQAAAAABQAKAA8AFAAZAB4AIwAo +AC0AMgA3ADsAQABFAEoATwBUAFkAXgBjAGgAbQByAHcAfACBAIYAiwCQAJUAmgCfAKQAqQCuALIAtwC8 +AMEAxgDLANAA1QDbAOAA5QDrAPAA9gD7AQEBBwENARMBGQEfASUBKwEyATgBPgFFAUwBUgFZAWABZwFu +AXUBfAGDAYsBkgGaAaEBqQGxAbkBwQHJAdEB2QHhAekB8gH6AgMCDAIUAh0CJgIvAjgCQQJLAlQCXQJn +AnECegKEAo4CmAKiAqwCtgLBAssC1QLgAusC9QMAAwsDFgMhAy0DOANDA08DWgNmA3IDfgOKA5YDogOu +A7oDxwPTA+AD7AP5BAYEEwQgBC0EOwRIBFUEYwRxBH4EjASaBKgEtgTEBNME4QTwBP4FDQUcBSsFOgVJ +BVgFZwV3BYYFlgWmBbUFxQXVBeUF9gYGBhYGJwY3BkgGWQZqBnsGjAadBq8GwAbRBuMG9QcHBxkHKwc9 +B08HYQd0B4YHmQesB78H0gflB/gICwgfCDIIRghaCG4IggiWCKoIvgjSCOcI+wkQCSUJOglPCWQJeQmP +CaQJugnPCeUJ+woRCicKPQpUCmoKgQqYCq4KxQrcCvMLCwsiCzkLUQtpC4ALmAuwC8gL4Qv5DBIMKgxD +DFwMdQyODKcMwAzZDPMNDQ0mDUANWg10DY4NqQ3DDd4N+A4TDi4OSQ5kDn8Omw62DtIO7g8JDyUPQQ9e +D3oPlg+zD88P7BAJECYQQxBhEH4QmxC5ENcQ9RETETERTxFtEYwRqhHJEegSBxImEkUSZBKEEqMSwxLj +EwMTIxNDE2MTgxOkE8UT5RQGFCcUSRRqFIsUrRTOFPAVEhU0FVYVeBWbFb0V4BYDFiYWSRZsFo8WshbW +FvoXHRdBF2UXiReuF9IX9xgbGEAYZRiKGK8Y1Rj6GSAZRRlrGZEZtxndGgQaKhpRGncanhrFGuwbFBs7 +G2MbihuyG9ocAhwqHFIcexyjHMwc9R0eHUcdcB2ZHcMd7B4WHkAeah6UHr4e6R8THz4faR+UH78f6iAV +IEEgbCCYIMQg8CEcIUghdSGhIc4h+yInIlUigiKvIt0jCiM4I2YjlCPCI/AkHyRNJHwkqyTaJQklOCVo +JZclxyX3JicmVyaHJrcm6CcYJ0kneierJ9woDSg/KHEooijUKQYpOClrKZ0p0CoCKjUqaCqbKs8rAis2 +K2krnSvRLAUsOSxuLKIs1y0MLUEtdi2rLeEuFi5MLoIuty7uLyQvWi+RL8cv/jA1MGwwpDDbMRIxSjGC +Mbox8jIqMmMymzLUMw0zRjN/M7gz8TQrNGU0njTYNRM1TTWHNcI1/TY3NnI2rjbpNyQ3YDecN9c4FDhQ +OIw4yDkFOUI5fzm8Ofk6Njp0OrI67zstO2s7qjvoPCc8ZTykPOM9Ij1hPaE94D4gPmA+oD7gPyE/YT+i +P+JAI0BkQKZA50EpQWpBrEHuQjBCckK1QvdDOkN9Q8BEA0RHRIpEzkUSRVVFmkXeRiJGZ0arRvBHNUd7 +R8BIBUhLSJFI10kdSWNJqUnwSjdKfUrESwxLU0uaS+JMKkxyTLpNAk1KTZNN3E4lTm5Ot08AT0lPk0/d +UCdQcVC7UQZRUFGbUeZSMVJ8UsdTE1NfU6pT9lRCVI9U21UoVXVVwlYPVlxWqVb3V0RXklfgWC9YfVjL +WRpZaVm4WgdaVlqmWvVbRVuVW+VcNVyGXNZdJ114XcleGl5sXr1fD19hX7NgBWBXYKpg/GFPYaJh9WJJ +Ypxi8GNDY5dj62RAZJRk6WU9ZZJl52Y9ZpJm6Gc9Z5Nn6Wg/aJZo7GlDaZpp8WpIap9q92tPa6dr/2xX +bK9tCG1gbbluEm5rbsRvHm94b9FwK3CGcOBxOnGVcfByS3KmcwFzXXO4dBR0cHTMdSh1hXXhdj52m3b4 +d1Z3s3gReG54zHkqeYl553pGeqV7BHtje8J8IXyBfOF9QX2hfgF+Yn7CfyN/hH/lgEeAqIEKgWuBzYIw +gpKC9INXg7qEHYSAhOOFR4Wrhg6GcobXhzuHn4gEiGmIzokziZmJ/opkisqLMIuWi/yMY4zKjTGNmI3/ +jmaOzo82j56QBpBukNaRP5GokhGSepLjk02TtpQglIqU9JVflcmWNJaflwqXdZfgmEyYuJkkmZCZ/Jpo +mtWbQpuvnByciZz3nWSd0p5Anq6fHZ+Ln/qgaaDYoUehtqImopajBqN2o+akVqTHpTilqaYapoum/adu +p+CoUqjEqTepqaocqo+rAqt1q+msXKzQrUStuK4trqGvFq+LsACwdbDqsWCx1rJLssKzOLOutCW0nLUT +tYq2AbZ5tvC3aLfguFm40blKucK6O7q1uy67p7whvJu9Fb2Pvgq+hL7/v3q/9cBwwOzBZ8Hjwl/C28NY +w9TEUcTOxUvFyMZGxsPHQce/yD3IvMk6ybnKOMq3yzbLtsw1zLXNNc21zjbOts83z7jQOdC60TzRvtI/ +0sHTRNPG1EnUy9VO1dHWVdbY11zX4Nhk2OjZbNnx2nba+9uA3AXcit0Q3ZbeHN6i3ynfr+A24L3hROHM +4lPi2+Nj4+vkc+T85YTmDeaW5x/nqegy6LzpRunQ6lvq5etw6/vshu0R7ZzuKO6070DvzPBY8OXxcvH/ +8ozzGfOn9DT0wvVQ9d72bfb794r4Gfio+Tj5x/pX+uf7d/wH/Jj9Kf26/kv+3P9t///SLS4vMFokY2xh +c3NuYW1lWCRjbGFzc2VzXxAQTlNCaXRtYXBJbWFnZVJlcKMvMTJaTlNJbWFnZVJlcFhOU09iamVjdNIt +LjQ1V05TQXJyYXmiNDLSLS43OF5OU011dGFibGVBcnJheaM3NDLTOjsOPD0+V05TV2hpdGVcTlNDb2xv +clNwYWNlRDAgMAAQA4AM0i0uQEFXTlNDb2xvcqJAMtItLkNEV05TSW1hZ2WiQzIACAARABoAJAApADIA +NwBJAEwAUQBTAGIAaAB3AH4AhQCSAJkApQCtALwAvgDAAMUAxwDJAMsAzQDUANkA5ADmAOgA6gDvAPIA +9AD2APgA/wEWATIBNAE2E6YTqxO2E78T0hPWE+ET6hPvE/cT+hP/FA4UEhQZFCEULhQzFDUUNxQ8FEQU +RxRMFFQAAAAAAAACAQAAAAAAAABFAAAAAAAAAAAAAAAAAAAUVw diff --git a/i18n/src/main/resources/sk.lproj/Browser.xib b/i18n/src/main/resources/sk.lproj/Browser.xib index 7f7e09cf8f..f294dd1e53 100644 --- a/i18n/src/main/resources/sk.lproj/Browser.xib +++ b/i18n/src/main/resources/sk.lproj/Browser.xib @@ -1,7 +1,7 @@ - + - + @@ -38,7 +38,7 @@ - + @@ -62,27 +62,27 @@ @@ -256,7 +256,7 @@ - + @@ -553,95 +553,95 @@ DQ YnBsaXN0MDDUAQIDBAUGBwpYJHZlcnNpb25ZJGFyY2hpdmVyVCR0b3BYJG9iamVjdHMSAAGGoF8QD05T -S2V5ZWRBcmNoaXZlctEICVRyb290gAGuCwwZGh8UJCkqMTQ3PUBVJG51bGzWDQ4PEBESExQVFhcYVk5T -U2l6ZV5OU1Jlc2l6aW5nTW9kZVYkY2xhc3NcTlNJbWFnZUZsYWdzVk5TUmVwc1dOU0NvbG9ygAIQAIAN -EiDDAACAA4ALVnsxLCAxfdIbDxweWk5TLm9iamVjdHOhHYAEgArSGw8gI6IhIoAFgAaACdMPJSYnKBRf -EBROU1RJRkZSZXByZXNlbnRhdGlvbl8QGU5TSW50ZXJuYWxMYXlvdXREaXJlY3Rpb26ACIAHTxESbE1N -ACoAAAAKAAAAEAEAAAMAAAABAAEAAAEBAAMAAAABAAEAAAECAAMAAAACAAgACAEDAAMAAAABAAEAAAEG -AAMAAAABAAEAAAEKAAMAAAABAAEAAAERAAQAAAABAAAACAESAAMAAAABAAEAAAEVAAMAAAABAAIAAAEW -AAMAAAABAAEAAAEXAAQAAAABAAAAAgEcAAMAAAABAAEAAAEoAAMAAAABAAIAAAFSAAMAAAABAAEAAAFT -AAMAAAACAAEAAYdzAAcAABGcAAAA0AAAAAAAABGcYXBwbAIAAABtbnRyR1JBWVhZWiAH3AAIABcADwAu -AA9hY3NwQVBQTAAAAABub25lAAAAAAAAAAAAAAAAAAAAAAAA9tYAAQAAAADTLWFwcGwAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAVkZXNjAAAAwAAAAHlkc2NtAAABPAAA -CBpjcHJ0AAAJWAAAACN3dHB0AAAJfAAAABRrVFJDAAAJkAAACAxkZXNjAAAAAAAAAB9HZW5lcmljIEdy -YXkgR2FtbWEgMi4yIFByb2ZpbGUAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAbWx1YwAAAAAAAAAfAAAADHNr -U0sAAAAuAAABhGRhREsAAAA6AAABsmNhRVMAAAA4AAAB7HZpVk4AAABAAAACJHB0QlIAAABKAAACZHVr -VUEAAAAsAAACrmZyRlUAAAA+AAAC2mh1SFUAAAA0AAADGHpoVFcAAAAaAAADTGtvS1IAAAAiAAADZm5i -Tk8AAAA6AAADiGNzQ1oAAAAoAAADwmhlSUwAAAAkAAAD6nJvUk8AAAAqAAAEDmRlREUAAABOAAAEOGl0 -SVQAAABOAAAEhnN2U0UAAAA4AAAE1HpoQ04AAAAaAAAFDGphSlAAAAAmAAAFJmVsR1IAAAAqAAAFTHB0 -UE8AAABSAAAFdm5sTkwAAABAAAAFyGVzRVMAAABMAAAGCHRoVEgAAAAyAAAGVHRyVFIAAAAkAAAGhmZp -RkkAAABGAAAGqmhySFIAAAA+AAAG8HBsUEwAAABKAAAHLmFyRUcAAAAsAAAHeHJ1UlUAAAA6AAAHpGVu -VVMAAAA8AAAH3gBWAWEAZQBvAGIAZQBjAG4A4QAgAHMAaQB2AOEAIABnAGEAbQBhACAAMgAsADIARwBl -AG4AZQByAGkAcwBrACAAZwByAOUAIAAyACwAMgAgAGcAYQBtAG0AYQAtAHAAcgBvAGYAaQBsAEcAYQBt -AG0AYQAgAGQAZQAgAGcAcgBpAHMAbwBzACAAZwBlAG4A6AByAGkAYwBhACAAMgAuADIAQx6lAHUAIABo -AOwAbgBoACAATQDgAHUAIAB4AOEAbQAgAEMAaAB1AG4AZwAgAEcAYQBtAG0AYQAgADIALgAyAFAAZQBy -AGYAaQBsACAARwBlAG4A6QByAGkAYwBvACAAZABhACAARwBhAG0AYQAgAGQAZQAgAEMAaQBuAHoAYQBz -ACAAMgAsADIEFwQwBDMEMAQ7BEwEPQQwACAARwByAGEAeQAtBDMEMAQ8BDAAIAAyAC4AMgBQAHIAbwBm -AGkAbAAgAGcA6QBuAOkAcgBpAHEAdQBlACAAZwByAGkAcwAgAGcAYQBtAG0AYQAgADIALAAyAMEAbAB0 -AGEAbADhAG4AbwBzACAAcwB6APwAcgBrAGUAIABnAGEAbQBtAGEAIAAyAC4AMpAadShwcJaOUUlepgAy -AC4AMoJyX2ljz4/wx3y8GAAg1ozAyQAgrBC5yAAgADIALgAyACDVBLhc0wzHfABHAGUAbgBlAHIAaQBz -AGsAIABnAHIA5QAgAGcAYQBtAG0AYQAgADIALAAyAC0AcAByAG8AZgBpAGwATwBiAGUAYwBuAOEAIAFh -AGUAZADhACAAZwBhAG0AYQAgADIALgAyBdIF0AXeBdQAIAXQBeQF1QXoACAF2wXcBdwF2QAgADIALgAy -AEcAYQBtAGEAIABnAHIAaQAgAGcAZQBuAGUAcgBpAGMBAwAgADIALAAyAEEAbABsAGcAZQBtAGUAaQBu -AGUAcwAgAEcAcgBhAHUAcwB0AHUAZgBlAG4ALQBQAHIAbwBmAGkAbAAgAEcAYQBtAG0AYQAgADIALAAy -AFAAcgBvAGYAaQBsAG8AIABnAHIAaQBnAGkAbwAgAGcAZQBuAGUAcgBpAGMAbwAgAGQAZQBsAGwAYQAg -AGcAYQBtAG0AYQAgADIALAAyAEcAZQBuAGUAcgBpAHMAawAgAGcAcgDlACAAMgAsADIAIABnAGEAbQBt -AGEAcAByAG8AZgBpAGxmbpAacHBepnz7ZXAAMgAuADJjz4/wZYdO9k4AgiwwsDDsMKQwrDDzMN4AIAAy -AC4AMgAgMNcw7TDVMKEwpDDrA5MDtQO9A7kDugPMACADkwO6A8EDuQAgA5MDrAO8A7wDsQAgADIALgAy -AFAAZQByAGYAaQBsACAAZwBlAG4A6QByAGkAYwBvACAAZABlACAAYwBpAG4AegBlAG4AdABvAHMAIABk -AGEAIABHAGEAbQBtAGEAIAAyACwAMgBBAGwAZwBlAG0AZQBlAG4AIABnAHIAaQBqAHMAIABnAGEAbQBt -AGEAIAAyACwAMgAtAHAAcgBvAGYAaQBlAGwAUABlAHIAZgBpAGwAIABnAGUAbgDpAHIAaQBjAG8AIABk -AGUAIABnAGEAbQBtAGEAIABkAGUAIABnAHIAaQBzAGUAcwAgADIALAAyDiMOMQ4HDioONQ5BDgEOIQ4h -DjIOQA4BDiMOIg5MDhcOMQ5IDicORA4bACAAMgAuADIARwBlAG4AZQBsACAARwByAGkAIABHAGEAbQBh -ACAAMgAsADIAWQBsAGUAaQBuAGUAbgAgAGgAYQByAG0AYQBhAG4AIABnAGEAbQBtAGEAIAAyACwAMgAg -AC0AcAByAG8AZgBpAGkAbABpAEcAZQBuAGUAcgBpAQ0AawBpACAARwByAGEAeQAgAEcAYQBtAG0AYQAg -ADIALgAyACAAcAByAG8AZgBpAGwAVQBuAGkAdwBlAHIAcwBhAGwAbgB5ACAAcAByAG8AZgBpAGwAIABz -AHoAYQByAG8BWwBjAGkAIABnAGEAbQBtAGEAIAAyACwAMgY6BicGRQYnACAAMgAuADIAIAZEBkgGRgAg -BjEGRQYnBi8GSgAgBjkGJwZFBB4EMQRJBDAETwAgBEEENQRABDAETwAgBDMEMAQ8BDwEMAAgADIALAAy -AC0EPwRABD4ERAQ4BDsETABHAGUAbgBlAHIAaQBjACAARwByAGEAeQAgAEcAYQBtAG0AYQAgADIALgAy -ACAAUAByAG8AZgBpAGwAZQAAdGV4dAAAAABDb3B5cmlnaHQgQXBwbGUgSW5jLiwgMjAxMgAAWFlaIAAA -AAAAAPNRAAEAAAABFsxjdXJ2AAAAAAAABAAAAAAFAAoADwAUABkAHgAjACgALQAyADcAOwBAAEUASgBP -AFQAWQBeAGMAaABtAHIAdwB8AIEAhgCLAJAAlQCaAJ8ApACpAK4AsgC3ALwAwQDGAMsA0ADVANsA4ADl -AOsA8AD2APsBAQEHAQ0BEwEZAR8BJQErATIBOAE+AUUBTAFSAVkBYAFnAW4BdQF8AYMBiwGSAZoBoQGp -AbEBuQHBAckB0QHZAeEB6QHyAfoCAwIMAhQCHQImAi8COAJBAksCVAJdAmcCcQJ6AoQCjgKYAqICrAK2 -AsECywLVAuAC6wL1AwADCwMWAyEDLQM4A0MDTwNaA2YDcgN+A4oDlgOiA64DugPHA9MD4APsA/kEBgQT -BCAELQQ7BEgEVQRjBHEEfgSMBJoEqAS2BMQE0wThBPAE/gUNBRwFKwU6BUkFWAVnBXcFhgWWBaYFtQXF -BdUF5QX2BgYGFgYnBjcGSAZZBmoGewaMBp0GrwbABtEG4wb1BwcHGQcrBz0HTwdhB3QHhgeZB6wHvwfS -B+UH+AgLCB8IMghGCFoIbgiCCJYIqgi+CNII5wj7CRAJJQk6CU8JZAl5CY8JpAm6Cc8J5Qn7ChEKJwo9 -ClQKagqBCpgKrgrFCtwK8wsLCyILOQtRC2kLgAuYC7ALyAvhC/kMEgwqDEMMXAx1DI4MpwzADNkM8w0N -DSYNQA1aDXQNjg2pDcMN3g34DhMOLg5JDmQOfw6bDrYO0g7uDwkPJQ9BD14Peg+WD7MPzw/sEAkQJhBD -EGEQfhCbELkQ1xD1ERMRMRFPEW0RjBGqEckR6BIHEiYSRRJkEoQSoxLDEuMTAxMjE0MTYxODE6QTxRPl -FAYUJxRJFGoUixStFM4U8BUSFTQVVhV4FZsVvRXgFgMWJhZJFmwWjxayFtYW+hcdF0EXZReJF64X0hf3 -GBsYQBhlGIoYrxjVGPoZIBlFGWsZkRm3Gd0aBBoqGlEadxqeGsUa7BsUGzsbYxuKG7Ib2hwCHCocUhx7 -HKMczBz1HR4dRx1wHZkdwx3sHhYeQB5qHpQevh7pHxMfPh9pH5Qfvx/qIBUgQSBsIJggxCDwIRwhSCF1 -IaEhziH7IiciVSKCIq8i3SMKIzgjZiOUI8Ij8CQfJE0kfCSrJNolCSU4JWgllyXHJfcmJyZXJocmtybo -JxgnSSd6J6sn3CgNKD8ocSiiKNQpBik4KWspnSnQKgIqNSpoKpsqzysCKzYraSudK9EsBSw5LG4soizX -LQwtQS12Last4S4WLkwugi63Lu4vJC9aL5Evxy/+MDUwbDCkMNsxEjFKMYIxujHyMioyYzKbMtQzDTNG -M38zuDPxNCs0ZTSeNNg1EzVNNYc1wjX9Njc2cjauNuk3JDdgN5w31zgUOFA4jDjIOQU5Qjl/Obw5+To2 -OnQ6sjrvOy07azuqO+g8JzxlPKQ84z0iPWE9oT3gPiA+YD6gPuA/IT9hP6I/4kAjQGRApkDnQSlBakGs -Qe5CMEJyQrVC90M6Q31DwEQDREdEikTORRJFVUWaRd5GIkZnRqtG8Ec1R3tHwEgFSEtIkUjXSR1JY0mp -SfBKN0p9SsRLDEtTS5pL4kwqTHJMuk0CTUpNk03cTiVObk63TwBPSU+TT91QJ1BxULtRBlFQUZtR5lIx -UnxSx1MTU19TqlP2VEJUj1TbVShVdVXCVg9WXFapVvdXRFeSV+BYL1h9WMtZGllpWbhaB1pWWqZa9VtF -W5Vb5Vw1XIZc1l0nXXhdyV4aXmxevV8PX2Ffs2AFYFdgqmD8YU9homH1YklinGLwY0Njl2PrZEBklGTp -ZT1lkmXnZj1mkmboZz1nk2fpaD9olmjsaUNpmmnxakhqn2r3a09rp2v/bFdsr20IbWBtuW4SbmtuxG8e -b3hv0XArcIZw4HE6cZVx8HJLcqZzAXNdc7h0FHRwdMx1KHWFdeF2Pnabdvh3VnezeBF4bnjMeSp5iXnn -ekZ6pXsEe2N7wnwhfIF84X1BfaF+AX5ifsJ/I3+Ef+WAR4CogQqBa4HNgjCCkoL0g1eDuoQdhICE44VH -hauGDoZyhteHO4efiASIaYjOiTOJmYn+imSKyoswi5aL/IxjjMqNMY2Yjf+OZo7OjzaPnpAGkG6Q1pE/ -kaiSEZJ6kuOTTZO2lCCUipT0lV+VyZY0lp+XCpd1l+CYTJi4mSSZkJn8mmia1ZtCm6+cHJyJnPedZJ3S -nkCerp8dn4uf+qBpoNihR6G2oiailqMGo3aj5qRWpMelOKWpphqmi6b9p26n4KhSqMSpN6mpqhyqj6sC -q3Wr6axcrNCtRK24ri2uoa8Wr4uwALB1sOqxYLHWskuywrM4s660JbSctRO1irYBtnm28Ldot+C4WbjR -uUq5wro7urW7LrunvCG8m70VvY++Cr6Evv+/er/1wHDA7MFnwePCX8Lbw1jD1MRRxM7FS8XIxkbGw8dB -x7/IPci8yTrJuco4yrfLNsu2zDXMtc01zbXONs62zzfPuNA50LrRPNG+0j/SwdNE08bUSdTL1U7V0dZV -1tjXXNfg2GTY6Nls2fHadtr724DcBdyK3RDdlt4c3qLfKd+v4DbgveFE4cziU+Lb42Pj6+Rz5PzlhOYN -5pbnH+ep6DLovOlG6dDqW+rl63Dr++yG7RHtnO4o7rTvQO/M8Fjw5fFy8f/yjPMZ86f0NPTC9VD13vZt -9vv3ivgZ+Kj5OPnH+lf65/t3/Af8mP0p/br+S/7c/23//9IrLC0uWiRjbGFzc25hbWVYJGNsYXNzZXNf -EBBOU0JpdG1hcEltYWdlUmVwoy0vMFpOU0ltYWdlUmVwWE5TT2JqZWN00issMjNXTlNBcnJheaIyMNIr -LDU2Xk5TTXV0YWJsZUFycmF5ozUyMNM4OQ86OzxXTlNXaGl0ZVxOU0NvbG9yU3BhY2VEMCAwABADgAzS -Kyw+P1dOU0NvbG9yoj4w0issQUJXTlNJbWFnZaJBMAAIABEAGgAkACkAMgA3AEkATABRAFMAYgBoAHUA -fACLAJIAnwCmAK4AsACyALQAuQC7AL0AxADJANQA1gDYANoA3wDiAOQA5gDoAO8BBgEiASQBJhOWE5sT -phOvE8ITxhPRE9oT3xPnE+oT7xP+FAIUCRQRFB4UIxQlFCcULBQ0FDcUPBREAAAAAAAAAgEAAAAAAAAA -QwAAAAAAAAAAAAAAAAAAFEc +S2V5ZWRBcmNoaXZlctEICVRyb290gAGuCwwbHCEaJissMzY5P0JVJG51bGzXDQ4PEBESExQVFhcYGRpW +TlNTaXplViRjbGFzc1xOU0ltYWdlRmxhZ3NWTlNSZXBzW05TVGludENvbG9yV05TQ29sb3JeTlNSZXNp +emluZ01vZGWAAoANEiDDAACAA4AAgAsQAFZ7MSwgMX3SHQ4eIFpOUy5vYmplY3RzoR+ABIAK0h0OIiWi +IySABYAGgAnTDicoKSoaXxAUTlNUSUZGUmVwcmVzZW50YXRpb25fEBlOU0ludGVybmFsTGF5b3V0RGly +ZWN0aW9ugAiAB08REmxNTQAqAAAACgAAABABAAADAAAAAQABAAABAQADAAAAAQABAAABAgADAAAAAgAI +AAgBAwADAAAAAQABAAABBgADAAAAAQABAAABCgADAAAAAQABAAABEQAEAAAAAQAAAAgBEgADAAAAAQAB +AAABFQADAAAAAQACAAABFgADAAAAAQABAAABFwAEAAAAAQAAAAIBHAADAAAAAQABAAABKAADAAAAAQAC +AAABUgADAAAAAQABAAABUwADAAAAAgABAAGHcwAHAAARnAAAANAAAAAAAAARnGFwcGwCAAAAbW50ckdS +QVlYWVogB9wACAAXAA8ALgAPYWNzcEFQUEwAAAAAbm9uZQAAAAAAAAAAAAAAAAAAAAAAAPbWAAEAAAAA +0y1hcHBsAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFZGVzYwAA +AMAAAAB5ZHNjbQAAATwAAAgaY3BydAAACVgAAAAjd3RwdAAACXwAAAAUa1RSQwAACZAAAAgMZGVzYwAA +AAAAAAAfR2VuZXJpYyBHcmF5IEdhbW1hIDIuMiBQcm9maWxlAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAG1s +dWMAAAAAAAAAHwAAAAxza1NLAAAALgAAAYRkYURLAAAAOgAAAbJjYUVTAAAAOAAAAex2aVZOAAAAQAAA +AiRwdEJSAAAASgAAAmR1a1VBAAAALAAAAq5mckZVAAAAPgAAAtpodUhVAAAANAAAAxh6aFRXAAAAGgAA +A0xrb0tSAAAAIgAAA2ZuYk5PAAAAOgAAA4hjc0NaAAAAKAAAA8JoZUlMAAAAJAAAA+pyb1JPAAAAKgAA +BA5kZURFAAAATgAABDhpdElUAAAATgAABIZzdlNFAAAAOAAABNR6aENOAAAAGgAABQxqYUpQAAAAJgAA +BSZlbEdSAAAAKgAABUxwdFBPAAAAUgAABXZubE5MAAAAQAAABchlc0VTAAAATAAABgh0aFRIAAAAMgAA +BlR0clRSAAAAJAAABoZmaUZJAAAARgAABqpockhSAAAAPgAABvBwbFBMAAAASgAABy5hckVHAAAALAAA +B3hydVJVAAAAOgAAB6RlblVTAAAAPAAAB94AVgFhAGUAbwBiAGUAYwBuAOEAIABzAGkAdgDhACAAZwBh +AG0AYQAgADIALAAyAEcAZQBuAGUAcgBpAHMAawAgAGcAcgDlACAAMgAsADIAIABnAGEAbQBtAGEALQBw +AHIAbwBmAGkAbABHAGEAbQBtAGEAIABkAGUAIABnAHIAaQBzAG8AcwAgAGcAZQBuAOgAcgBpAGMAYQAg +ADIALgAyAEMepQB1ACAAaADsAG4AaAAgAE0A4AB1ACAAeADhAG0AIABDAGgAdQBuAGcAIABHAGEAbQBt +AGEAIAAyAC4AMgBQAGUAcgBmAGkAbAAgAEcAZQBuAOkAcgBpAGMAbwAgAGQAYQAgAEcAYQBtAGEAIABk +AGUAIABDAGkAbgB6AGEAcwAgADIALAAyBBcEMAQzBDAEOwRMBD0EMAAgAEcAcgBhAHkALQQzBDAEPAQw +ACAAMgAuADIAUAByAG8AZgBpAGwAIABnAOkAbgDpAHIAaQBxAHUAZQAgAGcAcgBpAHMAIABnAGEAbQBt +AGEAIAAyACwAMgDBAGwAdABhAGwA4QBuAG8AcwAgAHMAegD8AHIAawBlACAAZwBhAG0AbQBhACAAMgAu +ADKQGnUocHCWjlFJXqYAMgAuADKCcl9pY8+P8Md8vBgAINaMwMkAIKwQucgAIAAyAC4AMgAg1QS4XNMM +x3wARwBlAG4AZQByAGkAcwBrACAAZwByAOUAIABnAGEAbQBtAGEAIAAyACwAMgAtAHAAcgBvAGYAaQBs +AE8AYgBlAGMAbgDhACABYQBlAGQA4QAgAGcAYQBtAGEAIAAyAC4AMgXSBdAF3gXUACAF0AXkBdUF6AAg +BdsF3AXcBdkAIAAyAC4AMgBHAGEAbQBhACAAZwByAGkAIABnAGUAbgBlAHIAaQBjAQMAIAAyACwAMgBB +AGwAbABnAGUAbQBlAGkAbgBlAHMAIABHAHIAYQB1AHMAdAB1AGYAZQBuAC0AUAByAG8AZgBpAGwAIABH +AGEAbQBtAGEAIAAyACwAMgBQAHIAbwBmAGkAbABvACAAZwByAGkAZwBpAG8AIABnAGUAbgBlAHIAaQBj +AG8AIABkAGUAbABsAGEAIABnAGEAbQBtAGEAIAAyACwAMgBHAGUAbgBlAHIAaQBzAGsAIABnAHIA5QAg +ADIALAAyACAAZwBhAG0AbQBhAHAAcgBvAGYAaQBsZm6QGnBwXqZ8+2VwADIALgAyY8+P8GWHTvZOAIIs +MLAw7DCkMKww8zDeACAAMgAuADIAIDDXMO0w1TChMKQw6wOTA7UDvQO5A7oDzAAgA5MDugPBA7kAIAOT +A6wDvAO8A7EAIAAyAC4AMgBQAGUAcgBmAGkAbAAgAGcAZQBuAOkAcgBpAGMAbwAgAGQAZQAgAGMAaQBu +AHoAZQBuAHQAbwBzACAAZABhACAARwBhAG0AbQBhACAAMgAsADIAQQBsAGcAZQBtAGUAZQBuACAAZwBy +AGkAagBzACAAZwBhAG0AbQBhACAAMgAsADIALQBwAHIAbwBmAGkAZQBsAFAAZQByAGYAaQBsACAAZwBl +AG4A6QByAGkAYwBvACAAZABlACAAZwBhAG0AbQBhACAAZABlACAAZwByAGkAcwBlAHMAIAAyACwAMg4j +DjEOBw4qDjUOQQ4BDiEOIQ4yDkAOAQ4jDiIOTA4XDjEOSA4nDkQOGwAgADIALgAyAEcAZQBuAGUAbAAg +AEcAcgBpACAARwBhAG0AYQAgADIALAAyAFkAbABlAGkAbgBlAG4AIABoAGEAcgBtAGEAYQBuACAAZwBh +AG0AbQBhACAAMgAsADIAIAAtAHAAcgBvAGYAaQBpAGwAaQBHAGUAbgBlAHIAaQENAGsAaQAgAEcAcgBh +AHkAIABHAGEAbQBtAGEAIAAyAC4AMgAgAHAAcgBvAGYAaQBsAFUAbgBpAHcAZQByAHMAYQBsAG4AeQAg +AHAAcgBvAGYAaQBsACAAcwB6AGEAcgBvAVsAYwBpACAAZwBhAG0AbQBhACAAMgAsADIGOgYnBkUGJwAg +ADIALgAyACAGRAZIBkYAIAYxBkUGJwYvBkoAIAY5BicGRQQeBDEESQQwBE8AIARBBDUEQAQwBE8AIAQz +BDAEPAQ8BDAAIAAyACwAMgAtBD8EQAQ+BEQEOAQ7BEwARwBlAG4AZQByAGkAYwAgAEcAcgBhAHkAIABH +AGEAbQBtAGEAIAAyAC4AMgAgAFAAcgBvAGYAaQBsAGUAAHRleHQAAAAAQ29weXJpZ2h0IEFwcGxlIElu +Yy4sIDIwMTIAAFhZWiAAAAAAAADzUQABAAAAARbMY3VydgAAAAAAAAQAAAAABQAKAA8AFAAZAB4AIwAo +AC0AMgA3ADsAQABFAEoATwBUAFkAXgBjAGgAbQByAHcAfACBAIYAiwCQAJUAmgCfAKQAqQCuALIAtwC8 +AMEAxgDLANAA1QDbAOAA5QDrAPAA9gD7AQEBBwENARMBGQEfASUBKwEyATgBPgFFAUwBUgFZAWABZwFu +AXUBfAGDAYsBkgGaAaEBqQGxAbkBwQHJAdEB2QHhAekB8gH6AgMCDAIUAh0CJgIvAjgCQQJLAlQCXQJn +AnECegKEAo4CmAKiAqwCtgLBAssC1QLgAusC9QMAAwsDFgMhAy0DOANDA08DWgNmA3IDfgOKA5YDogOu +A7oDxwPTA+AD7AP5BAYEEwQgBC0EOwRIBFUEYwRxBH4EjASaBKgEtgTEBNME4QTwBP4FDQUcBSsFOgVJ +BVgFZwV3BYYFlgWmBbUFxQXVBeUF9gYGBhYGJwY3BkgGWQZqBnsGjAadBq8GwAbRBuMG9QcHBxkHKwc9 +B08HYQd0B4YHmQesB78H0gflB/gICwgfCDIIRghaCG4IggiWCKoIvgjSCOcI+wkQCSUJOglPCWQJeQmP +CaQJugnPCeUJ+woRCicKPQpUCmoKgQqYCq4KxQrcCvMLCwsiCzkLUQtpC4ALmAuwC8gL4Qv5DBIMKgxD +DFwMdQyODKcMwAzZDPMNDQ0mDUANWg10DY4NqQ3DDd4N+A4TDi4OSQ5kDn8Omw62DtIO7g8JDyUPQQ9e +D3oPlg+zD88P7BAJECYQQxBhEH4QmxC5ENcQ9RETETERTxFtEYwRqhHJEegSBxImEkUSZBKEEqMSwxLj +EwMTIxNDE2MTgxOkE8UT5RQGFCcUSRRqFIsUrRTOFPAVEhU0FVYVeBWbFb0V4BYDFiYWSRZsFo8WshbW +FvoXHRdBF2UXiReuF9IX9xgbGEAYZRiKGK8Y1Rj6GSAZRRlrGZEZtxndGgQaKhpRGncanhrFGuwbFBs7 +G2MbihuyG9ocAhwqHFIcexyjHMwc9R0eHUcdcB2ZHcMd7B4WHkAeah6UHr4e6R8THz4faR+UH78f6iAV +IEEgbCCYIMQg8CEcIUghdSGhIc4h+yInIlUigiKvIt0jCiM4I2YjlCPCI/AkHyRNJHwkqyTaJQklOCVo +JZclxyX3JicmVyaHJrcm6CcYJ0kneierJ9woDSg/KHEooijUKQYpOClrKZ0p0CoCKjUqaCqbKs8rAis2 +K2krnSvRLAUsOSxuLKIs1y0MLUEtdi2rLeEuFi5MLoIuty7uLyQvWi+RL8cv/jA1MGwwpDDbMRIxSjGC +Mbox8jIqMmMymzLUMw0zRjN/M7gz8TQrNGU0njTYNRM1TTWHNcI1/TY3NnI2rjbpNyQ3YDecN9c4FDhQ +OIw4yDkFOUI5fzm8Ofk6Njp0OrI67zstO2s7qjvoPCc8ZTykPOM9Ij1hPaE94D4gPmA+oD7gPyE/YT+i +P+JAI0BkQKZA50EpQWpBrEHuQjBCckK1QvdDOkN9Q8BEA0RHRIpEzkUSRVVFmkXeRiJGZ0arRvBHNUd7 +R8BIBUhLSJFI10kdSWNJqUnwSjdKfUrESwxLU0uaS+JMKkxyTLpNAk1KTZNN3E4lTm5Ot08AT0lPk0/d +UCdQcVC7UQZRUFGbUeZSMVJ8UsdTE1NfU6pT9lRCVI9U21UoVXVVwlYPVlxWqVb3V0RXklfgWC9YfVjL +WRpZaVm4WgdaVlqmWvVbRVuVW+VcNVyGXNZdJ114XcleGl5sXr1fD19hX7NgBWBXYKpg/GFPYaJh9WJJ +Ypxi8GNDY5dj62RAZJRk6WU9ZZJl52Y9ZpJm6Gc9Z5Nn6Wg/aJZo7GlDaZpp8WpIap9q92tPa6dr/2xX +bK9tCG1gbbluEm5rbsRvHm94b9FwK3CGcOBxOnGVcfByS3KmcwFzXXO4dBR0cHTMdSh1hXXhdj52m3b4 +d1Z3s3gReG54zHkqeYl553pGeqV7BHtje8J8IXyBfOF9QX2hfgF+Yn7CfyN/hH/lgEeAqIEKgWuBzYIw +gpKC9INXg7qEHYSAhOOFR4Wrhg6GcobXhzuHn4gEiGmIzokziZmJ/opkisqLMIuWi/yMY4zKjTGNmI3/ +jmaOzo82j56QBpBukNaRP5GokhGSepLjk02TtpQglIqU9JVflcmWNJaflwqXdZfgmEyYuJkkmZCZ/Jpo +mtWbQpuvnByciZz3nWSd0p5Anq6fHZ+Ln/qgaaDYoUehtqImopajBqN2o+akVqTHpTilqaYapoum/adu +p+CoUqjEqTepqaocqo+rAqt1q+msXKzQrUStuK4trqGvFq+LsACwdbDqsWCx1rJLssKzOLOutCW0nLUT +tYq2AbZ5tvC3aLfguFm40blKucK6O7q1uy67p7whvJu9Fb2Pvgq+hL7/v3q/9cBwwOzBZ8Hjwl/C28NY +w9TEUcTOxUvFyMZGxsPHQce/yD3IvMk6ybnKOMq3yzbLtsw1zLXNNc21zjbOts83z7jQOdC60TzRvtI/ +0sHTRNPG1EnUy9VO1dHWVdbY11zX4Nhk2OjZbNnx2nba+9uA3AXcit0Q3ZbeHN6i3ynfr+A24L3hROHM +4lPi2+Nj4+vkc+T85YTmDeaW5x/nqegy6LzpRunQ6lvq5etw6/vshu0R7ZzuKO6070DvzPBY8OXxcvH/ +8ozzGfOn9DT0wvVQ9d72bfb794r4Gfio+Tj5x/pX+uf7d/wH/Jj9Kf26/kv+3P9t///SLS4vMFokY2xh +c3NuYW1lWCRjbGFzc2VzXxAQTlNCaXRtYXBJbWFnZVJlcKMvMTJaTlNJbWFnZVJlcFhOU09iamVjdNIt +LjQ1V05TQXJyYXmiNDLSLS43OF5OU011dGFibGVBcnJheaM3NDLTOjsOPD0+V05TV2hpdGVcTlNDb2xv +clNwYWNlRDAgMAAQA4AM0i0uQEFXTlNDb2xvcqJAMtItLkNEV05TSW1hZ2WiQzIACAARABoAJAApADIA +NwBJAEwAUQBTAGIAaAB3AH4AhQCSAJkApQCtALwAvgDAAMUAxwDJAMsAzQDUANkA5ADmAOgA6gDvAPIA +9AD2APgA/wEWATIBNAE2E6YTqxO2E78T0hPWE+ET6hPvE/cT+hP/FA4UEhQZFCEULhQzFDUUNxQ8FEQU +RxRMFFQAAAAAAAACAQAAAAAAAABFAAAAAAAAAAAAAAAAAAAUVw diff --git a/i18n/src/main/resources/sk.lproj/Command.xib b/i18n/src/main/resources/sk.lproj/Command.xib index be5a6e902e..9959267548 100644 --- a/i18n/src/main/resources/sk.lproj/Command.xib +++ b/i18n/src/main/resources/sk.lproj/Command.xib @@ -1,7 +1,7 @@ - + - + diff --git a/i18n/src/main/resources/sk.lproj/Connection.xib b/i18n/src/main/resources/sk.lproj/Connection.xib index 4cb70b7f26..5ad2a07c3e 100644 --- a/i18n/src/main/resources/sk.lproj/Connection.xib +++ b/i18n/src/main/resources/sk.lproj/Connection.xib @@ -1,7 +1,7 @@ - + - + @@ -76,7 +76,7 @@ - + diff --git a/i18n/src/main/resources/sk.lproj/Info.xib b/i18n/src/main/resources/sk.lproj/Info.xib index e3f5714868..adadf3a865 100644 --- a/i18n/src/main/resources/sk.lproj/Info.xib +++ b/i18n/src/main/resources/sk.lproj/Info.xib @@ -1,7 +1,7 @@ - + - + @@ -89,7 +89,7 @@ - + @@ -1017,21 +1017,21 @@ - + - + - + @@ -1121,21 +1121,21 @@ - + - + - + @@ -1250,27 +1250,27 @@ diff --git a/i18n/src/main/resources/sk.lproj/Login.strings b/i18n/src/main/resources/sk.lproj/Login.strings index ce5274e363..c5e8c92d63 100644 Binary files a/i18n/src/main/resources/sk.lproj/Login.strings and b/i18n/src/main/resources/sk.lproj/Login.strings differ diff --git a/i18n/src/main/resources/sk.lproj/Main.xib b/i18n/src/main/resources/sk.lproj/Main.xib index ff8a062929..fc468b4549 100644 --- a/i18n/src/main/resources/sk.lproj/Main.xib +++ b/i18n/src/main/resources/sk.lproj/Main.xib @@ -1,7 +1,7 @@ - + - + @@ -159,19 +159,19 @@ - + - + - + @@ -321,12 +321,12 @@ CA - + - + @@ -566,6 +566,10 @@ CA - + + + + + diff --git a/i18n/src/main/resources/sk.lproj/Preferences.xib b/i18n/src/main/resources/sk.lproj/Preferences.xib index fbb767c678..12cf29866c 100644 --- a/i18n/src/main/resources/sk.lproj/Preferences.xib +++ b/i18n/src/main/resources/sk.lproj/Preferences.xib @@ -1,7 +1,7 @@ - + - + @@ -266,7 +266,7 @@ - + @@ -307,7 +307,7 @@ - + @@ -538,7 +538,7 @@ - + @@ -578,7 +578,7 @@ - + @@ -613,7 +613,7 @@ - + @@ -642,7 +642,7 @@ - + @@ -1218,7 +1218,7 @@ - + @@ -1250,7 +1250,7 @@ - + @@ -1290,7 +1290,7 @@ - + @@ -1363,7 +1363,7 @@ - + @@ -1558,7 +1558,7 @@ - + @@ -1618,7 +1618,7 @@ - + @@ -1643,7 +1643,7 @@ - + @@ -1685,7 +1685,7 @@ - + @@ -1713,7 +1713,7 @@ - + @@ -1741,7 +1741,7 @@ - + @@ -1769,7 +1769,7 @@ - + @@ -1804,7 +1804,7 @@ - + @@ -1887,7 +1887,7 @@ - + @@ -1946,7 +1946,7 @@ - + @@ -1974,7 +1974,7 @@ - + @@ -2002,7 +2002,7 @@ - + diff --git a/i18n/src/main/resources/sk.lproj/Profiles.xib b/i18n/src/main/resources/sk.lproj/Profiles.xib index 18af5a58f4..20e5ce0f36 100644 --- a/i18n/src/main/resources/sk.lproj/Profiles.xib +++ b/i18n/src/main/resources/sk.lproj/Profiles.xib @@ -1,7 +1,7 @@ - + - + diff --git a/i18n/src/main/resources/sk.lproj/Prompt.xib b/i18n/src/main/resources/sk.lproj/Prompt.xib index 44fd9a2395..f6e8700d5d 100644 --- a/i18n/src/main/resources/sk.lproj/Prompt.xib +++ b/i18n/src/main/resources/sk.lproj/Prompt.xib @@ -1,7 +1,7 @@ - + - + @@ -95,7 +95,7 @@ Gw - + diff --git a/i18n/src/main/resources/sk.lproj/S3.strings b/i18n/src/main/resources/sk.lproj/S3.strings index bea4c5c0f5..e0d6698e94 100644 Binary files a/i18n/src/main/resources/sk.lproj/S3.strings and b/i18n/src/main/resources/sk.lproj/S3.strings differ diff --git a/i18n/src/main/resources/sk.lproj/Updater.strings b/i18n/src/main/resources/sk.lproj/Updater.strings index 59044f61a0..b09fc337f9 100644 Binary files a/i18n/src/main/resources/sk.lproj/Updater.strings and b/i18n/src/main/resources/sk.lproj/Updater.strings differ diff --git a/i18n/src/main/resources/sl.lproj/Bookmark.xib b/i18n/src/main/resources/sl.lproj/Bookmark.xib index a51250404b..26f826cf9b 100644 --- a/i18n/src/main/resources/sl.lproj/Bookmark.xib +++ b/i18n/src/main/resources/sl.lproj/Bookmark.xib @@ -1,7 +1,7 @@ - + - + @@ -92,7 +92,7 @@ - + @@ -489,95 +489,95 @@ YnBsaXN0MDDUAQIDBAUGBwpYJHZlcnNpb25ZJGFyY2hpdmVyVCR0b3BYJG9iamVjdHMSAAGGoF8QD05T -S2V5ZWRBcmNoaXZlctEICVRyb290gAGuCwwZGh8UJCkqMTQ3PUBVJG51bGzWDQ4PEBESExQVFhcYVk5T -U2l6ZV5OU1Jlc2l6aW5nTW9kZVYkY2xhc3NcTlNJbWFnZUZsYWdzVk5TUmVwc1dOU0NvbG9ygAIQAIAN -EiDDAACAA4ALVnsxLCAxfdIbDxweWk5TLm9iamVjdHOhHYAEgArSGw8gI6IhIoAFgAaACdMPJSYnKBRf -EBROU1RJRkZSZXByZXNlbnRhdGlvbl8QGU5TSW50ZXJuYWxMYXlvdXREaXJlY3Rpb26ACIAHTxESbE1N -ACoAAAAKAAAAEAEAAAMAAAABAAEAAAEBAAMAAAABAAEAAAECAAMAAAACAAgACAEDAAMAAAABAAEAAAEG -AAMAAAABAAEAAAEKAAMAAAABAAEAAAERAAQAAAABAAAACAESAAMAAAABAAEAAAEVAAMAAAABAAIAAAEW -AAMAAAABAAEAAAEXAAQAAAABAAAAAgEcAAMAAAABAAEAAAEoAAMAAAABAAIAAAFSAAMAAAABAAEAAAFT -AAMAAAACAAEAAYdzAAcAABGcAAAA0AAAAAAAABGcYXBwbAIAAABtbnRyR1JBWVhZWiAH3AAIABcADwAu -AA9hY3NwQVBQTAAAAABub25lAAAAAAAAAAAAAAAAAAAAAAAA9tYAAQAAAADTLWFwcGwAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAVkZXNjAAAAwAAAAHlkc2NtAAABPAAA -CBpjcHJ0AAAJWAAAACN3dHB0AAAJfAAAABRrVFJDAAAJkAAACAxkZXNjAAAAAAAAAB9HZW5lcmljIEdy -YXkgR2FtbWEgMi4yIFByb2ZpbGUAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAbWx1YwAAAAAAAAAfAAAADHNr -U0sAAAAuAAABhGRhREsAAAA6AAABsmNhRVMAAAA4AAAB7HZpVk4AAABAAAACJHB0QlIAAABKAAACZHVr -VUEAAAAsAAACrmZyRlUAAAA+AAAC2mh1SFUAAAA0AAADGHpoVFcAAAAaAAADTGtvS1IAAAAiAAADZm5i -Tk8AAAA6AAADiGNzQ1oAAAAoAAADwmhlSUwAAAAkAAAD6nJvUk8AAAAqAAAEDmRlREUAAABOAAAEOGl0 -SVQAAABOAAAEhnN2U0UAAAA4AAAE1HpoQ04AAAAaAAAFDGphSlAAAAAmAAAFJmVsR1IAAAAqAAAFTHB0 -UE8AAABSAAAFdm5sTkwAAABAAAAFyGVzRVMAAABMAAAGCHRoVEgAAAAyAAAGVHRyVFIAAAAkAAAGhmZp -RkkAAABGAAAGqmhySFIAAAA+AAAG8HBsUEwAAABKAAAHLmFyRUcAAAAsAAAHeHJ1UlUAAAA6AAAHpGVu -VVMAAAA8AAAH3gBWAWEAZQBvAGIAZQBjAG4A4QAgAHMAaQB2AOEAIABnAGEAbQBhACAAMgAsADIARwBl -AG4AZQByAGkAcwBrACAAZwByAOUAIAAyACwAMgAgAGcAYQBtAG0AYQAtAHAAcgBvAGYAaQBsAEcAYQBt -AG0AYQAgAGQAZQAgAGcAcgBpAHMAbwBzACAAZwBlAG4A6AByAGkAYwBhACAAMgAuADIAQx6lAHUAIABo -AOwAbgBoACAATQDgAHUAIAB4AOEAbQAgAEMAaAB1AG4AZwAgAEcAYQBtAG0AYQAgADIALgAyAFAAZQBy -AGYAaQBsACAARwBlAG4A6QByAGkAYwBvACAAZABhACAARwBhAG0AYQAgAGQAZQAgAEMAaQBuAHoAYQBz -ACAAMgAsADIEFwQwBDMEMAQ7BEwEPQQwACAARwByAGEAeQAtBDMEMAQ8BDAAIAAyAC4AMgBQAHIAbwBm -AGkAbAAgAGcA6QBuAOkAcgBpAHEAdQBlACAAZwByAGkAcwAgAGcAYQBtAG0AYQAgADIALAAyAMEAbAB0 -AGEAbADhAG4AbwBzACAAcwB6APwAcgBrAGUAIABnAGEAbQBtAGEAIAAyAC4AMpAadShwcJaOUUlepgAy -AC4AMoJyX2ljz4/wx3y8GAAg1ozAyQAgrBC5yAAgADIALgAyACDVBLhc0wzHfABHAGUAbgBlAHIAaQBz -AGsAIABnAHIA5QAgAGcAYQBtAG0AYQAgADIALAAyAC0AcAByAG8AZgBpAGwATwBiAGUAYwBuAOEAIAFh -AGUAZADhACAAZwBhAG0AYQAgADIALgAyBdIF0AXeBdQAIAXQBeQF1QXoACAF2wXcBdwF2QAgADIALgAy -AEcAYQBtAGEAIABnAHIAaQAgAGcAZQBuAGUAcgBpAGMBAwAgADIALAAyAEEAbABsAGcAZQBtAGUAaQBu -AGUAcwAgAEcAcgBhAHUAcwB0AHUAZgBlAG4ALQBQAHIAbwBmAGkAbAAgAEcAYQBtAG0AYQAgADIALAAy -AFAAcgBvAGYAaQBsAG8AIABnAHIAaQBnAGkAbwAgAGcAZQBuAGUAcgBpAGMAbwAgAGQAZQBsAGwAYQAg -AGcAYQBtAG0AYQAgADIALAAyAEcAZQBuAGUAcgBpAHMAawAgAGcAcgDlACAAMgAsADIAIABnAGEAbQBt -AGEAcAByAG8AZgBpAGxmbpAacHBepnz7ZXAAMgAuADJjz4/wZYdO9k4AgiwwsDDsMKQwrDDzMN4AIAAy -AC4AMgAgMNcw7TDVMKEwpDDrA5MDtQO9A7kDugPMACADkwO6A8EDuQAgA5MDrAO8A7wDsQAgADIALgAy -AFAAZQByAGYAaQBsACAAZwBlAG4A6QByAGkAYwBvACAAZABlACAAYwBpAG4AegBlAG4AdABvAHMAIABk -AGEAIABHAGEAbQBtAGEAIAAyACwAMgBBAGwAZwBlAG0AZQBlAG4AIABnAHIAaQBqAHMAIABnAGEAbQBt -AGEAIAAyACwAMgAtAHAAcgBvAGYAaQBlAGwAUABlAHIAZgBpAGwAIABnAGUAbgDpAHIAaQBjAG8AIABk -AGUAIABnAGEAbQBtAGEAIABkAGUAIABnAHIAaQBzAGUAcwAgADIALAAyDiMOMQ4HDioONQ5BDgEOIQ4h -DjIOQA4BDiMOIg5MDhcOMQ5IDicORA4bACAAMgAuADIARwBlAG4AZQBsACAARwByAGkAIABHAGEAbQBh -ACAAMgAsADIAWQBsAGUAaQBuAGUAbgAgAGgAYQByAG0AYQBhAG4AIABnAGEAbQBtAGEAIAAyACwAMgAg -AC0AcAByAG8AZgBpAGkAbABpAEcAZQBuAGUAcgBpAQ0AawBpACAARwByAGEAeQAgAEcAYQBtAG0AYQAg -ADIALgAyACAAcAByAG8AZgBpAGwAVQBuAGkAdwBlAHIAcwBhAGwAbgB5ACAAcAByAG8AZgBpAGwAIABz -AHoAYQByAG8BWwBjAGkAIABnAGEAbQBtAGEAIAAyACwAMgY6BicGRQYnACAAMgAuADIAIAZEBkgGRgAg -BjEGRQYnBi8GSgAgBjkGJwZFBB4EMQRJBDAETwAgBEEENQRABDAETwAgBDMEMAQ8BDwEMAAgADIALAAy -AC0EPwRABD4ERAQ4BDsETABHAGUAbgBlAHIAaQBjACAARwByAGEAeQAgAEcAYQBtAG0AYQAgADIALgAy -ACAAUAByAG8AZgBpAGwAZQAAdGV4dAAAAABDb3B5cmlnaHQgQXBwbGUgSW5jLiwgMjAxMgAAWFlaIAAA -AAAAAPNRAAEAAAABFsxjdXJ2AAAAAAAABAAAAAAFAAoADwAUABkAHgAjACgALQAyADcAOwBAAEUASgBP -AFQAWQBeAGMAaABtAHIAdwB8AIEAhgCLAJAAlQCaAJ8ApACpAK4AsgC3ALwAwQDGAMsA0ADVANsA4ADl -AOsA8AD2APsBAQEHAQ0BEwEZAR8BJQErATIBOAE+AUUBTAFSAVkBYAFnAW4BdQF8AYMBiwGSAZoBoQGp -AbEBuQHBAckB0QHZAeEB6QHyAfoCAwIMAhQCHQImAi8COAJBAksCVAJdAmcCcQJ6AoQCjgKYAqICrAK2 -AsECywLVAuAC6wL1AwADCwMWAyEDLQM4A0MDTwNaA2YDcgN+A4oDlgOiA64DugPHA9MD4APsA/kEBgQT -BCAELQQ7BEgEVQRjBHEEfgSMBJoEqAS2BMQE0wThBPAE/gUNBRwFKwU6BUkFWAVnBXcFhgWWBaYFtQXF -BdUF5QX2BgYGFgYnBjcGSAZZBmoGewaMBp0GrwbABtEG4wb1BwcHGQcrBz0HTwdhB3QHhgeZB6wHvwfS -B+UH+AgLCB8IMghGCFoIbgiCCJYIqgi+CNII5wj7CRAJJQk6CU8JZAl5CY8JpAm6Cc8J5Qn7ChEKJwo9 -ClQKagqBCpgKrgrFCtwK8wsLCyILOQtRC2kLgAuYC7ALyAvhC/kMEgwqDEMMXAx1DI4MpwzADNkM8w0N -DSYNQA1aDXQNjg2pDcMN3g34DhMOLg5JDmQOfw6bDrYO0g7uDwkPJQ9BD14Peg+WD7MPzw/sEAkQJhBD -EGEQfhCbELkQ1xD1ERMRMRFPEW0RjBGqEckR6BIHEiYSRRJkEoQSoxLDEuMTAxMjE0MTYxODE6QTxRPl -FAYUJxRJFGoUixStFM4U8BUSFTQVVhV4FZsVvRXgFgMWJhZJFmwWjxayFtYW+hcdF0EXZReJF64X0hf3 -GBsYQBhlGIoYrxjVGPoZIBlFGWsZkRm3Gd0aBBoqGlEadxqeGsUa7BsUGzsbYxuKG7Ib2hwCHCocUhx7 -HKMczBz1HR4dRx1wHZkdwx3sHhYeQB5qHpQevh7pHxMfPh9pH5Qfvx/qIBUgQSBsIJggxCDwIRwhSCF1 -IaEhziH7IiciVSKCIq8i3SMKIzgjZiOUI8Ij8CQfJE0kfCSrJNolCSU4JWgllyXHJfcmJyZXJocmtybo -JxgnSSd6J6sn3CgNKD8ocSiiKNQpBik4KWspnSnQKgIqNSpoKpsqzysCKzYraSudK9EsBSw5LG4soizX -LQwtQS12Last4S4WLkwugi63Lu4vJC9aL5Evxy/+MDUwbDCkMNsxEjFKMYIxujHyMioyYzKbMtQzDTNG -M38zuDPxNCs0ZTSeNNg1EzVNNYc1wjX9Njc2cjauNuk3JDdgN5w31zgUOFA4jDjIOQU5Qjl/Obw5+To2 -OnQ6sjrvOy07azuqO+g8JzxlPKQ84z0iPWE9oT3gPiA+YD6gPuA/IT9hP6I/4kAjQGRApkDnQSlBakGs -Qe5CMEJyQrVC90M6Q31DwEQDREdEikTORRJFVUWaRd5GIkZnRqtG8Ec1R3tHwEgFSEtIkUjXSR1JY0mp -SfBKN0p9SsRLDEtTS5pL4kwqTHJMuk0CTUpNk03cTiVObk63TwBPSU+TT91QJ1BxULtRBlFQUZtR5lIx -UnxSx1MTU19TqlP2VEJUj1TbVShVdVXCVg9WXFapVvdXRFeSV+BYL1h9WMtZGllpWbhaB1pWWqZa9VtF -W5Vb5Vw1XIZc1l0nXXhdyV4aXmxevV8PX2Ffs2AFYFdgqmD8YU9homH1YklinGLwY0Njl2PrZEBklGTp -ZT1lkmXnZj1mkmboZz1nk2fpaD9olmjsaUNpmmnxakhqn2r3a09rp2v/bFdsr20IbWBtuW4SbmtuxG8e -b3hv0XArcIZw4HE6cZVx8HJLcqZzAXNdc7h0FHRwdMx1KHWFdeF2Pnabdvh3VnezeBF4bnjMeSp5iXnn -ekZ6pXsEe2N7wnwhfIF84X1BfaF+AX5ifsJ/I3+Ef+WAR4CogQqBa4HNgjCCkoL0g1eDuoQdhICE44VH -hauGDoZyhteHO4efiASIaYjOiTOJmYn+imSKyoswi5aL/IxjjMqNMY2Yjf+OZo7OjzaPnpAGkG6Q1pE/ -kaiSEZJ6kuOTTZO2lCCUipT0lV+VyZY0lp+XCpd1l+CYTJi4mSSZkJn8mmia1ZtCm6+cHJyJnPedZJ3S -nkCerp8dn4uf+qBpoNihR6G2oiailqMGo3aj5qRWpMelOKWpphqmi6b9p26n4KhSqMSpN6mpqhyqj6sC -q3Wr6axcrNCtRK24ri2uoa8Wr4uwALB1sOqxYLHWskuywrM4s660JbSctRO1irYBtnm28Ldot+C4WbjR -uUq5wro7urW7LrunvCG8m70VvY++Cr6Evv+/er/1wHDA7MFnwePCX8Lbw1jD1MRRxM7FS8XIxkbGw8dB -x7/IPci8yTrJuco4yrfLNsu2zDXMtc01zbXONs62zzfPuNA50LrRPNG+0j/SwdNE08bUSdTL1U7V0dZV -1tjXXNfg2GTY6Nls2fHadtr724DcBdyK3RDdlt4c3qLfKd+v4DbgveFE4cziU+Lb42Pj6+Rz5PzlhOYN -5pbnH+ep6DLovOlG6dDqW+rl63Dr++yG7RHtnO4o7rTvQO/M8Fjw5fFy8f/yjPMZ86f0NPTC9VD13vZt -9vv3ivgZ+Kj5OPnH+lf65/t3/Af8mP0p/br+S/7c/23//9IrLC0uWiRjbGFzc25hbWVYJGNsYXNzZXNf -EBBOU0JpdG1hcEltYWdlUmVwoy0vMFpOU0ltYWdlUmVwWE5TT2JqZWN00issMjNXTlNBcnJheaIyMNIr -LDU2Xk5TTXV0YWJsZUFycmF5ozUyMNM4OQ86OzxXTlNXaGl0ZVxOU0NvbG9yU3BhY2VEMCAwABADgAzS -Kyw+P1dOU0NvbG9yoj4w0issQUJXTlNJbWFnZaJBMAAIABEAGgAkACkAMgA3AEkATABRAFMAYgBoAHUA -fACLAJIAnwCmAK4AsACyALQAuQC7AL0AxADJANQA1gDYANoA3wDiAOQA5gDoAO8BBgEiASQBJhOWE5sT -phOvE8ITxhPRE9oT3xPnE+oT7xP+FAIUCRQRFB4UIxQlFCcULBQ0FDcUPBREAAAAAAAAAgEAAAAAAAAA -QwAAAAAAAAAAAAAAAAAAFEc +S2V5ZWRBcmNoaXZlctEICVRyb290gAGuCwwbHCEaJissMzY5P0JVJG51bGzXDQ4PEBESExQVFhcYGRpW +TlNTaXplViRjbGFzc1xOU0ltYWdlRmxhZ3NWTlNSZXBzW05TVGludENvbG9yV05TQ29sb3JeTlNSZXNp +emluZ01vZGWAAoANEiDDAACAA4AAgAsQAFZ7MSwgMX3SHQ4eIFpOUy5vYmplY3RzoR+ABIAK0h0OIiWi +IySABYAGgAnTDicoKSoaXxAUTlNUSUZGUmVwcmVzZW50YXRpb25fEBlOU0ludGVybmFsTGF5b3V0RGly +ZWN0aW9ugAiAB08REmxNTQAqAAAACgAAABABAAADAAAAAQABAAABAQADAAAAAQABAAABAgADAAAAAgAI +AAgBAwADAAAAAQABAAABBgADAAAAAQABAAABCgADAAAAAQABAAABEQAEAAAAAQAAAAgBEgADAAAAAQAB +AAABFQADAAAAAQACAAABFgADAAAAAQABAAABFwAEAAAAAQAAAAIBHAADAAAAAQABAAABKAADAAAAAQAC +AAABUgADAAAAAQABAAABUwADAAAAAgABAAGHcwAHAAARnAAAANAAAAAAAAARnGFwcGwCAAAAbW50ckdS +QVlYWVogB9wACAAXAA8ALgAPYWNzcEFQUEwAAAAAbm9uZQAAAAAAAAAAAAAAAAAAAAAAAPbWAAEAAAAA +0y1hcHBsAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFZGVzYwAA +AMAAAAB5ZHNjbQAAATwAAAgaY3BydAAACVgAAAAjd3RwdAAACXwAAAAUa1RSQwAACZAAAAgMZGVzYwAA +AAAAAAAfR2VuZXJpYyBHcmF5IEdhbW1hIDIuMiBQcm9maWxlAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAG1s +dWMAAAAAAAAAHwAAAAxza1NLAAAALgAAAYRkYURLAAAAOgAAAbJjYUVTAAAAOAAAAex2aVZOAAAAQAAA +AiRwdEJSAAAASgAAAmR1a1VBAAAALAAAAq5mckZVAAAAPgAAAtpodUhVAAAANAAAAxh6aFRXAAAAGgAA +A0xrb0tSAAAAIgAAA2ZuYk5PAAAAOgAAA4hjc0NaAAAAKAAAA8JoZUlMAAAAJAAAA+pyb1JPAAAAKgAA +BA5kZURFAAAATgAABDhpdElUAAAATgAABIZzdlNFAAAAOAAABNR6aENOAAAAGgAABQxqYUpQAAAAJgAA +BSZlbEdSAAAAKgAABUxwdFBPAAAAUgAABXZubE5MAAAAQAAABchlc0VTAAAATAAABgh0aFRIAAAAMgAA +BlR0clRSAAAAJAAABoZmaUZJAAAARgAABqpockhSAAAAPgAABvBwbFBMAAAASgAABy5hckVHAAAALAAA +B3hydVJVAAAAOgAAB6RlblVTAAAAPAAAB94AVgFhAGUAbwBiAGUAYwBuAOEAIABzAGkAdgDhACAAZwBh +AG0AYQAgADIALAAyAEcAZQBuAGUAcgBpAHMAawAgAGcAcgDlACAAMgAsADIAIABnAGEAbQBtAGEALQBw +AHIAbwBmAGkAbABHAGEAbQBtAGEAIABkAGUAIABnAHIAaQBzAG8AcwAgAGcAZQBuAOgAcgBpAGMAYQAg +ADIALgAyAEMepQB1ACAAaADsAG4AaAAgAE0A4AB1ACAAeADhAG0AIABDAGgAdQBuAGcAIABHAGEAbQBt +AGEAIAAyAC4AMgBQAGUAcgBmAGkAbAAgAEcAZQBuAOkAcgBpAGMAbwAgAGQAYQAgAEcAYQBtAGEAIABk +AGUAIABDAGkAbgB6AGEAcwAgADIALAAyBBcEMAQzBDAEOwRMBD0EMAAgAEcAcgBhAHkALQQzBDAEPAQw +ACAAMgAuADIAUAByAG8AZgBpAGwAIABnAOkAbgDpAHIAaQBxAHUAZQAgAGcAcgBpAHMAIABnAGEAbQBt +AGEAIAAyACwAMgDBAGwAdABhAGwA4QBuAG8AcwAgAHMAegD8AHIAawBlACAAZwBhAG0AbQBhACAAMgAu +ADKQGnUocHCWjlFJXqYAMgAuADKCcl9pY8+P8Md8vBgAINaMwMkAIKwQucgAIAAyAC4AMgAg1QS4XNMM +x3wARwBlAG4AZQByAGkAcwBrACAAZwByAOUAIABnAGEAbQBtAGEAIAAyACwAMgAtAHAAcgBvAGYAaQBs +AE8AYgBlAGMAbgDhACABYQBlAGQA4QAgAGcAYQBtAGEAIAAyAC4AMgXSBdAF3gXUACAF0AXkBdUF6AAg +BdsF3AXcBdkAIAAyAC4AMgBHAGEAbQBhACAAZwByAGkAIABnAGUAbgBlAHIAaQBjAQMAIAAyACwAMgBB +AGwAbABnAGUAbQBlAGkAbgBlAHMAIABHAHIAYQB1AHMAdAB1AGYAZQBuAC0AUAByAG8AZgBpAGwAIABH +AGEAbQBtAGEAIAAyACwAMgBQAHIAbwBmAGkAbABvACAAZwByAGkAZwBpAG8AIABnAGUAbgBlAHIAaQBj +AG8AIABkAGUAbABsAGEAIABnAGEAbQBtAGEAIAAyACwAMgBHAGUAbgBlAHIAaQBzAGsAIABnAHIA5QAg +ADIALAAyACAAZwBhAG0AbQBhAHAAcgBvAGYAaQBsZm6QGnBwXqZ8+2VwADIALgAyY8+P8GWHTvZOAIIs +MLAw7DCkMKww8zDeACAAMgAuADIAIDDXMO0w1TChMKQw6wOTA7UDvQO5A7oDzAAgA5MDugPBA7kAIAOT +A6wDvAO8A7EAIAAyAC4AMgBQAGUAcgBmAGkAbAAgAGcAZQBuAOkAcgBpAGMAbwAgAGQAZQAgAGMAaQBu +AHoAZQBuAHQAbwBzACAAZABhACAARwBhAG0AbQBhACAAMgAsADIAQQBsAGcAZQBtAGUAZQBuACAAZwBy +AGkAagBzACAAZwBhAG0AbQBhACAAMgAsADIALQBwAHIAbwBmAGkAZQBsAFAAZQByAGYAaQBsACAAZwBl +AG4A6QByAGkAYwBvACAAZABlACAAZwBhAG0AbQBhACAAZABlACAAZwByAGkAcwBlAHMAIAAyACwAMg4j +DjEOBw4qDjUOQQ4BDiEOIQ4yDkAOAQ4jDiIOTA4XDjEOSA4nDkQOGwAgADIALgAyAEcAZQBuAGUAbAAg +AEcAcgBpACAARwBhAG0AYQAgADIALAAyAFkAbABlAGkAbgBlAG4AIABoAGEAcgBtAGEAYQBuACAAZwBh +AG0AbQBhACAAMgAsADIAIAAtAHAAcgBvAGYAaQBpAGwAaQBHAGUAbgBlAHIAaQENAGsAaQAgAEcAcgBh +AHkAIABHAGEAbQBtAGEAIAAyAC4AMgAgAHAAcgBvAGYAaQBsAFUAbgBpAHcAZQByAHMAYQBsAG4AeQAg +AHAAcgBvAGYAaQBsACAAcwB6AGEAcgBvAVsAYwBpACAAZwBhAG0AbQBhACAAMgAsADIGOgYnBkUGJwAg +ADIALgAyACAGRAZIBkYAIAYxBkUGJwYvBkoAIAY5BicGRQQeBDEESQQwBE8AIARBBDUEQAQwBE8AIAQz +BDAEPAQ8BDAAIAAyACwAMgAtBD8EQAQ+BEQEOAQ7BEwARwBlAG4AZQByAGkAYwAgAEcAcgBhAHkAIABH +AGEAbQBtAGEAIAAyAC4AMgAgAFAAcgBvAGYAaQBsAGUAAHRleHQAAAAAQ29weXJpZ2h0IEFwcGxlIElu +Yy4sIDIwMTIAAFhZWiAAAAAAAADzUQABAAAAARbMY3VydgAAAAAAAAQAAAAABQAKAA8AFAAZAB4AIwAo +AC0AMgA3ADsAQABFAEoATwBUAFkAXgBjAGgAbQByAHcAfACBAIYAiwCQAJUAmgCfAKQAqQCuALIAtwC8 +AMEAxgDLANAA1QDbAOAA5QDrAPAA9gD7AQEBBwENARMBGQEfASUBKwEyATgBPgFFAUwBUgFZAWABZwFu +AXUBfAGDAYsBkgGaAaEBqQGxAbkBwQHJAdEB2QHhAekB8gH6AgMCDAIUAh0CJgIvAjgCQQJLAlQCXQJn +AnECegKEAo4CmAKiAqwCtgLBAssC1QLgAusC9QMAAwsDFgMhAy0DOANDA08DWgNmA3IDfgOKA5YDogOu +A7oDxwPTA+AD7AP5BAYEEwQgBC0EOwRIBFUEYwRxBH4EjASaBKgEtgTEBNME4QTwBP4FDQUcBSsFOgVJ +BVgFZwV3BYYFlgWmBbUFxQXVBeUF9gYGBhYGJwY3BkgGWQZqBnsGjAadBq8GwAbRBuMG9QcHBxkHKwc9 +B08HYQd0B4YHmQesB78H0gflB/gICwgfCDIIRghaCG4IggiWCKoIvgjSCOcI+wkQCSUJOglPCWQJeQmP +CaQJugnPCeUJ+woRCicKPQpUCmoKgQqYCq4KxQrcCvMLCwsiCzkLUQtpC4ALmAuwC8gL4Qv5DBIMKgxD +DFwMdQyODKcMwAzZDPMNDQ0mDUANWg10DY4NqQ3DDd4N+A4TDi4OSQ5kDn8Omw62DtIO7g8JDyUPQQ9e +D3oPlg+zD88P7BAJECYQQxBhEH4QmxC5ENcQ9RETETERTxFtEYwRqhHJEegSBxImEkUSZBKEEqMSwxLj +EwMTIxNDE2MTgxOkE8UT5RQGFCcUSRRqFIsUrRTOFPAVEhU0FVYVeBWbFb0V4BYDFiYWSRZsFo8WshbW +FvoXHRdBF2UXiReuF9IX9xgbGEAYZRiKGK8Y1Rj6GSAZRRlrGZEZtxndGgQaKhpRGncanhrFGuwbFBs7 +G2MbihuyG9ocAhwqHFIcexyjHMwc9R0eHUcdcB2ZHcMd7B4WHkAeah6UHr4e6R8THz4faR+UH78f6iAV +IEEgbCCYIMQg8CEcIUghdSGhIc4h+yInIlUigiKvIt0jCiM4I2YjlCPCI/AkHyRNJHwkqyTaJQklOCVo +JZclxyX3JicmVyaHJrcm6CcYJ0kneierJ9woDSg/KHEooijUKQYpOClrKZ0p0CoCKjUqaCqbKs8rAis2 +K2krnSvRLAUsOSxuLKIs1y0MLUEtdi2rLeEuFi5MLoIuty7uLyQvWi+RL8cv/jA1MGwwpDDbMRIxSjGC +Mbox8jIqMmMymzLUMw0zRjN/M7gz8TQrNGU0njTYNRM1TTWHNcI1/TY3NnI2rjbpNyQ3YDecN9c4FDhQ +OIw4yDkFOUI5fzm8Ofk6Njp0OrI67zstO2s7qjvoPCc8ZTykPOM9Ij1hPaE94D4gPmA+oD7gPyE/YT+i +P+JAI0BkQKZA50EpQWpBrEHuQjBCckK1QvdDOkN9Q8BEA0RHRIpEzkUSRVVFmkXeRiJGZ0arRvBHNUd7 +R8BIBUhLSJFI10kdSWNJqUnwSjdKfUrESwxLU0uaS+JMKkxyTLpNAk1KTZNN3E4lTm5Ot08AT0lPk0/d +UCdQcVC7UQZRUFGbUeZSMVJ8UsdTE1NfU6pT9lRCVI9U21UoVXVVwlYPVlxWqVb3V0RXklfgWC9YfVjL +WRpZaVm4WgdaVlqmWvVbRVuVW+VcNVyGXNZdJ114XcleGl5sXr1fD19hX7NgBWBXYKpg/GFPYaJh9WJJ +Ypxi8GNDY5dj62RAZJRk6WU9ZZJl52Y9ZpJm6Gc9Z5Nn6Wg/aJZo7GlDaZpp8WpIap9q92tPa6dr/2xX +bK9tCG1gbbluEm5rbsRvHm94b9FwK3CGcOBxOnGVcfByS3KmcwFzXXO4dBR0cHTMdSh1hXXhdj52m3b4 +d1Z3s3gReG54zHkqeYl553pGeqV7BHtje8J8IXyBfOF9QX2hfgF+Yn7CfyN/hH/lgEeAqIEKgWuBzYIw +gpKC9INXg7qEHYSAhOOFR4Wrhg6GcobXhzuHn4gEiGmIzokziZmJ/opkisqLMIuWi/yMY4zKjTGNmI3/ +jmaOzo82j56QBpBukNaRP5GokhGSepLjk02TtpQglIqU9JVflcmWNJaflwqXdZfgmEyYuJkkmZCZ/Jpo +mtWbQpuvnByciZz3nWSd0p5Anq6fHZ+Ln/qgaaDYoUehtqImopajBqN2o+akVqTHpTilqaYapoum/adu +p+CoUqjEqTepqaocqo+rAqt1q+msXKzQrUStuK4trqGvFq+LsACwdbDqsWCx1rJLssKzOLOutCW0nLUT +tYq2AbZ5tvC3aLfguFm40blKucK6O7q1uy67p7whvJu9Fb2Pvgq+hL7/v3q/9cBwwOzBZ8Hjwl/C28NY +w9TEUcTOxUvFyMZGxsPHQce/yD3IvMk6ybnKOMq3yzbLtsw1zLXNNc21zjbOts83z7jQOdC60TzRvtI/ +0sHTRNPG1EnUy9VO1dHWVdbY11zX4Nhk2OjZbNnx2nba+9uA3AXcit0Q3ZbeHN6i3ynfr+A24L3hROHM +4lPi2+Nj4+vkc+T85YTmDeaW5x/nqegy6LzpRunQ6lvq5etw6/vshu0R7ZzuKO6070DvzPBY8OXxcvH/ +8ozzGfOn9DT0wvVQ9d72bfb794r4Gfio+Tj5x/pX+uf7d/wH/Jj9Kf26/kv+3P9t///SLS4vMFokY2xh +c3NuYW1lWCRjbGFzc2VzXxAQTlNCaXRtYXBJbWFnZVJlcKMvMTJaTlNJbWFnZVJlcFhOU09iamVjdNIt +LjQ1V05TQXJyYXmiNDLSLS43OF5OU011dGFibGVBcnJheaM3NDLTOjsOPD0+V05TV2hpdGVcTlNDb2xv +clNwYWNlRDAgMAAQA4AM0i0uQEFXTlNDb2xvcqJAMtItLkNEV05TSW1hZ2WiQzIACAARABoAJAApADIA +NwBJAEwAUQBTAGIAaAB3AH4AhQCSAJkApQCtALwAvgDAAMUAxwDJAMsAzQDUANkA5ADmAOgA6gDvAPIA +9AD2APgA/wEWATIBNAE2E6YTqxO2E78T0hPWE+ET6hPvE/cT+hP/FA4UEhQZFCEULhQzFDUUNxQ8FEQU +RxRMFFQAAAAAAAACAQAAAAAAAABFAAAAAAAAAAAAAAAAAAAUVw diff --git a/i18n/src/main/resources/sl.lproj/Browser.xib b/i18n/src/main/resources/sl.lproj/Browser.xib index 6cc8ad57ac..8a4647d037 100644 --- a/i18n/src/main/resources/sl.lproj/Browser.xib +++ b/i18n/src/main/resources/sl.lproj/Browser.xib @@ -1,7 +1,7 @@ - + - + @@ -38,7 +38,7 @@ - + @@ -62,27 +62,27 @@ @@ -256,7 +256,7 @@ - + @@ -553,95 +553,95 @@ DQ YnBsaXN0MDDUAQIDBAUGBwpYJHZlcnNpb25ZJGFyY2hpdmVyVCR0b3BYJG9iamVjdHMSAAGGoF8QD05T -S2V5ZWRBcmNoaXZlctEICVRyb290gAGuCwwZGh8UJCkqMTQ3PUBVJG51bGzWDQ4PEBESExQVFhcYVk5T -U2l6ZV5OU1Jlc2l6aW5nTW9kZVYkY2xhc3NcTlNJbWFnZUZsYWdzVk5TUmVwc1dOU0NvbG9ygAIQAIAN -EiDDAACAA4ALVnsxLCAxfdIbDxweWk5TLm9iamVjdHOhHYAEgArSGw8gI6IhIoAFgAaACdMPJSYnKBRf -EBROU1RJRkZSZXByZXNlbnRhdGlvbl8QGU5TSW50ZXJuYWxMYXlvdXREaXJlY3Rpb26ACIAHTxESbE1N -ACoAAAAKAAAAEAEAAAMAAAABAAEAAAEBAAMAAAABAAEAAAECAAMAAAACAAgACAEDAAMAAAABAAEAAAEG -AAMAAAABAAEAAAEKAAMAAAABAAEAAAERAAQAAAABAAAACAESAAMAAAABAAEAAAEVAAMAAAABAAIAAAEW -AAMAAAABAAEAAAEXAAQAAAABAAAAAgEcAAMAAAABAAEAAAEoAAMAAAABAAIAAAFSAAMAAAABAAEAAAFT -AAMAAAACAAEAAYdzAAcAABGcAAAA0AAAAAAAABGcYXBwbAIAAABtbnRyR1JBWVhZWiAH3AAIABcADwAu -AA9hY3NwQVBQTAAAAABub25lAAAAAAAAAAAAAAAAAAAAAAAA9tYAAQAAAADTLWFwcGwAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAVkZXNjAAAAwAAAAHlkc2NtAAABPAAA -CBpjcHJ0AAAJWAAAACN3dHB0AAAJfAAAABRrVFJDAAAJkAAACAxkZXNjAAAAAAAAAB9HZW5lcmljIEdy -YXkgR2FtbWEgMi4yIFByb2ZpbGUAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAbWx1YwAAAAAAAAAfAAAADHNr -U0sAAAAuAAABhGRhREsAAAA6AAABsmNhRVMAAAA4AAAB7HZpVk4AAABAAAACJHB0QlIAAABKAAACZHVr -VUEAAAAsAAACrmZyRlUAAAA+AAAC2mh1SFUAAAA0AAADGHpoVFcAAAAaAAADTGtvS1IAAAAiAAADZm5i -Tk8AAAA6AAADiGNzQ1oAAAAoAAADwmhlSUwAAAAkAAAD6nJvUk8AAAAqAAAEDmRlREUAAABOAAAEOGl0 -SVQAAABOAAAEhnN2U0UAAAA4AAAE1HpoQ04AAAAaAAAFDGphSlAAAAAmAAAFJmVsR1IAAAAqAAAFTHB0 -UE8AAABSAAAFdm5sTkwAAABAAAAFyGVzRVMAAABMAAAGCHRoVEgAAAAyAAAGVHRyVFIAAAAkAAAGhmZp -RkkAAABGAAAGqmhySFIAAAA+AAAG8HBsUEwAAABKAAAHLmFyRUcAAAAsAAAHeHJ1UlUAAAA6AAAHpGVu -VVMAAAA8AAAH3gBWAWEAZQBvAGIAZQBjAG4A4QAgAHMAaQB2AOEAIABnAGEAbQBhACAAMgAsADIARwBl -AG4AZQByAGkAcwBrACAAZwByAOUAIAAyACwAMgAgAGcAYQBtAG0AYQAtAHAAcgBvAGYAaQBsAEcAYQBt -AG0AYQAgAGQAZQAgAGcAcgBpAHMAbwBzACAAZwBlAG4A6AByAGkAYwBhACAAMgAuADIAQx6lAHUAIABo -AOwAbgBoACAATQDgAHUAIAB4AOEAbQAgAEMAaAB1AG4AZwAgAEcAYQBtAG0AYQAgADIALgAyAFAAZQBy -AGYAaQBsACAARwBlAG4A6QByAGkAYwBvACAAZABhACAARwBhAG0AYQAgAGQAZQAgAEMAaQBuAHoAYQBz -ACAAMgAsADIEFwQwBDMEMAQ7BEwEPQQwACAARwByAGEAeQAtBDMEMAQ8BDAAIAAyAC4AMgBQAHIAbwBm -AGkAbAAgAGcA6QBuAOkAcgBpAHEAdQBlACAAZwByAGkAcwAgAGcAYQBtAG0AYQAgADIALAAyAMEAbAB0 -AGEAbADhAG4AbwBzACAAcwB6APwAcgBrAGUAIABnAGEAbQBtAGEAIAAyAC4AMpAadShwcJaOUUlepgAy -AC4AMoJyX2ljz4/wx3y8GAAg1ozAyQAgrBC5yAAgADIALgAyACDVBLhc0wzHfABHAGUAbgBlAHIAaQBz -AGsAIABnAHIA5QAgAGcAYQBtAG0AYQAgADIALAAyAC0AcAByAG8AZgBpAGwATwBiAGUAYwBuAOEAIAFh -AGUAZADhACAAZwBhAG0AYQAgADIALgAyBdIF0AXeBdQAIAXQBeQF1QXoACAF2wXcBdwF2QAgADIALgAy -AEcAYQBtAGEAIABnAHIAaQAgAGcAZQBuAGUAcgBpAGMBAwAgADIALAAyAEEAbABsAGcAZQBtAGUAaQBu -AGUAcwAgAEcAcgBhAHUAcwB0AHUAZgBlAG4ALQBQAHIAbwBmAGkAbAAgAEcAYQBtAG0AYQAgADIALAAy -AFAAcgBvAGYAaQBsAG8AIABnAHIAaQBnAGkAbwAgAGcAZQBuAGUAcgBpAGMAbwAgAGQAZQBsAGwAYQAg -AGcAYQBtAG0AYQAgADIALAAyAEcAZQBuAGUAcgBpAHMAawAgAGcAcgDlACAAMgAsADIAIABnAGEAbQBt -AGEAcAByAG8AZgBpAGxmbpAacHBepnz7ZXAAMgAuADJjz4/wZYdO9k4AgiwwsDDsMKQwrDDzMN4AIAAy -AC4AMgAgMNcw7TDVMKEwpDDrA5MDtQO9A7kDugPMACADkwO6A8EDuQAgA5MDrAO8A7wDsQAgADIALgAy -AFAAZQByAGYAaQBsACAAZwBlAG4A6QByAGkAYwBvACAAZABlACAAYwBpAG4AegBlAG4AdABvAHMAIABk -AGEAIABHAGEAbQBtAGEAIAAyACwAMgBBAGwAZwBlAG0AZQBlAG4AIABnAHIAaQBqAHMAIABnAGEAbQBt -AGEAIAAyACwAMgAtAHAAcgBvAGYAaQBlAGwAUABlAHIAZgBpAGwAIABnAGUAbgDpAHIAaQBjAG8AIABk -AGUAIABnAGEAbQBtAGEAIABkAGUAIABnAHIAaQBzAGUAcwAgADIALAAyDiMOMQ4HDioONQ5BDgEOIQ4h -DjIOQA4BDiMOIg5MDhcOMQ5IDicORA4bACAAMgAuADIARwBlAG4AZQBsACAARwByAGkAIABHAGEAbQBh -ACAAMgAsADIAWQBsAGUAaQBuAGUAbgAgAGgAYQByAG0AYQBhAG4AIABnAGEAbQBtAGEAIAAyACwAMgAg -AC0AcAByAG8AZgBpAGkAbABpAEcAZQBuAGUAcgBpAQ0AawBpACAARwByAGEAeQAgAEcAYQBtAG0AYQAg -ADIALgAyACAAcAByAG8AZgBpAGwAVQBuAGkAdwBlAHIAcwBhAGwAbgB5ACAAcAByAG8AZgBpAGwAIABz -AHoAYQByAG8BWwBjAGkAIABnAGEAbQBtAGEAIAAyACwAMgY6BicGRQYnACAAMgAuADIAIAZEBkgGRgAg -BjEGRQYnBi8GSgAgBjkGJwZFBB4EMQRJBDAETwAgBEEENQRABDAETwAgBDMEMAQ8BDwEMAAgADIALAAy -AC0EPwRABD4ERAQ4BDsETABHAGUAbgBlAHIAaQBjACAARwByAGEAeQAgAEcAYQBtAG0AYQAgADIALgAy -ACAAUAByAG8AZgBpAGwAZQAAdGV4dAAAAABDb3B5cmlnaHQgQXBwbGUgSW5jLiwgMjAxMgAAWFlaIAAA -AAAAAPNRAAEAAAABFsxjdXJ2AAAAAAAABAAAAAAFAAoADwAUABkAHgAjACgALQAyADcAOwBAAEUASgBP -AFQAWQBeAGMAaABtAHIAdwB8AIEAhgCLAJAAlQCaAJ8ApACpAK4AsgC3ALwAwQDGAMsA0ADVANsA4ADl -AOsA8AD2APsBAQEHAQ0BEwEZAR8BJQErATIBOAE+AUUBTAFSAVkBYAFnAW4BdQF8AYMBiwGSAZoBoQGp -AbEBuQHBAckB0QHZAeEB6QHyAfoCAwIMAhQCHQImAi8COAJBAksCVAJdAmcCcQJ6AoQCjgKYAqICrAK2 -AsECywLVAuAC6wL1AwADCwMWAyEDLQM4A0MDTwNaA2YDcgN+A4oDlgOiA64DugPHA9MD4APsA/kEBgQT -BCAELQQ7BEgEVQRjBHEEfgSMBJoEqAS2BMQE0wThBPAE/gUNBRwFKwU6BUkFWAVnBXcFhgWWBaYFtQXF -BdUF5QX2BgYGFgYnBjcGSAZZBmoGewaMBp0GrwbABtEG4wb1BwcHGQcrBz0HTwdhB3QHhgeZB6wHvwfS -B+UH+AgLCB8IMghGCFoIbgiCCJYIqgi+CNII5wj7CRAJJQk6CU8JZAl5CY8JpAm6Cc8J5Qn7ChEKJwo9 -ClQKagqBCpgKrgrFCtwK8wsLCyILOQtRC2kLgAuYC7ALyAvhC/kMEgwqDEMMXAx1DI4MpwzADNkM8w0N -DSYNQA1aDXQNjg2pDcMN3g34DhMOLg5JDmQOfw6bDrYO0g7uDwkPJQ9BD14Peg+WD7MPzw/sEAkQJhBD -EGEQfhCbELkQ1xD1ERMRMRFPEW0RjBGqEckR6BIHEiYSRRJkEoQSoxLDEuMTAxMjE0MTYxODE6QTxRPl -FAYUJxRJFGoUixStFM4U8BUSFTQVVhV4FZsVvRXgFgMWJhZJFmwWjxayFtYW+hcdF0EXZReJF64X0hf3 -GBsYQBhlGIoYrxjVGPoZIBlFGWsZkRm3Gd0aBBoqGlEadxqeGsUa7BsUGzsbYxuKG7Ib2hwCHCocUhx7 -HKMczBz1HR4dRx1wHZkdwx3sHhYeQB5qHpQevh7pHxMfPh9pH5Qfvx/qIBUgQSBsIJggxCDwIRwhSCF1 -IaEhziH7IiciVSKCIq8i3SMKIzgjZiOUI8Ij8CQfJE0kfCSrJNolCSU4JWgllyXHJfcmJyZXJocmtybo -JxgnSSd6J6sn3CgNKD8ocSiiKNQpBik4KWspnSnQKgIqNSpoKpsqzysCKzYraSudK9EsBSw5LG4soizX -LQwtQS12Last4S4WLkwugi63Lu4vJC9aL5Evxy/+MDUwbDCkMNsxEjFKMYIxujHyMioyYzKbMtQzDTNG -M38zuDPxNCs0ZTSeNNg1EzVNNYc1wjX9Njc2cjauNuk3JDdgN5w31zgUOFA4jDjIOQU5Qjl/Obw5+To2 -OnQ6sjrvOy07azuqO+g8JzxlPKQ84z0iPWE9oT3gPiA+YD6gPuA/IT9hP6I/4kAjQGRApkDnQSlBakGs -Qe5CMEJyQrVC90M6Q31DwEQDREdEikTORRJFVUWaRd5GIkZnRqtG8Ec1R3tHwEgFSEtIkUjXSR1JY0mp -SfBKN0p9SsRLDEtTS5pL4kwqTHJMuk0CTUpNk03cTiVObk63TwBPSU+TT91QJ1BxULtRBlFQUZtR5lIx -UnxSx1MTU19TqlP2VEJUj1TbVShVdVXCVg9WXFapVvdXRFeSV+BYL1h9WMtZGllpWbhaB1pWWqZa9VtF -W5Vb5Vw1XIZc1l0nXXhdyV4aXmxevV8PX2Ffs2AFYFdgqmD8YU9homH1YklinGLwY0Njl2PrZEBklGTp -ZT1lkmXnZj1mkmboZz1nk2fpaD9olmjsaUNpmmnxakhqn2r3a09rp2v/bFdsr20IbWBtuW4SbmtuxG8e -b3hv0XArcIZw4HE6cZVx8HJLcqZzAXNdc7h0FHRwdMx1KHWFdeF2Pnabdvh3VnezeBF4bnjMeSp5iXnn -ekZ6pXsEe2N7wnwhfIF84X1BfaF+AX5ifsJ/I3+Ef+WAR4CogQqBa4HNgjCCkoL0g1eDuoQdhICE44VH -hauGDoZyhteHO4efiASIaYjOiTOJmYn+imSKyoswi5aL/IxjjMqNMY2Yjf+OZo7OjzaPnpAGkG6Q1pE/ -kaiSEZJ6kuOTTZO2lCCUipT0lV+VyZY0lp+XCpd1l+CYTJi4mSSZkJn8mmia1ZtCm6+cHJyJnPedZJ3S -nkCerp8dn4uf+qBpoNihR6G2oiailqMGo3aj5qRWpMelOKWpphqmi6b9p26n4KhSqMSpN6mpqhyqj6sC -q3Wr6axcrNCtRK24ri2uoa8Wr4uwALB1sOqxYLHWskuywrM4s660JbSctRO1irYBtnm28Ldot+C4WbjR -uUq5wro7urW7LrunvCG8m70VvY++Cr6Evv+/er/1wHDA7MFnwePCX8Lbw1jD1MRRxM7FS8XIxkbGw8dB -x7/IPci8yTrJuco4yrfLNsu2zDXMtc01zbXONs62zzfPuNA50LrRPNG+0j/SwdNE08bUSdTL1U7V0dZV -1tjXXNfg2GTY6Nls2fHadtr724DcBdyK3RDdlt4c3qLfKd+v4DbgveFE4cziU+Lb42Pj6+Rz5PzlhOYN -5pbnH+ep6DLovOlG6dDqW+rl63Dr++yG7RHtnO4o7rTvQO/M8Fjw5fFy8f/yjPMZ86f0NPTC9VD13vZt -9vv3ivgZ+Kj5OPnH+lf65/t3/Af8mP0p/br+S/7c/23//9IrLC0uWiRjbGFzc25hbWVYJGNsYXNzZXNf -EBBOU0JpdG1hcEltYWdlUmVwoy0vMFpOU0ltYWdlUmVwWE5TT2JqZWN00issMjNXTlNBcnJheaIyMNIr -LDU2Xk5TTXV0YWJsZUFycmF5ozUyMNM4OQ86OzxXTlNXaGl0ZVxOU0NvbG9yU3BhY2VEMCAwABADgAzS -Kyw+P1dOU0NvbG9yoj4w0issQUJXTlNJbWFnZaJBMAAIABEAGgAkACkAMgA3AEkATABRAFMAYgBoAHUA -fACLAJIAnwCmAK4AsACyALQAuQC7AL0AxADJANQA1gDYANoA3wDiAOQA5gDoAO8BBgEiASQBJhOWE5sT -phOvE8ITxhPRE9oT3xPnE+oT7xP+FAIUCRQRFB4UIxQlFCcULBQ0FDcUPBREAAAAAAAAAgEAAAAAAAAA -QwAAAAAAAAAAAAAAAAAAFEc +S2V5ZWRBcmNoaXZlctEICVRyb290gAGuCwwbHCEaJissMzY5P0JVJG51bGzXDQ4PEBESExQVFhcYGRpW +TlNTaXplViRjbGFzc1xOU0ltYWdlRmxhZ3NWTlNSZXBzW05TVGludENvbG9yV05TQ29sb3JeTlNSZXNp +emluZ01vZGWAAoANEiDDAACAA4AAgAsQAFZ7MSwgMX3SHQ4eIFpOUy5vYmplY3RzoR+ABIAK0h0OIiWi +IySABYAGgAnTDicoKSoaXxAUTlNUSUZGUmVwcmVzZW50YXRpb25fEBlOU0ludGVybmFsTGF5b3V0RGly +ZWN0aW9ugAiAB08REmxNTQAqAAAACgAAABABAAADAAAAAQABAAABAQADAAAAAQABAAABAgADAAAAAgAI +AAgBAwADAAAAAQABAAABBgADAAAAAQABAAABCgADAAAAAQABAAABEQAEAAAAAQAAAAgBEgADAAAAAQAB +AAABFQADAAAAAQACAAABFgADAAAAAQABAAABFwAEAAAAAQAAAAIBHAADAAAAAQABAAABKAADAAAAAQAC +AAABUgADAAAAAQABAAABUwADAAAAAgABAAGHcwAHAAARnAAAANAAAAAAAAARnGFwcGwCAAAAbW50ckdS +QVlYWVogB9wACAAXAA8ALgAPYWNzcEFQUEwAAAAAbm9uZQAAAAAAAAAAAAAAAAAAAAAAAPbWAAEAAAAA +0y1hcHBsAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFZGVzYwAA +AMAAAAB5ZHNjbQAAATwAAAgaY3BydAAACVgAAAAjd3RwdAAACXwAAAAUa1RSQwAACZAAAAgMZGVzYwAA +AAAAAAAfR2VuZXJpYyBHcmF5IEdhbW1hIDIuMiBQcm9maWxlAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAG1s +dWMAAAAAAAAAHwAAAAxza1NLAAAALgAAAYRkYURLAAAAOgAAAbJjYUVTAAAAOAAAAex2aVZOAAAAQAAA +AiRwdEJSAAAASgAAAmR1a1VBAAAALAAAAq5mckZVAAAAPgAAAtpodUhVAAAANAAAAxh6aFRXAAAAGgAA +A0xrb0tSAAAAIgAAA2ZuYk5PAAAAOgAAA4hjc0NaAAAAKAAAA8JoZUlMAAAAJAAAA+pyb1JPAAAAKgAA +BA5kZURFAAAATgAABDhpdElUAAAATgAABIZzdlNFAAAAOAAABNR6aENOAAAAGgAABQxqYUpQAAAAJgAA +BSZlbEdSAAAAKgAABUxwdFBPAAAAUgAABXZubE5MAAAAQAAABchlc0VTAAAATAAABgh0aFRIAAAAMgAA +BlR0clRSAAAAJAAABoZmaUZJAAAARgAABqpockhSAAAAPgAABvBwbFBMAAAASgAABy5hckVHAAAALAAA +B3hydVJVAAAAOgAAB6RlblVTAAAAPAAAB94AVgFhAGUAbwBiAGUAYwBuAOEAIABzAGkAdgDhACAAZwBh +AG0AYQAgADIALAAyAEcAZQBuAGUAcgBpAHMAawAgAGcAcgDlACAAMgAsADIAIABnAGEAbQBtAGEALQBw +AHIAbwBmAGkAbABHAGEAbQBtAGEAIABkAGUAIABnAHIAaQBzAG8AcwAgAGcAZQBuAOgAcgBpAGMAYQAg +ADIALgAyAEMepQB1ACAAaADsAG4AaAAgAE0A4AB1ACAAeADhAG0AIABDAGgAdQBuAGcAIABHAGEAbQBt +AGEAIAAyAC4AMgBQAGUAcgBmAGkAbAAgAEcAZQBuAOkAcgBpAGMAbwAgAGQAYQAgAEcAYQBtAGEAIABk +AGUAIABDAGkAbgB6AGEAcwAgADIALAAyBBcEMAQzBDAEOwRMBD0EMAAgAEcAcgBhAHkALQQzBDAEPAQw +ACAAMgAuADIAUAByAG8AZgBpAGwAIABnAOkAbgDpAHIAaQBxAHUAZQAgAGcAcgBpAHMAIABnAGEAbQBt +AGEAIAAyACwAMgDBAGwAdABhAGwA4QBuAG8AcwAgAHMAegD8AHIAawBlACAAZwBhAG0AbQBhACAAMgAu +ADKQGnUocHCWjlFJXqYAMgAuADKCcl9pY8+P8Md8vBgAINaMwMkAIKwQucgAIAAyAC4AMgAg1QS4XNMM +x3wARwBlAG4AZQByAGkAcwBrACAAZwByAOUAIABnAGEAbQBtAGEAIAAyACwAMgAtAHAAcgBvAGYAaQBs +AE8AYgBlAGMAbgDhACABYQBlAGQA4QAgAGcAYQBtAGEAIAAyAC4AMgXSBdAF3gXUACAF0AXkBdUF6AAg +BdsF3AXcBdkAIAAyAC4AMgBHAGEAbQBhACAAZwByAGkAIABnAGUAbgBlAHIAaQBjAQMAIAAyACwAMgBB +AGwAbABnAGUAbQBlAGkAbgBlAHMAIABHAHIAYQB1AHMAdAB1AGYAZQBuAC0AUAByAG8AZgBpAGwAIABH +AGEAbQBtAGEAIAAyACwAMgBQAHIAbwBmAGkAbABvACAAZwByAGkAZwBpAG8AIABnAGUAbgBlAHIAaQBj +AG8AIABkAGUAbABsAGEAIABnAGEAbQBtAGEAIAAyACwAMgBHAGUAbgBlAHIAaQBzAGsAIABnAHIA5QAg +ADIALAAyACAAZwBhAG0AbQBhAHAAcgBvAGYAaQBsZm6QGnBwXqZ8+2VwADIALgAyY8+P8GWHTvZOAIIs +MLAw7DCkMKww8zDeACAAMgAuADIAIDDXMO0w1TChMKQw6wOTA7UDvQO5A7oDzAAgA5MDugPBA7kAIAOT +A6wDvAO8A7EAIAAyAC4AMgBQAGUAcgBmAGkAbAAgAGcAZQBuAOkAcgBpAGMAbwAgAGQAZQAgAGMAaQBu +AHoAZQBuAHQAbwBzACAAZABhACAARwBhAG0AbQBhACAAMgAsADIAQQBsAGcAZQBtAGUAZQBuACAAZwBy +AGkAagBzACAAZwBhAG0AbQBhACAAMgAsADIALQBwAHIAbwBmAGkAZQBsAFAAZQByAGYAaQBsACAAZwBl +AG4A6QByAGkAYwBvACAAZABlACAAZwBhAG0AbQBhACAAZABlACAAZwByAGkAcwBlAHMAIAAyACwAMg4j +DjEOBw4qDjUOQQ4BDiEOIQ4yDkAOAQ4jDiIOTA4XDjEOSA4nDkQOGwAgADIALgAyAEcAZQBuAGUAbAAg +AEcAcgBpACAARwBhAG0AYQAgADIALAAyAFkAbABlAGkAbgBlAG4AIABoAGEAcgBtAGEAYQBuACAAZwBh +AG0AbQBhACAAMgAsADIAIAAtAHAAcgBvAGYAaQBpAGwAaQBHAGUAbgBlAHIAaQENAGsAaQAgAEcAcgBh +AHkAIABHAGEAbQBtAGEAIAAyAC4AMgAgAHAAcgBvAGYAaQBsAFUAbgBpAHcAZQByAHMAYQBsAG4AeQAg +AHAAcgBvAGYAaQBsACAAcwB6AGEAcgBvAVsAYwBpACAAZwBhAG0AbQBhACAAMgAsADIGOgYnBkUGJwAg +ADIALgAyACAGRAZIBkYAIAYxBkUGJwYvBkoAIAY5BicGRQQeBDEESQQwBE8AIARBBDUEQAQwBE8AIAQz +BDAEPAQ8BDAAIAAyACwAMgAtBD8EQAQ+BEQEOAQ7BEwARwBlAG4AZQByAGkAYwAgAEcAcgBhAHkAIABH +AGEAbQBtAGEAIAAyAC4AMgAgAFAAcgBvAGYAaQBsAGUAAHRleHQAAAAAQ29weXJpZ2h0IEFwcGxlIElu +Yy4sIDIwMTIAAFhZWiAAAAAAAADzUQABAAAAARbMY3VydgAAAAAAAAQAAAAABQAKAA8AFAAZAB4AIwAo +AC0AMgA3ADsAQABFAEoATwBUAFkAXgBjAGgAbQByAHcAfACBAIYAiwCQAJUAmgCfAKQAqQCuALIAtwC8 +AMEAxgDLANAA1QDbAOAA5QDrAPAA9gD7AQEBBwENARMBGQEfASUBKwEyATgBPgFFAUwBUgFZAWABZwFu +AXUBfAGDAYsBkgGaAaEBqQGxAbkBwQHJAdEB2QHhAekB8gH6AgMCDAIUAh0CJgIvAjgCQQJLAlQCXQJn +AnECegKEAo4CmAKiAqwCtgLBAssC1QLgAusC9QMAAwsDFgMhAy0DOANDA08DWgNmA3IDfgOKA5YDogOu +A7oDxwPTA+AD7AP5BAYEEwQgBC0EOwRIBFUEYwRxBH4EjASaBKgEtgTEBNME4QTwBP4FDQUcBSsFOgVJ +BVgFZwV3BYYFlgWmBbUFxQXVBeUF9gYGBhYGJwY3BkgGWQZqBnsGjAadBq8GwAbRBuMG9QcHBxkHKwc9 +B08HYQd0B4YHmQesB78H0gflB/gICwgfCDIIRghaCG4IggiWCKoIvgjSCOcI+wkQCSUJOglPCWQJeQmP +CaQJugnPCeUJ+woRCicKPQpUCmoKgQqYCq4KxQrcCvMLCwsiCzkLUQtpC4ALmAuwC8gL4Qv5DBIMKgxD +DFwMdQyODKcMwAzZDPMNDQ0mDUANWg10DY4NqQ3DDd4N+A4TDi4OSQ5kDn8Omw62DtIO7g8JDyUPQQ9e +D3oPlg+zD88P7BAJECYQQxBhEH4QmxC5ENcQ9RETETERTxFtEYwRqhHJEegSBxImEkUSZBKEEqMSwxLj +EwMTIxNDE2MTgxOkE8UT5RQGFCcUSRRqFIsUrRTOFPAVEhU0FVYVeBWbFb0V4BYDFiYWSRZsFo8WshbW +FvoXHRdBF2UXiReuF9IX9xgbGEAYZRiKGK8Y1Rj6GSAZRRlrGZEZtxndGgQaKhpRGncanhrFGuwbFBs7 +G2MbihuyG9ocAhwqHFIcexyjHMwc9R0eHUcdcB2ZHcMd7B4WHkAeah6UHr4e6R8THz4faR+UH78f6iAV +IEEgbCCYIMQg8CEcIUghdSGhIc4h+yInIlUigiKvIt0jCiM4I2YjlCPCI/AkHyRNJHwkqyTaJQklOCVo +JZclxyX3JicmVyaHJrcm6CcYJ0kneierJ9woDSg/KHEooijUKQYpOClrKZ0p0CoCKjUqaCqbKs8rAis2 +K2krnSvRLAUsOSxuLKIs1y0MLUEtdi2rLeEuFi5MLoIuty7uLyQvWi+RL8cv/jA1MGwwpDDbMRIxSjGC +Mbox8jIqMmMymzLUMw0zRjN/M7gz8TQrNGU0njTYNRM1TTWHNcI1/TY3NnI2rjbpNyQ3YDecN9c4FDhQ +OIw4yDkFOUI5fzm8Ofk6Njp0OrI67zstO2s7qjvoPCc8ZTykPOM9Ij1hPaE94D4gPmA+oD7gPyE/YT+i +P+JAI0BkQKZA50EpQWpBrEHuQjBCckK1QvdDOkN9Q8BEA0RHRIpEzkUSRVVFmkXeRiJGZ0arRvBHNUd7 +R8BIBUhLSJFI10kdSWNJqUnwSjdKfUrESwxLU0uaS+JMKkxyTLpNAk1KTZNN3E4lTm5Ot08AT0lPk0/d +UCdQcVC7UQZRUFGbUeZSMVJ8UsdTE1NfU6pT9lRCVI9U21UoVXVVwlYPVlxWqVb3V0RXklfgWC9YfVjL +WRpZaVm4WgdaVlqmWvVbRVuVW+VcNVyGXNZdJ114XcleGl5sXr1fD19hX7NgBWBXYKpg/GFPYaJh9WJJ +Ypxi8GNDY5dj62RAZJRk6WU9ZZJl52Y9ZpJm6Gc9Z5Nn6Wg/aJZo7GlDaZpp8WpIap9q92tPa6dr/2xX +bK9tCG1gbbluEm5rbsRvHm94b9FwK3CGcOBxOnGVcfByS3KmcwFzXXO4dBR0cHTMdSh1hXXhdj52m3b4 +d1Z3s3gReG54zHkqeYl553pGeqV7BHtje8J8IXyBfOF9QX2hfgF+Yn7CfyN/hH/lgEeAqIEKgWuBzYIw +gpKC9INXg7qEHYSAhOOFR4Wrhg6GcobXhzuHn4gEiGmIzokziZmJ/opkisqLMIuWi/yMY4zKjTGNmI3/ +jmaOzo82j56QBpBukNaRP5GokhGSepLjk02TtpQglIqU9JVflcmWNJaflwqXdZfgmEyYuJkkmZCZ/Jpo +mtWbQpuvnByciZz3nWSd0p5Anq6fHZ+Ln/qgaaDYoUehtqImopajBqN2o+akVqTHpTilqaYapoum/adu +p+CoUqjEqTepqaocqo+rAqt1q+msXKzQrUStuK4trqGvFq+LsACwdbDqsWCx1rJLssKzOLOutCW0nLUT +tYq2AbZ5tvC3aLfguFm40blKucK6O7q1uy67p7whvJu9Fb2Pvgq+hL7/v3q/9cBwwOzBZ8Hjwl/C28NY +w9TEUcTOxUvFyMZGxsPHQce/yD3IvMk6ybnKOMq3yzbLtsw1zLXNNc21zjbOts83z7jQOdC60TzRvtI/ +0sHTRNPG1EnUy9VO1dHWVdbY11zX4Nhk2OjZbNnx2nba+9uA3AXcit0Q3ZbeHN6i3ynfr+A24L3hROHM +4lPi2+Nj4+vkc+T85YTmDeaW5x/nqegy6LzpRunQ6lvq5etw6/vshu0R7ZzuKO6070DvzPBY8OXxcvH/ +8ozzGfOn9DT0wvVQ9d72bfb794r4Gfio+Tj5x/pX+uf7d/wH/Jj9Kf26/kv+3P9t///SLS4vMFokY2xh +c3NuYW1lWCRjbGFzc2VzXxAQTlNCaXRtYXBJbWFnZVJlcKMvMTJaTlNJbWFnZVJlcFhOU09iamVjdNIt +LjQ1V05TQXJyYXmiNDLSLS43OF5OU011dGFibGVBcnJheaM3NDLTOjsOPD0+V05TV2hpdGVcTlNDb2xv +clNwYWNlRDAgMAAQA4AM0i0uQEFXTlNDb2xvcqJAMtItLkNEV05TSW1hZ2WiQzIACAARABoAJAApADIA +NwBJAEwAUQBTAGIAaAB3AH4AhQCSAJkApQCtALwAvgDAAMUAxwDJAMsAzQDUANkA5ADmAOgA6gDvAPIA +9AD2APgA/wEWATIBNAE2E6YTqxO2E78T0hPWE+ET6hPvE/cT+hP/FA4UEhQZFCEULhQzFDUUNxQ8FEQU +RxRMFFQAAAAAAAACAQAAAAAAAABFAAAAAAAAAAAAAAAAAAAUVw diff --git a/i18n/src/main/resources/sl.lproj/Command.xib b/i18n/src/main/resources/sl.lproj/Command.xib index d9c161ae60..e6682d6794 100644 --- a/i18n/src/main/resources/sl.lproj/Command.xib +++ b/i18n/src/main/resources/sl.lproj/Command.xib @@ -1,7 +1,7 @@ - + - + diff --git a/i18n/src/main/resources/sl.lproj/Connection.xib b/i18n/src/main/resources/sl.lproj/Connection.xib index b5ab788c25..273a7112a5 100644 --- a/i18n/src/main/resources/sl.lproj/Connection.xib +++ b/i18n/src/main/resources/sl.lproj/Connection.xib @@ -1,7 +1,7 @@ - + - + @@ -76,7 +76,7 @@ - + diff --git a/i18n/src/main/resources/sl.lproj/Info.xib b/i18n/src/main/resources/sl.lproj/Info.xib index 65bf6c14f0..12ffaa6ec1 100644 --- a/i18n/src/main/resources/sl.lproj/Info.xib +++ b/i18n/src/main/resources/sl.lproj/Info.xib @@ -1,7 +1,7 @@ - + - + @@ -89,7 +89,7 @@ - + @@ -1017,21 +1017,21 @@ - + - + - + @@ -1121,21 +1121,21 @@ - + - + - + @@ -1250,27 +1250,27 @@ diff --git a/i18n/src/main/resources/sl.lproj/Login.strings b/i18n/src/main/resources/sl.lproj/Login.strings index 9285e33158..9b8e567402 100644 Binary files a/i18n/src/main/resources/sl.lproj/Login.strings and b/i18n/src/main/resources/sl.lproj/Login.strings differ diff --git a/i18n/src/main/resources/sl.lproj/Main.xib b/i18n/src/main/resources/sl.lproj/Main.xib index f073d0ebb5..60b5998b34 100644 --- a/i18n/src/main/resources/sl.lproj/Main.xib +++ b/i18n/src/main/resources/sl.lproj/Main.xib @@ -1,7 +1,7 @@ - + - + @@ -159,19 +159,19 @@ - + - + - + @@ -321,12 +321,12 @@ CA - + - + @@ -566,6 +566,10 @@ CA - + + + + + diff --git a/i18n/src/main/resources/sl.lproj/Preferences.xib b/i18n/src/main/resources/sl.lproj/Preferences.xib index e6653e694c..b87f5ed6f8 100644 --- a/i18n/src/main/resources/sl.lproj/Preferences.xib +++ b/i18n/src/main/resources/sl.lproj/Preferences.xib @@ -1,7 +1,7 @@ - + - + @@ -266,7 +266,7 @@ - + @@ -307,7 +307,7 @@ - + @@ -538,7 +538,7 @@ - + @@ -578,7 +578,7 @@ - + @@ -613,7 +613,7 @@ - + @@ -642,7 +642,7 @@ - + @@ -1218,7 +1218,7 @@ - + @@ -1250,7 +1250,7 @@ - + @@ -1290,7 +1290,7 @@ - + @@ -1363,7 +1363,7 @@ - + @@ -1558,7 +1558,7 @@ - + @@ -1618,7 +1618,7 @@ - + @@ -1643,7 +1643,7 @@ - + @@ -1685,7 +1685,7 @@ - + @@ -1713,7 +1713,7 @@ - + @@ -1741,7 +1741,7 @@ - + @@ -1770,7 +1770,7 @@ - + @@ -1805,7 +1805,7 @@ - + @@ -1888,7 +1888,7 @@ - + @@ -1947,7 +1947,7 @@ - + @@ -1975,7 +1975,7 @@ - + @@ -2004,7 +2004,7 @@ - + diff --git a/i18n/src/main/resources/sl.lproj/Profiles.xib b/i18n/src/main/resources/sl.lproj/Profiles.xib index 18af5a58f4..20e5ce0f36 100644 --- a/i18n/src/main/resources/sl.lproj/Profiles.xib +++ b/i18n/src/main/resources/sl.lproj/Profiles.xib @@ -1,7 +1,7 @@ - + - + diff --git a/i18n/src/main/resources/sl.lproj/Prompt.xib b/i18n/src/main/resources/sl.lproj/Prompt.xib index 9328c6e79f..4efd826700 100644 --- a/i18n/src/main/resources/sl.lproj/Prompt.xib +++ b/i18n/src/main/resources/sl.lproj/Prompt.xib @@ -1,7 +1,7 @@ - + - + @@ -95,7 +95,7 @@ Gw - + diff --git a/i18n/src/main/resources/sl.lproj/S3.strings b/i18n/src/main/resources/sl.lproj/S3.strings index fdb879400f..ed566d9da8 100644 Binary files a/i18n/src/main/resources/sl.lproj/S3.strings and b/i18n/src/main/resources/sl.lproj/S3.strings differ diff --git a/i18n/src/main/resources/sl.lproj/Updater.strings b/i18n/src/main/resources/sl.lproj/Updater.strings index 3ce8b3a3b4..edef78c3be 100644 Binary files a/i18n/src/main/resources/sl.lproj/Updater.strings and b/i18n/src/main/resources/sl.lproj/Updater.strings differ diff --git a/i18n/src/main/resources/sr.lproj/Bookmark.xib b/i18n/src/main/resources/sr.lproj/Bookmark.xib index c0997ba487..f816fa633b 100644 --- a/i18n/src/main/resources/sr.lproj/Bookmark.xib +++ b/i18n/src/main/resources/sr.lproj/Bookmark.xib @@ -1,7 +1,7 @@ - + - + @@ -92,7 +92,7 @@ - + @@ -489,95 +489,95 @@ YnBsaXN0MDDUAQIDBAUGBwpYJHZlcnNpb25ZJGFyY2hpdmVyVCR0b3BYJG9iamVjdHMSAAGGoF8QD05T -S2V5ZWRBcmNoaXZlctEICVRyb290gAGuCwwZGh8UJCkqMTQ3PUBVJG51bGzWDQ4PEBESExQVFhcYVk5T -U2l6ZV5OU1Jlc2l6aW5nTW9kZVYkY2xhc3NcTlNJbWFnZUZsYWdzVk5TUmVwc1dOU0NvbG9ygAIQAIAN -EiDDAACAA4ALVnsxLCAxfdIbDxweWk5TLm9iamVjdHOhHYAEgArSGw8gI6IhIoAFgAaACdMPJSYnKBRf -EBROU1RJRkZSZXByZXNlbnRhdGlvbl8QGU5TSW50ZXJuYWxMYXlvdXREaXJlY3Rpb26ACIAHTxESbE1N -ACoAAAAKAAAAEAEAAAMAAAABAAEAAAEBAAMAAAABAAEAAAECAAMAAAACAAgACAEDAAMAAAABAAEAAAEG -AAMAAAABAAEAAAEKAAMAAAABAAEAAAERAAQAAAABAAAACAESAAMAAAABAAEAAAEVAAMAAAABAAIAAAEW -AAMAAAABAAEAAAEXAAQAAAABAAAAAgEcAAMAAAABAAEAAAEoAAMAAAABAAIAAAFSAAMAAAABAAEAAAFT -AAMAAAACAAEAAYdzAAcAABGcAAAA0AAAAAAAABGcYXBwbAIAAABtbnRyR1JBWVhZWiAH3AAIABcADwAu -AA9hY3NwQVBQTAAAAABub25lAAAAAAAAAAAAAAAAAAAAAAAA9tYAAQAAAADTLWFwcGwAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAVkZXNjAAAAwAAAAHlkc2NtAAABPAAA -CBpjcHJ0AAAJWAAAACN3dHB0AAAJfAAAABRrVFJDAAAJkAAACAxkZXNjAAAAAAAAAB9HZW5lcmljIEdy -YXkgR2FtbWEgMi4yIFByb2ZpbGUAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAbWx1YwAAAAAAAAAfAAAADHNr -U0sAAAAuAAABhGRhREsAAAA6AAABsmNhRVMAAAA4AAAB7HZpVk4AAABAAAACJHB0QlIAAABKAAACZHVr -VUEAAAAsAAACrmZyRlUAAAA+AAAC2mh1SFUAAAA0AAADGHpoVFcAAAAaAAADTGtvS1IAAAAiAAADZm5i -Tk8AAAA6AAADiGNzQ1oAAAAoAAADwmhlSUwAAAAkAAAD6nJvUk8AAAAqAAAEDmRlREUAAABOAAAEOGl0 -SVQAAABOAAAEhnN2U0UAAAA4AAAE1HpoQ04AAAAaAAAFDGphSlAAAAAmAAAFJmVsR1IAAAAqAAAFTHB0 -UE8AAABSAAAFdm5sTkwAAABAAAAFyGVzRVMAAABMAAAGCHRoVEgAAAAyAAAGVHRyVFIAAAAkAAAGhmZp -RkkAAABGAAAGqmhySFIAAAA+AAAG8HBsUEwAAABKAAAHLmFyRUcAAAAsAAAHeHJ1UlUAAAA6AAAHpGVu -VVMAAAA8AAAH3gBWAWEAZQBvAGIAZQBjAG4A4QAgAHMAaQB2AOEAIABnAGEAbQBhACAAMgAsADIARwBl -AG4AZQByAGkAcwBrACAAZwByAOUAIAAyACwAMgAgAGcAYQBtAG0AYQAtAHAAcgBvAGYAaQBsAEcAYQBt -AG0AYQAgAGQAZQAgAGcAcgBpAHMAbwBzACAAZwBlAG4A6AByAGkAYwBhACAAMgAuADIAQx6lAHUAIABo -AOwAbgBoACAATQDgAHUAIAB4AOEAbQAgAEMAaAB1AG4AZwAgAEcAYQBtAG0AYQAgADIALgAyAFAAZQBy -AGYAaQBsACAARwBlAG4A6QByAGkAYwBvACAAZABhACAARwBhAG0AYQAgAGQAZQAgAEMAaQBuAHoAYQBz -ACAAMgAsADIEFwQwBDMEMAQ7BEwEPQQwACAARwByAGEAeQAtBDMEMAQ8BDAAIAAyAC4AMgBQAHIAbwBm -AGkAbAAgAGcA6QBuAOkAcgBpAHEAdQBlACAAZwByAGkAcwAgAGcAYQBtAG0AYQAgADIALAAyAMEAbAB0 -AGEAbADhAG4AbwBzACAAcwB6APwAcgBrAGUAIABnAGEAbQBtAGEAIAAyAC4AMpAadShwcJaOUUlepgAy -AC4AMoJyX2ljz4/wx3y8GAAg1ozAyQAgrBC5yAAgADIALgAyACDVBLhc0wzHfABHAGUAbgBlAHIAaQBz -AGsAIABnAHIA5QAgAGcAYQBtAG0AYQAgADIALAAyAC0AcAByAG8AZgBpAGwATwBiAGUAYwBuAOEAIAFh -AGUAZADhACAAZwBhAG0AYQAgADIALgAyBdIF0AXeBdQAIAXQBeQF1QXoACAF2wXcBdwF2QAgADIALgAy -AEcAYQBtAGEAIABnAHIAaQAgAGcAZQBuAGUAcgBpAGMBAwAgADIALAAyAEEAbABsAGcAZQBtAGUAaQBu -AGUAcwAgAEcAcgBhAHUAcwB0AHUAZgBlAG4ALQBQAHIAbwBmAGkAbAAgAEcAYQBtAG0AYQAgADIALAAy -AFAAcgBvAGYAaQBsAG8AIABnAHIAaQBnAGkAbwAgAGcAZQBuAGUAcgBpAGMAbwAgAGQAZQBsAGwAYQAg -AGcAYQBtAG0AYQAgADIALAAyAEcAZQBuAGUAcgBpAHMAawAgAGcAcgDlACAAMgAsADIAIABnAGEAbQBt -AGEAcAByAG8AZgBpAGxmbpAacHBepnz7ZXAAMgAuADJjz4/wZYdO9k4AgiwwsDDsMKQwrDDzMN4AIAAy -AC4AMgAgMNcw7TDVMKEwpDDrA5MDtQO9A7kDugPMACADkwO6A8EDuQAgA5MDrAO8A7wDsQAgADIALgAy -AFAAZQByAGYAaQBsACAAZwBlAG4A6QByAGkAYwBvACAAZABlACAAYwBpAG4AegBlAG4AdABvAHMAIABk -AGEAIABHAGEAbQBtAGEAIAAyACwAMgBBAGwAZwBlAG0AZQBlAG4AIABnAHIAaQBqAHMAIABnAGEAbQBt -AGEAIAAyACwAMgAtAHAAcgBvAGYAaQBlAGwAUABlAHIAZgBpAGwAIABnAGUAbgDpAHIAaQBjAG8AIABk -AGUAIABnAGEAbQBtAGEAIABkAGUAIABnAHIAaQBzAGUAcwAgADIALAAyDiMOMQ4HDioONQ5BDgEOIQ4h -DjIOQA4BDiMOIg5MDhcOMQ5IDicORA4bACAAMgAuADIARwBlAG4AZQBsACAARwByAGkAIABHAGEAbQBh -ACAAMgAsADIAWQBsAGUAaQBuAGUAbgAgAGgAYQByAG0AYQBhAG4AIABnAGEAbQBtAGEAIAAyACwAMgAg -AC0AcAByAG8AZgBpAGkAbABpAEcAZQBuAGUAcgBpAQ0AawBpACAARwByAGEAeQAgAEcAYQBtAG0AYQAg -ADIALgAyACAAcAByAG8AZgBpAGwAVQBuAGkAdwBlAHIAcwBhAGwAbgB5ACAAcAByAG8AZgBpAGwAIABz -AHoAYQByAG8BWwBjAGkAIABnAGEAbQBtAGEAIAAyACwAMgY6BicGRQYnACAAMgAuADIAIAZEBkgGRgAg -BjEGRQYnBi8GSgAgBjkGJwZFBB4EMQRJBDAETwAgBEEENQRABDAETwAgBDMEMAQ8BDwEMAAgADIALAAy -AC0EPwRABD4ERAQ4BDsETABHAGUAbgBlAHIAaQBjACAARwByAGEAeQAgAEcAYQBtAG0AYQAgADIALgAy -ACAAUAByAG8AZgBpAGwAZQAAdGV4dAAAAABDb3B5cmlnaHQgQXBwbGUgSW5jLiwgMjAxMgAAWFlaIAAA -AAAAAPNRAAEAAAABFsxjdXJ2AAAAAAAABAAAAAAFAAoADwAUABkAHgAjACgALQAyADcAOwBAAEUASgBP -AFQAWQBeAGMAaABtAHIAdwB8AIEAhgCLAJAAlQCaAJ8ApACpAK4AsgC3ALwAwQDGAMsA0ADVANsA4ADl -AOsA8AD2APsBAQEHAQ0BEwEZAR8BJQErATIBOAE+AUUBTAFSAVkBYAFnAW4BdQF8AYMBiwGSAZoBoQGp -AbEBuQHBAckB0QHZAeEB6QHyAfoCAwIMAhQCHQImAi8COAJBAksCVAJdAmcCcQJ6AoQCjgKYAqICrAK2 -AsECywLVAuAC6wL1AwADCwMWAyEDLQM4A0MDTwNaA2YDcgN+A4oDlgOiA64DugPHA9MD4APsA/kEBgQT -BCAELQQ7BEgEVQRjBHEEfgSMBJoEqAS2BMQE0wThBPAE/gUNBRwFKwU6BUkFWAVnBXcFhgWWBaYFtQXF -BdUF5QX2BgYGFgYnBjcGSAZZBmoGewaMBp0GrwbABtEG4wb1BwcHGQcrBz0HTwdhB3QHhgeZB6wHvwfS -B+UH+AgLCB8IMghGCFoIbgiCCJYIqgi+CNII5wj7CRAJJQk6CU8JZAl5CY8JpAm6Cc8J5Qn7ChEKJwo9 -ClQKagqBCpgKrgrFCtwK8wsLCyILOQtRC2kLgAuYC7ALyAvhC/kMEgwqDEMMXAx1DI4MpwzADNkM8w0N -DSYNQA1aDXQNjg2pDcMN3g34DhMOLg5JDmQOfw6bDrYO0g7uDwkPJQ9BD14Peg+WD7MPzw/sEAkQJhBD -EGEQfhCbELkQ1xD1ERMRMRFPEW0RjBGqEckR6BIHEiYSRRJkEoQSoxLDEuMTAxMjE0MTYxODE6QTxRPl -FAYUJxRJFGoUixStFM4U8BUSFTQVVhV4FZsVvRXgFgMWJhZJFmwWjxayFtYW+hcdF0EXZReJF64X0hf3 -GBsYQBhlGIoYrxjVGPoZIBlFGWsZkRm3Gd0aBBoqGlEadxqeGsUa7BsUGzsbYxuKG7Ib2hwCHCocUhx7 -HKMczBz1HR4dRx1wHZkdwx3sHhYeQB5qHpQevh7pHxMfPh9pH5Qfvx/qIBUgQSBsIJggxCDwIRwhSCF1 -IaEhziH7IiciVSKCIq8i3SMKIzgjZiOUI8Ij8CQfJE0kfCSrJNolCSU4JWgllyXHJfcmJyZXJocmtybo -JxgnSSd6J6sn3CgNKD8ocSiiKNQpBik4KWspnSnQKgIqNSpoKpsqzysCKzYraSudK9EsBSw5LG4soizX -LQwtQS12Last4S4WLkwugi63Lu4vJC9aL5Evxy/+MDUwbDCkMNsxEjFKMYIxujHyMioyYzKbMtQzDTNG -M38zuDPxNCs0ZTSeNNg1EzVNNYc1wjX9Njc2cjauNuk3JDdgN5w31zgUOFA4jDjIOQU5Qjl/Obw5+To2 -OnQ6sjrvOy07azuqO+g8JzxlPKQ84z0iPWE9oT3gPiA+YD6gPuA/IT9hP6I/4kAjQGRApkDnQSlBakGs -Qe5CMEJyQrVC90M6Q31DwEQDREdEikTORRJFVUWaRd5GIkZnRqtG8Ec1R3tHwEgFSEtIkUjXSR1JY0mp -SfBKN0p9SsRLDEtTS5pL4kwqTHJMuk0CTUpNk03cTiVObk63TwBPSU+TT91QJ1BxULtRBlFQUZtR5lIx -UnxSx1MTU19TqlP2VEJUj1TbVShVdVXCVg9WXFapVvdXRFeSV+BYL1h9WMtZGllpWbhaB1pWWqZa9VtF -W5Vb5Vw1XIZc1l0nXXhdyV4aXmxevV8PX2Ffs2AFYFdgqmD8YU9homH1YklinGLwY0Njl2PrZEBklGTp -ZT1lkmXnZj1mkmboZz1nk2fpaD9olmjsaUNpmmnxakhqn2r3a09rp2v/bFdsr20IbWBtuW4SbmtuxG8e -b3hv0XArcIZw4HE6cZVx8HJLcqZzAXNdc7h0FHRwdMx1KHWFdeF2Pnabdvh3VnezeBF4bnjMeSp5iXnn -ekZ6pXsEe2N7wnwhfIF84X1BfaF+AX5ifsJ/I3+Ef+WAR4CogQqBa4HNgjCCkoL0g1eDuoQdhICE44VH -hauGDoZyhteHO4efiASIaYjOiTOJmYn+imSKyoswi5aL/IxjjMqNMY2Yjf+OZo7OjzaPnpAGkG6Q1pE/ -kaiSEZJ6kuOTTZO2lCCUipT0lV+VyZY0lp+XCpd1l+CYTJi4mSSZkJn8mmia1ZtCm6+cHJyJnPedZJ3S -nkCerp8dn4uf+qBpoNihR6G2oiailqMGo3aj5qRWpMelOKWpphqmi6b9p26n4KhSqMSpN6mpqhyqj6sC -q3Wr6axcrNCtRK24ri2uoa8Wr4uwALB1sOqxYLHWskuywrM4s660JbSctRO1irYBtnm28Ldot+C4WbjR -uUq5wro7urW7LrunvCG8m70VvY++Cr6Evv+/er/1wHDA7MFnwePCX8Lbw1jD1MRRxM7FS8XIxkbGw8dB -x7/IPci8yTrJuco4yrfLNsu2zDXMtc01zbXONs62zzfPuNA50LrRPNG+0j/SwdNE08bUSdTL1U7V0dZV -1tjXXNfg2GTY6Nls2fHadtr724DcBdyK3RDdlt4c3qLfKd+v4DbgveFE4cziU+Lb42Pj6+Rz5PzlhOYN -5pbnH+ep6DLovOlG6dDqW+rl63Dr++yG7RHtnO4o7rTvQO/M8Fjw5fFy8f/yjPMZ86f0NPTC9VD13vZt -9vv3ivgZ+Kj5OPnH+lf65/t3/Af8mP0p/br+S/7c/23//9IrLC0uWiRjbGFzc25hbWVYJGNsYXNzZXNf -EBBOU0JpdG1hcEltYWdlUmVwoy0vMFpOU0ltYWdlUmVwWE5TT2JqZWN00issMjNXTlNBcnJheaIyMNIr -LDU2Xk5TTXV0YWJsZUFycmF5ozUyMNM4OQ86OzxXTlNXaGl0ZVxOU0NvbG9yU3BhY2VEMCAwABADgAzS -Kyw+P1dOU0NvbG9yoj4w0issQUJXTlNJbWFnZaJBMAAIABEAGgAkACkAMgA3AEkATABRAFMAYgBoAHUA -fACLAJIAnwCmAK4AsACyALQAuQC7AL0AxADJANQA1gDYANoA3wDiAOQA5gDoAO8BBgEiASQBJhOWE5sT -phOvE8ITxhPRE9oT3xPnE+oT7xP+FAIUCRQRFB4UIxQlFCcULBQ0FDcUPBREAAAAAAAAAgEAAAAAAAAA -QwAAAAAAAAAAAAAAAAAAFEc +S2V5ZWRBcmNoaXZlctEICVRyb290gAGuCwwbHCEaJissMzY5P0JVJG51bGzXDQ4PEBESExQVFhcYGRpW +TlNTaXplViRjbGFzc1xOU0ltYWdlRmxhZ3NWTlNSZXBzW05TVGludENvbG9yV05TQ29sb3JeTlNSZXNp +emluZ01vZGWAAoANEiDDAACAA4AAgAsQAFZ7MSwgMX3SHQ4eIFpOUy5vYmplY3RzoR+ABIAK0h0OIiWi +IySABYAGgAnTDicoKSoaXxAUTlNUSUZGUmVwcmVzZW50YXRpb25fEBlOU0ludGVybmFsTGF5b3V0RGly +ZWN0aW9ugAiAB08REmxNTQAqAAAACgAAABABAAADAAAAAQABAAABAQADAAAAAQABAAABAgADAAAAAgAI +AAgBAwADAAAAAQABAAABBgADAAAAAQABAAABCgADAAAAAQABAAABEQAEAAAAAQAAAAgBEgADAAAAAQAB +AAABFQADAAAAAQACAAABFgADAAAAAQABAAABFwAEAAAAAQAAAAIBHAADAAAAAQABAAABKAADAAAAAQAC +AAABUgADAAAAAQABAAABUwADAAAAAgABAAGHcwAHAAARnAAAANAAAAAAAAARnGFwcGwCAAAAbW50ckdS +QVlYWVogB9wACAAXAA8ALgAPYWNzcEFQUEwAAAAAbm9uZQAAAAAAAAAAAAAAAAAAAAAAAPbWAAEAAAAA +0y1hcHBsAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFZGVzYwAA +AMAAAAB5ZHNjbQAAATwAAAgaY3BydAAACVgAAAAjd3RwdAAACXwAAAAUa1RSQwAACZAAAAgMZGVzYwAA +AAAAAAAfR2VuZXJpYyBHcmF5IEdhbW1hIDIuMiBQcm9maWxlAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAG1s +dWMAAAAAAAAAHwAAAAxza1NLAAAALgAAAYRkYURLAAAAOgAAAbJjYUVTAAAAOAAAAex2aVZOAAAAQAAA +AiRwdEJSAAAASgAAAmR1a1VBAAAALAAAAq5mckZVAAAAPgAAAtpodUhVAAAANAAAAxh6aFRXAAAAGgAA +A0xrb0tSAAAAIgAAA2ZuYk5PAAAAOgAAA4hjc0NaAAAAKAAAA8JoZUlMAAAAJAAAA+pyb1JPAAAAKgAA +BA5kZURFAAAATgAABDhpdElUAAAATgAABIZzdlNFAAAAOAAABNR6aENOAAAAGgAABQxqYUpQAAAAJgAA +BSZlbEdSAAAAKgAABUxwdFBPAAAAUgAABXZubE5MAAAAQAAABchlc0VTAAAATAAABgh0aFRIAAAAMgAA +BlR0clRSAAAAJAAABoZmaUZJAAAARgAABqpockhSAAAAPgAABvBwbFBMAAAASgAABy5hckVHAAAALAAA +B3hydVJVAAAAOgAAB6RlblVTAAAAPAAAB94AVgFhAGUAbwBiAGUAYwBuAOEAIABzAGkAdgDhACAAZwBh +AG0AYQAgADIALAAyAEcAZQBuAGUAcgBpAHMAawAgAGcAcgDlACAAMgAsADIAIABnAGEAbQBtAGEALQBw +AHIAbwBmAGkAbABHAGEAbQBtAGEAIABkAGUAIABnAHIAaQBzAG8AcwAgAGcAZQBuAOgAcgBpAGMAYQAg +ADIALgAyAEMepQB1ACAAaADsAG4AaAAgAE0A4AB1ACAAeADhAG0AIABDAGgAdQBuAGcAIABHAGEAbQBt +AGEAIAAyAC4AMgBQAGUAcgBmAGkAbAAgAEcAZQBuAOkAcgBpAGMAbwAgAGQAYQAgAEcAYQBtAGEAIABk +AGUAIABDAGkAbgB6AGEAcwAgADIALAAyBBcEMAQzBDAEOwRMBD0EMAAgAEcAcgBhAHkALQQzBDAEPAQw +ACAAMgAuADIAUAByAG8AZgBpAGwAIABnAOkAbgDpAHIAaQBxAHUAZQAgAGcAcgBpAHMAIABnAGEAbQBt +AGEAIAAyACwAMgDBAGwAdABhAGwA4QBuAG8AcwAgAHMAegD8AHIAawBlACAAZwBhAG0AbQBhACAAMgAu +ADKQGnUocHCWjlFJXqYAMgAuADKCcl9pY8+P8Md8vBgAINaMwMkAIKwQucgAIAAyAC4AMgAg1QS4XNMM +x3wARwBlAG4AZQByAGkAcwBrACAAZwByAOUAIABnAGEAbQBtAGEAIAAyACwAMgAtAHAAcgBvAGYAaQBs +AE8AYgBlAGMAbgDhACABYQBlAGQA4QAgAGcAYQBtAGEAIAAyAC4AMgXSBdAF3gXUACAF0AXkBdUF6AAg +BdsF3AXcBdkAIAAyAC4AMgBHAGEAbQBhACAAZwByAGkAIABnAGUAbgBlAHIAaQBjAQMAIAAyACwAMgBB +AGwAbABnAGUAbQBlAGkAbgBlAHMAIABHAHIAYQB1AHMAdAB1AGYAZQBuAC0AUAByAG8AZgBpAGwAIABH +AGEAbQBtAGEAIAAyACwAMgBQAHIAbwBmAGkAbABvACAAZwByAGkAZwBpAG8AIABnAGUAbgBlAHIAaQBj +AG8AIABkAGUAbABsAGEAIABnAGEAbQBtAGEAIAAyACwAMgBHAGUAbgBlAHIAaQBzAGsAIABnAHIA5QAg +ADIALAAyACAAZwBhAG0AbQBhAHAAcgBvAGYAaQBsZm6QGnBwXqZ8+2VwADIALgAyY8+P8GWHTvZOAIIs +MLAw7DCkMKww8zDeACAAMgAuADIAIDDXMO0w1TChMKQw6wOTA7UDvQO5A7oDzAAgA5MDugPBA7kAIAOT +A6wDvAO8A7EAIAAyAC4AMgBQAGUAcgBmAGkAbAAgAGcAZQBuAOkAcgBpAGMAbwAgAGQAZQAgAGMAaQBu +AHoAZQBuAHQAbwBzACAAZABhACAARwBhAG0AbQBhACAAMgAsADIAQQBsAGcAZQBtAGUAZQBuACAAZwBy +AGkAagBzACAAZwBhAG0AbQBhACAAMgAsADIALQBwAHIAbwBmAGkAZQBsAFAAZQByAGYAaQBsACAAZwBl +AG4A6QByAGkAYwBvACAAZABlACAAZwBhAG0AbQBhACAAZABlACAAZwByAGkAcwBlAHMAIAAyACwAMg4j +DjEOBw4qDjUOQQ4BDiEOIQ4yDkAOAQ4jDiIOTA4XDjEOSA4nDkQOGwAgADIALgAyAEcAZQBuAGUAbAAg +AEcAcgBpACAARwBhAG0AYQAgADIALAAyAFkAbABlAGkAbgBlAG4AIABoAGEAcgBtAGEAYQBuACAAZwBh +AG0AbQBhACAAMgAsADIAIAAtAHAAcgBvAGYAaQBpAGwAaQBHAGUAbgBlAHIAaQENAGsAaQAgAEcAcgBh +AHkAIABHAGEAbQBtAGEAIAAyAC4AMgAgAHAAcgBvAGYAaQBsAFUAbgBpAHcAZQByAHMAYQBsAG4AeQAg +AHAAcgBvAGYAaQBsACAAcwB6AGEAcgBvAVsAYwBpACAAZwBhAG0AbQBhACAAMgAsADIGOgYnBkUGJwAg +ADIALgAyACAGRAZIBkYAIAYxBkUGJwYvBkoAIAY5BicGRQQeBDEESQQwBE8AIARBBDUEQAQwBE8AIAQz +BDAEPAQ8BDAAIAAyACwAMgAtBD8EQAQ+BEQEOAQ7BEwARwBlAG4AZQByAGkAYwAgAEcAcgBhAHkAIABH +AGEAbQBtAGEAIAAyAC4AMgAgAFAAcgBvAGYAaQBsAGUAAHRleHQAAAAAQ29weXJpZ2h0IEFwcGxlIElu +Yy4sIDIwMTIAAFhZWiAAAAAAAADzUQABAAAAARbMY3VydgAAAAAAAAQAAAAABQAKAA8AFAAZAB4AIwAo +AC0AMgA3ADsAQABFAEoATwBUAFkAXgBjAGgAbQByAHcAfACBAIYAiwCQAJUAmgCfAKQAqQCuALIAtwC8 +AMEAxgDLANAA1QDbAOAA5QDrAPAA9gD7AQEBBwENARMBGQEfASUBKwEyATgBPgFFAUwBUgFZAWABZwFu +AXUBfAGDAYsBkgGaAaEBqQGxAbkBwQHJAdEB2QHhAekB8gH6AgMCDAIUAh0CJgIvAjgCQQJLAlQCXQJn +AnECegKEAo4CmAKiAqwCtgLBAssC1QLgAusC9QMAAwsDFgMhAy0DOANDA08DWgNmA3IDfgOKA5YDogOu +A7oDxwPTA+AD7AP5BAYEEwQgBC0EOwRIBFUEYwRxBH4EjASaBKgEtgTEBNME4QTwBP4FDQUcBSsFOgVJ +BVgFZwV3BYYFlgWmBbUFxQXVBeUF9gYGBhYGJwY3BkgGWQZqBnsGjAadBq8GwAbRBuMG9QcHBxkHKwc9 +B08HYQd0B4YHmQesB78H0gflB/gICwgfCDIIRghaCG4IggiWCKoIvgjSCOcI+wkQCSUJOglPCWQJeQmP +CaQJugnPCeUJ+woRCicKPQpUCmoKgQqYCq4KxQrcCvMLCwsiCzkLUQtpC4ALmAuwC8gL4Qv5DBIMKgxD +DFwMdQyODKcMwAzZDPMNDQ0mDUANWg10DY4NqQ3DDd4N+A4TDi4OSQ5kDn8Omw62DtIO7g8JDyUPQQ9e +D3oPlg+zD88P7BAJECYQQxBhEH4QmxC5ENcQ9RETETERTxFtEYwRqhHJEegSBxImEkUSZBKEEqMSwxLj +EwMTIxNDE2MTgxOkE8UT5RQGFCcUSRRqFIsUrRTOFPAVEhU0FVYVeBWbFb0V4BYDFiYWSRZsFo8WshbW +FvoXHRdBF2UXiReuF9IX9xgbGEAYZRiKGK8Y1Rj6GSAZRRlrGZEZtxndGgQaKhpRGncanhrFGuwbFBs7 +G2MbihuyG9ocAhwqHFIcexyjHMwc9R0eHUcdcB2ZHcMd7B4WHkAeah6UHr4e6R8THz4faR+UH78f6iAV +IEEgbCCYIMQg8CEcIUghdSGhIc4h+yInIlUigiKvIt0jCiM4I2YjlCPCI/AkHyRNJHwkqyTaJQklOCVo +JZclxyX3JicmVyaHJrcm6CcYJ0kneierJ9woDSg/KHEooijUKQYpOClrKZ0p0CoCKjUqaCqbKs8rAis2 +K2krnSvRLAUsOSxuLKIs1y0MLUEtdi2rLeEuFi5MLoIuty7uLyQvWi+RL8cv/jA1MGwwpDDbMRIxSjGC +Mbox8jIqMmMymzLUMw0zRjN/M7gz8TQrNGU0njTYNRM1TTWHNcI1/TY3NnI2rjbpNyQ3YDecN9c4FDhQ +OIw4yDkFOUI5fzm8Ofk6Njp0OrI67zstO2s7qjvoPCc8ZTykPOM9Ij1hPaE94D4gPmA+oD7gPyE/YT+i +P+JAI0BkQKZA50EpQWpBrEHuQjBCckK1QvdDOkN9Q8BEA0RHRIpEzkUSRVVFmkXeRiJGZ0arRvBHNUd7 +R8BIBUhLSJFI10kdSWNJqUnwSjdKfUrESwxLU0uaS+JMKkxyTLpNAk1KTZNN3E4lTm5Ot08AT0lPk0/d +UCdQcVC7UQZRUFGbUeZSMVJ8UsdTE1NfU6pT9lRCVI9U21UoVXVVwlYPVlxWqVb3V0RXklfgWC9YfVjL +WRpZaVm4WgdaVlqmWvVbRVuVW+VcNVyGXNZdJ114XcleGl5sXr1fD19hX7NgBWBXYKpg/GFPYaJh9WJJ +Ypxi8GNDY5dj62RAZJRk6WU9ZZJl52Y9ZpJm6Gc9Z5Nn6Wg/aJZo7GlDaZpp8WpIap9q92tPa6dr/2xX +bK9tCG1gbbluEm5rbsRvHm94b9FwK3CGcOBxOnGVcfByS3KmcwFzXXO4dBR0cHTMdSh1hXXhdj52m3b4 +d1Z3s3gReG54zHkqeYl553pGeqV7BHtje8J8IXyBfOF9QX2hfgF+Yn7CfyN/hH/lgEeAqIEKgWuBzYIw +gpKC9INXg7qEHYSAhOOFR4Wrhg6GcobXhzuHn4gEiGmIzokziZmJ/opkisqLMIuWi/yMY4zKjTGNmI3/ +jmaOzo82j56QBpBukNaRP5GokhGSepLjk02TtpQglIqU9JVflcmWNJaflwqXdZfgmEyYuJkkmZCZ/Jpo +mtWbQpuvnByciZz3nWSd0p5Anq6fHZ+Ln/qgaaDYoUehtqImopajBqN2o+akVqTHpTilqaYapoum/adu +p+CoUqjEqTepqaocqo+rAqt1q+msXKzQrUStuK4trqGvFq+LsACwdbDqsWCx1rJLssKzOLOutCW0nLUT +tYq2AbZ5tvC3aLfguFm40blKucK6O7q1uy67p7whvJu9Fb2Pvgq+hL7/v3q/9cBwwOzBZ8Hjwl/C28NY +w9TEUcTOxUvFyMZGxsPHQce/yD3IvMk6ybnKOMq3yzbLtsw1zLXNNc21zjbOts83z7jQOdC60TzRvtI/ +0sHTRNPG1EnUy9VO1dHWVdbY11zX4Nhk2OjZbNnx2nba+9uA3AXcit0Q3ZbeHN6i3ynfr+A24L3hROHM +4lPi2+Nj4+vkc+T85YTmDeaW5x/nqegy6LzpRunQ6lvq5etw6/vshu0R7ZzuKO6070DvzPBY8OXxcvH/ +8ozzGfOn9DT0wvVQ9d72bfb794r4Gfio+Tj5x/pX+uf7d/wH/Jj9Kf26/kv+3P9t///SLS4vMFokY2xh +c3NuYW1lWCRjbGFzc2VzXxAQTlNCaXRtYXBJbWFnZVJlcKMvMTJaTlNJbWFnZVJlcFhOU09iamVjdNIt +LjQ1V05TQXJyYXmiNDLSLS43OF5OU011dGFibGVBcnJheaM3NDLTOjsOPD0+V05TV2hpdGVcTlNDb2xv +clNwYWNlRDAgMAAQA4AM0i0uQEFXTlNDb2xvcqJAMtItLkNEV05TSW1hZ2WiQzIACAARABoAJAApADIA +NwBJAEwAUQBTAGIAaAB3AH4AhQCSAJkApQCtALwAvgDAAMUAxwDJAMsAzQDUANkA5ADmAOgA6gDvAPIA +9AD2APgA/wEWATIBNAE2E6YTqxO2E78T0hPWE+ET6hPvE/cT+hP/FA4UEhQZFCEULhQzFDUUNxQ8FEQU +RxRMFFQAAAAAAAACAQAAAAAAAABFAAAAAAAAAAAAAAAAAAAUVw diff --git a/i18n/src/main/resources/sr.lproj/Browser.xib b/i18n/src/main/resources/sr.lproj/Browser.xib index 5d1fbfbf20..e092d25dff 100644 --- a/i18n/src/main/resources/sr.lproj/Browser.xib +++ b/i18n/src/main/resources/sr.lproj/Browser.xib @@ -1,7 +1,7 @@ - + - + @@ -38,7 +38,7 @@ - + @@ -62,27 +62,27 @@ @@ -256,7 +256,7 @@ - + @@ -553,95 +553,95 @@ DQ YnBsaXN0MDDUAQIDBAUGBwpYJHZlcnNpb25ZJGFyY2hpdmVyVCR0b3BYJG9iamVjdHMSAAGGoF8QD05T -S2V5ZWRBcmNoaXZlctEICVRyb290gAGuCwwZGh8UJCkqMTQ3PUBVJG51bGzWDQ4PEBESExQVFhcYVk5T -U2l6ZV5OU1Jlc2l6aW5nTW9kZVYkY2xhc3NcTlNJbWFnZUZsYWdzVk5TUmVwc1dOU0NvbG9ygAIQAIAN -EiDDAACAA4ALVnsxLCAxfdIbDxweWk5TLm9iamVjdHOhHYAEgArSGw8gI6IhIoAFgAaACdMPJSYnKBRf -EBROU1RJRkZSZXByZXNlbnRhdGlvbl8QGU5TSW50ZXJuYWxMYXlvdXREaXJlY3Rpb26ACIAHTxESbE1N -ACoAAAAKAAAAEAEAAAMAAAABAAEAAAEBAAMAAAABAAEAAAECAAMAAAACAAgACAEDAAMAAAABAAEAAAEG -AAMAAAABAAEAAAEKAAMAAAABAAEAAAERAAQAAAABAAAACAESAAMAAAABAAEAAAEVAAMAAAABAAIAAAEW -AAMAAAABAAEAAAEXAAQAAAABAAAAAgEcAAMAAAABAAEAAAEoAAMAAAABAAIAAAFSAAMAAAABAAEAAAFT -AAMAAAACAAEAAYdzAAcAABGcAAAA0AAAAAAAABGcYXBwbAIAAABtbnRyR1JBWVhZWiAH3AAIABcADwAu -AA9hY3NwQVBQTAAAAABub25lAAAAAAAAAAAAAAAAAAAAAAAA9tYAAQAAAADTLWFwcGwAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAVkZXNjAAAAwAAAAHlkc2NtAAABPAAA -CBpjcHJ0AAAJWAAAACN3dHB0AAAJfAAAABRrVFJDAAAJkAAACAxkZXNjAAAAAAAAAB9HZW5lcmljIEdy -YXkgR2FtbWEgMi4yIFByb2ZpbGUAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAbWx1YwAAAAAAAAAfAAAADHNr -U0sAAAAuAAABhGRhREsAAAA6AAABsmNhRVMAAAA4AAAB7HZpVk4AAABAAAACJHB0QlIAAABKAAACZHVr -VUEAAAAsAAACrmZyRlUAAAA+AAAC2mh1SFUAAAA0AAADGHpoVFcAAAAaAAADTGtvS1IAAAAiAAADZm5i -Tk8AAAA6AAADiGNzQ1oAAAAoAAADwmhlSUwAAAAkAAAD6nJvUk8AAAAqAAAEDmRlREUAAABOAAAEOGl0 -SVQAAABOAAAEhnN2U0UAAAA4AAAE1HpoQ04AAAAaAAAFDGphSlAAAAAmAAAFJmVsR1IAAAAqAAAFTHB0 -UE8AAABSAAAFdm5sTkwAAABAAAAFyGVzRVMAAABMAAAGCHRoVEgAAAAyAAAGVHRyVFIAAAAkAAAGhmZp -RkkAAABGAAAGqmhySFIAAAA+AAAG8HBsUEwAAABKAAAHLmFyRUcAAAAsAAAHeHJ1UlUAAAA6AAAHpGVu -VVMAAAA8AAAH3gBWAWEAZQBvAGIAZQBjAG4A4QAgAHMAaQB2AOEAIABnAGEAbQBhACAAMgAsADIARwBl -AG4AZQByAGkAcwBrACAAZwByAOUAIAAyACwAMgAgAGcAYQBtAG0AYQAtAHAAcgBvAGYAaQBsAEcAYQBt -AG0AYQAgAGQAZQAgAGcAcgBpAHMAbwBzACAAZwBlAG4A6AByAGkAYwBhACAAMgAuADIAQx6lAHUAIABo -AOwAbgBoACAATQDgAHUAIAB4AOEAbQAgAEMAaAB1AG4AZwAgAEcAYQBtAG0AYQAgADIALgAyAFAAZQBy -AGYAaQBsACAARwBlAG4A6QByAGkAYwBvACAAZABhACAARwBhAG0AYQAgAGQAZQAgAEMAaQBuAHoAYQBz -ACAAMgAsADIEFwQwBDMEMAQ7BEwEPQQwACAARwByAGEAeQAtBDMEMAQ8BDAAIAAyAC4AMgBQAHIAbwBm -AGkAbAAgAGcA6QBuAOkAcgBpAHEAdQBlACAAZwByAGkAcwAgAGcAYQBtAG0AYQAgADIALAAyAMEAbAB0 -AGEAbADhAG4AbwBzACAAcwB6APwAcgBrAGUAIABnAGEAbQBtAGEAIAAyAC4AMpAadShwcJaOUUlepgAy -AC4AMoJyX2ljz4/wx3y8GAAg1ozAyQAgrBC5yAAgADIALgAyACDVBLhc0wzHfABHAGUAbgBlAHIAaQBz -AGsAIABnAHIA5QAgAGcAYQBtAG0AYQAgADIALAAyAC0AcAByAG8AZgBpAGwATwBiAGUAYwBuAOEAIAFh -AGUAZADhACAAZwBhAG0AYQAgADIALgAyBdIF0AXeBdQAIAXQBeQF1QXoACAF2wXcBdwF2QAgADIALgAy -AEcAYQBtAGEAIABnAHIAaQAgAGcAZQBuAGUAcgBpAGMBAwAgADIALAAyAEEAbABsAGcAZQBtAGUAaQBu -AGUAcwAgAEcAcgBhAHUAcwB0AHUAZgBlAG4ALQBQAHIAbwBmAGkAbAAgAEcAYQBtAG0AYQAgADIALAAy -AFAAcgBvAGYAaQBsAG8AIABnAHIAaQBnAGkAbwAgAGcAZQBuAGUAcgBpAGMAbwAgAGQAZQBsAGwAYQAg -AGcAYQBtAG0AYQAgADIALAAyAEcAZQBuAGUAcgBpAHMAawAgAGcAcgDlACAAMgAsADIAIABnAGEAbQBt -AGEAcAByAG8AZgBpAGxmbpAacHBepnz7ZXAAMgAuADJjz4/wZYdO9k4AgiwwsDDsMKQwrDDzMN4AIAAy -AC4AMgAgMNcw7TDVMKEwpDDrA5MDtQO9A7kDugPMACADkwO6A8EDuQAgA5MDrAO8A7wDsQAgADIALgAy -AFAAZQByAGYAaQBsACAAZwBlAG4A6QByAGkAYwBvACAAZABlACAAYwBpAG4AegBlAG4AdABvAHMAIABk -AGEAIABHAGEAbQBtAGEAIAAyACwAMgBBAGwAZwBlAG0AZQBlAG4AIABnAHIAaQBqAHMAIABnAGEAbQBt -AGEAIAAyACwAMgAtAHAAcgBvAGYAaQBlAGwAUABlAHIAZgBpAGwAIABnAGUAbgDpAHIAaQBjAG8AIABk -AGUAIABnAGEAbQBtAGEAIABkAGUAIABnAHIAaQBzAGUAcwAgADIALAAyDiMOMQ4HDioONQ5BDgEOIQ4h -DjIOQA4BDiMOIg5MDhcOMQ5IDicORA4bACAAMgAuADIARwBlAG4AZQBsACAARwByAGkAIABHAGEAbQBh -ACAAMgAsADIAWQBsAGUAaQBuAGUAbgAgAGgAYQByAG0AYQBhAG4AIABnAGEAbQBtAGEAIAAyACwAMgAg -AC0AcAByAG8AZgBpAGkAbABpAEcAZQBuAGUAcgBpAQ0AawBpACAARwByAGEAeQAgAEcAYQBtAG0AYQAg -ADIALgAyACAAcAByAG8AZgBpAGwAVQBuAGkAdwBlAHIAcwBhAGwAbgB5ACAAcAByAG8AZgBpAGwAIABz -AHoAYQByAG8BWwBjAGkAIABnAGEAbQBtAGEAIAAyACwAMgY6BicGRQYnACAAMgAuADIAIAZEBkgGRgAg -BjEGRQYnBi8GSgAgBjkGJwZFBB4EMQRJBDAETwAgBEEENQRABDAETwAgBDMEMAQ8BDwEMAAgADIALAAy -AC0EPwRABD4ERAQ4BDsETABHAGUAbgBlAHIAaQBjACAARwByAGEAeQAgAEcAYQBtAG0AYQAgADIALgAy -ACAAUAByAG8AZgBpAGwAZQAAdGV4dAAAAABDb3B5cmlnaHQgQXBwbGUgSW5jLiwgMjAxMgAAWFlaIAAA -AAAAAPNRAAEAAAABFsxjdXJ2AAAAAAAABAAAAAAFAAoADwAUABkAHgAjACgALQAyADcAOwBAAEUASgBP -AFQAWQBeAGMAaABtAHIAdwB8AIEAhgCLAJAAlQCaAJ8ApACpAK4AsgC3ALwAwQDGAMsA0ADVANsA4ADl -AOsA8AD2APsBAQEHAQ0BEwEZAR8BJQErATIBOAE+AUUBTAFSAVkBYAFnAW4BdQF8AYMBiwGSAZoBoQGp -AbEBuQHBAckB0QHZAeEB6QHyAfoCAwIMAhQCHQImAi8COAJBAksCVAJdAmcCcQJ6AoQCjgKYAqICrAK2 -AsECywLVAuAC6wL1AwADCwMWAyEDLQM4A0MDTwNaA2YDcgN+A4oDlgOiA64DugPHA9MD4APsA/kEBgQT -BCAELQQ7BEgEVQRjBHEEfgSMBJoEqAS2BMQE0wThBPAE/gUNBRwFKwU6BUkFWAVnBXcFhgWWBaYFtQXF -BdUF5QX2BgYGFgYnBjcGSAZZBmoGewaMBp0GrwbABtEG4wb1BwcHGQcrBz0HTwdhB3QHhgeZB6wHvwfS -B+UH+AgLCB8IMghGCFoIbgiCCJYIqgi+CNII5wj7CRAJJQk6CU8JZAl5CY8JpAm6Cc8J5Qn7ChEKJwo9 -ClQKagqBCpgKrgrFCtwK8wsLCyILOQtRC2kLgAuYC7ALyAvhC/kMEgwqDEMMXAx1DI4MpwzADNkM8w0N -DSYNQA1aDXQNjg2pDcMN3g34DhMOLg5JDmQOfw6bDrYO0g7uDwkPJQ9BD14Peg+WD7MPzw/sEAkQJhBD -EGEQfhCbELkQ1xD1ERMRMRFPEW0RjBGqEckR6BIHEiYSRRJkEoQSoxLDEuMTAxMjE0MTYxODE6QTxRPl -FAYUJxRJFGoUixStFM4U8BUSFTQVVhV4FZsVvRXgFgMWJhZJFmwWjxayFtYW+hcdF0EXZReJF64X0hf3 -GBsYQBhlGIoYrxjVGPoZIBlFGWsZkRm3Gd0aBBoqGlEadxqeGsUa7BsUGzsbYxuKG7Ib2hwCHCocUhx7 -HKMczBz1HR4dRx1wHZkdwx3sHhYeQB5qHpQevh7pHxMfPh9pH5Qfvx/qIBUgQSBsIJggxCDwIRwhSCF1 -IaEhziH7IiciVSKCIq8i3SMKIzgjZiOUI8Ij8CQfJE0kfCSrJNolCSU4JWgllyXHJfcmJyZXJocmtybo -JxgnSSd6J6sn3CgNKD8ocSiiKNQpBik4KWspnSnQKgIqNSpoKpsqzysCKzYraSudK9EsBSw5LG4soizX -LQwtQS12Last4S4WLkwugi63Lu4vJC9aL5Evxy/+MDUwbDCkMNsxEjFKMYIxujHyMioyYzKbMtQzDTNG -M38zuDPxNCs0ZTSeNNg1EzVNNYc1wjX9Njc2cjauNuk3JDdgN5w31zgUOFA4jDjIOQU5Qjl/Obw5+To2 -OnQ6sjrvOy07azuqO+g8JzxlPKQ84z0iPWE9oT3gPiA+YD6gPuA/IT9hP6I/4kAjQGRApkDnQSlBakGs -Qe5CMEJyQrVC90M6Q31DwEQDREdEikTORRJFVUWaRd5GIkZnRqtG8Ec1R3tHwEgFSEtIkUjXSR1JY0mp -SfBKN0p9SsRLDEtTS5pL4kwqTHJMuk0CTUpNk03cTiVObk63TwBPSU+TT91QJ1BxULtRBlFQUZtR5lIx -UnxSx1MTU19TqlP2VEJUj1TbVShVdVXCVg9WXFapVvdXRFeSV+BYL1h9WMtZGllpWbhaB1pWWqZa9VtF -W5Vb5Vw1XIZc1l0nXXhdyV4aXmxevV8PX2Ffs2AFYFdgqmD8YU9homH1YklinGLwY0Njl2PrZEBklGTp -ZT1lkmXnZj1mkmboZz1nk2fpaD9olmjsaUNpmmnxakhqn2r3a09rp2v/bFdsr20IbWBtuW4SbmtuxG8e -b3hv0XArcIZw4HE6cZVx8HJLcqZzAXNdc7h0FHRwdMx1KHWFdeF2Pnabdvh3VnezeBF4bnjMeSp5iXnn -ekZ6pXsEe2N7wnwhfIF84X1BfaF+AX5ifsJ/I3+Ef+WAR4CogQqBa4HNgjCCkoL0g1eDuoQdhICE44VH -hauGDoZyhteHO4efiASIaYjOiTOJmYn+imSKyoswi5aL/IxjjMqNMY2Yjf+OZo7OjzaPnpAGkG6Q1pE/ -kaiSEZJ6kuOTTZO2lCCUipT0lV+VyZY0lp+XCpd1l+CYTJi4mSSZkJn8mmia1ZtCm6+cHJyJnPedZJ3S -nkCerp8dn4uf+qBpoNihR6G2oiailqMGo3aj5qRWpMelOKWpphqmi6b9p26n4KhSqMSpN6mpqhyqj6sC -q3Wr6axcrNCtRK24ri2uoa8Wr4uwALB1sOqxYLHWskuywrM4s660JbSctRO1irYBtnm28Ldot+C4WbjR -uUq5wro7urW7LrunvCG8m70VvY++Cr6Evv+/er/1wHDA7MFnwePCX8Lbw1jD1MRRxM7FS8XIxkbGw8dB -x7/IPci8yTrJuco4yrfLNsu2zDXMtc01zbXONs62zzfPuNA50LrRPNG+0j/SwdNE08bUSdTL1U7V0dZV -1tjXXNfg2GTY6Nls2fHadtr724DcBdyK3RDdlt4c3qLfKd+v4DbgveFE4cziU+Lb42Pj6+Rz5PzlhOYN -5pbnH+ep6DLovOlG6dDqW+rl63Dr++yG7RHtnO4o7rTvQO/M8Fjw5fFy8f/yjPMZ86f0NPTC9VD13vZt -9vv3ivgZ+Kj5OPnH+lf65/t3/Af8mP0p/br+S/7c/23//9IrLC0uWiRjbGFzc25hbWVYJGNsYXNzZXNf -EBBOU0JpdG1hcEltYWdlUmVwoy0vMFpOU0ltYWdlUmVwWE5TT2JqZWN00issMjNXTlNBcnJheaIyMNIr -LDU2Xk5TTXV0YWJsZUFycmF5ozUyMNM4OQ86OzxXTlNXaGl0ZVxOU0NvbG9yU3BhY2VEMCAwABADgAzS -Kyw+P1dOU0NvbG9yoj4w0issQUJXTlNJbWFnZaJBMAAIABEAGgAkACkAMgA3AEkATABRAFMAYgBoAHUA -fACLAJIAnwCmAK4AsACyALQAuQC7AL0AxADJANQA1gDYANoA3wDiAOQA5gDoAO8BBgEiASQBJhOWE5sT -phOvE8ITxhPRE9oT3xPnE+oT7xP+FAIUCRQRFB4UIxQlFCcULBQ0FDcUPBREAAAAAAAAAgEAAAAAAAAA -QwAAAAAAAAAAAAAAAAAAFEc +S2V5ZWRBcmNoaXZlctEICVRyb290gAGuCwwbHCEaJissMzY5P0JVJG51bGzXDQ4PEBESExQVFhcYGRpW +TlNTaXplViRjbGFzc1xOU0ltYWdlRmxhZ3NWTlNSZXBzW05TVGludENvbG9yV05TQ29sb3JeTlNSZXNp +emluZ01vZGWAAoANEiDDAACAA4AAgAsQAFZ7MSwgMX3SHQ4eIFpOUy5vYmplY3RzoR+ABIAK0h0OIiWi +IySABYAGgAnTDicoKSoaXxAUTlNUSUZGUmVwcmVzZW50YXRpb25fEBlOU0ludGVybmFsTGF5b3V0RGly +ZWN0aW9ugAiAB08REmxNTQAqAAAACgAAABABAAADAAAAAQABAAABAQADAAAAAQABAAABAgADAAAAAgAI +AAgBAwADAAAAAQABAAABBgADAAAAAQABAAABCgADAAAAAQABAAABEQAEAAAAAQAAAAgBEgADAAAAAQAB +AAABFQADAAAAAQACAAABFgADAAAAAQABAAABFwAEAAAAAQAAAAIBHAADAAAAAQABAAABKAADAAAAAQAC +AAABUgADAAAAAQABAAABUwADAAAAAgABAAGHcwAHAAARnAAAANAAAAAAAAARnGFwcGwCAAAAbW50ckdS +QVlYWVogB9wACAAXAA8ALgAPYWNzcEFQUEwAAAAAbm9uZQAAAAAAAAAAAAAAAAAAAAAAAPbWAAEAAAAA +0y1hcHBsAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFZGVzYwAA +AMAAAAB5ZHNjbQAAATwAAAgaY3BydAAACVgAAAAjd3RwdAAACXwAAAAUa1RSQwAACZAAAAgMZGVzYwAA +AAAAAAAfR2VuZXJpYyBHcmF5IEdhbW1hIDIuMiBQcm9maWxlAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAG1s +dWMAAAAAAAAAHwAAAAxza1NLAAAALgAAAYRkYURLAAAAOgAAAbJjYUVTAAAAOAAAAex2aVZOAAAAQAAA +AiRwdEJSAAAASgAAAmR1a1VBAAAALAAAAq5mckZVAAAAPgAAAtpodUhVAAAANAAAAxh6aFRXAAAAGgAA +A0xrb0tSAAAAIgAAA2ZuYk5PAAAAOgAAA4hjc0NaAAAAKAAAA8JoZUlMAAAAJAAAA+pyb1JPAAAAKgAA +BA5kZURFAAAATgAABDhpdElUAAAATgAABIZzdlNFAAAAOAAABNR6aENOAAAAGgAABQxqYUpQAAAAJgAA +BSZlbEdSAAAAKgAABUxwdFBPAAAAUgAABXZubE5MAAAAQAAABchlc0VTAAAATAAABgh0aFRIAAAAMgAA +BlR0clRSAAAAJAAABoZmaUZJAAAARgAABqpockhSAAAAPgAABvBwbFBMAAAASgAABy5hckVHAAAALAAA +B3hydVJVAAAAOgAAB6RlblVTAAAAPAAAB94AVgFhAGUAbwBiAGUAYwBuAOEAIABzAGkAdgDhACAAZwBh +AG0AYQAgADIALAAyAEcAZQBuAGUAcgBpAHMAawAgAGcAcgDlACAAMgAsADIAIABnAGEAbQBtAGEALQBw +AHIAbwBmAGkAbABHAGEAbQBtAGEAIABkAGUAIABnAHIAaQBzAG8AcwAgAGcAZQBuAOgAcgBpAGMAYQAg +ADIALgAyAEMepQB1ACAAaADsAG4AaAAgAE0A4AB1ACAAeADhAG0AIABDAGgAdQBuAGcAIABHAGEAbQBt +AGEAIAAyAC4AMgBQAGUAcgBmAGkAbAAgAEcAZQBuAOkAcgBpAGMAbwAgAGQAYQAgAEcAYQBtAGEAIABk +AGUAIABDAGkAbgB6AGEAcwAgADIALAAyBBcEMAQzBDAEOwRMBD0EMAAgAEcAcgBhAHkALQQzBDAEPAQw +ACAAMgAuADIAUAByAG8AZgBpAGwAIABnAOkAbgDpAHIAaQBxAHUAZQAgAGcAcgBpAHMAIABnAGEAbQBt +AGEAIAAyACwAMgDBAGwAdABhAGwA4QBuAG8AcwAgAHMAegD8AHIAawBlACAAZwBhAG0AbQBhACAAMgAu +ADKQGnUocHCWjlFJXqYAMgAuADKCcl9pY8+P8Md8vBgAINaMwMkAIKwQucgAIAAyAC4AMgAg1QS4XNMM +x3wARwBlAG4AZQByAGkAcwBrACAAZwByAOUAIABnAGEAbQBtAGEAIAAyACwAMgAtAHAAcgBvAGYAaQBs +AE8AYgBlAGMAbgDhACABYQBlAGQA4QAgAGcAYQBtAGEAIAAyAC4AMgXSBdAF3gXUACAF0AXkBdUF6AAg +BdsF3AXcBdkAIAAyAC4AMgBHAGEAbQBhACAAZwByAGkAIABnAGUAbgBlAHIAaQBjAQMAIAAyACwAMgBB +AGwAbABnAGUAbQBlAGkAbgBlAHMAIABHAHIAYQB1AHMAdAB1AGYAZQBuAC0AUAByAG8AZgBpAGwAIABH +AGEAbQBtAGEAIAAyACwAMgBQAHIAbwBmAGkAbABvACAAZwByAGkAZwBpAG8AIABnAGUAbgBlAHIAaQBj +AG8AIABkAGUAbABsAGEAIABnAGEAbQBtAGEAIAAyACwAMgBHAGUAbgBlAHIAaQBzAGsAIABnAHIA5QAg +ADIALAAyACAAZwBhAG0AbQBhAHAAcgBvAGYAaQBsZm6QGnBwXqZ8+2VwADIALgAyY8+P8GWHTvZOAIIs +MLAw7DCkMKww8zDeACAAMgAuADIAIDDXMO0w1TChMKQw6wOTA7UDvQO5A7oDzAAgA5MDugPBA7kAIAOT +A6wDvAO8A7EAIAAyAC4AMgBQAGUAcgBmAGkAbAAgAGcAZQBuAOkAcgBpAGMAbwAgAGQAZQAgAGMAaQBu +AHoAZQBuAHQAbwBzACAAZABhACAARwBhAG0AbQBhACAAMgAsADIAQQBsAGcAZQBtAGUAZQBuACAAZwBy +AGkAagBzACAAZwBhAG0AbQBhACAAMgAsADIALQBwAHIAbwBmAGkAZQBsAFAAZQByAGYAaQBsACAAZwBl +AG4A6QByAGkAYwBvACAAZABlACAAZwBhAG0AbQBhACAAZABlACAAZwByAGkAcwBlAHMAIAAyACwAMg4j +DjEOBw4qDjUOQQ4BDiEOIQ4yDkAOAQ4jDiIOTA4XDjEOSA4nDkQOGwAgADIALgAyAEcAZQBuAGUAbAAg +AEcAcgBpACAARwBhAG0AYQAgADIALAAyAFkAbABlAGkAbgBlAG4AIABoAGEAcgBtAGEAYQBuACAAZwBh +AG0AbQBhACAAMgAsADIAIAAtAHAAcgBvAGYAaQBpAGwAaQBHAGUAbgBlAHIAaQENAGsAaQAgAEcAcgBh +AHkAIABHAGEAbQBtAGEAIAAyAC4AMgAgAHAAcgBvAGYAaQBsAFUAbgBpAHcAZQByAHMAYQBsAG4AeQAg +AHAAcgBvAGYAaQBsACAAcwB6AGEAcgBvAVsAYwBpACAAZwBhAG0AbQBhACAAMgAsADIGOgYnBkUGJwAg +ADIALgAyACAGRAZIBkYAIAYxBkUGJwYvBkoAIAY5BicGRQQeBDEESQQwBE8AIARBBDUEQAQwBE8AIAQz +BDAEPAQ8BDAAIAAyACwAMgAtBD8EQAQ+BEQEOAQ7BEwARwBlAG4AZQByAGkAYwAgAEcAcgBhAHkAIABH +AGEAbQBtAGEAIAAyAC4AMgAgAFAAcgBvAGYAaQBsAGUAAHRleHQAAAAAQ29weXJpZ2h0IEFwcGxlIElu +Yy4sIDIwMTIAAFhZWiAAAAAAAADzUQABAAAAARbMY3VydgAAAAAAAAQAAAAABQAKAA8AFAAZAB4AIwAo +AC0AMgA3ADsAQABFAEoATwBUAFkAXgBjAGgAbQByAHcAfACBAIYAiwCQAJUAmgCfAKQAqQCuALIAtwC8 +AMEAxgDLANAA1QDbAOAA5QDrAPAA9gD7AQEBBwENARMBGQEfASUBKwEyATgBPgFFAUwBUgFZAWABZwFu +AXUBfAGDAYsBkgGaAaEBqQGxAbkBwQHJAdEB2QHhAekB8gH6AgMCDAIUAh0CJgIvAjgCQQJLAlQCXQJn +AnECegKEAo4CmAKiAqwCtgLBAssC1QLgAusC9QMAAwsDFgMhAy0DOANDA08DWgNmA3IDfgOKA5YDogOu +A7oDxwPTA+AD7AP5BAYEEwQgBC0EOwRIBFUEYwRxBH4EjASaBKgEtgTEBNME4QTwBP4FDQUcBSsFOgVJ +BVgFZwV3BYYFlgWmBbUFxQXVBeUF9gYGBhYGJwY3BkgGWQZqBnsGjAadBq8GwAbRBuMG9QcHBxkHKwc9 +B08HYQd0B4YHmQesB78H0gflB/gICwgfCDIIRghaCG4IggiWCKoIvgjSCOcI+wkQCSUJOglPCWQJeQmP +CaQJugnPCeUJ+woRCicKPQpUCmoKgQqYCq4KxQrcCvMLCwsiCzkLUQtpC4ALmAuwC8gL4Qv5DBIMKgxD +DFwMdQyODKcMwAzZDPMNDQ0mDUANWg10DY4NqQ3DDd4N+A4TDi4OSQ5kDn8Omw62DtIO7g8JDyUPQQ9e +D3oPlg+zD88P7BAJECYQQxBhEH4QmxC5ENcQ9RETETERTxFtEYwRqhHJEegSBxImEkUSZBKEEqMSwxLj +EwMTIxNDE2MTgxOkE8UT5RQGFCcUSRRqFIsUrRTOFPAVEhU0FVYVeBWbFb0V4BYDFiYWSRZsFo8WshbW +FvoXHRdBF2UXiReuF9IX9xgbGEAYZRiKGK8Y1Rj6GSAZRRlrGZEZtxndGgQaKhpRGncanhrFGuwbFBs7 +G2MbihuyG9ocAhwqHFIcexyjHMwc9R0eHUcdcB2ZHcMd7B4WHkAeah6UHr4e6R8THz4faR+UH78f6iAV +IEEgbCCYIMQg8CEcIUghdSGhIc4h+yInIlUigiKvIt0jCiM4I2YjlCPCI/AkHyRNJHwkqyTaJQklOCVo +JZclxyX3JicmVyaHJrcm6CcYJ0kneierJ9woDSg/KHEooijUKQYpOClrKZ0p0CoCKjUqaCqbKs8rAis2 +K2krnSvRLAUsOSxuLKIs1y0MLUEtdi2rLeEuFi5MLoIuty7uLyQvWi+RL8cv/jA1MGwwpDDbMRIxSjGC +Mbox8jIqMmMymzLUMw0zRjN/M7gz8TQrNGU0njTYNRM1TTWHNcI1/TY3NnI2rjbpNyQ3YDecN9c4FDhQ +OIw4yDkFOUI5fzm8Ofk6Njp0OrI67zstO2s7qjvoPCc8ZTykPOM9Ij1hPaE94D4gPmA+oD7gPyE/YT+i +P+JAI0BkQKZA50EpQWpBrEHuQjBCckK1QvdDOkN9Q8BEA0RHRIpEzkUSRVVFmkXeRiJGZ0arRvBHNUd7 +R8BIBUhLSJFI10kdSWNJqUnwSjdKfUrESwxLU0uaS+JMKkxyTLpNAk1KTZNN3E4lTm5Ot08AT0lPk0/d +UCdQcVC7UQZRUFGbUeZSMVJ8UsdTE1NfU6pT9lRCVI9U21UoVXVVwlYPVlxWqVb3V0RXklfgWC9YfVjL +WRpZaVm4WgdaVlqmWvVbRVuVW+VcNVyGXNZdJ114XcleGl5sXr1fD19hX7NgBWBXYKpg/GFPYaJh9WJJ +Ypxi8GNDY5dj62RAZJRk6WU9ZZJl52Y9ZpJm6Gc9Z5Nn6Wg/aJZo7GlDaZpp8WpIap9q92tPa6dr/2xX +bK9tCG1gbbluEm5rbsRvHm94b9FwK3CGcOBxOnGVcfByS3KmcwFzXXO4dBR0cHTMdSh1hXXhdj52m3b4 +d1Z3s3gReG54zHkqeYl553pGeqV7BHtje8J8IXyBfOF9QX2hfgF+Yn7CfyN/hH/lgEeAqIEKgWuBzYIw +gpKC9INXg7qEHYSAhOOFR4Wrhg6GcobXhzuHn4gEiGmIzokziZmJ/opkisqLMIuWi/yMY4zKjTGNmI3/ +jmaOzo82j56QBpBukNaRP5GokhGSepLjk02TtpQglIqU9JVflcmWNJaflwqXdZfgmEyYuJkkmZCZ/Jpo +mtWbQpuvnByciZz3nWSd0p5Anq6fHZ+Ln/qgaaDYoUehtqImopajBqN2o+akVqTHpTilqaYapoum/adu +p+CoUqjEqTepqaocqo+rAqt1q+msXKzQrUStuK4trqGvFq+LsACwdbDqsWCx1rJLssKzOLOutCW0nLUT +tYq2AbZ5tvC3aLfguFm40blKucK6O7q1uy67p7whvJu9Fb2Pvgq+hL7/v3q/9cBwwOzBZ8Hjwl/C28NY +w9TEUcTOxUvFyMZGxsPHQce/yD3IvMk6ybnKOMq3yzbLtsw1zLXNNc21zjbOts83z7jQOdC60TzRvtI/ +0sHTRNPG1EnUy9VO1dHWVdbY11zX4Nhk2OjZbNnx2nba+9uA3AXcit0Q3ZbeHN6i3ynfr+A24L3hROHM +4lPi2+Nj4+vkc+T85YTmDeaW5x/nqegy6LzpRunQ6lvq5etw6/vshu0R7ZzuKO6070DvzPBY8OXxcvH/ +8ozzGfOn9DT0wvVQ9d72bfb794r4Gfio+Tj5x/pX+uf7d/wH/Jj9Kf26/kv+3P9t///SLS4vMFokY2xh +c3NuYW1lWCRjbGFzc2VzXxAQTlNCaXRtYXBJbWFnZVJlcKMvMTJaTlNJbWFnZVJlcFhOU09iamVjdNIt +LjQ1V05TQXJyYXmiNDLSLS43OF5OU011dGFibGVBcnJheaM3NDLTOjsOPD0+V05TV2hpdGVcTlNDb2xv +clNwYWNlRDAgMAAQA4AM0i0uQEFXTlNDb2xvcqJAMtItLkNEV05TSW1hZ2WiQzIACAARABoAJAApADIA +NwBJAEwAUQBTAGIAaAB3AH4AhQCSAJkApQCtALwAvgDAAMUAxwDJAMsAzQDUANkA5ADmAOgA6gDvAPIA +9AD2APgA/wEWATIBNAE2E6YTqxO2E78T0hPWE+ET6hPvE/cT+hP/FA4UEhQZFCEULhQzFDUUNxQ8FEQU +RxRMFFQAAAAAAAACAQAAAAAAAABFAAAAAAAAAAAAAAAAAAAUVw diff --git a/i18n/src/main/resources/sr.lproj/Command.xib b/i18n/src/main/resources/sr.lproj/Command.xib index ebc0b29d4b..02ae441a44 100644 --- a/i18n/src/main/resources/sr.lproj/Command.xib +++ b/i18n/src/main/resources/sr.lproj/Command.xib @@ -1,7 +1,7 @@ - + - + diff --git a/i18n/src/main/resources/sr.lproj/Connection.xib b/i18n/src/main/resources/sr.lproj/Connection.xib index 34730b5f86..cc6b4b6cf8 100644 --- a/i18n/src/main/resources/sr.lproj/Connection.xib +++ b/i18n/src/main/resources/sr.lproj/Connection.xib @@ -1,7 +1,7 @@ - + - + @@ -76,7 +76,7 @@ - + diff --git a/i18n/src/main/resources/sr.lproj/Info.xib b/i18n/src/main/resources/sr.lproj/Info.xib index c7ae3b6cbc..068815b45d 100644 --- a/i18n/src/main/resources/sr.lproj/Info.xib +++ b/i18n/src/main/resources/sr.lproj/Info.xib @@ -1,7 +1,7 @@ - + - + @@ -89,7 +89,7 @@ - + @@ -1017,21 +1017,21 @@ - + - + - + @@ -1121,21 +1121,21 @@ - + - + - + @@ -1250,27 +1250,27 @@ diff --git a/i18n/src/main/resources/sr.lproj/Login.strings b/i18n/src/main/resources/sr.lproj/Login.strings index 77536d8323..db770b759a 100644 Binary files a/i18n/src/main/resources/sr.lproj/Login.strings and b/i18n/src/main/resources/sr.lproj/Login.strings differ diff --git a/i18n/src/main/resources/sr.lproj/Main.xib b/i18n/src/main/resources/sr.lproj/Main.xib index 874caa8838..14c850441d 100644 --- a/i18n/src/main/resources/sr.lproj/Main.xib +++ b/i18n/src/main/resources/sr.lproj/Main.xib @@ -1,7 +1,7 @@ - + - + @@ -159,19 +159,19 @@ - + - + - + @@ -321,12 +321,12 @@ CA - + - + @@ -566,6 +566,10 @@ CA - + + + + + diff --git a/i18n/src/main/resources/sr.lproj/Preferences.xib b/i18n/src/main/resources/sr.lproj/Preferences.xib index e22a63214b..6618d818bd 100644 --- a/i18n/src/main/resources/sr.lproj/Preferences.xib +++ b/i18n/src/main/resources/sr.lproj/Preferences.xib @@ -1,7 +1,7 @@ - + - + @@ -266,7 +266,7 @@ - + @@ -307,7 +307,7 @@ - + @@ -539,7 +539,7 @@ - + @@ -579,7 +579,7 @@ - + @@ -614,7 +614,7 @@ - + @@ -643,7 +643,7 @@ - + @@ -1219,7 +1219,7 @@ - + @@ -1251,7 +1251,7 @@ - + @@ -1291,7 +1291,7 @@ - + @@ -1364,7 +1364,7 @@ - + @@ -1559,7 +1559,7 @@ - + @@ -1619,7 +1619,7 @@ - + @@ -1644,7 +1644,7 @@ - + @@ -1686,7 +1686,7 @@ - + @@ -1714,7 +1714,7 @@ - + @@ -1742,7 +1742,7 @@ - + @@ -1771,7 +1771,7 @@ - + @@ -1806,7 +1806,7 @@ - + @@ -1889,7 +1889,7 @@ - + @@ -1948,7 +1948,7 @@ - + @@ -1976,7 +1976,7 @@ - + @@ -2005,7 +2005,7 @@ - + diff --git a/i18n/src/main/resources/sr.lproj/Profiles.xib b/i18n/src/main/resources/sr.lproj/Profiles.xib index 18af5a58f4..20e5ce0f36 100644 --- a/i18n/src/main/resources/sr.lproj/Profiles.xib +++ b/i18n/src/main/resources/sr.lproj/Profiles.xib @@ -1,7 +1,7 @@ - + - + diff --git a/i18n/src/main/resources/sr.lproj/Prompt.xib b/i18n/src/main/resources/sr.lproj/Prompt.xib index 65c26222b9..2a734459a7 100644 --- a/i18n/src/main/resources/sr.lproj/Prompt.xib +++ b/i18n/src/main/resources/sr.lproj/Prompt.xib @@ -1,7 +1,7 @@ - + - + @@ -95,7 +95,7 @@ Gw - + diff --git a/i18n/src/main/resources/sr.lproj/S3.strings b/i18n/src/main/resources/sr.lproj/S3.strings index 54f73dd8da..b66ee3ce1a 100644 Binary files a/i18n/src/main/resources/sr.lproj/S3.strings and b/i18n/src/main/resources/sr.lproj/S3.strings differ diff --git a/i18n/src/main/resources/sr.lproj/Updater.strings b/i18n/src/main/resources/sr.lproj/Updater.strings index 9aa5cd543f..7ffcb399f7 100644 Binary files a/i18n/src/main/resources/sr.lproj/Updater.strings and b/i18n/src/main/resources/sr.lproj/Updater.strings differ diff --git a/i18n/src/main/resources/sv.lproj/Bookmark.xib b/i18n/src/main/resources/sv.lproj/Bookmark.xib index 05f0a3ad70..3b17601856 100644 --- a/i18n/src/main/resources/sv.lproj/Bookmark.xib +++ b/i18n/src/main/resources/sv.lproj/Bookmark.xib @@ -1,7 +1,7 @@ - + - + @@ -92,7 +92,7 @@ - + @@ -489,95 +489,95 @@ YnBsaXN0MDDUAQIDBAUGBwpYJHZlcnNpb25ZJGFyY2hpdmVyVCR0b3BYJG9iamVjdHMSAAGGoF8QD05T -S2V5ZWRBcmNoaXZlctEICVRyb290gAGuCwwZGh8UJCkqMTQ3PUBVJG51bGzWDQ4PEBESExQVFhcYVk5T -U2l6ZV5OU1Jlc2l6aW5nTW9kZVYkY2xhc3NcTlNJbWFnZUZsYWdzVk5TUmVwc1dOU0NvbG9ygAIQAIAN -EiDDAACAA4ALVnsxLCAxfdIbDxweWk5TLm9iamVjdHOhHYAEgArSGw8gI6IhIoAFgAaACdMPJSYnKBRf -EBROU1RJRkZSZXByZXNlbnRhdGlvbl8QGU5TSW50ZXJuYWxMYXlvdXREaXJlY3Rpb26ACIAHTxESbE1N -ACoAAAAKAAAAEAEAAAMAAAABAAEAAAEBAAMAAAABAAEAAAECAAMAAAACAAgACAEDAAMAAAABAAEAAAEG -AAMAAAABAAEAAAEKAAMAAAABAAEAAAERAAQAAAABAAAACAESAAMAAAABAAEAAAEVAAMAAAABAAIAAAEW -AAMAAAABAAEAAAEXAAQAAAABAAAAAgEcAAMAAAABAAEAAAEoAAMAAAABAAIAAAFSAAMAAAABAAEAAAFT -AAMAAAACAAEAAYdzAAcAABGcAAAA0AAAAAAAABGcYXBwbAIAAABtbnRyR1JBWVhZWiAH3AAIABcADwAu -AA9hY3NwQVBQTAAAAABub25lAAAAAAAAAAAAAAAAAAAAAAAA9tYAAQAAAADTLWFwcGwAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAVkZXNjAAAAwAAAAHlkc2NtAAABPAAA -CBpjcHJ0AAAJWAAAACN3dHB0AAAJfAAAABRrVFJDAAAJkAAACAxkZXNjAAAAAAAAAB9HZW5lcmljIEdy -YXkgR2FtbWEgMi4yIFByb2ZpbGUAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAbWx1YwAAAAAAAAAfAAAADHNr -U0sAAAAuAAABhGRhREsAAAA6AAABsmNhRVMAAAA4AAAB7HZpVk4AAABAAAACJHB0QlIAAABKAAACZHVr -VUEAAAAsAAACrmZyRlUAAAA+AAAC2mh1SFUAAAA0AAADGHpoVFcAAAAaAAADTGtvS1IAAAAiAAADZm5i -Tk8AAAA6AAADiGNzQ1oAAAAoAAADwmhlSUwAAAAkAAAD6nJvUk8AAAAqAAAEDmRlREUAAABOAAAEOGl0 -SVQAAABOAAAEhnN2U0UAAAA4AAAE1HpoQ04AAAAaAAAFDGphSlAAAAAmAAAFJmVsR1IAAAAqAAAFTHB0 -UE8AAABSAAAFdm5sTkwAAABAAAAFyGVzRVMAAABMAAAGCHRoVEgAAAAyAAAGVHRyVFIAAAAkAAAGhmZp -RkkAAABGAAAGqmhySFIAAAA+AAAG8HBsUEwAAABKAAAHLmFyRUcAAAAsAAAHeHJ1UlUAAAA6AAAHpGVu -VVMAAAA8AAAH3gBWAWEAZQBvAGIAZQBjAG4A4QAgAHMAaQB2AOEAIABnAGEAbQBhACAAMgAsADIARwBl -AG4AZQByAGkAcwBrACAAZwByAOUAIAAyACwAMgAgAGcAYQBtAG0AYQAtAHAAcgBvAGYAaQBsAEcAYQBt -AG0AYQAgAGQAZQAgAGcAcgBpAHMAbwBzACAAZwBlAG4A6AByAGkAYwBhACAAMgAuADIAQx6lAHUAIABo -AOwAbgBoACAATQDgAHUAIAB4AOEAbQAgAEMAaAB1AG4AZwAgAEcAYQBtAG0AYQAgADIALgAyAFAAZQBy -AGYAaQBsACAARwBlAG4A6QByAGkAYwBvACAAZABhACAARwBhAG0AYQAgAGQAZQAgAEMAaQBuAHoAYQBz -ACAAMgAsADIEFwQwBDMEMAQ7BEwEPQQwACAARwByAGEAeQAtBDMEMAQ8BDAAIAAyAC4AMgBQAHIAbwBm -AGkAbAAgAGcA6QBuAOkAcgBpAHEAdQBlACAAZwByAGkAcwAgAGcAYQBtAG0AYQAgADIALAAyAMEAbAB0 -AGEAbADhAG4AbwBzACAAcwB6APwAcgBrAGUAIABnAGEAbQBtAGEAIAAyAC4AMpAadShwcJaOUUlepgAy -AC4AMoJyX2ljz4/wx3y8GAAg1ozAyQAgrBC5yAAgADIALgAyACDVBLhc0wzHfABHAGUAbgBlAHIAaQBz -AGsAIABnAHIA5QAgAGcAYQBtAG0AYQAgADIALAAyAC0AcAByAG8AZgBpAGwATwBiAGUAYwBuAOEAIAFh -AGUAZADhACAAZwBhAG0AYQAgADIALgAyBdIF0AXeBdQAIAXQBeQF1QXoACAF2wXcBdwF2QAgADIALgAy -AEcAYQBtAGEAIABnAHIAaQAgAGcAZQBuAGUAcgBpAGMBAwAgADIALAAyAEEAbABsAGcAZQBtAGUAaQBu -AGUAcwAgAEcAcgBhAHUAcwB0AHUAZgBlAG4ALQBQAHIAbwBmAGkAbAAgAEcAYQBtAG0AYQAgADIALAAy -AFAAcgBvAGYAaQBsAG8AIABnAHIAaQBnAGkAbwAgAGcAZQBuAGUAcgBpAGMAbwAgAGQAZQBsAGwAYQAg -AGcAYQBtAG0AYQAgADIALAAyAEcAZQBuAGUAcgBpAHMAawAgAGcAcgDlACAAMgAsADIAIABnAGEAbQBt -AGEAcAByAG8AZgBpAGxmbpAacHBepnz7ZXAAMgAuADJjz4/wZYdO9k4AgiwwsDDsMKQwrDDzMN4AIAAy -AC4AMgAgMNcw7TDVMKEwpDDrA5MDtQO9A7kDugPMACADkwO6A8EDuQAgA5MDrAO8A7wDsQAgADIALgAy -AFAAZQByAGYAaQBsACAAZwBlAG4A6QByAGkAYwBvACAAZABlACAAYwBpAG4AegBlAG4AdABvAHMAIABk -AGEAIABHAGEAbQBtAGEAIAAyACwAMgBBAGwAZwBlAG0AZQBlAG4AIABnAHIAaQBqAHMAIABnAGEAbQBt -AGEAIAAyACwAMgAtAHAAcgBvAGYAaQBlAGwAUABlAHIAZgBpAGwAIABnAGUAbgDpAHIAaQBjAG8AIABk -AGUAIABnAGEAbQBtAGEAIABkAGUAIABnAHIAaQBzAGUAcwAgADIALAAyDiMOMQ4HDioONQ5BDgEOIQ4h -DjIOQA4BDiMOIg5MDhcOMQ5IDicORA4bACAAMgAuADIARwBlAG4AZQBsACAARwByAGkAIABHAGEAbQBh -ACAAMgAsADIAWQBsAGUAaQBuAGUAbgAgAGgAYQByAG0AYQBhAG4AIABnAGEAbQBtAGEAIAAyACwAMgAg -AC0AcAByAG8AZgBpAGkAbABpAEcAZQBuAGUAcgBpAQ0AawBpACAARwByAGEAeQAgAEcAYQBtAG0AYQAg -ADIALgAyACAAcAByAG8AZgBpAGwAVQBuAGkAdwBlAHIAcwBhAGwAbgB5ACAAcAByAG8AZgBpAGwAIABz -AHoAYQByAG8BWwBjAGkAIABnAGEAbQBtAGEAIAAyACwAMgY6BicGRQYnACAAMgAuADIAIAZEBkgGRgAg -BjEGRQYnBi8GSgAgBjkGJwZFBB4EMQRJBDAETwAgBEEENQRABDAETwAgBDMEMAQ8BDwEMAAgADIALAAy -AC0EPwRABD4ERAQ4BDsETABHAGUAbgBlAHIAaQBjACAARwByAGEAeQAgAEcAYQBtAG0AYQAgADIALgAy -ACAAUAByAG8AZgBpAGwAZQAAdGV4dAAAAABDb3B5cmlnaHQgQXBwbGUgSW5jLiwgMjAxMgAAWFlaIAAA -AAAAAPNRAAEAAAABFsxjdXJ2AAAAAAAABAAAAAAFAAoADwAUABkAHgAjACgALQAyADcAOwBAAEUASgBP -AFQAWQBeAGMAaABtAHIAdwB8AIEAhgCLAJAAlQCaAJ8ApACpAK4AsgC3ALwAwQDGAMsA0ADVANsA4ADl -AOsA8AD2APsBAQEHAQ0BEwEZAR8BJQErATIBOAE+AUUBTAFSAVkBYAFnAW4BdQF8AYMBiwGSAZoBoQGp -AbEBuQHBAckB0QHZAeEB6QHyAfoCAwIMAhQCHQImAi8COAJBAksCVAJdAmcCcQJ6AoQCjgKYAqICrAK2 -AsECywLVAuAC6wL1AwADCwMWAyEDLQM4A0MDTwNaA2YDcgN+A4oDlgOiA64DugPHA9MD4APsA/kEBgQT -BCAELQQ7BEgEVQRjBHEEfgSMBJoEqAS2BMQE0wThBPAE/gUNBRwFKwU6BUkFWAVnBXcFhgWWBaYFtQXF -BdUF5QX2BgYGFgYnBjcGSAZZBmoGewaMBp0GrwbABtEG4wb1BwcHGQcrBz0HTwdhB3QHhgeZB6wHvwfS -B+UH+AgLCB8IMghGCFoIbgiCCJYIqgi+CNII5wj7CRAJJQk6CU8JZAl5CY8JpAm6Cc8J5Qn7ChEKJwo9 -ClQKagqBCpgKrgrFCtwK8wsLCyILOQtRC2kLgAuYC7ALyAvhC/kMEgwqDEMMXAx1DI4MpwzADNkM8w0N -DSYNQA1aDXQNjg2pDcMN3g34DhMOLg5JDmQOfw6bDrYO0g7uDwkPJQ9BD14Peg+WD7MPzw/sEAkQJhBD -EGEQfhCbELkQ1xD1ERMRMRFPEW0RjBGqEckR6BIHEiYSRRJkEoQSoxLDEuMTAxMjE0MTYxODE6QTxRPl -FAYUJxRJFGoUixStFM4U8BUSFTQVVhV4FZsVvRXgFgMWJhZJFmwWjxayFtYW+hcdF0EXZReJF64X0hf3 -GBsYQBhlGIoYrxjVGPoZIBlFGWsZkRm3Gd0aBBoqGlEadxqeGsUa7BsUGzsbYxuKG7Ib2hwCHCocUhx7 -HKMczBz1HR4dRx1wHZkdwx3sHhYeQB5qHpQevh7pHxMfPh9pH5Qfvx/qIBUgQSBsIJggxCDwIRwhSCF1 -IaEhziH7IiciVSKCIq8i3SMKIzgjZiOUI8Ij8CQfJE0kfCSrJNolCSU4JWgllyXHJfcmJyZXJocmtybo -JxgnSSd6J6sn3CgNKD8ocSiiKNQpBik4KWspnSnQKgIqNSpoKpsqzysCKzYraSudK9EsBSw5LG4soizX -LQwtQS12Last4S4WLkwugi63Lu4vJC9aL5Evxy/+MDUwbDCkMNsxEjFKMYIxujHyMioyYzKbMtQzDTNG -M38zuDPxNCs0ZTSeNNg1EzVNNYc1wjX9Njc2cjauNuk3JDdgN5w31zgUOFA4jDjIOQU5Qjl/Obw5+To2 -OnQ6sjrvOy07azuqO+g8JzxlPKQ84z0iPWE9oT3gPiA+YD6gPuA/IT9hP6I/4kAjQGRApkDnQSlBakGs -Qe5CMEJyQrVC90M6Q31DwEQDREdEikTORRJFVUWaRd5GIkZnRqtG8Ec1R3tHwEgFSEtIkUjXSR1JY0mp -SfBKN0p9SsRLDEtTS5pL4kwqTHJMuk0CTUpNk03cTiVObk63TwBPSU+TT91QJ1BxULtRBlFQUZtR5lIx -UnxSx1MTU19TqlP2VEJUj1TbVShVdVXCVg9WXFapVvdXRFeSV+BYL1h9WMtZGllpWbhaB1pWWqZa9VtF -W5Vb5Vw1XIZc1l0nXXhdyV4aXmxevV8PX2Ffs2AFYFdgqmD8YU9homH1YklinGLwY0Njl2PrZEBklGTp -ZT1lkmXnZj1mkmboZz1nk2fpaD9olmjsaUNpmmnxakhqn2r3a09rp2v/bFdsr20IbWBtuW4SbmtuxG8e -b3hv0XArcIZw4HE6cZVx8HJLcqZzAXNdc7h0FHRwdMx1KHWFdeF2Pnabdvh3VnezeBF4bnjMeSp5iXnn -ekZ6pXsEe2N7wnwhfIF84X1BfaF+AX5ifsJ/I3+Ef+WAR4CogQqBa4HNgjCCkoL0g1eDuoQdhICE44VH -hauGDoZyhteHO4efiASIaYjOiTOJmYn+imSKyoswi5aL/IxjjMqNMY2Yjf+OZo7OjzaPnpAGkG6Q1pE/ -kaiSEZJ6kuOTTZO2lCCUipT0lV+VyZY0lp+XCpd1l+CYTJi4mSSZkJn8mmia1ZtCm6+cHJyJnPedZJ3S -nkCerp8dn4uf+qBpoNihR6G2oiailqMGo3aj5qRWpMelOKWpphqmi6b9p26n4KhSqMSpN6mpqhyqj6sC -q3Wr6axcrNCtRK24ri2uoa8Wr4uwALB1sOqxYLHWskuywrM4s660JbSctRO1irYBtnm28Ldot+C4WbjR -uUq5wro7urW7LrunvCG8m70VvY++Cr6Evv+/er/1wHDA7MFnwePCX8Lbw1jD1MRRxM7FS8XIxkbGw8dB -x7/IPci8yTrJuco4yrfLNsu2zDXMtc01zbXONs62zzfPuNA50LrRPNG+0j/SwdNE08bUSdTL1U7V0dZV -1tjXXNfg2GTY6Nls2fHadtr724DcBdyK3RDdlt4c3qLfKd+v4DbgveFE4cziU+Lb42Pj6+Rz5PzlhOYN -5pbnH+ep6DLovOlG6dDqW+rl63Dr++yG7RHtnO4o7rTvQO/M8Fjw5fFy8f/yjPMZ86f0NPTC9VD13vZt -9vv3ivgZ+Kj5OPnH+lf65/t3/Af8mP0p/br+S/7c/23//9IrLC0uWiRjbGFzc25hbWVYJGNsYXNzZXNf -EBBOU0JpdG1hcEltYWdlUmVwoy0vMFpOU0ltYWdlUmVwWE5TT2JqZWN00issMjNXTlNBcnJheaIyMNIr -LDU2Xk5TTXV0YWJsZUFycmF5ozUyMNM4OQ86OzxXTlNXaGl0ZVxOU0NvbG9yU3BhY2VEMCAwABADgAzS -Kyw+P1dOU0NvbG9yoj4w0issQUJXTlNJbWFnZaJBMAAIABEAGgAkACkAMgA3AEkATABRAFMAYgBoAHUA -fACLAJIAnwCmAK4AsACyALQAuQC7AL0AxADJANQA1gDYANoA3wDiAOQA5gDoAO8BBgEiASQBJhOWE5sT -phOvE8ITxhPRE9oT3xPnE+oT7xP+FAIUCRQRFB4UIxQlFCcULBQ0FDcUPBREAAAAAAAAAgEAAAAAAAAA -QwAAAAAAAAAAAAAAAAAAFEc +S2V5ZWRBcmNoaXZlctEICVRyb290gAGuCwwbHCEaJissMzY5P0JVJG51bGzXDQ4PEBESExQVFhcYGRpW +TlNTaXplViRjbGFzc1xOU0ltYWdlRmxhZ3NWTlNSZXBzW05TVGludENvbG9yV05TQ29sb3JeTlNSZXNp +emluZ01vZGWAAoANEiDDAACAA4AAgAsQAFZ7MSwgMX3SHQ4eIFpOUy5vYmplY3RzoR+ABIAK0h0OIiWi +IySABYAGgAnTDicoKSoaXxAUTlNUSUZGUmVwcmVzZW50YXRpb25fEBlOU0ludGVybmFsTGF5b3V0RGly +ZWN0aW9ugAiAB08REmxNTQAqAAAACgAAABABAAADAAAAAQABAAABAQADAAAAAQABAAABAgADAAAAAgAI +AAgBAwADAAAAAQABAAABBgADAAAAAQABAAABCgADAAAAAQABAAABEQAEAAAAAQAAAAgBEgADAAAAAQAB +AAABFQADAAAAAQACAAABFgADAAAAAQABAAABFwAEAAAAAQAAAAIBHAADAAAAAQABAAABKAADAAAAAQAC +AAABUgADAAAAAQABAAABUwADAAAAAgABAAGHcwAHAAARnAAAANAAAAAAAAARnGFwcGwCAAAAbW50ckdS +QVlYWVogB9wACAAXAA8ALgAPYWNzcEFQUEwAAAAAbm9uZQAAAAAAAAAAAAAAAAAAAAAAAPbWAAEAAAAA +0y1hcHBsAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFZGVzYwAA +AMAAAAB5ZHNjbQAAATwAAAgaY3BydAAACVgAAAAjd3RwdAAACXwAAAAUa1RSQwAACZAAAAgMZGVzYwAA +AAAAAAAfR2VuZXJpYyBHcmF5IEdhbW1hIDIuMiBQcm9maWxlAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAG1s +dWMAAAAAAAAAHwAAAAxza1NLAAAALgAAAYRkYURLAAAAOgAAAbJjYUVTAAAAOAAAAex2aVZOAAAAQAAA +AiRwdEJSAAAASgAAAmR1a1VBAAAALAAAAq5mckZVAAAAPgAAAtpodUhVAAAANAAAAxh6aFRXAAAAGgAA +A0xrb0tSAAAAIgAAA2ZuYk5PAAAAOgAAA4hjc0NaAAAAKAAAA8JoZUlMAAAAJAAAA+pyb1JPAAAAKgAA +BA5kZURFAAAATgAABDhpdElUAAAATgAABIZzdlNFAAAAOAAABNR6aENOAAAAGgAABQxqYUpQAAAAJgAA +BSZlbEdSAAAAKgAABUxwdFBPAAAAUgAABXZubE5MAAAAQAAABchlc0VTAAAATAAABgh0aFRIAAAAMgAA +BlR0clRSAAAAJAAABoZmaUZJAAAARgAABqpockhSAAAAPgAABvBwbFBMAAAASgAABy5hckVHAAAALAAA +B3hydVJVAAAAOgAAB6RlblVTAAAAPAAAB94AVgFhAGUAbwBiAGUAYwBuAOEAIABzAGkAdgDhACAAZwBh +AG0AYQAgADIALAAyAEcAZQBuAGUAcgBpAHMAawAgAGcAcgDlACAAMgAsADIAIABnAGEAbQBtAGEALQBw +AHIAbwBmAGkAbABHAGEAbQBtAGEAIABkAGUAIABnAHIAaQBzAG8AcwAgAGcAZQBuAOgAcgBpAGMAYQAg +ADIALgAyAEMepQB1ACAAaADsAG4AaAAgAE0A4AB1ACAAeADhAG0AIABDAGgAdQBuAGcAIABHAGEAbQBt +AGEAIAAyAC4AMgBQAGUAcgBmAGkAbAAgAEcAZQBuAOkAcgBpAGMAbwAgAGQAYQAgAEcAYQBtAGEAIABk +AGUAIABDAGkAbgB6AGEAcwAgADIALAAyBBcEMAQzBDAEOwRMBD0EMAAgAEcAcgBhAHkALQQzBDAEPAQw +ACAAMgAuADIAUAByAG8AZgBpAGwAIABnAOkAbgDpAHIAaQBxAHUAZQAgAGcAcgBpAHMAIABnAGEAbQBt +AGEAIAAyACwAMgDBAGwAdABhAGwA4QBuAG8AcwAgAHMAegD8AHIAawBlACAAZwBhAG0AbQBhACAAMgAu +ADKQGnUocHCWjlFJXqYAMgAuADKCcl9pY8+P8Md8vBgAINaMwMkAIKwQucgAIAAyAC4AMgAg1QS4XNMM +x3wARwBlAG4AZQByAGkAcwBrACAAZwByAOUAIABnAGEAbQBtAGEAIAAyACwAMgAtAHAAcgBvAGYAaQBs +AE8AYgBlAGMAbgDhACABYQBlAGQA4QAgAGcAYQBtAGEAIAAyAC4AMgXSBdAF3gXUACAF0AXkBdUF6AAg +BdsF3AXcBdkAIAAyAC4AMgBHAGEAbQBhACAAZwByAGkAIABnAGUAbgBlAHIAaQBjAQMAIAAyACwAMgBB +AGwAbABnAGUAbQBlAGkAbgBlAHMAIABHAHIAYQB1AHMAdAB1AGYAZQBuAC0AUAByAG8AZgBpAGwAIABH +AGEAbQBtAGEAIAAyACwAMgBQAHIAbwBmAGkAbABvACAAZwByAGkAZwBpAG8AIABnAGUAbgBlAHIAaQBj +AG8AIABkAGUAbABsAGEAIABnAGEAbQBtAGEAIAAyACwAMgBHAGUAbgBlAHIAaQBzAGsAIABnAHIA5QAg +ADIALAAyACAAZwBhAG0AbQBhAHAAcgBvAGYAaQBsZm6QGnBwXqZ8+2VwADIALgAyY8+P8GWHTvZOAIIs +MLAw7DCkMKww8zDeACAAMgAuADIAIDDXMO0w1TChMKQw6wOTA7UDvQO5A7oDzAAgA5MDugPBA7kAIAOT +A6wDvAO8A7EAIAAyAC4AMgBQAGUAcgBmAGkAbAAgAGcAZQBuAOkAcgBpAGMAbwAgAGQAZQAgAGMAaQBu +AHoAZQBuAHQAbwBzACAAZABhACAARwBhAG0AbQBhACAAMgAsADIAQQBsAGcAZQBtAGUAZQBuACAAZwBy +AGkAagBzACAAZwBhAG0AbQBhACAAMgAsADIALQBwAHIAbwBmAGkAZQBsAFAAZQByAGYAaQBsACAAZwBl +AG4A6QByAGkAYwBvACAAZABlACAAZwBhAG0AbQBhACAAZABlACAAZwByAGkAcwBlAHMAIAAyACwAMg4j +DjEOBw4qDjUOQQ4BDiEOIQ4yDkAOAQ4jDiIOTA4XDjEOSA4nDkQOGwAgADIALgAyAEcAZQBuAGUAbAAg +AEcAcgBpACAARwBhAG0AYQAgADIALAAyAFkAbABlAGkAbgBlAG4AIABoAGEAcgBtAGEAYQBuACAAZwBh +AG0AbQBhACAAMgAsADIAIAAtAHAAcgBvAGYAaQBpAGwAaQBHAGUAbgBlAHIAaQENAGsAaQAgAEcAcgBh +AHkAIABHAGEAbQBtAGEAIAAyAC4AMgAgAHAAcgBvAGYAaQBsAFUAbgBpAHcAZQByAHMAYQBsAG4AeQAg +AHAAcgBvAGYAaQBsACAAcwB6AGEAcgBvAVsAYwBpACAAZwBhAG0AbQBhACAAMgAsADIGOgYnBkUGJwAg +ADIALgAyACAGRAZIBkYAIAYxBkUGJwYvBkoAIAY5BicGRQQeBDEESQQwBE8AIARBBDUEQAQwBE8AIAQz +BDAEPAQ8BDAAIAAyACwAMgAtBD8EQAQ+BEQEOAQ7BEwARwBlAG4AZQByAGkAYwAgAEcAcgBhAHkAIABH +AGEAbQBtAGEAIAAyAC4AMgAgAFAAcgBvAGYAaQBsAGUAAHRleHQAAAAAQ29weXJpZ2h0IEFwcGxlIElu +Yy4sIDIwMTIAAFhZWiAAAAAAAADzUQABAAAAARbMY3VydgAAAAAAAAQAAAAABQAKAA8AFAAZAB4AIwAo +AC0AMgA3ADsAQABFAEoATwBUAFkAXgBjAGgAbQByAHcAfACBAIYAiwCQAJUAmgCfAKQAqQCuALIAtwC8 +AMEAxgDLANAA1QDbAOAA5QDrAPAA9gD7AQEBBwENARMBGQEfASUBKwEyATgBPgFFAUwBUgFZAWABZwFu +AXUBfAGDAYsBkgGaAaEBqQGxAbkBwQHJAdEB2QHhAekB8gH6AgMCDAIUAh0CJgIvAjgCQQJLAlQCXQJn +AnECegKEAo4CmAKiAqwCtgLBAssC1QLgAusC9QMAAwsDFgMhAy0DOANDA08DWgNmA3IDfgOKA5YDogOu +A7oDxwPTA+AD7AP5BAYEEwQgBC0EOwRIBFUEYwRxBH4EjASaBKgEtgTEBNME4QTwBP4FDQUcBSsFOgVJ +BVgFZwV3BYYFlgWmBbUFxQXVBeUF9gYGBhYGJwY3BkgGWQZqBnsGjAadBq8GwAbRBuMG9QcHBxkHKwc9 +B08HYQd0B4YHmQesB78H0gflB/gICwgfCDIIRghaCG4IggiWCKoIvgjSCOcI+wkQCSUJOglPCWQJeQmP +CaQJugnPCeUJ+woRCicKPQpUCmoKgQqYCq4KxQrcCvMLCwsiCzkLUQtpC4ALmAuwC8gL4Qv5DBIMKgxD +DFwMdQyODKcMwAzZDPMNDQ0mDUANWg10DY4NqQ3DDd4N+A4TDi4OSQ5kDn8Omw62DtIO7g8JDyUPQQ9e +D3oPlg+zD88P7BAJECYQQxBhEH4QmxC5ENcQ9RETETERTxFtEYwRqhHJEegSBxImEkUSZBKEEqMSwxLj +EwMTIxNDE2MTgxOkE8UT5RQGFCcUSRRqFIsUrRTOFPAVEhU0FVYVeBWbFb0V4BYDFiYWSRZsFo8WshbW +FvoXHRdBF2UXiReuF9IX9xgbGEAYZRiKGK8Y1Rj6GSAZRRlrGZEZtxndGgQaKhpRGncanhrFGuwbFBs7 +G2MbihuyG9ocAhwqHFIcexyjHMwc9R0eHUcdcB2ZHcMd7B4WHkAeah6UHr4e6R8THz4faR+UH78f6iAV +IEEgbCCYIMQg8CEcIUghdSGhIc4h+yInIlUigiKvIt0jCiM4I2YjlCPCI/AkHyRNJHwkqyTaJQklOCVo +JZclxyX3JicmVyaHJrcm6CcYJ0kneierJ9woDSg/KHEooijUKQYpOClrKZ0p0CoCKjUqaCqbKs8rAis2 +K2krnSvRLAUsOSxuLKIs1y0MLUEtdi2rLeEuFi5MLoIuty7uLyQvWi+RL8cv/jA1MGwwpDDbMRIxSjGC +Mbox8jIqMmMymzLUMw0zRjN/M7gz8TQrNGU0njTYNRM1TTWHNcI1/TY3NnI2rjbpNyQ3YDecN9c4FDhQ +OIw4yDkFOUI5fzm8Ofk6Njp0OrI67zstO2s7qjvoPCc8ZTykPOM9Ij1hPaE94D4gPmA+oD7gPyE/YT+i +P+JAI0BkQKZA50EpQWpBrEHuQjBCckK1QvdDOkN9Q8BEA0RHRIpEzkUSRVVFmkXeRiJGZ0arRvBHNUd7 +R8BIBUhLSJFI10kdSWNJqUnwSjdKfUrESwxLU0uaS+JMKkxyTLpNAk1KTZNN3E4lTm5Ot08AT0lPk0/d +UCdQcVC7UQZRUFGbUeZSMVJ8UsdTE1NfU6pT9lRCVI9U21UoVXVVwlYPVlxWqVb3V0RXklfgWC9YfVjL +WRpZaVm4WgdaVlqmWvVbRVuVW+VcNVyGXNZdJ114XcleGl5sXr1fD19hX7NgBWBXYKpg/GFPYaJh9WJJ +Ypxi8GNDY5dj62RAZJRk6WU9ZZJl52Y9ZpJm6Gc9Z5Nn6Wg/aJZo7GlDaZpp8WpIap9q92tPa6dr/2xX +bK9tCG1gbbluEm5rbsRvHm94b9FwK3CGcOBxOnGVcfByS3KmcwFzXXO4dBR0cHTMdSh1hXXhdj52m3b4 +d1Z3s3gReG54zHkqeYl553pGeqV7BHtje8J8IXyBfOF9QX2hfgF+Yn7CfyN/hH/lgEeAqIEKgWuBzYIw +gpKC9INXg7qEHYSAhOOFR4Wrhg6GcobXhzuHn4gEiGmIzokziZmJ/opkisqLMIuWi/yMY4zKjTGNmI3/ +jmaOzo82j56QBpBukNaRP5GokhGSepLjk02TtpQglIqU9JVflcmWNJaflwqXdZfgmEyYuJkkmZCZ/Jpo +mtWbQpuvnByciZz3nWSd0p5Anq6fHZ+Ln/qgaaDYoUehtqImopajBqN2o+akVqTHpTilqaYapoum/adu +p+CoUqjEqTepqaocqo+rAqt1q+msXKzQrUStuK4trqGvFq+LsACwdbDqsWCx1rJLssKzOLOutCW0nLUT +tYq2AbZ5tvC3aLfguFm40blKucK6O7q1uy67p7whvJu9Fb2Pvgq+hL7/v3q/9cBwwOzBZ8Hjwl/C28NY +w9TEUcTOxUvFyMZGxsPHQce/yD3IvMk6ybnKOMq3yzbLtsw1zLXNNc21zjbOts83z7jQOdC60TzRvtI/ +0sHTRNPG1EnUy9VO1dHWVdbY11zX4Nhk2OjZbNnx2nba+9uA3AXcit0Q3ZbeHN6i3ynfr+A24L3hROHM +4lPi2+Nj4+vkc+T85YTmDeaW5x/nqegy6LzpRunQ6lvq5etw6/vshu0R7ZzuKO6070DvzPBY8OXxcvH/ +8ozzGfOn9DT0wvVQ9d72bfb794r4Gfio+Tj5x/pX+uf7d/wH/Jj9Kf26/kv+3P9t///SLS4vMFokY2xh +c3NuYW1lWCRjbGFzc2VzXxAQTlNCaXRtYXBJbWFnZVJlcKMvMTJaTlNJbWFnZVJlcFhOU09iamVjdNIt +LjQ1V05TQXJyYXmiNDLSLS43OF5OU011dGFibGVBcnJheaM3NDLTOjsOPD0+V05TV2hpdGVcTlNDb2xv +clNwYWNlRDAgMAAQA4AM0i0uQEFXTlNDb2xvcqJAMtItLkNEV05TSW1hZ2WiQzIACAARABoAJAApADIA +NwBJAEwAUQBTAGIAaAB3AH4AhQCSAJkApQCtALwAvgDAAMUAxwDJAMsAzQDUANkA5ADmAOgA6gDvAPIA +9AD2APgA/wEWATIBNAE2E6YTqxO2E78T0hPWE+ET6hPvE/cT+hP/FA4UEhQZFCEULhQzFDUUNxQ8FEQU +RxRMFFQAAAAAAAACAQAAAAAAAABFAAAAAAAAAAAAAAAAAAAUVw diff --git a/i18n/src/main/resources/sv.lproj/Browser.xib b/i18n/src/main/resources/sv.lproj/Browser.xib index 5c8235e049..5c5e75b733 100644 --- a/i18n/src/main/resources/sv.lproj/Browser.xib +++ b/i18n/src/main/resources/sv.lproj/Browser.xib @@ -1,7 +1,7 @@ - + - + @@ -38,7 +38,7 @@ - + @@ -62,27 +62,27 @@ @@ -256,7 +256,7 @@ - + @@ -553,95 +553,95 @@ DQ YnBsaXN0MDDUAQIDBAUGBwpYJHZlcnNpb25ZJGFyY2hpdmVyVCR0b3BYJG9iamVjdHMSAAGGoF8QD05T -S2V5ZWRBcmNoaXZlctEICVRyb290gAGuCwwZGh8UJCkqMTQ3PUBVJG51bGzWDQ4PEBESExQVFhcYVk5T -U2l6ZV5OU1Jlc2l6aW5nTW9kZVYkY2xhc3NcTlNJbWFnZUZsYWdzVk5TUmVwc1dOU0NvbG9ygAIQAIAN -EiDDAACAA4ALVnsxLCAxfdIbDxweWk5TLm9iamVjdHOhHYAEgArSGw8gI6IhIoAFgAaACdMPJSYnKBRf -EBROU1RJRkZSZXByZXNlbnRhdGlvbl8QGU5TSW50ZXJuYWxMYXlvdXREaXJlY3Rpb26ACIAHTxESbE1N -ACoAAAAKAAAAEAEAAAMAAAABAAEAAAEBAAMAAAABAAEAAAECAAMAAAACAAgACAEDAAMAAAABAAEAAAEG -AAMAAAABAAEAAAEKAAMAAAABAAEAAAERAAQAAAABAAAACAESAAMAAAABAAEAAAEVAAMAAAABAAIAAAEW -AAMAAAABAAEAAAEXAAQAAAABAAAAAgEcAAMAAAABAAEAAAEoAAMAAAABAAIAAAFSAAMAAAABAAEAAAFT -AAMAAAACAAEAAYdzAAcAABGcAAAA0AAAAAAAABGcYXBwbAIAAABtbnRyR1JBWVhZWiAH3AAIABcADwAu -AA9hY3NwQVBQTAAAAABub25lAAAAAAAAAAAAAAAAAAAAAAAA9tYAAQAAAADTLWFwcGwAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAVkZXNjAAAAwAAAAHlkc2NtAAABPAAA -CBpjcHJ0AAAJWAAAACN3dHB0AAAJfAAAABRrVFJDAAAJkAAACAxkZXNjAAAAAAAAAB9HZW5lcmljIEdy -YXkgR2FtbWEgMi4yIFByb2ZpbGUAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAbWx1YwAAAAAAAAAfAAAADHNr -U0sAAAAuAAABhGRhREsAAAA6AAABsmNhRVMAAAA4AAAB7HZpVk4AAABAAAACJHB0QlIAAABKAAACZHVr -VUEAAAAsAAACrmZyRlUAAAA+AAAC2mh1SFUAAAA0AAADGHpoVFcAAAAaAAADTGtvS1IAAAAiAAADZm5i -Tk8AAAA6AAADiGNzQ1oAAAAoAAADwmhlSUwAAAAkAAAD6nJvUk8AAAAqAAAEDmRlREUAAABOAAAEOGl0 -SVQAAABOAAAEhnN2U0UAAAA4AAAE1HpoQ04AAAAaAAAFDGphSlAAAAAmAAAFJmVsR1IAAAAqAAAFTHB0 -UE8AAABSAAAFdm5sTkwAAABAAAAFyGVzRVMAAABMAAAGCHRoVEgAAAAyAAAGVHRyVFIAAAAkAAAGhmZp -RkkAAABGAAAGqmhySFIAAAA+AAAG8HBsUEwAAABKAAAHLmFyRUcAAAAsAAAHeHJ1UlUAAAA6AAAHpGVu -VVMAAAA8AAAH3gBWAWEAZQBvAGIAZQBjAG4A4QAgAHMAaQB2AOEAIABnAGEAbQBhACAAMgAsADIARwBl -AG4AZQByAGkAcwBrACAAZwByAOUAIAAyACwAMgAgAGcAYQBtAG0AYQAtAHAAcgBvAGYAaQBsAEcAYQBt -AG0AYQAgAGQAZQAgAGcAcgBpAHMAbwBzACAAZwBlAG4A6AByAGkAYwBhACAAMgAuADIAQx6lAHUAIABo -AOwAbgBoACAATQDgAHUAIAB4AOEAbQAgAEMAaAB1AG4AZwAgAEcAYQBtAG0AYQAgADIALgAyAFAAZQBy -AGYAaQBsACAARwBlAG4A6QByAGkAYwBvACAAZABhACAARwBhAG0AYQAgAGQAZQAgAEMAaQBuAHoAYQBz -ACAAMgAsADIEFwQwBDMEMAQ7BEwEPQQwACAARwByAGEAeQAtBDMEMAQ8BDAAIAAyAC4AMgBQAHIAbwBm -AGkAbAAgAGcA6QBuAOkAcgBpAHEAdQBlACAAZwByAGkAcwAgAGcAYQBtAG0AYQAgADIALAAyAMEAbAB0 -AGEAbADhAG4AbwBzACAAcwB6APwAcgBrAGUAIABnAGEAbQBtAGEAIAAyAC4AMpAadShwcJaOUUlepgAy -AC4AMoJyX2ljz4/wx3y8GAAg1ozAyQAgrBC5yAAgADIALgAyACDVBLhc0wzHfABHAGUAbgBlAHIAaQBz -AGsAIABnAHIA5QAgAGcAYQBtAG0AYQAgADIALAAyAC0AcAByAG8AZgBpAGwATwBiAGUAYwBuAOEAIAFh -AGUAZADhACAAZwBhAG0AYQAgADIALgAyBdIF0AXeBdQAIAXQBeQF1QXoACAF2wXcBdwF2QAgADIALgAy -AEcAYQBtAGEAIABnAHIAaQAgAGcAZQBuAGUAcgBpAGMBAwAgADIALAAyAEEAbABsAGcAZQBtAGUAaQBu -AGUAcwAgAEcAcgBhAHUAcwB0AHUAZgBlAG4ALQBQAHIAbwBmAGkAbAAgAEcAYQBtAG0AYQAgADIALAAy -AFAAcgBvAGYAaQBsAG8AIABnAHIAaQBnAGkAbwAgAGcAZQBuAGUAcgBpAGMAbwAgAGQAZQBsAGwAYQAg -AGcAYQBtAG0AYQAgADIALAAyAEcAZQBuAGUAcgBpAHMAawAgAGcAcgDlACAAMgAsADIAIABnAGEAbQBt -AGEAcAByAG8AZgBpAGxmbpAacHBepnz7ZXAAMgAuADJjz4/wZYdO9k4AgiwwsDDsMKQwrDDzMN4AIAAy -AC4AMgAgMNcw7TDVMKEwpDDrA5MDtQO9A7kDugPMACADkwO6A8EDuQAgA5MDrAO8A7wDsQAgADIALgAy -AFAAZQByAGYAaQBsACAAZwBlAG4A6QByAGkAYwBvACAAZABlACAAYwBpAG4AegBlAG4AdABvAHMAIABk -AGEAIABHAGEAbQBtAGEAIAAyACwAMgBBAGwAZwBlAG0AZQBlAG4AIABnAHIAaQBqAHMAIABnAGEAbQBt -AGEAIAAyACwAMgAtAHAAcgBvAGYAaQBlAGwAUABlAHIAZgBpAGwAIABnAGUAbgDpAHIAaQBjAG8AIABk -AGUAIABnAGEAbQBtAGEAIABkAGUAIABnAHIAaQBzAGUAcwAgADIALAAyDiMOMQ4HDioONQ5BDgEOIQ4h -DjIOQA4BDiMOIg5MDhcOMQ5IDicORA4bACAAMgAuADIARwBlAG4AZQBsACAARwByAGkAIABHAGEAbQBh -ACAAMgAsADIAWQBsAGUAaQBuAGUAbgAgAGgAYQByAG0AYQBhAG4AIABnAGEAbQBtAGEAIAAyACwAMgAg -AC0AcAByAG8AZgBpAGkAbABpAEcAZQBuAGUAcgBpAQ0AawBpACAARwByAGEAeQAgAEcAYQBtAG0AYQAg -ADIALgAyACAAcAByAG8AZgBpAGwAVQBuAGkAdwBlAHIAcwBhAGwAbgB5ACAAcAByAG8AZgBpAGwAIABz -AHoAYQByAG8BWwBjAGkAIABnAGEAbQBtAGEAIAAyACwAMgY6BicGRQYnACAAMgAuADIAIAZEBkgGRgAg -BjEGRQYnBi8GSgAgBjkGJwZFBB4EMQRJBDAETwAgBEEENQRABDAETwAgBDMEMAQ8BDwEMAAgADIALAAy -AC0EPwRABD4ERAQ4BDsETABHAGUAbgBlAHIAaQBjACAARwByAGEAeQAgAEcAYQBtAG0AYQAgADIALgAy -ACAAUAByAG8AZgBpAGwAZQAAdGV4dAAAAABDb3B5cmlnaHQgQXBwbGUgSW5jLiwgMjAxMgAAWFlaIAAA -AAAAAPNRAAEAAAABFsxjdXJ2AAAAAAAABAAAAAAFAAoADwAUABkAHgAjACgALQAyADcAOwBAAEUASgBP -AFQAWQBeAGMAaABtAHIAdwB8AIEAhgCLAJAAlQCaAJ8ApACpAK4AsgC3ALwAwQDGAMsA0ADVANsA4ADl -AOsA8AD2APsBAQEHAQ0BEwEZAR8BJQErATIBOAE+AUUBTAFSAVkBYAFnAW4BdQF8AYMBiwGSAZoBoQGp -AbEBuQHBAckB0QHZAeEB6QHyAfoCAwIMAhQCHQImAi8COAJBAksCVAJdAmcCcQJ6AoQCjgKYAqICrAK2 -AsECywLVAuAC6wL1AwADCwMWAyEDLQM4A0MDTwNaA2YDcgN+A4oDlgOiA64DugPHA9MD4APsA/kEBgQT -BCAELQQ7BEgEVQRjBHEEfgSMBJoEqAS2BMQE0wThBPAE/gUNBRwFKwU6BUkFWAVnBXcFhgWWBaYFtQXF -BdUF5QX2BgYGFgYnBjcGSAZZBmoGewaMBp0GrwbABtEG4wb1BwcHGQcrBz0HTwdhB3QHhgeZB6wHvwfS -B+UH+AgLCB8IMghGCFoIbgiCCJYIqgi+CNII5wj7CRAJJQk6CU8JZAl5CY8JpAm6Cc8J5Qn7ChEKJwo9 -ClQKagqBCpgKrgrFCtwK8wsLCyILOQtRC2kLgAuYC7ALyAvhC/kMEgwqDEMMXAx1DI4MpwzADNkM8w0N -DSYNQA1aDXQNjg2pDcMN3g34DhMOLg5JDmQOfw6bDrYO0g7uDwkPJQ9BD14Peg+WD7MPzw/sEAkQJhBD -EGEQfhCbELkQ1xD1ERMRMRFPEW0RjBGqEckR6BIHEiYSRRJkEoQSoxLDEuMTAxMjE0MTYxODE6QTxRPl -FAYUJxRJFGoUixStFM4U8BUSFTQVVhV4FZsVvRXgFgMWJhZJFmwWjxayFtYW+hcdF0EXZReJF64X0hf3 -GBsYQBhlGIoYrxjVGPoZIBlFGWsZkRm3Gd0aBBoqGlEadxqeGsUa7BsUGzsbYxuKG7Ib2hwCHCocUhx7 -HKMczBz1HR4dRx1wHZkdwx3sHhYeQB5qHpQevh7pHxMfPh9pH5Qfvx/qIBUgQSBsIJggxCDwIRwhSCF1 -IaEhziH7IiciVSKCIq8i3SMKIzgjZiOUI8Ij8CQfJE0kfCSrJNolCSU4JWgllyXHJfcmJyZXJocmtybo -JxgnSSd6J6sn3CgNKD8ocSiiKNQpBik4KWspnSnQKgIqNSpoKpsqzysCKzYraSudK9EsBSw5LG4soizX -LQwtQS12Last4S4WLkwugi63Lu4vJC9aL5Evxy/+MDUwbDCkMNsxEjFKMYIxujHyMioyYzKbMtQzDTNG -M38zuDPxNCs0ZTSeNNg1EzVNNYc1wjX9Njc2cjauNuk3JDdgN5w31zgUOFA4jDjIOQU5Qjl/Obw5+To2 -OnQ6sjrvOy07azuqO+g8JzxlPKQ84z0iPWE9oT3gPiA+YD6gPuA/IT9hP6I/4kAjQGRApkDnQSlBakGs -Qe5CMEJyQrVC90M6Q31DwEQDREdEikTORRJFVUWaRd5GIkZnRqtG8Ec1R3tHwEgFSEtIkUjXSR1JY0mp -SfBKN0p9SsRLDEtTS5pL4kwqTHJMuk0CTUpNk03cTiVObk63TwBPSU+TT91QJ1BxULtRBlFQUZtR5lIx -UnxSx1MTU19TqlP2VEJUj1TbVShVdVXCVg9WXFapVvdXRFeSV+BYL1h9WMtZGllpWbhaB1pWWqZa9VtF -W5Vb5Vw1XIZc1l0nXXhdyV4aXmxevV8PX2Ffs2AFYFdgqmD8YU9homH1YklinGLwY0Njl2PrZEBklGTp -ZT1lkmXnZj1mkmboZz1nk2fpaD9olmjsaUNpmmnxakhqn2r3a09rp2v/bFdsr20IbWBtuW4SbmtuxG8e -b3hv0XArcIZw4HE6cZVx8HJLcqZzAXNdc7h0FHRwdMx1KHWFdeF2Pnabdvh3VnezeBF4bnjMeSp5iXnn -ekZ6pXsEe2N7wnwhfIF84X1BfaF+AX5ifsJ/I3+Ef+WAR4CogQqBa4HNgjCCkoL0g1eDuoQdhICE44VH -hauGDoZyhteHO4efiASIaYjOiTOJmYn+imSKyoswi5aL/IxjjMqNMY2Yjf+OZo7OjzaPnpAGkG6Q1pE/ -kaiSEZJ6kuOTTZO2lCCUipT0lV+VyZY0lp+XCpd1l+CYTJi4mSSZkJn8mmia1ZtCm6+cHJyJnPedZJ3S -nkCerp8dn4uf+qBpoNihR6G2oiailqMGo3aj5qRWpMelOKWpphqmi6b9p26n4KhSqMSpN6mpqhyqj6sC -q3Wr6axcrNCtRK24ri2uoa8Wr4uwALB1sOqxYLHWskuywrM4s660JbSctRO1irYBtnm28Ldot+C4WbjR -uUq5wro7urW7LrunvCG8m70VvY++Cr6Evv+/er/1wHDA7MFnwePCX8Lbw1jD1MRRxM7FS8XIxkbGw8dB -x7/IPci8yTrJuco4yrfLNsu2zDXMtc01zbXONs62zzfPuNA50LrRPNG+0j/SwdNE08bUSdTL1U7V0dZV -1tjXXNfg2GTY6Nls2fHadtr724DcBdyK3RDdlt4c3qLfKd+v4DbgveFE4cziU+Lb42Pj6+Rz5PzlhOYN -5pbnH+ep6DLovOlG6dDqW+rl63Dr++yG7RHtnO4o7rTvQO/M8Fjw5fFy8f/yjPMZ86f0NPTC9VD13vZt -9vv3ivgZ+Kj5OPnH+lf65/t3/Af8mP0p/br+S/7c/23//9IrLC0uWiRjbGFzc25hbWVYJGNsYXNzZXNf -EBBOU0JpdG1hcEltYWdlUmVwoy0vMFpOU0ltYWdlUmVwWE5TT2JqZWN00issMjNXTlNBcnJheaIyMNIr -LDU2Xk5TTXV0YWJsZUFycmF5ozUyMNM4OQ86OzxXTlNXaGl0ZVxOU0NvbG9yU3BhY2VEMCAwABADgAzS -Kyw+P1dOU0NvbG9yoj4w0issQUJXTlNJbWFnZaJBMAAIABEAGgAkACkAMgA3AEkATABRAFMAYgBoAHUA -fACLAJIAnwCmAK4AsACyALQAuQC7AL0AxADJANQA1gDYANoA3wDiAOQA5gDoAO8BBgEiASQBJhOWE5sT -phOvE8ITxhPRE9oT3xPnE+oT7xP+FAIUCRQRFB4UIxQlFCcULBQ0FDcUPBREAAAAAAAAAgEAAAAAAAAA -QwAAAAAAAAAAAAAAAAAAFEc +S2V5ZWRBcmNoaXZlctEICVRyb290gAGuCwwbHCEaJissMzY5P0JVJG51bGzXDQ4PEBESExQVFhcYGRpW +TlNTaXplViRjbGFzc1xOU0ltYWdlRmxhZ3NWTlNSZXBzW05TVGludENvbG9yV05TQ29sb3JeTlNSZXNp +emluZ01vZGWAAoANEiDDAACAA4AAgAsQAFZ7MSwgMX3SHQ4eIFpOUy5vYmplY3RzoR+ABIAK0h0OIiWi +IySABYAGgAnTDicoKSoaXxAUTlNUSUZGUmVwcmVzZW50YXRpb25fEBlOU0ludGVybmFsTGF5b3V0RGly +ZWN0aW9ugAiAB08REmxNTQAqAAAACgAAABABAAADAAAAAQABAAABAQADAAAAAQABAAABAgADAAAAAgAI +AAgBAwADAAAAAQABAAABBgADAAAAAQABAAABCgADAAAAAQABAAABEQAEAAAAAQAAAAgBEgADAAAAAQAB +AAABFQADAAAAAQACAAABFgADAAAAAQABAAABFwAEAAAAAQAAAAIBHAADAAAAAQABAAABKAADAAAAAQAC +AAABUgADAAAAAQABAAABUwADAAAAAgABAAGHcwAHAAARnAAAANAAAAAAAAARnGFwcGwCAAAAbW50ckdS +QVlYWVogB9wACAAXAA8ALgAPYWNzcEFQUEwAAAAAbm9uZQAAAAAAAAAAAAAAAAAAAAAAAPbWAAEAAAAA +0y1hcHBsAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFZGVzYwAA +AMAAAAB5ZHNjbQAAATwAAAgaY3BydAAACVgAAAAjd3RwdAAACXwAAAAUa1RSQwAACZAAAAgMZGVzYwAA +AAAAAAAfR2VuZXJpYyBHcmF5IEdhbW1hIDIuMiBQcm9maWxlAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAG1s +dWMAAAAAAAAAHwAAAAxza1NLAAAALgAAAYRkYURLAAAAOgAAAbJjYUVTAAAAOAAAAex2aVZOAAAAQAAA +AiRwdEJSAAAASgAAAmR1a1VBAAAALAAAAq5mckZVAAAAPgAAAtpodUhVAAAANAAAAxh6aFRXAAAAGgAA +A0xrb0tSAAAAIgAAA2ZuYk5PAAAAOgAAA4hjc0NaAAAAKAAAA8JoZUlMAAAAJAAAA+pyb1JPAAAAKgAA +BA5kZURFAAAATgAABDhpdElUAAAATgAABIZzdlNFAAAAOAAABNR6aENOAAAAGgAABQxqYUpQAAAAJgAA +BSZlbEdSAAAAKgAABUxwdFBPAAAAUgAABXZubE5MAAAAQAAABchlc0VTAAAATAAABgh0aFRIAAAAMgAA +BlR0clRSAAAAJAAABoZmaUZJAAAARgAABqpockhSAAAAPgAABvBwbFBMAAAASgAABy5hckVHAAAALAAA +B3hydVJVAAAAOgAAB6RlblVTAAAAPAAAB94AVgFhAGUAbwBiAGUAYwBuAOEAIABzAGkAdgDhACAAZwBh +AG0AYQAgADIALAAyAEcAZQBuAGUAcgBpAHMAawAgAGcAcgDlACAAMgAsADIAIABnAGEAbQBtAGEALQBw +AHIAbwBmAGkAbABHAGEAbQBtAGEAIABkAGUAIABnAHIAaQBzAG8AcwAgAGcAZQBuAOgAcgBpAGMAYQAg +ADIALgAyAEMepQB1ACAAaADsAG4AaAAgAE0A4AB1ACAAeADhAG0AIABDAGgAdQBuAGcAIABHAGEAbQBt +AGEAIAAyAC4AMgBQAGUAcgBmAGkAbAAgAEcAZQBuAOkAcgBpAGMAbwAgAGQAYQAgAEcAYQBtAGEAIABk +AGUAIABDAGkAbgB6AGEAcwAgADIALAAyBBcEMAQzBDAEOwRMBD0EMAAgAEcAcgBhAHkALQQzBDAEPAQw +ACAAMgAuADIAUAByAG8AZgBpAGwAIABnAOkAbgDpAHIAaQBxAHUAZQAgAGcAcgBpAHMAIABnAGEAbQBt +AGEAIAAyACwAMgDBAGwAdABhAGwA4QBuAG8AcwAgAHMAegD8AHIAawBlACAAZwBhAG0AbQBhACAAMgAu +ADKQGnUocHCWjlFJXqYAMgAuADKCcl9pY8+P8Md8vBgAINaMwMkAIKwQucgAIAAyAC4AMgAg1QS4XNMM +x3wARwBlAG4AZQByAGkAcwBrACAAZwByAOUAIABnAGEAbQBtAGEAIAAyACwAMgAtAHAAcgBvAGYAaQBs +AE8AYgBlAGMAbgDhACABYQBlAGQA4QAgAGcAYQBtAGEAIAAyAC4AMgXSBdAF3gXUACAF0AXkBdUF6AAg +BdsF3AXcBdkAIAAyAC4AMgBHAGEAbQBhACAAZwByAGkAIABnAGUAbgBlAHIAaQBjAQMAIAAyACwAMgBB +AGwAbABnAGUAbQBlAGkAbgBlAHMAIABHAHIAYQB1AHMAdAB1AGYAZQBuAC0AUAByAG8AZgBpAGwAIABH +AGEAbQBtAGEAIAAyACwAMgBQAHIAbwBmAGkAbABvACAAZwByAGkAZwBpAG8AIABnAGUAbgBlAHIAaQBj +AG8AIABkAGUAbABsAGEAIABnAGEAbQBtAGEAIAAyACwAMgBHAGUAbgBlAHIAaQBzAGsAIABnAHIA5QAg +ADIALAAyACAAZwBhAG0AbQBhAHAAcgBvAGYAaQBsZm6QGnBwXqZ8+2VwADIALgAyY8+P8GWHTvZOAIIs +MLAw7DCkMKww8zDeACAAMgAuADIAIDDXMO0w1TChMKQw6wOTA7UDvQO5A7oDzAAgA5MDugPBA7kAIAOT +A6wDvAO8A7EAIAAyAC4AMgBQAGUAcgBmAGkAbAAgAGcAZQBuAOkAcgBpAGMAbwAgAGQAZQAgAGMAaQBu +AHoAZQBuAHQAbwBzACAAZABhACAARwBhAG0AbQBhACAAMgAsADIAQQBsAGcAZQBtAGUAZQBuACAAZwBy +AGkAagBzACAAZwBhAG0AbQBhACAAMgAsADIALQBwAHIAbwBmAGkAZQBsAFAAZQByAGYAaQBsACAAZwBl +AG4A6QByAGkAYwBvACAAZABlACAAZwBhAG0AbQBhACAAZABlACAAZwByAGkAcwBlAHMAIAAyACwAMg4j +DjEOBw4qDjUOQQ4BDiEOIQ4yDkAOAQ4jDiIOTA4XDjEOSA4nDkQOGwAgADIALgAyAEcAZQBuAGUAbAAg +AEcAcgBpACAARwBhAG0AYQAgADIALAAyAFkAbABlAGkAbgBlAG4AIABoAGEAcgBtAGEAYQBuACAAZwBh +AG0AbQBhACAAMgAsADIAIAAtAHAAcgBvAGYAaQBpAGwAaQBHAGUAbgBlAHIAaQENAGsAaQAgAEcAcgBh +AHkAIABHAGEAbQBtAGEAIAAyAC4AMgAgAHAAcgBvAGYAaQBsAFUAbgBpAHcAZQByAHMAYQBsAG4AeQAg +AHAAcgBvAGYAaQBsACAAcwB6AGEAcgBvAVsAYwBpACAAZwBhAG0AbQBhACAAMgAsADIGOgYnBkUGJwAg +ADIALgAyACAGRAZIBkYAIAYxBkUGJwYvBkoAIAY5BicGRQQeBDEESQQwBE8AIARBBDUEQAQwBE8AIAQz +BDAEPAQ8BDAAIAAyACwAMgAtBD8EQAQ+BEQEOAQ7BEwARwBlAG4AZQByAGkAYwAgAEcAcgBhAHkAIABH +AGEAbQBtAGEAIAAyAC4AMgAgAFAAcgBvAGYAaQBsAGUAAHRleHQAAAAAQ29weXJpZ2h0IEFwcGxlIElu +Yy4sIDIwMTIAAFhZWiAAAAAAAADzUQABAAAAARbMY3VydgAAAAAAAAQAAAAABQAKAA8AFAAZAB4AIwAo +AC0AMgA3ADsAQABFAEoATwBUAFkAXgBjAGgAbQByAHcAfACBAIYAiwCQAJUAmgCfAKQAqQCuALIAtwC8 +AMEAxgDLANAA1QDbAOAA5QDrAPAA9gD7AQEBBwENARMBGQEfASUBKwEyATgBPgFFAUwBUgFZAWABZwFu +AXUBfAGDAYsBkgGaAaEBqQGxAbkBwQHJAdEB2QHhAekB8gH6AgMCDAIUAh0CJgIvAjgCQQJLAlQCXQJn +AnECegKEAo4CmAKiAqwCtgLBAssC1QLgAusC9QMAAwsDFgMhAy0DOANDA08DWgNmA3IDfgOKA5YDogOu +A7oDxwPTA+AD7AP5BAYEEwQgBC0EOwRIBFUEYwRxBH4EjASaBKgEtgTEBNME4QTwBP4FDQUcBSsFOgVJ +BVgFZwV3BYYFlgWmBbUFxQXVBeUF9gYGBhYGJwY3BkgGWQZqBnsGjAadBq8GwAbRBuMG9QcHBxkHKwc9 +B08HYQd0B4YHmQesB78H0gflB/gICwgfCDIIRghaCG4IggiWCKoIvgjSCOcI+wkQCSUJOglPCWQJeQmP +CaQJugnPCeUJ+woRCicKPQpUCmoKgQqYCq4KxQrcCvMLCwsiCzkLUQtpC4ALmAuwC8gL4Qv5DBIMKgxD +DFwMdQyODKcMwAzZDPMNDQ0mDUANWg10DY4NqQ3DDd4N+A4TDi4OSQ5kDn8Omw62DtIO7g8JDyUPQQ9e +D3oPlg+zD88P7BAJECYQQxBhEH4QmxC5ENcQ9RETETERTxFtEYwRqhHJEegSBxImEkUSZBKEEqMSwxLj +EwMTIxNDE2MTgxOkE8UT5RQGFCcUSRRqFIsUrRTOFPAVEhU0FVYVeBWbFb0V4BYDFiYWSRZsFo8WshbW +FvoXHRdBF2UXiReuF9IX9xgbGEAYZRiKGK8Y1Rj6GSAZRRlrGZEZtxndGgQaKhpRGncanhrFGuwbFBs7 +G2MbihuyG9ocAhwqHFIcexyjHMwc9R0eHUcdcB2ZHcMd7B4WHkAeah6UHr4e6R8THz4faR+UH78f6iAV +IEEgbCCYIMQg8CEcIUghdSGhIc4h+yInIlUigiKvIt0jCiM4I2YjlCPCI/AkHyRNJHwkqyTaJQklOCVo +JZclxyX3JicmVyaHJrcm6CcYJ0kneierJ9woDSg/KHEooijUKQYpOClrKZ0p0CoCKjUqaCqbKs8rAis2 +K2krnSvRLAUsOSxuLKIs1y0MLUEtdi2rLeEuFi5MLoIuty7uLyQvWi+RL8cv/jA1MGwwpDDbMRIxSjGC +Mbox8jIqMmMymzLUMw0zRjN/M7gz8TQrNGU0njTYNRM1TTWHNcI1/TY3NnI2rjbpNyQ3YDecN9c4FDhQ +OIw4yDkFOUI5fzm8Ofk6Njp0OrI67zstO2s7qjvoPCc8ZTykPOM9Ij1hPaE94D4gPmA+oD7gPyE/YT+i +P+JAI0BkQKZA50EpQWpBrEHuQjBCckK1QvdDOkN9Q8BEA0RHRIpEzkUSRVVFmkXeRiJGZ0arRvBHNUd7 +R8BIBUhLSJFI10kdSWNJqUnwSjdKfUrESwxLU0uaS+JMKkxyTLpNAk1KTZNN3E4lTm5Ot08AT0lPk0/d +UCdQcVC7UQZRUFGbUeZSMVJ8UsdTE1NfU6pT9lRCVI9U21UoVXVVwlYPVlxWqVb3V0RXklfgWC9YfVjL +WRpZaVm4WgdaVlqmWvVbRVuVW+VcNVyGXNZdJ114XcleGl5sXr1fD19hX7NgBWBXYKpg/GFPYaJh9WJJ +Ypxi8GNDY5dj62RAZJRk6WU9ZZJl52Y9ZpJm6Gc9Z5Nn6Wg/aJZo7GlDaZpp8WpIap9q92tPa6dr/2xX +bK9tCG1gbbluEm5rbsRvHm94b9FwK3CGcOBxOnGVcfByS3KmcwFzXXO4dBR0cHTMdSh1hXXhdj52m3b4 +d1Z3s3gReG54zHkqeYl553pGeqV7BHtje8J8IXyBfOF9QX2hfgF+Yn7CfyN/hH/lgEeAqIEKgWuBzYIw +gpKC9INXg7qEHYSAhOOFR4Wrhg6GcobXhzuHn4gEiGmIzokziZmJ/opkisqLMIuWi/yMY4zKjTGNmI3/ +jmaOzo82j56QBpBukNaRP5GokhGSepLjk02TtpQglIqU9JVflcmWNJaflwqXdZfgmEyYuJkkmZCZ/Jpo +mtWbQpuvnByciZz3nWSd0p5Anq6fHZ+Ln/qgaaDYoUehtqImopajBqN2o+akVqTHpTilqaYapoum/adu +p+CoUqjEqTepqaocqo+rAqt1q+msXKzQrUStuK4trqGvFq+LsACwdbDqsWCx1rJLssKzOLOutCW0nLUT +tYq2AbZ5tvC3aLfguFm40blKucK6O7q1uy67p7whvJu9Fb2Pvgq+hL7/v3q/9cBwwOzBZ8Hjwl/C28NY +w9TEUcTOxUvFyMZGxsPHQce/yD3IvMk6ybnKOMq3yzbLtsw1zLXNNc21zjbOts83z7jQOdC60TzRvtI/ +0sHTRNPG1EnUy9VO1dHWVdbY11zX4Nhk2OjZbNnx2nba+9uA3AXcit0Q3ZbeHN6i3ynfr+A24L3hROHM +4lPi2+Nj4+vkc+T85YTmDeaW5x/nqegy6LzpRunQ6lvq5etw6/vshu0R7ZzuKO6070DvzPBY8OXxcvH/ +8ozzGfOn9DT0wvVQ9d72bfb794r4Gfio+Tj5x/pX+uf7d/wH/Jj9Kf26/kv+3P9t///SLS4vMFokY2xh +c3NuYW1lWCRjbGFzc2VzXxAQTlNCaXRtYXBJbWFnZVJlcKMvMTJaTlNJbWFnZVJlcFhOU09iamVjdNIt +LjQ1V05TQXJyYXmiNDLSLS43OF5OU011dGFibGVBcnJheaM3NDLTOjsOPD0+V05TV2hpdGVcTlNDb2xv +clNwYWNlRDAgMAAQA4AM0i0uQEFXTlNDb2xvcqJAMtItLkNEV05TSW1hZ2WiQzIACAARABoAJAApADIA +NwBJAEwAUQBTAGIAaAB3AH4AhQCSAJkApQCtALwAvgDAAMUAxwDJAMsAzQDUANkA5ADmAOgA6gDvAPIA +9AD2APgA/wEWATIBNAE2E6YTqxO2E78T0hPWE+ET6hPvE/cT+hP/FA4UEhQZFCEULhQzFDUUNxQ8FEQU +RxRMFFQAAAAAAAACAQAAAAAAAABFAAAAAAAAAAAAAAAAAAAUVw diff --git a/i18n/src/main/resources/sv.lproj/Command.xib b/i18n/src/main/resources/sv.lproj/Command.xib index 602c3031b0..77ce007f92 100644 --- a/i18n/src/main/resources/sv.lproj/Command.xib +++ b/i18n/src/main/resources/sv.lproj/Command.xib @@ -1,7 +1,7 @@ - + - + diff --git a/i18n/src/main/resources/sv.lproj/Connection.xib b/i18n/src/main/resources/sv.lproj/Connection.xib index 53bdb19b18..24082b06cc 100644 --- a/i18n/src/main/resources/sv.lproj/Connection.xib +++ b/i18n/src/main/resources/sv.lproj/Connection.xib @@ -1,7 +1,7 @@ - + - + @@ -76,7 +76,7 @@ - + diff --git a/i18n/src/main/resources/sv.lproj/Info.xib b/i18n/src/main/resources/sv.lproj/Info.xib index 6edcb4b0fb..a40ce94bcc 100644 --- a/i18n/src/main/resources/sv.lproj/Info.xib +++ b/i18n/src/main/resources/sv.lproj/Info.xib @@ -1,7 +1,7 @@ - + - + @@ -89,7 +89,7 @@ - + @@ -1017,21 +1017,21 @@ - + - + - + @@ -1121,21 +1121,21 @@ - + - + - + @@ -1250,27 +1250,27 @@ diff --git a/i18n/src/main/resources/sv.lproj/Login.strings b/i18n/src/main/resources/sv.lproj/Login.strings index 3f3bc468d2..9e6f7985c9 100644 Binary files a/i18n/src/main/resources/sv.lproj/Login.strings and b/i18n/src/main/resources/sv.lproj/Login.strings differ diff --git a/i18n/src/main/resources/sv.lproj/Main.xib b/i18n/src/main/resources/sv.lproj/Main.xib index 3598aa6f01..e3fdd291c5 100644 --- a/i18n/src/main/resources/sv.lproj/Main.xib +++ b/i18n/src/main/resources/sv.lproj/Main.xib @@ -1,7 +1,7 @@ - + - + @@ -94,7 +94,7 @@ - + @@ -159,19 +159,19 @@ - + - + - + @@ -321,12 +321,12 @@ CA - + - + @@ -566,6 +566,10 @@ CA - + + + + + diff --git a/i18n/src/main/resources/sv.lproj/Preferences.xib b/i18n/src/main/resources/sv.lproj/Preferences.xib index abd2fe76f2..1902dc1435 100644 --- a/i18n/src/main/resources/sv.lproj/Preferences.xib +++ b/i18n/src/main/resources/sv.lproj/Preferences.xib @@ -1,7 +1,7 @@ - + - + @@ -266,7 +266,7 @@ - + @@ -307,7 +307,7 @@ - + @@ -538,7 +538,7 @@ - + @@ -578,7 +578,7 @@ - + @@ -613,7 +613,7 @@ - + @@ -642,7 +642,7 @@ - + @@ -1218,7 +1218,7 @@ - + @@ -1250,7 +1250,7 @@ - + @@ -1290,7 +1290,7 @@ - + @@ -1363,7 +1363,7 @@ - + @@ -1558,7 +1558,7 @@ - + @@ -1618,7 +1618,7 @@ - + @@ -1643,7 +1643,7 @@ - + @@ -1685,7 +1685,7 @@ - + @@ -1713,7 +1713,7 @@ - + @@ -1741,7 +1741,7 @@ - + @@ -1770,7 +1770,7 @@ - + @@ -1805,7 +1805,7 @@ - + @@ -1888,7 +1888,7 @@ - + @@ -1947,7 +1947,7 @@ - + @@ -1975,7 +1975,7 @@ - + @@ -2004,7 +2004,7 @@ - + diff --git a/i18n/src/main/resources/sv.lproj/Profiles.xib b/i18n/src/main/resources/sv.lproj/Profiles.xib index 7350b225f9..d680ff5e0a 100644 --- a/i18n/src/main/resources/sv.lproj/Profiles.xib +++ b/i18n/src/main/resources/sv.lproj/Profiles.xib @@ -1,7 +1,7 @@ - + - + diff --git a/i18n/src/main/resources/sv.lproj/Prompt.xib b/i18n/src/main/resources/sv.lproj/Prompt.xib index 48dce3cb46..14d596cfb4 100644 --- a/i18n/src/main/resources/sv.lproj/Prompt.xib +++ b/i18n/src/main/resources/sv.lproj/Prompt.xib @@ -1,7 +1,7 @@ - + - + @@ -95,7 +95,7 @@ Gw - + diff --git a/i18n/src/main/resources/sv.lproj/S3.strings b/i18n/src/main/resources/sv.lproj/S3.strings index b5bba4864e..1429c2f2c1 100644 Binary files a/i18n/src/main/resources/sv.lproj/S3.strings and b/i18n/src/main/resources/sv.lproj/S3.strings differ diff --git a/i18n/src/main/resources/sv.lproj/Updater.strings b/i18n/src/main/resources/sv.lproj/Updater.strings index 1e4eaba2a4..a0c40e69fa 100644 Binary files a/i18n/src/main/resources/sv.lproj/Updater.strings and b/i18n/src/main/resources/sv.lproj/Updater.strings differ diff --git a/i18n/src/main/resources/ta_IN.lproj/Bookmark.xib b/i18n/src/main/resources/ta_IN.lproj/Bookmark.xib index 10ffe272d0..3099a74920 100644 --- a/i18n/src/main/resources/ta_IN.lproj/Bookmark.xib +++ b/i18n/src/main/resources/ta_IN.lproj/Bookmark.xib @@ -1,7 +1,7 @@ - + - + @@ -92,7 +92,7 @@ - + @@ -489,95 +489,95 @@ YnBsaXN0MDDUAQIDBAUGBwpYJHZlcnNpb25ZJGFyY2hpdmVyVCR0b3BYJG9iamVjdHMSAAGGoF8QD05T -S2V5ZWRBcmNoaXZlctEICVRyb290gAGuCwwZGh8UJCkqMTQ3PUBVJG51bGzWDQ4PEBESExQVFhcYVk5T -U2l6ZV5OU1Jlc2l6aW5nTW9kZVYkY2xhc3NcTlNJbWFnZUZsYWdzVk5TUmVwc1dOU0NvbG9ygAIQAIAN -EiDDAACAA4ALVnsxLCAxfdIbDxweWk5TLm9iamVjdHOhHYAEgArSGw8gI6IhIoAFgAaACdMPJSYnKBRf -EBROU1RJRkZSZXByZXNlbnRhdGlvbl8QGU5TSW50ZXJuYWxMYXlvdXREaXJlY3Rpb26ACIAHTxESbE1N -ACoAAAAKAAAAEAEAAAMAAAABAAEAAAEBAAMAAAABAAEAAAECAAMAAAACAAgACAEDAAMAAAABAAEAAAEG -AAMAAAABAAEAAAEKAAMAAAABAAEAAAERAAQAAAABAAAACAESAAMAAAABAAEAAAEVAAMAAAABAAIAAAEW -AAMAAAABAAEAAAEXAAQAAAABAAAAAgEcAAMAAAABAAEAAAEoAAMAAAABAAIAAAFSAAMAAAABAAEAAAFT -AAMAAAACAAEAAYdzAAcAABGcAAAA0AAAAAAAABGcYXBwbAIAAABtbnRyR1JBWVhZWiAH3AAIABcADwAu -AA9hY3NwQVBQTAAAAABub25lAAAAAAAAAAAAAAAAAAAAAAAA9tYAAQAAAADTLWFwcGwAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAVkZXNjAAAAwAAAAHlkc2NtAAABPAAA -CBpjcHJ0AAAJWAAAACN3dHB0AAAJfAAAABRrVFJDAAAJkAAACAxkZXNjAAAAAAAAAB9HZW5lcmljIEdy -YXkgR2FtbWEgMi4yIFByb2ZpbGUAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAbWx1YwAAAAAAAAAfAAAADHNr -U0sAAAAuAAABhGRhREsAAAA6AAABsmNhRVMAAAA4AAAB7HZpVk4AAABAAAACJHB0QlIAAABKAAACZHVr -VUEAAAAsAAACrmZyRlUAAAA+AAAC2mh1SFUAAAA0AAADGHpoVFcAAAAaAAADTGtvS1IAAAAiAAADZm5i -Tk8AAAA6AAADiGNzQ1oAAAAoAAADwmhlSUwAAAAkAAAD6nJvUk8AAAAqAAAEDmRlREUAAABOAAAEOGl0 -SVQAAABOAAAEhnN2U0UAAAA4AAAE1HpoQ04AAAAaAAAFDGphSlAAAAAmAAAFJmVsR1IAAAAqAAAFTHB0 -UE8AAABSAAAFdm5sTkwAAABAAAAFyGVzRVMAAABMAAAGCHRoVEgAAAAyAAAGVHRyVFIAAAAkAAAGhmZp -RkkAAABGAAAGqmhySFIAAAA+AAAG8HBsUEwAAABKAAAHLmFyRUcAAAAsAAAHeHJ1UlUAAAA6AAAHpGVu -VVMAAAA8AAAH3gBWAWEAZQBvAGIAZQBjAG4A4QAgAHMAaQB2AOEAIABnAGEAbQBhACAAMgAsADIARwBl -AG4AZQByAGkAcwBrACAAZwByAOUAIAAyACwAMgAgAGcAYQBtAG0AYQAtAHAAcgBvAGYAaQBsAEcAYQBt -AG0AYQAgAGQAZQAgAGcAcgBpAHMAbwBzACAAZwBlAG4A6AByAGkAYwBhACAAMgAuADIAQx6lAHUAIABo -AOwAbgBoACAATQDgAHUAIAB4AOEAbQAgAEMAaAB1AG4AZwAgAEcAYQBtAG0AYQAgADIALgAyAFAAZQBy -AGYAaQBsACAARwBlAG4A6QByAGkAYwBvACAAZABhACAARwBhAG0AYQAgAGQAZQAgAEMAaQBuAHoAYQBz -ACAAMgAsADIEFwQwBDMEMAQ7BEwEPQQwACAARwByAGEAeQAtBDMEMAQ8BDAAIAAyAC4AMgBQAHIAbwBm -AGkAbAAgAGcA6QBuAOkAcgBpAHEAdQBlACAAZwByAGkAcwAgAGcAYQBtAG0AYQAgADIALAAyAMEAbAB0 -AGEAbADhAG4AbwBzACAAcwB6APwAcgBrAGUAIABnAGEAbQBtAGEAIAAyAC4AMpAadShwcJaOUUlepgAy -AC4AMoJyX2ljz4/wx3y8GAAg1ozAyQAgrBC5yAAgADIALgAyACDVBLhc0wzHfABHAGUAbgBlAHIAaQBz -AGsAIABnAHIA5QAgAGcAYQBtAG0AYQAgADIALAAyAC0AcAByAG8AZgBpAGwATwBiAGUAYwBuAOEAIAFh -AGUAZADhACAAZwBhAG0AYQAgADIALgAyBdIF0AXeBdQAIAXQBeQF1QXoACAF2wXcBdwF2QAgADIALgAy -AEcAYQBtAGEAIABnAHIAaQAgAGcAZQBuAGUAcgBpAGMBAwAgADIALAAyAEEAbABsAGcAZQBtAGUAaQBu -AGUAcwAgAEcAcgBhAHUAcwB0AHUAZgBlAG4ALQBQAHIAbwBmAGkAbAAgAEcAYQBtAG0AYQAgADIALAAy -AFAAcgBvAGYAaQBsAG8AIABnAHIAaQBnAGkAbwAgAGcAZQBuAGUAcgBpAGMAbwAgAGQAZQBsAGwAYQAg -AGcAYQBtAG0AYQAgADIALAAyAEcAZQBuAGUAcgBpAHMAawAgAGcAcgDlACAAMgAsADIAIABnAGEAbQBt -AGEAcAByAG8AZgBpAGxmbpAacHBepnz7ZXAAMgAuADJjz4/wZYdO9k4AgiwwsDDsMKQwrDDzMN4AIAAy -AC4AMgAgMNcw7TDVMKEwpDDrA5MDtQO9A7kDugPMACADkwO6A8EDuQAgA5MDrAO8A7wDsQAgADIALgAy -AFAAZQByAGYAaQBsACAAZwBlAG4A6QByAGkAYwBvACAAZABlACAAYwBpAG4AegBlAG4AdABvAHMAIABk -AGEAIABHAGEAbQBtAGEAIAAyACwAMgBBAGwAZwBlAG0AZQBlAG4AIABnAHIAaQBqAHMAIABnAGEAbQBt -AGEAIAAyACwAMgAtAHAAcgBvAGYAaQBlAGwAUABlAHIAZgBpAGwAIABnAGUAbgDpAHIAaQBjAG8AIABk -AGUAIABnAGEAbQBtAGEAIABkAGUAIABnAHIAaQBzAGUAcwAgADIALAAyDiMOMQ4HDioONQ5BDgEOIQ4h -DjIOQA4BDiMOIg5MDhcOMQ5IDicORA4bACAAMgAuADIARwBlAG4AZQBsACAARwByAGkAIABHAGEAbQBh -ACAAMgAsADIAWQBsAGUAaQBuAGUAbgAgAGgAYQByAG0AYQBhAG4AIABnAGEAbQBtAGEAIAAyACwAMgAg -AC0AcAByAG8AZgBpAGkAbABpAEcAZQBuAGUAcgBpAQ0AawBpACAARwByAGEAeQAgAEcAYQBtAG0AYQAg -ADIALgAyACAAcAByAG8AZgBpAGwAVQBuAGkAdwBlAHIAcwBhAGwAbgB5ACAAcAByAG8AZgBpAGwAIABz -AHoAYQByAG8BWwBjAGkAIABnAGEAbQBtAGEAIAAyACwAMgY6BicGRQYnACAAMgAuADIAIAZEBkgGRgAg -BjEGRQYnBi8GSgAgBjkGJwZFBB4EMQRJBDAETwAgBEEENQRABDAETwAgBDMEMAQ8BDwEMAAgADIALAAy -AC0EPwRABD4ERAQ4BDsETABHAGUAbgBlAHIAaQBjACAARwByAGEAeQAgAEcAYQBtAG0AYQAgADIALgAy -ACAAUAByAG8AZgBpAGwAZQAAdGV4dAAAAABDb3B5cmlnaHQgQXBwbGUgSW5jLiwgMjAxMgAAWFlaIAAA -AAAAAPNRAAEAAAABFsxjdXJ2AAAAAAAABAAAAAAFAAoADwAUABkAHgAjACgALQAyADcAOwBAAEUASgBP -AFQAWQBeAGMAaABtAHIAdwB8AIEAhgCLAJAAlQCaAJ8ApACpAK4AsgC3ALwAwQDGAMsA0ADVANsA4ADl -AOsA8AD2APsBAQEHAQ0BEwEZAR8BJQErATIBOAE+AUUBTAFSAVkBYAFnAW4BdQF8AYMBiwGSAZoBoQGp -AbEBuQHBAckB0QHZAeEB6QHyAfoCAwIMAhQCHQImAi8COAJBAksCVAJdAmcCcQJ6AoQCjgKYAqICrAK2 -AsECywLVAuAC6wL1AwADCwMWAyEDLQM4A0MDTwNaA2YDcgN+A4oDlgOiA64DugPHA9MD4APsA/kEBgQT -BCAELQQ7BEgEVQRjBHEEfgSMBJoEqAS2BMQE0wThBPAE/gUNBRwFKwU6BUkFWAVnBXcFhgWWBaYFtQXF -BdUF5QX2BgYGFgYnBjcGSAZZBmoGewaMBp0GrwbABtEG4wb1BwcHGQcrBz0HTwdhB3QHhgeZB6wHvwfS -B+UH+AgLCB8IMghGCFoIbgiCCJYIqgi+CNII5wj7CRAJJQk6CU8JZAl5CY8JpAm6Cc8J5Qn7ChEKJwo9 -ClQKagqBCpgKrgrFCtwK8wsLCyILOQtRC2kLgAuYC7ALyAvhC/kMEgwqDEMMXAx1DI4MpwzADNkM8w0N -DSYNQA1aDXQNjg2pDcMN3g34DhMOLg5JDmQOfw6bDrYO0g7uDwkPJQ9BD14Peg+WD7MPzw/sEAkQJhBD -EGEQfhCbELkQ1xD1ERMRMRFPEW0RjBGqEckR6BIHEiYSRRJkEoQSoxLDEuMTAxMjE0MTYxODE6QTxRPl -FAYUJxRJFGoUixStFM4U8BUSFTQVVhV4FZsVvRXgFgMWJhZJFmwWjxayFtYW+hcdF0EXZReJF64X0hf3 -GBsYQBhlGIoYrxjVGPoZIBlFGWsZkRm3Gd0aBBoqGlEadxqeGsUa7BsUGzsbYxuKG7Ib2hwCHCocUhx7 -HKMczBz1HR4dRx1wHZkdwx3sHhYeQB5qHpQevh7pHxMfPh9pH5Qfvx/qIBUgQSBsIJggxCDwIRwhSCF1 -IaEhziH7IiciVSKCIq8i3SMKIzgjZiOUI8Ij8CQfJE0kfCSrJNolCSU4JWgllyXHJfcmJyZXJocmtybo -JxgnSSd6J6sn3CgNKD8ocSiiKNQpBik4KWspnSnQKgIqNSpoKpsqzysCKzYraSudK9EsBSw5LG4soizX -LQwtQS12Last4S4WLkwugi63Lu4vJC9aL5Evxy/+MDUwbDCkMNsxEjFKMYIxujHyMioyYzKbMtQzDTNG -M38zuDPxNCs0ZTSeNNg1EzVNNYc1wjX9Njc2cjauNuk3JDdgN5w31zgUOFA4jDjIOQU5Qjl/Obw5+To2 -OnQ6sjrvOy07azuqO+g8JzxlPKQ84z0iPWE9oT3gPiA+YD6gPuA/IT9hP6I/4kAjQGRApkDnQSlBakGs -Qe5CMEJyQrVC90M6Q31DwEQDREdEikTORRJFVUWaRd5GIkZnRqtG8Ec1R3tHwEgFSEtIkUjXSR1JY0mp -SfBKN0p9SsRLDEtTS5pL4kwqTHJMuk0CTUpNk03cTiVObk63TwBPSU+TT91QJ1BxULtRBlFQUZtR5lIx -UnxSx1MTU19TqlP2VEJUj1TbVShVdVXCVg9WXFapVvdXRFeSV+BYL1h9WMtZGllpWbhaB1pWWqZa9VtF -W5Vb5Vw1XIZc1l0nXXhdyV4aXmxevV8PX2Ffs2AFYFdgqmD8YU9homH1YklinGLwY0Njl2PrZEBklGTp -ZT1lkmXnZj1mkmboZz1nk2fpaD9olmjsaUNpmmnxakhqn2r3a09rp2v/bFdsr20IbWBtuW4SbmtuxG8e -b3hv0XArcIZw4HE6cZVx8HJLcqZzAXNdc7h0FHRwdMx1KHWFdeF2Pnabdvh3VnezeBF4bnjMeSp5iXnn -ekZ6pXsEe2N7wnwhfIF84X1BfaF+AX5ifsJ/I3+Ef+WAR4CogQqBa4HNgjCCkoL0g1eDuoQdhICE44VH -hauGDoZyhteHO4efiASIaYjOiTOJmYn+imSKyoswi5aL/IxjjMqNMY2Yjf+OZo7OjzaPnpAGkG6Q1pE/ -kaiSEZJ6kuOTTZO2lCCUipT0lV+VyZY0lp+XCpd1l+CYTJi4mSSZkJn8mmia1ZtCm6+cHJyJnPedZJ3S -nkCerp8dn4uf+qBpoNihR6G2oiailqMGo3aj5qRWpMelOKWpphqmi6b9p26n4KhSqMSpN6mpqhyqj6sC -q3Wr6axcrNCtRK24ri2uoa8Wr4uwALB1sOqxYLHWskuywrM4s660JbSctRO1irYBtnm28Ldot+C4WbjR -uUq5wro7urW7LrunvCG8m70VvY++Cr6Evv+/er/1wHDA7MFnwePCX8Lbw1jD1MRRxM7FS8XIxkbGw8dB -x7/IPci8yTrJuco4yrfLNsu2zDXMtc01zbXONs62zzfPuNA50LrRPNG+0j/SwdNE08bUSdTL1U7V0dZV -1tjXXNfg2GTY6Nls2fHadtr724DcBdyK3RDdlt4c3qLfKd+v4DbgveFE4cziU+Lb42Pj6+Rz5PzlhOYN -5pbnH+ep6DLovOlG6dDqW+rl63Dr++yG7RHtnO4o7rTvQO/M8Fjw5fFy8f/yjPMZ86f0NPTC9VD13vZt -9vv3ivgZ+Kj5OPnH+lf65/t3/Af8mP0p/br+S/7c/23//9IrLC0uWiRjbGFzc25hbWVYJGNsYXNzZXNf -EBBOU0JpdG1hcEltYWdlUmVwoy0vMFpOU0ltYWdlUmVwWE5TT2JqZWN00issMjNXTlNBcnJheaIyMNIr -LDU2Xk5TTXV0YWJsZUFycmF5ozUyMNM4OQ86OzxXTlNXaGl0ZVxOU0NvbG9yU3BhY2VEMCAwABADgAzS -Kyw+P1dOU0NvbG9yoj4w0issQUJXTlNJbWFnZaJBMAAIABEAGgAkACkAMgA3AEkATABRAFMAYgBoAHUA -fACLAJIAnwCmAK4AsACyALQAuQC7AL0AxADJANQA1gDYANoA3wDiAOQA5gDoAO8BBgEiASQBJhOWE5sT -phOvE8ITxhPRE9oT3xPnE+oT7xP+FAIUCRQRFB4UIxQlFCcULBQ0FDcUPBREAAAAAAAAAgEAAAAAAAAA -QwAAAAAAAAAAAAAAAAAAFEc +S2V5ZWRBcmNoaXZlctEICVRyb290gAGuCwwbHCEaJissMzY5P0JVJG51bGzXDQ4PEBESExQVFhcYGRpW +TlNTaXplViRjbGFzc1xOU0ltYWdlRmxhZ3NWTlNSZXBzW05TVGludENvbG9yV05TQ29sb3JeTlNSZXNp +emluZ01vZGWAAoANEiDDAACAA4AAgAsQAFZ7MSwgMX3SHQ4eIFpOUy5vYmplY3RzoR+ABIAK0h0OIiWi +IySABYAGgAnTDicoKSoaXxAUTlNUSUZGUmVwcmVzZW50YXRpb25fEBlOU0ludGVybmFsTGF5b3V0RGly +ZWN0aW9ugAiAB08REmxNTQAqAAAACgAAABABAAADAAAAAQABAAABAQADAAAAAQABAAABAgADAAAAAgAI +AAgBAwADAAAAAQABAAABBgADAAAAAQABAAABCgADAAAAAQABAAABEQAEAAAAAQAAAAgBEgADAAAAAQAB +AAABFQADAAAAAQACAAABFgADAAAAAQABAAABFwAEAAAAAQAAAAIBHAADAAAAAQABAAABKAADAAAAAQAC +AAABUgADAAAAAQABAAABUwADAAAAAgABAAGHcwAHAAARnAAAANAAAAAAAAARnGFwcGwCAAAAbW50ckdS +QVlYWVogB9wACAAXAA8ALgAPYWNzcEFQUEwAAAAAbm9uZQAAAAAAAAAAAAAAAAAAAAAAAPbWAAEAAAAA +0y1hcHBsAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFZGVzYwAA +AMAAAAB5ZHNjbQAAATwAAAgaY3BydAAACVgAAAAjd3RwdAAACXwAAAAUa1RSQwAACZAAAAgMZGVzYwAA +AAAAAAAfR2VuZXJpYyBHcmF5IEdhbW1hIDIuMiBQcm9maWxlAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAG1s +dWMAAAAAAAAAHwAAAAxza1NLAAAALgAAAYRkYURLAAAAOgAAAbJjYUVTAAAAOAAAAex2aVZOAAAAQAAA +AiRwdEJSAAAASgAAAmR1a1VBAAAALAAAAq5mckZVAAAAPgAAAtpodUhVAAAANAAAAxh6aFRXAAAAGgAA +A0xrb0tSAAAAIgAAA2ZuYk5PAAAAOgAAA4hjc0NaAAAAKAAAA8JoZUlMAAAAJAAAA+pyb1JPAAAAKgAA +BA5kZURFAAAATgAABDhpdElUAAAATgAABIZzdlNFAAAAOAAABNR6aENOAAAAGgAABQxqYUpQAAAAJgAA +BSZlbEdSAAAAKgAABUxwdFBPAAAAUgAABXZubE5MAAAAQAAABchlc0VTAAAATAAABgh0aFRIAAAAMgAA +BlR0clRSAAAAJAAABoZmaUZJAAAARgAABqpockhSAAAAPgAABvBwbFBMAAAASgAABy5hckVHAAAALAAA +B3hydVJVAAAAOgAAB6RlblVTAAAAPAAAB94AVgFhAGUAbwBiAGUAYwBuAOEAIABzAGkAdgDhACAAZwBh +AG0AYQAgADIALAAyAEcAZQBuAGUAcgBpAHMAawAgAGcAcgDlACAAMgAsADIAIABnAGEAbQBtAGEALQBw +AHIAbwBmAGkAbABHAGEAbQBtAGEAIABkAGUAIABnAHIAaQBzAG8AcwAgAGcAZQBuAOgAcgBpAGMAYQAg +ADIALgAyAEMepQB1ACAAaADsAG4AaAAgAE0A4AB1ACAAeADhAG0AIABDAGgAdQBuAGcAIABHAGEAbQBt +AGEAIAAyAC4AMgBQAGUAcgBmAGkAbAAgAEcAZQBuAOkAcgBpAGMAbwAgAGQAYQAgAEcAYQBtAGEAIABk +AGUAIABDAGkAbgB6AGEAcwAgADIALAAyBBcEMAQzBDAEOwRMBD0EMAAgAEcAcgBhAHkALQQzBDAEPAQw +ACAAMgAuADIAUAByAG8AZgBpAGwAIABnAOkAbgDpAHIAaQBxAHUAZQAgAGcAcgBpAHMAIABnAGEAbQBt +AGEAIAAyACwAMgDBAGwAdABhAGwA4QBuAG8AcwAgAHMAegD8AHIAawBlACAAZwBhAG0AbQBhACAAMgAu +ADKQGnUocHCWjlFJXqYAMgAuADKCcl9pY8+P8Md8vBgAINaMwMkAIKwQucgAIAAyAC4AMgAg1QS4XNMM +x3wARwBlAG4AZQByAGkAcwBrACAAZwByAOUAIABnAGEAbQBtAGEAIAAyACwAMgAtAHAAcgBvAGYAaQBs +AE8AYgBlAGMAbgDhACABYQBlAGQA4QAgAGcAYQBtAGEAIAAyAC4AMgXSBdAF3gXUACAF0AXkBdUF6AAg +BdsF3AXcBdkAIAAyAC4AMgBHAGEAbQBhACAAZwByAGkAIABnAGUAbgBlAHIAaQBjAQMAIAAyACwAMgBB +AGwAbABnAGUAbQBlAGkAbgBlAHMAIABHAHIAYQB1AHMAdAB1AGYAZQBuAC0AUAByAG8AZgBpAGwAIABH +AGEAbQBtAGEAIAAyACwAMgBQAHIAbwBmAGkAbABvACAAZwByAGkAZwBpAG8AIABnAGUAbgBlAHIAaQBj +AG8AIABkAGUAbABsAGEAIABnAGEAbQBtAGEAIAAyACwAMgBHAGUAbgBlAHIAaQBzAGsAIABnAHIA5QAg +ADIALAAyACAAZwBhAG0AbQBhAHAAcgBvAGYAaQBsZm6QGnBwXqZ8+2VwADIALgAyY8+P8GWHTvZOAIIs +MLAw7DCkMKww8zDeACAAMgAuADIAIDDXMO0w1TChMKQw6wOTA7UDvQO5A7oDzAAgA5MDugPBA7kAIAOT +A6wDvAO8A7EAIAAyAC4AMgBQAGUAcgBmAGkAbAAgAGcAZQBuAOkAcgBpAGMAbwAgAGQAZQAgAGMAaQBu +AHoAZQBuAHQAbwBzACAAZABhACAARwBhAG0AbQBhACAAMgAsADIAQQBsAGcAZQBtAGUAZQBuACAAZwBy +AGkAagBzACAAZwBhAG0AbQBhACAAMgAsADIALQBwAHIAbwBmAGkAZQBsAFAAZQByAGYAaQBsACAAZwBl +AG4A6QByAGkAYwBvACAAZABlACAAZwBhAG0AbQBhACAAZABlACAAZwByAGkAcwBlAHMAIAAyACwAMg4j +DjEOBw4qDjUOQQ4BDiEOIQ4yDkAOAQ4jDiIOTA4XDjEOSA4nDkQOGwAgADIALgAyAEcAZQBuAGUAbAAg +AEcAcgBpACAARwBhAG0AYQAgADIALAAyAFkAbABlAGkAbgBlAG4AIABoAGEAcgBtAGEAYQBuACAAZwBh +AG0AbQBhACAAMgAsADIAIAAtAHAAcgBvAGYAaQBpAGwAaQBHAGUAbgBlAHIAaQENAGsAaQAgAEcAcgBh +AHkAIABHAGEAbQBtAGEAIAAyAC4AMgAgAHAAcgBvAGYAaQBsAFUAbgBpAHcAZQByAHMAYQBsAG4AeQAg +AHAAcgBvAGYAaQBsACAAcwB6AGEAcgBvAVsAYwBpACAAZwBhAG0AbQBhACAAMgAsADIGOgYnBkUGJwAg +ADIALgAyACAGRAZIBkYAIAYxBkUGJwYvBkoAIAY5BicGRQQeBDEESQQwBE8AIARBBDUEQAQwBE8AIAQz +BDAEPAQ8BDAAIAAyACwAMgAtBD8EQAQ+BEQEOAQ7BEwARwBlAG4AZQByAGkAYwAgAEcAcgBhAHkAIABH +AGEAbQBtAGEAIAAyAC4AMgAgAFAAcgBvAGYAaQBsAGUAAHRleHQAAAAAQ29weXJpZ2h0IEFwcGxlIElu +Yy4sIDIwMTIAAFhZWiAAAAAAAADzUQABAAAAARbMY3VydgAAAAAAAAQAAAAABQAKAA8AFAAZAB4AIwAo +AC0AMgA3ADsAQABFAEoATwBUAFkAXgBjAGgAbQByAHcAfACBAIYAiwCQAJUAmgCfAKQAqQCuALIAtwC8 +AMEAxgDLANAA1QDbAOAA5QDrAPAA9gD7AQEBBwENARMBGQEfASUBKwEyATgBPgFFAUwBUgFZAWABZwFu +AXUBfAGDAYsBkgGaAaEBqQGxAbkBwQHJAdEB2QHhAekB8gH6AgMCDAIUAh0CJgIvAjgCQQJLAlQCXQJn +AnECegKEAo4CmAKiAqwCtgLBAssC1QLgAusC9QMAAwsDFgMhAy0DOANDA08DWgNmA3IDfgOKA5YDogOu +A7oDxwPTA+AD7AP5BAYEEwQgBC0EOwRIBFUEYwRxBH4EjASaBKgEtgTEBNME4QTwBP4FDQUcBSsFOgVJ +BVgFZwV3BYYFlgWmBbUFxQXVBeUF9gYGBhYGJwY3BkgGWQZqBnsGjAadBq8GwAbRBuMG9QcHBxkHKwc9 +B08HYQd0B4YHmQesB78H0gflB/gICwgfCDIIRghaCG4IggiWCKoIvgjSCOcI+wkQCSUJOglPCWQJeQmP +CaQJugnPCeUJ+woRCicKPQpUCmoKgQqYCq4KxQrcCvMLCwsiCzkLUQtpC4ALmAuwC8gL4Qv5DBIMKgxD +DFwMdQyODKcMwAzZDPMNDQ0mDUANWg10DY4NqQ3DDd4N+A4TDi4OSQ5kDn8Omw62DtIO7g8JDyUPQQ9e +D3oPlg+zD88P7BAJECYQQxBhEH4QmxC5ENcQ9RETETERTxFtEYwRqhHJEegSBxImEkUSZBKEEqMSwxLj +EwMTIxNDE2MTgxOkE8UT5RQGFCcUSRRqFIsUrRTOFPAVEhU0FVYVeBWbFb0V4BYDFiYWSRZsFo8WshbW +FvoXHRdBF2UXiReuF9IX9xgbGEAYZRiKGK8Y1Rj6GSAZRRlrGZEZtxndGgQaKhpRGncanhrFGuwbFBs7 +G2MbihuyG9ocAhwqHFIcexyjHMwc9R0eHUcdcB2ZHcMd7B4WHkAeah6UHr4e6R8THz4faR+UH78f6iAV +IEEgbCCYIMQg8CEcIUghdSGhIc4h+yInIlUigiKvIt0jCiM4I2YjlCPCI/AkHyRNJHwkqyTaJQklOCVo +JZclxyX3JicmVyaHJrcm6CcYJ0kneierJ9woDSg/KHEooijUKQYpOClrKZ0p0CoCKjUqaCqbKs8rAis2 +K2krnSvRLAUsOSxuLKIs1y0MLUEtdi2rLeEuFi5MLoIuty7uLyQvWi+RL8cv/jA1MGwwpDDbMRIxSjGC +Mbox8jIqMmMymzLUMw0zRjN/M7gz8TQrNGU0njTYNRM1TTWHNcI1/TY3NnI2rjbpNyQ3YDecN9c4FDhQ +OIw4yDkFOUI5fzm8Ofk6Njp0OrI67zstO2s7qjvoPCc8ZTykPOM9Ij1hPaE94D4gPmA+oD7gPyE/YT+i +P+JAI0BkQKZA50EpQWpBrEHuQjBCckK1QvdDOkN9Q8BEA0RHRIpEzkUSRVVFmkXeRiJGZ0arRvBHNUd7 +R8BIBUhLSJFI10kdSWNJqUnwSjdKfUrESwxLU0uaS+JMKkxyTLpNAk1KTZNN3E4lTm5Ot08AT0lPk0/d +UCdQcVC7UQZRUFGbUeZSMVJ8UsdTE1NfU6pT9lRCVI9U21UoVXVVwlYPVlxWqVb3V0RXklfgWC9YfVjL +WRpZaVm4WgdaVlqmWvVbRVuVW+VcNVyGXNZdJ114XcleGl5sXr1fD19hX7NgBWBXYKpg/GFPYaJh9WJJ +Ypxi8GNDY5dj62RAZJRk6WU9ZZJl52Y9ZpJm6Gc9Z5Nn6Wg/aJZo7GlDaZpp8WpIap9q92tPa6dr/2xX +bK9tCG1gbbluEm5rbsRvHm94b9FwK3CGcOBxOnGVcfByS3KmcwFzXXO4dBR0cHTMdSh1hXXhdj52m3b4 +d1Z3s3gReG54zHkqeYl553pGeqV7BHtje8J8IXyBfOF9QX2hfgF+Yn7CfyN/hH/lgEeAqIEKgWuBzYIw +gpKC9INXg7qEHYSAhOOFR4Wrhg6GcobXhzuHn4gEiGmIzokziZmJ/opkisqLMIuWi/yMY4zKjTGNmI3/ +jmaOzo82j56QBpBukNaRP5GokhGSepLjk02TtpQglIqU9JVflcmWNJaflwqXdZfgmEyYuJkkmZCZ/Jpo +mtWbQpuvnByciZz3nWSd0p5Anq6fHZ+Ln/qgaaDYoUehtqImopajBqN2o+akVqTHpTilqaYapoum/adu +p+CoUqjEqTepqaocqo+rAqt1q+msXKzQrUStuK4trqGvFq+LsACwdbDqsWCx1rJLssKzOLOutCW0nLUT +tYq2AbZ5tvC3aLfguFm40blKucK6O7q1uy67p7whvJu9Fb2Pvgq+hL7/v3q/9cBwwOzBZ8Hjwl/C28NY +w9TEUcTOxUvFyMZGxsPHQce/yD3IvMk6ybnKOMq3yzbLtsw1zLXNNc21zjbOts83z7jQOdC60TzRvtI/ +0sHTRNPG1EnUy9VO1dHWVdbY11zX4Nhk2OjZbNnx2nba+9uA3AXcit0Q3ZbeHN6i3ynfr+A24L3hROHM +4lPi2+Nj4+vkc+T85YTmDeaW5x/nqegy6LzpRunQ6lvq5etw6/vshu0R7ZzuKO6070DvzPBY8OXxcvH/ +8ozzGfOn9DT0wvVQ9d72bfb794r4Gfio+Tj5x/pX+uf7d/wH/Jj9Kf26/kv+3P9t///SLS4vMFokY2xh +c3NuYW1lWCRjbGFzc2VzXxAQTlNCaXRtYXBJbWFnZVJlcKMvMTJaTlNJbWFnZVJlcFhOU09iamVjdNIt +LjQ1V05TQXJyYXmiNDLSLS43OF5OU011dGFibGVBcnJheaM3NDLTOjsOPD0+V05TV2hpdGVcTlNDb2xv +clNwYWNlRDAgMAAQA4AM0i0uQEFXTlNDb2xvcqJAMtItLkNEV05TSW1hZ2WiQzIACAARABoAJAApADIA +NwBJAEwAUQBTAGIAaAB3AH4AhQCSAJkApQCtALwAvgDAAMUAxwDJAMsAzQDUANkA5ADmAOgA6gDvAPIA +9AD2APgA/wEWATIBNAE2E6YTqxO2E78T0hPWE+ET6hPvE/cT+hP/FA4UEhQZFCEULhQzFDUUNxQ8FEQU +RxRMFFQAAAAAAAACAQAAAAAAAABFAAAAAAAAAAAAAAAAAAAUVw diff --git a/i18n/src/main/resources/ta_IN.lproj/Browser.xib b/i18n/src/main/resources/ta_IN.lproj/Browser.xib index fa8f2b9715..856aa5f684 100644 --- a/i18n/src/main/resources/ta_IN.lproj/Browser.xib +++ b/i18n/src/main/resources/ta_IN.lproj/Browser.xib @@ -1,7 +1,7 @@ - + - + @@ -38,7 +38,7 @@ - + @@ -62,27 +62,27 @@ @@ -256,7 +256,7 @@ - + @@ -553,95 +553,95 @@ DQ YnBsaXN0MDDUAQIDBAUGBwpYJHZlcnNpb25ZJGFyY2hpdmVyVCR0b3BYJG9iamVjdHMSAAGGoF8QD05T -S2V5ZWRBcmNoaXZlctEICVRyb290gAGuCwwZGh8UJCkqMTQ3PUBVJG51bGzWDQ4PEBESExQVFhcYVk5T -U2l6ZV5OU1Jlc2l6aW5nTW9kZVYkY2xhc3NcTlNJbWFnZUZsYWdzVk5TUmVwc1dOU0NvbG9ygAIQAIAN -EiDDAACAA4ALVnsxLCAxfdIbDxweWk5TLm9iamVjdHOhHYAEgArSGw8gI6IhIoAFgAaACdMPJSYnKBRf -EBROU1RJRkZSZXByZXNlbnRhdGlvbl8QGU5TSW50ZXJuYWxMYXlvdXREaXJlY3Rpb26ACIAHTxESbE1N -ACoAAAAKAAAAEAEAAAMAAAABAAEAAAEBAAMAAAABAAEAAAECAAMAAAACAAgACAEDAAMAAAABAAEAAAEG -AAMAAAABAAEAAAEKAAMAAAABAAEAAAERAAQAAAABAAAACAESAAMAAAABAAEAAAEVAAMAAAABAAIAAAEW -AAMAAAABAAEAAAEXAAQAAAABAAAAAgEcAAMAAAABAAEAAAEoAAMAAAABAAIAAAFSAAMAAAABAAEAAAFT -AAMAAAACAAEAAYdzAAcAABGcAAAA0AAAAAAAABGcYXBwbAIAAABtbnRyR1JBWVhZWiAH3AAIABcADwAu -AA9hY3NwQVBQTAAAAABub25lAAAAAAAAAAAAAAAAAAAAAAAA9tYAAQAAAADTLWFwcGwAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAVkZXNjAAAAwAAAAHlkc2NtAAABPAAA -CBpjcHJ0AAAJWAAAACN3dHB0AAAJfAAAABRrVFJDAAAJkAAACAxkZXNjAAAAAAAAAB9HZW5lcmljIEdy -YXkgR2FtbWEgMi4yIFByb2ZpbGUAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAbWx1YwAAAAAAAAAfAAAADHNr -U0sAAAAuAAABhGRhREsAAAA6AAABsmNhRVMAAAA4AAAB7HZpVk4AAABAAAACJHB0QlIAAABKAAACZHVr -VUEAAAAsAAACrmZyRlUAAAA+AAAC2mh1SFUAAAA0AAADGHpoVFcAAAAaAAADTGtvS1IAAAAiAAADZm5i -Tk8AAAA6AAADiGNzQ1oAAAAoAAADwmhlSUwAAAAkAAAD6nJvUk8AAAAqAAAEDmRlREUAAABOAAAEOGl0 -SVQAAABOAAAEhnN2U0UAAAA4AAAE1HpoQ04AAAAaAAAFDGphSlAAAAAmAAAFJmVsR1IAAAAqAAAFTHB0 -UE8AAABSAAAFdm5sTkwAAABAAAAFyGVzRVMAAABMAAAGCHRoVEgAAAAyAAAGVHRyVFIAAAAkAAAGhmZp -RkkAAABGAAAGqmhySFIAAAA+AAAG8HBsUEwAAABKAAAHLmFyRUcAAAAsAAAHeHJ1UlUAAAA6AAAHpGVu -VVMAAAA8AAAH3gBWAWEAZQBvAGIAZQBjAG4A4QAgAHMAaQB2AOEAIABnAGEAbQBhACAAMgAsADIARwBl -AG4AZQByAGkAcwBrACAAZwByAOUAIAAyACwAMgAgAGcAYQBtAG0AYQAtAHAAcgBvAGYAaQBsAEcAYQBt -AG0AYQAgAGQAZQAgAGcAcgBpAHMAbwBzACAAZwBlAG4A6AByAGkAYwBhACAAMgAuADIAQx6lAHUAIABo -AOwAbgBoACAATQDgAHUAIAB4AOEAbQAgAEMAaAB1AG4AZwAgAEcAYQBtAG0AYQAgADIALgAyAFAAZQBy -AGYAaQBsACAARwBlAG4A6QByAGkAYwBvACAAZABhACAARwBhAG0AYQAgAGQAZQAgAEMAaQBuAHoAYQBz -ACAAMgAsADIEFwQwBDMEMAQ7BEwEPQQwACAARwByAGEAeQAtBDMEMAQ8BDAAIAAyAC4AMgBQAHIAbwBm -AGkAbAAgAGcA6QBuAOkAcgBpAHEAdQBlACAAZwByAGkAcwAgAGcAYQBtAG0AYQAgADIALAAyAMEAbAB0 -AGEAbADhAG4AbwBzACAAcwB6APwAcgBrAGUAIABnAGEAbQBtAGEAIAAyAC4AMpAadShwcJaOUUlepgAy -AC4AMoJyX2ljz4/wx3y8GAAg1ozAyQAgrBC5yAAgADIALgAyACDVBLhc0wzHfABHAGUAbgBlAHIAaQBz -AGsAIABnAHIA5QAgAGcAYQBtAG0AYQAgADIALAAyAC0AcAByAG8AZgBpAGwATwBiAGUAYwBuAOEAIAFh -AGUAZADhACAAZwBhAG0AYQAgADIALgAyBdIF0AXeBdQAIAXQBeQF1QXoACAF2wXcBdwF2QAgADIALgAy -AEcAYQBtAGEAIABnAHIAaQAgAGcAZQBuAGUAcgBpAGMBAwAgADIALAAyAEEAbABsAGcAZQBtAGUAaQBu -AGUAcwAgAEcAcgBhAHUAcwB0AHUAZgBlAG4ALQBQAHIAbwBmAGkAbAAgAEcAYQBtAG0AYQAgADIALAAy -AFAAcgBvAGYAaQBsAG8AIABnAHIAaQBnAGkAbwAgAGcAZQBuAGUAcgBpAGMAbwAgAGQAZQBsAGwAYQAg -AGcAYQBtAG0AYQAgADIALAAyAEcAZQBuAGUAcgBpAHMAawAgAGcAcgDlACAAMgAsADIAIABnAGEAbQBt -AGEAcAByAG8AZgBpAGxmbpAacHBepnz7ZXAAMgAuADJjz4/wZYdO9k4AgiwwsDDsMKQwrDDzMN4AIAAy -AC4AMgAgMNcw7TDVMKEwpDDrA5MDtQO9A7kDugPMACADkwO6A8EDuQAgA5MDrAO8A7wDsQAgADIALgAy -AFAAZQByAGYAaQBsACAAZwBlAG4A6QByAGkAYwBvACAAZABlACAAYwBpAG4AegBlAG4AdABvAHMAIABk -AGEAIABHAGEAbQBtAGEAIAAyACwAMgBBAGwAZwBlAG0AZQBlAG4AIABnAHIAaQBqAHMAIABnAGEAbQBt -AGEAIAAyACwAMgAtAHAAcgBvAGYAaQBlAGwAUABlAHIAZgBpAGwAIABnAGUAbgDpAHIAaQBjAG8AIABk -AGUAIABnAGEAbQBtAGEAIABkAGUAIABnAHIAaQBzAGUAcwAgADIALAAyDiMOMQ4HDioONQ5BDgEOIQ4h -DjIOQA4BDiMOIg5MDhcOMQ5IDicORA4bACAAMgAuADIARwBlAG4AZQBsACAARwByAGkAIABHAGEAbQBh -ACAAMgAsADIAWQBsAGUAaQBuAGUAbgAgAGgAYQByAG0AYQBhAG4AIABnAGEAbQBtAGEAIAAyACwAMgAg -AC0AcAByAG8AZgBpAGkAbABpAEcAZQBuAGUAcgBpAQ0AawBpACAARwByAGEAeQAgAEcAYQBtAG0AYQAg -ADIALgAyACAAcAByAG8AZgBpAGwAVQBuAGkAdwBlAHIAcwBhAGwAbgB5ACAAcAByAG8AZgBpAGwAIABz -AHoAYQByAG8BWwBjAGkAIABnAGEAbQBtAGEAIAAyACwAMgY6BicGRQYnACAAMgAuADIAIAZEBkgGRgAg -BjEGRQYnBi8GSgAgBjkGJwZFBB4EMQRJBDAETwAgBEEENQRABDAETwAgBDMEMAQ8BDwEMAAgADIALAAy -AC0EPwRABD4ERAQ4BDsETABHAGUAbgBlAHIAaQBjACAARwByAGEAeQAgAEcAYQBtAG0AYQAgADIALgAy -ACAAUAByAG8AZgBpAGwAZQAAdGV4dAAAAABDb3B5cmlnaHQgQXBwbGUgSW5jLiwgMjAxMgAAWFlaIAAA -AAAAAPNRAAEAAAABFsxjdXJ2AAAAAAAABAAAAAAFAAoADwAUABkAHgAjACgALQAyADcAOwBAAEUASgBP -AFQAWQBeAGMAaABtAHIAdwB8AIEAhgCLAJAAlQCaAJ8ApACpAK4AsgC3ALwAwQDGAMsA0ADVANsA4ADl -AOsA8AD2APsBAQEHAQ0BEwEZAR8BJQErATIBOAE+AUUBTAFSAVkBYAFnAW4BdQF8AYMBiwGSAZoBoQGp -AbEBuQHBAckB0QHZAeEB6QHyAfoCAwIMAhQCHQImAi8COAJBAksCVAJdAmcCcQJ6AoQCjgKYAqICrAK2 -AsECywLVAuAC6wL1AwADCwMWAyEDLQM4A0MDTwNaA2YDcgN+A4oDlgOiA64DugPHA9MD4APsA/kEBgQT -BCAELQQ7BEgEVQRjBHEEfgSMBJoEqAS2BMQE0wThBPAE/gUNBRwFKwU6BUkFWAVnBXcFhgWWBaYFtQXF -BdUF5QX2BgYGFgYnBjcGSAZZBmoGewaMBp0GrwbABtEG4wb1BwcHGQcrBz0HTwdhB3QHhgeZB6wHvwfS -B+UH+AgLCB8IMghGCFoIbgiCCJYIqgi+CNII5wj7CRAJJQk6CU8JZAl5CY8JpAm6Cc8J5Qn7ChEKJwo9 -ClQKagqBCpgKrgrFCtwK8wsLCyILOQtRC2kLgAuYC7ALyAvhC/kMEgwqDEMMXAx1DI4MpwzADNkM8w0N -DSYNQA1aDXQNjg2pDcMN3g34DhMOLg5JDmQOfw6bDrYO0g7uDwkPJQ9BD14Peg+WD7MPzw/sEAkQJhBD -EGEQfhCbELkQ1xD1ERMRMRFPEW0RjBGqEckR6BIHEiYSRRJkEoQSoxLDEuMTAxMjE0MTYxODE6QTxRPl -FAYUJxRJFGoUixStFM4U8BUSFTQVVhV4FZsVvRXgFgMWJhZJFmwWjxayFtYW+hcdF0EXZReJF64X0hf3 -GBsYQBhlGIoYrxjVGPoZIBlFGWsZkRm3Gd0aBBoqGlEadxqeGsUa7BsUGzsbYxuKG7Ib2hwCHCocUhx7 -HKMczBz1HR4dRx1wHZkdwx3sHhYeQB5qHpQevh7pHxMfPh9pH5Qfvx/qIBUgQSBsIJggxCDwIRwhSCF1 -IaEhziH7IiciVSKCIq8i3SMKIzgjZiOUI8Ij8CQfJE0kfCSrJNolCSU4JWgllyXHJfcmJyZXJocmtybo -JxgnSSd6J6sn3CgNKD8ocSiiKNQpBik4KWspnSnQKgIqNSpoKpsqzysCKzYraSudK9EsBSw5LG4soizX -LQwtQS12Last4S4WLkwugi63Lu4vJC9aL5Evxy/+MDUwbDCkMNsxEjFKMYIxujHyMioyYzKbMtQzDTNG -M38zuDPxNCs0ZTSeNNg1EzVNNYc1wjX9Njc2cjauNuk3JDdgN5w31zgUOFA4jDjIOQU5Qjl/Obw5+To2 -OnQ6sjrvOy07azuqO+g8JzxlPKQ84z0iPWE9oT3gPiA+YD6gPuA/IT9hP6I/4kAjQGRApkDnQSlBakGs -Qe5CMEJyQrVC90M6Q31DwEQDREdEikTORRJFVUWaRd5GIkZnRqtG8Ec1R3tHwEgFSEtIkUjXSR1JY0mp -SfBKN0p9SsRLDEtTS5pL4kwqTHJMuk0CTUpNk03cTiVObk63TwBPSU+TT91QJ1BxULtRBlFQUZtR5lIx -UnxSx1MTU19TqlP2VEJUj1TbVShVdVXCVg9WXFapVvdXRFeSV+BYL1h9WMtZGllpWbhaB1pWWqZa9VtF -W5Vb5Vw1XIZc1l0nXXhdyV4aXmxevV8PX2Ffs2AFYFdgqmD8YU9homH1YklinGLwY0Njl2PrZEBklGTp -ZT1lkmXnZj1mkmboZz1nk2fpaD9olmjsaUNpmmnxakhqn2r3a09rp2v/bFdsr20IbWBtuW4SbmtuxG8e -b3hv0XArcIZw4HE6cZVx8HJLcqZzAXNdc7h0FHRwdMx1KHWFdeF2Pnabdvh3VnezeBF4bnjMeSp5iXnn -ekZ6pXsEe2N7wnwhfIF84X1BfaF+AX5ifsJ/I3+Ef+WAR4CogQqBa4HNgjCCkoL0g1eDuoQdhICE44VH -hauGDoZyhteHO4efiASIaYjOiTOJmYn+imSKyoswi5aL/IxjjMqNMY2Yjf+OZo7OjzaPnpAGkG6Q1pE/ -kaiSEZJ6kuOTTZO2lCCUipT0lV+VyZY0lp+XCpd1l+CYTJi4mSSZkJn8mmia1ZtCm6+cHJyJnPedZJ3S -nkCerp8dn4uf+qBpoNihR6G2oiailqMGo3aj5qRWpMelOKWpphqmi6b9p26n4KhSqMSpN6mpqhyqj6sC -q3Wr6axcrNCtRK24ri2uoa8Wr4uwALB1sOqxYLHWskuywrM4s660JbSctRO1irYBtnm28Ldot+C4WbjR -uUq5wro7urW7LrunvCG8m70VvY++Cr6Evv+/er/1wHDA7MFnwePCX8Lbw1jD1MRRxM7FS8XIxkbGw8dB -x7/IPci8yTrJuco4yrfLNsu2zDXMtc01zbXONs62zzfPuNA50LrRPNG+0j/SwdNE08bUSdTL1U7V0dZV -1tjXXNfg2GTY6Nls2fHadtr724DcBdyK3RDdlt4c3qLfKd+v4DbgveFE4cziU+Lb42Pj6+Rz5PzlhOYN -5pbnH+ep6DLovOlG6dDqW+rl63Dr++yG7RHtnO4o7rTvQO/M8Fjw5fFy8f/yjPMZ86f0NPTC9VD13vZt -9vv3ivgZ+Kj5OPnH+lf65/t3/Af8mP0p/br+S/7c/23//9IrLC0uWiRjbGFzc25hbWVYJGNsYXNzZXNf -EBBOU0JpdG1hcEltYWdlUmVwoy0vMFpOU0ltYWdlUmVwWE5TT2JqZWN00issMjNXTlNBcnJheaIyMNIr -LDU2Xk5TTXV0YWJsZUFycmF5ozUyMNM4OQ86OzxXTlNXaGl0ZVxOU0NvbG9yU3BhY2VEMCAwABADgAzS -Kyw+P1dOU0NvbG9yoj4w0issQUJXTlNJbWFnZaJBMAAIABEAGgAkACkAMgA3AEkATABRAFMAYgBoAHUA -fACLAJIAnwCmAK4AsACyALQAuQC7AL0AxADJANQA1gDYANoA3wDiAOQA5gDoAO8BBgEiASQBJhOWE5sT -phOvE8ITxhPRE9oT3xPnE+oT7xP+FAIUCRQRFB4UIxQlFCcULBQ0FDcUPBREAAAAAAAAAgEAAAAAAAAA -QwAAAAAAAAAAAAAAAAAAFEc +S2V5ZWRBcmNoaXZlctEICVRyb290gAGuCwwbHCEaJissMzY5P0JVJG51bGzXDQ4PEBESExQVFhcYGRpW +TlNTaXplViRjbGFzc1xOU0ltYWdlRmxhZ3NWTlNSZXBzW05TVGludENvbG9yV05TQ29sb3JeTlNSZXNp +emluZ01vZGWAAoANEiDDAACAA4AAgAsQAFZ7MSwgMX3SHQ4eIFpOUy5vYmplY3RzoR+ABIAK0h0OIiWi +IySABYAGgAnTDicoKSoaXxAUTlNUSUZGUmVwcmVzZW50YXRpb25fEBlOU0ludGVybmFsTGF5b3V0RGly +ZWN0aW9ugAiAB08REmxNTQAqAAAACgAAABABAAADAAAAAQABAAABAQADAAAAAQABAAABAgADAAAAAgAI +AAgBAwADAAAAAQABAAABBgADAAAAAQABAAABCgADAAAAAQABAAABEQAEAAAAAQAAAAgBEgADAAAAAQAB +AAABFQADAAAAAQACAAABFgADAAAAAQABAAABFwAEAAAAAQAAAAIBHAADAAAAAQABAAABKAADAAAAAQAC +AAABUgADAAAAAQABAAABUwADAAAAAgABAAGHcwAHAAARnAAAANAAAAAAAAARnGFwcGwCAAAAbW50ckdS +QVlYWVogB9wACAAXAA8ALgAPYWNzcEFQUEwAAAAAbm9uZQAAAAAAAAAAAAAAAAAAAAAAAPbWAAEAAAAA +0y1hcHBsAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFZGVzYwAA +AMAAAAB5ZHNjbQAAATwAAAgaY3BydAAACVgAAAAjd3RwdAAACXwAAAAUa1RSQwAACZAAAAgMZGVzYwAA +AAAAAAAfR2VuZXJpYyBHcmF5IEdhbW1hIDIuMiBQcm9maWxlAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAG1s +dWMAAAAAAAAAHwAAAAxza1NLAAAALgAAAYRkYURLAAAAOgAAAbJjYUVTAAAAOAAAAex2aVZOAAAAQAAA +AiRwdEJSAAAASgAAAmR1a1VBAAAALAAAAq5mckZVAAAAPgAAAtpodUhVAAAANAAAAxh6aFRXAAAAGgAA +A0xrb0tSAAAAIgAAA2ZuYk5PAAAAOgAAA4hjc0NaAAAAKAAAA8JoZUlMAAAAJAAAA+pyb1JPAAAAKgAA +BA5kZURFAAAATgAABDhpdElUAAAATgAABIZzdlNFAAAAOAAABNR6aENOAAAAGgAABQxqYUpQAAAAJgAA +BSZlbEdSAAAAKgAABUxwdFBPAAAAUgAABXZubE5MAAAAQAAABchlc0VTAAAATAAABgh0aFRIAAAAMgAA +BlR0clRSAAAAJAAABoZmaUZJAAAARgAABqpockhSAAAAPgAABvBwbFBMAAAASgAABy5hckVHAAAALAAA +B3hydVJVAAAAOgAAB6RlblVTAAAAPAAAB94AVgFhAGUAbwBiAGUAYwBuAOEAIABzAGkAdgDhACAAZwBh +AG0AYQAgADIALAAyAEcAZQBuAGUAcgBpAHMAawAgAGcAcgDlACAAMgAsADIAIABnAGEAbQBtAGEALQBw +AHIAbwBmAGkAbABHAGEAbQBtAGEAIABkAGUAIABnAHIAaQBzAG8AcwAgAGcAZQBuAOgAcgBpAGMAYQAg +ADIALgAyAEMepQB1ACAAaADsAG4AaAAgAE0A4AB1ACAAeADhAG0AIABDAGgAdQBuAGcAIABHAGEAbQBt +AGEAIAAyAC4AMgBQAGUAcgBmAGkAbAAgAEcAZQBuAOkAcgBpAGMAbwAgAGQAYQAgAEcAYQBtAGEAIABk +AGUAIABDAGkAbgB6AGEAcwAgADIALAAyBBcEMAQzBDAEOwRMBD0EMAAgAEcAcgBhAHkALQQzBDAEPAQw +ACAAMgAuADIAUAByAG8AZgBpAGwAIABnAOkAbgDpAHIAaQBxAHUAZQAgAGcAcgBpAHMAIABnAGEAbQBt +AGEAIAAyACwAMgDBAGwAdABhAGwA4QBuAG8AcwAgAHMAegD8AHIAawBlACAAZwBhAG0AbQBhACAAMgAu +ADKQGnUocHCWjlFJXqYAMgAuADKCcl9pY8+P8Md8vBgAINaMwMkAIKwQucgAIAAyAC4AMgAg1QS4XNMM +x3wARwBlAG4AZQByAGkAcwBrACAAZwByAOUAIABnAGEAbQBtAGEAIAAyACwAMgAtAHAAcgBvAGYAaQBs +AE8AYgBlAGMAbgDhACABYQBlAGQA4QAgAGcAYQBtAGEAIAAyAC4AMgXSBdAF3gXUACAF0AXkBdUF6AAg +BdsF3AXcBdkAIAAyAC4AMgBHAGEAbQBhACAAZwByAGkAIABnAGUAbgBlAHIAaQBjAQMAIAAyACwAMgBB +AGwAbABnAGUAbQBlAGkAbgBlAHMAIABHAHIAYQB1AHMAdAB1AGYAZQBuAC0AUAByAG8AZgBpAGwAIABH +AGEAbQBtAGEAIAAyACwAMgBQAHIAbwBmAGkAbABvACAAZwByAGkAZwBpAG8AIABnAGUAbgBlAHIAaQBj +AG8AIABkAGUAbABsAGEAIABnAGEAbQBtAGEAIAAyACwAMgBHAGUAbgBlAHIAaQBzAGsAIABnAHIA5QAg +ADIALAAyACAAZwBhAG0AbQBhAHAAcgBvAGYAaQBsZm6QGnBwXqZ8+2VwADIALgAyY8+P8GWHTvZOAIIs +MLAw7DCkMKww8zDeACAAMgAuADIAIDDXMO0w1TChMKQw6wOTA7UDvQO5A7oDzAAgA5MDugPBA7kAIAOT +A6wDvAO8A7EAIAAyAC4AMgBQAGUAcgBmAGkAbAAgAGcAZQBuAOkAcgBpAGMAbwAgAGQAZQAgAGMAaQBu +AHoAZQBuAHQAbwBzACAAZABhACAARwBhAG0AbQBhACAAMgAsADIAQQBsAGcAZQBtAGUAZQBuACAAZwBy +AGkAagBzACAAZwBhAG0AbQBhACAAMgAsADIALQBwAHIAbwBmAGkAZQBsAFAAZQByAGYAaQBsACAAZwBl +AG4A6QByAGkAYwBvACAAZABlACAAZwBhAG0AbQBhACAAZABlACAAZwByAGkAcwBlAHMAIAAyACwAMg4j +DjEOBw4qDjUOQQ4BDiEOIQ4yDkAOAQ4jDiIOTA4XDjEOSA4nDkQOGwAgADIALgAyAEcAZQBuAGUAbAAg +AEcAcgBpACAARwBhAG0AYQAgADIALAAyAFkAbABlAGkAbgBlAG4AIABoAGEAcgBtAGEAYQBuACAAZwBh +AG0AbQBhACAAMgAsADIAIAAtAHAAcgBvAGYAaQBpAGwAaQBHAGUAbgBlAHIAaQENAGsAaQAgAEcAcgBh +AHkAIABHAGEAbQBtAGEAIAAyAC4AMgAgAHAAcgBvAGYAaQBsAFUAbgBpAHcAZQByAHMAYQBsAG4AeQAg +AHAAcgBvAGYAaQBsACAAcwB6AGEAcgBvAVsAYwBpACAAZwBhAG0AbQBhACAAMgAsADIGOgYnBkUGJwAg +ADIALgAyACAGRAZIBkYAIAYxBkUGJwYvBkoAIAY5BicGRQQeBDEESQQwBE8AIARBBDUEQAQwBE8AIAQz +BDAEPAQ8BDAAIAAyACwAMgAtBD8EQAQ+BEQEOAQ7BEwARwBlAG4AZQByAGkAYwAgAEcAcgBhAHkAIABH +AGEAbQBtAGEAIAAyAC4AMgAgAFAAcgBvAGYAaQBsAGUAAHRleHQAAAAAQ29weXJpZ2h0IEFwcGxlIElu +Yy4sIDIwMTIAAFhZWiAAAAAAAADzUQABAAAAARbMY3VydgAAAAAAAAQAAAAABQAKAA8AFAAZAB4AIwAo +AC0AMgA3ADsAQABFAEoATwBUAFkAXgBjAGgAbQByAHcAfACBAIYAiwCQAJUAmgCfAKQAqQCuALIAtwC8 +AMEAxgDLANAA1QDbAOAA5QDrAPAA9gD7AQEBBwENARMBGQEfASUBKwEyATgBPgFFAUwBUgFZAWABZwFu +AXUBfAGDAYsBkgGaAaEBqQGxAbkBwQHJAdEB2QHhAekB8gH6AgMCDAIUAh0CJgIvAjgCQQJLAlQCXQJn +AnECegKEAo4CmAKiAqwCtgLBAssC1QLgAusC9QMAAwsDFgMhAy0DOANDA08DWgNmA3IDfgOKA5YDogOu +A7oDxwPTA+AD7AP5BAYEEwQgBC0EOwRIBFUEYwRxBH4EjASaBKgEtgTEBNME4QTwBP4FDQUcBSsFOgVJ +BVgFZwV3BYYFlgWmBbUFxQXVBeUF9gYGBhYGJwY3BkgGWQZqBnsGjAadBq8GwAbRBuMG9QcHBxkHKwc9 +B08HYQd0B4YHmQesB78H0gflB/gICwgfCDIIRghaCG4IggiWCKoIvgjSCOcI+wkQCSUJOglPCWQJeQmP +CaQJugnPCeUJ+woRCicKPQpUCmoKgQqYCq4KxQrcCvMLCwsiCzkLUQtpC4ALmAuwC8gL4Qv5DBIMKgxD +DFwMdQyODKcMwAzZDPMNDQ0mDUANWg10DY4NqQ3DDd4N+A4TDi4OSQ5kDn8Omw62DtIO7g8JDyUPQQ9e +D3oPlg+zD88P7BAJECYQQxBhEH4QmxC5ENcQ9RETETERTxFtEYwRqhHJEegSBxImEkUSZBKEEqMSwxLj +EwMTIxNDE2MTgxOkE8UT5RQGFCcUSRRqFIsUrRTOFPAVEhU0FVYVeBWbFb0V4BYDFiYWSRZsFo8WshbW +FvoXHRdBF2UXiReuF9IX9xgbGEAYZRiKGK8Y1Rj6GSAZRRlrGZEZtxndGgQaKhpRGncanhrFGuwbFBs7 +G2MbihuyG9ocAhwqHFIcexyjHMwc9R0eHUcdcB2ZHcMd7B4WHkAeah6UHr4e6R8THz4faR+UH78f6iAV +IEEgbCCYIMQg8CEcIUghdSGhIc4h+yInIlUigiKvIt0jCiM4I2YjlCPCI/AkHyRNJHwkqyTaJQklOCVo +JZclxyX3JicmVyaHJrcm6CcYJ0kneierJ9woDSg/KHEooijUKQYpOClrKZ0p0CoCKjUqaCqbKs8rAis2 +K2krnSvRLAUsOSxuLKIs1y0MLUEtdi2rLeEuFi5MLoIuty7uLyQvWi+RL8cv/jA1MGwwpDDbMRIxSjGC +Mbox8jIqMmMymzLUMw0zRjN/M7gz8TQrNGU0njTYNRM1TTWHNcI1/TY3NnI2rjbpNyQ3YDecN9c4FDhQ +OIw4yDkFOUI5fzm8Ofk6Njp0OrI67zstO2s7qjvoPCc8ZTykPOM9Ij1hPaE94D4gPmA+oD7gPyE/YT+i +P+JAI0BkQKZA50EpQWpBrEHuQjBCckK1QvdDOkN9Q8BEA0RHRIpEzkUSRVVFmkXeRiJGZ0arRvBHNUd7 +R8BIBUhLSJFI10kdSWNJqUnwSjdKfUrESwxLU0uaS+JMKkxyTLpNAk1KTZNN3E4lTm5Ot08AT0lPk0/d +UCdQcVC7UQZRUFGbUeZSMVJ8UsdTE1NfU6pT9lRCVI9U21UoVXVVwlYPVlxWqVb3V0RXklfgWC9YfVjL +WRpZaVm4WgdaVlqmWvVbRVuVW+VcNVyGXNZdJ114XcleGl5sXr1fD19hX7NgBWBXYKpg/GFPYaJh9WJJ +Ypxi8GNDY5dj62RAZJRk6WU9ZZJl52Y9ZpJm6Gc9Z5Nn6Wg/aJZo7GlDaZpp8WpIap9q92tPa6dr/2xX +bK9tCG1gbbluEm5rbsRvHm94b9FwK3CGcOBxOnGVcfByS3KmcwFzXXO4dBR0cHTMdSh1hXXhdj52m3b4 +d1Z3s3gReG54zHkqeYl553pGeqV7BHtje8J8IXyBfOF9QX2hfgF+Yn7CfyN/hH/lgEeAqIEKgWuBzYIw +gpKC9INXg7qEHYSAhOOFR4Wrhg6GcobXhzuHn4gEiGmIzokziZmJ/opkisqLMIuWi/yMY4zKjTGNmI3/ +jmaOzo82j56QBpBukNaRP5GokhGSepLjk02TtpQglIqU9JVflcmWNJaflwqXdZfgmEyYuJkkmZCZ/Jpo +mtWbQpuvnByciZz3nWSd0p5Anq6fHZ+Ln/qgaaDYoUehtqImopajBqN2o+akVqTHpTilqaYapoum/adu +p+CoUqjEqTepqaocqo+rAqt1q+msXKzQrUStuK4trqGvFq+LsACwdbDqsWCx1rJLssKzOLOutCW0nLUT +tYq2AbZ5tvC3aLfguFm40blKucK6O7q1uy67p7whvJu9Fb2Pvgq+hL7/v3q/9cBwwOzBZ8Hjwl/C28NY +w9TEUcTOxUvFyMZGxsPHQce/yD3IvMk6ybnKOMq3yzbLtsw1zLXNNc21zjbOts83z7jQOdC60TzRvtI/ +0sHTRNPG1EnUy9VO1dHWVdbY11zX4Nhk2OjZbNnx2nba+9uA3AXcit0Q3ZbeHN6i3ynfr+A24L3hROHM +4lPi2+Nj4+vkc+T85YTmDeaW5x/nqegy6LzpRunQ6lvq5etw6/vshu0R7ZzuKO6070DvzPBY8OXxcvH/ +8ozzGfOn9DT0wvVQ9d72bfb794r4Gfio+Tj5x/pX+uf7d/wH/Jj9Kf26/kv+3P9t///SLS4vMFokY2xh +c3NuYW1lWCRjbGFzc2VzXxAQTlNCaXRtYXBJbWFnZVJlcKMvMTJaTlNJbWFnZVJlcFhOU09iamVjdNIt +LjQ1V05TQXJyYXmiNDLSLS43OF5OU011dGFibGVBcnJheaM3NDLTOjsOPD0+V05TV2hpdGVcTlNDb2xv +clNwYWNlRDAgMAAQA4AM0i0uQEFXTlNDb2xvcqJAMtItLkNEV05TSW1hZ2WiQzIACAARABoAJAApADIA +NwBJAEwAUQBTAGIAaAB3AH4AhQCSAJkApQCtALwAvgDAAMUAxwDJAMsAzQDUANkA5ADmAOgA6gDvAPIA +9AD2APgA/wEWATIBNAE2E6YTqxO2E78T0hPWE+ET6hPvE/cT+hP/FA4UEhQZFCEULhQzFDUUNxQ8FEQU +RxRMFFQAAAAAAAACAQAAAAAAAABFAAAAAAAAAAAAAAAAAAAUVw diff --git a/i18n/src/main/resources/ta_IN.lproj/Command.xib b/i18n/src/main/resources/ta_IN.lproj/Command.xib index 8c4efc5e80..df5aedd02e 100644 --- a/i18n/src/main/resources/ta_IN.lproj/Command.xib +++ b/i18n/src/main/resources/ta_IN.lproj/Command.xib @@ -1,7 +1,7 @@ - + - + diff --git a/i18n/src/main/resources/ta_IN.lproj/Connection.xib b/i18n/src/main/resources/ta_IN.lproj/Connection.xib index 8dacde746e..6d076c3a36 100644 --- a/i18n/src/main/resources/ta_IN.lproj/Connection.xib +++ b/i18n/src/main/resources/ta_IN.lproj/Connection.xib @@ -1,7 +1,7 @@ - + - + @@ -76,7 +76,7 @@ - + diff --git a/i18n/src/main/resources/ta_IN.lproj/Info.xib b/i18n/src/main/resources/ta_IN.lproj/Info.xib index 1a1c19735c..c1e2826073 100644 --- a/i18n/src/main/resources/ta_IN.lproj/Info.xib +++ b/i18n/src/main/resources/ta_IN.lproj/Info.xib @@ -1,7 +1,7 @@ - + - + @@ -89,7 +89,7 @@ - + @@ -1017,21 +1017,21 @@ - + - + - + @@ -1121,21 +1121,21 @@ - + - + - + @@ -1250,27 +1250,27 @@ diff --git a/i18n/src/main/resources/ta_IN.lproj/Login.strings b/i18n/src/main/resources/ta_IN.lproj/Login.strings index 3bfc067eea..feb8e01825 100644 Binary files a/i18n/src/main/resources/ta_IN.lproj/Login.strings and b/i18n/src/main/resources/ta_IN.lproj/Login.strings differ diff --git a/i18n/src/main/resources/ta_IN.lproj/Main.xib b/i18n/src/main/resources/ta_IN.lproj/Main.xib index 428a5aac11..c4fa61c428 100644 --- a/i18n/src/main/resources/ta_IN.lproj/Main.xib +++ b/i18n/src/main/resources/ta_IN.lproj/Main.xib @@ -1,7 +1,7 @@ - + - + @@ -94,7 +94,7 @@ - + @@ -159,19 +159,19 @@ - + - + - + @@ -321,12 +321,12 @@ CA - + - + @@ -566,6 +566,10 @@ CA - + + + + + diff --git a/i18n/src/main/resources/ta_IN.lproj/Preferences.xib b/i18n/src/main/resources/ta_IN.lproj/Preferences.xib index 1d6525e0dc..319bc6d65d 100644 --- a/i18n/src/main/resources/ta_IN.lproj/Preferences.xib +++ b/i18n/src/main/resources/ta_IN.lproj/Preferences.xib @@ -1,7 +1,7 @@ - + - + @@ -266,7 +266,7 @@ - + @@ -307,7 +307,7 @@ - + @@ -539,7 +539,7 @@ - + @@ -579,7 +579,7 @@ - + @@ -614,7 +614,7 @@ - + @@ -643,7 +643,7 @@ - + @@ -1219,7 +1219,7 @@ - + @@ -1251,7 +1251,7 @@ - + @@ -1291,7 +1291,7 @@ - + @@ -1364,7 +1364,7 @@ - + @@ -1559,7 +1559,7 @@ - + @@ -1619,7 +1619,7 @@ - + @@ -1644,7 +1644,7 @@ - + @@ -1686,7 +1686,7 @@ - + @@ -1714,7 +1714,7 @@ - + @@ -1742,7 +1742,7 @@ - + @@ -1771,7 +1771,7 @@ - + @@ -1806,7 +1806,7 @@ - + @@ -1890,7 +1890,7 @@ - + @@ -1949,7 +1949,7 @@ - + @@ -1977,7 +1977,7 @@ - + @@ -2006,7 +2006,7 @@ - + diff --git a/i18n/src/main/resources/ta_IN.lproj/Profiles.xib b/i18n/src/main/resources/ta_IN.lproj/Profiles.xib index 59e769979c..1f209765ff 100644 --- a/i18n/src/main/resources/ta_IN.lproj/Profiles.xib +++ b/i18n/src/main/resources/ta_IN.lproj/Profiles.xib @@ -1,7 +1,7 @@ - + - + diff --git a/i18n/src/main/resources/ta_IN.lproj/Prompt.xib b/i18n/src/main/resources/ta_IN.lproj/Prompt.xib index 78e812ff58..4efa6355af 100644 --- a/i18n/src/main/resources/ta_IN.lproj/Prompt.xib +++ b/i18n/src/main/resources/ta_IN.lproj/Prompt.xib @@ -1,7 +1,7 @@ - + - + @@ -95,7 +95,7 @@ Gw - + diff --git a/i18n/src/main/resources/ta_IN.lproj/S3.strings b/i18n/src/main/resources/ta_IN.lproj/S3.strings index a13342ff0c..4b3a5d3ffe 100644 Binary files a/i18n/src/main/resources/ta_IN.lproj/S3.strings and b/i18n/src/main/resources/ta_IN.lproj/S3.strings differ diff --git a/i18n/src/main/resources/ta_IN.lproj/Updater.strings b/i18n/src/main/resources/ta_IN.lproj/Updater.strings index d0ce0ed2ad..0f84daee5d 100644 Binary files a/i18n/src/main/resources/ta_IN.lproj/Updater.strings and b/i18n/src/main/resources/ta_IN.lproj/Updater.strings differ diff --git a/i18n/src/main/resources/th.lproj/Bookmark.xib b/i18n/src/main/resources/th.lproj/Bookmark.xib index fce1b22565..05b3f23578 100644 --- a/i18n/src/main/resources/th.lproj/Bookmark.xib +++ b/i18n/src/main/resources/th.lproj/Bookmark.xib @@ -1,7 +1,7 @@ - + - + @@ -92,7 +92,7 @@ - + @@ -489,95 +489,95 @@ YnBsaXN0MDDUAQIDBAUGBwpYJHZlcnNpb25ZJGFyY2hpdmVyVCR0b3BYJG9iamVjdHMSAAGGoF8QD05T -S2V5ZWRBcmNoaXZlctEICVRyb290gAGuCwwZGh8UJCkqMTQ3PUBVJG51bGzWDQ4PEBESExQVFhcYVk5T -U2l6ZV5OU1Jlc2l6aW5nTW9kZVYkY2xhc3NcTlNJbWFnZUZsYWdzVk5TUmVwc1dOU0NvbG9ygAIQAIAN -EiDDAACAA4ALVnsxLCAxfdIbDxweWk5TLm9iamVjdHOhHYAEgArSGw8gI6IhIoAFgAaACdMPJSYnKBRf -EBROU1RJRkZSZXByZXNlbnRhdGlvbl8QGU5TSW50ZXJuYWxMYXlvdXREaXJlY3Rpb26ACIAHTxESbE1N -ACoAAAAKAAAAEAEAAAMAAAABAAEAAAEBAAMAAAABAAEAAAECAAMAAAACAAgACAEDAAMAAAABAAEAAAEG -AAMAAAABAAEAAAEKAAMAAAABAAEAAAERAAQAAAABAAAACAESAAMAAAABAAEAAAEVAAMAAAABAAIAAAEW -AAMAAAABAAEAAAEXAAQAAAABAAAAAgEcAAMAAAABAAEAAAEoAAMAAAABAAIAAAFSAAMAAAABAAEAAAFT -AAMAAAACAAEAAYdzAAcAABGcAAAA0AAAAAAAABGcYXBwbAIAAABtbnRyR1JBWVhZWiAH3AAIABcADwAu -AA9hY3NwQVBQTAAAAABub25lAAAAAAAAAAAAAAAAAAAAAAAA9tYAAQAAAADTLWFwcGwAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAVkZXNjAAAAwAAAAHlkc2NtAAABPAAA -CBpjcHJ0AAAJWAAAACN3dHB0AAAJfAAAABRrVFJDAAAJkAAACAxkZXNjAAAAAAAAAB9HZW5lcmljIEdy -YXkgR2FtbWEgMi4yIFByb2ZpbGUAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAbWx1YwAAAAAAAAAfAAAADHNr -U0sAAAAuAAABhGRhREsAAAA6AAABsmNhRVMAAAA4AAAB7HZpVk4AAABAAAACJHB0QlIAAABKAAACZHVr -VUEAAAAsAAACrmZyRlUAAAA+AAAC2mh1SFUAAAA0AAADGHpoVFcAAAAaAAADTGtvS1IAAAAiAAADZm5i -Tk8AAAA6AAADiGNzQ1oAAAAoAAADwmhlSUwAAAAkAAAD6nJvUk8AAAAqAAAEDmRlREUAAABOAAAEOGl0 -SVQAAABOAAAEhnN2U0UAAAA4AAAE1HpoQ04AAAAaAAAFDGphSlAAAAAmAAAFJmVsR1IAAAAqAAAFTHB0 -UE8AAABSAAAFdm5sTkwAAABAAAAFyGVzRVMAAABMAAAGCHRoVEgAAAAyAAAGVHRyVFIAAAAkAAAGhmZp -RkkAAABGAAAGqmhySFIAAAA+AAAG8HBsUEwAAABKAAAHLmFyRUcAAAAsAAAHeHJ1UlUAAAA6AAAHpGVu -VVMAAAA8AAAH3gBWAWEAZQBvAGIAZQBjAG4A4QAgAHMAaQB2AOEAIABnAGEAbQBhACAAMgAsADIARwBl -AG4AZQByAGkAcwBrACAAZwByAOUAIAAyACwAMgAgAGcAYQBtAG0AYQAtAHAAcgBvAGYAaQBsAEcAYQBt -AG0AYQAgAGQAZQAgAGcAcgBpAHMAbwBzACAAZwBlAG4A6AByAGkAYwBhACAAMgAuADIAQx6lAHUAIABo -AOwAbgBoACAATQDgAHUAIAB4AOEAbQAgAEMAaAB1AG4AZwAgAEcAYQBtAG0AYQAgADIALgAyAFAAZQBy -AGYAaQBsACAARwBlAG4A6QByAGkAYwBvACAAZABhACAARwBhAG0AYQAgAGQAZQAgAEMAaQBuAHoAYQBz -ACAAMgAsADIEFwQwBDMEMAQ7BEwEPQQwACAARwByAGEAeQAtBDMEMAQ8BDAAIAAyAC4AMgBQAHIAbwBm -AGkAbAAgAGcA6QBuAOkAcgBpAHEAdQBlACAAZwByAGkAcwAgAGcAYQBtAG0AYQAgADIALAAyAMEAbAB0 -AGEAbADhAG4AbwBzACAAcwB6APwAcgBrAGUAIABnAGEAbQBtAGEAIAAyAC4AMpAadShwcJaOUUlepgAy -AC4AMoJyX2ljz4/wx3y8GAAg1ozAyQAgrBC5yAAgADIALgAyACDVBLhc0wzHfABHAGUAbgBlAHIAaQBz -AGsAIABnAHIA5QAgAGcAYQBtAG0AYQAgADIALAAyAC0AcAByAG8AZgBpAGwATwBiAGUAYwBuAOEAIAFh -AGUAZADhACAAZwBhAG0AYQAgADIALgAyBdIF0AXeBdQAIAXQBeQF1QXoACAF2wXcBdwF2QAgADIALgAy -AEcAYQBtAGEAIABnAHIAaQAgAGcAZQBuAGUAcgBpAGMBAwAgADIALAAyAEEAbABsAGcAZQBtAGUAaQBu -AGUAcwAgAEcAcgBhAHUAcwB0AHUAZgBlAG4ALQBQAHIAbwBmAGkAbAAgAEcAYQBtAG0AYQAgADIALAAy -AFAAcgBvAGYAaQBsAG8AIABnAHIAaQBnAGkAbwAgAGcAZQBuAGUAcgBpAGMAbwAgAGQAZQBsAGwAYQAg -AGcAYQBtAG0AYQAgADIALAAyAEcAZQBuAGUAcgBpAHMAawAgAGcAcgDlACAAMgAsADIAIABnAGEAbQBt -AGEAcAByAG8AZgBpAGxmbpAacHBepnz7ZXAAMgAuADJjz4/wZYdO9k4AgiwwsDDsMKQwrDDzMN4AIAAy -AC4AMgAgMNcw7TDVMKEwpDDrA5MDtQO9A7kDugPMACADkwO6A8EDuQAgA5MDrAO8A7wDsQAgADIALgAy -AFAAZQByAGYAaQBsACAAZwBlAG4A6QByAGkAYwBvACAAZABlACAAYwBpAG4AegBlAG4AdABvAHMAIABk -AGEAIABHAGEAbQBtAGEAIAAyACwAMgBBAGwAZwBlAG0AZQBlAG4AIABnAHIAaQBqAHMAIABnAGEAbQBt -AGEAIAAyACwAMgAtAHAAcgBvAGYAaQBlAGwAUABlAHIAZgBpAGwAIABnAGUAbgDpAHIAaQBjAG8AIABk -AGUAIABnAGEAbQBtAGEAIABkAGUAIABnAHIAaQBzAGUAcwAgADIALAAyDiMOMQ4HDioONQ5BDgEOIQ4h -DjIOQA4BDiMOIg5MDhcOMQ5IDicORA4bACAAMgAuADIARwBlAG4AZQBsACAARwByAGkAIABHAGEAbQBh -ACAAMgAsADIAWQBsAGUAaQBuAGUAbgAgAGgAYQByAG0AYQBhAG4AIABnAGEAbQBtAGEAIAAyACwAMgAg -AC0AcAByAG8AZgBpAGkAbABpAEcAZQBuAGUAcgBpAQ0AawBpACAARwByAGEAeQAgAEcAYQBtAG0AYQAg -ADIALgAyACAAcAByAG8AZgBpAGwAVQBuAGkAdwBlAHIAcwBhAGwAbgB5ACAAcAByAG8AZgBpAGwAIABz -AHoAYQByAG8BWwBjAGkAIABnAGEAbQBtAGEAIAAyACwAMgY6BicGRQYnACAAMgAuADIAIAZEBkgGRgAg -BjEGRQYnBi8GSgAgBjkGJwZFBB4EMQRJBDAETwAgBEEENQRABDAETwAgBDMEMAQ8BDwEMAAgADIALAAy -AC0EPwRABD4ERAQ4BDsETABHAGUAbgBlAHIAaQBjACAARwByAGEAeQAgAEcAYQBtAG0AYQAgADIALgAy -ACAAUAByAG8AZgBpAGwAZQAAdGV4dAAAAABDb3B5cmlnaHQgQXBwbGUgSW5jLiwgMjAxMgAAWFlaIAAA -AAAAAPNRAAEAAAABFsxjdXJ2AAAAAAAABAAAAAAFAAoADwAUABkAHgAjACgALQAyADcAOwBAAEUASgBP -AFQAWQBeAGMAaABtAHIAdwB8AIEAhgCLAJAAlQCaAJ8ApACpAK4AsgC3ALwAwQDGAMsA0ADVANsA4ADl -AOsA8AD2APsBAQEHAQ0BEwEZAR8BJQErATIBOAE+AUUBTAFSAVkBYAFnAW4BdQF8AYMBiwGSAZoBoQGp -AbEBuQHBAckB0QHZAeEB6QHyAfoCAwIMAhQCHQImAi8COAJBAksCVAJdAmcCcQJ6AoQCjgKYAqICrAK2 -AsECywLVAuAC6wL1AwADCwMWAyEDLQM4A0MDTwNaA2YDcgN+A4oDlgOiA64DugPHA9MD4APsA/kEBgQT -BCAELQQ7BEgEVQRjBHEEfgSMBJoEqAS2BMQE0wThBPAE/gUNBRwFKwU6BUkFWAVnBXcFhgWWBaYFtQXF -BdUF5QX2BgYGFgYnBjcGSAZZBmoGewaMBp0GrwbABtEG4wb1BwcHGQcrBz0HTwdhB3QHhgeZB6wHvwfS -B+UH+AgLCB8IMghGCFoIbgiCCJYIqgi+CNII5wj7CRAJJQk6CU8JZAl5CY8JpAm6Cc8J5Qn7ChEKJwo9 -ClQKagqBCpgKrgrFCtwK8wsLCyILOQtRC2kLgAuYC7ALyAvhC/kMEgwqDEMMXAx1DI4MpwzADNkM8w0N -DSYNQA1aDXQNjg2pDcMN3g34DhMOLg5JDmQOfw6bDrYO0g7uDwkPJQ9BD14Peg+WD7MPzw/sEAkQJhBD -EGEQfhCbELkQ1xD1ERMRMRFPEW0RjBGqEckR6BIHEiYSRRJkEoQSoxLDEuMTAxMjE0MTYxODE6QTxRPl -FAYUJxRJFGoUixStFM4U8BUSFTQVVhV4FZsVvRXgFgMWJhZJFmwWjxayFtYW+hcdF0EXZReJF64X0hf3 -GBsYQBhlGIoYrxjVGPoZIBlFGWsZkRm3Gd0aBBoqGlEadxqeGsUa7BsUGzsbYxuKG7Ib2hwCHCocUhx7 -HKMczBz1HR4dRx1wHZkdwx3sHhYeQB5qHpQevh7pHxMfPh9pH5Qfvx/qIBUgQSBsIJggxCDwIRwhSCF1 -IaEhziH7IiciVSKCIq8i3SMKIzgjZiOUI8Ij8CQfJE0kfCSrJNolCSU4JWgllyXHJfcmJyZXJocmtybo -JxgnSSd6J6sn3CgNKD8ocSiiKNQpBik4KWspnSnQKgIqNSpoKpsqzysCKzYraSudK9EsBSw5LG4soizX -LQwtQS12Last4S4WLkwugi63Lu4vJC9aL5Evxy/+MDUwbDCkMNsxEjFKMYIxujHyMioyYzKbMtQzDTNG -M38zuDPxNCs0ZTSeNNg1EzVNNYc1wjX9Njc2cjauNuk3JDdgN5w31zgUOFA4jDjIOQU5Qjl/Obw5+To2 -OnQ6sjrvOy07azuqO+g8JzxlPKQ84z0iPWE9oT3gPiA+YD6gPuA/IT9hP6I/4kAjQGRApkDnQSlBakGs -Qe5CMEJyQrVC90M6Q31DwEQDREdEikTORRJFVUWaRd5GIkZnRqtG8Ec1R3tHwEgFSEtIkUjXSR1JY0mp -SfBKN0p9SsRLDEtTS5pL4kwqTHJMuk0CTUpNk03cTiVObk63TwBPSU+TT91QJ1BxULtRBlFQUZtR5lIx -UnxSx1MTU19TqlP2VEJUj1TbVShVdVXCVg9WXFapVvdXRFeSV+BYL1h9WMtZGllpWbhaB1pWWqZa9VtF -W5Vb5Vw1XIZc1l0nXXhdyV4aXmxevV8PX2Ffs2AFYFdgqmD8YU9homH1YklinGLwY0Njl2PrZEBklGTp -ZT1lkmXnZj1mkmboZz1nk2fpaD9olmjsaUNpmmnxakhqn2r3a09rp2v/bFdsr20IbWBtuW4SbmtuxG8e -b3hv0XArcIZw4HE6cZVx8HJLcqZzAXNdc7h0FHRwdMx1KHWFdeF2Pnabdvh3VnezeBF4bnjMeSp5iXnn -ekZ6pXsEe2N7wnwhfIF84X1BfaF+AX5ifsJ/I3+Ef+WAR4CogQqBa4HNgjCCkoL0g1eDuoQdhICE44VH -hauGDoZyhteHO4efiASIaYjOiTOJmYn+imSKyoswi5aL/IxjjMqNMY2Yjf+OZo7OjzaPnpAGkG6Q1pE/ -kaiSEZJ6kuOTTZO2lCCUipT0lV+VyZY0lp+XCpd1l+CYTJi4mSSZkJn8mmia1ZtCm6+cHJyJnPedZJ3S -nkCerp8dn4uf+qBpoNihR6G2oiailqMGo3aj5qRWpMelOKWpphqmi6b9p26n4KhSqMSpN6mpqhyqj6sC -q3Wr6axcrNCtRK24ri2uoa8Wr4uwALB1sOqxYLHWskuywrM4s660JbSctRO1irYBtnm28Ldot+C4WbjR -uUq5wro7urW7LrunvCG8m70VvY++Cr6Evv+/er/1wHDA7MFnwePCX8Lbw1jD1MRRxM7FS8XIxkbGw8dB -x7/IPci8yTrJuco4yrfLNsu2zDXMtc01zbXONs62zzfPuNA50LrRPNG+0j/SwdNE08bUSdTL1U7V0dZV -1tjXXNfg2GTY6Nls2fHadtr724DcBdyK3RDdlt4c3qLfKd+v4DbgveFE4cziU+Lb42Pj6+Rz5PzlhOYN -5pbnH+ep6DLovOlG6dDqW+rl63Dr++yG7RHtnO4o7rTvQO/M8Fjw5fFy8f/yjPMZ86f0NPTC9VD13vZt -9vv3ivgZ+Kj5OPnH+lf65/t3/Af8mP0p/br+S/7c/23//9IrLC0uWiRjbGFzc25hbWVYJGNsYXNzZXNf -EBBOU0JpdG1hcEltYWdlUmVwoy0vMFpOU0ltYWdlUmVwWE5TT2JqZWN00issMjNXTlNBcnJheaIyMNIr -LDU2Xk5TTXV0YWJsZUFycmF5ozUyMNM4OQ86OzxXTlNXaGl0ZVxOU0NvbG9yU3BhY2VEMCAwABADgAzS -Kyw+P1dOU0NvbG9yoj4w0issQUJXTlNJbWFnZaJBMAAIABEAGgAkACkAMgA3AEkATABRAFMAYgBoAHUA -fACLAJIAnwCmAK4AsACyALQAuQC7AL0AxADJANQA1gDYANoA3wDiAOQA5gDoAO8BBgEiASQBJhOWE5sT -phOvE8ITxhPRE9oT3xPnE+oT7xP+FAIUCRQRFB4UIxQlFCcULBQ0FDcUPBREAAAAAAAAAgEAAAAAAAAA -QwAAAAAAAAAAAAAAAAAAFEc +S2V5ZWRBcmNoaXZlctEICVRyb290gAGuCwwbHCEaJissMzY5P0JVJG51bGzXDQ4PEBESExQVFhcYGRpW +TlNTaXplViRjbGFzc1xOU0ltYWdlRmxhZ3NWTlNSZXBzW05TVGludENvbG9yV05TQ29sb3JeTlNSZXNp +emluZ01vZGWAAoANEiDDAACAA4AAgAsQAFZ7MSwgMX3SHQ4eIFpOUy5vYmplY3RzoR+ABIAK0h0OIiWi +IySABYAGgAnTDicoKSoaXxAUTlNUSUZGUmVwcmVzZW50YXRpb25fEBlOU0ludGVybmFsTGF5b3V0RGly +ZWN0aW9ugAiAB08REmxNTQAqAAAACgAAABABAAADAAAAAQABAAABAQADAAAAAQABAAABAgADAAAAAgAI +AAgBAwADAAAAAQABAAABBgADAAAAAQABAAABCgADAAAAAQABAAABEQAEAAAAAQAAAAgBEgADAAAAAQAB +AAABFQADAAAAAQACAAABFgADAAAAAQABAAABFwAEAAAAAQAAAAIBHAADAAAAAQABAAABKAADAAAAAQAC +AAABUgADAAAAAQABAAABUwADAAAAAgABAAGHcwAHAAARnAAAANAAAAAAAAARnGFwcGwCAAAAbW50ckdS +QVlYWVogB9wACAAXAA8ALgAPYWNzcEFQUEwAAAAAbm9uZQAAAAAAAAAAAAAAAAAAAAAAAPbWAAEAAAAA +0y1hcHBsAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFZGVzYwAA +AMAAAAB5ZHNjbQAAATwAAAgaY3BydAAACVgAAAAjd3RwdAAACXwAAAAUa1RSQwAACZAAAAgMZGVzYwAA +AAAAAAAfR2VuZXJpYyBHcmF5IEdhbW1hIDIuMiBQcm9maWxlAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAG1s +dWMAAAAAAAAAHwAAAAxza1NLAAAALgAAAYRkYURLAAAAOgAAAbJjYUVTAAAAOAAAAex2aVZOAAAAQAAA +AiRwdEJSAAAASgAAAmR1a1VBAAAALAAAAq5mckZVAAAAPgAAAtpodUhVAAAANAAAAxh6aFRXAAAAGgAA +A0xrb0tSAAAAIgAAA2ZuYk5PAAAAOgAAA4hjc0NaAAAAKAAAA8JoZUlMAAAAJAAAA+pyb1JPAAAAKgAA +BA5kZURFAAAATgAABDhpdElUAAAATgAABIZzdlNFAAAAOAAABNR6aENOAAAAGgAABQxqYUpQAAAAJgAA +BSZlbEdSAAAAKgAABUxwdFBPAAAAUgAABXZubE5MAAAAQAAABchlc0VTAAAATAAABgh0aFRIAAAAMgAA +BlR0clRSAAAAJAAABoZmaUZJAAAARgAABqpockhSAAAAPgAABvBwbFBMAAAASgAABy5hckVHAAAALAAA +B3hydVJVAAAAOgAAB6RlblVTAAAAPAAAB94AVgFhAGUAbwBiAGUAYwBuAOEAIABzAGkAdgDhACAAZwBh +AG0AYQAgADIALAAyAEcAZQBuAGUAcgBpAHMAawAgAGcAcgDlACAAMgAsADIAIABnAGEAbQBtAGEALQBw +AHIAbwBmAGkAbABHAGEAbQBtAGEAIABkAGUAIABnAHIAaQBzAG8AcwAgAGcAZQBuAOgAcgBpAGMAYQAg +ADIALgAyAEMepQB1ACAAaADsAG4AaAAgAE0A4AB1ACAAeADhAG0AIABDAGgAdQBuAGcAIABHAGEAbQBt +AGEAIAAyAC4AMgBQAGUAcgBmAGkAbAAgAEcAZQBuAOkAcgBpAGMAbwAgAGQAYQAgAEcAYQBtAGEAIABk +AGUAIABDAGkAbgB6AGEAcwAgADIALAAyBBcEMAQzBDAEOwRMBD0EMAAgAEcAcgBhAHkALQQzBDAEPAQw +ACAAMgAuADIAUAByAG8AZgBpAGwAIABnAOkAbgDpAHIAaQBxAHUAZQAgAGcAcgBpAHMAIABnAGEAbQBt +AGEAIAAyACwAMgDBAGwAdABhAGwA4QBuAG8AcwAgAHMAegD8AHIAawBlACAAZwBhAG0AbQBhACAAMgAu +ADKQGnUocHCWjlFJXqYAMgAuADKCcl9pY8+P8Md8vBgAINaMwMkAIKwQucgAIAAyAC4AMgAg1QS4XNMM +x3wARwBlAG4AZQByAGkAcwBrACAAZwByAOUAIABnAGEAbQBtAGEAIAAyACwAMgAtAHAAcgBvAGYAaQBs +AE8AYgBlAGMAbgDhACABYQBlAGQA4QAgAGcAYQBtAGEAIAAyAC4AMgXSBdAF3gXUACAF0AXkBdUF6AAg +BdsF3AXcBdkAIAAyAC4AMgBHAGEAbQBhACAAZwByAGkAIABnAGUAbgBlAHIAaQBjAQMAIAAyACwAMgBB +AGwAbABnAGUAbQBlAGkAbgBlAHMAIABHAHIAYQB1AHMAdAB1AGYAZQBuAC0AUAByAG8AZgBpAGwAIABH +AGEAbQBtAGEAIAAyACwAMgBQAHIAbwBmAGkAbABvACAAZwByAGkAZwBpAG8AIABnAGUAbgBlAHIAaQBj +AG8AIABkAGUAbABsAGEAIABnAGEAbQBtAGEAIAAyACwAMgBHAGUAbgBlAHIAaQBzAGsAIABnAHIA5QAg +ADIALAAyACAAZwBhAG0AbQBhAHAAcgBvAGYAaQBsZm6QGnBwXqZ8+2VwADIALgAyY8+P8GWHTvZOAIIs +MLAw7DCkMKww8zDeACAAMgAuADIAIDDXMO0w1TChMKQw6wOTA7UDvQO5A7oDzAAgA5MDugPBA7kAIAOT +A6wDvAO8A7EAIAAyAC4AMgBQAGUAcgBmAGkAbAAgAGcAZQBuAOkAcgBpAGMAbwAgAGQAZQAgAGMAaQBu +AHoAZQBuAHQAbwBzACAAZABhACAARwBhAG0AbQBhACAAMgAsADIAQQBsAGcAZQBtAGUAZQBuACAAZwBy +AGkAagBzACAAZwBhAG0AbQBhACAAMgAsADIALQBwAHIAbwBmAGkAZQBsAFAAZQByAGYAaQBsACAAZwBl +AG4A6QByAGkAYwBvACAAZABlACAAZwBhAG0AbQBhACAAZABlACAAZwByAGkAcwBlAHMAIAAyACwAMg4j +DjEOBw4qDjUOQQ4BDiEOIQ4yDkAOAQ4jDiIOTA4XDjEOSA4nDkQOGwAgADIALgAyAEcAZQBuAGUAbAAg +AEcAcgBpACAARwBhAG0AYQAgADIALAAyAFkAbABlAGkAbgBlAG4AIABoAGEAcgBtAGEAYQBuACAAZwBh +AG0AbQBhACAAMgAsADIAIAAtAHAAcgBvAGYAaQBpAGwAaQBHAGUAbgBlAHIAaQENAGsAaQAgAEcAcgBh +AHkAIABHAGEAbQBtAGEAIAAyAC4AMgAgAHAAcgBvAGYAaQBsAFUAbgBpAHcAZQByAHMAYQBsAG4AeQAg +AHAAcgBvAGYAaQBsACAAcwB6AGEAcgBvAVsAYwBpACAAZwBhAG0AbQBhACAAMgAsADIGOgYnBkUGJwAg +ADIALgAyACAGRAZIBkYAIAYxBkUGJwYvBkoAIAY5BicGRQQeBDEESQQwBE8AIARBBDUEQAQwBE8AIAQz +BDAEPAQ8BDAAIAAyACwAMgAtBD8EQAQ+BEQEOAQ7BEwARwBlAG4AZQByAGkAYwAgAEcAcgBhAHkAIABH +AGEAbQBtAGEAIAAyAC4AMgAgAFAAcgBvAGYAaQBsAGUAAHRleHQAAAAAQ29weXJpZ2h0IEFwcGxlIElu +Yy4sIDIwMTIAAFhZWiAAAAAAAADzUQABAAAAARbMY3VydgAAAAAAAAQAAAAABQAKAA8AFAAZAB4AIwAo +AC0AMgA3ADsAQABFAEoATwBUAFkAXgBjAGgAbQByAHcAfACBAIYAiwCQAJUAmgCfAKQAqQCuALIAtwC8 +AMEAxgDLANAA1QDbAOAA5QDrAPAA9gD7AQEBBwENARMBGQEfASUBKwEyATgBPgFFAUwBUgFZAWABZwFu +AXUBfAGDAYsBkgGaAaEBqQGxAbkBwQHJAdEB2QHhAekB8gH6AgMCDAIUAh0CJgIvAjgCQQJLAlQCXQJn +AnECegKEAo4CmAKiAqwCtgLBAssC1QLgAusC9QMAAwsDFgMhAy0DOANDA08DWgNmA3IDfgOKA5YDogOu +A7oDxwPTA+AD7AP5BAYEEwQgBC0EOwRIBFUEYwRxBH4EjASaBKgEtgTEBNME4QTwBP4FDQUcBSsFOgVJ +BVgFZwV3BYYFlgWmBbUFxQXVBeUF9gYGBhYGJwY3BkgGWQZqBnsGjAadBq8GwAbRBuMG9QcHBxkHKwc9 +B08HYQd0B4YHmQesB78H0gflB/gICwgfCDIIRghaCG4IggiWCKoIvgjSCOcI+wkQCSUJOglPCWQJeQmP +CaQJugnPCeUJ+woRCicKPQpUCmoKgQqYCq4KxQrcCvMLCwsiCzkLUQtpC4ALmAuwC8gL4Qv5DBIMKgxD +DFwMdQyODKcMwAzZDPMNDQ0mDUANWg10DY4NqQ3DDd4N+A4TDi4OSQ5kDn8Omw62DtIO7g8JDyUPQQ9e +D3oPlg+zD88P7BAJECYQQxBhEH4QmxC5ENcQ9RETETERTxFtEYwRqhHJEegSBxImEkUSZBKEEqMSwxLj +EwMTIxNDE2MTgxOkE8UT5RQGFCcUSRRqFIsUrRTOFPAVEhU0FVYVeBWbFb0V4BYDFiYWSRZsFo8WshbW +FvoXHRdBF2UXiReuF9IX9xgbGEAYZRiKGK8Y1Rj6GSAZRRlrGZEZtxndGgQaKhpRGncanhrFGuwbFBs7 +G2MbihuyG9ocAhwqHFIcexyjHMwc9R0eHUcdcB2ZHcMd7B4WHkAeah6UHr4e6R8THz4faR+UH78f6iAV +IEEgbCCYIMQg8CEcIUghdSGhIc4h+yInIlUigiKvIt0jCiM4I2YjlCPCI/AkHyRNJHwkqyTaJQklOCVo +JZclxyX3JicmVyaHJrcm6CcYJ0kneierJ9woDSg/KHEooijUKQYpOClrKZ0p0CoCKjUqaCqbKs8rAis2 +K2krnSvRLAUsOSxuLKIs1y0MLUEtdi2rLeEuFi5MLoIuty7uLyQvWi+RL8cv/jA1MGwwpDDbMRIxSjGC +Mbox8jIqMmMymzLUMw0zRjN/M7gz8TQrNGU0njTYNRM1TTWHNcI1/TY3NnI2rjbpNyQ3YDecN9c4FDhQ +OIw4yDkFOUI5fzm8Ofk6Njp0OrI67zstO2s7qjvoPCc8ZTykPOM9Ij1hPaE94D4gPmA+oD7gPyE/YT+i +P+JAI0BkQKZA50EpQWpBrEHuQjBCckK1QvdDOkN9Q8BEA0RHRIpEzkUSRVVFmkXeRiJGZ0arRvBHNUd7 +R8BIBUhLSJFI10kdSWNJqUnwSjdKfUrESwxLU0uaS+JMKkxyTLpNAk1KTZNN3E4lTm5Ot08AT0lPk0/d +UCdQcVC7UQZRUFGbUeZSMVJ8UsdTE1NfU6pT9lRCVI9U21UoVXVVwlYPVlxWqVb3V0RXklfgWC9YfVjL +WRpZaVm4WgdaVlqmWvVbRVuVW+VcNVyGXNZdJ114XcleGl5sXr1fD19hX7NgBWBXYKpg/GFPYaJh9WJJ +Ypxi8GNDY5dj62RAZJRk6WU9ZZJl52Y9ZpJm6Gc9Z5Nn6Wg/aJZo7GlDaZpp8WpIap9q92tPa6dr/2xX +bK9tCG1gbbluEm5rbsRvHm94b9FwK3CGcOBxOnGVcfByS3KmcwFzXXO4dBR0cHTMdSh1hXXhdj52m3b4 +d1Z3s3gReG54zHkqeYl553pGeqV7BHtje8J8IXyBfOF9QX2hfgF+Yn7CfyN/hH/lgEeAqIEKgWuBzYIw +gpKC9INXg7qEHYSAhOOFR4Wrhg6GcobXhzuHn4gEiGmIzokziZmJ/opkisqLMIuWi/yMY4zKjTGNmI3/ +jmaOzo82j56QBpBukNaRP5GokhGSepLjk02TtpQglIqU9JVflcmWNJaflwqXdZfgmEyYuJkkmZCZ/Jpo +mtWbQpuvnByciZz3nWSd0p5Anq6fHZ+Ln/qgaaDYoUehtqImopajBqN2o+akVqTHpTilqaYapoum/adu +p+CoUqjEqTepqaocqo+rAqt1q+msXKzQrUStuK4trqGvFq+LsACwdbDqsWCx1rJLssKzOLOutCW0nLUT +tYq2AbZ5tvC3aLfguFm40blKucK6O7q1uy67p7whvJu9Fb2Pvgq+hL7/v3q/9cBwwOzBZ8Hjwl/C28NY +w9TEUcTOxUvFyMZGxsPHQce/yD3IvMk6ybnKOMq3yzbLtsw1zLXNNc21zjbOts83z7jQOdC60TzRvtI/ +0sHTRNPG1EnUy9VO1dHWVdbY11zX4Nhk2OjZbNnx2nba+9uA3AXcit0Q3ZbeHN6i3ynfr+A24L3hROHM +4lPi2+Nj4+vkc+T85YTmDeaW5x/nqegy6LzpRunQ6lvq5etw6/vshu0R7ZzuKO6070DvzPBY8OXxcvH/ +8ozzGfOn9DT0wvVQ9d72bfb794r4Gfio+Tj5x/pX+uf7d/wH/Jj9Kf26/kv+3P9t///SLS4vMFokY2xh +c3NuYW1lWCRjbGFzc2VzXxAQTlNCaXRtYXBJbWFnZVJlcKMvMTJaTlNJbWFnZVJlcFhOU09iamVjdNIt +LjQ1V05TQXJyYXmiNDLSLS43OF5OU011dGFibGVBcnJheaM3NDLTOjsOPD0+V05TV2hpdGVcTlNDb2xv +clNwYWNlRDAgMAAQA4AM0i0uQEFXTlNDb2xvcqJAMtItLkNEV05TSW1hZ2WiQzIACAARABoAJAApADIA +NwBJAEwAUQBTAGIAaAB3AH4AhQCSAJkApQCtALwAvgDAAMUAxwDJAMsAzQDUANkA5ADmAOgA6gDvAPIA +9AD2APgA/wEWATIBNAE2E6YTqxO2E78T0hPWE+ET6hPvE/cT+hP/FA4UEhQZFCEULhQzFDUUNxQ8FEQU +RxRMFFQAAAAAAAACAQAAAAAAAABFAAAAAAAAAAAAAAAAAAAUVw diff --git a/i18n/src/main/resources/th.lproj/Browser.xib b/i18n/src/main/resources/th.lproj/Browser.xib index d6932636d6..ded330b74b 100644 --- a/i18n/src/main/resources/th.lproj/Browser.xib +++ b/i18n/src/main/resources/th.lproj/Browser.xib @@ -1,7 +1,7 @@ - + - + @@ -38,7 +38,7 @@ - + @@ -62,27 +62,27 @@ @@ -256,7 +256,7 @@ - + @@ -553,95 +553,95 @@ DQ YnBsaXN0MDDUAQIDBAUGBwpYJHZlcnNpb25ZJGFyY2hpdmVyVCR0b3BYJG9iamVjdHMSAAGGoF8QD05T -S2V5ZWRBcmNoaXZlctEICVRyb290gAGuCwwZGh8UJCkqMTQ3PUBVJG51bGzWDQ4PEBESExQVFhcYVk5T -U2l6ZV5OU1Jlc2l6aW5nTW9kZVYkY2xhc3NcTlNJbWFnZUZsYWdzVk5TUmVwc1dOU0NvbG9ygAIQAIAN -EiDDAACAA4ALVnsxLCAxfdIbDxweWk5TLm9iamVjdHOhHYAEgArSGw8gI6IhIoAFgAaACdMPJSYnKBRf -EBROU1RJRkZSZXByZXNlbnRhdGlvbl8QGU5TSW50ZXJuYWxMYXlvdXREaXJlY3Rpb26ACIAHTxESbE1N -ACoAAAAKAAAAEAEAAAMAAAABAAEAAAEBAAMAAAABAAEAAAECAAMAAAACAAgACAEDAAMAAAABAAEAAAEG -AAMAAAABAAEAAAEKAAMAAAABAAEAAAERAAQAAAABAAAACAESAAMAAAABAAEAAAEVAAMAAAABAAIAAAEW -AAMAAAABAAEAAAEXAAQAAAABAAAAAgEcAAMAAAABAAEAAAEoAAMAAAABAAIAAAFSAAMAAAABAAEAAAFT -AAMAAAACAAEAAYdzAAcAABGcAAAA0AAAAAAAABGcYXBwbAIAAABtbnRyR1JBWVhZWiAH3AAIABcADwAu -AA9hY3NwQVBQTAAAAABub25lAAAAAAAAAAAAAAAAAAAAAAAA9tYAAQAAAADTLWFwcGwAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAVkZXNjAAAAwAAAAHlkc2NtAAABPAAA -CBpjcHJ0AAAJWAAAACN3dHB0AAAJfAAAABRrVFJDAAAJkAAACAxkZXNjAAAAAAAAAB9HZW5lcmljIEdy -YXkgR2FtbWEgMi4yIFByb2ZpbGUAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAbWx1YwAAAAAAAAAfAAAADHNr -U0sAAAAuAAABhGRhREsAAAA6AAABsmNhRVMAAAA4AAAB7HZpVk4AAABAAAACJHB0QlIAAABKAAACZHVr -VUEAAAAsAAACrmZyRlUAAAA+AAAC2mh1SFUAAAA0AAADGHpoVFcAAAAaAAADTGtvS1IAAAAiAAADZm5i -Tk8AAAA6AAADiGNzQ1oAAAAoAAADwmhlSUwAAAAkAAAD6nJvUk8AAAAqAAAEDmRlREUAAABOAAAEOGl0 -SVQAAABOAAAEhnN2U0UAAAA4AAAE1HpoQ04AAAAaAAAFDGphSlAAAAAmAAAFJmVsR1IAAAAqAAAFTHB0 -UE8AAABSAAAFdm5sTkwAAABAAAAFyGVzRVMAAABMAAAGCHRoVEgAAAAyAAAGVHRyVFIAAAAkAAAGhmZp -RkkAAABGAAAGqmhySFIAAAA+AAAG8HBsUEwAAABKAAAHLmFyRUcAAAAsAAAHeHJ1UlUAAAA6AAAHpGVu -VVMAAAA8AAAH3gBWAWEAZQBvAGIAZQBjAG4A4QAgAHMAaQB2AOEAIABnAGEAbQBhACAAMgAsADIARwBl -AG4AZQByAGkAcwBrACAAZwByAOUAIAAyACwAMgAgAGcAYQBtAG0AYQAtAHAAcgBvAGYAaQBsAEcAYQBt -AG0AYQAgAGQAZQAgAGcAcgBpAHMAbwBzACAAZwBlAG4A6AByAGkAYwBhACAAMgAuADIAQx6lAHUAIABo -AOwAbgBoACAATQDgAHUAIAB4AOEAbQAgAEMAaAB1AG4AZwAgAEcAYQBtAG0AYQAgADIALgAyAFAAZQBy -AGYAaQBsACAARwBlAG4A6QByAGkAYwBvACAAZABhACAARwBhAG0AYQAgAGQAZQAgAEMAaQBuAHoAYQBz -ACAAMgAsADIEFwQwBDMEMAQ7BEwEPQQwACAARwByAGEAeQAtBDMEMAQ8BDAAIAAyAC4AMgBQAHIAbwBm -AGkAbAAgAGcA6QBuAOkAcgBpAHEAdQBlACAAZwByAGkAcwAgAGcAYQBtAG0AYQAgADIALAAyAMEAbAB0 -AGEAbADhAG4AbwBzACAAcwB6APwAcgBrAGUAIABnAGEAbQBtAGEAIAAyAC4AMpAadShwcJaOUUlepgAy -AC4AMoJyX2ljz4/wx3y8GAAg1ozAyQAgrBC5yAAgADIALgAyACDVBLhc0wzHfABHAGUAbgBlAHIAaQBz -AGsAIABnAHIA5QAgAGcAYQBtAG0AYQAgADIALAAyAC0AcAByAG8AZgBpAGwATwBiAGUAYwBuAOEAIAFh -AGUAZADhACAAZwBhAG0AYQAgADIALgAyBdIF0AXeBdQAIAXQBeQF1QXoACAF2wXcBdwF2QAgADIALgAy -AEcAYQBtAGEAIABnAHIAaQAgAGcAZQBuAGUAcgBpAGMBAwAgADIALAAyAEEAbABsAGcAZQBtAGUAaQBu -AGUAcwAgAEcAcgBhAHUAcwB0AHUAZgBlAG4ALQBQAHIAbwBmAGkAbAAgAEcAYQBtAG0AYQAgADIALAAy -AFAAcgBvAGYAaQBsAG8AIABnAHIAaQBnAGkAbwAgAGcAZQBuAGUAcgBpAGMAbwAgAGQAZQBsAGwAYQAg -AGcAYQBtAG0AYQAgADIALAAyAEcAZQBuAGUAcgBpAHMAawAgAGcAcgDlACAAMgAsADIAIABnAGEAbQBt -AGEAcAByAG8AZgBpAGxmbpAacHBepnz7ZXAAMgAuADJjz4/wZYdO9k4AgiwwsDDsMKQwrDDzMN4AIAAy -AC4AMgAgMNcw7TDVMKEwpDDrA5MDtQO9A7kDugPMACADkwO6A8EDuQAgA5MDrAO8A7wDsQAgADIALgAy -AFAAZQByAGYAaQBsACAAZwBlAG4A6QByAGkAYwBvACAAZABlACAAYwBpAG4AegBlAG4AdABvAHMAIABk -AGEAIABHAGEAbQBtAGEAIAAyACwAMgBBAGwAZwBlAG0AZQBlAG4AIABnAHIAaQBqAHMAIABnAGEAbQBt -AGEAIAAyACwAMgAtAHAAcgBvAGYAaQBlAGwAUABlAHIAZgBpAGwAIABnAGUAbgDpAHIAaQBjAG8AIABk -AGUAIABnAGEAbQBtAGEAIABkAGUAIABnAHIAaQBzAGUAcwAgADIALAAyDiMOMQ4HDioONQ5BDgEOIQ4h -DjIOQA4BDiMOIg5MDhcOMQ5IDicORA4bACAAMgAuADIARwBlAG4AZQBsACAARwByAGkAIABHAGEAbQBh -ACAAMgAsADIAWQBsAGUAaQBuAGUAbgAgAGgAYQByAG0AYQBhAG4AIABnAGEAbQBtAGEAIAAyACwAMgAg -AC0AcAByAG8AZgBpAGkAbABpAEcAZQBuAGUAcgBpAQ0AawBpACAARwByAGEAeQAgAEcAYQBtAG0AYQAg -ADIALgAyACAAcAByAG8AZgBpAGwAVQBuAGkAdwBlAHIAcwBhAGwAbgB5ACAAcAByAG8AZgBpAGwAIABz -AHoAYQByAG8BWwBjAGkAIABnAGEAbQBtAGEAIAAyACwAMgY6BicGRQYnACAAMgAuADIAIAZEBkgGRgAg -BjEGRQYnBi8GSgAgBjkGJwZFBB4EMQRJBDAETwAgBEEENQRABDAETwAgBDMEMAQ8BDwEMAAgADIALAAy -AC0EPwRABD4ERAQ4BDsETABHAGUAbgBlAHIAaQBjACAARwByAGEAeQAgAEcAYQBtAG0AYQAgADIALgAy -ACAAUAByAG8AZgBpAGwAZQAAdGV4dAAAAABDb3B5cmlnaHQgQXBwbGUgSW5jLiwgMjAxMgAAWFlaIAAA -AAAAAPNRAAEAAAABFsxjdXJ2AAAAAAAABAAAAAAFAAoADwAUABkAHgAjACgALQAyADcAOwBAAEUASgBP -AFQAWQBeAGMAaABtAHIAdwB8AIEAhgCLAJAAlQCaAJ8ApACpAK4AsgC3ALwAwQDGAMsA0ADVANsA4ADl -AOsA8AD2APsBAQEHAQ0BEwEZAR8BJQErATIBOAE+AUUBTAFSAVkBYAFnAW4BdQF8AYMBiwGSAZoBoQGp -AbEBuQHBAckB0QHZAeEB6QHyAfoCAwIMAhQCHQImAi8COAJBAksCVAJdAmcCcQJ6AoQCjgKYAqICrAK2 -AsECywLVAuAC6wL1AwADCwMWAyEDLQM4A0MDTwNaA2YDcgN+A4oDlgOiA64DugPHA9MD4APsA/kEBgQT -BCAELQQ7BEgEVQRjBHEEfgSMBJoEqAS2BMQE0wThBPAE/gUNBRwFKwU6BUkFWAVnBXcFhgWWBaYFtQXF -BdUF5QX2BgYGFgYnBjcGSAZZBmoGewaMBp0GrwbABtEG4wb1BwcHGQcrBz0HTwdhB3QHhgeZB6wHvwfS -B+UH+AgLCB8IMghGCFoIbgiCCJYIqgi+CNII5wj7CRAJJQk6CU8JZAl5CY8JpAm6Cc8J5Qn7ChEKJwo9 -ClQKagqBCpgKrgrFCtwK8wsLCyILOQtRC2kLgAuYC7ALyAvhC/kMEgwqDEMMXAx1DI4MpwzADNkM8w0N -DSYNQA1aDXQNjg2pDcMN3g34DhMOLg5JDmQOfw6bDrYO0g7uDwkPJQ9BD14Peg+WD7MPzw/sEAkQJhBD -EGEQfhCbELkQ1xD1ERMRMRFPEW0RjBGqEckR6BIHEiYSRRJkEoQSoxLDEuMTAxMjE0MTYxODE6QTxRPl -FAYUJxRJFGoUixStFM4U8BUSFTQVVhV4FZsVvRXgFgMWJhZJFmwWjxayFtYW+hcdF0EXZReJF64X0hf3 -GBsYQBhlGIoYrxjVGPoZIBlFGWsZkRm3Gd0aBBoqGlEadxqeGsUa7BsUGzsbYxuKG7Ib2hwCHCocUhx7 -HKMczBz1HR4dRx1wHZkdwx3sHhYeQB5qHpQevh7pHxMfPh9pH5Qfvx/qIBUgQSBsIJggxCDwIRwhSCF1 -IaEhziH7IiciVSKCIq8i3SMKIzgjZiOUI8Ij8CQfJE0kfCSrJNolCSU4JWgllyXHJfcmJyZXJocmtybo -JxgnSSd6J6sn3CgNKD8ocSiiKNQpBik4KWspnSnQKgIqNSpoKpsqzysCKzYraSudK9EsBSw5LG4soizX -LQwtQS12Last4S4WLkwugi63Lu4vJC9aL5Evxy/+MDUwbDCkMNsxEjFKMYIxujHyMioyYzKbMtQzDTNG -M38zuDPxNCs0ZTSeNNg1EzVNNYc1wjX9Njc2cjauNuk3JDdgN5w31zgUOFA4jDjIOQU5Qjl/Obw5+To2 -OnQ6sjrvOy07azuqO+g8JzxlPKQ84z0iPWE9oT3gPiA+YD6gPuA/IT9hP6I/4kAjQGRApkDnQSlBakGs -Qe5CMEJyQrVC90M6Q31DwEQDREdEikTORRJFVUWaRd5GIkZnRqtG8Ec1R3tHwEgFSEtIkUjXSR1JY0mp -SfBKN0p9SsRLDEtTS5pL4kwqTHJMuk0CTUpNk03cTiVObk63TwBPSU+TT91QJ1BxULtRBlFQUZtR5lIx -UnxSx1MTU19TqlP2VEJUj1TbVShVdVXCVg9WXFapVvdXRFeSV+BYL1h9WMtZGllpWbhaB1pWWqZa9VtF -W5Vb5Vw1XIZc1l0nXXhdyV4aXmxevV8PX2Ffs2AFYFdgqmD8YU9homH1YklinGLwY0Njl2PrZEBklGTp -ZT1lkmXnZj1mkmboZz1nk2fpaD9olmjsaUNpmmnxakhqn2r3a09rp2v/bFdsr20IbWBtuW4SbmtuxG8e -b3hv0XArcIZw4HE6cZVx8HJLcqZzAXNdc7h0FHRwdMx1KHWFdeF2Pnabdvh3VnezeBF4bnjMeSp5iXnn -ekZ6pXsEe2N7wnwhfIF84X1BfaF+AX5ifsJ/I3+Ef+WAR4CogQqBa4HNgjCCkoL0g1eDuoQdhICE44VH -hauGDoZyhteHO4efiASIaYjOiTOJmYn+imSKyoswi5aL/IxjjMqNMY2Yjf+OZo7OjzaPnpAGkG6Q1pE/ -kaiSEZJ6kuOTTZO2lCCUipT0lV+VyZY0lp+XCpd1l+CYTJi4mSSZkJn8mmia1ZtCm6+cHJyJnPedZJ3S -nkCerp8dn4uf+qBpoNihR6G2oiailqMGo3aj5qRWpMelOKWpphqmi6b9p26n4KhSqMSpN6mpqhyqj6sC -q3Wr6axcrNCtRK24ri2uoa8Wr4uwALB1sOqxYLHWskuywrM4s660JbSctRO1irYBtnm28Ldot+C4WbjR -uUq5wro7urW7LrunvCG8m70VvY++Cr6Evv+/er/1wHDA7MFnwePCX8Lbw1jD1MRRxM7FS8XIxkbGw8dB -x7/IPci8yTrJuco4yrfLNsu2zDXMtc01zbXONs62zzfPuNA50LrRPNG+0j/SwdNE08bUSdTL1U7V0dZV -1tjXXNfg2GTY6Nls2fHadtr724DcBdyK3RDdlt4c3qLfKd+v4DbgveFE4cziU+Lb42Pj6+Rz5PzlhOYN -5pbnH+ep6DLovOlG6dDqW+rl63Dr++yG7RHtnO4o7rTvQO/M8Fjw5fFy8f/yjPMZ86f0NPTC9VD13vZt -9vv3ivgZ+Kj5OPnH+lf65/t3/Af8mP0p/br+S/7c/23//9IrLC0uWiRjbGFzc25hbWVYJGNsYXNzZXNf -EBBOU0JpdG1hcEltYWdlUmVwoy0vMFpOU0ltYWdlUmVwWE5TT2JqZWN00issMjNXTlNBcnJheaIyMNIr -LDU2Xk5TTXV0YWJsZUFycmF5ozUyMNM4OQ86OzxXTlNXaGl0ZVxOU0NvbG9yU3BhY2VEMCAwABADgAzS -Kyw+P1dOU0NvbG9yoj4w0issQUJXTlNJbWFnZaJBMAAIABEAGgAkACkAMgA3AEkATABRAFMAYgBoAHUA -fACLAJIAnwCmAK4AsACyALQAuQC7AL0AxADJANQA1gDYANoA3wDiAOQA5gDoAO8BBgEiASQBJhOWE5sT -phOvE8ITxhPRE9oT3xPnE+oT7xP+FAIUCRQRFB4UIxQlFCcULBQ0FDcUPBREAAAAAAAAAgEAAAAAAAAA -QwAAAAAAAAAAAAAAAAAAFEc +S2V5ZWRBcmNoaXZlctEICVRyb290gAGuCwwbHCEaJissMzY5P0JVJG51bGzXDQ4PEBESExQVFhcYGRpW +TlNTaXplViRjbGFzc1xOU0ltYWdlRmxhZ3NWTlNSZXBzW05TVGludENvbG9yV05TQ29sb3JeTlNSZXNp +emluZ01vZGWAAoANEiDDAACAA4AAgAsQAFZ7MSwgMX3SHQ4eIFpOUy5vYmplY3RzoR+ABIAK0h0OIiWi +IySABYAGgAnTDicoKSoaXxAUTlNUSUZGUmVwcmVzZW50YXRpb25fEBlOU0ludGVybmFsTGF5b3V0RGly +ZWN0aW9ugAiAB08REmxNTQAqAAAACgAAABABAAADAAAAAQABAAABAQADAAAAAQABAAABAgADAAAAAgAI +AAgBAwADAAAAAQABAAABBgADAAAAAQABAAABCgADAAAAAQABAAABEQAEAAAAAQAAAAgBEgADAAAAAQAB +AAABFQADAAAAAQACAAABFgADAAAAAQABAAABFwAEAAAAAQAAAAIBHAADAAAAAQABAAABKAADAAAAAQAC +AAABUgADAAAAAQABAAABUwADAAAAAgABAAGHcwAHAAARnAAAANAAAAAAAAARnGFwcGwCAAAAbW50ckdS +QVlYWVogB9wACAAXAA8ALgAPYWNzcEFQUEwAAAAAbm9uZQAAAAAAAAAAAAAAAAAAAAAAAPbWAAEAAAAA +0y1hcHBsAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFZGVzYwAA +AMAAAAB5ZHNjbQAAATwAAAgaY3BydAAACVgAAAAjd3RwdAAACXwAAAAUa1RSQwAACZAAAAgMZGVzYwAA +AAAAAAAfR2VuZXJpYyBHcmF5IEdhbW1hIDIuMiBQcm9maWxlAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAG1s +dWMAAAAAAAAAHwAAAAxza1NLAAAALgAAAYRkYURLAAAAOgAAAbJjYUVTAAAAOAAAAex2aVZOAAAAQAAA +AiRwdEJSAAAASgAAAmR1a1VBAAAALAAAAq5mckZVAAAAPgAAAtpodUhVAAAANAAAAxh6aFRXAAAAGgAA +A0xrb0tSAAAAIgAAA2ZuYk5PAAAAOgAAA4hjc0NaAAAAKAAAA8JoZUlMAAAAJAAAA+pyb1JPAAAAKgAA +BA5kZURFAAAATgAABDhpdElUAAAATgAABIZzdlNFAAAAOAAABNR6aENOAAAAGgAABQxqYUpQAAAAJgAA +BSZlbEdSAAAAKgAABUxwdFBPAAAAUgAABXZubE5MAAAAQAAABchlc0VTAAAATAAABgh0aFRIAAAAMgAA +BlR0clRSAAAAJAAABoZmaUZJAAAARgAABqpockhSAAAAPgAABvBwbFBMAAAASgAABy5hckVHAAAALAAA +B3hydVJVAAAAOgAAB6RlblVTAAAAPAAAB94AVgFhAGUAbwBiAGUAYwBuAOEAIABzAGkAdgDhACAAZwBh +AG0AYQAgADIALAAyAEcAZQBuAGUAcgBpAHMAawAgAGcAcgDlACAAMgAsADIAIABnAGEAbQBtAGEALQBw +AHIAbwBmAGkAbABHAGEAbQBtAGEAIABkAGUAIABnAHIAaQBzAG8AcwAgAGcAZQBuAOgAcgBpAGMAYQAg +ADIALgAyAEMepQB1ACAAaADsAG4AaAAgAE0A4AB1ACAAeADhAG0AIABDAGgAdQBuAGcAIABHAGEAbQBt +AGEAIAAyAC4AMgBQAGUAcgBmAGkAbAAgAEcAZQBuAOkAcgBpAGMAbwAgAGQAYQAgAEcAYQBtAGEAIABk +AGUAIABDAGkAbgB6AGEAcwAgADIALAAyBBcEMAQzBDAEOwRMBD0EMAAgAEcAcgBhAHkALQQzBDAEPAQw +ACAAMgAuADIAUAByAG8AZgBpAGwAIABnAOkAbgDpAHIAaQBxAHUAZQAgAGcAcgBpAHMAIABnAGEAbQBt +AGEAIAAyACwAMgDBAGwAdABhAGwA4QBuAG8AcwAgAHMAegD8AHIAawBlACAAZwBhAG0AbQBhACAAMgAu +ADKQGnUocHCWjlFJXqYAMgAuADKCcl9pY8+P8Md8vBgAINaMwMkAIKwQucgAIAAyAC4AMgAg1QS4XNMM +x3wARwBlAG4AZQByAGkAcwBrACAAZwByAOUAIABnAGEAbQBtAGEAIAAyACwAMgAtAHAAcgBvAGYAaQBs +AE8AYgBlAGMAbgDhACABYQBlAGQA4QAgAGcAYQBtAGEAIAAyAC4AMgXSBdAF3gXUACAF0AXkBdUF6AAg +BdsF3AXcBdkAIAAyAC4AMgBHAGEAbQBhACAAZwByAGkAIABnAGUAbgBlAHIAaQBjAQMAIAAyACwAMgBB +AGwAbABnAGUAbQBlAGkAbgBlAHMAIABHAHIAYQB1AHMAdAB1AGYAZQBuAC0AUAByAG8AZgBpAGwAIABH +AGEAbQBtAGEAIAAyACwAMgBQAHIAbwBmAGkAbABvACAAZwByAGkAZwBpAG8AIABnAGUAbgBlAHIAaQBj +AG8AIABkAGUAbABsAGEAIABnAGEAbQBtAGEAIAAyACwAMgBHAGUAbgBlAHIAaQBzAGsAIABnAHIA5QAg +ADIALAAyACAAZwBhAG0AbQBhAHAAcgBvAGYAaQBsZm6QGnBwXqZ8+2VwADIALgAyY8+P8GWHTvZOAIIs +MLAw7DCkMKww8zDeACAAMgAuADIAIDDXMO0w1TChMKQw6wOTA7UDvQO5A7oDzAAgA5MDugPBA7kAIAOT +A6wDvAO8A7EAIAAyAC4AMgBQAGUAcgBmAGkAbAAgAGcAZQBuAOkAcgBpAGMAbwAgAGQAZQAgAGMAaQBu +AHoAZQBuAHQAbwBzACAAZABhACAARwBhAG0AbQBhACAAMgAsADIAQQBsAGcAZQBtAGUAZQBuACAAZwBy +AGkAagBzACAAZwBhAG0AbQBhACAAMgAsADIALQBwAHIAbwBmAGkAZQBsAFAAZQByAGYAaQBsACAAZwBl +AG4A6QByAGkAYwBvACAAZABlACAAZwBhAG0AbQBhACAAZABlACAAZwByAGkAcwBlAHMAIAAyACwAMg4j +DjEOBw4qDjUOQQ4BDiEOIQ4yDkAOAQ4jDiIOTA4XDjEOSA4nDkQOGwAgADIALgAyAEcAZQBuAGUAbAAg +AEcAcgBpACAARwBhAG0AYQAgADIALAAyAFkAbABlAGkAbgBlAG4AIABoAGEAcgBtAGEAYQBuACAAZwBh +AG0AbQBhACAAMgAsADIAIAAtAHAAcgBvAGYAaQBpAGwAaQBHAGUAbgBlAHIAaQENAGsAaQAgAEcAcgBh +AHkAIABHAGEAbQBtAGEAIAAyAC4AMgAgAHAAcgBvAGYAaQBsAFUAbgBpAHcAZQByAHMAYQBsAG4AeQAg +AHAAcgBvAGYAaQBsACAAcwB6AGEAcgBvAVsAYwBpACAAZwBhAG0AbQBhACAAMgAsADIGOgYnBkUGJwAg +ADIALgAyACAGRAZIBkYAIAYxBkUGJwYvBkoAIAY5BicGRQQeBDEESQQwBE8AIARBBDUEQAQwBE8AIAQz +BDAEPAQ8BDAAIAAyACwAMgAtBD8EQAQ+BEQEOAQ7BEwARwBlAG4AZQByAGkAYwAgAEcAcgBhAHkAIABH +AGEAbQBtAGEAIAAyAC4AMgAgAFAAcgBvAGYAaQBsAGUAAHRleHQAAAAAQ29weXJpZ2h0IEFwcGxlIElu +Yy4sIDIwMTIAAFhZWiAAAAAAAADzUQABAAAAARbMY3VydgAAAAAAAAQAAAAABQAKAA8AFAAZAB4AIwAo +AC0AMgA3ADsAQABFAEoATwBUAFkAXgBjAGgAbQByAHcAfACBAIYAiwCQAJUAmgCfAKQAqQCuALIAtwC8 +AMEAxgDLANAA1QDbAOAA5QDrAPAA9gD7AQEBBwENARMBGQEfASUBKwEyATgBPgFFAUwBUgFZAWABZwFu +AXUBfAGDAYsBkgGaAaEBqQGxAbkBwQHJAdEB2QHhAekB8gH6AgMCDAIUAh0CJgIvAjgCQQJLAlQCXQJn +AnECegKEAo4CmAKiAqwCtgLBAssC1QLgAusC9QMAAwsDFgMhAy0DOANDA08DWgNmA3IDfgOKA5YDogOu +A7oDxwPTA+AD7AP5BAYEEwQgBC0EOwRIBFUEYwRxBH4EjASaBKgEtgTEBNME4QTwBP4FDQUcBSsFOgVJ +BVgFZwV3BYYFlgWmBbUFxQXVBeUF9gYGBhYGJwY3BkgGWQZqBnsGjAadBq8GwAbRBuMG9QcHBxkHKwc9 +B08HYQd0B4YHmQesB78H0gflB/gICwgfCDIIRghaCG4IggiWCKoIvgjSCOcI+wkQCSUJOglPCWQJeQmP +CaQJugnPCeUJ+woRCicKPQpUCmoKgQqYCq4KxQrcCvMLCwsiCzkLUQtpC4ALmAuwC8gL4Qv5DBIMKgxD +DFwMdQyODKcMwAzZDPMNDQ0mDUANWg10DY4NqQ3DDd4N+A4TDi4OSQ5kDn8Omw62DtIO7g8JDyUPQQ9e +D3oPlg+zD88P7BAJECYQQxBhEH4QmxC5ENcQ9RETETERTxFtEYwRqhHJEegSBxImEkUSZBKEEqMSwxLj +EwMTIxNDE2MTgxOkE8UT5RQGFCcUSRRqFIsUrRTOFPAVEhU0FVYVeBWbFb0V4BYDFiYWSRZsFo8WshbW +FvoXHRdBF2UXiReuF9IX9xgbGEAYZRiKGK8Y1Rj6GSAZRRlrGZEZtxndGgQaKhpRGncanhrFGuwbFBs7 +G2MbihuyG9ocAhwqHFIcexyjHMwc9R0eHUcdcB2ZHcMd7B4WHkAeah6UHr4e6R8THz4faR+UH78f6iAV +IEEgbCCYIMQg8CEcIUghdSGhIc4h+yInIlUigiKvIt0jCiM4I2YjlCPCI/AkHyRNJHwkqyTaJQklOCVo +JZclxyX3JicmVyaHJrcm6CcYJ0kneierJ9woDSg/KHEooijUKQYpOClrKZ0p0CoCKjUqaCqbKs8rAis2 +K2krnSvRLAUsOSxuLKIs1y0MLUEtdi2rLeEuFi5MLoIuty7uLyQvWi+RL8cv/jA1MGwwpDDbMRIxSjGC +Mbox8jIqMmMymzLUMw0zRjN/M7gz8TQrNGU0njTYNRM1TTWHNcI1/TY3NnI2rjbpNyQ3YDecN9c4FDhQ +OIw4yDkFOUI5fzm8Ofk6Njp0OrI67zstO2s7qjvoPCc8ZTykPOM9Ij1hPaE94D4gPmA+oD7gPyE/YT+i +P+JAI0BkQKZA50EpQWpBrEHuQjBCckK1QvdDOkN9Q8BEA0RHRIpEzkUSRVVFmkXeRiJGZ0arRvBHNUd7 +R8BIBUhLSJFI10kdSWNJqUnwSjdKfUrESwxLU0uaS+JMKkxyTLpNAk1KTZNN3E4lTm5Ot08AT0lPk0/d +UCdQcVC7UQZRUFGbUeZSMVJ8UsdTE1NfU6pT9lRCVI9U21UoVXVVwlYPVlxWqVb3V0RXklfgWC9YfVjL +WRpZaVm4WgdaVlqmWvVbRVuVW+VcNVyGXNZdJ114XcleGl5sXr1fD19hX7NgBWBXYKpg/GFPYaJh9WJJ +Ypxi8GNDY5dj62RAZJRk6WU9ZZJl52Y9ZpJm6Gc9Z5Nn6Wg/aJZo7GlDaZpp8WpIap9q92tPa6dr/2xX +bK9tCG1gbbluEm5rbsRvHm94b9FwK3CGcOBxOnGVcfByS3KmcwFzXXO4dBR0cHTMdSh1hXXhdj52m3b4 +d1Z3s3gReG54zHkqeYl553pGeqV7BHtje8J8IXyBfOF9QX2hfgF+Yn7CfyN/hH/lgEeAqIEKgWuBzYIw +gpKC9INXg7qEHYSAhOOFR4Wrhg6GcobXhzuHn4gEiGmIzokziZmJ/opkisqLMIuWi/yMY4zKjTGNmI3/ +jmaOzo82j56QBpBukNaRP5GokhGSepLjk02TtpQglIqU9JVflcmWNJaflwqXdZfgmEyYuJkkmZCZ/Jpo +mtWbQpuvnByciZz3nWSd0p5Anq6fHZ+Ln/qgaaDYoUehtqImopajBqN2o+akVqTHpTilqaYapoum/adu +p+CoUqjEqTepqaocqo+rAqt1q+msXKzQrUStuK4trqGvFq+LsACwdbDqsWCx1rJLssKzOLOutCW0nLUT +tYq2AbZ5tvC3aLfguFm40blKucK6O7q1uy67p7whvJu9Fb2Pvgq+hL7/v3q/9cBwwOzBZ8Hjwl/C28NY +w9TEUcTOxUvFyMZGxsPHQce/yD3IvMk6ybnKOMq3yzbLtsw1zLXNNc21zjbOts83z7jQOdC60TzRvtI/ +0sHTRNPG1EnUy9VO1dHWVdbY11zX4Nhk2OjZbNnx2nba+9uA3AXcit0Q3ZbeHN6i3ynfr+A24L3hROHM +4lPi2+Nj4+vkc+T85YTmDeaW5x/nqegy6LzpRunQ6lvq5etw6/vshu0R7ZzuKO6070DvzPBY8OXxcvH/ +8ozzGfOn9DT0wvVQ9d72bfb794r4Gfio+Tj5x/pX+uf7d/wH/Jj9Kf26/kv+3P9t///SLS4vMFokY2xh +c3NuYW1lWCRjbGFzc2VzXxAQTlNCaXRtYXBJbWFnZVJlcKMvMTJaTlNJbWFnZVJlcFhOU09iamVjdNIt +LjQ1V05TQXJyYXmiNDLSLS43OF5OU011dGFibGVBcnJheaM3NDLTOjsOPD0+V05TV2hpdGVcTlNDb2xv +clNwYWNlRDAgMAAQA4AM0i0uQEFXTlNDb2xvcqJAMtItLkNEV05TSW1hZ2WiQzIACAARABoAJAApADIA +NwBJAEwAUQBTAGIAaAB3AH4AhQCSAJkApQCtALwAvgDAAMUAxwDJAMsAzQDUANkA5ADmAOgA6gDvAPIA +9AD2APgA/wEWATIBNAE2E6YTqxO2E78T0hPWE+ET6hPvE/cT+hP/FA4UEhQZFCEULhQzFDUUNxQ8FEQU +RxRMFFQAAAAAAAACAQAAAAAAAABFAAAAAAAAAAAAAAAAAAAUVw diff --git a/i18n/src/main/resources/th.lproj/Command.xib b/i18n/src/main/resources/th.lproj/Command.xib index fd040a3c1a..40c94a26cb 100644 --- a/i18n/src/main/resources/th.lproj/Command.xib +++ b/i18n/src/main/resources/th.lproj/Command.xib @@ -1,7 +1,7 @@ - + - + diff --git a/i18n/src/main/resources/th.lproj/Connection.xib b/i18n/src/main/resources/th.lproj/Connection.xib index 88d39206b8..6892b76941 100644 --- a/i18n/src/main/resources/th.lproj/Connection.xib +++ b/i18n/src/main/resources/th.lproj/Connection.xib @@ -1,7 +1,7 @@ - + - + @@ -76,7 +76,7 @@ - + diff --git a/i18n/src/main/resources/th.lproj/Info.xib b/i18n/src/main/resources/th.lproj/Info.xib index b6c1f11631..4e847ab2e8 100644 --- a/i18n/src/main/resources/th.lproj/Info.xib +++ b/i18n/src/main/resources/th.lproj/Info.xib @@ -1,7 +1,7 @@ - + - + @@ -89,7 +89,7 @@ - + @@ -1017,21 +1017,21 @@ - + - + - + @@ -1121,21 +1121,21 @@ - + - + - + @@ -1250,27 +1250,27 @@ diff --git a/i18n/src/main/resources/th.lproj/Login.strings b/i18n/src/main/resources/th.lproj/Login.strings index 7db78d4138..df2c226e43 100644 Binary files a/i18n/src/main/resources/th.lproj/Login.strings and b/i18n/src/main/resources/th.lproj/Login.strings differ diff --git a/i18n/src/main/resources/th.lproj/Main.xib b/i18n/src/main/resources/th.lproj/Main.xib index cc704c2465..9f463edca8 100644 --- a/i18n/src/main/resources/th.lproj/Main.xib +++ b/i18n/src/main/resources/th.lproj/Main.xib @@ -1,7 +1,7 @@ - + - + @@ -159,19 +159,19 @@ - + - + - + @@ -321,12 +321,12 @@ CA - + - + @@ -566,6 +566,10 @@ CA - + + + + + diff --git a/i18n/src/main/resources/th.lproj/Preferences.strings b/i18n/src/main/resources/th.lproj/Preferences.strings index 45ce92fad6..159f6fd290 100644 Binary files a/i18n/src/main/resources/th.lproj/Preferences.strings and b/i18n/src/main/resources/th.lproj/Preferences.strings differ diff --git a/i18n/src/main/resources/th.lproj/Preferences.strings.1 b/i18n/src/main/resources/th.lproj/Preferences.strings.1 index 4e47af1df0..7e7c11618a 100644 Binary files a/i18n/src/main/resources/th.lproj/Preferences.strings.1 and b/i18n/src/main/resources/th.lproj/Preferences.strings.1 differ diff --git a/i18n/src/main/resources/th.lproj/Preferences.xib b/i18n/src/main/resources/th.lproj/Preferences.xib index 166411e591..5f217af9fe 100644 --- a/i18n/src/main/resources/th.lproj/Preferences.xib +++ b/i18n/src/main/resources/th.lproj/Preferences.xib @@ -1,7 +1,7 @@ - + - + @@ -266,7 +266,7 @@ - + @@ -307,7 +307,7 @@ - + @@ -538,7 +538,7 @@ - + @@ -578,7 +578,7 @@ - + @@ -613,7 +613,7 @@ - + @@ -642,7 +642,7 @@ - + @@ -1218,7 +1218,7 @@ - + @@ -1250,7 +1250,7 @@ - + @@ -1290,7 +1290,7 @@ - + @@ -1363,7 +1363,7 @@ - + @@ -1506,7 +1506,7 @@ @@ -256,7 +256,7 @@ - + @@ -553,95 +553,95 @@ DQ YnBsaXN0MDDUAQIDBAUGBwpYJHZlcnNpb25ZJGFyY2hpdmVyVCR0b3BYJG9iamVjdHMSAAGGoF8QD05T -S2V5ZWRBcmNoaXZlctEICVRyb290gAGuCwwZGh8UJCkqMTQ3PUBVJG51bGzWDQ4PEBESExQVFhcYVk5T -U2l6ZV5OU1Jlc2l6aW5nTW9kZVYkY2xhc3NcTlNJbWFnZUZsYWdzVk5TUmVwc1dOU0NvbG9ygAIQAIAN -EiDDAACAA4ALVnsxLCAxfdIbDxweWk5TLm9iamVjdHOhHYAEgArSGw8gI6IhIoAFgAaACdMPJSYnKBRf -EBROU1RJRkZSZXByZXNlbnRhdGlvbl8QGU5TSW50ZXJuYWxMYXlvdXREaXJlY3Rpb26ACIAHTxESbE1N -ACoAAAAKAAAAEAEAAAMAAAABAAEAAAEBAAMAAAABAAEAAAECAAMAAAACAAgACAEDAAMAAAABAAEAAAEG -AAMAAAABAAEAAAEKAAMAAAABAAEAAAERAAQAAAABAAAACAESAAMAAAABAAEAAAEVAAMAAAABAAIAAAEW -AAMAAAABAAEAAAEXAAQAAAABAAAAAgEcAAMAAAABAAEAAAEoAAMAAAABAAIAAAFSAAMAAAABAAEAAAFT -AAMAAAACAAEAAYdzAAcAABGcAAAA0AAAAAAAABGcYXBwbAIAAABtbnRyR1JBWVhZWiAH3AAIABcADwAu -AA9hY3NwQVBQTAAAAABub25lAAAAAAAAAAAAAAAAAAAAAAAA9tYAAQAAAADTLWFwcGwAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAVkZXNjAAAAwAAAAHlkc2NtAAABPAAA -CBpjcHJ0AAAJWAAAACN3dHB0AAAJfAAAABRrVFJDAAAJkAAACAxkZXNjAAAAAAAAAB9HZW5lcmljIEdy -YXkgR2FtbWEgMi4yIFByb2ZpbGUAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAbWx1YwAAAAAAAAAfAAAADHNr -U0sAAAAuAAABhGRhREsAAAA6AAABsmNhRVMAAAA4AAAB7HZpVk4AAABAAAACJHB0QlIAAABKAAACZHVr -VUEAAAAsAAACrmZyRlUAAAA+AAAC2mh1SFUAAAA0AAADGHpoVFcAAAAaAAADTGtvS1IAAAAiAAADZm5i -Tk8AAAA6AAADiGNzQ1oAAAAoAAADwmhlSUwAAAAkAAAD6nJvUk8AAAAqAAAEDmRlREUAAABOAAAEOGl0 -SVQAAABOAAAEhnN2U0UAAAA4AAAE1HpoQ04AAAAaAAAFDGphSlAAAAAmAAAFJmVsR1IAAAAqAAAFTHB0 -UE8AAABSAAAFdm5sTkwAAABAAAAFyGVzRVMAAABMAAAGCHRoVEgAAAAyAAAGVHRyVFIAAAAkAAAGhmZp -RkkAAABGAAAGqmhySFIAAAA+AAAG8HBsUEwAAABKAAAHLmFyRUcAAAAsAAAHeHJ1UlUAAAA6AAAHpGVu -VVMAAAA8AAAH3gBWAWEAZQBvAGIAZQBjAG4A4QAgAHMAaQB2AOEAIABnAGEAbQBhACAAMgAsADIARwBl -AG4AZQByAGkAcwBrACAAZwByAOUAIAAyACwAMgAgAGcAYQBtAG0AYQAtAHAAcgBvAGYAaQBsAEcAYQBt -AG0AYQAgAGQAZQAgAGcAcgBpAHMAbwBzACAAZwBlAG4A6AByAGkAYwBhACAAMgAuADIAQx6lAHUAIABo -AOwAbgBoACAATQDgAHUAIAB4AOEAbQAgAEMAaAB1AG4AZwAgAEcAYQBtAG0AYQAgADIALgAyAFAAZQBy -AGYAaQBsACAARwBlAG4A6QByAGkAYwBvACAAZABhACAARwBhAG0AYQAgAGQAZQAgAEMAaQBuAHoAYQBz -ACAAMgAsADIEFwQwBDMEMAQ7BEwEPQQwACAARwByAGEAeQAtBDMEMAQ8BDAAIAAyAC4AMgBQAHIAbwBm -AGkAbAAgAGcA6QBuAOkAcgBpAHEAdQBlACAAZwByAGkAcwAgAGcAYQBtAG0AYQAgADIALAAyAMEAbAB0 -AGEAbADhAG4AbwBzACAAcwB6APwAcgBrAGUAIABnAGEAbQBtAGEAIAAyAC4AMpAadShwcJaOUUlepgAy -AC4AMoJyX2ljz4/wx3y8GAAg1ozAyQAgrBC5yAAgADIALgAyACDVBLhc0wzHfABHAGUAbgBlAHIAaQBz -AGsAIABnAHIA5QAgAGcAYQBtAG0AYQAgADIALAAyAC0AcAByAG8AZgBpAGwATwBiAGUAYwBuAOEAIAFh -AGUAZADhACAAZwBhAG0AYQAgADIALgAyBdIF0AXeBdQAIAXQBeQF1QXoACAF2wXcBdwF2QAgADIALgAy -AEcAYQBtAGEAIABnAHIAaQAgAGcAZQBuAGUAcgBpAGMBAwAgADIALAAyAEEAbABsAGcAZQBtAGUAaQBu -AGUAcwAgAEcAcgBhAHUAcwB0AHUAZgBlAG4ALQBQAHIAbwBmAGkAbAAgAEcAYQBtAG0AYQAgADIALAAy -AFAAcgBvAGYAaQBsAG8AIABnAHIAaQBnAGkAbwAgAGcAZQBuAGUAcgBpAGMAbwAgAGQAZQBsAGwAYQAg -AGcAYQBtAG0AYQAgADIALAAyAEcAZQBuAGUAcgBpAHMAawAgAGcAcgDlACAAMgAsADIAIABnAGEAbQBt -AGEAcAByAG8AZgBpAGxmbpAacHBepnz7ZXAAMgAuADJjz4/wZYdO9k4AgiwwsDDsMKQwrDDzMN4AIAAy -AC4AMgAgMNcw7TDVMKEwpDDrA5MDtQO9A7kDugPMACADkwO6A8EDuQAgA5MDrAO8A7wDsQAgADIALgAy -AFAAZQByAGYAaQBsACAAZwBlAG4A6QByAGkAYwBvACAAZABlACAAYwBpAG4AegBlAG4AdABvAHMAIABk -AGEAIABHAGEAbQBtAGEAIAAyACwAMgBBAGwAZwBlAG0AZQBlAG4AIABnAHIAaQBqAHMAIABnAGEAbQBt -AGEAIAAyACwAMgAtAHAAcgBvAGYAaQBlAGwAUABlAHIAZgBpAGwAIABnAGUAbgDpAHIAaQBjAG8AIABk -AGUAIABnAGEAbQBtAGEAIABkAGUAIABnAHIAaQBzAGUAcwAgADIALAAyDiMOMQ4HDioONQ5BDgEOIQ4h -DjIOQA4BDiMOIg5MDhcOMQ5IDicORA4bACAAMgAuADIARwBlAG4AZQBsACAARwByAGkAIABHAGEAbQBh -ACAAMgAsADIAWQBsAGUAaQBuAGUAbgAgAGgAYQByAG0AYQBhAG4AIABnAGEAbQBtAGEAIAAyACwAMgAg -AC0AcAByAG8AZgBpAGkAbABpAEcAZQBuAGUAcgBpAQ0AawBpACAARwByAGEAeQAgAEcAYQBtAG0AYQAg -ADIALgAyACAAcAByAG8AZgBpAGwAVQBuAGkAdwBlAHIAcwBhAGwAbgB5ACAAcAByAG8AZgBpAGwAIABz -AHoAYQByAG8BWwBjAGkAIABnAGEAbQBtAGEAIAAyACwAMgY6BicGRQYnACAAMgAuADIAIAZEBkgGRgAg -BjEGRQYnBi8GSgAgBjkGJwZFBB4EMQRJBDAETwAgBEEENQRABDAETwAgBDMEMAQ8BDwEMAAgADIALAAy -AC0EPwRABD4ERAQ4BDsETABHAGUAbgBlAHIAaQBjACAARwByAGEAeQAgAEcAYQBtAG0AYQAgADIALgAy -ACAAUAByAG8AZgBpAGwAZQAAdGV4dAAAAABDb3B5cmlnaHQgQXBwbGUgSW5jLiwgMjAxMgAAWFlaIAAA -AAAAAPNRAAEAAAABFsxjdXJ2AAAAAAAABAAAAAAFAAoADwAUABkAHgAjACgALQAyADcAOwBAAEUASgBP -AFQAWQBeAGMAaABtAHIAdwB8AIEAhgCLAJAAlQCaAJ8ApACpAK4AsgC3ALwAwQDGAMsA0ADVANsA4ADl -AOsA8AD2APsBAQEHAQ0BEwEZAR8BJQErATIBOAE+AUUBTAFSAVkBYAFnAW4BdQF8AYMBiwGSAZoBoQGp -AbEBuQHBAckB0QHZAeEB6QHyAfoCAwIMAhQCHQImAi8COAJBAksCVAJdAmcCcQJ6AoQCjgKYAqICrAK2 -AsECywLVAuAC6wL1AwADCwMWAyEDLQM4A0MDTwNaA2YDcgN+A4oDlgOiA64DugPHA9MD4APsA/kEBgQT -BCAELQQ7BEgEVQRjBHEEfgSMBJoEqAS2BMQE0wThBPAE/gUNBRwFKwU6BUkFWAVnBXcFhgWWBaYFtQXF -BdUF5QX2BgYGFgYnBjcGSAZZBmoGewaMBp0GrwbABtEG4wb1BwcHGQcrBz0HTwdhB3QHhgeZB6wHvwfS -B+UH+AgLCB8IMghGCFoIbgiCCJYIqgi+CNII5wj7CRAJJQk6CU8JZAl5CY8JpAm6Cc8J5Qn7ChEKJwo9 -ClQKagqBCpgKrgrFCtwK8wsLCyILOQtRC2kLgAuYC7ALyAvhC/kMEgwqDEMMXAx1DI4MpwzADNkM8w0N -DSYNQA1aDXQNjg2pDcMN3g34DhMOLg5JDmQOfw6bDrYO0g7uDwkPJQ9BD14Peg+WD7MPzw/sEAkQJhBD -EGEQfhCbELkQ1xD1ERMRMRFPEW0RjBGqEckR6BIHEiYSRRJkEoQSoxLDEuMTAxMjE0MTYxODE6QTxRPl -FAYUJxRJFGoUixStFM4U8BUSFTQVVhV4FZsVvRXgFgMWJhZJFmwWjxayFtYW+hcdF0EXZReJF64X0hf3 -GBsYQBhlGIoYrxjVGPoZIBlFGWsZkRm3Gd0aBBoqGlEadxqeGsUa7BsUGzsbYxuKG7Ib2hwCHCocUhx7 -HKMczBz1HR4dRx1wHZkdwx3sHhYeQB5qHpQevh7pHxMfPh9pH5Qfvx/qIBUgQSBsIJggxCDwIRwhSCF1 -IaEhziH7IiciVSKCIq8i3SMKIzgjZiOUI8Ij8CQfJE0kfCSrJNolCSU4JWgllyXHJfcmJyZXJocmtybo -JxgnSSd6J6sn3CgNKD8ocSiiKNQpBik4KWspnSnQKgIqNSpoKpsqzysCKzYraSudK9EsBSw5LG4soizX -LQwtQS12Last4S4WLkwugi63Lu4vJC9aL5Evxy/+MDUwbDCkMNsxEjFKMYIxujHyMioyYzKbMtQzDTNG -M38zuDPxNCs0ZTSeNNg1EzVNNYc1wjX9Njc2cjauNuk3JDdgN5w31zgUOFA4jDjIOQU5Qjl/Obw5+To2 -OnQ6sjrvOy07azuqO+g8JzxlPKQ84z0iPWE9oT3gPiA+YD6gPuA/IT9hP6I/4kAjQGRApkDnQSlBakGs -Qe5CMEJyQrVC90M6Q31DwEQDREdEikTORRJFVUWaRd5GIkZnRqtG8Ec1R3tHwEgFSEtIkUjXSR1JY0mp -SfBKN0p9SsRLDEtTS5pL4kwqTHJMuk0CTUpNk03cTiVObk63TwBPSU+TT91QJ1BxULtRBlFQUZtR5lIx -UnxSx1MTU19TqlP2VEJUj1TbVShVdVXCVg9WXFapVvdXRFeSV+BYL1h9WMtZGllpWbhaB1pWWqZa9VtF -W5Vb5Vw1XIZc1l0nXXhdyV4aXmxevV8PX2Ffs2AFYFdgqmD8YU9homH1YklinGLwY0Njl2PrZEBklGTp -ZT1lkmXnZj1mkmboZz1nk2fpaD9olmjsaUNpmmnxakhqn2r3a09rp2v/bFdsr20IbWBtuW4SbmtuxG8e -b3hv0XArcIZw4HE6cZVx8HJLcqZzAXNdc7h0FHRwdMx1KHWFdeF2Pnabdvh3VnezeBF4bnjMeSp5iXnn -ekZ6pXsEe2N7wnwhfIF84X1BfaF+AX5ifsJ/I3+Ef+WAR4CogQqBa4HNgjCCkoL0g1eDuoQdhICE44VH -hauGDoZyhteHO4efiASIaYjOiTOJmYn+imSKyoswi5aL/IxjjMqNMY2Yjf+OZo7OjzaPnpAGkG6Q1pE/ -kaiSEZJ6kuOTTZO2lCCUipT0lV+VyZY0lp+XCpd1l+CYTJi4mSSZkJn8mmia1ZtCm6+cHJyJnPedZJ3S -nkCerp8dn4uf+qBpoNihR6G2oiailqMGo3aj5qRWpMelOKWpphqmi6b9p26n4KhSqMSpN6mpqhyqj6sC -q3Wr6axcrNCtRK24ri2uoa8Wr4uwALB1sOqxYLHWskuywrM4s660JbSctRO1irYBtnm28Ldot+C4WbjR -uUq5wro7urW7LrunvCG8m70VvY++Cr6Evv+/er/1wHDA7MFnwePCX8Lbw1jD1MRRxM7FS8XIxkbGw8dB -x7/IPci8yTrJuco4yrfLNsu2zDXMtc01zbXONs62zzfPuNA50LrRPNG+0j/SwdNE08bUSdTL1U7V0dZV -1tjXXNfg2GTY6Nls2fHadtr724DcBdyK3RDdlt4c3qLfKd+v4DbgveFE4cziU+Lb42Pj6+Rz5PzlhOYN -5pbnH+ep6DLovOlG6dDqW+rl63Dr++yG7RHtnO4o7rTvQO/M8Fjw5fFy8f/yjPMZ86f0NPTC9VD13vZt -9vv3ivgZ+Kj5OPnH+lf65/t3/Af8mP0p/br+S/7c/23//9IrLC0uWiRjbGFzc25hbWVYJGNsYXNzZXNf -EBBOU0JpdG1hcEltYWdlUmVwoy0vMFpOU0ltYWdlUmVwWE5TT2JqZWN00issMjNXTlNBcnJheaIyMNIr -LDU2Xk5TTXV0YWJsZUFycmF5ozUyMNM4OQ86OzxXTlNXaGl0ZVxOU0NvbG9yU3BhY2VEMCAwABADgAzS -Kyw+P1dOU0NvbG9yoj4w0issQUJXTlNJbWFnZaJBMAAIABEAGgAkACkAMgA3AEkATABRAFMAYgBoAHUA -fACLAJIAnwCmAK4AsACyALQAuQC7AL0AxADJANQA1gDYANoA3wDiAOQA5gDoAO8BBgEiASQBJhOWE5sT -phOvE8ITxhPRE9oT3xPnE+oT7xP+FAIUCRQRFB4UIxQlFCcULBQ0FDcUPBREAAAAAAAAAgEAAAAAAAAA -QwAAAAAAAAAAAAAAAAAAFEc +S2V5ZWRBcmNoaXZlctEICVRyb290gAGuCwwbHCEaJissMzY5P0JVJG51bGzXDQ4PEBESExQVFhcYGRpW +TlNTaXplViRjbGFzc1xOU0ltYWdlRmxhZ3NWTlNSZXBzW05TVGludENvbG9yV05TQ29sb3JeTlNSZXNp +emluZ01vZGWAAoANEiDDAACAA4AAgAsQAFZ7MSwgMX3SHQ4eIFpOUy5vYmplY3RzoR+ABIAK0h0OIiWi +IySABYAGgAnTDicoKSoaXxAUTlNUSUZGUmVwcmVzZW50YXRpb25fEBlOU0ludGVybmFsTGF5b3V0RGly +ZWN0aW9ugAiAB08REmxNTQAqAAAACgAAABABAAADAAAAAQABAAABAQADAAAAAQABAAABAgADAAAAAgAI +AAgBAwADAAAAAQABAAABBgADAAAAAQABAAABCgADAAAAAQABAAABEQAEAAAAAQAAAAgBEgADAAAAAQAB +AAABFQADAAAAAQACAAABFgADAAAAAQABAAABFwAEAAAAAQAAAAIBHAADAAAAAQABAAABKAADAAAAAQAC +AAABUgADAAAAAQABAAABUwADAAAAAgABAAGHcwAHAAARnAAAANAAAAAAAAARnGFwcGwCAAAAbW50ckdS +QVlYWVogB9wACAAXAA8ALgAPYWNzcEFQUEwAAAAAbm9uZQAAAAAAAAAAAAAAAAAAAAAAAPbWAAEAAAAA +0y1hcHBsAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFZGVzYwAA +AMAAAAB5ZHNjbQAAATwAAAgaY3BydAAACVgAAAAjd3RwdAAACXwAAAAUa1RSQwAACZAAAAgMZGVzYwAA +AAAAAAAfR2VuZXJpYyBHcmF5IEdhbW1hIDIuMiBQcm9maWxlAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAG1s +dWMAAAAAAAAAHwAAAAxza1NLAAAALgAAAYRkYURLAAAAOgAAAbJjYUVTAAAAOAAAAex2aVZOAAAAQAAA +AiRwdEJSAAAASgAAAmR1a1VBAAAALAAAAq5mckZVAAAAPgAAAtpodUhVAAAANAAAAxh6aFRXAAAAGgAA +A0xrb0tSAAAAIgAAA2ZuYk5PAAAAOgAAA4hjc0NaAAAAKAAAA8JoZUlMAAAAJAAAA+pyb1JPAAAAKgAA +BA5kZURFAAAATgAABDhpdElUAAAATgAABIZzdlNFAAAAOAAABNR6aENOAAAAGgAABQxqYUpQAAAAJgAA +BSZlbEdSAAAAKgAABUxwdFBPAAAAUgAABXZubE5MAAAAQAAABchlc0VTAAAATAAABgh0aFRIAAAAMgAA +BlR0clRSAAAAJAAABoZmaUZJAAAARgAABqpockhSAAAAPgAABvBwbFBMAAAASgAABy5hckVHAAAALAAA +B3hydVJVAAAAOgAAB6RlblVTAAAAPAAAB94AVgFhAGUAbwBiAGUAYwBuAOEAIABzAGkAdgDhACAAZwBh +AG0AYQAgADIALAAyAEcAZQBuAGUAcgBpAHMAawAgAGcAcgDlACAAMgAsADIAIABnAGEAbQBtAGEALQBw +AHIAbwBmAGkAbABHAGEAbQBtAGEAIABkAGUAIABnAHIAaQBzAG8AcwAgAGcAZQBuAOgAcgBpAGMAYQAg +ADIALgAyAEMepQB1ACAAaADsAG4AaAAgAE0A4AB1ACAAeADhAG0AIABDAGgAdQBuAGcAIABHAGEAbQBt +AGEAIAAyAC4AMgBQAGUAcgBmAGkAbAAgAEcAZQBuAOkAcgBpAGMAbwAgAGQAYQAgAEcAYQBtAGEAIABk +AGUAIABDAGkAbgB6AGEAcwAgADIALAAyBBcEMAQzBDAEOwRMBD0EMAAgAEcAcgBhAHkALQQzBDAEPAQw +ACAAMgAuADIAUAByAG8AZgBpAGwAIABnAOkAbgDpAHIAaQBxAHUAZQAgAGcAcgBpAHMAIABnAGEAbQBt +AGEAIAAyACwAMgDBAGwAdABhAGwA4QBuAG8AcwAgAHMAegD8AHIAawBlACAAZwBhAG0AbQBhACAAMgAu +ADKQGnUocHCWjlFJXqYAMgAuADKCcl9pY8+P8Md8vBgAINaMwMkAIKwQucgAIAAyAC4AMgAg1QS4XNMM +x3wARwBlAG4AZQByAGkAcwBrACAAZwByAOUAIABnAGEAbQBtAGEAIAAyACwAMgAtAHAAcgBvAGYAaQBs +AE8AYgBlAGMAbgDhACABYQBlAGQA4QAgAGcAYQBtAGEAIAAyAC4AMgXSBdAF3gXUACAF0AXkBdUF6AAg +BdsF3AXcBdkAIAAyAC4AMgBHAGEAbQBhACAAZwByAGkAIABnAGUAbgBlAHIAaQBjAQMAIAAyACwAMgBB +AGwAbABnAGUAbQBlAGkAbgBlAHMAIABHAHIAYQB1AHMAdAB1AGYAZQBuAC0AUAByAG8AZgBpAGwAIABH +AGEAbQBtAGEAIAAyACwAMgBQAHIAbwBmAGkAbABvACAAZwByAGkAZwBpAG8AIABnAGUAbgBlAHIAaQBj +AG8AIABkAGUAbABsAGEAIABnAGEAbQBtAGEAIAAyACwAMgBHAGUAbgBlAHIAaQBzAGsAIABnAHIA5QAg +ADIALAAyACAAZwBhAG0AbQBhAHAAcgBvAGYAaQBsZm6QGnBwXqZ8+2VwADIALgAyY8+P8GWHTvZOAIIs +MLAw7DCkMKww8zDeACAAMgAuADIAIDDXMO0w1TChMKQw6wOTA7UDvQO5A7oDzAAgA5MDugPBA7kAIAOT +A6wDvAO8A7EAIAAyAC4AMgBQAGUAcgBmAGkAbAAgAGcAZQBuAOkAcgBpAGMAbwAgAGQAZQAgAGMAaQBu +AHoAZQBuAHQAbwBzACAAZABhACAARwBhAG0AbQBhACAAMgAsADIAQQBsAGcAZQBtAGUAZQBuACAAZwBy +AGkAagBzACAAZwBhAG0AbQBhACAAMgAsADIALQBwAHIAbwBmAGkAZQBsAFAAZQByAGYAaQBsACAAZwBl +AG4A6QByAGkAYwBvACAAZABlACAAZwBhAG0AbQBhACAAZABlACAAZwByAGkAcwBlAHMAIAAyACwAMg4j +DjEOBw4qDjUOQQ4BDiEOIQ4yDkAOAQ4jDiIOTA4XDjEOSA4nDkQOGwAgADIALgAyAEcAZQBuAGUAbAAg +AEcAcgBpACAARwBhAG0AYQAgADIALAAyAFkAbABlAGkAbgBlAG4AIABoAGEAcgBtAGEAYQBuACAAZwBh +AG0AbQBhACAAMgAsADIAIAAtAHAAcgBvAGYAaQBpAGwAaQBHAGUAbgBlAHIAaQENAGsAaQAgAEcAcgBh +AHkAIABHAGEAbQBtAGEAIAAyAC4AMgAgAHAAcgBvAGYAaQBsAFUAbgBpAHcAZQByAHMAYQBsAG4AeQAg +AHAAcgBvAGYAaQBsACAAcwB6AGEAcgBvAVsAYwBpACAAZwBhAG0AbQBhACAAMgAsADIGOgYnBkUGJwAg +ADIALgAyACAGRAZIBkYAIAYxBkUGJwYvBkoAIAY5BicGRQQeBDEESQQwBE8AIARBBDUEQAQwBE8AIAQz +BDAEPAQ8BDAAIAAyACwAMgAtBD8EQAQ+BEQEOAQ7BEwARwBlAG4AZQByAGkAYwAgAEcAcgBhAHkAIABH +AGEAbQBtAGEAIAAyAC4AMgAgAFAAcgBvAGYAaQBsAGUAAHRleHQAAAAAQ29weXJpZ2h0IEFwcGxlIElu +Yy4sIDIwMTIAAFhZWiAAAAAAAADzUQABAAAAARbMY3VydgAAAAAAAAQAAAAABQAKAA8AFAAZAB4AIwAo +AC0AMgA3ADsAQABFAEoATwBUAFkAXgBjAGgAbQByAHcAfACBAIYAiwCQAJUAmgCfAKQAqQCuALIAtwC8 +AMEAxgDLANAA1QDbAOAA5QDrAPAA9gD7AQEBBwENARMBGQEfASUBKwEyATgBPgFFAUwBUgFZAWABZwFu +AXUBfAGDAYsBkgGaAaEBqQGxAbkBwQHJAdEB2QHhAekB8gH6AgMCDAIUAh0CJgIvAjgCQQJLAlQCXQJn +AnECegKEAo4CmAKiAqwCtgLBAssC1QLgAusC9QMAAwsDFgMhAy0DOANDA08DWgNmA3IDfgOKA5YDogOu +A7oDxwPTA+AD7AP5BAYEEwQgBC0EOwRIBFUEYwRxBH4EjASaBKgEtgTEBNME4QTwBP4FDQUcBSsFOgVJ +BVgFZwV3BYYFlgWmBbUFxQXVBeUF9gYGBhYGJwY3BkgGWQZqBnsGjAadBq8GwAbRBuMG9QcHBxkHKwc9 +B08HYQd0B4YHmQesB78H0gflB/gICwgfCDIIRghaCG4IggiWCKoIvgjSCOcI+wkQCSUJOglPCWQJeQmP +CaQJugnPCeUJ+woRCicKPQpUCmoKgQqYCq4KxQrcCvMLCwsiCzkLUQtpC4ALmAuwC8gL4Qv5DBIMKgxD +DFwMdQyODKcMwAzZDPMNDQ0mDUANWg10DY4NqQ3DDd4N+A4TDi4OSQ5kDn8Omw62DtIO7g8JDyUPQQ9e +D3oPlg+zD88P7BAJECYQQxBhEH4QmxC5ENcQ9RETETERTxFtEYwRqhHJEegSBxImEkUSZBKEEqMSwxLj +EwMTIxNDE2MTgxOkE8UT5RQGFCcUSRRqFIsUrRTOFPAVEhU0FVYVeBWbFb0V4BYDFiYWSRZsFo8WshbW +FvoXHRdBF2UXiReuF9IX9xgbGEAYZRiKGK8Y1Rj6GSAZRRlrGZEZtxndGgQaKhpRGncanhrFGuwbFBs7 +G2MbihuyG9ocAhwqHFIcexyjHMwc9R0eHUcdcB2ZHcMd7B4WHkAeah6UHr4e6R8THz4faR+UH78f6iAV +IEEgbCCYIMQg8CEcIUghdSGhIc4h+yInIlUigiKvIt0jCiM4I2YjlCPCI/AkHyRNJHwkqyTaJQklOCVo +JZclxyX3JicmVyaHJrcm6CcYJ0kneierJ9woDSg/KHEooijUKQYpOClrKZ0p0CoCKjUqaCqbKs8rAis2 +K2krnSvRLAUsOSxuLKIs1y0MLUEtdi2rLeEuFi5MLoIuty7uLyQvWi+RL8cv/jA1MGwwpDDbMRIxSjGC +Mbox8jIqMmMymzLUMw0zRjN/M7gz8TQrNGU0njTYNRM1TTWHNcI1/TY3NnI2rjbpNyQ3YDecN9c4FDhQ +OIw4yDkFOUI5fzm8Ofk6Njp0OrI67zstO2s7qjvoPCc8ZTykPOM9Ij1hPaE94D4gPmA+oD7gPyE/YT+i +P+JAI0BkQKZA50EpQWpBrEHuQjBCckK1QvdDOkN9Q8BEA0RHRIpEzkUSRVVFmkXeRiJGZ0arRvBHNUd7 +R8BIBUhLSJFI10kdSWNJqUnwSjdKfUrESwxLU0uaS+JMKkxyTLpNAk1KTZNN3E4lTm5Ot08AT0lPk0/d +UCdQcVC7UQZRUFGbUeZSMVJ8UsdTE1NfU6pT9lRCVI9U21UoVXVVwlYPVlxWqVb3V0RXklfgWC9YfVjL +WRpZaVm4WgdaVlqmWvVbRVuVW+VcNVyGXNZdJ114XcleGl5sXr1fD19hX7NgBWBXYKpg/GFPYaJh9WJJ +Ypxi8GNDY5dj62RAZJRk6WU9ZZJl52Y9ZpJm6Gc9Z5Nn6Wg/aJZo7GlDaZpp8WpIap9q92tPa6dr/2xX +bK9tCG1gbbluEm5rbsRvHm94b9FwK3CGcOBxOnGVcfByS3KmcwFzXXO4dBR0cHTMdSh1hXXhdj52m3b4 +d1Z3s3gReG54zHkqeYl553pGeqV7BHtje8J8IXyBfOF9QX2hfgF+Yn7CfyN/hH/lgEeAqIEKgWuBzYIw +gpKC9INXg7qEHYSAhOOFR4Wrhg6GcobXhzuHn4gEiGmIzokziZmJ/opkisqLMIuWi/yMY4zKjTGNmI3/ +jmaOzo82j56QBpBukNaRP5GokhGSepLjk02TtpQglIqU9JVflcmWNJaflwqXdZfgmEyYuJkkmZCZ/Jpo +mtWbQpuvnByciZz3nWSd0p5Anq6fHZ+Ln/qgaaDYoUehtqImopajBqN2o+akVqTHpTilqaYapoum/adu +p+CoUqjEqTepqaocqo+rAqt1q+msXKzQrUStuK4trqGvFq+LsACwdbDqsWCx1rJLssKzOLOutCW0nLUT +tYq2AbZ5tvC3aLfguFm40blKucK6O7q1uy67p7whvJu9Fb2Pvgq+hL7/v3q/9cBwwOzBZ8Hjwl/C28NY +w9TEUcTOxUvFyMZGxsPHQce/yD3IvMk6ybnKOMq3yzbLtsw1zLXNNc21zjbOts83z7jQOdC60TzRvtI/ +0sHTRNPG1EnUy9VO1dHWVdbY11zX4Nhk2OjZbNnx2nba+9uA3AXcit0Q3ZbeHN6i3ynfr+A24L3hROHM +4lPi2+Nj4+vkc+T85YTmDeaW5x/nqegy6LzpRunQ6lvq5etw6/vshu0R7ZzuKO6070DvzPBY8OXxcvH/ +8ozzGfOn9DT0wvVQ9d72bfb794r4Gfio+Tj5x/pX+uf7d/wH/Jj9Kf26/kv+3P9t///SLS4vMFokY2xh +c3NuYW1lWCRjbGFzc2VzXxAQTlNCaXRtYXBJbWFnZVJlcKMvMTJaTlNJbWFnZVJlcFhOU09iamVjdNIt +LjQ1V05TQXJyYXmiNDLSLS43OF5OU011dGFibGVBcnJheaM3NDLTOjsOPD0+V05TV2hpdGVcTlNDb2xv +clNwYWNlRDAgMAAQA4AM0i0uQEFXTlNDb2xvcqJAMtItLkNEV05TSW1hZ2WiQzIACAARABoAJAApADIA +NwBJAEwAUQBTAGIAaAB3AH4AhQCSAJkApQCtALwAvgDAAMUAxwDJAMsAzQDUANkA5ADmAOgA6gDvAPIA +9AD2APgA/wEWATIBNAE2E6YTqxO2E78T0hPWE+ET6hPvE/cT+hP/FA4UEhQZFCEULhQzFDUUNxQ8FEQU +RxRMFFQAAAAAAAACAQAAAAAAAABFAAAAAAAAAAAAAAAAAAAUVw diff --git a/i18n/src/main/resources/tr.lproj/Command.xib b/i18n/src/main/resources/tr.lproj/Command.xib index be58f9c01a..358cd151ed 100644 --- a/i18n/src/main/resources/tr.lproj/Command.xib +++ b/i18n/src/main/resources/tr.lproj/Command.xib @@ -1,7 +1,7 @@ - + - + diff --git a/i18n/src/main/resources/tr.lproj/Connection.xib b/i18n/src/main/resources/tr.lproj/Connection.xib index 8941e41a73..a7919c22ee 100644 --- a/i18n/src/main/resources/tr.lproj/Connection.xib +++ b/i18n/src/main/resources/tr.lproj/Connection.xib @@ -1,7 +1,7 @@ - + - + @@ -76,7 +76,7 @@ - + diff --git a/i18n/src/main/resources/tr.lproj/Info.xib b/i18n/src/main/resources/tr.lproj/Info.xib index 107f7d0d33..3852f8fce5 100644 --- a/i18n/src/main/resources/tr.lproj/Info.xib +++ b/i18n/src/main/resources/tr.lproj/Info.xib @@ -1,7 +1,7 @@ - + - + @@ -89,7 +89,7 @@ - + @@ -1017,21 +1017,21 @@ - + - + - + @@ -1121,21 +1121,21 @@ - + - + - + @@ -1250,27 +1250,27 @@ diff --git a/i18n/src/main/resources/tr.lproj/Login.strings b/i18n/src/main/resources/tr.lproj/Login.strings index 0afd9ac44b..4f9b422914 100644 Binary files a/i18n/src/main/resources/tr.lproj/Login.strings and b/i18n/src/main/resources/tr.lproj/Login.strings differ diff --git a/i18n/src/main/resources/tr.lproj/Main.xib b/i18n/src/main/resources/tr.lproj/Main.xib index 17762f1be4..fa02bfda83 100644 --- a/i18n/src/main/resources/tr.lproj/Main.xib +++ b/i18n/src/main/resources/tr.lproj/Main.xib @@ -1,7 +1,7 @@ - + - + @@ -94,7 +94,7 @@ - + @@ -159,19 +159,19 @@ - + - + - + @@ -321,12 +321,12 @@ CA - + - + @@ -566,6 +566,10 @@ CA - + + + + + diff --git a/i18n/src/main/resources/tr.lproj/Preferences.xib b/i18n/src/main/resources/tr.lproj/Preferences.xib index b44a808869..5636a77bac 100644 --- a/i18n/src/main/resources/tr.lproj/Preferences.xib +++ b/i18n/src/main/resources/tr.lproj/Preferences.xib @@ -1,7 +1,7 @@ - + - + @@ -266,7 +266,7 @@ - + @@ -307,7 +307,7 @@ - + @@ -538,7 +538,7 @@ - + @@ -578,7 +578,7 @@ - + @@ -613,7 +613,7 @@ - + @@ -642,7 +642,7 @@ - + @@ -1218,7 +1218,7 @@ - + @@ -1250,7 +1250,7 @@ - + @@ -1290,7 +1290,7 @@ - + @@ -1363,7 +1363,7 @@ - + @@ -1558,7 +1558,7 @@ - + @@ -1618,7 +1618,7 @@ - + @@ -1643,7 +1643,7 @@ - + @@ -1685,7 +1685,7 @@ - + @@ -1713,7 +1713,7 @@ - + @@ -1741,7 +1741,7 @@ - + @@ -1770,7 +1770,7 @@ - + @@ -1805,7 +1805,7 @@ - + @@ -1888,7 +1888,7 @@ - + @@ -1947,7 +1947,7 @@ - + @@ -1975,7 +1975,7 @@ - + @@ -2004,7 +2004,7 @@ - + diff --git a/i18n/src/main/resources/tr.lproj/Profiles.xib b/i18n/src/main/resources/tr.lproj/Profiles.xib index 7fd4090e2d..64848ec11b 100644 --- a/i18n/src/main/resources/tr.lproj/Profiles.xib +++ b/i18n/src/main/resources/tr.lproj/Profiles.xib @@ -1,7 +1,7 @@ - + - + diff --git a/i18n/src/main/resources/tr.lproj/Prompt.xib b/i18n/src/main/resources/tr.lproj/Prompt.xib index 8770e301bd..a194362224 100644 --- a/i18n/src/main/resources/tr.lproj/Prompt.xib +++ b/i18n/src/main/resources/tr.lproj/Prompt.xib @@ -1,7 +1,7 @@ - + - + @@ -95,7 +95,7 @@ Gw - + diff --git a/i18n/src/main/resources/tr.lproj/S3.strings b/i18n/src/main/resources/tr.lproj/S3.strings index b5388b88ac..90d3ba5f09 100644 Binary files a/i18n/src/main/resources/tr.lproj/S3.strings and b/i18n/src/main/resources/tr.lproj/S3.strings differ diff --git a/i18n/src/main/resources/tr.lproj/Updater.strings b/i18n/src/main/resources/tr.lproj/Updater.strings index d0fcd8ad36..5c0e711e3e 100644 Binary files a/i18n/src/main/resources/tr.lproj/Updater.strings and b/i18n/src/main/resources/tr.lproj/Updater.strings differ diff --git a/i18n/src/main/resources/uk.lproj/Bookmark.strings b/i18n/src/main/resources/uk.lproj/Bookmark.strings index 9676b5fded..4701640bd9 100644 Binary files a/i18n/src/main/resources/uk.lproj/Bookmark.strings and b/i18n/src/main/resources/uk.lproj/Bookmark.strings differ diff --git a/i18n/src/main/resources/uk.lproj/Bookmark.strings.1 b/i18n/src/main/resources/uk.lproj/Bookmark.strings.1 index 3b41a156a2..c5227d9fa5 100644 Binary files a/i18n/src/main/resources/uk.lproj/Bookmark.strings.1 and b/i18n/src/main/resources/uk.lproj/Bookmark.strings.1 differ diff --git a/i18n/src/main/resources/uk.lproj/Bookmark.xib b/i18n/src/main/resources/uk.lproj/Bookmark.xib index a5508228a7..b75428e97c 100644 --- a/i18n/src/main/resources/uk.lproj/Bookmark.xib +++ b/i18n/src/main/resources/uk.lproj/Bookmark.xib @@ -1,7 +1,7 @@ - + - + @@ -92,7 +92,7 @@ - + @@ -440,7 +440,7 @@ - + @@ -489,95 +489,95 @@ YnBsaXN0MDDUAQIDBAUGBwpYJHZlcnNpb25ZJGFyY2hpdmVyVCR0b3BYJG9iamVjdHMSAAGGoF8QD05T -S2V5ZWRBcmNoaXZlctEICVRyb290gAGuCwwZGh8UJCkqMTQ3PUBVJG51bGzWDQ4PEBESExQVFhcYVk5T -U2l6ZV5OU1Jlc2l6aW5nTW9kZVYkY2xhc3NcTlNJbWFnZUZsYWdzVk5TUmVwc1dOU0NvbG9ygAIQAIAN -EiDDAACAA4ALVnsxLCAxfdIbDxweWk5TLm9iamVjdHOhHYAEgArSGw8gI6IhIoAFgAaACdMPJSYnKBRf -EBROU1RJRkZSZXByZXNlbnRhdGlvbl8QGU5TSW50ZXJuYWxMYXlvdXREaXJlY3Rpb26ACIAHTxESbE1N -ACoAAAAKAAAAEAEAAAMAAAABAAEAAAEBAAMAAAABAAEAAAECAAMAAAACAAgACAEDAAMAAAABAAEAAAEG -AAMAAAABAAEAAAEKAAMAAAABAAEAAAERAAQAAAABAAAACAESAAMAAAABAAEAAAEVAAMAAAABAAIAAAEW -AAMAAAABAAEAAAEXAAQAAAABAAAAAgEcAAMAAAABAAEAAAEoAAMAAAABAAIAAAFSAAMAAAABAAEAAAFT -AAMAAAACAAEAAYdzAAcAABGcAAAA0AAAAAAAABGcYXBwbAIAAABtbnRyR1JBWVhZWiAH3AAIABcADwAu -AA9hY3NwQVBQTAAAAABub25lAAAAAAAAAAAAAAAAAAAAAAAA9tYAAQAAAADTLWFwcGwAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAVkZXNjAAAAwAAAAHlkc2NtAAABPAAA -CBpjcHJ0AAAJWAAAACN3dHB0AAAJfAAAABRrVFJDAAAJkAAACAxkZXNjAAAAAAAAAB9HZW5lcmljIEdy -YXkgR2FtbWEgMi4yIFByb2ZpbGUAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAbWx1YwAAAAAAAAAfAAAADHNr -U0sAAAAuAAABhGRhREsAAAA6AAABsmNhRVMAAAA4AAAB7HZpVk4AAABAAAACJHB0QlIAAABKAAACZHVr -VUEAAAAsAAACrmZyRlUAAAA+AAAC2mh1SFUAAAA0AAADGHpoVFcAAAAaAAADTGtvS1IAAAAiAAADZm5i -Tk8AAAA6AAADiGNzQ1oAAAAoAAADwmhlSUwAAAAkAAAD6nJvUk8AAAAqAAAEDmRlREUAAABOAAAEOGl0 -SVQAAABOAAAEhnN2U0UAAAA4AAAE1HpoQ04AAAAaAAAFDGphSlAAAAAmAAAFJmVsR1IAAAAqAAAFTHB0 -UE8AAABSAAAFdm5sTkwAAABAAAAFyGVzRVMAAABMAAAGCHRoVEgAAAAyAAAGVHRyVFIAAAAkAAAGhmZp -RkkAAABGAAAGqmhySFIAAAA+AAAG8HBsUEwAAABKAAAHLmFyRUcAAAAsAAAHeHJ1UlUAAAA6AAAHpGVu -VVMAAAA8AAAH3gBWAWEAZQBvAGIAZQBjAG4A4QAgAHMAaQB2AOEAIABnAGEAbQBhACAAMgAsADIARwBl -AG4AZQByAGkAcwBrACAAZwByAOUAIAAyACwAMgAgAGcAYQBtAG0AYQAtAHAAcgBvAGYAaQBsAEcAYQBt -AG0AYQAgAGQAZQAgAGcAcgBpAHMAbwBzACAAZwBlAG4A6AByAGkAYwBhACAAMgAuADIAQx6lAHUAIABo -AOwAbgBoACAATQDgAHUAIAB4AOEAbQAgAEMAaAB1AG4AZwAgAEcAYQBtAG0AYQAgADIALgAyAFAAZQBy -AGYAaQBsACAARwBlAG4A6QByAGkAYwBvACAAZABhACAARwBhAG0AYQAgAGQAZQAgAEMAaQBuAHoAYQBz -ACAAMgAsADIEFwQwBDMEMAQ7BEwEPQQwACAARwByAGEAeQAtBDMEMAQ8BDAAIAAyAC4AMgBQAHIAbwBm -AGkAbAAgAGcA6QBuAOkAcgBpAHEAdQBlACAAZwByAGkAcwAgAGcAYQBtAG0AYQAgADIALAAyAMEAbAB0 -AGEAbADhAG4AbwBzACAAcwB6APwAcgBrAGUAIABnAGEAbQBtAGEAIAAyAC4AMpAadShwcJaOUUlepgAy -AC4AMoJyX2ljz4/wx3y8GAAg1ozAyQAgrBC5yAAgADIALgAyACDVBLhc0wzHfABHAGUAbgBlAHIAaQBz -AGsAIABnAHIA5QAgAGcAYQBtAG0AYQAgADIALAAyAC0AcAByAG8AZgBpAGwATwBiAGUAYwBuAOEAIAFh -AGUAZADhACAAZwBhAG0AYQAgADIALgAyBdIF0AXeBdQAIAXQBeQF1QXoACAF2wXcBdwF2QAgADIALgAy -AEcAYQBtAGEAIABnAHIAaQAgAGcAZQBuAGUAcgBpAGMBAwAgADIALAAyAEEAbABsAGcAZQBtAGUAaQBu -AGUAcwAgAEcAcgBhAHUAcwB0AHUAZgBlAG4ALQBQAHIAbwBmAGkAbAAgAEcAYQBtAG0AYQAgADIALAAy -AFAAcgBvAGYAaQBsAG8AIABnAHIAaQBnAGkAbwAgAGcAZQBuAGUAcgBpAGMAbwAgAGQAZQBsAGwAYQAg -AGcAYQBtAG0AYQAgADIALAAyAEcAZQBuAGUAcgBpAHMAawAgAGcAcgDlACAAMgAsADIAIABnAGEAbQBt -AGEAcAByAG8AZgBpAGxmbpAacHBepnz7ZXAAMgAuADJjz4/wZYdO9k4AgiwwsDDsMKQwrDDzMN4AIAAy -AC4AMgAgMNcw7TDVMKEwpDDrA5MDtQO9A7kDugPMACADkwO6A8EDuQAgA5MDrAO8A7wDsQAgADIALgAy -AFAAZQByAGYAaQBsACAAZwBlAG4A6QByAGkAYwBvACAAZABlACAAYwBpAG4AegBlAG4AdABvAHMAIABk -AGEAIABHAGEAbQBtAGEAIAAyACwAMgBBAGwAZwBlAG0AZQBlAG4AIABnAHIAaQBqAHMAIABnAGEAbQBt -AGEAIAAyACwAMgAtAHAAcgBvAGYAaQBlAGwAUABlAHIAZgBpAGwAIABnAGUAbgDpAHIAaQBjAG8AIABk -AGUAIABnAGEAbQBtAGEAIABkAGUAIABnAHIAaQBzAGUAcwAgADIALAAyDiMOMQ4HDioONQ5BDgEOIQ4h -DjIOQA4BDiMOIg5MDhcOMQ5IDicORA4bACAAMgAuADIARwBlAG4AZQBsACAARwByAGkAIABHAGEAbQBh -ACAAMgAsADIAWQBsAGUAaQBuAGUAbgAgAGgAYQByAG0AYQBhAG4AIABnAGEAbQBtAGEAIAAyACwAMgAg -AC0AcAByAG8AZgBpAGkAbABpAEcAZQBuAGUAcgBpAQ0AawBpACAARwByAGEAeQAgAEcAYQBtAG0AYQAg -ADIALgAyACAAcAByAG8AZgBpAGwAVQBuAGkAdwBlAHIAcwBhAGwAbgB5ACAAcAByAG8AZgBpAGwAIABz -AHoAYQByAG8BWwBjAGkAIABnAGEAbQBtAGEAIAAyACwAMgY6BicGRQYnACAAMgAuADIAIAZEBkgGRgAg -BjEGRQYnBi8GSgAgBjkGJwZFBB4EMQRJBDAETwAgBEEENQRABDAETwAgBDMEMAQ8BDwEMAAgADIALAAy -AC0EPwRABD4ERAQ4BDsETABHAGUAbgBlAHIAaQBjACAARwByAGEAeQAgAEcAYQBtAG0AYQAgADIALgAy -ACAAUAByAG8AZgBpAGwAZQAAdGV4dAAAAABDb3B5cmlnaHQgQXBwbGUgSW5jLiwgMjAxMgAAWFlaIAAA -AAAAAPNRAAEAAAABFsxjdXJ2AAAAAAAABAAAAAAFAAoADwAUABkAHgAjACgALQAyADcAOwBAAEUASgBP -AFQAWQBeAGMAaABtAHIAdwB8AIEAhgCLAJAAlQCaAJ8ApACpAK4AsgC3ALwAwQDGAMsA0ADVANsA4ADl -AOsA8AD2APsBAQEHAQ0BEwEZAR8BJQErATIBOAE+AUUBTAFSAVkBYAFnAW4BdQF8AYMBiwGSAZoBoQGp -AbEBuQHBAckB0QHZAeEB6QHyAfoCAwIMAhQCHQImAi8COAJBAksCVAJdAmcCcQJ6AoQCjgKYAqICrAK2 -AsECywLVAuAC6wL1AwADCwMWAyEDLQM4A0MDTwNaA2YDcgN+A4oDlgOiA64DugPHA9MD4APsA/kEBgQT -BCAELQQ7BEgEVQRjBHEEfgSMBJoEqAS2BMQE0wThBPAE/gUNBRwFKwU6BUkFWAVnBXcFhgWWBaYFtQXF -BdUF5QX2BgYGFgYnBjcGSAZZBmoGewaMBp0GrwbABtEG4wb1BwcHGQcrBz0HTwdhB3QHhgeZB6wHvwfS -B+UH+AgLCB8IMghGCFoIbgiCCJYIqgi+CNII5wj7CRAJJQk6CU8JZAl5CY8JpAm6Cc8J5Qn7ChEKJwo9 -ClQKagqBCpgKrgrFCtwK8wsLCyILOQtRC2kLgAuYC7ALyAvhC/kMEgwqDEMMXAx1DI4MpwzADNkM8w0N -DSYNQA1aDXQNjg2pDcMN3g34DhMOLg5JDmQOfw6bDrYO0g7uDwkPJQ9BD14Peg+WD7MPzw/sEAkQJhBD -EGEQfhCbELkQ1xD1ERMRMRFPEW0RjBGqEckR6BIHEiYSRRJkEoQSoxLDEuMTAxMjE0MTYxODE6QTxRPl -FAYUJxRJFGoUixStFM4U8BUSFTQVVhV4FZsVvRXgFgMWJhZJFmwWjxayFtYW+hcdF0EXZReJF64X0hf3 -GBsYQBhlGIoYrxjVGPoZIBlFGWsZkRm3Gd0aBBoqGlEadxqeGsUa7BsUGzsbYxuKG7Ib2hwCHCocUhx7 -HKMczBz1HR4dRx1wHZkdwx3sHhYeQB5qHpQevh7pHxMfPh9pH5Qfvx/qIBUgQSBsIJggxCDwIRwhSCF1 -IaEhziH7IiciVSKCIq8i3SMKIzgjZiOUI8Ij8CQfJE0kfCSrJNolCSU4JWgllyXHJfcmJyZXJocmtybo -JxgnSSd6J6sn3CgNKD8ocSiiKNQpBik4KWspnSnQKgIqNSpoKpsqzysCKzYraSudK9EsBSw5LG4soizX -LQwtQS12Last4S4WLkwugi63Lu4vJC9aL5Evxy/+MDUwbDCkMNsxEjFKMYIxujHyMioyYzKbMtQzDTNG -M38zuDPxNCs0ZTSeNNg1EzVNNYc1wjX9Njc2cjauNuk3JDdgN5w31zgUOFA4jDjIOQU5Qjl/Obw5+To2 -OnQ6sjrvOy07azuqO+g8JzxlPKQ84z0iPWE9oT3gPiA+YD6gPuA/IT9hP6I/4kAjQGRApkDnQSlBakGs -Qe5CMEJyQrVC90M6Q31DwEQDREdEikTORRJFVUWaRd5GIkZnRqtG8Ec1R3tHwEgFSEtIkUjXSR1JY0mp -SfBKN0p9SsRLDEtTS5pL4kwqTHJMuk0CTUpNk03cTiVObk63TwBPSU+TT91QJ1BxULtRBlFQUZtR5lIx -UnxSx1MTU19TqlP2VEJUj1TbVShVdVXCVg9WXFapVvdXRFeSV+BYL1h9WMtZGllpWbhaB1pWWqZa9VtF -W5Vb5Vw1XIZc1l0nXXhdyV4aXmxevV8PX2Ffs2AFYFdgqmD8YU9homH1YklinGLwY0Njl2PrZEBklGTp -ZT1lkmXnZj1mkmboZz1nk2fpaD9olmjsaUNpmmnxakhqn2r3a09rp2v/bFdsr20IbWBtuW4SbmtuxG8e -b3hv0XArcIZw4HE6cZVx8HJLcqZzAXNdc7h0FHRwdMx1KHWFdeF2Pnabdvh3VnezeBF4bnjMeSp5iXnn -ekZ6pXsEe2N7wnwhfIF84X1BfaF+AX5ifsJ/I3+Ef+WAR4CogQqBa4HNgjCCkoL0g1eDuoQdhICE44VH -hauGDoZyhteHO4efiASIaYjOiTOJmYn+imSKyoswi5aL/IxjjMqNMY2Yjf+OZo7OjzaPnpAGkG6Q1pE/ -kaiSEZJ6kuOTTZO2lCCUipT0lV+VyZY0lp+XCpd1l+CYTJi4mSSZkJn8mmia1ZtCm6+cHJyJnPedZJ3S -nkCerp8dn4uf+qBpoNihR6G2oiailqMGo3aj5qRWpMelOKWpphqmi6b9p26n4KhSqMSpN6mpqhyqj6sC -q3Wr6axcrNCtRK24ri2uoa8Wr4uwALB1sOqxYLHWskuywrM4s660JbSctRO1irYBtnm28Ldot+C4WbjR -uUq5wro7urW7LrunvCG8m70VvY++Cr6Evv+/er/1wHDA7MFnwePCX8Lbw1jD1MRRxM7FS8XIxkbGw8dB -x7/IPci8yTrJuco4yrfLNsu2zDXMtc01zbXONs62zzfPuNA50LrRPNG+0j/SwdNE08bUSdTL1U7V0dZV -1tjXXNfg2GTY6Nls2fHadtr724DcBdyK3RDdlt4c3qLfKd+v4DbgveFE4cziU+Lb42Pj6+Rz5PzlhOYN -5pbnH+ep6DLovOlG6dDqW+rl63Dr++yG7RHtnO4o7rTvQO/M8Fjw5fFy8f/yjPMZ86f0NPTC9VD13vZt -9vv3ivgZ+Kj5OPnH+lf65/t3/Af8mP0p/br+S/7c/23//9IrLC0uWiRjbGFzc25hbWVYJGNsYXNzZXNf -EBBOU0JpdG1hcEltYWdlUmVwoy0vMFpOU0ltYWdlUmVwWE5TT2JqZWN00issMjNXTlNBcnJheaIyMNIr -LDU2Xk5TTXV0YWJsZUFycmF5ozUyMNM4OQ86OzxXTlNXaGl0ZVxOU0NvbG9yU3BhY2VEMCAwABADgAzS -Kyw+P1dOU0NvbG9yoj4w0issQUJXTlNJbWFnZaJBMAAIABEAGgAkACkAMgA3AEkATABRAFMAYgBoAHUA -fACLAJIAnwCmAK4AsACyALQAuQC7AL0AxADJANQA1gDYANoA3wDiAOQA5gDoAO8BBgEiASQBJhOWE5sT -phOvE8ITxhPRE9oT3xPnE+oT7xP+FAIUCRQRFB4UIxQlFCcULBQ0FDcUPBREAAAAAAAAAgEAAAAAAAAA -QwAAAAAAAAAAAAAAAAAAFEc +S2V5ZWRBcmNoaXZlctEICVRyb290gAGuCwwbHCEaJissMzY5P0JVJG51bGzXDQ4PEBESExQVFhcYGRpW +TlNTaXplViRjbGFzc1xOU0ltYWdlRmxhZ3NWTlNSZXBzW05TVGludENvbG9yV05TQ29sb3JeTlNSZXNp +emluZ01vZGWAAoANEiDDAACAA4AAgAsQAFZ7MSwgMX3SHQ4eIFpOUy5vYmplY3RzoR+ABIAK0h0OIiWi +IySABYAGgAnTDicoKSoaXxAUTlNUSUZGUmVwcmVzZW50YXRpb25fEBlOU0ludGVybmFsTGF5b3V0RGly +ZWN0aW9ugAiAB08REmxNTQAqAAAACgAAABABAAADAAAAAQABAAABAQADAAAAAQABAAABAgADAAAAAgAI +AAgBAwADAAAAAQABAAABBgADAAAAAQABAAABCgADAAAAAQABAAABEQAEAAAAAQAAAAgBEgADAAAAAQAB +AAABFQADAAAAAQACAAABFgADAAAAAQABAAABFwAEAAAAAQAAAAIBHAADAAAAAQABAAABKAADAAAAAQAC +AAABUgADAAAAAQABAAABUwADAAAAAgABAAGHcwAHAAARnAAAANAAAAAAAAARnGFwcGwCAAAAbW50ckdS +QVlYWVogB9wACAAXAA8ALgAPYWNzcEFQUEwAAAAAbm9uZQAAAAAAAAAAAAAAAAAAAAAAAPbWAAEAAAAA +0y1hcHBsAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFZGVzYwAA +AMAAAAB5ZHNjbQAAATwAAAgaY3BydAAACVgAAAAjd3RwdAAACXwAAAAUa1RSQwAACZAAAAgMZGVzYwAA +AAAAAAAfR2VuZXJpYyBHcmF5IEdhbW1hIDIuMiBQcm9maWxlAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAG1s +dWMAAAAAAAAAHwAAAAxza1NLAAAALgAAAYRkYURLAAAAOgAAAbJjYUVTAAAAOAAAAex2aVZOAAAAQAAA +AiRwdEJSAAAASgAAAmR1a1VBAAAALAAAAq5mckZVAAAAPgAAAtpodUhVAAAANAAAAxh6aFRXAAAAGgAA +A0xrb0tSAAAAIgAAA2ZuYk5PAAAAOgAAA4hjc0NaAAAAKAAAA8JoZUlMAAAAJAAAA+pyb1JPAAAAKgAA +BA5kZURFAAAATgAABDhpdElUAAAATgAABIZzdlNFAAAAOAAABNR6aENOAAAAGgAABQxqYUpQAAAAJgAA +BSZlbEdSAAAAKgAABUxwdFBPAAAAUgAABXZubE5MAAAAQAAABchlc0VTAAAATAAABgh0aFRIAAAAMgAA +BlR0clRSAAAAJAAABoZmaUZJAAAARgAABqpockhSAAAAPgAABvBwbFBMAAAASgAABy5hckVHAAAALAAA +B3hydVJVAAAAOgAAB6RlblVTAAAAPAAAB94AVgFhAGUAbwBiAGUAYwBuAOEAIABzAGkAdgDhACAAZwBh +AG0AYQAgADIALAAyAEcAZQBuAGUAcgBpAHMAawAgAGcAcgDlACAAMgAsADIAIABnAGEAbQBtAGEALQBw +AHIAbwBmAGkAbABHAGEAbQBtAGEAIABkAGUAIABnAHIAaQBzAG8AcwAgAGcAZQBuAOgAcgBpAGMAYQAg +ADIALgAyAEMepQB1ACAAaADsAG4AaAAgAE0A4AB1ACAAeADhAG0AIABDAGgAdQBuAGcAIABHAGEAbQBt +AGEAIAAyAC4AMgBQAGUAcgBmAGkAbAAgAEcAZQBuAOkAcgBpAGMAbwAgAGQAYQAgAEcAYQBtAGEAIABk +AGUAIABDAGkAbgB6AGEAcwAgADIALAAyBBcEMAQzBDAEOwRMBD0EMAAgAEcAcgBhAHkALQQzBDAEPAQw +ACAAMgAuADIAUAByAG8AZgBpAGwAIABnAOkAbgDpAHIAaQBxAHUAZQAgAGcAcgBpAHMAIABnAGEAbQBt +AGEAIAAyACwAMgDBAGwAdABhAGwA4QBuAG8AcwAgAHMAegD8AHIAawBlACAAZwBhAG0AbQBhACAAMgAu +ADKQGnUocHCWjlFJXqYAMgAuADKCcl9pY8+P8Md8vBgAINaMwMkAIKwQucgAIAAyAC4AMgAg1QS4XNMM +x3wARwBlAG4AZQByAGkAcwBrACAAZwByAOUAIABnAGEAbQBtAGEAIAAyACwAMgAtAHAAcgBvAGYAaQBs +AE8AYgBlAGMAbgDhACABYQBlAGQA4QAgAGcAYQBtAGEAIAAyAC4AMgXSBdAF3gXUACAF0AXkBdUF6AAg +BdsF3AXcBdkAIAAyAC4AMgBHAGEAbQBhACAAZwByAGkAIABnAGUAbgBlAHIAaQBjAQMAIAAyACwAMgBB +AGwAbABnAGUAbQBlAGkAbgBlAHMAIABHAHIAYQB1AHMAdAB1AGYAZQBuAC0AUAByAG8AZgBpAGwAIABH +AGEAbQBtAGEAIAAyACwAMgBQAHIAbwBmAGkAbABvACAAZwByAGkAZwBpAG8AIABnAGUAbgBlAHIAaQBj +AG8AIABkAGUAbABsAGEAIABnAGEAbQBtAGEAIAAyACwAMgBHAGUAbgBlAHIAaQBzAGsAIABnAHIA5QAg +ADIALAAyACAAZwBhAG0AbQBhAHAAcgBvAGYAaQBsZm6QGnBwXqZ8+2VwADIALgAyY8+P8GWHTvZOAIIs +MLAw7DCkMKww8zDeACAAMgAuADIAIDDXMO0w1TChMKQw6wOTA7UDvQO5A7oDzAAgA5MDugPBA7kAIAOT +A6wDvAO8A7EAIAAyAC4AMgBQAGUAcgBmAGkAbAAgAGcAZQBuAOkAcgBpAGMAbwAgAGQAZQAgAGMAaQBu +AHoAZQBuAHQAbwBzACAAZABhACAARwBhAG0AbQBhACAAMgAsADIAQQBsAGcAZQBtAGUAZQBuACAAZwBy +AGkAagBzACAAZwBhAG0AbQBhACAAMgAsADIALQBwAHIAbwBmAGkAZQBsAFAAZQByAGYAaQBsACAAZwBl +AG4A6QByAGkAYwBvACAAZABlACAAZwBhAG0AbQBhACAAZABlACAAZwByAGkAcwBlAHMAIAAyACwAMg4j +DjEOBw4qDjUOQQ4BDiEOIQ4yDkAOAQ4jDiIOTA4XDjEOSA4nDkQOGwAgADIALgAyAEcAZQBuAGUAbAAg +AEcAcgBpACAARwBhAG0AYQAgADIALAAyAFkAbABlAGkAbgBlAG4AIABoAGEAcgBtAGEAYQBuACAAZwBh +AG0AbQBhACAAMgAsADIAIAAtAHAAcgBvAGYAaQBpAGwAaQBHAGUAbgBlAHIAaQENAGsAaQAgAEcAcgBh +AHkAIABHAGEAbQBtAGEAIAAyAC4AMgAgAHAAcgBvAGYAaQBsAFUAbgBpAHcAZQByAHMAYQBsAG4AeQAg +AHAAcgBvAGYAaQBsACAAcwB6AGEAcgBvAVsAYwBpACAAZwBhAG0AbQBhACAAMgAsADIGOgYnBkUGJwAg +ADIALgAyACAGRAZIBkYAIAYxBkUGJwYvBkoAIAY5BicGRQQeBDEESQQwBE8AIARBBDUEQAQwBE8AIAQz +BDAEPAQ8BDAAIAAyACwAMgAtBD8EQAQ+BEQEOAQ7BEwARwBlAG4AZQByAGkAYwAgAEcAcgBhAHkAIABH +AGEAbQBtAGEAIAAyAC4AMgAgAFAAcgBvAGYAaQBsAGUAAHRleHQAAAAAQ29weXJpZ2h0IEFwcGxlIElu +Yy4sIDIwMTIAAFhZWiAAAAAAAADzUQABAAAAARbMY3VydgAAAAAAAAQAAAAABQAKAA8AFAAZAB4AIwAo +AC0AMgA3ADsAQABFAEoATwBUAFkAXgBjAGgAbQByAHcAfACBAIYAiwCQAJUAmgCfAKQAqQCuALIAtwC8 +AMEAxgDLANAA1QDbAOAA5QDrAPAA9gD7AQEBBwENARMBGQEfASUBKwEyATgBPgFFAUwBUgFZAWABZwFu +AXUBfAGDAYsBkgGaAaEBqQGxAbkBwQHJAdEB2QHhAekB8gH6AgMCDAIUAh0CJgIvAjgCQQJLAlQCXQJn +AnECegKEAo4CmAKiAqwCtgLBAssC1QLgAusC9QMAAwsDFgMhAy0DOANDA08DWgNmA3IDfgOKA5YDogOu +A7oDxwPTA+AD7AP5BAYEEwQgBC0EOwRIBFUEYwRxBH4EjASaBKgEtgTEBNME4QTwBP4FDQUcBSsFOgVJ +BVgFZwV3BYYFlgWmBbUFxQXVBeUF9gYGBhYGJwY3BkgGWQZqBnsGjAadBq8GwAbRBuMG9QcHBxkHKwc9 +B08HYQd0B4YHmQesB78H0gflB/gICwgfCDIIRghaCG4IggiWCKoIvgjSCOcI+wkQCSUJOglPCWQJeQmP +CaQJugnPCeUJ+woRCicKPQpUCmoKgQqYCq4KxQrcCvMLCwsiCzkLUQtpC4ALmAuwC8gL4Qv5DBIMKgxD +DFwMdQyODKcMwAzZDPMNDQ0mDUANWg10DY4NqQ3DDd4N+A4TDi4OSQ5kDn8Omw62DtIO7g8JDyUPQQ9e +D3oPlg+zD88P7BAJECYQQxBhEH4QmxC5ENcQ9RETETERTxFtEYwRqhHJEegSBxImEkUSZBKEEqMSwxLj +EwMTIxNDE2MTgxOkE8UT5RQGFCcUSRRqFIsUrRTOFPAVEhU0FVYVeBWbFb0V4BYDFiYWSRZsFo8WshbW +FvoXHRdBF2UXiReuF9IX9xgbGEAYZRiKGK8Y1Rj6GSAZRRlrGZEZtxndGgQaKhpRGncanhrFGuwbFBs7 +G2MbihuyG9ocAhwqHFIcexyjHMwc9R0eHUcdcB2ZHcMd7B4WHkAeah6UHr4e6R8THz4faR+UH78f6iAV +IEEgbCCYIMQg8CEcIUghdSGhIc4h+yInIlUigiKvIt0jCiM4I2YjlCPCI/AkHyRNJHwkqyTaJQklOCVo +JZclxyX3JicmVyaHJrcm6CcYJ0kneierJ9woDSg/KHEooijUKQYpOClrKZ0p0CoCKjUqaCqbKs8rAis2 +K2krnSvRLAUsOSxuLKIs1y0MLUEtdi2rLeEuFi5MLoIuty7uLyQvWi+RL8cv/jA1MGwwpDDbMRIxSjGC +Mbox8jIqMmMymzLUMw0zRjN/M7gz8TQrNGU0njTYNRM1TTWHNcI1/TY3NnI2rjbpNyQ3YDecN9c4FDhQ +OIw4yDkFOUI5fzm8Ofk6Njp0OrI67zstO2s7qjvoPCc8ZTykPOM9Ij1hPaE94D4gPmA+oD7gPyE/YT+i +P+JAI0BkQKZA50EpQWpBrEHuQjBCckK1QvdDOkN9Q8BEA0RHRIpEzkUSRVVFmkXeRiJGZ0arRvBHNUd7 +R8BIBUhLSJFI10kdSWNJqUnwSjdKfUrESwxLU0uaS+JMKkxyTLpNAk1KTZNN3E4lTm5Ot08AT0lPk0/d +UCdQcVC7UQZRUFGbUeZSMVJ8UsdTE1NfU6pT9lRCVI9U21UoVXVVwlYPVlxWqVb3V0RXklfgWC9YfVjL +WRpZaVm4WgdaVlqmWvVbRVuVW+VcNVyGXNZdJ114XcleGl5sXr1fD19hX7NgBWBXYKpg/GFPYaJh9WJJ +Ypxi8GNDY5dj62RAZJRk6WU9ZZJl52Y9ZpJm6Gc9Z5Nn6Wg/aJZo7GlDaZpp8WpIap9q92tPa6dr/2xX +bK9tCG1gbbluEm5rbsRvHm94b9FwK3CGcOBxOnGVcfByS3KmcwFzXXO4dBR0cHTMdSh1hXXhdj52m3b4 +d1Z3s3gReG54zHkqeYl553pGeqV7BHtje8J8IXyBfOF9QX2hfgF+Yn7CfyN/hH/lgEeAqIEKgWuBzYIw +gpKC9INXg7qEHYSAhOOFR4Wrhg6GcobXhzuHn4gEiGmIzokziZmJ/opkisqLMIuWi/yMY4zKjTGNmI3/ +jmaOzo82j56QBpBukNaRP5GokhGSepLjk02TtpQglIqU9JVflcmWNJaflwqXdZfgmEyYuJkkmZCZ/Jpo +mtWbQpuvnByciZz3nWSd0p5Anq6fHZ+Ln/qgaaDYoUehtqImopajBqN2o+akVqTHpTilqaYapoum/adu +p+CoUqjEqTepqaocqo+rAqt1q+msXKzQrUStuK4trqGvFq+LsACwdbDqsWCx1rJLssKzOLOutCW0nLUT +tYq2AbZ5tvC3aLfguFm40blKucK6O7q1uy67p7whvJu9Fb2Pvgq+hL7/v3q/9cBwwOzBZ8Hjwl/C28NY +w9TEUcTOxUvFyMZGxsPHQce/yD3IvMk6ybnKOMq3yzbLtsw1zLXNNc21zjbOts83z7jQOdC60TzRvtI/ +0sHTRNPG1EnUy9VO1dHWVdbY11zX4Nhk2OjZbNnx2nba+9uA3AXcit0Q3ZbeHN6i3ynfr+A24L3hROHM +4lPi2+Nj4+vkc+T85YTmDeaW5x/nqegy6LzpRunQ6lvq5etw6/vshu0R7ZzuKO6070DvzPBY8OXxcvH/ +8ozzGfOn9DT0wvVQ9d72bfb794r4Gfio+Tj5x/pX+uf7d/wH/Jj9Kf26/kv+3P9t///SLS4vMFokY2xh +c3NuYW1lWCRjbGFzc2VzXxAQTlNCaXRtYXBJbWFnZVJlcKMvMTJaTlNJbWFnZVJlcFhOU09iamVjdNIt +LjQ1V05TQXJyYXmiNDLSLS43OF5OU011dGFibGVBcnJheaM3NDLTOjsOPD0+V05TV2hpdGVcTlNDb2xv +clNwYWNlRDAgMAAQA4AM0i0uQEFXTlNDb2xvcqJAMtItLkNEV05TSW1hZ2WiQzIACAARABoAJAApADIA +NwBJAEwAUQBTAGIAaAB3AH4AhQCSAJkApQCtALwAvgDAAMUAxwDJAMsAzQDUANkA5ADmAOgA6gDvAPIA +9AD2APgA/wEWATIBNAE2E6YTqxO2E78T0hPWE+ET6hPvE/cT+hP/FA4UEhQZFCEULhQzFDUUNxQ8FEQU +RxRMFFQAAAAAAAACAQAAAAAAAABFAAAAAAAAAAAAAAAAAAAUVw diff --git a/i18n/src/main/resources/uk.lproj/Browser.strings b/i18n/src/main/resources/uk.lproj/Browser.strings index 2c0d897674..636c69334f 100644 Binary files a/i18n/src/main/resources/uk.lproj/Browser.strings and b/i18n/src/main/resources/uk.lproj/Browser.strings differ diff --git a/i18n/src/main/resources/uk.lproj/Browser.strings.1 b/i18n/src/main/resources/uk.lproj/Browser.strings.1 index a8b5281f95..212e536102 100644 Binary files a/i18n/src/main/resources/uk.lproj/Browser.strings.1 and b/i18n/src/main/resources/uk.lproj/Browser.strings.1 differ diff --git a/i18n/src/main/resources/uk.lproj/Browser.xib b/i18n/src/main/resources/uk.lproj/Browser.xib index 85dfe143dd..442ca6791e 100644 --- a/i18n/src/main/resources/uk.lproj/Browser.xib +++ b/i18n/src/main/resources/uk.lproj/Browser.xib @@ -1,7 +1,7 @@ - + - + @@ -38,7 +38,7 @@ - + @@ -62,27 +62,27 @@ @@ -256,7 +256,7 @@ - + @@ -291,7 +291,7 @@ - + @@ -358,7 +358,7 @@ - + @@ -427,7 +427,7 @@ CA - + @@ -553,95 +553,95 @@ DQ YnBsaXN0MDDUAQIDBAUGBwpYJHZlcnNpb25ZJGFyY2hpdmVyVCR0b3BYJG9iamVjdHMSAAGGoF8QD05T -S2V5ZWRBcmNoaXZlctEICVRyb290gAGuCwwZGh8UJCkqMTQ3PUBVJG51bGzWDQ4PEBESExQVFhcYVk5T -U2l6ZV5OU1Jlc2l6aW5nTW9kZVYkY2xhc3NcTlNJbWFnZUZsYWdzVk5TUmVwc1dOU0NvbG9ygAIQAIAN -EiDDAACAA4ALVnsxLCAxfdIbDxweWk5TLm9iamVjdHOhHYAEgArSGw8gI6IhIoAFgAaACdMPJSYnKBRf -EBROU1RJRkZSZXByZXNlbnRhdGlvbl8QGU5TSW50ZXJuYWxMYXlvdXREaXJlY3Rpb26ACIAHTxESbE1N -ACoAAAAKAAAAEAEAAAMAAAABAAEAAAEBAAMAAAABAAEAAAECAAMAAAACAAgACAEDAAMAAAABAAEAAAEG -AAMAAAABAAEAAAEKAAMAAAABAAEAAAERAAQAAAABAAAACAESAAMAAAABAAEAAAEVAAMAAAABAAIAAAEW -AAMAAAABAAEAAAEXAAQAAAABAAAAAgEcAAMAAAABAAEAAAEoAAMAAAABAAIAAAFSAAMAAAABAAEAAAFT -AAMAAAACAAEAAYdzAAcAABGcAAAA0AAAAAAAABGcYXBwbAIAAABtbnRyR1JBWVhZWiAH3AAIABcADwAu -AA9hY3NwQVBQTAAAAABub25lAAAAAAAAAAAAAAAAAAAAAAAA9tYAAQAAAADTLWFwcGwAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAVkZXNjAAAAwAAAAHlkc2NtAAABPAAA -CBpjcHJ0AAAJWAAAACN3dHB0AAAJfAAAABRrVFJDAAAJkAAACAxkZXNjAAAAAAAAAB9HZW5lcmljIEdy -YXkgR2FtbWEgMi4yIFByb2ZpbGUAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAbWx1YwAAAAAAAAAfAAAADHNr -U0sAAAAuAAABhGRhREsAAAA6AAABsmNhRVMAAAA4AAAB7HZpVk4AAABAAAACJHB0QlIAAABKAAACZHVr -VUEAAAAsAAACrmZyRlUAAAA+AAAC2mh1SFUAAAA0AAADGHpoVFcAAAAaAAADTGtvS1IAAAAiAAADZm5i -Tk8AAAA6AAADiGNzQ1oAAAAoAAADwmhlSUwAAAAkAAAD6nJvUk8AAAAqAAAEDmRlREUAAABOAAAEOGl0 -SVQAAABOAAAEhnN2U0UAAAA4AAAE1HpoQ04AAAAaAAAFDGphSlAAAAAmAAAFJmVsR1IAAAAqAAAFTHB0 -UE8AAABSAAAFdm5sTkwAAABAAAAFyGVzRVMAAABMAAAGCHRoVEgAAAAyAAAGVHRyVFIAAAAkAAAGhmZp -RkkAAABGAAAGqmhySFIAAAA+AAAG8HBsUEwAAABKAAAHLmFyRUcAAAAsAAAHeHJ1UlUAAAA6AAAHpGVu -VVMAAAA8AAAH3gBWAWEAZQBvAGIAZQBjAG4A4QAgAHMAaQB2AOEAIABnAGEAbQBhACAAMgAsADIARwBl -AG4AZQByAGkAcwBrACAAZwByAOUAIAAyACwAMgAgAGcAYQBtAG0AYQAtAHAAcgBvAGYAaQBsAEcAYQBt -AG0AYQAgAGQAZQAgAGcAcgBpAHMAbwBzACAAZwBlAG4A6AByAGkAYwBhACAAMgAuADIAQx6lAHUAIABo -AOwAbgBoACAATQDgAHUAIAB4AOEAbQAgAEMAaAB1AG4AZwAgAEcAYQBtAG0AYQAgADIALgAyAFAAZQBy -AGYAaQBsACAARwBlAG4A6QByAGkAYwBvACAAZABhACAARwBhAG0AYQAgAGQAZQAgAEMAaQBuAHoAYQBz -ACAAMgAsADIEFwQwBDMEMAQ7BEwEPQQwACAARwByAGEAeQAtBDMEMAQ8BDAAIAAyAC4AMgBQAHIAbwBm -AGkAbAAgAGcA6QBuAOkAcgBpAHEAdQBlACAAZwByAGkAcwAgAGcAYQBtAG0AYQAgADIALAAyAMEAbAB0 -AGEAbADhAG4AbwBzACAAcwB6APwAcgBrAGUAIABnAGEAbQBtAGEAIAAyAC4AMpAadShwcJaOUUlepgAy -AC4AMoJyX2ljz4/wx3y8GAAg1ozAyQAgrBC5yAAgADIALgAyACDVBLhc0wzHfABHAGUAbgBlAHIAaQBz -AGsAIABnAHIA5QAgAGcAYQBtAG0AYQAgADIALAAyAC0AcAByAG8AZgBpAGwATwBiAGUAYwBuAOEAIAFh -AGUAZADhACAAZwBhAG0AYQAgADIALgAyBdIF0AXeBdQAIAXQBeQF1QXoACAF2wXcBdwF2QAgADIALgAy -AEcAYQBtAGEAIABnAHIAaQAgAGcAZQBuAGUAcgBpAGMBAwAgADIALAAyAEEAbABsAGcAZQBtAGUAaQBu -AGUAcwAgAEcAcgBhAHUAcwB0AHUAZgBlAG4ALQBQAHIAbwBmAGkAbAAgAEcAYQBtAG0AYQAgADIALAAy -AFAAcgBvAGYAaQBsAG8AIABnAHIAaQBnAGkAbwAgAGcAZQBuAGUAcgBpAGMAbwAgAGQAZQBsAGwAYQAg -AGcAYQBtAG0AYQAgADIALAAyAEcAZQBuAGUAcgBpAHMAawAgAGcAcgDlACAAMgAsADIAIABnAGEAbQBt -AGEAcAByAG8AZgBpAGxmbpAacHBepnz7ZXAAMgAuADJjz4/wZYdO9k4AgiwwsDDsMKQwrDDzMN4AIAAy -AC4AMgAgMNcw7TDVMKEwpDDrA5MDtQO9A7kDugPMACADkwO6A8EDuQAgA5MDrAO8A7wDsQAgADIALgAy -AFAAZQByAGYAaQBsACAAZwBlAG4A6QByAGkAYwBvACAAZABlACAAYwBpAG4AegBlAG4AdABvAHMAIABk -AGEAIABHAGEAbQBtAGEAIAAyACwAMgBBAGwAZwBlAG0AZQBlAG4AIABnAHIAaQBqAHMAIABnAGEAbQBt -AGEAIAAyACwAMgAtAHAAcgBvAGYAaQBlAGwAUABlAHIAZgBpAGwAIABnAGUAbgDpAHIAaQBjAG8AIABk -AGUAIABnAGEAbQBtAGEAIABkAGUAIABnAHIAaQBzAGUAcwAgADIALAAyDiMOMQ4HDioONQ5BDgEOIQ4h -DjIOQA4BDiMOIg5MDhcOMQ5IDicORA4bACAAMgAuADIARwBlAG4AZQBsACAARwByAGkAIABHAGEAbQBh -ACAAMgAsADIAWQBsAGUAaQBuAGUAbgAgAGgAYQByAG0AYQBhAG4AIABnAGEAbQBtAGEAIAAyACwAMgAg -AC0AcAByAG8AZgBpAGkAbABpAEcAZQBuAGUAcgBpAQ0AawBpACAARwByAGEAeQAgAEcAYQBtAG0AYQAg -ADIALgAyACAAcAByAG8AZgBpAGwAVQBuAGkAdwBlAHIAcwBhAGwAbgB5ACAAcAByAG8AZgBpAGwAIABz -AHoAYQByAG8BWwBjAGkAIABnAGEAbQBtAGEAIAAyACwAMgY6BicGRQYnACAAMgAuADIAIAZEBkgGRgAg -BjEGRQYnBi8GSgAgBjkGJwZFBB4EMQRJBDAETwAgBEEENQRABDAETwAgBDMEMAQ8BDwEMAAgADIALAAy -AC0EPwRABD4ERAQ4BDsETABHAGUAbgBlAHIAaQBjACAARwByAGEAeQAgAEcAYQBtAG0AYQAgADIALgAy -ACAAUAByAG8AZgBpAGwAZQAAdGV4dAAAAABDb3B5cmlnaHQgQXBwbGUgSW5jLiwgMjAxMgAAWFlaIAAA -AAAAAPNRAAEAAAABFsxjdXJ2AAAAAAAABAAAAAAFAAoADwAUABkAHgAjACgALQAyADcAOwBAAEUASgBP -AFQAWQBeAGMAaABtAHIAdwB8AIEAhgCLAJAAlQCaAJ8ApACpAK4AsgC3ALwAwQDGAMsA0ADVANsA4ADl -AOsA8AD2APsBAQEHAQ0BEwEZAR8BJQErATIBOAE+AUUBTAFSAVkBYAFnAW4BdQF8AYMBiwGSAZoBoQGp -AbEBuQHBAckB0QHZAeEB6QHyAfoCAwIMAhQCHQImAi8COAJBAksCVAJdAmcCcQJ6AoQCjgKYAqICrAK2 -AsECywLVAuAC6wL1AwADCwMWAyEDLQM4A0MDTwNaA2YDcgN+A4oDlgOiA64DugPHA9MD4APsA/kEBgQT -BCAELQQ7BEgEVQRjBHEEfgSMBJoEqAS2BMQE0wThBPAE/gUNBRwFKwU6BUkFWAVnBXcFhgWWBaYFtQXF -BdUF5QX2BgYGFgYnBjcGSAZZBmoGewaMBp0GrwbABtEG4wb1BwcHGQcrBz0HTwdhB3QHhgeZB6wHvwfS -B+UH+AgLCB8IMghGCFoIbgiCCJYIqgi+CNII5wj7CRAJJQk6CU8JZAl5CY8JpAm6Cc8J5Qn7ChEKJwo9 -ClQKagqBCpgKrgrFCtwK8wsLCyILOQtRC2kLgAuYC7ALyAvhC/kMEgwqDEMMXAx1DI4MpwzADNkM8w0N -DSYNQA1aDXQNjg2pDcMN3g34DhMOLg5JDmQOfw6bDrYO0g7uDwkPJQ9BD14Peg+WD7MPzw/sEAkQJhBD -EGEQfhCbELkQ1xD1ERMRMRFPEW0RjBGqEckR6BIHEiYSRRJkEoQSoxLDEuMTAxMjE0MTYxODE6QTxRPl -FAYUJxRJFGoUixStFM4U8BUSFTQVVhV4FZsVvRXgFgMWJhZJFmwWjxayFtYW+hcdF0EXZReJF64X0hf3 -GBsYQBhlGIoYrxjVGPoZIBlFGWsZkRm3Gd0aBBoqGlEadxqeGsUa7BsUGzsbYxuKG7Ib2hwCHCocUhx7 -HKMczBz1HR4dRx1wHZkdwx3sHhYeQB5qHpQevh7pHxMfPh9pH5Qfvx/qIBUgQSBsIJggxCDwIRwhSCF1 -IaEhziH7IiciVSKCIq8i3SMKIzgjZiOUI8Ij8CQfJE0kfCSrJNolCSU4JWgllyXHJfcmJyZXJocmtybo -JxgnSSd6J6sn3CgNKD8ocSiiKNQpBik4KWspnSnQKgIqNSpoKpsqzysCKzYraSudK9EsBSw5LG4soizX -LQwtQS12Last4S4WLkwugi63Lu4vJC9aL5Evxy/+MDUwbDCkMNsxEjFKMYIxujHyMioyYzKbMtQzDTNG -M38zuDPxNCs0ZTSeNNg1EzVNNYc1wjX9Njc2cjauNuk3JDdgN5w31zgUOFA4jDjIOQU5Qjl/Obw5+To2 -OnQ6sjrvOy07azuqO+g8JzxlPKQ84z0iPWE9oT3gPiA+YD6gPuA/IT9hP6I/4kAjQGRApkDnQSlBakGs -Qe5CMEJyQrVC90M6Q31DwEQDREdEikTORRJFVUWaRd5GIkZnRqtG8Ec1R3tHwEgFSEtIkUjXSR1JY0mp -SfBKN0p9SsRLDEtTS5pL4kwqTHJMuk0CTUpNk03cTiVObk63TwBPSU+TT91QJ1BxULtRBlFQUZtR5lIx -UnxSx1MTU19TqlP2VEJUj1TbVShVdVXCVg9WXFapVvdXRFeSV+BYL1h9WMtZGllpWbhaB1pWWqZa9VtF -W5Vb5Vw1XIZc1l0nXXhdyV4aXmxevV8PX2Ffs2AFYFdgqmD8YU9homH1YklinGLwY0Njl2PrZEBklGTp -ZT1lkmXnZj1mkmboZz1nk2fpaD9olmjsaUNpmmnxakhqn2r3a09rp2v/bFdsr20IbWBtuW4SbmtuxG8e -b3hv0XArcIZw4HE6cZVx8HJLcqZzAXNdc7h0FHRwdMx1KHWFdeF2Pnabdvh3VnezeBF4bnjMeSp5iXnn -ekZ6pXsEe2N7wnwhfIF84X1BfaF+AX5ifsJ/I3+Ef+WAR4CogQqBa4HNgjCCkoL0g1eDuoQdhICE44VH -hauGDoZyhteHO4efiASIaYjOiTOJmYn+imSKyoswi5aL/IxjjMqNMY2Yjf+OZo7OjzaPnpAGkG6Q1pE/ -kaiSEZJ6kuOTTZO2lCCUipT0lV+VyZY0lp+XCpd1l+CYTJi4mSSZkJn8mmia1ZtCm6+cHJyJnPedZJ3S -nkCerp8dn4uf+qBpoNihR6G2oiailqMGo3aj5qRWpMelOKWpphqmi6b9p26n4KhSqMSpN6mpqhyqj6sC -q3Wr6axcrNCtRK24ri2uoa8Wr4uwALB1sOqxYLHWskuywrM4s660JbSctRO1irYBtnm28Ldot+C4WbjR -uUq5wro7urW7LrunvCG8m70VvY++Cr6Evv+/er/1wHDA7MFnwePCX8Lbw1jD1MRRxM7FS8XIxkbGw8dB -x7/IPci8yTrJuco4yrfLNsu2zDXMtc01zbXONs62zzfPuNA50LrRPNG+0j/SwdNE08bUSdTL1U7V0dZV -1tjXXNfg2GTY6Nls2fHadtr724DcBdyK3RDdlt4c3qLfKd+v4DbgveFE4cziU+Lb42Pj6+Rz5PzlhOYN -5pbnH+ep6DLovOlG6dDqW+rl63Dr++yG7RHtnO4o7rTvQO/M8Fjw5fFy8f/yjPMZ86f0NPTC9VD13vZt -9vv3ivgZ+Kj5OPnH+lf65/t3/Af8mP0p/br+S/7c/23//9IrLC0uWiRjbGFzc25hbWVYJGNsYXNzZXNf -EBBOU0JpdG1hcEltYWdlUmVwoy0vMFpOU0ltYWdlUmVwWE5TT2JqZWN00issMjNXTlNBcnJheaIyMNIr -LDU2Xk5TTXV0YWJsZUFycmF5ozUyMNM4OQ86OzxXTlNXaGl0ZVxOU0NvbG9yU3BhY2VEMCAwABADgAzS -Kyw+P1dOU0NvbG9yoj4w0issQUJXTlNJbWFnZaJBMAAIABEAGgAkACkAMgA3AEkATABRAFMAYgBoAHUA -fACLAJIAnwCmAK4AsACyALQAuQC7AL0AxADJANQA1gDYANoA3wDiAOQA5gDoAO8BBgEiASQBJhOWE5sT -phOvE8ITxhPRE9oT3xPnE+oT7xP+FAIUCRQRFB4UIxQlFCcULBQ0FDcUPBREAAAAAAAAAgEAAAAAAAAA -QwAAAAAAAAAAAAAAAAAAFEc +S2V5ZWRBcmNoaXZlctEICVRyb290gAGuCwwbHCEaJissMzY5P0JVJG51bGzXDQ4PEBESExQVFhcYGRpW +TlNTaXplViRjbGFzc1xOU0ltYWdlRmxhZ3NWTlNSZXBzW05TVGludENvbG9yV05TQ29sb3JeTlNSZXNp +emluZ01vZGWAAoANEiDDAACAA4AAgAsQAFZ7MSwgMX3SHQ4eIFpOUy5vYmplY3RzoR+ABIAK0h0OIiWi +IySABYAGgAnTDicoKSoaXxAUTlNUSUZGUmVwcmVzZW50YXRpb25fEBlOU0ludGVybmFsTGF5b3V0RGly +ZWN0aW9ugAiAB08REmxNTQAqAAAACgAAABABAAADAAAAAQABAAABAQADAAAAAQABAAABAgADAAAAAgAI +AAgBAwADAAAAAQABAAABBgADAAAAAQABAAABCgADAAAAAQABAAABEQAEAAAAAQAAAAgBEgADAAAAAQAB +AAABFQADAAAAAQACAAABFgADAAAAAQABAAABFwAEAAAAAQAAAAIBHAADAAAAAQABAAABKAADAAAAAQAC +AAABUgADAAAAAQABAAABUwADAAAAAgABAAGHcwAHAAARnAAAANAAAAAAAAARnGFwcGwCAAAAbW50ckdS +QVlYWVogB9wACAAXAA8ALgAPYWNzcEFQUEwAAAAAbm9uZQAAAAAAAAAAAAAAAAAAAAAAAPbWAAEAAAAA +0y1hcHBsAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFZGVzYwAA +AMAAAAB5ZHNjbQAAATwAAAgaY3BydAAACVgAAAAjd3RwdAAACXwAAAAUa1RSQwAACZAAAAgMZGVzYwAA +AAAAAAAfR2VuZXJpYyBHcmF5IEdhbW1hIDIuMiBQcm9maWxlAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAG1s +dWMAAAAAAAAAHwAAAAxza1NLAAAALgAAAYRkYURLAAAAOgAAAbJjYUVTAAAAOAAAAex2aVZOAAAAQAAA +AiRwdEJSAAAASgAAAmR1a1VBAAAALAAAAq5mckZVAAAAPgAAAtpodUhVAAAANAAAAxh6aFRXAAAAGgAA +A0xrb0tSAAAAIgAAA2ZuYk5PAAAAOgAAA4hjc0NaAAAAKAAAA8JoZUlMAAAAJAAAA+pyb1JPAAAAKgAA +BA5kZURFAAAATgAABDhpdElUAAAATgAABIZzdlNFAAAAOAAABNR6aENOAAAAGgAABQxqYUpQAAAAJgAA +BSZlbEdSAAAAKgAABUxwdFBPAAAAUgAABXZubE5MAAAAQAAABchlc0VTAAAATAAABgh0aFRIAAAAMgAA +BlR0clRSAAAAJAAABoZmaUZJAAAARgAABqpockhSAAAAPgAABvBwbFBMAAAASgAABy5hckVHAAAALAAA +B3hydVJVAAAAOgAAB6RlblVTAAAAPAAAB94AVgFhAGUAbwBiAGUAYwBuAOEAIABzAGkAdgDhACAAZwBh +AG0AYQAgADIALAAyAEcAZQBuAGUAcgBpAHMAawAgAGcAcgDlACAAMgAsADIAIABnAGEAbQBtAGEALQBw +AHIAbwBmAGkAbABHAGEAbQBtAGEAIABkAGUAIABnAHIAaQBzAG8AcwAgAGcAZQBuAOgAcgBpAGMAYQAg +ADIALgAyAEMepQB1ACAAaADsAG4AaAAgAE0A4AB1ACAAeADhAG0AIABDAGgAdQBuAGcAIABHAGEAbQBt +AGEAIAAyAC4AMgBQAGUAcgBmAGkAbAAgAEcAZQBuAOkAcgBpAGMAbwAgAGQAYQAgAEcAYQBtAGEAIABk +AGUAIABDAGkAbgB6AGEAcwAgADIALAAyBBcEMAQzBDAEOwRMBD0EMAAgAEcAcgBhAHkALQQzBDAEPAQw +ACAAMgAuADIAUAByAG8AZgBpAGwAIABnAOkAbgDpAHIAaQBxAHUAZQAgAGcAcgBpAHMAIABnAGEAbQBt +AGEAIAAyACwAMgDBAGwAdABhAGwA4QBuAG8AcwAgAHMAegD8AHIAawBlACAAZwBhAG0AbQBhACAAMgAu +ADKQGnUocHCWjlFJXqYAMgAuADKCcl9pY8+P8Md8vBgAINaMwMkAIKwQucgAIAAyAC4AMgAg1QS4XNMM +x3wARwBlAG4AZQByAGkAcwBrACAAZwByAOUAIABnAGEAbQBtAGEAIAAyACwAMgAtAHAAcgBvAGYAaQBs +AE8AYgBlAGMAbgDhACABYQBlAGQA4QAgAGcAYQBtAGEAIAAyAC4AMgXSBdAF3gXUACAF0AXkBdUF6AAg +BdsF3AXcBdkAIAAyAC4AMgBHAGEAbQBhACAAZwByAGkAIABnAGUAbgBlAHIAaQBjAQMAIAAyACwAMgBB +AGwAbABnAGUAbQBlAGkAbgBlAHMAIABHAHIAYQB1AHMAdAB1AGYAZQBuAC0AUAByAG8AZgBpAGwAIABH +AGEAbQBtAGEAIAAyACwAMgBQAHIAbwBmAGkAbABvACAAZwByAGkAZwBpAG8AIABnAGUAbgBlAHIAaQBj +AG8AIABkAGUAbABsAGEAIABnAGEAbQBtAGEAIAAyACwAMgBHAGUAbgBlAHIAaQBzAGsAIABnAHIA5QAg +ADIALAAyACAAZwBhAG0AbQBhAHAAcgBvAGYAaQBsZm6QGnBwXqZ8+2VwADIALgAyY8+P8GWHTvZOAIIs +MLAw7DCkMKww8zDeACAAMgAuADIAIDDXMO0w1TChMKQw6wOTA7UDvQO5A7oDzAAgA5MDugPBA7kAIAOT +A6wDvAO8A7EAIAAyAC4AMgBQAGUAcgBmAGkAbAAgAGcAZQBuAOkAcgBpAGMAbwAgAGQAZQAgAGMAaQBu +AHoAZQBuAHQAbwBzACAAZABhACAARwBhAG0AbQBhACAAMgAsADIAQQBsAGcAZQBtAGUAZQBuACAAZwBy +AGkAagBzACAAZwBhAG0AbQBhACAAMgAsADIALQBwAHIAbwBmAGkAZQBsAFAAZQByAGYAaQBsACAAZwBl +AG4A6QByAGkAYwBvACAAZABlACAAZwBhAG0AbQBhACAAZABlACAAZwByAGkAcwBlAHMAIAAyACwAMg4j +DjEOBw4qDjUOQQ4BDiEOIQ4yDkAOAQ4jDiIOTA4XDjEOSA4nDkQOGwAgADIALgAyAEcAZQBuAGUAbAAg +AEcAcgBpACAARwBhAG0AYQAgADIALAAyAFkAbABlAGkAbgBlAG4AIABoAGEAcgBtAGEAYQBuACAAZwBh +AG0AbQBhACAAMgAsADIAIAAtAHAAcgBvAGYAaQBpAGwAaQBHAGUAbgBlAHIAaQENAGsAaQAgAEcAcgBh +AHkAIABHAGEAbQBtAGEAIAAyAC4AMgAgAHAAcgBvAGYAaQBsAFUAbgBpAHcAZQByAHMAYQBsAG4AeQAg +AHAAcgBvAGYAaQBsACAAcwB6AGEAcgBvAVsAYwBpACAAZwBhAG0AbQBhACAAMgAsADIGOgYnBkUGJwAg +ADIALgAyACAGRAZIBkYAIAYxBkUGJwYvBkoAIAY5BicGRQQeBDEESQQwBE8AIARBBDUEQAQwBE8AIAQz +BDAEPAQ8BDAAIAAyACwAMgAtBD8EQAQ+BEQEOAQ7BEwARwBlAG4AZQByAGkAYwAgAEcAcgBhAHkAIABH +AGEAbQBtAGEAIAAyAC4AMgAgAFAAcgBvAGYAaQBsAGUAAHRleHQAAAAAQ29weXJpZ2h0IEFwcGxlIElu +Yy4sIDIwMTIAAFhZWiAAAAAAAADzUQABAAAAARbMY3VydgAAAAAAAAQAAAAABQAKAA8AFAAZAB4AIwAo +AC0AMgA3ADsAQABFAEoATwBUAFkAXgBjAGgAbQByAHcAfACBAIYAiwCQAJUAmgCfAKQAqQCuALIAtwC8 +AMEAxgDLANAA1QDbAOAA5QDrAPAA9gD7AQEBBwENARMBGQEfASUBKwEyATgBPgFFAUwBUgFZAWABZwFu +AXUBfAGDAYsBkgGaAaEBqQGxAbkBwQHJAdEB2QHhAekB8gH6AgMCDAIUAh0CJgIvAjgCQQJLAlQCXQJn +AnECegKEAo4CmAKiAqwCtgLBAssC1QLgAusC9QMAAwsDFgMhAy0DOANDA08DWgNmA3IDfgOKA5YDogOu +A7oDxwPTA+AD7AP5BAYEEwQgBC0EOwRIBFUEYwRxBH4EjASaBKgEtgTEBNME4QTwBP4FDQUcBSsFOgVJ +BVgFZwV3BYYFlgWmBbUFxQXVBeUF9gYGBhYGJwY3BkgGWQZqBnsGjAadBq8GwAbRBuMG9QcHBxkHKwc9 +B08HYQd0B4YHmQesB78H0gflB/gICwgfCDIIRghaCG4IggiWCKoIvgjSCOcI+wkQCSUJOglPCWQJeQmP +CaQJugnPCeUJ+woRCicKPQpUCmoKgQqYCq4KxQrcCvMLCwsiCzkLUQtpC4ALmAuwC8gL4Qv5DBIMKgxD +DFwMdQyODKcMwAzZDPMNDQ0mDUANWg10DY4NqQ3DDd4N+A4TDi4OSQ5kDn8Omw62DtIO7g8JDyUPQQ9e +D3oPlg+zD88P7BAJECYQQxBhEH4QmxC5ENcQ9RETETERTxFtEYwRqhHJEegSBxImEkUSZBKEEqMSwxLj +EwMTIxNDE2MTgxOkE8UT5RQGFCcUSRRqFIsUrRTOFPAVEhU0FVYVeBWbFb0V4BYDFiYWSRZsFo8WshbW +FvoXHRdBF2UXiReuF9IX9xgbGEAYZRiKGK8Y1Rj6GSAZRRlrGZEZtxndGgQaKhpRGncanhrFGuwbFBs7 +G2MbihuyG9ocAhwqHFIcexyjHMwc9R0eHUcdcB2ZHcMd7B4WHkAeah6UHr4e6R8THz4faR+UH78f6iAV +IEEgbCCYIMQg8CEcIUghdSGhIc4h+yInIlUigiKvIt0jCiM4I2YjlCPCI/AkHyRNJHwkqyTaJQklOCVo +JZclxyX3JicmVyaHJrcm6CcYJ0kneierJ9woDSg/KHEooijUKQYpOClrKZ0p0CoCKjUqaCqbKs8rAis2 +K2krnSvRLAUsOSxuLKIs1y0MLUEtdi2rLeEuFi5MLoIuty7uLyQvWi+RL8cv/jA1MGwwpDDbMRIxSjGC +Mbox8jIqMmMymzLUMw0zRjN/M7gz8TQrNGU0njTYNRM1TTWHNcI1/TY3NnI2rjbpNyQ3YDecN9c4FDhQ +OIw4yDkFOUI5fzm8Ofk6Njp0OrI67zstO2s7qjvoPCc8ZTykPOM9Ij1hPaE94D4gPmA+oD7gPyE/YT+i +P+JAI0BkQKZA50EpQWpBrEHuQjBCckK1QvdDOkN9Q8BEA0RHRIpEzkUSRVVFmkXeRiJGZ0arRvBHNUd7 +R8BIBUhLSJFI10kdSWNJqUnwSjdKfUrESwxLU0uaS+JMKkxyTLpNAk1KTZNN3E4lTm5Ot08AT0lPk0/d +UCdQcVC7UQZRUFGbUeZSMVJ8UsdTE1NfU6pT9lRCVI9U21UoVXVVwlYPVlxWqVb3V0RXklfgWC9YfVjL +WRpZaVm4WgdaVlqmWvVbRVuVW+VcNVyGXNZdJ114XcleGl5sXr1fD19hX7NgBWBXYKpg/GFPYaJh9WJJ +Ypxi8GNDY5dj62RAZJRk6WU9ZZJl52Y9ZpJm6Gc9Z5Nn6Wg/aJZo7GlDaZpp8WpIap9q92tPa6dr/2xX +bK9tCG1gbbluEm5rbsRvHm94b9FwK3CGcOBxOnGVcfByS3KmcwFzXXO4dBR0cHTMdSh1hXXhdj52m3b4 +d1Z3s3gReG54zHkqeYl553pGeqV7BHtje8J8IXyBfOF9QX2hfgF+Yn7CfyN/hH/lgEeAqIEKgWuBzYIw +gpKC9INXg7qEHYSAhOOFR4Wrhg6GcobXhzuHn4gEiGmIzokziZmJ/opkisqLMIuWi/yMY4zKjTGNmI3/ +jmaOzo82j56QBpBukNaRP5GokhGSepLjk02TtpQglIqU9JVflcmWNJaflwqXdZfgmEyYuJkkmZCZ/Jpo +mtWbQpuvnByciZz3nWSd0p5Anq6fHZ+Ln/qgaaDYoUehtqImopajBqN2o+akVqTHpTilqaYapoum/adu +p+CoUqjEqTepqaocqo+rAqt1q+msXKzQrUStuK4trqGvFq+LsACwdbDqsWCx1rJLssKzOLOutCW0nLUT +tYq2AbZ5tvC3aLfguFm40blKucK6O7q1uy67p7whvJu9Fb2Pvgq+hL7/v3q/9cBwwOzBZ8Hjwl/C28NY +w9TEUcTOxUvFyMZGxsPHQce/yD3IvMk6ybnKOMq3yzbLtsw1zLXNNc21zjbOts83z7jQOdC60TzRvtI/ +0sHTRNPG1EnUy9VO1dHWVdbY11zX4Nhk2OjZbNnx2nba+9uA3AXcit0Q3ZbeHN6i3ynfr+A24L3hROHM +4lPi2+Nj4+vkc+T85YTmDeaW5x/nqegy6LzpRunQ6lvq5etw6/vshu0R7ZzuKO6070DvzPBY8OXxcvH/ +8ozzGfOn9DT0wvVQ9d72bfb794r4Gfio+Tj5x/pX+uf7d/wH/Jj9Kf26/kv+3P9t///SLS4vMFokY2xh +c3NuYW1lWCRjbGFzc2VzXxAQTlNCaXRtYXBJbWFnZVJlcKMvMTJaTlNJbWFnZVJlcFhOU09iamVjdNIt +LjQ1V05TQXJyYXmiNDLSLS43OF5OU011dGFibGVBcnJheaM3NDLTOjsOPD0+V05TV2hpdGVcTlNDb2xv +clNwYWNlRDAgMAAQA4AM0i0uQEFXTlNDb2xvcqJAMtItLkNEV05TSW1hZ2WiQzIACAARABoAJAApADIA +NwBJAEwAUQBTAGIAaAB3AH4AhQCSAJkApQCtALwAvgDAAMUAxwDJAMsAzQDUANkA5ADmAOgA6gDvAPIA +9AD2APgA/wEWATIBNAE2E6YTqxO2E78T0hPWE+ET6hPvE/cT+hP/FA4UEhQZFCEULhQzFDUUNxQ8FEQU +RxRMFFQAAAAAAAACAQAAAAAAAABFAAAAAAAAAAAAAAAAAAAUVw diff --git a/i18n/src/main/resources/uk.lproj/Command.xib b/i18n/src/main/resources/uk.lproj/Command.xib index e6451581f8..ccff7551b3 100644 --- a/i18n/src/main/resources/uk.lproj/Command.xib +++ b/i18n/src/main/resources/uk.lproj/Command.xib @@ -1,7 +1,7 @@ - + - + diff --git a/i18n/src/main/resources/uk.lproj/Connection.xib b/i18n/src/main/resources/uk.lproj/Connection.xib index 7628d289cd..7f6a395b33 100644 --- a/i18n/src/main/resources/uk.lproj/Connection.xib +++ b/i18n/src/main/resources/uk.lproj/Connection.xib @@ -1,7 +1,7 @@ - + - + @@ -76,7 +76,7 @@ - + diff --git a/i18n/src/main/resources/uk.lproj/Credentials.strings b/i18n/src/main/resources/uk.lproj/Credentials.strings index a4183c8ac7..dc3bd7c609 100644 Binary files a/i18n/src/main/resources/uk.lproj/Credentials.strings and b/i18n/src/main/resources/uk.lproj/Credentials.strings differ diff --git a/i18n/src/main/resources/uk.lproj/EUE.strings b/i18n/src/main/resources/uk.lproj/EUE.strings index e45b9610f4..ceb7331d11 100644 Binary files a/i18n/src/main/resources/uk.lproj/EUE.strings and b/i18n/src/main/resources/uk.lproj/EUE.strings differ diff --git a/i18n/src/main/resources/uk.lproj/Error.strings b/i18n/src/main/resources/uk.lproj/Error.strings index f5454f2c8c..1ce8ac18ec 100644 Binary files a/i18n/src/main/resources/uk.lproj/Error.strings and b/i18n/src/main/resources/uk.lproj/Error.strings differ diff --git a/i18n/src/main/resources/uk.lproj/Folder.strings b/i18n/src/main/resources/uk.lproj/Folder.strings index 5b79f1584a..6dce2a5b84 100644 Binary files a/i18n/src/main/resources/uk.lproj/Folder.strings and b/i18n/src/main/resources/uk.lproj/Folder.strings differ diff --git a/i18n/src/main/resources/uk.lproj/Info.strings b/i18n/src/main/resources/uk.lproj/Info.strings index 5037e175e0..0de51b4eb8 100644 Binary files a/i18n/src/main/resources/uk.lproj/Info.strings and b/i18n/src/main/resources/uk.lproj/Info.strings differ diff --git a/i18n/src/main/resources/uk.lproj/Info.strings.1 b/i18n/src/main/resources/uk.lproj/Info.strings.1 index b9105b0d2f..056685713a 100644 Binary files a/i18n/src/main/resources/uk.lproj/Info.strings.1 and b/i18n/src/main/resources/uk.lproj/Info.strings.1 differ diff --git a/i18n/src/main/resources/uk.lproj/Info.xib b/i18n/src/main/resources/uk.lproj/Info.xib index 5268ab84d7..cb480a8b57 100644 --- a/i18n/src/main/resources/uk.lproj/Info.xib +++ b/i18n/src/main/resources/uk.lproj/Info.xib @@ -1,7 +1,7 @@ - + - + @@ -89,7 +89,7 @@ - + @@ -1017,21 +1017,21 @@ - + - + - + @@ -1121,21 +1121,21 @@ - + - + - + @@ -1200,7 +1200,7 @@ - + @@ -1212,7 +1212,7 @@ - + @@ -1243,34 +1243,34 @@ - + diff --git a/i18n/src/main/resources/uk.lproj/InfoPlist.strings b/i18n/src/main/resources/uk.lproj/InfoPlist.strings index dbf31834c5..5229d481e7 100644 Binary files a/i18n/src/main/resources/uk.lproj/InfoPlist.strings and b/i18n/src/main/resources/uk.lproj/InfoPlist.strings differ diff --git a/i18n/src/main/resources/uk.lproj/Localizable.strings b/i18n/src/main/resources/uk.lproj/Localizable.strings index ceab7a6253..2a0c53ed3c 100644 Binary files a/i18n/src/main/resources/uk.lproj/Localizable.strings and b/i18n/src/main/resources/uk.lproj/Localizable.strings differ diff --git a/i18n/src/main/resources/uk.lproj/Login.strings b/i18n/src/main/resources/uk.lproj/Login.strings index 03d80f0270..3b377f01a1 100644 Binary files a/i18n/src/main/resources/uk.lproj/Login.strings and b/i18n/src/main/resources/uk.lproj/Login.strings differ diff --git a/i18n/src/main/resources/uk.lproj/Main.strings b/i18n/src/main/resources/uk.lproj/Main.strings index d3295a2575..bd9cc92858 100644 Binary files a/i18n/src/main/resources/uk.lproj/Main.strings and b/i18n/src/main/resources/uk.lproj/Main.strings differ diff --git a/i18n/src/main/resources/uk.lproj/Main.strings.1 b/i18n/src/main/resources/uk.lproj/Main.strings.1 index 5825b5295c..cc11bf9e3f 100644 Binary files a/i18n/src/main/resources/uk.lproj/Main.strings.1 and b/i18n/src/main/resources/uk.lproj/Main.strings.1 differ diff --git a/i18n/src/main/resources/uk.lproj/Main.xib b/i18n/src/main/resources/uk.lproj/Main.xib index c82055bed6..5cef5a5dfd 100644 --- a/i18n/src/main/resources/uk.lproj/Main.xib +++ b/i18n/src/main/resources/uk.lproj/Main.xib @@ -1,7 +1,7 @@ - + - + @@ -124,7 +124,7 @@ - + @@ -159,19 +159,19 @@ - + - + - + @@ -194,7 +194,7 @@ - + @@ -254,7 +254,7 @@ CA - + @@ -321,12 +321,12 @@ CA - + - + @@ -566,6 +566,10 @@ CA - + + + + + diff --git a/i18n/src/main/resources/uk.lproj/Preferences.strings b/i18n/src/main/resources/uk.lproj/Preferences.strings index b51ca0e2eb..327c4c05af 100644 Binary files a/i18n/src/main/resources/uk.lproj/Preferences.strings and b/i18n/src/main/resources/uk.lproj/Preferences.strings differ diff --git a/i18n/src/main/resources/uk.lproj/Preferences.strings.1 b/i18n/src/main/resources/uk.lproj/Preferences.strings.1 index 9af18d26da..5dc3dae6b7 100644 Binary files a/i18n/src/main/resources/uk.lproj/Preferences.strings.1 and b/i18n/src/main/resources/uk.lproj/Preferences.strings.1 differ diff --git a/i18n/src/main/resources/uk.lproj/Preferences.xib b/i18n/src/main/resources/uk.lproj/Preferences.xib index 5954600ae4..2b850efb31 100644 --- a/i18n/src/main/resources/uk.lproj/Preferences.xib +++ b/i18n/src/main/resources/uk.lproj/Preferences.xib @@ -1,7 +1,7 @@ - + - + @@ -245,7 +245,7 @@ - + @@ -266,7 +266,7 @@ - + @@ -307,7 +307,7 @@ - + @@ -413,7 +413,7 @@ @@ -256,7 +256,7 @@ - + @@ -553,95 +553,95 @@ DQ YnBsaXN0MDDUAQIDBAUGBwpYJHZlcnNpb25ZJGFyY2hpdmVyVCR0b3BYJG9iamVjdHMSAAGGoF8QD05T -S2V5ZWRBcmNoaXZlctEICVRyb290gAGuCwwZGh8UJCkqMTQ3PUBVJG51bGzWDQ4PEBESExQVFhcYVk5T -U2l6ZV5OU1Jlc2l6aW5nTW9kZVYkY2xhc3NcTlNJbWFnZUZsYWdzVk5TUmVwc1dOU0NvbG9ygAIQAIAN -EiDDAACAA4ALVnsxLCAxfdIbDxweWk5TLm9iamVjdHOhHYAEgArSGw8gI6IhIoAFgAaACdMPJSYnKBRf -EBROU1RJRkZSZXByZXNlbnRhdGlvbl8QGU5TSW50ZXJuYWxMYXlvdXREaXJlY3Rpb26ACIAHTxESbE1N -ACoAAAAKAAAAEAEAAAMAAAABAAEAAAEBAAMAAAABAAEAAAECAAMAAAACAAgACAEDAAMAAAABAAEAAAEG -AAMAAAABAAEAAAEKAAMAAAABAAEAAAERAAQAAAABAAAACAESAAMAAAABAAEAAAEVAAMAAAABAAIAAAEW -AAMAAAABAAEAAAEXAAQAAAABAAAAAgEcAAMAAAABAAEAAAEoAAMAAAABAAIAAAFSAAMAAAABAAEAAAFT -AAMAAAACAAEAAYdzAAcAABGcAAAA0AAAAAAAABGcYXBwbAIAAABtbnRyR1JBWVhZWiAH3AAIABcADwAu -AA9hY3NwQVBQTAAAAABub25lAAAAAAAAAAAAAAAAAAAAAAAA9tYAAQAAAADTLWFwcGwAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAVkZXNjAAAAwAAAAHlkc2NtAAABPAAA -CBpjcHJ0AAAJWAAAACN3dHB0AAAJfAAAABRrVFJDAAAJkAAACAxkZXNjAAAAAAAAAB9HZW5lcmljIEdy -YXkgR2FtbWEgMi4yIFByb2ZpbGUAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAbWx1YwAAAAAAAAAfAAAADHNr -U0sAAAAuAAABhGRhREsAAAA6AAABsmNhRVMAAAA4AAAB7HZpVk4AAABAAAACJHB0QlIAAABKAAACZHVr -VUEAAAAsAAACrmZyRlUAAAA+AAAC2mh1SFUAAAA0AAADGHpoVFcAAAAaAAADTGtvS1IAAAAiAAADZm5i -Tk8AAAA6AAADiGNzQ1oAAAAoAAADwmhlSUwAAAAkAAAD6nJvUk8AAAAqAAAEDmRlREUAAABOAAAEOGl0 -SVQAAABOAAAEhnN2U0UAAAA4AAAE1HpoQ04AAAAaAAAFDGphSlAAAAAmAAAFJmVsR1IAAAAqAAAFTHB0 -UE8AAABSAAAFdm5sTkwAAABAAAAFyGVzRVMAAABMAAAGCHRoVEgAAAAyAAAGVHRyVFIAAAAkAAAGhmZp -RkkAAABGAAAGqmhySFIAAAA+AAAG8HBsUEwAAABKAAAHLmFyRUcAAAAsAAAHeHJ1UlUAAAA6AAAHpGVu -VVMAAAA8AAAH3gBWAWEAZQBvAGIAZQBjAG4A4QAgAHMAaQB2AOEAIABnAGEAbQBhACAAMgAsADIARwBl -AG4AZQByAGkAcwBrACAAZwByAOUAIAAyACwAMgAgAGcAYQBtAG0AYQAtAHAAcgBvAGYAaQBsAEcAYQBt -AG0AYQAgAGQAZQAgAGcAcgBpAHMAbwBzACAAZwBlAG4A6AByAGkAYwBhACAAMgAuADIAQx6lAHUAIABo -AOwAbgBoACAATQDgAHUAIAB4AOEAbQAgAEMAaAB1AG4AZwAgAEcAYQBtAG0AYQAgADIALgAyAFAAZQBy -AGYAaQBsACAARwBlAG4A6QByAGkAYwBvACAAZABhACAARwBhAG0AYQAgAGQAZQAgAEMAaQBuAHoAYQBz -ACAAMgAsADIEFwQwBDMEMAQ7BEwEPQQwACAARwByAGEAeQAtBDMEMAQ8BDAAIAAyAC4AMgBQAHIAbwBm -AGkAbAAgAGcA6QBuAOkAcgBpAHEAdQBlACAAZwByAGkAcwAgAGcAYQBtAG0AYQAgADIALAAyAMEAbAB0 -AGEAbADhAG4AbwBzACAAcwB6APwAcgBrAGUAIABnAGEAbQBtAGEAIAAyAC4AMpAadShwcJaOUUlepgAy -AC4AMoJyX2ljz4/wx3y8GAAg1ozAyQAgrBC5yAAgADIALgAyACDVBLhc0wzHfABHAGUAbgBlAHIAaQBz -AGsAIABnAHIA5QAgAGcAYQBtAG0AYQAgADIALAAyAC0AcAByAG8AZgBpAGwATwBiAGUAYwBuAOEAIAFh -AGUAZADhACAAZwBhAG0AYQAgADIALgAyBdIF0AXeBdQAIAXQBeQF1QXoACAF2wXcBdwF2QAgADIALgAy -AEcAYQBtAGEAIABnAHIAaQAgAGcAZQBuAGUAcgBpAGMBAwAgADIALAAyAEEAbABsAGcAZQBtAGUAaQBu -AGUAcwAgAEcAcgBhAHUAcwB0AHUAZgBlAG4ALQBQAHIAbwBmAGkAbAAgAEcAYQBtAG0AYQAgADIALAAy -AFAAcgBvAGYAaQBsAG8AIABnAHIAaQBnAGkAbwAgAGcAZQBuAGUAcgBpAGMAbwAgAGQAZQBsAGwAYQAg -AGcAYQBtAG0AYQAgADIALAAyAEcAZQBuAGUAcgBpAHMAawAgAGcAcgDlACAAMgAsADIAIABnAGEAbQBt -AGEAcAByAG8AZgBpAGxmbpAacHBepnz7ZXAAMgAuADJjz4/wZYdO9k4AgiwwsDDsMKQwrDDzMN4AIAAy -AC4AMgAgMNcw7TDVMKEwpDDrA5MDtQO9A7kDugPMACADkwO6A8EDuQAgA5MDrAO8A7wDsQAgADIALgAy -AFAAZQByAGYAaQBsACAAZwBlAG4A6QByAGkAYwBvACAAZABlACAAYwBpAG4AegBlAG4AdABvAHMAIABk -AGEAIABHAGEAbQBtAGEAIAAyACwAMgBBAGwAZwBlAG0AZQBlAG4AIABnAHIAaQBqAHMAIABnAGEAbQBt -AGEAIAAyACwAMgAtAHAAcgBvAGYAaQBlAGwAUABlAHIAZgBpAGwAIABnAGUAbgDpAHIAaQBjAG8AIABk -AGUAIABnAGEAbQBtAGEAIABkAGUAIABnAHIAaQBzAGUAcwAgADIALAAyDiMOMQ4HDioONQ5BDgEOIQ4h -DjIOQA4BDiMOIg5MDhcOMQ5IDicORA4bACAAMgAuADIARwBlAG4AZQBsACAARwByAGkAIABHAGEAbQBh -ACAAMgAsADIAWQBsAGUAaQBuAGUAbgAgAGgAYQByAG0AYQBhAG4AIABnAGEAbQBtAGEAIAAyACwAMgAg -AC0AcAByAG8AZgBpAGkAbABpAEcAZQBuAGUAcgBpAQ0AawBpACAARwByAGEAeQAgAEcAYQBtAG0AYQAg -ADIALgAyACAAcAByAG8AZgBpAGwAVQBuAGkAdwBlAHIAcwBhAGwAbgB5ACAAcAByAG8AZgBpAGwAIABz -AHoAYQByAG8BWwBjAGkAIABnAGEAbQBtAGEAIAAyACwAMgY6BicGRQYnACAAMgAuADIAIAZEBkgGRgAg -BjEGRQYnBi8GSgAgBjkGJwZFBB4EMQRJBDAETwAgBEEENQRABDAETwAgBDMEMAQ8BDwEMAAgADIALAAy -AC0EPwRABD4ERAQ4BDsETABHAGUAbgBlAHIAaQBjACAARwByAGEAeQAgAEcAYQBtAG0AYQAgADIALgAy -ACAAUAByAG8AZgBpAGwAZQAAdGV4dAAAAABDb3B5cmlnaHQgQXBwbGUgSW5jLiwgMjAxMgAAWFlaIAAA -AAAAAPNRAAEAAAABFsxjdXJ2AAAAAAAABAAAAAAFAAoADwAUABkAHgAjACgALQAyADcAOwBAAEUASgBP -AFQAWQBeAGMAaABtAHIAdwB8AIEAhgCLAJAAlQCaAJ8ApACpAK4AsgC3ALwAwQDGAMsA0ADVANsA4ADl -AOsA8AD2APsBAQEHAQ0BEwEZAR8BJQErATIBOAE+AUUBTAFSAVkBYAFnAW4BdQF8AYMBiwGSAZoBoQGp -AbEBuQHBAckB0QHZAeEB6QHyAfoCAwIMAhQCHQImAi8COAJBAksCVAJdAmcCcQJ6AoQCjgKYAqICrAK2 -AsECywLVAuAC6wL1AwADCwMWAyEDLQM4A0MDTwNaA2YDcgN+A4oDlgOiA64DugPHA9MD4APsA/kEBgQT -BCAELQQ7BEgEVQRjBHEEfgSMBJoEqAS2BMQE0wThBPAE/gUNBRwFKwU6BUkFWAVnBXcFhgWWBaYFtQXF -BdUF5QX2BgYGFgYnBjcGSAZZBmoGewaMBp0GrwbABtEG4wb1BwcHGQcrBz0HTwdhB3QHhgeZB6wHvwfS -B+UH+AgLCB8IMghGCFoIbgiCCJYIqgi+CNII5wj7CRAJJQk6CU8JZAl5CY8JpAm6Cc8J5Qn7ChEKJwo9 -ClQKagqBCpgKrgrFCtwK8wsLCyILOQtRC2kLgAuYC7ALyAvhC/kMEgwqDEMMXAx1DI4MpwzADNkM8w0N -DSYNQA1aDXQNjg2pDcMN3g34DhMOLg5JDmQOfw6bDrYO0g7uDwkPJQ9BD14Peg+WD7MPzw/sEAkQJhBD -EGEQfhCbELkQ1xD1ERMRMRFPEW0RjBGqEckR6BIHEiYSRRJkEoQSoxLDEuMTAxMjE0MTYxODE6QTxRPl -FAYUJxRJFGoUixStFM4U8BUSFTQVVhV4FZsVvRXgFgMWJhZJFmwWjxayFtYW+hcdF0EXZReJF64X0hf3 -GBsYQBhlGIoYrxjVGPoZIBlFGWsZkRm3Gd0aBBoqGlEadxqeGsUa7BsUGzsbYxuKG7Ib2hwCHCocUhx7 -HKMczBz1HR4dRx1wHZkdwx3sHhYeQB5qHpQevh7pHxMfPh9pH5Qfvx/qIBUgQSBsIJggxCDwIRwhSCF1 -IaEhziH7IiciVSKCIq8i3SMKIzgjZiOUI8Ij8CQfJE0kfCSrJNolCSU4JWgllyXHJfcmJyZXJocmtybo -JxgnSSd6J6sn3CgNKD8ocSiiKNQpBik4KWspnSnQKgIqNSpoKpsqzysCKzYraSudK9EsBSw5LG4soizX -LQwtQS12Last4S4WLkwugi63Lu4vJC9aL5Evxy/+MDUwbDCkMNsxEjFKMYIxujHyMioyYzKbMtQzDTNG -M38zuDPxNCs0ZTSeNNg1EzVNNYc1wjX9Njc2cjauNuk3JDdgN5w31zgUOFA4jDjIOQU5Qjl/Obw5+To2 -OnQ6sjrvOy07azuqO+g8JzxlPKQ84z0iPWE9oT3gPiA+YD6gPuA/IT9hP6I/4kAjQGRApkDnQSlBakGs -Qe5CMEJyQrVC90M6Q31DwEQDREdEikTORRJFVUWaRd5GIkZnRqtG8Ec1R3tHwEgFSEtIkUjXSR1JY0mp -SfBKN0p9SsRLDEtTS5pL4kwqTHJMuk0CTUpNk03cTiVObk63TwBPSU+TT91QJ1BxULtRBlFQUZtR5lIx -UnxSx1MTU19TqlP2VEJUj1TbVShVdVXCVg9WXFapVvdXRFeSV+BYL1h9WMtZGllpWbhaB1pWWqZa9VtF -W5Vb5Vw1XIZc1l0nXXhdyV4aXmxevV8PX2Ffs2AFYFdgqmD8YU9homH1YklinGLwY0Njl2PrZEBklGTp -ZT1lkmXnZj1mkmboZz1nk2fpaD9olmjsaUNpmmnxakhqn2r3a09rp2v/bFdsr20IbWBtuW4SbmtuxG8e -b3hv0XArcIZw4HE6cZVx8HJLcqZzAXNdc7h0FHRwdMx1KHWFdeF2Pnabdvh3VnezeBF4bnjMeSp5iXnn -ekZ6pXsEe2N7wnwhfIF84X1BfaF+AX5ifsJ/I3+Ef+WAR4CogQqBa4HNgjCCkoL0g1eDuoQdhICE44VH -hauGDoZyhteHO4efiASIaYjOiTOJmYn+imSKyoswi5aL/IxjjMqNMY2Yjf+OZo7OjzaPnpAGkG6Q1pE/ -kaiSEZJ6kuOTTZO2lCCUipT0lV+VyZY0lp+XCpd1l+CYTJi4mSSZkJn8mmia1ZtCm6+cHJyJnPedZJ3S -nkCerp8dn4uf+qBpoNihR6G2oiailqMGo3aj5qRWpMelOKWpphqmi6b9p26n4KhSqMSpN6mpqhyqj6sC -q3Wr6axcrNCtRK24ri2uoa8Wr4uwALB1sOqxYLHWskuywrM4s660JbSctRO1irYBtnm28Ldot+C4WbjR -uUq5wro7urW7LrunvCG8m70VvY++Cr6Evv+/er/1wHDA7MFnwePCX8Lbw1jD1MRRxM7FS8XIxkbGw8dB -x7/IPci8yTrJuco4yrfLNsu2zDXMtc01zbXONs62zzfPuNA50LrRPNG+0j/SwdNE08bUSdTL1U7V0dZV -1tjXXNfg2GTY6Nls2fHadtr724DcBdyK3RDdlt4c3qLfKd+v4DbgveFE4cziU+Lb42Pj6+Rz5PzlhOYN -5pbnH+ep6DLovOlG6dDqW+rl63Dr++yG7RHtnO4o7rTvQO/M8Fjw5fFy8f/yjPMZ86f0NPTC9VD13vZt -9vv3ivgZ+Kj5OPnH+lf65/t3/Af8mP0p/br+S/7c/23//9IrLC0uWiRjbGFzc25hbWVYJGNsYXNzZXNf -EBBOU0JpdG1hcEltYWdlUmVwoy0vMFpOU0ltYWdlUmVwWE5TT2JqZWN00issMjNXTlNBcnJheaIyMNIr -LDU2Xk5TTXV0YWJsZUFycmF5ozUyMNM4OQ86OzxXTlNXaGl0ZVxOU0NvbG9yU3BhY2VEMCAwABADgAzS -Kyw+P1dOU0NvbG9yoj4w0issQUJXTlNJbWFnZaJBMAAIABEAGgAkACkAMgA3AEkATABRAFMAYgBoAHUA -fACLAJIAnwCmAK4AsACyALQAuQC7AL0AxADJANQA1gDYANoA3wDiAOQA5gDoAO8BBgEiASQBJhOWE5sT -phOvE8ITxhPRE9oT3xPnE+oT7xP+FAIUCRQRFB4UIxQlFCcULBQ0FDcUPBREAAAAAAAAAgEAAAAAAAAA -QwAAAAAAAAAAAAAAAAAAFEc +S2V5ZWRBcmNoaXZlctEICVRyb290gAGuCwwbHCEaJissMzY5P0JVJG51bGzXDQ4PEBESExQVFhcYGRpW +TlNTaXplViRjbGFzc1xOU0ltYWdlRmxhZ3NWTlNSZXBzW05TVGludENvbG9yV05TQ29sb3JeTlNSZXNp +emluZ01vZGWAAoANEiDDAACAA4AAgAsQAFZ7MSwgMX3SHQ4eIFpOUy5vYmplY3RzoR+ABIAK0h0OIiWi +IySABYAGgAnTDicoKSoaXxAUTlNUSUZGUmVwcmVzZW50YXRpb25fEBlOU0ludGVybmFsTGF5b3V0RGly +ZWN0aW9ugAiAB08REmxNTQAqAAAACgAAABABAAADAAAAAQABAAABAQADAAAAAQABAAABAgADAAAAAgAI +AAgBAwADAAAAAQABAAABBgADAAAAAQABAAABCgADAAAAAQABAAABEQAEAAAAAQAAAAgBEgADAAAAAQAB +AAABFQADAAAAAQACAAABFgADAAAAAQABAAABFwAEAAAAAQAAAAIBHAADAAAAAQABAAABKAADAAAAAQAC +AAABUgADAAAAAQABAAABUwADAAAAAgABAAGHcwAHAAARnAAAANAAAAAAAAARnGFwcGwCAAAAbW50ckdS +QVlYWVogB9wACAAXAA8ALgAPYWNzcEFQUEwAAAAAbm9uZQAAAAAAAAAAAAAAAAAAAAAAAPbWAAEAAAAA +0y1hcHBsAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFZGVzYwAA +AMAAAAB5ZHNjbQAAATwAAAgaY3BydAAACVgAAAAjd3RwdAAACXwAAAAUa1RSQwAACZAAAAgMZGVzYwAA +AAAAAAAfR2VuZXJpYyBHcmF5IEdhbW1hIDIuMiBQcm9maWxlAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAG1s +dWMAAAAAAAAAHwAAAAxza1NLAAAALgAAAYRkYURLAAAAOgAAAbJjYUVTAAAAOAAAAex2aVZOAAAAQAAA +AiRwdEJSAAAASgAAAmR1a1VBAAAALAAAAq5mckZVAAAAPgAAAtpodUhVAAAANAAAAxh6aFRXAAAAGgAA +A0xrb0tSAAAAIgAAA2ZuYk5PAAAAOgAAA4hjc0NaAAAAKAAAA8JoZUlMAAAAJAAAA+pyb1JPAAAAKgAA +BA5kZURFAAAATgAABDhpdElUAAAATgAABIZzdlNFAAAAOAAABNR6aENOAAAAGgAABQxqYUpQAAAAJgAA +BSZlbEdSAAAAKgAABUxwdFBPAAAAUgAABXZubE5MAAAAQAAABchlc0VTAAAATAAABgh0aFRIAAAAMgAA +BlR0clRSAAAAJAAABoZmaUZJAAAARgAABqpockhSAAAAPgAABvBwbFBMAAAASgAABy5hckVHAAAALAAA +B3hydVJVAAAAOgAAB6RlblVTAAAAPAAAB94AVgFhAGUAbwBiAGUAYwBuAOEAIABzAGkAdgDhACAAZwBh +AG0AYQAgADIALAAyAEcAZQBuAGUAcgBpAHMAawAgAGcAcgDlACAAMgAsADIAIABnAGEAbQBtAGEALQBw +AHIAbwBmAGkAbABHAGEAbQBtAGEAIABkAGUAIABnAHIAaQBzAG8AcwAgAGcAZQBuAOgAcgBpAGMAYQAg +ADIALgAyAEMepQB1ACAAaADsAG4AaAAgAE0A4AB1ACAAeADhAG0AIABDAGgAdQBuAGcAIABHAGEAbQBt +AGEAIAAyAC4AMgBQAGUAcgBmAGkAbAAgAEcAZQBuAOkAcgBpAGMAbwAgAGQAYQAgAEcAYQBtAGEAIABk +AGUAIABDAGkAbgB6AGEAcwAgADIALAAyBBcEMAQzBDAEOwRMBD0EMAAgAEcAcgBhAHkALQQzBDAEPAQw +ACAAMgAuADIAUAByAG8AZgBpAGwAIABnAOkAbgDpAHIAaQBxAHUAZQAgAGcAcgBpAHMAIABnAGEAbQBt +AGEAIAAyACwAMgDBAGwAdABhAGwA4QBuAG8AcwAgAHMAegD8AHIAawBlACAAZwBhAG0AbQBhACAAMgAu +ADKQGnUocHCWjlFJXqYAMgAuADKCcl9pY8+P8Md8vBgAINaMwMkAIKwQucgAIAAyAC4AMgAg1QS4XNMM +x3wARwBlAG4AZQByAGkAcwBrACAAZwByAOUAIABnAGEAbQBtAGEAIAAyACwAMgAtAHAAcgBvAGYAaQBs +AE8AYgBlAGMAbgDhACABYQBlAGQA4QAgAGcAYQBtAGEAIAAyAC4AMgXSBdAF3gXUACAF0AXkBdUF6AAg +BdsF3AXcBdkAIAAyAC4AMgBHAGEAbQBhACAAZwByAGkAIABnAGUAbgBlAHIAaQBjAQMAIAAyACwAMgBB +AGwAbABnAGUAbQBlAGkAbgBlAHMAIABHAHIAYQB1AHMAdAB1AGYAZQBuAC0AUAByAG8AZgBpAGwAIABH +AGEAbQBtAGEAIAAyACwAMgBQAHIAbwBmAGkAbABvACAAZwByAGkAZwBpAG8AIABnAGUAbgBlAHIAaQBj +AG8AIABkAGUAbABsAGEAIABnAGEAbQBtAGEAIAAyACwAMgBHAGUAbgBlAHIAaQBzAGsAIABnAHIA5QAg +ADIALAAyACAAZwBhAG0AbQBhAHAAcgBvAGYAaQBsZm6QGnBwXqZ8+2VwADIALgAyY8+P8GWHTvZOAIIs +MLAw7DCkMKww8zDeACAAMgAuADIAIDDXMO0w1TChMKQw6wOTA7UDvQO5A7oDzAAgA5MDugPBA7kAIAOT +A6wDvAO8A7EAIAAyAC4AMgBQAGUAcgBmAGkAbAAgAGcAZQBuAOkAcgBpAGMAbwAgAGQAZQAgAGMAaQBu +AHoAZQBuAHQAbwBzACAAZABhACAARwBhAG0AbQBhACAAMgAsADIAQQBsAGcAZQBtAGUAZQBuACAAZwBy +AGkAagBzACAAZwBhAG0AbQBhACAAMgAsADIALQBwAHIAbwBmAGkAZQBsAFAAZQByAGYAaQBsACAAZwBl +AG4A6QByAGkAYwBvACAAZABlACAAZwBhAG0AbQBhACAAZABlACAAZwByAGkAcwBlAHMAIAAyACwAMg4j +DjEOBw4qDjUOQQ4BDiEOIQ4yDkAOAQ4jDiIOTA4XDjEOSA4nDkQOGwAgADIALgAyAEcAZQBuAGUAbAAg +AEcAcgBpACAARwBhAG0AYQAgADIALAAyAFkAbABlAGkAbgBlAG4AIABoAGEAcgBtAGEAYQBuACAAZwBh +AG0AbQBhACAAMgAsADIAIAAtAHAAcgBvAGYAaQBpAGwAaQBHAGUAbgBlAHIAaQENAGsAaQAgAEcAcgBh +AHkAIABHAGEAbQBtAGEAIAAyAC4AMgAgAHAAcgBvAGYAaQBsAFUAbgBpAHcAZQByAHMAYQBsAG4AeQAg +AHAAcgBvAGYAaQBsACAAcwB6AGEAcgBvAVsAYwBpACAAZwBhAG0AbQBhACAAMgAsADIGOgYnBkUGJwAg +ADIALgAyACAGRAZIBkYAIAYxBkUGJwYvBkoAIAY5BicGRQQeBDEESQQwBE8AIARBBDUEQAQwBE8AIAQz +BDAEPAQ8BDAAIAAyACwAMgAtBD8EQAQ+BEQEOAQ7BEwARwBlAG4AZQByAGkAYwAgAEcAcgBhAHkAIABH +AGEAbQBtAGEAIAAyAC4AMgAgAFAAcgBvAGYAaQBsAGUAAHRleHQAAAAAQ29weXJpZ2h0IEFwcGxlIElu +Yy4sIDIwMTIAAFhZWiAAAAAAAADzUQABAAAAARbMY3VydgAAAAAAAAQAAAAABQAKAA8AFAAZAB4AIwAo +AC0AMgA3ADsAQABFAEoATwBUAFkAXgBjAGgAbQByAHcAfACBAIYAiwCQAJUAmgCfAKQAqQCuALIAtwC8 +AMEAxgDLANAA1QDbAOAA5QDrAPAA9gD7AQEBBwENARMBGQEfASUBKwEyATgBPgFFAUwBUgFZAWABZwFu +AXUBfAGDAYsBkgGaAaEBqQGxAbkBwQHJAdEB2QHhAekB8gH6AgMCDAIUAh0CJgIvAjgCQQJLAlQCXQJn +AnECegKEAo4CmAKiAqwCtgLBAssC1QLgAusC9QMAAwsDFgMhAy0DOANDA08DWgNmA3IDfgOKA5YDogOu +A7oDxwPTA+AD7AP5BAYEEwQgBC0EOwRIBFUEYwRxBH4EjASaBKgEtgTEBNME4QTwBP4FDQUcBSsFOgVJ +BVgFZwV3BYYFlgWmBbUFxQXVBeUF9gYGBhYGJwY3BkgGWQZqBnsGjAadBq8GwAbRBuMG9QcHBxkHKwc9 +B08HYQd0B4YHmQesB78H0gflB/gICwgfCDIIRghaCG4IggiWCKoIvgjSCOcI+wkQCSUJOglPCWQJeQmP +CaQJugnPCeUJ+woRCicKPQpUCmoKgQqYCq4KxQrcCvMLCwsiCzkLUQtpC4ALmAuwC8gL4Qv5DBIMKgxD +DFwMdQyODKcMwAzZDPMNDQ0mDUANWg10DY4NqQ3DDd4N+A4TDi4OSQ5kDn8Omw62DtIO7g8JDyUPQQ9e +D3oPlg+zD88P7BAJECYQQxBhEH4QmxC5ENcQ9RETETERTxFtEYwRqhHJEegSBxImEkUSZBKEEqMSwxLj +EwMTIxNDE2MTgxOkE8UT5RQGFCcUSRRqFIsUrRTOFPAVEhU0FVYVeBWbFb0V4BYDFiYWSRZsFo8WshbW +FvoXHRdBF2UXiReuF9IX9xgbGEAYZRiKGK8Y1Rj6GSAZRRlrGZEZtxndGgQaKhpRGncanhrFGuwbFBs7 +G2MbihuyG9ocAhwqHFIcexyjHMwc9R0eHUcdcB2ZHcMd7B4WHkAeah6UHr4e6R8THz4faR+UH78f6iAV +IEEgbCCYIMQg8CEcIUghdSGhIc4h+yInIlUigiKvIt0jCiM4I2YjlCPCI/AkHyRNJHwkqyTaJQklOCVo +JZclxyX3JicmVyaHJrcm6CcYJ0kneierJ9woDSg/KHEooijUKQYpOClrKZ0p0CoCKjUqaCqbKs8rAis2 +K2krnSvRLAUsOSxuLKIs1y0MLUEtdi2rLeEuFi5MLoIuty7uLyQvWi+RL8cv/jA1MGwwpDDbMRIxSjGC +Mbox8jIqMmMymzLUMw0zRjN/M7gz8TQrNGU0njTYNRM1TTWHNcI1/TY3NnI2rjbpNyQ3YDecN9c4FDhQ +OIw4yDkFOUI5fzm8Ofk6Njp0OrI67zstO2s7qjvoPCc8ZTykPOM9Ij1hPaE94D4gPmA+oD7gPyE/YT+i +P+JAI0BkQKZA50EpQWpBrEHuQjBCckK1QvdDOkN9Q8BEA0RHRIpEzkUSRVVFmkXeRiJGZ0arRvBHNUd7 +R8BIBUhLSJFI10kdSWNJqUnwSjdKfUrESwxLU0uaS+JMKkxyTLpNAk1KTZNN3E4lTm5Ot08AT0lPk0/d +UCdQcVC7UQZRUFGbUeZSMVJ8UsdTE1NfU6pT9lRCVI9U21UoVXVVwlYPVlxWqVb3V0RXklfgWC9YfVjL +WRpZaVm4WgdaVlqmWvVbRVuVW+VcNVyGXNZdJ114XcleGl5sXr1fD19hX7NgBWBXYKpg/GFPYaJh9WJJ +Ypxi8GNDY5dj62RAZJRk6WU9ZZJl52Y9ZpJm6Gc9Z5Nn6Wg/aJZo7GlDaZpp8WpIap9q92tPa6dr/2xX +bK9tCG1gbbluEm5rbsRvHm94b9FwK3CGcOBxOnGVcfByS3KmcwFzXXO4dBR0cHTMdSh1hXXhdj52m3b4 +d1Z3s3gReG54zHkqeYl553pGeqV7BHtje8J8IXyBfOF9QX2hfgF+Yn7CfyN/hH/lgEeAqIEKgWuBzYIw +gpKC9INXg7qEHYSAhOOFR4Wrhg6GcobXhzuHn4gEiGmIzokziZmJ/opkisqLMIuWi/yMY4zKjTGNmI3/ +jmaOzo82j56QBpBukNaRP5GokhGSepLjk02TtpQglIqU9JVflcmWNJaflwqXdZfgmEyYuJkkmZCZ/Jpo +mtWbQpuvnByciZz3nWSd0p5Anq6fHZ+Ln/qgaaDYoUehtqImopajBqN2o+akVqTHpTilqaYapoum/adu +p+CoUqjEqTepqaocqo+rAqt1q+msXKzQrUStuK4trqGvFq+LsACwdbDqsWCx1rJLssKzOLOutCW0nLUT +tYq2AbZ5tvC3aLfguFm40blKucK6O7q1uy67p7whvJu9Fb2Pvgq+hL7/v3q/9cBwwOzBZ8Hjwl/C28NY +w9TEUcTOxUvFyMZGxsPHQce/yD3IvMk6ybnKOMq3yzbLtsw1zLXNNc21zjbOts83z7jQOdC60TzRvtI/ +0sHTRNPG1EnUy9VO1dHWVdbY11zX4Nhk2OjZbNnx2nba+9uA3AXcit0Q3ZbeHN6i3ynfr+A24L3hROHM +4lPi2+Nj4+vkc+T85YTmDeaW5x/nqegy6LzpRunQ6lvq5etw6/vshu0R7ZzuKO6070DvzPBY8OXxcvH/ +8ozzGfOn9DT0wvVQ9d72bfb794r4Gfio+Tj5x/pX+uf7d/wH/Jj9Kf26/kv+3P9t///SLS4vMFokY2xh +c3NuYW1lWCRjbGFzc2VzXxAQTlNCaXRtYXBJbWFnZVJlcKMvMTJaTlNJbWFnZVJlcFhOU09iamVjdNIt +LjQ1V05TQXJyYXmiNDLSLS43OF5OU011dGFibGVBcnJheaM3NDLTOjsOPD0+V05TV2hpdGVcTlNDb2xv +clNwYWNlRDAgMAAQA4AM0i0uQEFXTlNDb2xvcqJAMtItLkNEV05TSW1hZ2WiQzIACAARABoAJAApADIA +NwBJAEwAUQBTAGIAaAB3AH4AhQCSAJkApQCtALwAvgDAAMUAxwDJAMsAzQDUANkA5ADmAOgA6gDvAPIA +9AD2APgA/wEWATIBNAE2E6YTqxO2E78T0hPWE+ET6hPvE/cT+hP/FA4UEhQZFCEULhQzFDUUNxQ8FEQU +RxRMFFQAAAAAAAACAQAAAAAAAABFAAAAAAAAAAAAAAAAAAAUVw diff --git a/i18n/src/main/resources/zh_CN.lproj/Command.xib b/i18n/src/main/resources/zh_CN.lproj/Command.xib index d37d3414de..f982a5a699 100644 --- a/i18n/src/main/resources/zh_CN.lproj/Command.xib +++ b/i18n/src/main/resources/zh_CN.lproj/Command.xib @@ -1,7 +1,7 @@ - + - + diff --git a/i18n/src/main/resources/zh_CN.lproj/Connection.xib b/i18n/src/main/resources/zh_CN.lproj/Connection.xib index 163d0f747d..9b5a42d9b4 100644 --- a/i18n/src/main/resources/zh_CN.lproj/Connection.xib +++ b/i18n/src/main/resources/zh_CN.lproj/Connection.xib @@ -1,7 +1,7 @@ - + - + @@ -76,7 +76,7 @@ - + diff --git a/i18n/src/main/resources/zh_CN.lproj/Info.xib b/i18n/src/main/resources/zh_CN.lproj/Info.xib index 98b2a47c10..a8c3d89f2e 100644 --- a/i18n/src/main/resources/zh_CN.lproj/Info.xib +++ b/i18n/src/main/resources/zh_CN.lproj/Info.xib @@ -1,7 +1,7 @@ - + - + @@ -89,7 +89,7 @@ - + @@ -1017,21 +1017,21 @@ - + - + - + @@ -1121,21 +1121,21 @@ - + - + - + @@ -1250,27 +1250,27 @@ diff --git a/i18n/src/main/resources/zh_CN.lproj/Login.strings b/i18n/src/main/resources/zh_CN.lproj/Login.strings index 3d0e86cfe6..78651317ab 100644 Binary files a/i18n/src/main/resources/zh_CN.lproj/Login.strings and b/i18n/src/main/resources/zh_CN.lproj/Login.strings differ diff --git a/i18n/src/main/resources/zh_CN.lproj/Main.xib b/i18n/src/main/resources/zh_CN.lproj/Main.xib index 6234676dd5..229ec9e354 100644 --- a/i18n/src/main/resources/zh_CN.lproj/Main.xib +++ b/i18n/src/main/resources/zh_CN.lproj/Main.xib @@ -1,7 +1,7 @@ - + - + @@ -94,7 +94,7 @@ - + @@ -159,19 +159,19 @@ - + - + - + @@ -321,12 +321,12 @@ CA - + - + @@ -566,6 +566,10 @@ CA - + + + + + diff --git a/i18n/src/main/resources/zh_CN.lproj/Preferences.xib b/i18n/src/main/resources/zh_CN.lproj/Preferences.xib index 2eeda33b95..406e6e81bf 100644 --- a/i18n/src/main/resources/zh_CN.lproj/Preferences.xib +++ b/i18n/src/main/resources/zh_CN.lproj/Preferences.xib @@ -1,7 +1,7 @@ - + - + @@ -266,7 +266,7 @@ - + @@ -307,7 +307,7 @@ - + @@ -538,7 +538,7 @@ - + @@ -578,7 +578,7 @@ - + @@ -613,7 +613,7 @@ - + @@ -642,7 +642,7 @@ - + @@ -1218,7 +1218,7 @@ - + @@ -1250,7 +1250,7 @@ - + @@ -1289,7 +1289,7 @@ - + @@ -1362,7 +1362,7 @@ - + @@ -1557,7 +1557,7 @@ - + @@ -1617,7 +1617,7 @@ - + @@ -1642,7 +1642,7 @@ - + @@ -1684,7 +1684,7 @@ - + @@ -1712,7 +1712,7 @@ - + @@ -1740,7 +1740,7 @@ - + @@ -1768,7 +1768,7 @@ - + @@ -1803,7 +1803,7 @@ - + @@ -1886,7 +1886,7 @@ - + @@ -1945,7 +1945,7 @@ - + @@ -1973,7 +1973,7 @@ - + @@ -2001,7 +2001,7 @@ - + diff --git a/i18n/src/main/resources/zh_CN.lproj/Profiles.xib b/i18n/src/main/resources/zh_CN.lproj/Profiles.xib index 744f4349d6..c849d7cb37 100644 --- a/i18n/src/main/resources/zh_CN.lproj/Profiles.xib +++ b/i18n/src/main/resources/zh_CN.lproj/Profiles.xib @@ -1,7 +1,7 @@ - + - + diff --git a/i18n/src/main/resources/zh_CN.lproj/Prompt.xib b/i18n/src/main/resources/zh_CN.lproj/Prompt.xib index 8d32eced21..255ec41a7b 100644 --- a/i18n/src/main/resources/zh_CN.lproj/Prompt.xib +++ b/i18n/src/main/resources/zh_CN.lproj/Prompt.xib @@ -1,7 +1,7 @@ - + - + @@ -95,7 +95,7 @@ Gw - + diff --git a/i18n/src/main/resources/zh_CN.lproj/S3.strings b/i18n/src/main/resources/zh_CN.lproj/S3.strings index 5f456dd7b5..fee50f6463 100644 Binary files a/i18n/src/main/resources/zh_CN.lproj/S3.strings and b/i18n/src/main/resources/zh_CN.lproj/S3.strings differ diff --git a/i18n/src/main/resources/zh_CN.lproj/Updater.strings b/i18n/src/main/resources/zh_CN.lproj/Updater.strings index 8ac3e18665..8a687289f0 100644 Binary files a/i18n/src/main/resources/zh_CN.lproj/Updater.strings and b/i18n/src/main/resources/zh_CN.lproj/Updater.strings differ diff --git a/i18n/src/main/resources/zh_TW.lproj/Bookmark.xib b/i18n/src/main/resources/zh_TW.lproj/Bookmark.xib index 80b03ee440..78c36bcbf0 100644 --- a/i18n/src/main/resources/zh_TW.lproj/Bookmark.xib +++ b/i18n/src/main/resources/zh_TW.lproj/Bookmark.xib @@ -1,7 +1,7 @@ - + - + @@ -92,7 +92,7 @@ - + @@ -489,95 +489,95 @@ YnBsaXN0MDDUAQIDBAUGBwpYJHZlcnNpb25ZJGFyY2hpdmVyVCR0b3BYJG9iamVjdHMSAAGGoF8QD05T -S2V5ZWRBcmNoaXZlctEICVRyb290gAGuCwwZGh8UJCkqMTQ3PUBVJG51bGzWDQ4PEBESExQVFhcYVk5T -U2l6ZV5OU1Jlc2l6aW5nTW9kZVYkY2xhc3NcTlNJbWFnZUZsYWdzVk5TUmVwc1dOU0NvbG9ygAIQAIAN -EiDDAACAA4ALVnsxLCAxfdIbDxweWk5TLm9iamVjdHOhHYAEgArSGw8gI6IhIoAFgAaACdMPJSYnKBRf -EBROU1RJRkZSZXByZXNlbnRhdGlvbl8QGU5TSW50ZXJuYWxMYXlvdXREaXJlY3Rpb26ACIAHTxESbE1N -ACoAAAAKAAAAEAEAAAMAAAABAAEAAAEBAAMAAAABAAEAAAECAAMAAAACAAgACAEDAAMAAAABAAEAAAEG -AAMAAAABAAEAAAEKAAMAAAABAAEAAAERAAQAAAABAAAACAESAAMAAAABAAEAAAEVAAMAAAABAAIAAAEW -AAMAAAABAAEAAAEXAAQAAAABAAAAAgEcAAMAAAABAAEAAAEoAAMAAAABAAIAAAFSAAMAAAABAAEAAAFT -AAMAAAACAAEAAYdzAAcAABGcAAAA0AAAAAAAABGcYXBwbAIAAABtbnRyR1JBWVhZWiAH3AAIABcADwAu -AA9hY3NwQVBQTAAAAABub25lAAAAAAAAAAAAAAAAAAAAAAAA9tYAAQAAAADTLWFwcGwAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAVkZXNjAAAAwAAAAHlkc2NtAAABPAAA -CBpjcHJ0AAAJWAAAACN3dHB0AAAJfAAAABRrVFJDAAAJkAAACAxkZXNjAAAAAAAAAB9HZW5lcmljIEdy -YXkgR2FtbWEgMi4yIFByb2ZpbGUAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAbWx1YwAAAAAAAAAfAAAADHNr -U0sAAAAuAAABhGRhREsAAAA6AAABsmNhRVMAAAA4AAAB7HZpVk4AAABAAAACJHB0QlIAAABKAAACZHVr -VUEAAAAsAAACrmZyRlUAAAA+AAAC2mh1SFUAAAA0AAADGHpoVFcAAAAaAAADTGtvS1IAAAAiAAADZm5i -Tk8AAAA6AAADiGNzQ1oAAAAoAAADwmhlSUwAAAAkAAAD6nJvUk8AAAAqAAAEDmRlREUAAABOAAAEOGl0 -SVQAAABOAAAEhnN2U0UAAAA4AAAE1HpoQ04AAAAaAAAFDGphSlAAAAAmAAAFJmVsR1IAAAAqAAAFTHB0 -UE8AAABSAAAFdm5sTkwAAABAAAAFyGVzRVMAAABMAAAGCHRoVEgAAAAyAAAGVHRyVFIAAAAkAAAGhmZp -RkkAAABGAAAGqmhySFIAAAA+AAAG8HBsUEwAAABKAAAHLmFyRUcAAAAsAAAHeHJ1UlUAAAA6AAAHpGVu -VVMAAAA8AAAH3gBWAWEAZQBvAGIAZQBjAG4A4QAgAHMAaQB2AOEAIABnAGEAbQBhACAAMgAsADIARwBl -AG4AZQByAGkAcwBrACAAZwByAOUAIAAyACwAMgAgAGcAYQBtAG0AYQAtAHAAcgBvAGYAaQBsAEcAYQBt -AG0AYQAgAGQAZQAgAGcAcgBpAHMAbwBzACAAZwBlAG4A6AByAGkAYwBhACAAMgAuADIAQx6lAHUAIABo -AOwAbgBoACAATQDgAHUAIAB4AOEAbQAgAEMAaAB1AG4AZwAgAEcAYQBtAG0AYQAgADIALgAyAFAAZQBy -AGYAaQBsACAARwBlAG4A6QByAGkAYwBvACAAZABhACAARwBhAG0AYQAgAGQAZQAgAEMAaQBuAHoAYQBz -ACAAMgAsADIEFwQwBDMEMAQ7BEwEPQQwACAARwByAGEAeQAtBDMEMAQ8BDAAIAAyAC4AMgBQAHIAbwBm -AGkAbAAgAGcA6QBuAOkAcgBpAHEAdQBlACAAZwByAGkAcwAgAGcAYQBtAG0AYQAgADIALAAyAMEAbAB0 -AGEAbADhAG4AbwBzACAAcwB6APwAcgBrAGUAIABnAGEAbQBtAGEAIAAyAC4AMpAadShwcJaOUUlepgAy -AC4AMoJyX2ljz4/wx3y8GAAg1ozAyQAgrBC5yAAgADIALgAyACDVBLhc0wzHfABHAGUAbgBlAHIAaQBz -AGsAIABnAHIA5QAgAGcAYQBtAG0AYQAgADIALAAyAC0AcAByAG8AZgBpAGwATwBiAGUAYwBuAOEAIAFh -AGUAZADhACAAZwBhAG0AYQAgADIALgAyBdIF0AXeBdQAIAXQBeQF1QXoACAF2wXcBdwF2QAgADIALgAy -AEcAYQBtAGEAIABnAHIAaQAgAGcAZQBuAGUAcgBpAGMBAwAgADIALAAyAEEAbABsAGcAZQBtAGUAaQBu -AGUAcwAgAEcAcgBhAHUAcwB0AHUAZgBlAG4ALQBQAHIAbwBmAGkAbAAgAEcAYQBtAG0AYQAgADIALAAy -AFAAcgBvAGYAaQBsAG8AIABnAHIAaQBnAGkAbwAgAGcAZQBuAGUAcgBpAGMAbwAgAGQAZQBsAGwAYQAg -AGcAYQBtAG0AYQAgADIALAAyAEcAZQBuAGUAcgBpAHMAawAgAGcAcgDlACAAMgAsADIAIABnAGEAbQBt -AGEAcAByAG8AZgBpAGxmbpAacHBepnz7ZXAAMgAuADJjz4/wZYdO9k4AgiwwsDDsMKQwrDDzMN4AIAAy -AC4AMgAgMNcw7TDVMKEwpDDrA5MDtQO9A7kDugPMACADkwO6A8EDuQAgA5MDrAO8A7wDsQAgADIALgAy -AFAAZQByAGYAaQBsACAAZwBlAG4A6QByAGkAYwBvACAAZABlACAAYwBpAG4AegBlAG4AdABvAHMAIABk -AGEAIABHAGEAbQBtAGEAIAAyACwAMgBBAGwAZwBlAG0AZQBlAG4AIABnAHIAaQBqAHMAIABnAGEAbQBt -AGEAIAAyACwAMgAtAHAAcgBvAGYAaQBlAGwAUABlAHIAZgBpAGwAIABnAGUAbgDpAHIAaQBjAG8AIABk -AGUAIABnAGEAbQBtAGEAIABkAGUAIABnAHIAaQBzAGUAcwAgADIALAAyDiMOMQ4HDioONQ5BDgEOIQ4h -DjIOQA4BDiMOIg5MDhcOMQ5IDicORA4bACAAMgAuADIARwBlAG4AZQBsACAARwByAGkAIABHAGEAbQBh -ACAAMgAsADIAWQBsAGUAaQBuAGUAbgAgAGgAYQByAG0AYQBhAG4AIABnAGEAbQBtAGEAIAAyACwAMgAg -AC0AcAByAG8AZgBpAGkAbABpAEcAZQBuAGUAcgBpAQ0AawBpACAARwByAGEAeQAgAEcAYQBtAG0AYQAg -ADIALgAyACAAcAByAG8AZgBpAGwAVQBuAGkAdwBlAHIAcwBhAGwAbgB5ACAAcAByAG8AZgBpAGwAIABz -AHoAYQByAG8BWwBjAGkAIABnAGEAbQBtAGEAIAAyACwAMgY6BicGRQYnACAAMgAuADIAIAZEBkgGRgAg -BjEGRQYnBi8GSgAgBjkGJwZFBB4EMQRJBDAETwAgBEEENQRABDAETwAgBDMEMAQ8BDwEMAAgADIALAAy -AC0EPwRABD4ERAQ4BDsETABHAGUAbgBlAHIAaQBjACAARwByAGEAeQAgAEcAYQBtAG0AYQAgADIALgAy -ACAAUAByAG8AZgBpAGwAZQAAdGV4dAAAAABDb3B5cmlnaHQgQXBwbGUgSW5jLiwgMjAxMgAAWFlaIAAA -AAAAAPNRAAEAAAABFsxjdXJ2AAAAAAAABAAAAAAFAAoADwAUABkAHgAjACgALQAyADcAOwBAAEUASgBP -AFQAWQBeAGMAaABtAHIAdwB8AIEAhgCLAJAAlQCaAJ8ApACpAK4AsgC3ALwAwQDGAMsA0ADVANsA4ADl -AOsA8AD2APsBAQEHAQ0BEwEZAR8BJQErATIBOAE+AUUBTAFSAVkBYAFnAW4BdQF8AYMBiwGSAZoBoQGp -AbEBuQHBAckB0QHZAeEB6QHyAfoCAwIMAhQCHQImAi8COAJBAksCVAJdAmcCcQJ6AoQCjgKYAqICrAK2 -AsECywLVAuAC6wL1AwADCwMWAyEDLQM4A0MDTwNaA2YDcgN+A4oDlgOiA64DugPHA9MD4APsA/kEBgQT -BCAELQQ7BEgEVQRjBHEEfgSMBJoEqAS2BMQE0wThBPAE/gUNBRwFKwU6BUkFWAVnBXcFhgWWBaYFtQXF -BdUF5QX2BgYGFgYnBjcGSAZZBmoGewaMBp0GrwbABtEG4wb1BwcHGQcrBz0HTwdhB3QHhgeZB6wHvwfS -B+UH+AgLCB8IMghGCFoIbgiCCJYIqgi+CNII5wj7CRAJJQk6CU8JZAl5CY8JpAm6Cc8J5Qn7ChEKJwo9 -ClQKagqBCpgKrgrFCtwK8wsLCyILOQtRC2kLgAuYC7ALyAvhC/kMEgwqDEMMXAx1DI4MpwzADNkM8w0N -DSYNQA1aDXQNjg2pDcMN3g34DhMOLg5JDmQOfw6bDrYO0g7uDwkPJQ9BD14Peg+WD7MPzw/sEAkQJhBD -EGEQfhCbELkQ1xD1ERMRMRFPEW0RjBGqEckR6BIHEiYSRRJkEoQSoxLDEuMTAxMjE0MTYxODE6QTxRPl -FAYUJxRJFGoUixStFM4U8BUSFTQVVhV4FZsVvRXgFgMWJhZJFmwWjxayFtYW+hcdF0EXZReJF64X0hf3 -GBsYQBhlGIoYrxjVGPoZIBlFGWsZkRm3Gd0aBBoqGlEadxqeGsUa7BsUGzsbYxuKG7Ib2hwCHCocUhx7 -HKMczBz1HR4dRx1wHZkdwx3sHhYeQB5qHpQevh7pHxMfPh9pH5Qfvx/qIBUgQSBsIJggxCDwIRwhSCF1 -IaEhziH7IiciVSKCIq8i3SMKIzgjZiOUI8Ij8CQfJE0kfCSrJNolCSU4JWgllyXHJfcmJyZXJocmtybo -JxgnSSd6J6sn3CgNKD8ocSiiKNQpBik4KWspnSnQKgIqNSpoKpsqzysCKzYraSudK9EsBSw5LG4soizX -LQwtQS12Last4S4WLkwugi63Lu4vJC9aL5Evxy/+MDUwbDCkMNsxEjFKMYIxujHyMioyYzKbMtQzDTNG -M38zuDPxNCs0ZTSeNNg1EzVNNYc1wjX9Njc2cjauNuk3JDdgN5w31zgUOFA4jDjIOQU5Qjl/Obw5+To2 -OnQ6sjrvOy07azuqO+g8JzxlPKQ84z0iPWE9oT3gPiA+YD6gPuA/IT9hP6I/4kAjQGRApkDnQSlBakGs -Qe5CMEJyQrVC90M6Q31DwEQDREdEikTORRJFVUWaRd5GIkZnRqtG8Ec1R3tHwEgFSEtIkUjXSR1JY0mp -SfBKN0p9SsRLDEtTS5pL4kwqTHJMuk0CTUpNk03cTiVObk63TwBPSU+TT91QJ1BxULtRBlFQUZtR5lIx -UnxSx1MTU19TqlP2VEJUj1TbVShVdVXCVg9WXFapVvdXRFeSV+BYL1h9WMtZGllpWbhaB1pWWqZa9VtF -W5Vb5Vw1XIZc1l0nXXhdyV4aXmxevV8PX2Ffs2AFYFdgqmD8YU9homH1YklinGLwY0Njl2PrZEBklGTp -ZT1lkmXnZj1mkmboZz1nk2fpaD9olmjsaUNpmmnxakhqn2r3a09rp2v/bFdsr20IbWBtuW4SbmtuxG8e -b3hv0XArcIZw4HE6cZVx8HJLcqZzAXNdc7h0FHRwdMx1KHWFdeF2Pnabdvh3VnezeBF4bnjMeSp5iXnn -ekZ6pXsEe2N7wnwhfIF84X1BfaF+AX5ifsJ/I3+Ef+WAR4CogQqBa4HNgjCCkoL0g1eDuoQdhICE44VH -hauGDoZyhteHO4efiASIaYjOiTOJmYn+imSKyoswi5aL/IxjjMqNMY2Yjf+OZo7OjzaPnpAGkG6Q1pE/ -kaiSEZJ6kuOTTZO2lCCUipT0lV+VyZY0lp+XCpd1l+CYTJi4mSSZkJn8mmia1ZtCm6+cHJyJnPedZJ3S -nkCerp8dn4uf+qBpoNihR6G2oiailqMGo3aj5qRWpMelOKWpphqmi6b9p26n4KhSqMSpN6mpqhyqj6sC -q3Wr6axcrNCtRK24ri2uoa8Wr4uwALB1sOqxYLHWskuywrM4s660JbSctRO1irYBtnm28Ldot+C4WbjR -uUq5wro7urW7LrunvCG8m70VvY++Cr6Evv+/er/1wHDA7MFnwePCX8Lbw1jD1MRRxM7FS8XIxkbGw8dB -x7/IPci8yTrJuco4yrfLNsu2zDXMtc01zbXONs62zzfPuNA50LrRPNG+0j/SwdNE08bUSdTL1U7V0dZV -1tjXXNfg2GTY6Nls2fHadtr724DcBdyK3RDdlt4c3qLfKd+v4DbgveFE4cziU+Lb42Pj6+Rz5PzlhOYN -5pbnH+ep6DLovOlG6dDqW+rl63Dr++yG7RHtnO4o7rTvQO/M8Fjw5fFy8f/yjPMZ86f0NPTC9VD13vZt -9vv3ivgZ+Kj5OPnH+lf65/t3/Af8mP0p/br+S/7c/23//9IrLC0uWiRjbGFzc25hbWVYJGNsYXNzZXNf -EBBOU0JpdG1hcEltYWdlUmVwoy0vMFpOU0ltYWdlUmVwWE5TT2JqZWN00issMjNXTlNBcnJheaIyMNIr -LDU2Xk5TTXV0YWJsZUFycmF5ozUyMNM4OQ86OzxXTlNXaGl0ZVxOU0NvbG9yU3BhY2VEMCAwABADgAzS -Kyw+P1dOU0NvbG9yoj4w0issQUJXTlNJbWFnZaJBMAAIABEAGgAkACkAMgA3AEkATABRAFMAYgBoAHUA -fACLAJIAnwCmAK4AsACyALQAuQC7AL0AxADJANQA1gDYANoA3wDiAOQA5gDoAO8BBgEiASQBJhOWE5sT -phOvE8ITxhPRE9oT3xPnE+oT7xP+FAIUCRQRFB4UIxQlFCcULBQ0FDcUPBREAAAAAAAAAgEAAAAAAAAA -QwAAAAAAAAAAAAAAAAAAFEc +S2V5ZWRBcmNoaXZlctEICVRyb290gAGuCwwbHCEaJissMzY5P0JVJG51bGzXDQ4PEBESExQVFhcYGRpW +TlNTaXplViRjbGFzc1xOU0ltYWdlRmxhZ3NWTlNSZXBzW05TVGludENvbG9yV05TQ29sb3JeTlNSZXNp +emluZ01vZGWAAoANEiDDAACAA4AAgAsQAFZ7MSwgMX3SHQ4eIFpOUy5vYmplY3RzoR+ABIAK0h0OIiWi +IySABYAGgAnTDicoKSoaXxAUTlNUSUZGUmVwcmVzZW50YXRpb25fEBlOU0ludGVybmFsTGF5b3V0RGly +ZWN0aW9ugAiAB08REmxNTQAqAAAACgAAABABAAADAAAAAQABAAABAQADAAAAAQABAAABAgADAAAAAgAI +AAgBAwADAAAAAQABAAABBgADAAAAAQABAAABCgADAAAAAQABAAABEQAEAAAAAQAAAAgBEgADAAAAAQAB +AAABFQADAAAAAQACAAABFgADAAAAAQABAAABFwAEAAAAAQAAAAIBHAADAAAAAQABAAABKAADAAAAAQAC +AAABUgADAAAAAQABAAABUwADAAAAAgABAAGHcwAHAAARnAAAANAAAAAAAAARnGFwcGwCAAAAbW50ckdS +QVlYWVogB9wACAAXAA8ALgAPYWNzcEFQUEwAAAAAbm9uZQAAAAAAAAAAAAAAAAAAAAAAAPbWAAEAAAAA +0y1hcHBsAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFZGVzYwAA +AMAAAAB5ZHNjbQAAATwAAAgaY3BydAAACVgAAAAjd3RwdAAACXwAAAAUa1RSQwAACZAAAAgMZGVzYwAA +AAAAAAAfR2VuZXJpYyBHcmF5IEdhbW1hIDIuMiBQcm9maWxlAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAG1s +dWMAAAAAAAAAHwAAAAxza1NLAAAALgAAAYRkYURLAAAAOgAAAbJjYUVTAAAAOAAAAex2aVZOAAAAQAAA +AiRwdEJSAAAASgAAAmR1a1VBAAAALAAAAq5mckZVAAAAPgAAAtpodUhVAAAANAAAAxh6aFRXAAAAGgAA +A0xrb0tSAAAAIgAAA2ZuYk5PAAAAOgAAA4hjc0NaAAAAKAAAA8JoZUlMAAAAJAAAA+pyb1JPAAAAKgAA +BA5kZURFAAAATgAABDhpdElUAAAATgAABIZzdlNFAAAAOAAABNR6aENOAAAAGgAABQxqYUpQAAAAJgAA +BSZlbEdSAAAAKgAABUxwdFBPAAAAUgAABXZubE5MAAAAQAAABchlc0VTAAAATAAABgh0aFRIAAAAMgAA +BlR0clRSAAAAJAAABoZmaUZJAAAARgAABqpockhSAAAAPgAABvBwbFBMAAAASgAABy5hckVHAAAALAAA +B3hydVJVAAAAOgAAB6RlblVTAAAAPAAAB94AVgFhAGUAbwBiAGUAYwBuAOEAIABzAGkAdgDhACAAZwBh +AG0AYQAgADIALAAyAEcAZQBuAGUAcgBpAHMAawAgAGcAcgDlACAAMgAsADIAIABnAGEAbQBtAGEALQBw +AHIAbwBmAGkAbABHAGEAbQBtAGEAIABkAGUAIABnAHIAaQBzAG8AcwAgAGcAZQBuAOgAcgBpAGMAYQAg +ADIALgAyAEMepQB1ACAAaADsAG4AaAAgAE0A4AB1ACAAeADhAG0AIABDAGgAdQBuAGcAIABHAGEAbQBt +AGEAIAAyAC4AMgBQAGUAcgBmAGkAbAAgAEcAZQBuAOkAcgBpAGMAbwAgAGQAYQAgAEcAYQBtAGEAIABk +AGUAIABDAGkAbgB6AGEAcwAgADIALAAyBBcEMAQzBDAEOwRMBD0EMAAgAEcAcgBhAHkALQQzBDAEPAQw +ACAAMgAuADIAUAByAG8AZgBpAGwAIABnAOkAbgDpAHIAaQBxAHUAZQAgAGcAcgBpAHMAIABnAGEAbQBt +AGEAIAAyACwAMgDBAGwAdABhAGwA4QBuAG8AcwAgAHMAegD8AHIAawBlACAAZwBhAG0AbQBhACAAMgAu +ADKQGnUocHCWjlFJXqYAMgAuADKCcl9pY8+P8Md8vBgAINaMwMkAIKwQucgAIAAyAC4AMgAg1QS4XNMM +x3wARwBlAG4AZQByAGkAcwBrACAAZwByAOUAIABnAGEAbQBtAGEAIAAyACwAMgAtAHAAcgBvAGYAaQBs +AE8AYgBlAGMAbgDhACABYQBlAGQA4QAgAGcAYQBtAGEAIAAyAC4AMgXSBdAF3gXUACAF0AXkBdUF6AAg +BdsF3AXcBdkAIAAyAC4AMgBHAGEAbQBhACAAZwByAGkAIABnAGUAbgBlAHIAaQBjAQMAIAAyACwAMgBB +AGwAbABnAGUAbQBlAGkAbgBlAHMAIABHAHIAYQB1AHMAdAB1AGYAZQBuAC0AUAByAG8AZgBpAGwAIABH +AGEAbQBtAGEAIAAyACwAMgBQAHIAbwBmAGkAbABvACAAZwByAGkAZwBpAG8AIABnAGUAbgBlAHIAaQBj +AG8AIABkAGUAbABsAGEAIABnAGEAbQBtAGEAIAAyACwAMgBHAGUAbgBlAHIAaQBzAGsAIABnAHIA5QAg +ADIALAAyACAAZwBhAG0AbQBhAHAAcgBvAGYAaQBsZm6QGnBwXqZ8+2VwADIALgAyY8+P8GWHTvZOAIIs +MLAw7DCkMKww8zDeACAAMgAuADIAIDDXMO0w1TChMKQw6wOTA7UDvQO5A7oDzAAgA5MDugPBA7kAIAOT +A6wDvAO8A7EAIAAyAC4AMgBQAGUAcgBmAGkAbAAgAGcAZQBuAOkAcgBpAGMAbwAgAGQAZQAgAGMAaQBu +AHoAZQBuAHQAbwBzACAAZABhACAARwBhAG0AbQBhACAAMgAsADIAQQBsAGcAZQBtAGUAZQBuACAAZwBy +AGkAagBzACAAZwBhAG0AbQBhACAAMgAsADIALQBwAHIAbwBmAGkAZQBsAFAAZQByAGYAaQBsACAAZwBl +AG4A6QByAGkAYwBvACAAZABlACAAZwBhAG0AbQBhACAAZABlACAAZwByAGkAcwBlAHMAIAAyACwAMg4j +DjEOBw4qDjUOQQ4BDiEOIQ4yDkAOAQ4jDiIOTA4XDjEOSA4nDkQOGwAgADIALgAyAEcAZQBuAGUAbAAg +AEcAcgBpACAARwBhAG0AYQAgADIALAAyAFkAbABlAGkAbgBlAG4AIABoAGEAcgBtAGEAYQBuACAAZwBh +AG0AbQBhACAAMgAsADIAIAAtAHAAcgBvAGYAaQBpAGwAaQBHAGUAbgBlAHIAaQENAGsAaQAgAEcAcgBh +AHkAIABHAGEAbQBtAGEAIAAyAC4AMgAgAHAAcgBvAGYAaQBsAFUAbgBpAHcAZQByAHMAYQBsAG4AeQAg +AHAAcgBvAGYAaQBsACAAcwB6AGEAcgBvAVsAYwBpACAAZwBhAG0AbQBhACAAMgAsADIGOgYnBkUGJwAg +ADIALgAyACAGRAZIBkYAIAYxBkUGJwYvBkoAIAY5BicGRQQeBDEESQQwBE8AIARBBDUEQAQwBE8AIAQz +BDAEPAQ8BDAAIAAyACwAMgAtBD8EQAQ+BEQEOAQ7BEwARwBlAG4AZQByAGkAYwAgAEcAcgBhAHkAIABH +AGEAbQBtAGEAIAAyAC4AMgAgAFAAcgBvAGYAaQBsAGUAAHRleHQAAAAAQ29weXJpZ2h0IEFwcGxlIElu +Yy4sIDIwMTIAAFhZWiAAAAAAAADzUQABAAAAARbMY3VydgAAAAAAAAQAAAAABQAKAA8AFAAZAB4AIwAo +AC0AMgA3ADsAQABFAEoATwBUAFkAXgBjAGgAbQByAHcAfACBAIYAiwCQAJUAmgCfAKQAqQCuALIAtwC8 +AMEAxgDLANAA1QDbAOAA5QDrAPAA9gD7AQEBBwENARMBGQEfASUBKwEyATgBPgFFAUwBUgFZAWABZwFu +AXUBfAGDAYsBkgGaAaEBqQGxAbkBwQHJAdEB2QHhAekB8gH6AgMCDAIUAh0CJgIvAjgCQQJLAlQCXQJn +AnECegKEAo4CmAKiAqwCtgLBAssC1QLgAusC9QMAAwsDFgMhAy0DOANDA08DWgNmA3IDfgOKA5YDogOu +A7oDxwPTA+AD7AP5BAYEEwQgBC0EOwRIBFUEYwRxBH4EjASaBKgEtgTEBNME4QTwBP4FDQUcBSsFOgVJ +BVgFZwV3BYYFlgWmBbUFxQXVBeUF9gYGBhYGJwY3BkgGWQZqBnsGjAadBq8GwAbRBuMG9QcHBxkHKwc9 +B08HYQd0B4YHmQesB78H0gflB/gICwgfCDIIRghaCG4IggiWCKoIvgjSCOcI+wkQCSUJOglPCWQJeQmP +CaQJugnPCeUJ+woRCicKPQpUCmoKgQqYCq4KxQrcCvMLCwsiCzkLUQtpC4ALmAuwC8gL4Qv5DBIMKgxD +DFwMdQyODKcMwAzZDPMNDQ0mDUANWg10DY4NqQ3DDd4N+A4TDi4OSQ5kDn8Omw62DtIO7g8JDyUPQQ9e +D3oPlg+zD88P7BAJECYQQxBhEH4QmxC5ENcQ9RETETERTxFtEYwRqhHJEegSBxImEkUSZBKEEqMSwxLj +EwMTIxNDE2MTgxOkE8UT5RQGFCcUSRRqFIsUrRTOFPAVEhU0FVYVeBWbFb0V4BYDFiYWSRZsFo8WshbW +FvoXHRdBF2UXiReuF9IX9xgbGEAYZRiKGK8Y1Rj6GSAZRRlrGZEZtxndGgQaKhpRGncanhrFGuwbFBs7 +G2MbihuyG9ocAhwqHFIcexyjHMwc9R0eHUcdcB2ZHcMd7B4WHkAeah6UHr4e6R8THz4faR+UH78f6iAV +IEEgbCCYIMQg8CEcIUghdSGhIc4h+yInIlUigiKvIt0jCiM4I2YjlCPCI/AkHyRNJHwkqyTaJQklOCVo +JZclxyX3JicmVyaHJrcm6CcYJ0kneierJ9woDSg/KHEooijUKQYpOClrKZ0p0CoCKjUqaCqbKs8rAis2 +K2krnSvRLAUsOSxuLKIs1y0MLUEtdi2rLeEuFi5MLoIuty7uLyQvWi+RL8cv/jA1MGwwpDDbMRIxSjGC +Mbox8jIqMmMymzLUMw0zRjN/M7gz8TQrNGU0njTYNRM1TTWHNcI1/TY3NnI2rjbpNyQ3YDecN9c4FDhQ +OIw4yDkFOUI5fzm8Ofk6Njp0OrI67zstO2s7qjvoPCc8ZTykPOM9Ij1hPaE94D4gPmA+oD7gPyE/YT+i +P+JAI0BkQKZA50EpQWpBrEHuQjBCckK1QvdDOkN9Q8BEA0RHRIpEzkUSRVVFmkXeRiJGZ0arRvBHNUd7 +R8BIBUhLSJFI10kdSWNJqUnwSjdKfUrESwxLU0uaS+JMKkxyTLpNAk1KTZNN3E4lTm5Ot08AT0lPk0/d +UCdQcVC7UQZRUFGbUeZSMVJ8UsdTE1NfU6pT9lRCVI9U21UoVXVVwlYPVlxWqVb3V0RXklfgWC9YfVjL +WRpZaVm4WgdaVlqmWvVbRVuVW+VcNVyGXNZdJ114XcleGl5sXr1fD19hX7NgBWBXYKpg/GFPYaJh9WJJ +Ypxi8GNDY5dj62RAZJRk6WU9ZZJl52Y9ZpJm6Gc9Z5Nn6Wg/aJZo7GlDaZpp8WpIap9q92tPa6dr/2xX +bK9tCG1gbbluEm5rbsRvHm94b9FwK3CGcOBxOnGVcfByS3KmcwFzXXO4dBR0cHTMdSh1hXXhdj52m3b4 +d1Z3s3gReG54zHkqeYl553pGeqV7BHtje8J8IXyBfOF9QX2hfgF+Yn7CfyN/hH/lgEeAqIEKgWuBzYIw +gpKC9INXg7qEHYSAhOOFR4Wrhg6GcobXhzuHn4gEiGmIzokziZmJ/opkisqLMIuWi/yMY4zKjTGNmI3/ +jmaOzo82j56QBpBukNaRP5GokhGSepLjk02TtpQglIqU9JVflcmWNJaflwqXdZfgmEyYuJkkmZCZ/Jpo +mtWbQpuvnByciZz3nWSd0p5Anq6fHZ+Ln/qgaaDYoUehtqImopajBqN2o+akVqTHpTilqaYapoum/adu +p+CoUqjEqTepqaocqo+rAqt1q+msXKzQrUStuK4trqGvFq+LsACwdbDqsWCx1rJLssKzOLOutCW0nLUT +tYq2AbZ5tvC3aLfguFm40blKucK6O7q1uy67p7whvJu9Fb2Pvgq+hL7/v3q/9cBwwOzBZ8Hjwl/C28NY +w9TEUcTOxUvFyMZGxsPHQce/yD3IvMk6ybnKOMq3yzbLtsw1zLXNNc21zjbOts83z7jQOdC60TzRvtI/ +0sHTRNPG1EnUy9VO1dHWVdbY11zX4Nhk2OjZbNnx2nba+9uA3AXcit0Q3ZbeHN6i3ynfr+A24L3hROHM +4lPi2+Nj4+vkc+T85YTmDeaW5x/nqegy6LzpRunQ6lvq5etw6/vshu0R7ZzuKO6070DvzPBY8OXxcvH/ +8ozzGfOn9DT0wvVQ9d72bfb794r4Gfio+Tj5x/pX+uf7d/wH/Jj9Kf26/kv+3P9t///SLS4vMFokY2xh +c3NuYW1lWCRjbGFzc2VzXxAQTlNCaXRtYXBJbWFnZVJlcKMvMTJaTlNJbWFnZVJlcFhOU09iamVjdNIt +LjQ1V05TQXJyYXmiNDLSLS43OF5OU011dGFibGVBcnJheaM3NDLTOjsOPD0+V05TV2hpdGVcTlNDb2xv +clNwYWNlRDAgMAAQA4AM0i0uQEFXTlNDb2xvcqJAMtItLkNEV05TSW1hZ2WiQzIACAARABoAJAApADIA +NwBJAEwAUQBTAGIAaAB3AH4AhQCSAJkApQCtALwAvgDAAMUAxwDJAMsAzQDUANkA5ADmAOgA6gDvAPIA +9AD2APgA/wEWATIBNAE2E6YTqxO2E78T0hPWE+ET6hPvE/cT+hP/FA4UEhQZFCEULhQzFDUUNxQ8FEQU +RxRMFFQAAAAAAAACAQAAAAAAAABFAAAAAAAAAAAAAAAAAAAUVw diff --git a/i18n/src/main/resources/zh_TW.lproj/Browser.xib b/i18n/src/main/resources/zh_TW.lproj/Browser.xib index 769f8b9072..5cc98872a0 100644 --- a/i18n/src/main/resources/zh_TW.lproj/Browser.xib +++ b/i18n/src/main/resources/zh_TW.lproj/Browser.xib @@ -1,7 +1,7 @@ - + - + @@ -38,7 +38,7 @@ - + @@ -62,27 +62,27 @@ @@ -256,7 +256,7 @@ - + @@ -553,95 +553,95 @@ DQ YnBsaXN0MDDUAQIDBAUGBwpYJHZlcnNpb25ZJGFyY2hpdmVyVCR0b3BYJG9iamVjdHMSAAGGoF8QD05T -S2V5ZWRBcmNoaXZlctEICVRyb290gAGuCwwZGh8UJCkqMTQ3PUBVJG51bGzWDQ4PEBESExQVFhcYVk5T -U2l6ZV5OU1Jlc2l6aW5nTW9kZVYkY2xhc3NcTlNJbWFnZUZsYWdzVk5TUmVwc1dOU0NvbG9ygAIQAIAN -EiDDAACAA4ALVnsxLCAxfdIbDxweWk5TLm9iamVjdHOhHYAEgArSGw8gI6IhIoAFgAaACdMPJSYnKBRf -EBROU1RJRkZSZXByZXNlbnRhdGlvbl8QGU5TSW50ZXJuYWxMYXlvdXREaXJlY3Rpb26ACIAHTxESbE1N -ACoAAAAKAAAAEAEAAAMAAAABAAEAAAEBAAMAAAABAAEAAAECAAMAAAACAAgACAEDAAMAAAABAAEAAAEG -AAMAAAABAAEAAAEKAAMAAAABAAEAAAERAAQAAAABAAAACAESAAMAAAABAAEAAAEVAAMAAAABAAIAAAEW -AAMAAAABAAEAAAEXAAQAAAABAAAAAgEcAAMAAAABAAEAAAEoAAMAAAABAAIAAAFSAAMAAAABAAEAAAFT -AAMAAAACAAEAAYdzAAcAABGcAAAA0AAAAAAAABGcYXBwbAIAAABtbnRyR1JBWVhZWiAH3AAIABcADwAu -AA9hY3NwQVBQTAAAAABub25lAAAAAAAAAAAAAAAAAAAAAAAA9tYAAQAAAADTLWFwcGwAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAVkZXNjAAAAwAAAAHlkc2NtAAABPAAA -CBpjcHJ0AAAJWAAAACN3dHB0AAAJfAAAABRrVFJDAAAJkAAACAxkZXNjAAAAAAAAAB9HZW5lcmljIEdy -YXkgR2FtbWEgMi4yIFByb2ZpbGUAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAbWx1YwAAAAAAAAAfAAAADHNr -U0sAAAAuAAABhGRhREsAAAA6AAABsmNhRVMAAAA4AAAB7HZpVk4AAABAAAACJHB0QlIAAABKAAACZHVr -VUEAAAAsAAACrmZyRlUAAAA+AAAC2mh1SFUAAAA0AAADGHpoVFcAAAAaAAADTGtvS1IAAAAiAAADZm5i -Tk8AAAA6AAADiGNzQ1oAAAAoAAADwmhlSUwAAAAkAAAD6nJvUk8AAAAqAAAEDmRlREUAAABOAAAEOGl0 -SVQAAABOAAAEhnN2U0UAAAA4AAAE1HpoQ04AAAAaAAAFDGphSlAAAAAmAAAFJmVsR1IAAAAqAAAFTHB0 -UE8AAABSAAAFdm5sTkwAAABAAAAFyGVzRVMAAABMAAAGCHRoVEgAAAAyAAAGVHRyVFIAAAAkAAAGhmZp -RkkAAABGAAAGqmhySFIAAAA+AAAG8HBsUEwAAABKAAAHLmFyRUcAAAAsAAAHeHJ1UlUAAAA6AAAHpGVu -VVMAAAA8AAAH3gBWAWEAZQBvAGIAZQBjAG4A4QAgAHMAaQB2AOEAIABnAGEAbQBhACAAMgAsADIARwBl -AG4AZQByAGkAcwBrACAAZwByAOUAIAAyACwAMgAgAGcAYQBtAG0AYQAtAHAAcgBvAGYAaQBsAEcAYQBt -AG0AYQAgAGQAZQAgAGcAcgBpAHMAbwBzACAAZwBlAG4A6AByAGkAYwBhACAAMgAuADIAQx6lAHUAIABo -AOwAbgBoACAATQDgAHUAIAB4AOEAbQAgAEMAaAB1AG4AZwAgAEcAYQBtAG0AYQAgADIALgAyAFAAZQBy -AGYAaQBsACAARwBlAG4A6QByAGkAYwBvACAAZABhACAARwBhAG0AYQAgAGQAZQAgAEMAaQBuAHoAYQBz -ACAAMgAsADIEFwQwBDMEMAQ7BEwEPQQwACAARwByAGEAeQAtBDMEMAQ8BDAAIAAyAC4AMgBQAHIAbwBm -AGkAbAAgAGcA6QBuAOkAcgBpAHEAdQBlACAAZwByAGkAcwAgAGcAYQBtAG0AYQAgADIALAAyAMEAbAB0 -AGEAbADhAG4AbwBzACAAcwB6APwAcgBrAGUAIABnAGEAbQBtAGEAIAAyAC4AMpAadShwcJaOUUlepgAy -AC4AMoJyX2ljz4/wx3y8GAAg1ozAyQAgrBC5yAAgADIALgAyACDVBLhc0wzHfABHAGUAbgBlAHIAaQBz -AGsAIABnAHIA5QAgAGcAYQBtAG0AYQAgADIALAAyAC0AcAByAG8AZgBpAGwATwBiAGUAYwBuAOEAIAFh -AGUAZADhACAAZwBhAG0AYQAgADIALgAyBdIF0AXeBdQAIAXQBeQF1QXoACAF2wXcBdwF2QAgADIALgAy -AEcAYQBtAGEAIABnAHIAaQAgAGcAZQBuAGUAcgBpAGMBAwAgADIALAAyAEEAbABsAGcAZQBtAGUAaQBu -AGUAcwAgAEcAcgBhAHUAcwB0AHUAZgBlAG4ALQBQAHIAbwBmAGkAbAAgAEcAYQBtAG0AYQAgADIALAAy -AFAAcgBvAGYAaQBsAG8AIABnAHIAaQBnAGkAbwAgAGcAZQBuAGUAcgBpAGMAbwAgAGQAZQBsAGwAYQAg -AGcAYQBtAG0AYQAgADIALAAyAEcAZQBuAGUAcgBpAHMAawAgAGcAcgDlACAAMgAsADIAIABnAGEAbQBt -AGEAcAByAG8AZgBpAGxmbpAacHBepnz7ZXAAMgAuADJjz4/wZYdO9k4AgiwwsDDsMKQwrDDzMN4AIAAy -AC4AMgAgMNcw7TDVMKEwpDDrA5MDtQO9A7kDugPMACADkwO6A8EDuQAgA5MDrAO8A7wDsQAgADIALgAy -AFAAZQByAGYAaQBsACAAZwBlAG4A6QByAGkAYwBvACAAZABlACAAYwBpAG4AegBlAG4AdABvAHMAIABk -AGEAIABHAGEAbQBtAGEAIAAyACwAMgBBAGwAZwBlAG0AZQBlAG4AIABnAHIAaQBqAHMAIABnAGEAbQBt -AGEAIAAyACwAMgAtAHAAcgBvAGYAaQBlAGwAUABlAHIAZgBpAGwAIABnAGUAbgDpAHIAaQBjAG8AIABk -AGUAIABnAGEAbQBtAGEAIABkAGUAIABnAHIAaQBzAGUAcwAgADIALAAyDiMOMQ4HDioONQ5BDgEOIQ4h -DjIOQA4BDiMOIg5MDhcOMQ5IDicORA4bACAAMgAuADIARwBlAG4AZQBsACAARwByAGkAIABHAGEAbQBh -ACAAMgAsADIAWQBsAGUAaQBuAGUAbgAgAGgAYQByAG0AYQBhAG4AIABnAGEAbQBtAGEAIAAyACwAMgAg -AC0AcAByAG8AZgBpAGkAbABpAEcAZQBuAGUAcgBpAQ0AawBpACAARwByAGEAeQAgAEcAYQBtAG0AYQAg -ADIALgAyACAAcAByAG8AZgBpAGwAVQBuAGkAdwBlAHIAcwBhAGwAbgB5ACAAcAByAG8AZgBpAGwAIABz -AHoAYQByAG8BWwBjAGkAIABnAGEAbQBtAGEAIAAyACwAMgY6BicGRQYnACAAMgAuADIAIAZEBkgGRgAg -BjEGRQYnBi8GSgAgBjkGJwZFBB4EMQRJBDAETwAgBEEENQRABDAETwAgBDMEMAQ8BDwEMAAgADIALAAy -AC0EPwRABD4ERAQ4BDsETABHAGUAbgBlAHIAaQBjACAARwByAGEAeQAgAEcAYQBtAG0AYQAgADIALgAy -ACAAUAByAG8AZgBpAGwAZQAAdGV4dAAAAABDb3B5cmlnaHQgQXBwbGUgSW5jLiwgMjAxMgAAWFlaIAAA -AAAAAPNRAAEAAAABFsxjdXJ2AAAAAAAABAAAAAAFAAoADwAUABkAHgAjACgALQAyADcAOwBAAEUASgBP -AFQAWQBeAGMAaABtAHIAdwB8AIEAhgCLAJAAlQCaAJ8ApACpAK4AsgC3ALwAwQDGAMsA0ADVANsA4ADl -AOsA8AD2APsBAQEHAQ0BEwEZAR8BJQErATIBOAE+AUUBTAFSAVkBYAFnAW4BdQF8AYMBiwGSAZoBoQGp -AbEBuQHBAckB0QHZAeEB6QHyAfoCAwIMAhQCHQImAi8COAJBAksCVAJdAmcCcQJ6AoQCjgKYAqICrAK2 -AsECywLVAuAC6wL1AwADCwMWAyEDLQM4A0MDTwNaA2YDcgN+A4oDlgOiA64DugPHA9MD4APsA/kEBgQT -BCAELQQ7BEgEVQRjBHEEfgSMBJoEqAS2BMQE0wThBPAE/gUNBRwFKwU6BUkFWAVnBXcFhgWWBaYFtQXF -BdUF5QX2BgYGFgYnBjcGSAZZBmoGewaMBp0GrwbABtEG4wb1BwcHGQcrBz0HTwdhB3QHhgeZB6wHvwfS -B+UH+AgLCB8IMghGCFoIbgiCCJYIqgi+CNII5wj7CRAJJQk6CU8JZAl5CY8JpAm6Cc8J5Qn7ChEKJwo9 -ClQKagqBCpgKrgrFCtwK8wsLCyILOQtRC2kLgAuYC7ALyAvhC/kMEgwqDEMMXAx1DI4MpwzADNkM8w0N -DSYNQA1aDXQNjg2pDcMN3g34DhMOLg5JDmQOfw6bDrYO0g7uDwkPJQ9BD14Peg+WD7MPzw/sEAkQJhBD -EGEQfhCbELkQ1xD1ERMRMRFPEW0RjBGqEckR6BIHEiYSRRJkEoQSoxLDEuMTAxMjE0MTYxODE6QTxRPl -FAYUJxRJFGoUixStFM4U8BUSFTQVVhV4FZsVvRXgFgMWJhZJFmwWjxayFtYW+hcdF0EXZReJF64X0hf3 -GBsYQBhlGIoYrxjVGPoZIBlFGWsZkRm3Gd0aBBoqGlEadxqeGsUa7BsUGzsbYxuKG7Ib2hwCHCocUhx7 -HKMczBz1HR4dRx1wHZkdwx3sHhYeQB5qHpQevh7pHxMfPh9pH5Qfvx/qIBUgQSBsIJggxCDwIRwhSCF1 -IaEhziH7IiciVSKCIq8i3SMKIzgjZiOUI8Ij8CQfJE0kfCSrJNolCSU4JWgllyXHJfcmJyZXJocmtybo -JxgnSSd6J6sn3CgNKD8ocSiiKNQpBik4KWspnSnQKgIqNSpoKpsqzysCKzYraSudK9EsBSw5LG4soizX -LQwtQS12Last4S4WLkwugi63Lu4vJC9aL5Evxy/+MDUwbDCkMNsxEjFKMYIxujHyMioyYzKbMtQzDTNG -M38zuDPxNCs0ZTSeNNg1EzVNNYc1wjX9Njc2cjauNuk3JDdgN5w31zgUOFA4jDjIOQU5Qjl/Obw5+To2 -OnQ6sjrvOy07azuqO+g8JzxlPKQ84z0iPWE9oT3gPiA+YD6gPuA/IT9hP6I/4kAjQGRApkDnQSlBakGs -Qe5CMEJyQrVC90M6Q31DwEQDREdEikTORRJFVUWaRd5GIkZnRqtG8Ec1R3tHwEgFSEtIkUjXSR1JY0mp -SfBKN0p9SsRLDEtTS5pL4kwqTHJMuk0CTUpNk03cTiVObk63TwBPSU+TT91QJ1BxULtRBlFQUZtR5lIx -UnxSx1MTU19TqlP2VEJUj1TbVShVdVXCVg9WXFapVvdXRFeSV+BYL1h9WMtZGllpWbhaB1pWWqZa9VtF -W5Vb5Vw1XIZc1l0nXXhdyV4aXmxevV8PX2Ffs2AFYFdgqmD8YU9homH1YklinGLwY0Njl2PrZEBklGTp -ZT1lkmXnZj1mkmboZz1nk2fpaD9olmjsaUNpmmnxakhqn2r3a09rp2v/bFdsr20IbWBtuW4SbmtuxG8e -b3hv0XArcIZw4HE6cZVx8HJLcqZzAXNdc7h0FHRwdMx1KHWFdeF2Pnabdvh3VnezeBF4bnjMeSp5iXnn -ekZ6pXsEe2N7wnwhfIF84X1BfaF+AX5ifsJ/I3+Ef+WAR4CogQqBa4HNgjCCkoL0g1eDuoQdhICE44VH -hauGDoZyhteHO4efiASIaYjOiTOJmYn+imSKyoswi5aL/IxjjMqNMY2Yjf+OZo7OjzaPnpAGkG6Q1pE/ -kaiSEZJ6kuOTTZO2lCCUipT0lV+VyZY0lp+XCpd1l+CYTJi4mSSZkJn8mmia1ZtCm6+cHJyJnPedZJ3S -nkCerp8dn4uf+qBpoNihR6G2oiailqMGo3aj5qRWpMelOKWpphqmi6b9p26n4KhSqMSpN6mpqhyqj6sC -q3Wr6axcrNCtRK24ri2uoa8Wr4uwALB1sOqxYLHWskuywrM4s660JbSctRO1irYBtnm28Ldot+C4WbjR -uUq5wro7urW7LrunvCG8m70VvY++Cr6Evv+/er/1wHDA7MFnwePCX8Lbw1jD1MRRxM7FS8XIxkbGw8dB -x7/IPci8yTrJuco4yrfLNsu2zDXMtc01zbXONs62zzfPuNA50LrRPNG+0j/SwdNE08bUSdTL1U7V0dZV -1tjXXNfg2GTY6Nls2fHadtr724DcBdyK3RDdlt4c3qLfKd+v4DbgveFE4cziU+Lb42Pj6+Rz5PzlhOYN -5pbnH+ep6DLovOlG6dDqW+rl63Dr++yG7RHtnO4o7rTvQO/M8Fjw5fFy8f/yjPMZ86f0NPTC9VD13vZt -9vv3ivgZ+Kj5OPnH+lf65/t3/Af8mP0p/br+S/7c/23//9IrLC0uWiRjbGFzc25hbWVYJGNsYXNzZXNf -EBBOU0JpdG1hcEltYWdlUmVwoy0vMFpOU0ltYWdlUmVwWE5TT2JqZWN00issMjNXTlNBcnJheaIyMNIr -LDU2Xk5TTXV0YWJsZUFycmF5ozUyMNM4OQ86OzxXTlNXaGl0ZVxOU0NvbG9yU3BhY2VEMCAwABADgAzS -Kyw+P1dOU0NvbG9yoj4w0issQUJXTlNJbWFnZaJBMAAIABEAGgAkACkAMgA3AEkATABRAFMAYgBoAHUA -fACLAJIAnwCmAK4AsACyALQAuQC7AL0AxADJANQA1gDYANoA3wDiAOQA5gDoAO8BBgEiASQBJhOWE5sT -phOvE8ITxhPRE9oT3xPnE+oT7xP+FAIUCRQRFB4UIxQlFCcULBQ0FDcUPBREAAAAAAAAAgEAAAAAAAAA -QwAAAAAAAAAAAAAAAAAAFEc +S2V5ZWRBcmNoaXZlctEICVRyb290gAGuCwwbHCEaJissMzY5P0JVJG51bGzXDQ4PEBESExQVFhcYGRpW +TlNTaXplViRjbGFzc1xOU0ltYWdlRmxhZ3NWTlNSZXBzW05TVGludENvbG9yV05TQ29sb3JeTlNSZXNp +emluZ01vZGWAAoANEiDDAACAA4AAgAsQAFZ7MSwgMX3SHQ4eIFpOUy5vYmplY3RzoR+ABIAK0h0OIiWi +IySABYAGgAnTDicoKSoaXxAUTlNUSUZGUmVwcmVzZW50YXRpb25fEBlOU0ludGVybmFsTGF5b3V0RGly +ZWN0aW9ugAiAB08REmxNTQAqAAAACgAAABABAAADAAAAAQABAAABAQADAAAAAQABAAABAgADAAAAAgAI +AAgBAwADAAAAAQABAAABBgADAAAAAQABAAABCgADAAAAAQABAAABEQAEAAAAAQAAAAgBEgADAAAAAQAB +AAABFQADAAAAAQACAAABFgADAAAAAQABAAABFwAEAAAAAQAAAAIBHAADAAAAAQABAAABKAADAAAAAQAC +AAABUgADAAAAAQABAAABUwADAAAAAgABAAGHcwAHAAARnAAAANAAAAAAAAARnGFwcGwCAAAAbW50ckdS +QVlYWVogB9wACAAXAA8ALgAPYWNzcEFQUEwAAAAAbm9uZQAAAAAAAAAAAAAAAAAAAAAAAPbWAAEAAAAA +0y1hcHBsAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFZGVzYwAA +AMAAAAB5ZHNjbQAAATwAAAgaY3BydAAACVgAAAAjd3RwdAAACXwAAAAUa1RSQwAACZAAAAgMZGVzYwAA +AAAAAAAfR2VuZXJpYyBHcmF5IEdhbW1hIDIuMiBQcm9maWxlAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAG1s +dWMAAAAAAAAAHwAAAAxza1NLAAAALgAAAYRkYURLAAAAOgAAAbJjYUVTAAAAOAAAAex2aVZOAAAAQAAA +AiRwdEJSAAAASgAAAmR1a1VBAAAALAAAAq5mckZVAAAAPgAAAtpodUhVAAAANAAAAxh6aFRXAAAAGgAA +A0xrb0tSAAAAIgAAA2ZuYk5PAAAAOgAAA4hjc0NaAAAAKAAAA8JoZUlMAAAAJAAAA+pyb1JPAAAAKgAA +BA5kZURFAAAATgAABDhpdElUAAAATgAABIZzdlNFAAAAOAAABNR6aENOAAAAGgAABQxqYUpQAAAAJgAA +BSZlbEdSAAAAKgAABUxwdFBPAAAAUgAABXZubE5MAAAAQAAABchlc0VTAAAATAAABgh0aFRIAAAAMgAA +BlR0clRSAAAAJAAABoZmaUZJAAAARgAABqpockhSAAAAPgAABvBwbFBMAAAASgAABy5hckVHAAAALAAA +B3hydVJVAAAAOgAAB6RlblVTAAAAPAAAB94AVgFhAGUAbwBiAGUAYwBuAOEAIABzAGkAdgDhACAAZwBh +AG0AYQAgADIALAAyAEcAZQBuAGUAcgBpAHMAawAgAGcAcgDlACAAMgAsADIAIABnAGEAbQBtAGEALQBw +AHIAbwBmAGkAbABHAGEAbQBtAGEAIABkAGUAIABnAHIAaQBzAG8AcwAgAGcAZQBuAOgAcgBpAGMAYQAg +ADIALgAyAEMepQB1ACAAaADsAG4AaAAgAE0A4AB1ACAAeADhAG0AIABDAGgAdQBuAGcAIABHAGEAbQBt +AGEAIAAyAC4AMgBQAGUAcgBmAGkAbAAgAEcAZQBuAOkAcgBpAGMAbwAgAGQAYQAgAEcAYQBtAGEAIABk +AGUAIABDAGkAbgB6AGEAcwAgADIALAAyBBcEMAQzBDAEOwRMBD0EMAAgAEcAcgBhAHkALQQzBDAEPAQw +ACAAMgAuADIAUAByAG8AZgBpAGwAIABnAOkAbgDpAHIAaQBxAHUAZQAgAGcAcgBpAHMAIABnAGEAbQBt +AGEAIAAyACwAMgDBAGwAdABhAGwA4QBuAG8AcwAgAHMAegD8AHIAawBlACAAZwBhAG0AbQBhACAAMgAu +ADKQGnUocHCWjlFJXqYAMgAuADKCcl9pY8+P8Md8vBgAINaMwMkAIKwQucgAIAAyAC4AMgAg1QS4XNMM +x3wARwBlAG4AZQByAGkAcwBrACAAZwByAOUAIABnAGEAbQBtAGEAIAAyACwAMgAtAHAAcgBvAGYAaQBs +AE8AYgBlAGMAbgDhACABYQBlAGQA4QAgAGcAYQBtAGEAIAAyAC4AMgXSBdAF3gXUACAF0AXkBdUF6AAg +BdsF3AXcBdkAIAAyAC4AMgBHAGEAbQBhACAAZwByAGkAIABnAGUAbgBlAHIAaQBjAQMAIAAyACwAMgBB +AGwAbABnAGUAbQBlAGkAbgBlAHMAIABHAHIAYQB1AHMAdAB1AGYAZQBuAC0AUAByAG8AZgBpAGwAIABH +AGEAbQBtAGEAIAAyACwAMgBQAHIAbwBmAGkAbABvACAAZwByAGkAZwBpAG8AIABnAGUAbgBlAHIAaQBj +AG8AIABkAGUAbABsAGEAIABnAGEAbQBtAGEAIAAyACwAMgBHAGUAbgBlAHIAaQBzAGsAIABnAHIA5QAg +ADIALAAyACAAZwBhAG0AbQBhAHAAcgBvAGYAaQBsZm6QGnBwXqZ8+2VwADIALgAyY8+P8GWHTvZOAIIs +MLAw7DCkMKww8zDeACAAMgAuADIAIDDXMO0w1TChMKQw6wOTA7UDvQO5A7oDzAAgA5MDugPBA7kAIAOT +A6wDvAO8A7EAIAAyAC4AMgBQAGUAcgBmAGkAbAAgAGcAZQBuAOkAcgBpAGMAbwAgAGQAZQAgAGMAaQBu +AHoAZQBuAHQAbwBzACAAZABhACAARwBhAG0AbQBhACAAMgAsADIAQQBsAGcAZQBtAGUAZQBuACAAZwBy +AGkAagBzACAAZwBhAG0AbQBhACAAMgAsADIALQBwAHIAbwBmAGkAZQBsAFAAZQByAGYAaQBsACAAZwBl +AG4A6QByAGkAYwBvACAAZABlACAAZwBhAG0AbQBhACAAZABlACAAZwByAGkAcwBlAHMAIAAyACwAMg4j +DjEOBw4qDjUOQQ4BDiEOIQ4yDkAOAQ4jDiIOTA4XDjEOSA4nDkQOGwAgADIALgAyAEcAZQBuAGUAbAAg +AEcAcgBpACAARwBhAG0AYQAgADIALAAyAFkAbABlAGkAbgBlAG4AIABoAGEAcgBtAGEAYQBuACAAZwBh +AG0AbQBhACAAMgAsADIAIAAtAHAAcgBvAGYAaQBpAGwAaQBHAGUAbgBlAHIAaQENAGsAaQAgAEcAcgBh +AHkAIABHAGEAbQBtAGEAIAAyAC4AMgAgAHAAcgBvAGYAaQBsAFUAbgBpAHcAZQByAHMAYQBsAG4AeQAg +AHAAcgBvAGYAaQBsACAAcwB6AGEAcgBvAVsAYwBpACAAZwBhAG0AbQBhACAAMgAsADIGOgYnBkUGJwAg +ADIALgAyACAGRAZIBkYAIAYxBkUGJwYvBkoAIAY5BicGRQQeBDEESQQwBE8AIARBBDUEQAQwBE8AIAQz +BDAEPAQ8BDAAIAAyACwAMgAtBD8EQAQ+BEQEOAQ7BEwARwBlAG4AZQByAGkAYwAgAEcAcgBhAHkAIABH +AGEAbQBtAGEAIAAyAC4AMgAgAFAAcgBvAGYAaQBsAGUAAHRleHQAAAAAQ29weXJpZ2h0IEFwcGxlIElu +Yy4sIDIwMTIAAFhZWiAAAAAAAADzUQABAAAAARbMY3VydgAAAAAAAAQAAAAABQAKAA8AFAAZAB4AIwAo +AC0AMgA3ADsAQABFAEoATwBUAFkAXgBjAGgAbQByAHcAfACBAIYAiwCQAJUAmgCfAKQAqQCuALIAtwC8 +AMEAxgDLANAA1QDbAOAA5QDrAPAA9gD7AQEBBwENARMBGQEfASUBKwEyATgBPgFFAUwBUgFZAWABZwFu +AXUBfAGDAYsBkgGaAaEBqQGxAbkBwQHJAdEB2QHhAekB8gH6AgMCDAIUAh0CJgIvAjgCQQJLAlQCXQJn +AnECegKEAo4CmAKiAqwCtgLBAssC1QLgAusC9QMAAwsDFgMhAy0DOANDA08DWgNmA3IDfgOKA5YDogOu +A7oDxwPTA+AD7AP5BAYEEwQgBC0EOwRIBFUEYwRxBH4EjASaBKgEtgTEBNME4QTwBP4FDQUcBSsFOgVJ +BVgFZwV3BYYFlgWmBbUFxQXVBeUF9gYGBhYGJwY3BkgGWQZqBnsGjAadBq8GwAbRBuMG9QcHBxkHKwc9 +B08HYQd0B4YHmQesB78H0gflB/gICwgfCDIIRghaCG4IggiWCKoIvgjSCOcI+wkQCSUJOglPCWQJeQmP +CaQJugnPCeUJ+woRCicKPQpUCmoKgQqYCq4KxQrcCvMLCwsiCzkLUQtpC4ALmAuwC8gL4Qv5DBIMKgxD +DFwMdQyODKcMwAzZDPMNDQ0mDUANWg10DY4NqQ3DDd4N+A4TDi4OSQ5kDn8Omw62DtIO7g8JDyUPQQ9e +D3oPlg+zD88P7BAJECYQQxBhEH4QmxC5ENcQ9RETETERTxFtEYwRqhHJEegSBxImEkUSZBKEEqMSwxLj +EwMTIxNDE2MTgxOkE8UT5RQGFCcUSRRqFIsUrRTOFPAVEhU0FVYVeBWbFb0V4BYDFiYWSRZsFo8WshbW +FvoXHRdBF2UXiReuF9IX9xgbGEAYZRiKGK8Y1Rj6GSAZRRlrGZEZtxndGgQaKhpRGncanhrFGuwbFBs7 +G2MbihuyG9ocAhwqHFIcexyjHMwc9R0eHUcdcB2ZHcMd7B4WHkAeah6UHr4e6R8THz4faR+UH78f6iAV +IEEgbCCYIMQg8CEcIUghdSGhIc4h+yInIlUigiKvIt0jCiM4I2YjlCPCI/AkHyRNJHwkqyTaJQklOCVo +JZclxyX3JicmVyaHJrcm6CcYJ0kneierJ9woDSg/KHEooijUKQYpOClrKZ0p0CoCKjUqaCqbKs8rAis2 +K2krnSvRLAUsOSxuLKIs1y0MLUEtdi2rLeEuFi5MLoIuty7uLyQvWi+RL8cv/jA1MGwwpDDbMRIxSjGC +Mbox8jIqMmMymzLUMw0zRjN/M7gz8TQrNGU0njTYNRM1TTWHNcI1/TY3NnI2rjbpNyQ3YDecN9c4FDhQ +OIw4yDkFOUI5fzm8Ofk6Njp0OrI67zstO2s7qjvoPCc8ZTykPOM9Ij1hPaE94D4gPmA+oD7gPyE/YT+i +P+JAI0BkQKZA50EpQWpBrEHuQjBCckK1QvdDOkN9Q8BEA0RHRIpEzkUSRVVFmkXeRiJGZ0arRvBHNUd7 +R8BIBUhLSJFI10kdSWNJqUnwSjdKfUrESwxLU0uaS+JMKkxyTLpNAk1KTZNN3E4lTm5Ot08AT0lPk0/d +UCdQcVC7UQZRUFGbUeZSMVJ8UsdTE1NfU6pT9lRCVI9U21UoVXVVwlYPVlxWqVb3V0RXklfgWC9YfVjL +WRpZaVm4WgdaVlqmWvVbRVuVW+VcNVyGXNZdJ114XcleGl5sXr1fD19hX7NgBWBXYKpg/GFPYaJh9WJJ +Ypxi8GNDY5dj62RAZJRk6WU9ZZJl52Y9ZpJm6Gc9Z5Nn6Wg/aJZo7GlDaZpp8WpIap9q92tPa6dr/2xX +bK9tCG1gbbluEm5rbsRvHm94b9FwK3CGcOBxOnGVcfByS3KmcwFzXXO4dBR0cHTMdSh1hXXhdj52m3b4 +d1Z3s3gReG54zHkqeYl553pGeqV7BHtje8J8IXyBfOF9QX2hfgF+Yn7CfyN/hH/lgEeAqIEKgWuBzYIw +gpKC9INXg7qEHYSAhOOFR4Wrhg6GcobXhzuHn4gEiGmIzokziZmJ/opkisqLMIuWi/yMY4zKjTGNmI3/ +jmaOzo82j56QBpBukNaRP5GokhGSepLjk02TtpQglIqU9JVflcmWNJaflwqXdZfgmEyYuJkkmZCZ/Jpo +mtWbQpuvnByciZz3nWSd0p5Anq6fHZ+Ln/qgaaDYoUehtqImopajBqN2o+akVqTHpTilqaYapoum/adu +p+CoUqjEqTepqaocqo+rAqt1q+msXKzQrUStuK4trqGvFq+LsACwdbDqsWCx1rJLssKzOLOutCW0nLUT +tYq2AbZ5tvC3aLfguFm40blKucK6O7q1uy67p7whvJu9Fb2Pvgq+hL7/v3q/9cBwwOzBZ8Hjwl/C28NY +w9TEUcTOxUvFyMZGxsPHQce/yD3IvMk6ybnKOMq3yzbLtsw1zLXNNc21zjbOts83z7jQOdC60TzRvtI/ +0sHTRNPG1EnUy9VO1dHWVdbY11zX4Nhk2OjZbNnx2nba+9uA3AXcit0Q3ZbeHN6i3ynfr+A24L3hROHM +4lPi2+Nj4+vkc+T85YTmDeaW5x/nqegy6LzpRunQ6lvq5etw6/vshu0R7ZzuKO6070DvzPBY8OXxcvH/ +8ozzGfOn9DT0wvVQ9d72bfb794r4Gfio+Tj5x/pX+uf7d/wH/Jj9Kf26/kv+3P9t///SLS4vMFokY2xh +c3NuYW1lWCRjbGFzc2VzXxAQTlNCaXRtYXBJbWFnZVJlcKMvMTJaTlNJbWFnZVJlcFhOU09iamVjdNIt +LjQ1V05TQXJyYXmiNDLSLS43OF5OU011dGFibGVBcnJheaM3NDLTOjsOPD0+V05TV2hpdGVcTlNDb2xv +clNwYWNlRDAgMAAQA4AM0i0uQEFXTlNDb2xvcqJAMtItLkNEV05TSW1hZ2WiQzIACAARABoAJAApADIA +NwBJAEwAUQBTAGIAaAB3AH4AhQCSAJkApQCtALwAvgDAAMUAxwDJAMsAzQDUANkA5ADmAOgA6gDvAPIA +9AD2APgA/wEWATIBNAE2E6YTqxO2E78T0hPWE+ET6hPvE/cT+hP/FA4UEhQZFCEULhQzFDUUNxQ8FEQU +RxRMFFQAAAAAAAACAQAAAAAAAABFAAAAAAAAAAAAAAAAAAAUVw diff --git a/i18n/src/main/resources/zh_TW.lproj/Command.xib b/i18n/src/main/resources/zh_TW.lproj/Command.xib index 3b50719b53..aad60e4d5e 100644 --- a/i18n/src/main/resources/zh_TW.lproj/Command.xib +++ b/i18n/src/main/resources/zh_TW.lproj/Command.xib @@ -1,7 +1,7 @@ - + - + diff --git a/i18n/src/main/resources/zh_TW.lproj/Connection.xib b/i18n/src/main/resources/zh_TW.lproj/Connection.xib index 5d82e7b558..760f5f272c 100644 --- a/i18n/src/main/resources/zh_TW.lproj/Connection.xib +++ b/i18n/src/main/resources/zh_TW.lproj/Connection.xib @@ -1,7 +1,7 @@ - + - + @@ -76,7 +76,7 @@ - + diff --git a/i18n/src/main/resources/zh_TW.lproj/Info.xib b/i18n/src/main/resources/zh_TW.lproj/Info.xib index 85201c8719..804ba2a9e8 100644 --- a/i18n/src/main/resources/zh_TW.lproj/Info.xib +++ b/i18n/src/main/resources/zh_TW.lproj/Info.xib @@ -1,7 +1,7 @@ - + - + @@ -89,7 +89,7 @@ - + @@ -1017,21 +1017,21 @@ - + - + - + @@ -1121,21 +1121,21 @@ - + - + - + @@ -1250,27 +1250,27 @@ diff --git a/i18n/src/main/resources/zh_TW.lproj/Login.strings b/i18n/src/main/resources/zh_TW.lproj/Login.strings index a08ec2ad45..9b67252d14 100644 Binary files a/i18n/src/main/resources/zh_TW.lproj/Login.strings and b/i18n/src/main/resources/zh_TW.lproj/Login.strings differ diff --git a/i18n/src/main/resources/zh_TW.lproj/Main.xib b/i18n/src/main/resources/zh_TW.lproj/Main.xib index ddc4848d1f..a64547e112 100644 --- a/i18n/src/main/resources/zh_TW.lproj/Main.xib +++ b/i18n/src/main/resources/zh_TW.lproj/Main.xib @@ -1,7 +1,7 @@ - + - + @@ -159,19 +159,19 @@ - + - + - + @@ -321,12 +321,12 @@ CA - + - + @@ -566,6 +566,10 @@ CA - + + + + + diff --git a/i18n/src/main/resources/zh_TW.lproj/Preferences.xib b/i18n/src/main/resources/zh_TW.lproj/Preferences.xib index 20200da8f6..7a010ad897 100644 --- a/i18n/src/main/resources/zh_TW.lproj/Preferences.xib +++ b/i18n/src/main/resources/zh_TW.lproj/Preferences.xib @@ -1,7 +1,7 @@ - + - + @@ -268,7 +268,7 @@ - + @@ -309,7 +309,7 @@ - + @@ -540,7 +540,7 @@ - + @@ -580,7 +580,7 @@ - + @@ -615,7 +615,7 @@ - + @@ -644,7 +644,7 @@ - + @@ -1220,7 +1220,7 @@ - + @@ -1252,7 +1252,7 @@ - + @@ -1292,7 +1292,7 @@ - + @@ -1365,7 +1365,7 @@ - + @@ -1560,7 +1560,7 @@ - + @@ -1620,7 +1620,7 @@ - + @@ -1645,7 +1645,7 @@ - + @@ -1687,7 +1687,7 @@ - + @@ -1715,7 +1715,7 @@ - + @@ -1743,7 +1743,7 @@ - + @@ -1772,7 +1772,7 @@ - + @@ -1807,7 +1807,7 @@ - + @@ -1890,7 +1890,7 @@ - + @@ -1949,7 +1949,7 @@ - + @@ -1977,7 +1977,7 @@ - + @@ -2006,7 +2006,7 @@ - + diff --git a/i18n/src/main/resources/zh_TW.lproj/Profiles.xib b/i18n/src/main/resources/zh_TW.lproj/Profiles.xib index a72a647b52..a2b648d866 100644 --- a/i18n/src/main/resources/zh_TW.lproj/Profiles.xib +++ b/i18n/src/main/resources/zh_TW.lproj/Profiles.xib @@ -1,7 +1,7 @@ - + - + diff --git a/i18n/src/main/resources/zh_TW.lproj/Prompt.xib b/i18n/src/main/resources/zh_TW.lproj/Prompt.xib index 682126900c..a6f3e3d8c3 100644 --- a/i18n/src/main/resources/zh_TW.lproj/Prompt.xib +++ b/i18n/src/main/resources/zh_TW.lproj/Prompt.xib @@ -1,7 +1,7 @@ - + - + @@ -95,7 +95,7 @@ Gw - + diff --git a/i18n/src/main/resources/zh_TW.lproj/S3.strings b/i18n/src/main/resources/zh_TW.lproj/S3.strings index dfe0ad502a..7e243fc8e3 100644 Binary files a/i18n/src/main/resources/zh_TW.lproj/S3.strings and b/i18n/src/main/resources/zh_TW.lproj/S3.strings differ diff --git a/i18n/src/main/resources/zh_TW.lproj/Updater.strings b/i18n/src/main/resources/zh_TW.lproj/Updater.strings index 59a99f8ec5..59993dcbf7 100644 Binary files a/i18n/src/main/resources/zh_TW.lproj/Updater.strings and b/i18n/src/main/resources/zh_TW.lproj/Updater.strings differ diff --git a/importer/dll/pom.xml b/importer/dll/pom.xml index 25b3655a1e..d805c90a04 100644 --- a/importer/dll/pom.xml +++ b/importer/dll/pom.xml @@ -5,7 +5,7 @@ ch.cyberduck parent ../../pom.xml - 9.2.0-SNAPSHOT + 9.4.0-SNAPSHOT Cyberduck.Importer pom diff --git a/importer/pom.xml b/importer/pom.xml index 1f94d47b2f..5be9b2a767 100644 --- a/importer/pom.xml +++ b/importer/pom.xml @@ -18,7 +18,7 @@ parent ch.cyberduck - 9.2.0-SNAPSHOT + 9.4.0-SNAPSHOT 4.0.0 diff --git a/importer/src/main/java/ch/cyberduck/core/importer/ThirdpartyBookmarkCollection.java b/importer/src/main/java/ch/cyberduck/core/importer/ThirdpartyBookmarkCollection.java index dc11c453c6..04aa62b872 100644 --- a/importer/src/main/java/ch/cyberduck/core/importer/ThirdpartyBookmarkCollection.java +++ b/importer/src/main/java/ch/cyberduck/core/importer/ThirdpartyBookmarkCollection.java @@ -29,7 +29,6 @@ import ch.cyberduck.core.PasswordStoreFactory; import ch.cyberduck.core.ProtocolFactory; import ch.cyberduck.core.exception.AccessDeniedException; import ch.cyberduck.core.exception.BackgroundException; -import ch.cyberduck.core.exception.LocalAccessDeniedException; import ch.cyberduck.core.io.Checksum; import ch.cyberduck.core.io.ChecksumComputeFactory; import ch.cyberduck.core.io.HashAlgorithm; @@ -157,7 +156,7 @@ public abstract class ThirdpartyBookmarkCollection extends Collection { keychain.addPassword(bookmark.getHostname(), credentials.getIdentity().getAbbreviatedPath(), credentials.getPassword()); } - catch(LocalAccessDeniedException e) { + catch(AccessDeniedException e) { log.error("Failure {} saving credentials for {} in password store", e, bookmark); } } @@ -166,7 +165,7 @@ public abstract class ThirdpartyBookmarkCollection extends Collection { keychain.addPassword(bookmark.getProtocol().getScheme(), bookmark.getPort(), bookmark.getHostname(), credentials.getUsername(), credentials.getPassword()); } - catch(LocalAccessDeniedException e) { + catch(AccessDeniedException e) { log.error("Failure {} saving credentials for {} in password store", e, bookmark); } // Reset password in memory diff --git a/irods/pom.xml b/irods/pom.xml index f1a38a29f4..ac2d41a123 100644 --- a/irods/pom.xml +++ b/irods/pom.xml @@ -18,7 +18,7 @@ ch.cyberduck parent - 9.2.0-SNAPSHOT + 9.4.0-SNAPSHOT irods jar diff --git a/irods/src/main/java/ch/cyberduck/core/irods/IRODSAttributesFinderFeature.java b/irods/src/main/java/ch/cyberduck/core/irods/IRODSAttributesFinderFeature.java index 8abbc111bd..9837f4290b 100644 --- a/irods/src/main/java/ch/cyberduck/core/irods/IRODSAttributesFinderFeature.java +++ b/irods/src/main/java/ch/cyberduck/core/irods/IRODSAttributesFinderFeature.java @@ -15,6 +15,7 @@ package ch.cyberduck.core.irods; * GNU General Public License for more details. */ +import ch.cyberduck.core.DefaultPathAttributes; import ch.cyberduck.core.ListProgressListener; import ch.cyberduck.core.Path; import ch.cyberduck.core.PathAttributes; @@ -57,7 +58,7 @@ public class IRODSAttributesFinderFeature implements AttributesFinder, Attribute @Override public PathAttributes toAttributes(final ObjStat stats) { - final PathAttributes attributes = new PathAttributes(); + final PathAttributes attributes = new DefaultPathAttributes(); attributes.setModificationDate(stats.getModifiedAt().getTime()); attributes.setCreationDate(stats.getCreatedAt().getTime()); attributes.setSize(stats.getObjSize()); diff --git a/irods/src/main/java/ch/cyberduck/core/irods/IRODSDeleteFeature.java b/irods/src/main/java/ch/cyberduck/core/irods/IRODSDeleteFeature.java index f429637d3c..47261db3ef 100644 --- a/irods/src/main/java/ch/cyberduck/core/irods/IRODSDeleteFeature.java +++ b/irods/src/main/java/ch/cyberduck/core/irods/IRODSDeleteFeature.java @@ -75,7 +75,7 @@ public class IRODSDeleteFeature implements Delete { } @Override - public EnumSet features() { + public EnumSet features(final Path file) { return EnumSet.of(Flags.recursive); } } diff --git a/irods/src/main/java/ch/cyberduck/core/irods/IRODSDirectoryFeature.java b/irods/src/main/java/ch/cyberduck/core/irods/IRODSDirectoryFeature.java index 0547329941..7f4e6ee90d 100644 --- a/irods/src/main/java/ch/cyberduck/core/irods/IRODSDirectoryFeature.java +++ b/irods/src/main/java/ch/cyberduck/core/irods/IRODSDirectoryFeature.java @@ -20,6 +20,7 @@ package ch.cyberduck.core.irods; import ch.cyberduck.core.Path; import ch.cyberduck.core.exception.BackgroundException; import ch.cyberduck.core.features.Directory; +import ch.cyberduck.core.features.Write; import ch.cyberduck.core.transfer.TransferStatus; import org.irods.jargon.core.exception.JargonException; @@ -35,7 +36,7 @@ public class IRODSDirectoryFeature implements Directory { } @Override - public Path mkdir(final Path folder, final TransferStatus status) throws BackgroundException { + public Path mkdir(final Write writer, final Path folder, final TransferStatus status) throws BackgroundException { try { final IRODSFileSystemAO fs = session.getClient(); final IRODSFile f = fs.getIRODSFileFactory().instanceIRODSFile(folder.getAbsolute()); diff --git a/irods/src/main/java/ch/cyberduck/core/irods/IRODSDownloadFeature.java b/irods/src/main/java/ch/cyberduck/core/irods/IRODSDownloadFeature.java index d4c73ac7b8..bcf7b24e55 100644 --- a/irods/src/main/java/ch/cyberduck/core/irods/IRODSDownloadFeature.java +++ b/irods/src/main/java/ch/cyberduck/core/irods/IRODSDownloadFeature.java @@ -51,7 +51,7 @@ public class IRODSDownloadFeature implements Download { } @Override - public void download(final Path file, final Local local, final BandwidthThrottle throttle, + public void download(final Read read, final Path file, final Local local, final BandwidthThrottle throttle, final StreamListener listener, final TransferStatus status, final ConnectionCallback callback) throws BackgroundException { try { @@ -87,9 +87,4 @@ public class IRODSDownloadFeature implements Download { public boolean offset(final Path file) { return false; } - - @Override - public Download withReader(final Read reader) { - return this; - } } diff --git a/irods/src/main/java/ch/cyberduck/core/irods/IRODSListService.java b/irods/src/main/java/ch/cyberduck/core/irods/IRODSListService.java index 9df21070b9..a06b9c37a9 100644 --- a/irods/src/main/java/ch/cyberduck/core/irods/IRODSListService.java +++ b/irods/src/main/java/ch/cyberduck/core/irods/IRODSListService.java @@ -18,6 +18,7 @@ package ch.cyberduck.core.irods; */ import ch.cyberduck.core.AttributedList; +import ch.cyberduck.core.DefaultPathAttributes; import ch.cyberduck.core.ListProgressListener; import ch.cyberduck.core.ListService; import ch.cyberduck.core.Path; @@ -61,7 +62,7 @@ public class IRODSListService implements ListService { if(StringUtils.equals(normalized, directory.getAbsolute())) { continue; } - final PathAttributes attributes = new PathAttributes(); + final PathAttributes attributes = new DefaultPathAttributes(); final ObjStat stats = fs.getObjStat(file.getAbsolutePath()); attributes.setModificationDate(stats.getModifiedAt().getTime()); attributes.setCreationDate(stats.getCreatedAt().getTime()); diff --git a/irods/src/main/java/ch/cyberduck/core/irods/IRODSSession.java b/irods/src/main/java/ch/cyberduck/core/irods/IRODSSession.java index 3526558d73..fa8303775b 100644 --- a/irods/src/main/java/ch/cyberduck/core/irods/IRODSSession.java +++ b/irods/src/main/java/ch/cyberduck/core/irods/IRODSSession.java @@ -38,8 +38,6 @@ import ch.cyberduck.core.features.Move; import ch.cyberduck.core.features.Read; import ch.cyberduck.core.features.Touch; import ch.cyberduck.core.features.Write; -import ch.cyberduck.core.preferences.HostPreferencesFactory; -import ch.cyberduck.core.preferences.PreferencesReader; import ch.cyberduck.core.proxy.ProxyFinder; import ch.cyberduck.core.shared.DefaultPathHomeFeature; import ch.cyberduck.core.shared.DelegatingHomeFeature; @@ -102,7 +100,6 @@ public class IRODSSession extends SSLSession { protected IRODSFileSystem configure(final IRODSFileSystem client) { final SettableJargonProperties properties = new SettableJargonProperties(client.getJargonProperties()); properties.setEncoding(host.getEncoding()); - final PreferencesReader preferences = HostPreferencesFactory.get(host); final int timeout = ConnectionTimeoutFactory.get(preferences).getTimeout() * 1000; properties.setIrodsSocketTimeout(timeout); properties.setIrodsParallelSocketTimeout(timeout); @@ -148,13 +145,16 @@ public class IRODSSession extends SSLSession { } @Override - protected void logout() throws BackgroundException { + public void disconnect() throws BackgroundException { try { client.getIRODSSession().closeSession(); } catch(JargonException e) { throw new IRODSExceptionMappingService().map(e); } + finally { + super.disconnect(); + } } @Override diff --git a/irods/src/main/java/ch/cyberduck/core/irods/IRODSTouchFeature.java b/irods/src/main/java/ch/cyberduck/core/irods/IRODSTouchFeature.java index acd8db1949..ac159c7fdd 100644 --- a/irods/src/main/java/ch/cyberduck/core/irods/IRODSTouchFeature.java +++ b/irods/src/main/java/ch/cyberduck/core/irods/IRODSTouchFeature.java @@ -20,6 +20,7 @@ package ch.cyberduck.core.irods; import ch.cyberduck.core.Path; import ch.cyberduck.core.exception.BackgroundException; import ch.cyberduck.core.features.Touch; +import ch.cyberduck.core.features.Write; import ch.cyberduck.core.transfer.TransferStatus; import org.irods.jargon.core.exception.JargonException; @@ -35,7 +36,7 @@ public class IRODSTouchFeature implements Touch { } @Override - public Path touch(final Path file, final TransferStatus status) throws BackgroundException { + public Path touch(final Write writer, final Path file, final TransferStatus status) throws BackgroundException { try { final IRODSFileSystemAO fs = session.getClient(); final int descriptor = fs.createFile(file.getAbsolute(), diff --git a/irods/src/main/java/ch/cyberduck/core/irods/IRODSUploadFeature.java b/irods/src/main/java/ch/cyberduck/core/irods/IRODSUploadFeature.java index 8c38fed794..93f4b59187 100644 --- a/irods/src/main/java/ch/cyberduck/core/irods/IRODSUploadFeature.java +++ b/irods/src/main/java/ch/cyberduck/core/irods/IRODSUploadFeature.java @@ -51,7 +51,7 @@ import org.irods.jargon.core.transfer.TransferControlBlock; import java.io.File; import java.text.MessageFormat; -public class IRODSUploadFeature implements Upload { +public class IRODSUploadFeature implements Upload { private static final Logger log = LogManager.getLogger(IRODSUploadFeature.class); private final IRODSSession session; @@ -61,9 +61,9 @@ public class IRODSUploadFeature implements Upload { } @Override - public Checksum upload(final Path file, final Local local, final BandwidthThrottle throttle, - final ProgressListener progress, final StreamListener streamListener, final TransferStatus status, - final ConnectionCallback callback) throws BackgroundException { + public Void upload(final Write write, final Path file, final Local local, final BandwidthThrottle throttle, + final ProgressListener progress, final StreamListener streamListener, final TransferStatus status, + final ConnectionCallback callback) throws BackgroundException { try { final IRODSFileSystemAO fs = session.getClient(); final IRODSFile f = fs.getIRODSFileFactory().instanceIRODSFile(file.getAbsolute()); @@ -83,8 +83,8 @@ public class IRODSUploadFeature implements Upload { ), block); if(status.isComplete()) { final DataObjectChecksumUtilitiesAO checksum = fs - .getIRODSAccessObjectFactory() - .getDataObjectChecksumUtilitiesAO(fs.getIRODSAccount()); + .getIRODSAccessObjectFactory() + .getDataObjectChecksumUtilitiesAO(fs.getIRODSAccount()); final ChecksumValue value = checksum.computeChecksumOnDataObject(f); final Checksum fingerprint = Checksum.parse(value.getChecksumStringValue()); if(null == fingerprint) { @@ -98,12 +98,11 @@ public class IRODSUploadFeature implements Upload { final Checksum expected = ChecksumComputeFactory.get(fingerprint.algorithm).compute(local.getInputStream(), new TransferStatus(status)); if(!expected.equals(fingerprint)) { throw new ChecksumException(MessageFormat.format(LocaleFactory.localizedString("Upload {0} failed", "Error"), file.getName()), - MessageFormat.format("Mismatch between {0} hash {1} of uploaded data and ETag {2} returned by the server", - fingerprint.algorithm.toString(), expected, fingerprint.hash)); + MessageFormat.format("Mismatch between {0} hash {1} of uploaded data and ETag {2} returned by the server", + fingerprint.algorithm.toString(), expected, fingerprint.hash)); } } } - return fingerprint; } return null; } @@ -116,9 +115,4 @@ public class IRODSUploadFeature implements Upload { public Write.Append append(final Path file, final TransferStatus status) throws BackgroundException { return new Write.Append(status.isExists()).withStatus(status); } - - @Override - public Upload withWriter(final Write writer) { - return this; - } } diff --git a/irods/src/main/java/ch/cyberduck/core/irods/IRODSWriteFeature.java b/irods/src/main/java/ch/cyberduck/core/irods/IRODSWriteFeature.java index 20e323035d..e567a368af 100644 --- a/irods/src/main/java/ch/cyberduck/core/irods/IRODSWriteFeature.java +++ b/irods/src/main/java/ch/cyberduck/core/irods/IRODSWriteFeature.java @@ -22,6 +22,7 @@ import ch.cyberduck.core.Path; import ch.cyberduck.core.exception.BackgroundException; import ch.cyberduck.core.features.Write; import ch.cyberduck.core.io.StatusOutputStream; +import ch.cyberduck.core.io.VoidStatusOutputStream; import ch.cyberduck.core.transfer.TransferStatus; import ch.cyberduck.core.worker.DefaultExceptionMappingService; @@ -29,11 +30,10 @@ import org.irods.jargon.core.exception.JargonException; import org.irods.jargon.core.exception.JargonRuntimeException; import org.irods.jargon.core.packinstr.DataObjInp; import org.irods.jargon.core.pub.IRODSFileSystemAO; -import org.irods.jargon.core.pub.domain.ObjStat; import org.irods.jargon.core.pub.io.IRODSFileOutputStream; import org.irods.jargon.core.pub.io.PackingIrodsOutputStream; -public class IRODSWriteFeature implements Write { +public class IRODSWriteFeature implements Write { private final IRODSSession session; @@ -42,24 +42,13 @@ public class IRODSWriteFeature implements Write { } @Override - public StatusOutputStream write(final Path file, final TransferStatus status, final ConnectionCallback callback) throws BackgroundException { + public StatusOutputStream write(final Path file, final TransferStatus status, final ConnectionCallback callback) throws BackgroundException { try { try { final IRODSFileSystemAO fs = session.getClient(); final IRODSFileOutputStream out = fs.getIRODSFileFactory().instanceIRODSFileOutputStream( file.getAbsolute(), status.isAppend() ? DataObjInp.OpenFlags.READ_WRITE : DataObjInp.OpenFlags.WRITE_TRUNCATE); - return new StatusOutputStream(new PackingIrodsOutputStream(out)) { - @Override - public ObjStat getStatus() throws BackgroundException { - // No remote attributes from server returned after upload - try { - return fs.getObjStat(file.getAbsolute()); - } - catch(JargonException e) { - throw new IRODSExceptionMappingService().map("Failure to read attributes of {0}", e, file); - } - } - }; + return new VoidStatusOutputStream(new PackingIrodsOutputStream(out)); } catch(JargonRuntimeException e) { if(e.getCause() instanceof JargonException) { diff --git a/irods/src/test/java/ch/cyberduck/core/irods/IRODSAttributesFinderFeatureTest.java b/irods/src/test/java/ch/cyberduck/core/irods/IRODSAttributesFinderFeatureTest.java index 3bcb945cc1..c5e479a359 100644 --- a/irods/src/test/java/ch/cyberduck/core/irods/IRODSAttributesFinderFeatureTest.java +++ b/irods/src/test/java/ch/cyberduck/core/irods/IRODSAttributesFinderFeatureTest.java @@ -27,7 +27,6 @@ import ch.cyberduck.core.ProtocolFactory; import ch.cyberduck.core.exception.NotfoundException; import ch.cyberduck.core.features.Delete; import ch.cyberduck.core.proxy.DisabledProxyFinder; -import ch.cyberduck.core.proxy.Proxy; import ch.cyberduck.core.serializer.impl.dd.ProfilePlistReader; import ch.cyberduck.core.transfer.TransferStatus; import ch.cyberduck.test.IntegrationTest; @@ -73,12 +72,12 @@ public class IRODSAttributesFinderFeatureTest extends VaultTest { session.open(new DisabledProxyFinder(), new DisabledHostKeyCallback(), new DisabledLoginCallback(), new DisabledCancelCallback()); session.login(new DisabledLoginCallback(), new DisabledCancelCallback()); - final Path folder = new IRODSDirectoryFeature(session).mkdir(new Path( + final Path folder = new IRODSDirectoryFeature(session).mkdir(new IRODSWriteFeature(session), new Path( new IRODSHomeFinderService(session).find(), UUID.randomUUID().toString(), EnumSet.of(Path.Type.directory)), new TransferStatus()); final IRODSAttributesFinderFeature f = new IRODSAttributesFinderFeature(session); final long folderTimestamp = f.find(folder).getModificationDate(); final Path test = new Path(folder, UUID.randomUUID().toString(), EnumSet.of(Path.Type.file)); - new IRODSTouchFeature(session).touch(test, new TransferStatus()); + new IRODSTouchFeature(session).touch(new IRODSWriteFeature(session), test, new TransferStatus()); assertEquals(folderTimestamp, f.find(folder).getModificationDate()); final PathAttributes attributes = f.find(test); assertEquals(0L, attributes.getSize()); diff --git a/irods/src/test/java/ch/cyberduck/core/irods/IRODSCopyFeatureTest.java b/irods/src/test/java/ch/cyberduck/core/irods/IRODSCopyFeatureTest.java index 98f4729eac..6db993b2ab 100644 --- a/irods/src/test/java/ch/cyberduck/core/irods/IRODSCopyFeatureTest.java +++ b/irods/src/test/java/ch/cyberduck/core/irods/IRODSCopyFeatureTest.java @@ -29,7 +29,6 @@ import ch.cyberduck.core.ProtocolFactory; import ch.cyberduck.core.features.Delete; import ch.cyberduck.core.io.DisabledStreamListener; import ch.cyberduck.core.proxy.DisabledProxyFinder; -import ch.cyberduck.core.proxy.Proxy; import ch.cyberduck.core.serializer.impl.dd.ProfilePlistReader; import ch.cyberduck.core.transfer.TransferStatus; import ch.cyberduck.test.IntegrationTest; @@ -62,7 +61,7 @@ public class IRODSCopyFeatureTest extends VaultTest { session.open(new DisabledProxyFinder(), new DisabledHostKeyCallback(), new DisabledLoginCallback(), new DisabledCancelCallback()); session.login(new DisabledLoginCallback(), new DisabledCancelCallback()); final Path test = new Path(new IRODSHomeFinderService(session).find(), UUID.randomUUID().toString(), EnumSet.of(Path.Type.file)); - new IRODSTouchFeature(session).touch(test, new TransferStatus()); + new IRODSTouchFeature(session).touch(new IRODSWriteFeature(session), test, new TransferStatus()); final Path copy = new Path(new IRODSHomeFinderService(session).find(), UUID.randomUUID().toString(), EnumSet.of(Path.Type.file)); new IRODSCopyFeature(session).copy(test, copy, new TransferStatus(), new DisabledConnectionCallback(), new DisabledStreamListener()); assertTrue(new IRODSFindFeature(session).find(test)); diff --git a/irods/src/test/java/ch/cyberduck/core/irods/IRODSDeleteFeatureTest.java b/irods/src/test/java/ch/cyberduck/core/irods/IRODSDeleteFeatureTest.java index 2c65b11051..2936c7c3df 100644 --- a/irods/src/test/java/ch/cyberduck/core/irods/IRODSDeleteFeatureTest.java +++ b/irods/src/test/java/ch/cyberduck/core/irods/IRODSDeleteFeatureTest.java @@ -28,7 +28,6 @@ import ch.cyberduck.core.ProtocolFactory; import ch.cyberduck.core.exception.NotfoundException; import ch.cyberduck.core.features.Delete; import ch.cyberduck.core.proxy.DisabledProxyFinder; -import ch.cyberduck.core.proxy.Proxy; import ch.cyberduck.core.serializer.impl.dd.ProfilePlistReader; import ch.cyberduck.core.transfer.TransferStatus; import ch.cyberduck.test.IntegrationTest; @@ -62,9 +61,9 @@ public class IRODSDeleteFeatureTest extends VaultTest { session.login(new DisabledLoginCallback(), new DisabledCancelCallback()); final Path folder = new Path(new IRODSHomeFinderService(session).find(), UUID.randomUUID().toString(), EnumSet.of(Path.Type.directory)); - new IRODSDirectoryFeature(session).mkdir(folder, new TransferStatus()); + new IRODSDirectoryFeature(session).mkdir(new IRODSWriteFeature(session), folder, new TransferStatus()); final Path file = new Path(folder, "f", EnumSet.of(Path.Type.file)); - new IRODSTouchFeature(session).touch(file, new TransferStatus()); + new IRODSTouchFeature(session).touch(new IRODSWriteFeature(session), file, new TransferStatus()); assertTrue(new IRODSFindFeature(session).find(folder)); assertTrue(new IRODSFindFeature(session).find(file)); new IRODSDeleteFeature(session).delete(Collections.singletonList(folder), new DisabledLoginCallback(), new Delete.DisabledCallback()); diff --git a/irods/src/test/java/ch/cyberduck/core/irods/IRODSDirectoryFeatureTest.java b/irods/src/test/java/ch/cyberduck/core/irods/IRODSDirectoryFeatureTest.java index 48e75a8889..9b4e716886 100644 --- a/irods/src/test/java/ch/cyberduck/core/irods/IRODSDirectoryFeatureTest.java +++ b/irods/src/test/java/ch/cyberduck/core/irods/IRODSDirectoryFeatureTest.java @@ -29,7 +29,6 @@ import ch.cyberduck.core.ProtocolFactory; import ch.cyberduck.core.features.Delete; import ch.cyberduck.core.features.Find; import ch.cyberduck.core.proxy.DisabledProxyFinder; -import ch.cyberduck.core.proxy.Proxy; import ch.cyberduck.core.serializer.impl.dd.ProfilePlistReader; import ch.cyberduck.core.transfer.TransferStatus; import ch.cyberduck.test.IntegrationTest; @@ -63,7 +62,7 @@ public class IRODSDirectoryFeatureTest extends VaultTest { session.login(new DisabledLoginCallback(), new DisabledCancelCallback()); final Path test = new Path(new IRODSHomeFinderService(session).find(), UUID.randomUUID().toString(), EnumSet.of(Path.Type.directory)); - new IRODSDirectoryFeature(session).mkdir(test, new TransferStatus()); + new IRODSDirectoryFeature(session).mkdir(new IRODSWriteFeature(session), test, new TransferStatus()); assertTrue(session.getFeature(Find.class).find(test)); session.getFeature(Delete.class).delete(Collections.singletonList(test), new DisabledLoginCallback(), new Delete.DisabledCallback()); diff --git a/irods/src/test/java/ch/cyberduck/core/irods/IRODSFindFeatureTest.java b/irods/src/test/java/ch/cyberduck/core/irods/IRODSFindFeatureTest.java index 5db1571ec7..a1e9fb9314 100644 --- a/irods/src/test/java/ch/cyberduck/core/irods/IRODSFindFeatureTest.java +++ b/irods/src/test/java/ch/cyberduck/core/irods/IRODSFindFeatureTest.java @@ -26,9 +26,7 @@ import ch.cyberduck.core.Path; import ch.cyberduck.core.Profile; import ch.cyberduck.core.ProtocolFactory; import ch.cyberduck.core.features.Delete; -import ch.cyberduck.core.features.Directory; import ch.cyberduck.core.proxy.DisabledProxyFinder; -import ch.cyberduck.core.proxy.Proxy; import ch.cyberduck.core.serializer.impl.dd.ProfilePlistReader; import ch.cyberduck.core.transfer.TransferStatus; import ch.cyberduck.test.IntegrationTest; @@ -62,7 +60,7 @@ public class IRODSFindFeatureTest extends VaultTest { session.login(new DisabledLoginCallback(), new DisabledCancelCallback()); final Path test = new Path(new IRODSHomeFinderService(session).find(), UUID.randomUUID().toString(), EnumSet.of(Path.Type.directory)); - session.getFeature(Directory.class).mkdir(test, new TransferStatus()); + new IRODSDirectoryFeature(session).mkdir(new IRODSWriteFeature(session), test, new TransferStatus()); assertTrue(new IRODSFindFeature(session).find(test)); session.getFeature(Delete.class).delete(Collections.singletonList(test), new DisabledLoginCallback(), new Delete.DisabledCallback()); diff --git a/irods/src/test/java/ch/cyberduck/core/irods/IRODSMoveFeatureTest.java b/irods/src/test/java/ch/cyberduck/core/irods/IRODSMoveFeatureTest.java index f0bb02f405..ca9a7a90d4 100644 --- a/irods/src/test/java/ch/cyberduck/core/irods/IRODSMoveFeatureTest.java +++ b/irods/src/test/java/ch/cyberduck/core/irods/IRODSMoveFeatureTest.java @@ -64,11 +64,11 @@ public class IRODSMoveFeatureTest extends VaultTest { final Path source = new Path(new IRODSHomeFinderService(session).find(), new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory)); final Path destination = new Path(new IRODSHomeFinderService(session).find(), new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory)); - new IRODSDirectoryFeature(session).mkdir(source, new TransferStatus()); + new IRODSDirectoryFeature(session).mkdir(new IRODSWriteFeature(session), source, new TransferStatus()); final String filename = new AlphanumericRandomStringService().random(); - new IRODSTouchFeature(session).touch(new Path(source, filename, EnumSet.of(Path.Type.file)), new TransferStatus()); + new IRODSTouchFeature(session).touch(new IRODSWriteFeature(session), new Path(source, filename, EnumSet.of(Path.Type.file)), new TransferStatus()); assertTrue(session.getFeature(Find.class).find(new Path(source, filename, EnumSet.of(Path.Type.file)))); - new IRODSDirectoryFeature(session).mkdir(destination, new TransferStatus()); + new IRODSDirectoryFeature(session).mkdir(new IRODSWriteFeature(session), destination, new TransferStatus()); new IRODSMoveFeature(session).move(source, destination, new TransferStatus().setExists(true), new Delete.DisabledCallback(), new DisabledConnectionCallback()); assertFalse(session.getFeature(Find.class).find(source)); assertFalse(session.getFeature(Find.class).find(new Path(source, filename, EnumSet.of(Path.Type.file)))); @@ -94,8 +94,8 @@ public class IRODSMoveFeatureTest extends VaultTest { final Path source = new Path(new IRODSHomeFinderService(session).find(), new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)); final Path destination = new Path(new IRODSHomeFinderService(session).find(), new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)); - new IRODSTouchFeature(session).touch(source, new TransferStatus()); - new IRODSTouchFeature(session).touch(destination, new TransferStatus()); + new IRODSTouchFeature(session).touch(new IRODSWriteFeature(session), source, new TransferStatus()); + new IRODSTouchFeature(session).touch(new IRODSWriteFeature(session), destination, new TransferStatus()); new IRODSMoveFeature(session).move(source, destination, new TransferStatus().setExists(true), new Delete.DisabledCallback(), new DisabledConnectionCallback()); assertFalse(session.getFeature(Find.class).find(source)); assertTrue(session.getFeature(Find.class).find(destination)); diff --git a/irods/src/test/java/ch/cyberduck/core/irods/IRODSReadFeatureTest.java b/irods/src/test/java/ch/cyberduck/core/irods/IRODSReadFeatureTest.java index 7a36583982..e77c908358 100644 --- a/irods/src/test/java/ch/cyberduck/core/irods/IRODSReadFeatureTest.java +++ b/irods/src/test/java/ch/cyberduck/core/irods/IRODSReadFeatureTest.java @@ -43,7 +43,6 @@ import ch.cyberduck.test.VaultTest; import org.apache.commons.io.IOUtils; import org.apache.commons.lang3.RandomUtils; -import org.irods.jargon.core.pub.domain.ObjStat; import org.junit.Test; import org.junit.experimental.categories.Category; @@ -129,7 +128,7 @@ public class IRODSReadFeatureTest extends VaultTest { session.open(new DisabledProxyFinder(), new DisabledHostKeyCallback(), new DisabledLoginCallback(), new DisabledCancelCallback()); session.login(new DisabledLoginCallback(), new DisabledCancelCallback()); final Path test = new Path(new IRODSHomeFinderService(session).find(), UUID.randomUUID().toString(), EnumSet.of(Path.Type.file)); - new IRODSTouchFeature(session).touch(test, new TransferStatus()); + new IRODSTouchFeature(session).touch(new IRODSWriteFeature(session), test, new TransferStatus()); final Local local = new Local(System.getProperty("java.io.tmpdir"), UUID.randomUUID().toString()); final byte[] content = RandomUtils.nextBytes(2048); @@ -137,8 +136,8 @@ public class IRODSReadFeatureTest extends VaultTest { assertNotNull(out); IOUtils.write(content, out); out.close(); - new DefaultUploadFeature(new IRODSWriteFeature(session)).upload( - test, local, new BandwidthThrottle(BandwidthThrottle.UNLIMITED), new DisabledProgressListener(), new DisabledStreamListener(), + new DefaultUploadFeature(session).upload( + new IRODSWriteFeature(session), test, local, new BandwidthThrottle(BandwidthThrottle.UNLIMITED), new DisabledProgressListener(), new DisabledStreamListener(), new TransferStatus().setLength(content.length), new DisabledConnectionCallback()); final TransferStatus status = new TransferStatus(); @@ -153,7 +152,7 @@ public class IRODSReadFeatureTest extends VaultTest { System.arraycopy(content, 100, reference, 0, content.length - 100); assertArrayEquals(reference, buffer.toByteArray()); in.close(); - new IRODSDeleteFeature(session).delete(Collections.singletonList(test), new DisabledLoginCallback(), new Delete.DisabledCallback()); + new IRODSDeleteFeature(session).delete(Collections.singletonList(test), new DisabledLoginCallback(), new Delete.DisabledCallback()); session.close(); } } diff --git a/irods/src/test/java/ch/cyberduck/core/irods/IRODSTouchFeatureTest.java b/irods/src/test/java/ch/cyberduck/core/irods/IRODSTouchFeatureTest.java index cfc98ca742..a7a9fb03fc 100644 --- a/irods/src/test/java/ch/cyberduck/core/irods/IRODSTouchFeatureTest.java +++ b/irods/src/test/java/ch/cyberduck/core/irods/IRODSTouchFeatureTest.java @@ -27,7 +27,6 @@ import ch.cyberduck.core.Profile; import ch.cyberduck.core.ProtocolFactory; import ch.cyberduck.core.features.Delete; import ch.cyberduck.core.proxy.DisabledProxyFinder; -import ch.cyberduck.core.proxy.Proxy; import ch.cyberduck.core.serializer.impl.dd.ProfilePlistReader; import ch.cyberduck.core.transfer.TransferStatus; import ch.cyberduck.test.IntegrationTest; @@ -60,7 +59,7 @@ public class IRODSTouchFeatureTest extends VaultTest { session.open(new DisabledProxyFinder(), new DisabledHostKeyCallback(), new DisabledLoginCallback(), new DisabledCancelCallback()); session.login(new DisabledLoginCallback(), new DisabledCancelCallback()); final Path test = new Path(new IRODSHomeFinderService(session).find(), UUID.randomUUID().toString(), EnumSet.of(Path.Type.file)); - new IRODSTouchFeature(session).touch(test, new TransferStatus()); + new IRODSTouchFeature(session).touch(new IRODSWriteFeature(session), test, new TransferStatus()); assertTrue(new IRODSFindFeature(session).find(test)); new IRODSDeleteFeature(session).delete(Collections.singletonList(test), new DisabledLoginCallback(), new Delete.DisabledCallback()); assertFalse(new IRODSFindFeature(session).find(test)); diff --git a/irods/src/test/java/ch/cyberduck/core/irods/IRODSUploadFeatureTest.java b/irods/src/test/java/ch/cyberduck/core/irods/IRODSUploadFeatureTest.java index 6382065a1f..947a3ac28e 100644 --- a/irods/src/test/java/ch/cyberduck/core/irods/IRODSUploadFeatureTest.java +++ b/irods/src/test/java/ch/cyberduck/core/irods/IRODSUploadFeatureTest.java @@ -32,9 +32,7 @@ import ch.cyberduck.core.ProtocolFactory; import ch.cyberduck.core.exception.ConnectionCanceledException; import ch.cyberduck.core.features.Delete; import ch.cyberduck.core.io.BandwidthThrottle; -import ch.cyberduck.core.io.Checksum; import ch.cyberduck.core.io.DisabledStreamListener; -import ch.cyberduck.core.io.MD5ChecksumCompute; import ch.cyberduck.core.proxy.DisabledProxyFinder; import ch.cyberduck.core.serializer.impl.dd.ProfilePlistReader; import ch.cyberduck.core.transfer.TransferStatus; @@ -47,7 +45,6 @@ import org.junit.Ignore; import org.junit.Test; import org.junit.experimental.categories.Category; -import java.io.FileInputStream; import java.io.InputStream; import java.io.OutputStream; import java.util.Collections; @@ -79,27 +76,24 @@ public class IRODSUploadFeatureTest extends VaultTest { final OutputStream out = local.getOutputStream(false); IOUtils.write(content, out); out.close(); - final Checksum checksumPart1; - final Checksum checksumPart2; final Path test = new Path(new IRODSHomeFinderService(session).find(), UUID.randomUUID().toString(), EnumSet.of(Path.Type.file)); { final TransferStatus status = new TransferStatus().setLength(content.length / 2); final BytecountStreamListener count = new BytecountStreamListener(); - checksumPart1 = new IRODSUploadFeature(session).upload( - test, local, new BandwidthThrottle(BandwidthThrottle.UNLIMITED), new DisabledProgressListener(), count, + new IRODSUploadFeature(session).upload( + new IRODSWriteFeature(session), test, local, new BandwidthThrottle(BandwidthThrottle.UNLIMITED), new DisabledProgressListener(), count, status, new DisabledConnectionCallback()); assertEquals(content.length / 2, count.getSent()); } { final TransferStatus status = new TransferStatus().setLength(content.length / 2).setOffset(content.length / 2).setAppend(true); - checksumPart2 = new IRODSUploadFeature(session).upload( - test, local, new BandwidthThrottle(BandwidthThrottle.UNLIMITED), new DisabledProgressListener(), new DisabledStreamListener(), + new IRODSUploadFeature(session).upload( + new IRODSWriteFeature(session), test, local, new BandwidthThrottle(BandwidthThrottle.UNLIMITED), new DisabledProgressListener(), new DisabledStreamListener(), status, new DisabledConnectionCallback()); assertEquals(content.length / 2, status.getOffset()); } - assertNotEquals(checksumPart1, checksumPart2); final byte[] buffer = new byte[content.length]; final InputStream in = new IRODSReadFeature(session).read(test, new TransferStatus().setLength(content.length), new DisabledConnectionCallback()); IOUtils.readFully(in, buffer); @@ -128,16 +122,13 @@ public class IRODSUploadFeatureTest extends VaultTest { final OutputStream out = local.getOutputStream(false); IOUtils.write(content, out); out.close(); - final Checksum checksum; final Path test = new Path(new IRODSHomeFinderService(session).find(), UUID.randomUUID().toString(), EnumSet.of(Path.Type.file)); final TransferStatus status = new TransferStatus().setLength(content.length); - final TransferStatus copy = new TransferStatus(status); final BytecountStreamListener count = new BytecountStreamListener(); - checksum = new IRODSUploadFeature(session).upload( - test, local, new BandwidthThrottle(BandwidthThrottle.UNLIMITED), new DisabledProgressListener(), count, status, new DisabledConnectionCallback()); + new IRODSUploadFeature(session).upload( + new IRODSWriteFeature(session), test, local, new BandwidthThrottle(BandwidthThrottle.UNLIMITED), new DisabledProgressListener(), count, status, new DisabledConnectionCallback()); assertTrue(status.isComplete()); assertEquals(content.length, count.getSent()); - assertEquals(checksum, new MD5ChecksumCompute().compute(new FileInputStream(local.getAbsolute()), copy)); final byte[] buffer = new byte[content.length]; final InputStream in = new IRODSReadFeature(session).read(test, new TransferStatus().setLength(content.length), new DisabledConnectionCallback()); IOUtils.readFully(in, buffer); @@ -167,8 +158,8 @@ public class IRODSUploadFeatureTest extends VaultTest { out.close(); final Path test = new Path(new IRODSHomeFinderService(session).find(), UUID.randomUUID().toString(), EnumSet.of(Path.Type.file)); final TransferStatus status = new TransferStatus().setLength(content.length); - final Checksum checksum = new IRODSUploadFeature(session).upload( - test, local, new BandwidthThrottle(BandwidthThrottle.UNLIMITED), new DisabledProgressListener(), new DisabledStreamListener() { + new IRODSUploadFeature(session).upload( + new IRODSWriteFeature(session), test, local, new BandwidthThrottle(BandwidthThrottle.UNLIMITED), new DisabledProgressListener(), new DisabledStreamListener() { @Override public void sent(final long bytes) { super.sent(bytes); diff --git a/irods/src/test/java/ch/cyberduck/core/irods/IRODSWriteFeatureTest.java b/irods/src/test/java/ch/cyberduck/core/irods/IRODSWriteFeatureTest.java index 41b67c679a..4212f1f739 100644 --- a/irods/src/test/java/ch/cyberduck/core/irods/IRODSWriteFeatureTest.java +++ b/irods/src/test/java/ch/cyberduck/core/irods/IRODSWriteFeatureTest.java @@ -41,7 +41,6 @@ import ch.cyberduck.test.VaultTest; import org.apache.commons.io.IOUtils; import org.apache.commons.lang3.RandomUtils; -import org.irods.jargon.core.pub.domain.ObjStat; import org.junit.Test; import org.junit.experimental.categories.Category; @@ -132,18 +131,15 @@ public class IRODSWriteFeatureTest extends VaultTest { final OutputStream out1 = new IRODSWriteFeature(session1).write(test1, new TransferStatus().setAppend(false).setLength(content.length), new DisabledConnectionCallback()); final OutputStream out2 = new IRODSWriteFeature(session2).write(test2, new TransferStatus().setAppend(false).setLength(content.length), new DisabledConnectionCallback()); - new Thread(new Runnable() { - @Override - public void run() { - try { - new StreamCopier(new TransferStatus(), new TransferStatus()).transfer(new ByteArrayInputStream(content), out2); - } - catch(BackgroundException e) { - fail(); - } - finally { - cw1.countDown(); - } + new Thread(() -> { + try { + new StreamCopier(new TransferStatus(), new TransferStatus()).transfer(new ByteArrayInputStream(content), out2); + } + catch(BackgroundException e) { + fail(); + } + finally { + cw1.countDown(); } }).start(); new Thread(new Runnable() { @@ -236,7 +232,7 @@ public class IRODSWriteFeatureTest extends VaultTest { assertEquals(0L, new IRODSUploadFeature(session).append(test, status).offset, 0L); - final StatusOutputStream out = feature.write(test, status, new DisabledConnectionCallback()); + final StatusOutputStream out = feature.write(test, status, new DisabledConnectionCallback()); assertNotNull(out); new StreamCopier(new TransferStatus(), new TransferStatus()).transfer(new ByteArrayInputStream(content), out); @@ -255,6 +251,7 @@ public class IRODSWriteFeatureTest extends VaultTest { final byte[] newcontent = RandomUtils.nextBytes(10); final TransferStatus status = new TransferStatus(); + status.setExists(true); status.setAppend(false); status.setLength(newcontent.length); status.setRemote(new IRODSAttributesFinderFeature(session).find(test)); @@ -262,15 +259,14 @@ public class IRODSWriteFeatureTest extends VaultTest { assertTrue(new IRODSUploadFeature(session).append(test, status).append); assertEquals(content.length, new IRODSUploadFeature(session).append(test, status).offset, 0L); - final StatusOutputStream out = feature.write(test, status, new DisabledConnectionCallback()); - assertNotNull(out); + final StatusOutputStream out = feature.write(test, status, new DisabledConnectionCallback()); + assertNull(out); new StreamCopier(new TransferStatus(), new TransferStatus()).transfer(new ByteArrayInputStream(newcontent), out); assertTrue(session.getFeature(Find.class).find(test)); final PathAttributes attributes = new IRODSAttributesFinderFeature(session).find(test); assertEquals(newcontent.length, attributes.getSize()); - assertEquals(new IRODSAttributesFinderFeature(session).toAttributes(out.getStatus()), attributes); final InputStream in = session.getFeature(Read.class).read(test, new TransferStatus(), new DisabledConnectionCallback()); final byte[] buffer = new byte[newcontent.length]; @@ -329,6 +325,7 @@ public class IRODSWriteFeatureTest extends VaultTest { final byte[] content_append = RandomUtils.nextBytes(100); final TransferStatus status_append = new TransferStatus(); + status_append.setExists(true); status_append.setAppend(true); status_append.setLength(content_append.length); status_append.setRemote(new IRODSAttributesFinderFeature(session).find(test)); diff --git a/jersey/pom.xml b/jersey/pom.xml index e4814c90cd..ce976b6e6c 100644 --- a/jersey/pom.xml +++ b/jersey/pom.xml @@ -19,7 +19,7 @@ parent ch.cyberduck - 9.2.0-SNAPSHOT + 9.4.0-SNAPSHOT jersey diff --git a/manta/pom.xml b/manta/pom.xml index 6962a728e8..ee105ca775 100644 --- a/manta/pom.xml +++ b/manta/pom.xml @@ -19,7 +19,7 @@ parent ch.cyberduck - 9.2.0-SNAPSHOT + 9.4.0-SNAPSHOT manta diff --git a/manta/src/main/java/ch/cyberduck/core/manta/MantaDeleteFeature.java b/manta/src/main/java/ch/cyberduck/core/manta/MantaDeleteFeature.java index 17e01ac26c..2ed256c7d1 100644 --- a/manta/src/main/java/ch/cyberduck/core/manta/MantaDeleteFeature.java +++ b/manta/src/main/java/ch/cyberduck/core/manta/MantaDeleteFeature.java @@ -72,7 +72,7 @@ public class MantaDeleteFeature implements Delete { } @Override - public EnumSet features() { + public EnumSet features(final Path file) { return EnumSet.of(Flags.recursive); } } diff --git a/manta/src/main/java/ch/cyberduck/core/manta/MantaDirectoryFeature.java b/manta/src/main/java/ch/cyberduck/core/manta/MantaDirectoryFeature.java index 17d8bbb732..82f90ca2e8 100644 --- a/manta/src/main/java/ch/cyberduck/core/manta/MantaDirectoryFeature.java +++ b/manta/src/main/java/ch/cyberduck/core/manta/MantaDirectoryFeature.java @@ -21,6 +21,7 @@ import ch.cyberduck.core.Path; import ch.cyberduck.core.exception.AccessDeniedException; import ch.cyberduck.core.exception.BackgroundException; import ch.cyberduck.core.features.Directory; +import ch.cyberduck.core.features.Write; import ch.cyberduck.core.transfer.TransferStatus; import java.io.IOException; @@ -38,7 +39,7 @@ public class MantaDirectoryFeature implements Directory { } @Override - public Path mkdir(final Path folder, final TransferStatus status) throws BackgroundException { + public Path mkdir(final Write writer, final Path folder, final TransferStatus status) throws BackgroundException { try { session.getClient().putDirectory(folder.getAbsolute()); return folder; diff --git a/manta/src/main/java/ch/cyberduck/core/manta/MantaMoveFeature.java b/manta/src/main/java/ch/cyberduck/core/manta/MantaMoveFeature.java index 47260683e4..a7c53f3a5e 100644 --- a/manta/src/main/java/ch/cyberduck/core/manta/MantaMoveFeature.java +++ b/manta/src/main/java/ch/cyberduck/core/manta/MantaMoveFeature.java @@ -17,8 +17,10 @@ package ch.cyberduck.core.manta; import ch.cyberduck.core.ConnectionCallback; import ch.cyberduck.core.DefaultIOExceptionMappingService; +import ch.cyberduck.core.DefaultPathAttributes; import ch.cyberduck.core.LocaleFactory; import ch.cyberduck.core.Path; +import ch.cyberduck.core.PathAttributes; import ch.cyberduck.core.exception.AccessDeniedException; import ch.cyberduck.core.exception.BackgroundException; import ch.cyberduck.core.features.Delete; @@ -47,7 +49,7 @@ public class MantaMoveFeature implements Move { try { session.getClient().move(file.getAbsolute(), renamed.getAbsolute()); // Copy original file attributes - return new Path(renamed).withAttributes(file.attributes()); + return new Path(renamed).withAttributes(new DefaultPathAttributes(file.attributes()).setVault(null)); } catch(MantaException e) { throw new MantaExceptionMappingService().map("Cannot rename {0}", e, file); diff --git a/manta/src/main/java/ch/cyberduck/core/manta/MantaObjectAttributeAdapter.java b/manta/src/main/java/ch/cyberduck/core/manta/MantaObjectAttributeAdapter.java index 3f2f617c4d..affd0daa18 100644 --- a/manta/src/main/java/ch/cyberduck/core/manta/MantaObjectAttributeAdapter.java +++ b/manta/src/main/java/ch/cyberduck/core/manta/MantaObjectAttributeAdapter.java @@ -15,6 +15,7 @@ package ch.cyberduck.core.manta; * GNU General Public License for more details. */ +import ch.cyberduck.core.DefaultPathAttributes; import ch.cyberduck.core.DefaultWebUrlProvider; import ch.cyberduck.core.DescriptiveUrl; import ch.cyberduck.core.Path; @@ -41,7 +42,7 @@ public final class MantaObjectAttributeAdapter implements AttributesAdapter T getFeature(final Class type) { + if(type == ComparisonService.class) { + return (T) new DefaultComparisonService(new ETagComparisonService(), ComparisonService.disabled); + } + return super.getFeature(type); + } } diff --git a/manta/src/main/java/ch/cyberduck/core/manta/MantaSearchFeature.java b/manta/src/main/java/ch/cyberduck/core/manta/MantaSearchFeature.java index 6df70bebba..fdd1aa6eef 100644 --- a/manta/src/main/java/ch/cyberduck/core/manta/MantaSearchFeature.java +++ b/manta/src/main/java/ch/cyberduck/core/manta/MantaSearchFeature.java @@ -100,7 +100,7 @@ public class MantaSearchFeature implements Search { } @Override - public EnumSet features() { + public EnumSet features(final Path workdir) { return EnumSet.of(Flags.recursive); } diff --git a/manta/src/main/java/ch/cyberduck/core/manta/MantaSession.java b/manta/src/main/java/ch/cyberduck/core/manta/MantaSession.java index 64e3052a10..116ceb5432 100644 --- a/manta/src/main/java/ch/cyberduck/core/manta/MantaSession.java +++ b/manta/src/main/java/ch/cyberduck/core/manta/MantaSession.java @@ -34,7 +34,6 @@ import ch.cyberduck.core.features.Search; import ch.cyberduck.core.features.Touch; import ch.cyberduck.core.features.Write; import ch.cyberduck.core.http.HttpSession; -import ch.cyberduck.core.preferences.HostPreferencesFactory; import ch.cyberduck.core.proxy.ProxyFinder; import ch.cyberduck.core.shared.DefaultPathHomeFeature; import ch.cyberduck.core.shared.DelegatingHomeFeature; @@ -69,7 +68,7 @@ public class MantaSession extends HttpSession { new StandardConfigContext() .setNoAuth(true) .setMantaKeyPath(null) - .setHttpsProtocols(HostPreferencesFactory.get(host).getProperty("connection.ssl.protocols")) + .setHttpsProtocols(preferences.getProperty("connection.ssl.protocols")) .setDisableNativeSignatures(true) .setMantaUser(host.getCredentials().getUsername()) .setMantaURL(String.format("%s://%s", host.getProtocol().getScheme().name(), host.getHostname())) @@ -111,9 +110,14 @@ public class MantaSession extends HttpSession { } @Override - protected void logout() { - if(client != null) { - client.closeWithWarning(); + public void disconnect() throws BackgroundException { + try { + if(client != null) { + client.closeQuietly(); + } + } + finally { + super.disconnect(); } } diff --git a/manta/src/main/java/ch/cyberduck/core/manta/MantaTouchFeature.java b/manta/src/main/java/ch/cyberduck/core/manta/MantaTouchFeature.java index 4beb05b2c8..e1924e7045 100644 --- a/manta/src/main/java/ch/cyberduck/core/manta/MantaTouchFeature.java +++ b/manta/src/main/java/ch/cyberduck/core/manta/MantaTouchFeature.java @@ -21,6 +21,7 @@ import ch.cyberduck.core.Path; import ch.cyberduck.core.exception.AccessDeniedException; import ch.cyberduck.core.exception.BackgroundException; import ch.cyberduck.core.features.Touch; +import ch.cyberduck.core.features.Write; import ch.cyberduck.core.transfer.TransferStatus; import java.io.IOException; @@ -30,7 +31,7 @@ import com.joyent.manta.client.MantaObjectResponse; import com.joyent.manta.exception.MantaClientHttpResponseException; import com.joyent.manta.exception.MantaException; -public class MantaTouchFeature implements Touch { +public class MantaTouchFeature implements Touch { private final MantaSession session; @@ -39,7 +40,7 @@ public class MantaTouchFeature implements Touch { } @Override - public Path touch(final Path file, final TransferStatus status) throws BackgroundException { + public Path touch(final Write writer, final Path file, final TransferStatus status) throws BackgroundException { try { if(!session.getClient().existsAndIsAccessible(file.getParent().getAbsolute())) { session.getClient().putDirectory(file.getParent().getAbsolute()); diff --git a/manta/src/test/java/ch/cyberduck/core/manta/AbstractMantaTest.java b/manta/src/test/java/ch/cyberduck/core/manta/AbstractMantaTest.java index f239ed9c3c..649d15431b 100644 --- a/manta/src/test/java/ch/cyberduck/core/manta/AbstractMantaTest.java +++ b/manta/src/test/java/ch/cyberduck/core/manta/AbstractMantaTest.java @@ -75,7 +75,7 @@ public abstract class AbstractMantaTest extends VaultTest { } final String user = PROPERTIES.get("manta.user"); - final Host host = new Host(profile, hostname, new Credentials(user).withIdentity(file)); + final Host host = new Host(profile, hostname, new Credentials(user).setIdentity(file)); session = new MantaSession(host, new DisabledX509TrustManager(), new DefaultX509KeyManager()); session.open(new DisabledProxyFinder(), new DisabledHostKeyCallback(), new DisabledLoginCallback(), new DisabledCancelCallback()); session.login(new DisabledLoginCallback(), new DisabledCancelCallback()); diff --git a/manta/src/test/java/ch/cyberduck/core/manta/MantaAttributesFinderFeatureTest.java b/manta/src/test/java/ch/cyberduck/core/manta/MantaAttributesFinderFeatureTest.java index 7fd82d8bc7..04431ffdf5 100644 --- a/manta/src/test/java/ch/cyberduck/core/manta/MantaAttributesFinderFeatureTest.java +++ b/manta/src/test/java/ch/cyberduck/core/manta/MantaAttributesFinderFeatureTest.java @@ -47,7 +47,7 @@ public class MantaAttributesFinderFeatureTest extends AbstractMantaTest { @Test public void testFindFile() throws Exception { final Path file = randomFile(); - new MantaTouchFeature(session).touch(file, new TransferStatus().setMime("x-application/cyberduck")); + new MantaTouchFeature(session).touch(new MantaWriteFeature(session), file, new TransferStatus().setMime("x-application/cyberduck")); final PathAttributes attributes = new MantaAttributesFinderFeature(session).find(file); assertNotNull(attributes); assertEquals(-1L, attributes.getCreationDate()); diff --git a/manta/src/test/java/ch/cyberduck/core/manta/MantaDirectoryFeatureTest.java b/manta/src/test/java/ch/cyberduck/core/manta/MantaDirectoryFeatureTest.java index 88f79c7c03..be1c70715d 100644 --- a/manta/src/test/java/ch/cyberduck/core/manta/MantaDirectoryFeatureTest.java +++ b/manta/src/test/java/ch/cyberduck/core/manta/MantaDirectoryFeatureTest.java @@ -39,7 +39,7 @@ public class MantaDirectoryFeatureTest extends AbstractMantaTest { @Test public void testMkdir() throws Exception { - final Path target = new MantaDirectoryFeature(session).mkdir(randomDirectory(), null); + final Path target = new MantaDirectoryFeature(session).mkdir(new MantaWriteFeature(session), randomDirectory(), null); final PathAttributes found = new MantaAttributesFinderFeature(session).find(target); assertNotEquals(Permission.EMPTY, found.getPermission()); new MantaDeleteFeature(session).delete(Collections.singletonList(target), new DisabledLoginCallback(), new Delete.DisabledCallback()); @@ -50,7 +50,7 @@ public class MantaDirectoryFeatureTest extends AbstractMantaTest { final RandomStringService randomStringService = new AlphanumericRandomStringService(); final Path target = new MantaDirectoryFeature(session) .mkdir( - new Path( + new MantaWriteFeature(session), new Path( testPathPrefix, String.format("%s %s", randomStringService.random(), randomStringService.random()), EnumSet.of(Path.Type.directory) diff --git a/manta/src/test/java/ch/cyberduck/core/manta/MantaListServiceTest.java b/manta/src/test/java/ch/cyberduck/core/manta/MantaListServiceTest.java index 02c163cf79..b5bd6bb512 100644 --- a/manta/src/test/java/ch/cyberduck/core/manta/MantaListServiceTest.java +++ b/manta/src/test/java/ch/cyberduck/core/manta/MantaListServiceTest.java @@ -44,7 +44,7 @@ public class MantaListServiceTest extends AbstractMantaTest { @Test public void testListEmptyFolder() throws Exception { - final Path folder = new MantaDirectoryFeature(session).mkdir(new Path( + final Path folder = new MantaDirectoryFeature(session).mkdir(new MantaWriteFeature(session), new Path( testPathPrefix, UUID.randomUUID().toString(), EnumSet.of(Path.Type.directory)), new TransferStatus()); assertTrue(new MantaListService(session).list(folder, new DisabledListProgressListener()).isEmpty()); new MantaDeleteFeature(session).delete(Collections.singletonList(folder), new DisabledLoginCallback(), new Delete.DisabledCallback()); diff --git a/manta/src/test/java/ch/cyberduck/core/manta/MantaMoveFeatureTest.java b/manta/src/test/java/ch/cyberduck/core/manta/MantaMoveFeatureTest.java index ba19b64038..1b0fbe4e6d 100644 --- a/manta/src/test/java/ch/cyberduck/core/manta/MantaMoveFeatureTest.java +++ b/manta/src/test/java/ch/cyberduck/core/manta/MantaMoveFeatureTest.java @@ -46,9 +46,9 @@ public class MantaMoveFeatureTest extends AbstractMantaTest { final Move move = new MantaMoveFeature(session); final Delete delete = new MantaDeleteFeature(session); final AttributesFinder attributesFinder = new MantaAttributesFinderFeature(session); - final Path drive = new MantaDirectoryFeature(session).mkdir(randomDirectory(), new TransferStatus()); + final Path drive = new MantaDirectoryFeature(session).mkdir(new MantaWriteFeature(session), randomDirectory(), new TransferStatus()); final Path file = new Path(drive, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)); - touch.touch(file, new TransferStatus().setMime("x-application/cyberduck")); + touch.touch(new MantaWriteFeature(session), file, new TransferStatus().setMime("x-application/cyberduck")); assertNotNull(attributesFinder.find(file)); Path rename = new Path(drive, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)); assertTrue(move.isSupported(file, Optional.of(rename))); @@ -66,12 +66,12 @@ public class MantaMoveFeatureTest extends AbstractMantaTest { final Move move = new MantaMoveFeature(session); final Delete delete = new MantaDeleteFeature(session); final AttributesFinder attributesFinder = new MantaAttributesFinderFeature(session); - final Path drive = new MantaDirectoryFeature(session).mkdir(randomDirectory(), new TransferStatus()); + final Path drive = new MantaDirectoryFeature(session).mkdir(new MantaWriteFeature(session), randomDirectory(), new TransferStatus()); Path targetDirectory = new Path(drive, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory)); - directory.mkdir(targetDirectory, null); + directory.mkdir(new MantaWriteFeature(session), targetDirectory, null); assertNotNull(attributesFinder.find(targetDirectory)); Path touchedFile = new Path(drive, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)); - touch.touch(touchedFile, new TransferStatus().setMime("x-application/cyberduck")); + touch.touch(new MantaWriteFeature(session), touchedFile, new TransferStatus().setMime("x-application/cyberduck")); assertNotNull(attributesFinder.find(touchedFile)); Path rename = new Path(targetDirectory, touchedFile.getName(), EnumSet.of(Path.Type.file)); assertTrue(move.isSupported(touchedFile, Optional.of(rename))); @@ -89,12 +89,12 @@ public class MantaMoveFeatureTest extends AbstractMantaTest { final Move move = new MantaMoveFeature(session); final Delete delete = new MantaDeleteFeature(session); final AttributesFinder attributesFinder = new MantaAttributesFinderFeature(session); - final Path drive = new MantaDirectoryFeature(session).mkdir(randomDirectory(), new TransferStatus()); + final Path drive = new MantaDirectoryFeature(session).mkdir(new MantaWriteFeature(session), randomDirectory(), new TransferStatus()); Path targetDirectory = new Path(drive, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory)); - directory.mkdir(targetDirectory, null); + directory.mkdir(new MantaWriteFeature(session), targetDirectory, null); assertNotNull(attributesFinder.find(targetDirectory)); Path touchedFile = new Path(drive, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)); - touch.touch(touchedFile, new TransferStatus().setMime("x-application/cyberduck")); + touch.touch(new MantaWriteFeature(session), touchedFile, new TransferStatus().setMime("x-application/cyberduck")); assertNotNull(attributesFinder.find(touchedFile)); Path rename = new Path(targetDirectory, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)); assertTrue(move.isSupported(touchedFile, Optional.of(rename))); diff --git a/manta/src/test/java/ch/cyberduck/core/manta/MantaProtocolTest.java b/manta/src/test/java/ch/cyberduck/core/manta/MantaProtocolTest.java index 3c2b02e88d..6c0c29e122 100644 --- a/manta/src/test/java/ch/cyberduck/core/manta/MantaProtocolTest.java +++ b/manta/src/test/java/ch/cyberduck/core/manta/MantaProtocolTest.java @@ -63,13 +63,13 @@ public class MantaProtocolTest { assertFalse(profile.validate(new Credentials(), new LoginOptions(profile))); assertFalse(profile.validate(new Credentials("u@domain"), new LoginOptions(profile))); assertFalse(profile.validate(new Credentials("u@domain", "p"), new LoginOptions(profile))); - assertFalse(profile.validate(new Credentials("u@domain").withIdentity(new NullLocal("/f") { + assertFalse(profile.validate(new Credentials("u@domain").setIdentity(new NullLocal("/f") { @Override public boolean exists() { return false; } }), new LoginOptions(profile))); - assertTrue(profile.validate(new Credentials("u@domain").withIdentity(new NullLocal("/f") { + assertTrue(profile.validate(new Credentials("u@domain").setIdentity(new NullLocal("/f") { @Override public boolean exists() { return true; diff --git a/manta/src/test/java/ch/cyberduck/core/manta/MantaReadFeatureTest.java b/manta/src/test/java/ch/cyberduck/core/manta/MantaReadFeatureTest.java index 8efb4291b4..75794fc4aa 100644 --- a/manta/src/test/java/ch/cyberduck/core/manta/MantaReadFeatureTest.java +++ b/manta/src/test/java/ch/cyberduck/core/manta/MantaReadFeatureTest.java @@ -50,7 +50,7 @@ public class MantaReadFeatureTest extends AbstractMantaTest { public void testReadNotFound() throws Exception { final TransferStatus status = new TransferStatus(); try { - final Path drive = new MantaDirectoryFeature(session).mkdir(randomDirectory(), new TransferStatus()); + final Path drive = new MantaDirectoryFeature(session).mkdir(new MantaWriteFeature(session), randomDirectory(), new TransferStatus()); new MantaReadFeature(session).read(new Path(drive, "nosuchname", EnumSet.of(Path.Type.file)), status, new DisabledConnectionCallback()); } catch(NotfoundException e) { @@ -61,9 +61,9 @@ public class MantaReadFeatureTest extends AbstractMantaTest { @Test public void testReadInterrupt() throws Exception { - final Path drive = new MantaDirectoryFeature(session).mkdir(randomDirectory(), new TransferStatus()); + final Path drive = new MantaDirectoryFeature(session).mkdir(new MantaWriteFeature(session), randomDirectory(), new TransferStatus()); final Path test = new Path(drive, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)); - new MantaTouchFeature(session).touch(test, new TransferStatus()); + new MantaTouchFeature(session).touch(new MantaWriteFeature(session), test, new TransferStatus()); // Unknown length in status final TransferStatus status = new TransferStatus(); // Read a single byte @@ -83,9 +83,9 @@ public class MantaReadFeatureTest extends AbstractMantaTest { @Test public void testReadRange() throws Exception { - final Path drive = new MantaDirectoryFeature(session).mkdir(randomDirectory(), new TransferStatus()); + final Path drive = new MantaDirectoryFeature(session).mkdir(new MantaWriteFeature(session), randomDirectory(), new TransferStatus()); final Path test = new Path(drive, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)); - new MantaTouchFeature(session).touch(test, new TransferStatus()); + new MantaTouchFeature(session).touch(new MantaWriteFeature(session), test, new TransferStatus()); final Local local = new Local(PROPERTIES.get("java.io.tmpdir"), new AlphanumericRandomStringService().random()); @@ -97,8 +97,8 @@ public class MantaReadFeatureTest extends AbstractMantaTest { assertNotNull(out); IOUtils.write(content, out); out.close(); - new DefaultUploadFeature<>(new MantaWriteFeature(session)).upload( - test, + new DefaultUploadFeature(session).upload( + new MantaWriteFeature(session), test, local, new BandwidthThrottle(BandwidthThrottle.UNLIMITED), new DisabledProgressListener(), new DisabledStreamListener(), @@ -123,9 +123,9 @@ public class MantaReadFeatureTest extends AbstractMantaTest { @Test public void testReadRangeUnknownLength() throws Exception { - final Path drive = new MantaDirectoryFeature(session).mkdir(randomDirectory(), new TransferStatus()); + final Path drive = new MantaDirectoryFeature(session).mkdir(new MantaWriteFeature(session), randomDirectory(), new TransferStatus()); final Path test = new Path(drive, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)); - new MantaTouchFeature(session).touch(test, new TransferStatus()); + new MantaTouchFeature(session).touch(new MantaWriteFeature(session), test, new TransferStatus()); final Local local = new Local(PROPERTIES.get("java.io.tmpdir"), new AlphanumericRandomStringService().random()); final byte[] content = RandomUtils.nextBytes(1000); @@ -133,8 +133,8 @@ public class MantaReadFeatureTest extends AbstractMantaTest { assertNotNull(out); IOUtils.write(content, out); out.close(); - new DefaultUploadFeature<>(new MantaWriteFeature(session)).upload( - test, local, new BandwidthThrottle(BandwidthThrottle.UNLIMITED), new DisabledProgressListener(), new DisabledStreamListener(), + new DefaultUploadFeature(session).upload( + new MantaWriteFeature(session), test, local, new BandwidthThrottle(BandwidthThrottle.UNLIMITED), new DisabledProgressListener(), new DisabledStreamListener(), new TransferStatus().setLength(content.length), new DisabledConnectionCallback()); final TransferStatus status = new TransferStatus(); diff --git a/manta/src/test/java/ch/cyberduck/core/manta/MantaSearchFeatureTest.java b/manta/src/test/java/ch/cyberduck/core/manta/MantaSearchFeatureTest.java index 26a7b9e789..1a7fb8fa29 100644 --- a/manta/src/test/java/ch/cyberduck/core/manta/MantaSearchFeatureTest.java +++ b/manta/src/test/java/ch/cyberduck/core/manta/MantaSearchFeatureTest.java @@ -46,7 +46,7 @@ public class MantaSearchFeatureTest extends AbstractMantaTest { final String emptyDirectoryName = new AlphanumericRandomStringService().random(); final Path emptyDirectory = new Path(testPathPrefix, emptyDirectoryName, EnumSet.of(AbstractPath.Type.directory)); - new MantaDirectoryFeature(session).mkdir(emptyDirectory, null); + new MantaDirectoryFeature(session).mkdir(new MantaWriteFeature(session), emptyDirectory, null); final MantaSearchFeature s = new MantaSearchFeature(session); final AttributedList search = s.search(emptyDirectory, new NullFilter<>(), new DisabledListProgressListener()); @@ -61,8 +61,8 @@ public class MantaSearchFeatureTest extends AbstractMantaTest { final String newDirectoryName = new AlphanumericRandomStringService().random(); final Path newDirectory = new Path(testPathPrefix, newDirectoryName, TYPE_DIRECTORY); final String newFileName = new AlphanumericRandomStringService().random(); - new MantaDirectoryFeature(session).mkdir(newDirectory, null); - new MantaTouchFeature(session).touch(new Path(newDirectory, newFileName, TYPE_FILE), null); + new MantaDirectoryFeature(session).mkdir(new MantaWriteFeature(session), newDirectory, null); + new MantaTouchFeature(session).touch(new MantaWriteFeature(session), new Path(newDirectory, newFileName, TYPE_FILE), null); final MantaSearchFeature s = new MantaSearchFeature(session); final AttributedList search = s.search(newDirectory, new NullFilter<>(), new DisabledListProgressListener()); @@ -82,10 +82,10 @@ public class MantaSearchFeatureTest extends AbstractMantaTest { final Path newDirectory = new Path(testPathPrefix, newDirectoryName, TYPE_DIRECTORY); final Path intermediateDirectory = new Path(newDirectory, intermediateDirectoryName, TYPE_DIRECTORY); - new MantaDirectoryFeature(session).mkdir(newDirectory, null); - new MantaDirectoryFeature(session).mkdir(intermediateDirectory, null); - new MantaTouchFeature(session).touch(new Path(newDirectory, intermediateFileName, TYPE_FILE), null); - new MantaTouchFeature(session).touch(new Path(intermediateDirectory, nestedFileName, TYPE_FILE), null); + new MantaDirectoryFeature(session).mkdir(new MantaWriteFeature(session), newDirectory, null); + new MantaDirectoryFeature(session).mkdir(new MantaWriteFeature(session), intermediateDirectory, null); + new MantaTouchFeature(session).touch(new MantaWriteFeature(session), new Path(newDirectory, intermediateFileName, TYPE_FILE), null); + new MantaTouchFeature(session).touch(new MantaWriteFeature(session), new Path(intermediateDirectory, nestedFileName, TYPE_FILE), null); final MantaSearchFeature s = new MantaSearchFeature(session); final AttributedList search = s.search(newDirectory, new NullFilter<>(), new DisabledListProgressListener()); diff --git a/manta/src/test/java/ch/cyberduck/core/manta/MantaTouchFeatureTest.java b/manta/src/test/java/ch/cyberduck/core/manta/MantaTouchFeatureTest.java index 40caefbfe9..7617c940f2 100644 --- a/manta/src/test/java/ch/cyberduck/core/manta/MantaTouchFeatureTest.java +++ b/manta/src/test/java/ch/cyberduck/core/manta/MantaTouchFeatureTest.java @@ -40,7 +40,7 @@ public class MantaTouchFeatureTest extends AbstractMantaTest { testPathPrefix, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)); - new MantaTouchFeature(session).touch(file, new TransferStatus().setMime("x-application/cyberduck")); + new MantaTouchFeature(session).touch(new MantaWriteFeature(session), file, new TransferStatus().setMime("x-application/cyberduck")); assertNotNull(new MantaAttributesFinderFeature(session).find(file)); new MantaDeleteFeature(session).delete(Collections.singletonList(file), new DisabledLoginCallback(), new Delete.DisabledCallback()); } @@ -52,7 +52,7 @@ public class MantaTouchFeatureTest extends AbstractMantaTest { testPathPrefix, String.format("%s %s", randomStringService.random(), randomStringService.random()), EnumSet.of(Path.Type.file)); - new MantaTouchFeature(session).touch(file, new TransferStatus().setMime("x-application/cyberduck")); + new MantaTouchFeature(session).touch(new MantaWriteFeature(session), file, new TransferStatus().setMime("x-application/cyberduck")); assertNotNull(new MantaAttributesFinderFeature(session).find(file)); new MantaDeleteFeature(session).delete(Collections.singletonList(file), new DisabledLoginCallback(), new Delete.DisabledCallback()); } diff --git a/manta/src/test/java/ch/cyberduck/core/manta/MantaWriteFeatureTest.java b/manta/src/test/java/ch/cyberduck/core/manta/MantaWriteFeatureTest.java index 94f8a496d1..ae550af944 100644 --- a/manta/src/test/java/ch/cyberduck/core/manta/MantaWriteFeatureTest.java +++ b/manta/src/test/java/ch/cyberduck/core/manta/MantaWriteFeatureTest.java @@ -44,7 +44,7 @@ public class MantaWriteFeatureTest extends AbstractMantaTest { @Test public void testWrite() throws Exception { final MantaWriteFeature feature = new MantaWriteFeature(session); - final Path container = new MantaDirectoryFeature(session).mkdir(randomDirectory(), new TransferStatus()); + final Path container = new MantaDirectoryFeature(session).mkdir(new MantaWriteFeature(session), randomDirectory(), new TransferStatus()); final byte[] content = RandomUtils.nextBytes(5 * 1024 * 1024); final TransferStatus status = new TransferStatus(); status.setLength(content.length); @@ -69,7 +69,7 @@ public class MantaWriteFeatureTest extends AbstractMantaTest { public void testWriteUnknownLength() throws Exception { final MantaWriteFeature feature = new MantaWriteFeature(session); final Path container = randomDirectory(); - new MantaDirectoryFeature(session).mkdir(container, new TransferStatus()); + new MantaDirectoryFeature(session).mkdir(new MantaWriteFeature(session), container, new TransferStatus()); final byte[] content = RandomUtils.nextBytes(5 * 1024 * 1024); final TransferStatus status = new TransferStatus(); diff --git a/nextcloud/pom.xml b/nextcloud/pom.xml index 34aa5be894..659ce81bef 100644 --- a/nextcloud/pom.xml +++ b/nextcloud/pom.xml @@ -18,7 +18,7 @@ ch.cyberduck parent - 9.2.0-SNAPSHOT + 9.4.0-SNAPSHOT nextcloud jar diff --git a/nextcloud/src/main/java/ch/cyberduck/core/nextcloud/NextcloudSession.java b/nextcloud/src/main/java/ch/cyberduck/core/nextcloud/NextcloudSession.java index ca8ffe4ac7..120a0462a1 100644 --- a/nextcloud/src/main/java/ch/cyberduck/core/nextcloud/NextcloudSession.java +++ b/nextcloud/src/main/java/ch/cyberduck/core/nextcloud/NextcloudSession.java @@ -95,11 +95,8 @@ public class NextcloudSession extends DAVSession { if(type == ListService.class) { return (T) new NextcloudListService(this); } - if(type == Directory.class) { - return (T) new DAVDirectoryFeature(this, new NextcloudAttributesFinderFeature(this)); - } if(type == Touch.class) { - return (T) new DAVTouchFeature(new NextcloudWriteFeature(this)); + return (T) new DAVTouchFeature(this); } if(type == AttributesFinder.class) { return (T) new NextcloudAttributesFinderFeature(this); @@ -110,7 +107,7 @@ public class NextcloudSession extends DAVSession { } } if(type == Upload.class) { - return (T) new HttpUploadFeature(new NextcloudWriteFeature(this)); + return (T) new HttpUploadFeature(); } if(type == Write.class) { return (T) new NextcloudWriteFeature(this); diff --git a/nextcloud/src/main/java/ch/cyberduck/core/nextcloud/NextcloudTimestampFeature.java b/nextcloud/src/main/java/ch/cyberduck/core/nextcloud/NextcloudTimestampFeature.java index b94e8b80dc..3fe4631087 100644 --- a/nextcloud/src/main/java/ch/cyberduck/core/nextcloud/NextcloudTimestampFeature.java +++ b/nextcloud/src/main/java/ch/cyberduck/core/nextcloud/NextcloudTimestampFeature.java @@ -15,31 +15,11 @@ package ch.cyberduck.core.nextcloud; * GNU General Public License for more details. */ -import ch.cyberduck.core.Path; import ch.cyberduck.core.dav.DAVTimestampFeature; -import ch.cyberduck.core.exception.BackgroundException; -import ch.cyberduck.core.exception.NotfoundException; - -import java.io.IOException; -import java.util.Optional; - -import com.github.sardine.DavResource; public class NextcloudTimestampFeature extends DAVTimestampFeature { - private final NextcloudSession session; - public NextcloudTimestampFeature(final NextcloudSession session) { - super(session); - this.session = session; - } - - @Override - protected DavResource getResource(final Path file) throws BackgroundException, IOException { - final Optional optional = new NextcloudAttributesFinderFeature(session).list(file).stream().findFirst(); - if(!optional.isPresent()) { - throw new NotfoundException(file.getAbsolute()); - } - return optional.get(); + super(session, new NextcloudAttributesFinderFeature(session)); } } diff --git a/nextcloud/src/test/java/ch/cyberduck/core/nextcloud/NextcloudAttributesFinderFeatureTest.java b/nextcloud/src/test/java/ch/cyberduck/core/nextcloud/NextcloudAttributesFinderFeatureTest.java index 7eea80724b..4cbfd9cb48 100644 --- a/nextcloud/src/test/java/ch/cyberduck/core/nextcloud/NextcloudAttributesFinderFeatureTest.java +++ b/nextcloud/src/test/java/ch/cyberduck/core/nextcloud/NextcloudAttributesFinderFeatureTest.java @@ -67,7 +67,7 @@ public class NextcloudAttributesFinderFeatureTest extends AbstractNextcloudTest @Test public void testFindFile() throws Exception { final Checksum checksum = ChecksumComputeFactory.get(HashAlgorithm.sha1).compute(new NullInputStream(0L), new TransferStatus()); - final Path test = new DAVTouchFeature(new NextcloudWriteFeature(session)).touch(new Path(new DefaultHomeFinderService(session).find(), + final Path test = new DAVTouchFeature(session).touch(new NextcloudWriteFeature(session), new Path(new DefaultHomeFinderService(session).find(), new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)), new TransferStatus().setChecksum(checksum)); final NextcloudAttributesFinderFeature f = new NextcloudAttributesFinderFeature(session); final PathAttributes attributes = f.find(test); @@ -94,8 +94,8 @@ public class NextcloudAttributesFinderFeatureTest extends AbstractNextcloudTest @Test public void testFindDirectory() throws Exception { final Path home = new DefaultHomeFinderService(session).find(); - final Path directory = new DAVDirectoryFeature(session, new NextcloudAttributesFinderFeature(session)).mkdir( - new Path(home, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory)), new TransferStatus()); + final Path directory = new DAVDirectoryFeature(session).mkdir( + new NextcloudWriteFeature(session), new Path(home, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory)), new TransferStatus()); final NextcloudAttributesFinderFeature f = new NextcloudAttributesFinderFeature(session); final PathAttributes attributes = f.find(directory); assertNotNull(attributes.getFileId()); @@ -189,7 +189,7 @@ public class NextcloudAttributesFinderFeatureTest extends AbstractNextcloudTest @Test public void testFindLock() throws Exception { - final Path test = new DAVTouchFeature(new NextcloudWriteFeature(session)).touch(new Path(new DefaultHomeFinderService(session).find(), + final Path test = new DAVTouchFeature(session).touch(new NextcloudWriteFeature(session), new Path(new DefaultHomeFinderService(session).find(), new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)), new TransferStatus()); final NextcloudAttributesFinderFeature f = new NextcloudAttributesFinderFeature(session); assertNull(f.find(test).getLockId()); diff --git a/nextcloud/src/test/java/ch/cyberduck/core/nextcloud/NextcloudListServiceTest.java b/nextcloud/src/test/java/ch/cyberduck/core/nextcloud/NextcloudListServiceTest.java index f0de44ef5f..066a3c7ed0 100644 --- a/nextcloud/src/test/java/ch/cyberduck/core/nextcloud/NextcloudListServiceTest.java +++ b/nextcloud/src/test/java/ch/cyberduck/core/nextcloud/NextcloudListServiceTest.java @@ -56,7 +56,7 @@ public class NextcloudListServiceTest extends AbstractNextcloudTest { @Test(expected = NotfoundException.class) public void testListFileException() throws Exception { - final Path test = new DAVTouchFeature(new NextcloudWriteFeature(session)).touch(new Path(new DefaultHomeFinderService(session).find(), + final Path test = new DAVTouchFeature(session).touch(new NextcloudWriteFeature(session), new Path(new DefaultHomeFinderService(session).find(), new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)), new TransferStatus()); try { final AttributedList list = new NextcloudListService(session).list(new Path(test.getAbsolute(), EnumSet.of(Path.Type.directory, Path.Type.volume)), @@ -70,12 +70,12 @@ public class NextcloudListServiceTest extends AbstractNextcloudTest { @Test public void testList() throws Exception { final Path home = new DefaultHomeFinderService(session).find(); - final Path directory = new DAVDirectoryFeature(session, new NextcloudAttributesFinderFeature(session)).mkdir(new Path(home, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory)), new TransferStatus()); + final Path directory = new DAVDirectoryFeature(session).mkdir(new NextcloudWriteFeature(session), new Path(home, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory)), new TransferStatus()); final PathAttributes directoryAttributes = new DAVAttributesFinderFeature(session).find(directory); final String folderEtag = directoryAttributes.getETag(); final long folderTimestamp = directoryAttributes.getModificationDate(); Thread.sleep(1000L); - final Path test = new DAVTouchFeature(new NextcloudWriteFeature(session)).touch(new Path(directory, + final Path test = new DAVTouchFeature(session).touch(new NextcloudWriteFeature(session), new Path(directory, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)), new TransferStatus()); assertEquals(Protocol.DirectoryTimestamp.implicit, session.getHost().getProtocol().getDirectoryTimestamp()); assertNotEquals(folderTimestamp, new DAVAttributesFinderFeature(session).find(directory).getModificationDate()); diff --git a/nextcloud/src/test/java/ch/cyberduck/core/nextcloud/NextcloudShareFeatureTest.java b/nextcloud/src/test/java/ch/cyberduck/core/nextcloud/NextcloudShareFeatureTest.java index da00dfd478..97c893098a 100644 --- a/nextcloud/src/test/java/ch/cyberduck/core/nextcloud/NextcloudShareFeatureTest.java +++ b/nextcloud/src/test/java/ch/cyberduck/core/nextcloud/NextcloudShareFeatureTest.java @@ -54,7 +54,7 @@ public class NextcloudShareFeatureTest extends AbstractNextcloudTest { @Test public void testToDownloadUrlNoPassword() throws Exception { final Path home = new NextcloudHomeFeature(session.getHost()).find(); - final Path file = new DAVTouchFeature(new NextcloudWriteFeature(session)).touch(new Path(home, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)), new TransferStatus()); + final Path file = new DAVTouchFeature(session).touch(new NextcloudWriteFeature(session), new Path(home, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)), new TransferStatus()); final DescriptiveUrl url = new NextcloudShareFeature(session).toDownloadUrl(file, Share.Sharee.world, null, new DisabledPasswordCallback()); assertNotSame(DescriptiveUrl.EMPTY, url); new DAVDeleteFeature(session).delete(Collections.singletonList(file), new DisabledPasswordCallback(), new Delete.DisabledCallback()); @@ -63,7 +63,7 @@ public class NextcloudShareFeatureTest extends AbstractNextcloudTest { @Test public void testToDownloadUrlSharee() throws Exception { final Path home = new NextcloudHomeFeature(session.getHost()).find(); - final Path file = new DAVTouchFeature(new NextcloudWriteFeature(session)).touch(new Path(home, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)), new TransferStatus()); + final Path file = new DAVTouchFeature(session).touch(new NextcloudWriteFeature(session), new Path(home, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)), new TransferStatus()); final NextcloudShareFeature provider = new NextcloudShareFeature(session); final Set sharees = provider.getSharees(Share.Type.download); assertFalse(sharees.isEmpty()); @@ -80,7 +80,7 @@ public class NextcloudShareFeatureTest extends AbstractNextcloudTest { @Test public void testToUploadUrlNoPassword() throws Exception { final Path home = new NextcloudHomeFeature(session.getHost()).find(); - final Path folder = new DAVDirectoryFeature(session, new NextcloudAttributesFinderFeature(session)).mkdir(new Path(home, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory)), new TransferStatus()); + final Path folder = new DAVDirectoryFeature(session).mkdir(new NextcloudWriteFeature(session), new Path(home, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory)), new TransferStatus()); final DescriptiveUrl url = new NextcloudShareFeature(session).toUploadUrl(folder, Share.Sharee.world, null, new DisabledPasswordCallback()); assertNotSame(DescriptiveUrl.EMPTY, url); new DAVDeleteFeature(session).delete(Collections.singletonList(folder), new DisabledPasswordCallback(), new Delete.DisabledCallback()); @@ -89,7 +89,7 @@ public class NextcloudShareFeatureTest extends AbstractNextcloudTest { @Test public void testToDownloadUrlPasswordTooShort() throws Exception { final Path home = new NextcloudHomeFeature(session.getHost()).find(); - final Path folder = new DAVDirectoryFeature(session, new NextcloudAttributesFinderFeature(session)).mkdir(new Path(home, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory)), new TransferStatus()); + final Path folder = new DAVDirectoryFeature(session).mkdir(new NextcloudWriteFeature(session), new Path(home, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory)), new TransferStatus()); try { new NextcloudShareFeature(session).toDownloadUrl(folder, Share.Sharee.world, null, new DisabledPasswordCallback() { @Override @@ -108,7 +108,7 @@ public class NextcloudShareFeatureTest extends AbstractNextcloudTest { @Test public void testToDownloadUrlPassword() throws Exception { final Path home = new NextcloudHomeFeature(session.getHost()).find(); - final Path file = new DAVTouchFeature(new NextcloudWriteFeature(session)).touch(new Path(home, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)), new TransferStatus()); + final Path file = new DAVTouchFeature(session).touch(new NextcloudWriteFeature(session), new Path(home, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)), new TransferStatus()); final DescriptiveUrl url = new NextcloudShareFeature(session).toDownloadUrl(file, Share.Sharee.world, null, new DisabledPasswordCallback() { @Override public Credentials prompt(final Host bookmark, final String title, final String reason, final LoginOptions options) { @@ -146,7 +146,7 @@ public class NextcloudShareFeatureTest extends AbstractNextcloudTest { @Test public void testToUploadUrl() throws Exception { final Path home = new NextcloudHomeFeature(session.getHost()).find(); - final Path folder = new DAVDirectoryFeature(session, new NextcloudAttributesFinderFeature(session)).mkdir(new Path(home, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory)), new TransferStatus()); + final Path folder = new DAVDirectoryFeature(session).mkdir(new NextcloudWriteFeature(session), new Path(home, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory)), new TransferStatus()); final DescriptiveUrl url = new NextcloudShareFeature(session).toUploadUrl(folder, Share.Sharee.world, null, new DisabledPasswordCallback() { @Override public Credentials prompt(final Host bookmark, final String title, final String reason, final LoginOptions options) { @@ -160,7 +160,7 @@ public class NextcloudShareFeatureTest extends AbstractNextcloudTest { @Test public void testToUploadUrlPasswordTooShort() throws Exception { final Path home = new NextcloudHomeFeature(session.getHost()).find(); - final Path folder = new DAVDirectoryFeature(session, new NextcloudAttributesFinderFeature(session)).mkdir(new Path(home, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory)), new TransferStatus()); + final Path folder = new DAVDirectoryFeature(session).mkdir(new NextcloudWriteFeature(session), new Path(home, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory)), new TransferStatus()); try { new NextcloudShareFeature(session).toUploadUrl(folder, Share.Sharee.world, null, new DisabledPasswordCallback() { @Override diff --git a/nextcloud/src/test/java/ch/cyberduck/core/nextcloud/NextcloudVersioningFeatureTest.java b/nextcloud/src/test/java/ch/cyberduck/core/nextcloud/NextcloudVersioningFeatureTest.java index 05d18ca401..9c1eddabaf 100644 --- a/nextcloud/src/test/java/ch/cyberduck/core/nextcloud/NextcloudVersioningFeatureTest.java +++ b/nextcloud/src/test/java/ch/cyberduck/core/nextcloud/NextcloudVersioningFeatureTest.java @@ -47,7 +47,7 @@ public class NextcloudVersioningFeatureTest extends AbstractNextcloudTest { @Test public void testRevert() throws Exception { - final Path directory = new DAVDirectoryFeature(session, new NextcloudAttributesFinderFeature(session)).mkdir(new Path(new DefaultHomeFinderService(session).find(), + final Path directory = new DAVDirectoryFeature(session).mkdir(new NextcloudWriteFeature(session), new Path(new DefaultHomeFinderService(session).find(), new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory, Path.Type.volume)), new TransferStatus()); final Path test = new Path(directory, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)); final TransferStatus status = new TransferStatus(); diff --git a/nio/pom.xml b/nio/pom.xml index 40f61fd65c..593dfa2e62 100644 --- a/nio/pom.xml +++ b/nio/pom.xml @@ -18,7 +18,7 @@ ch.cyberduck parent - 9.2.0-SNAPSHOT + 9.4.0-SNAPSHOT nio jar diff --git a/nio/src/main/java/ch/cyberduck/core/nio/LocalAttributesFinderFeature.java b/nio/src/main/java/ch/cyberduck/core/nio/LocalAttributesFinderFeature.java index b339933f75..75f66ce7ca 100644 --- a/nio/src/main/java/ch/cyberduck/core/nio/LocalAttributesFinderFeature.java +++ b/nio/src/main/java/ch/cyberduck/core/nio/LocalAttributesFinderFeature.java @@ -15,22 +15,22 @@ package ch.cyberduck.core.nio; * GNU General Public License for more details. */ +import ch.cyberduck.core.DefaultPathAttributes; import ch.cyberduck.core.ListProgressListener; +import ch.cyberduck.core.LocalAttributes; +import ch.cyberduck.core.LocalFactory; import ch.cyberduck.core.Path; import ch.cyberduck.core.PathAttributes; import ch.cyberduck.core.Permission; +import ch.cyberduck.core.ProxyPathAttributes; import ch.cyberduck.core.exception.BackgroundException; +import ch.cyberduck.core.features.AttributesAdapter; import ch.cyberduck.core.features.AttributesFinder; -import java.io.IOException; -import java.nio.file.Files; -import java.nio.file.LinkOption; -import java.nio.file.attribute.BasicFileAttributes; -import java.nio.file.attribute.DosFileAttributes; -import java.nio.file.attribute.PosixFileAttributes; -import java.nio.file.attribute.PosixFilePermissions; +import org.apache.commons.lang3.concurrent.ConcurrentException; +import org.apache.commons.lang3.concurrent.LazyInitializer; -public class LocalAttributesFinderFeature implements AttributesFinder { +public class LocalAttributesFinderFeature implements AttributesFinder, AttributesAdapter { private final LocalSession session; @@ -40,45 +40,95 @@ public class LocalAttributesFinderFeature implements AttributesFinder { @Override public PathAttributes find(final Path file, final ListProgressListener listener) throws BackgroundException { - try { - return this.toAttributes(session.toPath(file)); - } - catch(IOException e) { - throw new LocalExceptionMappingService().map("Failure to read attributes of {0}", e, file); - } + return this.toAttributes(session.toPath(file)); } - public PathAttributes toAttributes(final java.nio.file.Path file) throws IOException { - final boolean isPosix = session.isPosixFilesystem(); - final PathAttributes attributes = new PathAttributes(); - final Class provider = isPosix ? PosixFileAttributes.class : DosFileAttributes.class; - final BasicFileAttributes a = Files.readAttributes(file, provider, LinkOption.NOFOLLOW_LINKS); - if(a.isRegularFile()) { - attributes.setSize(a.size()); - } - attributes.setModificationDate(a.lastModifiedTime().toMillis()); - attributes.setCreationDate(a.creationTime().toMillis()); - attributes.setAccessedDate(a.lastAccessTime().toMillis()); - if(isPosix) { - attributes.setOwner(((PosixFileAttributes) a).owner().getName()); - attributes.setGroup(((PosixFileAttributes) a).group().getName()); - attributes.setPermission(new Permission(PosixFilePermissions.toString(((PosixFileAttributes) a).permissions()))); - } - else { - Permission.Action actions = Permission.Action.none; - if(Files.isReadable(file)) { - actions = actions.or(Permission.Action.read); + @Override + public PathAttributes toAttributes(final java.nio.file.Path file) { + return new ProxyLocalAttributes(new LazyInitializer() { + @Override + protected LocalAttributes initialize() { + return LocalFactory.get(file.toString()).attributes(); + } + }); + } + + private static final class ProxyLocalAttributes extends ProxyPathAttributes { + private final LazyInitializer proxy; + + public ProxyLocalAttributes(final LazyInitializer proxy) { + super(new DefaultPathAttributes()); + this.proxy = proxy; + } + + @Override + public long getModificationDate() { + try { + return proxy.get().getModificationDate(); + } + catch(ConcurrentException e) { + throw new RuntimeException(e); + } + } + + @Override + public long getCreationDate() { + try { + return proxy.get().getCreationDate(); + } + catch(ConcurrentException e) { + return -1L; + } + } + + @Override + public long getAccessedDate() { + try { + return proxy.get().getAccessedDate(); + } + catch(ConcurrentException e) { + return -1L; + } + } + + @Override + public long getSize() { + try { + return proxy.get().getSize(); + } + catch(ConcurrentException e) { + return -1L; + } + } + + @Override + public Permission getPermission() { + try { + return proxy.get().getPermission(); + } + catch(ConcurrentException e) { + return Permission.EMPTY; + } + } + + @Override + public String getOwner() { + try { + return proxy.get().getOwner(); + } + catch(ConcurrentException e) { + return null; + } + } + + @Override + public String getGroup() { + try { + return proxy.get().getGroup(); + } + catch(ConcurrentException e) { + return null; } - if(Files.isWritable(file)) { - actions = actions.or(Permission.Action.write); - } - if(Files.isExecutable(file)) { - actions = actions.or(Permission.Action.execute); - } - attributes.setPermission(new Permission( - actions, Permission.Action.none, Permission.Action.none - )); } - return attributes; } } diff --git a/nio/src/main/java/ch/cyberduck/core/nio/LocalDirectoryFeature.java b/nio/src/main/java/ch/cyberduck/core/nio/LocalDirectoryFeature.java index 0fb4d958dc..bd6655efe9 100644 --- a/nio/src/main/java/ch/cyberduck/core/nio/LocalDirectoryFeature.java +++ b/nio/src/main/java/ch/cyberduck/core/nio/LocalDirectoryFeature.java @@ -18,6 +18,7 @@ package ch.cyberduck.core.nio; import ch.cyberduck.core.Path; import ch.cyberduck.core.exception.BackgroundException; import ch.cyberduck.core.features.Directory; +import ch.cyberduck.core.features.Write; import ch.cyberduck.core.transfer.TransferStatus; import java.io.IOException; @@ -32,7 +33,7 @@ public class LocalDirectoryFeature implements Directory { } @Override - public Path mkdir(final Path folder, final TransferStatus status) throws BackgroundException { + public Path mkdir(final Write writer, final Path folder, final TransferStatus status) throws BackgroundException { try { Files.createDirectory(session.toPath(folder)); } diff --git a/nio/src/main/java/ch/cyberduck/core/nio/LocalListService.java b/nio/src/main/java/ch/cyberduck/core/nio/LocalListService.java index 7590bfffd7..c88441be70 100644 --- a/nio/src/main/java/ch/cyberduck/core/nio/LocalListService.java +++ b/nio/src/main/java/ch/cyberduck/core/nio/LocalListService.java @@ -50,28 +50,23 @@ public class LocalListService implements ListService { throw new LocalExceptionMappingService().map("Listing directory {0} failed", new NoSuchFileException(directory.getAbsolute()), directory); } - try (DirectoryStream stream = Files.newDirectoryStream(p)) { + try(DirectoryStream stream = Files.newDirectoryStream(p)) { for(java.nio.file.Path n : stream) { if(null == n.getFileName()) { continue; } - try { - final PathAttributes attributes = feature.toAttributes(n); - final EnumSet type = EnumSet.noneOf(Path.Type.class); - if(Files.isDirectory(n)) { - type.add(Path.Type.directory); - } - else { - type.add(Path.Type.file); - } - final Path file = new Path(directory, n.getFileName().toString(), type, attributes); - if(this.post(n, file)) { - paths.add(file); - listener.chunk(directory, paths); - } + final PathAttributes attributes = feature.toAttributes(n); + final EnumSet type = EnumSet.noneOf(Path.Type.class); + if(Files.isDirectory(n)) { + type.add(Path.Type.directory); } - catch(IOException e) { - log.warn("Failure reading attributes for {}", n); + else { + type.add(Path.Type.file); + } + final Path file = new Path(directory, n.getFileName().toString(), type, attributes); + if(this.post(n, file)) { + paths.add(file); + listener.chunk(directory, paths); } } } diff --git a/nio/src/main/java/ch/cyberduck/core/nio/LocalSession.java b/nio/src/main/java/ch/cyberduck/core/nio/LocalSession.java index 429393628e..c7db0dbf0b 100644 --- a/nio/src/main/java/ch/cyberduck/core/nio/LocalSession.java +++ b/nio/src/main/java/ch/cyberduck/core/nio/LocalSession.java @@ -112,6 +112,7 @@ public class LocalSession extends Session { protected void logout() throws BackgroundException { final Path home = new LocalHomeFinderFeature().find(); LocalFactory.get(this.toPath(home).toString()).release(lock); + super.logout(); } protected boolean isPosixFilesystem() { diff --git a/nio/src/main/java/ch/cyberduck/core/nio/LocalTouchFeature.java b/nio/src/main/java/ch/cyberduck/core/nio/LocalTouchFeature.java index b727e0666c..ac2d024351 100644 --- a/nio/src/main/java/ch/cyberduck/core/nio/LocalTouchFeature.java +++ b/nio/src/main/java/ch/cyberduck/core/nio/LocalTouchFeature.java @@ -17,13 +17,14 @@ package ch.cyberduck.core.nio; import ch.cyberduck.core.Path; import ch.cyberduck.core.exception.BackgroundException; import ch.cyberduck.core.features.Touch; +import ch.cyberduck.core.features.Write; import ch.cyberduck.core.transfer.TransferStatus; import java.io.IOException; import java.nio.file.FileAlreadyExistsException; import java.nio.file.Files; -public class LocalTouchFeature implements Touch { +public class LocalTouchFeature implements Touch { private final LocalSession session; @@ -32,7 +33,7 @@ public class LocalTouchFeature implements Touch { } @Override - public Path touch(final Path file, final TransferStatus status) throws BackgroundException { + public Path touch(final Write writer, final Path file, final TransferStatus status) throws BackgroundException { if(file.isFile()) { try { Files.createFile(session.toPath(file)); diff --git a/nio/src/main/java/ch/cyberduck/core/nio/LocalUploadFeature.java b/nio/src/main/java/ch/cyberduck/core/nio/LocalUploadFeature.java index bfd4c34c0f..94193a9833 100644 --- a/nio/src/main/java/ch/cyberduck/core/nio/LocalUploadFeature.java +++ b/nio/src/main/java/ch/cyberduck/core/nio/LocalUploadFeature.java @@ -16,6 +16,7 @@ package ch.cyberduck.core.nio; */ import ch.cyberduck.core.Path; +import ch.cyberduck.core.Session; import ch.cyberduck.core.exception.BackgroundException; import ch.cyberduck.core.features.Write; import ch.cyberduck.core.shared.DefaultUploadFeature; @@ -23,12 +24,8 @@ import ch.cyberduck.core.transfer.TransferStatus; public class LocalUploadFeature extends DefaultUploadFeature { - public LocalUploadFeature(final LocalSession session) { - super(new LocalWriteFeature(session)); - } - - public LocalUploadFeature(final Write writer) { - super(writer); + public LocalUploadFeature(final Session session) { + super(session); } @Override diff --git a/nio/src/test/java/ch/cyberduck/core/nio/LocalAttributesFinderFeatureTest.java b/nio/src/test/java/ch/cyberduck/core/nio/LocalAttributesFinderFeatureTest.java index d3640cebe8..cba4b76bad 100644 --- a/nio/src/test/java/ch/cyberduck/core/nio/LocalAttributesFinderFeatureTest.java +++ b/nio/src/test/java/ch/cyberduck/core/nio/LocalAttributesFinderFeatureTest.java @@ -35,6 +35,7 @@ import java.util.EnumSet; import java.util.UUID; import static org.junit.Assert.*; +import static org.junit.Assume.assumeTrue; public class LocalAttributesFinderFeatureTest { @@ -51,26 +52,25 @@ public class LocalAttributesFinderFeatureTest { @Test public void testConvert() throws Exception { final LocalSession session = new LocalSession(new Host(new LocalProtocol(), new LocalProtocol().getDefaultHostname())); - if(session.isPosixFilesystem()) { - session.open(new DisabledProxyFinder(), new DisabledHostKeyCallback(), new DisabledLoginCallback(), new DisabledCancelCallback()); - session.login(new DisabledLoginCallback(), new DisabledCancelCallback()); - final Path file = new Path(new LocalHomeFinderFeature().find(), UUID.randomUUID().toString(), EnumSet.of(Path.Type.file)); - new LocalTouchFeature(session).touch(file, new TransferStatus()); - final java.nio.file.Path local = session.toPath(file); - final PosixFileAttributes posixAttributes = Files.readAttributes(local, PosixFileAttributes.class); - final LocalAttributesFinderFeature finder = new LocalAttributesFinderFeature(session); - assertEquals(PosixFilePermissions.toString(posixAttributes.permissions()), finder.find(file).getPermission().getSymbol()); - Files.setPosixFilePermissions(local, PosixFilePermissions.fromString("rw-------")); - assertEquals("rw-------", finder.find(file).getPermission().getSymbol()); - Files.setPosixFilePermissions(local, PosixFilePermissions.fromString("rwxrwxrwx")); - assertEquals("rwxrwxrwx", finder.find(file).getPermission().getSymbol()); - Files.setPosixFilePermissions(local, PosixFilePermissions.fromString("rw-rw----")); - assertEquals("rw-rw----", finder.find(file).getPermission().getSymbol()); - assertEquals(posixAttributes.size(), finder.find(file).getSize()); - assertEquals(posixAttributes.lastModifiedTime().toMillis(), finder.find(file).getModificationDate()); - assertEquals(posixAttributes.creationTime().toMillis(), finder.find(file).getCreationDate()); - assertEquals(posixAttributes.lastAccessTime().toMillis(), finder.find(file).getAccessedDate()); - new LocalDeleteFeature(session).delete(Collections.singletonList(file), new DisabledLoginCallback(), new Delete.DisabledCallback()); - } + assumeTrue(session.isPosixFilesystem()); + session.open(new DisabledProxyFinder(), new DisabledHostKeyCallback(), new DisabledLoginCallback(), new DisabledCancelCallback()); + session.login(new DisabledLoginCallback(), new DisabledCancelCallback()); + final Path file = new Path(new LocalHomeFinderFeature().find(), UUID.randomUUID().toString(), EnumSet.of(Path.Type.file)); + new LocalTouchFeature(session).touch(new LocalWriteFeature(session), file, new TransferStatus()); + final java.nio.file.Path local = session.toPath(file); + final PosixFileAttributes posixAttributes = Files.readAttributes(local, PosixFileAttributes.class); + final LocalAttributesFinderFeature finder = new LocalAttributesFinderFeature(session); + assertEquals(PosixFilePermissions.toString(posixAttributes.permissions()), finder.find(file).getPermission().getSymbol()); + Files.setPosixFilePermissions(local, PosixFilePermissions.fromString("rw-------")); + assertEquals("rw-------", finder.find(file).getPermission().getSymbol()); + Files.setPosixFilePermissions(local, PosixFilePermissions.fromString("rwxrwxrwx")); + assertEquals("rwxrwxrwx", finder.find(file).getPermission().getSymbol()); + Files.setPosixFilePermissions(local, PosixFilePermissions.fromString("rw-rw----")); + assertEquals("rw-rw----", finder.find(file).getPermission().getSymbol()); + assertEquals(posixAttributes.size(), finder.find(file).getSize()); + assertEquals(posixAttributes.lastModifiedTime().toMillis(), finder.find(file).getModificationDate()); + assertEquals(posixAttributes.creationTime().toMillis(), finder.find(file).getCreationDate()); + assertEquals(posixAttributes.lastAccessTime().toMillis(), finder.find(file).getAccessedDate()); + new LocalDeleteFeature(session).delete(Collections.singletonList(file), new DisabledLoginCallback(), new Delete.DisabledCallback()); } } diff --git a/nio/src/test/java/ch/cyberduck/core/nio/LocalDeleteFeatureTest.java b/nio/src/test/java/ch/cyberduck/core/nio/LocalDeleteFeatureTest.java index 251203f9e2..23a1c705e1 100644 --- a/nio/src/test/java/ch/cyberduck/core/nio/LocalDeleteFeatureTest.java +++ b/nio/src/test/java/ch/cyberduck/core/nio/LocalDeleteFeatureTest.java @@ -44,9 +44,9 @@ public class LocalDeleteFeatureTest { session.open(new DisabledProxyFinder(), new DisabledHostKeyCallback(), new DisabledLoginCallback(), new DisabledCancelCallback()); session.login(new DisabledLoginCallback(), new DisabledCancelCallback()); final Path file = new Path(new LocalHomeFinderFeature().find(), new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)); - new LocalTouchFeature(session).touch(file, new TransferStatus()); + new LocalTouchFeature(session).touch(new LocalWriteFeature(session), file, new TransferStatus()); final Path folder = new Path(new LocalHomeFinderFeature().find(), new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory)); - new LocalDirectoryFeature(session).mkdir(folder, new TransferStatus()); + new LocalDirectoryFeature(session).mkdir(new LocalWriteFeature(session), folder, new TransferStatus()); new LocalDeleteFeature(session).delete(new ArrayList<>(Arrays.asList(file, folder)), new DisabledLoginCallback(), new Delete.DisabledCallback()); assertFalse(Files.exists(session.toPath(file))); assertFalse(Files.exists(session.toPath(folder))); @@ -58,9 +58,9 @@ public class LocalDeleteFeatureTest { session.open(new DisabledProxyFinder(), new DisabledHostKeyCallback(), new DisabledLoginCallback(), new DisabledCancelCallback()); session.login(new DisabledLoginCallback(), new DisabledCancelCallback()); final Path folder = new Path(new LocalHomeFinderFeature().find(), new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory)); - new LocalDirectoryFeature(session).mkdir(folder, new TransferStatus()); + new LocalDirectoryFeature(session).mkdir(new LocalWriteFeature(session), folder, new TransferStatus()); final Path file = new Path(folder, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)); - new LocalTouchFeature(session).touch(file, new TransferStatus()); + new LocalTouchFeature(session).touch(new LocalWriteFeature(session), file, new TransferStatus()); final Path symlink = new Path(new LocalHomeFinderFeature().find(), new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)); new LocalSymlinkFeature(session).symlink(symlink, folder.getAbsolute()); new LocalDeleteFeature(session).delete(new ArrayList<>(Collections.singletonList(symlink)), new DisabledLoginCallback(), new Delete.DisabledCallback()); diff --git a/nio/src/test/java/ch/cyberduck/core/nio/LocalDirectoryFeatureTest.java b/nio/src/test/java/ch/cyberduck/core/nio/LocalDirectoryFeatureTest.java index 7767596d5d..154a9e9e9e 100644 --- a/nio/src/test/java/ch/cyberduck/core/nio/LocalDirectoryFeatureTest.java +++ b/nio/src/test/java/ch/cyberduck/core/nio/LocalDirectoryFeatureTest.java @@ -43,9 +43,9 @@ public class LocalDirectoryFeatureTest { session.open(new DisabledProxyFinder(), new DisabledHostKeyCallback(), new DisabledLoginCallback(), new DisabledCancelCallback()); session.login(new DisabledLoginCallback(), new DisabledCancelCallback()); final Path folder = new Path(new LocalHomeFinderFeature().find(), UUID.randomUUID().toString(), EnumSet.of(Path.Type.directory)); - new LocalDirectoryFeature(session).mkdir(folder, new TransferStatus()); + new LocalDirectoryFeature(session).mkdir(new LocalWriteFeature(session), folder, new TransferStatus()); assertTrue(Files.exists(session.toPath(folder))); - assertThrows(ConflictException.class, () -> new LocalDirectoryFeature(session).mkdir(folder, new TransferStatus())); + assertThrows(ConflictException.class, () -> new LocalDirectoryFeature(session).mkdir(new LocalWriteFeature(session), folder, new TransferStatus())); new LocalDeleteFeature(session).delete(Collections.singletonList(folder), new DisabledLoginCallback(), new Delete.DisabledCallback()); assertFalse(Files.exists(session.toPath(folder))); } @@ -56,8 +56,8 @@ public class LocalDirectoryFeatureTest { session.open(new DisabledProxyFinder(), new DisabledHostKeyCallback(), new DisabledLoginCallback(), new DisabledCancelCallback()); session.login(new DisabledLoginCallback(), new DisabledCancelCallback()); final Path folder = new Path(new LocalHomeFinderFeature().find(), UUID.randomUUID().toString(), EnumSet.of(Path.Type.directory)); - new LocalDirectoryFeature(session).mkdir(folder, new TransferStatus()); - new LocalDirectoryFeature(session).mkdir(folder, new TransferStatus()); + new LocalDirectoryFeature(session).mkdir(new LocalWriteFeature(session), folder, new TransferStatus()); + new LocalDirectoryFeature(session).mkdir(new LocalWriteFeature(session), folder, new TransferStatus()); new LocalDeleteFeature(session).delete(Collections.singletonList(folder), new DisabledLoginCallback(), new Delete.DisabledCallback()); } } diff --git a/nio/src/test/java/ch/cyberduck/core/nio/LocalFindFeatureTest.java b/nio/src/test/java/ch/cyberduck/core/nio/LocalFindFeatureTest.java index 9f5aa5119d..eaca377cda 100644 --- a/nio/src/test/java/ch/cyberduck/core/nio/LocalFindFeatureTest.java +++ b/nio/src/test/java/ch/cyberduck/core/nio/LocalFindFeatureTest.java @@ -22,7 +22,6 @@ import ch.cyberduck.core.DisabledLoginCallback; import ch.cyberduck.core.Host; import ch.cyberduck.core.Path; import ch.cyberduck.core.proxy.DisabledProxyFinder; -import ch.cyberduck.core.proxy.Proxy; import ch.cyberduck.core.transfer.TransferStatus; import org.apache.commons.lang3.StringUtils; @@ -66,7 +65,7 @@ public class LocalFindFeatureTest { session.open(new DisabledProxyFinder(), new DisabledHostKeyCallback(), new DisabledLoginCallback(), new DisabledCancelCallback()); session.login(new DisabledLoginCallback(), new DisabledCancelCallback()); final Path home = new LocalHomeFinderFeature().find(); - final Path file = new LocalTouchFeature(session).touch(new Path(home, StringUtils.lowerCase(new AsciiRandomStringService().random()), EnumSet.of(Path.Type.file)), new TransferStatus()); + final Path file = new LocalTouchFeature(session).touch(new LocalWriteFeature(session), new Path(home, StringUtils.lowerCase(new AsciiRandomStringService().random()), EnumSet.of(Path.Type.file)), new TransferStatus()); assertTrue(new LocalFindFeature(session).find(file)); assertFalse(new LocalFindFeature(session).find(new Path(home, StringUtils.capitalize(file.getName()), EnumSet.of(Path.Type.file)))); session.close(); diff --git a/nio/src/test/java/ch/cyberduck/core/nio/LocalListServiceTest.java b/nio/src/test/java/ch/cyberduck/core/nio/LocalListServiceTest.java index e76533c52c..23f744466a 100644 --- a/nio/src/test/java/ch/cyberduck/core/nio/LocalListServiceTest.java +++ b/nio/src/test/java/ch/cyberduck/core/nio/LocalListServiceTest.java @@ -52,8 +52,8 @@ public class LocalListServiceTest { final Path home = new LocalHomeFinderFeature().find(); final Path file = new Path(home, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)); final Path directory = new Path(home, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory)); - new LocalDirectoryFeature(session).mkdir(directory, new TransferStatus()); - new LocalTouchFeature(session).touch(file, new TransferStatus()); + new LocalDirectoryFeature(session).mkdir(new LocalWriteFeature(session), directory, new TransferStatus()); + new LocalTouchFeature(session).touch(new LocalWriteFeature(session), file, new TransferStatus()); final AttributedList list = new LocalListService(session).list(home, new DisabledListProgressListener()); assertTrue(list.contains(file)); assertTrue(list.contains(directory)); @@ -90,7 +90,7 @@ public class LocalListServiceTest { final Path file = new Path(home, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)); final Path symlinkRelative = new Path(home, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file, AbstractPath.Type.symboliclink)); final Path symlinkAbsolute = new Path(home, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file, AbstractPath.Type.symboliclink)); - new LocalTouchFeature(session).touch(file, new TransferStatus()); + new LocalTouchFeature(session).touch(new LocalWriteFeature(session), file, new TransferStatus()); new LocalSymlinkFeature(session).symlink(symlinkRelative, file.getName()); new LocalSymlinkFeature(session).symlink(symlinkAbsolute, file.getAbsolute()); final AttributedList list = new LocalListService(session).list(home, new DisabledListProgressListener()); diff --git a/nio/src/test/java/ch/cyberduck/core/nio/LocalMoveFeatureTest.java b/nio/src/test/java/ch/cyberduck/core/nio/LocalMoveFeatureTest.java index 6f91c8db8f..5b69a0c810 100644 --- a/nio/src/test/java/ch/cyberduck/core/nio/LocalMoveFeatureTest.java +++ b/nio/src/test/java/ch/cyberduck/core/nio/LocalMoveFeatureTest.java @@ -46,7 +46,7 @@ public class LocalMoveFeatureTest { session.login(new DisabledLoginCallback(), new DisabledCancelCallback()); final Path workdir = new LocalHomeFinderFeature().find(); final Path test = new Path(workdir, UUID.randomUUID().toString(), EnumSet.of(Path.Type.file)); - new LocalTouchFeature(session).touch(test, new TransferStatus()); + new LocalTouchFeature(session).touch(new LocalWriteFeature(session), test, new TransferStatus()); final Path target = new Path(workdir, UUID.randomUUID().toString(), EnumSet.of(Path.Type.file)); new LocalMoveFeature(session).move(test, target, new TransferStatus(), new Delete.DisabledCallback(), new DisabledConnectionCallback()); assertFalse(new LocalFindFeature(session).find(test)); @@ -61,7 +61,7 @@ public class LocalMoveFeatureTest { session.login(new DisabledLoginCallback(), new DisabledCancelCallback()); final Path workdir = new LocalHomeFinderFeature().find(); final Path test = new Path(workdir, UUID.randomUUID().toString(), EnumSet.of(Path.Type.file)); - new LocalDirectoryFeature(session).mkdir(test, new TransferStatus()); + new LocalDirectoryFeature(session).mkdir(new LocalWriteFeature(session), test, new TransferStatus()); final Path target = new Path(workdir, UUID.randomUUID().toString(), EnumSet.of(Path.Type.file)); new LocalMoveFeature(session).move(test, target, new TransferStatus(), new Delete.DisabledCallback(), new DisabledConnectionCallback()); assertFalse(new LocalFindFeature(session).find(test)); @@ -76,7 +76,7 @@ public class LocalMoveFeatureTest { session.login(new DisabledLoginCallback(), new DisabledCancelCallback()); final Path workdir = new LocalHomeFinderFeature().find(); final Path test = new Path(workdir, StringUtils.lowerCase(new AsciiRandomStringService().random()), EnumSet.of(Path.Type.file)); - new LocalTouchFeature(session).touch(test, new TransferStatus()); + new LocalTouchFeature(session).touch(new LocalWriteFeature(session), test, new TransferStatus()); final Path target = new Path(workdir, StringUtils.capitalize(test.getName()), EnumSet.of(Path.Type.file)); new LocalMoveFeature(session).move(test, target, new TransferStatus().setExists(true), new Delete.DisabledCallback(), new DisabledConnectionCallback()); assertFalse(new LocalFindFeature(session).find(test)); @@ -91,9 +91,9 @@ public class LocalMoveFeatureTest { session.login(new DisabledLoginCallback(), new DisabledCancelCallback()); final Path workdir = new LocalHomeFinderFeature().find(); final Path test = new Path(workdir, UUID.randomUUID().toString(), EnumSet.of(Path.Type.file)); - new LocalTouchFeature(session).touch(test, new TransferStatus()); + new LocalTouchFeature(session).touch(new LocalWriteFeature(session), test, new TransferStatus()); final Path target = new Path(workdir, UUID.randomUUID().toString(), EnumSet.of(Path.Type.file)); - new LocalTouchFeature(session).touch(target, new TransferStatus()); + new LocalTouchFeature(session).touch(new LocalWriteFeature(session), target, new TransferStatus()); new LocalMoveFeature(session).move(test, target, new TransferStatus().setExists(true), new Delete.DisabledCallback(), new DisabledConnectionCallback()); assertFalse(new LocalFindFeature(session).find(test)); assertTrue(new LocalFindFeature(session).find(target)); diff --git a/nio/src/test/java/ch/cyberduck/core/nio/LocalReadFeatureTest.java b/nio/src/test/java/ch/cyberduck/core/nio/LocalReadFeatureTest.java index a7eb3edd01..94af340204 100644 --- a/nio/src/test/java/ch/cyberduck/core/nio/LocalReadFeatureTest.java +++ b/nio/src/test/java/ch/cyberduck/core/nio/LocalReadFeatureTest.java @@ -60,7 +60,7 @@ public class LocalReadFeatureTest { session.login(new DisabledLoginCallback(), new DisabledCancelCallback()); final Path workdir = new LocalHomeFinderFeature().find(); final Path test = new Path(workdir, UUID.randomUUID().toString(), EnumSet.of(Path.Type.file)); - new LocalTouchFeature(session).touch(test, new TransferStatus()); + new LocalTouchFeature(session).touch(new LocalWriteFeature(session), test, new TransferStatus()); final int length = 39865; final byte[] content = RandomUtils.nextBytes(length); { @@ -91,7 +91,7 @@ public class LocalReadFeatureTest { session.login(new DisabledLoginCallback(), new DisabledCancelCallback()); final Path workdir = new LocalHomeFinderFeature().find(); final Path test = new Path(workdir, UUID.randomUUID().toString(), EnumSet.of(Path.Type.file)); - new LocalTouchFeature(session).touch(test, new TransferStatus()); + new LocalTouchFeature(session).touch(new LocalWriteFeature(session), test, new TransferStatus()); final int length = 1048576; final byte[] content = RandomUtils.nextBytes(length); { diff --git a/nio/src/test/java/ch/cyberduck/core/nio/LocalSymlinkFeatureTest.java b/nio/src/test/java/ch/cyberduck/core/nio/LocalSymlinkFeatureTest.java index a9ed660056..14243a9cb1 100644 --- a/nio/src/test/java/ch/cyberduck/core/nio/LocalSymlinkFeatureTest.java +++ b/nio/src/test/java/ch/cyberduck/core/nio/LocalSymlinkFeatureTest.java @@ -45,7 +45,7 @@ public class LocalSymlinkFeatureTest { session.login(new DisabledLoginCallback(), new DisabledCancelCallback()); final Path workdir = new LocalHomeFinderFeature().find(); final Path target = new Path(workdir, UUID.randomUUID().toString(), EnumSet.of(Path.Type.file)); - new LocalTouchFeature(session).touch(target, new TransferStatus()); + new LocalTouchFeature(session).touch(new LocalWriteFeature(session), target, new TransferStatus()); final Path link = new Path(workdir, UUID.randomUUID().toString(), EnumSet.of(Path.Type.file, AbstractPath.Type.symboliclink)); new LocalSymlinkFeature(session).symlink(link, target.getName()); assertTrue(new LocalFindFeature(session).find(link)); diff --git a/nio/src/test/java/ch/cyberduck/core/nio/LocalTouchFeatureTest.java b/nio/src/test/java/ch/cyberduck/core/nio/LocalTouchFeatureTest.java index 1f520f5e21..927f0f4d3a 100644 --- a/nio/src/test/java/ch/cyberduck/core/nio/LocalTouchFeatureTest.java +++ b/nio/src/test/java/ch/cyberduck/core/nio/LocalTouchFeatureTest.java @@ -46,9 +46,9 @@ public class LocalTouchFeatureTest { session.login(new DisabledLoginCallback(), new DisabledCancelCallback()); final Path workdir = new LocalHomeFinderFeature().find(); final Path test = new Path(workdir, UUID.randomUUID().toString(), EnumSet.of(Path.Type.file)); - new LocalTouchFeature(session).touch(test, new TransferStatus()); + new LocalTouchFeature(session).touch(new LocalWriteFeature(session), test, new TransferStatus()); // Test override - new LocalTouchFeature(session).touch(test, new TransferStatus()); + new LocalTouchFeature(session).touch(new LocalWriteFeature(session), test, new TransferStatus()); assertTrue(new LocalFindFeature(session).find(test)); final AttributedList list = new LocalListService(session).list(workdir, new DisabledListProgressListener()); assertTrue(list.contains(test)); diff --git a/nio/src/test/java/ch/cyberduck/core/nio/LocalUnixPermissionFeatureTest.java b/nio/src/test/java/ch/cyberduck/core/nio/LocalUnixPermissionFeatureTest.java index ab7d88b835..0495cb01f1 100644 --- a/nio/src/test/java/ch/cyberduck/core/nio/LocalUnixPermissionFeatureTest.java +++ b/nio/src/test/java/ch/cyberduck/core/nio/LocalUnixPermissionFeatureTest.java @@ -47,14 +47,14 @@ public class LocalUnixPermissionFeatureTest { final Path workdir = new LocalHomeFinderFeature().find(); { final Path file = new Path(workdir, UUID.randomUUID().toString(), EnumSet.of(Path.Type.file)); - new LocalTouchFeature(session).touch(file, new TransferStatus()); + new LocalTouchFeature(session).touch(new LocalWriteFeature(session), file, new TransferStatus()); new LocalUnixPermissionFeature(session).setUnixPermission(file, new Permission(666)); assertEquals("666", new LocalListService(session).list(workdir, new DisabledListProgressListener()).get(file).attributes().getPermission().getMode()); new LocalDeleteFeature(session).delete(Collections.singletonList(file), new DisabledLoginCallback(), new Delete.DisabledCallback()); } { final Path directory = new Path(workdir, UUID.randomUUID().toString(), EnumSet.of(Path.Type.directory)); - new LocalDirectoryFeature(session).mkdir(directory, new TransferStatus()); + new LocalDirectoryFeature(session).mkdir(new LocalWriteFeature(session), directory, new TransferStatus()); new LocalUnixPermissionFeature(session).setUnixPermission(directory, new Permission(666)); assertEquals("666", new LocalListService(session).list(workdir, new DisabledListProgressListener()).get(directory).attributes().getPermission().getMode()); new LocalDeleteFeature(session).delete(Collections.singletonList(directory), new DisabledLoginCallback(), new Delete.DisabledCallback()); diff --git a/nio/src/test/java/ch/cyberduck/core/nio/LocalUploadFeatureTest.java b/nio/src/test/java/ch/cyberduck/core/nio/LocalUploadFeatureTest.java index 053dcdb062..66d1099421 100644 --- a/nio/src/test/java/ch/cyberduck/core/nio/LocalUploadFeatureTest.java +++ b/nio/src/test/java/ch/cyberduck/core/nio/LocalUploadFeatureTest.java @@ -41,7 +41,7 @@ public class LocalUploadFeatureTest { session.login(new DisabledLoginCallback(), new DisabledCancelCallback()); final Path workdir = new LocalHomeFinderFeature().find(); final Path test = new Path(workdir, UUID.randomUUID().toString(), EnumSet.of(Path.Type.file)); - new LocalTouchFeature(session).touch(test, new TransferStatus()); + new LocalTouchFeature(session).touch(new LocalWriteFeature(session), test, new TransferStatus()); assertTrue(new LocalUploadFeature(session).append(test, new TransferStatus().setExists(true).setLength(0L).setRemote(new LocalAttributesFinderFeature(session).find(test))).append); new LocalDeleteFeature(session).delete(Collections.singletonList(test), new DisabledLoginCallback(), new Delete.DisabledCallback()); } diff --git a/nio/src/test/java/ch/cyberduck/core/nio/LocalWriteFeatureTest.java b/nio/src/test/java/ch/cyberduck/core/nio/LocalWriteFeatureTest.java index 3b603de473..e374403b2d 100644 --- a/nio/src/test/java/ch/cyberduck/core/nio/LocalWriteFeatureTest.java +++ b/nio/src/test/java/ch/cyberduck/core/nio/LocalWriteFeatureTest.java @@ -58,7 +58,7 @@ public class LocalWriteFeatureTest { session.login(new DisabledLoginCallback(), new DisabledCancelCallback()); final Path workdir = new LocalHomeFinderFeature().find(); final Path target = new Path(workdir, UUID.randomUUID().toString(), EnumSet.of(Path.Type.file)); - new LocalTouchFeature(session).touch(target, new TransferStatus()); + new LocalTouchFeature(session).touch(new LocalWriteFeature(session), target, new TransferStatus()); assertTrue(new LocalFindFeature(session).find(target)); final String name = UUID.randomUUID().toString(); final Path symlink = new Path(workdir, name, EnumSet.of(Path.Type.file, AbstractPath.Type.symboliclink)); diff --git a/oauth/pom.xml b/oauth/pom.xml index 4acb6317f9..b80ba9899f 100644 --- a/oauth/pom.xml +++ b/oauth/pom.xml @@ -19,7 +19,7 @@ parent ch.cyberduck - 9.2.0-SNAPSHOT + 9.4.0-SNAPSHOT oauth @@ -50,5 +50,9 @@ com.google.http-client google-http-client-gson + + com.auth0 + java-jwt + diff --git a/oauth/src/main/java/ch/cyberduck/core/oauth/BrowserOAuth2AuthorizationCodeProvider.java b/oauth/src/main/java/ch/cyberduck/core/oauth/BrowserOAuth2AuthorizationCodeProvider.java index 4dbcfb5549..9a966bfcb6 100644 --- a/oauth/src/main/java/ch/cyberduck/core/oauth/BrowserOAuth2AuthorizationCodeProvider.java +++ b/oauth/src/main/java/ch/cyberduck/core/oauth/BrowserOAuth2AuthorizationCodeProvider.java @@ -42,16 +42,10 @@ public class BrowserOAuth2AuthorizationCodeProvider implements OAuth2Authorizati @Override public String prompt(final Host bookmark, final LoginCallback prompt, final String authorizationCodeUrl, final String redirectUri, final String state) throws BackgroundException { log.debug("Evaluate redirect URI {}", redirectUri); - if(StringUtils.endsWith(URIEncoder.decode(redirectUri), ":oauth")) { - return new CustomSchemeHandlerOAuth2AuthorizationCodeProvider().prompt( - bookmark, prompt, authorizationCodeUrl, redirectUri, state); - } - if(StringUtils.contains(redirectUri, "://oauth")) { - return new CustomSchemeHandlerOAuth2AuthorizationCodeProvider().prompt( - bookmark, prompt, authorizationCodeUrl, redirectUri, state); + if(StringUtils.endsWith(URIEncoder.decode(redirectUri), ":oauth") || StringUtils.contains(redirectUri, "://oauth")) { + return new CustomSchemeHandlerOAuth2AuthorizationCodeProvider().prompt(bookmark, prompt, authorizationCodeUrl, redirectUri, state); } log.debug("Prompt for authentication code for state {}", state); - return new PromptOAuth2AuthorizationCodeProvider().prompt( - bookmark, prompt, authorizationCodeUrl, redirectUri, state); + return new PromptOAuth2AuthorizationCodeProvider().prompt(bookmark, prompt, authorizationCodeUrl, redirectUri, state); } } diff --git a/oauth/src/main/java/ch/cyberduck/core/oauth/OAuth2AuthorizationCodeProvider.java b/oauth/src/main/java/ch/cyberduck/core/oauth/OAuth2AuthorizationCodeProvider.java index 1ed3f7df98..a243b74102 100644 --- a/oauth/src/main/java/ch/cyberduck/core/oauth/OAuth2AuthorizationCodeProvider.java +++ b/oauth/src/main/java/ch/cyberduck/core/oauth/OAuth2AuthorizationCodeProvider.java @@ -31,5 +31,5 @@ public interface OAuth2AuthorizationCodeProvider { * @param state Custom state * @return Authentication code */ - String prompt(Host bookmark, final LoginCallback prompt, String authorizationCodeRequestUrl, String redirectUri, final String state) throws BackgroundException; + String prompt(Host bookmark, LoginCallback prompt, String authorizationCodeRequestUrl, String redirectUri, String state) throws BackgroundException; } diff --git a/oauth/src/main/java/ch/cyberduck/core/oauth/OAuth2AuthorizationService.java b/oauth/src/main/java/ch/cyberduck/core/oauth/OAuth2AuthorizationService.java index 657613e9c4..4385b8fa98 100644 --- a/oauth/src/main/java/ch/cyberduck/core/oauth/OAuth2AuthorizationService.java +++ b/oauth/src/main/java/ch/cyberduck/core/oauth/OAuth2AuthorizationService.java @@ -16,8 +16,8 @@ package ch.cyberduck.core.oauth; */ import ch.cyberduck.core.*; +import ch.cyberduck.core.exception.AccessDeniedException; import ch.cyberduck.core.exception.BackgroundException; -import ch.cyberduck.core.exception.LocalAccessDeniedException; import ch.cyberduck.core.exception.LoginCanceledException; import ch.cyberduck.core.exception.LoginFailureException; import ch.cyberduck.core.http.DefaultHttpResponseExceptionMappingService; @@ -35,6 +35,9 @@ import java.util.HashMap; import java.util.List; import java.util.Map; +import com.auth0.jwt.JWT; +import com.auth0.jwt.exceptions.JWTDecodeException; +import com.auth0.jwt.interfaces.DecodedJWT; import com.google.api.client.auth.oauth2.AuthorizationCodeFlow; import com.google.api.client.auth.oauth2.AuthorizationCodeRequestUrl; import com.google.api.client.auth.oauth2.BearerToken; @@ -62,6 +65,9 @@ public class OAuth2AuthorizationService { = new GsonFactory(); private final Host host; + /** + * Static long-lived credentials + */ private final Credentials credentials; private final String tokenServerUrl; private final String authorizationServerUrl; @@ -105,7 +111,7 @@ public class OAuth2AuthorizationService { this.authorizationServerUrl = authorizationServerUrl; this.prompt = prompt; this.clientid = prompt(host, prompt, Profile.OAUTH_CLIENT_ID_KEY, LocaleFactory.localizedString( - Profile.OAUTH_CLIENT_ID_KEY, "Credentials"), clientid); + Profile.OAUTH_CLIENT_ID_KEY, "Credentials"), StringUtils.isBlank(clientid) ? null : clientid); this.clientsecret = prompt(host, prompt, Profile.OAUTH_CLIENT_SECRET_KEY, LocaleFactory.localizedString( Profile.OAUTH_CLIENT_SECRET_KEY, "Credentials"), clientsecret); this.scopes = scopes; @@ -114,29 +120,34 @@ public class OAuth2AuthorizationService { /** * Authorize when cached tokens expired otherwise return + * * @return Tokens retrieved */ - public Credentials validate() throws BackgroundException { - final OAuthTokens saved = credentials.getOauth(); + public OAuthTokens validate(final OAuthTokens saved) throws BackgroundException { if(saved.validate()) { // Found existing tokens if(saved.isExpired()) { - log.warn("Refresh expired access tokens {}", saved); - // Refresh expired access key + log.warn("Refresh expired tokens {}", saved); + // Refresh expired tokens try { - return credentials.withOauth(this.refresh(saved)); + final OAuthTokens refreshed = this.authorizeWithRefreshToken(saved); + log.debug("Refreshed tokens {} for {}", refreshed, host); + return this.save(refreshed); } catch(LoginFailureException e) { log.warn("Failure refreshing tokens from {} for {}", saved, host); - // Continue with new OAuth 2 flow + // Continue with authorization flow } } else { - log.debug("Returned saved OAuth tokens {} for {}", saved, host); - return credentials; + log.debug("Returned saved tokens {} for {}", saved, host); + return saved; } } - return credentials.withOauth(this.authorize()); + log.warn("Missing tokens {} for {}", saved, host); + final OAuthTokens tokens = this.authorize(); + log.debug("Retrieved tokens {} for {}", tokens, host); + return tokens; } /** @@ -144,9 +155,31 @@ public class OAuth2AuthorizationService { * * @return Same tokens saved */ - public OAuthTokens save(final OAuthTokens tokens) throws LocalAccessDeniedException { + public OAuthTokens save(final OAuthTokens tokens) throws AccessDeniedException { log.debug("Save new tokens {} for {}", tokens, host); - credentials.withOauth(tokens).withSaved(new LoginOptions().save); + credentials.setOauth(tokens).setSaved(new LoginOptions().save); + switch(flowType) { + case PasswordGrant: + // Skip modifying username used for password grant + break; + default: + try { + final DecodedJWT jwt = JWT.decode(tokens.getIdToken()); + // Standard claims + for(String claim : new String[]{"preferred_username", "email", "name", "nickname", "sub"}) { + final String value = jwt.getClaim(claim).asString(); + if(StringUtils.isNotBlank(value)) { + log.debug("Set username to {} from claim {}", value, claim); + credentials.setUsername(value); + break; + } + } + } + catch(JWTDecodeException e) { + log.warn("Failure {} decoding JWT {}", e, tokens.getIdToken()); + } + break; + } if(credentials.isSaved()) { store.save(host); } @@ -163,7 +196,7 @@ public class OAuth2AuthorizationService { // Save access token, refresh token and id token switch(flowType) { case AuthorizationCode: - response = this.authorizeWithCode(host, prompt); + response = this.authorizeWithCode(prompt); break; case PasswordGrant: response = this.authorizeWithPassword(credentials); @@ -177,9 +210,10 @@ public class OAuth2AuthorizationService { System.currentTimeMillis() + response.getExpiresInSeconds() * 1000, response.getIdToken()); } - private IdTokenResponse authorizeWithCode(final Host bookmark, final LoginCallback prompt) throws BackgroundException { - if(HostPreferencesFactory.get(bookmark).getBoolean("oauth.browser.open.warn")) { - prompt.warn(bookmark, + private IdTokenResponse authorizeWithCode(final LoginCallback prompt) throws BackgroundException { + log.debug("Request tokens with code"); + if(HostPreferencesFactory.get(host).getBoolean("oauth.browser.open.warn")) { + prompt.warn(host, LocaleFactory.localizedString("Provide additional login credentials", "Credentials"), new StringAppender() .append(LocaleFactory.localizedString("Open web browser to authenticate and obtain an authorization code", "Credentials")) @@ -213,7 +247,7 @@ public class OAuth2AuthorizationService { final String authorizationCodeUrl = authorizationCodeUrlBuilder.build(); log.debug("Open browser with URL {}", authorizationCodeUrl); final String authorizationCode = OAuth2AuthorizationCodeProviderFactory.get().prompt( - bookmark, prompt, authorizationCodeUrl, redirectUri, state); + host, prompt, authorizationCodeUrl, redirectUri, state); if(StringUtils.isBlank(authorizationCode)) { throw new LoginCanceledException(); } @@ -238,7 +272,7 @@ public class OAuth2AuthorizationService { private IdTokenResponse authorizeWithPassword(final Credentials credentials) throws BackgroundException { try { - log.debug("Request tokens for user {}", credentials.getUsername()); + log.debug("Request tokens with password {}", credentials); final PasswordTokenRequest request = new PasswordTokenRequest(transport, json, new GenericUrl(tokenServerUrl), credentials.getUsername(), credentials.getPassword() ) @@ -262,10 +296,10 @@ public class OAuth2AuthorizationService { } } - public OAuthTokens refresh(final OAuthTokens tokens) throws BackgroundException { + public OAuthTokens authorizeWithRefreshToken(final OAuthTokens tokens) throws BackgroundException { if(StringUtils.isBlank(tokens.getRefreshToken())) { log.warn("Missing refresh token in {}", tokens); - return tokens; + return this.authorize(); } log.debug("Refresh expired tokens {}", tokens); try { @@ -387,7 +421,8 @@ public class OAuth2AuthorizationService { if(null == value) { final Credentials input = prompt.prompt(bookmark, message, LocaleFactory.localizedString("Provide additional login credentials", "Credentials"), - new LoginOptions().icon(bookmark.getProtocol().disk())); + new LoginOptions().icon(bookmark.getProtocol().disk()) + .passwordPlaceholder(message).password(false)); if(input.isSaved()) { HostPreferencesFactory.get(bookmark).setProperty(property, input.getPassword()); } diff --git a/oauth/src/main/java/ch/cyberduck/core/oauth/OAuth2ErrorResponseInterceptor.java b/oauth/src/main/java/ch/cyberduck/core/oauth/OAuth2ErrorResponseInterceptor.java index 1c39105409..aefe5789c3 100644 --- a/oauth/src/main/java/ch/cyberduck/core/oauth/OAuth2ErrorResponseInterceptor.java +++ b/oauth/src/main/java/ch/cyberduck/core/oauth/OAuth2ErrorResponseInterceptor.java @@ -15,6 +15,7 @@ package ch.cyberduck.core.oauth; * GNU General Public License for more details. */ +import ch.cyberduck.core.Credentials; import ch.cyberduck.core.Host; import ch.cyberduck.core.exception.BackgroundException; import ch.cyberduck.core.http.DisabledServiceUnavailableRetryStrategy; @@ -28,9 +29,11 @@ import org.apache.logging.log4j.Logger; public class OAuth2ErrorResponseInterceptor extends DisabledServiceUnavailableRetryStrategy { private static final Logger log = LogManager.getLogger(OAuth2ErrorResponseInterceptor.class); + private final Credentials credentials; private final OAuth2RequestInterceptor service; public OAuth2ErrorResponseInterceptor(final Host host, final OAuth2RequestInterceptor service) { + this.credentials = host.getCredentials(); this.service = service; } @@ -40,7 +43,7 @@ public class OAuth2ErrorResponseInterceptor extends DisabledServiceUnavailableRe case HttpStatus.SC_UNAUTHORIZED: try { log.warn("Attempt to refresh OAuth tokens for failure {}", response); - service.save(service.refresh()); + service.save(service.authorizeWithRefreshToken(credentials.getOauth())); // Try again return true; } diff --git a/oauth/src/main/java/ch/cyberduck/core/oauth/OAuth2RequestInterceptor.java b/oauth/src/main/java/ch/cyberduck/core/oauth/OAuth2RequestInterceptor.java index 2573f60a93..03b099c863 100644 --- a/oauth/src/main/java/ch/cyberduck/core/oauth/OAuth2RequestInterceptor.java +++ b/oauth/src/main/java/ch/cyberduck/core/oauth/OAuth2RequestInterceptor.java @@ -20,10 +20,10 @@ import ch.cyberduck.core.Host; import ch.cyberduck.core.HostUrlProvider; import ch.cyberduck.core.LoginCallback; import ch.cyberduck.core.OAuthTokens; +import ch.cyberduck.core.Profile; import ch.cyberduck.core.Scheme; import ch.cyberduck.core.exception.BackgroundException; import ch.cyberduck.core.exception.LoginCanceledException; -import ch.cyberduck.core.exception.LoginFailureException; import org.apache.commons.lang3.StringUtils; import org.apache.http.HttpException; @@ -46,11 +46,7 @@ public class OAuth2RequestInterceptor extends OAuth2AuthorizationService impleme private static final Logger log = LogManager.getLogger(OAuth2RequestInterceptor.class); private final ReentrantLock lock = new ReentrantLock(); - - /** - * Currently valid tokens - */ - private OAuthTokens tokens = OAuthTokens.EMPTY; + private final Credentials credentials; public OAuth2RequestInterceptor(final HttpClient client, final Host host, final LoginCallback prompt) throws LoginCanceledException { this(client, host, @@ -58,8 +54,8 @@ public class OAuth2RequestInterceptor extends OAuth2AuthorizationService impleme host.getProtocol().getScheme(), host.getPort(), null, host.getHostname(), host.getProtocol().getOAuthTokenUrl()), Scheme.isURL(host.getProtocol().getOAuthAuthorizationUrl()) ? host.getProtocol().getOAuthAuthorizationUrl() : new HostUrlProvider().withUsername(false).withPath(true).get( host.getProtocol().getScheme(), host.getPort(), null, host.getHostname(), host.getProtocol().getOAuthAuthorizationUrl()), - host.getProtocol().getOAuthClientId(), - host.getProtocol().getOAuthClientSecret(), + null == host.getProperty(Profile.OAUTH_CLIENT_ID_KEY) ? host.getProtocol().getOAuthClientId() : host.getProperty(Profile.OAUTH_CLIENT_ID_KEY), + null == host.getProperty(Profile.OAUTH_CLIENT_SECRET_KEY) ? host.getProtocol().getOAuthClientSecret() : host.getProperty(Profile.OAUTH_CLIENT_SECRET_KEY), host.getProtocol().getOAuthScopes(), host.getProtocol().isOAuthPKCE(), prompt); } @@ -67,69 +63,17 @@ public class OAuth2RequestInterceptor extends OAuth2AuthorizationService impleme public OAuth2RequestInterceptor(final HttpClient client, final Host host, final String tokenServerUrl, final String authorizationServerUrl, final String clientid, final String clientsecret, final List scopes, final boolean pkce, final LoginCallback prompt) throws LoginCanceledException { super(client, host, tokenServerUrl, authorizationServerUrl, clientid, clientsecret, scopes, pkce, prompt); - } - - @Override - public Credentials validate() throws BackgroundException { - final Credentials credentials = super.validate(); - tokens = credentials.getOauth(); - return credentials; - } - - @Override - public OAuthTokens authorize() throws BackgroundException { - lock.lock(); - try { - return tokens = super.authorize(); - } - finally { - lock.unlock(); - } - } - - /** - * Refresh with cached refresh token - */ - public OAuthTokens refresh() throws BackgroundException { - lock.lock(); - try { - return tokens = this.refresh(tokens); - } - finally { - lock.unlock(); - } - } - - /** - * @param previous Refresh token - */ - @Override - public OAuthTokens refresh(final OAuthTokens previous) throws BackgroundException { - lock.lock(); - try { - return tokens = super.refresh(previous); - } - catch(LoginFailureException e) { - log.warn("Failure {} refreshing OAuth tokens", e.getMessage()); - return tokens = this.authorize(); - } - finally { - lock.unlock(); - } + this.credentials = host.getCredentials(); } @Override public void process(final HttpRequest request, final HttpContext context) throws HttpException, IOException { lock.lock(); try { + OAuthTokens tokens = credentials.getOauth(); if(tokens.isExpired()) { try { - final OAuthTokens previous = tokens; - final OAuthTokens refreshed = this.refresh(tokens); - // Skip saving tokens when not changed - if(!refreshed.equals(previous)) { - this.save(refreshed); - } + tokens = this.save(this.authorizeWithRefreshToken(tokens)); } catch(BackgroundException e) { log.warn("Failure {} refreshing OAuth tokens {}", e, tokens); @@ -170,14 +114,4 @@ public class OAuth2RequestInterceptor extends OAuth2AuthorizationService impleme super.withParameter(key, value); return this; } - - public OAuthTokens getTokens() { - lock.lock(); - try { - return tokens; - } - finally { - lock.unlock(); - } - } } diff --git a/onedrive/pom.xml b/onedrive/pom.xml index 4051c543c5..39a26f4c44 100644 --- a/onedrive/pom.xml +++ b/onedrive/pom.xml @@ -19,11 +19,11 @@ parent ch.cyberduck - 9.2.0-SNAPSHOT + 9.4.0-SNAPSHOT onedrive - 3.2.3 + 3.3.0 diff --git a/onedrive/src/main/java/ch/cyberduck/core/onedrive/AbstractDriveListService.java b/onedrive/src/main/java/ch/cyberduck/core/onedrive/AbstractDriveListService.java index cea7ed142a..c5e7e5f46a 100644 --- a/onedrive/src/main/java/ch/cyberduck/core/onedrive/AbstractDriveListService.java +++ b/onedrive/src/main/java/ch/cyberduck/core/onedrive/AbstractDriveListService.java @@ -15,6 +15,7 @@ package ch.cyberduck.core.onedrive; * GNU General Public License for more details. */ +import ch.cyberduck.core.DefaultPathAttributes; import ch.cyberduck.core.Path; import ch.cyberduck.core.PathAttributes; import ch.cyberduck.core.onedrive.features.GraphFileIdProvider; @@ -34,7 +35,7 @@ public abstract class AbstractDriveListService extends AbstractListService implements ListService { log.debug("Filtering metadata {} in {}", metadata, directory); continue; } - children.add(toPath(metadata, directory)); + final Path path = toPath(metadata, directory); + if (StringUtils.isBlank(path.attributes().getFileId())) { + log.warn("Filtering inaccessible {} for {}", path, metadata); + continue; + } + children.add(path); } } catch(OneDriveRuntimeException e) { // this catches iterator.hasNext in iterate() diff --git a/onedrive/src/main/java/ch/cyberduck/core/onedrive/AbstractSharepointSession.java b/onedrive/src/main/java/ch/cyberduck/core/onedrive/AbstractSharepointSession.java index 35a623afe3..0d502eb0cd 100644 --- a/onedrive/src/main/java/ch/cyberduck/core/onedrive/AbstractSharepointSession.java +++ b/onedrive/src/main/java/ch/cyberduck/core/onedrive/AbstractSharepointSession.java @@ -25,13 +25,13 @@ import ch.cyberduck.core.exception.NotfoundException; import ch.cyberduck.core.features.Home; import ch.cyberduck.core.features.Lock; import ch.cyberduck.core.onedrive.features.GraphLockFeature; -import ch.cyberduck.core.preferences.HostPreferencesFactory; import ch.cyberduck.core.ssl.X509KeyManager; import ch.cyberduck.core.ssl.X509TrustManager; import org.apache.commons.lang3.StringUtils; import org.nuxeo.onedrive.client.ODataQuery; import org.nuxeo.onedrive.client.OneDriveAPIException; +import org.nuxeo.onedrive.client.OneDriveRuntimeException; import org.nuxeo.onedrive.client.types.Drive; import org.nuxeo.onedrive.client.types.DriveItem; import org.nuxeo.onedrive.client.types.GroupItem; @@ -116,11 +116,14 @@ public abstract class AbstractSharepointSession extends GraphSession { return (DriveItem) remoteMetadata.getItem(); } } - catch(OneDriveAPIException oneDriveAPIException) { - throw new GraphExceptionMappingService(fileid).map(oneDriveAPIException); + catch(OneDriveAPIException e) { + throw new GraphExceptionMappingService(fileid).map(e); } - catch(IOException ioException) { - throw new DefaultIOExceptionMappingService().map(ioException); + catch(IOException e) { + throw new DefaultIOExceptionMappingService().map(e); + } + catch(OneDriveRuntimeException e) { + throw new GraphExceptionMappingService(fileid).map(e.getCause()); } } return ownItem; @@ -129,7 +132,7 @@ public abstract class AbstractSharepointSession extends GraphSession { @Override public T _getFeature(final Class type) { if(type == Lock.class) { - if(HostPreferencesFactory.get(host).getBoolean("sharepoint.lock.enable")) { + if(preferences.getBoolean("sharepoint.lock.enable")) { return (T) new GraphLockFeature(this, fileid); } } diff --git a/onedrive/src/main/java/ch/cyberduck/core/onedrive/GraphItemListService.java b/onedrive/src/main/java/ch/cyberduck/core/onedrive/GraphItemListService.java index 469a224266..998b8d3ace 100644 --- a/onedrive/src/main/java/ch/cyberduck/core/onedrive/GraphItemListService.java +++ b/onedrive/src/main/java/ch/cyberduck/core/onedrive/GraphItemListService.java @@ -44,6 +44,6 @@ public class GraphItemListService extends AbstractItemListService { log.debug("Return files for folder {}", folder); // getQuery(null): return new ODataQuery with default set of parameters // require listing Publication/VersionId - return Files.getFiles(folder, session.getQuery(null).top(HostPreferencesFactory.get(session.getHost()).getInteger("onedrive.listing.chunksize"))); + return Files.getFiles(folder, session.select(null).top(HostPreferencesFactory.get(session.getHost()).getInteger("onedrive.listing.chunksize"))); } } diff --git a/onedrive/src/main/java/ch/cyberduck/core/onedrive/GraphProtocol.java b/onedrive/src/main/java/ch/cyberduck/core/onedrive/GraphProtocol.java index fe281f6fbd..565d2c0213 100644 --- a/onedrive/src/main/java/ch/cyberduck/core/onedrive/GraphProtocol.java +++ b/onedrive/src/main/java/ch/cyberduck/core/onedrive/GraphProtocol.java @@ -20,6 +20,7 @@ import ch.cyberduck.core.LocaleFactory; import ch.cyberduck.core.Scheme; import ch.cyberduck.core.synchronization.ComparisonService; import ch.cyberduck.core.synchronization.DefaultComparisonService; +import ch.cyberduck.core.synchronization.VersionIdComparisonService; public abstract class GraphProtocol extends AbstractProtocol { @Override @@ -72,7 +73,7 @@ public abstract class GraphProtocol extends AbstractProtocol { @SuppressWarnings("unchecked") public T getFeature(final Class type) { if(type == ComparisonService.class) { - return (T) new DefaultComparisonService(DefaultComparisonService.forFiles(this), ComparisonService.disabled); + return (T) new DefaultComparisonService(new VersionIdComparisonService(), ComparisonService.disabled); } return super.getFeature(type); } diff --git a/onedrive/src/main/java/ch/cyberduck/core/onedrive/GraphSession.java b/onedrive/src/main/java/ch/cyberduck/core/onedrive/GraphSession.java index 88597b3435..5823e531ec 100644 --- a/onedrive/src/main/java/ch/cyberduck/core/onedrive/GraphSession.java +++ b/onedrive/src/main/java/ch/cyberduck/core/onedrive/GraphSession.java @@ -28,12 +28,10 @@ import ch.cyberduck.core.exception.ConnectionCanceledException; import ch.cyberduck.core.exception.HostParserException; 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.oauth.OAuth2ErrorResponseInterceptor; import ch.cyberduck.core.oauth.OAuth2RequestInterceptor; import ch.cyberduck.core.onedrive.features.*; -import ch.cyberduck.core.preferences.HostPreferencesFactory; import ch.cyberduck.core.proxy.ProxyFinder; import ch.cyberduck.core.shared.BufferWriteFeature; import ch.cyberduck.core.shared.DefaultHomeFinderService; @@ -44,13 +42,16 @@ import ch.cyberduck.core.threading.CancelCallback; import org.apache.http.HttpException; import org.apache.http.HttpHeaders; import org.apache.http.HttpRequest; +import org.apache.http.HttpRequestInterceptor; import org.apache.http.impl.client.HttpClientBuilder; +import org.apache.http.message.BasicHeader; import org.apache.http.protocol.HttpContext; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; import org.nuxeo.onedrive.client.ODataQuery; import org.nuxeo.onedrive.client.OneDriveAPI; import org.nuxeo.onedrive.client.OneDriveAPIException; +import org.nuxeo.onedrive.client.OneDriveRuntimeException; import org.nuxeo.onedrive.client.RequestExecutor; import org.nuxeo.onedrive.client.RequestHeader; import org.nuxeo.onedrive.client.Users; @@ -80,7 +81,7 @@ public abstract class GraphSession extends HttpSession { public abstract String getFileId(final DriveItem.Metadata metadata); - public ODataQuery getQuery(ODataQuery query) { + public ODataQuery select(ODataQuery query) { if(query == null) { query = new ODataQuery(); } @@ -96,6 +97,7 @@ public abstract class GraphSession extends HttpSession { // Drive Item properties DriveItem.Property.File, /*Usage: Determines File */ + DriveItem.Property.CTag, /*Usage: File Checksum/Version Comparison */ DriveItem.Property.FileSystemInfo, /*Usage: FileSystemInfo like Created and Modified */ DriveItem.Property.Folder, /*Usage: Determines Folder */ DriveItem.Property.Package, /*Usage: Determines OneNote */ @@ -110,8 +112,8 @@ public abstract class GraphSession extends HttpSession { return this.getItem(currentPath, true); } - public DriveItem.Metadata getMetadata(final DriveItem item, ODataQuery query) throws IOException { - return item.getMetadata(getQuery(query)); + public DriveItem.Metadata getMetadata(final DriveItem item, final ODataQuery query) throws IOException { + return item.getMetadata(this.select(query)); } public abstract DriveItem getItem(final Path file, final boolean resolveLastItem) throws BackgroundException; @@ -140,7 +142,7 @@ public abstract class GraphSession extends HttpSession { * * @param file The file path for which the container details should be retrieved. * @return An instance of {@code ContainerItem} representing the container details of the specified file path. - * Returns {@code ContainerItem.EMPTY} if no container details are found. + * Returns {@code ContainerItem.EMPTY} if no container details are found. */ public abstract ContainerItem getContainer(Path file); @@ -166,8 +168,10 @@ public abstract class GraphSession extends HttpSession { }.withRedirectUri(host.getProtocol().getOAuthRedirectUrl()) .withParameter("prompt", "select_account"); configuration.addInterceptorLast(authorizationService); + configuration.addInterceptorLast((HttpRequestInterceptor) (request, context) -> request + .addHeader(new BasicHeader("Prefer", "Include-Feature=AddToOneDrive"))); configuration.setServiceUnavailableRetryStrategy(new CustomServiceUnavailableRetryStrategy(host, - new ExecutionCountServiceUnavailableRetryStrategy(new OAuth2ErrorResponseInterceptor(host, authorizationService)))); + new OAuth2ErrorResponseInterceptor(host, authorizationService))); final RequestExecutor executor = new GraphCommonsHttpRequestExecutor(configuration.build()) { @Override public void addAuthorizationHeader(final Set headers) { @@ -212,7 +216,8 @@ public abstract class GraphSession extends HttpSession { @Override public void login(final LoginCallback prompt, final CancelCallback cancel) throws BackgroundException { - final Credentials credentials = authorizationService.validate(); + final Credentials credentials = host.getCredentials(); + credentials.setOauth(authorizationService.validate(credentials.getOauth())); try { user = Users.get(User.getCurrent(client), new ODataQuery().select(User.Select.values())); final String account = user.getUserPrincipalName(); @@ -225,18 +230,24 @@ public abstract class GraphSession extends HttpSession { catch(IOException e) { throw new DefaultIOExceptionMappingService().map(e); } + catch(OneDriveRuntimeException e) { + throw new GraphExceptionMappingService(fileid).map(e.getCause()); + } } @Override - protected void logout() throws BackgroundException { + public void disconnect() throws BackgroundException { try { - client.getExecutor().close(); + fileid.clear(); + if(client != null) { + client.getExecutor().close(); + } } catch(IOException e) { throw new DefaultIOExceptionMappingService().map(e); } finally { - fileid.clear(); + super.disconnect(); } } @@ -277,7 +288,7 @@ public abstract class GraphSession extends HttpSession { return (T) new GraphFindFeature(this, fileid); } if(type == Timestamp.class) { - if(HostPreferencesFactory.get(host).getBoolean("onedrive.timestamp.enable")) { + if(preferences.getBoolean("onedrive.timestamp.enable")) { return (T) new GraphTimestampFeature(this, fileid); } } @@ -288,7 +299,7 @@ public abstract class GraphSession extends HttpSession { return (T) new GraphUrlProvider(); } if(type == Share.class) { - return (T) new GraphSharedLinkFeature(this); + return (T) new GraphSharedLinkFeature(this, fileid); } if(type == Versioning.class) { return (T) new GraphVersioningFeature(this, fileid); diff --git a/onedrive/src/main/java/ch/cyberduck/core/onedrive/OneDriveSession.java b/onedrive/src/main/java/ch/cyberduck/core/onedrive/OneDriveSession.java index 72e5afca95..35dfa65bad 100644 --- a/onedrive/src/main/java/ch/cyberduck/core/onedrive/OneDriveSession.java +++ b/onedrive/src/main/java/ch/cyberduck/core/onedrive/OneDriveSession.java @@ -67,9 +67,11 @@ public class OneDriveSession extends GraphSession { remoteParent.getDriveId(), remoteMetadata.getId()); } } - else { + else if(parent != null) { return String.join(String.valueOf(Path.DELIMITER), parent.getDriveId(), metadata.getId()); } + + return null; } @Override diff --git a/onedrive/src/main/java/ch/cyberduck/core/onedrive/SharepointListService.java b/onedrive/src/main/java/ch/cyberduck/core/onedrive/SharepointListService.java index 712adb11f0..d902733be9 100644 --- a/onedrive/src/main/java/ch/cyberduck/core/onedrive/SharepointListService.java +++ b/onedrive/src/main/java/ch/cyberduck/core/onedrive/SharepointListService.java @@ -16,6 +16,7 @@ package ch.cyberduck.core.onedrive; */ 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; @@ -59,10 +60,10 @@ public class SharepointListService extends AbstractSharepointListService { final Site site = Site.byId(session.getClient(), "root"); final Site.Metadata metadata = site.getMetadata(null); // query: null: Default return set. final EnumSet type = EnumSet.copyOf(DEFAULT_NAME.getType()); - final Path path = new Path(directory, DEFAULT_NAME.getName(), type, new PathAttributes().setFileId(metadata.getId())); + final Path path = new Path(directory, DEFAULT_NAME.getName(), type, new DefaultPathAttributes().setFileId(metadata.getId())); path.setSymlinkTarget( new Path(SITES_NAME, metadata.getSiteCollection().getHostname(), SITES_NAME.getType(), - new PathAttributes().setFileId(metadata.getId()))); + new DefaultPathAttributes().setFileId(metadata.getId()))); return Optional.of(path); } catch(IOException ex) { diff --git a/onedrive/src/main/java/ch/cyberduck/core/onedrive/features/GraphAttributesFinderFeature.java b/onedrive/src/main/java/ch/cyberduck/core/onedrive/features/GraphAttributesFinderFeature.java index 5fdfe8601d..ae7f803256 100644 --- a/onedrive/src/main/java/ch/cyberduck/core/onedrive/features/GraphAttributesFinderFeature.java +++ b/onedrive/src/main/java/ch/cyberduck/core/onedrive/features/GraphAttributesFinderFeature.java @@ -16,6 +16,7 @@ package ch.cyberduck.core.onedrive.features; */ import ch.cyberduck.core.DefaultIOExceptionMappingService; +import ch.cyberduck.core.DefaultPathAttributes; import ch.cyberduck.core.DescriptiveUrl; import ch.cyberduck.core.ListProgressListener; import ch.cyberduck.core.Path; @@ -28,10 +29,14 @@ import ch.cyberduck.core.onedrive.GraphExceptionMappingService; import ch.cyberduck.core.onedrive.GraphSession; import ch.cyberduck.core.webloc.UrlFileWriterFactory; +import org.apache.commons.lang3.StringUtils; import org.nuxeo.onedrive.client.OneDriveAPIException; +import org.nuxeo.onedrive.client.OneDriveRuntimeException; import org.nuxeo.onedrive.client.types.DriveItem; import org.nuxeo.onedrive.client.types.DriveItemVersion; +import org.nuxeo.onedrive.client.types.File; import org.nuxeo.onedrive.client.types.FileSystemInfo; +import org.nuxeo.onedrive.client.types.Hashes; import org.nuxeo.onedrive.client.types.Publication; import java.io.IOException; @@ -50,7 +55,12 @@ public class GraphAttributesFinderFeature implements AttributesFinder, Attribute } static Optional getWebUrl(final DriveItem.Metadata metadata) { - return Optional.of(new DescriptiveUrl(metadata.getWebUrl(), DescriptiveUrl.Type.http)); + final String webUrl = metadata.getWebUrl(); + if(StringUtils.isBlank(webUrl)) { + return Optional.empty(); + } + + return Optional.of(new DescriptiveUrl(webUrl, DescriptiveUrl.Type.http)); } @Override @@ -77,12 +87,22 @@ public class GraphAttributesFinderFeature implements AttributesFinder, Attribute catch(IOException e) { throw new DefaultIOExceptionMappingService().map("Failure to read attributes of {0}", e, file); } + catch(OneDriveRuntimeException e) { + throw new GraphExceptionMappingService(fileid).map("Failure to read attributes of {0}", e.getCause(), file); + } } @Override public PathAttributes toAttributes(final DriveItem.Metadata metadata) { - final PathAttributes attributes = new PathAttributes(); + final PathAttributes attributes = new DefaultPathAttributes(); attributes.setETag(metadata.getETag()); + final File file = metadata.getFile(); + if(file != null) { + final Hashes hashes = file.getHashes(); + if(hashes != null) { + attributes.setVersionId(hashes.getQuickXorHash()); + } + } Optional webUrl = getWebUrl(metadata); if(metadata.isPackage()) { webUrl.ifPresent(url -> attributes.setSize(UrlFileWriterFactory.get().write(url).getBytes(Charset.defaultCharset()).length)); @@ -90,26 +110,34 @@ public class GraphAttributesFinderFeature implements AttributesFinder, Attribute else if(null != metadata.getSize()) { attributes.setSize(metadata.getSize()); } - setId(attributes, session.getFileId(metadata)); + attributes.setFileId(session.getFileId(metadata)); webUrl.ifPresent(attributes::setLink); final FileSystemInfo info = metadata.getFacet(FileSystemInfo.class); if(null != info) { - if(-1L == info.getLastModifiedDateTime().toInstant().toEpochMilli()) { - attributes.setModificationDate(metadata.getLastModifiedDateTime().toInstant().toEpochMilli()); + if(info.getLastModifiedDateTime().toInstant().toEpochMilli() <= 0L) { + if(metadata.getLastModifiedDateTime().toInstant().toEpochMilli() >= 0L) { + attributes.setModificationDate(metadata.getLastModifiedDateTime().toInstant().toEpochMilli()); + } } else { attributes.setModificationDate(info.getLastModifiedDateTime().toInstant().toEpochMilli()); } - if(-1 == info.getCreatedDateTime().toInstant().toEpochMilli()) { - attributes.setCreationDate(metadata.getCreatedDateTime().toInstant().toEpochMilli()); + if(info.getCreatedDateTime().toInstant().toEpochMilli() <= 0L) { + if(metadata.getCreatedDateTime().toInstant().toEpochMilli() >= 0L) { + attributes.setCreationDate(metadata.getCreatedDateTime().toInstant().toEpochMilli()); + } } else { attributes.setCreationDate(info.getCreatedDateTime().toInstant().toEpochMilli()); } } else { - attributes.setModificationDate(metadata.getLastModifiedDateTime().toInstant().toEpochMilli()); - attributes.setCreationDate(metadata.getCreatedDateTime().toInstant().toEpochMilli()); + if(metadata.getLastModifiedDateTime().toInstant().toEpochMilli() >= 0L) { + attributes.setModificationDate(metadata.getLastModifiedDateTime().toInstant().toEpochMilli()); + } + if(metadata.getCreatedDateTime().toInstant().toEpochMilli() >= 0L) { + attributes.setCreationDate(metadata.getCreatedDateTime().toInstant().toEpochMilli()); + } } final Publication publication = metadata.getPublication(); if(null != publication && publication.getLevel() == Publication.State.checkout) { @@ -119,15 +147,11 @@ public class GraphAttributesFinderFeature implements AttributesFinder, Attribute } public PathAttributes toAttributes(final DriveItem.Metadata metadata, final DriveItemVersion version) { - final PathAttributes attributes = toAttributes(metadata); + final PathAttributes attributes = this.toAttributes(metadata); attributes.setVersionId(version.getId()); attributes.setDuplicate(true); attributes.setSize(version.getSize()); attributes.setModificationDate(version.getLastModifiedDateTime().toInstant().toEpochMilli()); return attributes; } - - private void setId(final PathAttributes attributes, final String id) { - attributes.setFileId(id); - } } diff --git a/onedrive/src/main/java/ch/cyberduck/core/onedrive/features/GraphCopyFeature.java b/onedrive/src/main/java/ch/cyberduck/core/onedrive/features/GraphCopyFeature.java index af93e5556f..1a23927f44 100644 --- a/onedrive/src/main/java/ch/cyberduck/core/onedrive/features/GraphCopyFeature.java +++ b/onedrive/src/main/java/ch/cyberduck/core/onedrive/features/GraphCopyFeature.java @@ -35,6 +35,7 @@ import org.apache.logging.log4j.Logger; import org.nuxeo.onedrive.client.CopyOperation; import org.nuxeo.onedrive.client.Files; import org.nuxeo.onedrive.client.OneDriveAPIException; +import org.nuxeo.onedrive.client.OneDriveRuntimeException; import org.nuxeo.onedrive.client.types.DriveItem; import java.io.IOException; @@ -88,6 +89,9 @@ public class GraphCopyFeature implements Copy { catch(IOException e) { throw new DefaultIOExceptionMappingService().map("Cannot copy {0}", e, file); } + catch(OneDriveRuntimeException e) { + throw new GraphExceptionMappingService(fileid).map("Cannot copy {0}", e.getCause(), file); + } } @Override diff --git a/onedrive/src/main/java/ch/cyberduck/core/onedrive/features/GraphDeleteFeature.java b/onedrive/src/main/java/ch/cyberduck/core/onedrive/features/GraphDeleteFeature.java index 6d93336222..9418d05e37 100644 --- a/onedrive/src/main/java/ch/cyberduck/core/onedrive/features/GraphDeleteFeature.java +++ b/onedrive/src/main/java/ch/cyberduck/core/onedrive/features/GraphDeleteFeature.java @@ -28,6 +28,7 @@ import ch.cyberduck.core.transfer.TransferStatus; import org.nuxeo.onedrive.client.Files; import org.nuxeo.onedrive.client.OneDriveAPIException; +import org.nuxeo.onedrive.client.OneDriveRuntimeException; import org.nuxeo.onedrive.client.types.DriveItem; import java.io.IOException; @@ -60,6 +61,9 @@ public class GraphDeleteFeature implements Delete { catch(IOException e) { throw new DefaultIOExceptionMappingService().map("Cannot delete {0}", e, file); } + catch(OneDriveRuntimeException e) { + throw new GraphExceptionMappingService(fileid).map("Cannot delete {0}", e.getCause(), file); + } } } @@ -71,7 +75,7 @@ public class GraphDeleteFeature implements Delete { } @Override - public EnumSet features() { + public EnumSet features(final Path file) { return EnumSet.of(Flags.recursive); } } diff --git a/onedrive/src/main/java/ch/cyberduck/core/onedrive/features/GraphDirectoryFeature.java b/onedrive/src/main/java/ch/cyberduck/core/onedrive/features/GraphDirectoryFeature.java index 71f52bbcfd..6dc963bb4c 100644 --- a/onedrive/src/main/java/ch/cyberduck/core/onedrive/features/GraphDirectoryFeature.java +++ b/onedrive/src/main/java/ch/cyberduck/core/onedrive/features/GraphDirectoryFeature.java @@ -22,18 +22,20 @@ import ch.cyberduck.core.PathAttributes; import ch.cyberduck.core.exception.AccessDeniedException; import ch.cyberduck.core.exception.BackgroundException; import ch.cyberduck.core.features.Directory; +import ch.cyberduck.core.features.Write; import ch.cyberduck.core.onedrive.GraphExceptionMappingService; import ch.cyberduck.core.onedrive.GraphSession; import ch.cyberduck.core.transfer.TransferStatus; import org.nuxeo.onedrive.client.Files; import org.nuxeo.onedrive.client.OneDriveAPIException; +import org.nuxeo.onedrive.client.OneDriveRuntimeException; import org.nuxeo.onedrive.client.types.DriveItem; import java.io.IOException; import java.text.MessageFormat; -public class GraphDirectoryFeature implements Directory { +public class GraphDirectoryFeature implements Directory { private final GraphSession session; private final GraphAttributesFinderFeature attributes; @@ -46,7 +48,7 @@ public class GraphDirectoryFeature implements Directory { } @Override - public Path mkdir(final Path directory, final TransferStatus status) throws BackgroundException { + public Path mkdir(final Write writer, final Path directory, final TransferStatus status) throws BackgroundException { final DriveItem folder = session.getItem(directory.getParent()); try { final DriveItem.Metadata metadata = Files.createFolder(folder, directory.getName()); @@ -60,6 +62,9 @@ public class GraphDirectoryFeature implements Directory { catch(IOException e) { throw new DefaultIOExceptionMappingService().map("Cannot create folder {0}", e, directory); } + catch(OneDriveRuntimeException e) { + throw new GraphExceptionMappingService(fileid).map("Cannot create folder {0}", e.getCause(), directory); + } } @Override diff --git a/onedrive/src/main/java/ch/cyberduck/core/onedrive/features/GraphLockFeature.java b/onedrive/src/main/java/ch/cyberduck/core/onedrive/features/GraphLockFeature.java index 84ff3ac1c6..00d0ff64df 100644 --- a/onedrive/src/main/java/ch/cyberduck/core/onedrive/features/GraphLockFeature.java +++ b/onedrive/src/main/java/ch/cyberduck/core/onedrive/features/GraphLockFeature.java @@ -28,6 +28,7 @@ import ch.cyberduck.core.preferences.PreferencesFactory; import org.apache.http.HttpStatus; import org.nuxeo.onedrive.client.Files; import org.nuxeo.onedrive.client.OneDriveAPIException; +import org.nuxeo.onedrive.client.OneDriveRuntimeException; import org.nuxeo.onedrive.client.types.DriveItem; import java.io.IOException; @@ -53,10 +54,13 @@ public class GraphLockFeature implements Lock { if(e.getResponseCode() == HttpStatus.SC_INTERNAL_SERVER_ERROR) { throw new LockedException(e.getMessage(), e); } - throw new GraphExceptionMappingService(fileid).map("Failure to checkout file {0}", e, file); + throw new GraphExceptionMappingService(fileid).map("Failure to write attributes of {0}", e, file); } catch(IOException e) { - throw new DefaultIOExceptionMappingService().map(e, file); + throw new DefaultIOExceptionMappingService().map("Failure to write attributes of {0}", e, file); + } + catch(OneDriveRuntimeException e) { + throw new GraphExceptionMappingService(fileid).map("Failure to write attributes of {0}", e.getCause(), file); } } @@ -69,10 +73,13 @@ public class GraphLockFeature implements Lock { new AlphanumericRandomStringService().random())); } catch(OneDriveAPIException e) { - throw new GraphExceptionMappingService(fileid).map("Failure to check in file {0}", e, file); + throw new GraphExceptionMappingService(fileid).map("Failure to write attributes of {0}", e, file); } catch(IOException e) { - throw new DefaultIOExceptionMappingService().map(e, file); + throw new DefaultIOExceptionMappingService().map("Failure to write attributes of {0}", e, file); + } + catch(OneDriveRuntimeException e) { + throw new GraphExceptionMappingService(fileid).map("Failure to write attributes of {0}", e.getCause(), file); } } } diff --git a/onedrive/src/main/java/ch/cyberduck/core/onedrive/features/GraphMoveFeature.java b/onedrive/src/main/java/ch/cyberduck/core/onedrive/features/GraphMoveFeature.java index c751ca6567..5cbe91e8c8 100644 --- a/onedrive/src/main/java/ch/cyberduck/core/onedrive/features/GraphMoveFeature.java +++ b/onedrive/src/main/java/ch/cyberduck/core/onedrive/features/GraphMoveFeature.java @@ -35,6 +35,7 @@ import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; import org.nuxeo.onedrive.client.Files; import org.nuxeo.onedrive.client.OneDriveAPIException; +import org.nuxeo.onedrive.client.OneDriveRuntimeException; import org.nuxeo.onedrive.client.PatchOperation; import org.nuxeo.onedrive.client.types.DriveItem; import org.nuxeo.onedrive.client.types.FileSystemInfo; @@ -77,9 +78,11 @@ public class GraphMoveFeature implements Move { patchOperation.move(moveTarget); } // Keep current timestamp set - final FileSystemInfo info = new FileSystemInfo(); - info.setLastModifiedDateTime(Instant.ofEpochMilli(file.attributes().getModificationDate()).atOffset(ZoneOffset.UTC)); - patchOperation.facet("fileSystemInfo", info); + if(null != status.getModified()) { + final FileSystemInfo info = new FileSystemInfo(); + info.setLastModifiedDateTime(Instant.ofEpochMilli(status.getModified()).atOffset(ZoneOffset.UTC)); + patchOperation.facet("fileSystemInfo", info); + } final DriveItem item = session.getItem(file); try { final DriveItem.Metadata metadata = Files.patch(item, patchOperation); @@ -94,6 +97,9 @@ public class GraphMoveFeature implements Move { catch(IOException e) { throw new DefaultIOExceptionMappingService().map("Cannot rename {0}", e, file); } + catch(OneDriveRuntimeException e) { + throw new GraphExceptionMappingService(fileid).map("Cannot rename {0}", e.getCause(), file); + } } @Override diff --git a/onedrive/src/main/java/ch/cyberduck/core/onedrive/features/GraphQuotaFeature.java b/onedrive/src/main/java/ch/cyberduck/core/onedrive/features/GraphQuotaFeature.java index e029f2d29e..c12d991afd 100644 --- a/onedrive/src/main/java/ch/cyberduck/core/onedrive/features/GraphQuotaFeature.java +++ b/onedrive/src/main/java/ch/cyberduck/core/onedrive/features/GraphQuotaFeature.java @@ -25,6 +25,7 @@ import ch.cyberduck.core.onedrive.GraphSession; import org.nuxeo.onedrive.client.ODataQuery; import org.nuxeo.onedrive.client.OneDriveAPIException; +import org.nuxeo.onedrive.client.OneDriveRuntimeException; import org.nuxeo.onedrive.client.types.Drive; import org.nuxeo.onedrive.client.types.DriveItem; @@ -62,15 +63,17 @@ public class GraphQuotaFeature implements Quota { catch(IOException e) { throw new DefaultIOExceptionMappingService().map("Failure to read attributes of {0}", e, home); } - final org.nuxeo.onedrive.client.types.Quota quota = metadata.getQuota(); - if(quota != null) { - Long used = quota.getUsed(); + catch(OneDriveRuntimeException e) { + throw new GraphExceptionMappingService(fileid).map("Failure to read attributes of {0}", e.getCause(), home); + } + if(metadata.getQuota() != null) { + Long used = metadata.getQuota().getUsed(); if(used != null) { - Long remaining = quota.getRemaining(); + Long remaining = metadata.getQuota().getRemaining(); if(remaining != null && (used != 0 || remaining != 0)) { return new Space(used, remaining); } - Long total = quota.getTotal(); + Long total = metadata.getQuota().getTotal(); if(total != null && (used != 0 || total != 0)) { return new Space(used, total - used); } diff --git a/onedrive/src/main/java/ch/cyberduck/core/onedrive/features/GraphReadFeature.java b/onedrive/src/main/java/ch/cyberduck/core/onedrive/features/GraphReadFeature.java index 38116d8138..9e343649a6 100644 --- a/onedrive/src/main/java/ch/cyberduck/core/onedrive/features/GraphReadFeature.java +++ b/onedrive/src/main/java/ch/cyberduck/core/onedrive/features/GraphReadFeature.java @@ -34,6 +34,7 @@ import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; import org.nuxeo.onedrive.client.Files; import org.nuxeo.onedrive.client.OneDriveAPIException; +import org.nuxeo.onedrive.client.OneDriveRuntimeException; import org.nuxeo.onedrive.client.types.DriveItem; import java.io.IOException; @@ -101,5 +102,8 @@ public class GraphReadFeature implements Read { catch(IOException e) { throw new DefaultIOExceptionMappingService().map("Download {0} failed", e, file); } + catch(OneDriveRuntimeException e) { + throw new GraphExceptionMappingService(fileid).map("Download {0} failed", e.getCause(), file); + } } } diff --git a/onedrive/src/main/java/ch/cyberduck/core/onedrive/features/GraphSharedLinkFeature.java b/onedrive/src/main/java/ch/cyberduck/core/onedrive/features/GraphSharedLinkFeature.java index 2f002c81cf..dbf73b0891 100644 --- a/onedrive/src/main/java/ch/cyberduck/core/onedrive/features/GraphSharedLinkFeature.java +++ b/onedrive/src/main/java/ch/cyberduck/core/onedrive/features/GraphSharedLinkFeature.java @@ -22,21 +22,26 @@ import ch.cyberduck.core.PasswordCallback; import ch.cyberduck.core.Path; import ch.cyberduck.core.exception.BackgroundException; import ch.cyberduck.core.features.Share; +import ch.cyberduck.core.onedrive.GraphExceptionMappingService; import ch.cyberduck.core.onedrive.GraphSession; -import ch.cyberduck.core.worker.DefaultExceptionMappingService; import org.nuxeo.onedrive.client.Files; +import org.nuxeo.onedrive.client.OneDriveAPIException; +import org.nuxeo.onedrive.client.OneDriveRuntimeException; import org.nuxeo.onedrive.client.OneDriveSharingLink; import org.nuxeo.onedrive.client.types.DriveItem; import java.io.IOException; import java.text.MessageFormat; -public class GraphSharedLinkFeature implements Share { - private final GraphSession session; +public class GraphSharedLinkFeature implements Share { - public GraphSharedLinkFeature(final GraphSession session) { + private final GraphSession session; + private final GraphFileIdProvider fileid; + + public GraphSharedLinkFeature(final GraphSession session, final GraphFileIdProvider fileid) { this.session = session; + this.fileid = fileid; } @Override @@ -55,11 +60,14 @@ public class GraphSharedLinkFeature implements Share { return new DescriptiveUrl(Files.createSharedLink(item, OneDriveSharingLink.Type.VIEW).getLink().getWebUrl(), DescriptiveUrl.Type.signed, MessageFormat.format(LocaleFactory.localizedString("{0} URL"), LocaleFactory.localizedString("Pre-Signed", "S3"))); } + catch(OneDriveAPIException e) { + throw new GraphExceptionMappingService(fileid).map("Failure to write attributes of {0}", e, file); + } catch(IOException e) { throw new DefaultIOExceptionMappingService().map(e, file); } - catch(IllegalArgumentException e) { - throw new DefaultExceptionMappingService().map("Failed creating download url", e); + catch(OneDriveRuntimeException e) { + throw new GraphExceptionMappingService(fileid).map("Failure to write attributes of {0}", e.getCause(), file); } } diff --git a/onedrive/src/main/java/ch/cyberduck/core/onedrive/features/GraphTimestampFeature.java b/onedrive/src/main/java/ch/cyberduck/core/onedrive/features/GraphTimestampFeature.java index 0c72ea4795..6e3db36f82 100644 --- a/onedrive/src/main/java/ch/cyberduck/core/onedrive/features/GraphTimestampFeature.java +++ b/onedrive/src/main/java/ch/cyberduck/core/onedrive/features/GraphTimestampFeature.java @@ -27,6 +27,7 @@ import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; import org.nuxeo.onedrive.client.Files; import org.nuxeo.onedrive.client.OneDriveAPIException; +import org.nuxeo.onedrive.client.OneDriveRuntimeException; import org.nuxeo.onedrive.client.PatchOperation; import org.nuxeo.onedrive.client.types.DriveItem; import org.nuxeo.onedrive.client.types.FileSystemInfo; @@ -68,5 +69,8 @@ public class GraphTimestampFeature implements Timestamp { catch(IOException e) { throw new DefaultIOExceptionMappingService().map("Failure to write attributes of {0}", e, file); } + catch(OneDriveRuntimeException e) { + throw new GraphExceptionMappingService(fileid).map("Failure to write attributes of {0}", e.getCause(), file); + } } } diff --git a/onedrive/src/main/java/ch/cyberduck/core/onedrive/features/GraphTouchFeature.java b/onedrive/src/main/java/ch/cyberduck/core/onedrive/features/GraphTouchFeature.java index 261598cc3d..126b46c05b 100644 --- a/onedrive/src/main/java/ch/cyberduck/core/onedrive/features/GraphTouchFeature.java +++ b/onedrive/src/main/java/ch/cyberduck/core/onedrive/features/GraphTouchFeature.java @@ -24,6 +24,7 @@ import ch.cyberduck.core.URIEncoder; import ch.cyberduck.core.exception.AccessDeniedException; import ch.cyberduck.core.exception.BackgroundException; import ch.cyberduck.core.features.Touch; +import ch.cyberduck.core.features.Write; import ch.cyberduck.core.onedrive.GraphExceptionMappingService; import ch.cyberduck.core.onedrive.GraphSession; import ch.cyberduck.core.transfer.TransferStatus; @@ -31,6 +32,7 @@ import ch.cyberduck.core.transfer.TransferStatus; import org.apache.commons.lang3.StringUtils; import org.nuxeo.onedrive.client.Files; import org.nuxeo.onedrive.client.OneDriveAPIException; +import org.nuxeo.onedrive.client.OneDriveRuntimeException; import org.nuxeo.onedrive.client.types.DriveItem; import java.io.IOException; @@ -47,7 +49,7 @@ public class GraphTouchFeature implements Touch { } @Override - public Path touch(final Path file, final TransferStatus status) throws BackgroundException { + public Path touch(final Write writer, final Path file, final TransferStatus status) throws BackgroundException { try { final DriveItem folder = session.getItem(file.getParent()); final DriveItem.Metadata metadata = Files.createFile(folder, URIEncoder.encode(file.getName()), @@ -62,6 +64,9 @@ public class GraphTouchFeature implements Touch { catch(IOException e) { throw new DefaultIOExceptionMappingService().map("Cannot create {0}", e, file); } + catch(OneDriveRuntimeException e) { + throw new GraphExceptionMappingService(fileid).map("Cannot create {0}", e.getCause(), file); + } } @Override diff --git a/onedrive/src/main/java/ch/cyberduck/core/onedrive/features/GraphVersioningFeature.java b/onedrive/src/main/java/ch/cyberduck/core/onedrive/features/GraphVersioningFeature.java index 18d95534d8..5c6aafcc4d 100644 --- a/onedrive/src/main/java/ch/cyberduck/core/onedrive/features/GraphVersioningFeature.java +++ b/onedrive/src/main/java/ch/cyberduck/core/onedrive/features/GraphVersioningFeature.java @@ -29,6 +29,7 @@ import ch.cyberduck.core.onedrive.GraphSession; import org.nuxeo.onedrive.client.Files; import org.nuxeo.onedrive.client.OneDriveAPIException; +import org.nuxeo.onedrive.client.OneDriveRuntimeException; import org.nuxeo.onedrive.client.types.DriveItem; import org.nuxeo.onedrive.client.types.DriveItemVersion; @@ -71,6 +72,9 @@ public class GraphVersioningFeature implements Versioning { catch(IOException e) { throw new DefaultIOExceptionMappingService().map("Cannot revert file", e, file); } + catch(OneDriveRuntimeException e) { + throw new GraphExceptionMappingService(fileid).map("Cannot revert file", e.getCause(), file); + } } @Override @@ -95,6 +99,9 @@ public class GraphVersioningFeature implements Versioning { catch(IOException e) { throw new DefaultIOExceptionMappingService().map("Failure to read attributes of {0}", e, file); } + catch(OneDriveRuntimeException e) { + throw new GraphExceptionMappingService(fileid).map("Failure to read attributes of {0}", e.getCause(), file); + } return versions; } diff --git a/onedrive/src/main/java/ch/cyberduck/core/onedrive/features/GraphWriteFeature.java b/onedrive/src/main/java/ch/cyberduck/core/onedrive/features/GraphWriteFeature.java index 0158b332c8..9c0a51cd38 100644 --- a/onedrive/src/main/java/ch/cyberduck/core/onedrive/features/GraphWriteFeature.java +++ b/onedrive/src/main/java/ch/cyberduck/core/onedrive/features/GraphWriteFeature.java @@ -38,6 +38,7 @@ import org.apache.logging.log4j.Logger; import org.nuxeo.onedrive.client.Files; import org.nuxeo.onedrive.client.OneDriveAPIException; import org.nuxeo.onedrive.client.OneDriveJsonObject; +import org.nuxeo.onedrive.client.OneDriveRuntimeException; import org.nuxeo.onedrive.client.UploadSession; import org.nuxeo.onedrive.client.types.DriveItem; @@ -89,6 +90,9 @@ public class GraphWriteFeature implements Write { catch(IOException e) { throw new DefaultIOExceptionMappingService().map("Upload {0} failed", e, file); } + catch(OneDriveRuntimeException e) { + throw new GraphExceptionMappingService(fileid).map("Upload {0} failed", e.getCause(), file); + } } private final class ChunkedOutputStream extends OutputStream { @@ -140,6 +144,9 @@ public class GraphWriteFeature implements Write { catch(IOException e) { throw new DefaultIOExceptionMappingService().map("Upload {0} failed", e, file); } + catch(OneDriveRuntimeException e) { + throw new GraphExceptionMappingService(fileid).map("Download {0} failed", e.getCause(), file); + } return null; } }, overall).call(); @@ -161,7 +168,7 @@ public class GraphWriteFeature implements Write { log.warn("Abort upload session {} with no completed parts", upload); // Use touch feature for empty file upload upload.cancelUpload(); - new GraphTouchFeature(session, fileid).touch(file, overall); + new GraphTouchFeature(session, fileid).touch(GraphWriteFeature.this, file, overall); } } catch(BackgroundException e) { diff --git a/onedrive/src/main/java/ch/cyberduck/core/onedrive/features/sharepoint/GroupListService.java b/onedrive/src/main/java/ch/cyberduck/core/onedrive/features/sharepoint/GroupListService.java index 6b3c14e843..c22bd26795 100644 --- a/onedrive/src/main/java/ch/cyberduck/core/onedrive/features/sharepoint/GroupListService.java +++ b/onedrive/src/main/java/ch/cyberduck/core/onedrive/features/sharepoint/GroupListService.java @@ -15,6 +15,7 @@ package ch.cyberduck.core.onedrive.features.sharepoint; * GNU General Public License for more details. */ +import ch.cyberduck.core.DefaultPathAttributes; import ch.cyberduck.core.Path; import ch.cyberduck.core.PathAttributes; import ch.cyberduck.core.onedrive.AbstractListService; @@ -50,16 +51,9 @@ public class GroupListService extends AbstractListService { @Override protected Path toPath(final GroupItem.Metadata metadata, final Path directory) { - final PathAttributes attributes = new PathAttributes(); + final PathAttributes attributes = new DefaultPathAttributes(); attributes.setFileId(metadata.getId()); - final String name; - if(StringUtils.isBlank(metadata.getDisplayName())) { - name = metadata.getId(); - } - else { - name = metadata.getDisplayName(); - } - return new Path(directory, name, EnumSet.of(Path.Type.volume, Path.Type.directory, Path.Type.placeholder), attributes); + return new Path(directory, metadata.getDisplayName(), EnumSet.of(Path.Type.volume, Path.Type.directory, Path.Type.placeholder), attributes); } @Override @@ -67,6 +61,9 @@ public class GroupListService extends AbstractListService { if(StringUtils.isBlank(metadata.getId())) { return false; } + if(StringUtils.isBlank(metadata.getDisplayName())) { + return false; + } return super.filter(directory, metadata); } } diff --git a/onedrive/src/main/java/ch/cyberduck/core/onedrive/features/sharepoint/SitesListService.java b/onedrive/src/main/java/ch/cyberduck/core/onedrive/features/sharepoint/SitesListService.java index d8088bfd16..a728596658 100644 --- a/onedrive/src/main/java/ch/cyberduck/core/onedrive/features/sharepoint/SitesListService.java +++ b/onedrive/src/main/java/ch/cyberduck/core/onedrive/features/sharepoint/SitesListService.java @@ -1,6 +1,7 @@ package ch.cyberduck.core.onedrive.features.sharepoint; import ch.cyberduck.core.AttributedList; +import ch.cyberduck.core.DefaultPathAttributes; import ch.cyberduck.core.DescriptiveUrl; import ch.cyberduck.core.NullFilter; import ch.cyberduck.core.Path; @@ -63,6 +64,9 @@ public class SitesListService extends AbstractListService { if(StringUtils.isBlank(metadata.getId())) { return false; } + if(StringUtils.isBlank(metadata.getName())) { + return false; + } if(!session.isSingleSite() && directory.getParent().isRoot()) { if(metadata.getRoot() == null) { return false; @@ -114,7 +118,7 @@ public class SitesListService extends AbstractListService { @Override protected Path toPath(final Site.Metadata metadata, final Path directory) { - final PathAttributes attributes = new PathAttributes(); + final PathAttributes attributes = new DefaultPathAttributes(); attributes.setFileId(metadata.getId()); attributes.setDisplayname(metadata.getDisplayName()); attributes.setLink(new DescriptiveUrl(metadata.getWebUrl())); diff --git a/onedrive/src/test/java/ch/cyberduck/core/cryptomator/CopyWorkerTest.java b/onedrive/src/test/java/ch/cyberduck/core/cryptomator/CopyWorkerTest.java index 41bf2e004a..b48b7f32d9 100644 --- a/onedrive/src/test/java/ch/cyberduck/core/cryptomator/CopyWorkerTest.java +++ b/onedrive/src/test/java/ch/cyberduck/core/cryptomator/CopyWorkerTest.java @@ -29,11 +29,11 @@ import ch.cyberduck.core.cryptomator.features.CryptoWriteFeature; import ch.cyberduck.core.features.AttributesFinder; import ch.cyberduck.core.features.Directory; import ch.cyberduck.core.features.Find; +import ch.cyberduck.core.features.Write; import ch.cyberduck.core.io.StreamCopier; import ch.cyberduck.core.onedrive.AbstractOneDriveTest; import ch.cyberduck.core.onedrive.OneDriveHomeFinderService; import ch.cyberduck.core.onedrive.features.GraphAttributesFinderFeature; -import ch.cyberduck.core.onedrive.features.GraphDeleteFeature; import ch.cyberduck.core.onedrive.features.GraphDirectoryFeature; import ch.cyberduck.core.onedrive.features.GraphFindFeature; import ch.cyberduck.core.onedrive.features.GraphReadFeature; @@ -57,6 +57,7 @@ import org.junit.Test; import org.junit.experimental.categories.Category; import org.junit.runner.RunWith; import org.junit.runners.Parameterized; +import org.nuxeo.onedrive.client.types.DriveItem; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; @@ -83,7 +84,7 @@ public class CopyWorkerTest extends AbstractOneDriveTest { session.withRegistry(registry); final byte[] content = RandomUtils.nextBytes(40500); final TransferStatus status = new TransferStatus(); - new CryptoBulkFeature<>(session, new DisabledBulkFeature(), new GraphDeleteFeature(session, fileid), cryptomator).pre(Transfer.Type.upload, Collections.singletonMap(new TransferItem(source), status), new DisabledConnectionCallback()); + new CryptoBulkFeature<>(session, new DisabledBulkFeature(), 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 GraphWriteFeature(session, fileid), 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()); @@ -108,10 +109,11 @@ public class CopyWorkerTest extends AbstractOneDriveTest { cryptomator.create(session, new VaultCredentials("test"), vaultVersion); final DefaultVaultRegistry registry = new DefaultVaultRegistry(new DisabledPasswordCallback(), cryptomator); session.withRegistry(registry); - new CryptoTouchFeature<>(session, new DefaultTouchFeature<>(new GraphWriteFeature(session, fileid) - ), new GraphWriteFeature(session, fileid), cryptomator).touch(source, new TransferStatus()); + new CryptoTouchFeature<>(session, new DefaultTouchFeature( + session), cryptomator).touch(cryptomator.getFeature(session, Write.class, new GraphWriteFeature(session, fileid)), source, new TransferStatus()); assertTrue(cryptomator.getFeature(session, Find.class, new DefaultFindFeature(session)).find(source)); - cryptomator.getFeature(session, Directory.class, new GraphDirectoryFeature(session, fileid)).mkdir(targetFolder, new TransferStatus()); + cryptomator.getFeature(session, Directory.class, new GraphDirectoryFeature(session, fileid)).mkdir( + cryptomator.getFeature(session, Write.class, new GraphWriteFeature(session, fileid)), 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); @@ -132,10 +134,11 @@ public class CopyWorkerTest extends AbstractOneDriveTest { cryptomator.create(session, new VaultCredentials("test"), vaultVersion); final DefaultVaultRegistry registry = new DefaultVaultRegistry(new DisabledPasswordCallback(), cryptomator); session.withRegistry(registry); - new CryptoTouchFeature<>(session, new DefaultTouchFeature<>(new GraphWriteFeature(session, fileid) - ), new GraphWriteFeature(session, fileid), cryptomator).touch(source, new TransferStatus()); + new CryptoTouchFeature<>(session, new DefaultTouchFeature( + session), cryptomator).touch(cryptomator.getFeature(session, Write.class, new GraphWriteFeature(session, fileid)), source, new TransferStatus()); assertTrue(cryptomator.getFeature(session, Find.class, new DefaultFindFeature(session)).find(source)); - cryptomator.getFeature(session, Directory.class, new GraphDirectoryFeature(session, fileid)).mkdir(targetFolder, new TransferStatus()); + cryptomator.getFeature(session, Directory.class, new GraphDirectoryFeature(session, fileid)).mkdir( + cryptomator.getFeature(session, Write.class, new GraphWriteFeature(session, fileid)), 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); @@ -154,10 +157,11 @@ public class CopyWorkerTest extends AbstractOneDriveTest { cryptomator.create(session, new VaultCredentials("test"), vaultVersion); final DefaultVaultRegistry registry = new DefaultVaultRegistry(new DisabledPasswordCallback(), cryptomator); session.withRegistry(registry); - cryptomator.getFeature(session, Directory.class, new GraphDirectoryFeature(session, fileid)).mkdir(folder, new TransferStatus()); + cryptomator.getFeature(session, Directory.class, new GraphDirectoryFeature(session, fileid)).mkdir( + cryptomator.getFeature(session, Write.class, new GraphWriteFeature(session, fileid)), folder, new TransferStatus()); assertTrue(cryptomator.getFeature(session, Find.class, new DefaultFindFeature(session)).find(folder)); - new CryptoTouchFeature<>(session, new DefaultTouchFeature<>(new GraphWriteFeature(session, fileid) - ), new GraphWriteFeature(session, fileid), cryptomator).touch(file, new TransferStatus()); + new CryptoTouchFeature<>(session, new DefaultTouchFeature( + session), cryptomator).touch(cryptomator.getFeature(session, Write.class, new GraphWriteFeature(session, fileid)), 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)); @@ -190,7 +194,8 @@ public class CopyWorkerTest extends AbstractOneDriveTest { cryptomator.create(session, new VaultCredentials("test"), vaultVersion); final DefaultVaultRegistry registry = new DefaultVaultRegistry(new DisabledPasswordCallback(), cryptomator); session.withRegistry(registry); - cryptomator.getFeature(session, Directory.class, new GraphDirectoryFeature(session, fileid)).mkdir(encryptedFolder, new TransferStatus()); + cryptomator.getFeature(session, Directory.class, new GraphDirectoryFeature(session, fileid)).mkdir( + cryptomator.getFeature(session, Write.class, new GraphWriteFeature(session, fileid)), 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()); @@ -211,9 +216,9 @@ public class CopyWorkerTest extends AbstractOneDriveTest { 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 GraphDirectoryFeature(session, fileid).mkdir(cleartextFolder, new TransferStatus()); - new DefaultTouchFeature<>(new GraphWriteFeature(session, fileid) - ).touch(cleartextFile, new TransferStatus()); + new GraphDirectoryFeature(session, fileid).mkdir(new GraphWriteFeature(session, fileid), cleartextFolder, new TransferStatus()); + new DefaultTouchFeature( + session).touch(new GraphWriteFeature(session, fileid), cleartextFile, new TransferStatus()); assertTrue(new GraphFindFeature(session, fileid).find(cleartextFolder)); assertTrue(new GraphFindFeature(session, fileid).find(cleartextFile)); final CryptoVault cryptomator = new CryptoVault(vault); @@ -238,17 +243,18 @@ public class CopyWorkerTest extends AbstractOneDriveTest { final Path home = new OneDriveHomeFinderService().find(); 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 GraphDirectoryFeature(session, fileid).mkdir(clearFolder, new TransferStatus()); + new GraphDirectoryFeature(session, fileid).mkdir(new GraphWriteFeature(session, fileid), 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 GraphDirectoryFeature(session, fileid)).mkdir(encryptedFolder, new TransferStatus()); + cryptomator.getFeature(session, Directory.class, new GraphDirectoryFeature(session, fileid)).mkdir( + cryptomator.getFeature(session, Write.class, new GraphWriteFeature(session, fileid)), encryptedFolder, new TransferStatus()); assertTrue(cryptomator.getFeature(session, Find.class, new DefaultFindFeature(session)).find(encryptedFolder)); - new CryptoTouchFeature<>(session, new DefaultTouchFeature<>(new GraphWriteFeature(session, fileid) - ), new GraphWriteFeature(session, fileid), cryptomator).touch(encryptedFile, new TransferStatus()); + new CryptoTouchFeature<>(session, new DefaultTouchFeature( + session), cryptomator).touch(cryptomator.getFeature(session, Write.class, new GraphWriteFeature(session, fileid)), 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)); @@ -270,10 +276,11 @@ public class CopyWorkerTest extends AbstractOneDriveTest { cryptomator.create(session, new VaultCredentials("test"), vaultVersion); final DefaultVaultRegistry registry = new DefaultVaultRegistry(new DisabledPasswordCallback(), cryptomator); session.withRegistry(registry); - cryptomator.getFeature(session, Directory.class, new GraphDirectoryFeature(session, fileid)).mkdir(encryptedFolder, new TransferStatus()); + cryptomator.getFeature(session, Directory.class, new GraphDirectoryFeature(session, fileid)).mkdir( + cryptomator.getFeature(session, Write.class, new GraphWriteFeature(session, fileid)), encryptedFolder, new TransferStatus()); assertTrue(cryptomator.getFeature(session, Find.class, new DefaultFindFeature(session)).find(encryptedFolder)); - new CryptoTouchFeature<>(session, new DefaultTouchFeature<>(new GraphWriteFeature(session, fileid) - ), new GraphWriteFeature(session, fileid), cryptomator).touch(encryptedFile, new TransferStatus()); + new CryptoTouchFeature<>(session, new DefaultTouchFeature( + session), cryptomator).touch(cryptomator.getFeature(session, Write.class, new GraphWriteFeature(session, fileid)), 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)); diff --git a/onedrive/src/test/java/ch/cyberduck/core/cryptomator/GraphDirectoryFeatureTest.java b/onedrive/src/test/java/ch/cyberduck/core/cryptomator/GraphDirectoryFeatureTest.java index 2720e5a4e6..9fdc54709f 100644 --- a/onedrive/src/test/java/ch/cyberduck/core/cryptomator/GraphDirectoryFeatureTest.java +++ b/onedrive/src/test/java/ch/cyberduck/core/cryptomator/GraphDirectoryFeatureTest.java @@ -26,6 +26,7 @@ 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.onedrive.AbstractOneDriveTest; import ch.cyberduck.core.onedrive.GraphItemListService; import ch.cyberduck.core.onedrive.OneDriveHomeFinderService; @@ -33,6 +34,7 @@ import ch.cyberduck.core.onedrive.features.GraphAttributesFinderFeature; import ch.cyberduck.core.onedrive.features.GraphDeleteFeature; import ch.cyberduck.core.onedrive.features.GraphDirectoryFeature; import ch.cyberduck.core.onedrive.features.GraphFindFeature; +import ch.cyberduck.core.onedrive.features.GraphWriteFeature; import ch.cyberduck.core.shared.DefaultAttributesFinderFeature; import ch.cyberduck.core.shared.DefaultFindFeature; import ch.cyberduck.core.transfer.TransferStatus; @@ -65,7 +67,7 @@ public class GraphDirectoryFeatureTest extends AbstractOneDriveTest { cryptomator.create(session, new VaultCredentials("test"), vaultVersion); session.withRegistry(new DefaultVaultRegistry(new DisabledPasswordCallback(), cryptomator)); final Path test = cryptomator.getFeature(session, Directory.class, new GraphDirectoryFeature(session, fileid)).mkdir( - new Path(vault, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory)), new TransferStatus()); + cryptomator.getFeature(session, Write.class, new GraphWriteFeature(session, fileid)), new Path(vault, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory)), new TransferStatus()); assertNotNull(test.attributes().getVault()); final String id = test.attributes().getFileId(); final long timestamp = test.attributes().getModificationDate(); @@ -90,7 +92,7 @@ public class GraphDirectoryFeatureTest extends AbstractOneDriveTest { cryptomator.create(session, new VaultCredentials("test"), vaultVersion); session.withRegistry(new DefaultVaultRegistry(new DisabledPasswordCallback(), cryptomator)); final Path test = cryptomator.getFeature(session, Directory.class, new GraphDirectoryFeature(session, fileid)).mkdir( - new Path(vault, new AlphanumericRandomStringService(130).random(), EnumSet.of(Path.Type.directory)), new TransferStatus()); + cryptomator.getFeature(session, Write.class, new GraphWriteFeature(session, fileid)), new Path(vault, new AlphanumericRandomStringService(130).random(), EnumSet.of(Path.Type.directory)), new TransferStatus()); assertNotNull(test.attributes().getVault()); final String id = test.attributes().getFileId(); assertTrue(cryptomator.getFeature(session, Find.class, new DefaultFindFeature(session)).find(test)); diff --git a/onedrive/src/test/java/ch/cyberduck/core/cryptomator/GraphMoveFeatureTest.java b/onedrive/src/test/java/ch/cyberduck/core/cryptomator/GraphMoveFeatureTest.java index b07bb69225..12de9fca06 100644 --- a/onedrive/src/test/java/ch/cyberduck/core/cryptomator/GraphMoveFeatureTest.java +++ b/onedrive/src/test/java/ch/cyberduck/core/cryptomator/GraphMoveFeatureTest.java @@ -26,6 +26,7 @@ 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.onedrive.AbstractOneDriveTest; import ch.cyberduck.core.onedrive.OneDriveHomeFinderService; import ch.cyberduck.core.onedrive.features.GraphAttributesFinderFeature; @@ -45,6 +46,7 @@ import org.junit.Test; import org.junit.experimental.categories.Category; import org.junit.runner.RunWith; import org.junit.runners.Parameterized; +import org.nuxeo.onedrive.client.types.DriveItem; import java.util.Arrays; import java.util.EnumSet; @@ -63,11 +65,12 @@ public class GraphMoveFeatureTest extends AbstractOneDriveTest { final CryptoVault cryptomator = new CryptoVault(vault); cryptomator.create(session, new VaultCredentials("test"), vaultVersion); session.withRegistry(new DefaultVaultRegistry(new DisabledPasswordCallback(), cryptomator)); - cryptomator.getFeature(session, Directory.class, new GraphDirectoryFeature(session, fileid)).mkdir(folder, new TransferStatus()); + cryptomator.getFeature(session, Directory.class, new GraphDirectoryFeature(session, fileid)).mkdir( + cryptomator.getFeature(session, Write.class, new GraphWriteFeature(session, fileid)), folder, new TransferStatus()); final String filename = new AlphanumericRandomStringService().random(); - final Path file = new CryptoTouchFeature<>(session, new DefaultTouchFeature<>(new GraphWriteFeature(session, fileid) - ), new GraphWriteFeature(session, fileid), cryptomator).touch( - new Path(folder, filename, EnumSet.of(Path.Type.file)), new TransferStatus()); + final Path file = new CryptoTouchFeature<>(session, new DefaultTouchFeature( + session), cryptomator).touch( + cryptomator.getFeature(session, Write.class, new GraphWriteFeature(session, fileid)), new Path(folder, filename, 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 GraphMoveFeature(session, fileid)); // rename file diff --git a/onedrive/src/test/java/ch/cyberduck/core/cryptomator/GraphTouchFeatureTest.java b/onedrive/src/test/java/ch/cyberduck/core/cryptomator/GraphTouchFeatureTest.java index bd5cc13a17..be98c69d2f 100644 --- a/onedrive/src/test/java/ch/cyberduck/core/cryptomator/GraphTouchFeatureTest.java +++ b/onedrive/src/test/java/ch/cyberduck/core/cryptomator/GraphTouchFeatureTest.java @@ -24,6 +24,7 @@ import ch.cyberduck.core.cryptomator.features.CryptoTouchFeature; import ch.cyberduck.core.features.AttributesFinder; import ch.cyberduck.core.features.Delete; import ch.cyberduck.core.features.Find; +import ch.cyberduck.core.features.Write; import ch.cyberduck.core.onedrive.AbstractOneDriveTest; import ch.cyberduck.core.onedrive.OneDriveHomeFinderService; import ch.cyberduck.core.onedrive.features.GraphAttributesFinderFeature; @@ -41,6 +42,7 @@ import org.junit.Test; import org.junit.experimental.categories.Category; import org.junit.runner.RunWith; import org.junit.runners.Parameterized; +import org.nuxeo.onedrive.client.types.DriveItem; import java.util.Arrays; import java.util.EnumSet; @@ -61,8 +63,8 @@ public class GraphTouchFeatureTest extends AbstractOneDriveTest { 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 DefaultTouchFeature<>(new GraphWriteFeature(session, fileid)), new GraphWriteFeature(session, fileid), cryptomator).touch( - new Path(vault, new AlphanumericRandomStringService(130).random(), EnumSet.of(Path.Type.file)), status); + final Path test = new CryptoTouchFeature<>(session, new DefaultTouchFeature(session), cryptomator).touch( + cryptomator.getFeature(session, Write.class, new GraphWriteFeature(session, fileid)), new Path(vault, new AlphanumericRandomStringService(130).random(), EnumSet.of(Path.Type.file)), status); assertEquals(0L, test.attributes().getSize()); assertEquals(0L, status.getResponse().getSize()); assertNotNull(test.attributes().getVault()); @@ -82,8 +84,8 @@ public class GraphTouchFeatureTest extends AbstractOneDriveTest { 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 DefaultTouchFeature<>(new GraphWriteFeature(session, fileid)), new GraphWriteFeature(session, fileid), cryptomator).touch( - new Path(vault, new AlphanumericRandomStringService(130).random(), EnumSet.of(Path.Type.file)), status); + final Path test = new CryptoTouchFeature<>(session, new DefaultTouchFeature(session), cryptomator).touch( + cryptomator.getFeature(session, Write.class, new GraphWriteFeature(session, fileid)), new Path(vault, new AlphanumericRandomStringService(130).random(), EnumSet.of(Path.Type.file)), status); assertEquals(0L, test.attributes().getSize()); assertEquals(0L, status.getResponse().getSize()); assertNotNull(test.attributes().getVault()); diff --git a/onedrive/src/test/java/ch/cyberduck/core/cryptomator/MoveWorkerTest.java b/onedrive/src/test/java/ch/cyberduck/core/cryptomator/MoveWorkerTest.java index f8f6748212..13ac904b2a 100644 --- a/onedrive/src/test/java/ch/cyberduck/core/cryptomator/MoveWorkerTest.java +++ b/onedrive/src/test/java/ch/cyberduck/core/cryptomator/MoveWorkerTest.java @@ -33,6 +33,7 @@ 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.io.StreamCopier; import ch.cyberduck.core.onedrive.AbstractOneDriveTest; import ch.cyberduck.core.onedrive.GraphItemListService; @@ -60,6 +61,7 @@ import org.junit.Test; import org.junit.experimental.categories.Category; import org.junit.runner.RunWith; import org.junit.runners.Parameterized; +import org.nuxeo.onedrive.client.types.DriveItem; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; @@ -85,7 +87,7 @@ public class MoveWorkerTest extends AbstractOneDriveTest { session.withRegistry(new DefaultVaultRegistry(new DisabledPasswordCallback(), cryptomator)); final byte[] content = RandomUtils.nextBytes(40500); final TransferStatus status = new TransferStatus(); - new CryptoBulkFeature<>(session, new DisabledBulkFeature(), new GraphDeleteFeature(session, fileid), cryptomator).pre(Transfer.Type.upload, Collections.singletonMap(new TransferItem(source), status), new DisabledConnectionCallback()); + new CryptoBulkFeature<>(session, new DisabledBulkFeature(), 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 GraphWriteFeature(session, fileid), cryptomator).write(source, status.setLength(content.length), new DisabledConnectionCallback())); assertTrue(cryptomator.getFeature(session, Find.class, new DefaultFindFeature(session)).find(source)); final MoveWorker worker = new MoveWorker(Collections.singletonMap(source, target), new SessionPool.SingleSessionPool(session), PathCache.empty(), new DisabledProgressListener(), new DisabledLoginCallback()); @@ -108,10 +110,11 @@ public class MoveWorkerTest extends AbstractOneDriveTest { final CryptoVault cryptomator = new CryptoVault(vault); cryptomator.create(session, new VaultCredentials("test"), vaultVersion); session.withRegistry(new DefaultVaultRegistry(new DisabledPasswordCallback(), cryptomator)); - new CryptoTouchFeature<>(session, new DefaultTouchFeature<>(new GraphWriteFeature(session, fileid) - ), new GraphWriteFeature(session, fileid), cryptomator).touch(source, new TransferStatus()); + new CryptoTouchFeature<>(session, new DefaultTouchFeature( + session), cryptomator).touch(cryptomator.getFeature(session, Write.class, new GraphWriteFeature(session, fileid)), source, new TransferStatus()); assertTrue(cryptomator.getFeature(session, Find.class, new DefaultFindFeature(session)).find(source)); - cryptomator.getFeature(session, Directory.class, new GraphDirectoryFeature(session, fileid)).mkdir(targetFolder, new TransferStatus()); + cryptomator.getFeature(session, Directory.class, new GraphDirectoryFeature(session, fileid)).mkdir( + cryptomator.getFeature(session, Write.class, new GraphWriteFeature(session, fileid)), targetFolder, new TransferStatus()); assertTrue(cryptomator.getFeature(session, Find.class, new DefaultFindFeature(session)).find(targetFolder)); final MoveWorker worker = new MoveWorker(Collections.singletonMap(source, target), new SessionPool.SingleSessionPool(session), PathCache.empty(), new DisabledProgressListener(), new DisabledLoginCallback()); worker.run(session); @@ -131,10 +134,11 @@ public class MoveWorkerTest extends AbstractOneDriveTest { final CryptoVault cryptomator = new CryptoVault(vault); cryptomator.create(session, new VaultCredentials("test"), vaultVersion); session.withRegistry(new DefaultVaultRegistry(new DisabledPasswordCallback(), cryptomator)); - new CryptoTouchFeature<>(session, new DefaultTouchFeature<>(new GraphWriteFeature(session, fileid) - ), new GraphWriteFeature(session, fileid), cryptomator).touch(source, new TransferStatus()); + new CryptoTouchFeature<>(session, new DefaultTouchFeature( + session), cryptomator).touch(cryptomator.getFeature(session, Write.class, new GraphWriteFeature(session, fileid)), source, new TransferStatus()); assertTrue(cryptomator.getFeature(session, Find.class, new DefaultFindFeature(session)).find(source)); - cryptomator.getFeature(session, Directory.class, new GraphDirectoryFeature(session, fileid)).mkdir(targetFolder, new TransferStatus()); + cryptomator.getFeature(session, Directory.class, new GraphDirectoryFeature(session, fileid)).mkdir( + cryptomator.getFeature(session, Write.class, new GraphWriteFeature(session, fileid)), targetFolder, new TransferStatus()); assertTrue(cryptomator.getFeature(session, Find.class, new DefaultFindFeature(session)).find(targetFolder)); final MoveWorker worker = new MoveWorker(Collections.singletonMap(source, target), new SessionPool.SingleSessionPool(session), PathCache.empty(), new DisabledProgressListener(), new DisabledLoginCallback()); worker.run(session); @@ -152,10 +156,11 @@ public class MoveWorkerTest extends AbstractOneDriveTest { final CryptoVault cryptomator = new CryptoVault(vault); cryptomator.create(session, new VaultCredentials("test"), vaultVersion); session.withRegistry(new DefaultVaultRegistry(new DisabledPasswordCallback(), cryptomator)); - cryptomator.getFeature(session, Directory.class, new GraphDirectoryFeature(session, fileid)).mkdir(folder, new TransferStatus()); + cryptomator.getFeature(session, Directory.class, new GraphDirectoryFeature(session, fileid)).mkdir( + cryptomator.getFeature(session, Write.class, new GraphWriteFeature(session, fileid)), folder, new TransferStatus()); assertTrue(cryptomator.getFeature(session, Find.class, new DefaultFindFeature(session)).find(folder)); - new CryptoTouchFeature<>(session, new DefaultTouchFeature<>(new GraphWriteFeature(session, fileid) - ), new GraphWriteFeature(session, fileid), cryptomator).touch(file, new TransferStatus()); + new CryptoTouchFeature<>(session, new DefaultTouchFeature( + session), cryptomator).touch(cryptomator.getFeature(session, Write.class, new GraphWriteFeature(session, fileid)), file, new TransferStatus()); assertTrue(cryptomator.getFeature(session, Find.class, new DefaultFindFeature(session)).find(file)); // rename file final Path fileRenamed = new Path(folder, "f1", EnumSet.of(Path.Type.file)); @@ -178,7 +183,7 @@ public class MoveWorkerTest extends AbstractOneDriveTest { final Path home = new OneDriveHomeFinderService().find(); final Path vault = new Path(home, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory)); final Path clearFile = new Path(home, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)); - new DefaultTouchFeature<>(new GraphWriteFeature(session, fileid)).touch(clearFile, new TransferStatus()); + new DefaultTouchFeature(session).touch(new GraphWriteFeature(session, fileid), clearFile, new TransferStatus()); assertTrue(new DefaultFindFeature(session).find(clearFile)); 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)); @@ -186,7 +191,8 @@ public class MoveWorkerTest extends AbstractOneDriveTest { cryptomator.create(session, new VaultCredentials("test"), vaultVersion); final DefaultVaultRegistry registry = new DefaultVaultRegistry(new DisabledPasswordCallback(), cryptomator); session.withRegistry(registry); - cryptomator.getFeature(session, Directory.class, new GraphDirectoryFeature(session, fileid)).mkdir(encryptedFolder, new TransferStatus()); + cryptomator.getFeature(session, Directory.class, new GraphDirectoryFeature(session, fileid)).mkdir( + cryptomator.getFeature(session, Write.class, new GraphWriteFeature(session, fileid)), encryptedFolder, new TransferStatus()); assertTrue(cryptomator.getFeature(session, Find.class, new DefaultFindFeature(session)).find(encryptedFolder)); // move file into vault final MoveWorker worker = new MoveWorker(Collections.singletonMap(clearFile, encryptedFile), new SessionPool.SingleSessionPool(session), PathCache.empty(), new DisabledProgressListener(), new DisabledLoginCallback()); @@ -203,9 +209,9 @@ public class MoveWorkerTest extends AbstractOneDriveTest { 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)); final Path clearFile = new Path(clearFolder, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)); - new GraphDirectoryFeature(session, fileid).mkdir(clearFolder, new TransferStatus()); - new DefaultTouchFeature<>(new GraphWriteFeature(session, fileid) - ).touch(clearFile, new TransferStatus()); + new GraphDirectoryFeature(session, fileid).mkdir(new GraphWriteFeature(session, fileid), clearFolder, new TransferStatus()); + new DefaultTouchFeature( + session).touch(new GraphWriteFeature(session, fileid), clearFile, new TransferStatus()); assertTrue(new DefaultFindFeature(session).find(clearFolder)); assertTrue(new DefaultFindFeature(session).find(clearFile)); final CryptoVault cryptomator = new CryptoVault(vault); @@ -230,17 +236,18 @@ public class MoveWorkerTest extends AbstractOneDriveTest { final Path home = new OneDriveHomeFinderService().find(); 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 GraphDirectoryFeature(session, fileid).mkdir(clearFolder, new TransferStatus()); + new GraphDirectoryFeature(session, fileid).mkdir(new GraphWriteFeature(session, fileid), 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 GraphDirectoryFeature(session, fileid)).mkdir(encryptedFolder, new TransferStatus()); + cryptomator.getFeature(session, Directory.class, new GraphDirectoryFeature(session, fileid)).mkdir( + cryptomator.getFeature(session, Write.class, new GraphWriteFeature(session, fileid)), encryptedFolder, new TransferStatus()); assertTrue(cryptomator.getFeature(session, Find.class, new DefaultFindFeature(session)).find(encryptedFolder)); - new CryptoTouchFeature<>(session, new DefaultTouchFeature<>(new GraphWriteFeature(session, fileid) - ), new GraphWriteFeature(session, fileid), cryptomator).touch(encryptedFile, new TransferStatus()); + new CryptoTouchFeature<>(session, new DefaultTouchFeature( + session), cryptomator).touch(new GraphWriteFeature(session, fileid), encryptedFile, new TransferStatus()); assertTrue(cryptomator.getFeature(session, Find.class, new DefaultFindFeature(session)).find(encryptedFile)); assertEquals(0L, cryptomator.getFeature(session, AttributesFinder.class, new GraphAttributesFinderFeature(session, fileid)).find(encryptedFile).getSize()); // move file outside vault @@ -264,10 +271,11 @@ public class MoveWorkerTest extends AbstractOneDriveTest { cryptomator.create(session, new VaultCredentials("test"), vaultVersion); final DefaultVaultRegistry registry = new DefaultVaultRegistry(new DisabledPasswordCallback(), cryptomator); session.withRegistry(registry); - cryptomator.getFeature(session, Directory.class, new GraphDirectoryFeature(session, fileid)).mkdir(encryptedFolder, new TransferStatus()); + cryptomator.getFeature(session, Directory.class, new GraphDirectoryFeature(session, fileid)).mkdir( + cryptomator.getFeature(session, Write.class, new GraphWriteFeature(session, fileid)), encryptedFolder, new TransferStatus()); assertTrue(cryptomator.getFeature(session, Find.class, new DefaultFindFeature(session)).find(encryptedFolder)); - new CryptoTouchFeature<>(session, new DefaultTouchFeature<>(new GraphWriteFeature(session, fileid) - ), new GraphWriteFeature(session, fileid), cryptomator).touch(encryptedFile, new TransferStatus()); + new CryptoTouchFeature<>(session, new DefaultTouchFeature( + session), cryptomator).touch(cryptomator.getFeature(session, Write.class, new GraphWriteFeature(session, fileid)), encryptedFile, new TransferStatus()); assertTrue(cryptomator.getFeature(session, Find.class, new DefaultFindFeature(session)).find(encryptedFile)); // move directory outside vault final Path directoryRenamed = new Path(home, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory)); diff --git a/onedrive/src/test/java/ch/cyberduck/core/cryptomator/OneDriveListServiceTest.java b/onedrive/src/test/java/ch/cyberduck/core/cryptomator/OneDriveListServiceTest.java index 6564edb490..b7a15b186e 100644 --- a/onedrive/src/test/java/ch/cyberduck/core/cryptomator/OneDriveListServiceTest.java +++ b/onedrive/src/test/java/ch/cyberduck/core/cryptomator/OneDriveListServiceTest.java @@ -24,6 +24,7 @@ import ch.cyberduck.core.SimplePathPredicate; import ch.cyberduck.core.cryptomator.features.CryptoListService; import ch.cyberduck.core.cryptomator.features.CryptoTouchFeature; import ch.cyberduck.core.features.Delete; +import ch.cyberduck.core.features.Write; import ch.cyberduck.core.onedrive.AbstractOneDriveTest; import ch.cyberduck.core.onedrive.GraphItemListService; import ch.cyberduck.core.onedrive.OneDriveHomeFinderService; @@ -39,6 +40,7 @@ import org.junit.Test; import org.junit.experimental.categories.Category; import org.junit.runner.RunWith; import org.junit.runners.Parameterized; +import org.nuxeo.onedrive.client.types.DriveItem; import java.util.Arrays; import java.util.EnumSet; @@ -59,8 +61,8 @@ public class OneDriveListServiceTest extends AbstractOneDriveTest { cryptomator.create(session, new VaultCredentials("test"), vaultVersion); session.withRegistry(new DefaultVaultRegistry(new DisabledPasswordCallback(), cryptomator)); assertTrue(new CryptoListService(session, new GraphItemListService(session, fileid), cryptomator).list(vault, new DisabledListProgressListener()).isEmpty()); - new CryptoTouchFeature<>(session, new DefaultTouchFeature<>(new GraphWriteFeature(session, fileid) - ), new GraphWriteFeature(session, fileid), cryptomator).touch(test, new TransferStatus()); + new CryptoTouchFeature<>(session, new DefaultTouchFeature( + session), cryptomator).touch(cryptomator.getFeature(session, Write.class, new GraphWriteFeature(session, fileid)), test, new TransferStatus()); assertEquals(new SimplePathPredicate(test), new SimplePathPredicate(new CryptoListService(session, new GraphItemListService(session, fileid), cryptomator).list(vault, new DisabledListProgressListener()).get(0))); cryptomator.getFeature(session, Delete.class, new GraphDeleteFeature(session, fileid)).delete(Arrays.asList(test, vault), new DisabledLoginCallback(), new Delete.DisabledCallback()); } diff --git a/onedrive/src/test/java/ch/cyberduck/core/onedrive/AbstractOneDriveTest.java b/onedrive/src/test/java/ch/cyberduck/core/onedrive/AbstractOneDriveTest.java index 69d247a996..26e9e131eb 100644 --- a/onedrive/src/test/java/ch/cyberduck/core/onedrive/AbstractOneDriveTest.java +++ b/onedrive/src/test/java/ch/cyberduck/core/onedrive/AbstractOneDriveTest.java @@ -68,7 +68,7 @@ public abstract class AbstractOneDriveTest extends AbstractGraphTest { public final static class TestPasswordStore extends DisabledPasswordStore { @Override public String getPassword(final String serviceName, final String accountName) { - if(accountName.equals("Microsoft OneDrive (dkocher@iterate.ch) OAuth2 Token Expiry")) { + if(accountName.equals("Microsoft OneDrive (sharepoint@iterategmbh.onmicrosoft.com) OAuth2 Token Expiry")) { return PROPERTIES.get("onedrive.tokenexpiry"); } return null; @@ -76,10 +76,10 @@ public abstract class AbstractOneDriveTest extends AbstractGraphTest { @Override public String getPassword(Scheme scheme, int port, String hostname, String user) { - if(user.endsWith("Microsoft OneDrive (dkocher@iterate.ch) OAuth2 Access Token")) { + if(user.endsWith("Microsoft OneDrive (sharepoint@iterategmbh.onmicrosoft.com) OAuth2 Access Token")) { return PROPERTIES.get("onedrive.accesstoken"); } - if(user.endsWith("Microsoft OneDrive (dkocher@iterate.ch) OAuth2 Refresh Token")) { + if(user.endsWith("Microsoft OneDrive (sharepoint@iterategmbh.onmicrosoft.com) OAuth2 Refresh Token")) { return PROPERTIES.get("onedrive.refreshtoken"); } return null; @@ -87,17 +87,17 @@ public abstract class AbstractOneDriveTest extends AbstractGraphTest { @Override public void addPassword(final String serviceName, final String accountName, final String password) { - if(accountName.equals("Microsoft OneDrive (dkocher@iterate.ch) OAuth2 Token Expiry")) { + if(accountName.equals("Microsoft OneDrive (sharepoint@iterategmbh.onmicrosoft.com) OAuth2 Token Expiry")) { VaultTest.add("onedrive.tokenexpiry", password); } } @Override public void addPassword(final Scheme scheme, final int port, final String hostname, final String user, final String password) { - if(user.equals("Microsoft OneDrive (dkocher@iterate.ch) OAuth2 Access Token")) { + if(user.equals("Microsoft OneDrive (sharepoint@iterategmbh.onmicrosoft.com) OAuth2 Access Token")) { VaultTest.add("onedrive.accesstoken", password); } - if(user.equals("Microsoft OneDrive (dkocher@iterate.ch) OAuth2 Refresh Token")) { + if(user.equals("Microsoft OneDrive (sharepoint@iterategmbh.onmicrosoft.com) OAuth2 Refresh Token")) { VaultTest.add("onedrive.refreshtoken", password); } } diff --git a/onedrive/src/test/java/ch/cyberduck/core/onedrive/BufferWriteFeatureTest.java b/onedrive/src/test/java/ch/cyberduck/core/onedrive/BufferWriteFeatureTest.java index 446093ce4b..2fa870372a 100644 --- a/onedrive/src/test/java/ch/cyberduck/core/onedrive/BufferWriteFeatureTest.java +++ b/onedrive/src/test/java/ch/cyberduck/core/onedrive/BufferWriteFeatureTest.java @@ -24,6 +24,7 @@ import ch.cyberduck.core.features.Delete; import ch.cyberduck.core.io.StatusOutputStream; import ch.cyberduck.core.io.StreamCopier; import ch.cyberduck.core.onedrive.features.GraphDeleteFeature; +import ch.cyberduck.core.onedrive.features.GraphFindFeature; import ch.cyberduck.core.onedrive.features.GraphReadFeature; import ch.cyberduck.core.shared.BufferWriteFeature; import ch.cyberduck.core.shared.DefaultFindFeature; @@ -60,7 +61,7 @@ public class BufferWriteFeatureTest extends AbstractOneDriveTest { assertEquals(content.length, count.getSent()); assertEquals(content.length, status.getLength()); assertNull(out.getStatus()); - assertTrue(new DefaultFindFeature(session).find(file)); + assertTrue(new GraphFindFeature(session, fileid).find(file)); final byte[] compare = new byte[content.length]; final InputStream stream = new GraphReadFeature(session, fileid).read(file, new TransferStatus().setLength(content.length), new DisabledConnectionCallback()); IOUtils.readFully(stream, compare); @@ -82,7 +83,7 @@ public class BufferWriteFeatureTest extends AbstractOneDriveTest { final ByteArrayInputStream in = new ByteArrayInputStream(content); new StreamCopier(status, status).transfer(in, out); assertNull(out.getStatus()); - assertTrue(new DefaultFindFeature(session).find(file)); + assertTrue(new GraphFindFeature(session, fileid).find(file)); final byte[] compare = new byte[content.length]; final InputStream stream = new GraphReadFeature(session, fileid).read(file, new TransferStatus().setLength(content.length), new DisabledConnectionCallback()); IOUtils.readFully(stream, compare); @@ -97,7 +98,7 @@ public class BufferWriteFeatureTest extends AbstractOneDriveTest { final ByteArrayInputStream in = new ByteArrayInputStream(content); new StreamCopier(status, status).transfer(in, out); assertNull(out.getStatus()); - assertTrue(new DefaultFindFeature(session).find(file)); + assertTrue(new GraphFindFeature(session, fileid).find(file)); final byte[] compare = new byte[content.length]; final InputStream stream = new GraphReadFeature(session, fileid).read(file, new TransferStatus().setLength(content.length), new DisabledConnectionCallback()); IOUtils.readFully(stream, compare); @@ -119,7 +120,7 @@ public class BufferWriteFeatureTest extends AbstractOneDriveTest { final ByteArrayInputStream in = new ByteArrayInputStream(content); new StreamCopier(status, status).transfer(in, out); assertNull(out.getStatus()); - assertTrue(new DefaultFindFeature(session).find(file)); + assertTrue(new GraphFindFeature(session, fileid).find(file)); final byte[] compare = new byte[content.length]; final InputStream stream = new GraphReadFeature(session, fileid).read(file, new TransferStatus().setLength(content.length), new DisabledConnectionCallback()); IOUtils.readFully(stream, compare); diff --git a/onedrive/src/test/java/ch/cyberduck/core/onedrive/GraphAttributesFinderFeatureTest.java b/onedrive/src/test/java/ch/cyberduck/core/onedrive/GraphAttributesFinderFeatureTest.java index bbbd4887f3..bcbe44205e 100644 --- a/onedrive/src/test/java/ch/cyberduck/core/onedrive/GraphAttributesFinderFeatureTest.java +++ b/onedrive/src/test/java/ch/cyberduck/core/onedrive/GraphAttributesFinderFeatureTest.java @@ -27,6 +27,7 @@ import ch.cyberduck.core.onedrive.features.GraphDeleteFeature; import ch.cyberduck.core.onedrive.features.GraphDirectoryFeature; import ch.cyberduck.core.onedrive.features.GraphFileIdProvider; import ch.cyberduck.core.onedrive.features.GraphTouchFeature; +import ch.cyberduck.core.onedrive.features.GraphWriteFeature; import ch.cyberduck.core.transfer.TransferStatus; import ch.cyberduck.test.IntegrationTest; @@ -56,14 +57,14 @@ public class GraphAttributesFinderFeatureTest extends AbstractOneDriveTest { @Test public void testFindFile() throws Exception { final Path file = new Path(new OneDriveHomeFinderService().find(), new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)); - new GraphTouchFeature(session, fileid).touch(file, new TransferStatus().setMime("x-application/cyberduck")); + new GraphTouchFeature(session, fileid).touch(new GraphWriteFeature(session, fileid), file, new TransferStatus().setMime("x-application/cyberduck")); final PathAttributes attributes = new GraphAttributesFinderFeature(session, fileid).find(file); assertNotNull(attributes); assertNotEquals(-1L, attributes.getSize()); assertNotEquals(-1L, attributes.getCreationDate()); assertNotEquals(-1L, attributes.getModificationDate()); assertNotNull(attributes.getETag()); - assertNull(attributes.getVersionId()); + assertNotNull(attributes.getVersionId()); assertNotNull(attributes.getLink()); assertNotNull(attributes.getFileId()); new GraphDeleteFeature(session, fileid).delete(Collections.singletonList(file), new DisabledLoginCallback(), new Delete.DisabledCallback()); @@ -72,7 +73,7 @@ public class GraphAttributesFinderFeatureTest extends AbstractOneDriveTest { @Test public void testFindDirectory() throws Exception { final Path file = new Path(new OneDriveHomeFinderService().find(), new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory)); - new GraphDirectoryFeature(session, fileid).mkdir(file, new TransferStatus()); + new GraphDirectoryFeature(session, fileid).mkdir(new GraphWriteFeature(session, fileid), file, new TransferStatus()); final PathAttributes attributes = new GraphAttributesFinderFeature(session, fileid).find(file); assertNotNull(attributes); assertNotEquals(-1L, attributes.getSize()); @@ -89,10 +90,10 @@ public class GraphAttributesFinderFeatureTest extends AbstractOneDriveTest { public void testChangedFileId() throws Exception { final GraphFileIdProvider fileid = new GraphFileIdProvider(session); final Path drive = new OneDriveHomeFinderService().find(); - final Path test = new GraphTouchFeature(session, fileid).touch(new Path(drive, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)), new TransferStatus()); + final Path test = new GraphTouchFeature(session, fileid).touch(new GraphWriteFeature(session, fileid), new Path(drive, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)), new TransferStatus()); final String previousnodeid = test.attributes().getFileId(); new GraphDeleteFeature(session, fileid).delete(Collections.singletonList(test), new DisabledLoginCallback(), new Delete.DisabledCallback()); - final String latestnodeid = new GraphTouchFeature(session, fileid).touch(new Path(drive, test.getName(), EnumSet.of(Path.Type.file)), new TransferStatus()).attributes().getFileId(); + final String latestnodeid = new GraphTouchFeature(session, fileid).touch(new GraphWriteFeature(session, fileid), new Path(drive, test.getName(), EnumSet.of(Path.Type.file)), new TransferStatus()).attributes().getFileId(); assertNotNull(latestnodeid); // Assume previously seen but changed on server fileid.cache(test, previousnodeid); diff --git a/onedrive/src/test/java/ch/cyberduck/core/onedrive/GraphCopyFeatureTest.java b/onedrive/src/test/java/ch/cyberduck/core/onedrive/GraphCopyFeatureTest.java index 34b874fffb..87ff573783 100644 --- a/onedrive/src/test/java/ch/cyberduck/core/onedrive/GraphCopyFeatureTest.java +++ b/onedrive/src/test/java/ch/cyberduck/core/onedrive/GraphCopyFeatureTest.java @@ -28,8 +28,9 @@ import ch.cyberduck.core.onedrive.features.GraphAttributesFinderFeature; import ch.cyberduck.core.onedrive.features.GraphCopyFeature; import ch.cyberduck.core.onedrive.features.GraphDeleteFeature; import ch.cyberduck.core.onedrive.features.GraphDirectoryFeature; +import ch.cyberduck.core.onedrive.features.GraphFindFeature; import ch.cyberduck.core.onedrive.features.GraphTouchFeature; -import ch.cyberduck.core.shared.DefaultFindFeature; +import ch.cyberduck.core.onedrive.features.GraphWriteFeature; import ch.cyberduck.core.transfer.TransferStatus; import ch.cyberduck.test.IntegrationTest; @@ -51,7 +52,7 @@ public class GraphCopyFeatureTest extends AbstractOneDriveTest { final Touch touch = new GraphTouchFeature(session, fileid); final Delete delete = new GraphDeleteFeature(session, fileid); final Path drive = new OneDriveHomeFinderService().find(); - final Path file = touch.touch(new Path(drive, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)), new TransferStatus().setMime("x-application/cyberduck")); + final Path file = touch.touch(new GraphWriteFeature(session, fileid), new Path(drive, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)), new TransferStatus().setMime("x-application/cyberduck")); final String fileid = file.attributes().getFileId(); delete.delete(Collections.singletonList(file), new DisabledLoginCallback(), new Delete.DisabledCallback()); final Path target = new Path(drive, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)); @@ -64,10 +65,10 @@ public class GraphCopyFeatureTest extends AbstractOneDriveTest { public void testCopy() throws Exception { final Path drive = new OneDriveHomeFinderService().find(); Path directory = new GraphDirectoryFeature(session, fileid).mkdir( - new Path(drive, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory)), new TransferStatus()); + new GraphWriteFeature(session, fileid), new Path(drive, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory)), new TransferStatus()); assertNotNull(new GraphAttributesFinderFeature(session, fileid).find(directory)); final TransferStatus status = new TransferStatus(); - Path file = new GraphTouchFeature(session, fileid).touch(new Path(drive, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)), status.setMime("x-application/cyberduck")); + Path file = new GraphTouchFeature(session, fileid).touch(new GraphWriteFeature(session, fileid), new Path(drive, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)), status.setMime("x-application/cyberduck")); assertNotNull(new GraphAttributesFinderFeature(session, fileid).find(file)); Path rename = new Path(directory, file.getName(), EnumSet.of(Path.Type.file)); final GraphCopyFeature copy = new GraphCopyFeature(session, fileid); @@ -81,11 +82,11 @@ public class GraphCopyFeatureTest extends AbstractOneDriveTest { @Test public void testCopyToExistingFile() throws Exception { final Path folder = new GraphDirectoryFeature(session, fileid).mkdir( - new Path(new OneDriveHomeFinderService().find(), new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory)), new TransferStatus()); - final Path test = new GraphTouchFeature(session, fileid).touch(new Path(folder, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)), new TransferStatus()); - final Path copy = new GraphTouchFeature(session, fileid).touch(new Path(folder, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)), new TransferStatus()); + new GraphWriteFeature(session, fileid), new Path(new OneDriveHomeFinderService().find(), new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory)), new TransferStatus()); + final Path test = new GraphTouchFeature(session, fileid).touch(new GraphWriteFeature(session, fileid), new Path(folder, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)), new TransferStatus()); + final Path copy = new GraphTouchFeature(session, fileid).touch(new GraphWriteFeature(session, fileid), new Path(folder, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)), new TransferStatus()); new GraphCopyFeature(session, fileid).copy(test, copy, new TransferStatus().setExists(true), new DisabledConnectionCallback(), new DisabledStreamListener()); - final Find find = new DefaultFindFeature(session); + final Find find = new GraphFindFeature(session, fileid); assertTrue(find.find(test)); assertTrue(find.find(copy)); new GraphDeleteFeature(session, fileid).delete(Arrays.asList(test, copy), new DisabledLoginCallback(), new Delete.DisabledCallback()); diff --git a/onedrive/src/test/java/ch/cyberduck/core/onedrive/GraphDirectoryFeatureTest.java b/onedrive/src/test/java/ch/cyberduck/core/onedrive/GraphDirectoryFeatureTest.java index a16e339ecb..a6cb49e878 100644 --- a/onedrive/src/test/java/ch/cyberduck/core/onedrive/GraphDirectoryFeatureTest.java +++ b/onedrive/src/test/java/ch/cyberduck/core/onedrive/GraphDirectoryFeatureTest.java @@ -27,6 +27,7 @@ import ch.cyberduck.core.features.Delete; import ch.cyberduck.core.onedrive.features.GraphAttributesFinderFeature; import ch.cyberduck.core.onedrive.features.GraphDeleteFeature; import ch.cyberduck.core.onedrive.features.GraphDirectoryFeature; +import ch.cyberduck.core.onedrive.features.GraphWriteFeature; import ch.cyberduck.core.transfer.TransferStatus; import ch.cyberduck.test.IntegrationTest; @@ -44,11 +45,11 @@ public class GraphDirectoryFeatureTest extends AbstractOneDriveTest { @Test public void testMkdir() throws Exception { final TransferStatus status = new TransferStatus(); - final Path target = new GraphDirectoryFeature(session, fileid).mkdir(new Path(new OneDriveHomeFinderService().find(), new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory)), status); + final Path target = new GraphDirectoryFeature(session, fileid).mkdir(new GraphWriteFeature(session, fileid), new Path(new OneDriveHomeFinderService().find(), new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory)), status); final PathAttributes attributes = new GraphAttributesFinderFeature(session, fileid).find(target); assertNotNull(attributes.getETag()); assertEquals(target.attributes().getFileId(), attributes.getFileId()); - assertThrows(ConflictException.class, () -> new GraphDirectoryFeature(session, fileid).mkdir(target, new TransferStatus())); + assertThrows(ConflictException.class, () -> new GraphDirectoryFeature(session, fileid).mkdir(new GraphWriteFeature(session, fileid), target, new TransferStatus())); new GraphDeleteFeature(session, fileid).delete(Collections.singletonList(target), new DisabledLoginCallback(), new Delete.DisabledCallback()); } @@ -56,7 +57,7 @@ public class GraphDirectoryFeatureTest extends AbstractOneDriveTest { public void testWhitespaceMkdir() throws Exception { final RandomStringService randomStringService = new AlphanumericRandomStringService(); final String name = String.format("%s %s", randomStringService.random(), randomStringService.random()); - final Path target = new GraphDirectoryFeature(session, fileid).mkdir(new Path(new OneDriveHomeFinderService().find(), name, EnumSet.of(Path.Type.directory)), null); + final Path target = new GraphDirectoryFeature(session, fileid).mkdir(new GraphWriteFeature(session, fileid), new Path(new OneDriveHomeFinderService().find(), name, EnumSet.of(Path.Type.directory)), null); assertEquals(name, target.getName()); final AttributedList list = new GraphItemListService(session, fileid).list(new OneDriveHomeFinderService().find(), new DisabledListProgressListener()); assertTrue(list.contains(target)); diff --git a/onedrive/src/test/java/ch/cyberduck/core/onedrive/GraphFileIdProviderTest.java b/onedrive/src/test/java/ch/cyberduck/core/onedrive/GraphFileIdProviderTest.java index a2d9fece5f..460f7c2c18 100644 --- a/onedrive/src/test/java/ch/cyberduck/core/onedrive/GraphFileIdProviderTest.java +++ b/onedrive/src/test/java/ch/cyberduck/core/onedrive/GraphFileIdProviderTest.java @@ -12,6 +12,7 @@ import ch.cyberduck.core.onedrive.features.GraphDeleteFeature; import ch.cyberduck.core.onedrive.features.GraphDirectoryFeature; import ch.cyberduck.core.onedrive.features.GraphFileIdProvider; import ch.cyberduck.core.onedrive.features.GraphTouchFeature; +import ch.cyberduck.core.onedrive.features.GraphWriteFeature; import ch.cyberduck.core.transfer.TransferStatus; import ch.cyberduck.test.IntegrationTest; @@ -46,9 +47,9 @@ public class GraphFileIdProviderTest extends AbstractOneDriveTest { // } final Directory directoryFeature = new GraphDirectoryFeature(session, fileid); - final Path path2RWithId = directoryFeature.mkdir(path2R, new TransferStatus()); + final Path path2RWithId = directoryFeature.mkdir(new GraphWriteFeature(session, fileid), path2R, new TransferStatus()); assertNotNull(path2RWithId.attributes().getFileId()); - final Path path33WithId = directoryFeature.mkdir(path33, new TransferStatus()); + final Path path33WithId = directoryFeature.mkdir(new GraphWriteFeature(session, fileid), path33, new TransferStatus()); assertNotNull(path33WithId.attributes().getFileId()); assertNotEquals(path2RWithId.attributes().getFileId(), path33WithId.attributes().getFileId()); @@ -66,7 +67,7 @@ public class GraphFileIdProviderTest extends AbstractOneDriveTest { final GraphFileIdProvider nodeid = new GraphFileIdProvider(session); final Path home = new OneDriveHomeFinderService().find(); final String name = String.format("%s", new AlphanumericRandomStringService().random()); - final Path file = new GraphTouchFeature(session, nodeid).touch(new Path(home, name, EnumSet.of(Path.Type.file)), new TransferStatus()); + final Path file = new GraphTouchFeature(session, nodeid).touch(new GraphWriteFeature(session, fileid), new Path(home, name, EnumSet.of(Path.Type.file)), new TransferStatus()); nodeid.clear(); final String nodeId = nodeid.getFileId(new Path(home, name, EnumSet.of(Path.Type.file))); assertNotNull(nodeId); diff --git a/onedrive/src/test/java/ch/cyberduck/core/onedrive/GraphFindFeatureTest.java b/onedrive/src/test/java/ch/cyberduck/core/onedrive/GraphFindFeatureTest.java index 78a1d547db..1f77ab026d 100644 --- a/onedrive/src/test/java/ch/cyberduck/core/onedrive/GraphFindFeatureTest.java +++ b/onedrive/src/test/java/ch/cyberduck/core/onedrive/GraphFindFeatureTest.java @@ -23,7 +23,7 @@ import ch.cyberduck.core.onedrive.features.GraphDeleteFeature; import ch.cyberduck.core.onedrive.features.GraphDirectoryFeature; import ch.cyberduck.core.onedrive.features.GraphFindFeature; import ch.cyberduck.core.onedrive.features.GraphTouchFeature; -import ch.cyberduck.core.shared.DefaultHomeFinderService; +import ch.cyberduck.core.onedrive.features.GraphWriteFeature; import ch.cyberduck.core.transfer.TransferStatus; import ch.cyberduck.test.IntegrationTest; @@ -43,7 +43,7 @@ public class GraphFindFeatureTest extends AbstractOneDriveTest { @Test public void testFindDirectory() throws Exception { final Path folder = new GraphDirectoryFeature(session, fileid).mkdir( - new Path(new OneDriveHomeFinderService().find(), new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory)), new TransferStatus()); + new GraphWriteFeature(session, fileid), new Path(new OneDriveHomeFinderService().find(), new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory)), new TransferStatus()); assertTrue(new GraphFindFeature(session, fileid).find(folder)); assertFalse(new GraphFindFeature(session, fileid).find(new Path(folder.getAbsolute(), EnumSet.of(Path.Type.file)))); new GraphDeleteFeature(session, fileid).delete(Collections.singletonList(folder), new DisabledLoginCallback(), new Delete.DisabledCallback()); @@ -52,7 +52,7 @@ public class GraphFindFeatureTest extends AbstractOneDriveTest { @Test public void testFindFile() throws Exception { final Path file = new Path(new OneDriveHomeFinderService().find(), new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)); - new GraphTouchFeature(session, fileid).touch(file, new TransferStatus()); + new GraphTouchFeature(session, fileid).touch(new GraphWriteFeature(session, fileid), file, new TransferStatus()); assertTrue(new GraphFindFeature(session, fileid).find(file)); assertFalse(new GraphFindFeature(session, fileid).find(new Path(file.getAbsolute(), EnumSet.of(Path.Type.directory)))); new GraphDeleteFeature(session, fileid).delete(Collections.singletonList(file), new DisabledLoginCallback(), new Delete.DisabledCallback()); diff --git a/onedrive/src/test/java/ch/cyberduck/core/onedrive/GraphItemListServiceTest.java b/onedrive/src/test/java/ch/cyberduck/core/onedrive/GraphItemListServiceTest.java index 03005461d4..631bbe62e8 100644 --- a/onedrive/src/test/java/ch/cyberduck/core/onedrive/GraphItemListServiceTest.java +++ b/onedrive/src/test/java/ch/cyberduck/core/onedrive/GraphItemListServiceTest.java @@ -30,6 +30,7 @@ import ch.cyberduck.core.onedrive.features.GraphAttributesFinderFeature; import ch.cyberduck.core.onedrive.features.GraphDeleteFeature; import ch.cyberduck.core.onedrive.features.GraphDirectoryFeature; import ch.cyberduck.core.onedrive.features.GraphTouchFeature; +import ch.cyberduck.core.onedrive.features.GraphWriteFeature; import ch.cyberduck.core.transfer.TransferStatus; import ch.cyberduck.test.IntegrationTest; @@ -47,9 +48,9 @@ public class GraphItemListServiceTest extends AbstractOneDriveTest { @Test public void testListLexicographically() throws Exception { - final Path directory = new GraphDirectoryFeature(session, fileid).mkdir(new Path(new OneDriveHomeFinderService().find(), new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory)), new TransferStatus()); - final Path f2 = new GraphTouchFeature(session, fileid).touch(new Path(directory, "aa", EnumSet.of(Path.Type.file)), new TransferStatus()); - final Path f1 = new GraphTouchFeature(session, fileid).touch(new Path(directory, "a", EnumSet.of(Path.Type.file)), new TransferStatus()); + final Path directory = new GraphDirectoryFeature(session, fileid).mkdir(new GraphWriteFeature(session, fileid), new Path(new OneDriveHomeFinderService().find(), new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory)), new TransferStatus()); + final Path f2 = new GraphTouchFeature(session, fileid).touch(new GraphWriteFeature(session, fileid), new Path(directory, "aa", EnumSet.of(Path.Type.file)), new TransferStatus()); + final Path f1 = new GraphTouchFeature(session, fileid).touch(new GraphWriteFeature(session, fileid), new Path(directory, "a", EnumSet.of(Path.Type.file)), new TransferStatus()); final AttributedList list = new GraphItemListService(session, fileid).list(directory, new DisabledListProgressListener()); assertEquals(2, list.size()); assertEquals(new SimplePathPredicate(f1), new SimplePathPredicate(list.get(0))); @@ -67,7 +68,7 @@ public class GraphItemListServiceTest extends AbstractOneDriveTest { public void testListDriveChildren() throws Exception { final Path drive = new OneDriveHomeFinderService().find(); final Path file = new Path(drive, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)); - new GraphTouchFeature(session, fileid).touch(file, new TransferStatus()); + new GraphTouchFeature(session, fileid).touch(new GraphWriteFeature(session, fileid), file, new TransferStatus()); assertNotNull(new GraphAttributesFinderFeature(session, fileid).find(file)); final AttributedList list = new GraphItemListService(session, fileid).list(drive, new DisabledListProgressListener()); assertFalse(list.isEmpty()); @@ -90,7 +91,7 @@ public class GraphItemListServiceTest extends AbstractOneDriveTest { @Test public void testWhitespacedChild() throws Exception { final RandomStringService randomStringService = new AlphanumericRandomStringService(); - final Path target = new GraphDirectoryFeature(session, fileid).mkdir(new Path(new OneDriveHomeFinderService().find(), String.format("%s %s", randomStringService.random(), randomStringService.random()), EnumSet.of(Path.Type.directory)), null); + final Path target = new GraphDirectoryFeature(session, fileid).mkdir(new GraphWriteFeature(session, fileid), new Path(new OneDriveHomeFinderService().find(), String.format("%s %s", randomStringService.random(), randomStringService.random()), EnumSet.of(Path.Type.directory)), null); final AttributedList list = new GraphItemListService(session, fileid).list(target, new DisabledListProgressListener()); new GraphDeleteFeature(session, fileid).delete(Collections.singletonList(target), new DisabledLoginCallback(), new Delete.DisabledCallback()); } diff --git a/onedrive/src/test/java/ch/cyberduck/core/onedrive/GraphLockFeatureTest.java b/onedrive/src/test/java/ch/cyberduck/core/onedrive/GraphLockFeatureTest.java index b023a08b63..5092ac5eb2 100644 --- a/onedrive/src/test/java/ch/cyberduck/core/onedrive/GraphLockFeatureTest.java +++ b/onedrive/src/test/java/ch/cyberduck/core/onedrive/GraphLockFeatureTest.java @@ -27,6 +27,7 @@ import ch.cyberduck.core.onedrive.features.GraphAttributesFinderFeature; import ch.cyberduck.core.onedrive.features.GraphDeleteFeature; import ch.cyberduck.core.onedrive.features.GraphLockFeature; import ch.cyberduck.core.onedrive.features.GraphTouchFeature; +import ch.cyberduck.core.onedrive.features.GraphWriteFeature; import ch.cyberduck.core.transfer.TransferStatus; import ch.cyberduck.test.IntegrationTest; @@ -48,7 +49,7 @@ public class GraphLockFeatureTest extends AbstractSharepointTest { final ListService list = new SharepointListService(session, fileid); final AttributedList drives = list.list(new Path(SharepointListService.DEFAULT_NAME, DRIVES_CONTAINER, EnumSet.of(Path.Type.directory)), new DisabledListProgressListener()); final Path drive = drives.get(0); - final Path file = new GraphTouchFeature(session, fileid).touch(new Path(drive, + final Path file = new GraphTouchFeature(session, fileid).touch(new GraphWriteFeature(session, fileid), new Path(drive, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)), new TransferStatus()); final PathAttributes attr = new GraphAttributesFinderFeature(session, fileid).find(file); final GraphLockFeature feature = new GraphLockFeature(session, fileid); diff --git a/onedrive/src/test/java/ch/cyberduck/core/onedrive/GraphMoveFeatureTest.java b/onedrive/src/test/java/ch/cyberduck/core/onedrive/GraphMoveFeatureTest.java index a5646f39d3..fcff1b4c96 100644 --- a/onedrive/src/test/java/ch/cyberduck/core/onedrive/GraphMoveFeatureTest.java +++ b/onedrive/src/test/java/ch/cyberduck/core/onedrive/GraphMoveFeatureTest.java @@ -33,9 +33,10 @@ import ch.cyberduck.core.features.Touch; import ch.cyberduck.core.onedrive.features.GraphAttributesFinderFeature; import ch.cyberduck.core.onedrive.features.GraphDeleteFeature; import ch.cyberduck.core.onedrive.features.GraphDirectoryFeature; +import ch.cyberduck.core.onedrive.features.GraphFindFeature; import ch.cyberduck.core.onedrive.features.GraphMoveFeature; import ch.cyberduck.core.onedrive.features.GraphTouchFeature; -import ch.cyberduck.core.shared.DefaultFindFeature; +import ch.cyberduck.core.onedrive.features.GraphWriteFeature; import ch.cyberduck.core.transfer.TransferStatus; import ch.cyberduck.test.IntegrationTest; @@ -57,7 +58,7 @@ public class GraphMoveFeatureTest extends AbstractOneDriveTest { final Touch touch = new GraphTouchFeature(session, fileid); final Delete delete = new GraphDeleteFeature(session, fileid); final Path drive = new OneDriveHomeFinderService().find(); - final Path file = touch.touch(new Path(drive, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)), new TransferStatus().setMime("x-application/cyberduck")); + final Path file = touch.touch(new GraphWriteFeature(session, fileid), new Path(drive, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)), new TransferStatus().setMime("x-application/cyberduck")); final String fileid = file.attributes().getFileId(); delete.delete(Collections.singletonList(file), new DisabledLoginCallback(), new Delete.DisabledCallback()); final Path target = new Path(drive, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)); @@ -73,16 +74,19 @@ public class GraphMoveFeatureTest extends AbstractOneDriveTest { final Delete delete = new GraphDeleteFeature(session, fileid); final AttributesFinder attributesFinder = new GraphAttributesFinderFeature(session, fileid); final Path drive = new OneDriveHomeFinderService().find(); - final Path file = touch.touch(new Path(drive, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)), new TransferStatus().setMime("x-application/cyberduck")); + final Path file = touch.touch(new GraphWriteFeature(session, fileid), new Path(drive, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)), new TransferStatus().setMime("x-application/cyberduck")); final PathAttributes attributes = attributesFinder.find(file); assertNotNull(attributes); assertEquals(file.attributes().getFileId(), attributes.getFileId()); Path rename = new Path(drive, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)); assertTrue(move.isSupported(file, Optional.of(rename))); final TransferStatus status = new TransferStatus(); + status.setModified(file.attributes().getModificationDate()); final Path target = move.move(file, rename, status, new Delete.DisabledCallback(), new DisabledConnectionCallback()); assertEquals(attributes, target.attributes()); assertEquals(attributes.getFileId(), target.attributes().getFileId()); + assertEquals(attributes.getModificationDate(), target.attributes().getModificationDate()); + assertEquals(attributes.getVersionId(), target.attributes().getVersionId()); assertNotEquals(attributes.getETag(), attributesFinder.find(rename).getETag()); assertEquals(target.attributes().getETag(), attributesFinder.find(rename).getETag()); delete.delete(Collections.singletonList(rename), new DisabledLoginCallback(), new Delete.DisabledCallback()); @@ -97,20 +101,24 @@ public class GraphMoveFeatureTest extends AbstractOneDriveTest { final AttributesFinder attributesFinder = new GraphAttributesFinderFeature(session, fileid); final Path drive = new OneDriveHomeFinderService().find(); Path targetDirectory = new Path(drive, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory)); - directory.mkdir(targetDirectory, new TransferStatus()); + directory.mkdir(new GraphWriteFeature(session, fileid), targetDirectory, new TransferStatus()); assertNotNull(attributesFinder.find(targetDirectory)); Path touchedFile = new Path(drive, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)); - touch.touch(touchedFile, new TransferStatus().setMime("x-application/cyberduck")); + touch.touch(new GraphWriteFeature(session, fileid), touchedFile, new TransferStatus().setMime("x-application/cyberduck")); final PathAttributes attributes = attributesFinder.find(touchedFile); Path rename = new Path(targetDirectory, touchedFile.getName(), EnumSet.of(Path.Type.file)); assertTrue(move.isSupported(touchedFile, Optional.of(rename))); - final Path target = move.move(touchedFile, rename, new TransferStatus(), new Delete.DisabledCallback(), new DisabledConnectionCallback()); + final TransferStatus status = new TransferStatus(); + status.setModified(touchedFile.attributes().getModificationDate()); + final Path target = move.move(touchedFile, rename, status, new Delete.DisabledCallback(), new DisabledConnectionCallback()); final PathAttributes renamedAttributes = attributesFinder.find(rename); assertNotNull(renamedAttributes); assertEquals(attributes, renamedAttributes); assertNotEquals(attributes.getETag(), renamedAttributes.getETag()); + assertEquals(attributes.getModificationDate(), renamedAttributes.getModificationDate()); + assertEquals(attributes.getVersionId(), renamedAttributes.getVersionId()); assertEquals(target.attributes().getETag(), renamedAttributes.getETag()); delete.delete(Collections.singletonList(targetDirectory), new DisabledLoginCallback(), new Delete.DisabledCallback()); @@ -125,11 +133,11 @@ public class GraphMoveFeatureTest extends AbstractOneDriveTest { final AttributesFinder attributesFinder = new GraphAttributesFinderFeature(session, fileid); final Path drive = new OneDriveHomeFinderService().find(); Path targetDirectory = new Path(drive, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory)); - directory.mkdir(targetDirectory, new TransferStatus()); + directory.mkdir(new GraphWriteFeature(session, fileid), targetDirectory, new TransferStatus()); assertNotNull(attributesFinder.find(targetDirectory)); Path touchedFile = new Path(targetDirectory, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)); - touch.touch(touchedFile, new TransferStatus().setMime("x-application/cyberduck")); + touch.touch(new GraphWriteFeature(session, fileid), touchedFile, new TransferStatus().setMime("x-application/cyberduck")); assertNotNull(attributesFinder.find(touchedFile)); Path rename = new Path(drive, touchedFile.getName(), EnumSet.of(Path.Type.file)); @@ -150,11 +158,11 @@ public class GraphMoveFeatureTest extends AbstractOneDriveTest { final AttributesFinder attributesFinder = new GraphAttributesFinderFeature(session, fileid); final Path drive = new OneDriveHomeFinderService().find(); Path targetDirectory = new Path(drive, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory)); - directory.mkdir(targetDirectory, new TransferStatus()); + directory.mkdir(new GraphWriteFeature(session, fileid), targetDirectory, new TransferStatus()); assertNotNull(attributesFinder.find(targetDirectory)); Path touchedFile = new Path(drive, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)); - touch.touch(touchedFile, new TransferStatus().setMime("x-application/cyberduck")); + touch.touch(new GraphWriteFeature(session, fileid), touchedFile, new TransferStatus().setMime("x-application/cyberduck")); assertNotNull(attributesFinder.find(touchedFile)); Path rename = new Path(targetDirectory, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)); @@ -167,11 +175,11 @@ public class GraphMoveFeatureTest extends AbstractOneDriveTest { @Test public void testMoveToExistingFile() throws Exception { - final Path folder = new GraphDirectoryFeature(session, fileid).mkdir(new Path(new OneDriveHomeFinderService().find(), new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory)), new TransferStatus()); - final Path test = new GraphTouchFeature(session, fileid).touch(new Path(folder, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)), new TransferStatus()); - final Path temp = new GraphTouchFeature(session, fileid).touch(new Path(folder, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)), new TransferStatus()); + final Path folder = new GraphDirectoryFeature(session, fileid).mkdir(new GraphWriteFeature(session, fileid), new Path(new OneDriveHomeFinderService().find(), new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory)), new TransferStatus()); + final Path test = new GraphTouchFeature(session, fileid).touch(new GraphWriteFeature(session, fileid), new Path(folder, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)), new TransferStatus()); + final Path temp = new GraphTouchFeature(session, fileid).touch(new GraphWriteFeature(session, fileid), new Path(folder, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)), new TransferStatus()); new GraphMoveFeature(session, fileid).move(temp, test, new TransferStatus().setExists(true), new Delete.DisabledCallback(), new DisabledConnectionCallback()); - final Find find = new DefaultFindFeature(session); + final Find find = new GraphFindFeature(session, fileid); final AttributedList files = new GraphItemListService(session, fileid).list(folder, new DisabledListProgressListener()); assertEquals(1, files.size()); assertFalse(find.find(temp)); @@ -183,7 +191,7 @@ public class GraphMoveFeatureTest extends AbstractOneDriveTest { public void testRenameCaseOnly() throws Exception { final Path home = new OneDriveHomeFinderService().find(); final String name = new AlphanumericRandomStringService().random(); - final Path file = new GraphTouchFeature(session, fileid).touch(new Path(home, StringUtils.capitalize(name), EnumSet.of(Path.Type.file)), new TransferStatus()); + final Path file = new GraphTouchFeature(session, fileid).touch(new GraphWriteFeature(session, fileid), new Path(home, StringUtils.capitalize(name), EnumSet.of(Path.Type.file)), new TransferStatus()); final Path rename = new Path(home, StringUtils.lowerCase(name), EnumSet.of(Path.Type.file)); new GraphMoveFeature(session, fileid).move(file, rename, new TransferStatus().setExists(true), new Delete.DisabledCallback(), new DisabledConnectionCallback()); new GraphDeleteFeature(session, fileid).delete(Collections.singletonList(rename), new DisabledLoginCallback(), new Delete.DisabledCallback()); diff --git a/onedrive/src/test/java/ch/cyberduck/core/onedrive/GraphReadFeatureTest.java b/onedrive/src/test/java/ch/cyberduck/core/onedrive/GraphReadFeatureTest.java index 30cd03ba55..24953e9bc7 100644 --- a/onedrive/src/test/java/ch/cyberduck/core/onedrive/GraphReadFeatureTest.java +++ b/onedrive/src/test/java/ch/cyberduck/core/onedrive/GraphReadFeatureTest.java @@ -38,6 +38,7 @@ import org.apache.commons.io.IOUtils; import org.apache.commons.lang3.RandomUtils; import org.junit.Test; import org.junit.experimental.categories.Category; +import org.nuxeo.onedrive.client.types.DriveItem; import java.io.ByteArrayOutputStream; import java.io.InputStream; @@ -61,7 +62,7 @@ public class GraphReadFeatureTest extends AbstractOneDriveTest { public void testReadInterrupt() throws Exception { final Path drive = new OneDriveHomeFinderService().find(); final Path test = new Path(drive, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)); - new GraphTouchFeature(session, fileid).touch(test, new TransferStatus()); + new GraphTouchFeature(session, fileid).touch(new GraphWriteFeature(session, fileid), test, new TransferStatus()); // Unknown length in status final TransferStatus status = new TransferStatus(); // Read a single byte @@ -82,7 +83,7 @@ public class GraphReadFeatureTest extends AbstractOneDriveTest { public void testReadRange() throws Exception { final Path drive = new OneDriveHomeFinderService().find(); final Path test = new Path(drive, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)); - new GraphTouchFeature(session, fileid).touch(test, new TransferStatus()); + new GraphTouchFeature(session, fileid).touch(new GraphWriteFeature(session, fileid), test, new TransferStatus()); final Local local = new Local(System.getProperty("java.io.tmpdir"), new AlphanumericRandomStringService().random()); final byte[] content = RandomUtils.nextBytes(1000); @@ -90,8 +91,8 @@ public class GraphReadFeatureTest extends AbstractOneDriveTest { assertNotNull(out); IOUtils.write(content, out); out.close(); - new DefaultUploadFeature<>(new GraphWriteFeature(session, fileid)).upload( - test, local, new BandwidthThrottle(BandwidthThrottle.UNLIMITED), new DisabledProgressListener(), new DisabledStreamListener(), + new DefaultUploadFeature(session).upload( + new GraphWriteFeature(session, fileid), test, local, new BandwidthThrottle(BandwidthThrottle.UNLIMITED), new DisabledProgressListener(), new DisabledStreamListener(), new TransferStatus().setLength(content.length), new DisabledConnectionCallback()); final TransferStatus status = new TransferStatus(); @@ -115,7 +116,7 @@ public class GraphReadFeatureTest extends AbstractOneDriveTest { public void testReadInvalidRange() throws Exception { final Path drive = new OneDriveHomeFinderService().find(); final Path test = new Path(drive, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)); - new GraphTouchFeature(session, fileid).touch(test, new TransferStatus()); + new GraphTouchFeature(session, fileid).touch(new GraphWriteFeature(session, fileid), test, new TransferStatus()); final GraphReadFeature read = new GraphReadFeature(session, fileid); final InputStream in = read.read(test, new TransferStatus().setOffset(1).setAppend(true), new DisabledConnectionCallback()); assertNull(in); @@ -125,7 +126,7 @@ public class GraphReadFeatureTest extends AbstractOneDriveTest { public void testReadRangeUnknownLength() throws Exception { final Path drive = new OneDriveHomeFinderService().find(); final Path test = new Path(drive, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)); - new GraphTouchFeature(session, fileid).touch(test, new TransferStatus()); + new GraphTouchFeature(session, fileid).touch(new GraphWriteFeature(session, fileid), test, new TransferStatus()); final Local local = new Local(System.getProperty("java.io.tmpdir"), new AlphanumericRandomStringService().random()); final byte[] content = RandomUtils.nextBytes(1000); @@ -133,8 +134,8 @@ public class GraphReadFeatureTest extends AbstractOneDriveTest { assertNotNull(out); IOUtils.write(content, out); out.close(); - new DefaultUploadFeature<>(new GraphWriteFeature(session, fileid)).upload( - test, local, new BandwidthThrottle(BandwidthThrottle.UNLIMITED), new DisabledProgressListener(), new DisabledStreamListener(), + new DefaultUploadFeature(session).upload( + new GraphWriteFeature(session, fileid), test, local, new BandwidthThrottle(BandwidthThrottle.UNLIMITED), new DisabledProgressListener(), new DisabledStreamListener(), new TransferStatus().setLength(content.length), new DisabledConnectionCallback()); final TransferStatus status = new TransferStatus(); diff --git a/onedrive/src/test/java/ch/cyberduck/core/onedrive/GraphShareTest.java b/onedrive/src/test/java/ch/cyberduck/core/onedrive/GraphShareTest.java index 1b8346ba30..731b0468c5 100644 --- a/onedrive/src/test/java/ch/cyberduck/core/onedrive/GraphShareTest.java +++ b/onedrive/src/test/java/ch/cyberduck/core/onedrive/GraphShareTest.java @@ -25,6 +25,7 @@ import ch.cyberduck.core.features.Share; import ch.cyberduck.core.onedrive.features.GraphDeleteFeature; import ch.cyberduck.core.onedrive.features.GraphSharedLinkFeature; import ch.cyberduck.core.onedrive.features.GraphTouchFeature; +import ch.cyberduck.core.onedrive.features.GraphWriteFeature; import ch.cyberduck.core.transfer.TransferStatus; import ch.cyberduck.test.IntegrationTest; @@ -42,8 +43,8 @@ public class GraphShareTest extends AbstractOneDriveTest { @Test public void toUrl() throws Exception { final Path file = new Path(new OneDriveHomeFinderService().find(), new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)); - new GraphTouchFeature(session, fileid).touch(file, new TransferStatus().setMime("x-application/cyberduck")); - assertNotEquals(DescriptiveUrl.EMPTY, new GraphSharedLinkFeature(session).toDownloadUrl(file, Share.Sharee.world, null, new DisabledPasswordCallback())); + new GraphTouchFeature(session, fileid).touch(new GraphWriteFeature(session, fileid), file, new TransferStatus().setMime("x-application/cyberduck")); + assertNotEquals(DescriptiveUrl.EMPTY, new GraphSharedLinkFeature(session, fileid).toDownloadUrl(file, Share.Sharee.world, null, new DisabledPasswordCallback())); new GraphDeleteFeature(session, fileid).delete(Collections.singletonList(file), new DisabledLoginCallback(), new Delete.DisabledCallback()); } } diff --git a/onedrive/src/test/java/ch/cyberduck/core/onedrive/GraphTimestampFeatureTest.java b/onedrive/src/test/java/ch/cyberduck/core/onedrive/GraphTimestampFeatureTest.java new file mode 100644 index 0000000000..b5c09ee428 --- /dev/null +++ b/onedrive/src/test/java/ch/cyberduck/core/onedrive/GraphTimestampFeatureTest.java @@ -0,0 +1,53 @@ +package ch.cyberduck.core.onedrive; + +/* + * Copyright (c) 2002-2025 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 ch.cyberduck.core.AlphanumericRandomStringService; +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.onedrive.features.GraphAttributesFinderFeature; +import ch.cyberduck.core.onedrive.features.GraphDeleteFeature; +import ch.cyberduck.core.onedrive.features.GraphTimestampFeature; +import ch.cyberduck.core.onedrive.features.GraphTouchFeature; +import ch.cyberduck.core.onedrive.features.GraphWriteFeature; +import ch.cyberduck.core.transfer.TransferStatus; +import ch.cyberduck.test.IntegrationTest; + +import org.junit.Test; +import org.junit.experimental.categories.Category; +import static org.junit.Assert.assertEquals; + +import java.util.Collections; +import java.util.EnumSet; + +@Category(IntegrationTest.class) +public class GraphTimestampFeatureTest extends AbstractOneDriveTest { + + @Test + public void testSetTimestamp() throws Exception { + final Path drive = new OneDriveHomeFinderService().find(); + final Path test = new Path(drive, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)); + new GraphTouchFeature(session, fileid).touch(new GraphWriteFeature(session, fileid), test, new TransferStatus()); + final TransferStatus status = new TransferStatus(); + new GraphTimestampFeature(session, fileid).setTimestamp(test, status.setModified(1671187993791L)); + assertEquals(1671187993000L, status.getResponse().getModificationDate()); + final PathAttributes attr = new GraphAttributesFinderFeature(session, fileid).find(test); + assertEquals(1671187993000L, attr.getModificationDate()); + new GraphDeleteFeature(session, fileid).delete(Collections.singletonList(test), new DisabledLoginCallback(), new Delete.DisabledCallback()); + } +} \ No newline at end of file diff --git a/onedrive/src/test/java/ch/cyberduck/core/onedrive/GraphTouchFeatureTest.java b/onedrive/src/test/java/ch/cyberduck/core/onedrive/GraphTouchFeatureTest.java index 316654b085..048010d3b7 100644 --- a/onedrive/src/test/java/ch/cyberduck/core/onedrive/GraphTouchFeatureTest.java +++ b/onedrive/src/test/java/ch/cyberduck/core/onedrive/GraphTouchFeatureTest.java @@ -52,7 +52,7 @@ public class GraphTouchFeatureTest extends AbstractOneDriveTest { @Test public void testTouch() throws Exception { - final Path file = new GraphTouchFeature(session, fileid).touch(new Path(new OneDriveHomeFinderService().find(), + final Path file = new GraphTouchFeature(session, fileid).touch(new GraphWriteFeature(session, fileid), new Path(new OneDriveHomeFinderService().find(), new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)), new TransferStatus()); assertEquals(file.attributes().getFileId(), new GraphAttributesFinderFeature(session, fileid).find(file).getFileId()); new GraphDeleteFeature(session, fileid).delete(Collections.singletonList(file), new DisabledLoginCallback(), new Delete.DisabledCallback()); @@ -61,7 +61,7 @@ public class GraphTouchFeatureTest extends AbstractOneDriveTest { @Test public void testTouchUmlaut() throws Exception { final Path file = new Path(new OneDriveHomeFinderService().find(), String.format("%sä", new AlphanumericRandomStringService().random()), EnumSet.of(Path.Type.file)); - new GraphTouchFeature(session, fileid).touch(file, new TransferStatus()); + new GraphTouchFeature(session, fileid).touch(new GraphWriteFeature(session, fileid), file, new TransferStatus()); assertNotNull(new GraphAttributesFinderFeature(session, fileid).find(file)); new GraphDeleteFeature(session, fileid).delete(Collections.singletonList(file), new DisabledLoginCallback(), new Delete.DisabledCallback()); } @@ -69,7 +69,7 @@ public class GraphTouchFeatureTest extends AbstractOneDriveTest { @Test public void testTouchEqualSign() throws Exception { final Path file = new Path(new OneDriveHomeFinderService().find(), String.format("%s====", new AlphanumericRandomStringService().random()), EnumSet.of(Path.Type.file)); - new GraphTouchFeature(session, fileid).touch(file, new TransferStatus()); + new GraphTouchFeature(session, fileid).touch(new GraphWriteFeature(session, fileid), file, new TransferStatus()); assertNotNull(new GraphAttributesFinderFeature(session, fileid).find(file)); new GraphDeleteFeature(session, fileid).delete(Collections.singletonList(file), new DisabledLoginCallback(), new Delete.DisabledCallback()); } @@ -78,7 +78,7 @@ public class GraphTouchFeatureTest extends AbstractOneDriveTest { public void testWhitespaceTouch() throws Exception { final RandomStringService randomStringService = new AlphanumericRandomStringService(); final Path file = new Path(new OneDriveHomeFinderService().find(), String.format("%s %s", randomStringService.random(), randomStringService.random()), EnumSet.of(Path.Type.file)); - new GraphTouchFeature(session, fileid).touch(file, new TransferStatus().setMime("x-application/cyberduck")); + new GraphTouchFeature(session, fileid).touch(new GraphWriteFeature(session, fileid), file, new TransferStatus().setMime("x-application/cyberduck")); assertNotNull(new GraphAttributesFinderFeature(session, fileid).find(file)); new GraphDeleteFeature(session, fileid).delete(Collections.singletonList(file), new DisabledLoginCallback(), new Delete.DisabledCallback()); } @@ -88,7 +88,7 @@ public class GraphTouchFeatureTest extends AbstractOneDriveTest { final Path container = new OneDriveHomeFinderService().find(); final String filename = StringUtils.lowerCase(new AlphanumericRandomStringService().random()); final Path file = new GraphTouchFeature(session, fileid) - .touch(new Path(container, filename, EnumSet.of(Path.Type.file)), new TransferStatus().setLength(0L)); + .touch(new GraphWriteFeature(session, fileid), new Path(container, filename, EnumSet.of(Path.Type.file)), new TransferStatus().setLength(0L)); final byte[] content = RandomUtils.nextBytes(254); final TransferStatus status = new TransferStatus(); status.setLength(content.length); diff --git a/onedrive/src/test/java/ch/cyberduck/core/onedrive/OneDriveBusinessContextLoginTest.java b/onedrive/src/test/java/ch/cyberduck/core/onedrive/OneDriveBusinessContextLoginTest.java index 8966d811fb..6291d683a5 100644 --- a/onedrive/src/test/java/ch/cyberduck/core/onedrive/OneDriveBusinessContextLoginTest.java +++ b/onedrive/src/test/java/ch/cyberduck/core/onedrive/OneDriveBusinessContextLoginTest.java @@ -50,7 +50,7 @@ public class OneDriveBusinessContextLoginTest { public void testLogin() throws Exception { final ProtocolFactory factory = new ProtocolFactory(new HashSet<>(Collections.singleton(new OneDriveProtocol()))); final Profile profile = new ProfilePlistReader(factory).read( - this.getClass().getResourceAsStream("/Microsoft SharePoint.cyberduckprofile")); + this.getClass().getResourceAsStream("/Microsoft SharePoint.cyberduckprofile")); final Host host = new Host(profile, profile.getDefaultHostname()); final OneDriveSession session = new OneDriveSession(host, new DefaultX509TrustManager(), new DefaultX509KeyManager()); new LoginConnectionService(new DisabledLoginCallback() { @@ -60,23 +60,18 @@ public class OneDriveBusinessContextLoginTest { throw new LoginCanceledException(); } }, new DisabledHostKeyCallback(), - new DisabledPasswordStore() { - @Override - public String getPassword(Scheme scheme, int port, String hostname, String user) { - if("Microsoft OneDrive Business OAuth2 Access Token".equals(user)) { - return System.getProperties().getProperty("onedrive.business.accesstoken"); + new DisabledPasswordStore() { + @Override + public String getPassword(Scheme scheme, int port, String hostname, String user) { + if("Microsoft OneDrive Business OAuth2 Access Token".equals(user)) { + return System.getProperties().getProperty("onedrive.business.accesstoken"); + } + if("Microsoft OneDrive Business OAuth2 Refresh Token".equals(user)) { + return System.getProperties().getProperty("onedrive.business.refreshtoken"); + } + return null; } - if("Microsoft OneDrive Business OAuth2 Refresh Token".equals(user)) { - return System.getProperties().getProperty("onedrive.business.refreshtoken"); - } - return null; - } - - @Override - public String getPassword(String hostname, String user) { - return super.getPassword(hostname, user); - } - }, new DisabledProgressListener()).connect(session, new DisabledCancelCallback()); + }, new DisabledProgressListener()).connect(session, new DisabledCancelCallback()); assertEquals("/b!9prv2DvXt0Cua27a0kKBHlYP69u02QdCtkueQRimv8UsYPDHr-_uQoMvBiuYAjdH", (new OneDriveHomeFinderService().find().getAbsolute())); } } diff --git a/onedrive/src/test/java/ch/cyberduck/core/onedrive/OneDriveTimestampFeatureTest.java b/onedrive/src/test/java/ch/cyberduck/core/onedrive/OneDriveTimestampFeatureTest.java index c21756ac61..783587c72b 100644 --- a/onedrive/src/test/java/ch/cyberduck/core/onedrive/OneDriveTimestampFeatureTest.java +++ b/onedrive/src/test/java/ch/cyberduck/core/onedrive/OneDriveTimestampFeatureTest.java @@ -28,6 +28,7 @@ import ch.cyberduck.core.onedrive.features.GraphDeleteFeature; import ch.cyberduck.core.onedrive.features.GraphDirectoryFeature; import ch.cyberduck.core.onedrive.features.GraphTimestampFeature; import ch.cyberduck.core.onedrive.features.GraphTouchFeature; +import ch.cyberduck.core.onedrive.features.GraphWriteFeature; import ch.cyberduck.core.shared.DefaultAttributesFinderFeature; import ch.cyberduck.core.transfer.TransferStatus; import ch.cyberduck.test.IntegrationTest; @@ -48,7 +49,7 @@ public class OneDriveTimestampFeatureTest extends AbstractOneDriveTest { public void testSetTimestamp() throws Exception { final Path drive = new OneDriveHomeFinderService().find(); final Path file = new Path(drive, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)); - new GraphTouchFeature(session, fileid).touch(file, new TransferStatus().setMime("x-application/cyberduck")); + new GraphTouchFeature(session, fileid).touch(new GraphWriteFeature(session, fileid), file, new TransferStatus().setMime("x-application/cyberduck")); final PathAttributes attr = new GraphAttributesFinderFeature(session, fileid).find(file); assertNotEquals(PathAttributes.EMPTY, attr); final long modified = 1671187993791L; @@ -67,7 +68,7 @@ public class OneDriveTimestampFeatureTest extends AbstractOneDriveTest { public void testSetTimestampDirectory() throws Exception { final Path drive = new OneDriveHomeFinderService().find(); final Path test = new Path(drive, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory)); - new GraphDirectoryFeature(session, fileid).mkdir(test, null); + new GraphDirectoryFeature(session, fileid).mkdir(new GraphWriteFeature(session, fileid), test, null); final PathAttributes attr = new GraphAttributesFinderFeature(session, fileid).find(test); assertNotEquals(PathAttributes.EMPTY, attr); final long modified = 1671187993791L; diff --git a/onedrive/src/test/java/ch/cyberduck/core/onedrive/OneDriveVersioningFeatureTest.java b/onedrive/src/test/java/ch/cyberduck/core/onedrive/OneDriveVersioningFeatureTest.java index a31fa73f2d..4a9daf57e0 100644 --- a/onedrive/src/test/java/ch/cyberduck/core/onedrive/OneDriveVersioningFeatureTest.java +++ b/onedrive/src/test/java/ch/cyberduck/core/onedrive/OneDriveVersioningFeatureTest.java @@ -17,11 +17,13 @@ package ch.cyberduck.core.onedrive; 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.DisabledPasswordCallback; 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.StatusOutputStream; import ch.cyberduck.core.io.StreamCopier; @@ -54,9 +56,9 @@ public class OneDriveVersioningFeatureTest extends AbstractOneDriveTest { public void testList() throws Exception { final GraphFileIdProvider fileid = new GraphFileIdProvider(session); final Path room = new GraphDirectoryFeature(session, fileid).mkdir( - new Path(new OneDriveHomeFinderService().find(), new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory, Path.Type.volume)), new TransferStatus()); - final Path test = new GraphTouchFeature(session, fileid).touch(new Path(room, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)), new TransferStatus()); - assertNull(test.attributes().getVersionId()); + new GraphWriteFeature(session, fileid), new Path(new OneDriveHomeFinderService().find(), new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory, Path.Type.volume)), new TransferStatus()); + final Path test = new GraphTouchFeature(session, fileid).touch(new GraphWriteFeature(session, fileid), new Path(room, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)), new TransferStatus()); + assertNotNull(test.attributes().getVersionId()); final GraphVersioningFeature feature = new GraphVersioningFeature(session, fileid); assertEquals(0, feature.list(test, new DisabledListProgressListener()).size()); // Add initial content @@ -71,7 +73,7 @@ public class OneDriveVersioningFeatureTest extends AbstractOneDriveTest { } assertEquals(test.attributes().getFileId(), new GraphAttributesFinderFeature(session, fileid).find(test).getFileId()); assertEquals(0, feature.list(test, new DisabledListProgressListener()).size()); - final PathAttributes initialAttributes = new PathAttributes(test.attributes()); + final PathAttributes initialAttributes = new DefaultPathAttributes(test.attributes()); final byte[] content = RandomUtils.nextBytes(32769); final TransferStatus status = new TransferStatus(); status.setLength(content.length); @@ -79,13 +81,17 @@ public class OneDriveVersioningFeatureTest extends AbstractOneDriveTest { final GraphWriteFeature writer = new GraphWriteFeature(session, fileid); final StatusOutputStream out = writer.write(test, status, new DisabledConnectionCallback()); new StreamCopier(status, status).transfer(new ByteArrayInputStream(content), out); - assertNull(new GraphAttributesFinderFeature(session, fileid).toAttributes(out.getStatus()).getVersionId()); + assertNotNull(new GraphAttributesFinderFeature(session, fileid).toAttributes(out.getStatus()).getVersionId()); final PathAttributes updated = new GraphAttributesFinderFeature(session, fileid).find(test); + assertEquals(updated.getVersionId(), new GraphAttributesFinderFeature(session, fileid).toAttributes(out.getStatus()).getVersionId()); assertNotEquals(initialAttributes.getETag(), updated.getETag()); + assertNotNull(updated.getVersionId()); + assertNotEquals(initialAttributes.getVersionId(), updated.getVersionId()); { final AttributedList versions = feature.list(test, new DisabledListProgressListener()); assertEquals(1, versions.size()); assertEquals(213, versions.get(0).attributes().getSize(), 0L); + assertNotEquals(updated.getVersionId(), versions.get(0).attributes().getVersionId()); feature.revert(versions.get(0)); } // Delete versions permanently @@ -100,5 +106,6 @@ public class OneDriveVersioningFeatureTest extends AbstractOneDriveTest { } } new GraphDeleteFeature(session, fileid).delete(Collections.singletonList(test), new DisabledPasswordCallback(), new Delete.DisabledCallback()); + assertThrows(NotfoundException.class, () -> feature.list(test, new DisabledListProgressListener())); } } \ No newline at end of file diff --git a/onedrive/src/test/java/ch/cyberduck/core/onedrive/OneDriveWriteFeatureTest.java b/onedrive/src/test/java/ch/cyberduck/core/onedrive/OneDriveWriteFeatureTest.java index 61cc5f6c15..6bdd60e5e2 100644 --- a/onedrive/src/test/java/ch/cyberduck/core/onedrive/OneDriveWriteFeatureTest.java +++ b/onedrive/src/test/java/ch/cyberduck/core/onedrive/OneDriveWriteFeatureTest.java @@ -30,6 +30,7 @@ import ch.cyberduck.core.io.StreamCopier; import ch.cyberduck.core.onedrive.features.GraphAttributesFinderFeature; import ch.cyberduck.core.onedrive.features.GraphDeleteFeature; import ch.cyberduck.core.onedrive.features.GraphDirectoryFeature; +import ch.cyberduck.core.onedrive.features.GraphFindFeature; import ch.cyberduck.core.onedrive.features.GraphReadFeature; import ch.cyberduck.core.onedrive.features.GraphTouchFeature; import ch.cyberduck.core.onedrive.features.GraphWriteFeature; @@ -56,17 +57,18 @@ public class OneDriveWriteFeatureTest extends AbstractOneDriveTest { @Test public void testWrite() throws Exception { - final GraphWriteFeature feature = new GraphWriteFeature(session, fileid); final Path container = new OneDriveHomeFinderService().find(); - final Path folder = new GraphDirectoryFeature(session, fileid).mkdir(new Path(container, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory)), new TransferStatus()); + final Path folder = new GraphDirectoryFeature(session, fileid).mkdir(new GraphWriteFeature(session, fileid), new Path(container, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory)), new TransferStatus()); final PathAttributes folderAttributes = new GraphAttributesFinderFeature(session, fileid).find(folder); final String folderEtag = folderAttributes.getETag(); final long folderTimestamp = folderAttributes.getModificationDate(); final byte[] content = RandomUtils.nextBytes(5 * 1024 * 1024); - final TransferStatus status = new TransferStatus(); - status.setLength(content.length); + final TransferStatus status = new TransferStatus() + .setExists(true) + .setLength(content.length); final Path file = new Path(folder, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)); - final String id = new GraphTouchFeature(session, fileid).touch(file, new TransferStatus()).attributes().getFileId(); + final String id = new GraphTouchFeature(session, fileid).touch(new GraphWriteFeature(session, fileid), file, new TransferStatus()).attributes().getFileId(); + final GraphWriteFeature feature = new GraphWriteFeature(session, fileid); final StatusOutputStream out = feature.write(file, status, new DisabledConnectionCallback()); new StreamCopier(status, status).transfer(new ByteArrayInputStream(content), out); assertNotNull(out.getStatus()); @@ -75,7 +77,7 @@ public class OneDriveWriteFeatureTest extends AbstractOneDriveTest { assertEquals(Protocol.DirectoryTimestamp.explicit, session.getHost().getProtocol().getDirectoryTimestamp()); assertEquals(folderEtag, new GraphAttributesFinderFeature(session, fileid).find(folder).getETag()); assertEquals(folderTimestamp, new GraphAttributesFinderFeature(session, fileid).find(folder).getModificationDate()); - assertTrue(new DefaultFindFeature(session).find(file)); + assertTrue(new GraphFindFeature(session, fileid).find(file)); final byte[] compare = new byte[content.length]; final InputStream stream = new GraphReadFeature(session, fileid).read(file, new TransferStatus().setLength(content.length), new DisabledConnectionCallback()); IOUtils.readFully(stream, compare); @@ -107,7 +109,7 @@ public class OneDriveWriteFeatureTest extends AbstractOneDriveTest { in.close(); out.close(); assertNotNull(out.getStatus()); - assertTrue(new DefaultFindFeature(session).find(file)); + assertTrue(new GraphFindFeature(session, fileid).find(file)); final byte[] compare = new byte[content.length]; final InputStream stream = new GraphReadFeature(session, fileid).read(file, new TransferStatus().setLength(content.length), new DisabledConnectionCallback()); IOUtils.readFully(stream, compare); @@ -153,7 +155,7 @@ public class OneDriveWriteFeatureTest extends AbstractOneDriveTest { in.close(); out.close(); assertNotNull(out.getStatus()); - assertTrue(new DefaultFindFeature(session).find(file)); + assertTrue(new GraphFindFeature(session, fileid).find(file)); final byte[] compare = new byte[content.length]; final InputStream stream = new GraphReadFeature(session, fileid).read(file, new TransferStatus().setLength(content.length), new DisabledConnectionCallback()); IOUtils.readFully(stream, compare); diff --git a/onedrive/src/test/java/ch/cyberduck/core/onedrive/SharepointSessionTest.java b/onedrive/src/test/java/ch/cyberduck/core/onedrive/SharepointSessionTest.java index 015488c3d1..6e313f60a3 100644 --- a/onedrive/src/test/java/ch/cyberduck/core/onedrive/SharepointSessionTest.java +++ b/onedrive/src/test/java/ch/cyberduck/core/onedrive/SharepointSessionTest.java @@ -16,6 +16,7 @@ package ch.cyberduck.core.onedrive; */ import ch.cyberduck.core.AbstractPath; +import ch.cyberduck.core.DefaultPathAttributes; import ch.cyberduck.core.Host; import ch.cyberduck.core.Path; import ch.cyberduck.core.PathAttributes; @@ -77,7 +78,7 @@ public class SharepointSessionTest { @Test public void testContainerEquality() { final Path source = new Path("/Default/Drives/Docs", EnumSet.of(Path.Type.directory)) - .withAttributes(new PathAttributes() + .withAttributes(new DefaultPathAttributes() .setFileId("File Id")); final Path target = new Path("/Default/Drives/Docs", EnumSet.of(Path.Type.directory)); final GraphSession.ContainerItem sourceItem = session.getContainer(source); diff --git a/onedrive/src/test/java/ch/cyberduck/core/onedrive/SharepointTimestampFeatureTest.java b/onedrive/src/test/java/ch/cyberduck/core/onedrive/SharepointTimestampFeatureTest.java index a5a1c3bd7c..7588ca4904 100644 --- a/onedrive/src/test/java/ch/cyberduck/core/onedrive/SharepointTimestampFeatureTest.java +++ b/onedrive/src/test/java/ch/cyberduck/core/onedrive/SharepointTimestampFeatureTest.java @@ -30,6 +30,7 @@ import ch.cyberduck.core.onedrive.features.GraphDeleteFeature; import ch.cyberduck.core.onedrive.features.GraphDirectoryFeature; import ch.cyberduck.core.onedrive.features.GraphTimestampFeature; import ch.cyberduck.core.onedrive.features.GraphTouchFeature; +import ch.cyberduck.core.onedrive.features.GraphWriteFeature; import ch.cyberduck.core.shared.DefaultAttributesFinderFeature; import ch.cyberduck.core.transfer.TransferStatus; import ch.cyberduck.test.IntegrationTest; @@ -53,7 +54,7 @@ public class SharepointTimestampFeatureTest extends AbstractSharepointTest { final AttributedList drives = list.list(new Path(SharepointListService.DEFAULT_NAME, DRIVES_CONTAINER, EnumSet.of(Path.Type.directory)), new DisabledListProgressListener()); final Path drive = drives.get(0); final Path file = new Path(drive, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)); - new GraphTouchFeature(session, fileid).touch(file, new TransferStatus().setMime("x-application/cyberduck")); + new GraphTouchFeature(session, fileid).touch(new GraphWriteFeature(session, fileid), file, new TransferStatus().setMime("x-application/cyberduck")); final PathAttributes attr = new GraphAttributesFinderFeature(session, fileid).find(file); assertNotEquals(PathAttributes.EMPTY, attr); final long modified = Timestamp.toSeconds(1671187993791L); @@ -72,7 +73,7 @@ public class SharepointTimestampFeatureTest extends AbstractSharepointTest { final AttributedList drives = list.list(new Path(SharepointListService.DEFAULT_NAME, DRIVES_CONTAINER, EnumSet.of(Path.Type.directory)), new DisabledListProgressListener()); final Path drive = drives.get(0); final Path test = new Path(drive, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory)); - new GraphDirectoryFeature(session, fileid).mkdir(test, null); + new GraphDirectoryFeature(session, fileid).mkdir(new GraphWriteFeature(session, fileid), test, null); final PathAttributes attr = new GraphAttributesFinderFeature(session, fileid).find(test); assertNotEquals(PathAttributes.EMPTY, attr); final long modified = Timestamp.toSeconds(1671187993791L); diff --git a/onedrive/src/test/java/ch/cyberduck/core/onedrive/SharepointVersioningFeatureTest.java b/onedrive/src/test/java/ch/cyberduck/core/onedrive/SharepointVersioningFeatureTest.java index 41b5d1a39b..b982d4d00c 100644 --- a/onedrive/src/test/java/ch/cyberduck/core/onedrive/SharepointVersioningFeatureTest.java +++ b/onedrive/src/test/java/ch/cyberduck/core/onedrive/SharepointVersioningFeatureTest.java @@ -17,12 +17,14 @@ package ch.cyberduck.core.onedrive; 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.DisabledPasswordCallback; import ch.cyberduck.core.ListService; 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.StatusOutputStream; import ch.cyberduck.core.io.StreamCopier; @@ -57,8 +59,8 @@ public class SharepointVersioningFeatureTest extends AbstractSharepointTest { final ListService list = new SharepointListService(session, fileid); final AttributedList drives = list.list(new Path(SharepointListService.DEFAULT_NAME, DRIVES_CONTAINER, EnumSet.of(Path.Type.directory)), new DisabledListProgressListener()); final Path drive = drives.get(0); - final Path test = new GraphTouchFeature(session, fileid).touch(new Path(drive, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)), new TransferStatus()); - assertNull(test.attributes().getVersionId()); + final Path test = new GraphTouchFeature(session, fileid).touch(new GraphWriteFeature(session, fileid), new Path(drive, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)), new TransferStatus()); + assertNotNull(test.attributes().getVersionId()); final GraphVersioningFeature feature = new GraphVersioningFeature(session, fileid); assertEquals(0, feature.list(test, new DisabledListProgressListener()).size()); // Add initial content @@ -73,7 +75,8 @@ public class SharepointVersioningFeatureTest extends AbstractSharepointTest { } assertEquals(test.attributes().getFileId(), new GraphAttributesFinderFeature(session, fileid).find(test).getFileId()); assertEquals(0, feature.list(test, new DisabledListProgressListener()).size()); - final PathAttributes initialAttributes = new PathAttributes(test.attributes()); + final PathAttributes initialAttributes = new DefaultPathAttributes(test.attributes()); + assertNotNull(initialAttributes.getVersionId()); final byte[] content = RandomUtils.nextBytes(32769); final TransferStatus status = new TransferStatus(); status.setLength(content.length); @@ -81,11 +84,14 @@ public class SharepointVersioningFeatureTest extends AbstractSharepointTest { final GraphWriteFeature writer = new GraphWriteFeature(session, fileid); final StatusOutputStream out = writer.write(test, status, new DisabledConnectionCallback()); new StreamCopier(status, status).transfer(new ByteArrayInputStream(content), out); - assertNull(new GraphAttributesFinderFeature(session, fileid).toAttributes(out.getStatus()).getVersionId()); + final String ctag = new GraphAttributesFinderFeature(session, fileid).toAttributes(out.getStatus()).getVersionId(); + assertNotNull(ctag); + assertNotEquals(initialAttributes.getVersionId(), ctag); { final AttributedList versions = feature.list(test, new DisabledListProgressListener()); assertEquals(1, versions.size()); assertEquals(213, versions.get(0).attributes().getSize(), 0L); + assertNotEquals(ctag, versions.get(0).attributes().getVersionId()); feature.revert(versions.get(0)); } // Delete versions permanently @@ -100,5 +106,6 @@ public class SharepointVersioningFeatureTest extends AbstractSharepointTest { } } new GraphDeleteFeature(session, fileid).delete(Collections.singletonList(test), new DisabledPasswordCallback(), new Delete.DisabledCallback()); + assertThrows(NotfoundException.class, () -> feature.list(test, new DisabledListProgressListener())); } } \ No newline at end of file diff --git a/onedrive/src/test/java/ch/cyberduck/core/onedrive/SharepointWriteFeatureTest.java b/onedrive/src/test/java/ch/cyberduck/core/onedrive/SharepointWriteFeatureTest.java index 429f61a3f6..022b67f856 100644 --- a/onedrive/src/test/java/ch/cyberduck/core/onedrive/SharepointWriteFeatureTest.java +++ b/onedrive/src/test/java/ch/cyberduck/core/onedrive/SharepointWriteFeatureTest.java @@ -30,10 +30,10 @@ import ch.cyberduck.core.io.StreamCopier; import ch.cyberduck.core.onedrive.features.GraphAttributesFinderFeature; import ch.cyberduck.core.onedrive.features.GraphDeleteFeature; import ch.cyberduck.core.onedrive.features.GraphDirectoryFeature; +import ch.cyberduck.core.onedrive.features.GraphFindFeature; import ch.cyberduck.core.onedrive.features.GraphReadFeature; import ch.cyberduck.core.onedrive.features.GraphTouchFeature; import ch.cyberduck.core.onedrive.features.GraphWriteFeature; -import ch.cyberduck.core.shared.DefaultFindFeature; import ch.cyberduck.core.transfer.TransferStatus; import ch.cyberduck.test.IntegrationTest; @@ -60,7 +60,7 @@ public class SharepointWriteFeatureTest extends AbstractSharepointTest { final ListService list = new SharepointListService(session, fileid); final AttributedList drives = list.list(new Path(SharepointListService.DEFAULT_NAME, DRIVES_CONTAINER, EnumSet.of(Path.Type.directory)), new DisabledListProgressListener()); final Path container = drives.get(0); - final Path folder = new GraphDirectoryFeature(session, fileid).mkdir(new Path(container, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory)), new TransferStatus()); + final Path folder = new GraphDirectoryFeature(session, fileid).mkdir(new GraphWriteFeature(session, fileid), new Path(container, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory)), new TransferStatus()); final PathAttributes folderAttributes = new GraphAttributesFinderFeature(session, fileid).find(folder); final String folderEtag = folderAttributes.getETag(); final long folderTimestamp = folderAttributes.getModificationDate(); @@ -68,7 +68,7 @@ public class SharepointWriteFeatureTest extends AbstractSharepointTest { final TransferStatus status = new TransferStatus(); status.setLength(content.length); final Path file = new Path(folder, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)); - final String id = new GraphTouchFeature(session, fileid).touch(file, new TransferStatus()).attributes().getFileId(); + final String id = new GraphTouchFeature(session, fileid).touch(new GraphWriteFeature(session, fileid), file, new TransferStatus()).attributes().getFileId(); final StatusOutputStream out = feature.write(file, status, new DisabledConnectionCallback()); new StreamCopier(status, status).transfer(new ByteArrayInputStream(content), out); assertNotNull(out.getStatus()); @@ -77,7 +77,7 @@ public class SharepointWriteFeatureTest extends AbstractSharepointTest { assertEquals(Protocol.DirectoryTimestamp.explicit, session.getHost().getProtocol().getDirectoryTimestamp()); assertEquals(folderEtag, new GraphAttributesFinderFeature(session, fileid).find(folder).getETag()); assertEquals(folderTimestamp, new GraphAttributesFinderFeature(session, fileid).find(folder).getModificationDate()); - assertTrue(new DefaultFindFeature(session).find(file)); + assertTrue(new GraphFindFeature(session, fileid).find(file)); final byte[] compare = new byte[content.length]; final InputStream stream = new GraphReadFeature(session, fileid).read(file, new TransferStatus().setLength(content.length), new DisabledConnectionCallback()); IOUtils.readFully(stream, compare); diff --git a/openstack/pom.xml b/openstack/pom.xml index 09fa28890f..b36667d3e5 100644 --- a/openstack/pom.xml +++ b/openstack/pom.xml @@ -18,7 +18,7 @@ ch.cyberduck parent - 9.2.0-SNAPSHOT + 9.4.0-SNAPSHOT openstack jar diff --git a/openstack/src/main/java/ch/cyberduck/core/openstack/SwiftAttributesFinderFeature.java b/openstack/src/main/java/ch/cyberduck/core/openstack/SwiftAttributesFinderFeature.java index 9db21f0e27..af0deaafb4 100644 --- a/openstack/src/main/java/ch/cyberduck/core/openstack/SwiftAttributesFinderFeature.java +++ b/openstack/src/main/java/ch/cyberduck/core/openstack/SwiftAttributesFinderFeature.java @@ -20,6 +20,7 @@ package ch.cyberduck.core.openstack; import ch.cyberduck.core.CancellingListProgressListener; 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; @@ -78,7 +79,7 @@ public class SwiftAttributesFinderFeature implements AttributesFinder, Attribute if(containerService.isContainer(file)) { final ContainerInfo info = session.getClient().getContainerInfo(region, containerService.getContainer(file).getName()); - final PathAttributes attributes = new PathAttributes(); + final PathAttributes attributes = new DefaultPathAttributes(); attributes.setSize(info.getTotalSize()); attributes.setRegion(info.getRegion().getRegionId()); return attributes; @@ -110,9 +111,9 @@ public class SwiftAttributesFinderFeature implements AttributesFinder, Attribute return PathAttributes.EMPTY; } // Try to find pending large file upload - final Write.Append append = new SwiftLargeObjectUploadFeature(session, regionService, new SwiftWriteFeature(session, regionService)).append(file, new TransferStatus()); + final Write.Append append = new SwiftLargeObjectUploadFeature(session, regionService).append(file, new TransferStatus()); if(append.append) { - return new PathAttributes().setSize(append.offset); + return new DefaultPathAttributes().setSize(append.offset); } throw e; } @@ -140,7 +141,7 @@ public class SwiftAttributesFinderFeature implements AttributesFinder, Attribute @Override public PathAttributes toAttributes(final StorageObject object) { - final PathAttributes attributes = new PathAttributes(); + final PathAttributes attributes = new DefaultPathAttributes(); if(StringUtils.isNotBlank(object.getMd5sum())) { // For manifest files, the ETag in the response for a GET or HEAD on the manifest file is the MD5 sum of // the concatenated string of ETags for each of the segments in the manifest. @@ -166,7 +167,7 @@ public class SwiftAttributesFinderFeature implements AttributesFinder, Attribute } public PathAttributes toAttributes(final ObjectMetadata metadata) { - final PathAttributes attributes = new PathAttributes(); + final PathAttributes attributes = new DefaultPathAttributes(); attributes.setSize(Long.parseLong(metadata.getContentLength())); final String lastModified = metadata.getLastModified(); try { diff --git a/openstack/src/main/java/ch/cyberduck/core/openstack/SwiftContainerListService.java b/openstack/src/main/java/ch/cyberduck/core/openstack/SwiftContainerListService.java index 8fc6e05978..1b0cc4d48a 100644 --- a/openstack/src/main/java/ch/cyberduck/core/openstack/SwiftContainerListService.java +++ b/openstack/src/main/java/ch/cyberduck/core/openstack/SwiftContainerListService.java @@ -20,6 +20,7 @@ package ch.cyberduck.core.openstack; import ch.cyberduck.core.AttributedList; import ch.cyberduck.core.DefaultIOExceptionMappingService; +import ch.cyberduck.core.DefaultPathAttributes; import ch.cyberduck.core.ListProgressListener; import ch.cyberduck.core.Path; import ch.cyberduck.core.PathAttributes; @@ -73,7 +74,7 @@ public class SwiftContainerListService implements RootListService { do { chunk = client.listContainers(r, limit, marker); for(final Container f : chunk) { - final PathAttributes attributes = new PathAttributes(); + final PathAttributes attributes = new DefaultPathAttributes(); attributes.setRegion(f.getRegion().getRegionId()); containers.add(new Path(String.format("/%s", f.getName()), EnumSet.of(Path.Type.volume, Path.Type.directory), attributes)); diff --git a/openstack/src/main/java/ch/cyberduck/core/openstack/SwiftDirectoryFeature.java b/openstack/src/main/java/ch/cyberduck/core/openstack/SwiftDirectoryFeature.java index 2ee45888d7..7e36ba0844 100644 --- a/openstack/src/main/java/ch/cyberduck/core/openstack/SwiftDirectoryFeature.java +++ b/openstack/src/main/java/ch/cyberduck/core/openstack/SwiftDirectoryFeature.java @@ -42,24 +42,17 @@ public class SwiftDirectoryFeature implements Directory { private final SwiftSession session; private final SwiftRegionService regionService; - private Write writer; - public SwiftDirectoryFeature(final SwiftSession session) { this(session, new SwiftRegionService(session)); } public SwiftDirectoryFeature(final SwiftSession session, final SwiftRegionService regionService) { - this(session, regionService, new SwiftWriteFeature(session, regionService)); - } - - public SwiftDirectoryFeature(final SwiftSession session, final SwiftRegionService regionService, final Write writer) { this.session = session; this.regionService = regionService; - this.writer = writer; } @Override - public Path mkdir(final Path folder, final TransferStatus status) throws BackgroundException { + public Path mkdir(final Write writer, final Path folder, final TransferStatus status) throws BackgroundException { try { if(containerService.isContainer(folder)) { // Create container at top level @@ -82,9 +75,4 @@ public class SwiftDirectoryFeature implements Directory { } } - @Override - public SwiftDirectoryFeature withWriter(final Write writer) { - this.writer = writer; - return this; - } } diff --git a/openstack/src/main/java/ch/cyberduck/core/openstack/SwiftLargeObjectCopyFeature.java b/openstack/src/main/java/ch/cyberduck/core/openstack/SwiftLargeObjectCopyFeature.java index bd095c5df5..c7aa006efa 100644 --- a/openstack/src/main/java/ch/cyberduck/core/openstack/SwiftLargeObjectCopyFeature.java +++ b/openstack/src/main/java/ch/cyberduck/core/openstack/SwiftLargeObjectCopyFeature.java @@ -17,6 +17,7 @@ package ch.cyberduck.core.openstack; import ch.cyberduck.core.ConnectionCallback; import ch.cyberduck.core.DefaultIOExceptionMappingService; +import ch.cyberduck.core.DefaultPathAttributes; import ch.cyberduck.core.DefaultPathContainerService; import ch.cyberduck.core.LocaleFactory; import ch.cyberduck.core.Path; @@ -127,7 +128,7 @@ public class SwiftLargeObjectCopyFeature implements Copy { catch(IOException e) { throw new DefaultIOExceptionMappingService().map("Cannot copy {0}", e, source); } - final PathAttributes attributes = new PathAttributes(source.attributes()); + final PathAttributes attributes = new DefaultPathAttributes(source.attributes()); attributes.setChecksum(new Checksum(HashAlgorithm.md5, stored.getMd5sum())); return new Path(target).withAttributes(attributes); } diff --git a/openstack/src/main/java/ch/cyberduck/core/openstack/SwiftLargeObjectUploadFeature.java b/openstack/src/main/java/ch/cyberduck/core/openstack/SwiftLargeObjectUploadFeature.java index 760affdb16..2cf863d294 100644 --- a/openstack/src/main/java/ch/cyberduck/core/openstack/SwiftLargeObjectUploadFeature.java +++ b/openstack/src/main/java/ch/cyberduck/core/openstack/SwiftLargeObjectUploadFeature.java @@ -33,7 +33,6 @@ import ch.cyberduck.core.exception.BackgroundException; import ch.cyberduck.core.exception.ConnectionCanceledException; import ch.cyberduck.core.exception.NotfoundException; import ch.cyberduck.core.features.Delete; -import ch.cyberduck.core.features.Upload; import ch.cyberduck.core.features.Write; import ch.cyberduck.core.http.HttpUploadFeature; import ch.cyberduck.core.io.BandwidthThrottle; @@ -71,16 +70,14 @@ public class SwiftLargeObjectUploadFeature extends HttpUploadFeature writer; - - public SwiftLargeObjectUploadFeature(final SwiftSession session, final SwiftRegionService regionService, final Write writer) { - this(session, regionService, writer, HostPreferencesFactory.get(session.getHost()).getLong("openstack.upload.largeobject.size"), + public SwiftLargeObjectUploadFeature(final SwiftSession session, final SwiftRegionService regionService) { + this(session, regionService, HostPreferencesFactory.get(session.getHost()).getLong("openstack.upload.largeobject.size"), HostPreferencesFactory.get(session.getHost()).getInteger("openstack.upload.largeobject.concurrency")); } - public SwiftLargeObjectUploadFeature(final SwiftSession session, final SwiftRegionService regionService, final Write writer, + public SwiftLargeObjectUploadFeature(final SwiftSession session, final SwiftRegionService regionService, final Long segmentSize, final Integer concurrency) { - this(session, regionService, new SwiftObjectListService(session, regionService), new SwiftSegmentService(session, regionService), writer, + this(session, regionService, new SwiftObjectListService(session, regionService), new SwiftSegmentService(session, regionService), segmentSize, concurrency); } @@ -88,12 +85,9 @@ public class SwiftLargeObjectUploadFeature extends HttpUploadFeature writer, final Long segmentSize, final Integer concurrency) { - super(writer); this.session = session; this.regionService = regionService; - this.writer = writer; this.segmentSize = segmentSize; this.segmentService = segmentService; this.listService = listService; @@ -101,7 +95,7 @@ public class SwiftLargeObjectUploadFeature extends HttpUploadFeature write, final Path file, final Local local, final BandwidthThrottle throttle, final ProgressListener progress, final StreamListener streamListener, final TransferStatus status, @@ -152,7 +146,7 @@ public class SwiftLargeObjectUploadFeature extends HttpUploadFeature submit(final ThreadPool pool, final Path segment, final Local local, + private Future submit(final ThreadPool pool, final Write write, final Path segment, final Local local, final BandwidthThrottle throttle, final StreamListener listener, final TransferStatus overall, final Long offset, final Long length, final ConnectionCallback callback) throws ConnectionCanceledException { overall.validate(); @@ -209,10 +203,10 @@ public class SwiftLargeObjectUploadFeature extends HttpUploadFeature withWriter(final Write writer) { - this.writer = writer; - return super.withWriter(writer); - } } diff --git a/openstack/src/main/java/ch/cyberduck/core/openstack/SwiftLocationFeature.java b/openstack/src/main/java/ch/cyberduck/core/openstack/SwiftLocationFeature.java index 29e75e5bc7..29f1a7e5fd 100644 --- a/openstack/src/main/java/ch/cyberduck/core/openstack/SwiftLocationFeature.java +++ b/openstack/src/main/java/ch/cyberduck/core/openstack/SwiftLocationFeature.java @@ -59,12 +59,12 @@ public class SwiftLocationFeature implements Location { } @Override - public Name getDefault() { + public Name getDefault(final Path file) { return Location.unknown; } @Override - public Set getLocations() { + public Set getLocations(final Path file) { final Set locations = new LinkedHashSet<>(); if(StringUtils.isNotBlank(session.getHost().getRegion())) { locations.add(new SwiftRegion(session.getHost().getRegion())); diff --git a/openstack/src/main/java/ch/cyberduck/core/openstack/SwiftMetadataFeature.java b/openstack/src/main/java/ch/cyberduck/core/openstack/SwiftMetadataFeature.java index 8a825e9f0a..c8339c2402 100644 --- a/openstack/src/main/java/ch/cyberduck/core/openstack/SwiftMetadataFeature.java +++ b/openstack/src/main/java/ch/cyberduck/core/openstack/SwiftMetadataFeature.java @@ -54,7 +54,7 @@ public class SwiftMetadataFeature implements Headers { } @Override - public Map getDefault() { + public Map getDefault(final Path file) { return HostPreferencesFactory.get(session.getHost()).getMap("openstack.metadata.default"); } diff --git a/openstack/src/main/java/ch/cyberduck/core/openstack/SwiftMoveFeature.java b/openstack/src/main/java/ch/cyberduck/core/openstack/SwiftMoveFeature.java index 1a4936a644..81cdc56c14 100644 --- a/openstack/src/main/java/ch/cyberduck/core/openstack/SwiftMoveFeature.java +++ b/openstack/src/main/java/ch/cyberduck/core/openstack/SwiftMoveFeature.java @@ -57,12 +57,12 @@ public class SwiftMoveFeature implements Move { public Path move(final Path file, final Path renamed, final TransferStatus status, final Delete.Callback callback, final ConnectionCallback connectionCallback) throws BackgroundException { if(new DefaultPathPredicate(containerService.getContainer(file)).test(containerService.getContainer(renamed))) { // Either copy complete file contents (small file) or copy manifest (large file) - final Path rename = proxy.copy(file, renamed, new TransferStatus().setLength(file.attributes().getSize()), connectionCallback, new DisabledStreamListener()); + final Path rename = proxy.copy(file, renamed, status, connectionCallback, new DisabledStreamListener()); delete.delete(Collections.singletonMap(file, status), connectionCallback, callback, false); return rename; } else { - final Path copy = new SwiftSegmentCopyService(session, regionService).copy(file, renamed, new TransferStatus().setLength(file.attributes().getSize()), connectionCallback, new DisabledStreamListener()); + final Path copy = new SwiftSegmentCopyService(session, regionService).copy(file, renamed, status, connectionCallback, new DisabledStreamListener()); delete.delete(Collections.singletonMap(file, status), connectionCallback, callback); return copy; } diff --git a/openstack/src/main/java/ch/cyberduck/core/openstack/SwiftSession.java b/openstack/src/main/java/ch/cyberduck/core/openstack/SwiftSession.java index 65bbf849b9..f8535dc8ef 100644 --- a/openstack/src/main/java/ch/cyberduck/core/openstack/SwiftSession.java +++ b/openstack/src/main/java/ch/cyberduck/core/openstack/SwiftSession.java @@ -35,7 +35,6 @@ import ch.cyberduck.core.exception.InteroperabilityException; import ch.cyberduck.core.exception.LoginFailureException; import ch.cyberduck.core.features.*; import ch.cyberduck.core.http.HttpSession; -import ch.cyberduck.core.preferences.HostPreferencesFactory; import ch.cyberduck.core.proxy.ProxyFinder; import ch.cyberduck.core.shared.DelegatingSchedulerFeature; import ch.cyberduck.core.ssl.X509KeyManager; @@ -69,7 +68,7 @@ public class SwiftSession extends HttpSession { private final Map> distributions = new ConcurrentHashMap<>(); private final DelegatingSchedulerFeature scheduler = new DelegatingSchedulerFeature( - HostPreferencesFactory.get(host).getBoolean("openstack.account.preload") ? new SwiftAccountLoader(this) { + preferences.getBoolean("openstack.account.preload") ? new SwiftAccountLoader(this) { @Override protected Map operate(final PasswordCallback callback) throws BackgroundException { final Map result = super.operate(callback); @@ -78,7 +77,7 @@ public class SwiftSession extends HttpSession { return result; } } : Scheduler.noop, - HostPreferencesFactory.get(host).getBoolean("openstack.cdn.preload") ? new SwiftDistributionConfigurationLoader(this) { + preferences.getBoolean("openstack.cdn.preload") ? new SwiftDistributionConfigurationLoader(this) { @Override protected Map> operate(final PasswordCallback callback) throws BackgroundException { final Map> result = super.operate(callback); @@ -103,13 +102,23 @@ public class SwiftSession extends HttpSession { @Override protected void logout() throws BackgroundException { + scheduler.shutdown(false); + super.logout(); + } + + @Override + public void disconnect() throws BackgroundException { try { - scheduler.shutdown(false); - client.disconnect(); + if(client != null) { + client.disconnect(); + } } catch(IOException e) { throw new DefaultIOExceptionMappingService().map(e); } + finally { + super.disconnect(); + } } @Override @@ -163,10 +172,10 @@ public class SwiftSession extends HttpSession { return (T) new SwiftWriteFeature(this, regionService); } if(type == Upload.class) { - return (T) new SwiftThresholdUploadService(this, regionService, new SwiftWriteFeature(this, regionService)); + return (T) new SwiftThresholdUploadService(this, regionService); } if(type == Directory.class) { - return (T) new SwiftDirectoryFeature(this, regionService, new SwiftWriteFeature(this, regionService)); + return (T) new SwiftDirectoryFeature(this, regionService); } if(type == Delete.class) { return (T) new SwiftThresholdDeleteFeature(this, new SwiftSegmentService(this, regionService), regionService); diff --git a/openstack/src/main/java/ch/cyberduck/core/openstack/SwiftSmallObjectUploadFeature.java b/openstack/src/main/java/ch/cyberduck/core/openstack/SwiftSmallObjectUploadFeature.java index 02411a78b0..11616da0e1 100644 --- a/openstack/src/main/java/ch/cyberduck/core/openstack/SwiftSmallObjectUploadFeature.java +++ b/openstack/src/main/java/ch/cyberduck/core/openstack/SwiftSmallObjectUploadFeature.java @@ -19,7 +19,6 @@ package ch.cyberduck.core.openstack; 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; @@ -40,8 +39,7 @@ public class SwiftSmallObjectUploadFeature extends HttpUploadFeature writer) { - super(writer); + public SwiftSmallObjectUploadFeature(final SwiftSession session) { this.session = session; } diff --git a/openstack/src/main/java/ch/cyberduck/core/openstack/SwiftThresholdUploadService.java b/openstack/src/main/java/ch/cyberduck/core/openstack/SwiftThresholdUploadService.java index 9291387632..083867b1bc 100644 --- a/openstack/src/main/java/ch/cyberduck/core/openstack/SwiftThresholdUploadService.java +++ b/openstack/src/main/java/ch/cyberduck/core/openstack/SwiftThresholdUploadService.java @@ -41,42 +41,37 @@ public class SwiftThresholdUploadService implements Upload { private final SwiftRegionService regionService; private final Long threshold; - private Write writer; - - public SwiftThresholdUploadService(final SwiftSession session, final SwiftRegionService regionService, - final SwiftWriteFeature writer) { - this(session, regionService, writer, HostPreferencesFactory.get(session.getHost()).getLong("openstack.upload.largeobject.threshold")); + public SwiftThresholdUploadService(final SwiftSession session, final SwiftRegionService regionService) { + this(session, regionService, HostPreferencesFactory.get(session.getHost()).getLong("openstack.upload.largeobject.threshold")); } public SwiftThresholdUploadService(final SwiftSession session, final SwiftRegionService regionService, - final SwiftWriteFeature writer, final Long threshold) { this.session = session; this.regionService = regionService; - this.writer = writer; this.threshold = threshold; } @Override public Write.Append append(final Path file, final TransferStatus status) throws BackgroundException { if(this.threshold(status)) { - return new SwiftLargeObjectUploadFeature(session, regionService, writer).append(file, status); + return new SwiftLargeObjectUploadFeature(session, regionService).append(file, status); } return new Write.Append(false).withStatus(status); } @Override - public StorageObject upload(final Path file, final Local local, final BandwidthThrottle throttle, final ProgressListener progress, final StreamListener streamListener, + public StorageObject upload(final Write 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 Upload feature; if(this.threshold(status)) { - feature = new SwiftLargeObjectUploadFeature(session, regionService, writer); + feature = new SwiftLargeObjectUploadFeature(session, regionService); } else { - feature = new SwiftSmallObjectUploadFeature(session, writer); + feature = new SwiftSmallObjectUploadFeature(session); } - return feature.upload(file, local, throttle, progress, streamListener, status, callback); + return feature.upload(write, file, local, throttle, progress, streamListener, status, callback); } protected boolean threshold(final TransferStatus status) { @@ -93,9 +88,4 @@ public class SwiftThresholdUploadService implements Upload { return false; } - @Override - public Upload withWriter(final Write writer) { - this.writer = writer; - return this; - } } diff --git a/openstack/src/main/java/ch/cyberduck/core/openstack/SwiftTouchFeature.java b/openstack/src/main/java/ch/cyberduck/core/openstack/SwiftTouchFeature.java index b2510e17ad..44faaea64d 100644 --- a/openstack/src/main/java/ch/cyberduck/core/openstack/SwiftTouchFeature.java +++ b/openstack/src/main/java/ch/cyberduck/core/openstack/SwiftTouchFeature.java @@ -31,7 +31,7 @@ import ch.iterate.openstack.swift.model.StorageObject; public class SwiftTouchFeature extends DefaultTouchFeature { public SwiftTouchFeature(final SwiftSession session, final SwiftRegionService regionService) { - super(new SwiftWriteFeature(session, regionService)); + super(session); } @Override diff --git a/openstack/src/main/java/ch/cyberduck/core/openstack/SwiftWriteFeature.java b/openstack/src/main/java/ch/cyberduck/core/openstack/SwiftWriteFeature.java index 492c573b9b..923b6e8a9b 100644 --- a/openstack/src/main/java/ch/cyberduck/core/openstack/SwiftWriteFeature.java +++ b/openstack/src/main/java/ch/cyberduck/core/openstack/SwiftWriteFeature.java @@ -47,7 +47,7 @@ import ch.iterate.openstack.swift.exception.GenericException; import ch.iterate.openstack.swift.model.StorageObject; public class SwiftWriteFeature extends AbstractHttpWriteFeature implements Write { - private static final Logger log = LogManager.getLogger(SwiftSession.class); + private static final Logger log = LogManager.getLogger(SwiftWriteFeature.class); private final PathContainerService containerService = new DefaultPathContainerService(); diff --git a/openstack/src/test/java/ch/cyberduck/core/cryptomator/CopyWorkerTest.java b/openstack/src/test/java/ch/cyberduck/core/cryptomator/CopyWorkerTest.java index fcf7118954..bb939ea923 100644 --- a/openstack/src/test/java/ch/cyberduck/core/cryptomator/CopyWorkerTest.java +++ b/openstack/src/test/java/ch/cyberduck/core/cryptomator/CopyWorkerTest.java @@ -28,9 +28,9 @@ 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.StreamCopier; import ch.cyberduck.core.openstack.AbstractSwiftTest; -import ch.cyberduck.core.openstack.SwiftDeleteFeature; import ch.cyberduck.core.openstack.SwiftDirectoryFeature; import ch.cyberduck.core.openstack.SwiftFindFeature; import ch.cyberduck.core.openstack.SwiftReadFeature; @@ -63,6 +63,8 @@ import java.util.Arrays; import java.util.Collections; import java.util.EnumSet; +import ch.iterate.openstack.swift.model.StorageObject; + import static org.junit.Assert.*; import static org.junit.Assume.assumeTrue; @@ -82,7 +84,7 @@ public class CopyWorkerTest extends AbstractSwiftTest { session.withRegistry(registry); final byte[] content = RandomUtils.nextBytes(40500); final TransferStatus status = new TransferStatus(); - new CryptoBulkFeature<>(session, new DisabledBulkFeature(), new SwiftDeleteFeature(session), cryptomator).pre(Transfer.Type.upload, Collections.singletonMap(new TransferItem(source), status), new DisabledConnectionCallback()); + new CryptoBulkFeature<>(session, new DisabledBulkFeature(), 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 SwiftWriteFeature(session, new SwiftRegionService(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()); @@ -106,10 +108,11 @@ public class CopyWorkerTest extends AbstractSwiftTest { cryptomator.create(session, new VaultCredentials("test"), vaultVersion); final DefaultVaultRegistry registry = new DefaultVaultRegistry(new DisabledPasswordCallback(), cryptomator); session.withRegistry(registry); - new CryptoTouchFeature<>(session, new DefaultTouchFeature<>(new SwiftWriteFeature(session, new SwiftRegionService(session)) - ), new SwiftWriteFeature(session, new SwiftRegionService(session)), cryptomator).touch(source, new TransferStatus()); + new CryptoTouchFeature<>(session, new DefaultTouchFeature( + session), cryptomator).touch(new CryptoWriteFeature<>(session, new SwiftWriteFeature(session, new SwiftRegionService(session)), cryptomator), source, new TransferStatus()); assertTrue(cryptomator.getFeature(session, Find.class, new DefaultFindFeature(session)).find(source)); - cryptomator.getFeature(session, Directory.class, new SwiftDirectoryFeature(session)).mkdir(targetFolder, new TransferStatus()); + cryptomator.getFeature(session, Directory.class, new SwiftDirectoryFeature(session)).mkdir( + cryptomator.getFeature(session, Write.class, new SwiftWriteFeature(session, new SwiftRegionService(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); @@ -130,10 +133,11 @@ public class CopyWorkerTest extends AbstractSwiftTest { cryptomator.create(session, new VaultCredentials("test"), vaultVersion); final DefaultVaultRegistry registry = new DefaultVaultRegistry(new DisabledPasswordCallback(), cryptomator); session.withRegistry(registry); - new CryptoTouchFeature<>(session, new DefaultTouchFeature<>(new SwiftWriteFeature(session, new SwiftRegionService(session)) - ), new SwiftWriteFeature(session, new SwiftRegionService(session)), cryptomator).touch(source, new TransferStatus()); + new CryptoTouchFeature<>(session, new DefaultTouchFeature( + session), cryptomator).touch(new CryptoWriteFeature<>(session, new SwiftWriteFeature(session, new SwiftRegionService(session)), cryptomator), source, new TransferStatus()); assertTrue(cryptomator.getFeature(session, Find.class, new DefaultFindFeature(session)).find(source)); - cryptomator.getFeature(session, Directory.class, new SwiftDirectoryFeature(session)).mkdir(targetFolder, new TransferStatus()); + cryptomator.getFeature(session, Directory.class, new SwiftDirectoryFeature(session)).mkdir( + cryptomator.getFeature(session, Write.class, new SwiftWriteFeature(session, new SwiftRegionService(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); @@ -152,10 +156,11 @@ public class CopyWorkerTest extends AbstractSwiftTest { cryptomator.create(session, new VaultCredentials("test"), vaultVersion); final DefaultVaultRegistry registry = new DefaultVaultRegistry(new DisabledPasswordCallback(), cryptomator); session.withRegistry(registry); - cryptomator.getFeature(session, Directory.class, new SwiftDirectoryFeature(session)).mkdir(folder, new TransferStatus()); + cryptomator.getFeature(session, Directory.class, new SwiftDirectoryFeature(session)).mkdir( + cryptomator.getFeature(session, Write.class, new SwiftWriteFeature(session, new SwiftRegionService(session))), folder, new TransferStatus()); assertTrue(cryptomator.getFeature(session, Find.class, new DefaultFindFeature(session)).find(folder)); - new CryptoTouchFeature<>(session, new DefaultTouchFeature<>(new SwiftWriteFeature(session, new SwiftRegionService(session)) - ), new SwiftWriteFeature(session, new SwiftRegionService(session)), cryptomator).touch(file, new TransferStatus()); + new CryptoTouchFeature<>(session, new DefaultTouchFeature( + session), cryptomator).touch(new CryptoWriteFeature<>(session, new SwiftWriteFeature(session, new SwiftRegionService(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)); @@ -188,7 +193,8 @@ public class CopyWorkerTest extends AbstractSwiftTest { cryptomator.create(session, new VaultCredentials("test"), vaultVersion); final DefaultVaultRegistry registry = new DefaultVaultRegistry(new DisabledPasswordCallback(), cryptomator); session.withRegistry(registry); - cryptomator.getFeature(session, Directory.class, new SwiftDirectoryFeature(session)).mkdir(encryptedFolder, new TransferStatus()); + cryptomator.getFeature(session, Directory.class, new SwiftDirectoryFeature(session)).mkdir( + cryptomator.getFeature(session, Write.class, new SwiftWriteFeature(session, new SwiftRegionService(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()); @@ -208,8 +214,8 @@ public class CopyWorkerTest extends AbstractSwiftTest { 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 SwiftDirectoryFeature(session).mkdir(cleartextFolder, new TransferStatus()); - new SwiftTouchFeature(session, new SwiftRegionService(session)).touch(cleartextFile, new TransferStatus()); + new SwiftDirectoryFeature(session).mkdir(new SwiftWriteFeature(session, new SwiftRegionService(session)), cleartextFolder, new TransferStatus()); + new SwiftTouchFeature(session, new SwiftRegionService(session)).touch(new SwiftWriteFeature(session, new SwiftRegionService(session)), cleartextFile, new TransferStatus()); assertTrue(new SwiftFindFeature(session).find(cleartextFolder)); assertTrue(new SwiftFindFeature(session).find(cleartextFile)); final CryptoVault cryptomator = new CryptoVault(vault); @@ -234,17 +240,18 @@ public class CopyWorkerTest extends AbstractSwiftTest { final Path home = new Path("test.cyberduck.ch", EnumSet.of(Path.Type.volume, Path.Type.directory)); 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 SwiftDirectoryFeature(session).mkdir(clearFolder, new TransferStatus()); + new SwiftDirectoryFeature(session).mkdir(new SwiftWriteFeature(session, new SwiftRegionService(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 SwiftDirectoryFeature(session)).mkdir(encryptedFolder, new TransferStatus()); + cryptomator.getFeature(session, Directory.class, new SwiftDirectoryFeature(session)).mkdir( + cryptomator.getFeature(session, Write.class, new SwiftWriteFeature(session, new SwiftRegionService(session))), encryptedFolder, new TransferStatus()); assertTrue(cryptomator.getFeature(session, Find.class, new DefaultFindFeature(session)).find(encryptedFolder)); - new CryptoTouchFeature<>(session, new DefaultTouchFeature<>(new SwiftWriteFeature(session, new SwiftRegionService(session)) - ), new SwiftWriteFeature(session, new SwiftRegionService(session)), cryptomator).touch(encryptedFile, new TransferStatus()); + new CryptoTouchFeature<>(session, new DefaultTouchFeature( + session), cryptomator).touch(new CryptoWriteFeature<>(session, new SwiftWriteFeature(session, new SwiftRegionService(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)); @@ -266,10 +273,11 @@ public class CopyWorkerTest extends AbstractSwiftTest { cryptomator.create(session, new VaultCredentials("test"), vaultVersion); final DefaultVaultRegistry registry = new DefaultVaultRegistry(new DisabledPasswordCallback(), cryptomator); session.withRegistry(registry); - cryptomator.getFeature(session, Directory.class, new SwiftDirectoryFeature(session)).mkdir(encryptedFolder, new TransferStatus()); + cryptomator.getFeature(session, Directory.class, new SwiftDirectoryFeature(session)).mkdir( + cryptomator.getFeature(session, Write.class, new SwiftWriteFeature(session, new SwiftRegionService(session))), encryptedFolder, new TransferStatus()); assertTrue(cryptomator.getFeature(session, Find.class, new DefaultFindFeature(session)).find(encryptedFolder)); - new CryptoTouchFeature<>(session, new DefaultTouchFeature<>(new SwiftWriteFeature(session, new SwiftRegionService(session)) - ), new SwiftWriteFeature(session, new SwiftRegionService(session)), cryptomator).touch(encryptedFile, new TransferStatus()); + new CryptoTouchFeature<>(session, new DefaultTouchFeature( + session), cryptomator).touch(new CryptoWriteFeature<>(session, new SwiftWriteFeature(session, new SwiftRegionService(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)); diff --git a/openstack/src/test/java/ch/cyberduck/core/cryptomator/SwiftDirectoryFeatureTest.java b/openstack/src/test/java/ch/cyberduck/core/cryptomator/SwiftDirectoryFeatureTest.java index 9366edc877..16a9a6f021 100644 --- a/openstack/src/test/java/ch/cyberduck/core/cryptomator/SwiftDirectoryFeatureTest.java +++ b/openstack/src/test/java/ch/cyberduck/core/cryptomator/SwiftDirectoryFeatureTest.java @@ -22,9 +22,12 @@ import ch.cyberduck.core.Path; 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.openstack.AbstractSwiftTest; import ch.cyberduck.core.openstack.SwiftDeleteFeature; import ch.cyberduck.core.openstack.SwiftDirectoryFeature; +import ch.cyberduck.core.openstack.SwiftRegionService; +import ch.cyberduck.core.openstack.SwiftWriteFeature; import ch.cyberduck.core.shared.DefaultFindFeature; import ch.cyberduck.core.transfer.TransferStatus; import ch.cyberduck.core.vault.DefaultVaultRegistry; @@ -54,7 +57,8 @@ public class SwiftDirectoryFeatureTest extends AbstractSwiftTest { final CryptoVault cryptomator = new CryptoVault(vault); cryptomator.create(session, new VaultCredentials("test"), vaultVersion); session.withRegistry(new DefaultVaultRegistry(new DisabledPasswordCallback(), cryptomator)); - cryptomator.getFeature(session, Directory.class, new SwiftDirectoryFeature(session)).mkdir(test, new TransferStatus()); + cryptomator.getFeature(session, Directory.class, new SwiftDirectoryFeature(session)).mkdir( + cryptomator.getFeature(session, Write.class, new SwiftWriteFeature(session, new SwiftRegionService(session))), test, new TransferStatus()); assertTrue(cryptomator.getFeature(session, Find.class, new DefaultFindFeature(session)).find(test)); cryptomator.getFeature(session, Delete.class, new SwiftDeleteFeature(session)).delete(Arrays.asList(test, vault), new DisabledLoginCallback(), new Delete.DisabledCallback()); } @@ -68,7 +72,8 @@ public class SwiftDirectoryFeatureTest extends AbstractSwiftTest { final CryptoVault cryptomator = new CryptoVault(vault); cryptomator.create(session, new VaultCredentials("test"), vaultVersion); session.withRegistry(new DefaultVaultRegistry(new DisabledPasswordCallback(), cryptomator)); - cryptomator.getFeature(session, Directory.class, new SwiftDirectoryFeature(session)).mkdir(test, new TransferStatus()); + cryptomator.getFeature(session, Directory.class, new SwiftDirectoryFeature(session)).mkdir( + cryptomator.getFeature(session, Write.class, new SwiftWriteFeature(session, new SwiftRegionService(session))), test, new TransferStatus()); assertTrue(cryptomator.getFeature(session, Find.class, new DefaultFindFeature(session)).find(test)); cryptomator.getFeature(session, Delete.class, new SwiftDeleteFeature(session)).delete(Arrays.asList(test, vault), new DisabledLoginCallback(), new Delete.DisabledCallback()); } diff --git a/openstack/src/test/java/ch/cyberduck/core/cryptomator/SwiftLargeObjectUploadFeatureTest.java b/openstack/src/test/java/ch/cyberduck/core/cryptomator/SwiftLargeObjectUploadFeatureTest.java index 7624abd69f..3044112839 100644 --- a/openstack/src/test/java/ch/cyberduck/core/cryptomator/SwiftLargeObjectUploadFeatureTest.java +++ b/openstack/src/test/java/ch/cyberduck/core/cryptomator/SwiftLargeObjectUploadFeatureTest.java @@ -27,6 +27,7 @@ import ch.cyberduck.core.Path; import ch.cyberduck.core.cryptomator.features.CryptoListService; 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.Delete; import ch.cyberduck.core.features.Find; import ch.cyberduck.core.io.BandwidthThrottle; @@ -58,6 +59,8 @@ import java.util.Arrays; import java.util.EnumSet; import java.util.UUID; +import ch.iterate.openstack.swift.model.StorageObject; + import static org.junit.Assert.*; @Category(IntegrationTest.class) @@ -75,10 +78,8 @@ public class SwiftLargeObjectUploadFeatureTest extends AbstractSwiftTest { cryptomator.create(session, new VaultCredentials("test"), vaultVersion); session.withRegistry(new DefaultVaultRegistry(new DisabledPasswordCallback(), cryptomator)); final SwiftRegionService regionService = new SwiftRegionService(session); - final CryptoUploadFeature m = new CryptoUploadFeature<>(session, - new SwiftLargeObjectUploadFeature(session, regionService, new SwiftWriteFeature(session, - regionService), 5242880L, 5), new SwiftWriteFeature(session, - regionService), cryptomator); + final CryptoUploadFeature service = new CryptoUploadFeature<>(session, + new SwiftLargeObjectUploadFeature(session, regionService, 5242880L, 5), cryptomator); final Local local = new Local(System.getProperty("java.io.tmpdir"), UUID.randomUUID().toString()); final int length = 5242885; final byte[] content = RandomUtils.nextBytes(length); @@ -88,7 +89,7 @@ public class SwiftLargeObjectUploadFeatureTest extends AbstractSwiftTest { writeStatus.setHeader(cryptomator.getFileHeaderCryptor().encryptHeader(header)); writeStatus.setLength(content.length); final BytecountStreamListener count = new BytecountStreamListener(); - m.upload(test, local, new BandwidthThrottle(BandwidthThrottle.UNLIMITED), new DisabledProgressListener(), count, writeStatus, null); + service.upload(new CryptoWriteFeature<>(session, new SwiftWriteFeature(session, new SwiftRegionService(session)), cryptomator), test, local, new BandwidthThrottle(BandwidthThrottle.UNLIMITED), new DisabledProgressListener(), count, writeStatus, null); assertEquals(content.length, count.getSent()); assertTrue(writeStatus.isComplete()); assertEquals(content.length, writeStatus.getResponse().getSize()); diff --git a/openstack/src/test/java/ch/cyberduck/core/cryptomator/SwiftListServiceTest.java b/openstack/src/test/java/ch/cyberduck/core/cryptomator/SwiftListServiceTest.java index a319f708e6..eb7c9e2507 100644 --- a/openstack/src/test/java/ch/cyberduck/core/cryptomator/SwiftListServiceTest.java +++ b/openstack/src/test/java/ch/cyberduck/core/cryptomator/SwiftListServiceTest.java @@ -22,6 +22,7 @@ import ch.cyberduck.core.DisabledPasswordCallback; import ch.cyberduck.core.Path; import ch.cyberduck.core.cryptomator.features.CryptoListService; import ch.cyberduck.core.cryptomator.features.CryptoTouchFeature; +import ch.cyberduck.core.cryptomator.features.CryptoWriteFeature; import ch.cyberduck.core.features.Delete; import ch.cyberduck.core.openstack.AbstractSwiftTest; import ch.cyberduck.core.openstack.SwiftDeleteFeature; @@ -62,8 +63,8 @@ public class SwiftListServiceTest extends AbstractSwiftTest { session.withRegistry(new DefaultVaultRegistry(new DisabledPasswordCallback(), cryptomator)); assertTrue(new CryptoListService(session, new SwiftObjectListService(session), cryptomator).list(vault, new DisabledListProgressListener()).isEmpty()); final SwiftRegionService regionService = new SwiftRegionService(session); - new CryptoTouchFeature<>(session, new DefaultTouchFeature(new SwiftWriteFeature(session, regionService) - ), new SwiftWriteFeature(session, regionService), cryptomator).touch(test, new TransferStatus()); + new CryptoTouchFeature<>(session, new DefaultTouchFeature( + session), cryptomator).touch(new CryptoWriteFeature<>(session, new SwiftWriteFeature(session, regionService), cryptomator), test, new TransferStatus()); assertEquals(test, new CryptoListService(session, new SwiftObjectListService(session), cryptomator).list(vault, new DisabledListProgressListener()).get(0)); cryptomator.getFeature(session, Delete.class, new SwiftDeleteFeature(session)).delete(Arrays.asList(test, vault), new DisabledLoginCallback(), new Delete.DisabledCallback()); } diff --git a/openstack/src/test/java/ch/cyberduck/core/cryptomator/SwiftMoveFeatureTest.java b/openstack/src/test/java/ch/cyberduck/core/cryptomator/SwiftMoveFeatureTest.java index 22d5675567..4d55f73a36 100644 --- a/openstack/src/test/java/ch/cyberduck/core/cryptomator/SwiftMoveFeatureTest.java +++ b/openstack/src/test/java/ch/cyberduck/core/cryptomator/SwiftMoveFeatureTest.java @@ -25,6 +25,7 @@ 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.openstack.AbstractSwiftTest; import ch.cyberduck.core.openstack.SwiftDeleteFeature; import ch.cyberduck.core.openstack.SwiftDirectoryFeature; @@ -47,8 +48,6 @@ import org.junit.runners.Parameterized; import java.util.Arrays; import java.util.EnumSet; -import ch.iterate.openstack.swift.model.StorageObject; - import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertTrue; @@ -66,8 +65,10 @@ public class SwiftMoveFeatureTest extends AbstractSwiftTest { cryptomator.create(session, new VaultCredentials("test"), vaultVersion); final SwiftRegionService regionService = new SwiftRegionService(session); session.withRegistry(new DefaultVaultRegistry(new DisabledPasswordCallback(), cryptomator)); - cryptomator.getFeature(session, Directory.class, new SwiftDirectoryFeature(session, regionService)).mkdir(folder, new TransferStatus()); - new CryptoTouchFeature(session, new SwiftTouchFeature(session, regionService), new SwiftWriteFeature(session, regionService), cryptomator).touch(file, new TransferStatus()); + cryptomator.getFeature(session, Directory.class, new SwiftDirectoryFeature(session, regionService)).mkdir( + cryptomator.getFeature(session, Write.class, new SwiftWriteFeature(session, regionService)), folder, new TransferStatus()); + new CryptoTouchFeature<>(session, new SwiftTouchFeature(session, regionService), cryptomator).touch( + cryptomator.getFeature(session, Write.class, new SwiftWriteFeature(session, regionService)), file, new TransferStatus()); assertTrue(cryptomator.getFeature(session, Find.class, new DefaultFindFeature(session)).find(file)); final Move move = cryptomator.getFeature(session, Move.class, new SwiftMoveFeature(session, regionService)); // rename file diff --git a/openstack/src/test/java/ch/cyberduck/core/cryptomator/SwiftTouchFeatureTest.java b/openstack/src/test/java/ch/cyberduck/core/cryptomator/SwiftTouchFeatureTest.java index 20dfe9318c..b1e15592ec 100644 --- a/openstack/src/test/java/ch/cyberduck/core/cryptomator/SwiftTouchFeatureTest.java +++ b/openstack/src/test/java/ch/cyberduck/core/cryptomator/SwiftTouchFeatureTest.java @@ -20,6 +20,7 @@ import ch.cyberduck.core.DisabledLoginCallback; import ch.cyberduck.core.DisabledPasswordCallback; import ch.cyberduck.core.Path; 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.openstack.AbstractSwiftTest; @@ -43,6 +44,8 @@ import org.junit.runners.Parameterized; import java.util.Arrays; import java.util.EnumSet; +import ch.iterate.openstack.swift.model.StorageObject; + import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertTrue; import static org.junit.Assume.assumeTrue; @@ -61,8 +64,8 @@ public class SwiftTouchFeatureTest extends AbstractSwiftTest { session.withRegistry(new DefaultVaultRegistry(new DisabledPasswordCallback(), cryptomator)); final SwiftRegionService regionService = new SwiftRegionService(session); final TransferStatus status = new TransferStatus(); - final Path test = new CryptoTouchFeature<>(session, new SwiftTouchFeature(session, regionService), new SwiftWriteFeature(session, regionService), cryptomator).touch( - new Path(vault, new AlphanumericRandomStringService(130).random(), EnumSet.of(Path.Type.file)), status); + final Path test = new CryptoTouchFeature<>(session, new SwiftTouchFeature(session, regionService), cryptomator).touch( + new CryptoWriteFeature<>(session, new SwiftWriteFeature(session, new SwiftRegionService(session)), cryptomator), new Path(vault, new AlphanumericRandomStringService(130).random(), EnumSet.of(Path.Type.file)), status); assertEquals(0L, test.attributes().getSize()); assertEquals(0L, status.getResponse().getSize()); assertTrue(cryptomator.getFeature(session, Find.class, new SwiftFindFeature(session)).find(test)); @@ -77,10 +80,9 @@ public class SwiftTouchFeatureTest extends AbstractSwiftTest { final CryptoVault cryptomator = new CryptoVault(vault); cryptomator.create(session, new VaultCredentials("test"), vaultVersion); session.withRegistry(new DefaultVaultRegistry(new DisabledPasswordCallback(), cryptomator)); - final SwiftRegionService regionService = new SwiftRegionService(session); final TransferStatus status = new TransferStatus(); - final Path test = new CryptoTouchFeature<>(session, new DefaultTouchFeature<>(new SwiftWriteFeature(session, regionService)), new SwiftWriteFeature(session, regionService), cryptomator).touch( - new Path(vault, new AlphanumericRandomStringService(130).random(), EnumSet.of(Path.Type.file)), status); + final Path test = new CryptoTouchFeature(session, new DefaultTouchFeature<>(session), cryptomator).touch( + new CryptoWriteFeature<>(session, new SwiftWriteFeature(session, new SwiftRegionService(session)), cryptomator), new Path(vault, new AlphanumericRandomStringService(130).random(), EnumSet.of(Path.Type.file)), status); assertEquals(0L, test.attributes().getSize()); assertEquals(0L, status.getResponse().getSize()); assertTrue(cryptomator.getFeature(session, Find.class, new DefaultFindFeature(session)).find(test)); diff --git a/openstack/src/test/java/ch/cyberduck/core/openstack/SwiftAttributesFinderFeatureTest.java b/openstack/src/test/java/ch/cyberduck/core/openstack/SwiftAttributesFinderFeatureTest.java index 151fab169d..e22898a857 100644 --- a/openstack/src/test/java/ch/cyberduck/core/openstack/SwiftAttributesFinderFeatureTest.java +++ b/openstack/src/test/java/ch/cyberduck/core/openstack/SwiftAttributesFinderFeatureTest.java @@ -57,7 +57,7 @@ public class SwiftAttributesFinderFeatureTest extends AbstractSwiftTest { container.attributes().setRegion("IAD"); final String name = new AlphanumericRandomStringService().random(); final Path test = new SwiftTouchFeature(session, new SwiftRegionService(session)).touch( - new Path(container, String.format("%s-", name), EnumSet.of(Path.Type.file)), new TransferStatus()); + new SwiftWriteFeature(session, new SwiftRegionService(session)), new Path(container, String.format("%s-", name), EnumSet.of(Path.Type.file)), new TransferStatus()); final SwiftAttributesFinderFeature f = new SwiftAttributesFinderFeature(session); final PathAttributes attributes = f.find(test); assertEquals(0L, attributes.getSize()); @@ -101,7 +101,7 @@ public class SwiftAttributesFinderFeatureTest extends AbstractSwiftTest { container.attributes().setRegion("IAD"); final String name = UUID.randomUUID().toString(); final Path file = new Path(container, name, EnumSet.of(Path.Type.directory)); - new SwiftDirectoryFeature(session).mkdir(file, new TransferStatus()); + new SwiftDirectoryFeature(session).mkdir(new SwiftWriteFeature(session, new SwiftRegionService(session)), file, new TransferStatus()); final PathAttributes attributes = new SwiftAttributesFinderFeature(session).find(file); assertNotNull(attributes.getChecksum().hash); // Test wrong type @@ -122,7 +122,7 @@ public class SwiftAttributesFinderFeatureTest extends AbstractSwiftTest { assertTrue(new SwiftFindFeature(session).find(container)); final String prefix = new AlphanumericRandomStringService().random(); final Path test = new SwiftTouchFeature(session, new SwiftRegionService(session)).touch( - new Path(new Path(container, prefix, EnumSet.of(Path.Type.directory)), + new SwiftWriteFeature(session, new SwiftRegionService(session)), new Path(new Path(container, prefix, EnumSet.of(Path.Type.directory)), new AsciiRandomStringService().random(), EnumSet.of(Path.Type.file)), new TransferStatus()); assertNotNull(new SwiftAttributesFinderFeature(session).find(test)); assertNotNull(new SwiftAttributesFinderFeature(session).find(new Path(container, prefix, EnumSet.of(Path.Type.directory)))); diff --git a/openstack/src/test/java/ch/cyberduck/core/openstack/SwiftDefaultCopyFeatureTest.java b/openstack/src/test/java/ch/cyberduck/core/openstack/SwiftDefaultCopyFeatureTest.java index ae1496d526..8fa2f3db58 100644 --- a/openstack/src/test/java/ch/cyberduck/core/openstack/SwiftDefaultCopyFeatureTest.java +++ b/openstack/src/test/java/ch/cyberduck/core/openstack/SwiftDefaultCopyFeatureTest.java @@ -45,7 +45,7 @@ public class SwiftDefaultCopyFeatureTest extends AbstractSwiftTest { final Path container = new Path("test.cyberduck.ch", EnumSet.of(Path.Type.directory, Path.Type.volume)); container.attributes().setRegion("IAD"); final Path test = new Path(container, UUID.randomUUID().toString(), EnumSet.of(Path.Type.file)); - new SwiftTouchFeature(session, new SwiftRegionService(session)).touch(test, new TransferStatus()); + new SwiftTouchFeature(session, new SwiftRegionService(session)).touch(new SwiftWriteFeature(session, new SwiftRegionService(session)), test, new TransferStatus()); final Path copy = new Path(container, UUID.randomUUID().toString(), EnumSet.of(Path.Type.file)); new SwiftDefaultCopyFeature(session).copy(test, copy, new TransferStatus(), new DisabledConnectionCallback(), new DisabledStreamListener()); assertTrue(new SwiftFindFeature(session).find(test)); @@ -58,11 +58,11 @@ public class SwiftDefaultCopyFeatureTest extends AbstractSwiftTest { final Path container = new Path("test.cyberduck.ch", EnumSet.of(Path.Type.directory, Path.Type.volume)); container.attributes().setRegion("IAD"); final Path folder = new Path(container, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory)); - new SwiftDirectoryFeature(session).mkdir(folder, new TransferStatus()); + new SwiftDirectoryFeature(session).mkdir(new SwiftWriteFeature(session, new SwiftRegionService(session)), folder, new TransferStatus()); final Path test = new Path(folder, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)); - new SwiftTouchFeature(session, new SwiftRegionService(session)).touch(test, new TransferStatus()); + new SwiftTouchFeature(session, new SwiftRegionService(session)).touch(new SwiftWriteFeature(session, new SwiftRegionService(session)), test, new TransferStatus()); final Path copy = new Path(folder, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)); - new SwiftTouchFeature(session, new SwiftRegionService(session)).touch(copy, new TransferStatus()); + new SwiftTouchFeature(session, new SwiftRegionService(session)).touch(new SwiftWriteFeature(session, new SwiftRegionService(session)), copy, new TransferStatus()); new SwiftDefaultCopyFeature(session).copy(test, copy, new TransferStatus().setExists(true), new DisabledConnectionCallback(), new DisabledStreamListener()); final Find find = new DefaultFindFeature(session); assertTrue(find.find(test)); diff --git a/openstack/src/test/java/ch/cyberduck/core/openstack/SwiftDeleteFeatureTest.java b/openstack/src/test/java/ch/cyberduck/core/openstack/SwiftDeleteFeatureTest.java index dcc7545376..fb355091d3 100644 --- a/openstack/src/test/java/ch/cyberduck/core/openstack/SwiftDeleteFeatureTest.java +++ b/openstack/src/test/java/ch/cyberduck/core/openstack/SwiftDeleteFeatureTest.java @@ -67,7 +67,7 @@ public class SwiftDeleteFeatureTest extends AbstractSwiftTest { new Path(container, "t", EnumSet.of(Path.Type.directory)), name, EnumSet.of(Path.Type.directory)); final Path test = new Path(placeholder, UUID.randomUUID().toString(), EnumSet.of(Path.Type.file)); - new SwiftTouchFeature(session, new SwiftRegionService(session)).touch(test, new TransferStatus()); + new SwiftTouchFeature(session, new SwiftRegionService(session)).touch(new SwiftWriteFeature(session, new SwiftRegionService(session)), test, new TransferStatus()); final SwiftFindFeature find = new SwiftFindFeature(session); assertTrue(find.find(placeholder)); final SwiftObjectListService list = new SwiftObjectListService(session); @@ -104,7 +104,7 @@ public class SwiftDeleteFeatureTest extends AbstractSwiftTest { final Path container = new Path("test.cyberduck.ch", EnumSet.of(Path.Type.directory, Path.Type.volume)); container.attributes().setRegion("IAD"); final Path placeholder = new Path(container, name, EnumSet.of(Path.Type.directory)); - new SwiftDirectoryFeature(session).mkdir(placeholder, new TransferStatus()); + new SwiftDirectoryFeature(session).mkdir(new SwiftWriteFeature(session, new SwiftRegionService(session)), placeholder, new TransferStatus()); final SwiftFindFeature find = new SwiftFindFeature(session); assertTrue(find.find(placeholder)); new SwiftDeleteFeature(session).delete(Collections.singletonList(placeholder), new DisabledLoginCallback(), new Delete.DisabledCallback()); diff --git a/openstack/src/test/java/ch/cyberduck/core/openstack/SwiftDirectoryFeatureTest.java b/openstack/src/test/java/ch/cyberduck/core/openstack/SwiftDirectoryFeatureTest.java index 2dd5bc2593..8bf8360f39 100644 --- a/openstack/src/test/java/ch/cyberduck/core/openstack/SwiftDirectoryFeatureTest.java +++ b/openstack/src/test/java/ch/cyberduck/core/openstack/SwiftDirectoryFeatureTest.java @@ -43,12 +43,12 @@ public class SwiftDirectoryFeatureTest extends AbstractSwiftTest { @Test public void testCreateContainer() throws Exception { final SwiftRegionService region = new SwiftRegionService(session); - final SwiftDirectoryFeature feature = new SwiftDirectoryFeature(session, region, new SwiftWriteFeature(session, region)); + final SwiftDirectoryFeature feature = new SwiftDirectoryFeature(session, region); final Path test = new Path(new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory)); - final Path container = feature.mkdir(test, new TransferStatus().setRegion("ORD")); + final Path container = feature.mkdir(new SwiftWriteFeature(session, new SwiftRegionService(session)), test, new TransferStatus().setRegion("ORD")); assertTrue(new SwiftFindFeature(session, region).find(container)); // Can create again regardless if exists - feature.mkdir(test, new TransferStatus()); + feature.mkdir(new SwiftWriteFeature(session, new SwiftRegionService(session)), test, new TransferStatus()); new SwiftDeleteFeature(session, region).delete(Collections.singletonList(container), new DisabledLoginCallback(), new Delete.DisabledCallback()); assertFalse(new SwiftFindFeature(session, region).find(container)); } @@ -72,9 +72,9 @@ public class SwiftDirectoryFeatureTest extends AbstractSwiftTest { final Path container = new Path("/test.cyberduck.ch", EnumSet.of(Path.Type.volume, Path.Type.directory)); container.attributes().setRegion("IAD"); final SwiftRegionService region = new SwiftRegionService(session); - final SwiftDirectoryFeature feature = new SwiftDirectoryFeature(session, region, new SwiftWriteFeature(session, region)); - final Path parent = feature.mkdir(new Path(container, parentname, EnumSet.of(Path.Type.directory)), new TransferStatus()); - final Path placeholder = feature.mkdir(new Path(parent, name, EnumSet.of(Path.Type.directory)), new TransferStatus()); + final SwiftDirectoryFeature feature = new SwiftDirectoryFeature(session, region); + final Path parent = feature.mkdir(new SwiftWriteFeature(session, region), new Path(container, parentname, EnumSet.of(Path.Type.directory)), new TransferStatus()); + final Path placeholder = feature.mkdir(new SwiftWriteFeature(session, region), new Path(parent, name, EnumSet.of(Path.Type.directory)), new TransferStatus()); assertTrue(put.get()); assertTrue(new SwiftFindFeature(session, region).find(placeholder)); assertTrue(new DefaultFindFeature(session).find(placeholder)); diff --git a/openstack/src/test/java/ch/cyberduck/core/openstack/SwiftDistributionConfigurationTest.java b/openstack/src/test/java/ch/cyberduck/core/openstack/SwiftDistributionConfigurationTest.java index cf65173633..a09a656f4b 100644 --- a/openstack/src/test/java/ch/cyberduck/core/openstack/SwiftDistributionConfigurationTest.java +++ b/openstack/src/test/java/ch/cyberduck/core/openstack/SwiftDistributionConfigurationTest.java @@ -42,10 +42,9 @@ public class SwiftDistributionConfigurationTest extends AbstractSwiftTest { @Test public void testWriteDownloadConfigurationRackspace() throws Exception { - final DistributionConfiguration configuration = new SwiftDistributionConfiguration(session - ); + final DistributionConfiguration configuration = new SwiftDistributionConfiguration(session); final Path container = new Path(UUID.randomUUID().toString(), EnumSet.of(Path.Type.volume, Path.Type.directory)); - new SwiftDirectoryFeature(session).mkdir(container, new TransferStatus().setRegion("ORD")); + new SwiftDirectoryFeature(session).mkdir(new SwiftWriteFeature(session, new SwiftRegionService(session)), container, new TransferStatus().setRegion("ORD")); configuration.write(container, new Distribution(Distribution.DOWNLOAD, true), new DisabledLoginCallback()); assertTrue(configuration.read(container, Distribution.DOWNLOAD, new DisabledLoginCallback()).isEnabled()); new SwiftDeleteFeature(session).delete(Collections.singletonList(container), new DisabledLoginCallback(), new Delete.DisabledCallback()); @@ -53,10 +52,9 @@ public class SwiftDistributionConfigurationTest extends AbstractSwiftTest { @Test public void testWriteWebsiteConfigurationRackspace() throws Exception { - final DistributionConfiguration configuration = new SwiftDistributionConfiguration(session - ); + final DistributionConfiguration configuration = new SwiftDistributionConfiguration(session); final Path container = new Path(UUID.randomUUID().toString(), EnumSet.of(Path.Type.volume, Path.Type.directory)); - new SwiftDirectoryFeature(session).mkdir(container, new TransferStatus().setRegion("ORD")); + new SwiftDirectoryFeature(session).mkdir(new SwiftWriteFeature(session, new SwiftRegionService(session)), container, new TransferStatus().setRegion("ORD")); final Distribution config = new Distribution(Distribution.WEBSITE, true); config.setIndexDocument("index.html"); configuration.write(container, config, new DisabledLoginCallback()); diff --git a/openstack/src/test/java/ch/cyberduck/core/openstack/SwiftFindFeatureTest.java b/openstack/src/test/java/ch/cyberduck/core/openstack/SwiftFindFeatureTest.java index 7eca730e8c..08431fbdb5 100644 --- a/openstack/src/test/java/ch/cyberduck/core/openstack/SwiftFindFeatureTest.java +++ b/openstack/src/test/java/ch/cyberduck/core/openstack/SwiftFindFeatureTest.java @@ -37,12 +37,12 @@ public class SwiftFindFeatureTest extends AbstractSwiftTest { container.attributes().setRegion("IAD"); final String prefix = new AlphanumericRandomStringService().random(); final Path other = new Path(container, String.format("%s.%s", prefix, new AlphanumericRandomStringService().random()), EnumSet.of(Path.Type.file)); - new SwiftTouchFeature(session, new SwiftRegionService(session)).touch(other, new TransferStatus()); + new SwiftTouchFeature(session, new SwiftRegionService(session)).touch(new SwiftWriteFeature(session, new SwiftRegionService(session)), other, new TransferStatus()); final Path file = new Path(container, prefix, EnumSet.of(Path.Type.file)); final SwiftFindFeature feature = new SwiftFindFeature(session); assertFalse(feature.find(file)); assertFalse(feature.find(new Path(file).withType(EnumSet.of(Path.Type.directory)))); - new SwiftTouchFeature(session, new SwiftRegionService(session)).touch(file, new TransferStatus()); + new SwiftTouchFeature(session, new SwiftRegionService(session)).touch(new SwiftWriteFeature(session, new SwiftRegionService(session)), file, new TransferStatus()); assertTrue(feature.find(file)); assertFalse(feature.find(new Path(file).withType(EnumSet.of(Path.Type.directory)))); assertFalse(feature.find(new Path(String.format("%s-", file.getAbsolute()), EnumSet.of(Path.Type.file)))); @@ -59,12 +59,12 @@ public class SwiftFindFeatureTest extends AbstractSwiftTest { container.attributes().setRegion("IAD"); final String suffix = new AlphanumericRandomStringService().random(); final Path other = new Path(container, String.format("%s.%s", new AlphanumericRandomStringService().random(), suffix), EnumSet.of(Path.Type.file)); - new SwiftTouchFeature(session, new SwiftRegionService(session)).touch(other, new TransferStatus()); + new SwiftTouchFeature(session, new SwiftRegionService(session)).touch(new SwiftWriteFeature(session, new SwiftRegionService(session)), other, new TransferStatus()); final Path file = new Path(container, suffix, EnumSet.of(Path.Type.file)); final SwiftFindFeature feature = new SwiftFindFeature(session); assertFalse(feature.find(file)); assertFalse(feature.find(new Path(file).withType(EnumSet.of(Path.Type.directory)))); - new SwiftTouchFeature(session, new SwiftRegionService(session)).touch(file, new TransferStatus()); + new SwiftTouchFeature(session, new SwiftRegionService(session)).touch(new SwiftWriteFeature(session, new SwiftRegionService(session)), file, new TransferStatus()); assertTrue(feature.find(file)); assertFalse(feature.find(new Path(file).withType(EnumSet.of(Path.Type.directory)))); assertFalse(feature.find(new Path(String.format("%s-", file.getAbsolute()), EnumSet.of(Path.Type.file)))); @@ -87,7 +87,7 @@ public class SwiftFindFeatureTest extends AbstractSwiftTest { assertTrue(new SwiftFindFeature(session).find(container)); final String prefix = new AlphanumericRandomStringService().random(); final Path test = new SwiftTouchFeature(session, new SwiftRegionService(session)).touch( - new Path(new Path(container, prefix, EnumSet.of(Path.Type.directory)), + new SwiftWriteFeature(session, new SwiftRegionService(session)), new Path(new Path(container, prefix, EnumSet.of(Path.Type.directory)), new AsciiRandomStringService().random(), EnumSet.of(Path.Type.file)), new TransferStatus()); assertTrue(new SwiftFindFeature(session).find(test)); assertTrue(new SwiftFindFeature(session).find(new Path(container, prefix, EnumSet.of(Path.Type.directory, Path.Type.placeholder)))); diff --git a/openstack/src/test/java/ch/cyberduck/core/openstack/SwiftLargeObjectUploadFeatureTest.java b/openstack/src/test/java/ch/cyberduck/core/openstack/SwiftLargeObjectUploadFeatureTest.java index b2c90283a4..0f87d29da9 100644 --- a/openstack/src/test/java/ch/cyberduck/core/openstack/SwiftLargeObjectUploadFeatureTest.java +++ b/openstack/src/test/java/ch/cyberduck/core/openstack/SwiftLargeObjectUploadFeatureTest.java @@ -63,8 +63,8 @@ public class SwiftLargeObjectUploadFeatureTest extends AbstractSwiftTest { final AtomicBoolean interrupt = new AtomicBoolean(); final BytecountStreamListener listener = new BytecountStreamListener(); try { - new SwiftLargeObjectUploadFeature(session, new SwiftRegionService(session), new SwiftWriteFeature(session, new SwiftRegionService(session)), - 1 * 1024L * 1024L, 1).upload(test, new Local(System.getProperty("java.io.tmpdir"), name) { + new SwiftLargeObjectUploadFeature(session, new SwiftRegionService(session), + 1 * 1024L * 1024L, 1).upload(new SwiftWriteFeature(session, new SwiftRegionService(session)), test, new Local(System.getProperty("java.io.tmpdir"), name) { @Override public InputStream getInputStream() throws AccessDeniedException { return new CountingInputStream(super.getInputStream()) { @@ -88,8 +88,8 @@ public class SwiftLargeObjectUploadFeatureTest extends AbstractSwiftTest { assertEquals(TransferStatus.UNKNOWN_LENGTH, status.getResponse().getSize()); final TransferStatus append = new TransferStatus().setAppend(true).setLength(content.length); - new SwiftLargeObjectUploadFeature(session, new SwiftRegionService(session), new SwiftWriteFeature(session, new SwiftRegionService(session)), - 1 * 1024L * 1024L, 1).upload(test, local, + new SwiftLargeObjectUploadFeature(session, new SwiftRegionService(session), + 1 * 1024L * 1024L, 1).upload(new SwiftWriteFeature(session, new SwiftRegionService(session)), test, local, new BandwidthThrottle(BandwidthThrottle.UNLIMITED), new DisabledProgressListener(), new DisabledStreamListener(), append, new DisabledLoginCallback()); assertEquals(content.length, append.getResponse().getSize()); @@ -120,21 +120,21 @@ public class SwiftLargeObjectUploadFeatureTest extends AbstractSwiftTest { status.setLength(content.length); final AtomicBoolean interrupt = new AtomicBoolean(); final SwiftRegionService regionService = new SwiftRegionService(session); - final SwiftLargeObjectUploadFeature feature = new SwiftLargeObjectUploadFeature(session, regionService, new SwiftWriteFeature(session, regionService), + final SwiftLargeObjectUploadFeature feature = new SwiftLargeObjectUploadFeature(session, regionService, 1024L * 1024L, 1) { @Override - public StorageObject 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 StorageObject upload(final Write 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() >= 1L * 1024L * 1024L) { 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 listener = new BytecountStreamListener(); try { - feature.upload(test, new Local(System.getProperty("java.io.tmpdir"), name), + feature.upload(new SwiftWriteFeature(session, new SwiftRegionService(session)), test, new Local(System.getProperty("java.io.tmpdir"), name), new BandwidthThrottle(BandwidthThrottle.UNLIMITED), new DisabledProgressListener(), listener, status, new DisabledLoginCallback()); } catch(BackgroundException e) { @@ -150,7 +150,7 @@ public class SwiftLargeObjectUploadFeatureTest extends AbstractSwiftTest { assertEquals(1 * 1024L * 1024L, new SwiftAttributesFinderFeature(session).find(test).getSize()); final TransferStatus append = new TransferStatus().setAppend(true).setLength(1024L * 1024L).setOffset(1024L * 1024L); - feature.upload(test, local, + feature.upload(new SwiftWriteFeature(session, new SwiftRegionService(session)), test, local, new BandwidthThrottle(BandwidthThrottle.UNLIMITED), new DisabledProgressListener(), listener, append, new DisabledLoginCallback()); assertEquals(2 * 1024L * 1024L, listener.getSent()); @@ -193,10 +193,10 @@ public class SwiftLargeObjectUploadFeatureTest extends AbstractSwiftTest { regionService, new SwiftObjectListService(session, regionService), new SwiftSegmentService(session, ".segments-test/"), - new SwiftWriteFeature(session, regionService), (long) (content.length / 2), 4); + (long) (content.length / 2), 4); final BytecountStreamListener count = new BytecountStreamListener(); - final StorageObject object = upload.upload(test, local, new BandwidthThrottle(BandwidthThrottle.UNLIMITED), new DisabledProgressListener(), count, + final StorageObject object = upload.upload(new SwiftWriteFeature(session, new SwiftRegionService(session)), test, local, new BandwidthThrottle(BandwidthThrottle.UNLIMITED), new DisabledProgressListener(), count, status, new DisabledConnectionCallback()); assertEquals(Checksum.NONE, Checksum.parse(object.getMd5sum())); assertNotEquals(Checksum.NONE, new SwiftAttributesFinderFeature(session).find(test).getChecksum()); @@ -246,10 +246,10 @@ public class SwiftLargeObjectUploadFeatureTest extends AbstractSwiftTest { regionService, new SwiftObjectListService(session, regionService), new SwiftSegmentService(session, ".segments-test/"), - new SwiftWriteFeature(session, regionService), 1048576L, 4); + 1048576L, 4); final BytecountStreamListener count = new BytecountStreamListener(); - upload.upload(test, local, new BandwidthThrottle(BandwidthThrottle.UNLIMITED), new DisabledProgressListener(), count, + upload.upload(new SwiftWriteFeature(session, regionService), test, local, new BandwidthThrottle(BandwidthThrottle.UNLIMITED), new DisabledProgressListener(), count, status, new DisabledConnectionCallback()); assertTrue(status.isComplete()); @@ -284,8 +284,7 @@ public class SwiftLargeObjectUploadFeatureTest extends AbstractSwiftTest { final Path container = new Path("test.cyberduck.ch", EnumSet.of(Path.Type.directory, Path.Type.volume)); container.attributes().setRegion("IAD"); final SwiftRegionService regionService = new SwiftRegionService(session); - final Write.Append append = new SwiftLargeObjectUploadFeature(session, regionService, - new SwiftWriteFeature(session, regionService)) + final Write.Append append = new SwiftLargeObjectUploadFeature(session, regionService) .append(new Path(container, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)), new TransferStatus()); assertFalse(append.append); assertEquals(Write.override, append); diff --git a/openstack/src/test/java/ch/cyberduck/core/openstack/SwiftLocationFeatureTest.java b/openstack/src/test/java/ch/cyberduck/core/openstack/SwiftLocationFeatureTest.java index 752c82dbb9..af47e0073f 100644 --- a/openstack/src/test/java/ch/cyberduck/core/openstack/SwiftLocationFeatureTest.java +++ b/openstack/src/test/java/ch/cyberduck/core/openstack/SwiftLocationFeatureTest.java @@ -2,6 +2,7 @@ package ch.cyberduck.core.openstack; import ch.cyberduck.core.Path; import ch.cyberduck.core.exception.NotfoundException; +import ch.cyberduck.core.features.Home; import ch.cyberduck.core.features.Location; import ch.cyberduck.test.IntegrationTest; @@ -19,7 +20,7 @@ public class SwiftLocationFeatureTest extends AbstractSwiftTest { @Test public void testGetLocations() throws Exception { - final Set locations = new SwiftLocationFeature(session).getLocations(); + final Set locations = new SwiftLocationFeature(session).getLocations(Home.root()); assertTrue(locations.contains(new SwiftLocationFeature.SwiftRegion("DFW"))); assertTrue(locations.contains(new SwiftLocationFeature.SwiftRegion("ORD"))); assertTrue(locations.contains(new SwiftLocationFeature.SwiftRegion("SYD"))); diff --git a/openstack/src/test/java/ch/cyberduck/core/openstack/SwiftMetadataFeatureTest.java b/openstack/src/test/java/ch/cyberduck/core/openstack/SwiftMetadataFeatureTest.java index 4d0de5e69d..1f84ffa99e 100644 --- a/openstack/src/test/java/ch/cyberduck/core/openstack/SwiftMetadataFeatureTest.java +++ b/openstack/src/test/java/ch/cyberduck/core/openstack/SwiftMetadataFeatureTest.java @@ -50,7 +50,7 @@ public class SwiftMetadataFeatureTest extends AbstractSwiftTest { final Path container = new Path("test.cyberduck.ch", EnumSet.of(Path.Type.directory, Path.Type.volume)); container.attributes().setRegion("IAD"); final Path test = new Path(container, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)); - new SwiftTouchFeature(session, new SwiftRegionService(session)).touch(test, new TransferStatus().setMime("text/plain")); + new SwiftTouchFeature(session, new SwiftRegionService(session)).touch(new SwiftWriteFeature(session, new SwiftRegionService(session)), test, new TransferStatus().setMime("text/plain")); final String v = UUID.randomUUID().toString(); final SwiftMetadataFeature feature = new SwiftMetadataFeature(session); feature.setMetadata(test, Collections.singletonMap("Test", v)); diff --git a/openstack/src/test/java/ch/cyberduck/core/openstack/SwiftMoveFeatureTest.java b/openstack/src/test/java/ch/cyberduck/core/openstack/SwiftMoveFeatureTest.java index 7625b3dfa1..aaeab10452 100644 --- a/openstack/src/test/java/ch/cyberduck/core/openstack/SwiftMoveFeatureTest.java +++ b/openstack/src/test/java/ch/cyberduck/core/openstack/SwiftMoveFeatureTest.java @@ -52,7 +52,7 @@ public class SwiftMoveFeatureTest extends AbstractSwiftTest { container.attributes().setRegion("IAD"); final Path test = new Path(container, UUID.randomUUID().toString(), EnumSet.of(Path.Type.file)); test.attributes().setRegion("IAD"); - new SwiftTouchFeature(session, new SwiftRegionService(session)).touch(test, new TransferStatus()); + new SwiftTouchFeature(session, new SwiftRegionService(session)).touch(new SwiftWriteFeature(session, new SwiftRegionService(session)), test, new TransferStatus()); assertTrue(new SwiftFindFeature(session).find(test)); final Path target = new Path(container, UUID.randomUUID().toString(), EnumSet.of(Path.Type.file)); target.attributes().setRegion("IAD"); @@ -68,10 +68,10 @@ public class SwiftMoveFeatureTest extends AbstractSwiftTest { container.attributes().setRegion("IAD"); final Path test = new Path(container, UUID.randomUUID().toString(), EnumSet.of(Path.Type.file)); test.attributes().setRegion("IAD"); - new SwiftTouchFeature(session, new SwiftRegionService(session)).touch(test, new TransferStatus()); + new SwiftTouchFeature(session, new SwiftRegionService(session)).touch(new SwiftWriteFeature(session, new SwiftRegionService(session)), test, new TransferStatus()); final Path target = new Path(container, UUID.randomUUID().toString(), EnumSet.of(Path.Type.file)); target.attributes().setRegion("IAD"); - new SwiftTouchFeature(session, new SwiftRegionService(session)).touch(target, new TransferStatus()); + new SwiftTouchFeature(session, new SwiftRegionService(session)).touch(new SwiftWriteFeature(session, new SwiftRegionService(session)), target, new TransferStatus()); new SwiftMoveFeature(session).move(test, target, new TransferStatus(), new Delete.DisabledCallback(), new DisabledConnectionCallback()); assertFalse(new SwiftFindFeature(session).find(test)); assertTrue(new SwiftFindFeature(session).find(target)); diff --git a/openstack/src/test/java/ch/cyberduck/core/openstack/SwiftMultipleDeleteFeatureTest.java b/openstack/src/test/java/ch/cyberduck/core/openstack/SwiftMultipleDeleteFeatureTest.java index cefdee1034..fee20e07a7 100644 --- a/openstack/src/test/java/ch/cyberduck/core/openstack/SwiftMultipleDeleteFeatureTest.java +++ b/openstack/src/test/java/ch/cyberduck/core/openstack/SwiftMultipleDeleteFeatureTest.java @@ -7,6 +7,7 @@ import ch.cyberduck.core.Path; import ch.cyberduck.core.exception.BackgroundException; import ch.cyberduck.core.exception.NotfoundException; import ch.cyberduck.core.features.Delete; +import ch.cyberduck.core.features.Home; import ch.cyberduck.core.features.Location; import ch.cyberduck.test.IntegrationTest; @@ -36,7 +37,7 @@ public class SwiftMultipleDeleteFeatureTest extends AbstractSwiftTest { @Test public void testDeleteMultiple() throws Exception { final Path container = new Path("test.cyberduck.ch", EnumSet.of(Path.Type.directory, Path.Type.volume)); - for(Location.Name region : new SwiftLocationFeature(session).getLocations()) { + for(Location.Name region : new SwiftLocationFeature(session).getLocations(Home.root())) { container.attributes().setRegion(region.getIdentifier()); new SwiftListService(session, new SwiftRegionService(session)).list(container, new ListProgressListener() { @Override diff --git a/openstack/src/test/java/ch/cyberduck/core/openstack/SwiftObjectListServiceTest.java b/openstack/src/test/java/ch/cyberduck/core/openstack/SwiftObjectListServiceTest.java index 8df46236d0..6b0da4f8be 100644 --- a/openstack/src/test/java/ch/cyberduck/core/openstack/SwiftObjectListServiceTest.java +++ b/openstack/src/test/java/ch/cyberduck/core/openstack/SwiftObjectListServiceTest.java @@ -46,16 +46,16 @@ public class SwiftObjectListServiceTest extends AbstractSwiftTest { public void testListDirectory() throws Exception { final Path container = new Path("test.cyberduck.ch", EnumSet.of(Path.Type.directory, Path.Type.volume)); container.attributes().setRegion("IAD"); - final Path placeholder = new SwiftDirectoryFeature(session).mkdir(new Path(container, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory)), new TransferStatus()); + final Path placeholder = new SwiftDirectoryFeature(session).mkdir(new SwiftWriteFeature(session, new SwiftRegionService(session)), new Path(container, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory)), new TransferStatus()); final Path f1 = new SwiftTouchFeature(session, new SwiftRegionService(session)).touch( - new Path(placeholder, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)), new TransferStatus()); + new SwiftWriteFeature(session, new SwiftRegionService(session)), new Path(placeholder, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)), new TransferStatus()); final Path f2 = new SwiftTouchFeature(session, new SwiftRegionService(session)).touch( - new Path(placeholder, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)), new TransferStatus()); + new SwiftWriteFeature(session, new SwiftRegionService(session)), new Path(placeholder, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)), new TransferStatus()); final Path d1 = new SwiftDirectoryFeature(session, new SwiftRegionService(session)).mkdir( - new Path(placeholder, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory)), new TransferStatus()); + new SwiftWriteFeature(session, new SwiftRegionService(session)), new Path(placeholder, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory)), new TransferStatus()); // Must add file in directory for virtual prefix to be returned in directory listing final Path d1f1 = new SwiftTouchFeature(session, new SwiftRegionService(session)).touch( - new Path(d1, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)), new TransferStatus()); + new SwiftWriteFeature(session, new SwiftRegionService(session)), new Path(d1, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)), new TransferStatus()); final AttributedList list = new SwiftObjectListService(session).list(placeholder, new DisabledListProgressListener()); assertEquals(3, list.size()); assertTrue(list.contains(f1)); @@ -68,9 +68,9 @@ public class SwiftObjectListServiceTest extends AbstractSwiftTest { public void testListDotInKey() throws Exception { final Path container = new Path("test.cyberduck.ch", EnumSet.of(Path.Type.directory, Path.Type.volume)); container.attributes().setRegion("IAD"); - final Path placeholder = new SwiftDirectoryFeature(session).mkdir(new Path(container, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory)), new TransferStatus()); + final Path placeholder = new SwiftDirectoryFeature(session).mkdir(new SwiftWriteFeature(session, new SwiftRegionService(session)), new Path(container, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory)), new TransferStatus()); final Path test = new SwiftTouchFeature(session, new SwiftRegionService(session)).touch( - new Path(placeholder, String.format("%s..", new AlphanumericRandomStringService().random()), EnumSet.of(Path.Type.file)), new TransferStatus()); + new SwiftWriteFeature(session, new SwiftRegionService(session)), new Path(placeholder, String.format("%s..", new AlphanumericRandomStringService().random()), EnumSet.of(Path.Type.file)), new TransferStatus()); final AttributedList list = new SwiftObjectListService(session).list(placeholder, new DisabledListProgressListener()); assertEquals(1, list.size()); assertTrue(list.contains(test)); @@ -89,7 +89,7 @@ public class SwiftObjectListServiceTest extends AbstractSwiftTest { catch(NotfoundException e) { // Expected } - final Path file = new SwiftTouchFeature(session, new SwiftRegionService(session)).touch(new Path(container, String.format("%s-", name), EnumSet.of(Path.Type.file)), new TransferStatus()); + final Path file = new SwiftTouchFeature(session, new SwiftRegionService(session)).touch(new SwiftWriteFeature(session, new SwiftRegionService(session)), new Path(container, String.format("%s-", name), EnumSet.of(Path.Type.file)), new TransferStatus()); try { new SwiftObjectListService(session).list(new Path(container, name, EnumSet.of(Path.Type.directory)), new DisabledListProgressListener()); fail(); @@ -105,7 +105,7 @@ public class SwiftObjectListServiceTest extends AbstractSwiftTest { final Path container = new Path("test.cyberduck.ch", EnumSet.of(Path.Type.directory, Path.Type.volume)); container.attributes().setRegion("IAD"); final String name = new AlphanumericRandomStringService().random(); - final Path file = new SwiftTouchFeature(session, new SwiftRegionService(session)).touch(new Path(container, name, EnumSet.of(Path.Type.file)), new TransferStatus()); + final Path file = new SwiftTouchFeature(session, new SwiftRegionService(session)).touch(new SwiftWriteFeature(session, new SwiftRegionService(session)), new Path(container, name, EnumSet.of(Path.Type.file)), new TransferStatus()); try { new SwiftObjectListService(session).list(new Path(container, name, EnumSet.of(Path.Type.directory)), new DisabledListProgressListener()); fail(); @@ -126,7 +126,7 @@ public class SwiftObjectListServiceTest extends AbstractSwiftTest { public void testListPlaceholder() throws Exception { final Path container = new Path("test.cyberduck.ch", EnumSet.of(Path.Type.directory, Path.Type.volume)); container.attributes().setRegion("IAD"); - final Path placeholder = new SwiftDirectoryFeature(session).mkdir(new Path(container, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory)), new TransferStatus()); + final Path placeholder = new SwiftDirectoryFeature(session).mkdir(new SwiftWriteFeature(session, new SwiftRegionService(session)), new Path(container, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory)), new TransferStatus()); final AtomicBoolean callback = new AtomicBoolean(); assertTrue(new SwiftObjectListService(session).list(placeholder, new DisabledListProgressListener() { @Override @@ -136,7 +136,7 @@ public class SwiftObjectListServiceTest extends AbstractSwiftTest { } }).isEmpty()); assertTrue(callback.get()); - final Path placeholder2 = new SwiftDirectoryFeature(session).mkdir(new Path(placeholder, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory)), new TransferStatus()); + final Path placeholder2 = new SwiftDirectoryFeature(session).mkdir(new SwiftWriteFeature(session, new SwiftRegionService(session)), new Path(placeholder, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory)), new TransferStatus()); assertTrue(new SwiftObjectListService(session).list(placeholder2, new DisabledListProgressListener()).isEmpty()); new SwiftDeleteFeature(session).delete(Arrays.asList(placeholder, placeholder2), new DisabledLoginCallback(), new Delete.DisabledCallback()); } @@ -147,7 +147,7 @@ public class SwiftObjectListServiceTest extends AbstractSwiftTest { container.attributes().setRegion("IAD"); final String name = new AlphanumericRandomStringService().random(); final Path placeholder = new Path(container, name, EnumSet.of(Path.Type.directory)); - new SwiftDirectoryFeature(session).mkdir(placeholder, new TransferStatus()); + new SwiftDirectoryFeature(session).mkdir(new SwiftWriteFeature(session, new SwiftRegionService(session)), placeholder, new TransferStatus()); final AttributedList list = new SwiftObjectListService(session).list(placeholder.getParent(), new DisabledListProgressListener()); assertTrue(list.contains(placeholder)); assertTrue(list.contains(new Path(container, name, EnumSet.of(Path.Type.directory, Path.Type.placeholder)))); @@ -161,9 +161,9 @@ public class SwiftObjectListServiceTest extends AbstractSwiftTest { container.attributes().setRegion("IAD"); final SwiftRegionService regionService = new SwiftRegionService(session); final Path base = new SwiftTouchFeature(session, regionService).touch( - new Path(container, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)), new TransferStatus()); + new SwiftWriteFeature(session, new SwiftRegionService(session)), new Path(container, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)), new TransferStatus()); final Path child = new SwiftTouchFeature(session, regionService).touch( - new Path(base, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)), new TransferStatus()); + new SwiftWriteFeature(session, new SwiftRegionService(session)), new Path(base, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)), new TransferStatus()); { final AttributedList list = new SwiftObjectListService(session).list(container, new DisabledListProgressListener()); assertTrue(list.contains(base)); @@ -191,7 +191,7 @@ public class SwiftObjectListServiceTest extends AbstractSwiftTest { container.attributes().setRegion("IAD"); final SwiftRegionService regionService = new SwiftRegionService(session); final Path directory = new SwiftDirectoryFeature(session, regionService).mkdir( - new Path(container, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory)), new TransferStatus()); + new SwiftWriteFeature(session, new SwiftRegionService(session)), new Path(container, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory)), new TransferStatus()); final List files = Arrays.asList( "Z", "aa", "0a", "a", "AAA", "B", "~$a", ".c" ); @@ -208,7 +208,7 @@ public class SwiftObjectListServiceTest extends AbstractSwiftTest { } }).isEmpty()); for(String f : files) { - new SwiftTouchFeature(session, regionService).touch(new Path(directory, f, EnumSet.of(Path.Type.file)), new TransferStatus()); + new SwiftTouchFeature(session, regionService).touch(new SwiftWriteFeature(session, new SwiftRegionService(session)), new Path(directory, f, EnumSet.of(Path.Type.file)), new TransferStatus()); } final AttributedList list = new SwiftObjectListService(session, regionService).list(directory, new DisabledListProgressListener()); for(int i = 0; i < list.size(); i++) { diff --git a/openstack/src/test/java/ch/cyberduck/core/openstack/SwiftReadFeatureTest.java b/openstack/src/test/java/ch/cyberduck/core/openstack/SwiftReadFeatureTest.java index 1fa0e83758..9db2a304b4 100644 --- a/openstack/src/test/java/ch/cyberduck/core/openstack/SwiftReadFeatureTest.java +++ b/openstack/src/test/java/ch/cyberduck/core/openstack/SwiftReadFeatureTest.java @@ -45,7 +45,7 @@ public class SwiftReadFeatureTest extends AbstractSwiftTest { final Path container = new Path("test.cyberduck.ch", EnumSet.of(Path.Type.directory, Path.Type.volume)); container.attributes().setRegion("IAD"); final Path test = new Path(container, UUID.randomUUID().toString(), EnumSet.of(Path.Type.file)); - new SwiftTouchFeature(session, new SwiftRegionService(session)).touch(test, new TransferStatus()); + new SwiftTouchFeature(session, new SwiftRegionService(session)).touch(new SwiftWriteFeature(session, new SwiftRegionService(session)), test, new TransferStatus()); final byte[] content = RandomUtils.nextBytes(1023); final SwiftRegionService regionService = new SwiftRegionService(session); final HttpResponseOutputStream out = new SwiftWriteFeature(session, regionService).write(test, new TransferStatus().setLength(content.length), new DisabledConnectionCallback()); @@ -74,7 +74,7 @@ public class SwiftReadFeatureTest extends AbstractSwiftTest { final Path container = new Path("test.cyberduck.ch", EnumSet.of(Path.Type.directory, Path.Type.volume)); container.attributes().setRegion("IAD"); final Path test = new Path(container, UUID.randomUUID().toString(), EnumSet.of(Path.Type.file)); - new SwiftTouchFeature(session, new SwiftRegionService(session)).touch(test, new TransferStatus()); + new SwiftTouchFeature(session, new SwiftRegionService(session)).touch(new SwiftWriteFeature(session, new SwiftRegionService(session)), test, new TransferStatus()); final byte[] content = RandomUtils.nextBytes(1023); final SwiftRegionService regionService = new SwiftRegionService(session); final HttpResponseOutputStream out = new SwiftWriteFeature(session, regionService).write(test, new TransferStatus().setLength(content.length), new DisabledConnectionCallback()); @@ -110,7 +110,7 @@ public class SwiftReadFeatureTest extends AbstractSwiftTest { EnumSet.of(Path.Type.file)), status, new DisabledConnectionCallback()); assertNotNull(in); final BytecountStreamListener count = new BytecountStreamListener(); - new StreamCopier(status, status).withListener(count).transfer(in, NullOutputStream.NULL_OUTPUT_STREAM); + new StreamCopier(status, status).withListener(count).transfer(in, NullOutputStream.INSTANCE); assertEquals(182L, count.getRecv()); assertEquals(182L, status.getLength()); in.close(); diff --git a/openstack/src/test/java/ch/cyberduck/core/openstack/SwiftSessionTest.java b/openstack/src/test/java/ch/cyberduck/core/openstack/SwiftSessionTest.java index fcab2f31dd..32d3a764a2 100644 --- a/openstack/src/test/java/ch/cyberduck/core/openstack/SwiftSessionTest.java +++ b/openstack/src/test/java/ch/cyberduck/core/openstack/SwiftSessionTest.java @@ -23,6 +23,8 @@ import ch.cyberduck.core.features.Versioning; import ch.cyberduck.core.proxy.DisabledProxyFinder; import ch.cyberduck.core.serializer.impl.dd.ProfilePlistReader; import ch.cyberduck.core.shared.DefaultVersioningFeature; +import ch.cyberduck.core.ssl.DefaultX509KeyManager; +import ch.cyberduck.core.ssl.DisabledX509TrustManager; import ch.cyberduck.test.IntegrationTest; import org.junit.Test; @@ -70,6 +72,8 @@ public class SwiftSessionTest extends AbstractSwiftTest { final Host host = new Host(profile, profile.getDefaultHostname(), new Credentials( PROPERTIES.get("rackspace.user"), PROPERTIES.get("rackspace.password") )); + final SwiftSession session = new SwiftSession(host, new DisabledX509TrustManager(), new DefaultX509KeyManager()); + assertNotNull(session.open(new DisabledProxyFinder(), new DisabledHostKeyCallback(), new DisabledLoginCallback(), new DisabledCancelCallback())); assertTrue(session.isConnected()); session.close(); assertFalse(session.isConnected()); @@ -81,6 +85,7 @@ public class SwiftSessionTest extends AbstractSwiftTest { final Host host = new Host(new SwiftProtocol(), "identity.api.rackspacecloud.com", new Credentials( "a", "s" )); + final SwiftSession session = new SwiftSession(host, new DisabledX509TrustManager(), new DefaultX509KeyManager()); assertNotNull(session.open(new DisabledProxyFinder(), new DisabledHostKeyCallback(), new DisabledLoginCallback(), new DisabledCancelCallback())); assertTrue(session.isConnected()); assertNotNull(session.getClient()); diff --git a/openstack/src/test/java/ch/cyberduck/core/openstack/SwiftSmallObjectUploadFeatureTest.java b/openstack/src/test/java/ch/cyberduck/core/openstack/SwiftSmallObjectUploadFeatureTest.java index 9b57459fe3..5cc86a01a9 100644 --- a/openstack/src/test/java/ch/cyberduck/core/openstack/SwiftSmallObjectUploadFeatureTest.java +++ b/openstack/src/test/java/ch/cyberduck/core/openstack/SwiftSmallObjectUploadFeatureTest.java @@ -39,8 +39,7 @@ public class SwiftSmallObjectUploadFeatureTest extends AbstractSwiftTest { @Test public void testDecorate() throws Exception { final NullInputStream n = new NullInputStream(1L); - assertSame(NullInputStream.class, new SwiftSmallObjectUploadFeature(session, new SwiftWriteFeature( - session, new SwiftRegionService(session))).decorate(n, null).getClass()); + assertSame(NullInputStream.class, new SwiftSmallObjectUploadFeature(session).decorate(n, null).getClass()); } @Test(expected = ChecksumException.class) @@ -48,8 +47,7 @@ public class SwiftSmallObjectUploadFeatureTest extends AbstractSwiftTest { final StorageObject o = new StorageObject("f"); o.setMd5sum("d41d8cd98f00b204e9800998ecf8427f"); try { - new SwiftSmallObjectUploadFeature(session, new SwiftWriteFeature( - session, new SwiftRegionService(session))).post( + new SwiftSmallObjectUploadFeature(session).post( new Path("/f", EnumSet.of(Path.Type.file)), MessageDigest.getInstance("MD5"), o ); } @@ -64,8 +62,7 @@ public class SwiftSmallObjectUploadFeatureTest extends AbstractSwiftTest { public void testPostChecksum() throws Exception { final StorageObject o = new StorageObject("f"); o.setMd5sum("d41d8cd98f00b204e9800998ecf8427e"); - new SwiftSmallObjectUploadFeature(session, new SwiftWriteFeature( - session, new SwiftRegionService(session))).post( + new SwiftSmallObjectUploadFeature(session).post( new Path("/f", EnumSet.of(Path.Type.file)), MessageDigest.getInstance("MD5"), o ); } diff --git a/openstack/src/test/java/ch/cyberduck/core/openstack/SwiftTouchFeatureTest.java b/openstack/src/test/java/ch/cyberduck/core/openstack/SwiftTouchFeatureTest.java index 003a28fc0d..64dcdd3e2f 100644 --- a/openstack/src/test/java/ch/cyberduck/core/openstack/SwiftTouchFeatureTest.java +++ b/openstack/src/test/java/ch/cyberduck/core/openstack/SwiftTouchFeatureTest.java @@ -30,7 +30,7 @@ public class SwiftTouchFeatureTest extends AbstractSwiftTest { final Path container = new Path("test.cyberduck.ch", EnumSet.of(Path.Type.directory, Path.Type.volume)); container.attributes().setRegion("IAD"); final Path test = new SwiftTouchFeature(session, new SwiftRegionService(session)).touch( - new Path(container, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)), new TransferStatus()); + new SwiftWriteFeature(session, new SwiftRegionService(session)), new Path(container, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)), new TransferStatus()); final PathAttributes attributes = new SwiftAttributesFinderFeature(session).find(test); assertEquals(test.attributes().getChecksum(), attributes.getChecksum()); assertNotEquals(-1L, attributes.getModificationDate()); diff --git a/openstack/src/test/java/ch/cyberduck/core/openstack/SwiftUrlProviderTest.java b/openstack/src/test/java/ch/cyberduck/core/openstack/SwiftUrlProviderTest.java index 9c5ee654f6..4b0d92aea0 100644 --- a/openstack/src/test/java/ch/cyberduck/core/openstack/SwiftUrlProviderTest.java +++ b/openstack/src/test/java/ch/cyberduck/core/openstack/SwiftUrlProviderTest.java @@ -60,7 +60,7 @@ public class SwiftUrlProviderTest extends AbstractSwiftTest { container.attributes().setRegion("IAD"); final Path file = new Path(container, UUID.randomUUID().toString(), EnumSet.of(Path.Type.file)); container.attributes().setRegion("IAD"); - new SwiftTouchFeature(session, new SwiftRegionService(session)).touch(file, new TransferStatus()); + new SwiftTouchFeature(session, new SwiftRegionService(session)).touch(new SwiftWriteFeature(session, new SwiftRegionService(session)), file, new TransferStatus()); final DescriptiveUrlBag list = provider.toUrl(file); final DescriptiveUrl signed = list.find(DescriptiveUrl.Type.signed); assertNotNull(signed); diff --git a/openstack/src/test/java/ch/cyberduck/core/worker/SwiftSingleTransferWorkerTest.java b/openstack/src/test/java/ch/cyberduck/core/worker/SwiftSingleTransferWorkerTest.java index 914040c559..f7d6709ed4 100644 --- a/openstack/src/test/java/ch/cyberduck/core/worker/SwiftSingleTransferWorkerTest.java +++ b/openstack/src/test/java/ch/cyberduck/core/worker/SwiftSingleTransferWorkerTest.java @@ -36,7 +36,6 @@ import ch.cyberduck.core.openstack.SwiftLargeObjectUploadFeature; import ch.cyberduck.core.openstack.SwiftProtocol; import ch.cyberduck.core.openstack.SwiftRegionService; import ch.cyberduck.core.openstack.SwiftSession; -import ch.cyberduck.core.openstack.SwiftWriteFeature; import ch.cyberduck.core.ssl.DefaultX509KeyManager; import ch.cyberduck.core.ssl.DisabledX509TrustManager; import ch.cyberduck.core.transfer.DisabledTransferErrorCallback; @@ -100,7 +99,7 @@ public class SwiftSingleTransferWorkerTest extends VaultTest { public T _getFeature(final Class type) { if(type == Upload.class) { final SwiftRegionService regionService = new SwiftRegionService(this); - return (T) new SwiftLargeObjectUploadFeature(this, regionService, new SwiftWriteFeature(this, regionService), 1024L * 1024L, 5) { + return (T) new SwiftLargeObjectUploadFeature(this, regionService, 1024L * 1024L, 5) { @Override protected InputStream decorate(final InputStream in, final MessageDigest digest) { if(failed.get()) { diff --git a/osx/docktile/main.swift b/osx/docktile/main.swift new file mode 100644 index 0000000000..fefb13a7c4 --- /dev/null +++ b/osx/docktile/main.swift @@ -0,0 +1,33 @@ +/* + * Copyright (c) 2025 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 AppKit + +class CustomTilePlugIn: NSObject, NSDockTilePlugIn { + + func setDockTile(_ dockTile: NSDockTile?) { + if let dockTile = dockTile { + // A DockTile was provided by the system + guard let image = Bundle(for: Self.self).image(forResource: "cyberduck-application-rect") + else { + return + } + dockTile.contentView = NSImageView(image: image) + dockTile.display() + } else { + // Application icon was removed from the Dock + } + } +} diff --git a/osx/pom.xml b/osx/pom.xml index 71f897aa2b..0dc9a38b71 100644 --- a/osx/pom.xml +++ b/osx/pom.xml @@ -18,7 +18,7 @@ ch.cyberduck parent - 9.2.0-SNAPSHOT + 9.4.0-SNAPSHOT osx jar @@ -115,7 +115,7 @@ org.sparkle-project sparkle zip - 2.6.3 + 2.8.1 ${project.build.directory} @@ -133,7 +133,7 @@ net.temurin jdk zip - 21.0.7 + 21.0.9 ${project.build.directory} @@ -229,18 +229,15 @@ ${project.version} - - - sparkle - - - - !env.JENKINS_HOME - - Release - local + --entitlements ../setup/app/default.entitlements --requirements ../setup/app/codesign-requirement.bin + default.provisionprofile + + + + debug + Apple Development: David Kocher (DF448WW9PY) Apple Development: David Kocher (DF448WW9PY) --entitlements ../setup/app/default.entitlements @@ -252,6 +249,14 @@ --entitlements ../setup/app/sandbox.entitlements + + appstore + + Mac App Store + appstore.provisionprofile + true + + installer diff --git a/osx/src/main/java/ch/cyberduck/ui/cocoa/callback/PromptAlertCallback.java b/osx/src/main/java/ch/cyberduck/ui/cocoa/callback/PromptAlertCallback.java index 3519b6408f..7af880d1c6 100644 --- a/osx/src/main/java/ch/cyberduck/ui/cocoa/callback/PromptAlertCallback.java +++ b/osx/src/main/java/ch/cyberduck/ui/cocoa/callback/PromptAlertCallback.java @@ -29,7 +29,11 @@ import ch.cyberduck.core.threading.DefaultFailureDiagnostics; import ch.cyberduck.core.threading.FailureDiagnostics; import ch.cyberduck.ui.cocoa.controller.BackgroundExceptionAlertController; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; + public class PromptAlertCallback implements AlertCallback { + private static final Logger log = LogManager.getLogger(PromptAlertCallback.class); private final ProxyController controller; private final NotificationAlertCallback notification = new NotificationAlertCallback(); @@ -40,6 +44,7 @@ public class PromptAlertCallback implements AlertCallback { @Override public boolean alert(final Host host, final BackgroundException failure) { + log.warn("Notify for failure {}", failure.toString()); final FailureDiagnostics.Type type = new DefaultFailureDiagnostics().determine(failure); switch(type) { case cancel: diff --git a/osx/src/main/java/ch/cyberduck/ui/cocoa/callback/PromptCertificateIdentityCallback.java b/osx/src/main/java/ch/cyberduck/ui/cocoa/callback/PromptCertificateIdentityCallback.java index d4917ad60b..216c6e2ef7 100644 --- a/osx/src/main/java/ch/cyberduck/ui/cocoa/callback/PromptCertificateIdentityCallback.java +++ b/osx/src/main/java/ch/cyberduck/ui/cocoa/callback/PromptCertificateIdentityCallback.java @@ -16,6 +16,7 @@ package ch.cyberduck.ui.cocoa.callback; */ import ch.cyberduck.binding.AlertRunner; +import ch.cyberduck.binding.Outlet; import ch.cyberduck.binding.ProxyController; import ch.cyberduck.binding.SheetController; import ch.cyberduck.binding.WindowController; @@ -31,10 +32,10 @@ import ch.cyberduck.core.keychain.SFChooseIdentityPanel; import ch.cyberduck.core.keychain.SecIdentityRef; import ch.cyberduck.core.keychain.SecPolicyRef; import ch.cyberduck.core.keychain.SecurityFunctions; -import ch.cyberduck.core.threading.DefaultMainAction; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; +import org.rococoa.Rococoa; import java.security.cert.CertificateException; import java.security.cert.X509Certificate; @@ -53,36 +54,44 @@ public class PromptCertificateIdentityCallback implements CertificateIdentityCal public PromptCertificateIdentityCallback(final ProxyController controller) { this.controller = controller; - this.window = null; - } + if(controller instanceof WindowController) { + this.window = ((WindowController) controller).window(); + } + else { + this.window = null; - public PromptCertificateIdentityCallback(final WindowController controller) { - this.controller = controller; - this.window = controller.window(); + } } @Override public X509Certificate prompt(final String hostname, final List certificates) throws ConnectionCanceledException { final AtomicReference ref = new AtomicReference<>(); - controller.invoke(new DefaultMainAction() { - @Override - public void run() { - ref.set(SFChooseIdentityPanel.sharedChooseIdentityPanel()); - } - }, true); - final SFChooseIdentityPanel panel = ref.get(); - panel.setDomain(hostname); - final SecPolicyRef policyRef = SecurityFunctions.library.SecPolicyCreateSSL(true, hostname); - panel.setPolicies(policyRef); - FoundationKitFunctions.library.CFRelease(policyRef); - panel.setShowsHelp(false); - panel.setAlternateButtonTitle(LocaleFactory.localizedString("Disconnect")); - panel.setInformativeText(MessageFormat.format(LocaleFactory.localizedString( - "The server requires a certificate to validate your identity. Select the certificate to authenticate yourself to {0}."), hostname)); final NSArray identities = KeychainCertificateStore.toDEREncodedCertificates(certificates); - final int option = controller.alert(new SheetController.NoBundleSheetController(panel), new AlertRunner() { + final int option = controller.alert(new SheetController.NoBundleSheetController() { + @Outlet + private SFChooseIdentityPanel panel; + + @Override + public void loadBundle() { + panel = SFChooseIdentityPanel.sharedChooseIdentityPanel(); + panel.setDomain(hostname); + final SecPolicyRef policyRef = SecurityFunctions.library.SecPolicyCreateSSL(true, hostname); + panel.setPolicies(policyRef); + FoundationKitFunctions.library.CFRelease(policyRef); + panel.setShowsHelp(false); + panel.setAlternateButtonTitle(LocaleFactory.localizedString("Disconnect")); + panel.setInformativeText(MessageFormat.format(LocaleFactory.localizedString( + "The server requires a certificate to validate your identity. Select the certificate to authenticate yourself to {0}."), hostname)); + } + + @Override + public NSWindow window() { + return panel; + } + }, new AlertRunner() { @Override public void alert(final NSWindow sheet, final SheetCallback callback) { + final SFChooseIdentityPanel panel = Rococoa.cast(sheet, SFChooseIdentityPanel.class); if(null == window) { callback.callback(panel.runModalForIdentities_message(identities, null).intValue()); } @@ -97,6 +106,7 @@ public class PromptCertificateIdentityCallback implements CertificateIdentityCal switch(option) { case SheetCallback.DEFAULT_OPTION: // Use the identity method to obtain the identity chosen by the user. + final SFChooseIdentityPanel panel = ref.get(); final SecIdentityRef identityRef = panel.identity(); if(null == identityRef) { log.warn("No identity selected for {}", hostname); diff --git a/osx/src/main/java/ch/cyberduck/ui/cocoa/callback/PromptCertificateTrustCallback.java b/osx/src/main/java/ch/cyberduck/ui/cocoa/callback/PromptCertificateTrustCallback.java index 753ec4c53d..0398bc7242 100644 --- a/osx/src/main/java/ch/cyberduck/ui/cocoa/callback/PromptCertificateTrustCallback.java +++ b/osx/src/main/java/ch/cyberduck/ui/cocoa/callback/PromptCertificateTrustCallback.java @@ -16,6 +16,7 @@ package ch.cyberduck.ui.cocoa.callback; */ import ch.cyberduck.binding.AlertRunner; +import ch.cyberduck.binding.Outlet; import ch.cyberduck.binding.ProxyController; import ch.cyberduck.binding.SheetController; import ch.cyberduck.binding.WindowController; @@ -30,14 +31,13 @@ import ch.cyberduck.core.keychain.SFCertificateTrustPanel; import ch.cyberduck.core.keychain.SecPolicyRef; import ch.cyberduck.core.keychain.SecTrustRef; import ch.cyberduck.core.keychain.SecurityFunctions; -import ch.cyberduck.core.threading.DefaultMainAction; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; +import org.rococoa.Rococoa; import java.security.cert.X509Certificate; import java.util.List; -import java.util.concurrent.atomic.AtomicReference; import com.sun.jna.ptr.PointerByReference; @@ -52,12 +52,13 @@ public class PromptCertificateTrustCallback implements CertificateTrustCallback public PromptCertificateTrustCallback(final ProxyController controller) { this.controller = controller; - this.window = null; - } + if(controller instanceof WindowController) { + this.window = ((WindowController) controller).window(); + } + else { + this.window = null; - public PromptCertificateTrustCallback(final WindowController controller) { - this.controller = controller; - this.window = controller.window(); + } } @Override @@ -66,22 +67,28 @@ public class PromptCertificateTrustCallback implements CertificateTrustCallback final PointerByReference reference = new PointerByReference(); SecurityFunctions.library.SecTrustCreateWithCertificates(KeychainCertificateStore.toDEREncodedCertificates(certificates), policyRef, reference); final SecTrustRef trustRef = new SecTrustRef(reference.getValue()); - final AtomicReference ref = new AtomicReference<>(); - controller.invoke(new DefaultMainAction() { - @Override - public void run() { - ref.set(SFCertificateTrustPanel.sharedCertificateTrustPanel()); - } - }, true); - final SFCertificateTrustPanel panel = ref.get(); - panel.setInformativeText(null); - panel.setAlternateButtonTitle(LocaleFactory.localizedString("Disconnect")); - panel.setPolicies(policyRef); - panel.setShowsHelp(true); log.debug("Display trust panel for controller {}", controller); - final int option = controller.alert(new SheetController.NoBundleSheetController(panel), new AlertRunner() { + final int option = controller.alert(new SheetController.NoBundleSheetController() { + @Outlet + private SFCertificateTrustPanel panel; + + @Override + public void loadBundle() { + panel = SFCertificateTrustPanel.sharedCertificateTrustPanel(); + panel.setInformativeText(null); + panel.setAlternateButtonTitle(LocaleFactory.localizedString("Disconnect")); + panel.setPolicies(policyRef); + panel.setShowsHelp(true); + } + + @Override + public NSWindow window() { + return panel; + } + }, new AlertRunner() { @Override public void alert(final NSWindow sheet, final SheetCallback callback) { + final SFCertificateTrustPanel panel = Rococoa.cast(sheet, SFCertificateTrustPanel.class); if(null == window) { callback.callback(panel.runModalForTrust_message(trustRef, null).intValue()); } diff --git a/osx/src/main/java/ch/cyberduck/ui/cocoa/callback/PromptLoginCallback.java b/osx/src/main/java/ch/cyberduck/ui/cocoa/callback/PromptLoginCallback.java index a1a4b49b0b..bbc2fd2e6b 100644 --- a/osx/src/main/java/ch/cyberduck/ui/cocoa/callback/PromptLoginCallback.java +++ b/osx/src/main/java/ch/cyberduck/ui/cocoa/callback/PromptLoginCallback.java @@ -24,7 +24,6 @@ import ch.cyberduck.binding.WindowController; import ch.cyberduck.binding.application.NSOpenPanel; import ch.cyberduck.binding.application.NSWindow; import ch.cyberduck.binding.application.SheetCallback; -import ch.cyberduck.binding.foundation.NSObject; import ch.cyberduck.binding.foundation.NSURL; import ch.cyberduck.core.Credentials; import ch.cyberduck.core.Host; @@ -58,19 +57,16 @@ public class PromptLoginCallback extends PromptPasswordCallback implements Login private final ProxyController controller; private final NSWindow window; - @Outlet - private NSOpenPanel select; - public PromptLoginCallback(final ProxyController controller) { super(controller); this.controller = controller; - this.window = null; - } + if(controller instanceof WindowController) { + this.window = ((WindowController) controller).window(); + } + else { + this.window = null; - public PromptLoginCallback(final WindowController controller) { - super(controller); - this.controller = controller; - this.window = controller.window(); + } } @Override @@ -107,8 +103,8 @@ public class PromptLoginCallback extends PromptPasswordCallback implements Login public Credentials prompt(final Host bookmark, final String username, final String title, final String reason, final LoginOptions options) throws LoginCanceledException { log.debug("Prompt for credentials for {}", username); - final Credentials credentials = new Credentials(username).withSaved(options.save); - final LoginController alert = new LoginController(new Host(bookmark).withCredentials(credentials), title, reason, options); + final Credentials credentials = new Credentials(username).setSaved(options.save); + final LoginController alert = new LoginController(new Host(bookmark).setCredentials(credentials), title, reason, options); final int option = controller.alert(alert); if(option == SheetCallback.CANCEL_OPTION) { throw new LoginCanceledException(); @@ -117,10 +113,24 @@ public class PromptLoginCallback extends PromptPasswordCallback implements Login } public Local select(final Local identity) throws LoginCanceledException { - final int option = controller.alert(new SheetController.NoBundleSheetController(select), new AlertRunner() { + final SheetController.NoBundleSheetController sheet = new SheetController.NoBundleSheetController() { + @Outlet + private NSOpenPanel select; + + @Override + public void loadBundle() { + select = NSOpenPanel.openPanel(); + } + + @Override + public NSWindow window() { + return select; + } + }; + final int option = controller.alert(sheet, new AlertRunner() { @Override public void alert(final NSWindow sheet, final SheetCallback callback) { - select = NSOpenPanel.openPanel(); + final NSOpenPanel select = Rococoa.cast(sheet, NSOpenPanel.class); select.setCanChooseDirectories(false); select.setCanChooseFiles(true); select.setAllowsMultipleSelection(false); @@ -138,9 +148,9 @@ public class PromptLoginCallback extends PromptPasswordCallback implements Login } }); if(option == SheetCallback.DEFAULT_OPTION) { - final NSObject url = select.URLs().lastObject(); + final NSURL url = Rococoa.cast(Rococoa.cast(sheet.window(), NSOpenPanel.class).URLs().lastObject(), NSURL.class); if(url != null) { - final Local selected = LocalFactory.get(Rococoa.cast(url, NSURL.class).path()); + final Local selected = LocalFactory.get(url.path()); return selected.setBookmark(FilesystemBookmarkResolverFactory.get().create(selected)); } } diff --git a/osx/src/main/java/ch/cyberduck/ui/cocoa/callback/PromptPasswordCallback.java b/osx/src/main/java/ch/cyberduck/ui/cocoa/callback/PromptPasswordCallback.java index b7e7419114..9f29bae023 100644 --- a/osx/src/main/java/ch/cyberduck/ui/cocoa/callback/PromptPasswordCallback.java +++ b/osx/src/main/java/ch/cyberduck/ui/cocoa/callback/PromptPasswordCallback.java @@ -16,9 +16,7 @@ package ch.cyberduck.ui.cocoa.callback; */ import ch.cyberduck.binding.ProxyController; -import ch.cyberduck.binding.application.NSControl; import ch.cyberduck.binding.application.SheetCallback; -import ch.cyberduck.binding.foundation.NSNotification; import ch.cyberduck.core.Credentials; import ch.cyberduck.core.Host; import ch.cyberduck.core.LoginOptions; @@ -43,7 +41,6 @@ public class PromptPasswordCallback implements PasswordCallback { return; } alert.setPasswordFieldText(input); - alert.passwordFieldTextDidChange(NSNotification.notificationWithName(NSControl.NSControlTextDidChangeNotification, input)); alert.closeSheetWithOption(SheetCallback.ALTERNATE_OPTION); } @@ -52,7 +49,7 @@ public class PromptPasswordCallback implements PasswordCallback { if(suppressed) { throw new LoginCanceledException(); } - final Credentials credentials = new Credentials().withSaved(options.save); + final Credentials credentials = new Credentials().setSaved(options.save); alert = new PasswordController(bookmark, credentials, title, reason, options); final int option = controller.alert(alert); if(option == SheetCallback.CANCEL_OPTION) { diff --git a/osx/src/main/java/ch/cyberduck/ui/cocoa/controller/AbstractPathTableDelegate.java b/osx/src/main/java/ch/cyberduck/ui/cocoa/controller/AbstractPathTableDelegate.java index 4b5f7b67e0..b5399bf4d1 100644 --- a/osx/src/main/java/ch/cyberduck/ui/cocoa/controller/AbstractPathTableDelegate.java +++ b/osx/src/main/java/ch/cyberduck/ui/cocoa/controller/AbstractPathTableDelegate.java @@ -41,7 +41,7 @@ import org.apache.logging.log4j.Logger; import java.util.Comparator; public abstract class AbstractPathTableDelegate extends AbstractTableDelegate { - private static final Logger log = LogManager.getLogger(AbstractTableDelegate.class); + private static final Logger log = LogManager.getLogger(AbstractPathTableDelegate.class); protected AbstractPathTableDelegate(final NSTableColumn selectedColumn) { super(selectedColumn); diff --git a/osx/src/main/java/ch/cyberduck/ui/cocoa/controller/ActivityController.java b/osx/src/main/java/ch/cyberduck/ui/cocoa/controller/ActivityController.java index e1afa5a9af..29a9b4cd35 100644 --- a/osx/src/main/java/ch/cyberduck/ui/cocoa/controller/ActivityController.java +++ b/osx/src/main/java/ch/cyberduck/ui/cocoa/controller/ActivityController.java @@ -48,7 +48,7 @@ public class ActivityController extends WindowController { private final BackgroundActionRegistry registry; - private final Map tasks + private final Map, TaskController> tasks = Collections.synchronizedMap(new LinkedHashMap<>()); public ActivityController() { @@ -68,9 +68,7 @@ public class ActivityController extends WindowController { final BackgroundAction[] actions = registry.toArray( new BackgroundAction[registry.size()]); for(final BackgroundAction action : actions) { - final TaskController c = new TaskController(action); - c.loadBundle(); - tasks.put(action, c); + tasks.put(action, new TaskController(action)); } this.reload(); } @@ -83,18 +81,18 @@ public class ActivityController extends WindowController { super.invalidate(); } - private final AbstractCollectionListener backgroundActionListener - = new AbstractCollectionListener() { + private final AbstractCollectionListener> backgroundActionListener + = new AbstractCollectionListener>() { @Override - public void collectionItemAdded(final BackgroundAction action) { + public void collectionItemAdded(final BackgroundAction action) { log.debug("Add background action {}", action); tasks.put(action, new TaskController(action)); reload(); } @Override - public void collectionItemRemoved(final BackgroundAction action) { + public void collectionItemRemoved(final BackgroundAction action) { log.debug("Remove background action {}", action); final TaskController controller = tasks.remove(action); if(null == controller) { @@ -125,6 +123,7 @@ public class ActivityController extends WindowController { public void setWindow(NSWindow window) { window.setContentMinSize(window.frame().size); window.setTitle(LocaleFactory.localizedString("Activity")); + window.setHidesOnDeactivate(false); super.setWindow(window); } @@ -203,6 +202,7 @@ public class ActivityController extends WindowController { if(null == controller) { return null; } + controller.loadBundle(); return controller.view(); } }).id()); diff --git a/osx/src/main/java/ch/cyberduck/ui/cocoa/controller/BookmarkController.java b/osx/src/main/java/ch/cyberduck/ui/cocoa/controller/BookmarkController.java index e2ef349d7d..38bd8763ae 100644 --- a/osx/src/main/java/ch/cyberduck/ui/cocoa/controller/BookmarkController.java +++ b/osx/src/main/java/ch/cyberduck/ui/cocoa/controller/BookmarkController.java @@ -157,7 +157,7 @@ public class BookmarkController extends SheetController implements CollectionLis this.addProtocol(protocol); } this.protocolPopup.menu().addItem(NSMenuItem.separatorItem()); - for(Protocol protocol : protocols.find(new DefaultProtocolPredicate(EnumSet.of(Protocol.Type.file, Protocol.Type.none)))) { + for(Protocol protocol : protocols.find(new DefaultProtocolPredicate(EnumSet.of(Protocol.Type.file)))) { this.addProtocol(protocol); } this.protocolPopup.menu().addItem(NSMenuItem.separatorItem()); @@ -282,12 +282,12 @@ public class BookmarkController extends SheetController implements CollectionLis @Override public void cleanup() { alertIcon.setEnabled(!reachable); - alertIcon.setImage(reachable ? null : IconCacheFactory.get().iconNamed("alert.tiff")); + alertIcon.setImage(reachable ? null : IconCacheFactory.get().iconNamed("NSCaution", 16)); } }); } else { - alertIcon.setImage(IconCacheFactory.get().iconNamed("alert.tiff")); + alertIcon.setImage(IconCacheFactory.get().iconNamed("NSCaution", 16)); alertIcon.setEnabled(false); } } @@ -362,8 +362,8 @@ public class BookmarkController extends SheetController implements CollectionLis public void setUsernameField(final NSTextField field) { this.usernameField = field; this.notificationCenter.addObserver(this.id(), - Foundation.selector("usernameInputDidChange:"), - NSControl.NSControlTextDidChangeNotification, + Foundation.selector("usernameFieldTextDidEndEditing:"), + NSControl.NSControlTextDidEndEditingNotification, field.id()); this.addObserver(new BookmarkObserver() { @Override @@ -376,7 +376,7 @@ public class BookmarkController extends SheetController implements CollectionLis } @Action - public void usernameInputDidChange(final NSNotification sender) { + public void usernameFieldTextDidEndEditing(final NSNotification sender) { bookmark.getCredentials().setUsername(StringUtils.trim(usernameField.stringValue())); this.update(); } diff --git a/osx/src/main/java/ch/cyberduck/ui/cocoa/controller/BrowserController.java b/osx/src/main/java/ch/cyberduck/ui/cocoa/controller/BrowserController.java index 903c64c1c8..46dbe746d7 100644 --- a/osx/src/main/java/ch/cyberduck/ui/cocoa/controller/BrowserController.java +++ b/osx/src/main/java/ch/cyberduck/ui/cocoa/controller/BrowserController.java @@ -2150,7 +2150,7 @@ public class BrowserController extends WindowController implements NSToolbar.Del } @Override - public void stop(final BackgroundAction action) { + public void stop(final BackgroundAction action) { this.invoke(new DefaultMainAction() { @Override public void run() { @@ -2161,7 +2161,7 @@ public class BrowserController extends WindowController implements NSToolbar.Del } @Override - public void start(final BackgroundAction action) { + public void start(final BackgroundAction action) { this.invoke(new DefaultMainAction() { @Override public void run() { @@ -2403,8 +2403,10 @@ public class BrowserController extends WindowController implements NSToolbar.Del @Action public void createFolderButtonClicked(final ID sender) { final Location feature = pool.getFeature(Location.class); - final AlertController sheet = new FolderController(this.getWorkdirFromSelection(), this.getSelectedPath(), cache, - feature != null ? feature.getLocations() : Collections.emptySet(), feature != null ? feature.getDefault() : Location.unknown, new FolderController.Callback() { + final Path selected = this.getSelectedPath(); + final Path workdir = this.getWorkdirFromSelection(); + final AlertController sheet = new FolderController(workdir, selected, cache, + feature != null ? feature.getLocations(workdir) : Collections.emptySet(), feature != null ? feature.getDefault(workdir) : Location.unknown, new FolderController.Callback() { @Override public void callback(final Path folder, final String region) { @@ -2412,7 +2414,7 @@ public class BrowserController extends WindowController implements NSToolbar.Del new CreateDirectoryWorker(folder, region) { @Override public void cleanup(final Path folder) { - reload(workdir, Collections.singletonList(folder), Collections.singletonList(folder)); + reload(BrowserController.this.workdir, Collections.singletonList(folder), Collections.singletonList(folder)); } })); } @@ -2423,8 +2425,10 @@ public class BrowserController extends WindowController implements NSToolbar.Del @Action public void createEncryptedVaultButtonClicked(final ID sender) { final Location feature = pool.getFeature(Location.class); - final AlertController sheet = new VaultController(this.getWorkdirFromSelection(), this.getSelectedPath(), cache, - feature != null ? feature.getLocations() : Collections.emptySet(), feature != null ? feature.getDefault() : Location.unknown, new VaultController.Callback() { + final Path selected = this.getSelectedPath(); + final Path workdir = this.getWorkdirFromSelection(); + final AlertController sheet = new VaultController(workdir, selected, cache, + feature != null ? feature.getLocations(workdir) : Collections.emptySet(), feature != null ? feature.getDefault(workdir) : Location.unknown, new VaultController.Callback() { @Override public void callback(final Path folder, final String region, final VaultCredentials passphrase) { background(new WorkerBackgroundAction<>(BrowserController.this, pool, @@ -2434,7 +2438,7 @@ public class BrowserController extends WindowController implements NSToolbar.Del HostPreferencesFactory.get(pool.getHost()).getProperty("cryptomator.vault.pepper").getBytes(StandardCharsets.UTF_8))) { @Override public void cleanup(final Path vault) { - reload(workdir, Collections.singletonList(folder), Collections.singletonList(folder)); + reload(BrowserController.this.workdir, Collections.singletonList(folder), Collections.singletonList(folder)); } }) ); @@ -3595,8 +3599,18 @@ public class BrowserController extends WindowController implements NSToolbar.Del } }, bookmarks, - history, - rendezvous; + history { + @Override + public NSImage image() { + return IconCacheFactory.get().iconNamed("history", 16); + } + }, + rendezvous { + @Override + public NSImage image() { + return IconCacheFactory.get().iconNamed("bonjour", 16); + } + }; public static BookmarkSwitchSegement byPosition(final int position) { return BookmarkSwitchSegement.values()[position]; diff --git a/osx/src/main/java/ch/cyberduck/ui/cocoa/controller/DefaultBookmarkController.java b/osx/src/main/java/ch/cyberduck/ui/cocoa/controller/DefaultBookmarkController.java index 4c9901157f..e39066d0e3 100644 --- a/osx/src/main/java/ch/cyberduck/ui/cocoa/controller/DefaultBookmarkController.java +++ b/osx/src/main/java/ch/cyberduck/ui/cocoa/controller/DefaultBookmarkController.java @@ -34,7 +34,7 @@ import ch.cyberduck.core.DisabledCertificateIdentityCallback; import ch.cyberduck.core.Host; import ch.cyberduck.core.LocaleFactory; import ch.cyberduck.core.LoginOptions; -import ch.cyberduck.core.exception.LocalAccessDeniedException; +import ch.cyberduck.core.exception.AccessDeniedException; import ch.cyberduck.core.ssl.KeychainX509KeyManager; import ch.cyberduck.ui.LoginInputValidator; @@ -174,7 +174,7 @@ public class DefaultBookmarkController extends BookmarkController { StringUtils.strip(passwordField.stringValue()) ); } - catch(LocalAccessDeniedException e) { + catch(AccessDeniedException e) { log.error("Failure saving credentials for {} in keychain. {}", bookmark, e); } } diff --git a/osx/src/main/java/ch/cyberduck/ui/cocoa/controller/DownloadController.java b/osx/src/main/java/ch/cyberduck/ui/cocoa/controller/DownloadController.java index 298d53f2b1..dac15006db 100644 --- a/osx/src/main/java/ch/cyberduck/ui/cocoa/controller/DownloadController.java +++ b/osx/src/main/java/ch/cyberduck/ui/cocoa/controller/DownloadController.java @@ -45,7 +45,7 @@ public class DownloadController extends AlertController { private static final Logger log = LogManager.getLogger(DownloadController.class); @Outlet - protected final NSTextField urlField = NSTextField.textFieldWithString(StringUtils.EMPTY); + private NSTextField urlField; private final PathKindDetector detector = new DefaultPathKindDetector(); private final String url; @@ -71,6 +71,7 @@ public class DownloadController extends AlertController { @Override public NSView getAccessoryView(final NSAlert alert) { + urlField = NSTextField.textFieldWithString(StringUtils.EMPTY); urlField.cell().setWraps(false); urlField.setAttributedStringValue(NSAttributedString.attributedStringWithAttributes(StringUtils.EMPTY, TRUNCATE_MIDDLE_ATTRIBUTES)); return urlField; diff --git a/osx/src/main/java/ch/cyberduck/ui/cocoa/controller/ExtendedBookmarkController.java b/osx/src/main/java/ch/cyberduck/ui/cocoa/controller/ExtendedBookmarkController.java index 3e4d0e2333..3f012ddcd1 100644 --- a/osx/src/main/java/ch/cyberduck/ui/cocoa/controller/ExtendedBookmarkController.java +++ b/osx/src/main/java/ch/cyberduck/ui/cocoa/controller/ExtendedBookmarkController.java @@ -175,7 +175,7 @@ public class ExtendedBookmarkController extends DefaultBookmarkController { @Action public void transferPopupClicked(final NSPopUpButton sender) { - bookmark.setTransfer(Host.TransferType.valueOf(sender.selectedItem().representedObject())); + bookmark.setTransferType(Host.TransferType.valueOf(sender.selectedItem().representedObject())); this.update(); } diff --git a/osx/src/main/java/ch/cyberduck/ui/cocoa/controller/FileController.java b/osx/src/main/java/ch/cyberduck/ui/cocoa/controller/FileController.java index a83c32866f..d6ce0f5fc6 100644 --- a/osx/src/main/java/ch/cyberduck/ui/cocoa/controller/FileController.java +++ b/osx/src/main/java/ch/cyberduck/ui/cocoa/controller/FileController.java @@ -37,7 +37,7 @@ public abstract class FileController extends AlertController { private final Cache cache; @Outlet - protected NSTextField inputField = NSTextField.textFieldWithString(StringUtils.EMPTY); + protected NSTextField inputField; public FileController(final Path workdir, final Path selected, final Cache cache) { this.workdir = workdir; @@ -47,6 +47,7 @@ public abstract class FileController extends AlertController { @Override public NSView getAccessoryView(final NSAlert alert) { + inputField = NSTextField.textFieldWithString(StringUtils.EMPTY); inputField.cell().setWraps(false); inputField.cell().setPlaceholderString(alert.informativeText()); return inputField; @@ -55,7 +56,6 @@ public abstract class FileController extends AlertController { @Override protected void focus(final NSAlert alert) { super.focus(alert); - alert.window().makeFirstResponder(inputField); inputField.selectText(null); } diff --git a/osx/src/main/java/ch/cyberduck/ui/cocoa/controller/InfoController.java b/osx/src/main/java/ch/cyberduck/ui/cocoa/controller/InfoController.java index 2735af3bd7..b70d2ac144 100644 --- a/osx/src/main/java/ch/cyberduck/ui/cocoa/controller/InfoController.java +++ b/osx/src/main/java/ch/cyberduck/ui/cocoa/controller/InfoController.java @@ -1043,7 +1043,7 @@ public class InfoController extends ToolbarWindowController { public void aclAddButtonClicked(NSMenuItem sender) { final AclPermission feature = session.getFeature(AclPermission.class); - for(Acl.User grantee : feature.getAvailableAclUsers()) { + for(Acl.User grantee : feature.getAvailableAclUsers(files)) { if(sender.representedObject().equals(grantee.getPlaceholder())) { this.addAclItem(new Acl.UserAndRole(grantee, new Acl.Role(StringUtils.EMPTY))); } @@ -1919,7 +1919,7 @@ public class InfoController extends ToolbarWindowController { if(this.toggleS3Settings(false)) { final Path file = this.getSelected(); if(session.getFeature(Redundancy.class) != null) { - for(String redundancy : session.getFeature(Redundancy.class).getClasses()) { + for(String redundancy : session.getFeature(Redundancy.class).getClasses(file)) { storageClassPopup.addItemWithTitle(LocaleFactory.localizedString(redundancy, "S3")); storageClassPopup.lastItem().setRepresentedObject(redundancy); } @@ -2227,7 +2227,7 @@ public class InfoController extends ToolbarWindowController { aclAddButton.removeAllItems(); this.aclAddButton.addItemWithTitle(StringUtils.EMPTY); this.aclAddButton.lastItem().setImage(IconCacheFactory.get().iconNamed("NSActionTemplate")); - for(Acl.User user : feature.getAvailableAclUsers()) { + for(Acl.User user : feature.getAvailableAclUsers(files)) { this.aclAddButton.addItemWithTitle(user.getPlaceholder()); this.aclAddButton.lastItem().setAction(Foundation.selector("aclAddButtonClicked:")); this.aclAddButton.lastItem().setTarget(this.id()); diff --git a/osx/src/main/java/ch/cyberduck/ui/cocoa/controller/LoginController.java b/osx/src/main/java/ch/cyberduck/ui/cocoa/controller/LoginController.java index 5bb93971b7..dd46b1c915 100644 --- a/osx/src/main/java/ch/cyberduck/ui/cocoa/controller/LoginController.java +++ b/osx/src/main/java/ch/cyberduck/ui/cocoa/controller/LoginController.java @@ -61,15 +61,14 @@ public class LoginController extends AlertController { private final LoginOptions options; @Outlet - private final NSTextField usernameField = NSTextField.textFieldWithString(StringUtils.EMPTY); + private NSTextField usernameField; @Outlet - private final NSTextField passwordField = NSSecureTextField.textFieldWithString(StringUtils.EMPTY); + private NSTextField passwordField; @Outlet - private final NSPopUpButton privateKeyPopup = NSPopUpButton.buttonPullsDown(false); + private NSPopUpButton privateKeyPopup; @Outlet private NSOpenPanel privateKeyOpenPanel; - public LoginController(final Host bookmark, final String title, final String reason, final LoginOptions options) { super(new LoginInputValidator(bookmark, options)); this.bookmark = bookmark; @@ -102,6 +101,7 @@ public class LoginController extends AlertController { public NSView getAccessoryView(final NSAlert alert) { final NSView accessoryView = NSView.create(); if(options.publickey) { + privateKeyPopup = NSPopUpButton.buttonPullsDown(false); privateKeyPopup.setTarget(this.id()); privateKeyPopup.setAction(Foundation.selector("privateKeyPopupClicked:")); privateKeyPopup.removeAllItems(); @@ -130,6 +130,7 @@ public class LoginController extends AlertController { this.addAccessorySubview(accessoryView, privateKeyPopup); } if(options.password) { + passwordField = NSSecureTextField.textFieldWithString(StringUtils.EMPTY); this.updateField(passwordField, bookmark.getCredentials().getPassword()); passwordField.cell().setPlaceholderString(options.getPasswordPlaceholder()); NSNotificationCenter.defaultCenter().addObserver(this.id(), @@ -139,6 +140,7 @@ public class LoginController extends AlertController { this.addAccessorySubview(accessoryView, passwordField); } if(options.user) { + usernameField = NSTextField.textFieldWithString(StringUtils.EMPTY); this.updateField(usernameField, bookmark.getCredentials().getUsername()); usernameField.cell().setPlaceholderString(options.getUsernamePlaceholder()); NSNotificationCenter.defaultCenter().addObserver(this.id(), @@ -154,10 +156,10 @@ public class LoginController extends AlertController { protected void focus(final NSAlert alert) { super.focus(alert); if(options.user) { - window.makeFirstResponder(usernameField); + usernameField.selectText(null); } if(options.password && !StringUtils.isBlank(bookmark.getCredentials().getUsername())) { - window.makeFirstResponder(passwordField); + passwordField.selectText(null); } } @@ -190,7 +192,7 @@ public class LoginController extends AlertController { } @Action - public void usernameInputDidChange(final NSNotification sender) { + public void usernameFieldTextDidChange(final NSNotification notification) { bookmark.getCredentials().setUsername(StringUtils.trim(usernameField.stringValue())); } diff --git a/osx/src/main/java/ch/cyberduck/ui/cocoa/controller/MainController.java b/osx/src/main/java/ch/cyberduck/ui/cocoa/controller/MainController.java index 2ffe1b09f8..edf04ba9b6 100644 --- a/osx/src/main/java/ch/cyberduck/ui/cocoa/controller/MainController.java +++ b/osx/src/main/java/ch/cyberduck/ui/cocoa/controller/MainController.java @@ -21,7 +21,6 @@ import ch.cyberduck.binding.BundleController; import ch.cyberduck.binding.Delegate; import ch.cyberduck.binding.Outlet; import ch.cyberduck.binding.ProxyController; -import ch.cyberduck.binding.SheetController; import ch.cyberduck.binding.SystemAlertController; import ch.cyberduck.binding.application.NSAlert; import ch.cyberduck.binding.application.NSApplication; @@ -167,8 +166,6 @@ public class MainController extends BundleController implements NSApplication.De */ private boolean displayDonationPrompt = true; - @Outlet - private SheetController donationController; @Outlet private NSMenu applicationMenu; @Outlet @@ -216,10 +213,10 @@ public class MainController extends BundleController implements NSApplication.De browsers.remove(controller); } }); + controller.display(); if(StringUtils.isNotBlank(frame)) { controller.window().setFrameUsingName(frame); } - controller.display(); browsers.add(controller); return controller; } @@ -635,7 +632,7 @@ public class MainController extends BundleController implements NSApplication.De } }.id()); - alert.runModal(); + this.alert(alert); } return true; } @@ -891,10 +888,6 @@ public class MainController extends BundleController implements NSApplication.De NSWindow.setAllowsAutomaticWindowTabbing(true); // Load main menu this.loadBundle(); - if(preferences.getBoolean("queue.window.open.default")) { - final TransferController c = TransferControllerFactory.get(); - c.display(); - } final AbstractHostCollection bookmarks = BookmarkCollection.defaultCollection(); final AbstractHostCollection sessions = SessionsCollection.defaultCollection(); this.background(new AbstractBackgroundAction() { @@ -948,6 +941,14 @@ public class MainController extends BundleController implements NSApplication.De transfers.load(); return null; } + + @Override + public void cleanup() { + if(preferences.getBoolean("queue.window.open.default")) { + final TransferController c = TransferControllerFactory.get(); + c.display(); + } + } }); final Rendezvous bonjour = RendezvousFactory.instance(); bonjour.addListener(new NotificationRendezvousListener(bonjour)); @@ -1155,8 +1156,7 @@ public class MainController extends BundleController implements NSApplication.De } // Make sure prompt is not loaded twice upon next quit event displayDonationPrompt = false; - donationController = new DonateAlertController(app); - this.alert(donationController); + this.alert(new DonateAlertController(app)); // Delay application termination. Dismissing the donation dialog will reply to quit. return NSApplication.NSTerminateLater; } diff --git a/osx/src/main/java/ch/cyberduck/ui/cocoa/controller/PasswordController.java b/osx/src/main/java/ch/cyberduck/ui/cocoa/controller/PasswordController.java index d8dfd47fd0..d24998ef2e 100644 --- a/osx/src/main/java/ch/cyberduck/ui/cocoa/controller/PasswordController.java +++ b/osx/src/main/java/ch/cyberduck/ui/cocoa/controller/PasswordController.java @@ -41,11 +41,12 @@ import ch.cyberduck.core.resources.IconCacheFactory; import org.apache.commons.lang3.StringUtils; import org.rococoa.Foundation; +import org.rococoa.Rococoa; public class PasswordController extends AlertController { @Outlet - private final NSTextField inputField; + private NSTextField inputField; private final Host bookmark; private final Credentials credentials; @@ -59,12 +60,6 @@ public class PasswordController extends AlertController { this.title = title; this.reason = reason; this.options = options; - if(options.password) { - this.inputField = NSSecureTextField.textFieldWithString(StringUtils.EMPTY); - } - else { - this.inputField = NSTextField.textFieldWithString(StringUtils.EMPTY); - } } @Override @@ -106,6 +101,12 @@ public class PasswordController extends AlertController { @Override public NSView getAccessoryView(final NSAlert alert) { + if(options.password) { + inputField = NSSecureTextField.textFieldWithString(StringUtils.EMPTY); + } + else { + inputField = NSTextField.textFieldWithString(StringUtils.EMPTY); + } final NSView accessoryView = NSView.create(); inputField.cell().setPlaceholderString(options.getPasswordPlaceholder()); NSNotificationCenter.defaultCenter().addObserver(this.id(), @@ -117,7 +118,7 @@ public class PasswordController extends AlertController { } public void setPasswordFieldText(final String input) { - inputField.setStringValue(input); + credentials.setPassword(input); } @Override diff --git a/osx/src/main/java/ch/cyberduck/ui/cocoa/controller/PreferencesController.java b/osx/src/main/java/ch/cyberduck/ui/cocoa/controller/PreferencesController.java index fecc0ce860..8209833f83 100644 --- a/osx/src/main/java/ch/cyberduck/ui/cocoa/controller/PreferencesController.java +++ b/osx/src/main/java/ch/cyberduck/ui/cocoa/controller/PreferencesController.java @@ -51,6 +51,8 @@ import ch.cyberduck.core.s3.S3EncryptionFeature; import ch.cyberduck.core.threading.DefaultMainAction; import ch.cyberduck.core.threading.WindowMainAction; import ch.cyberduck.core.transfer.TransferAction; +import ch.cyberduck.core.updater.PeriodicUpdateChecker; +import ch.cyberduck.core.updater.PeriodicUpdateCheckerFactory; import ch.cyberduck.core.urlhandler.SchemeHandlerFactory; import ch.cyberduck.ui.cocoa.view.BookmarkCell; @@ -89,6 +91,9 @@ public class PreferencesController extends ToolbarWindowController { private final Preferences preferences = PreferencesFactory.get(); + private final PeriodicUpdateChecker updater + = PeriodicUpdateCheckerFactory.get(this); + private final ConnectionTimeout connectionTimeoutPreferences = ConnectionTimeoutFactory.get(); @@ -210,7 +215,7 @@ public class PreferencesController extends ToolbarWindowController { if(preferences.getBoolean("cryptomator.enable")) { this.addPanel(views, new PreferencesLabel(PreferencesToolbarItem.cryptomator), panelCryptomator); } - if(null != preferences.getProperty("SUExpectsDSASignature")) { + if(updater.hasUpdatePrivileges()) { this.addPanel(views, new PreferencesLabel(PreferencesToolbarItem.update), panelUpdate); } this.addPanel(views, new PreferencesLabel(PreferencesToolbarItem.language), panelLanguage); diff --git a/osx/src/main/java/ch/cyberduck/ui/cocoa/controller/ProgressController.java b/osx/src/main/java/ch/cyberduck/ui/cocoa/controller/ProgressController.java index 861204f47b..e99c478247 100644 --- a/osx/src/main/java/ch/cyberduck/ui/cocoa/controller/ProgressController.java +++ b/osx/src/main/java/ch/cyberduck/ui/cocoa/controller/ProgressController.java @@ -116,6 +116,7 @@ public class ProgressController extends BundleController implements TransferList public ProgressController(final Transfer transfer) { this.transfer = transfer; + this.loadBundle(); } // ---------------------------------------------------------- diff --git a/osx/src/main/java/ch/cyberduck/ui/cocoa/controller/TaskController.java b/osx/src/main/java/ch/cyberduck/ui/cocoa/controller/TaskController.java index fb4317227b..93cb86a237 100644 --- a/osx/src/main/java/ch/cyberduck/ui/cocoa/controller/TaskController.java +++ b/osx/src/main/java/ch/cyberduck/ui/cocoa/controller/TaskController.java @@ -65,7 +65,7 @@ public class TaskController extends BundleController { this.progress.startAnimation(null); } this.task.addListener(new BackgroundActionListener() { - public void start(BackgroundAction action) { + public void start(BackgroundAction action) { invoke(new DefaultMainAction() { @Override public void run() { @@ -74,7 +74,7 @@ public class TaskController extends BundleController { }); } - public void cancel(BackgroundAction action) { + public void cancel(BackgroundAction action) { invoke(new DefaultMainAction() { @Override public void run() { @@ -83,7 +83,7 @@ public class TaskController extends BundleController { }); } - public void stop(BackgroundAction action) { + public void stop(BackgroundAction action) { invoke(new DefaultMainAction() { @Override public void run() { diff --git a/osx/src/main/java/ch/cyberduck/ui/cocoa/controller/TranscriptController.java b/osx/src/main/java/ch/cyberduck/ui/cocoa/controller/TranscriptController.java index 865eb97dda..41b37a1d3d 100644 --- a/osx/src/main/java/ch/cyberduck/ui/cocoa/controller/TranscriptController.java +++ b/osx/src/main/java/ch/cyberduck/ui/cocoa/controller/TranscriptController.java @@ -84,9 +84,11 @@ public abstract class TranscriptController extends BundleController implements T public void run() { switch(request) { case request: + case requestheader: write(FIXED_WITH_FONT_REQUEST_ATTRIBUTES, transcript); break; case response: + case responseheader: write(FIXED_WITH_FONT_RESPONSE_ATTRIBUTES, transcript); break; } diff --git a/osx/src/main/java/ch/cyberduck/ui/cocoa/controller/TransferControllerFactory.java b/osx/src/main/java/ch/cyberduck/ui/cocoa/controller/TransferControllerFactory.java index 9847e01ee1..28e84a2180 100644 --- a/osx/src/main/java/ch/cyberduck/ui/cocoa/controller/TransferControllerFactory.java +++ b/osx/src/main/java/ch/cyberduck/ui/cocoa/controller/TransferControllerFactory.java @@ -44,6 +44,7 @@ public final class TransferControllerFactory { super.invalidate(); } }; + shared.loadBundle(); } return shared; } diff --git a/osx/src/main/java/ch/cyberduck/ui/cocoa/controller/VaultController.java b/osx/src/main/java/ch/cyberduck/ui/cocoa/controller/VaultController.java index a660e22aff..71522eb374 100644 --- a/osx/src/main/java/ch/cyberduck/ui/cocoa/controller/VaultController.java +++ b/osx/src/main/java/ch/cyberduck/ui/cocoa/controller/VaultController.java @@ -46,11 +46,11 @@ public class VaultController extends FolderController { private final Callback callback; @Outlet - private final NSSecureTextField passwordField = NSSecureTextField.textFieldWithString(StringUtils.EMPTY); + private NSSecureTextField passwordField; @Outlet - private final NSSecureTextField confirmField = NSSecureTextField.textFieldWithString(StringUtils.EMPTY); + private NSSecureTextField confirmField; @Outlet - private final NSLevelIndicator strengthIndicator = NSLevelIndicator.levelIndicatorWithFrame(new NSRect(0, 18)); + private NSLevelIndicator strengthIndicator; private final NSNotificationCenter notificationCenter = NSNotificationCenter.defaultCenter(); @@ -87,15 +87,17 @@ public class VaultController extends FolderController { public NSView getAccessoryView(final NSAlert alert) { final NSView accessoryView = NSView.create(); + confirmField = NSSecureTextField.textFieldWithString(StringUtils.EMPTY); confirmField.cell().setPlaceholderString(LocaleFactory.localizedString("Confirm Passphrase", "Cryptomator")); this.addAccessorySubview(accessoryView, confirmField); + strengthIndicator = NSLevelIndicator.levelIndicatorWithFrame(new NSRect(0, 18)); strengthIndicator.setTickMarkPosition(1); if(strengthIndicator.respondsToSelector(Foundation.selector("setLevelIndicatorStyle:"))) { strengthIndicator.setLevelIndicatorStyle(NSLevelIndicator.NSDiscreteCapacityLevelIndicatorStyle); } this.addAccessorySubview(accessoryView, strengthIndicator); - + passwordField = NSSecureTextField.textFieldWithString(StringUtils.EMPTY); passwordField.cell().setPlaceholderString(LocaleFactory.localizedString("Passphrase", "Cryptomator")); notificationCenter.addObserver(this.id(), Foundation.selector("passwordFieldTextDidChange:"), @@ -127,7 +129,7 @@ public class VaultController extends FolderController { @Override public void callback(final int returncode, final Path file) { file.setType(EnumSet.of(Path.Type.directory)); - final VaultCredentials credentials = new VaultCredentials(passwordField.stringValue()).withSaved(this.isSuppressed()); + final VaultCredentials credentials = new VaultCredentials(passwordField.stringValue()).setSaved(this.isSuppressed()); callback.callback(file, this.getLocation(), credentials); } diff --git a/osx/src/main/java/ch/cyberduck/ui/cocoa/datasource/BrowserTableDataSource.java b/osx/src/main/java/ch/cyberduck/ui/cocoa/datasource/BrowserTableDataSource.java index 5c6b29fc90..6ed721ad3f 100644 --- a/osx/src/main/java/ch/cyberduck/ui/cocoa/datasource/BrowserTableDataSource.java +++ b/osx/src/main/java/ch/cyberduck/ui/cocoa/datasource/BrowserTableDataSource.java @@ -29,20 +29,7 @@ import ch.cyberduck.binding.foundation.NSFileManager; import ch.cyberduck.binding.foundation.NSMutableArray; import ch.cyberduck.binding.foundation.NSObject; import ch.cyberduck.binding.foundation.NSURL; -import ch.cyberduck.core.AbstractHostCollection; -import ch.cyberduck.core.Acl; -import ch.cyberduck.core.AttributedList; -import ch.cyberduck.core.Cache; -import ch.cyberduck.core.Host; -import ch.cyberduck.core.HostParser; -import ch.cyberduck.core.Local; -import ch.cyberduck.core.LocalFactory; -import ch.cyberduck.core.LocaleFactory; -import ch.cyberduck.core.Path; -import ch.cyberduck.core.PathAttributes; -import ch.cyberduck.core.Permission; -import ch.cyberduck.core.Scheme; -import ch.cyberduck.core.UserDateFormatterFactory; +import ch.cyberduck.core.*; import ch.cyberduck.core.cache.LRUCache; import ch.cyberduck.core.date.AbstractUserDateFormatter; import ch.cyberduck.core.exception.AccessDeniedException; @@ -198,7 +185,7 @@ public abstract class BrowserTableDataSource extends ProxyController implements log.debug("Set new value {} for item {}", value, item); if(identifier.equals(BrowserColumn.filename.name())) { if(StringUtils.isNotBlank(value.toString()) && !item.getName().equals(value.toString())) { - final Path renamed = new Path(item.getParent(), value.toString(), item.getType(), new PathAttributes(item.attributes()).setVersionId(null)); + final Path renamed = new Path(item.getParent(), value.toString(), item.getType(), new DefaultPathAttributes(item.attributes()).setVersionId(null)); new MoveController(controller).rename(item, renamed); } } diff --git a/osx/src/main/java/ch/cyberduck/ui/cocoa/datasource/TransferPromptDataSource.java b/osx/src/main/java/ch/cyberduck/ui/cocoa/datasource/TransferPromptDataSource.java index 7032a9dd0f..bcd61312c6 100644 --- a/osx/src/main/java/ch/cyberduck/ui/cocoa/datasource/TransferPromptDataSource.java +++ b/osx/src/main/java/ch/cyberduck/ui/cocoa/datasource/TransferPromptDataSource.java @@ -187,7 +187,7 @@ public abstract class TransferPromptDataSource extends OutlineDataSource { if(identifier.equals(Column.warning.name())) { if(file.remote.isFile()) { if(status.getLength() == 0) { - return IconCacheFactory.get().iconNamed("alert.tiff"); + return IconCacheFactory.get().iconNamed("NSCaution", 16); } } return null; diff --git a/osx/src/main/java/ch/cyberduck/ui/cocoa/delegate/BookmarkMenuDelegate.java b/osx/src/main/java/ch/cyberduck/ui/cocoa/delegate/BookmarkMenuDelegate.java index b8a4946491..07248b3949 100644 --- a/osx/src/main/java/ch/cyberduck/ui/cocoa/delegate/BookmarkMenuDelegate.java +++ b/osx/src/main/java/ch/cyberduck/ui/cocoa/delegate/BookmarkMenuDelegate.java @@ -122,7 +122,7 @@ public class BookmarkMenuDelegate extends CollectionMenuDelegate { { final NSMenuItem item = NSMenuItem.itemWithTitle(LocaleFactory.get().localize("History", "Localizable"), null, StringUtils.EMPTY); item.setEnabled(true); - item.setImage(IconCacheFactory.get().iconNamed("history.tiff", 16)); + item.setImage(IconCacheFactory.get().iconNamed("history", 16)); item.setTarget(this.id()); item.setAction(Foundation.selector("historyMenuClicked:")); historyMenu.setSupermenu(null); @@ -132,7 +132,7 @@ public class BookmarkMenuDelegate extends CollectionMenuDelegate { { final NSMenuItem item = NSMenuItem.itemWithTitle(LocaleFactory.get().localize("Bonjour", "Main"), null, StringUtils.EMPTY); item.setEnabled(true); - item.setImage(IconCacheFactory.get().iconNamed("rendezvous.tiff", 16)); + item.setImage(IconCacheFactory.get().iconNamed("bonjour", 16)); rendezvousMenu.setSupermenu(null); item.setSubmenu(rendezvousMenu); menu.addItem(item); diff --git a/osx/src/main/java/ch/cyberduck/ui/cocoa/toolbar/TransferToolbarFactory.java b/osx/src/main/java/ch/cyberduck/ui/cocoa/toolbar/TransferToolbarFactory.java index c7d0c50563..a95370454d 100644 --- a/osx/src/main/java/ch/cyberduck/ui/cocoa/toolbar/TransferToolbarFactory.java +++ b/osx/src/main/java/ch/cyberduck/ui/cocoa/toolbar/TransferToolbarFactory.java @@ -270,7 +270,6 @@ public class TransferToolbarFactory extends AbstractToolbarFactory implements To button.setTarget(controller.id()); button.setAction(bandwidth.action()); item.setView(button); - item.setMaxSize(new NSSize(button.frame().size.width.doubleValue(), button.frame().size.height.doubleValue())); return item; } case connections: { @@ -308,7 +307,6 @@ public class TransferToolbarFactory extends AbstractToolbarFactory implements To button.setAction(connections.action()); button.selectItemAtIndex(button.indexOfItemWithRepresentedObject(String.valueOf(preferences.getInteger("queue.connections.limit")))); item.setView(button); - item.setMaxSize(new NSSize(button.frame().size.width.doubleValue(), button.frame().size.height.doubleValue())); return item; } default: { diff --git a/owncloud/pom.xml b/owncloud/pom.xml index ad12eb08b4..b290a0329a 100644 --- a/owncloud/pom.xml +++ b/owncloud/pom.xml @@ -18,7 +18,7 @@ ch.cyberduck parent - 9.2.0-SNAPSHOT + 9.4.0-SNAPSHOT owncloud jar @@ -53,10 +53,6 @@ com.fasterxml.jackson.dataformat jackson-dataformat-xml - - com.auth0 - java-jwt - ch.cyberduck test diff --git a/owncloud/src/main/java/ch/cyberduck/core/owncloud/OcisUploadFeature.java b/owncloud/src/main/java/ch/cyberduck/core/owncloud/OcisUploadFeature.java index 66b4013956..5ef45e895f 100644 --- a/owncloud/src/main/java/ch/cyberduck/core/owncloud/OcisUploadFeature.java +++ b/owncloud/src/main/java/ch/cyberduck/core/owncloud/OcisUploadFeature.java @@ -15,20 +15,12 @@ package ch.cyberduck.core.owncloud; * GNU General Public License for more details. */ -import ch.cyberduck.core.Host; -import ch.cyberduck.core.features.Write; import ch.cyberduck.core.tus.TusCapabilities; import ch.cyberduck.core.tus.TusUploadFeature; -import org.apache.http.client.HttpClient; - public class OcisUploadFeature extends TusUploadFeature { - public OcisUploadFeature(final OwncloudSession session, final Write writer, final TusCapabilities capabilities) { - this(session.getHost(), session.getClient().getClient(), writer, capabilities); - } - - public OcisUploadFeature(final Host host, final HttpClient client, final Write writer, final TusCapabilities capabilities) { - super(host, client, writer, capabilities); + public OcisUploadFeature(final OwncloudSession session, final TusCapabilities capabilities) { + super(session.getHost(), session.getClient(), capabilities); } } diff --git a/owncloud/src/main/java/ch/cyberduck/core/owncloud/OwncloudProtocol.java b/owncloud/src/main/java/ch/cyberduck/core/owncloud/OwncloudProtocol.java index 55d147c403..3f8fd1b422 100644 --- a/owncloud/src/main/java/ch/cyberduck/core/owncloud/OwncloudProtocol.java +++ b/owncloud/src/main/java/ch/cyberduck/core/owncloud/OwncloudProtocol.java @@ -79,7 +79,7 @@ public class OwncloudProtocol extends AbstractProtocol { @SuppressWarnings("unchecked") public T getFeature(final Class type) { if(type == ComparisonService.class) { - return (T) new DefaultComparisonService(DefaultComparisonService.forFiles(this), new ETagComparisonService()); + return (T) new DefaultComparisonService(new ETagComparisonService(), new ETagComparisonService()); } if(type == CredentialsConfigurator.class) { return (T) new WindowsIntegratedCredentialsConfigurator(); diff --git a/owncloud/src/main/java/ch/cyberduck/core/owncloud/OwncloudSession.java b/owncloud/src/main/java/ch/cyberduck/core/owncloud/OwncloudSession.java index 5964412b88..37a52a13d3 100644 --- a/owncloud/src/main/java/ch/cyberduck/core/owncloud/OwncloudSession.java +++ b/owncloud/src/main/java/ch/cyberduck/core/owncloud/OwncloudSession.java @@ -15,23 +15,18 @@ package ch.cyberduck.core.owncloud; * GNU General Public License for more details. */ -import ch.cyberduck.core.Credentials; import ch.cyberduck.core.DefaultIOExceptionMappingService; import ch.cyberduck.core.Host; import ch.cyberduck.core.HostKeyCallback; import ch.cyberduck.core.ListService; import ch.cyberduck.core.LoginCallback; -import ch.cyberduck.core.OAuthTokens; -import ch.cyberduck.core.Path; import ch.cyberduck.core.UrlProvider; import ch.cyberduck.core.dav.DAVClient; -import ch.cyberduck.core.dav.DAVDirectoryFeature; import ch.cyberduck.core.dav.DAVSession; import ch.cyberduck.core.dav.DAVTouchFeature; import ch.cyberduck.core.exception.BackgroundException; import ch.cyberduck.core.features.AttributesFinder; import ch.cyberduck.core.features.Delete; -import ch.cyberduck.core.features.Directory; import ch.cyberduck.core.features.Home; import ch.cyberduck.core.features.Lock; import ch.cyberduck.core.features.Read; @@ -63,16 +58,11 @@ import ch.cyberduck.core.tus.TusCapabilitiesResponseHandler; import ch.cyberduck.core.tus.TusWriteFeature; import org.apache.commons.lang3.ArrayUtils; -import org.apache.commons.lang3.StringUtils; import org.apache.http.client.HttpResponseException; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; import java.io.IOException; -import java.util.EnumSet; - -import com.auth0.jwt.JWT; -import com.auth0.jwt.exceptions.JWTDecodeException; import static ch.cyberduck.core.tus.TusCapabilities.TUS_VERSION; @@ -106,19 +96,6 @@ public class OwncloudSession extends DAVSession { @Override public void login(final LoginCallback prompt, final CancelCallback cancel) throws BackgroundException { super.login(prompt, cancel); - if(host.getProtocol().isOAuthConfigurable()) { - final Credentials credentials = host.getCredentials(); - final OAuthTokens oauth = credentials.getOauth(); - try { - final String username = JWT.decode(oauth.getIdToken()).getClaim("preferred_username").asString(); - if(StringUtils.isNotBlank(username)) { - credentials.setUsername(username); - } - } - catch(JWTDecodeException e) { - log.warn("Failure {} decoding JWT {}", e, oauth.getIdToken()); - } - } try { client.execute(new OcsCapabilitiesRequest(host), new OcsCapabilitiesResponseHandler(ocs)); } @@ -139,11 +116,8 @@ public class OwncloudSession extends DAVSession { if(type == ListService.class) { return (T) new NextcloudListService(this); } - if(type == Directory.class) { - return (T) new DAVDirectoryFeature(this, new OwncloudAttributesFinderFeature(this)); - } if(type == Touch.class) { - return (T) new DAVTouchFeature(new NextcloudWriteFeature(this)); + return (T) new DAVTouchFeature(this); } if(type == AttributesFinder.class) { return (T) new OwncloudAttributesFinderFeature(this); @@ -155,22 +129,15 @@ public class OwncloudSession extends DAVSession { } if(type == Upload.class) { if(ArrayUtils.contains(tus.versions, TUS_VERSION) && tus.extensions.contains(TusCapabilities.Extension.creation)) { - return (T) new OcisUploadFeature(host, client.getClient(), new TusWriteFeature(tus, client.getClient()), tus); - } - else { - return (T) new HttpUploadFeature(new NextcloudWriteFeature(this)); + return (T) new OcisUploadFeature(this, tus); } + return (T) new HttpUploadFeature(); } if(type == Write.class) { - return (T) new NextcloudWriteFeature(this) { - @Override - public EnumSet features(final Path file) { - if(ArrayUtils.contains(tus.versions, TUS_VERSION) && tus.extensions.contains(TusCapabilities.Extension.creation)) { - return new TusWriteFeature(tus, client.getClient()).features(file); - } - return super.features(file); - } - }; + if(ArrayUtils.contains(tus.versions, TUS_VERSION) && tus.extensions.contains(TusCapabilities.Extension.creation)) { + return (T) new TusWriteFeature(tus, client); + } + return (T) new NextcloudWriteFeature(this); } if(type == UrlProvider.class) { return (T) new NextcloudUrlProvider(this); diff --git a/owncloud/src/main/java/ch/cyberduck/core/owncloud/OwncloudTimestampFeature.java b/owncloud/src/main/java/ch/cyberduck/core/owncloud/OwncloudTimestampFeature.java index 3dfc2020be..ed89800c02 100644 --- a/owncloud/src/main/java/ch/cyberduck/core/owncloud/OwncloudTimestampFeature.java +++ b/owncloud/src/main/java/ch/cyberduck/core/owncloud/OwncloudTimestampFeature.java @@ -15,31 +15,11 @@ package ch.cyberduck.core.owncloud; * GNU General Public License for more details. */ -import ch.cyberduck.core.Path; import ch.cyberduck.core.dav.DAVTimestampFeature; -import ch.cyberduck.core.exception.BackgroundException; -import ch.cyberduck.core.exception.NotfoundException; - -import java.io.IOException; -import java.util.Optional; - -import com.github.sardine.DavResource; public class OwncloudTimestampFeature extends DAVTimestampFeature { - private final OwncloudSession session; - public OwncloudTimestampFeature(final OwncloudSession session) { - super(session); - this.session = session; - } - - @Override - protected DavResource getResource(final Path file) throws BackgroundException, IOException { - final Optional optional = new OwncloudAttributesFinderFeature(session).list(file).stream().findFirst(); - if(!optional.isPresent()) { - throw new NotfoundException(file.getAbsolute()); - } - return optional.get(); + super(session, new OwncloudAttributesFinderFeature(session)); } } diff --git a/owncloud/src/test/java/ch/cyberduck/core/cryptomator/OcisUploadFeatureTest.java b/owncloud/src/test/java/ch/cyberduck/core/cryptomator/OcisUploadFeatureTest.java index f4b3aa0896..8d697d3e17 100644 --- a/owncloud/src/test/java/ch/cyberduck/core/cryptomator/OcisUploadFeatureTest.java +++ b/owncloud/src/test/java/ch/cyberduck/core/cryptomator/OcisUploadFeatureTest.java @@ -36,6 +36,7 @@ import ch.cyberduck.core.features.Find; import ch.cyberduck.core.io.BandwidthThrottle; import ch.cyberduck.core.io.HashAlgorithm; import ch.cyberduck.core.io.StreamCopier; +import ch.cyberduck.core.nextcloud.NextcloudWriteFeature; import ch.cyberduck.core.owncloud.AbstractOcisTest; import ch.cyberduck.core.owncloud.OcisUploadFeature; import ch.cyberduck.core.owncloud.OwncloudAttributesFinderFeature; @@ -72,7 +73,7 @@ public class OcisUploadFeatureTest extends AbstractOcisTest { @Test public void testUploadVault() throws Exception { // 5L * 1024L * 1024L - final Path directory = new DAVDirectoryFeature(session).mkdir(new Path(new OwncloudHomeFeature(session.getHost()).find(), + final Path directory = new DAVDirectoryFeature(session).mkdir(new NextcloudWriteFeature(session), new Path(new OwncloudHomeFeature(session.getHost()).find(), new AlphanumericRandomStringService().random(), EnumSet.of(AbstractPath.Type.directory)), new TransferStatus()); final CryptoVault cryptomator = new CryptoVault( new Path(directory, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory))); @@ -81,8 +82,8 @@ public class OcisUploadFeatureTest extends AbstractOcisTest { final TusCapabilities capabilities = new TusCapabilities().withHashAlgorithm(HashAlgorithm.sha1); final CryptoUploadFeature service = new CryptoUploadFeature<>(session, new OcisUploadFeature(session, - new TusWriteFeature(capabilities, session.getClient().getClient()), capabilities), - new TusWriteFeature(capabilities, session.getClient().getClient()), cryptomator); + capabilities), + 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)); @@ -92,7 +93,7 @@ public class OcisUploadFeatureTest extends AbstractOcisTest { 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 TusWriteFeature(capabilities, session.getClient()), 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 DAVFindFeature(session)).find(test)); diff --git a/owncloud/src/test/java/ch/cyberduck/core/owncloud/OcisUploadFeatureTest.java b/owncloud/src/test/java/ch/cyberduck/core/owncloud/OcisUploadFeatureTest.java index 315b9c2d39..9c4b222e95 100644 --- a/owncloud/src/test/java/ch/cyberduck/core/owncloud/OcisUploadFeatureTest.java +++ b/owncloud/src/test/java/ch/cyberduck/core/owncloud/OcisUploadFeatureTest.java @@ -33,6 +33,7 @@ import ch.cyberduck.core.features.Delete; import ch.cyberduck.core.io.BandwidthThrottle; import ch.cyberduck.core.io.HashAlgorithm; import ch.cyberduck.core.io.SHA1ChecksumCompute; +import ch.cyberduck.core.nextcloud.NextcloudWriteFeature; import ch.cyberduck.core.transfer.TransferStatus; import ch.cyberduck.core.tus.TusCapabilities; import ch.cyberduck.core.tus.TusWriteFeature; @@ -58,9 +59,8 @@ public class OcisUploadFeatureTest extends AbstractOcisTest { final TusCapabilities capabilities = new TusCapabilities().withHashAlgorithm(HashAlgorithm.sha1) .withExtension(TusCapabilities.Extension.checksum) .withExtension(TusCapabilities.Extension.creation); - final OcisUploadFeature feature = new OcisUploadFeature(session, - new TusWriteFeature(capabilities, session.getClient().getClient()), capabilities); - final Path directory = new DAVDirectoryFeature(session).mkdir(new Path(new OwncloudHomeFeature(session.getHost()).find(), + final OcisUploadFeature feature = new OcisUploadFeature(session, capabilities); + final Path directory = new DAVDirectoryFeature(session).mkdir(new NextcloudWriteFeature(session), new Path(new OwncloudHomeFeature(session.getHost()).find(), new AlphanumericRandomStringService().random(), EnumSet.of(AbstractPath.Type.directory)), new TransferStatus()); final String name = new AlphanumericRandomStringService().random(); final Path file = new Path(directory, name, EnumSet.of(Path.Type.file)); @@ -74,7 +74,7 @@ public class OcisUploadFeatureTest extends AbstractOcisTest { status.setLength(content.length); final BytecountStreamListener count = new BytecountStreamListener(); assertFalse(feature.append(file, status).append); - final Void response = feature.upload(file, local, new BandwidthThrottle(BandwidthThrottle.UNLIMITED), new DisabledProgressListener(), count, status, new DisabledConnectionCallback()); + final Void response = feature.upload(new TusWriteFeature(capabilities, session.getClient()), file, local, new BandwidthThrottle(BandwidthThrottle.UNLIMITED), new DisabledProgressListener(), count, status, new DisabledConnectionCallback()); assertTrue(status.isComplete()); assertEquals(content.length, count.getSent()); assertTrue(status.isComplete()); @@ -96,7 +96,7 @@ public class OcisUploadFeatureTest extends AbstractOcisTest { status.setLength(content.length); final BytecountStreamListener count = new BytecountStreamListener(); assertFalse(feature.append(file, status).append); - final Void response = feature.upload(file, local, new BandwidthThrottle(BandwidthThrottle.UNLIMITED), new DisabledProgressListener(), count, status, new DisabledConnectionCallback()); + final Void response = feature.upload(new TusWriteFeature(capabilities, session.getClient()), file, local, new BandwidthThrottle(BandwidthThrottle.UNLIMITED), new DisabledProgressListener(), count, status, new DisabledConnectionCallback()); assertTrue(status.isComplete()); assertEquals(content.length, count.getSent()); assertTrue(status.isComplete()); diff --git a/owncloud/src/test/java/ch/cyberduck/core/owncloud/OwncloudVersioningFeatureTest.java b/owncloud/src/test/java/ch/cyberduck/core/owncloud/OwncloudVersioningFeatureTest.java index ae422be5a0..e0edfd1bb1 100644 --- a/owncloud/src/test/java/ch/cyberduck/core/owncloud/OwncloudVersioningFeatureTest.java +++ b/owncloud/src/test/java/ch/cyberduck/core/owncloud/OwncloudVersioningFeatureTest.java @@ -49,12 +49,12 @@ public class OwncloudVersioningFeatureTest extends AbstractOwncloudTest { @Test public void testRevert() throws Exception { - final Path directory = new DAVDirectoryFeature(session).mkdir(new Path( + final NextcloudWriteFeature writer = new NextcloudWriteFeature(session); + final Path directory = new DAVDirectoryFeature(session).mkdir(writer, new Path( new OwncloudHomeFeature(session.getHost()).find(), new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory, Path.Type.volume)), new TransferStatus()); final Path test = new Path(directory, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)); final TransferStatus status = new TransferStatus(); - final NextcloudWriteFeature writer = new NextcloudWriteFeature(session); final byte[] initialContent = RandomUtils.nextBytes(32769); { new StreamCopier(status, status).transfer(new ByteArrayInputStream(initialContent), writer.write(test, status.setLength(initialContent.length), new DisabledConnectionCallback())); diff --git a/pom.xml b/pom.xml index 69c8c3327c..c7ca73878b 100644 --- a/pom.xml +++ b/pom.xml @@ -6,7 +6,7 @@ parent Cyberduck pom - 9.2.0-SNAPSHOT + 9.4.0-SNAPSHOT defaults @@ -83,10 +83,10 @@ 5.14.0 2.38 - 2.14.2 + 2.19.2 1.7.36 - 2.25.0 - 1.81 + 2.25.3 + 1.83 4.5.14 1.46.3 4.5.0 @@ -114,6 +114,11 @@ jna-platform ${jna-version} + + net.bytebuddy + byte-buddy + 1.18.3 + cglib cglib @@ -122,7 +127,7 @@ ch.iterate.s3 jets3t - 0.9.39 + 0.9.40 com.auth0 @@ -162,22 +167,22 @@ commons-codec commons-codec - 1.18.0 + 1.20.0 commons-net commons-net - 3.11.1 + 3.12.0 org.apache.commons commons-pool2 - 2.12.1 + 2.13.1 commons-io commons-io - 2.19.0 + 2.21.0 commons-collections @@ -202,12 +207,17 @@ org.apache.commons commons-text - 1.13.1 + 1.15.0 + + + org.apache.commons + commons-compress + 1.28.0 com.google.code.gson gson - 2.13.1 + 2.13.2 org.apache.logging.log4j @@ -249,6 +259,11 @@ jackson-dataformat-xml ${jackson-version} + + com.fasterxml.jackson.dataformat + jackson-dataformat-cbor + ${jackson-version} + com.fasterxml.jackson.core jackson-core @@ -280,7 +295,7 @@ ${jackson-version} - com.fasterxml.jackson.core + com.fasterxml.jackson.module jackson-module-jaxb-annotations ${jackson-version} @@ -307,7 +322,7 @@ io.swagger.core.v3 swagger-annotations - 2.2.34 + 2.2.41 org.glassfish.jersey.core @@ -342,7 +357,7 @@ com.google.api-client google-api-client - 2.8.0 + 2.8.1 org.apache.httpcomponents.client5 @@ -434,7 +449,7 @@ maven-compiler-plugin - 3.14.0 + 3.14.1 UTF-8 -Xlint:unchecked @@ -452,7 +467,7 @@ maven-jar-plugin - 3.4.2 + 3.5.0 true @@ -472,7 +487,7 @@ org.apache.maven.plugins maven-source-plugin - 3.3.1 + 3.4.0 org.codehaus.mojo @@ -482,7 +497,7 @@ org.apache.maven.plugins maven-resources-plugin - 3.3.1 + 3.4.0 UTF-8 @@ -490,7 +505,7 @@ org.apache.maven.plugins maven-release-plugin - 3.1.1 + 3.3.1 -DskipTests @@ -498,7 +513,7 @@ org.apache.maven.plugins maven-antrun-plugin - 3.1.0 + 3.2.0 run-ant-target @@ -522,7 +537,7 @@ org.apache.maven.plugins maven-javadoc-plugin - 3.11.2 + 3.12.0 8 none @@ -540,7 +555,7 @@ org.apache.maven.plugins maven-surefire-plugin - 3.5.3 + 3.5.4 @{argLine} --add-opens=java.base/sun.security.ssl=ALL-UNNAMED --add-opens=java.base/sun.security.util=ALL-UNNAMED @@ -560,14 +575,14 @@ org.apache.maven.surefire surefire-junit47 - 3.5.3 + 3.5.4 org.apache.maven.plugins maven-failsafe-plugin - 3.5.3 + 3.5.4 ${project.build.directory}/surefire-reports UTF-8 @@ -589,7 +604,7 @@ org.apache.maven.surefire surefire-junit47 - 3.5.3 + 3.5.4 @@ -601,7 +616,7 @@ org.apache.maven.plugins maven-dependency-plugin - 3.8.1 + 3.9.0 copy-dependencies-dylib-target @@ -694,7 +709,7 @@ org.apache.maven.plugins maven-assembly-plugin - 3.7.1 + 3.8.0 org.apache.maven.plugins @@ -715,12 +730,12 @@ io.swagger swagger-codegen-maven-plugin - 2.4.45 + 2.4.50 io.swagger.codegen.v3 swagger-codegen-maven-plugin - 3.0.69 + 3.0.75 src/main/java @@ -742,7 +757,7 @@ org.apache.maven.plugins maven-enforcer-plugin - 3.5.0 + 3.6.2 enforce-bytecode-version @@ -795,7 +810,7 @@ org.codehaus.mojo extra-enforcer-rules - 1.10.0 + 1.11.0 @@ -876,7 +891,7 @@ org.apache.maven.plugins maven-javadoc-plugin - 3.11.2 + 3.12.0 aggregate @@ -963,7 +978,7 @@ org.apache.maven.plugins maven-gpg-plugin - 3.2.7 + 3.2.8 sign-artifacts diff --git a/profiles/default/Microsoft SharePoint.cyberduckprofile b/profiles/default/Microsoft SharePoint.cyberduckprofile index b50e843cda..7966c2f896 100644 --- a/profiles/default/Microsoft SharePoint.cyberduckprofile +++ b/profiles/default/Microsoft SharePoint.cyberduckprofile @@ -44,6 +44,7 @@ sites.readwrite.all files.readwrite.all user.read + groupmember.read.all offline_access OAuth Client ID diff --git a/profiles/default/S3 (HTTPS).cyberduckprofile b/profiles/default/S3 (HTTPS).cyberduckprofile index cf29d08b88..92b1545cb3 100644 --- a/profiles/default/S3 (HTTPS).cyberduckprofile +++ b/profiles/default/S3 (HTTPS).cyberduckprofile @@ -37,6 +37,7 @@ af-south-1 ap-east-1 + ap-east-2 ap-south-1 ap-south-2 ap-northeast-1 @@ -47,6 +48,7 @@ ap-southeast-3 ap-southeast-4 ap-southeast-5 + ap-southeast-7 ca-central-1 ca-west-1 eu-west-1 @@ -60,6 +62,7 @@ il-central-1 me-central-1 me-south-1 + mx-central-1 sa-east-1 us-east-1 us-east-2 diff --git a/profiles/pom.xml b/profiles/pom.xml index 1b8b64065b..eb0fb693e9 100644 --- a/profiles/pom.xml +++ b/profiles/pom.xml @@ -19,7 +19,7 @@ parent ch.cyberduck - 9.2.0-SNAPSHOT + 9.4.0-SNAPSHOT profiles jar diff --git a/protocols/dll/pom.xml b/protocols/dll/pom.xml index 52c4948ccf..4d7d0d546a 100644 --- a/protocols/dll/pom.xml +++ b/protocols/dll/pom.xml @@ -5,7 +5,7 @@ ch.cyberduck parent ../../pom.xml - 9.2.0-SNAPSHOT + 9.4.0-SNAPSHOT Cyberduck.Protocols pom diff --git a/protocols/pom.xml b/protocols/pom.xml index 0882e1bbfb..3dde99b5c4 100644 --- a/protocols/pom.xml +++ b/protocols/pom.xml @@ -18,7 +18,7 @@ parent ch.cyberduck - 9.2.0-SNAPSHOT + 9.4.0-SNAPSHOT 4.0.0 pom diff --git a/runtime.xml b/runtime.xml index de02069a3a..587bc5404f 100644 --- a/runtime.xml +++ b/runtime.xml @@ -2,7 +2,7 @@ - + ch.cyberduck parent - 9.2.0-SNAPSHOT + 9.4.0-SNAPSHOT s3 jar @@ -79,7 +79,7 @@ org.testcontainers testcontainers - 1.21.2 + 2.0.3 test diff --git a/s3/src/main/java/ch/cyberduck/core/auth/AWSCredentialsConfigurator.java b/s3/src/main/java/ch/cyberduck/core/auth/AWSCredentialsConfigurator.java deleted file mode 100644 index ac63c832d6..0000000000 --- a/s3/src/main/java/ch/cyberduck/core/auth/AWSCredentialsConfigurator.java +++ /dev/null @@ -1,121 +0,0 @@ -package ch.cyberduck.core.auth; - -/* - * Copyright (c) 2002-2017 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 ch.cyberduck.core.Credentials; -import ch.cyberduck.core.CredentialsConfigurator; -import ch.cyberduck.core.Host; -import ch.cyberduck.core.LoginOptions; -import ch.cyberduck.core.exception.LoginCanceledException; - -import org.apache.logging.log4j.LogManager; -import org.apache.logging.log4j.Logger; -import org.jets3t.service.security.ProviderCredentials; - -import java.util.Arrays; - -import com.amazonaws.SdkClientException; -import com.amazonaws.auth.AWSCredentials; -import com.amazonaws.auth.AWSCredentialsProvider; -import com.amazonaws.auth.AWSSessionCredentials; -import com.amazonaws.auth.AWSSessionCredentialsProvider; -import com.amazonaws.auth.AWSStaticCredentialsProvider; - -public class AWSCredentialsConfigurator implements CredentialsConfigurator { - private static final Logger log = LogManager.getLogger(AWSCredentialsConfigurator.class); - - private final AWSCredentialsProvider[] providers; - - public AWSCredentialsConfigurator(final AWSCredentialsProvider... providers) { - this.providers = providers; - } - - @Override - public Credentials configure(final Host host) { - if(!host.getCredentials().validate(host.getProtocol(), new LoginOptions(host.getProtocol()).password(false))) { - final Credentials credentials = new Credentials(host.getCredentials()); - // Lookup from default profile if no access key is set in bookmark - for(AWSCredentialsProvider provider : providers) { - try { - final AWSCredentials c = provider.getCredentials(); - log.debug("Configure {} with {}", host, c); - credentials.setUsername(c.getAWSAccessKeyId()); - credentials.setPassword(c.getAWSSecretKey()); - if(c instanceof AWSSessionCredentials) { - credentials.setToken(((AWSSessionCredentials) c).getSessionToken()); - } - break; - } - catch(SdkClientException e) { - log.debug("Ignore failure loading credentials from provider {}", provider); - // Continue searching with next provider - } - } - return credentials; - } - return CredentialsConfigurator.DISABLED.configure(host); - } - - @Override - public CredentialsConfigurator reload() throws LoginCanceledException { - log.debug("Reload from {}", Arrays.toString(providers)); - for(AWSCredentialsProvider provider : providers) { - provider.refresh(); - } - return this; - } - - public static AWSCredentialsProvider toAWSCredentialsProvider(final ProviderCredentials credentials) { - return credentials instanceof org.jets3t.service.security.AWSSessionCredentials ? - new AWSSessionCredentialsProvider() { - @Override - public AWSSessionCredentials getCredentials() { - return new AWSSessionCredentials() { - @Override - public String getSessionToken() { - return ((org.jets3t.service.security.AWSSessionCredentials) credentials).getSessionToken(); - } - - @Override - public String getAWSAccessKeyId() { - return credentials.getAccessKey(); - } - - @Override - public String getAWSSecretKey() { - return credentials.getSecretKey(); - } - }; - } - - @Override - public void refresh() { - // Not supported - } - } : - new AWSStaticCredentialsProvider(new AWSCredentials() { - @Override - public String getAWSAccessKeyId() { - return credentials.getAccessKey(); - } - - @Override - public String getAWSSecretKey() { - return credentials.getSecretKey(); - } - }); - } -} diff --git a/s3/src/main/java/ch/cyberduck/core/auth/AWSSessionCredentialsRetriever.java b/s3/src/main/java/ch/cyberduck/core/auth/AWSSessionCredentialsRetriever.java index 14e08897fe..83e4a974d8 100644 --- a/s3/src/main/java/ch/cyberduck/core/auth/AWSSessionCredentialsRetriever.java +++ b/s3/src/main/java/ch/cyberduck/core/auth/AWSSessionCredentialsRetriever.java @@ -16,7 +16,6 @@ package ch.cyberduck.core.auth; */ import ch.cyberduck.core.Credentials; -import ch.cyberduck.core.CredentialsConfigurator; import ch.cyberduck.core.DisabledLoginCallback; import ch.cyberduck.core.DisabledTranscriptListener; import ch.cyberduck.core.Host; @@ -27,7 +26,6 @@ import ch.cyberduck.core.TemporaryAccessTokens; import ch.cyberduck.core.date.ISO8601DateFormatter; import ch.cyberduck.core.date.InvalidDateException; import ch.cyberduck.core.exception.BackgroundException; -import ch.cyberduck.core.exception.LoginCanceledException; import ch.cyberduck.core.exception.LoginFailureException; import ch.cyberduck.core.http.HttpConnectionPoolBuilder; import ch.cyberduck.core.proxy.ProxyFactory; @@ -37,11 +35,9 @@ import ch.cyberduck.core.ssl.X509KeyManager; import ch.cyberduck.core.ssl.X509TrustManager; import org.apache.http.HttpEntity; -import org.apache.http.HttpResponse; import org.apache.http.HttpStatus; import org.apache.http.client.ClientProtocolException; import org.apache.http.client.HttpResponseException; -import org.apache.http.client.ResponseHandler; import org.apache.http.client.methods.HttpGet; import org.apache.http.client.methods.HttpRequestBase; import org.apache.http.impl.client.CloseableHttpClient; @@ -70,32 +66,6 @@ public class AWSSessionCredentialsRetriever implements S3CredentialsStrategy { this.url = url; } - public static class Configurator implements CredentialsConfigurator { - private final AWSSessionCredentialsRetriever retriever; - private final String url; - - public Configurator(final X509TrustManager trust, final X509KeyManager key, final String url) { - this.url = url; - this.retriever = new AWSSessionCredentialsRetriever(trust, key, url); - } - - @Override - public Configurator reload() throws LoginCanceledException { - return this; - } - - @Override - public Credentials configure(final Host host) { - try { - return retriever.get(); - } - catch(BackgroundException e) { - log.warn("Ignore failure {} retrieving credentials from {}", e, url); - return host.getCredentials(); - } - } - } - @Override public Credentials get() throws BackgroundException { log.debug("Configure credentials from {}", url); @@ -105,23 +75,20 @@ public class AWSSessionCredentialsRetriever implements S3CredentialsStrategy { final HttpClientBuilder configuration = builder.build(ProxyFactory.get(), new DisabledTranscriptListener(), new DisabledLoginCallback()); try (CloseableHttpClient client = configuration.build()) { - final HttpRequestBase resource = new HttpGet(new HostUrlProvider().withUsername(false).withPath(true).get(address)); - return client.execute(resource, new ResponseHandler() { - @Override - public Credentials handleResponse(final HttpResponse response) throws IOException { - switch(response.getStatusLine().getStatusCode()) { - case HttpStatus.SC_OK: - final HttpEntity entity = response.getEntity(); - if(entity == null) { - log.warn("Missing response entity in {}", response); - throw new ClientProtocolException("Empty response"); - } - else { - return parse(entity.getContent()); - } - } - throw new HttpResponseException(response.getStatusLine().getStatusCode(), response.getStatusLine().getReasonPhrase()); + final HttpRequestBase resource = new HttpGet(url); + return client.execute(resource, response -> { + switch(response.getStatusLine().getStatusCode()) { + case HttpStatus.SC_OK: + final HttpEntity entity = response.getEntity(); + if(entity == null) { + log.warn("Missing response entity in {}", response); + throw new ClientProtocolException("Empty response"); + } + else { + return parse(entity.getContent()); + } } + throw new HttpResponseException(response.getStatusLine().getStatusCode(), response.getStatusLine().getReasonPhrase()); }); } catch(IOException e) { @@ -161,6 +128,9 @@ public class AWSSessionCredentialsRetriever implements S3CredentialsStrategy { } } reader.endObject(); - return new Credentials().withTokens(new TemporaryAccessTokens(key, secret, token, expiration != null ? expiration.getTime() : -1L)); + final Credentials credentials = new Credentials().setTokens(new TemporaryAccessTokens( + key, secret, token, expiration != null ? expiration.getTime() : -1L)); + log.debug("Received session credentials {}", credentials); + return credentials; } } diff --git a/s3/src/main/java/ch/cyberduck/core/aws/CustomClientConfiguration.java b/s3/src/main/java/ch/cyberduck/core/aws/CustomClientConfiguration.java index e519bdb177..b438b878e1 100644 --- a/s3/src/main/java/ch/cyberduck/core/aws/CustomClientConfiguration.java +++ b/s3/src/main/java/ch/cyberduck/core/aws/CustomClientConfiguration.java @@ -50,15 +50,12 @@ import com.amazonaws.DnsResolver; public class CustomClientConfiguration extends ClientConfiguration { public CustomClientConfiguration(final Host host, final ThreadLocalHostnameDelegatingTrustManager trust, final X509KeyManager key) { - this.setDnsResolver(new DnsResolver() { - @Override - public InetAddress[] resolve(final String host) throws UnknownHostException { - try { - return new Resolver().resolve(host, new DisabledCancelCallback()); - } - catch(ResolveFailedException | ResolveCanceledException e) { - throw new UnknownHostException(e.getDetail(false)); - } + this.setDnsResolver(hostname -> { + try { + return new Resolver().resolve(hostname, new DisabledCancelCallback()); + } + catch(ResolveFailedException | ResolveCanceledException e) { + throw new UnknownHostException(e.getDetail(false)); } }); final int timeout = ConnectionTimeoutFactory.get(host).getTimeout() * 1000; diff --git a/s3/src/main/java/ch/cyberduck/core/cloudfront/CloudFrontDistributionConfiguration.java b/s3/src/main/java/ch/cyberduck/core/cloudfront/CloudFrontDistributionConfiguration.java index 951ee13650..1e9ce11ecc 100644 --- a/s3/src/main/java/ch/cyberduck/core/cloudfront/CloudFrontDistributionConfiguration.java +++ b/s3/src/main/java/ch/cyberduck/core/cloudfront/CloudFrontDistributionConfiguration.java @@ -25,9 +25,7 @@ import ch.cyberduck.core.HostUrlProvider; import ch.cyberduck.core.LocaleFactory; import ch.cyberduck.core.LoginCallback; import ch.cyberduck.core.Path; -import ch.cyberduck.core.PathContainerService; import ch.cyberduck.core.Scheme; -import ch.cyberduck.core.auth.AWSCredentialsConfigurator; import ch.cyberduck.core.aws.AmazonServiceExceptionMappingService; import ch.cyberduck.core.aws.CustomClientConfiguration; import ch.cyberduck.core.cdn.Distribution; @@ -44,6 +42,8 @@ import ch.cyberduck.core.preferences.HostPreferencesFactory; import ch.cyberduck.core.preferences.Preferences; import ch.cyberduck.core.preferences.PreferencesFactory; import ch.cyberduck.core.s3.S3BucketListService; +import ch.cyberduck.core.s3.S3CredentialsStrategy; +import ch.cyberduck.core.s3.S3PathContainerService; import ch.cyberduck.core.s3.S3Protocol; import ch.cyberduck.core.s3.S3Session; import ch.cyberduck.core.ssl.ThreadLocalHostnameDelegatingTrustManager; @@ -89,7 +89,7 @@ public class CloudFrontDistributionConfiguration implements DistributionConfigur this.bookmark = session.getHost(); this.location = location; this.configuration = new CustomClientConfiguration(bookmark, - new ThreadLocalHostnameDelegatingTrustManager(trust, bookmark.getHostname()), key); + new ThreadLocalHostnameDelegatingTrustManager(trust, bookmark.getHostname()), key); } @Override @@ -113,13 +113,13 @@ public class CloudFrontDistributionConfiguration implements DistributionConfigur @Override public Distribution read(final Path file, final Distribution.Method method, final LoginCallback prompt) throws BackgroundException { - final Path container = session.getFeature(PathContainerService.class).getContainer(file); + final Path container = new S3PathContainerService(session.getHost()).getContainer(file); try { log.debug("List {} distributions", method); final AmazonCloudFront client = this.client(container); if(method.equals(Distribution.STREAMING)) { for(StreamingDistributionSummary d : client.listStreamingDistributions( - new ListStreamingDistributionsRequest()).getStreamingDistributionList().getItems()) { + new ListStreamingDistributionsRequest()).getStreamingDistributionList().getItems()) { final S3Origin config = d.getS3Origin(); if(config != null) { final URI origin = this.getOrigin(container, method); @@ -133,7 +133,7 @@ public class CloudFrontDistributionConfiguration implements DistributionConfigur else if(method.equals(Distribution.DOWNLOAD)) { // List distributions restricting to bucket name origin for(DistributionSummary d : client.listDistributions( - new ListDistributionsRequest()).getDistributionList().getItems()) { + new ListDistributionsRequest()).getDistributionList().getItems()) { for(Origin o : d.getOrigins().getItems()) { final S3OriginConfig config = o.getS3OriginConfig(); if(config != null) { @@ -171,7 +171,7 @@ public class CloudFrontDistributionConfiguration implements DistributionConfigur @Override public void write(final Path file, final Distribution distribution, final LoginCallback prompt) throws BackgroundException { - final Path container = session.getFeature(PathContainerService.class).getContainer(file); + final Path container = new S3PathContainerService(session.getHost()).getContainer(file); try { if(null == distribution.getId()) { // No existing configuration @@ -183,7 +183,7 @@ public class CloudFrontDistributionConfiguration implements DistributionConfigur distribution.setId(this.createDownloadDistribution(container, distribution).getId()); } else if(distribution.getMethod().equals(Distribution.CUSTOM) - || distribution.getMethod().equals(Distribution.WEBSITE_CDN)) { + || distribution.getMethod().equals(Distribution.WEBSITE_CDN)) { distribution.setId(this.createCustomDistribution(container, distribution).getId()); } } @@ -195,7 +195,7 @@ public class CloudFrontDistributionConfiguration implements DistributionConfigur distribution.setEtag(this.updateStreamingDistribution(container, distribution).getETag()); } else if(distribution.getMethod().equals(Distribution.CUSTOM) - || distribution.getMethod().equals(Distribution.WEBSITE_CDN)) { + || distribution.getMethod().equals(Distribution.WEBSITE_CDN)) { distribution.setEtag(this.updateCustomDistribution(container, distribution).getETag()); } } @@ -210,15 +210,15 @@ public class CloudFrontDistributionConfiguration implements DistributionConfigur public T getFeature(final Class type, final Distribution.Method method) { if(type == Purge.class || type == Index.class) { if(method.equals(Distribution.DOWNLOAD) - || method.equals(Distribution.WEBSITE_CDN) - || method.equals(Distribution.CUSTOM)) { + || method.equals(Distribution.WEBSITE_CDN) + || method.equals(Distribution.CUSTOM)) { return (T) this; } } if(type == DistributionLogging.class) { if(method.equals(Distribution.DOWNLOAD) - || method.equals(Distribution.STREAMING) - || method.equals(Distribution.CUSTOM)) { + || method.equals(Distribution.STREAMING) + || method.equals(Distribution.CUSTOM)) { return (T) this; } } @@ -242,17 +242,17 @@ public class CloudFrontDistributionConfiguration implements DistributionConfigur if(d.isEnabled()) { final List keys = new ArrayList<>(); for(Path file : files) { - if(session.getFeature(PathContainerService.class).isContainer(file)) { + if(new S3PathContainerService(session.getHost()).isContainer(file)) { // To invalidate all of the objects in a distribution keys.add(String.format("%s*", Path.DELIMITER)); } else { if(file.isDirectory()) { // The *, which replaces 0 or more characters, must be the last character in the invalidation path - keys.add(String.format("/%s*", session.getFeature(PathContainerService.class).getKey(file))); + keys.add(String.format("/%s*", new S3PathContainerService(session.getHost()).getKey(file))); } else { - keys.add(String.format("/%s", session.getFeature(PathContainerService.class).getKey(file))); + keys.add(String.format("/%s", new S3PathContainerService(session.getHost()).getKey(file))); } } } @@ -262,7 +262,7 @@ public class CloudFrontDistributionConfiguration implements DistributionConfigur else { final AmazonCloudFront client = this.client(container); client.createInvalidation(new CreateInvalidationRequest(d.getId(), - new InvalidationBatch(new Paths().withItems(keys).withQuantity(keys.size()), new AlphanumericRandomStringService().random()) + new InvalidationBatch(new Paths().withItems(keys).withQuantity(keys.size()), new AlphanumericRandomStringService().random()) )); } } @@ -284,8 +284,8 @@ public class CloudFrontDistributionConfiguration implements DistributionConfigur String marker = null; do { final ListInvalidationsResult response = client.listInvalidations(new ListInvalidationsRequest(distribution.getId()) - .withMaxItems(String.valueOf(1000)) - .withMarker(marker)); + .withMaxItems(String.valueOf(1000)) + .withMarker(marker)); for(InvalidationSummary s : response.getInvalidationList().getItems()) { // When the invalidation batch is finished, the status is Completed. if("Completed".equals(s.getStatus())) { @@ -319,70 +319,70 @@ public class CloudFrontDistributionConfiguration implements DistributionConfigur * @return Distribution configuration */ protected StreamingDistribution createStreamingDistribution(final Path container, final Distribution distribution) - throws BackgroundException { + throws BackgroundException { log.debug("Create new {} distribution", distribution); final AmazonCloudFront client = this.client(container); final URI origin = this.getOrigin(container, distribution.getMethod()); final String originId = String.format("%s-%s", preferences.getProperty("application.name"), new AlphanumericRandomStringService().random()); final StreamingDistributionConfig config = new StreamingDistributionConfig(new AlphanumericRandomStringService().random(), - new S3Origin(origin.getHost(), StringUtils.EMPTY), distribution.isEnabled()) - .withComment(originId) - .withTrustedSigners(new TrustedSigners().withEnabled(false).withQuantity(0)) - .withAliases(new Aliases().withItems(distribution.getCNAMEs()).withQuantity(distribution.getCNAMEs().length)); + new S3Origin(origin.getHost(), StringUtils.EMPTY), distribution.isEnabled()) + .withComment(originId) + .withTrustedSigners(new TrustedSigners().withEnabled(false).withQuantity(0)) + .withAliases(new Aliases().withItems(distribution.getCNAMEs()).withQuantity(distribution.getCNAMEs().length)); // Make bucket name fully qualified final String loggingTarget = ServiceUtils.generateS3HostnameForBucket(distribution.getLoggingContainer(), - false, new S3Protocol().getDefaultHostname()); + false, new S3Protocol().getDefaultHostname()); log.debug("Set logging target for {} to {}", distribution, loggingTarget); config.setLogging(new StreamingLoggingConfig() - .withEnabled(distribution.isLogging()) - .withBucket(loggingTarget) + .withEnabled(distribution.isLogging()) + .withBucket(loggingTarget) .withPrefix(HostPreferencesFactory.get(session.getHost()).getProperty("cloudfront.logging.prefix")) ); return client.createStreamingDistribution(new CreateStreamingDistributionRequest(config)).getStreamingDistribution(); } protected com.amazonaws.services.cloudfront.model.Distribution createDownloadDistribution(final Path container, final Distribution distribution) - throws BackgroundException { + throws BackgroundException { log.debug("Create new {} distribution", distribution); final AmazonCloudFront client = this.client(container); final URI origin = this.getOrigin(container, distribution.getMethod()); final String originId = String.format("%s-%s", preferences.getProperty("application.name"), new AlphanumericRandomStringService().random()); final DistributionConfig config = new DistributionConfig(new AlphanumericRandomStringService().random(), distribution.isEnabled()) - .withComment(originId) - .withOrigins(new Origins() - .withQuantity(1) - .withItems(new Origin() - .withId(originId) - .withCustomHeaders(new CustomHeaders().withQuantity(0)) - .withOriginPath(StringUtils.EMPTY) - .withDomainName(origin.getHost()) - .withS3OriginConfig(new S3OriginConfig().withOriginAccessIdentity(StringUtils.EMPTY)) + .withComment(originId) + .withOrigins(new Origins() + .withQuantity(1) + .withItems(new Origin() + .withId(originId) + .withCustomHeaders(new CustomHeaders().withQuantity(0)) + .withOriginPath(StringUtils.EMPTY) + .withDomainName(origin.getHost()) + .withS3OriginConfig(new S3OriginConfig().withOriginAccessIdentity(StringUtils.EMPTY)) + ) ) - ) - .withPriceClass(PriceClass.PriceClass_All) - .withDefaultCacheBehavior(new DefaultCacheBehavior() - .withTargetOriginId(originId) - .withForwardedValues(new ForwardedValues().withQueryString(true).withCookies(new CookiePreference().withForward(ItemSelection.All))) - .withViewerProtocolPolicy(ViewerProtocolPolicy.AllowAll) - .withMinTTL(0L) - .withTrustedSigners(new TrustedSigners().withEnabled(false).withQuantity(0))) - .withDefaultRootObject(distribution.getIndexDocument()) - .withAliases(new Aliases().withItems(distribution.getCNAMEs()).withQuantity(distribution.getCNAMEs().length)); + .withPriceClass(PriceClass.PriceClass_All) + .withDefaultCacheBehavior(new DefaultCacheBehavior() + .withTargetOriginId(originId) + .withForwardedValues(new ForwardedValues().withQueryString(true).withCookies(new CookiePreference().withForward(ItemSelection.All))) + .withViewerProtocolPolicy(ViewerProtocolPolicy.AllowAll) + .withMinTTL(0L) + .withTrustedSigners(new TrustedSigners().withEnabled(false).withQuantity(0))) + .withDefaultRootObject(distribution.getIndexDocument()) + .withAliases(new Aliases().withItems(distribution.getCNAMEs()).withQuantity(distribution.getCNAMEs().length)); // Make bucket name fully qualified final String loggingTarget = ServiceUtils.generateS3HostnameForBucket(distribution.getLoggingContainer(), - false, new S3Protocol().getDefaultHostname()); + false, new S3Protocol().getDefaultHostname()); log.debug("Set logging target for {} to {}", distribution, loggingTarget); config.setLogging(new LoggingConfig() - .withEnabled(distribution.isLogging()) - .withIncludeCookies(true) - .withBucket(loggingTarget) + .withEnabled(distribution.isLogging()) + .withIncludeCookies(true) + .withBucket(loggingTarget) .withPrefix(HostPreferencesFactory.get(session.getHost()).getProperty("cloudfront.logging.prefix") - )); + )); return client.createDistribution(new CreateDistributionRequest(config)).getDistribution(); } protected com.amazonaws.services.cloudfront.model.Distribution createCustomDistribution(final Path container, final Distribution distribution) - throws BackgroundException { + throws BackgroundException { final AmazonCloudFront client = this.client(container); int httpPort = 80; int httpsPort = 443; @@ -397,38 +397,38 @@ public class CloudFrontDistributionConfiguration implements DistributionConfigur } final String originId = String.format("%s-%s", preferences.getProperty("application.name"), new AlphanumericRandomStringService().random()); final DistributionConfig config = new DistributionConfig(new AlphanumericRandomStringService().random(), distribution.isEnabled()) - .withComment(originId) - .withOrigins(new Origins() - .withQuantity(1) - .withItems(new Origin() - .withId(originId) - .withDomainName(origin.getHost()) - .withCustomOriginConfig(new CustomOriginConfig() - .withHTTPPort(httpPort) - .withHTTPSPort(httpsPort) - .withOriginSslProtocols(new OriginSslProtocols().withQuantity(2).withItems("TLSv1.1", "TLSv1.2")) - .withOriginProtocolPolicy(this.getPolicy(distribution.getMethod())) - ) + .withComment(originId) + .withOrigins(new Origins() + .withQuantity(1) + .withItems(new Origin() + .withId(originId) + .withDomainName(origin.getHost()) + .withCustomOriginConfig(new CustomOriginConfig() + .withHTTPPort(httpPort) + .withHTTPSPort(httpsPort) + .withOriginSslProtocols(new OriginSslProtocols().withQuantity(2).withItems("TLSv1.1", "TLSv1.2")) + .withOriginProtocolPolicy(this.getPolicy(distribution.getMethod())) + ) + ) ) - ) - .withPriceClass(PriceClass.PriceClass_All) - .withDefaultCacheBehavior(new DefaultCacheBehavior() - .withTargetOriginId(originId) - .withForwardedValues(new ForwardedValues().withQueryString(true).withCookies(new CookiePreference().withForward(ItemSelection.All))) - .withViewerProtocolPolicy(ViewerProtocolPolicy.AllowAll) - .withMinTTL(0L) - .withTrustedSigners(new TrustedSigners().withEnabled(false).withQuantity(0))) - .withDefaultRootObject(distribution.getIndexDocument()) - .withAliases(new Aliases().withItems(distribution.getCNAMEs()).withQuantity(distribution.getCNAMEs().length)); + .withPriceClass(PriceClass.PriceClass_All) + .withDefaultCacheBehavior(new DefaultCacheBehavior() + .withTargetOriginId(originId) + .withForwardedValues(new ForwardedValues().withQueryString(true).withCookies(new CookiePreference().withForward(ItemSelection.All))) + .withViewerProtocolPolicy(ViewerProtocolPolicy.AllowAll) + .withMinTTL(0L) + .withTrustedSigners(new TrustedSigners().withEnabled(false).withQuantity(0))) + .withDefaultRootObject(distribution.getIndexDocument()) + .withAliases(new Aliases().withItems(distribution.getCNAMEs()).withQuantity(distribution.getCNAMEs().length)); if(distribution.isLogging()) { // Make bucket name fully qualified final String loggingTarget = ServiceUtils.generateS3HostnameForBucket(distribution.getLoggingContainer(), - false, new S3Protocol().getDefaultHostname()); + false, new S3Protocol().getDefaultHostname()); log.debug("Set logging target for {} to {}", distribution, loggingTarget); config.setLogging(new LoggingConfig() - .withEnabled(distribution.isLogging()) - .withIncludeCookies(true) - .withBucket(loggingTarget) + .withEnabled(distribution.isLogging()) + .withIncludeCookies(true) + .withBucket(loggingTarget) .withPrefix(HostPreferencesFactory.get(session.getHost()).getProperty("cloudfront.logging.prefix")) ); } @@ -439,24 +439,24 @@ public class CloudFrontDistributionConfiguration implements DistributionConfigur * Amazon CloudFront Extension used to enable or disable a distribution configuration and its CNAMESs */ protected UpdateDistributionResult updateDownloadDistribution(final Path container, final Distribution distribution) - throws BackgroundException { + throws BackgroundException { final URI origin = this.getOrigin(container, distribution.getMethod()); log.debug("Update {} distribution with origin {}", distribution, origin); final AmazonCloudFront client = this.client(container); final GetDistributionConfigResult response = client.getDistributionConfig(new GetDistributionConfigRequest(distribution.getId())); final DistributionConfig config = response.getDistributionConfig() - .withEnabled(distribution.isEnabled()) - .withDefaultRootObject(distribution.getIndexDocument()) - .withAliases(new Aliases().withItems(distribution.getCNAMEs()).withQuantity(distribution.getCNAMEs().length)); + .withEnabled(distribution.isEnabled()) + .withDefaultRootObject(distribution.getIndexDocument()) + .withAliases(new Aliases().withItems(distribution.getCNAMEs()).withQuantity(distribution.getCNAMEs().length)); if(distribution.isLogging()) { // Make bucket name fully qualified final String loggingTarget = ServiceUtils.generateS3HostnameForBucket(distribution.getLoggingContainer(), - false, new S3Protocol().getDefaultHostname()); + false, new S3Protocol().getDefaultHostname()); log.debug("Set logging target for {} to {}", distribution, loggingTarget); config.setLogging(new LoggingConfig() - .withEnabled(distribution.isLogging()) - .withIncludeCookies(true) - .withBucket(loggingTarget) + .withEnabled(distribution.isLogging()) + .withIncludeCookies(true) + .withBucket(loggingTarget) .withPrefix(HostPreferencesFactory.get(session.getHost()).getProperty("cloudfront.logging.prefix")) ); } @@ -464,23 +464,23 @@ public class CloudFrontDistributionConfiguration implements DistributionConfigur } protected UpdateStreamingDistributionResult updateStreamingDistribution(final Path container, final Distribution distribution) - throws BackgroundException { + throws BackgroundException { final URI origin = this.getOrigin(container, distribution.getMethod()); log.debug("Update {} distribution with origin {}", distribution, origin); final AmazonCloudFront client = this.client(container); final GetStreamingDistributionConfigResult response = client.getStreamingDistributionConfig(new GetStreamingDistributionConfigRequest(distribution.getId())); final StreamingDistributionConfig config = response.getStreamingDistributionConfig() - .withEnabled(distribution.isEnabled()) - .withS3Origin(new S3Origin(origin.getHost(), StringUtils.EMPTY)) - .withAliases(new Aliases().withItems(distribution.getCNAMEs()).withQuantity(distribution.getCNAMEs().length)); + .withEnabled(distribution.isEnabled()) + .withS3Origin(new S3Origin(origin.getHost(), StringUtils.EMPTY)) + .withAliases(new Aliases().withItems(distribution.getCNAMEs()).withQuantity(distribution.getCNAMEs().length)); if(distribution.isLogging()) { // Make bucket name fully qualified final String loggingTarget = ServiceUtils.generateS3HostnameForBucket(distribution.getLoggingContainer(), - false, new S3Protocol().getDefaultHostname()); + false, new S3Protocol().getDefaultHostname()); log.debug("Set logging target for {} to {}", distribution, loggingTarget); config.setLogging(new StreamingLoggingConfig() - .withEnabled(distribution.isLogging()) - .withBucket(loggingTarget) + .withEnabled(distribution.isLogging()) + .withBucket(loggingTarget) .withPrefix(HostPreferencesFactory.get(session.getHost()).getProperty("cloudfront.logging.prefix")) ); } @@ -488,30 +488,30 @@ public class CloudFrontDistributionConfiguration implements DistributionConfigur } protected UpdateDistributionResult updateCustomDistribution(final Path container, final Distribution distribution) - throws BackgroundException { + throws BackgroundException { final URI origin = this.getOrigin(container, distribution.getMethod()); log.debug("Update {} distribution with origin {}", distribution, origin); final AmazonCloudFront client = this.client(container); final GetDistributionConfigResult response = client.getDistributionConfig(new GetDistributionConfigRequest(distribution.getId())); final DistributionConfig config = response.getDistributionConfig() - .withEnabled(distribution.isEnabled()) - .withDefaultRootObject(distribution.getIndexDocument() != null ? distribution.getIndexDocument() : StringUtils.EMPTY) - .withAliases(new Aliases().withItems(distribution.getCNAMEs()).withQuantity(distribution.getCNAMEs().length)); + .withEnabled(distribution.isEnabled()) + .withDefaultRootObject(distribution.getIndexDocument() != null ? distribution.getIndexDocument() : StringUtils.EMPTY) + .withAliases(new Aliases().withItems(distribution.getCNAMEs()).withQuantity(distribution.getCNAMEs().length)); // Make bucket name fully qualified final String loggingTarget = ServiceUtils.generateS3HostnameForBucket(distribution.getLoggingContainer(), - false, new S3Protocol().getDefaultHostname()); + false, new S3Protocol().getDefaultHostname()); log.debug("Set logging target for {} to {}", distribution, loggingTarget); config.setLogging(new LoggingConfig() - .withEnabled(distribution.isLogging()) - .withIncludeCookies(true) - .withBucket(loggingTarget) + .withEnabled(distribution.isLogging()) + .withIncludeCookies(true) + .withBucket(loggingTarget) .withPrefix(HostPreferencesFactory.get(session.getHost()).getProperty("cloudfront.logging.prefix")) ); return client.updateDistribution(new UpdateDistributionRequest(config, distribution.getId(), response.getETag())); } protected void deleteDownloadDistribution(final Path container, final Distribution distribution) - throws BackgroundException { + throws BackgroundException { final URI origin = this.getOrigin(container, distribution.getMethod()); log.debug("Update {} distribution with origin {}", distribution, origin); final AmazonCloudFront client = this.client(container); @@ -519,7 +519,7 @@ public class CloudFrontDistributionConfiguration implements DistributionConfigur } protected void deleteStreamingDistribution(final Path container, final Distribution distribution) - throws BackgroundException { + throws BackgroundException { final URI origin = this.getOrigin(container, distribution.getMethod()); log.debug("Update {} distribution with origin {}", distribution, origin); final AmazonCloudFront client = this.client(container); @@ -553,15 +553,15 @@ public class CloudFrontDistributionConfiguration implements DistributionConfigur distribution.setCNAMEs(configuration.getAliases().getItems().toArray(new String[configuration.getAliases().getItems().size()])); distribution.setLogging(configuration.getLogging().isEnabled()); distribution.setLoggingContainer(StringUtils.isNotBlank(configuration.getLogging().getBucket()) ? - ServiceUtils.findBucketNameInHostname(configuration.getLogging().getBucket(), new S3Protocol().getDefaultHostname()) : null); + ServiceUtils.findBucketNameInHostname(configuration.getLogging().getBucket(), new S3Protocol().getDefaultHostname()) : null); if(this.getFeature(Purge.class, method) != null) { distribution.setInvalidationStatus(this.readInvalidationStatus(client, distribution)); } if(this.getFeature(DistributionLogging.class, method) != null) { try { distribution.setContainers(new S3BucketListService(session).list( - new Path(String.valueOf(Path.DELIMITER), EnumSet.of(Path.Type.volume, Path.Type.directory)), - new DisabledListProgressListener()).toList()); + new Path(String.valueOf(Path.DELIMITER), EnumSet.of(Path.Type.volume, Path.Type.directory)), + new DisabledListProgressListener()).toList()); } catch(AccessDeniedException | InteroperabilityException e) { log.warn("Failure listing buckets. {}", e.getMessage()); @@ -593,7 +593,7 @@ public class CloudFrontDistributionConfiguration implements DistributionConfigur distribution.setCNAMEs(configuration.getAliases().getItems().toArray(new String[configuration.getAliases().getItems().size()])); distribution.setLogging(configuration.getLogging().isEnabled()); distribution.setLoggingContainer(StringUtils.isNotBlank(configuration.getLogging().getBucket()) ? - ServiceUtils.findBucketNameInHostname(configuration.getLogging().getBucket(), new S3Protocol().getDefaultHostname()) : null); + ServiceUtils.findBucketNameInHostname(configuration.getLogging().getBucket(), new S3Protocol().getDefaultHostname()) : null); if(StringUtils.isNotBlank(configuration.getDefaultRootObject())) { distribution.setIndexDocument(configuration.getDefaultRootObject()); } @@ -602,8 +602,8 @@ public class CloudFrontDistributionConfiguration implements DistributionConfigur } if(this.getFeature(DistributionLogging.class, method) != null) { distribution.setContainers(new S3BucketListService(session).list( - new Path(String.valueOf(Path.DELIMITER), EnumSet.of(Path.Type.volume, Path.Type.directory)), - new DisabledListProgressListener()).toList()); + new Path(String.valueOf(Path.DELIMITER), EnumSet.of(Path.Type.volume, Path.Type.directory)), + new DisabledListProgressListener()).toList()); } return distribution; } @@ -614,10 +614,8 @@ public class CloudFrontDistributionConfiguration implements DistributionConfigur private AmazonCloudFront client(final Path container) throws BackgroundException { final AmazonCloudFrontClientBuilder builder = AmazonCloudFrontClientBuilder.standard() - .withClientConfiguration(configuration); - if(session.getClient().isAuthenticatedConnection()) { - builder.withCredentials(AWSCredentialsConfigurator.toAWSCredentialsProvider(session.getClient().getProviderCredentials())); - } + .withClientConfiguration(configuration) + .withCredentials(S3CredentialsStrategy.toCredentialsProvider(session.getAuthentication().get())); final Location.Name region = this.getRegion(container); if(S3Session.isAwsHostname(session.getHost().getHostname(), false)) { if(Location.unknown.equals(region)) { @@ -629,7 +627,7 @@ public class CloudFrontDistributionConfiguration implements DistributionConfigur } else { builder.withEndpointConfiguration(new AwsClientBuilder.EndpointConfiguration( - new HostUrlProvider(false).get(session.getHost()), region.getIdentifier())); + new HostUrlProvider(false).get(session.getHost()), region.getIdentifier())); } return builder.build(); } diff --git a/s3/src/main/java/ch/cyberduck/core/cloudfront/WebsiteCloudFrontDistributionConfiguration.java b/s3/src/main/java/ch/cyberduck/core/cloudfront/WebsiteCloudFrontDistributionConfiguration.java index 4dfe649a0d..9668fb2df2 100644 --- a/s3/src/main/java/ch/cyberduck/core/cloudfront/WebsiteCloudFrontDistributionConfiguration.java +++ b/s3/src/main/java/ch/cyberduck/core/cloudfront/WebsiteCloudFrontDistributionConfiguration.java @@ -32,6 +32,7 @@ import ch.cyberduck.core.features.Location; import ch.cyberduck.core.s3.S3BucketListService; import ch.cyberduck.core.s3.S3ExceptionMappingService; import ch.cyberduck.core.s3.S3LocationFeature; +import ch.cyberduck.core.s3.S3PathContainerService; import ch.cyberduck.core.s3.S3Session; import ch.cyberduck.core.ssl.X509KeyManager; import ch.cyberduck.core.ssl.X509TrustManager; @@ -59,7 +60,7 @@ public class WebsiteCloudFrontDistributionConfiguration extends CloudFrontDistri public WebsiteCloudFrontDistributionConfiguration(final S3Session session, final S3LocationFeature location, final X509TrustManager trust, final X509KeyManager key) { super(session, location, trust, key); this.session = session; - this.containerService = session.getFeature(PathContainerService.class); + this.containerService = new S3PathContainerService(session.getHost()); } /** diff --git a/s3/src/main/java/ch/cyberduck/core/kms/KMSEncryptionFeature.java b/s3/src/main/java/ch/cyberduck/core/kms/KMSEncryptionFeature.java index 82e74f6470..e4b80cd7ba 100644 --- a/s3/src/main/java/ch/cyberduck/core/kms/KMSEncryptionFeature.java +++ b/s3/src/main/java/ch/cyberduck/core/kms/KMSEncryptionFeature.java @@ -20,7 +20,6 @@ import ch.cyberduck.core.HostUrlProvider; import ch.cyberduck.core.LoginCallback; import ch.cyberduck.core.Path; import ch.cyberduck.core.PathContainerService; -import ch.cyberduck.core.auth.AWSCredentialsConfigurator; import ch.cyberduck.core.aws.AmazonServiceExceptionMappingService; import ch.cyberduck.core.aws.CustomClientConfiguration; import ch.cyberduck.core.exception.AccessDeniedException; @@ -28,7 +27,9 @@ import ch.cyberduck.core.exception.BackgroundException; import ch.cyberduck.core.features.Location; import ch.cyberduck.core.preferences.HostPreferencesFactory; import ch.cyberduck.core.s3.S3AccessControlListFeature; +import ch.cyberduck.core.s3.S3CredentialsStrategy; import ch.cyberduck.core.s3.S3EncryptionFeature; +import ch.cyberduck.core.s3.S3PathContainerService; import ch.cyberduck.core.s3.S3Session; import ch.cyberduck.core.ssl.ThreadLocalHostnameDelegatingTrustManager; import ch.cyberduck.core.ssl.X509KeyManager; @@ -66,7 +67,7 @@ public class KMSEncryptionFeature extends S3EncryptionFeature { final Host bookmark = session.getHost(); this.configuration = new CustomClientConfiguration(bookmark, new ThreadLocalHostnameDelegatingTrustManager(trust, bookmark.getHostname()), key); - this.containerService = session.getFeature(PathContainerService.class); + this.containerService = new S3PathContainerService(session.getHost()); } @Override @@ -130,10 +131,8 @@ public class KMSEncryptionFeature extends S3EncryptionFeature { private AWSKMS client(final Path container) throws BackgroundException { final AWSKMSClientBuilder builder = AWSKMSClientBuilder.standard() - .withClientConfiguration(configuration); - if(session.getClient().isAuthenticatedConnection()) { - builder.withCredentials(AWSCredentialsConfigurator.toAWSCredentialsProvider(session.getClient().getProviderCredentials())); - } + .withClientConfiguration(configuration) + .withCredentials(S3CredentialsStrategy.toCredentialsProvider(session.getAuthentication().get())); final Location.Name region = location.getLocation(container); if(S3Session.isAwsHostname(session.getHost().getHostname(), false)) { if(Location.unknown.equals(region)) { @@ -145,7 +144,7 @@ public class KMSEncryptionFeature extends S3EncryptionFeature { } else { builder.withEndpointConfiguration(new AwsClientBuilder.EndpointConfiguration( - new HostUrlProvider(false).get(session.getHost()), region.getIdentifier())); + new HostUrlProvider(false).get(session.getHost()), region.getIdentifier())); } return builder.build(); } diff --git a/s3/src/main/java/ch/cyberduck/core/restore/Glacier.java b/s3/src/main/java/ch/cyberduck/core/restore/Glacier.java index 90a3907f07..91e84e00b4 100644 --- a/s3/src/main/java/ch/cyberduck/core/restore/Glacier.java +++ b/s3/src/main/java/ch/cyberduck/core/restore/Glacier.java @@ -19,8 +19,6 @@ import ch.cyberduck.core.Host; import ch.cyberduck.core.HostUrlProvider; import ch.cyberduck.core.LoginCallback; import ch.cyberduck.core.Path; -import ch.cyberduck.core.PathContainerService; -import ch.cyberduck.core.auth.AWSCredentialsConfigurator; import ch.cyberduck.core.aws.AmazonServiceExceptionMappingService; import ch.cyberduck.core.aws.CustomClientConfiguration; import ch.cyberduck.core.exception.BackgroundException; @@ -28,6 +26,8 @@ import ch.cyberduck.core.exception.ConflictException; import ch.cyberduck.core.features.Location; import ch.cyberduck.core.features.Restore; import ch.cyberduck.core.preferences.HostPreferencesFactory; +import ch.cyberduck.core.s3.S3CredentialsStrategy; +import ch.cyberduck.core.s3.S3PathContainerService; import ch.cyberduck.core.s3.S3Session; import ch.cyberduck.core.ssl.ThreadLocalHostnameDelegatingTrustManager; import ch.cyberduck.core.ssl.X509KeyManager; @@ -73,7 +73,7 @@ public class Glacier implements Restore { */ @Override public void restore(final Path file, final LoginCallback prompt) throws BackgroundException { - final Path container = session.getFeature(PathContainerService.class).getContainer(file); + final Path container = new S3PathContainerService(session.getHost()).getContainer(file); try { try { final AmazonS3 client = client(container); @@ -81,9 +81,9 @@ public class Glacier implements Restore { // This is the default option for the GLACIER and DEEP_ARCHIVE retrieval requests that do not specify // the retrieval option. S3 Standard retrievals typically complete within 3-5 hours from the GLACIER // storage class and typically complete within 12 hours from the DEEP_ARCHIVE storage class. - client.restoreObjectV2(new RestoreObjectRequest(container.getName(), session.getFeature(PathContainerService.class).getKey(file)) - // To restore a specific object version, you can provide a version ID. If you don't provide a version ID, Amazon S3 restores the current version. - .withVersionId(file.attributes().getVersionId()) + client.restoreObjectV2(new RestoreObjectRequest(container.getName(), new S3PathContainerService(session.getHost()).getKey(file)) + // To restore a specific object version, you can provide a version ID. If you don't provide a version ID, Amazon S3 restores the current version. + .withVersionId(file.attributes().getVersionId()) .withExpirationInDays(HostPreferencesFactory.get(session.getHost()).getInteger("s3.glacier.restore.expiration.days")) .withGlacierJobParameters(new GlacierJobParameters().withTier(HostPreferencesFactory.get(session.getHost()).getProperty("s3.glacier.restore.tier"))) ); @@ -102,16 +102,13 @@ public class Glacier implements Restore { @Override public boolean isRestorable(final Path file) { return StringUtils.equals("GLACIER", file.attributes().getStorageClass()) || - StringUtils.equals("DEEP_ARCHIVE", file.attributes().getStorageClass()); + StringUtils.equals("DEEP_ARCHIVE", file.attributes().getStorageClass()); } private AmazonS3 client(final Path container) throws BackgroundException { final AmazonS3ClientBuilder builder = AmazonS3ClientBuilder.standard() - .withCredentials(AWSCredentialsConfigurator.toAWSCredentialsProvider(session.getClient().getProviderCredentials())) - .withClientConfiguration(configuration); - if(session.getClient().isAuthenticatedConnection()) { - builder.withCredentials(AWSCredentialsConfigurator.toAWSCredentialsProvider(session.getClient().getProviderCredentials())); - } + .withClientConfiguration(configuration) + .withCredentials(S3CredentialsStrategy.toCredentialsProvider(session.getAuthentication().get())); final Location.Name region = this.getRegion(container); if(S3Session.isAwsHostname(session.getHost().getHostname(), false)) { if(Location.unknown.equals(region)) { @@ -123,7 +120,7 @@ public class Glacier implements Restore { } else { builder.withEndpointConfiguration(new AwsClientBuilder.EndpointConfiguration( - new HostUrlProvider(false).get(session.getHost()), region.getIdentifier())); + new HostUrlProvider(false).get(session.getHost()), region.getIdentifier())); } return builder.build(); } diff --git a/s3/src/main/java/ch/cyberduck/core/s3/RequestEntityRestStorageService.java b/s3/src/main/java/ch/cyberduck/core/s3/RequestEntityRestStorageService.java index 7c00e15db3..fb8bbcdc38 100644 --- a/s3/src/main/java/ch/cyberduck/core/s3/RequestEntityRestStorageService.java +++ b/s3/src/main/java/ch/cyberduck/core/s3/RequestEntityRestStorageService.java @@ -104,6 +104,11 @@ public class RequestEntityRestStorageService extends RestS3Service { this.setHttpClient(configuration.build()); } + @Override + public boolean isAuthenticatedConnection() { + return true; + } + public Jets3tProperties getConfiguration() { return properties; } @@ -215,8 +220,9 @@ public class RequestEntityRestStorageService extends RestS3Service { log.debug("Set hostname to {}", hostname); final String virtualPath; // Allow for non-standard virtual directory paths on the server-side - if(StringUtils.isNotBlank(host.getProtocol().getContext()) && !Scheme.isURL(host.getProtocol().getContext())) { - virtualPath = PathNormalizer.normalize(host.getProtocol().getContext()); + final String context = host.getProtocol().getContext(); + if(StringUtils.isNotBlank(context) && !Scheme.isURL(context)) { + virtualPath = PathNormalizer.normalize(context); } else { virtualPath = StringUtils.EMPTY; diff --git a/s3/src/main/java/ch/cyberduck/core/s3/S3AWS2SignatureRequestInterceptor.java b/s3/src/main/java/ch/cyberduck/core/s3/S3AWS2SignatureRequestInterceptor.java index 8bcd21843a..bd1354f331 100644 --- a/s3/src/main/java/ch/cyberduck/core/s3/S3AWS2SignatureRequestInterceptor.java +++ b/s3/src/main/java/ch/cyberduck/core/s3/S3AWS2SignatureRequestInterceptor.java @@ -15,6 +15,8 @@ package ch.cyberduck.core.s3; * GNU General Public License for more details. */ +import ch.cyberduck.core.Credentials; +import ch.cyberduck.core.exception.BackgroundException; import ch.cyberduck.core.preferences.HostPreferencesFactory; import org.apache.commons.lang3.StringUtils; @@ -25,7 +27,6 @@ import org.apache.http.HttpRequestInterceptor; import org.apache.http.protocol.HttpContext; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; -import org.jets3t.service.security.ProviderCredentials; import org.jets3t.service.utils.RestUtils; import org.jets3t.service.utils.ServiceUtils; @@ -38,8 +39,10 @@ import java.util.Map; import java.util.function.Predicate; import java.util.stream.Collectors; +import static com.amazonaws.services.s3.Headers.SECURITY_TOKEN; + public class S3AWS2SignatureRequestInterceptor implements HttpRequestInterceptor { - private static final Logger log = LogManager.getLogger(S3Session.class); + private static final Logger log = LogManager.getLogger(S3AWS2SignatureRequestInterceptor.class); private final S3Session session; @@ -49,59 +52,68 @@ public class S3AWS2SignatureRequestInterceptor implements HttpRequestInterceptor @Override public void process(final HttpRequest request, final HttpContext context) throws IOException { - if(!session.getClient().isAuthenticatedConnection()) { - log.warn("Skip authentication request {}", request); - return; - } - final ProviderCredentials credentials = session.getClient().getProviderCredentials(); - final String bucketName; - if(context.getAttribute("bucket") == null) { - log.warn("No bucket name in context {}", context); - bucketName = StringUtils.EMPTY; - } - else { - bucketName = context.getAttribute("bucket").toString(); - } - log.debug("Use bucket name {} from context", bucketName); - final URI uri; try { - uri = new URI(request.getRequestLine().getUri()); + final Credentials credentials = session.getAuthentication().get(); + if(credentials.isAnonymousLogin()) { + log.warn("Skip authentication request {}", request); + return; + } + if(StringUtils.isNotBlank(credentials.getToken())) { + request.setHeader(SECURITY_TOKEN, credentials.getToken()); + } + final String bucketName; + if(context.getAttribute("bucket") == null) { + log.warn("No bucket name in context {}", context); + bucketName = StringUtils.EMPTY; + } + else { + bucketName = context.getAttribute("bucket").toString(); + } + log.debug("Use bucket name {} from context", bucketName); + final URI uri; + try { + uri = new URI(request.getRequestLine().getUri()); + } + catch(URISyntaxException e) { + throw new IOException(e); + } + String path = uri.getRawPath(); + // If the request specifies a bucket using the HTTP Host header (virtual hosted-style), append + // the bucket name preceded by a "/" + if(!StringUtils.startsWith(path, String.format("/%s", bucketName))) { + path = String.format("/%s%s", bucketName, path); + } + final String queryString = uri.getRawQuery(); + if(StringUtils.isNotBlank(queryString)) { + path += String.format("?%s", queryString); + } + // Generate a canonical string representing the operation. + final String canonicalString = RestUtils.makeServiceCanonicalString( + request.getRequestLine().getMethod(), + path, + this.getHeaders(request), + null, + session.getRestHeaderPrefix(), + session.getClient().getResourceParameterNames()); + // Sign the canonical string. + final String signedCanonical = ServiceUtils.signWithHmacSha1( + credentials.getTokens().getSecretAccessKey(), canonicalString); + // Add encoded authorization to connection as HTTP Authorization header. + final String authorizationString = session.getSignatureIdentifier() + " " + + credentials.getTokens().getAccessKeyId() + ":" + signedCanonical; + request.setHeader(HttpHeaders.AUTHORIZATION, authorizationString); } - catch(URISyntaxException e) { + catch(BackgroundException e) { + log.warn("Error {} retrieving credentials", e.toString()); throw new IOException(e); } - String path = uri.getRawPath(); - // If the request specifies a bucket using the HTTP Host header (virtual hosted-style), append - // the bucket name preceded by a "/" - if(!StringUtils.startsWith(path, String.format("/%s", bucketName))) { - path = String.format("/%s%s", bucketName, path); - } - final String queryString = uri.getRawQuery(); - if(StringUtils.isNotBlank(queryString)) { - path += String.format("?%s", queryString); - } - // Generate a canonical string representing the operation. - final String canonicalString = RestUtils.makeServiceCanonicalString( - request.getRequestLine().getMethod(), - path, - this.getHeaders(request), - null, - session.getRestHeaderPrefix(), - session.getClient().getResourceParameterNames()); - // Sign the canonical string. - final String signedCanonical = ServiceUtils.signWithHmacSha1( - credentials.getSecretKey(), canonicalString); - // Add encoded authorization to connection as HTTP Authorization header. - final String authorizationString = session.getSignatureIdentifier() + " " - + credentials.getAccessKey() + ":" + signedCanonical; - request.setHeader(HttpHeaders.AUTHORIZATION, authorizationString); } final class HttpHeaderFilter implements Predicate
{ @Override public boolean test(final Header header) { - return !HostPreferencesFactory.get(session.getHost()).getList("s3.signature.headers.exclude").stream() - .filter(s -> StringUtils.equalsIgnoreCase(s, header.getName())).findAny().isPresent(); + return HostPreferencesFactory.get(session.getHost()).getList("s3.signature.headers.exclude").stream() + .noneMatch(s -> StringUtils.equalsIgnoreCase(s, header.getName())); } } diff --git a/s3/src/main/java/ch/cyberduck/core/s3/S3AWS4SignatureRequestInterceptor.java b/s3/src/main/java/ch/cyberduck/core/s3/S3AWS4SignatureRequestInterceptor.java index 95042bfd7a..a02d2eebba 100644 --- a/s3/src/main/java/ch/cyberduck/core/s3/S3AWS4SignatureRequestInterceptor.java +++ b/s3/src/main/java/ch/cyberduck/core/s3/S3AWS4SignatureRequestInterceptor.java @@ -15,6 +15,8 @@ package ch.cyberduck.core.s3; * GNU General Public License for more details. */ +import ch.cyberduck.core.Credentials; +import ch.cyberduck.core.exception.BackgroundException; import ch.cyberduck.core.preferences.HostPreferencesFactory; import org.apache.commons.lang3.StringUtils; @@ -28,7 +30,6 @@ import org.apache.http.protocol.HttpContext; import org.apache.http.protocol.HttpCoreContext; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; -import org.jets3t.service.security.ProviderCredentials; import org.jets3t.service.utils.ServiceUtils; import org.jets3t.service.utils.SignatureUtils; @@ -44,9 +45,10 @@ import java.util.stream.Collectors; import com.amazonaws.auth.internal.SignerConstants; import static com.amazonaws.services.s3.Headers.S3_ALTERNATE_DATE; +import static com.amazonaws.services.s3.Headers.SECURITY_TOKEN; public class S3AWS4SignatureRequestInterceptor implements HttpRequestInterceptor { - private static final Logger log = LogManager.getLogger(S3Session.class); + private static final Logger log = LogManager.getLogger(S3AWS4SignatureRequestInterceptor.class); private final S3Session session; @@ -56,86 +58,95 @@ public class S3AWS4SignatureRequestInterceptor implements HttpRequestInterceptor @Override public void process(final HttpRequest request, final HttpContext context) throws IOException { - if(!session.getClient().isAuthenticatedConnection()) { - log.warn("Skip authentication request {}", request); - return; - } - final ProviderCredentials credentials = session.getClient().getProviderCredentials(); - final String bucketName; - if(context.getAttribute("bucket") == null) { - log.warn("No bucket name in context {}", context); - bucketName = StringUtils.EMPTY; - } - else { - bucketName = context.getAttribute("bucket").toString(); - } - log.debug("Use bucket name {} from context", bucketName); - final URI uri; try { - uri = new URI(request.getRequestLine().getUri()); + final Credentials credentials = session.getAuthentication().get(); + if(credentials.isAnonymousLogin()) { + log.warn("Skip authentication request {}", request); + return; + } + if(StringUtils.isNotBlank(credentials.getToken())) { + request.setHeader(SECURITY_TOKEN, credentials.getToken()); + } + final String bucketName; + if(context.getAttribute("bucket") == null) { + log.warn("No bucket name in context {}", context); + bucketName = StringUtils.EMPTY; + } + else { + bucketName = context.getAttribute("bucket").toString(); + } + log.debug("Use bucket name {} from context", bucketName); + final URI uri; + try { + uri = new URI(request.getRequestLine().getUri()); + } + catch(URISyntaxException e) { + throw new IOException(e); + } + String region = session.getClient().getRegionEndpointCache().getRegionForBucketName(bucketName); + if(null == region) { + final HttpHost host = (HttpHost) context.getAttribute(HttpCoreContext.HTTP_TARGET_HOST); + if(host != null) { + try { + region = SignatureUtils.awsRegionForRequest(new URI(host.toURI())); + log.debug("Determined region {} from URI {}", region, host.toURI()); + } + catch(URISyntaxException e) { + throw new IOException(e); + } + } + if(region != null) { + log.debug("Cache region {} for bucket {}", region, bucketName); + session.getClient().getRegionEndpointCache().putRegionForBucketName(bucketName, region); + } + } + if(null == region) { + region = session.getHost().getRegion(); + log.debug("Determined region {} from {}", region, session.getHost()); + } + if(null == region) { + region = HostPreferencesFactory.get(session.getHost()).getProperty("s3.location"); + log.debug("Determined region {} from defaults", region); + } + final HttpUriRequest message = (HttpUriRequest) request; + String requestPayloadHexSHA256Hash = + SignatureUtils.awsV4GetOrCalculatePayloadHash(message); + message.setHeader(SignerConstants.X_AMZ_CONTENT_SHA256, requestPayloadHexSHA256Hash); + // Generate AWS-flavoured ISO8601 timestamp string + final String timestampISO8601 = message.getFirstHeader(S3_ALTERNATE_DATE).getValue(); + // Canonical request string + final String canonicalRequestString = + SignatureUtils.awsV4BuildCanonicalRequestString(uri, + request.getRequestLine().getMethod(), this.getHeaders(request), requestPayloadHexSHA256Hash); + // String to sign + final String stringToSign = SignatureUtils.awsV4BuildStringToSign( + session.getSignatureVersion().toString(), canonicalRequestString, + timestampISO8601, region); + // Signing key + final byte[] signingKey = SignatureUtils.awsV4BuildSigningKey( + credentials.getTokens().getSecretAccessKey(), timestampISO8601, region); + // Request signature + final String signature = ServiceUtils.toHex(ServiceUtils.hmacSHA256( + signingKey, ServiceUtils.stringToBytes(stringToSign))); + // Authorization header value + final String authorizationHeaderValue = + SignatureUtils.awsV4BuildAuthorizationHeaderValue( + credentials.getTokens().getAccessKeyId(), signature, + session.getSignatureVersion().toString(), canonicalRequestString, + timestampISO8601, region); + message.setHeader(HttpHeaders.AUTHORIZATION, authorizationHeaderValue); } - catch(URISyntaxException e) { + catch(BackgroundException e) { + log.warn("Error {} retrieving credentials", e.toString()); throw new IOException(e); } - String region = session.getClient().getRegionEndpointCache().getRegionForBucketName(bucketName); - if(null == region) { - final HttpHost host = (HttpHost) context.getAttribute(HttpCoreContext.HTTP_TARGET_HOST); - if(host != null) { - try { - region = SignatureUtils.awsRegionForRequest(new URI(host.toURI())); - log.debug("Determined region {} from URI {}", region, host.toURI()); - } - catch(URISyntaxException e) { - throw new IOException(e); - } - } - if(region != null) { - log.debug("Cache region {} for bucket {}", region, bucketName); - session.getClient().getRegionEndpointCache().putRegionForBucketName(bucketName, region); - } - } - if(null == region) { - region = session.getHost().getRegion(); - log.debug("Determined region {} from {}", region, session.getHost()); - } - if(null == region) { - region = HostPreferencesFactory.get(session.getHost()).getProperty("s3.location"); - log.debug("Determined region {} from defaults", region); - } - final HttpUriRequest message = (HttpUriRequest) request; - String requestPayloadHexSHA256Hash = - SignatureUtils.awsV4GetOrCalculatePayloadHash(message); - message.setHeader(SignerConstants.X_AMZ_CONTENT_SHA256, requestPayloadHexSHA256Hash); - // Generate AWS-flavoured ISO8601 timestamp string - final String timestampISO8601 = message.getFirstHeader(S3_ALTERNATE_DATE).getValue(); - // Canonical request string - final String canonicalRequestString = - SignatureUtils.awsV4BuildCanonicalRequestString(uri, - request.getRequestLine().getMethod(), this.getHeaders(request), requestPayloadHexSHA256Hash); - // String to sign - final String stringToSign = SignatureUtils.awsV4BuildStringToSign( - session.getSignatureVersion().toString(), canonicalRequestString, - timestampISO8601, region); - // Signing key - final byte[] signingKey = SignatureUtils.awsV4BuildSigningKey( - credentials.getSecretKey(), timestampISO8601, region); - // Request signature - final String signature = ServiceUtils.toHex(ServiceUtils.hmacSHA256( - signingKey, ServiceUtils.stringToBytes(stringToSign))); - // Authorization header value - final String authorizationHeaderValue = - SignatureUtils.awsV4BuildAuthorizationHeaderValue( - credentials.getAccessKey(), signature, - session.getSignatureVersion().toString(), canonicalRequestString, - timestampISO8601, region); - message.setHeader(HttpHeaders.AUTHORIZATION, authorizationHeaderValue); } final class HttpHeaderFilter implements Predicate
{ @Override public boolean test(final Header header) { - return !HostPreferencesFactory.get(session.getHost()).getList("s3.signature.headers.exclude").stream() - .filter(s -> StringUtils.equalsIgnoreCase(s, header.getName())).findAny().isPresent(); + return HostPreferencesFactory.get(session.getHost()).getList("s3.signature.headers.exclude").stream() + .noneMatch(s -> StringUtils.equalsIgnoreCase(s, header.getName())); } } diff --git a/s3/src/main/java/ch/cyberduck/core/s3/S3AbstractListService.java b/s3/src/main/java/ch/cyberduck/core/s3/S3AbstractListService.java index c4ce8854cc..c785aa3837 100644 --- a/s3/src/main/java/ch/cyberduck/core/s3/S3AbstractListService.java +++ b/s3/src/main/java/ch/cyberduck/core/s3/S3AbstractListService.java @@ -26,7 +26,7 @@ public abstract class S3AbstractListService implements ListService { private final PathContainerService containerService; public S3AbstractListService(final S3Session session) { - this.containerService = session.getFeature(PathContainerService.class); + this.containerService = new S3PathContainerService(session.getHost()); } protected String createPrefix(final Path directory) { diff --git a/s3/src/main/java/ch/cyberduck/core/s3/S3AccessControlListFeature.java b/s3/src/main/java/ch/cyberduck/core/s3/S3AccessControlListFeature.java index f7da95f646..14c4493381 100644 --- a/s3/src/main/java/ch/cyberduck/core/s3/S3AccessControlListFeature.java +++ b/s3/src/main/java/ch/cyberduck/core/s3/S3AccessControlListFeature.java @@ -70,7 +70,7 @@ public class S3AccessControlListFeature implements AclPermission { public S3AccessControlListFeature(final S3Session session) { this.session = session; - this.containerService = session.getFeature(PathContainerService.class); + this.containerService = new S3PathContainerService(session.getHost()); } private boolean isBucketOwnerEnforced(final Path bucket) throws BackgroundException { @@ -127,10 +127,10 @@ public class S3AccessControlListFeature implements AclPermission { // bucket's existing ACL already allows write access by the anonymous user. // In general, you can only access the ACL of a bucket if the ACL already in place // for that bucket (in S3) allows you to do so. - acl = this.toAcl(session.getClient().getBucketAcl(bucket.isRoot() ? StringUtils.EMPTY : bucket.getName())); + acl = S3AccessControlListFeature.toAcl(session.getClient().getBucketAcl(bucket.isRoot() ? StringUtils.EMPTY : bucket.getName())); } else { - acl = this.toAcl(session.getClient().getVersionedObjectAcl(file.attributes().getVersionId(), + acl = S3AccessControlListFeature.toAcl(session.getClient().getVersionedObjectAcl(file.attributes().getVersionId(), bucket.isRoot() ? StringUtils.EMPTY : bucket.getName(), containerService.getKey(file))); } if(this.isBucketOwnerEnforced(bucket)) { @@ -158,7 +158,7 @@ public class S3AccessControlListFeature implements AclPermission { public void setPermission(final Path file, final TransferStatus status) throws BackgroundException { try { // Read owner from bucket - final AccessControlList list = this.toAcl(status.getAcl()); + final AccessControlList list = toAcl(status.getAcl()); final Path bucket = containerService.getContainer(file); if(containerService.isContainer(file)) { session.getClient().putBucketAcl(bucket.isRoot() ? StringUtils.EMPTY : bucket.getName(), list); @@ -185,7 +185,7 @@ public class S3AccessControlListFeature implements AclPermission { * @param acl Edited ACL * @return ACL to write to server */ - protected AccessControlList toAcl(final Acl acl) { + protected static AccessControlList toAcl(final Acl acl) { if(Acl.EMPTY.equals(acl)) { return null; } @@ -255,7 +255,7 @@ public class S3AccessControlListFeature implements AclPermission { * @param list ACL from server * @return Editable ACL */ - protected Acl toAcl(final AccessControlList list) { + protected static Acl toAcl(final AccessControlList list) { if(null == list) { return Acl.EMPTY; } @@ -329,7 +329,7 @@ public class S3AccessControlListFeature implements AclPermission { } @Override - public List getAvailableAclUsers() { + public List getAvailableAclUsers(final List files) { return new ArrayList<>(Arrays.asList( new Acl.CanonicalUser(), new Acl.GroupUser(Acl.GroupUser.AUTHENTICATED, false) { diff --git a/s3/src/main/java/ch/cyberduck/core/s3/S3AttributesAdapter.java b/s3/src/main/java/ch/cyberduck/core/s3/S3AttributesAdapter.java index de4f690841..831a94a4a9 100644 --- a/s3/src/main/java/ch/cyberduck/core/s3/S3AttributesAdapter.java +++ b/s3/src/main/java/ch/cyberduck/core/s3/S3AttributesAdapter.java @@ -15,6 +15,7 @@ package ch.cyberduck.core.s3; * GNU General Public License for more details. */ +import ch.cyberduck.core.DefaultPathAttributes; import ch.cyberduck.core.Host; import ch.cyberduck.core.PathAttributes; import ch.cyberduck.core.features.AttributesAdapter; @@ -44,7 +45,7 @@ public class S3AttributesAdapter implements AttributesAdapter { @Override public PathAttributes toAttributes(final StorageObject object) { - final PathAttributes attributes = new PathAttributes(); + final PathAttributes attributes = new DefaultPathAttributes(); attributes.setSize(object.getContentLength()); final Date lastmodified = object.getLastModifiedDate(); if(lastmodified != null) { diff --git a/s3/src/main/java/ch/cyberduck/core/s3/S3AttributesFinderFeature.java b/s3/src/main/java/ch/cyberduck/core/s3/S3AttributesFinderFeature.java index dac7fae519..8e0599ae8a 100644 --- a/s3/src/main/java/ch/cyberduck/core/s3/S3AttributesFinderFeature.java +++ b/s3/src/main/java/ch/cyberduck/core/s3/S3AttributesFinderFeature.java @@ -19,6 +19,7 @@ package ch.cyberduck.core.s3; */ import ch.cyberduck.core.CancellingListProgressListener; +import ch.cyberduck.core.DefaultPathAttributes; import ch.cyberduck.core.ListProgressListener; import ch.cyberduck.core.Path; import ch.cyberduck.core.PathAttributes; @@ -36,9 +37,6 @@ import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; import org.jets3t.service.ServiceException; -import java.util.Collections; - -import static ch.cyberduck.core.s3.S3VersionedObjectListService.KEY_DELETE_MARKER; public class S3AttributesFinderFeature implements AttributesFinder { private static final Logger log = LogManager.getLogger(S3AttributesFinderFeature.class); @@ -49,7 +47,7 @@ public class S3AttributesFinderFeature implements AttributesFinder { public S3AttributesFinderFeature(final S3Session session, final S3AccessControlListFeature acl) { this.session = session; - this.containerService = session.getFeature(PathContainerService.class); + this.containerService = new S3PathContainerService(session.getHost()); this.acl = acl; } @@ -59,15 +57,15 @@ public class S3AttributesFinderFeature implements AttributesFinder { return PathAttributes.EMPTY; } if(containerService.isContainer(file)) { - final PathAttributes attributes = new PathAttributes(); + final PathAttributes attributes = new DefaultPathAttributes(); log.debug("Read location for bucket {}", file); attributes.setRegion(new S3LocationFeature(session, session.getClient().getRegionEndpointCache()).getLocation(file).getIdentifier()); return attributes; } if(file.getType().contains(Path.Type.upload)) { - final Write.Append append = new S3MultipartUploadService(session, new S3WriteFeature(session, acl), acl).append(file, new TransferStatus()); + final Write.Append append = new S3MultipartUploadService(session, acl).append(file, new TransferStatus()); if(append.append) { - return new PathAttributes().setSize(append.offset); + return new DefaultPathAttributes().setSize(append.offset); } throw new NotfoundException(file.getAbsolute()); } @@ -83,9 +81,8 @@ public class S3AttributesFinderFeature implements AttributesFinder { case 405: log.debug("Mark file {} as delete marker", file); // Only DELETE method is allowed for delete markers - attr = new PathAttributes(); - attr.setCustom(Collections.singletonMap(KEY_DELETE_MARKER, Boolean.TRUE.toString())); - attr.setDuplicate(true); + attr = new DefaultPathAttributes(); + attr.setTrashed(true); return attr; } throw new S3ExceptionMappingService().map("Failure to read attributes of {0}", e, file); @@ -133,9 +130,9 @@ public class S3AttributesFinderFeature implements AttributesFinder { } else { if(HostPreferencesFactory.get(session.getHost()).getBoolean("s3.upload.multipart.lookup")) { - final Write.Append append = new S3MultipartUploadService(session, new S3WriteFeature(session, acl), acl).append(file, new TransferStatus()); + final Write.Append append = new S3MultipartUploadService(session, acl).append(file, new TransferStatus()); if(append.append) { - return new PathAttributes().setSize(append.offset); + return new DefaultPathAttributes().setSize(append.offset); } } } diff --git a/s3/src/main/java/ch/cyberduck/core/s3/S3AuthenticationResponseInterceptor.java b/s3/src/main/java/ch/cyberduck/core/s3/S3AuthenticationResponseInterceptor.java index 406162d6e5..59f195e3cf 100644 --- a/s3/src/main/java/ch/cyberduck/core/s3/S3AuthenticationResponseInterceptor.java +++ b/s3/src/main/java/ch/cyberduck/core/s3/S3AuthenticationResponseInterceptor.java @@ -25,8 +25,6 @@ import org.apache.http.HttpStatus; import org.apache.http.protocol.HttpContext; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; -import org.jets3t.service.security.AWSCredentials; -import org.jets3t.service.security.AWSSessionCredentials; import java.io.IOException; @@ -36,11 +34,9 @@ import java.io.IOException; public class S3AuthenticationResponseInterceptor extends DisabledServiceUnavailableRetryStrategy implements S3CredentialsStrategy { private static final Logger log = LogManager.getLogger(S3AuthenticationResponseInterceptor.class); - private final S3Session session; private final S3CredentialsStrategy authenticator; - public S3AuthenticationResponseInterceptor(final S3Session session, final S3CredentialsStrategy authenticator) { - this.session = session; + public S3AuthenticationResponseInterceptor(final S3CredentialsStrategy authenticator) { this.authenticator = authenticator; } @@ -64,15 +60,6 @@ public class S3AuthenticationResponseInterceptor extends DisabledServiceUnavaila log.warn("Handle failure {} from response {}", failure, response); final Credentials credentials = authenticator.get(); log.debug("Reconfigure client with credentials {}", credentials); - if(credentials.getTokens().validate()) { - session.getClient().setProviderCredentials(new AWSSessionCredentials( - credentials.getTokens().getAccessKeyId(), credentials.getTokens().getSecretAccessKey(), - credentials.getTokens().getSessionToken())); - } - else { - session.getClient().setProviderCredentials(new AWSCredentials( - credentials.getUsername(), credentials.getPassword())); - } return true; } } diff --git a/s3/src/main/java/ch/cyberduck/core/s3/S3BucketCreateService.java b/s3/src/main/java/ch/cyberduck/core/s3/S3BucketCreateService.java index f66b6319a6..8921904fe0 100644 --- a/s3/src/main/java/ch/cyberduck/core/s3/S3BucketCreateService.java +++ b/s3/src/main/java/ch/cyberduck/core/s3/S3BucketCreateService.java @@ -40,7 +40,7 @@ public class S3BucketCreateService { public S3BucketCreateService(final S3Session session) { this.session = session; - this.containerService = session.getFeature(PathContainerService.class); + this.containerService = new S3PathContainerService(session.getHost()); } public void create(final Path bucket, final String region) throws BackgroundException { diff --git a/s3/src/main/java/ch/cyberduck/core/s3/S3BucketListService.java b/s3/src/main/java/ch/cyberduck/core/s3/S3BucketListService.java index bff3a9249b..ac75201f3f 100644 --- a/s3/src/main/java/ch/cyberduck/core/s3/S3BucketListService.java +++ b/s3/src/main/java/ch/cyberduck/core/s3/S3BucketListService.java @@ -19,6 +19,7 @@ package ch.cyberduck.core.s3; */ 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; @@ -55,7 +56,7 @@ public class S3BucketListService implements RootListService { final AttributedList buckets = new AttributedList<>(); // List all buckets owned for(StorageBucket b : session.getClient().listAllBuckets()) { - final PathAttributes attr = new PathAttributes(); + final PathAttributes attr = new DefaultPathAttributes(); final Path bucket = new Path(PathNormalizer.normalize(b.getName()), EnumSet.of(Path.Type.volume, Path.Type.directory), attr); if(b.getOwner() != null) { // Null if the owner is not available diff --git a/s3/src/main/java/ch/cyberduck/core/s3/S3BulkTransferAccelerationFeature.java b/s3/src/main/java/ch/cyberduck/core/s3/S3BulkTransferAccelerationFeature.java index c982f669bf..df640344d7 100644 --- a/s3/src/main/java/ch/cyberduck/core/s3/S3BulkTransferAccelerationFeature.java +++ b/s3/src/main/java/ch/cyberduck/core/s3/S3BulkTransferAccelerationFeature.java @@ -23,7 +23,6 @@ import ch.cyberduck.core.exception.BackgroundException; import ch.cyberduck.core.exception.InteroperabilityException; import ch.cyberduck.core.exception.NotfoundException; import ch.cyberduck.core.features.Bulk; -import ch.cyberduck.core.features.Delete; import ch.cyberduck.core.features.TransferAcceleration; import ch.cyberduck.core.preferences.HostPreferencesFactory; import ch.cyberduck.core.transfer.Transfer; @@ -44,14 +43,10 @@ public class S3BulkTransferAccelerationFeature implements Bulk { private final TransferAcceleration accelerationService; private final PathContainerService containerService; - public S3BulkTransferAccelerationFeature(final S3Session session) { - this(session, session.getFeature(TransferAcceleration.class)); - } - public S3BulkTransferAccelerationFeature(final S3Session session, final TransferAcceleration accelerationService) { this.session = session; this.accelerationService = accelerationService; - this.containerService = session.getFeature(PathContainerService.class); + this.containerService = new S3PathContainerService(session.getHost()); } @Override @@ -65,11 +60,6 @@ public class S3BulkTransferAccelerationFeature implements Bulk { this.configure(files, callback, false); } - @Override - public Bulk withDelete(final Delete delete) { - return this; - } - private void configure(final Map files, final ConnectionCallback callback, final boolean enabled) throws BackgroundException { final Set buckets = new HashSet<>(); for(TransferItem file : files.keySet()) { diff --git a/s3/src/main/java/ch/cyberduck/core/s3/S3CopyFeature.java b/s3/src/main/java/ch/cyberduck/core/s3/S3CopyFeature.java index 53489cce30..f6b91f4a27 100644 --- a/s3/src/main/java/ch/cyberduck/core/s3/S3CopyFeature.java +++ b/s3/src/main/java/ch/cyberduck/core/s3/S3CopyFeature.java @@ -19,6 +19,7 @@ package ch.cyberduck.core.s3; import ch.cyberduck.core.Acl; import ch.cyberduck.core.ConnectionCallback; +import ch.cyberduck.core.DefaultPathAttributes; import ch.cyberduck.core.LocaleFactory; import ch.cyberduck.core.Path; import ch.cyberduck.core.PathAttributes; @@ -38,6 +39,7 @@ import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; import org.jets3t.service.Constants; import org.jets3t.service.ServiceException; +import org.jets3t.service.model.BaseStorageItem; import org.jets3t.service.model.S3Object; import java.text.MessageFormat; @@ -55,7 +57,7 @@ public class S3CopyFeature implements Copy { public S3CopyFeature(final S3Session session, final S3AccessControlListFeature acl) { this.session = session; this.acl = acl; - this.containerService = session.getFeature(PathContainerService.class); + this.containerService = new S3PathContainerService(session.getHost()); } @Override @@ -78,19 +80,21 @@ public class S3CopyFeature implements Copy { } catch(AccessDeniedException | InteroperabilityException e) { log.warn("Ignore failure {}", e.getMessage()); + status.setAcl(acl.getDefault(target)); } } final S3Object destination = new S3WriteFeature(session, acl).getDetails(target, status); - destination.setAcl(acl.toAcl(status.getAcl())); + destination.setAcl(S3AccessControlListFeature.toAcl(status.getAcl())); final Path bucket = containerService.getContainer(target); destination.setBucketName(bucket.isRoot() ? StringUtils.EMPTY : bucket.getName()); destination.replaceAllMetadata(new HashMap<>(new S3MetadataFeature(session, acl).getMetadata(source))); - final String versionId = this.copy(source, destination, status, listener); - return new Path(target).withAttributes(new PathAttributes(source.attributes()).setVersionId( - HostPreferencesFactory.get(session.getHost()).getBoolean("s3.listing.versioning.enable") ? versionId : null)); + final CopyResult result = this.copy(source, destination, status, listener); + return new Path(target).withAttributes(new DefaultPathAttributes(source.attributes()) + .setVersionId(HostPreferencesFactory.get(session.getHost()).getBoolean("s3.listing.versioning.enable") ? result.versionId : null) + .setETag(StringUtils.remove(result.etag, '"'))); } - protected String copy(final Path source, final S3Object destination, final TransferStatus status, final StreamListener listener) throws BackgroundException { + protected CopyResult copy(final Path source, final S3Object destination, final TransferStatus status, final StreamListener listener) throws BackgroundException { try { // Copying object applying the metadata of the original final Path bucket = containerService.getContainer(source); @@ -100,7 +104,10 @@ public class S3CopyFeature implements Copy { destination.getBucketName(), destination, false); listener.sent(status.getLength()); final Map complete = (Map) stringObjectMap.get(Constants.KEY_FOR_COMPLETE_METADATA); - return (String) complete.get(Constants.AMZ_VERSION_ID); + return new CopyResult( + (String) complete.get(Constants.AMZ_VERSION_ID), + (String) complete.get(BaseStorageItem.METADATA_HEADER_ETAG) + ); } catch(ServiceException e) { throw new S3ExceptionMappingService().map("Cannot copy {0}", e, source); @@ -118,4 +125,14 @@ public class S3CopyFeature implements Copy { } } } + + protected static final class CopyResult { + public final String versionId; + public final String etag; + + public CopyResult(final String versionId, final String length) { + this.versionId = versionId; + this.etag = length; + } + } } diff --git a/s3/src/main/java/ch/cyberduck/core/s3/S3CredentialsConfigurator.java b/s3/src/main/java/ch/cyberduck/core/s3/S3CredentialsConfigurator.java index 21004e2f5e..50164835e4 100644 --- a/s3/src/main/java/ch/cyberduck/core/s3/S3CredentialsConfigurator.java +++ b/s3/src/main/java/ch/cyberduck/core/s3/S3CredentialsConfigurator.java @@ -15,22 +15,15 @@ package ch.cyberduck.core.s3; * GNU General Public License for more details. */ -import ch.cyberduck.core.AsciiRandomStringService; import ch.cyberduck.core.Credentials; import ch.cyberduck.core.CredentialsConfigurator; import ch.cyberduck.core.Host; import ch.cyberduck.core.Local; import ch.cyberduck.core.LocalFactory; -import ch.cyberduck.core.LocaleFactory; -import ch.cyberduck.core.LoginOptions; -import ch.cyberduck.core.PasswordCallback; +import ch.cyberduck.core.Profile; import ch.cyberduck.core.TemporaryAccessTokens; -import ch.cyberduck.core.aws.CustomClientConfiguration; import ch.cyberduck.core.exception.AccessDeniedException; import ch.cyberduck.core.exception.LoginCanceledException; -import ch.cyberduck.core.ssl.ThreadLocalHostnameDelegatingTrustManager; -import ch.cyberduck.core.ssl.X509KeyManager; -import ch.cyberduck.core.ssl.X509TrustManager; import org.apache.commons.lang3.StringUtils; import org.apache.logging.log4j.LogManager; @@ -43,25 +36,11 @@ import java.time.Instant; import java.util.HashMap; import java.util.LinkedHashMap; import java.util.Map; -import java.util.Optional; import java.util.Scanner; -import java.util.function.Predicate; -import com.amazonaws.ClientConfiguration; -import com.amazonaws.auth.AWSCredentials; -import com.amazonaws.auth.AWSSessionCredentials; -import com.amazonaws.auth.AWSStaticCredentialsProvider; import com.amazonaws.auth.profile.internal.AbstractProfilesConfigFileScanner; import com.amazonaws.auth.profile.internal.AllProfiles; import com.amazonaws.auth.profile.internal.BasicProfile; -import com.amazonaws.regions.Regions; -import com.amazonaws.services.securitytoken.AWSSecurityTokenService; -import com.amazonaws.services.securitytoken.AWSSecurityTokenServiceClientBuilder; -import com.amazonaws.services.securitytoken.model.AWSSecurityTokenServiceException; -import com.amazonaws.services.securitytoken.model.AssumeRoleRequest; -import com.amazonaws.services.securitytoken.model.AssumeRoleResult; -import com.amazonaws.services.securitytoken.model.GetSessionTokenRequest; -import com.amazonaws.services.securitytoken.model.GetSessionTokenResult; import com.fasterxml.jackson.annotation.JsonAutoDetect.Visibility; import com.fasterxml.jackson.annotation.JsonInclude.Include; import com.fasterxml.jackson.annotation.JsonProperty; @@ -82,192 +61,92 @@ public class S3CredentialsConfigurator implements CredentialsConfigurator { private static final Logger log = LogManager.getLogger(S3CredentialsConfigurator.class); private final Local directory; - private final X509TrustManager trust; - private final X509KeyManager key; - private final PasswordCallback prompt; + + /** + * Profiles by name + */ private final Map profiles = new LinkedHashMap<>(); - public S3CredentialsConfigurator(final X509TrustManager trust, final X509KeyManager key, final PasswordCallback prompt) { - this(LocalFactory.get(LocalFactory.get(), ".aws"), trust, key, prompt); + public S3CredentialsConfigurator() { + this(LocalFactory.get(LocalFactory.get(), ".aws")); } - public S3CredentialsConfigurator(final Local directory, final X509TrustManager trust, final X509KeyManager key, final PasswordCallback prompt) { + public S3CredentialsConfigurator(final Local directory) { this.directory = directory; - this.trust = trust; - this.key = key; - this.prompt = prompt; } @Override public Credentials configure(final Host host) { final Credentials credentials = new Credentials(host.getCredentials()); - final String profile = credentials.getUsername(); - final Optional> optional = profiles.entrySet().stream().filter(new Predicate>() { - @Override - public boolean test(final Map.Entry entry) { - final String profileName = entry.getKey(); - final BasicProfile basicProfile = entry.getValue(); - final String awsAccessIdKey = basicProfile.getAwsAccessIdKey(); - // Matching access key or profile name - if(StringUtils.equals(profileName, profile)) { - log.debug("Found matching profile {} for profile name {}", profile, profileName); - return true; - } - else if(StringUtils.equals(awsAccessIdKey, profile)) { - log.debug("Found matching profile {} for access key {}", profile, awsAccessIdKey); - return true; - } - return false; + if(credentials.isPasswordAuthentication()) { + return credentials; + } + final BasicProfile profile = profiles.entrySet().stream().filter(entry -> { + // Matching access key or profile name + if(StringUtils.equals(entry.getKey(), credentials.getUsername())) { + log.debug("Found matching profile {} for profile name {}", credentials.getUsername(), entry.getKey()); + return true; } - }).findFirst(); - if(optional.isPresent()) { - final Map.Entry entry = optional.get(); - final BasicProfile basicProfile = entry.getValue(); - final String tokenCode; - if(basicProfile.getProperties().containsKey("mfa_serial")) { - try { - tokenCode = prompt.prompt( - host, LocaleFactory.localizedString("Provide additional login credentials", "Credentials"), - String.format("%s %s", LocaleFactory.localizedString("Multi-Factor Authentication", "S3"), - basicProfile.getPropertyValue("mfa_serial")), - new LoginOptions(host.getProtocol()) - .password(true) - .passwordPlaceholder(LocaleFactory.localizedString("MFA Authentication Code", "S3")) - .keychain(false) - ).getPassword(); - } - catch(LoginCanceledException e) { - log.warn("Canceled MFA prompt for profile {}", basicProfile); + else if(StringUtils.equals(entry.getValue().getAwsAccessIdKey(), credentials.getUsername())) { + log.debug("Found matching profile {} for access key {}", credentials.getUsername(), entry.getValue().getAwsAccessIdKey()); + return true; + } + return false; + }).map(Map.Entry::getValue).findFirst().orElse(StringUtils.isBlank(host.getCredentials().getUsername()) ? profiles.get("default") : null); + if(null != profile) { + if(profile.isRoleBasedProfile()) { + log.debug("Configure credentials from role based profile {}", profile.getProfileName()); + if(StringUtils.isBlank(profile.getRoleSourceProfile())) { + log.warn("Missing source profile reference in profile {}", profile.getProfileName()); return credentials; } - } - else { - tokenCode = null; - } - final Integer durationSeconds; - if(basicProfile.getProperties().containsKey("duration_seconds")) { - durationSeconds = Integer.valueOf(basicProfile.getPropertyValue("duration_seconds")); - } - else { - durationSeconds = null; - } - if(basicProfile.isRoleBasedProfile()) { - log.debug("Configure credentials from role based profile {}", basicProfile.getProfileName()); - if(StringUtils.isBlank(basicProfile.getRoleSourceProfile())) { - log.warn("Missing source profile reference in profile {}", basicProfile.getProfileName()); - return credentials; - } - else if(!profiles.containsKey(basicProfile.getRoleSourceProfile())) { - log.warn("Missing source profile with name {}", basicProfile.getRoleSourceProfile()); + else if(!profiles.containsKey(profile.getRoleSourceProfile())) { + log.warn("Missing source profile with name {}", profile.getRoleSourceProfile()); return credentials; } else { - final BasicProfile sourceProfile = profiles.get(basicProfile.getRoleSourceProfile()); - final AWSSecurityTokenService service; + final BasicProfile sourceProfile = profiles.get(profile.getRoleSourceProfile()); if(sourceProfile.getProperties().containsKey("sso_start_url")) { + log.debug("Set credentials from cached AWS CLI cache for {}", sourceProfile.getProfileName()); // Read cached SSO credentials final CachedCredential cached = this.fetchSsoCredentials(sourceProfile.getProperties()); if(null == cached) { return credentials; } - service = this.getTokenService(host, host.getRegion(), - cached.accessKey, cached.secretKey, cached.sessionToken); + // No further token exchange required + return credentials.setTokens(new TemporaryAccessTokens( + cached.accessKey, cached.secretKey, cached.sessionToken, Instant.parse(cached.expiration).toEpochMilli())); } else { // If a profile defines the role_arn property then the profile is treated as an assume role profile - service = this.getTokenService(host, host.getRegion(), - sourceProfile.getAwsAccessIdKey(), - sourceProfile.getAwsSecretAccessKey(), - sourceProfile.getAwsSessionToken()); - } - // Starts a new session by sending a request to the AWS Security Token Service (STS) to assume a - // role using the long-lived AWS credentials - final AssumeRoleRequest assumeRoleRequest = new AssumeRoleRequest() - .withExternalId(basicProfile.getRoleExternalId()) - .withRoleArn(basicProfile.getRoleArn()) - // Specify this value if the IAM user has a policy that requires MFA authentication - .withSerialNumber(basicProfile.getPropertyValue("mfa_serial")) - // The value provided by the MFA device, if MFA is required - .withTokenCode(tokenCode - // mfa_serial - The identification number of the MFA device to use when assuming a role. This is an optional parameter. - // Specify this value if the trust policy of the role being assumed includes a condition that requires MFA authentication. - // The value is either the serial number for a hardware device (such as GAHT12345678) or an Amazon Resource Name (ARN) for - // a virtual device (such as arn:aws:iam::123456789012:mfa/user). - ) - .withRoleSessionName(basicProfile.getRoleSessionName() == null ? new AsciiRandomStringService().random() : basicProfile.getRoleSessionName()) - .withDurationSeconds(durationSeconds - // duration_seconds - Specifies the maximum duration of the role session, in seconds. The value can range from 900 seconds - // (15 minutes) up to the maximum session duration setting for the role (which can be a maximum of 43200). This is an - // optional parameter and by default, the value is set to 3600 seconds. - ); - log.debug("Request {} from {}", assumeRoleRequest, service); - try { - final AssumeRoleResult assumeRoleResult = service.assumeRole(assumeRoleRequest); - log.debug("Set credentials from {}", assumeRoleResult); - credentials.setTokens(new TemporaryAccessTokens( - assumeRoleResult.getCredentials().getAccessKeyId(), - assumeRoleResult.getCredentials().getSecretAccessKey(), - assumeRoleResult.getCredentials().getSessionToken(), - assumeRoleResult.getCredentials().getExpiration().getTime())); - } - catch(AWSSecurityTokenServiceException e) { - log.warn(e.getErrorMessage()); - return credentials; + return credentials.setTokens(new TemporaryAccessTokens( + sourceProfile.getAwsAccessIdKey(), sourceProfile.getAwsSecretAccessKey(), sourceProfile.getAwsSessionToken())) + .setProperty(Profile.STS_ROLE_ARN_PROPERTY_KEY, profile.getRoleArn()) + .setProperty(Profile.STS_MFA_ARN_PROPERTY_KEY, profile.getPropertyValue("mfa_serial")); } } } else { - log.debug("Configure credentials from basic profile {}", basicProfile.getProfileName()); - final Map profileProperties = basicProfile.getProperties(); + log.debug("Configure credentials from basic profile {}", profile.getProfileName()); + final Map profileProperties = profile.getProperties(); if(profileProperties.containsKey("sso_start_url") || profileProperties.containsKey("sso_session")) { // Read cached SSO credentials + log.debug("Set credentials from cached AWS CLI cache for {}", profile.getProfileName()); final CachedCredential cached = this.fetchSsoCredentials(profileProperties); if(null == cached) { return credentials; } - return credentials.withTokens(new TemporaryAccessTokens( - cached.accessKey, cached.secretKey, cached.sessionToken, - Instant.parse(cached.expiration).toEpochMilli())); + return credentials.setTokens(new TemporaryAccessTokens( + cached.accessKey, cached.secretKey, cached.sessionToken, Instant.parse(cached.expiration).toEpochMilli())); } - if(tokenCode != null) { - // Obtain session token - log.debug("Get session token from credentials in profile {}", basicProfile.getProfileName()); - final AWSSecurityTokenService service = this.getTokenService(host, - host.getRegion(), - basicProfile.getAwsAccessIdKey(), - basicProfile.getAwsSecretAccessKey(), - basicProfile.getAwsSessionToken()); - // The purpose of the sts:GetSessionToken operation is to authenticate the user using MFA. - final GetSessionTokenRequest sessionTokenRequest = new GetSessionTokenRequest() - // The value provided by the MFA device, if MFA is required - .withTokenCode(tokenCode) - // Specify this value if the IAM user has a policy that requires MFA authentication - .withSerialNumber(basicProfile.getPropertyValue("mfa_serial")) - .withDurationSeconds(durationSeconds); - log.debug("Request {} from {}", sessionTokenRequest, service); - try { - final GetSessionTokenResult sessionTokenResult = service.getSessionToken(sessionTokenRequest); - log.debug("Set credentials from {}", sessionTokenResult); - return credentials.withTokens(new TemporaryAccessTokens( - sessionTokenResult.getCredentials().getAccessKeyId(), - sessionTokenResult.getCredentials().getSecretAccessKey(), - sessionTokenResult.getCredentials().getSessionToken(), - sessionTokenResult.getCredentials().getExpiration().getTime())); - } - catch(AWSSecurityTokenServiceException e) { - log.warn(e.getErrorMessage()); - return credentials; - } - } - log.debug("Set credentials from profile {}", basicProfile.getProfileName()); + log.debug("Set credentials from profile {}", profile.getProfileName()); return credentials - .withTokens(new TemporaryAccessTokens( - basicProfile.getAwsAccessIdKey(), - basicProfile.getAwsSecretAccessKey(), - basicProfile.getAwsSessionToken(), - -1L)) - .withUsername(basicProfile.getAwsAccessIdKey()) - .withPassword(basicProfile.getAwsSecretAccessKey()); + .setTokens(new TemporaryAccessTokens( + profile.getAwsAccessIdKey(), + profile.getAwsSecretAccessKey(), + profile.getAwsSessionToken())) + .setProperty(Profile.STS_ROLE_ARN_PROPERTY_KEY, profile.getRoleArn()) + .setProperty(Profile.STS_MFA_ARN_PROPERTY_KEY, profile.getPropertyValue("mfa_serial")); } } else { @@ -303,7 +182,7 @@ public class S3CredentialsConfigurator implements CredentialsConfigurator { } } catch(AccessDeniedException | IllegalArgumentException | IOException e) { - log.warn(String.format("Failure reading %s and %s", configFile, credentialsFile)); + log.warn("Failure reading {} and {}", configFile, credentialsFile); return this; } if(allProfileProperties.isEmpty()) { @@ -356,7 +235,7 @@ public class S3CredentialsConfigurator implements CredentialsConfigurator { log.warn("Missing file {} with cached SSO credentials.", cachedCredentialsFile.getAbsolute()); return null; } - try (InputStream in = cachedCredentialsFile.getInputStream()) { + try(InputStream in = cachedCredentialsFile.getInputStream()) { final CachedCredentials cached = mapper.readValue(in, CachedCredentials.class); if(null == cached.credentials) { log.warn("Failure parsing SSO credentials."); @@ -399,40 +278,6 @@ public class S3CredentialsConfigurator implements CredentialsConfigurator { private String expiration; } - protected AWSSecurityTokenService getTokenService(final Host host, final String region, final String accessKey, final String secretKey, final String sessionToken) { - final ClientConfiguration configuration = new CustomClientConfiguration(host, - new ThreadLocalHostnameDelegatingTrustManager(trust, host.getHostname()), key); - return AWSSecurityTokenServiceClientBuilder.standard() - .withCredentials(new AWSStaticCredentialsProvider(StringUtils.isBlank(sessionToken) ? new AWSCredentials() { - @Override - public String getAWSAccessKeyId() { - return accessKey; - } - - @Override - public String getAWSSecretKey() { - return secretKey; - } - } : new AWSSessionCredentials() { - @Override - public String getAWSAccessKeyId() { - return accessKey; - } - - @Override - public String getAWSSecretKey() { - return secretKey; - } - - @Override - public String getSessionToken() { - return sessionToken; - } - })) - .withClientConfiguration(configuration) - .withRegion(StringUtils.isNotBlank(region) ? Regions.fromName(region) : Regions.DEFAULT_REGION).build(); - } - /** * Implementation of AbstractProfilesConfigFileScanner that groups profile properties into a map while scanning * through the credentials profile. @@ -452,7 +297,7 @@ public class S3CredentialsConfigurator implements CredentialsConfigurator { return new LinkedHashMap<>(); } log.debug("Reading AWS file {}", file); - try (InputStream inputStream = file.getInputStream(); Scanner scanner = new Scanner(inputStream, StandardCharsets.UTF_8.name())) { + try(InputStream inputStream = file.getInputStream(); Scanner scanner = new Scanner(inputStream, StandardCharsets.UTF_8.name())) { run(scanner); return new LinkedHashMap<>(allProfileProperties); } diff --git a/s3/src/main/java/ch/cyberduck/core/s3/S3CredentialsStrategy.java b/s3/src/main/java/ch/cyberduck/core/s3/S3CredentialsStrategy.java index 3766124b01..706b58975f 100644 --- a/s3/src/main/java/ch/cyberduck/core/s3/S3CredentialsStrategy.java +++ b/s3/src/main/java/ch/cyberduck/core/s3/S3CredentialsStrategy.java @@ -18,8 +18,29 @@ package ch.cyberduck.core.s3; import ch.cyberduck.core.Credentials; import ch.cyberduck.core.exception.BackgroundException; +import org.apache.commons.lang3.StringUtils; + +import com.amazonaws.auth.AWSCredentialsProvider; +import com.amazonaws.auth.AWSStaticCredentialsProvider; +import com.amazonaws.auth.AnonymousAWSCredentials; +import com.amazonaws.auth.BasicAWSCredentials; +import com.amazonaws.auth.BasicSessionCredentials; + public interface S3CredentialsStrategy { + static AWSCredentialsProvider toCredentialsProvider(final Credentials credentials) { + if(credentials.isAnonymousLogin()) { + return new AWSStaticCredentialsProvider(new AnonymousAWSCredentials()); + } + if(credentials.isTokenAuthentication()) { + return new AWSStaticCredentialsProvider( + StringUtils.isNotBlank(credentials.getTokens().getSessionToken()) ? + new BasicSessionCredentials(credentials.getTokens().getAccessKeyId(), credentials.getTokens().getSecretAccessKey(), credentials.getTokens().getSessionToken()) : + new BasicAWSCredentials(credentials.getTokens().getAccessKeyId(), credentials.getTokens().getSecretAccessKey())); + } + return new AWSStaticCredentialsProvider(new BasicAWSCredentials(credentials.getUsername(), credentials.getPassword())); + } + /** * @return Retrieve credentials to sign requests */ diff --git a/s3/src/main/java/ch/cyberduck/core/s3/S3DefaultDeleteFeature.java b/s3/src/main/java/ch/cyberduck/core/s3/S3DefaultDeleteFeature.java index 71c554873b..c1bcb15f91 100644 --- a/s3/src/main/java/ch/cyberduck/core/s3/S3DefaultDeleteFeature.java +++ b/s3/src/main/java/ch/cyberduck/core/s3/S3DefaultDeleteFeature.java @@ -52,7 +52,7 @@ public class S3DefaultDeleteFeature implements Delete { this.session = session; this.multipartService = multipartService; this.versioningService = versioningService; - this.containerService = session.getFeature(PathContainerService.class); + this.containerService = new S3PathContainerService(session.getHost()); } public void delete(final Map files, final PasswordCallback prompt, final Callback callback) throws BackgroundException { diff --git a/s3/src/main/java/ch/cyberduck/core/s3/S3DefaultMultipartService.java b/s3/src/main/java/ch/cyberduck/core/s3/S3DefaultMultipartService.java index 00100d2f53..d3e84af986 100644 --- a/s3/src/main/java/ch/cyberduck/core/s3/S3DefaultMultipartService.java +++ b/s3/src/main/java/ch/cyberduck/core/s3/S3DefaultMultipartService.java @@ -53,7 +53,7 @@ public class S3DefaultMultipartService implements S3MultipartService { public S3DefaultMultipartService(final S3Session session) { this.session = session; - this.containerService = session.getFeature(PathContainerService.class); + this.containerService = new S3PathContainerService(session.getHost()); } @Override diff --git a/s3/src/main/java/ch/cyberduck/core/s3/S3DirectoryFeature.java b/s3/src/main/java/ch/cyberduck/core/s3/S3DirectoryFeature.java index 97f56f6197..a2385695b1 100644 --- a/s3/src/main/java/ch/cyberduck/core/s3/S3DirectoryFeature.java +++ b/s3/src/main/java/ch/cyberduck/core/s3/S3DirectoryFeature.java @@ -42,27 +42,24 @@ public class S3DirectoryFeature implements Directory { private final S3AccessControlListFeature acl; private final PathContainerService containerService; - private Write writer; - - public S3DirectoryFeature(final S3Session session, final Write writer, final S3AccessControlListFeature acl) { + public S3DirectoryFeature(final S3Session session, final S3AccessControlListFeature acl) { this.session = session; - this.writer = writer; - this.containerService = session.getFeature(PathContainerService.class); + this.containerService = new S3PathContainerService(session.getHost()); this.acl = acl; } @Override - public Path mkdir(final Path folder, final TransferStatus status) throws BackgroundException { + public Path mkdir(final Write writer, final Path folder, final TransferStatus status) throws BackgroundException { if(containerService.isContainer(folder)) { final S3BucketCreateService service = new S3BucketCreateService(session); service.create(folder, StringUtils.isBlank(status.getRegion()) ? - new S3LocationFeature(session, session.getClient().getRegionEndpointCache()).getDefault().getIdentifier() : status.getRegion()); + new S3LocationFeature(session, session.getClient().getRegionEndpointCache()).getDefault(folder).getIdentifier() : status.getRegion()); return folder; } else { final EnumSet type = EnumSet.copyOf(folder.getType()); type.add(Path.Type.placeholder); - return new S3TouchFeature(session, acl).withWriter(writer).touch(folder + return new S3TouchFeature(session, acl).touch(writer, folder .withType(type), status // Add placeholder object .setMime(MIMETYPE) @@ -84,9 +81,4 @@ public class S3DirectoryFeature implements Directory { } } - @Override - public S3DirectoryFeature withWriter(final Write writer) { - this.writer = writer; - return this; - } } diff --git a/s3/src/main/java/ch/cyberduck/core/s3/S3EncryptionFeature.java b/s3/src/main/java/ch/cyberduck/core/s3/S3EncryptionFeature.java index e7e108ed71..0d6e73ca53 100644 --- a/s3/src/main/java/ch/cyberduck/core/s3/S3EncryptionFeature.java +++ b/s3/src/main/java/ch/cyberduck/core/s3/S3EncryptionFeature.java @@ -43,7 +43,7 @@ public class S3EncryptionFeature implements Encryption { public S3EncryptionFeature(final S3Session session, final S3AccessControlListFeature acl) { this.session = session; - this.containerService = session.getFeature(PathContainerService.class); + this.containerService = new S3PathContainerService(session.getHost()); this.acl = acl; } diff --git a/s3/src/main/java/ch/cyberduck/core/s3/S3FindFeature.java b/s3/src/main/java/ch/cyberduck/core/s3/S3FindFeature.java index b5ecebd0c3..1b83dd3e7a 100644 --- a/s3/src/main/java/ch/cyberduck/core/s3/S3FindFeature.java +++ b/s3/src/main/java/ch/cyberduck/core/s3/S3FindFeature.java @@ -44,7 +44,7 @@ public class S3FindFeature implements Find { this.session = session; this.acl = acl; this.attributes = new S3AttributesFinderFeature(session, acl); - this.containerService = session.getFeature(PathContainerService.class); + this.containerService = new S3PathContainerService(session.getHost()); } @Override diff --git a/s3/src/main/java/ch/cyberduck/core/s3/S3LifecycleConfiguration.java b/s3/src/main/java/ch/cyberduck/core/s3/S3LifecycleConfiguration.java index efedca66d8..de5aa26495 100644 --- a/s3/src/main/java/ch/cyberduck/core/s3/S3LifecycleConfiguration.java +++ b/s3/src/main/java/ch/cyberduck/core/s3/S3LifecycleConfiguration.java @@ -42,7 +42,7 @@ public class S3LifecycleConfiguration implements Lifecycle { public S3LifecycleConfiguration(final S3Session session) { this.session = session; - this.containerService = session.getFeature(PathContainerService.class); + this.containerService = new S3PathContainerService(session.getHost()); } @Override diff --git a/s3/src/main/java/ch/cyberduck/core/s3/S3ListService.java b/s3/src/main/java/ch/cyberduck/core/s3/S3ListService.java index e3136708cf..f9e5eb275f 100644 --- a/s3/src/main/java/ch/cyberduck/core/s3/S3ListService.java +++ b/s3/src/main/java/ch/cyberduck/core/s3/S3ListService.java @@ -16,6 +16,7 @@ package ch.cyberduck.core.s3; */ import ch.cyberduck.core.AttributedList; +import ch.cyberduck.core.DefaultPathAttributes; import ch.cyberduck.core.ListProgressListener; import ch.cyberduck.core.ListService; import ch.cyberduck.core.Path; @@ -94,7 +95,7 @@ public class S3ListService implements ListService { if(HostPreferencesFactory.get(session.getHost()).getBoolean("s3.upload.multipart.lookup")) { try { for(MultipartUpload upload : new S3DefaultMultipartService(session).find(directory)) { - final PathAttributes attributes = new PathAttributes(); + final PathAttributes attributes = new DefaultPathAttributes(); attributes.setHidden(true); attributes.setVersionId(upload.getUploadId()); attributes.setModificationDate(upload.getInitiatedDate().getTime()); diff --git a/s3/src/main/java/ch/cyberduck/core/s3/S3LocationFeature.java b/s3/src/main/java/ch/cyberduck/core/s3/S3LocationFeature.java index 14d28b53e4..e51e18ecf2 100644 --- a/s3/src/main/java/ch/cyberduck/core/s3/S3LocationFeature.java +++ b/s3/src/main/java/ch/cyberduck/core/s3/S3LocationFeature.java @@ -51,16 +51,16 @@ public class S3LocationFeature implements Location { public S3LocationFeature(final S3Session session, final RegionEndpointCache cache) { this.session = session; this.cache = cache; - this.containerService = session.getFeature(PathContainerService.class); + this.containerService = new S3PathContainerService(session.getHost()); } @Override - public Name getDefault() { + public Name getDefault(final Path file) { return new S3Region(HostPreferencesFactory.get(session.getHost()).getProperty("s3.location")); } @Override - public Set getLocations() { + public Set getLocations(final Path file) { if(StringUtils.isNotEmpty(RequestEntityRestStorageService.findBucketInHostname(session.getHost()))) { log.debug("Return empty set for hostname {}", session.getHost()); // Connected to single bucket @@ -71,14 +71,14 @@ public class S3LocationFeature implements Location { @Override public Name getLocation(final Path file) throws BackgroundException { - if(StringUtils.isNotBlank(session.getHost().getRegion())) { - return new S3Region(session.getHost().getRegion()); - } final Path bucket = containerService.getContainer(file); return this.getLocation(bucket.isRoot() ? StringUtils.EMPTY : bucket.getName()); } protected Name getLocation(final String bucketname) throws BackgroundException { + if(StringUtils.isNotBlank(session.getHost().getRegion())) { + return new S3Region(session.getHost().getRegion()); + } try { if(cache.containsRegionForBucketName(bucketname)) { return new S3Region(cache.getRegionForBucketName(bucketname)); diff --git a/s3/src/main/java/ch/cyberduck/core/s3/S3LoggingFeature.java b/s3/src/main/java/ch/cyberduck/core/s3/S3LoggingFeature.java index 9633a5f2bf..c3528311dc 100644 --- a/s3/src/main/java/ch/cyberduck/core/s3/S3LoggingFeature.java +++ b/s3/src/main/java/ch/cyberduck/core/s3/S3LoggingFeature.java @@ -46,7 +46,7 @@ public class S3LoggingFeature implements Logging { public S3LoggingFeature(final S3Session session) { this.session = session; - this.containerService = session.getFeature(PathContainerService.class); + this.containerService = new S3PathContainerService(session.getHost()); } @Override diff --git a/s3/src/main/java/ch/cyberduck/core/s3/S3MetadataFeature.java b/s3/src/main/java/ch/cyberduck/core/s3/S3MetadataFeature.java index 34e4b9b2fd..77c24f4e9a 100644 --- a/s3/src/main/java/ch/cyberduck/core/s3/S3MetadataFeature.java +++ b/s3/src/main/java/ch/cyberduck/core/s3/S3MetadataFeature.java @@ -52,11 +52,11 @@ public class S3MetadataFeature implements Headers { public S3MetadataFeature(final S3Session session, final S3AccessControlListFeature acl) { this.session = session; this.acl = acl; - this.containerService = session.getFeature(PathContainerService.class); + this.containerService = new S3PathContainerService(session.getHost()); } @Override - public Map getDefault() { + public Map getDefault(final Path file) { return HostPreferencesFactory.get(session.getHost()).getMap("s3.metadata.default"); } @@ -75,7 +75,7 @@ public class S3MetadataFeature implements Headers { // Apply non-standard ACL final Acl list = acl.getPermission(file); if(list.isEditable()) { - target.setAcl(acl.toAcl(list)); + target.setAcl(S3AccessControlListFeature.toAcl(list)); } } catch(AccessDeniedException | InteroperabilityException e) { diff --git a/s3/src/main/java/ch/cyberduck/core/s3/S3MoveFeature.java b/s3/src/main/java/ch/cyberduck/core/s3/S3MoveFeature.java index 68459281ea..0983c33173 100644 --- a/s3/src/main/java/ch/cyberduck/core/s3/S3MoveFeature.java +++ b/s3/src/main/java/ch/cyberduck/core/s3/S3MoveFeature.java @@ -19,6 +19,7 @@ package ch.cyberduck.core.s3; */ import ch.cyberduck.core.ConnectionCallback; +import ch.cyberduck.core.DefaultPathAttributes; import ch.cyberduck.core.Path; import ch.cyberduck.core.PathAttributes; import ch.cyberduck.core.PathContainerService; @@ -27,6 +28,7 @@ import ch.cyberduck.core.exception.NotfoundException; import ch.cyberduck.core.features.Delete; import ch.cyberduck.core.features.Directory; import ch.cyberduck.core.features.Move; +import ch.cyberduck.core.features.Write; import ch.cyberduck.core.io.DisabledStreamListener; import ch.cyberduck.core.transfer.TransferStatus; @@ -38,8 +40,6 @@ import org.jets3t.service.model.BaseVersionOrDeleteMarker; import java.util.Collections; import java.util.Optional; -import static ch.cyberduck.core.s3.S3VersionedObjectListService.KEY_DELETE_MARKER; - public class S3MoveFeature implements Move { private final S3Session session; @@ -49,7 +49,7 @@ public class S3MoveFeature implements Move { public S3MoveFeature(final S3Session session, final S3AccessControlListFeature acl) { this.session = session; - this.containerService = session.getFeature(PathContainerService.class); + this.containerService = new S3PathContainerService(session.getHost()); this.proxy = new S3ThresholdCopyFeature(session, acl); this.delete = new S3DefaultDeleteFeature(session, acl); } @@ -57,7 +57,7 @@ public class S3MoveFeature implements Move { @Override public Path move(final Path source, final Path renamed, final TransferStatus status, final Delete.Callback callback, final ConnectionCallback connectionCallback) throws BackgroundException { Path target; - if(source.attributes().getCustom().containsKey(KEY_DELETE_MARKER)) { + if(source.attributes().isTrashed()) { // Delete marker, copy not supported but we have to retain the delete marker at the target target = new Path(renamed); target.attributes().setVersionId(null); @@ -70,7 +70,7 @@ public class S3MoveFeature implements Move { String.valueOf(Path.DELIMITER), 1, null, null, false); if(marker.getItems().length == 1) { final BaseVersionOrDeleteMarker markerObject = marker.getItems()[0]; - target.attributes().setVersionId(markerObject.getVersionId()).setCustom(Collections.singletonMap(KEY_DELETE_MARKER, Boolean.TRUE.toString())); + target.attributes().setVersionId(markerObject.getVersionId()).setTrashed(true); delete.delete(Collections.singletonMap(source, status), connectionCallback, callback); } else { @@ -83,15 +83,15 @@ public class S3MoveFeature implements Move { } else { try { - target = proxy.copy(source, renamed, status.setLength(source.attributes().getSize()), connectionCallback, new DisabledStreamListener()); + target = proxy.copy(source, renamed, status, connectionCallback, new DisabledStreamListener()); // Copy source path and nullify version id to add a delete marker - delete.delete(Collections.singletonMap(new Path(source).withAttributes(new PathAttributes(source.attributes()).setVersionId(null)), status), + delete.delete(Collections.singletonMap(new Path(source).withAttributes(new DefaultPathAttributes(source.attributes()).setVersionId(null)), status), connectionCallback, callback); } catch(NotfoundException e) { if(source.getType().contains(Path.Type.placeholder)) { // No placeholder object to copy, create a new one at the target - target = session.getFeature(Directory.class).mkdir(renamed, new TransferStatus().setRegion(source.attributes().getRegion())); + target = session.getFeature(Directory.class).mkdir(session.getFeature(Write.class), renamed, status); } else { throw e; diff --git a/s3/src/main/java/ch/cyberduck/core/s3/S3MultipartCopyFeature.java b/s3/src/main/java/ch/cyberduck/core/s3/S3MultipartCopyFeature.java index 2d67ea0aa0..af71cd747c 100644 --- a/s3/src/main/java/ch/cyberduck/core/s3/S3MultipartCopyFeature.java +++ b/s3/src/main/java/ch/cyberduck/core/s3/S3MultipartCopyFeature.java @@ -60,13 +60,13 @@ public class S3MultipartCopyFeature extends S3CopyFeature { public S3MultipartCopyFeature(final S3Session session, final S3AccessControlListFeature acl) { super(session, acl); this.session = session; - this.containerService = session.getFeature(PathContainerService.class); + this.containerService = new S3PathContainerService(session.getHost()); this.pool = ThreadPoolFactory.get("multipart", HostPreferencesFactory.get(session.getHost()).getInteger("s3.upload.multipart.concurrency")); this.partsize = HostPreferencesFactory.get(session.getHost()).getLong("s3.copy.multipart.size"); } @Override - protected String copy(final Path source, final S3Object destination, final TransferStatus status, final StreamListener listener) throws BackgroundException { + protected CopyResult copy(final Path source, final S3Object destination, final TransferStatus status, final StreamListener listener) throws BackgroundException { try { final List completed = new ArrayList<>(); // ID for the initiated multipart upload. @@ -95,7 +95,7 @@ public class S3MultipartCopyFeature extends S3CopyFeature { // has been sent, it is important that you check the response body to determine whether the request succeeded. final MultipartCompleted complete = session.getClient().multipartCompleteUpload(multipart, completed); log.debug("Completed multipart upload for {} with checksum {}", complete.getObjectKey(), complete.getEtag()); - return complete.getVersionId(); + return new CopyResult(complete.getVersionId(), complete.getEtag()); } catch(ServiceException e) { throw new S3ExceptionMappingService().map("Cannot copy {0}", e, source); diff --git a/s3/src/main/java/ch/cyberduck/core/s3/S3MultipartUploadService.java b/s3/src/main/java/ch/cyberduck/core/s3/S3MultipartUploadService.java index 1233112940..8c8021a647 100644 --- a/s3/src/main/java/ch/cyberduck/core/s3/S3MultipartUploadService.java +++ b/s3/src/main/java/ch/cyberduck/core/s3/S3MultipartUploadService.java @@ -30,7 +30,6 @@ import ch.cyberduck.core.exception.BackgroundException; import ch.cyberduck.core.exception.ChecksumException; import ch.cyberduck.core.exception.ConnectionCanceledException; import ch.cyberduck.core.exception.InteroperabilityException; -import ch.cyberduck.core.features.Upload; import ch.cyberduck.core.features.Write; import ch.cyberduck.core.http.HttpUploadFeature; import ch.cyberduck.core.io.BandwidthThrottle; @@ -75,31 +74,28 @@ public class S3MultipartUploadService extends HttpUploadFeature writer; /** * A split smaller than 5M is not allowed */ private final Long partsize; private final Integer concurrency; - public S3MultipartUploadService(final S3Session session, final Write writer, final S3AccessControlListFeature acl) { - this(session, writer, acl, HostPreferencesFactory.get(session.getHost()).getLong("s3.upload.multipart.size"), + public S3MultipartUploadService(final S3Session session, final S3AccessControlListFeature acl) { + this(session, acl, HostPreferencesFactory.get(session.getHost()).getLong("s3.upload.multipart.size"), HostPreferencesFactory.get(session.getHost()).getInteger("s3.upload.multipart.concurrency")); } - public S3MultipartUploadService(final S3Session session, final Write writer, final S3AccessControlListFeature acl, final Long partsize, final Integer concurrency) { - super(writer); + public S3MultipartUploadService(final S3Session session, final S3AccessControlListFeature acl, final Long partsize, final Integer concurrency) { this.session = session; this.multipartService = new S3DefaultMultipartService(session); - this.containerService = session.getFeature(PathContainerService.class); - this.writer = writer; + this.containerService = new S3PathContainerService(session.getHost()); this.acl = acl; this.partsize = Math.max(HostPreferencesFactory.get(session.getHost()).getLong("s3.upload.multipart.partsize.minimum"), partsize); this.concurrency = concurrency; } @Override - public StorageObject upload(final Path file, final Local local, final BandwidthThrottle throttle, final ProgressListener progress, final StreamListener streamListener, + public StorageObject upload(final Write 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 ThreadPool pool = ThreadPoolFactory.get("multipart", concurrency); try { @@ -156,7 +152,7 @@ public class S3MultipartUploadService extends HttpUploadFeature submit(final ThreadPool pool, final Path file, final Local local, + private Future submit(final ThreadPool pool, final Write write, final Path file, final Local local, final BandwidthThrottle throttle, final StreamListener listener, final TransferStatus overall, final MultipartUpload multipart, final int partNumber, final long offset, final long length, final ConnectionCallback callback) throws ConnectionCanceledException { @@ -230,7 +226,7 @@ public class S3MultipartUploadService extends HttpUploadFeature checksum = writer.checksum(file, status).computeAll(local.getInputStream(), status); + final Set checksum = write.checksum(file, status).computeAll(local.getInputStream(), status); switch(session.getSignatureVersion()) { case AWS4HMACSHA256: checksum.stream().filter(v -> v.algorithm.equals(HashAlgorithm.sha256)).findFirst().ifPresent(status::setChecksum); @@ -244,7 +240,7 @@ public class S3MultipartUploadService extends HttpUploadFeature withWriter(final Write writer) { - this.writer = writer; - return super.withWriter(writer); - } } diff --git a/s3/src/main/java/ch/cyberduck/core/s3/S3MultipartWriteFeature.java b/s3/src/main/java/ch/cyberduck/core/s3/S3MultipartWriteFeature.java index 4d3fa7d6c1..d1b1b92462 100644 --- a/s3/src/main/java/ch/cyberduck/core/s3/S3MultipartWriteFeature.java +++ b/s3/src/main/java/ch/cyberduck/core/s3/S3MultipartWriteFeature.java @@ -53,7 +53,7 @@ public class S3MultipartWriteFeature implements MultipartWrite { public S3MultipartWriteFeature(final S3Session session, final S3AccessControlListFeature acl) { this.session = session; - this.containerService = session.getFeature(PathContainerService.class); + this.containerService = new S3PathContainerService(session.getHost()); this.acl = acl; } diff --git a/s3/src/main/java/ch/cyberduck/core/s3/S3MultipleDeleteFeature.java b/s3/src/main/java/ch/cyberduck/core/s3/S3MultipleDeleteFeature.java index 21e3a47197..d16b28c9c9 100644 --- a/s3/src/main/java/ch/cyberduck/core/s3/S3MultipleDeleteFeature.java +++ b/s3/src/main/java/ch/cyberduck/core/s3/S3MultipleDeleteFeature.java @@ -58,7 +58,7 @@ public class S3MultipleDeleteFeature implements Delete { this.session = session; this.multipartService = multipartService; this.versioningService = versioningService; - this.containerService = session.getFeature(PathContainerService.class); + this.containerService = new S3PathContainerService(session.getHost()); } public void delete(final Map files, final PasswordCallback prompt, final Callback callback) throws BackgroundException { diff --git a/s3/src/main/java/ch/cyberduck/core/s3/S3ObjectListService.java b/s3/src/main/java/ch/cyberduck/core/s3/S3ObjectListService.java index 4ee21d7955..00ffdee702 100644 --- a/s3/src/main/java/ch/cyberduck/core/s3/S3ObjectListService.java +++ b/s3/src/main/java/ch/cyberduck/core/s3/S3ObjectListService.java @@ -19,6 +19,7 @@ package ch.cyberduck.core.s3; */ import ch.cyberduck.core.AttributedList; +import ch.cyberduck.core.DefaultPathAttributes; import ch.cyberduck.core.ListProgressListener; import ch.cyberduck.core.ListService; import ch.cyberduck.core.Path; @@ -58,7 +59,7 @@ public class S3ObjectListService extends S3AbstractListService implements ListSe super(session); this.session = session; this.attributes = new S3AttributesFinderFeature(session, acl); - this.containerService = session.getFeature(PathContainerService.class); + this.containerService = new S3PathContainerService(session.getHost()); this.metadata = metadata; } @@ -119,12 +120,12 @@ public class S3ObjectListService extends S3AbstractListService implements ListSe final String[] prefixes = chunk.getCommonPrefixes(); for(String common : prefixes) { log.debug("Handle common prefix {}", common); - final String key = StringUtils.chomp(URIEncoder.decode(common), String.valueOf(Path.DELIMITER)); + final String key = StringUtils.removeEnd(URIEncoder.decode(common), String.valueOf(Path.DELIMITER)); if(new SimplePathPredicate(PathNormalizer.compose(bucket, key)).test(directory)) { continue; } final Path f; - final PathAttributes attr = new PathAttributes(); + final PathAttributes attr = new DefaultPathAttributes(); attr.setRegion(bucket.attributes().getRegion()); if(null == delimiter) { f = new Path(String.format("%s/%s", bucket.getAbsolute(), key), diff --git a/s3/src/main/java/ch/cyberduck/core/s3/S3PathContainerServiceFactory.java b/s3/src/main/java/ch/cyberduck/core/s3/S3PathContainerServiceFactory.java deleted file mode 100644 index 9529db80e5..0000000000 --- a/s3/src/main/java/ch/cyberduck/core/s3/S3PathContainerServiceFactory.java +++ /dev/null @@ -1,55 +0,0 @@ -package ch.cyberduck.core.s3; - -/* - * Copyright (c) 2002-2023 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 ch.cyberduck.core.Factory; -import ch.cyberduck.core.FactoryException; -import ch.cyberduck.core.Host; -import ch.cyberduck.core.PathContainerService; - -import org.apache.commons.lang3.reflect.ConstructorUtils; -import org.apache.logging.log4j.LogManager; -import org.apache.logging.log4j.Logger; - -import java.lang.reflect.Constructor; -import java.lang.reflect.InvocationTargetException; - -public class S3PathContainerServiceFactory extends Factory { - private static final Logger log = LogManager.getLogger(S3PathContainerServiceFactory.class); - - private S3PathContainerServiceFactory() { - super("factory.s3.pathcontainerservice.class"); - } - - public static PathContainerService get(final Host host) { - return new S3PathContainerServiceFactory().create(host); - } - - private PathContainerService create(final Host host) { - try { - final Constructor constructor = ConstructorUtils.getMatchingAccessibleConstructor(clazz, host.getClass()); - if(null == constructor) { - log.warn("No matching constructor for parameter {}", host.getClass()); - // Call default constructor for disabled implementations - return clazz.getDeclaredConstructor().newInstance(); - } - return constructor.newInstance(host); - } - catch(InstantiationException | InvocationTargetException | IllegalAccessException | NoSuchMethodException e) { - throw new FactoryException(e.getMessage(), e); - } - } -} diff --git a/s3/src/main/java/ch/cyberduck/core/s3/S3PresignedUrlProvider.java b/s3/src/main/java/ch/cyberduck/core/s3/S3PresignedUrlProvider.java index 0de073471b..a952eb1562 100644 --- a/s3/src/main/java/ch/cyberduck/core/s3/S3PresignedUrlProvider.java +++ b/s3/src/main/java/ch/cyberduck/core/s3/S3PresignedUrlProvider.java @@ -18,7 +18,10 @@ package ch.cyberduck.core.s3; * feedback@cyberduck.io */ +import ch.cyberduck.core.Credentials; import ch.cyberduck.core.Host; +import ch.cyberduck.core.PathNormalizer; +import ch.cyberduck.core.Scheme; import ch.cyberduck.core.preferences.HostPreferences; import ch.cyberduck.core.preferences.HostPreferencesFactory; @@ -28,6 +31,7 @@ import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; import org.jets3t.service.impl.rest.httpclient.RestS3Service; import org.jets3t.service.security.AWSCredentials; +import org.jets3t.service.security.AWSSessionCredentials; public class S3PresignedUrlProvider { private static final Logger log = LogManager.getLogger(S3PresignedUrlProvider.class); @@ -50,9 +54,12 @@ public class S3PresignedUrlProvider { * @param expiry Milliseconds * @return a URL signed in such a way as to grant access to an S3 resource to whoever uses it. */ - public String create(final String secret, final String bucket, final String region, final String key, final String method, final long expiry) { + public String create(final Credentials credentials, final String bucket, final String region, final String key, final String method, final long expiry) { final Host bookmark = session.getHost(); - return new RestS3Service(new AWSCredentials(StringUtils.strip(bookmark.getCredentials().getUsername()), StringUtils.strip(secret))) { + return new RestS3Service(credentials.getTokens().validate() ? + new AWSSessionCredentials(credentials.getTokens().getAccessKeyId(), credentials.getTokens().getSecretAccessKey(), + credentials.getTokens().getSessionToken()) : + new AWSCredentials(credentials.getTokens().getAccessKeyId(), credentials.getTokens().getSecretAccessKey())) { @Override public String getEndpoint() { if(S3Session.isAwsHostname(bookmark.getHostname())) { @@ -64,6 +71,15 @@ public class S3PresignedUrlProvider { return bookmark.getHostname(); } + @Override + protected String getVirtualPath() { + final String context = bookmark.getProtocol().getContext(); + if(StringUtils.isNotBlank(context) && !Scheme.isURL(context)) { + return PathNormalizer.normalize(context); + } + return StringUtils.EMPTY; + } + @Override protected void initializeProxy(final HttpClientBuilder httpClientBuilder) { // diff --git a/s3/src/main/java/ch/cyberduck/core/s3/S3Protocol.java b/s3/src/main/java/ch/cyberduck/core/s3/S3Protocol.java index 8d15f40a4d..39d4ce92a8 100644 --- a/s3/src/main/java/ch/cyberduck/core/s3/S3Protocol.java +++ b/s3/src/main/java/ch/cyberduck/core/s3/S3Protocol.java @@ -18,13 +18,14 @@ package ch.cyberduck.core.s3; */ import ch.cyberduck.core.AbstractProtocol; +import ch.cyberduck.core.Credentials; import ch.cyberduck.core.CredentialsConfigurator; import ch.cyberduck.core.DirectoryDelimiterPathContainerService; import ch.cyberduck.core.LocaleFactory; +import ch.cyberduck.core.LoginOptions; import ch.cyberduck.core.PathContainerService; import ch.cyberduck.core.Protocol; import ch.cyberduck.core.Scheme; -import ch.cyberduck.core.auth.AWSCredentialsConfigurator; import ch.cyberduck.core.features.Location; import ch.cyberduck.core.io.HashAlgorithm; import ch.cyberduck.core.preferences.PreferencesFactory; @@ -32,6 +33,7 @@ import ch.cyberduck.core.synchronization.ComparisonService; import ch.cyberduck.core.synchronization.DefaultComparisonService; import ch.cyberduck.core.synchronization.ETagComparisonService; +import org.apache.commons.lang3.StringUtils; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; @@ -39,21 +41,13 @@ import java.util.List; import java.util.Set; import java.util.stream.Collectors; -import com.amazonaws.auth.AWSCredentialsProviderChain; -import com.amazonaws.auth.EnvironmentVariableCredentialsProvider; -import com.amazonaws.auth.profile.ProfileCredentialsProvider; import com.google.auto.service.AutoService; @AutoService(Protocol.class) public class S3Protocol extends AbstractProtocol { private static final Logger log = LogManager.getLogger(S3Protocol.class); - private final AWSCredentialsConfigurator credentials = new AWSCredentialsConfigurator( - new AWSCredentialsProviderChain( - new ProfileCredentialsProvider(), - new EnvironmentVariableCredentialsProvider() - ) - ); + private final CredentialsConfigurator credentials = new S3CredentialsConfigurator(); @Override public String getName() { @@ -112,7 +106,7 @@ public class S3Protocol extends AbstractProtocol { @Override public String getTokenPlaceholder() { - return LocaleFactory.localizedString("MFA Authentication Code", "S3"); + return LocaleFactory.localizedString("Session Token", "S3"); } @Override @@ -126,6 +120,21 @@ public class S3Protocol extends AbstractProtocol { return PreferencesFactory.get().getProperty("s3.signature.version"); } + @Override + public boolean validate(final Credentials credentials, final LoginOptions options) { + if(options.token) { + if(credentials.isTokenAuthentication()) { + if(StringUtils.isBlank(credentials.getTokens().getAccessKeyId())) { + return false; + } + if(StringUtils.isBlank(credentials.getTokens().getSecretAccessKey())) { + return false; + } + } + } + return super.validate(credentials, options); + } + public enum AuthenticationHeaderSignatureVersion { AWS2 { @Override @@ -179,6 +188,7 @@ public class S3Protocol extends AbstractProtocol { return (T) new DefaultComparisonService(new ETagComparisonService(), ComparisonService.disabled); } if(type == CredentialsConfigurator.class) { + // Fetching configuration from AWS CLI return (T) credentials; } return super.getFeature(type); diff --git a/s3/src/main/java/ch/cyberduck/core/s3/S3ReadFeature.java b/s3/src/main/java/ch/cyberduck/core/s3/S3ReadFeature.java index d6f812ac67..6132b200bc 100644 --- a/s3/src/main/java/ch/cyberduck/core/s3/S3ReadFeature.java +++ b/s3/src/main/java/ch/cyberduck/core/s3/S3ReadFeature.java @@ -50,7 +50,7 @@ public class S3ReadFeature implements Read { public S3ReadFeature(final S3Session session) { this.session = session; - this.containerService = session.getFeature(PathContainerService.class); + this.containerService = new S3PathContainerService(session.getHost()); } @Override diff --git a/s3/src/main/java/ch/cyberduck/core/s3/S3SearchFeature.java b/s3/src/main/java/ch/cyberduck/core/s3/S3SearchFeature.java index c6815f6674..45826b77ec 100644 --- a/s3/src/main/java/ch/cyberduck/core/s3/S3SearchFeature.java +++ b/s3/src/main/java/ch/cyberduck/core/s3/S3SearchFeature.java @@ -72,7 +72,7 @@ public class S3SearchFeature implements Search { } @Override - public EnumSet features() { + public EnumSet features(final Path workdir) { return EnumSet.of(Flags.recursive); } } diff --git a/s3/src/main/java/ch/cyberduck/core/s3/S3Session.java b/s3/src/main/java/ch/cyberduck/core/s3/S3Session.java index 8772dcdf8f..50afffe7db 100644 --- a/s3/src/main/java/ch/cyberduck/core/s3/S3Session.java +++ b/s3/src/main/java/ch/cyberduck/core/s3/S3Session.java @@ -27,11 +27,11 @@ import ch.cyberduck.core.ListService; import ch.cyberduck.core.LoginCallback; import ch.cyberduck.core.Path; import ch.cyberduck.core.PathContainerService; +import ch.cyberduck.core.Profile; import ch.cyberduck.core.Scheme; +import ch.cyberduck.core.TemporaryAccessTokens; import ch.cyberduck.core.UrlProvider; -import ch.cyberduck.core.auth.AWSCredentialsConfigurator; import ch.cyberduck.core.auth.AWSSessionCredentialsRetriever; -import ch.cyberduck.core.aws.CustomClientConfiguration; import ch.cyberduck.core.cdn.Distribution; import ch.cyberduck.core.cdn.DistributionConfiguration; import ch.cyberduck.core.cloudfront.CloudFrontDistributionConfigurationPreloader; @@ -40,13 +40,10 @@ import ch.cyberduck.core.exception.BackgroundException; import ch.cyberduck.core.exception.LoginCanceledException; 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.kms.KMSEncryptionFeature; import ch.cyberduck.core.oauth.OAuth2AuthorizationService; import ch.cyberduck.core.oauth.OAuth2RequestInterceptor; -import ch.cyberduck.core.preferences.HostPreferences; -import ch.cyberduck.core.preferences.HostPreferencesFactory; import ch.cyberduck.core.proxy.ProxyFinder; import ch.cyberduck.core.restore.Glacier; import ch.cyberduck.core.shared.DefaultPathHomeFeature; @@ -54,11 +51,12 @@ import ch.cyberduck.core.shared.DelegatingHomeFeature; import ch.cyberduck.core.shared.DisabledBulkFeature; import ch.cyberduck.core.ssl.DefaultX509KeyManager; import ch.cyberduck.core.ssl.DisabledX509TrustManager; -import ch.cyberduck.core.ssl.ThreadLocalHostnameDelegatingTrustManager; import ch.cyberduck.core.ssl.X509KeyManager; import ch.cyberduck.core.ssl.X509TrustManager; -import ch.cyberduck.core.sts.STSAssumeRoleCredentialsRequestInterceptor; -import ch.cyberduck.core.sts.STSExceptionMappingService; +import ch.cyberduck.core.sts.STSAssumeRoleCredentialsStrategy; +import ch.cyberduck.core.sts.STSAssumeRoleWithWebIdentityCredentialsStrategy; +import ch.cyberduck.core.sts.STSAuthorizationService; +import ch.cyberduck.core.sts.STSGetSessionTokenCredentialsStrategy; import ch.cyberduck.core.threading.CancelCallback; import org.apache.commons.lang3.StringUtils; @@ -78,9 +76,6 @@ import org.apache.logging.log4j.Logger; import org.jets3t.service.ServiceException; import org.jets3t.service.impl.rest.XmlResponsesSaxParser; import org.jets3t.service.impl.rest.httpclient.RegionEndpointCache; -import org.jets3t.service.security.AWSCredentials; -import org.jets3t.service.security.AWSSessionCredentials; -import org.jets3t.service.security.ProviderCredentials; import org.jets3t.service.utils.SignatureUtils; import java.util.HashSet; @@ -88,21 +83,12 @@ import java.util.Map; import java.util.Set; import java.util.concurrent.ConcurrentHashMap; -import com.amazonaws.client.builder.AwsClientBuilder; -import com.amazonaws.services.securitytoken.AWSSecurityTokenService; -import com.amazonaws.services.securitytoken.AWSSecurityTokenServiceClientBuilder; -import com.amazonaws.services.securitytoken.model.AWSSecurityTokenServiceException; -import com.amazonaws.services.securitytoken.model.GetCallerIdentityRequest; -import com.amazonaws.services.securitytoken.model.GetCallerIdentityResult; - -import static com.amazonaws.services.s3.Headers.*; +import static com.amazonaws.services.s3.Headers.REQUESTER_PAYS_HEADER; +import static com.amazonaws.services.s3.Headers.S3_ALTERNATE_DATE; public class S3Session extends HttpSession { private static final Logger log = LogManager.getLogger(S3Session.class); - private final HostPreferences preferences - = HostPreferencesFactory.get(host); - private final S3AccessControlListFeature acl = new S3AccessControlListFeature(this); private final Versioning versioning = preferences.getBoolean("s3.versioning.enable") @@ -147,13 +133,23 @@ public class S3Session extends HttpSession { @Override protected void logout() throws BackgroundException { + scheduler.shutdown(false); + super.logout(); + } + + @Override + public void disconnect() throws BackgroundException { try { - scheduler.shutdown(false); - client.shutdown(); + if(client != null) { + client.shutdown(); + } } catch(ServiceException e) { throw new S3ExceptionMappingService().map(e); } + finally { + super.disconnect(); + } } protected XmlResponsesSaxParser getXmlResponseSaxParser() throws ServiceException { @@ -192,7 +188,10 @@ public class S3Session extends HttpSession { @Override protected RequestEntityRestStorageService connect(final ProxyFinder proxy, final HostKeyCallback hostkey, final LoginCallback prompt, final CancelCallback cancel) throws BackgroundException { final HttpClientBuilder configuration = builder.build(proxy, this, prompt); - authentication = this.configureCredentialsStrategy(proxy, configuration, prompt); + authentication = this.configureCredentialsStrategy(configuration, prompt); + log.debug("Configured authentication strategy {}", authentication); + configuration.setServiceUnavailableRetryStrategy(new CustomServiceUnavailableRetryStrategy(host, + new S3AuthenticationResponseInterceptor(authentication))); if(preferences.getBoolean("s3.upload.expect-continue")) { final String header = HTTP.EXPECT_DIRECTIVE; log.debug("Add request handler for {}", header); @@ -244,15 +243,6 @@ public class S3Session extends HttpSession { request.setHeader(S3_ALTERNATE_DATE, SignatureUtils.formatAwsFlavouredISO8601Date(client.getCurrentTimeWithOffset())); } }); - configuration.addInterceptorLast(new HttpRequestInterceptor() { - @Override - public void process(final HttpRequest request, final HttpContext context) { - final ProviderCredentials credentials = client.getProviderCredentials(); - if(credentials instanceof AWSSessionCredentials) { - request.setHeader(SECURITY_TOKEN, ((AWSSessionCredentials) credentials).getSessionToken()); - } - } - }); switch(authenticationHeaderSignatureVersion) { case AWS4HMACSHA256: configuration.addInterceptorLast(new S3AWS4SignatureRequestInterceptor(this)); @@ -266,7 +256,7 @@ public class S3Session extends HttpSession { return client; } - protected S3CredentialsStrategy configureCredentialsStrategy(final ProxyFinder proxy, final HttpClientBuilder configuration, + protected S3CredentialsStrategy configureCredentialsStrategy(final HttpClientBuilder configuration, final LoginCallback prompt) throws LoginCanceledException { if(host.getProtocol().isOAuthConfigurable()) { final OAuth2RequestInterceptor oauth = new OAuth2RequestInterceptor(configuration.build(), host, prompt) @@ -274,81 +264,52 @@ public class S3Session extends HttpSession { if(host.getProtocol().getAuthorization() != null) { oauth.withFlowType(OAuth2AuthorizationService.FlowType.valueOf(host.getProtocol().getAuthorization())); } + log.debug("Add interceptor {}", oauth); configuration.addInterceptorLast(oauth); - final STSAssumeRoleCredentialsRequestInterceptor interceptor - = new STSAssumeRoleCredentialsRequestInterceptor(oauth, this, trust, key, prompt); - configuration.addInterceptorLast(interceptor); - configuration.setServiceUnavailableRetryStrategy(new CustomServiceUnavailableRetryStrategy(host, - new S3AuthenticationResponseInterceptor(this, interceptor))); - return interceptor; + final S3CredentialsStrategy strategy + = new STSAssumeRoleWithWebIdentityCredentialsStrategy(oauth, host, trust, key, prompt); + log.debug("Return authenticator {}", strategy); + return strategy; } - else { - if(S3Session.isAwsHostname(host.getHostname())) { - final S3AuthenticationResponseInterceptor interceptor; - // Try auto-configure - if(Scheme.isURL(host.getProtocol().getContext())) { - // Fetch temporary session token from instance metadata - interceptor = new S3AuthenticationResponseInterceptor(this, - new AWSSessionCredentialsRetriever(trust, key, host.getProtocol().getContext()) - ); - } - else { - final Credentials credentials = new S3CredentialsConfigurator( - new ThreadLocalHostnameDelegatingTrustManager(trust, host.getHostname()), key, prompt).reload().configure(host); - // Fetch temporary session token from AWS CLI configuration - interceptor = new S3AuthenticationResponseInterceptor(this, () -> credentials); - } - configuration.setServiceUnavailableRetryStrategy(new CustomServiceUnavailableRetryStrategy(host, - new ExecutionCountServiceUnavailableRetryStrategy(interceptor))); - return interceptor; - } - else { - final Credentials credentials = new Credentials(host.getCredentials()); - return () -> credentials; + if(S3Session.isAwsHostname(host.getHostname())) { + // Try auto-configure + final String context = preferences.getProperty(Profile.CONTEXT_KEY); + if(Scheme.isURL(context)) { + log.debug("Auto-configure credentials from HTTP endpoint {}", context); + // Fetch temporary session token from instance metadata + return new AWSSessionCredentialsRetriever(trust, key, context); } } + if(host.getProtocol().isRoleConfigurable() || host.getProperty(Profile.STS_ROLE_ARN_PROPERTY_KEY) != null) { + final S3CredentialsStrategy strategy = new STSAssumeRoleCredentialsStrategy(host, trust, key, prompt); + log.debug("Return authenticator {}", strategy); + return strategy; + } + if(host.getProtocol().isMultiFactorConfigurable() || host.getProperty(Profile.STS_MFA_ARN_PROPERTY_KEY) != null) { + final S3CredentialsStrategy strategy = new STSGetSessionTokenCredentialsStrategy(host, trust, key, prompt); + log.debug("Return authenticator {}", strategy); + return strategy; + } + // Keep copy of credentials + final Credentials credentials = host.getCredentials(); + if(credentials.isTokenAuthentication()) { + return () -> credentials; + } + credentials.setTokens(new TemporaryAccessTokens(host.getCredentials().getUsername(), host.getCredentials().getPassword())); + return () -> credentials; } @Override public void login(final LoginCallback prompt, final CancelCallback cancel) throws BackgroundException { final Credentials credentials = authentication.get(); - if(credentials.isAnonymousLogin()) { - log.debug("Connect non-authenticated with no credentials for {}", host); - client.setProviderCredentials(null); - } - else { - if(credentials.getTokens().validate()) { - log.debug("Connect with session token for {}", host); - client.setProviderCredentials(new AWSSessionCredentials( - credentials.getTokens().getAccessKeyId(), credentials.getTokens().getSecretAccessKey(), - credentials.getTokens().getSessionToken())); - } - else { - log.debug("Connect with static keys for {}", host); - client.setProviderCredentials(new AWSCredentials(credentials.getUsername(), credentials.getPassword())); - } - } final Path home = new DelegatingHomeFeature(new DefaultPathHomeFeature(host)).find(); if(S3Session.isAwsHostname(host.getHostname(), false)) { if(StringUtils.isEmpty(RequestEntityRestStorageService.findBucketInHostname(host))) { - if(client.isAuthenticatedConnection()) { - final CustomClientConfiguration configuration = new CustomClientConfiguration(host, - new ThreadLocalHostnameDelegatingTrustManager(trust, host.getHostname()), key); - final AWSSecurityTokenServiceClientBuilder builder = AWSSecurityTokenServiceClientBuilder.standard() - .withEndpointConfiguration(new AwsClientBuilder.EndpointConfiguration(host.getProtocol().getSTSEndpoint(), null)) - .withCredentials(AWSCredentialsConfigurator.toAWSCredentialsProvider(client.getProviderCredentials())) - .withClientConfiguration(configuration); - final AWSSecurityTokenService service = builder.build(); + if(!credentials.isAnonymousLogin()) { // Returns details about the IAM user or role whose credentials are used to call the operation. // No permissions are required to perform this operation. - try { - final GetCallerIdentityResult identity = service.getCallerIdentity(new GetCallerIdentityRequest()); - log.debug("Successfully verified credentials for {}", identity); - return; - } - catch(AWSSecurityTokenServiceException e) { - throw new STSExceptionMappingService().map(e); - } + new STSAuthorizationService(host, trust, key, prompt).getCallerIdentity(credentials); + return; } } } @@ -403,7 +364,7 @@ public class S3Session extends HttpSession { return (T) new S3ThresholdUploadService(this, acl); } if(type == Directory.class) { - return (T) new S3DirectoryFeature(this, new S3WriteFeature(this, acl), acl); + return (T) new S3DirectoryFeature(this, acl); } if(type == Move.class) { return (T) new S3MoveFeature(this, acl); @@ -499,7 +460,7 @@ public class S3Session extends HttpSession { return null; } if(type == PathContainerService.class) { - return (T) S3PathContainerServiceFactory.get(host); + return (T) new S3PathContainerService(host); } return super._getFeature(type); } diff --git a/s3/src/main/java/ch/cyberduck/core/s3/S3SingleUploadService.java b/s3/src/main/java/ch/cyberduck/core/s3/S3SingleUploadService.java index f4bc4e0dc3..29bf4900e2 100644 --- a/s3/src/main/java/ch/cyberduck/core/s3/S3SingleUploadService.java +++ b/s3/src/main/java/ch/cyberduck/core/s3/S3SingleUploadService.java @@ -23,7 +23,6 @@ import ch.cyberduck.core.Path; import ch.cyberduck.core.ProgressListener; import ch.cyberduck.core.exception.BackgroundException; import ch.cyberduck.core.exception.InteroperabilityException; -import ch.cyberduck.core.features.Upload; import ch.cyberduck.core.features.Write; import ch.cyberduck.core.http.HttpUploadFeature; import ch.cyberduck.core.io.BandwidthThrottle; @@ -47,34 +46,31 @@ public class S3SingleUploadService extends HttpUploadFeature writer; - public S3SingleUploadService(final S3Session session, final Write writer) { - super(writer); + public S3SingleUploadService(final S3Session session) { this.session = session; - this.writer = writer; } @Override - public StorageObject upload(final Path file, final Local local, final BandwidthThrottle throttle, + public StorageObject upload(final Write 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 S3Protocol.AuthenticationHeaderSignatureVersion signatureVersion = session.getSignatureVersion(); switch(signatureVersion) { case AWS4HMACSHA256: if(!HashAlgorithm.sha256.equals(status.getChecksum().algorithm)) { // Checksum not set in upload filter - status.setChecksum(writer.checksum(file, status).compute(local.getInputStream(), status)); + status.setChecksum(write.checksum(file, status).compute(local.getInputStream(), status)); } break; } try { - return super.upload(file, local, throttle, progress, streamListener, status, callback); + return super.upload(write, file, local, throttle, progress, streamListener, status, callback); } catch(InteroperabilityException e) { if(!session.getSignatureVersion().equals(signatureVersion)) { // Retry if upload fails with Header "x-amz-content-sha256" set to the hex-encoded SHA256 hash of the // request payload is required for AWS Version 4 request signing - return this.upload(file, local, throttle, progress, streamListener, status, callback); + return this.upload(write, file, local, throttle, progress, streamListener, status, callback); } throw e; } @@ -113,10 +109,4 @@ public class S3SingleUploadService extends HttpUploadFeature withWriter(final Write writer) { - this.writer = writer; - return super.withWriter(writer); - } } diff --git a/s3/src/main/java/ch/cyberduck/core/s3/S3StorageClassFeature.java b/s3/src/main/java/ch/cyberduck/core/s3/S3StorageClassFeature.java index e3be235040..c785dd83a6 100644 --- a/s3/src/main/java/ch/cyberduck/core/s3/S3StorageClassFeature.java +++ b/s3/src/main/java/ch/cyberduck/core/s3/S3StorageClassFeature.java @@ -41,17 +41,17 @@ public class S3StorageClassFeature implements Redundancy { public S3StorageClassFeature(final S3Session session, final S3AccessControlListFeature acl) { this.session = session; - this.containerService = session.getFeature(PathContainerService.class); + this.containerService = new S3PathContainerService(session.getHost()); this.acl = acl; } @Override - public String getDefault() { + public String getDefault(final Path file) { return HostPreferencesFactory.get(session.getHost()).getProperty("s3.storage.class"); } @Override - public Set getClasses() { + public Set getClasses(final Path file) { return new LinkedHashSet<>(HostPreferencesFactory.get(session.getHost()).getList("s3.storage.class.options")); } diff --git a/s3/src/main/java/ch/cyberduck/core/s3/S3ThresholdCopyFeature.java b/s3/src/main/java/ch/cyberduck/core/s3/S3ThresholdCopyFeature.java index 264a766618..b24a9d9f70 100644 --- a/s3/src/main/java/ch/cyberduck/core/s3/S3ThresholdCopyFeature.java +++ b/s3/src/main/java/ch/cyberduck/core/s3/S3ThresholdCopyFeature.java @@ -47,7 +47,7 @@ public class S3ThresholdCopyFeature implements Copy { this.session = session; this.accessControlListFeature = accessControlListFeature; this.multipartThreshold = HostPreferencesFactory.get(session.getHost()).getLong("s3.upload.multipart.required.threshold"); - this.containerService = session.getFeature(PathContainerService.class); + this.containerService = new S3PathContainerService(session.getHost()); } public Path copy(final Path source, final Path copy, final TransferStatus status, final ConnectionCallback callback, final StreamListener listener) throws BackgroundException { diff --git a/s3/src/main/java/ch/cyberduck/core/s3/S3ThresholdUploadService.java b/s3/src/main/java/ch/cyberduck/core/s3/S3ThresholdUploadService.java index 28eb61849e..e1026a1073 100644 --- a/s3/src/main/java/ch/cyberduck/core/s3/S3ThresholdUploadService.java +++ b/s3/src/main/java/ch/cyberduck/core/s3/S3ThresholdUploadService.java @@ -43,8 +43,6 @@ public class S3ThresholdUploadService implements Upload { private final S3AccessControlListFeature acl; private final Long threshold; - private Write writer; - public S3ThresholdUploadService(final S3Session session, final S3AccessControlListFeature acl) { this(session, acl, HostPreferencesFactory.get(session.getHost()).getLong("s3.upload.multipart.threshold")); } @@ -53,29 +51,28 @@ public class S3ThresholdUploadService implements Upload { this.session = session; this.acl = acl; this.threshold = threshold; - this.writer = new S3WriteFeature(session, acl); } @Override public Write.Append append(final Path file, final TransferStatus status) throws BackgroundException { if(this.threshold(status)) { - return new S3MultipartUploadService(session, writer, acl).append(file, status); + return new S3MultipartUploadService(session, acl).append(file, status); } return new Write.Append(false).withStatus(status); } @Override - public StorageObject upload(final Path file, Local local, final BandwidthThrottle throttle, final ProgressListener progress, final StreamListener streamListener, + public StorageObject upload(final Write write, final Path file, Local local, final BandwidthThrottle throttle, final ProgressListener progress, final StreamListener streamListener, final TransferStatus status, final ConnectionCallback prompt) throws BackgroundException { if(this.threshold(status)) { try { - return new S3MultipartUploadService(session, writer, acl).upload(file, local, throttle, progress, streamListener, status, prompt); + return new S3MultipartUploadService(session, acl).upload(write, file, local, throttle, progress, streamListener, status, prompt); } catch(NotfoundException | InteroperabilityException e) { log.warn("Failure {} using multipart upload. Fallback to single upload.", e.getMessage()); status.setAppend(false); try { - return new S3SingleUploadService(session, writer).upload(file, local, throttle, progress, streamListener, status, prompt); + return new S3SingleUploadService(session).upload(write, file, local, throttle, progress, streamListener, status, prompt); } catch(BackgroundException f) { log.warn("Failure {} using single upload. Throw original multipart failure {}", e, e); @@ -84,7 +81,7 @@ public class S3ThresholdUploadService implements Upload { } } // Use single upload service - return new S3SingleUploadService(session, writer).upload(file, local, throttle, progress, streamListener, status, prompt); + return new S3SingleUploadService(session).upload(write, file, local, throttle, progress, streamListener, status, prompt); } protected boolean threshold(final TransferStatus status) { @@ -101,9 +98,4 @@ public class S3ThresholdUploadService implements Upload { return false; } - @Override - public Upload withWriter(final Write writer) { - this.writer = writer; - return this; - } } diff --git a/s3/src/main/java/ch/cyberduck/core/s3/S3TouchFeature.java b/s3/src/main/java/ch/cyberduck/core/s3/S3TouchFeature.java index ae95b4eb3f..4e0f148bd4 100644 --- a/s3/src/main/java/ch/cyberduck/core/s3/S3TouchFeature.java +++ b/s3/src/main/java/ch/cyberduck/core/s3/S3TouchFeature.java @@ -22,6 +22,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; @@ -36,13 +37,13 @@ public class S3TouchFeature extends DefaultTouchFeature { private final S3Session session; public S3TouchFeature(final S3Session session, final S3AccessControlListFeature acl) { - super(new S3WriteFeature(session, acl)); + super(session); this.session = 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 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 diff --git a/s3/src/main/java/ch/cyberduck/core/s3/S3TransferAccelerationService.java b/s3/src/main/java/ch/cyberduck/core/s3/S3TransferAccelerationService.java index 8cedd4a4e2..9c14d4a809 100644 --- a/s3/src/main/java/ch/cyberduck/core/s3/S3TransferAccelerationService.java +++ b/s3/src/main/java/ch/cyberduck/core/s3/S3TransferAccelerationService.java @@ -41,7 +41,7 @@ public class S3TransferAccelerationService implements TransferAcceleration { public S3TransferAccelerationService(final S3Session session) { this.session = session; - this.containerService = session.getFeature(PathContainerService.class); + this.containerService = new S3PathContainerService(session.getHost()); } @Override diff --git a/s3/src/main/java/ch/cyberduck/core/s3/S3UrlProvider.java b/s3/src/main/java/ch/cyberduck/core/s3/S3UrlProvider.java index fed52a1d09..61bcbc6c42 100644 --- a/s3/src/main/java/ch/cyberduck/core/s3/S3UrlProvider.java +++ b/s3/src/main/java/ch/cyberduck/core/s3/S3UrlProvider.java @@ -17,6 +17,7 @@ package ch.cyberduck.core.s3; * Bug fixes, suggestions and comments should be sent to feedback@cyberduck.ch */ +import ch.cyberduck.core.Credentials; import ch.cyberduck.core.DescriptiveUrl; import ch.cyberduck.core.DescriptiveUrlBag; import ch.cyberduck.core.HostWebUrlProvider; @@ -60,7 +61,7 @@ public class S3UrlProvider implements UrlProvider { public S3UrlProvider(final S3Session session, final Map> distributions) { this.session = session; this.distributions = distributions; - this.containerService = session.getFeature(PathContainerService.class); + this.containerService = new S3PathContainerService(session.getHost()); } @Override @@ -202,23 +203,20 @@ public class S3UrlProvider implements UrlProvider { @Override public String getUrl() { - final String secret; + final Credentials credentials; try { - secret = session.getAuthentication().get().getPassword(); + credentials = session.getAuthentication().get(); } catch(BackgroundException e) { log.error("Failure retrieving secret required to sign temporary URL", e); return DescriptiveUrl.EMPTY.getUrl(); } - if(StringUtils.isBlank(secret)) { - log.error("No secret found in password store required to sign temporary URL"); - return DescriptiveUrl.EMPTY.getUrl(); - } String region = session.getHost().getRegion(); final Path bucket = containerService.getContainer(file); + final String bucketname = bucket.isRoot() ? RequestEntityRestStorageService.findBucketInHostname(session.getHost()) : bucket.getName(); if(session.isConnected()) { - if(session.getClient().getRegionEndpointCache().containsRegionForBucketName(bucket.getName())) { - region = session.getClient().getRegionEndpointCache().getRegionForBucketName(bucket.getName()); + if(session.getClient().getRegionEndpointCache().containsRegionForBucketName(bucketname)) { + region = session.getClient().getRegionEndpointCache().getRegionForBucketName(bucketname); } } if(StringUtils.isBlank(region)) { @@ -230,8 +228,8 @@ public class S3UrlProvider implements UrlProvider { } } return new S3PresignedUrlProvider(session).create( - secret, - bucket.isRoot() ? RequestEntityRestStorageService.findBucketInHostname(session.getHost()) : bucket.getName(), + credentials, + bucketname, region, containerService.getKey(file), "GET", expiry.getTimeInMillis()); } diff --git a/s3/src/main/java/ch/cyberduck/core/s3/S3VersionedObjectListService.java b/s3/src/main/java/ch/cyberduck/core/s3/S3VersionedObjectListService.java index f451a6fb88..d62b1a13fd 100644 --- a/s3/src/main/java/ch/cyberduck/core/s3/S3VersionedObjectListService.java +++ b/s3/src/main/java/ch/cyberduck/core/s3/S3VersionedObjectListService.java @@ -16,6 +16,7 @@ package ch.cyberduck.core.s3; */ import ch.cyberduck.core.AttributedList; +import ch.cyberduck.core.DefaultPathAttributes; import ch.cyberduck.core.ListProgressListener; import ch.cyberduck.core.ListService; import ch.cyberduck.core.Path; @@ -45,21 +46,17 @@ import org.jets3t.service.model.S3Version; import java.util.ArrayList; import java.util.Arrays; -import java.util.Collections; import java.util.EnumSet; import java.util.List; import java.util.concurrent.ExecutionException; import java.util.concurrent.Future; import com.google.common.base.Throwables; -import com.google.common.collect.ImmutableMap; import com.google.common.util.concurrent.Uninterruptibles; public class S3VersionedObjectListService extends S3AbstractListService implements ListService { private static final Logger log = LogManager.getLogger(S3VersionedObjectListService.class); - public static final String KEY_DELETE_MARKER = "delete_marker"; - private final PathContainerService containerService; private final S3Session session; private final S3AttributesFinderFeature attributes; @@ -88,7 +85,7 @@ public class S3VersionedObjectListService extends S3AbstractListService implemen this.session = session; this.attributes = new S3AttributesFinderFeature(session, acl); this.concurrency = concurrency; - this.containerService = session.getFeature(PathContainerService.class); + this.containerService = new S3PathContainerService(session.getHost()); this.metadata = metadata; } @@ -118,17 +115,15 @@ public class S3VersionedObjectListService extends S3AbstractListService implemen hasDirectoryPlaceholder = true; continue; } - final PathAttributes attr = new PathAttributes(); + final PathAttributes attr = new DefaultPathAttributes(); attr.setVersionId(marker.getVersionId()); if(!StringUtils.equals(lastKey, key)) { // Reset revision for next file revision = 0L; } attr.setRevision(++revision); - attr.setDuplicate(marker.isDeleteMarker() && marker.isLatest() || !marker.isLatest()); - if(marker.isDeleteMarker()) { - attr.setCustom(Collections.singletonMap(KEY_DELETE_MARKER, String.valueOf(true))); - } + attr.setDuplicate(!marker.isLatest()); + attr.setTrashed(marker.isDeleteMarker()); attr.setModificationDate(marker.getLastModified().getTime()); attr.setRegion(bucket.attributes().getRegion()); if(marker instanceof S3Version) { @@ -162,11 +157,14 @@ public class S3VersionedObjectListService extends S3AbstractListService implemen if(new SimplePathPredicate(PathNormalizer.compose(bucket, URIEncoder.decode(common))).test(directory)) { continue; } + log.debug("Handle common prefix {}", common); folders.add(this.submit(pool, bucket, directory, URIEncoder.decode(common))); } for(Future f : folders) { try { - objects.add(Uninterruptibles.getUninterruptibly(f)); + final Path resolved = Uninterruptibles.getUninterruptibly(f); + log.debug("Resolved common prefix {}", resolved); + objects.add(resolved); } catch(ExecutionException e) { log.warn("Listing versioned objects failed with execution failure {}", e.getMessage()); @@ -221,9 +219,9 @@ public class S3VersionedObjectListService extends S3AbstractListService implemen return pool.execute(new BackgroundExceptionCallable() { @Override public Path call() throws BackgroundException { - final PathAttributes attr = new PathAttributes(); + final PathAttributes attr = new DefaultPathAttributes(); attr.setRegion(bucket.attributes().getRegion()); - final String key = StringUtils.chomp(prefix, String.valueOf(Path.DELIMITER)); + final String key = StringUtils.removeEnd(prefix, String.valueOf(Path.DELIMITER)); try { final VersionOrDeleteMarkersChunk versions = session.getClient().listVersionedObjectsChunked( bucket.isRoot() ? StringUtils.EMPTY : bucket.getName(), prefix, null, 1, @@ -233,7 +231,8 @@ public class S3VersionedObjectListService extends S3AbstractListService implemen if(URIEncoder.decode(version.getKey()).equals(prefix)) { attr.setVersionId(version.getVersionId()); if(version.isDeleteMarker()) { - attr.setCustom(ImmutableMap.of(KEY_DELETE_MARKER, Boolean.TRUE.toString())); + log.debug("Set trashed attribute for prefix {}", key); + attr.setTrashed(true); } } // No placeholder but objects inside; need to check if all of them are deleted @@ -241,6 +240,7 @@ public class S3VersionedObjectListService extends S3AbstractListService implemen bucket.isRoot() ? StringUtils.EMPTY : bucket.getName(), prefix, null, 1, null, false); if(unversioned.getObjects().length == 0) { + log.debug("Set duplicate attribute for prefix {}", key); attr.setDuplicate(true); } } diff --git a/s3/src/main/java/ch/cyberduck/core/s3/S3VersioningFeature.java b/s3/src/main/java/ch/cyberduck/core/s3/S3VersioningFeature.java index fcaf924151..ece26c9153 100644 --- a/s3/src/main/java/ch/cyberduck/core/s3/S3VersioningFeature.java +++ b/s3/src/main/java/ch/cyberduck/core/s3/S3VersioningFeature.java @@ -61,7 +61,7 @@ public class S3VersioningFeature implements Versioning { public S3VersioningFeature(final S3Session session, final S3AccessControlListFeature acl) { this.session = session; this.acl = acl; - this.containerService = session.getFeature(PathContainerService.class); + this.containerService = new S3PathContainerService(session.getHost()); } @Override @@ -180,7 +180,7 @@ public class S3VersioningFeature implements Versioning { // Apply non standard ACL final Acl list = acl.getPermission(file); if(list.isEditable()) { - destination.setAcl(acl.toAcl(list)); + destination.setAcl(S3AccessControlListFeature.toAcl(list)); } } catch(AccessDeniedException | InteroperabilityException e) { @@ -190,7 +190,7 @@ public class S3VersioningFeature implements Versioning { final String bucketname = bucket.isRoot() ? RequestEntityRestStorageService.findBucketInHostname(session.getHost()) : bucket.getName(); session.getClient().copyVersionedObject(file.attributes().getVersionId(), bucketname, containerService.getKey(file), bucketname, destination, false); - if(file.getParent().attributes().getCustom().containsKey(S3VersionedObjectListService.KEY_DELETE_MARKER)) { + if(file.getParent().attributes().isTrashed()) { // revert placeholder session.getClient().deleteVersionedObject( file.getParent().attributes().getVersionId(), diff --git a/s3/src/main/java/ch/cyberduck/core/s3/S3WriteFeature.java b/s3/src/main/java/ch/cyberduck/core/s3/S3WriteFeature.java index 70953a2324..fb439d625e 100644 --- a/s3/src/main/java/ch/cyberduck/core/s3/S3WriteFeature.java +++ b/s3/src/main/java/ch/cyberduck/core/s3/S3WriteFeature.java @@ -63,7 +63,7 @@ public class S3WriteFeature extends AbstractHttpWriteFeature impl public S3WriteFeature(final S3Session session, final S3AccessControlListFeature acl) { super(new S3AttributesAdapter(session.getHost())); this.session = session; - this.containerService = session.getFeature(PathContainerService.class); + this.containerService = new S3PathContainerService(session.getHost()); this.acl = acl; } @@ -130,7 +130,7 @@ public class S3WriteFeature extends AbstractHttpWriteFeature impl if(!Acl.EMPTY.equals(status.getAcl())) { if(status.getAcl().isCanned()) { log.debug("Set canned ACL {} for {}", status.getAcl(), file); - object.setAcl(acl.toAcl(status.getAcl())); + object.setAcl(S3AccessControlListFeature.toAcl(status.getAcl())); // Reset in status to skip setting ACL in upload filter already applied as canned ACL status.setAcl(Acl.EMPTY); } diff --git a/s3/src/main/java/ch/cyberduck/core/sts/STSAssumeRoleAuthorizationService.java b/s3/src/main/java/ch/cyberduck/core/sts/STSAssumeRoleAuthorizationService.java deleted file mode 100644 index 7c34dc3d96..0000000000 --- a/s3/src/main/java/ch/cyberduck/core/sts/STSAssumeRoleAuthorizationService.java +++ /dev/null @@ -1,169 +0,0 @@ -package ch.cyberduck.core.sts; - -/* - * Copyright (c) 2002-2023 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 ch.cyberduck.core.AsciiRandomStringService; -import ch.cyberduck.core.Credentials; -import ch.cyberduck.core.Host; -import ch.cyberduck.core.LocaleFactory; -import ch.cyberduck.core.LoginCallback; -import ch.cyberduck.core.LoginOptions; -import ch.cyberduck.core.OAuthTokens; -import ch.cyberduck.core.TemporaryAccessTokens; -import ch.cyberduck.core.aws.CustomClientConfiguration; -import ch.cyberduck.core.exception.BackgroundException; -import ch.cyberduck.core.exception.LoginFailureException; -import ch.cyberduck.core.preferences.HostPreferences; -import ch.cyberduck.core.preferences.HostPreferencesFactory; -import ch.cyberduck.core.ssl.ThreadLocalHostnameDelegatingTrustManager; -import ch.cyberduck.core.ssl.X509KeyManager; -import ch.cyberduck.core.ssl.X509TrustManager; - -import org.apache.commons.lang3.StringUtils; -import org.apache.logging.log4j.LogManager; -import org.apache.logging.log4j.Logger; - -import com.amazonaws.auth.AWSStaticCredentialsProvider; -import com.amazonaws.auth.AnonymousAWSCredentials; -import com.amazonaws.client.builder.AwsClientBuilder; -import com.amazonaws.services.securitytoken.AWSSecurityTokenService; -import com.amazonaws.services.securitytoken.AWSSecurityTokenServiceClientBuilder; -import com.amazonaws.services.securitytoken.model.AWSSecurityTokenServiceException; -import com.amazonaws.services.securitytoken.model.AssumeRoleWithSAMLRequest; -import com.amazonaws.services.securitytoken.model.AssumeRoleWithSAMLResult; -import com.amazonaws.services.securitytoken.model.AssumeRoleWithWebIdentityRequest; -import com.amazonaws.services.securitytoken.model.AssumeRoleWithWebIdentityResult; -import com.auth0.jwt.JWT; -import com.auth0.jwt.exceptions.JWTDecodeException; - -public class STSAssumeRoleAuthorizationService { - private static final Logger log = LogManager.getLogger(STSAssumeRoleAuthorizationService.class); - - private final AWSSecurityTokenService service; - private final LoginCallback prompt; - private final Host bookmark; - - public STSAssumeRoleAuthorizationService(final Host bookmark, final X509TrustManager trust, final X509KeyManager key, final LoginCallback prompt) { - this(bookmark, AWSSecurityTokenServiceClientBuilder - .standard() - .withEndpointConfiguration(new AwsClientBuilder.EndpointConfiguration(bookmark.getProtocol().getSTSEndpoint(), null)) - .withCredentials(new AWSStaticCredentialsProvider(new AnonymousAWSCredentials())) - .withClientConfiguration(new CustomClientConfiguration(bookmark, - new ThreadLocalHostnameDelegatingTrustManager(trust, bookmark.getProtocol().getSTSEndpoint()), key)) - .build(), prompt); - } - - public STSAssumeRoleAuthorizationService(final Host bookmark, final AWSSecurityTokenService service, final LoginCallback prompt) { - this.bookmark = bookmark; - this.service = service; - this.prompt = prompt; - } - - public TemporaryAccessTokens authorize(final String sAMLAssertion) throws BackgroundException { - final AssumeRoleWithSAMLRequest request = new AssumeRoleWithSAMLRequest().withSAMLAssertion(sAMLAssertion); - final HostPreferences preferences = HostPreferencesFactory.get(bookmark); - if(preferences.getInteger("s3.assumerole.durationseconds") != -1) { - request.setDurationSeconds(preferences.getInteger("s3.assumerole.durationseconds")); - } - if(StringUtils.isNotBlank(preferences.getProperty("s3.assumerole.policy"))) { - request.setPolicy(preferences.getProperty("s3.assumerole.policy")); - } - if(StringUtils.isNotBlank(preferences.getProperty("s3.assumerole.rolearn"))) { - request.setRoleArn(preferences.getProperty("s3.assumerole.rolearn")); - } - try { - final AssumeRoleWithSAMLResult result = service.assumeRoleWithSAML(request); - log.debug("Received assume role identity result {}", result); - final Credentials credentials = bookmark.getCredentials(); - final TemporaryAccessTokens tokens = new TemporaryAccessTokens(result.getCredentials().getAccessKeyId(), - result.getCredentials().getSecretAccessKey(), - result.getCredentials().getSessionToken(), - result.getCredentials().getExpiration().getTime()); - credentials.setTokens(tokens); - return tokens; - } - catch(AWSSecurityTokenServiceException e) { - throw new STSExceptionMappingService().map(e); - } - } - - public TemporaryAccessTokens authorize(final OAuthTokens oauth) throws BackgroundException { - final AssumeRoleWithWebIdentityRequest request = new AssumeRoleWithWebIdentityRequest(); - log.debug("Assume role with OIDC Id token for {}", bookmark); - final String webIdentityToken = this.getWebIdentityToken(oauth); - request.setWebIdentityToken(webIdentityToken); - final HostPreferences preferences = HostPreferencesFactory.get(bookmark); - if(preferences.getInteger("s3.assumerole.durationseconds") != -1) { - request.setDurationSeconds(preferences.getInteger("s3.assumerole.durationseconds")); - } - if(StringUtils.isNotBlank(preferences.getProperty("s3.assumerole.policy"))) { - request.setPolicy(preferences.getProperty("s3.assumerole.policy")); - } - if(StringUtils.isNotBlank(preferences.getProperty("s3.assumerole.rolearn"))) { - request.setRoleArn(preferences.getProperty("s3.assumerole.rolearn")); - } - else { - if(StringUtils.EMPTY.equals(preferences.getProperty("s3.assumerole.rolearn"))) { - // When defined in connection profile but with empty value - log.debug("Prompt for Role ARN"); - final Credentials input = prompt.prompt(bookmark, - LocaleFactory.localizedString("Role Amazon Resource Name (ARN)", "Credentials"), - LocaleFactory.localizedString("Provide additional login credentials", "Credentials"), - new LoginOptions().icon(bookmark.getProtocol().disk())); - if(input.isSaved()) { - preferences.setProperty("s3.assumerole.rolearn", input.getPassword()); - } - request.setRoleArn(input.getPassword()); - } - } - final String sub; - try { - sub = JWT.decode(webIdentityToken).getSubject(); - } - catch(JWTDecodeException e) { - log.warn("Failure {} decoding JWT {}", e, webIdentityToken); - throw new LoginFailureException("Invalid JWT or JSON format in authentication token", e); - } - if(StringUtils.isNotBlank(preferences.getProperty("s3.assumerole.rolesessionname"))) { - request.setRoleSessionName(preferences.getProperty("s3.assumerole.rolesessionname")); - } - else { - if(StringUtils.isNotBlank(sub)) { - request.setRoleSessionName(sub); - } - else { - log.warn("Missing subject in decoding JWT {}", webIdentityToken); - request.setRoleSessionName(new AsciiRandomStringService().random()); - } - } - try { - log.debug("Use request {}", request); - final AssumeRoleWithWebIdentityResult result = service.assumeRoleWithWebIdentity(request); - log.debug("Received assume role identity result {}", result); - return new TemporaryAccessTokens(result.getCredentials().getAccessKeyId(), - result.getCredentials().getSecretAccessKey(), - result.getCredentials().getSessionToken(), - result.getCredentials().getExpiration().getTime()); - } - catch(AWSSecurityTokenServiceException e) { - throw new STSExceptionMappingService().map(e); - } - } - - protected String getWebIdentityToken(final OAuthTokens oauth) { - return oauth.getIdToken(); - } -} diff --git a/s3/src/main/java/ch/cyberduck/core/sts/STSAssumeRoleCredentialsRequestInterceptor.java b/s3/src/main/java/ch/cyberduck/core/sts/STSAssumeRoleCredentialsRequestInterceptor.java deleted file mode 100644 index b810706de1..0000000000 --- a/s3/src/main/java/ch/cyberduck/core/sts/STSAssumeRoleCredentialsRequestInterceptor.java +++ /dev/null @@ -1,134 +0,0 @@ -package ch.cyberduck.core.sts; - -/* - * Copyright (c) 2002-2023 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 ch.cyberduck.core.Credentials; -import ch.cyberduck.core.LoginCallback; -import ch.cyberduck.core.OAuthTokens; -import ch.cyberduck.core.TemporaryAccessTokens; -import ch.cyberduck.core.exception.BackgroundException; -import ch.cyberduck.core.exception.LoginFailureException; -import ch.cyberduck.core.oauth.OAuth2RequestInterceptor; -import ch.cyberduck.core.s3.S3CredentialsStrategy; -import ch.cyberduck.core.s3.S3Session; -import ch.cyberduck.core.ssl.X509KeyManager; -import ch.cyberduck.core.ssl.X509TrustManager; - -import org.apache.http.HttpException; -import org.apache.http.HttpRequest; -import org.apache.http.HttpRequestInterceptor; -import org.apache.http.protocol.HttpContext; -import org.apache.logging.log4j.LogManager; -import org.apache.logging.log4j.Logger; -import org.jets3t.service.security.AWSSessionCredentials; - -import java.io.IOException; -import java.util.concurrent.locks.ReentrantLock; - -import com.amazonaws.services.securitytoken.AWSSecurityTokenService; -import com.auth0.jwt.JWT; -import com.auth0.jwt.exceptions.JWTDecodeException; - -/** - * Swap OIDC Id token for temporary security credentials - */ -public class STSAssumeRoleCredentialsRequestInterceptor extends STSAssumeRoleAuthorizationService implements S3CredentialsStrategy, HttpRequestInterceptor { - private static final Logger log = LogManager.getLogger(STSAssumeRoleCredentialsRequestInterceptor.class); - - private final ReentrantLock lock = new ReentrantLock(); - - /** - * Currently valid tokens - */ - private TemporaryAccessTokens tokens = TemporaryAccessTokens.EMPTY; - - /** - * Handle authentication with OpenID connect retrieving token for STS - */ - private final OAuth2RequestInterceptor oauth; - private final S3Session session; - - public STSAssumeRoleCredentialsRequestInterceptor(final OAuth2RequestInterceptor oauth, final S3Session session, - final X509TrustManager trust, final X509KeyManager key, - final LoginCallback prompt) { - super(session.getHost(), trust, key, prompt); - this.oauth = oauth; - this.session = session; - } - - public STSAssumeRoleCredentialsRequestInterceptor(final OAuth2RequestInterceptor oauth, final S3Session session, - final AWSSecurityTokenService service, final LoginCallback prompt) { - super(session.getHost(), service, prompt); - this.oauth = oauth; - this.session = session; - } - - public TemporaryAccessTokens refresh(final OAuthTokens oidc) throws BackgroundException { - lock.lock(); - try { - return this.tokens = this.authorize(oidc); - } - catch(LoginFailureException e) { - // Expired STS tokens - log.warn("Failure {} authorizing. Retry with refreshed OAuth tokens", e.getMessage()); - return this.tokens = this.authorize(oauth.refresh(oidc)); - } - finally { - lock.unlock(); - } - } - - @Override - public void process(final HttpRequest request, final HttpContext context) throws HttpException, IOException { - lock.lock(); - try { - if(tokens.isExpired()) { - try { - this.refresh(oauth.getTokens()); - log.info("Authorizing service request with STS tokens {}", tokens); - session.getClient().setProviderCredentials(new AWSSessionCredentials(tokens.getAccessKeyId(), tokens.getSecretAccessKey(), - tokens.getSessionToken())); - } - catch(BackgroundException e) { - log.warn("Failure {} refreshing STS tokens {}", e, tokens); - // Follow-up error 401 handled in error interceptor - } - } - } - finally { - lock.unlock(); - } - } - - @Override - public Credentials get() throws BackgroundException { - // Get temporary credentials from STS using Web Identity (OIDC) token - final Credentials credentials = oauth.validate(); - final OAuthTokens identity = credentials.getOauth(); - final String token = this.getWebIdentityToken(identity); - final String sub; - try { - sub = JWT.decode(token).getSubject(); - } - catch(JWTDecodeException e) { - throw new LoginFailureException("Invalid JWT or JSON format in authentication token", e); - } - final TemporaryAccessTokens tokens = this.refresh(identity); - return credentials - .withUsername(sub) - .withTokens(tokens); - } -} diff --git a/s3/src/main/java/ch/cyberduck/core/sts/STSAssumeRoleCredentialsStrategy.java b/s3/src/main/java/ch/cyberduck/core/sts/STSAssumeRoleCredentialsStrategy.java new file mode 100644 index 0000000000..89be40310f --- /dev/null +++ b/s3/src/main/java/ch/cyberduck/core/sts/STSAssumeRoleCredentialsStrategy.java @@ -0,0 +1,51 @@ +package ch.cyberduck.core.sts; + +/* + * Copyright (c) 2002-2025 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 ch.cyberduck.core.Credentials; +import ch.cyberduck.core.Host; +import ch.cyberduck.core.LoginCallback; +import ch.cyberduck.core.Profile; +import ch.cyberduck.core.TemporaryAccessTokens; +import ch.cyberduck.core.exception.BackgroundException; +import ch.cyberduck.core.preferences.ProxyPreferencesReader; +import ch.cyberduck.core.s3.S3CredentialsStrategy; +import ch.cyberduck.core.ssl.X509KeyManager; +import ch.cyberduck.core.ssl.X509TrustManager; + +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; + +/** + * Swap static access key id and secret access key with temporary credentials obtained from STS AssumeRole + */ +public class STSAssumeRoleCredentialsStrategy extends STSCredentialsStrategy implements S3CredentialsStrategy { + private static final Logger log = LogManager.getLogger(STSAssumeRoleCredentialsStrategy.class); + + private final Host host; + + public STSAssumeRoleCredentialsStrategy(final Host host, final X509TrustManager trust, final X509KeyManager key, final LoginCallback prompt) { + super(host, trust, key, prompt); + this.host = host; + } + + @Override + public TemporaryAccessTokens refresh(final Credentials credentials) throws BackgroundException { + final String arn = new ProxyPreferencesReader(host, credentials).getProperty(Profile.STS_ROLE_ARN_PROPERTY_KEY, "s3.assumerole.rolearn"); + log.debug("Retrieve temporary credentials with {} for role ARN {}", credentials, arn); + return this.assumeRole(credentials, arn); + } +} diff --git a/s3/src/main/java/ch/cyberduck/core/sts/STSAssumeRoleWithWebIdentityCredentialsStrategy.java b/s3/src/main/java/ch/cyberduck/core/sts/STSAssumeRoleWithWebIdentityCredentialsStrategy.java new file mode 100644 index 0000000000..a73171370b --- /dev/null +++ b/s3/src/main/java/ch/cyberduck/core/sts/STSAssumeRoleWithWebIdentityCredentialsStrategy.java @@ -0,0 +1,59 @@ +package ch.cyberduck.core.sts; + +/* + * Copyright (c) 2002-2023 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 ch.cyberduck.core.Credentials; +import ch.cyberduck.core.Host; +import ch.cyberduck.core.LoginCallback; +import ch.cyberduck.core.Profile; +import ch.cyberduck.core.TemporaryAccessTokens; +import ch.cyberduck.core.exception.BackgroundException; +import ch.cyberduck.core.oauth.OAuth2RequestInterceptor; +import ch.cyberduck.core.preferences.ProxyPreferencesReader; +import ch.cyberduck.core.s3.S3CredentialsStrategy; +import ch.cyberduck.core.ssl.X509KeyManager; +import ch.cyberduck.core.ssl.X509TrustManager; + +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; + +/** + * Swap OIDC Id token for temporary security credentials + */ +public class STSAssumeRoleWithWebIdentityCredentialsStrategy extends STSCredentialsStrategy implements S3CredentialsStrategy { + private static final Logger log = LogManager.getLogger(STSAssumeRoleWithWebIdentityCredentialsStrategy.class); + + /** + * Handle authentication with OpenID connect retrieving token for STS + */ + private final OAuth2RequestInterceptor oauth; + private final Host host; + + public STSAssumeRoleWithWebIdentityCredentialsStrategy(final OAuth2RequestInterceptor oauth, final Host host, + final X509TrustManager trust, final X509KeyManager key, + final LoginCallback prompt) { + super(host, trust, key, prompt); + this.oauth = oauth; + this.host = host; + } + + @Override + public TemporaryAccessTokens refresh(final Credentials credentials) throws BackgroundException { + final String arn = new ProxyPreferencesReader(host, credentials).getProperty(Profile.STS_ROLE_ARN_PROPERTY_KEY, "s3.assumerole.rolearn"); + log.debug("Retrieve temporary credentials with {} for role ARN {}", credentials, arn); + return this.assumeRoleWithWebIdentity(oauth.validate(credentials.getOauth()), arn); + } +} \ No newline at end of file diff --git a/s3/src/main/java/ch/cyberduck/core/sts/STSAuthorizationService.java b/s3/src/main/java/ch/cyberduck/core/sts/STSAuthorizationService.java new file mode 100644 index 0000000000..3d45b87025 --- /dev/null +++ b/s3/src/main/java/ch/cyberduck/core/sts/STSAuthorizationService.java @@ -0,0 +1,369 @@ +package ch.cyberduck.core.sts; + +/* + * Copyright (c) 2002-2023 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 ch.cyberduck.core.AsciiRandomStringService; +import ch.cyberduck.core.Credentials; +import ch.cyberduck.core.Host; +import ch.cyberduck.core.LocaleFactory; +import ch.cyberduck.core.LoginOptions; +import ch.cyberduck.core.OAuthTokens; +import ch.cyberduck.core.PasswordCallback; +import ch.cyberduck.core.Profile; +import ch.cyberduck.core.TemporaryAccessTokens; +import ch.cyberduck.core.aws.CustomClientConfiguration; +import ch.cyberduck.core.exception.BackgroundException; +import ch.cyberduck.core.exception.LoginCanceledException; +import ch.cyberduck.core.exception.LoginFailureException; +import ch.cyberduck.core.preferences.HostPreferences; +import ch.cyberduck.core.preferences.HostPreferencesFactory; +import ch.cyberduck.core.preferences.PreferencesReader; +import ch.cyberduck.core.preferences.ProxyPreferencesReader; +import ch.cyberduck.core.s3.S3CredentialsStrategy; +import ch.cyberduck.core.ssl.ThreadLocalHostnameDelegatingTrustManager; +import ch.cyberduck.core.ssl.X509KeyManager; +import ch.cyberduck.core.ssl.X509TrustManager; + +import org.apache.commons.lang3.StringUtils; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; + +import java.util.stream.Collectors; + +import com.amazonaws.auth.AWSStaticCredentialsProvider; +import com.amazonaws.auth.AnonymousAWSCredentials; +import com.amazonaws.client.builder.AwsClientBuilder; +import com.amazonaws.services.securitytoken.AWSSecurityTokenService; +import com.amazonaws.services.securitytoken.AWSSecurityTokenServiceClientBuilder; +import com.amazonaws.services.securitytoken.model.AWSSecurityTokenServiceException; +import com.amazonaws.services.securitytoken.model.AssumeRoleRequest; +import com.amazonaws.services.securitytoken.model.AssumeRoleResult; +import com.amazonaws.services.securitytoken.model.AssumeRoleWithSAMLRequest; +import com.amazonaws.services.securitytoken.model.AssumeRoleWithSAMLResult; +import com.amazonaws.services.securitytoken.model.AssumeRoleWithWebIdentityRequest; +import com.amazonaws.services.securitytoken.model.AssumeRoleWithWebIdentityResult; +import com.amazonaws.services.securitytoken.model.GetCallerIdentityRequest; +import com.amazonaws.services.securitytoken.model.GetCallerIdentityResult; +import com.amazonaws.services.securitytoken.model.GetSessionTokenRequest; +import com.amazonaws.services.securitytoken.model.GetSessionTokenResult; +import com.amazonaws.services.securitytoken.model.Tag; +import com.auth0.jwt.JWT; +import com.auth0.jwt.exceptions.JWTDecodeException; + +public class STSAuthorizationService { + private static final Logger log = LogManager.getLogger(STSAuthorizationService.class); + + private final AWSSecurityTokenService service; + private final PasswordCallback prompt; + private final Host bookmark; + private final HostPreferences preferences; + + public STSAuthorizationService(final Host bookmark, final X509TrustManager trust, final X509KeyManager key, final PasswordCallback prompt) { + this.bookmark = bookmark; + this.service = AWSSecurityTokenServiceClientBuilder + .standard() + .withEndpointConfiguration(new AwsClientBuilder.EndpointConfiguration(bookmark.getProtocol().getSTSEndpoint(), null)) + .withCredentials(new AWSStaticCredentialsProvider(new AnonymousAWSCredentials())) + .withClientConfiguration(new CustomClientConfiguration(bookmark, + new ThreadLocalHostnameDelegatingTrustManager(trust, bookmark.getProtocol().getSTSEndpoint()), key)) + .build(); + this.prompt = prompt; + this.preferences = HostPreferencesFactory.get(bookmark); + } + + /** + * Validate credentials by requesting caller identity + * + * @param credentials AWS credentials + * @return User ID + */ + public String getCallerIdentity(final Credentials credentials) throws BackgroundException { + try { + final GetCallerIdentityResult identity = service.getCallerIdentity(new GetCallerIdentityRequest() + .withRequestCredentialsProvider(S3CredentialsStrategy.toCredentialsProvider(credentials))); + log.debug("Successfully verified credentials for {}", identity); + return identity.getUserId(); + } + catch(AWSSecurityTokenServiceException e) { + throw new STSExceptionMappingService().map(e); + } + } + + public TemporaryAccessTokens getSessionToken(final Credentials credentials, final String mfaArn) throws BackgroundException { + log.debug("Get session token with credentials {} for {}", credentials, bookmark); + // The purpose of the sts:GetSessionToken operation is to authenticate the user using MFA. + final GetSessionTokenRequest request = new GetSessionTokenRequest() + .withRequestCredentialsProvider(S3CredentialsStrategy.toCredentialsProvider(credentials)); + if(StringUtils.isNotBlank(mfaArn)) { + log.debug("Found MFA ARN {} for {}", mfaArn, bookmark); + request.setSerialNumber(mfaArn); + } + else { + if(bookmark.getProtocol().isMultiFactorConfigurable()) { + // When defined in connection profile but with empty value + log.debug("Prompt for MFA ARN"); + try { + final Credentials input = prompt.prompt(bookmark, + LocaleFactory.localizedString("MFA Device Identifier", "S3"), + LocaleFactory.localizedString("Provide additional login credentials", "Credentials"), + new LoginOptions().icon(bookmark.getProtocol().disk()).password(false) + .passwordPlaceholder(LocaleFactory.localizedString("Serial Number or Amazon Resource Name (ARN)", "S3"))); + if(input.isSaved()) { + preferences.setProperty(Profile.STS_MFA_ARN_PROPERTY_KEY, input.getPassword()); + } + request.setSerialNumber(input.getPassword()); + } + catch(LoginCanceledException e) { + log.warn("Canceled MFA ARN input for {}", bookmark); + } + } + } + if(request.getSerialNumber() != null) { + log.debug("Prompt for MFA token code"); + final String tokenCode = prompt.prompt( + bookmark, String.format("%s %s", LocaleFactory.localizedString("Multi-Factor Authentication", "S3"), + mfaArn), + LocaleFactory.localizedString("Provide additional login credentials", "Credentials"), + new LoginOptions(bookmark.getProtocol()) + .password(true) + .passwordPlaceholder(LocaleFactory.localizedString("MFA Authentication Code", "S3")) + .keychain(false) + ).getPassword(); + if(StringUtils.isNotBlank(tokenCode)) { + request.setTokenCode(tokenCode); + } + } + log.debug("Request {} from {}", request, service); + try { + final GetSessionTokenResult result = service.getSessionToken(request); + log.debug("Set credentials from {}", result); + return new TemporaryAccessTokens( + result.getCredentials().getAccessKeyId(), + result.getCredentials().getSecretAccessKey(), + result.getCredentials().getSessionToken(), + result.getCredentials().getExpiration().getTime()); + } + catch(AWSSecurityTokenServiceException e) { + throw new STSExceptionMappingService().map(e); + } + } + + /** + * Assume role with previously obtained AWS credentials + *

+ * - Prompts for ARN for role to assume when missing + * - Prompts for MFA token code when required + * + * @param credentials AWS static or session token credentials + * @see Profile#STS_ROLE_ARN_PROPERTY_KEY + * @see Profile#STS_MFA_ARN_PROPERTY_KEY + */ + public TemporaryAccessTokens assumeRole(final Credentials credentials, final String roleArn) throws BackgroundException { + log.debug("Assume role with credentials {} for {}", credentials, bookmark); + final PreferencesReader settings = new ProxyPreferencesReader(bookmark, credentials); + final AssumeRoleRequest request = new AssumeRoleRequest() + .withRequestCredentialsProvider(S3CredentialsStrategy.toCredentialsProvider(credentials)); + if(StringUtils.isNotBlank(settings.getProperty("s3.assumerole.durationseconds", Profile.STS_DURATION_SECONDS_PROPERTY_KEY))) { + request.setDurationSeconds(PreferencesReader.toInteger(settings.getProperty("s3.assumerole.durationseconds", Profile.STS_DURATION_SECONDS_PROPERTY_KEY))); + } + request.setTags(settings.getMap(Profile.STS_TAGS_PROPERTY_KEY).entrySet().stream().map( + entry -> new Tag().withKey(entry.getKey()).withValue(entry.getValue())).collect(Collectors.toList()) + ); + if(StringUtils.isNotBlank(roleArn)) { + log.debug("Found Role ARN {} for {}", roleArn, bookmark); + request.setRoleArn(roleArn); + } + else { + if(bookmark.getProtocol().isRoleConfigurable()) { + // When defined in connection profile but with empty value + log.debug("Prompt for Role ARN"); + final Credentials input = prompt.prompt(bookmark, + LocaleFactory.localizedString("Role Amazon Resource Name (ARN)", "S3"), + LocaleFactory.localizedString("Provide additional login credentials", "Credentials"), + new LoginOptions().icon(bookmark.getProtocol().disk()).password(false) + .passwordPlaceholder(LocaleFactory.localizedString("Amazon Resource Name (ARN)", "S3"))); + if(input.isSaved()) { + preferences.setProperty(Profile.STS_ROLE_ARN_PROPERTY_KEY, input.getPassword()); + } + request.setRoleArn(input.getPassword()); + } + } + final String mfaArn = settings.getProperty(Profile.STS_MFA_ARN_PROPERTY_KEY); + if(StringUtils.isNotBlank(mfaArn)) { + log.debug("Found MFA ARN {} for {}", mfaArn, bookmark); + request.setSerialNumber(mfaArn); + } + else { + if(bookmark.getProtocol().isMultiFactorConfigurable()) { + // When defined in connection profile but with empty value + log.debug("Prompt for MFA ARN"); + try { + final Credentials input = prompt.prompt(bookmark, + LocaleFactory.localizedString("MFA Device Identifier", "S3"), + LocaleFactory.localizedString("Provide additional login credentials", "Credentials"), + new LoginOptions().icon(bookmark.getProtocol().disk()).password(false) + .passwordPlaceholder(LocaleFactory.localizedString("Serial Number or Amazon Resource Name (ARN)", "S3"))); + if(input.isSaved()) { + preferences.setProperty(Profile.STS_MFA_ARN_PROPERTY_KEY, input.getPassword()); + } + request.setSerialNumber(input.getPassword()); + } + catch(LoginCanceledException e) { + log.warn("Canceled MFA ARN input for {}", bookmark); + } + } + } + if(request.getSerialNumber() != null) { + log.debug("Prompt for MFA token code"); + final String tokenCode = prompt.prompt( + bookmark, String.format("%s %s", LocaleFactory.localizedString("Multi-Factor Authentication", "S3"), + mfaArn), + LocaleFactory.localizedString("Provide additional login credentials", "Credentials"), + new LoginOptions(bookmark.getProtocol()) + .password(true) + .passwordPlaceholder(LocaleFactory.localizedString("MFA Authentication Code", "S3")) + .keychain(false) + ).getPassword(); + if(StringUtils.isNotBlank(tokenCode)) { + request.setTokenCode(tokenCode); + } + } + if(StringUtils.isNotBlank(settings.getProperty(Profile.STS_ROLE_SESSION_NAME_PROPERTY_KEY, "s3.assumerole.rolesessionname"))) { + request.setRoleSessionName(settings.getProperty(Profile.STS_ROLE_SESSION_NAME_PROPERTY_KEY, "s3.assumerole.rolesessionname")); + } + else { + request.setRoleSessionName(new AsciiRandomStringService().random()); + } + log.debug("Request {} from {}", request, service); + try { + final AssumeRoleResult result = service.assumeRole(request); + log.debug("Received assume role identity result {}", result); + return new TemporaryAccessTokens(result.getCredentials().getAccessKeyId(), + result.getCredentials().getSecretAccessKey(), + result.getCredentials().getSessionToken(), + result.getCredentials().getExpiration().getTime()); + } + catch(AWSSecurityTokenServiceException e) { + throw new STSExceptionMappingService().map(e); + } + } + + public TemporaryAccessTokens assumeRoleWithSAML(final String samlAssertion, final String roleArn) throws BackgroundException { + log.debug("Assume role with SAML with assertion {} for {}", samlAssertion, bookmark); + final PreferencesReader settings = HostPreferencesFactory.get(bookmark); + final AssumeRoleWithSAMLRequest request = new AssumeRoleWithSAMLRequest().withSAMLAssertion(samlAssertion) + .withRequestCredentialsProvider(new AWSStaticCredentialsProvider(new AnonymousAWSCredentials())); + if(StringUtils.isNotBlank(settings.getProperty("s3.assumerole.durationseconds", Profile.STS_DURATION_SECONDS_PROPERTY_KEY))) { + request.setDurationSeconds(PreferencesReader.toInteger(settings.getProperty("s3.assumerole.durationseconds", Profile.STS_DURATION_SECONDS_PROPERTY_KEY))); + } + request.setPolicy(settings.getProperty("s3.assumerole.policy")); + if(StringUtils.isNotBlank(roleArn)) { + log.debug("Found Role ARN {} for {}", roleArn, bookmark); + request.setRoleArn(roleArn); + } + try { + final AssumeRoleWithSAMLResult result = service.assumeRoleWithSAML(request); + log.debug("Received assume role identity result {}", result); + return new TemporaryAccessTokens(result.getCredentials().getAccessKeyId(), + result.getCredentials().getSecretAccessKey(), + result.getCredentials().getSessionToken(), + result.getCredentials().getExpiration().getTime()); + } + catch(AWSSecurityTokenServiceException e) { + throw new STSExceptionMappingService().map(e); + } + } + + /** + * Assume role with web identity token + * + * @param oauth OIDC tokens + * @return Temporary access tokens for the assumed role + */ + public TemporaryAccessTokens assumeRoleWithWebIdentity(final OAuthTokens oauth, final String roleArn) throws BackgroundException { + log.debug("Assume role with web identity {} for {}", oauth, bookmark); + final PreferencesReader settings = HostPreferencesFactory.get(bookmark); + final AssumeRoleWithWebIdentityRequest request = new AssumeRoleWithWebIdentityRequest() + .withRequestCredentialsProvider(new AWSStaticCredentialsProvider(new AnonymousAWSCredentials())); + log.debug("Assume role with OIDC Id token for {}", bookmark); + final String webIdentityToken = this.getWebIdentityToken(oauth); + request.setWebIdentityToken(webIdentityToken); + if(StringUtils.isNotBlank(settings.getProperty("s3.assumerole.durationseconds", Profile.STS_DURATION_SECONDS_PROPERTY_KEY))) { + request.setDurationSeconds(PreferencesReader.toInteger(settings.getProperty("s3.assumerole.durationseconds", Profile.STS_DURATION_SECONDS_PROPERTY_KEY))); + } + request.setPolicy(settings.getProperty("s3.assumerole.policy")); + if(StringUtils.isNotBlank(roleArn)) { + request.setRoleArn(roleArn); + } + else { + if(bookmark.getProtocol().isRoleConfigurable()) { + // When defined in connection profile but with empty value + log.debug("Prompt for Role ARN"); + final Credentials input = prompt.prompt(bookmark, + LocaleFactory.localizedString("Role Amazon Resource Name (ARN)", "Credentials"), + LocaleFactory.localizedString("Provide additional login credentials", "Credentials"), + new LoginOptions().icon(bookmark.getProtocol().disk()).password(false) + .passwordPlaceholder(LocaleFactory.localizedString("Amazon Resource Name (ARN)", "S3"))); + if(input.isSaved()) { + preferences.setProperty(Profile.STS_ROLE_ARN_PROPERTY_KEY, input.getPassword()); + } + request.setRoleArn(input.getPassword()); + } + } + final String sub; + try { + sub = JWT.decode(webIdentityToken).getSubject(); + } + catch(JWTDecodeException e) { + log.warn("Failure {} decoding JWT {}", e, webIdentityToken); + throw new LoginFailureException("Invalid JWT or JSON format in authentication token", e); + } + if(StringUtils.isNotBlank(settings.getProperty(Profile.STS_ROLE_SESSION_NAME_PROPERTY_KEY, "s3.assumerole.rolesessionname"))) { + request.setRoleSessionName(settings.getProperty(Profile.STS_ROLE_SESSION_NAME_PROPERTY_KEY, "s3.assumerole.rolesessionname")); + } + else { + if(StringUtils.isNotBlank(sub)) { + request.setRoleSessionName(sub); + } + else { + log.warn("Missing subject in decoding JWT {}", webIdentityToken); + request.setRoleSessionName(new AsciiRandomStringService().random()); + } + } + try { + log.debug("Use request {}", request); + final AssumeRoleWithWebIdentityResult result = service.assumeRoleWithWebIdentity(request); + log.debug("Received assume role identity result {}", result); + return new TemporaryAccessTokens(result.getCredentials().getAccessKeyId(), + result.getCredentials().getSecretAccessKey(), + result.getCredentials().getSessionToken(), + result.getCredentials().getExpiration().getTime()); + } + catch(AWSSecurityTokenServiceException e) { + throw new STSExceptionMappingService().map(e); + } + } + + /** + * Get web identity token from OIDC tokens in JWT format + * + * @param oauth OIDC tokens + * @return OIDC Id token value + */ + protected String getWebIdentityToken(final OAuthTokens oauth) { + return oauth.getIdToken(); + } +} diff --git a/s3/src/main/java/ch/cyberduck/core/sts/STSCredentialsStrategy.java b/s3/src/main/java/ch/cyberduck/core/sts/STSCredentialsStrategy.java new file mode 100644 index 0000000000..15cb3ac15f --- /dev/null +++ b/s3/src/main/java/ch/cyberduck/core/sts/STSCredentialsStrategy.java @@ -0,0 +1,71 @@ +package ch.cyberduck.core.sts; + +/* + * Copyright (c) 2002-2025 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 ch.cyberduck.core.Credentials; +import ch.cyberduck.core.Host; +import ch.cyberduck.core.LoginCallback; +import ch.cyberduck.core.TemporaryAccessTokens; +import ch.cyberduck.core.exception.BackgroundException; +import ch.cyberduck.core.s3.S3CredentialsStrategy; +import ch.cyberduck.core.ssl.X509KeyManager; +import ch.cyberduck.core.ssl.X509TrustManager; + +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; + +import java.util.concurrent.locks.ReentrantLock; + +/** + * Swap static access key id and secret access key with temporary credentials obtained from STS AssumeRole + */ +public abstract class STSCredentialsStrategy extends STSAuthorizationService implements S3CredentialsStrategy { + private static final Logger log = LogManager.getLogger(STSCredentialsStrategy.class); + + private final ReentrantLock lock = new ReentrantLock(); + private final Host host; + + public STSCredentialsStrategy(final Host host, final X509TrustManager trust, final X509KeyManager key, final LoginCallback prompt) { + super(host, trust, key, prompt); + this.host = host; + } + + /** + * Request new temporary access tokens from static access key in credentials + * + * @param credentials Static long-lived credentials + * @return Temporary access tokens from STS service + */ + public abstract TemporaryAccessTokens refresh(final Credentials credentials) throws BackgroundException; + + @Override + public Credentials get() throws BackgroundException { + lock.lock(); + try { + final Credentials credentials = host.getCredentials(); + final TemporaryAccessTokens tokens = credentials.getTokens(); + // Get temporary credentials from STS using static long-lived credentials + if(tokens.isExpired()) { + log.debug("Refresh expired tokens {} for {}", tokens, host); + credentials.setTokens(this.refresh(credentials)); + } + return credentials; + } + finally { + lock.unlock(); + } + } +} diff --git a/s3/src/main/java/ch/cyberduck/core/sts/STSExceptionMappingService.java b/s3/src/main/java/ch/cyberduck/core/sts/STSExceptionMappingService.java index e35410ceda..6096dd7a50 100644 --- a/s3/src/main/java/ch/cyberduck/core/sts/STSExceptionMappingService.java +++ b/s3/src/main/java/ch/cyberduck/core/sts/STSExceptionMappingService.java @@ -20,12 +20,17 @@ import ch.cyberduck.core.exception.BackgroundException; import ch.cyberduck.core.exception.ExpiredTokenException; import ch.cyberduck.core.exception.LoginFailureException; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; + import com.amazonaws.services.securitytoken.model.AWSSecurityTokenServiceException; public class STSExceptionMappingService implements ExceptionMappingService { + private static final Logger log = LogManager.getLogger(STSExceptionMappingService.class); @Override public BackgroundException map(final AWSSecurityTokenServiceException e) { + log.warn("Map failure {}", e.toString()); if("RequestExpired".equals(e.getErrorCode())) { // The web identity token that was passed is expired or is not valid. Get a new identity token from the identity // provider and then retry the request. diff --git a/s3/src/main/java/ch/cyberduck/core/sts/STSGetSessionTokenCredentialsStrategy.java b/s3/src/main/java/ch/cyberduck/core/sts/STSGetSessionTokenCredentialsStrategy.java new file mode 100644 index 0000000000..624988052f --- /dev/null +++ b/s3/src/main/java/ch/cyberduck/core/sts/STSGetSessionTokenCredentialsStrategy.java @@ -0,0 +1,51 @@ +package ch.cyberduck.core.sts; + +/* + * Copyright (c) 2002-2025 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 ch.cyberduck.core.Credentials; +import ch.cyberduck.core.Host; +import ch.cyberduck.core.LoginCallback; +import ch.cyberduck.core.Profile; +import ch.cyberduck.core.TemporaryAccessTokens; +import ch.cyberduck.core.exception.BackgroundException; +import ch.cyberduck.core.preferences.ProxyPreferencesReader; +import ch.cyberduck.core.s3.S3CredentialsStrategy; +import ch.cyberduck.core.ssl.X509KeyManager; +import ch.cyberduck.core.ssl.X509TrustManager; + +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; + +/** + * Swap static access key id and secret access key with temporary credentials obtained from STS AssumeRole + */ +public class STSGetSessionTokenCredentialsStrategy extends STSCredentialsStrategy implements S3CredentialsStrategy { + private static final Logger log = LogManager.getLogger(STSGetSessionTokenCredentialsStrategy.class); + + private final Host host; + + public STSGetSessionTokenCredentialsStrategy(final Host host, final X509TrustManager trust, final X509KeyManager key, final LoginCallback prompt) { + super(host, trust, key, prompt); + this.host = host; + } + + @Override + public TemporaryAccessTokens refresh(final Credentials credentials) throws BackgroundException { + final String arn = new ProxyPreferencesReader(host, credentials).getProperty(Profile.STS_MFA_ARN_PROPERTY_KEY); + log.debug("Retrieve temporary credentials with {} for role ARN {}", credentials, arn); + return this.getSessionToken(credentials, arn); + } +} diff --git a/s3/src/test/java/ch/cyberduck/core/cryptomator/CopyWorkerTest.java b/s3/src/test/java/ch/cyberduck/core/cryptomator/CopyWorkerTest.java index ddd2f44f6e..ec59a84f52 100644 --- a/s3/src/test/java/ch/cyberduck/core/cryptomator/CopyWorkerTest.java +++ b/s3/src/test/java/ch/cyberduck/core/cryptomator/CopyWorkerTest.java @@ -28,11 +28,11 @@ 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.StreamCopier; import ch.cyberduck.core.pool.SessionPool; import ch.cyberduck.core.s3.AbstractS3Test; import ch.cyberduck.core.s3.S3AccessControlListFeature; -import ch.cyberduck.core.s3.S3DefaultDeleteFeature; import ch.cyberduck.core.s3.S3DirectoryFeature; import ch.cyberduck.core.s3.S3FindFeature; import ch.cyberduck.core.s3.S3MultipartWriteFeature; @@ -83,7 +83,7 @@ public class CopyWorkerTest extends AbstractS3Test { final byte[] content = RandomUtils.nextBytes(40500); final TransferStatus status = new TransferStatus(); final S3AccessControlListFeature acl = new S3AccessControlListFeature(session); - new CryptoBulkFeature<>(session, new DisabledBulkFeature(), new S3DefaultDeleteFeature(session, acl), cryptomator).pre(Transfer.Type.upload, Collections.singletonMap(new TransferItem(source), status), new DisabledConnectionCallback()); + new CryptoBulkFeature<>(session, new DisabledBulkFeature(), 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 S3MultipartWriteFeature(session, acl), 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()); @@ -108,10 +108,11 @@ public class CopyWorkerTest extends AbstractS3Test { final DefaultVaultRegistry registry = new DefaultVaultRegistry(new DisabledPasswordCallback(), cryptomator); session.withRegistry(registry); final S3AccessControlListFeature acl = new S3AccessControlListFeature(session); - new CryptoTouchFeature<>(session, new S3TouchFeature(session, acl), new S3WriteFeature(session, acl), cryptomator).touch(source, new TransferStatus()); + new CryptoTouchFeature<>(session, new S3TouchFeature(session, acl), cryptomator).touch( + cryptomator.getFeature(session, Write.class, new S3WriteFeature(session, acl)), source, new TransferStatus()); assertTrue(cryptomator.getFeature(session, Find.class, new DefaultFindFeature(session)).find(source)); - - cryptomator.getFeature(session, Directory.class, new S3DirectoryFeature(session, new S3WriteFeature(session, acl), acl)).mkdir(targetFolder, new TransferStatus()); + cryptomator.getFeature(session, Directory.class, new S3DirectoryFeature(session, acl)).mkdir( + cryptomator.getFeature(session, Write.class, new S3WriteFeature(session, acl)), 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); @@ -133,9 +134,11 @@ public class CopyWorkerTest extends AbstractS3Test { final DefaultVaultRegistry registry = new DefaultVaultRegistry(new DisabledPasswordCallback(), cryptomator); session.withRegistry(registry); final S3AccessControlListFeature acl = new S3AccessControlListFeature(session); - new CryptoTouchFeature<>(session, new S3TouchFeature(session, acl), new S3WriteFeature(session, acl), cryptomator).touch(source, new TransferStatus()); + new CryptoTouchFeature<>(session, new S3TouchFeature(session, acl), cryptomator).touch( + new CryptoWriteFeature<>(session, new S3WriteFeature(session, new S3AccessControlListFeature(session)), cryptomator), source, new TransferStatus()); assertTrue(cryptomator.getFeature(session, Find.class, new DefaultFindFeature(session)).find(source)); - cryptomator.getFeature(session, Directory.class, new S3DirectoryFeature(session, new S3WriteFeature(session, acl), acl)).mkdir(targetFolder, new TransferStatus()); + cryptomator.getFeature(session, Directory.class, new S3DirectoryFeature(session, acl)).mkdir( + cryptomator.getFeature(session, Write.class, new S3WriteFeature(session, acl)), 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); @@ -155,9 +158,11 @@ public class CopyWorkerTest extends AbstractS3Test { final DefaultVaultRegistry registry = new DefaultVaultRegistry(new DisabledPasswordCallback(), cryptomator); session.withRegistry(registry); final S3AccessControlListFeature acl = new S3AccessControlListFeature(session); - cryptomator.getFeature(session, Directory.class, new S3DirectoryFeature(session, new S3WriteFeature(session, acl), acl)).mkdir(folder, new TransferStatus()); + cryptomator.getFeature(session, Directory.class, new S3DirectoryFeature(session, acl)).mkdir( + cryptomator.getFeature(session, Write.class, new S3WriteFeature(session, acl)), folder, new TransferStatus()); assertTrue(cryptomator.getFeature(session, Find.class, new DefaultFindFeature(session)).find(folder)); - new CryptoTouchFeature<>(session, new S3TouchFeature(session, acl), new S3WriteFeature(session, acl), cryptomator).touch(file, new TransferStatus()); + new CryptoTouchFeature<>(session, new S3TouchFeature(session, acl), cryptomator).touch( + cryptomator.getFeature(session, Write.class, new S3WriteFeature(session, acl)), 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)); @@ -190,7 +195,8 @@ public class CopyWorkerTest extends AbstractS3Test { cryptomator.create(session, new VaultCredentials("test"), vaultVersion); final DefaultVaultRegistry registry = new DefaultVaultRegistry(new DisabledPasswordCallback(), cryptomator); session.withRegistry(registry); - cryptomator.getFeature(session, Directory.class, new S3DirectoryFeature(session, new S3WriteFeature(session, acl), acl)).mkdir(encryptedFolder, new TransferStatus()); + cryptomator.getFeature(session, Directory.class, new S3DirectoryFeature(session, acl)).mkdir( + cryptomator.getFeature(session, Write.class, new S3WriteFeature(session, acl)), 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()); @@ -211,8 +217,8 @@ public class CopyWorkerTest extends AbstractS3Test { 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)); final S3AccessControlListFeature acl = new S3AccessControlListFeature(session); - new S3DirectoryFeature(session, new S3WriteFeature(session, acl), acl).mkdir(cleartextFolder, new TransferStatus()); - new S3TouchFeature(session, acl).touch(cleartextFile, new TransferStatus()); + new S3DirectoryFeature(session, acl).mkdir(new S3WriteFeature(session, acl), cleartextFolder, new TransferStatus()); + new S3TouchFeature(session, acl).touch(new S3WriteFeature(session, acl), cleartextFile, new TransferStatus()); assertTrue(new S3FindFeature(session, acl).find(cleartextFolder)); assertTrue(new S3FindFeature(session, acl).find(cleartextFile)); final CryptoVault cryptomator = new CryptoVault(vault); @@ -238,17 +244,18 @@ public class CopyWorkerTest extends AbstractS3Test { 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)); final S3AccessControlListFeature acl = new S3AccessControlListFeature(session); - new S3DirectoryFeature(session, new S3WriteFeature(session, acl), - acl).mkdir(clearFolder, new TransferStatus()); + new S3DirectoryFeature(session, acl).mkdir(new S3WriteFeature(session, acl), 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 S3DirectoryFeature(session, new S3WriteFeature(session, acl), acl)).mkdir(encryptedFolder, new TransferStatus()); + cryptomator.getFeature(session, Directory.class, new S3DirectoryFeature(session, acl)).mkdir( + cryptomator.getFeature(session, Write.class, new S3WriteFeature(session, acl)), encryptedFolder, new TransferStatus()); assertTrue(cryptomator.getFeature(session, Find.class, new DefaultFindFeature(session)).find(encryptedFolder)); - new CryptoTouchFeature<>(session, new S3TouchFeature(session, acl), new S3WriteFeature(session, acl), cryptomator).touch(encryptedFile, new TransferStatus()); + new CryptoTouchFeature<>(session, new S3TouchFeature(session, acl), cryptomator).touch( + cryptomator.getFeature(session, Write.class, new S3WriteFeature(session, acl)), 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)); @@ -271,10 +278,11 @@ public class CopyWorkerTest extends AbstractS3Test { final DefaultVaultRegistry registry = new DefaultVaultRegistry(new DisabledPasswordCallback(), cryptomator); session.withRegistry(registry); final S3AccessControlListFeature acl = new S3AccessControlListFeature(session); - cryptomator.getFeature(session, Directory.class, new S3DirectoryFeature(session, new S3WriteFeature(session, acl), - acl)).mkdir(encryptedFolder, new TransferStatus()); + cryptomator.getFeature(session, Directory.class, new S3DirectoryFeature(session, acl)).mkdir( + cryptomator.getFeature(session, Write.class, new S3WriteFeature(session, acl)), encryptedFolder, new TransferStatus()); assertTrue(cryptomator.getFeature(session, Find.class, new DefaultFindFeature(session)).find(encryptedFolder)); - new CryptoTouchFeature<>(session, new S3TouchFeature(session, acl), new S3WriteFeature(session, acl), cryptomator).touch(encryptedFile, new TransferStatus()); + new CryptoTouchFeature<>(session, new S3TouchFeature(session, acl), cryptomator).touch( + cryptomator.getFeature(session, Write.class, new S3WriteFeature(session, acl)), 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)); diff --git a/s3/src/test/java/ch/cyberduck/core/cryptomator/MoveWorkerTest.java b/s3/src/test/java/ch/cyberduck/core/cryptomator/MoveWorkerTest.java index 7593a27fa9..7f1010f341 100644 --- a/s3/src/test/java/ch/cyberduck/core/cryptomator/MoveWorkerTest.java +++ b/s3/src/test/java/ch/cyberduck/core/cryptomator/MoveWorkerTest.java @@ -32,6 +32,7 @@ import ch.cyberduck.core.exception.NotfoundException; 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.io.StreamCopier; import ch.cyberduck.core.pool.SessionPool; import ch.cyberduck.core.s3.AbstractS3Test; @@ -87,7 +88,7 @@ public class MoveWorkerTest extends AbstractS3Test { final byte[] content = RandomUtils.nextBytes(40500); final TransferStatus status = new TransferStatus(); final S3AccessControlListFeature acl = new S3AccessControlListFeature(session); - new CryptoBulkFeature<>(session, new DisabledBulkFeature(), new S3DefaultDeleteFeature(session, acl), cryptomator).pre(Transfer.Type.upload, Collections.singletonMap(new TransferItem(source), status), new DisabledConnectionCallback()); + new CryptoBulkFeature<>(session, new DisabledBulkFeature(), 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 S3MultipartWriteFeature(session, acl), cryptomator).write(source, status.setLength(content.length), new DisabledConnectionCallback())); assertTrue(cryptomator.getFeature(session, Find.class, new DefaultFindFeature(session)).find(source)); final MoveWorker worker = new MoveWorker(Collections.singletonMap(source, target), new SessionPool.SingleSessionPool(session), PathCache.empty(), new DisabledProgressListener(), new DisabledLoginCallback()); @@ -109,10 +110,11 @@ public class MoveWorkerTest extends AbstractS3Test { cryptomator.create(session, new VaultCredentials("test"), vaultVersion); session.withRegistry(new DefaultVaultRegistry(new DisabledPasswordCallback(), cryptomator)); final S3AccessControlListFeature acl = new S3AccessControlListFeature(session); - new CryptoTouchFeature<>(session, new S3TouchFeature(session, acl), new S3WriteFeature(session, acl), cryptomator).touch(source, new TransferStatus()); + new CryptoTouchFeature<>(session, new S3TouchFeature(session, acl), cryptomator).touch( + cryptomator.getFeature(session, Write.class, new S3WriteFeature(session, acl)), source, new TransferStatus()); assertTrue(cryptomator.getFeature(session, Find.class, new DefaultFindFeature(session)).find(source)); - final Path targetFolder = cryptomator.getFeature(session, Directory.class, new S3DirectoryFeature(session, new S3WriteFeature(session, acl), acl)).mkdir( - new Path(vault, UUID.randomUUID().toString(), EnumSet.of(Path.Type.directory)), new TransferStatus()); + final Path targetFolder = cryptomator.getFeature(session, Directory.class, new S3DirectoryFeature(session, acl)).mkdir( + cryptomator.getFeature(session, Write.class, new S3WriteFeature(session, acl)), new Path(vault, UUID.randomUUID().toString(), EnumSet.of(Path.Type.directory)), new TransferStatus()); assertTrue(cryptomator.getFeature(session, Find.class, new DefaultFindFeature(session)).find(targetFolder)); final Path target = new Path(targetFolder, UUID.randomUUID().toString(), EnumSet.of(Path.Type.file)); final MoveWorker worker = new MoveWorker(Collections.singletonMap(source, target), new SessionPool.SingleSessionPool(session), PathCache.empty(), new DisabledProgressListener(), new DisabledLoginCallback()); @@ -132,10 +134,11 @@ public class MoveWorkerTest extends AbstractS3Test { cryptomator.create(session, new VaultCredentials("test"), vaultVersion); session.withRegistry(new DefaultVaultRegistry(new DisabledPasswordCallback(), cryptomator)); final S3AccessControlListFeature acl = new S3AccessControlListFeature(session); - new CryptoTouchFeature<>(session, new S3TouchFeature(session, acl), new S3WriteFeature(session, acl), cryptomator).touch(source, new TransferStatus()); + new CryptoTouchFeature<>(session, new S3TouchFeature(session, acl), cryptomator).touch( + new CryptoWriteFeature<>(session, new S3WriteFeature(session, new S3AccessControlListFeature(session)), cryptomator), source, new TransferStatus()); assertTrue(cryptomator.getFeature(session, Find.class, new DefaultFindFeature(session)).find(source)); - final Path targetFolder = cryptomator.getFeature(session, Directory.class, new S3DirectoryFeature(session, new S3WriteFeature(session, acl), acl)).mkdir( - new Path(vault, new AlphanumericRandomStringService(130).random(), EnumSet.of(Path.Type.directory)), new TransferStatus()); + final Path targetFolder = cryptomator.getFeature(session, Directory.class, new S3DirectoryFeature(session, acl)).mkdir( + new S3WriteFeature(session, new S3AccessControlListFeature(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(targetFolder)); final Path target = new Path(targetFolder, new AlphanumericRandomStringService(130).random(), EnumSet.of(Path.Type.file)); final MoveWorker worker = new MoveWorker(Collections.singletonMap(source, target), new SessionPool.SingleSessionPool(session), PathCache.empty(), new DisabledProgressListener(), new DisabledLoginCallback()); @@ -153,11 +156,12 @@ public class MoveWorkerTest extends AbstractS3Test { cryptomator.create(session, new VaultCredentials("test"), vaultVersion); session.withRegistry(new DefaultVaultRegistry(new DisabledPasswordCallback(), cryptomator)); final S3AccessControlListFeature acl = new S3AccessControlListFeature(session); - final Path folder = cryptomator.getFeature(session, Directory.class, new S3DirectoryFeature(session, new S3WriteFeature(session, acl), acl)).mkdir( - new Path(vault, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory)), new TransferStatus()); + final Path folder = cryptomator.getFeature(session, Directory.class, new S3DirectoryFeature(session, acl)).mkdir( + cryptomator.getFeature(session, Write.class, new S3WriteFeature(session, acl)), new Path(vault, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory)), new TransferStatus()); assertTrue(cryptomator.getFeature(session, Find.class, new DefaultFindFeature(session)).find(folder)); final Path file = new Path(folder, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)); - new CryptoTouchFeature<>(session, new S3TouchFeature(session, acl), new S3WriteFeature(session, acl), cryptomator).touch(file, new TransferStatus()); + new CryptoTouchFeature<>(session, new S3TouchFeature(session, acl), cryptomator).touch( + cryptomator.getFeature(session, Write.class, new S3WriteFeature(session, acl)), file, new TransferStatus()); assertTrue(cryptomator.getFeature(session, Find.class, new DefaultFindFeature(session)).find(file)); // rename file final Path fileRenamed = new Path(folder, "f1", EnumSet.of(Path.Type.file)); @@ -187,14 +191,14 @@ public class MoveWorkerTest extends AbstractS3Test { final Path vault = new Path(home, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory)); final Path clearFile = new Path(home, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)); final S3AccessControlListFeature acl = new S3AccessControlListFeature(session); - new S3TouchFeature(session, acl).touch(clearFile, new TransferStatus()); + new S3TouchFeature(session, acl).touch(new S3WriteFeature(session, new S3AccessControlListFeature(session)), clearFile, new TransferStatus()); assertTrue(new S3FindFeature(session, acl).find(clearFile)); final CryptoVault cryptomator = new CryptoVault(vault); cryptomator.create(session, new VaultCredentials("test"), vaultVersion); final DefaultVaultRegistry registry = new DefaultVaultRegistry(new DisabledPasswordCallback(), cryptomator); session.withRegistry(registry); - final Path encryptedFolder = cryptomator.getFeature(session, Directory.class, new S3DirectoryFeature(session, new S3WriteFeature(session, acl), acl)).mkdir( - new Path(vault, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory)), new TransferStatus()); + final Path encryptedFolder = cryptomator.getFeature(session, Directory.class, new S3DirectoryFeature(session, acl)).mkdir( + cryptomator.getFeature(session, Write.class, new S3WriteFeature(session, acl)), new Path(vault, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory)), new TransferStatus()); assertTrue(cryptomator.getFeature(session, Find.class, new DefaultFindFeature(session)).find(encryptedFolder)); final Path encryptedFile = new Path(encryptedFolder, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)); // move file into vault @@ -211,9 +215,9 @@ public class MoveWorkerTest extends AbstractS3Test { final Path home = new Path("test-eu-central-1-cyberduck", EnumSet.of(Path.Type.directory, Path.Type.volume)); final Path vault = new Path(home, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory)); final S3AccessControlListFeature acl = new S3AccessControlListFeature(session); - final Path clearFolder = new S3DirectoryFeature(session, new S3WriteFeature(session, acl), acl).mkdir(new Path(home, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory)), new TransferStatus()); + final Path clearFolder = new S3DirectoryFeature(session, acl).mkdir(new S3WriteFeature(session, new S3AccessControlListFeature(session)), new Path(home, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory)), new TransferStatus()); final Path clearFile = new Path(clearFolder, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)); - new S3TouchFeature(session, acl).touch(clearFile, new TransferStatus()); + new S3TouchFeature(session, acl).touch(new S3WriteFeature(session, new S3AccessControlListFeature(session)), clearFile, new TransferStatus()); assertTrue(new S3FindFeature(session, acl).find(clearFolder)); assertTrue(new S3FindFeature(session, acl).find(clearFile)); final CryptoVault cryptomator = new CryptoVault(vault); @@ -238,17 +242,18 @@ public class MoveWorkerTest extends AbstractS3Test { final Path home = new Path("test-eu-central-1-cyberduck", EnumSet.of(Path.Type.directory, Path.Type.volume)); final Path vault = new Path(home, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory)); final S3AccessControlListFeature acl = new S3AccessControlListFeature(session); - final Path clearFolder = new S3DirectoryFeature(session, new S3WriteFeature(session, acl), acl).mkdir( - new Path(home, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory)), new TransferStatus()); + final Path clearFolder = new S3DirectoryFeature(session, acl).mkdir( + new S3WriteFeature(session, acl), new Path(home, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory)), new TransferStatus()); final CryptoVault cryptomator = new CryptoVault(vault); cryptomator.create(session, new VaultCredentials("test"), vaultVersion); final DefaultVaultRegistry registry = new DefaultVaultRegistry(new DisabledPasswordCallback(), cryptomator); session.withRegistry(registry); - final Path encryptedFolder = cryptomator.getFeature(session, Directory.class, new S3DirectoryFeature(session, new S3WriteFeature(session, acl), acl)).mkdir( - new Path(vault, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory)), new TransferStatus()); + final Path encryptedFolder = cryptomator.getFeature(session, Directory.class, new S3DirectoryFeature(session, acl)).mkdir( + cryptomator.getFeature(session, Write.class, new S3WriteFeature(session, acl)), new Path(vault, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory)), new TransferStatus()); final Path encryptedFile = new Path(encryptedFolder, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)); assertTrue(cryptomator.getFeature(session, Find.class, new DefaultFindFeature(session)).find(encryptedFolder)); - new CryptoTouchFeature<>(session, new S3TouchFeature(session, acl), new S3WriteFeature(session, acl), cryptomator).touch(encryptedFile, new TransferStatus()); + new CryptoTouchFeature<>(session, new S3TouchFeature(session, acl), cryptomator).touch( + cryptomator.getFeature(session, Write.class, new S3WriteFeature(session, acl)), encryptedFile, new TransferStatus()); assertTrue(cryptomator.getFeature(session, Find.class, new DefaultFindFeature(session)).find(encryptedFile)); // move file outside vault final Path fileRenamed = new Path(clearFolder, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)); @@ -270,11 +275,12 @@ public class MoveWorkerTest extends AbstractS3Test { final DefaultVaultRegistry registry = new DefaultVaultRegistry(new DisabledPasswordCallback(), cryptomator); session.withRegistry(registry); final S3AccessControlListFeature acl = new S3AccessControlListFeature(session); - final Path encryptedFolder = cryptomator.getFeature(session, Directory.class, new S3DirectoryFeature(session, new S3WriteFeature(session, acl), acl)).mkdir( - new Path(vault, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory)), new TransferStatus()); + final Path encryptedFolder = cryptomator.getFeature(session, Directory.class, new S3DirectoryFeature(session, acl)).mkdir( + cryptomator.getFeature(session, Write.class, new S3WriteFeature(session, acl)), new Path(vault, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory)), new TransferStatus()); final Path encryptedFile = new Path(encryptedFolder, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)); assertTrue(cryptomator.getFeature(session, Find.class, new DefaultFindFeature(session)).find(encryptedFolder)); - new CryptoTouchFeature<>(session, new S3TouchFeature(session, acl), new S3WriteFeature(session, acl), cryptomator).touch(encryptedFile, new TransferStatus()); + new CryptoTouchFeature<>(session, new S3TouchFeature(session, acl), cryptomator).touch( + cryptomator.getFeature(session, Write.class, new S3WriteFeature(session, acl)), encryptedFile, new TransferStatus()); assertTrue(cryptomator.getFeature(session, Find.class, new DefaultFindFeature(session)).find(encryptedFile)); // move directory outside vault final Path directoryRenamed = new Path(home, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory, Path.Type.placeholder)); diff --git a/s3/src/test/java/ch/cyberduck/core/cryptomator/S3DirectoryFeatureTest.java b/s3/src/test/java/ch/cyberduck/core/cryptomator/S3DirectoryFeatureTest.java index 79c9498bc3..0a39e12db4 100644 --- a/s3/src/test/java/ch/cyberduck/core/cryptomator/S3DirectoryFeatureTest.java +++ b/s3/src/test/java/ch/cyberduck/core/cryptomator/S3DirectoryFeatureTest.java @@ -16,7 +16,6 @@ package ch.cyberduck.core.cryptomator; */ import ch.cyberduck.core.AlphanumericRandomStringService; -import ch.cyberduck.core.DisabledListProgressListener; import ch.cyberduck.core.DisabledLoginCallback; import ch.cyberduck.core.DisabledPasswordCallback; import ch.cyberduck.core.Path; @@ -26,6 +25,7 @@ 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.s3.AbstractS3Test; import ch.cyberduck.core.s3.S3AccessControlListFeature; import ch.cyberduck.core.s3.S3AttributesFinderFeature; @@ -65,15 +65,16 @@ public class S3DirectoryFeatureTest extends AbstractS3Test { cryptomator.create(session, new VaultCredentials("test"), vaultVersion); session.withRegistry(new DefaultVaultRegistry(new DisabledPasswordCallback(), cryptomator)); final S3AccessControlListFeature acl = new S3AccessControlListFeature(session); - final Path test = cryptomator.getFeature(session, Directory.class, new S3DirectoryFeature(session, new S3WriteFeature(session, acl), acl)).mkdir(new Path(vault, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory)), new TransferStatus()); + final Path test = cryptomator.getFeature(session, Directory.class, new S3DirectoryFeature(session, acl)).mkdir( + cryptomator.getFeature(session, Write.class, new S3WriteFeature(session, acl)), 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 S3FindFeature(session, acl)).find(test)); assertTrue(cryptomator.getFeature(session, Find.class, new DefaultFindFeature(session)).find(test)); final PathAttributes attributes = cryptomator.getFeature(session, AttributesFinder.class, new S3AttributesFinderFeature(session, acl)).find(test); assertNotNull(attributes.getVersionId()); cryptomator.getFeature(session, Delete.class, new S3DefaultDeleteFeature(session, acl)).delete(Collections.singletonList(test), new DisabledLoginCallback(), new Delete.DisabledCallback()); - assertTrue(new CryptoListService(session, new S3ListService(session, acl), cryptomator).list(vault, new DisabledListProgressListener()) - .toStream().filter(f -> !f.attributes().isDuplicate()).collect(Collectors.toList()).isEmpty()); + assertTrue(new CryptoListService(session, new S3ListService(session, acl), cryptomator).list(vault) + .toStream().filter(f -> !f.attributes().isDuplicate() && !f.attributes().isTrashed()).collect(Collectors.toList()).isEmpty()); cryptomator.getFeature(session, Delete.class, new S3DefaultDeleteFeature(session, acl)).delete(Arrays.asList(test, vault), new DisabledLoginCallback(), new Delete.DisabledCallback()); } @@ -86,7 +87,8 @@ public class S3DirectoryFeatureTest extends AbstractS3Test { cryptomator.create(session, new VaultCredentials("test"), vaultVersion); session.withRegistry(new DefaultVaultRegistry(new DisabledPasswordCallback(), cryptomator)); final S3AccessControlListFeature acl = new S3AccessControlListFeature(session); - final Path test = cryptomator.getFeature(session, Directory.class, new S3DirectoryFeature(session, new S3WriteFeature(session, acl), acl)).mkdir(new Path(vault, new AlphanumericRandomStringService(130).random(), EnumSet.of(Path.Type.directory)), new TransferStatus()); + final Path test = cryptomator.getFeature(session, Directory.class, new S3DirectoryFeature(session, acl)).mkdir( + cryptomator.getFeature(session, Write.class, new S3WriteFeature(session, acl)), new Path(vault, new AlphanumericRandomStringService(130).random(), EnumSet.of(Path.Type.directory)), new TransferStatus()); final String versionId = test.attributes().getVersionId(); assertTrue(cryptomator.getFeature(session, Find.class, new DefaultFindFeature(session)).find(test)); final PathAttributes attributes = cryptomator.getFeature(session, AttributesFinder.class, new S3AttributesFinderFeature(session, acl)).find(test); diff --git a/s3/src/test/java/ch/cyberduck/core/cryptomator/S3MoveFeatureTest.java b/s3/src/test/java/ch/cyberduck/core/cryptomator/S3MoveFeatureTest.java index 250a5e9c6b..7d3a0a6bd8 100644 --- a/s3/src/test/java/ch/cyberduck/core/cryptomator/S3MoveFeatureTest.java +++ b/s3/src/test/java/ch/cyberduck/core/cryptomator/S3MoveFeatureTest.java @@ -25,6 +25,7 @@ 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.s3.AbstractS3Test; import ch.cyberduck.core.s3.S3AccessControlListFeature; import ch.cyberduck.core.s3.S3DefaultDeleteFeature; @@ -64,10 +65,12 @@ public class S3MoveFeatureTest extends AbstractS3Test { cryptomator.create(session, new VaultCredentials("test"), vaultVersion); session.withRegistry(new DefaultVaultRegistry(new DisabledPasswordCallback(), cryptomator)); final S3AccessControlListFeature acl = new S3AccessControlListFeature(session); - cryptomator.getFeature(session, Directory.class, new S3DirectoryFeature(session, new S3WriteFeature(session, acl), acl)).mkdir(folder, new TransferStatus()); - new CryptoTouchFeature<>(session, new S3TouchFeature(session, acl), new S3WriteFeature(session, acl), cryptomator).touch(file, new TransferStatus()); + cryptomator.getFeature(session, Directory.class, new S3DirectoryFeature(session, acl)).mkdir( + cryptomator.getFeature(session, Write.class, new S3WriteFeature(session, acl)), folder, new TransferStatus()); + new CryptoTouchFeature<>(session, new S3TouchFeature(session, acl), cryptomator).touch( + cryptomator.getFeature(session, Write.class, new S3WriteFeature(session, acl)), file, new TransferStatus()); assertTrue(cryptomator.getFeature(session, Find.class, new DefaultFindFeature(session)).find(file)); - final Move move = cryptomator.getFeature(session, Move.class, new S3MoveFeature(session, new S3AccessControlListFeature(session))); + final Move move = cryptomator.getFeature(session, Move.class, new S3MoveFeature(session, acl)); // 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()); diff --git a/s3/src/test/java/ch/cyberduck/core/cryptomator/S3MultipartUploadServiceTest.java b/s3/src/test/java/ch/cyberduck/core/cryptomator/S3MultipartUploadServiceTest.java index 3168e90a0c..c53c19e2b4 100644 --- a/s3/src/test/java/ch/cyberduck/core/cryptomator/S3MultipartUploadServiceTest.java +++ b/s3/src/test/java/ch/cyberduck/core/cryptomator/S3MultipartUploadServiceTest.java @@ -26,6 +26,7 @@ import ch.cyberduck.core.Path; 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; @@ -51,6 +52,7 @@ import ch.cyberduck.test.IntegrationTest; import org.apache.commons.io.IOUtils; import org.apache.commons.lang3.RandomUtils; import org.cryptomator.cryptolib.api.FileHeader; +import org.jets3t.service.model.StorageObject; import org.junit.Test; import org.junit.experimental.categories.Category; import org.junit.runner.RunWith; @@ -80,9 +82,9 @@ public class S3MultipartUploadServiceTest extends AbstractS3Test { cryptomator.create(session, new VaultCredentials("test"), vaultVersion); session.withRegistry(new DefaultVaultRegistry(new DisabledPasswordCallback(), cryptomator)); final S3AccessControlListFeature acl = new S3AccessControlListFeature(session); - final CryptoUploadFeature m = new CryptoUploadFeature<>(session, - new S3MultipartUploadService(session, new S3WriteFeature(session, acl), acl, 5L * 1024L * 1024L, 5), - new S3WriteFeature(session, acl), cryptomator); + final CryptoUploadFeature m = new CryptoUploadFeature<>(session, + new S3MultipartUploadService(session, acl, 5L * 1024L * 1024L, 5), + cryptomator); final Local local = new Local(System.getProperty("java.io.tmpdir"), UUID.randomUUID().toString()); final int length = 5242880; final byte[] content = RandomUtils.nextBytes(length); @@ -92,7 +94,7 @@ public class S3MultipartUploadServiceTest extends AbstractS3Test { writeStatus.setHeader(cryptomator.getFileHeaderCryptor().encryptHeader(header)); writeStatus.setLength(content.length); final BytecountStreamListener count = new BytecountStreamListener(); - m.upload(test, local, new BandwidthThrottle(BandwidthThrottle.UNLIMITED), new DisabledProgressListener(), count, writeStatus, null); + m.upload(new CryptoWriteFeature<>(session, new S3WriteFeature(session, acl), cryptomator), test, local, new BandwidthThrottle(BandwidthThrottle.UNLIMITED), new DisabledProgressListener(), count, writeStatus, null); assertEquals(content.length, count.getSent()); assertTrue(writeStatus.isComplete()); assertTrue(cryptomator.getFeature(session, Find.class, new S3FindFeature(session, acl)).find(test)); @@ -116,9 +118,9 @@ public class S3MultipartUploadServiceTest extends AbstractS3Test { cryptomator.create(session, new VaultCredentials("test"), vaultVersion); session.withRegistry(new DefaultVaultRegistry(new DisabledPasswordCallback(), cryptomator)); final S3AccessControlListFeature acl = new S3AccessControlListFeature(session); - final CryptoUploadFeature m = new CryptoUploadFeature<>(session, - new S3MultipartUploadService(session, new S3WriteFeature(session, acl), acl, 5L * 1024L * 1024L, 5), - new S3WriteFeature(session, acl), cryptomator); + final CryptoUploadFeature m = new CryptoUploadFeature<>(session, + new S3MultipartUploadService(session, acl, 5L * 1024L * 1024L, 5), + cryptomator); final Local local = new Local(System.getProperty("java.io.tmpdir"), UUID.randomUUID().toString()); final byte[] content = RandomUtils.nextBytes(6 * 1024 * 1024); IOUtils.write(content, local.getOutputStream(false)); @@ -126,7 +128,7 @@ public class S3MultipartUploadServiceTest extends AbstractS3Test { final FileHeader header = cryptomator.getFileHeaderCryptor().create(); writeStatus.setHeader(cryptomator.getFileHeaderCryptor().encryptHeader(header)); writeStatus.setLength(content.length); - m.upload(test, local, new BandwidthThrottle(BandwidthThrottle.UNLIMITED), new DisabledProgressListener(), new DisabledStreamListener(), writeStatus, null); + m.upload(new CryptoWriteFeature<>(session, new S3WriteFeature(session, acl), cryptomator), test, local, new BandwidthThrottle(BandwidthThrottle.UNLIMITED), new DisabledProgressListener(), new DisabledStreamListener(), writeStatus, null); assertTrue(writeStatus.isComplete()); assertTrue(cryptomator.getFeature(session, Find.class, new S3FindFeature(session, acl)).find(test)); assertEquals(content.length, cryptomator.getFeature(session, AttributesFinder.class, new S3AttributesFinderFeature(session, acl)).find(test).getSize()); @@ -151,15 +153,15 @@ public class S3MultipartUploadServiceTest extends AbstractS3Test { final TransferStatus writeStatus = new TransferStatus(); final byte[] content = RandomUtils.nextBytes(6 * 1024 * 1024); writeStatus.setLength(content.length); - final CryptoBulkFeature> bulk = new CryptoBulkFeature<>(session, new DisabledBulkFeature(), new S3DefaultDeleteFeature(session, new S3AccessControlListFeature(session)), cryptomator); + final CryptoBulkFeature> bulk = new CryptoBulkFeature<>(session, new DisabledBulkFeature(), cryptomator); bulk.pre(Transfer.Type.upload, Collections.singletonMap(new TransferItem(test), writeStatus), new DisabledConnectionCallback()); final S3AccessControlListFeature acl = new S3AccessControlListFeature(session); - final CryptoUploadFeature m = new CryptoUploadFeature<>(session, - new S3MultipartUploadService(session, new S3WriteFeature(session, acl), acl, 5L * 1024L * 1024L, 5), - new S3WriteFeature(session, acl), cryptomator); + final CryptoUploadFeature m = new CryptoUploadFeature<>(session, + new S3MultipartUploadService(session, acl, 5L * 1024L * 1024L, 5), + cryptomator); final Local local = new Local(System.getProperty("java.io.tmpdir"), UUID.randomUUID().toString()); IOUtils.write(content, local.getOutputStream(false)); - m.upload(test, local, new BandwidthThrottle(BandwidthThrottle.UNLIMITED), new DisabledProgressListener(), new DisabledStreamListener(), writeStatus, null); + m.upload(new CryptoWriteFeature<>(session, new S3WriteFeature(session, acl), cryptomator), test, local, new BandwidthThrottle(BandwidthThrottle.UNLIMITED), new DisabledProgressListener(), new DisabledStreamListener(), writeStatus, null); assertTrue(writeStatus.isComplete()); assertTrue(cryptomator.getFeature(session, Find.class, new S3FindFeature(session, acl)).find(test)); assertEquals(content.length, cryptomator.getFeature(session, AttributesFinder.class, new S3AttributesFinderFeature(session, acl)).find(test).getSize()); diff --git a/s3/src/test/java/ch/cyberduck/core/cryptomator/S3ObjectListServiceTest.java b/s3/src/test/java/ch/cyberduck/core/cryptomator/S3ObjectListServiceTest.java index 881065ac53..9ab29fa168 100644 --- a/s3/src/test/java/ch/cyberduck/core/cryptomator/S3ObjectListServiceTest.java +++ b/s3/src/test/java/ch/cyberduck/core/cryptomator/S3ObjectListServiceTest.java @@ -22,6 +22,7 @@ import ch.cyberduck.core.DisabledPasswordCallback; import ch.cyberduck.core.Path; import ch.cyberduck.core.cryptomator.features.CryptoListService; import ch.cyberduck.core.cryptomator.features.CryptoTouchFeature; +import ch.cyberduck.core.cryptomator.features.CryptoWriteFeature; import ch.cyberduck.core.features.Delete; import ch.cyberduck.core.s3.AbstractS3Test; import ch.cyberduck.core.s3.S3AccessControlListFeature; @@ -59,7 +60,8 @@ public class S3ObjectListServiceTest extends AbstractS3Test { session.withRegistry(new DefaultVaultRegistry(new DisabledPasswordCallback(), cryptomator)); final S3AccessControlListFeature acl = new S3AccessControlListFeature(session); assertTrue(new CryptoListService(session, new S3ObjectListService(session, acl), cryptomator).list(vault, new DisabledListProgressListener()).isEmpty()); - new CryptoTouchFeature<>(session, new S3TouchFeature(session, acl), new S3WriteFeature(session, acl), cryptomator).touch(test, new TransferStatus()); + new CryptoTouchFeature<>(session, new S3TouchFeature(session, acl), cryptomator).touch( + new CryptoWriteFeature<>(session, new S3WriteFeature(session, new S3AccessControlListFeature(session)), cryptomator), test, new TransferStatus()); assertEquals(test, new CryptoListService(session, new S3ObjectListService(session, acl), cryptomator).list(vault, new DisabledListProgressListener()).get(0)); cryptomator.getFeature(session, Delete.class, new S3DefaultDeleteFeature(session, acl)).delete(Arrays.asList(test, vault), new DisabledLoginCallback(), new Delete.DisabledCallback()); } diff --git a/s3/src/test/java/ch/cyberduck/core/cryptomator/S3SingleUploadServiceTest.java b/s3/src/test/java/ch/cyberduck/core/cryptomator/S3SingleUploadServiceTest.java index b9bad785d7..9a5395beac 100644 --- a/s3/src/test/java/ch/cyberduck/core/cryptomator/S3SingleUploadServiceTest.java +++ b/s3/src/test/java/ch/cyberduck/core/cryptomator/S3SingleUploadServiceTest.java @@ -25,6 +25,7 @@ import ch.cyberduck.core.Local; import ch.cyberduck.core.Path; 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; @@ -46,6 +47,7 @@ import ch.cyberduck.test.IntegrationTest; import org.apache.commons.io.IOUtils; import org.apache.commons.lang3.RandomUtils; import org.cryptomator.cryptolib.api.FileHeader; +import org.jets3t.service.model.StorageObject; import org.junit.Test; import org.junit.experimental.categories.Category; import org.junit.runner.RunWith; @@ -81,10 +83,10 @@ public class S3SingleUploadServiceTest extends AbstractS3Test { writeStatus.setLength(content.length); final BytecountStreamListener count = new BytecountStreamListener(); final S3AccessControlListFeature acl = new S3AccessControlListFeature(session); - final CryptoUploadFeature m = new CryptoUploadFeature<>(session, - new S3SingleUploadService(session, new S3WriteFeature(session, acl)), - new S3WriteFeature(session, acl), cryptomator); - m.upload(test, local, new BandwidthThrottle(BandwidthThrottle.UNLIMITED), new DisabledProgressListener(), count, writeStatus, null); + final CryptoUploadFeature m = new CryptoUploadFeature<>(session, + new S3SingleUploadService(session), + cryptomator); + m.upload(new CryptoWriteFeature<>(session, new S3WriteFeature(session, acl), cryptomator), test, local, new BandwidthThrottle(BandwidthThrottle.UNLIMITED), new DisabledProgressListener(), count, writeStatus, null); assertEquals(content.length, count.getSent()); assertTrue(writeStatus.isComplete()); assertTrue(cryptomator.getFeature(session, Find.class, new S3FindFeature(session, acl)).find(test)); diff --git a/s3/src/test/java/ch/cyberduck/core/cryptomator/S3TouchFeatureTest.java b/s3/src/test/java/ch/cyberduck/core/cryptomator/S3TouchFeatureTest.java index df96b40af7..6ff42ecdb3 100644 --- a/s3/src/test/java/ch/cyberduck/core/cryptomator/S3TouchFeatureTest.java +++ b/s3/src/test/java/ch/cyberduck/core/cryptomator/S3TouchFeatureTest.java @@ -20,6 +20,7 @@ import ch.cyberduck.core.DisabledLoginCallback; import ch.cyberduck.core.DisabledPasswordCallback; import ch.cyberduck.core.Path; import ch.cyberduck.core.cryptomator.features.CryptoTouchFeature; +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; @@ -37,6 +38,7 @@ import ch.cyberduck.core.vault.DefaultVaultRegistry; import ch.cyberduck.core.vault.VaultCredentials; import ch.cyberduck.test.IntegrationTest; +import org.jets3t.service.model.StorageObject; import org.junit.Ignore; import org.junit.Test; import org.junit.experimental.categories.Category; @@ -63,8 +65,8 @@ public class S3TouchFeatureTest extends AbstractS3Test { session.withRegistry(new DefaultVaultRegistry(new DisabledPasswordCallback(), cryptomator)); final TransferStatus status = new TransferStatus(); final S3AccessControlListFeature acl = new S3AccessControlListFeature(session); - final Path test = new CryptoTouchFeature<>(session, new S3TouchFeature(session, acl), new S3WriteFeature(session, acl), cryptomator).touch( - new Path(vault, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)), status); + final Path test = new CryptoTouchFeature<>(session, new S3TouchFeature(session, acl), cryptomator).touch( + new CryptoWriteFeature<>(session, new S3WriteFeature(session, new S3AccessControlListFeature(session)), cryptomator), new Path(vault, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)), status); assertEquals(0L, test.attributes().getSize()); assertEquals(0L, status.getResponse().getSize()); assertTrue(cryptomator.getFeature(session, Find.class, new DefaultFindFeature(session)).find(test)); @@ -82,8 +84,8 @@ public class S3TouchFeatureTest extends AbstractS3Test { session.withRegistry(new DefaultVaultRegistry(new DisabledPasswordCallback(), cryptomator)); final TransferStatus status = new TransferStatus(); final S3AccessControlListFeature acl = new S3AccessControlListFeature(session); - final Path test = new CryptoTouchFeature<>(session, new S3TouchFeature(session, acl), new S3WriteFeature(session, acl), cryptomator).touch( - new Path(vault, new AlphanumericRandomStringService(130).random(), EnumSet.of(Path.Type.file)), status); + final Path test = new CryptoTouchFeature<>(session, new S3TouchFeature(session, acl), cryptomator).touch( + new CryptoWriteFeature<>(session, new S3WriteFeature(session, new S3AccessControlListFeature(session)), cryptomator), new Path(vault, new AlphanumericRandomStringService(130).random(), EnumSet.of(Path.Type.file)), status); assertEquals(0L, test.attributes().getSize()); assertEquals(0L, status.getResponse().getSize()); assertTrue(cryptomator.getFeature(session, Find.class, new S3FindFeature(session, acl)).find(test)); @@ -100,8 +102,8 @@ public class S3TouchFeatureTest extends AbstractS3Test { session.withRegistry(new DefaultVaultRegistry(new DisabledPasswordCallback(), cryptomator)); final TransferStatus status = new TransferStatus(); final S3AccessControlListFeature acl = new S3AccessControlListFeature(session); - final Path test = new CryptoTouchFeature<>(session, new DefaultTouchFeature<>(new S3WriteFeature(session, acl)), new S3WriteFeature(session, acl), cryptomator).touch( - new Path(vault, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)), status); + final Path test = new CryptoTouchFeature<>(session, new DefaultTouchFeature(session), cryptomator).touch( + new CryptoWriteFeature<>(session, new S3WriteFeature(session, new S3AccessControlListFeature(session)), cryptomator), new Path(vault, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)), status); assertEquals(0L, test.attributes().getSize()); assertEquals(0L, status.getResponse().getSize()); assertTrue(cryptomator.getFeature(session, Find.class, new DefaultFindFeature(session)).find(test)); diff --git a/s3/src/test/java/ch/cyberduck/core/cryptomator/S3VersioningFeatureTest.java b/s3/src/test/java/ch/cyberduck/core/cryptomator/S3VersioningFeatureTest.java index b6067b9636..1e1a523501 100644 --- a/s3/src/test/java/ch/cyberduck/core/cryptomator/S3VersioningFeatureTest.java +++ b/s3/src/test/java/ch/cyberduck/core/cryptomator/S3VersioningFeatureTest.java @@ -19,6 +19,7 @@ package ch.cyberduck.core.cryptomator; 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; @@ -76,9 +77,9 @@ public class S3VersioningFeatureTest extends AbstractS3Test { session.withRegistry(new DefaultVaultRegistry(new DisabledPasswordCallback(), cryptomator)); final S3AccessControlListFeature acl = new S3AccessControlListFeature(session); final AttributesFinder f = cryptomator.getFeature(session, AttributesFinder.class, new S3AttributesFinderFeature(session, acl)); - final Path test = new CryptoTouchFeature<>(session, new S3TouchFeature(session, acl), new S3WriteFeature(session, acl), cryptomator).touch( - new Path(vault, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)), new TransferStatus()); - final PathAttributes initialAttributes = new PathAttributes(test.attributes()); + final Path test = new CryptoTouchFeature<>(session, new S3TouchFeature(session, acl), cryptomator).touch( + new CryptoWriteFeature<>(session, new S3WriteFeature(session, new S3AccessControlListFeature(session)), cryptomator), new Path(vault, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)), new TransferStatus()); + final PathAttributes initialAttributes = new DefaultPathAttributes(test.attributes()); final String initialVersion = test.attributes().getVersionId(); final byte[] content = RandomUtils.nextBytes(32769); final TransferStatus status = new TransferStatus(); diff --git a/s3/src/test/java/ch/cyberduck/core/kms/KMSEncryptionFeatureTest.java b/s3/src/test/java/ch/cyberduck/core/kms/KMSEncryptionFeatureTest.java index 3d445ac9d1..3a87dc38b7 100644 --- a/s3/src/test/java/ch/cyberduck/core/kms/KMSEncryptionFeatureTest.java +++ b/s3/src/test/java/ch/cyberduck/core/kms/KMSEncryptionFeatureTest.java @@ -29,6 +29,7 @@ import ch.cyberduck.core.s3.S3DefaultDeleteFeature; import ch.cyberduck.core.s3.S3EncryptionFeature; import ch.cyberduck.core.s3.S3LocationFeature; import ch.cyberduck.core.s3.S3TouchFeature; +import ch.cyberduck.core.s3.S3WriteFeature; import ch.cyberduck.core.ssl.DefaultX509KeyManager; import ch.cyberduck.core.ssl.DisabledX509TrustManager; import ch.cyberduck.core.transfer.TransferStatus; @@ -50,7 +51,7 @@ public class KMSEncryptionFeatureTest extends AbstractS3Test { public void testSetEncryptionKMSDefaultKeySignatureVersionV4() throws Exception { final Path container = new Path("test-eu-central-1-cyberduck", EnumSet.of(Path.Type.volume, Path.Type.directory)); final S3AccessControlListFeature acl = new S3AccessControlListFeature(session); - final Path test = new S3TouchFeature(session, acl).touch(new Path(container, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)), new TransferStatus()); + final Path test = new S3TouchFeature(session, acl).touch(new S3WriteFeature(session, new S3AccessControlListFeature(session)), new Path(container, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)), new TransferStatus()); final S3EncryptionFeature feature = new S3EncryptionFeature(session, acl); feature.setEncryption(test, KMSEncryptionFeature.SSE_KMS_DEFAULT); final Encryption.Algorithm value = feature.getEncryption(test); @@ -70,7 +71,7 @@ public class KMSEncryptionFeatureTest extends AbstractS3Test { public void testSetEncryptionKMSCustomKeySignatureVersionV4() throws Exception { final Path container = new Path("test-eu-west-1-cyberduck", EnumSet.of(Path.Type.volume, Path.Type.directory)); final Path test = new Path(container, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)); - new S3TouchFeature(session, new S3AccessControlListFeature(session)).touch(test, new TransferStatus()); + new S3TouchFeature(session, new S3AccessControlListFeature(session)).touch(new S3WriteFeature(session, new S3AccessControlListFeature(session)), test, new TransferStatus()); final S3EncryptionFeature feature = new S3EncryptionFeature(session, new S3AccessControlListFeature(session)); feature.setEncryption(test, new Encryption.Algorithm("aws:kms", "arn:aws:kms:eu-west-1:930717317329:key/015fa0af-f95e-483e-8fb6-abffb46fb783")); final Encryption.Algorithm value = feature.getEncryption(test); diff --git a/s3/src/test/java/ch/cyberduck/core/profiles/ProfilesSynchronizeWorkerTest.java b/s3/src/test/java/ch/cyberduck/core/profiles/ProfilesSynchronizeWorkerTest.java index abaef2d3a3..150ed89ba6 100644 --- a/s3/src/test/java/ch/cyberduck/core/profiles/ProfilesSynchronizeWorkerTest.java +++ b/s3/src/test/java/ch/cyberduck/core/profiles/ProfilesSynchronizeWorkerTest.java @@ -50,7 +50,7 @@ public class ProfilesSynchronizeWorkerTest { public void testRunCloudfrontEndpoint() throws Exception { // Registry in temporary folder final ProtocolFactory protocols = new ProtocolFactory(new HashSet<>(Collections.singletonList(new S3Protocol()))); - final Host host = new HostParser(protocols, new S3Protocol()).get("s3://djynunjb246r8.cloudfront.net").withCredentials( + final Host host = new HostParser(protocols, new S3Protocol()).get("s3://djynunjb246r8.cloudfront.net").setCredentials( new Credentials(PreferencesFactory.get().getProperty("connection.login.anon.name"))); final Session session = new S3Session(host, new DisabledX509TrustManager(), new DefaultX509KeyManager()); session.open(new DisabledProxyFinder(), new DisabledHostKeyCallback(), new DisabledLoginCallback(), new DisabledCancelCallback()); @@ -98,7 +98,7 @@ public class ProfilesSynchronizeWorkerTest { public void testRunVirtualHostEndpoint() throws Exception { // Registry in temporary folder final ProtocolFactory protocols = new ProtocolFactory(new HashSet<>(Collections.singletonList(new S3Protocol()))); - final Host host = new HostParser(protocols, new S3Protocol()).get("s3:/profiles.cyberduck.io").withCredentials( + final Host host = new HostParser(protocols, new S3Protocol()).get("s3:/profiles.cyberduck.io").setCredentials( new Credentials(PreferencesFactory.get().getProperty("connection.login.anon.name"))); final Session session = new S3Session(host, new DisabledX509TrustManager(), new DefaultX509KeyManager()); session.open(new DisabledProxyFinder(), new DisabledHostKeyCallback(), new DisabledLoginCallback(), new DisabledCancelCallback()); diff --git a/s3/src/test/java/ch/cyberduck/core/profiles/RemoteProfilesFinderTest.java b/s3/src/test/java/ch/cyberduck/core/profiles/RemoteProfilesFinderTest.java index 015988bf03..2cfec89023 100644 --- a/s3/src/test/java/ch/cyberduck/core/profiles/RemoteProfilesFinderTest.java +++ b/s3/src/test/java/ch/cyberduck/core/profiles/RemoteProfilesFinderTest.java @@ -15,6 +15,7 @@ package ch.cyberduck.core.profiles; * GNU General Public License for more details. */ +import ch.cyberduck.core.Credentials; import ch.cyberduck.core.DisabledCancelCallback; import ch.cyberduck.core.DisabledHostKeyCallback; import ch.cyberduck.core.DisabledLoginCallback; @@ -22,8 +23,8 @@ import ch.cyberduck.core.HostParser; import ch.cyberduck.core.ProtocolFactory; import ch.cyberduck.core.Session; import ch.cyberduck.core.io.Checksum; +import ch.cyberduck.core.preferences.PreferencesFactory; import ch.cyberduck.core.proxy.DisabledProxyFinder; -import ch.cyberduck.core.proxy.Proxy; import ch.cyberduck.core.s3.S3Protocol; import ch.cyberduck.core.s3.S3Session; import ch.cyberduck.core.ssl.DefaultX509KeyManager; @@ -51,7 +52,8 @@ public class RemoteProfilesFinderTest { return true; } })); - final Session session = new S3Session(new HostParser(protocols).get("s3:/profiles.cyberduck.io"), new DisabledX509TrustManager(), new DefaultX509KeyManager()); + final Session session = new S3Session(new HostParser(protocols).get("s3:/profiles.cyberduck.io") + .setCredentials(new Credentials(PreferencesFactory.get().getProperty("connection.login.anon.name"))), new DisabledX509TrustManager(), new DefaultX509KeyManager()); session.open(new DisabledProxyFinder(), new DisabledHostKeyCallback(), new DisabledLoginCallback(), new DisabledCancelCallback()); final RemoteProfilesFinder finder = new RemoteProfilesFinder(session); final Set stream = finder.find(); diff --git a/s3/src/test/java/ch/cyberduck/core/s3/AbstractS3Test.java b/s3/src/test/java/ch/cyberduck/core/s3/AbstractS3Test.java index 6577317469..393db04998 100644 --- a/s3/src/test/java/ch/cyberduck/core/s3/AbstractS3Test.java +++ b/s3/src/test/java/ch/cyberduck/core/s3/AbstractS3Test.java @@ -88,7 +88,6 @@ public abstract class AbstractS3Test extends VaultTest { }, new DisabledHostKeyCallback(), new DisabledPasswordStore(), new DisabledProgressListener()); login.check(session, new DisabledCancelCallback()); - session.getHost().getCredentials().setPassword(PROPERTIES.get("s3.secret")); } @Before @@ -109,7 +108,6 @@ public abstract class AbstractS3Test extends VaultTest { }, new DisabledHostKeyCallback(), new DisabledPasswordStore(), new DisabledProgressListener()); login.check(virtualhost, new DisabledCancelCallback()); - virtualhost.getHost().getCredentials().setPassword(PROPERTIES.get("s3.secret")); } @Before @@ -117,7 +115,7 @@ public abstract class AbstractS3Test extends VaultTest { final ProtocolFactory factory = new ProtocolFactory(new HashSet<>(Collections.singleton(new S3Protocol()))); final Profile profile = new ProfilePlistReader(factory).read( this.getClass().getResourceAsStream("/S3 (HTTPS).cyberduckprofile")); - final Host host = new Host(profile, "d4fobtprygi46.cloudfront.net", new Credentials("anonymous")).withRegion("eu-central-1"); + final Host host = new Host(profile, "d4fobtprygi46.cloudfront.net", new Credentials("anonymous")).setRegion("eu-central-1"); cloudfront = new S3Session(host, new DefaultX509TrustManager(), new DefaultX509KeyManager()); final LoginConnectionService login = new LoginConnectionService(new DisabledLoginCallback() { @Override @@ -128,6 +126,5 @@ public abstract class AbstractS3Test extends VaultTest { }, new DisabledHostKeyCallback(), new DisabledPasswordStore(), new DisabledProgressListener()); login.check(cloudfront, new DisabledCancelCallback()); - cloudfront.getHost().getCredentials().setPassword(PROPERTIES.get("s3.secret")); } } diff --git a/s3/src/test/java/ch/cyberduck/core/s3/S3AccessControlListFeatureTest.java b/s3/src/test/java/ch/cyberduck/core/s3/S3AccessControlListFeatureTest.java index f98de43641..5f12ead558 100644 --- a/s3/src/test/java/ch/cyberduck/core/s3/S3AccessControlListFeatureTest.java +++ b/s3/src/test/java/ch/cyberduck/core/s3/S3AccessControlListFeatureTest.java @@ -87,7 +87,7 @@ public class S3AccessControlListFeatureTest extends AbstractS3Test { public void testWrite() throws Exception { final Path container = new Path("test-eu-central-1-cyberduck", EnumSet.of(Path.Type.directory, Path.Type.volume)); final Path test = new Path(container, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)); - new S3TouchFeature(session, new S3AccessControlListFeature(session)).touch(test, new TransferStatus()); + new S3TouchFeature(session, new S3AccessControlListFeature(session)).touch(new S3WriteFeature(session, new S3AccessControlListFeature(session)), test, new TransferStatus()); final S3AccessControlListFeature f = new S3AccessControlListFeature(session); final Acl acl = new Acl(); acl.addAll(new Acl.GroupUser(Acl.GroupUser.EVERYONE), new Acl.Role(Acl.Role.READ)); @@ -102,8 +102,8 @@ public class S3AccessControlListFeatureTest extends AbstractS3Test { @Test public void testWriteVirtualHostBucket() throws Exception { final Path test = new Path(new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)); - new S3TouchFeature(virtualhost, new S3AccessControlListFeature(virtualhost)).touch(test, new TransferStatus()); final S3AccessControlListFeature f = new S3AccessControlListFeature(virtualhost); + new S3TouchFeature(virtualhost, f).touch(new S3WriteFeature(virtualhost, f), test, new TransferStatus()); final Acl acl = new Acl(); acl.addAll(new Acl.Owner("80b9982b7b08045ee86680cc47f43c84bf439494a89ece22b5330f8a49477cf6"), new Acl.Role(Acl.Role.FULL)); acl.addAll(new Acl.GroupUser(Acl.GroupUser.EVERYONE), new Acl.Role(Acl.Role.READ)); @@ -132,7 +132,7 @@ public class S3AccessControlListFeatureTest extends AbstractS3Test { final Path container = new Path(String.format("cd-%s", new AlphanumericRandomStringService().random().toLowerCase(Locale.getDefault())), EnumSet.of(Path.Type.directory, Path.Type.volume)); new S3BucketCreateService(session).create(container, null); final Path test = new Path(container, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)); - new S3TouchFeature(session, new S3AccessControlListFeature(session)).touch(test, new TransferStatus()); + new S3TouchFeature(session, new S3AccessControlListFeature(session)).touch(new S3WriteFeature(session, new S3AccessControlListFeature(session)), test, new TransferStatus()); final S3AccessControlListFeature f = new S3AccessControlListFeature(session); { final Acl acl = new Acl(); @@ -153,7 +153,7 @@ public class S3AccessControlListFeatureTest extends AbstractS3Test { @Test public void testReadWithDelimiter() throws Exception { final Path container = new Path("test-eu-central-1-cyberduck", EnumSet.of(Path.Type.directory, Path.Type.volume)); - final Path test = new S3TouchFeature(session, new S3AccessControlListFeature(session)).touch(new Path(new Path(container, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory)), new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)), new TransferStatus()); + final Path test = new S3TouchFeature(session, new S3AccessControlListFeature(session)).touch(new S3WriteFeature(session, new S3AccessControlListFeature(session)), new Path(new Path(container, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory)), new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)), new TransferStatus()); final S3AccessControlListFeature f = new S3AccessControlListFeature(session); assertNotNull(f.getPermission(test)); new S3DefaultDeleteFeature(session, new S3AccessControlListFeature(session)).delete(Collections.singletonList(test), new DisabledLoginCallback(), new Delete.DisabledCallback()); @@ -163,7 +163,7 @@ public class S3AccessControlListFeatureTest extends AbstractS3Test { public void testReadDirectoryPlaceholder() throws Exception { final Path container = new Path("test-eu-central-1-cyberduck", EnumSet.of(Path.Type.directory, Path.Type.volume)); final S3AccessControlListFeature acl = new S3AccessControlListFeature(session); - final Path placeholder = new S3DirectoryFeature(session, new S3WriteFeature(session, acl), acl).mkdir(new Path(container, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory)), new TransferStatus()); + final Path placeholder = new S3DirectoryFeature(session, acl).mkdir(new S3WriteFeature(session, new S3AccessControlListFeature(session)), new Path(container, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory)), new TransferStatus()); assertNotNull(acl.getPermission(placeholder)); new S3DefaultDeleteFeature(session, acl).delete(Collections.singletonList(placeholder), new DisabledLoginCallback(), new Delete.DisabledCallback()); } @@ -190,7 +190,7 @@ public class S3AccessControlListFeatureTest extends AbstractS3Test { @Test public void testReadVersioned() throws Exception { final Path container = new Path("versioning-test-eu-central-1-cyberduck", EnumSet.of(Path.Type.directory, Path.Type.volume)); - final Path test = new S3TouchFeature(session, new S3AccessControlListFeature(session)).touch(new Path(container, new AsciiRandomStringService().random(), EnumSet.of(Path.Type.file)), new TransferStatus()); + final Path test = new S3TouchFeature(session, new S3AccessControlListFeature(session)).touch(new S3WriteFeature(session, new S3AccessControlListFeature(session)), new Path(container, new AsciiRandomStringService().random(), EnumSet.of(Path.Type.file)), new TransferStatus()); assertTrue(new DefaultFindFeature(session).find(test)); try { new S3AccessControlListFeature(session).getPermission(test); @@ -204,37 +204,37 @@ public class S3AccessControlListFeatureTest extends AbstractS3Test { @Test public void testRoles() { final S3AccessControlListFeature f = new S3AccessControlListFeature(session); - assertTrue(f.getAvailableAclUsers().stream().filter(user -> user instanceof Acl.CanonicalUser).findAny().isPresent()); - assertTrue(f.getAvailableAclUsers().stream().filter(user -> user instanceof Acl.EmailUser).findAny().isPresent()); + assertTrue(f.getAvailableAclUsers(Collections.emptyList()).stream().filter(user -> user instanceof Acl.CanonicalUser).findAny().isPresent()); + assertTrue(f.getAvailableAclUsers(Collections.emptyList()).stream().filter(user -> user instanceof Acl.EmailUser).findAny().isPresent()); } @Test public void testCannedLists() { final S3AccessControlListFeature f = new S3AccessControlListFeature(session); - assertSame(Acl.CANNED_PRIVATE, f.toAcl(AccessControlList.REST_CANNED_PRIVATE)); - assertSame(Acl.CANNED_PUBLIC_READ, f.toAcl(AccessControlList.REST_CANNED_PUBLIC_READ)); - assertSame(Acl.CANNED_PUBLIC_READ_WRITE, f.toAcl(AccessControlList.REST_CANNED_PUBLIC_READ_WRITE)); - assertSame(Acl.CANNED_AUTHENTICATED_READ, f.toAcl(AccessControlList.REST_CANNED_AUTHENTICATED_READ)); - assertSame(Acl.CANNED_BUCKET_OWNER_FULLCONTROL, f.toAcl(AccessControlList.REST_CANNED_BUCKET_OWNER_FULLCONTROL)); - assertSame(Acl.CANNED_BUCKET_OWNER_READ, f.toAcl(AccessControlList.REST_CANNED_BUCKET_OWNER_READ)); + assertSame(Acl.CANNED_PRIVATE, S3AccessControlListFeature.toAcl(AccessControlList.REST_CANNED_PRIVATE)); + assertSame(Acl.CANNED_PUBLIC_READ, S3AccessControlListFeature.toAcl(AccessControlList.REST_CANNED_PUBLIC_READ)); + assertSame(Acl.CANNED_PUBLIC_READ_WRITE, S3AccessControlListFeature.toAcl(AccessControlList.REST_CANNED_PUBLIC_READ_WRITE)); + assertSame(Acl.CANNED_AUTHENTICATED_READ, S3AccessControlListFeature.toAcl(AccessControlList.REST_CANNED_AUTHENTICATED_READ)); + assertSame(Acl.CANNED_BUCKET_OWNER_FULLCONTROL, S3AccessControlListFeature.toAcl(AccessControlList.REST_CANNED_BUCKET_OWNER_FULLCONTROL)); + assertSame(Acl.CANNED_BUCKET_OWNER_READ, S3AccessControlListFeature.toAcl(AccessControlList.REST_CANNED_BUCKET_OWNER_READ)); - assertSame(AccessControlList.REST_CANNED_PRIVATE, f.toAcl(Acl.CANNED_PRIVATE)); - assertEquals(Acl.CANNED_PRIVATE.getCannedString(), f.toAcl(Acl.CANNED_PRIVATE).getValueForRESTHeaderACL()); + assertSame(AccessControlList.REST_CANNED_PRIVATE, S3AccessControlListFeature.toAcl(Acl.CANNED_PRIVATE)); + assertEquals(Acl.CANNED_PRIVATE.getCannedString(), S3AccessControlListFeature.toAcl(Acl.CANNED_PRIVATE).getValueForRESTHeaderACL()); - assertSame(AccessControlList.REST_CANNED_PUBLIC_READ, f.toAcl(Acl.CANNED_PUBLIC_READ)); - assertEquals(Acl.CANNED_PUBLIC_READ.getCannedString(), f.toAcl(Acl.CANNED_PUBLIC_READ).getValueForRESTHeaderACL()); + assertSame(AccessControlList.REST_CANNED_PUBLIC_READ, S3AccessControlListFeature.toAcl(Acl.CANNED_PUBLIC_READ)); + assertEquals(Acl.CANNED_PUBLIC_READ.getCannedString(), S3AccessControlListFeature.toAcl(Acl.CANNED_PUBLIC_READ).getValueForRESTHeaderACL()); - assertSame(AccessControlList.REST_CANNED_PUBLIC_READ_WRITE, f.toAcl(Acl.CANNED_PUBLIC_READ_WRITE)); - assertEquals(Acl.CANNED_PUBLIC_READ_WRITE.getCannedString(), f.toAcl(Acl.CANNED_PUBLIC_READ_WRITE).getValueForRESTHeaderACL()); + assertSame(AccessControlList.REST_CANNED_PUBLIC_READ_WRITE, S3AccessControlListFeature.toAcl(Acl.CANNED_PUBLIC_READ_WRITE)); + assertEquals(Acl.CANNED_PUBLIC_READ_WRITE.getCannedString(), S3AccessControlListFeature.toAcl(Acl.CANNED_PUBLIC_READ_WRITE).getValueForRESTHeaderACL()); - assertSame(AccessControlList.REST_CANNED_AUTHENTICATED_READ, f.toAcl(Acl.CANNED_AUTHENTICATED_READ)); - assertEquals(Acl.CANNED_AUTHENTICATED_READ.getCannedString(), f.toAcl(Acl.CANNED_AUTHENTICATED_READ).getValueForRESTHeaderACL()); + assertSame(AccessControlList.REST_CANNED_AUTHENTICATED_READ, S3AccessControlListFeature.toAcl(Acl.CANNED_AUTHENTICATED_READ)); + assertEquals(Acl.CANNED_AUTHENTICATED_READ.getCannedString(), S3AccessControlListFeature.toAcl(Acl.CANNED_AUTHENTICATED_READ).getValueForRESTHeaderACL()); - assertSame(AccessControlList.REST_CANNED_BUCKET_OWNER_FULLCONTROL, f.toAcl(Acl.CANNED_BUCKET_OWNER_FULLCONTROL)); - assertEquals(Acl.CANNED_BUCKET_OWNER_FULLCONTROL.getCannedString(), f.toAcl(Acl.CANNED_BUCKET_OWNER_FULLCONTROL).getValueForRESTHeaderACL()); + assertSame(AccessControlList.REST_CANNED_BUCKET_OWNER_FULLCONTROL, S3AccessControlListFeature.toAcl(Acl.CANNED_BUCKET_OWNER_FULLCONTROL)); + assertEquals(Acl.CANNED_BUCKET_OWNER_FULLCONTROL.getCannedString(), S3AccessControlListFeature.toAcl(Acl.CANNED_BUCKET_OWNER_FULLCONTROL).getValueForRESTHeaderACL()); - assertSame(AccessControlList.REST_CANNED_BUCKET_OWNER_READ, f.toAcl(Acl.CANNED_BUCKET_OWNER_READ)); - assertEquals(Acl.CANNED_BUCKET_OWNER_READ.getCannedString(), f.toAcl(Acl.CANNED_BUCKET_OWNER_READ).getValueForRESTHeaderACL()); + assertSame(AccessControlList.REST_CANNED_BUCKET_OWNER_READ, S3AccessControlListFeature.toAcl(Acl.CANNED_BUCKET_OWNER_READ)); + assertEquals(Acl.CANNED_BUCKET_OWNER_READ.getCannedString(), S3AccessControlListFeature.toAcl(Acl.CANNED_BUCKET_OWNER_READ).getValueForRESTHeaderACL()); } @Test @@ -242,13 +242,13 @@ public class S3AccessControlListFeatureTest extends AbstractS3Test { final S3AccessControlListFeature f = new S3AccessControlListFeature(session); final AccessControlList list = new AccessControlList(); list.setOwner(new StorageOwner("", "")); - assertEquals(Acl.EMPTY, f.toAcl(list)); + assertEquals(Acl.EMPTY, S3AccessControlListFeature.toAcl(list)); } @Test public void testInvalidOwner() { final S3AccessControlListFeature f = new S3AccessControlListFeature(session); - assertNull(f.toAcl(Acl.EMPTY)); - assertNull(f.toAcl(new Acl(new Acl.UserAndRole(new Acl.Owner(""), new Acl.Role(Acl.Role.FULL))))); + assertNull(S3AccessControlListFeature.toAcl(Acl.EMPTY)); + assertNull(S3AccessControlListFeature.toAcl(new Acl(new Acl.UserAndRole(new Acl.Owner(""), new Acl.Role(Acl.Role.FULL))))); } } diff --git a/s3/src/test/java/ch/cyberduck/core/s3/S3AttributesFinderFeatureTest.java b/s3/src/test/java/ch/cyberduck/core/s3/S3AttributesFinderFeatureTest.java index 172e09cbcf..5c5708fd3c 100644 --- a/s3/src/test/java/ch/cyberduck/core/s3/S3AttributesFinderFeatureTest.java +++ b/s3/src/test/java/ch/cyberduck/core/s3/S3AttributesFinderFeatureTest.java @@ -22,7 +22,6 @@ import java.util.Arrays; import java.util.Collections; import java.util.EnumSet; -import static ch.cyberduck.core.s3.S3VersionedObjectListService.KEY_DELETE_MARKER; import static org.junit.Assert.*; @Category(IntegrationTest.class) @@ -38,7 +37,7 @@ public class S3AttributesFinderFeatureTest extends AbstractS3Test { public void testFindFileUsEast() throws Exception { final Path container = new Path("test-us-east-1-cyberduck", EnumSet.of(Path.Type.directory, Path.Type.volume)); final S3AccessControlListFeature acl = new S3AccessControlListFeature(session); - final Path test = new S3TouchFeature(session, acl).touch(new Path(container, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)), new TransferStatus()); + final Path test = new S3TouchFeature(session, acl).touch(new S3WriteFeature(session, new S3AccessControlListFeature(session)), new Path(container, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)), new TransferStatus()); final S3AttributesFinderFeature f = new S3AttributesFinderFeature(session, acl); final PathAttributes attributes = f.find(test); assertEquals(0L, attributes.getSize()); @@ -91,7 +90,7 @@ public class S3AttributesFinderFeatureTest extends AbstractS3Test { public void testFindPlaceholder() throws Exception { final Path container = new Path("test-eu-central-1-cyberduck", EnumSet.of(Path.Type.directory, Path.Type.volume)); final S3AccessControlListFeature acl = new S3AccessControlListFeature(session); - final Path test = new S3DirectoryFeature(session, new S3WriteFeature(session, acl), acl).mkdir(new Path(container, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory)), new TransferStatus()); + final Path test = new S3DirectoryFeature(session, acl).mkdir(new S3WriteFeature(session, new S3AccessControlListFeature(session)), new Path(container, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory)), new TransferStatus()); final PathAttributes attributes = new S3AttributesFinderFeature(session, acl).find(test); new S3DefaultDeleteFeature(session, acl).delete(Collections.singletonList(test), new DisabledLoginCallback(), new Delete.DisabledCallback()); assertEquals(0L, attributes.getSize()); @@ -104,7 +103,7 @@ public class S3AttributesFinderFeatureTest extends AbstractS3Test { final Path bucket = new Path("versioning-test-eu-central-1-cyberduck", EnumSet.of(Path.Type.volume, Path.Type.directory)); final Path f = new Path(bucket, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)); final S3AccessControlListFeature acl = new S3AccessControlListFeature(session); - final Path testWithVersionId = new S3TouchFeature(session, acl).touch(f, new TransferStatus()); + final Path testWithVersionId = new S3TouchFeature(session, acl).touch(new S3WriteFeature(session, acl), f, new TransferStatus()); final PathAttributes attr = new S3AttributesFinderFeature(session, acl).find(f); final String versionId = attr.getVersionId(); assertNotNull(versionId); @@ -115,14 +114,6 @@ public class S3AttributesFinderFeatureTest extends AbstractS3Test { { final PathAttributes marker = new S3AttributesFinderFeature(session, acl).find(testWithVersionId); assertTrue(marker.isDuplicate()); - assertFalse(marker.getCustom().containsKey(KEY_DELETE_MARKER)); - assertNotNull(marker.getVersionId()); - assertEquals(versionId, marker.getVersionId()); - } - { - final PathAttributes marker = new S3AttributesFinderFeature(session, acl).find(testWithVersionId); - assertTrue(marker.isDuplicate()); - assertFalse(marker.getCustom().containsKey(KEY_DELETE_MARKER)); assertNotNull(marker.getVersionId()); assertEquals(versionId, marker.getVersionId()); } @@ -152,7 +143,7 @@ public class S3AttributesFinderFeatureTest extends AbstractS3Test { container.attributes().setRegion("us-east-1"); final S3AccessControlListFeature acl = new S3AccessControlListFeature(session); final Path file = new S3TouchFeature(session, acl).touch( - new Path(container, String.format("%s~", new AlphanumericRandomStringService().random()), EnumSet.of(Path.Type.file)), new TransferStatus()); + new S3WriteFeature(session, new S3AccessControlListFeature(session)), new Path(container, String.format("%s~", new AlphanumericRandomStringService().random()), EnumSet.of(Path.Type.file)), new TransferStatus()); new S3AttributesFinderFeature(session, acl).find(file); new S3DefaultDeleteFeature(session, acl).delete(Collections.singletonList(file), new DisabledLoginCallback(), new Delete.DisabledCallback()); } @@ -162,7 +153,7 @@ public class S3AttributesFinderFeatureTest extends AbstractS3Test { final Path container = new Path("test-eu-central-1-cyberduck", EnumSet.of(Path.Type.directory, Path.Type.volume)); final S3AccessControlListFeature acl = new S3AccessControlListFeature(session); final Path file = new S3TouchFeature(session, acl).touch( - new Path(container, String.format("%s@", new AlphanumericRandomStringService().random()), EnumSet.of(Path.Type.file)), new TransferStatus()); + new S3WriteFeature(session, new S3AccessControlListFeature(session)), new Path(container, String.format("%s@", new AlphanumericRandomStringService().random()), EnumSet.of(Path.Type.file)), new TransferStatus()); new S3AttributesFinderFeature(session, acl).find(file); new S3DefaultDeleteFeature(session, acl).delete(Collections.singletonList(file), new DisabledLoginCallback(), new Delete.DisabledCallback()); } @@ -171,9 +162,9 @@ public class S3AttributesFinderFeatureTest extends AbstractS3Test { public void testReadWhitespaceInKey() throws Exception { final Path container = new Path("test-eu-central-1-cyberduck", EnumSet.of(Path.Type.directory, Path.Type.volume)); final S3AccessControlListFeature acl = new S3AccessControlListFeature(session); - final Path directory = new S3DirectoryFeature(session, new S3WriteFeature(session, acl), acl).mkdir(new Path(container, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory)), new TransferStatus()); + final Path directory = new S3DirectoryFeature(session, acl).mkdir(new S3WriteFeature(session, new S3AccessControlListFeature(session)), new Path(container, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory)), new TransferStatus()); final Path file = new S3TouchFeature(session, new S3AccessControlListFeature(session)).touch( - new Path(directory, String.format("%s %s", new AlphanumericRandomStringService(4).random(), new AlphanumericRandomStringService(4).random()), EnumSet.of(Path.Type.file)), new TransferStatus()); + new S3WriteFeature(session, new S3AccessControlListFeature(session)), new Path(directory, String.format("%s %s", new AlphanumericRandomStringService(4).random(), new AlphanumericRandomStringService(4).random()), EnumSet.of(Path.Type.file)), new TransferStatus()); new S3AttributesFinderFeature(session, new S3AccessControlListFeature(session)).find(file); new S3DefaultDeleteFeature(session, acl).delete(Arrays.asList(directory, file), new DisabledLoginCallback(), new Delete.DisabledCallback()); } @@ -185,7 +176,7 @@ public class S3AttributesFinderFeatureTest extends AbstractS3Test { assertTrue(new S3FindFeature(session, acl).find(container)); final String prefix = new AlphanumericRandomStringService().random(); final Path test = new S3TouchFeature(session, acl).touch( - new Path(new Path(container, prefix, EnumSet.of(Path.Type.directory)), + new S3WriteFeature(session, new S3AccessControlListFeature(session)), new Path(new Path(container, prefix, EnumSet.of(Path.Type.directory)), new AsciiRandomStringService().random(), EnumSet.of(Path.Type.file)), new TransferStatus()); assertNotNull(new S3AttributesFinderFeature(session, acl).find(test)); assertNotNull(new S3AttributesFinderFeature(session, acl).find(new Path(container, prefix, EnumSet.of(Path.Type.directory)))); @@ -220,7 +211,7 @@ public class S3AttributesFinderFeatureTest extends AbstractS3Test { public void testDeleted() throws Exception { final Path bucket = new Path("versioning-test-eu-central-1-cyberduck", EnumSet.of(Path.Type.volume, Path.Type.directory)); final S3AccessControlListFeature acl = new S3AccessControlListFeature(session); - final Path test = new S3TouchFeature(session, acl).touch(new Path(bucket, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)), new TransferStatus()); + final Path test = new S3TouchFeature(session, acl).touch(new S3WriteFeature(session, new S3AccessControlListFeature(session)), new Path(bucket, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)), new TransferStatus()); assertNotNull(test.attributes().getVersionId()); assertNotEquals(PathAttributes.EMPTY, new S3AttributesFinderFeature(session, acl).find(test)); new S3DefaultDeleteFeature(session, acl).delete(Collections.singletonList(test), new DisabledPasswordCallback(), new Delete.DisabledCallback()); @@ -231,19 +222,21 @@ public class S3AttributesFinderFeatureTest extends AbstractS3Test { public void testDeletedWithMarker() throws Exception { final Path bucket = new Path("versioning-test-eu-central-1-cyberduck", EnumSet.of(Path.Type.volume, Path.Type.directory)); final S3AccessControlListFeature acl = new S3AccessControlListFeature(session); - final Path test = new S3TouchFeature(session, acl).touch(new Path(bucket, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)), new TransferStatus()); + final Path test = new S3TouchFeature(session, acl).touch(new S3WriteFeature(session, new S3AccessControlListFeature(session)), new Path(bucket, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)), new TransferStatus()); assertNotNull(test.attributes().getVersionId()); - assertNotEquals(PathAttributes.EMPTY, new S3AttributesFinderFeature(session, acl).find(test)); + assertEquals(test.attributes(), new S3AttributesFinderFeature(session, acl).find(test)); // Add delete marker new S3DefaultDeleteFeature(session, acl).delete(Collections.singletonList(new Path(test).withAttributes(PathAttributes.EMPTY)), new DisabledPasswordCallback(), new Delete.DisabledCallback()); + assertNotNull(test.attributes().getVersionId()); assertTrue(new S3FindFeature(session, acl).find(new Path(test))); - assertFalse(new S3AttributesFinderFeature(session, acl).find(test).getCustom().containsKey(KEY_DELETE_MARKER)); + assertTrue(new S3AttributesFinderFeature(session, acl).find(test).isDuplicate()); + assertFalse(new S3AttributesFinderFeature(session, acl).find(test).isTrashed()); assertFalse(new S3FindFeature(session, acl).find(new Path(test).withAttributes(PathAttributes.EMPTY))); // Test reading delete marker itself final Path marker = new S3VersionedObjectListService(session, acl).list(bucket, new DisabledListProgressListener()).find(new SimplePathPredicate(test)); - assertTrue(marker.attributes().isDuplicate()); - assertTrue(marker.attributes().getCustom().containsKey(KEY_DELETE_MARKER)); - assertTrue(new S3AttributesFinderFeature(session, acl).find(marker).getCustom().containsKey(KEY_DELETE_MARKER)); + assertTrue(marker.attributes().isTrashed()); + assertFalse(marker.attributes().isDuplicate()); + assertTrue(new S3AttributesFinderFeature(session, acl).find(marker).isTrashed()); assertTrue(new S3FindFeature(session, acl).find(marker)); } @@ -256,7 +249,7 @@ public class S3AttributesFinderFeatureTest extends AbstractS3Test { status.setAcl(Acl.CANNED_PUBLIC_READ); final S3AccessControlListFeature acl = new S3AccessControlListFeature(session); final Path file = new S3TouchFeature(session, acl).touch( - new Path(new Path("test-eu-central-1-cyberduck", EnumSet.of(Path.Type.directory, Path.Type.volume)), + new S3WriteFeature(session, new S3AccessControlListFeature(session)), new Path(new Path("test-eu-central-1-cyberduck", EnumSet.of(Path.Type.directory, Path.Type.volume)), name, EnumSet.of(Path.Type.file)), status); final PathAttributes attributes = f.find(new Path(file.getName(), EnumSet.of(Path.Type.file))); assertEquals(0L, attributes.getSize()); @@ -272,8 +265,8 @@ public class S3AttributesFinderFeatureTest extends AbstractS3Test { assertEquals(PathAttributes.EMPTY, f.find(new Path("/", EnumSet.of(Path.Type.directory)))); final String name = new AlphanumericRandomStringService().random(); final TransferStatus status = new TransferStatus(); - final Path file = new S3TouchFeature(virtualhost, new S3AccessControlListFeature(virtualhost)).touch( - new Path(name, EnumSet.of(Path.Type.file)), status); + final Path file = new S3TouchFeature(virtualhost, acl).touch( + new S3WriteFeature(virtualhost, acl), new Path(name, EnumSet.of(Path.Type.file)), status); final PathAttributes attributes = f.find(new Path(file.getName(), EnumSet.of(Path.Type.file))); assertEquals(0L, attributes.getSize()); assertEquals("d41d8cd98f00b204e9800998ecf8427e", attributes.getChecksum().hash); diff --git a/s3/src/test/java/ch/cyberduck/core/s3/S3CopyFeatureTest.java b/s3/src/test/java/ch/cyberduck/core/s3/S3CopyFeatureTest.java index 73b0a2dc5f..203042e150 100644 --- a/s3/src/test/java/ch/cyberduck/core/s3/S3CopyFeatureTest.java +++ b/s3/src/test/java/ch/cyberduck/core/s3/S3CopyFeatureTest.java @@ -43,7 +43,7 @@ public class S3CopyFeatureTest extends AbstractS3Test { final Path test = new Path(container, new AsciiRandomStringService().random(), EnumSet.of(Path.Type.file)); test.attributes().setSize(0L); final S3AccessControlListFeature acl = new S3AccessControlListFeature(session); - new S3TouchFeature(session, acl).touch(test, new TransferStatus()); + new S3TouchFeature(session, acl).touch(new S3WriteFeature(session, new S3AccessControlListFeature(session)), test, new TransferStatus()); final Path copy = new Path(container, new AsciiRandomStringService().random(), EnumSet.of(Path.Type.file)); new S3CopyFeature(session, acl).copy(test, copy, new TransferStatus(), new DisabledConnectionCallback(), new DisabledStreamListener()); assertTrue(new S3FindFeature(session, acl).find(test)); @@ -60,7 +60,7 @@ public class S3CopyFeatureTest extends AbstractS3Test { final TransferStatus status = new TransferStatus(); status.setMetadata(Collections.singletonMap("cyberduck", "m")); final S3AccessControlListFeature acl = new S3AccessControlListFeature(session); - final Path test = new S3TouchFeature(session, acl).touch(new Path(container, new AsciiRandomStringService().random(), EnumSet.of(Path.Type.file)), status); + final Path test = new S3TouchFeature(session, acl).touch(new S3WriteFeature(session, new S3AccessControlListFeature(session)), new Path(container, new AsciiRandomStringService().random(), EnumSet.of(Path.Type.file)), status); final Path copy = new S3CopyFeature(session, acl).copy(test, new Path(container, new AsciiRandomStringService().random(), EnumSet.of(Path.Type.file)), new TransferStatus(), new DisabledConnectionCallback(), new DisabledStreamListener()); assertTrue(new S3FindFeature(session, acl).find(test)); @@ -76,7 +76,7 @@ public class S3CopyFeatureTest extends AbstractS3Test { final Path bucket = new Path("test-eu-central-1-cyberduck", EnumSet.of(Path.Type.directory, Path.Type.volume)); final TransferStatus status = new TransferStatus(); final S3AccessControlListFeature acl = new S3AccessControlListFeature(session); - final Path test = new S3TouchFeature(session, acl).touch(new Path(bucket, new AsciiRandomStringService().random(), EnumSet.of(Path.Type.file)), status); + final Path test = new S3TouchFeature(session, acl).touch(new S3WriteFeature(session, new S3AccessControlListFeature(session)), new Path(bucket, new AsciiRandomStringService().random(), EnumSet.of(Path.Type.file)), status); final Path copy = new S3CopyFeature(session, acl).copy(test, new Path(new Path("test-eu-central-1-cyberduck-ownerenforced", EnumSet.of(Path.Type.directory, Path.Type.volume)), new AsciiRandomStringService().random(), EnumSet.of(Path.Type.file)), new TransferStatus(), new DisabledConnectionCallback(), new DisabledStreamListener()); assertTrue(new S3FindFeature(session, acl).find(test)); @@ -91,7 +91,7 @@ public class S3CopyFeatureTest extends AbstractS3Test { final TransferStatus status = new TransferStatus(); status.setMetadata(Collections.singletonMap("cyberduck", "m")); final S3AccessControlListFeature acl = new S3AccessControlListFeature(session); - final Path test = new S3TouchFeature(session, acl).touch(new Path(container, new AsciiRandomStringService().random(), EnumSet.of(Path.Type.file)), status); + final Path test = new S3TouchFeature(session, acl).touch(new S3WriteFeature(session, new S3AccessControlListFeature(session)), new Path(container, new AsciiRandomStringService().random(), EnumSet.of(Path.Type.file)), status); assertNotNull(test.attributes().getVersionId()); final Path copy = new S3CopyFeature(session, acl).copy(test, new Path(container, new AsciiRandomStringService().random(), EnumSet.of(Path.Type.file)), new TransferStatus(), new DisabledConnectionCallback(), new DisabledStreamListener()); @@ -110,7 +110,7 @@ public class S3CopyFeatureTest extends AbstractS3Test { final TransferStatus status = new TransferStatus(); status.setMetadata(Collections.singletonMap("cyberduck", "m")); final S3AccessControlListFeature acl = new S3AccessControlListFeature(virtualhost); - final Path test = new S3TouchFeature(virtualhost, acl).touch(new Path(new AsciiRandomStringService().random(), EnumSet.of(Path.Type.file)), status); + final Path test = new S3TouchFeature(virtualhost, acl).touch(new S3WriteFeature(virtualhost, new S3AccessControlListFeature(virtualhost)), new Path(new AsciiRandomStringService().random(), EnumSet.of(Path.Type.file)), status); final Path copy = new S3CopyFeature(virtualhost, acl).copy(test, new Path(new AsciiRandomStringService().random(), EnumSet.of(Path.Type.file)), new TransferStatus(), new DisabledConnectionCallback(), new DisabledStreamListener()); assertTrue(new S3FindFeature(virtualhost, acl).find(test)); diff --git a/s3/src/test/java/ch/cyberduck/core/s3/S3CredentialsConfiguratorTest.java b/s3/src/test/java/ch/cyberduck/core/s3/S3CredentialsConfiguratorTest.java index 83adc3c272..cd1c56643a 100644 --- a/s3/src/test/java/ch/cyberduck/core/s3/S3CredentialsConfiguratorTest.java +++ b/s3/src/test/java/ch/cyberduck/core/s3/S3CredentialsConfiguratorTest.java @@ -16,12 +16,9 @@ package ch.cyberduck.core.s3; */ import ch.cyberduck.core.Credentials; -import ch.cyberduck.core.DisabledPasswordCallback; import ch.cyberduck.core.Host; import ch.cyberduck.core.LocalFactory; import ch.cyberduck.core.TestProtocol; -import ch.cyberduck.core.ssl.DefaultX509KeyManager; -import ch.cyberduck.core.ssl.DisabledX509TrustManager; import org.apache.commons.lang3.StringUtils; import org.junit.Test; @@ -34,15 +31,15 @@ public class S3CredentialsConfiguratorTest { @Test public void testConfigure() throws Exception { - new S3CredentialsConfigurator(new DisabledX509TrustManager(), new DefaultX509KeyManager(), new DisabledPasswordCallback()) + new S3CredentialsConfigurator() .reload().configure(new Host(new TestProtocol())); } @Test public void readFailureForInvalidAWSCredentialsProfileEntry() throws Exception { final Credentials credentials = new Credentials("test_s3_profile"); - final Credentials verify = new S3CredentialsConfigurator(LocalFactory.get(new File("src/test/resources/invalid/.aws").getAbsolutePath()), - new DisabledX509TrustManager(), new DefaultX509KeyManager(), new DisabledPasswordCallback()) + final Credentials verify = new S3CredentialsConfigurator(LocalFactory.get(new File("src/test/resources/invalid/.aws").getAbsolutePath()) + ) .reload().configure(new Host(new TestProtocol(), StringUtils.EMPTY, credentials)); assertEquals(credentials, verify); } @@ -50,10 +47,10 @@ public class S3CredentialsConfiguratorTest { @Test public void readSuccessForValidAWSCredentialsProfileEntry() throws Exception { final Credentials verify = new S3CredentialsConfigurator(LocalFactory.get(new File("src/test/resources/valid/.aws").getAbsolutePath()) - , new DisabledX509TrustManager(), new DefaultX509KeyManager(), new DisabledPasswordCallback()) + ) .reload().configure(new Host(new TestProtocol(), StringUtils.EMPTY, new Credentials("test_s3_profile"))); - assertEquals("EXAMPLEKEYID", verify.getUsername()); - assertEquals("EXAMPLESECRETKEY", verify.getPassword()); + assertEquals("test_s3_profile", verify.getUsername()); + assertEquals("", verify.getPassword()); assertEquals("EXAMPLEKEYID", verify.getTokens().getAccessKeyId()); assertEquals("EXAMPLESECRETKEY", verify.getTokens().getSecretAccessKey()); assertEquals("EXAMPLETOKEN", verify.getTokens().getSessionToken()); @@ -62,7 +59,7 @@ public class S3CredentialsConfiguratorTest { @Test public void readSSOCachedTemporaryTokens() throws Exception { final Credentials verify = new S3CredentialsConfigurator(LocalFactory.get(new File("src/test/resources/valid/.aws").getAbsolutePath()) - , new DisabledX509TrustManager(), new DefaultX509KeyManager(), new DisabledPasswordCallback()) + ) .reload().configure(new Host(new TestProtocol(), StringUtils.EMPTY, new Credentials("ReadOnlyAccess-189584543480"))); assertEquals("TESTACCESSKEY", verify.getTokens().getAccessKeyId()); assertEquals("TESTSECRETKEY", verify.getTokens().getSecretAccessKey()); diff --git a/s3/src/test/java/ch/cyberduck/core/s3/S3DefaultDeleteFeatureTest.java b/s3/src/test/java/ch/cyberduck/core/s3/S3DefaultDeleteFeatureTest.java index a8f091591c..5ff90a438a 100644 --- a/s3/src/test/java/ch/cyberduck/core/s3/S3DefaultDeleteFeatureTest.java +++ b/s3/src/test/java/ch/cyberduck/core/s3/S3DefaultDeleteFeatureTest.java @@ -44,7 +44,7 @@ public class S3DefaultDeleteFeatureTest extends AbstractS3Test { public void testDeleteFile() throws Exception { final Path container = new Path("test-eu-central-1-cyberduck", EnumSet.of(Path.Type.volume, Path.Type.directory)); final Path test = new Path(container, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)); - new S3TouchFeature(session, new S3AccessControlListFeature(session)).touch(test, new TransferStatus()); + new S3TouchFeature(session, new S3AccessControlListFeature(session)).touch(new S3WriteFeature(session, new S3AccessControlListFeature(session)), test, new TransferStatus()); assertTrue(new S3FindFeature(session, new S3AccessControlListFeature(session)).find(test)); new S3DefaultDeleteFeature(session, new S3AccessControlListFeature(session)).delete(Collections.singletonList(test), new DisabledLoginCallback(), new Delete.DisabledCallback()); assertFalse(new S3FindFeature(session, new S3AccessControlListFeature(session)).find(test)); @@ -55,7 +55,7 @@ public class S3DefaultDeleteFeatureTest extends AbstractS3Test { final Path container = new Path("test-eu-central-1-cyberduck", EnumSet.of(Path.Type.volume, Path.Type.directory)); final Path test = new Path(container, String.format("%s\\%s", new AlphanumericRandomStringService().random(), new AlphanumericRandomStringService().random()), EnumSet.of(Path.Type.file)); - new S3TouchFeature(session, new S3AccessControlListFeature(session)).touch(test, new TransferStatus()); + new S3TouchFeature(session, new S3AccessControlListFeature(session)).touch(new S3WriteFeature(session, new S3AccessControlListFeature(session)), test, new TransferStatus()); assertTrue(new S3FindFeature(session, new S3AccessControlListFeature(session)).find(test)); new S3DefaultDeleteFeature(session, new S3AccessControlListFeature(session)).delete(Collections.singletonList(test), new DisabledLoginCallback(), new Delete.DisabledCallback()); assertFalse(new S3FindFeature(session, new S3AccessControlListFeature(session)).find(test)); @@ -65,7 +65,7 @@ public class S3DefaultDeleteFeatureTest extends AbstractS3Test { public void testDeleteFileVirtualHost() throws Exception { final Path test = new Path(new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)); final S3AccessControlListFeature acl = new S3AccessControlListFeature(virtualhost); - new S3TouchFeature(virtualhost, acl).touch(test, new TransferStatus()); + new S3TouchFeature(virtualhost, acl).touch(new S3WriteFeature(virtualhost, acl), test, new TransferStatus()); assertTrue(new S3FindFeature(virtualhost, acl).find(test)); new S3DefaultDeleteFeature(virtualhost, acl).delete(Arrays.asList(test, test), new DisabledLoginCallback(), new Delete.DisabledCallback()); assertFalse(new S3FindFeature(virtualhost, acl).find(test)); @@ -75,7 +75,7 @@ public class S3DefaultDeleteFeatureTest extends AbstractS3Test { public void testDeletePlaceholder() throws Exception { final Path container = new Path("versioning-test-eu-central-1-cyberduck", EnumSet.of(Path.Type.volume, Path.Type.directory)); final S3AccessControlListFeature acl = new S3AccessControlListFeature(session); - final Path test = new S3DirectoryFeature(session, new S3WriteFeature(session, acl), acl).mkdir(new Path(container, + final Path test = new S3DirectoryFeature(session, acl).mkdir(new S3WriteFeature(session, new S3AccessControlListFeature(session)), new Path(container, String.format("%s %s", new AlphanumericRandomStringService().random(), new AlphanumericRandomStringService().random()), EnumSet.of(Path.Type.directory)), new TransferStatus()); assertTrue(new S3FindFeature(session, acl).find(test)); assertTrue(new DefaultFindFeature(session).find(test)); @@ -90,8 +90,8 @@ public class S3DefaultDeleteFeatureTest extends AbstractS3Test { final Path container = new Path("versioning-test-eu-central-1-cyberduck", EnumSet.of(Path.Type.volume, Path.Type.directory)); final String name = new AlphanumericRandomStringService().random(); final S3AccessControlListFeature acl = new S3AccessControlListFeature(session); - final Path test = new S3DirectoryFeature(session, new S3WriteFeature(session, acl), acl).mkdir( - new Path(container, name, EnumSet.of(Path.Type.directory)), new TransferStatus()); + final Path test = new S3DirectoryFeature(session, acl).mkdir( + new S3WriteFeature(session, new S3AccessControlListFeature(session)), new Path(container, name, EnumSet.of(Path.Type.directory)), new TransferStatus()); assertTrue(new S3FindFeature(session, acl).find(test)); assertTrue(new DefaultFindFeature(session).find(test)); new S3DefaultDeleteFeature(session, new S3AccessControlListFeature(session)).delete(Collections.singletonList(test), new DisabledLoginCallback(), new Delete.DisabledCallback()); diff --git a/s3/src/test/java/ch/cyberduck/core/s3/S3DefaultMultipartServiceTest.java b/s3/src/test/java/ch/cyberduck/core/s3/S3DefaultMultipartServiceTest.java index d479a7a73f..27712ec18b 100644 --- a/s3/src/test/java/ch/cyberduck/core/s3/S3DefaultMultipartServiceTest.java +++ b/s3/src/test/java/ch/cyberduck/core/s3/S3DefaultMultipartServiceTest.java @@ -64,8 +64,8 @@ public class S3DefaultMultipartServiceTest extends AbstractS3Test { public void testFind() throws Exception { final Path container = new Path("test-eu-central-1-cyberduck", EnumSet.of(Path.Type.directory, Path.Type.volume)); final S3AccessControlListFeature acl = new S3AccessControlListFeature(session); - final Path directory = new S3DirectoryFeature(session, new S3WriteFeature(session, acl), acl).mkdir( - new Path(container, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory)), new TransferStatus()); + final Path directory = new S3DirectoryFeature(session, acl).mkdir( + new S3WriteFeature(session, acl), new Path(container, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory)), new TransferStatus()); final S3DefaultMultipartService service = new S3DefaultMultipartService(session); assertTrue(service.find(directory).isEmpty()); final Path file = new Path(directory, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)); @@ -75,7 +75,8 @@ public class S3DefaultMultipartServiceTest extends AbstractS3Test { assertFalse(service.find(directory).isEmpty()); assertTrue(service.find(new Path(container, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory))).isEmpty()); assertEquals(first.getUploadId(), service.find(directory).iterator().next().getUploadId()); - assertFalse(new S3FindFeature(session, acl).find(file)); + // Depends on s3.upload.multipart.lookup=true + assertTrue(new S3FindFeature(session, acl).find(file)); final Path upload = new S3ListService(session, acl).list(directory, new DisabledListProgressListener()).find(new SimplePathPredicate(file)); assertNotNull(upload); assertTrue(new S3FindFeature(session, acl).find(upload)); diff --git a/s3/src/test/java/ch/cyberduck/core/s3/S3DirectoryFeatureTest.java b/s3/src/test/java/ch/cyberduck/core/s3/S3DirectoryFeatureTest.java index 0e0d75349d..dcd613fed7 100644 --- a/s3/src/test/java/ch/cyberduck/core/s3/S3DirectoryFeatureTest.java +++ b/s3/src/test/java/ch/cyberduck/core/s3/S3DirectoryFeatureTest.java @@ -49,7 +49,7 @@ public class S3DirectoryFeatureTest extends AbstractS3Test { @Ignore public void testCreateBucket() throws Exception { final S3AccessControlListFeature acl = new S3AccessControlListFeature(session); - final S3DirectoryFeature feature = new S3DirectoryFeature(session, new S3WriteFeature(session, acl), acl); + final S3DirectoryFeature feature = new S3DirectoryFeature(session, acl); for(Location.Name region : session.getHost().getProtocol().getRegions()) { switch(region.getIdentifier()) { case "me-south-1": @@ -60,7 +60,7 @@ public class S3DirectoryFeatureTest extends AbstractS3Test { final Path test = new Path(new DefaultHomeFinderService(session).find(), new AsciiRandomStringService(30).random(), EnumSet.of(Path.Type.directory, Path.Type.volume)); assertTrue(feature.isSupported(test.getParent(), test.getName())); test.attributes().setRegion(region.getIdentifier()); - feature.mkdir(test, new TransferStatus().setRegion(region.getIdentifier())); + feature.mkdir(new S3WriteFeature(session, new S3AccessControlListFeature(session)), test, new TransferStatus().setRegion(region.getIdentifier())); assertTrue(new S3FindFeature(session, acl).find(test)); assertEquals(region.getIdentifier(), new S3LocationFeature(session, session.getClient().getRegionEndpointCache()).getLocation(test).getIdentifier()); new S3DefaultDeleteFeature(session, acl).delete(Collections.singletonList(test), new DisabledLoginCallback(), new Delete.DisabledCallback()); @@ -88,12 +88,12 @@ public class S3DirectoryFeatureTest extends AbstractS3Test { assertNotNull(session.open(new DisabledProxyFinder(), new DisabledHostKeyCallback(), new DisabledLoginCallback(), new DisabledCancelCallback())); session.login(new DisabledLoginCallback(), new DisabledCancelCallback()); final S3AccessControlListFeature acl = new S3AccessControlListFeature(session); - final S3DirectoryFeature feature = new S3DirectoryFeature(session, new S3WriteFeature(session, acl), acl); + final S3DirectoryFeature feature = new S3DirectoryFeature(session, acl); for(Location.Name region : Collections.singletonList(new S3LocationFeature.S3Region("us-east-1"))) { final Path test = new Path(new DefaultHomeFinderService(session).find(), new AsciiRandomStringService(30).random(), EnumSet.of(Path.Type.directory, Path.Type.volume)); assertTrue(feature.isSupported(test.getParent(), test.getName())); test.attributes().setRegion(region.getIdentifier()); - feature.mkdir(test, new TransferStatus().setRegion(region.getIdentifier())); + feature.mkdir(new S3WriteFeature(session, new S3AccessControlListFeature(session)), test, new TransferStatus().setRegion(region.getIdentifier())); assertTrue(new S3FindFeature(session, acl).find(test)); assertEquals(region.getIdentifier(), new S3LocationFeature(session, session.getClient().getRegionEndpointCache()).getLocation(test).getIdentifier()); new S3DefaultDeleteFeature(session, acl).delete(Collections.singletonList(test), new DisabledLoginCallback(), new Delete.DisabledCallback()); @@ -121,12 +121,12 @@ public class S3DirectoryFeatureTest extends AbstractS3Test { assertNotNull(session.open(new DisabledProxyFinder(), new DisabledHostKeyCallback(), new DisabledLoginCallback(), new DisabledCancelCallback())); session.login(new DisabledLoginCallback(), new DisabledCancelCallback()); final S3AccessControlListFeature acl = new S3AccessControlListFeature(session); - final S3DirectoryFeature feature = new S3DirectoryFeature(session, new S3WriteFeature(session, acl), acl); + final S3DirectoryFeature feature = new S3DirectoryFeature(session, acl); for(Location.Name region : Collections.singletonList(new S3LocationFeature.S3Region("us-east-1"))) { final Path test = new Path(new DefaultHomeFinderService(session).find(), new AsciiRandomStringService(30).random(), EnumSet.of(Path.Type.directory, Path.Type.volume)); assertTrue(feature.isSupported(test.getParent(), test.getName())); test.attributes().setRegion(region.getIdentifier()); - feature.mkdir(test, new TransferStatus().setRegion(region.getIdentifier())); + feature.mkdir(new S3WriteFeature(session, new S3AccessControlListFeature(session)), test, new TransferStatus().setRegion(region.getIdentifier())); assertTrue(new S3FindFeature(session, acl).find(test)); assertEquals(region.getIdentifier(), new S3LocationFeature(session, session.getClient().getRegionEndpointCache()).getLocation(test).getIdentifier()); new S3DefaultDeleteFeature(session, acl).delete(Collections.singletonList(test), new DisabledLoginCallback(), new Delete.DisabledCallback()); @@ -138,11 +138,11 @@ public class S3DirectoryFeatureTest extends AbstractS3Test { public void testCreateBucketInvalidName() throws Exception { final S3AccessControlListFeature acl = new S3AccessControlListFeature(session); final Path test = new Path(new DefaultHomeFinderService(session).find(), "untitled folder", EnumSet.of(Path.Type.directory, Path.Type.volume)); - assertFalse(new S3DirectoryFeature(session, new S3WriteFeature(session, acl), acl).isSupported(test.getParent(), test.getName())); - assertTrue(new S3DirectoryFeature(virtualhost, new S3WriteFeature(session, acl), acl).isSupported(test.getParent(), test.getName())); + assertFalse(new S3DirectoryFeature(session, acl).isSupported(test.getParent(), test.getName())); + assertTrue(new S3DirectoryFeature(virtualhost, acl).isSupported(test.getParent(), test.getName())); final S3LocationFeature.S3Region region = new S3LocationFeature.S3Region("eu-west-2"); test.attributes().setRegion(region.getIdentifier()); - new S3DirectoryFeature(session, new S3WriteFeature(session, acl), acl).mkdir(test, new TransferStatus().setRegion(region.getIdentifier())); + new S3DirectoryFeature(session, acl).mkdir(new S3WriteFeature(session, new S3AccessControlListFeature(session)), test, new TransferStatus().setRegion(region.getIdentifier())); assertTrue(new S3FindFeature(session, acl).find(test)); new S3DefaultDeleteFeature(session, acl).delete(Collections.singletonList(test), new DisabledLoginCallback(), new Delete.DisabledCallback()); } @@ -164,7 +164,7 @@ public class S3DirectoryFeatureTest extends AbstractS3Test { }); final Path container = new Path("test-eu-central-1-cyberduck", EnumSet.of(Path.Type.directory, Path.Type.volume)); final S3AccessControlListFeature acl = new S3AccessControlListFeature(session); - final Path test = new S3DirectoryFeature(session, new S3WriteFeature(session, acl), acl).mkdir(new Path(container, name, EnumSet.of(Path.Type.directory)), new TransferStatus()); + final Path test = new S3DirectoryFeature(session, acl).mkdir(new S3WriteFeature(session, new S3AccessControlListFeature(session)), new Path(container, name, EnumSet.of(Path.Type.directory)), new TransferStatus()); assertTrue(test.getType().contains(Path.Type.placeholder)); assertTrue(b.get()); assertTrue(new S3FindFeature(session, acl).find(test)); @@ -195,9 +195,9 @@ public class S3DirectoryFeatureTest extends AbstractS3Test { login.check(session, new DisabledCancelCallback()); final String name = String.format("%s %s", new AlphanumericRandomStringService().random(), new AlphanumericRandomStringService().random()); final S3AccessControlListFeature acl = new S3AccessControlListFeature(session); - final Path bucket = new S3DirectoryFeature(session, new S3WriteFeature(session, acl), acl).mkdir( - new Path(new DefaultHomeFinderService(session).find(), new AsciiRandomStringService().random(), EnumSet.of(Path.Type.directory, Path.Type.volume)), new TransferStatus()); - final Path test = new S3DirectoryFeature(session, new S3WriteFeature(session, acl), acl).mkdir(new Path(bucket, name, EnumSet.of(Path.Type.directory)), new TransferStatus()); + final Path bucket = new S3DirectoryFeature(session, acl).mkdir( + new S3WriteFeature(session, new S3AccessControlListFeature(session)), new Path(new DefaultHomeFinderService(session).find(), new AsciiRandomStringService().random(), EnumSet.of(Path.Type.directory, Path.Type.volume)), new TransferStatus()); + final Path test = new S3DirectoryFeature(session, acl).mkdir(new S3WriteFeature(session, new S3AccessControlListFeature(session)), new Path(bucket, name, EnumSet.of(Path.Type.directory)), new TransferStatus()); assertTrue(test.getType().contains(Path.Type.placeholder)); assertTrue(new S3FindFeature(session, acl).find(test)); assertNotNull(new S3AttributesFinderFeature(session, acl).find(test)); @@ -212,7 +212,7 @@ public class S3DirectoryFeatureTest extends AbstractS3Test { public void testCreatePlaceholderVersioningDelete() throws Exception { final Path bucket = new Path("versioning-test-eu-central-1-cyberduck", EnumSet.of(Path.Type.directory, Path.Type.volume)); final S3AccessControlListFeature acl = new S3AccessControlListFeature(session); - final Path test = new S3DirectoryFeature(session, new S3WriteFeature(session, acl), acl).mkdir(new Path(bucket, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory)), new TransferStatus()); + final Path test = new S3DirectoryFeature(session, acl).mkdir(new S3WriteFeature(session, new S3AccessControlListFeature(session)), new Path(bucket, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory)), new TransferStatus()); assertTrue(test.getType().contains(Path.Type.placeholder)); assertTrue(new S3FindFeature(session, acl).find(test)); assertTrue(new S3VersionedObjectListService(session, acl).list(bucket, new DisabledListProgressListener()).contains(test)); @@ -226,8 +226,8 @@ public class S3DirectoryFeatureTest extends AbstractS3Test { public void testCreatePlaceholderVersioningDeleteWithMarker() throws Exception { final Path bucket = new Path("versioning-test-eu-central-1-cyberduck", EnumSet.of(Path.Type.directory, Path.Type.volume)); final S3AccessControlListFeature acl = new S3AccessControlListFeature(session); - final Path directory = new S3DirectoryFeature(session, new S3WriteFeature(session, acl), acl).mkdir(new Path(bucket, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory)), new TransferStatus()); - final Path test = new S3DirectoryFeature(session, new S3WriteFeature(session, acl), acl).mkdir(new Path(directory, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory)), new TransferStatus()); + final Path directory = new S3DirectoryFeature(session, acl).mkdir(new S3WriteFeature(session, new S3AccessControlListFeature(session)), new Path(bucket, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory)), new TransferStatus()); + final Path test = new S3DirectoryFeature(session, acl).mkdir(new S3WriteFeature(session, new S3AccessControlListFeature(session)), new Path(directory, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory)), new TransferStatus()); assertTrue(test.getType().contains(Path.Type.placeholder)); assertTrue(new S3FindFeature(session, acl).find(test)); assertTrue(new S3VersionedObjectListService(session, acl).list(directory, new DisabledListProgressListener()).contains(test)); @@ -243,9 +243,9 @@ public class S3DirectoryFeatureTest extends AbstractS3Test { public void testDirectoryDeleteWithVersioning() throws Exception { final Path bucket = new Path("versioning-test-eu-central-1-cyberduck", EnumSet.of(Path.Type.directory, Path.Type.volume)); final S3AccessControlListFeature acl = new S3AccessControlListFeature(session); - final Path parent = new S3DirectoryFeature(session, new S3WriteFeature(session, acl), acl).mkdir(new Path(bucket, + final Path parent = new S3DirectoryFeature(session, acl).mkdir(new S3WriteFeature(session, new S3AccessControlListFeature(session)), new Path(bucket, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory)), new TransferStatus()); - final Path test = new S3DirectoryFeature(session, new S3WriteFeature(session, acl), acl).mkdir(new Path(parent, + final Path test = new S3DirectoryFeature(session, acl).mkdir(new S3WriteFeature(session, new S3AccessControlListFeature(session)), new Path(parent, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory)), new TransferStatus()); assertNotNull(test.attributes().getVersionId()); assertTrue(test.isPlaceholder()); @@ -268,7 +268,7 @@ public class S3DirectoryFeatureTest extends AbstractS3Test { final String name = String.format("%s=", new AlphanumericRandomStringService().random()); final Path container = new Path("test-eu-central-1-cyberduck", EnumSet.of(Path.Type.directory, Path.Type.volume)); final S3AccessControlListFeature acl = new S3AccessControlListFeature(session); - final Path test = new S3DirectoryFeature(session, new S3WriteFeature(session, acl), acl).mkdir(new Path(container, name, EnumSet.of(Path.Type.directory)), new TransferStatus()); + final Path test = new S3DirectoryFeature(session, acl).mkdir(new S3WriteFeature(session, new S3AccessControlListFeature(session)), new Path(container, name, EnumSet.of(Path.Type.directory)), new TransferStatus()); assertTrue(new S3FindFeature(session, acl).find(test)); assertTrue(new DefaultFindFeature(session).find(test)); assertTrue(new S3ObjectListService(session, acl).list(test, new DisabledListProgressListener()).isEmpty()); @@ -278,19 +278,19 @@ public class S3DirectoryFeatureTest extends AbstractS3Test { @Test public void testCreatePlaceholderVirtualHost() throws Exception { final S3AccessControlListFeature acl = new S3AccessControlListFeature(virtualhost); - final Path test = new S3DirectoryFeature(virtualhost, new S3WriteFeature(virtualhost, acl), acl).mkdir( - new Path(new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory, Path.Type.volume)), new TransferStatus()); + final Path test = new S3DirectoryFeature(virtualhost, acl).mkdir( + new S3WriteFeature(virtualhost, acl), new Path(new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory, Path.Type.volume)), new TransferStatus()); assertTrue(new S3FindFeature(virtualhost, acl).find(test)); assertTrue(new DefaultFindFeature(virtualhost).find(test)); assertTrue(new S3ObjectListService(virtualhost, acl).list(test, new DisabledListProgressListener()).isEmpty()); - new S3DefaultDeleteFeature(virtualhost, new S3AccessControlListFeature(virtualhost)).delete(Collections.singletonList(test), new DisabledLoginCallback(), new Delete.DisabledCallback()); + new S3DefaultDeleteFeature(virtualhost, acl).delete(Collections.singletonList(test), new DisabledLoginCallback(), new Delete.DisabledCallback()); } @Test public void testBackslash() throws Exception { final Path container = new Path("test-eu-central-1-cyberduck", EnumSet.of(Path.Type.volume, Path.Type.directory)); - final Path test = new S3DirectoryFeature(session, new S3WriteFeature(session, new S3AccessControlListFeature(session)), new S3AccessControlListFeature(session)).mkdir( - new Path(container, String.format("%s\\%s", new AlphanumericRandomStringService().random(), + final Path test = new S3DirectoryFeature(session, new S3AccessControlListFeature(session)).mkdir( + new S3WriteFeature(session, new S3AccessControlListFeature(session)), new Path(container, String.format("%s\\%s", new AlphanumericRandomStringService().random(), new AlphanumericRandomStringService().random()), EnumSet.of(Path.Type.directory)), new TransferStatus()); assertTrue(new S3FindFeature(session, new S3AccessControlListFeature(session)).find(test)); new S3DefaultDeleteFeature(session, new S3AccessControlListFeature(session)).delete(Collections.singletonList(test), new DisabledLoginCallback(), new Delete.DisabledCallback()); @@ -301,13 +301,13 @@ public class S3DirectoryFeatureTest extends AbstractS3Test { public void testTouchUriEncoding() throws Exception { final Path container = new Path("test-eu-central-1-cyberduck", EnumSet.of(Path.Type.volume, Path.Type.directory)); final S3AccessControlListFeature acl = new S3AccessControlListFeature(session); - final Path directory = new S3DirectoryFeature(session, new S3WriteFeature(session, new S3AccessControlListFeature(session)), new S3AccessControlListFeature(session)).mkdir( - new Path(container, String.format("%s-+*~@([", new AsciiRandomStringService().random()), EnumSet.of(Path.Type.file)), new TransferStatus()); + final Path directory = new S3DirectoryFeature(session, new S3AccessControlListFeature(session)).mkdir( + new S3WriteFeature(session, new S3AccessControlListFeature(session)), new Path(container, String.format("%s-+*~@([", new AsciiRandomStringService().random()), EnumSet.of(Path.Type.file)), new TransferStatus()); assertNull(directory.attributes().getVersionId()); assertTrue(new S3FindFeature(session, acl).find(directory)); assertEquals(directory.attributes(), new S3AttributesFinderFeature(session, acl).find(directory)); final Path test = new S3TouchFeature(session, acl).touch( - new Path(directory, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)), new TransferStatus()); + new S3WriteFeature(session, new S3AccessControlListFeature(session)), new Path(directory, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)), new TransferStatus()); assertTrue(new S3FindFeature(session, acl).find(test)); new S3DefaultDeleteFeature(session, acl).delete(Arrays.asList(test, directory), new DisabledLoginCallback(), new Delete.DisabledCallback()); assertFalse(new S3FindFeature(session, acl).find(directory)); diff --git a/s3/src/test/java/ch/cyberduck/core/s3/S3EncryptionFeatureTest.java b/s3/src/test/java/ch/cyberduck/core/s3/S3EncryptionFeatureTest.java index 64571075a1..cfee15654e 100644 --- a/s3/src/test/java/ch/cyberduck/core/s3/S3EncryptionFeatureTest.java +++ b/s3/src/test/java/ch/cyberduck/core/s3/S3EncryptionFeatureTest.java @@ -48,7 +48,7 @@ public class S3EncryptionFeatureTest extends AbstractS3Test { final Path container = new Path("versioning-test-eu-central-1-cyberduck", EnumSet.of(Path.Type.volume, Path.Type.directory)); final Path test = new Path(container, UUID.randomUUID().toString(), EnumSet.of(Path.Type.file)); final S3AccessControlListFeature acl = new S3AccessControlListFeature(session); - new S3TouchFeature(session, acl).touch(test, new TransferStatus()); + new S3TouchFeature(session, acl).touch(new S3WriteFeature(session, new S3AccessControlListFeature(session)), test, new TransferStatus()); final S3EncryptionFeature feature = new S3EncryptionFeature(session, acl); feature.setEncryption(test, S3EncryptionFeature.SSE_AES256); final Encryption.Algorithm value = feature.getEncryption(test); @@ -61,8 +61,8 @@ public class S3EncryptionFeatureTest extends AbstractS3Test { public void testSetEncryptionAES256Placeholder() throws Exception { final Path container = new Path("versioning-test-eu-central-1-cyberduck", EnumSet.of(Path.Type.volume, Path.Type.directory)); final S3AccessControlListFeature acl = new S3AccessControlListFeature(session); - final Path test = new S3DirectoryFeature(session, new S3WriteFeature(session, acl), acl).mkdir( - new Path(container, UUID.randomUUID().toString(), EnumSet.of(Path.Type.directory)), new TransferStatus()); + final Path test = new S3DirectoryFeature(session, acl).mkdir( + new S3WriteFeature(session, new S3AccessControlListFeature(session)), new Path(container, UUID.randomUUID().toString(), EnumSet.of(Path.Type.directory)), new TransferStatus()); final S3EncryptionFeature feature = new S3EncryptionFeature(session, acl); feature.setEncryption(test, S3EncryptionFeature.SSE_AES256); final Encryption.Algorithm value = feature.getEncryption(test); diff --git a/s3/src/test/java/ch/cyberduck/core/s3/S3FindFeatureTest.java b/s3/src/test/java/ch/cyberduck/core/s3/S3FindFeatureTest.java index 6baeb5719e..18d70c1a04 100644 --- a/s3/src/test/java/ch/cyberduck/core/s3/S3FindFeatureTest.java +++ b/s3/src/test/java/ch/cyberduck/core/s3/S3FindFeatureTest.java @@ -63,7 +63,7 @@ public class S3FindFeatureTest extends AbstractS3Test { assertTrue(new S3FindFeature(session, acl).find(container)); final String prefix = new AlphanumericRandomStringService().random(); final Path test = new S3TouchFeature(session, acl).touch( - new Path(new Path(container, prefix, EnumSet.of(Path.Type.directory)), + new S3WriteFeature(session, new S3AccessControlListFeature(session)), new Path(new Path(container, prefix, EnumSet.of(Path.Type.directory)), new AsciiRandomStringService().random(), EnumSet.of(Path.Type.file)), new TransferStatus()); assertTrue(new S3FindFeature(session, acl).find(test)); assertFalse(new S3FindFeature(session, acl).find(new Path(test.getAbsolute(), EnumSet.of(Path.Type.directory)))); @@ -88,7 +88,7 @@ public class S3FindFeatureTest extends AbstractS3Test { assertTrue(new S3FindFeature(virtualhost, acl).find(container)); final String prefix = new AlphanumericRandomStringService().random(); final Path test = new S3TouchFeature(virtualhost, acl).touch( - new Path(new Path(container, prefix, EnumSet.of(Path.Type.directory)), + new S3WriteFeature(virtualhost, new S3AccessControlListFeature(virtualhost)), new Path(new Path(container, prefix, EnumSet.of(Path.Type.directory)), new AsciiRandomStringService().random(), EnumSet.of(Path.Type.file)), new TransferStatus()); assertTrue(new S3FindFeature(virtualhost, acl).find(test)); assertFalse(new S3FindFeature(virtualhost, acl).find(new Path(test.getAbsolute(), EnumSet.of(Path.Type.directory)))); diff --git a/s3/src/test/java/ch/cyberduck/core/s3/S3ListServiceTest.java b/s3/src/test/java/ch/cyberduck/core/s3/S3ListServiceTest.java index d8b524fdb5..41da379501 100644 --- a/s3/src/test/java/ch/cyberduck/core/s3/S3ListServiceTest.java +++ b/s3/src/test/java/ch/cyberduck/core/s3/S3ListServiceTest.java @@ -51,8 +51,8 @@ public class S3ListServiceTest extends AbstractS3Test { public void testListMultipartUploadDot() throws Exception { final Path bucket = new Path("test-eu-central-1-cyberduck", EnumSet.of(Path.Type.directory, Path.Type.volume)); final S3AccessControlListFeature acl = new S3AccessControlListFeature(session); - final Path placeholder = new S3DirectoryFeature(session, new S3WriteFeature(session, acl), acl).mkdir( - new Path(bucket, new AsciiRandomStringService().random(), EnumSet.of(Path.Type.directory, Path.Type.placeholder)), new TransferStatus()); + final Path placeholder = new S3DirectoryFeature(session, acl).mkdir( + new S3WriteFeature(session, new S3AccessControlListFeature(session)), new Path(bucket, new AsciiRandomStringService().random(), EnumSet.of(Path.Type.directory, Path.Type.placeholder)), new TransferStatus()); final MultipartUpload multipart = session.getClient().multipartStartUpload(bucket.getName(), String.format("%s/.", new S3PathContainerService(session.getHost()).getKey(placeholder)), Collections.emptyMap()); assertNotNull(new S3ListService(session, acl).list(placeholder, new DisabledListProgressListener()).find(path -> path.getName().equals("."))); new S3DefaultMultipartService(session).delete(multipart); diff --git a/s3/src/test/java/ch/cyberduck/core/s3/S3LocationFeatureTest.java b/s3/src/test/java/ch/cyberduck/core/s3/S3LocationFeatureTest.java index 1c07264a8b..4f5d725209 100644 --- a/s3/src/test/java/ch/cyberduck/core/s3/S3LocationFeatureTest.java +++ b/s3/src/test/java/ch/cyberduck/core/s3/S3LocationFeatureTest.java @@ -152,7 +152,7 @@ public class S3LocationFeatureTest extends AbstractS3Test { public void testEmptyThirdPartyProvider() { final Host host = new Host(new S3Protocol(), "mys3"); final S3Session session = new S3Session(host); - assertTrue(new S3LocationFeature(session).getLocations().isEmpty()); + assertTrue(new S3LocationFeature(session).getLocations(Home.root()).isEmpty()); } @Test @@ -162,12 +162,12 @@ public class S3LocationFeatureTest extends AbstractS3Test { this.getClass().getResourceAsStream("/Wasabi (us-central-1).cyberduckprofile")); final S3Session session = new S3Session(new Host(profile, profile.getDefaultHostname())); final S3LocationFeature feature = new S3LocationFeature(session); - assertFalse(feature.getLocations().isEmpty()); - assertTrue(feature.getLocations().contains(new Location.Name("us-central-1"))); + assertFalse(feature.getLocations(Home.root()).isEmpty()); + assertTrue(feature.getLocations(Home.root()).contains(new Location.Name("us-central-1"))); } @Test public void testVirtualhost() { - assertTrue(new S3LocationFeature(virtualhost, virtualhost.getClient().getRegionEndpointCache()).getLocations().isEmpty()); + assertTrue(new S3LocationFeature(virtualhost, virtualhost.getClient().getRegionEndpointCache()).getLocations(Home.root()).isEmpty()); } } diff --git a/s3/src/test/java/ch/cyberduck/core/s3/S3MetadataFeatureTest.java b/s3/src/test/java/ch/cyberduck/core/s3/S3MetadataFeatureTest.java index 4da44893bc..8fe5bc478b 100644 --- a/s3/src/test/java/ch/cyberduck/core/s3/S3MetadataFeatureTest.java +++ b/s3/src/test/java/ch/cyberduck/core/s3/S3MetadataFeatureTest.java @@ -52,7 +52,7 @@ public class S3MetadataFeatureTest extends AbstractS3Test { final Path container = new Path("versioning-test-eu-central-1-cyberduck", EnumSet.of(Path.Type.volume, Path.Type.directory)); final Path test = new Path(container, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)); final S3AccessControlListFeature acl = new S3AccessControlListFeature(session); - new S3TouchFeature(session, acl).touch(test, new TransferStatus() + new S3TouchFeature(session, acl).touch(new S3WriteFeature(session, new S3AccessControlListFeature(session)), test, new TransferStatus() .setMetadata(Collections.singletonMap("app", "cyberduck")) .setMime("text/plain")); final S3MetadataFeature feature = new S3MetadataFeature(session, acl); @@ -75,7 +75,7 @@ public class S3MetadataFeatureTest extends AbstractS3Test { final Path container = new Path("versioning-test-eu-central-1-cyberduck", EnumSet.of(Path.Type.volume, Path.Type.directory)); final S3AccessControlListFeature acl = new S3AccessControlListFeature(session); final Path test = new S3TouchFeature(session, acl).touch( - new Path(container, UUID.randomUUID().toString(), EnumSet.of(Path.Type.file)), new TransferStatus()); + new S3WriteFeature(session, new S3AccessControlListFeature(session)), new Path(container, UUID.randomUUID().toString(), EnumSet.of(Path.Type.file)), new TransferStatus()); final S3MetadataFeature feature = new S3MetadataFeature(session, acl); final Map metadata = feature.getMetadata(test); @@ -106,7 +106,7 @@ public class S3MetadataFeatureTest extends AbstractS3Test { final Path container = new Path("versioning-test-eu-central-1-cyberduck", EnumSet.of(Path.Type.volume, Path.Type.directory)); final Path test = new Path(container, UUID.randomUUID().toString(), EnumSet.of(Path.Type.file)); final S3AccessControlListFeature acls = new S3AccessControlListFeature(session); - new S3TouchFeature(session, acls).touch(test, new TransferStatus()); + new S3TouchFeature(session, acls).touch(new S3WriteFeature(session, new S3AccessControlListFeature(session)), test, new TransferStatus()); final S3MetadataFeature feature = new S3MetadataFeature(session, acls); final Map reference = feature.getMetadata(test); @@ -150,7 +150,7 @@ public class S3MetadataFeatureTest extends AbstractS3Test { final Path container = new Path("versioning-test-eu-central-1-cyberduck", EnumSet.of(Path.Type.volume, Path.Type.directory)); final Path test = new Path(container, UUID.randomUUID().toString(), EnumSet.of(Path.Type.file)); final S3AccessControlListFeature acl = new S3AccessControlListFeature(session); - new S3TouchFeature(session, acl).touch(test, new TransferStatus()); + new S3TouchFeature(session, acl).touch(new S3WriteFeature(session, new S3AccessControlListFeature(session)), test, new TransferStatus()); final S3MetadataFeature feature = new S3MetadataFeature(session, acl); assertTrue(feature.getMetadata(test).containsKey("Content-Type")); feature.setMetadata(test, Collections.singletonMap("Content-type", "text/plain")); diff --git a/s3/src/test/java/ch/cyberduck/core/s3/S3MoveFeatureTest.java b/s3/src/test/java/ch/cyberduck/core/s3/S3MoveFeatureTest.java index 58a4800ceb..71db9a2429 100644 --- a/s3/src/test/java/ch/cyberduck/core/s3/S3MoveFeatureTest.java +++ b/s3/src/test/java/ch/cyberduck/core/s3/S3MoveFeatureTest.java @@ -51,7 +51,7 @@ public class S3MoveFeatureTest extends AbstractS3Test { public void testMove() throws Exception { final Path container = new Path("test-eu-central-1-cyberduck", EnumSet.of(Path.Type.directory, Path.Type.volume)); final S3AccessControlListFeature acl = new S3AccessControlListFeature(session); - final Path test = new S3TouchFeature(session, acl).touch(new Path(container, new AsciiRandomStringService().random(), EnumSet.of(Path.Type.file)), new TransferStatus()); + final Path test = new S3TouchFeature(session, acl).touch(new S3WriteFeature(session, new S3AccessControlListFeature(session)), new Path(container, new AsciiRandomStringService().random(), EnumSet.of(Path.Type.file)), new TransferStatus()); assertNull(test.attributes().getVersionId()); assertTrue(new S3FindFeature(session, acl).find(test)); final Path renamed = new S3MoveFeature(session, acl).move(test, new Path(container, new AsciiRandomStringService().random(), EnumSet.of(Path.Type.file)), @@ -59,6 +59,8 @@ public class S3MoveFeatureTest extends AbstractS3Test { assertFalse(new S3FindFeature(session, acl).find(test)); assertTrue(new S3FindFeature(session, acl).find(renamed)); final PathAttributes targetAttr = new S3AttributesFinderFeature(session, acl).find(renamed); + assertEquals(renamed.attributes().getETag(), targetAttr.getETag()); + assertEquals(renamed.attributes().getChecksum(), targetAttr.getChecksum()); assertEquals(Comparison.equal, session.getHost().getProtocol().getFeature(ComparisonService.class).compare(Path.Type.file, test.attributes(), targetAttr)); assertEquals(Comparison.equal, session.getHost().getProtocol().getFeature(ComparisonService.class).compare(Path.Type.file, renamed.attributes(), targetAttr)); new S3DefaultDeleteFeature(session, acl).delete(Collections.singletonList(renamed), new DisabledLoginCallback(), new Delete.DisabledCallback()); @@ -67,7 +69,7 @@ public class S3MoveFeatureTest extends AbstractS3Test { @Test public void testMoveVirtualHost() throws Exception { final S3AccessControlListFeature acl = new S3AccessControlListFeature(virtualhost); - final Path test = new S3TouchFeature(virtualhost, acl).touch(new Path(new AsciiRandomStringService().random(), EnumSet.of(Path.Type.file)), new TransferStatus()); + final Path test = new S3TouchFeature(virtualhost, acl).touch(new S3WriteFeature(virtualhost, new S3AccessControlListFeature(virtualhost)), new Path(new AsciiRandomStringService().random(), EnumSet.of(Path.Type.file)), new TransferStatus()); assertTrue(new S3FindFeature(virtualhost, acl).find(test)); final Path renamed = new Path(new AsciiRandomStringService().random(), EnumSet.of(Path.Type.file)); new S3MoveFeature(virtualhost, acl).move(test, renamed, new TransferStatus(), new Delete.DisabledCallback(), new DisabledConnectionCallback()); @@ -79,13 +81,13 @@ public class S3MoveFeatureTest extends AbstractS3Test { @Test public void testMovePlaceholderVirtualHost() throws Exception { final S3AccessControlListFeature acl = new S3AccessControlListFeature(virtualhost); - final Path test = new S3DirectoryFeature(virtualhost, new S3WriteFeature(virtualhost, acl), acl).mkdir(new Path(new AsciiRandomStringService().random(), EnumSet.of(Path.Type.directory)), new TransferStatus()); + final Path test = new S3DirectoryFeature(virtualhost, acl).mkdir(new S3WriteFeature(virtualhost, acl), new Path(new AsciiRandomStringService().random(), EnumSet.of(Path.Type.directory)), new TransferStatus()); assertTrue(new S3FindFeature(virtualhost, acl).find(test)); final Path renamed = new Path(new AsciiRandomStringService().random(), EnumSet.of(Path.Type.directory)); new S3MoveFeature(virtualhost, acl).move(test, renamed, new TransferStatus(), new Delete.DisabledCallback(), new DisabledConnectionCallback()); assertFalse(new DefaultFindFeature(virtualhost).find(test)); assertTrue(new S3FindFeature(virtualhost, acl).find(renamed)); - new S3DefaultDeleteFeature(virtualhost, new S3AccessControlListFeature(virtualhost)).delete(Collections.singletonList(renamed), new DisabledLoginCallback(), new Delete.DisabledCallback()); + new S3DefaultDeleteFeature(virtualhost, acl).delete(Collections.singletonList(renamed), new DisabledLoginCallback(), new Delete.DisabledCallback()); } @Test @@ -93,7 +95,7 @@ public class S3MoveFeatureTest extends AbstractS3Test { final Path container = new Path("versioning-test-eu-central-1-cyberduck", EnumSet.of(Path.Type.directory, Path.Type.volume)); Path test = new Path(container, new AsciiRandomStringService().random(), EnumSet.of(Path.Type.file)); final S3AccessControlListFeature acl = new S3AccessControlListFeature(session); - assertNotNull(new S3TouchFeature(session, acl).touch(test, new TransferStatus()).attributes().getVersionId()); + assertNotNull(new S3TouchFeature(session, acl).touch(new S3WriteFeature(session, acl), test, new TransferStatus()).attributes().getVersionId()); assertTrue(new S3FindFeature(session, acl).find(test)); // Write some data to add a new version final S3WriteFeature feature = new S3WriteFeature(session, acl); @@ -111,8 +113,8 @@ public class S3MoveFeatureTest extends AbstractS3Test { assertTrue(new S3FindFeature(session, acl).find(test)); // Ensure that the latest version of the source file is a delete marker test = new S3ListService(session, acl).list(container, new DisabledListProgressListener()).find(new SimplePathPredicate(test)); - assertTrue(test.attributes().isDuplicate()); - assertTrue(new S3AttributesFinderFeature(session, acl).find(test).isDuplicate()); + assertTrue(test.attributes().isTrashed()); + assertTrue(new S3AttributesFinderFeature(session, acl).find(test).isTrashed()); assertTrue(new S3FindFeature(session, acl).find(renamed)); assertEquals(content.length, new S3AttributesFinderFeature(session, acl).find(renamed).getSize()); new S3DefaultDeleteFeature(session, acl).delete(Collections.singletonList(renamed), new DisabledLoginCallback(), new Delete.DisabledCallback()); @@ -124,7 +126,7 @@ public class S3MoveFeatureTest extends AbstractS3Test { final Path placeholder = new Path(container, new AsciiRandomStringService().random(), EnumSet.of(Path.Type.directory)); final Path test = new Path(placeholder, new AsciiRandomStringService().random(), EnumSet.of(Path.Type.file)); final S3AccessControlListFeature acl = new S3AccessControlListFeature(session); - new S3TouchFeature(session, acl).touch(test, new TransferStatus()); + new S3TouchFeature(session, acl).touch(new S3WriteFeature(session, new S3AccessControlListFeature(session)), test, new TransferStatus()); final Path renamed = new Path(placeholder, new AsciiRandomStringService().random(), EnumSet.of(Path.Type.file)); new S3MoveFeature(session, acl).move(test, renamed, new TransferStatus(), new Delete.DisabledCallback(), new DisabledConnectionCallback()); assertFalse(new S3FindFeature(session, acl).find(test)); @@ -148,7 +150,7 @@ public class S3MoveFeatureTest extends AbstractS3Test { final S3TouchFeature touch = new S3TouchFeature(session, acl); final TransferStatus status = new TransferStatus(); status.setEncryption(S3EncryptionFeature.SSE_AES256); - touch.touch(test, status); + touch.touch(new S3WriteFeature(session, new S3AccessControlListFeature(session)), test, status); assertTrue(new S3FindFeature(session, acl).find(test)); final Path renamed = new Path(container, new AsciiRandomStringService().random(), EnumSet.of(Path.Type.file)); new S3MoveFeature(session, acl).move(test, renamed, new TransferStatus(), new Delete.DisabledCallback(), new DisabledConnectionCallback()); @@ -163,7 +165,7 @@ public class S3MoveFeatureTest extends AbstractS3Test { final Path test = new Path(container, new AsciiRandomStringService().random(), EnumSet.of(Path.Type.file)); final S3AccessControlListFeature acl = new S3AccessControlListFeature(session); final S3TouchFeature touch = new S3TouchFeature(session, acl); - touch.touch(test, new TransferStatus()); + touch.touch(new S3WriteFeature(session, new S3AccessControlListFeature(session)), test, new TransferStatus()); assertTrue(new S3FindFeature(session, acl).find(test)); final Path renamed = new Path(container, new AsciiRandomStringService().random(), EnumSet.of(Path.Type.file)); new S3MoveFeature(session, acl).move(test, renamed, new TransferStatus(), new Delete.DisabledCallback(), new DisabledConnectionCallback()); diff --git a/s3/src/test/java/ch/cyberduck/core/s3/S3MultipartCopyFeatureTest.java b/s3/src/test/java/ch/cyberduck/core/s3/S3MultipartCopyFeatureTest.java index aa3d929792..f29512c713 100644 --- a/s3/src/test/java/ch/cyberduck/core/s3/S3MultipartCopyFeatureTest.java +++ b/s3/src/test/java/ch/cyberduck/core/s3/S3MultipartCopyFeatureTest.java @@ -55,12 +55,14 @@ public class S3MultipartCopyFeatureTest extends AbstractS3Test { assertNotNull(out); new StreamCopier(new TransferStatus(), new TransferStatus()).transfer(new ByteArrayInputStream(content), out); out.close(); - test.attributes().setSize(content.length); + // Set remote attributes + test.withAttributes(status.getResponse()); final Path copy = new Path(container, UUID.randomUUID().toString(), EnumSet.of(Path.Type.file)); - final S3MultipartCopyFeature feature = new S3MultipartCopyFeature(session, acl); - feature.copy(test, copy, status, new DisabledConnectionCallback(), new DisabledStreamListener()); + final Path copied = feature.copy(test, copy, status, new DisabledConnectionCallback(), new DisabledStreamListener()); assertTrue(new S3FindFeature(session, acl).find(test)); + assertNotEquals(test.attributes().getETag(), copied.attributes().getETag()); + assertNotEquals(test.attributes().getChecksum(), copied.attributes().getETag()); assertEquals(content.length, new S3AttributesFinderFeature(session, acl).find(test).getSize()); new S3DefaultDeleteFeature(session, acl).delete(Collections.singletonList(test), new DisabledLoginCallback(), new Delete.DisabledCallback()); assertTrue(new S3FindFeature(session, acl).find(copy)); diff --git a/s3/src/test/java/ch/cyberduck/core/s3/S3MultipartUploadServiceTest.java b/s3/src/test/java/ch/cyberduck/core/s3/S3MultipartUploadServiceTest.java index 923eeb6a7c..12277fd673 100644 --- a/s3/src/test/java/ch/cyberduck/core/s3/S3MultipartUploadServiceTest.java +++ b/s3/src/test/java/ch/cyberduck/core/s3/S3MultipartUploadServiceTest.java @@ -3,6 +3,7 @@ package ch.cyberduck.core.s3; import ch.cyberduck.core.AlphanumericRandomStringService; import ch.cyberduck.core.BytecountStreamListener; import ch.cyberduck.core.ConnectionCallback; +import ch.cyberduck.core.DefaultPathAttributes; import ch.cyberduck.core.DisabledConnectionCallback; import ch.cyberduck.core.DisabledListProgressListener; import ch.cyberduck.core.DisabledLoginCallback; @@ -50,7 +51,7 @@ public class S3MultipartUploadServiceTest extends AbstractS3Test { @Test public void testUploadSinglePart() throws Exception { final S3AccessControlListFeature acl = new S3AccessControlListFeature(session); - final S3MultipartUploadService service = new S3MultipartUploadService(session, new S3WriteFeature(session, acl), acl, 5 * 1024L * 1024L, 2); + final S3MultipartUploadService service = new S3MultipartUploadService(session, acl, 5 * 1024L * 1024L, 2); final Path container = new Path("test-eu-central-1-cyberduck", EnumSet.of(Path.Type.directory, Path.Type.volume)); final String name = String.format(" %s.txt", UUID.randomUUID()); final Path test = new Path(container, name, EnumSet.of(Path.Type.file)); @@ -61,7 +62,7 @@ public class S3MultipartUploadServiceTest extends AbstractS3Test { status.setLength(random.length); status.setStorageClass(S3Object.STORAGE_CLASS_REDUCED_REDUNDANCY); final BytecountStreamListener count = new BytecountStreamListener(); - service.upload(test, local, new BandwidthThrottle(BandwidthThrottle.UNLIMITED), + service.upload(new S3WriteFeature(session, acl), test, local, new BandwidthThrottle(BandwidthThrottle.UNLIMITED), new DisabledProgressListener(), count, status, new DisabledLoginCallback()); assertEquals(random.length, count.getSent()); assertSame(Checksum.NONE, status.getResponse().getChecksum()); @@ -85,7 +86,7 @@ public class S3MultipartUploadServiceTest extends AbstractS3Test { @Test public void testUploadBucketInHostname() throws Exception { final S3AccessControlListFeature acl = new S3AccessControlListFeature(virtualhost); - final S3MultipartUploadService service = new S3MultipartUploadService(virtualhost, new S3WriteFeature(virtualhost, acl), acl, 5 * 1024L * 1024L, 2); + final S3MultipartUploadService service = new S3MultipartUploadService(virtualhost, acl, 5 * 1024L * 1024L, 2); final Path container = new Path("test-eu-central-1-cyberduck", EnumSet.of(Path.Type.directory, Path.Type.volume)); final String name = String.format(" %s.txt", UUID.randomUUID()); final Path test = new Path(container, name, EnumSet.of(Path.Type.file)); @@ -96,7 +97,7 @@ public class S3MultipartUploadServiceTest extends AbstractS3Test { status.setLength(random.length); status.setStorageClass(S3Object.STORAGE_CLASS_REDUCED_REDUNDANCY); final BytecountStreamListener count = new BytecountStreamListener(); - service.upload(test, local, new BandwidthThrottle(BandwidthThrottle.UNLIMITED), + service.upload(new S3WriteFeature(virtualhost, acl), test, local, new BandwidthThrottle(BandwidthThrottle.UNLIMITED), new DisabledProgressListener(), count, status, new DisabledLoginCallback()); assertEquals(random.length, count.getSent()); assertSame(Checksum.NONE, status.getResponse().getChecksum()); @@ -120,7 +121,7 @@ public class S3MultipartUploadServiceTest extends AbstractS3Test { @Test public void testUploadSinglePartEncrypted() throws Exception { final S3AccessControlListFeature acl = new S3AccessControlListFeature(session); - final S3MultipartUploadService service = new S3MultipartUploadService(session, new S3WriteFeature(session, acl), acl, 5 * 1024L * 1024L, 2); + final S3MultipartUploadService service = new S3MultipartUploadService(session, acl, 5 * 1024L * 1024L, 2); final Path container = new Path("test-eu-central-1-cyberduck", EnumSet.of(Path.Type.directory, Path.Type.volume)); final String name = UUID.randomUUID() + ".txt"; final Path test = new Path(container, name, EnumSet.of(Path.Type.file)); @@ -133,7 +134,7 @@ public class S3MultipartUploadServiceTest extends AbstractS3Test { status.setModified(System.currentTimeMillis()); status.setStorageClass(S3Object.STORAGE_CLASS_REDUCED_REDUNDANCY); final BytecountStreamListener count = new BytecountStreamListener(); - service.upload(test, local, new BandwidthThrottle(BandwidthThrottle.UNLIMITED), + service.upload(new S3WriteFeature(session, acl), test, local, new BandwidthThrottle(BandwidthThrottle.UNLIMITED), new DisabledProgressListener(), count, status, new DisabledLoginCallback()); assertEquals(random.length, count.getSent()); assertSame(Checksum.NONE, status.getResponse().getChecksum()); @@ -153,19 +154,19 @@ public class S3MultipartUploadServiceTest extends AbstractS3Test { @Test(expected = NotfoundException.class) public void testUploadInvalidContainer() throws Exception { final S3AccessControlListFeature acl = new S3AccessControlListFeature(session); - final S3MultipartUploadService m = new S3MultipartUploadService(session, new S3WriteFeature(session, acl), acl, 5 * 1024L * 1024L, 1); + final S3MultipartUploadService m = new S3MultipartUploadService(session, acl, 5 * 1024L * 1024L, 1); final Path container = new Path("nosuchcontainer.cyberduck.ch", EnumSet.of(Path.Type.directory, Path.Type.volume)); final Path test = new Path(container, UUID.randomUUID().toString(), EnumSet.of(Path.Type.file)); final Local local = new Local(System.getProperty("java.io.tmpdir"), UUID.randomUUID().toString()); final TransferStatus status = new TransferStatus(); - m.upload(test, local, new BandwidthThrottle(BandwidthThrottle.UNLIMITED), new DisabledProgressListener(), new DisabledStreamListener(), status, null); + m.upload(new S3WriteFeature(session, acl), test, local, new BandwidthThrottle(BandwidthThrottle.UNLIMITED), new DisabledProgressListener(), new DisabledStreamListener(), status, null); } @Test public void testMultipleParts() throws Exception { // 5L * 1024L * 1024L final S3AccessControlListFeature acl = new S3AccessControlListFeature(session); - final S3MultipartUploadService m = new S3MultipartUploadService(session, new S3WriteFeature(session, acl), acl, 5 * 1024L * 1024L, 5); + final S3MultipartUploadService m = new S3MultipartUploadService(session, acl, 5 * 1024L * 1024L, 5); final Path container = new Path("test-eu-central-1-cyberduck", EnumSet.of(Path.Type.directory, Path.Type.volume)); final Path test = new Path(container, UUID.randomUUID().toString(), EnumSet.of(Path.Type.file)); final Local local = new Local(System.getProperty("java.io.tmpdir"), UUID.randomUUID().toString()); @@ -175,7 +176,7 @@ public class S3MultipartUploadServiceTest extends AbstractS3Test { final TransferStatus status = new TransferStatus(); status.setLength(content.length); final BytecountStreamListener count = new BytecountStreamListener(); - m.upload(test, local, new BandwidthThrottle(BandwidthThrottle.UNLIMITED), new DisabledProgressListener(), count, status, null); + m.upload(new S3WriteFeature(session, acl), test, local, new BandwidthThrottle(BandwidthThrottle.UNLIMITED), new DisabledProgressListener(), count, status, null); assertEquals(content.length, count.getSent()); assertTrue(status.isComplete()); assertNotSame(PathAttributes.EMPTY, status.getResponse()); @@ -189,7 +190,7 @@ public class S3MultipartUploadServiceTest extends AbstractS3Test { public void testMultiplePartsWithSHA256Checksum() throws Exception { // 5L * 1024L * 1024L final S3AccessControlListFeature acl = new S3AccessControlListFeature(session); - final S3MultipartUploadService m = new S3MultipartUploadService(session, new S3WriteFeature(session, acl), acl, 5 * 1024L * 1024L, 5); + final S3MultipartUploadService m = new S3MultipartUploadService(session, acl, 5 * 1024L * 1024L, 5); final Path container = new Path("test-eu-central-1-cyberduck", EnumSet.of(Path.Type.directory, Path.Type.volume)); final Path test = new Path(container, UUID.randomUUID().toString(), EnumSet.of(Path.Type.file)); final Local local = new Local(System.getProperty("java.io.tmpdir"), UUID.randomUUID().toString()); @@ -200,7 +201,7 @@ public class S3MultipartUploadServiceTest extends AbstractS3Test { status.setLength(content.length); status.setModified(System.currentTimeMillis()); final BytecountStreamListener count = new BytecountStreamListener(); - m.upload(test, local, new BandwidthThrottle(BandwidthThrottle.UNLIMITED), new DisabledProgressListener(), count, status, null); + m.upload(new S3WriteFeature(session, acl), test, local, new BandwidthThrottle(BandwidthThrottle.UNLIMITED), new DisabledProgressListener(), count, status, null); assertEquals(content.length, count.getSent()); assertTrue(status.isComplete()); assertNotSame(PathAttributes.EMPTY, status.getResponse()); @@ -227,17 +228,17 @@ public class S3MultipartUploadServiceTest extends AbstractS3Test { final AtomicBoolean interrupt = new AtomicBoolean(); final BytecountStreamListener count = new BytecountStreamListener(); final S3AccessControlListFeature acl = new S3AccessControlListFeature(session); - final S3MultipartUploadService feature = new S3MultipartUploadService(session, new S3WriteFeature(session, acl), acl, 10L * 1024L * 1024L, 1) { + final S3MultipartUploadService feature = new S3MultipartUploadService(session, acl, 10L * 1024L * 1024L, 1) { @Override - public StorageObject 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 StorageObject upload(final Write 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(status.getOffset() >= 10L * 1024L * 1024L) { 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 { - feature.upload(test, new Local(System.getProperty("java.io.tmpdir"), name), + feature.upload(new S3WriteFeature(session, acl), test, new Local(System.getProperty("java.io.tmpdir"), name), new BandwidthThrottle(BandwidthThrottle.UNLIMITED), new DisabledProgressListener(), count, status, new DisabledLoginCallback()); } @@ -254,7 +255,7 @@ public class S3MultipartUploadServiceTest extends AbstractS3Test { assertTrue(upload.getType().contains(Path.Type.upload)); assertEquals(10L * 1024L * 1024L, feature.append(upload, status).offset, 0L); final TransferStatus append = new TransferStatus().setAppend(true).setLength(2L * 1024L * 1024L).setOffset(10L * 1024L * 1024L); - new S3MultipartUploadService(session, new S3WriteFeature(session, acl), acl, 10L * 1024L * 1024L, 1).upload(test, local, + new S3MultipartUploadService(session, acl, 10L * 1024L * 1024L, 1).upload(new S3WriteFeature(session, acl), test, local, new BandwidthThrottle(BandwidthThrottle.UNLIMITED), new DisabledProgressListener(), count, append, new DisabledConnectionCallback()); assertEquals(12L * 1024L * 1024L, count.getSent()); @@ -287,7 +288,7 @@ public class S3MultipartUploadServiceTest extends AbstractS3Test { final BytecountStreamListener count = new BytecountStreamListener(); final S3AccessControlListFeature acl = new S3AccessControlListFeature(session); try { - new S3MultipartUploadService(session, new S3WriteFeature(session, acl), acl, 10485760L, 1).upload(test, new Local(System.getProperty("java.io.tmpdir"), name) { + new S3MultipartUploadService(session, acl, 10485760L, 1).upload(new S3WriteFeature(session, acl), test, new Local(System.getProperty("java.io.tmpdir"), name) { @Override public InputStream getInputStream() throws AccessDeniedException { return new CountingInputStream(super.getInputStream()) { @@ -312,8 +313,8 @@ public class S3MultipartUploadServiceTest extends AbstractS3Test { assertFalse(status.isComplete()); final TransferStatus append = new TransferStatus().setAppend(true).setLength(content.length); - new S3MultipartUploadService(session, new S3WriteFeature(session, acl), acl, 10485760L, 1).upload( - test, local, new BandwidthThrottle(BandwidthThrottle.UNLIMITED), + new S3MultipartUploadService(session, acl, 10485760L, 1).upload( + new S3WriteFeature(session, acl), test, local, new BandwidthThrottle(BandwidthThrottle.UNLIMITED), new DisabledProgressListener(), count, append, new DisabledConnectionCallback()); assertEquals(32769L, count.getSent()); @@ -333,7 +334,7 @@ public class S3MultipartUploadServiceTest extends AbstractS3Test { @Test public void testAppendBelowLimit() throws Exception { final S3AccessControlListFeature acl = new S3AccessControlListFeature(session); - final S3MultipartUploadService feature = new S3MultipartUploadService(session, new S3WriteFeature(session, acl), acl, 5 * 1024L * 1024L, 5); + final S3MultipartUploadService feature = new S3MultipartUploadService(session, acl, 5 * 1024L * 1024L, 5); final Write.Append append = feature.append(new Path("/p", EnumSet.of(Path.Type.file)), new TransferStatus().setLength(0L)); assertFalse(append.append); } @@ -341,8 +342,8 @@ public class S3MultipartUploadServiceTest extends AbstractS3Test { @Test public void testSize() throws Exception { final S3AccessControlListFeature acl = new S3AccessControlListFeature(session); - final S3MultipartUploadService feature = new S3MultipartUploadService(session, new S3WriteFeature(session, acl), acl, 5 * 1024L * 1024L, 5); - final Write.Append append = feature.append(new Path("/p", EnumSet.of(Path.Type.file)), new TransferStatus().setLength(0L).setRemote(new PathAttributes().setSize(3L))); + final S3MultipartUploadService feature = new S3MultipartUploadService(session, acl, 5 * 1024L * 1024L, 5); + final Write.Append append = feature.append(new Path("/p", EnumSet.of(Path.Type.file)), new TransferStatus().setLength(0L).setRemote(new DefaultPathAttributes().setSize(3L))); assertFalse(append.append); assertEquals(0L, append.offset, 0L); } @@ -351,7 +352,7 @@ public class S3MultipartUploadServiceTest extends AbstractS3Test { public void testAppendNoMultipartFound() throws Exception { final Path container = new Path("test-eu-central-1-cyberduck", EnumSet.of(Path.Type.directory, Path.Type.volume)); final S3AccessControlListFeature acl = new S3AccessControlListFeature(session); - final S3MultipartUploadService feature = new S3MultipartUploadService(session, new S3WriteFeature(session, acl), acl, 5 * 1024L * 1024L, 5); + final S3MultipartUploadService feature = new S3MultipartUploadService(session, acl, 5 * 1024L * 1024L, 5); assertFalse(feature.append(new Path(container, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)), new TransferStatus().setLength(Long.MAX_VALUE)).append); assertEquals(Write.override, feature.append(new Path(container, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)), new TransferStatus().setLength(Long.MAX_VALUE))); assertEquals(Write.override, feature.append(new Path(container, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)), new TransferStatus().setLength(0L))); diff --git a/s3/src/test/java/ch/cyberduck/core/s3/S3MultipleDeleteFeatureTest.java b/s3/src/test/java/ch/cyberduck/core/s3/S3MultipleDeleteFeatureTest.java index 47c88fb5c7..919a0637a5 100644 --- a/s3/src/test/java/ch/cyberduck/core/s3/S3MultipleDeleteFeatureTest.java +++ b/s3/src/test/java/ch/cyberduck/core/s3/S3MultipleDeleteFeatureTest.java @@ -48,7 +48,7 @@ public class S3MultipleDeleteFeatureTest extends AbstractS3Test { public void testDeleteFile() throws Exception { final Path container = new Path("test-eu-central-1-cyberduck", EnumSet.of(Path.Type.directory, Path.Type.volume)); final Path test = new Path(container, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)); - new S3TouchFeature(session, new S3AccessControlListFeature(session)).touch(test, new TransferStatus()); + new S3TouchFeature(session, new S3AccessControlListFeature(session)).touch(new S3WriteFeature(session, new S3AccessControlListFeature(session)), test, new TransferStatus()); assertTrue(new S3FindFeature(session, new S3AccessControlListFeature(session)).find(test)); new S3MultipleDeleteFeature(session, new S3AccessControlListFeature(session)).delete(Arrays.asList(test, test), new DisabledLoginCallback(), new Delete.DisabledCallback()); assertFalse(new S3FindFeature(session, new S3AccessControlListFeature(session)).find(test)); @@ -59,7 +59,7 @@ public class S3MultipleDeleteFeatureTest extends AbstractS3Test { final Path container = new Path("test-eu-central-1-cyberduck", EnumSet.of(Path.Type.volume, Path.Type.directory)); final Path test = new Path(container, String.format("%s\\%s", new AlphanumericRandomStringService().random(), new AlphanumericRandomStringService().random()), EnumSet.of(Path.Type.file)); - new S3TouchFeature(session, new S3AccessControlListFeature(session)).touch(test, new TransferStatus()); + new S3TouchFeature(session, new S3AccessControlListFeature(session)).touch(new S3WriteFeature(session, new S3AccessControlListFeature(session)), test, new TransferStatus()); assertTrue(new S3FindFeature(session, new S3AccessControlListFeature(session)).find(test)); new S3MultipleDeleteFeature(session, new S3AccessControlListFeature(session)).delete(Collections.singletonList(test), new DisabledLoginCallback(), new Delete.DisabledCallback()); assertFalse(new S3FindFeature(session, new S3AccessControlListFeature(session)).find(test)); @@ -69,7 +69,7 @@ public class S3MultipleDeleteFeatureTest extends AbstractS3Test { public void testDeleteFileVirtualHost() throws Exception { final Path test = new Path(new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)); final S3AccessControlListFeature acl = new S3AccessControlListFeature(virtualhost); - new S3TouchFeature(virtualhost, acl).touch(test, new TransferStatus()); + new S3TouchFeature(virtualhost, acl).touch(new S3WriteFeature(virtualhost, acl), test, new TransferStatus()); assertTrue(new S3FindFeature(virtualhost, acl).find(test)); new S3MultipleDeleteFeature(virtualhost, acl).delete(Arrays.asList(test, test), new DisabledLoginCallback(), new Delete.DisabledCallback()); assertFalse(new S3FindFeature(virtualhost, acl).find(test)); @@ -79,8 +79,8 @@ public class S3MultipleDeleteFeatureTest extends AbstractS3Test { public void testDeletePlaceholder() throws Exception { final Path container = new Path("test-eu-central-1-cyberduck", EnumSet.of(Path.Type.directory, Path.Type.volume)); final S3AccessControlListFeature acl = new S3AccessControlListFeature(session); - final Path test = new S3DirectoryFeature(session, new S3WriteFeature(session, acl), acl).mkdir( - new Path(container, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory)), new TransferStatus()); + final Path test = new S3DirectoryFeature(session, acl).mkdir( + new S3WriteFeature(session, new S3AccessControlListFeature(session)), new Path(container, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory)), new TransferStatus()); assertTrue(new S3FindFeature(session, acl).find(test)); assertTrue(new DefaultFindFeature(session).find(test)); new S3MultipleDeleteFeature(session, acl).delete(Collections.singletonList(test), new DisabledLoginCallback(), new Delete.DisabledCallback()); @@ -93,16 +93,16 @@ public class S3MultipleDeleteFeatureTest extends AbstractS3Test { final String name = new AlphanumericRandomStringService().random(); final S3AccessControlListFeature acl = new S3AccessControlListFeature(session); { - final Path test = new S3DirectoryFeature(session, new S3WriteFeature(session, acl), acl).mkdir( - new Path(container, name, EnumSet.of(Path.Type.directory)), new TransferStatus()); + final Path test = new S3DirectoryFeature(session, acl).mkdir( + new S3WriteFeature(session, new S3AccessControlListFeature(session)), new Path(container, name, EnumSet.of(Path.Type.directory)), new TransferStatus()); assertTrue(new S3FindFeature(session, acl).find(test)); assertTrue(new DefaultFindFeature(session).find(test)); new S3MultipleDeleteFeature(session, acl).delete(Collections.singletonList(test), new DisabledLoginCallback(), new Delete.DisabledCallback()); assertFalse(new S3FindFeature(session, acl).find(test)); } { - final Path test = new S3DirectoryFeature(session, new S3WriteFeature(session, acl), acl).mkdir( - new Path(container, name, EnumSet.of(Path.Type.directory)), new TransferStatus()); + final Path test = new S3DirectoryFeature(session, acl).mkdir( + new S3WriteFeature(session, new S3AccessControlListFeature(session)), new Path(container, name, EnumSet.of(Path.Type.directory)), new TransferStatus()); assertTrue(new S3FindFeature(session, acl).find(test)); assertTrue(new DefaultFindFeature(session).find(test)); new S3MultipleDeleteFeature(session, acl).delete(Collections.singletonList(test), new DisabledLoginCallback(), new Delete.DisabledCallback()); @@ -116,7 +116,7 @@ public class S3MultipleDeleteFeatureTest extends AbstractS3Test { public void testDeleteContainer() throws Exception { final Path container = new Path(new AsciiRandomStringService().random(), EnumSet.of(Path.Type.volume, Path.Type.directory)); final S3AccessControlListFeature acl = new S3AccessControlListFeature(session); - new S3DirectoryFeature(session, new S3WriteFeature(session, acl), acl).mkdir(container, new TransferStatus()); + new S3DirectoryFeature(session, acl).mkdir(new S3WriteFeature(session, new S3AccessControlListFeature(session)), container, new TransferStatus()); assertTrue(new S3FindFeature(session, acl).find(container)); new S3MultipleDeleteFeature(session, acl).delete(Arrays.asList(container, new Path(container, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file))), new DisabledLoginCallback(), new Delete.DisabledCallback()); diff --git a/s3/src/test/java/ch/cyberduck/core/s3/S3ObjectListServiceTest.java b/s3/src/test/java/ch/cyberduck/core/s3/S3ObjectListServiceTest.java index fb42bd3b7f..51cc523d83 100644 --- a/s3/src/test/java/ch/cyberduck/core/s3/S3ObjectListServiceTest.java +++ b/s3/src/test/java/ch/cyberduck/core/s3/S3ObjectListServiceTest.java @@ -67,7 +67,7 @@ public class S3ObjectListServiceTest extends AbstractS3Test { public void testDirectory() throws Exception { final Path bucket = new Path("test-eu-central-1-cyberduck", EnumSet.of(Path.Type.directory, Path.Type.volume)); final S3AccessControlListFeature acl = new S3AccessControlListFeature(session); - final Path directory = new S3DirectoryFeature(session, new S3WriteFeature(session, acl), acl).mkdir(new Path(bucket, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory)), new TransferStatus()); + final Path directory = new S3DirectoryFeature(session, acl).mkdir(new S3WriteFeature(session, new S3AccessControlListFeature(session)), new Path(bucket, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory)), new TransferStatus()); final S3ObjectListService feature = new S3ObjectListService(session, acl); assertTrue(feature.list(bucket, new DisabledListProgressListener()).contains(directory)); final AtomicBoolean callback = new AtomicBoolean(); @@ -90,7 +90,7 @@ public class S3ObjectListServiceTest extends AbstractS3Test { final String name = new AlphanumericRandomStringService().random(); final S3AccessControlListFeature acl = new S3AccessControlListFeature(session); assertThrows(NotfoundException.class, () -> new S3ObjectListService(session, acl).list(new Path(container, name, EnumSet.of(Path.Type.directory)), new DisabledListProgressListener())); - final Path file = new S3TouchFeature(session, acl).touch(new Path(container, String.format("%s-", name), EnumSet.of(Path.Type.file)), new TransferStatus()); + final Path file = new S3TouchFeature(session, acl).touch(new S3WriteFeature(session, new S3AccessControlListFeature(session)), new Path(container, String.format("%s-", name), EnumSet.of(Path.Type.file)), new TransferStatus()); assertThrows(NotfoundException.class, () -> new S3ObjectListService(session, acl).list(new Path(container, name, EnumSet.of(Path.Type.directory)), new DisabledListProgressListener())); new S3DefaultDeleteFeature(session, acl).delete(Collections.singletonList(file), new DisabledLoginCallback(), new Delete.DisabledCallback()); } @@ -100,7 +100,7 @@ public class S3ObjectListServiceTest extends AbstractS3Test { final Path container = new Path("test-eu-central-1-cyberduck", EnumSet.of(Path.Type.volume, Path.Type.directory)); final String name = new AlphanumericRandomStringService().random(); final S3AccessControlListFeature acl = new S3AccessControlListFeature(session); - final Path file = new S3TouchFeature(session, acl).touch(new Path(container, name, EnumSet.of(Path.Type.file)), new TransferStatus()); + final Path file = new S3TouchFeature(session, acl).touch(new S3WriteFeature(session, new S3AccessControlListFeature(session)), new Path(container, name, EnumSet.of(Path.Type.file)), new TransferStatus()); assertTrue(new S3ObjectListService(session, acl).list(container, new DisabledListProgressListener()).contains(file)); assertThrows(NotfoundException.class, () -> new S3ObjectListService(session, acl).list(new Path(container, name, EnumSet.of(Path.Type.directory)), new DisabledListProgressListener())); new S3DefaultDeleteFeature(session, acl).delete(Collections.singletonList(file), new DisabledLoginCallback(), new Delete.DisabledCallback()); @@ -124,8 +124,8 @@ public class S3ObjectListServiceTest extends AbstractS3Test { new DisabledPasswordStore(), new DisabledProgressListener()); login.check(session, new DisabledCancelCallback()); final S3AccessControlListFeature acl = new S3AccessControlListFeature(session); - final Path directory = new S3DirectoryFeature(session, new S3WriteFeature(session, acl), acl).mkdir( - new Path(new DefaultHomeFinderService(session).find(), new AsciiRandomStringService().random(), EnumSet.of(Path.Type.directory, Path.Type.volume)), new TransferStatus()); + final Path directory = new S3DirectoryFeature(session, acl).mkdir( + new S3WriteFeature(session, new S3AccessControlListFeature(session)), new Path(new DefaultHomeFinderService(session).find(), new AsciiRandomStringService().random(), EnumSet.of(Path.Type.directory, Path.Type.volume)), new TransferStatus()); try { new S3ObjectListService(session, acl).list(new Path(directory, new AsciiRandomStringService(30).random(), EnumSet.of(Path.Type.directory)), new DisabledListProgressListener()); } @@ -145,7 +145,7 @@ public class S3ObjectListServiceTest extends AbstractS3Test { final Path container = new Path("test-eu-central-1-cyberduck", EnumSet.of(Path.Type.directory, Path.Type.volume)); final S3AccessControlListFeature acl = new S3AccessControlListFeature(session); final Path placeholder = new S3TouchFeature(session, acl).touch( - new Path(container, String.format("^<%%%s", new AlphanumericRandomStringService().random()), EnumSet.of(Path.Type.file)), new TransferStatus()); + new S3WriteFeature(session, new S3AccessControlListFeature(session)), new Path(container, String.format("^<%%%s", new AlphanumericRandomStringService().random()), EnumSet.of(Path.Type.file)), new TransferStatus()); assertTrue(new S3ObjectListService(session, acl).list(container, new DisabledListProgressListener()).contains(placeholder)); new S3DefaultDeleteFeature(session, acl).delete(Collections.singletonList(placeholder), new DisabledLoginCallback(), new Delete.DisabledCallback()); } @@ -155,7 +155,7 @@ public class S3ObjectListServiceTest extends AbstractS3Test { final Path container = new Path("test-eu-central-1-cyberduck", EnumSet.of(Path.Type.directory, Path.Type.volume)); final S3AccessControlListFeature acl = new S3AccessControlListFeature(session); final Path placeholder = new S3TouchFeature(session, acl).touch( - new Path(container, String.format("test-\u001F-%s", new AlphanumericRandomStringService().random()), EnumSet.of(Path.Type.file)), new TransferStatus()); + new S3WriteFeature(session, new S3AccessControlListFeature(session)), new Path(container, String.format("test-\u001F-%s", new AlphanumericRandomStringService().random()), EnumSet.of(Path.Type.file)), new TransferStatus()); assertTrue(new S3ObjectListService(session, acl).list(container, new DisabledListProgressListener()).contains(placeholder)); new S3DefaultDeleteFeature(session, acl).delete(Collections.singletonList(placeholder), new DisabledLoginCallback(), new Delete.DisabledCallback()); } @@ -164,8 +164,8 @@ public class S3ObjectListServiceTest extends AbstractS3Test { public void testListPlaceholder() throws Exception { final Path container = new Path("test-eu-central-1-cyberduck", EnumSet.of(Path.Type.directory, Path.Type.volume)); final S3AccessControlListFeature acl = new S3AccessControlListFeature(session); - final Path placeholder = new S3DirectoryFeature(session, new S3WriteFeature(session, acl), acl).mkdir( - new Path(container, UUID.randomUUID().toString(), EnumSet.of(Path.Type.directory)), new TransferStatus()); + final Path placeholder = new S3DirectoryFeature(session, acl).mkdir( + new S3WriteFeature(session, new S3AccessControlListFeature(session)), new Path(container, UUID.randomUUID().toString(), EnumSet.of(Path.Type.directory)), new TransferStatus()); final AttributedList list = new S3ObjectListService(session, acl).list(placeholder, new DisabledListProgressListener()); assertTrue(list.isEmpty()); new S3DefaultDeleteFeature(session, acl).delete(Collections.singletonList(placeholder), new DisabledLoginCallback(), new Delete.DisabledCallback()); @@ -175,11 +175,11 @@ public class S3ObjectListServiceTest extends AbstractS3Test { public void testListPlaceholderTilde() throws Exception { final Path container = new Path("test-eu-central-1-cyberduck", EnumSet.of(Path.Type.directory, Path.Type.volume)); final S3AccessControlListFeature acl = new S3AccessControlListFeature(session); - final Path placeholderTildeEnd = new S3DirectoryFeature(session, new S3WriteFeature(session, acl), acl).mkdir( - new Path(container, String.format("%s~", new AlphanumericRandomStringService().random()), EnumSet.of(Path.Type.directory)), new TransferStatus()); + final Path placeholderTildeEnd = new S3DirectoryFeature(session, acl).mkdir( + new S3WriteFeature(session, new S3AccessControlListFeature(session)), new Path(container, String.format("%s~", new AlphanumericRandomStringService().random()), EnumSet.of(Path.Type.directory)), new TransferStatus()); assertTrue(new S3ObjectListService(session, acl).list(container, new DisabledListProgressListener()).contains(placeholderTildeEnd)); - final Path placeholderTildeStart = new S3DirectoryFeature(session, new S3WriteFeature(session, acl), acl).mkdir( - new Path(container, String.format("~%s", new AlphanumericRandomStringService().random()), EnumSet.of(Path.Type.directory)), new TransferStatus()); + final Path placeholderTildeStart = new S3DirectoryFeature(session, acl).mkdir( + new S3WriteFeature(session, new S3AccessControlListFeature(session)), new Path(container, String.format("~%s", new AlphanumericRandomStringService().random()), EnumSet.of(Path.Type.directory)), new TransferStatus()); assertTrue(new S3ObjectListService(session, acl).list(container, new DisabledListProgressListener()).contains(placeholderTildeStart)); assertTrue(new S3ObjectListService(session, acl).list(placeholderTildeEnd, new DisabledListProgressListener()).isEmpty()); assertTrue(new S3ObjectListService(session, acl).list(placeholderTildeStart, new DisabledListProgressListener()).isEmpty()); @@ -191,7 +191,7 @@ public class S3ObjectListServiceTest extends AbstractS3Test { final Path container = new Path("test-eu-central-1-cyberduck", EnumSet.of(Path.Type.directory, Path.Type.volume)); final S3AccessControlListFeature acl = new S3AccessControlListFeature(session); final Path file = new S3TouchFeature(session, acl).touch( - new Path(container, String.format("@%s", new AlphanumericRandomStringService().random()), EnumSet.of(Path.Type.file)), new TransferStatus()); + new S3WriteFeature(session, new S3AccessControlListFeature(session)), new Path(container, String.format("@%s", new AlphanumericRandomStringService().random()), EnumSet.of(Path.Type.file)), new TransferStatus()); assertTrue(new S3ObjectListService(session, acl).list(container, new DisabledListProgressListener()).contains(file)); new S3DefaultDeleteFeature(session, acl).delete(Collections.singletonList(file), new DisabledLoginCallback(), new Delete.DisabledCallback()); } @@ -200,8 +200,8 @@ public class S3ObjectListServiceTest extends AbstractS3Test { public void testListPlaceholderAtSignSignatureAWS4() throws Exception { final Path container = new Path("test-eu-central-1-cyberduck", EnumSet.of(Path.Type.directory, Path.Type.volume)); final S3AccessControlListFeature acl = new S3AccessControlListFeature(session); - final Path placeholder = new S3DirectoryFeature(session, new S3WriteFeature(session, acl), acl).mkdir( - new Path(container, String.format("%s@", UUID.randomUUID()), EnumSet.of(Path.Type.directory)), new TransferStatus()); + final Path placeholder = new S3DirectoryFeature(session, acl).mkdir( + new S3WriteFeature(session, new S3AccessControlListFeature(session)), new Path(container, String.format("%s@", UUID.randomUUID()), EnumSet.of(Path.Type.directory)), new TransferStatus()); final AttributedList list = new S3ObjectListService(session, acl).list(placeholder, new DisabledListProgressListener()); assertTrue(list.isEmpty()); new S3DefaultDeleteFeature(session, acl).delete(Collections.singletonList(placeholder), new DisabledLoginCallback(), new Delete.DisabledCallback()); @@ -253,7 +253,7 @@ public class S3ObjectListServiceTest extends AbstractS3Test { final Path container = new Path("test-eu-central-1-cyberduck", EnumSet.of(Path.Type.directory, Path.Type.volume)); final S3AccessControlListFeature acl = new S3AccessControlListFeature(session); final Path file = new S3TouchFeature(session, acl).touch( - new Path(container, String.format("test+%s", new AlphanumericRandomStringService().random()), EnumSet.of(Path.Type.file)), new TransferStatus()); + new S3WriteFeature(session, new S3AccessControlListFeature(session)), new Path(container, String.format("test+%s", new AlphanumericRandomStringService().random()), EnumSet.of(Path.Type.file)), new TransferStatus()); assertTrue(new S3ObjectListService(session, acl).list(container, new DisabledListProgressListener()).contains(file)); new S3DefaultDeleteFeature(session, acl).delete(Collections.singletonList(file), new DisabledLoginCallback(), new Delete.DisabledCallback()); } @@ -263,7 +263,7 @@ public class S3ObjectListServiceTest extends AbstractS3Test { final Path container = new Path("test-eu-central-1-cyberduck", EnumSet.of(Path.Type.directory, Path.Type.volume)); final S3AccessControlListFeature acl = new S3AccessControlListFeature(session); final Path file = new S3TouchFeature(session, acl).touch( - new Path(container, ".", EnumSet.of(Path.Type.file)), new TransferStatus()); + new S3WriteFeature(session, new S3AccessControlListFeature(session)), new Path(container, ".", EnumSet.of(Path.Type.file)), new TransferStatus()); assertTrue(new S3ObjectListService(session, acl).list(container, new DisabledListProgressListener()).contains(file)); new S3DefaultDeleteFeature(session, acl).delete(Collections.singletonList(file), new DisabledLoginCallback(), new Delete.DisabledCallback()); } @@ -272,8 +272,8 @@ public class S3ObjectListServiceTest extends AbstractS3Test { public void testListPlaceholderDot() throws Exception { final Path container = new Path("test-eu-central-1-cyberduck", EnumSet.of(Path.Type.directory, Path.Type.volume)); final S3AccessControlListFeature acl = new S3AccessControlListFeature(session); - final Path placeholder = new S3DirectoryFeature(session, new S3WriteFeature(session, acl), acl).mkdir( - new Path(container, ".", EnumSet.of(Path.Type.directory)), new TransferStatus()); + final Path placeholder = new S3DirectoryFeature(session, acl).mkdir( + new S3WriteFeature(session, new S3AccessControlListFeature(session)), new Path(container, ".", EnumSet.of(Path.Type.directory)), new TransferStatus()); assertTrue(new S3ObjectListService(session, new S3AccessControlListFeature(session)).list(container, new DisabledListProgressListener()).contains(placeholder)); new S3DefaultDeleteFeature(session, acl).delete(Collections.singletonList(placeholder), new DisabledLoginCallback(), new Delete.DisabledCallback()); } @@ -282,8 +282,8 @@ public class S3ObjectListServiceTest extends AbstractS3Test { public void testListPlaceholderPlusCharacter() throws Exception { final Path container = new Path("test-eu-central-1-cyberduck", EnumSet.of(Path.Type.directory, Path.Type.volume)); final S3AccessControlListFeature acl = new S3AccessControlListFeature(session); - final Path placeholder = new S3DirectoryFeature(session, new S3WriteFeature(session, acl), acl).mkdir( - new Path(container, String.format("test+%s", new AlphanumericRandomStringService().random()), EnumSet.of(Path.Type.directory)), new TransferStatus()); + final Path placeholder = new S3DirectoryFeature(session, acl).mkdir( + new S3WriteFeature(session, new S3AccessControlListFeature(session)), new Path(container, String.format("test+%s", new AlphanumericRandomStringService().random()), EnumSet.of(Path.Type.directory)), new TransferStatus()); assertTrue(new S3ObjectListService(session, acl).list(container, new DisabledListProgressListener()).contains(placeholder)); assertTrue(new S3ObjectListService(session, acl).list(placeholder, new DisabledListProgressListener()).isEmpty()); new S3DefaultDeleteFeature(session, acl).delete(Collections.singletonList(placeholder), new DisabledLoginCallback(), new Delete.DisabledCallback()); @@ -293,8 +293,8 @@ public class S3ObjectListServiceTest extends AbstractS3Test { public void testDetermineCorrectRegionFrom400Reply() throws Exception { final Path bucket = new Path(new DefaultHomeFinderService(session).find(), new AsciiRandomStringService(30).random(), EnumSet.of(Path.Type.directory, Path.Type.volume)); final S3AccessControlListFeature acl = new S3AccessControlListFeature(session); - final S3DirectoryFeature feature = new S3DirectoryFeature(session, new S3WriteFeature(session, acl), acl); - feature.mkdir(bucket, new TransferStatus().setRegion("eu-central-1")); + final S3DirectoryFeature feature = new S3DirectoryFeature(session, acl); + feature.mkdir(new S3WriteFeature(session, new S3AccessControlListFeature(session)), bucket, new TransferStatus().setRegion("eu-central-1")); // Populate incorrect region in cache final RegionEndpointCache cache = session.getClient().getRegionEndpointCache(); assertEquals("eu-central-1", cache.getRegionForBucketName(bucket.getName())); diff --git a/s3/src/test/java/ch/cyberduck/core/s3/S3PresignedUrlProviderTest.java b/s3/src/test/java/ch/cyberduck/core/s3/S3PresignedUrlProviderTest.java index 0e46d20493..5778318814 100644 --- a/s3/src/test/java/ch/cyberduck/core/s3/S3PresignedUrlProviderTest.java +++ b/s3/src/test/java/ch/cyberduck/core/s3/S3PresignedUrlProviderTest.java @@ -40,7 +40,7 @@ public class S3PresignedUrlProviderTest extends AbstractS3Test { public void testCreateEuWest() throws Exception { final Calendar expiry = Calendar.getInstance(TimeZone.getTimeZone("UTC")); expiry.add(Calendar.MILLISECOND, (int) TimeUnit.DAYS.toMillis(7)); - final String url = new S3PresignedUrlProvider(session).create(PROPERTIES.get("s3.secret"), + final String url = new S3PresignedUrlProvider(session).create(session.getAuthentication().get(), "test-eu-west-1-cyberduck", "eu-west-1", "f", "GET", expiry.getTimeInMillis()); assertNotNull(url); assertEquals("test-eu-west-1-cyberduck.s3.dualstack.eu-west-1.amazonaws.com", URI.create(url).getHost()); @@ -52,7 +52,7 @@ public class S3PresignedUrlProviderTest extends AbstractS3Test { public void testCreateEuCentral() throws Exception { final Calendar expiry = Calendar.getInstance(TimeZone.getTimeZone("UTC")); expiry.add(Calendar.MILLISECOND, (int) TimeUnit.DAYS.toMillis(7)); - final String url = new S3PresignedUrlProvider(session).create(PROPERTIES.get("s3.secret"), + final String url = new S3PresignedUrlProvider(session).create(session.getAuthentication().get(), "test-eu-central-1-cyberduck", "eu-central-1", "f", "GET", expiry.getTimeInMillis()); assertNotNull(url); assertEquals("test-eu-central-1-cyberduck.s3.dualstack.eu-central-1.amazonaws.com", URI.create(url).getHost()); @@ -62,7 +62,7 @@ public class S3PresignedUrlProviderTest extends AbstractS3Test { public void testCreateVirtualHost() throws Exception { final Calendar expiry = Calendar.getInstance(TimeZone.getTimeZone("UTC")); expiry.add(Calendar.MILLISECOND, (int) TimeUnit.DAYS.toMillis(7)); - final String url = new S3PresignedUrlProvider(virtualhost).create(System.getProperties().getProperty("s3.secret"), + final String url = new S3PresignedUrlProvider(virtualhost).create(virtualhost.getHost().getCredentials(), "test-eu-central-1-cyberduck", "eu-central-1", "f", "GET", expiry.getTimeInMillis()); assertNotNull(url); assertEquals("test-eu-central-1-cyberduck.s3.dualstack.eu-central-1.amazonaws.com", URI.create(url).getHost()); @@ -73,7 +73,7 @@ public class S3PresignedUrlProviderTest extends AbstractS3Test { public void testCreateEuCentralAtSign() throws Exception { final Calendar expiry = Calendar.getInstance(TimeZone.getTimeZone("UTC")); expiry.add(Calendar.MILLISECOND, (int) TimeUnit.DAYS.toMillis(7)); - final String url = new S3PresignedUrlProvider(session).create(PROPERTIES.get("s3.secret"), + final String url = new S3PresignedUrlProvider(session).create(session.getAuthentication().get(), "test-eu-central-1-cyberduck", "eu-central-1", "@f", "GET", expiry.getTimeInMillis()); assertNotNull(url); assertEquals("test-eu-central-1-cyberduck.s3.dualstack.eu-central-1.amazonaws.com", URI.create(url).getHost()); @@ -84,7 +84,7 @@ public class S3PresignedUrlProviderTest extends AbstractS3Test { public void testCreateDefault() throws Exception { final Calendar expiry = Calendar.getInstance(TimeZone.getTimeZone("UTC")); expiry.add(Calendar.MILLISECOND, (int) TimeUnit.DAYS.toMillis(7)); - final String url = new S3PresignedUrlProvider(session).create(PROPERTIES.get("s3.secret"), + final String url = new S3PresignedUrlProvider(session).create(session.getAuthentication().get(), "test-us-east-1-cyberduck", "us-east-1", "f", "GET", expiry.getTimeInMillis()); assertNotNull(url); assertEquals("test-us-east-1-cyberduck.s3.dualstack.us-east-1.amazonaws.com", URI.create(url).getHost()); @@ -94,31 +94,41 @@ public class S3PresignedUrlProviderTest extends AbstractS3Test { } @Test - public void testCustomHostname() { + public void testCustomHostname() throws Exception { final Calendar expiry = Calendar.getInstance(TimeZone.getTimeZone("UTC")); expiry.add(Calendar.MILLISECOND, (int) TimeUnit.DAYS.toMillis(7)); final Host host = new Host(new S3Protocol(), "h"); - final S3Session session = new S3Session(host); - final String url = new S3PresignedUrlProvider(session).create(PROPERTIES.get("s3.secret"), + final S3Session session = new S3Session(host) { + @Override + public S3CredentialsStrategy getAuthentication() { + return host::getCredentials; + } + }; + final String url = new S3PresignedUrlProvider(session).create(session.getAuthentication().get(), "test-us-east-1-cyberduck", "us-east-1", "f", "GET", expiry.getTimeInMillis()); assertNotNull(url); assertEquals("test-us-east-1-cyberduck.h", URI.create(url).getHost()); } @Test - public void testCustomHostnameWithRegion() { + public void testCustomHostnameWithRegion() throws Exception { final Calendar expiry = Calendar.getInstance(TimeZone.getTimeZone("UTC")); expiry.add(Calendar.MILLISECOND, (int) TimeUnit.DAYS.toMillis(7)); final Host host = new Host(new S3Protocol(), "s3.eu-central-1.wasabisys.com"); - final S3Session session = new S3Session(host); - final String url = new S3PresignedUrlProvider(session).create(PROPERTIES.get("s3.secret"), + final S3Session session = new S3Session(host) { + @Override + public S3CredentialsStrategy getAuthentication() { + return host::getCredentials; + } + }; + final String url = new S3PresignedUrlProvider(session).create(session.getAuthentication().get(), "cyberduck", "eu-central-1", "f", "GET", expiry.getTimeInMillis()); assertNotNull(url); assertEquals("cyberduck.s3.eu-central-1.wasabisys.com", URI.create(url).getHost()); } @Test - public void testDefaultHostnameWithRegionWithProfile() { + public void testDefaultHostnameWithRegionWithProfile() throws Exception { final Calendar expiry = Calendar.getInstance(TimeZone.getTimeZone("UTC")); expiry.add(Calendar.MILLISECOND, (int) TimeUnit.DAYS.toMillis(7)); final Host host = new Host(new S3Protocol() { @@ -127,15 +137,20 @@ public class S3PresignedUrlProviderTest extends AbstractS3Test { return "wasabisys.com"; } }, "wasabisys.com"); - final S3Session session = new S3Session(host); - final String url = new S3PresignedUrlProvider(session).create(PROPERTIES.get("s3.secret"), + final S3Session session = new S3Session(host) { + @Override + public S3CredentialsStrategy getAuthentication() { + return host::getCredentials; + } + }; + final String url = new S3PresignedUrlProvider(session).create(session.getAuthentication().get(), "cyberduck", "eu-central-1", "f", "GET", expiry.getTimeInMillis()); assertNotNull(url); assertEquals("cyberduck.wasabisys.com", URI.create(url).getHost()); } @Test - public void testDefaultHostnameWithProfile() { + public void testDefaultHostnameWithProfile() throws Exception { final Calendar expiry = Calendar.getInstance(TimeZone.getTimeZone("UTC")); expiry.add(Calendar.MILLISECOND, (int) TimeUnit.DAYS.toMillis(7)); final Host host = new Host(new S3Protocol() { @@ -144,15 +159,20 @@ public class S3PresignedUrlProviderTest extends AbstractS3Test { return "h"; } }, "h"); - final S3Session session = new S3Session(host); - final String url = new S3PresignedUrlProvider(session).create(PROPERTIES.get("s3.secret"), + final S3Session session = new S3Session(host) { + @Override + public S3CredentialsStrategy getAuthentication() { + return host::getCredentials; + } + }; + final String url = new S3PresignedUrlProvider(session).create(session.getAuthentication().get(), "test-us-east-1-cyberduck", "us-east-1", "f", "GET", expiry.getTimeInMillis()); assertNotNull(url); assertEquals("test-us-east-1-cyberduck.h", URI.create(url).getHost()); } @Test - public void testDnsBucketNamingDisabled() { + public void testDnsBucketNamingDisabled() throws Exception { final Host host = new Host(new S3Protocol(), new S3Protocol().getDefaultHostname(), new Credentials( PROPERTIES.get("s3.key"), PROPERTIES.get("s3.secret") )) { @@ -164,13 +184,49 @@ public class S3PresignedUrlProviderTest extends AbstractS3Test { return super.getProperty(key); } }; - final S3Session session = new S3Session(host); + final S3Session session = new S3Session(host) { + @Override + public S3CredentialsStrategy getAuthentication() { + return host::getCredentials; + } + }; final Calendar expiry = Calendar.getInstance(TimeZone.getTimeZone("UTC")); expiry.add(Calendar.MILLISECOND, (int) TimeUnit.DAYS.toMillis(7)); - final String url = new S3PresignedUrlProvider(session).create(PROPERTIES.get("s3.secret"), + final String url = new S3PresignedUrlProvider(session).create(session.getAuthentication().get(), "test-bucket", "region", "f", "GET", expiry.getTimeInMillis()); assertNotNull(url); assertEquals("s3.dualstack.region.amazonaws.com", URI.create(url).getHost()); assertEquals("/test-bucket/f", URI.create(url).getPath()); } + + @Test + public void testContextPath() throws Exception { + final Calendar expiry = Calendar.getInstance(TimeZone.getTimeZone("UTC")); + expiry.add(Calendar.MILLISECOND, (int) TimeUnit.DAYS.toMillis(7)); + final Host host = new Host(new S3Protocol() { + @Override + public String getContext() { + return "/storage/v1/s3"; + } + }, "example.supabase.co") { + @Override + public String getProperty(final String key) { + if("s3.bucket.virtualhost.disable".equals(key)) { + return String.valueOf(true); + } + return super.getProperty(key); + } + }; + final S3Session session = new S3Session(host) { + @Override + public S3CredentialsStrategy getAuthentication() { + return host::getCredentials; + } + }; + final String url = new S3PresignedUrlProvider(session).create(session.getAuthentication().get(), + "bucket", "eu-west-1", "key.jpg", "GET", expiry.getTimeInMillis()); + assertNotNull(url); + assertEquals("example.supabase.co", URI.create(url).getHost()); + assertEquals("/storage/v1/s3/bucket/key.jpg", URI.create(url).getPath()); + } } diff --git a/s3/src/test/java/ch/cyberduck/core/s3/S3ProtocolTest.java b/s3/src/test/java/ch/cyberduck/core/s3/S3ProtocolTest.java index cdd63192e4..093316a342 100644 --- a/s3/src/test/java/ch/cyberduck/core/s3/S3ProtocolTest.java +++ b/s3/src/test/java/ch/cyberduck/core/s3/S3ProtocolTest.java @@ -5,6 +5,7 @@ import ch.cyberduck.core.LoginOptions; import ch.cyberduck.core.Profile; import ch.cyberduck.core.ProtocolFactory; import ch.cyberduck.core.Scheme; +import ch.cyberduck.core.TemporaryAccessTokens; import ch.cyberduck.core.TestProtocol; import ch.cyberduck.core.serializer.impl.dd.ProfilePlistReader; @@ -88,5 +89,11 @@ public class S3ProtocolTest { assertFalse(new Credentials("user", "").validate(new S3Protocol(), new LoginOptions(new S3Protocol()))); assertFalse(new Credentials("user", " ").validate(new S3Protocol(), new LoginOptions(new S3Protocol()))); assertTrue(new Credentials("user", "key").validate(new S3Protocol(), new LoginOptions(new S3Protocol()))); + assertFalse(new Credentials("alias").setTokens(new TemporaryAccessTokens("a", "b")) + .validate(new S3Protocol(), new LoginOptions(new S3Protocol()))); + assertTrue(new Credentials("alias").setTokens(new TemporaryAccessTokens("a", "b")) + .validate(new S3Protocol(), new LoginOptions(new S3Protocol()).password(false).token(true))); + assertTrue(new Credentials("alias").setTokens(new TemporaryAccessTokens("a", "b", "c")) + .validate(new S3Protocol(), new LoginOptions(new S3Protocol()).password(false).token(true))); } } diff --git a/s3/src/test/java/ch/cyberduck/core/s3/S3PublicUrlProviderTest.java b/s3/src/test/java/ch/cyberduck/core/s3/S3PublicUrlProviderTest.java index 02e99e4e9b..39da18e61b 100644 --- a/s3/src/test/java/ch/cyberduck/core/s3/S3PublicUrlProviderTest.java +++ b/s3/src/test/java/ch/cyberduck/core/s3/S3PublicUrlProviderTest.java @@ -41,7 +41,7 @@ public class S3PublicUrlProviderTest extends AbstractS3Test { final Path bucket = new Path("test-eu-central-1-cyberduck", EnumSet.of(Path.Type.directory, Path.Type.volume)); final Path test = new Path(bucket, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)); final S3AccessControlListFeature acl = new S3AccessControlListFeature(session); - new S3TouchFeature(session, acl).touch(test, new TransferStatus()); + new S3TouchFeature(session, acl).touch(new S3WriteFeature(session, new S3AccessControlListFeature(session)), test, new TransferStatus()); final S3PublicUrlProvider provider = new S3PublicUrlProvider(session, acl); assertFalse(provider.isSupported(bucket, Share.Type.download)); assertTrue(provider.isSupported(test, Share.Type.download)); diff --git a/s3/src/test/java/ch/cyberduck/core/s3/S3ReadFeatureTest.java b/s3/src/test/java/ch/cyberduck/core/s3/S3ReadFeatureTest.java index b221c8d3b7..4f403222c4 100644 --- a/s3/src/test/java/ch/cyberduck/core/s3/S3ReadFeatureTest.java +++ b/s3/src/test/java/ch/cyberduck/core/s3/S3ReadFeatureTest.java @@ -45,7 +45,7 @@ public class S3ReadFeatureTest extends AbstractS3Test { public void testReadRange() throws Exception { final Path container = new Path("test-eu-central-1-cyberduck", EnumSet.of(Path.Type.directory, Path.Type.volume)); final Path test = new Path(container, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)); - new S3TouchFeature(session, new S3AccessControlListFeature(session)).touch(test, new TransferStatus()); + new S3TouchFeature(session, new S3AccessControlListFeature(session)).touch(new S3WriteFeature(session, new S3AccessControlListFeature(session)), test, new TransferStatus()); final byte[] content = RandomUtils.nextBytes(1000); final TransferStatus status = new TransferStatus().setLength(content.length); status.setChecksum(new SHA256ChecksumCompute().compute(new ByteArrayInputStream(content), status)); @@ -69,7 +69,7 @@ public class S3ReadFeatureTest extends AbstractS3Test { public void testReadEmoji() throws Exception { final Path container = new Path("test-eu-central-1-cyberduck", EnumSet.of(Path.Type.directory, Path.Type.volume)); final Path test = new S3TouchFeature(session, new S3AccessControlListFeature(session)).touch( - new Path(container, String.format("%s-\uD83D\uDE80", new AlphanumericRandomStringService().random()), EnumSet.of(Path.Type.file)), new TransferStatus()); + new S3WriteFeature(session, new S3AccessControlListFeature(session)), new Path(container, String.format("%s-\uD83D\uDE80", new AlphanumericRandomStringService().random()), EnumSet.of(Path.Type.file)), new TransferStatus()); assertTrue(new S3ListService(session, new S3AccessControlListFeature(session)).list(container, new DisabledListProgressListener()).contains(test)); final InputStream in = new S3ReadFeature(session).read(test, new TransferStatus().setLength(0L), new DisabledConnectionCallback()); in.close(); @@ -80,7 +80,7 @@ public class S3ReadFeatureTest extends AbstractS3Test { public void testReadRangeUnknownLength() throws Exception { final Path container = new Path("test-eu-central-1-cyberduck", EnumSet.of(Path.Type.directory, Path.Type.volume)); final Path test = new Path(container, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)); - new S3TouchFeature(session, new S3AccessControlListFeature(session)).touch(test, new TransferStatus()); + new S3TouchFeature(session, new S3AccessControlListFeature(session)).touch(new S3WriteFeature(session, new S3AccessControlListFeature(session)), test, new TransferStatus()); final byte[] content = RandomUtils.nextBytes(1000); final TransferStatus status = new TransferStatus().setLength(content.length); status.setChecksum(new SHA256ChecksumCompute().compute(new ByteArrayInputStream(content), status)); @@ -160,7 +160,7 @@ public class S3ReadFeatureTest extends AbstractS3Test { final CountingInputStream in = new CountingInputStream(new S3ReadFeature(cloudfront).read( new Path(file.getName(), EnumSet.of(Path.Type.file)), status, new DisabledConnectionCallback())); final BytecountStreamListener count = new BytecountStreamListener(); - new StreamCopier(status, status).withListener(count).transfer(in, NullOutputStream.NULL_OUTPUT_STREAM); + new StreamCopier(status, status).withListener(count).transfer(in, NullOutputStream.INSTANCE); assertEquals(content.length, count.getRecv()); new S3DefaultDeleteFeature(session, new S3AccessControlListFeature(session)).delete(Collections.singletonList(file), new DisabledLoginCallback(), new Delete.DisabledCallback()); } @@ -177,7 +177,7 @@ public class S3ReadFeatureTest extends AbstractS3Test { final CountingInputStream in = new CountingInputStream(new S3ReadFeature(virtualhost).read( new Path(file.getName(), EnumSet.of(Path.Type.file)), status, new DisabledConnectionCallback())); final BytecountStreamListener count = new BytecountStreamListener(); - new StreamCopier(status, status).withListener(count).transfer(in, NullOutputStream.NULL_OUTPUT_STREAM); + new StreamCopier(status, status).withListener(count).transfer(in, NullOutputStream.INSTANCE); assertEquals(content.length, count.getRecv()); new S3DefaultDeleteFeature(virtualhost, new S3AccessControlListFeature(virtualhost)).delete(Collections.singletonList(file), new DisabledLoginCallback(), new Delete.DisabledCallback()); } diff --git a/s3/src/test/java/ch/cyberduck/core/s3/S3SearchFeatureTest.java b/s3/src/test/java/ch/cyberduck/core/s3/S3SearchFeatureTest.java index c4ff09bdd6..5424a4f350 100644 --- a/s3/src/test/java/ch/cyberduck/core/s3/S3SearchFeatureTest.java +++ b/s3/src/test/java/ch/cyberduck/core/s3/S3SearchFeatureTest.java @@ -21,6 +21,7 @@ import ch.cyberduck.core.DisabledListProgressListener; import ch.cyberduck.core.DisabledLoginCallback; import ch.cyberduck.core.Path; import ch.cyberduck.core.SimplePathPredicate; +import ch.cyberduck.core.exception.AccessDeniedException; import ch.cyberduck.core.features.Delete; import ch.cyberduck.core.features.Home; import ch.cyberduck.core.features.Touch; @@ -47,12 +48,12 @@ public class S3SearchFeatureTest extends AbstractS3Test { final Path root = Home.root(); final Path bucket = new Path(root, "test-eu-central-1-cyberduck", EnumSet.of(Path.Type.directory, Path.Type.volume)); final S3AccessControlListFeature acl = new S3AccessControlListFeature(session); - final Path file = new S3TouchFeature(session, acl).touch(new Path(bucket, name, EnumSet.of(Path.Type.file)), new TransferStatus()); + final Path file = new S3TouchFeature(session, acl).touch(new S3WriteFeature(session, new S3AccessControlListFeature(session)), new Path(bucket, name, EnumSet.of(Path.Type.file)), new TransferStatus()); final S3SearchFeature feature = new S3SearchFeature(session, acl); assertNotNull(feature.search(bucket, new SearchFilter(name), new DisabledListProgressListener()).find(new SimplePathPredicate(file))); assertNotNull(feature.search(bucket, new SearchFilter(StringUtils.substring(name, 2)), new DisabledListProgressListener()).find(new SimplePathPredicate(file))); assertNotNull(feature.search(bucket, new SearchFilter(StringUtils.substring(name, 0, name.length() - 2)), new DisabledListProgressListener()).find(new SimplePathPredicate(file))); - assertNotNull(feature.search(root, new SearchFilter(name), new DisabledListProgressListener()).find(new SimplePathPredicate(file))); + assertThrows(AccessDeniedException.class, () -> feature.search(root, new SearchFilter(name), new DisabledListProgressListener()).find(new SimplePathPredicate(file))); final Path subdir = new Path(bucket, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory)); assertNull(feature.search(subdir, new SearchFilter(name), new DisabledListProgressListener()).find(new SimplePathPredicate(file))); new S3DefaultDeleteFeature(session, acl).delete(Collections.singletonList(file), new DisabledLoginCallback(), new Delete.DisabledCallback()); @@ -64,7 +65,7 @@ public class S3SearchFeatureTest extends AbstractS3Test { final S3AccessControlListFeature acl = new S3AccessControlListFeature(session); final String name = new AlphanumericRandomStringService().random(); final Path file = new Path(bucket, name, EnumSet.of(Path.Type.file)); - session.getFeature(Touch.class).touch(file, new TransferStatus()); + session.getFeature(Touch.class).touch(new S3WriteFeature(session, new S3AccessControlListFeature(session)), file, new TransferStatus()); final S3SearchFeature feature = new S3SearchFeature(session, acl); assertTrue(feature.search(bucket, new SearchFilter(name), new DisabledListProgressListener()).contains(file)); assertTrue(feature.search(bucket, new SearchFilter(StringUtils.upperCase(name)), new DisabledListProgressListener()).contains(file)); @@ -76,10 +77,10 @@ public class S3SearchFeatureTest extends AbstractS3Test { assertTrue(result.contains(file)); } assertFalse(feature.search(new Path(bucket, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory)), new SearchFilter(name), new DisabledListProgressListener()).contains(file)); - final Path subdir = new S3DirectoryFeature(session, new S3WriteFeature(session, acl), acl).mkdir(new Path(bucket, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory)), new TransferStatus()); + final Path subdir = new S3DirectoryFeature(session, acl).mkdir(new S3WriteFeature(session, new S3AccessControlListFeature(session)), new Path(bucket, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory)), new TransferStatus()); assertTrue(feature.search(bucket, new SearchFilter(new AlphanumericRandomStringService().random()), new DisabledListProgressListener()).isEmpty()); assertFalse(feature.search(subdir, new SearchFilter(name), new DisabledListProgressListener()).contains(file)); - final Path filesubdir = new S3TouchFeature(session, acl).touch(new Path(subdir, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)), new TransferStatus()); + final Path filesubdir = new S3TouchFeature(session, acl).touch(new S3WriteFeature(session, new S3AccessControlListFeature(session)), new Path(subdir, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)), new TransferStatus()); { final AttributedList result = feature.search(bucket, new SearchFilter(filesubdir.getName()), new DisabledListProgressListener()); assertNotNull(result.find(new SimplePathPredicate(filesubdir))); diff --git a/s3/src/test/java/ch/cyberduck/core/s3/S3SessionTest.java b/s3/src/test/java/ch/cyberduck/core/s3/S3SessionTest.java index bb4d18d1ab..094114c461 100644 --- a/s3/src/test/java/ch/cyberduck/core/s3/S3SessionTest.java +++ b/s3/src/test/java/ch/cyberduck/core/s3/S3SessionTest.java @@ -13,11 +13,9 @@ import ch.cyberduck.core.LoginConnectionService; import ch.cyberduck.core.Profile; import ch.cyberduck.core.ProtocolFactory; import ch.cyberduck.core.TemporaryAccessTokens; -import ch.cyberduck.core.TranscriptListener; import ch.cyberduck.core.cdn.DistributionConfiguration; import ch.cyberduck.core.exception.BackgroundException; import ch.cyberduck.core.exception.ExpiredTokenException; -import ch.cyberduck.core.exception.LoginFailureException; import ch.cyberduck.core.features.AclPermission; import ch.cyberduck.core.features.Copy; import ch.cyberduck.core.features.Delete; @@ -115,7 +113,7 @@ public class S3SessionTest extends AbstractS3Test { public void testConnectSessionTokenStatic() throws Exception { final S3Protocol protocol = new S3Protocol(); final Host host = new Host(protocol, protocol.getDefaultHostname(), new Credentials() - .withTokens(new TemporaryAccessTokens( + .setTokens(new TemporaryAccessTokens( "ASIA5RMYTHDIR37CTCXI", "TsnhChH4FlBt7hql2KnzrwNizmktJnO8YzDQwFqx", "FQoDYXdzEN3//////////wEaDLAz85HLZTQ7zu6/OSKrAfwLewUMHKaswh5sXv50BgMwbeKfCoMATjagvM+KV9++z0I6rItmMectuYoEGCOcnWHKZxtvpZAGcjlvgEDPw1KRYu16riUnd2Yo3doskqAoH0dlL2nH0eoj0d81H5e6IjdlGCm1E3K3zQPFLfMbvn1tdDQR1HV8o9eslmxo54hWMY2M14EpZhcXQMlns0mfYLYHLEVvgpz/8xYjR0yKDxJlXSATEpXtowHtqSi8tL7aBQ==", @@ -252,15 +250,6 @@ public class S3SessionTest extends AbstractS3Test { session.close(); } - @Test(expected = LoginFailureException.class) - public void testConnectCn_North_1() throws Exception { - final Host host = new Host(new S3Protocol(), "s3.cn-north-1.amazonaws.com.cn", new Credentials("AWS-QWEZUKJHGVCVBJHG", "uztfjkjnbvcf")); - final S3Session session = new S3Session(host); - session.open(new DisabledProxyFinder(), new DisabledHostKeyCallback(), new DisabledLoginCallback(), new DisabledCancelCallback()); - session.login(new DisabledLoginCallback(), new DisabledCancelCallback()); - session.close(); - } - @Test public void testAwsHostnames() { assertFalse(S3Session.isAwsHostname("play.min.io")); diff --git a/s3/src/test/java/ch/cyberduck/core/s3/S3SingleUploadServiceTest.java b/s3/src/test/java/ch/cyberduck/core/s3/S3SingleUploadServiceTest.java index 1923d5652d..47ddc2d567 100644 --- a/s3/src/test/java/ch/cyberduck/core/s3/S3SingleUploadServiceTest.java +++ b/s3/src/test/java/ch/cyberduck/core/s3/S3SingleUploadServiceTest.java @@ -50,14 +50,14 @@ public class S3SingleUploadServiceTest extends AbstractS3Test { public void testDecorate() throws Exception { final NullInputStream n = new NullInputStream(1L); final S3Session session = new S3Session(new Host(new S3Protocol())); - assertSame(NullInputStream.class, new S3SingleUploadService(session, - new S3WriteFeature(session, new S3AccessControlListFeature(session))).decorate(n, null).getClass()); + assertSame(NullInputStream.class, new S3SingleUploadService(session + ).decorate(n, null).getClass()); } @Test public void testUpload() throws Exception { final S3AccessControlListFeature acl = new S3AccessControlListFeature(session); - final S3SingleUploadService service = new S3SingleUploadService(session, new S3WriteFeature(session, acl)); + final S3SingleUploadService service = new S3SingleUploadService(session); final Path container = new Path("test-eu-central-1-cyberduck", EnumSet.of(Path.Type.directory, Path.Type.volume)); final String name = UUID.randomUUID() + ".txt"; final Path test = new Path(container, name, EnumSet.of(Path.Type.file)); @@ -69,7 +69,7 @@ public class S3SingleUploadServiceTest extends AbstractS3Test { final TransferStatus status = new TransferStatus(); status.setLength(random.length); status.setMime("text/plain"); - service.upload(test, local, new BandwidthThrottle(BandwidthThrottle.UNLIMITED), + service.upload(new S3WriteFeature(session, acl), test, local, new BandwidthThrottle(BandwidthThrottle.UNLIMITED), new DisabledProgressListener(), new DisabledStreamListener(), status, new DisabledLoginCallback()); assertTrue(new S3FindFeature(session, acl).find(test)); final PathAttributes attr = new S3AttributesFinderFeature(session, acl).find(test); @@ -83,7 +83,7 @@ public class S3SingleUploadServiceTest extends AbstractS3Test { @Test public void testUploadSSE() throws Exception { final S3AccessControlListFeature acl = new S3AccessControlListFeature(session); - final S3SingleUploadService service = new S3SingleUploadService(session, new S3WriteFeature(session, acl)); + final S3SingleUploadService service = new S3SingleUploadService(session); final Path container = new Path("test-eu-central-1-cyberduck", EnumSet.of(Path.Type.directory, Path.Type.volume)); final String name = UUID.randomUUID() + ".txt"; final Path test = new Path(container, name, EnumSet.of(Path.Type.file)); @@ -95,7 +95,7 @@ public class S3SingleUploadServiceTest extends AbstractS3Test { final TransferStatus status = new TransferStatus(); status.setLength(random.length); status.setEncryption(KMSEncryptionFeature.SSE_KMS_DEFAULT); - service.upload(test, local, new BandwidthThrottle(BandwidthThrottle.UNLIMITED), + service.upload(new S3WriteFeature(session, acl), test, local, new BandwidthThrottle(BandwidthThrottle.UNLIMITED), new DisabledProgressListener(), new DisabledStreamListener(), status, new DisabledLoginCallback()); assertTrue(new S3FindFeature(session, acl).find(test)); final PathAttributes attributes = new S3AttributesFinderFeature(session, acl).find(test); @@ -107,7 +107,7 @@ public class S3SingleUploadServiceTest extends AbstractS3Test { @Test public void testUploadWithSHA256Checksum() throws Exception { final S3AccessControlListFeature acl = new S3AccessControlListFeature(session); - final S3SingleUploadService service = new S3SingleUploadService(session, new S3WriteFeature(session, acl)); + final S3SingleUploadService service = new S3SingleUploadService(session); final Path container = new Path("test-eu-central-1-cyberduck", EnumSet.of(Path.Type.directory, Path.Type.volume)); final String name = UUID.randomUUID() + ".txt"; final Path test = new Path(container, name, EnumSet.of(Path.Type.file)); @@ -118,7 +118,7 @@ public class S3SingleUploadServiceTest extends AbstractS3Test { out.close(); final TransferStatus status = new TransferStatus(); status.setLength(random.length); - service.upload(test, local, new BandwidthThrottle(BandwidthThrottle.UNLIMITED), + service.upload(new S3WriteFeature(session, acl), test, local, new BandwidthThrottle(BandwidthThrottle.UNLIMITED), new DisabledProgressListener(), new DisabledStreamListener(), status, new DisabledLoginCallback()); assertTrue(new S3FindFeature(session, acl).find(test)); final PathAttributes attributes = new S3AttributesFinderFeature(session, acl).find(test); @@ -129,13 +129,13 @@ public class S3SingleUploadServiceTest extends AbstractS3Test { @Test(expected = NotfoundException.class) public void testUploadInvalidContainer() throws Exception { - final S3SingleUploadService m = new S3SingleUploadService(session, new S3WriteFeature(session, new S3AccessControlListFeature(session))); + final S3SingleUploadService m = new S3SingleUploadService(session); final Path container = new Path("nosuchcontainer.cyberduck.ch", EnumSet.of(Path.Type.directory, Path.Type.volume)); final Path test = new Path(container, UUID.randomUUID().toString(), EnumSet.of(Path.Type.file)); final Local local = new Local(System.getProperty("java.io.tmpdir"), UUID.randomUUID().toString()); LocalTouchFactory.get().touch(local); final TransferStatus status = new TransferStatus(); - m.upload(test, local, new BandwidthThrottle(BandwidthThrottle.UNLIMITED), new DisabledProgressListener(), new DisabledStreamListener(), + m.upload(new S3WriteFeature(session, new S3AccessControlListFeature(session)), test, local, new BandwidthThrottle(BandwidthThrottle.UNLIMITED), new DisabledProgressListener(), new DisabledStreamListener(), status, new DisabledLoginCallback()); } } diff --git a/s3/src/test/java/ch/cyberduck/core/s3/S3StorageClassFeatureTest.java b/s3/src/test/java/ch/cyberduck/core/s3/S3StorageClassFeatureTest.java index 48393414e0..23bc721121 100644 --- a/s3/src/test/java/ch/cyberduck/core/s3/S3StorageClassFeatureTest.java +++ b/s3/src/test/java/ch/cyberduck/core/s3/S3StorageClassFeatureTest.java @@ -24,6 +24,7 @@ import ch.cyberduck.core.Path; import ch.cyberduck.core.exception.AccessDeniedException; import ch.cyberduck.core.exception.NotfoundException; import ch.cyberduck.core.features.Delete; +import ch.cyberduck.core.features.Home; import ch.cyberduck.core.transfer.TransferStatus; import ch.cyberduck.test.IntegrationTest; @@ -44,10 +45,10 @@ public class S3StorageClassFeatureTest extends AbstractS3Test { @Test public void testGetClasses() { assertArrayEquals(Collections.singletonList(S3Object.STORAGE_CLASS_STANDARD).toArray(), - new S3StorageClassFeature(new S3Session(new Host(new S3Protocol())), new S3AccessControlListFeature(session)).getClasses().toArray()); + new S3StorageClassFeature(new S3Session(new Host(new S3Protocol())), new S3AccessControlListFeature(session)).getClasses(Home.root()).toArray()); assertArrayEquals(Arrays.asList(S3Object.STORAGE_CLASS_STANDARD, "INTELLIGENT_TIERING", S3Object.STORAGE_CLASS_INFREQUENT_ACCESS, "ONEZONE_IA", S3Object.STORAGE_CLASS_REDUCED_REDUNDANCY, S3Object.STORAGE_CLASS_GLACIER, "GLACIER_IR", "DEEP_ARCHIVE").toArray(), - new S3StorageClassFeature(session, new S3AccessControlListFeature(session)).getClasses().toArray()); + new S3StorageClassFeature(session, new S3AccessControlListFeature(session)).getClasses(Home.root()).toArray()); } @Test(expected = NotfoundException.class) @@ -63,7 +64,7 @@ public class S3StorageClassFeatureTest extends AbstractS3Test { final S3StorageClassFeature feature = new S3StorageClassFeature(session, new S3AccessControlListFeature(session)); final Path container = new Path("test-eu-central-1-cyberduck", EnumSet.of(Path.Type.volume, Path.Type.directory)); assertNull(S3Object.STORAGE_CLASS_STANDARD, feature.getClass(container)); - final Path test = new S3TouchFeature(session, new S3AccessControlListFeature(session)).touch(new Path(container, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)), new TransferStatus()); + final Path test = new S3TouchFeature(session, new S3AccessControlListFeature(session)).touch(new S3WriteFeature(session, new S3AccessControlListFeature(session)), new Path(container, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)), new TransferStatus()); assertEquals(S3Object.STORAGE_CLASS_STANDARD, feature.getClass(test)); feature.setClass(test, S3Object.STORAGE_CLASS_INFREQUENT_ACCESS); assertEquals(S3Object.STORAGE_CLASS_INFREQUENT_ACCESS, feature.getClass(test)); @@ -78,7 +79,7 @@ public class S3StorageClassFeatureTest extends AbstractS3Test { final Path container = new Path("versioning-test-eu-central-1-cyberduck", EnumSet.of(Path.Type.volume, Path.Type.directory)); final TransferStatus status = new TransferStatus(); status.setStorageClass("GLACIER"); - final Path test = new S3TouchFeature(session, new S3AccessControlListFeature(session)).touch(new Path(container, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)), status); + final Path test = new S3TouchFeature(session, new S3AccessControlListFeature(session)).touch(new S3WriteFeature(session, new S3AccessControlListFeature(session)), new Path(container, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)), status); final S3StorageClassFeature feature = new S3StorageClassFeature(session, new S3AccessControlListFeature(session)); assertEquals(S3Object.STORAGE_CLASS_GLACIER, feature.getClass(test)); try { @@ -93,8 +94,8 @@ public class S3StorageClassFeatureTest extends AbstractS3Test { public void testSetClassPlaceholder() throws Exception { final Path container = new Path("test-eu-central-1-cyberduck", EnumSet.of(Path.Type.volume, Path.Type.directory)); final S3AccessControlListFeature acl = new S3AccessControlListFeature(session); - final Path test = new S3DirectoryFeature(session, new S3WriteFeature(session, acl), acl).mkdir( - new Path(container, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory)), new TransferStatus()); + final Path test = new S3DirectoryFeature(session, acl).mkdir( + new S3WriteFeature(session, new S3AccessControlListFeature(session)), new Path(container, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory)), new TransferStatus()); final S3StorageClassFeature feature = new S3StorageClassFeature(session, acl); assertEquals(S3Object.STORAGE_CLASS_STANDARD, feature.getClass(test)); feature.setClass(test, S3Object.STORAGE_CLASS_INFREQUENT_ACCESS); diff --git a/s3/src/test/java/ch/cyberduck/core/s3/S3ThresholdUploadServiceTest.java b/s3/src/test/java/ch/cyberduck/core/s3/S3ThresholdUploadServiceTest.java index ccd5736983..8a0981c206 100644 --- a/s3/src/test/java/ch/cyberduck/core/s3/S3ThresholdUploadServiceTest.java +++ b/s3/src/test/java/ch/cyberduck/core/s3/S3ThresholdUploadServiceTest.java @@ -54,7 +54,7 @@ public class S3ThresholdUploadServiceTest extends AbstractS3Test { final Path test = new Path(container, UUID.randomUUID().toString(), EnumSet.of(Path.Type.file)); final Local local = new NullLocal(System.getProperty("java.io.tmpdir"), UUID.randomUUID().toString()); final TransferStatus status = new TransferStatus().setLength(5 * 1024L); - m.upload(test, local, new BandwidthThrottle(BandwidthThrottle.UNLIMITED), new DisabledProgressListener(), new DisabledStreamListener(), status, null); + m.upload(new S3WriteFeature(session, new S3AccessControlListFeature(session)), test, local, new BandwidthThrottle(BandwidthThrottle.UNLIMITED), new DisabledProgressListener(), new DisabledStreamListener(), status, null); } @Test @@ -70,7 +70,7 @@ public class S3ThresholdUploadServiceTest extends AbstractS3Test { status.setLength(random.length); status.setStorageClass(S3Object.STORAGE_CLASS_REDUCED_REDUNDANCY); final BytecountStreamListener count = new BytecountStreamListener(); - service.upload(test, local, new BandwidthThrottle(BandwidthThrottle.UNLIMITED), + service.upload(new S3WriteFeature(session, new S3AccessControlListFeature(session)), test, local, new BandwidthThrottle(BandwidthThrottle.UNLIMITED), new DisabledProgressListener(), count, status, new DisabledLoginCallback()); assertEquals(random.length, count.getSent(), 0L); assertTrue(status.isComplete()); @@ -95,7 +95,7 @@ public class S3ThresholdUploadServiceTest extends AbstractS3Test { status.setLength(random.length); status.setStorageClass(S3Object.STORAGE_CLASS_REDUCED_REDUNDANCY); final BytecountStreamListener count = new BytecountStreamListener(); - service.upload(test, local, new BandwidthThrottle(BandwidthThrottle.UNLIMITED), + service.upload(new S3WriteFeature(session, new S3AccessControlListFeature(session)), test, local, new BandwidthThrottle(BandwidthThrottle.UNLIMITED), new DisabledProgressListener(), count, status, new DisabledLoginCallback()); assertEquals(random.length, count.getSent()); assertTrue(status.isComplete()); @@ -121,7 +121,7 @@ public class S3ThresholdUploadServiceTest extends AbstractS3Test { status.setLength(random.length); status.setMime("text/plain"); final BytecountStreamListener count = new BytecountStreamListener(); - service.upload(test, local, new BandwidthThrottle(BandwidthThrottle.UNLIMITED), + service.upload(new S3WriteFeature(session, new S3AccessControlListFeature(session)), test, local, new BandwidthThrottle(BandwidthThrottle.UNLIMITED), new DisabledProgressListener(), count, status, new DisabledLoginCallback()); assertEquals(random.length, count.getSent()); assertTrue(status.isComplete()); diff --git a/s3/src/test/java/ch/cyberduck/core/s3/S3TimestampFeatureTest.java b/s3/src/test/java/ch/cyberduck/core/s3/S3TimestampFeatureTest.java index 7cff60d640..9e9c189cbd 100644 --- a/s3/src/test/java/ch/cyberduck/core/s3/S3TimestampFeatureTest.java +++ b/s3/src/test/java/ch/cyberduck/core/s3/S3TimestampFeatureTest.java @@ -44,7 +44,7 @@ public class S3TimestampFeatureTest extends AbstractS3Test { final Path bucket = new Path("versioning-test-eu-central-1-cyberduck", EnumSet.of(Path.Type.volume, Path.Type.directory)); final TransferStatus status = new TransferStatus(); final S3AccessControlListFeature acl = new S3AccessControlListFeature(session); - final Path test = new S3TouchFeature(session, acl).touch(new Path(bucket, + final Path test = new S3TouchFeature(session, acl).touch(new S3WriteFeature(session, new S3AccessControlListFeature(session)), new Path(bucket, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)), status.setCreated(1695159781972L).setModified(1530305150672L)); final String versionId = test.attributes().getVersionId(); diff --git a/s3/src/test/java/ch/cyberduck/core/s3/S3TouchFeatureTest.java b/s3/src/test/java/ch/cyberduck/core/s3/S3TouchFeatureTest.java index 13a47e1cbd..59d9adc919 100644 --- a/s3/src/test/java/ch/cyberduck/core/s3/S3TouchFeatureTest.java +++ b/s3/src/test/java/ch/cyberduck/core/s3/S3TouchFeatureTest.java @@ -1,6 +1,7 @@ package ch.cyberduck.core.s3; import ch.cyberduck.core.AsciiRandomStringService; +import ch.cyberduck.core.DefaultPathAttributes; import ch.cyberduck.core.DisabledLoginCallback; import ch.cyberduck.core.Path; import ch.cyberduck.core.PathAttributes; @@ -42,7 +43,7 @@ public class S3TouchFeatureTest extends AbstractS3Test { final String filename = new AsciiRandomStringService().random(); assertFalse(feature.isSupported(Home.root(), filename)); assertTrue(feature.isSupported(container, filename)); - final Path test = feature.touch(new Path(container, filename, EnumSet.of(Path.Type.file)), new TransferStatus()); + final Path test = feature.touch(new S3WriteFeature(session, new S3AccessControlListFeature(session)), new Path(container, filename, EnumSet.of(Path.Type.file)), new TransferStatus()); assertNull(test.attributes().getVersionId()); assertTrue(new S3FindFeature(session, acl).find(test)); assertEquals(test.attributes(), new S3AttributesFinderFeature(session, acl).find(test)); @@ -53,11 +54,11 @@ public class S3TouchFeatureTest extends AbstractS3Test { @Test public void testTouchVirtualHost() throws Exception { - final S3AccessControlListFeature acl = new S3AccessControlListFeature(session); + final S3AccessControlListFeature acl = new S3AccessControlListFeature(virtualhost); final S3TouchFeature feature = new S3TouchFeature(virtualhost, acl); final String filename = new AsciiRandomStringService().random(); assertTrue(feature.isSupported(Home.root(), filename)); - final Path test = feature.touch(new Path(filename, EnumSet.of(Path.Type.file)), new TransferStatus()); + final Path test = feature.touch(new S3WriteFeature(virtualhost, new S3AccessControlListFeature(virtualhost)), new Path(filename, EnumSet.of(Path.Type.file)), new TransferStatus()); assertNull(test.attributes().getVersionId()); assertTrue(new S3FindFeature(virtualhost, acl).find(test)); assertEquals(test.attributes(), new S3AttributesFinderFeature(virtualhost, acl).find(test)); @@ -71,7 +72,7 @@ public class S3TouchFeatureTest extends AbstractS3Test { final Path container = new Path("test-eu-central-1-cyberduck", EnumSet.of(Path.Type.volume, Path.Type.directory)); final S3AccessControlListFeature acl = new S3AccessControlListFeature(session); final Path test = new S3TouchFeature(session, acl).touch( - new Path(container, String.format("%s\n-\r", new AsciiRandomStringService().random()), EnumSet.of(Path.Type.file)), new TransferStatus()); + new S3WriteFeature(session, new S3AccessControlListFeature(session)), new Path(container, String.format("%s\n-\r", new AsciiRandomStringService().random()), EnumSet.of(Path.Type.file)), new TransferStatus()); assertNull(test.attributes().getVersionId()); assertTrue(new S3FindFeature(session, acl).find(test)); assertEquals(test.attributes(), new DefaultAttributesFinderFeature(session).find(test)); @@ -85,7 +86,7 @@ public class S3TouchFeatureTest extends AbstractS3Test { final Path container = new Path("test-eu-central-1-cyberduck", EnumSet.of(Path.Type.volume, Path.Type.directory)); final S3AccessControlListFeature acl = new S3AccessControlListFeature(session); final Path test = new S3TouchFeature(session, acl).touch( - new Path(container, String.format("%s-+*~@([", new AsciiRandomStringService().random()), EnumSet.of(Path.Type.file)), new TransferStatus()); + new S3WriteFeature(session, new S3AccessControlListFeature(session)), new Path(container, String.format("%s-+*~@([", new AsciiRandomStringService().random()), EnumSet.of(Path.Type.file)), new TransferStatus()); assertNull(test.attributes().getVersionId()); assertTrue(new S3FindFeature(session, acl).find(test)); assertEquals(test.attributes(), new S3AttributesFinderFeature(session, acl).find(test)); @@ -98,32 +99,32 @@ public class S3TouchFeatureTest extends AbstractS3Test { final Path container = new Path("versioning-test-eu-central-1-cyberduck", EnumSet.of(Path.Type.directory, Path.Type.volume)); final Path file = new Path(container, new AsciiRandomStringService().random(), EnumSet.of(Path.Type.file)); final S3AccessControlListFeature acl = new S3AccessControlListFeature(session); - final String version1 = new S3TouchFeature(session, acl).touch(file, new TransferStatus()).attributes().getVersionId(); + final String version1 = new S3TouchFeature(session, acl).touch(new S3WriteFeature(session, new S3AccessControlListFeature(session)), file, new TransferStatus()).attributes().getVersionId(); assertNotNull(version1); assertEquals(version1, new S3AttributesFinderFeature(session, acl).find(file).getVersionId()); - final String version2 = new S3TouchFeature(session, acl).touch(file, new TransferStatus()).attributes().getVersionId(); + final String version2 = new S3TouchFeature(session, acl).touch(new S3WriteFeature(session, new S3AccessControlListFeature(session)), file, new TransferStatus()).attributes().getVersionId(); assertNotNull(version2); assertEquals(version2, new S3AttributesFinderFeature(session, acl).find(file).getVersionId()); assertTrue(new S3FindFeature(session, acl).find(file)); assertTrue(new DefaultFindFeature(session).find(file)); assertTrue(new DefaultFindFeature(session).find(new Path(file.getParent(), file.getName(), file.getType(), - new PathAttributes(file.attributes()).setVersionId(version1)))); + new DefaultPathAttributes(file.attributes()).setVersionId(version1)))); assertTrue(new DefaultFindFeature(session).find(new Path(file.getParent(), file.getName(), file.getType(), - new PathAttributes(file.attributes()).setVersionId(version2)))); + new DefaultPathAttributes(file.attributes()).setVersionId(version2)))); assertTrue(new S3FindFeature(session, acl).find(new Path(file.getParent(), file.getName(), file.getType(), - new PathAttributes(file.attributes()).setVersionId(version1)))); + new DefaultPathAttributes(file.attributes()).setVersionId(version1)))); assertTrue(new S3FindFeature(session, acl).find(new Path(file.getParent(), file.getName(), file.getType(), - new PathAttributes(file.attributes()).setVersionId(version2)))); + new DefaultPathAttributes(file.attributes()).setVersionId(version2)))); new S3DefaultDeleteFeature(session, acl).delete(Collections.singletonList(new Path(file).withAttributes(PathAttributes.EMPTY)), new DisabledLoginCallback(), new Delete.DisabledCallback()); // Versioned files are not deleted but with delete marker added assertTrue(new DefaultFindFeature(session).find(new Path(file.getParent(), file.getName(), file.getType(), - new PathAttributes(file.attributes()).setVersionId(version1)))); + new DefaultPathAttributes(file.attributes()).setVersionId(version1)))); assertTrue(new DefaultFindFeature(session).find(new Path(file.getParent(), file.getName(), file.getType(), - new PathAttributes(file.attributes()).setVersionId(version2)))); + new DefaultPathAttributes(file.attributes()).setVersionId(version2)))); assertTrue(new S3FindFeature(session, acl).find(new Path(file.getParent(), file.getName(), file.getType(), - new PathAttributes(file.attributes()).setVersionId(version1)))); + new DefaultPathAttributes(file.attributes()).setVersionId(version1)))); assertTrue(new S3FindFeature(session, acl).find(new Path(file.getParent(), file.getName(), file.getType(), - new PathAttributes(file.attributes()).setVersionId(version2)))); + new DefaultPathAttributes(file.attributes()).setVersionId(version2)))); } @Test(expected = AccessDeniedException.class) @@ -133,7 +134,7 @@ public class S3TouchFeatureTest extends AbstractS3Test { final S3TouchFeature touch = new S3TouchFeature(session, new S3AccessControlListFeature(session)); final TransferStatus status = new TransferStatus(); status.setEncryption(Encryption.Algorithm.NONE); - touch.touch(test, status); + touch.touch(new S3WriteFeature(session, new S3AccessControlListFeature(session)), test, status); } @Test @@ -143,6 +144,6 @@ public class S3TouchFeatureTest extends AbstractS3Test { final S3TouchFeature touch = new S3TouchFeature(session, new S3AccessControlListFeature(session)); final TransferStatus status = new TransferStatus(); status.setEncryption(S3EncryptionFeature.SSE_AES256); - touch.touch(test, status); + touch.touch(new S3WriteFeature(session, new S3AccessControlListFeature(session)), test, status); } } diff --git a/s3/src/test/java/ch/cyberduck/core/s3/S3TransferAccelerationServiceTest.java b/s3/src/test/java/ch/cyberduck/core/s3/S3TransferAccelerationServiceTest.java index b9a44af3e8..1cbd0d1146 100644 --- a/s3/src/test/java/ch/cyberduck/core/s3/S3TransferAccelerationServiceTest.java +++ b/s3/src/test/java/ch/cyberduck/core/s3/S3TransferAccelerationServiceTest.java @@ -56,7 +56,7 @@ public class S3TransferAccelerationServiceTest extends AbstractS3Test { @Override public void log(final Type request, final String message) { switch(request) { - case request: + case requestheader: if("Host: test-eu-central-1-cyberduck.s3-accelerate.dualstack.amazonaws.com".equals(message)) { b.set(true); } diff --git a/s3/src/test/java/ch/cyberduck/core/s3/S3UrlProviderTest.java b/s3/src/test/java/ch/cyberduck/core/s3/S3UrlProviderTest.java index 8e3cf4e8fa..7fb71b218e 100644 --- a/s3/src/test/java/ch/cyberduck/core/s3/S3UrlProviderTest.java +++ b/s3/src/test/java/ch/cyberduck/core/s3/S3UrlProviderTest.java @@ -9,6 +9,7 @@ import ch.cyberduck.core.DisabledLoginCallback; import ch.cyberduck.core.Host; import ch.cyberduck.core.Path; import ch.cyberduck.core.exception.BackgroundException; +import ch.cyberduck.core.features.Home; import ch.cyberduck.core.proxy.DisabledProxyFinder; import ch.cyberduck.test.IntegrationTest; @@ -142,18 +143,22 @@ public class S3UrlProviderTest extends AbstractS3Test { } @Test - public void testToSignedUrl() { + public void testToSignedUrl() throws Exception { final S3UrlProvider provider = new S3UrlProvider(session, Collections.emptyMap()); - assertTrue(provider.toSignedUrl(new Path("/test-eu-west-1-cyberduck/test", EnumSet.of(Path.Type.file)), 30).getUrl().startsWith( - "https://test-eu-west-1-cyberduck.s3.amazonaws.com/test?X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Credential=")); + final Path bucket = new Path("/test-eu-west-1-cyberduck", EnumSet.of(Path.Type.directory, Path.Type.volume)); + new S3ObjectListService(session, new S3AccessControlListFeature(session)).list(bucket); + assertTrue(provider.toSignedUrl(new Path(bucket, "test", EnumSet.of(Path.Type.file)), 30).getUrl().startsWith( + "https://test-eu-west-1-cyberduck.s3.dualstack.eu-west-1.amazonaws.com/test?X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Credential=")); } @Test - public void testToSignedUrlVirtualHost() { + public void testToSignedUrlVirtualHost() throws Exception { final S3UrlProvider provider = new S3UrlProvider(virtualhost, Collections.emptyMap()); - final String url = provider.toSignedUrl(new Path("/t", EnumSet.of(Path.Type.file)), 30).getUrl(); + final Path bucket = Home.root(); + new S3ObjectListService(virtualhost, new S3AccessControlListFeature(virtualhost)).list(bucket); + final String url = provider.toSignedUrl(new Path(bucket, "t", EnumSet.of(Path.Type.file)), 30).getUrl(); assertTrue(url, url.startsWith( - "https://test-eu-central-1-cyberduck.s3.amazonaws.com/t?X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Credential=")); + "https://test-eu-central-1-cyberduck.s3.dualstack.eu-central-1.amazonaws.com/t?X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Credential=")); } @Test diff --git a/s3/src/test/java/ch/cyberduck/core/s3/S3VersionedObjectListServiceTest.java b/s3/src/test/java/ch/cyberduck/core/s3/S3VersionedObjectListServiceTest.java index 18004c7315..da6c22adda 100644 --- a/s3/src/test/java/ch/cyberduck/core/s3/S3VersionedObjectListServiceTest.java +++ b/s3/src/test/java/ch/cyberduck/core/s3/S3VersionedObjectListServiceTest.java @@ -18,6 +18,7 @@ package ch.cyberduck.core.s3; import ch.cyberduck.core.AlphanumericRandomStringService; import ch.cyberduck.core.AsciiRandomStringService; import ch.cyberduck.core.AttributedList; +import ch.cyberduck.core.DefaultPathAttributes; import ch.cyberduck.core.DefaultPathPredicate; import ch.cyberduck.core.DisabledConnectionCallback; import ch.cyberduck.core.DisabledListProgressListener; @@ -28,7 +29,6 @@ import ch.cyberduck.core.Path; import ch.cyberduck.core.PathAttributes; import ch.cyberduck.core.SimplePathPredicate; import ch.cyberduck.core.VersioningConfiguration; -import ch.cyberduck.core.exception.BackgroundException; import ch.cyberduck.core.exception.NotfoundException; import ch.cyberduck.core.features.Delete; import ch.cyberduck.core.features.Home; @@ -61,7 +61,7 @@ public class S3VersionedObjectListServiceTest extends AbstractS3Test { @Test public void testList() throws Exception { final Path bucket = new Path("test-eu-central-1-cyberduck", EnumSet.of(Path.Type.directory, Path.Type.volume)); - final Path file = new S3TouchFeature(session, new S3AccessControlListFeature(session)).touch(new Path(bucket, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)), new TransferStatus()); + final Path file = new S3TouchFeature(session, new S3AccessControlListFeature(session)).touch(new S3WriteFeature(session, new S3AccessControlListFeature(session)), new Path(bucket, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)), new TransferStatus()); file.attributes().setVersionId("null"); final AttributedList list = new S3VersionedObjectListService(session, new S3AccessControlListFeature(session)).list(bucket, new DisabledListProgressListener()); assertNotSame(AttributedList.EMPTY, list); @@ -84,7 +84,7 @@ public class S3VersionedObjectListServiceTest extends AbstractS3Test { public void testDirectory() throws Exception { final Path bucket = new Path("test-eu-central-1-cyberduck", EnumSet.of(Path.Type.directory, Path.Type.volume)); final S3AccessControlListFeature acl = new S3AccessControlListFeature(session); - final Path directory = new S3DirectoryFeature(session, new S3WriteFeature(session, acl), acl).mkdir(new Path(bucket, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory)), new TransferStatus()); + final Path directory = new S3DirectoryFeature(session, acl).mkdir(new S3WriteFeature(session, new S3AccessControlListFeature(session)), new Path(bucket, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory)), new TransferStatus()); final S3VersionedObjectListService feature = new S3VersionedObjectListService(session, acl); assertTrue(feature.list(bucket, new DisabledListProgressListener()).contains(directory)); assertTrue(feature.list(directory, new DisabledListProgressListener()).isEmpty()); @@ -110,7 +110,7 @@ public class S3VersionedObjectListServiceTest extends AbstractS3Test { catch(NotfoundException e) { // Expected } - final Path file = new S3TouchFeature(session, acl).touch(new Path(container, String.format("%s-", name), EnumSet.of(Path.Type.file)), new TransferStatus()); + final Path file = new S3TouchFeature(session, acl).touch(new S3WriteFeature(session, new S3AccessControlListFeature(session)), new Path(container, String.format("%s-", name), EnumSet.of(Path.Type.file)), new TransferStatus()); try { new S3VersionedObjectListService(session, acl).list(new Path(container, name, EnumSet.of(Path.Type.directory)), new DisabledListProgressListener()); fail(); @@ -126,7 +126,7 @@ public class S3VersionedObjectListServiceTest extends AbstractS3Test { final Path container = new Path("versioning-test-eu-central-1-cyberduck", EnumSet.of(Path.Type.volume, Path.Type.directory)); final String name = new AlphanumericRandomStringService().random(); final S3AccessControlListFeature acl = new S3AccessControlListFeature(session); - final Path file = new S3TouchFeature(session, acl).touch(new Path(container, name, EnumSet.of(Path.Type.file)), new TransferStatus()); + final Path file = new S3TouchFeature(session, acl).touch(new S3WriteFeature(session, new S3AccessControlListFeature(session)), new Path(container, name, EnumSet.of(Path.Type.file)), new TransferStatus()); try { new S3VersionedObjectListService(session, acl).list(new Path(container, name, EnumSet.of(Path.Type.directory)), new DisabledListProgressListener()); fail(); @@ -140,10 +140,10 @@ public class S3VersionedObjectListServiceTest extends AbstractS3Test { @Test public void testEnableVersioningExistingFiles() throws Exception { final S3AccessControlListFeature acl = new S3AccessControlListFeature(session); - final Path bucket = new S3DirectoryFeature(session, new S3WriteFeature(session, acl), acl) - .mkdir(new Path(new AsciiRandomStringService().random(), EnumSet.of(Path.Type.directory, Path.Type.volume)), new TransferStatus()); + final Path bucket = new S3DirectoryFeature(session, acl) + .mkdir(new S3WriteFeature(session, new S3AccessControlListFeature(session)), new Path(new AsciiRandomStringService().random(), EnumSet.of(Path.Type.directory, Path.Type.volume)), new TransferStatus()); assertTrue(new S3FindFeature(session, acl).find(bucket)); - final Path file = new S3TouchFeature(session, acl).touch(new Path(bucket, new AsciiRandomStringService().random(), EnumSet.of(Path.Type.file)), new TransferStatus()); + final Path file = new S3TouchFeature(session, acl).touch(new S3WriteFeature(session, new S3AccessControlListFeature(session)), new Path(bucket, new AsciiRandomStringService().random(), EnumSet.of(Path.Type.file)), new TransferStatus()); final S3WriteFeature feature = new S3WriteFeature(session, acl); { final byte[] content = RandomUtils.nextBytes(1024); @@ -192,7 +192,7 @@ public class S3VersionedObjectListServiceTest extends AbstractS3Test { assertEquals(1, versions.size()); assertEquals(versions.get(0), list.get(1)); new S3DefaultDeleteFeature(session, acl).delete(Arrays.asList( - new Path(file).withAttributes(new PathAttributes().setVersionId("null")), + new Path(file).withAttributes(new DefaultPathAttributes().setVersionId("null")), new Path(file).withAttributes(new DefaultAttributesFinderFeature(session).find(file)), bucket), new DisabledLoginCallback(), new Delete.DisabledCallback()); } @@ -202,7 +202,7 @@ public class S3VersionedObjectListServiceTest extends AbstractS3Test { container.attributes().setRegion("us-east-1"); final S3AccessControlListFeature acl = new S3AccessControlListFeature(session); final Path placeholder = new S3TouchFeature(session, acl).touch( - new Path(container, String.format("^<%%%s", new AlphanumericRandomStringService().random()), EnumSet.of(Path.Type.file)), new TransferStatus()); + new S3WriteFeature(session, new S3AccessControlListFeature(session)), new Path(container, String.format("^<%%%s", new AlphanumericRandomStringService().random()), EnumSet.of(Path.Type.file)), new TransferStatus()); assertTrue(new S3VersionedObjectListService(session, acl).list(container, new DisabledListProgressListener()).contains(placeholder)); new S3DefaultDeleteFeature(session, acl).delete(Collections.singletonList(placeholder), new DisabledLoginCallback(), new Delete.DisabledCallback()); } @@ -213,7 +213,7 @@ public class S3VersionedObjectListServiceTest extends AbstractS3Test { container.attributes().setRegion("us-east-1"); final S3AccessControlListFeature acl = new S3AccessControlListFeature(session); final Path placeholder = new S3TouchFeature(session, acl).touch( - new Path(container, String.format("test-\u001F-%s", new AlphanumericRandomStringService().random()), EnumSet.of(Path.Type.file)), new TransferStatus()); + new S3WriteFeature(session, new S3AccessControlListFeature(session)), new Path(container, String.format("test-\u001F-%s", new AlphanumericRandomStringService().random()), EnumSet.of(Path.Type.file)), new TransferStatus()); assertTrue(new S3VersionedObjectListService(session, acl).list(container, new DisabledListProgressListener()).contains(placeholder)); new S3DefaultDeleteFeature(session, acl).delete(Collections.singletonList(placeholder), new DisabledLoginCallback(), new Delete.DisabledCallback()); } @@ -224,7 +224,7 @@ public class S3VersionedObjectListServiceTest extends AbstractS3Test { container.attributes().setRegion("us-east-1"); final S3AccessControlListFeature acl = new S3AccessControlListFeature(session); final Path placeholder = new S3TouchFeature(session, acl).touch( - new Path(container, String.format("test+%s", new AlphanumericRandomStringService().random()), EnumSet.of(Path.Type.file)), new TransferStatus()); + new S3WriteFeature(session, new S3AccessControlListFeature(session)), new Path(container, String.format("test+%s", new AlphanumericRandomStringService().random()), EnumSet.of(Path.Type.file)), new TransferStatus()); assertTrue(new S3VersionedObjectListService(session, acl).list(container, new DisabledListProgressListener()).contains(placeholder)); new S3DefaultDeleteFeature(session, acl).delete(Collections.singletonList(placeholder), new DisabledLoginCallback(), new Delete.DisabledCallback()); } @@ -234,9 +234,9 @@ public class S3VersionedObjectListServiceTest extends AbstractS3Test { final Path bucket = new Path("versioning-test-eu-central-1-cyberduck", EnumSet.of(Path.Type.directory, Path.Type.volume)); bucket.attributes().setRegion("us-east-1"); final S3AccessControlListFeature acl = new S3AccessControlListFeature(session); - final Path directory = new S3DirectoryFeature(session, new S3WriteFeature(session, acl), acl).mkdir(new Path(bucket, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory)), new TransferStatus().setLength(0L)); - final Path placeholder = new S3DirectoryFeature(session, new S3WriteFeature(session, acl), acl).mkdir( - new Path(directory, String.format("test+%s", new AlphanumericRandomStringService().random()), EnumSet.of(Path.Type.directory)), new TransferStatus()); + final Path directory = new S3DirectoryFeature(session, acl).mkdir(new S3WriteFeature(session, new S3AccessControlListFeature(session)), new Path(bucket, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory)), new TransferStatus().setLength(0L)); + final Path placeholder = new S3DirectoryFeature(session, acl).mkdir( + new S3WriteFeature(session, new S3AccessControlListFeature(session)), new Path(directory, String.format("test+%s", new AlphanumericRandomStringService().random()), EnumSet.of(Path.Type.directory)), new TransferStatus()); assertTrue(new S3VersionedObjectListService(session, acl).list(directory, new DisabledListProgressListener()).contains(placeholder)); assertTrue(new S3VersionedObjectListService(session, acl).list(placeholder, new DisabledListProgressListener()).isEmpty()); new S3DefaultDeleteFeature(session, acl).delete(Collections.singletonList(placeholder), new DisabledLoginCallback(), new Delete.DisabledCallback()); @@ -255,12 +255,12 @@ public class S3VersionedObjectListServiceTest extends AbstractS3Test { public void testDirectoyPlaceholderNoChildren() throws Exception { final Path bucket = new Path("versioning-test-eu-central-1-cyberduck", EnumSet.of(Path.Type.directory, Path.Type.volume)); final S3AccessControlListFeature acl = new S3AccessControlListFeature(session); - final Path directory = new S3DirectoryFeature(session, new S3WriteFeature(session, acl), acl).mkdir(new Path(bucket, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory)), new TransferStatus()); + final Path directory = new S3DirectoryFeature(session, acl).mkdir(new S3WriteFeature(session, new S3AccessControlListFeature(session)), new Path(bucket, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory)), new TransferStatus()); assertFalse(isDuplicate(directory, new S3VersionedObjectListService(session, acl).list(bucket, new DisabledListProgressListener()))); // Nullify version to add delete marker directory.attributes().setVersionId(null); new S3DefaultDeleteFeature(session, acl).delete(Collections.singletonList(directory), new DisabledLoginCallback(), new Delete.DisabledCallback()); - assertTrue(isDuplicate(directory, new S3VersionedObjectListService(session, acl).list(bucket, new DisabledListProgressListener()))); + assertTrue(isTrashed(directory, new S3VersionedObjectListService(session, acl).list(bucket, new DisabledListProgressListener()))); } /** @@ -270,27 +270,29 @@ public class S3VersionedObjectListServiceTest extends AbstractS3Test { public void testDirectoyPlaceholderWithChildren() throws Exception { final Path bucket = new Path("versioning-test-eu-central-1-cyberduck", EnumSet.of(Path.Type.directory, Path.Type.volume)); final S3AccessControlListFeature acl = new S3AccessControlListFeature(session); - final Path directory = new S3DirectoryFeature(session, new S3WriteFeature(session, acl), acl).mkdir(new Path(bucket, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory)), new TransferStatus()); + final Path directory = new S3DirectoryFeature(session, acl).mkdir(new S3WriteFeature(session, acl), new Path(bucket, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory)), new TransferStatus()); assertTrue(new S3VersionedObjectListService(session, acl).list(bucket, new DisabledListProgressListener()).contains(directory)); - final Path child1 = new S3TouchFeature(session, acl).touch(new Path(directory, new AsciiRandomStringService().random(), EnumSet.of(Path.Type.file)), new TransferStatus()); + final Path child1 = new S3TouchFeature(session, acl).touch(new S3WriteFeature(session, acl), new Path(directory, new AsciiRandomStringService().random(), EnumSet.of(Path.Type.file)), new TransferStatus()); assertTrue(new S3VersionedObjectListService(session, acl).list(directory, new DisabledListProgressListener()).contains(child1)); // Nullify version to add delete marker child1.attributes().setVersionId(null); - final Path child2 = new S3TouchFeature(session, acl).touch(new Path(directory, new AsciiRandomStringService().random(), EnumSet.of(Path.Type.file)), new TransferStatus()); + final Path child2 = new S3TouchFeature(session, acl).touch(new S3WriteFeature(session, acl), new Path(directory, new AsciiRandomStringService().random(), EnumSet.of(Path.Type.file)), new TransferStatus()); assertTrue(new S3VersionedObjectListService(session, acl).list(directory, new DisabledListProgressListener()).contains(child2)); // Nullify version to add delete marker child2.attributes().setVersionId(null); new S3DefaultDeleteFeature(session, acl).delete(Collections.singletonList(child1), new DisabledLoginCallback(), new Delete.DisabledCallback()); - assertTrue(isDuplicate(child1, new S3VersionedObjectListService(session, acl).list(directory, new DisabledListProgressListener()))); + assertTrue(isTrashed(child1, new S3VersionedObjectListService(session, acl).list(directory, new DisabledListProgressListener()))); + assertFalse(isTrashed(directory, new S3VersionedObjectListService(session, acl).list(bucket, new DisabledListProgressListener()))); assertFalse(isDuplicate(directory, new S3VersionedObjectListService(session, acl).list(bucket, new DisabledListProgressListener()))); // Nullify version to add delete marker directory.attributes().setVersionId(null); new S3DefaultDeleteFeature(session, acl).delete(Collections.singletonList(directory), new DisabledLoginCallback(), new Delete.DisabledCallback()); // No placeholder object but child object under this prefix should still be found + assertTrue(isTrashed(directory, new S3VersionedObjectListService(session, acl).list(bucket, new DisabledListProgressListener()))); assertFalse(isDuplicate(directory, new S3VersionedObjectListService(session, acl).list(bucket, new DisabledListProgressListener()))); new S3DefaultDeleteFeature(session, acl).delete(Collections.singletonList(child2), new DisabledLoginCallback(), new Delete.DisabledCallback()); - assertTrue(isDuplicate(child2, new S3VersionedObjectListService(session, acl).list(directory, new DisabledListProgressListener()))); - assertTrue(isDuplicate(directory, new S3VersionedObjectListService(session, acl).list(bucket, new DisabledListProgressListener()))); + assertTrue(isTrashed(child2, new S3VersionedObjectListService(session, acl).list(directory, new DisabledListProgressListener()))); + assertTrue(isTrashed(directory, new S3VersionedObjectListService(session, acl).list(bucket, new DisabledListProgressListener()))); } /** @@ -301,47 +303,50 @@ public class S3VersionedObjectListServiceTest extends AbstractS3Test { final Path bucket = new Path("versioning-test-eu-central-1-cyberduck", EnumSet.of(Path.Type.directory, Path.Type.volume)); final Path directory = new Path(bucket, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory)); final S3AccessControlListFeature acl = new S3AccessControlListFeature(session); - final Path child1 = new S3TouchFeature(session, acl).touch(new Path(directory, new AsciiRandomStringService().random(), EnumSet.of(Path.Type.file)), new TransferStatus()); + final Path child1 = new S3TouchFeature(session, acl).touch(new S3WriteFeature(session, acl), new Path(directory, new AsciiRandomStringService().random(), EnumSet.of(Path.Type.file)), new TransferStatus()); assertTrue(new S3VersionedObjectListService(session, acl).list(bucket, new DisabledListProgressListener()).contains(directory)); assertTrue(new S3VersionedObjectListService(session, acl).list(directory, new DisabledListProgressListener()).contains(child1)); // Nullify version to add delete marker child1.attributes().setVersionId(null); - final Path child2 = new S3TouchFeature(session, acl).touch(new Path(directory, new AsciiRandomStringService().random(), EnumSet.of(Path.Type.file)), new TransferStatus()); + final Path child2 = new S3TouchFeature(session, acl).touch(new S3WriteFeature(session, acl), new Path(directory, new AsciiRandomStringService().random(), EnumSet.of(Path.Type.file)), new TransferStatus()); assertTrue(new S3VersionedObjectListService(session, acl).list(directory, new DisabledListProgressListener()).contains(child2)); // Nullify version to add delete marker child2.attributes().setVersionId(null); new S3DefaultDeleteFeature(session, acl).delete(Collections.singletonList(child1), new DisabledLoginCallback(), new Delete.DisabledCallback()); - assertTrue(isDuplicate(child1, new S3VersionedObjectListService(session, acl).list(directory, new DisabledListProgressListener()))); - assertFalse(isDuplicate(directory, new S3VersionedObjectListService(session, acl).list(bucket, new DisabledListProgressListener()))); + assertTrue(isTrashed(child1, new S3VersionedObjectListService(session, acl).list(directory, new DisabledListProgressListener()))); + assertFalse(isTrashed(directory, new S3VersionedObjectListService(session, acl).list(bucket, new DisabledListProgressListener()))); new S3DefaultDeleteFeature(session, acl).delete(Collections.singletonList(child2), new DisabledLoginCallback(), new Delete.DisabledCallback()); - assertTrue(isDuplicate(child2, new S3VersionedObjectListService(session, acl).list(directory, new DisabledListProgressListener()))); + assertTrue(isTrashed(child2, new S3VersionedObjectListService(session, acl).list(directory, new DisabledListProgressListener()))); // Prefix with only deleted files must be marked as hidden assertTrue(isDuplicate(directory, new S3VersionedObjectListService(session, acl).list(bucket, new DisabledListProgressListener()))); } - private boolean isDuplicate(final Path file, final AttributedList list) throws BackgroundException { + private boolean isDuplicate(final Path file, final AttributedList list) { final Path found = list.find(new SimplePathPredicate(file)); if(null == found) { fail(MessageFormat.format("Path {0} not found", file)); } - if(null == found.attributes().getVersionId()) { - return found.attributes().isDuplicate(); - } - final PathAttributes attr = new S3AttributesFinderFeature(session, new S3AccessControlListFeature(session)).find(found); - assertEquals(found.attributes().getCustom(), attr.getCustom()); return found.attributes().isDuplicate(); } + private boolean isTrashed(final Path file, final AttributedList list) { + final Path found = list.find(new SimplePathPredicate(file)); + if(null == found) { + fail(MessageFormat.format("Path {0} not found", file)); + } + return found.attributes().isTrashed(); + } + @Test public void testListFileDot() throws Exception { final Path container = new Path("test-eu-central-1-cyberduck", EnumSet.of(Path.Type.directory, Path.Type.volume)); - final Path file = new S3TouchFeature(session, new S3AccessControlListFeature(session)).touch( - new Path(container, ".", EnumSet.of(Path.Type.file)), new TransferStatus()); + final S3AccessControlListFeature acl = new S3AccessControlListFeature(session); + final Path file = new S3TouchFeature(session, acl).touch( + new S3WriteFeature(session, acl), new Path(container, ".", EnumSet.of(Path.Type.file)), new TransferStatus()); assertEquals(".", file.getName()); assertEquals(container, file.getParent()); - final S3AccessControlListFeature acl = new S3AccessControlListFeature(session); file.withAttributes(new S3AttributesFinderFeature(session, acl).find(file)); - assertNotNull(new S3VersionedObjectListService(session, new S3AccessControlListFeature(session)).list(container, new DisabledListProgressListener()) + assertNotNull(new S3VersionedObjectListService(session, acl).list(container, new DisabledListProgressListener()) .find(new SimplePathPredicate(file))); new S3DefaultDeleteFeature(session, acl).delete(Collections.singletonList(file), new DisabledLoginCallback(), new Delete.DisabledCallback()); } @@ -350,9 +355,9 @@ public class S3VersionedObjectListServiceTest extends AbstractS3Test { public void testListPlaceholderDot() throws Exception { final Path container = new Path("test-eu-central-1-cyberduck", EnumSet.of(Path.Type.directory, Path.Type.volume)); final S3AccessControlListFeature acl = new S3AccessControlListFeature(session); - final Path placeholder = new S3DirectoryFeature(session, new S3WriteFeature(session, acl), acl).mkdir( - new Path(container, ".", EnumSet.of(Path.Type.directory)), new TransferStatus()); - assertTrue(new S3VersionedObjectListService(session, new S3AccessControlListFeature(session)).list(container, new DisabledListProgressListener()).contains(placeholder)); + final Path placeholder = new S3DirectoryFeature(session, acl).mkdir( + new S3WriteFeature(session, acl), new Path(container, ".", EnumSet.of(Path.Type.directory)), new TransferStatus()); + assertTrue(new S3VersionedObjectListService(session, acl).list(container, new DisabledListProgressListener()).contains(placeholder)); new S3DefaultDeleteFeature(session, acl).delete(Collections.singletonList(placeholder), new DisabledLoginCallback(), new Delete.DisabledCallback()); } diff --git a/s3/src/test/java/ch/cyberduck/core/s3/S3VersioningFeatureTest.java b/s3/src/test/java/ch/cyberduck/core/s3/S3VersioningFeatureTest.java index e856f6c797..37ff29c9ff 100644 --- a/s3/src/test/java/ch/cyberduck/core/s3/S3VersioningFeatureTest.java +++ b/s3/src/test/java/ch/cyberduck/core/s3/S3VersioningFeatureTest.java @@ -19,6 +19,7 @@ package ch.cyberduck.core.s3; 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; @@ -70,7 +71,7 @@ public class S3VersioningFeatureTest extends AbstractS3Test { public void testSetConfiguration() throws Exception { final Path container = new Path(UUID.randomUUID().toString(), EnumSet.of(Path.Type.directory, Path.Type.volume)); final S3AccessControlListFeature acl = new S3AccessControlListFeature(session); - new S3DirectoryFeature(session, new S3WriteFeature(session, acl), acl).mkdir(container, new TransferStatus()); + new S3DirectoryFeature(session, acl).mkdir(new S3WriteFeature(session, new S3AccessControlListFeature(session)), container, new TransferStatus()); final Versioning feature = new S3VersioningFeature(session, acl); feature.setConfiguration(container, new DisabledLoginCallback(), new VersioningConfiguration(true, false)); assertTrue(feature.getConfiguration(container).isEnabled()); @@ -88,10 +89,10 @@ public class S3VersioningFeatureTest extends AbstractS3Test { public void testRevert() throws Exception { final Path bucket = new Path("versioning-test-eu-central-1-cyberduck", EnumSet.of(Path.Type.directory, Path.Type.volume)); final S3AccessControlListFeature acl = new S3AccessControlListFeature(session); - final Path directory = new S3DirectoryFeature(session, new S3WriteFeature(session, acl), acl).mkdir(new Path( + final Path directory = new S3DirectoryFeature(session, acl).mkdir(new S3WriteFeature(session, new S3AccessControlListFeature(session)), new Path( bucket, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory)), new TransferStatus()); - final Path test = new S3TouchFeature(session, acl).touch(new Path(directory, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)), new TransferStatus()); - final Path ignored = new S3TouchFeature(session, acl).touch(new Path(directory, String.format("%s-2", test.getName()), EnumSet.of(Path.Type.file)), new TransferStatus()); + final Path test = new S3TouchFeature(session, acl).touch(new S3WriteFeature(session, new S3AccessControlListFeature(session)), new Path(directory, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)), new TransferStatus()); + final Path ignored = new S3TouchFeature(session, acl).touch(new S3WriteFeature(session, new S3AccessControlListFeature(session)), new Path(directory, 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); @@ -100,7 +101,7 @@ public class S3VersioningFeatureTest extends AbstractS3Test { final HttpResponseOutputStream out = writer.write(ignored, status, new DisabledConnectionCallback()); new StreamCopier(status, status).transfer(new ByteArrayInputStream(content), out); } - 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(); diff --git a/s3/src/test/java/ch/cyberduck/core/shared/CachingAttributesFinderFeatureTest.java b/s3/src/test/java/ch/cyberduck/core/shared/CachingAttributesFinderFeatureTest.java index 08ebc61643..1ccd932a20 100644 --- a/s3/src/test/java/ch/cyberduck/core/shared/CachingAttributesFinderFeatureTest.java +++ b/s3/src/test/java/ch/cyberduck/core/shared/CachingAttributesFinderFeatureTest.java @@ -17,6 +17,7 @@ package ch.cyberduck.core.shared; import ch.cyberduck.core.AlphanumericRandomStringService; import ch.cyberduck.core.CachingAttributesFinderFeature; +import ch.cyberduck.core.DefaultPathAttributes; import ch.cyberduck.core.DisabledConnectionCallback; import ch.cyberduck.core.DisabledLoginCallback; import ch.cyberduck.core.ListProgressListener; @@ -76,7 +77,7 @@ public class CachingAttributesFinderFeatureTest extends AbstractS3Test { final String name = new AlphanumericRandomStringService().random(); final Path bucket = new Path("versioning-test-eu-central-1-cyberduck", EnumSet.of(Path.Type.volume, Path.Type.directory)); final S3AccessControlListFeature acl = new S3AccessControlListFeature(session); - final Path file = new S3TouchFeature(session, acl).touch(new Path(bucket, name, EnumSet.of(Path.Type.file)), new TransferStatus()); + final Path file = new S3TouchFeature(session, acl).touch(new S3WriteFeature(session, new S3AccessControlListFeature(session)), new Path(bucket, name, EnumSet.of(Path.Type.file)), new TransferStatus()); final String initialVersion = file.attributes().getVersionId(); assertNotNull(initialVersion); final PathAttributes lookup = f.find(file); @@ -108,19 +109,19 @@ public class CachingAttributesFinderFeatureTest extends AbstractS3Test { final HttpResponseOutputStream out = new S3WriteFeature(session, acl).write(file, status, new DisabledConnectionCallback()); IOUtils.copy(new ByteArrayInputStream(content), out); out.close(); - assertEquals(initialVersion, f.find(file.withAttributes(new PathAttributes(file.attributes()).setVersionId(initialVersion))).getVersionId()); + assertEquals(initialVersion, f.find(file.withAttributes(new DefaultPathAttributes(file.attributes()).setVersionId(initialVersion))).getVersionId()); final String newVersion = ((S3Object) out.getStatus()).getVersionId(); try { - f.find(file.withAttributes(new PathAttributes(file.attributes()).setVersionId(newVersion))); + f.find(file.withAttributes(new DefaultPathAttributes(file.attributes()).setVersionId(newVersion))); fail(); } catch(NotfoundException e) { // Expected } cache.clear(); - assertEquals(newVersion, f.find(file.withAttributes(new PathAttributes(file.attributes()).setVersionId(newVersion))).getVersionId()); + assertEquals(newVersion, f.find(file.withAttributes(new DefaultPathAttributes(file.attributes()).setVersionId(newVersion))).getVersionId()); assertEquals(newVersion, f.find(file.withAttributes(PathAttributes.EMPTY)).getVersionId()); - assertNotEquals(initialVersion, f.find(file.withAttributes(new PathAttributes(file.attributes()).setVersionId(newVersion))).getVersionId()); + assertNotEquals(initialVersion, f.find(file.withAttributes(new DefaultPathAttributes(file.attributes()).setVersionId(newVersion))).getVersionId()); assertEquals(new S3AttributesAdapter(session.getHost()).toAttributes(out.getStatus()).getVersionId(), f.find(file).getVersionId()); new S3DefaultDeleteFeature(session, acl).delete(Collections.singletonList(file), new DisabledLoginCallback(), new Delete.DisabledCallback()); } diff --git a/s3/src/test/java/ch/cyberduck/core/shared/CachingFindFeatureTest.java b/s3/src/test/java/ch/cyberduck/core/shared/CachingFindFeatureTest.java index 9bd2513404..2b795a4057 100644 --- a/s3/src/test/java/ch/cyberduck/core/shared/CachingFindFeatureTest.java +++ b/s3/src/test/java/ch/cyberduck/core/shared/CachingFindFeatureTest.java @@ -28,6 +28,7 @@ import ch.cyberduck.core.s3.AbstractS3Test; import ch.cyberduck.core.s3.S3AccessControlListFeature; import ch.cyberduck.core.s3.S3DefaultDeleteFeature; import ch.cyberduck.core.s3.S3TouchFeature; +import ch.cyberduck.core.s3.S3WriteFeature; import ch.cyberduck.core.transfer.TransferStatus; import ch.cyberduck.test.IntegrationTest; @@ -50,7 +51,7 @@ public class CachingFindFeatureTest extends AbstractS3Test { final CachingFindFeature f = new CachingFindFeature(session, cache, new DefaultFindFeature(session)); assertFalse(f.find(new Path(bucket, name, EnumSet.of(Path.Type.file)))); final S3AccessControlListFeature acl = new S3AccessControlListFeature(session); - final Path test = new S3TouchFeature(session, acl).touch(new Path(bucket, name, EnumSet.of(Path.Type.file)), new TransferStatus()); + final Path test = new S3TouchFeature(session, acl).touch(new S3WriteFeature(session, new S3AccessControlListFeature(session)), new Path(bucket, name, EnumSet.of(Path.Type.file)), new TransferStatus()); assertFalse(f.find(test)); cache.clear(); assertTrue(f.find(test)); diff --git a/s3/src/test/java/ch/cyberduck/core/shared/DefaultAttributesFinderFeatureTest.java b/s3/src/test/java/ch/cyberduck/core/shared/DefaultAttributesFinderFeatureTest.java index a8c09cf981..164e3ea706 100644 --- a/s3/src/test/java/ch/cyberduck/core/shared/DefaultAttributesFinderFeatureTest.java +++ b/s3/src/test/java/ch/cyberduck/core/shared/DefaultAttributesFinderFeatureTest.java @@ -16,6 +16,7 @@ package ch.cyberduck.core.shared; */ import ch.cyberduck.core.AlphanumericRandomStringService; +import ch.cyberduck.core.DefaultPathAttributes; import ch.cyberduck.core.DisabledConnectionCallback; import ch.cyberduck.core.DisabledLoginCallback; import ch.cyberduck.core.Path; @@ -62,7 +63,7 @@ public class DefaultAttributesFinderFeatureTest extends AbstractS3Test { final String name = new AlphanumericRandomStringService().random(); final Path bucket = new Path("versioning-test-eu-central-1-cyberduck", EnumSet.of(Path.Type.volume, Path.Type.directory)); final S3AccessControlListFeature acl = new S3AccessControlListFeature(session); - final Path file = new S3TouchFeature(session, acl).touch(new Path(bucket, name, EnumSet.of(Path.Type.file)), new TransferStatus()); + final Path file = new S3TouchFeature(session, acl).touch(new S3WriteFeature(session, new S3AccessControlListFeature(session)), new Path(bucket, name, EnumSet.of(Path.Type.file)), new TransferStatus()); final String initialVersion = file.attributes().getVersionId(); assertNotNull(initialVersion); assertNotSame(file.attributes(), f.find(file)); @@ -85,10 +86,10 @@ public class DefaultAttributesFinderFeatureTest extends AbstractS3Test { final HttpResponseOutputStream out = new S3WriteFeature(session, acl).write(file, status, new DisabledConnectionCallback()); IOUtils.copy(new ByteArrayInputStream(content), out); out.close(); - assertEquals(initialVersion, f.find(file.withAttributes(new PathAttributes(file.attributes()).setVersionId(initialVersion))).getVersionId()); + assertEquals(initialVersion, f.find(file.withAttributes(new DefaultPathAttributes(file.attributes()).setVersionId(initialVersion))).getVersionId()); final String newVersion = ((S3Object) out.getStatus()).getVersionId(); - assertEquals(newVersion, f.find(file.withAttributes(new PathAttributes(file.attributes()).setVersionId(newVersion))).getVersionId()); - assertNotEquals(initialVersion, f.find(file.withAttributes(new PathAttributes(file.attributes()).setVersionId(newVersion))).getVersionId()); + assertEquals(newVersion, f.find(file.withAttributes(new DefaultPathAttributes(file.attributes()).setVersionId(newVersion))).getVersionId()); + assertNotEquals(initialVersion, f.find(file.withAttributes(new DefaultPathAttributes(file.attributes()).setVersionId(newVersion))).getVersionId()); assertEquals(new S3AttributesAdapter(session.getHost()).toAttributes(out.getStatus()).getVersionId(), f.find(file).getVersionId()); new S3DefaultDeleteFeature(session, acl).delete(Collections.singletonList(file), new DisabledLoginCallback(), new Delete.DisabledCallback()); } diff --git a/s3/src/test/java/ch/cyberduck/core/shared/DefaultDownloadFeatureTest.java b/s3/src/test/java/ch/cyberduck/core/shared/DefaultDownloadFeatureTest.java index fba75077d9..1d6451177c 100644 --- a/s3/src/test/java/ch/cyberduck/core/shared/DefaultDownloadFeatureTest.java +++ b/s3/src/test/java/ch/cyberduck/core/shared/DefaultDownloadFeatureTest.java @@ -59,7 +59,7 @@ public class DefaultDownloadFeatureTest extends AbstractS3Test { final Path container = new Path("versioning-test-eu-central-1-cyberduck", EnumSet.of(Path.Type.volume, Path.Type.directory)); final S3AccessControlListFeature acl = new S3AccessControlListFeature(session); final Path test = new S3TouchFeature(session, acl).touch( - new Path(container, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)), new TransferStatus()); + new S3WriteFeature(session, new S3AccessControlListFeature(session)), new Path(container, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)), new TransferStatus()); { final byte[] content = RandomUtils.nextBytes(39864); final TransferStatus writeStatus = new TransferStatus().setLength(content.length).setChecksum(new SHA256ChecksumCompute().compute(new ByteArrayInputStream(content), new TransferStatus())); @@ -78,8 +78,8 @@ public class DefaultDownloadFeatureTest extends AbstractS3Test { test.attributes().setVersionId(((S3Object) out.getStatus()).getVersionId()); final Local local = new Local(System.getProperty("java.io.tmpdir"), UUID.randomUUID().toString()); final TransferStatus readStatus = new TransferStatus().setLength(content.length); - new DefaultDownloadFeature(new S3ReadFeature(session)).download( - test, local, new BandwidthThrottle(BandwidthThrottle.UNLIMITED), new DisabledStreamListener(), + new DefaultDownloadFeature(session).download( + new S3ReadFeature(session), test, local, new BandwidthThrottle(BandwidthThrottle.UNLIMITED), new DisabledStreamListener(), readStatus, new DisabledConnectionCallback()); final byte[] buffer = new byte[content.length]; diff --git a/s3/src/test/java/ch/cyberduck/core/shared/DefaultFindFeatureTest.java b/s3/src/test/java/ch/cyberduck/core/shared/DefaultFindFeatureTest.java index 6f0223e399..d0ad99c3e2 100644 --- a/s3/src/test/java/ch/cyberduck/core/shared/DefaultFindFeatureTest.java +++ b/s3/src/test/java/ch/cyberduck/core/shared/DefaultFindFeatureTest.java @@ -23,6 +23,7 @@ import ch.cyberduck.core.s3.AbstractS3Test; import ch.cyberduck.core.s3.S3AccessControlListFeature; import ch.cyberduck.core.s3.S3DefaultDeleteFeature; import ch.cyberduck.core.s3.S3TouchFeature; +import ch.cyberduck.core.s3.S3WriteFeature; import ch.cyberduck.core.transfer.TransferStatus; import ch.cyberduck.test.IntegrationTest; @@ -44,7 +45,7 @@ public class DefaultFindFeatureTest extends AbstractS3Test { final Path file = new Path(bucket, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)); assertFalse(new DefaultFindFeature(session).find(file)); final S3AccessControlListFeature acl = new S3AccessControlListFeature(session); - final Path test = new S3TouchFeature(session, acl).touch(file, new TransferStatus()); + final Path test = new S3TouchFeature(session, acl).touch(new S3WriteFeature(session, new S3AccessControlListFeature(session)), file, new TransferStatus()); // Find without version id set in attributes assertTrue(new DefaultFindFeature(session).find(test)); assertTrue(new DefaultFindFeature(session).find(file)); diff --git a/s3/src/test/java/ch/cyberduck/core/shared/DefaultTouchFeatureTest.java b/s3/src/test/java/ch/cyberduck/core/shared/DefaultTouchFeatureTest.java index 037fb5500c..a9a5c1c4c7 100644 --- a/s3/src/test/java/ch/cyberduck/core/shared/DefaultTouchFeatureTest.java +++ b/s3/src/test/java/ch/cyberduck/core/shared/DefaultTouchFeatureTest.java @@ -27,6 +27,7 @@ import ch.cyberduck.core.s3.S3DefaultDeleteFeature; import ch.cyberduck.core.s3.S3WriteFeature; import ch.cyberduck.core.transfer.TransferStatus; +import org.jets3t.service.model.StorageObject; import org.junit.Ignore; import org.junit.Test; @@ -44,7 +45,7 @@ public class DefaultTouchFeatureTest extends AbstractS3Test { final Path container = new Path("test-eu-central-1-cyberduck", EnumSet.of(Path.Type.directory, Path.Type.volume)); final Path test = new Path(container, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)); final S3AccessControlListFeature acl = new S3AccessControlListFeature(session); - new DefaultTouchFeature<>(new S3WriteFeature(session, acl)).touch(test, new TransferStatus()); + new DefaultTouchFeature(session).touch(new S3WriteFeature(session, new S3AccessControlListFeature(session)), test, new TransferStatus()); final S3AttributesFinderFeature f = new S3AttributesFinderFeature(session, acl); final PathAttributes attributes = f.find(test); assertEquals(0L, attributes.getSize()); diff --git a/s3/src/test/java/ch/cyberduck/core/sts/AbstractAssumeRoleWithWebIdentityTest.java b/s3/src/test/java/ch/cyberduck/core/sts/AbstractAssumeRoleWithWebIdentityTest.java index 3a83a5b12c..8b3bd45592 100644 --- a/s3/src/test/java/ch/cyberduck/core/sts/AbstractAssumeRoleWithWebIdentityTest.java +++ b/s3/src/test/java/ch/cyberduck/core/sts/AbstractAssumeRoleWithWebIdentityTest.java @@ -33,7 +33,6 @@ public abstract class AbstractAssumeRoleWithWebIdentityTest { private static final ComposeContainer container = new ComposeContainer( new File(AbstractAssumeRoleWithWebIdentityTest.class.getResource("/testcontainer/docker-compose.yml").getFile())) .withPull(false) - .withLocalCompose(true) .withExposedService("keycloak-1", 8080, Wait.forListeningPort()) .withExposedService("minio-1", 9000, Wait.forListeningPort()); diff --git a/s3/src/test/java/ch/cyberduck/core/sts/AssumeRoleWithWebIdentityAuthenticationTest.java b/s3/src/test/java/ch/cyberduck/core/sts/AssumeRoleWithWebIdentityAuthenticationTest.java index e369bd83a4..a3da178d3f 100644 --- a/s3/src/test/java/ch/cyberduck/core/sts/AssumeRoleWithWebIdentityAuthenticationTest.java +++ b/s3/src/test/java/ch/cyberduck/core/sts/AssumeRoleWithWebIdentityAuthenticationTest.java @@ -37,10 +37,8 @@ import ch.cyberduck.core.s3.S3Session; import ch.cyberduck.core.serializer.impl.dd.ProfilePlistReader; import ch.cyberduck.test.TestcontainerTest; -import org.jets3t.service.security.AWSSessionCredentials; import org.junit.Test; import org.junit.experimental.categories.Category; -import org.testcontainers.shaded.org.apache.commons.lang3.StringUtils; import java.util.Collections; import java.util.EnumSet; @@ -56,20 +54,22 @@ public class AssumeRoleWithWebIdentityAuthenticationTest extends AbstractAssumeR public void testSuccessfulLogin() throws BackgroundException { final Protocol profile = new ProfilePlistReader(new ProtocolFactory(new HashSet<>(Collections.singleton(new S3Protocol())))).read( AbstractAssumeRoleWithWebIdentityTest.class.getResourceAsStream("/S3 (OIDC).cyberduckprofile")); - final Host host = new Host(profile, profile.getDefaultHostname(), new Credentials("rouser", "rouser")); + final Credentials credentials = new Credentials("rouser", "rouser"); + final Host host = new Host(profile, profile.getDefaultHostname(), credentials); final S3Session session = new S3Session(host); session.open(new DisabledProxyFinder(), new DisabledHostKeyCallback(), new DisabledLoginCallback(), new DisabledCancelCallback()); session.login(new DisabledLoginCallback(), new DisabledCancelCallback()); - final Credentials credentials = host.getCredentials(); - assertNotEquals("rouser", credentials.getUsername()); - assertNotEquals(StringUtils.EMPTY, credentials.getPassword()); + assertEquals("rouser", credentials.getUsername()); + assertEquals("rouser", credentials.getPassword()); assertNotNull(credentials.getTokens().getAccessKeyId()); assertNotNull(credentials.getTokens().getSecretAccessKey()); assertNotNull(credentials.getTokens().getSessionToken()); assertNotNull(credentials.getOauth().getIdToken()); assertNotNull(credentials.getOauth().getRefreshToken()); + assertEquals("rouser", credentials.getUsername()); + assertEquals("rouser", credentials.getPassword()); assertNotEquals(Optional.of(Long.MAX_VALUE).get(), credentials.getOauth().getExpiryInMilliseconds()); session.close(); } @@ -100,17 +100,19 @@ public class AssumeRoleWithWebIdentityAuthenticationTest extends AbstractAssumeR public void testTokenRefresh() throws BackgroundException, InterruptedException { final Protocol profile = new ProfilePlistReader(new ProtocolFactory(new HashSet<>(Collections.singleton(new S3Protocol())))).read( AbstractAssumeRoleWithWebIdentityTest.class.getResourceAsStream("/S3 (OIDC).cyberduckprofile")); - final Host host = new Host(profile, profile.getDefaultHostname(), new Credentials("rawuser", "rawuser")); + final Credentials credentials = new Credentials("rawuser", "rawuser"); + final Host host = new Host(profile, profile.getDefaultHostname(), credentials); final S3Session session = new S3Session(host); session.open(new DisabledProxyFinder(), new DisabledHostKeyCallback(), new DisabledLoginCallback(), new DisabledCancelCallback()); session.login(new DisabledLoginCallback(), new DisabledCancelCallback()); - final Credentials credentials = host.getCredentials(); final OAuthTokens oauth = credentials.getOauth(); assertTrue(oauth.validate()); final TemporaryAccessTokens tokens = credentials.getTokens(); assertTrue(tokens.validate()); + credentials.setOauth(OAuthTokens.EMPTY).setTokens(TemporaryAccessTokens.EMPTY); + Path container = new Path("cyberduckbucket", EnumSet.of(Path.Type.directory, Path.Type.volume)); assertTrue(new S3FindFeature(session, new S3AccessControlListFeature(session)).find(container)); @@ -129,24 +131,22 @@ public class AssumeRoleWithWebIdentityAuthenticationTest extends AbstractAssumeR * Fetch OpenID Connect Id token initially fails because of invalid refresh token. Must re-run OAuth flow. */ @Test - public void testLoginInvalidOAuthTokensLogin() throws Exception { + public void testLoginInvalidOAuthTokens() throws Exception { final Protocol profile = new ProfilePlistReader(new ProtocolFactory(new HashSet<>(Collections.singleton(new S3Protocol())))).read( AbstractAssumeRoleWithWebIdentityTest.class.getResourceAsStream("/S3 (OIDC).cyberduckprofile")); final Credentials credentials = new Credentials("rouser", "rouser") - .withOauth(new OAuthTokens( + .setOauth(new OAuthTokens( "eyJhbGciOiJSUzI1NiIsInR5cCIgOiAiSldUIiwia2lkIiA6ICJDQmpaYUNSeU5USmZqV0VmMU1fZXZLRVliMEdGLXU0QzhjZ3RZYnBtZUlFIn0.eyJleHAiOjE2OTE5OTc3MzUsImlhdCI6MTY5MTk5NzcwNSwianRpIjoiNDA1MGUxMGYtNzZjNC00MjYwLTk1YTctZTMyMTE2YTA3N2NlIiwiaXNzIjoiaHR0cDovL2xvY2FsaG9zdDo4MDgwL3JlYWxtcy9jeWJlcmR1Y2tyZWFsbSIsInN1YiI6IjMzNGRiZWIwLTE5NWQtNDJhMS1hMWQ2LTEyODFmMDBiZmIxZCIsInR5cCI6IkJlYXJlciIsImF6cCI6Im1pbmlvIiwic2Vzc2lvbl9zdGF0ZSI6IjNkZDY0MDVlLTNkMGMtNDVjOS05MTZkLTllYTNkNWY1ODVkYiIsInJlYWxtX2FjY2VzcyI6eyJyb2xlcyI6WyJvZmZsaW5lX2FjY2VzcyIsInVtYV9hdXRob3JpemF0aW9uIiwidXNlciJdfSwic2NvcGUiOiJvcGVuaWQgbWluaW8tYXV0aG9yaXphdGlvbiIsInNpZCI6IjNkZDY0MDVlLTNkMGMtNDVjOS05MTZkLTllYTNkNWY1ODVkYiIsInBvbGljeSI6WyJyZWFkb25seSJdfQ.uKxLmSW6j2EQEo86j0WZOKWgavhS8Ub7TjrnynUi4m1ls0SchvgCilVpzIzNdFL9Y7khiqxl7si5BezbTLPgwyh4GDgrHcJwBk5D6aOcaH6hYcAtcbOiu1KEyfj1O_lwvDCHb-J07TIEeuvquOs2nD7FxqafHjLe-3pL6JuTtBtlx8WKloO9PY-Dn-ntuyqikr7ysLcDBfFJda487cmeTADxiMQ_MmoidW3uGXn0Ps6vhRgteUQO5JTKMa7MT1PKMTY8iNnSdNVuhKkBodnkXMSo5JEt4veqR9Yh-WPT_XL8caUiGInYvHty-n6-yhGhNckrlvtmJc0dJsts4hi1Mw", "eyJhbGciOiJIUzI1NiIsInR5cCIgOiAiSldUIiwia2lkIiA6ICIxNzA0N2Q0NS0wMTVhLTQwYWItYjc5NS03Y2Y1ZDE2ZmFhMmQifQ.eyJleHAiOjE2OTE5OTk1MDUsImlhdCI6MTY5MTk5NzcwNSwianRpIjoiY2U4OGVlMjMtOTQ1Yi00YzlmLWExMjAtZjU2ODk0NzIwZDk0IiwiaXNzIjoiaHR0cDovL2xvY2FsaG9zdDo4MDgwL3JlYWxtcy9jeWJlcmR1Y2tyZWFsbSIsImF1ZCI6Imh0dHA6Ly9sb2NhbGhvc3Q6ODA4MC9yZWFsbXMvY3liZXJkdWNrcmVhbG0iLCJzdWIiOiIzMzRkYmViMC0xOTVkLTQyYTEtYTFkNi0xMjgxZjAwYmZiMWQiLCJ0eXAiOiJSZWZyZXNoIiwiYXpwIjoibWluaW8iLCJzZXNzaW9uX3N0YXRlIjoiM2RkNjQwNWUtM2QwYy00NWM5LTkxNmQtOWVhM2Q1ZjU4NWRiIiwic2NvcGUiOiJvcGVuaWQgbWluaW8tYXV0aG9yaXphdGlvbiIsInNpZCI6IjNkZDY0MDVlLTNkMGMtNDVjOS05MTZkLTllYTNkNWY1ODVkYiJ9.iRFLFjU-Uyv81flgieBht2K2BSlM-67fe5unvqI9PXA", Long.MAX_VALUE, "eyJhbGciOiJSUzI1NiIsInR5cCIgOiAiSldUIiwia2lkIiA6ICJDQmpaYUNSeU5USmZqV0VmMU1fZXZLRVliMEdGLXU0QzhjZ3RZYnBtZUlFIn0.eyJleHAiOjE2OTE5OTc3MzUsImlhdCI6MTY5MTk5NzcwNSwiYXV0aF90aW1lIjowLCJqdGkiOiJlYWZiNWE5NS1lYmY3LTQ0OTEtODAwYy0yZjU1NTk2MjQ0YzIiLCJpc3MiOiJodHRwOi8vbG9jYWxob3N0OjgwODAvcmVhbG1zL2N5YmVyZHVja3JlYWxtIiwiYXVkIjoibWluaW8iLCJzdWIiOiIzMzRkYmViMC0xOTVkLTQyYTEtYTFkNi0xMjgxZjAwYmZiMWQiLCJ0eXAiOiJJRCIsImF6cCI6Im1pbmlvIiwic2Vzc2lvbl9zdGF0ZSI6IjNkZDY0MDVlLTNkMGMtNDVjOS05MTZkLTllYTNkNWY1ODVkYiIsImF0X2hhc2giOiJWX1lIZTVpc0UzY0IyOGF4cXQzRGpnIiwic2lkIjoiM2RkNjQwNWUtM2QwYy00NWM5LTkxNmQtOWVhM2Q1ZjU4NWRiIiwicG9saWN5IjpbInJlYWRvbmx5Il19.bXjcBJY7H79O9rtYr3b_EpKuclaRRsWGIVm5SEesqMM3aIkGq6ikWNmoL4Ffy48Frx1E3UnvG5PQfd8C2-XgNg_9EnWyR1MkgxJ67xQOAT10E77wZ0YbFWYIcdOojR98rmh4_TGVeTaGwDMMQZzRMr0nQwfZP3TQ8ciRhor8svnkFkk3FBzT1rSJA0bJv181HyerQl0f_TnTEnr3UjmmFmDrNASxHoXbwqiE4L-qZBnNiz97jLxGULfyVn4CZUub53x0ka0KGnLeicFHDh1asiHMW18o9-BUh8cGp-Ywm7Xu_f_c8XokNjG8ls56Xp7g8rQ4-d3J0F0-TAgnn7xO1g")) - .withTokens(TemporaryAccessTokens.EMPTY); + .setTokens(TemporaryAccessTokens.EMPTY); final Host host = new Host(profile, profile.getDefaultHostname(), credentials); final S3Session session = new S3Session(host); assertNotNull(session.open(new DisabledProxyFinder(), new DisabledHostKeyCallback(), new DisabledLoginCallback(), new DisabledCancelCallback())); assertTrue(session.isConnected()); assertNotNull(session.getClient()); - session.login(new DisabledLoginCallback(), new DisabledCancelCallback()); - assertNotEquals(OAuthTokens.EMPTY, credentials.getOauth()); - assertNotEquals(TemporaryAccessTokens.EMPTY, credentials.getTokens()); + assertThrows(LoginFailureException.class, () -> session.login(new DisabledLoginCallback(), new DisabledCancelCallback())); } /** @@ -158,8 +158,8 @@ public class AssumeRoleWithWebIdentityAuthenticationTest extends AbstractAssumeR final Protocol profile = new ProfilePlistReader(new ProtocolFactory(new HashSet<>(Collections.singleton(new S3Protocol())))).read( AbstractAssumeRoleWithWebIdentityTest.class.getResourceAsStream("/S3 (OIDC).cyberduckprofile")); final Credentials credentials = new Credentials("rouser", "rouser") - .withOauth(OAuthTokens.EMPTY) - .withTokens(new TemporaryAccessTokens( + .setOauth(OAuthTokens.EMPTY) + .setTokens(new TemporaryAccessTokens( "5K1AVE34L4U1SQ7QTMWM", "LfkexzCDPojZpdIoNLNvHxrUi1KI5yP3Yken+DGI", "eyJhbGciOiJIUzUxMiIsInR5cCI6IkpXVCJ9.eyJhY2Nlc3NLZXkiOiI1SzFBVkUzNEw0VTFTUTdRVE1XTSIsImF0X2hhc2giOiJWX1lIZTVpc0UzY0IyOGF4cXQzRGpnIiwiYXVkIjoibWluaW8iLCJhdXRoX3RpbWUiOjAsImF6cCI6Im1pbmlvIiwiZXhwIjoxNjkxOTk3NzM1LCJpYXQiOjE2OTE5OTc3MDUsImlzcyI6Imh0dHA6Ly9sb2NhbGhvc3Q6ODA4MC9yZWFsbXMvY3liZXJkdWNrcmVhbG0iLCJqdGkiOiJlYWZiNWE5NS1lYmY3LTQ0OTEtODAwYy0yZjU1NTk2MjQ0YzIiLCJwb2xpY3kiOiJyZWFkb25seSIsInNlc3Npb25fc3RhdGUiOiIzZGQ2NDA1ZS0zZDBjLTQ1YzktOTE2ZC05ZWEzZDVmNTg1ZGIiLCJzaWQiOiIzZGQ2NDA1ZS0zZDBjLTQ1YzktOTE2ZC05ZWEzZDVmNTg1ZGIiLCJzdWIiOiIzMzRkYmViMC0xOTVkLTQyYTEtYTFkNi0xMjgxZjAwYmZiMWQiLCJ0eXAiOiJJRCJ9.HmyC7XuJw9XnsNUd2ZuGSVIPjnGHPpgbXX1HSbNJuhis1kUjhcrYY2HnQZ-uScoX57o_C3fF1eEv_t1kW2U6Rw", @@ -170,12 +170,12 @@ public class AssumeRoleWithWebIdentityAuthenticationTest extends AbstractAssumeR assertNotNull(session.open(new DisabledProxyFinder(), new DisabledHostKeyCallback(), new DisabledLoginCallback(), new DisabledCancelCallback())); assertTrue(session.isConnected()); assertNotNull(session.getClient()); - session.getClient().setProviderCredentials(new AWSSessionCredentials( - credentials.getTokens().getAccessKeyId(), credentials.getTokens().getSecretAccessKey(), - credentials.getTokens().getSessionToken())); new S3BucketListService(session).list( new Path(String.valueOf(Path.DELIMITER), EnumSet.of(Path.Type.volume, Path.Type.directory)), new DisabledListProgressListener()); assertNotEquals(OAuthTokens.EMPTY, credentials.getOauth()); assertNotEquals(TemporaryAccessTokens.EMPTY, credentials.getTokens()); + credentials.setOauth(OAuthTokens.EMPTY).setTokens(TemporaryAccessTokens.EMPTY); + new S3BucketListService(session).list( + new Path(String.valueOf(Path.DELIMITER), EnumSet.of(Path.Type.volume, Path.Type.directory)), new DisabledListProgressListener()); } } \ No newline at end of file diff --git a/s3/src/test/java/ch/cyberduck/core/sts/AssumeRoleWithWebIdentityAuthorizationTest.java b/s3/src/test/java/ch/cyberduck/core/sts/AssumeRoleWithWebIdentityAuthorizationTest.java index 575715a964..5c49bcac43 100644 --- a/s3/src/test/java/ch/cyberduck/core/sts/AssumeRoleWithWebIdentityAuthorizationTest.java +++ b/s3/src/test/java/ch/cyberduck/core/sts/AssumeRoleWithWebIdentityAuthorizationTest.java @@ -36,6 +36,7 @@ import ch.cyberduck.core.s3.S3Protocol; import ch.cyberduck.core.s3.S3ReadFeature; import ch.cyberduck.core.s3.S3Session; import ch.cyberduck.core.s3.S3TouchFeature; +import ch.cyberduck.core.s3.S3WriteFeature; import ch.cyberduck.core.serializer.impl.dd.ProfilePlistReader; import ch.cyberduck.core.transfer.TransferStatus; import ch.cyberduck.test.TestcontainerTest; @@ -90,7 +91,7 @@ public class AssumeRoleWithWebIdentityAuthorizationTest extends AbstractAssumeRo final Path container = new Path("cyberduckbucket", EnumSet.of(Path.Type.directory, Path.Type.volume)); final Path test = new Path(container, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)); final S3AccessControlListFeature acl = new S3AccessControlListFeature(session); - new S3TouchFeature(session, acl).touch(test, new TransferStatus()); + new S3TouchFeature(session, acl).touch(new S3WriteFeature(session, new S3AccessControlListFeature(session)), test, new TransferStatus()); assertTrue(new S3FindFeature(session, acl).find(test)); new S3DefaultDeleteFeature(session, acl).delete(Collections.singletonList(test), new DisabledLoginCallback(), new Delete.DisabledCallback()); assertFalse(new S3FindFeature(session, acl).find(test)); @@ -107,7 +108,7 @@ public class AssumeRoleWithWebIdentityAuthorizationTest extends AbstractAssumeRo session.login(new DisabledLoginCallback(), new DisabledCancelCallback()); final Path container = new Path("cyberduckbucket", EnumSet.of(Path.Type.directory, Path.Type.volume)); final Path test = new Path(container, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)); - assertThrows(AccessDeniedException.class, () -> new S3TouchFeature(session, new S3AccessControlListFeature(session)).touch(test, new TransferStatus())); + assertThrows(AccessDeniedException.class, () -> new S3TouchFeature(session, new S3AccessControlListFeature(session)).touch(new S3WriteFeature(session, new S3AccessControlListFeature(session)), test, new TransferStatus())); assertFalse(new S3FindFeature(session, new S3AccessControlListFeature(session)).find(test)); session.close(); } diff --git a/s3/src/test/java/ch/cyberduck/core/worker/CopyWorkerTest.java b/s3/src/test/java/ch/cyberduck/core/worker/CopyWorkerTest.java index ba4e19ac58..7f4703ac4d 100644 --- a/s3/src/test/java/ch/cyberduck/core/worker/CopyWorkerTest.java +++ b/s3/src/test/java/ch/cyberduck/core/worker/CopyWorkerTest.java @@ -49,7 +49,7 @@ public class CopyWorkerTest extends AbstractS3Test { final Path home = new Path("test-eu-central-1-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 S3TouchFeature(session, new S3AccessControlListFeature(session)).touch(source, new TransferStatus()); + new S3TouchFeature(session, new S3AccessControlListFeature(session)).touch(new S3WriteFeature(session, new S3AccessControlListFeature(session)), source, new TransferStatus()); new S3AccessControlListFeature(session).setPermission(source, new Acl( new Acl.UserAndRole( new Acl.Owner("80b9982b7b08045ee86680cc47f43c84bf439494a89ece22b5330f8a49477cf6"), new Acl.Role(Acl.Role.FULL) @@ -77,11 +77,11 @@ public class CopyWorkerTest extends AbstractS3Test { final Path home = new Path("test-eu-central-1-cyberduck", EnumSet.of(Path.Type.directory, Path.Type.volume)); final Path sourceFile = new Path(home, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)); final S3AccessControlListFeature acl = new S3AccessControlListFeature(session); - new S3TouchFeature(session, acl).touch(sourceFile, new TransferStatus()); + new S3TouchFeature(session, acl).touch(new S3WriteFeature(session, new S3AccessControlListFeature(session)), sourceFile, new TransferStatus()); assertTrue(new S3FindFeature(session, acl).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 S3DirectoryFeature(session, new S3WriteFeature(session, acl), acl).mkdir(targetFolder, new TransferStatus()); + new S3DirectoryFeature(session, acl).mkdir(new S3WriteFeature(session, new S3AccessControlListFeature(session)), targetFolder, new TransferStatus()); assertTrue(new S3FindFeature(session, acl).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()); @@ -97,8 +97,8 @@ public class CopyWorkerTest extends AbstractS3Test { 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)); final S3AccessControlListFeature acl = new S3AccessControlListFeature(session); - new S3DirectoryFeature(session, new S3WriteFeature(session, acl), acl).mkdir(folder, new TransferStatus()); - new S3TouchFeature(session, acl).touch(sourceFile, new TransferStatus()); + new S3DirectoryFeature(session, acl).mkdir(new S3WriteFeature(session, new S3AccessControlListFeature(session)), folder, new TransferStatus()); + new S3TouchFeature(session, acl).touch(new S3WriteFeature(session, new S3AccessControlListFeature(session)), sourceFile, new TransferStatus()); assertTrue(new S3FindFeature(session, acl).find(folder)); assertTrue(new S3FindFeature(session, acl).find(sourceFile)); // move directory into vault diff --git a/s3/src/test/java/ch/cyberduck/core/worker/DeleteWorkerTest.java b/s3/src/test/java/ch/cyberduck/core/worker/DeleteWorkerTest.java index 69950c6c15..9096e2514b 100644 --- a/s3/src/test/java/ch/cyberduck/core/worker/DeleteWorkerTest.java +++ b/s3/src/test/java/ch/cyberduck/core/worker/DeleteWorkerTest.java @@ -50,11 +50,11 @@ public class DeleteWorkerTest extends AbstractS3Test { public void testDelete() throws Exception { final Path home = new Path("test-eu-central-1-cyberduck", EnumSet.of(Path.Type.directory, Path.Type.volume)); final S3AccessControlListFeature acl = new S3AccessControlListFeature(session); - final Path folder = new S3DirectoryFeature(session, new S3WriteFeature(session, acl), acl).mkdir( - new Path(home, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory)), new TransferStatus()); + final Path folder = new S3DirectoryFeature(session, acl).mkdir( + new S3WriteFeature(session, new S3AccessControlListFeature(session)), new Path(home, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory)), new TransferStatus()); assertTrue(new S3FindFeature(session, acl).find(folder)); final Path file = new S3TouchFeature(session, acl).touch( - new Path(folder, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)), new TransferStatus()); + new S3WriteFeature(session, new S3AccessControlListFeature(session)), new Path(folder, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)), new TransferStatus()); assertNull(file.attributes().getVersionId()); new DeleteWorker(new DisabledLoginCallback(), Collections.singletonList(folder), new DisabledProgressListener()).run(session); assertFalse(new S3FindFeature(session, acl).find(file)); @@ -64,11 +64,11 @@ public class DeleteWorkerTest extends AbstractS3Test { public void testDeleteVersioning() throws Exception { final Path home = new Path("versioning-test-eu-central-1-cyberduck", EnumSet.of(Path.Type.directory, Path.Type.volume)); final S3AccessControlListFeature acl = new S3AccessControlListFeature(session); - final Path folder = new S3DirectoryFeature(session, new S3WriteFeature(session, acl), acl).mkdir( - new Path(home, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory)), new TransferStatus()); + final Path folder = new S3DirectoryFeature(session, acl).mkdir( + new S3WriteFeature(session, new S3AccessControlListFeature(session)), new Path(home, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory)), new TransferStatus()); assertTrue(new S3FindFeature(session, acl).find(folder)); final Path file = new S3TouchFeature(session, acl).touch( - new Path(folder, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)), new TransferStatus()); + new S3WriteFeature(session, new S3AccessControlListFeature(session)), new Path(folder, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)), new TransferStatus()); assertNotNull(file.attributes().getVersionId()); assertTrue(new S3FindFeature(session, acl).find(file)); new DeleteWorker(new DisabledLoginCallback(), Collections.singletonList(folder), new DisabledProgressListener()).run(session); diff --git a/s3/src/test/java/ch/cyberduck/core/worker/MoveWorkerTest.java b/s3/src/test/java/ch/cyberduck/core/worker/MoveWorkerTest.java index 23ae9f073d..f40168b71f 100644 --- a/s3/src/test/java/ch/cyberduck/core/worker/MoveWorkerTest.java +++ b/s3/src/test/java/ch/cyberduck/core/worker/MoveWorkerTest.java @@ -56,7 +56,7 @@ public class MoveWorkerTest extends AbstractS3Test { final Path home = new Path("test-eu-central-1-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 S3TouchFeature(session, new S3AccessControlListFeature(session)).touch(source, new TransferStatus()); + new S3TouchFeature(session, new S3AccessControlListFeature(session)).touch(new S3WriteFeature(session, new S3AccessControlListFeature(session)), source, new TransferStatus()); new S3AccessControlListFeature(session).setPermission(source, new Acl( new Acl.UserAndRole( new Acl.Owner("80b9982b7b08045ee86680cc47f43c84bf439494a89ece22b5330f8a49477cf6"), new Acl.Role(Acl.Role.FULL) @@ -85,17 +85,17 @@ public class MoveWorkerTest extends AbstractS3Test { public void testMoveVersionedDirectory() throws Exception { final Path bucket = new Path("versioning-test-eu-central-1-cyberduck", EnumSet.of(Path.Type.directory, Path.Type.volume)); final S3AccessControlListFeature acl = new S3AccessControlListFeature(session); - final Path sourceDirectory = new S3DirectoryFeature(session, new S3WriteFeature(session, acl), acl).mkdir(new Path(bucket, + final Path sourceDirectory = new S3DirectoryFeature(session, acl).mkdir(new S3WriteFeature(session, new S3AccessControlListFeature(session)), new Path(bucket, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory)), new TransferStatus()); - final Path targetDirectory = new S3DirectoryFeature(session, new S3WriteFeature(session, acl), acl).mkdir(new Path(bucket, + final Path targetDirectory = new S3DirectoryFeature(session, acl).mkdir(new S3WriteFeature(session, new S3AccessControlListFeature(session)), new Path(bucket, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory)), new TransferStatus()); final S3TouchFeature touch = new S3TouchFeature(session, acl); - Path test = touch.touch(new Path(sourceDirectory, new AsciiRandomStringService().random(), EnumSet.of(Path.Type.file)), new TransferStatus()); + Path test = touch.touch(new S3WriteFeature(session, new S3AccessControlListFeature(session)), new Path(sourceDirectory, new AsciiRandomStringService().random(), EnumSet.of(Path.Type.file)), new TransferStatus()); assertTrue(new S3FindFeature(session, acl).find(test)); final S3DefaultDeleteFeature delete = new S3DefaultDeleteFeature(session, acl); delete.delete(Collections.singletonList(new Path(test).withAttributes(PathAttributes.EMPTY)), new DisabledPasswordCallback(), new Delete.DisabledCallback()); assertTrue(new S3FindFeature(session, acl).find(test)); - test = touch.touch(test, new TransferStatus()); + test = touch.touch(new S3WriteFeature(session, new S3AccessControlListFeature(session)), test, new TransferStatus()); assertTrue(new S3FindFeature(session, acl).find(test)); final S3VersionedObjectListService list = new S3VersionedObjectListService(session, acl); final AttributedList versioned = list.list(sourceDirectory, new DisabledListProgressListener()); diff --git a/s3/src/test/java/ch/cyberduck/core/worker/S3SingleTransferWorkerTest.java b/s3/src/test/java/ch/cyberduck/core/worker/S3SingleTransferWorkerTest.java index 472fb4fd19..e88952037d 100644 --- a/s3/src/test/java/ch/cyberduck/core/worker/S3SingleTransferWorkerTest.java +++ b/s3/src/test/java/ch/cyberduck/core/worker/S3SingleTransferWorkerTest.java @@ -145,7 +145,7 @@ public class S3SingleTransferWorkerTest extends AbstractS3Test { @SuppressWarnings("unchecked") public T _getFeature(final Class type) { if(type == Upload.class) { - return (T) new S3MultipartUploadService(this, new S3WriteFeature(this, new S3AccessControlListFeature(this)), new S3AccessControlListFeature(this), 5 * 1024L * 1024L, 5) { + return (T) new S3MultipartUploadService(this, new S3AccessControlListFeature(this), 5 * 1024L * 1024L, 5) { @Override protected InputStream decorate(final InputStream in, final MessageDigest digest) { if(failed.get()) { @@ -223,7 +223,7 @@ public class S3SingleTransferWorkerTest extends AbstractS3Test { @SuppressWarnings("unchecked") public T _getFeature(final Class type) { if(type == Upload.class) { - return (T) new S3SingleUploadService(this, new S3WriteFeature(this, new S3AccessControlListFeature(this))) { + return (T) new S3SingleUploadService(this) { @Override protected InputStream decorate(final InputStream in, final MessageDigest digest) { if(failed.get()) { diff --git a/setup/app/Info.plist b/setup/app/Info.plist index e44ba0c479..7115474ce9 100644 --- a/setup/app/Info.plist +++ b/setup/app/Info.plist @@ -276,6 +276,8 @@ ${SPARKLEFEED} SUEnableAutomaticChecks + NSDockTilePlugIn + Cyberduck Dock Plugin.docktileplugin #endif NSServices diff --git a/setup/app/appstore.provisionprofile b/setup/app/appstore.provisionprofile new file mode 100644 index 0000000000..320b162a21 Binary files /dev/null and b/setup/app/appstore.provisionprofile differ diff --git a/setup/app/sandbox.entitlements b/setup/app/sandbox.entitlements index ed93b48d86..54985478db 100644 --- a/setup/app/sandbox.entitlements +++ b/setup/app/sandbox.entitlements @@ -2,6 +2,10 @@ + com.apple.application-identifier + G69SCX94XU.ch.sudo.cyberduck + com.apple.developer.team-identifier + G69SCX94XU com.apple.security.app-sandbox com.apple.security.files.bookmarks.app-scope diff --git a/setup/cert/certificate.crt b/setup/cert/certificate.crt new file mode 100644 index 0000000000..a3082e1b81 Binary files /dev/null and b/setup/cert/certificate.crt differ diff --git a/smb/pom.xml b/smb/pom.xml index 97ee7758d3..39ee0fd1df 100644 --- a/smb/pom.xml +++ b/smb/pom.xml @@ -18,13 +18,13 @@ ch.cyberduck parent - 9.2.0-SNAPSHOT + 9.4.0-SNAPSHOT smb jar 0.14.0 - 1.21.2 + 2.0.3 diff --git a/smb/src/main/java/ch/cyberduck/core/smb/SMBAttributesFinderFeature.java b/smb/src/main/java/ch/cyberduck/core/smb/SMBAttributesFinderFeature.java index d408039342..adc8ddae20 100644 --- a/smb/src/main/java/ch/cyberduck/core/smb/SMBAttributesFinderFeature.java +++ b/smb/src/main/java/ch/cyberduck/core/smb/SMBAttributesFinderFeature.java @@ -15,6 +15,7 @@ package ch.cyberduck.core.smb; * GNU General Public License for more details. */ +import ch.cyberduck.core.DefaultPathAttributes; import ch.cyberduck.core.ListProgressListener; import ch.cyberduck.core.Path; import ch.cyberduck.core.PathAttributes; @@ -45,7 +46,7 @@ public class SMBAttributesFinderFeature implements AttributesFinder, AttributesA try { if(new SMBPathContainerService(session).isContainer(file)) { final ShareInfo shareInformation = share.get().getShareInformation(); - final PathAttributes attributes = new PathAttributes(); + final PathAttributes attributes = new DefaultPathAttributes(); final long used = shareInformation.getTotalSpace() - shareInformation.getFreeSpace(); attributes.setSize(used); attributes.setQuota(new Quota.Space(used, shareInformation.getFreeSpace())); @@ -72,7 +73,7 @@ public class SMBAttributesFinderFeature implements AttributesFinder, AttributesA @Override public PathAttributes toAttributes(final FileAllInformation model) { - final PathAttributes attributes = new PathAttributes(); + final PathAttributes attributes = new DefaultPathAttributes(); attributes.setAccessedDate(model.getBasicInformation().getLastAccessTime().toEpochMillis()); attributes.setModificationDate(model.getBasicInformation().getLastWriteTime().toEpochMillis()); attributes.setCreationDate(model.getBasicInformation().getCreationTime().toEpochMillis()); diff --git a/smb/src/main/java/ch/cyberduck/core/smb/SMBDeleteFeature.java b/smb/src/main/java/ch/cyberduck/core/smb/SMBDeleteFeature.java index f959938291..7515e58b4a 100644 --- a/smb/src/main/java/ch/cyberduck/core/smb/SMBDeleteFeature.java +++ b/smb/src/main/java/ch/cyberduck/core/smb/SMBDeleteFeature.java @@ -60,7 +60,7 @@ public class SMBDeleteFeature implements Delete { } @Override - public EnumSet features() { + public EnumSet features(final Path file) { return EnumSet.of(Flags.recursive); } diff --git a/smb/src/main/java/ch/cyberduck/core/smb/SMBDirectoryFeature.java b/smb/src/main/java/ch/cyberduck/core/smb/SMBDirectoryFeature.java index 80907b9bab..e7a1817034 100644 --- a/smb/src/main/java/ch/cyberduck/core/smb/SMBDirectoryFeature.java +++ b/smb/src/main/java/ch/cyberduck/core/smb/SMBDirectoryFeature.java @@ -18,11 +18,12 @@ package ch.cyberduck.core.smb; import ch.cyberduck.core.Path; import ch.cyberduck.core.exception.BackgroundException; import ch.cyberduck.core.features.Directory; +import ch.cyberduck.core.features.Write; import ch.cyberduck.core.transfer.TransferStatus; import com.hierynomus.smbj.common.SMBRuntimeException; -public class SMBDirectoryFeature implements Directory { +public class SMBDirectoryFeature implements Directory { private final SMBSession session; @@ -31,7 +32,7 @@ public class SMBDirectoryFeature implements Directory { } @Override - public Path mkdir(final Path folder, final TransferStatus status) throws BackgroundException { + public Path mkdir(final Write writer, final Path folder, final TransferStatus status) throws BackgroundException { final SMBSession.DiskShareWrapper share = session.openShare(folder); try { share.get().mkdir(new SMBPathContainerService(session).getKey(folder)); diff --git a/smb/src/main/java/ch/cyberduck/core/smb/SMBListService.java b/smb/src/main/java/ch/cyberduck/core/smb/SMBListService.java index 74027f0601..4a1f7e65fe 100644 --- a/smb/src/main/java/ch/cyberduck/core/smb/SMBListService.java +++ b/smb/src/main/java/ch/cyberduck/core/smb/SMBListService.java @@ -16,6 +16,7 @@ package ch.cyberduck.core.smb; */ import ch.cyberduck.core.AttributedList; +import ch.cyberduck.core.DefaultPathAttributes; import ch.cyberduck.core.ListProgressListener; import ch.cyberduck.core.ListService; import ch.cyberduck.core.Path; @@ -74,7 +75,7 @@ public class SMBListService implements ListService { else { type.add(Path.Type.file); } - final PathAttributes attr = new PathAttributes(); + final PathAttributes attr = new DefaultPathAttributes(); attr.setAccessedDate(f.getLastAccessTime().toEpochMillis()); attr.setModificationDate(f.getLastWriteTime().toEpochMillis()); attr.setCreationDate(f.getCreationTime().toEpochMillis()); diff --git a/smb/src/main/java/ch/cyberduck/core/smb/SMBSession.java b/smb/src/main/java/ch/cyberduck/core/smb/SMBSession.java index c73f79e94d..a381ac1b1e 100644 --- a/smb/src/main/java/ch/cyberduck/core/smb/SMBSession.java +++ b/smb/src/main/java/ch/cyberduck/core/smb/SMBSession.java @@ -37,7 +37,6 @@ import ch.cyberduck.core.features.Read; import ch.cyberduck.core.features.Timestamp; import ch.cyberduck.core.features.Touch; import ch.cyberduck.core.features.Write; -import ch.cyberduck.core.preferences.HostPreferencesFactory; import ch.cyberduck.core.proxy.ProxyFinder; import ch.cyberduck.core.proxy.ProxySocketFactory; import ch.cyberduck.core.random.SecureRandomProviderFactory; @@ -230,17 +229,17 @@ public class SMBSession extends ch.cyberduck.core.Session { protected Connection connect(final ProxyFinder proxy, final HostKeyCallback key, final LoginCallback prompt, final CancelCallback cancel) throws BackgroundException { try { final SMBClient client = new SMBClient(SmbConfig.builder() - .withWorkStationName(HostPreferencesFactory.get(host).getProperty("smb.ntlm.workstation")) + .withWorkStationName(preferences.getProperty("smb.ntlm.workstation")) .withSocketFactory(new ProxySocketFactory(host)) - .withTimeout(ConnectionTimeoutFactory.get(HostPreferencesFactory.get(host)).getTimeout(), TimeUnit.SECONDS) - .withSoTimeout(HostPreferencesFactory.get(host).getLong("smb.socket.timeout"), TimeUnit.SECONDS) + .withTimeout(ConnectionTimeoutFactory.get(preferences).getTimeout(), TimeUnit.SECONDS) + .withSoTimeout(preferences.getLong("smb.socket.timeout"), TimeUnit.SECONDS) .withAuthenticators(new NtlmAuthenticator.Factory()) - .withDfsEnabled(HostPreferencesFactory.get(host).getBoolean("smb.dfs.enable")) - .withEncryptData(HostPreferencesFactory.get(host).getBoolean("smb.encrypt.enable")) - .withSigningEnabled(HostPreferencesFactory.get(host).getBoolean("smb.signing.enable")) - .withSigningRequired(HostPreferencesFactory.get(host).getBoolean("smb.signing.required")) + .withDfsEnabled(preferences.getBoolean("smb.dfs.enable")) + .withEncryptData(preferences.getBoolean("smb.encrypt.enable")) + .withSigningEnabled(preferences.getBoolean("smb.signing.enable")) + .withSigningRequired(preferences.getBoolean("smb.signing.required")) .withRandomProvider(SecureRandomProviderFactory.get().provide()) - .withMultiProtocolNegotiate(HostPreferencesFactory.get(host).getBoolean("smb.protocol.negotiate.enable")) + .withMultiProtocolNegotiate(preferences.getBoolean("smb.protocol.negotiate.enable")) .withTransportLayerFactory(new AsyncDirectTcpTransportFactory<>()) .build()); final Connection connection = client.connect(getHost().getHostname(), getHost().getPort()); @@ -275,10 +274,11 @@ public class SMBSession extends ch.cyberduck.core.Session { } else { username = credentials.getUsername(); - domain = HostPreferencesFactory.get(host).getProperty("smb.domain.default"); + domain = preferences.getProperty("smb.domain.default"); } context = new AuthenticationContext(username, credentials.getPassword().toCharArray(), domain); } + log.debug("Login with context {}", context); try { shares = new SMBRootListService(this, prompt, session = client.authenticate(context)); } @@ -341,22 +341,25 @@ public class SMBSession extends ch.cyberduck.core.Session { } @Override - protected void logout() throws BackgroundException { - if(session != null) { - try { + public void logout() throws BackgroundException { + try { + if(session != null) { session.logoff(); } - catch(SMBRuntimeException e) { - throw new SMBExceptionMappingService().map(e); - } - catch(TransportException e) { - throw new BackgroundException(e); - } + } + catch(SMBRuntimeException e) { + throw new SMBExceptionMappingService().map(e); + } + catch(TransportException | IllegalStateException e) { + throw new BackgroundException(e); + } + finally { + super.logout(); } } @Override - protected void disconnect() { + protected void disconnect() throws BackgroundException { try { client.close(); } diff --git a/smb/src/main/java/ch/cyberduck/core/smb/SMBTouchFeature.java b/smb/src/main/java/ch/cyberduck/core/smb/SMBTouchFeature.java index 691516d641..339773a146 100644 --- a/smb/src/main/java/ch/cyberduck/core/smb/SMBTouchFeature.java +++ b/smb/src/main/java/ch/cyberduck/core/smb/SMBTouchFeature.java @@ -20,6 +20,6 @@ import ch.cyberduck.core.shared.DefaultTouchFeature; public class SMBTouchFeature extends DefaultTouchFeature { public SMBTouchFeature(final SMBSession session) { - super(new SMBWriteFeature(session)); + super(session); } } diff --git a/smb/src/main/java/ch/cyberduck/core/smb/SMBTransportExceptionMappingService.java b/smb/src/main/java/ch/cyberduck/core/smb/SMBTransportExceptionMappingService.java index 714ac38702..685caa0a00 100644 --- a/smb/src/main/java/ch/cyberduck/core/smb/SMBTransportExceptionMappingService.java +++ b/smb/src/main/java/ch/cyberduck/core/smb/SMBTransportExceptionMappingService.java @@ -18,13 +18,16 @@ import ch.cyberduck.core.AbstractExceptionMappingService; import ch.cyberduck.core.exception.BackgroundException; import ch.cyberduck.core.exception.ConnectionRefusedException; import ch.cyberduck.core.exception.UnsupportedException; +import ch.cyberduck.core.worker.DefaultExceptionMappingService; 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.util.concurrent.ExecutionException; +import com.google.common.base.Throwables; import com.hierynomus.mssmb.SMB1NotSupportedException; import com.hierynomus.smbj.common.SMBRuntimeException; @@ -34,16 +37,19 @@ public class SMBTransportExceptionMappingService extends AbstractExceptionMappin @Override public BackgroundException map(final IOException failure) { log.warn("Map failure {}", failure.toString()); + for(Throwable cause : ExceptionUtils.getThrowableList(failure)) { + if(cause instanceof SMBRuntimeException) { + return new SMBExceptionMappingService().map((SMBRuntimeException) cause); + } + if(cause instanceof ExecutionException) { + return new DefaultExceptionMappingService().map(Throwables.getRootCause(cause)); + } + } final StringBuilder buffer = new StringBuilder(); this.append(buffer, failure.getMessage()); if(failure instanceof SMB1NotSupportedException) { return new UnsupportedException(buffer.toString(), failure); } - for(Throwable cause : ExceptionUtils.getThrowableList(failure)) { - if(cause instanceof SMBRuntimeException) { - return new SMBExceptionMappingService().map((SMBRuntimeException) cause); - } - } return new ConnectionRefusedException(buffer.toString(), failure); } } diff --git a/smb/src/test/java/ch/cyberduck/core/smb/AbstractSMBTest.java b/smb/src/test/java/ch/cyberduck/core/smb/AbstractSMBTest.java index 8f1e593b49..6cd1386f8e 100644 --- a/smb/src/test/java/ch/cyberduck/core/smb/AbstractSMBTest.java +++ b/smb/src/test/java/ch/cyberduck/core/smb/AbstractSMBTest.java @@ -31,7 +31,6 @@ import org.junit.After; import org.junit.AfterClass; import org.junit.Before; import org.junit.BeforeClass; -import org.junit.ClassRule; import org.junit.experimental.categories.Category; import org.testcontainers.containers.GenericContainer; import org.testcontainers.containers.wait.strategy.Wait; @@ -42,8 +41,7 @@ import static org.junit.Assert.fail; @Category(TestcontainerTest.class) public abstract class AbstractSMBTest { - @ClassRule - public static TestContainer container = TestContainer.getInstance(); + private static final TestContainer container = new TestContainer(); @BeforeClass public static void start() { @@ -60,7 +58,7 @@ public abstract class AbstractSMBTest { @Before public void setup() throws BackgroundException { session = new SMBSession(new Host(new SMBProtocol(), container.getHost(), container.getMappedPort(445), "/user") - .withCredentials(new Credentials("WORKGROUP\\smbj", "pass"))); + .setCredentials(new Credentials("WORKGROUP\\smbj", "pass"))); final LoginConnectionService login = new LoginConnectionService(new DisabledLoginCallback() { @Override public Credentials prompt(final Host bookmark, final String title, final String reason, final LoginOptions options) { @@ -101,12 +99,5 @@ public abstract class AbstractSMBTest { addExposedPort(SMB_PORT); waitingFor(Wait.forListeningPort()); } - - static TestContainer getInstance() { - if(instance == null) { - instance = new TestContainer(); - } - return instance; - } } } diff --git a/smb/src/test/java/ch/cyberduck/core/smb/SMBAttributesFinderFeatureTest.java b/smb/src/test/java/ch/cyberduck/core/smb/SMBAttributesFinderFeatureTest.java index 922da9e7c4..a09e88a33b 100644 --- a/smb/src/test/java/ch/cyberduck/core/smb/SMBAttributesFinderFeatureTest.java +++ b/smb/src/test/java/ch/cyberduck/core/smb/SMBAttributesFinderFeatureTest.java @@ -52,7 +52,7 @@ public class SMBAttributesFinderFeatureTest extends AbstractSMBTest { @Test public void testFindFile() throws Exception { - final Path test = new SMBTouchFeature(session).touch(new Path(new DefaultHomeFinderService(session).find(), + final Path test = new SMBTouchFeature(session).touch(new SMBWriteFeature(session), new Path(new DefaultHomeFinderService(session).find(), new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)), new TransferStatus()); final SMBAttributesFinderFeature f = new SMBAttributesFinderFeature(session); final PathAttributes attributes = f.find(test); @@ -65,7 +65,7 @@ public class SMBAttributesFinderFeatureTest extends AbstractSMBTest { @Test public void testFindDirectory() throws Exception { - final Path test = new SMBDirectoryFeature(session).mkdir(new Path(new DefaultHomeFinderService(session).find(), + final Path test = new SMBDirectoryFeature(session).mkdir(new SMBWriteFeature(session), new Path(new DefaultHomeFinderService(session).find(), new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory)), new TransferStatus()); final SMBAttributesFinderFeature f = new SMBAttributesFinderFeature(session); final PathAttributes attributes = f.find(test); diff --git a/smb/src/test/java/ch/cyberduck/core/smb/SMBCopyFeatureTest.java b/smb/src/test/java/ch/cyberduck/core/smb/SMBCopyFeatureTest.java index 0cdc375267..c364f1a459 100644 --- a/smb/src/test/java/ch/cyberduck/core/smb/SMBCopyFeatureTest.java +++ b/smb/src/test/java/ch/cyberduck/core/smb/SMBCopyFeatureTest.java @@ -43,11 +43,11 @@ public class SMBCopyFeatureTest extends AbstractSMBTest { @Test public void testCopyFile() throws Exception { final Path home = new DefaultHomeFinderService(session).find(); - final Path file = new SMBTouchFeature(session).touch(new Path(home, + final Path file = new SMBTouchFeature(session).touch(new SMBWriteFeature(session), new Path(home, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)), new TransferStatus()); final PathAttributes attr = new SMBAttributesFinderFeature(session).find(file); final Path destinationFolder = new SMBDirectoryFeature(session).mkdir( - new Path(home, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory)), new TransferStatus()); + new SMBWriteFeature(session), new Path(home, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory)), new TransferStatus()); final Path copy = new Path(destinationFolder, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)); final Path fileCopied = new SMBCopyFeature(session).copy(file, copy, new TransferStatus(), new DisabledConnectionCallback(), new DisabledStreamListener()); assertNotEquals(attr, new SMBAttributesFinderFeature(session).find(fileCopied)); @@ -61,13 +61,13 @@ public class SMBCopyFeatureTest extends AbstractSMBTest { public void testCopyToExistingFile() throws Exception { final Path home = new DefaultHomeFinderService(session).find(); final Path sourceFolder = new SMBDirectoryFeature(session).mkdir( - new Path(home, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory)), new TransferStatus()); + new SMBWriteFeature(session), new Path(home, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory)), new TransferStatus()); final Path destinationFolder = new SMBDirectoryFeature(session).mkdir( - new Path(home, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory)), new TransferStatus()); - final Path file = new SMBTouchFeature(session).touch(new Path(sourceFolder, + new SMBWriteFeature(session), new Path(home, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory)), new TransferStatus()); + final Path file = new SMBTouchFeature(session).touch(new SMBWriteFeature(session), new Path(sourceFolder, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)), new TransferStatus()); final Path copy = new Path(destinationFolder, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)); - new SMBTouchFeature(session).touch(copy, new TransferStatus()); + new SMBTouchFeature(session).touch(new SMBWriteFeature(session), copy, new TransferStatus()); new SMBCopyFeature(session).copy(file, copy, new TransferStatus().setExists(true), new DisabledConnectionCallback(), new DisabledStreamListener()); ListService list = new SMBListService(session); assertTrue(list.list(sourceFolder, new DisabledListProgressListener()).contains(file)); diff --git a/smb/src/test/java/ch/cyberduck/core/smb/SMBDeleteFeatureTest.java b/smb/src/test/java/ch/cyberduck/core/smb/SMBDeleteFeatureTest.java index f9f3f649e9..4c2f4e6fb5 100644 --- a/smb/src/test/java/ch/cyberduck/core/smb/SMBDeleteFeatureTest.java +++ b/smb/src/test/java/ch/cyberduck/core/smb/SMBDeleteFeatureTest.java @@ -42,9 +42,9 @@ public class SMBDeleteFeatureTest extends AbstractSMBTest { public void testDeleteFile() throws Exception { final Path home = new DefaultHomeFinderService(session).find(); final Path folder = new SMBDirectoryFeature(session).mkdir( - new Path(home, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory)), new TransferStatus()); + new SMBWriteFeature(session), new Path(home, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory)), new TransferStatus()); final Path file = new SMBTouchFeature(session).touch( - new Path(folder, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)), new TransferStatus()); + new SMBWriteFeature(session), new Path(folder, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)), new TransferStatus()); assertTrue(new SMBListService(session).list(folder, new DisabledListProgressListener()).contains(file)); new SMBDeleteFeature(session).delete(Collections.singletonList(file), new DisabledPasswordCallback(), new Delete.DisabledCallback()); assertFalse(new DefaultFindFeature(session).find(file, new DisabledListProgressListener())); @@ -55,7 +55,7 @@ public class SMBDeleteFeatureTest extends AbstractSMBTest { public void testDeleteFolder() throws Exception { final Path home = new DefaultHomeFinderService(session).find(); final Path folder = new SMBDirectoryFeature(session).mkdir( - new Path(home, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory)), new TransferStatus()); + new SMBWriteFeature(session), new Path(home, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory)), new TransferStatus()); assertTrue(new SMBListService(session).list(home, new DisabledListProgressListener()).contains(folder)); new SMBDeleteFeature(session).delete(Collections.singletonList(folder), new DisabledPasswordCallback(), new Delete.DisabledCallback()); assertFalse(new DefaultFindFeature(session).find(folder, new DisabledListProgressListener())); @@ -65,9 +65,9 @@ public class SMBDeleteFeatureTest extends AbstractSMBTest { public void testDeleteFileAndFolder() throws Exception { final Path home = new DefaultHomeFinderService(session).find(); final Path folder = new SMBDirectoryFeature(session).mkdir( - new Path(home, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory)), new TransferStatus()); + new SMBWriteFeature(session), new Path(home, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory)), new TransferStatus()); final Path file = new SMBTouchFeature(session).touch( - new Path(home, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)), new TransferStatus()); + new SMBWriteFeature(session), new Path(home, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)), new TransferStatus()); assertTrue(new SMBFindFeature(session).find(file)); new SMBDeleteFeature(session).delete(Arrays.asList(file, folder), new DisabledPasswordCallback(), new Delete.DisabledCallback()); assertFalse(new SMBFindFeature(session).find(file)); diff --git a/smb/src/test/java/ch/cyberduck/core/smb/SMBDirectoryFeatureTest.java b/smb/src/test/java/ch/cyberduck/core/smb/SMBDirectoryFeatureTest.java index 357b39dba6..c9f555eb12 100644 --- a/smb/src/test/java/ch/cyberduck/core/smb/SMBDirectoryFeatureTest.java +++ b/smb/src/test/java/ch/cyberduck/core/smb/SMBDirectoryFeatureTest.java @@ -39,9 +39,9 @@ public class SMBDirectoryFeatureTest extends AbstractSMBTest { @Test public void testMakeDirectory() throws Exception { final Path test = new SMBDirectoryFeature(session).mkdir( - new Path(new DefaultHomeFinderService(session).find(), new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory)), new TransferStatus()); + new SMBWriteFeature(session), new Path(new DefaultHomeFinderService(session).find(), new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory)), new TransferStatus()); assertTrue(new SMBFindFeature(session).find(test)); - assertThrows(ConflictException.class, () -> new SMBDirectoryFeature(session).mkdir(test, new TransferStatus())); + assertThrows(ConflictException.class, () -> new SMBDirectoryFeature(session).mkdir(new SMBWriteFeature(session), test, new TransferStatus())); new SMBDeleteFeature(session).delete(Collections.singletonList(test), new DisabledLoginCallback(), new Delete.DisabledCallback()); } } diff --git a/smb/src/test/java/ch/cyberduck/core/smb/SMBFindFeatureTest.java b/smb/src/test/java/ch/cyberduck/core/smb/SMBFindFeatureTest.java index b813e16201..25612497af 100644 --- a/smb/src/test/java/ch/cyberduck/core/smb/SMBFindFeatureTest.java +++ b/smb/src/test/java/ch/cyberduck/core/smb/SMBFindFeatureTest.java @@ -50,7 +50,7 @@ public class SMBFindFeatureTest extends AbstractSMBTest { @Test public void testFindFile() throws Exception { final Path file = new Path(new DefaultHomeFinderService(session).find(), new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)); - new SMBTouchFeature(session).touch(file, new TransferStatus()); + new SMBTouchFeature(session).touch(new SMBWriteFeature(session), file, new TransferStatus()); assertTrue(new SMBFindFeature(session).find(file)); assertFalse(new SMBFindFeature(session).find(new Path(file.getAbsolute(), EnumSet.of(Path.Type.directory)))); new SMBDeleteFeature(session).delete(Collections.singletonList(file), new DisabledLoginCallback(), new Delete.DisabledCallback()); diff --git a/smb/src/test/java/ch/cyberduck/core/smb/SMBListServiceTest.java b/smb/src/test/java/ch/cyberduck/core/smb/SMBListServiceTest.java index da12bcc6b4..09a8ce7ac8 100644 --- a/smb/src/test/java/ch/cyberduck/core/smb/SMBListServiceTest.java +++ b/smb/src/test/java/ch/cyberduck/core/smb/SMBListServiceTest.java @@ -48,11 +48,11 @@ public class SMBListServiceTest extends AbstractSMBTest { public void testList() throws Exception { final Path home = new DefaultHomeFinderService(session).find(); final Path testFolder = new SMBDirectoryFeature(session).mkdir( - new Path(home, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory)), new TransferStatus()); - final Path testFile = new SMBTouchFeature(session).touch(new Path(testFolder, + new SMBWriteFeature(session), new Path(home, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory)), new TransferStatus()); + final Path testFile = new SMBTouchFeature(session).touch(new SMBWriteFeature(session), new Path(testFolder, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)), new TransferStatus()); final Path innerFolder = new SMBDirectoryFeature(session).mkdir( - new Path(testFolder, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory)), new TransferStatus()); + new SMBWriteFeature(session), new Path(testFolder, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory)), new TransferStatus()); final AttributedList result = new SMBListService(session).list(testFolder, new DisabledListProgressListener()); assertEquals(2, result.size()); assertTrue(result.contains(testFile)); @@ -64,7 +64,7 @@ public class SMBListServiceTest extends AbstractSMBTest { public void testListEmptyFolder() throws Exception { final Path home = new DefaultHomeFinderService(session).find(); final Path emptyFolder = new SMBDirectoryFeature(session).mkdir( - new Path(home, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory)), new TransferStatus()); + new SMBWriteFeature(session), new Path(home, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory)), new TransferStatus()); final AttributedList result = new SMBListService(session).list(emptyFolder, new DisabledListProgressListener()); assertEquals(0, result.size()); new SMBDeleteFeature(session).delete(Collections.singletonList(emptyFolder), new DisabledLoginCallback(), new Delete.DisabledCallback()); diff --git a/smb/src/test/java/ch/cyberduck/core/smb/SMBMoveFeatureTest.java b/smb/src/test/java/ch/cyberduck/core/smb/SMBMoveFeatureTest.java index 5cf283efbf..119db52f5a 100644 --- a/smb/src/test/java/ch/cyberduck/core/smb/SMBMoveFeatureTest.java +++ b/smb/src/test/java/ch/cyberduck/core/smb/SMBMoveFeatureTest.java @@ -41,9 +41,9 @@ public class SMBMoveFeatureTest extends AbstractSMBTest { public void testRename() throws Exception { final Path home = new DefaultHomeFinderService(session).find(); final Path folder = new SMBDirectoryFeature(session).mkdir( - new Path(home, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory)), new TransferStatus()); + new SMBWriteFeature(session), new Path(home, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory)), new TransferStatus()); final Path file = new SMBTouchFeature(session).touch( - new Path(folder, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)), new TransferStatus()); + new SMBWriteFeature(session), new Path(folder, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)), new TransferStatus()); // rename file final Path fileRenamed = new SMBMoveFeature(session).move(file, new Path(folder, @@ -69,9 +69,9 @@ public class SMBMoveFeatureTest extends AbstractSMBTest { public void testRenameFileOverride() throws Exception { final Path home = new DefaultHomeFinderService(session).find(); final Path file = new SMBTouchFeature(session).touch( - new Path(home, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)), new TransferStatus()); + new SMBWriteFeature(session), new Path(home, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)), new TransferStatus()); final Path target = new SMBTouchFeature(session).touch( - new Path(home, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)), new TransferStatus()); + new SMBWriteFeature(session), new Path(home, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)), new TransferStatus()); // rename file final Path fileRenamed = new SMBMoveFeature(session).move(file, target, new TransferStatus().setExists(true), new Delete.DisabledCallback(), new DisabledConnectionCallback()); @@ -92,7 +92,7 @@ public class SMBMoveFeatureTest extends AbstractSMBTest { public void testRenameCaseOnly() throws Exception { final Path home = new DefaultHomeFinderService(session).find(); final String name = new AlphanumericRandomStringService().random(); - final Path file = new SMBTouchFeature(session).touch(new Path(home, StringUtils.upperCase(name), EnumSet.of(Path.Type.file)), new TransferStatus()); + final Path file = new SMBTouchFeature(session).touch(new SMBWriteFeature(session), new Path(home, StringUtils.upperCase(name), EnumSet.of(Path.Type.file)), new TransferStatus()); final Path rename = new Path(home, StringUtils.lowerCase(name), EnumSet.of(Path.Type.file)); new SMBMoveFeature(session).move(file, rename, new TransferStatus().setExists(true), new Delete.DisabledCallback(), new DisabledConnectionCallback()); new SMBDeleteFeature(session).delete(Collections.singletonList(rename), new DisabledLoginCallback(), new Delete.DisabledCallback()); diff --git a/smb/src/test/java/ch/cyberduck/core/smb/SMBReadFeatureTest.java b/smb/src/test/java/ch/cyberduck/core/smb/SMBReadFeatureTest.java index 0b30e72950..b4b14e3450 100644 --- a/smb/src/test/java/ch/cyberduck/core/smb/SMBReadFeatureTest.java +++ b/smb/src/test/java/ch/cyberduck/core/smb/SMBReadFeatureTest.java @@ -65,9 +65,9 @@ public class SMBReadFeatureTest extends AbstractSMBTest { status.setLength(content.length); final Path home = new DefaultHomeFinderService(session).find(); final Path folder = new SMBDirectoryFeature(session).mkdir( - new Path(home, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory)), new TransferStatus()); + new SMBWriteFeature(session), new Path(home, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory)), new TransferStatus()); final Path test = new SMBTouchFeature(session).touch( - new Path(folder, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)), new TransferStatus()); + new SMBWriteFeature(session), new Path(folder, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)), new TransferStatus()); final Write writer = new SMBWriteFeature(session); status.setChecksum(writer.checksum(test, status).compute(new ByteArrayInputStream(content), status)); final OutputStream out = writer.write(test, status.setExists(true), new DisabledConnectionCallback()); @@ -128,9 +128,9 @@ public class SMBReadFeatureTest extends AbstractSMBTest { final byte[] content = RandomUtils.nextBytes(length); status.setLength(content.length); final Path folder = new SMBDirectoryFeature(session).mkdir( - new Path(home, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory)), new TransferStatus()); + new SMBWriteFeature(session), new Path(home, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory)), new TransferStatus()); final Path test = new SMBTouchFeature(session).touch( - new Path(folder, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)), new TransferStatus()); + new SMBWriteFeature(session), new Path(folder, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)), new TransferStatus()); final Write writer = new SMBWriteFeature(session); status.setChecksum(writer.checksum(test, status).compute(new ByteArrayInputStream(content), status)); final OutputStream out = writer.write(test, status.setExists(true), new DisabledConnectionCallback()); diff --git a/smb/src/test/java/ch/cyberduck/core/smb/SMBRootListServiceTest.java b/smb/src/test/java/ch/cyberduck/core/smb/SMBRootListServiceTest.java index f400e18cbf..65c005c855 100644 --- a/smb/src/test/java/ch/cyberduck/core/smb/SMBRootListServiceTest.java +++ b/smb/src/test/java/ch/cyberduck/core/smb/SMBRootListServiceTest.java @@ -39,6 +39,5 @@ public class SMBRootListServiceTest extends AbstractSMBTest { assertNotEquals(TransferStatus.UNKNOWN_LENGTH, f.attributes().getSize()); assertNotEquals(Quota.unknown, f.attributes().getQuota()); } - session.close(); } } \ No newline at end of file diff --git a/smb/src/test/java/ch/cyberduck/core/smb/SMBSessionTest.java b/smb/src/test/java/ch/cyberduck/core/smb/SMBSessionTest.java index 936c77a4e7..4ec16ce05d 100644 --- a/smb/src/test/java/ch/cyberduck/core/smb/SMBSessionTest.java +++ b/smb/src/test/java/ch/cyberduck/core/smb/SMBSessionTest.java @@ -33,7 +33,6 @@ import ch.cyberduck.core.features.Touch; import ch.cyberduck.core.features.UnixPermission; import ch.cyberduck.core.features.Write; import ch.cyberduck.core.proxy.DisabledProxyFinder; -import ch.cyberduck.core.proxy.Proxy; import ch.cyberduck.test.TestcontainerTest; import org.junit.Test; @@ -47,7 +46,7 @@ public class SMBSessionTest extends AbstractSMBTest { @Test public void testConnectRefused() { final Host host = new Host(new SMBProtocol(), session.getHost().getHostname(), 135) - .withCredentials(session.getHost().getCredentials()); + .setCredentials(session.getHost().getCredentials()); final SMBSession session = new SMBSession(host); assertThrows(ConnectionRefusedException.class, () -> session.open(new DisabledProxyFinder(), new DisabledHostKeyCallback(), new DisabledLoginCallback(), new DisabledCancelCallback())); } diff --git a/smb/src/test/java/ch/cyberduck/core/smb/SMBTimestampFeatureTest.java b/smb/src/test/java/ch/cyberduck/core/smb/SMBTimestampFeatureTest.java index 0363176aa5..d5ab7d1697 100644 --- a/smb/src/test/java/ch/cyberduck/core/smb/SMBTimestampFeatureTest.java +++ b/smb/src/test/java/ch/cyberduck/core/smb/SMBTimestampFeatureTest.java @@ -82,7 +82,7 @@ public class SMBTimestampFeatureTest extends AbstractSMBTest { final TransferStatus status = new TransferStatus(); final Path home = new DefaultHomeFinderService(session).find(); final Path f = new SMBDirectoryFeature(session).mkdir( - new Path(home, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory)), new TransferStatus()); + new SMBWriteFeature(session), new Path(home, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory)), new TransferStatus()); // make sure timestamps are different long oldTime = new SMBAttributesFinderFeature(session).find(f).getModificationDate(); status.setModified(oldTime + 2000); diff --git a/smb/src/test/java/ch/cyberduck/core/smb/SMBTouchFeatureTest.java b/smb/src/test/java/ch/cyberduck/core/smb/SMBTouchFeatureTest.java index 7f291be6fc..b14185ec95 100644 --- a/smb/src/test/java/ch/cyberduck/core/smb/SMBTouchFeatureTest.java +++ b/smb/src/test/java/ch/cyberduck/core/smb/SMBTouchFeatureTest.java @@ -42,9 +42,9 @@ public class SMBTouchFeatureTest extends AbstractSMBTest { final Path home = new DefaultHomeFinderService(session).find(); final String filename = StringUtils.lowerCase(new AlphanumericRandomStringService().random()); final Path test = new SMBTouchFeature(session) - .touch(new Path(home, filename, EnumSet.of(Path.Type.file)), new TransferStatus().setLength(0L)); + .touch(new SMBWriteFeature(session), new Path(home, filename, EnumSet.of(Path.Type.file)), new TransferStatus().setLength(0L)); assertThrows(ConflictException.class, () -> new SMBTouchFeature(session) - .touch(new Path(home, StringUtils.upperCase(filename), EnumSet.of(Path.Type.file)), new TransferStatus().setLength(0L))); + .touch(new SMBWriteFeature(session), new Path(home, StringUtils.upperCase(filename), EnumSet.of(Path.Type.file)), new TransferStatus().setLength(0L))); assertTrue(new SMBFindFeature(session).find(test)); assertTrue(new SMBFindFeature(session).find(new Path(home, StringUtils.upperCase(filename), EnumSet.of(Path.Type.file)))); assertEquals(new SMBAttributesFinderFeature(session).find(test), @@ -56,7 +56,7 @@ public class SMBTouchFeatureTest extends AbstractSMBTest { public void testTouchLongFilename() throws Exception { final Path home = new DefaultHomeFinderService(session).find(); final Path test = new SMBTouchFeature(session).touch( - new Path(home, new AlphanumericRandomStringService(130).random(), EnumSet.of(Path.Type.file)), new TransferStatus()); + new SMBWriteFeature(session), new Path(home, new AlphanumericRandomStringService(130).random(), EnumSet.of(Path.Type.file)), new TransferStatus()); new SMBDeleteFeature(session).delete(Collections.singletonList(test), new DisabledLoginCallback(), new Delete.DisabledCallback()); } } diff --git a/smb/src/test/java/ch/cyberduck/core/smb/SMBTransportExceptionMappingServiceTest.java b/smb/src/test/java/ch/cyberduck/core/smb/SMBTransportExceptionMappingServiceTest.java index 7a52bed263..19a6ddd5ed 100644 --- a/smb/src/test/java/ch/cyberduck/core/smb/SMBTransportExceptionMappingServiceTest.java +++ b/smb/src/test/java/ch/cyberduck/core/smb/SMBTransportExceptionMappingServiceTest.java @@ -22,6 +22,7 @@ import ch.cyberduck.core.exception.ConnectionTimeoutException; import org.junit.Test; import java.io.EOFException; +import java.net.SocketException; import java.util.concurrent.ExecutionException; import java.util.concurrent.TimeoutException; @@ -33,23 +34,29 @@ import static org.junit.Assert.assertEquals; public class SMBTransportExceptionMappingServiceTest { @Test - public void map() { - { - final BackgroundException f = new SMBTransportExceptionMappingService().map( - new TransportException(new ExecutionException(new SMBRuntimeException(new TimeoutException("Timeout expired"))))); - assertEquals("Connection failed", f.getMessage()); - assertEquals(ConnectionTimeoutException.class, f.getClass()); - } - { - final BackgroundException f = new SMBTransportExceptionMappingService().map(new TransportException(new ExecutionException(new SMBRuntimeException(new TimeoutException("Timeout expired"))))); - assertEquals("Timeout expired. The connection attempt timed out. The server may be down, or your network may not be properly configured.", - f.getDetail()); - assertEquals(ConnectionTimeoutException.class, f.getClass()); - } + public void testTimeoutExpired() { + final TransportException failure = new TransportException(new ExecutionException(new SMBRuntimeException(new TimeoutException("Timeout expired")))); + final BackgroundException f = new SMBTransportExceptionMappingService().map(failure); + assertEquals("Connection failed", f.getMessage()); + assertEquals("Timeout expired. The connection attempt timed out. The server may be down, or your network may not be properly configured.", f.getDetail()); + assertEquals(ConnectionTimeoutException.class, f.getClass()); + } + + @Test + public void testPacketEof() { // com.hierynomus.smbj.common.SMBRuntimeException: com.hierynomus.protocol.transport.TransportException: java.io.EOFException: EOF while reading packet - { - final BackgroundException f = new SMBTransportExceptionMappingService().map(new TransportException(new EOFException("EOF while reading packet"))); - assertEquals(ConnectionRefusedException.class, f.getClass()); - } + final TransportException failure = new TransportException(new EOFException("EOF while reading packet")); + final BackgroundException f = new SMBTransportExceptionMappingService().map(failure); + assertEquals(ConnectionRefusedException.class, f.getClass()); + } + + @Test + public void testConnectException() { + // com.hierynomus.smbj.common.SMBRuntimeException: com.hierynomus.protocol.transport.TransportException: java.io.EOFException: EOF while reading packet + final TransportException failure = new TransportException(new ExecutionException(new SocketException("Network is unreachable"))); + final BackgroundException f = new SMBTransportExceptionMappingService().map(failure); + assertEquals(ConnectionRefusedException.class, f.getClass()); + assertEquals("Connection failed", f.getMessage()); + assertEquals("Network is unreachable. The connection attempt was rejected. The server may be down, or your network may not be properly configured.", f.getDetail()); } } \ No newline at end of file diff --git a/spectra/pom.xml b/spectra/pom.xml index d226579abe..d14d3fab1a 100644 --- a/spectra/pom.xml +++ b/spectra/pom.xml @@ -18,7 +18,7 @@ ch.cyberduck parent - 9.2.0-SNAPSHOT + 9.4.0-SNAPSHOT spectra jar diff --git a/spectra/src/main/java/ch/cyberduck/core/spectra/SpectraAttributesFinderFeature.java b/spectra/src/main/java/ch/cyberduck/core/spectra/SpectraAttributesFinderFeature.java index 2092c7ae00..4bb530263c 100644 --- a/spectra/src/main/java/ch/cyberduck/core/spectra/SpectraAttributesFinderFeature.java +++ b/spectra/src/main/java/ch/cyberduck/core/spectra/SpectraAttributesFinderFeature.java @@ -16,6 +16,7 @@ package ch.cyberduck.core.spectra; */ import ch.cyberduck.core.DefaultIOExceptionMappingService; +import ch.cyberduck.core.DefaultPathAttributes; import ch.cyberduck.core.ListProgressListener; import ch.cyberduck.core.Path; import ch.cyberduck.core.PathAttributes; @@ -80,7 +81,7 @@ public class SpectraAttributesFinderFeature implements AttributesFinder, Attribu @Override public PathAttributes toAttributes(final HeadObjectResponse object) { - final PathAttributes attributes = new PathAttributes(); + final PathAttributes attributes = new DefaultPathAttributes(); attributes.setSize(object.getObjectSize()); final Map metadata = new HashMap<>(); for(String key : object.getMetadata().keys()) { diff --git a/spectra/src/main/java/ch/cyberduck/core/spectra/SpectraBulkService.java b/spectra/src/main/java/ch/cyberduck/core/spectra/SpectraBulkService.java index d60d0b1a73..dbf2ec2c0f 100644 --- a/spectra/src/main/java/ch/cyberduck/core/spectra/SpectraBulkService.java +++ b/spectra/src/main/java/ch/cyberduck/core/spectra/SpectraBulkService.java @@ -75,7 +75,6 @@ public class SpectraBulkService implements Bulk> { private static final Logger log = LogManager.getLogger(SpectraBulkService.class); private final SpectraSession session; - private Delete delete; private final PathContainerService containerService; private static final String REQUEST_PARAMETER_JOBID_IDENTIFIER = "job"; @@ -83,16 +82,9 @@ public class SpectraBulkService implements Bulk> { public SpectraBulkService(final SpectraSession session) { this.session = session; - this.delete = new SpectraDeleteFeature(session); this.containerService = new S3PathContainerService(session.getHost()); } - @Override - public Bulk> withDelete(final Delete delete) { - this.delete = delete; - return this; - } - @Override public void post(final Transfer.Type type, final Map files, final ConnectionCallback callback) { // @@ -123,7 +115,7 @@ public class SpectraBulkService implements Bulk> { case upload: if(status.isExists()) { log.warn("Delete existing file {}", file); - delete.delete(Collections.singletonMap(file, status), callback, new Delete.DisabledCallback()); + session.getFeature(Delete.class).delete(Collections.singletonMap(file, status), callback, new Delete.DisabledCallback()); } break; } diff --git a/spectra/src/main/java/ch/cyberduck/core/spectra/SpectraDeleteFeature.java b/spectra/src/main/java/ch/cyberduck/core/spectra/SpectraDeleteFeature.java index 841993b800..dcc2ae47c9 100644 --- a/spectra/src/main/java/ch/cyberduck/core/spectra/SpectraDeleteFeature.java +++ b/spectra/src/main/java/ch/cyberduck/core/spectra/SpectraDeleteFeature.java @@ -95,7 +95,7 @@ public class SpectraDeleteFeature implements Delete { } @Override - public EnumSet features() { + public EnumSet features(final Path file) { return EnumSet.of(Flags.recursive); } } diff --git a/spectra/src/main/java/ch/cyberduck/core/spectra/SpectraDirectoryFeature.java b/spectra/src/main/java/ch/cyberduck/core/spectra/SpectraDirectoryFeature.java index 344934d2c5..2ea205fba4 100644 --- a/spectra/src/main/java/ch/cyberduck/core/spectra/SpectraDirectoryFeature.java +++ b/spectra/src/main/java/ch/cyberduck/core/spectra/SpectraDirectoryFeature.java @@ -30,22 +30,20 @@ import org.jets3t.service.model.StorageObject; public class SpectraDirectoryFeature extends S3DirectoryFeature { private final PathContainerService containerService; - private final Write writer; - public SpectraDirectoryFeature(final SpectraSession session, final Write writer) { - super(session, writer, new S3AccessControlListFeature(session)); + public SpectraDirectoryFeature(final SpectraSession session) { + super(session, new S3AccessControlListFeature(session)); this.containerService = new S3PathContainerService(session.getHost()); - this.writer = writer; } @Override - public Path mkdir(final Path folder, final TransferStatus status) throws BackgroundException { + public Path mkdir(final Write writer, final Path folder, final TransferStatus status) throws BackgroundException { if(containerService.isContainer(folder)) { - return super.mkdir(folder, status); + return super.mkdir(writer, folder, status); } else { status.setChecksum(writer.checksum(folder, status).compute(new NullInputStream(0L), status)); - return super.mkdir(folder, status); + return super.mkdir(writer, folder, status); } } } diff --git a/spectra/src/main/java/ch/cyberduck/core/spectra/SpectraObjectListService.java b/spectra/src/main/java/ch/cyberduck/core/spectra/SpectraObjectListService.java index 2cfe6a9c75..ea9f2661f0 100644 --- a/spectra/src/main/java/ch/cyberduck/core/spectra/SpectraObjectListService.java +++ b/spectra/src/main/java/ch/cyberduck/core/spectra/SpectraObjectListService.java @@ -17,6 +17,7 @@ package ch.cyberduck.core.spectra; import ch.cyberduck.core.AttributedList; import ch.cyberduck.core.DefaultIOExceptionMappingService; +import ch.cyberduck.core.DefaultPathAttributes; import ch.cyberduck.core.ListProgressListener; import ch.cyberduck.core.Path; import ch.cyberduck.core.PathAttributes; @@ -118,11 +119,11 @@ public class SpectraObjectListService extends S3AbstractListService { lastKey = key; } for(CommonPrefixes common : response.getListBucketResult().getCommonPrefixes()) { - final String key = StringUtils.chomp(common.getPrefix(), String.valueOf(Path.DELIMITER)); + final String key = StringUtils.removeEnd(common.getPrefix(), String.valueOf(Path.DELIMITER)); if(new Path(bucket, key, EnumSet.of(Path.Type.directory)).equals(directory)) { continue; } - objects.add(new Path(directory, PathNormalizer.name(key), EnumSet.of(Path.Type.directory, Path.Type.placeholder), new PathAttributes())); + objects.add(new Path(directory, PathNormalizer.name(key), EnumSet.of(Path.Type.directory, Path.Type.placeholder), new DefaultPathAttributes())); } marker = response.getListBucketResult().getNextMarker(); listener.chunk(directory, objects); @@ -145,7 +146,7 @@ public class SpectraObjectListService extends S3AbstractListService { @NotNull private PathAttributes toAttributes(final Contents object) { - final PathAttributes attr = new PathAttributes(); + final PathAttributes attr = new DefaultPathAttributes(); attr.setETag(object.getETag()); attr.setModificationDate(object.getLastModified().getTime()); attr.setOwner(object.getOwner().getDisplayName()); diff --git a/spectra/src/main/java/ch/cyberduck/core/spectra/SpectraSession.java b/spectra/src/main/java/ch/cyberduck/core/spectra/SpectraSession.java index b974818ec6..fa6d5e5343 100644 --- a/spectra/src/main/java/ch/cyberduck/core/spectra/SpectraSession.java +++ b/spectra/src/main/java/ch/cyberduck/core/spectra/SpectraSession.java @@ -67,7 +67,7 @@ public class SpectraSession extends S3Session { return (T) new SpectraTouchFeature(this); } if(type == Directory.class) { - return (T) new SpectraDirectoryFeature(this, new SpectraWriteFeature(this)); + return (T) new SpectraDirectoryFeature(this); } if(type == Move.class) { // Disable operation not supported @@ -103,10 +103,10 @@ public class SpectraSession extends S3Session { return (T) new SpectraReadFeature(this, new SpectraBulkService(this)); } if(type == Upload.class) { - return (T) new SpectraUploadFeature(this, new SpectraWriteFeature(this), new SpectraBulkService(this)); + return (T) new SpectraUploadFeature(new SpectraBulkService(this)); } if(type == Download.class) { - return (T) new DefaultDownloadFeature(new SpectraReadFeature(this, new SpectraBulkService(this))); + return (T) new DefaultDownloadFeature(this); } if(type == Headers.class || type == Metadata.class) { return null; diff --git a/spectra/src/main/java/ch/cyberduck/core/spectra/SpectraTouchFeature.java b/spectra/src/main/java/ch/cyberduck/core/spectra/SpectraTouchFeature.java index 54428dfb0c..d9428d6f2b 100644 --- a/spectra/src/main/java/ch/cyberduck/core/spectra/SpectraTouchFeature.java +++ b/spectra/src/main/java/ch/cyberduck/core/spectra/SpectraTouchFeature.java @@ -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.Transfer; import ch.cyberduck.core.transfer.TransferItem; @@ -34,15 +35,15 @@ public class SpectraTouchFeature extends DefaultTouchFeature { private final SpectraSession session; public SpectraTouchFeature(final SpectraSession session) { - super(new SpectraWriteFeature(session)); + super(session); this.session = session; } @Override - public Path touch(final Path file, final TransferStatus status) throws BackgroundException { + public Path touch(final Write writer, final Path file, final TransferStatus status) throws BackgroundException { final SpectraBulkService bulk = new SpectraBulkService(session); bulk.pre(Transfer.Type.upload, Collections.singletonMap(new TransferItem(file), status), new DisabledConnectionCallback()); - return super.touch(file, status); + return super.touch(writer, file, status); } @Override diff --git a/spectra/src/main/java/ch/cyberduck/core/spectra/SpectraUploadFeature.java b/spectra/src/main/java/ch/cyberduck/core/spectra/SpectraUploadFeature.java index 3ab8e38161..50b15aa60f 100644 --- a/spectra/src/main/java/ch/cyberduck/core/spectra/SpectraUploadFeature.java +++ b/spectra/src/main/java/ch/cyberduck/core/spectra/SpectraUploadFeature.java @@ -20,7 +20,6 @@ import ch.cyberduck.core.Local; import ch.cyberduck.core.Path; import ch.cyberduck.core.ProgressListener; import ch.cyberduck.core.exception.BackgroundException; -import ch.cyberduck.core.features.Upload; import ch.cyberduck.core.features.Write; import ch.cyberduck.core.http.HttpUploadFeature; import ch.cyberduck.core.io.BandwidthThrottle; @@ -38,17 +37,14 @@ import java.util.List; public class SpectraUploadFeature extends HttpUploadFeature { - private Write writer; private final SpectraBulkService bulk; - public SpectraUploadFeature(final SpectraSession session, final Write writer, final SpectraBulkService bulk) { - super(writer); - this.writer = writer; + public SpectraUploadFeature(final SpectraBulkService bulk) { this.bulk = bulk; } @Override - public StorageObject upload(final Path file, final Local local, final BandwidthThrottle throttle, + public StorageObject upload(final Write 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(Checksum.NONE == status.getChecksum()) { // The client-side checksum is passed to the BlackPearl gateway by supplying the applicable CRC HTTP header. @@ -57,21 +53,16 @@ public class SpectraUploadFeature extends HttpUploadFeature chunks = bulk.query(Transfer.Type.upload, file, status); StorageObject stored = null; for(TransferStatus chunk : chunks) { chunk.setChecksum(ChecksumComputeFactory.get(HashAlgorithm.md5).compute(local.getInputStream(), chunk)); - stored = super.upload(file, local, throttle, progress, streamListener, chunk, callback); + stored = super.upload(write, file, local, throttle, progress, streamListener, chunk, callback); } return stored; } - @Override - public Upload withWriter(final Write writer) { - this.writer = writer; - return super.withWriter(writer); - } } diff --git a/spectra/src/test/java/ch/cyberduck/core/spectra/SpectraAttributesFinderFeatureTest.java b/spectra/src/test/java/ch/cyberduck/core/spectra/SpectraAttributesFinderFeatureTest.java index 668a973b21..ca23a5e7ee 100644 --- a/spectra/src/test/java/ch/cyberduck/core/spectra/SpectraAttributesFinderFeatureTest.java +++ b/spectra/src/test/java/ch/cyberduck/core/spectra/SpectraAttributesFinderFeatureTest.java @@ -38,10 +38,10 @@ public class SpectraAttributesFinderFeatureTest extends AbstractSpectraTest { @Test public void testFindFile() throws Exception { - final Path container = new SpectraDirectoryFeature(session, new SpectraWriteFeature(session)).mkdir( - new Path(new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory, Path.Type.volume)), new TransferStatus()); + final Path container = new SpectraDirectoryFeature(session).mkdir( + new SpectraWriteFeature(session), new Path(new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory, Path.Type.volume)), new TransferStatus()); final Path test = new Path(container, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)); - new SpectraTouchFeature(session).touch(test, new TransferStatus()); + new SpectraTouchFeature(session).touch(new SpectraWriteFeature(session), test, new TransferStatus()); final SpectraAttributesFinderFeature f = new SpectraAttributesFinderFeature(session); final PathAttributes attributes = f.find(test); assertEquals(0L, attributes.getSize()); @@ -61,8 +61,8 @@ public class SpectraAttributesFinderFeatureTest extends AbstractSpectraTest { @Test public void testFindBucket() throws Exception { - final Path container = new SpectraDirectoryFeature(session, new SpectraWriteFeature(session)).mkdir( - new Path(new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory, Path.Type.volume)), new TransferStatus()); + final Path container = new SpectraDirectoryFeature(session).mkdir( + new SpectraWriteFeature(session), new Path(new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory, Path.Type.volume)), new TransferStatus()); final PathAttributes attributes = new SpectraAttributesFinderFeature(session).find(container); assertEquals(-1L, attributes.getSize()); assertNull(attributes.getRegion()); @@ -80,9 +80,9 @@ public class SpectraAttributesFinderFeatureTest extends AbstractSpectraTest { @Test public void testFindPlaceholder() throws Exception { - final Path container = new SpectraDirectoryFeature(session, new SpectraWriteFeature(session)).mkdir( - new Path(new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory, Path.Type.volume)), new TransferStatus()); - final Path test = new SpectraDirectoryFeature(session, new SpectraWriteFeature(session)).mkdir(new Path(container, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory)), new TransferStatus()); + final Path container = new SpectraDirectoryFeature(session).mkdir( + new SpectraWriteFeature(session), new Path(new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory, Path.Type.volume)), new TransferStatus()); + final Path test = new SpectraDirectoryFeature(session).mkdir(new SpectraWriteFeature(session), new Path(container, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory)), new TransferStatus()); final PathAttributes attributes = new SpectraAttributesFinderFeature(session).find(test); assertEquals(0L, attributes.getSize()); assertEquals(Checksum.parse("d41d8cd98f00b204e9800998ecf8427e"), attributes.getChecksum()); @@ -93,22 +93,22 @@ public class SpectraAttributesFinderFeatureTest extends AbstractSpectraTest { @Test public void testReadTildeInKey() throws Exception { - final Path container = new SpectraDirectoryFeature(session, new SpectraWriteFeature(session)).mkdir( - new Path(new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory, Path.Type.volume)), new TransferStatus()); + final Path container = new SpectraDirectoryFeature(session).mkdir( + new SpectraWriteFeature(session), new Path(new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory, Path.Type.volume)), new TransferStatus()); container.attributes().setRegion("us-east-1"); final Path file = new Path(container, String.format("%s~", new AlphanumericRandomStringService().random()), EnumSet.of(Path.Type.file)); - new SpectraTouchFeature(session).touch(file, new TransferStatus()); + new SpectraTouchFeature(session).touch(new SpectraWriteFeature(session), file, new TransferStatus()); new SpectraAttributesFinderFeature(session).find(file); new SpectraDeleteFeature(session).delete(Collections.singletonList(container), new DisabledLoginCallback(), new Delete.DisabledCallback()); } @Test public void testReadAtSignInKey() throws Exception { - final Path container = new SpectraDirectoryFeature(session, new SpectraWriteFeature(session)).mkdir( - new Path(new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory, Path.Type.volume)), new TransferStatus()); + final Path container = new SpectraDirectoryFeature(session).mkdir( + new SpectraWriteFeature(session), new Path(new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory, Path.Type.volume)), new TransferStatus()); container.attributes().setRegion("us-east-1"); final Path file = new Path(container, String.format("%s@", new AlphanumericRandomStringService().random()), EnumSet.of(Path.Type.file)); - new SpectraTouchFeature(session).touch(file, new TransferStatus()); + new SpectraTouchFeature(session).touch(new SpectraWriteFeature(session), file, new TransferStatus()); new SpectraAttributesFinderFeature(session).find(file); new SpectraDeleteFeature(session).delete(Collections.singletonList(container), new DisabledLoginCallback(), new Delete.DisabledCallback()); } diff --git a/spectra/src/test/java/ch/cyberduck/core/spectra/SpectraBulkServiceTest.java b/spectra/src/test/java/ch/cyberduck/core/spectra/SpectraBulkServiceTest.java index f5391ac553..21aa1fa05a 100644 --- a/spectra/src/test/java/ch/cyberduck/core/spectra/SpectraBulkServiceTest.java +++ b/spectra/src/test/java/ch/cyberduck/core/spectra/SpectraBulkServiceTest.java @@ -48,8 +48,8 @@ public class SpectraBulkServiceTest extends AbstractSpectraTest { public void testPreUploadSingleFile() throws Exception { final Map files = new HashMap<>(); final TransferStatus status = new TransferStatus(); - final Path container = new SpectraDirectoryFeature(session, new SpectraWriteFeature(session)).mkdir( - new Path(new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory, Path.Type.volume)), new TransferStatus()); + final Path container = new SpectraDirectoryFeature(session).mkdir( + new SpectraWriteFeature(session), new Path(new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory, Path.Type.volume)), new TransferStatus()); final Path file = new Path(container, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)); files.put(new TransferItem(file), status.setLength(1L)); final SpectraBulkService bulk = new SpectraBulkService(session); @@ -63,8 +63,8 @@ public class SpectraBulkServiceTest extends AbstractSpectraTest { @Test public void testPreUploadDirectoryFile() throws Exception { final Map files = new HashMap<>(); - final Path container = new SpectraDirectoryFeature(session, new SpectraWriteFeature(session)).mkdir( - new Path(new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory, Path.Type.volume)), new TransferStatus()); + final Path container = new SpectraDirectoryFeature(session).mkdir( + new SpectraWriteFeature(session), new Path(new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory, Path.Type.volume)), new TransferStatus()); final Path directory = new Path(container, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory)); final TransferStatus directoryStatus = new TransferStatus().setLength(0L); files.put(new TransferItem(directory), directoryStatus); @@ -97,8 +97,8 @@ public class SpectraBulkServiceTest extends AbstractSpectraTest { public void testPreUploadLargeFile() throws Exception { final Map files = new HashMap<>(); final TransferStatus status = new TransferStatus(); - final Path container = new SpectraDirectoryFeature(session, new SpectraWriteFeature(session)).mkdir( - new Path(new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory, Path.Type.volume)), new TransferStatus()); + final Path container = new SpectraDirectoryFeature(session).mkdir( + new SpectraWriteFeature(session), new Path(new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory, Path.Type.volume)), new TransferStatus()); final Path file = new Path(container, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)); files.put(new TransferItem(file), // 11GB @@ -129,8 +129,8 @@ public class SpectraBulkServiceTest extends AbstractSpectraTest { public void testPreUploadMultipleLargeFile() throws Exception { final Map files = new HashMap<>(); final TransferStatus status = new TransferStatus(); - final Path container = new SpectraDirectoryFeature(session, new SpectraWriteFeature(session)).mkdir( - new Path(new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory, Path.Type.volume)), new TransferStatus()); + final Path container = new SpectraDirectoryFeature(session).mkdir( + new SpectraWriteFeature(session), new Path(new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory, Path.Type.volume)), new TransferStatus()); files.put(new TransferItem(new Path(container, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file))), // 11GB status.setLength(118111600640L) diff --git a/spectra/src/test/java/ch/cyberduck/core/spectra/SpectraDeleteFeatureTest.java b/spectra/src/test/java/ch/cyberduck/core/spectra/SpectraDeleteFeatureTest.java index 458c062bbc..972207a4ab 100644 --- a/spectra/src/test/java/ch/cyberduck/core/spectra/SpectraDeleteFeatureTest.java +++ b/spectra/src/test/java/ch/cyberduck/core/spectra/SpectraDeleteFeatureTest.java @@ -37,7 +37,7 @@ public class SpectraDeleteFeatureTest extends AbstractSpectraTest { public void testDeleteContainer() throws Exception { final Path container = new Path(new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.volume, Path.Type.directory)); container.attributes().setRegion("US"); - new SpectraDirectoryFeature(session, new SpectraWriteFeature(session)).mkdir(container, new TransferStatus()); + new SpectraDirectoryFeature(session).mkdir(new SpectraWriteFeature(session), container, new TransferStatus()); assertTrue(new SpectraFindFeature(session).find(container)); new SpectraDeleteFeature(session).delete(Collections.singletonList(container), new DisabledLoginCallback(), new Delete.DisabledCallback()); } diff --git a/spectra/src/test/java/ch/cyberduck/core/spectra/SpectraDirectoryFeatureTest.java b/spectra/src/test/java/ch/cyberduck/core/spectra/SpectraDirectoryFeatureTest.java index 1cd109f37e..d489fbc0e9 100644 --- a/spectra/src/test/java/ch/cyberduck/core/spectra/SpectraDirectoryFeatureTest.java +++ b/spectra/src/test/java/ch/cyberduck/core/spectra/SpectraDirectoryFeatureTest.java @@ -36,9 +36,9 @@ public class SpectraDirectoryFeatureTest extends AbstractSpectraTest { @Test public void testCreateBucket() throws Exception { - final SpectraDirectoryFeature feature = new SpectraDirectoryFeature(session, new SpectraWriteFeature(session)); + final SpectraDirectoryFeature feature = new SpectraDirectoryFeature(session); final Path test = new Path(new DefaultHomeFinderService(session).find(), new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory, Path.Type.volume)); - feature.mkdir(test, new TransferStatus()); + feature.mkdir(new SpectraWriteFeature(session), test, new TransferStatus()); assertTrue(new SpectraFindFeature(session).find(test)); new SpectraDeleteFeature(session).delete(Collections.singletonList(test), new DisabledLoginCallback(), new Delete.DisabledCallback()); } @@ -47,10 +47,10 @@ public class SpectraDirectoryFeatureTest extends AbstractSpectraTest { public void testCreatePlaceholder() throws Exception { final String bucketname = new AlphanumericRandomStringService().random(); final String name = new AlphanumericRandomStringService().random(); - final Path container = new SpectraDirectoryFeature(session, new SpectraWriteFeature(session)).mkdir( - new Path(bucketname, EnumSet.of(Path.Type.directory, Path.Type.volume)), new TransferStatus()); - final Path test = new SpectraDirectoryFeature(session, new SpectraWriteFeature(session)).mkdir( - new Path(container, name, EnumSet.of(Path.Type.directory)), new TransferStatus()); + final Path container = new SpectraDirectoryFeature(session).mkdir( + new SpectraWriteFeature(session), new Path(bucketname, EnumSet.of(Path.Type.directory, Path.Type.volume)), new TransferStatus()); + final Path test = new SpectraDirectoryFeature(session).mkdir( + new SpectraWriteFeature(session), new Path(container, name, EnumSet.of(Path.Type.directory)), new TransferStatus()); assertTrue(new SpectraFindFeature(session).find(test)); assertTrue(new DefaultFindFeature(session).find(test)); new SpectraDeleteFeature(session).delete(Collections.singletonList(container), new DisabledLoginCallback(), new Delete.DisabledCallback()); diff --git a/spectra/src/test/java/ch/cyberduck/core/spectra/SpectraFindFeatureTest.java b/spectra/src/test/java/ch/cyberduck/core/spectra/SpectraFindFeatureTest.java index 168b29f46e..678fdc8fc5 100644 --- a/spectra/src/test/java/ch/cyberduck/core/spectra/SpectraFindFeatureTest.java +++ b/spectra/src/test/java/ch/cyberduck/core/spectra/SpectraFindFeatureTest.java @@ -36,8 +36,8 @@ public class SpectraFindFeatureTest extends AbstractSpectraTest { @Test public void testFindNotFound() throws Exception { - final Path container = new SpectraDirectoryFeature(session, new SpectraWriteFeature(session)).mkdir( - new Path(new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory, Path.Type.volume)), new TransferStatus()); + final Path container = new SpectraDirectoryFeature(session).mkdir( + new SpectraWriteFeature(session), new Path(new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory, Path.Type.volume)), new TransferStatus()); final Path test = new Path(container, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)); final SpectraFindFeature f = new SpectraFindFeature(session); assertFalse(f.find(test)); @@ -52,8 +52,8 @@ public class SpectraFindFeatureTest extends AbstractSpectraTest { @Test public void testFindBucket() throws Exception { - final Path container = new SpectraDirectoryFeature(session, new SpectraWriteFeature(session)).mkdir( - new Path(new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory, Path.Type.volume)), new TransferStatus()); + final Path container = new SpectraDirectoryFeature(session).mkdir( + new SpectraWriteFeature(session), new Path(new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory, Path.Type.volume)), new TransferStatus()); assertTrue(new SpectraFindFeature(session).find(container)); new SpectraDeleteFeature(session).delete(Collections.singletonList(container), new DisabledLoginCallback(), new Delete.DisabledCallback()); } diff --git a/spectra/src/test/java/ch/cyberduck/core/spectra/SpectraMultipleDeleteFeatureTest.java b/spectra/src/test/java/ch/cyberduck/core/spectra/SpectraMultipleDeleteFeatureTest.java index b1edb5a8a6..8868b98386 100644 --- a/spectra/src/test/java/ch/cyberduck/core/spectra/SpectraMultipleDeleteFeatureTest.java +++ b/spectra/src/test/java/ch/cyberduck/core/spectra/SpectraMultipleDeleteFeatureTest.java @@ -42,8 +42,8 @@ public class SpectraMultipleDeleteFeatureTest extends AbstractSpectraTest { @Test public void testDeleteFile() throws Exception { - final Path container = new SpectraDirectoryFeature(session, new SpectraWriteFeature(session)).mkdir( - new Path(new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory, Path.Type.volume)), new TransferStatus()); + final Path container = new SpectraDirectoryFeature(session).mkdir( + new SpectraWriteFeature(session), new Path(new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory, Path.Type.volume)), new TransferStatus()); final Path test = new Path(container, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)); final byte[] content = RandomUtils.nextBytes(1024); final HttpResponseOutputStream out = new SpectraWriteFeature(session).write(test, new TransferStatus().setLength(content.length), new DisabledConnectionCallback()); @@ -57,10 +57,10 @@ public class SpectraMultipleDeleteFeatureTest extends AbstractSpectraTest { @Test public void testDeletePlaceholder() throws Exception { - final Path container = new SpectraDirectoryFeature(session, new SpectraWriteFeature(session)).mkdir( - new Path(new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory, Path.Type.volume)), new TransferStatus()); - final Path test = new SpectraDirectoryFeature(session, new SpectraWriteFeature(session)).mkdir( - new Path(container, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory)), new TransferStatus()); + final Path container = new SpectraDirectoryFeature(session).mkdir( + new SpectraWriteFeature(session), new Path(new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory, Path.Type.volume)), new TransferStatus()); + final Path test = new SpectraDirectoryFeature(session).mkdir( + new SpectraWriteFeature(session), new Path(container, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory)), new TransferStatus()); assertTrue(new SpectraFindFeature(session).find(test)); new SpectraDeleteFeature(session).delete(Collections.singletonList(test), new DisabledLoginCallback(), new Delete.DisabledCallback()); assertFalse(new SpectraFindFeature(session).find(test)); diff --git a/spectra/src/test/java/ch/cyberduck/core/spectra/SpectraObjectListServiceTest.java b/spectra/src/test/java/ch/cyberduck/core/spectra/SpectraObjectListServiceTest.java index e38010dc1e..e9b5d4a257 100644 --- a/spectra/src/test/java/ch/cyberduck/core/spectra/SpectraObjectListServiceTest.java +++ b/spectra/src/test/java/ch/cyberduck/core/spectra/SpectraObjectListServiceTest.java @@ -50,8 +50,8 @@ public class SpectraObjectListServiceTest extends AbstractSpectraTest { @Test public void tetsEmptyPlaceholder() throws Exception { - final Path container = new SpectraDirectoryFeature(session, new SpectraWriteFeature(session)).mkdir( - new Path(new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory, Path.Type.volume)), new TransferStatus()); + final Path container = new SpectraDirectoryFeature(session).mkdir( + new SpectraWriteFeature(session), new Path(new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory, Path.Type.volume)), new TransferStatus()); try { new SpectraObjectListService(session).list(new Path(container, "empty", EnumSet.of(Path.Type.directory, Path.Type.placeholder)), new DisabledListProgressListener()); @@ -71,10 +71,10 @@ public class SpectraObjectListServiceTest extends AbstractSpectraTest { @Test public void testListPlaceholder() throws Exception { - final Path container = new SpectraDirectoryFeature(session, new SpectraWriteFeature(session)).mkdir( - new Path(new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory, Path.Type.volume)), new TransferStatus()); - final Path placeholder = new SpectraDirectoryFeature(session, new SpectraWriteFeature(session)).mkdir( - new Path(container, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory)), new TransferStatus()); + final Path container = new SpectraDirectoryFeature(session).mkdir( + new SpectraWriteFeature(session), new Path(new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory, Path.Type.volume)), new TransferStatus()); + final Path placeholder = new SpectraDirectoryFeature(session).mkdir( + new SpectraWriteFeature(session), new Path(container, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory)), new TransferStatus()); final AttributedList list = new SpectraObjectListService(session).list(placeholder, new DisabledListProgressListener()); assertTrue(list.isEmpty()); new SpectraDeleteFeature(session).delete(Collections.singletonList(container), new DisabledLoginCallback(), new Delete.DisabledCallback()); @@ -83,11 +83,11 @@ public class SpectraObjectListServiceTest extends AbstractSpectraTest { @Test @Ignore public void testVersioning() throws Exception { - final Path container = new SpectraDirectoryFeature(session, new SpectraWriteFeature(session)).mkdir( - new Path(new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory, Path.Type.volume)), new TransferStatus()); + final Path container = new SpectraDirectoryFeature(session).mkdir( + new SpectraWriteFeature(session), new Path(new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory, Path.Type.volume)), new TransferStatus()); new SpectraVersioningFeature(session).setConfiguration(container, new DisabledPasswordCallback(), new VersioningConfiguration(true)); final Path folder = new Path(container, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory)); - new SpectraDirectoryFeature(session, new SpectraWriteFeature(session)).mkdir(folder, new TransferStatus()); + new SpectraDirectoryFeature(session).mkdir(new SpectraWriteFeature(session), folder, new TransferStatus()); final Path test = new Path(folder, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)); final byte[] content = RandomUtils.nextBytes(1000); final TransferStatus status = new TransferStatus().setLength(content.length); @@ -130,40 +130,40 @@ public class SpectraObjectListServiceTest extends AbstractSpectraTest { @Test public void testListFilePlusCharacter() throws Exception { - final Path container = new SpectraDirectoryFeature(session, new SpectraWriteFeature(session)).mkdir( - new Path(new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory, Path.Type.volume)), new TransferStatus()); + final Path container = new SpectraDirectoryFeature(session).mkdir( + new SpectraWriteFeature(session), new Path(new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory, Path.Type.volume)), new TransferStatus()); final Path file = new SpectraTouchFeature(session).touch( - new Path(container, String.format("test+%s", new AlphanumericRandomStringService().random()), EnumSet.of(Path.Type.file)), new TransferStatus()); + new SpectraWriteFeature(session), new Path(container, String.format("test+%s", new AlphanumericRandomStringService().random()), EnumSet.of(Path.Type.file)), new TransferStatus()); assertNotNull(new SpectraObjectListService(session).list(container, new DisabledListProgressListener()).find(new SimplePathPredicate(file))); new SpectraDeleteFeature(session).delete(Collections.singletonList(container), new DisabledLoginCallback(), new Delete.DisabledCallback()); } @Test public void testListFileDot() throws Exception { - final Path container = new SpectraDirectoryFeature(session, new SpectraWriteFeature(session)).mkdir( - new Path(new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory, Path.Type.volume)), new TransferStatus()); + final Path container = new SpectraDirectoryFeature(session).mkdir( + new SpectraWriteFeature(session), new Path(new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory, Path.Type.volume)), new TransferStatus()); final Path file = new SpectraTouchFeature(session).touch( - new Path(container, ".", EnumSet.of(Path.Type.file)), new TransferStatus()); + new SpectraWriteFeature(session), new Path(container, ".", EnumSet.of(Path.Type.file)), new TransferStatus()); assertNotNull(new SpectraObjectListService(session).list(container, new DisabledListProgressListener()).find(new SimplePathPredicate(file))); new SpectraDeleteFeature(session).delete(Collections.singletonList(container), new DisabledLoginCallback(), new Delete.DisabledCallback()); } @Test public void testListPlaceholderDot() throws Exception { - final Path container = new SpectraDirectoryFeature(session, new SpectraWriteFeature(session)).mkdir( - new Path(new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory, Path.Type.volume)), new TransferStatus()); - final Path placeholder = new SpectraDirectoryFeature(session, new SpectraWriteFeature(session)).mkdir( - new Path(container, ".", EnumSet.of(Path.Type.directory)), new TransferStatus()); + final Path container = new SpectraDirectoryFeature(session).mkdir( + new SpectraWriteFeature(session), new Path(new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory, Path.Type.volume)), new TransferStatus()); + final Path placeholder = new SpectraDirectoryFeature(session).mkdir( + new SpectraWriteFeature(session), new Path(container, ".", EnumSet.of(Path.Type.directory)), new TransferStatus()); assertTrue(new SpectraObjectListService(session).list(container, new DisabledListProgressListener()).contains(placeholder)); new SpectraDeleteFeature(session).delete(Collections.singletonList(container), new DisabledLoginCallback(), new Delete.DisabledCallback()); } @Test public void testListPlaceholderPlusCharacter() throws Exception { - final Path container = new SpectraDirectoryFeature(session, new SpectraWriteFeature(session)).mkdir( - new Path(new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory, Path.Type.volume)), new TransferStatus()); - final Path placeholder = new SpectraDirectoryFeature(session, new SpectraWriteFeature(session)).mkdir( - new Path(container, String.format("test+%s", new AlphanumericRandomStringService().random()), EnumSet.of(Path.Type.directory)), new TransferStatus()); + final Path container = new SpectraDirectoryFeature(session).mkdir( + new SpectraWriteFeature(session), new Path(new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory, Path.Type.volume)), new TransferStatus()); + final Path placeholder = new SpectraDirectoryFeature(session).mkdir( + new SpectraWriteFeature(session), new Path(container, String.format("test+%s", new AlphanumericRandomStringService().random()), EnumSet.of(Path.Type.directory)), new TransferStatus()); assertTrue(new SpectraObjectListService(session).list(container, new DisabledListProgressListener()).contains(placeholder)); assertTrue(new SpectraObjectListService(session).list(placeholder, new DisabledListProgressListener()).isEmpty()); new SpectraDeleteFeature(session).delete(Collections.singletonList(container), new DisabledLoginCallback(), new Delete.DisabledCallback()); diff --git a/spectra/src/test/java/ch/cyberduck/core/spectra/SpectraReadFeatureTest.java b/spectra/src/test/java/ch/cyberduck/core/spectra/SpectraReadFeatureTest.java index 82f1ad3935..f3f73d0f2c 100644 --- a/spectra/src/test/java/ch/cyberduck/core/spectra/SpectraReadFeatureTest.java +++ b/spectra/src/test/java/ch/cyberduck/core/spectra/SpectraReadFeatureTest.java @@ -46,8 +46,8 @@ public class SpectraReadFeatureTest extends AbstractSpectraTest { @Test public void testReadNotFound() throws Exception { final TransferStatus status = new TransferStatus(); - final Path container = new SpectraDirectoryFeature(session, new SpectraWriteFeature(session)).mkdir( - new Path(new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory, Path.Type.volume)), new TransferStatus()); + final Path container = new SpectraDirectoryFeature(session).mkdir( + new SpectraWriteFeature(session), new Path(new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory, Path.Type.volume)), new TransferStatus()); final Path test = new Path(container, "nosuchname", EnumSet.of(Path.Type.file)); try { new SpectraBulkService(session).pre(Transfer.Type.download, Collections.singletonMap(new TransferItem(test), status), new DisabledConnectionCallback()); @@ -62,8 +62,8 @@ public class SpectraReadFeatureTest extends AbstractSpectraTest { @Test public void testRead() throws Exception { - final Path container = new SpectraDirectoryFeature(session, new SpectraWriteFeature(session)).mkdir( - new Path(new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory, Path.Type.volume)), new TransferStatus()); + final Path container = new SpectraDirectoryFeature(session).mkdir( + new SpectraWriteFeature(session), new Path(new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory, Path.Type.volume)), new TransferStatus()); final Path test = new Path(container, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)); final byte[] content = RandomUtils.nextBytes(1023); final TransferStatus status = new TransferStatus().setLength(content.length); diff --git a/spectra/src/test/java/ch/cyberduck/core/spectra/SpectraTouchFeatureTest.java b/spectra/src/test/java/ch/cyberduck/core/spectra/SpectraTouchFeatureTest.java index deba737c19..a87be4e345 100644 --- a/spectra/src/test/java/ch/cyberduck/core/spectra/SpectraTouchFeatureTest.java +++ b/spectra/src/test/java/ch/cyberduck/core/spectra/SpectraTouchFeatureTest.java @@ -43,10 +43,10 @@ public class SpectraTouchFeatureTest extends AbstractSpectraTest { @Test public void testTouch() throws Exception { - final Path container = new SpectraDirectoryFeature(session, new SpectraWriteFeature(session)).mkdir( - new Path(new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory, Path.Type.volume)), new TransferStatus()); + final Path container = new SpectraDirectoryFeature(session).mkdir( + new SpectraWriteFeature(session), new Path(new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory, Path.Type.volume)), new TransferStatus()); final Path test = new Path(container, new AlphanumericRandomStringService().random() + ".txt", EnumSet.of(Path.Type.file)); - new SpectraTouchFeature(session).touch(test, new TransferStatus()); + new SpectraTouchFeature(session).touch(new SpectraWriteFeature(session), test, new TransferStatus()); assertTrue(new SpectraFindFeature(session).find(test)); new SpectraDeleteFeature(session).delete(Collections.singletonList(test), new DisabledLoginCallback(), new Delete.DisabledCallback()); assertFalse(new SpectraFindFeature(session).find(test)); diff --git a/spectra/src/test/java/ch/cyberduck/core/spectra/SpectraUploadFeatureTest.java b/spectra/src/test/java/ch/cyberduck/core/spectra/SpectraUploadFeatureTest.java index f188f39c8d..cc07f2fcba 100644 --- a/spectra/src/test/java/ch/cyberduck/core/spectra/SpectraUploadFeatureTest.java +++ b/spectra/src/test/java/ch/cyberduck/core/spectra/SpectraUploadFeatureTest.java @@ -54,14 +54,14 @@ public class SpectraUploadFeatureTest extends AbstractSpectraTest { final OutputStream out = local.getOutputStream(false); IOUtils.write(content, out); out.close(); - final Path container = new SpectraDirectoryFeature(session, new SpectraWriteFeature(session)).mkdir( - new Path(new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory, Path.Type.volume)), new TransferStatus()); + final Path container = new SpectraDirectoryFeature(session).mkdir( + new SpectraWriteFeature(session), new Path(new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory, Path.Type.volume)), new TransferStatus()); final Path test = new Path(container, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)); final TransferStatus writeStatus = new TransferStatus().setLength(content.length); final SpectraBulkService bulk = new SpectraBulkService(session); bulk.pre(Transfer.Type.upload, Collections.singletonMap(new TransferItem(test), writeStatus), new DisabledConnectionCallback()); - final SpectraUploadFeature upload = new SpectraUploadFeature(session, new SpectraWriteFeature(session), new SpectraBulkService(session)); - upload.upload(test, local, new BandwidthThrottle(BandwidthThrottle.UNLIMITED), new DisabledProgressListener(), new DisabledStreamListener(), + final SpectraUploadFeature upload = new SpectraUploadFeature(new SpectraBulkService(session)); + upload.upload(new SpectraWriteFeature(session), test, local, new BandwidthThrottle(BandwidthThrottle.UNLIMITED), new DisabledProgressListener(), new DisabledStreamListener(), writeStatus, new DisabledConnectionCallback()); final byte[] buffer = new byte[content.length]; final TransferStatus readStatus = new TransferStatus().setLength(content.length); @@ -96,8 +96,8 @@ public class SpectraUploadFeatureTest extends AbstractSpectraTest { out.close(); status2 = new TransferStatus().setLength(content.length); } - final Path container = new SpectraDirectoryFeature(session, new SpectraWriteFeature(session)).mkdir( - new Path(new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory, Path.Type.volume)), new TransferStatus()); + final Path container = new SpectraDirectoryFeature(session).mkdir( + new SpectraWriteFeature(session), new Path(new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory, Path.Type.volume)), new TransferStatus()); final Path test1 = new Path(container, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)); final Path test2 = new Path(container, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)); final SpectraBulkService bulk = new SpectraBulkService(session); @@ -105,10 +105,10 @@ public class SpectraUploadFeatureTest extends AbstractSpectraTest { files.put(new TransferItem(test1), status1); files.put(new TransferItem(test2), status2); bulk.pre(Transfer.Type.upload, files, new DisabledConnectionCallback()); - final SpectraUploadFeature upload = new SpectraUploadFeature(session, new SpectraWriteFeature(session), new SpectraBulkService(session)); - upload.upload(test1, local1, new BandwidthThrottle(BandwidthThrottle.UNLIMITED), new DisabledProgressListener(), new DisabledStreamListener(), + final SpectraUploadFeature upload = new SpectraUploadFeature(new SpectraBulkService(session)); + upload.upload(new SpectraWriteFeature(session), test1, local1, new BandwidthThrottle(BandwidthThrottle.UNLIMITED), new DisabledProgressListener(), new DisabledStreamListener(), status1, new DisabledConnectionCallback()); - upload.upload(test2, local2, new BandwidthThrottle(BandwidthThrottle.UNLIMITED), new DisabledProgressListener(), new DisabledStreamListener(), + upload.upload(new SpectraWriteFeature(session), test2, local2, new BandwidthThrottle(BandwidthThrottle.UNLIMITED), new DisabledProgressListener(), new DisabledStreamListener(), status2, new DisabledConnectionCallback()); new SpectraDeleteFeature(session).delete(Arrays.asList(test1, test2), new DisabledLoginCallback(), new Delete.DisabledCallback()); local1.delete(); diff --git a/spectra/src/test/java/ch/cyberduck/core/spectra/SpectraVersioningFeatureTest.java b/spectra/src/test/java/ch/cyberduck/core/spectra/SpectraVersioningFeatureTest.java index 496dc6de83..ea67af3715 100644 --- a/spectra/src/test/java/ch/cyberduck/core/spectra/SpectraVersioningFeatureTest.java +++ b/spectra/src/test/java/ch/cyberduck/core/spectra/SpectraVersioningFeatureTest.java @@ -39,8 +39,8 @@ public class SpectraVersioningFeatureTest extends AbstractSpectraTest { @Test @Ignore public void testSetConfiguration() throws Exception { - final Path container = new SpectraDirectoryFeature(session, new SpectraWriteFeature(session)).mkdir( - new Path(new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory, Path.Type.volume)), new TransferStatus()); + final Path container = new SpectraDirectoryFeature(session).mkdir( + new SpectraWriteFeature(session), new Path(new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory, Path.Type.volume)), new TransferStatus()); final Versioning feature = new SpectraVersioningFeature(session); feature.setConfiguration(container, new DisabledLoginCallback(), new VersioningConfiguration(true, false)); assertTrue(feature.getConfiguration(container).isEnabled()); diff --git a/spectra/src/test/java/ch/cyberduck/core/spectra/SpectraWriteFeatureTest.java b/spectra/src/test/java/ch/cyberduck/core/spectra/SpectraWriteFeatureTest.java index d1efd0012d..9f5c3ca01f 100644 --- a/spectra/src/test/java/ch/cyberduck/core/spectra/SpectraWriteFeatureTest.java +++ b/spectra/src/test/java/ch/cyberduck/core/spectra/SpectraWriteFeatureTest.java @@ -44,8 +44,8 @@ public class SpectraWriteFeatureTest extends AbstractSpectraTest { @Test public void testWriteOverwrite() throws Exception { - final Path container = new SpectraDirectoryFeature(session, new SpectraWriteFeature(session)).mkdir( - new Path(new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory, Path.Type.volume)), new TransferStatus()); + final Path container = new SpectraDirectoryFeature(session).mkdir( + new SpectraWriteFeature(session), new Path(new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory, Path.Type.volume)), new TransferStatus()); final Path test = new Path(container, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)); final byte[] content = RandomUtils.nextBytes(1000); final TransferStatus status = new TransferStatus().setLength(content.length); @@ -73,11 +73,11 @@ public class SpectraWriteFeatureTest extends AbstractSpectraTest { @Test public void testOverwriteZeroSized() throws Exception { - final Path container = new SpectraDirectoryFeature(session, new SpectraWriteFeature(session)).mkdir( - new Path(new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory, Path.Type.volume)), new TransferStatus()); + final Path container = new SpectraDirectoryFeature(session).mkdir( + new SpectraWriteFeature(session), new Path(new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory, Path.Type.volume)), new TransferStatus()); final Path test = new Path(container, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)); // Make 0-byte file - new SpectraTouchFeature(session).touch(test, new TransferStatus()); + new SpectraTouchFeature(session).touch(new SpectraWriteFeature(session), test, new TransferStatus()); // Replace content final byte[] content = RandomUtils.nextBytes(1023); final TransferStatus status = new TransferStatus().setLength(content.length); @@ -93,8 +93,8 @@ public class SpectraWriteFeatureTest extends AbstractSpectraTest { @Test public void testSPECTRA69() throws Exception { - final Path container = new SpectraDirectoryFeature(session, new SpectraWriteFeature(session)).mkdir( - new Path(new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory, Path.Type.volume)), new TransferStatus()); + final Path container = new SpectraDirectoryFeature(session).mkdir( + new SpectraWriteFeature(session), new Path(new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory, Path.Type.volume)), new TransferStatus()); final Path test = new Path(container, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)); // Allocate final SpectraBulkService bulk = new SpectraBulkService(session); diff --git a/spectra/src/test/java/ch/cyberduck/core/worker/SpectraSingleTransferWorkerTest.java b/spectra/src/test/java/ch/cyberduck/core/worker/SpectraSingleTransferWorkerTest.java index f69258d77c..bcbd9680b0 100644 --- a/spectra/src/test/java/ch/cyberduck/core/worker/SpectraSingleTransferWorkerTest.java +++ b/spectra/src/test/java/ch/cyberduck/core/worker/SpectraSingleTransferWorkerTest.java @@ -143,7 +143,7 @@ public class SpectraSingleTransferWorkerTest extends VaultTest { return (T) write; } if(type == Upload.class) { - return (T) new SpectraUploadFeature(this, write, new SpectraBulkService(this)); + return (T) new SpectraUploadFeature(new SpectraBulkService(this)); } return super._getFeature(type); } @@ -152,8 +152,8 @@ public class SpectraSingleTransferWorkerTest extends VaultTest { new DisabledHostKeyCallback(), new DisabledPasswordStore(), new DisabledProgressListener()).connect(session, new DisabledCancelCallback()); - final Path container = new SpectraDirectoryFeature(session, new SpectraWriteFeature(session)).mkdir( - new Path(new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory, Path.Type.volume)), new TransferStatus()); + final Path container = new SpectraDirectoryFeature(session).mkdir( + new SpectraWriteFeature(session), new Path(new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory, Path.Type.volume)), new TransferStatus()); final Path test = new Path(container, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)); final Transfer t = new UploadTransfer(session.getHost(), test, local); assertTrue(new SingleTransferWorker(session, session, t, new TransferOptions(), new TransferSpeedometer(t), new DisabledTransferPrompt() { diff --git a/src/main/msbuild/BannedSymbols.Cyberduck.Core.txt b/src/main/msbuild/BannedSymbols.Cyberduck.Core.txt new file mode 100644 index 0000000000..21ccf6e7f1 --- /dev/null +++ b/src/main/msbuild/BannedSymbols.Cyberduck.Core.txt @@ -0,0 +1,10 @@ +F:Windows.Win32.CorePInvoke.BHID_DataObject +F:Windows.Win32.CorePInvoke.PATHCCH_ALLOW_LONG_PATHS +M:Windows.Win32.CorePInvoke.ILCreateFromPath(System.String) +M:Windows.Win32.CorePInvoke.ILCreateFromPath(Windows.Win32.Foundation.PCWSTR); +M:Windows.Win32.CorePInvoke.ILCreateFromPathSafe(System.String) +M:Windows.Win32.CorePInvoke.PathCchCanonicalizeEx(System.Span{System.Char}@,System.String,Windows.Win32.UI.Shell.PATHCCH_OPTIONS) +M:Windows.Win32.CorePInvoke.PathCchCanonicalizeEx(Windows.Win32.Foundation.PWSTR,System.UIntPtr,System.String,Windows.Win32.UI.Shell.PATHCCH_OPTIONS) +M:Windows.Win32.CorePInvoke.PathCchCanonicalizeEx(Windows.Win32.Foundation.PWSTR,System.UIntPtr,Windows.Win32.Foundation.PCWSTR,Windows.Win32.UI.Shell.PATHCCH_OPTIONS); +M:Windows.Win32.CorePInvoke.PathCchStripPrefix(System.Span{System.Char}@,System.UIntPtr) +M:Windows.Win32.CorePInvoke.PathCchStripPrefix(Windows.Win32.Foundation.PWSTR,System.UIntPtr) diff --git a/src/template/msbuild/Version.props b/src/template/msbuild/Version.props index 12e454fa07..8d57710663 100644 --- a/src/template/msbuild/Version.props +++ b/src/template/msbuild/Version.props @@ -12,6 +12,9 @@ ${msbuild.sign} + ${signtool} + ${signtool.csp} + ${signtool.kc} - \ No newline at end of file + diff --git a/ssh/pom.xml b/ssh/pom.xml index 5fa9ecf74e..734a5e3dfa 100644 --- a/ssh/pom.xml +++ b/ssh/pom.xml @@ -18,7 +18,7 @@ ch.cyberduck parent - 9.2.0-SNAPSHOT + 9.4.0-SNAPSHOT ssh jar @@ -94,7 +94,7 @@ org.apache.sshd sshd-sftp - 2.15.0 + 2.16.0 test diff --git a/ssh/src/main/java/ch/cyberduck/core/sftp/PreferencesHostKeyVerifier.java b/ssh/src/main/java/ch/cyberduck/core/sftp/PreferencesHostKeyVerifier.java index 54c3daa134..20339a3a57 100644 --- a/ssh/src/main/java/ch/cyberduck/core/sftp/PreferencesHostKeyVerifier.java +++ b/ssh/src/main/java/ch/cyberduck/core/sftp/PreferencesHostKeyVerifier.java @@ -77,6 +77,7 @@ public abstract class PreferencesHostKeyVerifier extends AbstractHostKeyCallback @Override protected void allow(final Host host, final PublicKey key, final boolean persist) { if(persist) { + log.debug("Save host key {} to preferences for {}", key, host); preferences.setProperty(this.toFormat(host, key), Base64.toBase64String(key.getEncoded())); } } diff --git a/ssh/src/main/java/ch/cyberduck/core/sftp/SFTPAttributesFinderFeature.java b/ssh/src/main/java/ch/cyberduck/core/sftp/SFTPAttributesFinderFeature.java index 9ca1cc77c4..876b0118ae 100644 --- a/ssh/src/main/java/ch/cyberduck/core/sftp/SFTPAttributesFinderFeature.java +++ b/ssh/src/main/java/ch/cyberduck/core/sftp/SFTPAttributesFinderFeature.java @@ -17,6 +17,7 @@ package ch.cyberduck.core.sftp; * Bug fixes, suggestions and comments should be sent to feedback@cyberduck.ch */ +import ch.cyberduck.core.DefaultPathAttributes; import ch.cyberduck.core.ListProgressListener; import ch.cyberduck.core.Path; import ch.cyberduck.core.PathAttributes; @@ -87,7 +88,7 @@ public class SFTPAttributesFinderFeature implements AttributesFinder, Attributes @Override public PathAttributes toAttributes(final FileAttributes stat) { - final PathAttributes attributes = new PathAttributes(); + final PathAttributes attributes = new DefaultPathAttributes(); switch(stat.getType()) { case REGULAR: case UNKNOWN: diff --git a/ssh/src/main/java/ch/cyberduck/core/sftp/SFTPDirectoryFeature.java b/ssh/src/main/java/ch/cyberduck/core/sftp/SFTPDirectoryFeature.java index 49389a4e9e..2804d7e796 100644 --- a/ssh/src/main/java/ch/cyberduck/core/sftp/SFTPDirectoryFeature.java +++ b/ssh/src/main/java/ch/cyberduck/core/sftp/SFTPDirectoryFeature.java @@ -21,6 +21,7 @@ import ch.cyberduck.core.Path; import ch.cyberduck.core.Permission; import ch.cyberduck.core.exception.BackgroundException; import ch.cyberduck.core.features.Directory; +import ch.cyberduck.core.features.Write; import ch.cyberduck.core.transfer.TransferStatus; import java.io.IOException; @@ -36,7 +37,7 @@ public class SFTPDirectoryFeature implements Directory { } @Override - public Path mkdir(final Path folder, final TransferStatus status) throws BackgroundException { + public Path mkdir(final Write writer, final Path folder, final TransferStatus status) throws BackgroundException { try { final FileAttributes attrs; if(Permission.EMPTY != status.getPermission()) { diff --git a/ssh/src/main/java/ch/cyberduck/core/sftp/SFTPListService.java b/ssh/src/main/java/ch/cyberduck/core/sftp/SFTPListService.java index 5e1f5bfd70..bf595cec75 100644 --- a/ssh/src/main/java/ch/cyberduck/core/sftp/SFTPListService.java +++ b/ssh/src/main/java/ch/cyberduck/core/sftp/SFTPListService.java @@ -20,6 +20,7 @@ package ch.cyberduck.core.sftp; import ch.cyberduck.core.AttributedList; import ch.cyberduck.core.ListProgressListener; import ch.cyberduck.core.ListService; +import ch.cyberduck.core.LocaleFactory; import ch.cyberduck.core.Path; import ch.cyberduck.core.PathAttributes; import ch.cyberduck.core.PathNormalizer; @@ -34,6 +35,7 @@ import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; import java.io.IOException; +import java.text.MessageFormat; import java.util.EnumSet; import java.util.List; @@ -151,4 +153,12 @@ public class SFTPListService implements ListService { } return true; } + + @Override + public void preflight(final Path directory) throws BackgroundException { + if(!directory.attributes().getPermission().isExecutable() || !directory.attributes().getPermission().isReadable()) { + throw new AccessDeniedException(MessageFormat.format(LocaleFactory.localizedString("Listing directory {0} failed", "Error"), + directory.getName())).withFile(directory); + } + } } diff --git a/ssh/src/main/java/ch/cyberduck/core/sftp/SFTPMoveFeature.java b/ssh/src/main/java/ch/cyberduck/core/sftp/SFTPMoveFeature.java index 70383c450a..ddf45ddaf4 100644 --- a/ssh/src/main/java/ch/cyberduck/core/sftp/SFTPMoveFeature.java +++ b/ssh/src/main/java/ch/cyberduck/core/sftp/SFTPMoveFeature.java @@ -18,7 +18,9 @@ package ch.cyberduck.core.sftp; */ import ch.cyberduck.core.ConnectionCallback; +import ch.cyberduck.core.DefaultPathAttributes; import ch.cyberduck.core.Path; +import ch.cyberduck.core.PathAttributes; import ch.cyberduck.core.exception.BackgroundException; import ch.cyberduck.core.features.Delete; import ch.cyberduck.core.features.Move; @@ -46,7 +48,7 @@ public class SFTPMoveFeature implements Move { session.sftp().rename(file.getAbsolute(), renamed.getAbsolute(), status.isExists() ? new HashSet<>(Arrays.asList(RenameFlags.OVERWRITE, RenameFlags.NATIVE)) : Collections.singleton(RenameFlags.NATIVE)); // Copy original file attributes - return new Path(renamed).withAttributes(file.attributes()); + return new Path(renamed).withAttributes(new DefaultPathAttributes(file.attributes()).setVault(null)); } catch(IOException e) { throw new SFTPExceptionMappingService().map("Cannot rename {0}", e, file); diff --git a/ssh/src/main/java/ch/cyberduck/core/sftp/SFTPSession.java b/ssh/src/main/java/ch/cyberduck/core/sftp/SFTPSession.java index 1ec760ad6f..b38f431c4b 100644 --- a/ssh/src/main/java/ch/cyberduck/core/sftp/SFTPSession.java +++ b/ssh/src/main/java/ch/cyberduck/core/sftp/SFTPSession.java @@ -29,8 +29,6 @@ import ch.cyberduck.core.exception.InteroperabilityException; import ch.cyberduck.core.exception.LoginCanceledException; import ch.cyberduck.core.exception.LoginFailureException; import ch.cyberduck.core.features.*; -import ch.cyberduck.core.preferences.HostPreferencesFactory; -import ch.cyberduck.core.preferences.PreferencesReader; import ch.cyberduck.core.proxy.ProxyFinder; import ch.cyberduck.core.proxy.ProxySocketFactory; import ch.cyberduck.core.sftp.auth.SFTPAgentAuthentication; @@ -41,10 +39,8 @@ import ch.cyberduck.core.sftp.auth.SFTPPublicKeyAuthentication; import ch.cyberduck.core.sftp.compression.JcraftDelayedZlibCompression; import ch.cyberduck.core.sftp.compression.JcraftZlibCompression; import ch.cyberduck.core.sftp.openssh.OpenSSHAgentAuthenticator; -import ch.cyberduck.core.sftp.openssh.OpenSSHCredentialsConfigurator; import ch.cyberduck.core.sftp.openssh.OpenSSHHostnameConfigurator; import ch.cyberduck.core.sftp.openssh.OpenSSHIdentityAgentConfigurator; -import ch.cyberduck.core.sftp.openssh.OpenSSHJumpHostConfigurator; import ch.cyberduck.core.sftp.openssh.OpenSSHPreferredAuthenticationsConfigurator; import ch.cyberduck.core.sftp.openssh.WindowsOpenSSHAgentAuthenticator; import ch.cyberduck.core.sftp.putty.PageantAuthenticator; @@ -96,8 +92,6 @@ import net.schmizz.sshj.transport.verification.HostKeyVerifier; public class SFTPSession extends Session { private static final Logger log = LogManager.getLogger(SFTPSession.class); - private final PreferencesReader preferences = HostPreferencesFactory.get(host); - private SFTPEngine sftp; private StateDisconnectListener disconnectListener; private NegotiatedAlgorithms algorithms; @@ -148,20 +142,14 @@ public class SFTPSession extends Session { final SSHClient connection = this.toClient(key, configuration); try { // Look for jump host configuration - final Host proxy = new OpenSSHJumpHostConfigurator().getJumphost(host.getHostname()); + final Host proxy = host.getJumphost(); if(null != proxy) { log.info("Connect using jump host configuration {}", proxy); final SSHClient hop = this.toClient(key, configuration); hop.connect(proxy.getHostname(), proxy.getPort()); - final Credentials proxyCredentials = new OpenSSHCredentialsConfigurator().configure(proxy); - proxy.setCredentials(proxyCredentials); - final KeychainLoginService service = new KeychainLoginService(); - service.validate(proxy, prompt, new LoginOptions(proxy.getProtocol())); // Authenticate with jump host this.authenticate(hop, proxy, prompt, new DisabledCancelCallback()); log.debug("Authenticated with jump host {}", proxy); - // Write credentials to keychain - service.save(proxy); final DirectConnection tunnel = hop.newDirectConnection( new OpenSSHHostnameConfigurator().getHostname(host.getHostname()), host.getPort()); // Connect to internal host @@ -289,7 +277,10 @@ public class SFTPSession extends Session { defaultMethods.add(new SFTPAgentAuthentication(client, new WindowsOpenSSHAgentAuthenticator())); break; } - final String configuration = new OpenSSHIdentityAgentConfigurator().getIdentityAgent(host.getHostname()); + String configuration = new OpenSSHIdentityAgentConfigurator().getIdentityAgent(host.getHostname()); + if(null == configuration) { + configuration = System.getenv("SSH_AUTH_SOCK"); + } if(configuration != null) { final String identityAgent = LocalFactory.get(configuration).getAbsolute(); log.debug("Determined identity agent {} for {}", identityAgent, host.getHostname()); @@ -404,20 +395,22 @@ public class SFTPSession extends Session { } @Override - protected void logout() throws BackgroundException { + public void logout() throws BackgroundException { try { - if(null == sftp) { - return; + if(null != sftp) { + sftp.close(); } - sftp.close(); } catch(IOException e) { throw new SFTPExceptionMappingService().map(e); } + finally { + super.logout(); + } } @Override - public void disconnect() { + public void disconnect() throws BackgroundException { try { client.close(); } diff --git a/ssh/src/main/java/ch/cyberduck/core/sftp/SFTPTouchFeature.java b/ssh/src/main/java/ch/cyberduck/core/sftp/SFTPTouchFeature.java index 494686d3f0..8626d62b06 100644 --- a/ssh/src/main/java/ch/cyberduck/core/sftp/SFTPTouchFeature.java +++ b/ssh/src/main/java/ch/cyberduck/core/sftp/SFTPTouchFeature.java @@ -21,6 +21,7 @@ import ch.cyberduck.core.Path; import ch.cyberduck.core.Permission; import ch.cyberduck.core.exception.BackgroundException; import ch.cyberduck.core.features.Touch; +import ch.cyberduck.core.features.Write; import ch.cyberduck.core.transfer.TransferStatus; import java.io.IOException; @@ -39,7 +40,7 @@ public class SFTPTouchFeature implements Touch { } @Override - public Path touch(final Path file, final TransferStatus status) throws BackgroundException { + public Path touch(final Write writer, final Path file, final TransferStatus status) throws BackgroundException { try { final FileAttributes attrs; if(Permission.EMPTY != status.getPermission()) { diff --git a/ssh/src/main/java/ch/cyberduck/core/sftp/SFTPUploadFeature.java b/ssh/src/main/java/ch/cyberduck/core/sftp/SFTPUploadFeature.java index 8c9aec6134..428690ef34 100644 --- a/ssh/src/main/java/ch/cyberduck/core/sftp/SFTPUploadFeature.java +++ b/ssh/src/main/java/ch/cyberduck/core/sftp/SFTPUploadFeature.java @@ -16,6 +16,7 @@ package ch.cyberduck.core.sftp; */ import ch.cyberduck.core.Path; +import ch.cyberduck.core.Session; import ch.cyberduck.core.exception.BackgroundException; import ch.cyberduck.core.features.Write; import ch.cyberduck.core.shared.DefaultUploadFeature; @@ -23,12 +24,8 @@ import ch.cyberduck.core.transfer.TransferStatus; public class SFTPUploadFeature extends DefaultUploadFeature { - public SFTPUploadFeature(final SFTPSession session) { - super(new SFTPWriteFeature(session)); - } - - public SFTPUploadFeature(final Write writer) { - super(writer); + public SFTPUploadFeature(final Session session) { + super(session); } @Override diff --git a/ssh/src/main/java/ch/cyberduck/core/sftp/auth/SFTPChallengeResponseAuthentication.java b/ssh/src/main/java/ch/cyberduck/core/sftp/auth/SFTPChallengeResponseAuthentication.java index 30162e31f6..2a5706101e 100644 --- a/ssh/src/main/java/ch/cyberduck/core/sftp/auth/SFTPChallengeResponseAuthentication.java +++ b/ssh/src/main/java/ch/cyberduck/core/sftp/auth/SFTPChallengeResponseAuthentication.java @@ -112,8 +112,8 @@ public class SFTPChallengeResponseAuthentication implements AuthenticationProvid } flag.set(true); return credentials - .withPassword(input.getPassword()) - .withSaved(input.isSaved()).getPassword().toCharArray(); + .setPassword(input.getPassword()) + .setSaved(input.isSaved()).getPassword().toCharArray(); } catch(LoginCanceledException e) { canceled.set(true); diff --git a/ssh/src/main/java/ch/cyberduck/core/sftp/auth/SFTPPublicKeyAuthentication.java b/ssh/src/main/java/ch/cyberduck/core/sftp/auth/SFTPPublicKeyAuthentication.java index 0827cc0895..c26e529556 100644 --- a/ssh/src/main/java/ch/cyberduck/core/sftp/auth/SFTPPublicKeyAuthentication.java +++ b/ssh/src/main/java/ch/cyberduck/core/sftp/auth/SFTPPublicKeyAuthentication.java @@ -107,7 +107,7 @@ public class SFTPPublicKeyAuthentication implements AuthenticationProvider identities = Arrays.asList(proxy.getIdentities()); - log.debug("Found {} identities", identities.size()); - return identities; + try { + final Identity[] retrieved = proxy.getIdentities(); + if(null == retrieved) { + return Collections.emptyList(); + } + final List identities = Arrays.asList(retrieved); + log.debug("Found {} identities", identities.size()); + return identities; + } + catch(Exception e) { + log.warn("Ignore failure reading identities from {}", proxy); + return Collections.emptyList(); + } } @Override diff --git a/ssh/src/main/java/ch/cyberduck/core/sftp/openssh/OpenSSHCredentialsConfigurator.java b/ssh/src/main/java/ch/cyberduck/core/sftp/openssh/OpenSSHCredentialsConfigurator.java index 82929a96b2..559cdf39f7 100644 --- a/ssh/src/main/java/ch/cyberduck/core/sftp/openssh/OpenSSHCredentialsConfigurator.java +++ b/ssh/src/main/java/ch/cyberduck/core/sftp/openssh/OpenSSHCredentialsConfigurator.java @@ -39,8 +39,8 @@ public class OpenSSHCredentialsConfigurator implements CredentialsConfigurator { @Override public Credentials configure(final Host host) { + final Credentials credentials = new Credentials(host.getCredentials()); if(StringUtils.isNotBlank(host.getHostname())) { - final Credentials credentials = new Credentials(host.getCredentials()); configuration.refresh(); // Update this host credentials from the OpenSSH configuration file in ~/.ssh/config final OpenSshConfig.Host entry = configuration.lookup(host.getHostname()); @@ -73,9 +73,8 @@ public class OpenSSHCredentialsConfigurator implements CredentialsConfigurator { } } } - return credentials; } - return CredentialsConfigurator.DISABLED.configure(host); + return credentials; } @Override diff --git a/ssh/src/main/java/ch/cyberduck/core/sftp/putty/PageantAuthenticator.java b/ssh/src/main/java/ch/cyberduck/core/sftp/putty/PageantAuthenticator.java index 7cb750d0aa..33e970faa5 100644 --- a/ssh/src/main/java/ch/cyberduck/core/sftp/putty/PageantAuthenticator.java +++ b/ssh/src/main/java/ch/cyberduck/core/sftp/putty/PageantAuthenticator.java @@ -23,7 +23,7 @@ import ch.cyberduck.core.sftp.auth.AgentAuthenticator; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; -import java.util.ArrayList; +import java.util.Arrays; import java.util.Collection; import java.util.Collections; import java.util.List; @@ -62,15 +62,19 @@ public class PageantAuthenticator extends AgentAuthenticator { return Collections.emptyList(); } log.debug("Retrieve identities from proxy {}", proxy); - final List identities = new ArrayList(); try { - Collections.addAll(identities, proxy.getIdentities()); + final Identity[] retrieved = proxy.getIdentities(); + if(null == retrieved) { + return Collections.emptyList(); + } + final List identities = Arrays.asList(retrieved); log.debug("Found {} identities", identities.size()); + return identities; } catch(Exception e) { log.warn("Ignore failure reading identities from {}", proxy); + return Collections.emptyList(); } - return identities; } @Override diff --git a/ssh/src/test/java/ch/cyberduck/core/cryptomator/CopyWorkerTest.java b/ssh/src/test/java/ch/cyberduck/core/cryptomator/CopyWorkerTest.java index 12d63481c4..3466e20d78 100644 --- a/ssh/src/test/java/ch/cyberduck/core/cryptomator/CopyWorkerTest.java +++ b/ssh/src/test/java/ch/cyberduck/core/cryptomator/CopyWorkerTest.java @@ -29,11 +29,11 @@ import ch.cyberduck.core.cryptomator.features.CryptoWriteFeature; import ch.cyberduck.core.features.AttributesFinder; import ch.cyberduck.core.features.Directory; import ch.cyberduck.core.features.Find; +import ch.cyberduck.core.features.Write; import ch.cyberduck.core.io.StreamCopier; import ch.cyberduck.core.pool.SessionPool; import ch.cyberduck.core.sftp.AbstractSFTPTest; import ch.cyberduck.core.sftp.SFTPAttributesFinderFeature; -import ch.cyberduck.core.sftp.SFTPDeleteFeature; import ch.cyberduck.core.sftp.SFTPDirectoryFeature; import ch.cyberduck.core.sftp.SFTPFindFeature; import ch.cyberduck.core.sftp.SFTPHomeDirectoryService; @@ -84,7 +84,7 @@ public class CopyWorkerTest extends AbstractSFTPTest { session.withRegistry(registry); final byte[] content = RandomUtils.nextBytes(40500); final TransferStatus status = new TransferStatus(); - new CryptoBulkFeature<>(session, new DisabledBulkFeature(), new SFTPDeleteFeature(session), cryptomator).pre(Transfer.Type.upload, Collections.singletonMap(new TransferItem(source), status), new DisabledConnectionCallback()); + new CryptoBulkFeature<>(session, new DisabledBulkFeature(), 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 SFTPWriteFeature(session), cryptomator).write(source, status.setLength(content.length), new DisabledConnectionCallback())); assertTrue(cryptomator.getFeature(session, Find.class, new DefaultFindFeature(session)).find(source)); assertEquals(content.length, cryptomator.getFeature(session, AttributesFinder.class, new SFTPAttributesFinderFeature(session)).find(source).getSize()); @@ -109,10 +109,11 @@ public class CopyWorkerTest extends AbstractSFTPTest { cryptomator.create(session, new VaultCredentials("test"), vaultVersion); final DefaultVaultRegistry registry = new DefaultVaultRegistry(new DisabledPasswordCallback(), cryptomator); session.withRegistry(registry); - new CryptoTouchFeature<>(session, new DefaultTouchFeature<>(new SFTPWriteFeature(session) - ), new SFTPWriteFeature(session), cryptomator).touch(source, new TransferStatus()); + new CryptoTouchFeature<>(session, new DefaultTouchFeature( + session), cryptomator).touch(new CryptoWriteFeature<>(session, new SFTPWriteFeature(session), cryptomator), source, new TransferStatus()); assertTrue(cryptomator.getFeature(session, Find.class, new DefaultFindFeature(session)).find(source)); - cryptomator.getFeature(session, Directory.class, new SFTPDirectoryFeature(session)).mkdir(targetFolder, new TransferStatus()); + cryptomator.getFeature(session, Directory.class, new SFTPDirectoryFeature(session)).mkdir( + new CryptoWriteFeature<>(session, new SFTPWriteFeature(session), cryptomator), 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); @@ -133,10 +134,11 @@ public class CopyWorkerTest extends AbstractSFTPTest { cryptomator.create(session, new VaultCredentials("test"), vaultVersion); final DefaultVaultRegistry registry = new DefaultVaultRegistry(new DisabledPasswordCallback(), cryptomator); session.withRegistry(registry); - new CryptoTouchFeature<>(session, new DefaultTouchFeature<>(new SFTPWriteFeature(session) - ), new SFTPWriteFeature(session), cryptomator).touch(source, new TransferStatus()); + new CryptoTouchFeature<>(session, new DefaultTouchFeature( + session), cryptomator).touch(new CryptoWriteFeature<>(session, new SFTPWriteFeature(session), cryptomator), source, new TransferStatus()); assertTrue(cryptomator.getFeature(session, Find.class, new DefaultFindFeature(session)).find(source)); - cryptomator.getFeature(session, Directory.class, new SFTPDirectoryFeature(session)).mkdir(targetFolder, new TransferStatus()); + cryptomator.getFeature(session, Directory.class, new SFTPDirectoryFeature(session)).mkdir( + cryptomator.getFeature(session, Write.class, new SFTPWriteFeature(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); @@ -155,10 +157,11 @@ public class CopyWorkerTest extends AbstractSFTPTest { cryptomator.create(session, new VaultCredentials("test"), vaultVersion); final DefaultVaultRegistry registry = new DefaultVaultRegistry(new DisabledPasswordCallback(), cryptomator); session.withRegistry(registry); - cryptomator.getFeature(session, Directory.class, new SFTPDirectoryFeature(session)).mkdir(folder, new TransferStatus()); + cryptomator.getFeature(session, Directory.class, new SFTPDirectoryFeature(session)).mkdir( + cryptomator.getFeature(session, Write.class, new SFTPWriteFeature(session)), folder, new TransferStatus()); assertTrue(cryptomator.getFeature(session, Find.class, new DefaultFindFeature(session)).find(folder)); - new CryptoTouchFeature<>(session, new DefaultTouchFeature<>(new SFTPWriteFeature(session) - ), new SFTPWriteFeature(session), cryptomator).touch(file, new TransferStatus()); + new CryptoTouchFeature<>(session, new DefaultTouchFeature( + session), cryptomator).touch(new CryptoWriteFeature<>(session, new SFTPWriteFeature(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)); @@ -181,7 +184,7 @@ public class CopyWorkerTest extends AbstractSFTPTest { final Path home = new SFTPHomeDirectoryService(session).find(); 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)); - new SFTPTouchFeature(session).touch(cleartextFile, new TransferStatus()); + new SFTPTouchFeature(session).touch(new SFTPWriteFeature(session), cleartextFile, new TransferStatus()); assertTrue(new SFTPFindFeature(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)); @@ -189,7 +192,8 @@ public class CopyWorkerTest extends AbstractSFTPTest { cryptomator.create(session, new VaultCredentials("test"), vaultVersion); final DefaultVaultRegistry registry = new DefaultVaultRegistry(new DisabledPasswordCallback(), cryptomator); session.withRegistry(registry); - cryptomator.getFeature(session, Directory.class, new SFTPDirectoryFeature(session)).mkdir(encryptedFolder, new TransferStatus()); + cryptomator.getFeature(session, Directory.class, new SFTPDirectoryFeature(session)).mkdir( + cryptomator.getFeature(session, Write.class, new SFTPWriteFeature(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()); @@ -206,8 +210,8 @@ public class CopyWorkerTest extends AbstractSFTPTest { 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 SFTPDirectoryFeature(session).mkdir(cleartextFolder, new TransferStatus()); - new SFTPTouchFeature(session).touch(cleartextFile, new TransferStatus()); + new SFTPDirectoryFeature(session).mkdir(new SFTPWriteFeature(session), cleartextFolder, new TransferStatus()); + new SFTPTouchFeature(session).touch(new SFTPWriteFeature(session), cleartextFile, new TransferStatus()); assertTrue(new SFTPFindFeature(session).find(cleartextFolder)); assertTrue(new SFTPFindFeature(session).find(cleartextFile)); final CryptoVault cryptomator = new CryptoVault(vault); @@ -232,17 +236,18 @@ public class CopyWorkerTest extends AbstractSFTPTest { final Path home = new SFTPHomeDirectoryService(session).find(); 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 SFTPDirectoryFeature(session).mkdir(clearFolder, new TransferStatus()); + new SFTPDirectoryFeature(session).mkdir(new SFTPWriteFeature(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 SFTPDirectoryFeature(session)).mkdir(encryptedFolder, new TransferStatus()); + cryptomator.getFeature(session, Directory.class, new SFTPDirectoryFeature(session)).mkdir( + cryptomator.getFeature(session, Write.class, new SFTPWriteFeature(session)), encryptedFolder, new TransferStatus()); assertTrue(cryptomator.getFeature(session, Find.class, new DefaultFindFeature(session)).find(encryptedFolder)); - new CryptoTouchFeature<>(session, new DefaultTouchFeature<>(new SFTPWriteFeature(session) - ), new SFTPWriteFeature(session), cryptomator).touch(encryptedFile, new TransferStatus()); + new CryptoTouchFeature<>(session, new DefaultTouchFeature( + session), cryptomator).touch(new CryptoWriteFeature<>(session, new SFTPWriteFeature(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)); @@ -264,10 +269,11 @@ public class CopyWorkerTest extends AbstractSFTPTest { cryptomator.create(session, new VaultCredentials("test"), vaultVersion); final DefaultVaultRegistry registry = new DefaultVaultRegistry(new DisabledPasswordCallback(), cryptomator); session.withRegistry(registry); - cryptomator.getFeature(session, Directory.class, new SFTPDirectoryFeature(session)).mkdir(encryptedFolder, new TransferStatus()); + cryptomator.getFeature(session, Directory.class, new SFTPDirectoryFeature(session)).mkdir( + cryptomator.getFeature(session, Write.class, new SFTPWriteFeature(session)), encryptedFolder, new TransferStatus()); assertTrue(cryptomator.getFeature(session, Find.class, new DefaultFindFeature(session)).find(encryptedFolder)); - new CryptoTouchFeature<>(session, new DefaultTouchFeature<>(new SFTPWriteFeature(session) - ), new SFTPWriteFeature(session), cryptomator).touch(encryptedFile, new TransferStatus()); + new CryptoTouchFeature<>(session, new DefaultTouchFeature( + session), cryptomator).touch(new CryptoWriteFeature<>(session, new SFTPWriteFeature(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)); diff --git a/ssh/src/test/java/ch/cyberduck/core/cryptomator/DefaultTouchFeatureTest.java b/ssh/src/test/java/ch/cyberduck/core/cryptomator/DefaultTouchFeatureTest.java index 23e3d8ce56..3e94c89b21 100644 --- a/ssh/src/test/java/ch/cyberduck/core/cryptomator/DefaultTouchFeatureTest.java +++ b/ssh/src/test/java/ch/cyberduck/core/cryptomator/DefaultTouchFeatureTest.java @@ -19,6 +19,7 @@ import ch.cyberduck.core.DisabledLoginCallback; import ch.cyberduck.core.DisabledPasswordCallback; import ch.cyberduck.core.Path; 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.sftp.AbstractSFTPTest; @@ -55,8 +56,8 @@ public class DefaultTouchFeatureTest extends AbstractSFTPTest { final CryptoVault cryptomator = new CryptoVault(vault); cryptomator.create(session, new VaultCredentials("test"), vaultVersion); session.withRegistry(new DefaultVaultRegistry(new DisabledPasswordCallback(), cryptomator)); - new CryptoTouchFeature<>(session, new DefaultTouchFeature<>(new SFTPWriteFeature(session) - ), new SFTPWriteFeature(session), cryptomator).touch(test, new TransferStatus()); + new CryptoTouchFeature<>(session, new DefaultTouchFeature( + session), cryptomator).touch(new CryptoWriteFeature<>(session, new SFTPWriteFeature(session), cryptomator), test, new TransferStatus()); assertTrue(cryptomator.getFeature(session, Find.class, new DefaultFindFeature(session)).find(test)); cryptomator.getFeature(session, Delete.class, new SFTPDeleteFeature(session)).delete(Arrays.asList(test, vault), new DisabledLoginCallback(), new Delete.DisabledCallback()); } diff --git a/ssh/src/test/java/ch/cyberduck/core/cryptomator/MoveWorkerTest.java b/ssh/src/test/java/ch/cyberduck/core/cryptomator/MoveWorkerTest.java index 4638b98101..57ca0de2b5 100644 --- a/ssh/src/test/java/ch/cyberduck/core/cryptomator/MoveWorkerTest.java +++ b/ssh/src/test/java/ch/cyberduck/core/cryptomator/MoveWorkerTest.java @@ -32,6 +32,7 @@ import ch.cyberduck.core.exception.NotfoundException; 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.io.StreamCopier; import ch.cyberduck.core.pool.SessionPool; import ch.cyberduck.core.sftp.AbstractSFTPTest; @@ -86,7 +87,7 @@ public class MoveWorkerTest extends AbstractSFTPTest { session.withRegistry(new DefaultVaultRegistry(new DisabledPasswordCallback(), cryptomator)); final byte[] content = RandomUtils.nextBytes(40500); final TransferStatus status = new TransferStatus(); - new CryptoBulkFeature<>(session, new DisabledBulkFeature(), new SFTPDeleteFeature(session), cryptomator).pre(Transfer.Type.upload, Collections.singletonMap(new TransferItem(source), status), new DisabledConnectionCallback()); + new CryptoBulkFeature<>(session, new DisabledBulkFeature(), 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 SFTPWriteFeature(session), cryptomator).write(source, status.setLength(content.length), new DisabledConnectionCallback())); assertTrue(cryptomator.getFeature(session, Find.class, new DefaultFindFeature(session)).find(source)); final MoveWorker worker = new MoveWorker(Collections.singletonMap(source, target), new SessionPool.SingleSessionPool(session), PathCache.empty(), new DisabledProgressListener(), new DisabledLoginCallback()); @@ -109,10 +110,11 @@ public class MoveWorkerTest extends AbstractSFTPTest { final CryptoVault cryptomator = new CryptoVault(vault); cryptomator.create(session, new VaultCredentials("test"), vaultVersion); session.withRegistry(new DefaultVaultRegistry(new DisabledPasswordCallback(), cryptomator)); - new CryptoTouchFeature<>(session, new DefaultTouchFeature<>(new SFTPWriteFeature(session) - ), new SFTPWriteFeature(session), cryptomator).touch(source, new TransferStatus()); + new CryptoTouchFeature<>(session, new DefaultTouchFeature( + session), cryptomator).touch(new CryptoWriteFeature<>(session, new SFTPWriteFeature(session), cryptomator), source, new TransferStatus()); assertTrue(cryptomator.getFeature(session, Find.class, new DefaultFindFeature(session)).find(source)); - cryptomator.getFeature(session, Directory.class, new SFTPDirectoryFeature(session)).mkdir(targetFolder, new TransferStatus()); + cryptomator.getFeature(session, Directory.class, new SFTPDirectoryFeature(session)).mkdir( + cryptomator.getFeature(session, Write.class, new SFTPWriteFeature(session)), targetFolder, new TransferStatus()); assertTrue(cryptomator.getFeature(session, Find.class, new DefaultFindFeature(session)).find(targetFolder)); final MoveWorker worker = new MoveWorker(Collections.singletonMap(source, target), new SessionPool.SingleSessionPool(session), PathCache.empty(), new DisabledProgressListener(), new DisabledLoginCallback()); worker.run(session); @@ -132,10 +134,11 @@ public class MoveWorkerTest extends AbstractSFTPTest { final CryptoVault cryptomator = new CryptoVault(vault); cryptomator.create(session, new VaultCredentials("test"), vaultVersion); session.withRegistry(new DefaultVaultRegistry(new DisabledPasswordCallback(), cryptomator)); - new CryptoTouchFeature<>(session, new DefaultTouchFeature<>(new SFTPWriteFeature(session) - ), new SFTPWriteFeature(session), cryptomator).touch(source, new TransferStatus()); + new CryptoTouchFeature<>(session, new DefaultTouchFeature( + session), cryptomator).touch(new CryptoWriteFeature<>(session, new SFTPWriteFeature(session), cryptomator), source, new TransferStatus()); assertTrue(cryptomator.getFeature(session, Find.class, new DefaultFindFeature(session)).find(source)); - cryptomator.getFeature(session, Directory.class, new SFTPDirectoryFeature(session)).mkdir(targetFolder, new TransferStatus()); + cryptomator.getFeature(session, Directory.class, new SFTPDirectoryFeature(session)).mkdir( + cryptomator.getFeature(session, Write.class, new SFTPWriteFeature(session)), targetFolder, new TransferStatus()); assertTrue(cryptomator.getFeature(session, Find.class, new DefaultFindFeature(session)).find(targetFolder)); final MoveWorker worker = new MoveWorker(Collections.singletonMap(source, target), new SessionPool.SingleSessionPool(session), PathCache.empty(), new DisabledProgressListener(), new DisabledLoginCallback()); worker.run(session); @@ -153,10 +156,11 @@ public class MoveWorkerTest extends AbstractSFTPTest { final CryptoVault cryptomator = new CryptoVault(vault); cryptomator.create(session, new VaultCredentials("test"), vaultVersion); session.withRegistry(new DefaultVaultRegistry(new DisabledPasswordCallback(), cryptomator)); - cryptomator.getFeature(session, Directory.class, new SFTPDirectoryFeature(session)).mkdir(folder, new TransferStatus()); + cryptomator.getFeature(session, Directory.class, new SFTPDirectoryFeature(session)).mkdir( + cryptomator.getFeature(session, Write.class, new SFTPWriteFeature(session)), folder, new TransferStatus()); assertTrue(cryptomator.getFeature(session, Find.class, new DefaultFindFeature(session)).find(folder)); - new CryptoTouchFeature<>(session, new DefaultTouchFeature<>(new SFTPWriteFeature(session) - ), new SFTPWriteFeature(session), cryptomator).touch(file, new TransferStatus()); + new CryptoTouchFeature<>(session, new DefaultTouchFeature( + session), cryptomator).touch(new CryptoWriteFeature<>(session, new SFTPWriteFeature(session), cryptomator), file, new TransferStatus()); assertTrue(cryptomator.getFeature(session, Find.class, new DefaultFindFeature(session)).find(file)); // rename file final Path fileRenamed = new Path(folder, "f1", EnumSet.of(Path.Type.file)); @@ -185,7 +189,7 @@ public class MoveWorkerTest extends AbstractSFTPTest { final Path home = new SFTPHomeDirectoryService(session).find(); final Path vault = new Path(home, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory)); final Path clearFile = new Path(home, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)); - new SFTPTouchFeature(session).touch(clearFile, new TransferStatus()); + new SFTPTouchFeature(session).touch(new SFTPWriteFeature(session), clearFile, new TransferStatus()); assertTrue(new SFTPFindFeature(session).find(clearFile)); 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)); @@ -193,7 +197,8 @@ public class MoveWorkerTest extends AbstractSFTPTest { cryptomator.create(session, new VaultCredentials("test"), vaultVersion); final DefaultVaultRegistry registry = new DefaultVaultRegistry(new DisabledPasswordCallback(), cryptomator); session.withRegistry(registry); - cryptomator.getFeature(session, Directory.class, new SFTPDirectoryFeature(session)).mkdir(encryptedFolder, new TransferStatus()); + cryptomator.getFeature(session, Directory.class, new SFTPDirectoryFeature(session)).mkdir( + cryptomator.getFeature(session, Write.class, new SFTPWriteFeature(session)), encryptedFolder, new TransferStatus()); assertTrue(cryptomator.getFeature(session, Find.class, new DefaultFindFeature(session)).find(encryptedFolder)); // move file into vault final MoveWorker worker = new MoveWorker(Collections.singletonMap(clearFile, encryptedFile), new SessionPool.SingleSessionPool(session), PathCache.empty(), new DisabledProgressListener(), new DisabledLoginCallback()); @@ -210,8 +215,8 @@ public class MoveWorkerTest extends AbstractSFTPTest { 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)); final Path clearFile = new Path(clearFolder, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)); - new SFTPDirectoryFeature(session).mkdir(clearFolder, new TransferStatus()); - new SFTPTouchFeature(session).touch(clearFile, new TransferStatus()); + new SFTPDirectoryFeature(session).mkdir(new SFTPWriteFeature(session), clearFolder, new TransferStatus()); + new SFTPTouchFeature(session).touch(new SFTPWriteFeature(session), clearFile, new TransferStatus()); assertTrue(new SFTPFindFeature(session).find(clearFolder)); assertTrue(new SFTPFindFeature(session).find(clearFile)); final CryptoVault cryptomator = new CryptoVault(vault); @@ -236,17 +241,18 @@ public class MoveWorkerTest extends AbstractSFTPTest { final Path home = new SFTPHomeDirectoryService(session).find(); 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 SFTPDirectoryFeature(session).mkdir(clearFolder, new TransferStatus()); + new SFTPDirectoryFeature(session).mkdir(new SFTPWriteFeature(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 SFTPDirectoryFeature(session)).mkdir(encryptedFolder, new TransferStatus()); + cryptomator.getFeature(session, Directory.class, new SFTPDirectoryFeature(session)).mkdir( + cryptomator.getFeature(session, Write.class, new SFTPWriteFeature(session)), encryptedFolder, new TransferStatus()); assertTrue(cryptomator.getFeature(session, Find.class, new DefaultFindFeature(session)).find(encryptedFolder)); - new CryptoTouchFeature<>(session, new DefaultTouchFeature<>(new SFTPWriteFeature(session) - ), new SFTPWriteFeature(session), cryptomator).touch(encryptedFile, new TransferStatus()); + new CryptoTouchFeature<>(session, new DefaultTouchFeature( + session), cryptomator).touch(new CryptoWriteFeature<>(session, new SFTPWriteFeature(session), cryptomator), encryptedFile, new TransferStatus()); assertTrue(cryptomator.getFeature(session, Find.class, new DefaultFindFeature(session)).find(encryptedFile)); // move file outside vault final Path fileRenamed = new Path(clearFolder, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)); @@ -269,10 +275,11 @@ public class MoveWorkerTest extends AbstractSFTPTest { cryptomator.create(session, new VaultCredentials("test"), vaultVersion); final DefaultVaultRegistry registry = new DefaultVaultRegistry(new DisabledPasswordCallback(), cryptomator); session.withRegistry(registry); - cryptomator.getFeature(session, Directory.class, new SFTPDirectoryFeature(session)).mkdir(encryptedFolder, new TransferStatus()); + cryptomator.getFeature(session, Directory.class, new SFTPDirectoryFeature(session)).mkdir( + cryptomator.getFeature(session, Write.class, new SFTPWriteFeature(session)), encryptedFolder, new TransferStatus()); assertTrue(cryptomator.getFeature(session, Find.class, new DefaultFindFeature(session)).find(encryptedFolder)); - new CryptoTouchFeature<>(session, new DefaultTouchFeature<>(new SFTPWriteFeature(session) - ), new SFTPWriteFeature(session), cryptomator).touch(encryptedFile, new TransferStatus()); + new CryptoTouchFeature<>(session, new DefaultTouchFeature( + session), cryptomator).touch(new CryptoWriteFeature<>(session, new SFTPWriteFeature(session), cryptomator), encryptedFile, new TransferStatus()); assertTrue(cryptomator.getFeature(session, Find.class, new DefaultFindFeature(session)).find(encryptedFile)); // move directory outside vault final Path directoryRenamed = new Path(home, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory)); diff --git a/ssh/src/test/java/ch/cyberduck/core/cryptomator/SFTPAttributesFinderFeatureTest.java b/ssh/src/test/java/ch/cyberduck/core/cryptomator/SFTPAttributesFinderFeatureTest.java index 77c4c1e09a..0e48ecafaa 100644 --- a/ssh/src/test/java/ch/cyberduck/core/cryptomator/SFTPAttributesFinderFeatureTest.java +++ b/ssh/src/test/java/ch/cyberduck/core/cryptomator/SFTPAttributesFinderFeatureTest.java @@ -27,6 +27,7 @@ import ch.cyberduck.core.PathAttributes; import ch.cyberduck.core.PathCache; import ch.cyberduck.core.cryptomator.features.CryptoListService; import ch.cyberduck.core.cryptomator.features.CryptoTouchFeature; +import ch.cyberduck.core.cryptomator.features.CryptoWriteFeature; import ch.cyberduck.core.features.AttributesFinder; import ch.cyberduck.core.features.Delete; import ch.cyberduck.core.sftp.AbstractSFTPTest; @@ -63,9 +64,9 @@ public class SFTPAttributesFinderFeatureTest extends AbstractSFTPTest { final CryptoVault cryptomator = new CryptoVault(vault); cryptomator.create(session, new VaultCredentials("test"), vaultVersion); session.withRegistry(new DefaultVaultRegistry(new DisabledPasswordCallback(), cryptomator)); - final Path test = new CryptoTouchFeature<>(session, new DefaultTouchFeature<>(new SFTPWriteFeature(session) - ), new SFTPWriteFeature(session), cryptomator).touch( - new Path(vault, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)), new TransferStatus()); + final Path test = new CryptoTouchFeature<>(session, new DefaultTouchFeature( + session), cryptomator).touch( + new CryptoWriteFeature<>(session, new SFTPWriteFeature(session), cryptomator), new Path(vault, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)), new TransferStatus()); test.attributes().setSize(0L); final PathAttributes attributes = cryptomator.getFeature(session, AttributesFinder.class, new DefaultAttributesFinderFeature(session)).find(test); assertNotNull(attributes); @@ -84,9 +85,9 @@ public class SFTPAttributesFinderFeatureTest extends AbstractSFTPTest { final CryptoVault cryptomator = new CryptoVault(vault); cryptomator.create(session, new VaultCredentials("test"), vaultVersion); session.withRegistry(new DefaultVaultRegistry(new DisabledPasswordCallback(), cryptomator)); - final Path test = new CryptoTouchFeature<>(session, new DefaultTouchFeature<>(new SFTPWriteFeature(session) - ), new SFTPWriteFeature(session), cryptomator).touch( - new Path(vault, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)), new TransferStatus()); + final Path test = new CryptoTouchFeature<>(session, new DefaultTouchFeature( + session), cryptomator).touch( + new CryptoWriteFeature<>(session, new SFTPWriteFeature(session), cryptomator), new Path(vault, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)), new TransferStatus()); test.attributes().setSize(0L); final PathAttributes attributes = cryptomator.getFeature(session, AttributesFinder.class, new DefaultAttributesFinderFeature(session)).find(test); assertNotNull(attributes); @@ -101,9 +102,9 @@ public class SFTPAttributesFinderFeatureTest extends AbstractSFTPTest { final CryptoVault cryptomator = new CryptoVault(vault); cryptomator.create(session, new VaultCredentials("test"), vaultVersion); session.withRegistry(new DefaultVaultRegistry(new DisabledPasswordCallback(), cryptomator)); - final Path test = new CryptoTouchFeature<>(session, new DefaultTouchFeature<>(new SFTPWriteFeature(session) - ), new SFTPWriteFeature(session), cryptomator).touch( - new Path(vault, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)), new TransferStatus()); + final Path test = new CryptoTouchFeature<>(session, new DefaultTouchFeature( + session), cryptomator).touch( + new CryptoWriteFeature<>(session, new SFTPWriteFeature(session), cryptomator), new Path(vault, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)), new TransferStatus()); final Path found = new CryptoListService(session, new SFTPListService(session), cryptomator).list(test.getParent(), new DisabledListProgressListener()).get(test); assertEquals(0L, found.attributes().getSize()); final Cache cache = new PathCache(1); diff --git a/ssh/src/test/java/ch/cyberduck/core/cryptomator/SFTPDirectoryFeatureTest.java b/ssh/src/test/java/ch/cyberduck/core/cryptomator/SFTPDirectoryFeatureTest.java index c1fa6641cf..c5e2acc351 100644 --- a/ssh/src/test/java/ch/cyberduck/core/cryptomator/SFTPDirectoryFeatureTest.java +++ b/ssh/src/test/java/ch/cyberduck/core/cryptomator/SFTPDirectoryFeatureTest.java @@ -22,11 +22,13 @@ import ch.cyberduck.core.Path; 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.sftp.AbstractSFTPTest; import ch.cyberduck.core.sftp.SFTPDeleteFeature; import ch.cyberduck.core.sftp.SFTPDirectoryFeature; import ch.cyberduck.core.sftp.SFTPFindFeature; import ch.cyberduck.core.sftp.SFTPHomeDirectoryService; +import ch.cyberduck.core.sftp.SFTPWriteFeature; import ch.cyberduck.core.shared.DefaultFindFeature; import ch.cyberduck.core.transfer.TransferStatus; import ch.cyberduck.core.vault.DefaultVaultRegistry; @@ -56,11 +58,10 @@ public class SFTPDirectoryFeatureTest extends AbstractSFTPTest { final CryptoVault cryptomator = new CryptoVault(vault); cryptomator.create(session, new VaultCredentials("test"), vaultVersion); session.withRegistry(new DefaultVaultRegistry(new DisabledPasswordCallback(), cryptomator)); - // cryptomator.getFeature(session, Directory.class, new SFTPDirectoryFeature(session)).mkdir(test, null, new TransferStatus()); - cryptomator.getFeature(session, Directory.class, new SFTPDirectoryFeature(session)).mkdir(test, new TransferStatus()); + cryptomator.getFeature(session, Directory.class, new SFTPDirectoryFeature(session)).mkdir( + cryptomator.getFeature(session, Write.class, new SFTPWriteFeature(session)), test, new TransferStatus()); assertTrue(cryptomator.getFeature(session, Find.class, new SFTPFindFeature(session)).find(test)); assertTrue(cryptomator.getFeature(session, Find.class, new DefaultFindFeature(session)).find(test)); -// cryptomator.getFeature(session, Delete.class, new SFTPDeleteFeature(session)).delete(Arrays.asList(test, vault), new DisabledLoginCallback(), new Delete.DisabledCallback()); cryptomator.getFeature(session, Delete.class, new SFTPDeleteFeature(session)).delete(Arrays.asList(test, vault), new DisabledLoginCallback(), new Delete.DisabledCallback()); } @@ -73,7 +74,7 @@ public class SFTPDirectoryFeatureTest extends AbstractSFTPTest { final CryptoVault cryptomator = new CryptoVault(vault); cryptomator.create(session, new VaultCredentials("test"), vaultVersion); session.withRegistry(new DefaultVaultRegistry(new DisabledPasswordCallback(), cryptomator)); - cryptomator.getFeature(session, Directory.class, new SFTPDirectoryFeature(session)).mkdir(test, new TransferStatus()); + cryptomator.getFeature(session, Directory.class, new SFTPDirectoryFeature(session)).mkdir(new SFTPWriteFeature(session), test, new TransferStatus()); assertTrue(cryptomator.getFeature(session, Find.class, new SFTPFindFeature(session)).find(test)); assertTrue(cryptomator.getFeature(session, Find.class, new DefaultFindFeature(session)).find(test)); cryptomator.getFeature(session, Delete.class, new SFTPDeleteFeature(session)).delete(Arrays.asList(test, vault), new DisabledLoginCallback(), new Delete.DisabledCallback()); diff --git a/ssh/src/test/java/ch/cyberduck/core/cryptomator/SFTPFindFeatureTest.java b/ssh/src/test/java/ch/cyberduck/core/cryptomator/SFTPFindFeatureTest.java index bb1acffba3..0f2735a857 100644 --- a/ssh/src/test/java/ch/cyberduck/core/cryptomator/SFTPFindFeatureTest.java +++ b/ssh/src/test/java/ch/cyberduck/core/cryptomator/SFTPFindFeatureTest.java @@ -20,6 +20,7 @@ import ch.cyberduck.core.DisabledLoginCallback; import ch.cyberduck.core.DisabledPasswordCallback; import ch.cyberduck.core.Path; 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.sftp.AbstractSFTPTest; @@ -59,8 +60,8 @@ public class SFTPFindFeatureTest extends AbstractSFTPTest { cryptomator.create(session, new VaultCredentials("test"), vaultVersion); session.withRegistry(new DefaultVaultRegistry(new DisabledPasswordCallback(), cryptomator)); assertFalse(cryptomator.getFeature(session, Find.class, new DefaultFindFeature(session)).find(new Path(vault, "a", EnumSet.of(Path.Type.directory)))); - new CryptoTouchFeature<>(session, new CryptoTouchFeature<>(session, new DefaultTouchFeature<>(new SFTPWriteFeature(session) - ), new SFTPWriteFeature(session), cryptomator), new SFTPWriteFeature(session), cryptomator).touch(test, new TransferStatus()); + new CryptoTouchFeature<>(session, new CryptoTouchFeature<>(session, new DefaultTouchFeature( + session), cryptomator), cryptomator).touch(new CryptoWriteFeature<>(session, new SFTPWriteFeature(session), cryptomator), test, new TransferStatus()); assertTrue(cryptomator.getFeature(session, Find.class, new DefaultFindFeature(session)).find(test)); cryptomator.getFeature(session, Delete.class, new SFTPDeleteFeature(session)).delete(Arrays.asList(test, vault), new DisabledLoginCallback(), new Delete.DisabledCallback()); } @@ -75,8 +76,8 @@ public class SFTPFindFeatureTest extends AbstractSFTPTest { final CryptoVault cryptomator = new CryptoVault(vault); cryptomator.create(session, new VaultCredentials("test"), vaultVersion); session.withRegistry(new DefaultVaultRegistry(new DisabledPasswordCallback(), cryptomator)); - new CryptoTouchFeature<>(session, new CryptoTouchFeature<>(session, new DefaultTouchFeature<>(new SFTPWriteFeature(session) - ), new SFTPWriteFeature(session), cryptomator), new SFTPWriteFeature(session), cryptomator).touch(test, new TransferStatus()); + new CryptoTouchFeature<>(session, new CryptoTouchFeature<>(session, new DefaultTouchFeature( + session), cryptomator), cryptomator).touch(new CryptoWriteFeature<>(session, new SFTPWriteFeature(session), cryptomator), test, new TransferStatus()); assertTrue(cryptomator.getFeature(session, Find.class, new DefaultFindFeature(session)).find(test)); cryptomator.getFeature(session, Delete.class, new SFTPDeleteFeature(session)).delete(Arrays.asList(test, vault), new DisabledLoginCallback(), new Delete.DisabledCallback()); } diff --git a/ssh/src/test/java/ch/cyberduck/core/cryptomator/SFTPListServiceTest.java b/ssh/src/test/java/ch/cyberduck/core/cryptomator/SFTPListServiceTest.java index 050acebbab..f1a060a895 100644 --- a/ssh/src/test/java/ch/cyberduck/core/cryptomator/SFTPListServiceTest.java +++ b/ssh/src/test/java/ch/cyberduck/core/cryptomator/SFTPListServiceTest.java @@ -27,6 +27,7 @@ import ch.cyberduck.core.PathCache; 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.cryptomator.features.CryptoWriteFeature; import ch.cyberduck.core.exception.BackgroundException; import ch.cyberduck.core.features.Delete; import ch.cyberduck.core.sftp.AbstractSFTPTest; @@ -66,8 +67,8 @@ public class SFTPListServiceTest extends AbstractSFTPTest { cryptomator.create(session, new VaultCredentials("test"), vaultVersion); session.withRegistry(new DefaultVaultRegistry(new DisabledPasswordCallback(), cryptomator)); assertTrue(new CryptoListService(session, new SFTPListService(session), cryptomator).list(vault, new DisabledListProgressListener()).isEmpty()); - new CryptoTouchFeature<>(session, new CryptoTouchFeature<>(session, new DefaultTouchFeature<>(new SFTPWriteFeature(session)), - new SFTPWriteFeature(session), cryptomator), new SFTPWriteFeature(session), cryptomator).touch(test, new TransferStatus()); + new CryptoTouchFeature<>(session, new CryptoTouchFeature<>(session, new DefaultTouchFeature(session), + cryptomator), cryptomator).touch(new CryptoWriteFeature<>(session, new SFTPWriteFeature(session), cryptomator), test, new TransferStatus()); assertEquals(test, new CryptoListService(session, new SFTPListService(session), cryptomator).list(vault, new DisabledListProgressListener() { @Override public void cleanup(final Path directory, final AttributedList list, final Optional e) { diff --git a/ssh/src/test/java/ch/cyberduck/core/cryptomator/SFTPMoveFeatureTest.java b/ssh/src/test/java/ch/cyberduck/core/cryptomator/SFTPMoveFeatureTest.java index 4fdc99181b..067997a6a9 100644 --- a/ssh/src/test/java/ch/cyberduck/core/cryptomator/SFTPMoveFeatureTest.java +++ b/ssh/src/test/java/ch/cyberduck/core/cryptomator/SFTPMoveFeatureTest.java @@ -21,11 +21,13 @@ import ch.cyberduck.core.DisabledLoginCallback; import ch.cyberduck.core.DisabledPasswordCallback; import ch.cyberduck.core.Path; import ch.cyberduck.core.cryptomator.features.CryptoTouchFeature; +import ch.cyberduck.core.cryptomator.features.CryptoWriteFeature; 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.Move; +import ch.cyberduck.core.features.Write; import ch.cyberduck.core.sftp.AbstractSFTPTest; import ch.cyberduck.core.sftp.SFTPAttributesFinderFeature; import ch.cyberduck.core.sftp.SFTPDeleteFeature; @@ -67,8 +69,8 @@ public class SFTPMoveFeatureTest extends AbstractSFTPTest { final CryptoVault cryptomator = new CryptoVault(vault); cryptomator.create(session, new VaultCredentials("test"), vaultVersion); session.withRegistry(new DefaultVaultRegistry(new DisabledPasswordCallback(), cryptomator)); - new CryptoTouchFeature<>(session, new CryptoTouchFeature<>(session, new DefaultTouchFeature<>(new SFTPWriteFeature(session) - ), new SFTPWriteFeature(session), cryptomator), new SFTPWriteFeature(session), cryptomator).touch(source, new TransferStatus()); + new CryptoTouchFeature<>(session, new CryptoTouchFeature<>(session, new DefaultTouchFeature( + session), cryptomator), cryptomator).touch(new CryptoWriteFeature<>(session, new SFTPWriteFeature(session), cryptomator), source, new TransferStatus()); assertTrue(cryptomator.getFeature(session, Find.class, new DefaultFindFeature(session)).find(source)); cryptomator.getFeature(session, Move.class, new SFTPMoveFeature(session)).move(source, target, new TransferStatus(), new Delete.DisabledCallback(), new DisabledConnectionCallback()); assertFalse(cryptomator.getFeature(session, Find.class, new DefaultFindFeature(session)).find(source)); @@ -87,10 +89,11 @@ public class SFTPMoveFeatureTest extends AbstractSFTPTest { final CryptoVault cryptomator = new CryptoVault(vault); cryptomator.create(session, new VaultCredentials("test"), vaultVersion); session.withRegistry(new DefaultVaultRegistry(new DisabledPasswordCallback(), cryptomator)); - new CryptoTouchFeature<>(session, new CryptoTouchFeature<>(session, new DefaultTouchFeature<>(new SFTPWriteFeature(session) - ), new SFTPWriteFeature(session), cryptomator), new SFTPWriteFeature(session), cryptomator).touch(source, new TransferStatus()); + new CryptoTouchFeature<>(session, new CryptoTouchFeature<>(session, new DefaultTouchFeature( + session), cryptomator), cryptomator).touch(new CryptoWriteFeature<>(session, new SFTPWriteFeature(session), cryptomator), source, new TransferStatus()); assertTrue(cryptomator.getFeature(session, Find.class, new DefaultFindFeature(session)).find(source)); - cryptomator.getFeature(session, Directory.class, new SFTPDirectoryFeature(session)).mkdir(targetFolder, new TransferStatus()); + cryptomator.getFeature(session, Directory.class, new SFTPDirectoryFeature(session)).mkdir( + new CryptoWriteFeature<>(session, new SFTPWriteFeature(session), cryptomator), targetFolder, new TransferStatus()); assertTrue(cryptomator.getFeature(session, Find.class, new DefaultFindFeature(session)).find(targetFolder)); cryptomator.getFeature(session, Move.class, new SFTPMoveFeature(session)).move(source, target, new TransferStatus(), new Delete.DisabledCallback(), new DisabledConnectionCallback()); assertFalse(cryptomator.getFeature(session, Find.class, new DefaultFindFeature(session)).find(source)); @@ -110,10 +113,11 @@ public class SFTPMoveFeatureTest extends AbstractSFTPTest { final CryptoVault cryptomator = new CryptoVault(vault); cryptomator.create(session, new VaultCredentials("test"), vaultVersion); session.withRegistry(new DefaultVaultRegistry(new DisabledPasswordCallback(), cryptomator)); - new CryptoTouchFeature<>(session, new CryptoTouchFeature<>(session, new DefaultTouchFeature<>(new SFTPWriteFeature(session) - ), new SFTPWriteFeature(session), cryptomator), new SFTPWriteFeature(session), cryptomator).touch(source, new TransferStatus()); + new CryptoTouchFeature<>(session, new CryptoTouchFeature<>(session, new DefaultTouchFeature( + session), cryptomator), cryptomator).touch(new CryptoWriteFeature<>(session, new SFTPWriteFeature(session), cryptomator), source, new TransferStatus()); assertTrue(cryptomator.getFeature(session, Find.class, new DefaultFindFeature(session)).find(source)); - cryptomator.getFeature(session, Directory.class, new SFTPDirectoryFeature(session)).mkdir(targetFolder, new TransferStatus()); + cryptomator.getFeature(session, Directory.class, new SFTPDirectoryFeature(session)).mkdir( + cryptomator.getFeature(session, Write.class, new SFTPWriteFeature(session)), targetFolder, new TransferStatus()); assertTrue(cryptomator.getFeature(session, Find.class, new DefaultFindFeature(session)).find(targetFolder)); cryptomator.getFeature(session, Move.class, new SFTPMoveFeature(session)).move(source, target, new TransferStatus(), new Delete.DisabledCallback(), new DisabledConnectionCallback()); assertFalse(cryptomator.getFeature(session, Find.class, new DefaultFindFeature(session)).find(source)); @@ -129,12 +133,13 @@ public class SFTPMoveFeatureTest extends AbstractSFTPTest { final CryptoVault cryptomator = new CryptoVault(vault); cryptomator.create(session, new VaultCredentials("test"), vaultVersion); session.withRegistry(new DefaultVaultRegistry(new DisabledPasswordCallback(), cryptomator)); - cryptomator.getFeature(session, Directory.class, new SFTPDirectoryFeature(session)).mkdir(folder, new TransferStatus()); + cryptomator.getFeature(session, Directory.class, new SFTPDirectoryFeature(session)).mkdir( + cryptomator.getFeature(session, Write.class, new SFTPWriteFeature(session)), folder, new TransferStatus()); assertTrue(cryptomator.getFeature(session, Find.class, new DefaultFindFeature(session)).find(folder)); final Path template = new Path(folder, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)); - final Path file = new CryptoTouchFeature<>(session, new CryptoTouchFeature<>(session, new DefaultTouchFeature<>(new SFTPWriteFeature(session) - ), new SFTPWriteFeature(session), cryptomator), new SFTPWriteFeature(session), cryptomator).touch( - template, new TransferStatus()).withAttributes(cryptomator.getFeature(session, AttributesFinder.class, new SFTPAttributesFinderFeature(session)).find(template)); + final Path file = new CryptoTouchFeature<>(session, new CryptoTouchFeature<>(session, new DefaultTouchFeature( + session), cryptomator), cryptomator).touch( + new CryptoWriteFeature<>(session, new SFTPWriteFeature(session), cryptomator), template, new TransferStatus()).withAttributes(cryptomator.getFeature(session, AttributesFinder.class, new SFTPAttributesFinderFeature(session)).find(template)); assertTrue(cryptomator.getFeature(session, Find.class, new DefaultFindFeature(session)).find(file)); final Move move = cryptomator.getFeature(session, Move.class, new SFTPMoveFeature(session)); // rename file @@ -154,7 +159,8 @@ public class SFTPMoveFeatureTest extends AbstractSFTPTest { final CryptoVault cryptomator = new CryptoVault(vault); cryptomator.create(session, new VaultCredentials("test"), vaultVersion); session.withRegistry(new DefaultVaultRegistry(new DisabledPasswordCallback(), cryptomator)); - cryptomator.getFeature(session, Directory.class, new SFTPDirectoryFeature(session)).mkdir(folder, new TransferStatus()); + cryptomator.getFeature(session, Directory.class, new SFTPDirectoryFeature(session)).mkdir( + cryptomator.getFeature(session, Write.class, new SFTPWriteFeature(session)), folder, new TransferStatus()); assertTrue(cryptomator.getFeature(session, Find.class, new DefaultFindFeature(session)).find(folder)); final Move move = cryptomator.getFeature(session, Move.class, new SFTPMoveFeature(session)); // rename folder @@ -174,10 +180,11 @@ public class SFTPMoveFeatureTest extends AbstractSFTPTest { final CryptoVault cryptomator = new CryptoVault(vault); cryptomator.create(session, new VaultCredentials("test"), vaultVersion); session.withRegistry(new DefaultVaultRegistry(new DisabledPasswordCallback(), cryptomator)); - cryptomator.getFeature(session, Directory.class, new SFTPDirectoryFeature(session)).mkdir(folder, new TransferStatus()); + cryptomator.getFeature(session, Directory.class, new SFTPDirectoryFeature(session)).mkdir( + cryptomator.getFeature(session, Write.class, new SFTPWriteFeature(session)), folder, new TransferStatus()); assertTrue(cryptomator.getFeature(session, Find.class, new DefaultFindFeature(session)).find(folder)); - new CryptoTouchFeature<>(session, new CryptoTouchFeature<>(session, new DefaultTouchFeature<>(new SFTPWriteFeature(session) - ), new SFTPWriteFeature(session), cryptomator), new SFTPWriteFeature(session), cryptomator).touch(file, new TransferStatus()); + new CryptoTouchFeature<>(session, new CryptoTouchFeature<>(session, new DefaultTouchFeature( + session), cryptomator), cryptomator).touch(new CryptoWriteFeature<>(session, new SFTPWriteFeature(session), cryptomator), file, new TransferStatus()); assertTrue(cryptomator.getFeature(session, Find.class, new DefaultFindFeature(session)).find(file)); final Move move = cryptomator.getFeature(session, Move.class, new SFTPMoveFeature(session)); // rename file diff --git a/ssh/src/test/java/ch/cyberduck/core/cryptomator/SFTPSymlinkFeatureTest.java b/ssh/src/test/java/ch/cyberduck/core/cryptomator/SFTPSymlinkFeatureTest.java index 825f16d692..7a74f516b4 100644 --- a/ssh/src/test/java/ch/cyberduck/core/cryptomator/SFTPSymlinkFeatureTest.java +++ b/ssh/src/test/java/ch/cyberduck/core/cryptomator/SFTPSymlinkFeatureTest.java @@ -23,6 +23,7 @@ import ch.cyberduck.core.Path; import ch.cyberduck.core.cryptomator.features.CryptoListService; import ch.cyberduck.core.cryptomator.features.CryptoSymlinkFeature; 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.sftp.AbstractSFTPTest; @@ -63,8 +64,9 @@ public class SFTPSymlinkFeatureTest extends AbstractSFTPTest { cryptomator.create(session, new VaultCredentials("test"), vaultVersion); session.withRegistry(new DefaultVaultRegistry(new DisabledPasswordCallback(), cryptomator)); final Path target = new Path(vault, UUID.randomUUID().toString(), EnumSet.of(Path.Type.file)); - new CryptoTouchFeature<>(session, new CryptoTouchFeature<>(session, new DefaultTouchFeature<>(new SFTPWriteFeature(session) - ), new SFTPWriteFeature(session), cryptomator), new SFTPWriteFeature(session), cryptomator).touch(target, new TransferStatus()); + new CryptoTouchFeature<>(session, new CryptoTouchFeature<>(session, new DefaultTouchFeature( + session), cryptomator), cryptomator).touch( + new CryptoWriteFeature<>(session, new SFTPWriteFeature(session), cryptomator), target, new TransferStatus()); final Path link = new Path(vault, UUID.randomUUID().toString(), EnumSet.of(Path.Type.file, AbstractPath.Type.symboliclink)); new CryptoSymlinkFeature(session, new SFTPSymlinkFeature(session), cryptomator).symlink(link, target.getName()); assertTrue(cryptomator.getFeature(session, Find.class, new SFTPFindFeature(session)).find(link)); diff --git a/ssh/src/test/java/ch/cyberduck/core/sftp/SFTPAttributesFinderFeatureTest.java b/ssh/src/test/java/ch/cyberduck/core/sftp/SFTPAttributesFinderFeatureTest.java index c6c3bfbf4c..8db798dc62 100644 --- a/ssh/src/test/java/ch/cyberduck/core/sftp/SFTPAttributesFinderFeatureTest.java +++ b/ssh/src/test/java/ch/cyberduck/core/sftp/SFTPAttributesFinderFeatureTest.java @@ -27,7 +27,7 @@ public class SFTPAttributesFinderFeatureTest extends AbstractSFTPTest { @Test public void testFindDirectory() throws Exception { final Path test = new Path(new SFTPHomeDirectoryService(session).find(), UUID.randomUUID().toString(), EnumSet.of(Path.Type.directory)); - new SFTPDirectoryFeature(session).mkdir(test, new TransferStatus()); + new SFTPDirectoryFeature(session).mkdir(new SFTPWriteFeature(session), test, new TransferStatus()); final SFTPAttributesFinderFeature f = new SFTPAttributesFinderFeature(session); final PathAttributes attributes = f.find(test); assertNotNull(attributes); @@ -43,7 +43,7 @@ public class SFTPAttributesFinderFeatureTest extends AbstractSFTPTest { @Test public void testFindSymbolicLink() throws Exception { - final Path file = new SFTPTouchFeature(session).touch(new Path(new SFTPHomeDirectoryService(session).find(), new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)), new TransferStatus()); + final Path file = new SFTPTouchFeature(session).touch(new SFTPWriteFeature(session), new Path(new SFTPHomeDirectoryService(session).find(), new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)), new TransferStatus()); final Path symlink = new Path(new SFTPHomeDirectoryService(session).find(), new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)); new SFTPSymlinkFeature(session).symlink(symlink, file.getAbsolute()); final SFTPAttributesFinderFeature f = new SFTPAttributesFinderFeature(session); diff --git a/ssh/src/test/java/ch/cyberduck/core/sftp/SFTPCompressFeatureTest.java b/ssh/src/test/java/ch/cyberduck/core/sftp/SFTPCompressFeatureTest.java index b64dc5a4d1..c209786008 100644 --- a/ssh/src/test/java/ch/cyberduck/core/sftp/SFTPCompressFeatureTest.java +++ b/ssh/src/test/java/ch/cyberduck/core/sftp/SFTPCompressFeatureTest.java @@ -31,7 +31,7 @@ public class SFTPCompressFeatureTest extends AbstractSFTPTest { for(Archive archive : Archive.getKnownArchives()) { final Path workdir = new SFTPHomeDirectoryService(session).find(); final Path test = new Path(workdir, UUID.randomUUID().toString(), EnumSet.of(Path.Type.file)); - session.getFeature(Touch.class).touch(test, new TransferStatus()); + session.getFeature(Touch.class).touch(new SFTPWriteFeature(session), test, new TransferStatus()); feature.archive(archive, workdir, Collections.singletonList(test), new ProgressListener() { @Override public void message(final String message) { diff --git a/ssh/src/test/java/ch/cyberduck/core/sftp/SFTPDeleteFeatureTest.java b/ssh/src/test/java/ch/cyberduck/core/sftp/SFTPDeleteFeatureTest.java index e85135a37f..84e700042a 100644 --- a/ssh/src/test/java/ch/cyberduck/core/sftp/SFTPDeleteFeatureTest.java +++ b/ssh/src/test/java/ch/cyberduck/core/sftp/SFTPDeleteFeatureTest.java @@ -53,11 +53,11 @@ public class SFTPDeleteFeatureTest extends AbstractSFTPTest { @Test public void testDeleteFolder() throws Exception { final Path folder = new Path(new SFTPHomeDirectoryService(session).find(), UUID.randomUUID().toString(), EnumSet.of(Path.Type.directory)); - new SFTPDirectoryFeature(session).mkdir(folder, new TransferStatus()); + new SFTPDirectoryFeature(session).mkdir(new SFTPWriteFeature(session), folder, new TransferStatus()); final Path file = new Path(folder, UUID.randomUUID().toString(), EnumSet.of(Path.Type.file)); - new SFTPTouchFeature(session).touch(file, new TransferStatus()); + new SFTPTouchFeature(session).touch(new SFTPWriteFeature(session), file, new TransferStatus()); final Path subdir = new Path(folder, UUID.randomUUID().toString(), EnumSet.of(Path.Type.directory)); - new SFTPDirectoryFeature(session).mkdir(subdir, new TransferStatus()); + new SFTPDirectoryFeature(session).mkdir(new SFTPWriteFeature(session), subdir, new TransferStatus()); new SFTPDeleteFeature(session).delete(Arrays.asList(subdir, file, folder), new DisabledLoginCallback(), new Delete.DisabledCallback()); } } diff --git a/ssh/src/test/java/ch/cyberduck/core/sftp/SFTPDirectoryFeatureTest.java b/ssh/src/test/java/ch/cyberduck/core/sftp/SFTPDirectoryFeatureTest.java index 1d9f9af6db..13e97c60c0 100644 --- a/ssh/src/test/java/ch/cyberduck/core/sftp/SFTPDirectoryFeatureTest.java +++ b/ssh/src/test/java/ch/cyberduck/core/sftp/SFTPDirectoryFeatureTest.java @@ -40,9 +40,9 @@ public class SFTPDirectoryFeatureTest extends AbstractSFTPTest { @Test public void testMakeDirectory() throws Exception { final Path test = new Path(new SFTPHomeDirectoryService(session).find(), UUID.randomUUID().toString(), EnumSet.of(Path.Type.directory)); - new SFTPDirectoryFeature(session).mkdir(test, new TransferStatus()); + new SFTPDirectoryFeature(session).mkdir(new SFTPWriteFeature(session), test, new TransferStatus()); assertTrue(new SFTPFindFeature(session).find(test)); - assertThrows(ConflictException.class, () -> new SFTPDirectoryFeature(session).mkdir(test, new TransferStatus())); + assertThrows(ConflictException.class, () -> new SFTPDirectoryFeature(session).mkdir(new SFTPWriteFeature(session), test, new TransferStatus())); new SFTPDeleteFeature(session).delete(Collections.singletonList(test), new DisabledLoginCallback(), new Delete.DisabledCallback()); } } diff --git a/ssh/src/test/java/ch/cyberduck/core/sftp/SFTPFindFeatureTest.java b/ssh/src/test/java/ch/cyberduck/core/sftp/SFTPFindFeatureTest.java index 8fc6bee01f..70fd7eb6ac 100644 --- a/ssh/src/test/java/ch/cyberduck/core/sftp/SFTPFindFeatureTest.java +++ b/ssh/src/test/java/ch/cyberduck/core/sftp/SFTPFindFeatureTest.java @@ -33,7 +33,7 @@ public class SFTPFindFeatureTest extends AbstractSFTPTest { @Test public void testFindFile() throws Exception { final Path file = new Path(new SFTPHomeDirectoryService(session).find(), new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)); - new SFTPTouchFeature(session).touch(file, new TransferStatus()); + new SFTPTouchFeature(session).touch(new SFTPWriteFeature(session), file, new TransferStatus()); assertTrue(new SFTPFindFeature(session).find(file)); assertFalse(new SFTPFindFeature(session).find(new Path(file.getAbsolute(), EnumSet.of(Path.Type.directory)))); new SFTPDeleteFeature(session).delete(Collections.singletonList(file), new DisabledLoginCallback(), new Delete.DisabledCallback()); diff --git a/ssh/src/test/java/ch/cyberduck/core/sftp/SFTPListServiceTest.java b/ssh/src/test/java/ch/cyberduck/core/sftp/SFTPListServiceTest.java index b6b886e264..e395961afc 100644 --- a/ssh/src/test/java/ch/cyberduck/core/sftp/SFTPListServiceTest.java +++ b/ssh/src/test/java/ch/cyberduck/core/sftp/SFTPListServiceTest.java @@ -52,10 +52,10 @@ public class SFTPListServiceTest extends AbstractSFTPTest { final Path symlinkRelative = new Path(home, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file, AbstractPath.Type.symboliclink)); final Path symlinkAbsolute = new Path(home, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file, AbstractPath.Type.symboliclink)); final Path directory = new Path(home, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory)); - new SFTPTouchFeature(session).touch(file, new TransferStatus()); + new SFTPTouchFeature(session).touch(new SFTPWriteFeature(session), file, new TransferStatus()); new SFTPSymlinkFeature(session).symlink(symlinkRelative, file.getName()); new SFTPSymlinkFeature(session).symlink(symlinkAbsolute, file.getAbsolute()); - new SFTPDirectoryFeature(session).mkdir(directory, new TransferStatus()); + new SFTPDirectoryFeature(session).mkdir(new SFTPWriteFeature(session), directory, new TransferStatus()); final Permission permission = new Permission(Permission.Action.read_write, Permission.Action.read_write, Permission.Action.read_write); new SFTPUnixPermissionFeature(session).setUnixPermission(file, permission); final AttributedList list = new SFTPListService(session).list(home, new DisabledListProgressListener()); diff --git a/ssh/src/test/java/ch/cyberduck/core/sftp/SFTPMoveFeatureTest.java b/ssh/src/test/java/ch/cyberduck/core/sftp/SFTPMoveFeatureTest.java index 61a00b67e7..331c8e27c1 100644 --- a/ssh/src/test/java/ch/cyberduck/core/sftp/SFTPMoveFeatureTest.java +++ b/ssh/src/test/java/ch/cyberduck/core/sftp/SFTPMoveFeatureTest.java @@ -41,7 +41,7 @@ public class SFTPMoveFeatureTest extends AbstractSFTPTest { @Test public void testMove() throws Exception { final Path workdir = new SFTPHomeDirectoryService(session).find(); - final Path test = new SFTPTouchFeature(session).touch(new Path(workdir, UUID.randomUUID().toString(), EnumSet.of(Path.Type.file)), new TransferStatus()); + final Path test = new SFTPTouchFeature(session).touch(new SFTPWriteFeature(session), new Path(workdir, UUID.randomUUID().toString(), EnumSet.of(Path.Type.file)), new TransferStatus()); assertEquals(TransferStatus.UNKNOWN_LENGTH, test.attributes().getSize()); final Path target = new SFTPMoveFeature(session).move(test, new Path(workdir, UUID.randomUUID().toString(), EnumSet.of(Path.Type.file)), new TransferStatus(), new Delete.DisabledCallback(), new DisabledConnectionCallback()); @@ -55,9 +55,9 @@ public class SFTPMoveFeatureTest extends AbstractSFTPTest { public void testMoveOverride() throws Exception { final Path workdir = new SFTPHomeDirectoryService(session).find(); final Path test = new Path(workdir, UUID.randomUUID().toString(), EnumSet.of(Path.Type.file)); - new SFTPTouchFeature(session).touch(test, new TransferStatus()); + new SFTPTouchFeature(session).touch(new SFTPWriteFeature(session), test, new TransferStatus()); final Path target = new Path(workdir, UUID.randomUUID().toString(), EnumSet.of(Path.Type.file)); - new SFTPTouchFeature(session).touch(target, new TransferStatus()); + new SFTPTouchFeature(session).touch(new SFTPWriteFeature(session), target, new TransferStatus()); assertThrows(ConflictException.class, () -> new SFTPMoveFeature(session).move(test, target, new TransferStatus().setExists(false), new Delete.DisabledCallback(), new DisabledConnectionCallback())); new SFTPMoveFeature(session).move(test, target, new TransferStatus().setExists(true), new Delete.DisabledCallback(), new DisabledConnectionCallback()); assertFalse(new SFTPFindFeature(session).find(test)); diff --git a/ssh/src/test/java/ch/cyberduck/core/sftp/SFTPReadFeatureTest.java b/ssh/src/test/java/ch/cyberduck/core/sftp/SFTPReadFeatureTest.java index 03c9d926c3..2c761b5f80 100644 --- a/ssh/src/test/java/ch/cyberduck/core/sftp/SFTPReadFeatureTest.java +++ b/ssh/src/test/java/ch/cyberduck/core/sftp/SFTPReadFeatureTest.java @@ -54,7 +54,7 @@ public class SFTPReadFeatureTest extends AbstractSFTPTest { public void testRead() throws Exception { final Path home = new SFTPHomeDirectoryService(session).find(); final Path test = new Path(home, UUID.randomUUID().toString(), EnumSet.of(Path.Type.file)); - new SFTPTouchFeature(session).touch(test, new TransferStatus()); + new SFTPTouchFeature(session).touch(new SFTPWriteFeature(session), test, new TransferStatus()); final int length = 39865; final byte[] content = RandomUtils.nextBytes(length); { @@ -81,7 +81,7 @@ public class SFTPReadFeatureTest extends AbstractSFTPTest { public void testReadRange() throws Exception { final Path home = new SFTPHomeDirectoryService(session).find(); final Path test = new Path(home, UUID.randomUUID().toString(), EnumSet.of(Path.Type.file)); - new SFTPTouchFeature(session).touch(test, new TransferStatus()); + new SFTPTouchFeature(session).touch(new SFTPWriteFeature(session), test, new TransferStatus()); final int length = 1048576; final byte[] content = RandomUtils.nextBytes(length); { diff --git a/ssh/src/test/java/ch/cyberduck/core/sftp/SFTPSymlinkFeatureTest.java b/ssh/src/test/java/ch/cyberduck/core/sftp/SFTPSymlinkFeatureTest.java index 418d59d2e8..4a99775fa6 100644 --- a/ssh/src/test/java/ch/cyberduck/core/sftp/SFTPSymlinkFeatureTest.java +++ b/ssh/src/test/java/ch/cyberduck/core/sftp/SFTPSymlinkFeatureTest.java @@ -14,7 +14,6 @@ import org.junit.experimental.categories.Category; import java.util.Collections; import java.util.EnumSet; -import java.util.UUID; import static org.junit.Assert.*; @@ -25,7 +24,7 @@ public class SFTPSymlinkFeatureTest extends AbstractSFTPTest { public void testSymlink() throws Exception { final SFTPHomeDirectoryService workdir = new SFTPHomeDirectoryService(session); final Path target = new Path(workdir.find(), new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)); - new SFTPTouchFeature(session).touch(target, new TransferStatus()); + new SFTPTouchFeature(session).touch(new SFTPWriteFeature(session), target, new TransferStatus()); final Path link = new Path(workdir.find(), new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file, AbstractPath.Type.symboliclink)); new SFTPSymlinkFeature(session).symlink(link, target.getName()); assertTrue(new SFTPFindFeature(session).find(link)); diff --git a/ssh/src/test/java/ch/cyberduck/core/sftp/SFTPTimestampFeatureTest.java b/ssh/src/test/java/ch/cyberduck/core/sftp/SFTPTimestampFeatureTest.java index f5cd456ec1..e39ea96e4f 100644 --- a/ssh/src/test/java/ch/cyberduck/core/sftp/SFTPTimestampFeatureTest.java +++ b/ssh/src/test/java/ch/cyberduck/core/sftp/SFTPTimestampFeatureTest.java @@ -42,7 +42,7 @@ public class SFTPTimestampFeatureTest extends AbstractSFTPTest { public void testSetTimestamp() throws Exception { final Path home = new SFTPHomeDirectoryService(session).find(); final Path test = new Path(home, UUID.randomUUID().toString(), EnumSet.of(Path.Type.file)); - new SFTPTouchFeature(session).touch(test, new TransferStatus()); + new SFTPTouchFeature(session).touch(new SFTPWriteFeature(session), test, new TransferStatus()); final long modified = System.currentTimeMillis(); new SFTPTimestampFeature(session).setTimestamp(test, modified); assertEquals(TimeUnit.SECONDS.toMillis(TimeUnit.MILLISECONDS.toSeconds(modified)), new SFTPAttributesFinderFeature(session).find(test).getModificationDate()); @@ -55,7 +55,7 @@ public class SFTPTimestampFeatureTest extends AbstractSFTPTest { public void testSetTimestampDirectory() throws Exception { final Path home = new SFTPHomeDirectoryService(session).find(); final Path test = new Path(home, UUID.randomUUID().toString(), EnumSet.of(Path.Type.directory)); - new SFTPDirectoryFeature(session).mkdir(test, new TransferStatus()); + new SFTPDirectoryFeature(session).mkdir(new SFTPWriteFeature(session), test, new TransferStatus()); final long modified = System.currentTimeMillis(); new SFTPTimestampFeature(session).setTimestamp(test, modified); assertEquals(TimeUnit.SECONDS.toMillis(TimeUnit.MILLISECONDS.toSeconds(modified)), new SFTPListService(session).list(home, new DisabledListProgressListener()).get(test).attributes().getModificationDate(), 0); diff --git a/ssh/src/test/java/ch/cyberduck/core/sftp/SFTPTouchFeatureTest.java b/ssh/src/test/java/ch/cyberduck/core/sftp/SFTPTouchFeatureTest.java index 7f604c4b75..bf4ede50b0 100644 --- a/ssh/src/test/java/ch/cyberduck/core/sftp/SFTPTouchFeatureTest.java +++ b/ssh/src/test/java/ch/cyberduck/core/sftp/SFTPTouchFeatureTest.java @@ -43,10 +43,10 @@ public class SFTPTouchFeatureTest extends AbstractSFTPTest { public void testTouch() throws Exception { final Path home = new SFTPHomeDirectoryService(session).find(); final Path test = new Path(home, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)); - new SFTPTouchFeature(session).touch(test, new TransferStatus()); + new SFTPTouchFeature(session).touch(new SFTPWriteFeature(session), test, new TransferStatus()); new SFTPUnixPermissionFeature(session).setUnixPermission(test, new Permission("664")); // Test override - new SFTPTouchFeature(session).touch(test, new TransferStatus()); + new SFTPTouchFeature(session).touch(new SFTPWriteFeature(session), test, new TransferStatus()); final AttributedList list = new SFTPListService(session).list(home, new DisabledListProgressListener()); assertTrue(list.contains(test)); assertEquals("664", list.get(test).attributes().getPermission().getMode()); diff --git a/ssh/src/test/java/ch/cyberduck/core/sftp/SFTPUnixPermissionFeatureTest.java b/ssh/src/test/java/ch/cyberduck/core/sftp/SFTPUnixPermissionFeatureTest.java index 50d115db4b..7dc8064423 100644 --- a/ssh/src/test/java/ch/cyberduck/core/sftp/SFTPUnixPermissionFeatureTest.java +++ b/ssh/src/test/java/ch/cyberduck/core/sftp/SFTPUnixPermissionFeatureTest.java @@ -63,14 +63,14 @@ public class SFTPUnixPermissionFeatureTest extends AbstractSFTPTest { final Path home = new SFTPHomeDirectoryService(session).find(); { final Path file = new Path(home, UUID.randomUUID().toString(), EnumSet.of(Path.Type.file)); - new SFTPTouchFeature(session).touch(file, new TransferStatus()); + new SFTPTouchFeature(session).touch(new SFTPWriteFeature(session), file, new TransferStatus()); new SFTPUnixPermissionFeature(session).setUnixPermission(file, new Permission(666)); assertEquals("666", new SFTPListService(session).list(home, new DisabledListProgressListener()).get(file).attributes().getPermission().getMode()); new SFTPDeleteFeature(session).delete(Collections.singletonList(file), new DisabledLoginCallback(), new Delete.DisabledCallback()); } { final Path directory = new Path(home, UUID.randomUUID().toString(), EnumSet.of(Path.Type.directory)); - new SFTPDirectoryFeature(session).mkdir(directory, new TransferStatus()); + new SFTPDirectoryFeature(session).mkdir(new SFTPWriteFeature(session), directory, new TransferStatus()); new SFTPUnixPermissionFeature(session).setUnixPermission(directory, new Permission(666)); assertEquals("666", new SFTPListService(session).list(home, new DisabledListProgressListener()).get(directory).attributes().getPermission().getMode()); new SFTPDeleteFeature(session).delete(Collections.singletonList(directory), new DisabledLoginCallback(), new Delete.DisabledCallback()); @@ -81,7 +81,7 @@ public class SFTPUnixPermissionFeatureTest extends AbstractSFTPTest { @Ignore public void testRetainStickyBits() throws Exception { final Path test = new Path(new SFTPHomeDirectoryService(session).find(), UUID.randomUUID().toString(), EnumSet.of(Path.Type.file)); - new SFTPTouchFeature(session).touch(test, new TransferStatus()); + new SFTPTouchFeature(session).touch(new SFTPWriteFeature(session), test, new TransferStatus()); final SFTPUnixPermissionFeature feature = new SFTPUnixPermissionFeature(session); feature.setUnixPermission(test, new Permission(Permission.Action.all, Permission.Action.read, Permission.Action.read, diff --git a/ssh/src/test/java/ch/cyberduck/core/sftp/SFTPUploadFeatureTest.java b/ssh/src/test/java/ch/cyberduck/core/sftp/SFTPUploadFeatureTest.java index cc984ee6d2..13603d2b9d 100644 --- a/ssh/src/test/java/ch/cyberduck/core/sftp/SFTPUploadFeatureTest.java +++ b/ssh/src/test/java/ch/cyberduck/core/sftp/SFTPUploadFeatureTest.java @@ -34,7 +34,7 @@ public class SFTPUploadFeatureTest extends AbstractSFTPTest { public void testAppend() throws Exception { final Path workdir = new SFTPHomeDirectoryService(session).find(); final Path test = new Path(workdir, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)); - new SFTPTouchFeature(session).touch(test, new TransferStatus()); + new SFTPTouchFeature(session).touch(new SFTPWriteFeature(session), test, new TransferStatus()); assertTrue(new SFTPUploadFeature(session).append(test, new TransferStatus().setExists(true).setLength(1L).setRemote(new SFTPAttributesFinderFeature(session).find(test))).append); new SFTPDeleteFeature(session).delete(Collections.singletonList(test), new DisabledLoginCallback(), new Delete.DisabledCallback()); } diff --git a/ssh/src/test/java/ch/cyberduck/core/sftp/SFTPWriteFeatureTest.java b/ssh/src/test/java/ch/cyberduck/core/sftp/SFTPWriteFeatureTest.java index e935e7c070..00f9e52360 100644 --- a/ssh/src/test/java/ch/cyberduck/core/sftp/SFTPWriteFeatureTest.java +++ b/ssh/src/test/java/ch/cyberduck/core/sftp/SFTPWriteFeatureTest.java @@ -70,7 +70,7 @@ public class SFTPWriteFeatureTest extends AbstractSFTPTest { @Test public void testWrite() throws Exception { - final Path folder = new SFTPDirectoryFeature(session).mkdir(new Path(new SFTPHomeDirectoryService(session).find(), new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory)), new TransferStatus()); + final Path folder = new SFTPDirectoryFeature(session).mkdir(new SFTPWriteFeature(session), new Path(new SFTPHomeDirectoryService(session).find(), new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory)), new TransferStatus()); final long folderModification = new SFTPAttributesFinderFeature(session).find(folder).getModificationDate(); final Path test = new Path(folder, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)); // Only seconds in modification date @@ -108,7 +108,7 @@ public class SFTPWriteFeatureTest extends AbstractSFTPTest { public void testWriteSymlink() throws Exception { final Path workdir = new SFTPHomeDirectoryService(session).find(); final Path target = new Path(workdir, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)); - new SFTPTouchFeature(session).touch(target, new TransferStatus()); + new SFTPTouchFeature(session).touch(new SFTPWriteFeature(session), target, new TransferStatus()); assertTrue(new SFTPFindFeature(session).find(target)); final String name = new AlphanumericRandomStringService().random(); final Path symlink = new Path(workdir, name, EnumSet.of(Path.Type.file, AbstractPath.Type.symboliclink)); diff --git a/ssh/src/test/java/ch/cyberduck/core/sftp/auth/SFTPAgentAuthenticationTest.java b/ssh/src/test/java/ch/cyberduck/core/sftp/auth/SFTPAgentAuthenticationTest.java index 8b59481d26..12b6bd538d 100644 --- a/ssh/src/test/java/ch/cyberduck/core/sftp/auth/SFTPAgentAuthenticationTest.java +++ b/ssh/src/test/java/ch/cyberduck/core/sftp/auth/SFTPAgentAuthenticationTest.java @@ -42,7 +42,7 @@ public class SFTPAgentAuthenticationTest { @Test public void filterIdentitiesMatch() { final SFTPAgentAuthentication authentication = new SFTPAgentAuthentication(new SSHClient(), new OpenSSHAgentAuthenticator(new AgentProxy(null))); - final Credentials credentials = new Credentials("user").withIdentity(new Local("mykey") { + final Credentials credentials = new Credentials("user").setIdentity(new Local("mykey") { @Override public boolean exists() { return true; @@ -66,7 +66,7 @@ public class SFTPAgentAuthenticationTest { @Test public void filterIdentitiesNoMatch() { final SFTPAgentAuthentication authentication = new SFTPAgentAuthentication(new SSHClient(), new OpenSSHAgentAuthenticator(new AgentProxy(null))); - final Credentials credentials = new Credentials("user").withIdentity(new Local("mykey") { + final Credentials credentials = new Credentials("user").setIdentity(new Local("mykey") { @Override public boolean exists() { return true; diff --git a/ssh/src/test/java/ch/cyberduck/core/sftp/auth/SFTPPasswordAuthenticationTest.java b/ssh/src/test/java/ch/cyberduck/core/sftp/auth/SFTPPasswordAuthenticationTest.java index af088826fb..ff454a90a8 100644 --- a/ssh/src/test/java/ch/cyberduck/core/sftp/auth/SFTPPasswordAuthenticationTest.java +++ b/ssh/src/test/java/ch/cyberduck/core/sftp/auth/SFTPPasswordAuthenticationTest.java @@ -20,7 +20,6 @@ import ch.cyberduck.core.DisabledHostKeyCallback; import ch.cyberduck.core.DisabledLoginCallback; import ch.cyberduck.core.exception.LoginFailureException; import ch.cyberduck.core.proxy.DisabledProxyFinder; -import ch.cyberduck.core.proxy.Proxy; import ch.cyberduck.core.sftp.AbstractSFTPTest; import ch.cyberduck.test.IntegrationTest; diff --git a/ssh/src/test/java/ch/cyberduck/core/sftp/auth/SFTPPublicKeyAuthenticationTest.java b/ssh/src/test/java/ch/cyberduck/core/sftp/auth/SFTPPublicKeyAuthenticationTest.java index 484635916f..afd3b2b86f 100644 --- a/ssh/src/test/java/ch/cyberduck/core/sftp/auth/SFTPPublicKeyAuthenticationTest.java +++ b/ssh/src/test/java/ch/cyberduck/core/sftp/auth/SFTPPublicKeyAuthenticationTest.java @@ -22,12 +22,10 @@ import ch.cyberduck.core.DisabledLoginCallback; import ch.cyberduck.core.Host; import ch.cyberduck.core.Local; import ch.cyberduck.core.LoginOptions; -import ch.cyberduck.core.exception.InteroperabilityException; import ch.cyberduck.core.exception.LoginCanceledException; import ch.cyberduck.core.exception.LoginFailureException; import ch.cyberduck.core.local.DefaultLocalTouchFeature; import ch.cyberduck.core.proxy.DisabledProxyFinder; -import ch.cyberduck.core.proxy.Proxy; import ch.cyberduck.core.sftp.AbstractSFTPTest; import ch.cyberduck.test.IntegrationTest; @@ -223,7 +221,7 @@ public class SFTPPublicKeyAuthenticationTest extends AbstractSFTPTest { } } - @Test(expected = LoginFailureException.class) + @Test public void testUnknownFormat() throws Exception { final Local key = new Local(System.getProperty("java.io.tmpdir"), UUID.randomUUID().toString()); try { @@ -233,7 +231,7 @@ public class SFTPPublicKeyAuthenticationTest extends AbstractSFTPTest { session.disconnect(); session.open(new DisabledProxyFinder(), new DisabledHostKeyCallback(), new DisabledLoginCallback(), new DisabledCancelCallback()); session.getHost().getCredentials().setIdentity(key); - assertTrue(new SFTPPublicKeyAuthentication(session.getClient()).authenticate(session.getHost(), new DisabledLoginCallback() { + assertFalse(new SFTPPublicKeyAuthentication(session.getClient()).authenticate(session.getHost(), new DisabledLoginCallback() { @Override public Credentials prompt(final Host bookmark, String username, String title, String reason, LoginOptions options) throws LoginCanceledException { fail(); diff --git a/ssh/src/test/java/ch/cyberduck/core/shared/CachingAttributesFinderFeatureTest.java b/ssh/src/test/java/ch/cyberduck/core/shared/CachingAttributesFinderFeatureTest.java index b3f843559c..0eaf36eeb8 100644 --- a/ssh/src/test/java/ch/cyberduck/core/shared/CachingAttributesFinderFeatureTest.java +++ b/ssh/src/test/java/ch/cyberduck/core/shared/CachingAttributesFinderFeatureTest.java @@ -3,6 +3,7 @@ package ch.cyberduck.core.shared; import ch.cyberduck.core.AlphanumericRandomStringService; import ch.cyberduck.core.Attributes; import ch.cyberduck.core.CachingAttributesFinderFeature; +import ch.cyberduck.core.DefaultPathAttributes; import ch.cyberduck.core.DisabledLoginCallback; import ch.cyberduck.core.ListProgressListener; import ch.cyberduck.core.Path; @@ -17,6 +18,7 @@ import ch.cyberduck.core.sftp.SFTPDeleteFeature; import ch.cyberduck.core.sftp.SFTPHomeDirectoryService; import ch.cyberduck.core.sftp.SFTPTouchFeature; import ch.cyberduck.core.sftp.SFTPUnixPermissionFeature; +import ch.cyberduck.core.sftp.SFTPWriteFeature; import ch.cyberduck.core.transfer.TransferStatus; import ch.cyberduck.test.IntegrationTest; @@ -52,7 +54,7 @@ public class CachingAttributesFinderFeatureTest extends AbstractSFTPTest { final PathCache cache = new PathCache(1); final AttributesFinder f = new CachingAttributesFinderFeature(session, cache, new DefaultAttributesFinderFeature(session)); final Path workdir = new SFTPHomeDirectoryService(session).find(); - final Path file = new SFTPTouchFeature(session).touch(new Path(workdir, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)), new TransferStatus()); + final Path file = new SFTPTouchFeature(session).touch(new SFTPWriteFeature(session), new Path(workdir, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)), new TransferStatus()); new SFTPUnixPermissionFeature(session).setUnixPermission(file, new Permission("-rw-rw-rw-")); final Attributes lookup = f.find(file); assertEquals(0L, lookup.getSize()); @@ -76,7 +78,7 @@ public class CachingAttributesFinderFeatureTest extends AbstractSFTPTest { // Expected } cache.invalidate(workdir); - final PathAttributes newAttr = new PathAttributes(); + final PathAttributes newAttr = new DefaultPathAttributes(); assertSame(newAttr, new CachingAttributesFinderFeature(session, cache, new AttributesFinder() { @Override public PathAttributes find(final Path file, final ListProgressListener listener) { diff --git a/ssh/src/test/java/ch/cyberduck/core/shared/DefaultAttributesFinderFeatureTest.java b/ssh/src/test/java/ch/cyberduck/core/shared/DefaultAttributesFinderFeatureTest.java index 8ea45757fb..93a31464d3 100644 --- a/ssh/src/test/java/ch/cyberduck/core/shared/DefaultAttributesFinderFeatureTest.java +++ b/ssh/src/test/java/ch/cyberduck/core/shared/DefaultAttributesFinderFeatureTest.java @@ -13,6 +13,7 @@ import ch.cyberduck.core.sftp.SFTPDeleteFeature; import ch.cyberduck.core.sftp.SFTPHomeDirectoryService; import ch.cyberduck.core.sftp.SFTPTouchFeature; import ch.cyberduck.core.sftp.SFTPUnixPermissionFeature; +import ch.cyberduck.core.sftp.SFTPWriteFeature; import ch.cyberduck.core.transfer.TransferStatus; import ch.cyberduck.test.IntegrationTest; @@ -38,7 +39,7 @@ public class DefaultAttributesFinderFeatureTest extends AbstractSFTPTest { final AttributesFinder f = new DefaultAttributesFinderFeature(session); final Path workdir = new SFTPHomeDirectoryService(session).find(); final Path file = new Path(workdir, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)); - new SFTPTouchFeature(session).touch(file, new TransferStatus()); + new SFTPTouchFeature(session).touch(new SFTPWriteFeature(session), file, new TransferStatus()); new SFTPUnixPermissionFeature(session).setUnixPermission(file, new Permission("-rw-rw-rw-")); final Attributes attributes = f.find(file); assertEquals(0L, attributes.getSize()); diff --git a/ssh/src/test/java/ch/cyberduck/core/shared/DefaultCopyFeatureTest.java b/ssh/src/test/java/ch/cyberduck/core/shared/DefaultCopyFeatureTest.java index 1215e10cd7..89090ab04a 100644 --- a/ssh/src/test/java/ch/cyberduck/core/shared/DefaultCopyFeatureTest.java +++ b/ssh/src/test/java/ch/cyberduck/core/shared/DefaultCopyFeatureTest.java @@ -56,7 +56,7 @@ public class DefaultCopyFeatureTest extends AbstractSFTPTest { public void testCopy() throws Exception { final Path source = new Path(new SFTPHomeDirectoryService(session).find(), new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)); final Path target = new Path(new SFTPHomeDirectoryService(session).find(), new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)); - new SFTPTouchFeature(session).touch(source, new TransferStatus()); + new SFTPTouchFeature(session).touch(new SFTPWriteFeature(session), source, new TransferStatus()); final byte[] content = RandomUtils.nextBytes(524); final TransferStatus status = new TransferStatus().setLength(content.length); final OutputStream out = new SFTPWriteFeature(session).write(source, status, new DisabledConnectionCallback()); diff --git a/ssh/src/test/java/ch/cyberduck/core/shared/DefaultDownloadFeatureTest.java b/ssh/src/test/java/ch/cyberduck/core/shared/DefaultDownloadFeatureTest.java index 6024b73838..a2f5df699e 100644 --- a/ssh/src/test/java/ch/cyberduck/core/shared/DefaultDownloadFeatureTest.java +++ b/ssh/src/test/java/ch/cyberduck/core/shared/DefaultDownloadFeatureTest.java @@ -54,7 +54,7 @@ public class DefaultDownloadFeatureTest extends AbstractSFTPTest { @Test public void testTransferAppend() throws Exception { final Path test = new Path(new SFTPHomeDirectoryService(session).find(), UUID.randomUUID().toString(), EnumSet.of(Path.Type.file)); - session.getFeature(Touch.class).touch(test, new TransferStatus()); + session.getFeature(Touch.class).touch(new SFTPWriteFeature(session), test, new TransferStatus()); final byte[] content = new byte[39864]; new Random().nextBytes(content); { @@ -67,15 +67,15 @@ public class DefaultDownloadFeatureTest extends AbstractSFTPTest { final Local local = new Local(System.getProperty("java.io.tmpdir"), UUID.randomUUID().toString()); { final TransferStatus status = new TransferStatus().setLength(content.length / 2); - new DefaultDownloadFeature(new SFTPReadFeature(session)).download( - test, local, new BandwidthThrottle(BandwidthThrottle.UNLIMITED), new DisabledStreamListener(), + new DefaultDownloadFeature(session).download( + new SFTPReadFeature(session), test, local, new BandwidthThrottle(BandwidthThrottle.UNLIMITED), new DisabledStreamListener(), status, new DisabledConnectionCallback()); } { final TransferStatus status = new TransferStatus().setLength(content.length / 2).setOffset(content.length / 2).setAppend(true); - new DefaultDownloadFeature(new SFTPReadFeature(session)).download( - test, local, new BandwidthThrottle(BandwidthThrottle.UNLIMITED), new DisabledStreamListener(), + new DefaultDownloadFeature(session).download( + new SFTPReadFeature(session), test, local, new BandwidthThrottle(BandwidthThrottle.UNLIMITED), new DisabledStreamListener(), status, new DisabledConnectionCallback()); } @@ -91,7 +91,7 @@ public class DefaultDownloadFeatureTest extends AbstractSFTPTest { @Test public void testTransferUnknownSize() throws Exception { final Path test = new Path(new SFTPHomeDirectoryService(session).find(), UUID.randomUUID().toString(), EnumSet.of(Path.Type.file)); - session.getFeature(Touch.class).touch(test, new TransferStatus()); + session.getFeature(Touch.class).touch(new SFTPWriteFeature(session), test, new TransferStatus()); final byte[] content = new byte[1]; new Random().nextBytes(content); { @@ -104,8 +104,8 @@ public class DefaultDownloadFeatureTest extends AbstractSFTPTest { final Local local = new Local(System.getProperty("java.io.tmpdir"), UUID.randomUUID().toString()); { final TransferStatus status = new TransferStatus().setLength(-1L); - new DefaultDownloadFeature(new SFTPReadFeature(session)).download( - test, local, new BandwidthThrottle(BandwidthThrottle.UNLIMITED), new DisabledStreamListener(), + new DefaultDownloadFeature(session).download( + new SFTPReadFeature(session), test, local, new BandwidthThrottle(BandwidthThrottle.UNLIMITED), new DisabledStreamListener(), status, new DisabledConnectionCallback()); } diff --git a/ssh/src/test/java/ch/cyberduck/core/shared/DefaultUploadFeatureTest.java b/ssh/src/test/java/ch/cyberduck/core/shared/DefaultUploadFeatureTest.java index 4a7d842d43..73707a9fbc 100644 --- a/ssh/src/test/java/ch/cyberduck/core/shared/DefaultUploadFeatureTest.java +++ b/ssh/src/test/java/ch/cyberduck/core/shared/DefaultUploadFeatureTest.java @@ -59,15 +59,15 @@ public class DefaultUploadFeatureTest extends AbstractSFTPTest { final Path test = new Path(new SFTPHomeDirectoryService(session).find(), UUID.randomUUID().toString(), EnumSet.of(Path.Type.file)); { final TransferStatus status = new TransferStatus().setLength(content.length / 2); - new DefaultUploadFeature(new SFTPWriteFeature(session)).upload( - test, local, new BandwidthThrottle(BandwidthThrottle.UNLIMITED), new DisabledProgressListener(), new DisabledStreamListener(), + new DefaultUploadFeature(session).upload( + new SFTPWriteFeature(session), test, local, new BandwidthThrottle(BandwidthThrottle.UNLIMITED), new DisabledProgressListener(), new DisabledStreamListener(), status, new DisabledConnectionCallback()); } { final TransferStatus status = new TransferStatus().setLength(content.length / 2).setOffset(content.length / 2).setAppend(true); - new DefaultUploadFeature(new SFTPWriteFeature(session)).upload( - test, local, new BandwidthThrottle(BandwidthThrottle.UNLIMITED), new DisabledProgressListener(), new DisabledStreamListener(), + new DefaultUploadFeature(session).upload( + new SFTPWriteFeature(session), test, local, new BandwidthThrottle(BandwidthThrottle.UNLIMITED), new DisabledProgressListener(), new DisabledStreamListener(), status, new DisabledConnectionCallback()); } diff --git a/ssh/src/test/java/ch/cyberduck/core/worker/CopyWorkerTest.java b/ssh/src/test/java/ch/cyberduck/core/worker/CopyWorkerTest.java index ccfece9b84..7ddfdeafb7 100644 --- a/ssh/src/test/java/ch/cyberduck/core/worker/CopyWorkerTest.java +++ b/ssh/src/test/java/ch/cyberduck/core/worker/CopyWorkerTest.java @@ -27,6 +27,7 @@ import ch.cyberduck.core.sftp.SFTPDirectoryFeature; import ch.cyberduck.core.sftp.SFTPFindFeature; import ch.cyberduck.core.sftp.SFTPHomeDirectoryService; import ch.cyberduck.core.sftp.SFTPTouchFeature; +import ch.cyberduck.core.sftp.SFTPWriteFeature; import ch.cyberduck.core.transfer.TransferStatus; import ch.cyberduck.test.IntegrationTest; @@ -47,7 +48,7 @@ public class CopyWorkerTest extends AbstractSFTPTest { final Path home = new SFTPHomeDirectoryService(session).find(); 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 SFTPTouchFeature(session).touch(source, new TransferStatus()); + new SFTPTouchFeature(session).touch(new SFTPWriteFeature(session), source, new TransferStatus()); assertTrue(new SFTPFindFeature(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); @@ -60,11 +61,11 @@ public class CopyWorkerTest extends AbstractSFTPTest { public void testCopyFileToDirectory() throws Exception { final Path home = new SFTPHomeDirectoryService(session).find(); final Path sourceFile = new Path(home, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)); - new SFTPTouchFeature(session).touch(sourceFile, new TransferStatus()); + new SFTPTouchFeature(session).touch(new SFTPWriteFeature(session), sourceFile, new TransferStatus()); assertTrue(new SFTPFindFeature(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 SFTPDirectoryFeature(session).mkdir(targetFolder, new TransferStatus()); + new SFTPDirectoryFeature(session).mkdir(new SFTPWriteFeature(session), targetFolder, new TransferStatus()); assertTrue(new SFTPFindFeature(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()); @@ -77,8 +78,8 @@ public class CopyWorkerTest extends AbstractSFTPTest { @Test public void testCopyDirectory() throws Exception { final Path home = new SFTPHomeDirectoryService(session).find(); - final Path folder = new SFTPDirectoryFeature(session).mkdir(new Path(home, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory)), new TransferStatus()); - final Path sourceFile = new SFTPTouchFeature(session).touch(new Path(folder, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)), new TransferStatus()); + final Path folder = new SFTPDirectoryFeature(session).mkdir(new SFTPWriteFeature(session), new Path(home, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory)), new TransferStatus()); + final Path sourceFile = new SFTPTouchFeature(session).touch(new SFTPWriteFeature(session), new Path(folder, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)), new TransferStatus()); assertTrue(new SFTPFindFeature(session).find(folder)); assertTrue(new SFTPFindFeature(session).find(sourceFile)); final Path targetFolder = new Path(home, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory)); diff --git a/ssh/src/test/java/ch/cyberduck/core/worker/SFTPSingleTransferWorkerTest.java b/ssh/src/test/java/ch/cyberduck/core/worker/SFTPSingleTransferWorkerTest.java index 1df53d4c12..ab736462ee 100644 --- a/ssh/src/test/java/ch/cyberduck/core/worker/SFTPSingleTransferWorkerTest.java +++ b/ssh/src/test/java/ch/cyberduck/core/worker/SFTPSingleTransferWorkerTest.java @@ -31,7 +31,7 @@ import ch.cyberduck.core.LoginConnectionService; import ch.cyberduck.core.Path; import ch.cyberduck.core.exception.BackgroundException; import ch.cyberduck.core.features.Delete; -import ch.cyberduck.core.features.Upload; +import ch.cyberduck.core.features.Write; import ch.cyberduck.core.io.StatusOutputStream; import ch.cyberduck.core.io.VoidStatusOutputStream; import ch.cyberduck.core.notification.DisabledNotificationService; @@ -42,7 +42,6 @@ import ch.cyberduck.core.sftp.SFTPDeleteFeature; import ch.cyberduck.core.sftp.SFTPDirectoryFeature; import ch.cyberduck.core.sftp.SFTPHomeDirectoryService; import ch.cyberduck.core.sftp.SFTPSession; -import ch.cyberduck.core.sftp.SFTPUploadFeature; import ch.cyberduck.core.sftp.SFTPWriteFeature; import ch.cyberduck.core.ssl.DefaultX509KeyManager; import ch.cyberduck.core.ssl.DisabledX509TrustManager; @@ -97,7 +96,7 @@ public class SFTPSingleTransferWorkerTest extends AbstractSFTPTest { } return super.getProperty(key); } - }.withCredentials(new Credentials("test", "test")); + }.setCredentials(new Credentials("test", "test")); final SFTPSession session = new SFTPSession(host, new DisabledX509TrustManager(), new DefaultX509KeyManager()) { final SFTPWriteFeature write = new SFTPWriteFeature(this) { @Override @@ -130,8 +129,8 @@ public class SFTPSingleTransferWorkerTest extends AbstractSFTPTest { @Override @SuppressWarnings("unchecked") public T _getFeature(final Class type) { - if(type == Upload.class) { - return (T) new SFTPUploadFeature(write); + if(type == Write.class) { + return (T) write; } return super._getFeature(type); } @@ -204,7 +203,7 @@ public class SFTPSingleTransferWorkerTest extends AbstractSFTPTest { final BytecountStreamListener counter = new BytecountStreamListener(); final Path remotedirectory = new Path(new SFTPHomeDirectoryService(session).find(), new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory)); - new SFTPDirectoryFeature(session).mkdir(remotedirectory, new TransferStatus()); + new SFTPDirectoryFeature(session).mkdir(new SFTPWriteFeature(session), remotedirectory, new TransferStatus()); final Path remotefile = new Path(remotedirectory, local.getName(), EnumSet.of(Path.Type.file)); final TransferStatus status = new TransferStatus(); final byte[] content = RandomUtils.nextBytes(8576); diff --git a/storegate/pom.xml b/storegate/pom.xml index 0c5a1ee19b..0e49cb4928 100644 --- a/storegate/pom.xml +++ b/storegate/pom.xml @@ -19,7 +19,7 @@ parent ch.cyberduck - 9.2.0-SNAPSHOT + 9.4.0-SNAPSHOT storegate diff --git a/storegate/src/main/java/ch/cyberduck/core/storegate/StoregateAttributesFinderFeature.java b/storegate/src/main/java/ch/cyberduck/core/storegate/StoregateAttributesFinderFeature.java index bf51396e5d..bd72d20fa2 100644 --- a/storegate/src/main/java/ch/cyberduck/core/storegate/StoregateAttributesFinderFeature.java +++ b/storegate/src/main/java/ch/cyberduck/core/storegate/StoregateAttributesFinderFeature.java @@ -15,6 +15,7 @@ package ch.cyberduck.core.storegate; * GNU General Public License for more details. */ +import ch.cyberduck.core.DefaultPathAttributes; import ch.cyberduck.core.DefaultPathContainerService; import ch.cyberduck.core.ListProgressListener; import ch.cyberduck.core.Path; @@ -33,8 +34,11 @@ import ch.cyberduck.core.storegate.io.swagger.client.model.File; import ch.cyberduck.core.storegate.io.swagger.client.model.RootFolder; import org.apache.commons.lang3.StringUtils; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; public class StoregateAttributesFinderFeature implements AttributesFinder, AttributesAdapter { + private static final Logger log = LogManager.getLogger(StoregateAttributesFinderFeature.class); private final StoregateSession session; private final StoregateIdProvider fileid; @@ -52,6 +56,7 @@ public class StoregateAttributesFinderFeature implements AttributesFinder, Attri for(RootFolder r : session.roots()) { if(StringUtils.equalsIgnoreCase(file.getName(), PathNormalizer.name(r.getPath())) || StringUtils.equalsIgnoreCase(file.getName(), PathNormalizer.name(r.getName()))) { + log.debug("Found root folder match for {}", file); return this.toAttributes(r); } } @@ -67,7 +72,7 @@ public class StoregateAttributesFinderFeature implements AttributesFinder, Attri @Override public PathAttributes toAttributes(final File f) { - final PathAttributes attrs = new PathAttributes(); + final PathAttributes attrs = new DefaultPathAttributes(); if(0 != f.getModified().getMillis()) { attrs.setModificationDate(f.getModified().getMillis()); } @@ -117,7 +122,7 @@ public class StoregateAttributesFinderFeature implements AttributesFinder, Attri } public PathAttributes toAttributes(final RootFolder f) { - final PathAttributes attrs = new PathAttributes(); + final PathAttributes attrs = new DefaultPathAttributes(); if(0 != f.getModified().getMillis()) { attrs.setModificationDate(f.getModified().getMillis()); } diff --git a/storegate/src/main/java/ch/cyberduck/core/storegate/StoregateDeleteFeature.java b/storegate/src/main/java/ch/cyberduck/core/storegate/StoregateDeleteFeature.java index 3192cc13b6..06d9073cdb 100644 --- a/storegate/src/main/java/ch/cyberduck/core/storegate/StoregateDeleteFeature.java +++ b/storegate/src/main/java/ch/cyberduck/core/storegate/StoregateDeleteFeature.java @@ -80,7 +80,7 @@ public class StoregateDeleteFeature implements Delete { } @Override - public EnumSet features() { + public EnumSet features(final Path file) { return EnumSet.of(Flags.recursive); } } diff --git a/storegate/src/main/java/ch/cyberduck/core/storegate/StoregateDirectoryFeature.java b/storegate/src/main/java/ch/cyberduck/core/storegate/StoregateDirectoryFeature.java index 8206d4c793..4d0d61ffa1 100644 --- a/storegate/src/main/java/ch/cyberduck/core/storegate/StoregateDirectoryFeature.java +++ b/storegate/src/main/java/ch/cyberduck/core/storegate/StoregateDirectoryFeature.java @@ -17,10 +17,10 @@ package ch.cyberduck.core.storegate; import ch.cyberduck.core.LocaleFactory; import ch.cyberduck.core.Path; -import ch.cyberduck.core.VersionId; import ch.cyberduck.core.exception.AccessDeniedException; import ch.cyberduck.core.exception.BackgroundException; import ch.cyberduck.core.features.Directory; +import ch.cyberduck.core.features.Write; import ch.cyberduck.core.storegate.io.swagger.client.ApiException; import ch.cyberduck.core.storegate.io.swagger.client.api.FilesApi; import ch.cyberduck.core.storegate.io.swagger.client.model.CreateFolderRequest; @@ -29,7 +29,7 @@ import ch.cyberduck.core.transfer.TransferStatus; import java.text.MessageFormat; -public class StoregateDirectoryFeature implements Directory { +public class StoregateDirectoryFeature implements Directory { private final StoregateSession session; private final StoregateIdProvider fileid; @@ -40,7 +40,7 @@ public class StoregateDirectoryFeature implements Directory { } @Override - public Path mkdir(final Path folder, final TransferStatus status) throws BackgroundException { + public Path mkdir(final Write writer, final Path folder, final TransferStatus status) throws BackgroundException { try { final FilesApi files = new FilesApi(session.getClient()); final CreateFolderRequest request = new CreateFolderRequest(); diff --git a/storegate/src/main/java/ch/cyberduck/core/storegate/StoregateIdProvider.java b/storegate/src/main/java/ch/cyberduck/core/storegate/StoregateIdProvider.java index 70991e7535..29843ae30f 100644 --- a/storegate/src/main/java/ch/cyberduck/core/storegate/StoregateIdProvider.java +++ b/storegate/src/main/java/ch/cyberduck/core/storegate/StoregateIdProvider.java @@ -72,6 +72,7 @@ public class StoregateIdProvider extends CachingFileIdProvider implements FileId for(RootFolder r : session.roots()) { if(StringUtils.equalsIgnoreCase(name, PathNormalizer.name(r.getPath())) || StringUtils.equalsIgnoreCase(name, PathNormalizer.name(r.getName()))) { + log.debug("Found root folder match for {}", file); if(service.isContainer(file)) { return r.getPath(); } diff --git a/storegate/src/main/java/ch/cyberduck/core/storegate/StoregateListService.java b/storegate/src/main/java/ch/cyberduck/core/storegate/StoregateListService.java index ea018b8612..12191711ee 100644 --- a/storegate/src/main/java/ch/cyberduck/core/storegate/StoregateListService.java +++ b/storegate/src/main/java/ch/cyberduck/core/storegate/StoregateListService.java @@ -30,9 +30,13 @@ import ch.cyberduck.core.storegate.io.swagger.client.model.File; import ch.cyberduck.core.storegate.io.swagger.client.model.FileContents; import ch.cyberduck.core.storegate.io.swagger.client.model.RootFolder; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; + import java.util.EnumSet; public class StoregateListService implements ListService { + private static final Logger log = LogManager.getLogger(StoregateListService.class); private final StoregateSession session; private final StoregateIdProvider fileid; @@ -54,9 +58,13 @@ public class StoregateListService implements ListService { switch(root.getRootFolderType()) { case 0: // My Files case 1: // Common + log.debug("Add root folder {}", root); list.add(new Path(directory, PathNormalizer.name(root.getName()), EnumSet.of(Path.Type.directory, Path.Type.volume), attributes.toAttributes(root))); break; + default: + log.warn("Ignoring root folder {}", root); + break; } listener.chunk(directory, list); } diff --git a/storegate/src/main/java/ch/cyberduck/core/storegate/StoregateMoveFeature.java b/storegate/src/main/java/ch/cyberduck/core/storegate/StoregateMoveFeature.java index 213bd29496..1c9eb66522 100644 --- a/storegate/src/main/java/ch/cyberduck/core/storegate/StoregateMoveFeature.java +++ b/storegate/src/main/java/ch/cyberduck/core/storegate/StoregateMoveFeature.java @@ -17,6 +17,7 @@ package ch.cyberduck.core.storegate; 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.exception.BackgroundException; @@ -72,7 +73,7 @@ public class StoregateMoveFeature implements Move { try { switch(response.getStatusLine().getStatusCode()) { case HttpStatus.SC_NO_CONTENT: - final PathAttributes attr = new PathAttributes(file.attributes()); + final PathAttributes attr = new DefaultPathAttributes(file.attributes()); fileid.cache(file, null); fileid.cache(renamed, attr.getFileId()); return new Path(renamed).withAttributes(attr); diff --git a/storegate/src/main/java/ch/cyberduck/core/storegate/StoregateSession.java b/storegate/src/main/java/ch/cyberduck/core/storegate/StoregateSession.java index 7fd9c7c8e3..a734b1cd62 100644 --- a/storegate/src/main/java/ch/cyberduck/core/storegate/StoregateSession.java +++ b/storegate/src/main/java/ch/cyberduck/core/storegate/StoregateSession.java @@ -31,13 +31,10 @@ import ch.cyberduck.core.exception.ConnectionCanceledException; import ch.cyberduck.core.exception.LoginFailureException; 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.jersey.HttpComponentsProvider; import ch.cyberduck.core.oauth.OAuth2ErrorResponseInterceptor; import ch.cyberduck.core.oauth.OAuth2RequestInterceptor; -import ch.cyberduck.core.preferences.HostPreferencesFactory; -import ch.cyberduck.core.preferences.PreferencesReader; import ch.cyberduck.core.proxy.ProxyFinder; import ch.cyberduck.core.ssl.X509KeyManager; import ch.cyberduck.core.ssl.X509TrustManager; @@ -100,7 +97,6 @@ public class StoregateSession extends HttpSession { @Override protected StoregateApiClient connect(final ProxyFinder proxy, final HostKeyCallback key, final LoginCallback prompt, final CancelCallback cancel) throws ConnectionCanceledException { final HttpClientBuilder configuration = builder.build(proxy, this, prompt); - final PreferencesReader preferences = HostPreferencesFactory.get(host); authorizationService = new OAuth2RequestInterceptor(configuration.addInterceptorLast(new HttpRequestInterceptor() { @Override public void process(final HttpRequest request, final HttpContext context) { @@ -116,7 +112,7 @@ public class StoregateSession extends HttpSession { // Force login even if browser session already exists authorizationService.withParameter("prompt", "login"); configuration.setServiceUnavailableRetryStrategy(new CustomServiceUnavailableRetryStrategy(host, - new ExecutionCountServiceUnavailableRetryStrategy(new OAuth2ErrorResponseInterceptor(host, authorizationService)))); + new OAuth2ErrorResponseInterceptor(host, authorizationService))); configuration.addInterceptorLast(authorizationService); final CloseableHttpClient apache = configuration.build(); final StoregateApiClient client = new StoregateApiClient(apache); @@ -137,7 +133,8 @@ public class StoregateSession extends HttpSession { @Override public void login(final LoginCallback controller, final CancelCallback cancel) throws BackgroundException { - final Credentials credentials = authorizationService.validate(); + final Credentials credentials = host.getCredentials(); + credentials.setOauth(authorizationService.validate(credentials.getOauth())); try { final HttpRequestBase request = new HttpPost( new HostUrlProvider().withUsername(false).withPath(true).get( @@ -180,6 +177,7 @@ public class StoregateSession extends HttpSession { credentials.setUsername(me.getUsername()); // Get root folders roots = new SettingsApi(client).settingsGetRootfolders(); + log.debug("Retrieved {} root folders", roots.size()); } catch(ApiException e) { throw new StoregateExceptionMappingService(fileid).map(e); @@ -194,9 +192,14 @@ public class StoregateSession extends HttpSession { } @Override - protected void logout() { - client.getHttpClient().close(); + public void disconnect() throws BackgroundException { fileid.clear(); + try { + client.getHttpClient().close(); + } + finally { + super.disconnect(); + } } @Override diff --git a/storegate/src/main/java/ch/cyberduck/core/storegate/StoregateShareFeature.java b/storegate/src/main/java/ch/cyberduck/core/storegate/StoregateShareFeature.java index c7faf39b8b..d5a770492d 100644 --- a/storegate/src/main/java/ch/cyberduck/core/storegate/StoregateShareFeature.java +++ b/storegate/src/main/java/ch/cyberduck/core/storegate/StoregateShareFeature.java @@ -43,6 +43,9 @@ public class StoregateShareFeature implements Share { public boolean isSupported(final Path file, final Type type) { switch(type) { case upload: + if(file.isRoot()) { + return false; + } return file.isDirectory(); } return true; diff --git a/storegate/src/main/java/ch/cyberduck/core/storegate/StoregateTouchFeature.java b/storegate/src/main/java/ch/cyberduck/core/storegate/StoregateTouchFeature.java index 00d4158a3e..56073a6e03 100644 --- a/storegate/src/main/java/ch/cyberduck/core/storegate/StoregateTouchFeature.java +++ b/storegate/src/main/java/ch/cyberduck/core/storegate/StoregateTouchFeature.java @@ -27,7 +27,7 @@ import java.text.MessageFormat; public class StoregateTouchFeature extends DefaultTouchFeature { public StoregateTouchFeature(final StoregateSession session, final StoregateIdProvider fileid) { - super(new StoregateWriteFeature(session, fileid)); + super(session); } @Override diff --git a/storegate/src/test/java/ch/cyberduck/core/storegate/StoregateAttributesFinderFeatureTest.java b/storegate/src/test/java/ch/cyberduck/core/storegate/StoregateAttributesFinderFeatureTest.java index 890a31b9c8..39448b91fb 100644 --- a/storegate/src/test/java/ch/cyberduck/core/storegate/StoregateAttributesFinderFeatureTest.java +++ b/storegate/src/test/java/ch/cyberduck/core/storegate/StoregateAttributesFinderFeatureTest.java @@ -65,11 +65,11 @@ public class StoregateAttributesFinderFeatureTest extends AbstractStoregateTest public void testFind() throws Exception { final StoregateIdProvider nodeid = new StoregateIdProvider(session); final Path room = new StoregateDirectoryFeature(session, nodeid).mkdir( - new Path(String.format("/My files/%s", new AlphanumericRandomStringService().random()), + new StoregateWriteFeature(session, nodeid), new Path(String.format("/My files/%s", new AlphanumericRandomStringService().random()), EnumSet.of(Path.Type.directory, Path.Type.volume)), new TransferStatus()); assertTrue(room.attributes().getPermission().isExecutable()); final Path test = new StoregateTouchFeature(session, nodeid).touch( - new Path(room, String.format("%s", new AlphanumericRandomStringService().random()), EnumSet.of(Path.Type.file)), new TransferStatus()); + new StoregateWriteFeature(session, nodeid), new Path(room, String.format("%s", new AlphanumericRandomStringService().random()), EnumSet.of(Path.Type.file)), new TransferStatus()); final PathAttributes attr = new StoregateAttributesFinderFeature(session, nodeid).find(test); assertEquals(attr, new StoregateAttributesFinderFeature(session, nodeid).find(new Path(test.getParent(), StringUtils.upperCase(test.getName()), test.getType()))); assertEquals(attr, new StoregateAttributesFinderFeature(session, nodeid).find(new Path(test.getParent(), StringUtils.lowerCase(test.getName()), test.getType()))); @@ -90,9 +90,9 @@ public class StoregateAttributesFinderFeatureTest extends AbstractStoregateTest public void testChangedNodeId() throws Exception { final StoregateIdProvider nodeid = new StoregateIdProvider(session); final Path room = new StoregateDirectoryFeature(session, nodeid).mkdir( - new Path(String.format("/My files/%s", new AlphanumericRandomStringService().random()), + new StoregateWriteFeature(session, nodeid), new Path(String.format("/My files/%s", new AlphanumericRandomStringService().random()), EnumSet.of(Path.Type.directory, Path.Type.volume)), new TransferStatus()); - final Path test = new StoregateTouchFeature(session, nodeid).touch(new Path(room, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)), new TransferStatus()); + final Path test = new StoregateTouchFeature(session, nodeid).touch(new StoregateWriteFeature(session, nodeid), new Path(room, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)), new TransferStatus()); final String latestnodeid = test.attributes().getFileId(); assertNotNull(latestnodeid); // Assume previously seen but changed on server diff --git a/storegate/src/test/java/ch/cyberduck/core/storegate/StoregateCopyFeatureTest.java b/storegate/src/test/java/ch/cyberduck/core/storegate/StoregateCopyFeatureTest.java index 60327df6af..914b43eeda 100644 --- a/storegate/src/test/java/ch/cyberduck/core/storegate/StoregateCopyFeatureTest.java +++ b/storegate/src/test/java/ch/cyberduck/core/storegate/StoregateCopyFeatureTest.java @@ -44,12 +44,12 @@ public class StoregateCopyFeatureTest extends AbstractStoregateTest { @Test public void testCopyFileServerSide() throws Exception { final StoregateIdProvider nodeid = new StoregateIdProvider(session); - final Path room = new StoregateDirectoryFeature(session, nodeid).mkdir(new Path( + final Path room = new StoregateDirectoryFeature(session, nodeid).mkdir(new StoregateWriteFeature(session, nodeid), new Path( String.format("/My files/%s", new AlphanumericRandomStringService().random()), EnumSet.of(Path.Type.directory, Path.Type.volume)), new TransferStatus()); - final Path test = new StoregateTouchFeature(session, nodeid).touch(new Path(room, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)), new TransferStatus()); - final Path copy = new Path(new StoregateDirectoryFeature(session, nodeid).mkdir(new Path(room, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory)), new TransferStatus()), test.getName(), EnumSet.of(Path.Type.file)); + final Path test = new StoregateTouchFeature(session, nodeid).touch(new StoregateWriteFeature(session, nodeid), new Path(room, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)), new TransferStatus()); + final Path copy = new Path(new StoregateDirectoryFeature(session, nodeid).mkdir(new StoregateWriteFeature(session, nodeid), new Path(room, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory)), new TransferStatus()), test.getName(), EnumSet.of(Path.Type.file)); final TransferStatus status = new TransferStatus(); - new StoregateTouchFeature(session, nodeid).touch(copy, status); + new StoregateTouchFeature(session, nodeid).touch(new StoregateWriteFeature(session, nodeid), copy, status); final StoregateCopyFeature feature = new StoregateCopyFeature(session, nodeid); assertTrue(feature.isSupported(test, Optional.of(copy))); assertNotEquals(test.attributes().getFileId(), new StoregateCopyFeature(session, nodeid).copy(test, copy, new TransferStatus(), new DisabledConnectionCallback(), new DisabledStreamListener()).attributes().getFileId()); @@ -61,10 +61,10 @@ public class StoregateCopyFeatureTest extends AbstractStoregateTest { @Test public void testCopyFileWithRename() throws Exception { final StoregateIdProvider fileid = new StoregateIdProvider(session); - final Path room = new StoregateDirectoryFeature(session, fileid).mkdir(new Path( + final Path room = new StoregateDirectoryFeature(session, fileid).mkdir(new StoregateWriteFeature(session, fileid), new Path( String.format("/My files/%s", new AlphanumericRandomStringService().random()), EnumSet.of(Path.Type.directory, Path.Type.volume)), new TransferStatus()); - final Path test = new StoregateTouchFeature(session, fileid).touch(new Path(room, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)), new TransferStatus()); - final Path copy = new Path(new StoregateDirectoryFeature(session, fileid).mkdir(new Path(room, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory)), new TransferStatus()), new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)); + final Path test = new StoregateTouchFeature(session, fileid).touch(new StoregateWriteFeature(session, fileid), new Path(room, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)), new TransferStatus()); + final Path copy = new Path(new StoregateDirectoryFeature(session, fileid).mkdir(new StoregateWriteFeature(session, fileid), new Path(room, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory)), new TransferStatus()), new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)); assertNotEquals(test.attributes().getFileId(), new StoregateCopyFeature(session, fileid).copy(test, copy, new TransferStatus(), new DisabledConnectionCallback(), new DisabledStreamListener()).attributes().getFileId()); assertTrue(new DefaultFindFeature(session).find(test)); assertTrue(new DefaultFindFeature(session).find(copy)); @@ -74,15 +74,15 @@ public class StoregateCopyFeatureTest extends AbstractStoregateTest { @Test public void testCopyServerSideToExistingFile() throws Exception { final StoregateIdProvider fileid = new StoregateIdProvider(session); - final Path top = new StoregateDirectoryFeature(session, fileid).mkdir(new Path( + final Path top = new StoregateDirectoryFeature(session, fileid).mkdir(new StoregateWriteFeature(session, fileid), new Path( String.format("/My files/%s", new AlphanumericRandomStringService().random()), EnumSet.of(Path.Type.directory, Path.Type.volume)), new TransferStatus()); final Path sourceFolder = new Path(top, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory)); final Path targetFolder = new Path(top, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory)); - new StoregateDirectoryFeature(session, fileid).mkdir(sourceFolder, new TransferStatus()); - new StoregateDirectoryFeature(session, fileid).mkdir(targetFolder, new TransferStatus()); - final Path test = new StoregateTouchFeature(session, fileid).touch(new Path(sourceFolder, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)), new TransferStatus()); + new StoregateDirectoryFeature(session, fileid).mkdir(new StoregateWriteFeature(session, fileid), sourceFolder, new TransferStatus()); + new StoregateDirectoryFeature(session, fileid).mkdir(new StoregateWriteFeature(session, fileid), targetFolder, new TransferStatus()); + final Path test = new StoregateTouchFeature(session, fileid).touch(new StoregateWriteFeature(session, fileid), new Path(sourceFolder, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)), new TransferStatus()); final Path copy = new Path(targetFolder, test.getName(), EnumSet.of(Path.Type.file)); - new StoregateTouchFeature(session, fileid).touch(copy, new TransferStatus()); + new StoregateTouchFeature(session, fileid).touch(new StoregateWriteFeature(session, fileid), copy, new TransferStatus()); final StoregateCopyFeature feature = new StoregateCopyFeature(session, fileid); assertTrue(feature.isSupported(test, Optional.of(copy))); assertNotEquals(test.attributes().getFileId(), new StoregateCopyFeature(session, fileid).copy(test, copy, new TransferStatus().setExists(true), new DisabledConnectionCallback(), new DisabledStreamListener()).attributes().getFileId()); @@ -95,13 +95,13 @@ public class StoregateCopyFeatureTest extends AbstractStoregateTest { @Test public void testCopyWithRenameToExistingFile() throws Exception { final StoregateIdProvider fileid = new StoregateIdProvider(session); - final Path top = new StoregateDirectoryFeature(session, fileid).mkdir(new Path( + final Path top = new StoregateDirectoryFeature(session, fileid).mkdir(new StoregateWriteFeature(session, fileid), new Path( String.format("/My files/%s", new AlphanumericRandomStringService().random()), EnumSet.of(Path.Type.directory, Path.Type.volume)), new TransferStatus()); final Path folder = new Path(top, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory)); - new StoregateDirectoryFeature(session, fileid).mkdir(folder, new TransferStatus()); - final Path test = new StoregateTouchFeature(session, fileid).touch(new Path(folder, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)), new TransferStatus()); + new StoregateDirectoryFeature(session, fileid).mkdir(new StoregateWriteFeature(session, fileid), folder, new TransferStatus()); + final Path test = new StoregateTouchFeature(session, fileid).touch(new StoregateWriteFeature(session, fileid), new Path(folder, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)), new TransferStatus()); final Path test2 = new Path(folder, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)); - new StoregateTouchFeature(session, fileid).touch(test2, new TransferStatus()); + new StoregateTouchFeature(session, fileid).touch(new StoregateWriteFeature(session, fileid), test2, new TransferStatus()); assertNotEquals(test.attributes().getFileId(), new StoregateCopyFeature(session, fileid).copy(test, test2, new TransferStatus().setExists(true), new DisabledConnectionCallback(), new DisabledStreamListener()).attributes().getFileId()); final Find find = new DefaultFindFeature(session); assertTrue(find.find(test)); @@ -112,13 +112,13 @@ public class StoregateCopyFeatureTest extends AbstractStoregateTest { @Test public void testCopyDirectoryServerSide() throws Exception { final StoregateIdProvider fileid = new StoregateIdProvider(session); - final Path top = new StoregateDirectoryFeature(session, fileid).mkdir(new Path( + final Path top = new StoregateDirectoryFeature(session, fileid).mkdir(new StoregateWriteFeature(session, fileid), new Path( String.format("/My files/%s", new AlphanumericRandomStringService().random()), EnumSet.of(Path.Type.directory, Path.Type.volume)), new TransferStatus()); - final Path directory = new StoregateDirectoryFeature(session, fileid).mkdir(new Path(top, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory)), new TransferStatus()); + final Path directory = new StoregateDirectoryFeature(session, fileid).mkdir(new StoregateWriteFeature(session, fileid), new Path(top, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory)), new TransferStatus()); final String name = new AlphanumericRandomStringService().random(); final TransferStatus status = new TransferStatus(); - final Path file = new StoregateTouchFeature(session, fileid).touch(new Path(directory, name, EnumSet.of(Path.Type.file)), status); - final Path target_parent = new StoregateDirectoryFeature(session, fileid).mkdir(new Path(top, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory)), new TransferStatus()); + final Path file = new StoregateTouchFeature(session, fileid).touch(new StoregateWriteFeature(session, fileid), new Path(directory, name, EnumSet.of(Path.Type.file)), status); + final Path target_parent = new StoregateDirectoryFeature(session, fileid).mkdir(new StoregateWriteFeature(session, fileid), new Path(top, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory)), new TransferStatus()); final Path target = new Path(target_parent, directory.getName(), EnumSet.of(Path.Type.directory)); final StoregateCopyFeature feature = new StoregateCopyFeature(session, fileid); assertTrue(feature.isSupported(directory, Optional.of(target))); diff --git a/storegate/src/test/java/ch/cyberduck/core/storegate/StoregateDeleteFeatureTest.java b/storegate/src/test/java/ch/cyberduck/core/storegate/StoregateDeleteFeatureTest.java index bc9feb8c7b..240cc672e4 100644 --- a/storegate/src/test/java/ch/cyberduck/core/storegate/StoregateDeleteFeatureTest.java +++ b/storegate/src/test/java/ch/cyberduck/core/storegate/StoregateDeleteFeatureTest.java @@ -42,7 +42,7 @@ public class StoregateDeleteFeatureTest extends AbstractStoregateTest { final StoregateIdProvider nodeid = new StoregateIdProvider(session); final Path room = new Path("/My files", EnumSet.of(Path.Type.directory, Path.Type.volume)); final Path fileInRoom = new Path(room, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)); - new StoregateTouchFeature(session, nodeid).touch(fileInRoom, new TransferStatus()); + new StoregateTouchFeature(session, nodeid).touch(new StoregateWriteFeature(session, nodeid), fileInRoom, new TransferStatus()); assertTrue(new DefaultFindFeature(session).find(fileInRoom)); new StoregateDeleteFeature(session, nodeid).delete(Collections.singletonList(fileInRoom), new DisabledLoginCallback(), new Delete.DisabledCallback()); assertFalse(new DefaultFindFeature(session).find(fileInRoom)); @@ -53,7 +53,7 @@ public class StoregateDeleteFeatureTest extends AbstractStoregateTest { final StoregateIdProvider nodeid = new StoregateIdProvider(session); final Path room = new Path("/My files", EnumSet.of(Path.Type.directory, Path.Type.volume)); final Path fileInRoom = new Path(room, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)); - new StoregateTouchFeature(session, nodeid).touch(fileInRoom, new TransferStatus()); + new StoregateTouchFeature(session, nodeid).touch(new StoregateWriteFeature(session, nodeid), fileInRoom, new TransferStatus()); final String lock = new StoregateLockFeature(session, nodeid).lock(fileInRoom); assertTrue(new DefaultFindFeature(session).find(fileInRoom)); new StoregateDeleteFeature(session, nodeid).delete(Collections.singletonMap(fileInRoom, new TransferStatus().setLockId(lock)), new DisabledLoginCallback(), new Delete.DisabledCallback()); @@ -63,13 +63,13 @@ public class StoregateDeleteFeatureTest extends AbstractStoregateTest { @Test public void testDeleteFolderRoomWithContent() throws Exception { final StoregateIdProvider nodeid = new StoregateIdProvider(session); - final Path room = new StoregateDirectoryFeature(session, nodeid).mkdir(new Path( + final Path room = new StoregateDirectoryFeature(session, nodeid).mkdir(new StoregateWriteFeature(session, nodeid), new Path( String.format("/My files/%s", new AlphanumericRandomStringService().random()), EnumSet.of(Path.Type.directory, Path.Type.volume)), new TransferStatus()); - final Path folder = new StoregateDirectoryFeature(session, nodeid).mkdir(new Path(room, + final Path folder = new StoregateDirectoryFeature(session, nodeid).mkdir(new StoregateWriteFeature(session, nodeid), new Path(room, new AlphanumericRandomStringService().random().toLowerCase(), EnumSet.of(Path.Type.directory)), new TransferStatus()); assertTrue(new DefaultFindFeature(session).find(folder)); final Path file = new Path(folder, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)); - new StoregateTouchFeature(session, nodeid).touch(file, new TransferStatus()); + new StoregateTouchFeature(session, nodeid).touch(new StoregateWriteFeature(session, nodeid), file, new TransferStatus()); assertTrue(new DefaultFindFeature(session).find(file)); new StoregateDeleteFeature(session, nodeid).delete(Collections.singletonList(folder), new DisabledLoginCallback(), new Delete.DisabledCallback()); assertFalse(new DefaultFindFeature(session).find(folder)); diff --git a/storegate/src/test/java/ch/cyberduck/core/storegate/StoregateDirectoryFeatureTest.java b/storegate/src/test/java/ch/cyberduck/core/storegate/StoregateDirectoryFeatureTest.java index 75bb60a617..4a3cc597d0 100644 --- a/storegate/src/test/java/ch/cyberduck/core/storegate/StoregateDirectoryFeatureTest.java +++ b/storegate/src/test/java/ch/cyberduck/core/storegate/StoregateDirectoryFeatureTest.java @@ -39,10 +39,10 @@ public class StoregateDirectoryFeatureTest extends AbstractStoregateTest { public void testCreateDirectory() throws Exception { final StoregateIdProvider nodeid = new StoregateIdProvider(session); final Path folder = new StoregateDirectoryFeature(session, nodeid).mkdir( - new Path(String.format("/My files/%s", new AlphanumericRandomStringService().random()), EnumSet.of(Path.Type.directory)), new TransferStatus()); + new StoregateWriteFeature(session, nodeid), new Path(String.format("/My files/%s", new AlphanumericRandomStringService().random()), EnumSet.of(Path.Type.directory)), new TransferStatus()); assertTrue(new StoregateFindFeature(session, nodeid).find(folder)); // Can create again regardless if exists - new StoregateDirectoryFeature(session, nodeid).mkdir(folder, new TransferStatus()); + new StoregateDirectoryFeature(session, nodeid).mkdir(new StoregateWriteFeature(session, nodeid), folder, new TransferStatus()); new StoregateDeleteFeature(session, nodeid).delete(Collections.singletonList(folder), new DisabledLoginCallback(), new Delete.DisabledCallback()); assertFalse(new DefaultFindFeature(session).find(folder)); } diff --git a/storegate/src/test/java/ch/cyberduck/core/storegate/StoregateIdProviderTest.java b/storegate/src/test/java/ch/cyberduck/core/storegate/StoregateIdProviderTest.java index e2089fd094..465a8977a9 100644 --- a/storegate/src/test/java/ch/cyberduck/core/storegate/StoregateIdProviderTest.java +++ b/storegate/src/test/java/ch/cyberduck/core/storegate/StoregateIdProviderTest.java @@ -41,7 +41,7 @@ public class StoregateIdProviderTest extends AbstractStoregateTest { final StoregateIdProvider nodeid = new StoregateIdProvider(session); final Path home = new Path("/My files", EnumSet.of(Path.Type.directory, Path.Type.volume)); final String name = String.format("%s", new AlphanumericRandomStringService().random()); - final Path file = new StoregateTouchFeature(session, nodeid).touch(new Path(home, name, EnumSet.of(Path.Type.file)), new TransferStatus()); + final Path file = new StoregateTouchFeature(session, nodeid).touch(new StoregateWriteFeature(session, nodeid), new Path(home, name, EnumSet.of(Path.Type.file)), new TransferStatus()); nodeid.clear(); final String nodeId = nodeid.getFileId(new Path(home, name, EnumSet.of(Path.Type.file))); assertNotNull(nodeId); diff --git a/storegate/src/test/java/ch/cyberduck/core/storegate/StoregateListServiceTest.java b/storegate/src/test/java/ch/cyberduck/core/storegate/StoregateListServiceTest.java index a55049df87..d720171d77 100644 --- a/storegate/src/test/java/ch/cyberduck/core/storegate/StoregateListServiceTest.java +++ b/storegate/src/test/java/ch/cyberduck/core/storegate/StoregateListServiceTest.java @@ -84,10 +84,10 @@ public class StoregateListServiceTest extends AbstractStoregateTest { public void testList() throws Exception { final StoregateIdProvider nodeid = new StoregateIdProvider(session); final Path room = new Path("/My files", EnumSet.of(Path.Type.directory, Path.Type.volume)); - final Path folder = new StoregateDirectoryFeature(session, nodeid).mkdir(new Path(room, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory)), new TransferStatus()); + final Path folder = new StoregateDirectoryFeature(session, nodeid).mkdir(new StoregateWriteFeature(session, nodeid), new Path(room, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory)), new TransferStatus()); final TransferStatus status = new TransferStatus(); status.setHidden(true); - final Path file = new StoregateTouchFeature(session, nodeid).touch(new Path(folder, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)), status); + final Path file = new StoregateTouchFeature(session, nodeid).touch(new StoregateWriteFeature(session, nodeid), new Path(folder, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)), status); final AttributedList list = new StoregateListService(session, nodeid).list(folder, new DisabledListProgressListener()); assertNotSame(AttributedList.emptyList(), list); assertTrue(list.contains(file)); @@ -101,11 +101,11 @@ public class StoregateListServiceTest extends AbstractStoregateTest { public void testListWithHiddenFile() throws Exception { final StoregateIdProvider nodeid = new StoregateIdProvider(session); final Path room = new Path("/My files", EnumSet.of(Path.Type.directory, Path.Type.volume)); - final Path folder = new StoregateDirectoryFeature(session, nodeid).mkdir(new Path(room, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory)), new TransferStatus()); + final Path folder = new StoregateDirectoryFeature(session, nodeid).mkdir(new StoregateWriteFeature(session, nodeid), new Path(room, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory)), new TransferStatus()); final Path file = new Path(folder, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)); final TransferStatus status = new TransferStatus(); status.setHidden(true); - new StoregateTouchFeature(session, nodeid).touch(file, status); + new StoregateTouchFeature(session, nodeid).touch(new StoregateWriteFeature(session, nodeid), file, status); final AttributedList list = new StoregateListService(session, nodeid).list(folder, new IndexedListProgressListener() { @Override public void message(final String message) { @@ -128,7 +128,7 @@ public class StoregateListServiceTest extends AbstractStoregateTest { public void testListEmptyFolder() throws Exception { final StoregateIdProvider nodeid = new StoregateIdProvider(session); final Path room = new Path("/My files", EnumSet.of(Path.Type.directory, Path.Type.volume)); - final Path folder = new StoregateDirectoryFeature(session, nodeid).mkdir(new Path(room, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory)), new TransferStatus()); + final Path folder = new StoregateDirectoryFeature(session, nodeid).mkdir(new StoregateWriteFeature(session, nodeid), new Path(room, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory)), new TransferStatus()); final AtomicBoolean callback = new AtomicBoolean(); assertTrue(new StoregateListService(session, nodeid).list(folder, new DisabledListProgressListener() { @Override diff --git a/storegate/src/test/java/ch/cyberduck/core/storegate/StoregateLockFeatureTest.java b/storegate/src/test/java/ch/cyberduck/core/storegate/StoregateLockFeatureTest.java index dd0f58be8c..18eca90358 100644 --- a/storegate/src/test/java/ch/cyberduck/core/storegate/StoregateLockFeatureTest.java +++ b/storegate/src/test/java/ch/cyberduck/core/storegate/StoregateLockFeatureTest.java @@ -38,10 +38,10 @@ public class StoregateLockFeatureTest extends AbstractStoregateTest { @Test public void testLock() throws Exception { final StoregateIdProvider nodeid = new StoregateIdProvider(session); - final Path room = new StoregateDirectoryFeature(session, nodeid).mkdir(new Path( + final Path room = new StoregateDirectoryFeature(session, nodeid).mkdir(new StoregateWriteFeature(session, nodeid), new Path( String.format("/My files/%s", new AlphanumericRandomStringService().random()), EnumSet.of(Path.Type.directory, Path.Type.volume)), new TransferStatus()); final Path fileInRoom = new Path(room, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)); - new StoregateTouchFeature(session, nodeid).touch(fileInRoom, new TransferStatus()); + new StoregateTouchFeature(session, nodeid).touch(new StoregateWriteFeature(session, nodeid), fileInRoom, new TransferStatus()); assertTrue(new DefaultFindFeature(session).find(fileInRoom)); final String lock = new StoregateLockFeature(session, nodeid).lock(fileInRoom); assertNotNull(lock); diff --git a/storegate/src/test/java/ch/cyberduck/core/storegate/StoregateMoveFeatureTest.java b/storegate/src/test/java/ch/cyberduck/core/storegate/StoregateMoveFeatureTest.java index cbdbd993da..100b39b1e9 100644 --- a/storegate/src/test/java/ch/cyberduck/core/storegate/StoregateMoveFeatureTest.java +++ b/storegate/src/test/java/ch/cyberduck/core/storegate/StoregateMoveFeatureTest.java @@ -44,10 +44,10 @@ public class StoregateMoveFeatureTest extends AbstractStoregateTest { public void testMove() throws Exception { final StoregateIdProvider nodeid = new StoregateIdProvider(session); final Path room = new StoregateDirectoryFeature(session, nodeid).mkdir( - new Path(String.format("/My files/%s", new AlphanumericRandomStringService().random()), + new StoregateWriteFeature(session, nodeid), new Path(String.format("/My files/%s", new AlphanumericRandomStringService().random()), EnumSet.of(Path.Type.directory, Path.Type.volume)), new TransferStatus()); final TransferStatus status = new TransferStatus(); - final Path test = new StoregateTouchFeature(session, nodeid).touch(new Path(room, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)), status); + final Path test = new StoregateTouchFeature(session, nodeid).touch(new StoregateWriteFeature(session, nodeid), new Path(room, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)), status); final String fileid = test.attributes().getFileId(); final Path target = new StoregateMoveFeature(session, nodeid).move(test, new Path(room, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)), new TransferStatus(), new Delete.DisabledCallback(), new DisabledConnectionCallback()); assertEquals(fileid, target.attributes().getFileId()); @@ -63,9 +63,9 @@ public class StoregateMoveFeatureTest extends AbstractStoregateTest { public void testMoveWithLock() throws Exception { final StoregateIdProvider nodeid = new StoregateIdProvider(session); final Path room = new StoregateDirectoryFeature(session, nodeid).mkdir( - new Path(String.format("/My files/%s", new AlphanumericRandomStringService().random()), + new StoregateWriteFeature(session, nodeid), new Path(String.format("/My files/%s", new AlphanumericRandomStringService().random()), EnumSet.of(Path.Type.directory, Path.Type.volume)), new TransferStatus()); - final Path test = new StoregateTouchFeature(session, nodeid).touch(new Path(room, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)), new TransferStatus()); + final Path test = new StoregateTouchFeature(session, nodeid).touch(new StoregateWriteFeature(session, nodeid), new Path(room, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)), new TransferStatus()); final String fileid = test.attributes().getFileId(); final String lockId = new StoregateLockFeature(session, nodeid).lock(test); final Path target = new Path(room, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)); @@ -81,13 +81,13 @@ public class StoregateMoveFeatureTest extends AbstractStoregateTest { public void testMoveToDifferentFolder() throws Exception { final StoregateIdProvider nodeid = new StoregateIdProvider(session); final Path folder1 = new StoregateDirectoryFeature(session, nodeid).mkdir( - new Path(String.format("/My files/%s", new AlphanumericRandomStringService().random()), + new StoregateWriteFeature(session, nodeid), new Path(String.format("/My files/%s", new AlphanumericRandomStringService().random()), EnumSet.of(Path.Type.directory, Path.Type.volume)), new TransferStatus()); final Path folder2 = new StoregateDirectoryFeature(session, nodeid).mkdir( - new Path(String.format("/My files/%s", new AlphanumericRandomStringService().random()), + new StoregateWriteFeature(session, nodeid), new Path(String.format("/My files/%s", new AlphanumericRandomStringService().random()), EnumSet.of(Path.Type.directory, Path.Type.volume)), new TransferStatus()); final Path test = new Path(folder1, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)); - new StoregateTouchFeature(session, nodeid).touch(test, new TransferStatus()); + new StoregateTouchFeature(session, nodeid).touch(new StoregateWriteFeature(session, nodeid), test, new TransferStatus()); final Path target = new Path(folder2, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)); new StoregateMoveFeature(session, nodeid).move(test, target, new TransferStatus(), new Delete.DisabledCallback(), new DisabledConnectionCallback()); assertFalse(new DefaultFindFeature(session).find(test)); @@ -100,11 +100,11 @@ public class StoregateMoveFeatureTest extends AbstractStoregateTest { public void testMoveDirectory() throws Exception { final StoregateIdProvider nodeid = new StoregateIdProvider(session); final Path room = new StoregateDirectoryFeature(session, nodeid).mkdir( - new Path(String.format("/My files/%s", new AlphanumericRandomStringService().random()), + new StoregateWriteFeature(session, nodeid), new Path(String.format("/My files/%s", new AlphanumericRandomStringService().random()), EnumSet.of(Path.Type.directory, Path.Type.volume)), new TransferStatus()); final String foldername = new AlphanumericRandomStringService().random(); - final Path test = new StoregateDirectoryFeature(session, nodeid).mkdir(new Path(room, foldername, EnumSet.of(Path.Type.directory)), new TransferStatus()); - final Path testsub = new StoregateDirectoryFeature(session, nodeid).mkdir(new Path(test, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory)), new TransferStatus()); + final Path test = new StoregateDirectoryFeature(session, nodeid).mkdir(new StoregateWriteFeature(session, nodeid), new Path(room, foldername, EnumSet.of(Path.Type.directory)), new TransferStatus()); + final Path testsub = new StoregateDirectoryFeature(session, nodeid).mkdir(new StoregateWriteFeature(session, nodeid), new Path(test, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory)), new TransferStatus()); final Path target = new Path(room, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory)); new StoregateMoveFeature(session, nodeid).move(test, target, new TransferStatus(), new Delete.DisabledCallback(), new DisabledConnectionCallback()); assertEquals(0, session.getMetrics().get(Copy.class)); @@ -117,12 +117,12 @@ public class StoregateMoveFeatureTest extends AbstractStoregateTest { public void testMoveOverride() throws Exception { final StoregateIdProvider nodeid = new StoregateIdProvider(session); final Path room = new StoregateDirectoryFeature(session, nodeid).mkdir( - new Path(String.format("/My files/%s", new AlphanumericRandomStringService().random()), + new StoregateWriteFeature(session, nodeid), new Path(String.format("/My files/%s", new AlphanumericRandomStringService().random()), EnumSet.of(Path.Type.directory, Path.Type.volume)), new TransferStatus()); final String filename = new AlphanumericRandomStringService().random(); - final Path test = new StoregateTouchFeature(session, nodeid).touch(new Path(room, filename, EnumSet.of(Path.Type.file)), new TransferStatus()); + final Path test = new StoregateTouchFeature(session, nodeid).touch(new StoregateWriteFeature(session, nodeid), new Path(room, filename, EnumSet.of(Path.Type.file)), new TransferStatus()); final Path target = new Path(room, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)); - new StoregateTouchFeature(session, nodeid).touch(target, new TransferStatus()); + new StoregateTouchFeature(session, nodeid).touch(new StoregateWriteFeature(session, nodeid), target, new TransferStatus()); new StoregateMoveFeature(session, nodeid).move(test, target, new TransferStatus().setExists(true), new Delete.DisabledCallback(), new DisabledConnectionCallback()); assertFalse(new DefaultFindFeature(session).find(new Path(room, filename, EnumSet.of(Path.Type.file)))); assertTrue(new DefaultFindFeature(session).find(target)); @@ -133,12 +133,12 @@ public class StoregateMoveFeatureTest extends AbstractStoregateTest { public void testMoveToDifferentParentAndRename() throws Exception { final StoregateIdProvider nodeid = new StoregateIdProvider(session); final Path room = new StoregateDirectoryFeature(session, nodeid).mkdir( - new Path(String.format("/My files/%s", new AlphanumericRandomStringService().random()), + new StoregateWriteFeature(session, nodeid), new Path(String.format("/My files/%s", new AlphanumericRandomStringService().random()), EnumSet.of(Path.Type.directory, Path.Type.volume)), new TransferStatus()); final String filename = new AlphanumericRandomStringService().random(); - final Path test = new StoregateTouchFeature(session, nodeid).touch(new Path(room, filename, EnumSet.of(Path.Type.file)), new TransferStatus()); + final Path test = new StoregateTouchFeature(session, nodeid).touch(new StoregateWriteFeature(session, nodeid), new Path(room, filename, EnumSet.of(Path.Type.file)), new TransferStatus()); final Path target = new Path(room, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)); - new StoregateTouchFeature(session, nodeid).touch(target, new TransferStatus()); + new StoregateTouchFeature(session, nodeid).touch(new StoregateWriteFeature(session, nodeid), target, new TransferStatus()); new StoregateMoveFeature(session, nodeid).move(test, target, new TransferStatus().setExists(true), new Delete.DisabledCallback(), new DisabledConnectionCallback()); assertFalse(new DefaultFindFeature(session).find(new Path(room, filename, EnumSet.of(Path.Type.file)))); assertTrue(new DefaultFindFeature(session).find(target)); diff --git a/storegate/src/test/java/ch/cyberduck/core/storegate/StoregateMultipartWriteFeatureTest.java b/storegate/src/test/java/ch/cyberduck/core/storegate/StoregateMultipartWriteFeatureTest.java index c4d5e69fa5..c73599421b 100644 --- a/storegate/src/test/java/ch/cyberduck/core/storegate/StoregateMultipartWriteFeatureTest.java +++ b/storegate/src/test/java/ch/cyberduck/core/storegate/StoregateMultipartWriteFeatureTest.java @@ -51,14 +51,14 @@ public class StoregateMultipartWriteFeatureTest extends AbstractStoregateTest { public void testReadWrite() throws Exception { final StoregateIdProvider nodeid = new StoregateIdProvider(session); final Path folder = new StoregateDirectoryFeature(session, nodeid).mkdir( - new Path(String.format("/My files/%s", new AlphanumericRandomStringService().random()), + new StoregateWriteFeature(session, nodeid), new Path(String.format("/My files/%s", new AlphanumericRandomStringService().random()), EnumSet.of(Path.Type.directory, Path.Type.volume)), new TransferStatus()); final byte[] content = RandomUtils.nextBytes(524289); final TransferStatus status = new TransferStatus(); status.setLength(content.length); final Path test = new Path(folder, UUID.randomUUID().toString(), EnumSet.of(Path.Type.file)); final StoregateMultipartWriteFeature writer = new StoregateMultipartWriteFeature(session, nodeid); - final HttpResponseOutputStream out = writer.write(test, status, new DisabledConnectionCallback()); + final HttpResponseOutputStream out = new StoregateWriteFeature(session, nodeid).write(test, status, new DisabledConnectionCallback()); assertNotNull(out); new StreamCopier(status, status).transfer(new ByteArrayInputStream(content), out); final String version = out.getStatus().getId(); @@ -79,11 +79,11 @@ public class StoregateMultipartWriteFeatureTest extends AbstractStoregateTest { public void testWriteWithLock() throws Exception { final StoregateIdProvider nodeid = new StoregateIdProvider(session); final Path room = new StoregateDirectoryFeature(session, nodeid).mkdir( - new Path(String.format("/My files/%s", new AlphanumericRandomStringService().random()), + new StoregateWriteFeature(session, nodeid), new Path(String.format("/My files/%s", new AlphanumericRandomStringService().random()), EnumSet.of(Path.Type.directory, Path.Type.volume)), new TransferStatus()); final byte[] content = RandomUtils.nextBytes(32769); final Path test = new StoregateTouchFeature(session, nodeid).touch( - new Path(room, String.format("%s", new AlphanumericRandomStringService().random()), EnumSet.of(Path.Type.file)), new TransferStatus()); + new StoregateWriteFeature(session, nodeid), new Path(room, String.format("%s", new AlphanumericRandomStringService().random()), EnumSet.of(Path.Type.file)), new TransferStatus()); final String lockId = new StoregateLockFeature(session, nodeid).lock(test); final TransferStatus status = new TransferStatus().setLength(TransferStatus.UNKNOWN_LENGTH); final StoregateMultipartWriteFeature writer = new StoregateMultipartWriteFeature(session, nodeid); @@ -107,7 +107,7 @@ public class StoregateMultipartWriteFeatureTest extends AbstractStoregateTest { public void testWriteCancel() throws Exception { final StoregateIdProvider nodeid = new StoregateIdProvider(session); final Path room = new StoregateDirectoryFeature(session, nodeid).mkdir( - new Path(String.format("/My files/%s", new AlphanumericRandomStringService().random()), + new StoregateWriteFeature(session, nodeid), new Path(String.format("/My files/%s", new AlphanumericRandomStringService().random()), EnumSet.of(Path.Type.directory, Path.Type.volume)), new TransferStatus().setLength(TransferStatus.UNKNOWN_LENGTH)); final byte[] content = RandomUtils.nextBytes(32769); final Path test = new Path(room, String.format("{%s", new AlphanumericRandomStringService().random()), EnumSet.of(Path.Type.file)); @@ -123,7 +123,7 @@ public class StoregateMultipartWriteFeatureTest extends AbstractStoregateTest { }; status.setLength(content.length); final StoregateMultipartWriteFeature writer = new StoregateMultipartWriteFeature(session, nodeid); - final HttpResponseOutputStream out = writer.write(test, status, new DisabledConnectionCallback()); + final HttpResponseOutputStream out = new StoregateWriteFeature(session, nodeid).write(test, status, new DisabledConnectionCallback()); assertNotNull(out); new StreamCopier(status, status).withListener(listener).transfer(new ByteArrayInputStream(content), out); assertFalse(new StoregateFindFeature(session, nodeid).find(test)); @@ -135,13 +135,13 @@ public class StoregateMultipartWriteFeatureTest extends AbstractStoregateTest { public void testWriteSingleByte() throws Exception { final StoregateIdProvider nodeid = new StoregateIdProvider(session); final Path room = new StoregateDirectoryFeature(session, nodeid).mkdir( - new Path(String.format("/My files/%s", new AlphanumericRandomStringService().random()), + new StoregateWriteFeature(session, nodeid), new Path(String.format("/My files/%s", new AlphanumericRandomStringService().random()), EnumSet.of(Path.Type.directory, Path.Type.volume)), new TransferStatus()); final byte[] content = RandomUtils.nextBytes(1); final TransferStatus status = new TransferStatus().setLength(content.length); final Path test = new Path(room, UUID.randomUUID().toString(), EnumSet.of(Path.Type.file)); final StoregateMultipartWriteFeature writer = new StoregateMultipartWriteFeature(session, nodeid); - final HttpResponseOutputStream out = writer.write(test, status, new DisabledConnectionCallback()); + final HttpResponseOutputStream out = new StoregateWriteFeature(session, nodeid).write(test, status, new DisabledConnectionCallback()); assertNotNull(out); new StreamCopier(status, status).transfer(new ByteArrayInputStream(content), out); final String version = out.getStatus().getId(); @@ -155,7 +155,7 @@ public class StoregateMultipartWriteFeatureTest extends AbstractStoregateTest { public void testWriteSingleByteUnknownLength() throws Exception { final StoregateIdProvider nodeid = new StoregateIdProvider(session); final Path room = new StoregateDirectoryFeature(session, nodeid).mkdir( - new Path(String.format("/My files/%s", new AlphanumericRandomStringService().random()), + new StoregateWriteFeature(session, nodeid), new Path(String.format("/My files/%s", new AlphanumericRandomStringService().random()), EnumSet.of(Path.Type.directory, Path.Type.volume)), new TransferStatus()); final byte[] content = RandomUtils.nextBytes(1); final TransferStatus status = new TransferStatus().setLength(TransferStatus.UNKNOWN_LENGTH); @@ -176,12 +176,12 @@ public class StoregateMultipartWriteFeatureTest extends AbstractStoregateTest { public void testWriteZeroLength() throws Exception { final StoregateIdProvider nodeid = new StoregateIdProvider(session); final Path room = new StoregateDirectoryFeature(session, nodeid).mkdir( - new Path(String.format("/My files/%s", new AlphanumericRandomStringService().random()), + new StoregateWriteFeature(session, nodeid), new Path(String.format("/My files/%s", new AlphanumericRandomStringService().random()), EnumSet.of(Path.Type.directory, Path.Type.volume)), new TransferStatus()); final TransferStatus status = new TransferStatus().setLength(0L); final Path test = new Path(room, UUID.randomUUID().toString(), EnumSet.of(Path.Type.file)); final StoregateMultipartWriteFeature writer = new StoregateMultipartWriteFeature(session, nodeid); - final HttpResponseOutputStream out = writer.write(test, status, new DisabledConnectionCallback()); + final HttpResponseOutputStream out = new StoregateWriteFeature(session, nodeid).write(test, status, new DisabledConnectionCallback()); assertNotNull(out); new StreamCopier(status, status).transfer(new NullInputStream(0L), out); assertEquals(0L, out.getStatus().getSize(), 0L); diff --git a/storegate/src/test/java/ch/cyberduck/core/storegate/StoregateReadFeatureTest.java b/storegate/src/test/java/ch/cyberduck/core/storegate/StoregateReadFeatureTest.java index 20fe33860a..b9060589b2 100644 --- a/storegate/src/test/java/ch/cyberduck/core/storegate/StoregateReadFeatureTest.java +++ b/storegate/src/test/java/ch/cyberduck/core/storegate/StoregateReadFeatureTest.java @@ -56,7 +56,7 @@ public class StoregateReadFeatureTest extends AbstractStoregateTest { final TransferStatus status = new TransferStatus(); final StoregateIdProvider nodeid = new StoregateIdProvider(session); final Path room = new StoregateDirectoryFeature(session, nodeid).mkdir( - new Path(new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory, Path.Type.volume)), new TransferStatus()); + new StoregateWriteFeature(session, nodeid), new Path(new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory, Path.Type.volume)), new TransferStatus()); try { new StoregateReadFeature(session, nodeid).read(new Path(room, "nosuchname", EnumSet.of(Path.Type.file)), status, new DisabledConnectionCallback()); } @@ -72,7 +72,7 @@ public class StoregateReadFeatureTest extends AbstractStoregateTest { writeStatus.setLength(content.length); final StoregateIdProvider nodeid = new StoregateIdProvider(session); final Path folder = new StoregateDirectoryFeature(session, nodeid).mkdir( - new Path(String.format("/My files/%s", new AlphanumericRandomStringService().random()), + new StoregateWriteFeature(session, nodeid), new Path(String.format("/My files/%s", new AlphanumericRandomStringService().random()), EnumSet.of(Path.Type.directory, Path.Type.volume)), new TransferStatus()); final Path test = new Path(folder, UUID.randomUUID().toString(), EnumSet.of(Path.Type.file)); final StoregateWriteFeature writer = new StoregateWriteFeature(session, nodeid); @@ -99,10 +99,10 @@ public class StoregateReadFeatureTest extends AbstractStoregateTest { public void testReadRange() throws Exception { final StoregateIdProvider nodeid = new StoregateIdProvider(session); final Path folder = new StoregateDirectoryFeature(session, nodeid).mkdir( - new Path(String.format("/My files/%s", new AlphanumericRandomStringService().random()), + new StoregateWriteFeature(session, nodeid), new Path(String.format("/My files/%s", new AlphanumericRandomStringService().random()), EnumSet.of(Path.Type.directory, Path.Type.volume)), new TransferStatus()); final Path test = new Path(folder, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)); - new StoregateTouchFeature(session, nodeid).touch(test, new TransferStatus()); + new StoregateTouchFeature(session, nodeid).touch(new StoregateWriteFeature(session, nodeid), test, new TransferStatus()); final Local local = new Local(System.getProperty("java.io.tmpdir"), new AlphanumericRandomStringService().random()); final byte[] content = RandomUtils.nextBytes(1023); final OutputStream out = local.getOutputStream(false); @@ -111,8 +111,8 @@ public class StoregateReadFeatureTest extends AbstractStoregateTest { out.close(); final TransferStatus upload = new TransferStatus().setLength(content.length); upload.setExists(true); - new DefaultUploadFeature<>(new StoregateWriteFeature(session, nodeid)).upload( - test, local, new BandwidthThrottle(BandwidthThrottle.UNLIMITED), new DisabledProgressListener(), new DisabledStreamListener(), upload, + new DefaultUploadFeature(session).upload( + new StoregateWriteFeature(session, nodeid), test, local, new BandwidthThrottle(BandwidthThrottle.UNLIMITED), new DisabledProgressListener(), new DisabledStreamListener(), upload, new DisabledConnectionCallback()); final TransferStatus status = new TransferStatus(); status.setLength(content.length); @@ -133,10 +133,10 @@ public class StoregateReadFeatureTest extends AbstractStoregateTest { public void testReadRangeUnknownLength() throws Exception { final StoregateIdProvider nodeid = new StoregateIdProvider(session); final Path room = new StoregateDirectoryFeature(session, nodeid).mkdir( - new Path(String.format("/My files/%s", new AlphanumericRandomStringService().random()), + new StoregateWriteFeature(session, nodeid), new Path(String.format("/My files/%s", new AlphanumericRandomStringService().random()), EnumSet.of(Path.Type.directory, Path.Type.volume)), new TransferStatus()); final Path test = new Path(room, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)); - new StoregateTouchFeature(session, nodeid).touch(test, new TransferStatus()); + new StoregateTouchFeature(session, nodeid).touch(new StoregateWriteFeature(session, nodeid), test, new TransferStatus()); final Local local = new Local(System.getProperty("java.io.tmpdir"), new AlphanumericRandomStringService().random()); final byte[] content = RandomUtils.nextBytes(1000); final OutputStream out = local.getOutputStream(false); @@ -145,8 +145,8 @@ public class StoregateReadFeatureTest extends AbstractStoregateTest { out.close(); final TransferStatus upload = new TransferStatus().setLength(content.length); upload.setExists(true); - new DefaultUploadFeature<>(new StoregateWriteFeature(session, nodeid)).upload( - test, local, new BandwidthThrottle(BandwidthThrottle.UNLIMITED), new DisabledProgressListener(), new DisabledStreamListener(), upload, + new DefaultUploadFeature(session).upload( + new StoregateWriteFeature(session, nodeid), test, local, new BandwidthThrottle(BandwidthThrottle.UNLIMITED), new DisabledProgressListener(), new DisabledStreamListener(), upload, new DisabledConnectionCallback()); final TransferStatus status = new TransferStatus(); status.setLength(-1L); @@ -171,7 +171,7 @@ public class StoregateReadFeatureTest extends AbstractStoregateTest { writeStatus.setLength(content.length); final StoregateIdProvider nodeid = new StoregateIdProvider(session); final Path room = new StoregateDirectoryFeature(session, nodeid).mkdir( - new Path(String.format("/My files/%s", new AlphanumericRandomStringService().random()), + new StoregateWriteFeature(session, nodeid), new Path(String.format("/My files/%s", new AlphanumericRandomStringService().random()), EnumSet.of(Path.Type.directory, Path.Type.volume)), new TransferStatus()); final Path test = new Path(room, UUID.randomUUID().toString(), EnumSet.of(Path.Type.file)); final StoregateWriteFeature writer = new StoregateWriteFeature(session, nodeid); diff --git a/storegate/src/test/java/ch/cyberduck/core/storegate/StoregateShareFeatureTest.java b/storegate/src/test/java/ch/cyberduck/core/storegate/StoregateShareFeatureTest.java index 7151950291..bb72308d64 100644 --- a/storegate/src/test/java/ch/cyberduck/core/storegate/StoregateShareFeatureTest.java +++ b/storegate/src/test/java/ch/cyberduck/core/storegate/StoregateShareFeatureTest.java @@ -39,11 +39,10 @@ public class StoregateShareFeatureTest extends AbstractStoregateTest { @Test public void toDownloadUrl() throws Exception { final StoregateIdProvider nodeid = new StoregateIdProvider(session); - final Path room = new StoregateDirectoryFeature(session, nodeid).mkdir( - new Path(String.format("/My files/%s", new AlphanumericRandomStringService().random()), + final Path room = new StoregateDirectoryFeature(session, nodeid).mkdir(new StoregateWriteFeature(session, nodeid), new Path(String.format("/My files/%s", new AlphanumericRandomStringService().random()), EnumSet.of(Path.Type.directory, Path.Type.volume)), new TransferStatus()); final Path test = new StoregateTouchFeature(session, nodeid).touch( - new Path(room, String.format("%s", new AlphanumericRandomStringService().random()), EnumSet.of(Path.Type.file)), new TransferStatus()); + new StoregateWriteFeature(session, nodeid), new Path(room, String.format("%s", new AlphanumericRandomStringService().random()), EnumSet.of(Path.Type.file)), new TransferStatus()); assertNotNull(new StoregateShareFeature(session, nodeid).toDownloadUrl(test, Share.Sharee.world, null, new DisabledPasswordCallback()).getUrl()); new StoregateDeleteFeature(session, nodeid).delete(Collections.singletonList(room), new DisabledPasswordCallback(), new Delete.DisabledCallback()); } @@ -52,7 +51,7 @@ public class StoregateShareFeatureTest extends AbstractStoregateTest { public void toUploadUrl() throws Exception { final StoregateIdProvider nodeid = new StoregateIdProvider(session); final Path room = new StoregateDirectoryFeature(session, nodeid).mkdir( - new Path(String.format("/My files/%s", new AlphanumericRandomStringService().random()), + new StoregateWriteFeature(session, nodeid), new Path(String.format("/My files/%s", new AlphanumericRandomStringService().random()), EnumSet.of(Path.Type.directory, Path.Type.volume)), new TransferStatus()); assertNotNull(new StoregateShareFeature(session, nodeid).toUploadUrl(room, Share.Sharee.world, null, new DisabledPasswordCallback()).getUrl()); new StoregateDeleteFeature(session, nodeid).delete(Collections.singletonList(room), new DisabledPasswordCallback(), new Delete.DisabledCallback()); diff --git a/storegate/src/test/java/ch/cyberduck/core/storegate/StoregateTimestampFeatureTest.java b/storegate/src/test/java/ch/cyberduck/core/storegate/StoregateTimestampFeatureTest.java index c1b8676f24..d3dcc03f2e 100644 --- a/storegate/src/test/java/ch/cyberduck/core/storegate/StoregateTimestampFeatureTest.java +++ b/storegate/src/test/java/ch/cyberduck/core/storegate/StoregateTimestampFeatureTest.java @@ -40,10 +40,10 @@ public class StoregateTimestampFeatureTest extends AbstractStoregateTest { @Test public void testSetTimestamp() throws Exception { final StoregateIdProvider nodeid = new StoregateIdProvider(session); - final Path room = new StoregateDirectoryFeature(session, nodeid).mkdir(new Path( + final Path room = new StoregateDirectoryFeature(session, nodeid).mkdir(new StoregateWriteFeature(session, nodeid), new Path( String.format("/My files/%s", new AlphanumericRandomStringService().random()), EnumSet.of(Path.Type.directory, Path.Type.volume)), new TransferStatus()); final Path file = new Path(room, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)); - new StoregateTouchFeature(session, nodeid).touch(file, new TransferStatus().setMime("x-application/cyberduck")); + new StoregateTouchFeature(session, nodeid).touch(new StoregateWriteFeature(session, nodeid), file, new TransferStatus().setMime("x-application/cyberduck")); assertNotNull(new StoregateAttributesFinderFeature(session, nodeid).find(file)); final long created = 1695161463630L; final long modified = Instant.now().minusSeconds(5 * 24 * 60 * 60).getEpochSecond() * 1000; @@ -63,10 +63,10 @@ public class StoregateTimestampFeatureTest extends AbstractStoregateTest { @Test public void testSetTimestampDirectory() throws Exception { final StoregateIdProvider nodeid = new StoregateIdProvider(session); - final Path room = new StoregateDirectoryFeature(session, nodeid).mkdir(new Path( + final Path room = new StoregateDirectoryFeature(session, nodeid).mkdir(new StoregateWriteFeature(session, nodeid), new Path( String.format("/My files/%s", new AlphanumericRandomStringService().random()), EnumSet.of(Path.Type.directory, Path.Type.volume)), new TransferStatus()); final Path test = new Path(room, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory)); - new StoregateDirectoryFeature(session, nodeid).mkdir(test, null); + new StoregateDirectoryFeature(session, nodeid).mkdir(new StoregateWriteFeature(session, nodeid), test, null); assertNotNull(new StoregateAttributesFinderFeature(session, nodeid).find(test)); final long modified = Instant.now().minusSeconds(5 * 24 * 60 * 60).getEpochSecond() * 1000; new StoregateTimestampFeature(session, nodeid).setTimestamp(test, modified); diff --git a/storegate/src/test/java/ch/cyberduck/core/storegate/StoregateWriteFeatureTest.java b/storegate/src/test/java/ch/cyberduck/core/storegate/StoregateWriteFeatureTest.java index a85f9f3e3d..9c3cbb1d16 100644 --- a/storegate/src/test/java/ch/cyberduck/core/storegate/StoregateWriteFeatureTest.java +++ b/storegate/src/test/java/ch/cyberduck/core/storegate/StoregateWriteFeatureTest.java @@ -53,7 +53,7 @@ public class StoregateWriteFeatureTest extends AbstractStoregateTest { public void testReadWrite() throws Exception { final StoregateIdProvider nodeid = new StoregateIdProvider(session); final Path room = new StoregateDirectoryFeature(session, nodeid).mkdir( - new Path(String.format("/My files/%s", new AlphanumericRandomStringService().random()), + new StoregateWriteFeature(session, nodeid), new Path(String.format("/My files/%s", new AlphanumericRandomStringService().random()), EnumSet.of(Path.Type.directory, Path.Type.volume)), new TransferStatus()); final long folderTimestamp = new StoregateAttributesFinderFeature(session, nodeid).find(room).getModificationDate(); final byte[] content = RandomUtils.nextBytes(32769); @@ -109,7 +109,7 @@ public class StoregateWriteFeatureTest extends AbstractStoregateTest { final StoregateIdProvider nodeid = new StoregateIdProvider(session); final StoregateWriteFeature feature = new StoregateWriteFeature(session, nodeid); final Path room = new StoregateDirectoryFeature(session, nodeid).mkdir( - new Path(String.format("/My files/%s", new AlphanumericRandomStringService().random()), + new StoregateWriteFeature(session, nodeid), new Path(String.format("/My files/%s", new AlphanumericRandomStringService().random()), EnumSet.of(Path.Type.directory, Path.Type.volume)), new TransferStatus()); final byte[] content = RandomUtils.nextBytes(1); final TransferStatus status = new TransferStatus(); @@ -134,11 +134,11 @@ public class StoregateWriteFeatureTest extends AbstractStoregateTest { public void testWriteWithLock() throws Exception { final StoregateIdProvider nodeid = new StoregateIdProvider(session); final Path room = new StoregateDirectoryFeature(session, nodeid).mkdir( - new Path(String.format("/My files/%s", new AlphanumericRandomStringService().random()), + new StoregateWriteFeature(session, nodeid), new Path(String.format("/My files/%s", new AlphanumericRandomStringService().random()), EnumSet.of(Path.Type.directory, Path.Type.volume)), new TransferStatus()); final byte[] content = RandomUtils.nextBytes(32769); final Path test = new StoregateTouchFeature(session, nodeid).touch( - new Path(room, String.format("%s", new AlphanumericRandomStringService().random()), EnumSet.of(Path.Type.file)), new TransferStatus()); + new StoregateWriteFeature(session, nodeid), new Path(room, String.format("%s", new AlphanumericRandomStringService().random()), EnumSet.of(Path.Type.file)), new TransferStatus()); final String lockId = new StoregateLockFeature(session, nodeid).lock(test); final TransferStatus status = new TransferStatus(); status.setLength(content.length); @@ -164,11 +164,11 @@ public class StoregateWriteFeatureTest extends AbstractStoregateTest { public void testWriteWithLockAlreadyReleased() throws Exception { final StoregateIdProvider nodeid = new StoregateIdProvider(session); final Path room = new StoregateDirectoryFeature(session, nodeid).mkdir( - new Path(String.format("/My files/%s", new AlphanumericRandomStringService().random()), + new StoregateWriteFeature(session, nodeid), new Path(String.format("/My files/%s", new AlphanumericRandomStringService().random()), EnumSet.of(Path.Type.directory, Path.Type.volume)), new TransferStatus()); final byte[] content = RandomUtils.nextBytes(32769); final Path test = new StoregateTouchFeature(session, nodeid).touch( - new Path(room, String.format("%s", new AlphanumericRandomStringService().random()), EnumSet.of(Path.Type.file)), new TransferStatus()); + new StoregateWriteFeature(session, nodeid), new Path(room, String.format("%s", new AlphanumericRandomStringService().random()), EnumSet.of(Path.Type.file)), new TransferStatus()); final TransferStatus status = new TransferStatus(); status.setLength(content.length); final String lockId = new StoregateLockFeature(session, nodeid).lock(test); @@ -186,7 +186,7 @@ public class StoregateWriteFeatureTest extends AbstractStoregateTest { public void testWriteCancel() throws Exception { final StoregateIdProvider nodeid = new StoregateIdProvider(session); final Path room = new StoregateDirectoryFeature(session, nodeid).mkdir( - new Path(String.format("/My files/%s", new AlphanumericRandomStringService().random()), + new StoregateWriteFeature(session, nodeid), new Path(String.format("/My files/%s", new AlphanumericRandomStringService().random()), EnumSet.of(Path.Type.directory, Path.Type.volume)), new TransferStatus()); final byte[] content = RandomUtils.nextBytes(32769); diff --git a/test/pom.xml b/test/pom.xml index 592bd7837c..d3589016d3 100644 --- a/test/pom.xml +++ b/test/pom.xml @@ -18,7 +18,7 @@ ch.cyberduck parent - 9.2.0-SNAPSHOT + 9.4.0-SNAPSHOT 4.0.0 pom diff --git a/tus/pom.xml b/tus/pom.xml index 6e432cf185..401c24fab2 100644 --- a/tus/pom.xml +++ b/tus/pom.xml @@ -18,7 +18,7 @@ ch.cyberduck parent - 9.2.0-SNAPSHOT + 9.4.0-SNAPSHOT tus jar @@ -29,6 +29,11 @@ core ${project.version} + + ch.cyberduck + webdav + ${project.version} + ch.cyberduck test diff --git a/tus/src/main/java/ch/cyberduck/core/tus/TusUploadFeature.java b/tus/src/main/java/ch/cyberduck/core/tus/TusUploadFeature.java index d1c15f6e44..b4f1485602 100644 --- a/tus/src/main/java/ch/cyberduck/core/tus/TusUploadFeature.java +++ b/tus/src/main/java/ch/cyberduck/core/tus/TusUploadFeature.java @@ -24,10 +24,10 @@ import ch.cyberduck.core.Path; import ch.cyberduck.core.ProgressListener; import ch.cyberduck.core.StringAppender; import ch.cyberduck.core.concurrency.Interruptibles; +import ch.cyberduck.core.dav.DAVClient; import ch.cyberduck.core.exception.BackgroundException; import ch.cyberduck.core.exception.InteroperabilityException; import ch.cyberduck.core.exception.NotfoundException; -import ch.cyberduck.core.features.Upload; import ch.cyberduck.core.features.Write; import ch.cyberduck.core.http.DefaultHttpResponseExceptionMappingService; import ch.cyberduck.core.http.HttpUploadFeature; @@ -48,7 +48,6 @@ import org.apache.http.Header; import org.apache.http.HttpHeaders; import org.apache.http.HttpResponse; import org.apache.http.HttpStatus; -import org.apache.http.client.HttpClient; import org.apache.http.client.HttpResponseException; import org.apache.http.client.ResponseHandler; import org.apache.http.client.methods.HttpHead; @@ -74,22 +73,19 @@ public class TusUploadFeature extends HttpUploadFeature { public static final String UPLOAD_URL = "uploadUrl"; private final Host host; - private final HttpClient client; + private final DAVClient client; private final Preferences preferences = PreferencesFactory.get(); - private Write writer; private final TusCapabilities capabilities; - public TusUploadFeature(final Host host, final HttpClient client, final Write writer, final TusCapabilities capabilities) { - super(writer); + public TusUploadFeature(final Host host, final DAVClient client, final TusCapabilities capabilities) { this.host = host; this.client = client; - this.writer = writer; this.capabilities = capabilities; } @Override - public Void upload(final Path file, final Local local, final BandwidthThrottle throttle, final ProgressListener progress, final StreamListener streamListener, + public Void upload(final Write write, final Path file, final Local local, final BandwidthThrottle throttle, final ProgressListener progress, final StreamListener streamListener, final TransferStatus status, final ConnectionCallback callback) throws BackgroundException { // In order to achieve parallel upload the Concatenation extension MAY be used. try { @@ -143,7 +139,7 @@ public class TusUploadFeature extends HttpUploadFeature { } while(remaining > 0) { final long length = Math.min(preferences.getInteger("tus.chunk.size"), remaining); - chunks.add(this.submit(file, local, throttle, streamListener, status, + chunks.add(this.submit(write, file, local, throttle, streamListener, status, uploadUrl, offset, length, callback)); remaining -= length; offset += length; @@ -163,7 +159,7 @@ public class TusUploadFeature extends HttpUploadFeature { } } - private Future submit(final Path file, final Local local, + private Future submit(final Write write, final Path file, final Local local, final BandwidthThrottle throttle, final StreamListener listener, final TransferStatus overall, final String uploadUrl, final long offset, final long length, final ConnectionCallback callback) throws BackgroundException { @@ -178,12 +174,12 @@ public class TusUploadFeature extends HttpUploadFeature { .setOffset(offset) .setLength(length); status.setHeader(overall.getHeader()); - status.setChecksum(writer.checksum(file, status).compute(local.getInputStream(), status)); + status.setChecksum(write.checksum(file, status).compute(local.getInputStream(), status)); final Map parameters = new HashMap<>(); parameters.put(UPLOAD_URL, uploadUrl); status.setParameters(parameters); final Void response = TusUploadFeature.this.upload( - file, local, throttle, listener, status, overall, status, callback); + write, file, local, throttle, listener, status, overall, status, callback); log.info("Received response {}", response); return null; } @@ -239,9 +235,4 @@ public class TusUploadFeature extends HttpUploadFeature { null == status.getChecksum() ? StringUtils.EMPTY : String.format(":%s", status.getChecksum().base64)); } - @Override - public Upload withWriter(final Write writer) { - this.writer = writer; - return super.withWriter(writer); - } } diff --git a/tus/src/main/java/ch/cyberduck/core/tus/TusWriteFeature.java b/tus/src/main/java/ch/cyberduck/core/tus/TusWriteFeature.java index 53d87482e9..89d7174337 100644 --- a/tus/src/main/java/ch/cyberduck/core/tus/TusWriteFeature.java +++ b/tus/src/main/java/ch/cyberduck/core/tus/TusWriteFeature.java @@ -19,6 +19,7 @@ import ch.cyberduck.core.ConnectionCallback; import ch.cyberduck.core.DefaultIOExceptionMappingService; import ch.cyberduck.core.Path; import ch.cyberduck.core.VoidAttributesAdapter; +import ch.cyberduck.core.dav.DAVClient; import ch.cyberduck.core.exception.BackgroundException; import ch.cyberduck.core.http.AbstractHttpWriteFeature; import ch.cyberduck.core.http.DefaultHttpResponseExceptionMappingService; @@ -34,7 +35,6 @@ import org.apache.http.HttpEntity; import org.apache.http.HttpHeaders; import org.apache.http.HttpResponse; import org.apache.http.HttpStatus; -import org.apache.http.client.HttpClient; import org.apache.http.client.HttpResponseException; import org.apache.http.client.ResponseHandler; import org.apache.http.client.methods.HttpPatch; @@ -50,9 +50,9 @@ public class TusWriteFeature extends AbstractHttpWriteFeature { private static final Logger log = LogManager.getLogger(TusWriteFeature.class); private final TusCapabilities capabilities; - private final HttpClient client; + private final DAVClient client; - public TusWriteFeature(final TusCapabilities capabilities, final HttpClient client) { + public TusWriteFeature(final TusCapabilities capabilities, final DAVClient client) { super(new VoidAttributesAdapter()); this.capabilities = capabilities; this.client = client; diff --git a/webdav/pom.xml b/webdav/pom.xml index b2280e1bd4..75b88ab70f 100644 --- a/webdav/pom.xml +++ b/webdav/pom.xml @@ -18,14 +18,14 @@ ch.cyberduck parent - 9.2.0-SNAPSHOT + 9.4.0-SNAPSHOT webdav jar 5.14.1 - 4.0.5.2421 + 4.0.5.2500 diff --git a/webdav/src/main/java/ch/cyberduck/core/dav/DAVAttributesFinderFeature.java b/webdav/src/main/java/ch/cyberduck/core/dav/DAVAttributesFinderFeature.java index ffe1f68be7..a2fd3c6360 100644 --- a/webdav/src/main/java/ch/cyberduck/core/dav/DAVAttributesFinderFeature.java +++ b/webdav/src/main/java/ch/cyberduck/core/dav/DAVAttributesFinderFeature.java @@ -15,6 +15,7 @@ package ch.cyberduck.core.dav; * GNU General Public License for more details. */ +import ch.cyberduck.core.DefaultPathAttributes; import ch.cyberduck.core.ListProgressListener; import ch.cyberduck.core.Path; import ch.cyberduck.core.PathAttributes; @@ -110,7 +111,7 @@ public class DAVAttributesFinderFeature implements AttributesFinder, AttributesA protected PathAttributes head(final Path file) throws IOException { final Map headers = session.getClient().execute( new HttpHead(new DAVPathEncoder().encode(file)), new HeadersResponseHandler()); - final PathAttributes attributes = new PathAttributes(); + final PathAttributes attributes = new DefaultPathAttributes(); try { attributes.setModificationDate(rfc1123.parse(headers.get(HttpHeaders.LAST_MODIFIED)).getTime()); } @@ -143,7 +144,7 @@ public class DAVAttributesFinderFeature implements AttributesFinder, AttributesA @Override public PathAttributes toAttributes(final DavResource resource) { - final PathAttributes attributes = new PathAttributes(); + final PathAttributes attributes = new DefaultPathAttributes(); final Map properties = resource.getCustomPropsNS(); if(null != properties && properties.containsKey(DAVTimestampFeature.LAST_MODIFIED_CUSTOM_NAMESPACE)) { final String value = properties.get(DAVTimestampFeature.LAST_MODIFIED_CUSTOM_NAMESPACE); diff --git a/webdav/src/main/java/ch/cyberduck/core/dav/DAVCopyFeature.java b/webdav/src/main/java/ch/cyberduck/core/dav/DAVCopyFeature.java index 516bb71e44..951199ca75 100644 --- a/webdav/src/main/java/ch/cyberduck/core/dav/DAVCopyFeature.java +++ b/webdav/src/main/java/ch/cyberduck/core/dav/DAVCopyFeature.java @@ -18,6 +18,7 @@ package ch.cyberduck.core.dav; */ import ch.cyberduck.core.ConnectionCallback; +import ch.cyberduck.core.DefaultPathAttributes; import ch.cyberduck.core.DescriptiveUrl; import ch.cyberduck.core.Path; import ch.cyberduck.core.PathAttributes; @@ -59,7 +60,7 @@ public class DAVCopyFeature implements Copy { session.getClient().copy(new DAVPathEncoder().encode(source), target, status.isExists()); } listener.sent(status.getLength()); - return new Path(copy).withAttributes(new PathAttributes(source.attributes()).setLockId(null)); + return new Path(copy).withAttributes(new DefaultPathAttributes(source.attributes()).setLockId(null)); } catch(SardineException e) { throw new DAVExceptionMappingService().map("Cannot copy {0}", e, source); diff --git a/webdav/src/main/java/ch/cyberduck/core/dav/DAVDeleteFeature.java b/webdav/src/main/java/ch/cyberduck/core/dav/DAVDeleteFeature.java index 91daad6ab0..8d5c83440e 100644 --- a/webdav/src/main/java/ch/cyberduck/core/dav/DAVDeleteFeature.java +++ b/webdav/src/main/java/ch/cyberduck/core/dav/DAVDeleteFeature.java @@ -87,7 +87,7 @@ public class DAVDeleteFeature implements Delete { } @Override - public EnumSet features() { + public EnumSet features(final Path file) { return EnumSet.of(Flags.recursive); } } diff --git a/webdav/src/main/java/ch/cyberduck/core/dav/DAVDirectoryFeature.java b/webdav/src/main/java/ch/cyberduck/core/dav/DAVDirectoryFeature.java index b6049237d6..baa938adcf 100644 --- a/webdav/src/main/java/ch/cyberduck/core/dav/DAVDirectoryFeature.java +++ b/webdav/src/main/java/ch/cyberduck/core/dav/DAVDirectoryFeature.java @@ -19,8 +19,8 @@ package ch.cyberduck.core.dav; import ch.cyberduck.core.Path; import ch.cyberduck.core.exception.BackgroundException; -import ch.cyberduck.core.features.AttributesFinder; import ch.cyberduck.core.features.Directory; +import ch.cyberduck.core.features.Write; import ch.cyberduck.core.http.HttpExceptionMappingService; import ch.cyberduck.core.transfer.TransferStatus; @@ -28,20 +28,16 @@ import java.io.IOException; import com.github.sardine.impl.SardineException; -public class DAVDirectoryFeature implements Directory { +public class DAVDirectoryFeature implements Directory { private final DAVSession session; public DAVDirectoryFeature(final DAVSession session) { - this(session, new DAVAttributesFinderFeature(session)); - } - - public DAVDirectoryFeature(final DAVSession session, final AttributesFinder attributes) { this.session = session; } @Override - public Path mkdir(final Path folder, final TransferStatus status) throws BackgroundException { + public Path mkdir(final Write writer, final Path folder, final TransferStatus status) throws BackgroundException { try { session.getClient().createDirectory(new DAVPathEncoder().encode(folder)); } diff --git a/webdav/src/main/java/ch/cyberduck/core/dav/DAVMetadataFeature.java b/webdav/src/main/java/ch/cyberduck/core/dav/DAVMetadataFeature.java index fa65fa9a81..4426d8fac4 100644 --- a/webdav/src/main/java/ch/cyberduck/core/dav/DAVMetadataFeature.java +++ b/webdav/src/main/java/ch/cyberduck/core/dav/DAVMetadataFeature.java @@ -54,7 +54,7 @@ public class DAVMetadataFeature implements Headers { } @Override - public Map getDefault() { + public Map getDefault(final Path file) { return HostPreferencesFactory.get(session.getHost()).getMap("webdav.metadata.default"); } diff --git a/webdav/src/main/java/ch/cyberduck/core/dav/DAVMoveFeature.java b/webdav/src/main/java/ch/cyberduck/core/dav/DAVMoveFeature.java index cefe7550d1..5a3f5a610a 100644 --- a/webdav/src/main/java/ch/cyberduck/core/dav/DAVMoveFeature.java +++ b/webdav/src/main/java/ch/cyberduck/core/dav/DAVMoveFeature.java @@ -19,6 +19,7 @@ package ch.cyberduck.core.dav; */ import ch.cyberduck.core.ConnectionCallback; +import ch.cyberduck.core.DefaultPathAttributes; import ch.cyberduck.core.DescriptiveUrl; import ch.cyberduck.core.Path; import ch.cyberduck.core.PathAttributes; @@ -62,7 +63,7 @@ public class DAVMoveFeature implements Move { status.isExists()); } // Copy original file attributes - return new Path(renamed).withAttributes(new PathAttributes(file.attributes()).setLockId(null)); + return new Path(renamed).withAttributes(new DefaultPathAttributes(file.attributes()).setVault(null).setLockId(null)); } catch(SardineException e) { throw new DAVExceptionMappingService().map("Cannot rename {0}", e, file); diff --git a/webdav/src/main/java/ch/cyberduck/core/dav/DAVRedirectStrategy.java b/webdav/src/main/java/ch/cyberduck/core/dav/DAVRedirectStrategy.java index 3edd66ecf1..82528346cc 100644 --- a/webdav/src/main/java/ch/cyberduck/core/dav/DAVRedirectStrategy.java +++ b/webdav/src/main/java/ch/cyberduck/core/dav/DAVRedirectStrategy.java @@ -17,7 +17,7 @@ package ch.cyberduck.core.dav; import ch.cyberduck.core.http.RedirectCallback; -import org.apache.commons.lang3.StringUtils; +import org.apache.commons.lang3.RegExUtils; import org.apache.http.HttpEntityEnclosingRequest; import org.apache.http.HttpRequest; import org.apache.http.HttpResponse; @@ -54,7 +54,7 @@ public class DAVRedirectStrategy extends SardineRedirectStrategy { @Override protected URI createLocationURI(final String location) throws ProtocolException { - return super.createLocationURI(StringUtils.replaceAll(location, " ", "%20")); + return super.createLocationURI(RegExUtils.replaceAll(location, " ", "%20")); } private HttpUriRequest copyEntity(final HttpEntityEnclosingRequestBase redirect, final HttpRequest original) { diff --git a/webdav/src/main/java/ch/cyberduck/core/dav/DAVSession.java b/webdav/src/main/java/ch/cyberduck/core/dav/DAVSession.java index 481e8b511b..52bb00bd72 100644 --- a/webdav/src/main/java/ch/cyberduck/core/dav/DAVSession.java +++ b/webdav/src/main/java/ch/cyberduck/core/dav/DAVSession.java @@ -44,7 +44,6 @@ import ch.cyberduck.core.exception.ListCanceledException; import ch.cyberduck.core.exception.LoginCanceledException; import ch.cyberduck.core.features.*; import ch.cyberduck.core.http.CustomServiceUnavailableRetryStrategy; -import ch.cyberduck.core.http.ExecutionCountServiceUnavailableRetryStrategy; import ch.cyberduck.core.http.HttpExceptionMappingService; import ch.cyberduck.core.http.HttpSession; import ch.cyberduck.core.http.PreferencesRedirectCallback; @@ -52,7 +51,6 @@ import ch.cyberduck.core.http.RedirectCallback; import ch.cyberduck.core.oauth.OAuth2AuthorizationService; import ch.cyberduck.core.oauth.OAuth2ErrorResponseInterceptor; import ch.cyberduck.core.oauth.OAuth2RequestInterceptor; -import ch.cyberduck.core.preferences.HostPreferencesFactory; import ch.cyberduck.core.preferences.PreferencesReader; import ch.cyberduck.core.proxy.ProxyFinder; import ch.cyberduck.core.shared.DefaultPathHomeFeature; @@ -91,7 +89,6 @@ public class DAVSession extends HttpSession { private static final Logger log = LogManager.getLogger(DAVSession.class); private final RedirectCallback redirect; - private final PreferencesReader preferences = HostPreferencesFactory.get(host); private final HttpCapabilities capabilities = new HttpCapabilities(preferences); private OAuth2RequestInterceptor authorizationService; @@ -121,7 +118,7 @@ public class DAVSession extends HttpSession { } configuration.addInterceptorLast(authorizationService); configuration.setServiceUnavailableRetryStrategy(new CustomServiceUnavailableRetryStrategy(host, - new ExecutionCountServiceUnavailableRetryStrategy(new OAuth2ErrorResponseInterceptor(host, authorizationService)))); + new OAuth2ErrorResponseInterceptor(host, authorizationService))); } configuration.setRedirectStrategy(new DAVRedirectStrategy(redirect)); configuration.addInterceptorLast(new MicrosoftIISPersistentAuthResponseInterceptor()); @@ -129,22 +126,23 @@ public class DAVSession extends HttpSession { } @Override - protected void logout() throws BackgroundException { + public void disconnect() throws BackgroundException { try { client.shutdown(); } catch(IOException e) { throw new HttpExceptionMappingService().map(e); } + super.disconnect(); } @Override public void login(final LoginCallback prompt, final CancelCallback cancel) throws BackgroundException { + final Credentials credentials = host.getCredentials(); if(host.getProtocol().isOAuthConfigurable()) { - authorizationService.validate(); + credentials.setOauth(authorizationService.validate(credentials.getOauth())); } if(host.getProtocol().isPasswordConfigurable()) { - final Credentials credentials = host.getCredentials(); final String domain, username; if(credentials.getUsername().contains("\\")) { domain = StringUtils.substringBefore(credentials.getUsername(), "\\"); @@ -152,7 +150,7 @@ public class DAVSession extends HttpSession { } else { username = credentials.getUsername(); - domain = HostPreferencesFactory.get(host).getProperty("webdav.ntlm.domain"); + domain = preferences.getProperty("webdav.ntlm.domain"); } for(String scheme : Arrays.asList(AuthSchemes.NTLM, AuthSchemes.SPNEGO)) { client.setCredentials( @@ -308,10 +306,10 @@ public class DAVSession extends HttpSession { return (T) new DAVReadFeature(this); } if(type == Write.class) { - return (T) new DAVWriteFeature(this, capabilities.expectcontinue); + return (T) new DAVWriteFeature(this, capabilities); } if(type == Upload.class) { - return (T) new DAVUploadFeature(new DAVWriteFeature(this, capabilities.expectcontinue)); + return (T) new DAVUploadFeature(this, capabilities); } if(type == Delete.class) { return (T) new DAVDeleteFeature(this); diff --git a/webdav/src/main/java/ch/cyberduck/core/dav/DAVTimestampFeature.java b/webdav/src/main/java/ch/cyberduck/core/dav/DAVTimestampFeature.java index c913643b6e..725707e113 100644 --- a/webdav/src/main/java/ch/cyberduck/core/dav/DAVTimestampFeature.java +++ b/webdav/src/main/java/ch/cyberduck/core/dav/DAVTimestampFeature.java @@ -44,6 +44,7 @@ import com.github.sardine.util.SardineUtil; public class DAVTimestampFeature implements Timestamp { private final DAVSession session; + private final DAVAttributesFinderFeature attributes; public static final QName LAST_MODIFIED_DEFAULT_NAMESPACE = SardineUtil.createQNameWithDefaultNamespace("lastmodified"); @@ -62,7 +63,12 @@ public class DAVTimestampFeature implements Timestamp { SardineUtil.createQNameWithCustomNamespace("lastmodified_server"); public DAVTimestampFeature(final DAVSession session) { + this(session, new DAVAttributesFinderFeature(session)); + } + + public DAVTimestampFeature(final DAVSession session, final DAVAttributesFinderFeature attributes) { this.session = session; + this.attributes = attributes; } @Override @@ -73,7 +79,7 @@ public class DAVTimestampFeature implements Timestamp { final DavResource resource = this.getResource(file); session.getClient().patch(new DAVPathEncoder().encode(file), this.getCustomProperties(resource, status.getModified()), Collections.emptyList(), this.getCustomHeaders(file, status)); - status.setResponse(new DAVAttributesFinderFeature(session).toAttributes(resource).setModificationDate( + status.setResponse(attributes.toAttributes(resource).setModificationDate( Timestamp.toSeconds(status.getModified()))); } } @@ -92,7 +98,7 @@ public class DAVTimestampFeature implements Timestamp { * @return Latest properties */ protected DavResource getResource(final Path file) throws BackgroundException, IOException { - final Optional optional = new DAVAttributesFinderFeature(session).list(file).stream().findFirst(); + final Optional optional = attributes.list(file).stream().findFirst(); if(!optional.isPresent()) { throw new NotfoundException(file.getAbsolute()); } diff --git a/webdav/src/main/java/ch/cyberduck/core/dav/DAVTouchFeature.java b/webdav/src/main/java/ch/cyberduck/core/dav/DAVTouchFeature.java index 1027cce85b..19587df737 100644 --- a/webdav/src/main/java/ch/cyberduck/core/dav/DAVTouchFeature.java +++ b/webdav/src/main/java/ch/cyberduck/core/dav/DAVTouchFeature.java @@ -15,16 +15,11 @@ package ch.cyberduck.core.dav; * GNU General Public License for more details. */ -import ch.cyberduck.core.features.Write; import ch.cyberduck.core.shared.DefaultTouchFeature; public class DAVTouchFeature extends DefaultTouchFeature { public DAVTouchFeature(final DAVSession session) { - super(new DAVWriteFeature(session)); - } - - public DAVTouchFeature(final Write writer) { - super(writer); + super(session); } } diff --git a/webdav/src/main/java/ch/cyberduck/core/dav/DAVUploadFeature.java b/webdav/src/main/java/ch/cyberduck/core/dav/DAVUploadFeature.java index a5c479dbf6..1368d6adf7 100644 --- a/webdav/src/main/java/ch/cyberduck/core/dav/DAVUploadFeature.java +++ b/webdav/src/main/java/ch/cyberduck/core/dav/DAVUploadFeature.java @@ -23,16 +23,22 @@ import ch.cyberduck.core.features.Write; import ch.cyberduck.core.http.HttpUploadFeature; import ch.cyberduck.core.transfer.TransferStatus; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; + import java.security.MessageDigest; public class DAVUploadFeature extends HttpUploadFeature { + private static final Logger log = LogManager.getLogger(DAVUploadFeature.class); + + private final DAVSession.HttpCapabilities capabilities; public DAVUploadFeature(final DAVSession session) { - super(new DAVWriteFeature(session)); + this(session, new DAVSession.HttpCapabilities(session.getHost())); } - public DAVUploadFeature(final Write writer) { - super(writer); + public DAVUploadFeature(final DAVSession session, final DAVSession.HttpCapabilities capabilities) { + this.capabilities = capabilities; } @Override @@ -40,6 +46,11 @@ public class DAVUploadFeature extends HttpUploadFeature { if(status.getLength() == TransferStatus.UNKNOWN_LENGTH) { return new Write.Append(false).withStatus(status); } - return new Write.Append(status.isExists()).withStatus(status); + if(capabilities.iis) { + return new Write.Append(false).withStatus(status); + } + final Write.Append append = new Write.Append(status.isExists()).withStatus(status); + log.debug("Determined append {} for file {} with status {}", append, file, status); + return append; } } diff --git a/webdav/src/main/java/ch/cyberduck/core/dav/DAVWindowsIntegratedCredentialsConfigurator.java b/webdav/src/main/java/ch/cyberduck/core/dav/DAVWindowsIntegratedCredentialsConfigurator.java index 8b7ec1b7d9..6ba650fc2d 100644 --- a/webdav/src/main/java/ch/cyberduck/core/dav/DAVWindowsIntegratedCredentialsConfigurator.java +++ b/webdav/src/main/java/ch/cyberduck/core/dav/DAVWindowsIntegratedCredentialsConfigurator.java @@ -34,7 +34,7 @@ public class DAVWindowsIntegratedCredentialsConfigurator implements CredentialsC if(HostPreferencesFactory.get(host).getBoolean("webdav.ntlm.windows.authentication.enable")) { return proxy.configure(host); } - return CredentialsConfigurator.DISABLED.configure(host); + return host.getCredentials(); } @Override diff --git a/webdav/src/main/java/ch/cyberduck/core/dav/DAVWriteFeature.java b/webdav/src/main/java/ch/cyberduck/core/dav/DAVWriteFeature.java index d8c95808c0..0ba8800b3b 100644 --- a/webdav/src/main/java/ch/cyberduck/core/dav/DAVWriteFeature.java +++ b/webdav/src/main/java/ch/cyberduck/core/dav/DAVWriteFeature.java @@ -31,7 +31,6 @@ import ch.cyberduck.core.http.DelayedHttpEntityCallable; import ch.cyberduck.core.http.HttpExceptionMappingService; import ch.cyberduck.core.http.HttpRange; import ch.cyberduck.core.http.HttpResponseOutputStream; -import ch.cyberduck.core.preferences.HostPreferencesFactory; import ch.cyberduck.core.transfer.TransferStatus; import org.apache.http.Header; @@ -58,35 +57,35 @@ public class DAVWriteFeature extends AbstractHttpWriteFeature implements W /** * Use Expect directive */ - private final boolean expect; + private final DAVSession.HttpCapabilities capabilities; public DAVWriteFeature(final DAVSession session) { - this(session, HostPreferencesFactory.get(session.getHost()).getBoolean("webdav.expect-continue")); + this(session, new DAVSession.HttpCapabilities(session.getHost())); } - public DAVWriteFeature(final DAVSession session, final boolean expect) { + public DAVWriteFeature(final DAVSession session, final DAVSession.HttpCapabilities capabilities) { super(new VoidAttributesAdapter()); this.session = session; - this.expect = expect; + this.capabilities = capabilities; } @Override public HttpResponseOutputStream write(final Path file, final TransferStatus status, final ConnectionCallback callback) throws BackgroundException { try { - return this.write(file, this.toHeaders(file, status, expect), status); + return this.write(file, this.toHeaders(file, status, capabilities.expectcontinue), status); } catch(ConflictException e) { - if(expect) { + if(capabilities.expectcontinue) { if(null != status.getLockId()) { // Handle 412 Precondition Failed with expired token log.warn("Retry failure {} with lock id {} removed", e, status.getLockId()); - return this.write(file, this.toHeaders(file, status.setLockId(null), expect), status); + return this.write(file, this.toHeaders(file, status.setLockId(null), capabilities.expectcontinue), status); } } throw e; } catch(InteroperabilityException e) { - if(expect) { + if(capabilities.expectcontinue) { // Handle 417 Expectation Failed log.warn("Retry failure {} with Expect: Continue removed", e.getMessage()); return this.write(file, this.toHeaders(file, status.setLockId(null), false), status); @@ -153,6 +152,9 @@ public class DAVWriteFeature extends AbstractHttpWriteFeature implements W @Override public EnumSet features(final Path file) { + if(capabilities.iis) { + return EnumSet.noneOf(Flags.class); + } return EnumSet.of(Flags.random); } } diff --git a/webdav/src/main/java/ch/cyberduck/core/dav/microsoft/MicrosoftIISDAVTimestampFeature.java b/webdav/src/main/java/ch/cyberduck/core/dav/microsoft/MicrosoftIISDAVTimestampFeature.java index e25e9b7236..2af00cc03d 100644 --- a/webdav/src/main/java/ch/cyberduck/core/dav/microsoft/MicrosoftIISDAVTimestampFeature.java +++ b/webdav/src/main/java/ch/cyberduck/core/dav/microsoft/MicrosoftIISDAVTimestampFeature.java @@ -15,19 +15,15 @@ package ch.cyberduck.core.dav.microsoft; * GNU General Public License for more details. */ -import ch.cyberduck.core.Path; import ch.cyberduck.core.date.RFC1123DateFormatter; import ch.cyberduck.core.dav.DAVSession; import ch.cyberduck.core.dav.DAVTimestampFeature; -import ch.cyberduck.core.exception.NotfoundException; import org.w3c.dom.Element; import javax.xml.namespace.QName; -import java.io.IOException; import java.util.ArrayList; import java.util.List; -import java.util.Optional; import java.util.TimeZone; import com.github.sardine.DavResource; @@ -41,20 +37,8 @@ public class MicrosoftIISDAVTimestampFeature extends DAVTimestampFeature { public static final QName LAST_MODIFIED_WIN32_CUSTOM_NAMESPACE = new QName(MS_NAMESPACE_URI, MS_NAMESPACE_LASTMODIFIED, MS_NAMESPACE_PREFIX); - private final DAVSession session; - public MicrosoftIISDAVTimestampFeature(final DAVSession session) { - super(session); - this.session = session; - } - - @Override - protected DavResource getResource(final Path file) throws NotfoundException, IOException { - final Optional optional = new MicrosoftIISDAVAttributesFinderFeature(session).list(file).stream().findFirst(); - if(!optional.isPresent()) { - throw new NotfoundException(file.getAbsolute()); - } - return optional.get(); + super(session, new MicrosoftIISDAVAttributesFinderFeature(session)); } protected List getCustomProperties(final DavResource resource, final Long modified) { diff --git a/webdav/src/test/java/ch/cyberduck/core/cryptomator/CopyWorkerTest.java b/webdav/src/test/java/ch/cyberduck/core/cryptomator/CopyWorkerTest.java index 229a7c2c01..20abda9c91 100644 --- a/webdav/src/test/java/ch/cyberduck/core/cryptomator/CopyWorkerTest.java +++ b/webdav/src/test/java/ch/cyberduck/core/cryptomator/CopyWorkerTest.java @@ -27,13 +27,13 @@ import ch.cyberduck.core.cryptomator.features.CryptoReadFeature; import ch.cyberduck.core.cryptomator.features.CryptoTouchFeature; import ch.cyberduck.core.cryptomator.features.CryptoWriteFeature; import ch.cyberduck.core.dav.AbstractDAVTest; -import ch.cyberduck.core.dav.DAVDeleteFeature; import ch.cyberduck.core.dav.DAVDirectoryFeature; import ch.cyberduck.core.dav.DAVFindFeature; import ch.cyberduck.core.dav.DAVReadFeature; import ch.cyberduck.core.dav.DAVWriteFeature; import ch.cyberduck.core.features.Directory; import ch.cyberduck.core.features.Find; +import ch.cyberduck.core.features.Write; import ch.cyberduck.core.io.StreamCopier; import ch.cyberduck.core.pool.SessionPool; import ch.cyberduck.core.shared.DefaultFindFeature; @@ -81,7 +81,7 @@ public class CopyWorkerTest extends AbstractDAVTest { session.withRegistry(registry); final byte[] content = RandomUtils.nextBytes(40500); final TransferStatus status = new TransferStatus(); - new CryptoBulkFeature<>(session, new DisabledBulkFeature(), new DAVDeleteFeature(session), cryptomator).pre(Transfer.Type.upload, Collections.singletonMap(new TransferItem(source), status), new DisabledConnectionCallback()); + new CryptoBulkFeature<>(session, new DisabledBulkFeature(), 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 DAVWriteFeature(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()); @@ -105,10 +105,11 @@ public class CopyWorkerTest extends AbstractDAVTest { cryptomator.create(session, new VaultCredentials("test"), vaultVersion); final DefaultVaultRegistry registry = new DefaultVaultRegistry(new DisabledPasswordCallback(), cryptomator); session.withRegistry(registry); - new CryptoTouchFeature<>(session, new DefaultTouchFeature<>(new DAVWriteFeature(session) - ), new DAVWriteFeature(session), cryptomator).touch(source, new TransferStatus()); + new CryptoTouchFeature<>(session, new DefaultTouchFeature( + session), cryptomator).touch(new CryptoWriteFeature<>(session, new DAVWriteFeature(session), cryptomator), source, new TransferStatus()); assertTrue(cryptomator.getFeature(session, Find.class, new DefaultFindFeature(session)).find(source)); - cryptomator.getFeature(session, Directory.class, new DAVDirectoryFeature(session)).mkdir(targetFolder, new TransferStatus()); + cryptomator.getFeature(session, Directory.class, new DAVDirectoryFeature(session)).mkdir( + cryptomator.getFeature(session, Write.class, new DAVWriteFeature(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); @@ -129,10 +130,11 @@ public class CopyWorkerTest extends AbstractDAVTest { cryptomator.create(session, new VaultCredentials("test"), vaultVersion); final DefaultVaultRegistry registry = new DefaultVaultRegistry(new DisabledPasswordCallback(), cryptomator); session.withRegistry(registry); - new CryptoTouchFeature<>(session, new DefaultTouchFeature<>(new DAVWriteFeature(session) - ), new DAVWriteFeature(session), cryptomator).touch(source, new TransferStatus()); + new CryptoTouchFeature<>(session, new DefaultTouchFeature( + session), cryptomator).touch(new CryptoWriteFeature<>(session, new DAVWriteFeature(session), cryptomator), source, new TransferStatus()); assertTrue(cryptomator.getFeature(session, Find.class, new DefaultFindFeature(session)).find(source)); - cryptomator.getFeature(session, Directory.class, new DAVDirectoryFeature(session)).mkdir(targetFolder, new TransferStatus()); + cryptomator.getFeature(session, Directory.class, new DAVDirectoryFeature(session)).mkdir( + cryptomator.getFeature(session, Write.class, new DAVWriteFeature(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); @@ -151,10 +153,11 @@ public class CopyWorkerTest extends AbstractDAVTest { cryptomator.create(session, new VaultCredentials("test"), vaultVersion); final DefaultVaultRegistry registry = new DefaultVaultRegistry(new DisabledPasswordCallback(), cryptomator); session.withRegistry(registry); - cryptomator.getFeature(session, Directory.class, new DAVDirectoryFeature(session)).mkdir(folder, new TransferStatus()); + cryptomator.getFeature(session, Directory.class, new DAVDirectoryFeature(session)).mkdir( + cryptomator.getFeature(session, Write.class, new DAVWriteFeature(session)), folder, new TransferStatus()); assertTrue(cryptomator.getFeature(session, Find.class, new DefaultFindFeature(session)).find(folder)); - new CryptoTouchFeature<>(session, new DefaultTouchFeature<>(new DAVWriteFeature(session) - ), new DAVWriteFeature(session), cryptomator).touch(file, new TransferStatus()); + new CryptoTouchFeature<>(session, new DefaultTouchFeature( + session), cryptomator).touch(new CryptoWriteFeature<>(session, new DAVWriteFeature(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)); @@ -186,7 +189,8 @@ public class CopyWorkerTest extends AbstractDAVTest { cryptomator.create(session, new VaultCredentials("test"), vaultVersion); final DefaultVaultRegistry registry = new DefaultVaultRegistry(new DisabledPasswordCallback(), cryptomator); session.withRegistry(registry); - cryptomator.getFeature(session, Directory.class, new DAVDirectoryFeature(session)).mkdir(encryptedFolder, new TransferStatus()); + cryptomator.getFeature(session, Directory.class, new DAVDirectoryFeature(session)).mkdir( + cryptomator.getFeature(session, Write.class, new DAVWriteFeature(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()); @@ -206,9 +210,9 @@ public class CopyWorkerTest extends AbstractDAVTest { 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 DAVDirectoryFeature(session).mkdir(cleartextFolder, new TransferStatus()); - new DefaultTouchFeature<>(new DAVWriteFeature(session) - ).touch(cleartextFile, new TransferStatus()); + new DAVDirectoryFeature(session).mkdir(new DAVWriteFeature(session), cleartextFolder, new TransferStatus()); + new DefaultTouchFeature( + session).touch(new DAVWriteFeature(session), cleartextFile, new TransferStatus()); assertTrue(new DAVFindFeature(session).find(cleartextFolder)); assertTrue(new DAVFindFeature(session).find(cleartextFile)); final CryptoVault cryptomator = new CryptoVault(vault); @@ -233,18 +237,19 @@ public class CopyWorkerTest extends AbstractDAVTest { final Path home = new DefaultHomeFinderService(session).find(); 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 DAVDirectoryFeature(session).mkdir(clearFolder, new TransferStatus()); + new DAVDirectoryFeature(session).mkdir(new DAVWriteFeature(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 DAVDirectoryFeature(session)).mkdir(encryptedFolder, new TransferStatus()); + cryptomator.getFeature(session, Directory.class, new DAVDirectoryFeature(session)).mkdir( + cryptomator.getFeature(session, Write.class, new DAVWriteFeature(session)), encryptedFolder, new TransferStatus()); assertTrue(cryptomator.getFeature(session, Find.class, new DefaultFindFeature(session)).find(encryptedFolder)); final byte[] content = RandomUtils.nextBytes(40500); final TransferStatus status = new TransferStatus().setLength(content.length); - new CryptoBulkFeature<>(session, new DisabledBulkFeature(), new DAVDeleteFeature(session), cryptomator).pre(Transfer.Type.upload, Collections.singletonMap(new TransferItem(encryptedFile), status), new DisabledConnectionCallback()); + new CryptoBulkFeature<>(session, new DisabledBulkFeature(), cryptomator).pre(Transfer.Type.upload, Collections.singletonMap(new TransferItem(encryptedFile), status), new DisabledConnectionCallback()); new StreamCopier(new TransferStatus(), new TransferStatus()).transfer(new ByteArrayInputStream(content), new CryptoWriteFeature<>(session, new DAVWriteFeature(session), cryptomator).write(encryptedFile, status, new DisabledConnectionCallback())); assertTrue(cryptomator.getFeature(session, Find.class, new DefaultFindFeature(session)).find(encryptedFile)); // move file outside vault @@ -270,9 +275,11 @@ public class CopyWorkerTest extends AbstractDAVTest { cryptomator.create(session, new VaultCredentials("test"), vaultVersion); final DefaultVaultRegistry registry = new DefaultVaultRegistry(new DisabledPasswordCallback(), cryptomator); session.withRegistry(registry); - cryptomator.getFeature(session, Directory.class, new DAVDirectoryFeature(session)).mkdir(encryptedFolder, new TransferStatus()); + cryptomator.getFeature(session, Directory.class, new DAVDirectoryFeature(session)).mkdir( + new CryptoWriteFeature<>(session, new DAVWriteFeature(session), cryptomator), encryptedFolder, new TransferStatus()); assertTrue(cryptomator.getFeature(session, Find.class, new DefaultFindFeature(session)).find(encryptedFolder)); - new CryptoTouchFeature<>(session, new DefaultTouchFeature<>(new DAVWriteFeature(session)), new DAVWriteFeature(session), cryptomator).touch(encryptedFile, new TransferStatus()); + new CryptoTouchFeature<>(session, new DefaultTouchFeature(session), cryptomator).touch( + new CryptoWriteFeature<>(session, new DAVWriteFeature(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)); diff --git a/webdav/src/test/java/ch/cyberduck/core/cryptomator/CryptoDAVSingleTransferWorkerTest.java b/webdav/src/test/java/ch/cyberduck/core/cryptomator/CryptoDAVSingleTransferWorkerTest.java index ee1c8be25d..a7b262d7fa 100644 --- a/webdav/src/test/java/ch/cyberduck/core/cryptomator/CryptoDAVSingleTransferWorkerTest.java +++ b/webdav/src/test/java/ch/cyberduck/core/cryptomator/CryptoDAVSingleTransferWorkerTest.java @@ -41,6 +41,7 @@ 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.io.DisabledStreamListener; import ch.cyberduck.core.io.StatusOutputStream; import ch.cyberduck.core.io.StreamCopier; @@ -156,7 +157,8 @@ public class CryptoDAVSingleTransferWorkerTest extends AbstractDAVTest { return new VaultCredentials("test"); } })); - final Path dir1 = cryptomator.getFeature(session, Directory.class, new DAVDirectoryFeature(session)).mkdir(new Path(vault, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory)), new TransferStatus()); + final Path dir1 = cryptomator.getFeature(session, Directory.class, new DAVDirectoryFeature(session)).mkdir( + cryptomator.getFeature(session, Write.class, new DAVWriteFeature(session)), new Path(vault, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory)), new TransferStatus()); final Local localDirectory1 = new Local(System.getProperty("java.io.tmpdir"), new AlphanumericRandomStringService().random()); final Path file1 = new Path(dir1, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)); final FileHeader header = cryptomator.getFileHeaderCryptor().create(); diff --git a/webdav/src/test/java/ch/cyberduck/core/cryptomator/DAVDirectoryFeatureTest.java b/webdav/src/test/java/ch/cyberduck/core/cryptomator/DAVDirectoryFeatureTest.java index cc7fca5f4a..df3f84fbc0 100644 --- a/webdav/src/test/java/ch/cyberduck/core/cryptomator/DAVDirectoryFeatureTest.java +++ b/webdav/src/test/java/ch/cyberduck/core/cryptomator/DAVDirectoryFeatureTest.java @@ -22,9 +22,11 @@ import ch.cyberduck.core.Path; import ch.cyberduck.core.dav.AbstractDAVTest; import ch.cyberduck.core.dav.DAVDeleteFeature; import ch.cyberduck.core.dav.DAVDirectoryFeature; +import ch.cyberduck.core.dav.DAVWriteFeature; 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.shared.DefaultHomeFinderService; import ch.cyberduck.core.transfer.TransferStatus; @@ -55,7 +57,8 @@ public class DAVDirectoryFeatureTest extends AbstractDAVTest { final CryptoVault cryptomator = new CryptoVault(vault); cryptomator.create(session, new VaultCredentials("test"), vaultVersion); session.withRegistry(new DefaultVaultRegistry(new DisabledPasswordCallback(), cryptomator)); - cryptomator.getFeature(session, Directory.class, new DAVDirectoryFeature(session)).mkdir(test, new TransferStatus()); + cryptomator.getFeature(session, Directory.class, new DAVDirectoryFeature(session)).mkdir( + cryptomator.getFeature(session, Write.class, new DAVWriteFeature(session)), test, new TransferStatus()); assertTrue(cryptomator.getFeature(session, Find.class, new DefaultFindFeature(session)).find(test)); cryptomator.getFeature(session, Delete.class, new DAVDeleteFeature(session)).delete(Arrays.asList(test, vault), new DisabledLoginCallback(), new Delete.DisabledCallback()); } @@ -69,7 +72,8 @@ public class DAVDirectoryFeatureTest extends AbstractDAVTest { final CryptoVault cryptomator = new CryptoVault(vault); cryptomator.create(session, new VaultCredentials("test"), vaultVersion); session.withRegistry(new DefaultVaultRegistry(new DisabledPasswordCallback(), cryptomator)); - cryptomator.getFeature(session, Directory.class, new DAVDirectoryFeature(session)).mkdir(test, new TransferStatus()); + cryptomator.getFeature(session, Directory.class, new DAVDirectoryFeature(session)).mkdir( + cryptomator.getFeature(session, Write.class, new DAVWriteFeature(session)), test, new TransferStatus()); assertTrue(cryptomator.getFeature(session, Find.class, new DefaultFindFeature(session)).find(test)); cryptomator.getFeature(session, Delete.class, new DAVDeleteFeature(session)).delete(Arrays.asList(test, vault), new DisabledLoginCallback(), new Delete.DisabledCallback()); } diff --git a/webdav/src/test/java/ch/cyberduck/core/cryptomator/DAVListServiceTest.java b/webdav/src/test/java/ch/cyberduck/core/cryptomator/DAVListServiceTest.java index d51156b8a7..857675a1c0 100644 --- a/webdav/src/test/java/ch/cyberduck/core/cryptomator/DAVListServiceTest.java +++ b/webdav/src/test/java/ch/cyberduck/core/cryptomator/DAVListServiceTest.java @@ -22,6 +22,7 @@ import ch.cyberduck.core.DisabledPasswordCallback; import ch.cyberduck.core.Path; import ch.cyberduck.core.cryptomator.features.CryptoListService; import ch.cyberduck.core.cryptomator.features.CryptoTouchFeature; +import ch.cyberduck.core.cryptomator.features.CryptoWriteFeature; import ch.cyberduck.core.dav.AbstractDAVTest; import ch.cyberduck.core.dav.DAVDeleteFeature; import ch.cyberduck.core.dav.DAVListService; @@ -58,8 +59,8 @@ public class DAVListServiceTest extends AbstractDAVTest { cryptomator.create(session, new VaultCredentials("test"), vaultVersion); session.withRegistry(new DefaultVaultRegistry(new DisabledPasswordCallback(), cryptomator)); assertTrue(new CryptoListService(session, new DAVListService(session), cryptomator).list(vault, new DisabledListProgressListener()).isEmpty()); - new CryptoTouchFeature<>(session, new DefaultTouchFeature<>(new DAVWriteFeature(session) - ), new DAVWriteFeature(session), cryptomator).touch(test, new TransferStatus()); + new CryptoTouchFeature<>(session, new DefaultTouchFeature( + session), cryptomator).touch(new CryptoWriteFeature<>(session, new DAVWriteFeature(session), cryptomator), test, new TransferStatus()); assertEquals(test, new CryptoListService(session, new DAVListService(session), cryptomator).list(vault, new DisabledListProgressListener()).get(0)); cryptomator.getFeature(session, Delete.class, new DAVDeleteFeature(session)).delete(Arrays.asList(test, vault), new DisabledLoginCallback(), new Delete.DisabledCallback()); } diff --git a/webdav/src/test/java/ch/cyberduck/core/cryptomator/DAVMoveFeatureTest.java b/webdav/src/test/java/ch/cyberduck/core/cryptomator/DAVMoveFeatureTest.java index c17480b5a5..4ec36cda83 100644 --- a/webdav/src/test/java/ch/cyberduck/core/cryptomator/DAVMoveFeatureTest.java +++ b/webdav/src/test/java/ch/cyberduck/core/cryptomator/DAVMoveFeatureTest.java @@ -21,6 +21,7 @@ import ch.cyberduck.core.DisabledLoginCallback; import ch.cyberduck.core.DisabledPasswordCallback; import ch.cyberduck.core.Path; import ch.cyberduck.core.cryptomator.features.CryptoTouchFeature; +import ch.cyberduck.core.cryptomator.features.CryptoWriteFeature; import ch.cyberduck.core.dav.AbstractDAVTest; import ch.cyberduck.core.dav.DAVAttributesFinderFeature; import ch.cyberduck.core.dav.DAVDeleteFeature; @@ -33,6 +34,7 @@ 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.shared.DefaultHomeFinderService; import ch.cyberduck.core.shared.DefaultTouchFeature; @@ -63,10 +65,11 @@ public class DAVMoveFeatureTest extends AbstractDAVTest { final CryptoVault cryptomator = new CryptoVault(vault); cryptomator.create(session, new VaultCredentials("test"), vaultVersion); session.withRegistry(new DefaultVaultRegistry(new DisabledPasswordCallback(), cryptomator)); - cryptomator.getFeature(session, Directory.class, new DAVDirectoryFeature(session)).mkdir(folder, new TransferStatus()); + cryptomator.getFeature(session, Directory.class, new DAVDirectoryFeature(session)).mkdir( + cryptomator.getFeature(session, Write.class, new DAVWriteFeature(session)), folder, new TransferStatus()); final Path template = new Path(folder, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)); - final Path file = new CryptoTouchFeature<>(session, new DefaultTouchFeature<>(new DAVWriteFeature(session)), - new DAVWriteFeature(session), cryptomator).touch(template, new TransferStatus()) + final Path file = new CryptoTouchFeature<>(session, new DefaultTouchFeature(session), + cryptomator).touch(new CryptoWriteFeature<>(session, new DAVWriteFeature(session), cryptomator), template, new TransferStatus()) .withAttributes(cryptomator.getFeature(session, AttributesFinder.class, new DAVAttributesFinderFeature(session)).find(template)); assertTrue(cryptomator.getFeature(session, Find.class, new DefaultFindFeature(session)).find(file)); final Move move = cryptomator.getFeature(session, Move.class, new DAVMoveFeature(session)); diff --git a/webdav/src/test/java/ch/cyberduck/core/cryptomator/DAVReadFeatureTest.java b/webdav/src/test/java/ch/cyberduck/core/cryptomator/DAVReadFeatureTest.java index 621882ba02..3f38d3f3b8 100644 --- a/webdav/src/test/java/ch/cyberduck/core/cryptomator/DAVReadFeatureTest.java +++ b/webdav/src/test/java/ch/cyberduck/core/cryptomator/DAVReadFeatureTest.java @@ -87,7 +87,7 @@ public class DAVReadFeatureTest extends AbstractDAVTest { out.close(); assertTrue(cryptomator.getFeature(session, Find.class, new DAVFindFeature(session)).find(test)); assertEquals(content.length, new CryptoListService(session, new DAVListService(session), cryptomator).list(test.getParent(), new DisabledListProgressListener()).get(test).attributes().getSize()); - assertEquals(content.length, new CryptoUploadFeature<>(session, new DAVUploadFeature(session), new DAVWriteFeature(session), cryptomator).append(test, status.setRemote(cryptomator.getFeature(session, AttributesFinder.class, new DAVAttributesFinderFeature(session)).find(test))).offset, 0L); + assertEquals(content.length, new CryptoUploadFeature<>(session, new DAVUploadFeature(session), cryptomator).append(test, status.setRemote(cryptomator.getFeature(session, AttributesFinder.class, new DAVAttributesFinderFeature(session)).find(test))).offset, 0L); { final ByteArrayOutputStream buffer = new ByteArrayOutputStream(40000); final TransferStatus read = new TransferStatus(); diff --git a/webdav/src/test/java/ch/cyberduck/core/cryptomator/DAVTouchFeatureTest.java b/webdav/src/test/java/ch/cyberduck/core/cryptomator/DAVTouchFeatureTest.java index 945f838320..1dbbdd0c76 100644 --- a/webdav/src/test/java/ch/cyberduck/core/cryptomator/DAVTouchFeatureTest.java +++ b/webdav/src/test/java/ch/cyberduck/core/cryptomator/DAVTouchFeatureTest.java @@ -20,6 +20,7 @@ import ch.cyberduck.core.DisabledLoginCallback; import ch.cyberduck.core.DisabledPasswordCallback; import ch.cyberduck.core.Path; import ch.cyberduck.core.cryptomator.features.CryptoTouchFeature; +import ch.cyberduck.core.cryptomator.features.CryptoWriteFeature; import ch.cyberduck.core.dav.AbstractDAVTest; import ch.cyberduck.core.dav.DAVAttributesFinderFeature; import ch.cyberduck.core.dav.DAVDeleteFeature; @@ -62,8 +63,8 @@ public class DAVTouchFeatureTest extends AbstractDAVTest { cryptomator.create(session, new VaultCredentials("test"), vaultVersion); session.withRegistry(new DefaultVaultRegistry(new DisabledPasswordCallback(), cryptomator)); final Path template = new Path(vault, new AlphanumericRandomStringService(130).random(), EnumSet.of(Path.Type.file)); - final Path test = new CryptoTouchFeature<>(session, new DefaultTouchFeature<>(new DAVWriteFeature(session) - ), new DAVWriteFeature(session), cryptomator).touch(template, new TransferStatus()) + final Path test = new CryptoTouchFeature<>(session, new DefaultTouchFeature( + session), cryptomator).touch(new CryptoWriteFeature<>(session, new DAVWriteFeature(session), cryptomator), template, new TransferStatus()) .withAttributes(cryptomator.getFeature(session, AttributesFinder.class, new DAVAttributesFinderFeature(session)).find(template)); assertTrue(cryptomator.getFeature(session, Find.class, new DAVFindFeature(session)).find(test)); assertEquals(test.attributes(), cryptomator.getFeature(session, AttributesFinder.class, new DAVAttributesFinderFeature(session)).find(test)); @@ -79,8 +80,8 @@ public class DAVTouchFeatureTest extends AbstractDAVTest { final CryptoVault cryptomator = new CryptoVault(vault); cryptomator.create(session, new VaultCredentials("test"), vaultVersion); session.withRegistry(new DefaultVaultRegistry(new DisabledPasswordCallback(), cryptomator)); - new CryptoTouchFeature<>(session, new DefaultTouchFeature<>(new DAVWriteFeature(session) - ), new DAVWriteFeature(session), cryptomator).touch(test, new TransferStatus()); + new CryptoTouchFeature<>(session, new DefaultTouchFeature( + session), cryptomator).touch(new CryptoWriteFeature<>(session, new DAVWriteFeature(session), cryptomator), test, new TransferStatus()); assertTrue(cryptomator.getFeature(session, Find.class, new DefaultFindFeature(session)).find(test)); cryptomator.getFeature(session, Delete.class, new DAVDeleteFeature(session)).delete(Arrays.asList(test, vault), new DisabledLoginCallback(), new Delete.DisabledCallback()); } @@ -94,12 +95,12 @@ public class DAVTouchFeatureTest extends AbstractDAVTest { final CryptoVault cryptomator = new CryptoVault(vault); cryptomator.create(session, new VaultCredentials("test"), vaultVersion); session.withRegistry(new DefaultVaultRegistry(new DisabledPasswordCallback(), cryptomator)); - new CryptoTouchFeature<>(session, new DefaultTouchFeature<>(new DAVWriteFeature(session) - ), new DAVWriteFeature(session), cryptomator).touch(test, new TransferStatus()); + new CryptoTouchFeature<>(session, new DefaultTouchFeature( + session), cryptomator).touch(new CryptoWriteFeature<>(session, new DAVWriteFeature(session), cryptomator), test, new TransferStatus()); assertTrue(cryptomator.getFeature(session, Find.class, new DefaultFindFeature(session)).find(test)); cryptomator.getFeature(session, Delete.class, new DAVDeleteFeature(session)).delete(Collections.singletonList(test), new DisabledLoginCallback(), new Delete.DisabledCallback()); - new CryptoTouchFeature<>(session, new DefaultTouchFeature<>(new DAVWriteFeature(session) - ), new DAVWriteFeature(session), cryptomator).touch(test, new TransferStatus()); + new CryptoTouchFeature<>(session, new DefaultTouchFeature( + session), cryptomator).touch(new CryptoWriteFeature<>(session, new DAVWriteFeature(session), cryptomator), test, new TransferStatus()); assertTrue(cryptomator.getFeature(session, Find.class, new DefaultFindFeature(session)).find(test)); cryptomator.getFeature(session, Delete.class, new DAVDeleteFeature(session)).delete(Arrays.asList(test, vault), new DisabledLoginCallback(), new Delete.DisabledCallback()); } diff --git a/webdav/src/test/java/ch/cyberduck/core/cryptomator/DAVWriteFeatureTest.java b/webdav/src/test/java/ch/cyberduck/core/cryptomator/DAVWriteFeatureTest.java index 26fbefa547..1ca8f86568 100644 --- a/webdav/src/test/java/ch/cyberduck/core/cryptomator/DAVWriteFeatureTest.java +++ b/webdav/src/test/java/ch/cyberduck/core/cryptomator/DAVWriteFeatureTest.java @@ -87,7 +87,7 @@ public class DAVWriteFeatureTest extends AbstractDAVTest { out.close(); assertTrue(cryptomator.getFeature(session, Find.class, new DAVFindFeature(session)).find(test)); assertEquals(content.length, new CryptoListService(session, new DAVListService(session), cryptomator).list(test.getParent(), new DisabledListProgressListener()).get(test).attributes().getSize()); - assertEquals(content.length, new CryptoUploadFeature<>(session, new DAVUploadFeature(session), new DAVWriteFeature(session), cryptomator).append(test, status + assertEquals(content.length, new CryptoUploadFeature<>(session, new DAVUploadFeature(session), cryptomator).append(test, status .setRemote(cryptomator.getFeature(session, AttributesFinder.class, new DAVAttributesFinderFeature(session)).find(test))).offset, 0L); final ByteArrayOutputStream buffer = new ByteArrayOutputStream(content.length); final InputStream in = new CryptoReadFeature(session, new DAVReadFeature(session), cryptomator).read(test, new TransferStatus().setLength(content.length), new DisabledConnectionCallback()); diff --git a/webdav/src/test/java/ch/cyberduck/core/dav/DAVAttributesFinderFeatureTest.java b/webdav/src/test/java/ch/cyberduck/core/dav/DAVAttributesFinderFeatureTest.java index 4ee1f20903..4405a6bc4a 100644 --- a/webdav/src/test/java/ch/cyberduck/core/dav/DAVAttributesFinderFeatureTest.java +++ b/webdav/src/test/java/ch/cyberduck/core/dav/DAVAttributesFinderFeatureTest.java @@ -11,6 +11,7 @@ import ch.cyberduck.core.Path; import ch.cyberduck.core.PathAttributes; import ch.cyberduck.core.cryptomator.CryptoVault; import ch.cyberduck.core.cryptomator.features.CryptoTouchFeature; +import ch.cyberduck.core.cryptomator.features.CryptoWriteFeature; import ch.cyberduck.core.date.RFC1123DateFormatter; import ch.cyberduck.core.exception.InteroperabilityException; import ch.cyberduck.core.exception.NotfoundException; @@ -66,7 +67,7 @@ public class DAVAttributesFinderFeatureTest extends AbstractDAVTest { @Test public void testFindFile() throws Exception { - final Path test = new DAVTouchFeature(session).touch(new Path(new DefaultHomeFinderService(session).find(), + final Path test = new DAVTouchFeature(session).touch(new DAVWriteFeature(session), new Path(new DefaultHomeFinderService(session).find(), new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)), new TransferStatus()); final DAVAttributesFinderFeature f = new DAVAttributesFinderFeature(session); final PathAttributes attributes = f.find(test); @@ -88,7 +89,7 @@ public class DAVAttributesFinderFeatureTest extends AbstractDAVTest { @Test public void testFindDirectory() throws Exception { - final Path test = new DAVDirectoryFeature(session).mkdir(new Path(new DefaultHomeFinderService(session).find(), + final Path test = new DAVDirectoryFeature(session).mkdir(new DAVWriteFeature(session), new Path(new DefaultHomeFinderService(session).find(), new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory)), new TransferStatus()); final DAVAttributesFinderFeature f = new DAVAttributesFinderFeature(session); final PathAttributes attributes = f.find(test); @@ -168,7 +169,7 @@ public class DAVAttributesFinderFeatureTest extends AbstractDAVTest { @Test public void testFindLock() throws Exception { - final Path test = new DAVTouchFeature(session).touch(new Path(new DefaultHomeFinderService(session).find(), + final Path test = new DAVTouchFeature(session).touch(new DAVWriteFeature(session), new Path(new DefaultHomeFinderService(session).find(), new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)), new TransferStatus()); final DAVAttributesFinderFeature f = new DAVAttributesFinderFeature(session); assertNull(f.find(test).getLockId()); @@ -190,8 +191,8 @@ public class DAVAttributesFinderFeatureTest extends AbstractDAVTest { final CryptoVault cryptomator = new CryptoVault(vault); cryptomator.create(session, new VaultCredentials("test"), vaultVersion); session.withRegistry(new DefaultVaultRegistry(new DisabledPasswordCallback(), cryptomator)); - final Path test = new CryptoTouchFeature<>(session, new DAVTouchFeature(session), new DAVWriteFeature(session), cryptomator).touch( - new Path(vault, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)), new TransferStatus()); + final Path test = new CryptoTouchFeature<>(session, new DAVTouchFeature(session), cryptomator).touch( + new CryptoWriteFeature<>(session, new DAVWriteFeature(session), cryptomator), new Path(vault, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)), new TransferStatus()); test.attributes().setSize(0L); final PathAttributes attributes = cryptomator.getFeature(session, AttributesFinder.class, new DefaultAttributesFinderFeature(session)).find(test); assertNotNull(attributes); diff --git a/webdav/src/test/java/ch/cyberduck/core/dav/DAVCopyFeatureTest.java b/webdav/src/test/java/ch/cyberduck/core/dav/DAVCopyFeatureTest.java index 8b794ccf40..fa9f66a012 100644 --- a/webdav/src/test/java/ch/cyberduck/core/dav/DAVCopyFeatureTest.java +++ b/webdav/src/test/java/ch/cyberduck/core/dav/DAVCopyFeatureTest.java @@ -46,7 +46,7 @@ public class DAVCopyFeatureTest extends AbstractDAVTest { @Test public void testCopyFile() throws Exception { final Path test = new Path(new DefaultHomeFinderService(session).find(), new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)); - new DAVTouchFeature(session).touch(test, new TransferStatus()); + new DAVTouchFeature(session).touch(new DAVWriteFeature(session), test, new TransferStatus()); final Path copy = new Path(new DefaultHomeFinderService(session).find(), new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)); new DAVCopyFeature(session).copy(test, copy, new TransferStatus(), new DisabledConnectionCallback(), new DisabledStreamListener()); assertEquals(new DAVAttributesFinderFeature(session).find(test), new DAVAttributesFinderFeature(session).find(copy)); @@ -58,7 +58,7 @@ public class DAVCopyFeatureTest extends AbstractDAVTest { @Test public void testCopyWithLock() throws Exception { - final Path test = new DAVTouchFeature(session).touch(new Path(new DefaultHomeFinderService(session).find(), new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)), new TransferStatus()); + final Path test = new DAVTouchFeature(session).touch(new DAVWriteFeature(session), new Path(new DefaultHomeFinderService(session).find(), new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)), new TransferStatus()); assertThrows(InteroperabilityException.class, () -> new DAVLockFeature(session).lock(test)); new DAVDeleteFeature(session).delete(Collections.singletonMap(test, new TransferStatus()), new DisabledLoginCallback(), new Delete.DisabledCallback()); } @@ -66,11 +66,11 @@ public class DAVCopyFeatureTest extends AbstractDAVTest { @Test public void testCopyToExistingFile() throws Exception { final Path folder = new Path(new DefaultHomeFinderService(session).find(), new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory)); - new DAVDirectoryFeature(session).mkdir(folder, new TransferStatus()); + new DAVDirectoryFeature(session).mkdir(new DAVWriteFeature(session), folder, new TransferStatus()); final Path test = new Path(folder, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)); - new DAVTouchFeature(session).touch(test, new TransferStatus()); + new DAVTouchFeature(session).touch(new DAVWriteFeature(session), test, new TransferStatus()); final Path copy = new Path(folder, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)); - new DAVTouchFeature(session).touch(copy, new TransferStatus()); + new DAVTouchFeature(session).touch(new DAVWriteFeature(session), copy, new TransferStatus()); assertThrows(ConflictException.class, () -> new DAVCopyFeature(session).copy(test, copy, new TransferStatus().setExists(false), new DisabledConnectionCallback(), new DisabledStreamListener())); new DAVCopyFeature(session).copy(test, copy, new TransferStatus().setExists(true), new DisabledConnectionCallback(), new DisabledStreamListener()); final Find find = new DefaultFindFeature(session); @@ -84,10 +84,10 @@ public class DAVCopyFeatureTest extends AbstractDAVTest { final Path directory = new Path(new DefaultHomeFinderService(session).find(), new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory)); final String name = new AlphanumericRandomStringService().random(); final Path file = new Path(directory, name, EnumSet.of(Path.Type.file)); - new DAVDirectoryFeature(session).mkdir(directory, new TransferStatus()); - new DAVTouchFeature(session).touch(file, new TransferStatus()); + new DAVDirectoryFeature(session).mkdir(new DAVWriteFeature(session), directory, new TransferStatus()); + new DAVTouchFeature(session).touch(new DAVWriteFeature(session), file, new TransferStatus()); final Path copy = new Path(new DefaultHomeFinderService(session).find(), new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory)); - new DAVDirectoryFeature(session).mkdir(copy, new TransferStatus()); + new DAVDirectoryFeature(session).mkdir(new DAVWriteFeature(session), copy, new TransferStatus()); assertThrows(ConflictException.class, () -> new DAVCopyFeature(session).copy(directory, copy, new TransferStatus().setExists(false), new DisabledConnectionCallback(), new DisabledStreamListener())); new DAVCopyFeature(session).copy(directory, copy, new TransferStatus().setExists(true), new DisabledConnectionCallback(), new DisabledStreamListener()); assertTrue(new DAVFindFeature(session).find(file)); diff --git a/webdav/src/test/java/ch/cyberduck/core/dav/DAVDeleteFeatureTest.java b/webdav/src/test/java/ch/cyberduck/core/dav/DAVDeleteFeatureTest.java index bca7417199..fd74fe16e1 100644 --- a/webdav/src/test/java/ch/cyberduck/core/dav/DAVDeleteFeatureTest.java +++ b/webdav/src/test/java/ch/cyberduck/core/dav/DAVDeleteFeatureTest.java @@ -25,7 +25,7 @@ public class DAVDeleteFeatureTest extends AbstractDAVTest { @Test public void testDeleteFile() throws Exception { final Path test = new Path(new DefaultHomeFinderService(session).find(), new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)); - new DAVTouchFeature(session).touch(test, new TransferStatus()); + new DAVTouchFeature(session).touch(new DAVWriteFeature(session), test, new TransferStatus()); assertTrue(new DAVFindFeature(session).find(test)); new DAVDeleteFeature(session).delete(Collections.singletonMap(test, new TransferStatus()), new DisabledLoginCallback(), new Delete.DisabledCallback()); assertFalse(new DAVFindFeature(session).find(test)); @@ -34,7 +34,7 @@ public class DAVDeleteFeatureTest extends AbstractDAVTest { @Test public void testDeleteFileWithLock() throws Exception { final Path test = new Path(new DefaultHomeFinderService(session).find(), new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)); - new DAVTouchFeature(session).touch(test, new TransferStatus()); + new DAVTouchFeature(session).touch(new DAVWriteFeature(session), test, new TransferStatus()); String lock = null; try { lock = new DAVLockFeature(session).lock(test); @@ -50,9 +50,9 @@ public class DAVDeleteFeatureTest extends AbstractDAVTest { @Test public void testDeleteDirectory() throws Exception { final Path test = new Path(new DefaultHomeFinderService(session).find(), new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory)); - new DAVDirectoryFeature(session).mkdir(test, new TransferStatus()); + new DAVDirectoryFeature(session).mkdir(new DAVWriteFeature(session), test, new TransferStatus()); assertTrue(new DAVFindFeature(session).find(test)); - new DAVTouchFeature(session).touch(new Path(test, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)), new TransferStatus()); + new DAVTouchFeature(session).touch(new DAVWriteFeature(session), new Path(test, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)), new TransferStatus()); new DAVDeleteFeature(session).delete(Collections.singletonMap(test, new TransferStatus()), new DisabledLoginCallback(), new Delete.DisabledCallback()); assertFalse(new DAVFindFeature(session).find(test)); } diff --git a/webdav/src/test/java/ch/cyberduck/core/dav/DAVDirectoryFeatureTest.java b/webdav/src/test/java/ch/cyberduck/core/dav/DAVDirectoryFeatureTest.java index 4b32b04aa1..c5bba881fc 100644 --- a/webdav/src/test/java/ch/cyberduck/core/dav/DAVDirectoryFeatureTest.java +++ b/webdav/src/test/java/ch/cyberduck/core/dav/DAVDirectoryFeatureTest.java @@ -41,9 +41,9 @@ public class DAVDirectoryFeatureTest extends AbstractDAVTest { @Test public void testMakeDirectory() throws Exception { final Path test = new Path(new DefaultHomeFinderService(session).find(), new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory)); - new DAVDirectoryFeature(session).mkdir(test, new TransferStatus()); + new DAVDirectoryFeature(session).mkdir(new DAVWriteFeature(session), test, new TransferStatus()); assertTrue(session.getFeature(Find.class).find(test)); - assertThrows(ConflictException.class, () -> new DAVDirectoryFeature(session).mkdir(test, new TransferStatus())); + assertThrows(ConflictException.class, () -> new DAVDirectoryFeature(session).mkdir(new DAVWriteFeature(session), test, new TransferStatus())); new DAVDeleteFeature(session).delete(Collections.singletonList(test), new DisabledLoginCallback(), new Delete.DisabledCallback()); assertFalse(session.getFeature(Find.class).find(test)); } diff --git a/webdav/src/test/java/ch/cyberduck/core/dav/DAVListServiceTest.java b/webdav/src/test/java/ch/cyberduck/core/dav/DAVListServiceTest.java index 479da766e8..c56f69bab5 100644 --- a/webdav/src/test/java/ch/cyberduck/core/dav/DAVListServiceTest.java +++ b/webdav/src/test/java/ch/cyberduck/core/dav/DAVListServiceTest.java @@ -46,7 +46,7 @@ public class DAVListServiceTest extends AbstractDAVTest { @Test public void testListFileException() throws Exception { - final Path test = new DAVTouchFeature(session).touch(new Path(new DefaultHomeFinderService(session).find(), + final Path test = new DAVTouchFeature(session).touch(new DAVWriteFeature(session), new Path(new DefaultHomeFinderService(session).find(), new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)), new TransferStatus()); assertThrows(NotfoundException.class, () -> new DAVListService(session).list(new Path(test.getAbsolute(), EnumSet.of(Path.Type.directory, Path.Type.volume)), new DisabledListProgressListener())); diff --git a/webdav/src/test/java/ch/cyberduck/core/dav/DAVLockFeatureTest.java b/webdav/src/test/java/ch/cyberduck/core/dav/DAVLockFeatureTest.java index f607fa75cb..4ebe667583 100644 --- a/webdav/src/test/java/ch/cyberduck/core/dav/DAVLockFeatureTest.java +++ b/webdav/src/test/java/ch/cyberduck/core/dav/DAVLockFeatureTest.java @@ -53,7 +53,7 @@ public class DAVLockFeatureTest extends AbstractDAVTest { status.setLength(content.length); final Path test = new Path(new DefaultHomeFinderService(session).find(), new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)); final HttpUploadFeature upload = new DAVUploadFeature(session); - upload.upload(test, local, new BandwidthThrottle(BandwidthThrottle.UNLIMITED), + upload.upload(new DAVWriteFeature(session), test, local, new BandwidthThrottle(BandwidthThrottle.UNLIMITED), new DisabledProgressListener(), new DisabledStreamListener(), status, new DisabledConnectionCallback()); String lock = null; try { diff --git a/webdav/src/test/java/ch/cyberduck/core/dav/DAVMetadataFeatureTest.java b/webdav/src/test/java/ch/cyberduck/core/dav/DAVMetadataFeatureTest.java index 73db72c128..a3470799df 100644 --- a/webdav/src/test/java/ch/cyberduck/core/dav/DAVMetadataFeatureTest.java +++ b/webdav/src/test/java/ch/cyberduck/core/dav/DAVMetadataFeatureTest.java @@ -40,7 +40,7 @@ public class DAVMetadataFeatureTest extends AbstractDAVTest { @Test public void testSetMetadataFile() throws Exception { final TransferStatus status = new TransferStatus(); - final Path test = new DAVTouchFeature(session).touch(new Path(new DefaultHomeFinderService(session).find(), new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)), status); + final Path test = new DAVTouchFeature(session).touch(new DAVWriteFeature(session), new Path(new DefaultHomeFinderService(session).find(), new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)), status); final String v = new AlphanumericRandomStringService().random(); final DAVMetadataFeature feature = new DAVMetadataFeature(session); feature.setMetadata(test, status.setMetadata(Collections.singletonMap("Test", v))); @@ -58,7 +58,7 @@ public class DAVMetadataFeatureTest extends AbstractDAVTest { @Test public void testSetMetadataFolder() throws Exception { final TransferStatus status = new TransferStatus(); - final Path test = new DAVDirectoryFeature(session).mkdir(new Path(new DefaultHomeFinderService(session).find(), new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory)), status); + final Path test = new DAVDirectoryFeature(session).mkdir(new DAVWriteFeature(session), new Path(new DefaultHomeFinderService(session).find(), new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory)), status); final String v = new AlphanumericRandomStringService().random(); final DAVMetadataFeature feature = new DAVMetadataFeature(session); feature.setMetadata(test, status.setMetadata(Collections.singletonMap("Test", v))); diff --git a/webdav/src/test/java/ch/cyberduck/core/dav/DAVMoveFeatureTest.java b/webdav/src/test/java/ch/cyberduck/core/dav/DAVMoveFeatureTest.java index beab7f7771..9fb3432724 100644 --- a/webdav/src/test/java/ch/cyberduck/core/dav/DAVMoveFeatureTest.java +++ b/webdav/src/test/java/ch/cyberduck/core/dav/DAVMoveFeatureTest.java @@ -47,7 +47,7 @@ public class DAVMoveFeatureTest extends AbstractDAVTest { @Test public void testMove() throws Exception { - final Path test = new DAVTouchFeature(session).touch(new Path(new DefaultHomeFinderService(session).find(), new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)), new TransferStatus()); + final Path test = new DAVTouchFeature(session).touch(new DAVWriteFeature(session), new Path(new DefaultHomeFinderService(session).find(), new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)), new TransferStatus()); assertEquals(TransferStatus.UNKNOWN_LENGTH, test.attributes().getSize()); final TransferStatus status = new TransferStatus(); new DAVTimestampFeature(session).setTimestamp(test, status.setModified(5000L)); @@ -63,7 +63,7 @@ public class DAVMoveFeatureTest extends AbstractDAVTest { @Test public void testMoveWithLock() throws Exception { - final Path test = new DAVTouchFeature(session).touch(new Path(new DefaultHomeFinderService(session).find(), new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)), new TransferStatus()); + final Path test = new DAVTouchFeature(session).touch(new DAVWriteFeature(session), new Path(new DefaultHomeFinderService(session).find(), new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)), new TransferStatus()); assertThrows(InteroperabilityException.class, () -> new DAVLockFeature(session).lock(test)); new DAVDeleteFeature(session).delete(Collections.singletonList(test), new DisabledLoginCallback(), new Delete.DisabledCallback()); } @@ -71,7 +71,7 @@ public class DAVMoveFeatureTest extends AbstractDAVTest { @Test public void testMoveDirectory() throws Exception { final Path folder = new Path(new DefaultHomeFinderService(session).find(), new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory)); - new DAVDirectoryFeature(session).mkdir(folder, new TransferStatus()); + new DAVDirectoryFeature(session).mkdir(new DAVWriteFeature(session), folder, new TransferStatus()); final Path test = new Path(folder, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)); { final byte[] content = RandomUtils.nextBytes(3547); @@ -99,9 +99,9 @@ public class DAVMoveFeatureTest extends AbstractDAVTest { @Test public void testMoveOverride() throws Exception { final Path test = new Path(new DefaultHomeFinderService(session).find(), new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)); - new DAVTouchFeature(session).touch(test, new TransferStatus()); + new DAVTouchFeature(session).touch(new DAVWriteFeature(session), test, new TransferStatus()); final Path target = new Path(new DefaultHomeFinderService(session).find(), new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)); - new DAVTouchFeature(session).touch(target, new TransferStatus()); + new DAVTouchFeature(session).touch(new DAVWriteFeature(session), target, new TransferStatus()); assertThrows(ConflictException.class, () -> new DAVMoveFeature(session).move(test, target, new TransferStatus().setExists(false), new Delete.DisabledCallback(), new DisabledConnectionCallback())); new DAVMoveFeature(session).move(test, target, new TransferStatus().setExists(true), new Delete.DisabledCallback(), new DisabledConnectionCallback()); assertFalse(new DAVFindFeature(session).find(test)); diff --git a/webdav/src/test/java/ch/cyberduck/core/dav/DAVReadFeatureTest.java b/webdav/src/test/java/ch/cyberduck/core/dav/DAVReadFeatureTest.java index 4c854a5deb..7a6b39ac7a 100644 --- a/webdav/src/test/java/ch/cyberduck/core/dav/DAVReadFeatureTest.java +++ b/webdav/src/test/java/ch/cyberduck/core/dav/DAVReadFeatureTest.java @@ -8,7 +8,6 @@ import ch.cyberduck.core.Local; import ch.cyberduck.core.Path; import ch.cyberduck.core.exception.NotfoundException; import ch.cyberduck.core.features.Delete; -import ch.cyberduck.core.features.Read; import ch.cyberduck.core.io.BandwidthThrottle; import ch.cyberduck.core.io.DisabledStreamListener; import ch.cyberduck.core.io.StreamCopier; @@ -57,7 +56,7 @@ public class DAVReadFeatureTest extends AbstractDAVTest { IOUtils.write(content, out); out.close(); new DAVUploadFeature(session).upload( - test, local, new BandwidthThrottle(BandwidthThrottle.UNLIMITED), new DisabledProgressListener(), new DisabledStreamListener(), + new DAVWriteFeature(session), test, local, new BandwidthThrottle(BandwidthThrottle.UNLIMITED), new DisabledProgressListener(), new DisabledStreamListener(), new TransferStatus().setLength(content.length), new DisabledConnectionCallback()); // Unknown length in status @@ -69,7 +68,7 @@ public class DAVReadFeatureTest extends AbstractDAVTest { return this; } }; - new DefaultDownloadFeature(session.getFeature(Read.class)).download(test, local, new BandwidthThrottle(BandwidthThrottle.UNLIMITED), + new DefaultDownloadFeature(session).download(new DAVReadFeature(session), test, local, new BandwidthThrottle(BandwidthThrottle.UNLIMITED), new DisabledStreamListener(), status, new DisabledLoginCallback()); assertEquals(923L, local.attributes().getSize()); new DAVDeleteFeature(session).delete(Collections.singletonList(test), new DisabledLoginCallback(), new Delete.DisabledCallback()); @@ -77,7 +76,7 @@ public class DAVReadFeatureTest extends AbstractDAVTest { @Test public void testReadInterrupt() throws Exception { - final Path test = new DAVTouchFeature(session).touch(new Path(new DefaultHomeFinderService(session).find(), + final Path test = new DAVTouchFeature(session).touch(new DAVWriteFeature(session), new Path(new DefaultHomeFinderService(session).find(), new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)), new TransferStatus()); // Unknown length in status final TransferStatus status = new TransferStatus(); @@ -105,7 +104,7 @@ public class DAVReadFeatureTest extends AbstractDAVTest { IOUtils.write(content, out); out.close(); new DAVUploadFeature(session).upload( - test, local, new BandwidthThrottle(BandwidthThrottle.UNLIMITED), new DisabledProgressListener(), new DisabledStreamListener(), + new DAVWriteFeature(session), test, local, new BandwidthThrottle(BandwidthThrottle.UNLIMITED), new DisabledProgressListener(), new DisabledStreamListener(), new TransferStatus().setLength(content.length), new DisabledConnectionCallback()); final TransferStatus status = new TransferStatus(); @@ -125,7 +124,7 @@ public class DAVReadFeatureTest extends AbstractDAVTest { @Test public void testReadRangeUnknownLength() throws Exception { - final Path test = new DAVTouchFeature(session).touch(new Path(new DefaultHomeFinderService(session).find(), new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)), new TransferStatus()); + final Path test = new DAVTouchFeature(session).touch(new DAVWriteFeature(session), new Path(new DefaultHomeFinderService(session).find(), new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)), new TransferStatus()); final Local local = new Local(System.getProperty("java.io.tmpdir"), new AlphanumericRandomStringService().random()); final byte[] content = RandomUtils.nextBytes(1023); final OutputStream out = local.getOutputStream(false); @@ -133,7 +132,7 @@ public class DAVReadFeatureTest extends AbstractDAVTest { IOUtils.write(content, out); out.close(); new DAVUploadFeature(session).upload( - test, local, new BandwidthThrottle(BandwidthThrottle.UNLIMITED), new DisabledProgressListener(), new DisabledStreamListener(), + new DAVWriteFeature(session), test, local, new BandwidthThrottle(BandwidthThrottle.UNLIMITED), new DisabledProgressListener(), new DisabledStreamListener(), new TransferStatus().setLength(content.length), new DisabledConnectionCallback()); final TransferStatus status = new TransferStatus(); @@ -153,7 +152,7 @@ public class DAVReadFeatureTest extends AbstractDAVTest { @Test public void testReadCloseReleaseEntity() throws Exception { - final Path test = new DAVTouchFeature(session).touch(new Path(new DefaultHomeFinderService(session).find(), + final Path test = new DAVTouchFeature(session).touch(new DAVWriteFeature(session), new Path(new DefaultHomeFinderService(session).find(), new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)), new TransferStatus()); final TransferStatus status = new TransferStatus(); final CountingInputStream in = new CountingInputStream(new DAVReadFeature(session).read(test, status, new DisabledConnectionCallback())); diff --git a/webdav/src/test/java/ch/cyberduck/core/dav/DAVSessionTest.java b/webdav/src/test/java/ch/cyberduck/core/dav/DAVSessionTest.java index 37fa594d2e..a75b371c89 100644 --- a/webdav/src/test/java/ch/cyberduck/core/dav/DAVSessionTest.java +++ b/webdav/src/test/java/ch/cyberduck/core/dav/DAVSessionTest.java @@ -104,7 +104,8 @@ public class DAVSessionTest extends AbstractDAVTest { @Test public void testTouch() throws Exception { - final Path test = new DAVTouchFeature(session).touch(new Path(new DefaultHomeFinderService(session).find(), new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)), new TransferStatus());; + final Path test = new DAVTouchFeature(session).touch(new DAVWriteFeature(session), new Path(new DefaultHomeFinderService(session).find(), new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)), new TransferStatus()); + ; assertTrue(session.getFeature(Find.class).find(test)); new DAVDeleteFeature(session).delete(Collections.singletonList(test), new DisabledLoginCallback(), new Delete.DisabledCallback()); assertFalse(session.getFeature(Find.class).find(test)); diff --git a/webdav/src/test/java/ch/cyberduck/core/dav/DAVTimestampFeatureTest.java b/webdav/src/test/java/ch/cyberduck/core/dav/DAVTimestampFeatureTest.java index ad743a7386..152a978869 100644 --- a/webdav/src/test/java/ch/cyberduck/core/dav/DAVTimestampFeatureTest.java +++ b/webdav/src/test/java/ch/cyberduck/core/dav/DAVTimestampFeatureTest.java @@ -41,7 +41,7 @@ public class DAVTimestampFeatureTest extends AbstractDAVTest { @Test public void testSetTimestamp() throws Exception { final TransferStatus status = new TransferStatus(); - final Path file = new DAVTouchFeature(session).touch(new Path(new DefaultHomeFinderService(session).find(), new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)), status); + final Path file = new DAVTouchFeature(session).touch(new DAVWriteFeature(session), new Path(new DefaultHomeFinderService(session).find(), new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)), status); new DAVTimestampFeature(session).setTimestamp(file, status.setModified(5100L)); final PathAttributes attr = new DAVAttributesFinderFeature(session).find(file); assertEquals(5000L, attr.getModificationDate()); @@ -56,12 +56,12 @@ public class DAVTimestampFeatureTest extends AbstractDAVTest { @Test public void testSetTimestampFolderExplicitImplicit() throws Exception { - final Path folder = new DAVDirectoryFeature(session).mkdir(new Path(new DefaultHomeFinderService(session).find(), new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory)), new TransferStatus()); + final Path folder = new DAVDirectoryFeature(session).mkdir(new DAVWriteFeature(session), new Path(new DefaultHomeFinderService(session).find(), new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory)), new TransferStatus()); new DAVTimestampFeature(session).setTimestamp(folder, 5100L); assertEquals(5000L, new DAVAttributesFinderFeature(session).find(folder).getModificationDate()); assertEquals(5000L, new DefaultAttributesFinderFeature(session).find(folder).getModificationDate()); Thread.sleep(1000L); - final Path file = new DAVTouchFeature(session).touch(new Path(folder, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)), new TransferStatus()); + final Path file = new DAVTouchFeature(session).touch(new DAVWriteFeature(session), new Path(folder, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)), new TransferStatus()); assertNotEquals(5000L, new DAVAttributesFinderFeature(session).find(folder).getModificationDate()); assertNotEquals(5000L, new DefaultAttributesFinderFeature(session).find(folder).getModificationDate()); new DAVDeleteFeature(session).delete(Arrays.asList(file, folder), new DisabledLoginCallback(), new Delete.DisabledCallback()); diff --git a/webdav/src/test/java/ch/cyberduck/core/dav/DAVUploadFeatureTest.java b/webdav/src/test/java/ch/cyberduck/core/dav/DAVUploadFeatureTest.java index 2d2a40d56d..ab43838c42 100644 --- a/webdav/src/test/java/ch/cyberduck/core/dav/DAVUploadFeatureTest.java +++ b/webdav/src/test/java/ch/cyberduck/core/dav/DAVUploadFeatureTest.java @@ -18,6 +18,7 @@ package ch.cyberduck.core.dav; */ import ch.cyberduck.core.AlphanumericRandomStringService; +import ch.cyberduck.core.DefaultPathAttributes; import ch.cyberduck.core.DisabledConnectionCallback; import ch.cyberduck.core.DisabledLoginCallback; import ch.cyberduck.core.DisabledProgressListener; @@ -25,6 +26,7 @@ import ch.cyberduck.core.Local; import ch.cyberduck.core.Path; import ch.cyberduck.core.exception.AccessDeniedException; import ch.cyberduck.core.features.Delete; +import ch.cyberduck.core.features.Write; import ch.cyberduck.core.io.BandwidthThrottle; import ch.cyberduck.core.io.DisabledStreamListener; import ch.cyberduck.core.local.DefaultLocalTouchFeature; @@ -57,7 +59,7 @@ public class DAVUploadFeatureTest extends AbstractDAVTest { final Path test = new Path(new Path("/dav/accessdenied", EnumSet.of(Path.Type.directory)), "nosuchname", EnumSet.of(Path.Type.file)); try { new DAVUploadFeature(session).upload( - test, local, new BandwidthThrottle(BandwidthThrottle.UNLIMITED), new DisabledProgressListener(), new DisabledStreamListener(), + new DAVWriteFeature(session), test, local, new BandwidthThrottle(BandwidthThrottle.UNLIMITED), new DisabledProgressListener(), new DisabledStreamListener(), status, new DisabledConnectionCallback()); } @@ -79,18 +81,18 @@ public class DAVUploadFeatureTest extends AbstractDAVTest { IOUtils.write(content, out); out.close(); final Path test = new Path(new DefaultHomeFinderService(session).find(), new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)); + final DAVUploadFeature feature = new DAVUploadFeature(session); + final TransferStatus status = new TransferStatus().setLength(content.length / 2); { - final TransferStatus status = new TransferStatus().setLength(content.length / 2); - new DAVUploadFeature(session).upload( - test, local, new BandwidthThrottle(BandwidthThrottle.UNLIMITED), new DisabledProgressListener(), new DisabledStreamListener(), - status, - new DisabledConnectionCallback()); + feature.upload(new DAVWriteFeature(session), test, local, new BandwidthThrottle(BandwidthThrottle.UNLIMITED), new DisabledProgressListener(), new DisabledStreamListener(), + status, new DisabledConnectionCallback()); } { - final TransferStatus status = new TransferStatus().setLength(content.length / 2).setOffset(content.length / 2).setAppend(true); - new DAVUploadFeature(session).upload( - test, local, new BandwidthThrottle(BandwidthThrottle.UNLIMITED), new DisabledProgressListener(), new DisabledStreamListener(), - status, + final Write.Append append = feature.append(test, status.setLength(content.length).setExists(true).setRemote(new DefaultPathAttributes().setSize(content.length / 2))); + assertTrue(append.append); + assertEquals(content.length / 2, append.offset, 0L); + feature.upload(new DAVWriteFeature(session), test, local, new BandwidthThrottle(BandwidthThrottle.UNLIMITED), new DisabledProgressListener(), new DisabledStreamListener(), + status.setLength(content.length / 2).setOffset(append.offset).setAppend(append.append), new DisabledConnectionCallback()); } final byte[] buffer = new byte[content.length]; @@ -105,7 +107,7 @@ public class DAVUploadFeatureTest extends AbstractDAVTest { @Test public void testAppendZeroBytes() throws Exception { final DAVUploadFeature feature = new DAVUploadFeature(session); - final Path test = new DAVTouchFeature(session).touch(new Path(new DefaultHomeFinderService(session).find(), new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)), new TransferStatus()); + final Path test = new DAVTouchFeature(session).touch(new DAVWriteFeature(session), new Path(new DefaultHomeFinderService(session).find(), new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)), new TransferStatus()); assertTrue(feature.append(test, new TransferStatus().setExists(true).setLength(0L).setRemote(new DAVAttributesFinderFeature(session).find(test))).append); new DAVDeleteFeature(session).delete(Collections.singletonList(test), new DisabledLoginCallback(), new Delete.DisabledCallback()); } diff --git a/webdav/src/test/java/ch/cyberduck/core/dav/DAVWriteFeatureTest.java b/webdav/src/test/java/ch/cyberduck/core/dav/DAVWriteFeatureTest.java index 2e62bb698f..e47bfed81e 100644 --- a/webdav/src/test/java/ch/cyberduck/core/dav/DAVWriteFeatureTest.java +++ b/webdav/src/test/java/ch/cyberduck/core/dav/DAVWriteFeatureTest.java @@ -53,7 +53,7 @@ public class DAVWriteFeatureTest extends AbstractDAVTest { status.setLength(content.length); final Path test = new Path(new DefaultHomeFinderService(session).find(), new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)); final HttpUploadFeature upload = new DAVUploadFeature(session); - upload.upload(test, local, new BandwidthThrottle(BandwidthThrottle.UNLIMITED), + upload.upload(new DAVWriteFeature(session), test, local, new BandwidthThrottle(BandwidthThrottle.UNLIMITED), new DisabledProgressListener(), new DisabledStreamListener(), status, new DisabledConnectionCallback()); assertTrue(session.getFeature(Find.class).find(test)); assertEquals(content.length, new DAVListService(session).list(test.getParent(), new DisabledListProgressListener()).get(test).attributes().getSize(), 0L); @@ -86,7 +86,7 @@ public class DAVWriteFeatureTest extends AbstractDAVTest { status.setLength(TransferStatus.UNKNOWN_LENGTH); final Path test = new Path(new DefaultHomeFinderService(session).find(), new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)); final HttpUploadFeature upload = new DAVUploadFeature(session); - upload.upload(test, local, new BandwidthThrottle(BandwidthThrottle.UNLIMITED), + upload.upload(new DAVWriteFeature(session), test, local, new BandwidthThrottle(BandwidthThrottle.UNLIMITED), new DisabledProgressListener(), new DisabledStreamListener(), status, new DisabledConnectionCallback()); assertTrue(session.getFeature(Find.class).find(test)); assertEquals(content.length, new DAVListService(session).list(test.getParent(), new DisabledListProgressListener()).get(test).attributes().getSize(), 0L); @@ -111,7 +111,7 @@ public class DAVWriteFeatureTest extends AbstractDAVTest { @Test public void testReplaceContent() throws Exception { final Local local = new Local(System.getProperty("java.io.tmpdir"), new AlphanumericRandomStringService().random()); - final Path folder = new DAVDirectoryFeature(session).mkdir(new Path(new DefaultHomeFinderService(session).find(), new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory)), new TransferStatus()); + final Path folder = new DAVDirectoryFeature(session).mkdir(new DAVWriteFeature(session), new Path(new DefaultHomeFinderService(session).find(), new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory)), new TransferStatus()); final Path test = new Path(folder, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)); final HttpUploadFeature upload = new DAVUploadFeature(session); { @@ -122,7 +122,7 @@ public class DAVWriteFeatureTest extends AbstractDAVTest { out.close(); final TransferStatus status = new TransferStatus(); status.setLength(content.length); - upload.upload(test, local, new BandwidthThrottle(BandwidthThrottle.UNLIMITED), + upload.upload(new DAVWriteFeature(session), test, local, new BandwidthThrottle(BandwidthThrottle.UNLIMITED), new DisabledProgressListener(), new DisabledStreamListener(), status, new DisabledConnectionCallback()); assertNotEquals(folderEtag, new DAVAttributesFinderFeature(session).find(folder).getETag()); } @@ -136,7 +136,7 @@ public class DAVWriteFeatureTest extends AbstractDAVTest { out.close(); final TransferStatus status = new TransferStatus(); status.setLength(content.length); - upload.upload(test, local, new BandwidthThrottle(BandwidthThrottle.UNLIMITED), + upload.upload(new DAVWriteFeature(session), test, local, new BandwidthThrottle(BandwidthThrottle.UNLIMITED), new DisabledProgressListener(), new DisabledStreamListener(), status, new DisabledConnectionCallback()); assertEquals(folderEtag, new DAVAttributesFinderFeature(session).find(folder).getETag()); assertNotEquals(fileEtag, new DAVAttributesFinderFeature(session).find(test).getETag()); diff --git a/webdav/src/test/java/ch/cyberduck/core/dav/microsoft/MicrosoftIISDAVCopyFeatureTest.java b/webdav/src/test/java/ch/cyberduck/core/dav/microsoft/MicrosoftIISDAVCopyFeatureTest.java index c6039f431f..2f690c62f7 100644 --- a/webdav/src/test/java/ch/cyberduck/core/dav/microsoft/MicrosoftIISDAVCopyFeatureTest.java +++ b/webdav/src/test/java/ch/cyberduck/core/dav/microsoft/MicrosoftIISDAVCopyFeatureTest.java @@ -27,6 +27,7 @@ import ch.cyberduck.core.dav.DAVDeleteFeature; import ch.cyberduck.core.dav.DAVDirectoryFeature; import ch.cyberduck.core.dav.DAVLockFeature; import ch.cyberduck.core.dav.DAVTouchFeature; +import ch.cyberduck.core.dav.DAVWriteFeature; import ch.cyberduck.core.exception.ConflictException; import ch.cyberduck.core.features.Delete; import ch.cyberduck.core.features.Find; @@ -51,7 +52,7 @@ public class MicrosoftIISDAVCopyFeatureTest extends AbstractMicrosoftIISDAVTest @Test public void testCopyFile() throws Exception { final Path test = new Path(new DefaultHomeFinderService(session).find(), new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)); - new DAVTouchFeature(session).touch(test, new TransferStatus()); + new DAVTouchFeature(session).touch(new DAVWriteFeature(session), test, new TransferStatus()); final Path copy = new Path(new DefaultHomeFinderService(session).find(), new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)); new DAVCopyFeature(session).copy(test, copy, new TransferStatus(), new DisabledConnectionCallback(), new DisabledStreamListener()); assertEquals(new DAVAttributesFinderFeature(session).find(test), new DAVAttributesFinderFeature(session).find(copy)); @@ -63,7 +64,7 @@ public class MicrosoftIISDAVCopyFeatureTest extends AbstractMicrosoftIISDAVTest @Test public void testCopyWithLock() throws Exception { - final Path test = new DAVTouchFeature(session).touch(new Path(new DefaultHomeFinderService(session).find(), new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)), new TransferStatus()); + final Path test = new DAVTouchFeature(session).touch(new DAVWriteFeature(session), new Path(new DefaultHomeFinderService(session).find(), new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)), new TransferStatus()); final String lock = new DAVLockFeature(session).lock(test); assertEquals(TransferStatus.UNKNOWN_LENGTH, test.attributes().getSize()); final Path target = new DAVCopyFeature(session).copy(test, @@ -78,11 +79,11 @@ public class MicrosoftIISDAVCopyFeatureTest extends AbstractMicrosoftIISDAVTest @Test public void testCopyToExistingFile() throws Exception { final Path folder = new Path(new DefaultHomeFinderService(session).find(), new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory)); - new DAVDirectoryFeature(session).mkdir(folder, new TransferStatus()); + new DAVDirectoryFeature(session).mkdir(new DAVWriteFeature(session), folder, new TransferStatus()); final Path test = new Path(folder, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)); - new DAVTouchFeature(session).touch(test, new TransferStatus()); + new DAVTouchFeature(session).touch(new DAVWriteFeature(session), test, new TransferStatus()); final Path copy = new Path(folder, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)); - new DAVTouchFeature(session).touch(copy, new TransferStatus()); + new DAVTouchFeature(session).touch(new DAVWriteFeature(session), copy, new TransferStatus()); assertThrows(ConflictException.class, () -> new DAVCopyFeature(session).copy(test, copy, new TransferStatus().setExists(false), new DisabledConnectionCallback(), new DisabledStreamListener())); new DAVCopyFeature(session).copy(test, copy, new TransferStatus().setExists(true), new DisabledConnectionCallback(), new DisabledStreamListener()); final Find find = new DefaultFindFeature(session); @@ -94,12 +95,12 @@ public class MicrosoftIISDAVCopyFeatureTest extends AbstractMicrosoftIISDAVTest @Test public void testCopyWithLockToExistingFile() throws Exception { final Path folder = new Path(new DefaultHomeFinderService(session).find(), new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory)); - new DAVDirectoryFeature(session).mkdir(folder, new TransferStatus()); + new DAVDirectoryFeature(session).mkdir(new DAVWriteFeature(session), folder, new TransferStatus()); final Path test = new Path(folder, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)); - new DAVTouchFeature(session).touch(test, new TransferStatus()); + new DAVTouchFeature(session).touch(new DAVWriteFeature(session), test, new TransferStatus()); final String lock = new DAVLockFeature(session).lock(test); final Path copy = new Path(folder, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)); - new DAVTouchFeature(session).touch(copy, new TransferStatus()); + new DAVTouchFeature(session).touch(new DAVWriteFeature(session), copy, new TransferStatus()); assertThrows(ConflictException.class, () -> new DAVCopyFeature(session).copy(test, copy, new TransferStatus().setExists(false), new DisabledConnectionCallback(), new DisabledStreamListener())); new DAVCopyFeature(session).copy(test, copy, new TransferStatus().setExists(true).setLockId(lock), new DisabledConnectionCallback(), new DisabledStreamListener()); final Find find = new DefaultFindFeature(session); @@ -114,10 +115,10 @@ public class MicrosoftIISDAVCopyFeatureTest extends AbstractMicrosoftIISDAVTest final Path directory = new Path(new DefaultHomeFinderService(session).find(), new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory)); final String name = new AlphanumericRandomStringService().random(); final Path file = new Path(directory, name, EnumSet.of(Path.Type.file)); - new DAVDirectoryFeature(session).mkdir(directory, new TransferStatus()); - new DAVTouchFeature(session).touch(file, new TransferStatus()); + new DAVDirectoryFeature(session).mkdir(new DAVWriteFeature(session), directory, new TransferStatus()); + new DAVTouchFeature(session).touch(new DAVWriteFeature(session), file, new TransferStatus()); final Path copy = new Path(new DefaultHomeFinderService(session).find(), new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory)); - new DAVDirectoryFeature(session).mkdir(copy, new TransferStatus()); + new DAVDirectoryFeature(session).mkdir(new DAVWriteFeature(session), copy, new TransferStatus()); assertThrows(ConflictException.class, () -> new DAVCopyFeature(session).copy(directory, copy, new TransferStatus().setExists(false), new DisabledConnectionCallback(), new DisabledStreamListener())); new DAVCopyFeature(session).copy(directory, copy, new TransferStatus().setExists(true), new DisabledConnectionCallback(), new DisabledStreamListener()); assertTrue(new MicrosoftIISDAVFindFeature(session).find(file)); diff --git a/webdav/src/test/java/ch/cyberduck/core/dav/microsoft/MicrosoftIISDAVLockFeatureTest.java b/webdav/src/test/java/ch/cyberduck/core/dav/microsoft/MicrosoftIISDAVLockFeatureTest.java index 66cc963b59..c1eddbf4b5 100644 --- a/webdav/src/test/java/ch/cyberduck/core/dav/microsoft/MicrosoftIISDAVLockFeatureTest.java +++ b/webdav/src/test/java/ch/cyberduck/core/dav/microsoft/MicrosoftIISDAVLockFeatureTest.java @@ -26,6 +26,7 @@ import ch.cyberduck.core.PathAttributes; import ch.cyberduck.core.dav.DAVDeleteFeature; import ch.cyberduck.core.dav.DAVLockFeature; import ch.cyberduck.core.dav.DAVUploadFeature; +import ch.cyberduck.core.dav.DAVWriteFeature; import ch.cyberduck.core.features.Delete; import ch.cyberduck.core.http.HttpUploadFeature; import ch.cyberduck.core.io.BandwidthThrottle; @@ -60,7 +61,7 @@ public class MicrosoftIISDAVLockFeatureTest extends AbstractMicrosoftIISDAVTest status.setLength(content.length); final Path test = new Path(new DefaultHomeFinderService(session).find(), new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)); final HttpUploadFeature upload = new DAVUploadFeature(session); - upload.upload(test, local, new BandwidthThrottle(BandwidthThrottle.UNLIMITED), + upload.upload(new DAVWriteFeature(session), test, local, new BandwidthThrottle(BandwidthThrottle.UNLIMITED), new DisabledProgressListener(), new DisabledStreamListener(), status, new DisabledConnectionCallback()); final String lock = new DAVLockFeature(session).lock(test); assertTrue(new MicrosoftIISDAVFindFeature(session).find(test)); diff --git a/webdav/src/test/java/ch/cyberduck/core/dav/microsoft/MicrosoftIISDAVMoveFeatureTest.java b/webdav/src/test/java/ch/cyberduck/core/dav/microsoft/MicrosoftIISDAVMoveFeatureTest.java index a709e6b8c9..2c06945140 100644 --- a/webdav/src/test/java/ch/cyberduck/core/dav/microsoft/MicrosoftIISDAVMoveFeatureTest.java +++ b/webdav/src/test/java/ch/cyberduck/core/dav/microsoft/MicrosoftIISDAVMoveFeatureTest.java @@ -55,7 +55,7 @@ public class MicrosoftIISDAVMoveFeatureTest extends AbstractMicrosoftIISDAVTest @Test public void testMove() throws Exception { - final Path test = new DAVTouchFeature(session).touch(new Path(new DefaultHomeFinderService(session).find(), new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)), new TransferStatus()); + final Path test = new DAVTouchFeature(session).touch(new DAVWriteFeature(session), new Path(new DefaultHomeFinderService(session).find(), new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)), new TransferStatus()); assertEquals(TransferStatus.UNKNOWN_LENGTH, test.attributes().getSize()); final TransferStatus status = new TransferStatus(); new DAVTimestampFeature(session).setTimestamp(test, status.setModified(5000L)); @@ -71,7 +71,7 @@ public class MicrosoftIISDAVMoveFeatureTest extends AbstractMicrosoftIISDAVTest @Test public void testMoveWithLock() throws Exception { - final Path test = new DAVTouchFeature(session).touch(new Path(new DefaultHomeFinderService(session).find(), new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)), new TransferStatus()); + final Path test = new DAVTouchFeature(session).touch(new DAVWriteFeature(session), new Path(new DefaultHomeFinderService(session).find(), new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)), new TransferStatus()); final String lock = new DAVLockFeature(session).lock(test); assertEquals(TransferStatus.UNKNOWN_LENGTH, test.attributes().getSize()); final Path target = new Path(new DefaultHomeFinderService(session).find(), new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)); @@ -86,7 +86,7 @@ public class MicrosoftIISDAVMoveFeatureTest extends AbstractMicrosoftIISDAVTest @Test public void testMoveDirectory() throws Exception { final Path folder = new Path(new DefaultHomeFinderService(session).find(), new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory)); - new DAVDirectoryFeature(session).mkdir(folder, new TransferStatus()); + new DAVDirectoryFeature(session).mkdir(new DAVWriteFeature(session), folder, new TransferStatus()); final Path test = new Path(folder, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)); { final byte[] content = RandomUtils.nextBytes(3547); @@ -114,9 +114,9 @@ public class MicrosoftIISDAVMoveFeatureTest extends AbstractMicrosoftIISDAVTest @Test public void testMoveOverride() throws Exception { final Path test = new Path(new DefaultHomeFinderService(session).find(), new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)); - new DAVTouchFeature(session).touch(test, new TransferStatus()); + new DAVTouchFeature(session).touch(new DAVWriteFeature(session), test, new TransferStatus()); final Path target = new Path(new DefaultHomeFinderService(session).find(), new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)); - new DAVTouchFeature(session).touch(target, new TransferStatus()); + new DAVTouchFeature(session).touch(new DAVWriteFeature(session), target, new TransferStatus()); assertThrows(ConflictException.class, () -> new DAVMoveFeature(session).move(test, target, new TransferStatus().setExists(false), new Delete.DisabledCallback(), new DisabledConnectionCallback())); new DAVMoveFeature(session).move(test, target, new TransferStatus().setExists(true), new Delete.DisabledCallback(), new DisabledConnectionCallback()); assertFalse(new MicrosoftIISDAVFindFeature(session).find(test)); @@ -127,9 +127,9 @@ public class MicrosoftIISDAVMoveFeatureTest extends AbstractMicrosoftIISDAVTest @Test public void testMoveOverrideWithLock() throws Exception { final Path test = new Path(new DefaultHomeFinderService(session).find(), new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)); - new DAVTouchFeature(session).touch(test, new TransferStatus()); + new DAVTouchFeature(session).touch(new DAVWriteFeature(session), test, new TransferStatus()); final Path target = new Path(new DefaultHomeFinderService(session).find(), new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)); - new DAVTouchFeature(session).touch(target, new TransferStatus()); + new DAVTouchFeature(session).touch(new DAVWriteFeature(session), target, new TransferStatus()); assertThrows(ConflictException.class, () -> new DAVMoveFeature(session).move(test, target, new TransferStatus().setExists(false), new Delete.DisabledCallback(), new DisabledConnectionCallback())); final String lock = new DAVLockFeature(session).lock(test); assertThrows(LockedException.class, () -> new DAVMoveFeature(session).move(test, target, new TransferStatus().setExists(false), new Delete.DisabledCallback(), new DisabledConnectionCallback())); diff --git a/webdav/src/test/java/ch/cyberduck/core/dav/microsoft/MicrosoftIISDAVReadFeatureTest.java b/webdav/src/test/java/ch/cyberduck/core/dav/microsoft/MicrosoftIISDAVReadFeatureTest.java index 6bac49477c..f06817d01d 100644 --- a/webdav/src/test/java/ch/cyberduck/core/dav/microsoft/MicrosoftIISDAVReadFeatureTest.java +++ b/webdav/src/test/java/ch/cyberduck/core/dav/microsoft/MicrosoftIISDAVReadFeatureTest.java @@ -24,6 +24,7 @@ import ch.cyberduck.core.Local; import ch.cyberduck.core.Path; import ch.cyberduck.core.dav.DAVDeleteFeature; import ch.cyberduck.core.dav.DAVUploadFeature; +import ch.cyberduck.core.dav.DAVWriteFeature; import ch.cyberduck.core.features.Delete; import ch.cyberduck.core.io.BandwidthThrottle; import ch.cyberduck.core.io.DisabledStreamListener; @@ -66,7 +67,7 @@ public class MicrosoftIISDAVReadFeatureTest extends AbstractMicrosoftIISDAVTest IOUtils.write(content, out); out.close(); new DAVUploadFeature(session).upload( - test, local, new BandwidthThrottle(BandwidthThrottle.UNLIMITED), new DisabledProgressListener(), new DisabledStreamListener(), + new DAVWriteFeature(session), test, local, new BandwidthThrottle(BandwidthThrottle.UNLIMITED), new DisabledProgressListener(), new DisabledStreamListener(), new TransferStatus().setLength(content.length), new DisabledConnectionCallback()); final ExecutorService service = Executors.newCachedThreadPool(); diff --git a/webdav/src/test/java/ch/cyberduck/core/dav/microsoft/MicrosoftIISDAVTimestampFeatureTest.java b/webdav/src/test/java/ch/cyberduck/core/dav/microsoft/MicrosoftIISDAVTimestampFeatureTest.java index 526fdb9002..82fe1cde1c 100644 --- a/webdav/src/test/java/ch/cyberduck/core/dav/microsoft/MicrosoftIISDAVTimestampFeatureTest.java +++ b/webdav/src/test/java/ch/cyberduck/core/dav/microsoft/MicrosoftIISDAVTimestampFeatureTest.java @@ -23,6 +23,7 @@ import ch.cyberduck.core.Path; import ch.cyberduck.core.PathAttributes; import ch.cyberduck.core.dav.DAVDeleteFeature; import ch.cyberduck.core.dav.DAVTouchFeature; +import ch.cyberduck.core.dav.DAVWriteFeature; import ch.cyberduck.core.features.Delete; import ch.cyberduck.core.shared.DefaultHomeFinderService; import ch.cyberduck.core.transfer.TransferStatus; @@ -41,7 +42,7 @@ public class MicrosoftIISDAVTimestampFeatureTest extends AbstractMicrosoftIISDAV @Test public void testSetTimestamp() throws Exception { - final Path file = new DAVTouchFeature(session).touch(new Path(new DefaultHomeFinderService(session).find(), new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)), new TransferStatus()); + final Path file = new DAVTouchFeature(session).touch(new DAVWriteFeature(session), new Path(new DefaultHomeFinderService(session).find(), new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)), new TransferStatus()); assertTrue(new MicrosoftIISDAVFindFeature(session).find(file)); assertNotSame(PathAttributes.EMPTY, new MicrosoftIISDAVAttributesFinderFeature(session).find(file)); final TransferStatus status = new TransferStatus(); diff --git a/webdav/src/test/java/ch/cyberduck/core/dav/microsoft/MicrosoftIISDAVUploadFeatureTest.java b/webdav/src/test/java/ch/cyberduck/core/dav/microsoft/MicrosoftIISDAVUploadFeatureTest.java index d714233b0f..9699d47379 100644 --- a/webdav/src/test/java/ch/cyberduck/core/dav/microsoft/MicrosoftIISDAVUploadFeatureTest.java +++ b/webdav/src/test/java/ch/cyberduck/core/dav/microsoft/MicrosoftIISDAVUploadFeatureTest.java @@ -23,6 +23,7 @@ import ch.cyberduck.core.Local; import ch.cyberduck.core.Path; import ch.cyberduck.core.dav.DAVDeleteFeature; import ch.cyberduck.core.dav.DAVUploadFeature; +import ch.cyberduck.core.dav.DAVWriteFeature; import ch.cyberduck.core.exception.InteroperabilityException; import ch.cyberduck.core.features.Delete; import ch.cyberduck.core.io.BandwidthThrottle; @@ -58,7 +59,7 @@ public class MicrosoftIISDAVUploadFeatureTest extends AbstractMicrosoftIISDAVTes IOUtils.write(content, out); out.close(); new DAVUploadFeature(session).upload( - test, local, new BandwidthThrottle(BandwidthThrottle.UNLIMITED), new DisabledProgressListener(), new DisabledStreamListener(), + new DAVWriteFeature(session), test, local, new BandwidthThrottle(BandwidthThrottle.UNLIMITED), new DisabledProgressListener(), new DisabledStreamListener(), new TransferStatus().setLength(content.length), new DisabledConnectionCallback()); new DAVDeleteFeature(session).delete(Collections.singletonList(test), new DisabledLoginCallback(), new Delete.DisabledCallback()); @@ -78,7 +79,7 @@ public class MicrosoftIISDAVUploadFeatureTest extends AbstractMicrosoftIISDAVTes final ClientConnectionManager manager = session.getClient().getClient().getConnectionManager(); manager.closeIdleConnections(0L, TimeUnit.MILLISECONDS); assertThrows(InteroperabilityException.class, () -> new DAVUploadFeature(session).upload( - test, local, new BandwidthThrottle(BandwidthThrottle.UNLIMITED), new DisabledProgressListener(), new DisabledStreamListener(), + new DAVWriteFeature(session), test, local, new BandwidthThrottle(BandwidthThrottle.UNLIMITED), new DisabledProgressListener(), new DisabledStreamListener(), new TransferStatus().setLength(content.length), new DisabledConnectionCallback())); local.delete(); @@ -97,7 +98,7 @@ public class MicrosoftIISDAVUploadFeatureTest extends AbstractMicrosoftIISDAVTes final ClientConnectionManager manager = session.getClient().getClient().getConnectionManager(); manager.closeIdleConnections(0L, TimeUnit.MILLISECONDS); new DAVUploadFeature(session).upload( - test, local, new BandwidthThrottle(BandwidthThrottle.UNLIMITED), new DisabledProgressListener(), new DisabledStreamListener(), + new DAVWriteFeature(session), test, local, new BandwidthThrottle(BandwidthThrottle.UNLIMITED), new DisabledProgressListener(), new DisabledStreamListener(), new TransferStatus().setLength(content.length), new DisabledConnectionCallback()); new DAVDeleteFeature(session).delete(Collections.singletonList(test), new DisabledLoginCallback(), new Delete.DisabledCallback()); diff --git a/webdav/src/test/java/ch/cyberduck/core/shared/CachingAttributesFinderFeatureTest.java b/webdav/src/test/java/ch/cyberduck/core/shared/CachingAttributesFinderFeatureTest.java index feac3f53f7..197fa18fd2 100644 --- a/webdav/src/test/java/ch/cyberduck/core/shared/CachingAttributesFinderFeatureTest.java +++ b/webdav/src/test/java/ch/cyberduck/core/shared/CachingAttributesFinderFeatureTest.java @@ -26,6 +26,7 @@ import ch.cyberduck.core.PathCache; import ch.cyberduck.core.dav.AbstractDAVTest; import ch.cyberduck.core.dav.DAVDeleteFeature; import ch.cyberduck.core.dav.DAVTouchFeature; +import ch.cyberduck.core.dav.DAVWriteFeature; import ch.cyberduck.core.exception.NotfoundException; import ch.cyberduck.core.features.AttributesFinder; import ch.cyberduck.core.features.Delete; @@ -64,7 +65,7 @@ public class CachingAttributesFinderFeatureTest extends AbstractDAVTest { final PathCache cache = new PathCache(1); final AttributesFinder f = new CachingAttributesFinderFeature(session, cache, new DefaultAttributesFinderFeature(session)); final String name = new AlphanumericRandomStringService().random(); - final Path file = new DAVTouchFeature(session).touch(new Path(new DefaultHomeFinderService(session).find(), new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)), new TransferStatus()); + final Path file = new DAVTouchFeature(session).touch(new DAVWriteFeature(session), new Path(new DefaultHomeFinderService(session).find(), new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)), new TransferStatus()); final Attributes lookup = f.find(file); assertEquals(0L, lookup.getSize()); // Test cache diff --git a/webdav/src/test/java/ch/cyberduck/core/shared/DefaultAttributesFinderFeatureTest.java b/webdav/src/test/java/ch/cyberduck/core/shared/DefaultAttributesFinderFeatureTest.java index 3e7ba4ccfe..abea58a590 100644 --- a/webdav/src/test/java/ch/cyberduck/core/shared/DefaultAttributesFinderFeatureTest.java +++ b/webdav/src/test/java/ch/cyberduck/core/shared/DefaultAttributesFinderFeatureTest.java @@ -22,6 +22,7 @@ import ch.cyberduck.core.Path; import ch.cyberduck.core.dav.AbstractDAVTest; import ch.cyberduck.core.dav.DAVDeleteFeature; import ch.cyberduck.core.dav.DAVTouchFeature; +import ch.cyberduck.core.dav.DAVWriteFeature; import ch.cyberduck.core.exception.NotfoundException; import ch.cyberduck.core.features.AttributesFinder; import ch.cyberduck.core.features.Delete; @@ -49,7 +50,7 @@ public class DefaultAttributesFinderFeatureTest extends AbstractDAVTest { public void testAttributes() throws Exception { final AttributesFinder f = new DefaultAttributesFinderFeature(session); final String name = new AlphanumericRandomStringService().random(); - final Path file = new DAVTouchFeature(session).touch(new Path(new DefaultHomeFinderService(session).find(), new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)), new TransferStatus()); + final Path file = new DAVTouchFeature(session).touch(new DAVWriteFeature(session), new Path(new DefaultHomeFinderService(session).find(), new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)), new TransferStatus()); final Attributes attributes = f.find(file); assertEquals(0L, attributes.getSize()); // Test wrong type diff --git a/webdav/src/test/java/ch/cyberduck/core/worker/CopyWorkerTest.java b/webdav/src/test/java/ch/cyberduck/core/worker/CopyWorkerTest.java index 51e6764bcf..0c14bf8ead 100644 --- a/webdav/src/test/java/ch/cyberduck/core/worker/CopyWorkerTest.java +++ b/webdav/src/test/java/ch/cyberduck/core/worker/CopyWorkerTest.java @@ -25,6 +25,7 @@ import ch.cyberduck.core.dav.AbstractDAVTest; import ch.cyberduck.core.dav.DAVDirectoryFeature; import ch.cyberduck.core.dav.DAVFindFeature; import ch.cyberduck.core.dav.DAVTouchFeature; +import ch.cyberduck.core.dav.DAVWriteFeature; import ch.cyberduck.core.pool.SessionPool; import ch.cyberduck.core.shared.DefaultHomeFinderService; import ch.cyberduck.core.transfer.TransferStatus; @@ -47,7 +48,7 @@ public class CopyWorkerTest extends AbstractDAVTest { final Path home = new DefaultHomeFinderService(session).find(); 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 DAVTouchFeature(session).touch(source, new TransferStatus()); + new DAVTouchFeature(session).touch(new DAVWriteFeature(session), source, new TransferStatus()); assertTrue(new DAVFindFeature(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); @@ -60,11 +61,11 @@ public class CopyWorkerTest extends AbstractDAVTest { public void testCopyFileToDirectory() throws Exception { final Path home = new DefaultHomeFinderService(session).find(); final Path sourceFile = new Path(home, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)); - new DAVTouchFeature(session).touch(sourceFile, new TransferStatus()); + new DAVTouchFeature(session).touch(new DAVWriteFeature(session), sourceFile, new TransferStatus()); assertTrue(new DAVFindFeature(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 DAVDirectoryFeature(session).mkdir(targetFolder, new TransferStatus()); + new DAVDirectoryFeature(session).mkdir(new DAVWriteFeature(session), targetFolder, new TransferStatus()); assertTrue(new DAVFindFeature(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()); @@ -77,8 +78,8 @@ public class CopyWorkerTest extends AbstractDAVTest { @Test public void testCopyDirectory() throws Exception { final Path home = new DefaultHomeFinderService(session).find(); - final Path folder = new DAVDirectoryFeature(session).mkdir(new Path(home, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory)), new TransferStatus()); - final Path sourceFile = new DAVTouchFeature(session).touch(new Path(folder, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)), new TransferStatus()); + final Path folder = new DAVDirectoryFeature(session).mkdir(new DAVWriteFeature(session), new Path(home, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory)), new TransferStatus()); + final Path sourceFile = new DAVTouchFeature(session).touch(new DAVWriteFeature(session), new Path(folder, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)), new TransferStatus()); assertTrue(new DAVFindFeature(session).find(folder)); assertTrue(new DAVFindFeature(session).find(sourceFile)); final Path targetFolder = new Path(home, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory)); diff --git a/webdav/src/test/java/ch/cyberduck/core/worker/DAVConcurrentTransferWorkerTest.java b/webdav/src/test/java/ch/cyberduck/core/worker/DAVConcurrentTransferWorkerTest.java index 55d9076804..764b51d40a 100644 --- a/webdav/src/test/java/ch/cyberduck/core/worker/DAVConcurrentTransferWorkerTest.java +++ b/webdav/src/test/java/ch/cyberduck/core/worker/DAVConcurrentTransferWorkerTest.java @@ -22,6 +22,7 @@ import ch.cyberduck.core.dav.DAVDeleteFeature; import ch.cyberduck.core.dav.DAVReadFeature; import ch.cyberduck.core.dav.DAVSession; import ch.cyberduck.core.dav.DAVUploadFeature; +import ch.cyberduck.core.dav.DAVWriteFeature; import ch.cyberduck.core.exception.BackgroundException; import ch.cyberduck.core.features.Delete; import ch.cyberduck.core.features.Read; @@ -79,7 +80,7 @@ public class DAVConcurrentTransferWorkerTest extends AbstractDAVTest { assertNotNull(out); IOUtils.write(content, out); out.close(); - new DAVUploadFeature(session).upload(test, local, new BandwidthThrottle(BandwidthThrottle.UNLIMITED), new DisabledProgressListener(), new DisabledStreamListener(), new TransferStatus().setLength(content.length), new DisabledConnectionCallback()); + new DAVUploadFeature(session).upload(new DAVWriteFeature(session), test, local, new BandwidthThrottle(BandwidthThrottle.UNLIMITED), new DisabledProgressListener(), new DisabledStreamListener(), new TransferStatus().setLength(content.length), new DisabledConnectionCallback()); assertEquals(content.length, new DAVAttributesFinderFeature(session).find(test).getSize()); final Local localFile = new DefaultTemporaryFileService().create(test.getName()); final Transfer download = new DownloadTransfer(new Host(new TestProtocol()), Collections.singletonList(new TransferItem(test, localFile)), new NullFilter<>()); @@ -112,7 +113,7 @@ public class DAVConcurrentTransferWorkerTest extends AbstractDAVTest { assertNotNull(out); IOUtils.write(content, out); out.close(); - new DAVUploadFeature(session).upload(test, local, new BandwidthThrottle(BandwidthThrottle.UNLIMITED), new DisabledProgressListener(), new DisabledStreamListener(), new TransferStatus().setLength(content.length), new DisabledConnectionCallback()); + new DAVUploadFeature(session).upload(new DAVWriteFeature(session), test, local, new BandwidthThrottle(BandwidthThrottle.UNLIMITED), new DisabledProgressListener(), new DisabledStreamListener(), new TransferStatus().setLength(content.length), new DisabledConnectionCallback()); final AtomicBoolean failed = new AtomicBoolean(); final Host host = new Host(session.getHost()) { @Override diff --git a/webdav/src/test/java/ch/cyberduck/core/worker/DAVSingleTransferWorkerTest.java b/webdav/src/test/java/ch/cyberduck/core/worker/DAVSingleTransferWorkerTest.java index ff3fbbe438..3f8277d7b2 100644 --- a/webdav/src/test/java/ch/cyberduck/core/worker/DAVSingleTransferWorkerTest.java +++ b/webdav/src/test/java/ch/cyberduck/core/worker/DAVSingleTransferWorkerTest.java @@ -33,6 +33,7 @@ import ch.cyberduck.core.dav.DAVDeleteFeature; import ch.cyberduck.core.dav.DAVReadFeature; import ch.cyberduck.core.dav.DAVSession; import ch.cyberduck.core.dav.DAVUploadFeature; +import ch.cyberduck.core.dav.DAVWriteFeature; import ch.cyberduck.core.exception.BackgroundException; import ch.cyberduck.core.features.Delete; import ch.cyberduck.core.features.Read; @@ -82,7 +83,7 @@ public class DAVSingleTransferWorkerTest extends AbstractDAVTest { assertNotNull(out); IOUtils.write(content, out); out.close(); - new DAVUploadFeature(session).upload(test, local, new BandwidthThrottle(BandwidthThrottle.UNLIMITED), new DisabledProgressListener(), new DisabledStreamListener(), new TransferStatus().setLength(content.length), new DisabledConnectionCallback()); + new DAVUploadFeature(session).upload(new DAVWriteFeature(session), test, local, new BandwidthThrottle(BandwidthThrottle.UNLIMITED), new DisabledProgressListener(), new DisabledStreamListener(), new TransferStatus().setLength(content.length), new DisabledConnectionCallback()); final AtomicBoolean failed = new AtomicBoolean(); final Host host = new Host(session.getHost()) { @Override diff --git a/windows/build.xml b/windows/build.xml index 49087fb388..10c76114cb 100755 --- a/windows/build.xml +++ b/windows/build.xml @@ -60,11 +60,6 @@ - - - - - diff --git a/windows/pom.xml b/windows/pom.xml index 8d3622fe8f..d9d8852caa 100644 --- a/windows/pom.xml +++ b/windows/pom.xml @@ -18,7 +18,7 @@ parent ch.cyberduck - 9.2.0-SNAPSHOT + 9.4.0-SNAPSHOT 4.0.0 Cyberduck.Native diff --git a/windows/src/main/csharp/Cyberduck.csproj b/windows/src/main/csharp/Cyberduck.csproj index 80d5ead984..58b5010bf0 100644 --- a/windows/src/main/csharp/Cyberduck.csproj +++ b/windows/src/main/csharp/Cyberduck.csproj @@ -44,7 +44,7 @@ - all + build;buildMultitargeting;buildTransitive @@ -89,4 +89,4 @@ - \ No newline at end of file + diff --git a/windows/src/main/csharp/ch/cyberduck/core/CrashReporter.cs b/windows/src/main/csharp/ch/cyberduck/core/CrashReporter.cs index 83b07f0d1b..aec1b91846 100644 --- a/windows/src/main/csharp/ch/cyberduck/core/CrashReporter.cs +++ b/windows/src/main/csharp/ch/cyberduck/core/CrashReporter.cs @@ -54,7 +54,7 @@ namespace Ch.Cyberduck.Core ExceptionReportGenerator reportGenerator = new ExceptionReportGenerator(info); ExceptionReport report = reportGenerator.CreateExceptionReport(); - string crashDir = Path.Combine(SupportDirectoryFinderFactory.get().find().getAbsolute(), + string crashDir = Path.Combine(SupportDirectoryFinderFactory.get().find().PlatformPath(), "CrashReporter"); Directory.CreateDirectory(crashDir); using (StreamWriter outfile = new StreamWriter(Path.Combine(crashDir, DateTime.Now.Ticks + ".txt"))) diff --git a/windows/src/main/csharp/ch/cyberduck/ui/ViewModels/TransfersViewModel.cs b/windows/src/main/csharp/ch/cyberduck/ui/ViewModels/TransfersViewModel.cs index 6bed6d20a6..c0329a8f4c 100644 --- a/windows/src/main/csharp/ch/cyberduck/ui/ViewModels/TransfersViewModel.cs +++ b/windows/src/main/csharp/ch/cyberduck/ui/ViewModels/TransfersViewModel.cs @@ -424,11 +424,8 @@ public sealed partial class TransfersViewModel : ObservableObject, IDisposable private void OnRevealTransfer(TransferViewModel transfer) { - if (SelectedTransfers.Count is 0) - { - SelectedTransfer = transfer; - WeakReferenceMessenger.Default.Send(new BringIntoViewMessage(transfer)); - } + SelectedTransfer = transfer; + WeakReferenceMessenger.Default.Send(new BringIntoViewMessage(transfer)); } private void OnSelectedBandwidthChanged(BandwidthViewModel value) diff --git a/windows/src/main/csharp/ch/cyberduck/ui/controller/BookmarkController.cs b/windows/src/main/csharp/ch/cyberduck/ui/controller/BookmarkController.cs index 7ccf13ebac..119c499f1e 100644 --- a/windows/src/main/csharp/ch/cyberduck/ui/controller/BookmarkController.cs +++ b/windows/src/main/csharp/ch/cyberduck/ui/controller/BookmarkController.cs @@ -420,7 +420,7 @@ namespace Ch.Cyberduck.Ui.Controller } foreach (Protocol protocol in p.find(new DefaultProtocolPredicate( - EnumSet.of(Protocol.Type.file, Protocol.Type.none))).toArray(new Protocol[] { })) + EnumSet.of(Protocol.Type.file))).toArray(new Protocol[] { })) { protocols.Add(new KeyValueIconTriple(protocol, protocol.getDescription(), protocol.disk())); @@ -643,7 +643,7 @@ namespace Ch.Cyberduck.Ui.Controller private void View_ChangedTransferEvent() { - _host.setTransfer(View.SelectedTransferMode); + _host.setTransferType(View.SelectedTransferMode); ItemChanged(); } diff --git a/windows/src/main/csharp/ch/cyberduck/ui/controller/BrowserController.cs b/windows/src/main/csharp/ch/cyberduck/ui/controller/BrowserController.cs index 2c53565baf..15a18ec9cd 100644 --- a/windows/src/main/csharp/ch/cyberduck/ui/controller/BrowserController.cs +++ b/windows/src/main/csharp/ch/cyberduck/ui/controller/BrowserController.cs @@ -1687,7 +1687,7 @@ namespace Ch.Cyberduck.Ui.Controller for (int i = 0; i < _pasteboard.size(); i++) { Path next = (Path)_pasteboard.get(i); - Path renamed = new Path(parent, next.getName(), next.getType(), new PathAttributes(next.attributes())); + Path renamed = new Path(parent, next.getName(), next.getType(), new DefaultPathAttributes(next.attributes())); files.Add(next, renamed); } _pasteboard.clear(); @@ -2098,8 +2098,8 @@ namespace Ch.Cyberduck.Ui.Controller Location feature = (Location)Pool.getFeature(typeof(Location)); FolderController fc = new FolderController(ObjectFactory.GetInstance(), this, feature != null - ? (IList)Utils.ConvertFromJavaList(feature.getLocations()) - : new List(), feature != null ? feature.getDefault() : Location.unknown); + ? (IList)Utils.ConvertFromJavaList(feature.getLocations(Workdir)) + : new List(), feature != null ? feature.getDefault(Workdir) : Location.unknown); fc.Show(); } @@ -2108,8 +2108,8 @@ namespace Ch.Cyberduck.Ui.Controller Location feature = (Location)Pool.getFeature(typeof(Location)); FolderController fc = new VaultController(ObjectFactory.GetInstance(), this, feature != null - ? (IList)Utils.ConvertFromJavaList(feature.getLocations()) - : new List(), feature != null ? feature.getDefault() : Location.unknown); + ? (IList)Utils.ConvertFromJavaList(feature.getLocations(Workdir)) + : new List(), feature != null ? feature.getDefault(Workdir) : Location.unknown); fc.Show(); } diff --git a/windows/src/main/csharp/ch/cyberduck/ui/controller/InfoController.cs b/windows/src/main/csharp/ch/cyberduck/ui/controller/InfoController.cs index 6b38f6672b..9f23980278 100755 --- a/windows/src/main/csharp/ch/cyberduck/ui/controller/InfoController.cs +++ b/windows/src/main/csharp/ch/cyberduck/ui/controller/InfoController.cs @@ -352,7 +352,7 @@ namespace Ch.Cyberduck.Ui.Controller { AclPermission feature = (AclPermission)_controller.Pool.getFeature(typeof(AclPermission)); IDictionary mapping = new Dictionary(); - List aclUsers = feature.getAvailableAclUsers(); + List aclUsers = feature.getAvailableAclUsers(Utils.ConvertToJavaList(Files)); for (int i = 0; i < aclUsers.size(); i++) { Acl.User user = (Acl.User)aclUsers.get(i); @@ -1387,7 +1387,7 @@ namespace Ch.Cyberduck.Ui.Controller Redundancy redundancyFeature = (Redundancy)session.getFeature(typeof(Redundancy)); if (redundancyFeature != null) { - Iterator iter = redundancyFeature.getClasses().iterator(); + Iterator iter = redundancyFeature.getClasses(_container).iterator(); while (iter.hasNext()) { string redundancy = (string)iter.next(); diff --git a/windows/src/main/csharp/ch/cyberduck/ui/controller/PromptLoginController.cs b/windows/src/main/csharp/ch/cyberduck/ui/controller/PromptLoginController.cs index 626c6c2a2d..9c13f41af4 100644 --- a/windows/src/main/csharp/ch/cyberduck/ui/controller/PromptLoginController.cs +++ b/windows/src/main/csharp/ch/cyberduck/ui/controller/PromptLoginController.cs @@ -88,7 +88,7 @@ namespace Ch.Cyberduck.Ui.Controller public Credentials prompt(Host bookmark, String username, String title, String reason, LoginOptions options) { View = ObjectFactory.GetInstance(); - var credentials = new Credentials().withSaved(options.save()).withUsername(username); + var credentials = new Credentials().setSaved(options.save()).setUsername(username); InitEventHandlers(bookmark, credentials, options); @@ -119,6 +119,7 @@ namespace Ch.Cyberduck.Ui.Controller private void InitPrivateKeys() { + _keys.Clear(); foreach ( Local key in Utils.ConvertFromJavaList( diff --git a/windows/src/main/csharp/ch/cyberduck/ui/controller/PromptPasswordController.cs b/windows/src/main/csharp/ch/cyberduck/ui/controller/PromptPasswordController.cs index c5fac30970..7102453cda 100644 --- a/windows/src/main/csharp/ch/cyberduck/ui/controller/PromptPasswordController.cs +++ b/windows/src/main/csharp/ch/cyberduck/ui/controller/PromptPasswordController.cs @@ -44,7 +44,7 @@ namespace Ch.Cyberduck.Ui.Controller public Credentials prompt(Host bookmark, string title, string reason, LoginOptions options) { - Credentials credentials = new Credentials().withSaved(options.save()); + Credentials credentials = new Credentials().setSaved(options.save()); AsyncDelegate d = delegate { View = ObjectFactory.GetInstance(); diff --git a/windows/src/main/csharp/ch/cyberduck/ui/controller/VaultController.cs b/windows/src/main/csharp/ch/cyberduck/ui/controller/VaultController.cs index 8a2d8df955..ac029d3a55 100644 --- a/windows/src/main/csharp/ch/cyberduck/ui/controller/VaultController.cs +++ b/windows/src/main/csharp/ch/cyberduck/ui/controller/VaultController.cs @@ -95,7 +95,7 @@ namespace Ch.Cyberduck.Ui.Controller public InnerCreateVaultWorker(BrowserController controller, Path folder, String filename, String region, String passphrase) - : base(region, new VaultCredentials(passphrase).withSaved(false), VaultFactory.get(folder, + : base(region, new VaultCredentials(passphrase).setSaved(false), VaultFactory.get(folder, HostPreferencesFactory.get(controller.Pool.getHost()).getProperty("cryptomator.vault.masterkey.filename"), HostPreferencesFactory.get(controller.Pool.getHost()).getProperty("cryptomator.vault.config.filename"), Encoding.UTF8.GetBytes(HostPreferencesFactory.get(controller.Pool.getHost()).getProperty("cryptomator.vault.pepper")))) diff --git a/windows/src/main/csharp/ch/cyberduck/ui/winforms/BrowserForm.cs b/windows/src/main/csharp/ch/cyberduck/ui/winforms/BrowserForm.cs index 77108ac04b..4a953be707 100644 --- a/windows/src/main/csharp/ch/cyberduck/ui/winforms/BrowserForm.cs +++ b/windows/src/main/csharp/ch/cyberduck/ui/winforms/BrowserForm.cs @@ -755,11 +755,11 @@ namespace Ch.Cyberduck.Ui.Winforms public void AddTranscriptEntry(TranscriptListener.Type request, string entry) { transcriptBox.SelectionFont = FixedFont; - if (request == TranscriptListener.Type.request) + if (request == TranscriptListener.Type.request || request == TranscriptListener.Type.requestheader) { transcriptBox.SelectionColor = Color.Black; } - else if (request == TranscriptListener.Type.response) + else if (request == TranscriptListener.Type.response || request == TranscriptListener.Type.responseheader) { transcriptBox.SelectionColor = Color.DarkGray; } diff --git a/windows/src/main/package/package.wapproj b/windows/src/main/package/package.wapproj index 3ae1ed9c96..19bbcdc900 100644 --- a/windows/src/main/package/package.wapproj +++ b/windows/src/main/package/package.wapproj @@ -27,7 +27,7 @@ false - $(SignOutput) + false @@ -61,4 +61,41 @@ - \ No newline at end of file + + + + <_AppxPackagePayloadCopy Include="@(AppxPackagePayload)" Condition="Exists('$(TargetDir)%(TargetPath)')" /> + + + + + + + + + + + + $(SignAppxPackageExeFullPath) + + + + + + + + + + + + + + + diff --git a/windows/src/main/wix/Bootstrapper/Cyberduck.Bootstrapper.wixproj b/windows/src/main/wix/Bootstrapper/Cyberduck.Bootstrapper.wixproj index a49733496e..b090ab4e9d 100644 --- a/windows/src/main/wix/Bootstrapper/Cyberduck.Bootstrapper.wixproj +++ b/windows/src/main/wix/Bootstrapper/Cyberduck.Bootstrapper.wixproj @@ -1,4 +1,4 @@ - + Cyberduck-Installer-$(FileVersion) x64 @@ -16,4 +16,4 @@ - \ No newline at end of file + diff --git a/windows/src/main/wix/Bundle/Cyberduck.Bundle.wixproj b/windows/src/main/wix/Bundle/Cyberduck.Bundle.wixproj index b0c6812e83..9f43c7d55f 100644 --- a/windows/src/main/wix/Bundle/Cyberduck.Bundle.wixproj +++ b/windows/src/main/wix/Bundle/Cyberduck.Bundle.wixproj @@ -10,4 +10,4 @@ - \ No newline at end of file + diff --git a/windows/src/main/wix/Bundle/Cyberduck.wxs b/windows/src/main/wix/Bundle/Cyberduck.wxs index ccff03cd40..fb6e8fa5a6 100644 --- a/windows/src/main/wix/Bundle/Cyberduck.wxs +++ b/windows/src/main/wix/Bundle/Cyberduck.wxs @@ -205,4 +205,4 @@ - \ No newline at end of file + diff --git a/www/update/changelog.html b/www/update/changelog.html index 6d4c3fea39..230311d8fc 100755 --- a/www/update/changelog.html +++ b/www/update/changelog.html @@ -84,6 +84,113 @@ Mastodon.

+

+ Version 9.3.1 +

+
    +
  • Bugfix Validate tokens when using AWS S3 CLI connection profile (#17690) +
  • +
+ +

+ Version 9.3.0 +

+
    +
  • Feature Rewrite protocol implementation (Azure) (#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) +
  • +
  • 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) +
  • +
  • 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) +
  • +
  • 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) +
  • +
+ +

+ Version 9.2.4 +

+
    +
  • Bugfix Unable to close connection window with "Cancel" (macOS) (#17366) +
  • +
  • Bugfix Choose "Cancel" in upload prompt continues transfer (macOS) (#17358) +
  • +
  • Bugfix Change button styles for bottom bar of window (macOS) (#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) +
  • +
  • Bugfix Cache connection profiles loaded in Preferences → Profiles (#17432) +
  • +
+ +

+ Version 9.2.3 +

+
    +
  • Bugfix Failure opening application (Windows)
  • +
+ +

+ Version 9.2.2 +

+
    +
  • Bugfix Remove Dock Tile Plugin (macOS, Mac App Store)
  • +
+ +

+ Version 9.2.1 +

+
    +
  • Bugfix Unable to enter username in login prompt (macOS)
  • +
+ +

+ Version 9.2.0 +

+
    +
  • Feature Updated runtime (Windows) (#16854) +
  • +
  • Feature Add plugin to set application icon (macOS) (#17170) +
  • +
  • Feature Support for Mexico (Central) region (S3)
  • +
  • Feature Support for Asia Pacific (Thailand) region (S3)
  • +
  • Feature Support for Asia Pacific (Taipei) region (S3)
  • +
+

Version 9.1.7

@@ -106,8 +213,7 @@
  • Bugfix No attempt to use SSH agent for authentication (SFTP) (Windows)
  • Bugfix Share menu item not enabled (OneDrive, SharePoint) (#17082) + target="_blank" href="https://trac.cyberduck.io/ticket/17082">#17082)