132 lines
4.2 KiB
Swift
132 lines
4.2 KiB
Swift
//
|
|
// SerenityService.swift
|
|
// PrivadoVPN
|
|
//
|
|
// Created by Juraldinio on 3/30/21.
|
|
// Copyright © 2021 Privado LLC. All rights reserved.
|
|
//
|
|
|
|
import Foundation
|
|
|
|
final class SerenityService: BayeuxClientOutput {
|
|
|
|
enum State {
|
|
case notReceiving
|
|
case preparing
|
|
case receving
|
|
}
|
|
|
|
struct Credentials: Equatable {
|
|
let token: String
|
|
let username: String
|
|
}
|
|
|
|
private var bayeuxClient: BayeuxClient<PrivadoNotification>?
|
|
private var lastVpnState: VPNConnectionState?
|
|
private(set) var credentials: Credentials?
|
|
|
|
let stateEmitter = Emitter<State>()
|
|
let notificationsEmitter = Emitter<[PrivadoNotification]>()
|
|
|
|
// MARK: - Interface
|
|
|
|
func connect(with credentials: Credentials) { // establishNewConnection
|
|
|
|
guard self.credentials != credentials else { return }
|
|
|
|
self.reset()
|
|
|
|
guard let client = self.createBayeuxClient(using: credentials) else { return }
|
|
|
|
self.credentials = credentials
|
|
self.bayeuxClient = client
|
|
|
|
client.connect()
|
|
}
|
|
|
|
func reviseClient(with credentials: Credentials) {
|
|
|
|
guard credentials != self.credentials
|
|
, let client = self.createBayeuxClient(using: credentials) else { return }
|
|
|
|
self.reset()
|
|
|
|
self.credentials = credentials
|
|
self.bayeuxClient = client
|
|
client.connect()
|
|
}
|
|
|
|
func reset() {
|
|
|
|
self.credentials = nil
|
|
|
|
guard let bayeuxClient = self.bayeuxClient else { return }
|
|
bayeuxClient.disconnect()
|
|
self.bayeuxClient = nil
|
|
}
|
|
|
|
// MARK: - BayeuxClientOutput
|
|
|
|
func didReceiveNotifications<Notification>(_ notifications: [Notification]) {
|
|
let notification: [PrivadoNotification] = notifications
|
|
.compactMap { $0 as? PrivadoNotification }
|
|
self.notificationsEmitter.invoke(notification)
|
|
}
|
|
|
|
// MARK: - Private
|
|
|
|
private func createBayeuxClient(using credentials: Credentials) -> BayeuxClient<PrivadoNotification>? {
|
|
|
|
guard let serenityFayeUrl = URL(string: PrivadoConstants.Serenity.serverPath)
|
|
, let subscriptionChannel = Self.subscriptionChannel(for: credentials) else {
|
|
return nil
|
|
}
|
|
|
|
let handshakeCredentials = HandshakeSystemChannel.Credentials(username: credentials.username, token: credentials.token)
|
|
let client = BayeuxClient<PrivadoNotification>(url: serenityFayeUrl,
|
|
handshakeCredentials: handshakeCredentials,
|
|
subscriptionChannel: subscriptionChannel,
|
|
output: self,
|
|
autoReconnect: true)
|
|
|
|
client.stateEmitter.addReaction { [weak self] state -> ShouldContinueReceiveNotifications in
|
|
print("BayeuxClient: \(state)")
|
|
switch state {
|
|
case .connected:
|
|
self?.stateEmitter.invoke(.receving)
|
|
case .handshake,
|
|
.preparing,
|
|
.connect:
|
|
self?.stateEmitter.invoke(.preparing)
|
|
case .error,
|
|
.disconnected:
|
|
self?.stateEmitter.invoke(.notReceiving)
|
|
}
|
|
|
|
return self.isExist
|
|
}
|
|
|
|
return client
|
|
}
|
|
|
|
// MARK: - Static
|
|
|
|
private static func subscriptionChannel(for credentials: Credentials) -> String? {
|
|
|
|
guard let keyData = PrivadoConstants.Serenity.Crypto.cypherKey.data(using: .utf8)
|
|
, let ivData = PrivadoConstants.Serenity.Crypto.iv.data(using: .utf8)
|
|
, let encryptedUsernameData = CryptoAES(key: keyData, iv: ivData).encrypt(message: credentials.username) else {
|
|
return nil
|
|
}
|
|
|
|
var resultData = ivData
|
|
resultData.append(encryptedUsernameData)
|
|
return "/" + resultData
|
|
.base64EncodedString()
|
|
.replacingOccurrences(of: "=", with: "")
|
|
.replacingOccurrences(of: "+", with: "-")
|
|
.replacingOccurrences(of: "/", with: "_")
|
|
}
|
|
|
|
}
|