Advertisement
Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- //: [Previous](@previous)
- import Foundation
- URLCache.shared = URLCache(memoryCapacity: 0, diskCapacity: 0, diskPath: nil)
- public struct FailerMessage {
- var to: [String] = []
- var cc: [String]? = []
- var bcc: [String]? = []
- var from: String
- var subject: String
- var body: String?
- var attachments: [Data]? = []
- }
- public enum FailerError: Error {
- case internalFailerError
- case unableToConnectToHost
- }
- public final class Failer: NSObject {
- public typealias Login = (username: String, password: String)
- open var server: String
- open var port: Int
- open var login: Login?
- fileprivate var inputStream: InputStream!
- fileprivate var outputStream: OutputStream!
- fileprivate let maxReadLength = 1024
- // MARK: Initialization
- public init(server: String, port: Int = 25, login: Login? = nil) {
- self.server = server
- self.port = port
- self.login = login
- }
- // MARK: Sending
- public func send(_ message: FailerMessage) throws {
- try connect()
- let emailData: Data = try compile(message: message)
- try send(emailData)
- }
- // MARK: Connection
- private func connect() throws {
- var input: InputStream? = nil
- var output: OutputStream? = nil
- Stream.getStreamsToHost(withName: server, port: port, inputStream: &input, outputStream: &output)
- guard let inputSafe = input, let outputSafe = output else {
- throw FailerError.unableToConnectToHost
- }
- self.inputStream = inputSafe
- self.outputStream = outputSafe
- // TODO: Authentication using login
- // Enable SSL/TLS on the streams
- // inputStream!.setProperty(kCFStreamSocketSecurityLevelNegotiatedSSL, forKey: Stream.PropertyKey(rawValue: kCFStreamPropertySocketSecurityLevel as String))
- // outputStream!.setProperty(kCFStreamSocketSecurityLevelNegotiatedSSL, forKey: Stream.PropertyKey(rawValue: kCFStreamPropertySocketSecurityLevel as String))
- //
- // // Define custom SSL/TLS settings
- // let sslSettings: [NSString : Any] = [
- // NSStream automatically sets up the socket, the streams and creates a trust object and evaulates it before you even get a chance to check the trust yourself. Only proper SSL certificates will work with this method. If you have a self signed certificate like I do, you need to disable the trust check here and evaulate the trust against your custom root CA yourself.
- // NSString(format: kCFStreamSSLValidatesCertificateChain): kCFBooleanFalse,
- // //
- // NSString(format: kCFStreamSSLPeerName): kCFNull,
- // // We are an SSL/TLS client, not a server
- // NSString(format: kCFStreamSSLIsServer): kCFBooleanFalse,
- //
- // NSString(format: kCFStreamSocketSecurityLevelNegotiatedSSL): kCFBooleanTrue
- // ]
- //
- // // Set the SSL/TLS settingson the streams
- // inputStream!.setProperty(sslSettings, forKey: Stream.PropertyKey(rawValue: kCFStreamPropertySSLSettings as String))
- // outputStream!.setProperty(sslSettings, forKey: Stream.PropertyKey(rawValue: kCFStreamPropertySSLSettings as String))
- inputStream.delegate = self
- outputStream.delegate = self
- inputStream.schedule(in: .main, forMode: .commonModes)
- outputStream.schedule(in: .main, forMode: .commonModes)
- inputStream.open()
- outputStream.open()
- }
- private func compile(message: FailerMessage) throws -> Data {
- let headersString = headers(from: message)
- guard var emailData = headersString.data(using: String.Encoding.utf8) else {
- throw FailerError.internalFailerErrorq
- }
- if let attachment = try attachments(from: message) {
- emailData.append(attachment)
- }
- return emailData
- }
- private func send(_ data: Data) throws {
- print(data)
- _ = data.withUnsafeBytes {
- outputStream.write($0, maxLength: data.count)
- }
- }
- fileprivate func finish() {
- inputStream.close()
- outputStream.close()
- }
- }
- fileprivate extension Failer {
- func headers(from message: FailerMessage) -> String {
- var content = "From: \(message.from)\r\n"
- content += "To: \(message.to.joined(separator: ", "))\r\n"
- if let cc = message.cc, cc.count > 0 {
- content += "Cc: \(cc.joined(separator: ", "))\r\n"
- }
- if let bcc = message.bcc, bcc.count > 0 {
- content += "Bcc: \(bcc.joined(separator: ", "))\r\n"
- }
- content += "Subject: \(message.subject)\r\n"
- // TODO: Handle email body
- content += "\r\n"
- return content
- }
- func attachments(from message: FailerMessage) throws -> Data? {
- return nil
- }
- }
- extension Failer: StreamDelegate {
- public func stream(_ aStream: Stream, handle eventCode: Stream.Event)
- {
- switch eventCode
- {
- case Stream.Event.endEncountered:
- print("socked died")
- aStream.close()
- aStream.remove(from: RunLoop.main, forMode: .defaultRunLoopMode)
- break
- case Stream.Event.hasSpaceAvailable:
- print("matching presented certificate with expected")
- var sslInfo: SecTrust? = aStream.property(forKey: kCFStreamPropertySSLContext as Stream.PropertyKey) as! SecTrust?
- sslInfo = aStream.property(forKey: kCFStreamPropertySSLPeerTrust as Stream.PropertyKey) as! SecTrust?
- sslInfo = aStream.property(forKey: kCFStreamSSLValidatesCertificateChain as Stream.PropertyKey) as! SecTrust?
- sslInfo = aStream.property(forKey: kCFStreamPropertySSLSettings as Stream.PropertyKey) as! SecTrust?
- sslInfo = aStream.property(forKey: kCFStreamSSLLevel as Stream.PropertyKey) as! SecTrust?
- sslInfo = aStream.property(forKey: kCFStreamSSLPeerName as Stream.PropertyKey) as! SecTrust?
- print(sslInfo ?? "notnin'")
- // Get the presented certificate
- let sslTrustInput: SecTrust? = aStream.property(forKey: kCFStreamPropertySSLPeerTrust as Stream.PropertyKey) as! SecTrust?
- if (sslTrustInput == nil) {
- print("something went horribly wrong in fetching the presented certificate")
- broadcastSocketResult(result: false)
- return
- }
- // if (Vars.expectedCert == nil) {
- // print("probably a bug, there is no expected certificate in Vars. fail/crash in 3, 2, 1...")
- // broadcastSocketResult(result: false)
- // return
- // }
- //set the expected certificate as the only "trusted" one
- // let acceptedCerts: NSMutableArray = NSMutableArray()
- // acceptedCerts.add(Vars.expectedCert!)
- // SecTrustSetAnchorCertificates(sslTrustInput!, acceptedCerts)
- //check the certificate match test results
- var result: SecTrustResultType = SecTrustResultType.fatalTrustFailure //must initialize with something
- let err: OSStatus = SecTrustEvaluate(sslTrustInput!, &result)
- if (err != errSecSuccess) {
- print("problem evaluating certificate match")
- broadcastSocketResult(result: false)
- return
- }
- if (result != SecTrustResultType.proceed) {
- print("certificate was not signed by private CA")
- broadcastSocketResult(result: false)
- return
- }
- print("socket ssl turned out ok")
- broadcastSocketResult(result: true)
- break
- case Stream.Event.openCompleted:
- print("Socket is useable")
- break
- case Stream.Event.errorOccurred:
- print("Error: \(aStream.streamError)")
- broadcastSocketResult(result: false)
- break;
- default:
- print("Some other code" + String(describing: eventCode))
- broadcastSocketResult(result: false)
- break
- }
- }
- private func broadcastSocketResult(result: Bool) {
- //let extras = [Const.BORADCAST_SOCKET_RESULT: result]
- //NotificationCenter.default.post(name: NSNotification.Name(rawValue: Const.BROADCAST_SOCKET), object: extras)
- }
- private func readAvailableBytes(stream: InputStream) {
- let buffer = UnsafeMutablePointer<UInt8>.allocate(capacity: maxReadLength)
- while stream.hasBytesAvailable {
- let numberOfBytesRead = inputStream.read(buffer, maxLength: maxReadLength)
- if numberOfBytesRead < 0 {
- if let _ = inputStream.streamError {
- break
- }
- }
- if let output = processedMessageString(buffer: buffer, length: numberOfBytesRead) {
- print(output)
- }
- }
- }
- private func processedMessageString(buffer: UnsafeMutablePointer<UInt8>, length: Int) -> String? {
- let string = String(bytesNoCopy: buffer, length: length, encoding: .ascii, freeWhenDone: true)
- return string
- }
- }
- let failer = Failer(server: "smtp.gmail.com", port: 25, login: nil)
- let message = FailerMessage(to: ["ondrej.rafaj@gmail.com"], cc: nil, bcc: nil, from: "smtptesteu@gmail.com", subject: "Test subject Yo!", body: "Ondrej has a beautiful body", attachments: nil)
- do {
- try failer.send(message)
- }
- catch {
- print("Failer error: \(error)")
- }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement