From cf0b324dfa625f2b10e6dfc289d6343e42216f28 Mon Sep 17 00:00:00 2001 From: Amir Abbas Mousavian Date: Wed, 6 Jul 2016 12:44:57 +0430 Subject: [PATCH] updated readme, copyright - broke FileProvider protocol --- README.md | 101 +++++++++++++++++++++----------- Source/FileProvider.swift | 70 ++++++++++++++++++---- Source/LocalFileProvider.swift | 8 +-- Source/SMB2Types.swift | 37 +++++------- Source/SMBClient.swift | 10 ++-- Source/SMBFileProvider.swift | 8 +-- Source/TCPSocketClient.swift | 37 ++++++++---- Source/WebDAVFileProvider.swift | 60 ++++--------------- 8 files changed, 191 insertions(+), 140 deletions(-) diff --git a/README.md b/README.md index 29e9181..e330baf 100644 --- a/README.md +++ b/README.md @@ -1,48 +1,66 @@ # FileProvider (experimental) -This Swift library provide a swifty way to deal with local and remote files and directories in same way. This library provides implementaion of WebDav and SMB/CIFS (incomplete) and local files. +>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) + + + + + +This library provides implementaion of WebDav and SMB/CIFS (incomplete) and local files. All functions are async calls and it wont block your main thread. +## Features + +- [x] **LocalFileProvider** a wrapper for `NSFileManager` with some additions like searching and reading a portion of file +- [x] **WebDAVFileProvider** WebDAV protocol is usual file transmission system on Macs +- [ ] **SMBFileProvider** SMB/CIFS and SMB2/3 are file and printer sharing protocol which is originated from IBM & Microsoft and SMB2/3 is now replacing AFP protocol on MacOS. I Implemented data types and some basic functions but *main interface is not implemented yet!* +- [ ] **DropboxFileProvider** +- [ ] **FTPFileProvider** +- [ ] **AmazonS3FileProvider** + +## Requirements + +- **Swift 2.2** +- iOS 7.0 , OSX 10.9 +- XCode 7.3 + ## Installation -#### Manually -Copy Source folder to your project! +### Cocoapods / Carthage / Swift Package Manager -#### Git clone -Use this command on terminal to get a clone: +I will add when project is completed is ready to use in production envioronment + +### Git clone +To have latest updates with ease, use this command on terminal to get a clone: git clone https://github.com/amosavian/FileProvider FileProvider + +You can update your library using this command in FileProvider folder: -#### Submodule into your project -Use this command if you have a git based project in your projects directory: + git pull + +### Submodule into your project +if you have a git based project, use this command in your projects directory: git submodule add https://github.com/amosavian/FileProvider FileProvider -#### Cocoapods / Carthage / Swift Package Manager - -I will add when project is completed is ready to use in production envioronment +### Manually +Copy Source folder to your project and voila! ## Usage Each provider has a specific class which conforms to FileProvider protocol and share same syntax -### Providers - -For now this providers are supported: - -**LocalFileProvider :** a wrapper for `NSFileManager` with some additions like searching and reading a portion of file - -**WebDAVFileProvider :** WebDAV protocol is usual file transmission system on Macs - -**SMBFileProvider :** SMB/CIFS and SMB2/3 are file and printer sharing protocol which is originated from Windows and SMB2/3 is now replacing AFP protocol on MacOS. I Implemented data types and some basic functions but *main interface is not implemented yet!* - -**FTPFileProvider :** not implemented yet! - -**DropboxFileProvider :** not implemented yet! - -Your pull requests are welcomed! - ### Initialization For LocalFileProvider if you want to deal with `Documents` folder @@ -191,12 +209,29 @@ If you want to retrieve a portion of file you should can `contentsAtPath` method let data = "What's up Newyork!".dataUsingEncoding(NSUTF8StringEncoding) documentsFileProvider.writeContentsAtPath(path: "old.txt", contents data: data, atomically: true, completionHandler: nil) +### Monitoring FIle Changes -### TODO List +## Contribute -- [ ] TCPSocket test (using telnet) -- [ ] SMB2 protocol support using TCPSocketClient -- [ ] FTP/FTPS protocol support using TCPSocketClient -- [ ] Dropbox support -- [ ] Amazon S3 support -- [ ] SMB1 protocol implementation \ No newline at end of file +We would love for you to contribute to **FileProvider**, check the `LICENSE` file for more info. + +## Meta + +Amir-Abbas Mousavia – [@amosavian](https://twitter.com/amosavian) + +Distributed under the MIT license. See `LICENSE` for more information. + +[https://github.com/yourname/github-link](https://github.com/dbader/) + +[swift-image]:https://img.shields.io/badge/swift-2.3-green.svg +[swift-url]: https://swift.org/ +[license-image]: https://img.shields.io/badge/License-MIT-blue.svg +[license-url]: LICENSE + + + +[codebeat-image]: https://codebeat.co/badges/c19b47ea-2f9d-45df-8458-b2d952fe9dad +[codebeat-url]: https://codebeat.co/projects/github-com-vsouza-awesomeios-com diff --git a/Source/FileProvider.swift b/Source/FileProvider.swift index 88b58b3..f8891d1 100644 --- a/Source/FileProvider.swift +++ b/Source/FileProvider.swift @@ -1,9 +1,9 @@ // // FileProvider.swift -// ExtDownloader +// FileProvider // -// Created by Amir Abbas Mousavian on 3/28/95. -// Copyright © 1395 Mousavian. All rights reserved. +// Created by Amir Abbas Mousavian. +// Copyright © 2016 Mousavian. Distributed under MIT license. // import Foundation @@ -11,7 +11,7 @@ import Foundation import UIKit #endif #if (OSX) -import Cocoa +import AppKit #endif public enum FileType: String { @@ -96,7 +96,7 @@ public class FileObject { public typealias SimpleCompletionHandler = ((error: ErrorType?) -> Void)? -public protocol FileProvider: class { +public protocol FileProviderBasic: class { var type: String { get } var isPathRelative: Bool { get } var baseURL: NSURL? { get } @@ -110,7 +110,9 @@ public protocol FileProvider: class { */ func contentsOfDirectoryAtPath(path: String, completionHandler: ((contents: [FileObject], error: ErrorType?) -> Void)) func attributesOfItemAtPath(path: String, completionHandler: ((attributes: FileObject?, error: ErrorType?) -> Void)) - +} + +public protocol FileProviderOperations: FileProviderBasic { 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) @@ -119,18 +121,26 @@ public protocol FileProvider: class { func copyLocalFileToPath(localFile: NSURL, toPath: String, completionHandler: SimpleCompletionHandler) func copyPathToLocalFile(path: String, toLocalURL: NSURL, 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 searchFilesAtPath(path: String, recursive: Bool, query: String, foundItemHandler: ((FileObject) -> Void)?, completionHandler: ((files: [FileObject], error: ErrorType?) -> Void)) - +} + +public protocol FileProviderMonitor: FileProviderBasic { func registerNotifcation(path: String, eventHandler: (() -> Void)) func unregisterNotifcation(path: String) } -extension FileProvider { +public protocol FileProvider: FileProviderBasic, FileProviderOperations, FileProviderReadWrite { + +} + +extension FileProviderBasic { var bareCurrentPath: String { return currentPath.stringByTrimmingCharactersInSet(NSCharacterSet(charactersInString: ". /")) } @@ -170,6 +180,42 @@ extension FileProvider { internal func NotImplemented() { assert(false, "method not implemented") } + + internal func resolveRFCDate(httpDateString: String) -> NSDate? { + let dateFor: NSDateFormatter = NSDateFormatter() + dateFor.locale = NSLocale(localeIdentifier: "en_US") + dateFor.dateFormat = "EEE',' dd' 'MMM' 'yyyy HH':'mm':'ss zzz" + if let rfc1123 = dateFor.dateFromString(httpDateString) { + return rfc1123 + } + dateFor.dateFormat = "EEEE',' dd'-'MMM'-'yy HH':'mm':'ss z" + if let rfc850 = dateFor.dateFromString(httpDateString) { + return rfc850 + } + dateFor.dateFormat = "EEE MMM d HH':'mm':'ss yyyy" + if let asctime = dateFor.dateFromString(httpDateString) { + return asctime + } + //self.init() + return nil + } + + internal func jsonToDictionary(jsonString: String) -> [String: AnyObject]? { + guard let data = jsonString.dataUsingEncoding(NSUTF8StringEncoding) else { + return nil + } + if let dic = try? NSJSONSerialization.JSONObjectWithData(data, options: NSJSONReadingOptions()) 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) + } + return nil + } } #if (iOS) @@ -194,9 +240,9 @@ public enum FileOperation { } public protocol FileProviderDelegate { - func fileproviderSucceed(fileProvider: FileProvider, operation: FileOperation) - func fileproviderFailed(fileProvider: FileProvider, operation: FileOperation) - func fileproviderProgress(fileProvider: FileProvider, operation: FileOperation, progress: Float) + func fileproviderSucceed(fileProvider: FileProviderOperations, operation: FileOperation) + func fileproviderFailed(fileProvider: FileProviderOperations, operation: FileOperation) + func fileproviderProgress(fileProvider: FileProviderOperations, operation: FileOperation, progress: Float) } diff --git a/Source/LocalFileProvider.swift b/Source/LocalFileProvider.swift index bf669b3..c88c2ff 100644 --- a/Source/LocalFileProvider.swift +++ b/Source/LocalFileProvider.swift @@ -1,9 +1,9 @@ // // LocalFileProvider.swift -// ExtDownloader +// FileProvider // -// Created by Amir Abbas Mousavian on 3/29/95. -// Copyright © 1395 Mousavian. All rights reserved. +// Created by Amir Abbas Mousavian. +// Copyright © 2016 Mousavian. Distributed under MIT license. // import Foundation @@ -17,7 +17,7 @@ public final class LocalFileObject: FileObject { } } -public class LocalFileProvider: FileProvider { +public class LocalFileProvider: FileProvider, FileProviderMonitor { public let type = "NSFileManager" public var isPathRelative: Bool = true public var baseURL: NSURL? = LocalFileProvider.defaultBaseURL() diff --git a/Source/SMB2Types.swift b/Source/SMB2Types.swift index 79a3f19..68fddb4 100644 --- a/Source/SMB2Types.swift +++ b/Source/SMB2Types.swift @@ -1,9 +1,9 @@ // // SMB2Types.swift -// ExtDownloader +// FileProvider // -// Created by Amir Abbas Mousavian on 4/15/95. -// Copyright © 1395 Mousavian. All rights reserved. +// Created by Amir Abbas Mousavian. +// Copyright © 2016 Mousavian. Distributed under MIT license. // import Foundation @@ -12,12 +12,12 @@ import Foundation struct SMB2 { struct Header: FileProviderSMBHeader { // 64 bytes // header is always \u{fe}SMB - var protocolID: UInt32 + let protocolID: UInt32 static let protocolConst: UInt32 = 0x424d53fe - var size: UInt16 - var creditCharge: UInt16 + let size: UInt16 + let creditCharge: UInt16 // error messages from the server to the client - var status: UInt32 + let status: UInt32 enum StatusSeverity: UInt8 { case Success = 0, Information, Warning, Error } @@ -25,32 +25,25 @@ struct SMB2 { let severity = StatusSeverity(rawValue: UInt8(status >> 30))! return (severity, status & 0x20000000 != 0, UInt16((status & 0x0FFF0000) >> 16), UInt16(status & 0x0000FFFF)) } - private var _command: UInt16 + private let _command: UInt16 var command: Command { get { return Command(rawValue: _command) ?? .INVALID } - set { - _command = newValue.rawValue - } } - var creditRequestResponse: UInt16 - var flags: Flags + let creditRequestResponse: UInt16 + let flags: Flags var nextCommand: UInt32 - var messageId: UInt64 - private var reserved: UInt32 - var treeId: UInt32 + let messageId: UInt64 + private let reserved: UInt32 + let treeId: UInt32 var asyncId: UInt64 { get { return UInt64(reserved) + (UInt64(treeId) << 32) } - set { - reserved = UInt32(newValue & 0xffffffff) - treeId = UInt32(newValue >> 32) - } } - var sessionId: UInt64 - var signature: (UInt64, UInt64) + let sessionId: UInt64 + let signature: (UInt64, UInt64) init(command: Command, status: NTStatus = .SUCCESS, creditCharge: UInt16 = 0, creditRequestResponse: UInt16, flags: Flags = [], nextCommand: UInt32 = 0, messageId: UInt64, treeId: UInt32, sessionId: UInt64, signature: (UInt64, UInt64) = (0, 0)) { self.protocolID = self.dynamicType.protocolConst diff --git a/Source/SMBClient.swift b/Source/SMBClient.swift index 2f6e088..8aff01a 100644 --- a/Source/SMBClient.swift +++ b/Source/SMBClient.swift @@ -1,9 +1,9 @@ // // SMBTransmitter.swift -// ExtDownloader +// FileProvider // -// Created by Amir Abbas Mousavian on 4/10/95. -// Copyright © 1395 Mousavian. All rights reserved. +// Created by Amir Abbas Mousavian. +// Copyright © 2016 Mousavian. Distributed under MIT license. // import Foundation @@ -45,7 +45,7 @@ class SMBProtocolClient: TCPSocketClient { } catch _ { return nil } - self.waitForResponse() + self.waitUntilResponse() let response = try? SMBProtocolClient.digestSMB2Message(dataReceived) return response??.message as? SMB2.NegotiateResponse } @@ -218,7 +218,7 @@ struct SMBTime { } protocol FileProviderSMBHeader { - var protocolID: UInt32 { get set } + var protocolID: UInt32 { get } static var protocolConst: UInt32 { get } } diff --git a/Source/SMBFileProvider.swift b/Source/SMBFileProvider.swift index 5378774..0756108 100644 --- a/Source/SMBFileProvider.swift +++ b/Source/SMBFileProvider.swift @@ -1,14 +1,14 @@ // // SambaFileProvider.swift -// ExtDownloader +// FileProvider // -// Created by Amir Abbas Mousavian on 3/29/95. -// Copyright © 1395 Mousavian. All rights reserved. +// Created by Amir Abbas Mousavian. +// Copyright © 2016 Mousavian. Distributed under MIT license. // import Foundation -class SMBFileProvider: FileProvider { +class SMBFileProvider: FileProvider, FileProviderMonitor { var type: String = "Samba" var isPathRelative: Bool = true var baseURL: NSURL? diff --git a/Source/TCPSocketClient.swift b/Source/TCPSocketClient.swift index 60e91cd..56d16b1 100644 --- a/Source/TCPSocketClient.swift +++ b/Source/TCPSocketClient.swift @@ -1,9 +1,9 @@ // // SocketTransmitter.swift -// ExtDownloader +// FileProvider // -// Created by Amir Abbas Mousavian on 4/9/95. -// Copyright © 1395 Mousavian. All rights reserved. +// Created by Amir Abbas Mousavian. +// Copyright © 2016 Mousavian. Distributed under MIT license. // import Foundation @@ -39,7 +39,7 @@ public class TCPSocketClient: NSObject, NSStreamDelegate { public let secureConnection: Bool /// server's ports which is value between 1 to 65535 private let port: UInt32 - private var connected = false + private var open = false /** * - parameter baseURL: a url with valid scheme, dns or ip host and ports @@ -126,14 +126,14 @@ public class TCPSocketClient: NSObject, NSStreamDelegate { public func stream(aStream: NSStream, handleEvent eventCode: NSStreamEvent) { switch (eventCode) { case NSStreamEvent.ErrorOccurred: - connected = false + open = false case NSStreamEvent.EndEncountered: break case NSStreamEvent.None: break case NSStreamEvent.OpenCompleted: let activeStatus: [NSStreamStatus] = [.Open, .Reading, .Writing, .AtEnd] - connected = activeStatus.contains(inputStream?.streamStatus ?? .NotOpen) && activeStatus.contains(outputStream?.streamStatus ?? .NotOpen) + open = activeStatus.contains(inputStream?.streamStatus ?? .NotOpen) && activeStatus.contains(outputStream?.streamStatus ?? .NotOpen) case NSStreamEvent.HasBytesAvailable: var buffer = [UInt8](count: 2048, repeatedValue: 0) if ( aStream == inputStream) { @@ -165,13 +165,16 @@ public class TCPSocketClient: NSObject, NSStreamDelegate { */ public func send(data data: NSData?) throws { - if self.outputStream?.hasSpaceAvailable ?? false { + guard let outputStream = outputStream else { + return + } + if outputStream.hasSpaceAvailable ?? false { if let data = data { dataToBeSent.appendData(data) } if dataToBeSent.length > 0 { - let bytesWritten = self.outputStream?.write(UnsafePointer(dataToBeSent.bytes), maxLength: dataToBeSent.length) ?? -1 + let bytesWritten = outputStream.write(UnsafePointer(dataToBeSent.bytes), maxLength: dataToBeSent.length) ?? -1 if bytesWritten > 0 { let range = NSRange(location: 0, length: bytesWritten) dataToBeSent.replaceBytesInRange(range, withBytes: nil, length: 0) @@ -187,14 +190,23 @@ public class TCPSocketClient: NSObject, NSStreamDelegate { } } + /** + * Clears entire send and receive buffer + */ + + public func flush() { + dataToBeSent.length = 0 + dataReceived.length = 0 + } + /** * Put's thread in sleep until all data is sent * **Note:** Don't call this method from main thread */ - internal func waitForSendDataPurge() { + internal func waitUntillDataSent() { if NSThread.isMainThread() { - assertionFailure("waitForSendDataPurge() method can't be called from main thread") + assert(false, "waitUntillDataSent() method can't be called from main thread") } while true { if dataToBeSent.length == 0 { @@ -213,9 +225,9 @@ public class TCPSocketClient: NSObject, NSStreamDelegate { * - returns: A Bool value indicates all response loaded from server successfullt */ - internal func waitForResponse() -> Bool { + internal func waitUntilResponse() -> Bool { if NSThread.isMainThread() { - assertionFailure("waitForResponse() method can't be called from main thread") + assert(false, "waitUntilResponse() method can't be called from main thread") } var finished = false while !finished { @@ -230,7 +242,6 @@ public class TCPSocketClient: NSObject, NSStreamDelegate { } NSRunLoop.currentRunLoop().runUntilDate(NSDate(timeIntervalSinceNow: 0.1)); - NSThread.currentThread() NSThread.sleepForTimeInterval(0.1) } return false diff --git a/Source/WebDAVFileProvider.swift b/Source/WebDAVFileProvider.swift index ade2b54..67635a9 100644 --- a/Source/WebDAVFileProvider.swift +++ b/Source/WebDAVFileProvider.swift @@ -1,9 +1,9 @@ // // WebDAVFileProvider.swift -// ExtDownloader +// FileProvider // -// Created by Amir Abbas Mousavian on 4/6/95. -// Copyright © 1395 Mousavian. All rights reserved. +// Created by Amir Abbas Mousavian. +// Copyright © 2016 Mousavian. Distributed under MIT license. // import Foundation @@ -407,8 +407,7 @@ public class WebDAVFileProvider: NSObject, FileProvider { task.resume() } - public func registerNotifcation(path: String, eventHandler: (() -> Void)) { - NotImplemented() + private 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. @@ -416,8 +415,8 @@ public class WebDAVFileProvider: NSObject, FileProvider { * with previous results */ } - public func unregisterNotifcation(path: String) { - NotImplemented() + private func unregisterNotifcation(path: String) { + } // TODO: implements methods for lock mechanism } @@ -507,53 +506,20 @@ internal extension WebDAVFileProvider { return result } - func DavResponseToFileObject(davResponse: DavResponse) -> WebDavFileObject { - let href = davResponse.href + private func DavResponseToFileObject(davResponse: DavResponse) -> WebDavFileObject { + var href = davResponse.href + if href.baseURL == nil { + href = absoluteURL(href.path ?? "") + } let name = davResponse.prop["displayname"] ?? (davResponse.hrefString.stringByRemovingPercentEncoding! as NSString).lastPathComponent let size = Int64(davResponse.prop["getcontentlength"] ?? "-1") ?? NSURLSessionTransferSizeUnknown - let createdDate = self.resolveHTTPDate(davResponse.prop["creationdate"] ?? "") - let modifiedDate = self.resolveHTTPDate(davResponse.prop["getlastmodified"] ?? "") + let createdDate = self.resolveRFCDate(davResponse.prop["creationdate"] ?? "") + let modifiedDate = self.resolveRFCDate(davResponse.prop["getlastmodified"] ?? "") let contentType = davResponse.prop["getcontenttype"] ?? "octet/stream" let isDirectory = contentType == "httpd/unix-directory" let entryTag = davResponse.prop["getetag"] return WebDavFileObject(absoluteURL: href, name: name, size: size, contentType: contentType, createdDate: createdDate, modifiedDate: modifiedDate, fileType: isDirectory ? .Directory : .Regular, isHidden: false, isReadOnly: false, entryTag: entryTag) } - - func resolveHTTPDate(httpDateString: String) -> NSDate? { - let dateFor: NSDateFormatter = NSDateFormatter() - dateFor.locale = NSLocale(localeIdentifier: "en_US") - dateFor.dateFormat = "EEE',' dd' 'MMM' 'yyyy HH':'mm':'ss zzz" - if let rfc1123 = dateFor.dateFromString(httpDateString) { - return rfc1123 - } - dateFor.dateFormat = "EEEE',' dd'-'MMM'-'yy HH':'mm':'ss z" - if let rfc850 = dateFor.dateFromString(httpDateString) { - return rfc850 - } - dateFor.dateFormat = "EEE MMM d HH':'mm':'ss yyyy" - if let asctime = dateFor.dateFromString(httpDateString) { - return asctime - } - //self.init() - return nil - } - - func jsonToDictionary(jsonString: String) -> [String: AnyObject]? { - guard let data = jsonString.dataUsingEncoding(NSUTF8StringEncoding) else { - return nil - } - if let dic = try? NSJSONSerialization.JSONObjectWithData(data, options: NSJSONReadingOptions()) as? [String: AnyObject] { - return dic - } - return nil - } - - func dictionaryToJSON(dictionary: [String: AnyObject]) -> String? { - if let data = try? NSJSONSerialization.dataWithJSONObject(dictionary, options: NSJSONWritingOptions()) { - return String(data: data, encoding: NSUTF8StringEncoding) - } - return nil - } } // MARK: URLSession delegate