Compare commits

..

8 Commits

Author SHA1 Message Date
Amir Abbas Mousavian 4f56e20441 createFile definition improved to resolve #10 2016-12-01 12:12:53 +03:30
Amir Abbas Mousavian 9dda618b73 Revert "createFile definition improved"
This reverts commit da60c05188.
2016-12-01 12:10:14 +03:30
Amir Abbas Mousavian da60c05188 createFile definition improved 2016-12-01 11:54:03 +03:30
Amir Abbas Mousavian 4366855d54 Added NSCopying conformance, SMB headers gardening 2016-11-28 19:40:09 +03:30
Amir Abbas Mousavian fe05fd83fe Replaced encode/decode methods with Data extension 2016-11-26 00:11:46 +03:30
Amir Abbas Mousavian 826d207e6b OperationHandle optimizations
- code refactoring for RemoteOperationHandle usage and description
- bug fix: move operation in Dropbox provider did copy
- bug fix: dynamic inProgress result for RemoteOperationHandle
2016-11-24 22:54:00 +03:30
Amir Abbas Mousavian 66fc1e1284 Fixed unexpected behaviors in Remote providers:
- Calling delegate and completion methods
- refactoring codes
2016-11-23 23:42:36 +03:30
Amir Abbas Mousavian a1d489f5a5 Optimization in project settings for dynamic linking 2016-11-23 19:43:28 +03:30
29 changed files with 754 additions and 687 deletions
+4 -4
View File
@@ -16,8 +16,8 @@ Pod::Spec.new do |s|
#
s.name = "FileProvider"
s.version = "0.6.0"
s.summary = "FileManager replacement for Local and Remote (WebDAV/Dropbox/SMB2) files on iOS and MacOS."
s.version = "0.7.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"
+27 -48
View File
@@ -10,9 +10,7 @@
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 */; };
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 */; };
791950F51DE58A5400B4426E /* libxml2.tbd in Frameworks */ = {isa = PBXBuildFile; fileRef = 791950F41DE58A5400B4426E /* libxml2.tbd */; };
7924B1961D89DAE000589DB7 /* Document.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7924B18D1D89DAE000589DB7 /* Document.swift */; };
7924B1971D89DAE000589DB7 /* Document.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7924B18D1D89DAE000589DB7 /* Document.swift */; };
7924B1981D89DAE000589DB7 /* Document.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7924B18D1D89DAE000589DB7 /* Document.swift */; };
@@ -22,9 +20,6 @@
7924B19C1D89DAE000589DB7 /* Error.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7924B18F1D89DAE000589DB7 /* Error.swift */; };
7924B19D1D89DAE000589DB7 /* Error.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7924B18F1D89DAE000589DB7 /* Error.swift */; };
7924B19E1D89DAE000589DB7 /* Error.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7924B18F1D89DAE000589DB7 /* Error.swift */; };
7924B19F1D89DAE000589DB7 /* Info.plist in Resources */ = {isa = PBXBuildFile; fileRef = 7924B1901D89DAE000589DB7 /* Info.plist */; };
7924B1A01D89DAE000589DB7 /* Info.plist in Resources */ = {isa = PBXBuildFile; fileRef = 7924B1901D89DAE000589DB7 /* Info.plist */; };
7924B1A11D89DAE000589DB7 /* Info.plist in Resources */ = {isa = PBXBuildFile; fileRef = 7924B1901D89DAE000589DB7 /* Info.plist */; };
7924B1A21D89DAE000589DB7 /* Options.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7924B1911D89DAE000589DB7 /* Options.swift */; };
7924B1A31D89DAE000589DB7 /* Options.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7924B1911D89DAE000589DB7 /* Options.swift */; };
7924B1A41D89DAE000589DB7 /* Options.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7924B1911D89DAE000589DB7 /* Options.swift */; };
@@ -98,11 +93,10 @@
/* Begin PBXFileReference section */
7902C0851D61B56D00564440 /* RemoteSession.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RemoteSession.swift; sourceTree = "<group>"; };
7924B18C1D89DAE000589DB7 /* AEXML.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AEXML.h; 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; };
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>"; };
7924B18F1D89DAE000589DB7 /* Error.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Error.swift; sourceTree = "<group>"; };
7924B1901D89DAE000589DB7 /* Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
7924B1911D89DAE000589DB7 /* Options.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Options.swift; sourceTree = "<group>"; };
7924B1921D89DAE000589DB7 /* Parser.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Parser.swift; sourceTree = "<group>"; };
7924B1A81D89F79200589DB7 /* FPSStreamTask.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = FPSStreamTask.swift; sourceTree = "<group>"; };
@@ -140,6 +134,7 @@
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
files = (
791950F51DE58A5400B4426E /* libxml2.tbd in Frameworks */,
);
runOnlyForDeploymentPostprocessing = 0;
};
@@ -160,14 +155,20 @@
/* End PBXFrameworksBuildPhase section */
/* Begin PBXGroup section */
791950F31DE58A5300B4426E /* Frameworks */ = {
isa = PBXGroup;
children = (
791950F41DE58A5400B4426E /* libxml2.tbd */,
);
name = Frameworks;
sourceTree = "<group>";
};
7924B18B1D89DAE000589DB7 /* AEXML */ = {
isa = PBXGroup;
children = (
7924B18C1D89DAE000589DB7 /* AEXML.h */,
7924B18D1D89DAE000589DB7 /* Document.swift */,
7924B18E1D89DAE000589DB7 /* Element.swift */,
7924B18F1D89DAE000589DB7 /* Error.swift */,
7924B1901D89DAE000589DB7 /* Info.plist */,
7924B1911D89DAE000589DB7 /* Options.swift */,
7924B1921D89DAE000589DB7 /* Parser.swift */,
);
@@ -180,6 +181,7 @@
799396911D48C02300086753 /* Sources */,
7993968A1D48B8C700086753 /* Pod */,
799396681D48B7F600086753 /* Products */,
791950F31DE58A5300B4426E /* Frameworks */,
);
sourceTree = "<group>";
};
@@ -206,9 +208,9 @@
799396911D48C02300086753 /* Sources */ = {
isa = PBXGroup;
children = (
7924B18B1D89DAE000589DB7 /* AEXML */,
799396991D48C02300086753 /* SMBTypes */,
799396931D48C02300086753 /* DropboxFileProvider.swift */,
7924B18B1D89DAE000589DB7 /* AEXML */,
794C21FD1D58912A00EC49B8 /* DropboxHelper.swift */,
799396941D48C02300086753 /* FileProvider.h */,
799396951D48C02300086753 /* FileProvider.swift */,
@@ -249,7 +251,6 @@
isa = PBXHeadersBuildPhase;
buildActionMask = 2147483647;
files = (
7924B1931D89DAE000589DB7 /* AEXML.h in Headers */,
);
runOnlyForDeploymentPostprocessing = 0;
};
@@ -257,7 +258,6 @@
isa = PBXHeadersBuildPhase;
buildActionMask = 2147483647;
files = (
7924B1941D89DAE000589DB7 /* AEXML.h in Headers */,
);
runOnlyForDeploymentPostprocessing = 0;
};
@@ -265,7 +265,6 @@
isa = PBXHeadersBuildPhase;
buildActionMask = 2147483647;
files = (
7924B1951D89DAE000589DB7 /* AEXML.h in Headers */,
);
runOnlyForDeploymentPostprocessing = 0;
};
@@ -332,7 +331,7 @@
7993965C1D48B7BF00086753 /* Project object */ = {
isa = PBXProject;
attributes = {
LastUpgradeCheck = 0800;
LastUpgradeCheck = 0810;
TargetAttributes = {
799396661D48B7F600086753 = {
CreatedOnToolsVersion = 7.3.1;
@@ -370,7 +369,6 @@
isa = PBXResourcesBuildPhase;
buildActionMask = 2147483647;
files = (
7924B19F1D89DAE000589DB7 /* Info.plist in Resources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
@@ -378,7 +376,6 @@
isa = PBXResourcesBuildPhase;
buildActionMask = 2147483647;
files = (
7924B1A01D89DAE000589DB7 /* Info.plist in Resources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
@@ -386,7 +383,6 @@
isa = PBXResourcesBuildPhase;
buildActionMask = 2147483647;
files = (
7924B1A11D89DAE000589DB7 /* Info.plist in Resources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
@@ -501,6 +497,7 @@
799396601D48B7BF00086753 /* Debug */ = {
isa = XCBuildConfiguration;
buildSettings = {
BUNDLE_VERSION_STRING = 0.7.0;
CLANG_WARN_BOOL_CONVERSION = YES;
CLANG_WARN_CONSTANT_CONVERSION = YES;
CLANG_WARN_EMPTY_BODY = YES;
@@ -510,9 +507,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,13 +520,14 @@
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
ONLY_ACTIVE_ARCH = YES;
SWIFT_VERSION = 3.0;
SWIFT_VERSION = 3.0.1;
};
name = Debug;
};
799396611D48B7BF00086753 /* Release */ = {
isa = XCBuildConfiguration;
buildSettings = {
BUNDLE_VERSION_STRING = 0.7.0;
CLANG_WARN_BOOL_CONVERSION = YES;
CLANG_WARN_CONSTANT_CONVERSION = YES;
CLANG_WARN_EMPTY_BODY = YES;
@@ -536,6 +537,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;
@@ -545,7 +548,7 @@
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule";
SWIFT_VERSION = 3.0;
SWIFT_VERSION = 3.0.1;
};
name = Release;
};
@@ -554,9 +557,8 @@
buildSettings = {
ALWAYS_SEARCH_USER_PATHS = NO;
APPLICATION_EXTENSION_API_ONLY = YES;
BUNDLE_VERSION_STRING = 0.7.1;
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;
@@ -568,19 +570,15 @@
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
CLANG_WARN_UNREACHABLE_CODE = YES;
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
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)",
@@ -596,7 +594,6 @@
IPHONEOS_DEPLOYMENT_TARGET = 8.0;
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
MTL_ENABLE_DEBUG_INFO = YES;
ONLY_ACTIVE_ARCH = YES;
PRODUCT_BUNDLE_IDENTIFIER = "com.mousavian.FileProvider-iOS";
PRODUCT_NAME = FileProvider;
SDKROOT = iphoneos;
@@ -613,9 +610,8 @@
buildSettings = {
ALWAYS_SEARCH_USER_PATHS = NO;
APPLICATION_EXTENSION_API_ONLY = YES;
BUNDLE_VERSION_STRING = 0.7.1;
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,16 +623,13 @@
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
CLANG_WARN_UNREACHABLE_CODE = YES;
CLANG_WARN__DUPLICATE_METHOD_MATCH = 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;
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;
@@ -680,9 +673,7 @@
CLANG_WARN_UNREACHABLE_CODE = YES;
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
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;
@@ -690,10 +681,8 @@
ENABLE_STRICT_OBJC_MSGSEND = YES;
ENABLE_TESTABILITY = YES;
FRAMEWORK_VERSION = A;
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)",
@@ -709,7 +698,6 @@
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks @loader_path/Frameworks";
MACOSX_DEPLOYMENT_TARGET = 10.10;
MTL_ENABLE_DEBUG_INFO = YES;
ONLY_ACTIVE_ARCH = YES;
PRODUCT_BUNDLE_IDENTIFIER = "com.mousavian.FileProvider-OSX";
PRODUCT_NAME = FileProvider;
SDKROOT = macosx;
@@ -740,9 +728,7 @@
CLANG_WARN_UNREACHABLE_CODE = YES;
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
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;
@@ -750,7 +736,6 @@
ENABLE_NS_ASSERTIONS = NO;
ENABLE_STRICT_OBJC_MSGSEND = YES;
FRAMEWORK_VERSION = A;
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;
@@ -791,19 +776,16 @@
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
CLANG_WARN_UNREACHABLE_CODE = YES;
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
COPY_PHASE_STRIP = NO;
"CODE_SIGN_IDENTITY[sdk=appletvos*]" = "";
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)",
@@ -818,7 +800,6 @@
INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks";
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
MTL_ENABLE_DEBUG_INFO = YES;
ONLY_ACTIVE_ARCH = YES;
PRODUCT_BUNDLE_IDENTIFIER = "com.mousavian.FileProvider-tvOS";
PRODUCT_NAME = FileProvider;
SDKROOT = appletvos;
@@ -850,16 +831,14 @@
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
CLANG_WARN_UNREACHABLE_CODE = YES;
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
COPY_PHASE_STRIP = NO;
"CODE_SIGN_IDENTITY[sdk=appletvos*]" = "";
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;
@@ -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"
+1 -1
View File
@@ -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
View File
@@ -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
View File
@@ -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>
+3 -3
View File
@@ -115,7 +115,7 @@ Your class should conforms `FileProviderDelegate` class:
case .remove(path: let path):
print("\(path) has been deleted.")
default:
break
print("\(operation.actionDescription) from \(operation.source ?? "") to \(operation.destination) succeed")
}
}
@@ -126,7 +126,7 @@ Your class should conforms `FileProviderDelegate` class:
case .remove(path: let path):
print("\(path) can't be deleted.")
default:
break
print("\(operation.actionDescription) from \(operation.source ?? "") to \(operation.destination) failed")
}
}
@@ -255,7 +255,7 @@ If you want to retrieve a portion of file you can use `contents` method with off
### 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.
Creating/Copying/Deleting functions return a `OperationHandle` for remote operations. It provides operation type, 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.
+79 -70
View File
@@ -13,7 +13,7 @@ import CoreGraphics
// Because this class uses NSURLSession, it's necessary to disable App Transport Security
// in case of using this class with unencrypted HTTP connection.
open class DropboxFileProvider: NSObject, FileProviderBasic {
open class DropboxFileProvider: NSObject, FileProviderBasicRemote {
open static let type: String = "WebDAV"
open let isPathRelative: Bool = true
open let baseURL: URL?
@@ -25,24 +25,31 @@ open class DropboxFileProvider: NSObject, FileProviderBasic {
}
open weak var delegate: FileProviderDelegate?
open let credential: URLCredential?
open private(set) var cache: URLCache?
public var useCache: Bool = false
public var validatingCache: Bool = true
fileprivate var _session: URLSession?
fileprivate var sessionDelegate: SessionDelegate?
internal var session: URLSession {
public var session: URLSession {
if _session == nil {
self.sessionDelegate = SessionDelegate(fileProvider: self, credential: credential)
let queue = OperationQueue()
//queue.underlyingQueue = dispatch_queue
_session = URLSession(configuration: URLSessionConfiguration.default, delegate: sessionDelegate as URLSessionDelegate?, delegateQueue: queue)
let config = URLSessionConfiguration.default
config.urlCache = cache
config.requestCachePolicy = .returnCacheDataElseLoad
_session = URLSession(configuration: config, delegate: sessionDelegate as URLSessionDelegate?, delegateQueue: queue)
}
return _session!
}
public init? (credential: URLCredential?) {
public init? (credential: URLCredential?, cache: URLCache? = nil) {
self.baseURL = nil
dispatch_queue = DispatchQueue(label: "FileProvider.\(DropboxFileProvider.type)", attributes: DispatchQueue.Attributes.concurrent)
//let url = baseURL.uw_absoluteString
self.credential = credential
self.cache = cache
}
deinit {
@@ -64,20 +71,16 @@ open class DropboxFileProvider: NSObject, FileProviderBasic {
let requestDictionary = ["path": correctPath(path)! as NSString]
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(.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: .utf8)) : nil
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) {
completionHandler(file, dbError)
return
fileObject = file
}
completionHandler(nil, dbError)
return
}
completionHandler(nil, error)
completionHandler(fileObject, dbError ?? error)
})
task.resume()
}
@@ -88,13 +91,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
var totalSize: Int64 = -1
var usedSize: Int64 = 0
if let data = data, let jsonStr = String(data: data, 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
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()
}
@@ -110,8 +113,9 @@ extension DropboxFileProvider: FileProviderOperations {
return doOperation(.create(path: path), 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 create(file fileName: String, at path: String, contents data: Data?, completionHandler: SimpleCompletionHandler) -> OperationHandle? {
let filePath = (path as NSString).appendingPathComponent(fileName)
return self.writeContents(path: filePath, contents: data ?? Data(), completionHandler: completionHandler)
}
public func moveItem(path: String, to toPath: String, overwrite: Bool = false, completionHandler: SimpleCompletionHandler) -> OperationHandle? {
@@ -131,25 +135,18 @@ extension DropboxFileProvider: FileProviderOperations {
return nil
}
let url: String
var path: String?, fromPath: String?, toPath: String?
guard let sourcePath = operation.source else { return nil }
let destPath = operation.destination
switch operation {
case .create(path: let p):
case .create:
url = "https://api.dropboxapi.com/2/files/create_folder"
path = p
case .copy(source: let fp, destination: let tp):
case .copy:
url = "https://api.dropboxapi.com/2/files/copy"
fromPath = fp
toPath = tp
case .move(source: let fp, destination: let tp):
case .move:
url = "https://api.dropboxapi.com/2/files/move"
fromPath = fp
toPath = tp
case .modify(path: let p):
return nil
case .remove(path: let p):
case .remove:
url = "https://api.dropboxapi.com/2/files/delete"
path = p
case .link(link: _, target: _):
default: // modify, link, fetch
return nil
}
var request = URLRequest(url: URL(string: url)!)
@@ -157,31 +154,29 @@ extension DropboxFileProvider: FileProviderOperations {
request.setValue("Bearer \(credential?.password ?? "")", forHTTPHeaderField: "Authorization")
request.setValue("application/json", forHTTPHeaderField: "Content-Type")
var requestDictionary = [String: AnyObject]()
requestDictionary["path"] = correctPath(path) as NSString?
requestDictionary["from_path"] = correctPath(fromPath) as NSString?
requestDictionary["to_path"] = correctPath(toPath) as NSString?
if let dest = correctPath(destPath) as NSString? {
requestDictionary["from_path"] = correctPath(sourcePath) as NSString?
requestDictionary["to_path"] = dest
} else {
requestDictionary["path"] = correctPath(sourcePath) as NSString?
}
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: .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: sourcePath, errorDescription: String(data: data ?? Data(), encoding: .utf8))
}
completionHandler?(error)
})
completionHandler?(dbError ?? error)
self.delegateNotify(operation, error: dbError ?? error)
})
task.taskDescription = operation.json
task.resume()
return RemoteOperationHandle(tasks: [task])
return RemoteOperationHandle(operationType: operation, tasks: [task])
}
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 {
let opType = FileOperationType.copy(source: localFile.absoluteString, destination: toPath)
guard fileOperationDelegate?.fileProvider(self, shouldDoOperation: opType) ?? true == true else {
return nil
}
guard let data = try? Data(contentsOf: localFile) else {
@@ -189,11 +184,12 @@ extension DropboxFileProvider: FileProviderOperations {
completionHandler?(error)
return nil
}
return upload_simple(toPath, data: data, overwrite: true, operation: .copy(source: localFile.absoluteString, destination: toPath), completionHandler: completionHandler)
return upload_simple(toPath, data: data, overwrite: true, operation: opType, completionHandler: completionHandler)
}
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 {
let opType = FileOperationType.copy(source: path, destination: destURL.absoluteString)
guard fileOperationDelegate?.fileProvider(self, shouldDoOperation: opType) ?? true == true else {
return nil
}
let url = URL(string: "https://content.dropboxapi.com/2/files/download")!
@@ -217,9 +213,9 @@ extension DropboxFileProvider: FileProviderOperations {
completionHandler?(e)
}
})
task.taskDescription = dictionaryToJSON(["type": "Copy" as NSString, "source": path as NSString, "dest": destURL.absoluteString as NSString])
task.taskDescription = opType.json
task.resume()
return RemoteOperationHandle(tasks: [task])
return RemoteOperationHandle(operationType: opType, tasks: [task])
}
}
@@ -229,6 +225,7 @@ extension DropboxFileProvider: FileProviderReadWrite {
}
public func contents(path: String, offset: Int64, length: Int, completionHandler: @escaping ((_ contents: Data?, _ error: Error?) -> Void)) -> OperationHandle? {
let opType = FileOperationType.fetch(path: path)
let url = URL(string: "https://content.dropboxapi.com/2/files/download")!
var request = URLRequest(url: url)
request.httpMethod = "GET"
@@ -240,25 +237,26 @@ 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: .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.taskDescription = opType.json
task.resume()
return RemoteOperationHandle(tasks: [task])
return RemoteOperationHandle(operationType: opType, tasks: [task])
}
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 {
let opType = FileOperationType.modify(path: path)
guard fileOperationDelegate?.fileProvider(self, shouldDoOperation: opType) ?? true == true else {
return nil
}
// FIXME: remove 150MB restriction
return upload_simple(path, data: data, overwrite: true, operation: .modify(path: path), completionHandler: completionHandler)
return upload_simple(path, data: data, overwrite: true, operation: opType, completionHandler: completionHandler)
}
public func searchFiles(path: String, recursive: Bool, query: String, foundItemHandler: ((FileObject) -> Void)?, completionHandler: @escaping ((_ files: [FileObject], _ error: Error?) -> Void)) {
@@ -327,7 +325,7 @@ extension DropboxFileProvider: ExtendedFileProvider {
requestDictionary["format"] = "jpeg" as NSString
requestDictionary["size"] = "w\(Int(dimension.width))h\(Int(dimension.height))" as NSString
request.setValue(dictionaryToJSON(requestDictionary), forHTTPHeaderField: "Dropbox-API-Arg")
self.session.dataTask(with: request, completionHandler: { (data, response, error) in
let task = self.session.dataTask(with: request, completionHandler: { (data, response, error) in
var image: ImageClass? = nil
if let r = response as? HTTPURLResponse, let result = r.allHeaderFields["Dropbox-API-Result"] as? String, let jsonResult = jsonToDictionary(result) {
if jsonResult["error"] != nil {
@@ -338,7 +336,8 @@ extension DropboxFileProvider: ExtendedFileProvider {
image = ImageClass(data: data)
}
completionHandler(image, error)
})
})
task.resume()
}
public func propertiesOfFile(path: String, completionHandler: @escaping ((_ propertiesDictionary: [String : Any], _ keys: [String], _ error: Error?) -> Void)) {
@@ -346,4 +345,14 @@ extension DropboxFileProvider: ExtendedFileProvider {
}
}
extension DropboxFileProvider: FileProvider {}
extension DropboxFileProvider: FileProvider {
open func copy(with zone: NSZone? = nil) -> Any {
let copy = DropboxFileProvider(credential: self.credential, cache: self.cache)!
copy.currentPath = self.currentPath
copy.delegate = self.delegate
copy.fileOperationDelegate = self.fileOperationDelegate
copy.useCache = self.useCache
copy.validatingCache = self.validatingCache
return copy
}
}
+8 -26
View File
@@ -53,13 +53,13 @@ internal extension DropboxFileProvider {
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: .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,14 +69,13 @@ 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.taskDescription = FileOperationType.fetch(path: path).json
task.resume()
}
@@ -101,29 +100,12 @@ internal extension DropboxFileProvider {
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: .utf8))
}
defer {
self.delegateNotify(.create(path: targetPath), error: responseError ?? error)
}
completionHandler?(responseError ?? error)
self.delegateNotify(.create(path: targetPath), error: responseError ?? error)
})
var dic: [String: AnyObject] = ["type": operation.description as NSString]
switch operation {
case .create(path: let s):
dic["source"] = s as NSString
case .copy(source: let s, destination: let d):
dic["source"] = s as NSString
dic["dest"] = d as NSString
case .modify(path: let s):
dic["source"] = s as NSString
case .move(source: let s, destination: let d):
dic["source"] = s as NSString
dic["dest"] = d as NSString
default:
break
}
task.taskDescription = dictionaryToJSON(dic)
task.taskDescription = operation.json
task.resume()
return RemoteOperationHandle(tasks: [task])
return RemoteOperationHandle(operationType: operation, 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)) {
+96 -21
View File
@@ -114,13 +114,68 @@ public protocol FileProviderBasic: class {
func storageProperties(completionHandler: @escaping ((_ total: Int64, _ used: Int64) -> Void))
}
public protocol FileProviderBasicRemote: FileProviderBasic {
var session: URLSession { get }
var cache: URLCache? { get }
var useCache: Bool { get set }
var validatingCache: Bool { get set }
}
internal extension FileProviderBasicRemote {
func returnCachedDate(with request: URLRequest, validatingCache: Bool, completionHandler: @escaping (Data?, URLResponse?, Error?) -> Swift.Void) -> Bool {
guard let cache = self.cache else { return false }
if let response = cache.cachedResponse(for: request) {
var validatedCache = !validatingCache
let lastModifiedDate = (response.response as? HTTPURLResponse)?.allHeaderFields["Last-Modified"] as? String
let eTag = (response.response as? HTTPURLResponse)?.allHeaderFields["ETag"] as? String
if lastModifiedDate == nil && eTag == nil, validatingCache {
var validateRequest = request
validateRequest.httpMethod = "HEAD"
let group = DispatchGroup()
group.enter()
self.session.dataTask(with: validateRequest, completionHandler: { (_, response, e) in
if let httpResponse = response as? HTTPURLResponse {
let currentETag = httpResponse.allHeaderFields["ETag"] as? String
let currentLastModifiedDate = httpResponse.allHeaderFields["ETag"] as? String ?? "nonvalidetag"
validatedCache = (eTag != nil && currentETag == eTag)
|| (lastModifiedDate != nil && currentLastModifiedDate == lastModifiedDate)
}
group.leave()
}).resume()
_ = group.wait(timeout: DispatchTime.now() + self.session.configuration.timeoutIntervalForRequest)
}
if validatedCache {
completionHandler(response.data, response.response, nil)
return true
}
}
return false
}
func runDataTask(with request: URLRequest, operationHandle: RemoteOperationHandle? = nil, completionHandler: @escaping (Data?, URLResponse?, Error?) -> Swift.Void) {
let useCache = self.useCache
let validatingCache = self.validatingCache
dispatch_queue.async {
if useCache {
if self.returnCachedDate(with: request, validatingCache: validatingCache, completionHandler: completionHandler) {
return
}
}
let task = self.session.dataTask(with: request, completionHandler: completionHandler)
task.taskDescription = operationHandle?.operationType.json
operationHandle?.add(task: task)
task.resume()
}
}
}
public protocol FileProviderOperations: FileProviderBasic {
var fileOperationDelegate : FileOperationDelegate? { get set }
@discardableResult
func create(folder: String, at: String, completionHandler: SimpleCompletionHandler) -> OperationHandle?
@discardableResult
func create(file: FileObject, at: String, contents data: Data?, completionHandler: SimpleCompletionHandler) -> OperationHandle?
func create(file: String, at: String, contents data: Data?, completionHandler: SimpleCompletionHandler) -> OperationHandle?
@discardableResult
func moveItem(path: String, to: String, overwrite: Bool, completionHandler: SimpleCompletionHandler) -> OperationHandle?
@discardableResult
@@ -151,8 +206,7 @@ public protocol FileProviderMonitor: FileProviderBasic {
func isRegisteredForNotification(path: String) -> Bool
}
public protocol FileProvider: FileProviderBasic, FileProviderOperations, FileProviderReadWrite {
public protocol FileProvider: FileProviderBasic, FileProviderOperations, FileProviderReadWrite, NSCopying {
}
extension FileProviderBasic {
@@ -289,37 +343,58 @@ public enum FileOperationType: CustomStringConvertible {
case modify (path: String)
case remove (path: String)
case link (link: String, target: String)
case fetch (path: String)
public var description: String {
switch self {
case .create(path: _): return "Create"
case .copy(source: _, destination: _): return "Copy"
case .move(source: _, destination: _): return "Move"
case .modify(path: _): return "Modify"
case .remove(path: _): return "Remove"
case .link(link: _, target: _): return "Link"
case .create: return "Create"
case .copy: return "Copy"
case .move: return "Move"
case .modify: return "Modify"
case .remove: return "Remove"
case .link: return "Link"
case .fetch: return "Fetch"
}
}
internal var actionDescription: String {
switch self {
case .create(path: _): return "Creating"
case .copy(source: _, destination: _): return "Copying"
case .move(source: _, destination: _): return "Moving"
case .modify(path: _): return "Modifying"
case .remove(path: _): return "Removing"
case .link(link: _, target: _): return "Linking"
}
public var actionDescription: String {
return description.trimmingCharacters(in: CharacterSet(charactersIn: "e")) + "ing"
}
public var source: String? {
guard let reflect = Mirror(reflecting: self).children.first?.value else { return nil }
let mirror = Mirror(reflecting: reflect)
return reflect as? String ?? mirror.children.first?.value as? String
}
public var destination: String? {
guard let reflect = Mirror(reflecting: self).children.first?.value else { return nil }
let mirror = Mirror(reflecting: reflect)
return mirror.children.dropFirst().first?.value as? String
}
internal var json: String? {
var dictionary: [String: AnyObject] = ["type": self.description as NSString]
dictionary["source"] = source as NSString?
dictionary["dest"] = destination as NSString?
return dictionaryToJSON(dictionary)
}
}
@objc
public protocol OperationHandle {
var progress: Float { get }
var operationType: FileOperationType { get }
var bytesSoFar: Int64 { get }
var totalBytes: Int64 { get }
var inProgress: Bool { get }
func cancel()
func cancel() -> Bool
}
public extension OperationHandle {
public var progress: Float {
let bytesSoFar = self.bytesSoFar
let totalBytes = self.totalBytes
return totalBytes > 0 ? Float(Double(bytesSoFar) / Double(totalBytes)) : Float.nan
}
}
internal class Weak<T: AnyObject> {
+189 -52
View File
@@ -28,8 +28,8 @@ open class LocalFileProvider: FileProvider, FileProviderMonitor {
open weak var delegate: FileProviderDelegate?
open let credential: URLCredential? = nil
open let fileManager = FileManager()
open let opFileManager = FileManager()
open private(set) var fileManager = FileManager()
open private(set) var opFileManager = FileManager()
fileprivate var fileProviderManagerDelegate: LocalFileProviderManagerDelegate? = nil
public init () {
@@ -102,31 +102,32 @@ open class LocalFileProvider: FileProvider, FileProviderMonitor {
open weak var fileOperationDelegate : FileOperationDelegate?
@objc(createWithFolder:at:completionHandler:)
@discardableResult
open func create(folder folderName: String, at atPath: String, completionHandler: SimpleCompletionHandler) -> OperationHandle? {
let opType = FileOperationType.create(path: (atPath as NSString).appendingPathComponent(folderName) + "/")
operation_queue.async {
do {
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) + "/"))
self.delegate?.fileproviderSucceed(self, operation: opType)
})
} catch let e as NSError {
completionHandler?(e)
DispatchQueue.main.async(execute: {
self.delegate?.fileproviderFailed(self, operation: .create(path: (atPath as NSString).appendingPathComponent(folderName) + "/"))
self.delegate?.fileproviderFailed(self, operation: opType)
})
}
}
return nil
return LocalOperationHandle(operationType: opType, baseURL: self.baseURL)
}
@discardableResult
open func create(file fileAttribs: FileObject, at atPath: String, contents data: Data?, completionHandler: SimpleCompletionHandler) -> OperationHandle? {
open func create(file fileName: String, at atPath: String, contents data: Data?, completionHandler: SimpleCompletionHandler) -> OperationHandle? {
let opType = FileOperationType.create(path: (atPath as NSString).appendingPathComponent(fileName))
operation_queue.async {
let fileURL = self.absoluteURL(atPath).appendingPathComponent(fileAttribs.name)
var attributes = [String : Any]()
let fileURL = self.absoluteURL(atPath).appendingPathComponent(fileName)
/*var attributes = [String : Any]()
if let createdDate = fileAttribs.createdDate {
attributes[FileAttributeKey.creationDate.rawValue] = createdDate as NSDate
}
@@ -135,29 +136,29 @@ open class LocalFileProvider: FileProvider, FileProviderMonitor {
}
if fileAttribs.isReadOnly {
attributes[FileAttributeKey.posixPermissions.rawValue] = NSNumber(value: 365 as Int16)
}
let success = self.opFileManager.createFile(atPath: fileURL.path, contents: data, attributes: attributes)
}*/
let success = self.opFileManager.createFile(atPath: fileURL.path, contents: data, attributes: nil/*attributes*/)
if success {
do {
/*do {
try (fileURL as NSURL).setResourceValue(fileAttribs.isHidden, forKey: URLResourceKey.isHiddenKey)
} catch _ {}
} catch _ {}*/
completionHandler?(nil)
DispatchQueue.main.async(execute: {
self.delegate?.fileproviderSucceed(self, operation: .create(path: (atPath as NSString).appendingPathComponent(fileAttribs.name)))
self.delegate?.fileproviderSucceed(self, operation: opType)
})
} else {
completionHandler?(self.throwError(atPath, code: URLError.cannotCreateFile as FoundationErrorEnum))
DispatchQueue.main.async(execute: {
self.delegate?.fileproviderFailed(self, operation: .create(path: (atPath as NSString).appendingPathComponent(fileAttribs.name)))
self.delegate?.fileproviderFailed(self, operation: opType)
})
}
}
return nil
return LocalOperationHandle(operationType: opType, baseURL: self.baseURL)
}
@discardableResult
open func moveItem(path: String, to toPath: String, overwrite: Bool = false, completionHandler: SimpleCompletionHandler) -> OperationHandle? {
// FIXME: progress
let opType = FileOperationType.move(source: path, destination: toPath)
operation_queue.async {
if !overwrite && self.fileManager.fileExists(atPath: self.absoluteURL(toPath).path) {
completionHandler?(self.throwError(toPath, code: URLError.cannotMoveFile as FoundationErrorEnum))
@@ -167,21 +168,21 @@ open class LocalFileProvider: FileProvider, FileProviderMonitor {
try self.opFileManager.moveItem(at: self.absoluteURL(path), to: self.absoluteURL(toPath))
completionHandler?(nil)
DispatchQueue.main.async(execute: {
self.delegate?.fileproviderSucceed(self, operation: .move(source: path, destination: toPath))
self.delegate?.fileproviderSucceed(self, operation: opType)
})
} catch let e as NSError {
completionHandler?(e)
DispatchQueue.main.async(execute: {
self.delegate?.fileproviderFailed(self, operation: .move(source: path, destination: toPath))
self.delegate?.fileproviderFailed(self, operation: opType)
})
}
}
return nil
return LocalOperationHandle(operationType: opType, baseURL: self.baseURL)
}
@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
let opType = FileOperationType.copy(source: path, destination: toPath)
operation_queue.async {
if !overwrite && self.fileManager.fileExists(atPath: self.absoluteURL(toPath).path) {
completionHandler?(self.throwError(toPath, code: URLError.cannotWriteToFile as FoundationErrorEnum))
@@ -191,73 +192,76 @@ open class LocalFileProvider: FileProvider, FileProviderMonitor {
try self.opFileManager.copyItem(at: self.absoluteURL(path), to: self.absoluteURL(toPath))
completionHandler?(nil)
DispatchQueue.main.async(execute: {
self.delegate?.fileproviderSucceed(self, operation: .copy(source: path, destination: toPath))
self.delegate?.fileproviderSucceed(self, operation: opType)
})
} catch let e as NSError {
completionHandler?(e)
DispatchQueue.main.async(execute: {
self.delegate?.fileproviderFailed(self, operation: .copy(source: path, destination: toPath))
self.delegate?.fileproviderFailed(self, operation: opType)
})
}
}
return nil
return LocalOperationHandle(operationType: opType, baseURL: self.baseURL)
}
@discardableResult
open func removeItem(path: String, completionHandler: SimpleCompletionHandler) -> OperationHandle? {
let opType = FileOperationType.remove(path: path)
operation_queue.async {
do {
try self.opFileManager.removeItem(at: self.absoluteURL(path))
completionHandler?(nil)
DispatchQueue.main.async(execute: {
self.delegate?.fileproviderSucceed(self, operation: .remove(path: path))
self.delegate?.fileproviderSucceed(self, operation: opType)
})
} catch let e as NSError {
completionHandler?(e)
DispatchQueue.main.async(execute: {
self.delegate?.fileproviderFailed(self, operation: .remove(path: path))
self.delegate?.fileproviderFailed(self, operation: opType)
})
}
}
return nil
return LocalOperationHandle(operationType: opType, baseURL: self.baseURL)
}
@discardableResult
open func copyItem(localFile: URL, to toPath: String, completionHandler: SimpleCompletionHandler) -> OperationHandle? {
let opType = FileOperationType.copy(source: localFile.absoluteString, destination: toPath)
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.absoluteString, destination: toPath))
self.delegate?.fileproviderSucceed(self, operation: opType)
})
} catch let e as NSError {
completionHandler?(e)
DispatchQueue.main.async(execute: {
self.delegate?.fileproviderFailed(self, operation: .copy(source: localFile.absoluteString, destination: toPath))
self.delegate?.fileproviderFailed(self, operation: opType)
})
}
}
return nil
return LocalOperationHandle(operationType: opType, baseURL: self.baseURL)
}
@discardableResult
open func copyItem(path: String, toLocalURL: URL, completionHandler: SimpleCompletionHandler) -> OperationHandle? {
let opType = FileOperationType.copy(source: path, destination: toLocalURL.absoluteString)
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.absoluteString))
self.delegate?.fileproviderSucceed(self, operation: opType)
})
} catch let e as NSError {
completionHandler?(e)
DispatchQueue.main.async(execute: {
self.delegate?.fileproviderFailed(self, operation: .copy(source: path, destination: toLocalURL.absoluteString))
self.delegate?.fileproviderFailed(self, operation: opType)
})
}
}
return nil
return LocalOperationHandle(operationType: opType, baseURL: self.baseURL)
}
@discardableResult
@@ -271,49 +275,43 @@ open class LocalFileProvider: FileProvider, FileProviderMonitor {
@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
let opType = FileOperationType.fetch(path: path)
dispatch_queue.async {
let aPath = self.absoluteURL(path).path
guard !self.attributesOfItem(url: self.absoluteURL(path)).isDirectory && self.fileManager.fileExists(atPath: aPath) else {
completionHandler(nil, self.throwError(path, code: URLError.cannotOpenFile as FoundationErrorEnum))
return
}
let fd_from = open(aPath, O_RDONLY)
if fd_from < 0 {
guard let handle = FileHandle(forReadingAtPath: aPath) else {
completionHandler(nil, self.throwError(path, code: URLError.cannotOpenFile as FoundationErrorEnum))
return
}
defer { precondition(close(fd_from) >= 0) }
lseek(fd_from, offset, SEEK_SET)
var buf = [UInt8](repeating: 0, count: length)
let nread = read(fd_from, &buf, buf.count)
if nread < 0 {
completionHandler(nil, self.throwError(path, code: URLError.noPermissionsToReadFile as FoundationErrorEnum))
} else if nread == 0 {
completionHandler(nil, nil)
} else {
let data = Data(bytesNoCopy: UnsafeMutablePointer<UInt8>(&buf), count: nread, deallocator: .free)
completionHandler(data, nil)
defer {
handle.closeFile()
}
handle.seek(toFileOffset: UInt64(offset))
let data = handle.readData(ofLength: length)
completionHandler(data, nil)
}
return nil
return LocalOperationHandle(operationType: opType, baseURL: self.baseURL)
}
@discardableResult
open func writeContents(path: String, contents data: Data, atomically: Bool, completionHandler: SimpleCompletionHandler) -> OperationHandle? {
let opType = FileOperationType.modify(path: path)
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))
self.delegate?.fileproviderSucceed(self, operation: opType)
})
}
return nil
return LocalOperationHandle(operationType: opType, baseURL: self.baseURL)
}
open func searchFiles(path: String, recursive: Bool, query: String, foundItemHandler: ((FileObject) -> Void)?, completionHandler: @escaping ((_ files: [FileObject], _ error: Error?) -> Void)) {
dispatch_queue.async {
let iterator = self.fileManager.enumerator(at: self.absoluteURL(path), includingPropertiesForKeys: nil, options: recursive ? FileManager.DirectoryEnumerationOptions() : .skipsSubdirectoryDescendants) { (url, e) -> Bool in
let iterator = self.fileManager.enumerator(at: self.absoluteURL(path), includingPropertiesForKeys: nil, options: recursive ? [] : [.skipsSubdirectoryDescendants, .skipsPackageDescendants]) { (url, e) -> Bool in
completionHandler([], e)
return true
}
@@ -363,6 +361,15 @@ open class LocalFileProvider: FileProvider, FileProviderMonitor {
open func isRegisteredForNotification(path: String) -> Bool {
return monitors.map( { self.relativePathOf(url: $0.url) } ).contains(path)
}
open func copy(with zone: NSZone? = nil) -> Any {
let copy = LocalFileProvider(baseURL: self.baseURL!)
copy.currentPath = self.currentPath
copy.delegate = self.delegate
copy.fileOperationDelegate = self.fileOperationDelegate
copy.isPathRelative = self.isPathRelative
return copy
}
}
public extension LocalFileProvider {
@@ -515,3 +522,133 @@ internal class LocalFolderMonitor {
source.cancel()
}
}
open class LocalOperationHandle: OperationHandle {
public let baseURL: URL
public let operationType: FileOperationType
init (operationType: FileOperationType, baseURL: URL?) {
self.baseURL = baseURL ?? LocalFileProvider.defaultBaseURL()
self.operationType = operationType
}
private var sourceURL: URL? {
guard let source = operationType.source else { return nil }
return source.hasPrefix("file://") ? URL(fileURLWithPath: source) : baseURL.appendingPathComponent(source)
}
private var destURL: URL? {
guard let dest = operationType.destination else { return nil }
return dest.hasPrefix("file://") ? URL(fileURLWithPath: dest) : baseURL.appendingPathComponent(dest)
}
/// Caution: may put pressure on CPU, may have latency
open var bytesSoFar: Int64 {
assert(!Thread.isMainThread, "Don't run \(#function) method on main thread")
switch operationType {
case .modify:
guard let url = sourceURL, url.isFileURL else { return 0 }
if url.fileIsDirectory {
return iterateDirectory(url, deep: true).totalsize
} else {
return url.fileSize
}
case .copy, .move:
guard let url = destURL, url.isFileURL else { return 0 }
if url.fileIsDirectory {
return iterateDirectory(url, deep: true).totalsize
} else {
return url.fileSize
}
default:
return 0
}
}
/// Caution: may put pressure on CPU, may have latency
open var totalBytes: Int64 {
assert(!Thread.isMainThread, "Don't run \(#function) method on main thread")
switch operationType {
case .copy, .move:
guard let url = sourceURL, url.isFileURL else { return 0 }
if url.fileIsDirectory {
return iterateDirectory(url, deep: true).totalsize
} else {
return url.fileSize
}
default:
return 0
}
}
/// Not usable in local provider
open var inProgress: Bool {
return false
}
/// Not usable in local provider
open func cancel() -> Bool{
return false
}
func iterateDirectory(_ pathURL: URL, deep: Bool) -> (folders: Int, files: Int, totalsize: Int64) {
var folders = 0
var files = 0
var totalsize: Int64 = 0
let keys: [URLResourceKey] = [.isDirectoryKey, .fileSizeKey]
let enumOpt: FileManager.DirectoryEnumerationOptions = !deep ? [.skipsSubdirectoryDescendants, .skipsPackageDescendants] : []
let fp = FileManager()
let filesList = fp.enumerator(at: pathURL, includingPropertiesForKeys: keys, options: enumOpt, errorHandler: nil)
while let fileURL = filesList?.nextObject() as? URL {
var isdirv, sizev: AnyObject?
do {
try (fileURL as NSURL).getResourceValue(&isdirv, forKey: URLResourceKey.isDirectoryKey)
} catch _ {
}
do {
try (fileURL as NSURL).getResourceValue(&sizev, forKey: URLResourceKey.fileSizeKey)
} catch _ {
}
let isdir = isdirv?.boolValue ?? false
let size = sizev?.int64Value ?? 0
if isdir {
folders += 1
} else {
files += 1
}
totalsize += size
}
return (folders, files, totalsize)
}
}
internal extension URL {
var fileIsDirectory: Bool {
var isdirv: AnyObject?
do {
try (self as NSURL).getResourceValue(&isdirv, forKey: URLResourceKey.isDirectoryKey)
} catch _ {
}
return isdirv?.boolValue ?? false
}
var fileSize: Int64 {
var sizev: AnyObject?
do {
try (self as NSURL).getResourceValue(&sizev, forKey: URLResourceKey.fileSizeKey)
} catch _ {
}
return sizev?.int64Value ?? -1
}
var fileExists: Bool {
if self.isFileURL {
return FileManager.default.fileExists(atPath: self.path)
}
return false
}
}
+11 -14
View File
@@ -12,14 +12,11 @@ open class RemoteOperationHandle: OperationHandle {
internal var tasks: [Weak<URLSessionTask>]
private var _inProgress = false
open var inProgress: Bool {
return _inProgress
}
open private(set) var operationType: FileOperationType
init(tasks: [URLSessionTask]) {
init(operationType: FileOperationType, tasks: [URLSessionTask]) {
self.operationType = operationType
self.tasks = tasks.map { Weak<URLSessionTask>($0) }
_inProgress = true
}
internal func add(task: URLSessionTask) {
@@ -30,12 +27,6 @@ open class RemoteOperationHandle: OperationHandle {
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 {
@@ -56,11 +47,17 @@ open class RemoteOperationHandle: OperationHandle {
}
}
open func cancel() {
open func cancel() -> Bool {
var canceled = false
for taskbox in tasks {
taskbox.value?.cancel()
canceled = true
}
_inProgress = false
return canceled
}
open var inProgress: Bool {
return tasks.reduce(false) { $0 || $1.value?.state ?? .canceling == .running }
}
}
+30 -21
View File
@@ -12,12 +12,24 @@ import Foundation
// For big-endian platforms like PowerPC, there must be a huge overhaul
protocol SMBProtocolClientDelegate: class {
func receivedSMB2Response(_ header: SMB2.Header, response: SMBResponse)
func receivedResponse(client: SMB2ProtocolClient, response: SMBResponse, for: SMBRequest)
}
class SMB2ProtocolClient: FPSStreamTask {
var currentMessageID: UInt64 = 0
var sessionId: UInt64 = 0
var timeout: TimeInterval = 30
private(set) var lastMessageID: UInt64 = 0
private(set) var sessionId: UInt64 = 0
private func messageId() -> UInt64 {
defer {
lastMessageID += 1
}
return lastMessageID
}
private(set) var establishedTrees = Array<SMB2.TreeConnectResponse>()
private(set) var requestStack = [Int: SMBRequest]()
private(set) var responseStack = [Int: SMBResponse]()
weak var delegate: SMBProtocolClientDelegate?
@@ -40,7 +52,7 @@ class SMB2ProtocolClient: FPSStreamTask {
let data = createSMB2Message(header: smbHeader, message: msg)
self.writeData(data, timeout: 0, completionHandler: { (e) in
if self.sessionId == 0 {
self.readData(OfMinLength: 64, maxLength: 65536, timeout: 30, completionHandler: { (data, eof, e2) in
self.readData(OfMinLength: 64, maxLength: 65536, timeout: self.timeout, completionHandler: { (data, eof, e2) in
// TODO: set session id
completionHandler?(e2 ?? e)
})
@@ -70,6 +82,7 @@ class SMB2ProtocolClient: FPSStreamTask {
})
return mId
}
func sendTreeDisconnect(id treeId: UInt32, completionHandler: SimpleCompletionHandler) -> UInt64 {
let mId = messageId()
let smbHeader = SMB2.Header(command: .TREE_DISCONNECT, creditRequestResponse: 111, messageId: mId, treeId: treeId, sessionId: sessionId)
@@ -92,15 +105,13 @@ class SMB2ProtocolClient: FPSStreamTask {
return mId
}
func messageId() -> UInt64 {
defer {
currentMessageID += 1
}
return currentMessageID
func reset() {
}
// MARK: create and analyse messages
}
// MARK: create and analyse messages
extension SMB2ProtocolClient {
func determineSMBVersion(_ data: Data) -> Float {
let smbverChar: Int8 = Int8(bitPattern: data.first ?? 0)
let version = 0 - smbverChar
@@ -116,7 +127,7 @@ class SMB2ProtocolClient: FPSStreamTask {
throw SMBFileProviderError.incompatibleHeader
}
let headersize = MemoryLayout<SMB1.Header>.size
let header: SMB1.Header = decode(data)
let header: SMB1.Header = data.scanValue()!
var blocks = [(params: [UInt16], message: Data?)]()
var offset = headersize
while offset < data.count {
@@ -128,7 +139,7 @@ class SMB2ProtocolClient: FPSStreamTask {
offset += MemoryLayout<UInt8>.size
var rawParamWords = [UInt8](buffer[offset..<(offset + paramWordsCount * 2)])
let paramData = Data(bytesNoCopy: UnsafeMutablePointer<UInt8>(&rawParamWords), count: rawParamWords.count, deallocator: .free)
paramWords = decode(paramData)
paramWords = paramData.scanValue()!
offset += paramWordsCount * 2
let messageBytesCount = Int(UInt16(buffer[0]) + UInt16(buffer[1]) << 8)
offset += MemoryLayout<UInt16>.size
@@ -143,7 +154,7 @@ class SMB2ProtocolClient: FPSStreamTask {
return (header, blocks)
}
func digestSMB2Message(_ data: Data) throws -> (header: SMB2.Header, message: SMBResponse?)? {
func digestSMB2Message(_ data: Data) throws -> SMBResponse? {
guard data.count > 65 else {
throw URLError(.badServerResponse)
}
@@ -154,7 +165,7 @@ class SMB2ProtocolClient: FPSStreamTask {
let headerData = data.subdata(in: 0..<headersize)
let messageSize = data.count - headersize
let messageData = data.subdata(in: headersize..<(headersize + messageSize))
let header: SMB2.Header = decode(headerData)
let header: SMB2.Header = headerData.scanValue()!
switch header.command {
case .NEGOTIATE:
return (header, SMB2.NegotiateResponse(data: messageData))
@@ -200,8 +211,7 @@ class SMB2ProtocolClient: FPSStreamTask {
}
func createSMBMessage(header: SMB1.Header, blocks: [(params: Data?, message: Data?)]) -> Data {
var headerv = header
var result = encode(&headerv)
var result = Data(value: header)
for block in blocks {
var paramWordsCount = UInt8(block.params?.count ?? 0)
result.append(&paramWordsCount, count: MemoryLayout.size(ofValue: paramWordsCount))
@@ -218,9 +228,8 @@ class SMB2ProtocolClient: FPSStreamTask {
return result
}
func createSMB2Message(header: SMB2.Header, message: SMBRequest) -> Data {
var headerv = header
var result = encode(&headerv)
func createSMB2Message(header: SMB2.Header, message: SMBRequestBody) -> Data {
var result = Data(value: header)
result.append(message.data())
return result
}
+10 -2
View File
@@ -8,7 +8,7 @@
import Foundation
open class SMBFileProvider: FileProvider, FileProviderMonitor {
class SMBFileProvider: FileProvider, FileProviderMonitor {
open static var type: String = "Samba"
open var isPathRelative: Bool = true
open var baseURL: URL?
@@ -48,7 +48,7 @@ open class SMBFileProvider: FileProvider, FileProviderMonitor {
return nil
}
open func create(file fileAttribs: FileObject, at atPath: String, contents data: Data?, completionHandler: SimpleCompletionHandler) -> OperationHandle? {
open func create(file fileName: String, at atPath: String, contents data: Data?, completionHandler: SimpleCompletionHandler) -> OperationHandle? {
NotImplemented()
return nil
}
@@ -108,6 +108,14 @@ open class SMBFileProvider: FileProvider, FileProviderMonitor {
open func isRegisteredForNotification(path: String) -> Bool {
return false
}
open func copy(with zone: NSZone? = nil) -> Any {
let copy = SMBFileProvider(baseURL: self.baseURL!, credential: self.credential!, afterInitialized: { _ in })!
copy.currentPath = self.currentPath
copy.delegate = self.delegate
copy.fileOperationDelegate = self.fileOperationDelegate
return copy
}
}
// MARK: basic CIFS interactivity
+22 -4
View File
@@ -8,17 +8,35 @@
import Foundation
protocol SMBRequest {
protocol SMBRequestBody {
func data() -> Data
}
protocol SMBResponse {
extension SMBRequestBody {
func data() -> Data {
return Data(value: self)
}
}
protocol SMBResponseBody {
init? (data: Data)
}
extension SMBResponseBody {
init? (data: Data) {
if let v: Self = data.scanValue() {
self = v
} else {
return nil
}
}
}
protocol IOCtlRequestProtocol: SMBRequest {}
protocol IOCtlResponseProtocol: SMBResponse {}
typealias SMBRequest = (header: SMB2.Header, body: SMBRequestBody?)
typealias SMBResponse = (header: SMB2.Header, body: SMBResponseBody?)
protocol IOCtlRequestProtocol: SMBRequestBody {}
protocol IOCtlResponseProtocol: SMBResponseBody {}
struct SMBTime {
+13 -34
View File
@@ -11,7 +11,7 @@ import Foundation
extension SMB2 {
// MARK: SMB2 Create
struct CreateRequest: SMBRequest {
struct CreateRequest: SMBRequestBody {
let header: CreateRequest.Header
let name: String?
let contexts: [CreateContext]
@@ -40,7 +40,7 @@ extension SMB2 {
header.contextLength = 0
//result.appendData(nameData)
}
var result = encode(&header)
var result = Data(value: header)
result.append(body)
return result
}
@@ -158,7 +158,7 @@ extension SMB2 {
}
}
struct CreateResponse: SMBResponse {
struct CreateResponse: SMBResponseBody {
struct Header {
let size: UInt16
fileprivate let _oplockLevel: UInt8
@@ -186,7 +186,7 @@ extension SMB2 {
guard data.count >= MemoryLayout<CreateResponse.Header>.size else {
return nil
}
self.header = decode(data)
self.header = data.scanValue()!
if self.header.contextsOffset > 0 {
var contexts = [CreateContext]()
var contextOffset = Int(self.header.contextsOffset) - MemoryLayout<SMB2.Header>.size
@@ -195,14 +195,9 @@ extension SMB2 {
self.contexts = contexts
return
}
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: contextOffset..<(contextOffset + lastContextLen))
if let newContext = CreateContext(data: lastContextData) {
contexts.append(newContext)
}
contextOffset = Int(lastContextHeader.header.next) - MemoryLayout<SMB2.Header>.size
while contextOffset > 0, let context: CreateContext = data.scanValue(start: contextOffset) {
contexts.append(context)
contextOffset = Int(context.header.next) - MemoryLayout<SMB2.Header>.size
}
}
self.contexts = contexts
@@ -244,12 +239,12 @@ extension SMB2 {
guard data.count > headersize else {
return nil
}
self.header = decode(data)
self.header = data.scanValue()!
self.buffer = data.subdata(in: headersize..<data.count)
}
func data() -> Data {
var result = encode(header)
var result = Data(value: header)
result.append(buffer)
return result
}
@@ -363,7 +358,7 @@ extension SMB2 {
// MARK: SMB2 Close
struct CloseRequest: SMBRequest {
struct CloseRequest: SMBRequestBody {
let size: UInt16
let flags: CloseFlags
fileprivate let reserved2: UInt32
@@ -377,13 +372,9 @@ extension SMB2 {
self.flags = []
self.reserved2 = 0
}
func data() -> Data {
return encode(self)
}
}
struct CloseResponse: SMBResponse {
struct CloseResponse: SMBResponseBody {
let size: UInt16
let flags: CloseFlags
fileprivate let reserved: UInt32
@@ -394,10 +385,6 @@ extension SMB2 {
let allocationSize: UInt64
let endOfFile: UInt64
let fileAttributes: FileAttributes
init? (data: Data) {
self = decode(data)
}
}
struct CloseFlags: OptionSet {
@@ -412,7 +399,7 @@ extension SMB2 {
// MARK: SMB2 Flush
struct FlushRequest: SMBRequest {
struct FlushRequest: SMBRequestBody {
let size: UInt16
fileprivate let reserved: UInt16
fileprivate let reserved2: UInt32
@@ -426,13 +413,9 @@ extension SMB2 {
self.reserved = 0
self.reserved2 = 0
}
func data() -> Data {
return encode(self)
}
}
struct FlushResponse: SMBResponse {
struct FlushResponse: SMBResponseBody {
let size: UInt16
let reserved: UInt16
@@ -440,9 +423,5 @@ extension SMB2 {
self.size = 4
self.reserved = 0
}
init? (data: Data) {
self = decode(data)
}
}
}
+13 -37
View File
@@ -11,7 +11,7 @@ import Foundation
extension SMB2 {
// MARK: SMB2 Read
struct ReadRequest: SMBRequest {
struct ReadRequest: SMBRequestBody {
let size: UInt16
fileprivate let padding: UInt8
let flags: ReadRequest.Flags
@@ -43,10 +43,6 @@ extension SMB2 {
self.channelBuffer = 0
}
func data() -> Data {
return encode(read)
}
struct Flags: OptionSet {
let rawValue: UInt8
@@ -58,7 +54,7 @@ extension SMB2 {
}
}
struct ReadRespone: SMBResponse {
struct ReadRespone: SMBResponseBody {
struct Header {
let size: UInt16
let offset: UInt8
@@ -75,7 +71,7 @@ extension SMB2 {
guard data.count > 16 else {
return nil
}
self.header = decode(data)
self.header = data.scanValue()!
let headersize = MemoryLayout<Header>.size
self.buffer = data.subdata(in: headersize..<data.count)
}
@@ -89,7 +85,7 @@ extension SMB2 {
// MARK: SMB2 Write
struct WriteRequest: SMBRequest {
struct WriteRequest: SMBRequestBody {
let header: WriteRequest.Header
let channelInfo: ChannelInfo?
let fileData: Data
@@ -124,7 +120,7 @@ extension SMB2 {
}
func data() -> Data {
var result = encode(self.header)
var result = Data(value: self.header)
if let channelInfo = channelInfo {
result.append(channelInfo.data())
}
@@ -144,41 +140,29 @@ extension SMB2 {
}
}
struct WriteResponse: SMBResponse {
struct WriteResponse: SMBResponseBody {
let size: UInt16
fileprivate let reserved: UInt16
let writtenBytes: UInt32
fileprivate let remaining: UInt32
fileprivate let channelInfoOffset: UInt16
fileprivate let channelInfoLength: UInt16
init?(data: Data) {
self = decode(data)
}
}
struct ChannelInfo: SMBRequest {
struct ChannelInfo: SMBRequestBody {
let offset: UInt64
let token: UInt32
let length: UInt32
func data() -> Data {
return encode(data)
}
}
// MARK: SMB2 Lock
struct LockElement: SMBRequest {
struct LockElement: SMBRequestBody {
let offset: UInt64
let length: UInt64
let flags: LockElement.Flags
fileprivate let reserved: UInt32
func data() -> Data {
return encode(self)
}
struct Flags: OptionSet {
let rawValue: UInt32
@@ -193,7 +177,7 @@ extension SMB2 {
}
}
struct LockRequest: SMBRequest {
struct LockRequest: SMBRequestBody {
let header: LockRequest.Header
let locks: [LockElement]
@@ -203,9 +187,9 @@ extension SMB2 {
}
func data() -> Data {
var result = encode(header)
var result = Data(value: header)
for lock in locks {
result.append(encode(lock))
result.append(Data(value: lock))
}
return result
}
@@ -218,7 +202,7 @@ extension SMB2 {
}
}
struct LockResponse: SMBResponse {
struct LockResponse: SMBResponseBody {
let size: UInt16
let reserved: UInt16
@@ -226,15 +210,11 @@ extension SMB2 {
self.size = 4
self.reserved = 0
}
init? (data: Data) {
self = decode(data)
}
}
// MARK: SMB2 Cancel
struct CancelRequest: SMBRequest {
struct CancelRequest: SMBRequestBody {
let size: UInt16
let reserved: UInt16
@@ -242,9 +222,5 @@ extension SMB2 {
self.size = 4
self.reserved = 0
}
func data() -> Data {
return encode(self)
}
}
}
+20 -54
View File
@@ -15,7 +15,7 @@ extension SMB2 {
* IOCtl usage is usually limited in SMB to pipe requests and duplicating file inside server
*/
struct IOCtlRequest: SMBRequest {
struct IOCtlRequest: SMBRequestBody {
let header: Header
let requestData: IOCtlRequestProtocol?
@@ -26,7 +26,7 @@ extension SMB2 {
}
func data() -> Data {
var result = encode(self.header)
var result = Data(value: self.header)
if let reqData = requestData?.data() {
result.append(reqData)
}
@@ -63,12 +63,12 @@ extension SMB2 {
}
}
struct IOCtlResponse: SMBResponse {
struct IOCtlResponse: SMBResponseBody {
let header: Header
let responseData: IOCtlResponseProtocol?
init?(data: Data) {
self.header = decode(data)
self.header = data.scanValue()!
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 {
@@ -140,10 +140,10 @@ extension SMB2 {
let chunks: [Chunk]
func data() -> Data {
var result = encode(sourceKey)
result.append(encode(chunkCount))
var reserved: UInt32 = 0
result.append(encode(&reserved))
var result = Data(value: sourceKey)
result.append(Data(value: chunkCount))
let reserved: UInt32 = 0
result.append(Data(value: reserved))
return Data()
}
@@ -152,10 +152,6 @@ extension SMB2 {
let targetOffset: UInt64
let length: UInt32
fileprivate let reserved: UInt32
func data() -> Data {
return encode(self)
}
}
}
@@ -182,10 +178,6 @@ extension SMB2 {
self.length = length
self.offset = offset
}
func data() -> Data {
return encode(self)
}
}
struct ResilencyRequest: IOCtlRequestProtocol {
@@ -197,10 +189,6 @@ extension SMB2 {
self.timeout = timeout
self.reserved = 0
}
func data() -> Data {
return encode(self)
}
}
struct ValidateNegotiateInfo: IOCtlRequestProtocol {
@@ -213,8 +201,8 @@ extension SMB2 {
}
func data() -> Data {
var result = encode(self.header)
dialects.forEach { result.append(encode($0)) }
var result = Data(value: self.header)
dialects.forEach { result.append(Data(value: $0)) }
return result
}
@@ -234,10 +222,6 @@ extension SMB2 {
let chunksCount: UInt32
let chunksBytesWritten: UInt32
let totalBytesWriiten: UInt32
init?(data: Data) {
self = decode(data)
}
}
// SRV_ENUMERATE_SNAPSHOTS
@@ -247,8 +231,9 @@ extension SMB2 {
let snapshots: [SMBTime]
init?(data: Data) {
self.count = decode(data)
self.returnedCount = decode(data.subdata(in: 4..<8))
guard data.count > 8 else { return nil }
self.count = data.scanValue()!
self.returnedCount = data.scanValue(start: 4)!
//let size: UInt32 = decode(data.subdataWithRange(NSRange(location: 8, length: 4)))
var snapshots = [SMBTime]()
let dateFormatter = DateFormatter()
@@ -258,7 +243,7 @@ extension SMB2 {
if data.count < offset + 48 {
return nil
}
let datestring = String(data: data.subdata(in: offset..<(offset + 48)), encoding: .utf16)
let datestring = data.scanString(start: offset, length: 48, encoding: .utf16)
if let datestring = datestring, let date = dateFormatter.date(from: datestring) {
snapshots.append(SMBTime(date: date))
}
@@ -271,32 +256,21 @@ extension SMB2 {
let key: (UInt64, UInt64, UInt64)
fileprivate let contextLength: UInt32
fileprivate let context: UInt32
init?(data: Data) {
self = decode(data)
}
}
struct ReadHash: IOCtlResponseProtocol {
// TODO: Implement IOCTL READ_HASH
init?(data: Data) {
self = decode(data)
}
}
struct NetworkInterfaceInfo: IOCtlResponseProtocol {
let items: [NetworkInterfaceInfo.Item]
init?(data: Data) {
let count = data.count / MemoryLayout<Item>.size
guard count > 0 else {
return nil
}
var items = [Item]()
for i in 0..<count {
let itemdata = data.subdata(in: (i * MemoryLayout<Item>.size)..<((i + 1) * MemoryLayout<Item>.size))
items.append(decode(itemdata))
var offset = 0
while let item: Item = data.scanValue(start: offset) {
items.append(item)
offset += MemoryLayout<Item>.size
}
self.items = items
}
@@ -335,15 +309,11 @@ extension SMB2 {
static let ipv6: sa_family_t = 0x17
var sockaddr: sockaddr_in {
var sockaddrStorage = self.sockaddrStorage
let data = Data(bytes: &sockaddrStorage, count: 16)
return decode(data)
return Data.mapMemory(from: self.sockaddrStorage)!
}
var sockaddr6: sockaddr_in6 {
var sockaddrStorage = self.sockaddrStorage
let data = Data(bytes: &sockaddrStorage, count: 28)
return decode(data)
return Data.mapMemory(from: self.sockaddrStorage)!
}
}
}
@@ -356,10 +326,6 @@ extension SMB2 {
var dialect: (major: Int, minor: Int) {
return (major: Int(_dialect & 0xFF), minor: Int(_dialect >> 8))
}
init?(data: Data) {
self = decode(data)
}
}
}
+6 -15
View File
@@ -11,7 +11,7 @@ import Foundation
extension SMB2 {
// MARK: SMB2 Change Notify
struct ChangeNotifyRequest: SMBRequest {
struct ChangeNotifyRequest: SMBRequestBody {
let size: UInt16
let flags: ChangeNotifyRequest.Flags
let outputBufferLength: UInt32
@@ -28,10 +28,6 @@ extension SMB2 {
self.reserved = 0
}
func data() -> Data {
return encode(self)
}
struct Flags: OptionSet {
let rawValue: UInt16
@@ -79,7 +75,7 @@ extension SMB2 {
}
}
struct ChangeNotifyResponse: SMBResponse {
struct ChangeNotifyResponse: SMBResponseBody {
let notifications: [(action: FileNotifyAction, fileName: String)]
init?(data: Data) {
@@ -89,19 +85,14 @@ extension SMB2 {
var offset = 0
while i < maxLoop {
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)
let nextOffset: UInt32 = data.scanValue(start: offset) ?? 0
let actionValue: UInt32 = data.scanValue(start: offset + 4) ?? 0
guard let action = FileNotifyAction(rawValue: actionValue) else {
continue
}
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) ?? ""
let fileNameLen = Int(data.scanValue(start: offset + 8) as UInt32? ?? 0)
let fileName = data.scanString(start: offset + 12, length: fileNameLen, encoding: .utf16) ?? ""
result.append((action: action, fileName: fileName))
offset += Int(nextOffset)
+50 -77
View File
@@ -11,7 +11,7 @@ import Foundation
extension SMB2 {
// MARK: SMB2 Query Directory
struct QueryDirectoryRequest: SMBRequest {
struct QueryDirectoryRequest: SMBRequestBody {
let header: QueryDirectoryRequest.Header
let searchPattern: String?
@@ -28,7 +28,7 @@ extension SMB2 {
}
func data() -> Data {
var result = encode(header)
var result = Data(value: header)
if let patternData = searchPattern?.data(using: .utf16) {
result.append(patternData)
}
@@ -60,7 +60,7 @@ extension SMB2 {
}
}
struct QueryDirectoryResponse: SMBResponse {
struct QueryDirectoryResponse: SMBResponseBody {
let buffer: Data
func parseAs(type: FileInformationEnum) -> [(header: SMB2FilesInformationHeader, fileName: String)] {
@@ -68,43 +68,24 @@ extension SMB2 {
var result = [(header: SMB2FilesInformationHeader, fileName: String)]()
while true {
let header: SMB2FilesInformationHeader
let headersize: Int
switch type {
case .fileDirectoryInformation:
headersize = MemoryLayout<FileDirectoryInformationHeader>.size
let headerData = buffer.subdata(in: offset..<(offset + headersize))
let h: FileDirectoryInformationHeader = decode(headerData)
header = h
header = buffer.scanValue(start: offset) as FileDirectoryInformationHeader!
case .fileFullDirectoryInformation:
headersize = MemoryLayout<FileFullDirectoryInformationHeader>.size
let headerData = buffer.subdata(in: offset..<(offset + headersize))
let h: FileFullDirectoryInformationHeader = decode(headerData)
header = h
header = buffer.scanValue(start: offset) as FileFullDirectoryInformationHeader!
case .fileIdFullDirectoryInformation:
headersize = MemoryLayout<FileIdFullDirectoryInformationHeader>.size
let headerData = buffer.subdata(in: offset..<(offset + headersize))
let h: FileIdFullDirectoryInformationHeader = decode(headerData)
header = h
header = buffer.scanValue(start: offset) as FileIdFullDirectoryInformationHeader!
case .fileBothDirectoryInformation:
headersize = MemoryLayout<FileBothDirectoryInformationHeader>.size
let headerData = buffer.subdata(in: offset..<(offset + headersize))
let h: FileBothDirectoryInformationHeader = decode(headerData)
header = h
header = buffer.scanValue(start: offset) as FileBothDirectoryInformationHeader!
case .fileIdBothDirectoryInformation:
headersize = MemoryLayout<FileIdBothDirectoryInformationHeader>.size
let headerData = buffer.subdata(in: offset..<(offset + headersize))
let h: FileIdBothDirectoryInformationHeader = decode(headerData)
header = h
header = buffer.scanValue(start: offset) as FileIdBothDirectoryInformationHeader!
case .fileNamesInformation:
headersize = MemoryLayout<FileNamesInformationHeader>.size
let headerData = buffer.subdata(in: offset..<(offset + headersize))
let h: FileNamesInformationHeader = decode(headerData)
header = h
header = buffer.scanValue(start: offset) as FileNamesInformationHeader!
default:
return []
}
let fnData = buffer.subdata(in: (offset + headersize)..<(offset + headersize + Int(header.fileNameLength)))
let fileName = String(data: fnData, encoding: .utf16) ?? ""
let headersize = MemoryLayout.size(ofValue: header)
let fileName = buffer.scanString(start: headersize, length: Int(header.fileNameLength), encoding: .utf16) ?? ""
result.append((header: header, fileName: fileName))
if header.nextEntryOffset == 0 {
break
@@ -115,8 +96,8 @@ extension SMB2 {
}
init? (data: Data) {
let offset = Int(decode(data.subdata(in: 2..<4)) as UInt16)
let length = Int(decode(data.subdata(in: 4..<8)) as UInt32)
let offset = Int(data.scanValue(start: 2) as UInt16!)
let length = Int(data.scanValue(start: 4) as UInt32!)
guard data.count > offset + length else {
return nil
}
@@ -126,7 +107,7 @@ extension SMB2 {
// MARK: SMB2 Query Info
struct QueryInfoRequest: SMBRequest {
struct QueryInfoRequest: SMBRequestBody {
let header: Header
let buffer: Data?
@@ -143,8 +124,8 @@ extension SMB2 {
}
let strLength = UInt8(strData.count)
let nextOffset = UInt32(4 + 1 + strData.count)
var data = encode(nextOffset)
data.append(encode(strLength))
var data = Data(value: nextOffset)
data.append(Data(value: strLength))
data.append(strData)
data.count += 1
let padSize = (data.count) % 4
@@ -170,8 +151,7 @@ extension SMB2 {
// TODO: Implement QUOTA_INFO init
func data() -> Data {
let headerData = encode(header)
var result = headerData
var result = Data(value: header)
if let buffer = buffer {
result.append(buffer)
}
@@ -204,12 +184,11 @@ extension SMB2 {
}
}
struct QueryInfoResponse: SMBResponse {
struct QueryInfoResponse: SMBResponseBody {
let buffer: Data
init?(data: Data) {
let structSizeData = data.subdata(in: 0..<2)
let structSize: UInt16 = decode(structSizeData)
let structSize: UInt16 = data.scanValue()!
guard structSize == 9 else {
return nil
}
@@ -217,10 +196,9 @@ extension SMB2 {
/*let offsetData = data.subdataWithRange(NSRange(location: 2, length: 2))
let offset: UInt16 = decode(offsetData)*/
let lengthData = data.subdata(in: 4..<8)
let length = Int(decode(lengthData) as UInt32)
let length = Int(data.scanValue(start: 4) as UInt32!)
guard data.count >= 8 + Int(length) else {
guard data.count >= 8 + length else {
return nil
}
@@ -228,40 +206,38 @@ extension SMB2 {
}
var asAccessInformation: FileAccessInformation {
return decode(buffer)
return buffer.scanValue()!
}
var asAlignmentInformation: FileAlignmentInformation {
return decode(buffer)
return buffer.scanValue()!
}
var asAllInformation: (header: FileAllInformationHeader, name: String) {
let header: FileAllInformationHeader = decode(buffer)
let header: FileAllInformationHeader = buffer.scanValue()!
let headersize = MemoryLayout<FileAllInformationHeader>.size
let nameData = buffer.subdata(in: headersize..<(headersize + Int(header.nameLength)))
let name = String(data: nameData, encoding: .utf16) ?? ""
let name = buffer.scanString(start: headersize, length: Int(header.nameLength), 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: .utf16) ?? ""
return buffer.scanString(start: 0, length: buffer.count, encoding: .utf16) ?? ""
}
var asAttributeTagInformation: FileAttributeTagInformation {
return decode(buffer)
return buffer.scanValue()!
}
var asBasicInformation: FileBasicInformation {
return decode(buffer)
return buffer.scanValue()!
}
var asCompressionInformation: FileCompressionInformation {
return decode(buffer)
return buffer.scanValue()!
}
var asEaInformation: FileEaInformation {
return decode(buffer)
return buffer.scanValue()!
}
var asFullEaInformation: FileFullEaInformation {
@@ -270,83 +246,80 @@ extension SMB2 {
}
var asInternalInformation: FileInternalInformation {
return decode(buffer)
return buffer.scanValue()!
}
var asModeInformation: FileModeInformation {
return decode(buffer)
return buffer.scanValue()!
}
var asNetworkOpenInformation: FileNetworkOpenInformation {
return decode(buffer)
return buffer.scanValue()!
}
var asPipeInformation: FilePipeInformation {
return decode(buffer)
return buffer.scanValue()!
}
var asPipeLocalInformation: FilePipeLocalInformation {
return decode(buffer)
return buffer.scanValue()!
}
var asPipeRemoteInformation: FilePipeRemoteInformation {
return decode(buffer)
return buffer.scanValue()!
}
var asPositionInformation: FilePositionInformation {
return decode(buffer)
return buffer.scanValue()!
}
var asStandardInformation: FileStandardInformation {
return decode(buffer)
return buffer.scanValue()!
}
var asStreamInformation: (header: FileStreamInformationHeader, name: String) {
let header: FileStreamInformationHeader = decode(buffer)
let header: FileStreamInformationHeader = buffer.scanValue()!
let headersize = MemoryLayout<FileStreamInformationHeader>.size
let nameData = buffer.subdata(in: headersize..<(headersize + Int(header.streamNameLength)))
let name = String(data: nameData, encoding: .utf16) ?? ""
let name = buffer.scanString(start: headersize, length: Int(header.streamNameLength), encoding: .utf16) ?? ""
return (header, name)
}
var asFsVolumeInformation: (header: FileFsVolumeInformationHeader, name: String) {
let header: FileFsVolumeInformationHeader = decode(buffer)
let header: FileFsVolumeInformationHeader = buffer.scanValue()!
let headersize = MemoryLayout<FileFsVolumeInformationHeader>.size
let nameData = buffer.subdata(in: headersize..<(headersize + Int(header.labelLength)))
let name = String(data: nameData, encoding: .utf16) ?? ""
let name = buffer.scanString(start: headersize, length: Int(header.labelLength), encoding: .utf16) ?? ""
return (header, name)
}
var asFsSizeInformation: FileFsSizeInformation {
return decode(buffer)
return buffer.scanValue()!
}
var asFsDeviceInformation: FileFsDeviceInformation {
return decode(buffer)
return buffer.scanValue()!
}
var asFsAttributeInformation: (header: FileFsAttributeInformationHeader, name: String) {
let header: FileFsAttributeInformationHeader = decode(buffer)
let header: FileFsAttributeInformationHeader = buffer.scanValue()!
let headersize = MemoryLayout<FileFsAttributeInformationHeader>.size
let nameData = buffer.subdata(in: headersize..<(headersize + Int(header.nameLength)))
let name = String(data: nameData, encoding: .utf16) ?? ""
let name = buffer.scanString(start: headersize, length: Int(header.nameLength), encoding: .utf16) ?? ""
return (header, name)
}
var asFsControlInformation: FileFsControlInformation {
return decode(buffer)
return buffer.scanValue()!
}
var asFsFullSizeInformation: FileFsFullSizeInformation {
return decode(buffer)
return buffer.scanValue()!
}
var asFsObjectIdInformation: FileFsObjectIdInformation {
return decode(buffer)
return buffer.scanValue()!
}
var asFsSectorSizeInformation: FileFsSectorSizeInformation {
return decode(buffer)
return buffer.scanValue()!
}
}
}
+7 -33
View File
@@ -8,7 +8,7 @@
import Foundation
protocol SMB2FilesInformationHeader: SMBResponse {
protocol SMB2FilesInformationHeader: SMBResponseBody {
var nextEntryOffset: UInt32 { get }
var fileIndex: UInt32 { get }
var fileNameLength : UInt32 { get }
@@ -127,10 +127,6 @@ extension SMB2 {
let allocationSize: UInt64
let fileAttributes: FileAttributes
let fileNameLength : UInt32
init?(data: Data) {
self = decode(data)
}
}
struct FileFullDirectoryInformationHeader: SMB2FilesInformationHeader {
@@ -145,10 +141,6 @@ extension SMB2 {
let fileAttributes: FileAttributes
let fileNameLength : UInt32
let extendedAttributesSize: UInt32
init?(data: Data) {
self = decode(data)
}
}
struct FileIdFullDirectoryInformationHeader: SMB2FilesInformationHeader {
@@ -165,10 +157,6 @@ extension SMB2 {
let extendedAttributesSize: UInt32
fileprivate let reserved: UInt32
let fileId: FileId
init?(data: Data) {
self = decode(data)
}
}
struct FileBothDirectoryInformationHeader: SMB2FilesInformationHeader {
@@ -187,14 +175,9 @@ extension SMB2 {
fileprivate let reserved: UInt8
fileprivate let _shortName: FileShortNameType
var shortName: String? {
let s = encode(_shortName)
var d = s
d.count = Int(shortNameLen)
return String(data: d, encoding: .utf16)
}
init?(data: Data) {
self = decode(data)
var data = Data(value: _shortName)
data.count = Int(shortNameLen)
return String(data: data, encoding: .utf16)
}
}
@@ -214,27 +197,18 @@ extension SMB2 {
fileprivate let reserved: UInt8
fileprivate let _shortName: FileShortNameType
var shortName: String? {
let s = encode(_shortName)
var d = s
d.count = Int(shortNameLen)
return String(data: d, encoding: .utf16)
var data = Data(value: _shortName)
data.count = Int(shortNameLen)
return String(data: data, encoding: .utf16)
}
fileprivate let reserved2: UInt16
let fileId : FileId
init?(data: Data) {
self = decode(data)
}
}
struct FileNamesInformationHeader: SMB2FilesInformationHeader {
let nextEntryOffset: UInt32
let fileIndex: UInt32
let fileNameLength : UInt32
init?(data: Data) {
self = decode(data)
}
}
typealias FileShortNameType = (UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8,
+14 -32
View File
@@ -11,7 +11,7 @@ import Foundation
extension SMB2 {
// MARK: SMB2 Negotiating
struct NegotiateRequest: SMBRequest {
struct NegotiateRequest: SMBRequestBody {
let header: NegotiateRequest.Header
let dialects: [UInt16]
let contexts: [(type: NegotiateContextType, data: Data)]
@@ -43,13 +43,11 @@ extension SMB2 {
var contextData = Data()
for context in contexts {
var contextType = context.type.rawValue
contextData.append(UnsafeBufferPointer(start: &contextType, count: 2))
var dataLen = UInt16(context.data.count)
contextData.append(Data(value: context.type.rawValue))
contextData.count += 4
contextData.append(UnsafeBufferPointer(start: &dataLen, count: 2))
contextData.append(Data(value: UInt16(context.data.count)))
}
var result = encode(&header)
var result = Data(value: header)
result.append(dialectData as Data)
result.append(contextData as Data)
return result
@@ -91,16 +89,16 @@ extension SMB2 {
}
}
struct NegotiateResponse: SMBResponse {
struct NegotiateResponse: SMBResponseBody {
let header: NegotiateResponse.Header
let buffer: Data?
let contexts: [(type: NegotiateContextType, data: Data)]
init? (data: Data) {
if data.count < 64 {
guard data.count >= 64 else {
return nil
}
self.header = decode(data)
self.header = data.scanValue()!
if Int(header.size) != 65 {
return nil
}
@@ -176,7 +174,7 @@ extension SMB2 {
// MARK: SMB2 Session Setup
struct SessionSetupRequest: SMBRequest {
struct SessionSetupRequest: SMBRequestBody {
let header: SessionSetupRequest.Header
let buffer: Data?
@@ -194,7 +192,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 = encode(&header)
var result = Data(value: header)
if let buffer = self.buffer {
result.append(buffer)
}
@@ -235,15 +233,15 @@ extension SMB2 {
}
}
struct SessionSetupResponse: SMBResponse {
struct SessionSetupResponse: SMBResponseBody {
let header: SessionSetupResponse.Header
let buffer: Data?
init? (data: Data) {
if data.count < 64 {
guard data.count >= 64 else {
return nil
}
self.header = decode(data)
self.header = data.scanValue()!
if Int(header.size) != 9 {
return nil
}
@@ -289,7 +287,7 @@ extension SMB2 {
// MARK: SMB2 Log off
struct LogOff: SMBRequest, SMBResponse {
struct LogOff: SMBRequestBody, SMBResponseBody {
let size: UInt16
let reserved: UInt16
@@ -297,19 +295,11 @@ extension SMB2 {
self.size = 4
self.reserved = 0
}
init? (data: Data) {
self = decode(data)
}
func data() -> Data {
return encode(self)
}
}
// MARK: SMB2 Echo
struct Echo: SMBRequest, SMBResponse {
struct Echo: SMBRequestBody, SMBResponseBody {
let size: UInt16
let reserved: UInt16
@@ -317,13 +307,5 @@ extension SMB2 {
self.size = 4
self.reserved = 0
}
init? (data: Data) {
self = decode(data)
}
func data() -> Data {
return encode(self)
}
}
}
+5 -9
View File
@@ -10,14 +10,14 @@ import Foundation
extension SMB2 {
// MARK: SMB2 Set Info
struct SetInfoRequest: SMBRequest {
struct SetInfoRequest: SMBRequestBody {
let header: Header
let buffer: Data?
func data() -> Data {
return Data()
var result = Data(value: header)
result.append(buffer ?? Data())
return result
}
struct Header {
@@ -32,15 +32,11 @@ extension SMB2 {
}
}
struct SetInfoResponse: SMBResponse {
struct SetInfoResponse: SMBResponseBody {
let size: UInt16
init() {
self.size = 2
}
init? (data: Data) {
self = decode(data)
}
}
}
+4 -19
View File
@@ -11,7 +11,7 @@ import Foundation
extension SMB2 {
// MARK: SMB2 Tree Connect
struct TreeConnectRequest: SMBRequest {
struct TreeConnectRequest: SMBRequestBody {
let header: TreeConnectRequest.Header
let buffer: Data?
var path: String {
@@ -34,7 +34,7 @@ extension SMB2 {
var header = self.header
header.pathOffset = UInt16(MemoryLayout<SMB2.Header>.size + MemoryLayout<TreeConnectRequest.Header>.size)
header.pathLength = UInt16(buffer?.count ?? 0)
var result = encode(&header)
var result = Data(value: header)
if let buffer = self.buffer {
result.append(buffer)
}
@@ -66,7 +66,7 @@ extension SMB2 {
}
}
struct TreeConnectResponse: SMBResponse {
struct TreeConnectResponse: SMBResponseBody {
let size: UInt16 // = 16
fileprivate let _type: UInt8
var type: ShareType {
@@ -77,13 +77,6 @@ extension SMB2 {
let capabilities: TreeConnectResponse.Capabilities
let maximalAccess: FileAccessMask
init? (data: Data) {
if data.count != 16 {
return nil
}
self = decode(data)
}
enum ShareType: UInt8 {
case UNKNOWN = 0x00
case DISK = 0x01
@@ -131,7 +124,7 @@ extension SMB2 {
// MARK: SMB2 Tree Disconnect
struct TreeDisconnect: SMBRequest, SMBResponse {
struct TreeDisconnect: SMBRequestBody, SMBResponseBody {
let size: UInt16
let reserved: UInt16
@@ -139,13 +132,5 @@ extension SMB2 {
self.size = 4
self.reserved = 0
}
init? (data: Data) {
self = decode(data)
}
func data() -> Data {
return encode(self)
}
}
}
+25 -14
View File
@@ -8,22 +8,33 @@
import Foundation
internal func encode<T>(_ value: inout T) -> Data {
return withUnsafePointer(to: &value) { p in
Data(bytes: p, count: MemoryLayout.size(ofValue: value))
extension Data {
init<T>(value: T) {
var value = value
self = Data(buffer: UnsafeBufferPointer(start: &value, count: MemoryLayout.size(ofValue: value)))
}
}
internal func encode<T>(_ value: T) -> Data {
var value = value
return encode(&value)
}
internal func decode<T>(_ data: Data) -> T {
let pointer = UnsafeMutablePointer<T>.allocate(capacity: MemoryLayout<T.Type>.size)
(data as NSData).getBytes(pointer, length: MemoryLayout<T.Type>.size)
return pointer.move()
func scanValue<T>() -> T? {
guard MemoryLayout<T>.size <= self.count else { return nil }
return self.withUnsafeBytes { $0.pointee }
}
func scanValue<T>(start: Int) -> T? {
let length = MemoryLayout<T>.size
guard self.count >= start + length else { return nil }
return self.subdata(in: start..<start+length).withUnsafeBytes { $0.pointee }
}
func scanString(start: Int = 0, length: Int, encoding: String.Encoding) -> String? {
guard self.count >= start + length else { return nil }
return String(data: self.subdata(in: start..<start+length), encoding: encoding)
}
static func mapMemory<T, U>(from: T) -> U? {
guard MemoryLayout<T>.size >= MemoryLayout<U>.size else { return nil }
let data = Data(value: from)
return data.scanValue()
}
}
protocol FileProviderSMBHeader {
+1 -1
View File
@@ -10,7 +10,7 @@ import Foundation
/// Error Types and Description
public enum NTStatus: UInt32, Error, CustomStringConvertible {
enum NTStatus: UInt32, Error, CustomStringConvertible {
case SUCCESS = 0x00000000
case NOT_IMPLEMENTED = 0xC0000002
case INVALID_DEVICE_REQUEST = 0xC0000010
+111 -91
View File
@@ -21,35 +21,41 @@ public final class WebDavFileObject: FileObject {
// codebeat:enable[ARITY]
}
// Because this class uses NSURLSession, it's necessary to disable App Transport Security
// in case of using this class with unencrypted HTTP connection.
/// Because this class uses NSURLSession, it's necessary to disable App Transport Security
/// in case of using this class with unencrypted HTTP connection.
open class WebDAVFileProvider: NSObject, FileProviderBasic {
open class WebDAVFileProvider: NSObject, FileProviderBasicRemote {
open static let type: String = "WebDAV"
open let isPathRelative: Bool = true
open let baseURL: URL?
open var currentPath: String = ""
open var dispatch_queue: DispatchQueue {
public var dispatch_queue: DispatchQueue {
willSet {
assert(_session == nil, "It's not effective to change dispatch_queue property after session is initialized.")
}
}
open weak var delegate: FileProviderDelegate?
public weak var delegate: FileProviderDelegate?
open let credential: URLCredential?
open private(set) var cache: URLCache?
public var useCache: Bool = false
public var validatingCache: Bool = true
fileprivate var _session: URLSession?
fileprivate var sessionDelegate: SessionDelegate?
fileprivate var session: URLSession {
public var session: URLSession {
if _session == nil {
self.sessionDelegate = SessionDelegate(fileProvider: self, credential: credential)
let queue = OperationQueue()
//queue.underlyingQueue = dispatch_queue
_session = URLSession(configuration: URLSessionConfiguration.default, delegate: sessionDelegate as URLSessionDownloadDelegate?, delegateQueue: queue)
let config = URLSessionConfiguration.default
config.urlCache = cache
config.requestCachePolicy = .returnCacheDataElseLoad
_session = URLSession(configuration: config, delegate: sessionDelegate as URLSessionDownloadDelegate?, delegateQueue: queue)
}
return _session!
}
public init? (baseURL: URL, credential: URLCredential?) {
public init? (baseURL: URL, credential: URLCredential?, cache: URLCache? = nil) {
if !["http", "https"].contains(baseURL.uw_scheme.lowercased()) {
return nil
}
@@ -57,6 +63,7 @@ open class WebDAVFileProvider: NSObject, FileProviderBasic {
dispatch_queue = DispatchQueue(label: "FileProvider.\(WebDAVFileProvider.type)", attributes: DispatchQueue.Attributes.concurrent)
//let url = baseURL.uw_absoluteString
self.credential = credential
self.cache = cache
}
deinit {
@@ -64,6 +71,7 @@ open class WebDAVFileProvider: NSObject, FileProviderBasic {
}
open func contentsOfDirectory(path: String, completionHandler: @escaping ((_ contents: [FileObject], _ error: Error?) -> Void)) {
let opType = FileOperationType.fetch(path: path)
let url = absoluteURL(path)
var request = URLRequest(url: url)
request.httpMethod = "PROPFIND"
@@ -71,26 +79,23 @@ open class WebDAVFileProvider: NSObject, FileProviderBasic {
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: .utf8)
request.setValue(String(request.httpBody!.count), forHTTPHeaderField: "Content-Length")
let task = session.dataTask(with: request, completionHandler: { (data, response, error) in
runDataTask(with: request, operationHandle: RemoteOperationHandle(operationType: opType, tasks: []), 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)
})
task.resume()
completionHandler(fileObjects, responseError ?? error)
})
}
open func attributesOfItem(path: String, completionHandler: @escaping ((_ attributes: FileObject?, _ error: Error?) -> Void)) {
@@ -101,7 +106,7 @@ open class WebDAVFileProvider: NSObject, FileProviderBasic {
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: .utf8)
request.setValue(String(request.httpBody!.count), forHTTPHeaderField: "Content-Length")
let task = session.dataTask(with: request, completionHandler: { (data, response, error) in
runDataTask(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)
@@ -115,7 +120,6 @@ open class WebDAVFileProvider: NSObject, FileProviderBasic {
}
completionHandler(nil, responseError ?? error)
})
task.resume()
}
open func storageProperties(completionHandler: @escaping ((_ total: Int64, _ used: Int64) -> Void)) {
@@ -131,19 +135,18 @@ open class WebDAVFileProvider: NSObject, FileProviderBasic {
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: .utf8)
request.setValue(String(request.httpBody!.count), forHTTPHeaderField: "Content-Length")
let task = session.dataTask(with: request, completionHandler: { (data, response, error) in
runDataTask(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)
})
task.resume()
completionHandler(totalSize, usedSize)
})
}
open weak var fileOperationDelegate: FileOperationDelegate?
@@ -152,7 +155,8 @@ open class WebDAVFileProvider: NSObject, FileProviderBasic {
extension WebDAVFileProvider: FileProviderOperations {
@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 {
let opType = FileOperationType.create(path: (atPath as NSString).appendingPathComponent(folderName) + "/")
guard fileOperationDelegate?.fileProvider(self, shouldDoOperation: opType) ?? true == true else {
return nil
}
let url = absoluteURL((atPath as NSString).appendingPathComponent(folderName) + "/")
@@ -163,20 +167,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)
})
self.delegateNotify(opType, error: responseError ?? error)
})
task.taskDescription = opType.json
task.resume()
return RemoteOperationHandle(tasks: [task])
return RemoteOperationHandle(operationType: opType, tasks: [task])
}
@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 {
public func create(file fileName: String, at path: String, contents data: Data?, completionHandler: SimpleCompletionHandler) -> OperationHandle? {
let opType = FileOperationType.create(path: (path as NSString).appendingPathComponent(fileName))
guard fileOperationDelegate?.fileProvider(self, shouldDoOperation: opType) ?? true == true else {
return nil
}
let url = absoluteURL(path)
@@ -188,95 +190,101 @@ extension WebDAVFileProvider: FileProviderOperations {
responseError = FileProviderWebDavError(code: rCode, url: url)
}
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])
self.delegateNotify(opType, error: responseError ?? error)
})
task.taskDescription = opType.json
task.resume()
return RemoteOperationHandle(tasks: [task])
return RemoteOperationHandle(operationType: opType, tasks: [task])
}
@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 {
let opType = FileOperationType.move(source: path, destination: toPath)
guard fileOperationDelegate?.fileProvider(self, shouldDoOperation: opType) ?? true == true else {
return nil
}
return self.copyMoveItem(move: true, path: path, toPath: toPath, overwrite: overwrite, completionHandler: completionHandler)
return self.copyMoveItem(operation: opType, 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 {
let opType = FileOperationType.copy(source: path, destination: toPath)
guard fileOperationDelegate?.fileProvider(self, shouldDoOperation: opType) ?? true == true else {
return nil
}
return self.copyMoveItem(move: false, path: path, toPath: toPath, overwrite: overwrite, completionHandler: completionHandler)
return self.copyMoveItem(operation: opType, overwrite: overwrite, completionHandler: completionHandler)
}
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 {
request.httpMethod = "MOVE"
} else {
request.httpMethod = "COPY"
}
request.setValue(absoluteURL(path).absoluteString, forHTTPHeaderField: "Destination")
fileprivate func copyMoveItem(operation opType: FileOperationType, overwrite: Bool, completionHandler: SimpleCompletionHandler) -> OperationHandle? {
guard let source = opType.source, let dest = opType.destination else { return nil }
// Using switch is more readable than using reflect. Maybe some
let sourceURL = absoluteURL(source)
var request = URLRequest(url: sourceURL)
request.httpMethod = opType.description.uppercased() // "COPY" or "MOVE"
request.setValue(absoluteURL(dest).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 ? FileOperationType.move(source: path, destination: toPath) : .copy(source: path, destination: toPath)
self.delegateNotify(op, error: error)
if response.statusCode >= 300 {
responseError = FileProviderWebDavError(code: code, url: sourceURL)
}
if code == .multiStatus, let data = data {
let xresponses = self.parseXMLResponse(data)
for xresponse in xresponses where (xresponse.status ?? 0) >= 300 {
completionHandler?(FileProviderWebDavError(code: code, url: url))
completionHandler?(FileProviderWebDavError(code: code, url: sourceURL))
}
} else {
completionHandler?(FileProviderWebDavError(code: code, url: url))
}
return
}
completionHandler?(error)
})
if (response as? HTTPURLResponse)?.statusCode ?? 0 != FileProviderHTTPErrorCode.multiStatus.rawValue {
completionHandler?(responseError ?? error)
}
self.delegateNotify(opType, error: responseError ?? error)
})
task.taskDescription = opType.json
task.resume()
return RemoteOperationHandle(tasks: [task])
return RemoteOperationHandle(operationType: opType, tasks: [task])
}
@discardableResult
public func removeItem(path: String, completionHandler: SimpleCompletionHandler) -> OperationHandle? {
guard fileOperationDelegate?.fileProvider(self, shouldDoOperation: .remove(path: path)) ?? true == true else {
let opType = FileOperationType.remove(path: path)
guard fileOperationDelegate?.fileProvider(self, shouldDoOperation: opType) ?? 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 ?? 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(opType, error: responseError ?? error)
})
task.taskDescription = opType.json
task.resume()
return RemoteOperationHandle(tasks: [task])
return RemoteOperationHandle(operationType: opType, tasks: [task])
}
@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 {
let opType = FileOperationType.copy(source: localFile.absoluteString, destination: toPath)
guard fileOperationDelegate?.fileProvider(self, shouldDoOperation: opType) ?? true == true else {
return nil
}
let url = absoluteURL(toPath)
@@ -288,16 +296,17 @@ extension WebDAVFileProvider: FileProviderOperations {
responseError = FileProviderWebDavError(code: rCode, url: url)
}
completionHandler?(responseError ?? error)
self.delegateNotify(.move(source: localFile.absoluteString, destination: toPath), error: responseError ?? error)
self.delegateNotify(opType, error: responseError ?? error)
})
task.taskDescription = dictionaryToJSON(["type": "Copy" as NSString, "source": localFile.absoluteString as NSString, "dest": toPath as NSString])
task.taskDescription = opType.json
task.resume()
return RemoteOperationHandle(tasks: [task])
return RemoteOperationHandle(operationType: opType, tasks: [task])
}
@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 {
let opType = FileOperationType.copy(source: path, destination: toLocalURL.absoluteString)
guard fileOperationDelegate?.fileProvider(self, shouldDoOperation: opType) ?? true == true else {
return nil
}
let url = absoluteURL(path)
@@ -316,10 +325,11 @@ extension WebDAVFileProvider: FileProviderOperations {
}
}
completionHandler?(responseError ?? error)
self.delegateNotify(opType, error: responseError ?? error)
})
task.taskDescription = dictionaryToJSON(["type": "Copy" as NSString, "source": path as NSString, "dest": toLocalURL.absoluteString as NSString])
task.taskDescription = opType.json
task.resume()
return RemoteOperationHandle(tasks: [task])
return RemoteOperationHandle(operationType: opType, tasks: [task])
}
}
@@ -331,6 +341,7 @@ extension WebDAVFileProvider: FileProviderReadWrite {
@discardableResult
public func contents(path: String, offset: Int64, length: Int, completionHandler: @escaping ((_ contents: Data?, _ error: Error?) -> Void)) -> OperationHandle? {
let opType = FileOperationType.fetch(path: path)
let url = absoluteURL(path)
var request = URLRequest(url: url)
request.httpMethod = "GET"
@@ -339,20 +350,21 @@ extension WebDAVFileProvider: FileProviderReadWrite {
} else if offset > 0 && length < 0 {
request.setValue("bytes=\(offset)-", forHTTPHeaderField: "Range")
}
let task = session.dataTask(with: request, completionHandler: { (data, response, error) in
let handle = RemoteOperationHandle(operationType: opType, tasks: [])
runDataTask(with: request, operationHandle: handle, 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)
}
completionHandler(data, responseError ?? error)
})
task.resume()
return RemoteOperationHandle(tasks: [task])
})
return handle
}
@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 {
let opType = FileOperationType.modify(path: path)
guard fileOperationDelegate?.fileProvider(self, shouldDoOperation: opType) ?? true == true else {
return nil
}
// FIXME: lock destination before writing process
@@ -365,7 +377,7 @@ extension WebDAVFileProvider: FileProviderReadWrite {
responseError = FileProviderWebDavError(code: rCode, url: self.absoluteURL(path))
}
defer {
self.delegateNotify(.modify(path: path), error: responseError ?? error)
self.delegateNotify(opType, error: responseError ?? error)
}
if let error = error {
completionHandler?(error)
@@ -375,9 +387,9 @@ extension WebDAVFileProvider: FileProviderReadWrite {
self.moveItem(path: (path as NSString).appendingPathExtension("tmp")!, to: path, completionHandler: completionHandler)
}
})
task.taskDescription = dictionaryToJSON(["type": "Modify" as NSString, "source": path as NSString])
task.taskDescription = opType.json
task.resume()
return RemoteOperationHandle(tasks: [task])
return RemoteOperationHandle(operationType: opType, tasks: [task])
}
public func searchFiles(path: String, recursive: Bool, query: String, foundItemHandler: ((FileObject) -> Void)?, completionHandler: @escaping ((_ files: [FileObject], _ error: Error?) -> Void)) {
@@ -387,8 +399,7 @@ extension WebDAVFileProvider: FileProviderReadWrite {
//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: .utf8)
request.setValue(String(request.httpBody!.count), forHTTPHeaderField: "Content-Length")
let task = session.dataTask(with: request, completionHandler: { (data, response, error) in
runDataTask(with: request, completionHandler: { (data, response, error) in
// FIXME: paginating results
var responseError: FileProviderWebDavError?
if let code = (response as? HTTPURLResponse)?.statusCode , code >= 300, let rCode = FileProviderHTTPErrorCode(rawValue: code) {
@@ -410,8 +421,7 @@ extension WebDAVFileProvider: FileProviderReadWrite {
return
}
completionHandler([], responseError ?? error)
})
task.resume()
})
}
fileprivate func registerNotifcation(path: String, eventHandler: (() -> Void)) {
@@ -428,7 +438,17 @@ extension WebDAVFileProvider: FileProviderReadWrite {
// TODO: implements methods for lock mechanism
}
extension WebDAVFileProvider: FileProvider {}
extension WebDAVFileProvider: FileProvider {
open func copy(with zone: NSZone? = nil) -> Any {
let copy = WebDAVFileProvider(baseURL: self.baseURL!, credential: self.credential, cache: self.cache)!
copy.currentPath = self.currentPath
copy.delegate = self.delegate
copy.fileOperationDelegate = self.fileOperationDelegate
copy.useCache = self.useCache
copy.validatingCache = self.validatingCache
return copy
}
}
// MARK: WEBDAV XML response implementation