Compare commits

...

4 Commits

Author SHA1 Message Date
Amir Abbas Mousavian fe05fd83fe Replaced encode/decode methods with Data extension 2016-11-26 00:11:46 +03:30
Amir Abbas Mousavian 826d207e6b OperationHandle optimizations
- code refactoring for RemoteOperationHandle usage and description
- bug fix: move operation in Dropbox provider did copy
- bug fix: dynamic inProgress result for RemoteOperationHandle
2016-11-24 22:54:00 +03:30
Amir Abbas Mousavian 66fc1e1284 Fixed unexpected behaviors in Remote providers:
- Calling delegate and completion methods
- refactoring codes
2016-11-23 23:42:36 +03:30
Amir Abbas Mousavian a1d489f5a5 Optimization in project settings for dynamic linking 2016-11-23 19:43:28 +03:30
29 changed files with 518 additions and 543 deletions
+4 -4
View File
@@ -16,8 +16,8 @@ Pod::Spec.new do |s|
#
s.name = "FileProvider"
s.version = "0.6.0"
s.summary = "FileManager replacement for Local and Remote (WebDAV/Dropbox/SMB2) files on iOS and MacOS."
s.version = "0.7.0"
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"
+25 -29
View File
@@ -10,6 +10,7 @@
7902C0861D61B56D00564440 /* RemoteSession.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7902C0851D61B56D00564440 /* RemoteSession.swift */; };
7902C0871D61B67100564440 /* RemoteSession.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7902C0851D61B56D00564440 /* RemoteSession.swift */; };
7902C0881D61B67100564440 /* RemoteSession.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7902C0851D61B56D00564440 /* RemoteSession.swift */; };
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 */; };
@@ -98,6 +99,7 @@
/* Begin PBXFileReference section */
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,9 +218,9 @@
799396911D48C02300086753 /* Sources */ = {
isa = PBXGroup;
children = (
7924B18B1D89DAE000589DB7 /* AEXML */,
799396991D48C02300086753 /* SMBTypes */,
799396931D48C02300086753 /* DropboxFileProvider.swift */,
7924B18B1D89DAE000589DB7 /* AEXML */,
794C21FD1D58912A00EC49B8 /* DropboxHelper.swift */,
799396941D48C02300086753 /* FileProvider.h */,
799396951D48C02300086753 /* FileProvider.swift */,
@@ -332,7 +344,7 @@
7993965C1D48B7BF00086753 /* Project object */ = {
isa = PBXProject;
attributes = {
LastUpgradeCheck = 0800;
LastUpgradeCheck = 0810;
TargetAttributes = {
799396661D48B7F600086753 = {
CreatedOnToolsVersion = 7.3.1;
@@ -501,6 +513,7 @@
799396601D48B7BF00086753 /* Debug */ = {
isa = XCBuildConfiguration;
buildSettings = {
BUNDLE_VERSION_STRING = 0.7.0;
CLANG_WARN_BOOL_CONVERSION = YES;
CLANG_WARN_CONSTANT_CONVERSION = YES;
CLANG_WARN_EMPTY_BODY = YES;
@@ -510,9 +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.7.0;
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,19 +585,15 @@
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
CLANG_WARN_UNREACHABLE_CODE = YES;
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
COPY_PHASE_STRIP = NO;
CURRENT_PROJECT_VERSION = 1;
DEBUG_INFORMATION_FORMAT = dwarf;
DEFINES_MODULE = YES;
DYLIB_COMPATIBILITY_VERSION = 1;
DYLIB_CURRENT_VERSION = 1;
DYLIB_INSTALL_NAME_BASE = "@rpath";
ENABLE_STRICT_OBJC_MSGSEND = YES;
ENABLE_TESTABILITY = YES;
GCC_C_LANGUAGE_STANDARD = gnu99;
GCC_DYNAMIC_NO_PIC = NO;
GCC_NO_COMMON_BLOCKS = YES;
GCC_OPTIMIZATION_LEVEL = 0;
GCC_PREPROCESSOR_DEFINITIONS = (
"DEBUG=1",
"$(inherited)",
@@ -614,8 +627,6 @@
ALWAYS_SEARCH_USER_PATHS = NO;
APPLICATION_EXTENSION_API_ONLY = YES;
CLANG_ANALYZER_NONNULL = YES;
CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
CLANG_CXX_LIBRARY = "libc++";
CLANG_ENABLE_MODULES = YES;
CLANG_ENABLE_OBJC_ARC = YES;
CLANG_WARN_BOOL_CONVERSION = YES;
@@ -627,16 +638,13 @@
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
CLANG_WARN_UNREACHABLE_CODE = YES;
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
COPY_PHASE_STRIP = NO;
CURRENT_PROJECT_VERSION = 1;
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
DEFINES_MODULE = YES;
DYLIB_COMPATIBILITY_VERSION = 1;
DYLIB_CURRENT_VERSION = 1;
DYLIB_INSTALL_NAME_BASE = "@rpath";
ENABLE_NS_ASSERTIONS = NO;
ENABLE_STRICT_OBJC_MSGSEND = YES;
GCC_C_LANGUAGE_STANDARD = gnu99;
GCC_NO_COMMON_BLOCKS = YES;
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
@@ -680,9 +688,7 @@
CLANG_WARN_UNREACHABLE_CODE = YES;
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
COMBINE_HIDPI_IMAGES = YES;
COPY_PHASE_STRIP = NO;
CURRENT_PROJECT_VERSION = 1;
DEBUG_INFORMATION_FORMAT = dwarf;
DEFINES_MODULE = YES;
DYLIB_COMPATIBILITY_VERSION = 1;
DYLIB_CURRENT_VERSION = 1;
@@ -690,10 +696,8 @@
ENABLE_STRICT_OBJC_MSGSEND = YES;
ENABLE_TESTABILITY = YES;
FRAMEWORK_VERSION = A;
GCC_C_LANGUAGE_STANDARD = gnu99;
GCC_DYNAMIC_NO_PIC = NO;
GCC_NO_COMMON_BLOCKS = YES;
GCC_OPTIMIZATION_LEVEL = 0;
GCC_PREPROCESSOR_DEFINITIONS = (
"DEBUG=1",
"$(inherited)",
@@ -740,9 +744,7 @@
CLANG_WARN_UNREACHABLE_CODE = YES;
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
COMBINE_HIDPI_IMAGES = YES;
COPY_PHASE_STRIP = NO;
CURRENT_PROJECT_VERSION = 1;
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
DEFINES_MODULE = YES;
DYLIB_COMPATIBILITY_VERSION = 1;
DYLIB_CURRENT_VERSION = 1;
@@ -750,7 +752,6 @@
ENABLE_NS_ASSERTIONS = NO;
ENABLE_STRICT_OBJC_MSGSEND = YES;
FRAMEWORK_VERSION = A;
GCC_C_LANGUAGE_STANDARD = gnu99;
GCC_NO_COMMON_BLOCKS = YES;
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
@@ -791,19 +792,16 @@
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
CLANG_WARN_UNREACHABLE_CODE = YES;
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
COPY_PHASE_STRIP = NO;
"CODE_SIGN_IDENTITY[sdk=appletvos*]" = "";
CURRENT_PROJECT_VERSION = 1;
DEBUG_INFORMATION_FORMAT = dwarf;
DEFINES_MODULE = YES;
DYLIB_COMPATIBILITY_VERSION = 1;
DYLIB_CURRENT_VERSION = 1;
DYLIB_INSTALL_NAME_BASE = "@rpath";
ENABLE_STRICT_OBJC_MSGSEND = YES;
ENABLE_TESTABILITY = YES;
GCC_C_LANGUAGE_STANDARD = gnu99;
GCC_DYNAMIC_NO_PIC = NO;
GCC_NO_COMMON_BLOCKS = YES;
GCC_OPTIMIZATION_LEVEL = 0;
GCC_PREPROCESSOR_DEFINITIONS = (
"DEBUG=1",
"$(inherited)",
@@ -850,16 +848,14 @@
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
CLANG_WARN_UNREACHABLE_CODE = YES;
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
COPY_PHASE_STRIP = NO;
"CODE_SIGN_IDENTITY[sdk=appletvos*]" = "";
CURRENT_PROJECT_VERSION = 1;
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
DEFINES_MODULE = YES;
DYLIB_COMPATIBILITY_VERSION = 1;
DYLIB_CURRENT_VERSION = 1;
DYLIB_INSTALL_NAME_BASE = "@rpath";
ENABLE_NS_ASSERTIONS = NO;
ENABLE_STRICT_OBJC_MSGSEND = YES;
GCC_C_LANGUAGE_STANDARD = gnu99;
GCC_NO_COMMON_BLOCKS = YES;
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
LastUpgradeVersion = "0800"
LastUpgradeVersion = "0810"
version = "1.3">
<BuildAction
parallelizeBuildables = "YES"
@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
LastUpgradeVersion = "0800"
LastUpgradeVersion = "0810"
version = "1.3">
<BuildAction
parallelizeBuildables = "YES"
@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
LastUpgradeVersion = "0800"
LastUpgradeVersion = "0810"
version = "1.3">
<BuildAction
parallelizeBuildables = "YES"
+1 -1
View File
@@ -15,7 +15,7 @@
<key>CFBundlePackageType</key>
<string>FMWK</string>
<key>CFBundleShortVersionString</key>
<string>0.3.0</string>
<string>${BUNDLE_VERSION_STRING}</string>
<key>CFBundleSignature</key>
<string>????</string>
<key>CFBundleVersion</key>
+1 -1
View File
@@ -15,7 +15,7 @@
<key>CFBundlePackageType</key>
<string>FMWK</string>
<key>CFBundleShortVersionString</key>
<string>0.3.0</string>
<string>${BUNDLE_VERSION_STRING}</string>
<key>CFBundleSignature</key>
<string>????</string>
<key>CFBundleVersion</key>
+1 -1
View File
@@ -15,7 +15,7 @@
<key>CFBundlePackageType</key>
<string>FMWK</string>
<key>CFBundleShortVersionString</key>
<string>0.3.0</string>
<string>${BUNDLE_VERSION_STRING}</string>
<key>CFBundleSignature</key>
<string>????</string>
<key>CFBundleVersion</key>
+3 -3
View File
@@ -115,7 +115,7 @@ Your class should conforms `FileProviderDelegate` class:
case .remove(path: let path):
print("\(path) has been deleted.")
default:
break
print("\(operation.actionDescription) from \(operation.source ?? "") to \(operation.destination) succeed")
}
}
@@ -126,7 +126,7 @@ Your class should conforms `FileProviderDelegate` class:
case .remove(path: let path):
print("\(path) can't be deleted.")
default:
break
print("\(operation.actionDescription) from \(operation.source ?? "") to \(operation.destination) failed")
}
}
@@ -255,7 +255,7 @@ If you want to retrieve a portion of file you can use `contents` method with off
### Operation Handle
Creating/Copying/Deleting functions return a `OperationHandle` for remote operations. It provides progress and a `.cancel()` method which allows you to cancel operation in midst.
Creating/Copying/Deleting functions return a `OperationHandle` for remote operations. It provides operation type, progress and a `.cancel()` method which allows you to cancel operation in midst.
It's not supported by native `(NS)FileManager` so `LocalFileProvider`, but this functionality will be added to future `PosixFileProvider` class.
+53 -62
View File
@@ -64,20 +64,16 @@ open class DropboxFileProvider: NSObject, FileProviderBasic {
let requestDictionary = ["path": correctPath(path)! as NSString]
request.httpBody = dictionaryToJSON(requestDictionary)?.data(using: .utf8)
let task = session.dataTask(with: request, completionHandler: { (data, response, error) in
var dbError: FileProviderDropboxError?
var fileObject: DropboxFileObject?
if let response = response as? HTTPURLResponse {
defer {
self.delegateNotify(.create(path: path), error: error)
}
let code = FileProviderHTTPErrorCode(rawValue: response.statusCode)
let dbError: FileProviderDropboxError? = code != nil ? FileProviderDropboxError(code: code!, path: path, errorDescription: String(data: data ?? Data(), encoding: .utf8)) : nil
dbError = code != nil ? FileProviderDropboxError(code: code!, path: path, errorDescription: String(data: data ?? Data(), encoding: .utf8)) : nil
if let data = data, let jsonStr = String(data: data, encoding: .utf8), let json = jsonToDictionary(jsonStr), let file = self.mapToFileObject(json) {
completionHandler(file, dbError)
return
fileObject = file
}
completionHandler(nil, dbError)
return
}
completionHandler(nil, error)
completionHandler(fileObject, dbError ?? error)
})
task.resume()
}
@@ -88,13 +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
var totalSize: Int64 = -1
var usedSize: Int64 = 0
if let data = data, let jsonStr = String(data: data, encoding: .utf8), let json = jsonToDictionary(jsonStr) {
let totalSize = ((json["allocation"] as? NSDictionary)?["allocated"] as? NSNumber)?.int64Value ?? -1
let usedSize = (json["used"] as? NSNumber)?.int64Value ?? 0
completionHandler(totalSize, usedSize)
return
totalSize = ((json["allocation"] as? NSDictionary)?["allocated"] as? NSNumber)?.int64Value ?? -1
usedSize = (json["used"] as? NSNumber)?.int64Value ?? 0
}
completionHandler(-1, 0)
completionHandler(totalSize, usedSize)
})
task.resume()
}
@@ -131,25 +127,18 @@ extension DropboxFileProvider: FileProviderOperations {
return nil
}
let url: String
var path: String?, fromPath: String?, toPath: String?
guard let sourcePath = operation.source else { return nil }
let destPath = operation.destination
switch operation {
case .create(path: let p):
case .create:
url = "https://api.dropboxapi.com/2/files/create_folder"
path = p
case .copy(source: let fp, destination: let tp):
case .copy:
url = "https://api.dropboxapi.com/2/files/copy"
fromPath = fp
toPath = tp
case .move(source: let fp, destination: let tp):
case .move:
url = "https://api.dropboxapi.com/2/files/move"
fromPath = fp
toPath = tp
case .modify(path: let p):
return nil
case .remove(path: let p):
case .remove:
url = "https://api.dropboxapi.com/2/files/delete"
path = p
case .link(link: _, target: _):
default: // modify, link, fetch
return nil
}
var request = URLRequest(url: URL(string: url)!)
@@ -157,31 +146,29 @@ extension DropboxFileProvider: FileProviderOperations {
request.setValue("Bearer \(credential?.password ?? "")", forHTTPHeaderField: "Authorization")
request.setValue("application/json", forHTTPHeaderField: "Content-Type")
var requestDictionary = [String: AnyObject]()
requestDictionary["path"] = correctPath(path) as NSString?
requestDictionary["from_path"] = correctPath(fromPath) as NSString?
requestDictionary["to_path"] = correctPath(toPath) as NSString?
if let dest = correctPath(destPath) as NSString? {
requestDictionary["from_path"] = correctPath(sourcePath) as NSString?
requestDictionary["to_path"] = dest
} else {
requestDictionary["path"] = correctPath(sourcePath) as NSString?
}
request.httpBody = dictionaryToJSON(requestDictionary)?.data(using: .utf8)
let task = session.dataTask(with: request, completionHandler: { (data, response, error) in
if let response = response as? HTTPURLResponse {
let code = FileProviderHTTPErrorCode(rawValue: response.statusCode)
let dbError: FileProviderDropboxError? = code != nil ? FileProviderDropboxError(code: code!, path: path ?? fromPath ?? "", errorDescription: String(data: data ?? Data(), encoding: .utf8)) : nil
defer {
self.delegateNotify(operation, error: error ?? dbError)
}
/*if let data = data, let jsonStr = String(data: data, encoding: NSUTF8StringEncoding) {
let json = self.jsonToDictionary(jsonStr)
}*/
completionHandler?(dbError)
return
var dbError: FileProviderDropboxError?
if let response = response as? HTTPURLResponse, response.statusCode >= 300, let code = FileProviderHTTPErrorCode(rawValue: response.statusCode) {
dbError = FileProviderDropboxError(code: code, path: sourcePath, errorDescription: String(data: data ?? Data(), encoding: .utf8))
}
completionHandler?(error)
})
completionHandler?(dbError ?? error)
self.delegateNotify(operation, error: dbError ?? error)
})
task.taskDescription = operation.json
task.resume()
return RemoteOperationHandle(tasks: [task])
return RemoteOperationHandle(operationType: operation, tasks: [task])
}
public func copyItem(localFile: URL, to toPath: String, completionHandler: SimpleCompletionHandler) -> OperationHandle? {
guard fileOperationDelegate?.fileProvider(self, shouldDoOperation: .copy(source: localFile.absoluteString, destination: toPath)) ?? true == true else {
let opType = FileOperationType.copy(source: localFile.absoluteString, destination: toPath)
guard fileOperationDelegate?.fileProvider(self, shouldDoOperation: opType) ?? true == true else {
return nil
}
guard let data = try? Data(contentsOf: localFile) else {
@@ -189,11 +176,12 @@ extension DropboxFileProvider: FileProviderOperations {
completionHandler?(error)
return nil
}
return upload_simple(toPath, data: data, overwrite: true, operation: .copy(source: localFile.absoluteString, destination: toPath), completionHandler: completionHandler)
return upload_simple(toPath, data: data, overwrite: true, operation: opType, completionHandler: completionHandler)
}
public func copyItem(path: String, toLocalURL destURL: URL, completionHandler: SimpleCompletionHandler) -> OperationHandle? {
guard fileOperationDelegate?.fileProvider(self, shouldDoOperation: .copy(source: path, destination: destURL.absoluteString)) ?? true == true else {
let opType = FileOperationType.copy(source: path, destination: destURL.absoluteString)
guard fileOperationDelegate?.fileProvider(self, shouldDoOperation: opType) ?? true == true else {
return nil
}
let url = URL(string: "https://content.dropboxapi.com/2/files/download")!
@@ -217,9 +205,9 @@ extension DropboxFileProvider: FileProviderOperations {
completionHandler?(e)
}
})
task.taskDescription = dictionaryToJSON(["type": "Copy" as NSString, "source": path as NSString, "dest": destURL.absoluteString as NSString])
task.taskDescription = opType.json
task.resume()
return RemoteOperationHandle(tasks: [task])
return RemoteOperationHandle(operationType: opType, tasks: [task])
}
}
@@ -229,6 +217,7 @@ extension DropboxFileProvider: FileProviderReadWrite {
}
public func contents(path: String, offset: Int64, length: Int, completionHandler: @escaping ((_ contents: Data?, _ error: Error?) -> Void)) -> OperationHandle? {
let opType = FileOperationType.fetch(path: path)
let url = URL(string: "https://content.dropboxapi.com/2/files/download")!
var request = URLRequest(url: url)
request.httpMethod = "GET"
@@ -240,25 +229,26 @@ extension DropboxFileProvider: FileProviderReadWrite {
}
let requestDictionary = ["path": path]
request.setValue(dictionaryToJSON(requestDictionary as [String : AnyObject]), forHTTPHeaderField: "Dropbox-API-Arg")
let task = session.dataTask(with: request, completionHandler: { (datam, response, error) in
guard let data = datam, let httpResponse = response as? HTTPURLResponse , httpResponse.statusCode < 300 else {
let code = FileProviderHTTPErrorCode(rawValue: (response as? HTTPURLResponse)?.statusCode ?? -1)
let dbError: FileProviderDropboxError? = code != nil ? FileProviderDropboxError(code: code!, path: path, errorDescription: String(data: datam ?? Data(), encoding: .utf8)) : nil
completionHandler(nil, dbError ?? error)
return
let task = session.dataTask(with: request, completionHandler: { (data, response, error) in
var dbError: FileProviderDropboxError?
if let httpResponse = response as? HTTPURLResponse , httpResponse.statusCode >= 300, let code = FileProviderHTTPErrorCode(rawValue: httpResponse.statusCode) {
dbError = FileProviderDropboxError(code: code, path: path, errorDescription: String(data: data ?? Data(), encoding: .utf8))
}
completionHandler(data, error)
let filedata = dbError ?? error == nil ? data : nil
completionHandler(filedata, dbError ?? error)
})
task.taskDescription = opType.json
task.resume()
return RemoteOperationHandle(tasks: [task])
return RemoteOperationHandle(operationType: opType, tasks: [task])
}
public func writeContents(path: String, contents data: Data, atomically: Bool = false, completionHandler: SimpleCompletionHandler) -> OperationHandle? {
guard fileOperationDelegate?.fileProvider(self, shouldDoOperation: .modify(path: path)) ?? true == true else {
let opType = FileOperationType.modify(path: path)
guard fileOperationDelegate?.fileProvider(self, shouldDoOperation: opType) ?? true == true else {
return nil
}
// FIXME: remove 150MB restriction
return upload_simple(path, data: data, overwrite: true, operation: .modify(path: path), completionHandler: completionHandler)
return upload_simple(path, data: data, overwrite: true, operation: opType, completionHandler: completionHandler)
}
public func searchFiles(path: String, recursive: Bool, query: String, foundItemHandler: ((FileObject) -> Void)?, completionHandler: @escaping ((_ files: [FileObject], _ error: Error?) -> Void)) {
@@ -327,7 +317,7 @@ extension DropboxFileProvider: ExtendedFileProvider {
requestDictionary["format"] = "jpeg" as NSString
requestDictionary["size"] = "w\(Int(dimension.width))h\(Int(dimension.height))" as NSString
request.setValue(dictionaryToJSON(requestDictionary), forHTTPHeaderField: "Dropbox-API-Arg")
self.session.dataTask(with: request, completionHandler: { (data, response, error) in
let task = self.session.dataTask(with: request, completionHandler: { (data, response, error) in
var image: ImageClass? = nil
if let r = response as? HTTPURLResponse, let result = r.allHeaderFields["Dropbox-API-Result"] as? String, let jsonResult = jsonToDictionary(result) {
if jsonResult["error"] != nil {
@@ -338,7 +328,8 @@ extension DropboxFileProvider: ExtendedFileProvider {
image = ImageClass(data: data)
}
completionHandler(image, error)
})
})
task.resume()
}
public func propertiesOfFile(path: String, completionHandler: @escaping ((_ propertiesDictionary: [String : Any], _ keys: [String], _ error: Error?) -> Void)) {
+8 -26
View File
@@ -53,13 +53,13 @@ internal extension DropboxFileProvider {
request.httpBody = dictionaryToJSON(requestDictionary)?.data(using: .utf8)
let task = session.dataTask(with: request, completionHandler: { (data, response, error) in
var responseError: FileProviderDropboxError?
var files = prevContents
if let code = (response as? HTTPURLResponse)?.statusCode , code >= 300, let rCode = FileProviderHTTPErrorCode(rawValue: code) {
responseError = FileProviderDropboxError(code: rCode, path: path, errorDescription: String(data: data ?? Data(), encoding: .utf8))
}
if let data = data, let jsonStr = String(data: data, encoding: .utf8) {
let json = jsonToDictionary(jsonStr)
if let entries = json?["entries"] as? [AnyObject] , entries.count > 0 {
var files = prevContents
for entry in entries {
if let entry = entry as? [String: AnyObject], let file = self.mapToFileObject(entry) {
files.append(file)
@@ -69,14 +69,13 @@ internal extension DropboxFileProvider {
let hasmore = (json?["has_more"] as? NSNumber)?.boolValue ?? false
if hasmore {
self.list(path, cursor: ncursor, prevContents: files, completionHandler: completionHandler)
} else {
completionHandler(files, ncursor, responseError ?? error)
return
}
return
}
}
completionHandler([], nil, responseError ?? error)
})
completionHandler(files, nil, responseError ?? error)
})
task.taskDescription = FileOperationType.fetch(path: path).json
task.resume()
}
@@ -101,29 +100,12 @@ internal extension DropboxFileProvider {
if let code = (response as? HTTPURLResponse)?.statusCode , code >= 300, let rCode = FileProviderHTTPErrorCode(rawValue: code) {
responseError = FileProviderDropboxError(code: rCode, path: targetPath, errorDescription: String(data: data ?? Data(), encoding: .utf8))
}
defer {
self.delegateNotify(.create(path: targetPath), error: responseError ?? error)
}
completionHandler?(responseError ?? error)
self.delegateNotify(.create(path: targetPath), error: responseError ?? error)
})
var dic: [String: AnyObject] = ["type": operation.description as NSString]
switch operation {
case .create(path: let s):
dic["source"] = s as NSString
case .copy(source: let s, destination: let d):
dic["source"] = s as NSString
dic["dest"] = d as NSString
case .modify(path: let s):
dic["source"] = s as NSString
case .move(source: let s, destination: let d):
dic["source"] = s as NSString
dic["dest"] = d as NSString
default:
break
}
task.taskDescription = dictionaryToJSON(dic)
task.taskDescription = operation.json
task.resume()
return RemoteOperationHandle(tasks: [task])
return RemoteOperationHandle(operationType: operation, tasks: [task])
}
func search(_ startPath: String = "", query: String, start: Int = 0, maxResultPerPage: Int = 25, maxResults: Int = -1, foundItem:@escaping ((_ file: DropboxFileObject) -> Void), completionHandler: @escaping ((_ error: Error?) -> Void)) {
+39 -18
View File
@@ -289,37 +289,58 @@ public enum FileOperationType: CustomStringConvertible {
case modify (path: String)
case remove (path: String)
case link (link: String, target: String)
case fetch (path: String)
public var description: String {
switch self {
case .create(path: _): return "Create"
case .copy(source: _, destination: _): return "Copy"
case .move(source: _, destination: _): return "Move"
case .modify(path: _): return "Modify"
case .remove(path: _): return "Remove"
case .link(link: _, target: _): return "Link"
case .create: return "Create"
case .copy: return "Copy"
case .move: return "Move"
case .modify: return "Modify"
case .remove: return "Remove"
case .link: return "Link"
case .fetch: return "Fetch"
}
}
internal var actionDescription: String {
switch self {
case .create(path: _): return "Creating"
case .copy(source: _, destination: _): return "Copying"
case .move(source: _, destination: _): return "Moving"
case .modify(path: _): return "Modifying"
case .remove(path: _): return "Removing"
case .link(link: _, target: _): return "Linking"
}
public var actionDescription: String {
return description.trimmingCharacters(in: CharacterSet(charactersIn: "e")) + "ing"
}
public var source: String? {
guard let reflect = Mirror(reflecting: self).children.first?.value else { return nil }
let mirror = Mirror(reflecting: reflect)
return reflect as? String ?? mirror.children.first?.value as? String
}
public var destination: String? {
guard let reflect = Mirror(reflecting: self).children.first?.value else { return nil }
let mirror = Mirror(reflecting: reflect)
return mirror.children.dropFirst().first?.value as? String
}
internal var json: String? {
var dictionary: [String: AnyObject] = ["type": self.description as NSString]
dictionary["source"] = source as NSString?
dictionary["dest"] = destination as NSString?
return dictionaryToJSON(dictionary)
}
}
@objc
public protocol OperationHandle {
var progress: Float { get }
var operationType: FileOperationType { get }
var bytesSoFar: Int64 { get }
var totalBytes: Int64 { get }
var inProgress: Bool { get }
func cancel()
func cancel() -> Bool
}
public extension OperationHandle {
public var progress: Float {
let bytesSoFar = self.bytesSoFar
let totalBytes = self.totalBytes
return totalBytes > 0 ? Float(Double(bytesSoFar) / Double(totalBytes)) : Float.nan
}
}
internal class Weak<T: AnyObject> {
+141 -12
View File
@@ -28,8 +28,8 @@ open class LocalFileProvider: FileProvider, FileProviderMonitor {
open weak var delegate: FileProviderDelegate?
open let credential: URLCredential? = nil
open let fileManager = FileManager()
open let opFileManager = FileManager()
open private(set) var fileManager = FileManager()
open private(set) var opFileManager = FileManager()
fileprivate var fileProviderManagerDelegate: LocalFileProviderManagerDelegate? = nil
public init () {
@@ -102,7 +102,6 @@ open class LocalFileProvider: FileProvider, FileProviderMonitor {
open weak var fileOperationDelegate : FileOperationDelegate?
@objc(createWithFolder:at:completionHandler:)
@discardableResult
open func create(folder folderName: String, at atPath: String, completionHandler: SimpleCompletionHandler) -> OperationHandle? {
operation_queue.async {
@@ -119,7 +118,7 @@ open class LocalFileProvider: FileProvider, FileProviderMonitor {
})
}
}
return nil
return LocalOperationHandle(operationType: .create(path: (atPath as NSString).appendingPathComponent(folderName)), baseURL: self.baseURL)
}
@discardableResult
@@ -152,7 +151,7 @@ open class LocalFileProvider: FileProvider, FileProviderMonitor {
})
}
}
return nil
return LocalOperationHandle(operationType: .create(path: (atPath as NSString).appendingPathComponent(fileAttribs.name)), baseURL: self.baseURL)
}
@discardableResult
@@ -176,7 +175,7 @@ open class LocalFileProvider: FileProvider, FileProviderMonitor {
})
}
}
return nil
return LocalOperationHandle(operationType: .move(source: path, destination: toPath), baseURL: self.baseURL)
}
@discardableResult
@@ -200,7 +199,7 @@ open class LocalFileProvider: FileProvider, FileProviderMonitor {
})
}
}
return nil
return LocalOperationHandle(operationType: .copy(source: path, destination: toPath), baseURL: self.baseURL)
}
@discardableResult
@@ -219,7 +218,7 @@ open class LocalFileProvider: FileProvider, FileProviderMonitor {
})
}
}
return nil
return LocalOperationHandle(operationType: .remove(path: path), baseURL: self.baseURL)
}
@discardableResult
@@ -238,7 +237,7 @@ open class LocalFileProvider: FileProvider, FileProviderMonitor {
})
}
}
return nil
return LocalOperationHandle(operationType: .move(source: localFile.absoluteString, destination: toPath), baseURL: self.baseURL)
}
@discardableResult
@@ -257,7 +256,7 @@ open class LocalFileProvider: FileProvider, FileProviderMonitor {
})
}
}
return nil
return LocalOperationHandle(operationType: .move(source: path, destination: toLocalURL.absoluteString), baseURL: self.baseURL)
}
@discardableResult
@@ -297,7 +296,7 @@ open class LocalFileProvider: FileProvider, FileProviderMonitor {
completionHandler(data, nil)
}
}
return nil
return LocalOperationHandle(operationType: .fetch(path: path), baseURL: self.baseURL)
}
@discardableResult
@@ -308,7 +307,7 @@ open class LocalFileProvider: FileProvider, FileProviderMonitor {
self.delegate?.fileproviderSucceed(self, operation: .modify(path: path))
})
}
return nil
return LocalOperationHandle(operationType: .modify(path: path), baseURL: self.baseURL)
}
open func searchFiles(path: String, recursive: Bool, query: String, foundItemHandler: ((FileObject) -> Void)?, completionHandler: @escaping ((_ files: [FileObject], _ error: Error?) -> Void)) {
@@ -515,3 +514,133 @@ internal class LocalFolderMonitor {
source.cancel()
}
}
open class LocalOperationHandle: OperationHandle {
public let baseURL: URL
public let operationType: FileOperationType
init (operationType: FileOperationType, baseURL: URL?) {
self.baseURL = baseURL ?? LocalFileProvider.defaultBaseURL()
self.operationType = operationType
}
private var sourceURL: URL? {
guard let source = operationType.source else { return nil }
return source.hasPrefix("file://") ? URL(fileURLWithPath: source) : baseURL.appendingPathComponent(source)
}
private var destURL: URL? {
guard let dest = operationType.destination else { return nil }
return dest.hasPrefix("file://") ? URL(fileURLWithPath: dest) : baseURL.appendingPathComponent(dest)
}
/// Caution: may put pressure on CPU, may have latency
open var bytesSoFar: Int64 {
assert(!Thread.isMainThread, "Don't run \(#function) method on main thread")
switch operationType {
case .modify:
guard let url = sourceURL, url.isFileURL else { return 0 }
if url.fileIsDirectory {
return iterateDirectory(url, deep: true).totalsize
} else {
return url.fileSize
}
case .copy, .move:
guard let url = destURL, url.isFileURL else { return 0 }
if url.fileIsDirectory {
return iterateDirectory(url, deep: true).totalsize
} else {
return url.fileSize
}
default:
return 0
}
}
/// Caution: may put pressure on CPU, may have latency
open var totalBytes: Int64 {
assert(!Thread.isMainThread, "Don't run \(#function) method on main thread")
switch operationType {
case .copy, .move:
guard let url = sourceURL, url.isFileURL else { return 0 }
if url.fileIsDirectory {
return iterateDirectory(url, deep: true).totalsize
} else {
return url.fileSize
}
default:
return 0
}
}
/// Not usable in local provider
open var inProgress: Bool {
return false
}
/// Not usable in local provider
open func cancel() -> Bool{
return false
}
func iterateDirectory(_ pathURL: URL, deep: Bool) -> (folders: Int, files: Int, totalsize: Int64) {
var folders = 0
var files = 0
var totalsize: Int64 = 0
let keys: [URLResourceKey] = [.isDirectoryKey, .fileSizeKey]
let enumOpt: FileManager.DirectoryEnumerationOptions = !deep ? [.skipsSubdirectoryDescendants, .skipsPackageDescendants] : []
let fp = FileManager()
let filesList = fp.enumerator(at: pathURL, includingPropertiesForKeys: keys, options: enumOpt, errorHandler: nil)
while let fileURL = filesList?.nextObject() as? URL {
var isdirv, sizev: AnyObject?
do {
try (fileURL as NSURL).getResourceValue(&isdirv, forKey: URLResourceKey.isDirectoryKey)
} catch _ {
}
do {
try (fileURL as NSURL).getResourceValue(&sizev, forKey: URLResourceKey.fileSizeKey)
} catch _ {
}
let isdir = isdirv?.boolValue ?? false
let size = sizev?.int64Value ?? 0
if isdir {
folders += 1
} else {
files += 1
}
totalsize += size
}
return (folders, files, totalsize)
}
}
internal extension URL {
var fileIsDirectory: Bool {
var isdirv: AnyObject?
do {
try (self as NSURL).getResourceValue(&isdirv, forKey: URLResourceKey.isDirectoryKey)
} catch _ {
}
return isdirv?.boolValue ?? false
}
var fileSize: Int64 {
var sizev: AnyObject?
do {
try (self as NSURL).getResourceValue(&sizev, forKey: URLResourceKey.fileSizeKey)
} catch _ {
}
return sizev?.int64Value ?? -1
}
var fileExists: Bool {
if self.isFileURL {
return FileManager.default.fileExists(atPath: self.path)
}
return false
}
}
+11 -14
View File
@@ -12,14 +12,11 @@ open class RemoteOperationHandle: OperationHandle {
internal var tasks: [Weak<URLSessionTask>]
private var _inProgress = false
open var inProgress: Bool {
return _inProgress
}
open private(set) var operationType: FileOperationType
init(tasks: [URLSessionTask]) {
init(operationType: FileOperationType, tasks: [URLSessionTask]) {
self.operationType = operationType
self.tasks = tasks.map { Weak<URLSessionTask>($0) }
_inProgress = true
}
internal func add(task: URLSessionTask) {
@@ -30,12 +27,6 @@ open class RemoteOperationHandle: OperationHandle {
self.tasks = tasks.filter { $0.value != nil }
}
open var progress: Float {
let bytesSoFar = self.bytesSoFar
let totalBytes = self.totalBytes
return totalBytes > 0 ? Float(Double(bytesSoFar) / Double(totalBytes)) : Float.nan
}
open var bytesSoFar: Int64 {
return tasks.reduce(0) {
if let task = $1.value as? URLSessionUploadTask {
@@ -56,11 +47,17 @@ open class RemoteOperationHandle: OperationHandle {
}
}
open func cancel() {
open func cancel() -> Bool {
var canceled = false
for taskbox in tasks {
taskbox.value?.cancel()
canceled = true
}
_inProgress = false
return canceled
}
open var inProgress: Bool {
return tasks.reduce(false) { $0 || $1.value?.state ?? .canceling == .running }
}
}
+10 -10
View File
@@ -70,6 +70,7 @@ class SMB2ProtocolClient: FPSStreamTask {
})
return mId
}
func sendTreeDisconnect(id treeId: UInt32, completionHandler: SimpleCompletionHandler) -> UInt64 {
let mId = messageId()
let smbHeader = SMB2.Header(command: .TREE_DISCONNECT, creditRequestResponse: 111, messageId: mId, treeId: treeId, sessionId: sessionId)
@@ -98,9 +99,10 @@ class SMB2ProtocolClient: FPSStreamTask {
}
return currentMessageID
}
// MARK: create and analyse messages
}
// MARK: create and analyse messages
extension SMB2ProtocolClient {
func determineSMBVersion(_ data: Data) -> Float {
let smbverChar: Int8 = Int8(bitPattern: data.first ?? 0)
let version = 0 - smbverChar
@@ -116,7 +118,7 @@ class SMB2ProtocolClient: FPSStreamTask {
throw SMBFileProviderError.incompatibleHeader
}
let headersize = MemoryLayout<SMB1.Header>.size
let header: SMB1.Header = decode(data)
let header: SMB1.Header = data.scanValue()!
var blocks = [(params: [UInt16], message: Data?)]()
var offset = headersize
while offset < data.count {
@@ -128,7 +130,7 @@ class SMB2ProtocolClient: FPSStreamTask {
offset += MemoryLayout<UInt8>.size
var rawParamWords = [UInt8](buffer[offset..<(offset + paramWordsCount * 2)])
let paramData = Data(bytesNoCopy: UnsafeMutablePointer<UInt8>(&rawParamWords), count: rawParamWords.count, deallocator: .free)
paramWords = decode(paramData)
paramWords = paramData.scanValue()!
offset += paramWordsCount * 2
let messageBytesCount = Int(UInt16(buffer[0]) + UInt16(buffer[1]) << 8)
offset += MemoryLayout<UInt16>.size
@@ -154,7 +156,7 @@ class SMB2ProtocolClient: FPSStreamTask {
let headerData = data.subdata(in: 0..<headersize)
let messageSize = data.count - headersize
let messageData = data.subdata(in: headersize..<(headersize + messageSize))
let header: SMB2.Header = decode(headerData)
let header: SMB2.Header = headerData.scanValue()!
switch header.command {
case .NEGOTIATE:
return (header, SMB2.NegotiateResponse(data: messageData))
@@ -200,8 +202,7 @@ class SMB2ProtocolClient: FPSStreamTask {
}
func createSMBMessage(header: SMB1.Header, blocks: [(params: Data?, message: Data?)]) -> Data {
var headerv = header
var result = encode(&headerv)
var result = Data(value: header)
for block in blocks {
var paramWordsCount = UInt8(block.params?.count ?? 0)
result.append(&paramWordsCount, count: MemoryLayout.size(ofValue: paramWordsCount))
@@ -219,8 +220,7 @@ class SMB2ProtocolClient: FPSStreamTask {
}
func createSMB2Message(header: SMB2.Header, message: SMBRequest) -> Data {
var headerv = header
var result = encode(&headerv)
var result = Data(value: header)
result.append(message.data())
return result
}
+1 -1
View File
@@ -8,7 +8,7 @@
import Foundation
open class SMBFileProvider: FileProvider, FileProviderMonitor {
class SMBFileProvider: FileProvider, FileProviderMonitor {
open static var type: String = "Samba"
open var isPathRelative: Bool = true
open var baseURL: URL?
+16
View File
@@ -12,10 +12,26 @@ protocol SMBRequest {
func data() -> Data
}
extension SMBRequest {
func data() -> Data {
return Data(value: self)
}
}
protocol SMBResponse {
init? (data: Data)
}
extension SMBResponse {
init? (data: Data) {
if let v: Self = data.scanValue() {
self = v
} else {
return nil
}
}
}
protocol IOCtlRequestProtocol: SMBRequest {}
protocol IOCtlResponseProtocol: SMBResponse {}
+7 -28
View File
@@ -40,7 +40,7 @@ extension SMB2 {
header.contextLength = 0
//result.appendData(nameData)
}
var result = encode(&header)
var result = Data(value: header)
result.append(body)
return result
}
@@ -186,7 +186,7 @@ extension SMB2 {
guard data.count >= MemoryLayout<CreateResponse.Header>.size else {
return nil
}
self.header = decode(data)
self.header = data.scanValue()!
if self.header.contextsOffset > 0 {
var contexts = [CreateContext]()
var contextOffset = Int(self.header.contextsOffset) - MemoryLayout<SMB2.Header>.size
@@ -195,14 +195,9 @@ extension SMB2 {
self.contexts = contexts
return
}
let contextDataHeader = data.subdata(in: contextOffset..<(contextOffset + MemoryLayout<CreateContext.Header>.size))
if let lastContextHeader = CreateContext(data: contextDataHeader) {
let lastContextLen = Int(lastContextHeader.header.dataOffset) + Int(lastContextHeader.header.dataLength) - contextOffset
let lastContextData = data.subdata(in: contextOffset..<(contextOffset + lastContextLen))
if let newContext = CreateContext(data: lastContextData) {
contexts.append(newContext)
}
contextOffset = Int(lastContextHeader.header.next) - MemoryLayout<SMB2.Header>.size
while contextOffset > 0, let context: CreateContext = data.scanValue(start: contextOffset) {
contexts.append(context)
contextOffset = Int(context.header.next) - MemoryLayout<SMB2.Header>.size
}
}
self.contexts = contexts
@@ -244,12 +239,12 @@ extension SMB2 {
guard data.count > headersize else {
return nil
}
self.header = decode(data)
self.header = data.scanValue()!
self.buffer = data.subdata(in: headersize..<data.count)
}
func data() -> Data {
var result = encode(header)
var result = Data(value: header)
result.append(buffer)
return result
}
@@ -377,10 +372,6 @@ extension SMB2 {
self.flags = []
self.reserved2 = 0
}
func data() -> Data {
return encode(self)
}
}
struct CloseResponse: SMBResponse {
@@ -394,10 +385,6 @@ extension SMB2 {
let allocationSize: UInt64
let endOfFile: UInt64
let fileAttributes: FileAttributes
init? (data: Data) {
self = decode(data)
}
}
struct CloseFlags: OptionSet {
@@ -426,10 +413,6 @@ extension SMB2 {
self.reserved = 0
self.reserved2 = 0
}
func data() -> Data {
return encode(self)
}
}
struct FlushResponse: SMBResponse {
@@ -440,9 +423,5 @@ extension SMB2 {
self.size = 4
self.reserved = 0
}
init? (data: Data) {
self = decode(data)
}
}
}
+4 -28
View File
@@ -43,10 +43,6 @@ extension SMB2 {
self.channelBuffer = 0
}
func data() -> Data {
return encode(read)
}
struct Flags: OptionSet {
let rawValue: UInt8
@@ -75,7 +71,7 @@ extension SMB2 {
guard data.count > 16 else {
return nil
}
self.header = decode(data)
self.header = data.scanValue()!
let headersize = MemoryLayout<Header>.size
self.buffer = data.subdata(in: headersize..<data.count)
}
@@ -124,7 +120,7 @@ extension SMB2 {
}
func data() -> Data {
var result = encode(self.header)
var result = Data(value: self.header)
if let channelInfo = channelInfo {
result.append(channelInfo.data())
}
@@ -151,20 +147,12 @@ extension SMB2 {
fileprivate let remaining: UInt32
fileprivate let channelInfoOffset: UInt16
fileprivate let channelInfoLength: UInt16
init?(data: Data) {
self = decode(data)
}
}
struct ChannelInfo: SMBRequest {
let offset: UInt64
let token: UInt32
let length: UInt32
func data() -> Data {
return encode(data)
}
}
// MARK: SMB2 Lock
@@ -175,10 +163,6 @@ extension SMB2 {
let flags: LockElement.Flags
fileprivate let reserved: UInt32
func data() -> Data {
return encode(self)
}
struct Flags: OptionSet {
let rawValue: UInt32
@@ -203,9 +187,9 @@ extension SMB2 {
}
func data() -> Data {
var result = encode(header)
var result = Data(value: header)
for lock in locks {
result.append(encode(lock))
result.append(Data(value: lock))
}
return result
}
@@ -226,10 +210,6 @@ extension SMB2 {
self.size = 4
self.reserved = 0
}
init? (data: Data) {
self = decode(data)
}
}
// MARK: SMB2 Cancel
@@ -242,9 +222,5 @@ extension SMB2 {
self.size = 4
self.reserved = 0
}
func data() -> Data {
return encode(self)
}
}
}
+18 -52
View File
@@ -26,7 +26,7 @@ extension SMB2 {
}
func data() -> Data {
var result = encode(self.header)
var result = Data(value: self.header)
if let reqData = requestData?.data() {
result.append(reqData)
}
@@ -68,7 +68,7 @@ extension SMB2 {
let responseData: IOCtlResponseProtocol?
init?(data: Data) {
self.header = decode(data)
self.header = data.scanValue()!
let endRange = Int(self.header.outputOffset - 64) + Int(self.header.outputCount)
let response = data.subdata(in: Int(self.header.outputOffset - 64)..<endRange)
switch self.header.ctlCode {
@@ -140,10 +140,10 @@ extension SMB2 {
let chunks: [Chunk]
func data() -> Data {
var result = encode(sourceKey)
result.append(encode(chunkCount))
var reserved: UInt32 = 0
result.append(encode(&reserved))
var result = Data(value: sourceKey)
result.append(Data(value: chunkCount))
let reserved: UInt32 = 0
result.append(Data(value: reserved))
return Data()
}
@@ -152,10 +152,6 @@ extension SMB2 {
let targetOffset: UInt64
let length: UInt32
fileprivate let reserved: UInt32
func data() -> Data {
return encode(self)
}
}
}
@@ -182,10 +178,6 @@ extension SMB2 {
self.length = length
self.offset = offset
}
func data() -> Data {
return encode(self)
}
}
struct ResilencyRequest: IOCtlRequestProtocol {
@@ -197,10 +189,6 @@ extension SMB2 {
self.timeout = timeout
self.reserved = 0
}
func data() -> Data {
return encode(self)
}
}
struct ValidateNegotiateInfo: IOCtlRequestProtocol {
@@ -213,8 +201,8 @@ extension SMB2 {
}
func data() -> Data {
var result = encode(self.header)
dialects.forEach { result.append(encode($0)) }
var result = Data(value: self.header)
dialects.forEach { result.append(Data(value: $0)) }
return result
}
@@ -234,10 +222,6 @@ extension SMB2 {
let chunksCount: UInt32
let chunksBytesWritten: UInt32
let totalBytesWriiten: UInt32
init?(data: Data) {
self = decode(data)
}
}
// SRV_ENUMERATE_SNAPSHOTS
@@ -247,8 +231,9 @@ extension SMB2 {
let snapshots: [SMBTime]
init?(data: Data) {
self.count = decode(data)
self.returnedCount = decode(data.subdata(in: 4..<8))
guard data.count > 8 else { return nil }
self.count = data.scanValue()!
self.returnedCount = data.scanValue(start: 4)!
//let size: UInt32 = decode(data.subdataWithRange(NSRange(location: 8, length: 4)))
var snapshots = [SMBTime]()
let dateFormatter = DateFormatter()
@@ -258,7 +243,7 @@ extension SMB2 {
if data.count < offset + 48 {
return nil
}
let datestring = String(data: data.subdata(in: offset..<(offset + 48)), encoding: .utf16)
let datestring = data.scanString(start: offset, length: 48, encoding: .utf16)
if let datestring = datestring, let date = dateFormatter.date(from: datestring) {
snapshots.append(SMBTime(date: date))
}
@@ -271,32 +256,21 @@ extension SMB2 {
let key: (UInt64, UInt64, UInt64)
fileprivate let contextLength: UInt32
fileprivate let context: UInt32
init?(data: Data) {
self = decode(data)
}
}
struct ReadHash: IOCtlResponseProtocol {
// TODO: Implement IOCTL READ_HASH
init?(data: Data) {
self = decode(data)
}
}
struct NetworkInterfaceInfo: IOCtlResponseProtocol {
let items: [NetworkInterfaceInfo.Item]
init?(data: Data) {
let count = data.count / MemoryLayout<Item>.size
guard count > 0 else {
return nil
}
var items = [Item]()
for i in 0..<count {
let itemdata = data.subdata(in: (i * MemoryLayout<Item>.size)..<((i + 1) * MemoryLayout<Item>.size))
items.append(decode(itemdata))
var offset = 0
while let item: Item = data.scanValue(start: offset) {
items.append(item)
offset += MemoryLayout<Item>.size
}
self.items = items
}
@@ -335,15 +309,11 @@ extension SMB2 {
static let ipv6: sa_family_t = 0x17
var sockaddr: sockaddr_in {
var sockaddrStorage = self.sockaddrStorage
let data = Data(bytes: &sockaddrStorage, count: 16)
return decode(data)
return Data.mapMemory(from: self.sockaddrStorage)!
}
var sockaddr6: sockaddr_in6 {
var sockaddrStorage = self.sockaddrStorage
let data = Data(bytes: &sockaddrStorage, count: 28)
return decode(data)
return Data.mapMemory(from: self.sockaddrStorage)!
}
}
}
@@ -356,10 +326,6 @@ extension SMB2 {
var dialect: (major: Int, minor: Int) {
return (major: Int(_dialect & 0xFF), minor: Int(_dialect >> 8))
}
init?(data: Data) {
self = decode(data)
}
}
}
+4 -13
View File
@@ -28,10 +28,6 @@ extension SMB2 {
self.reserved = 0
}
func data() -> Data {
return encode(self)
}
struct Flags: OptionSet {
let rawValue: UInt16
@@ -89,19 +85,14 @@ extension SMB2 {
var offset = 0
while i < maxLoop {
let nextOffsetData = data.subdata(in: offset..<(offset + 4))
let nextOffset: UInt32 = decode(nextOffsetData)
let actionData = data.subdata(in: (offset + 4)..<(offset + 8))
let actionValue: UInt32 = decode(actionData)
let nextOffset: UInt32 = data.scanValue(start: offset) ?? 0
let actionValue: UInt32 = data.scanValue(start: offset + 4) ?? 0
guard let action = FileNotifyAction(rawValue: actionValue) else {
continue
}
let fileLenData = data.subdata(in: (offset + 8)..<(offset + 12))
let fileNameLen = Int(decode(fileLenData) as UInt32)
let fileNameData = data.subdata(in: (offset + 12)..<(offset + 12 + fileNameLen))
let fileName = String(data: fileNameData, encoding: .utf16) ?? ""
let fileNameLen = Int(data.scanValue(start: offset + 8) as UInt32? ?? 0)
let fileName = data.scanString(start: offset + 12, length: fileNameLen, encoding: .utf16) ?? ""
result.append((action: action, fileName: fileName))
offset += Int(nextOffset)
+46 -73
View File
@@ -28,7 +28,7 @@ extension SMB2 {
}
func data() -> Data {
var result = encode(header)
var result = Data(value: header)
if let patternData = searchPattern?.data(using: .utf16) {
result.append(patternData)
}
@@ -68,43 +68,24 @@ extension SMB2 {
var result = [(header: SMB2FilesInformationHeader, fileName: String)]()
while true {
let header: SMB2FilesInformationHeader
let headersize: Int
switch type {
case .fileDirectoryInformation:
headersize = MemoryLayout<FileDirectoryInformationHeader>.size
let headerData = buffer.subdata(in: offset..<(offset + headersize))
let h: FileDirectoryInformationHeader = decode(headerData)
header = h
header = buffer.scanValue(start: offset) as FileDirectoryInformationHeader!
case .fileFullDirectoryInformation:
headersize = MemoryLayout<FileFullDirectoryInformationHeader>.size
let headerData = buffer.subdata(in: offset..<(offset + headersize))
let h: FileFullDirectoryInformationHeader = decode(headerData)
header = h
header = buffer.scanValue(start: offset) as FileFullDirectoryInformationHeader!
case .fileIdFullDirectoryInformation:
headersize = MemoryLayout<FileIdFullDirectoryInformationHeader>.size
let headerData = buffer.subdata(in: offset..<(offset + headersize))
let h: FileIdFullDirectoryInformationHeader = decode(headerData)
header = h
header = buffer.scanValue(start: offset) as FileIdFullDirectoryInformationHeader!
case .fileBothDirectoryInformation:
headersize = MemoryLayout<FileBothDirectoryInformationHeader>.size
let headerData = buffer.subdata(in: offset..<(offset + headersize))
let h: FileBothDirectoryInformationHeader = decode(headerData)
header = h
header = buffer.scanValue(start: offset) as FileBothDirectoryInformationHeader!
case .fileIdBothDirectoryInformation:
headersize = MemoryLayout<FileIdBothDirectoryInformationHeader>.size
let headerData = buffer.subdata(in: offset..<(offset + headersize))
let h: FileIdBothDirectoryInformationHeader = decode(headerData)
header = h
header = buffer.scanValue(start: offset) as FileIdBothDirectoryInformationHeader!
case .fileNamesInformation:
headersize = MemoryLayout<FileNamesInformationHeader>.size
let headerData = buffer.subdata(in: offset..<(offset + headersize))
let h: FileNamesInformationHeader = decode(headerData)
header = h
header = buffer.scanValue(start: offset) as FileNamesInformationHeader!
default:
return []
}
let fnData = buffer.subdata(in: (offset + headersize)..<(offset + headersize + Int(header.fileNameLength)))
let fileName = String(data: fnData, encoding: .utf16) ?? ""
let headersize = MemoryLayout.size(ofValue: header)
let fileName = buffer.scanString(start: headersize, length: Int(header.fileNameLength), encoding: .utf16) ?? ""
result.append((header: header, fileName: fileName))
if header.nextEntryOffset == 0 {
break
@@ -115,8 +96,8 @@ extension SMB2 {
}
init? (data: Data) {
let offset = Int(decode(data.subdata(in: 2..<4)) as UInt16)
let length = Int(decode(data.subdata(in: 4..<8)) as UInt32)
let offset = Int(data.scanValue(start: 2) as UInt16!)
let length = Int(data.scanValue(start: 4) as UInt32!)
guard data.count > offset + length else {
return nil
}
@@ -143,8 +124,8 @@ extension SMB2 {
}
let strLength = UInt8(strData.count)
let nextOffset = UInt32(4 + 1 + strData.count)
var data = encode(nextOffset)
data.append(encode(strLength))
var data = Data(value: nextOffset)
data.append(Data(value: strLength))
data.append(strData)
data.count += 1
let padSize = (data.count) % 4
@@ -170,8 +151,7 @@ extension SMB2 {
// TODO: Implement QUOTA_INFO init
func data() -> Data {
let headerData = encode(header)
var result = headerData
var result = Data(value: header)
if let buffer = buffer {
result.append(buffer)
}
@@ -208,8 +188,7 @@ extension SMB2 {
let buffer: Data
init?(data: Data) {
let structSizeData = data.subdata(in: 0..<2)
let structSize: UInt16 = decode(structSizeData)
let structSize: UInt16 = data.scanValue()!
guard structSize == 9 else {
return nil
}
@@ -217,10 +196,9 @@ extension SMB2 {
/*let offsetData = data.subdataWithRange(NSRange(location: 2, length: 2))
let offset: UInt16 = decode(offsetData)*/
let lengthData = data.subdata(in: 4..<8)
let length = Int(decode(lengthData) as UInt32)
let length = Int(data.scanValue(start: 4) as UInt32!)
guard data.count >= 8 + Int(length) else {
guard data.count >= 8 + length else {
return nil
}
@@ -228,40 +206,38 @@ extension SMB2 {
}
var asAccessInformation: FileAccessInformation {
return decode(buffer)
return buffer.scanValue()!
}
var asAlignmentInformation: FileAlignmentInformation {
return decode(buffer)
return buffer.scanValue()!
}
var asAllInformation: (header: FileAllInformationHeader, name: String) {
let header: FileAllInformationHeader = decode(buffer)
let header: FileAllInformationHeader = buffer.scanValue()!
let headersize = MemoryLayout<FileAllInformationHeader>.size
let nameData = buffer.subdata(in: headersize..<(headersize + Int(header.nameLength)))
let name = String(data: nameData, encoding: .utf16) ?? ""
let name = buffer.scanString(start: headersize, length: Int(header.nameLength), encoding: .utf16) ?? ""
return (header, name)
}
var asAlternateNameInformation: String {
let b = (buffer as NSData).bytes.bindMemory(to: CChar.self, capacity: buffer.count)
return String(cString: b, encoding: .utf16) ?? ""
return buffer.scanString(start: 0, length: buffer.count, encoding: .utf16) ?? ""
}
var asAttributeTagInformation: FileAttributeTagInformation {
return decode(buffer)
return buffer.scanValue()!
}
var asBasicInformation: FileBasicInformation {
return decode(buffer)
return buffer.scanValue()!
}
var asCompressionInformation: FileCompressionInformation {
return decode(buffer)
return buffer.scanValue()!
}
var asEaInformation: FileEaInformation {
return decode(buffer)
return buffer.scanValue()!
}
var asFullEaInformation: FileFullEaInformation {
@@ -270,83 +246,80 @@ extension SMB2 {
}
var asInternalInformation: FileInternalInformation {
return decode(buffer)
return buffer.scanValue()!
}
var asModeInformation: FileModeInformation {
return decode(buffer)
return buffer.scanValue()!
}
var asNetworkOpenInformation: FileNetworkOpenInformation {
return decode(buffer)
return buffer.scanValue()!
}
var asPipeInformation: FilePipeInformation {
return decode(buffer)
return buffer.scanValue()!
}
var asPipeLocalInformation: FilePipeLocalInformation {
return decode(buffer)
return buffer.scanValue()!
}
var asPipeRemoteInformation: FilePipeRemoteInformation {
return decode(buffer)
return buffer.scanValue()!
}
var asPositionInformation: FilePositionInformation {
return decode(buffer)
return buffer.scanValue()!
}
var asStandardInformation: FileStandardInformation {
return decode(buffer)
return buffer.scanValue()!
}
var asStreamInformation: (header: FileStreamInformationHeader, name: String) {
let header: FileStreamInformationHeader = decode(buffer)
let header: FileStreamInformationHeader = buffer.scanValue()!
let headersize = MemoryLayout<FileStreamInformationHeader>.size
let nameData = buffer.subdata(in: headersize..<(headersize + Int(header.streamNameLength)))
let name = String(data: nameData, encoding: .utf16) ?? ""
let name = buffer.scanString(start: headersize, length: Int(header.streamNameLength), encoding: .utf16) ?? ""
return (header, name)
}
var asFsVolumeInformation: (header: FileFsVolumeInformationHeader, name: String) {
let header: FileFsVolumeInformationHeader = decode(buffer)
let header: FileFsVolumeInformationHeader = buffer.scanValue()!
let headersize = MemoryLayout<FileFsVolumeInformationHeader>.size
let nameData = buffer.subdata(in: headersize..<(headersize + Int(header.labelLength)))
let name = String(data: nameData, encoding: .utf16) ?? ""
let name = buffer.scanString(start: headersize, length: Int(header.labelLength), encoding: .utf16) ?? ""
return (header, name)
}
var asFsSizeInformation: FileFsSizeInformation {
return decode(buffer)
return buffer.scanValue()!
}
var asFsDeviceInformation: FileFsDeviceInformation {
return decode(buffer)
return buffer.scanValue()!
}
var asFsAttributeInformation: (header: FileFsAttributeInformationHeader, name: String) {
let header: FileFsAttributeInformationHeader = decode(buffer)
let header: FileFsAttributeInformationHeader = buffer.scanValue()!
let headersize = MemoryLayout<FileFsAttributeInformationHeader>.size
let nameData = buffer.subdata(in: headersize..<(headersize + Int(header.nameLength)))
let name = String(data: nameData, encoding: .utf16) ?? ""
let name = buffer.scanString(start: headersize, length: Int(header.nameLength), encoding: .utf16) ?? ""
return (header, name)
}
var asFsControlInformation: FileFsControlInformation {
return decode(buffer)
return buffer.scanValue()!
}
var asFsFullSizeInformation: FileFsFullSizeInformation {
return decode(buffer)
return buffer.scanValue()!
}
var asFsObjectIdInformation: FileFsObjectIdInformation {
return decode(buffer)
return buffer.scanValue()!
}
var asFsSectorSizeInformation: FileFsSectorSizeInformation {
return decode(buffer)
return buffer.scanValue()!
}
}
}
+6 -32
View File
@@ -127,10 +127,6 @@ extension SMB2 {
let allocationSize: UInt64
let fileAttributes: FileAttributes
let fileNameLength : UInt32
init?(data: Data) {
self = decode(data)
}
}
struct FileFullDirectoryInformationHeader: SMB2FilesInformationHeader {
@@ -145,10 +141,6 @@ extension SMB2 {
let fileAttributes: FileAttributes
let fileNameLength : UInt32
let extendedAttributesSize: UInt32
init?(data: Data) {
self = decode(data)
}
}
struct FileIdFullDirectoryInformationHeader: SMB2FilesInformationHeader {
@@ -165,10 +157,6 @@ extension SMB2 {
let extendedAttributesSize: UInt32
fileprivate let reserved: UInt32
let fileId: FileId
init?(data: Data) {
self = decode(data)
}
}
struct FileBothDirectoryInformationHeader: SMB2FilesInformationHeader {
@@ -187,14 +175,9 @@ extension SMB2 {
fileprivate let reserved: UInt8
fileprivate let _shortName: FileShortNameType
var shortName: String? {
let s = encode(_shortName)
var d = s
d.count = Int(shortNameLen)
return String(data: d, encoding: .utf16)
}
init?(data: Data) {
self = decode(data)
var data = Data(value: _shortName)
data.count = Int(shortNameLen)
return String(data: data, encoding: .utf16)
}
}
@@ -214,27 +197,18 @@ extension SMB2 {
fileprivate let reserved: UInt8
fileprivate let _shortName: FileShortNameType
var shortName: String? {
let s = encode(_shortName)
var d = s
d.count = Int(shortNameLen)
return String(data: d, encoding: .utf16)
var data = Data(value: _shortName)
data.count = Int(shortNameLen)
return String(data: data, encoding: .utf16)
}
fileprivate let reserved2: UInt16
let fileId : FileId
init?(data: Data) {
self = decode(data)
}
}
struct FileNamesInformationHeader: SMB2FilesInformationHeader {
let nextEntryOffset: UInt32
let fileIndex: UInt32
let fileNameLength : UInt32
init?(data: Data) {
self = decode(data)
}
}
typealias FileShortNameType = (UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8,
+8 -26
View File
@@ -43,13 +43,11 @@ extension SMB2 {
var contextData = Data()
for context in contexts {
var contextType = context.type.rawValue
contextData.append(UnsafeBufferPointer(start: &contextType, count: 2))
var dataLen = UInt16(context.data.count)
contextData.append(Data(value: context.type.rawValue))
contextData.count += 4
contextData.append(UnsafeBufferPointer(start: &dataLen, count: 2))
contextData.append(Data(value: UInt16(context.data.count)))
}
var result = encode(&header)
var result = Data(value: header)
result.append(dialectData as Data)
result.append(contextData as Data)
return result
@@ -97,10 +95,10 @@ extension SMB2 {
let contexts: [(type: NegotiateContextType, data: Data)]
init? (data: Data) {
if data.count < 64 {
guard data.count >= 64 else {
return nil
}
self.header = decode(data)
self.header = data.scanValue()!
if Int(header.size) != 65 {
return nil
}
@@ -194,7 +192,7 @@ extension SMB2 {
var header = self.header
header.bufferOffset = UInt16(MemoryLayout<SMB2.Header>.size + MemoryLayout<SessionSetupRequest.Header>.size)
header.bufferLength = UInt16(buffer?.count ?? 0)
var result = encode(&header)
var result = Data(value: header)
if let buffer = self.buffer {
result.append(buffer)
}
@@ -240,10 +238,10 @@ extension SMB2 {
let buffer: Data?
init? (data: Data) {
if data.count < 64 {
guard data.count >= 64 else {
return nil
}
self.header = decode(data)
self.header = data.scanValue()!
if Int(header.size) != 9 {
return nil
}
@@ -297,14 +295,6 @@ extension SMB2 {
self.size = 4
self.reserved = 0
}
init? (data: Data) {
self = decode(data)
}
func data() -> Data {
return encode(self)
}
}
// MARK: SMB2 Echo
@@ -317,13 +307,5 @@ extension SMB2 {
self.size = 4
self.reserved = 0
}
init? (data: Data) {
self = decode(data)
}
func data() -> Data {
return encode(self)
}
}
}
+3 -7
View File
@@ -14,10 +14,10 @@ extension SMB2 {
let header: Header
let buffer: Data?
func data() -> Data {
return Data()
var result = Data(value: header)
result.append(buffer ?? Data())
return result
}
struct Header {
@@ -38,9 +38,5 @@ extension SMB2 {
init() {
self.size = 2
}
init? (data: Data) {
self = decode(data)
}
}
}
+1 -16
View File
@@ -34,7 +34,7 @@ extension SMB2 {
var header = self.header
header.pathOffset = UInt16(MemoryLayout<SMB2.Header>.size + MemoryLayout<TreeConnectRequest.Header>.size)
header.pathLength = UInt16(buffer?.count ?? 0)
var result = encode(&header)
var result = Data(value: header)
if let buffer = self.buffer {
result.append(buffer)
}
@@ -77,13 +77,6 @@ extension SMB2 {
let capabilities: TreeConnectResponse.Capabilities
let maximalAccess: FileAccessMask
init? (data: Data) {
if data.count != 16 {
return nil
}
self = decode(data)
}
enum ShareType: UInt8 {
case UNKNOWN = 0x00
case DISK = 0x01
@@ -139,13 +132,5 @@ extension SMB2 {
self.size = 4
self.reserved = 0
}
init? (data: Data) {
self = decode(data)
}
func data() -> Data {
return encode(self)
}
}
}
+25 -14
View File
@@ -8,22 +8,33 @@
import Foundation
internal func encode<T>(_ value: inout T) -> Data {
return withUnsafePointer(to: &value) { p in
Data(bytes: p, count: MemoryLayout.size(ofValue: value))
extension Data {
init<T>(value: T) {
var value = value
self = Data(buffer: UnsafeBufferPointer(start: &value, count: MemoryLayout.size(ofValue: value)))
}
}
internal func encode<T>(_ value: T) -> Data {
var value = value
return encode(&value)
}
internal func decode<T>(_ data: Data) -> T {
let pointer = UnsafeMutablePointer<T>.allocate(capacity: MemoryLayout<T.Type>.size)
(data as NSData).getBytes(pointer, length: MemoryLayout<T.Type>.size)
return pointer.move()
func scanValue<T>() -> T? {
guard MemoryLayout<T>.size <= self.count else { return nil }
return self.withUnsafeBytes { $0.pointee }
}
func scanValue<T>(start: Int) -> T? {
let length = MemoryLayout<T>.size
guard self.count >= start + length else { return nil }
return self.subdata(in: start..<start+length).withUnsafeBytes { $0.pointee }
}
func scanString(start: Int = 0, length: Int, encoding: String.Encoding) -> String? {
guard self.count >= start + length else { return nil }
return String(data: self.subdata(in: start..<start+length), encoding: encoding)
}
static func mapMemory<T, U>(from: T) -> U? {
guard MemoryLayout<T>.size >= MemoryLayout<U>.size else { return nil }
let data = Data(value: from)
return data.scanValue()
}
}
protocol FileProviderSMBHeader {
+1 -1
View File
@@ -10,7 +10,7 @@ import Foundation
/// Error Types and Description
public enum NTStatus: UInt32, Error, CustomStringConvertible {
enum NTStatus: UInt32, Error, CustomStringConvertible {
case SUCCESS = 0x00000000
case NOT_IMPLEMENTED = 0xC0000002
case INVALID_DEVICE_REQUEST = 0xC0000010
+78 -68
View File
@@ -64,6 +64,7 @@ open class WebDAVFileProvider: NSObject, FileProviderBasic {
}
open func contentsOfDirectory(path: String, completionHandler: @escaping ((_ contents: [FileObject], _ error: Error?) -> Void)) {
let opType = FileOperationType.fetch(path: path)
let url = absoluteURL(path)
var request = URLRequest(url: url)
request.httpMethod = "PROPFIND"
@@ -76,20 +77,19 @@ open class WebDAVFileProvider: NSObject, FileProviderBasic {
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.taskDescription = opType.json
task.resume()
}
@@ -132,16 +132,16 @@ open class WebDAVFileProvider: NSObject, FileProviderBasic {
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()
}
@@ -152,7 +152,8 @@ open class WebDAVFileProvider: NSObject, FileProviderBasic {
extension WebDAVFileProvider: FileProviderOperations {
@discardableResult
public func create(folder folderName: String, at atPath: String, completionHandler: SimpleCompletionHandler) -> OperationHandle? {
guard fileOperationDelegate?.fileProvider(self, shouldDoOperation: .create(path: (atPath as NSString).appendingPathComponent(folderName) + "/")) ?? true == true else {
let opType = FileOperationType.create(path: (atPath as NSString).appendingPathComponent(folderName) + "/")
guard fileOperationDelegate?.fileProvider(self, shouldDoOperation: opType) ?? true == true else {
return nil
}
let url = absoluteURL((atPath as NSString).appendingPathComponent(folderName) + "/")
@@ -163,20 +164,18 @@ extension WebDAVFileProvider: FileProviderOperations {
if let code = (response as? HTTPURLResponse)?.statusCode , code >= 300, let rCode = FileProviderHTTPErrorCode(rawValue: code) {
responseError = FileProviderWebDavError(code: rCode, url: url)
}
if let response = response as? HTTPURLResponse, let code = FileProviderHTTPErrorCode(rawValue: response.statusCode) , code != .ok {
completionHandler?(FileProviderWebDavError(code: code, url: url))
return
}
completionHandler?(responseError ?? error)
self.delegateNotify(.create(path: (atPath as NSString).appendingPathComponent(folderName) + "/"), error: responseError ?? error)
})
self.delegateNotify(opType, error: responseError ?? error)
})
task.taskDescription = opType.json
task.resume()
return RemoteOperationHandle(tasks: [task])
return RemoteOperationHandle(operationType: opType, tasks: [task])
}
@discardableResult
public func create(file fileAttribs: FileObject, at path: String, contents data: Data?, completionHandler: SimpleCompletionHandler) -> OperationHandle? {
guard fileOperationDelegate?.fileProvider(self, shouldDoOperation: .create(path: path)) ?? true == true else {
let opType = FileOperationType.create(path: (path as NSString).appendingPathComponent(fileAttribs.name))
guard fileOperationDelegate?.fileProvider(self, shouldDoOperation: opType) ?? true == true else {
return nil
}
let url = absoluteURL(path)
@@ -188,95 +187,101 @@ extension WebDAVFileProvider: FileProviderOperations {
responseError = FileProviderWebDavError(code: rCode, url: url)
}
completionHandler?(responseError ?? error)
self.delegateNotify(.create(path: (path as NSString).appendingPathComponent(fileAttribs.name)), error: responseError ?? error)
})
task.taskDescription = dictionaryToJSON(["type": "Create" as NSString, "source": (path as NSString).appendingPathComponent(fileAttribs.name) as NSString])
self.delegateNotify(opType, error: responseError ?? error)
})
task.taskDescription = opType.json
task.resume()
return RemoteOperationHandle(tasks: [task])
return RemoteOperationHandle(operationType: opType, tasks: [task])
}
@discardableResult
public func moveItem(path: String, to toPath: String, overwrite: Bool = false, completionHandler: SimpleCompletionHandler) -> OperationHandle? {
guard fileOperationDelegate?.fileProvider(self, shouldDoOperation: .move(source: path, destination: toPath)) ?? true == true else {
let opType = FileOperationType.move(source: path, destination: toPath)
guard fileOperationDelegate?.fileProvider(self, shouldDoOperation: opType) ?? true == true else {
return nil
}
return self.copyMoveItem(move: true, path: path, toPath: toPath, overwrite: overwrite, completionHandler: completionHandler)
return self.copyMoveItem(operation: opType, overwrite: overwrite, completionHandler: completionHandler)
}
@discardableResult
public func copyItem(path: String, to toPath: String, overwrite: Bool = false, completionHandler: SimpleCompletionHandler) -> OperationHandle? {
guard fileOperationDelegate?.fileProvider(self, shouldDoOperation: .copy(source: path, destination: toPath)) ?? true == true else {
let opType = FileOperationType.copy(source: path, destination: toPath)
guard fileOperationDelegate?.fileProvider(self, shouldDoOperation: opType) ?? true == true else {
return nil
}
return self.copyMoveItem(move: false, path: path, toPath: toPath, overwrite: overwrite, completionHandler: completionHandler)
return self.copyMoveItem(operation: opType, overwrite: overwrite, completionHandler: completionHandler)
}
fileprivate func copyMoveItem(move:Bool, path: String, toPath: String, overwrite: Bool, completionHandler: SimpleCompletionHandler) -> OperationHandle? {
let url = absoluteURL(path)
var request = URLRequest(url: url)
if move {
request.httpMethod = "MOVE"
} else {
request.httpMethod = "COPY"
}
request.setValue(absoluteURL(path).absoluteString, forHTTPHeaderField: "Destination")
fileprivate func copyMoveItem(operation opType: FileOperationType, overwrite: Bool, completionHandler: SimpleCompletionHandler) -> OperationHandle? {
guard let source = opType.source, let dest = opType.destination else { return nil }
// Using switch is more readable than using reflect. Maybe some
let sourceURL = absoluteURL(source)
var request = URLRequest(url: sourceURL)
request.httpMethod = opType.description.uppercased() // "COPY" or "MOVE"
request.setValue(absoluteURL(dest).absoluteString, forHTTPHeaderField: "Destination")
if !overwrite {
request.setValue("F", forHTTPHeaderField: "Overwrite")
}
let task = session.dataTask(with: request, completionHandler: { (data, response, error) in
var responseError: FileProviderWebDavError?
if let response = response as? HTTPURLResponse, let code = FileProviderHTTPErrorCode(rawValue: response.statusCode) {
defer {
let op = move ? FileOperationType.move(source: path, destination: toPath) : .copy(source: path, destination: toPath)
self.delegateNotify(op, error: error)
if response.statusCode >= 300 {
responseError = FileProviderWebDavError(code: code, url: sourceURL)
}
if code == .multiStatus, let data = data {
let xresponses = self.parseXMLResponse(data)
for xresponse in xresponses where (xresponse.status ?? 0) >= 300 {
completionHandler?(FileProviderWebDavError(code: code, url: url))
completionHandler?(FileProviderWebDavError(code: code, url: sourceURL))
}
} else {
completionHandler?(FileProviderWebDavError(code: code, url: url))
}
return
}
completionHandler?(error)
})
if (response as? HTTPURLResponse)?.statusCode ?? 0 != FileProviderHTTPErrorCode.multiStatus.rawValue {
completionHandler?(responseError ?? error)
}
self.delegateNotify(opType, error: responseError ?? error)
})
task.taskDescription = opType.json
task.resume()
return RemoteOperationHandle(tasks: [task])
return RemoteOperationHandle(operationType: opType, tasks: [task])
}
@discardableResult
public func removeItem(path: String, completionHandler: SimpleCompletionHandler) -> OperationHandle? {
guard fileOperationDelegate?.fileProvider(self, shouldDoOperation: .remove(path: path)) ?? true == true else {
let opType = FileOperationType.remove(path: path)
guard fileOperationDelegate?.fileProvider(self, shouldDoOperation: opType) ?? true == true else {
return nil
}
let url = absoluteURL(path)
var request = URLRequest(url: url)
request.httpMethod = "DELETE"
let task = session.dataTask(with: request, completionHandler: { (data, response, error) in
var responseError: FileProviderWebDavError?
if let response = response as? HTTPURLResponse, let code = FileProviderHTTPErrorCode(rawValue: response.statusCode) {
defer {
self.delegateNotify(.remove(path: path), error: error)
if response.statusCode >= 300 {
responseError = FileProviderWebDavError(code: code, url: url)
}
if code == .multiStatus, let data = data {
let xresponses = self.parseXMLResponse(data)
for xresponse in xresponses where (xresponse.status ?? 0) >= 300 {
completionHandler?(FileProviderWebDavError(code: code, url: url))
}
} else {
completionHandler?(FileProviderWebDavError(code: code, url: url))
}
return
}
completionHandler?(error)
})
if (response as? HTTPURLResponse)?.statusCode ?? 0 != FileProviderHTTPErrorCode.multiStatus.rawValue {
completionHandler?(responseError ?? error)
}
self.delegateNotify(opType, error: responseError ?? error)
})
task.taskDescription = opType.json
task.resume()
return RemoteOperationHandle(tasks: [task])
return RemoteOperationHandle(operationType: opType, tasks: [task])
}
@discardableResult
public func copyItem(localFile: URL, to toPath: String, completionHandler: SimpleCompletionHandler) -> OperationHandle? {
guard fileOperationDelegate?.fileProvider(self, shouldDoOperation: .copy(source: localFile.absoluteString, destination: toPath)) ?? true == true else {
let opType = FileOperationType.copy(source: localFile.absoluteString, destination: toPath)
guard fileOperationDelegate?.fileProvider(self, shouldDoOperation: opType) ?? true == true else {
return nil
}
let url = absoluteURL(toPath)
@@ -288,16 +293,17 @@ extension WebDAVFileProvider: FileProviderOperations {
responseError = FileProviderWebDavError(code: rCode, url: url)
}
completionHandler?(responseError ?? error)
self.delegateNotify(.move(source: localFile.absoluteString, destination: toPath), error: responseError ?? error)
self.delegateNotify(opType, error: responseError ?? error)
})
task.taskDescription = dictionaryToJSON(["type": "Copy" as NSString, "source": localFile.absoluteString as NSString, "dest": toPath as NSString])
task.taskDescription = opType.json
task.resume()
return RemoteOperationHandle(tasks: [task])
return RemoteOperationHandle(operationType: opType, tasks: [task])
}
@discardableResult
public func copyItem(path: String, toLocalURL: URL, completionHandler: SimpleCompletionHandler) -> OperationHandle? {
guard fileOperationDelegate?.fileProvider(self, shouldDoOperation: .copy(source: path, destination: toLocalURL.absoluteString)) ?? true == true else {
let opType = FileOperationType.copy(source: path, destination: toLocalURL.absoluteString)
guard fileOperationDelegate?.fileProvider(self, shouldDoOperation: opType) ?? true == true else {
return nil
}
let url = absoluteURL(path)
@@ -316,10 +322,11 @@ extension WebDAVFileProvider: FileProviderOperations {
}
}
completionHandler?(responseError ?? error)
self.delegateNotify(opType, error: responseError ?? error)
})
task.taskDescription = dictionaryToJSON(["type": "Copy" as NSString, "source": path as NSString, "dest": toLocalURL.absoluteString as NSString])
task.taskDescription = opType.json
task.resume()
return RemoteOperationHandle(tasks: [task])
return RemoteOperationHandle(operationType: opType, tasks: [task])
}
}
@@ -331,6 +338,7 @@ extension WebDAVFileProvider: FileProviderReadWrite {
@discardableResult
public func contents(path: String, offset: Int64, length: Int, completionHandler: @escaping ((_ contents: Data?, _ error: Error?) -> Void)) -> OperationHandle? {
let opType = FileOperationType.fetch(path: path)
let url = absoluteURL(path)
var request = URLRequest(url: url)
request.httpMethod = "GET"
@@ -345,14 +353,16 @@ extension WebDAVFileProvider: FileProviderReadWrite {
responseError = FileProviderWebDavError(code: rCode, url: url)
}
completionHandler(data, responseError ?? error)
})
})
task.taskDescription = opType.json
task.resume()
return RemoteOperationHandle(tasks: [task])
return RemoteOperationHandle(operationType: opType, tasks: [task])
}
@discardableResult
public func writeContents(path: String, contents data: Data, atomically: Bool = false, completionHandler: SimpleCompletionHandler) -> OperationHandle? {
guard fileOperationDelegate?.fileProvider(self, shouldDoOperation: .modify(path: path)) ?? true == true else {
let opType = FileOperationType.modify(path: path)
guard fileOperationDelegate?.fileProvider(self, shouldDoOperation: opType) ?? true == true else {
return nil
}
// FIXME: lock destination before writing process
@@ -365,7 +375,7 @@ extension WebDAVFileProvider: FileProviderReadWrite {
responseError = FileProviderWebDavError(code: rCode, url: self.absoluteURL(path))
}
defer {
self.delegateNotify(.modify(path: path), error: responseError ?? error)
self.delegateNotify(opType, error: responseError ?? error)
}
if let error = error {
completionHandler?(error)
@@ -375,9 +385,9 @@ extension WebDAVFileProvider: FileProviderReadWrite {
self.moveItem(path: (path as NSString).appendingPathExtension("tmp")!, to: path, completionHandler: completionHandler)
}
})
task.taskDescription = dictionaryToJSON(["type": "Modify" as NSString, "source": path as NSString])
task.taskDescription = opType.json
task.resume()
return RemoteOperationHandle(tasks: [task])
return RemoteOperationHandle(operationType: opType, tasks: [task])
}
public func searchFiles(path: String, recursive: Bool, query: String, foundItemHandler: ((FileObject) -> Void)?, completionHandler: @escaping ((_ files: [FileObject], _ error: Error?) -> Void)) {