SocketTransmitter class
This commit is contained in:
@@ -2,7 +2,7 @@
|
||||
|
||||
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.
|
||||
|
||||
All functions are async recalls and it wont block your main thread.
|
||||
All functions are async calls and it wont block your main thread.
|
||||
|
||||
## Installation
|
||||
|
||||
@@ -16,11 +16,11 @@ Each provider has a specific class which conforms to FileProvider protocol and s
|
||||
|
||||
For now this providers are supported:
|
||||
|
||||
**LocalFileProvider :** a wrapper for NSFileManager with some additions like searching and reading a portion of file
|
||||
**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 too. I Implemented data types and some basic functions but main interface is not implemented yet!
|
||||
**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!*
|
||||
|
||||
**DropboxFileProvider :** not implemented yet!
|
||||
|
||||
@@ -51,6 +51,8 @@ For interaction with UI, set delegate variable of `FileProvider` object
|
||||
|
||||
For updating User interface please consider using delegate method instead of completion handlers. Delegate methods are guaranteed to run in main thread to avoid bugs.
|
||||
|
||||
Your class should conforms `FileProviderDelegate` class:
|
||||
|
||||
override func viewDidLoad() {
|
||||
documentsFileProvider.delegate = self
|
||||
}
|
||||
@@ -126,7 +128,7 @@ Creating new file from data stream:
|
||||
|
||||
documentsFileProvider.removeItemAtPath(path: "new.txt", completionHandler: nil)
|
||||
|
||||
Caution: This method will not delete directories with content.
|
||||
***Caution:*** This method will not delete directories with content.
|
||||
|
||||
|
||||
### Retrieve Content of File
|
||||
@@ -142,7 +144,7 @@ THere is two method for this purpose, one of them loads entire file into NSData
|
||||
|
||||
If you want to retrieve a portion of file you should can `contentsAtPath` method with offset and length arguments. Please note first byte of file has offset: 0.
|
||||
|
||||
documentsFileProvider.contentsAtPath(path: "old.txt:, offset: 2, length: 5, completionHandler: {
|
||||
documentsFileProvider.contentsAtPath(path: "old.txt", offset: 2, length: 5, completionHandler: {
|
||||
(contents: NSData?, error: ErrorType?) -> Void
|
||||
if let contents = contents {
|
||||
print(String(data: contents, encoding: NSUTF8StringEncoding)) // "llo w"
|
||||
@@ -152,6 +154,6 @@ If you want to retrieve a portion of file you should can `contentsAtPath` method
|
||||
### Write Data To Files
|
||||
|
||||
let data = "What's up Newyork!".dataUsingEncoding(NSUTF8StringEncoding)
|
||||
documentsFileProvider.writeContentsAtPath(path: "old, contents data: data, atomically: true, completionHandler: nil)
|
||||
documentsFileProvider.writeContentsAtPath(path: "old.txt", contents data: data, atomically: true, completionHandler: nil)
|
||||
|
||||
|
||||
|
||||
@@ -158,7 +158,7 @@ extension FileProvider {
|
||||
}
|
||||
|
||||
func NotImplemented() {
|
||||
assert(false, "method not implemented")
|
||||
assertionFailure("method not implemented")
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -21,7 +21,7 @@ func decode<T>(data: NSData) -> T {
|
||||
return pointer.move()
|
||||
}
|
||||
|
||||
class SMBFileProvider: NSObject, FileProvider, NSStreamDelegate {
|
||||
class SMBFileProvider: FileProvider {
|
||||
var type: String = "Samba"
|
||||
var isPathRelative: Bool = true
|
||||
var baseURL: NSURL?
|
||||
@@ -32,9 +32,6 @@ class SMBFileProvider: NSObject, FileProvider, NSStreamDelegate {
|
||||
|
||||
typealias FileObjectClass = FileObject
|
||||
|
||||
var inputStream: NSInputStream?
|
||||
var outputStream: NSOutputStream?
|
||||
|
||||
init? (baseURL: NSURL, credential: NSURLCredential, afterInitialized: SimpleCompletionHandler) {
|
||||
guard baseURL.scheme.lowercaseString == "smb" else {
|
||||
return nil
|
||||
@@ -43,79 +40,12 @@ class SMBFileProvider: NSObject, FileProvider, NSStreamDelegate {
|
||||
dispatch_queue = dispatch_queue_create("FileProvider.\(type)", DISPATCH_QUEUE_CONCURRENT)
|
||||
//let url = baseURL.absoluteString
|
||||
self.credential = credential
|
||||
super.init()
|
||||
|
||||
connect()
|
||||
}
|
||||
|
||||
func connect() -> Bool {
|
||||
guard let hostStr = self.baseURL?.host else {
|
||||
return false
|
||||
}
|
||||
var readStream : Unmanaged<CFReadStream>?
|
||||
var writeStream : Unmanaged<CFWriteStream>?
|
||||
let host : CFString = NSString(string: hostStr)
|
||||
let port : UInt32 = self.baseURL?.port?.unsignedIntValue ?? 80
|
||||
|
||||
CFStreamCreatePairWithSocketToHost(kCFAllocatorDefault, host, port, &readStream, &writeStream)
|
||||
|
||||
inputStream = readStream?.takeRetainedValue()
|
||||
outputStream = writeStream?.takeRetainedValue()
|
||||
|
||||
guard let inputStream = inputStream, outputStream = outputStream else {
|
||||
return false
|
||||
}
|
||||
|
||||
inputStream.delegate = self
|
||||
outputStream.delegate = self
|
||||
|
||||
inputStream.scheduleInRunLoop(NSRunLoop.currentRunLoop(), forMode: NSDefaultRunLoopMode)
|
||||
outputStream.scheduleInRunLoop(NSRunLoop.currentRunLoop(), forMode: NSDefaultRunLoopMode)
|
||||
|
||||
inputStream.open()
|
||||
outputStream.open()
|
||||
return true
|
||||
}
|
||||
|
||||
func stream(aStream: NSStream, handleEvent eventCode: NSStreamEvent) {
|
||||
switch (eventCode) {
|
||||
case NSStreamEvent.ErrorOccurred:
|
||||
NSLog("ErrorOccurred")
|
||||
case NSStreamEvent.EndEncountered:
|
||||
NSLog("EndEncountered")
|
||||
case NSStreamEvent.None:
|
||||
NSLog("None")
|
||||
case NSStreamEvent.HasBytesAvailable:
|
||||
NSLog("HasBytesAvaible")
|
||||
var buffer = [UInt8](count: 4096, repeatedValue: 0)
|
||||
if ( aStream == inputStream) {
|
||||
while (inputStream!.hasBytesAvailable ?? false) {
|
||||
let len = inputStream!.read(&buffer, maxLength: buffer.count)
|
||||
if(len > 0) {
|
||||
let output = NSString(bytes: &buffer, length: buffer.count, encoding: NSUTF8StringEncoding)
|
||||
if (output != ""){
|
||||
NSLog("server said: %@", output!)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
case NSStreamEvent.OpenCompleted:
|
||||
NSLog("OpenCompleted")
|
||||
case NSStreamEvent.HasSpaceAvailable:
|
||||
NSLog("HasSpaceAvailable")
|
||||
default:
|
||||
NSLog("allZeros")
|
||||
}
|
||||
}
|
||||
|
||||
deinit {
|
||||
inputStream?.setValue(kCFBooleanTrue, forKey: kCFStreamPropertyShouldCloseNativeSocket as String)
|
||||
outputStream?.setValue(kCFBooleanTrue, forKey: kCFStreamPropertyShouldCloseNativeSocket as String)
|
||||
}
|
||||
|
||||
func contentsOfDirectoryAtPath(path: String, completionHandler: ((contents: [FileObjectClass], error: ErrorType?) -> Void)) {
|
||||
NotImplemented()
|
||||
dispatch_async(dispatch_queue) {
|
||||
self.NotImplemented()
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -257,6 +187,21 @@ extension SMBFileProvider {
|
||||
return result
|
||||
}
|
||||
|
||||
func negotiateAndConnectV2() -> UInt64? {
|
||||
guard let sockt = TCPSocketTransmitter(baseURL: self.baseURL!) else {
|
||||
return nil
|
||||
}
|
||||
sockt.connect()
|
||||
let negHeader = HeaderV2(command: .NEGOTIATE, creditRequestResponse: 0, messageId: 0, treeId: 0, sessionId: 0)
|
||||
let negMessage = NSData()
|
||||
self.createSMBMessage(negHeader, blocks: [(params: nil, message: negMessage)])
|
||||
_ = try? sockt.send(data: nil)
|
||||
sockt.waitForResponse()
|
||||
print(sockt.dataRecieved)
|
||||
|
||||
return 0
|
||||
}
|
||||
|
||||
private func smbTimeToUnix(smbTime: UInt64) -> UInt {
|
||||
return UInt(smbTime / 10000000) - 11644473600
|
||||
}
|
||||
@@ -538,7 +483,7 @@ extension SMBFileProvider {
|
||||
var signatureLo: UInt64
|
||||
var signatureHi: UInt64
|
||||
|
||||
init(command: CommandsV2, status: NTStatus = .SUCCESS, creditCharge: UInt16, creditRequestResponse: UInt16, flags: FlagsV2, nextCommand: UInt32 = 0, messageId: UInt64, treeId: UInt32, sessionId: UInt64,signatureLo: UInt64, signatureHi: UInt64) {
|
||||
init(command: CommandsV2, status: NTStatus = .SUCCESS, creditCharge: UInt16 = 0, creditRequestResponse: UInt16, flags: FlagsV2 = [], nextCommand: UInt32 = 0, messageId: UInt64, treeId: UInt32, sessionId: UInt64,signatureLo: UInt64 = 0, signatureHi: UInt64 = 0) {
|
||||
self.protocolID = self.dynamicType.protocolConst
|
||||
self.structSize = 64
|
||||
self.status = status.rawValue
|
||||
|
||||
@@ -0,0 +1,198 @@
|
||||
//
|
||||
// SocketTransmitter.swift
|
||||
// ExtDownloader
|
||||
//
|
||||
// Created by Amir Abbas Mousavian on 4/9/95.
|
||||
// Copyright © 1395 Mousavian. All rights reserved.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
class TCPSocketTransmitter: NSObject, NSStreamDelegate {
|
||||
static let ports = ["http": 80,
|
||||
"https": 443,
|
||||
"smb": 445,
|
||||
"ftp": 21,
|
||||
"sftp": 22,
|
||||
"sftp": 2121,
|
||||
"telnet": 23,
|
||||
"pop": 110,
|
||||
"smtp": 25,
|
||||
"imap": 143]
|
||||
static let securePorts = ["https": 443,
|
||||
"smb": 445,
|
||||
"sftp": 22,
|
||||
"sftp": 2121,
|
||||
"telnet": 992,
|
||||
"pop": 995,
|
||||
"smtp": 465,
|
||||
"imap": 993]
|
||||
|
||||
private var inputStream: NSInputStream?
|
||||
private var outputStream: NSOutputStream?
|
||||
private var dataToBeSent: NSMutableData = NSMutableData()
|
||||
let dataRecieved: NSMutableData = NSMutableData()
|
||||
|
||||
let baseURL: NSURL
|
||||
let secureConnection: Bool
|
||||
private let port: UInt32
|
||||
private var connected = false
|
||||
|
||||
init?(baseURL: NSURL, secure: Bool = false) {
|
||||
self.baseURL = baseURL
|
||||
self.secureConnection = secure
|
||||
let scheme = baseURL.scheme.lowercaseString
|
||||
let defaultPort = secure ? UInt32(TCPSocketTransmitter.securePorts[scheme] ?? 0) : UInt32(TCPSocketTransmitter.ports[scheme] ?? 0)
|
||||
self.port = baseURL.port?.unsignedIntValue ?? defaultPort
|
||||
if self.port == 0 {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
deinit {
|
||||
disconnect()
|
||||
}
|
||||
|
||||
func connect() -> Bool {
|
||||
guard let hostStr = baseURL.host else {
|
||||
return false
|
||||
}
|
||||
var readStream : Unmanaged<CFReadStream>?
|
||||
var writeStream : Unmanaged<CFWriteStream>?
|
||||
let host : CFString = NSString(string: hostStr)
|
||||
|
||||
CFStreamCreatePairWithSocketToHost(kCFAllocatorDefault, host, self.port, &readStream, &writeStream)
|
||||
inputStream = readStream?.takeRetainedValue()
|
||||
outputStream = writeStream?.takeRetainedValue()
|
||||
|
||||
guard let inputStream = inputStream, outputStream = outputStream else {
|
||||
return false
|
||||
}
|
||||
|
||||
inputStream.delegate = self
|
||||
outputStream.delegate = self
|
||||
|
||||
inputStream.scheduleInRunLoop(NSRunLoop.currentRunLoop(), forMode: NSDefaultRunLoopMode)
|
||||
outputStream.scheduleInRunLoop(NSRunLoop.currentRunLoop(), forMode: NSDefaultRunLoopMode)
|
||||
|
||||
if secureConnection {
|
||||
inputStream.setProperty(NSStreamSocketSecurityLevelNegotiatedSSL, forKey: NSStreamSocketSecurityLevelKey)
|
||||
outputStream.setProperty(NSStreamSocketSecurityLevelNegotiatedSSL, forKey: NSStreamSocketSecurityLevelKey)
|
||||
}
|
||||
|
||||
inputStream.open()
|
||||
outputStream.open()
|
||||
return true
|
||||
}
|
||||
|
||||
func disconnect() {
|
||||
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?.delegate = nil
|
||||
self.outputStream?.delegate = nil
|
||||
|
||||
self.inputStream = nil
|
||||
self.outputStream = nil
|
||||
}
|
||||
|
||||
func stream(aStream: NSStream, handleEvent eventCode: NSStreamEvent) {
|
||||
switch (eventCode) {
|
||||
case NSStreamEvent.ErrorOccurred:
|
||||
break
|
||||
case NSStreamEvent.EndEncountered:
|
||||
connected = false
|
||||
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)
|
||||
case NSStreamEvent.HasBytesAvailable:
|
||||
var buffer = [UInt8](count: 2048, repeatedValue: 0)
|
||||
if ( aStream == inputStream) {
|
||||
while (inputStream!.hasBytesAvailable ?? false) {
|
||||
let len = inputStream!.read(&buffer, maxLength: buffer.count)
|
||||
if len > 0 {
|
||||
dataRecieved.appendBytes(&buffer, length: len)
|
||||
}
|
||||
}
|
||||
}
|
||||
case NSStreamEvent.HasSpaceAvailable:
|
||||
if aStream == outputStream {
|
||||
do {
|
||||
try send(data: nil)
|
||||
} catch _ {
|
||||
NSLog("Sending error")
|
||||
}
|
||||
|
||||
}
|
||||
default:
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
func send(data data: NSData?) throws {
|
||||
if self.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
|
||||
if bytesWritten > 0 {
|
||||
let range = NSRange(location: 0, length: bytesWritten)
|
||||
dataToBeSent.replaceBytesInRange(range, withBytes: nil, length: 0)
|
||||
} else {
|
||||
throw NSError(domain: NSURLErrorDomain, code: NSURLError.NetworkConnectionLost.rawValue, userInfo: nil)
|
||||
}
|
||||
}
|
||||
//println("Sent the following")
|
||||
} else { //steam busy
|
||||
if let data = data {
|
||||
dataToBeSent.appendData(data)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func waitForSendDataPurge() {
|
||||
if NSThread.isMainThread() {
|
||||
assertionFailure("waitForSendDataPurge() method can't be called from main thread")
|
||||
}
|
||||
while true {
|
||||
if dataToBeSent.length == 0 {
|
||||
break
|
||||
}
|
||||
|
||||
NSRunLoop.currentRunLoop().runUntilDate(NSDate(timeIntervalSinceNow: 0.1));
|
||||
NSThread.sleepForTimeInterval(0.1)
|
||||
}
|
||||
}
|
||||
|
||||
func waitForResponse() -> Bool {
|
||||
if NSThread.isMainThread() {
|
||||
assertionFailure("waitForResponse() method can't be called from main thread")
|
||||
}
|
||||
var finished = false
|
||||
while !finished {
|
||||
switch inputStream?.streamStatus ?? .Error {
|
||||
case .AtEnd:
|
||||
finished = true
|
||||
return true
|
||||
case .Closed, .Error:
|
||||
return false
|
||||
default:
|
||||
finished = false
|
||||
}
|
||||
|
||||
NSRunLoop.currentRunLoop().runUntilDate(NSDate(timeIntervalSinceNow: 0.1));
|
||||
NSThread.sleepForTimeInterval(0.1)
|
||||
}
|
||||
return false
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user