Compare commits
11 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 66fc1e1284 | |||
| a1d489f5a5 | |||
| ad2699cd19 | |||
| 97ae86cedb | |||
| 71b07cac1b | |||
| a15f8f3809 | |||
| dc1270d8d1 | |||
| 68b1e23be3 | |||
| 057c9fd940 | |||
| 6d63322779 | |||
| fc6b46d17a |
+18
-1
@@ -1,10 +1,27 @@
|
||||
language: objective-c
|
||||
osx_image: xcode8
|
||||
xcode_project: FileProvider.xcodeproj
|
||||
env:
|
||||
global:
|
||||
- FRAMEWORK_NAME=FileProvider.framework
|
||||
before_install:
|
||||
- gem install xcpretty --no-rdoc --no-ri --no-document --quiet
|
||||
- brew update
|
||||
- brew outdated carthage || brew upgrade carthage
|
||||
script:
|
||||
- set pipefail
|
||||
- xcodebuild -version
|
||||
- xcodebuild -project FileProvider.xcodeproj -scheme "FileProvider OSX" -sdk macosx | xcpretty
|
||||
- xcodebuild -project FileProvider.xcodeproj -scheme "FileProvider iOS" -sdk iphonesimulator ONLY_ACTIVE_ARCH=NO | xcpretty
|
||||
- xcodebuild -project FileProvider.xcodeproj -scheme "FileProvider tvOS" -sdk appletvsimulator ONLY_ACTIVE_ARCH=NO | xcpretty
|
||||
- pod lib lint --quick
|
||||
after_success:
|
||||
- bash <(curl -s https://codecov.io/bash)
|
||||
- bash <(curl -s https://codecov.io/bash)
|
||||
before_deploy:
|
||||
- carthage build --no-skip-current
|
||||
- carthage archive FileProvider
|
||||
deploy:
|
||||
file: FileProvider.framework.zip
|
||||
skip_cleanup: true
|
||||
on:
|
||||
tags: true
|
||||
@@ -16,8 +16,8 @@ Pod::Spec.new do |s|
|
||||
#
|
||||
|
||||
s.name = "FileProvider"
|
||||
s.version = "0.5.1"
|
||||
s.summary = "FileManager replacement for Local and Remote (WebDAV/Dropbox/SMB2) files on iOS and MacOS."
|
||||
s.version = "0.6.1"
|
||||
s.summary = "FileManager replacement for Local and Remote (WebDAV/Dropbox/SMB2) files on iOS and macOS."
|
||||
|
||||
# This description is used to generate tags and improve search results.
|
||||
# * Think: What does it do? Why did you write it? What is the focus?
|
||||
@@ -26,8 +26,8 @@ Pod::Spec.new do |s|
|
||||
# * Finally, don't worry about the indent, CocoaPods strips it!
|
||||
s.description = <<-DESC
|
||||
This Swift library provide a swifty way to deal with local and remote files
|
||||
and directories in same way. For now Local and WebDAV providers are ready to use
|
||||
and SMB2, Dropbox, FTP and AmazonS3 is planned for future.
|
||||
and directories in same way. For now Local, WebDAV and Dropbox providers are ready to use.
|
||||
SMB2, FTP and AmazonS3 is planned for future.
|
||||
DESC
|
||||
|
||||
s.homepage = "https://github.com/amosavian/FileProvider"
|
||||
|
||||
@@ -7,9 +7,10 @@
|
||||
objects = {
|
||||
|
||||
/* Begin PBXBuildFile section */
|
||||
7902C0861D61B56D00564440 /* SessionDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7902C0851D61B56D00564440 /* SessionDelegate.swift */; };
|
||||
7902C0871D61B67100564440 /* SessionDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7902C0851D61B56D00564440 /* SessionDelegate.swift */; };
|
||||
7902C0881D61B67100564440 /* SessionDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7902C0851D61B56D00564440 /* SessionDelegate.swift */; };
|
||||
7902C0861D61B56D00564440 /* RemoteSession.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7902C0851D61B56D00564440 /* RemoteSession.swift */; };
|
||||
7902C0871D61B67100564440 /* RemoteSession.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7902C0851D61B56D00564440 /* RemoteSession.swift */; };
|
||||
7902C0881D61B67100564440 /* RemoteSession.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7902C0851D61B56D00564440 /* RemoteSession.swift */; };
|
||||
791950F51DE58A5400B4426E /* libxml2.tbd in Frameworks */ = {isa = PBXBuildFile; fileRef = 791950F41DE58A5400B4426E /* libxml2.tbd */; };
|
||||
7924B1931D89DAE000589DB7 /* AEXML.h in Headers */ = {isa = PBXBuildFile; fileRef = 7924B18C1D89DAE000589DB7 /* AEXML.h */; };
|
||||
7924B1941D89DAE000589DB7 /* AEXML.h in Headers */ = {isa = PBXBuildFile; fileRef = 7924B18C1D89DAE000589DB7 /* AEXML.h */; };
|
||||
7924B1951D89DAE000589DB7 /* AEXML.h in Headers */ = {isa = PBXBuildFile; fileRef = 7924B18C1D89DAE000589DB7 /* AEXML.h */; };
|
||||
@@ -97,7 +98,8 @@
|
||||
/* End PBXBuildFile section */
|
||||
|
||||
/* Begin PBXFileReference section */
|
||||
7902C0851D61B56D00564440 /* SessionDelegate.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SessionDelegate.swift; sourceTree = "<group>"; };
|
||||
7902C0851D61B56D00564440 /* RemoteSession.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RemoteSession.swift; sourceTree = "<group>"; };
|
||||
791950F41DE58A5400B4426E /* libxml2.tbd */ = {isa = PBXFileReference; lastKnownFileType = "sourcecode.text-based-dylib-definition"; name = libxml2.tbd; path = Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS10.1.sdk/usr/lib/libxml2.tbd; sourceTree = DEVELOPER_DIR; };
|
||||
7924B18C1D89DAE000589DB7 /* AEXML.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AEXML.h; sourceTree = "<group>"; };
|
||||
7924B18D1D89DAE000589DB7 /* Document.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Document.swift; sourceTree = "<group>"; };
|
||||
7924B18E1D89DAE000589DB7 /* Element.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Element.swift; sourceTree = "<group>"; };
|
||||
@@ -140,6 +142,7 @@
|
||||
isa = PBXFrameworksBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
791950F51DE58A5400B4426E /* libxml2.tbd in Frameworks */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
@@ -160,6 +163,14 @@
|
||||
/* End PBXFrameworksBuildPhase section */
|
||||
|
||||
/* Begin PBXGroup section */
|
||||
791950F31DE58A5300B4426E /* Frameworks */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
791950F41DE58A5400B4426E /* libxml2.tbd */,
|
||||
);
|
||||
name = Frameworks;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
7924B18B1D89DAE000589DB7 /* AEXML */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
@@ -180,6 +191,7 @@
|
||||
799396911D48C02300086753 /* Sources */,
|
||||
7993968A1D48B8C700086753 /* Pod */,
|
||||
799396681D48B7F600086753 /* Products */,
|
||||
791950F31DE58A5300B4426E /* Frameworks */,
|
||||
);
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
@@ -206,14 +218,14 @@
|
||||
799396911D48C02300086753 /* Sources */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
7924B18B1D89DAE000589DB7 /* AEXML */,
|
||||
799396991D48C02300086753 /* SMBTypes */,
|
||||
799396931D48C02300086753 /* DropboxFileProvider.swift */,
|
||||
7924B18B1D89DAE000589DB7 /* AEXML */,
|
||||
794C21FD1D58912A00EC49B8 /* DropboxHelper.swift */,
|
||||
799396941D48C02300086753 /* FileProvider.h */,
|
||||
799396951D48C02300086753 /* FileProvider.swift */,
|
||||
799396961D48C02300086753 /* LocalFileProvider.swift */,
|
||||
7902C0851D61B56D00564440 /* SessionDelegate.swift */,
|
||||
7902C0851D61B56D00564440 /* RemoteSession.swift */,
|
||||
7924B1A81D89F79200589DB7 /* FPSStreamTask.swift */,
|
||||
799396971D48C02300086753 /* SMBClient.swift */,
|
||||
799396981D48C02300086753 /* SMBFileProvider.swift */,
|
||||
@@ -332,7 +344,7 @@
|
||||
7993965C1D48B7BF00086753 /* Project object */ = {
|
||||
isa = PBXProject;
|
||||
attributes = {
|
||||
LastUpgradeCheck = 0800;
|
||||
LastUpgradeCheck = 0810;
|
||||
TargetAttributes = {
|
||||
799396661D48B7F600086753 = {
|
||||
CreatedOnToolsVersion = 7.3.1;
|
||||
@@ -413,7 +425,7 @@
|
||||
794C220A1D5893F800EC49B8 /* SMB2Notification.swift in Sources */,
|
||||
799396D11D48C02300086753 /* SMB2SetInfo.swift in Sources */,
|
||||
7924B1961D89DAE000589DB7 /* Document.swift in Sources */,
|
||||
7902C0861D61B56D00564440 /* SessionDelegate.swift in Sources */,
|
||||
7902C0861D61B56D00564440 /* RemoteSession.swift in Sources */,
|
||||
799396CE1D48C02300086753 /* SMB2Session.swift in Sources */,
|
||||
799396E01D48C02300086753 /* WebDAVFileProvider.swift in Sources */,
|
||||
799396DA1D48C02300086753 /* SMBErrorType.swift in Sources */,
|
||||
@@ -447,7 +459,7 @@
|
||||
794C220B1D5893F800EC49B8 /* SMB2Notification.swift in Sources */,
|
||||
799396D21D48C02300086753 /* SMB2SetInfo.swift in Sources */,
|
||||
7924B1971D89DAE000589DB7 /* Document.swift in Sources */,
|
||||
7902C0871D61B67100564440 /* SessionDelegate.swift in Sources */,
|
||||
7902C0871D61B67100564440 /* RemoteSession.swift in Sources */,
|
||||
799396CF1D48C02300086753 /* SMB2Session.swift in Sources */,
|
||||
799396E11D48C02300086753 /* WebDAVFileProvider.swift in Sources */,
|
||||
799396DB1D48C02300086753 /* SMBErrorType.swift in Sources */,
|
||||
@@ -481,7 +493,7 @@
|
||||
794C220C1D5893F800EC49B8 /* SMB2Notification.swift in Sources */,
|
||||
799396D31D48C02300086753 /* SMB2SetInfo.swift in Sources */,
|
||||
7924B1981D89DAE000589DB7 /* Document.swift in Sources */,
|
||||
7902C0881D61B67100564440 /* SessionDelegate.swift in Sources */,
|
||||
7902C0881D61B67100564440 /* RemoteSession.swift in Sources */,
|
||||
799396D01D48C02300086753 /* SMB2Session.swift in Sources */,
|
||||
799396E21D48C02300086753 /* WebDAVFileProvider.swift in Sources */,
|
||||
799396DC1D48C02300086753 /* SMBErrorType.swift in Sources */,
|
||||
@@ -501,6 +513,7 @@
|
||||
799396601D48B7BF00086753 /* Debug */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
BUNDLE_VERSION_STRING = 0.6.1;
|
||||
CLANG_WARN_BOOL_CONVERSION = YES;
|
||||
CLANG_WARN_CONSTANT_CONVERSION = YES;
|
||||
CLANG_WARN_EMPTY_BODY = YES;
|
||||
@@ -510,9 +523,12 @@
|
||||
CLANG_WARN_SUSPICIOUS_MOVE = YES;
|
||||
CLANG_WARN_UNREACHABLE_CODE = YES;
|
||||
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
|
||||
DEBUG_INFORMATION_FORMAT = dwarf;
|
||||
ENABLE_BITCODE = YES;
|
||||
ENABLE_STRICT_OBJC_MSGSEND = YES;
|
||||
ENABLE_TESTABILITY = YES;
|
||||
GCC_NO_COMMON_BLOCKS = YES;
|
||||
GCC_OPTIMIZATION_LEVEL = 0;
|
||||
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
|
||||
GCC_WARN_ABOUT_RETURN_TYPE = YES;
|
||||
GCC_WARN_UNDECLARED_SELECTOR = YES;
|
||||
@@ -520,12 +536,14 @@
|
||||
GCC_WARN_UNUSED_FUNCTION = YES;
|
||||
GCC_WARN_UNUSED_VARIABLE = YES;
|
||||
ONLY_ACTIVE_ARCH = YES;
|
||||
SWIFT_VERSION = 3.0.1;
|
||||
};
|
||||
name = Debug;
|
||||
};
|
||||
799396611D48B7BF00086753 /* Release */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
BUNDLE_VERSION_STRING = 0.6.1;
|
||||
CLANG_WARN_BOOL_CONVERSION = YES;
|
||||
CLANG_WARN_CONSTANT_CONVERSION = YES;
|
||||
CLANG_WARN_EMPTY_BODY = YES;
|
||||
@@ -535,6 +553,8 @@
|
||||
CLANG_WARN_SUSPICIOUS_MOVE = YES;
|
||||
CLANG_WARN_UNREACHABLE_CODE = YES;
|
||||
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
|
||||
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
|
||||
ENABLE_BITCODE = YES;
|
||||
ENABLE_STRICT_OBJC_MSGSEND = YES;
|
||||
GCC_NO_COMMON_BLOCKS = YES;
|
||||
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
|
||||
@@ -544,6 +564,7 @@
|
||||
GCC_WARN_UNUSED_FUNCTION = YES;
|
||||
GCC_WARN_UNUSED_VARIABLE = YES;
|
||||
SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule";
|
||||
SWIFT_VERSION = 3.0.1;
|
||||
};
|
||||
name = Release;
|
||||
};
|
||||
@@ -553,8 +574,6 @@
|
||||
ALWAYS_SEARCH_USER_PATHS = NO;
|
||||
APPLICATION_EXTENSION_API_ONLY = YES;
|
||||
CLANG_ANALYZER_NONNULL = YES;
|
||||
CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
|
||||
CLANG_CXX_LIBRARY = "libc++";
|
||||
CLANG_ENABLE_MODULES = YES;
|
||||
CLANG_ENABLE_OBJC_ARC = YES;
|
||||
CLANG_WARN_BOOL_CONVERSION = YES;
|
||||
@@ -566,20 +585,15 @@
|
||||
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
|
||||
CLANG_WARN_UNREACHABLE_CODE = YES;
|
||||
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
|
||||
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "";
|
||||
COPY_PHASE_STRIP = NO;
|
||||
CURRENT_PROJECT_VERSION = 1;
|
||||
DEBUG_INFORMATION_FORMAT = dwarf;
|
||||
DEFINES_MODULE = YES;
|
||||
DYLIB_COMPATIBILITY_VERSION = 1;
|
||||
DYLIB_CURRENT_VERSION = 1;
|
||||
DYLIB_INSTALL_NAME_BASE = "@rpath";
|
||||
ENABLE_STRICT_OBJC_MSGSEND = YES;
|
||||
ENABLE_TESTABILITY = YES;
|
||||
GCC_C_LANGUAGE_STANDARD = gnu99;
|
||||
GCC_DYNAMIC_NO_PIC = NO;
|
||||
GCC_NO_COMMON_BLOCKS = YES;
|
||||
GCC_OPTIMIZATION_LEVEL = 0;
|
||||
GCC_PREPROCESSOR_DEFINITIONS = (
|
||||
"DEBUG=1",
|
||||
"$(inherited)",
|
||||
@@ -601,7 +615,6 @@
|
||||
SDKROOT = iphoneos;
|
||||
SKIP_INSTALL = YES;
|
||||
SWIFT_OPTIMIZATION_LEVEL = "-Onone";
|
||||
SWIFT_VERSION = 3.0;
|
||||
TARGETED_DEVICE_FAMILY = "1,2";
|
||||
VERSIONING_SYSTEM = "apple-generic";
|
||||
VERSION_INFO_PREFIX = "";
|
||||
@@ -614,8 +627,6 @@
|
||||
ALWAYS_SEARCH_USER_PATHS = NO;
|
||||
APPLICATION_EXTENSION_API_ONLY = YES;
|
||||
CLANG_ANALYZER_NONNULL = YES;
|
||||
CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
|
||||
CLANG_CXX_LIBRARY = "libc++";
|
||||
CLANG_ENABLE_MODULES = YES;
|
||||
CLANG_ENABLE_OBJC_ARC = YES;
|
||||
CLANG_WARN_BOOL_CONVERSION = YES;
|
||||
@@ -627,17 +638,13 @@
|
||||
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
|
||||
CLANG_WARN_UNREACHABLE_CODE = YES;
|
||||
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
|
||||
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "";
|
||||
COPY_PHASE_STRIP = NO;
|
||||
CURRENT_PROJECT_VERSION = 1;
|
||||
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
|
||||
DEFINES_MODULE = YES;
|
||||
DYLIB_COMPATIBILITY_VERSION = 1;
|
||||
DYLIB_CURRENT_VERSION = 1;
|
||||
DYLIB_INSTALL_NAME_BASE = "@rpath";
|
||||
ENABLE_NS_ASSERTIONS = NO;
|
||||
ENABLE_STRICT_OBJC_MSGSEND = YES;
|
||||
GCC_C_LANGUAGE_STANDARD = gnu99;
|
||||
GCC_NO_COMMON_BLOCKS = YES;
|
||||
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
|
||||
GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
|
||||
@@ -654,7 +661,6 @@
|
||||
PRODUCT_NAME = FileProvider;
|
||||
SDKROOT = iphoneos;
|
||||
SKIP_INSTALL = YES;
|
||||
SWIFT_VERSION = 3.0;
|
||||
TARGETED_DEVICE_FAMILY = "1,2";
|
||||
VALIDATE_PRODUCT = YES;
|
||||
VERSIONING_SYSTEM = "apple-generic";
|
||||
@@ -681,11 +687,8 @@
|
||||
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
|
||||
CLANG_WARN_UNREACHABLE_CODE = YES;
|
||||
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
|
||||
CODE_SIGN_IDENTITY = "";
|
||||
COMBINE_HIDPI_IMAGES = YES;
|
||||
COPY_PHASE_STRIP = NO;
|
||||
CURRENT_PROJECT_VERSION = 1;
|
||||
DEBUG_INFORMATION_FORMAT = dwarf;
|
||||
DEFINES_MODULE = YES;
|
||||
DYLIB_COMPATIBILITY_VERSION = 1;
|
||||
DYLIB_CURRENT_VERSION = 1;
|
||||
@@ -696,7 +699,6 @@
|
||||
GCC_C_LANGUAGE_STANDARD = gnu99;
|
||||
GCC_DYNAMIC_NO_PIC = NO;
|
||||
GCC_NO_COMMON_BLOCKS = YES;
|
||||
GCC_OPTIMIZATION_LEVEL = 0;
|
||||
GCC_PREPROCESSOR_DEFINITIONS = (
|
||||
"DEBUG=1",
|
||||
"$(inherited)",
|
||||
@@ -742,11 +744,8 @@
|
||||
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
|
||||
CLANG_WARN_UNREACHABLE_CODE = YES;
|
||||
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
|
||||
CODE_SIGN_IDENTITY = "";
|
||||
COMBINE_HIDPI_IMAGES = YES;
|
||||
COPY_PHASE_STRIP = NO;
|
||||
CURRENT_PROJECT_VERSION = 1;
|
||||
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
|
||||
DEFINES_MODULE = YES;
|
||||
DYLIB_COMPATIBILITY_VERSION = 1;
|
||||
DYLIB_CURRENT_VERSION = 1;
|
||||
@@ -796,9 +795,7 @@
|
||||
CLANG_WARN_UNREACHABLE_CODE = YES;
|
||||
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
|
||||
"CODE_SIGN_IDENTITY[sdk=appletvos*]" = "";
|
||||
COPY_PHASE_STRIP = NO;
|
||||
CURRENT_PROJECT_VERSION = 1;
|
||||
DEBUG_INFORMATION_FORMAT = dwarf;
|
||||
DEFINES_MODULE = YES;
|
||||
DYLIB_COMPATIBILITY_VERSION = 1;
|
||||
DYLIB_CURRENT_VERSION = 1;
|
||||
@@ -808,7 +805,6 @@
|
||||
GCC_C_LANGUAGE_STANDARD = gnu99;
|
||||
GCC_DYNAMIC_NO_PIC = NO;
|
||||
GCC_NO_COMMON_BLOCKS = YES;
|
||||
GCC_OPTIMIZATION_LEVEL = 0;
|
||||
GCC_PREPROCESSOR_DEFINITIONS = (
|
||||
"DEBUG=1",
|
||||
"$(inherited)",
|
||||
@@ -830,7 +826,7 @@
|
||||
SKIP_INSTALL = YES;
|
||||
SWIFT_OPTIMIZATION_LEVEL = "-Onone";
|
||||
TARGETED_DEVICE_FAMILY = 3;
|
||||
TVOS_DEPLOYMENT_TARGET = 9.0;
|
||||
TVOS_DEPLOYMENT_TARGET = 10.0;
|
||||
VERSIONING_SYSTEM = "apple-generic";
|
||||
VERSION_INFO_PREFIX = "";
|
||||
};
|
||||
@@ -856,9 +852,7 @@
|
||||
CLANG_WARN_UNREACHABLE_CODE = YES;
|
||||
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
|
||||
"CODE_SIGN_IDENTITY[sdk=appletvos*]" = "";
|
||||
COPY_PHASE_STRIP = NO;
|
||||
CURRENT_PROJECT_VERSION = 1;
|
||||
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
|
||||
DEFINES_MODULE = YES;
|
||||
DYLIB_COMPATIBILITY_VERSION = 1;
|
||||
DYLIB_CURRENT_VERSION = 1;
|
||||
@@ -882,7 +876,7 @@
|
||||
SDKROOT = appletvos;
|
||||
SKIP_INSTALL = YES;
|
||||
TARGETED_DEVICE_FAMILY = 3;
|
||||
TVOS_DEPLOYMENT_TARGET = 9.0;
|
||||
TVOS_DEPLOYMENT_TARGET = 10.0;
|
||||
VALIDATE_PRODUCT = YES;
|
||||
VERSIONING_SYSTEM = "apple-generic";
|
||||
VERSION_INFO_PREFIX = "";
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<Scheme
|
||||
LastUpgradeVersion = "0800"
|
||||
LastUpgradeVersion = "0810"
|
||||
version = "1.3">
|
||||
<BuildAction
|
||||
parallelizeBuildables = "YES"
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<Scheme
|
||||
LastUpgradeVersion = "0800"
|
||||
LastUpgradeVersion = "0810"
|
||||
version = "1.3">
|
||||
<BuildAction
|
||||
parallelizeBuildables = "YES"
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<Scheme
|
||||
LastUpgradeVersion = "0800"
|
||||
LastUpgradeVersion = "0810"
|
||||
version = "1.3">
|
||||
<BuildAction
|
||||
parallelizeBuildables = "YES"
|
||||
|
||||
@@ -15,7 +15,7 @@
|
||||
<key>CFBundlePackageType</key>
|
||||
<string>FMWK</string>
|
||||
<key>CFBundleShortVersionString</key>
|
||||
<string>0.3.0</string>
|
||||
<string>${BUNDLE_VERSION_STRING}</string>
|
||||
<key>CFBundleSignature</key>
|
||||
<string>????</string>
|
||||
<key>CFBundleVersion</key>
|
||||
|
||||
+1
-1
@@ -15,7 +15,7 @@
|
||||
<key>CFBundlePackageType</key>
|
||||
<string>FMWK</string>
|
||||
<key>CFBundleShortVersionString</key>
|
||||
<string>0.3.0</string>
|
||||
<string>${BUNDLE_VERSION_STRING}</string>
|
||||
<key>CFBundleSignature</key>
|
||||
<string>????</string>
|
||||
<key>CFBundleVersion</key>
|
||||
|
||||
+1
-1
@@ -15,7 +15,7 @@
|
||||
<key>CFBundlePackageType</key>
|
||||
<string>FMWK</string>
|
||||
<key>CFBundleShortVersionString</key>
|
||||
<string>0.3.0</string>
|
||||
<string>${BUNDLE_VERSION_STRING}</string>
|
||||
<key>CFBundleSignature</key>
|
||||
<string>????</string>
|
||||
<key>CFBundleVersion</key>
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
# FileProvider (experimental)
|
||||
# FileProvider
|
||||
|
||||
>This Swift library provide a swifty way to deal with local and remote files and directories in a unified way.
|
||||
|
||||
[![Swift Version][swift-image]][swift-url]
|
||||
[![License][license-image]][license-url]
|
||||
[]()
|
||||
[]()
|
||||
|
||||
[![Build Status][travis-image]][travis-url]
|
||||
[](https://cocoapods.org/pods/FileProvider)
|
||||
@@ -16,7 +16,7 @@
|
||||
[](https://github.com/Carthage/Carthage)
|
||||
--->
|
||||
|
||||
This library provides implementaion of WebDav and SMB/CIFS (incomplete) and local files.
|
||||
This library provides implementaion of WebDav and SMB2 (incomplete) and local files.
|
||||
|
||||
All functions are async calls and it wont block your main thread.
|
||||
|
||||
@@ -27,7 +27,7 @@ Local and WebDAV providers are fully tested and can be used in production enviro
|
||||
- [x] **LocalFileProvider** a wrapper around `FileManager` with some additions like searching and reading a portion of file.
|
||||
- [x] **WebDAVFileProvider** WebDAV protocol is defacto file transmission standard, replaced FTP.
|
||||
- [x] **DropboxFileProvider** A wrapper around Dropbox Web API. For now it has limitation in uploading files up to 150MB.
|
||||
- [ ] **SMBFileProvider** SMB2/3 introduced in 2006, which is a file and printer sharing protocol originated from Microsoft Windows and now is replacing AFP protocol on MacOS. I implemented data types and some basic functions but *main interface is not implemented yet!*. SMB1/CIFS is depericated and very tricky to be implemented
|
||||
- [ ] **SMBFileProvider** SMB2/3 introduced in 2006, which is a file and printer sharing protocol originated from Microsoft Windows and now is replacing AFP protocol on MacOS. I implemented data types and some basic functions but *main interface is not implemented yet!* SMB1/CIFS is depericated and very tricky to be implemented
|
||||
- [ ] **FTPFileProvider** while deprecated in 1990s, it's still in use on some Web hosts.
|
||||
- [ ] **AmazonS3FileProvider**
|
||||
|
||||
@@ -85,8 +85,8 @@ You can't change the base url later. and all paths are related to this base url
|
||||
|
||||
For remote file providers authentication may be necessary:
|
||||
|
||||
let credential = URLCredential(user: "user", password: "pass", persistence: URLCredentialPersistence.Permanent)
|
||||
let webdavProvider = WebDAVFileProvider(baseURL: NSURL(string: "http://www.example.com/dav")!, credential: credential)
|
||||
let credential = URLCredential(user: "user", password: "pass", persistence: .permanent)
|
||||
let webdavProvider = WebDAVFileProvider(baseURL: URL(string: "http://www.example.com/dav")!, credential: credential)
|
||||
|
||||
* In case you want to connect non-secure servers for WebDAV (http) in iOS 9+ / macOS 10.11+ you should disable App Transport Security (ATS) according to [this guide.](https://gist.github.com/mlynch/284699d676fe9ed0abfa)
|
||||
|
||||
@@ -94,13 +94,13 @@ For remote file providers authentication may be necessary:
|
||||
|
||||
For interaction with UI, set delegate variable of `FileProvider` object
|
||||
|
||||
You can use `absoluteURL()` method if provider to get direct access url (local or remote files) for some file systems which allows to do so (Dropbox doesn't support and returns path simply)
|
||||
You can use `absoluteURL()` method if provider to get direct access url (local or remote files) for some file systems which allows to do so (Dropbox doesn't support and returns path simply wrapped in URL)
|
||||
|
||||
### Delegates
|
||||
|
||||
For updating User interface please consider using delegate method instead of completion handlers. Delegate methods are guaranteed to run in main thread to avoid bugs.
|
||||
|
||||
It's simply tree method which indicated whether the operation failed, succeed and how much of operation has been done (suitable for uploading and downloading operations).
|
||||
It's simply three method which indicated whether the operation failed, succeed and how much of operation has been done (suitable for uploading and downloading operations).
|
||||
|
||||
Your class should conforms `FileProviderDelegate` class:
|
||||
|
||||
@@ -139,7 +139,7 @@ Your class should conforms `FileProviderDelegate` class:
|
||||
}
|
||||
}
|
||||
|
||||
**Note:** `fileproviderProgress()` delegate method is not called by `LocalFileProvider`.
|
||||
**Note:** `fileproviderProgress()` delegate method is not called by `LocalFileProvider` currently.
|
||||
|
||||
It's recommended to use completion handlers for error handling or result processing.
|
||||
|
||||
@@ -147,9 +147,9 @@ It's recommended to use completion handlers for error handling or result process
|
||||
|
||||
You can also implement `FileOperationDelegate` protocol to control behaviour of file operation (copy, move/rename, remove and linking), and decide which files should be removed for example and which won't.
|
||||
|
||||
`fileProvider:shouldDoOperation:` method is called before doing a operation. You sould return `true` if you want to do operation or `false` if you want to stop that operation.
|
||||
`fileProvider(shouldDoOperation:)` method is called before doing a operation. You sould return `true` if you want to do operation or `false` if you want to stop that operation.
|
||||
|
||||
`fileProvider:shouldProceedAfterError:operation:` will be called if an error occured during file operations. Return `true` if you want to continue operation on next files or `false` if you want stop operation further. Default value is false if you don't implement delegate.
|
||||
`fileProvider(shouldProceedAfterError:, operation:)` will be called if an error occured during file operations. Return `true` if you want to continue operation on next files or `false` if you want stop operation further. Default value is false if you don't implement delegate.
|
||||
|
||||
**Note: these methods will be called for files in a directory and its subfolders recursively.**
|
||||
|
||||
@@ -164,8 +164,8 @@ For a single file:
|
||||
if let attributes = attributes {
|
||||
print("File Size: \(attributes.size)")
|
||||
print("Creation Date: \(attributes.createdDate)")
|
||||
print("Modification Date: \(modifiedDate)")
|
||||
print("Is Read Only: \(isReadOnly)")
|
||||
print("Modification Date: \(attributes.modifiedDate)")
|
||||
print("Is Read Only: \(attributes.isReadOnly)")
|
||||
}
|
||||
})
|
||||
|
||||
@@ -177,7 +177,7 @@ To get list of files in a directory:
|
||||
print("Name: \(attributes.name)")
|
||||
print("Size: \(attributes.size)")
|
||||
print("Creation Date: \(attributes.createdDate)")
|
||||
print("Modification Date: \(modifiedDate)")
|
||||
print("Modification Date: \(attributes.modifiedDate)")
|
||||
}
|
||||
})
|
||||
|
||||
@@ -186,17 +186,17 @@ To get size of strage and used/free space:
|
||||
func storageProperties(completionHandler: {(total: Int64, used: Int64) -> Void in
|
||||
print("Total Storage Space: \(total)")
|
||||
print("Used Space: \(used)")
|
||||
print("Free Space: \(total - frees)")
|
||||
print("Free Space: \(total - used)")
|
||||
})
|
||||
|
||||
* if this function is unavailable on provider or an error has been occurred, total space will be reported "-1" and used space "0"
|
||||
* if this function is unavailable on provider or an error has been occurred, total space will be reported `-1` and used space `0`
|
||||
|
||||
### Change current directory
|
||||
|
||||
documentsProvider.currentPath = "/New Folder"
|
||||
// now path is ~/Documents/New Folder
|
||||
|
||||
You can then pass "" (empty string) to contentsOfDirectoryAtPath method to list files in current directory.
|
||||
You can then pass "" (empty string) to `contentsOfDirectory` method to list files in current directory.
|
||||
|
||||
### Creating File and Folders
|
||||
|
||||
@@ -220,6 +220,8 @@ Move file old.txt to new.txt in current path:
|
||||
|
||||
documentsProvider.moveItem(path: "new folder/old.txt", to: "new.txt", overwrite: false, completionHandler: nil)
|
||||
|
||||
**Note:** To have a consistent behaviour, create intermediate directories first if necessary.
|
||||
|
||||
### Delete Files
|
||||
|
||||
documentsProvider.removeItem(path: "new.txt", completionHandler: nil)
|
||||
@@ -237,7 +239,7 @@ There is two method for this purpose, one of them loads entire file into NSData
|
||||
}
|
||||
})
|
||||
|
||||
If you want to retrieve a portion of file you should can `contentsAtPath` method with offset and length arguments. Please note first byte of file has offset: 0.
|
||||
If you want to retrieve a portion of file you can use `contents` method with offset and length arguments. Please note first byte of file has offset: 0.
|
||||
|
||||
documentsProvider.contents(path: "old.txt", offset: 2, length: 5, completionHandler: {
|
||||
(contents: Data?, error: ErrorType?) -> Void
|
||||
@@ -251,6 +253,12 @@ If you want to retrieve a portion of file you should can `contentsAtPath` method
|
||||
let data = "What's up Newyork!".data(encoding: String.encoding.utf8)
|
||||
documentsProvider.writeContents(path: "old.txt", content: data, atomically: true, completionHandler: nil)
|
||||
|
||||
### Operation Handle
|
||||
|
||||
Creating/Copying/Deleting functions return a `OperationHandle` for remote operations. It provides progress and a `.cancel()` method which allows you to cancel operation in midst.
|
||||
|
||||
It's not supported by native `(NS)FileManager` so `LocalFileProvider`, but this functionality will be added to future `PosixFileProvider` class.
|
||||
|
||||
### Monitoring FIle Changes
|
||||
|
||||
You can monitor updates in some file system (Local and SMB2), there is three methods in supporting provider you can use to register a handler, to unregister and to check whether it's being monitored or not. It's useful to find out when new files added or removed from directory and update user interface. The handler will be dispatched to main threads to avoid UI bugs with a 0.25 sec delay.
|
||||
|
||||
@@ -62,22 +62,18 @@ open class DropboxFileProvider: NSObject, FileProviderBasic {
|
||||
request.setValue("Bearer \(credential?.password ?? "")", forHTTPHeaderField: "Authorization")
|
||||
request.setValue("application/json", forHTTPHeaderField: "Content-Type")
|
||||
let requestDictionary = ["path": correctPath(path)! as NSString]
|
||||
request.httpBody = dictionaryToJSON(requestDictionary)?.data(using: String.Encoding.utf8)
|
||||
request.httpBody = dictionaryToJSON(requestDictionary)?.data(using: .utf8)
|
||||
let task = session.dataTask(with: request, completionHandler: { (data, response, error) in
|
||||
var dbError: FileProviderDropboxError?
|
||||
var fileObject: DropboxFileObject?
|
||||
if let response = response as? HTTPURLResponse {
|
||||
defer {
|
||||
self.delegateNotify(FileOperation.create(path: path), error: error)
|
||||
}
|
||||
let code = FileProviderHTTPErrorCode(rawValue: response.statusCode)
|
||||
let dbError: FileProviderDropboxError? = code != nil ? FileProviderDropboxError(code: code!, path: path, errorDescription: String(data: data ?? Data(), encoding: String.Encoding.utf8)) : nil
|
||||
if let data = data, let jsonStr = String(data: data, encoding: String.Encoding.utf8), let json = jsonToDictionary(jsonStr), let file = self.mapToFileObject(json) {
|
||||
completionHandler(file, dbError)
|
||||
return
|
||||
dbError = code != nil ? FileProviderDropboxError(code: code!, path: path, errorDescription: String(data: data ?? Data(), encoding: .utf8)) : nil
|
||||
if let data = data, let jsonStr = String(data: data, encoding: .utf8), let json = jsonToDictionary(jsonStr), let file = self.mapToFileObject(json) {
|
||||
fileObject = file
|
||||
}
|
||||
completionHandler(nil, dbError)
|
||||
return
|
||||
}
|
||||
completionHandler(nil, error)
|
||||
completionHandler(fileObject, dbError ?? error)
|
||||
})
|
||||
task.resume()
|
||||
}
|
||||
@@ -88,13 +84,13 @@ open class DropboxFileProvider: NSObject, FileProviderBasic {
|
||||
request.httpMethod = "POST"
|
||||
request.setValue("Bearer \(credential?.password ?? "")", forHTTPHeaderField: "Authorization")
|
||||
let task = session.dataTask(with: request, completionHandler: { (data, response, error) in
|
||||
if let data = data, let jsonStr = String(data: data, encoding: String.Encoding.utf8), let json = jsonToDictionary(jsonStr) {
|
||||
let totalSize = ((json["allocation"] as? NSDictionary)?["allocated"] as? NSNumber)?.int64Value ?? -1
|
||||
let usedSize = (json["used"] as? NSNumber)?.int64Value ?? 0
|
||||
completionHandler(totalSize, usedSize)
|
||||
return
|
||||
var totalSize: Int64 = -1
|
||||
var usedSize: Int64 = 0
|
||||
if let data = data, let jsonStr = String(data: data, encoding: .utf8), let json = jsonToDictionary(jsonStr) {
|
||||
totalSize = ((json["allocation"] as? NSDictionary)?["allocated"] as? NSNumber)?.int64Value ?? -1
|
||||
usedSize = (json["used"] as? NSNumber)?.int64Value ?? 0
|
||||
}
|
||||
completionHandler(-1, 0)
|
||||
completionHandler(totalSize, usedSize)
|
||||
})
|
||||
task.resume()
|
||||
}
|
||||
@@ -104,29 +100,32 @@ open class DropboxFileProvider: NSObject, FileProviderBasic {
|
||||
|
||||
extension DropboxFileProvider: FileProviderOperations {
|
||||
|
||||
|
||||
public func create(folder folderName: String, at atPath: String, completionHandler: SimpleCompletionHandler) {
|
||||
|
||||
public func create(folder folderName: String, at atPath: String, completionHandler: SimpleCompletionHandler) -> OperationHandle? {
|
||||
let path = (atPath as NSString).appendingPathComponent(folderName) + "/"
|
||||
doOperation(.create(path: path), completionHandler: completionHandler)
|
||||
return doOperation(.create(path: path), completionHandler: completionHandler)
|
||||
}
|
||||
|
||||
public func create(file fileAttribs: FileObject, at path: String, contents data: Data?, completionHandler: SimpleCompletionHandler) {
|
||||
self.writeContents(path: path, contents: data ?? Data(), completionHandler: completionHandler)
|
||||
public func create(file fileAttribs: FileObject, at path: String, contents data: Data?, completionHandler: SimpleCompletionHandler) -> OperationHandle? {
|
||||
return self.writeContents(path: path, contents: data ?? Data(), completionHandler: completionHandler)
|
||||
}
|
||||
|
||||
public func moveItem(path: String, to toPath: String, overwrite: Bool = false, completionHandler: SimpleCompletionHandler) {
|
||||
doOperation(.move(source: path, destination: toPath), completionHandler: completionHandler)
|
||||
public func moveItem(path: String, to toPath: String, overwrite: Bool = false, completionHandler: SimpleCompletionHandler) -> OperationHandle? {
|
||||
return doOperation(.move(source: path, destination: toPath), completionHandler: completionHandler)
|
||||
}
|
||||
|
||||
public func copyItem(path: String, to toPath: String, overwrite: Bool = false, completionHandler: SimpleCompletionHandler) {
|
||||
doOperation(.copy(source: path, destination: toPath), completionHandler: completionHandler)
|
||||
public func copyItem(path: String, to toPath: String, overwrite: Bool = false, completionHandler: SimpleCompletionHandler) -> OperationHandle? {
|
||||
return doOperation(.copy(source: path, destination: toPath), completionHandler: completionHandler)
|
||||
}
|
||||
|
||||
public func removeItem(path: String, completionHandler: SimpleCompletionHandler) {
|
||||
doOperation(.remove(path: path), completionHandler: completionHandler)
|
||||
public func removeItem(path: String, completionHandler: SimpleCompletionHandler) -> OperationHandle? {
|
||||
return doOperation(.remove(path: path), completionHandler: completionHandler)
|
||||
}
|
||||
|
||||
fileprivate func doOperation(_ operation: FileOperation, completionHandler: SimpleCompletionHandler) {
|
||||
fileprivate func doOperation(_ operation: FileOperationType, completionHandler: SimpleCompletionHandler) -> OperationHandle? {
|
||||
guard fileOperationDelegate?.fileProvider(self, shouldDoOperation: operation) ?? true == true else {
|
||||
return nil
|
||||
}
|
||||
let url: String
|
||||
var path: String?, fromPath: String?, toPath: String?
|
||||
switch operation {
|
||||
@@ -141,13 +140,13 @@ extension DropboxFileProvider: FileProviderOperations {
|
||||
url = "https://api.dropboxapi.com/2/files/move"
|
||||
fromPath = fp
|
||||
toPath = tp
|
||||
case .modify(path: let p):
|
||||
return
|
||||
case .remove(path: let p):
|
||||
url = "https://api.dropboxapi.com/2/files/delete"
|
||||
path = p
|
||||
case .modify(path: _):
|
||||
return nil
|
||||
case .link(link: _, target: _):
|
||||
return
|
||||
return nil
|
||||
}
|
||||
var request = URLRequest(url: URL(string: url)!)
|
||||
request.httpMethod = "POST"
|
||||
@@ -157,35 +156,35 @@ extension DropboxFileProvider: FileProviderOperations {
|
||||
requestDictionary["path"] = correctPath(path) as NSString?
|
||||
requestDictionary["from_path"] = correctPath(fromPath) as NSString?
|
||||
requestDictionary["to_path"] = correctPath(toPath) as NSString?
|
||||
request.httpBody = dictionaryToJSON(requestDictionary)?.data(using: String.Encoding.utf8)
|
||||
request.httpBody = dictionaryToJSON(requestDictionary)?.data(using: .utf8)
|
||||
let task = session.dataTask(with: request, completionHandler: { (data, response, error) in
|
||||
if let response = response as? HTTPURLResponse {
|
||||
let code = FileProviderHTTPErrorCode(rawValue: response.statusCode)
|
||||
let dbError: FileProviderDropboxError? = code != nil ? FileProviderDropboxError(code: code!, path: path ?? fromPath ?? "", errorDescription: String(data: data ?? Data(), encoding: String.Encoding.utf8)) : nil
|
||||
defer {
|
||||
self.delegateNotify(operation, error: error ?? dbError)
|
||||
}
|
||||
/*if let data = data, let jsonStr = String(data: data, encoding: NSUTF8StringEncoding) {
|
||||
let json = self.jsonToDictionary(jsonStr)
|
||||
}*/
|
||||
completionHandler?(dbError)
|
||||
return
|
||||
var dbError: FileProviderDropboxError?
|
||||
if let response = response as? HTTPURLResponse, response.statusCode >= 300, let code = FileProviderHTTPErrorCode(rawValue: response.statusCode) {
|
||||
dbError = FileProviderDropboxError(code: code, path: path ?? fromPath ?? "", errorDescription: String(data: data ?? Data(), encoding: .utf8))
|
||||
}
|
||||
completionHandler?(error)
|
||||
completionHandler?(dbError ?? error)
|
||||
self.delegateNotify(operation, error: dbError ?? error)
|
||||
})
|
||||
task.resume()
|
||||
return RemoteOperationHandle(tasks: [task])
|
||||
}
|
||||
|
||||
public func copyItem(localFile: URL, to toPath: String, completionHandler: SimpleCompletionHandler) {
|
||||
guard let data = try? Data(contentsOf: localFile) else {
|
||||
let error = throwError(localFile.uw_absoluteString, code: URLError.fileDoesNotExist as FoundationErrorEnum)
|
||||
completionHandler?(error)
|
||||
return
|
||||
public func copyItem(localFile: URL, to toPath: String, completionHandler: SimpleCompletionHandler) -> OperationHandle? {
|
||||
guard fileOperationDelegate?.fileProvider(self, shouldDoOperation: .copy(source: localFile.absoluteString, destination: toPath)) ?? true == true else {
|
||||
return nil
|
||||
}
|
||||
upload_simple(toPath, data: data, overwrite: true, operation: .copy(source: localFile.uw_absoluteString, destination: toPath), completionHandler: completionHandler)
|
||||
guard let data = try? Data(contentsOf: localFile) else {
|
||||
let error = throwError(localFile.absoluteString, code: URLError.fileDoesNotExist as FoundationErrorEnum)
|
||||
completionHandler?(error)
|
||||
return nil
|
||||
}
|
||||
return upload_simple(toPath, data: data, overwrite: true, operation: .copy(source: localFile.absoluteString, destination: toPath), completionHandler: completionHandler)
|
||||
}
|
||||
|
||||
public func copyItem(path: String, toLocalURL destURL: URL, completionHandler: SimpleCompletionHandler) {
|
||||
public func copyItem(path: String, toLocalURL destURL: URL, completionHandler: SimpleCompletionHandler) -> OperationHandle? {
|
||||
guard fileOperationDelegate?.fileProvider(self, shouldDoOperation: .copy(source: path, destination: destURL.absoluteString)) ?? true == true else {
|
||||
return nil
|
||||
}
|
||||
let url = URL(string: "https://content.dropboxapi.com/2/files/download")!
|
||||
var request = URLRequest(url: url)
|
||||
request.httpMethod = "GET"
|
||||
@@ -207,17 +206,18 @@ extension DropboxFileProvider: FileProviderOperations {
|
||||
completionHandler?(e)
|
||||
}
|
||||
})
|
||||
task.taskDescription = dictionaryToJSON(["type": "Copy" as NSString, "source": path as NSString, "dest": destURL.uw_absoluteString as NSString])
|
||||
task.taskDescription = dictionaryToJSON(["type": "Copy" as NSString, "source": path as NSString, "dest": destURL.absoluteString as NSString])
|
||||
task.resume()
|
||||
return RemoteOperationHandle(tasks: [task])
|
||||
}
|
||||
}
|
||||
|
||||
extension DropboxFileProvider: FileProviderReadWrite {
|
||||
public func contents(path: String, completionHandler: @escaping ((_ contents: Data?, _ error: Error?) -> Void)) {
|
||||
self.contents(path: path, offset: 0, length: -1, completionHandler: completionHandler)
|
||||
public func contents(path: String, completionHandler: @escaping ((_ contents: Data?, _ error: Error?) -> Void)) -> OperationHandle? {
|
||||
return self.contents(path: path, offset: 0, length: -1, completionHandler: completionHandler)
|
||||
}
|
||||
|
||||
public func contents(path: String, offset: Int64, length: Int, completionHandler: @escaping ((_ contents: Data?, _ error: Error?) -> Void)) {
|
||||
public func contents(path: String, offset: Int64, length: Int, completionHandler: @escaping ((_ contents: Data?, _ error: Error?) -> Void)) -> OperationHandle? {
|
||||
let url = URL(string: "https://content.dropboxapi.com/2/files/download")!
|
||||
var request = URLRequest(url: url)
|
||||
request.httpMethod = "GET"
|
||||
@@ -229,21 +229,24 @@ extension DropboxFileProvider: FileProviderReadWrite {
|
||||
}
|
||||
let requestDictionary = ["path": path]
|
||||
request.setValue(dictionaryToJSON(requestDictionary as [String : AnyObject]), forHTTPHeaderField: "Dropbox-API-Arg")
|
||||
let task = session.dataTask(with: request, completionHandler: { (datam, response, error) in
|
||||
guard let data = datam, let httpResponse = response as? HTTPURLResponse , httpResponse.statusCode < 300 else {
|
||||
let code = FileProviderHTTPErrorCode(rawValue: (response as? HTTPURLResponse)?.statusCode ?? -1)
|
||||
let dbError: FileProviderDropboxError? = code != nil ? FileProviderDropboxError(code: code!, path: path, errorDescription: String(data: datam ?? Data(), encoding: String.Encoding.utf8)) : nil
|
||||
completionHandler(nil, dbError ?? error)
|
||||
return
|
||||
let task = session.dataTask(with: request, completionHandler: { (data, response, error) in
|
||||
var dbError: FileProviderDropboxError?
|
||||
if let httpResponse = response as? HTTPURLResponse , httpResponse.statusCode >= 300, let code = FileProviderHTTPErrorCode(rawValue: httpResponse.statusCode) {
|
||||
dbError = FileProviderDropboxError(code: code, path: path, errorDescription: String(data: data ?? Data(), encoding: .utf8))
|
||||
}
|
||||
completionHandler(data, error)
|
||||
let filedata = dbError ?? error == nil ? data : nil
|
||||
completionHandler(filedata, dbError ?? error)
|
||||
})
|
||||
task.resume()
|
||||
return RemoteOperationHandle(tasks: [task])
|
||||
}
|
||||
|
||||
public func writeContents(path: String, contents data: Data, atomically: Bool = false, completionHandler: SimpleCompletionHandler) {
|
||||
public func writeContents(path: String, contents data: Data, atomically: Bool = false, completionHandler: SimpleCompletionHandler) -> OperationHandle? {
|
||||
guard fileOperationDelegate?.fileProvider(self, shouldDoOperation: .modify(path: path)) ?? true == true else {
|
||||
return nil
|
||||
}
|
||||
// FIXME: remove 150MB restriction
|
||||
upload_simple(path, data: data, overwrite: true, operation: .modify(path: path), completionHandler: completionHandler)
|
||||
return upload_simple(path, data: data, overwrite: true, operation: .modify(path: path), completionHandler: completionHandler)
|
||||
}
|
||||
|
||||
public func searchFiles(path: String, recursive: Bool, query: String, foundItemHandler: ((FileObject) -> Void)?, completionHandler: @escaping ((_ files: [FileObject], _ error: Error?) -> Void)) {
|
||||
|
||||
+14
-17
@@ -50,16 +50,16 @@ internal extension DropboxFileProvider {
|
||||
request.httpMethod = "POST"
|
||||
request.setValue("Bearer \(credential?.password ?? "")", forHTTPHeaderField: "Authorization")
|
||||
request.setValue("application/json", forHTTPHeaderField: "Content-Type")
|
||||
request.httpBody = dictionaryToJSON(requestDictionary)?.data(using: String.Encoding.utf8)
|
||||
request.httpBody = dictionaryToJSON(requestDictionary)?.data(using: .utf8)
|
||||
let task = session.dataTask(with: request, completionHandler: { (data, response, error) in
|
||||
var responseError: FileProviderDropboxError?
|
||||
var files = prevContents
|
||||
if let code = (response as? HTTPURLResponse)?.statusCode , code >= 300, let rCode = FileProviderHTTPErrorCode(rawValue: code) {
|
||||
responseError = FileProviderDropboxError(code: rCode, path: path, errorDescription: String(data: data ?? Data(), encoding: String.Encoding.utf8))
|
||||
responseError = FileProviderDropboxError(code: rCode, path: path, errorDescription: String(data: data ?? Data(), encoding: .utf8))
|
||||
}
|
||||
if let data = data, let jsonStr = String(data: data, encoding: String.Encoding.utf8) {
|
||||
if let data = data, let jsonStr = String(data: data, encoding: .utf8) {
|
||||
let json = jsonToDictionary(jsonStr)
|
||||
if let entries = json?["entries"] as? [AnyObject] , entries.count > 0 {
|
||||
var files = prevContents
|
||||
for entry in entries {
|
||||
if let entry = entry as? [String: AnyObject], let file = self.mapToFileObject(entry) {
|
||||
files.append(file)
|
||||
@@ -69,18 +69,16 @@ internal extension DropboxFileProvider {
|
||||
let hasmore = (json?["has_more"] as? NSNumber)?.boolValue ?? false
|
||||
if hasmore {
|
||||
self.list(path, cursor: ncursor, prevContents: files, completionHandler: completionHandler)
|
||||
} else {
|
||||
completionHandler(files, ncursor, responseError ?? error)
|
||||
return
|
||||
}
|
||||
return
|
||||
}
|
||||
}
|
||||
completionHandler([], nil, responseError ?? error)
|
||||
completionHandler(files, nil, responseError ?? error)
|
||||
})
|
||||
task.resume()
|
||||
}
|
||||
|
||||
func upload_simple(_ targetPath: String, data: Data, modifiedDate: Date = Date(), overwrite: Bool, operation: FileOperation, completionHandler: SimpleCompletionHandler) {
|
||||
func upload_simple(_ targetPath: String, data: Data, modifiedDate: Date = Date(), overwrite: Bool, operation: FileOperationType, completionHandler: SimpleCompletionHandler) -> OperationHandle? {
|
||||
assert(data.count < 150*1024*1024, "Maximum size of allowed size to upload is 150MB")
|
||||
var requestDictionary = [String: AnyObject]()
|
||||
let url: URL
|
||||
@@ -99,12 +97,10 @@ internal extension DropboxFileProvider {
|
||||
let task = session.uploadTask(with: request, from: data, completionHandler: { (data, response, error) in
|
||||
var responseError: FileProviderDropboxError?
|
||||
if let code = (response as? HTTPURLResponse)?.statusCode , code >= 300, let rCode = FileProviderHTTPErrorCode(rawValue: code) {
|
||||
responseError = FileProviderDropboxError(code: rCode, path: targetPath, errorDescription: String(data: data ?? Data(), encoding: String.Encoding.utf8))
|
||||
}
|
||||
defer {
|
||||
self.delegateNotify(.create(path: targetPath), error: responseError ?? error)
|
||||
responseError = FileProviderDropboxError(code: rCode, path: targetPath, errorDescription: String(data: data ?? Data(), encoding: .utf8))
|
||||
}
|
||||
completionHandler?(responseError ?? error)
|
||||
self.delegateNotify(.create(path: targetPath), error: responseError ?? error)
|
||||
})
|
||||
var dic: [String: AnyObject] = ["type": operation.description as NSString]
|
||||
switch operation {
|
||||
@@ -123,6 +119,7 @@ internal extension DropboxFileProvider {
|
||||
}
|
||||
task.taskDescription = dictionaryToJSON(dic)
|
||||
task.resume()
|
||||
return RemoteOperationHandle(tasks: [task])
|
||||
}
|
||||
|
||||
func search(_ startPath: String = "", query: String, start: Int = 0, maxResultPerPage: Int = 25, maxResults: Int = -1, foundItem:@escaping ((_ file: DropboxFileObject) -> Void), completionHandler: @escaping ((_ error: Error?) -> Void)) {
|
||||
@@ -135,13 +132,13 @@ internal extension DropboxFileProvider {
|
||||
requestDictionary["query"] = query as NSString
|
||||
requestDictionary["start"] = start as NSNumber
|
||||
requestDictionary["max_results"] = maxResultPerPage as NSNumber
|
||||
request.httpBody = dictionaryToJSON(requestDictionary)?.data(using: String.Encoding.utf8)
|
||||
request.httpBody = dictionaryToJSON(requestDictionary)?.data(using: .utf8)
|
||||
let task = session.dataTask(with: request, completionHandler: { (data, response, error) in
|
||||
var responseError: FileProviderDropboxError?
|
||||
if let code = (response as? HTTPURLResponse)?.statusCode , code >= 300, let rCode = FileProviderHTTPErrorCode(rawValue: code) {
|
||||
responseError = FileProviderDropboxError(code: rCode, path: startPath, errorDescription: String(data: data ?? Data(), encoding: String.Encoding.utf8))
|
||||
responseError = FileProviderDropboxError(code: rCode, path: startPath, errorDescription: String(data: data ?? Data(), encoding: .utf8))
|
||||
}
|
||||
if let data = data, let jsonStr = String(data: data, encoding: String.Encoding.utf8) {
|
||||
if let data = data, let jsonStr = String(data: data, encoding: .utf8) {
|
||||
let json = jsonToDictionary(jsonStr)
|
||||
if let entries = json?["matches"] as? [AnyObject] , entries.count > 0 {
|
||||
for entry in entries {
|
||||
@@ -185,7 +182,7 @@ internal extension DropboxFileProvider {
|
||||
return DropboxFileObject(name: name, path: path, size: size, serverTime: serverTime, modifiedDate: modifiedDate, fileType: isDirectory ? .directory : .regular, isReadOnly: isReadonly, id: id, rev: rev)
|
||||
}
|
||||
|
||||
func delegateNotify(_ operation: FileOperation, error: Error?) {
|
||||
func delegateNotify(_ operation: FileOperationType, error: Error?) {
|
||||
DispatchQueue.main.async(execute: {
|
||||
if error == nil {
|
||||
self.delegate?.fileproviderSucceed(self, operation: operation)
|
||||
|
||||
+53
-44
@@ -117,20 +117,30 @@ public protocol FileProviderBasic: class {
|
||||
public protocol FileProviderOperations: FileProviderBasic {
|
||||
var fileOperationDelegate : FileOperationDelegate? { get set }
|
||||
|
||||
func create(folder: String, at: String, completionHandler: SimpleCompletionHandler)
|
||||
func create(file: FileObject, at: String, contents data: Data?, completionHandler: SimpleCompletionHandler)
|
||||
func moveItem(path: String, to: String, overwrite: Bool, completionHandler: SimpleCompletionHandler)
|
||||
func copyItem(path: String, to: String, overwrite: Bool, completionHandler: SimpleCompletionHandler)
|
||||
func removeItem(path: String, completionHandler: SimpleCompletionHandler)
|
||||
@discardableResult
|
||||
func create(folder: String, at: String, completionHandler: SimpleCompletionHandler) -> OperationHandle?
|
||||
@discardableResult
|
||||
func create(file: FileObject, at: String, contents data: Data?, completionHandler: SimpleCompletionHandler) -> OperationHandle?
|
||||
@discardableResult
|
||||
func moveItem(path: String, to: String, overwrite: Bool, completionHandler: SimpleCompletionHandler) -> OperationHandle?
|
||||
@discardableResult
|
||||
func copyItem(path: String, to: String, overwrite: Bool, completionHandler: SimpleCompletionHandler) -> OperationHandle?
|
||||
@discardableResult
|
||||
func removeItem(path: String, completionHandler: SimpleCompletionHandler) -> OperationHandle?
|
||||
|
||||
func copyItem(localFile: URL, to: String, completionHandler: SimpleCompletionHandler)
|
||||
func copyItem(path: String, toLocalURL: URL, completionHandler: SimpleCompletionHandler)
|
||||
@discardableResult
|
||||
func copyItem(localFile: URL, to: String, completionHandler: SimpleCompletionHandler) -> OperationHandle?
|
||||
@discardableResult
|
||||
func copyItem(path: String, toLocalURL: URL, completionHandler: SimpleCompletionHandler) -> OperationHandle?
|
||||
}
|
||||
|
||||
public protocol FileProviderReadWrite: FileProviderBasic {
|
||||
func contents(path: String, completionHandler: @escaping ((_ contents: Data?, _ error: Error?) -> Void))
|
||||
func contents(path: String, offset: Int64, length: Int, completionHandler: @escaping ((_ contents: Data?, _ error: Error?) -> Void))
|
||||
func writeContents(path: String, contents: Data, atomically: Bool, completionHandler: SimpleCompletionHandler)
|
||||
@discardableResult
|
||||
func contents(path: String, completionHandler: @escaping ((_ contents: Data?, _ error: Error?) -> Void)) -> OperationHandle?
|
||||
@discardableResult
|
||||
func contents(path: String, offset: Int64, length: Int, completionHandler: @escaping ((_ contents: Data?, _ error: Error?) -> Void)) -> OperationHandle?
|
||||
@discardableResult
|
||||
func writeContents(path: String, contents: Data, atomically: Bool, completionHandler: SimpleCompletionHandler) -> OperationHandle?
|
||||
|
||||
func searchFiles(path: String, recursive: Bool, query: String, foundItemHandler: ((FileObject) -> Void)?, completionHandler: @escaping ((_ files: [FileObject], _ error: Error?) -> Void))
|
||||
}
|
||||
@@ -162,12 +172,12 @@ extension FileProviderBasic {
|
||||
rpath = self.currentPath
|
||||
}
|
||||
if isPathRelative, let baseURL = baseURL {
|
||||
if rpath.hasPrefix("/") && baseURL.uw_absoluteString.hasSuffix("/") {
|
||||
if rpath.hasPrefix("/") && baseURL.absoluteString.hasSuffix("/") {
|
||||
var npath = rpath
|
||||
npath.remove(at: npath.startIndex)
|
||||
return baseURL.uw_URLByAppendingPathComponent(npath)
|
||||
return baseURL.appendingPathComponent(npath)
|
||||
} else {
|
||||
return baseURL.uw_URLByAppendingPathComponent(rpath)
|
||||
return baseURL.appendingPathComponent(rpath)
|
||||
}
|
||||
} else {
|
||||
return URL(fileURLWithPath: rpath).standardizedFileURL
|
||||
@@ -175,8 +185,8 @@ extension FileProviderBasic {
|
||||
}
|
||||
|
||||
public func relativePathOf(url: URL) -> String {
|
||||
guard let baseURL = self.baseURL else { return url.uw_absoluteString }
|
||||
return url.standardizedFileURL.uw_absoluteString.replacingOccurrences(of: baseURL.uw_absoluteString, with: "/").removingPercentEncoding!
|
||||
guard let baseURL = self.baseURL else { return url.absoluteString }
|
||||
return url.standardizedFileURL.absoluteString.replacingOccurrences(of: baseURL.absoluteString, with: "/").removingPercentEncoding!
|
||||
}
|
||||
|
||||
internal func correctPath(_ path: String?) -> String? {
|
||||
@@ -211,7 +221,7 @@ extension FileProviderBasic {
|
||||
let similiar = contents.map {
|
||||
$0.absoluteURL?.lastPathComponent ?? $0.name
|
||||
}.filter {
|
||||
$0.hasPrefix(result) && (!fileExt.isEmpty && $0.hasSuffix("." + fileExt))
|
||||
$0.hasPrefix(result)
|
||||
}
|
||||
while similiar.contains(result + (!fileExt.isEmpty ? "." + fileExt : "")) {
|
||||
result = "\(bareFileName) \(i)"
|
||||
@@ -233,7 +243,7 @@ extension FileProviderBasic {
|
||||
default:
|
||||
domain = NSCocoaErrorDomain
|
||||
}
|
||||
return NSError(domain: domain, code: code.rawValue, userInfo: [NSURLErrorFailingURLErrorKey: fileURL, NSURLErrorFailingURLStringErrorKey: fileURL.uw_absoluteString])
|
||||
return NSError(domain: domain, code: code.rawValue, userInfo: [NSURLErrorFailingURLErrorKey: fileURL, NSURLErrorFailingURLStringErrorKey: fileURL.absoluteString])
|
||||
}
|
||||
|
||||
internal func NotImplemented() {
|
||||
@@ -272,7 +282,7 @@ public protocol ExtendedFileProvider: FileProvider {
|
||||
func propertiesOfFile(path: String, completionHandler: @escaping ((_ propertiesDictionary: [String: Any], _ keys: [String], _ error: Error?) -> Void))
|
||||
}
|
||||
|
||||
public enum FileOperation: CustomStringConvertible {
|
||||
public enum FileOperationType: CustomStringConvertible {
|
||||
case create (path: String)
|
||||
case copy (source: String, destination: String)
|
||||
case move (source: String, destination: String)
|
||||
@@ -291,7 +301,7 @@ public enum FileOperation: CustomStringConvertible {
|
||||
}
|
||||
}
|
||||
|
||||
internal var actionDescription: String {
|
||||
public var actionDescription: String {
|
||||
switch self {
|
||||
case .create(path: _): return "Creating"
|
||||
case .copy(source: _, destination: _): return "Copying"
|
||||
@@ -301,50 +311,49 @@ public enum FileOperation: CustomStringConvertible {
|
||||
case .link(link: _, target: _): return "Linking"
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@objc
|
||||
public protocol OperationHandle {
|
||||
var progress: Float { get }
|
||||
var bytesSoFar: Int64 { get }
|
||||
var totalBytes: Int64 { get }
|
||||
var inProgress: Bool { get }
|
||||
func cancel()
|
||||
}
|
||||
|
||||
internal class Weak<T: AnyObject> {
|
||||
weak var value : T?
|
||||
init (_ value: T) {
|
||||
self.value = value
|
||||
}
|
||||
}
|
||||
|
||||
public protocol FileProviderDelegate: class {
|
||||
func fileproviderSucceed(_ fileProvider: FileProviderOperations, operation: FileOperation)
|
||||
func fileproviderFailed(_ fileProvider: FileProviderOperations, operation: FileOperation)
|
||||
func fileproviderProgress(_ fileProvider: FileProviderOperations, operation: FileOperation, progress: Float)
|
||||
func fileproviderSucceed(_ fileProvider: FileProviderOperations, operation: FileOperationType)
|
||||
func fileproviderFailed(_ fileProvider: FileProviderOperations, operation: FileOperationType)
|
||||
func fileproviderProgress(_ fileProvider: FileProviderOperations, operation: FileOperationType, progress: Float)
|
||||
}
|
||||
|
||||
public protocol FileOperationDelegate: class {
|
||||
|
||||
/// fileProvider(_:shouldOperate:) gives the delegate an opportunity to filter the file operation. Returning true from this method will allow the copy to happen. Returning false from this method causes the item in question to be skipped. If the item skipped was a directory, no children of that directory will be subject of the operation, nor will the delegate be notified of those children.
|
||||
func fileProvider(_ fileProvider: FileProviderOperations, shouldDoOperation operation: FileOperation) -> Bool
|
||||
func fileProvider(_ fileProvider: FileProviderOperations, shouldDoOperation operation: FileOperationType) -> Bool
|
||||
|
||||
/// fileProvider(_:shouldProceedAfterError:copyingItemAtPath:toPath:) gives the delegate an opportunity to recover from or continue copying after an error. If an error occurs, the error object will contain an ErrorType indicating the problem. The source path and destination paths are also provided. If this method returns true, the FileProvider instance will continue as if the error had not occurred. If this method returns false, the NSFileManager instance will stop copying, return false from copyItemAtPath:toPath:error: and the error will be provied there.
|
||||
func fileProvider(_ fileProvider: FileProviderOperations, shouldProceedAfterError error: Error, operation: FileOperation) -> Bool
|
||||
func fileProvider(_ fileProvider: FileProviderOperations, shouldProceedAfterError error: Error, operation: FileOperationType) -> Bool
|
||||
}
|
||||
|
||||
// THESE ARE METHODS TO PROVIDE COMPATIBILITY WITH SWIFT 2.3 SIMOULTANIOUSLY!
|
||||
|
||||
internal extension URL {
|
||||
var uw_scheme: String {
|
||||
#if swift(>=2.3)
|
||||
return self.scheme ?? ""
|
||||
#else
|
||||
return self.scheme
|
||||
#endif
|
||||
}
|
||||
|
||||
var uw_absoluteString: String {
|
||||
return self.absoluteString
|
||||
}
|
||||
|
||||
func uw_URLByAppendingPathComponent(_ pathComponent: String) -> URL {
|
||||
return self.appendingPathComponent(pathComponent)
|
||||
}
|
||||
|
||||
func uw_URLByAppendingPathExtension(_ pathExtension: String) -> URL {
|
||||
return self.appendingPathExtension(pathExtension)
|
||||
return self.scheme ?? ""
|
||||
}
|
||||
}
|
||||
|
||||
internal func jsonToDictionary(_ jsonString: String) -> [String: AnyObject]? {
|
||||
guard let data = jsonString.data(using: String.Encoding.utf8) else {
|
||||
guard let data = jsonString.data(using: .utf8) else {
|
||||
return nil
|
||||
}
|
||||
if let dic = try? JSONSerialization.jsonObject(with: data, options: JSONSerialization.ReadingOptions()) as? [String: AnyObject] {
|
||||
@@ -355,7 +364,7 @@ internal func jsonToDictionary(_ jsonString: String) -> [String: AnyObject]? {
|
||||
|
||||
internal func dictionaryToJSON(_ dictionary: [String: AnyObject]) -> String? {
|
||||
if let data = try? JSONSerialization.data(withJSONObject: dictionary, options: JSONSerialization.WritingOptions()) {
|
||||
return String(data: data, encoding: String.Encoding.utf8)
|
||||
return String(data: data, encoding: .utf8)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -102,10 +102,12 @@ open class LocalFileProvider: FileProvider, FileProviderMonitor {
|
||||
|
||||
open weak var fileOperationDelegate : FileOperationDelegate?
|
||||
|
||||
@objc(createWithFolder:at:completionHandler:) open func create(folder folderName: String, at atPath: String, completionHandler: SimpleCompletionHandler) {
|
||||
@objc(createWithFolder:at:completionHandler:)
|
||||
@discardableResult
|
||||
open func create(folder folderName: String, at atPath: String, completionHandler: SimpleCompletionHandler) -> OperationHandle? {
|
||||
operation_queue.async {
|
||||
do {
|
||||
try self.opFileManager.createDirectory(at: self.absoluteURL(atPath).uw_URLByAppendingPathComponent(folderName), withIntermediateDirectories: true, attributes: [:])
|
||||
try self.opFileManager.createDirectory(at: self.absoluteURL(atPath).appendingPathComponent(folderName), withIntermediateDirectories: true, attributes: [:])
|
||||
completionHandler?(nil)
|
||||
DispatchQueue.main.async(execute: {
|
||||
self.delegate?.fileproviderSucceed(self, operation: .create(path: (atPath as NSString).appendingPathComponent(folderName) + "/"))
|
||||
@@ -117,11 +119,13 @@ open class LocalFileProvider: FileProvider, FileProviderMonitor {
|
||||
})
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
open func create(file fileAttribs: FileObject, at atPath: String, contents data: Data?, completionHandler: SimpleCompletionHandler) {
|
||||
@discardableResult
|
||||
open func create(file fileAttribs: FileObject, at atPath: String, contents data: Data?, completionHandler: SimpleCompletionHandler) -> OperationHandle? {
|
||||
operation_queue.async {
|
||||
let fileURL = self.absoluteURL(atPath).uw_URLByAppendingPathComponent(fileAttribs.name)
|
||||
let fileURL = self.absoluteURL(atPath).appendingPathComponent(fileAttribs.name)
|
||||
var attributes = [String : Any]()
|
||||
if let createdDate = fileAttribs.createdDate {
|
||||
attributes[FileAttributeKey.creationDate.rawValue] = createdDate as NSDate
|
||||
@@ -148,9 +152,11 @@ open class LocalFileProvider: FileProvider, FileProviderMonitor {
|
||||
})
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
open func moveItem(path: String, to toPath: String, overwrite: Bool = false, completionHandler: SimpleCompletionHandler) {
|
||||
@discardableResult
|
||||
open func moveItem(path: String, to toPath: String, overwrite: Bool = false, completionHandler: SimpleCompletionHandler) -> OperationHandle? {
|
||||
// FIXME: progress
|
||||
operation_queue.async {
|
||||
if !overwrite && self.fileManager.fileExists(atPath: self.absoluteURL(toPath).path) {
|
||||
@@ -170,9 +176,11 @@ open class LocalFileProvider: FileProvider, FileProviderMonitor {
|
||||
})
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
open func copyItem(path: String, to toPath: String, overwrite: Bool = false, completionHandler: SimpleCompletionHandler) {
|
||||
@discardableResult
|
||||
open func copyItem(path: String, to toPath: String, overwrite: Bool = false, completionHandler: SimpleCompletionHandler) -> OperationHandle? {
|
||||
// FIXME: progress, for files > 100mb, monitor file by another thread, for dirs check copied items count
|
||||
operation_queue.async {
|
||||
if !overwrite && self.fileManager.fileExists(atPath: self.absoluteURL(toPath).path) {
|
||||
@@ -192,9 +200,11 @@ open class LocalFileProvider: FileProvider, FileProviderMonitor {
|
||||
})
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
open func removeItem(path: String, completionHandler: SimpleCompletionHandler) {
|
||||
@discardableResult
|
||||
open func removeItem(path: String, completionHandler: SimpleCompletionHandler) -> OperationHandle? {
|
||||
operation_queue.async {
|
||||
do {
|
||||
try self.opFileManager.removeItem(at: self.absoluteURL(path))
|
||||
@@ -209,50 +219,58 @@ open class LocalFileProvider: FileProvider, FileProviderMonitor {
|
||||
})
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
open func copyItem(localFile: URL, to toPath: String, completionHandler: SimpleCompletionHandler) {
|
||||
@discardableResult
|
||||
open func copyItem(localFile: URL, to toPath: String, completionHandler: SimpleCompletionHandler) -> OperationHandle? {
|
||||
operation_queue.async {
|
||||
do {
|
||||
try self.opFileManager.copyItem(at: localFile, to: self.absoluteURL(toPath))
|
||||
completionHandler?(nil)
|
||||
DispatchQueue.main.async(execute: {
|
||||
self.delegate?.fileproviderSucceed(self, operation: .copy(source: localFile.uw_absoluteString, destination: toPath))
|
||||
self.delegate?.fileproviderSucceed(self, operation: .copy(source: localFile.absoluteString, destination: toPath))
|
||||
})
|
||||
} catch let e as NSError {
|
||||
completionHandler?(e)
|
||||
DispatchQueue.main.async(execute: {
|
||||
self.delegate?.fileproviderFailed(self, operation: .copy(source: localFile.uw_absoluteString, destination: toPath))
|
||||
self.delegate?.fileproviderFailed(self, operation: .copy(source: localFile.absoluteString, destination: toPath))
|
||||
})
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
open func copyItem(path: String, toLocalURL: URL, completionHandler: SimpleCompletionHandler) {
|
||||
@discardableResult
|
||||
open func copyItem(path: String, toLocalURL: URL, completionHandler: SimpleCompletionHandler) -> OperationHandle? {
|
||||
operation_queue.async {
|
||||
do {
|
||||
try self.opFileManager.copyItem(at: self.absoluteURL(path), to: toLocalURL)
|
||||
completionHandler?(nil)
|
||||
DispatchQueue.main.async(execute: {
|
||||
self.delegate?.fileproviderSucceed(self, operation: .copy(source: path, destination: toLocalURL.uw_absoluteString))
|
||||
self.delegate?.fileproviderSucceed(self, operation: .copy(source: path, destination: toLocalURL.absoluteString))
|
||||
})
|
||||
} catch let e as NSError {
|
||||
completionHandler?(e)
|
||||
DispatchQueue.main.async(execute: {
|
||||
self.delegate?.fileproviderFailed(self, operation: .copy(source: path, destination: toLocalURL.uw_absoluteString))
|
||||
self.delegate?.fileproviderFailed(self, operation: .copy(source: path, destination: toLocalURL.absoluteString))
|
||||
})
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
open func contents(path: String, completionHandler: @escaping ((_ contents: Data?, _ error: Error?) -> Void)) {
|
||||
@discardableResult
|
||||
open func contents(path: String, completionHandler: @escaping ((_ contents: Data?, _ error: Error?) -> Void)) -> OperationHandle? {
|
||||
dispatch_queue.async {
|
||||
let data = self.fileManager.contents(atPath: self.absoluteURL(path).path)
|
||||
completionHandler(data, nil)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
open func contents(path: String, offset: Int64, length: Int, completionHandler: @escaping ((_ contents: Data?, _ error: Error?) -> Void)) {
|
||||
@discardableResult
|
||||
open func contents(path: String, offset: Int64, length: Int, completionHandler: @escaping ((_ contents: Data?, _ error: Error?) -> Void)) -> OperationHandle? {
|
||||
// Unfortunatlely there is no method provided in NSFileManager to read a segment of file.
|
||||
// So we have to fallback to POSIX provided methods
|
||||
dispatch_queue.async {
|
||||
@@ -279,15 +297,18 @@ open class LocalFileProvider: FileProvider, FileProviderMonitor {
|
||||
completionHandler(data, nil)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
open func writeContents(path: String, contents data: Data, atomically: Bool, completionHandler: SimpleCompletionHandler) {
|
||||
@discardableResult
|
||||
open func writeContents(path: String, contents data: Data, atomically: Bool, completionHandler: SimpleCompletionHandler) -> OperationHandle? {
|
||||
operation_queue.async {
|
||||
try? data.write(to: self.absoluteURL(path), options: atomically ? [.atomic] : [])
|
||||
DispatchQueue.main.async(execute: {
|
||||
self.delegate?.fileproviderSucceed(self, operation: .modify(path: path))
|
||||
})
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
open func searchFiles(path: String, recursive: Bool, query: String, foundItemHandler: ((FileObject) -> Void)?, completionHandler: @escaping ((_ files: [FileObject], _ error: Error?) -> Void)) {
|
||||
|
||||
@@ -8,6 +8,62 @@
|
||||
|
||||
import Foundation
|
||||
|
||||
open class RemoteOperationHandle: OperationHandle {
|
||||
|
||||
internal var tasks: [Weak<URLSessionTask>]
|
||||
|
||||
private var _inProgress = false
|
||||
open var inProgress: Bool {
|
||||
return _inProgress
|
||||
}
|
||||
|
||||
init(tasks: [URLSessionTask]) {
|
||||
self.tasks = tasks.map { Weak<URLSessionTask>($0) }
|
||||
_inProgress = true
|
||||
}
|
||||
|
||||
internal func add(task: URLSessionTask) {
|
||||
tasks.append(Weak<URLSessionTask>(task))
|
||||
}
|
||||
|
||||
private func reape() {
|
||||
self.tasks = tasks.filter { $0.value != nil }
|
||||
}
|
||||
|
||||
open var progress: Float {
|
||||
let bytesSoFar = self.bytesSoFar
|
||||
let totalBytes = self.totalBytes
|
||||
return totalBytes > 0 ? Float(Double(bytesSoFar) / Double(totalBytes)) : Float.nan
|
||||
}
|
||||
|
||||
open var bytesSoFar: Int64 {
|
||||
return tasks.reduce(0) {
|
||||
if let task = $1.value as? URLSessionUploadTask {
|
||||
return $0 + task.countOfBytesSent
|
||||
} else {
|
||||
return $0 + ($1.value?.countOfBytesReceived ?? 0)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
open var totalBytes: Int64 {
|
||||
return tasks.reduce(0) {
|
||||
if let task = $1.value as? URLSessionUploadTask {
|
||||
return $0 + task.countOfBytesExpectedToSend
|
||||
} else {
|
||||
return $0 + ($1.value?.countOfBytesExpectedToSend ?? 0)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
open func cancel() {
|
||||
for taskbox in tasks {
|
||||
taskbox.value?.cancel()
|
||||
}
|
||||
_inProgress = false
|
||||
}
|
||||
}
|
||||
|
||||
class SessionDelegate: NSObject, URLSessionDataDelegate, URLSessionDownloadDelegate {
|
||||
|
||||
weak var fileProvider: FileProvider?
|
||||
@@ -38,7 +94,7 @@ class SessionDelegate: NSObject, URLSessionDataDelegate, URLSessionDownloadDeleg
|
||||
return
|
||||
}
|
||||
let dest = json["dest"] as? String
|
||||
let op : FileOperation
|
||||
let op : FileOperationType
|
||||
switch type {
|
||||
case "Create":
|
||||
op = .create(path: source)
|
||||
@@ -102,8 +102,7 @@ class SMB2ProtocolClient: FPSStreamTask {
|
||||
// MARK: create and analyse messages
|
||||
|
||||
func determineSMBVersion(_ data: Data) -> Float {
|
||||
var smbverChar: Int8 = 0
|
||||
(data as NSData).getBytes(&smbverChar, length: 1)
|
||||
let smbverChar: Int8 = Int8(bitPattern: data.first ?? 0)
|
||||
let version = 0 - smbverChar
|
||||
return Float(version)
|
||||
}
|
||||
@@ -136,9 +135,9 @@ class SMB2ProtocolClient: FPSStreamTask {
|
||||
guard data.count >= (offset + messageBytesCount) else {
|
||||
throw SMBFileProviderError.incorrectMessageLength
|
||||
}
|
||||
var rawMessage = [UInt8](buffer[offset..<(offset + messageBytesCount)])
|
||||
let rawMessage = [UInt8](buffer[offset..<(offset + messageBytesCount)])
|
||||
offset += messageBytesCount
|
||||
let message = NSData(bytes: &rawMessage, length: rawMessage.count) as Data
|
||||
let message = Data(bytes: rawMessage)
|
||||
blocks.append((params: paramWords, message: message))
|
||||
}
|
||||
return (header, blocks)
|
||||
@@ -152,9 +151,9 @@ class SMB2ProtocolClient: FPSStreamTask {
|
||||
throw SMBFileProviderError.incompatibleHeader
|
||||
}
|
||||
let headersize = MemoryLayout<SMB2.Header>.size
|
||||
let headerData = data.subdata(in: NSRange(location: 0, length: headersize))
|
||||
let headerData = data.subdata(in: 0..<headersize)
|
||||
let messageSize = data.count - headersize
|
||||
let messageData = data.subdata(in: NSRange(location: headersize, length: messageSize))
|
||||
let messageData = data.subdata(in: headersize..<(headersize + messageSize))
|
||||
let header: SMB2.Header = decode(headerData)
|
||||
switch header.command {
|
||||
case .NEGOTIATE:
|
||||
@@ -202,7 +201,7 @@ class SMB2ProtocolClient: FPSStreamTask {
|
||||
|
||||
func createSMBMessage(header: SMB1.Header, blocks: [(params: Data?, message: Data?)]) -> Data {
|
||||
var headerv = header
|
||||
var result = NSData(data: encode(&headerv)) as Data
|
||||
var result = encode(&headerv)
|
||||
for block in blocks {
|
||||
var paramWordsCount = UInt8(block.params?.count ?? 0)
|
||||
result.append(¶mWordsCount, count: MemoryLayout.size(ofValue: paramWordsCount))
|
||||
@@ -221,8 +220,8 @@ class SMB2ProtocolClient: FPSStreamTask {
|
||||
|
||||
func createSMB2Message(header: SMB2.Header, message: SMBRequest) -> Data {
|
||||
var headerv = header
|
||||
var result = NSData(data: encode(&headerv)) as Data
|
||||
result.append(message.data() as Data)
|
||||
var result = encode(&headerv)
|
||||
result.append(message.data())
|
||||
return result
|
||||
}
|
||||
}
|
||||
|
||||
@@ -31,9 +31,6 @@ open class SMBFileProvider: FileProvider, FileProviderMonitor {
|
||||
|
||||
open func contentsOfDirectory(path: String, completionHandler: @escaping ((_ contents: [FileObjectClass], _ error: Error?) -> Void)) {
|
||||
NotImplemented()
|
||||
dispatch_queue.async {
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
open func attributesOfItem(path: String, completionHandler: @escaping ((_ attributes: FileObjectClass?, _ error: Error?) -> Void)) {
|
||||
@@ -46,44 +43,54 @@ open class SMBFileProvider: FileProvider, FileProviderMonitor {
|
||||
|
||||
open weak var fileOperationDelegate: FileOperationDelegate?
|
||||
|
||||
open func create(folder folderName: String, at atPath: String, completionHandler: SimpleCompletionHandler) {
|
||||
open func create(folder folderName: String, at atPath: String, completionHandler: SimpleCompletionHandler) -> OperationHandle? {
|
||||
NotImplemented()
|
||||
return nil
|
||||
}
|
||||
|
||||
open func create(file fileAttribs: FileObject, at atPath: String, contents data: Data?, completionHandler: SimpleCompletionHandler) {
|
||||
open func create(file fileAttribs: FileObject, at atPath: String, contents data: Data?, completionHandler: SimpleCompletionHandler) -> OperationHandle? {
|
||||
NotImplemented()
|
||||
return nil
|
||||
}
|
||||
|
||||
open func moveItem(path: String, to toPath: String, overwrite: Bool = false, completionHandler: SimpleCompletionHandler) {
|
||||
open func moveItem(path: String, to toPath: String, overwrite: Bool = false, completionHandler: SimpleCompletionHandler) -> OperationHandle? {
|
||||
NotImplemented()
|
||||
return nil
|
||||
}
|
||||
|
||||
open func copyItem(path: String, to toPath: String, overwrite: Bool = false, completionHandler: SimpleCompletionHandler) {
|
||||
open func copyItem(path: String, to toPath: String, overwrite: Bool = false, completionHandler: SimpleCompletionHandler) -> OperationHandle? {
|
||||
NotImplemented()
|
||||
return nil
|
||||
}
|
||||
|
||||
open func removeItem(path: String, completionHandler: SimpleCompletionHandler) {
|
||||
open func removeItem(path: String, completionHandler: SimpleCompletionHandler) -> OperationHandle? {
|
||||
NotImplemented()
|
||||
return nil
|
||||
}
|
||||
|
||||
open func copyItem(localFile: URL, to toPath: String, completionHandler: SimpleCompletionHandler) {
|
||||
open func copyItem(localFile: URL, to toPath: String, completionHandler: SimpleCompletionHandler) -> OperationHandle? {
|
||||
NotImplemented()
|
||||
return nil
|
||||
}
|
||||
|
||||
open func copyItem(path: String, toLocalURL: URL, completionHandler: SimpleCompletionHandler) {
|
||||
open func copyItem(path: String, toLocalURL: URL, completionHandler: SimpleCompletionHandler) -> OperationHandle? {
|
||||
NotImplemented()
|
||||
return nil
|
||||
}
|
||||
|
||||
open func contents(path: String, completionHandler: @escaping ((_ contents: Data?, _ error: Error?) -> Void)) {
|
||||
open func contents(path: String, completionHandler: @escaping ((_ contents: Data?, _ error: Error?) -> Void)) -> OperationHandle? {
|
||||
NotImplemented()
|
||||
return nil
|
||||
}
|
||||
|
||||
open func contents(path: String, offset: Int64, length: Int, completionHandler: @escaping ((_ contents: Data?, _ error: Error?) -> Void)) {
|
||||
open func contents(path: String, offset: Int64, length: Int, completionHandler: @escaping ((_ contents: Data?, _ error: Error?) -> Void)) -> OperationHandle? {
|
||||
NotImplemented()
|
||||
return nil
|
||||
}
|
||||
|
||||
open func writeContents(path: String, contents data: Data, atomically: Bool, completionHandler: SimpleCompletionHandler) {
|
||||
open func writeContents(path: String, contents data: Data, atomically: Bool, completionHandler: SimpleCompletionHandler) -> OperationHandle? {
|
||||
NotImplemented()
|
||||
return nil
|
||||
}
|
||||
|
||||
open func searchFiles(path: String, recursive: Bool, query: String, foundItemHandler:((FileObjectClass) -> Void)?, completionHandler: @escaping ((_ files: [FileObjectClass], _ error: Error?) -> Void)) {
|
||||
|
||||
@@ -25,8 +25,8 @@ extension SMB2 {
|
||||
func data() -> Data {
|
||||
var header = self.header
|
||||
var offset = 0x78 //UInt16(sizeof(SMB2.Header.self) + sizeof(CreateContext.Header.self) - 1)
|
||||
let body = NSMutableData()
|
||||
if let name = self.name, let nameData = name.data(using: String.Encoding.utf16) {
|
||||
var body = Data()
|
||||
if let name = self.name, let nameData = name.data(using: .utf16) {
|
||||
header.nameOffset = UInt16(offset)
|
||||
header.nameLength = UInt16(nameData.count)
|
||||
offset += nameData.count
|
||||
@@ -40,8 +40,8 @@ extension SMB2 {
|
||||
header.contextLength = 0
|
||||
//result.appendData(nameData)
|
||||
}
|
||||
var result = NSData(data: encode(&header)) as Data
|
||||
result.append(body as Data)
|
||||
var result = encode(&header)
|
||||
result.append(body)
|
||||
return result
|
||||
}
|
||||
|
||||
@@ -195,10 +195,10 @@ extension SMB2 {
|
||||
self.contexts = contexts
|
||||
return
|
||||
}
|
||||
let contextDataHeader = data.subdata(in: NSRange(location: contextOffset, length: MemoryLayout<CreateContext.Header>.size))
|
||||
let contextDataHeader = data.subdata(in: contextOffset..<(contextOffset + MemoryLayout<CreateContext.Header>.size))
|
||||
if let lastContextHeader = CreateContext(data: contextDataHeader) {
|
||||
let lastContextLen = Int(lastContextHeader.header.dataOffset) + Int(lastContextHeader.header.dataLength) - contextOffset
|
||||
let lastContextData = data.subdata(in: NSRange(location: contextOffset, length: lastContextLen))
|
||||
let lastContextData = data.subdata(in: contextOffset..<(contextOffset + lastContextLen))
|
||||
if let newContext = CreateContext(data: lastContextData) {
|
||||
contexts.append(newContext)
|
||||
}
|
||||
@@ -226,7 +226,7 @@ extension SMB2 {
|
||||
let buffer: Data
|
||||
|
||||
init(name: ContextNames, data: Data) {
|
||||
let nameData = NSData(data: (name.rawValue).data(using: String.Encoding.utf16)!) as Data
|
||||
let nameData = (name.rawValue).data(using: .utf16) ?? Data()
|
||||
self.header = CreateContext.Header(next: 0, nameOffset: 32, nameLength: UInt16(nameData.count), reserved: 0, dataOffset: UInt16(nameData.count), dataLength: UInt32(data.count))
|
||||
self.buffer = data
|
||||
}
|
||||
@@ -234,8 +234,8 @@ extension SMB2 {
|
||||
init(name: UUID, data: Data) {
|
||||
var uuid = uuid_t(0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0)
|
||||
(name as NSUUID).getBytes(&uuid.0)
|
||||
let nameData = NSMutableData(bytes: &uuid, length: 16)
|
||||
self.header = CreateContext.Header(next: 0, nameOffset: 32, nameLength: UInt16(nameData.length), reserved: 0, dataOffset: UInt16(nameData.length), dataLength: UInt32(data.count))
|
||||
var nameData = Data(bytes: &uuid.0, count: 16)
|
||||
self.header = CreateContext.Header(next: 0, nameOffset: 32, nameLength: UInt16(nameData.count), reserved: 0, dataOffset: UInt16(nameData.count), dataLength: UInt32(data.count))
|
||||
self.buffer = data
|
||||
}
|
||||
|
||||
@@ -245,11 +245,11 @@ extension SMB2 {
|
||||
return nil
|
||||
}
|
||||
self.header = decode(data)
|
||||
self.buffer = data.subdata(in: NSRange(location: headersize, length: data.count - headersize))
|
||||
self.buffer = data.subdata(in: headersize..<data.count)
|
||||
}
|
||||
|
||||
func data() -> Data {
|
||||
var result = NSData(data: encode(header)) as Data
|
||||
var result = encode(header)
|
||||
result.append(buffer)
|
||||
return result
|
||||
}
|
||||
|
||||
@@ -77,7 +77,7 @@ extension SMB2 {
|
||||
}
|
||||
self.header = decode(data)
|
||||
let headersize = MemoryLayout<Header>.size
|
||||
self.buffer = data.subdata(in: NSRange(location: headersize, length: data.count - headersize))
|
||||
self.buffer = data.subdata(in: headersize..<data.count)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -124,7 +124,7 @@ extension SMB2 {
|
||||
}
|
||||
|
||||
func data() -> Data {
|
||||
var result = NSData(data: encode(self.header)) as Data
|
||||
var result = encode(self.header)
|
||||
if let channelInfo = channelInfo {
|
||||
result.append(channelInfo.data())
|
||||
}
|
||||
@@ -203,7 +203,7 @@ extension SMB2 {
|
||||
}
|
||||
|
||||
func data() -> Data {
|
||||
var result = NSData(data: encode(header)) as Data
|
||||
var result = encode(header)
|
||||
for lock in locks {
|
||||
result.append(encode(lock))
|
||||
}
|
||||
|
||||
@@ -26,7 +26,7 @@ extension SMB2 {
|
||||
}
|
||||
|
||||
func data() -> Data {
|
||||
var result = NSData(data: encode(self.header)) as Data
|
||||
var result = encode(self.header)
|
||||
if let reqData = requestData?.data() {
|
||||
result.append(reqData)
|
||||
}
|
||||
@@ -69,8 +69,8 @@ extension SMB2 {
|
||||
|
||||
init?(data: Data) {
|
||||
self.header = decode(data)
|
||||
let responseRange = NSRange(location: Int(self.header.outputOffset - 64), length: Int(self.header.outputCount))
|
||||
let response = data.subdata(in: responseRange)
|
||||
let endRange = Int(self.header.outputOffset - 64) + Int(self.header.outputCount)
|
||||
let response = data.subdata(in: Int(self.header.outputOffset - 64)..<endRange)
|
||||
switch self.header.ctlCode {
|
||||
case .SRV_COPYCHUNK, .SRV_COPYCHUNK_WRITE:
|
||||
self.responseData = IOCtlResponseData.SrvCopyChunk(data: response)
|
||||
@@ -140,7 +140,7 @@ extension SMB2 {
|
||||
let chunks: [Chunk]
|
||||
|
||||
func data() -> Data {
|
||||
var result = NSData(data: encode(sourceKey)) as Data
|
||||
var result = encode(sourceKey)
|
||||
result.append(encode(chunkCount))
|
||||
var reserved: UInt32 = 0
|
||||
result.append(encode(&reserved))
|
||||
@@ -213,7 +213,7 @@ extension SMB2 {
|
||||
}
|
||||
|
||||
func data() -> Data {
|
||||
var result = NSData(data: encode(self.header)) as Data
|
||||
var result = encode(self.header)
|
||||
dialects.forEach { result.append(encode($0)) }
|
||||
return result
|
||||
}
|
||||
@@ -248,7 +248,7 @@ extension SMB2 {
|
||||
|
||||
init?(data: Data) {
|
||||
self.count = decode(data)
|
||||
self.returnedCount = decode(data.subdata(in: NSRange(location: 4, length: 4)))
|
||||
self.returnedCount = decode(data.subdata(in: 4..<8))
|
||||
//let size: UInt32 = decode(data.subdataWithRange(NSRange(location: 8, length: 4)))
|
||||
var snapshots = [SMBTime]()
|
||||
let dateFormatter = DateFormatter()
|
||||
@@ -258,7 +258,7 @@ extension SMB2 {
|
||||
if data.count < offset + 48 {
|
||||
return nil
|
||||
}
|
||||
let datestring = String(data: data.subdata(in: NSRange(location: offset, length: 48)), encoding: String.Encoding.utf16)
|
||||
let datestring = String(data: data.subdata(in: offset..<(offset + 48)), encoding: .utf16)
|
||||
if let datestring = datestring, let date = dateFormatter.date(from: datestring) {
|
||||
snapshots.append(SMBTime(date: date))
|
||||
}
|
||||
@@ -295,7 +295,7 @@ extension SMB2 {
|
||||
}
|
||||
var items = [Item]()
|
||||
for i in 0..<count {
|
||||
let itemdata = data.subdata(in: NSRange(location: i * MemoryLayout<Item>.size, length: MemoryLayout<Item>.size))
|
||||
let itemdata = data.subdata(in: (i * MemoryLayout<Item>.size)..<((i + 1) * MemoryLayout<Item>.size))
|
||||
items.append(decode(itemdata))
|
||||
}
|
||||
self.items = items
|
||||
@@ -310,7 +310,8 @@ extension SMB2 {
|
||||
fileprivate let reserved: UInt32
|
||||
/// Speed of the network interface in bits per second
|
||||
let linkSpeed: UInt64
|
||||
fileprivate let sockaddrStorage: (UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8,
|
||||
fileprivate let sockaddrStorage:
|
||||
(UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8,
|
||||
UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8,
|
||||
UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8,
|
||||
UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8,
|
||||
|
||||
@@ -87,22 +87,24 @@ extension SMB2 {
|
||||
var i = 0
|
||||
var result = [(action: FileNotifyAction, fileName: String)]()
|
||||
|
||||
var offset: UInt32 = 0
|
||||
var offset = 0
|
||||
while i < maxLoop {
|
||||
let actionData = data.subdata(in: NSRange(location: Int(offset + 4), length: 4))
|
||||
let nextOffsetData = data.subdata(in: offset..<(offset + 4))
|
||||
let nextOffset: UInt32 = decode(nextOffsetData)
|
||||
|
||||
let actionData = data.subdata(in: (offset + 4)..<(offset + 8))
|
||||
let actionValue: UInt32 = decode(actionData)
|
||||
guard let action = FileNotifyAction(rawValue: actionValue) else {
|
||||
continue
|
||||
}
|
||||
let fileLenData = data.subdata(in: NSRange(location: Int(offset + 8), length: 4))
|
||||
let fileNameLen: UInt32 = decode(fileLenData)
|
||||
let fileNameData = data.subdata(in: NSRange(location: Int(offset + 12), length: Int(12 + fileNameLen)))
|
||||
let fileName = String(data: fileNameData, encoding: String.Encoding.utf16) ?? ""
|
||||
|
||||
let fileLenData = data.subdata(in: (offset + 8)..<(offset + 12))
|
||||
let fileNameLen = Int(decode(fileLenData) as UInt32)
|
||||
let fileNameData = data.subdata(in: (offset + 12)..<(offset + 12 + fileNameLen))
|
||||
let fileName = String(data: fileNameData, encoding: .utf16) ?? ""
|
||||
result.append((action: action, fileName: fileName))
|
||||
|
||||
let nextOffsetData = data.subdata(in: NSRange(location: Int(offset), length: 4))
|
||||
let nextOffset: UInt32 = decode(nextOffsetData)
|
||||
offset += nextOffset
|
||||
offset += Int(nextOffset)
|
||||
if nextOffset == 0 {
|
||||
break
|
||||
}
|
||||
|
||||
@@ -22,14 +22,14 @@ extension SMB2 {
|
||||
assert(FileInformationEnum.queryDirectory.contains(infoClass), "Invalid FileInformationClass used for QueryDirectoryRequest")
|
||||
let searchPatternOffset = searchPattern != nil ? MemoryLayout<SMB2.Header>.size + MemoryLayout<QueryDirectoryRequest.Header>.size : 0
|
||||
let nflags = flags.intersection(fileIndex > 0 ? [.INDEX_SPECIFIED] : [])
|
||||
let searchPatternLength = searchPattern?.data(using: String.Encoding.utf16)?.count ?? 0
|
||||
let searchPatternLength = searchPattern?.data(using: .utf16)?.count ?? 0
|
||||
self.header = Header(size: 53, infoClass: infoClass, flags: nflags, fileIndex: fileIndex, fileId: fileId, searchPatternOffset: UInt8(searchPatternOffset), searchPatternLength: UInt8(searchPatternLength), bufferLength: bufferLength)
|
||||
self.searchPattern = searchPattern
|
||||
}
|
||||
|
||||
func data() -> Data {
|
||||
var result = NSData(data: encode(header)) as Data
|
||||
if let patternData = searchPattern?.data(using: String.Encoding.utf16) {
|
||||
var result = encode(header)
|
||||
if let patternData = searchPattern?.data(using: .utf16) {
|
||||
result.append(patternData)
|
||||
}
|
||||
return result
|
||||
@@ -68,36 +68,43 @@ extension SMB2 {
|
||||
var result = [(header: SMB2FilesInformationHeader, fileName: String)]()
|
||||
while true {
|
||||
let header: SMB2FilesInformationHeader
|
||||
let headersize: Int
|
||||
switch type {
|
||||
case .fileDirectoryInformation:
|
||||
let headerData = buffer.subdata(in: NSRange(location: offset, length: MemoryLayout<FileDirectoryInformationHeader>.size))
|
||||
headersize = MemoryLayout<FileDirectoryInformationHeader>.size
|
||||
let headerData = buffer.subdata(in: offset..<(offset + headersize))
|
||||
let h: FileDirectoryInformationHeader = decode(headerData)
|
||||
header = h
|
||||
case .fileFullDirectoryInformation:
|
||||
let headerData = buffer.subdata(in: NSRange(location: offset, length: MemoryLayout<FileFullDirectoryInformationHeader>.size))
|
||||
headersize = MemoryLayout<FileFullDirectoryInformationHeader>.size
|
||||
let headerData = buffer.subdata(in: offset..<(offset + headersize))
|
||||
let h: FileFullDirectoryInformationHeader = decode(headerData)
|
||||
header = h
|
||||
case .fileIdFullDirectoryInformation:
|
||||
let headerData = buffer.subdata(in: NSRange(location: offset, length: MemoryLayout<FileIdFullDirectoryInformationHeader>.size))
|
||||
headersize = MemoryLayout<FileIdFullDirectoryInformationHeader>.size
|
||||
let headerData = buffer.subdata(in: offset..<(offset + headersize))
|
||||
let h: FileIdFullDirectoryInformationHeader = decode(headerData)
|
||||
header = h
|
||||
case .fileBothDirectoryInformation:
|
||||
let headerData = buffer.subdata(in: NSRange(location: offset, length: MemoryLayout<FileBothDirectoryInformationHeader>.size))
|
||||
headersize = MemoryLayout<FileBothDirectoryInformationHeader>.size
|
||||
let headerData = buffer.subdata(in: offset..<(offset + headersize))
|
||||
let h: FileBothDirectoryInformationHeader = decode(headerData)
|
||||
header = h
|
||||
case .fileIdBothDirectoryInformation:
|
||||
let headerData = buffer.subdata(in: NSRange(location: offset, length: MemoryLayout<FileIdBothDirectoryInformationHeader>.size))
|
||||
headersize = MemoryLayout<FileIdBothDirectoryInformationHeader>.size
|
||||
let headerData = buffer.subdata(in: offset..<(offset + headersize))
|
||||
let h: FileIdBothDirectoryInformationHeader = decode(headerData)
|
||||
header = h
|
||||
case .fileNamesInformation:
|
||||
let headerData = buffer.subdata(in: NSRange(location: offset, length: MemoryLayout<FileNamesInformationHeader>.size))
|
||||
headersize = MemoryLayout<FileNamesInformationHeader>.size
|
||||
let headerData = buffer.subdata(in: offset..<(offset + headersize))
|
||||
let h: FileNamesInformationHeader = decode(headerData)
|
||||
header = h
|
||||
default:
|
||||
return []
|
||||
}
|
||||
let fnData = buffer.subdata(in: NSRange(location: offset + MemoryLayout.size(ofValue: header), length: Int(header.fileNameLength)))
|
||||
let fileName = String(data: fnData, encoding: String.Encoding.utf16) ?? ""
|
||||
let fnData = buffer.subdata(in: (offset + headersize)..<(offset + headersize + Int(header.fileNameLength)))
|
||||
let fileName = String(data: fnData, encoding: .utf16) ?? ""
|
||||
result.append((header: header, fileName: fileName))
|
||||
if header.nextEntryOffset == 0 {
|
||||
break
|
||||
@@ -108,12 +115,12 @@ extension SMB2 {
|
||||
}
|
||||
|
||||
init? (data: Data) {
|
||||
let offset: UInt16 = decode(data.subdata(in: NSRange(location: 2, length: 2)))
|
||||
let length: UInt32 = decode(data.subdata(in: NSRange(location: 4, length: 4)))
|
||||
guard data.count > Int(offset) + Int(length) else {
|
||||
let offset = Int(decode(data.subdata(in: 2..<4)) as UInt16)
|
||||
let length = Int(decode(data.subdata(in: 4..<8)) as UInt32)
|
||||
guard data.count > offset + length else {
|
||||
return nil
|
||||
}
|
||||
self.buffer = data.subdata(in: NSRange(location: Int(offset), length: Int(length)))
|
||||
self.buffer = data.subdata(in: offset..<(offset + length))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -129,22 +136,24 @@ extension SMB2 {
|
||||
}
|
||||
|
||||
init(fileId: FileId, extendedAttributes: [String], flags: Flags = [], outputBufferLength: UInt32 = 65535) {
|
||||
let buffer = NSMutableData()
|
||||
var buffer = Data()
|
||||
for ea in extendedAttributes {
|
||||
let strData = ea.data(using: String.Encoding.ascii)!
|
||||
guard let strData = ea.data(using: .ascii) else {
|
||||
continue
|
||||
}
|
||||
let strLength = UInt8(strData.count)
|
||||
let nextOffset = UInt32(4 + 1 + strData.count)
|
||||
let data = (encode(nextOffset) as NSData).mutableCopy() as! NSMutableData
|
||||
var data = encode(nextOffset)
|
||||
data.append(encode(strLength))
|
||||
data.append(strData)
|
||||
data.length += 1
|
||||
let padSize = (data.length) % 4
|
||||
data.length += padSize
|
||||
data.count += 1
|
||||
let padSize = (data.count) % 4
|
||||
data.count += padSize
|
||||
buffer.append(data as Data)
|
||||
}
|
||||
|
||||
let bufferOffset = UInt16(MemoryLayout<SMB2.Header>.size + MemoryLayout<QueryInfoRequest.Header>.size)
|
||||
self.header = Header(size: 41, infoType: 1, infoClass: FileInformationEnum.fileFullEaInformation.rawValue, outputBufferLength: outputBufferLength, inputBufferOffset: bufferOffset, reserved: 0, inputBufferLength: UInt32(buffer.length), additionalInformation: [], flags: flags, fileId: fileId)
|
||||
self.header = Header(size: 41, infoType: 1, infoClass: FileInformationEnum.fileFullEaInformation.rawValue, outputBufferLength: outputBufferLength, inputBufferOffset: bufferOffset, reserved: 0, inputBufferLength: UInt32(buffer.count), additionalInformation: [], flags: flags, fileId: fileId)
|
||||
self.buffer = buffer as Data
|
||||
}
|
||||
|
||||
@@ -162,7 +171,7 @@ extension SMB2 {
|
||||
|
||||
func data() -> Data {
|
||||
let headerData = encode(header)
|
||||
var result = NSData(data: headerData) as Data
|
||||
var result = headerData
|
||||
if let buffer = buffer {
|
||||
result.append(buffer)
|
||||
}
|
||||
@@ -199,7 +208,7 @@ extension SMB2 {
|
||||
let buffer: Data
|
||||
|
||||
init?(data: Data) {
|
||||
let structSizeData = data.subdata(in: NSRange(location: 0, length: 2))
|
||||
let structSizeData = data.subdata(in: 0..<2)
|
||||
let structSize: UInt16 = decode(structSizeData)
|
||||
guard structSize == 9 else {
|
||||
return nil
|
||||
@@ -208,14 +217,14 @@ extension SMB2 {
|
||||
/*let offsetData = data.subdataWithRange(NSRange(location: 2, length: 2))
|
||||
let offset: UInt16 = decode(offsetData)*/
|
||||
|
||||
let lengthData = data.subdata(in: NSRange(location: 4, length: 4))
|
||||
let length: UInt32 = decode(lengthData)
|
||||
let lengthData = data.subdata(in: 4..<8)
|
||||
let length = Int(decode(lengthData) as UInt32)
|
||||
|
||||
guard data.count >= 8 + Int(length) else {
|
||||
return nil
|
||||
}
|
||||
|
||||
self.buffer = data.subdata(in: NSRange(location: 8, length: Int(length)))
|
||||
self.buffer = data.subdata(in: 8..<(8 + length))
|
||||
}
|
||||
|
||||
var asAccessInformation: FileAccessInformation {
|
||||
@@ -228,14 +237,15 @@ extension SMB2 {
|
||||
|
||||
var asAllInformation: (header: FileAllInformationHeader, name: String) {
|
||||
let header: FileAllInformationHeader = decode(buffer)
|
||||
let nameData = buffer.subdata(in: NSRange(location: MemoryLayout<FileAllInformationHeader>.size, length: Int(header.nameLength)))
|
||||
let name = String(data: nameData, encoding: String.Encoding.utf16) ?? ""
|
||||
let headersize = MemoryLayout<FileAllInformationHeader>.size
|
||||
let nameData = buffer.subdata(in: headersize..<(headersize + Int(header.nameLength)))
|
||||
let name = String(data: nameData, encoding: .utf16) ?? ""
|
||||
return (header, name)
|
||||
}
|
||||
|
||||
var asAlternateNameInformation: String {
|
||||
let b = (buffer as NSData).bytes.bindMemory(to: CChar.self, capacity: buffer.count)
|
||||
return String(cString: b, encoding: String.Encoding.utf16) ?? ""
|
||||
return String(cString: b, encoding: .utf16) ?? ""
|
||||
}
|
||||
|
||||
var asAttributeTagInformation: FileAttributeTagInformation {
|
||||
@@ -293,15 +303,17 @@ extension SMB2 {
|
||||
|
||||
var asStreamInformation: (header: FileStreamInformationHeader, name: String) {
|
||||
let header: FileStreamInformationHeader = decode(buffer)
|
||||
let nameData = buffer.subdata(in: NSRange(location: MemoryLayout<FileStreamInformationHeader>.size, length: Int(header.streamNameLength)))
|
||||
let name = String(data: nameData, encoding: String.Encoding.utf16) ?? ""
|
||||
let headersize = MemoryLayout<FileStreamInformationHeader>.size
|
||||
let nameData = buffer.subdata(in: headersize..<(headersize + Int(header.streamNameLength)))
|
||||
let name = String(data: nameData, encoding: .utf16) ?? ""
|
||||
return (header, name)
|
||||
}
|
||||
|
||||
var asFsVolumeInformation: (header: FileFsVolumeInformationHeader, name: String) {
|
||||
let header: FileFsVolumeInformationHeader = decode(buffer)
|
||||
let nameData = buffer.subdata(in: NSRange(location: MemoryLayout<FileFsVolumeInformationHeader>.size, length: Int(header.labelLength)))
|
||||
let name = String(data: nameData, encoding: String.Encoding.utf16) ?? ""
|
||||
let headersize = MemoryLayout<FileFsVolumeInformationHeader>.size
|
||||
let nameData = buffer.subdata(in: headersize..<(headersize + Int(header.labelLength)))
|
||||
let name = String(data: nameData, encoding: .utf16) ?? ""
|
||||
return (header, name)
|
||||
}
|
||||
|
||||
@@ -315,8 +327,9 @@ extension SMB2 {
|
||||
|
||||
var asFsAttributeInformation: (header: FileFsAttributeInformationHeader, name: String) {
|
||||
let header: FileFsAttributeInformationHeader = decode(buffer)
|
||||
let nameData = buffer.subdata(in: NSRange(location: MemoryLayout<FileFsAttributeInformationHeader>.size, length: Int(header.nameLength)))
|
||||
let name = String(data: nameData, encoding: String.Encoding.utf16) ?? ""
|
||||
let headersize = MemoryLayout<FileFsAttributeInformationHeader>.size
|
||||
let nameData = buffer.subdata(in: headersize..<(headersize + Int(header.nameLength)))
|
||||
let name = String(data: nameData, encoding: .utf16) ?? ""
|
||||
return (header, name)
|
||||
}
|
||||
|
||||
|
||||
@@ -16,7 +16,7 @@ protocol SMB2FilesInformationHeader: SMBResponse {
|
||||
|
||||
extension SMB2 {
|
||||
enum FileInformationEnum: UInt8 {
|
||||
case `nil` = 0x00
|
||||
case none = 0x00
|
||||
case fileDirectoryInformation = 0x01
|
||||
case fileFullDirectoryInformation = 0x02
|
||||
case fileBothDirectoryInformation = 0x03
|
||||
@@ -88,7 +88,7 @@ extension SMB2 {
|
||||
}
|
||||
|
||||
enum FileSystemInformationEnum: UInt8 {
|
||||
case `nil` = 0
|
||||
case none = 0
|
||||
case fileFsAttributeInformation
|
||||
case fileFsControlInformation
|
||||
case fileFsDeviceInformation
|
||||
@@ -188,9 +188,9 @@ extension SMB2 {
|
||||
fileprivate let _shortName: FileShortNameType
|
||||
var shortName: String? {
|
||||
let s = encode(_shortName)
|
||||
var d = NSData(data: s) as Data
|
||||
var d = s
|
||||
d.count = Int(shortNameLen)
|
||||
return String(data: d, encoding: String.Encoding.utf16)
|
||||
return String(data: d, encoding: .utf16)
|
||||
}
|
||||
|
||||
init?(data: Data) {
|
||||
@@ -215,9 +215,9 @@ extension SMB2 {
|
||||
fileprivate let _shortName: FileShortNameType
|
||||
var shortName: String? {
|
||||
let s = encode(_shortName)
|
||||
var d = NSData(data: s) as Data
|
||||
var d = s
|
||||
d.count = Int(shortNameLen)
|
||||
return String(data: d, encoding: String.Encoding.utf16)
|
||||
return String(data: d, encoding: .utf16)
|
||||
}
|
||||
fileprivate let reserved2: UInt16
|
||||
let fileId : FileId
|
||||
|
||||
@@ -31,25 +31,25 @@ extension SMB2 {
|
||||
func data() -> Data {
|
||||
var header = self.header
|
||||
header.dialectCount = UInt16(dialects.count)
|
||||
let dialectData = NSMutableData()
|
||||
var dialectData = Data()
|
||||
for dialect in dialects {
|
||||
var dialect = dialect
|
||||
dialectData.append(&dialect, length: 2)
|
||||
dialectData.append(UnsafeBufferPointer(start: &dialect, count: 2))
|
||||
}
|
||||
let pad = ((1024 - dialectData.length) % 8)
|
||||
dialectData.increaseLength(by: pad)
|
||||
header.contextOffset = UInt32(MemoryLayout<NegotiateRequest.Header>.size) + UInt32(dialectData.length)
|
||||
let pad = ((1024 - dialectData.count) % 8)
|
||||
dialectData.count += pad
|
||||
header.contextOffset = UInt32(MemoryLayout<NegotiateRequest.Header>.size) + UInt32(dialectData.count)
|
||||
header.contextCount = UInt16(contexts.count)
|
||||
|
||||
let contextData = NSMutableData()
|
||||
var contextData = Data()
|
||||
for context in contexts {
|
||||
var contextType = context.type.rawValue
|
||||
contextData.append(&contextType, length: 2)
|
||||
contextData.append(UnsafeBufferPointer(start: &contextType, count: 2))
|
||||
var dataLen = UInt16(context.data.count)
|
||||
contextData.increaseLength(by: 4)
|
||||
contextData.append(&dataLen, length: 2)
|
||||
contextData.count += 4
|
||||
contextData.append(UnsafeBufferPointer(start: &dataLen, count: 2))
|
||||
}
|
||||
var result = NSData(data: encode(&header)) as Data
|
||||
var result = encode(&header)
|
||||
result.append(dialectData as Data)
|
||||
result.append(contextData as Data)
|
||||
return result
|
||||
@@ -107,7 +107,7 @@ extension SMB2 {
|
||||
let bufOffset = Int(self.header.bufferOffset) - MemoryLayout<SMB2.Header>.size
|
||||
let bufLen = Int(self.header.bufferLength)
|
||||
if bufOffset > 0 && bufLen > 0 && data.count >= bufOffset + bufLen {
|
||||
self.buffer = data.subdata(in: NSRange(location: bufOffset, length: bufLen))
|
||||
self.buffer = data.subdata(in: bufOffset..<(bufOffset + bufLen))
|
||||
} else {
|
||||
self.buffer = nil
|
||||
}
|
||||
@@ -194,7 +194,7 @@ extension SMB2 {
|
||||
var header = self.header
|
||||
header.bufferOffset = UInt16(MemoryLayout<SMB2.Header>.size + MemoryLayout<SessionSetupRequest.Header>.size)
|
||||
header.bufferLength = UInt16(buffer?.count ?? 0)
|
||||
var result = NSData(data: encode(&header)) as Data
|
||||
var result = encode(&header)
|
||||
if let buffer = self.buffer {
|
||||
result.append(buffer)
|
||||
}
|
||||
@@ -250,7 +250,7 @@ extension SMB2 {
|
||||
let bufOffset = Int(self.header.bufferOffset) - MemoryLayout<SMB2.Header>.size
|
||||
let bufLen = Int(self.header.bufferLength)
|
||||
if bufOffset > 0 && bufLen > 0 && data.count >= bufOffset + bufLen {
|
||||
self.buffer = data.subdata(in: NSRange(location: bufOffset, length: bufLen))
|
||||
self.buffer = data.subdata(in: bufOffset..<(bufOffset + bufLen))
|
||||
} else {
|
||||
self.buffer = nil
|
||||
}
|
||||
|
||||
@@ -27,14 +27,14 @@ extension SMB2 {
|
||||
}
|
||||
self.header = header
|
||||
let path = "\\\\\(host)\\\(share)"
|
||||
self.buffer = path.data(using: String.Encoding.utf16)
|
||||
self.buffer = path.data(using: .utf16)
|
||||
}
|
||||
|
||||
func data() -> Data {
|
||||
var header = self.header
|
||||
header.pathOffset = UInt16(MemoryLayout<SMB2.Header>.size + MemoryLayout<TreeConnectRequest.Header>.size)
|
||||
header.pathLength = UInt16(buffer?.count ?? 0)
|
||||
var result = NSData(data: encode(&header)) as Data
|
||||
var result = encode(&header)
|
||||
if let buffer = self.buffer {
|
||||
result.append(buffer)
|
||||
}
|
||||
|
||||
@@ -10,15 +10,13 @@ import Foundation
|
||||
|
||||
internal func encode<T>(_ value: inout T) -> Data {
|
||||
return withUnsafePointer(to: &value) { p in
|
||||
NSData(bytes: p, length: MemoryLayout.size(ofValue: value)) as Data
|
||||
Data(bytes: p, count: MemoryLayout.size(ofValue: value))
|
||||
}
|
||||
}
|
||||
|
||||
internal func encode<T>(_ value: T) -> Data {
|
||||
var value = value
|
||||
return withUnsafePointer(to: &value) { p in
|
||||
NSData(bytes: p, length: MemoryLayout.size(ofValue: value)) as Data
|
||||
}
|
||||
return encode(&value)
|
||||
}
|
||||
|
||||
internal func decode<T>(_ data: Data) -> T {
|
||||
@@ -155,10 +153,5 @@ struct SMB2 {
|
||||
|
||||
// MARK: SMB2 Oplock Break
|
||||
|
||||
}
|
||||
|
||||
extension Data {
|
||||
func subdata(in range: NSRange) -> Data {
|
||||
return (self as NSData).subdata(with: range)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -7,26 +7,6 @@
|
||||
//
|
||||
|
||||
import Foundation
|
||||
fileprivate func < <T : Comparable>(lhs: T?, rhs: T?) -> Bool {
|
||||
switch (lhs, rhs) {
|
||||
case let (l?, r?):
|
||||
return l < r
|
||||
case (nil, _?):
|
||||
return true
|
||||
default:
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
fileprivate func >= <T : Comparable>(lhs: T?, rhs: T?) -> Bool {
|
||||
switch (lhs, rhs) {
|
||||
case let (l?, r?):
|
||||
return l >= r
|
||||
default:
|
||||
return !(lhs < rhs)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public final class WebDavFileObject: FileObject {
|
||||
public let contentType: String
|
||||
@@ -89,26 +69,24 @@ open class WebDAVFileProvider: NSObject, FileProviderBasic {
|
||||
request.httpMethod = "PROPFIND"
|
||||
request.setValue("1", forHTTPHeaderField: "Depth")
|
||||
request.setValue("text/xml; charset=\"utf-8\"", forHTTPHeaderField: "Content-Type")
|
||||
request.httpBody = "<?xml version=\"1.0\" encoding=\"utf-8\" ?>\n<D:propfind xmlns:D=\"DAV:\">\n<D:allprop/></D:propfind>".data(using: String.Encoding.utf8)
|
||||
request.httpBody = "<?xml version=\"1.0\" encoding=\"utf-8\" ?>\n<D:propfind xmlns:D=\"DAV:\">\n<D:allprop/></D:propfind>".data(using: .utf8)
|
||||
request.setValue(String(request.httpBody!.count), forHTTPHeaderField: "Content-Length")
|
||||
let task = session.dataTask(with: request, completionHandler: { (data, response, error) in
|
||||
var responseError: FileProviderWebDavError?
|
||||
if let code = (response as? HTTPURLResponse)?.statusCode , code >= 300, let rCode = FileProviderHTTPErrorCode(rawValue: code) {
|
||||
responseError = FileProviderWebDavError(code: rCode, url: url)
|
||||
}
|
||||
var fileObjects = [WebDavFileObject]()
|
||||
if let data = data {
|
||||
let xresponse = self.parseXMLResponse(data)
|
||||
var fileObjects = [WebDavFileObject]()
|
||||
for attr in xresponse {
|
||||
if attr.href.path == url.path {
|
||||
continue
|
||||
}
|
||||
fileObjects.append(self.mapToFileObject(attr))
|
||||
}
|
||||
completionHandler(fileObjects, responseError ?? error)
|
||||
return
|
||||
}
|
||||
completionHandler([], responseError ?? error)
|
||||
completionHandler(fileObjects, responseError ?? error)
|
||||
})
|
||||
task.resume()
|
||||
}
|
||||
@@ -119,7 +97,7 @@ open class WebDAVFileProvider: NSObject, FileProviderBasic {
|
||||
request.httpMethod = "PROPFIND"
|
||||
request.setValue("1", forHTTPHeaderField: "Depth")
|
||||
request.setValue("text/xml; charset=\"utf-8\"", forHTTPHeaderField: "Content-Type")
|
||||
request.httpBody = "<?xml version=\"1.0\" encoding=\"utf-8\" ?>\n<D:propfind xmlns:D=\"DAV:\">\n<D:allprop/></D:propfind>".data(using: String.Encoding.utf8)
|
||||
request.httpBody = "<?xml version=\"1.0\" encoding=\"utf-8\" ?>\n<D:propfind xmlns:D=\"DAV:\">\n<D:allprop/></D:propfind>".data(using: .utf8)
|
||||
request.setValue(String(request.httpBody!.count), forHTTPHeaderField: "Content-Length")
|
||||
let task = session.dataTask(with: request, completionHandler: { (data, response, error) in
|
||||
var responseError: FileProviderWebDavError?
|
||||
@@ -149,19 +127,19 @@ open class WebDAVFileProvider: NSObject, FileProviderBasic {
|
||||
request.httpMethod = "PROPFIND"
|
||||
request.setValue("0", forHTTPHeaderField: "Depth")
|
||||
request.setValue("text/xml; charset=\"utf-8\"", forHTTPHeaderField: "Content-Type")
|
||||
request.httpBody = "<?xml version=\"1.0\" encoding=\"utf-8\" ?>\n<D:propfind xmlns:D=\"DAV:\">\n<D:prop><D:quota-available-bytes/><D:quota-used-bytes/></D:prop>\n</D:propfind>".data(using: String.Encoding.utf8)
|
||||
request.httpBody = "<?xml version=\"1.0\" encoding=\"utf-8\" ?>\n<D:propfind xmlns:D=\"DAV:\">\n<D:prop><D:quota-available-bytes/><D:quota-used-bytes/></D:prop>\n</D:propfind>".data(using: .utf8)
|
||||
request.setValue(String(request.httpBody!.count), forHTTPHeaderField: "Content-Length")
|
||||
let task = session.dataTask(with: request, completionHandler: { (data, response, error) in
|
||||
var totalSize: Int64 = -1
|
||||
var usedSize: Int64 = 0
|
||||
if let data = data {
|
||||
let xresponse = self.parseXMLResponse(data)
|
||||
if let attr = xresponse.first {
|
||||
let totalSize = Int64(attr.prop["quota-available-bytes"] ?? "")
|
||||
let usedSize = Int64(attr.prop["quota-used-bytes"] ?? "")
|
||||
completionHandler(totalSize ?? -1, usedSize ?? 0)
|
||||
return
|
||||
totalSize = Int64(attr.prop["quota-available-bytes"] ?? "") ?? -1
|
||||
usedSize = Int64(attr.prop["quota-used-bytes"] ?? "") ?? 0
|
||||
}
|
||||
}
|
||||
completionHandler(-1, 0)
|
||||
completionHandler(totalSize, usedSize)
|
||||
})
|
||||
task.resume()
|
||||
}
|
||||
@@ -170,7 +148,11 @@ open class WebDAVFileProvider: NSObject, FileProviderBasic {
|
||||
}
|
||||
|
||||
extension WebDAVFileProvider: FileProviderOperations {
|
||||
public func create(folder folderName: String, at atPath: String, completionHandler: SimpleCompletionHandler) {
|
||||
@discardableResult
|
||||
public func create(folder folderName: String, at atPath: String, completionHandler: SimpleCompletionHandler) -> OperationHandle? {
|
||||
guard fileOperationDelegate?.fileProvider(self, shouldDoOperation: .create(path: (atPath as NSString).appendingPathComponent(folderName) + "/")) ?? true == true else {
|
||||
return nil
|
||||
}
|
||||
let url = absoluteURL((atPath as NSString).appendingPathComponent(folderName) + "/")
|
||||
var request = URLRequest(url: url)
|
||||
request.httpMethod = "MKCOL"
|
||||
@@ -179,17 +161,18 @@ extension WebDAVFileProvider: FileProviderOperations {
|
||||
if let code = (response as? HTTPURLResponse)?.statusCode , code >= 300, let rCode = FileProviderHTTPErrorCode(rawValue: code) {
|
||||
responseError = FileProviderWebDavError(code: rCode, url: url)
|
||||
}
|
||||
if let response = response as? HTTPURLResponse, let code = FileProviderHTTPErrorCode(rawValue: response.statusCode) , code != .ok {
|
||||
completionHandler?(FileProviderWebDavError(code: code, url: url))
|
||||
return
|
||||
}
|
||||
completionHandler?(responseError ?? error)
|
||||
self.delegateNotify(.create(path: (atPath as NSString).appendingPathComponent(folderName) + "/"), error: responseError ?? error)
|
||||
})
|
||||
})
|
||||
task.resume()
|
||||
return RemoteOperationHandle(tasks: [task])
|
||||
}
|
||||
|
||||
public func create(file fileAttribs: FileObject, at path: String, contents data: Data?, completionHandler: SimpleCompletionHandler) {
|
||||
@discardableResult
|
||||
public func create(file fileAttribs: FileObject, at path: String, contents data: Data?, completionHandler: SimpleCompletionHandler) -> OperationHandle? {
|
||||
guard fileOperationDelegate?.fileProvider(self, shouldDoOperation: .create(path: path)) ?? true == true else {
|
||||
return nil
|
||||
}
|
||||
let url = absoluteURL(path)
|
||||
var request = URLRequest(url: url)
|
||||
request.httpMethod = "PUT"
|
||||
@@ -200,20 +183,29 @@ extension WebDAVFileProvider: FileProviderOperations {
|
||||
}
|
||||
completionHandler?(responseError ?? error)
|
||||
self.delegateNotify(.create(path: (path as NSString).appendingPathComponent(fileAttribs.name)), error: responseError ?? error)
|
||||
})
|
||||
})
|
||||
task.taskDescription = dictionaryToJSON(["type": "Create" as NSString, "source": (path as NSString).appendingPathComponent(fileAttribs.name) as NSString])
|
||||
task.resume()
|
||||
return RemoteOperationHandle(tasks: [task])
|
||||
}
|
||||
|
||||
public func moveItem(path: String, to toPath: String, overwrite: Bool = false, completionHandler: SimpleCompletionHandler) {
|
||||
self.copyMoveItem(move: true, path: path, toPath: toPath, overwrite: overwrite, completionHandler: completionHandler)
|
||||
@discardableResult
|
||||
public func moveItem(path: String, to toPath: String, overwrite: Bool = false, completionHandler: SimpleCompletionHandler) -> OperationHandle? {
|
||||
guard fileOperationDelegate?.fileProvider(self, shouldDoOperation: .move(source: path, destination: toPath)) ?? true == true else {
|
||||
return nil
|
||||
}
|
||||
return self.copyMoveItem(move: true, path: path, toPath: toPath, overwrite: overwrite, completionHandler: completionHandler)
|
||||
}
|
||||
|
||||
public func copyItem(path: String, to toPath: String, overwrite: Bool = false, completionHandler: SimpleCompletionHandler) {
|
||||
self.copyMoveItem(move: false, path: path, toPath: toPath, overwrite: overwrite, completionHandler: completionHandler)
|
||||
@discardableResult
|
||||
public func copyItem(path: String, to toPath: String, overwrite: Bool = false, completionHandler: SimpleCompletionHandler) -> OperationHandle? {
|
||||
guard fileOperationDelegate?.fileProvider(self, shouldDoOperation: .copy(source: path, destination: toPath)) ?? true == true else {
|
||||
return nil
|
||||
}
|
||||
return self.copyMoveItem(move: false, path: path, toPath: toPath, overwrite: overwrite, completionHandler: completionHandler)
|
||||
}
|
||||
|
||||
fileprivate func copyMoveItem(move:Bool, path: String, toPath: String, overwrite: Bool, completionHandler: SimpleCompletionHandler) {
|
||||
fileprivate func copyMoveItem(move:Bool, path: String, toPath: String, overwrite: Bool, completionHandler: SimpleCompletionHandler) -> OperationHandle? {
|
||||
let url = absoluteURL(path)
|
||||
var request = URLRequest(url: url)
|
||||
if move {
|
||||
@@ -221,56 +213,68 @@ extension WebDAVFileProvider: FileProviderOperations {
|
||||
} else {
|
||||
request.httpMethod = "COPY"
|
||||
}
|
||||
request.setValue(absoluteURL(path).uw_absoluteString, forHTTPHeaderField: "Destination")
|
||||
request.setValue(absoluteURL(path).absoluteString, forHTTPHeaderField: "Destination")
|
||||
if !overwrite {
|
||||
request.setValue("F", forHTTPHeaderField: "Overwrite")
|
||||
}
|
||||
let task = session.dataTask(with: request, completionHandler: { (data, response, error) in
|
||||
var responseError: FileProviderWebDavError?
|
||||
if let response = response as? HTTPURLResponse, let code = FileProviderHTTPErrorCode(rawValue: response.statusCode) {
|
||||
defer {
|
||||
let op = move ? FileOperation.move(source: path, destination: toPath) : .copy(source: path, destination: toPath)
|
||||
self.delegateNotify(op, error: error)
|
||||
if response.statusCode >= 300 {
|
||||
responseError = FileProviderWebDavError(code: code, url: url)
|
||||
}
|
||||
if code == .multiStatus, let data = data {
|
||||
let xresponses = self.parseXMLResponse(data)
|
||||
for xresponse in xresponses where xresponse.status >= 300 {
|
||||
for xresponse in xresponses where (xresponse.status ?? 0) >= 300 {
|
||||
completionHandler?(FileProviderWebDavError(code: code, url: url))
|
||||
}
|
||||
} else {
|
||||
completionHandler?(FileProviderWebDavError(code: code, url: url))
|
||||
}
|
||||
return
|
||||
}
|
||||
completionHandler?(error)
|
||||
})
|
||||
if (response as? HTTPURLResponse)?.statusCode ?? 0 != FileProviderHTTPErrorCode.multiStatus.rawValue {
|
||||
completionHandler?(responseError ?? error)
|
||||
}
|
||||
let op = move ? FileOperationType.move(source: path, destination: toPath) : .copy(source: path, destination: toPath)
|
||||
self.delegateNotify(op, error: responseError ?? error)
|
||||
})
|
||||
task.resume()
|
||||
return RemoteOperationHandle(tasks: [task])
|
||||
}
|
||||
|
||||
public func removeItem(path: String, completionHandler: SimpleCompletionHandler) {
|
||||
@discardableResult
|
||||
public func removeItem(path: String, completionHandler: SimpleCompletionHandler) -> OperationHandle? {
|
||||
guard fileOperationDelegate?.fileProvider(self, shouldDoOperation: .remove(path: path)) ?? true == true else {
|
||||
return nil
|
||||
}
|
||||
let url = absoluteURL(path)
|
||||
var request = URLRequest(url: url)
|
||||
request.httpMethod = "DELETE"
|
||||
let task = session.dataTask(with: request, completionHandler: { (data, response, error) in
|
||||
var responseError: FileProviderWebDavError?
|
||||
if let response = response as? HTTPURLResponse, let code = FileProviderHTTPErrorCode(rawValue: response.statusCode) {
|
||||
defer {
|
||||
self.delegateNotify(.remove(path: path), error: error)
|
||||
if response.statusCode >= 300 {
|
||||
responseError = FileProviderWebDavError(code: code, url: url)
|
||||
}
|
||||
if code == .multiStatus, let data = data {
|
||||
let xresponses = self.parseXMLResponse(data)
|
||||
for xresponse in xresponses where xresponse.status >= 300 {
|
||||
for xresponse in xresponses where (xresponse.status ?? 0) >= 300 {
|
||||
completionHandler?(FileProviderWebDavError(code: code, url: url))
|
||||
}
|
||||
} else {
|
||||
completionHandler?(FileProviderWebDavError(code: code, url: url))
|
||||
}
|
||||
return
|
||||
}
|
||||
completionHandler?(error)
|
||||
})
|
||||
if (response as? HTTPURLResponse)?.statusCode ?? 0 != FileProviderHTTPErrorCode.multiStatus.rawValue {
|
||||
completionHandler?(responseError ?? error)
|
||||
}
|
||||
self.delegateNotify(.remove(path: path), error: responseError ?? error)
|
||||
})
|
||||
task.resume()
|
||||
return RemoteOperationHandle(tasks: [task])
|
||||
}
|
||||
|
||||
public func copyItem(localFile: URL, to toPath: String, completionHandler: SimpleCompletionHandler) {
|
||||
@discardableResult
|
||||
public func copyItem(localFile: URL, to toPath: String, completionHandler: SimpleCompletionHandler) -> OperationHandle? {
|
||||
guard fileOperationDelegate?.fileProvider(self, shouldDoOperation: .copy(source: localFile.absoluteString, destination: toPath)) ?? true == true else {
|
||||
return nil
|
||||
}
|
||||
let url = absoluteURL(toPath)
|
||||
var request = URLRequest(url: url)
|
||||
request.httpMethod = "PUT"
|
||||
@@ -280,13 +284,18 @@ extension WebDAVFileProvider: FileProviderOperations {
|
||||
responseError = FileProviderWebDavError(code: rCode, url: url)
|
||||
}
|
||||
completionHandler?(responseError ?? error)
|
||||
self.delegateNotify(.move(source: localFile.uw_absoluteString, destination: toPath), error: responseError ?? error)
|
||||
self.delegateNotify(.copy(source: localFile.absoluteString, destination: toPath), error: responseError ?? error)
|
||||
})
|
||||
task.taskDescription = dictionaryToJSON(["type": "Copy" as NSString, "source": localFile.uw_absoluteString as NSString, "dest": toPath as NSString])
|
||||
task.taskDescription = dictionaryToJSON(["type": "Copy" as NSString, "source": localFile.absoluteString as NSString, "dest": toPath as NSString])
|
||||
task.resume()
|
||||
return RemoteOperationHandle(tasks: [task])
|
||||
}
|
||||
|
||||
public func copyItem(path: String, toLocalURL: URL, completionHandler: SimpleCompletionHandler) {
|
||||
@discardableResult
|
||||
public func copyItem(path: String, toLocalURL: URL, completionHandler: SimpleCompletionHandler) -> OperationHandle? {
|
||||
guard fileOperationDelegate?.fileProvider(self, shouldDoOperation: .copy(source: path, destination: toLocalURL.absoluteString)) ?? true == true else {
|
||||
return nil
|
||||
}
|
||||
let url = absoluteURL(path)
|
||||
let request = URLRequest(url: url)
|
||||
let task = session.downloadTask(with: request, completionHandler: { (sourceFileURL, response, error) in
|
||||
@@ -303,18 +312,22 @@ extension WebDAVFileProvider: FileProviderOperations {
|
||||
}
|
||||
}
|
||||
completionHandler?(responseError ?? error)
|
||||
self.delegateNotify(.copy(source: path, destination: toLocalURL.absoluteString), error: responseError ?? error)
|
||||
})
|
||||
task.taskDescription = dictionaryToJSON(["type": "Copy" as NSString, "source": path as NSString, "dest": toLocalURL.uw_absoluteString as NSString])
|
||||
task.taskDescription = dictionaryToJSON(["type": "Copy" as NSString, "source": path as NSString, "dest": toLocalURL.absoluteString as NSString])
|
||||
task.resume()
|
||||
return RemoteOperationHandle(tasks: [task])
|
||||
}
|
||||
}
|
||||
|
||||
extension WebDAVFileProvider: FileProviderReadWrite {
|
||||
public func contents(path: String, completionHandler: @escaping ((_ contents: Data?, _ error: Error?) -> Void)) {
|
||||
self.contents(path: path, offset: 0, length: -1, completionHandler: completionHandler)
|
||||
@discardableResult
|
||||
public func contents(path: String, completionHandler: @escaping ((_ contents: Data?, _ error: Error?) -> Void)) -> OperationHandle? {
|
||||
return self.contents(path: path, offset: 0, length: -1, completionHandler: completionHandler)
|
||||
}
|
||||
|
||||
public func contents(path: String, offset: Int64, length: Int, completionHandler: @escaping ((_ contents: Data?, _ error: Error?) -> Void)) {
|
||||
@discardableResult
|
||||
public func contents(path: String, offset: Int64, length: Int, completionHandler: @escaping ((_ contents: Data?, _ error: Error?) -> Void)) -> OperationHandle? {
|
||||
let url = absoluteURL(path)
|
||||
var request = URLRequest(url: url)
|
||||
request.httpMethod = "GET"
|
||||
@@ -331,11 +344,16 @@ extension WebDAVFileProvider: FileProviderReadWrite {
|
||||
completionHandler(data, responseError ?? error)
|
||||
})
|
||||
task.resume()
|
||||
return RemoteOperationHandle(tasks: [task])
|
||||
}
|
||||
|
||||
public func writeContents(path: String, contents data: Data, atomically: Bool = false, completionHandler: SimpleCompletionHandler) {
|
||||
@discardableResult
|
||||
public func writeContents(path: String, contents data: Data, atomically: Bool = false, completionHandler: SimpleCompletionHandler) -> OperationHandle? {
|
||||
guard fileOperationDelegate?.fileProvider(self, shouldDoOperation: .modify(path: path)) ?? true == true else {
|
||||
return nil
|
||||
}
|
||||
// FIXME: lock destination before writing process
|
||||
let url = atomically ? absoluteURL(path).uw_URLByAppendingPathExtension("tmp") : absoluteURL(path)
|
||||
let url = atomically ? absoluteURL(path).appendingPathExtension("tmp") : absoluteURL(path)
|
||||
var request = URLRequest(url: url)
|
||||
request.httpMethod = "PUT"
|
||||
let task = session.uploadTask(with: request, from: data, completionHandler: { (data, response, error) in
|
||||
@@ -356,6 +374,7 @@ extension WebDAVFileProvider: FileProviderReadWrite {
|
||||
})
|
||||
task.taskDescription = dictionaryToJSON(["type": "Modify" as NSString, "source": path as NSString])
|
||||
task.resume()
|
||||
return RemoteOperationHandle(tasks: [task])
|
||||
}
|
||||
|
||||
public func searchFiles(path: String, recursive: Bool, query: String, foundItemHandler: ((FileObject) -> Void)?, completionHandler: @escaping ((_ files: [FileObject], _ error: Error?) -> Void)) {
|
||||
@@ -364,7 +383,7 @@ extension WebDAVFileProvider: FileProviderReadWrite {
|
||||
request.httpMethod = "PROPFIND"
|
||||
//request.setValue("1", forHTTPHeaderField: "Depth")
|
||||
request.setValue("text/xml; charset=\"utf-8\"", forHTTPHeaderField: "Content-Type")
|
||||
request.httpBody = "<?xml version=\"1.0\" encoding=\"utf-8\" ?>\n<D:propfind xmlns:D=\"DAV:\">\n<D:allprop/></D:propfind>".data(using: String.Encoding.utf8)
|
||||
request.httpBody = "<?xml version=\"1.0\" encoding=\"utf-8\" ?>\n<D:propfind xmlns:D=\"DAV:\">\n<D:allprop/></D:propfind>".data(using: .utf8)
|
||||
request.setValue(String(request.httpBody!.count), forHTTPHeaderField: "Content-Length")
|
||||
let task = session.dataTask(with: request, completionHandler: { (data, response, error) in
|
||||
// FIXME: paginating results
|
||||
@@ -504,7 +523,7 @@ internal extension WebDAVFileProvider {
|
||||
return WebDavFileObject(absoluteURL: href, name: name, path: href.path, size: size, contentType: contentType, createdDate: createdDate, modifiedDate: modifiedDate, fileType: isDirectory ? .directory : .regular, isHidden: false, isReadOnly: false, entryTag: entryTag)
|
||||
}
|
||||
|
||||
fileprivate func delegateNotify(_ operation: FileOperation, error: Error?) {
|
||||
fileprivate func delegateNotify(_ operation: FileOperationType, error: Error?) {
|
||||
DispatchQueue.main.async(execute: {
|
||||
if error == nil {
|
||||
self.delegate?.fileproviderSucceed(self, operation: operation)
|
||||
|
||||
Reference in New Issue
Block a user