Compare commits

...

12 Commits

Author SHA1 Message Date
Amir Abbas Mousavian e6a450e3e2 tvOS support 2016-07-27 15:02:14 +04:30
Amir Abbas Mousavian 0fd6da3658 Cocoapod project added 2016-07-27 14:56:33 +04:30
Amir Abbas Mousavian 67b6cf64e2 insert slash in the beginning of path 2016-07-26 10:34:34 +04:30
Amir Abbas Mousavian 3ab252e598 WebDav refactors and Dropbox partial implementation 2016-07-25 19:18:13 +04:30
Amir Abbas Mousavian 09f9909363 spliced SMB2 Types into several files 2016-07-21 00:40:21 +04:30
Amir Abbas Mousavian c6d069cb46 Added Write and IOCtl commands' types 2016-07-20 17:59:25 +04:30
Amir Abbas Mousavian 0004bd4c90 update podspec and silent compiler hints 2016-07-15 16:09:46 +04:30
Amir Abbas Mousavian bf4337bcb3 Updated Readme 2016-07-15 16:00:14 +04:30
Amir Abbas Mousavian 3301d2c004 Bug fixes for path handling and LocalFolderMonitor
- Bugs in fileByUniqueName(), relativePath() and absoluteURL fixed
- Introduced delay in LocalFolderMonitor to prevent excess handler calling
2016-07-13 13:30:32 +04:30
Amir Abbas Mousavian f0cd7846d8 FileOpreationDelegate, delegate strong reference bug, fileByUniqueName method 2016-07-11 13:31:22 +04:30
Amir Abbas Mousavian 8fd7da669d Readme.md error 2016-07-06 21:34:54 +04:30
Amir Abbas Mousavian 13a68c84f8 Readme.md update for cocoapods 2016-07-06 21:32:14 +04:30
32 changed files with 4344 additions and 1956 deletions
+6 -6
View File
@@ -16,8 +16,8 @@ Pod::Spec.new do |s|
#
s.name = "FileProvider"
s.version = "0.1.0"
s.summary = "Extended Local/WebDAV/SMB/CIFS/etc. File Manager for Swift on iOS and MacOS."
s.version = "0.3.0"
s.summary = "NSFileManager 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?
@@ -67,13 +67,13 @@ Pod::Spec.new do |s|
#
# s.platform = :ios
# s.platform = :ios, "7.0"
# s.platform = :ios, "8.0"
# When using multiple platforms
s.ios.deployment_target = "8.0"
s.osx.deployment_target = "10.10"
# s.watchos.deployment_target = "2.0"
# s.tvos.deployment_target = "9.0"
s.tvos.deployment_target = "9.0"
# ――― Source Location ―――――――――――――――――――――――――――――――――――――――――――――――――――――――――― #
@@ -93,8 +93,8 @@ Pod::Spec.new do |s|
# Not including the public_header_files will make all headers public.
#
s.source_files = "Source/*.swift"
s.exclude_files = "Source/Exclude"
s.source_files = "Sources/*.swift"
s.exclude_files = "Sources/Exclude"
# s.public_header_files = "Classes/**/*.h"
+801
View File
@@ -0,0 +1,801 @@
// !$*UTF8*$!
{
archiveVersion = 1;
classes = {
};
objectVersion = 46;
objects = {
/* Begin PBXBuildFile section */
799396A71D48C02300086753 /* AEXML.swift in Sources */ = {isa = PBXBuildFile; fileRef = 799396921D48C02300086753 /* AEXML.swift */; };
799396A81D48C02300086753 /* AEXML.swift in Sources */ = {isa = PBXBuildFile; fileRef = 799396921D48C02300086753 /* AEXML.swift */; };
799396A91D48C02300086753 /* AEXML.swift in Sources */ = {isa = PBXBuildFile; fileRef = 799396921D48C02300086753 /* AEXML.swift */; };
799396AA1D48C02300086753 /* DropboxFileProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = 799396931D48C02300086753 /* DropboxFileProvider.swift */; };
799396AB1D48C02300086753 /* DropboxFileProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = 799396931D48C02300086753 /* DropboxFileProvider.swift */; };
799396AC1D48C02300086753 /* DropboxFileProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = 799396931D48C02300086753 /* DropboxFileProvider.swift */; };
799396B01D48C02300086753 /* FileProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = 799396951D48C02300086753 /* FileProvider.swift */; };
799396B11D48C02300086753 /* FileProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = 799396951D48C02300086753 /* FileProvider.swift */; };
799396B21D48C02300086753 /* FileProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = 799396951D48C02300086753 /* FileProvider.swift */; };
799396B31D48C02300086753 /* LocalFileProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = 799396961D48C02300086753 /* LocalFileProvider.swift */; };
799396B41D48C02300086753 /* LocalFileProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = 799396961D48C02300086753 /* LocalFileProvider.swift */; };
799396B51D48C02300086753 /* LocalFileProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = 799396961D48C02300086753 /* LocalFileProvider.swift */; };
799396B61D48C02300086753 /* SMBClient.swift in Sources */ = {isa = PBXBuildFile; fileRef = 799396971D48C02300086753 /* SMBClient.swift */; };
799396B71D48C02300086753 /* SMBClient.swift in Sources */ = {isa = PBXBuildFile; fileRef = 799396971D48C02300086753 /* SMBClient.swift */; };
799396B81D48C02300086753 /* SMBClient.swift in Sources */ = {isa = PBXBuildFile; fileRef = 799396971D48C02300086753 /* SMBClient.swift */; };
799396B91D48C02300086753 /* SMBFileProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = 799396981D48C02300086753 /* SMBFileProvider.swift */; };
799396BA1D48C02300086753 /* SMBFileProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = 799396981D48C02300086753 /* SMBFileProvider.swift */; };
799396BB1D48C02300086753 /* SMBFileProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = 799396981D48C02300086753 /* SMBFileProvider.swift */; };
799396BC1D48C02300086753 /* CIFSTypes.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7993969A1D48C02300086753 /* CIFSTypes.swift */; };
799396BD1D48C02300086753 /* CIFSTypes.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7993969A1D48C02300086753 /* CIFSTypes.swift */; };
799396BE1D48C02300086753 /* CIFSTypes.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7993969A1D48C02300086753 /* CIFSTypes.swift */; };
799396BF1D48C02300086753 /* SMB2DataTypes.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7993969B1D48C02300086753 /* SMB2DataTypes.swift */; };
799396C01D48C02300086753 /* SMB2DataTypes.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7993969B1D48C02300086753 /* SMB2DataTypes.swift */; };
799396C11D48C02300086753 /* SMB2DataTypes.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7993969B1D48C02300086753 /* SMB2DataTypes.swift */; };
799396C21D48C02300086753 /* SMB2FileHandle.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7993969C1D48C02300086753 /* SMB2FileHandle.swift */; };
799396C31D48C02300086753 /* SMB2FileHandle.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7993969C1D48C02300086753 /* SMB2FileHandle.swift */; };
799396C41D48C02300086753 /* SMB2FileHandle.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7993969C1D48C02300086753 /* SMB2FileHandle.swift */; };
799396C51D48C02300086753 /* SMB2FileOperation.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7993969D1D48C02300086753 /* SMB2FileOperation.swift */; };
799396C61D48C02300086753 /* SMB2FileOperation.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7993969D1D48C02300086753 /* SMB2FileOperation.swift */; };
799396C71D48C02300086753 /* SMB2FileOperation.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7993969D1D48C02300086753 /* SMB2FileOperation.swift */; };
799396C81D48C02300086753 /* SMB2IOCtl.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7993969E1D48C02300086753 /* SMB2IOCtl.swift */; };
799396C91D48C02300086753 /* SMB2IOCtl.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7993969E1D48C02300086753 /* SMB2IOCtl.swift */; };
799396CA1D48C02300086753 /* SMB2IOCtl.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7993969E1D48C02300086753 /* SMB2IOCtl.swift */; };
799396CB1D48C02300086753 /* SMB2Query.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7993969F1D48C02300086753 /* SMB2Query.swift */; };
799396CC1D48C02300086753 /* SMB2Query.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7993969F1D48C02300086753 /* SMB2Query.swift */; };
799396CD1D48C02300086753 /* SMB2Query.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7993969F1D48C02300086753 /* SMB2Query.swift */; };
799396CE1D48C02300086753 /* SMB2Session.swift in Sources */ = {isa = PBXBuildFile; fileRef = 799396A01D48C02300086753 /* SMB2Session.swift */; };
799396CF1D48C02300086753 /* SMB2Session.swift in Sources */ = {isa = PBXBuildFile; fileRef = 799396A01D48C02300086753 /* SMB2Session.swift */; };
799396D01D48C02300086753 /* SMB2Session.swift in Sources */ = {isa = PBXBuildFile; fileRef = 799396A01D48C02300086753 /* SMB2Session.swift */; };
799396D11D48C02300086753 /* SMB2SetInfo.swift in Sources */ = {isa = PBXBuildFile; fileRef = 799396A11D48C02300086753 /* SMB2SetInfo.swift */; };
799396D21D48C02300086753 /* SMB2SetInfo.swift in Sources */ = {isa = PBXBuildFile; fileRef = 799396A11D48C02300086753 /* SMB2SetInfo.swift */; };
799396D31D48C02300086753 /* SMB2SetInfo.swift in Sources */ = {isa = PBXBuildFile; fileRef = 799396A11D48C02300086753 /* SMB2SetInfo.swift */; };
799396D41D48C02300086753 /* SMB2Tree.swift in Sources */ = {isa = PBXBuildFile; fileRef = 799396A21D48C02300086753 /* SMB2Tree.swift */; };
799396D51D48C02300086753 /* SMB2Tree.swift in Sources */ = {isa = PBXBuildFile; fileRef = 799396A21D48C02300086753 /* SMB2Tree.swift */; };
799396D61D48C02300086753 /* SMB2Tree.swift in Sources */ = {isa = PBXBuildFile; fileRef = 799396A21D48C02300086753 /* SMB2Tree.swift */; };
799396D71D48C02300086753 /* SMB2Types.swift in Sources */ = {isa = PBXBuildFile; fileRef = 799396A31D48C02300086753 /* SMB2Types.swift */; };
799396D81D48C02300086753 /* SMB2Types.swift in Sources */ = {isa = PBXBuildFile; fileRef = 799396A31D48C02300086753 /* SMB2Types.swift */; };
799396D91D48C02300086753 /* SMB2Types.swift in Sources */ = {isa = PBXBuildFile; fileRef = 799396A31D48C02300086753 /* SMB2Types.swift */; };
799396DA1D48C02300086753 /* SMBErrorType.swift in Sources */ = {isa = PBXBuildFile; fileRef = 799396A41D48C02300086753 /* SMBErrorType.swift */; };
799396DB1D48C02300086753 /* SMBErrorType.swift in Sources */ = {isa = PBXBuildFile; fileRef = 799396A41D48C02300086753 /* SMBErrorType.swift */; };
799396DC1D48C02300086753 /* SMBErrorType.swift in Sources */ = {isa = PBXBuildFile; fileRef = 799396A41D48C02300086753 /* SMBErrorType.swift */; };
799396DD1D48C02300086753 /* TCPSocketClient.swift in Sources */ = {isa = PBXBuildFile; fileRef = 799396A51D48C02300086753 /* TCPSocketClient.swift */; };
799396DE1D48C02300086753 /* TCPSocketClient.swift in Sources */ = {isa = PBXBuildFile; fileRef = 799396A51D48C02300086753 /* TCPSocketClient.swift */; };
799396DF1D48C02300086753 /* TCPSocketClient.swift in Sources */ = {isa = PBXBuildFile; fileRef = 799396A51D48C02300086753 /* TCPSocketClient.swift */; };
799396E01D48C02300086753 /* WebDAVFileProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = 799396A61D48C02300086753 /* WebDAVFileProvider.swift */; };
799396E11D48C02300086753 /* WebDAVFileProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = 799396A61D48C02300086753 /* WebDAVFileProvider.swift */; };
799396E21D48C02300086753 /* WebDAVFileProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = 799396A61D48C02300086753 /* WebDAVFileProvider.swift */; };
/* End PBXBuildFile section */
/* Begin PBXFileReference section */
799396671D48B7F600086753 /* FileProvider.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = FileProvider.framework; sourceTree = BUILT_PRODUCTS_DIR; };
799396751D48B80D00086753 /* FileProvider.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = FileProvider.framework; sourceTree = BUILT_PRODUCTS_DIR; };
799396821D48B82700086753 /* FileProvider.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = FileProvider.framework; sourceTree = BUILT_PRODUCTS_DIR; };
7993968B1D48B8C700086753 /* Info-iOS.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = "Info-iOS.plist"; sourceTree = "<group>"; };
7993968C1D48B8C700086753 /* Info-MacOS.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = "Info-MacOS.plist"; sourceTree = "<group>"; };
7993968D1D48B8C700086753 /* Info-tvOS.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = "Info-tvOS.plist"; sourceTree = "<group>"; };
799396921D48C02300086753 /* AEXML.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AEXML.swift; sourceTree = "<group>"; };
799396931D48C02300086753 /* DropboxFileProvider.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DropboxFileProvider.swift; sourceTree = "<group>"; };
799396941D48C02300086753 /* FileProvider.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FileProvider.h; sourceTree = "<group>"; };
799396951D48C02300086753 /* FileProvider.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = FileProvider.swift; sourceTree = "<group>"; };
799396961D48C02300086753 /* LocalFileProvider.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = LocalFileProvider.swift; sourceTree = "<group>"; };
799396971D48C02300086753 /* SMBClient.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SMBClient.swift; sourceTree = "<group>"; };
799396981D48C02300086753 /* SMBFileProvider.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SMBFileProvider.swift; sourceTree = "<group>"; };
7993969A1D48C02300086753 /* CIFSTypes.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CIFSTypes.swift; sourceTree = "<group>"; };
7993969B1D48C02300086753 /* SMB2DataTypes.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SMB2DataTypes.swift; sourceTree = "<group>"; };
7993969C1D48C02300086753 /* SMB2FileHandle.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SMB2FileHandle.swift; sourceTree = "<group>"; };
7993969D1D48C02300086753 /* SMB2FileOperation.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SMB2FileOperation.swift; sourceTree = "<group>"; };
7993969E1D48C02300086753 /* SMB2IOCtl.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SMB2IOCtl.swift; sourceTree = "<group>"; };
7993969F1D48C02300086753 /* SMB2Query.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SMB2Query.swift; sourceTree = "<group>"; };
799396A01D48C02300086753 /* SMB2Session.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SMB2Session.swift; sourceTree = "<group>"; };
799396A11D48C02300086753 /* SMB2SetInfo.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SMB2SetInfo.swift; sourceTree = "<group>"; };
799396A21D48C02300086753 /* SMB2Tree.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SMB2Tree.swift; sourceTree = "<group>"; };
799396A31D48C02300086753 /* SMB2Types.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SMB2Types.swift; sourceTree = "<group>"; };
799396A41D48C02300086753 /* SMBErrorType.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SMBErrorType.swift; sourceTree = "<group>"; };
799396A51D48C02300086753 /* TCPSocketClient.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TCPSocketClient.swift; sourceTree = "<group>"; };
799396A61D48C02300086753 /* WebDAVFileProvider.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = WebDAVFileProvider.swift; sourceTree = "<group>"; };
/* End PBXFileReference section */
/* Begin PBXFrameworksBuildPhase section */
799396631D48B7F600086753 /* Frameworks */ = {
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
files = (
);
runOnlyForDeploymentPostprocessing = 0;
};
799396711D48B80D00086753 /* Frameworks */ = {
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
files = (
);
runOnlyForDeploymentPostprocessing = 0;
};
7993967E1D48B82700086753 /* Frameworks */ = {
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
files = (
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXFrameworksBuildPhase section */
/* Begin PBXGroup section */
7993965B1D48B7BF00086753 = {
isa = PBXGroup;
children = (
799396911D48C02300086753 /* Sources */,
7993968A1D48B8C700086753 /* Pod */,
799396681D48B7F600086753 /* Products */,
);
sourceTree = "<group>";
};
799396681D48B7F600086753 /* Products */ = {
isa = PBXGroup;
children = (
799396671D48B7F600086753 /* FileProvider.framework */,
799396751D48B80D00086753 /* FileProvider.framework */,
799396821D48B82700086753 /* FileProvider.framework */,
);
name = Products;
sourceTree = "<group>";
};
7993968A1D48B8C700086753 /* Pod */ = {
isa = PBXGroup;
children = (
7993968B1D48B8C700086753 /* Info-iOS.plist */,
7993968C1D48B8C700086753 /* Info-MacOS.plist */,
7993968D1D48B8C700086753 /* Info-tvOS.plist */,
);
path = Pod;
sourceTree = "<group>";
};
799396911D48C02300086753 /* Sources */ = {
isa = PBXGroup;
children = (
799396991D48C02300086753 /* SMBTypes */,
799396921D48C02300086753 /* AEXML.swift */,
799396931D48C02300086753 /* DropboxFileProvider.swift */,
799396941D48C02300086753 /* FileProvider.h */,
799396951D48C02300086753 /* FileProvider.swift */,
799396961D48C02300086753 /* LocalFileProvider.swift */,
799396971D48C02300086753 /* SMBClient.swift */,
799396981D48C02300086753 /* SMBFileProvider.swift */,
799396A51D48C02300086753 /* TCPSocketClient.swift */,
799396A61D48C02300086753 /* WebDAVFileProvider.swift */,
);
path = Sources;
sourceTree = "<group>";
};
799396991D48C02300086753 /* SMBTypes */ = {
isa = PBXGroup;
children = (
7993969A1D48C02300086753 /* CIFSTypes.swift */,
7993969B1D48C02300086753 /* SMB2DataTypes.swift */,
7993969C1D48C02300086753 /* SMB2FileHandle.swift */,
7993969D1D48C02300086753 /* SMB2FileOperation.swift */,
7993969E1D48C02300086753 /* SMB2IOCtl.swift */,
7993969F1D48C02300086753 /* SMB2Query.swift */,
799396A01D48C02300086753 /* SMB2Session.swift */,
799396A11D48C02300086753 /* SMB2SetInfo.swift */,
799396A21D48C02300086753 /* SMB2Tree.swift */,
799396A31D48C02300086753 /* SMB2Types.swift */,
799396A41D48C02300086753 /* SMBErrorType.swift */,
);
path = SMBTypes;
sourceTree = "<group>";
};
/* End PBXGroup section */
/* Begin PBXHeadersBuildPhase section */
799396641D48B7F600086753 /* Headers */ = {
isa = PBXHeadersBuildPhase;
buildActionMask = 2147483647;
files = (
);
runOnlyForDeploymentPostprocessing = 0;
};
799396721D48B80D00086753 /* Headers */ = {
isa = PBXHeadersBuildPhase;
buildActionMask = 2147483647;
files = (
);
runOnlyForDeploymentPostprocessing = 0;
};
7993967F1D48B82700086753 /* Headers */ = {
isa = PBXHeadersBuildPhase;
buildActionMask = 2147483647;
files = (
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXHeadersBuildPhase section */
/* Begin PBXNativeTarget section */
799396661D48B7F600086753 /* FileProvider iOS */ = {
isa = PBXNativeTarget;
buildConfigurationList = 7993966F1D48B7F600086753 /* Build configuration list for PBXNativeTarget "FileProvider iOS" */;
buildPhases = (
799396621D48B7F600086753 /* Sources */,
799396631D48B7F600086753 /* Frameworks */,
799396641D48B7F600086753 /* Headers */,
799396651D48B7F600086753 /* Resources */,
);
buildRules = (
);
dependencies = (
);
name = "FileProvider iOS";
productName = "FileProvider iOS";
productReference = 799396671D48B7F600086753 /* FileProvider.framework */;
productType = "com.apple.product-type.framework";
};
799396741D48B80D00086753 /* FileProvider OSX */ = {
isa = PBXNativeTarget;
buildConfigurationList = 7993967A1D48B80D00086753 /* Build configuration list for PBXNativeTarget "FileProvider OSX" */;
buildPhases = (
799396701D48B80D00086753 /* Sources */,
799396711D48B80D00086753 /* Frameworks */,
799396721D48B80D00086753 /* Headers */,
799396731D48B80D00086753 /* Resources */,
);
buildRules = (
);
dependencies = (
);
name = "FileProvider OSX";
productName = "FileProvider OSX";
productReference = 799396751D48B80D00086753 /* FileProvider.framework */;
productType = "com.apple.product-type.framework";
};
799396811D48B82700086753 /* FileProvider tvOS */ = {
isa = PBXNativeTarget;
buildConfigurationList = 799396871D48B82700086753 /* Build configuration list for PBXNativeTarget "FileProvider tvOS" */;
buildPhases = (
7993967D1D48B82700086753 /* Sources */,
7993967E1D48B82700086753 /* Frameworks */,
7993967F1D48B82700086753 /* Headers */,
799396801D48B82700086753 /* Resources */,
);
buildRules = (
);
dependencies = (
);
name = "FileProvider tvOS";
productName = "FileProvider tvOS";
productReference = 799396821D48B82700086753 /* FileProvider.framework */;
productType = "com.apple.product-type.framework";
};
/* End PBXNativeTarget section */
/* Begin PBXProject section */
7993965C1D48B7BF00086753 /* Project object */ = {
isa = PBXProject;
attributes = {
LastUpgradeCheck = 0730;
TargetAttributes = {
799396661D48B7F600086753 = {
CreatedOnToolsVersion = 7.3.1;
};
799396741D48B80D00086753 = {
CreatedOnToolsVersion = 7.3.1;
};
799396811D48B82700086753 = {
CreatedOnToolsVersion = 7.3.1;
};
};
};
buildConfigurationList = 7993965F1D48B7BF00086753 /* Build configuration list for PBXProject "FileProvider" */;
compatibilityVersion = "Xcode 3.2";
developmentRegion = English;
hasScannedForEncodings = 0;
knownRegions = (
en,
);
mainGroup = 7993965B1D48B7BF00086753;
productRefGroup = 799396681D48B7F600086753 /* Products */;
projectDirPath = "";
projectRoot = "";
targets = (
799396661D48B7F600086753 /* FileProvider iOS */,
799396741D48B80D00086753 /* FileProvider OSX */,
799396811D48B82700086753 /* FileProvider tvOS */,
);
};
/* End PBXProject section */
/* Begin PBXResourcesBuildPhase section */
799396651D48B7F600086753 /* Resources */ = {
isa = PBXResourcesBuildPhase;
buildActionMask = 2147483647;
files = (
);
runOnlyForDeploymentPostprocessing = 0;
};
799396731D48B80D00086753 /* Resources */ = {
isa = PBXResourcesBuildPhase;
buildActionMask = 2147483647;
files = (
);
runOnlyForDeploymentPostprocessing = 0;
};
799396801D48B82700086753 /* Resources */ = {
isa = PBXResourcesBuildPhase;
buildActionMask = 2147483647;
files = (
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXResourcesBuildPhase section */
/* Begin PBXSourcesBuildPhase section */
799396621D48B7F600086753 /* Sources */ = {
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
files = (
799396B31D48C02300086753 /* LocalFileProvider.swift in Sources */,
799396D41D48C02300086753 /* SMB2Tree.swift in Sources */,
799396C81D48C02300086753 /* SMB2IOCtl.swift in Sources */,
799396DD1D48C02300086753 /* TCPSocketClient.swift in Sources */,
799396D71D48C02300086753 /* SMB2Types.swift in Sources */,
799396C51D48C02300086753 /* SMB2FileOperation.swift in Sources */,
799396BF1D48C02300086753 /* SMB2DataTypes.swift in Sources */,
799396B91D48C02300086753 /* SMBFileProvider.swift in Sources */,
799396BC1D48C02300086753 /* CIFSTypes.swift in Sources */,
799396D11D48C02300086753 /* SMB2SetInfo.swift in Sources */,
799396A71D48C02300086753 /* AEXML.swift in Sources */,
799396CE1D48C02300086753 /* SMB2Session.swift in Sources */,
799396E01D48C02300086753 /* WebDAVFileProvider.swift in Sources */,
799396DA1D48C02300086753 /* SMBErrorType.swift in Sources */,
799396C21D48C02300086753 /* SMB2FileHandle.swift in Sources */,
799396CB1D48C02300086753 /* SMB2Query.swift in Sources */,
799396AA1D48C02300086753 /* DropboxFileProvider.swift in Sources */,
799396B01D48C02300086753 /* FileProvider.swift in Sources */,
799396B61D48C02300086753 /* SMBClient.swift in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
799396701D48B80D00086753 /* Sources */ = {
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
files = (
799396B41D48C02300086753 /* LocalFileProvider.swift in Sources */,
799396D51D48C02300086753 /* SMB2Tree.swift in Sources */,
799396C91D48C02300086753 /* SMB2IOCtl.swift in Sources */,
799396DE1D48C02300086753 /* TCPSocketClient.swift in Sources */,
799396D81D48C02300086753 /* SMB2Types.swift in Sources */,
799396C61D48C02300086753 /* SMB2FileOperation.swift in Sources */,
799396C01D48C02300086753 /* SMB2DataTypes.swift in Sources */,
799396BA1D48C02300086753 /* SMBFileProvider.swift in Sources */,
799396BD1D48C02300086753 /* CIFSTypes.swift in Sources */,
799396D21D48C02300086753 /* SMB2SetInfo.swift in Sources */,
799396A81D48C02300086753 /* AEXML.swift in Sources */,
799396CF1D48C02300086753 /* SMB2Session.swift in Sources */,
799396E11D48C02300086753 /* WebDAVFileProvider.swift in Sources */,
799396DB1D48C02300086753 /* SMBErrorType.swift in Sources */,
799396C31D48C02300086753 /* SMB2FileHandle.swift in Sources */,
799396CC1D48C02300086753 /* SMB2Query.swift in Sources */,
799396AB1D48C02300086753 /* DropboxFileProvider.swift in Sources */,
799396B11D48C02300086753 /* FileProvider.swift in Sources */,
799396B71D48C02300086753 /* SMBClient.swift in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
7993967D1D48B82700086753 /* Sources */ = {
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
files = (
799396B51D48C02300086753 /* LocalFileProvider.swift in Sources */,
799396D61D48C02300086753 /* SMB2Tree.swift in Sources */,
799396CA1D48C02300086753 /* SMB2IOCtl.swift in Sources */,
799396DF1D48C02300086753 /* TCPSocketClient.swift in Sources */,
799396D91D48C02300086753 /* SMB2Types.swift in Sources */,
799396C71D48C02300086753 /* SMB2FileOperation.swift in Sources */,
799396C11D48C02300086753 /* SMB2DataTypes.swift in Sources */,
799396BB1D48C02300086753 /* SMBFileProvider.swift in Sources */,
799396BE1D48C02300086753 /* CIFSTypes.swift in Sources */,
799396D31D48C02300086753 /* SMB2SetInfo.swift in Sources */,
799396A91D48C02300086753 /* AEXML.swift in Sources */,
799396D01D48C02300086753 /* SMB2Session.swift in Sources */,
799396E21D48C02300086753 /* WebDAVFileProvider.swift in Sources */,
799396DC1D48C02300086753 /* SMBErrorType.swift in Sources */,
799396C41D48C02300086753 /* SMB2FileHandle.swift in Sources */,
799396CD1D48C02300086753 /* SMB2Query.swift in Sources */,
799396AC1D48C02300086753 /* DropboxFileProvider.swift in Sources */,
799396B21D48C02300086753 /* FileProvider.swift in Sources */,
799396B81D48C02300086753 /* SMBClient.swift in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXSourcesBuildPhase section */
/* Begin XCBuildConfiguration section */
799396601D48B7BF00086753 /* Debug */ = {
isa = XCBuildConfiguration;
buildSettings = {
};
name = Debug;
};
799396611D48B7BF00086753 /* Release */ = {
isa = XCBuildConfiguration;
buildSettings = {
};
name = Release;
};
7993966D1D48B7F600086753 /* Debug */ = {
isa = XCBuildConfiguration;
buildSettings = {
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;
CLANG_WARN_CONSTANT_CONVERSION = YES;
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
CLANG_WARN_EMPTY_BODY = YES;
CLANG_WARN_ENUM_CONVERSION = YES;
CLANG_WARN_INT_CONVERSION = YES;
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
CLANG_WARN_UNREACHABLE_CODE = YES;
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
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)",
);
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
GCC_WARN_UNDECLARED_SELECTOR = YES;
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
INFOPLIST_FILE = "Pod/Info-iOS.plist";
INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks";
IPHONEOS_DEPLOYMENT_TARGET = 8.0;
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
MTL_ENABLE_DEBUG_INFO = YES;
ONLY_ACTIVE_ARCH = YES;
PRODUCT_BUNDLE_IDENTIFIER = "com.mousavian.FileProvider-iOS";
PRODUCT_NAME = FileProvider;
SDKROOT = iphoneos;
SKIP_INSTALL = YES;
SWIFT_OPTIMIZATION_LEVEL = "-Onone";
TARGETED_DEVICE_FAMILY = "1,2";
VERSIONING_SYSTEM = "apple-generic";
VERSION_INFO_PREFIX = "";
};
name = Debug;
};
7993966E1D48B7F600086753 /* Release */ = {
isa = XCBuildConfiguration;
buildSettings = {
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;
CLANG_WARN_CONSTANT_CONVERSION = YES;
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
CLANG_WARN_EMPTY_BODY = YES;
CLANG_WARN_ENUM_CONVERSION = YES;
CLANG_WARN_INT_CONVERSION = YES;
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
CLANG_WARN_UNREACHABLE_CODE = YES;
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
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;
GCC_WARN_UNDECLARED_SELECTOR = YES;
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
INFOPLIST_FILE = "Pod/Info-iOS.plist";
INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks";
IPHONEOS_DEPLOYMENT_TARGET = 8.0;
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
MTL_ENABLE_DEBUG_INFO = NO;
PRODUCT_BUNDLE_IDENTIFIER = "com.mousavian.FileProvider-iOS";
PRODUCT_NAME = FileProvider;
SDKROOT = iphoneos;
SKIP_INSTALL = YES;
TARGETED_DEVICE_FAMILY = "1,2";
VALIDATE_PRODUCT = YES;
VERSIONING_SYSTEM = "apple-generic";
VERSION_INFO_PREFIX = "";
};
name = Release;
};
7993967B1D48B80D00086753 /* Debug */ = {
isa = XCBuildConfiguration;
buildSettings = {
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;
CLANG_WARN_CONSTANT_CONVERSION = YES;
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
CLANG_WARN_EMPTY_BODY = YES;
CLANG_WARN_ENUM_CONVERSION = YES;
CLANG_WARN_INT_CONVERSION = YES;
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;
DYLIB_INSTALL_NAME_BASE = "@rpath";
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)",
);
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
GCC_WARN_UNDECLARED_SELECTOR = YES;
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
INFOPLIST_FILE = "Pod/Info-MacOS.plist";
INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks";
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks @loader_path/Frameworks";
MACOSX_DEPLOYMENT_TARGET = 10.10;
MTL_ENABLE_DEBUG_INFO = YES;
ONLY_ACTIVE_ARCH = YES;
PRODUCT_BUNDLE_IDENTIFIER = "com.mousavian.FileProvider-OSX";
PRODUCT_NAME = FileProvider;
SDKROOT = macosx;
SKIP_INSTALL = YES;
SWIFT_OPTIMIZATION_LEVEL = "-Onone";
VERSIONING_SYSTEM = "apple-generic";
VERSION_INFO_PREFIX = "";
};
name = Debug;
};
7993967C1D48B80D00086753 /* Release */ = {
isa = XCBuildConfiguration;
buildSettings = {
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;
CLANG_WARN_CONSTANT_CONVERSION = YES;
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
CLANG_WARN_EMPTY_BODY = YES;
CLANG_WARN_ENUM_CONVERSION = YES;
CLANG_WARN_INT_CONVERSION = YES;
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;
DYLIB_INSTALL_NAME_BASE = "@rpath";
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;
GCC_WARN_UNDECLARED_SELECTOR = YES;
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
INFOPLIST_FILE = "Pod/Info-MacOS.plist";
INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks";
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks @loader_path/Frameworks";
MACOSX_DEPLOYMENT_TARGET = 10.10;
MTL_ENABLE_DEBUG_INFO = NO;
PRODUCT_BUNDLE_IDENTIFIER = "com.mousavian.FileProvider-OSX";
PRODUCT_NAME = FileProvider;
SDKROOT = macosx;
SKIP_INSTALL = YES;
VERSIONING_SYSTEM = "apple-generic";
VERSION_INFO_PREFIX = "";
};
name = Release;
};
799396881D48B82700086753 /* Debug */ = {
isa = XCBuildConfiguration;
buildSettings = {
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;
CLANG_WARN_CONSTANT_CONVERSION = YES;
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
CLANG_WARN_EMPTY_BODY = YES;
CLANG_WARN_ENUM_CONVERSION = YES;
CLANG_WARN_INT_CONVERSION = YES;
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)",
);
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
GCC_WARN_UNDECLARED_SELECTOR = YES;
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
INFOPLIST_FILE = "Pod/Info-tvOS.plist";
INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks";
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
MTL_ENABLE_DEBUG_INFO = YES;
ONLY_ACTIVE_ARCH = YES;
PRODUCT_BUNDLE_IDENTIFIER = "com.mousavian.FileProvider-tvOS";
PRODUCT_NAME = FileProvider;
SDKROOT = appletvos;
SKIP_INSTALL = YES;
SWIFT_OPTIMIZATION_LEVEL = "-Onone";
TARGETED_DEVICE_FAMILY = 3;
TVOS_DEPLOYMENT_TARGET = 9.0;
VERSIONING_SYSTEM = "apple-generic";
VERSION_INFO_PREFIX = "";
};
name = Debug;
};
799396891D48B82700086753 /* Release */ = {
isa = XCBuildConfiguration;
buildSettings = {
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;
CLANG_WARN_CONSTANT_CONVERSION = YES;
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
CLANG_WARN_EMPTY_BODY = YES;
CLANG_WARN_ENUM_CONVERSION = YES;
CLANG_WARN_INT_CONVERSION = YES;
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;
GCC_WARN_UNDECLARED_SELECTOR = YES;
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
INFOPLIST_FILE = "Pod/Info-tvOS.plist";
INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks";
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
MTL_ENABLE_DEBUG_INFO = NO;
PRODUCT_BUNDLE_IDENTIFIER = "com.mousavian.FileProvider-tvOS";
PRODUCT_NAME = FileProvider;
SDKROOT = appletvos;
SKIP_INSTALL = YES;
TARGETED_DEVICE_FAMILY = 3;
TVOS_DEPLOYMENT_TARGET = 9.0;
VALIDATE_PRODUCT = YES;
VERSIONING_SYSTEM = "apple-generic";
VERSION_INFO_PREFIX = "";
};
name = Release;
};
/* End XCBuildConfiguration section */
/* Begin XCConfigurationList section */
7993965F1D48B7BF00086753 /* Build configuration list for PBXProject "FileProvider" */ = {
isa = XCConfigurationList;
buildConfigurations = (
799396601D48B7BF00086753 /* Debug */,
799396611D48B7BF00086753 /* Release */,
);
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
};
7993966F1D48B7F600086753 /* Build configuration list for PBXNativeTarget "FileProvider iOS" */ = {
isa = XCConfigurationList;
buildConfigurations = (
7993966D1D48B7F600086753 /* Debug */,
7993966E1D48B7F600086753 /* Release */,
);
defaultConfigurationIsVisible = 0;
};
7993967A1D48B80D00086753 /* Build configuration list for PBXNativeTarget "FileProvider OSX" */ = {
isa = XCConfigurationList;
buildConfigurations = (
7993967B1D48B80D00086753 /* Debug */,
7993967C1D48B80D00086753 /* Release */,
);
defaultConfigurationIsVisible = 0;
};
799396871D48B82700086753 /* Build configuration list for PBXNativeTarget "FileProvider tvOS" */ = {
isa = XCConfigurationList;
buildConfigurations = (
799396881D48B82700086753 /* Debug */,
799396891D48B82700086753 /* Release */,
);
defaultConfigurationIsVisible = 0;
};
/* End XCConfigurationList section */
};
rootObject = 7993965C1D48B7BF00086753 /* Project object */;
}
@@ -0,0 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?>
<Workspace
version = "1.0">
<FileRef
location = "self:FileProvider.xcodeproj">
</FileRef>
</Workspace>
@@ -0,0 +1,80 @@
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
LastUpgradeVersion = "0730"
version = "1.3">
<BuildAction
parallelizeBuildables = "YES"
buildImplicitDependencies = "YES">
<BuildActionEntries>
<BuildActionEntry
buildForTesting = "YES"
buildForRunning = "YES"
buildForProfiling = "YES"
buildForArchiving = "YES"
buildForAnalyzing = "YES">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "799396741D48B80D00086753"
BuildableName = "FileProvider.framework"
BlueprintName = "FileProvider OSX"
ReferencedContainer = "container:FileProvider.xcodeproj">
</BuildableReference>
</BuildActionEntry>
</BuildActionEntries>
</BuildAction>
<TestAction
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
shouldUseLaunchSchemeArgsEnv = "YES">
<Testables>
</Testables>
<AdditionalOptions>
</AdditionalOptions>
</TestAction>
<LaunchAction
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
launchStyle = "0"
useCustomWorkingDirectory = "NO"
ignoresPersistentStateOnLaunch = "NO"
debugDocumentVersioning = "YES"
debugServiceExtension = "internal"
allowLocationSimulation = "YES">
<MacroExpansion>
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "799396741D48B80D00086753"
BuildableName = "FileProvider.framework"
BlueprintName = "FileProvider OSX"
ReferencedContainer = "container:FileProvider.xcodeproj">
</BuildableReference>
</MacroExpansion>
<AdditionalOptions>
</AdditionalOptions>
</LaunchAction>
<ProfileAction
buildConfiguration = "Release"
shouldUseLaunchSchemeArgsEnv = "YES"
savedToolIdentifier = ""
useCustomWorkingDirectory = "NO"
debugDocumentVersioning = "YES">
<MacroExpansion>
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "799396741D48B80D00086753"
BuildableName = "FileProvider.framework"
BlueprintName = "FileProvider OSX"
ReferencedContainer = "container:FileProvider.xcodeproj">
</BuildableReference>
</MacroExpansion>
</ProfileAction>
<AnalyzeAction
buildConfiguration = "Debug">
</AnalyzeAction>
<ArchiveAction
buildConfiguration = "Release"
revealArchiveInOrganizer = "YES">
</ArchiveAction>
</Scheme>
@@ -0,0 +1,80 @@
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
LastUpgradeVersion = "0730"
version = "1.3">
<BuildAction
parallelizeBuildables = "YES"
buildImplicitDependencies = "YES">
<BuildActionEntries>
<BuildActionEntry
buildForTesting = "YES"
buildForRunning = "YES"
buildForProfiling = "YES"
buildForArchiving = "YES"
buildForAnalyzing = "YES">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "799396661D48B7F600086753"
BuildableName = "FileProvider.framework"
BlueprintName = "FileProvider iOS"
ReferencedContainer = "container:FileProvider.xcodeproj">
</BuildableReference>
</BuildActionEntry>
</BuildActionEntries>
</BuildAction>
<TestAction
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
shouldUseLaunchSchemeArgsEnv = "YES">
<Testables>
</Testables>
<AdditionalOptions>
</AdditionalOptions>
</TestAction>
<LaunchAction
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
launchStyle = "0"
useCustomWorkingDirectory = "NO"
ignoresPersistentStateOnLaunch = "NO"
debugDocumentVersioning = "YES"
debugServiceExtension = "internal"
allowLocationSimulation = "YES">
<MacroExpansion>
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "799396661D48B7F600086753"
BuildableName = "FileProvider.framework"
BlueprintName = "FileProvider iOS"
ReferencedContainer = "container:FileProvider.xcodeproj">
</BuildableReference>
</MacroExpansion>
<AdditionalOptions>
</AdditionalOptions>
</LaunchAction>
<ProfileAction
buildConfiguration = "Release"
shouldUseLaunchSchemeArgsEnv = "YES"
savedToolIdentifier = ""
useCustomWorkingDirectory = "NO"
debugDocumentVersioning = "YES">
<MacroExpansion>
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "799396661D48B7F600086753"
BuildableName = "FileProvider.framework"
BlueprintName = "FileProvider iOS"
ReferencedContainer = "container:FileProvider.xcodeproj">
</BuildableReference>
</MacroExpansion>
</ProfileAction>
<AnalyzeAction
buildConfiguration = "Debug">
</AnalyzeAction>
<ArchiveAction
buildConfiguration = "Release"
revealArchiveInOrganizer = "YES">
</ArchiveAction>
</Scheme>
@@ -0,0 +1,80 @@
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
LastUpgradeVersion = "0730"
version = "1.3">
<BuildAction
parallelizeBuildables = "YES"
buildImplicitDependencies = "YES">
<BuildActionEntries>
<BuildActionEntry
buildForTesting = "YES"
buildForRunning = "YES"
buildForProfiling = "YES"
buildForArchiving = "YES"
buildForAnalyzing = "YES">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "799396811D48B82700086753"
BuildableName = "FileProvider.framework"
BlueprintName = "FileProvider tvOS"
ReferencedContainer = "container:FileProvider.xcodeproj">
</BuildableReference>
</BuildActionEntry>
</BuildActionEntries>
</BuildAction>
<TestAction
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
shouldUseLaunchSchemeArgsEnv = "YES">
<Testables>
</Testables>
<AdditionalOptions>
</AdditionalOptions>
</TestAction>
<LaunchAction
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
launchStyle = "0"
useCustomWorkingDirectory = "NO"
ignoresPersistentStateOnLaunch = "NO"
debugDocumentVersioning = "YES"
debugServiceExtension = "internal"
allowLocationSimulation = "YES">
<MacroExpansion>
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "799396811D48B82700086753"
BuildableName = "FileProvider.framework"
BlueprintName = "FileProvider tvOS"
ReferencedContainer = "container:FileProvider.xcodeproj">
</BuildableReference>
</MacroExpansion>
<AdditionalOptions>
</AdditionalOptions>
</LaunchAction>
<ProfileAction
buildConfiguration = "Release"
shouldUseLaunchSchemeArgsEnv = "YES"
savedToolIdentifier = ""
useCustomWorkingDirectory = "NO"
debugDocumentVersioning = "YES">
<MacroExpansion>
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "799396811D48B82700086753"
BuildableName = "FileProvider.framework"
BlueprintName = "FileProvider tvOS"
ReferencedContainer = "container:FileProvider.xcodeproj">
</BuildableReference>
</MacroExpansion>
</ProfileAction>
<AnalyzeAction
buildConfiguration = "Debug">
</AnalyzeAction>
<ArchiveAction
buildConfiguration = "Release"
revealArchiveInOrganizer = "YES">
</ArchiveAction>
</Scheme>
+26
View File
@@ -0,0 +1,26 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>CFBundleDevelopmentRegion</key>
<string>en</string>
<key>CFBundleExecutable</key>
<string>$(EXECUTABLE_NAME)</string>
<key>CFBundleIdentifier</key>
<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
<key>CFBundleInfoDictionaryVersion</key>
<string>6.0</string>
<key>CFBundleName</key>
<string>$(PRODUCT_NAME)</string>
<key>CFBundlePackageType</key>
<string>FMWK</string>
<key>CFBundleShortVersionString</key>
<string>0.3.0</string>
<key>CFBundleSignature</key>
<string>????</string>
<key>CFBundleVersion</key>
<string>$(CURRENT_PROJECT_VERSION)</string>
<key>NSPrincipalClass</key>
<string></string>
</dict>
</plist>
+26
View File
@@ -0,0 +1,26 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>CFBundleDevelopmentRegion</key>
<string>en</string>
<key>CFBundleExecutable</key>
<string>$(EXECUTABLE_NAME)</string>
<key>CFBundleIdentifier</key>
<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
<key>CFBundleInfoDictionaryVersion</key>
<string>6.0</string>
<key>CFBundleName</key>
<string>$(PRODUCT_NAME)</string>
<key>CFBundlePackageType</key>
<string>FMWK</string>
<key>CFBundleShortVersionString</key>
<string>0.3.0</string>
<key>CFBundleSignature</key>
<string>????</string>
<key>CFBundleVersion</key>
<string>$(CURRENT_PROJECT_VERSION)</string>
<key>NSPrincipalClass</key>
<string></string>
</dict>
</plist>
+26
View File
@@ -0,0 +1,26 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>CFBundleDevelopmentRegion</key>
<string>en</string>
<key>CFBundleExecutable</key>
<string>$(EXECUTABLE_NAME)</string>
<key>CFBundleIdentifier</key>
<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
<key>CFBundleInfoDictionaryVersion</key>
<string>6.0</string>
<key>CFBundleName</key>
<string>$(PRODUCT_NAME)</string>
<key>CFBundlePackageType</key>
<string>FMWK</string>
<key>CFBundleShortVersionString</key>
<string>0.3.0</string>
<key>CFBundleSignature</key>
<string>????</string>
<key>CFBundleVersion</key>
<string>$(CURRENT_PROJECT_VERSION)</string>
<key>NSPrincipalClass</key>
<string></string>
</dict>
</plist>
+80 -43
View File
@@ -1,46 +1,50 @@
# FileProvider (experimental)
>This Swift library provide a swifty way to deal with local and remote files and directories in same way.
>This Swift library provide a swifty way to deal with local and remote files and directories in a unified way.
[![Swift Version][swift-image]][swift-url]
[![License][license-image]][license-url]
[![Platform](https://img.shields.io/badge/Platform-iOS%2C%20OSX-lightgray.svg)]()
[![codebeat badge](https://codebeat.co/badges/7b359f48-78eb-4647-ab22-56262a827517)](https://codebeat.co/projects/github-com-amosavian-fileprovider)
[![CocoaPods Compatible](https://img.shields.io/cocoapods/v/FileProvider.svg)](https://img.shields.io/cocoapods/v/FileProvider.svg)
[![codebeat badge][codebeat-image]][codebeat-url]
<!---
[![Build Status][travis-image]][travis-url]
[![Carthage compatible](https://img.shields.io/badge/Carthage-compatible-4BC51D.svg?style=flat)](https://github.com/Carthage/Carthage)
[![CocoaPods Compatible](https://img.shields.io/cocoapods/v/EZSwiftExtensions.svg)](https://img.shields.io/cocoapods/v/LFAlertController.svg)
[![Carthage compatible](https://img.shields.io/badge/Carthage-compatible-4BC51D.svg?style=flat)](https://github.com/Carthage/Carthage)
--->
This library provides implementaion of WebDav and SMB/CIFS (incomplete) and local files.
All functions are async calls and it wont block your main thread.
Local and WebDAV providers are fully tested and can be used in production environment.
## Features
- [x] **LocalFileProvider** a wrapper for `NSFileManager` with some additions like searching and reading a portion of file
- [x] **WebDAVFileProvider** WebDAV protocol is usual file transmission system on Macs
- [ ] **SMBFileProvider** SMB/CIFS and SMB2/3 are file and printer sharing protocol which is originated from IBM & Microsoft and SMB2/3 is now replacing AFP protocol on MacOS. I Implemented data types and some basic functions but *main interface is not implemented yet!*
- [ ] **DropboxFileProvider**
- [x] **LocalFileProvider** a wrapper around `NSFileManager` with some additions like searching and reading a portion of file.
- [x] **WebDAVFileProvider** WebDAV protocol is usual file transmission system on Macs.
- [ ] **SMBFileProvider** SMB/CIFS and SMB2/3 are file and printer sharing protocol which is originated from IBM & Microsoft and SMB2/3 is now replacing AFP protocol on MacOS. I implemented data types and some basic functions but *main interface is not implemented yet!*
- [ ] **DropboxFileProvider** *partially implemented*
- [ ] **FTPFileProvider**
- [ ] **AmazonS3FileProvider**
## Requirements
- **Swift 2.2**
- iOS 7.0 , OSX 10.9
- iOS 8.0 , OSX 10.10
- XCode 7.3
## Installation
### Cocoapods / Carthage / Swift Package Manager
I will add when project is completed is ready to use in production envioronment
FileProvider supports both CocoaPods.
### Git clone
Add this line to your pods file:
pod "FileProvider"
### Git
To have latest updates with ease, use this command on terminal to get a clone:
git clone https://github.com/amosavian/FileProvider FileProvider
@@ -49,13 +53,12 @@ You can update your library using this command in FileProvider folder:
git pull
### Submodule into your project
if you have a git based project, use this command in your projects directory:
if you have a git based project, use this command in your projects directory to add this project as a submodule to your project:
git submodule add https://github.com/amosavian/FileProvider FileProvider
### Manually
Copy Source folder to your project and voila!
Copy Source folder to your project and Voila!
## Usage
@@ -65,13 +68,13 @@ Each provider has a specific class which conforms to FileProvider protocol and s
For LocalFileProvider if you want to deal with `Documents` folder
let documentsFileProvider = LocalFileProvider()
let documentsProvider = LocalFileProvider()
is equal to:
let documentPath = NSSearchPathForDirectoriesInDomains(NSSearchPathDirectory.DocumentDirectory, NSSearchPathDomainMask.UserDomainMask, true);
let documentsURL = NSURL(fileURLWithPath: documentPath);
let documentsFileProvider = LocalFileProvider(baseURL: documentsURL)
let documentsProvider = LocalFileProvider(baseURL: documentsURL)
You can't change the base url later. and all paths are related to this base url by default.
@@ -79,10 +82,14 @@ For remote file providers authentication may be necessary:
let credential = NSURLCredential(user: "user", password: "pass", persistence: NSURLCredentialPersistence.Permanent)
let webdavProvider = WebDAVFileProvider(baseURL: "http://www.example.com/dav", credential: credential)
* For Dropbox, user is clientID and password is Token which both must be retrieved via [OAuth2 API of Dropbox](https://www.dropbox.com/developers/reference/oauth-guide). There are libraries like [p2/OAuth2](https://github.com/p2/OAuth2) or [OAuthSwift](https://github.com/OAuthSwift/OAuthSwift) which can facilate the procedure to retrieve token.
For interaction with UI, set delegate variable of `FileProvider` object
### Delegate
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)
### 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.
@@ -91,7 +98,7 @@ It's simply tree method which indicated whether the operation failed, succeed an
Your class should conforms `FileProviderDelegate` class:
override func viewDidLoad() {
documentsFileProvider.delegate = self
documentsProvider.delegate = self
}
func fileproviderSucceed(fileProvider: FileProvider, operation: FileOperation) {
@@ -125,14 +132,27 @@ Your class should conforms `FileProviderDelegate` class:
}
}
**Note:** `fileproviderProgress()` delegate method is not called by `LocalFileProvider`.
Use completion handlers for error handling or result processing as far as possible.
It's recommended to use completion handlers for error handling or result processing.
#### Controlling file operations
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: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.**
### Directory contents and file attributes
There is a `FileObject` class which holds file attributes like size and creation date. You can retrieve information of files inside a directory or get information of a file directly
There is a `FileObject` class which holds file attributes like size and creation date. You can retrieve information of files inside a directory or get information of a file directly.
documentsFileProvider.attributesOfItemAtPath(path: "/file.txt", completionHandler: {
For a single file:
documentsProvider.attributesOfItemAtPath(path: "/file.txt", completionHandler: {
(attributes: LocalFileObject?, error: ErrorType?) -> Void} in
if let attributes = attributes {
print("File Size: \(attributes.size)")
@@ -142,7 +162,9 @@ There is a `FileObject` class which holds file attributes like size and creation
}
)
documentsFileProvider.contentsOfDirectoryAtPath(path: "/", completionHandler: {
To get list of files in a directory:
documentsProvider.contentsOfDirectoryAtPath(path: "/", completionHandler: {
(contents: [LocalFileObject], error: ErrorType?) -> Void} in
for file in contents {
print("Name: \(attributes.name)")
@@ -154,41 +176,44 @@ There is a `FileObject` class which holds file attributes like size and creation
### Change current directory
documentsFileProvider.currentPath = "/New Folder"
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.
### Creating File and Folders
Creating new directory:
documentsFileProvider.createFolder(folderName: "new folder", atPath: "/", completionHandler: nil)
documentsProvider.createFolder(folderName: "new folder", atPath: "/", completionHandler: nil)
Creating new file from data stream:
let data = "hello world!".dataUsingEncoding(NSUTF8StringEncoding)
let file = FileObject(name: "old.txt", createdDate: NSDate(), modifiedDate: NSDate(), isHidden: false, isReadOnly: true)
documentsFileProvider.createFile(fileAttribs: file, atPath: "/", contents: data, completionHandler: nil)
documentsProvider.createFile(fileAttribs: file, atPath: "/", contents: data, completionHandler: nil)
### Copy and Move/Rename Files
// Copy file old.txt to new.txt in current path
documentsFileProvider.copyItemAtPath(path: "new folder/old.txt", toPath: "new.txt", overwrite: false, completionHandler: nil)
Copy file old.txt to new.txt in current path:
// Move file old.txt to new.txt in current path
documentsFileProvider.moveItemAtPath(path: "new folder/old.txt", toPath: "new.txt", overwrite: false, completionHandler: nil)
documentsProvider.copyItemAtPath(path: "new folder/old.txt", toPath: "new.txt", overwrite: false, completionHandler: nil)
Move file old.txt to new.txt in current path:
documentsProvider.moveItemAtPath(path: "new folder/old.txt", toPath: "new.txt", overwrite: false, completionHandler: nil)
### Delete Files
documentsFileProvider.removeItemAtPath(path: "new.txt", completionHandler: nil)
***Caution:*** This method will not delete directories with content.
documentsProvider.removeItemAtPath(path: "new.txt", completionHandler: nil)
***Caution:*** This method will delete directories with all it's content recursively.
### Retrieve Content of File
THere is two method for this purpose, one of them loads entire file into NSData and another can load a portion of file.
documentsFileProvider.contentsAtPath(path: "old.txt:, completionHandler: {
documentsProvider.contentsAtPath(path: "old.txt", completionHandler: {
(contents: NSData?, error: ErrorType?) -> Void
if let contents = contents {
print(String(data: contents, encoding: NSUTF8StringEncoding)) // "hello world!"
@@ -197,7 +222,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.
documentsFileProvider.contentsAtPath(path: "old.txt", offset: 2, length: 5, completionHandler: {
documentsProvider.contentsAtPath(path: "old.txt", offset: 2, length: 5, completionHandler: {
(contents: NSData?, error: ErrorType?) -> Void
if let contents = contents {
print(String(data: contents, encoding: NSUTF8StringEncoding)) // "llo w"
@@ -207,31 +232,43 @@ If you want to retrieve a portion of file you should can `contentsAtPath` method
### Write Data To Files
let data = "What's up Newyork!".dataUsingEncoding(NSUTF8StringEncoding)
documentsFileProvider.writeContentsAtPath(path: "old.txt", contents data: data, atomically: true, completionHandler: nil)
documentsProvider.writeContentsAtPath(path: "old.txt", contents data: data, atomically: true, completionHandler: nil)
### 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.
documentsProvider.registerNotifcation(provider.currentPath)
{
// calling functions to update UI
}
// To discontinue monitoring folders:
documentsProvider.unregisterNotifcation(provider.currentPath)
* **Please note** in LocalFileProvider it will also monitor changes in subfolders. This behaviour can varies according to file system specification.
## Contribute
We would love for you to contribute to **FileProvider**, check the `LICENSE` file for more info.
## Meta
Amir-Abbas Mousavia [@amosavian](https://twitter.com/amosavian)
Amir-Abbas Mousavian [@amosavian](https://twitter.com/amosavian)
Distributed under the MIT license. See `LICENSE` for more information.
[https://github.com/yourname/github-link](https://github.com/dbader/)
[swift-image]:https://img.shields.io/badge/swift-2.3-green.svg
[swift-image]:https://img.shields.io/badge/swift-2.2-green.svg
[swift-url]: https://swift.org/
[license-image]: https://img.shields.io/badge/License-MIT-blue.svg
[license-url]: LICENSE
[codebeat-image]: https://codebeat.co/badges/7b359f48-78eb-4647-ab22-56262a827517
[codebeat-url]: https://codebeat.co/projects/github-com-amosavian-fileprovider
<!---
[travis-image]: https://img.shields.io/travis/dbader/node-datadog-metrics/master.svg?style=flat-square
[travis-url]: https://travis-ci.org/dbader/node-datadog-metrics
--->
[codebeat-image]: https://codebeat.co/badges/c19b47ea-2f9d-45df-8458-b2d952fe9dad
[codebeat-url]: https://codebeat.co/projects/github-com-vsouza-awesomeios-com
--->
File diff suppressed because it is too large Load Diff
-630
View File
@@ -1,630 +0,0 @@
//
// SMBTransmitter.swift
// FileProvider
//
// Created by Amir Abbas Mousavian.
// Copyright © 2016 Mousavian. Distributed under MIT license.
//
import Foundation
protocol SMBRequest {
func data() -> NSData
}
protocol SMBResponse {
init? (data: NSData)
}
internal func encode<T>(inout value: T) -> NSData {
return withUnsafePointer(&value) { p in
NSData(bytes: p, length: sizeofValue(value))
}
}
internal func decode<T>(data: NSData) -> T {
let pointer = UnsafeMutablePointer<T>.alloc(sizeof(T.Type))
data.getBytes(pointer, length: sizeof(T.Type))
return pointer.move()
}
// This client implementation is for little-endian platform, namely x86, x64 & arm
// For big-endian platforms like PowerPC, there must be a huge overhaul
class SMBProtocolClient: TCPSocketClient {
var currentMessageID: UInt64 = 0
func negotiateToSMB2() -> SMB2.NegotiateResponse? {
let smbHeader = SMB2.Header(command: .NEGOTIATE, creditRequestResponse: 126, messageId: messageId(), treeId: 0, sessionId: 0)
currentMessageID += 1
let negMessage = SMB2.NegotiateRequest(request: SMB2.NegotiateRequest.Header(capabilities: []))
SMBProtocolClient.createSMB2Message(smbHeader, message: negMessage)
do {
try self.send(data: nil)
} catch _ {
return nil
}
self.waitUntilResponse()
let response = try? SMBProtocolClient.digestSMB2Message(dataReceived)
return response??.message as? SMB2.NegotiateResponse
}
func sessionSetupForSMB2() -> SMB2.SessionSetupResponse? {
return nil
}
func messageId() -> UInt64 {
defer {
currentMessageID += 1
}
return currentMessageID
}
// MARK: create and analyse messages
class func determineSMBVersion(data: NSData) -> Float {
var smbverChar: Int8 = 0
data.getBytes(&smbverChar, length: 1)
let version = 0 - smbverChar
return Float(version)
}
class func digestSMBMessage(data: NSData) throws -> (header: SMB1.Header, blocks: [(params: [UInt16], message: NSData?)]) {
guard data.length > 30 else {
throw NSURLError.BadServerResponse
}
var buffer = [UInt8](count: data.length, repeatedValue: 0)
guard determineSMBVersion(data) == 1 else {
throw SMBFileProviderError.IncompatibleHeader
}
let headersize = sizeof(SMB1.Header.self)
let header: SMB1.Header = decode(data)
var blocks = [(params: [UInt16], message: NSData?)]()
var offset = headersize
while offset < data.length {
let paramWords: [UInt16]
let paramWordsCount = Int(buffer[offset])
guard data.length > (paramWordsCount * 2 + offset) else {
throw SMBFileProviderError.IncorrectParamsLength
}
offset += sizeof(UInt8)
var rawParamWords = [UInt8](buffer[offset..<(offset + paramWordsCount * 2)])
let paramData = NSData(bytesNoCopy: &rawParamWords, length: rawParamWords.count)
paramWords = decode(paramData)
offset += paramWordsCount * 2
let messageBytesCountLittleEndian = [UInt8](buffer[offset...(offset + 1)])
let messageBytesCount = Int(UnsafePointer<UInt16>(messageBytesCountLittleEndian).memory)
offset += sizeof(UInt16)
guard data.length >= (offset + messageBytesCount) else {
throw SMBFileProviderError.IncorrectMessageLength
}
var rawMessage = [UInt8](buffer[offset..<(offset + messageBytesCount)])
offset += messageBytesCount
let message = NSData(bytes: &rawMessage, length: rawMessage.count)
blocks.append((params: paramWords, message: message))
}
return (header, blocks)
}
class func digestSMB2Message(data: NSData) throws -> (header: SMB2.Header, message: SMBResponse?)? {
guard data.length > 65 else {
throw NSURLError.BadServerResponse
}
guard determineSMBVersion(data) == 2 else {
throw SMBFileProviderError.IncompatibleHeader
}
let headersize = sizeof(SMB2.Header.self)
let headerData = data.subdataWithRange(NSRange(location: 0, length: headersize))
let messageSize = data.length - headersize
let messageData = data.subdataWithRange(NSRange(location: headersize, length: messageSize))
let header: SMB2.Header = decode(headerData)
switch header.command {
case .NEGOTIATE:
return (header, SMB2.NegotiateResponse(data: messageData))
case .SESSION_SETUP:
return (header, SMB2.SessionSetupResponse(data: messageData))
case .LOGOFF:
return (header, SMB2.LogOff(data: messageData))
case .TREE_CONNECT:
return (header, SMB2.TreeConnectResponse(data: messageData))
case .TREE_DISCONNECT:
return (header, SMB2.TreeDisconnect(data: messageData))
case .CREATE:
return (header, SMB2.CreateResponse(data: messageData))
case .CLOSE:
return (header, SMB2.CloseResponse(data: messageData))
case .FLUSH:
return (header, SMB2.FlushResponse(data: messageData))
case .READ:
return (header, nil) // FIXME:
case .WRITE:
return (header, nil) // FIXME:
case .LOCK:
return (header, nil) // FIXME:
case .IOCTL:
return (header, nil)
case .CANCEL:
return (header, nil)
case .ECHO:
return (header, SMB2.Echo(data: messageData))
case .QUERY_DIRECTORY:
return (header, nil) // FIXME:
case .CHANGE_NOTIFY:
return (header, nil) // FIXME:
case .QUERY_INFO:
return (header, nil) // FIXME:
case .SET_INFO:
return (header, nil) // FIXME:
case .OPLOCK_BREAK:
return (header, nil) // FIXME:
case .INVALID:
throw SMBFileProviderError.InvalidCommand
}
}
class func createSMBMessage(header: SMB1.Header, blocks: [(params: NSData?, message: NSData?)]) -> NSData {
var headerv = header
let result = NSMutableData(data: encode(&headerv))
for block in blocks {
var paramWordsCount = UInt8(block.params?.length ?? 0)
result.appendBytes(&paramWordsCount, length: sizeofValue(paramWordsCount))
if let params = block.params {
result.appendData(params)
}
var messageLen = UInt16(block.message?.length ?? 0)
result.appendBytes(&messageLen, length: sizeofValue(messageLen))
if let message = block.message {
result.appendData(message)
}
}
return result
}
class func createSMB2Message(header: SMB2.Header, message: SMBRequest) -> NSData {
var headerv = header
let result = NSMutableData(data: encode(&headerv))
result.appendData(message.data())
return result
}
}
struct SMBTime {
var time: UInt64
init(time: UInt64) {
self.time = time
}
init(unixTime: UInt) {
self.time = (UInt64(unixTime) + 11644473600) * 10000000
}
init(timeIntervalSince1970: NSTimeInterval) {
self.time = UInt64((timeIntervalSince1970 + 11644473600) * 10000000)
}
init(date: NSDate) {
self.init(timeIntervalSince1970: date.timeIntervalSince1970)
}
var unixTime: UInt {
return UInt(self.time / 10000000 - 11644473600)
}
var date: NSDate {
return NSDate(timeIntervalSince1970: Double(self.time) / 10000000 - 11644473600)
}
}
protocol FileProviderSMBHeader {
var protocolID: UInt32 { get }
static var protocolConst: UInt32 { get }
}
// SMB/CIFS Types
struct SMB1 {
struct Header { // 32 bytes
// header is always \u{ff}SMB
let protocolID: UInt32
static let protocolConst: UInt32 = 0x424d53ff
private var _command: UInt8
var command: Command {
get {
return Command(rawValue: _command) ?? .INVALID
}
set {
_command = newValue.rawValue
}
}
// error messages from the server to the client
private var _status: (UInt8, UInt8, UInt8, UInt8)
var error: (Class: UInt8, code: UInt16) {
get {
return (_status.0, UInt16(_status.2) + (UInt16(_status.3) << 8))
}
set {
_status = (newValue.Class, 0, UInt8(newValue.code & 0xff), UInt8(newValue.code >> 8))
}
}
var ntStatus: UInt32 {
get {
let statusLo = UInt32(_status.0) + UInt32(_status.1) << 8
let statusHi = UInt32(_status.2) + UInt32(_status.3) << 8
return statusHi << 16 + statusLo
}
set {
_status = (UInt8(newValue & 0xff), UInt8(newValue >> 8 & 0xff), UInt8(newValue >> 16 & 0xff), UInt8(newValue >> 24 & 0xff))
}
}
var flags: Flags
var flags2: Flags2
var pidHigh: UInt16
// encryption key used for validating messages over connectionless transports
private var _securityKey: (UInt16, UInt16)
var securityKey: UInt32 {
get {
return UInt32(_securityKey.1) << 16 + UInt32(_securityKey.0)
}
set {
_securityKey = (UInt16(newValue & 0xffff), UInt16(newValue >> 16))
}
}
/// Connection identifier
var securityCID: UInt16
/// Identifier of the sequence of a message over connectionless transports
var securitySequenceNumber: UInt16
private var ununsed: UInt16
var treeId: UInt16
var pidLow: UInt16
var userId: UInt16
var multiplexId: UInt16
var pid: UInt32 {
get {
return UInt32(pidLow) + UInt32(pidHigh) << 16
}
set {
pidLow = UInt16(newValue & 0xffff)
pidHigh = UInt16(newValue >> 16)
}
}
init(command: Command, ntStatus: UInt32 = 0, flags: Flags, flags2: Flags2 = [.LONG_NAMES, .ERR_STATUS, .UNICODE], securityKey: UInt32 = 0, securityCID: UInt16 = 0, securitySequenceNumber: UInt16 = 0, treeId: UInt16, pid: UInt32, userId: UInt16, multiplexId: UInt16) {
self.protocolID = Header.protocolConst
self._command = command.rawValue
_status = (UInt8(ntStatus & 0xff), UInt8(ntStatus >> 8 & 0xff), UInt8(ntStatus >> 16 & 0xff), UInt8(ntStatus >> 24 & 0xff))
self.flags = flags
self.flags2 = flags2
self._securityKey = (UInt16(securityKey & 0xffff), UInt16(securityKey >> 16))
self.securityCID = securityCID
self.securitySequenceNumber = securitySequenceNumber
self.ununsed = 0
self.treeId = treeId
self.pidLow = UInt16(pid & 0xffff)
self.pidHigh = UInt16(pid >> 16)
self.userId = userId
self.multiplexId = multiplexId
}
}
struct Flags: OptionSetType {
let rawValue: UInt8
init(rawValue: UInt8) {
self.rawValue = rawValue
}
/** This bit is set (1) in the NEGOTIATE (0x72) Response if the server supports
* LOCK_AND_READ (0x13) and WRITE_AND_UNLOCK (0x14) commands. */
static let LOCK_AND_READ_OK = Flags(rawValue: 0x01)
static let BUF_AVAIL = Flags(rawValue: 0x02)
static let CASE_INSENSITIVE = Flags(rawValue: 0x08)
static let CANONICALIZED_PATHS = Flags(rawValue: 0x10)
static let OPLOCK = Flags(rawValue: 0x20)
static let OPBATCH = Flags(rawValue: 0x40)
/** When on, this message is being sent from the server in response to a client request. */
static let REPLY = Flags(rawValue: 0x80)
}
struct Flags2: OptionSetType {
let rawValue: UInt16
init(rawValue: UInt16) {
self.rawValue = rawValue
}
/** Client: the message MAY contain long file names. */
static let LONG_NAMES = Flags2(rawValue: 0x0001)
/** Client: the client is aware of extended attributes (EAs). */
static let EAS = Flags2(rawValue: 0x0002)
/** Client: the client is requesting signing (if signing is not yet active) or the message
* being sent is signed. This bit is used on the SMB header of an SESSION_SETUP_ANDX */
static let SMB_SECURITY_SIGNATURE = Flags2(rawValue: 0x0004)
/// Reserved but not implemented.
static let IS_LONG_NAME = Flags2(rawValue: 0x0040)
/** client aware of Extended Security negotiation */
static let EXT_SEC = Flags2(rawValue: 0x0800)
/** any pathnames in this SMB SHOULD be resolved in the Distributed File System (DFS) */
static let DFS = Flags2(rawValue: 0x1000)
/**
* This flag is useful only on a read request. If the bit is set, then the client MAY read
* the file if the client does not have read permission but does have execute permission. */
static let PAGING_IO = Flags2(rawValue: 0x2000) // READ_IF_EXECUTE
/**
* Client: the server MUST return errors as 32-bit NTSTATUS codes in the response
* Server: the Status field in the header is formatted as an NTSTATUS cod */
static let ERR_STATUS = Flags2(rawValue: 0x4000)
/**
* Each field that contains a string in this SMB message MUST be encoded
* as an array of 16-bit Unicode characters */
static let UNICODE = Flags2(rawValue: 0x8000)
}
enum Command: UInt8 {
case CREATE_DIRECTORY = 0x00
case DELETE_DIRECTORY = 0x01
case OPEN = 0x02
case CREATE = 0x03
case CLOSE = 0x04
case FLUSH = 0x05
case DELETE = 0x06
case RENAME = 0x07
case QUERY_INFORMATION = 0x08
case SET_INFORMATION = 0x09
case READ = 0x0A
case WRITE = 0x0B
case LOCK_BYTE_RANGE = 0x0C
case UNLOCK_BYTE_RANGE = 0x0D
case CREATE_TEMPORARY = 0x0E
case CREATE_NEW = 0x0F
case CHECK_DIRECTORY = 0x10
case PROCESS_EXIT = 0x11
case SEEK = 0x12
case LOCK_AND_READ = 0x13
case WRITE_AND_UNLOCK = 0x14
case READ_RAW = 0x1A
case READ_MPX = 0x1B
case READ_MPX_SECONDARY = 0x1C
case WRITE_RAW = 0x1D
case WRITE_MPX = 0x1E
case WRITE_COMPLETE = 0x20
case SET_INFORMATION2 = 0x22
case QUERY_INFORMATION2 = 0x23
case LOCKING_ANDX = 0x24
case TRANSACTION = 0x25
case TRANSACTION_SECONDARY = 0x26
case IOCTL = 0x27
case IOCTL_SECONDARY = 0x28
case COPY = 0x29
case MOVE = 0x2A
case ECHO = 0x2B
case WRITE_AND_CLOSE = 0x2C
case OPEN_ANDX = 0x2D
case READ_ANDX = 0x2E
case WRITE_ANDX = 0x2F
case CLOSE_AND_TREE_DISC = 0x31
case TRANSACTION2 = 0x32
case TRANSACTION2_SECONDARY = 0x33
case FIND_CLOSE2 = 0x34
case FIND_NOTIFY_CLOSE = 0x35
case TREE_CONNECT = 0x70
case TREE_DISCONNECT = 0x71
case NEGOTIATE = 0x72
case SESSION_SETUP_ANDX = 0x73
case LOGOFF_ANDX = 0x74
case TREE_CONNECT_ANDX = 0x75
case QUERY_INFORMATION_DISK = 0x80
case SEARCH = 0x81
case FIND = 0x82
case FIND_UNIQUE = 0x83
case NT_TRANSACT = 0xA0
case NT_TRANSACT_SECONDARY = 0xA1
case NT_CREATE_ANDX = 0xA2
case NT_CANCEL = 0xA4
case OPEN_PRINT_FILE = 0xC0
case WRITE_PRINT_FILE = 0xC1
case CLOSE_PRINT_FILE = 0xC2
case GET_PRINT_QUEUE = 0xC3
case READ_BULK = 0xD8
case WRITE_BULK = 0xD9
case WRITE_BULK_DATA = 0xDA
case INVALID = 0xFE
}
}
/// Error Types and Description
enum NTStatus: UInt32, ErrorType, CustomStringConvertible {
case SUCCESS = 0x00000000
case NOT_IMPLEMENTED = 0xC0000002
case INVALID_DEVICE_REQUEST = 0xC0000010
case ILLEGAL_FUNCTION = 0xC00000AF
case NO_SUCH_FILE = 0xC000000F
case NO_SUCH_DEVICE = 0xC000000E
case OBJECT_NAME_NOT_FOUND = 0xC0000034
case OBJECT_PATH_INVALID = 0xC0000039
case OBJECT_PATH_NOT_FOUND = 0xC000003A
case OBJECT_PATH_SYNTAX_BAD = 0xC000003B
case DFS_EXIT_PATH_FOUND = 0xC000009B
case REDIRECTOR_NOT_STARTED = 0xC00000FB
case TOO_MANY_OPENED_FILES = 0xC000011F
case ACCESS_DENIED = 0xC0000022
case INVALID_LOCK_SEQUENCE = 0xC000001E
case INVALID_VIEW_SIZE = 0xC000001F
case ALREADY_COMMITTED = 0xC0000021
case PORT_CONNECTION_REFUSED = 0xC0000041
case THREAD_IS_TERMINATING = 0xC000004B
case DELETE_PENDING = 0xC0000056
case PRIVILEGE_NOT_HELD = 0xC0000061
case LOGON_FAILURE = 0xC000006D
case FILE_IS_A_DIRECTORY = 0xC00000BA
case FILE_RENAMED = 0xC00000D5
case PROCESS_IS_TERMINATING = 0xC000010A
case DIRECTORY_NOT_EMPTY = 0xC0000101
case CANNOT_DELETE = 0xC0000121
case FILE_NOT_AVAILABLE = 0xC0000467
case FILE_DELETED = 0xC0000123
case SMB_BAD_FID = 0x00060001
case INVALID_HANDLE = 0xC0000008
case OBJECT_TYPE_MISMATCH = 0xC0000024
case PORT_DISCONNECTED = 0xC0000037
case INVALID_PORT_HANDLE = 0xC0000042
case FILE_CLOSED = 0xC0000128
case HANDLE_NOT_CLOSABLE = 0xC0000235
case SECTION_TOO_BIG = 0xC0000040
case TOO_MANY_PAGING_FILES = 0xC0000097
case INSUFF_SERVER_RESOURCES = 0xC0000205
case OS2_INVALID_ACCESS = 0x000C0001
case ACCESS_DENIED_2 = 0xC00000CA
case DATA_ERROR = 0xC000009C
case NOT_SAME_DEVICE = 0xC00000D4
case NO_MORE_FILES = 0x80000006
case NO_MORE_ENTRIES = 0x8000001A
case UNSUCCESSFUL = 0xC0000001
case SHARING_VIOLATION = 0xC0000043
case FILE_LOCK_CONFLICT = 0xC0000054
case LOCK_NOT_GRANTED = 0xC0000055
case END_OF_FILE = 0xC0000011
case NOT_SUPPORTED = 0xC00000BB
case OBJECT_NAME_COLLISION = 0xC0000035
case INVALID_PARAMETER = 0xC000000D
case OS2_INVALID_LEVEL = 0x007C0001
case OS2_NEGATIVE_SEEK = 0x00830001
case RANGE_NOT_LOCKED = 0xC000007E
case OS2_NO_MORE_SIDS = 0x00710001
case OS2_CANCEL_VIOLATION = 0x00AD0001
case OS2_ATOMIC_LOCKS_NOT_SUPPORTED = 0x00AE0001
case INVALID_INFO_CLASS = 0xC0000003
case INVALID_PIPE_STATE = 0xC00000AD
case INVALID_READ_MODE = 0xC00000B4
case OS2_CANNOT_COPY = 0x010A0001
case STOPPED_ON_SYMLINK = 0x8000002D
case INSTANCE_NOT_AVAILABLE = 0xC00000AB
case PIPE_NOT_AVAILABLE = 0xC00000AC
case PIPE_BUSY = 0xC00000AE
case PIPE_CLOSING = 0xC00000B1
case PIPE_EMPTY = 0xC00000D9
case PIPE_DISCONNECTED = 0xC00000B0
case BUFFER_OVERFLOW = 0x80000005
case MORE_PROCESSING_REQUIRED = 0xC0000016
case EA_TOO_LARGE = 0xC0000050
case OS2_EAS_DIDNT_FIT = 0x01130001
case EAS_NOT_SUPPORTED = 0xC000004F
case EA_LIST_INCONSISTENT = 0x80000014
case OS2_EA_ACCESS_DENIED = 0x03E20001
case NOTIFY_ENUM_DIR = 0x0000010C
case INVALID_SMB = 0x00010002
case WRONG_PASSWORD = 0xC000006A
case PATH_NOT_COVERED = 0xC0000257
case NETWORK_NAME_DELETED = 0xC00000C9
case SMB_BAD_TID = 0x00050002
case BAD_NETWORK_NAME = 0xC00000CC
case BAD_DEVICE_TYPE = 0xC00000CB
case SMB_BAD_COMMAND = 0x00160002
case PRINT_QUEUE_FULL = 0xC00000C6
case NO_SPOOL_SPACE = 0xC00000C7
case PRINT_CANCELLED = 0xC00000C8
case UNEXPECTED_NETWORK_ERROR = 0xC00000C4
case IO_TIMEOUT = 0xC00000B5
case REQUEST_NOT_ACCEPTED = 0xC00000D0
case TOO_MANY_SESSIONS = 0xC00000CE
case SMB_BAD_UID = 0x005B0002
case SMB_USE_MPX = 0x00FA0002
case SMB_USE_STANDARD = 0x00FB0002
case SMB_CONTINUE_MPX = 0x00FC0002
case ACCOUNT_DISABLED = 0xC0000072
case ACCOUNT_EXPIRED = 0xC0000193
case INVALID_WORKSTATION = 0xC0000070
case INVALID_LOGON_HOURS = 0xC000006F
case PASSWORD_EXPIRED = 0xC0000071
case PASSWORD_MUST_CHANGE = 0xC0000224
case SMB_NO_SUPPORT = 0xFFFF0002
case MEDIA_WRITE_PROTECTED = 0xC00000A2
case NO_MEDIA_IN_DEVICE = 0xC0000013
case INVALID_DEVICE_STATE = 0xC0000184
case DATA_ERROR_2 = 0xC000003E
case CRC_ERROR = 0xC000003F
case DISK_CORRUPT_ERROR = 0xC0000032
case NONEXISTENT_SECTOR = 0xC0000015
case DEVICE_PAPER_EMPTY = 0x8000000E
case WRONG_VOLUME = 0xC0000012
case DISK_FULL = 0xC000007F
case BUFFER_TOO_SMALL = 0xC0000023
case BAD_IMPERSONATION_LEVEL = 0xC00000A5
case USER_SESSION_DELETED = 0xC0000203
case NETWORK_SESSION_EXPIRED = 0xC000035C
case SMB_TOO_MANY_UIDS = 0xC000205A
var description: String {
switch self {
case NOT_IMPLEMENTED, INVALID_DEVICE_REQUEST, ILLEGAL_FUNCTION:
return "Invalid Function."
case NO_SUCH_FILE, NO_SUCH_DEVICE, OBJECT_NAME_NOT_FOUND:
return "File not found."
case OBJECT_PATH_INVALID, OBJECT_PATH_NOT_FOUND, OBJECT_PATH_SYNTAX_BAD, DFS_EXIT_PATH_FOUND, REDIRECTOR_NOT_STARTED:
return "A component in the path prefix is not a directory."
case TOO_MANY_OPENED_FILES:
return "Too many open files. No FIDs are available."
case ACCESS_DENIED, INVALID_LOCK_SEQUENCE, INVALID_VIEW_SIZE, ALREADY_COMMITTED, PORT_CONNECTION_REFUSED, THREAD_IS_TERMINATING, DELETE_PENDING, PRIVILEGE_NOT_HELD, LOGON_FAILURE, FILE_IS_A_DIRECTORY, FILE_RENAMED, PROCESS_IS_TERMINATING, CANNOT_DELETE, FILE_DELETED:
return "Access denied."
case SMB_BAD_FID, INVALID_HANDLE, OBJECT_TYPE_MISMATCH, PORT_DISCONNECTED, INVALID_PORT_HANDLE, FILE_CLOSED, HANDLE_NOT_CLOSABLE:
return "Invalid FID."
case SECTION_TOO_BIG, TOO_MANY_PAGING_FILES, INSUFF_SERVER_RESOURCES:
return "Insufficient server memory to perform the requested operation."
case OS2_INVALID_ACCESS:
return "Invalid open mode."
case DATA_ERROR:
return "Bad data. (May be generated by IOCTL calls on the server.)"
case DIRECTORY_NOT_EMPTY:
return "Remove of directory failed because it was not empty."
case NOT_SAME_DEVICE:
return "A file system operation (such as a rename) across two devices was attempted."
case NO_MORE_FILES:
return "No (more) files found following a file search command."
case UNSUCCESSFUL:
return "General error."
case SHARING_VIOLATION:
return "Sharing violation. A requested open mode conflicts with the sharing mode of an existing file handle."
case FILE_LOCK_CONFLICT, LOCK_NOT_GRANTED:
return "A lock request specified an invalid locking mode, or conflicted with an existing file lock."
case END_OF_FILE:
return "Attempted to read beyond the end of the file."
case NOT_SUPPORTED:
return "This command is not supported by the server."
case OBJECT_NAME_COLLISION:
return "An attempt to create a file or directory failed because an object with the same pathname already exists."
case INVALID_PARAMETER:
return "A parameter supplied with the message is invalid."
case OS2_INVALID_LEVEL:
return "Invalid information level."
case OS2_NEGATIVE_SEEK:
return "An attempt was made to seek to a negative absolute offset within a file."
case RANGE_NOT_LOCKED:
return "The byte range specified in an unlock request was not locked."
case OS2_NO_MORE_SIDS:
return "Maximum number of searches has been exhausted."
case OS2_CANCEL_VIOLATION:
return "No lock request was outstanding for the supplied cancel region."
case OS2_ATOMIC_LOCKS_NOT_SUPPORTED:
return "The file system does not support atomic changes to the lock type."
case INVALID_INFO_CLASS, INVALID_PIPE_STATE, INVALID_READ_MODE:
return "Invalid named pipe."
case OS2_CANNOT_COPY:
return "The copy functions cannot be used."
case INSTANCE_NOT_AVAILABLE, PIPE_NOT_AVAILABLE, PIPE_BUSY:
return "All instances of the designated named pipe are busy."
case PIPE_CLOSING, PIPE_EMPTY:
return "The designated named pipe is in the process of being closed."
case PIPE_DISCONNECTED:
return "The designated named pipe exists, but there is no server process listening on the server side."
case BUFFER_OVERFLOW, MORE_PROCESSING_REQUIRED:
return "There is more data available to read on the designated named pipe."
case EA_TOO_LARGE, OS2_EAS_DIDNT_FIT:
return "Either there are no extended attributes, or the available extended attributes did not fit into the response."
case EAS_NOT_SUPPORTED:
return "The server file system does not support Extended Attributes."
case OS2_EA_ACCESS_DENIED:
return "Access to the extended attribute was denied."
default:
return ""
}
}
}
+385
View File
@@ -0,0 +1,385 @@
//
// DropboxFileProvider.swift
// FileProvider
//
// Created by Amir Abbas Mousavian.
// Copyright © 2016 Mousavian. Distributed under MIT license.
//
import Foundation
public enum FileProviderDropboxErrorCode: Int {
case BadInputParameter = 400
case ExpiredToken = 401
case Forbidden = 403
case Endpoint = 409
case TooManyRequests = 429
case InternalServer = 500
case BadGateway = 502
}
public struct FileProviderDropboxError: ErrorType, CustomStringConvertible {
let code: FileProviderDropboxErrorCode
let path: String
public var description: String {
switch code {
case .BadInputParameter: return "Bad input parameter."
case .ExpiredToken: return "Bad or expired token. To fix this, you should re-authenticate the user."
case .Forbidden: return "Forbidden."
case .Endpoint: return "Endpoint-specific error."
case .TooManyRequests: return "Your app is making too many requests"
case .InternalServer: return "An error occurred on the Dropbox servers."
case .BadGateway: return "An error occurred on the Dropbox servers."
}
}
}
public final class DropboxFileObject: FileObject {
let serverTime: NSDate?
let id: String?
let rev: String?
init(absoluteURL: NSURL, name: String, path: String, size: Int64, serverTime: NSDate?, createdDate: NSDate?, modifiedDate: NSDate?, fileType: FileType, isHidden: Bool, isReadOnly: Bool, id: String?, rev: String?) {
self.serverTime = serverTime
self.id = id
self.rev = rev
super.init(absoluteURL: absoluteURL, name: name, path: path, size: size, createdDate: createdDate, modifiedDate: modifiedDate, fileType: fileType, isHidden: isHidden, isReadOnly: isReadOnly)
}
}
// Because this class uses NSURLSession, it's necessary to disable App Transport Security
// in case of using this class with unencrypted HTTP connection.
public class DropboxFileProvider: NSObject, FileProviderBasic {
public let type: String = "WebDAV"
public let isPathRelative: Bool = true
public let baseURL: NSURL?
public var currentPath: String = ""
public var dispatch_queue: dispatch_queue_t {
willSet {
assert(_session == nil, "It's not effective to change dispatch_queue property after session is initialized.")
}
}
public weak var delegate: FileProviderDelegate?
public let credential: NSURLCredential?
private var _session: NSURLSession?
private var session: NSURLSession {
if _session == nil {
let queue = NSOperationQueue()
//queue.underlyingQueue = dispatch_queue
_session = NSURLSession(configuration: NSURLSessionConfiguration.defaultSessionConfiguration(), delegate: self, delegateQueue: queue)
}
return _session!
}
init? (baseURL: NSURL, credential: NSURLCredential?) {
if !["http", "https"].contains(baseURL.scheme.lowercaseString) {
return nil
}
self.baseURL = baseURL
dispatch_queue = dispatch_queue_create("FileProvider.\(type)", DISPATCH_QUEUE_CONCURRENT)
//let url = baseURL.absoluteString
self.credential = credential
}
deinit {
_session?.invalidateAndCancel()
}
public func contentsOfDirectoryAtPath(path: String, completionHandler: ((contents: [FileObject], error: ErrorType?) -> Void)) {
NotImplemented()
}
public func attributesOfItemAtPath(path: String, completionHandler: ((attributes: FileObject?, error: ErrorType?) -> Void)) {
let url = NSURL(string: "https://api.dropboxapi.com/2/files/list_revisions")!
let request = NSMutableURLRequest(URL: url)
request.HTTPMethod = "POST"
request.setValue("Bearer \(credential?.password ?? "")", forHTTPHeaderField: "Authorization")
request.setValue("application/json", forHTTPHeaderField: "Content-Type")
let requestDictionary = ["path": correctPath(path)!]
request.HTTPBody = dictionaryToJSON(requestDictionary)?.dataUsingEncoding(NSUTF8StringEncoding)
let task = session.dataTaskWithRequest(request) { (data, response, error) in
if let response = response as? NSHTTPURLResponse {
defer {
self.delegateNotify(FileOperation.Create(path: path), error: error)
}
let code = FileProviderDropboxErrorCode(rawValue: response.statusCode)
let dbError: FileProviderDropboxError? = code != nil ? FileProviderDropboxError(code: code!, path: path) : nil
if let data = data, let jsonStr = String(data: data, encoding: NSUTF8StringEncoding) {
let json = self.jsonToDictionary(jsonStr)
if (json?["is_deleted"] as? NSNumber)?.boolValue ?? false, let entries = json?["entries"] as? [AnyObject] where entries.count > 0 , let entry = entries[0] as? [String: AnyObject], let file = self.mapToFileObject(entry) {
completionHandler(attributes: file, error: dbError)
return
}
}
completionHandler(attributes: nil, error: dbError)
return
}
completionHandler(attributes: nil, error: error)
}
task.resume()
}
public weak var fileOperationDelegate: FileOperationDelegate?
}
extension DropboxFileProvider: FileProviderOperations {
public func createFolder(folderName: String, atPath: String, completionHandler: SimpleCompletionHandler) {
let path = (atPath as NSString).stringByAppendingPathComponent(folderName) + "/"
doOperation(.Create(path: path), completionHandler: completionHandler)
}
public func createFile(fileAttribs: FileObject, atPath path: String, contents data: NSData?, completionHandler: SimpleCompletionHandler) {
NotImplemented()
}
public func moveItemAtPath(path: String, toPath: String, overwrite: Bool = false, completionHandler: SimpleCompletionHandler) {
doOperation(.Move(source: path, destination: toPath), completionHandler: completionHandler)
}
public func copyItemAtPath(path: String, toPath: String, overwrite: Bool = false, completionHandler: SimpleCompletionHandler) {
doOperation(.Copy(source: path, destination: toPath), completionHandler: completionHandler)
}
public func removeItemAtPath(path: String, completionHandler: SimpleCompletionHandler) {
doOperation(.Remove(path: path), completionHandler: completionHandler)
}
private func doOperation(operation: FileOperation, completionHandler: SimpleCompletionHandler) {
let url: String
var path: String?, fromPath: String?, toPath: String?
switch operation {
case .Create(path: let p):
url = "https://api.dropboxapi.com/2/files/create_folder"
path = p
case .Copy(source: let fp, destination: let tp):
url = "https://api.dropboxapi.com/2/files/copy"
fromPath = fp
toPath = tp
case .Move(source: let fp, destination: let tp):
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 .Link(link: _, target: _):
return
}
let request = NSMutableURLRequest(URL: NSURL(string: url)!)
request.HTTPMethod = "POST"
request.setValue("Bearer \(credential?.password ?? "")", forHTTPHeaderField: "Authorization")
request.setValue("application/json", forHTTPHeaderField: "Content-Type")
var requestDictionary = [String: AnyObject]()
requestDictionary["path"] = correctPath(path)
requestDictionary["from_path"] = correctPath(fromPath)
requestDictionary["to_path"] = correctPath(toPath)
request.HTTPBody = dictionaryToJSON(requestDictionary)?.dataUsingEncoding(NSUTF8StringEncoding)
let task = session.dataTaskWithRequest(request) { (data, response, error) in
if let response = response as? NSHTTPURLResponse {
let code = FileProviderDropboxErrorCode(rawValue: response.statusCode)
let dbError: FileProviderDropboxError? = code != nil ? FileProviderDropboxError(code: code!, path: path ?? fromPath ?? "") : 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?(error: dbError)
return
}
completionHandler?(error: error)
}
task.resume()
}
public func copyLocalFileToPath(localFile: NSURL, toPath: String, completionHandler: SimpleCompletionHandler) {
NotImplemented()
let request = NSMutableURLRequest(URL: absoluteURL(toPath))
request.HTTPMethod = "PUT"
let task = session.uploadTaskWithRequest(request, fromFile: localFile) { (data, response, error) in
completionHandler?(error: error)
self.delegateNotify(.Move(source: localFile.absoluteString, destination: toPath), error: error)
}
task.taskDescription = self.dictionaryToJSON(["type": "Copy", "source": localFile.absoluteString, "dest": toPath])
task.resume()
}
public func copyPathToLocalFile(path: String, toLocalURL: NSURL, completionHandler: SimpleCompletionHandler) {
NotImplemented()
let request = NSMutableURLRequest(URL: absoluteURL(path))
let task = session.downloadTaskWithRequest(request) { (sourceFileURL, response, error) in
if let sourceFileURL = sourceFileURL {
do {
try NSFileManager.defaultManager().copyItemAtURL(sourceFileURL, toURL: toLocalURL)
} catch let e {
completionHandler?(error: e)
return
}
}
completionHandler?(error: error)
}
task.taskDescription = self.dictionaryToJSON(["type": "Copy", "source": path, "dest": toLocalURL.absoluteString])
task.resume()
}
}
extension DropboxFileProvider: FileProviderReadWrite {
public func contentsAtPath(path: String, completionHandler: ((contents: NSData?, error: ErrorType?) -> Void)) {
self.contentsAtPath(path, offset: 0, length: -1, completionHandler: completionHandler)
}
public func contentsAtPath(path: String, offset: Int64, length: Int, completionHandler: ((contents: NSData?, error: ErrorType?) -> Void)) {
let url = NSURL(string: "https://api.dropboxapi.com/2/files/download")!
let request = NSMutableURLRequest(URL: url)
request.HTTPMethod = "GET"
request.setValue("Bearer \(credential?.password ?? "")", forHTTPHeaderField: "Authorization")
request.setValue("application/json", forHTTPHeaderField: "Content-Type")
if length > 0 {
request.setValue("bytes=\(offset)-\(offset + length)", forHTTPHeaderField: "Range")
} else if offset > 0 && length < 0 {
request.setValue("bytes=\(offset)-", forHTTPHeaderField: "Range")
}
let requestDictionary = ["path": path]
request.setValue(dictionaryToJSON(requestDictionary), forHTTPHeaderField: "Dropbox-API-Arg")
let task = session.downloadTaskWithRequest(request, completionHandler: { (cacheURL, response, error) in
guard let cacheURL = cacheURL, let httpResponse = response as? NSHTTPURLResponse where httpResponse.statusCode < 300 else {
let code = FileProviderDropboxErrorCode(rawValue: (response as? NSHTTPURLResponse)?.statusCode ?? -1)
let dbError: FileProviderDropboxError? = code != nil ? FileProviderDropboxError(code: code!, path: path) : nil
completionHandler(contents: nil, error: dbError ?? error)
return
}
let destURL = NSURL(fileURLWithPath: NSTemporaryDirectory()).URLByAppendingPathComponent(cacheURL.lastPathComponent ?? "tmpfile")
do {
try NSFileManager.defaultManager().moveItemAtURL(cacheURL, toURL: destURL)
completionHandler(contents: NSData(contentsOfURL: destURL), error: error)
} catch let e {
completionHandler(contents: nil, error: e)
}
})
task.resume()
}
public func writeContentsAtPath(path: String, contents data: NSData, atomically: Bool = false, completionHandler: SimpleCompletionHandler) {
NotImplemented()
let url = atomically ? absoluteURL(path).URLByAppendingPathExtension("tmp") : absoluteURL(path)
let request = NSMutableURLRequest(URL: url)
request.HTTPMethod = "PUT"
let task = session.uploadTaskWithRequest(request, fromData: data) { (data, response, error) in
defer {
self.delegateNotify(.Modify(path: path), error: error)
}
if atomically {
self.moveItemAtPath((path as NSString).stringByAppendingPathExtension("tmp")!, toPath: path, completionHandler: completionHandler)
}
if let error = error {
// If there is no error, completionHandler has been executed by move command
completionHandler?(error: error)
}
}
task.taskDescription = self.dictionaryToJSON(["type": "Modify", "source": path])
task.resume()
}
public func searchFilesAtPath(path: String, recursive: Bool, query: String, foundItemHandler: ((FileObject) -> Void)?, completionHandler: ((files: [FileObject], error: ErrorType?) -> Void)) {
NotImplemented()
}
private func registerNotifcation(path: String, eventHandler: (() -> Void)) {
/* There is two ways to monitor folders chaging in Dropbox. Either using webooks
* which means you have to implement a server to translate it to push notifications
* or using apiv2 list_folder/longpoll method. The second one is implemeted here.
* Tough webhooks are much more efficient, longpoll is much simpler to implement!
* You can implemnt your own webhook service and replace this method accordingly.
*/
NotImplemented()
}
private func unregisterNotifcation(path: String) {
NotImplemented()
}
}
internal extension DropboxFileProvider {
private func mapToFileObject(jsonStr: String) -> DropboxFileObject? {
guard let json = self.jsonToDictionary(jsonStr) else { return nil }
return self.mapToFileObject(json)
}
private func mapToFileObject(json: [String: AnyObject]) -> DropboxFileObject? {
guard let name = json["name"] as? String else { return nil }
guard let path = json["path_display"] as? String else { return nil }
let href = NSURL(string: path)!
let size = (json["size"] as? NSNumber)?.longLongValue ?? -1
let serverTime = resolveDate(json["server_modified"] as? String ?? "")
let modifiedDate = resolveDate(json["client_modified"] as? String ?? "")
let isDirectory = (json[".tag"] as? String) == "folder"
let isReadonly = (json["sharing_info"]?["read_only"] as? NSNumber)?.boolValue ?? false
let id = json["id"] as? String
let rev = json["id"] as? String
return DropboxFileObject(absoluteURL: href, name: name, path: path, size: size, serverTime: serverTime, createdDate: nil, modifiedDate: modifiedDate, fileType: isDirectory ? .Directory : .Regular, isHidden: false, isReadOnly: isReadonly, id: id, rev: rev)
}
private func delegateNotify(operation: FileOperation, error: ErrorType?) {
dispatch_async(dispatch_get_main_queue(), {
if error == nil {
self.delegate?.fileproviderSucceed(self, operation: operation)
} else {
self.delegate?.fileproviderFailed(self, operation: operation)
}
})
}
}
// MARK: URLSession delegate
extension DropboxFileProvider: NSURLSessionDataDelegate, NSURLSessionDownloadDelegate {
public func URLSession(session: NSURLSession, downloadTask: NSURLSessionDownloadTask, didFinishDownloadingToURL location: NSURL) {
return
}
public func URLSession(session: NSURLSession, task: NSURLSessionTask, didSendBodyData bytesSent: Int64, totalBytesSent: Int64, totalBytesExpectedToSend: Int64) {
guard let desc = task.taskDescription, let json = jsonToDictionary(desc) else {
return
}
guard let type = json["type"] as? String, let source = json["source"] as? String else {
return
}
let dest = json["dest"] as? String
let op : FileOperation
switch type {
case "Create":
op = .Create(path: source)
case "Copy":
guard let dest = dest else { return }
op = .Copy(source: source, destination: dest)
case "Move":
guard let dest = dest else { return }
op = .Move(source: source, destination: dest)
case "Modify":
op = .Modify(path: source)
case "Remove":
op = .Remove(path: source)
case "Link":
guard let dest = dest else { return }
op = .Link(link: source, target: dest)
default:
return
}
let progress = Float(totalBytesSent) / Float(totalBytesExpectedToSend)
self.delegate?.fileproviderProgress(self, operation: op, progress: progress)
}
public func URLSession(session: NSURLSession, downloadTask: NSURLSessionDownloadTask, didWriteData bytesWritten: Int64, totalBytesWritten: Int64, totalBytesExpectedToWrite: Int64) {
guard let desc = downloadTask.taskDescription, let json = jsonToDictionary(desc), let source = json["source"] as? String, dest = json["dest"] as? String else {
return
}
self.delegate?.fileproviderProgress(self, operation: .Copy(source: source, destination: dest), progress: Float(totalBytesWritten) / Float(totalBytesExpectedToWrite))
}
}
+35
View File
@@ -0,0 +1,35 @@
//
// FileProvider iOS.h
// FileProvider iOS
//
// Created by Amir Abbas Mousavian on 5/6/95.
//
//
#if TARGET_OS_IPHONE || TARGET_IPHONE_SIMULATOR
#import <UIKit/UIKit.h>
//! Project version number for FileProvider iOS.
FOUNDATION_EXPORT double FileProvider_iOSVersionNumber;
//! Project version string for FileProvider iOS.
FOUNDATION_EXPORT const unsigned char FileProvider_iOSVersionString[];
#elif defined TARGET_OS_TV
#import <UIKit/UIKit.h>
//! Project version number for FileProvider tvOS.
FOUNDATION_EXPORT double FileProvider_tvOSVersionNumber;
//! Project version string for FileProvider tvOS.
FOUNDATION_EXPORT const unsigned char FileProvider_tvOSVersionString[];
#elif defined TARGET_OS_MAC
#import <Cocoa/Cocoa.h>
//! Project version number for FileProvider OSX.
FOUNDATION_EXPORT double FileProvider_OSXVersionNumber;
//! Project version string for FileProvider OSX.
FOUNDATION_EXPORT const unsigned char FileProvider_OSXVersionString[];
#else
// Unsupported platform
#endif
// In this header, you should import all the public headers of your framework using statements like #import <FileProvider_iOS/PublicHeader.h>
@@ -7,11 +7,12 @@
//
import Foundation
#if (iOS)
#if os(iOS) || os(tvOS)
import UIKit
#endif
#if (OSX)
import AppKit
public typealias ImageClass = UIImage
#elseif os(OSX)
import Cocoa
public typealias ImageClass = NSImage
#endif
public enum FileType: String {
@@ -63,6 +64,7 @@ extension NSCocoaError: FoundationErrorEnum {}
public class FileObject {
let absoluteURL: NSURL?
let name: String
let path: String
let size: Int64
let createdDate: NSDate?
let modifiedDate: NSDate?
@@ -70,9 +72,10 @@ public class FileObject {
let isHidden: Bool
let isReadOnly: Bool
init(absoluteURL: NSURL, name: String, size: Int64, createdDate: NSDate?, modifiedDate: NSDate?, fileType: FileType, isHidden: Bool, isReadOnly: Bool) {
init(absoluteURL: NSURL?, name: String, path: String, size: Int64, createdDate: NSDate?, modifiedDate: NSDate?, fileType: FileType, isHidden: Bool, isReadOnly: Bool) {
self.absoluteURL = absoluteURL
self.name = name
self.path = path
self.size = size
self.createdDate = createdDate
self.modifiedDate = modifiedDate
@@ -81,9 +84,10 @@ public class FileObject {
self.isReadOnly = isReadOnly
}
init(name: String, createdDate: NSDate?, modifiedDate: NSDate?, isHidden: Bool, isReadOnly: Bool) {
self.absoluteURL = NSURL()
init(name: String, path: String, createdDate: NSDate?, modifiedDate: NSDate?, isHidden: Bool, isReadOnly: Bool) {
self.absoluteURL = nil
self.name = name
self.path = path
self.size = -1
self.createdDate = createdDate
self.modifiedDate = modifiedDate
@@ -91,6 +95,14 @@ public class FileObject {
self.isHidden = isHidden
self.isReadOnly = isReadOnly
}
var isDirectory: Bool {
return self.fileType == .Directory
}
var isSymLink: Bool {
return self.fileType == .SymbolicLink
}
}
@@ -113,6 +125,8 @@ public protocol FileProviderBasic: class {
}
public protocol FileProviderOperations: FileProviderBasic {
var fileOperationDelegate : FileOperationDelegate? { get set }
func createFolder(folderName: String, atPath: String, completionHandler: SimpleCompletionHandler)
func createFile(fileAttribs: FileObject, atPath: String, contents data: NSData?, completionHandler: SimpleCompletionHandler)
func moveItemAtPath(path: String, toPath: String, overwrite: Bool, completionHandler: SimpleCompletionHandler)
@@ -134,6 +148,7 @@ public protocol FileProviderReadWrite: FileProviderBasic {
public protocol FileProviderMonitor: FileProviderBasic {
func registerNotifcation(path: String, eventHandler: (() -> Void))
func unregisterNotifcation(path: String)
func isRegisteredForNotification(path: String) -> Bool
}
public protocol FileProvider: FileProviderBasic, FileProviderOperations, FileProviderReadWrite {
@@ -145,7 +160,7 @@ extension FileProviderBasic {
return currentPath.stringByTrimmingCharactersInSet(NSCharacterSet(charactersInString: ". /"))
}
func absoluteURL(path: String? = nil) -> NSURL {
public func absoluteURL(path: String? = nil) -> NSURL {
let rpath: String
if let path = path {
rpath = path
@@ -161,10 +176,58 @@ extension FileProviderBasic {
return baseURL.URLByAppendingPathComponent(rpath)
}
} else {
return NSURL(fileURLWithPath: rpath)
return NSURL(fileURLWithPath: rpath).URLByStandardizingPath!
}
}
public func relativePathOf(url url: NSURL) -> String {
guard let baseURL = self.baseURL else { return url.absoluteString }
return url.URLByStandardizingPath!.absoluteString.stringByReplacingOccurrencesOfString(baseURL.absoluteString, withString: "/").stringByRemovingPercentEncoding!
}
internal func correctPath(path: String?) -> String? {
guard let path = path else { return nil }
return path.hasPrefix("/") ? path : "/" + path
}
public func fileByUniqueName(filePath: String) -> String {
let fileUrl = NSURL(fileURLWithPath: filePath)
let dirPath = fileUrl.URLByDeletingLastPathComponent?.path ?? ""
guard let fileName = fileUrl.URLByDeletingPathExtension?.lastPathComponent else {
return filePath
}
let fileExt = fileUrl.pathExtension ?? ""
var result = fileName
let group = dispatch_group_create()
dispatch_group_enter(group)
self.contentsOfDirectoryAtPath(dirPath) { (contents, error) in
var bareFileName = fileName
let number = Int(fileName.componentsSeparatedByString(" ").filter {
!$0.stringByTrimmingCharactersInSet(NSCharacterSet.whitespaceAndNewlineCharacterSet()).isEmpty
}.last ?? "noname")
if let _ = number {
result = fileName.componentsSeparatedByString(" ").filter {
!$0.stringByTrimmingCharactersInSet(NSCharacterSet.whitespaceAndNewlineCharacterSet()).isEmpty
}.dropLast().joinWithSeparator(" ")
bareFileName = result
}
var i = number ?? 2
let similiar = contents.map {
$0.absoluteURL?.lastPathComponent ?? $0.name
}.filter {
$0.hasPrefix(result) && (!fileExt.isEmpty && $0.hasSuffix("." + fileExt))
}
while similiar.contains(result + (!fileExt.isEmpty ? "." + fileExt : "")) {
result = "\(bareFileName) \(i)"
i += 1
}
dispatch_group_leave(group)
}
dispatch_group_wait(group, DISPATCH_TIME_FOREVER)
let finalFile = result + (!fileExt.isEmpty ? "." + fileExt : "")
return (dirPath as NSString).stringByAppendingPathComponent(finalFile)
}
internal func throwError(path: String, code: FoundationErrorEnum) -> NSError {
let fileURL = self.absoluteURL(path)
let domain: String
@@ -181,21 +244,25 @@ extension FileProviderBasic {
assert(false, "method not implemented")
}
internal func resolveRFCDate(httpDateString: String) -> NSDate? {
internal func resolveDate(dateString: String) -> NSDate? {
let dateFor: NSDateFormatter = NSDateFormatter()
dateFor.locale = NSLocale(localeIdentifier: "en_US")
dateFor.dateFormat = "EEE',' dd' 'MMM' 'yyyy HH':'mm':'ss zzz"
if let rfc1123 = dateFor.dateFromString(httpDateString) {
if let rfc1123 = dateFor.dateFromString(dateString) {
return rfc1123
}
dateFor.dateFormat = "EEEE',' dd'-'MMM'-'yy HH':'mm':'ss z"
if let rfc850 = dateFor.dateFromString(httpDateString) {
if let rfc850 = dateFor.dateFromString(dateString) {
return rfc850
}
dateFor.dateFormat = "EEE MMM d HH':'mm':'ss yyyy"
if let asctime = dateFor.dateFromString(httpDateString) {
if let asctime = dateFor.dateFromString(dateString) {
return asctime
}
dateFor.dateFormat = "yyyy'-'MM'-'dd'T'HH':'mm':'ssz"
if let isotime = dateFor.dateFromString(dateString) {
return isotime
}
//self.init()
return nil
}
@@ -218,17 +285,13 @@ extension FileProviderBasic {
}
}
#if (iOS)
public protocol ExtendedFileProvider: FileProvider {
func thumbnailOfFileAtPath(path: String, dimension: CGSize, completionHandler: ((image: UIImage?, error: ErrorType?) -> Void))
func thumbnailOfFileSupported(path: String) -> Bool
func propertiesOfFileSupported(path: String) -> Bool
func thumbnailOfFileAtPath(path: String, dimension: CGSize, completionHandler: ((image: ImageClass?, error: ErrorType?) -> Void))
func propertiesOfFileAtPath(path: String, completionHandler: ((propertiesDictionary: [String: AnyObject], keys: [String], error: ErrorType?) -> Void))
}
#elseif (OSX)
public protocol ExtendedFileProvider: FileProvider {
func thumbnailOfFileAtPath(path: String, dimension: CGSize, completionHandler: ((image: NSImage?, error: ErrorType?) -> Void))
func propertiesOfFileAtPath(path: String, completionHandler: ((propertiesDictionary: [String: AnyObject], keys: [String], error: ErrorType?) -> Void))
}
#endif
public enum FileOperation {
case Create (path: String)
@@ -237,12 +300,42 @@ public enum FileOperation {
case Modify (path: String)
case Remove (path: String)
case Link (link: String, target: String)
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"
}
}
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 protocol FileProviderDelegate {
public protocol FileProviderDelegate: class {
func fileproviderSucceed(fileProvider: FileProviderOperations, operation: FileOperation)
func fileproviderFailed(fileProvider: FileProviderOperations, operation: FileOperation)
func fileproviderProgress(fileProvider: FileProviderOperations, operation: FileOperation, 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
/// 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: ErrorType, operation: FileOperation) -> Bool
}
@@ -11,30 +11,39 @@ import Foundation
public final class LocalFileObject: FileObject {
let allocatedSize: Int64
init(absoluteURL: NSURL, name: String, size: Int64, allocatedSize: Int64, createdDate: NSDate?, modifiedDate: NSDate?, fileType: FileType, isHidden: Bool, isReadOnly: Bool) {
init(absoluteURL: NSURL, name: String, path: String, size: Int64, allocatedSize: Int64, createdDate: NSDate?, modifiedDate: NSDate?, fileType: FileType, isHidden: Bool, isReadOnly: Bool) {
self.allocatedSize = allocatedSize
super.init(absoluteURL: absoluteURL, name: name, size: size, createdDate: createdDate, modifiedDate: modifiedDate, fileType: fileType, isHidden: isHidden, isReadOnly: isReadOnly)
super.init(absoluteURL: absoluteURL, name: name, path: path, size: size, createdDate: createdDate, modifiedDate: modifiedDate, fileType: fileType, isHidden: isHidden, isReadOnly: isReadOnly)
}
}
public class LocalFileProvider: FileProvider, FileProviderMonitor {
public let type = "NSFileManager"
public let type = "Local"
public var isPathRelative: Bool = true
public var baseURL: NSURL? = LocalFileProvider.defaultBaseURL()
public var currentPath: String = ""
public var dispatch_queue: dispatch_queue_t
public var delegate: FileProviderDelegate?
public var operation_queue: dispatch_queue_t
public weak var delegate: FileProviderDelegate?
public let credential: NSURLCredential? = nil
let fileManager = NSFileManager()
public let fileManager = NSFileManager()
public let opFileManager = NSFileManager()
private var fileProviderManagerDelegate: LocalFileProviderManagerDelegate? = nil
init () {
dispatch_queue = dispatch_queue_create("FileProvider.\(type)", DISPATCH_QUEUE_SERIAL)
dispatch_queue = dispatch_queue_create("FileProvider.\(type)", DISPATCH_QUEUE_CONCURRENT)
operation_queue = dispatch_queue_create("FileProvider.\(type).Operation", DISPATCH_QUEUE_SERIAL)
fileProviderManagerDelegate = LocalFileProviderManagerDelegate(provider: self)
opFileManager.delegate = fileProviderManagerDelegate
}
init (baseURL: NSURL) {
self.baseURL = baseURL
dispatch_queue = dispatch_queue_create("FileProvider.\(type)", DISPATCH_QUEUE_SERIAL)
dispatch_queue = dispatch_queue_create("FileProvider.\(type)", DISPATCH_QUEUE_CONCURRENT)
operation_queue = dispatch_queue_create("FileProvider.\(type).Operation", DISPATCH_QUEUE_SERIAL)
fileProviderManagerDelegate = LocalFileProviderManagerDelegate(provider: self)
opFileManager.delegate = fileProviderManagerDelegate
}
private static func defaultBaseURL() -> NSURL {
@@ -56,7 +65,7 @@ public class LocalFileProvider: FileProvider, FileProviderMonitor {
}
}
private func attributesOfItemAtURL(fileURL: NSURL) -> LocalFileObject {
internal func attributesOfItemAtURL(fileURL: NSURL) -> LocalFileObject {
var namev, sizev, allocated, filetypev, creationDatev, modifiedDatev, hiddenv, readonlyv: AnyObject?
_ = try? fileURL.getResourceValue(&namev, forKey: NSURLNameKey)
_ = try? fileURL.getResourceValue(&sizev, forKey: NSURLFileSizeKey)
@@ -66,7 +75,13 @@ public class LocalFileProvider: FileProvider, FileProviderMonitor {
_ = try? fileURL.getResourceValue(&filetypev, forKey: NSURLFileResourceTypeKey)
_ = try? fileURL.getResourceValue(&hiddenv, forKey: NSURLIsHiddenKey)
_ = try? fileURL.getResourceValue(&readonlyv, forKey: NSURLVolumeIsReadOnlyKey)
let fileAttr = LocalFileObject(absoluteURL: fileURL, name: namev as! String, size: sizev?.longLongValue ?? -1, allocatedSize: allocated?.longLongValue ?? -1, createdDate: creationDatev as? NSDate, modifiedDate: modifiedDatev as? NSDate, fileType: FileType(urlResourceTypeValue: filetypev as? String ?? ""), isHidden: hiddenv?.boolValue ?? false, isReadOnly: readonlyv?.boolValue ?? false)
let path: String
if isPathRelative {
path = self.relativePathOf(url: fileURL)
} else {
path = fileURL.path!
}
let fileAttr = LocalFileObject(absoluteURL: fileURL, name: namev as! String, path: path, size: sizev?.longLongValue ?? -1, allocatedSize: allocated?.longLongValue ?? -1, createdDate: creationDatev as? NSDate, modifiedDate: modifiedDatev as? NSDate, fileType: FileType(urlResourceTypeValue: filetypev as? String ?? ""), isHidden: hiddenv?.boolValue ?? false, isReadOnly: readonlyv?.boolValue ?? false)
return fileAttr
}
@@ -76,10 +91,12 @@ public class LocalFileProvider: FileProvider, FileProviderMonitor {
}
}
public weak var fileOperationDelegate : FileOperationDelegate?
public func createFolder(folderName: String, atPath: String, completionHandler: SimpleCompletionHandler) {
dispatch_async(dispatch_queue) {
dispatch_async(operation_queue) {
do {
try self.fileManager.createDirectoryAtURL(self.absoluteURL(atPath).URLByAppendingPathComponent(folderName), withIntermediateDirectories: true, attributes: [:])
try self.opFileManager.createDirectoryAtURL(self.absoluteURL(atPath).URLByAppendingPathComponent(folderName), withIntermediateDirectories: true, attributes: [:])
completionHandler?(error: nil)
dispatch_async(dispatch_get_main_queue(), {
self.delegate?.fileproviderSucceed(self, operation: .Create(path: (atPath as NSString).stringByAppendingPathComponent(folderName) + "/"))
@@ -94,7 +111,7 @@ public class LocalFileProvider: FileProvider, FileProviderMonitor {
}
public func createFile(fileAttribs: FileObject, atPath: String, contents data: NSData?, completionHandler: SimpleCompletionHandler) {
dispatch_async(dispatch_queue) {
dispatch_async(operation_queue) {
let fileURL = self.absoluteURL(atPath).URLByAppendingPathComponent(fileAttribs.name)
var attributes = [String : AnyObject]()
if let createdDate = fileAttribs.createdDate {
@@ -106,7 +123,7 @@ public class LocalFileProvider: FileProvider, FileProviderMonitor {
if fileAttribs.isReadOnly {
attributes[NSFilePosixPermissions] = NSNumber(short: 365 /*555 o*/)
}
let success = self.fileManager.createFileAtPath(fileURL.path!, contents: data, attributes: attributes)
let success = self.opFileManager.createFileAtPath(fileURL.path!, contents: data, attributes: attributes)
if success {
do {
try fileURL.setResourceValue(fileAttribs.isHidden, forKey: NSURLIsHiddenKey)
@@ -126,13 +143,13 @@ public class LocalFileProvider: FileProvider, FileProviderMonitor {
public func moveItemAtPath(path: String, toPath: String, overwrite: Bool = false, completionHandler: SimpleCompletionHandler) {
// FIXME: progress
dispatch_async(dispatch_queue) {
dispatch_async(operation_queue) {
if !overwrite && self.fileManager.fileExistsAtPath(self.absoluteURL(toPath).path ?? "") {
completionHandler?(error: self.throwError(toPath, code: NSURLError.CannotMoveFile))
return
}
do {
try self.fileManager.moveItemAtURL(self.absoluteURL(path), toURL: self.absoluteURL(toPath))
try self.opFileManager.moveItemAtURL(self.absoluteURL(path), toURL: self.absoluteURL(toPath))
completionHandler?(error: nil)
dispatch_async(dispatch_get_main_queue(), {
self.delegate?.fileproviderSucceed(self, operation: .Move(source: path, destination: toPath))
@@ -148,13 +165,13 @@ public class LocalFileProvider: FileProvider, FileProviderMonitor {
public func copyItemAtPath(path: String, toPath: String, overwrite: Bool = false, completionHandler: SimpleCompletionHandler) {
// FIXME: progress, for files > 100mb, monitor file by another thread, for dirs check copied items count
dispatch_async(dispatch_queue) {
dispatch_async(operation_queue) {
if !overwrite && self.fileManager.fileExistsAtPath(self.absoluteURL(toPath).path ?? "") {
completionHandler?(error: self.throwError(toPath, code: NSURLError.CannotWriteToFile))
return
}
do {
try self.fileManager.copyItemAtURL(self.absoluteURL(path), toURL: self.absoluteURL(toPath))
try self.opFileManager.copyItemAtURL(self.absoluteURL(path), toURL: self.absoluteURL(toPath))
completionHandler?(error: nil)
dispatch_async(dispatch_get_main_queue(), {
self.delegate?.fileproviderSucceed(self, operation: .Copy(source: path, destination: toPath))
@@ -169,9 +186,9 @@ public class LocalFileProvider: FileProvider, FileProviderMonitor {
}
public func removeItemAtPath(path: String, completionHandler: SimpleCompletionHandler) {
dispatch_async(dispatch_queue) {
dispatch_async(operation_queue) {
do {
try self.fileManager.removeItemAtURL(self.absoluteURL(path))
try self.opFileManager.removeItemAtURL(self.absoluteURL(path))
completionHandler?(error: nil)
dispatch_async(dispatch_get_main_queue(), {
self.delegate?.fileproviderSucceed(self, operation: .Remove(path: path))
@@ -186,9 +203,9 @@ public class LocalFileProvider: FileProvider, FileProviderMonitor {
}
public func copyLocalFileToPath(localFile: NSURL, toPath: String, completionHandler: SimpleCompletionHandler) {
dispatch_async(dispatch_queue) {
dispatch_async(operation_queue) {
do {
try self.fileManager.copyItemAtURL(localFile, toURL: self.absoluteURL(toPath))
try self.opFileManager.copyItemAtURL(localFile, toURL: self.absoluteURL(toPath))
completionHandler?(error: nil)
dispatch_async(dispatch_get_main_queue(), {
self.delegate?.fileproviderSucceed(self, operation: .Copy(source: localFile.absoluteString, destination: toPath))
@@ -203,9 +220,9 @@ public class LocalFileProvider: FileProvider, FileProviderMonitor {
}
public func copyPathToLocalFile(path: String, toLocalURL: NSURL, completionHandler: SimpleCompletionHandler) {
dispatch_async(dispatch_queue) {
dispatch_async(operation_queue) {
do {
try self.fileManager.copyItemAtURL(self.absoluteURL(path), toURL: toLocalURL)
try self.opFileManager.copyItemAtURL(self.absoluteURL(path), toURL: toLocalURL)
completionHandler?(error: nil)
dispatch_async(dispatch_get_main_queue(), {
self.delegate?.fileproviderSucceed(self, operation: .Copy(source: path, destination: toLocalURL.absoluteString))
@@ -231,7 +248,7 @@ public class LocalFileProvider: FileProvider, FileProviderMonitor {
// So we have to fallback to POSIX provided methods
dispatch_async(dispatch_queue) {
let aPath = self.absoluteURL(path).path!
if self.attributesOfItemAtURL(self.absoluteURL(path)).fileType == .Directory {
if self.attributesOfItemAtURL(self.absoluteURL(path)).isDirectory {
self.throwError(path, code: NSURLError.FileIsDirectory)
}
if !self.fileManager.fileExistsAtPath(aPath) {
@@ -256,7 +273,7 @@ public class LocalFileProvider: FileProvider, FileProviderMonitor {
}
public func writeContentsAtPath(path: String, contents data: NSData, atomically: Bool, completionHandler: SimpleCompletionHandler) {
dispatch_async(dispatch_queue) {
dispatch_async(operation_queue) {
data.writeToURL(self.absoluteURL(path), atomically: atomically)
dispatch_async(dispatch_get_main_queue(), {
self.delegate?.fileproviderSucceed(self, operation: .Modify(path: path))
@@ -282,38 +299,46 @@ public class LocalFileProvider: FileProvider, FileProviderMonitor {
}
}
private var monitorDictionary = [String : LocalFolderMonitor]()
private var monitors = [LocalFolderMonitor]()
public func registerNotifcation(path: String, eventHandler: (() -> Void)) {
self.unregisterNotifcation(path)
let absurl = self.absoluteURL(path)
var isdirv: AnyObject?
do {
try absoluteURL(path).getResourceValue(&isdirv, forKey: NSURLIsDirectoryKey)
try absurl.getResourceValue(&isdirv, forKey: NSURLIsDirectoryKey)
} catch _ {
}
if !(isdirv?.boolValue ?? false) {
return
}
let monitor = LocalFolderMonitor(url: absoluteURL(path)) {
let monitor = LocalFolderMonitor(url: absurl) {
eventHandler()
}
monitor.start()
monitorDictionary[path] = monitor
monitors.append(monitor)
}
public func unregisterNotifcation(path: String) {
if let prevMonitor = monitorDictionary[path] {
prevMonitor.stop()
monitorDictionary.removeValueForKey(path)
var removedMonitor: LocalFolderMonitor?
for (i, monitor) in monitors.enumerate() {
if self.relativePathOf(url: monitor.url) == path {
removedMonitor = monitors.removeAtIndex(i)
}
}
removedMonitor?.stop()
}
public func isRegisteredForNotification(path: String) -> Bool {
return monitors.map( { self.relativePathOf(url: $0.url) } ).contains(path)
}
}
extension LocalFileProvider {
public func createSymbolicLinkAtPath(path: String, withDestinationPath destPath: String, completionHandler: SimpleCompletionHandler) {
dispatch_async(dispatch_queue) {
dispatch_async(operation_queue) {
do {
try self.fileManager.createSymbolicLinkAtURL(self.absoluteURL(path), withDestinationURL: self.absoluteURL(destPath))
try self.opFileManager.createSymbolicLinkAtURL(self.absoluteURL(path), withDestinationURL: self.absoluteURL(destPath))
completionHandler?(error: nil)
dispatch_async(dispatch_get_main_queue(), {
self.delegate?.fileproviderSucceed(self, operation: .Link(link: path, target: destPath))
@@ -328,15 +353,95 @@ extension LocalFileProvider {
}
}
private class LocalFolderMonitor {
class LocalFileProviderManagerDelegate: NSObject, NSFileManagerDelegate {
weak var provider: LocalFileProvider?
init(provider: LocalFileProvider) {
self.provider = provider
}
func fileManager(fileManager: NSFileManager, shouldCopyItemAtURL srcURL: NSURL, toURL dstURL: NSURL) -> Bool {
guard let provider = self.provider, delegate = provider.fileOperationDelegate else {
return true
}
let srcPath = provider.relativePathOf(url: srcURL)
let dstPath = provider.relativePathOf(url: dstURL)
return delegate.fileProvider(provider, shouldDoOperation: .Copy(source: srcPath, destination: dstPath))
}
func fileManager(fileManager: NSFileManager, shouldMoveItemAtURL srcURL: NSURL, toURL dstURL: NSURL) -> Bool {
guard let provider = self.provider, delegate = provider.fileOperationDelegate else {
return true
}
let srcPath = provider.relativePathOf(url: srcURL)
let dstPath = provider.relativePathOf(url: dstURL)
return delegate.fileProvider(provider, shouldDoOperation: .Move(source: srcPath, destination: dstPath))
}
func fileManager(fileManager: NSFileManager, shouldRemoveItemAtURL URL: NSURL) -> Bool {
guard let provider = self.provider, delegate = provider.fileOperationDelegate else {
return true
}
let path = provider.relativePathOf(url: URL)
return delegate.fileProvider(provider, shouldDoOperation: .Remove(path: path))
}
func fileManager(fileManager: NSFileManager, shouldLinkItemAtURL srcURL: NSURL, toURL dstURL: NSURL) -> Bool {
guard let provider = self.provider, delegate = provider.fileOperationDelegate else {
return true
}
let srcPath = provider.relativePathOf(url: srcURL)
let dstPath = provider.relativePathOf(url: dstURL)
return delegate.fileProvider(provider, shouldDoOperation: .Link(link: srcPath, target: dstPath))
}
func fileManager(fileManager: NSFileManager, shouldProceedAfterError error: NSError, copyingItemAtURL srcURL: NSURL, toURL dstURL: NSURL) -> Bool {
guard let provider = self.provider, delegate = provider.fileOperationDelegate else {
return false
}
let srcPath = provider.relativePathOf(url: srcURL)
let dstPath = provider.relativePathOf(url: dstURL)
return delegate.fileProvider(provider, shouldProceedAfterError: error, operation: .Copy(source: srcPath, destination: dstPath))
}
func fileManager(fileManager: NSFileManager, shouldProceedAfterError error: NSError, movingItemAtURL srcURL: NSURL, toURL dstURL: NSURL) -> Bool {
guard let provider = self.provider, delegate = provider.fileOperationDelegate else {
return false
}
let srcPath = provider.relativePathOf(url: srcURL)
let dstPath = provider.relativePathOf(url: dstURL)
return delegate.fileProvider(provider, shouldProceedAfterError: error, operation: .Move(source: srcPath, destination: dstPath))
}
func fileManager(fileManager: NSFileManager, shouldProceedAfterError error: NSError, removingItemAtURL URL: NSURL) -> Bool {
guard let provider = self.provider, delegate = provider.fileOperationDelegate else {
return false
}
let path = provider.relativePathOf(url: URL)
return delegate.fileProvider(provider, shouldProceedAfterError: error, operation: .Remove(path: path))
}
func fileManager(fileManager: NSFileManager, shouldProceedAfterError error: NSError, linkingItemAtURL srcURL: NSURL, toURL dstURL: NSURL) -> Bool {
guard let provider = self.provider, delegate = provider.fileOperationDelegate else {
return false
}
let srcPath = provider.relativePathOf(url: srcURL)
let dstPath = provider.relativePathOf(url: dstURL)
return delegate.fileProvider(provider, shouldProceedAfterError: error, operation: .Link(link: srcPath, target: dstPath))
}
}
internal class LocalFolderMonitor {
private let source: dispatch_source_t
private let descriptor: CInt
private let qq: dispatch_queue_t = dispatch_get_main_queue()
private let qq: dispatch_queue_t = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0)
private var state: Bool = false
private var monitoredTime: NSTimeInterval = NSDate().timeIntervalSinceReferenceDate
var url: NSURL
/// Creates a folder monitor object with monitoring enabled.
init(url: NSURL, handler: ()->Void) {
self.url = url
descriptor = open(url.fileSystemRepresentation, O_EVTONLY)
source = dispatch_source_create(
@@ -345,8 +450,23 @@ private class LocalFolderMonitor {
DISPATCH_VNODE_WRITE,
qq
)
dispatch_source_set_event_handler(source, handler)
// Folder monitoring is recursive and deep. Monitoring a root folder may be very costly
// We have a 0.2 second delay to ensure we wont call handler 1000s times when there is
// a huge file operation. This ensures app will work smoothly while this 250 milisec won't
// affect user experince much
let main_handler: ()->Void = {
if NSDate().timeIntervalSinceReferenceDate < self.monitoredTime + 0.2 {
return
}
self.monitoredTime = NSDate().timeIntervalSinceReferenceDate
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, Int64(NSEC_PER_SEC) / 4), dispatch_get_main_queue(), {
handler()
})
}
dispatch_source_set_event_handler(source, main_handler)
dispatch_source_set_cancel_handler(source) {
close(self.descriptor)
}
start()
}
@@ -367,7 +487,6 @@ private class LocalFolderMonitor {
}
deinit {
close(descriptor)
dispatch_source_cancel(source)
}
}
+194
View File
@@ -0,0 +1,194 @@
//
// SMBTransmitter.swift
// FileProvider
//
// Created by Amir Abbas Mousavian.
// Copyright © 2016 Mousavian. Distributed under MIT license.
//
import Foundation
internal func encode<T>(inout value: T) -> NSData {
return withUnsafePointer(&value) { p in
NSData(bytes: p, length: sizeofValue(value))
}
}
internal func encode<T>(value: T) -> NSData {
var value = value
return withUnsafePointer(&value) { p in
NSData(bytes: p, length: sizeofValue(value))
}
}
internal func decode<T>(data: NSData) -> T {
let pointer = UnsafeMutablePointer<T>.alloc(sizeof(T.Type))
data.getBytes(pointer, length: sizeof(T.Type))
return pointer.move()
}
// This client implementation is for little-endian platform, namely x86, x64 & arm
// For big-endian platforms like PowerPC, there must be a huge overhaul
class SMBProtocolClient: TCPSocketClient {
var currentMessageID: UInt64 = 0
func negotiateToSMB2() -> SMB2.NegotiateResponse? {
let smbHeader = SMB2.Header(command: .NEGOTIATE, creditRequestResponse: 126, messageId: messageId(), treeId: 0, sessionId: 0)
currentMessageID += 1
let negMessage = SMB2.NegotiateRequest(request: SMB2.NegotiateRequest.Header(capabilities: []))
SMBProtocolClient.createSMB2Message(smbHeader, message: negMessage)
do {
try self.send(data: nil)
} catch _ {
return nil
}
self.waitUntilResponse()
let response = try? SMBProtocolClient.digestSMB2Message(dataReceived)
return response??.message as? SMB2.NegotiateResponse
}
func sessionSetupForSMB2() -> SMB2.SessionSetupResponse? {
return nil
}
func messageId() -> UInt64 {
defer {
currentMessageID += 1
}
return currentMessageID
}
// MARK: create and analyse messages
class func determineSMBVersion(data: NSData) -> Float {
var smbverChar: Int8 = 0
data.getBytes(&smbverChar, length: 1)
let version = 0 - smbverChar
return Float(version)
}
class func digestSMBMessage(data: NSData) throws -> (header: SMB1.Header, blocks: [(params: [UInt16], message: NSData?)]) {
guard data.length > 30 else {
throw NSURLError.BadServerResponse
}
var buffer = [UInt8](count: data.length, repeatedValue: 0)
guard determineSMBVersion(data) == 1 else {
throw SMBFileProviderError.IncompatibleHeader
}
let headersize = sizeof(SMB1.Header.self)
let header: SMB1.Header = decode(data)
var blocks = [(params: [UInt16], message: NSData?)]()
var offset = headersize
while offset < data.length {
let paramWords: [UInt16]
let paramWordsCount = Int(buffer[offset])
guard data.length > (paramWordsCount * 2 + offset) else {
throw SMBFileProviderError.IncorrectParamsLength
}
offset += sizeof(UInt8)
var rawParamWords = [UInt8](buffer[offset..<(offset + paramWordsCount * 2)])
let paramData = NSData(bytesNoCopy: &rawParamWords, length: rawParamWords.count)
paramWords = decode(paramData)
offset += paramWordsCount * 2
let messageBytesCountLittleEndian = [UInt8](buffer[offset...(offset + 1)])
let messageBytesCount = Int(UnsafePointer<UInt16>(messageBytesCountLittleEndian).memory)
offset += sizeof(UInt16)
guard data.length >= (offset + messageBytesCount) else {
throw SMBFileProviderError.IncorrectMessageLength
}
var rawMessage = [UInt8](buffer[offset..<(offset + messageBytesCount)])
offset += messageBytesCount
let message = NSData(bytes: &rawMessage, length: rawMessage.count)
blocks.append((params: paramWords, message: message))
}
return (header, blocks)
}
class func digestSMB2Message(data: NSData) throws -> (header: SMB2.Header, message: SMBResponse?)? {
guard data.length > 65 else {
throw NSURLError.BadServerResponse
}
guard determineSMBVersion(data) == 2 else {
throw SMBFileProviderError.IncompatibleHeader
}
let headersize = sizeof(SMB2.Header.self)
let headerData = data.subdataWithRange(NSRange(location: 0, length: headersize))
let messageSize = data.length - headersize
let messageData = data.subdataWithRange(NSRange(location: headersize, length: messageSize))
let header: SMB2.Header = decode(headerData)
switch header.command {
case .NEGOTIATE:
return (header, SMB2.NegotiateResponse(data: messageData))
case .SESSION_SETUP:
return (header, SMB2.SessionSetupResponse(data: messageData))
case .LOGOFF:
return (header, SMB2.LogOff(data: messageData))
case .TREE_CONNECT:
return (header, SMB2.TreeConnectResponse(data: messageData))
case .TREE_DISCONNECT:
return (header, SMB2.TreeDisconnect(data: messageData))
case .CREATE:
return (header, SMB2.CreateResponse(data: messageData))
case .CLOSE:
return (header, SMB2.CloseResponse(data: messageData))
case .FLUSH:
return (header, SMB2.FlushResponse(data: messageData))
case .READ:
return (header, nil) // FIXME:
case .WRITE:
return (header, nil) // FIXME:
case .LOCK:
return (header, nil) // FIXME:
case .IOCTL:
return (header, nil)
case .CANCEL:
return (header, nil)
case .ECHO:
return (header, SMB2.Echo(data: messageData))
case .QUERY_DIRECTORY:
return (header, nil) // FIXME:
case .CHANGE_NOTIFY:
return (header, nil) // FIXME:
case .QUERY_INFO:
return (header, nil) // FIXME:
case .SET_INFO:
return (header, nil) // FIXME:
case .OPLOCK_BREAK:
return (header, nil) // FIXME:
case .INVALID:
throw SMBFileProviderError.InvalidCommand
}
}
class func createSMBMessage(header: SMB1.Header, blocks: [(params: NSData?, message: NSData?)]) -> NSData {
var headerv = header
let result = NSMutableData(data: encode(&headerv))
for block in blocks {
var paramWordsCount = UInt8(block.params?.length ?? 0)
result.appendBytes(&paramWordsCount, length: sizeofValue(paramWordsCount))
if let params = block.params {
result.appendData(params)
}
var messageLen = UInt16(block.message?.length ?? 0)
result.appendBytes(&messageLen, length: sizeofValue(messageLen))
if let message = block.message {
result.appendData(message)
}
}
return result
}
class func createSMB2Message(header: SMB2.Header, message: SMBRequest) -> NSData {
var headerv = header
let result = NSMutableData(data: encode(&headerv))
result.appendData(message.data())
return result
}
}
protocol FileProviderSMBHeader {
var protocolID: UInt32 { get }
static var protocolConst: UInt32 { get }
}
@@ -14,7 +14,7 @@ class SMBFileProvider: FileProvider, FileProviderMonitor {
var baseURL: NSURL?
var currentPath: String = ""
var dispatch_queue: dispatch_queue_t
var delegate: FileProviderDelegate?
weak var delegate: FileProviderDelegate?
let credential: NSURLCredential?
typealias FileObjectClass = FileObject
@@ -40,6 +40,8 @@ class SMBFileProvider: FileProvider, FileProviderMonitor {
NotImplemented()
}
weak var fileOperationDelegate: FileOperationDelegate?
func createFolder(folderName: String, atPath: String, completionHandler: SimpleCompletionHandler) {
NotImplemented()
}
@@ -91,7 +93,10 @@ class SMBFileProvider: FileProvider, FileProviderMonitor {
func unregisterNotifcation(path: String) {
NotImplemented()
}
func isRegisteredForNotification(path: String) -> Bool {
return false
}
}
// MARK: basic CIFS interactivity
+217
View File
@@ -0,0 +1,217 @@
//
// CIFSTypes.swift
// ExtDownloader
//
// Created by Amir Abbas Mousavian on 4/30/95.
// Copyright © 1395 Mousavian. All rights reserved.
//
import Foundation
// SMB/CIFS Types
struct SMB1 {
struct Header { // 32 bytes
// header is always \u{ff}SMB
let protocolID: UInt32
static let protocolConst: UInt32 = 0x424d53ff
private var _command: UInt8
var command: Command {
get {
return Command(rawValue: _command) ?? .INVALID
}
set {
_command = newValue.rawValue
}
}
// error messages from the server to the client
private var _status: (UInt8, UInt8, UInt8, UInt8)
var error: (Class: UInt8, code: UInt16) {
get {
return (_status.0, UInt16(_status.2) + (UInt16(_status.3) << 8))
}
set {
_status = (newValue.Class, 0, UInt8(newValue.code & 0xff), UInt8(newValue.code >> 8))
}
}
var ntStatus: UInt32 {
get {
let statusLo = UInt32(_status.0) + UInt32(_status.1) << 8
let statusHi = UInt32(_status.2) + UInt32(_status.3) << 8
return statusHi << 16 + statusLo
}
set {
_status = (UInt8(newValue & 0xff), UInt8(newValue >> 8 & 0xff), UInt8(newValue >> 16 & 0xff), UInt8(newValue >> 24 & 0xff))
}
}
var flags: Flags
var flags2: Flags2
var pidHigh: UInt16
// encryption key used for validating messages over connectionless transports
private var _securityKey: (UInt16, UInt16)
var securityKey: UInt32 {
get {
return UInt32(_securityKey.1) << 16 + UInt32(_securityKey.0)
}
set {
_securityKey = (UInt16(newValue & 0xffff), UInt16(newValue >> 16))
}
}
/// Connection identifier
var securityCID: UInt16
/// Identifier of the sequence of a message over connectionless transports
var securitySequenceNumber: UInt16
private var ununsed: UInt16
var treeId: UInt16
var pidLow: UInt16
var userId: UInt16
var multiplexId: UInt16
var pid: UInt32 {
get {
return UInt32(pidLow) + UInt32(pidHigh) << 16
}
set {
pidLow = UInt16(newValue & 0xffff)
pidHigh = UInt16(newValue >> 16)
}
}
init(command: Command, ntStatus: UInt32 = 0, flags: Flags, flags2: Flags2 = [.LONG_NAMES, .ERR_STATUS, .UNICODE], securityKey: UInt32 = 0, securityCID: UInt16 = 0, securitySequenceNumber: UInt16 = 0, treeId: UInt16, pid: UInt32, userId: UInt16, multiplexId: UInt16) {
self.protocolID = Header.protocolConst
self._command = command.rawValue
_status = (UInt8(ntStatus & 0xff), UInt8(ntStatus >> 8 & 0xff), UInt8(ntStatus >> 16 & 0xff), UInt8(ntStatus >> 24 & 0xff))
self.flags = flags
self.flags2 = flags2
self._securityKey = (UInt16(securityKey & 0xffff), UInt16(securityKey >> 16))
self.securityCID = securityCID
self.securitySequenceNumber = securitySequenceNumber
self.ununsed = 0
self.treeId = treeId
self.pidLow = UInt16(pid & 0xffff)
self.pidHigh = UInt16(pid >> 16)
self.userId = userId
self.multiplexId = multiplexId
}
}
struct Flags: OptionSetType {
let rawValue: UInt8
init(rawValue: UInt8) {
self.rawValue = rawValue
}
/** This bit is set (1) in the NEGOTIATE (0x72) Response if the server supports
* LOCK_AND_READ (0x13) and WRITE_AND_UNLOCK (0x14) commands. */
static let LOCK_AND_READ_OK = Flags(rawValue: 0x01)
static let BUF_AVAIL = Flags(rawValue: 0x02)
static let CASE_INSENSITIVE = Flags(rawValue: 0x08)
static let CANONICALIZED_PATHS = Flags(rawValue: 0x10)
static let OPLOCK = Flags(rawValue: 0x20)
static let OPBATCH = Flags(rawValue: 0x40)
/** When on, this message is being sent from the server in response to a client request. */
static let REPLY = Flags(rawValue: 0x80)
}
struct Flags2: OptionSetType {
let rawValue: UInt16
init(rawValue: UInt16) {
self.rawValue = rawValue
}
/** Client: the message MAY contain long file names. */
static let LONG_NAMES = Flags2(rawValue: 0x0001)
/** Client: the client is aware of extended attributes (EAs). */
static let EAS = Flags2(rawValue: 0x0002)
/** Client: the client is requesting signing (if signing is not yet active) or the message
* being sent is signed. This bit is used on the SMB header of an SESSION_SETUP_ANDX */
static let SMB_SECURITY_SIGNATURE = Flags2(rawValue: 0x0004)
/// Reserved but not implemented.
static let IS_LONG_NAME = Flags2(rawValue: 0x0040)
/** client aware of Extended Security negotiation */
static let EXT_SEC = Flags2(rawValue: 0x0800)
/** any pathnames in this SMB SHOULD be resolved in the Distributed File System (DFS) */
static let DFS = Flags2(rawValue: 0x1000)
/**
* This flag is useful only on a read request. If the bit is set, then the client MAY read
* the file if the client does not have read permission but does have execute permission. */
static let PAGING_IO = Flags2(rawValue: 0x2000) // READ_IF_EXECUTE
/**
* Client: the server MUST return errors as 32-bit NTSTATUS codes in the response
* Server: the Status field in the header is formatted as an NTSTATUS cod */
static let ERR_STATUS = Flags2(rawValue: 0x4000)
/**
* Each field that contains a string in this SMB message MUST be encoded
* as an array of 16-bit Unicode characters */
static let UNICODE = Flags2(rawValue: 0x8000)
}
enum Command: UInt8 {
case CREATE_DIRECTORY = 0x00
case DELETE_DIRECTORY = 0x01
case OPEN = 0x02
case CREATE = 0x03
case CLOSE = 0x04
case FLUSH = 0x05
case DELETE = 0x06
case RENAME = 0x07
case QUERY_INFORMATION = 0x08
case SET_INFORMATION = 0x09
case READ = 0x0A
case WRITE = 0x0B
case LOCK_BYTE_RANGE = 0x0C
case UNLOCK_BYTE_RANGE = 0x0D
case CREATE_TEMPORARY = 0x0E
case CREATE_NEW = 0x0F
case CHECK_DIRECTORY = 0x10
case PROCESS_EXIT = 0x11
case SEEK = 0x12
case LOCK_AND_READ = 0x13
case WRITE_AND_UNLOCK = 0x14
case READ_RAW = 0x1A
case READ_MPX = 0x1B
case READ_MPX_SECONDARY = 0x1C
case WRITE_RAW = 0x1D
case WRITE_MPX = 0x1E
case WRITE_COMPLETE = 0x20
case SET_INFORMATION2 = 0x22
case QUERY_INFORMATION2 = 0x23
case LOCKING_ANDX = 0x24
case TRANSACTION = 0x25
case TRANSACTION_SECONDARY = 0x26
case IOCTL = 0x27
case IOCTL_SECONDARY = 0x28
case COPY = 0x29
case MOVE = 0x2A
case ECHO = 0x2B
case WRITE_AND_CLOSE = 0x2C
case OPEN_ANDX = 0x2D
case READ_ANDX = 0x2E
case WRITE_ANDX = 0x2F
case CLOSE_AND_TREE_DISC = 0x31
case TRANSACTION2 = 0x32
case TRANSACTION2_SECONDARY = 0x33
case FIND_CLOSE2 = 0x34
case FIND_NOTIFY_CLOSE = 0x35
case TREE_CONNECT = 0x70
case TREE_DISCONNECT = 0x71
case NEGOTIATE = 0x72
case SESSION_SETUP_ANDX = 0x73
case LOGOFF_ANDX = 0x74
case TREE_CONNECT_ANDX = 0x75
case QUERY_INFORMATION_DISK = 0x80
case SEARCH = 0x81
case FIND = 0x82
case FIND_UNIQUE = 0x83
case NT_TRANSACT = 0xA0
case NT_TRANSACT_SECONDARY = 0xA1
case NT_CREATE_ANDX = 0xA2
case NT_CANCEL = 0xA4
case OPEN_PRINT_FILE = 0xC0
case WRITE_PRINT_FILE = 0xC1
case CLOSE_PRINT_FILE = 0xC2
case GET_PRINT_QUEUE = 0xC3
case READ_BULK = 0xD8
case WRITE_BULK = 0xD9
case WRITE_BULK_DATA = 0xDA
case INVALID = 0xFE
}
}
+49
View File
@@ -0,0 +1,49 @@
//
// SMB2DataTypes.swift
// ExtDownloader
//
// Created by Amir Abbas Mousavian on 4/30/95.
// Copyright © 1395 Mousavian. All rights reserved.
//
import Foundation
protocol SMBRequest {
func data() -> NSData
}
protocol SMBResponse {
init? (data: NSData)
}
protocol IOCtlRequestProtocol: SMBRequest {}
protocol IOCtlResponseProtocol: SMBResponse {}
struct SMBTime {
var time: UInt64
init(time: UInt64) {
self.time = time
}
init(unixTime: UInt) {
self.time = (UInt64(unixTime) + 11644473600) * 10000000
}
init(timeIntervalSince1970: NSTimeInterval) {
self.time = UInt64((timeIntervalSince1970 + 11644473600) * 10000000)
}
init(date: NSDate) {
self.init(timeIntervalSince1970: date.timeIntervalSince1970)
}
var unixTime: UInt {
return UInt(self.time / 10000000 - 11644473600)
}
var date: NSDate {
return NSDate(timeIntervalSince1970: Double(self.time) / 10000000 - 11644473600)
}
}
+448
View File
@@ -0,0 +1,448 @@
//
// SMB2CreateClose.swift
// ExtDownloader
//
// Created by Amir Abbas Mousavian on 4/30/95.
// Copyright © 1395 Mousavian. All rights reserved.
//
import Foundation
extension SMB2 {
// MARK: SMB2 Create
struct CreateRequest: SMBRequest {
let header: CreateRequest.Header
let name: String?
let contexts: [CreateContext]
init (header: CreateRequest.Header, name: String? = nil, contexts: [CreateContext] = []) {
self.header = header
self.name = name
self.contexts = contexts
}
func data() -> NSData {
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.dataUsingEncoding(NSUTF8StringEncoding) {
header.nameOffset = UInt16(offset)
header.nameLength = UInt16(nameData.length)
offset += nameData.length
body.appendData(nameData)
}
if contexts.count > 0 {
// TODO: Context CreateRequest implementation, 8 bit allign offset
header.contextOffset = UInt32(offset)
header.contextLength = 0
//result.appendData(nameData)
}
let result = NSMutableData(data: encode(&header))
result.appendData(body)
return result
}
struct Header {
let size: UInt16
private let securityFlags: UInt8
private var _requestedOplockLevel: UInt8
var requestedOplockLevel: OplockLevel {
get {
return OplockLevel(rawValue: _requestedOplockLevel)!
}
set {
_requestedOplockLevel = newValue.rawValue
}
}
private var _impersonationLevel: UInt32
var impersonationLevel: ImpersonationLevel {
get {
return ImpersonationLevel(rawValue: _impersonationLevel)!
}
set {
_impersonationLevel = newValue.rawValue
}
}
private let flags: UInt64
private let reserved: UInt64
let access: FileAccessMask
let fileAttributes: FileAttributes
let shareAccess: ShareAccess
private var _desposition: UInt32
var desposition: CreateDisposition {
get {
return CreateDisposition(rawValue: _desposition)!
}
set {
_desposition = newValue.rawValue
}
}
let options: CreateOptions
var nameOffset: UInt16
var nameLength: UInt16
var contextOffset: UInt32
var contextLength: UInt32
init(requestedOplockLevel: OplockLevel = .NONE, impersonationLevel: ImpersonationLevel = .Anonymous, access: FileAccessMask = [.GENERIC_ALL], fileAttributes: FileAttributes = [], shareAccess: ShareAccess = [.READ], desposition: CreateDisposition = .OPEN_IF, options: CreateOptions = []) {
self.size = 57
self.securityFlags = 0
self._requestedOplockLevel = requestedOplockLevel.rawValue
self._impersonationLevel = impersonationLevel.rawValue
self.flags = 0
self.reserved = 0
self.access = access
self.fileAttributes = fileAttributes
self.shareAccess = shareAccess
self._desposition = desposition.rawValue
self.options = options
self.nameOffset = 0
self.nameLength = 0
self.contextOffset = 0
self.contextLength = 0
}
}
struct CreateOptions: OptionSetType {
let rawValue: UInt32
init(rawValue: UInt32) {
self.rawValue = rawValue
}
static let DIRECTORY_FILE = CreateOptions(rawValue: 0x00000001)
static let WRITE_THROUGH = CreateOptions(rawValue: 0x00000002)
static let SEQUENTIAL_ONLY = CreateOptions(rawValue: 0x00000004)
static let NO_INTERMEDIATE_BUFFERING = CreateOptions(rawValue: 0x00000008)
static let NON_DIRECTORY_FILE = CreateOptions(rawValue: 0x00000040)
static let NO_EA_KNOWLEDGE = CreateOptions(rawValue: 0x00000200)
static let RANDOM_ACCESS = CreateOptions(rawValue: 0x00000800)
static let DELETE_ON_CLOSE = CreateOptions(rawValue: 0x00001000)
static let OPEN_BY_FILE_ID = CreateOptions(rawValue: 0x00002000)
static let OPEN_FOR_BACKUP_INTENT = CreateOptions(rawValue: 0x00004000)
static let NO_COMPRESSION = CreateOptions(rawValue: 0x00008000)
static let OPEN_REPARSE_POINT = CreateOptions(rawValue: 0x00200000)
static let OPEN_NO_RECALL = CreateOptions(rawValue: 0x00400000)
private static let SYNCHRONOUS_IO_ALERT = CreateOptions(rawValue: 0x00000010)
private static let SYNCHRONOUS_IO_NONALERT = CreateOptions(rawValue: 0x00000020)
private static let COMPLETE_IF_OPLOCKED = CreateOptions(rawValue: 0x00000100)
private static let REMOTE_INSTANCE = CreateOptions(rawValue: 0x00000400)
private static let OPEN_FOR_FREE_SPACE_QUERY = CreateOptions(rawValue: 0x00800000)
private static let OPEN_REQUIRING_OPLOCK = CreateOptions(rawValue: 0x00010000)
private static let DISALLOW_EXCLUSIVE = CreateOptions(rawValue: 0x00020000)
private static let RESERVE_OPFILTER = CreateOptions(rawValue: 0x00100000)
}
enum CreateDisposition: UInt32 {
/// If the file already exists, supersede it. Otherwise, create the file.
case SUPERSEDE = 0x00000000
/// If the file already exists, return success; otherwise, fail the operation.
case OPEN = 0x00000001
/// If the file already exists, fail the operation; otherwise, create the file.
case CREATE = 0x00000002
/// Open the file if it already exists; otherwise, create the file.
case OPEN_IF = 0x00000003
/// Overwrite the file if it already exists; otherwise, fail the operation.
case OVERWRITE = 0x00000004
/// Overwrite the file if it already exists; otherwise, create the file.
case OVERWRITE_IF = 0x00000005
}
enum ImpersonationLevel: UInt32 {
case Anonymous = 0x00000000
case Identification = 0x00000001
case Impersonation = 0x00000002
case Delegate = 0x00000003
}
}
struct CreateResponse: SMBResponse {
struct Header {
let size: UInt16
private let _oplockLevel: UInt8
var oplockLevel: OplockLevel {
return OplockLevel(rawValue: _oplockLevel)!
}
private let reserved: UInt32
let creationTime: SMBTime
let lastAccessTime: SMBTime
let lastWriteTime: SMBTime
let changeTime: SMBTime
let allocationSize: UInt64
let endOfFile: UInt64
let fileAttributes: FileAttributes
private let reserved2: UInt32
let fileId: FileId
let contextsOffset: UInt32
let ContextsLength: UInt32
}
let header: CreateResponse.Header
let contexts: [CreateContext]
init? (data: NSData) {
guard data.length >= sizeof(CreateResponse.Header.self) else {
return nil
}
self.header = decode(data)
if self.header.contextsOffset > 0 {
var contexts = [CreateContext]()
var contextOffset = Int(self.header.contextsOffset) - sizeof(SMB2.Header.self)
while contextOffset > 0 {
guard contextOffset < data.length else {
self.contexts = contexts
return
}
let contextDataHeader = data.subdataWithRange(NSRange(location: contextOffset, length: sizeof(CreateContext.Header.self)))
if let lastContextHeader = CreateContext(data: contextDataHeader) {
let lastContextLen = Int(lastContextHeader.header.dataOffset) + Int(lastContextHeader.header.dataLength) - contextOffset
let lastContextData = data.subdataWithRange(NSRange(location: contextOffset, length: lastContextLen))
if let newContext = CreateContext(data: lastContextData) {
contexts.append(newContext)
}
contextOffset = Int(lastContextHeader.header.next) - sizeof(SMB2.Header.self)
}
}
self.contexts = contexts
} else {
self.contexts = []
}
}
}
struct CreateContext {
struct Header {
var next: UInt32
let nameOffset: UInt16
let nameLength: UInt16
private let reserved: UInt16
let dataOffset: UInt16
let dataLength: UInt32
}
var header: CreateContext.Header
let buffer: NSData
init(name: ContextNames, data: NSData) {
let nameData = NSMutableData(data: (name.rawValue).dataUsingEncoding(NSUTF8StringEncoding)!)
self.header = CreateContext.Header(next: 0, nameOffset: 32, nameLength: UInt16(nameData.length), reserved: 0, dataOffset: UInt16(nameData.length), dataLength: UInt32(data.length))
self.buffer = data
}
init(name: NSUUID, data: NSData) {
var uuid = uuid_t(0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0)
name.getUUIDBytes(&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.length))
self.buffer = data
}
init? (data: NSData) {
let headersize = sizeof(Header)
guard data.length > headersize else {
return nil
}
self.header = decode(data)
self.buffer = data.subdataWithRange(NSRange(location: headersize, length: data.length - headersize))
}
func data() -> NSData {
let result = NSMutableData(data: encode(header))
result.appendData(buffer)
return result
}
enum ContextNames: String {
/// Request Create Context: Extended attributes
case EA_BUFFER = "ExtA"
/// Request Create Context: Security descriptor
case SD_BUFFER = "SecD"
/// Request & Response Create Context: Open to be durable
case DURABLE_HANDLE = "DHnQ"
case DURABLE_HANDLE_RESPONSE_V2 = "DH2Q"
/// Request Create Context: Reconnect to a durable open after being disconnected
case DURABLE_HANDLE_RECONNECT = "DHnC"
/// Request Create Context: Required allocation size of the newly created file
case ALLOCATION_SIZE = "AISi"
/// Request & Response Create Context: Maximal access information
case QUERY_MAXIMAL_ACCESS = "MxAc"
case TIMEWARP_TOKEN = "TWrp"
/// Response Create Context: DiskID of the open file in a volume.
case QUERY_ON_DISK_ID = "QFid"
/// Response Create Context: A lease. This value is only supported for the SMB 2.1 and 3.x dialect family.
case LEASE = "RqLs"
}
}
enum OplockLevel: UInt8 {
case NONE = 0x00
case LEVEL_II = 0x01
case EXCLUSIVE = 0x08
case BATCH = 0x09
case LEASE = 0xFF
}
struct ShareAccess: OptionSetType {
let rawValue: UInt32
init(rawValue: UInt32) {
self.rawValue = rawValue
}
static let READ = ShareAccess(rawValue: 0x00000001)
static let WRITE = ShareAccess(rawValue: 0x00000002)
static let DELETE = ShareAccess(rawValue: 0x00000004)
}
struct FileAccessMask: OptionSetType {
let rawValue: UInt32
init(rawValue: UInt32) {
self.rawValue = rawValue
}
// File and Printer/Pipe Accesses
static let FILE_READ_DATA = FileAccessMask(rawValue: 0x00000001)
static let FILE_WRITE_DATA = FileAccessMask(rawValue: 0x00000002)
static let FILE_APPEND_DATA = FileAccessMask(rawValue: 0x00000004)
static let FILE_EXECUTE = FileAccessMask(rawValue: 0x00000020)
// Directory
static let FILE_LIST_DIRECTORY = FileAccessMask(rawValue: 0x00000001)
static let FILE_ADD_FILE = FileAccessMask(rawValue: 0x00000002)
static let FILE_ADD_SUBDIRECTORY = FileAccessMask(rawValue: 0x00000004)
static let FILE_TRAVERSE = FileAccessMask(rawValue: 0x00000020)
// Generic
static let FILE_READ_EA = FileAccessMask(rawValue: 0x00000008)
static let FILE_WRITE_EA = FileAccessMask(rawValue: 0x00000010)
static let FILE_DELETE_CHILD = FileAccessMask(rawValue: 0x00000040)
static let FILE_READ_ATTRIBUTES = FileAccessMask(rawValue: 0x00000080)
static let FILE_WRITE_ATTRIBUTES = FileAccessMask(rawValue: 0x00000100)
static let DELETE = FileAccessMask(rawValue: 0x00010000)
static let READ_CONTROL = FileAccessMask(rawValue: 0x00020000)
static let WRITE_DAC = FileAccessMask(rawValue: 0x00040000)
static let WRITE_OWNER = FileAccessMask(rawValue: 0x00080000)
static let SYNCHRONIZE = FileAccessMask(rawValue: 0x00100000)
static let ACCESS_SYSTEM_SECURITY = FileAccessMask(rawValue: 0x01000000)
static let MAXIMUM_ALLOWED = FileAccessMask(rawValue: 0x02000000)
static let GENERIC_ALL = FileAccessMask(rawValue: 0x10000000)
static let GENERIC_EXECUTE = FileAccessMask(rawValue: 0x20000000)
static let GENERIC_WRITE = FileAccessMask(rawValue: 0x40000000)
static let GENERIC_READ = FileAccessMask(rawValue: 0x80000000)
}
struct FileAttributes: OptionSetType {
let rawValue: UInt32
init(rawValue: UInt32) {
self.rawValue = rawValue
}
static let READONLY = FileAttributes(rawValue: 0x00000001)
static let HIDDEN = FileAttributes(rawValue: 0x00000002)
static let SYSTEM = FileAttributes(rawValue: 0x00000004)
static let DIRECTORY = FileAttributes(rawValue: 0x00000010)
static let ARCHIVE = FileAttributes(rawValue: 0x00000020)
static let NORMAL = FileAttributes(rawValue: 0x00000080)
static let TEMPORARY = FileAttributes(rawValue: 0x00000100)
static let SPARSE_FILE = FileAttributes(rawValue: 0x00000200)
static let REPARSE_POINT = FileAttributes(rawValue: 0x00000400)
static let COMPRESSED = FileAttributes(rawValue: 0x00000800)
static let OFFLINE = FileAttributes(rawValue: 0x00001000)
static let NOT_CONTENT_INDEXED = FileAttributes(rawValue: 0x00002000)
static let ENCRYPTED = FileAttributes(rawValue: 0x00004000)
static let INTEGRITY_STREAM = FileAttributes(rawValue: 0x00008000)
static let NO_SCRUB_DATA = FileAttributes(rawValue: 0x00020000)
}
struct FileId {
let persistent: UInt64
let volatile: UInt64
}
// MARK: SMB2 Close
struct CloseRequest: SMBRequest {
let size: UInt16
let flags: CloseFlags
private let reserved2: UInt32
let filePersistantId: UInt64
let fileVolatileId: UInt64
init(filePersistantId: UInt64, fileVolatileId: UInt64) {
self.size = 24
self.filePersistantId = filePersistantId
self.fileVolatileId = fileVolatileId
self.flags = []
self.reserved2 = 0
}
func data() -> NSData {
return encode(self)
}
}
struct CloseResponse: SMBResponse {
let size: UInt16
let flags: CloseFlags
private let reserved: UInt32
let creationTime: SMBTime
let lastAccessTime: SMBTime
let lastWriteTime: SMBTime
let changeTime: SMBTime
let allocationSize: UInt64
let endOfFile: UInt64
let fileAttributes: FileAttributes
init? (data: NSData) {
self = decode(data)
}
}
struct CloseFlags: OptionSetType {
let rawValue: UInt16
init(rawValue: UInt16) {
self.rawValue = rawValue
}
static let POSTQUERY_ATTRIB = Flags(rawValue: 0x0001)
}
// MARK: SMB2 Flush
struct FlushRequest: SMBRequest {
let size: UInt16
private let reserved: UInt16
private let reserved2: UInt32
let filePersistantId: UInt64
let fileVolatileId: UInt64
init(filePersistantId: UInt64, fileVolatileId: UInt64) {
self.size = 24
self.filePersistantId = filePersistantId
self.fileVolatileId = fileVolatileId
self.reserved = 0
self.reserved2 = 0
}
func data() -> NSData {
return encode(self)
}
}
struct FlushResponse: SMBResponse {
let size: UInt16
let reserved: UInt16
init() {
self.size = 4
self.reserved = 0
}
init? (data: NSData) {
self = decode(data)
}
}
}
+251
View File
@@ -0,0 +1,251 @@
//
// SMB2FileOperation.swift
// ExtDownloader
//
// Created by Amir Abbas Mousavian on 4/30/95.
// Copyright © 1395 Mousavian. All rights reserved.
//
import Foundation
extension SMB2 {
// MARK: SMB2 Read
struct ReadRequest: SMBRequest {
let size: UInt16
private let padding: UInt8
let flags: ReadRequest.Flags
let length: UInt32
let offset: UInt64
let fileId: FileId
let minimumLength: UInt32
private let _channel: UInt32
var channel: Channel {
return Channel(rawValue: _channel) ?? .NONE
}
let remainingBytes: UInt32
private let channelInfoOffset: UInt16
private let channelInfoLength: UInt16
private let channelBuffer: UInt8
init (fileId: FileId, offset: UInt64, length: UInt32, flags: ReadRequest.Flags = [], minimumLength: UInt32 = 0, remainingBytes: UInt32 = 0, channel: Channel = .NONE) {
self.size = 49
self.padding = 0
self.flags = flags
self.length = length
self.offset = offset
self.fileId = fileId
self.minimumLength = minimumLength
self._channel = channel.rawValue
self.remainingBytes = remainingBytes
self.channelInfoOffset = 0
self.channelInfoLength = 0
self.channelBuffer = 0
}
func data() -> NSData {
return encode(read)
}
struct Flags: OptionSetType {
let rawValue: UInt8
init(rawValue: UInt8) {
self.rawValue = rawValue
}
static let UNBUFFERED = Flags(rawValue: 0x01)
}
}
struct ReadRespone: SMBResponse {
struct Header {
let size: UInt16
let offset: UInt8
private let reserved: UInt8
let length: UInt32
let remaining: UInt32
private let reserved2: UInt32
}
let header: ReadRespone.Header
let buffer: NSData
init?(data: NSData) {
guard data.length > 16 else {
return nil
}
self.header = decode(data)
let headersize = sizeof(Header)
self.buffer = data.subdataWithRange(NSRange(location: headersize, length: data.length - headersize))
}
}
enum Channel: UInt32 {
case NONE = 0x00000000
case RDMA_V1 = 0x00000001
case RDMA_V1_INVALIDATE = 0x00000002
}
// MARK: SMB2 Write
struct WriteRequest: SMBRequest {
let header: WriteRequest.Header
let channelInfo: ChannelInfo?
let fileData: NSData
struct Header {
let size: UInt16
let dataOffset: UInt16
let length: UInt32
let offset: UInt64
let fileId: FileId
private let _channel: UInt32
var channel: Channel {
return Channel(rawValue: _channel) ?? .NONE
}
let remainingBytes: UInt32
let channelInfoOffset: UInt16
let channelInfoLength: UInt16
let flags: WriteRequest.Flags
}
init(fileId: FileId, offset: UInt64, remainingBytes: UInt32 = 0, data: NSData, channel: Channel = .NONE, channelInfo: ChannelInfo? = nil, flags: WriteRequest.Flags = []) {
var channelInfoOffset: UInt16 = 0
var channelInfoLength: UInt16 = 0
if channel != .NONE, let channelInfo = channelInfo {
channelInfoOffset = UInt16(sizeof(SMB2.Header.self) + sizeof(WriteRequest.Header.self))
channelInfoLength = UInt16(sizeof(channelInfo.dynamicType))
}
let dataOffset = UInt16(sizeof(SMB2.Header.self) + sizeof(WriteRequest.Header.self)) + channelInfoLength
self.header = WriteRequest.Header(size: UInt16(49), dataOffset: dataOffset, length: UInt32(data.length), offset: offset, fileId: fileId, _channel: channel.rawValue, remainingBytes: remainingBytes, channelInfoOffset: channelInfoOffset, channelInfoLength: channelInfoLength, flags: flags)
self.channelInfo = channelInfo
self.fileData = data
}
func data() -> NSData {
let result = NSMutableData(data: encode(self.header))
if let channelInfo = channelInfo {
result.appendData(channelInfo.data())
}
result.appendData(fileData)
return result
}
struct Flags: OptionSetType {
let rawValue: UInt32
init(rawValue: UInt32) {
self.rawValue = rawValue
}
static let THROUGH = Flags(rawValue: 0x00000001)
static let UNBUFFERED = Flags(rawValue: 0x00000002)
}
}
struct WriteResponse: SMBResponse {
let size: UInt16
private let reserved: UInt16
let writtenBytes: UInt32
private let remaining: UInt32
private let channelInfoOffset: UInt16
private let channelInfoLength: UInt16
init?(data: NSData) {
self = decode(data)
}
}
struct ChannelInfo: SMBRequest {
let offset: UInt64
let token: UInt32
let length: UInt32
func data() -> NSData {
return encode(data)
}
}
// MARK: SMB2 Lock
struct LockElement: SMBRequest {
let offset: UInt64
let length: UInt64
let flags: LockElement.Flags
private let reserved: UInt32
func data() -> NSData {
return encode(self)
}
struct Flags: OptionSetType {
let rawValue: UInt32
init(rawValue: UInt32) {
self.rawValue = rawValue
}
static let SHARED_LOCK = Flags(rawValue: 0x00000001)
static let EXCLUSIVE_LOCK = Flags(rawValue: 0x00000002)
static let UNLOCK = Flags(rawValue: 0x00000004)
static let FAIL_IMMEDIATELY = Flags(rawValue: 0x00000010)
}
}
struct LockRequest: SMBRequest {
let header: LockRequest.Header
let locks: [LockElement]
init(fileId: FileId,locks: [LockElement], lockSequenceNumber : Int8 = 0, lockSequenceIndex: UInt32 = 0) {
self.header = LockRequest.Header(size: 48, lockCount: UInt16(locks.count), lockSequence: UInt32(lockSequenceNumber << 28) + lockSequenceIndex, fileId: fileId)
self.locks = locks
}
func data() -> NSData {
let result = NSMutableData(data: encode(header))
for lock in locks {
result.appendData(encode(lock))
}
return result
}
struct Header {
let size: UInt16
private let lockCount: UInt16
let lockSequence: UInt32
let fileId : FileId
}
}
struct LockResponse: SMBResponse {
let size: UInt16
let reserved: UInt16
init() {
self.size = 4
self.reserved = 0
}
init? (data: NSData) {
self = decode(data)
}
}
// MARK: SMB2 Cancel
struct CancelRequest: SMBRequest {
let size: UInt16
let reserved: UInt16
init() {
self.size = 4
self.reserved = 0
}
func data() -> NSData {
return encode(self)
}
}
}
+388
View File
@@ -0,0 +1,388 @@
//
// SMB2IOCtl.swift
// ExtDownloader
//
// Created by Amir Abbas Mousavian on 4/30/95.
// Copyright © 1395 Mousavian. All rights reserved.
//
import Foundation
extension SMB2 {
// MARK: SMB2 IOCTL
/**
* IOCtl usage is usually limited in SMB to pipe requests and duplicating file inside server
*/
struct IOCtlRequest: SMBRequest {
let header: Header
let requestData: IOCtlRequestProtocol?
init(fileId: FileId ,ctlCode: IOCtlCode, requestData: IOCtlRequestProtocol?, flags: IOCtlRequest.Flags = []) {
let offset = requestData != nil ? UInt32(sizeof(SMB2.Header.self) + sizeof(IOCtlRequest.Header.self)) : 0
self.header = Header(size: 57, reserved: 0, _ctlCode: ctlCode.rawValue, fileId: fileId, inputOffset: offset, inputCount: UInt32((requestData?.data().length ?? 0)), maxInputResponse: 0, outputOffset: offset, outputCount: 0, maxOutputResponse: UInt32(Int32.max), flags: flags, reserved2: 0)
self.requestData = requestData
}
func data() -> NSData {
let result = NSMutableData(data: encode(self.header))
if let reqData = requestData?.data() {
result.appendData(reqData)
}
return result
}
struct Header {
let size: UInt16
private let reserved: UInt16
private let _ctlCode: UInt32
var ctlCode: IOCtlCode {
return IOCtlCode(rawValue: _ctlCode)!
}
let fileId: FileId
let inputOffset: UInt32
let inputCount: UInt32
let maxInputResponse: UInt32
let outputOffset: UInt32
let outputCount: UInt32
let maxOutputResponse: UInt32
let flags: IOCtlRequest.Flags
private let reserved2: UInt32
}
struct Flags: OptionSetType {
let rawValue: UInt32
init(rawValue: UInt32) {
self.rawValue = rawValue
}
static let IOCTL = Flags(rawValue: 0x00000000)
static let FSCTL = Flags(rawValue: 0x00000001)
}
}
struct IOCtlResponse: SMBResponse {
let header: Header
let responseData: IOCtlResponseProtocol?
init?(data: NSData) {
self.header = decode(data)
let responseRange = NSRange(location: Int(self.header.outputOffset - 64), length: Int(self.header.outputCount))
let response = data.subdataWithRange(responseRange)
switch self.header.ctlCode {
case .SRV_COPYCHUNK, .SRV_COPYCHUNK_WRITE:
self.responseData = IOCtlResponseData.SrvCopyChunk(data: response)
case .SRV_ENUMERATE_SNAPSHOTS:
self.responseData = IOCtlResponseData.SrvSnapshots(data: response)
case .SRV_REQUEST_RESUME_KEY:
self.responseData = IOCtlResponseData.ResumeKey(data: response)
case .SRV_READ_HASH:
self.responseData = IOCtlResponseData.ReadHash(data: response)
case .QUERY_NETWORK_INTERFACE_INFO:
self.responseData = IOCtlResponseData.NetworkInterfaceInfo(data: response)
case .VALIDATE_NEGOTIATE_INFO:
self.responseData = IOCtlResponseData.ValidateNegotiateInfo(data: response)
default:
self.responseData = nil
}
}
struct Header {
let size: UInt16
private let reserved: UInt16
private let _ctlCode: UInt32
var ctlCode: IOCtlCode {
return IOCtlCode(rawValue: _ctlCode)!
}
let fileId: FileId
let inputOffset: UInt32
let inputCount: UInt32
let outputOffset: UInt32
let outputCount: UInt32
private let flags: UInt32
private let reserved2: UInt32
}
}
enum IOCtlCode: UInt32 {
case DFS_GET_REFERRALS = 0x00060194
case DFS_GET_REFERRALS_EX = 0x000601B0
case SET_REPARSE_POINT = 0x000900A4
case FILE_LEVEL_TRIM = 0x00098208
case PIPE_PEEK = 0x0011400C
case PIPE_WAIT = 0x00110018
/// PIPE_TRANSCEIVE is valid only on a named pipe with mode set to FILE_PIPE_MESSAGE_MODE.
case PIPE_TRANSCEIVE = 0x0011C017
/// Get ResumeKey used by the client to uniquely identify the source file in an FSCTL_SRV_COPYCHUNK or FSCTL_SRV_COPYCHUNK_WRITE request.
case SRV_REQUEST_RESUME_KEY = 0x00140078
/// Get all the revision time-stamps that are associated with the Tree Connect share in which the open resides
case SRV_ENUMERATE_SNAPSHOTS = 0x00144064
/// Reads a chunk of file for performing server side copy operations.
case SRV_COPYCHUNK = 0x001440F2
/// Retrieve data from the Content Information File associated with a specified file, not valid for the SMB 2.0.2 dialect.
case SRV_READ_HASH = 0x001441BB
/// Writes the chunk of file for performing server side copy operations.
case SRV_COPYCHUNK_WRITE = 0x001480F2
/// Request resiliency for a specified open file, not valid for the SMB 2.0.2 dialect.
case LMR_REQUEST_RESILIENCY = 0x001401D4
/// Get server network interface info e.g. link speed and socket address information
case QUERY_NETWORK_INTERFACE_INFO = 0x001401FC
/// Request validation of a previous SMB 2 NEGOTIATE, valid for SMB 3.0 and SMB 3.0.2 dialects.
case VALIDATE_NEGOTIATE_INFO = 0x00140204
}
struct IOCtlRequestData {
struct CopyChunk: IOCtlRequestProtocol {
let sourceKey: (UInt64, UInt64, UInt64)
let chunkCount: UInt32
let chunks: [Chunk]
func data() -> NSData {
let result = NSMutableData(data: encode(sourceKey))
result.appendData(encode(chunkCount))
var reserved: UInt32 = 0
result.appendData(encode(&reserved))
return NSData()
}
struct Chunk {
let sourceOffset: UInt64
let targetOffset: UInt64
let length: UInt32
private let reserved: UInt32
func data() -> NSData {
return encode(self)
}
}
}
struct ReadHash: IOCtlRequestProtocol {
let _hashType: UInt32
var hashType: IOCtlHashType {
return IOCtlHashType(rawValue: _hashType) ?? .PEER_DIST
}
let _hashVersion: UInt32
var hashVersion: IOCtlHashVersion {
return IOCtlHashVersion(rawValue: _hashVersion) ?? .VER_1
}
let _hashRetrievalType: UInt32
var hashRetrievalType: IOCtlHashRetrievalType {
return IOCtlHashRetrievalType(rawValue: _hashRetrievalType) ?? .FILE_BASED
}
let length: UInt32
let offset: UInt64
init(offset: UInt64, length: UInt32, hashType: IOCtlHashType = .PEER_DIST, hashVersion: IOCtlHashVersion = .VER_1, hashRetrievalType: IOCtlHashRetrievalType = .FILE_BASED) {
self._hashType = hashType.rawValue
self._hashVersion = hashVersion.rawValue
self._hashRetrievalType = hashRetrievalType.rawValue
self.length = length
self.offset = offset
}
func data() -> NSData {
return encode(self)
}
}
struct ResilencyRequest: IOCtlRequestProtocol {
let timeout: UInt32
private let reserved: UInt32
/// The requested time the server holds the file open after a disconnect before releasing it. This time is in milliseconds.
init(timeout: UInt32) {
self.timeout = timeout
self.reserved = 0
}
func data() -> NSData {
return encode(self)
}
}
struct ValidateNegotiateInfo: IOCtlRequestProtocol {
let header: ValidateNegotiateInfo.Header
let dialects: [UInt16]
init(dialects: [UInt16], guid: uuid_t, capabilities: IOCtlCapabilities, securityMode: UInt16) {
self.header = Header(capabilities: capabilities, guid: guid, securityMode: securityMode, dialectCount: UInt16(dialects.count))
self.dialects = dialects
}
func data() -> NSData {
let result = NSMutableData(data: encode(self.header))
dialects.forEach { result.appendData(encode($0)) }
return result
}
struct Header {
let capabilities: IOCtlCapabilities
/// Client's GUID
let guid: uuid_t
let securityMode: UInt16
let dialectCount: UInt16
}
}
}
struct IOCtlResponseData {
// SRV_COPYCHUNK, SRV_COPYCHUNK_WRITE
struct SrvCopyChunk: IOCtlResponseProtocol {
let chunksCount: UInt32
let chunksBytesWritten: UInt32
let totalBytesWriiten: UInt32
init?(data: NSData) {
self = decode(data)
}
}
// SRV_ENUMERATE_SNAPSHOTS
struct SrvSnapshots: IOCtlResponseProtocol {
let count: UInt32
let snapshots: [SMBTime]
init?(data: NSData) {
self.count = decode(data)
let returnedCount: UInt32 = decode(data.subdataWithRange(NSRange(location: 4, length: 4)))
//let size: UInt32 = decode(data.subdataWithRange(NSRange(location: 8, length: 4)))
var snapshots = [SMBTime]()
let dateFormatter = NSDateFormatter()
dateFormatter.dateFormat = "'@GMT-'yyyy'.'MM'.'dd'-'HH'.'mm'.'ss"
for i in 0..<Int(returnedCount) {
let offset = 24 + i * 48
if data.length < offset + 48 {
return nil
}
let datestring = String(data: data.subdataWithRange(NSRange(location: offset, length: 48)), encoding: NSUTF16StringEncoding)
if let datestring = datestring, let date = dateFormatter.dateFromString(datestring) {
snapshots.append(SMBTime(date: date))
}
}
self.snapshots = snapshots
}
}
struct ResumeKey: IOCtlResponseProtocol {
let key: (UInt64, UInt64, UInt64)
private let contextLength: UInt32
private let context: UInt32
init?(data: NSData) {
self = decode(data)
}
}
struct ReadHash: IOCtlResponseProtocol {
// TODO: Implement IOCTL READ_HASH
init?(data: NSData) {
self = decode(data)
}
}
struct NetworkInterfaceInfo: IOCtlResponseProtocol {
let items: [NetworkInterfaceInfo.Item]
init?(data: NSData) {
let count = data.length / sizeof(Item)
guard count > 0 else {
return nil
}
var items = [Item]()
for i in 0..<count {
let itemdata = data.subdataWithRange(NSRange(location: i * sizeof(Item), length: sizeof(Item)))
items.append(decode(itemdata))
}
self.items = items
}
struct Item {
/// The offset, in bytes, from the beginning of this structure to the beginning of a subsequent 8-byte aligned network interface.
let next: UInt32
/// specifies the network interface index.
let ifIndex: UInt32
let capability: IOCtlCapabilities
private let reserved: UInt32
/// Speed of the network interface in bits per second
let linkSpeed: UInt64
private 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,
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,
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,
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)
var family: sa_family_t {
return sockaddrStorage.1
}
static let ipv4: sa_family_t = 0x02
static let ipv6: sa_family_t = 0x17
var sockaddr: sockaddr_in {
var sockaddrStorage = self.sockaddrStorage
let data = NSData(bytes: &sockaddrStorage, length: 16)
return decode(data)
}
var sockaddr6: sockaddr_in6 {
var sockaddrStorage = self.sockaddrStorage
let data = NSData(bytes: &sockaddrStorage, length: 28)
return decode(data)
}
}
}
struct ValidateNegotiateInfo: IOCtlResponseProtocol {
let capabilities: IOCtlCapabilities
let guid: uuid_t
let securityMode: UInt16
private let _dialect: UInt16
var dialect: (major: Int, minor: Int) {
return (major: Int(_dialect & 0xFF), minor: Int(_dialect >> 8))
}
init?(data: NSData) {
self = decode(data)
}
}
}
struct IOCtlCapabilities: OptionSetType {
let rawValue: UInt32
init(rawValue: UInt32) {
self.rawValue = rawValue
}
static let RSS_CAPABLE = IOCtlCapabilities(rawValue: 0x00000001)
static let RDMA_CAPABLE = IOCtlCapabilities(rawValue: 0x00000002)
}
enum IOCtlHashType: UInt32 {
case PEER_DIST = 0x00000001
}
enum IOCtlHashVersion: UInt32 {
case VER_1 = 0x00000001
case VER_2 = 0x00000002
}
enum IOCtlHashRetrievalType: UInt32 {
case HASH_BASED = 0x00000001
case FILE_BASED = 0x00000002
}
}
+18
View File
@@ -0,0 +1,18 @@
//
// SMB2Query.swift
// ExtDownloader
//
// Created by Amir Abbas Mousavian on 4/31/95.
// Copyright © 1395 Mousavian. All rights reserved.
//
import Foundation
extension SMB2 {
// MARK: SMB2 Query Directory
// MARK: SMB2 Change Notify
// MARK: SMB2 Query Info
}
+318
View File
@@ -0,0 +1,318 @@
//
// SMB2NegotiationTypes.swift
// ExtDownloader
//
// Created by Amir Abbas Mousavian on 4/30/95.
// Copyright © 1395 Mousavian. All rights reserved.
//
import Foundation
extension SMB2 {
// MARK: SMB2 Negotiating
struct NegotiateRequest: SMBRequest {
let request: NegotiateRequest.Header
let dialects: [UInt16]
let contexts: [(type: NegotiateContextType, data: NSData)]
init(request: NegotiateRequest.Header, dialects: [UInt16] = [0x0202], contexts: [(type: NegotiateContextType, data: NSData)] = []) {
self.request = request
self.dialects = dialects
self.contexts = contexts
}
func data() -> NSData {
var request = self.request
request.dialectCount = UInt16(dialects.count)
let dialectData = NSMutableData()
for dialect in dialects {
var dialect = dialect
dialectData.appendBytes(&dialect, length: 2)
}
let pad = ((1024 - dialectData.length) % 8)
dialectData.increaseLengthBy(pad)
request.contextOffset = UInt32(sizeof(request.dynamicType.self)) + UInt32(dialectData.length)
request.contextCount = UInt16(contexts.count)
let contextData = NSMutableData()
for context in contexts {
var contextType = context.type.rawValue
contextData.appendBytes(&contextType, length: 2)
var dataLen = UInt16(context.data.length)
contextData.increaseLengthBy(4)
contextData.appendBytes(&dataLen, length: 2)
}
let result = NSMutableData(data: encode(&request))
result.appendData(dialectData)
result.appendData(contextData)
return result
}
struct Header {
var size: UInt16
var dialectCount: UInt16
let singing: NegotiateSinging
private let reserved: UInt16
let capabilities: GlobalCapabilities
let guid: uuid_t
var contextOffset: UInt32
var contextCount: UInt16
private let reserved2: UInt16
var clientStartTime: SMBTime {
let time = UInt64(contextOffset) + (UInt64(contextCount) << 32) + (UInt64(contextCount) << 48)
return SMBTime(time: time)
}
init(singing: NegotiateSinging = [.ENABLED], capabilities: GlobalCapabilities, guid: uuid_t? = nil, clientStartTime: SMBTime? = nil) {
self.size = 36
self.dialectCount = 0
self.singing = singing
self.reserved = 0
self.capabilities = capabilities
self.guid = guid ?? (0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0)
if let clientStartTime = clientStartTime {
let time = clientStartTime.time
self.contextOffset = UInt32(time & 0xffffffff)
self.contextCount = UInt16(time & 0x0000ffff00000000 >> 32)
self.reserved2 = UInt16(time >> 48)
} else {
self.contextOffset = 0
self.contextCount = 0
self.reserved2 = 0
}
}
}
}
struct NegotiateResponse: SMBResponse {
let header: NegotiateResponse.Header
let buffer: NSData?
let contexts: [(type: NegotiateContextType, data: NSData)]
init? (data: NSData) {
if data.length < 64 {
return nil
}
self.header = decode(data)
if Int(header.size) != 65 {
return nil
}
let bufOffset = Int(self.header.bufferOffset) - sizeof(SMB2.Header.self)
let bufLen = Int(self.header.bufferLength)
if bufOffset > 0 && bufLen > 0 && data.length >= bufOffset + bufLen {
self.buffer = data.subdataWithRange(NSRange(location: bufOffset, length: bufLen))
} else {
self.buffer = nil
}
let contextCount = Int(self.header.contextCount)
let contextOffset = Int(self.header.contextOffset) - sizeof(SMB2.Header.self)
if contextCount > 0 && contextOffset > 0 {
// TODO: NegotiateResponse context support for SMB3
self.contexts = []
} else {
self.contexts = []
}
}
struct Header {
let size: UInt16
let singing: NegotiateSinging
let dialect: UInt16
let contextCount: UInt16
let serverGuid: uuid_t
let capabilities: GlobalCapabilities
let maxTransactSize: UInt32
let maxReadSize: UInt32
let maxWriteSize: UInt32
let systemTime: SMBTime
let serverStartTime: SMBTime
let bufferOffset: UInt16
let bufferLength: UInt16
let contextOffset: UInt32
}
}
struct NegotiateSinging: OptionSetType {
let rawValue: UInt16
init(rawValue: UInt16) {
self.rawValue = rawValue
}
static let ENABLED = NegotiateSinging(rawValue: 0x0001)
static let REQUIRED = NegotiateSinging(rawValue: 0x0002)
}
struct NegotiateContextType: OptionSetType {
let rawValue: UInt16
init(rawValue: UInt16) {
self.rawValue = rawValue
}
static let PREAUTH_INTEGRITY_CAPABILITIES = NegotiateContextType(rawValue: 0x0001)
static let ENCRYPTION_CAPABILITIES = NegotiateContextType(rawValue: 0x0002)
}
struct GlobalCapabilities: OptionSetType {
let rawValue: UInt32
init(rawValue: UInt32) {
self.rawValue = rawValue
}
static let DFS = GlobalCapabilities(rawValue: 0x00000001)
static let LEASING = GlobalCapabilities(rawValue: 0x00000002)
static let LARGE_MTU = GlobalCapabilities(rawValue: 0x00000004)
static let MULTI_CHANNEL = GlobalCapabilities(rawValue: 0x00000008)
static let PERSISTENT_HANDLES = GlobalCapabilities(rawValue: 0x00000010)
static let DIRECTORY_LEASING = GlobalCapabilities(rawValue: 0x00000020)
static let ENCRYPTION = GlobalCapabilities(rawValue: 0x00000040)
}
// MARK: SMB2 Session Setup
struct SessionSetupRequest: SMBRequest {
let header: SessionSetupRequest.Header
let buffer: NSData?
init(header: SessionSetupRequest.Header, buffer: NSData) {
self.header = header
self.buffer = buffer
}
func data() -> NSData {
var header = self.header
header.bufferOffset = UInt16(sizeof(SMB2.Header.self) + sizeof(SessionSetupRequest.Header.self))
header.bufferLength = UInt16(buffer?.length ?? 0)
let result = NSMutableData(data: encode(&header))
if let buffer = self.buffer {
result.appendData(buffer)
}
return result
}
struct Header {
let size: UInt16
let flags: SessionSetupRequest.Flags
let signing: SessionSetupSinging
let capabilities: GlobalCapabilities
private let channel: UInt32
var bufferOffset: UInt16
var bufferLength: UInt16
let sessionId: UInt64
init(sessionId: UInt64, flags: SessionSetupRequest.Flags = [], singing: SessionSetupSinging, capabilities: GlobalCapabilities) {
self.size = 25
self.flags = flags
self.signing = singing
self.capabilities = capabilities
self.channel = 0
self.bufferOffset = 0
self.bufferLength = 0
self.sessionId = sessionId
}
}
/// Works the client implements the SMB 3.x dialect family
struct Flags: OptionSetType {
let rawValue: UInt8
init(rawValue: UInt8) {
self.rawValue = rawValue
}
static let BINDING = NegotiateSinging(rawValue: 0x01)
}
}
struct SessionSetupResponse: SMBResponse {
let header: SessionSetupResponse.Header
let buffer: NSData?
init? (data: NSData) {
if data.length < 64 {
return nil
}
self.header = decode(data)
if Int(header.size) != 9 {
return nil
}
let bufOffset = Int(self.header.bufferOffset) - sizeof(SMB2.Header.self)
let bufLen = Int(self.header.bufferLength)
if bufOffset > 0 && bufLen > 0 && data.length >= bufOffset + bufLen {
self.buffer = data.subdataWithRange(NSRange(location: bufOffset, length: bufLen))
} else {
self.buffer = nil
}
}
struct Header {
let size: UInt16
let flags: SessionSetupResponse.Flags
let bufferOffset: UInt16
let bufferLength: UInt16
}
struct Flags: OptionSetType {
let rawValue: UInt16
init(rawValue: UInt16) {
self.rawValue = rawValue
}
static let IS_GUEST = Flags(rawValue: 0x0001)
static let IS_NULL = Flags(rawValue: 0x0002)
static let ENCRYPT_DATA = Flags(rawValue: 0x0004)
}
}
struct SessionSetupSinging: OptionSetType {
let rawValue: UInt8
init(rawValue: UInt8) {
self.rawValue = rawValue
}
static let ENABLED = NegotiateSinging(rawValue: 0x01)
static let REQUIRED = NegotiateSinging(rawValue: 0x02)
}
// MARK: SMB2 Log off
struct LogOff: SMBRequest, SMBResponse {
let size: UInt16
let reserved: UInt16
init() {
self.size = 4
self.reserved = 0
}
init? (data: NSData) {
self = decode(data)
}
func data() -> NSData {
return encode(self)
}
}
// MARK: SMB2 Echo
struct Echo: SMBRequest, SMBResponse {
let size: UInt16
let reserved: UInt16
init() {
self.size = 4
self.reserved = 0
}
init? (data: NSData) {
self = decode(data)
}
func data() -> NSData {
return encode(self)
}
}
}
+14
View File
@@ -0,0 +1,14 @@
//
// SMB2SetInfo.swift
// ExtDownloader
//
// Created by Amir Abbas Mousavian on 4/31/95.
// Copyright © 1395 Mousavian. All rights reserved.
//
import Foundation
extension SMB2 {
// MARK: SMB2 Set Info
}
+151
View File
@@ -0,0 +1,151 @@
//
// SMB2Tree.swift
// ExtDownloader
//
// Created by Amir Abbas Mousavian on 4/30/95.
// Copyright © 1395 Mousavian. All rights reserved.
//
import Foundation
extension SMB2 {
// MARK: SMB2 Tree Connect
struct TreeConnectRequest: SMBRequest {
let header: TreeConnectRequest.Header
let buffer: NSData?
var path: String {
return ""
}
var share: String {
return ""
}
init? (header: TreeConnectRequest.Header, host: String, share: String) {
guard !host.containsString("/") && !host.containsString("/") && !share.containsString("/") && !share.containsString("/") else {
return nil
}
self.header = header
let path = "\\\\\(host)\\\(share)"
self.buffer = path.dataUsingEncoding(NSUTF8StringEncoding)
}
func data() -> NSData {
var header = self.header
header.pathOffset = UInt16(sizeof(SMB2.Header.self) + sizeof(TreeConnectRequest.Header.self))
header.pathLength = UInt16(buffer?.length ?? 0)
let result = NSMutableData(data: encode(&header))
if let buffer = self.buffer {
result.appendData(buffer)
}
return result
}
struct Header {
let size: UInt16
let flags: TreeConnectRequest.Flags
var pathOffset: UInt16
var pathLength: UInt16
init(flags: TreeConnectRequest.Flags) {
self.size = 9
self.flags = flags
self.pathOffset = 0
self.pathLength = 0
}
}
struct Flags: OptionSetType {
let rawValue: UInt16
init(rawValue: UInt16) {
self.rawValue = rawValue
}
static let SHAREFLAG_CLUSTER_RECONNECT = Flags(rawValue: 0x0001)
}
}
struct TreeConnectResponse: SMBResponse {
let size: UInt16 // = 16
private let _type: UInt8
var type: ShareType {
return ShareType(rawValue: _type) ?? .UNKNOWN
}
private let reserved: UInt8
let flags: TreeConnectResponse.ShareFlags
let capabilities: TreeConnectResponse.Capabilities
let maximalAccess: FileAccessMask
init? (data: NSData) {
if data.length != 16 {
return nil
}
self = decode(data)
}
enum ShareType: UInt8 {
case UNKNOWN = 0x00
case DISK = 0x01
case PIPE = 0x02
case PRINT = 0x03
}
struct ShareFlags: OptionSetType {
let rawValue: UInt32
init(rawValue: UInt32) {
self.rawValue = rawValue
}
static let DFS = ShareFlags(rawValue: 0x00000001)
static let DFS_ROOT = ShareFlags(rawValue: 0x00000002)
static let MANUAL_CACHING = ShareFlags(rawValue: 0x00000000)
static let AUTO_CACHING = ShareFlags(rawValue: 0x00000010)
static let VDO_CACHING = ShareFlags(rawValue: 0x00000020)
static let NO_CACHING = ShareFlags(rawValue: 0x00000030)
static let RESTRICT_EXCLUSIVE_OPENS = ShareFlags(rawValue: 0x00000100)
static let FORCE_SHARED_DELETE = ShareFlags(rawValue: 0x00000200)
static let ALLOW_NAMESPACE_CACHING = ShareFlags(rawValue: 0x00000400)
static let ACCESS_BASED_DIRECTORY_ENUM = ShareFlags(rawValue: 0x00000800)
static let FORCE_LEVELII_OPLOCK = ShareFlags(rawValue: 0x00001000)
static let ENABLE_HASH_V1 = ShareFlags(rawValue: 0x00002000)
static let ENABLE_HASH_V2 = ShareFlags(rawValue: 0x00004000)
static let ENCRYPT_DATA = ShareFlags(rawValue: 0x00008000)
}
struct Capabilities: OptionSetType {
let rawValue: UInt32
init(rawValue: UInt32) {
self.rawValue = rawValue
}
static let DFS = Capabilities(rawValue: 0x00000008)
static let CONTINUOUS_AVAILABILITY = Capabilities(rawValue: 0x00000010)
static let SCALEOUT = Capabilities(rawValue: 0x00000020)
static let CLUSTER = Capabilities(rawValue: 0x00000040)
static let ASYMMETRIC = Capabilities(rawValue: 0x00000080)
}
}
// MARK: SMB2 Tree Disconnect
struct TreeDisconnect: SMBRequest, SMBResponse {
let size: UInt16
let reserved: UInt16
init() {
self.size = 4
self.reserved = 0
}
init? (data: NSData) {
self = decode(data)
}
func data() -> NSData {
return encode(self)
}
}
}
+131
View File
@@ -0,0 +1,131 @@
//
// SMB2Types.swift
// FileProvider
//
// Created by Amir Abbas Mousavian.
// Copyright © 2016 Mousavian. Distributed under MIT license.
//
import Foundation
// SMB2 Types
struct SMB2 {
struct Header: FileProviderSMBHeader { // 64 bytes
// header is always \u{fe}SMB
let protocolID: UInt32
static let protocolConst: UInt32 = 0x424d53fe
let size: UInt16
let creditCharge: UInt16
// error messages from the server to the client
let status: UInt32
enum StatusSeverity: UInt8 {
case Success = 0, Information, Warning, Error
}
var statusDetails: (severity: StatusSeverity, customer: Bool, facility: UInt16, code: UInt16) {
let severity = StatusSeverity(rawValue: UInt8(status >> 30))!
return (severity, status & 0x20000000 != 0, UInt16((status & 0x0FFF0000) >> 16), UInt16(status & 0x0000FFFF))
}
private let _command: UInt16
var command: Command {
get {
return Command(rawValue: _command) ?? .INVALID
}
}
let creditRequestResponse: UInt16
let flags: Flags
var nextCommand: UInt32
let messageId: UInt64
private let reserved: UInt32
let treeId: UInt32
var asyncId: UInt64 {
get {
return UInt64(reserved) + (UInt64(treeId) << 32)
}
}
let sessionId: UInt64
let signature: (UInt64, UInt64)
init(command: Command, status: NTStatus = .SUCCESS, creditCharge: UInt16 = 0, creditRequestResponse: UInt16, flags: Flags = [], nextCommand: UInt32 = 0, messageId: UInt64, treeId: UInt32, sessionId: UInt64, signature: (UInt64, UInt64) = (0, 0)) {
self.protocolID = self.dynamicType.protocolConst
self.size = 64
self.status = status.rawValue
self._command = command.rawValue
self.creditCharge = creditCharge
self.creditRequestResponse = creditRequestResponse
self.flags = flags
self.nextCommand = nextCommand
self.messageId = messageId
self.reserved = 0
self.treeId = treeId
self.sessionId = sessionId
self.signature = signature
}
init(asyncCommand: Command, status: NTStatus = .SUCCESS, creditCharge: UInt16 = 0, creditRequestResponse: UInt16, flags: Flags = [.ASYNC_COMMAND], nextCommand: UInt32 = 0, messageId: UInt64, asyncId: UInt64, sessionId: UInt64, signature: (UInt64, UInt64) = (0, 0)) {
self.protocolID = self.dynamicType.protocolConst
self.size = 64
self.status = status.rawValue
self._command = asyncCommand.rawValue
self.creditCharge = creditCharge
self.creditRequestResponse = creditRequestResponse
self.flags = flags.union([Flags.ASYNC_COMMAND])
self.nextCommand = nextCommand
self.messageId = messageId
self.reserved = UInt32(asyncId & 0xffffffff)
self.treeId = UInt32(asyncId >> 32)
self.sessionId = sessionId
self.signature = signature
}
}
struct Flags: OptionSetType {
var rawValue: UInt32
init(rawValue: UInt32) {
self.rawValue = rawValue
}
var priorityMask: UInt8 {
get {
return UInt8((rawValue & Flags.PRIORITY_MASK.rawValue) >> 4)
}
set {
rawValue = (rawValue & 0xffffff8f) | (UInt32(newValue & 0x7) << 4)
}
}
static let SERVER_TO_REDIR = Flags(rawValue: 0x00000001)
static let ASYNC_COMMAND = Flags(rawValue: 0x00000002)
static let RELATED_OPERATIONS = Flags(rawValue: 0x00000004)
static let SIGNED = Flags(rawValue: 0x00000008)
private static let PRIORITY_MASK = Flags(rawValue: 0x00000070)
static let DFS_OPERATIONS = Flags(rawValue: 0x10000000)
static let REPLAY_OPERATION = Flags(rawValue: 0x20000000)
}
enum Command: UInt16 {
case NEGOTIATE = 0x0000
case SESSION_SETUP = 0x0001
case LOGOFF = 0x0002
case TREE_CONNECT = 0x0003
case TREE_DISCONNECT = 0x0004
case CREATE = 0x0005
case CLOSE = 0x0006
case FLUSH = 0x0007
case READ = 0x0008
case WRITE = 0x0009
case LOCK = 0x000A
case IOCTL = 0x000B
case CANCEL = 0x000C
case ECHO = 0x000D
case QUERY_DIRECTORY = 0x000E
case CHANGE_NOTIFY = 0x000F
case QUERY_INFO = 0x0010
case SET_INFO = 0x0011
case OPLOCK_BREAK = 0x0012
case INVALID = 0xFFFF
}
// MARK: SMB2 Oplock Break
}
+208
View File
@@ -0,0 +1,208 @@
//
// SMBErrorType.swift
// ExtDownloader
//
// Created by Amir Abbas Mousavian on 4/30/95.
// Copyright © 1395 Mousavian. All rights reserved.
//
import Foundation
/// Error Types and Description
enum NTStatus: UInt32, ErrorType, CustomStringConvertible {
case SUCCESS = 0x00000000
case NOT_IMPLEMENTED = 0xC0000002
case INVALID_DEVICE_REQUEST = 0xC0000010
case ILLEGAL_FUNCTION = 0xC00000AF
case NO_SUCH_FILE = 0xC000000F
case NO_SUCH_DEVICE = 0xC000000E
case OBJECT_NAME_NOT_FOUND = 0xC0000034
case OBJECT_PATH_INVALID = 0xC0000039
case OBJECT_PATH_NOT_FOUND = 0xC000003A
case OBJECT_PATH_SYNTAX_BAD = 0xC000003B
case DFS_EXIT_PATH_FOUND = 0xC000009B
case REDIRECTOR_NOT_STARTED = 0xC00000FB
case TOO_MANY_OPENED_FILES = 0xC000011F
case ACCESS_DENIED = 0xC0000022
case INVALID_LOCK_SEQUENCE = 0xC000001E
case INVALID_VIEW_SIZE = 0xC000001F
case ALREADY_COMMITTED = 0xC0000021
case PORT_CONNECTION_REFUSED = 0xC0000041
case THREAD_IS_TERMINATING = 0xC000004B
case DELETE_PENDING = 0xC0000056
case PRIVILEGE_NOT_HELD = 0xC0000061
case LOGON_FAILURE = 0xC000006D
case FILE_IS_A_DIRECTORY = 0xC00000BA
case FILE_RENAMED = 0xC00000D5
case PROCESS_IS_TERMINATING = 0xC000010A
case DIRECTORY_NOT_EMPTY = 0xC0000101
case CANNOT_DELETE = 0xC0000121
case FILE_NOT_AVAILABLE = 0xC0000467
case FILE_DELETED = 0xC0000123
case SMB_BAD_FID = 0x00060001
case INVALID_HANDLE = 0xC0000008
case OBJECT_TYPE_MISMATCH = 0xC0000024
case PORT_DISCONNECTED = 0xC0000037
case INVALID_PORT_HANDLE = 0xC0000042
case FILE_CLOSED = 0xC0000128
case HANDLE_NOT_CLOSABLE = 0xC0000235
case SECTION_TOO_BIG = 0xC0000040
case TOO_MANY_PAGING_FILES = 0xC0000097
case INSUFF_SERVER_RESOURCES = 0xC0000205
case OS2_INVALID_ACCESS = 0x000C0001
case ACCESS_DENIED_2 = 0xC00000CA
case DATA_ERROR = 0xC000009C
case NOT_SAME_DEVICE = 0xC00000D4
case NO_MORE_FILES = 0x80000006
case NO_MORE_ENTRIES = 0x8000001A
case UNSUCCESSFUL = 0xC0000001
case SHARING_VIOLATION = 0xC0000043
case FILE_LOCK_CONFLICT = 0xC0000054
case LOCK_NOT_GRANTED = 0xC0000055
case END_OF_FILE = 0xC0000011
case NOT_SUPPORTED = 0xC00000BB
case OBJECT_NAME_COLLISION = 0xC0000035
case INVALID_PARAMETER = 0xC000000D
case OS2_INVALID_LEVEL = 0x007C0001
case OS2_NEGATIVE_SEEK = 0x00830001
case RANGE_NOT_LOCKED = 0xC000007E
case OS2_NO_MORE_SIDS = 0x00710001
case OS2_CANCEL_VIOLATION = 0x00AD0001
case OS2_ATOMIC_LOCKS_NOT_SUPPORTED = 0x00AE0001
case INVALID_INFO_CLASS = 0xC0000003
case INVALID_PIPE_STATE = 0xC00000AD
case INVALID_READ_MODE = 0xC00000B4
case OS2_CANNOT_COPY = 0x010A0001
case STOPPED_ON_SYMLINK = 0x8000002D
case INSTANCE_NOT_AVAILABLE = 0xC00000AB
case PIPE_NOT_AVAILABLE = 0xC00000AC
case PIPE_BUSY = 0xC00000AE
case PIPE_CLOSING = 0xC00000B1
case PIPE_EMPTY = 0xC00000D9
case PIPE_DISCONNECTED = 0xC00000B0
case BUFFER_OVERFLOW = 0x80000005
case MORE_PROCESSING_REQUIRED = 0xC0000016
case EA_TOO_LARGE = 0xC0000050
case OS2_EAS_DIDNT_FIT = 0x01130001
case EAS_NOT_SUPPORTED = 0xC000004F
case EA_LIST_INCONSISTENT = 0x80000014
case OS2_EA_ACCESS_DENIED = 0x03E20001
case NOTIFY_ENUM_DIR = 0x0000010C
case INVALID_SMB = 0x00010002
case WRONG_PASSWORD = 0xC000006A
case PATH_NOT_COVERED = 0xC0000257
case NETWORK_NAME_DELETED = 0xC00000C9
case SMB_BAD_TID = 0x00050002
case BAD_NETWORK_NAME = 0xC00000CC
case BAD_DEVICE_TYPE = 0xC00000CB
case SMB_BAD_COMMAND = 0x00160002
case PRINT_QUEUE_FULL = 0xC00000C6
case NO_SPOOL_SPACE = 0xC00000C7
case PRINT_CANCELLED = 0xC00000C8
case UNEXPECTED_NETWORK_ERROR = 0xC00000C4
case IO_TIMEOUT = 0xC00000B5
case REQUEST_NOT_ACCEPTED = 0xC00000D0
case TOO_MANY_SESSIONS = 0xC00000CE
case SMB_BAD_UID = 0x005B0002
case SMB_USE_MPX = 0x00FA0002
case SMB_USE_STANDARD = 0x00FB0002
case SMB_CONTINUE_MPX = 0x00FC0002
case ACCOUNT_DISABLED = 0xC0000072
case ACCOUNT_EXPIRED = 0xC0000193
case INVALID_WORKSTATION = 0xC0000070
case INVALID_LOGON_HOURS = 0xC000006F
case PASSWORD_EXPIRED = 0xC0000071
case PASSWORD_MUST_CHANGE = 0xC0000224
case SMB_NO_SUPPORT = 0xFFFF0002
case MEDIA_WRITE_PROTECTED = 0xC00000A2
case NO_MEDIA_IN_DEVICE = 0xC0000013
case INVALID_DEVICE_STATE = 0xC0000184
case DATA_ERROR_2 = 0xC000003E
case CRC_ERROR = 0xC000003F
case DISK_CORRUPT_ERROR = 0xC0000032
case NONEXISTENT_SECTOR = 0xC0000015
case DEVICE_PAPER_EMPTY = 0x8000000E
case WRONG_VOLUME = 0xC0000012
case DISK_FULL = 0xC000007F
case BUFFER_TOO_SMALL = 0xC0000023
case BAD_IMPERSONATION_LEVEL = 0xC00000A5
case USER_SESSION_DELETED = 0xC0000203
case NETWORK_SESSION_EXPIRED = 0xC000035C
case SMB_TOO_MANY_UIDS = 0xC000205A
var description: String {
switch self {
case NOT_IMPLEMENTED, INVALID_DEVICE_REQUEST, ILLEGAL_FUNCTION:
return "Invalid Function."
case NO_SUCH_FILE, NO_SUCH_DEVICE, OBJECT_NAME_NOT_FOUND:
return "File not found."
case OBJECT_PATH_INVALID, OBJECT_PATH_NOT_FOUND, OBJECT_PATH_SYNTAX_BAD, DFS_EXIT_PATH_FOUND, REDIRECTOR_NOT_STARTED:
return "A component in the path prefix is not a directory."
case TOO_MANY_OPENED_FILES:
return "Too many open files. No FIDs are available."
case ACCESS_DENIED, INVALID_LOCK_SEQUENCE, INVALID_VIEW_SIZE, ALREADY_COMMITTED, PORT_CONNECTION_REFUSED, THREAD_IS_TERMINATING, DELETE_PENDING, PRIVILEGE_NOT_HELD, LOGON_FAILURE, FILE_IS_A_DIRECTORY, FILE_RENAMED, PROCESS_IS_TERMINATING, CANNOT_DELETE, FILE_DELETED:
return "Access denied."
case SMB_BAD_FID, INVALID_HANDLE, OBJECT_TYPE_MISMATCH, PORT_DISCONNECTED, INVALID_PORT_HANDLE, FILE_CLOSED, HANDLE_NOT_CLOSABLE:
return "Invalid FID."
case SECTION_TOO_BIG, TOO_MANY_PAGING_FILES, INSUFF_SERVER_RESOURCES:
return "Insufficient server memory to perform the requested operation."
case OS2_INVALID_ACCESS:
return "Invalid open mode."
case DATA_ERROR:
return "Bad data. (May be generated by IOCTL calls on the server.)"
case DIRECTORY_NOT_EMPTY:
return "Remove of directory failed because it was not empty."
case NOT_SAME_DEVICE:
return "A file system operation (such as a rename) across two devices was attempted."
case NO_MORE_FILES:
return "No (more) files found following a file search command."
case UNSUCCESSFUL:
return "General error."
case SHARING_VIOLATION:
return "Sharing violation. A requested open mode conflicts with the sharing mode of an existing file handle."
case FILE_LOCK_CONFLICT, LOCK_NOT_GRANTED:
return "A lock request specified an invalid locking mode, or conflicted with an existing file lock."
case END_OF_FILE:
return "Attempted to read beyond the end of the file."
case NOT_SUPPORTED:
return "This command is not supported by the server."
case OBJECT_NAME_COLLISION:
return "An attempt to create a file or directory failed because an object with the same pathname already exists."
case INVALID_PARAMETER:
return "A parameter supplied with the message is invalid."
case OS2_INVALID_LEVEL:
return "Invalid information level."
case OS2_NEGATIVE_SEEK:
return "An attempt was made to seek to a negative absolute offset within a file."
case RANGE_NOT_LOCKED:
return "The byte range specified in an unlock request was not locked."
case OS2_NO_MORE_SIDS:
return "Maximum number of searches has been exhausted."
case OS2_CANCEL_VIOLATION:
return "No lock request was outstanding for the supplied cancel region."
case OS2_ATOMIC_LOCKS_NOT_SUPPORTED:
return "The file system does not support atomic changes to the lock type."
case INVALID_INFO_CLASS, INVALID_PIPE_STATE, INVALID_READ_MODE:
return "Invalid named pipe."
case OS2_CANNOT_COPY:
return "The copy functions cannot be used."
case INSTANCE_NOT_AVAILABLE, PIPE_NOT_AVAILABLE, PIPE_BUSY:
return "All instances of the designated named pipe are busy."
case PIPE_CLOSING, PIPE_EMPTY:
return "The designated named pipe is in the process of being closed."
case PIPE_DISCONNECTED:
return "The designated named pipe exists, but there is no server process listening on the server side."
case BUFFER_OVERFLOW, MORE_PROCESSING_REQUIRED:
return "There is more data available to read on the designated named pipe."
case EA_TOO_LARGE, OS2_EAS_DIDNT_FIT:
return "Either there are no extended attributes, or the available extended attributes did not fit into the response."
case EAS_NOT_SUPPORTED:
return "The server file system does not support Extended Attributes."
case OS2_EA_ACCESS_DENIED:
return "Access to the extended attribute was denied."
default:
return ""
}
}
}
@@ -51,10 +51,10 @@ public final class WebDavFileObject: FileObject {
let contentType: String
let entryTag: String?
init(absoluteURL: NSURL, name: String, size: Int64, contentType: String, createdDate: NSDate?, modifiedDate: NSDate?, fileType: FileType, isHidden: Bool, isReadOnly: Bool, entryTag: String?) {
init(absoluteURL: NSURL, name: String, path: String, size: Int64, contentType: String, createdDate: NSDate?, modifiedDate: NSDate?, fileType: FileType, isHidden: Bool, isReadOnly: Bool, entryTag: String?) {
self.contentType = contentType
self.entryTag = entryTag
super.init(absoluteURL: absoluteURL, name: name, size: size, createdDate: createdDate, modifiedDate: modifiedDate, fileType: fileType, isHidden: isHidden, isReadOnly: isReadOnly)
super.init(absoluteURL: absoluteURL, name: name, path: path, size: size, createdDate: createdDate, modifiedDate: modifiedDate, fileType: fileType, isHidden: isHidden, isReadOnly: isReadOnly)
}
}
@@ -71,14 +71,14 @@ public class WebDAVFileProvider: NSObject, FileProviderBasic {
assert(_session == nil, "It's not effective to change dispatch_queue property after session is initialized.")
}
}
public var delegate: FileProviderDelegate?
public weak var delegate: FileProviderDelegate?
public let credential: NSURLCredential?
private var _session: NSURLSession?
private var session: NSURLSession {
if _session == nil {
let queue = NSOperationQueue()
queue.underlyingQueue = dispatch_queue
//queue.underlyingQueue = dispatch_queue
_session = NSURLSession(configuration: NSURLSessionConfiguration.defaultSessionConfiguration(), delegate: self, delegateQueue: queue)
}
return _session!
@@ -145,11 +145,12 @@ public class WebDAVFileProvider: NSObject, FileProviderBasic {
}
task.resume()
}
public weak var fileOperationDelegate: FileOperationDelegate?
}
extension WebDAVFileProvider: FileProviderOperations {
public func createFolder(folderName: String, atPath: String, completionHandler: SimpleCompletionHandler) {
let session = NSURLSession(configuration: NSURLSessionConfiguration.defaultSessionConfiguration())
let url = absoluteURL((atPath as NSString).stringByAppendingPathComponent(folderName) + "/")
let request = NSMutableURLRequest(URL: url)
request.HTTPMethod = "MKCOL"
@@ -177,42 +178,21 @@ extension WebDAVFileProvider: FileProviderOperations {
}
public func moveItemAtPath(path: String, toPath: String, overwrite: Bool = false, completionHandler: SimpleCompletionHandler) {
let session = NSURLSession(configuration: NSURLSessionConfiguration.defaultSessionConfiguration())
let url = absoluteURL(path)
let request = NSMutableURLRequest(URL: url)
request.HTTPMethod = "MOVE"
request.setValue(baseURL?.absoluteString, forHTTPHeaderField: "Host")
request.setValue(absoluteURL(path).absoluteString, forHTTPHeaderField: "Destination")
if !overwrite {
request.setValue("F", forHTTPHeaderField: "Overwrite")
}
let task = session.dataTaskWithRequest(request) { (data, response, error) in
if let response = response as? NSHTTPURLResponse, let code = FileProviderWebDavErrorCode(rawValue: response.statusCode) {
defer {
self.delegateNotify(.Move(source: path, destination: toPath), error: error)
}
if code == .MultiStatus, let data = data {
let xresponses = self.parseXMLResponse(data)
for xresponse in xresponses {
if xresponse.status >= 300 {
completionHandler?(error: FileProviderWebDavError(code: code, url: url))
}
}
} else {
completionHandler?(error: FileProviderWebDavError(code: code, url: url))
}
return
}
completionHandler?(error: error)
}
task.resume()
self.copyMoveItemAtPath(true, path: path, toPath: toPath, overwrite: overwrite, completionHandler: completionHandler)
}
public func copyItemAtPath(path: String, toPath: String, overwrite: Bool = false, completionHandler: SimpleCompletionHandler) {
let session = NSURLSession(configuration: NSURLSessionConfiguration.defaultSessionConfiguration())
self.copyMoveItemAtPath(false, path: path, toPath: toPath, overwrite: overwrite, completionHandler: completionHandler)
}
private func copyMoveItemAtPath(move:Bool, path: String, toPath: String, overwrite: Bool, completionHandler: SimpleCompletionHandler) {
let url = absoluteURL(path)
let request = NSMutableURLRequest(URL: url)
request.HTTPMethod = "COPY"
if move {
request.HTTPMethod = "MOVE"
} else {
request.HTTPMethod = "COPY"
}
request.setValue(baseURL?.absoluteString, forHTTPHeaderField: "Host")
request.setValue(absoluteURL(path).absoluteString, forHTTPHeaderField: "Destination")
if !overwrite {
@@ -221,14 +201,13 @@ extension WebDAVFileProvider: FileProviderOperations {
let task = session.dataTaskWithRequest(request) { (data, response, error) in
if let response = response as? NSHTTPURLResponse, let code = FileProviderWebDavErrorCode(rawValue: response.statusCode) {
defer {
self.delegateNotify(.Copy(source: path, destination: toPath), error: error)
let op = move ? FileOperation.Move(source: path, destination: toPath) : .Copy(source: path, destination: toPath)
self.delegateNotify(op, error: error)
}
if code == .MultiStatus, let data = data {
let xresponses = self.parseXMLResponse(data)
for xresponse in xresponses {
if xresponse.status >= 300 {
completionHandler?(error: FileProviderWebDavError(code: code, url: url))
}
for xresponse in xresponses where xresponse.status >= 300 {
completionHandler?(error: FileProviderWebDavError(code: code, url: url))
}
} else {
completionHandler?(error: FileProviderWebDavError(code: code, url: url))
@@ -241,7 +220,6 @@ extension WebDAVFileProvider: FileProviderOperations {
}
public func removeItemAtPath(path: String, completionHandler: SimpleCompletionHandler) {
let session = NSURLSession(configuration: NSURLSessionConfiguration.defaultSessionConfiguration())
let url = absoluteURL(path)
let request = NSMutableURLRequest(URL: url)
request.HTTPMethod = "DELETE"
@@ -253,10 +231,8 @@ extension WebDAVFileProvider: FileProviderOperations {
}
if code == .MultiStatus, let data = data {
let xresponses = self.parseXMLResponse(data)
for xresponse in xresponses {
if xresponse.status >= 300 {
completionHandler?(error: FileProviderWebDavError(code: code, url: url))
}
for xresponse in xresponses where xresponse.status >= 300 {
completionHandler?(error: FileProviderWebDavError(code: code, url: url))
}
} else {
completionHandler?(error: FileProviderWebDavError(code: code, url: url))
@@ -299,19 +275,17 @@ extension WebDAVFileProvider: FileProviderOperations {
extension WebDAVFileProvider: FileProviderReadWrite {
public func contentsAtPath(path: String, completionHandler: ((contents: NSData?, error: ErrorType?) -> Void)) {
let request = NSMutableURLRequest(URL: absoluteURL(path))
request.HTTPMethod = "GET"
let task = session.dataTaskWithRequest(request) { (data, response, error) in
completionHandler(contents: data, error: error)
}
task.resume()
self.contentsAtPath(path, offset: 0, length: -1, completionHandler: completionHandler)
}
public func contentsAtPath(path: String, offset: Int64, length: Int, completionHandler: ((contents: NSData?, error: ErrorType?) -> Void)) {
let session = NSURLSession(configuration: NSURLSessionConfiguration.defaultSessionConfiguration())
let request = NSMutableURLRequest(URL: absoluteURL(path))
request.HTTPMethod = "GET"
request.setValue("bytes=\(offset)-\(offset + length)", forHTTPHeaderField: "Range")
if length > 0 {
request.setValue("bytes=\(offset)-\(offset + length)", forHTTPHeaderField: "Range")
} else if offset > 0 && length < 0 {
request.setValue("bytes=\(offset)-", forHTTPHeaderField: "Range")
}
let task = session.dataTaskWithRequest(request) { (data, response, error) in
completionHandler(contents: data, error: error)
}
@@ -399,16 +373,12 @@ internal extension WebDAVFileProvider {
let xml = try AEXMLDocument(xmlData: response)
var rootnode = xml.root
var responsetag = "response"
for node in rootnode.all ?? [] {
if node.name.lowercaseString.hasSuffix("multistatus") {
rootnode = node
}
for node in rootnode.all ?? [] where node.name.lowercaseString.hasSuffix("multistatus") {
rootnode = node
}
for node in rootnode.children ?? [] {
if node.name.lowercaseString.hasSuffix("response") {
responsetag = node.name
break
}
for node in rootnode.children ?? [] where node.name.lowercaseString.hasSuffix("response") {
responsetag = node.name
break
}
for responseNode in rootnode[responsetag].all ?? [] {
var hreftag = "href"
@@ -434,29 +404,23 @@ internal extension WebDAVFileProvider {
}
var propDic = [String: String]()
let propStatNode = responseNode[propstattag]
for node in propStatNode.children ?? [] {
if node.name.lowercaseString.hasSuffix("status") {
statustag = node.name
break
}
for node in propStatNode.children ?? [] where node.name.lowercaseString.hasSuffix("status"){
statustag = node.name
break
}
let statusDesc2 = (propStatNode[statustag].stringValue).componentsSeparatedByString(" ")
if statusDesc2.count > 2 {
status = Int(statusDesc2[1])
}
var proptag = "prop"
for tnode in propStatNode.children ?? [] {
if tnode.name.lowercaseString.hasSuffix("prop") {
proptag = tnode.name
}
for tnode in propStatNode.children ?? [] where tnode.name.lowercaseString.hasSuffix("prop") {
proptag = tnode.name
break
}
for propItemNode in propStatNode[proptag].children ?? [] {
propDic[propItemNode.name.componentsSeparatedByString(":").last!.lowercaseString] = propItemNode.value
if propItemNode.name.hasSuffix("resourcetype") {
if propItemNode.xmlStringCompact.containsString("collection") {
propDic["getcontenttype"] = "httpd/unix-directory"
}
if propItemNode.name.hasSuffix("resourcetype") && propItemNode.xmlStringCompact.containsString("collection") {
propDic["getcontenttype"] = "httpd/unix-directory"
}
}
result.append(DavResponse(href: hrefURL, hrefString: href, status: status, prop: propDic))
@@ -474,12 +438,12 @@ internal extension WebDAVFileProvider {
}
let name = davResponse.prop["displayname"] ?? (davResponse.hrefString.stringByRemovingPercentEncoding! as NSString).lastPathComponent
let size = Int64(davResponse.prop["getcontentlength"] ?? "-1") ?? NSURLSessionTransferSizeUnknown
let createdDate = self.resolveRFCDate(davResponse.prop["creationdate"] ?? "")
let modifiedDate = self.resolveRFCDate(davResponse.prop["getlastmodified"] ?? "")
let createdDate = self.resolveDate(davResponse.prop["creationdate"] ?? "")
let modifiedDate = self.resolveDate(davResponse.prop["getlastmodified"] ?? "")
let contentType = davResponse.prop["getcontenttype"] ?? "octet/stream"
let isDirectory = contentType == "httpd/unix-directory"
let entryTag = davResponse.prop["getetag"]
return WebDavFileObject(absoluteURL: href, name: name, size: size, contentType: contentType, createdDate: createdDate, modifiedDate: modifiedDate, fileType: isDirectory ? .Directory : .Regular, isHidden: false, isReadOnly: false, entryTag: entryTag)
return WebDavFileObject(absoluteURL: href, name: name, path: href.path ?? name, size: size, contentType: contentType, createdDate: createdDate, modifiedDate: modifiedDate, fileType: isDirectory ? .Directory : .Regular, isHidden: false, isReadOnly: false, entryTag: entryTag)
}
private func delegateNotify(operation: FileOperation, error: ErrorType?) {