SocketTransmitter class

This commit is contained in:
Amir Abbas Mousavian
2016-06-30 01:24:47 +04:30
parent 963919c4cc
commit f1c05ea3c8
4 changed files with 226 additions and 81 deletions
+8 -6
View File
@@ -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)
+1 -1
View File
@@ -158,7 +158,7 @@ extension FileProvider {
}
func NotImplemented() {
assert(false, "method not implemented")
assertionFailure("method not implemented")
}
}
+19 -74
View File
@@ -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
+198
View File
@@ -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
}
}