diff --git a/FileProvider.podspec b/FileProvider.podspec new file mode 100644 index 0000000..03cde55 --- /dev/null +++ b/FileProvider.podspec @@ -0,0 +1,140 @@ +# +# Be sure to run `pod spec lint FileProvider.podspec' to ensure this is a +# valid spec and to remove all comments including this before submitting the spec. +# +# To learn more about Podspec attributes see http://docs.cocoapods.org/specification.html +# To see working Podspecs in the CocoaPods repo see https://github.com/CocoaPods/Specs/ +# + +Pod::Spec.new do |s| + + # ――― Spec Metadata ―――――――――――――――――――――――――――――――――――――――――――――――――――――――――― # + # + # These will help people to find your library, and whilst it + # can feel like a chore to fill in it's definitely to your advantage. The + # summary should be tweet-length, and the description more in depth. + # + + s.name = "FileProvider" + s.version = "0.1.0" + s.summary = "Extended Local/WebDAV/SMB/CIFS/etc. File Manager for Swift 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? + # * Try to keep it short, snappy and to the point. + # * Write the description between the DESC delimiters below. + # * Finally, don't worry about the indent, CocoaPods strips it! + s.description = <<-DESC + This Swift library provide a swifty way to deal with local and remote files + and directories in same way. For now Local and WebDAV providers are ready to use + and SMB2, Dropbox, FTP and AmazonS3 is planned for future. + DESC + + s.homepage = "https://github.com/amosavian/FileProvider" + # s.screenshots = "www.example.com/screenshots_1.gif", "www.example.com/screenshots_2.gif" + + + # ――― Spec License ――――――――――――――――――――――――――――――――――――――――――――――――――――――――――― # + # + # Licensing your code is important. See http://choosealicense.com for more info. + # CocoaPods will detect a license file if there is a named LICENSE* + # Popular ones are 'MIT', 'BSD' and 'Apache License, Version 2.0'. + # + + # s.license = "MIT (example)" + s.license = { :type => "MIT", :file => "LICENSE" } + + + # ――― Author Metadata ――――――――――――――――――――――――――――――――――――――――――――――――――――――――― # + # + # Specify the authors of the library, with email addresses. Email addresses + # of the authors are extracted from the SCM log. E.g. $ git log. CocoaPods also + # accepts just a name if you'd rather not provide an email address. + # + # Specify a social_media_url where others can refer to, for example a twitter + # profile URL. + # + + s.author = { "Amir Abbas Mousavian" => "a.mosavian@gmail.com" } + # Or just: s.author = "Amir Abbas Mousavian" + # s.authors = { "Amir Abbas Mousavian" => "a.mosavian@gmail.com" } + # s.social_media_url = "https://twitter.com/amosavian" + + # ――― Platform Specifics ――――――――――――――――――――――――――――――――――――――――――――――――――――――― # + # + # If this Pod runs only on iOS or OS X, then specify the platform and + # the deployment target. You can optionally include the target after the platform. + # + + # s.platform = :ios + # s.platform = :ios, "7.0" + + # When using multiple platforms + s.ios.deployment_target = "7.0" + s.osx.deployment_target = "10.9" + # s.watchos.deployment_target = "2.0" + # s.tvos.deployment_target = "9.0" + + + # ――― Source Location ―――――――――――――――――――――――――――――――――――――――――――――――――――――――――― # + # + # Specify the location from where the source should be retrieved. + # Supports git, hg, bzr, svn and HTTP. + # + + s.source = { :git => "https://github.com/amosavian/FileProvider.git", :tag => "#{s.version}" } + + + # ――― Source Code ―――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――― # + # + # CocoaPods is smart about how it includes source code. For source files + # giving a folder will include any swift, h, m, mm, c & cpp files. + # For header files it will include any header in the folder. + # Not including the public_header_files will make all headers public. + # + + s.source_files = "Sources" + s.exclude_files = "Sources/Exclude" + + # s.public_header_files = "Classes/**/*.h" + + + # ――― Resources ―――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――― # + # + # A list of resources included with the Pod. These are copied into the + # target bundle with a build phase script. Anything else will be cleaned. + # You can preserve files from being cleaned, please don't preserve + # non-essential files like tests, examples and documentation. + # + + # s.resource = "icon.png" + # s.resources = "Resources/*.png" + + # s.preserve_paths = "FilesToSave", "MoreFilesToSave" + + + # ――― Project Linking ―――――――――――――――――――――――――――――――――――――――――――――――――――――――――― # + # + # Link your library with frameworks, or libraries. Libraries do not include + # the lib prefix of their name. + # + + # s.framework = "SomeFramework" + # s.frameworks = "SomeFramework", "AnotherFramework" + + # s.library = "iconv" + # s.libraries = "iconv", "xml2" + + + # ――― Project Settings ――――――――――――――――――――――――――――――――――――――――――――――――――――――――― # + # + # If your library depends on compiler flags you can set them in the xcconfig hash + # where they will only apply to your library. If you depend on other Podspecs + # you can include multiple dependencies to ensure it works. + + # s.requires_arc = true + + # s.xcconfig = { "HEADER_SEARCH_PATHS" => "$(SDKROOT)/usr/include/libxml2" } + # s.dependency "JSONKit", "~> 1.4" + +end diff --git a/README.md b/README.md index e330baf..3beb6ae 100644 --- a/README.md +++ b/README.md @@ -3,12 +3,12 @@ >This Swift library provide a swifty way to deal with local and remote files and directories in same way. [![Swift Version][swift-image]][swift-url] -[![Build Status][travis-image]][travis-url] [![License][license-image]][license-url] [![Platform](https://img.shields.io/badge/Platform-iOS%2C%20OSX-lightgray.svg)]() [![codebeat badge](https://codebeat.co/badges/7b359f48-78eb-4647-ab22-56262a827517)](https://codebeat.co/projects/github-com-amosavian-fileprovider) diff --git a/Source/SMB2Types.swift b/Source/SMB2Types.swift index 68fddb4..268e3b8 100644 --- a/Source/SMB2Types.swift +++ b/Source/SMB2Types.swift @@ -71,9 +71,8 @@ struct SMB2 { self.flags = flags.union([Flags.ASYNC_COMMAND]) self.nextCommand = nextCommand self.messageId = messageId - self.reserved = 0 - reserved = UInt32(asyncId & 0xffffffff) - treeId = UInt32(asyncId >> 32) + self.reserved = UInt32(asyncId & 0xffffffff) + self.treeId = UInt32(asyncId >> 32) self.sessionId = sessionId self.signature = signature } @@ -721,8 +720,7 @@ struct SMB2 { let endOfFile: UInt64 let fileAttributes: FileAttributes private let reserved2: UInt32 - let fileIdPersistent: UInt64 - let fileIdVolatile: UInt64 + let fileId: FileId let contextsOffset: UInt32 let ContextsLength: UInt32 } @@ -905,6 +903,11 @@ struct SMB2 { static let NO_SCRUB_DATA = FileAttributes(rawValue: 0x00020000) } + struct FileId { + let persistent: UInt64 + let volatile: UInt64 + } + // MARK: SMB2 Close struct CloseRequest: SMBRequest { @@ -1000,8 +1003,7 @@ struct SMB2 { let flags: ReadRequest.Flags let length: UInt32 let offset: UInt64 - let fileIdPersistent: UInt64 - let fileIdVolatile: UInt64 + let fileId: FileId let minimumLength: UInt32 private let _channel: UInt32 var channel: ReadRequest.Channel { @@ -1012,14 +1014,13 @@ struct SMB2 { private let channelInfoLength: UInt16 private let channelBuffer: UInt8 - init (fileIdPersistent: UInt64, fileIdVolatile: UInt64, offset: UInt64, length: UInt32, flags: ReadRequest.Flags = [], minimumLength: UInt32 = 0, remainingBytes: UInt32 = 0, channel: ReadRequest.Channel = .NONE) { + init (fileId: FileId, offset: UInt64, length: UInt32, flags: ReadRequest.Flags = [], minimumLength: UInt32 = 0, remainingBytes: UInt32 = 0, channel: ReadRequest.Channel = .NONE) { self.size = 49 self.padding = 0 self.flags = flags self.length = length self.offset = offset - self.fileIdPersistent = fileIdPersistent - self.fileIdVolatile = fileIdVolatile + self.fileId = fileId self.minimumLength = minimumLength self._channel = channel.rawValue self.remainingBytes = remainingBytes diff --git a/Source/SMBClient.swift b/Source/SMBClient.swift index 8aff01a..00c6098 100644 --- a/Source/SMBClient.swift +++ b/Source/SMBClient.swift @@ -226,7 +226,7 @@ protocol FileProviderSMBHeader { struct SMB1 { struct Header { // 32 bytes // header is always \u{ff}SMB - var protocolID: UInt32 + let protocolID: UInt32 static let protocolConst: UInt32 = 0x424d53ff private var _command: UInt8 var command: Command { diff --git a/Source/WebDAVFileProvider.swift b/Source/WebDAVFileProvider.swift index 67635a9..5625920 100644 --- a/Source/WebDAVFileProvider.swift +++ b/Source/WebDAVFileProvider.swift @@ -61,7 +61,7 @@ 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, FileProvider { +public class WebDAVFileProvider: NSObject, FileProviderBasic { public let type: String = "WebDAV" public let isPathRelative: Bool = true public let baseURL: NSURL? @@ -115,7 +115,7 @@ public class WebDAVFileProvider: NSObject, FileProvider { if attr.href.path == url.path { continue } - fileObjects.append(self.DavResponseToFileObject(attr)) + fileObjects.append(self.mapToFileObject(attr)) } completionHandler(contents: fileObjects, error: error) return @@ -137,7 +137,7 @@ public class WebDAVFileProvider: NSObject, FileProvider { if let data = data { let xresponse = self.parseXMLResponse(data) if let attr = xresponse.first { - completionHandler(attributes: self.DavResponseToFileObject(attr), error: error) + completionHandler(attributes: self.mapToFileObject(attr), error: error) return } } @@ -145,7 +145,9 @@ public class WebDAVFileProvider: NSObject, FileProvider { } task.resume() } - +} + +extension WebDAVFileProvider: FileProviderOperations { public func createFolder(folderName: String, atPath: String, completionHandler: SimpleCompletionHandler) { let session = NSURLSession(configuration: NSURLSessionConfiguration.defaultSessionConfiguration()) let url = absoluteURL((atPath as NSString).stringByAppendingPathComponent(folderName) + "/") @@ -158,13 +160,7 @@ public class WebDAVFileProvider: NSObject, FileProvider { return } completionHandler?(error: error) - dispatch_async(dispatch_get_main_queue(), { - if error == nil { - self.delegate?.fileproviderSucceed(self, operation: .Create(path: (atPath as NSString).stringByAppendingPathComponent(folderName) + "/")) - } else { - self.delegate?.fileproviderFailed(self, operation: .Create(path: (atPath as NSString).stringByAppendingPathComponent(folderName) + "/")) - } - }) + self.delegateNotify(.Create(path: (atPath as NSString).stringByAppendingPathComponent(folderName) + "/"), error: error) } task.resume() } @@ -174,13 +170,7 @@ public class WebDAVFileProvider: NSObject, FileProvider { request.HTTPMethod = "PUT" let task = session.uploadTaskWithRequest(request, fromData: data) { (data, response, error) in completionHandler?(error: error) - dispatch_async(dispatch_get_main_queue(), { - if error == nil { - self.delegate?.fileproviderSucceed(self, operation: .Create(path: (path as NSString).stringByAppendingPathComponent(fileAttribs.name))) - } else { - self.delegate?.fileproviderFailed(self, operation: .Create(path: (path as NSString).stringByAppendingPathComponent(fileAttribs.name))) - } - }) + self.delegateNotify(.Create(path: (path as NSString).stringByAppendingPathComponent(fileAttribs.name)), error: error) } task.taskDescription = self.dictionaryToJSON(["type": "Create", "source": (path as NSString).stringByAppendingPathComponent(fileAttribs.name)]) task.resume() @@ -199,13 +189,7 @@ public class WebDAVFileProvider: NSObject, FileProvider { let task = session.dataTaskWithRequest(request) { (data, response, error) in if let response = response as? NSHTTPURLResponse, let code = FileProviderWebDavErrorCode(rawValue: response.statusCode) { defer { - dispatch_async(dispatch_get_main_queue(), { - if error == nil { - self.delegate?.fileproviderSucceed(self, operation: .Move(source: path, destination: toPath)) - } else { - self.delegate?.fileproviderFailed(self, operation: .Move(source: path, destination: toPath)) - } - }) + self.delegateNotify(.Move(source: path, destination: toPath), error: error) } if code == .MultiStatus, let data = data { let xresponses = self.parseXMLResponse(data) @@ -237,13 +221,7 @@ public class WebDAVFileProvider: NSObject, FileProvider { let task = session.dataTaskWithRequest(request) { (data, response, error) in if let response = response as? NSHTTPURLResponse, let code = FileProviderWebDavErrorCode(rawValue: response.statusCode) { defer { - dispatch_async(dispatch_get_main_queue(), { - if error == nil { - self.delegate?.fileproviderSucceed(self, operation: .Copy(source: path, destination: toPath)) - } else { - self.delegate?.fileproviderFailed(self, operation: .Copy(source: path, destination: toPath)) - } - }) + self.delegateNotify(.Copy(source: path, destination: toPath), error: error) } if code == .MultiStatus, let data = data { let xresponses = self.parseXMLResponse(data) @@ -271,13 +249,7 @@ public class WebDAVFileProvider: NSObject, FileProvider { let task = session.dataTaskWithRequest(request) { (data, response, error) in if let response = response as? NSHTTPURLResponse, let code = FileProviderWebDavErrorCode(rawValue: response.statusCode) { defer { - dispatch_async(dispatch_get_main_queue(), { - if error == nil { - self.delegate?.fileproviderSucceed(self, operation: .Remove(path: path)) - } else { - self.delegate?.fileproviderFailed(self, operation: .Remove(path: path)) - } - }) + self.delegateNotify(.Remove(path: path), error: error) } if code == .MultiStatus, let data = data { let xresponses = self.parseXMLResponse(data) @@ -301,13 +273,7 @@ public class WebDAVFileProvider: NSObject, FileProvider { request.HTTPMethod = "PUT" let task = session.uploadTaskWithRequest(request, fromFile: localFile) { (data, response, error) in completionHandler?(error: error) - dispatch_async(dispatch_get_main_queue(), { - if error == nil { - self.delegate?.fileproviderSucceed(self, operation: .Move(source: localFile.absoluteString, destination: toPath)) - } else { - self.delegate?.fileproviderFailed(self, operation: .Move(source: localFile.absoluteString, destination: toPath)) - } - }) + self.delegateNotify(.Move(source: localFile.absoluteString, destination: toPath), error: error) } task.taskDescription = self.dictionaryToJSON(["type": "Copy", "source": localFile.absoluteString, "dest": toPath]) task.resume() @@ -329,7 +295,9 @@ public class WebDAVFileProvider: NSObject, FileProvider { task.taskDescription = self.dictionaryToJSON(["type": "Copy", "source": path, "dest": toLocalURL.absoluteString]) task.resume() } - +} + +extension WebDAVFileProvider: FileProviderReadWrite { public func contentsAtPath(path: String, completionHandler: ((contents: NSData?, error: ErrorType?) -> Void)) { let request = NSMutableURLRequest(URL: absoluteURL(path)) request.HTTPMethod = "GET" @@ -357,13 +325,7 @@ public class WebDAVFileProvider: NSObject, FileProvider { request.HTTPMethod = "PUT" let task = session.uploadTaskWithRequest(request, fromData: data) { (data, response, error) in defer { - dispatch_async(dispatch_get_main_queue(), { - if error == nil { - self.delegate?.fileproviderSucceed(self, operation: .Modify(path: path)) - } else { - self.delegate?.fileproviderFailed(self, operation: .Modify(path: path)) - } - }) + self.delegateNotify(.Modify(path: path), error: error) } if atomically { self.moveItemAtPath((path as NSString).stringByAppendingPathExtension("tmp")!, toPath: path, completionHandler: completionHandler) @@ -395,7 +357,7 @@ public class WebDAVFileProvider: NSObject, FileProvider { if let path = attr.href.path where !((path as NSString).lastPathComponent.containsString(query)) { continue } - let fileObject = self.DavResponseToFileObject(attr) + let fileObject = self.mapToFileObject(attr) fileObjects.append(fileObject) foundItemHandler?(fileObject) } @@ -500,13 +462,12 @@ internal extension WebDAVFileProvider { result.append(DavResponse(href: hrefURL, hrefString: href, status: status, prop: propDic)) } } - } catch _ { - + } catch _ { } return result } - private func DavResponseToFileObject(davResponse: DavResponse) -> WebDavFileObject { + private func mapToFileObject(davResponse: DavResponse) -> WebDavFileObject { var href = davResponse.href if href.baseURL == nil { href = absoluteURL(href.path ?? "") @@ -520,6 +481,16 @@ internal extension WebDAVFileProvider { let entryTag = davResponse.prop["getetag"] return WebDavFileObject(absoluteURL: href, name: name, size: size, contentType: contentType, createdDate: createdDate, modifiedDate: modifiedDate, fileType: isDirectory ? .Directory : .Regular, isHidden: false, isReadOnly: false, entryTag: entryTag) } + + private func delegateNotify(operation: FileOperation, error: ErrorType?) { + dispatch_async(dispatch_get_main_queue(), { + if error == nil { + self.delegate?.fileproviderSucceed(self, operation: operation) + } else { + self.delegate?.fileproviderFailed(self, operation: operation) + } + }) + } } // MARK: URLSession delegate