Compare commits

..

8 Commits

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