Compare commits
5 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| bb9c08e309 | |||
| 315ead606e | |||
| d63e9c7f04 | |||
| a5fe28c18d | |||
| 542c18bab6 |
@@ -0,0 +1 @@
|
||||
3.0
|
||||
+1
-1
@@ -1,5 +1,5 @@
|
||||
language: objective-c
|
||||
osx_image: xcode7.3
|
||||
osx_image: xcode8
|
||||
script:
|
||||
- set pipefail
|
||||
- xcodebuild -project FileProvider.xcodeproj -scheme "FileProvider OSX" -sdk macosx | xcpretty
|
||||
|
||||
@@ -16,8 +16,8 @@ Pod::Spec.new do |s|
|
||||
#
|
||||
|
||||
s.name = "FileProvider"
|
||||
s.version = "0.4.1"
|
||||
s.summary = "NSFileManager replacement for Local and Remote (WebDAV/Dropbox/SMB2) files on iOS and MacOS."
|
||||
s.version = "0.5.1"
|
||||
s.summary = "FileManager replacement for Local and Remote (WebDAV/Dropbox/SMB2) files on iOS and MacOS."
|
||||
|
||||
# This description is used to generate tags and improve search results.
|
||||
# * Think: What does it do? Why did you write it? What is the focus?
|
||||
|
||||
@@ -10,6 +10,33 @@
|
||||
7902C0861D61B56D00564440 /* SessionDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7902C0851D61B56D00564440 /* SessionDelegate.swift */; };
|
||||
7902C0871D61B67100564440 /* SessionDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7902C0851D61B56D00564440 /* SessionDelegate.swift */; };
|
||||
7902C0881D61B67100564440 /* SessionDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7902C0851D61B56D00564440 /* SessionDelegate.swift */; };
|
||||
7924B1931D89DAE000589DB7 /* AEXML.h in Headers */ = {isa = PBXBuildFile; fileRef = 7924B18C1D89DAE000589DB7 /* AEXML.h */; };
|
||||
7924B1941D89DAE000589DB7 /* AEXML.h in Headers */ = {isa = PBXBuildFile; fileRef = 7924B18C1D89DAE000589DB7 /* AEXML.h */; };
|
||||
7924B1951D89DAE000589DB7 /* AEXML.h in Headers */ = {isa = PBXBuildFile; fileRef = 7924B18C1D89DAE000589DB7 /* AEXML.h */; };
|
||||
7924B1961D89DAE000589DB7 /* Document.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7924B18D1D89DAE000589DB7 /* Document.swift */; };
|
||||
7924B1971D89DAE000589DB7 /* Document.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7924B18D1D89DAE000589DB7 /* Document.swift */; };
|
||||
7924B1981D89DAE000589DB7 /* Document.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7924B18D1D89DAE000589DB7 /* Document.swift */; };
|
||||
7924B1991D89DAE000589DB7 /* Element.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7924B18E1D89DAE000589DB7 /* Element.swift */; };
|
||||
7924B19A1D89DAE000589DB7 /* Element.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7924B18E1D89DAE000589DB7 /* Element.swift */; };
|
||||
7924B19B1D89DAE000589DB7 /* Element.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7924B18E1D89DAE000589DB7 /* Element.swift */; };
|
||||
7924B19C1D89DAE000589DB7 /* Error.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7924B18F1D89DAE000589DB7 /* Error.swift */; };
|
||||
7924B19D1D89DAE000589DB7 /* Error.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7924B18F1D89DAE000589DB7 /* Error.swift */; };
|
||||
7924B19E1D89DAE000589DB7 /* Error.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7924B18F1D89DAE000589DB7 /* Error.swift */; };
|
||||
7924B19F1D89DAE000589DB7 /* Info.plist in Resources */ = {isa = PBXBuildFile; fileRef = 7924B1901D89DAE000589DB7 /* Info.plist */; };
|
||||
7924B1A01D89DAE000589DB7 /* Info.plist in Resources */ = {isa = PBXBuildFile; fileRef = 7924B1901D89DAE000589DB7 /* Info.plist */; };
|
||||
7924B1A11D89DAE000589DB7 /* Info.plist in Resources */ = {isa = PBXBuildFile; fileRef = 7924B1901D89DAE000589DB7 /* Info.plist */; };
|
||||
7924B1A21D89DAE000589DB7 /* Options.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7924B1911D89DAE000589DB7 /* Options.swift */; };
|
||||
7924B1A31D89DAE000589DB7 /* Options.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7924B1911D89DAE000589DB7 /* Options.swift */; };
|
||||
7924B1A41D89DAE000589DB7 /* Options.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7924B1911D89DAE000589DB7 /* Options.swift */; };
|
||||
7924B1A51D89DAE000589DB7 /* Parser.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7924B1921D89DAE000589DB7 /* Parser.swift */; };
|
||||
7924B1A61D89DAE000589DB7 /* Parser.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7924B1921D89DAE000589DB7 /* Parser.swift */; };
|
||||
7924B1A71D89DAE000589DB7 /* Parser.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7924B1921D89DAE000589DB7 /* Parser.swift */; };
|
||||
7924B1AD1D89F7D800589DB7 /* SMBClient.swift in Sources */ = {isa = PBXBuildFile; fileRef = 799396971D48C02300086753 /* SMBClient.swift */; };
|
||||
7924B1AE1D89F7D900589DB7 /* SMBClient.swift in Sources */ = {isa = PBXBuildFile; fileRef = 799396971D48C02300086753 /* SMBClient.swift */; };
|
||||
7924B1B01D89F7DE00589DB7 /* FPSStreamTask.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7924B1A81D89F79200589DB7 /* FPSStreamTask.swift */; };
|
||||
7924B1B11D89F7DF00589DB7 /* FPSStreamTask.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7924B1A81D89F79200589DB7 /* FPSStreamTask.swift */; };
|
||||
7924B1B21D89FCDA00589DB7 /* FPSStreamTask.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7924B1A81D89F79200589DB7 /* FPSStreamTask.swift */; };
|
||||
7924B1B31D89FD6400589DB7 /* SMBClient.swift in Sources */ = {isa = PBXBuildFile; fileRef = 799396971D48C02300086753 /* SMBClient.swift */; };
|
||||
794C21FE1D58912A00EC49B8 /* DropboxHelper.swift in Sources */ = {isa = PBXBuildFile; fileRef = 794C21FD1D58912A00EC49B8 /* DropboxHelper.swift */; };
|
||||
794C21FF1D58912A00EC49B8 /* DropboxHelper.swift in Sources */ = {isa = PBXBuildFile; fileRef = 794C21FD1D58912A00EC49B8 /* DropboxHelper.swift */; };
|
||||
794C22001D58912A00EC49B8 /* DropboxHelper.swift in Sources */ = {isa = PBXBuildFile; fileRef = 794C21FD1D58912A00EC49B8 /* DropboxHelper.swift */; };
|
||||
@@ -19,12 +46,6 @@
|
||||
794C220E1D591A4B00EC49B8 /* SMB2QueryTypes.swift in Sources */ = {isa = PBXBuildFile; fileRef = 794C220D1D591A4B00EC49B8 /* SMB2QueryTypes.swift */; };
|
||||
794C220F1D591A4B00EC49B8 /* SMB2QueryTypes.swift in Sources */ = {isa = PBXBuildFile; fileRef = 794C220D1D591A4B00EC49B8 /* SMB2QueryTypes.swift */; };
|
||||
794C22101D591A4B00EC49B8 /* SMB2QueryTypes.swift in Sources */ = {isa = PBXBuildFile; fileRef = 794C220D1D591A4B00EC49B8 /* SMB2QueryTypes.swift */; };
|
||||
794C22121D599FBE00EC49B8 /* FPSStreamTask.swift in Sources */ = {isa = PBXBuildFile; fileRef = 794C22111D599FBE00EC49B8 /* FPSStreamTask.swift */; };
|
||||
794C22131D599FBE00EC49B8 /* FPSStreamTask.swift in Sources */ = {isa = PBXBuildFile; fileRef = 794C22111D599FBE00EC49B8 /* FPSStreamTask.swift */; };
|
||||
794C22141D599FBE00EC49B8 /* FPSStreamTask.swift in Sources */ = {isa = PBXBuildFile; fileRef = 794C22111D599FBE00EC49B8 /* FPSStreamTask.swift */; };
|
||||
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 */; };
|
||||
@@ -34,9 +55,6 @@
|
||||
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 */; };
|
||||
@@ -80,17 +98,23 @@
|
||||
|
||||
/* Begin PBXFileReference section */
|
||||
7902C0851D61B56D00564440 /* SessionDelegate.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SessionDelegate.swift; sourceTree = "<group>"; };
|
||||
7924B18C1D89DAE000589DB7 /* AEXML.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AEXML.h; sourceTree = "<group>"; };
|
||||
7924B18D1D89DAE000589DB7 /* Document.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Document.swift; sourceTree = "<group>"; };
|
||||
7924B18E1D89DAE000589DB7 /* Element.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Element.swift; sourceTree = "<group>"; };
|
||||
7924B18F1D89DAE000589DB7 /* Error.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Error.swift; sourceTree = "<group>"; };
|
||||
7924B1901D89DAE000589DB7 /* Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
|
||||
7924B1911D89DAE000589DB7 /* Options.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Options.swift; sourceTree = "<group>"; };
|
||||
7924B1921D89DAE000589DB7 /* Parser.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Parser.swift; sourceTree = "<group>"; };
|
||||
7924B1A81D89F79200589DB7 /* FPSStreamTask.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = FPSStreamTask.swift; sourceTree = "<group>"; };
|
||||
794C21FD1D58912A00EC49B8 /* DropboxHelper.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DropboxHelper.swift; sourceTree = "<group>"; };
|
||||
794C22091D5893F800EC49B8 /* SMB2Notification.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SMB2Notification.swift; sourceTree = "<group>"; };
|
||||
794C220D1D591A4B00EC49B8 /* SMB2QueryTypes.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SMB2QueryTypes.swift; sourceTree = "<group>"; };
|
||||
794C22111D599FBE00EC49B8 /* FPSStreamTask.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = FPSStreamTask.swift; sourceTree = "<group>"; };
|
||||
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>"; };
|
||||
@@ -136,6 +160,20 @@
|
||||
/* End PBXFrameworksBuildPhase section */
|
||||
|
||||
/* Begin PBXGroup section */
|
||||
7924B18B1D89DAE000589DB7 /* AEXML */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
7924B18C1D89DAE000589DB7 /* AEXML.h */,
|
||||
7924B18D1D89DAE000589DB7 /* Document.swift */,
|
||||
7924B18E1D89DAE000589DB7 /* Element.swift */,
|
||||
7924B18F1D89DAE000589DB7 /* Error.swift */,
|
||||
7924B1901D89DAE000589DB7 /* Info.plist */,
|
||||
7924B1911D89DAE000589DB7 /* Options.swift */,
|
||||
7924B1921D89DAE000589DB7 /* Parser.swift */,
|
||||
);
|
||||
path = AEXML;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
7993965B1D48B7BF00086753 = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
@@ -169,16 +207,16 @@
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
799396991D48C02300086753 /* SMBTypes */,
|
||||
799396921D48C02300086753 /* AEXML.swift */,
|
||||
799396931D48C02300086753 /* DropboxFileProvider.swift */,
|
||||
7924B18B1D89DAE000589DB7 /* AEXML */,
|
||||
794C21FD1D58912A00EC49B8 /* DropboxHelper.swift */,
|
||||
799396941D48C02300086753 /* FileProvider.h */,
|
||||
799396951D48C02300086753 /* FileProvider.swift */,
|
||||
799396961D48C02300086753 /* LocalFileProvider.swift */,
|
||||
7902C0851D61B56D00564440 /* SessionDelegate.swift */,
|
||||
7924B1A81D89F79200589DB7 /* FPSStreamTask.swift */,
|
||||
799396971D48C02300086753 /* SMBClient.swift */,
|
||||
799396981D48C02300086753 /* SMBFileProvider.swift */,
|
||||
794C22111D599FBE00EC49B8 /* FPSStreamTask.swift */,
|
||||
799396A61D48C02300086753 /* WebDAVFileProvider.swift */,
|
||||
);
|
||||
path = Sources;
|
||||
@@ -211,6 +249,7 @@
|
||||
isa = PBXHeadersBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
7924B1931D89DAE000589DB7 /* AEXML.h in Headers */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
@@ -218,6 +257,7 @@
|
||||
isa = PBXHeadersBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
7924B1941D89DAE000589DB7 /* AEXML.h in Headers */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
@@ -225,6 +265,7 @@
|
||||
isa = PBXHeadersBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
7924B1951D89DAE000589DB7 /* AEXML.h in Headers */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
@@ -291,10 +332,11 @@
|
||||
7993965C1D48B7BF00086753 /* Project object */ = {
|
||||
isa = PBXProject;
|
||||
attributes = {
|
||||
LastUpgradeCheck = 0730;
|
||||
LastUpgradeCheck = 0800;
|
||||
TargetAttributes = {
|
||||
799396661D48B7F600086753 = {
|
||||
CreatedOnToolsVersion = 7.3.1;
|
||||
LastSwiftMigration = 0800;
|
||||
};
|
||||
799396741D48B80D00086753 = {
|
||||
CreatedOnToolsVersion = 7.3.1;
|
||||
@@ -328,6 +370,7 @@
|
||||
isa = PBXResourcesBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
7924B19F1D89DAE000589DB7 /* Info.plist in Resources */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
@@ -335,6 +378,7 @@
|
||||
isa = PBXResourcesBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
7924B1A01D89DAE000589DB7 /* Info.plist in Resources */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
@@ -342,6 +386,7 @@
|
||||
isa = PBXResourcesBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
7924B1A11D89DAE000589DB7 /* Info.plist in Resources */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
@@ -354,8 +399,11 @@
|
||||
files = (
|
||||
799396B31D48C02300086753 /* LocalFileProvider.swift in Sources */,
|
||||
799396D41D48C02300086753 /* SMB2Tree.swift in Sources */,
|
||||
7924B1A21D89DAE000589DB7 /* Options.swift in Sources */,
|
||||
7924B1991D89DAE000589DB7 /* Element.swift in Sources */,
|
||||
799396C81D48C02300086753 /* SMB2IOCtl.swift in Sources */,
|
||||
799396D71D48C02300086753 /* SMB2Types.swift in Sources */,
|
||||
7924B1B21D89FCDA00589DB7 /* FPSStreamTask.swift in Sources */,
|
||||
799396C51D48C02300086753 /* SMB2FileOperation.swift in Sources */,
|
||||
799396BF1D48C02300086753 /* SMB2DataTypes.swift in Sources */,
|
||||
799396B91D48C02300086753 /* SMBFileProvider.swift in Sources */,
|
||||
@@ -364,17 +412,18 @@
|
||||
799396BC1D48C02300086753 /* CIFSTypes.swift in Sources */,
|
||||
794C220A1D5893F800EC49B8 /* SMB2Notification.swift in Sources */,
|
||||
799396D11D48C02300086753 /* SMB2SetInfo.swift in Sources */,
|
||||
7924B1961D89DAE000589DB7 /* Document.swift in Sources */,
|
||||
7902C0861D61B56D00564440 /* SessionDelegate.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 */,
|
||||
794C22121D599FBE00EC49B8 /* FPSStreamTask.swift in Sources */,
|
||||
799396AA1D48C02300086753 /* DropboxFileProvider.swift in Sources */,
|
||||
7924B1B31D89FD6400589DB7 /* SMBClient.swift in Sources */,
|
||||
7924B1A51D89DAE000589DB7 /* Parser.swift in Sources */,
|
||||
799396B01D48C02300086753 /* FileProvider.swift in Sources */,
|
||||
799396B61D48C02300086753 /* SMBClient.swift in Sources */,
|
||||
7924B19C1D89DAE000589DB7 /* Error.swift in Sources */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
@@ -384,6 +433,9 @@
|
||||
files = (
|
||||
799396B41D48C02300086753 /* LocalFileProvider.swift in Sources */,
|
||||
799396D51D48C02300086753 /* SMB2Tree.swift in Sources */,
|
||||
7924B1A31D89DAE000589DB7 /* Options.swift in Sources */,
|
||||
7924B1B01D89F7DE00589DB7 /* FPSStreamTask.swift in Sources */,
|
||||
7924B19A1D89DAE000589DB7 /* Element.swift in Sources */,
|
||||
799396C91D48C02300086753 /* SMB2IOCtl.swift in Sources */,
|
||||
799396D81D48C02300086753 /* SMB2Types.swift in Sources */,
|
||||
799396C61D48C02300086753 /* SMB2FileOperation.swift in Sources */,
|
||||
@@ -394,17 +446,18 @@
|
||||
799396BD1D48C02300086753 /* CIFSTypes.swift in Sources */,
|
||||
794C220B1D5893F800EC49B8 /* SMB2Notification.swift in Sources */,
|
||||
799396D21D48C02300086753 /* SMB2SetInfo.swift in Sources */,
|
||||
7924B1971D89DAE000589DB7 /* Document.swift in Sources */,
|
||||
7902C0871D61B67100564440 /* SessionDelegate.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 */,
|
||||
794C22131D599FBE00EC49B8 /* FPSStreamTask.swift in Sources */,
|
||||
7924B1AD1D89F7D800589DB7 /* SMBClient.swift in Sources */,
|
||||
799396AB1D48C02300086753 /* DropboxFileProvider.swift in Sources */,
|
||||
7924B1A61D89DAE000589DB7 /* Parser.swift in Sources */,
|
||||
799396B11D48C02300086753 /* FileProvider.swift in Sources */,
|
||||
799396B71D48C02300086753 /* SMBClient.swift in Sources */,
|
||||
7924B19D1D89DAE000589DB7 /* Error.swift in Sources */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
@@ -414,6 +467,9 @@
|
||||
files = (
|
||||
799396B51D48C02300086753 /* LocalFileProvider.swift in Sources */,
|
||||
799396D61D48C02300086753 /* SMB2Tree.swift in Sources */,
|
||||
7924B1A41D89DAE000589DB7 /* Options.swift in Sources */,
|
||||
7924B1B11D89F7DF00589DB7 /* FPSStreamTask.swift in Sources */,
|
||||
7924B19B1D89DAE000589DB7 /* Element.swift in Sources */,
|
||||
799396CA1D48C02300086753 /* SMB2IOCtl.swift in Sources */,
|
||||
799396D91D48C02300086753 /* SMB2Types.swift in Sources */,
|
||||
799396C71D48C02300086753 /* SMB2FileOperation.swift in Sources */,
|
||||
@@ -424,17 +480,18 @@
|
||||
799396BE1D48C02300086753 /* CIFSTypes.swift in Sources */,
|
||||
794C220C1D5893F800EC49B8 /* SMB2Notification.swift in Sources */,
|
||||
799396D31D48C02300086753 /* SMB2SetInfo.swift in Sources */,
|
||||
7924B1981D89DAE000589DB7 /* Document.swift in Sources */,
|
||||
7902C0881D61B67100564440 /* SessionDelegate.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 */,
|
||||
794C22141D599FBE00EC49B8 /* FPSStreamTask.swift in Sources */,
|
||||
7924B1AE1D89F7D900589DB7 /* SMBClient.swift in Sources */,
|
||||
799396AC1D48C02300086753 /* DropboxFileProvider.swift in Sources */,
|
||||
7924B1A71D89DAE000589DB7 /* Parser.swift in Sources */,
|
||||
799396B21D48C02300086753 /* FileProvider.swift in Sources */,
|
||||
799396B81D48C02300086753 /* SMBClient.swift in Sources */,
|
||||
7924B19E1D89DAE000589DB7 /* Error.swift in Sources */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
@@ -444,12 +501,49 @@
|
||||
799396601D48B7BF00086753 /* Debug */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
CLANG_WARN_BOOL_CONVERSION = YES;
|
||||
CLANG_WARN_CONSTANT_CONVERSION = YES;
|
||||
CLANG_WARN_EMPTY_BODY = YES;
|
||||
CLANG_WARN_ENUM_CONVERSION = YES;
|
||||
CLANG_WARN_INFINITE_RECURSION = YES;
|
||||
CLANG_WARN_INT_CONVERSION = YES;
|
||||
CLANG_WARN_SUSPICIOUS_MOVE = YES;
|
||||
CLANG_WARN_UNREACHABLE_CODE = YES;
|
||||
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
|
||||
ENABLE_STRICT_OBJC_MSGSEND = YES;
|
||||
ENABLE_TESTABILITY = YES;
|
||||
GCC_NO_COMMON_BLOCKS = YES;
|
||||
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
|
||||
GCC_WARN_ABOUT_RETURN_TYPE = YES;
|
||||
GCC_WARN_UNDECLARED_SELECTOR = YES;
|
||||
GCC_WARN_UNINITIALIZED_AUTOS = YES;
|
||||
GCC_WARN_UNUSED_FUNCTION = YES;
|
||||
GCC_WARN_UNUSED_VARIABLE = YES;
|
||||
ONLY_ACTIVE_ARCH = YES;
|
||||
};
|
||||
name = Debug;
|
||||
};
|
||||
799396611D48B7BF00086753 /* Release */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
CLANG_WARN_BOOL_CONVERSION = YES;
|
||||
CLANG_WARN_CONSTANT_CONVERSION = YES;
|
||||
CLANG_WARN_EMPTY_BODY = YES;
|
||||
CLANG_WARN_ENUM_CONVERSION = YES;
|
||||
CLANG_WARN_INFINITE_RECURSION = YES;
|
||||
CLANG_WARN_INT_CONVERSION = YES;
|
||||
CLANG_WARN_SUSPICIOUS_MOVE = YES;
|
||||
CLANG_WARN_UNREACHABLE_CODE = YES;
|
||||
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
|
||||
ENABLE_STRICT_OBJC_MSGSEND = YES;
|
||||
GCC_NO_COMMON_BLOCKS = YES;
|
||||
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
|
||||
GCC_WARN_ABOUT_RETURN_TYPE = YES;
|
||||
GCC_WARN_UNDECLARED_SELECTOR = YES;
|
||||
GCC_WARN_UNINITIALIZED_AUTOS = YES;
|
||||
GCC_WARN_UNUSED_FUNCTION = YES;
|
||||
GCC_WARN_UNUSED_VARIABLE = YES;
|
||||
SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule";
|
||||
};
|
||||
name = Release;
|
||||
};
|
||||
@@ -472,7 +566,7 @@
|
||||
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";
|
||||
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "";
|
||||
COPY_PHASE_STRIP = NO;
|
||||
CURRENT_PROJECT_VERSION = 1;
|
||||
DEBUG_INFORMATION_FORMAT = dwarf;
|
||||
@@ -507,6 +601,7 @@
|
||||
SDKROOT = iphoneos;
|
||||
SKIP_INSTALL = YES;
|
||||
SWIFT_OPTIMIZATION_LEVEL = "-Onone";
|
||||
SWIFT_VERSION = 3.0;
|
||||
TARGETED_DEVICE_FAMILY = "1,2";
|
||||
VERSIONING_SYSTEM = "apple-generic";
|
||||
VERSION_INFO_PREFIX = "";
|
||||
@@ -532,7 +627,7 @@
|
||||
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";
|
||||
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "";
|
||||
COPY_PHASE_STRIP = NO;
|
||||
CURRENT_PROJECT_VERSION = 1;
|
||||
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
|
||||
@@ -559,6 +654,7 @@
|
||||
PRODUCT_NAME = FileProvider;
|
||||
SDKROOT = iphoneos;
|
||||
SKIP_INSTALL = YES;
|
||||
SWIFT_VERSION = 3.0;
|
||||
TARGETED_DEVICE_FAMILY = "1,2";
|
||||
VALIDATE_PRODUCT = YES;
|
||||
VERSIONING_SYSTEM = "apple-generic";
|
||||
@@ -585,7 +681,7 @@
|
||||
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
|
||||
CLANG_WARN_UNREACHABLE_CODE = YES;
|
||||
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
|
||||
CODE_SIGN_IDENTITY = "-";
|
||||
CODE_SIGN_IDENTITY = "";
|
||||
COMBINE_HIDPI_IMAGES = YES;
|
||||
COPY_PHASE_STRIP = NO;
|
||||
CURRENT_PROJECT_VERSION = 1;
|
||||
@@ -646,7 +742,7 @@
|
||||
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
|
||||
CLANG_WARN_UNREACHABLE_CODE = YES;
|
||||
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
|
||||
CODE_SIGN_IDENTITY = "-";
|
||||
CODE_SIGN_IDENTITY = "";
|
||||
COMBINE_HIDPI_IMAGES = YES;
|
||||
COPY_PHASE_STRIP = NO;
|
||||
CURRENT_PROJECT_VERSION = 1;
|
||||
@@ -699,6 +795,7 @@
|
||||
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
|
||||
CLANG_WARN_UNREACHABLE_CODE = YES;
|
||||
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
|
||||
"CODE_SIGN_IDENTITY[sdk=appletvos*]" = "";
|
||||
COPY_PHASE_STRIP = NO;
|
||||
CURRENT_PROJECT_VERSION = 1;
|
||||
DEBUG_INFORMATION_FORMAT = dwarf;
|
||||
@@ -758,6 +855,7 @@
|
||||
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
|
||||
CLANG_WARN_UNREACHABLE_CODE = YES;
|
||||
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
|
||||
"CODE_SIGN_IDENTITY[sdk=appletvos*]" = "";
|
||||
COPY_PHASE_STRIP = NO;
|
||||
CURRENT_PROJECT_VERSION = 1;
|
||||
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<Scheme
|
||||
LastUpgradeVersion = "0730"
|
||||
LastUpgradeVersion = "0800"
|
||||
version = "1.3">
|
||||
<BuildAction
|
||||
parallelizeBuildables = "YES"
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<Scheme
|
||||
LastUpgradeVersion = "0730"
|
||||
LastUpgradeVersion = "0800"
|
||||
version = "1.3">
|
||||
<BuildAction
|
||||
parallelizeBuildables = "YES"
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<Scheme
|
||||
LastUpgradeVersion = "0730"
|
||||
LastUpgradeVersion = "0800"
|
||||
version = "1.3">
|
||||
<BuildAction
|
||||
parallelizeBuildables = "YES"
|
||||
|
||||
@@ -24,7 +24,7 @@ Local and WebDAV providers are fully tested and can be used in production enviro
|
||||
|
||||
## Features
|
||||
|
||||
- [x] **LocalFileProvider** a wrapper around `NSFileManager` with some additions like searching and reading a portion of file.
|
||||
- [x] **LocalFileProvider** a wrapper around `FileManager` with some additions like searching and reading a portion of file.
|
||||
- [x] **WebDAVFileProvider** WebDAV protocol is defacto file transmission standard, replaced FTP.
|
||||
- [x] **DropboxFileProvider** A wrapper around Dropbox Web API. For now it has limitation in uploading files up to 150MB.
|
||||
- [ ] **SMBFileProvider** SMB2/3 introduced in 2006, which is a file and printer sharing protocol originated from Microsoft Windows and now is replacing AFP protocol on MacOS. I implemented data types and some basic functions but *main interface is not implemented yet!*. SMB1/CIFS is depericated and very tricky to be implemented
|
||||
@@ -33,9 +33,11 @@ Local and WebDAV providers are fully tested and can be used in production enviro
|
||||
|
||||
## Requirements
|
||||
|
||||
- **Swift 2.2 or 2.3**
|
||||
- **Swift 3**
|
||||
- iOS 8.0 , OSX 10.10
|
||||
- XCode 7.3
|
||||
- XCode 8.0
|
||||
|
||||
Legacy version is available in swift-2 branch
|
||||
|
||||
## Installation
|
||||
|
||||
@@ -76,14 +78,14 @@ For LocalFileProvider if you want to deal with `Documents` folder
|
||||
is equal to:
|
||||
|
||||
let documentPath = NSSearchPathForDirectoriesInDomains(NSSearchPathDirectory.DocumentDirectory, NSSearchPathDomainMask.UserDomainMask, true);
|
||||
let documentsURL = NSURL(fileURLWithPath: documentPath);
|
||||
let documentsURL = URL(fileURLWithPath: documentPath);
|
||||
let documentsProvider = LocalFileProvider(baseURL: documentsURL)
|
||||
|
||||
You can't change the base url later. and all paths are related to this base url by default.
|
||||
|
||||
For remote file providers authentication may be necessary:
|
||||
|
||||
let credential = NSURLCredential(user: "user", password: "pass", persistence: NSURLCredentialPersistence.Permanent)
|
||||
let credential = URLCredential(user: "user", password: "pass", persistence: URLCredentialPersistence.Permanent)
|
||||
let webdavProvider = WebDAVFileProvider(baseURL: NSURL(string: "http://www.example.com/dav")!, credential: credential)
|
||||
|
||||
* In case you want to connect non-secure servers for WebDAV (http) in iOS 9+ / macOS 10.11+ you should disable App Transport Security (ATS) according to [this guide.](https://gist.github.com/mlynch/284699d676fe9ed0abfa)
|
||||
@@ -103,35 +105,35 @@ It's simply tree method which indicated whether the operation failed, succeed an
|
||||
Your class should conforms `FileProviderDelegate` class:
|
||||
|
||||
override func viewDidLoad() {
|
||||
documentsProvider.delegate = self
|
||||
documentsProvider.delegate = self as FileProviderDelegate
|
||||
}
|
||||
|
||||
func fileproviderSucceed(fileProvider: FileProviderOperations, operation: FileOperation) {
|
||||
func fileproviderSucceed(_ fileProvider: FileProviderOperations, operation: FileOperation) {
|
||||
switch operation {
|
||||
case .Copy(source: let source, destination: let dest):
|
||||
NSLog("\(source) copied to \(dest).")
|
||||
case .Remove(path: let path):
|
||||
NSLog("\(path) has been deleted.")
|
||||
case .copy(source: let source, destination: let dest):
|
||||
print("\(source) copied to \(dest).")
|
||||
case .remove(path: let path):
|
||||
print("\(path) has been deleted.")
|
||||
default:
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
func fileproviderFailed(fileProvider: FileProviderOperations, operation: FileOperation) {
|
||||
func fileproviderFailed(_ fileProvider: FileProviderOperations, operation: FileOperation) {
|
||||
switch operation {
|
||||
case .Copy(source: let source, destination: let dest):
|
||||
NSLog("copy of \(source) failed.")
|
||||
case .Remove(path: let path):
|
||||
NSLog("\(path) can't be deleted.")
|
||||
case .copy(source: let source, destination: let dest):
|
||||
print("copy of \(source) failed.")
|
||||
case .remove(path: let path):
|
||||
print("\(path) can't be deleted.")
|
||||
default:
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
func fileproviderProgress(fileProvider: FileProviderOperations, operation: FileOperation, progress: Float) {
|
||||
func fileproviderProgress(_ fileProvider: FileProviderOperations, operation: FileOperation, progress: Float) {
|
||||
switch operation {
|
||||
case .Copy(source: let source, destination: let dest):
|
||||
NSLog("Copy\(source) to \(dest): \(progress * 100) completed.")
|
||||
case .copy(source: let source, destination: let dest):
|
||||
print("Copy\(source) to \(dest): \(progress * 100) completed.")
|
||||
default:
|
||||
break
|
||||
}
|
||||
@@ -157,7 +159,7 @@ There is a `FileObject` class which holds file attributes like size and creation
|
||||
|
||||
For a single file:
|
||||
|
||||
documentsProvider.attributesOfItemAtPath(path: "/file.txt", completionHandler: {
|
||||
documentsProvider.attributesOfItem(path: "/file.txt", completionHandler: {
|
||||
(attributes: LocalFileObject?, error: ErrorType?) -> Void in
|
||||
if let attributes = attributes {
|
||||
print("File Size: \(attributes.size)")
|
||||
@@ -169,7 +171,7 @@ For a single file:
|
||||
|
||||
To get list of files in a directory:
|
||||
|
||||
documentsProvider.contentsOfDirectoryAtPath(path: "/", completionHandler: {
|
||||
documentsProvider.contentsOfDirectory(path: "/", completionHandler: {
|
||||
(contents: [LocalFileObject], error: ErrorType?) -> Void in
|
||||
for file in contents {
|
||||
print("Name: \(attributes.name)")
|
||||
@@ -200,27 +202,27 @@ You can then pass "" (empty string) to contentsOfDirectoryAtPath method to list
|
||||
|
||||
Creating new directory:
|
||||
|
||||
documentsProvider.createFolder(folderName: "new folder", atPath: "/", completionHandler: nil)
|
||||
documentsProvider.create(folder: "new folder", at: "/", 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)
|
||||
documentsProvider.createFile(fileAttribs: file, atPath: "/", contents: data, completionHandler: nil)
|
||||
let data = "hello world!".data(encoding: String.encoding.utf8)
|
||||
let file = FileObject(name: "old.txt", createdDate: Date(), modifiedDate: Date(), isHidden: false, isReadOnly: true)
|
||||
documentsProvider.create(file: file, at: "/", contents: data, completionHandler: nil)
|
||||
|
||||
### Copy and Move/Rename Files
|
||||
|
||||
Copy file old.txt to new.txt in current path:
|
||||
|
||||
documentsProvider.copyItemAtPath(path: "new folder/old.txt", toPath: "new.txt", overwrite: false, completionHandler: nil)
|
||||
documentsProvider.copyItem(path: "new folder/old.txt", to: "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)
|
||||
documentsProvider.moveItem(path: "new folder/old.txt", to: "new.txt", overwrite: false, completionHandler: nil)
|
||||
|
||||
### Delete Files
|
||||
|
||||
documentsProvider.removeItemAtPath(path: "new.txt", completionHandler: nil)
|
||||
documentsProvider.removeItem(path: "new.txt", completionHandler: nil)
|
||||
|
||||
***Caution:*** This method will delete directories with all it's content recursively.
|
||||
|
||||
@@ -228,38 +230,38 @@ Move file old.txt to new.txt in current path:
|
||||
|
||||
There is two method for this purpose, one of them loads entire file into NSData and another can load a portion of file.
|
||||
|
||||
documentsProvider.contentsAtPath(path: "old.txt", completionHandler: {
|
||||
(contents: NSData?, error: ErrorType?) -> Void
|
||||
documentsProvider.contents(path: "old.txt", completionHandler: {
|
||||
(contents: Data?, error: ErrorType?) -> Void
|
||||
if let contents = contents {
|
||||
print(String(data: contents, encoding: NSUTF8StringEncoding)) // "hello world!"
|
||||
print(String(data: contents, encoding: String.encoding.utf8)) // "hello world!"
|
||||
}
|
||||
})
|
||||
|
||||
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.
|
||||
|
||||
documentsProvider.contentsAtPath(path: "old.txt", offset: 2, length: 5, completionHandler: {
|
||||
(contents: NSData?, error: ErrorType?) -> Void
|
||||
documentsProvider.contents(path: "old.txt", offset: 2, length: 5, completionHandler: {
|
||||
(contents: Data?, error: ErrorType?) -> Void
|
||||
if let contents = contents {
|
||||
print(String(data: contents, encoding: NSUTF8StringEncoding)) // "llo w"
|
||||
print(String(data: contents, encoding: String.encoding.utf8)) // "llo w"
|
||||
}
|
||||
})
|
||||
|
||||
### Write Data To Files
|
||||
|
||||
let data = "What's up Newyork!".dataUsingEncoding(NSUTF8StringEncoding)
|
||||
documentsProvider.writeContentsAtPath(path: "old.txt", contents data: data, atomically: true, completionHandler: nil)
|
||||
let data = "What's up Newyork!".data(encoding: String.encoding.utf8)
|
||||
documentsProvider.writeContents(path: "old.txt", content: 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)
|
||||
documentsProvider.registerNotifcation(path: provider.currentPath)
|
||||
{
|
||||
// calling functions to update UI
|
||||
}
|
||||
|
||||
// To discontinue monitoring folders:
|
||||
documentsProvider.unregisterNotifcation(provider.currentPath)
|
||||
documentsProvider.unregisterNotifcation(path: provider.currentPath)
|
||||
|
||||
* **Please note** in LocalFileProvider it will also monitor changes in subfolders. This behaviour can varies according to file system specification.
|
||||
|
||||
@@ -282,7 +284,7 @@ Distributed under the MIT license. See `LICENSE` for more information.
|
||||
|
||||
[https://github.com/amosavian/](https://github.com/amosavian/)
|
||||
|
||||
[swift-image]:https://img.shields.io/badge/Swift-2.2%2C%202.3-green.svg
|
||||
[swift-image]:https://img.shields.io/badge/swift-3.0-orange.svg
|
||||
[swift-url]: https://swift.org/
|
||||
[license-image]: https://img.shields.io/badge/License-MIT-blue.svg
|
||||
[license-url]: LICENSE
|
||||
|
||||
@@ -1,492 +0,0 @@
|
||||
//
|
||||
// AEXML.swift
|
||||
//
|
||||
// Copyright (c) 2014 Marko Tadić <tadija@me.com> http://tadija.net
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to deal
|
||||
// in the Software without restriction, including without limitation the rights
|
||||
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
// copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in all
|
||||
// copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
// SOFTWARE.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
// MARK: - AEXMLElement
|
||||
|
||||
/**
|
||||
This is base class for holding XML structure.
|
||||
|
||||
You can access its structure by using subscript like this: `element["foo"]["bar"]` which would
|
||||
return `<bar></bar>` element from `<element><foo><bar></bar></foo></element>` XML as an `AEXMLElement` object.
|
||||
*/
|
||||
public class AEXMLElement {
|
||||
|
||||
/// A type representing an error value that can be inside `error` property.
|
||||
public enum Error: ErrorType {
|
||||
case ElementNotFound
|
||||
case RootElementMissing
|
||||
}
|
||||
|
||||
private struct Defaults {
|
||||
static let name = String()
|
||||
static let attributes = [String : String]()
|
||||
}
|
||||
|
||||
// MARK: Properties
|
||||
|
||||
/// Every `AEXMLElement` should have its parent element instead of `AEXMLDocument` which parent is `nil`.
|
||||
public private(set) weak var parent: AEXMLElement?
|
||||
|
||||
/// Child XML elements.
|
||||
public private(set) var children: [AEXMLElement] = [AEXMLElement]()
|
||||
|
||||
/// XML Element name (defaults to empty string).
|
||||
public var name: String
|
||||
|
||||
/// XML Element value.
|
||||
public var value: String?
|
||||
|
||||
/// XML Element attributes (defaults to empty dictionary).
|
||||
public var attributes: [String : String]
|
||||
|
||||
/// Error value (`nil` if there is no error).
|
||||
public var error: Error?
|
||||
|
||||
/// String representation of `value` property (if `value` is `nil` this is empty String).
|
||||
public var stringValue: String { return value ?? String() }
|
||||
|
||||
/// Boolean representation of `value` property (if `value` is "true" or 1 this is `True`, otherwise `False`).
|
||||
public var boolValue: Bool { return stringValue.lowercaseString == "true" || Int(stringValue) == 1 ? true : false }
|
||||
|
||||
/// Integer representation of `value` property (this is **0** if `value` can't be represented as Integer).
|
||||
public var intValue: Int { return Int(stringValue) ?? 0 }
|
||||
|
||||
/// Double representation of `value` property (this is **0.00** if `value` can't be represented as Double).
|
||||
public var doubleValue: Double { return (stringValue as NSString).doubleValue }
|
||||
|
||||
// MARK: Lifecycle
|
||||
|
||||
/**
|
||||
Designated initializer - all parameters are optional.
|
||||
|
||||
- parameter name: XML element name.
|
||||
- parameter value: XML element value
|
||||
- parameter attributes: XML element attributes
|
||||
|
||||
- returns: An initialized `AEXMLElement` object.
|
||||
*/
|
||||
public init(_ name: String? = nil, value: String? = nil, attributes: [String : String]? = nil) {
|
||||
self.name = name ?? Defaults.name
|
||||
self.value = value
|
||||
self.attributes = attributes ?? Defaults.attributes
|
||||
}
|
||||
|
||||
// MARK: XML Read
|
||||
|
||||
/// The first element with given name **(Empty element with error if not exists)**.
|
||||
public subscript(key: String) -> AEXMLElement {
|
||||
guard let
|
||||
first = children.filter({ $0.name == key }).first
|
||||
else {
|
||||
let errorElement = AEXMLElement(key)
|
||||
errorElement.error = Error.ElementNotFound
|
||||
return errorElement
|
||||
}
|
||||
return first
|
||||
}
|
||||
|
||||
/// Returns all of the elements with equal name as `self` **(nil if not exists)**.
|
||||
public var all: [AEXMLElement]? { return parent?.children.filter { $0.name == self.name } }
|
||||
|
||||
/// Returns the first element with equal name as `self` **(nil if not exists)**.
|
||||
public var first: AEXMLElement? { return all?.first }
|
||||
|
||||
/// Returns the last element with equal name as `self` **(nil if not exists)**.
|
||||
public var last: AEXMLElement? { return all?.last }
|
||||
|
||||
/// Returns number of all elements with equal name as `self`.
|
||||
public var count: Int { return all?.count ?? 0 }
|
||||
|
||||
private func allWithCondition(fulfillCondition: (element: AEXMLElement) -> Bool) -> [AEXMLElement]? {
|
||||
var found = [AEXMLElement]()
|
||||
if let elements = all {
|
||||
for element in elements {
|
||||
if fulfillCondition(element: element) {
|
||||
found.append(element)
|
||||
}
|
||||
}
|
||||
return found.count > 0 ? found : nil
|
||||
} else {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
Returns all elements with given value.
|
||||
|
||||
- parameter value: XML element value.
|
||||
|
||||
- returns: Optional Array of found XML elements.
|
||||
*/
|
||||
public func allWithValue(value: String) -> [AEXMLElement]? {
|
||||
let found = allWithCondition { (element) -> Bool in
|
||||
return element.value == value
|
||||
}
|
||||
return found
|
||||
}
|
||||
|
||||
/**
|
||||
Returns all elements with given attributes.
|
||||
|
||||
- parameter attributes: Dictionary of Keys and Values of attributes.
|
||||
|
||||
- returns: Optional Array of found XML elements.
|
||||
*/
|
||||
public func allWithAttributes(attributes: [String : String]) -> [AEXMLElement]? {
|
||||
let found = allWithCondition { (element) -> Bool in
|
||||
var countAttributes = 0
|
||||
for (key, value) in attributes {
|
||||
if element.attributes[key] == value {
|
||||
countAttributes += 1
|
||||
}
|
||||
}
|
||||
return countAttributes == attributes.count
|
||||
}
|
||||
return found
|
||||
}
|
||||
|
||||
// MARK: XML Write
|
||||
|
||||
/**
|
||||
Adds child XML element to `self`.
|
||||
|
||||
- parameter child: Child XML element to add.
|
||||
|
||||
- returns: Child XML element with `self` as `parent`.
|
||||
*/
|
||||
public func addChild(child: AEXMLElement) -> AEXMLElement {
|
||||
child.parent = self
|
||||
children.append(child)
|
||||
return child
|
||||
}
|
||||
|
||||
/**
|
||||
Adds child XML element to `self`.
|
||||
|
||||
- parameter name: Child XML element name.
|
||||
- parameter value: Child XML element value.
|
||||
- parameter attributes: Child XML element attributes.
|
||||
|
||||
- returns: Child XML element with `self` as `parent`.
|
||||
*/
|
||||
public func addChild(name name: String, value: String? = nil, attributes: [String : String]? = nil) -> AEXMLElement {
|
||||
let child = AEXMLElement(name, value: value, attributes: attributes)
|
||||
return addChild(child)
|
||||
}
|
||||
|
||||
/// Removes `self` from `parent` XML element.
|
||||
public func removeFromParent() {
|
||||
parent?.removeChild(self)
|
||||
}
|
||||
|
||||
private func removeChild(child: AEXMLElement) {
|
||||
if let childIndex = children.indexOf({ $0 === child }) {
|
||||
children.removeAtIndex(childIndex)
|
||||
}
|
||||
}
|
||||
|
||||
private var parentsCount: Int {
|
||||
var count = 0
|
||||
var element = self
|
||||
while let parent = element.parent {
|
||||
count += 1
|
||||
element = parent
|
||||
}
|
||||
return count
|
||||
}
|
||||
|
||||
private func indentation(depth: Int) -> String {
|
||||
var count = depth
|
||||
var indent = String()
|
||||
|
||||
while count > 0 {
|
||||
indent += "\t"
|
||||
count -= 1
|
||||
}
|
||||
|
||||
return indent
|
||||
}
|
||||
|
||||
/// Complete hierarchy of `self` and `children` in **XML** escaped and formatted String
|
||||
public var xmlString: String {
|
||||
var xml = String()
|
||||
|
||||
// open element
|
||||
xml += indentation(parentsCount - 1)
|
||||
xml += "<\(name)"
|
||||
|
||||
if attributes.count > 0 {
|
||||
// insert attributes
|
||||
for (key, value) in attributes {
|
||||
xml += " \(key)=\"\(value.xmlEscaped)\""
|
||||
}
|
||||
}
|
||||
|
||||
if value == nil && children.count == 0 {
|
||||
// close element
|
||||
xml += " />"
|
||||
} else {
|
||||
if children.count > 0 {
|
||||
// add children
|
||||
xml += ">\n"
|
||||
for child in children {
|
||||
xml += "\(child.xmlString)\n"
|
||||
}
|
||||
// add indentation
|
||||
xml += indentation(parentsCount - 1)
|
||||
xml += "</\(name)>"
|
||||
} else {
|
||||
// insert string value and close element
|
||||
xml += ">\(stringValue.xmlEscaped)</\(name)>"
|
||||
}
|
||||
}
|
||||
|
||||
return xml
|
||||
}
|
||||
|
||||
/// Same as `xmlString` but without `\n` and `\t` characters
|
||||
public var xmlStringCompact: String {
|
||||
let chars = NSCharacterSet(charactersInString: "\n\t")
|
||||
return xmlString.componentsSeparatedByCharactersInSet(chars).joinWithSeparator("")
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public extension String {
|
||||
|
||||
/// String representation of self with XML special characters escaped.
|
||||
public var xmlEscaped: String {
|
||||
// we need to make sure "&" is escaped first. Not doing this may break escaping the other characters
|
||||
var escaped = stringByReplacingOccurrencesOfString("&", withString: "&", options: .LiteralSearch)
|
||||
|
||||
// replace the other four special characters
|
||||
let escapeChars = ["<" : "<", ">" : ">", "'" : "'", "\"" : """]
|
||||
for (char, echar) in escapeChars {
|
||||
escaped = escaped.stringByReplacingOccurrencesOfString(char, withString: echar, options: .LiteralSearch)
|
||||
}
|
||||
|
||||
return escaped
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// MARK: - AEXMLDocument
|
||||
|
||||
/**
|
||||
This class is inherited from `AEXMLElement` and has a few addons to represent **XML Document**.
|
||||
|
||||
XML Parsing is also done with this object.
|
||||
*/
|
||||
public class AEXMLDocument: AEXMLElement {
|
||||
|
||||
private struct Defaults {
|
||||
static let version = 1.0
|
||||
static let encoding = "utf-8"
|
||||
static let standalone = "no"
|
||||
static let documentName = "AEXMLDocument"
|
||||
}
|
||||
|
||||
/// Default options used by NSXMLParser
|
||||
public struct NSXMLParserOptions {
|
||||
public var shouldProcessNamespaces = false
|
||||
public var shouldReportNamespacePrefixes = false
|
||||
public var shouldResolveExternalEntities = false
|
||||
|
||||
public init() {}
|
||||
}
|
||||
|
||||
// MARK: Properties
|
||||
|
||||
/// This is only used for XML Document header (default value is 1.0).
|
||||
public let version: Double
|
||||
|
||||
/// This is only used for XML Document header (default value is "utf-8").
|
||||
public let encoding: String
|
||||
|
||||
/// This is only used for XML Document header (default value is "no").
|
||||
public let standalone: String
|
||||
|
||||
/// Options for NSXMLParser (default values are `false`)
|
||||
public let xmlParserOptions: NSXMLParserOptions
|
||||
|
||||
/// Root (the first child element) element of XML Document **(Empty element with error if not exists)**.
|
||||
public var root: AEXMLElement {
|
||||
guard let rootElement = children.first else {
|
||||
let errorElement = AEXMLElement()
|
||||
errorElement.error = Error.RootElementMissing
|
||||
return errorElement
|
||||
}
|
||||
return rootElement
|
||||
}
|
||||
|
||||
// MARK: Lifecycle
|
||||
|
||||
/**
|
||||
Designated initializer - Creates and returns XML Document object.
|
||||
|
||||
- parameter version: Version value for XML Document header (defaults to 1.0).
|
||||
- parameter encoding: Encoding value for XML Document header (defaults to "utf-8").
|
||||
- parameter standalone: Standalone value for XML Document header (defaults to "no").
|
||||
- parameter root: Root XML element for XML Document (defaults to `nil`).
|
||||
- parameter xmlParserOptions: Options for NSXMLParser (defaults to `false` for all).
|
||||
|
||||
- returns: An initialized XML Document object.
|
||||
*/
|
||||
public init(version: Double = Defaults.version,
|
||||
encoding: String = Defaults.encoding,
|
||||
standalone: String = Defaults.standalone,
|
||||
root: AEXMLElement? = nil,
|
||||
xmlParserOptions: NSXMLParserOptions = NSXMLParserOptions())
|
||||
{
|
||||
// set document properties
|
||||
self.version = version
|
||||
self.encoding = encoding
|
||||
self.standalone = standalone
|
||||
self.xmlParserOptions = xmlParserOptions
|
||||
|
||||
// init super with default name
|
||||
super.init(Defaults.documentName)
|
||||
|
||||
// document has no parent element
|
||||
parent = nil
|
||||
|
||||
// add root element to document (if any)
|
||||
if let rootElement = root {
|
||||
addChild(rootElement)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
Convenience initializer - used for parsing XML data (by calling `loadXMLData:` internally).
|
||||
|
||||
- parameter version: Version value for XML Document header (defaults to 1.0).
|
||||
- parameter encoding: Encoding value for XML Document header (defaults to "utf-8").
|
||||
- parameter standalone: Standalone value for XML Document header (defaults to "no").
|
||||
- parameter xmlData: XML data to parse.
|
||||
- parameter xmlParserOptions: Options for NSXMLParser (defaults to `false` for all).
|
||||
|
||||
- returns: An initialized XML Document object containing parsed data. Throws error if data could not be parsed.
|
||||
*/
|
||||
public convenience init(version: Double = Defaults.version,
|
||||
encoding: String = Defaults.encoding,
|
||||
standalone: String = Defaults.standalone,
|
||||
xmlData: NSData,
|
||||
xmlParserOptions: NSXMLParserOptions = NSXMLParserOptions()) throws
|
||||
{
|
||||
self.init(version: version, encoding: encoding, standalone: standalone, xmlParserOptions: xmlParserOptions)
|
||||
try loadXMLData(xmlData)
|
||||
}
|
||||
|
||||
// MARK: Read XML
|
||||
|
||||
/**
|
||||
Creates instance of `AEXMLParser` (private class which is simple wrapper around `NSXMLParser`)
|
||||
and starts parsing the given XML data. Throws error if data could not be parsed.
|
||||
|
||||
- parameter data: XML which should be parsed.
|
||||
*/
|
||||
public func loadXMLData(data: NSData) throws {
|
||||
children.removeAll(keepCapacity: false)
|
||||
let xmlParser = AEXMLParser(xmlDocument: self, xmlData: data)
|
||||
try xmlParser.parse()
|
||||
}
|
||||
|
||||
// MARK: Override
|
||||
|
||||
/// Override of `xmlString` property of `AEXMLElement` - it just inserts XML Document header at the beginning.
|
||||
public override var xmlString: String {
|
||||
var xml = "<?xml version=\"\(version)\" encoding=\"\(encoding)\" standalone=\"\(standalone)\"?>\n"
|
||||
for child in children {
|
||||
xml += child.xmlString
|
||||
}
|
||||
return xml
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// MARK: - AEXMLParser
|
||||
|
||||
private class AEXMLParser: NSObject, NSXMLParserDelegate {
|
||||
|
||||
// MARK: Properties
|
||||
|
||||
let xmlDocument: AEXMLDocument
|
||||
let xmlData: NSData
|
||||
|
||||
var currentParent: AEXMLElement?
|
||||
var currentElement: AEXMLElement?
|
||||
var currentValue = String()
|
||||
var parseError: NSError?
|
||||
|
||||
// MARK: Lifecycle
|
||||
|
||||
init(xmlDocument: AEXMLDocument, xmlData: NSData) {
|
||||
self.xmlDocument = xmlDocument
|
||||
self.xmlData = xmlData
|
||||
currentParent = xmlDocument
|
||||
super.init()
|
||||
}
|
||||
|
||||
// MARK: XML Parse
|
||||
|
||||
func parse() throws {
|
||||
let parser = NSXMLParser(data: xmlData)
|
||||
parser.delegate = self
|
||||
|
||||
parser.shouldProcessNamespaces = xmlDocument.xmlParserOptions.shouldProcessNamespaces
|
||||
parser.shouldReportNamespacePrefixes = xmlDocument.xmlParserOptions.shouldReportNamespacePrefixes
|
||||
parser.shouldResolveExternalEntities = xmlDocument.xmlParserOptions.shouldResolveExternalEntities
|
||||
|
||||
let success = parser.parse()
|
||||
if !success {
|
||||
throw parseError ?? NSError(domain: "net.tadija.AEXML", code: 1, userInfo: nil)
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: NSXMLParserDelegate
|
||||
|
||||
@objc func parser(parser: NSXMLParser, didStartElement elementName: String, namespaceURI: String?, qualifiedName qName: String?, attributes attributeDict: [String : String]) {
|
||||
currentValue = String()
|
||||
currentElement = currentParent?.addChild(name: elementName, attributes: attributeDict)
|
||||
currentParent = currentElement
|
||||
}
|
||||
|
||||
@objc func parser(parser: NSXMLParser, foundCharacters string: String) {
|
||||
currentValue += string
|
||||
let newValue = currentValue.stringByTrimmingCharactersInSet(NSCharacterSet.whitespaceAndNewlineCharacterSet())
|
||||
currentElement?.value = newValue == String() ? nil : newValue
|
||||
}
|
||||
|
||||
@objc func parser(parser: NSXMLParser, didEndElement elementName: String, namespaceURI: String?, qualifiedName qName: String?) {
|
||||
currentParent = currentParent?.parent
|
||||
currentElement = nil
|
||||
}
|
||||
|
||||
@objc func parser(parser: NSXMLParser, parseErrorOccurred parseError: NSError) {
|
||||
self.parseError = parseError
|
||||
}
|
||||
|
||||
}
|
||||
Executable
+29
@@ -0,0 +1,29 @@
|
||||
//
|
||||
// AEXML.h
|
||||
//
|
||||
// Copyright (c) 2014 Marko Tadić <tadija@me.com> http://tadija.net
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to deal
|
||||
// in the Software without restriction, including without limitation the rights
|
||||
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
// copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in all
|
||||
// copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
// SOFTWARE.
|
||||
//
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
FOUNDATION_EXPORT double AEXMLVersionNumber;
|
||||
FOUNDATION_EXPORT const unsigned char AEXMLVersionString[];
|
||||
|
||||
Executable
+128
@@ -0,0 +1,128 @@
|
||||
//
|
||||
// Document.swift
|
||||
//
|
||||
// Copyright (c) 2014-2016 Marko Tadić <tadija@me.com> http://tadija.net
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to deal
|
||||
// in the Software without restriction, including without limitation the rights
|
||||
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
// copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in all
|
||||
// copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
// SOFTWARE.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
/**
|
||||
This class is inherited from `AEXMLElement` and has a few addons to represent **XML Document**.
|
||||
|
||||
XML Parsing is also done with this object.
|
||||
*/
|
||||
open class AEXMLDocument: AEXMLElement {
|
||||
|
||||
// MARK: - Properties
|
||||
|
||||
/// Root (the first child element) element of XML Document **(Empty element with error if not exists)**.
|
||||
open var root: AEXMLElement {
|
||||
guard let rootElement = children.first else {
|
||||
let errorElement = AEXMLElement(name: "Error")
|
||||
errorElement.error = AEXMLError.rootElementMissing
|
||||
return errorElement
|
||||
}
|
||||
return rootElement
|
||||
}
|
||||
|
||||
open let options: AEXMLOptions
|
||||
|
||||
// MARK: - Lifecycle
|
||||
|
||||
/**
|
||||
Designated initializer - Creates and returns new XML Document object.
|
||||
|
||||
- parameter root: Root XML element for XML Document (defaults to `nil`).
|
||||
- parameter options: Options for XML Document header and parser settings (defaults to `AEXMLOptions()`).
|
||||
|
||||
- returns: Initialized XML Document object.
|
||||
*/
|
||||
public init(root: AEXMLElement? = nil, options: AEXMLOptions = AEXMLOptions()) {
|
||||
self.options = options
|
||||
|
||||
let documentName = String(describing: AEXMLDocument.self)
|
||||
super.init(name: documentName)
|
||||
|
||||
// document has no parent element
|
||||
parent = nil
|
||||
|
||||
// add root element to document (if any)
|
||||
if let rootElement = root {
|
||||
_ = addChild(rootElement)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
Convenience initializer - used for parsing XML data (by calling `loadXMLData:` internally).
|
||||
|
||||
- parameter xmlData: XML data to parse.
|
||||
- parameter options: Options for XML Document header and parser settings (defaults to `AEXMLOptions()`).
|
||||
|
||||
- returns: Initialized XML Document object containing parsed data. Throws error if data could not be parsed.
|
||||
*/
|
||||
public convenience init(xml: Data, options: AEXMLOptions = AEXMLOptions()) throws {
|
||||
self.init(options: options)
|
||||
try loadXML(xml)
|
||||
}
|
||||
|
||||
/**
|
||||
Convenience initializer - used for parsing XML string (by calling `init(xmlData:options:)` internally).
|
||||
|
||||
- parameter xmlString: XML string to parse.
|
||||
- parameter encoding: String encoding for creating `Data` from `xmlString` (defaults to `String.Encoding.utf8`)
|
||||
- parameter options: Options for XML Document header and parser settings (defaults to `AEXMLOptions()`).
|
||||
|
||||
- returns: Initialized XML Document object containing parsed data. Throws error if data could not be parsed.
|
||||
*/
|
||||
public convenience init(xml: String,
|
||||
encoding: String.Encoding = String.Encoding.utf8,
|
||||
options: AEXMLOptions = AEXMLOptions()) throws
|
||||
{
|
||||
guard let data = xml.data(using: encoding) else { throw AEXMLError.parsingFailed }
|
||||
try self.init(xml: data, options: options)
|
||||
}
|
||||
|
||||
// MARK: - Parse XML
|
||||
|
||||
/**
|
||||
Creates instance of `AEXMLParser` (private class which is simple wrapper around `XMLParser`)
|
||||
and starts parsing the given XML data. Throws error if data could not be parsed.
|
||||
|
||||
- parameter data: XML which should be parsed.
|
||||
*/
|
||||
open func loadXML(_ data: Data) throws {
|
||||
children.removeAll(keepingCapacity: false)
|
||||
let xmlParser = AEXMLParser(document: self, data: data)
|
||||
try xmlParser.parse()
|
||||
}
|
||||
|
||||
// MARK: - Override
|
||||
|
||||
/// Override of `xml` property of `AEXMLElement` - it just inserts XML Document header at the beginning.
|
||||
open override var xml: String {
|
||||
var xml = "\(options.documentHeader.xmlString)\n"
|
||||
for child in children {
|
||||
xml += child.xml
|
||||
}
|
||||
return xml
|
||||
}
|
||||
|
||||
}
|
||||
Executable
+285
@@ -0,0 +1,285 @@
|
||||
//
|
||||
// Element.swift
|
||||
//
|
||||
// Copyright (c) 2014-2016 Marko Tadić <tadija@me.com> http://tadija.net
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to deal
|
||||
// in the Software without restriction, including without limitation the rights
|
||||
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
// copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in all
|
||||
// copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
// SOFTWARE.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
/**
|
||||
This is base class for holding XML structure.
|
||||
|
||||
You can access its structure by using subscript like this: `element["foo"]["bar"]` which would
|
||||
return `<bar></bar>` element from `<element><foo><bar></bar></foo></element>` XML as an `AEXMLElement` object.
|
||||
*/
|
||||
open class AEXMLElement {
|
||||
|
||||
// MARK: - Properties
|
||||
|
||||
/// Every `AEXMLElement` should have its parent element instead of `AEXMLDocument` which parent is `nil`.
|
||||
open internal(set) weak var parent: AEXMLElement?
|
||||
|
||||
/// Child XML elements.
|
||||
open internal(set) var children = [AEXMLElement]()
|
||||
|
||||
/// XML Element name.
|
||||
open var name: String
|
||||
|
||||
/// XML Element value.
|
||||
open var value: String?
|
||||
|
||||
/// XML Element attributes.
|
||||
open var attributes: [String : String]
|
||||
|
||||
/// Error value (`nil` if there is no error).
|
||||
open var error: AEXMLError?
|
||||
|
||||
/// String representation of `value` property (if `value` is `nil` this is empty String).
|
||||
open var string: String { return value ?? String() }
|
||||
|
||||
/// Boolean representation of `value` property (if `value` is "true" or 1 this is `True`, otherwise `False`).
|
||||
open var bool: Bool { return string.lowercased() == "true" || Int(string) == 1 ? true : false }
|
||||
|
||||
/// Integer representation of `value` property (this is **0** if `value` can't be represented as Integer).
|
||||
open var int: Int { return Int(string) ?? 0 }
|
||||
|
||||
/// Double representation of `value` property (this is **0.00** if `value` can't be represented as Double).
|
||||
open var double: Double { return Double(string) ?? 0.00 }
|
||||
|
||||
// MARK: - Lifecycle
|
||||
|
||||
/**
|
||||
Designated initializer - all parameters are optional.
|
||||
|
||||
- parameter name: XML element name.
|
||||
- parameter value: XML element value (defaults to `nil`).
|
||||
- parameter attributes: XML element attributes (defaults to empty dictionary).
|
||||
|
||||
- returns: An initialized `AEXMLElement` object.
|
||||
*/
|
||||
public init(name: String, value: String? = nil, attributes: [String : String] = [String : String]()) {
|
||||
self.name = name
|
||||
self.value = value
|
||||
self.attributes = attributes
|
||||
}
|
||||
|
||||
// MARK: - XML Read
|
||||
|
||||
/// The first element with given name **(Empty element with error if not exists)**.
|
||||
open subscript(key: String) -> AEXMLElement {
|
||||
guard let
|
||||
first = children.filter({ $0.name == key }).first
|
||||
else {
|
||||
let errorElement = AEXMLElement(name: key)
|
||||
errorElement.error = AEXMLError.elementNotFound
|
||||
return errorElement
|
||||
}
|
||||
return first
|
||||
}
|
||||
|
||||
/// Returns all of the elements with equal name as `self` **(nil if not exists)**.
|
||||
open var all: [AEXMLElement]? { return parent?.children.filter { $0.name == self.name } }
|
||||
|
||||
/// Returns the first element with equal name as `self` **(nil if not exists)**.
|
||||
open var first: AEXMLElement? { return all?.first }
|
||||
|
||||
/// Returns the last element with equal name as `self` **(nil if not exists)**.
|
||||
open var last: AEXMLElement? { return all?.last }
|
||||
|
||||
/// Returns number of all elements with equal name as `self`.
|
||||
open var count: Int { return all?.count ?? 0 }
|
||||
|
||||
fileprivate func filter(withCondition condition: (AEXMLElement) -> Bool) -> [AEXMLElement]? {
|
||||
guard let elements = all else { return nil }
|
||||
|
||||
var found = [AEXMLElement]()
|
||||
for element in elements {
|
||||
if condition(element) {
|
||||
found.append(element)
|
||||
}
|
||||
}
|
||||
|
||||
return found.count > 0 ? found : nil
|
||||
}
|
||||
|
||||
/**
|
||||
Returns all elements with given value.
|
||||
|
||||
- parameter value: XML element value.
|
||||
|
||||
- returns: Optional Array of found XML elements.
|
||||
*/
|
||||
open func all(withValue value: String) -> [AEXMLElement]? {
|
||||
let found = filter { (element) -> Bool in
|
||||
return element.value == value
|
||||
}
|
||||
return found
|
||||
}
|
||||
|
||||
/**
|
||||
Returns all elements with given attributes.
|
||||
|
||||
- parameter attributes: Dictionary of Keys and Values of attributes.
|
||||
|
||||
- returns: Optional Array of found XML elements.
|
||||
*/
|
||||
open func all(withAttributes attributes: [String : String]) -> [AEXMLElement]? {
|
||||
let found = filter { (element) -> Bool in
|
||||
var countAttributes = 0
|
||||
for (key, value) in attributes {
|
||||
if element.attributes[key] == value {
|
||||
countAttributes += 1
|
||||
}
|
||||
}
|
||||
return countAttributes == attributes.count
|
||||
}
|
||||
return found
|
||||
}
|
||||
|
||||
// MARK: - XML Write
|
||||
|
||||
/**
|
||||
Adds child XML element to `self`.
|
||||
|
||||
- parameter child: Child XML element to add.
|
||||
|
||||
- returns: Child XML element with `self` as `parent`.
|
||||
*/
|
||||
@discardableResult open func addChild(_ child: AEXMLElement) -> AEXMLElement {
|
||||
child.parent = self
|
||||
children.append(child)
|
||||
return child
|
||||
}
|
||||
|
||||
/**
|
||||
Adds child XML element to `self`.
|
||||
|
||||
- parameter name: Child XML element name.
|
||||
- parameter value: Child XML element value (defaults to `nil`).
|
||||
- parameter attributes: Child XML element attributes (defaults to empty dictionary).
|
||||
|
||||
- returns: Child XML element with `self` as `parent`.
|
||||
*/
|
||||
@discardableResult open func addChild(name: String,
|
||||
value: String? = nil,
|
||||
attributes: [String : String] = [String : String]()) -> AEXMLElement
|
||||
{
|
||||
let child = AEXMLElement(name: name, value: value, attributes: attributes)
|
||||
return addChild(child)
|
||||
}
|
||||
|
||||
/// Removes `self` from `parent` XML element.
|
||||
open func removeFromParent() {
|
||||
parent?.removeChild(self)
|
||||
}
|
||||
|
||||
fileprivate func removeChild(_ child: AEXMLElement) {
|
||||
if let childIndex = children.index(where: { $0 === child }) {
|
||||
children.remove(at: childIndex)
|
||||
}
|
||||
}
|
||||
|
||||
fileprivate var parentsCount: Int {
|
||||
var count = 0
|
||||
var element = self
|
||||
|
||||
while let parent = element.parent {
|
||||
count += 1
|
||||
element = parent
|
||||
}
|
||||
|
||||
return count
|
||||
}
|
||||
|
||||
fileprivate func indent(withDepth depth: Int) -> String {
|
||||
var count = depth
|
||||
var indent = String()
|
||||
|
||||
while count > 0 {
|
||||
indent += "\t"
|
||||
count -= 1
|
||||
}
|
||||
|
||||
return indent
|
||||
}
|
||||
|
||||
/// Complete hierarchy of `self` and `children` in **XML** escaped and formatted String
|
||||
open var xml: String {
|
||||
var xml = String()
|
||||
|
||||
// open element
|
||||
xml += indent(withDepth: parentsCount - 1)
|
||||
xml += "<\(name)"
|
||||
|
||||
if attributes.count > 0 {
|
||||
// insert attributes
|
||||
for (key, value) in attributes {
|
||||
xml += " \(key)=\"\(value.xmlEscaped)\""
|
||||
}
|
||||
}
|
||||
|
||||
if value == nil && children.count == 0 {
|
||||
// close element
|
||||
xml += " />"
|
||||
} else {
|
||||
if children.count > 0 {
|
||||
// add children
|
||||
xml += ">\n"
|
||||
for child in children {
|
||||
xml += "\(child.xml)\n"
|
||||
}
|
||||
// add indentation
|
||||
xml += indent(withDepth: parentsCount - 1)
|
||||
xml += "</\(name)>"
|
||||
} else {
|
||||
// insert string value and close element
|
||||
xml += ">\(string.xmlEscaped)</\(name)>"
|
||||
}
|
||||
}
|
||||
|
||||
return xml
|
||||
}
|
||||
|
||||
/// Same as `xmlString` but without `\n` and `\t` characters
|
||||
open var xmlCompact: String {
|
||||
let chars = CharacterSet(charactersIn: "\n\t")
|
||||
return xml.components(separatedBy: chars).joined(separator: "")
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public extension String {
|
||||
|
||||
/// String representation of self with XML special characters escaped.
|
||||
public var xmlEscaped: String {
|
||||
// we need to make sure "&" is escaped first. Not doing this may break escaping the other characters
|
||||
var escaped = replacingOccurrences(of: "&", with: "&", options: .literal)
|
||||
|
||||
// replace the other four special characters
|
||||
let escapeChars = ["<" : "<", ">" : ">", "'" : "'", "\"" : """]
|
||||
for (char, echar) in escapeChars {
|
||||
escaped = escaped.replacingOccurrences(of: char, with: echar, options: .literal)
|
||||
}
|
||||
|
||||
return escaped
|
||||
}
|
||||
|
||||
}
|
||||
Executable
+37
@@ -0,0 +1,37 @@
|
||||
//
|
||||
// Error.swift
|
||||
//
|
||||
// Copyright (c) 2014-2016 Marko Tadić <tadija@me.com> http://tadija.net
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to deal
|
||||
// in the Software without restriction, including without limitation the rights
|
||||
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
// copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in all
|
||||
// copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
// SOFTWARE.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
/// A type representing error value that can be thrown or inside `error` property of `AEXMLElement`.
|
||||
public enum AEXMLError: Error {
|
||||
/// This will be inside `error` property of `AEXMLElement` when subscript is used for not-existing element.
|
||||
case elementNotFound
|
||||
|
||||
/// This will be inside `error` property of `AEXMLDocument` when there is no root element.
|
||||
case rootElementMissing
|
||||
|
||||
/// `AEXMLDocument` can throw this error on `init` or `loadXMLData` if parsing with `XMLParser` was not successful.
|
||||
case parsingFailed
|
||||
}
|
||||
Executable
+26
@@ -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>3.0.0</string>
|
||||
<key>CFBundleSignature</key>
|
||||
<string>????</string>
|
||||
<key>CFBundleVersion</key>
|
||||
<string>$(CURRENT_PROJECT_VERSION)</string>
|
||||
<key>NSPrincipalClass</key>
|
||||
<string></string>
|
||||
</dict>
|
||||
</plist>
|
||||
Executable
+52
@@ -0,0 +1,52 @@
|
||||
//
|
||||
// Options.swift
|
||||
// AEXML
|
||||
//
|
||||
// Created by Marko Tadic on 9/10/16.
|
||||
// Copyright © 2016 AE. All rights reserved.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
/// Options used in `AEXMLDocument`
|
||||
public struct AEXMLOptions {
|
||||
|
||||
/// Values used in XML Document header
|
||||
public struct DocumentHeader {
|
||||
/// Version value for XML Document header (defaults to 1.0).
|
||||
public var version = 1.0
|
||||
|
||||
/// Encoding value for XML Document header (defaults to "utf-8").
|
||||
public var encoding = "utf-8"
|
||||
|
||||
/// Standalone value for XML Document header (defaults to "no").
|
||||
public var standalone = "no"
|
||||
|
||||
/// XML Document header
|
||||
public var xmlString: String {
|
||||
return "<?xml version=\"\(version)\" encoding=\"\(encoding)\" standalone=\"\(standalone)\"?>"
|
||||
}
|
||||
}
|
||||
|
||||
/// Settings used by `Foundation.XMLParser`
|
||||
public struct ParserSettings {
|
||||
/// Parser reports the namespaces and qualified names of elements. (defaults to `false`)
|
||||
public var shouldProcessNamespaces = false
|
||||
|
||||
/// Parser reports the prefixes indicating the scope of namespace declarations. (defaults to `false`)
|
||||
public var shouldReportNamespacePrefixes = false
|
||||
|
||||
/// Parser reports declarations of external entities. (defaults to `false`)
|
||||
public var shouldResolveExternalEntities = false
|
||||
}
|
||||
|
||||
/// Values used in XML Document header (defaults to `DocumentHeader()`)
|
||||
public var documentHeader = DocumentHeader()
|
||||
|
||||
/// Settings used by `Foundation.XMLParser` (defaults to `ParserSettings()`)
|
||||
public var parserSettings = ParserSettings()
|
||||
|
||||
/// Designated initializer - Creates and returns default `AEXMLOptions`.
|
||||
public init() {}
|
||||
|
||||
}
|
||||
Executable
+101
@@ -0,0 +1,101 @@
|
||||
//
|
||||
// Parser.swift
|
||||
//
|
||||
// Copyright (c) 2014-2016 Marko Tadić <tadija@me.com> http://tadija.net
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to deal
|
||||
// in the Software without restriction, including without limitation the rights
|
||||
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
// copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in all
|
||||
// copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
// SOFTWARE.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
/// Simple wrapper around `Foundation.XMLParser`.
|
||||
internal class AEXMLParser: NSObject, XMLParserDelegate {
|
||||
|
||||
// MARK: - Properties
|
||||
|
||||
let document: AEXMLDocument
|
||||
let data: Data
|
||||
|
||||
var currentParent: AEXMLElement?
|
||||
var currentElement: AEXMLElement?
|
||||
var currentValue = String()
|
||||
|
||||
var parseError: Error?
|
||||
|
||||
// MARK: - Lifecycle
|
||||
|
||||
init(document: AEXMLDocument, data: Data) {
|
||||
self.document = document
|
||||
self.data = data
|
||||
currentParent = document
|
||||
|
||||
super.init()
|
||||
}
|
||||
|
||||
// MARK: - API
|
||||
|
||||
func parse() throws {
|
||||
let parser = XMLParser(data: data)
|
||||
parser.delegate = self
|
||||
|
||||
parser.shouldProcessNamespaces = document.options.parserSettings.shouldProcessNamespaces
|
||||
parser.shouldReportNamespacePrefixes = document.options.parserSettings.shouldReportNamespacePrefixes
|
||||
parser.shouldResolveExternalEntities = document.options.parserSettings.shouldResolveExternalEntities
|
||||
|
||||
let success = parser.parse()
|
||||
|
||||
if !success {
|
||||
guard let error = parseError else { throw AEXMLError.parsingFailed }
|
||||
throw error
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - XMLParserDelegate
|
||||
|
||||
@objc func parser(_ parser: XMLParser,
|
||||
didStartElement elementName: String,
|
||||
namespaceURI: String?,
|
||||
qualifiedName qName: String?,
|
||||
attributes attributeDict: [String : String])
|
||||
{
|
||||
currentValue = String()
|
||||
currentElement = currentParent?.addChild(name: elementName, attributes: attributeDict)
|
||||
currentParent = currentElement
|
||||
}
|
||||
|
||||
@objc func parser(_ parser: XMLParser, foundCharacters string: String) {
|
||||
currentValue += string
|
||||
let newValue = currentValue.trimmingCharacters(in: CharacterSet.whitespacesAndNewlines)
|
||||
currentElement?.value = newValue == String() ? nil : newValue
|
||||
}
|
||||
|
||||
@objc func parser(_ parser: XMLParser,
|
||||
didEndElement elementName: String,
|
||||
namespaceURI: String?,
|
||||
qualifiedName qName: String?)
|
||||
{
|
||||
currentParent = currentParent?.parent
|
||||
currentElement = nil
|
||||
}
|
||||
|
||||
@objc func parser(_ parser: XMLParser, parseErrorOccurred parseError: Error) {
|
||||
self.parseError = parseError
|
||||
}
|
||||
|
||||
}
|
||||
+129
-126
@@ -1,3 +1,4 @@
|
||||
|
||||
//
|
||||
// DropboxFileProvider.swift
|
||||
// FileProvider
|
||||
@@ -12,34 +13,34 @@ import CoreGraphics
|
||||
// Because this class uses NSURLSession, it's necessary to disable App Transport Security
|
||||
// in case of using this class with unencrypted HTTP connection.
|
||||
|
||||
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 {
|
||||
open class DropboxFileProvider: NSObject, FileProviderBasic {
|
||||
open static let type: String = "WebDAV"
|
||||
open let isPathRelative: Bool = true
|
||||
open let baseURL: URL?
|
||||
open var currentPath: String = ""
|
||||
open var dispatch_queue: DispatchQueue {
|
||||
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?
|
||||
open weak var delegate: FileProviderDelegate?
|
||||
open let credential: URLCredential?
|
||||
|
||||
private var _session: NSURLSession?
|
||||
private var sessionDelegate: SessionDelegate?
|
||||
internal var session: NSURLSession {
|
||||
fileprivate var _session: URLSession?
|
||||
fileprivate var sessionDelegate: SessionDelegate?
|
||||
internal var session: URLSession {
|
||||
if _session == nil {
|
||||
self.sessionDelegate = SessionDelegate(fileProvider: self, credential: credential)
|
||||
let queue = NSOperationQueue()
|
||||
let queue = OperationQueue()
|
||||
//queue.underlyingQueue = dispatch_queue
|
||||
_session = NSURLSession(configuration: NSURLSessionConfiguration.defaultSessionConfiguration(), delegate: sessionDelegate, delegateQueue: queue)
|
||||
_session = URLSession(configuration: URLSessionConfiguration.default, delegate: sessionDelegate as URLSessionDelegate?, delegateQueue: queue)
|
||||
}
|
||||
return _session!
|
||||
}
|
||||
|
||||
public init? (credential: NSURLCredential?) {
|
||||
public init? (credential: URLCredential?) {
|
||||
self.baseURL = nil
|
||||
dispatch_queue = dispatch_queue_create("FileProvider.\(type)", DISPATCH_QUEUE_CONCURRENT)
|
||||
dispatch_queue = DispatchQueue(label: "FileProvider.\(DropboxFileProvider.type)", attributes: DispatchQueue.Attributes.concurrent)
|
||||
//let url = baseURL.uw_absoluteString
|
||||
self.credential = credential
|
||||
}
|
||||
@@ -48,176 +49,178 @@ public class DropboxFileProvider: NSObject, FileProviderBasic {
|
||||
_session?.invalidateAndCancel()
|
||||
}
|
||||
|
||||
public func contentsOfDirectoryAtPath(path: String, completionHandler: ((contents: [FileObject], error: ErrorType?) -> Void)) {
|
||||
open func contentsOfDirectory(path: String, completionHandler: @escaping ((_ contents: [FileObject], _ error: Error?) -> Void)) {
|
||||
list(path) { (contents, cursor, error) in
|
||||
completionHandler(contents: contents, error: error)
|
||||
completionHandler(contents, error)
|
||||
}
|
||||
}
|
||||
|
||||
public func attributesOfItemAtPath(path: String, completionHandler: ((attributes: FileObject?, error: ErrorType?) -> Void)) {
|
||||
let url = NSURL(string: "https://api.dropboxapi.com/2/files/get_metadata")!
|
||||
let request = NSMutableURLRequest(URL: url)
|
||||
request.HTTPMethod = "POST"
|
||||
open func attributesOfItem(path: String, completionHandler: @escaping ((_ attributes: FileObject?, _ error: Error?) -> Void)) {
|
||||
let url = URL(string: "https://api.dropboxapi.com/2/files/get_metadata")!
|
||||
var request = URLRequest(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 {
|
||||
let requestDictionary = ["path": correctPath(path)! as NSString]
|
||||
request.httpBody = dictionaryToJSON(requestDictionary)?.data(using: String.Encoding.utf8)
|
||||
let task = session.dataTask(with: request, completionHandler: { (data, response, error) in
|
||||
if let response = response as? HTTPURLResponse {
|
||||
defer {
|
||||
self.delegateNotify(FileOperation.Create(path: path), error: error)
|
||||
self.delegateNotify(FileOperation.create(path: path), error: error)
|
||||
}
|
||||
let code = FileProviderHTTPErrorCode(rawValue: response.statusCode)
|
||||
let dbError: FileProviderDropboxError? = code != nil ? FileProviderDropboxError(code: code!, path: path, errorDescription: String(data: data ?? NSData(), encoding: NSUTF8StringEncoding)) : nil
|
||||
if let data = data, let jsonStr = String(data: data, encoding: NSUTF8StringEncoding), let json = jsonToDictionary(jsonStr), let file = self.mapToFileObject(json) {
|
||||
completionHandler(attributes: file, error: dbError)
|
||||
let dbError: FileProviderDropboxError? = code != nil ? FileProviderDropboxError(code: code!, path: path, errorDescription: String(data: data ?? Data(), encoding: String.Encoding.utf8)) : nil
|
||||
if let data = data, let jsonStr = String(data: data, encoding: String.Encoding.utf8), let json = jsonToDictionary(jsonStr), let file = self.mapToFileObject(json) {
|
||||
completionHandler(file, dbError)
|
||||
return
|
||||
}
|
||||
completionHandler(attributes: nil, error: dbError)
|
||||
completionHandler(nil, dbError)
|
||||
return
|
||||
}
|
||||
completionHandler(attributes: nil, error: error)
|
||||
}
|
||||
completionHandler(nil, error)
|
||||
})
|
||||
task.resume()
|
||||
}
|
||||
|
||||
public func storageProperties(completionHandler: ((total: Int64, used: Int64) -> Void)) {
|
||||
let url = NSURL(string: "https://api.dropboxapi.com/2/users/get_space_usage")!
|
||||
let request = NSMutableURLRequest(URL: url)
|
||||
request.HTTPMethod = "POST"
|
||||
open func storageProperties(completionHandler: @escaping ((_ total: Int64, _ used: Int64) -> Void)) {
|
||||
let url = URL(string: "https://api.dropboxapi.com/2/users/get_space_usage")!
|
||||
var request = URLRequest(url: url)
|
||||
request.httpMethod = "POST"
|
||||
request.setValue("Bearer \(credential?.password ?? "")", forHTTPHeaderField: "Authorization")
|
||||
let task = session.dataTaskWithRequest(request) { (data, response, error) in
|
||||
if let data = data, let jsonStr = String(data: data, encoding: NSUTF8StringEncoding), let json = jsonToDictionary(jsonStr) {
|
||||
let totalSize = ((json["allocation"] as? NSDictionary)?["allocated"] as? NSNumber)?.longLongValue ?? -1
|
||||
let usedSize = (json["used"] as? NSNumber)?.longLongValue ?? 0
|
||||
completionHandler(total: totalSize, used: usedSize)
|
||||
let task = session.dataTask(with: request, completionHandler: { (data, response, error) in
|
||||
if let data = data, let jsonStr = String(data: data, encoding: String.Encoding.utf8), let json = jsonToDictionary(jsonStr) {
|
||||
let totalSize = ((json["allocation"] as? NSDictionary)?["allocated"] as? NSNumber)?.int64Value ?? -1
|
||||
let usedSize = (json["used"] as? NSNumber)?.int64Value ?? 0
|
||||
completionHandler(totalSize, usedSize)
|
||||
return
|
||||
}
|
||||
completionHandler(total: -1, used: 0)
|
||||
}
|
||||
completionHandler(-1, 0)
|
||||
})
|
||||
task.resume()
|
||||
}
|
||||
|
||||
public weak var fileOperationDelegate: FileOperationDelegate?
|
||||
open 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 create(folder folderName: String, at atPath: String, completionHandler: SimpleCompletionHandler) {
|
||||
let path = (atPath as NSString).appendingPathComponent(folderName) + "/"
|
||||
doOperation(.create(path: path), completionHandler: completionHandler)
|
||||
}
|
||||
|
||||
public func createFile(fileAttribs: FileObject, atPath path: String, contents data: NSData?, completionHandler: SimpleCompletionHandler) {
|
||||
self.writeContentsAtPath(path, contents: data ?? NSData(), completionHandler: completionHandler)
|
||||
public func create(file fileAttribs: FileObject, at path: String, contents data: Data?, completionHandler: SimpleCompletionHandler) {
|
||||
self.writeContents(path: path, contents: data ?? Data(), completionHandler: completionHandler)
|
||||
}
|
||||
|
||||
public func moveItemAtPath(path: String, toPath: String, overwrite: Bool = false, completionHandler: SimpleCompletionHandler) {
|
||||
doOperation(.Move(source: path, destination: toPath), completionHandler: completionHandler)
|
||||
public func moveItem(path: String, to toPath: String, overwrite: Bool = false, completionHandler: SimpleCompletionHandler) {
|
||||
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 copyItem(path: String, to 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)
|
||||
public func removeItem(path: String, completionHandler: SimpleCompletionHandler) {
|
||||
doOperation(.remove(path: path), completionHandler: completionHandler)
|
||||
}
|
||||
|
||||
private func doOperation(operation: FileOperation, completionHandler: SimpleCompletionHandler) {
|
||||
fileprivate func doOperation(_ operation: FileOperation, completionHandler: SimpleCompletionHandler) {
|
||||
let url: String
|
||||
var path: String?, fromPath: String?, toPath: String?
|
||||
switch operation {
|
||||
case .Create(path: let p):
|
||||
case .create(path: let p):
|
||||
url = "https://api.dropboxapi.com/2/files/create_folder"
|
||||
path = p
|
||||
case .Copy(source: let fp, destination: let tp):
|
||||
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):
|
||||
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):
|
||||
case .modify(path: let p):
|
||||
return
|
||||
case .Remove(path: let p):
|
||||
case .remove(path: let p):
|
||||
url = "https://api.dropboxapi.com/2/files/delete"
|
||||
path = p
|
||||
case .Link(link: _, target: _):
|
||||
case .link(link: _, target: _):
|
||||
return
|
||||
}
|
||||
let request = NSMutableURLRequest(URL: NSURL(string: url)!)
|
||||
request.HTTPMethod = "POST"
|
||||
var request = URLRequest(url: URL(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 {
|
||||
requestDictionary["path"] = correctPath(path) as NSString?
|
||||
requestDictionary["from_path"] = correctPath(fromPath) as NSString?
|
||||
requestDictionary["to_path"] = correctPath(toPath) as NSString?
|
||||
request.httpBody = dictionaryToJSON(requestDictionary)?.data(using: String.Encoding.utf8)
|
||||
let task = session.dataTask(with: request, completionHandler: { (data, response, error) in
|
||||
if let response = response as? HTTPURLResponse {
|
||||
let code = FileProviderHTTPErrorCode(rawValue: response.statusCode)
|
||||
let dbError: FileProviderDropboxError? = code != nil ? FileProviderDropboxError(code: code!, path: path ?? fromPath ?? "", errorDescription: String(data: data ?? NSData(), encoding: NSUTF8StringEncoding)) : nil
|
||||
let dbError: FileProviderDropboxError? = code != nil ? FileProviderDropboxError(code: code!, path: path ?? fromPath ?? "", errorDescription: String(data: data ?? Data(), encoding: String.Encoding.utf8)) : nil
|
||||
defer {
|
||||
self.delegateNotify(operation, error: error ?? dbError)
|
||||
}
|
||||
/*if let data = data, let jsonStr = String(data: data, encoding: NSUTF8StringEncoding) {
|
||||
let json = self.jsonToDictionary(jsonStr)
|
||||
}*/
|
||||
completionHandler?(error: dbError)
|
||||
completionHandler?(dbError)
|
||||
return
|
||||
}
|
||||
completionHandler?(error: error)
|
||||
}
|
||||
completionHandler?(error)
|
||||
})
|
||||
task.resume()
|
||||
}
|
||||
|
||||
public func copyLocalFileToPath(localFile: NSURL, toPath: String, completionHandler: SimpleCompletionHandler) {
|
||||
guard let data = NSData(contentsOfURL: localFile) else {
|
||||
let error = throwError(localFile.uw_absoluteString, code: NSURLError.FileDoesNotExist)
|
||||
completionHandler?(error: error)
|
||||
public func copyItem(localFile: URL, to toPath: String, completionHandler: SimpleCompletionHandler) {
|
||||
guard let data = try? Data(contentsOf: localFile) else {
|
||||
let error = throwError(localFile.uw_absoluteString, code: URLError.fileDoesNotExist as FoundationErrorEnum)
|
||||
completionHandler?(error)
|
||||
return
|
||||
}
|
||||
upload_simple(toPath, data: data, overwrite: true, operation: .Copy(source: localFile.uw_absoluteString, destination: toPath), completionHandler: completionHandler)
|
||||
upload_simple(toPath, data: data, overwrite: true, operation: .copy(source: localFile.uw_absoluteString, destination: toPath), completionHandler: completionHandler)
|
||||
}
|
||||
|
||||
public func copyPathToLocalFile(path: String, toLocalURL destURL: NSURL, completionHandler: SimpleCompletionHandler) {
|
||||
let url = NSURL(string: "https://content.dropboxapi.com/2/files/download")!
|
||||
let request = NSMutableURLRequest(URL: url)
|
||||
request.HTTPMethod = "GET"
|
||||
public func copyItem(path: String, toLocalURL destURL: URL, completionHandler: SimpleCompletionHandler) {
|
||||
let url = URL(string: "https://content.dropboxapi.com/2/files/download")!
|
||||
var request = URLRequest(url: url)
|
||||
request.httpMethod = "GET"
|
||||
request.setValue("Bearer \(credential?.password ?? "")", forHTTPHeaderField: "Authorization")
|
||||
request.setValue("application/json", forHTTPHeaderField: "Content-Type")
|
||||
let requestDictionary = ["path": path]
|
||||
let requestDictionary = ["path": path as NSString]
|
||||
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 = FileProviderHTTPErrorCode(rawValue: (response as? NSHTTPURLResponse)?.statusCode ?? -1)
|
||||
let task = session.downloadTask(with: request, completionHandler: { (cacheURL, response, error) in
|
||||
guard let cacheURL = cacheURL, let httpResponse = response as? HTTPURLResponse , httpResponse.statusCode < 300 else {
|
||||
let code = FileProviderHTTPErrorCode(rawValue: (response as? HTTPURLResponse)?.statusCode ?? -1)
|
||||
let dbError: FileProviderDropboxError? = code != nil ? FileProviderDropboxError(code: code!, path: path, errorDescription: nil) : nil
|
||||
completionHandler?(error: dbError ?? error)
|
||||
completionHandler?(dbError ?? error)
|
||||
return
|
||||
}
|
||||
do {
|
||||
try NSFileManager.defaultManager().moveItemAtURL(cacheURL, toURL: destURL)
|
||||
completionHandler?(error: nil)
|
||||
try FileManager.default.moveItem(at: cacheURL, to: destURL)
|
||||
completionHandler?(nil)
|
||||
} catch let e {
|
||||
completionHandler?(error: e)
|
||||
completionHandler?(e)
|
||||
}
|
||||
})
|
||||
task.taskDescription = dictionaryToJSON(["type": "Copy", "source": path, "dest": destURL.uw_absoluteString])
|
||||
task.taskDescription = dictionaryToJSON(["type": "Copy" as NSString, "source": path as NSString, "dest": destURL.uw_absoluteString as NSString])
|
||||
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 contents(path: String, completionHandler: @escaping ((_ contents: Data?, _ error: Error?) -> Void)) {
|
||||
self.contents(path: 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://content.dropboxapi.com/2/files/download")!
|
||||
let request = NSMutableURLRequest(URL: url)
|
||||
request.HTTPMethod = "GET"
|
||||
public func contents(path: String, offset: Int64, length: Int, completionHandler: @escaping ((_ contents: Data?, _ error: Error?) -> Void)) {
|
||||
let url = URL(string: "https://content.dropboxapi.com/2/files/download")!
|
||||
var request = URLRequest(url: url)
|
||||
request.httpMethod = "GET"
|
||||
request.setValue("Bearer \(credential?.password ?? "")", forHTTPHeaderField: "Authorization")
|
||||
if length > 0 {
|
||||
request.setValue("bytes=\(offset)-\(offset + length)", forHTTPHeaderField: "Range")
|
||||
@@ -225,35 +228,35 @@ extension DropboxFileProvider: FileProviderReadWrite {
|
||||
request.setValue("bytes=\(offset)-", forHTTPHeaderField: "Range")
|
||||
}
|
||||
let requestDictionary = ["path": path]
|
||||
request.setValue(dictionaryToJSON(requestDictionary), forHTTPHeaderField: "Dropbox-API-Arg")
|
||||
let task = session.dataTaskWithRequest(request, completionHandler: { (datam, response, error) in
|
||||
guard let data = datam, let httpResponse = response as? NSHTTPURLResponse where httpResponse.statusCode < 300 else {
|
||||
let code = FileProviderHTTPErrorCode(rawValue: (response as? NSHTTPURLResponse)?.statusCode ?? -1)
|
||||
let dbError: FileProviderDropboxError? = code != nil ? FileProviderDropboxError(code: code!, path: path, errorDescription: String(data: datam ?? NSData(), encoding: NSUTF8StringEncoding)) : nil
|
||||
completionHandler(contents: nil, error: dbError ?? error)
|
||||
request.setValue(dictionaryToJSON(requestDictionary as [String : AnyObject]), forHTTPHeaderField: "Dropbox-API-Arg")
|
||||
let task = session.dataTask(with: request, completionHandler: { (datam, response, error) in
|
||||
guard let data = datam, let httpResponse = response as? HTTPURLResponse , httpResponse.statusCode < 300 else {
|
||||
let code = FileProviderHTTPErrorCode(rawValue: (response as? HTTPURLResponse)?.statusCode ?? -1)
|
||||
let dbError: FileProviderDropboxError? = code != nil ? FileProviderDropboxError(code: code!, path: path, errorDescription: String(data: datam ?? Data(), encoding: String.Encoding.utf8)) : nil
|
||||
completionHandler(nil, dbError ?? error)
|
||||
return
|
||||
}
|
||||
completionHandler(contents: data, error: error)
|
||||
completionHandler(data, error)
|
||||
})
|
||||
task.resume()
|
||||
}
|
||||
|
||||
public func writeContentsAtPath(path: String, contents data: NSData, atomically: Bool = false, completionHandler: SimpleCompletionHandler) {
|
||||
public func writeContents(path: String, contents data: Data, atomically: Bool = false, completionHandler: SimpleCompletionHandler) {
|
||||
// FIXME: remove 150MB restriction
|
||||
upload_simple(path, data: data, overwrite: true, operation: .Modify(path: path), completionHandler: completionHandler)
|
||||
upload_simple(path, data: data, overwrite: true, operation: .modify(path: path), completionHandler: completionHandler)
|
||||
}
|
||||
|
||||
public func searchFilesAtPath(path: String, recursive: Bool, query: String, foundItemHandler: ((FileObject) -> Void)?, completionHandler: ((files: [FileObject], error: ErrorType?) -> Void)) {
|
||||
public func searchFiles(path: String, recursive: Bool, query: String, foundItemHandler: ((FileObject) -> Void)?, completionHandler: @escaping ((_ files: [FileObject], _ error: Error?) -> Void)) {
|
||||
var foundFiles = [DropboxFileObject]()
|
||||
search(path, query: query, foundItem: { (file) in
|
||||
foundFiles.append(file)
|
||||
foundItemHandler?(file)
|
||||
}, completionHandler: { (error) in
|
||||
completionHandler(files: foundFiles, error: error)
|
||||
completionHandler(foundFiles, error)
|
||||
})
|
||||
}
|
||||
|
||||
private func registerNotifcation(path: String, eventHandler: (() -> Void)) {
|
||||
fileprivate func registerNotifcation(path: String, eventHandler: (() -> Void)) {
|
||||
/* There is two ways to monitor folders changing 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.
|
||||
@@ -262,7 +265,7 @@ extension DropboxFileProvider: FileProviderReadWrite {
|
||||
*/
|
||||
NotImplemented()
|
||||
}
|
||||
private func unregisterNotifcation(path: String) {
|
||||
fileprivate func unregisterNotifcation(path: String) {
|
||||
NotImplemented()
|
||||
}
|
||||
|
||||
@@ -271,7 +274,7 @@ extension DropboxFileProvider: FileProviderReadWrite {
|
||||
|
||||
extension DropboxFileProvider: ExtendedFileProvider {
|
||||
public func thumbnailOfFileSupported(path: String) -> Bool {
|
||||
switch (path as NSString).pathExtension.lowercaseString {
|
||||
switch (path as NSString).pathExtension.lowercased() {
|
||||
case "jpg", "jpeg", "gif", "bmp", "png", "tif", "tiff":
|
||||
return true
|
||||
/*case "doc", "docx", "docm", "xls", "xlsx", "xlsm":
|
||||
@@ -289,11 +292,11 @@ extension DropboxFileProvider: ExtendedFileProvider {
|
||||
return false
|
||||
}
|
||||
|
||||
public func thumbnailOfFileAtPath(path: String, dimension: CGSize, completionHandler: ((image: ImageClass?, error: ErrorType?) -> Void)) {
|
||||
let url: NSURL
|
||||
switch (path as NSString).pathExtension.lowercaseString {
|
||||
public func thumbnailOfFile(path: String, dimension: CGSize, completionHandler: @escaping ((_ image: ImageClass?, _ error: Error?) -> Void)) {
|
||||
let url: URL
|
||||
switch (path as NSString).pathExtension.lowercased() {
|
||||
case "jpg", "jpeg", "gif", "bmp", "png", "tif", "tiff":
|
||||
url = NSURL(string: "https://content.dropboxapi.com/2/files/get_thumbnail")!
|
||||
url = URL(string: "https://content.dropboxapi.com/2/files/get_thumbnail")!
|
||||
/*case "doc", "docx", "docm", "xls", "xlsx", "xlsm":
|
||||
fallthrough
|
||||
case "ppt", "pps", "ppsx", "ppsm", "pptx", "pptm":
|
||||
@@ -303,27 +306,27 @@ extension DropboxFileProvider: ExtendedFileProvider {
|
||||
default:
|
||||
return
|
||||
}
|
||||
let request = NSMutableURLRequest(URL: url)
|
||||
var request = URLRequest(url: url)
|
||||
request.setValue("Bearer \(credential?.password ?? "")", forHTTPHeaderField: "Authorization")
|
||||
var requestDictionary = ["path": path]
|
||||
requestDictionary["format"] = "jpeg"
|
||||
requestDictionary["size"] = "w\(Int(dimension.width))h\(Int(dimension.height))"
|
||||
var requestDictionary = ["path": path as NSString]
|
||||
requestDictionary["format"] = "jpeg" as NSString
|
||||
requestDictionary["size"] = "w\(Int(dimension.width))h\(Int(dimension.height))" as NSString
|
||||
request.setValue(dictionaryToJSON(requestDictionary), forHTTPHeaderField: "Dropbox-API-Arg")
|
||||
self.session.dataTaskWithRequest(request) { (data, response, error) in
|
||||
self.session.dataTask(with: request, completionHandler: { (data, response, error) in
|
||||
var image: ImageClass? = nil
|
||||
if let r = response as? NSHTTPURLResponse, let result = r.allHeaderFields["Dropbox-API-Result"] as? String, let jsonResult = jsonToDictionary(result) {
|
||||
if let r = response as? HTTPURLResponse, let result = r.allHeaderFields["Dropbox-API-Result"] as? String, let jsonResult = jsonToDictionary(result) {
|
||||
if jsonResult["error"] != nil {
|
||||
completionHandler(image: nil, error: self.throwError(path, code: NSURLError.CannotDecodeRawData))
|
||||
completionHandler(nil, self.throwError(path, code: URLError.cannotDecodeRawData as FoundationErrorEnum))
|
||||
}
|
||||
}
|
||||
if let data = data {
|
||||
image = ImageClass(data: data)
|
||||
}
|
||||
completionHandler(image: image, error: error)
|
||||
}
|
||||
completionHandler(image, error)
|
||||
})
|
||||
}
|
||||
|
||||
public func propertiesOfFileAtPath(path: String, completionHandler: ((propertiesDictionary: [String : AnyObject], keys: [String], error: ErrorType?) -> Void)) {
|
||||
public func propertiesOfFile(path: String, completionHandler: @escaping ((_ propertiesDictionary: [String : Any], _ keys: [String], _ error: Error?) -> Void)) {
|
||||
NotImplemented()
|
||||
}
|
||||
}
|
||||
|
||||
+77
-77
@@ -8,7 +8,7 @@
|
||||
|
||||
import Foundation
|
||||
|
||||
public struct FileProviderDropboxError: ErrorType, CustomStringConvertible {
|
||||
public struct FileProviderDropboxError: Error, CustomStringConvertible {
|
||||
public let code: FileProviderHTTPErrorCode
|
||||
public let path: String
|
||||
public let errorDescription: String?
|
||||
@@ -19,46 +19,46 @@ public struct FileProviderDropboxError: ErrorType, CustomStringConvertible {
|
||||
}
|
||||
|
||||
public final class DropboxFileObject: FileObject {
|
||||
public let serverTime: NSDate?
|
||||
public let serverTime: Date?
|
||||
public let id: String?
|
||||
public let rev: String?
|
||||
|
||||
// codebeat:disable[ARITY]
|
||||
public init(name: String, path: String, size: Int64 = -1, serverTime: NSDate? = nil, modifiedDate: NSDate? = nil, fileType: FileType = .Regular, isHidden: Bool = false, isReadOnly: Bool = false, id: String? = nil, rev: String? = nil) {
|
||||
public init(name: String, path: String, size: Int64 = -1, serverTime: Date? = nil, modifiedDate: Date? = nil, fileType: FileType = .regular, isHidden: Bool = false, isReadOnly: Bool = false, id: String? = nil, rev: String? = nil) {
|
||||
self.serverTime = serverTime
|
||||
self.id = id
|
||||
self.rev = rev
|
||||
super.init(absoluteURL: NSURL(string: path), name: name, path: path, size: size, createdDate: nil, modifiedDate: modifiedDate, fileType: fileType, isHidden: isHidden, isReadOnly: isReadOnly)
|
||||
super.init(absoluteURL: URL(string: path), name: name, path: path, size: size, createdDate: nil, modifiedDate: modifiedDate, fileType: fileType, isHidden: isHidden, isReadOnly: isReadOnly)
|
||||
}
|
||||
// codebeat:enable[ARITY]
|
||||
}
|
||||
|
||||
// codebeat:disable[ARITY]
|
||||
internal extension DropboxFileProvider {
|
||||
func list(path: String, cursor: String? = nil, prevContents: [DropboxFileObject] = [], recursive: Bool = false, completionHandler: ((contents: [FileObject], cursor: String?, error: ErrorType?) -> Void)) {
|
||||
func list(_ path: String, cursor: String? = nil, prevContents: [DropboxFileObject] = [], recursive: Bool = false, completionHandler: @escaping ((_ contents: [FileObject], _ cursor: String?, _ error: Error?) -> Void)) {
|
||||
var requestDictionary = [String: AnyObject]()
|
||||
let url: NSURL
|
||||
let url: URL
|
||||
if let cursor = cursor {
|
||||
url = NSURL(string: "https://api.dropboxapi.com/2/files/list_folder/continue")!
|
||||
requestDictionary["cursor"] = cursor
|
||||
url = URL(string: "https://api.dropboxapi.com/2/files/list_folder/continue")!
|
||||
requestDictionary["cursor"] = cursor as NSString?
|
||||
} else {
|
||||
url = NSURL(string: "https://api.dropboxapi.com/2/files/list_folder")!
|
||||
requestDictionary["path"] = correctPath(path)
|
||||
requestDictionary["recursive"] = recursive
|
||||
url = URL(string: "https://api.dropboxapi.com/2/files/list_folder")!
|
||||
requestDictionary["path"] = correctPath(path) as NSString?
|
||||
requestDictionary["recursive"] = recursive as NSNumber?
|
||||
}
|
||||
let request = NSMutableURLRequest(URL: url)
|
||||
request.HTTPMethod = "POST"
|
||||
var request = URLRequest(url: url)
|
||||
request.httpMethod = "POST"
|
||||
request.setValue("Bearer \(credential?.password ?? "")", forHTTPHeaderField: "Authorization")
|
||||
request.setValue("application/json", forHTTPHeaderField: "Content-Type")
|
||||
request.HTTPBody = dictionaryToJSON(requestDictionary)?.dataUsingEncoding(NSUTF8StringEncoding)
|
||||
let task = session.dataTaskWithRequest(request) { (data, response, error) in
|
||||
request.httpBody = dictionaryToJSON(requestDictionary)?.data(using: String.Encoding.utf8)
|
||||
let task = session.dataTask(with: request, completionHandler: { (data, response, error) in
|
||||
var responseError: FileProviderDropboxError?
|
||||
if let code = (response as? NSHTTPURLResponse)?.statusCode where code >= 300, let rCode = FileProviderHTTPErrorCode(rawValue: code) {
|
||||
responseError = FileProviderDropboxError(code: rCode, path: path, errorDescription: String(data: data ?? NSData(), encoding: NSUTF8StringEncoding))
|
||||
if let code = (response as? HTTPURLResponse)?.statusCode , code >= 300, let rCode = FileProviderHTTPErrorCode(rawValue: code) {
|
||||
responseError = FileProviderDropboxError(code: rCode, path: path, errorDescription: String(data: data ?? Data(), encoding: String.Encoding.utf8))
|
||||
}
|
||||
if let data = data, let jsonStr = String(data: data, encoding: NSUTF8StringEncoding) {
|
||||
if let data = data, let jsonStr = String(data: data, encoding: String.Encoding.utf8) {
|
||||
let json = jsonToDictionary(jsonStr)
|
||||
if let entries = json?["entries"] as? [AnyObject] where entries.count > 0 {
|
||||
if let entries = json?["entries"] as? [AnyObject] , entries.count > 0 {
|
||||
var files = prevContents
|
||||
for entry in entries {
|
||||
if let entry = entry as? [String: AnyObject], let file = self.mapToFileObject(entry) {
|
||||
@@ -70,54 +70,54 @@ internal extension DropboxFileProvider {
|
||||
if hasmore {
|
||||
self.list(path, cursor: ncursor, prevContents: files, completionHandler: completionHandler)
|
||||
} else {
|
||||
completionHandler(contents: files, cursor: ncursor, error: responseError ?? error)
|
||||
completionHandler(files, ncursor, responseError ?? error)
|
||||
}
|
||||
return
|
||||
}
|
||||
}
|
||||
completionHandler(contents: [], cursor: nil, error: responseError ?? error)
|
||||
}
|
||||
completionHandler([], nil, responseError ?? error)
|
||||
})
|
||||
task.resume()
|
||||
}
|
||||
|
||||
func upload_simple(targetPath: String, data: NSData, modifiedDate: NSDate = NSDate(), overwrite: Bool, operation: FileOperation, completionHandler: SimpleCompletionHandler) {
|
||||
assert(data.length < 150*1024*1024, "Maximum size of allowed size to upload is 150MB")
|
||||
func upload_simple(_ targetPath: String, data: Data, modifiedDate: Date = Date(), overwrite: Bool, operation: FileOperation, completionHandler: SimpleCompletionHandler) {
|
||||
assert(data.count < 150*1024*1024, "Maximum size of allowed size to upload is 150MB")
|
||||
var requestDictionary = [String: AnyObject]()
|
||||
let url: NSURL
|
||||
url = NSURL(string: "https://content.dropboxapi.com/2/files/upload")!
|
||||
requestDictionary["path"] = correctPath(targetPath)
|
||||
requestDictionary["mode"] = overwrite ? "overwrite" : "add"
|
||||
let dateFormatter = NSDateFormatter()
|
||||
let url: URL
|
||||
url = URL(string: "https://content.dropboxapi.com/2/files/upload")!
|
||||
requestDictionary["path"] = correctPath(targetPath) as NSString?
|
||||
requestDictionary["mode"] = (overwrite ? "overwrite" : "add") as NSString
|
||||
let dateFormatter = DateFormatter()
|
||||
dateFormatter.dateFormat = "yyyy'-'MM'-'dd'T'HH':'mm':'ssz"
|
||||
requestDictionary["client_modified"] = dateFormatter.stringFromDate(modifiedDate)
|
||||
let request = NSMutableURLRequest(URL: url)
|
||||
request.HTTPMethod = "POST"
|
||||
requestDictionary["client_modified"] = dateFormatter.string(from: modifiedDate) as NSString
|
||||
var request = URLRequest(url: url)
|
||||
request.httpMethod = "POST"
|
||||
request.setValue("Bearer \(credential?.password ?? "")", forHTTPHeaderField: "Authorization")
|
||||
request.setValue("application/octet-stream", forHTTPHeaderField: "Content-Type")
|
||||
request.setValue(dictionaryToJSON(requestDictionary), forHTTPHeaderField: "Dropbox-API-Arg")
|
||||
request.HTTPBody = data
|
||||
let task = session.uploadTaskWithRequest(request, fromData: data) { (data, response, error) in
|
||||
request.httpBody = data
|
||||
let task = session.uploadTask(with: request, from: data, completionHandler: { (data, response, error) in
|
||||
var responseError: FileProviderDropboxError?
|
||||
if let code = (response as? NSHTTPURLResponse)?.statusCode where code >= 300, let rCode = FileProviderHTTPErrorCode(rawValue: code) {
|
||||
responseError = FileProviderDropboxError(code: rCode, path: targetPath, errorDescription: String(data: data ?? NSData(), encoding: NSUTF8StringEncoding))
|
||||
if let code = (response as? HTTPURLResponse)?.statusCode , code >= 300, let rCode = FileProviderHTTPErrorCode(rawValue: code) {
|
||||
responseError = FileProviderDropboxError(code: rCode, path: targetPath, errorDescription: String(data: data ?? Data(), encoding: String.Encoding.utf8))
|
||||
}
|
||||
defer {
|
||||
self.delegateNotify(.Create(path: targetPath), error: responseError ?? error)
|
||||
self.delegateNotify(.create(path: targetPath), error: responseError ?? error)
|
||||
}
|
||||
completionHandler?(error: responseError ?? error)
|
||||
}
|
||||
var dic: [String: AnyObject] = ["type": operation.description]
|
||||
completionHandler?(responseError ?? error)
|
||||
})
|
||||
var dic: [String: AnyObject] = ["type": operation.description as NSString]
|
||||
switch operation {
|
||||
case .Create(path: let s):
|
||||
dic["source"] = s
|
||||
case .Copy(source: let s, destination: let d):
|
||||
dic["source"] = s
|
||||
dic["dest"] = d
|
||||
case .Modify(path: let s):
|
||||
dic["source"] = s
|
||||
case .Move(source: let s, destination: let d):
|
||||
dic["source"] = s
|
||||
dic["dest"] = d
|
||||
case .create(path: let s):
|
||||
dic["source"] = s as NSString
|
||||
case .copy(source: let s, destination: let d):
|
||||
dic["source"] = s as NSString
|
||||
dic["dest"] = d as NSString
|
||||
case .modify(path: let s):
|
||||
dic["source"] = s as NSString
|
||||
case .move(source: let s, destination: let d):
|
||||
dic["source"] = s as NSString
|
||||
dic["dest"] = d as NSString
|
||||
default:
|
||||
break
|
||||
}
|
||||
@@ -125,28 +125,28 @@ internal extension DropboxFileProvider {
|
||||
task.resume()
|
||||
}
|
||||
|
||||
func search(startPath: String = "", query: String, start: Int = 0, maxResultPerPage: Int = 25, maxResults: Int = -1, foundItem:((file: DropboxFileObject) -> Void), completionHandler: ((error: ErrorType?) -> Void)) {
|
||||
let url = NSURL(string: "https://api.dropboxapi.com/2/files/search")!
|
||||
let request = NSMutableURLRequest(URL: url)
|
||||
request.HTTPMethod = "POST"
|
||||
func search(_ startPath: String = "", query: String, start: Int = 0, maxResultPerPage: Int = 25, maxResults: Int = -1, foundItem:@escaping ((_ file: DropboxFileObject) -> Void), completionHandler: @escaping ((_ error: Error?) -> Void)) {
|
||||
let url = URL(string: "https://api.dropboxapi.com/2/files/search")!
|
||||
var request = URLRequest(url: url)
|
||||
request.httpMethod = "POST"
|
||||
request.setValue("Bearer \(credential?.password ?? "")", forHTTPHeaderField: "Authorization")
|
||||
request.setValue("application/json", forHTTPHeaderField: "Content-Type")
|
||||
var requestDictionary: [String: AnyObject] = ["path": startPath]
|
||||
requestDictionary["query"] = query
|
||||
requestDictionary["start"] = start
|
||||
requestDictionary["max_results"] = maxResultPerPage
|
||||
request.HTTPBody = dictionaryToJSON(requestDictionary)?.dataUsingEncoding(NSUTF8StringEncoding)
|
||||
let task = session.dataTaskWithRequest(request) { (data, response, error) in
|
||||
var requestDictionary: [String: AnyObject] = ["path": startPath as NSString]
|
||||
requestDictionary["query"] = query as NSString
|
||||
requestDictionary["start"] = start as NSNumber
|
||||
requestDictionary["max_results"] = maxResultPerPage as NSNumber
|
||||
request.httpBody = dictionaryToJSON(requestDictionary)?.data(using: String.Encoding.utf8)
|
||||
let task = session.dataTask(with: request, completionHandler: { (data, response, error) in
|
||||
var responseError: FileProviderDropboxError?
|
||||
if let code = (response as? NSHTTPURLResponse)?.statusCode where code >= 300, let rCode = FileProviderHTTPErrorCode(rawValue: code) {
|
||||
responseError = FileProviderDropboxError(code: rCode, path: startPath, errorDescription: String(data: data ?? NSData(), encoding: NSUTF8StringEncoding))
|
||||
if let code = (response as? HTTPURLResponse)?.statusCode , code >= 300, let rCode = FileProviderHTTPErrorCode(rawValue: code) {
|
||||
responseError = FileProviderDropboxError(code: rCode, path: startPath, errorDescription: String(data: data ?? Data(), encoding: String.Encoding.utf8))
|
||||
}
|
||||
if let data = data, let jsonStr = String(data: data, encoding: NSUTF8StringEncoding) {
|
||||
if let data = data, let jsonStr = String(data: data, encoding: String.Encoding.utf8) {
|
||||
let json = jsonToDictionary(jsonStr)
|
||||
if let entries = json?["matches"] as? [AnyObject] where entries.count > 0 {
|
||||
if let entries = json?["matches"] as? [AnyObject] , entries.count > 0 {
|
||||
for entry in entries {
|
||||
if let entry = entry as? [String: AnyObject], let file = self.mapToFileObject(entry) {
|
||||
foundItem(file: file)
|
||||
foundItem(file)
|
||||
}
|
||||
}
|
||||
let rstart = json?["start"] as? Int
|
||||
@@ -154,39 +154,39 @@ internal extension DropboxFileProvider {
|
||||
if hasmore, let rstart = rstart {
|
||||
self.search(startPath, query: query, start: rstart + entries.count, maxResultPerPage: maxResultPerPage, foundItem: foundItem, completionHandler: completionHandler)
|
||||
} else {
|
||||
completionHandler(error: responseError ?? error)
|
||||
completionHandler(responseError ?? error)
|
||||
}
|
||||
return
|
||||
}
|
||||
}
|
||||
completionHandler(error: responseError ?? error)
|
||||
}
|
||||
completionHandler(responseError ?? error)
|
||||
})
|
||||
task.resume()
|
||||
}
|
||||
}
|
||||
// codebeat:enable[ARITY]
|
||||
|
||||
internal extension DropboxFileProvider {
|
||||
func mapToFileObject(jsonStr: String) -> DropboxFileObject? {
|
||||
func mapToFileObject(_ jsonStr: String) -> DropboxFileObject? {
|
||||
guard let json = jsonToDictionary(jsonStr) else { return nil }
|
||||
return self.mapToFileObject(json)
|
||||
}
|
||||
|
||||
func mapToFileObject(json: [String: AnyObject]) -> DropboxFileObject? {
|
||||
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 size = (json["size"] as? NSNumber)?.longLongValue ?? -1
|
||||
let serverTime = resolveDate(json["server_modified"] as? String ?? "")
|
||||
let modifiedDate = resolveDate(json["client_modified"] as? String ?? "")
|
||||
let size = (json["size"] as? NSNumber)?.int64Value ?? -1
|
||||
let serverTime = resolve(dateString: json["server_modified"] as? String ?? "")
|
||||
let modifiedDate = resolve(dateString: 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(name: name, path: path, size: size, serverTime: serverTime, modifiedDate: modifiedDate, fileType: isDirectory ? .Directory : .Regular, isReadOnly: isReadonly, id: id, rev: rev)
|
||||
return DropboxFileObject(name: name, path: path, size: size, serverTime: serverTime, modifiedDate: modifiedDate, fileType: isDirectory ? .directory : .regular, isReadOnly: isReadonly, id: id, rev: rev)
|
||||
}
|
||||
|
||||
func delegateNotify(operation: FileOperation, error: ErrorType?) {
|
||||
dispatch_async(dispatch_get_main_queue(), {
|
||||
func delegateNotify(_ operation: FileOperation, error: Error?) {
|
||||
DispatchQueue.main.async(execute: {
|
||||
if error == nil {
|
||||
self.delegate?.fileproviderSucceed(self, operation: operation)
|
||||
} else {
|
||||
@@ -194,4 +194,4 @@ internal extension DropboxFileProvider {
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+119
-119
@@ -14,26 +14,26 @@ private var lasttaskIdAssociated = 1_000_000_000
|
||||
/// This class is a replica of NSURLSessionStreamTask with same api for iOS 7/8
|
||||
/// while it will fallback to NSURLSessionStreamTask in iOS 9.
|
||||
@objc
|
||||
public class FPSStreamTask: NSURLSessionTask, NSStreamDelegate {
|
||||
private var inputStream: NSInputStream?
|
||||
private var outputStream: NSOutputStream?
|
||||
open class FPSStreamTask: URLSessionTask, StreamDelegate {
|
||||
fileprivate var inputStream: InputStream?
|
||||
fileprivate var outputStream: OutputStream?
|
||||
|
||||
private var dispatch_queue: dispatch_queue_t!
|
||||
internal var _underlyingSession: NSURLSession
|
||||
private var streamDelegate: FPSStreamDelegate? {
|
||||
fileprivate var dispatch_queue: DispatchQueue!
|
||||
internal var _underlyingSession: URLSession
|
||||
fileprivate var streamDelegate: FPSStreamDelegate? {
|
||||
return (_underlyingSession.delegate as? FPSStreamDelegate)
|
||||
}
|
||||
private var _taskIdentifier: Int
|
||||
fileprivate var _taskIdentifier: Int
|
||||
|
||||
@available(iOS 9.0, OSX 10.11, *)
|
||||
static var streamTasks = [Int: NSURLSessionStreamTask]()
|
||||
static var streamTasks = [Int: URLSessionStreamTask]()
|
||||
|
||||
@available(iOS 9.0, OSX 10.11, *)
|
||||
internal var _underlyingTask: NSURLSessionStreamTask? {
|
||||
internal var _underlyingTask: URLSessionStreamTask? {
|
||||
return FPSStreamTask.streamTasks[_taskIdentifier]
|
||||
}
|
||||
|
||||
public override var taskIdentifier: Int {
|
||||
open override var taskIdentifier: Int {
|
||||
if #available(iOS 9.0, OSX 10.11, *) {
|
||||
return _underlyingTask!.taskIdentifier
|
||||
} else {
|
||||
@@ -41,8 +41,8 @@ public class FPSStreamTask: NSURLSessionTask, NSStreamDelegate {
|
||||
}
|
||||
}
|
||||
|
||||
private var _state: NSURLSessionTaskState = .Suspended
|
||||
override public var state: NSURLSessionTaskState {
|
||||
fileprivate var _state: URLSessionTask.State = .suspended
|
||||
override open var state: URLSessionTask.State {
|
||||
if #available(iOS 9.0, OSX 10.11, *) {
|
||||
return _underlyingTask!.state
|
||||
} else {
|
||||
@@ -50,7 +50,7 @@ public class FPSStreamTask: NSURLSessionTask, NSStreamDelegate {
|
||||
}
|
||||
}
|
||||
|
||||
override public var originalRequest: NSURLRequest? {
|
||||
override open var originalRequest: URLRequest? {
|
||||
if #available(iOS 9.0, OSX 10.11, *) {
|
||||
return _underlyingTask!.originalRequest
|
||||
} else {
|
||||
@@ -58,7 +58,7 @@ public class FPSStreamTask: NSURLSessionTask, NSStreamDelegate {
|
||||
}
|
||||
}
|
||||
|
||||
override public var currentRequest: NSURLRequest? {
|
||||
override open var currentRequest: URLRequest? {
|
||||
if #available(iOS 9.0, OSX 10.11, *) {
|
||||
return _underlyingTask!.currentRequest
|
||||
} else {
|
||||
@@ -66,10 +66,10 @@ public class FPSStreamTask: NSURLSessionTask, NSStreamDelegate {
|
||||
}
|
||||
}
|
||||
|
||||
private var _countOfBytesSent: Int64 = 0
|
||||
private var _countOfBytesRecieved: Int64 = 0
|
||||
fileprivate var _countOfBytesSent: Int64 = 0
|
||||
fileprivate var _countOfBytesRecieved: Int64 = 0
|
||||
|
||||
override public var countOfBytesSent: Int64 {
|
||||
override open var countOfBytesSent: Int64 {
|
||||
if #available(iOS 9.0, OSX 10.11, *) {
|
||||
return _underlyingTask!.countOfBytesSent
|
||||
} else {
|
||||
@@ -77,7 +77,7 @@ public class FPSStreamTask: NSURLSessionTask, NSStreamDelegate {
|
||||
}
|
||||
}
|
||||
|
||||
override public var countOfBytesReceived: Int64 {
|
||||
override open var countOfBytesReceived: Int64 {
|
||||
if #available(iOS 9.0, OSX 10.11, *) {
|
||||
return _underlyingTask!.countOfBytesReceived
|
||||
} else {
|
||||
@@ -85,7 +85,7 @@ public class FPSStreamTask: NSURLSessionTask, NSStreamDelegate {
|
||||
}
|
||||
}
|
||||
|
||||
override public var countOfBytesExpectedToSend: Int64 {
|
||||
override open var countOfBytesExpectedToSend: Int64 {
|
||||
if #available(iOS 9.0, OSX 10.11, *) {
|
||||
return _underlyingTask!.countOfBytesExpectedToSend
|
||||
} else {
|
||||
@@ -93,7 +93,7 @@ public class FPSStreamTask: NSURLSessionTask, NSStreamDelegate {
|
||||
}
|
||||
}
|
||||
|
||||
override public var countOfBytesExpectedToReceive: Int64 {
|
||||
override open var countOfBytesExpectedToReceive: Int64 {
|
||||
if #available(iOS 9.0, OSX 10.11, *) {
|
||||
return _underlyingTask!.countOfBytesExpectedToReceive
|
||||
} else {
|
||||
@@ -106,49 +106,49 @@ public class FPSStreamTask: NSURLSessionTask, NSStreamDelegate {
|
||||
}
|
||||
|
||||
var host: (hostname: String, port: Int)?
|
||||
var service: NSNetService?
|
||||
var service: NetService?
|
||||
|
||||
internal init(session: NSURLSession, host: String, port: Int) {
|
||||
internal init(session: URLSession, host: String, port: Int) {
|
||||
self._underlyingSession = session
|
||||
if #available(iOS 9.0, OSX 10.11, *) {
|
||||
let task = session.streamTaskWithHostName(host, port: port)
|
||||
let task = session.streamTask(withHostName: host, port: port)
|
||||
self._taskIdentifier = task.taskIdentifier
|
||||
FPSStreamTask.streamTasks[_taskIdentifier] = task
|
||||
} else {
|
||||
lasttaskIdAssociated += 1
|
||||
self._taskIdentifier = lasttaskIdAssociated
|
||||
self.host = (host, port)
|
||||
self.dispatch_queue = dispatch_queue_create("FSPStreamTask", DISPATCH_QUEUE_CONCURRENT)
|
||||
self.dispatch_queue = DispatchQueue(label: "FSPStreamTask", attributes: DispatchQueue.Attributes.concurrent)
|
||||
}
|
||||
}
|
||||
|
||||
internal init(session: NSURLSession, netService: NSNetService) {
|
||||
internal init(session: URLSession, netService: NetService) {
|
||||
self._underlyingSession = session
|
||||
if #available(iOS 9.0, OSX 10.11, *) {
|
||||
let task = session.streamTaskWithNetService(netService)
|
||||
let task = session.streamTask(with: netService)
|
||||
self._taskIdentifier = task.taskIdentifier
|
||||
FPSStreamTask.streamTasks[_taskIdentifier] = task
|
||||
} else {
|
||||
lasttaskIdAssociated += 1
|
||||
self._taskIdentifier = lasttaskIdAssociated
|
||||
self.service = netService
|
||||
self.dispatch_queue = dispatch_queue_create("FSPStreamTask", DISPATCH_QUEUE_CONCURRENT)
|
||||
self.dispatch_queue = DispatchQueue(label: "FSPStreamTask", attributes: DispatchQueue.Attributes.concurrent)
|
||||
}
|
||||
}
|
||||
|
||||
override public func cancel() {
|
||||
override open func cancel() {
|
||||
if #available(iOS 9.0, OSX 10.11, *) {
|
||||
_underlyingTask!.cancel()
|
||||
} else {
|
||||
self._state = .Canceling
|
||||
self._state = .canceling
|
||||
inputStream?.setValue(kCFBooleanTrue, forKey: kCFStreamPropertyShouldCloseNativeSocket as String)
|
||||
outputStream?.setValue(kCFBooleanTrue, forKey: kCFStreamPropertyShouldCloseNativeSocket as String)
|
||||
|
||||
self.inputStream?.close()
|
||||
self.outputStream?.close()
|
||||
|
||||
self.inputStream?.removeFromRunLoop(NSRunLoop.currentRunLoop(), forMode: NSDefaultRunLoopMode)
|
||||
self.outputStream?.removeFromRunLoop(NSRunLoop.currentRunLoop(), forMode: NSDefaultRunLoopMode)
|
||||
self.inputStream?.remove(from: RunLoop.current, forMode: RunLoopMode.defaultRunLoopMode)
|
||||
self.outputStream?.remove(from: RunLoop.current, forMode: RunLoopMode.defaultRunLoopMode)
|
||||
|
||||
self.inputStream?.delegate = nil
|
||||
self.outputStream?.delegate = nil
|
||||
@@ -156,14 +156,14 @@ public class FPSStreamTask: NSURLSessionTask, NSStreamDelegate {
|
||||
self.inputStream = nil
|
||||
self.outputStream = nil
|
||||
|
||||
self._state = .Completed
|
||||
self._state = .completed
|
||||
self._countOfBytesSent = 0
|
||||
self._countOfBytesRecieved = 0
|
||||
}
|
||||
}
|
||||
|
||||
var _error: NSError? = nil
|
||||
override public var error: NSError? {
|
||||
var _error: Error? = nil
|
||||
override open var error: Error? {
|
||||
if #available(iOS 9.0, OSX 10.11, *) {
|
||||
return _underlyingTask!.error
|
||||
} else {
|
||||
@@ -171,19 +171,15 @@ public class FPSStreamTask: NSURLSessionTask, NSStreamDelegate {
|
||||
}
|
||||
}
|
||||
|
||||
override public func suspend() {
|
||||
override open func suspend() {
|
||||
if #available(iOS 9.0, OSX 10.11, *) {
|
||||
_underlyingTask!.suspend()
|
||||
} else {
|
||||
inputStream?.close()
|
||||
outputStream?.close()
|
||||
streamDelegate?.URLSession?(_underlyingSession, readClosedForStreamTask: self)
|
||||
streamDelegate?.URLSession?(_underlyingSession, writeClosedForStreamTask: self)
|
||||
self._state = .Suspended
|
||||
self._state = .suspended
|
||||
}
|
||||
}
|
||||
|
||||
override public func resume() {
|
||||
override open func resume() {
|
||||
if #available(iOS 9.0, OSX 10.11, *) {
|
||||
_underlyingTask!.resume()
|
||||
} else {
|
||||
@@ -195,74 +191,74 @@ public class FPSStreamTask: NSURLSessionTask, NSStreamDelegate {
|
||||
let hostRef: CFString = NSString(string: host.hostname)
|
||||
CFStreamCreatePairWithSocketToHost(kCFAllocatorDefault, hostRef, UInt32(host.port), &readStream, &writeStream)
|
||||
} else if let service = service {
|
||||
let cfnetService = CFNetServiceCreate(kCFAllocatorDefault, service.domain, service.type, service.name, Int32(service.port))
|
||||
let cfnetService = CFNetServiceCreate(kCFAllocatorDefault, service.domain as CFString, service.type as CFString, service.name as CFString, Int32(service.port))
|
||||
CFStreamCreatePairWithSocketToNetService(kCFAllocatorDefault, cfnetService.takeRetainedValue(), &readStream, &writeStream)
|
||||
}
|
||||
|
||||
inputStream = readStream?.takeRetainedValue()
|
||||
outputStream = writeStream?.takeRetainedValue()
|
||||
guard let inputStream = inputStream, outputStream = outputStream else {
|
||||
guard let inputStream = inputStream, let outputStream = outputStream else {
|
||||
return
|
||||
}
|
||||
streamDelegate?.URLSession?(self._underlyingSession, streamTask: self, didBecomeInputStream: inputStream, outputStream: outputStream)
|
||||
streamDelegate?.urlSession?(self._underlyingSession, streamTask: self, didBecome: inputStream, outputStream: outputStream)
|
||||
}
|
||||
|
||||
guard let inputStream = inputStream, outputStream = outputStream else {
|
||||
guard let inputStream = inputStream, let outputStream = outputStream else {
|
||||
return
|
||||
}
|
||||
|
||||
inputStream.delegate = self
|
||||
outputStream.delegate = self
|
||||
|
||||
dispatch_sync(dispatch_queue, {
|
||||
inputStream.scheduleInRunLoop(NSRunLoop.currentRunLoop(), forMode: NSDefaultRunLoopMode)
|
||||
outputStream.scheduleInRunLoop(NSRunLoop.currentRunLoop(), forMode: NSDefaultRunLoopMode)
|
||||
dispatch_queue.sync(execute: {
|
||||
inputStream.schedule(in: RunLoop.current, forMode: RunLoopMode.defaultRunLoopMode)
|
||||
outputStream.schedule(in: RunLoop.current, forMode: RunLoopMode.defaultRunLoopMode)
|
||||
})
|
||||
|
||||
inputStream.open()
|
||||
outputStream.open()
|
||||
|
||||
_state = .Running
|
||||
_state = .running
|
||||
}
|
||||
}
|
||||
|
||||
private let dataToBeSent: NSMutableData = NSMutableData()
|
||||
private let dataReceived: NSMutableData = NSMutableData()
|
||||
fileprivate let dataToBeSent: NSMutableData = NSMutableData()
|
||||
fileprivate let dataReceived: NSMutableData = NSMutableData()
|
||||
|
||||
/* Read minBytes, or at most maxBytes bytes and invoke the completion
|
||||
* handler on the sessions delegate queue with the data or an error.
|
||||
* If an error occurs, any outstanding reads will also fail, and new
|
||||
* read requests will error out immediately.
|
||||
*/
|
||||
public func readDataOfMinLength(minBytes: Int, maxLength maxBytes: Int, timeout: NSTimeInterval, completionHandler: (NSData?, Bool, NSError?) -> Void) {
|
||||
open func readData(OfMinLength minBytes: Int, maxLength maxBytes: Int, timeout: TimeInterval, completionHandler: @escaping (Data?, Bool, NSError?) -> Void) {
|
||||
if #available(iOS 9.0, OSX 10.11, *) {
|
||||
_underlyingTask!.readDataOfMinLength(minBytes, maxLength: maxBytes, timeout: timeout, completionHandler: completionHandler)
|
||||
_underlyingTask!.readData(ofMinLength: minBytes, maxLength: maxBytes, timeout: timeout, completionHandler: completionHandler as! (Data?, Bool, Error?) -> Void)
|
||||
} else {
|
||||
guard let inputStream = inputStream else {
|
||||
return
|
||||
}
|
||||
var timedOut: Bool = false
|
||||
dispatch_async(dispatch_queue) {
|
||||
dispatch_queue.async {
|
||||
if timeout > 0 {
|
||||
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, Int64(timeout * 1_000_000_000)), self.dispatch_queue, {
|
||||
self.dispatch_queue.asyncAfter(deadline: DispatchTime.now() + Double(Int64(timeout * 1_000_000_000)) / Double(NSEC_PER_SEC), execute: {
|
||||
timedOut = true
|
||||
completionHandler(nil, inputStream.streamStatus == .AtEnd, inputStream.streamError)
|
||||
completionHandler(nil, inputStream.streamStatus == .atEnd, inputStream.streamError as NSError?)
|
||||
})
|
||||
}
|
||||
while (self.dataReceived.length == 0 || self.dataReceived.length < minBytes) && !timedOut {
|
||||
NSRunLoop.currentRunLoop().runUntilDate(NSDate(timeIntervalSinceNow: 0.1));
|
||||
NSThread.sleepForTimeInterval(0.1)
|
||||
RunLoop.current.run(until: Date(timeIntervalSinceNow: 0.1));
|
||||
Thread.sleep(forTimeInterval: 0.1)
|
||||
}
|
||||
let dR = NSMutableData()
|
||||
if self.dataReceived.length > maxBytes {
|
||||
let range = NSRange(location: 0, length: maxBytes - 1)
|
||||
dR.appendData(self.dataReceived.subdataWithRange(range))
|
||||
self.dataReceived.replaceBytesInRange(range, withBytes: nil, length: 0)
|
||||
dR.append(self.dataReceived.subdata(with: range))
|
||||
self.dataReceived.replaceBytes(in: range, withBytes: nil, length: 0)
|
||||
} else {
|
||||
dR.appendData(self.dataReceived)
|
||||
dR.append(self.dataReceived as Data)
|
||||
self.dataReceived.length = 0
|
||||
}
|
||||
completionHandler(dR, inputStream.streamStatus == .AtEnd, inputStream.streamError)
|
||||
completionHandler(dR as Data, inputStream.streamStatus == .atEnd, inputStream.streamError as NSError?)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -272,32 +268,32 @@ public class FPSStreamTask: NSURLSessionTask, NSStreamDelegate {
|
||||
* occur. Note that invocation of the completion handler does not
|
||||
* guarantee that the remote side has received all the bytes, only
|
||||
* that they have been written to the kernel. */
|
||||
public func writeData(data: NSData, timeout: NSTimeInterval, completionHandler: (NSError?) -> Void) {
|
||||
open func writeData(_ data: Data, timeout: TimeInterval, completionHandler: @escaping (Error?) -> Void) {
|
||||
if #available(iOS 9.0, OSX 10.11, *) {
|
||||
_underlyingTask!.writeData(data, timeout: timeout, completionHandler: completionHandler)
|
||||
_underlyingTask!.write(data, timeout: timeout, completionHandler: completionHandler)
|
||||
} else {
|
||||
guard let outputStream = outputStream else {
|
||||
return
|
||||
}
|
||||
var timedOut: Bool = false
|
||||
dispatch_async(dispatch_queue) {
|
||||
dispatch_queue.async {
|
||||
if timeout > 0 {
|
||||
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, Int64(timeout * 1_000_000_000)), self.dispatch_queue, {
|
||||
self.dispatch_queue.asyncAfter(deadline: DispatchTime.now() + Double(Int64(timeout * 1_000_000_000)) / Double(NSEC_PER_SEC), execute: {
|
||||
timedOut = true
|
||||
completionHandler(self._error)
|
||||
})
|
||||
}
|
||||
|
||||
self.dataToBeSent.appendData(data)
|
||||
self.dataToBeSent.append(data)
|
||||
while !outputStream.hasSpaceAvailable && !timedOut {
|
||||
NSRunLoop.currentRunLoop().runUntilDate(NSDate(timeIntervalSinceNow: 0.1));
|
||||
NSThread.sleepForTimeInterval(0.1)
|
||||
RunLoop.current.run(until: Date(timeIntervalSinceNow: 0.1));
|
||||
Thread.sleep(forTimeInterval: 0.1)
|
||||
}
|
||||
if self.dataToBeSent.length > 0 {
|
||||
let bytesWritten = outputStream.write(UnsafePointer(self.dataToBeSent.bytes), maxLength: self.dataToBeSent.length) ?? -1
|
||||
let bytesWritten = outputStream.write(self.dataToBeSent.bytes.bindMemory(to: UInt8.self, capacity: self.dataToBeSent.length), maxLength: self.dataToBeSent.length)
|
||||
if bytesWritten > 0 {
|
||||
let range = NSRange(location: 0, length: bytesWritten)
|
||||
self.dataToBeSent.replaceBytesInRange(range, withBytes: nil, length: 0)
|
||||
self.dataToBeSent.replaceBytes(in: range, withBytes: nil, length: 0)
|
||||
self._countOfBytesSent += bytesWritten
|
||||
completionHandler(nil)
|
||||
} else {
|
||||
@@ -315,20 +311,20 @@ public class FPSStreamTask: NSURLSessionTask, NSStreamDelegate {
|
||||
* message. When that message is received, the task object is
|
||||
* considered completed and will not receive any more delegate
|
||||
* messages. */
|
||||
public func captureStreams() {
|
||||
open func captureStreams() {
|
||||
if #available(iOS 9.0, OSX 10.11, *) {
|
||||
_underlyingTask!.captureStreams()
|
||||
} else {
|
||||
guard let outputStream = outputStream, let inputStream = inputStream else {
|
||||
return
|
||||
}
|
||||
dispatch_async(dispatch_queue) {
|
||||
dispatch_queue.async {
|
||||
self.write(false)
|
||||
while inputStream.streamStatus != .AtEnd {
|
||||
NSRunLoop.currentRunLoop().runUntilDate(NSDate(timeIntervalSinceNow: 0.1));
|
||||
NSThread.sleepForTimeInterval(0.1)
|
||||
while inputStream.streamStatus != .atEnd {
|
||||
RunLoop.current.run(until: Date(timeIntervalSinceNow: 0.1));
|
||||
Thread.sleep(forTimeInterval: 0.1)
|
||||
}
|
||||
self.streamDelegate?.URLSession?(self._underlyingSession, streamTask: self, didBecomeInputStream: inputStream, outputStream: outputStream)
|
||||
self.streamDelegate?.urlSession?(self._underlyingSession, streamTask: self, didBecome: inputStream, outputStream: outputStream)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -339,38 +335,38 @@ public class FPSStreamTask: NSURLSessionTask, NSStreamDelegate {
|
||||
* back to the client, so best practice is to continue reading from
|
||||
* the server until you receive EOF.
|
||||
*/
|
||||
public func closeWrite() {
|
||||
open func closeWrite() {
|
||||
if #available(iOS 9.0, OSX 10.11, *) {
|
||||
_underlyingTask!.closeWrite()
|
||||
} else {
|
||||
dispatch_async(dispatch_queue, {
|
||||
dispatch_queue.async(execute: {
|
||||
self.write(true)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
private func write(close: Bool) {
|
||||
fileprivate func write(_ close: Bool) {
|
||||
guard let outputStream = outputStream else {
|
||||
return
|
||||
}
|
||||
while self.dataToBeSent.length > 0 {
|
||||
let bytesWritten = outputStream.write(UnsafePointer(self.dataToBeSent.bytes), maxLength: self.dataToBeSent.length) ?? -1
|
||||
let bytesWritten = outputStream.write(self.dataToBeSent.bytes.bindMemory(to: UInt8.self, capacity: self.dataToBeSent.length), maxLength: self.dataToBeSent.length)
|
||||
if bytesWritten > 0 {
|
||||
let range = NSRange(location: 0, length: bytesWritten)
|
||||
self.dataToBeSent.replaceBytesInRange(range, withBytes: nil, length: 0)
|
||||
self.dataToBeSent.replaceBytes(in: range, withBytes: nil, length: 0)
|
||||
self._countOfBytesSent += bytesWritten
|
||||
} else {
|
||||
self._error = outputStream.streamError
|
||||
self._error = outputStream.streamError as NSError?
|
||||
}
|
||||
if self.dataToBeSent.length == 0 {
|
||||
break
|
||||
}
|
||||
NSRunLoop.currentRunLoop().runUntilDate(NSDate(timeIntervalSinceNow: 0.1));
|
||||
NSThread.sleepForTimeInterval(0.1)
|
||||
RunLoop.current.run(until: Date(timeIntervalSinceNow: 0.1));
|
||||
Thread.sleep(forTimeInterval: 0.1)
|
||||
}
|
||||
if close {
|
||||
outputStream.close()
|
||||
self.streamDelegate?.URLSession?(self._underlyingSession, writeClosedForStreamTask: self)
|
||||
self.streamDelegate?.urlSession?(self._underlyingSession, writeClosedFor: self)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -378,20 +374,20 @@ public class FPSStreamTask: NSURLSessionTask, NSStreamDelegate {
|
||||
* All outstanding IO will complete before the read side is closed.
|
||||
* You may continue writing to the server.
|
||||
*/
|
||||
public func closeRead() {
|
||||
open func closeRead() {
|
||||
if #available(iOS 9.0, OSX 10.11, *) {
|
||||
_underlyingTask!.closeRead()
|
||||
} else {
|
||||
guard let inputStream = inputStream else {
|
||||
return
|
||||
}
|
||||
dispatch_async(dispatch_queue) {
|
||||
while inputStream.streamStatus != .AtEnd {
|
||||
NSRunLoop.currentRunLoop().runUntilDate(NSDate(timeIntervalSinceNow: 0.1));
|
||||
NSThread.sleepForTimeInterval(0.1)
|
||||
dispatch_queue.async {
|
||||
while inputStream.streamStatus != .atEnd {
|
||||
RunLoop.current.run(until: Date(timeIntervalSinceNow: 0.1));
|
||||
Thread.sleep(forTimeInterval: 0.1)
|
||||
}
|
||||
inputStream.close()
|
||||
self.streamDelegate?.URLSession?(self._underlyingSession, readClosedForStreamTask: self)
|
||||
self.streamDelegate?.urlSession?(self._underlyingSession, readClosedFor: self)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -401,12 +397,12 @@ public class FPSStreamTask: NSURLSessionTask, NSStreamDelegate {
|
||||
* IO has completed. TLS authentication callbacks are sent to the
|
||||
* session's -URLSession:task:didReceiveChallenge:completionHandler:
|
||||
*/
|
||||
public func startSecureConnection() {
|
||||
open func startSecureConnection() {
|
||||
if #available(iOS 9.0, OSX 10.11, *) {
|
||||
_underlyingTask!.startSecureConnection()
|
||||
} else {
|
||||
inputStream!.setProperty(NSStreamSocketSecurityLevelNegotiatedSSL, forKey: NSStreamSocketSecurityLevelKey)
|
||||
outputStream!.setProperty(NSStreamSocketSecurityLevelNegotiatedSSL, forKey: NSStreamSocketSecurityLevelKey)
|
||||
inputStream!.setProperty(StreamSocketSecurityLevel.negotiatedSSL.rawValue, forKey: Stream.PropertyKey.socketSecurityLevelKey)
|
||||
outputStream!.setProperty(StreamSocketSecurityLevel.negotiatedSSL.rawValue, forKey: Stream.PropertyKey.socketSecurityLevelKey)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -414,38 +410,38 @@ public class FPSStreamTask: NSURLSessionTask, NSStreamDelegate {
|
||||
* Cleanly close a secure connection after all pending secure IO has
|
||||
* completed.
|
||||
*/
|
||||
public func stopSecureConnection() {
|
||||
open func stopSecureConnection() {
|
||||
if #available(iOS 9.0, OSX 10.11, *) {
|
||||
_underlyingTask!.stopSecureConnection()
|
||||
} else {
|
||||
inputStream!.setProperty(NSStreamSocketSecurityLevelNone, forKey: NSStreamSocketSecurityLevelKey)
|
||||
outputStream!.setProperty(NSStreamSocketSecurityLevelNone, forKey: NSStreamSocketSecurityLevelKey)
|
||||
inputStream!.setProperty(StreamSocketSecurityLevel.none.rawValue, forKey: Stream.PropertyKey.socketSecurityLevelKey)
|
||||
outputStream!.setProperty(StreamSocketSecurityLevel.none.rawValue, forKey: Stream.PropertyKey.socketSecurityLevelKey)
|
||||
}
|
||||
}
|
||||
|
||||
public func stream(aStream: NSStream, handleEvent eventCode: NSStreamEvent) {
|
||||
open func stream(_ aStream: Stream, handle eventCode: Stream.Event) {
|
||||
switch (eventCode) {
|
||||
case NSStreamEvent.ErrorOccurred:
|
||||
self._error = aStream.streamError
|
||||
streamDelegate?.URLSession?(_underlyingSession, task: self, didCompleteWithError: error)
|
||||
case NSStreamEvent.EndEncountered:
|
||||
case Stream.Event.errorOccurred:
|
||||
self._error = aStream.streamError as NSError?
|
||||
streamDelegate?.urlSession?(_underlyingSession, task: self, didCompleteWithError: error)
|
||||
case Stream.Event.endEncountered:
|
||||
break
|
||||
case NSStreamEvent.None:
|
||||
case Stream.Event():
|
||||
break
|
||||
case NSStreamEvent.OpenCompleted:
|
||||
case Stream.Event.openCompleted:
|
||||
break
|
||||
case NSStreamEvent.HasBytesAvailable:
|
||||
var buffer = [UInt8](count: 2048, repeatedValue: 0)
|
||||
case Stream.Event.hasBytesAvailable:
|
||||
var buffer = [UInt8](repeating: 0, count: 2048)
|
||||
if (aStream == inputStream) {
|
||||
while (inputStream!.hasBytesAvailable ?? false) {
|
||||
while (inputStream!.hasBytesAvailable) {
|
||||
let len = inputStream!.read(&buffer, maxLength: buffer.count)
|
||||
if len > 0 {
|
||||
dataReceived.appendBytes(&buffer, length: len)
|
||||
dataReceived.append(&buffer, length: len)
|
||||
self._countOfBytesRecieved += len
|
||||
}
|
||||
}
|
||||
}
|
||||
case NSStreamEvent.HasSpaceAvailable:
|
||||
case Stream.Event.hasSpaceAvailable:
|
||||
break
|
||||
default:
|
||||
break
|
||||
@@ -453,23 +449,24 @@ public class FPSStreamTask: NSURLSessionTask, NSStreamDelegate {
|
||||
}
|
||||
}
|
||||
|
||||
extension NSURLSession {
|
||||
extension URLSession {
|
||||
/* Creates a bidirectional stream task to a given host and port.
|
||||
*/
|
||||
public func fpstreamTaskWithHostName(hostname: String, port: Int) -> FPSStreamTask {
|
||||
public func fpstreamTaskWithHostName(_ hostname: String, port: Int) -> FPSStreamTask {
|
||||
return FPSStreamTask(session: self, host: hostname, port: port)
|
||||
}
|
||||
|
||||
/* Creates a bidirectional stream task with an NSNetService to identify the endpoint.
|
||||
* The NSNetService will be resolved before any IO completes.
|
||||
*/
|
||||
public func fpstreamTaskWithNetService(service: NSNetService) -> FPSStreamTask {
|
||||
public func fpstreamTaskWithNetService(_ service: NetService) -> FPSStreamTask {
|
||||
return fpstreamTaskWithNetService(service)
|
||||
}
|
||||
}
|
||||
|
||||
@objc
|
||||
public protocol FPSStreamDelegate : NSURLSessionTaskDelegate {
|
||||
public protocol FPSStreamDelegate : URLSessionTaskDelegate {
|
||||
|
||||
|
||||
/* Indiciates that the read side of a connection has been closed. Any
|
||||
* outstanding reads complete, but future reads will immediately fail.
|
||||
@@ -477,13 +474,15 @@ public protocol FPSStreamDelegate : NSURLSessionTaskDelegate {
|
||||
* this delegate message is received, there may still be bytes
|
||||
* available. You only know that no more bytes are available when you
|
||||
* are able to read until EOF. */
|
||||
optional func URLSession(session: NSURLSession, readClosedForStreamTask streamTask: FPSStreamTask)
|
||||
@objc optional func urlSession(_ session: URLSession, readClosedFor streamTask: FPSStreamTask)
|
||||
|
||||
|
||||
/* Indiciates that the write side of a connection has been closed.
|
||||
* Any outstanding writes complete, but future writes will immediately
|
||||
* fail.
|
||||
*/
|
||||
optional func URLSession(session: NSURLSession, writeClosedForStreamTask streamTask: FPSStreamTask)
|
||||
@objc optional func urlSession(_ session: URLSession, writeClosedFor streamTask: FPSStreamTask)
|
||||
|
||||
|
||||
/* A notification that the system has determined that a better route
|
||||
* to the host has been detected (eg, a wi-fi interface becoming
|
||||
@@ -492,7 +491,8 @@ public protocol FPSStreamDelegate : NSURLSessionTaskDelegate {
|
||||
* there is no guarantee that the future task will be able to connect
|
||||
* to the host, so callers should should be prepared for failure of
|
||||
* reads and writes over any new interface. */
|
||||
optional func URLSession(session: NSURLSession, betterRouteDiscoveredForStreamTask streamTask: FPSStreamTask)
|
||||
@objc optional func urlSession(_ session: URLSession, betterRouteDiscoveredFor streamTask: FPSStreamTask)
|
||||
|
||||
|
||||
/* The given task has been completed, and unopened NSInputStream and
|
||||
* NSOutputStream objects are created from the underlying network
|
||||
@@ -500,7 +500,7 @@ public protocol FPSStreamDelegate : NSURLSessionTaskDelegate {
|
||||
* completed (including any necessary handshakes.) The streamTask
|
||||
* will not receive any further delegate messages.
|
||||
*/
|
||||
optional func URLSession(session: NSURLSession, streamTask: FPSStreamTask, didBecomeInputStream inputStream: NSInputStream, outputStream: NSOutputStream)
|
||||
@objc optional func urlSession(_ session: URLSession, streamTask: FPSStreamTask, didBecome inputStream: InputStream, outputStream: OutputStream)
|
||||
}
|
||||
|
||||
private let ports = ["http": 80,
|
||||
@@ -520,4 +520,4 @@ private let securePorts = ["https": 443,
|
||||
"telnet": 992,
|
||||
"pop": 995,
|
||||
"smtp": 465,
|
||||
"imap": 993]
|
||||
"imap": 993]
|
||||
|
||||
+139
-149
@@ -16,39 +16,39 @@ public typealias ImageClass = NSImage
|
||||
#endif
|
||||
|
||||
public enum FileType: String {
|
||||
case Directory
|
||||
case Regular
|
||||
case SymbolicLink
|
||||
case Socket
|
||||
case CharacterSpecial
|
||||
case BlockSpecial
|
||||
case NamedPipe
|
||||
case Unknown
|
||||
case directory
|
||||
case regular
|
||||
case symbolicLink
|
||||
case socket
|
||||
case characterSpecial
|
||||
case blockSpecial
|
||||
case namedPipe
|
||||
case unknown
|
||||
|
||||
public init(urlResourceTypeValue: String) {
|
||||
public init(urlResourceTypeValue: URLFileResourceType) {
|
||||
switch urlResourceTypeValue {
|
||||
case NSURLFileResourceTypeNamedPipe: self = .NamedPipe
|
||||
case NSURLFileResourceTypeCharacterSpecial: self = .CharacterSpecial
|
||||
case NSURLFileResourceTypeDirectory: self = .Directory
|
||||
case NSURLFileResourceTypeBlockSpecial: self = .BlockSpecial
|
||||
case NSURLFileResourceTypeRegular: self = .Regular
|
||||
case NSURLFileResourceTypeSymbolicLink: self = .SymbolicLink
|
||||
case NSURLFileResourceTypeSocket: self = .Socket
|
||||
case NSURLFileResourceTypeUnknown: self = .Unknown
|
||||
default: self = .Unknown
|
||||
case URLFileResourceType.namedPipe: self = .namedPipe
|
||||
case URLFileResourceType.characterSpecial: self = .characterSpecial
|
||||
case URLFileResourceType.directory: self = .directory
|
||||
case URLFileResourceType.blockSpecial: self = .blockSpecial
|
||||
case URLFileResourceType.regular: self = .regular
|
||||
case URLFileResourceType.symbolicLink: self = .symbolicLink
|
||||
case URLFileResourceType.socket: self = .socket
|
||||
case URLFileResourceType.unknown: self = .unknown
|
||||
default: self = .unknown
|
||||
}
|
||||
}
|
||||
|
||||
public init(fileTypeValue: String) {
|
||||
public init(fileTypeValue: FileAttributeType) {
|
||||
switch fileTypeValue {
|
||||
case NSFileTypeCharacterSpecial: self = .CharacterSpecial
|
||||
case NSFileTypeDirectory: self = .Directory
|
||||
case NSFileTypeBlockSpecial: self = .BlockSpecial
|
||||
case NSFileTypeRegular: self = .Regular
|
||||
case NSFileTypeSymbolicLink: self = .SymbolicLink
|
||||
case NSFileTypeSocket: self = .Socket
|
||||
case NSFileTypeUnknown: self = .Unknown
|
||||
default: self = .Unknown
|
||||
case FileAttributeType.typeCharacterSpecial: self = .characterSpecial
|
||||
case FileAttributeType.typeDirectory: self = .directory
|
||||
case FileAttributeType.typeBlockSpecial: self = .blockSpecial
|
||||
case FileAttributeType.typeRegular: self = .regular
|
||||
case FileAttributeType.typeSymbolicLink: self = .symbolicLink
|
||||
case FileAttributeType.typeSocket: self = .socket
|
||||
case FileAttributeType.typeUnknown: self = .unknown
|
||||
default: self = .unknown
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -58,21 +58,21 @@ public protocol FoundationErrorEnum {
|
||||
var rawValue: Int { get }
|
||||
}
|
||||
|
||||
extension NSURLError: FoundationErrorEnum {}
|
||||
extension NSCocoaError: FoundationErrorEnum {}
|
||||
extension URLError.Code: FoundationErrorEnum {}
|
||||
extension CocoaError.Code: FoundationErrorEnum {}
|
||||
|
||||
public class FileObject {
|
||||
public let absoluteURL: NSURL?
|
||||
public let name: String
|
||||
public let path: String
|
||||
public let size: Int64
|
||||
public let createdDate: NSDate?
|
||||
public let modifiedDate: NSDate?
|
||||
public let fileType: FileType
|
||||
public let isHidden: Bool
|
||||
public let isReadOnly: Bool
|
||||
open class FileObject {
|
||||
open let absoluteURL: URL?
|
||||
open let name: String
|
||||
open let path: String
|
||||
open let size: Int64
|
||||
open let createdDate: Date?
|
||||
open let modifiedDate: Date?
|
||||
open let fileType: FileType
|
||||
open let isHidden: Bool
|
||||
open let isReadOnly: Bool
|
||||
|
||||
public init(absoluteURL: NSURL? = nil, name: String, path: String, size: Int64 = -1, createdDate: NSDate? = nil, modifiedDate: NSDate? = nil, fileType: FileType = .Regular, isHidden: Bool = false, isReadOnly: Bool = false) {
|
||||
public init(absoluteURL: URL? = nil, name: String, path: String, size: Int64 = -1, createdDate: Date? = nil, modifiedDate: Date? = nil, fileType: FileType = .regular, isHidden: Bool = false, isReadOnly: Bool = false) {
|
||||
self.absoluteURL = absoluteURL
|
||||
self.name = name
|
||||
self.path = path
|
||||
@@ -84,59 +84,59 @@ public class FileObject {
|
||||
self.isReadOnly = isReadOnly
|
||||
}
|
||||
|
||||
public var isDirectory: Bool {
|
||||
return self.fileType == .Directory
|
||||
open var isDirectory: Bool {
|
||||
return self.fileType == .directory
|
||||
}
|
||||
|
||||
public var isSymLink: Bool {
|
||||
return self.fileType == .SymbolicLink
|
||||
open var isSymLink: Bool {
|
||||
return self.fileType == .symbolicLink
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public typealias SimpleCompletionHandler = ((error: ErrorType?) -> Void)?
|
||||
public typealias SimpleCompletionHandler = ((_ error: Error?) -> Void)?
|
||||
|
||||
public protocol FileProviderBasic: class {
|
||||
var type: String { get }
|
||||
static var type: String { get }
|
||||
var isPathRelative: Bool { get }
|
||||
var baseURL: NSURL? { get }
|
||||
var baseURL: URL? { get }
|
||||
var currentPath: String { get set }
|
||||
var dispatch_queue: dispatch_queue_t { get set }
|
||||
var dispatch_queue: DispatchQueue { get set }
|
||||
var delegate: FileProviderDelegate? { get set }
|
||||
var credential: NSURLCredential? { get }
|
||||
var credential: URLCredential? { get }
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
func contentsOfDirectoryAtPath(path: String, completionHandler: ((contents: [FileObject], error: ErrorType?) -> Void))
|
||||
func attributesOfItemAtPath(path: String, completionHandler: ((attributes: FileObject?, error: ErrorType?) -> Void))
|
||||
func contentsOfDirectory(path: String, completionHandler: @escaping ((_ contents: [FileObject], _ error: Error?) -> Void))
|
||||
func attributesOfItem(path: String, completionHandler: @escaping ((_ attributes: FileObject?, _ error: Error?) -> Void))
|
||||
|
||||
func storageProperties(completionHandler: ((total: Int64, used: Int64) -> Void))
|
||||
func storageProperties(completionHandler: @escaping ((_ total: Int64, _ used: Int64) -> Void))
|
||||
}
|
||||
|
||||
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)
|
||||
func copyItemAtPath(path: String, toPath: String, overwrite: Bool, completionHandler: SimpleCompletionHandler)
|
||||
func removeItemAtPath(path: String, completionHandler: SimpleCompletionHandler)
|
||||
func create(folder: String, at: String, completionHandler: SimpleCompletionHandler)
|
||||
func create(file: FileObject, at: String, contents data: Data?, completionHandler: SimpleCompletionHandler)
|
||||
func moveItem(path: String, to: String, overwrite: Bool, completionHandler: SimpleCompletionHandler)
|
||||
func copyItem(path: String, to: String, overwrite: Bool, completionHandler: SimpleCompletionHandler)
|
||||
func removeItem(path: String, completionHandler: SimpleCompletionHandler)
|
||||
|
||||
func copyLocalFileToPath(localFile: NSURL, toPath: String, completionHandler: SimpleCompletionHandler)
|
||||
func copyPathToLocalFile(path: String, toLocalURL: NSURL, completionHandler: SimpleCompletionHandler)
|
||||
func copyItem(localFile: URL, to: String, completionHandler: SimpleCompletionHandler)
|
||||
func copyItem(path: String, toLocalURL: URL, completionHandler: SimpleCompletionHandler)
|
||||
}
|
||||
|
||||
public protocol FileProviderReadWrite: FileProviderBasic {
|
||||
func contentsAtPath(path: String, completionHandler: ((contents: NSData?, error: ErrorType?) -> Void))
|
||||
func contentsAtPath(path: String, offset: Int64, length: Int, completionHandler: ((contents: NSData?, error: ErrorType?) -> Void))
|
||||
func writeContentsAtPath(path: String, contents data: NSData, atomically: Bool, completionHandler: SimpleCompletionHandler)
|
||||
func contents(path: String, completionHandler: @escaping ((_ contents: Data?, _ error: Error?) -> Void))
|
||||
func contents(path: String, offset: Int64, length: Int, completionHandler: @escaping ((_ contents: Data?, _ error: Error?) -> Void))
|
||||
func writeContents(path: String, contents: Data, atomically: Bool, completionHandler: SimpleCompletionHandler)
|
||||
|
||||
func searchFilesAtPath(path: String, recursive: Bool, query: String, foundItemHandler: ((FileObject) -> Void)?, completionHandler: ((files: [FileObject], error: ErrorType?) -> Void))
|
||||
func searchFiles(path: String, recursive: Bool, query: String, foundItemHandler: ((FileObject) -> Void)?, completionHandler: @escaping ((_ files: [FileObject], _ error: Error?) -> Void))
|
||||
}
|
||||
|
||||
public protocol FileProviderMonitor: FileProviderBasic {
|
||||
func registerNotifcation(path: String, eventHandler: (() -> Void))
|
||||
func registerNotifcation(path: String, eventHandler: @escaping (() -> Void))
|
||||
func unregisterNotifcation(path: String)
|
||||
func isRegisteredForNotification(path: String) -> Bool
|
||||
}
|
||||
@@ -146,11 +146,15 @@ public protocol FileProvider: FileProviderBasic, FileProviderOperations, FilePro
|
||||
}
|
||||
|
||||
extension FileProviderBasic {
|
||||
public var bareCurrentPath: String {
|
||||
return currentPath.stringByTrimmingCharactersInSet(NSCharacterSet(charactersInString: ". /"))
|
||||
public var type: String {
|
||||
return type(of: self).type
|
||||
}
|
||||
|
||||
public func absoluteURL(path: String? = nil) -> NSURL {
|
||||
public var bareCurrentPath: String {
|
||||
return currentPath.trimmingCharacters(in: CharacterSet(charactersIn: ". /"))
|
||||
}
|
||||
|
||||
public func absoluteURL(_ path: String? = nil) -> URL {
|
||||
let rpath: String
|
||||
if let path = path {
|
||||
rpath = path
|
||||
@@ -160,49 +164,47 @@ extension FileProviderBasic {
|
||||
if isPathRelative, let baseURL = baseURL {
|
||||
if rpath.hasPrefix("/") && baseURL.uw_absoluteString.hasSuffix("/") {
|
||||
var npath = rpath
|
||||
npath.removeAtIndex(npath.startIndex)
|
||||
npath.remove(at: npath.startIndex)
|
||||
return baseURL.uw_URLByAppendingPathComponent(npath)
|
||||
} else {
|
||||
return baseURL.uw_URLByAppendingPathComponent(rpath)
|
||||
}
|
||||
} else {
|
||||
return NSURL(fileURLWithPath: rpath).URLByStandardizingPath!
|
||||
return URL(fileURLWithPath: rpath).standardizedFileURL
|
||||
}
|
||||
}
|
||||
|
||||
public func relativePathOf(url url: NSURL) -> String {
|
||||
public func relativePathOf(url: URL) -> String {
|
||||
guard let baseURL = self.baseURL else { return url.uw_absoluteString }
|
||||
return url.URLByStandardizingPath!.uw_absoluteString.stringByReplacingOccurrencesOfString(baseURL.uw_absoluteString, withString: "/").stringByRemovingPercentEncoding!
|
||||
return url.standardizedFileURL.uw_absoluteString.replacingOccurrences(of: baseURL.uw_absoluteString, with: "/").removingPercentEncoding!
|
||||
}
|
||||
|
||||
internal func correctPath(path: String?) -> String? {
|
||||
internal func correctPath(_ path: String?) -> String? {
|
||||
guard let path = path else { return nil }
|
||||
var p = path.hasPrefix("/") ? path : "/" + path
|
||||
if p.hasSuffix("/") {
|
||||
p.removeAtIndex(p.endIndex.predecessor())
|
||||
p.remove(at: p.characters.index(before: p.endIndex))
|
||||
}
|
||||
return p
|
||||
}
|
||||
|
||||
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 ?? ""
|
||||
public func fileByUniqueName(_ filePath: String) -> String {
|
||||
let fileUrl = URL(fileURLWithPath: filePath)
|
||||
let dirPath = fileUrl.deletingLastPathComponent().path
|
||||
let fileName = fileUrl.deletingPathExtension().lastPathComponent
|
||||
let fileExt = fileUrl.pathExtension
|
||||
var result = fileName
|
||||
let group = dispatch_group_create()
|
||||
dispatch_group_enter(group)
|
||||
self.contentsOfDirectoryAtPath(dirPath) { (contents, error) in
|
||||
let group = DispatchGroup()
|
||||
group.enter()
|
||||
self.contentsOfDirectory(path: dirPath) { (contents, error) in
|
||||
var bareFileName = fileName
|
||||
let number = Int(fileName.componentsSeparatedByString(" ").filter {
|
||||
!$0.stringByTrimmingCharactersInSet(NSCharacterSet.whitespaceAndNewlineCharacterSet()).isEmpty
|
||||
let number = Int(fileName.components(separatedBy: " ").filter {
|
||||
!$0.trimmingCharacters(in: CharacterSet.whitespacesAndNewlines).isEmpty
|
||||
}.last ?? "noname")
|
||||
if let _ = number {
|
||||
result = fileName.componentsSeparatedByString(" ").filter {
|
||||
!$0.stringByTrimmingCharactersInSet(NSCharacterSet.whitespaceAndNewlineCharacterSet()).isEmpty
|
||||
}.dropLast().joinWithSeparator(" ")
|
||||
result = fileName.components(separatedBy: " ").filter {
|
||||
!$0.trimmingCharacters(in: CharacterSet.whitespacesAndNewlines).isEmpty
|
||||
}.dropLast().joined(separator: " ")
|
||||
bareFileName = result
|
||||
}
|
||||
var i = number ?? 2
|
||||
@@ -215,18 +217,18 @@ extension FileProviderBasic {
|
||||
result = "\(bareFileName) \(i)"
|
||||
i += 1
|
||||
}
|
||||
dispatch_group_leave(group)
|
||||
group.leave()
|
||||
}
|
||||
dispatch_group_wait(group, DISPATCH_TIME_FOREVER)
|
||||
_ = group.wait(timeout: DispatchTime.distantFuture)
|
||||
let finalFile = result + (!fileExt.isEmpty ? "." + fileExt : "")
|
||||
return (dirPath as NSString).stringByAppendingPathComponent(finalFile)
|
||||
return (dirPath as NSString).appendingPathComponent(finalFile)
|
||||
}
|
||||
|
||||
internal func throwError(path: String, code: FoundationErrorEnum) -> NSError {
|
||||
internal func throwError(_ path: String, code: FoundationErrorEnum) -> NSError {
|
||||
let fileURL = self.absoluteURL(path)
|
||||
let domain: String
|
||||
switch code {
|
||||
case is NSURLError:
|
||||
case is URLError:
|
||||
domain = NSURLErrorDomain
|
||||
default:
|
||||
domain = NSCocoaErrorDomain
|
||||
@@ -238,23 +240,23 @@ extension FileProviderBasic {
|
||||
assert(false, "method not implemented")
|
||||
}
|
||||
|
||||
internal func resolveDate(dateString: String) -> NSDate? {
|
||||
let dateFor: NSDateFormatter = NSDateFormatter()
|
||||
dateFor.locale = NSLocale(localeIdentifier: "en_US")
|
||||
internal func resolve(dateString: String) -> Date? {
|
||||
let dateFor: DateFormatter = DateFormatter()
|
||||
dateFor.locale = Locale(identifier: "en_US")
|
||||
dateFor.dateFormat = "EEE',' dd' 'MMM' 'yyyy HH':'mm':'ss zzz"
|
||||
if let rfc1123 = dateFor.dateFromString(dateString) {
|
||||
if let rfc1123 = dateFor.date(from: dateString) {
|
||||
return rfc1123
|
||||
}
|
||||
dateFor.dateFormat = "EEEE',' dd'-'MMM'-'yy HH':'mm':'ss z"
|
||||
if let rfc850 = dateFor.dateFromString(dateString) {
|
||||
if let rfc850 = dateFor.date(from: dateString) {
|
||||
return rfc850
|
||||
}
|
||||
dateFor.dateFormat = "EEE MMM d HH':'mm':'ss yyyy"
|
||||
if let asctime = dateFor.dateFromString(dateString) {
|
||||
if let asctime = dateFor.date(from: dateString) {
|
||||
return asctime
|
||||
}
|
||||
dateFor.dateFormat = "yyyy'-'MM'-'dd'T'HH':'mm':'ssz"
|
||||
if let isotime = dateFor.dateFromString(dateString) {
|
||||
if let isotime = dateFor.date(from: dateString) {
|
||||
return isotime
|
||||
}
|
||||
//self.init()
|
||||
@@ -266,60 +268,60 @@ extension FileProviderBasic {
|
||||
public protocol ExtendedFileProvider: FileProvider {
|
||||
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))
|
||||
func thumbnailOfFile(path: String, dimension: CGSize, completionHandler: @escaping ((_ image: ImageClass?, _ error: Error?) -> Void))
|
||||
func propertiesOfFile(path: String, completionHandler: @escaping ((_ propertiesDictionary: [String: Any], _ keys: [String], _ error: Error?) -> Void))
|
||||
}
|
||||
|
||||
public enum FileOperation: CustomStringConvertible {
|
||||
case Create (path: String)
|
||||
case Copy (source: String, destination: String)
|
||||
case Move (source: String, destination: String)
|
||||
case Modify (path: String)
|
||||
case Remove (path: String)
|
||||
case Link (link: String, target: String)
|
||||
case create (path: String)
|
||||
case copy (source: String, destination: String)
|
||||
case move (source: String, destination: String)
|
||||
case modify (path: String)
|
||||
case remove (path: String)
|
||||
case link (link: String, target: String)
|
||||
|
||||
public var description: String {
|
||||
switch self {
|
||||
case .Create(path: _): return "Create"
|
||||
case .Copy(source: _, destination: _): return "Copy"
|
||||
case .Move(source: _, destination: _): return "Move"
|
||||
case .Modify(path: _): return "Modify"
|
||||
case .Remove(path: _): return "Remove"
|
||||
case .Link(link: _, target: _): return "Link"
|
||||
case .create(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"
|
||||
}
|
||||
}
|
||||
|
||||
internal var actionDescription: String {
|
||||
switch self {
|
||||
case .Create(path: _): return "Creating"
|
||||
case .Copy(source: _, destination: _): return "Copying"
|
||||
case .Move(source: _, destination: _): return "Moving"
|
||||
case .Modify(path: _): return "Modifying"
|
||||
case .Remove(path: _): return "Removing"
|
||||
case .Link(link: _, target: _): return "Linking"
|
||||
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: class {
|
||||
func fileproviderSucceed(fileProvider: FileProviderOperations, operation: FileOperation)
|
||||
func fileproviderFailed(fileProvider: FileProviderOperations, operation: FileOperation)
|
||||
func fileproviderProgress(fileProvider: FileProviderOperations, operation: FileOperation, progress: Float)
|
||||
func fileproviderSucceed(_ fileProvider: FileProviderOperations, operation: 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
|
||||
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
|
||||
func fileProvider(_ fileProvider: FileProviderOperations, shouldProceedAfterError error: Error, operation: FileOperation) -> Bool
|
||||
}
|
||||
|
||||
// THESE ARE METHODS TO PROVIDE COMPATIBILITY WITH SWIFT 2.3 SIMOULTANIOUSLY!
|
||||
|
||||
internal extension NSURL {
|
||||
internal extension URL {
|
||||
var uw_scheme: String {
|
||||
#if swift(>=2.3)
|
||||
return self.scheme ?? ""
|
||||
@@ -329,43 +331,31 @@ internal extension NSURL {
|
||||
}
|
||||
|
||||
var uw_absoluteString: String {
|
||||
#if swift(>=2.3)
|
||||
return self.absoluteString ?? ""
|
||||
#else
|
||||
return self.absoluteString
|
||||
#endif
|
||||
return self.absoluteString
|
||||
}
|
||||
|
||||
func uw_URLByAppendingPathComponent(pathComponent: String) -> NSURL {
|
||||
#if swift(>=2.3)
|
||||
return self.URLByAppendingPathComponent(pathComponent)!
|
||||
#else
|
||||
return self.URLByAppendingPathComponent(pathComponent)
|
||||
#endif
|
||||
func uw_URLByAppendingPathComponent(_ pathComponent: String) -> URL {
|
||||
return self.appendingPathComponent(pathComponent)
|
||||
}
|
||||
|
||||
func uw_URLByAppendingPathExtension(pathExtension: String) -> NSURL {
|
||||
#if swift(>=2.3)
|
||||
return self.URLByAppendingPathExtension(pathExtension)!
|
||||
#else
|
||||
return self.URLByAppendingPathExtension(pathExtension)
|
||||
#endif
|
||||
func uw_URLByAppendingPathExtension(_ pathExtension: String) -> URL {
|
||||
return self.appendingPathExtension(pathExtension)
|
||||
}
|
||||
}
|
||||
|
||||
internal func jsonToDictionary(jsonString: String) -> [String: AnyObject]? {
|
||||
guard let data = jsonString.dataUsingEncoding(NSUTF8StringEncoding) else {
|
||||
internal func jsonToDictionary(_ jsonString: String) -> [String: AnyObject]? {
|
||||
guard let data = jsonString.data(using: String.Encoding.utf8) else {
|
||||
return nil
|
||||
}
|
||||
if let dic = try? NSJSONSerialization.JSONObjectWithData(data, options: NSJSONReadingOptions()) as? [String: AnyObject] {
|
||||
if let dic = try? JSONSerialization.jsonObject(with: data, options: JSONSerialization.ReadingOptions()) as? [String: AnyObject] {
|
||||
return dic
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
internal func dictionaryToJSON(dictionary: [String: AnyObject]) -> String? {
|
||||
if let data = try? NSJSONSerialization.dataWithJSONObject(dictionary, options: NSJSONWritingOptions()) {
|
||||
return String(data: data, encoding: NSUTF8StringEncoding)
|
||||
internal func dictionaryToJSON(_ dictionary: [String: AnyObject]) -> String? {
|
||||
if let data = try? JSONSerialization.data(withJSONObject: dictionary, options: JSONSerialization.WritingOptions()) {
|
||||
return String(data: data, encoding: String.Encoding.utf8)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
+212
-217
@@ -11,310 +11,311 @@ import Foundation
|
||||
public final class LocalFileObject: FileObject {
|
||||
public let allocatedSize: Int64
|
||||
// codebeat:disable[ARITY]
|
||||
public init(absoluteURL: NSURL, name: String, path: String, size: Int64 = -1, allocatedSize: Int64 = 0, createdDate: NSDate? = nil, modifiedDate: NSDate? = nil, fileType: FileType = .Regular, isHidden: Bool = false, isReadOnly: Bool = false) {
|
||||
public init(absoluteURL: URL, name: String, path: String, size: Int64 = -1, allocatedSize: Int64 = 0, createdDate: Date? = nil, modifiedDate: Date? = nil, fileType: FileType = .regular, isHidden: Bool = false, isReadOnly: Bool = false) {
|
||||
self.allocatedSize = allocatedSize
|
||||
super.init(absoluteURL: absoluteURL, name: name, path: path, size: size, createdDate: createdDate, modifiedDate: modifiedDate, fileType: fileType, isHidden: isHidden, isReadOnly: isReadOnly)
|
||||
}
|
||||
// codebeat:enable[ARITY]
|
||||
}
|
||||
|
||||
public class LocalFileProvider: FileProvider, FileProviderMonitor {
|
||||
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 operation_queue: dispatch_queue_t
|
||||
public weak var delegate: FileProviderDelegate?
|
||||
public let credential: NSURLCredential? = nil
|
||||
open class LocalFileProvider: FileProvider, FileProviderMonitor {
|
||||
open static let type = "Local"
|
||||
open var isPathRelative: Bool = true
|
||||
open var baseURL: URL? = LocalFileProvider.defaultBaseURL()
|
||||
open var currentPath: String = ""
|
||||
open var dispatch_queue: DispatchQueue
|
||||
open var operation_queue: DispatchQueue
|
||||
open weak var delegate: FileProviderDelegate?
|
||||
open let credential: URLCredential? = nil
|
||||
|
||||
public let fileManager = NSFileManager()
|
||||
public let opFileManager = NSFileManager()
|
||||
private var fileProviderManagerDelegate: LocalFileProviderManagerDelegate? = nil
|
||||
open let fileManager = FileManager()
|
||||
open let opFileManager = FileManager()
|
||||
fileprivate var fileProviderManagerDelegate: LocalFileProviderManagerDelegate? = nil
|
||||
|
||||
public init () {
|
||||
dispatch_queue = dispatch_queue_create("FileProvider.\(type)", DISPATCH_QUEUE_CONCURRENT)
|
||||
operation_queue = dispatch_queue_create("FileProvider.\(type).Operation", DISPATCH_QUEUE_SERIAL)
|
||||
dispatch_queue = DispatchQueue(label: "FileProvider.\(LocalFileProvider.type)", attributes: DispatchQueue.Attributes.concurrent)
|
||||
operation_queue = DispatchQueue(label: "FileProvider.\(LocalFileProvider.type).Operation", attributes: [])
|
||||
fileProviderManagerDelegate = LocalFileProviderManagerDelegate(provider: self)
|
||||
opFileManager.delegate = fileProviderManagerDelegate
|
||||
}
|
||||
|
||||
public init (baseURL: NSURL) {
|
||||
public init (baseURL: URL) {
|
||||
self.baseURL = baseURL
|
||||
dispatch_queue = dispatch_queue_create("FileProvider.\(type)", DISPATCH_QUEUE_CONCURRENT)
|
||||
operation_queue = dispatch_queue_create("FileProvider.\(type).Operation", DISPATCH_QUEUE_SERIAL)
|
||||
dispatch_queue = DispatchQueue(label: "FileProvider.\(LocalFileProvider.type)", attributes: DispatchQueue.Attributes.concurrent)
|
||||
operation_queue = DispatchQueue(label: "FileProvider.\(LocalFileProvider.type).Operation", attributes: [])
|
||||
fileProviderManagerDelegate = LocalFileProviderManagerDelegate(provider: self)
|
||||
opFileManager.delegate = fileProviderManagerDelegate
|
||||
}
|
||||
|
||||
private static func defaultBaseURL() -> NSURL {
|
||||
let paths = NSSearchPathForDirectoriesInDomains(NSSearchPathDirectory.DocumentDirectory, NSSearchPathDomainMask.UserDomainMask, true);
|
||||
return NSURL(fileURLWithPath: paths[0])
|
||||
fileprivate static func defaultBaseURL() -> URL {
|
||||
let paths = NSSearchPathForDirectoriesInDomains(FileManager.SearchPathDirectory.documentDirectory, FileManager.SearchPathDomainMask.userDomainMask, true);
|
||||
return URL(fileURLWithPath: paths[0])
|
||||
}
|
||||
|
||||
public func contentsOfDirectoryAtPath(path: String, completionHandler: ((contents: [FileObject], error: ErrorType?) -> Void)) {
|
||||
dispatch_async(dispatch_queue) {
|
||||
open func contentsOfDirectory(path: String, completionHandler: @escaping ((_ contents: [FileObject], _ error: Error?) -> Void)) {
|
||||
dispatch_queue.async {
|
||||
do {
|
||||
let contents = try self.fileManager.contentsOfDirectoryAtURL(self.absoluteURL(path), includingPropertiesForKeys: [NSURLNameKey, NSURLFileSizeKey, NSURLFileAllocatedSizeKey, NSURLCreationDateKey, NSURLContentModificationDateKey, NSURLIsHiddenKey, NSURLVolumeIsReadOnlyKey, NSFileGroupOwnerAccountName], options: NSDirectoryEnumerationOptions.SkipsSubdirectoryDescendants)
|
||||
let contents = try self.fileManager.contentsOfDirectory(at: self.absoluteURL(path), includingPropertiesForKeys: [URLResourceKey.nameKey, URLResourceKey.fileSizeKey, URLResourceKey.fileAllocatedSizeKey, URLResourceKey.creationDateKey, URLResourceKey.contentModificationDateKey, URLResourceKey.isHiddenKey, URLResourceKey.volumeIsReadOnlyKey], options: FileManager.DirectoryEnumerationOptions.skipsSubdirectoryDescendants)
|
||||
let filesAttributes = contents.map({ (fileURL) -> LocalFileObject in
|
||||
return self.attributesOfItemAtURL(fileURL)
|
||||
return self.attributesOfItem(url: fileURL)
|
||||
})
|
||||
completionHandler(contents: filesAttributes, error: nil)
|
||||
completionHandler(filesAttributes, nil)
|
||||
} catch let e as NSError {
|
||||
completionHandler(contents: [], error: e)
|
||||
completionHandler([], e)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
internal func attributesOfItemAtURL(fileURL: NSURL) -> LocalFileObject {
|
||||
internal func attributesOfItem(url fileURL: URL) -> LocalFileObject {
|
||||
var namev, sizev, allocated, filetypev, creationDatev, modifiedDatev, hiddenv, readonlyv: AnyObject?
|
||||
_ = try? fileURL.getResourceValue(&namev, forKey: NSURLNameKey)
|
||||
_ = try? fileURL.getResourceValue(&sizev, forKey: NSURLFileSizeKey)
|
||||
_ = try? fileURL.getResourceValue(&allocated, forKey: NSURLFileAllocatedSizeKey)
|
||||
_ = try? fileURL.getResourceValue(&creationDatev, forKey: NSURLCreationDateKey)
|
||||
_ = try? fileURL.getResourceValue(&modifiedDatev, forKey: NSURLContentModificationDateKey)
|
||||
_ = try? fileURL.getResourceValue(&filetypev, forKey: NSURLFileResourceTypeKey)
|
||||
_ = try? fileURL.getResourceValue(&hiddenv, forKey: NSURLIsHiddenKey)
|
||||
_ = try? fileURL.getResourceValue(&readonlyv, forKey: NSURLVolumeIsReadOnlyKey)
|
||||
_ = try? (fileURL as NSURL).getResourceValue(&namev, forKey: URLResourceKey.nameKey)
|
||||
_ = try? (fileURL as NSURL).getResourceValue(&sizev, forKey: URLResourceKey.fileSizeKey)
|
||||
_ = try? (fileURL as NSURL).getResourceValue(&allocated, forKey: URLResourceKey.fileAllocatedSizeKey)
|
||||
_ = try? (fileURL as NSURL).getResourceValue(&creationDatev, forKey: URLResourceKey.creationDateKey)
|
||||
_ = try? (fileURL as NSURL).getResourceValue(&modifiedDatev, forKey: URLResourceKey.contentModificationDateKey)
|
||||
_ = try? (fileURL as NSURL).getResourceValue(&filetypev, forKey: URLResourceKey.fileResourceTypeKey)
|
||||
_ = try? (fileURL as NSURL).getResourceValue(&hiddenv, forKey: URLResourceKey.isHiddenKey)
|
||||
_ = try? (fileURL as NSURL).getResourceValue(&readonlyv, forKey: URLResourceKey.volumeIsReadOnlyKey)
|
||||
let path: String
|
||||
if isPathRelative {
|
||||
path = self.relativePathOf(url: fileURL)
|
||||
} else {
|
||||
path = fileURL.path!
|
||||
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)
|
||||
let filetype = URLFileResourceType(rawValue: filetypev as? String ?? "")
|
||||
let fileAttr = LocalFileObject(absoluteURL: fileURL, name: namev as! String, path: path, size: sizev?.int64Value ?? -1, allocatedSize: allocated?.int64Value ?? -1, createdDate: creationDatev as? Date, modifiedDate: modifiedDatev as? Date, fileType: FileType(urlResourceTypeValue: filetype), isHidden: hiddenv?.boolValue ?? false, isReadOnly: readonlyv?.boolValue ?? false)
|
||||
return fileAttr
|
||||
}
|
||||
|
||||
public func storageProperties(completionHandler: ((total: Int64, used: Int64) -> Void)) {
|
||||
let dict = (try? NSFileManager.defaultManager().attributesOfFileSystemForPath(baseURL?.path ?? "/")) as NSDictionary?;
|
||||
let totalSize = dict?.objectForKey(NSFileSystemSize)?.longLongValue ?? -1;
|
||||
let freeSize = dict?.objectForKey(NSFileSystemFreeSize)?.longLongValue ?? 0;
|
||||
completionHandler(total: totalSize, used: totalSize - freeSize)
|
||||
open func storageProperties(completionHandler: (@escaping (_ total: Int64, _ used: Int64) -> Void)) {
|
||||
let dict = (try? FileManager.default.attributesOfFileSystem(forPath: baseURL?.path ?? "/"))
|
||||
let totalSize = (dict?[FileAttributeKey.systemSize] as? NSNumber)?.int64Value ?? -1;
|
||||
let freeSize = (dict?[FileAttributeKey.systemFreeSize] as? NSNumber)?.int64Value ?? 0;
|
||||
completionHandler(totalSize, totalSize - freeSize)
|
||||
}
|
||||
|
||||
public func attributesOfItemAtPath(path: String, completionHandler: ((attributes: FileObject?, error: ErrorType?) -> Void)) {
|
||||
dispatch_async(dispatch_queue) {
|
||||
completionHandler(attributes: self.attributesOfItemAtURL(self.absoluteURL(path)), error: nil)
|
||||
open func attributesOfItem(path: String, completionHandler: @escaping ((_ attributes: FileObject?, _ error: Error?) -> Void)) {
|
||||
dispatch_queue.async {
|
||||
completionHandler(self.attributesOfItem(url: self.absoluteURL(path)), nil)
|
||||
}
|
||||
}
|
||||
|
||||
public weak var fileOperationDelegate : FileOperationDelegate?
|
||||
open weak var fileOperationDelegate : FileOperationDelegate?
|
||||
|
||||
public func createFolder(folderName: String, atPath: String, completionHandler: SimpleCompletionHandler) {
|
||||
dispatch_async(operation_queue) {
|
||||
@objc(createWithFolder:at:completionHandler:) open func create(folder folderName: String, at atPath: String, completionHandler: SimpleCompletionHandler) {
|
||||
operation_queue.async {
|
||||
do {
|
||||
try self.opFileManager.createDirectoryAtURL(self.absoluteURL(atPath).uw_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) + "/"))
|
||||
try self.opFileManager.createDirectory(at: self.absoluteURL(atPath).uw_URLByAppendingPathComponent(folderName), withIntermediateDirectories: true, attributes: [:])
|
||||
completionHandler?(nil)
|
||||
DispatchQueue.main.async(execute: {
|
||||
self.delegate?.fileproviderSucceed(self, operation: .create(path: (atPath as NSString).appendingPathComponent(folderName) + "/"))
|
||||
})
|
||||
} catch let e as NSError {
|
||||
completionHandler?(error: e)
|
||||
dispatch_async(dispatch_get_main_queue(), {
|
||||
self.delegate?.fileproviderFailed(self, operation: .Create(path: (atPath as NSString).stringByAppendingPathComponent(folderName) + "/"))
|
||||
completionHandler?(e)
|
||||
DispatchQueue.main.async(execute: {
|
||||
self.delegate?.fileproviderFailed(self, operation: .create(path: (atPath as NSString).appendingPathComponent(folderName) + "/"))
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public func createFile(fileAttribs: FileObject, atPath: String, contents data: NSData?, completionHandler: SimpleCompletionHandler) {
|
||||
dispatch_async(operation_queue) {
|
||||
open func create(file fileAttribs: FileObject, at atPath: String, contents data: Data?, completionHandler: SimpleCompletionHandler) {
|
||||
operation_queue.async {
|
||||
let fileURL = self.absoluteURL(atPath).uw_URLByAppendingPathComponent(fileAttribs.name)
|
||||
var attributes = [String : AnyObject]()
|
||||
var attributes = [String : Any]()
|
||||
if let createdDate = fileAttribs.createdDate {
|
||||
attributes[NSFileCreationDate] = createdDate
|
||||
attributes[FileAttributeKey.creationDate.rawValue] = createdDate as NSDate
|
||||
}
|
||||
if let modDate = fileAttribs.modifiedDate {
|
||||
attributes[NSFileModificationDate] = modDate
|
||||
attributes[FileAttributeKey.modificationDate.rawValue] = modDate as NSDate
|
||||
}
|
||||
if fileAttribs.isReadOnly {
|
||||
attributes[NSFilePosixPermissions] = NSNumber(short: 365 /*555 o*/)
|
||||
attributes[FileAttributeKey.posixPermissions.rawValue] = NSNumber(value: 365 as Int16)
|
||||
}
|
||||
let success = self.opFileManager.createFileAtPath(fileURL.path!, contents: data, attributes: attributes)
|
||||
let success = self.opFileManager.createFile(atPath: fileURL.path, contents: data, attributes: attributes)
|
||||
if success {
|
||||
do {
|
||||
try fileURL.setResourceValue(fileAttribs.isHidden, forKey: NSURLIsHiddenKey)
|
||||
try (fileURL as NSURL).setResourceValue(fileAttribs.isHidden, forKey: URLResourceKey.isHiddenKey)
|
||||
} catch _ {}
|
||||
completionHandler?(error: nil)
|
||||
dispatch_async(dispatch_get_main_queue(), {
|
||||
self.delegate?.fileproviderSucceed(self, operation: .Create(path: (atPath as NSString).stringByAppendingPathComponent(fileAttribs.name)))
|
||||
completionHandler?(nil)
|
||||
DispatchQueue.main.async(execute: {
|
||||
self.delegate?.fileproviderSucceed(self, operation: .create(path: (atPath as NSString).appendingPathComponent(fileAttribs.name)))
|
||||
})
|
||||
} else {
|
||||
completionHandler?(error: self.throwError(atPath, code: NSURLError.CannotCreateFile))
|
||||
dispatch_async(dispatch_get_main_queue(), {
|
||||
self.delegate?.fileproviderFailed(self, operation: .Create(path: (atPath as NSString).stringByAppendingPathComponent(fileAttribs.name)))
|
||||
completionHandler?(self.throwError(atPath, code: URLError.cannotCreateFile as FoundationErrorEnum))
|
||||
DispatchQueue.main.async(execute: {
|
||||
self.delegate?.fileproviderFailed(self, operation: .create(path: (atPath as NSString).appendingPathComponent(fileAttribs.name)))
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public func moveItemAtPath(path: String, toPath: String, overwrite: Bool = false, completionHandler: SimpleCompletionHandler) {
|
||||
open func moveItem(path: String, to toPath: String, overwrite: Bool = false, completionHandler: SimpleCompletionHandler) {
|
||||
// FIXME: progress
|
||||
dispatch_async(operation_queue) {
|
||||
if !overwrite && self.fileManager.fileExistsAtPath(self.absoluteURL(toPath).path ?? "") {
|
||||
completionHandler?(error: self.throwError(toPath, code: NSURLError.CannotMoveFile))
|
||||
operation_queue.async {
|
||||
if !overwrite && self.fileManager.fileExists(atPath: self.absoluteURL(toPath).path) {
|
||||
completionHandler?(self.throwError(toPath, code: URLError.cannotMoveFile as FoundationErrorEnum))
|
||||
return
|
||||
}
|
||||
do {
|
||||
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))
|
||||
try self.opFileManager.moveItem(at: self.absoluteURL(path), to: self.absoluteURL(toPath))
|
||||
completionHandler?(nil)
|
||||
DispatchQueue.main.async(execute: {
|
||||
self.delegate?.fileproviderSucceed(self, operation: .move(source: path, destination: toPath))
|
||||
})
|
||||
} catch let e as NSError {
|
||||
completionHandler?(error: e)
|
||||
dispatch_async(dispatch_get_main_queue(), {
|
||||
self.delegate?.fileproviderFailed(self, operation: .Move(source: path, destination: toPath))
|
||||
completionHandler?(e)
|
||||
DispatchQueue.main.async(execute: {
|
||||
self.delegate?.fileproviderFailed(self, operation: .move(source: path, destination: toPath))
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public func copyItemAtPath(path: String, toPath: String, overwrite: Bool = false, completionHandler: SimpleCompletionHandler) {
|
||||
open func copyItem(path: String, to 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(operation_queue) {
|
||||
if !overwrite && self.fileManager.fileExistsAtPath(self.absoluteURL(toPath).path ?? "") {
|
||||
completionHandler?(error: self.throwError(toPath, code: NSURLError.CannotWriteToFile))
|
||||
operation_queue.async {
|
||||
if !overwrite && self.fileManager.fileExists(atPath: self.absoluteURL(toPath).path) {
|
||||
completionHandler?(self.throwError(toPath, code: URLError.cannotWriteToFile as FoundationErrorEnum))
|
||||
return
|
||||
}
|
||||
do {
|
||||
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))
|
||||
try self.opFileManager.copyItem(at: self.absoluteURL(path), to: self.absoluteURL(toPath))
|
||||
completionHandler?(nil)
|
||||
DispatchQueue.main.async(execute: {
|
||||
self.delegate?.fileproviderSucceed(self, operation: .copy(source: path, destination: toPath))
|
||||
})
|
||||
} catch let e as NSError {
|
||||
completionHandler?(error: e)
|
||||
dispatch_async(dispatch_get_main_queue(), {
|
||||
self.delegate?.fileproviderFailed(self, operation: .Copy(source: path, destination: toPath))
|
||||
completionHandler?(e)
|
||||
DispatchQueue.main.async(execute: {
|
||||
self.delegate?.fileproviderFailed(self, operation: .copy(source: path, destination: toPath))
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public func removeItemAtPath(path: String, completionHandler: SimpleCompletionHandler) {
|
||||
dispatch_async(operation_queue) {
|
||||
open func removeItem(path: String, completionHandler: SimpleCompletionHandler) {
|
||||
operation_queue.async {
|
||||
do {
|
||||
try self.opFileManager.removeItemAtURL(self.absoluteURL(path))
|
||||
completionHandler?(error: nil)
|
||||
dispatch_async(dispatch_get_main_queue(), {
|
||||
self.delegate?.fileproviderSucceed(self, operation: .Remove(path: path))
|
||||
try self.opFileManager.removeItem(at: self.absoluteURL(path))
|
||||
completionHandler?(nil)
|
||||
DispatchQueue.main.async(execute: {
|
||||
self.delegate?.fileproviderSucceed(self, operation: .remove(path: path))
|
||||
})
|
||||
} catch let e as NSError {
|
||||
completionHandler?(error: e)
|
||||
dispatch_async(dispatch_get_main_queue(), {
|
||||
self.delegate?.fileproviderFailed(self, operation: .Remove(path: path))
|
||||
completionHandler?(e)
|
||||
DispatchQueue.main.async(execute: {
|
||||
self.delegate?.fileproviderFailed(self, operation: .remove(path: path))
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public func copyLocalFileToPath(localFile: NSURL, toPath: String, completionHandler: SimpleCompletionHandler) {
|
||||
dispatch_async(operation_queue) {
|
||||
open func copyItem(localFile: URL, to toPath: String, completionHandler: SimpleCompletionHandler) {
|
||||
operation_queue.async {
|
||||
do {
|
||||
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.uw_absoluteString, destination: toPath))
|
||||
try self.opFileManager.copyItem(at: localFile, to: self.absoluteURL(toPath))
|
||||
completionHandler?(nil)
|
||||
DispatchQueue.main.async(execute: {
|
||||
self.delegate?.fileproviderSucceed(self, operation: .copy(source: localFile.uw_absoluteString, destination: toPath))
|
||||
})
|
||||
} catch let e as NSError {
|
||||
completionHandler?(error: e)
|
||||
dispatch_async(dispatch_get_main_queue(), {
|
||||
self.delegate?.fileproviderFailed(self, operation: .Copy(source: localFile.uw_absoluteString, destination: toPath))
|
||||
completionHandler?(e)
|
||||
DispatchQueue.main.async(execute: {
|
||||
self.delegate?.fileproviderFailed(self, operation: .copy(source: localFile.uw_absoluteString, destination: toPath))
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public func copyPathToLocalFile(path: String, toLocalURL: NSURL, completionHandler: SimpleCompletionHandler) {
|
||||
dispatch_async(operation_queue) {
|
||||
open func copyItem(path: String, toLocalURL: URL, completionHandler: SimpleCompletionHandler) {
|
||||
operation_queue.async {
|
||||
do {
|
||||
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.uw_absoluteString))
|
||||
try self.opFileManager.copyItem(at: self.absoluteURL(path), to: toLocalURL)
|
||||
completionHandler?(nil)
|
||||
DispatchQueue.main.async(execute: {
|
||||
self.delegate?.fileproviderSucceed(self, operation: .copy(source: path, destination: toLocalURL.uw_absoluteString))
|
||||
})
|
||||
} catch let e as NSError {
|
||||
completionHandler?(error: e)
|
||||
dispatch_async(dispatch_get_main_queue(), {
|
||||
self.delegate?.fileproviderFailed(self, operation: .Copy(source: path, destination: toLocalURL.uw_absoluteString))
|
||||
completionHandler?(e)
|
||||
DispatchQueue.main.async(execute: {
|
||||
self.delegate?.fileproviderFailed(self, operation: .copy(source: path, destination: toLocalURL.uw_absoluteString))
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public func contentsAtPath(path: String, completionHandler: ((contents: NSData?, error: ErrorType?) -> Void)) {
|
||||
dispatch_async(dispatch_queue) {
|
||||
let data = self.fileManager.contentsAtPath(self.absoluteURL(path).path!)
|
||||
completionHandler(contents: data, error: nil)
|
||||
open func contents(path: String, completionHandler: @escaping ((_ contents: Data?, _ error: Error?) -> Void)) {
|
||||
dispatch_queue.async {
|
||||
let data = self.fileManager.contents(atPath: self.absoluteURL(path).path)
|
||||
completionHandler(data, nil)
|
||||
}
|
||||
}
|
||||
|
||||
public func contentsAtPath(path: String, offset: Int64, length: Int, completionHandler: ((contents: NSData?, error: ErrorType?) -> Void)) {
|
||||
open func contents(path: String, offset: Int64, length: Int, completionHandler: @escaping ((_ contents: Data?, _ error: Error?) -> Void)) {
|
||||
// Unfortunatlely there is no method provided in NSFileManager to read a segment of file.
|
||||
// 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)).isDirectory {
|
||||
self.throwError(path, code: NSURLError.FileIsDirectory)
|
||||
}
|
||||
if !self.fileManager.fileExistsAtPath(aPath) {
|
||||
self.throwError(path, code: NSURLError.FileDoesNotExist)
|
||||
dispatch_queue.async {
|
||||
let aPath = self.absoluteURL(path).path
|
||||
guard !self.attributesOfItem(url: self.absoluteURL(path)).isDirectory && self.fileManager.fileExists(atPath: aPath) else {
|
||||
completionHandler(nil, self.throwError(path, code: URLError.cannotOpenFile as FoundationErrorEnum))
|
||||
return
|
||||
}
|
||||
let fd_from = open(aPath, O_RDONLY)
|
||||
if fd_from < 0 {
|
||||
completionHandler(contents: nil, error: self.throwError(path, code: NSURLError.CannotOpenFile))
|
||||
completionHandler(nil, self.throwError(path, code: URLError.cannotOpenFile as FoundationErrorEnum))
|
||||
return
|
||||
}
|
||||
defer { precondition(close(fd_from) >= 0) }
|
||||
lseek(fd_from, offset, SEEK_SET)
|
||||
var buf = [UInt8](count: length, repeatedValue: 0)
|
||||
var buf = [UInt8](repeating: 0, count: length)
|
||||
let nread = read(fd_from, &buf, buf.count)
|
||||
if nread < 0 { self.throwError(path, code: NSURLError.NoPermissionsToReadFile) }
|
||||
if nread == 0 {
|
||||
completionHandler(contents: nil, error: nil)
|
||||
if nread < 0 {
|
||||
completionHandler(nil, self.throwError(path, code: URLError.noPermissionsToReadFile as FoundationErrorEnum))
|
||||
} else if nread == 0 {
|
||||
completionHandler(nil, nil)
|
||||
} else {
|
||||
let data = NSData(bytesNoCopy: &buf, length: nread, freeWhenDone: true)
|
||||
completionHandler(contents: data, error: nil)
|
||||
let data = Data(bytesNoCopy: UnsafeMutablePointer<UInt8>(&buf), count: nread, deallocator: .free)
|
||||
completionHandler(data, nil)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public func writeContentsAtPath(path: String, contents data: NSData, atomically: Bool, completionHandler: SimpleCompletionHandler) {
|
||||
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))
|
||||
open func writeContents(path: String, contents data: Data, atomically: Bool, completionHandler: SimpleCompletionHandler) {
|
||||
operation_queue.async {
|
||||
try? data.write(to: self.absoluteURL(path), options: atomically ? [.atomic] : [])
|
||||
DispatchQueue.main.async(execute: {
|
||||
self.delegate?.fileproviderSucceed(self, operation: .modify(path: path))
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
public func searchFilesAtPath(path: String, recursive: Bool, query: String, foundItemHandler: ((FileObject) -> Void)?, completionHandler: ((files: [FileObject], error: ErrorType?) -> Void)) {
|
||||
dispatch_async(dispatch_queue) {
|
||||
let iterator = self.fileManager.enumeratorAtURL(self.absoluteURL(path), includingPropertiesForKeys: nil, options: recursive ? NSDirectoryEnumerationOptions() : .SkipsSubdirectoryDescendants) { (url, e) -> Bool in
|
||||
completionHandler(files: [], error: e)
|
||||
open func searchFiles(path: String, recursive: Bool, query: String, foundItemHandler: ((FileObject) -> Void)?, completionHandler: @escaping ((_ files: [FileObject], _ error: Error?) -> Void)) {
|
||||
dispatch_queue.async {
|
||||
let iterator = self.fileManager.enumerator(at: self.absoluteURL(path), includingPropertiesForKeys: nil, options: recursive ? FileManager.DirectoryEnumerationOptions() : .skipsSubdirectoryDescendants) { (url, e) -> Bool in
|
||||
completionHandler([], e)
|
||||
return true
|
||||
}
|
||||
var result = [LocalFileObject]()
|
||||
while let fileURL = iterator?.nextObject() as? NSURL {
|
||||
if fileURL.lastPathComponent?.lowercaseString.containsString(query.lowercaseString) ?? false {
|
||||
let fileObject = self.attributesOfItemAtURL(fileURL)
|
||||
result.append(self.attributesOfItemAtURL(fileURL))
|
||||
while let fileURL = iterator?.nextObject() as? URL {
|
||||
if fileURL.lastPathComponent.lowercased().contains(query.lowercased()) {
|
||||
let fileObject = self.attributesOfItem(url: fileURL)
|
||||
result.append(self.attributesOfItem(url: fileURL))
|
||||
foundItemHandler?(fileObject)
|
||||
}
|
||||
}
|
||||
completionHandler(files: result, error: nil)
|
||||
completionHandler(result, nil)
|
||||
}
|
||||
}
|
||||
|
||||
private var monitors = [LocalFolderMonitor]()
|
||||
fileprivate var monitors = [LocalFolderMonitor]()
|
||||
|
||||
public func registerNotifcation(path: String, eventHandler: (() -> Void)) {
|
||||
self.unregisterNotifcation(path)
|
||||
open func registerNotifcation(path: String, eventHandler: @escaping (() -> Void)) {
|
||||
self.unregisterNotifcation(path: path)
|
||||
let absurl = self.absoluteURL(path)
|
||||
var isdirv: AnyObject?
|
||||
do {
|
||||
try absurl.getResourceValue(&isdirv, forKey: NSURLIsDirectoryKey)
|
||||
try (absurl as NSURL).getResourceValue(&isdirv, forKey: URLResourceKey.isDirectoryKey)
|
||||
} catch _ {
|
||||
}
|
||||
if !(isdirv?.boolValue ?? false) {
|
||||
@@ -327,153 +328,147 @@ public class LocalFileProvider: FileProvider, FileProviderMonitor {
|
||||
monitors.append(monitor)
|
||||
}
|
||||
|
||||
public func unregisterNotifcation(path: String) {
|
||||
open func unregisterNotifcation(path: String) {
|
||||
var removedMonitor: LocalFolderMonitor?
|
||||
for (i, monitor) in monitors.enumerate() {
|
||||
for (i, monitor) in monitors.enumerated() {
|
||||
if self.relativePathOf(url: monitor.url) == path {
|
||||
removedMonitor = monitors.removeAtIndex(i)
|
||||
removedMonitor = monitors.remove(at: i)
|
||||
break
|
||||
}
|
||||
}
|
||||
removedMonitor?.stop()
|
||||
}
|
||||
|
||||
public func isRegisteredForNotification(path: String) -> Bool {
|
||||
open func isRegisteredForNotification(path: String) -> Bool {
|
||||
return monitors.map( { self.relativePathOf(url: $0.url) } ).contains(path)
|
||||
}
|
||||
}
|
||||
|
||||
public extension LocalFileProvider {
|
||||
public func createSymbolicLinkAtPath(path: String, withDestinationPath destPath: String, completionHandler: SimpleCompletionHandler) {
|
||||
dispatch_async(operation_queue) {
|
||||
public func create(symbolicLink path: String, withDestinationPath destPath: String, completionHandler: SimpleCompletionHandler) {
|
||||
operation_queue.async {
|
||||
do {
|
||||
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))
|
||||
try self.opFileManager.createSymbolicLink(at: self.absoluteURL(path), withDestinationURL: self.absoluteURL(destPath))
|
||||
completionHandler?(nil)
|
||||
DispatchQueue.main.async(execute: {
|
||||
self.delegate?.fileproviderSucceed(self, operation: .link(link: path, target: destPath))
|
||||
})
|
||||
} catch let e as NSError {
|
||||
completionHandler?(error: e)
|
||||
dispatch_async(dispatch_get_main_queue(), {
|
||||
self.delegate?.fileproviderFailed(self, operation: .Link(link: path, target: destPath))
|
||||
completionHandler?(e)
|
||||
DispatchQueue.main.async(execute: {
|
||||
self.delegate?.fileproviderFailed(self, operation: .link(link: path, target: destPath))
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
internal class LocalFileProviderManagerDelegate: NSObject, NSFileManagerDelegate {
|
||||
internal class LocalFileProviderManagerDelegate: NSObject, FileManagerDelegate {
|
||||
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 {
|
||||
func fileManager(_ fileManager: FileManager, shouldCopyItemAt srcURL: URL, to dstURL: URL) -> Bool {
|
||||
guard let provider = self.provider, let 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))
|
||||
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 {
|
||||
func fileManager(_ fileManager: FileManager, shouldMoveItemAt srcURL: URL, to dstURL: URL) -> Bool {
|
||||
guard let provider = self.provider, let 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))
|
||||
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 {
|
||||
func fileManager(_ fileManager: FileManager, shouldRemoveItemAt URL: URL) -> Bool {
|
||||
guard let provider = self.provider, let delegate = provider.fileOperationDelegate else {
|
||||
return true
|
||||
}
|
||||
let path = provider.relativePathOf(url: URL)
|
||||
return delegate.fileProvider(provider, shouldDoOperation: .Remove(path: path))
|
||||
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 {
|
||||
func fileManager(_ fileManager: FileManager, shouldLinkItemAt srcURL: URL, to dstURL: URL) -> Bool {
|
||||
guard let provider = self.provider, let 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))
|
||||
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 {
|
||||
func fileManager(_ fileManager: FileManager, shouldProceedAfterError error: Error, copyingItemAt srcURL: URL, to dstURL: URL) -> Bool {
|
||||
guard let provider = self.provider, let 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))
|
||||
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 {
|
||||
func fileManager(_ fileManager: FileManager, shouldProceedAfterError error: Error, movingItemAt srcURL: URL, to dstURL: URL) -> Bool {
|
||||
guard let provider = self.provider, let 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))
|
||||
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 {
|
||||
func fileManager(_ fileManager: FileManager, shouldProceedAfterError error: Error, removingItemAt URL: URL) -> Bool {
|
||||
guard let provider = self.provider, let delegate = provider.fileOperationDelegate else {
|
||||
return false
|
||||
}
|
||||
let path = provider.relativePathOf(url: URL)
|
||||
return delegate.fileProvider(provider, shouldProceedAfterError: error, operation: .Remove(path: path))
|
||||
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 {
|
||||
func fileManager(_ fileManager: FileManager, shouldProceedAfterError error: Error, linkingItemAt srcURL: URL, to dstURL: URL) -> Bool {
|
||||
guard let provider = self.provider, let 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))
|
||||
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_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0)
|
||||
private var state: Bool = false
|
||||
private var monitoredTime: NSTimeInterval = NSDate().timeIntervalSinceReferenceDate
|
||||
var url: NSURL
|
||||
fileprivate let source: DispatchSourceFileSystemObject
|
||||
fileprivate let descriptor: CInt
|
||||
fileprivate let qq: DispatchQueue = DispatchQueue.global(qos: DispatchQoS.QoSClass.default)
|
||||
fileprivate var state: Bool = false
|
||||
fileprivate var monitoredTime: TimeInterval = Date().timeIntervalSinceReferenceDate
|
||||
var url: URL
|
||||
|
||||
/// Creates a folder monitor object with monitoring enabled.
|
||||
init(url: NSURL, handler: ()->Void) {
|
||||
init(url: URL, handler: @escaping ()->Void) {
|
||||
self.url = url
|
||||
descriptor = open(url.fileSystemRepresentation, O_EVTONLY)
|
||||
|
||||
source = dispatch_source_create(
|
||||
DISPATCH_SOURCE_TYPE_VNODE,
|
||||
UInt(descriptor),
|
||||
DISPATCH_VNODE_WRITE,
|
||||
qq
|
||||
)
|
||||
descriptor = open((url as NSURL).fileSystemRepresentation, O_EVTONLY)
|
||||
source = DispatchSource.makeFileSystemObjectSource(fileDescriptor: descriptor, eventMask: DispatchSource.FileSystemEvent.write, queue: qq)
|
||||
// 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 {
|
||||
if Date().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(), {
|
||||
self.monitoredTime = Date().timeIntervalSinceReferenceDate
|
||||
DispatchQueue.main.asyncAfter(deadline: DispatchTime.now() + 0.25, execute: {
|
||||
handler()
|
||||
})
|
||||
}
|
||||
dispatch_source_set_event_handler(source, main_handler)
|
||||
dispatch_source_set_cancel_handler(source) {
|
||||
source.setEventHandler(handler: main_handler)
|
||||
source.setCancelHandler {
|
||||
close(self.descriptor)
|
||||
}
|
||||
start()
|
||||
@@ -483,7 +478,7 @@ internal class LocalFolderMonitor {
|
||||
func start() {
|
||||
if !state {
|
||||
state = true
|
||||
dispatch_resume(source)
|
||||
source.resume()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -491,11 +486,11 @@ internal class LocalFolderMonitor {
|
||||
func stop() {
|
||||
if state {
|
||||
state = false
|
||||
dispatch_suspend(source)
|
||||
source.suspend()
|
||||
}
|
||||
}
|
||||
|
||||
deinit {
|
||||
dispatch_source_cancel(source)
|
||||
source.cancel()
|
||||
}
|
||||
}
|
||||
|
||||
+60
-83
@@ -8,31 +8,11 @@
|
||||
|
||||
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
|
||||
|
||||
protocol SMBProtocolClientDelegate: class {
|
||||
func receivedSMB2Response(header: SMB2.Header, response: SMBResponse)
|
||||
func receivedSMB2Response(_ header: SMB2.Header, response: SMBResponse)
|
||||
}
|
||||
|
||||
class SMB2ProtocolClient: FPSStreamTask {
|
||||
@@ -43,25 +23,26 @@ class SMB2ProtocolClient: FPSStreamTask {
|
||||
|
||||
func sendNegotiate(completionHandler: SimpleCompletionHandler) -> UInt64 {
|
||||
let mId = messageId()
|
||||
let smbHeader = SMB2.Header(command: .NEGOTIATE, creditRequestResponse: 126, messageId: mId, treeId: 0, sessionId: 0)
|
||||
let smbHeader = SMB2.Header(command: .NEGOTIATE, creditRequestResponse: UInt16(126), messageId: mId, treeId: UInt32(0), sessionId: UInt64(0))
|
||||
let msg = SMB2.NegotiateRequest()
|
||||
let data = createSMB2Message(smbHeader, message: msg)
|
||||
let data = createSMB2Message(header: smbHeader, message: msg)
|
||||
self.writeData(data, timeout: 0, completionHandler: { (e) in
|
||||
completionHandler?(error: e)
|
||||
completionHandler?(e)
|
||||
})
|
||||
return mId
|
||||
}
|
||||
|
||||
func sendSessionSetup(completionHandler: SimpleCompletionHandler) -> UInt64 {
|
||||
let mId = messageId()
|
||||
let smbHeader = SMB2.Header(command: .SESSION_SETUP, creditRequestResponse: sessionId > 0 ? 124 : 125, messageId: mId, treeId: 0, sessionId: sessionId)
|
||||
let credit = UInt16(sessionId > 0 ? 124 : 125)
|
||||
let smbHeader = SMB2.Header(command: SMB2.Command.SESSION_SETUP, creditRequestResponse: credit, messageId: mId, treeId: UInt32(0), sessionId: sessionId)
|
||||
let msg = SMB2.SessionSetupRequest(singing: [])
|
||||
let data = createSMB2Message(smbHeader, message: msg)
|
||||
let data = createSMB2Message(header: smbHeader, message: msg)
|
||||
self.writeData(data, timeout: 0, completionHandler: { (e) in
|
||||
if self.sessionId == 0 {
|
||||
self.readDataOfMinLength(64, maxLength: 65536, timeout: 30, completionHandler: { (data, eof, e2) in
|
||||
self.readData(OfMinLength: 64, maxLength: 65536, timeout: 30, completionHandler: { (data, eof, e2) in
|
||||
// TODO: set session id
|
||||
completionHandler?(error: e2 ?? e)
|
||||
completionHandler?(e2 ?? e)
|
||||
})
|
||||
}
|
||||
})
|
||||
@@ -70,42 +51,43 @@ class SMB2ProtocolClient: FPSStreamTask {
|
||||
|
||||
func sendTreeConnect(completionHandler: SimpleCompletionHandler) -> UInt64 {
|
||||
let req = self.currentRequest ?? self.originalRequest
|
||||
guard let url = req?.URL, let host = url.host else {
|
||||
guard let url = req?.url, let host = url.host else {
|
||||
return 0
|
||||
}
|
||||
let mId = messageId()
|
||||
let smbHeader = SMB2.Header(command: .TREE_CONNECT, creditRequestResponse: 123, messageId: mId, treeId: 0, sessionId: sessionId)
|
||||
var share = ""
|
||||
if let cmp = url.pathComponents where cmp.count > 0 {
|
||||
let cmp = url.pathComponents
|
||||
if cmp.count > 0 {
|
||||
share = cmp[0]
|
||||
}
|
||||
let tcHeader = SMB2.TreeConnectRequest.Header(flags: [])
|
||||
let msg = SMB2.TreeConnectRequest(header: tcHeader, host: host, share: share)
|
||||
let data = createSMB2Message(smbHeader, message: msg!)
|
||||
let data = createSMB2Message(header: smbHeader, message: msg!)
|
||||
self.writeData(data, timeout: 0, completionHandler: { (e) in
|
||||
completionHandler?(error: e)
|
||||
completionHandler?(e)
|
||||
|
||||
})
|
||||
return mId
|
||||
}
|
||||
func sendTreeDisconnect(treeId: UInt32, completionHandler: SimpleCompletionHandler) -> UInt64 {
|
||||
func sendTreeDisconnect(id treeId: UInt32, completionHandler: SimpleCompletionHandler) -> UInt64 {
|
||||
let mId = messageId()
|
||||
let smbHeader = SMB2.Header(command: .TREE_DISCONNECT, creditRequestResponse: 111, messageId: mId, treeId: treeId, sessionId: sessionId)
|
||||
let msg = SMB2.TreeDisconnect()
|
||||
let data = createSMB2Message(smbHeader, message: msg)
|
||||
let data = createSMB2Message(header: smbHeader, message: msg)
|
||||
self.writeData(data, timeout: 0, completionHandler: { (e) in
|
||||
completionHandler?(error: e)
|
||||
completionHandler?(e)
|
||||
})
|
||||
return mId
|
||||
}
|
||||
|
||||
func sendLogoff(treeId: UInt32, completionHandler: SimpleCompletionHandler) -> UInt64 {
|
||||
func sendLogoff(id treeId: UInt32, completionHandler: SimpleCompletionHandler) -> UInt64 {
|
||||
let mId = messageId()
|
||||
let smbHeader = SMB2.Header(command: .LOGOFF, creditRequestResponse: 0, messageId: mId, treeId: 0, sessionId: sessionId)
|
||||
let msg = SMB2.LogOff()
|
||||
let data = createSMB2Message(smbHeader, message: msg)
|
||||
let data = createSMB2Message(header: smbHeader, message: msg)
|
||||
self.writeData(data, timeout: 0, completionHandler: { (e) in
|
||||
completionHandler?(error: e)
|
||||
completionHandler?(e)
|
||||
})
|
||||
return mId
|
||||
}
|
||||
@@ -119,61 +101,60 @@ class SMB2ProtocolClient: FPSStreamTask {
|
||||
|
||||
// MARK: create and analyse messages
|
||||
|
||||
func determineSMBVersion(data: NSData) -> Float {
|
||||
func determineSMBVersion(_ data: Data) -> Float {
|
||||
var smbverChar: Int8 = 0
|
||||
data.getBytes(&smbverChar, length: 1)
|
||||
(data as NSData).getBytes(&smbverChar, length: 1)
|
||||
let version = 0 - smbverChar
|
||||
return Float(version)
|
||||
}
|
||||
|
||||
func digestSMBMessage(data: NSData) throws -> (header: SMB1.Header, blocks: [(params: [UInt16], message: NSData?)]) {
|
||||
guard data.length > 30 else {
|
||||
throw NSURLError.BadServerResponse
|
||||
func digestSMBMessage(_ data: Data) throws -> (header: SMB1.Header, blocks: [(params: [UInt16], message: Data?)]) {
|
||||
guard data.count > 30 else {
|
||||
throw URLError(.badServerResponse)
|
||||
}
|
||||
var buffer = [UInt8](count: data.length, repeatedValue: 0)
|
||||
var buffer = [UInt8](repeating: 0, count: data.count)
|
||||
guard determineSMBVersion(data) == 1 else {
|
||||
throw SMBFileProviderError.IncompatibleHeader
|
||||
throw SMBFileProviderError.incompatibleHeader
|
||||
}
|
||||
let headersize = sizeof(SMB1.Header.self)
|
||||
let headersize = MemoryLayout<SMB1.Header>.size
|
||||
let header: SMB1.Header = decode(data)
|
||||
var blocks = [(params: [UInt16], message: NSData?)]()
|
||||
var blocks = [(params: [UInt16], message: Data?)]()
|
||||
var offset = headersize
|
||||
while offset < data.length {
|
||||
while offset < data.count {
|
||||
let paramWords: [UInt16]
|
||||
let paramWordsCount = Int(buffer[offset])
|
||||
guard data.length > (paramWordsCount * 2 + offset) else {
|
||||
throw SMBFileProviderError.IncorrectParamsLength
|
||||
guard data.count > (paramWordsCount * 2 + offset) else {
|
||||
throw SMBFileProviderError.incorrectParamsLength
|
||||
}
|
||||
offset += sizeof(UInt8)
|
||||
offset += MemoryLayout<UInt8>.size
|
||||
var rawParamWords = [UInt8](buffer[offset..<(offset + paramWordsCount * 2)])
|
||||
let paramData = NSData(bytesNoCopy: &rawParamWords, length: rawParamWords.count)
|
||||
let paramData = Data(bytesNoCopy: UnsafeMutablePointer<UInt8>(&rawParamWords), count: rawParamWords.count, deallocator: .free)
|
||||
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
|
||||
let messageBytesCount = Int(UInt16(buffer[0]) + UInt16(buffer[1]) << 8)
|
||||
offset += MemoryLayout<UInt16>.size
|
||||
guard data.count >= (offset + messageBytesCount) else {
|
||||
throw SMBFileProviderError.incorrectMessageLength
|
||||
}
|
||||
var rawMessage = [UInt8](buffer[offset..<(offset + messageBytesCount)])
|
||||
offset += messageBytesCount
|
||||
let message = NSData(bytes: &rawMessage, length: rawMessage.count)
|
||||
let message = NSData(bytes: &rawMessage, length: rawMessage.count) as Data
|
||||
blocks.append((params: paramWords, message: message))
|
||||
}
|
||||
return (header, blocks)
|
||||
}
|
||||
|
||||
func digestSMB2Message(data: NSData) throws -> (header: SMB2.Header, message: SMBResponse?)? {
|
||||
guard data.length > 65 else {
|
||||
throw NSURLError.BadServerResponse
|
||||
func digestSMB2Message(_ data: Data) throws -> (header: SMB2.Header, message: SMBResponse?)? {
|
||||
guard data.count > 65 else {
|
||||
throw URLError(.badServerResponse)
|
||||
}
|
||||
guard determineSMBVersion(data) == 2 else {
|
||||
throw SMBFileProviderError.IncompatibleHeader
|
||||
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 headersize = MemoryLayout<SMB2.Header>.size
|
||||
let headerData = data.subdata(in: NSRange(location: 0, length: headersize))
|
||||
let messageSize = data.count - headersize
|
||||
let messageData = data.subdata(in: NSRange(location: headersize, length: messageSize))
|
||||
let header: SMB2.Header = decode(headerData)
|
||||
switch header.command {
|
||||
case .NEGOTIATE:
|
||||
@@ -215,37 +196,33 @@ class SMB2ProtocolClient: FPSStreamTask {
|
||||
case .OPLOCK_BREAK:
|
||||
return (header, nil) // FIXME:
|
||||
case .INVALID:
|
||||
throw SMBFileProviderError.InvalidCommand
|
||||
throw SMBFileProviderError.invalidCommand
|
||||
}
|
||||
}
|
||||
|
||||
func createSMBMessage(header: SMB1.Header, blocks: [(params: NSData?, message: NSData?)]) -> NSData {
|
||||
func createSMBMessage(header: SMB1.Header, blocks: [(params: Data?, message: Data?)]) -> Data {
|
||||
var headerv = header
|
||||
let result = NSMutableData(data: encode(&headerv))
|
||||
var result = NSData(data: encode(&headerv)) as Data
|
||||
for block in blocks {
|
||||
var paramWordsCount = UInt8(block.params?.length ?? 0)
|
||||
result.appendBytes(¶mWordsCount, length: sizeofValue(paramWordsCount))
|
||||
var paramWordsCount = UInt8(block.params?.count ?? 0)
|
||||
result.append(¶mWordsCount, count: MemoryLayout.size(ofValue: paramWordsCount))
|
||||
if let params = block.params {
|
||||
result.appendData(params)
|
||||
result.append(params)
|
||||
}
|
||||
var messageLen = UInt16(block.message?.length ?? 0)
|
||||
result.appendBytes(&messageLen, length: sizeofValue(messageLen))
|
||||
var messageLen = UInt16(block.message?.count ?? 0)
|
||||
let b = UnsafeBufferPointer(start: &messageLen, count: MemoryLayout.size(ofValue: messageLen))
|
||||
result.append(b)
|
||||
if let message = block.message {
|
||||
result.appendData(message)
|
||||
result.append(message)
|
||||
}
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
func createSMB2Message(header: SMB2.Header, message: SMBRequest) -> NSData {
|
||||
func createSMB2Message(header: SMB2.Header, message: SMBRequest) -> Data {
|
||||
var headerv = header
|
||||
let result = NSMutableData(data: encode(&headerv))
|
||||
result.appendData(message.data())
|
||||
var result = NSData(data: encode(&headerv)) as Data
|
||||
result.append(message.data() as Data)
|
||||
return result
|
||||
}
|
||||
}
|
||||
|
||||
protocol FileProviderSMBHeader {
|
||||
var protocolID: UInt32 { get }
|
||||
static var protocolConst: UInt32 { get }
|
||||
}
|
||||
@@ -8,108 +8,108 @@
|
||||
|
||||
import Foundation
|
||||
|
||||
public class SMBFileProvider: FileProvider, FileProviderMonitor {
|
||||
public var type: String = "Samba"
|
||||
public var isPathRelative: Bool = true
|
||||
public var baseURL: NSURL?
|
||||
public var currentPath: String = ""
|
||||
public var dispatch_queue: dispatch_queue_t
|
||||
public weak var delegate: FileProviderDelegate?
|
||||
public let credential: NSURLCredential?
|
||||
open class SMBFileProvider: FileProvider, FileProviderMonitor {
|
||||
open static var type: String = "Samba"
|
||||
open var isPathRelative: Bool = true
|
||||
open var baseURL: URL?
|
||||
open var currentPath: String = ""
|
||||
open var dispatch_queue: DispatchQueue
|
||||
open weak var delegate: FileProviderDelegate?
|
||||
open let credential: URLCredential?
|
||||
|
||||
public typealias FileObjectClass = FileObject
|
||||
|
||||
public init? (baseURL: NSURL, credential: NSURLCredential, afterInitialized: SimpleCompletionHandler) {
|
||||
guard baseURL.uw_scheme.lowercaseString == "smb" else {
|
||||
public init? (baseURL: URL, credential: URLCredential, afterInitialized: SimpleCompletionHandler) {
|
||||
guard baseURL.uw_scheme.lowercased() == "smb" else {
|
||||
return nil
|
||||
}
|
||||
self.baseURL = baseURL
|
||||
dispatch_queue = dispatch_queue_create("FileProvider.\(type)", DISPATCH_QUEUE_CONCURRENT)
|
||||
dispatch_queue = DispatchQueue(label: "FileProvider.\(SMBFileProvider.type)", attributes: DispatchQueue.Attributes.concurrent)
|
||||
//let url = baseURL.uw_absoluteString
|
||||
self.credential = credential
|
||||
}
|
||||
|
||||
public func contentsOfDirectoryAtPath(path: String, completionHandler: ((contents: [FileObjectClass], error: ErrorType?) -> Void)) {
|
||||
open func contentsOfDirectory(path: String, completionHandler: @escaping ((_ contents: [FileObjectClass], _ error: Error?) -> Void)) {
|
||||
NotImplemented()
|
||||
dispatch_async(dispatch_queue) {
|
||||
dispatch_queue.async {
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
public func attributesOfItemAtPath(path: String, completionHandler: ((attributes: FileObjectClass?, error: ErrorType?) -> Void)) {
|
||||
open func attributesOfItem(path: String, completionHandler: @escaping ((_ attributes: FileObjectClass?, _ error: Error?) -> Void)) {
|
||||
NotImplemented()
|
||||
}
|
||||
|
||||
public func storageProperties(completionHandler: ((total: Int64, used: Int64) -> Void)) {
|
||||
open func storageProperties(completionHandler: @escaping ((_ total: Int64, _ used: Int64) -> Void)) {
|
||||
NotImplemented()
|
||||
}
|
||||
|
||||
public weak var fileOperationDelegate: FileOperationDelegate?
|
||||
open weak var fileOperationDelegate: FileOperationDelegate?
|
||||
|
||||
public func createFolder(folderName: String, atPath: String, completionHandler: SimpleCompletionHandler) {
|
||||
open func create(folder folderName: String, at atPath: String, completionHandler: SimpleCompletionHandler) {
|
||||
NotImplemented()
|
||||
}
|
||||
|
||||
public func createFile(fileAttribs: FileObject, atPath: String, contents data: NSData?, completionHandler: SimpleCompletionHandler) {
|
||||
open func create(file fileAttribs: FileObject, at atPath: String, contents data: Data?, completionHandler: SimpleCompletionHandler) {
|
||||
NotImplemented()
|
||||
}
|
||||
|
||||
public func moveItemAtPath(path: String, toPath: String, overwrite: Bool = false, completionHandler: SimpleCompletionHandler) {
|
||||
open func moveItem(path: String, to toPath: String, overwrite: Bool = false, completionHandler: SimpleCompletionHandler) {
|
||||
NotImplemented()
|
||||
}
|
||||
|
||||
public func copyItemAtPath(path: String, toPath: String, overwrite: Bool = false, completionHandler: SimpleCompletionHandler) {
|
||||
open func copyItem(path: String, to toPath: String, overwrite: Bool = false, completionHandler: SimpleCompletionHandler) {
|
||||
NotImplemented()
|
||||
}
|
||||
|
||||
public func removeItemAtPath(path: String, completionHandler: SimpleCompletionHandler) {
|
||||
open func removeItem(path: String, completionHandler: SimpleCompletionHandler) {
|
||||
NotImplemented()
|
||||
}
|
||||
|
||||
public func copyLocalFileToPath(localFile: NSURL, toPath: String, completionHandler: SimpleCompletionHandler) {
|
||||
open func copyItem(localFile: URL, to toPath: String, completionHandler: SimpleCompletionHandler) {
|
||||
NotImplemented()
|
||||
}
|
||||
|
||||
public func copyPathToLocalFile(path: String, toLocalURL: NSURL, completionHandler: SimpleCompletionHandler) {
|
||||
open func copyItem(path: String, toLocalURL: URL, completionHandler: SimpleCompletionHandler) {
|
||||
NotImplemented()
|
||||
}
|
||||
|
||||
public func contentsAtPath(path: String, completionHandler: ((contents: NSData?, error: ErrorType?) -> Void)) {
|
||||
open func contents(path: String, completionHandler: @escaping ((_ contents: Data?, _ error: Error?) -> Void)) {
|
||||
NotImplemented()
|
||||
}
|
||||
|
||||
public func contentsAtPath(path: String, offset: Int64, length: Int, completionHandler: ((contents: NSData?, error: ErrorType?) -> Void)) {
|
||||
open func contents(path: String, offset: Int64, length: Int, completionHandler: @escaping ((_ contents: Data?, _ error: Error?) -> Void)) {
|
||||
NotImplemented()
|
||||
}
|
||||
|
||||
public func writeContentsAtPath(path: String, contents data: NSData, atomically: Bool, completionHandler: SimpleCompletionHandler) {
|
||||
open func writeContents(path: String, contents data: Data, atomically: Bool, completionHandler: SimpleCompletionHandler) {
|
||||
NotImplemented()
|
||||
}
|
||||
|
||||
public func searchFilesAtPath(path: String, recursive: Bool, query: String, foundItemHandler: ((FileObjectClass) -> Void)?, completionHandler: ((files: [FileObjectClass], error: ErrorType?) -> Void)) {
|
||||
open func searchFiles(path: String, recursive: Bool, query: String, foundItemHandler:((FileObjectClass) -> Void)?, completionHandler: @escaping ((_ files: [FileObjectClass], _ error: Error?) -> Void)) {
|
||||
NotImplemented()
|
||||
}
|
||||
|
||||
public func registerNotifcation(path: String, eventHandler: (() -> Void)) {
|
||||
open func registerNotifcation(path: String, eventHandler: @escaping (() -> Void)) {
|
||||
NotImplemented()
|
||||
}
|
||||
|
||||
public func unregisterNotifcation(path: String) {
|
||||
open func unregisterNotifcation(path: String) {
|
||||
NotImplemented()
|
||||
}
|
||||
|
||||
public func isRegisteredForNotification(path: String) -> Bool {
|
||||
open func isRegisteredForNotification(path: String) -> Bool {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: basic CIFS interactivity
|
||||
public enum SMBFileProviderError: Int, ErrorType, CustomStringConvertible {
|
||||
case BadHeader
|
||||
case IncompatibleHeader
|
||||
case IncorrectParamsLength
|
||||
case IncorrectMessageLength
|
||||
case InvalidCommand
|
||||
public enum SMBFileProviderError: Int, Error, CustomStringConvertible {
|
||||
case badHeader
|
||||
case incompatibleHeader
|
||||
case incorrectParamsLength
|
||||
case incorrectMessageLength
|
||||
case invalidCommand
|
||||
|
||||
public var description: String {
|
||||
return "SMB message structure is invalid"
|
||||
@@ -117,8 +117,8 @@ public enum SMBFileProviderError: Int, ErrorType, CustomStringConvertible {
|
||||
}
|
||||
|
||||
private extension SMBFileProvider {
|
||||
private func getPID() -> UInt32 {
|
||||
return UInt32(NSProcessInfo.processInfo().processIdentifier)
|
||||
func getPID() -> UInt32 {
|
||||
return UInt32(ProcessInfo.processInfo.processIdentifier)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -14,7 +14,7 @@ struct SMB1 {
|
||||
// header is always \u{ff}SMB
|
||||
let protocolID: UInt32
|
||||
static let protocolConst: UInt32 = 0x424d53ff
|
||||
private var _command: UInt8
|
||||
fileprivate var _command: UInt8
|
||||
var command: Command {
|
||||
get {
|
||||
return Command(rawValue: _command) ?? .INVALID
|
||||
@@ -24,7 +24,7 @@ struct SMB1 {
|
||||
}
|
||||
}
|
||||
// error messages from the server to the client
|
||||
private var _status: (UInt8, UInt8, UInt8, UInt8)
|
||||
fileprivate var _status: (UInt8, UInt8, UInt8, UInt8)
|
||||
var error: (Class: UInt8, code: UInt16) {
|
||||
get {
|
||||
return (_status.0, UInt16(_status.2) + (UInt16(_status.3) << 8))
|
||||
@@ -47,7 +47,7 @@ struct SMB1 {
|
||||
var flags2: Flags2
|
||||
var pidHigh: UInt16
|
||||
// encryption key used for validating messages over connectionless transports
|
||||
private var _securityKey: (UInt16, UInt16)
|
||||
fileprivate var _securityKey: (UInt16, UInt16)
|
||||
var securityKey: UInt32 {
|
||||
get {
|
||||
return UInt32(_securityKey.1) << 16 + UInt32(_securityKey.0)
|
||||
@@ -60,7 +60,7 @@ struct SMB1 {
|
||||
var securityCID: UInt16
|
||||
/// Identifier of the sequence of a message over connectionless transports
|
||||
var securitySequenceNumber: UInt16
|
||||
private var ununsed: UInt16
|
||||
fileprivate var ununsed: UInt16
|
||||
var treeId: UInt16
|
||||
var pidLow: UInt16
|
||||
var userId: UInt16
|
||||
@@ -93,7 +93,7 @@ struct SMB1 {
|
||||
}
|
||||
}
|
||||
|
||||
struct Flags: OptionSetType {
|
||||
struct Flags: OptionSet {
|
||||
let rawValue: UInt8
|
||||
|
||||
init(rawValue: UInt8) {
|
||||
@@ -111,7 +111,7 @@ struct SMB1 {
|
||||
static let REPLY = Flags(rawValue: 0x80)
|
||||
}
|
||||
|
||||
struct Flags2: OptionSetType {
|
||||
struct Flags2: OptionSet {
|
||||
let rawValue: UInt16
|
||||
|
||||
init(rawValue: UInt16) {
|
||||
@@ -214,4 +214,4 @@ struct SMB1 {
|
||||
case WRITE_BULK_DATA = 0xDA
|
||||
case INVALID = 0xFE
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9,11 +9,11 @@
|
||||
import Foundation
|
||||
|
||||
protocol SMBRequest {
|
||||
func data() -> NSData
|
||||
func data() -> Data
|
||||
}
|
||||
|
||||
protocol SMBResponse {
|
||||
init? (data: NSData)
|
||||
init? (data: Data)
|
||||
}
|
||||
|
||||
|
||||
@@ -32,11 +32,11 @@ struct SMBTime {
|
||||
self.time = (Int64(unixTime) + 11644473600) * 10000000
|
||||
}
|
||||
|
||||
init(timeIntervalSince1970: NSTimeInterval) {
|
||||
init(timeIntervalSince1970: TimeInterval) {
|
||||
self.time = Int64((timeIntervalSince1970 + 11644473600) * 10000000)
|
||||
}
|
||||
|
||||
init(date: NSDate) {
|
||||
init(date: Date) {
|
||||
self.init(timeIntervalSince1970: date.timeIntervalSince1970)
|
||||
}
|
||||
|
||||
@@ -44,7 +44,7 @@ struct SMBTime {
|
||||
return UInt(self.time / 10000000 - 11644473600)
|
||||
}
|
||||
|
||||
var date: NSDate {
|
||||
return NSDate(timeIntervalSince1970: Double(self.time) / 10000000 - 11644473600)
|
||||
var date: Date {
|
||||
return Date(timeIntervalSince1970: Double(self.time) / 10000000 - 11644473600)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -22,15 +22,15 @@ extension SMB2 {
|
||||
self.contexts = contexts
|
||||
}
|
||||
|
||||
func data() -> NSData {
|
||||
func data() -> Data {
|
||||
var header = self.header
|
||||
var offset = 0x78 //UInt16(sizeof(SMB2.Header.self) + sizeof(CreateContext.Header.self) - 1)
|
||||
let body = NSMutableData()
|
||||
if let name = self.name, let nameData = name.dataUsingEncoding(NSUTF16StringEncoding) {
|
||||
if let name = self.name, let nameData = name.data(using: String.Encoding.utf16) {
|
||||
header.nameOffset = UInt16(offset)
|
||||
header.nameLength = UInt16(nameData.length)
|
||||
offset += nameData.length
|
||||
body.appendData(nameData)
|
||||
header.nameLength = UInt16(nameData.count)
|
||||
offset += nameData.count
|
||||
body.append(nameData)
|
||||
}
|
||||
if contexts.count > 0 {
|
||||
// TODO: Context CreateRequest implementation, 8 bit allign offset
|
||||
@@ -40,15 +40,15 @@ extension SMB2 {
|
||||
header.contextLength = 0
|
||||
//result.appendData(nameData)
|
||||
}
|
||||
let result = NSMutableData(data: encode(&header))
|
||||
result.appendData(body)
|
||||
var result = NSData(data: encode(&header)) as Data
|
||||
result.append(body as Data)
|
||||
return result
|
||||
}
|
||||
|
||||
struct Header {
|
||||
let size: UInt16
|
||||
private let securityFlags: UInt8
|
||||
private var _requestedOplockLevel: UInt8
|
||||
fileprivate let securityFlags: UInt8
|
||||
fileprivate var _requestedOplockLevel: UInt8
|
||||
var requestedOplockLevel: OplockLevel {
|
||||
get {
|
||||
return OplockLevel(rawValue: _requestedOplockLevel)!
|
||||
@@ -57,7 +57,7 @@ extension SMB2 {
|
||||
_requestedOplockLevel = newValue.rawValue
|
||||
}
|
||||
}
|
||||
private var _impersonationLevel: UInt32
|
||||
fileprivate var _impersonationLevel: UInt32
|
||||
var impersonationLevel: ImpersonationLevel {
|
||||
get {
|
||||
return ImpersonationLevel(rawValue: _impersonationLevel)!
|
||||
@@ -66,12 +66,12 @@ extension SMB2 {
|
||||
_impersonationLevel = newValue.rawValue
|
||||
}
|
||||
}
|
||||
private let flags: UInt64
|
||||
private let reserved: UInt64
|
||||
fileprivate let flags: UInt64
|
||||
fileprivate let reserved: UInt64
|
||||
let access: FileAccessMask
|
||||
let fileAttributes: FileAttributes
|
||||
let shareAccess: ShareAccess
|
||||
private var _desposition: UInt32
|
||||
fileprivate var _desposition: UInt32
|
||||
var desposition: CreateDisposition {
|
||||
get {
|
||||
return CreateDisposition(rawValue: _desposition)!
|
||||
@@ -86,7 +86,7 @@ extension SMB2 {
|
||||
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 = []) {
|
||||
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
|
||||
@@ -105,7 +105,7 @@ extension SMB2 {
|
||||
}
|
||||
}
|
||||
|
||||
struct CreateOptions: OptionSetType {
|
||||
struct CreateOptions: OptionSet {
|
||||
let rawValue: UInt32
|
||||
|
||||
init(rawValue: UInt32) {
|
||||
@@ -125,14 +125,14 @@ extension SMB2 {
|
||||
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)
|
||||
fileprivate static let SYNCHRONOUS_IO_ALERT = CreateOptions(rawValue: 0x00000010)
|
||||
fileprivate static let SYNCHRONOUS_IO_NONALERT = CreateOptions(rawValue: 0x00000020)
|
||||
fileprivate static let COMPLETE_IF_OPLOCKED = CreateOptions(rawValue: 0x00000100)
|
||||
fileprivate static let REMOTE_INSTANCE = CreateOptions(rawValue: 0x00000400)
|
||||
fileprivate static let OPEN_FOR_FREE_SPACE_QUERY = CreateOptions(rawValue: 0x00800000)
|
||||
fileprivate static let OPEN_REQUIRING_OPLOCK = CreateOptions(rawValue: 0x00010000)
|
||||
fileprivate static let DISALLOW_EXCLUSIVE = CreateOptions(rawValue: 0x00020000)
|
||||
fileprivate static let RESERVE_OPFILTER = CreateOptions(rawValue: 0x00100000)
|
||||
}
|
||||
|
||||
enum CreateDisposition: UInt32 {
|
||||
@@ -151,21 +151,21 @@ extension SMB2 {
|
||||
}
|
||||
|
||||
enum ImpersonationLevel: UInt32 {
|
||||
case Anonymous = 0x00000000
|
||||
case Identification = 0x00000001
|
||||
case Impersonation = 0x00000002
|
||||
case Delegate = 0x00000003
|
||||
case anonymous = 0x00000000
|
||||
case identification = 0x00000001
|
||||
case impersonation = 0x00000002
|
||||
case delegate = 0x00000003
|
||||
}
|
||||
}
|
||||
|
||||
struct CreateResponse: SMBResponse {
|
||||
struct Header {
|
||||
let size: UInt16
|
||||
private let _oplockLevel: UInt8
|
||||
fileprivate let _oplockLevel: UInt8
|
||||
var oplockLevel: OplockLevel {
|
||||
return OplockLevel(rawValue: _oplockLevel)!
|
||||
}
|
||||
private let reserved: UInt32
|
||||
fileprivate let reserved: UInt32
|
||||
let creationTime: SMBTime
|
||||
let lastAccessTime: SMBTime
|
||||
let lastWriteTime: SMBTime
|
||||
@@ -173,7 +173,7 @@ extension SMB2 {
|
||||
let allocationSize: UInt64
|
||||
let endOfFile: UInt64
|
||||
let fileAttributes: FileAttributes
|
||||
private let reserved2: UInt32
|
||||
fileprivate let reserved2: UInt32
|
||||
let fileId: FileId
|
||||
let contextsOffset: UInt32
|
||||
let ContextsLength: UInt32
|
||||
@@ -182,27 +182,27 @@ extension SMB2 {
|
||||
let header: CreateResponse.Header
|
||||
let contexts: [CreateContext]
|
||||
|
||||
init? (data: NSData) {
|
||||
guard data.length >= sizeof(CreateResponse.Header.self) else {
|
||||
init? (data: Data) {
|
||||
guard data.count >= MemoryLayout<CreateResponse.Header>.size 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)
|
||||
var contextOffset = Int(self.header.contextsOffset) - MemoryLayout<SMB2.Header>.size
|
||||
while contextOffset > 0 {
|
||||
guard contextOffset < data.length else {
|
||||
guard contextOffset < data.count else {
|
||||
self.contexts = contexts
|
||||
return
|
||||
}
|
||||
let contextDataHeader = data.subdataWithRange(NSRange(location: contextOffset, length: sizeof(CreateContext.Header.self)))
|
||||
let contextDataHeader = data.subdata(in: NSRange(location: contextOffset, length: MemoryLayout<CreateContext.Header>.size))
|
||||
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))
|
||||
let lastContextData = data.subdata(in: NSRange(location: contextOffset, length: lastContextLen))
|
||||
if let newContext = CreateContext(data: lastContextData) {
|
||||
contexts.append(newContext)
|
||||
}
|
||||
contextOffset = Int(lastContextHeader.header.next) - sizeof(SMB2.Header.self)
|
||||
contextOffset = Int(lastContextHeader.header.next) - MemoryLayout<SMB2.Header>.size
|
||||
}
|
||||
}
|
||||
self.contexts = contexts
|
||||
@@ -217,40 +217,40 @@ extension SMB2 {
|
||||
var next: UInt32
|
||||
let nameOffset: UInt16
|
||||
let nameLength: UInt16
|
||||
private let reserved: UInt16
|
||||
fileprivate let reserved: UInt16
|
||||
let dataOffset: UInt16
|
||||
let dataLength: UInt32
|
||||
}
|
||||
|
||||
var header: CreateContext.Header
|
||||
let buffer: NSData
|
||||
let buffer: Data
|
||||
|
||||
init(name: ContextNames, data: NSData) {
|
||||
let nameData = NSMutableData(data: (name.rawValue).dataUsingEncoding(NSUTF16StringEncoding)!)
|
||||
self.header = CreateContext.Header(next: 0, nameOffset: 32, nameLength: UInt16(nameData.length), reserved: 0, dataOffset: UInt16(nameData.length), dataLength: UInt32(data.length))
|
||||
init(name: ContextNames, data: Data) {
|
||||
let nameData = NSData(data: (name.rawValue).data(using: String.Encoding.utf16)!) as Data
|
||||
self.header = CreateContext.Header(next: 0, nameOffset: 32, nameLength: UInt16(nameData.count), reserved: 0, dataOffset: UInt16(nameData.count), dataLength: UInt32(data.count))
|
||||
self.buffer = data
|
||||
}
|
||||
|
||||
init(name: NSUUID, data: NSData) {
|
||||
init(name: UUID, data: Data) {
|
||||
var uuid = uuid_t(0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0)
|
||||
name.getUUIDBytes(&uuid.0)
|
||||
(name as NSUUID).getBytes(&uuid.0)
|
||||
let nameData = NSMutableData(bytes: &uuid, length: 16)
|
||||
self.header = CreateContext.Header(next: 0, nameOffset: 32, nameLength: UInt16(nameData.length), reserved: 0, dataOffset: UInt16(nameData.length), dataLength: UInt32(data.length))
|
||||
self.header = CreateContext.Header(next: 0, nameOffset: 32, nameLength: UInt16(nameData.length), reserved: 0, dataOffset: UInt16(nameData.length), dataLength: UInt32(data.count))
|
||||
self.buffer = data
|
||||
}
|
||||
|
||||
init? (data: NSData) {
|
||||
let headersize = sizeof(Header)
|
||||
guard data.length > headersize else {
|
||||
init? (data: Data) {
|
||||
let headersize = MemoryLayout<Header>.size
|
||||
guard data.count > headersize else {
|
||||
return nil
|
||||
}
|
||||
self.header = decode(data)
|
||||
self.buffer = data.subdataWithRange(NSRange(location: headersize, length: data.length - headersize))
|
||||
self.buffer = data.subdata(in: NSRange(location: headersize, length: data.count - headersize))
|
||||
}
|
||||
|
||||
func data() -> NSData {
|
||||
let result = NSMutableData(data: encode(header))
|
||||
result.appendData(buffer)
|
||||
func data() -> Data {
|
||||
var result = NSData(data: encode(header)) as Data
|
||||
result.append(buffer)
|
||||
return result
|
||||
}
|
||||
|
||||
@@ -284,7 +284,7 @@ extension SMB2 {
|
||||
case LEASE = 0xFF
|
||||
}
|
||||
|
||||
struct ShareAccess: OptionSetType {
|
||||
struct ShareAccess: OptionSet {
|
||||
let rawValue: UInt32
|
||||
|
||||
init(rawValue: UInt32) {
|
||||
@@ -296,7 +296,7 @@ extension SMB2 {
|
||||
static let DELETE = ShareAccess(rawValue: 0x00000004)
|
||||
}
|
||||
|
||||
struct FileAccessMask: OptionSetType {
|
||||
struct FileAccessMask: OptionSet {
|
||||
let rawValue: UInt32
|
||||
|
||||
init(rawValue: UInt32) {
|
||||
@@ -332,7 +332,7 @@ extension SMB2 {
|
||||
static let GENERIC_READ = FileAccessMask(rawValue: 0x80000000)
|
||||
}
|
||||
|
||||
struct FileAttributes: OptionSetType {
|
||||
struct FileAttributes: OptionSet {
|
||||
let rawValue: UInt32
|
||||
|
||||
init(rawValue: UInt32) {
|
||||
@@ -366,7 +366,7 @@ extension SMB2 {
|
||||
struct CloseRequest: SMBRequest {
|
||||
let size: UInt16
|
||||
let flags: CloseFlags
|
||||
private let reserved2: UInt32
|
||||
fileprivate let reserved2: UInt32
|
||||
let filePersistantId: UInt64
|
||||
let fileVolatileId: UInt64
|
||||
|
||||
@@ -378,7 +378,7 @@ extension SMB2 {
|
||||
self.reserved2 = 0
|
||||
}
|
||||
|
||||
func data() -> NSData {
|
||||
func data() -> Data {
|
||||
return encode(self)
|
||||
}
|
||||
}
|
||||
@@ -386,7 +386,7 @@ extension SMB2 {
|
||||
struct CloseResponse: SMBResponse {
|
||||
let size: UInt16
|
||||
let flags: CloseFlags
|
||||
private let reserved: UInt32
|
||||
fileprivate let reserved: UInt32
|
||||
let creationTime: SMBTime
|
||||
let lastAccessTime: SMBTime
|
||||
let lastWriteTime: SMBTime
|
||||
@@ -395,12 +395,12 @@ extension SMB2 {
|
||||
let endOfFile: UInt64
|
||||
let fileAttributes: FileAttributes
|
||||
|
||||
init? (data: NSData) {
|
||||
init? (data: Data) {
|
||||
self = decode(data)
|
||||
}
|
||||
}
|
||||
|
||||
struct CloseFlags: OptionSetType {
|
||||
struct CloseFlags: OptionSet {
|
||||
let rawValue: UInt16
|
||||
|
||||
init(rawValue: UInt16) {
|
||||
@@ -414,8 +414,8 @@ extension SMB2 {
|
||||
|
||||
struct FlushRequest: SMBRequest {
|
||||
let size: UInt16
|
||||
private let reserved: UInt16
|
||||
private let reserved2: UInt32
|
||||
fileprivate let reserved: UInt16
|
||||
fileprivate let reserved2: UInt32
|
||||
let filePersistantId: UInt64
|
||||
let fileVolatileId: UInt64
|
||||
|
||||
@@ -427,7 +427,7 @@ extension SMB2 {
|
||||
self.reserved2 = 0
|
||||
}
|
||||
|
||||
func data() -> NSData {
|
||||
func data() -> Data {
|
||||
return encode(self)
|
||||
}
|
||||
}
|
||||
@@ -441,8 +441,8 @@ extension SMB2 {
|
||||
self.reserved = 0
|
||||
}
|
||||
|
||||
init? (data: NSData) {
|
||||
init? (data: Data) {
|
||||
self = decode(data)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -13,20 +13,20 @@ extension SMB2 {
|
||||
|
||||
struct ReadRequest: SMBRequest {
|
||||
let size: UInt16
|
||||
private let padding: UInt8
|
||||
fileprivate let padding: UInt8
|
||||
let flags: ReadRequest.Flags
|
||||
let length: UInt32
|
||||
let offset: UInt64
|
||||
let fileId: FileId
|
||||
let minimumLength: UInt32
|
||||
private let _channel: UInt32
|
||||
fileprivate 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
|
||||
fileprivate let channelInfoOffset: UInt16
|
||||
fileprivate let channelInfoLength: UInt16
|
||||
fileprivate 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
|
||||
@@ -43,11 +43,11 @@ extension SMB2 {
|
||||
self.channelBuffer = 0
|
||||
}
|
||||
|
||||
func data() -> NSData {
|
||||
func data() -> Data {
|
||||
return encode(read)
|
||||
}
|
||||
|
||||
struct Flags: OptionSetType {
|
||||
struct Flags: OptionSet {
|
||||
let rawValue: UInt8
|
||||
|
||||
init(rawValue: UInt8) {
|
||||
@@ -62,22 +62,22 @@ extension SMB2 {
|
||||
struct Header {
|
||||
let size: UInt16
|
||||
let offset: UInt8
|
||||
private let reserved: UInt8
|
||||
fileprivate let reserved: UInt8
|
||||
let length: UInt32
|
||||
let remaining: UInt32
|
||||
private let reserved2: UInt32
|
||||
fileprivate let reserved2: UInt32
|
||||
|
||||
}
|
||||
let header: ReadRespone.Header
|
||||
let buffer: NSData
|
||||
let buffer: Data
|
||||
|
||||
init?(data: NSData) {
|
||||
guard data.length > 16 else {
|
||||
init?(data: Data) {
|
||||
guard data.count > 16 else {
|
||||
return nil
|
||||
}
|
||||
self.header = decode(data)
|
||||
let headersize = sizeof(Header)
|
||||
self.buffer = data.subdataWithRange(NSRange(location: headersize, length: data.length - headersize))
|
||||
let headersize = MemoryLayout<Header>.size
|
||||
self.buffer = data.subdata(in: NSRange(location: headersize, length: data.count - headersize))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -92,7 +92,7 @@ extension SMB2 {
|
||||
struct WriteRequest: SMBRequest {
|
||||
let header: WriteRequest.Header
|
||||
let channelInfo: ChannelInfo?
|
||||
let fileData: NSData
|
||||
let fileData: Data
|
||||
|
||||
struct Header {
|
||||
let size: UInt16
|
||||
@@ -100,7 +100,7 @@ extension SMB2 {
|
||||
let length: UInt32
|
||||
let offset: UInt64
|
||||
let fileId: FileId
|
||||
private let _channel: UInt32
|
||||
fileprivate let _channel: UInt32
|
||||
var channel: Channel {
|
||||
return Channel(rawValue: _channel) ?? .NONE
|
||||
}
|
||||
@@ -110,29 +110,29 @@ extension SMB2 {
|
||||
let flags: WriteRequest.Flags
|
||||
}
|
||||
|
||||
init(fileId: FileId, offset: UInt64, remainingBytes: UInt32 = 0, data: NSData, channel: Channel = .NONE, channelInfo: ChannelInfo? = nil, flags: WriteRequest.Flags = []) {
|
||||
init(fileId: FileId, offset: UInt64, remainingBytes: UInt32 = 0, data: Data, 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))
|
||||
if channel != .NONE, let _ = channelInfo {
|
||||
channelInfoOffset = UInt16(MemoryLayout<SMB2.Header>.size + MemoryLayout<WriteRequest.Header>.size)
|
||||
channelInfoLength = UInt16(MemoryLayout<SMB2.ChannelInfo>.size)
|
||||
}
|
||||
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)
|
||||
let dataOffset = UInt16(MemoryLayout<SMB2.Header>.size + MemoryLayout<WriteRequest.Header>.size) + channelInfoLength
|
||||
self.header = WriteRequest.Header(size: UInt16(49), dataOffset: dataOffset, length: UInt32(data.count), 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))
|
||||
func data() -> Data {
|
||||
var result = NSData(data: encode(self.header)) as Data
|
||||
if let channelInfo = channelInfo {
|
||||
result.appendData(channelInfo.data())
|
||||
result.append(channelInfo.data())
|
||||
}
|
||||
result.appendData(fileData)
|
||||
result.append(fileData)
|
||||
return result
|
||||
}
|
||||
|
||||
struct Flags: OptionSetType {
|
||||
struct Flags: OptionSet {
|
||||
let rawValue: UInt32
|
||||
|
||||
init(rawValue: UInt32) {
|
||||
@@ -146,13 +146,13 @@ extension SMB2 {
|
||||
|
||||
struct WriteResponse: SMBResponse {
|
||||
let size: UInt16
|
||||
private let reserved: UInt16
|
||||
fileprivate let reserved: UInt16
|
||||
let writtenBytes: UInt32
|
||||
private let remaining: UInt32
|
||||
private let channelInfoOffset: UInt16
|
||||
private let channelInfoLength: UInt16
|
||||
fileprivate let remaining: UInt32
|
||||
fileprivate let channelInfoOffset: UInt16
|
||||
fileprivate let channelInfoLength: UInt16
|
||||
|
||||
init?(data: NSData) {
|
||||
init?(data: Data) {
|
||||
self = decode(data)
|
||||
}
|
||||
}
|
||||
@@ -162,7 +162,7 @@ extension SMB2 {
|
||||
let token: UInt32
|
||||
let length: UInt32
|
||||
|
||||
func data() -> NSData {
|
||||
func data() -> Data {
|
||||
return encode(data)
|
||||
}
|
||||
}
|
||||
@@ -173,13 +173,13 @@ extension SMB2 {
|
||||
let offset: UInt64
|
||||
let length: UInt64
|
||||
let flags: LockElement.Flags
|
||||
private let reserved: UInt32
|
||||
fileprivate let reserved: UInt32
|
||||
|
||||
func data() -> NSData {
|
||||
func data() -> Data {
|
||||
return encode(self)
|
||||
}
|
||||
|
||||
struct Flags: OptionSetType {
|
||||
struct Flags: OptionSet {
|
||||
let rawValue: UInt32
|
||||
|
||||
init(rawValue: UInt32) {
|
||||
@@ -202,17 +202,17 @@ extension SMB2 {
|
||||
self.locks = locks
|
||||
}
|
||||
|
||||
func data() -> NSData {
|
||||
let result = NSMutableData(data: encode(header))
|
||||
func data() -> Data {
|
||||
var result = NSData(data: encode(header)) as Data
|
||||
for lock in locks {
|
||||
result.appendData(encode(lock))
|
||||
result.append(encode(lock))
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
struct Header {
|
||||
let size: UInt16
|
||||
private let lockCount: UInt16
|
||||
fileprivate let lockCount: UInt16
|
||||
let lockSequence: UInt32
|
||||
let fileId : FileId
|
||||
}
|
||||
@@ -227,7 +227,7 @@ extension SMB2 {
|
||||
self.reserved = 0
|
||||
}
|
||||
|
||||
init? (data: NSData) {
|
||||
init? (data: Data) {
|
||||
self = decode(data)
|
||||
}
|
||||
}
|
||||
@@ -243,8 +243,8 @@ extension SMB2 {
|
||||
self.reserved = 0
|
||||
}
|
||||
|
||||
func data() -> NSData {
|
||||
func data() -> Data {
|
||||
return encode(self)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -20,23 +20,23 @@ extension SMB2 {
|
||||
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)
|
||||
let offset = requestData != nil ? UInt32(MemoryLayout<SMB2.Header>.size + MemoryLayout<IOCtlRequest.Header>.size) : 0
|
||||
self.header = Header(size: 57, reserved: 0, _ctlCode: ctlCode.rawValue, fileId: fileId, inputOffset: offset, inputCount: UInt32((requestData?.data().count ?? 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))
|
||||
func data() -> Data {
|
||||
var result = NSData(data: encode(self.header)) as Data
|
||||
if let reqData = requestData?.data() {
|
||||
result.appendData(reqData)
|
||||
result.append(reqData)
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
struct Header {
|
||||
let size: UInt16
|
||||
private let reserved: UInt16
|
||||
private let _ctlCode: UInt32
|
||||
fileprivate let reserved: UInt16
|
||||
fileprivate let _ctlCode: UInt32
|
||||
var ctlCode: IOCtlCode {
|
||||
return IOCtlCode(rawValue: _ctlCode)!
|
||||
}
|
||||
@@ -48,10 +48,10 @@ extension SMB2 {
|
||||
let outputCount: UInt32
|
||||
let maxOutputResponse: UInt32
|
||||
let flags: IOCtlRequest.Flags
|
||||
private let reserved2: UInt32
|
||||
fileprivate let reserved2: UInt32
|
||||
}
|
||||
|
||||
struct Flags: OptionSetType {
|
||||
struct Flags: OptionSet {
|
||||
let rawValue: UInt32
|
||||
|
||||
init(rawValue: UInt32) {
|
||||
@@ -67,10 +67,10 @@ extension SMB2 {
|
||||
let header: Header
|
||||
let responseData: IOCtlResponseProtocol?
|
||||
|
||||
init?(data: NSData) {
|
||||
init?(data: Data) {
|
||||
self.header = decode(data)
|
||||
let responseRange = NSRange(location: Int(self.header.outputOffset - 64), length: Int(self.header.outputCount))
|
||||
let response = data.subdataWithRange(responseRange)
|
||||
let response = data.subdata(in: responseRange)
|
||||
switch self.header.ctlCode {
|
||||
case .SRV_COPYCHUNK, .SRV_COPYCHUNK_WRITE:
|
||||
self.responseData = IOCtlResponseData.SrvCopyChunk(data: response)
|
||||
@@ -91,8 +91,8 @@ extension SMB2 {
|
||||
|
||||
struct Header {
|
||||
let size: UInt16
|
||||
private let reserved: UInt16
|
||||
private let _ctlCode: UInt32
|
||||
fileprivate let reserved: UInt16
|
||||
fileprivate let _ctlCode: UInt32
|
||||
var ctlCode: IOCtlCode {
|
||||
return IOCtlCode(rawValue: _ctlCode)!
|
||||
}
|
||||
@@ -101,8 +101,8 @@ extension SMB2 {
|
||||
let inputCount: UInt32
|
||||
let outputOffset: UInt32
|
||||
let outputCount: UInt32
|
||||
private let flags: UInt32
|
||||
private let reserved2: UInt32
|
||||
fileprivate let flags: UInt32
|
||||
fileprivate let reserved2: UInt32
|
||||
}
|
||||
}
|
||||
|
||||
@@ -139,21 +139,21 @@ extension SMB2 {
|
||||
let chunkCount: UInt32
|
||||
let chunks: [Chunk]
|
||||
|
||||
func data() -> NSData {
|
||||
let result = NSMutableData(data: encode(sourceKey))
|
||||
result.appendData(encode(chunkCount))
|
||||
func data() -> Data {
|
||||
var result = NSData(data: encode(sourceKey)) as Data
|
||||
result.append(encode(chunkCount))
|
||||
var reserved: UInt32 = 0
|
||||
result.appendData(encode(&reserved))
|
||||
return NSData()
|
||||
result.append(encode(&reserved))
|
||||
return Data()
|
||||
}
|
||||
|
||||
struct Chunk {
|
||||
let sourceOffset: UInt64
|
||||
let targetOffset: UInt64
|
||||
let length: UInt32
|
||||
private let reserved: UInt32
|
||||
fileprivate let reserved: UInt32
|
||||
|
||||
func data() -> NSData {
|
||||
func data() -> Data {
|
||||
return encode(self)
|
||||
}
|
||||
}
|
||||
@@ -183,14 +183,14 @@ extension SMB2 {
|
||||
self.offset = offset
|
||||
}
|
||||
|
||||
func data() -> NSData {
|
||||
func data() -> Data {
|
||||
return encode(self)
|
||||
}
|
||||
}
|
||||
|
||||
struct ResilencyRequest: IOCtlRequestProtocol {
|
||||
let timeout: UInt32
|
||||
private let reserved: UInt32
|
||||
fileprivate 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) {
|
||||
@@ -198,7 +198,7 @@ extension SMB2 {
|
||||
self.reserved = 0
|
||||
}
|
||||
|
||||
func data() -> NSData {
|
||||
func data() -> Data {
|
||||
return encode(self)
|
||||
}
|
||||
}
|
||||
@@ -212,9 +212,9 @@ extension SMB2 {
|
||||
self.dialects = dialects
|
||||
}
|
||||
|
||||
func data() -> NSData {
|
||||
let result = NSMutableData(data: encode(self.header))
|
||||
dialects.forEach { result.appendData(encode($0)) }
|
||||
func data() -> Data {
|
||||
var result = NSData(data: encode(self.header)) as Data
|
||||
dialects.forEach { result.append(encode($0)) }
|
||||
return result
|
||||
}
|
||||
|
||||
@@ -235,7 +235,7 @@ extension SMB2 {
|
||||
let chunksBytesWritten: UInt32
|
||||
let totalBytesWriiten: UInt32
|
||||
|
||||
init?(data: NSData) {
|
||||
init?(data: Data) {
|
||||
self = decode(data)
|
||||
}
|
||||
}
|
||||
@@ -246,20 +246,20 @@ extension SMB2 {
|
||||
let returnedCount: UInt32
|
||||
let snapshots: [SMBTime]
|
||||
|
||||
init?(data: NSData) {
|
||||
init?(data: Data) {
|
||||
self.count = decode(data)
|
||||
self.returnedCount = decode(data.subdataWithRange(NSRange(location: 4, length: 4)))
|
||||
self.returnedCount = decode(data.subdata(in: NSRange(location: 4, length: 4)))
|
||||
//let size: UInt32 = decode(data.subdataWithRange(NSRange(location: 8, length: 4)))
|
||||
var snapshots = [SMBTime]()
|
||||
let dateFormatter = NSDateFormatter()
|
||||
let dateFormatter = DateFormatter()
|
||||
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 {
|
||||
if data.count < 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) {
|
||||
let datestring = String(data: data.subdata(in: NSRange(location: offset, length: 48)), encoding: String.Encoding.utf16)
|
||||
if let datestring = datestring, let date = dateFormatter.date(from: datestring) {
|
||||
snapshots.append(SMBTime(date: date))
|
||||
}
|
||||
}
|
||||
@@ -269,10 +269,10 @@ extension SMB2 {
|
||||
|
||||
struct ResumeKey: IOCtlResponseProtocol {
|
||||
let key: (UInt64, UInt64, UInt64)
|
||||
private let contextLength: UInt32
|
||||
private let context: UInt32
|
||||
fileprivate let contextLength: UInt32
|
||||
fileprivate let context: UInt32
|
||||
|
||||
init?(data: NSData) {
|
||||
init?(data: Data) {
|
||||
self = decode(data)
|
||||
}
|
||||
}
|
||||
@@ -280,7 +280,7 @@ extension SMB2 {
|
||||
struct ReadHash: IOCtlResponseProtocol {
|
||||
// TODO: Implement IOCTL READ_HASH
|
||||
|
||||
init?(data: NSData) {
|
||||
init?(data: Data) {
|
||||
self = decode(data)
|
||||
}
|
||||
}
|
||||
@@ -288,14 +288,14 @@ extension SMB2 {
|
||||
struct NetworkInterfaceInfo: IOCtlResponseProtocol {
|
||||
let items: [NetworkInterfaceInfo.Item]
|
||||
|
||||
init?(data: NSData) {
|
||||
let count = data.length / sizeof(Item)
|
||||
init?(data: Data) {
|
||||
let count = data.count / MemoryLayout<Item>.size
|
||||
guard count > 0 else {
|
||||
return nil
|
||||
}
|
||||
var items = [Item]()
|
||||
for i in 0..<count {
|
||||
let itemdata = data.subdataWithRange(NSRange(location: i * sizeof(Item), length: sizeof(Item)))
|
||||
let itemdata = data.subdata(in: NSRange(location: i * MemoryLayout<Item>.size, length: MemoryLayout<Item>.size))
|
||||
items.append(decode(itemdata))
|
||||
}
|
||||
self.items = items
|
||||
@@ -307,10 +307,10 @@ extension SMB2 {
|
||||
/// specifies the network interface index.
|
||||
let ifIndex: UInt32
|
||||
let capability: IOCtlCapabilities
|
||||
private let reserved: UInt32
|
||||
fileprivate 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,
|
||||
fileprivate let sockaddrStorage: (UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8,
|
||||
UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8,
|
||||
UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8,
|
||||
UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8,
|
||||
@@ -335,13 +335,13 @@ extension SMB2 {
|
||||
|
||||
var sockaddr: sockaddr_in {
|
||||
var sockaddrStorage = self.sockaddrStorage
|
||||
let data = NSData(bytes: &sockaddrStorage, length: 16)
|
||||
let data = Data(bytes: &sockaddrStorage, count: 16)
|
||||
return decode(data)
|
||||
}
|
||||
|
||||
var sockaddr6: sockaddr_in6 {
|
||||
var sockaddrStorage = self.sockaddrStorage
|
||||
let data = NSData(bytes: &sockaddrStorage, length: 28)
|
||||
let data = Data(bytes: &sockaddrStorage, count: 28)
|
||||
return decode(data)
|
||||
}
|
||||
}
|
||||
@@ -351,18 +351,18 @@ extension SMB2 {
|
||||
let capabilities: IOCtlCapabilities
|
||||
let guid: uuid_t
|
||||
let securityMode: UInt16
|
||||
private let _dialect: UInt16
|
||||
fileprivate let _dialect: UInt16
|
||||
var dialect: (major: Int, minor: Int) {
|
||||
return (major: Int(_dialect & 0xFF), minor: Int(_dialect >> 8))
|
||||
}
|
||||
|
||||
init?(data: NSData) {
|
||||
init?(data: Data) {
|
||||
self = decode(data)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
struct IOCtlCapabilities: OptionSetType {
|
||||
struct IOCtlCapabilities: OptionSet {
|
||||
let rawValue: UInt32
|
||||
|
||||
init(rawValue: UInt32) {
|
||||
@@ -386,4 +386,4 @@ extension SMB2 {
|
||||
case HASH_BASED = 0x00000001
|
||||
case FILE_BASED = 0x00000002
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -17,7 +17,7 @@ extension SMB2 {
|
||||
let outputBufferLength: UInt32
|
||||
let fileId: FileId
|
||||
let completionFilters: CompletionFilter
|
||||
private let reserved: UInt32
|
||||
fileprivate let reserved: UInt32
|
||||
|
||||
init(fileId: FileId, completionFilters: CompletionFilter, flags: ChangeNotifyRequest.Flags = [], outputBufferLength: UInt32 = 65535) {
|
||||
self.size = 32
|
||||
@@ -28,11 +28,11 @@ extension SMB2 {
|
||||
self.reserved = 0
|
||||
}
|
||||
|
||||
func data() -> NSData {
|
||||
func data() -> Data {
|
||||
return encode(self)
|
||||
}
|
||||
|
||||
struct Flags: OptionSetType {
|
||||
struct Flags: OptionSet {
|
||||
let rawValue: UInt16
|
||||
|
||||
init(rawValue: UInt16) {
|
||||
@@ -42,7 +42,7 @@ extension SMB2 {
|
||||
static let WATCH_TREE = Flags(rawValue: 0x0001)
|
||||
}
|
||||
|
||||
struct CompletionFilter: OptionSetType {
|
||||
struct CompletionFilter: OptionSet {
|
||||
let rawValue: UInt32
|
||||
|
||||
init(rawValue: UInt32) {
|
||||
@@ -82,25 +82,25 @@ extension SMB2 {
|
||||
struct ChangeNotifyResponse: SMBResponse {
|
||||
let notifications: [(action: FileNotifyAction, fileName: String)]
|
||||
|
||||
init?(data: NSData) {
|
||||
init?(data: Data) {
|
||||
let maxLoop = 1000
|
||||
var i = 0
|
||||
var result = [(action: FileNotifyAction, fileName: String)]()
|
||||
|
||||
var offset: UInt32 = 0
|
||||
while i < maxLoop {
|
||||
let actionData = data.subdataWithRange(NSRange(location: Int(offset + 4), length: 4))
|
||||
let actionData = data.subdata(in: NSRange(location: Int(offset + 4), length: 4))
|
||||
let actionValue: UInt32 = decode(actionData)
|
||||
guard let action = FileNotifyAction(rawValue: actionValue) else {
|
||||
continue
|
||||
}
|
||||
let fileLenData = data.subdataWithRange(NSRange(location: Int(offset + 8), length: 4))
|
||||
let fileLenData = data.subdata(in: NSRange(location: Int(offset + 8), length: 4))
|
||||
let fileNameLen: UInt32 = decode(fileLenData)
|
||||
let fileNameData = data.subdataWithRange(NSRange(location: Int(offset + 12), length: Int(12 + fileNameLen)))
|
||||
let fileName = String(data: fileNameData, encoding: NSUTF16StringEncoding) ?? ""
|
||||
let fileNameData = data.subdata(in: NSRange(location: Int(offset + 12), length: Int(12 + fileNameLen)))
|
||||
let fileName = String(data: fileNameData, encoding: String.Encoding.utf16) ?? ""
|
||||
result.append((action: action, fileName: fileName))
|
||||
|
||||
let nextOffsetData = data.subdataWithRange(NSRange(location: Int(offset), length: 4))
|
||||
let nextOffsetData = data.subdata(in: NSRange(location: Int(offset), length: 4))
|
||||
let nextOffset: UInt32 = decode(nextOffsetData)
|
||||
offset += nextOffset
|
||||
if nextOffset == 0 {
|
||||
@@ -137,4 +137,4 @@ extension SMB2 {
|
||||
/// An attempt to tunnel object ID information to a file being renamed failed because the file already has an object ID. This notification is only sent when the directory being monitored is the special directory "\$Extend\$ObjId:$O:$INDEX_ALLOCATION".
|
||||
case TUNNELLED_ID_COLLISION = 0x0000000B
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -20,17 +20,17 @@ extension SMB2 {
|
||||
/// - **fileIndex:** The byte offset within the directory, indicating the position at which to resume the enumeration.
|
||||
init(fileId: FileId, infoClass: FileInformationEnum, flags: Flags, bufferLength: UInt32 = 65535, searchPattern: String? = nil, fileIndex: UInt32 = 0) {
|
||||
assert(FileInformationEnum.queryDirectory.contains(infoClass), "Invalid FileInformationClass used for QueryDirectoryRequest")
|
||||
let searchPatternOffset = searchPattern != nil ? sizeof(SMB2.Header.self) + sizeof(QueryDirectoryRequest.Header.self) : 0
|
||||
let nflags = flags.intersect(fileIndex > 0 ? [.INDEX_SPECIFIED] : [])
|
||||
let searchPatternLength = searchPattern?.dataUsingEncoding(NSUTF16StringEncoding)?.length ?? 0
|
||||
let searchPatternOffset = searchPattern != nil ? MemoryLayout<SMB2.Header>.size + MemoryLayout<QueryDirectoryRequest.Header>.size : 0
|
||||
let nflags = flags.intersection(fileIndex > 0 ? [.INDEX_SPECIFIED] : [])
|
||||
let searchPatternLength = searchPattern?.data(using: String.Encoding.utf16)?.count ?? 0
|
||||
self.header = Header(size: 53, infoClass: infoClass, flags: nflags, fileIndex: fileIndex, fileId: fileId, searchPatternOffset: UInt8(searchPatternOffset), searchPatternLength: UInt8(searchPatternLength), bufferLength: bufferLength)
|
||||
self.searchPattern = searchPattern
|
||||
}
|
||||
|
||||
func data() -> NSData {
|
||||
let result = NSMutableData(data: encode(header))
|
||||
if let patternData = searchPattern?.dataUsingEncoding(NSUTF16StringEncoding) {
|
||||
result.appendData(patternData)
|
||||
func data() -> Data {
|
||||
var result = NSData(data: encode(header)) as Data
|
||||
if let patternData = searchPattern?.data(using: String.Encoding.utf16) {
|
||||
result.append(patternData)
|
||||
}
|
||||
return result
|
||||
}
|
||||
@@ -46,7 +46,7 @@ extension SMB2 {
|
||||
let bufferLength: UInt32
|
||||
}
|
||||
|
||||
struct Flags: OptionSetType {
|
||||
struct Flags: OptionSet {
|
||||
let rawValue: UInt8
|
||||
|
||||
init(rawValue: UInt8) {
|
||||
@@ -61,43 +61,43 @@ extension SMB2 {
|
||||
}
|
||||
|
||||
struct QueryDirectoryResponse: SMBResponse {
|
||||
let buffer: NSData
|
||||
let buffer: Data
|
||||
|
||||
func parseAs(type type: FileInformationEnum) -> [(header: SMB2FilesInformationHeader, fileName: String)] {
|
||||
func parseAs(type: FileInformationEnum) -> [(header: SMB2FilesInformationHeader, fileName: String)] {
|
||||
var offset = 0
|
||||
var result = [(header: SMB2FilesInformationHeader, fileName: String)]()
|
||||
while true {
|
||||
let header: SMB2FilesInformationHeader
|
||||
switch type {
|
||||
case .FileDirectoryInformation:
|
||||
let headerData = buffer.subdataWithRange(NSRange(location: offset, length: sizeof(FileDirectoryInformationHeader)))
|
||||
case .fileDirectoryInformation:
|
||||
let headerData = buffer.subdata(in: NSRange(location: offset, length: MemoryLayout<FileDirectoryInformationHeader>.size))
|
||||
let h: FileDirectoryInformationHeader = decode(headerData)
|
||||
header = h
|
||||
case .FileFullDirectoryInformation:
|
||||
let headerData = buffer.subdataWithRange(NSRange(location: offset, length: sizeof(FileFullDirectoryInformationHeader)))
|
||||
case .fileFullDirectoryInformation:
|
||||
let headerData = buffer.subdata(in: NSRange(location: offset, length: MemoryLayout<FileFullDirectoryInformationHeader>.size))
|
||||
let h: FileFullDirectoryInformationHeader = decode(headerData)
|
||||
header = h
|
||||
case .FileIdFullDirectoryInformation:
|
||||
let headerData = buffer.subdataWithRange(NSRange(location: offset, length: sizeof(FileIdFullDirectoryInformationHeader)))
|
||||
case .fileIdFullDirectoryInformation:
|
||||
let headerData = buffer.subdata(in: NSRange(location: offset, length: MemoryLayout<FileIdFullDirectoryInformationHeader>.size))
|
||||
let h: FileIdFullDirectoryInformationHeader = decode(headerData)
|
||||
header = h
|
||||
case .FileBothDirectoryInformation:
|
||||
let headerData = buffer.subdataWithRange(NSRange(location: offset, length: sizeof(FileBothDirectoryInformationHeader)))
|
||||
case .fileBothDirectoryInformation:
|
||||
let headerData = buffer.subdata(in: NSRange(location: offset, length: MemoryLayout<FileBothDirectoryInformationHeader>.size))
|
||||
let h: FileBothDirectoryInformationHeader = decode(headerData)
|
||||
header = h
|
||||
case .FileIdBothDirectoryInformation:
|
||||
let headerData = buffer.subdataWithRange(NSRange(location: offset, length: sizeof(FileIdBothDirectoryInformationHeader)))
|
||||
case .fileIdBothDirectoryInformation:
|
||||
let headerData = buffer.subdata(in: NSRange(location: offset, length: MemoryLayout<FileIdBothDirectoryInformationHeader>.size))
|
||||
let h: FileIdBothDirectoryInformationHeader = decode(headerData)
|
||||
header = h
|
||||
case .FileNamesInformation:
|
||||
let headerData = buffer.subdataWithRange(NSRange(location: offset, length: sizeof(FileNamesInformationHeader)))
|
||||
case .fileNamesInformation:
|
||||
let headerData = buffer.subdata(in: NSRange(location: offset, length: MemoryLayout<FileNamesInformationHeader>.size))
|
||||
let h: FileNamesInformationHeader = decode(headerData)
|
||||
header = h
|
||||
default:
|
||||
return []
|
||||
}
|
||||
let fnData = buffer.subdataWithRange(NSRange(location: offset + sizeofValue(header), length: Int(header.fileNameLength)))
|
||||
let fileName = String(data: fnData, usingEncoding: NSUTF16StringEncoding)
|
||||
let fnData = buffer.subdata(in: NSRange(location: offset + MemoryLayout.size(ofValue: header), length: Int(header.fileNameLength)))
|
||||
let fileName = String(data: fnData, encoding: String.Encoding.utf16) ?? ""
|
||||
result.append((header: header, fileName: fileName))
|
||||
if header.nextEntryOffset == 0 {
|
||||
break
|
||||
@@ -107,13 +107,13 @@ extension SMB2 {
|
||||
return result
|
||||
}
|
||||
|
||||
init? (data: NSData) {
|
||||
let offset: UInt16 = decode(data.subdataWithRange(NSRange(location: 2, length: 2)))
|
||||
let length: UInt32 = decode(data.subdataWithRange(NSRange(location: 4, length: 4)))
|
||||
guard data.length > Int(offset) + Int(length) else {
|
||||
init? (data: Data) {
|
||||
let offset: UInt16 = decode(data.subdata(in: NSRange(location: 2, length: 2)))
|
||||
let length: UInt32 = decode(data.subdata(in: NSRange(location: 4, length: 4)))
|
||||
guard data.count > Int(offset) + Int(length) else {
|
||||
return nil
|
||||
}
|
||||
self.buffer = data.subdataWithRange(NSRange(location: Int(offset), length: Int(length)))
|
||||
self.buffer = data.subdata(in: NSRange(location: Int(offset), length: Int(length)))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -121,7 +121,7 @@ extension SMB2 {
|
||||
|
||||
struct QueryInfoRequest: SMBRequest {
|
||||
let header: Header
|
||||
let buffer: NSData?
|
||||
let buffer: Data?
|
||||
|
||||
init(fileId: FileId, infoClass: FileInformationEnum, outputBufferLength: UInt32 = 65535) {
|
||||
self.header = Header(size: 41, infoType: 1, infoClass: infoClass.rawValue, outputBufferLength: outputBufferLength, inputBufferOffset: 0, reserved: 0, inputBufferLength: 0, additionalInformation: [], flags: [], fileId: fileId)
|
||||
@@ -131,21 +131,21 @@ extension SMB2 {
|
||||
init(fileId: FileId, extendedAttributes: [String], flags: Flags = [], outputBufferLength: UInt32 = 65535) {
|
||||
let buffer = NSMutableData()
|
||||
for ea in extendedAttributes {
|
||||
let strData = ea.dataUsingEncoding(NSASCIIStringEncoding)!
|
||||
let strLength = UInt8(strData.length)
|
||||
let nextOffset = UInt32(4 + 1 + strData.length)
|
||||
let data = encode(nextOffset).mutableCopy() as! NSMutableData
|
||||
data.appendData(encode(strLength))
|
||||
data.appendData(strData)
|
||||
let strData = ea.data(using: String.Encoding.ascii)!
|
||||
let strLength = UInt8(strData.count)
|
||||
let nextOffset = UInt32(4 + 1 + strData.count)
|
||||
let data = (encode(nextOffset) as NSData).mutableCopy() as! NSMutableData
|
||||
data.append(encode(strLength))
|
||||
data.append(strData)
|
||||
data.length += 1
|
||||
let padSize = (data.length) % 4
|
||||
data.length += padSize
|
||||
buffer.appendData(data)
|
||||
buffer.append(data as Data)
|
||||
}
|
||||
|
||||
let bufferOffset = UInt16(sizeof(SMB2.Header.self) + sizeof(QueryInfoRequest.Header.self))
|
||||
self.header = Header(size: 41, infoType: 1, infoClass: FileInformationEnum.FileFullEaInformation.rawValue, outputBufferLength: outputBufferLength, inputBufferOffset: bufferOffset, reserved: 0, inputBufferLength: UInt32(buffer.length), additionalInformation: [], flags: flags, fileId: fileId)
|
||||
self.buffer = buffer
|
||||
let bufferOffset = UInt16(MemoryLayout<SMB2.Header>.size + MemoryLayout<QueryInfoRequest.Header>.size)
|
||||
self.header = Header(size: 41, infoType: 1, infoClass: FileInformationEnum.fileFullEaInformation.rawValue, outputBufferLength: outputBufferLength, inputBufferOffset: bufferOffset, reserved: 0, inputBufferLength: UInt32(buffer.length), additionalInformation: [], flags: flags, fileId: fileId)
|
||||
self.buffer = buffer as Data
|
||||
}
|
||||
|
||||
init(fileId: FileId, infoClass: FileSystemInformationEnum, outputBufferLength: UInt32 = 65535) {
|
||||
@@ -160,11 +160,11 @@ extension SMB2 {
|
||||
|
||||
// TODO: Implement QUOTA_INFO init
|
||||
|
||||
func data() -> NSData {
|
||||
func data() -> Data {
|
||||
let headerData = encode(header)
|
||||
let result = NSMutableData(data: headerData)
|
||||
var result = NSData(data: headerData) as Data
|
||||
if let buffer = buffer {
|
||||
result.appendData(buffer)
|
||||
result.append(buffer)
|
||||
}
|
||||
return result
|
||||
}
|
||||
@@ -175,14 +175,14 @@ extension SMB2 {
|
||||
let infoClass: UInt8
|
||||
let outputBufferLength: UInt32
|
||||
let inputBufferOffset: UInt16
|
||||
private let reserved: UInt16
|
||||
fileprivate let reserved: UInt16
|
||||
let inputBufferLength: UInt32
|
||||
let additionalInformation: FileSecurityInfo
|
||||
let flags: QueryInfoRequest.Flags
|
||||
let fileId: FileId
|
||||
}
|
||||
|
||||
struct Flags: OptionSetType {
|
||||
struct Flags: OptionSet {
|
||||
let rawValue: UInt32
|
||||
|
||||
init(rawValue: UInt32) {
|
||||
@@ -196,10 +196,10 @@ extension SMB2 {
|
||||
}
|
||||
|
||||
struct QueryInfoResponse: SMBResponse {
|
||||
let buffer: NSData
|
||||
let buffer: Data
|
||||
|
||||
init?(data: NSData) {
|
||||
let structSizeData = data.subdataWithRange(NSRange(location: 0, length: 2))
|
||||
init?(data: Data) {
|
||||
let structSizeData = data.subdata(in: NSRange(location: 0, length: 2))
|
||||
let structSize: UInt16 = decode(structSizeData)
|
||||
guard structSize == 9 else {
|
||||
return nil
|
||||
@@ -208,14 +208,14 @@ extension SMB2 {
|
||||
/*let offsetData = data.subdataWithRange(NSRange(location: 2, length: 2))
|
||||
let offset: UInt16 = decode(offsetData)*/
|
||||
|
||||
let lengthData = data.subdataWithRange(NSRange(location: 4, length: 4))
|
||||
let lengthData = data.subdata(in: NSRange(location: 4, length: 4))
|
||||
let length: UInt32 = decode(lengthData)
|
||||
|
||||
guard data.length >= 8 + Int(length) else {
|
||||
guard data.count >= 8 + Int(length) else {
|
||||
return nil
|
||||
}
|
||||
|
||||
self.buffer = data.subdataWithRange(NSRange(location: 8, length: Int(length)))
|
||||
self.buffer = data.subdata(in: NSRange(location: 8, length: Int(length)))
|
||||
}
|
||||
|
||||
var asAccessInformation: FileAccessInformation {
|
||||
@@ -228,14 +228,14 @@ extension SMB2 {
|
||||
|
||||
var asAllInformation: (header: FileAllInformationHeader, name: String) {
|
||||
let header: FileAllInformationHeader = decode(buffer)
|
||||
let nameData = buffer.subdataWithRange(NSRange(location: sizeof(FileAllInformationHeader), length: Int(header.nameLength)))
|
||||
let name = String(data: nameData, encoding: NSUTF16StringEncoding) ?? ""
|
||||
let nameData = buffer.subdata(in: NSRange(location: MemoryLayout<FileAllInformationHeader>.size, length: Int(header.nameLength)))
|
||||
let name = String(data: nameData, encoding: String.Encoding.utf16) ?? ""
|
||||
return (header, name)
|
||||
}
|
||||
|
||||
var asAlternateNameInformation: String {
|
||||
let b = UnsafePointer<CChar>(buffer.bytes)
|
||||
return String(CString: b, encoding: NSUTF16StringEncoding) ?? ""
|
||||
let b = (buffer as NSData).bytes.bindMemory(to: CChar.self, capacity: buffer.count)
|
||||
return String(cString: b, encoding: String.Encoding.utf16) ?? ""
|
||||
}
|
||||
|
||||
var asAttributeTagInformation: FileAttributeTagInformation {
|
||||
@@ -293,15 +293,15 @@ extension SMB2 {
|
||||
|
||||
var asStreamInformation: (header: FileStreamInformationHeader, name: String) {
|
||||
let header: FileStreamInformationHeader = decode(buffer)
|
||||
let nameData = buffer.subdataWithRange(NSRange(location: sizeof(FileStreamInformationHeader), length: Int(header.streamNameLength)))
|
||||
let name = String(data: nameData, encoding: NSUTF16StringEncoding) ?? ""
|
||||
let nameData = buffer.subdata(in: NSRange(location: MemoryLayout<FileStreamInformationHeader>.size, length: Int(header.streamNameLength)))
|
||||
let name = String(data: nameData, encoding: String.Encoding.utf16) ?? ""
|
||||
return (header, name)
|
||||
}
|
||||
|
||||
var asFsVolumeInformation: (header: FileFsVolumeInformationHeader, name: String) {
|
||||
let header: FileFsVolumeInformationHeader = decode(buffer)
|
||||
let nameData = buffer.subdataWithRange(NSRange(location: sizeof(FileFsVolumeInformationHeader), length: Int(header.labelLength)))
|
||||
let name = String(data: nameData, encoding: NSUTF16StringEncoding) ?? ""
|
||||
let nameData = buffer.subdata(in: NSRange(location: MemoryLayout<FileFsVolumeInformationHeader>.size, length: Int(header.labelLength)))
|
||||
let name = String(data: nameData, encoding: String.Encoding.utf16) ?? ""
|
||||
return (header, name)
|
||||
}
|
||||
|
||||
@@ -315,8 +315,8 @@ extension SMB2 {
|
||||
|
||||
var asFsAttributeInformation: (header: FileFsAttributeInformationHeader, name: String) {
|
||||
let header: FileFsAttributeInformationHeader = decode(buffer)
|
||||
let nameData = buffer.subdataWithRange(NSRange(location: sizeof(FileFsAttributeInformationHeader), length: Int(header.nameLength)))
|
||||
let name = String(data: nameData, encoding: NSUTF16StringEncoding) ?? ""
|
||||
let nameData = buffer.subdata(in: NSRange(location: MemoryLayout<FileFsAttributeInformationHeader>.size, length: Int(header.nameLength)))
|
||||
let name = String(data: nameData, encoding: String.Encoding.utf16) ?? ""
|
||||
return (header, name)
|
||||
}
|
||||
|
||||
@@ -336,4 +336,4 @@ extension SMB2 {
|
||||
return decode(buffer)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,90 +16,90 @@ protocol SMB2FilesInformationHeader: SMBResponse {
|
||||
|
||||
extension SMB2 {
|
||||
enum FileInformationEnum: UInt8 {
|
||||
case Nil = 0x00
|
||||
case FileDirectoryInformation = 0x01
|
||||
case FileFullDirectoryInformation = 0x02
|
||||
case FileBothDirectoryInformation = 0x03
|
||||
case FileBasicInformation = 0x04
|
||||
case FileStandardInformation = 0x05
|
||||
case FileInternalInformation = 0x06
|
||||
case FileEaInformation = 0x07
|
||||
case FileAccessInformation = 0x08
|
||||
case FileNameInformation = 0x09
|
||||
case FileRenameInformation = 0x0A
|
||||
case FileLinkInformation = 0x0B
|
||||
case FileNamesInformation = 0x0C
|
||||
case FileDispositionInformation = 0x0D
|
||||
case FilePositionInformation = 0x0E
|
||||
case FileFullEaInformation = 0x0F
|
||||
case FileModeInformation = 0x10
|
||||
case FileAlignmentInformation = 0x11
|
||||
case FileAllInformation = 0x12
|
||||
case FileAllocationInformation = 0x13
|
||||
case FileEndOfFileInformation = 0x14
|
||||
case FileAlternateNameInformation = 0x15
|
||||
case FileStreamInformation = 0x16
|
||||
case FilePipeInformation = 0x17
|
||||
case FilePipeLocalInformation = 0x18
|
||||
case FilePipeRemoteInformation = 0x19
|
||||
case FileMailslotQueryInformation = 0x1A
|
||||
case FileMailslotSetInformation = 0x1B
|
||||
case FileCompressionInformation = 0x1C
|
||||
case FileObjectIdInformation = 0x1D
|
||||
case FileCompletionInformation = 0x1E
|
||||
case FileMoveClusterInformation = 0x1F
|
||||
case FileQuotaInformation = 0x20
|
||||
case FileReparsePointInformation = 0x21
|
||||
case FileNetworkOpenInformation = 0x22
|
||||
case FileAttributeTagInformation = 0x23
|
||||
case FileTrackingInformation = 0x24
|
||||
case FileIdBothDirectoryInformation = 0x25
|
||||
case FileIdFullDirectoryInformation = 0x26
|
||||
case FileValidDataLengthInformation = 0x27
|
||||
case FileShortNameInformation = 0x28
|
||||
case FileIoCompletionNotificationInformation = 0x29
|
||||
case FileIoStatusBlockRangeInformation = 0x2A
|
||||
case FileIoPriorityHintInformation = 0x2B
|
||||
case FileSfioReserveInformation = 0x2C
|
||||
case FileSfioVolumeInformation = 0x2D
|
||||
case FileHardLinkInformation = 0x2E
|
||||
case FileProcessIdsUsingFileInformation = 0x2F
|
||||
case FileNormalizedNameInformation = 0x30
|
||||
case FileNetworkPhysicalNameInformation = 0x31
|
||||
case FileIdGlobalTxDirectoryInformation = 0x32
|
||||
case FileIsRemoteDeviceInformation = 0x33
|
||||
case FileUnusedInformation = 0x34
|
||||
case FileNumaNodeInformation = 0x35
|
||||
case FileStandardLinkInformation = 0x36
|
||||
case FileRemoteProtocolInformation = 0x37
|
||||
case FileRenameInformationBypassAccessCheck = 0x38
|
||||
case FileLinkInformationBypassAccessCheck = 0x39
|
||||
case FileVolumeNameInformation = 0x3A
|
||||
case FileIdInformation = 0x3B
|
||||
case FileIdExtdDirectoryInformation = 0x3C
|
||||
case FileReplaceCompletionInformation = 0x3D
|
||||
case FileHardLinkFullIdInformation = 0x3E
|
||||
case FileIdExtdBothDirectoryInformation = 0x3F
|
||||
case FileMaximumInformation = 0x40
|
||||
case `nil` = 0x00
|
||||
case fileDirectoryInformation = 0x01
|
||||
case fileFullDirectoryInformation = 0x02
|
||||
case fileBothDirectoryInformation = 0x03
|
||||
case fileBasicInformation = 0x04
|
||||
case fileStandardInformation = 0x05
|
||||
case fileInternalInformation = 0x06
|
||||
case fileEaInformation = 0x07
|
||||
case fileAccessInformation = 0x08
|
||||
case fileNameInformation = 0x09
|
||||
case fileRenameInformation = 0x0A
|
||||
case fileLinkInformation = 0x0B
|
||||
case fileNamesInformation = 0x0C
|
||||
case fileDispositionInformation = 0x0D
|
||||
case filePositionInformation = 0x0E
|
||||
case fileFullEaInformation = 0x0F
|
||||
case fileModeInformation = 0x10
|
||||
case fileAlignmentInformation = 0x11
|
||||
case fileAllInformation = 0x12
|
||||
case fileAllocationInformation = 0x13
|
||||
case fileEndOfFileInformation = 0x14
|
||||
case fileAlternateNameInformation = 0x15
|
||||
case fileStreamInformation = 0x16
|
||||
case filePipeInformation = 0x17
|
||||
case filePipeLocalInformation = 0x18
|
||||
case filePipeRemoteInformation = 0x19
|
||||
case fileMailslotQueryInformation = 0x1A
|
||||
case fileMailslotSetInformation = 0x1B
|
||||
case fileCompressionInformation = 0x1C
|
||||
case fileObjectIdInformation = 0x1D
|
||||
case fileCompletionInformation = 0x1E
|
||||
case fileMoveClusterInformation = 0x1F
|
||||
case fileQuotaInformation = 0x20
|
||||
case fileReparsePointInformation = 0x21
|
||||
case fileNetworkOpenInformation = 0x22
|
||||
case fileAttributeTagInformation = 0x23
|
||||
case fileTrackingInformation = 0x24
|
||||
case fileIdBothDirectoryInformation = 0x25
|
||||
case fileIdFullDirectoryInformation = 0x26
|
||||
case fileValidDataLengthInformation = 0x27
|
||||
case fileShortNameInformation = 0x28
|
||||
case fileIoCompletionNotificationInformation = 0x29
|
||||
case fileIoStatusBlockRangeInformation = 0x2A
|
||||
case fileIoPriorityHintInformation = 0x2B
|
||||
case fileSfioReserveInformation = 0x2C
|
||||
case fileSfioVolumeInformation = 0x2D
|
||||
case fileHardLinkInformation = 0x2E
|
||||
case fileProcessIdsUsingFileInformation = 0x2F
|
||||
case fileNormalizedNameInformation = 0x30
|
||||
case fileNetworkPhysicalNameInformation = 0x31
|
||||
case fileIdGlobalTxDirectoryInformation = 0x32
|
||||
case fileIsRemoteDeviceInformation = 0x33
|
||||
case fileUnusedInformation = 0x34
|
||||
case fileNumaNodeInformation = 0x35
|
||||
case fileStandardLinkInformation = 0x36
|
||||
case fileRemoteProtocolInformation = 0x37
|
||||
case fileRenameInformationBypassAccessCheck = 0x38
|
||||
case fileLinkInformationBypassAccessCheck = 0x39
|
||||
case fileVolumeNameInformation = 0x3A
|
||||
case fileIdInformation = 0x3B
|
||||
case fileIdExtdDirectoryInformation = 0x3C
|
||||
case fileReplaceCompletionInformation = 0x3D
|
||||
case fileHardLinkFullIdInformation = 0x3E
|
||||
case fileIdExtdBothDirectoryInformation = 0x3F
|
||||
case fileMaximumInformation = 0x40
|
||||
|
||||
static let queryDirectory: [FileInformationEnum] = [.FileDirectoryInformation, .FileFullDirectoryInformation, .FileIdFullDirectoryInformation, .FileBothDirectoryInformation, .FileIdBothDirectoryInformation, .FileNamesInformation]
|
||||
static let queryDirectory: [FileInformationEnum] = [.fileDirectoryInformation, .fileFullDirectoryInformation, .fileIdFullDirectoryInformation, .fileBothDirectoryInformation, .fileIdBothDirectoryInformation, .fileNamesInformation]
|
||||
|
||||
static let queryInfoFile: [FileInformationEnum] = [.FileAccessInformation, .FileAlignmentInformation, .FileAllInformation, .FileAlternateNameInformation, .FileAttributeTagInformation, .FileBasicInformation, .FileCompressionInformation, FileEaInformation, .FileFullEaInformation, .FileInternalInformation, .FileModeInformation, .FileNetworkOpenInformation, .FilePipeInformation, .FilePipeLocalInformation, .FilePipeRemoteInformation, .FilePositionInformation, .FileStandardInformation, .FileStreamInformation]
|
||||
static let queryInfoFile: [FileInformationEnum] = [.fileAccessInformation, .fileAlignmentInformation, .fileAllInformation, .fileAlternateNameInformation, .fileAttributeTagInformation, .fileBasicInformation, .fileCompressionInformation, fileEaInformation, .fileFullEaInformation, .fileInternalInformation, .fileModeInformation, .fileNetworkOpenInformation, .filePipeInformation, .filePipeLocalInformation, .filePipeRemoteInformation, .filePositionInformation, .fileStandardInformation, .fileStreamInformation]
|
||||
}
|
||||
|
||||
enum FileSystemInformationEnum: UInt8 {
|
||||
case Nil = 0
|
||||
case FileFsAttributeInformation
|
||||
case FileFsControlInformation
|
||||
case FileFsDeviceInformation
|
||||
case FileFsFullSizeInformation
|
||||
case FileFsObjectIdInformation
|
||||
case FileFsSectorSizeInformation
|
||||
case FileFsSizeInformation
|
||||
case FileFsVolumeInformation
|
||||
case `nil` = 0
|
||||
case fileFsAttributeInformation
|
||||
case fileFsControlInformation
|
||||
case fileFsDeviceInformation
|
||||
case fileFsFullSizeInformation
|
||||
case fileFsObjectIdInformation
|
||||
case fileFsSectorSizeInformation
|
||||
case fileFsSizeInformation
|
||||
case fileFsVolumeInformation
|
||||
}
|
||||
|
||||
struct FileSecurityInfo: OptionSetType {
|
||||
struct FileSecurityInfo: OptionSet {
|
||||
let rawValue: UInt32
|
||||
|
||||
init(rawValue: UInt32) {
|
||||
@@ -128,7 +128,7 @@ extension SMB2 {
|
||||
let fileAttributes: FileAttributes
|
||||
let fileNameLength : UInt32
|
||||
|
||||
init?(data: NSData) {
|
||||
init?(data: Data) {
|
||||
self = decode(data)
|
||||
}
|
||||
}
|
||||
@@ -146,7 +146,7 @@ extension SMB2 {
|
||||
let fileNameLength : UInt32
|
||||
let extendedAttributesSize: UInt32
|
||||
|
||||
init?(data: NSData) {
|
||||
init?(data: Data) {
|
||||
self = decode(data)
|
||||
}
|
||||
}
|
||||
@@ -163,10 +163,10 @@ extension SMB2 {
|
||||
let fileAttributes: FileAttributes
|
||||
let fileNameLength : UInt32
|
||||
let extendedAttributesSize: UInt32
|
||||
private let reserved: UInt32
|
||||
fileprivate let reserved: UInt32
|
||||
let fileId: FileId
|
||||
|
||||
init?(data: NSData) {
|
||||
init?(data: Data) {
|
||||
self = decode(data)
|
||||
}
|
||||
}
|
||||
@@ -183,17 +183,17 @@ extension SMB2 {
|
||||
let fileAttributes: FileAttributes
|
||||
let fileNameLength : UInt32
|
||||
let extendedAttributesSize: UInt32
|
||||
private let shortNameLen: UInt8
|
||||
private let reserved: UInt8
|
||||
private let _shortName: FileShortNameType
|
||||
fileprivate let shortNameLen: UInt8
|
||||
fileprivate let reserved: UInt8
|
||||
fileprivate let _shortName: FileShortNameType
|
||||
var shortName: String? {
|
||||
let s = encode(_shortName)
|
||||
let d = NSMutableData(data: s)
|
||||
d.length = Int(shortNameLen)
|
||||
return String(data: d, encoding: NSUTF16StringEncoding)
|
||||
var d = NSData(data: s) as Data
|
||||
d.count = Int(shortNameLen)
|
||||
return String(data: d, encoding: String.Encoding.utf16)
|
||||
}
|
||||
|
||||
init?(data: NSData) {
|
||||
init?(data: Data) {
|
||||
self = decode(data)
|
||||
}
|
||||
}
|
||||
@@ -210,19 +210,19 @@ extension SMB2 {
|
||||
let fileAttributes: FileAttributes
|
||||
let fileNameLength : UInt32
|
||||
let extendedAttributesSize: UInt32
|
||||
private let shortNameLen: UInt8
|
||||
private let reserved: UInt8
|
||||
private let _shortName: FileShortNameType
|
||||
fileprivate let shortNameLen: UInt8
|
||||
fileprivate let reserved: UInt8
|
||||
fileprivate let _shortName: FileShortNameType
|
||||
var shortName: String? {
|
||||
let s = encode(_shortName)
|
||||
let d = NSMutableData(data: s)
|
||||
d.length = Int(shortNameLen)
|
||||
return String(data: d, encoding: NSUTF16StringEncoding)
|
||||
var d = NSData(data: s) as Data
|
||||
d.count = Int(shortNameLen)
|
||||
return String(data: d, encoding: String.Encoding.utf16)
|
||||
}
|
||||
private let reserved2: UInt16
|
||||
fileprivate let reserved2: UInt16
|
||||
let fileId : FileId
|
||||
|
||||
init?(data: NSData) {
|
||||
init?(data: Data) {
|
||||
self = decode(data)
|
||||
}
|
||||
}
|
||||
@@ -232,7 +232,7 @@ extension SMB2 {
|
||||
let fileIndex: UInt32
|
||||
let fileNameLength : UInt32
|
||||
|
||||
init?(data: NSData) {
|
||||
init?(data: Data) {
|
||||
self = decode(data)
|
||||
}
|
||||
}
|
||||
@@ -246,7 +246,7 @@ extension SMB2 {
|
||||
}
|
||||
|
||||
struct FileAlignmentInformation {
|
||||
private let _alignment: UInt32
|
||||
fileprivate let _alignment: UInt32
|
||||
var alignmentLength: UInt32 {
|
||||
return _alignment + 1
|
||||
}
|
||||
@@ -275,7 +275,7 @@ extension SMB2 {
|
||||
let lastWriteTime: SMBTime
|
||||
let changeTime: SMBTime
|
||||
let fileAttributes: FileAttributes
|
||||
private let reserved: UInt32 = 0
|
||||
fileprivate let reserved: UInt32 = 0
|
||||
}
|
||||
|
||||
struct FileCompressionInformation {
|
||||
@@ -285,7 +285,7 @@ extension SMB2 {
|
||||
let compressionUnitShift: UInt8
|
||||
let chunkShift: UInt8
|
||||
let clusterShift: UInt8
|
||||
private let reserved: (UInt8, UInt16)
|
||||
fileprivate let reserved: (UInt8, UInt16)
|
||||
}
|
||||
|
||||
struct FileEaInformation {
|
||||
@@ -303,7 +303,7 @@ extension SMB2 {
|
||||
struct FileModeInformation {
|
||||
let mode: Mode
|
||||
|
||||
struct Mode: OptionSetType {
|
||||
struct Mode: OptionSet {
|
||||
let rawValue: UInt32
|
||||
|
||||
init(rawValue: UInt32) {
|
||||
@@ -325,15 +325,15 @@ extension SMB2 {
|
||||
let lastWriteTime: SMBTime
|
||||
let changeTime: SMBTime
|
||||
let fileAttributes: FileAttributes
|
||||
private let reserved: UInt32
|
||||
fileprivate let reserved: UInt32
|
||||
}
|
||||
|
||||
struct FilePipeInformation {
|
||||
private let _readMode: UInt32
|
||||
fileprivate let _readMode: UInt32
|
||||
var readMode: ReadMode {
|
||||
return ReadMode(rawValue: _readMode) ?? .BYTE_STREAM_MODE
|
||||
}
|
||||
private let _completionMode: UInt32
|
||||
fileprivate let _completionMode: UInt32
|
||||
var completionMode: CompletionMode {
|
||||
return CompletionMode(rawValue: _completionMode) ?? .QUEUE_OPERATION
|
||||
}
|
||||
@@ -350,11 +350,11 @@ extension SMB2 {
|
||||
}
|
||||
|
||||
struct FilePipeLocalInformation {
|
||||
private let _namedPipeType: UInt32
|
||||
fileprivate let _namedPipeType: UInt32
|
||||
var namedPipeType: Type {
|
||||
return Type(rawValue: _namedPipeType) ?? .BYTE_STREAM_TYPE
|
||||
}
|
||||
private let _namedPipeConfiguration: UInt32
|
||||
fileprivate let _namedPipeConfiguration: UInt32
|
||||
var namedPipeConfiguration: Configuration {
|
||||
return Configuration(rawValue: _namedPipeConfiguration) ?? .INBOUND
|
||||
}
|
||||
@@ -364,16 +364,16 @@ extension SMB2 {
|
||||
let readDataAvailable: UInt32
|
||||
let outboundQuota: UInt32
|
||||
let writeQuotaAvailable: UInt32
|
||||
private let _namedPipeState: UInt32
|
||||
fileprivate let _namedPipeState: UInt32
|
||||
var namedPipeState: State {
|
||||
return State(rawValue: _namedPipeState) ?? .DISCONNECTED_STATE
|
||||
}
|
||||
private let _namedPipeEnd: UInt32
|
||||
fileprivate let _namedPipeEnd: UInt32
|
||||
var namedPipeEnd: End {
|
||||
return End(rawValue: _namedPipeEnd) ?? .CLIENT_END
|
||||
}
|
||||
|
||||
enum Type: UInt32 {
|
||||
enum `Type`: UInt32 {
|
||||
case BYTE_STREAM_TYPE = 0x00000000
|
||||
case MESSAGE_TYPE = 0x00000001
|
||||
}
|
||||
@@ -412,7 +412,7 @@ extension SMB2 {
|
||||
let numberOfLinks: UInt32
|
||||
let deletePending: Bool
|
||||
let directory: Bool
|
||||
private let reserved: UInt16
|
||||
fileprivate let reserved: UInt16
|
||||
}
|
||||
|
||||
struct FileStreamInformationHeader {
|
||||
@@ -438,7 +438,7 @@ extension SMB2 {
|
||||
}
|
||||
|
||||
struct FileFsDeviceInformation {
|
||||
private let _deviceType: UInt32
|
||||
fileprivate let _deviceType: UInt32
|
||||
var deviceType: DeviceType {
|
||||
return DeviceType(rawValue: _deviceType) ?? .DISK
|
||||
}
|
||||
@@ -449,7 +449,7 @@ extension SMB2 {
|
||||
case DISK = 0x00000007
|
||||
}
|
||||
|
||||
struct Charactristics: OptionSetType {
|
||||
struct Charactristics: OptionSet {
|
||||
let rawValue: UInt32
|
||||
|
||||
init(rawValue: UInt32) {
|
||||
@@ -488,7 +488,7 @@ extension SMB2 {
|
||||
let maximumFileNameLength: Int32
|
||||
let nameLength: UInt32
|
||||
|
||||
struct Attributes: OptionSetType {
|
||||
struct Attributes: OptionSet {
|
||||
let rawValue: UInt32
|
||||
|
||||
init(rawValue: UInt32) {
|
||||
@@ -551,9 +551,9 @@ extension SMB2 {
|
||||
let defaultQuotaThreshold: UInt64
|
||||
let defaultQuotaLimit: UInt64
|
||||
let flags: Flags
|
||||
private let padding: UInt32 = 0
|
||||
fileprivate let padding: UInt32 = 0
|
||||
|
||||
struct Flags: OptionSetType {
|
||||
struct Flags: OptionSet {
|
||||
let rawValue: UInt32
|
||||
|
||||
init(rawValue: UInt32) {
|
||||
@@ -603,7 +603,7 @@ extension SMB2 {
|
||||
let byteOffsetForSectorAlignment: UInt32
|
||||
let byteOffsetForPartitionAlignment: UInt32
|
||||
|
||||
struct Flags: OptionSetType {
|
||||
struct Flags: OptionSet {
|
||||
let rawValue: UInt32
|
||||
|
||||
init(rawValue: UInt32) {
|
||||
@@ -620,4 +620,4 @@ extension SMB2 {
|
||||
static let TRIM_ENABLED = Flags(rawValue: 0x00000010)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -14,44 +14,44 @@ extension SMB2 {
|
||||
struct NegotiateRequest: SMBRequest {
|
||||
let header: NegotiateRequest.Header
|
||||
let dialects: [UInt16]
|
||||
let contexts: [(type: NegotiateContextType, data: NSData)]
|
||||
let contexts: [(type: NegotiateContextType, data: Data)]
|
||||
|
||||
init(header: NegotiateRequest.Header, dialects: [UInt16] = [0x0202], contexts: [(type: NegotiateContextType, data: NSData)] = []) {
|
||||
init(header: NegotiateRequest.Header, dialects: [UInt16] = [0x0202], contexts: [(type: NegotiateContextType, data: Data)] = []) {
|
||||
self.header = header
|
||||
self.dialects = dialects
|
||||
self.contexts = contexts
|
||||
}
|
||||
|
||||
init(dialects: [UInt16] = [0x0202], contexts: [(type: NegotiateContextType, data: NSData)] = [],capabilities: GlobalCapabilities = [], clientStartTime: SMBTime? = nil, guid: uuid_t? = nil, signing: NegotiateSinging = [.ENABLED]) {
|
||||
init(dialects: [UInt16] = [0x0202], contexts: [(type: NegotiateContextType, data: Data)] = [],capabilities: GlobalCapabilities = [], clientStartTime: SMBTime? = nil, guid: uuid_t? = nil, signing: NegotiateSinging = [.ENABLED]) {
|
||||
self.header = Header(capabilities: capabilities, clientStartTime: clientStartTime, guid: guid, signing: signing)
|
||||
self.dialects = dialects
|
||||
self.contexts = contexts
|
||||
}
|
||||
|
||||
func data() -> NSData {
|
||||
func data() -> Data {
|
||||
var header = self.header
|
||||
header.dialectCount = UInt16(dialects.count)
|
||||
let dialectData = NSMutableData()
|
||||
for dialect in dialects {
|
||||
var dialect = dialect
|
||||
dialectData.appendBytes(&dialect, length: 2)
|
||||
dialectData.append(&dialect, length: 2)
|
||||
}
|
||||
let pad = ((1024 - dialectData.length) % 8)
|
||||
dialectData.increaseLengthBy(pad)
|
||||
header.contextOffset = UInt32(sizeof(header.dynamicType.self)) + UInt32(dialectData.length)
|
||||
dialectData.increaseLength(by: pad)
|
||||
header.contextOffset = UInt32(MemoryLayout<NegotiateRequest.Header>.size) + UInt32(dialectData.length)
|
||||
header.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)
|
||||
contextData.append(&contextType, length: 2)
|
||||
var dataLen = UInt16(context.data.count)
|
||||
contextData.increaseLength(by: 4)
|
||||
contextData.append(&dataLen, length: 2)
|
||||
}
|
||||
let result = NSMutableData(data: encode(&header))
|
||||
result.appendData(dialectData)
|
||||
result.appendData(contextData)
|
||||
var result = NSData(data: encode(&header)) as Data
|
||||
result.append(dialectData as Data)
|
||||
result.append(contextData as Data)
|
||||
return result
|
||||
}
|
||||
|
||||
@@ -59,12 +59,12 @@ extension SMB2 {
|
||||
var size: UInt16
|
||||
var dialectCount: UInt16
|
||||
let signing: NegotiateSinging
|
||||
private let reserved: UInt16
|
||||
fileprivate let reserved: UInt16
|
||||
let capabilities: GlobalCapabilities
|
||||
let guid: uuid_t
|
||||
var contextOffset: UInt32
|
||||
var contextCount: UInt16
|
||||
private let reserved2: UInt16
|
||||
fileprivate let reserved2: UInt16
|
||||
var clientStartTime: SMBTime {
|
||||
let time = Int64(contextOffset) + (Int64(contextCount) << 32) + (Int64(contextCount) << 48)
|
||||
return SMBTime(time: time)
|
||||
@@ -93,26 +93,26 @@ extension SMB2 {
|
||||
|
||||
struct NegotiateResponse: SMBResponse {
|
||||
let header: NegotiateResponse.Header
|
||||
let buffer: NSData?
|
||||
let contexts: [(type: NegotiateContextType, data: NSData)]
|
||||
let buffer: Data?
|
||||
let contexts: [(type: NegotiateContextType, data: Data)]
|
||||
|
||||
init? (data: NSData) {
|
||||
if data.length < 64 {
|
||||
init? (data: Data) {
|
||||
if data.count < 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 bufOffset = Int(self.header.bufferOffset) - MemoryLayout<SMB2.Header>.size
|
||||
let bufLen = Int(self.header.bufferLength)
|
||||
if bufOffset > 0 && bufLen > 0 && data.length >= bufOffset + bufLen {
|
||||
self.buffer = data.subdataWithRange(NSRange(location: bufOffset, length: bufLen))
|
||||
if bufOffset > 0 && bufLen > 0 && data.count >= bufOffset + bufLen {
|
||||
self.buffer = data.subdata(in: 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)
|
||||
let contextOffset = Int(self.header.contextOffset) - MemoryLayout<SMB2.Header>.size
|
||||
if contextCount > 0 && contextOffset > 0 {
|
||||
// TODO: NegotiateResponse context support for SMB3
|
||||
self.contexts = []
|
||||
@@ -139,7 +139,7 @@ extension SMB2 {
|
||||
}
|
||||
}
|
||||
|
||||
struct NegotiateSinging: OptionSetType {
|
||||
struct NegotiateSinging: OptionSet {
|
||||
let rawValue: UInt16
|
||||
|
||||
init(rawValue: UInt16) {
|
||||
@@ -149,7 +149,7 @@ extension SMB2 {
|
||||
static let REQUIRED = NegotiateSinging(rawValue: 0x0002)
|
||||
}
|
||||
|
||||
struct NegotiateContextType: OptionSetType {
|
||||
struct NegotiateContextType: OptionSet {
|
||||
let rawValue: UInt16
|
||||
|
||||
init(rawValue: UInt16) {
|
||||
@@ -159,7 +159,7 @@ extension SMB2 {
|
||||
static let ENCRYPTION_CAPABILITIES = NegotiateContextType(rawValue: 0x0002)
|
||||
}
|
||||
|
||||
struct GlobalCapabilities: OptionSetType {
|
||||
struct GlobalCapabilities: OptionSet {
|
||||
let rawValue: UInt32
|
||||
|
||||
init(rawValue: UInt32) {
|
||||
@@ -178,25 +178,25 @@ extension SMB2 {
|
||||
|
||||
struct SessionSetupRequest: SMBRequest {
|
||||
let header: SessionSetupRequest.Header
|
||||
let buffer: NSData?
|
||||
let buffer: Data?
|
||||
|
||||
init(header: SessionSetupRequest.Header, buffer: NSData) {
|
||||
init(header: SessionSetupRequest.Header, buffer: Data) {
|
||||
self.header = header
|
||||
self.buffer = buffer
|
||||
}
|
||||
|
||||
init(sessionId: UInt64 = 0, flags: SessionSetupRequest.Flags = [], singing: SessionSetupSinging = [.ENABLED], capabilities: GlobalCapabilities = [], securityData: NSData? = nil) {
|
||||
init(sessionId: UInt64 = 0, flags: SessionSetupRequest.Flags = [], singing: SessionSetupSinging = [.ENABLED], capabilities: GlobalCapabilities = [], securityData: Data? = nil) {
|
||||
self.header = Header(sessionId: sessionId, flags: flags, singing: singing, capabilities: capabilities)
|
||||
self.buffer = securityData
|
||||
}
|
||||
|
||||
func data() -> NSData {
|
||||
func data() -> Data {
|
||||
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))
|
||||
header.bufferOffset = UInt16(MemoryLayout<SMB2.Header>.size + MemoryLayout<SessionSetupRequest.Header>.size)
|
||||
header.bufferLength = UInt16(buffer?.count ?? 0)
|
||||
var result = NSData(data: encode(&header)) as Data
|
||||
if let buffer = self.buffer {
|
||||
result.appendData(buffer)
|
||||
result.append(buffer)
|
||||
}
|
||||
return result
|
||||
}
|
||||
@@ -206,7 +206,7 @@ extension SMB2 {
|
||||
let flags: SessionSetupRequest.Flags
|
||||
let signing: SessionSetupSinging
|
||||
let capabilities: GlobalCapabilities
|
||||
private let channel: UInt32
|
||||
fileprivate let channel: UInt32
|
||||
var bufferOffset: UInt16
|
||||
var bufferLength: UInt16
|
||||
let sessionId: UInt64
|
||||
@@ -224,7 +224,7 @@ extension SMB2 {
|
||||
}
|
||||
|
||||
/// Works the client implements the SMB 3.x dialect family
|
||||
struct Flags: OptionSetType {
|
||||
struct Flags: OptionSet {
|
||||
let rawValue: UInt8
|
||||
|
||||
init(rawValue: UInt8) {
|
||||
@@ -237,20 +237,20 @@ extension SMB2 {
|
||||
|
||||
struct SessionSetupResponse: SMBResponse {
|
||||
let header: SessionSetupResponse.Header
|
||||
let buffer: NSData?
|
||||
let buffer: Data?
|
||||
|
||||
init? (data: NSData) {
|
||||
if data.length < 64 {
|
||||
init? (data: Data) {
|
||||
if data.count < 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 bufOffset = Int(self.header.bufferOffset) - MemoryLayout<SMB2.Header>.size
|
||||
let bufLen = Int(self.header.bufferLength)
|
||||
if bufOffset > 0 && bufLen > 0 && data.length >= bufOffset + bufLen {
|
||||
self.buffer = data.subdataWithRange(NSRange(location: bufOffset, length: bufLen))
|
||||
if bufOffset > 0 && bufLen > 0 && data.count >= bufOffset + bufLen {
|
||||
self.buffer = data.subdata(in: NSRange(location: bufOffset, length: bufLen))
|
||||
} else {
|
||||
self.buffer = nil
|
||||
}
|
||||
@@ -263,7 +263,7 @@ extension SMB2 {
|
||||
let bufferLength: UInt16
|
||||
}
|
||||
|
||||
struct Flags: OptionSetType {
|
||||
struct Flags: OptionSet {
|
||||
let rawValue: UInt16
|
||||
|
||||
init(rawValue: UInt16) {
|
||||
@@ -276,7 +276,7 @@ extension SMB2 {
|
||||
}
|
||||
}
|
||||
|
||||
struct SessionSetupSinging: OptionSetType {
|
||||
struct SessionSetupSinging: OptionSet {
|
||||
let rawValue: UInt8
|
||||
|
||||
init(rawValue: UInt8) {
|
||||
@@ -298,11 +298,11 @@ extension SMB2 {
|
||||
self.reserved = 0
|
||||
}
|
||||
|
||||
init? (data: NSData) {
|
||||
init? (data: Data) {
|
||||
self = decode(data)
|
||||
}
|
||||
|
||||
func data() -> NSData {
|
||||
func data() -> Data {
|
||||
return encode(self)
|
||||
}
|
||||
}
|
||||
@@ -318,12 +318,12 @@ extension SMB2 {
|
||||
self.reserved = 0
|
||||
}
|
||||
|
||||
init? (data: NSData) {
|
||||
init? (data: Data) {
|
||||
self = decode(data)
|
||||
}
|
||||
|
||||
func data() -> NSData {
|
||||
func data() -> Data {
|
||||
return encode(self)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -12,21 +12,21 @@ extension SMB2 {
|
||||
// MARK: SMB2 Set Info
|
||||
struct SetInfoRequest: SMBRequest {
|
||||
let header: Header
|
||||
let buffer: NSData?
|
||||
let buffer: Data?
|
||||
|
||||
|
||||
|
||||
func data() -> NSData {
|
||||
return NSData()
|
||||
func data() -> Data {
|
||||
return Data()
|
||||
}
|
||||
|
||||
struct Header {
|
||||
let size: UInt16 = 33
|
||||
let infoType: UInt8
|
||||
private let infoClass: UInt8
|
||||
fileprivate let infoClass: UInt8
|
||||
let bufferLength: UInt32
|
||||
let bufferOffset: UInt16
|
||||
private let reserved: UInt16
|
||||
fileprivate let reserved: UInt16
|
||||
let securityInfo: FileSecurityInfo
|
||||
let fileId: FileId
|
||||
}
|
||||
@@ -39,8 +39,8 @@ extension SMB2 {
|
||||
self.size = 2
|
||||
}
|
||||
|
||||
init? (data: NSData) {
|
||||
init? (data: Data) {
|
||||
self = decode(data)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -13,7 +13,7 @@ extension SMB2 {
|
||||
|
||||
struct TreeConnectRequest: SMBRequest {
|
||||
let header: TreeConnectRequest.Header
|
||||
let buffer: NSData?
|
||||
let buffer: Data?
|
||||
var path: String {
|
||||
return ""
|
||||
}
|
||||
@@ -22,21 +22,21 @@ extension SMB2 {
|
||||
}
|
||||
|
||||
init? (header: TreeConnectRequest.Header, host: String, share: String) {
|
||||
guard !host.containsString("/") && !share.containsString("/") else {
|
||||
guard !host.contains("/") && !share.contains("/") else {
|
||||
return nil
|
||||
}
|
||||
self.header = header
|
||||
let path = "\\\\\(host)\\\(share)"
|
||||
self.buffer = path.dataUsingEncoding(NSUTF16StringEncoding)
|
||||
self.buffer = path.data(using: String.Encoding.utf16)
|
||||
}
|
||||
|
||||
func data() -> NSData {
|
||||
func data() -> Data {
|
||||
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))
|
||||
header.pathOffset = UInt16(MemoryLayout<SMB2.Header>.size + MemoryLayout<TreeConnectRequest.Header>.size)
|
||||
header.pathLength = UInt16(buffer?.count ?? 0)
|
||||
var result = NSData(data: encode(&header)) as Data
|
||||
if let buffer = self.buffer {
|
||||
result.appendData(buffer)
|
||||
result.append(buffer)
|
||||
}
|
||||
return result
|
||||
}
|
||||
@@ -55,7 +55,7 @@ extension SMB2 {
|
||||
}
|
||||
}
|
||||
|
||||
struct Flags: OptionSetType {
|
||||
struct Flags: OptionSet {
|
||||
let rawValue: UInt16
|
||||
|
||||
init(rawValue: UInt16) {
|
||||
@@ -68,17 +68,17 @@ extension SMB2 {
|
||||
|
||||
struct TreeConnectResponse: SMBResponse {
|
||||
let size: UInt16 // = 16
|
||||
private let _type: UInt8
|
||||
fileprivate let _type: UInt8
|
||||
var type: ShareType {
|
||||
return ShareType(rawValue: _type) ?? .UNKNOWN
|
||||
}
|
||||
private let reserved: UInt8
|
||||
fileprivate let reserved: UInt8
|
||||
let flags: TreeConnectResponse.ShareFlags
|
||||
let capabilities: TreeConnectResponse.Capabilities
|
||||
let maximalAccess: FileAccessMask
|
||||
|
||||
init? (data: NSData) {
|
||||
if data.length != 16 {
|
||||
init? (data: Data) {
|
||||
if data.count != 16 {
|
||||
return nil
|
||||
}
|
||||
self = decode(data)
|
||||
@@ -91,7 +91,7 @@ extension SMB2 {
|
||||
case PRINT = 0x03
|
||||
}
|
||||
|
||||
struct ShareFlags: OptionSetType {
|
||||
struct ShareFlags: OptionSet {
|
||||
let rawValue: UInt32
|
||||
|
||||
init(rawValue: UInt32) {
|
||||
@@ -114,7 +114,7 @@ extension SMB2 {
|
||||
static let ENCRYPT_DATA = ShareFlags(rawValue: 0x00008000)
|
||||
}
|
||||
|
||||
struct Capabilities: OptionSetType {
|
||||
struct Capabilities: OptionSet {
|
||||
let rawValue: UInt32
|
||||
|
||||
init(rawValue: UInt32) {
|
||||
@@ -140,12 +140,12 @@ extension SMB2 {
|
||||
self.reserved = 0
|
||||
}
|
||||
|
||||
init? (data: NSData) {
|
||||
init? (data: Data) {
|
||||
self = decode(data)
|
||||
}
|
||||
|
||||
func data() -> NSData {
|
||||
func data() -> Data {
|
||||
return encode(self)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,6 +8,31 @@
|
||||
|
||||
import Foundation
|
||||
|
||||
internal func encode<T>(_ value: inout T) -> Data {
|
||||
return withUnsafePointer(to: &value) { p in
|
||||
NSData(bytes: p, length: MemoryLayout.size(ofValue: value)) as Data
|
||||
}
|
||||
}
|
||||
|
||||
internal func encode<T>(_ value: T) -> Data {
|
||||
var value = value
|
||||
return withUnsafePointer(to: &value) { p in
|
||||
NSData(bytes: p, length: MemoryLayout.size(ofValue: value)) as Data
|
||||
}
|
||||
}
|
||||
|
||||
internal func decode<T>(_ data: Data) -> T {
|
||||
let pointer = UnsafeMutablePointer<T>.allocate(capacity: MemoryLayout<T.Type>.size)
|
||||
(data as NSData).getBytes(pointer, length: MemoryLayout<T.Type>.size)
|
||||
|
||||
return pointer.move()
|
||||
}
|
||||
|
||||
protocol FileProviderSMBHeader {
|
||||
var protocolID: UInt32 { get }
|
||||
static var protocolConst: UInt32 { get }
|
||||
}
|
||||
|
||||
// SMB2 Types
|
||||
struct SMB2 {
|
||||
struct Header: FileProviderSMBHeader { // 64 bytes
|
||||
@@ -19,13 +44,13 @@ struct SMB2 {
|
||||
// error messages from the server to the client
|
||||
let status: UInt32
|
||||
enum StatusSeverity: UInt8 {
|
||||
case Success = 0, Information, Warning, Error
|
||||
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
|
||||
fileprivate let _command: UInt16
|
||||
var command: Command {
|
||||
get {
|
||||
return Command(rawValue: _command) ?? .INVALID
|
||||
@@ -35,7 +60,7 @@ struct SMB2 {
|
||||
let flags: Flags
|
||||
var nextCommand: UInt32
|
||||
let messageId: UInt64
|
||||
private let reserved: UInt32
|
||||
fileprivate let reserved: UInt32
|
||||
let treeId: UInt32
|
||||
var asyncId: UInt64 {
|
||||
get {
|
||||
@@ -47,7 +72,7 @@ struct SMB2 {
|
||||
|
||||
// codebeat:disable[ARITY]
|
||||
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.protocolID = type(of: self).protocolConst
|
||||
self.size = 64
|
||||
self.status = status.rawValue
|
||||
self._command = command.rawValue
|
||||
@@ -63,7 +88,7 @@ struct SMB2 {
|
||||
}
|
||||
|
||||
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.protocolID = type(of: self).protocolConst
|
||||
self.size = 64
|
||||
self.status = status.rawValue
|
||||
self._command = asyncCommand.rawValue
|
||||
@@ -80,7 +105,7 @@ struct SMB2 {
|
||||
// codebeat:enable[ARITY]
|
||||
}
|
||||
|
||||
struct Flags: OptionSetType {
|
||||
struct Flags: OptionSet {
|
||||
var rawValue: UInt32
|
||||
|
||||
init(rawValue: UInt32) {
|
||||
@@ -100,7 +125,7 @@ struct SMB2 {
|
||||
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)
|
||||
fileprivate static let PRIORITY_MASK = Flags(rawValue: 0x00000070)
|
||||
static let DFS_OPERATIONS = Flags(rawValue: 0x10000000)
|
||||
static let REPLAY_OPERATION = Flags(rawValue: 0x20000000)
|
||||
}
|
||||
@@ -131,3 +156,9 @@ struct SMB2 {
|
||||
// MARK: SMB2 Oplock Break
|
||||
|
||||
}
|
||||
|
||||
extension Data {
|
||||
func subdata(in range: NSRange) -> Data {
|
||||
return (self as NSData).subdata(with: range)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -10,7 +10,7 @@ import Foundation
|
||||
|
||||
/// Error Types and Description
|
||||
|
||||
public enum NTStatus: UInt32, ErrorType, CustomStringConvertible {
|
||||
public enum NTStatus: UInt32, Error, CustomStringConvertible {
|
||||
case SUCCESS = 0x00000000
|
||||
case NOT_IMPLEMENTED = 0xC0000002
|
||||
case INVALID_DEVICE_REQUEST = 0xC0000010
|
||||
@@ -133,76 +133,76 @@ public enum NTStatus: UInt32, ErrorType, CustomStringConvertible {
|
||||
|
||||
public var description: String {
|
||||
switch self {
|
||||
case NOT_IMPLEMENTED, INVALID_DEVICE_REQUEST, ILLEGAL_FUNCTION:
|
||||
case .NOT_IMPLEMENTED, .INVALID_DEVICE_REQUEST, .ILLEGAL_FUNCTION:
|
||||
return "Invalid Function."
|
||||
case NO_SUCH_FILE, NO_SUCH_DEVICE, OBJECT_NAME_NOT_FOUND:
|
||||
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:
|
||||
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:
|
||||
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:
|
||||
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:
|
||||
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:
|
||||
case .SECTION_TOO_BIG, .TOO_MANY_PAGING_FILES, .INSUFF_SERVER_RESOURCES:
|
||||
return "Insufficient server memory to perform the requested operation."
|
||||
case OS2_INVALID_ACCESS:
|
||||
case .OS2_INVALID_ACCESS:
|
||||
return "Invalid open mode."
|
||||
case DATA_ERROR:
|
||||
case .DATA_ERROR:
|
||||
return "Bad data. (May be generated by IOCTL calls on the server.)"
|
||||
case DIRECTORY_NOT_EMPTY:
|
||||
case .DIRECTORY_NOT_EMPTY:
|
||||
return "Remove of directory failed because it was not empty."
|
||||
case NOT_SAME_DEVICE:
|
||||
case .NOT_SAME_DEVICE:
|
||||
return "A file system operation (such as a rename) across two devices was attempted."
|
||||
case NO_MORE_FILES:
|
||||
case .NO_MORE_FILES:
|
||||
return "No (more) files found following a file search command."
|
||||
case UNSUCCESSFUL:
|
||||
case .UNSUCCESSFUL:
|
||||
return "General error."
|
||||
case SHARING_VIOLATION:
|
||||
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:
|
||||
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:
|
||||
case .END_OF_FILE:
|
||||
return "Attempted to read beyond the end of the file."
|
||||
case NOT_SUPPORTED:
|
||||
case .NOT_SUPPORTED:
|
||||
return "This command is not supported by the server."
|
||||
case OBJECT_NAME_COLLISION:
|
||||
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:
|
||||
case .INVALID_PARAMETER:
|
||||
return "A parameter supplied with the message is invalid."
|
||||
case OS2_INVALID_LEVEL:
|
||||
case .OS2_INVALID_LEVEL:
|
||||
return "Invalid information level."
|
||||
case OS2_NEGATIVE_SEEK:
|
||||
case .OS2_NEGATIVE_SEEK:
|
||||
return "An attempt was made to seek to a negative absolute offset within a file."
|
||||
case RANGE_NOT_LOCKED:
|
||||
case .RANGE_NOT_LOCKED:
|
||||
return "The byte range specified in an unlock request was not locked."
|
||||
case OS2_NO_MORE_SIDS:
|
||||
case .OS2_NO_MORE_SIDS:
|
||||
return "Maximum number of searches has been exhausted."
|
||||
case OS2_CANCEL_VIOLATION:
|
||||
case .OS2_CANCEL_VIOLATION:
|
||||
return "No lock request was outstanding for the supplied cancel region."
|
||||
case OS2_ATOMIC_LOCKS_NOT_SUPPORTED:
|
||||
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:
|
||||
case .INVALID_INFO_CLASS, .INVALID_PIPE_STATE, .INVALID_READ_MODE:
|
||||
return "Invalid named pipe."
|
||||
case OS2_CANNOT_COPY:
|
||||
case .OS2_CANNOT_COPY:
|
||||
return "The copy functions cannot be used."
|
||||
case INSTANCE_NOT_AVAILABLE, PIPE_NOT_AVAILABLE, PIPE_BUSY:
|
||||
case .INSTANCE_NOT_AVAILABLE, .PIPE_NOT_AVAILABLE, .PIPE_BUSY:
|
||||
return "All instances of the designated named pipe are busy."
|
||||
case PIPE_CLOSING, PIPE_EMPTY:
|
||||
case .PIPE_CLOSING, .PIPE_EMPTY:
|
||||
return "The designated named pipe is in the process of being closed."
|
||||
case PIPE_DISCONNECTED:
|
||||
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:
|
||||
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:
|
||||
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:
|
||||
case .EAS_NOT_SUPPORTED:
|
||||
return "The server file system does not support Extended Attributes."
|
||||
case OS2_EA_ACCESS_DENIED:
|
||||
case .OS2_EA_ACCESS_DENIED:
|
||||
return "Access to the extended attribute was denied."
|
||||
default:
|
||||
return ""
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,28 +8,28 @@
|
||||
|
||||
import Foundation
|
||||
|
||||
class SessionDelegate: NSObject, NSURLSessionDataDelegate, NSURLSessionDownloadDelegate {
|
||||
class SessionDelegate: NSObject, URLSessionDataDelegate, URLSessionDownloadDelegate {
|
||||
|
||||
weak var fileProvider: FileProvider?
|
||||
var credential: NSURLCredential?
|
||||
var credential: URLCredential?
|
||||
|
||||
var finishDownloadHandler: ((session: NSURLSession, downloadTask: NSURLSessionDownloadTask, didFinishDownloadingToURL: NSURL) -> Void)?
|
||||
var didSendDataHandler: ((session: NSURLSession, task: NSURLSessionTask, bytesSent: Int64, totalBytesSent: Int64, totalBytesExpectedToSend: Int64) -> Void)?
|
||||
var didReceivedData: ((session: NSURLSession, downloadTask: NSURLSessionDownloadTask, bytesWritten: Int64, totalBytesWritten: Int64, totalBytesExpectedToWrite: Int64) -> Void)?
|
||||
var finishDownloadHandler: ((_ session: Foundation.URLSession, _ downloadTask: URLSessionDownloadTask, _ didFinishDownloadingToURL: URL) -> Void)?
|
||||
var didSendDataHandler: ((_ session: Foundation.URLSession, _ task: URLSessionTask, _ bytesSent: Int64, _ totalBytesSent: Int64, _ totalBytesExpectedToSend: Int64) -> Void)?
|
||||
var didReceivedData: ((_ session: Foundation.URLSession, _ downloadTask: URLSessionDownloadTask, _ bytesWritten: Int64, _ totalBytesWritten: Int64, _ totalBytesExpectedToWrite: Int64) -> Void)?
|
||||
|
||||
init(fileProvider: FileProvider, credential: NSURLCredential?) {
|
||||
init(fileProvider: FileProvider, credential: URLCredential?) {
|
||||
self.fileProvider = fileProvider
|
||||
self.credential = credential
|
||||
}
|
||||
|
||||
// codebeat:disable[ARITY]
|
||||
func URLSession(session: NSURLSession, downloadTask: NSURLSessionDownloadTask, didFinishDownloadingToURL location: NSURL) {
|
||||
self.finishDownloadHandler?(session: session, downloadTask: downloadTask, didFinishDownloadingToURL: location)
|
||||
func urlSession(_ session: URLSession, downloadTask: URLSessionDownloadTask, didFinishDownloadingTo location: URL) {
|
||||
self.finishDownloadHandler?(session, downloadTask, location)
|
||||
return
|
||||
}
|
||||
|
||||
func URLSession(session: NSURLSession, task: NSURLSessionTask, didSendBodyData bytesSent: Int64, totalBytesSent: Int64, totalBytesExpectedToSend: Int64) {
|
||||
self.didSendDataHandler?(session: session, task: task, bytesSent: bytesSent, totalBytesSent: totalBytesSent, totalBytesExpectedToSend: totalBytesExpectedToSend)
|
||||
func urlSession(_ session: URLSession, task: URLSessionTask, didSendBodyData bytesSent: Int64, totalBytesSent: Int64, totalBytesExpectedToSend: Int64) {
|
||||
self.didSendDataHandler?(session, task, bytesSent, totalBytesSent, totalBytesExpectedToSend)
|
||||
|
||||
guard let desc = task.taskDescription, let json = jsonToDictionary(desc) else {
|
||||
return
|
||||
@@ -41,20 +41,20 @@ class SessionDelegate: NSObject, NSURLSessionDataDelegate, NSURLSessionDownloadD
|
||||
let op : FileOperation
|
||||
switch type {
|
||||
case "Create":
|
||||
op = .Create(path: source)
|
||||
op = .create(path: source)
|
||||
case "Copy":
|
||||
guard let dest = dest else { return }
|
||||
op = .Copy(source: source, destination: dest)
|
||||
op = .copy(source: source, destination: dest)
|
||||
case "Move":
|
||||
guard let dest = dest else { return }
|
||||
op = .Move(source: source, destination: dest)
|
||||
op = .move(source: source, destination: dest)
|
||||
case "Modify":
|
||||
op = .Modify(path: source)
|
||||
op = .modify(path: source)
|
||||
case "Remove":
|
||||
op = .Remove(path: source)
|
||||
op = .remove(path: source)
|
||||
case "Link":
|
||||
guard let dest = dest else { return }
|
||||
op = .Link(link: source, target: dest)
|
||||
op = .link(link: source, target: dest)
|
||||
default:
|
||||
return
|
||||
}
|
||||
@@ -64,95 +64,95 @@ class SessionDelegate: NSObject, NSURLSessionDataDelegate, NSURLSessionDownloadD
|
||||
fileProvider?.delegate?.fileproviderProgress(fileProvider!, operation: op, progress: progress)
|
||||
}
|
||||
|
||||
func URLSession(session: NSURLSession, downloadTask: NSURLSessionDownloadTask, didWriteData bytesWritten: Int64, totalBytesWritten: Int64, totalBytesExpectedToWrite: Int64) {
|
||||
self.didReceivedData?(session: session, downloadTask: downloadTask, bytesWritten: bytesWritten, totalBytesWritten: totalBytesWritten, totalBytesExpectedToWrite: totalBytesExpectedToWrite)
|
||||
func urlSession(_ session: URLSession, downloadTask: URLSessionDownloadTask, didWriteData bytesWritten: Int64, totalBytesWritten: Int64, totalBytesExpectedToWrite: Int64) {
|
||||
self.didReceivedData?(session, downloadTask, bytesWritten, totalBytesWritten, totalBytesExpectedToWrite)
|
||||
|
||||
guard let desc = downloadTask.taskDescription, let json = jsonToDictionary(desc), let source = json["source"] as? String, dest = json["dest"] as? String else {
|
||||
guard let desc = downloadTask.taskDescription, let json = jsonToDictionary(desc), let source = json["source"] as? String, let dest = json["dest"] as? String else {
|
||||
return
|
||||
}
|
||||
|
||||
fileProvider?.delegate?.fileproviderProgress(fileProvider!, operation: .Copy(source: source, destination: dest), progress: Float(totalBytesWritten) / Float(totalBytesExpectedToWrite))
|
||||
fileProvider?.delegate?.fileproviderProgress(fileProvider!, operation: .copy(source: source, destination: dest), progress: Float(totalBytesWritten) / Float(totalBytesExpectedToWrite))
|
||||
}
|
||||
|
||||
func URLSession(session: NSURLSession, task: NSURLSessionTask, didReceiveChallenge challenge: NSURLAuthenticationChallenge, completionHandler: (NSURLSessionAuthChallengeDisposition, NSURLCredential?) -> Void) {
|
||||
let deposition: NSURLSessionAuthChallengeDisposition = credential != nil ? .UseCredential : .PerformDefaultHandling
|
||||
func urlSession(_ session: URLSession, task: URLSessionTask, didReceive challenge: URLAuthenticationChallenge, completionHandler: @escaping (URLSession.AuthChallengeDisposition, URLCredential?) -> Void) {
|
||||
let deposition: Foundation.URLSession.AuthChallengeDisposition = credential != nil ? .useCredential : .performDefaultHandling
|
||||
completionHandler(deposition, credential)
|
||||
}
|
||||
|
||||
func URLSession(session: NSURLSession, didReceiveChallenge challenge: NSURLAuthenticationChallenge, completionHandler: (NSURLSessionAuthChallengeDisposition, NSURLCredential?) -> Void) {
|
||||
let deposition: NSURLSessionAuthChallengeDisposition = credential != nil ? .UseCredential : .PerformDefaultHandling
|
||||
func urlSession(_ session: URLSession, didReceive challenge: URLAuthenticationChallenge, completionHandler: @escaping (URLSession.AuthChallengeDisposition, URLCredential?) -> Void) {
|
||||
let deposition: Foundation.URLSession.AuthChallengeDisposition = credential != nil ? .useCredential : .performDefaultHandling
|
||||
completionHandler(deposition, credential)
|
||||
}
|
||||
}
|
||||
|
||||
public enum FileProviderHTTPErrorCode: Int {
|
||||
case Continue = 100
|
||||
case SwitchingProtocols = 101
|
||||
case Processing = 102
|
||||
case OK = 200
|
||||
case Created = 201
|
||||
case Accepted = 202
|
||||
case NonAuthoritativeInformation = 203
|
||||
case NoContent = 204
|
||||
case ResetContent = 205
|
||||
case PartialContent = 206
|
||||
case MultiStatus = 207
|
||||
case AlreadyReported = 208
|
||||
case IMUsed = 226
|
||||
case MultipleChoices = 300
|
||||
case MovedPermanently = 301
|
||||
case Found = 302
|
||||
case SeeOther = 303
|
||||
case NotModified = 304
|
||||
case UseProxy = 305
|
||||
case SwitchProxy = 306
|
||||
case TemporaryRedirect = 307
|
||||
case PermanentRedirect = 308
|
||||
case BadRequest = 400
|
||||
case Unauthorized = 401
|
||||
case PaymentRequired = 402
|
||||
case Forbidden = 403
|
||||
case NotFound = 404
|
||||
case MethodNotAllowed = 405
|
||||
case NotAcceptable = 406
|
||||
case ProxyAuthenticationRequired = 407
|
||||
case RequestTimeout = 408
|
||||
case Conflict = 409
|
||||
case Gone = 410
|
||||
case LengthRequired = 411
|
||||
case PreconditionFailed = 412
|
||||
case PayloadTooLarge = 413
|
||||
case URITooLong = 414
|
||||
case UnsupportedMediaType = 415
|
||||
case RangeNotSatisfiable = 416
|
||||
case ExpectationFailed = 417
|
||||
case MisdirectedRequest = 421
|
||||
case UnprocessableEntity = 422
|
||||
case Locked = 423
|
||||
case FailedDependency = 424
|
||||
case UnorderedCollection = 425
|
||||
case UpgradeRequired = 426
|
||||
case PreconditionRequired = 428
|
||||
case TooManyRequests = 429
|
||||
case RequestHeaderFieldsTooLarge = 431
|
||||
case UnavailableForLegalReasons = 451
|
||||
case InternalServerError = 500
|
||||
case BadGateway = 502
|
||||
case ServiceUnavailable = 503
|
||||
case GatewayTimeout = 504
|
||||
case HTTPVersionNotSupported = 505
|
||||
case VariantlsoNegotiates = 506
|
||||
case InsufficientStorage = 507
|
||||
case LoopDetected = 508
|
||||
case BandwidthLimitExceeded = 509
|
||||
case NotExtended = 510
|
||||
case NetworkAuthenticationRequired = 511
|
||||
case `continue` = 100
|
||||
case switchingProtocols = 101
|
||||
case processing = 102
|
||||
case ok = 200
|
||||
case created = 201
|
||||
case accepted = 202
|
||||
case nonAuthoritativeInformation = 203
|
||||
case noContent = 204
|
||||
case resetContent = 205
|
||||
case partialContent = 206
|
||||
case multiStatus = 207
|
||||
case alreadyReported = 208
|
||||
case imUsed = 226
|
||||
case multipleChoices = 300
|
||||
case movedPermanently = 301
|
||||
case found = 302
|
||||
case seeOther = 303
|
||||
case notModified = 304
|
||||
case useProxy = 305
|
||||
case switchProxy = 306
|
||||
case temporaryRedirect = 307
|
||||
case permanentRedirect = 308
|
||||
case badRequest = 400
|
||||
case unauthorized = 401
|
||||
case paymentRequired = 402
|
||||
case forbidden = 403
|
||||
case notFound = 404
|
||||
case methodNotAllowed = 405
|
||||
case notAcceptable = 406
|
||||
case proxyAuthenticationRequired = 407
|
||||
case requestTimeout = 408
|
||||
case conflict = 409
|
||||
case gone = 410
|
||||
case lengthRequired = 411
|
||||
case preconditionFailed = 412
|
||||
case payloadTooLarge = 413
|
||||
case uriTooLong = 414
|
||||
case unsupportedMediaType = 415
|
||||
case rangeNotSatisfiable = 416
|
||||
case expectationFailed = 417
|
||||
case misdirectedRequest = 421
|
||||
case unprocessableEntity = 422
|
||||
case locked = 423
|
||||
case failedDependency = 424
|
||||
case unorderedCollection = 425
|
||||
case upgradeRequired = 426
|
||||
case preconditionRequired = 428
|
||||
case tooManyRequests = 429
|
||||
case requestHeaderFieldsTooLarge = 431
|
||||
case unavailableForLegalReasons = 451
|
||||
case internalServerError = 500
|
||||
case badGateway = 502
|
||||
case serviceUnavailable = 503
|
||||
case gatewayTimeout = 504
|
||||
case httpVersionNotSupported = 505
|
||||
case variantlsoNegotiates = 506
|
||||
case insufficientStorage = 507
|
||||
case loopDetected = 508
|
||||
case bandwidthLimitExceeded = 509
|
||||
case notExtended = 510
|
||||
case networkAuthenticationRequired = 511
|
||||
|
||||
private static let status1xx = [100: "Continue", 101: "Switching Protocols", 102: "Processing"]
|
||||
private static let status2xx = [200: "OK", 201: "Created", 202: "Accepted", 203: "Non-Authoritative Information", 204: "No Content", 205: "Reset Content", 206: "Partial Content", 207: "Multi-Status", 208: "Already Reported", 226: "IM Used"]
|
||||
private static let status3xx = [300: "Multiple Choices", 301: "Moved Permanently", 302: "Found", 303: "See Other", 304: "Not Modified", 305: "Use Proxy", 306: "Switch Proxy", 307: "Temporary Redirect", 308: "Permanent Redirect"]
|
||||
private static let status4xx = [400: "Bad Request", 401: "Unauthorized/Expired Session", 402: "Payment Required", 403: "Forbidden", 404: "Not Found", 405: "Method Not Allowed", 406: "Not Acceptable", 407: "Proxy Authentication Required", 408: "Request Timeout", 409: "Conflict", 410: "Gone", 411: "Length Required", 412: "Precondition Failed", 413: "Payload Too Large", 414: "URI Too Long", 415: "Unsupported Media Type", 416: "Range Not Satisfiable", 417: "Expectation Failed", 421: "Misdirected Request", 422: "Unprocessable Entity", 423: "Locked", 424: "Failed Dependency", 425: "Unordered Collection", 426: "Upgrade Required", 428: "Precondition Required", 429: "Too Many Requests", 431: "Request Header Fields Too Large", 451: "Unavailable For Legal Reasons"]
|
||||
private static let status5xx = [500: "Internal Server Error", 501: "Not Implemented", 502: "Bad Gateway", 503: "Service Unavailable", 504: "Gateway Timeout", 505: "HTTP Version Not Supported", 506: "Variant Also Negotiates", 507: "Insufficient Storage", 508: "Loop Detected", 509: "Bandwidth Limit Exceeded", 510: "Not Extended", 511: "Network Authentication Required"]
|
||||
fileprivate static let status1xx = [100: "Continue", 101: "Switching Protocols", 102: "Processing"]
|
||||
fileprivate static let status2xx = [200: "OK", 201: "Created", 202: "Accepted", 203: "Non-Authoritative Information", 204: "No Content", 205: "Reset Content", 206: "Partial Content", 207: "Multi-Status", 208: "Already Reported", 226: "IM Used"]
|
||||
fileprivate static let status3xx = [300: "Multiple Choices", 301: "Moved Permanently", 302: "Found", 303: "See Other", 304: "Not Modified", 305: "Use Proxy", 306: "Switch Proxy", 307: "Temporary Redirect", 308: "Permanent Redirect"]
|
||||
fileprivate static let status4xx = [400: "Bad Request", 401: "Unauthorized/Expired Session", 402: "Payment Required", 403: "Forbidden", 404: "Not Found", 405: "Method Not Allowed", 406: "Not Acceptable", 407: "Proxy Authentication Required", 408: "Request Timeout", 409: "Conflict", 410: "Gone", 411: "Length Required", 412: "Precondition Failed", 413: "Payload Too Large", 414: "URI Too Long", 415: "Unsupported Media Type", 416: "Range Not Satisfiable", 417: "Expectation Failed", 421: "Misdirected Request", 422: "Unprocessable Entity", 423: "Locked", 424: "Failed Dependency", 425: "Unordered Collection", 426: "Upgrade Required", 428: "Precondition Required", 429: "Too Many Requests", 431: "Request Header Fields Too Large", 451: "Unavailable For Legal Reasons"]
|
||||
fileprivate static let status5xx = [500: "Internal Server Error", 501: "Not Implemented", 502: "Bad Gateway", 503: "Service Unavailable", 504: "Gateway Timeout", 505: "HTTP Version Not Supported", 506: "Variant Also Negotiates", 507: "Insufficient Storage", 508: "Loop Detected", 509: "Bandwidth Limit Exceeded", 510: "Not Extended", 511: "Network Authentication Required"]
|
||||
|
||||
public var description: String {
|
||||
switch self.rawValue {
|
||||
@@ -176,4 +176,4 @@ public enum FileProviderHTTPErrorCode: Int {
|
||||
default: return "Server Error"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+194
-173
@@ -7,13 +7,33 @@
|
||||
//
|
||||
|
||||
import Foundation
|
||||
fileprivate func < <T : Comparable>(lhs: T?, rhs: T?) -> Bool {
|
||||
switch (lhs, rhs) {
|
||||
case let (l?, r?):
|
||||
return l < r
|
||||
case (nil, _?):
|
||||
return true
|
||||
default:
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
fileprivate func >= <T : Comparable>(lhs: T?, rhs: T?) -> Bool {
|
||||
switch (lhs, rhs) {
|
||||
case let (l?, r?):
|
||||
return l >= r
|
||||
default:
|
||||
return !(lhs < rhs)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public final class WebDavFileObject: FileObject {
|
||||
public let contentType: String
|
||||
public let entryTag: String?
|
||||
|
||||
// codebeat:disable[ARITY]
|
||||
public init(absoluteURL: NSURL, name: String, path: String, size: Int64 = -1, contentType: String = "", createdDate: NSDate? = nil, modifiedDate: NSDate? = nil, fileType: FileType = .Regular, isHidden: Bool = false, isReadOnly: Bool = false, entryTag: String? = nil) {
|
||||
public init(absoluteURL: URL, name: String, path: String, size: Int64 = -1, contentType: String = "", createdDate: Date? = nil, modifiedDate: Date? = nil, fileType: FileType = .regular, isHidden: Bool = false, isReadOnly: Bool = false, entryTag: String? = nil) {
|
||||
self.contentType = contentType
|
||||
self.entryTag = entryTag
|
||||
super.init(absoluteURL: absoluteURL, name: name, path: path, size: size, createdDate: createdDate, modifiedDate: modifiedDate, fileType: fileType, isHidden: isHidden, isReadOnly: isReadOnly)
|
||||
@@ -24,37 +44,37 @@ public final class WebDavFileObject: FileObject {
|
||||
// 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 WebDAVFileProvider: 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 {
|
||||
open class WebDAVFileProvider: NSObject, FileProviderBasic {
|
||||
open static let type: String = "WebDAV"
|
||||
open let isPathRelative: Bool = true
|
||||
open let baseURL: URL?
|
||||
open var currentPath: String = ""
|
||||
open var dispatch_queue: DispatchQueue {
|
||||
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?
|
||||
open weak var delegate: FileProviderDelegate?
|
||||
open let credential: URLCredential?
|
||||
|
||||
private var _session: NSURLSession?
|
||||
private var sessionDelegate: SessionDelegate?
|
||||
private var session: NSURLSession {
|
||||
fileprivate var _session: URLSession?
|
||||
fileprivate var sessionDelegate: SessionDelegate?
|
||||
fileprivate var session: URLSession {
|
||||
if _session == nil {
|
||||
self.sessionDelegate = SessionDelegate(fileProvider: self, credential: credential)
|
||||
let queue = NSOperationQueue()
|
||||
let queue = OperationQueue()
|
||||
//queue.underlyingQueue = dispatch_queue
|
||||
_session = NSURLSession(configuration: NSURLSessionConfiguration.defaultSessionConfiguration(), delegate: sessionDelegate, delegateQueue: queue)
|
||||
_session = URLSession(configuration: URLSessionConfiguration.default, delegate: sessionDelegate as URLSessionDownloadDelegate?, delegateQueue: queue)
|
||||
}
|
||||
return _session!
|
||||
}
|
||||
|
||||
public init? (baseURL: NSURL, credential: NSURLCredential?) {
|
||||
if !["http", "https"].contains(baseURL.uw_scheme.lowercaseString) {
|
||||
public init? (baseURL: URL, credential: URLCredential?) {
|
||||
if !["http", "https"].contains(baseURL.uw_scheme.lowercased()) {
|
||||
return nil
|
||||
}
|
||||
self.baseURL = baseURL
|
||||
dispatch_queue = dispatch_queue_create("FileProvider.\(type)", DISPATCH_QUEUE_CONCURRENT)
|
||||
dispatch_queue = DispatchQueue(label: "FileProvider.\(WebDAVFileProvider.type)", attributes: DispatchQueue.Attributes.concurrent)
|
||||
//let url = baseURL.uw_absoluteString
|
||||
self.credential = credential
|
||||
}
|
||||
@@ -63,17 +83,17 @@ public class WebDAVFileProvider: NSObject, FileProviderBasic {
|
||||
_session?.invalidateAndCancel()
|
||||
}
|
||||
|
||||
public func contentsOfDirectoryAtPath(path: String, completionHandler: ((contents: [FileObject], error: ErrorType?) -> Void)) {
|
||||
open func contentsOfDirectory(path: String, completionHandler: @escaping ((_ contents: [FileObject], _ error: Error?) -> Void)) {
|
||||
let url = absoluteURL(path)
|
||||
let request = NSMutableURLRequest(URL: url)
|
||||
request.HTTPMethod = "PROPFIND"
|
||||
var request = URLRequest(url: url)
|
||||
request.httpMethod = "PROPFIND"
|
||||
request.setValue("1", forHTTPHeaderField: "Depth")
|
||||
request.setValue("text/xml; charset=\"utf-8\"", forHTTPHeaderField: "Content-Type")
|
||||
request.HTTPBody = "<?xml version=\"1.0\" encoding=\"utf-8\" ?>\n<D:propfind xmlns:D=\"DAV:\">\n<D:allprop/></D:propfind>".dataUsingEncoding(NSUTF8StringEncoding)
|
||||
request.setValue(String(request.HTTPBody!.length), forHTTPHeaderField: "Content-Length")
|
||||
let task = session.dataTaskWithRequest(request) { (data, response, error) in
|
||||
request.httpBody = "<?xml version=\"1.0\" encoding=\"utf-8\" ?>\n<D:propfind xmlns:D=\"DAV:\">\n<D:allprop/></D:propfind>".data(using: String.Encoding.utf8)
|
||||
request.setValue(String(request.httpBody!.count), forHTTPHeaderField: "Content-Length")
|
||||
let task = session.dataTask(with: request, completionHandler: { (data, response, error) in
|
||||
var responseError: FileProviderWebDavError?
|
||||
if let code = (response as? NSHTTPURLResponse)?.statusCode where code >= 300, let rCode = FileProviderHTTPErrorCode(rawValue: code) {
|
||||
if let code = (response as? HTTPURLResponse)?.statusCode , code >= 300, let rCode = FileProviderHTTPErrorCode(rawValue: code) {
|
||||
responseError = FileProviderWebDavError(code: rCode, url: url)
|
||||
}
|
||||
if let data = data {
|
||||
@@ -85,293 +105,294 @@ public class WebDAVFileProvider: NSObject, FileProviderBasic {
|
||||
}
|
||||
fileObjects.append(self.mapToFileObject(attr))
|
||||
}
|
||||
completionHandler(contents: fileObjects, error: responseError ?? error)
|
||||
completionHandler(fileObjects, responseError ?? error)
|
||||
return
|
||||
}
|
||||
completionHandler(contents: [], error: responseError ?? error)
|
||||
}
|
||||
completionHandler([], responseError ?? error)
|
||||
})
|
||||
task.resume()
|
||||
}
|
||||
|
||||
public func attributesOfItemAtPath(path: String, completionHandler: ((attributes: FileObject?, error: ErrorType?) -> Void)) {
|
||||
open func attributesOfItem(path: String, completionHandler: @escaping ((_ attributes: FileObject?, _ error: Error?) -> Void)) {
|
||||
let url = absoluteURL(path)
|
||||
let request = NSMutableURLRequest(URL: url)
|
||||
request.HTTPMethod = "PROPFIND"
|
||||
var request = URLRequest(url: url)
|
||||
request.httpMethod = "PROPFIND"
|
||||
request.setValue("1", forHTTPHeaderField: "Depth")
|
||||
request.setValue("text/xml; charset=\"utf-8\"", forHTTPHeaderField: "Content-Type")
|
||||
request.HTTPBody = "<?xml version=\"1.0\" encoding=\"utf-8\" ?>\n<D:propfind xmlns:D=\"DAV:\">\n<D:allprop/></D:propfind>".dataUsingEncoding(NSUTF8StringEncoding)
|
||||
request.setValue(String(request.HTTPBody!.length), forHTTPHeaderField: "Content-Length")
|
||||
let task = session.dataTaskWithRequest(request) { (data, response, error) in
|
||||
request.httpBody = "<?xml version=\"1.0\" encoding=\"utf-8\" ?>\n<D:propfind xmlns:D=\"DAV:\">\n<D:allprop/></D:propfind>".data(using: String.Encoding.utf8)
|
||||
request.setValue(String(request.httpBody!.count), forHTTPHeaderField: "Content-Length")
|
||||
let task = session.dataTask(with: request, completionHandler: { (data, response, error) in
|
||||
var responseError: FileProviderWebDavError?
|
||||
if let code = (response as? NSHTTPURLResponse)?.statusCode where code >= 300, let rCode = FileProviderHTTPErrorCode(rawValue: code) {
|
||||
if let code = (response as? HTTPURLResponse)?.statusCode, code >= 300, let rCode = FileProviderHTTPErrorCode(rawValue: code) {
|
||||
responseError = FileProviderWebDavError(code: rCode, url: url)
|
||||
}
|
||||
if let data = data {
|
||||
let xresponse = self.parseXMLResponse(data)
|
||||
if let attr = xresponse.first {
|
||||
completionHandler(attributes: self.mapToFileObject(attr), error: responseError ?? error)
|
||||
completionHandler(self.mapToFileObject(attr), responseError ?? error)
|
||||
return
|
||||
}
|
||||
}
|
||||
completionHandler(attributes: nil, error: responseError ?? error)
|
||||
}
|
||||
completionHandler(nil, responseError ?? error)
|
||||
})
|
||||
task.resume()
|
||||
}
|
||||
|
||||
public func storageProperties(completionHandler: ((total: Int64, used: Int64) -> Void)) {
|
||||
open func storageProperties(completionHandler: @escaping ((_ total: Int64, _ used: Int64) -> Void)) {
|
||||
// Not all WebDAV clients implements RFC2518 which allows geting storage quota.
|
||||
// In this case you won't get error. totalSize is NSURLSessionTransferSizeUnknown
|
||||
// and used space is zero.
|
||||
guard let baseURL = baseURL else {
|
||||
return
|
||||
}
|
||||
let request = NSMutableURLRequest(URL: baseURL)
|
||||
request.HTTPMethod = "PROPFIND"
|
||||
var request = URLRequest(url: baseURL)
|
||||
request.httpMethod = "PROPFIND"
|
||||
request.setValue("0", forHTTPHeaderField: "Depth")
|
||||
request.setValue("text/xml; charset=\"utf-8\"", forHTTPHeaderField: "Content-Type")
|
||||
request.HTTPBody = "<?xml version=\"1.0\" encoding=\"utf-8\" ?>\n<D:propfind xmlns:D=\"DAV:\">\n<D:prop><D:quota-available-bytes/><D:quota-used-bytes/></D:prop>\n</D:propfind>".dataUsingEncoding(NSUTF8StringEncoding)
|
||||
request.setValue(String(request.HTTPBody!.length), forHTTPHeaderField: "Content-Length")
|
||||
let task = session.dataTaskWithRequest(request) { (data, response, error) in
|
||||
request.httpBody = "<?xml version=\"1.0\" encoding=\"utf-8\" ?>\n<D:propfind xmlns:D=\"DAV:\">\n<D:prop><D:quota-available-bytes/><D:quota-used-bytes/></D:prop>\n</D:propfind>".data(using: String.Encoding.utf8)
|
||||
request.setValue(String(request.httpBody!.count), forHTTPHeaderField: "Content-Length")
|
||||
let task = session.dataTask(with: request, completionHandler: { (data, response, error) in
|
||||
if let data = data {
|
||||
let xresponse = self.parseXMLResponse(data)
|
||||
if let attr = xresponse.first {
|
||||
let totalSize = Int64(attr.prop["quota-available-bytes"] ?? "")
|
||||
let usedSize = Int64(attr.prop["quota-used-bytes"] ?? "")
|
||||
completionHandler(total: totalSize ?? -1, used: usedSize ?? 0)
|
||||
completionHandler(totalSize ?? -1, usedSize ?? 0)
|
||||
return
|
||||
}
|
||||
}
|
||||
completionHandler(total: -1, used: 0)
|
||||
}
|
||||
completionHandler(-1, 0)
|
||||
})
|
||||
task.resume()
|
||||
}
|
||||
|
||||
public weak var fileOperationDelegate: FileOperationDelegate?
|
||||
open weak var fileOperationDelegate: FileOperationDelegate?
|
||||
}
|
||||
|
||||
extension WebDAVFileProvider: FileProviderOperations {
|
||||
public func createFolder(folderName: String, atPath: String, completionHandler: SimpleCompletionHandler) {
|
||||
let url = absoluteURL((atPath as NSString).stringByAppendingPathComponent(folderName) + "/")
|
||||
let request = NSMutableURLRequest(URL: url)
|
||||
request.HTTPMethod = "MKCOL"
|
||||
let task = session.dataTaskWithRequest(request) { (data, response, error) in
|
||||
public func create(folder folderName: String, at atPath: String, completionHandler: SimpleCompletionHandler) {
|
||||
let url = absoluteURL((atPath as NSString).appendingPathComponent(folderName) + "/")
|
||||
var request = URLRequest(url: url)
|
||||
request.httpMethod = "MKCOL"
|
||||
let task = session.dataTask(with: request, completionHandler: { (data, response, error) in
|
||||
var responseError: FileProviderWebDavError?
|
||||
if let code = (response as? NSHTTPURLResponse)?.statusCode where code >= 300, let rCode = FileProviderHTTPErrorCode(rawValue: code) {
|
||||
if let code = (response as? HTTPURLResponse)?.statusCode , code >= 300, let rCode = FileProviderHTTPErrorCode(rawValue: code) {
|
||||
responseError = FileProviderWebDavError(code: rCode, url: url)
|
||||
}
|
||||
if let response = response as? NSHTTPURLResponse, let code = FileProviderHTTPErrorCode(rawValue: response.statusCode) where code != .OK {
|
||||
completionHandler?(error: FileProviderWebDavError(code: code, url: url))
|
||||
if let response = response as? HTTPURLResponse, let code = FileProviderHTTPErrorCode(rawValue: response.statusCode) , code != .ok {
|
||||
completionHandler?(FileProviderWebDavError(code: code, url: url))
|
||||
return
|
||||
}
|
||||
completionHandler?(error: responseError ?? error)
|
||||
self.delegateNotify(.Create(path: (atPath as NSString).stringByAppendingPathComponent(folderName) + "/"), error: responseError ?? error)
|
||||
}
|
||||
completionHandler?(responseError ?? error)
|
||||
self.delegateNotify(.create(path: (atPath as NSString).appendingPathComponent(folderName) + "/"), error: responseError ?? error)
|
||||
})
|
||||
task.resume()
|
||||
}
|
||||
|
||||
public func createFile(fileAttribs: FileObject, atPath path: String, contents data: NSData?, completionHandler: SimpleCompletionHandler) {
|
||||
public func create(file fileAttribs: FileObject, at path: String, contents data: Data?, completionHandler: SimpleCompletionHandler) {
|
||||
let url = absoluteURL(path)
|
||||
let request = NSMutableURLRequest(URL: url)
|
||||
request.HTTPMethod = "PUT"
|
||||
let task = session.uploadTaskWithRequest(request, fromData: data) { (data, response, error) in
|
||||
var request = URLRequest(url: url)
|
||||
request.httpMethod = "PUT"
|
||||
let task = session.uploadTask(with: request, from: data, completionHandler: { (data, response, error) in
|
||||
var responseError: FileProviderWebDavError?
|
||||
if let code = (response as? NSHTTPURLResponse)?.statusCode where code >= 300, let rCode = FileProviderHTTPErrorCode(rawValue: code) {
|
||||
if let code = (response as? HTTPURLResponse)?.statusCode , code >= 300, let rCode = FileProviderHTTPErrorCode(rawValue: code) {
|
||||
responseError = FileProviderWebDavError(code: rCode, url: url)
|
||||
}
|
||||
completionHandler?(error: responseError ?? error)
|
||||
self.delegateNotify(.Create(path: (path as NSString).stringByAppendingPathComponent(fileAttribs.name)), error: responseError ?? error)
|
||||
}
|
||||
task.taskDescription = dictionaryToJSON(["type": "Create", "source": (path as NSString).stringByAppendingPathComponent(fileAttribs.name)])
|
||||
completionHandler?(responseError ?? error)
|
||||
self.delegateNotify(.create(path: (path as NSString).appendingPathComponent(fileAttribs.name)), error: responseError ?? error)
|
||||
})
|
||||
task.taskDescription = dictionaryToJSON(["type": "Create" as NSString, "source": (path as NSString).appendingPathComponent(fileAttribs.name) as NSString])
|
||||
task.resume()
|
||||
}
|
||||
|
||||
public func moveItemAtPath(path: String, toPath: String, overwrite: Bool = false, completionHandler: SimpleCompletionHandler) {
|
||||
self.copyMoveItemAtPath(true, path: path, toPath: toPath, overwrite: overwrite, completionHandler: completionHandler)
|
||||
public func moveItem(path: String, to toPath: String, overwrite: Bool = false, completionHandler: SimpleCompletionHandler) {
|
||||
self.copyMoveItem(move: true, path: path, toPath: toPath, overwrite: overwrite, completionHandler: completionHandler)
|
||||
}
|
||||
|
||||
public func copyItemAtPath(path: String, toPath: String, overwrite: Bool = false, completionHandler: SimpleCompletionHandler) {
|
||||
self.copyMoveItemAtPath(false, path: path, toPath: toPath, overwrite: overwrite, completionHandler: completionHandler)
|
||||
public func copyItem(path: String, to toPath: String, overwrite: Bool = false, completionHandler: SimpleCompletionHandler) {
|
||||
self.copyMoveItem(move: false, path: path, toPath: toPath, overwrite: overwrite, completionHandler: completionHandler)
|
||||
}
|
||||
|
||||
private func copyMoveItemAtPath(move:Bool, path: String, toPath: String, overwrite: Bool, completionHandler: SimpleCompletionHandler) {
|
||||
fileprivate func copyMoveItem(move:Bool, path: String, toPath: String, overwrite: Bool, completionHandler: SimpleCompletionHandler) {
|
||||
let url = absoluteURL(path)
|
||||
let request = NSMutableURLRequest(URL: url)
|
||||
var request = URLRequest(url: url)
|
||||
if move {
|
||||
request.HTTPMethod = "MOVE"
|
||||
request.httpMethod = "MOVE"
|
||||
} else {
|
||||
request.HTTPMethod = "COPY"
|
||||
request.httpMethod = "COPY"
|
||||
}
|
||||
request.setValue(absoluteURL(path).uw_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 = FileProviderHTTPErrorCode(rawValue: response.statusCode) {
|
||||
let task = session.dataTask(with: request, completionHandler: { (data, response, error) in
|
||||
if let response = response as? HTTPURLResponse, let code = FileProviderHTTPErrorCode(rawValue: response.statusCode) {
|
||||
defer {
|
||||
let op = move ? FileOperation.Move(source: path, destination: toPath) : .Copy(source: path, destination: toPath)
|
||||
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 {
|
||||
if code == .multiStatus, let data = data {
|
||||
let xresponses = self.parseXMLResponse(data)
|
||||
for xresponse in xresponses where xresponse.status >= 300 {
|
||||
completionHandler?(error: FileProviderWebDavError(code: code, url: url))
|
||||
completionHandler?(FileProviderWebDavError(code: code, url: url))
|
||||
}
|
||||
} else {
|
||||
completionHandler?(error: FileProviderWebDavError(code: code, url: url))
|
||||
completionHandler?(FileProviderWebDavError(code: code, url: url))
|
||||
}
|
||||
return
|
||||
}
|
||||
completionHandler?(error: error)
|
||||
}
|
||||
completionHandler?(error)
|
||||
})
|
||||
task.resume()
|
||||
}
|
||||
|
||||
public func removeItemAtPath(path: String, completionHandler: SimpleCompletionHandler) {
|
||||
public func removeItem(path: String, completionHandler: SimpleCompletionHandler) {
|
||||
let url = absoluteURL(path)
|
||||
let request = NSMutableURLRequest(URL: url)
|
||||
request.HTTPMethod = "DELETE"
|
||||
let task = session.dataTaskWithRequest(request) { (data, response, error) in
|
||||
if let response = response as? NSHTTPURLResponse, let code = FileProviderHTTPErrorCode(rawValue: response.statusCode) {
|
||||
var request = URLRequest(url: url)
|
||||
request.httpMethod = "DELETE"
|
||||
let task = session.dataTask(with: request, completionHandler: { (data, response, error) in
|
||||
if let response = response as? HTTPURLResponse, let code = FileProviderHTTPErrorCode(rawValue: response.statusCode) {
|
||||
defer {
|
||||
self.delegateNotify(.Remove(path: path), error: error)
|
||||
self.delegateNotify(.remove(path: path), error: error)
|
||||
}
|
||||
if code == .MultiStatus, let data = data {
|
||||
if code == .multiStatus, let data = data {
|
||||
let xresponses = self.parseXMLResponse(data)
|
||||
for xresponse in xresponses where xresponse.status >= 300 {
|
||||
completionHandler?(error: FileProviderWebDavError(code: code, url: url))
|
||||
completionHandler?(FileProviderWebDavError(code: code, url: url))
|
||||
}
|
||||
} else {
|
||||
completionHandler?(error: FileProviderWebDavError(code: code, url: url))
|
||||
completionHandler?(FileProviderWebDavError(code: code, url: url))
|
||||
}
|
||||
return
|
||||
}
|
||||
completionHandler?(error: error)
|
||||
}
|
||||
completionHandler?(error)
|
||||
})
|
||||
task.resume()
|
||||
}
|
||||
|
||||
public func copyLocalFileToPath(localFile: NSURL, toPath: String, completionHandler: SimpleCompletionHandler) {
|
||||
public func copyItem(localFile: URL, to toPath: String, completionHandler: SimpleCompletionHandler) {
|
||||
let url = absoluteURL(toPath)
|
||||
let request = NSMutableURLRequest(URL: url)
|
||||
request.HTTPMethod = "PUT"
|
||||
let task = session.uploadTaskWithRequest(request, fromFile: localFile) { (data, response, error) in
|
||||
var request = URLRequest(url: url)
|
||||
request.httpMethod = "PUT"
|
||||
let task = session.uploadTask(with: request, fromFile: localFile, completionHandler: { (data, response, error) in
|
||||
var responseError: FileProviderWebDavError?
|
||||
if let code = (response as? NSHTTPURLResponse)?.statusCode where code >= 300, let rCode = FileProviderHTTPErrorCode(rawValue: code) {
|
||||
if let code = (response as? HTTPURLResponse)?.statusCode , code >= 300, let rCode = FileProviderHTTPErrorCode(rawValue: code) {
|
||||
responseError = FileProviderWebDavError(code: rCode, url: url)
|
||||
}
|
||||
completionHandler?(error: responseError ?? error)
|
||||
self.delegateNotify(.Move(source: localFile.uw_absoluteString, destination: toPath), error: responseError ?? error)
|
||||
}
|
||||
task.taskDescription = dictionaryToJSON(["type": "Copy", "source": localFile.uw_absoluteString, "dest": toPath])
|
||||
completionHandler?(responseError ?? error)
|
||||
self.delegateNotify(.move(source: localFile.uw_absoluteString, destination: toPath), error: responseError ?? error)
|
||||
})
|
||||
task.taskDescription = dictionaryToJSON(["type": "Copy" as NSString, "source": localFile.uw_absoluteString as NSString, "dest": toPath as NSString])
|
||||
task.resume()
|
||||
}
|
||||
|
||||
public func copyPathToLocalFile(path: String, toLocalURL: NSURL, completionHandler: SimpleCompletionHandler) {
|
||||
public func copyItem(path: String, toLocalURL: URL, completionHandler: SimpleCompletionHandler) {
|
||||
let url = absoluteURL(path)
|
||||
let request = NSMutableURLRequest(URL: url)
|
||||
let task = session.downloadTaskWithRequest(request) { (sourceFileURL, response, error) in
|
||||
let request = URLRequest(url: url)
|
||||
let task = session.downloadTask(with: request, completionHandler: { (sourceFileURL, response, error) in
|
||||
var responseError: FileProviderWebDavError?
|
||||
if let code = (response as? NSHTTPURLResponse)?.statusCode where code >= 300, let rCode = FileProviderHTTPErrorCode(rawValue: code) {
|
||||
if let code = (response as? HTTPURLResponse)?.statusCode , code >= 300, let rCode = FileProviderHTTPErrorCode(rawValue: code) {
|
||||
responseError = FileProviderWebDavError(code: rCode, url: url)
|
||||
}
|
||||
if let sourceFileURL = sourceFileURL {
|
||||
do {
|
||||
try NSFileManager.defaultManager().copyItemAtURL(sourceFileURL, toURL: toLocalURL)
|
||||
try FileManager.default.copyItem(at: sourceFileURL, to: toLocalURL)
|
||||
} catch let e {
|
||||
completionHandler?(error: e)
|
||||
completionHandler?(e)
|
||||
return
|
||||
}
|
||||
}
|
||||
completionHandler?(error: responseError ?? error)
|
||||
}
|
||||
task.taskDescription = dictionaryToJSON(["type": "Copy", "source": path, "dest": toLocalURL.uw_absoluteString])
|
||||
completionHandler?(responseError ?? error)
|
||||
})
|
||||
task.taskDescription = dictionaryToJSON(["type": "Copy" as NSString, "source": path as NSString, "dest": toLocalURL.uw_absoluteString as NSString])
|
||||
task.resume()
|
||||
}
|
||||
}
|
||||
|
||||
extension WebDAVFileProvider: FileProviderReadWrite {
|
||||
public func contentsAtPath(path: String, completionHandler: ((contents: NSData?, error: ErrorType?) -> Void)) {
|
||||
self.contentsAtPath(path, offset: 0, length: -1, completionHandler: completionHandler)
|
||||
public func contents(path: String, completionHandler: @escaping ((_ contents: Data?, _ error: Error?) -> Void)) {
|
||||
self.contents(path: path, offset: 0, length: -1, completionHandler: completionHandler)
|
||||
}
|
||||
|
||||
public func contentsAtPath(path: String, offset: Int64, length: Int, completionHandler: ((contents: NSData?, error: ErrorType?) -> Void)) {
|
||||
public func contents(path: String, offset: Int64, length: Int, completionHandler: @escaping ((_ contents: Data?, _ error: Error?) -> Void)) {
|
||||
let url = absoluteURL(path)
|
||||
let request = NSMutableURLRequest(URL: url)
|
||||
request.HTTPMethod = "GET"
|
||||
var request = URLRequest(url: url)
|
||||
request.httpMethod = "GET"
|
||||
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
|
||||
let task = session.dataTask(with: request, completionHandler: { (data, response, error) in
|
||||
var responseError: FileProviderWebDavError?
|
||||
if let code = (response as? NSHTTPURLResponse)?.statusCode where code >= 300, let rCode = FileProviderHTTPErrorCode(rawValue: code) {
|
||||
if let code = (response as? HTTPURLResponse)?.statusCode , code >= 300, let rCode = FileProviderHTTPErrorCode(rawValue: code) {
|
||||
responseError = FileProviderWebDavError(code: rCode, url: url)
|
||||
}
|
||||
completionHandler(contents: data, error: responseError ?? error)
|
||||
}
|
||||
completionHandler(data, responseError ?? error)
|
||||
})
|
||||
task.resume()
|
||||
}
|
||||
|
||||
public func writeContentsAtPath(path: String, contents data: NSData, atomically: Bool = false, completionHandler: SimpleCompletionHandler) {
|
||||
public func writeContents(path: String, contents data: Data, atomically: Bool = false, completionHandler: SimpleCompletionHandler) {
|
||||
// FIXME: lock destination before writing process
|
||||
let url = atomically ? absoluteURL(path).uw_URLByAppendingPathExtension("tmp") : absoluteURL(path)
|
||||
let request = NSMutableURLRequest(URL: url)
|
||||
request.HTTPMethod = "PUT"
|
||||
let task = session.uploadTaskWithRequest(request, fromData: data) { (data, response, error) in
|
||||
var request = URLRequest(url: url)
|
||||
request.httpMethod = "PUT"
|
||||
let task = session.uploadTask(with: request, from: data, completionHandler: { (data, response, error) in
|
||||
var responseError: FileProviderWebDavError?
|
||||
if let code = (response as? NSHTTPURLResponse)?.statusCode where code >= 300, let rCode = FileProviderHTTPErrorCode(rawValue: code) {
|
||||
if let code = (response as? HTTPURLResponse)?.statusCode , code >= 300, let rCode = FileProviderHTTPErrorCode(rawValue: code) {
|
||||
responseError = FileProviderWebDavError(code: rCode, url: self.absoluteURL(path))
|
||||
}
|
||||
defer {
|
||||
self.delegateNotify(.Modify(path: path), error: responseError ?? error)
|
||||
self.delegateNotify(.modify(path: path), error: responseError ?? error)
|
||||
}
|
||||
if let error = error {
|
||||
completionHandler?(error: error)
|
||||
completionHandler?(error)
|
||||
return
|
||||
}
|
||||
if atomically {
|
||||
self.moveItemAtPath((path as NSString).stringByAppendingPathExtension("tmp")!, toPath: path, completionHandler: completionHandler)
|
||||
self.moveItem(path: (path as NSString).appendingPathExtension("tmp")!, to: path, completionHandler: completionHandler)
|
||||
}
|
||||
}
|
||||
task.taskDescription = dictionaryToJSON(["type": "Modify", "source": path])
|
||||
})
|
||||
task.taskDescription = dictionaryToJSON(["type": "Modify" as NSString, "source": path as NSString])
|
||||
task.resume()
|
||||
}
|
||||
|
||||
public func searchFilesAtPath(path: String, recursive: Bool, query: String, foundItemHandler: ((FileObject) -> Void)?, completionHandler: ((files: [FileObject], error: ErrorType?) -> Void)) {
|
||||
public func searchFiles(path: String, recursive: Bool, query: String, foundItemHandler: ((FileObject) -> Void)?, completionHandler: @escaping ((_ files: [FileObject], _ error: Error?) -> Void)) {
|
||||
let url = absoluteURL(path)
|
||||
let request = NSMutableURLRequest(URL: url)
|
||||
request.HTTPMethod = "PROPFIND"
|
||||
var request = URLRequest(url: url)
|
||||
request.httpMethod = "PROPFIND"
|
||||
//request.setValue("1", forHTTPHeaderField: "Depth")
|
||||
request.setValue("text/xml; charset=\"utf-8\"", forHTTPHeaderField: "Content-Type")
|
||||
request.HTTPBody = "<?xml version=\"1.0\" encoding=\"utf-8\" ?>\n<D:propfind xmlns:D=\"DAV:\">\n<D:allprop/></D:propfind>".dataUsingEncoding(NSUTF8StringEncoding)
|
||||
request.setValue(String(request.HTTPBody!.length), forHTTPHeaderField: "Content-Length")
|
||||
let task = session.dataTaskWithRequest(request) { (data, response, error) in
|
||||
request.httpBody = "<?xml version=\"1.0\" encoding=\"utf-8\" ?>\n<D:propfind xmlns:D=\"DAV:\">\n<D:allprop/></D:propfind>".data(using: String.Encoding.utf8)
|
||||
request.setValue(String(request.httpBody!.count), forHTTPHeaderField: "Content-Length")
|
||||
let task = session.dataTask(with: request, completionHandler: { (data, response, error) in
|
||||
// FIXME: paginating results
|
||||
var responseError: FileProviderWebDavError?
|
||||
if let code = (response as? NSHTTPURLResponse)?.statusCode where code >= 300, let rCode = FileProviderHTTPErrorCode(rawValue: code) {
|
||||
if let code = (response as? HTTPURLResponse)?.statusCode , code >= 300, let rCode = FileProviderHTTPErrorCode(rawValue: code) {
|
||||
responseError = FileProviderWebDavError(code: rCode, url: url)
|
||||
}
|
||||
if let data = data {
|
||||
let xresponse = self.parseXMLResponse(data)
|
||||
var fileObjects = [WebDavFileObject]()
|
||||
for attr in xresponse {
|
||||
if let path = attr.href.path where !((path as NSString).lastPathComponent.containsString(query)) {
|
||||
let path = attr.href.path
|
||||
if !((path as NSString).lastPathComponent.contains(query)) {
|
||||
continue
|
||||
}
|
||||
let fileObject = self.mapToFileObject(attr)
|
||||
fileObjects.append(fileObject)
|
||||
foundItemHandler?(fileObject)
|
||||
}
|
||||
completionHandler(files: fileObjects, error: responseError ?? error)
|
||||
completionHandler(fileObjects, responseError ?? error)
|
||||
return
|
||||
}
|
||||
completionHandler(files: [], error: responseError ?? error)
|
||||
}
|
||||
completionHandler([], responseError ?? error)
|
||||
})
|
||||
task.resume()
|
||||
}
|
||||
|
||||
private func registerNotifcation(path: String, eventHandler: (() -> Void)) {
|
||||
fileprivate func registerNotifcation(path: String, eventHandler: (() -> Void)) {
|
||||
/* There is no unified api for monitoring WebDAV server content change/update
|
||||
* Microsoft Exchange uses SUBSCRIBE method, Apple uses push notification system.
|
||||
* while both is unavailable in a mobile platform.
|
||||
@@ -379,7 +400,7 @@ extension WebDAVFileProvider: FileProviderReadWrite {
|
||||
* with previous results
|
||||
*/
|
||||
}
|
||||
private func unregisterNotifcation(path: String) {
|
||||
fileprivate func unregisterNotifcation(path: String) {
|
||||
|
||||
}
|
||||
// TODO: implements methods for lock mechanism
|
||||
@@ -391,22 +412,22 @@ extension WebDAVFileProvider: FileProvider {}
|
||||
|
||||
internal extension WebDAVFileProvider {
|
||||
struct DavResponse {
|
||||
let href: NSURL
|
||||
let href: URL
|
||||
let hrefString: String
|
||||
let status: Int?
|
||||
let prop: [String: String]
|
||||
}
|
||||
|
||||
private func parseXMLResponse(response: NSData) -> [DavResponse] {
|
||||
fileprivate func parseXMLResponse(_ response: Data) -> [DavResponse] {
|
||||
var result = [DavResponse]()
|
||||
do {
|
||||
let xml = try AEXMLDocument(xmlData: response)
|
||||
let xml = try AEXMLDocument(xml: response)
|
||||
var rootnode = xml.root
|
||||
var responsetag = "response"
|
||||
for node in rootnode.all ?? [] where node.name.lowercaseString.hasSuffix("multistatus") {
|
||||
for node in rootnode.all ?? [] where node.name.lowercased().hasSuffix("multistatus") {
|
||||
rootnode = node
|
||||
}
|
||||
for node in rootnode.children ?? [] where node.name.lowercaseString.hasSuffix("response") {
|
||||
for node in rootnode.children where node.name.lowercased().hasSuffix("response") {
|
||||
responsetag = node.name
|
||||
break
|
||||
}
|
||||
@@ -420,46 +441,46 @@ internal extension WebDAVFileProvider {
|
||||
return result
|
||||
}
|
||||
|
||||
private func mapNodeToDavResponse(node: AEXMLElement) -> DavResponse? {
|
||||
fileprivate func mapNodeToDavResponse(_ node: AEXMLElement) -> DavResponse? {
|
||||
var hreftag = "href"
|
||||
var statustag = "status"
|
||||
var propstattag = "propstat"
|
||||
for node in node.children ?? [] {
|
||||
if node.name.lowercaseString.hasSuffix("href") {
|
||||
for node in node.children {
|
||||
if node.name.lowercased().hasSuffix("href") {
|
||||
hreftag = node.name
|
||||
}
|
||||
if node.name.lowercaseString.hasSuffix("status") {
|
||||
if node.name.lowercased().hasSuffix("status") {
|
||||
statustag = node.name
|
||||
}
|
||||
if node.name.lowercaseString.hasSuffix("propstat") {
|
||||
if node.name.lowercased().hasSuffix("propstat") {
|
||||
propstattag = node.name
|
||||
}
|
||||
}
|
||||
let href = node[hreftag].value
|
||||
if let href = href, hrefURL = NSURL(string: href) {
|
||||
if let href = href, let hrefURL = URL(string: href) {
|
||||
var status: Int?
|
||||
let statusDesc = (node[statustag].stringValue).componentsSeparatedByString(" ")
|
||||
let statusDesc = (node[statustag].string).components(separatedBy: " ")
|
||||
if statusDesc.count > 2 {
|
||||
status = Int(statusDesc[1])
|
||||
}
|
||||
var propDic = [String: String]()
|
||||
let propStatNode = node[propstattag]
|
||||
for node in propStatNode.children ?? [] where node.name.lowercaseString.hasSuffix("status"){
|
||||
for node in propStatNode.children where node.name.lowercased().hasSuffix("status"){
|
||||
statustag = node.name
|
||||
break
|
||||
}
|
||||
let statusDesc2 = (propStatNode[statustag].stringValue).componentsSeparatedByString(" ")
|
||||
let statusDesc2 = (propStatNode[statustag].string).components(separatedBy: " ")
|
||||
if statusDesc2.count > 2 {
|
||||
status = Int(statusDesc2[1])
|
||||
}
|
||||
var proptag = "prop"
|
||||
for tnode in propStatNode.children ?? [] where tnode.name.lowercaseString.hasSuffix("prop") {
|
||||
for tnode in propStatNode.children where tnode.name.lowercased().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") && propItemNode.xmlString.containsString("collection") {
|
||||
for propItemNode in propStatNode[proptag].children {
|
||||
propDic[propItemNode.name.components(separatedBy: ":").last!.lowercased()] = propItemNode.value
|
||||
if propItemNode.name.hasSuffix("resourcetype") && propItemNode.xml.contains("collection") {
|
||||
propDic["getcontenttype"] = "httpd/unix-directory"
|
||||
}
|
||||
}
|
||||
@@ -468,23 +489,23 @@ internal extension WebDAVFileProvider {
|
||||
return nil
|
||||
}
|
||||
|
||||
private func mapToFileObject(davResponse: DavResponse) -> WebDavFileObject {
|
||||
fileprivate func mapToFileObject(_ davResponse: DavResponse) -> WebDavFileObject {
|
||||
var href = davResponse.href
|
||||
if href.baseURL == nil {
|
||||
href = absoluteURL(href.path ?? "")
|
||||
href = absoluteURL(href.path)
|
||||
}
|
||||
let name = davResponse.prop["displayname"] ?? (davResponse.hrefString.stringByRemovingPercentEncoding! as NSString).lastPathComponent
|
||||
let name = davResponse.prop["displayname"] ?? (davResponse.hrefString.removingPercentEncoding! as NSString).lastPathComponent
|
||||
let size = Int64(davResponse.prop["getcontentlength"] ?? "-1") ?? NSURLSessionTransferSizeUnknown
|
||||
let createdDate = self.resolveDate(davResponse.prop["creationdate"] ?? "")
|
||||
let modifiedDate = self.resolveDate(davResponse.prop["getlastmodified"] ?? "")
|
||||
let createdDate = self.resolve(dateString: davResponse.prop["creationdate"] ?? "")
|
||||
let modifiedDate = self.resolve(dateString: 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, path: href.path ?? 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, 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?) {
|
||||
dispatch_async(dispatch_get_main_queue(), {
|
||||
fileprivate func delegateNotify(_ operation: FileOperation, error: Error?) {
|
||||
DispatchQueue.main.async(execute: {
|
||||
if error == nil {
|
||||
self.delegate?.fileproviderSucceed(self, operation: operation)
|
||||
} else {
|
||||
@@ -494,11 +515,11 @@ internal extension WebDAVFileProvider {
|
||||
}
|
||||
}
|
||||
|
||||
public struct FileProviderWebDavError: ErrorType, CustomStringConvertible {
|
||||
public struct FileProviderWebDavError: Error, CustomStringConvertible {
|
||||
public let code: FileProviderHTTPErrorCode
|
||||
public let url: NSURL
|
||||
public let url: URL
|
||||
|
||||
public var description: String {
|
||||
return code.description
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user