230 lines
7.5 KiB
Swift
230 lines
7.5 KiB
Swift
//
|
|
// ServerCheck.swift
|
|
// ChatSecure
|
|
//
|
|
// Created by Chris Ballinger on 2/15/17.
|
|
// Copyright © 2017 Chris Ballinger. All rights reserved.
|
|
//
|
|
|
|
import Foundation
|
|
import OTRAssets
|
|
|
|
/**
|
|
* The purpose of this class is to collect and process server
|
|
* and push info in one place.
|
|
*
|
|
* All public members must be accessed from the main queue.
|
|
*/
|
|
public class ServerCheck: XMPPModule {
|
|
|
|
@objc public let push: PushController
|
|
@objc public let serverCapabilities: OTRServerCapabilities
|
|
@objc public let xmppCapabilities: XMPPCapabilities
|
|
@objc public let pushModule: XMPPPushModule
|
|
|
|
@objc public var result = ServerCheckResult()
|
|
|
|
@objc public static let UpdateNotificationName = Notification.Name(rawValue: "ServerCheckUpdateNotification")
|
|
|
|
deinit {
|
|
NotificationCenter.default.removeObserver(self)
|
|
}
|
|
|
|
/// pushModule should be activated elsewhere
|
|
@objc public init(push: PushController,
|
|
pushModule: XMPPPushModule,
|
|
dispatchQueue: DispatchQueue? = nil) {
|
|
self.push = push
|
|
let capsStorage = XMPPCapabilitiesCoreDataStorage(inMemoryStore: ())!
|
|
let capabilities = XMPPCapabilities(capabilitiesStorage: capsStorage, dispatchQueue: dispatchQueue)
|
|
self.xmppCapabilities = capabilities
|
|
self.serverCapabilities = OTRServerCapabilities(capabilities: capabilities, dispatchQueue: dispatchQueue)
|
|
|
|
self.pushModule = pushModule
|
|
super.init(dispatchQueue: dispatchQueue)
|
|
|
|
self.xmppCapabilities.autoFetchHashedCapabilities = true
|
|
self.xmppCapabilities.autoFetchNonHashedCapabilities = true
|
|
self.xmppCapabilities.autoFetchMyServerCapabilities = true
|
|
|
|
serverCapabilities.addDelegate(self, delegateQueue: moduleQueue)
|
|
pushModule.addDelegate(self, delegateQueue: moduleQueue)
|
|
NotificationCenter.default.addObserver(self, selector: #selector(pushAccountChanged(_:)), name: Notification.Name(rawValue: OTRPushAccountDeviceChanged), object: push)
|
|
NotificationCenter.default.addObserver(self, selector: #selector(pushAccountChanged(_:)), name: Notification.Name(rawValue: OTRPushAccountTokensChanged), object: push)
|
|
fetch()
|
|
}
|
|
|
|
// MARK: XMPPModule overrides
|
|
|
|
@discardableResult override public func activate(_ xmppStream: XMPPStream) -> Bool {
|
|
guard super.activate(xmppStream),
|
|
xmppCapabilities.activate(xmppStream),
|
|
serverCapabilities.activate(xmppStream)
|
|
else {
|
|
return false
|
|
}
|
|
return true
|
|
}
|
|
|
|
public override func deactivate() {
|
|
serverCapabilities.deactivate()
|
|
xmppCapabilities.deactivate()
|
|
super.deactivate()
|
|
}
|
|
|
|
// MARK: Public API
|
|
|
|
@objc public func getCombinedPushStatus() -> ServerCheckPushStatus {
|
|
if let xmpp = xmppStream, xmpp.state != .STATE_XMPP_CONNECTED || !xmpp.isAuthenticated {
|
|
return .unknown
|
|
}
|
|
return result.getCombinedPushStatus()
|
|
}
|
|
|
|
|
|
|
|
/// set pushInfoReady, capabilitiesReady, pushStatusUpdate to get result
|
|
@objc public func fetch() {
|
|
refreshPush()
|
|
refreshCapabilities()
|
|
checkReady()
|
|
}
|
|
|
|
/// Must be called from main queue
|
|
@objc public func refresh() {
|
|
result.pushInfo = nil
|
|
serverCapabilities.fetchAllCapabilities()
|
|
fetch()
|
|
}
|
|
|
|
// This will refresh the pushStatusUpdate block
|
|
private func updatePushStatus() {
|
|
guard let push = result.pushInfo, let pubsubEndpoint = push.pubsubEndpoint else { return }
|
|
if let jid = XMPPJID(user: nil, domain: pubsubEndpoint, resource: nil) {
|
|
let status = pushModule.registrationStatus(forServerJID: jid)
|
|
result.pushStatus = status
|
|
postUpdateNotification()
|
|
}
|
|
}
|
|
|
|
@objc private func pushAccountChanged(_ notification: Notification) {
|
|
DispatchQueue.main.async {
|
|
self.refresh()
|
|
}
|
|
}
|
|
|
|
private func checkReady() {
|
|
if let _ = result.pushInfo {
|
|
postUpdateNotification()
|
|
}
|
|
if let _ = result.capabilities {
|
|
postUpdateNotification()
|
|
}
|
|
updatePushStatus()
|
|
}
|
|
|
|
private func postUpdateNotification() {
|
|
DispatchQueue.main.async {
|
|
NotificationCenter.default.post(name: type(of: self).UpdateNotificationName, object: self)
|
|
}
|
|
}
|
|
|
|
private func refreshPush() {
|
|
push.gatherPushInfo(completion: { (info) in
|
|
self.result.pushInfo = info
|
|
self.checkReady()
|
|
}, callbackQueue: DispatchQueue.main)
|
|
}
|
|
|
|
private func refreshCapabilities() {
|
|
let caps = ServerCapabilityInfo.allCapabilities()
|
|
result.capabilities = serverCapabilities.markAvailable(capabilities: caps)
|
|
checkReady()
|
|
}
|
|
|
|
}
|
|
|
|
// MARK: - OTRServerCapabilitiesDelegate
|
|
|
|
extension ServerCheck: OTRServerCapabilitiesDelegate {
|
|
|
|
@objc public func serverCapabilities(_ sender: OTRServerCapabilities, didDiscoverCapabilities capabilities: [XMPPJID : XMLElement]) {
|
|
refreshCapabilities()
|
|
}
|
|
|
|
}
|
|
|
|
// MARK: - XMPPPushDelegate
|
|
|
|
extension ServerCheck: XMPPPushDelegate {
|
|
|
|
public func pushModule(_ module: XMPPPushModule, didRegisterWithResponseIq responseIq: XMPPIQ, outgoingIq: XMPPIQ) {
|
|
checkReady()
|
|
}
|
|
|
|
public func pushModule(_ module: XMPPPushModule, failedToRegisterWithErrorIq errorIq: XMPPIQ?, outgoingIq: XMPPIQ) {
|
|
checkReady()
|
|
}
|
|
|
|
public func pushModule(_ module: XMPPPushModule, disabledPushForServerJID serverJID: XMPPJID, node: String?, responseIq: XMPPIQ, outgoingIq: XMPPIQ) {
|
|
checkReady()
|
|
}
|
|
|
|
public func pushModule(_ module: XMPPPushModule, failedToDisablePushWithErrorIq errorIq: XMPPIQ?, serverJID: XMPPJID, node: String?, outgoingIq: XMPPIQ) {
|
|
checkReady()
|
|
}
|
|
|
|
public func pushModule(_ module: XMPPPushModule, readyWithCapabilities caps: XMLElement, jid: XMPPJID) {
|
|
// This _should_ be handled elsewhere in OTRServerCapabilities
|
|
// Not sure why it's not working properly
|
|
if let caps = result.capabilities {
|
|
caps[.XEP0357]?.status = .Available
|
|
}
|
|
checkReady()
|
|
}
|
|
}
|
|
|
|
@objc
|
|
public enum ServerCheckPushStatus: UInt {
|
|
case unknown
|
|
case broken
|
|
case working
|
|
}
|
|
|
|
@objc(OTRServerCheckResult)
|
|
public class ServerCheckResult: NSObject {
|
|
public var capabilities: [CapabilityCode : ServerCapabilityInfo]?
|
|
public var pushInfo: PushInfo?
|
|
public var pushStatus: XMPPPushStatus?
|
|
|
|
/** This lets you collect all push info in one place */
|
|
fileprivate func getCombinedPushStatus() -> ServerCheckPushStatus {
|
|
var checkStatus: ServerCheckPushStatus = .unknown
|
|
if let pushInfo = pushInfo, !pushInfo.pushMaybeWorks() {
|
|
return .broken
|
|
}
|
|
if let pushStatus = pushStatus, pushStatus != .registered {
|
|
return .broken
|
|
}
|
|
if let pushCap = capabilities?[.XEP0357], pushCap.status != .Available {
|
|
return .broken
|
|
}
|
|
guard let caps = capabilities, let push = pushInfo, let status = pushStatus else {
|
|
return .unknown
|
|
}
|
|
var xepExists = false
|
|
if let pushCap = caps[.XEP0357], pushCap.status == .Available {
|
|
xepExists = true
|
|
}
|
|
let pushAcctWorks = push.pushMaybeWorks()
|
|
let xmppWorks = status == .registered
|
|
if xepExists && pushAcctWorks && xmppWorks {
|
|
checkStatus = .working
|
|
} else {
|
|
checkStatus = .broken
|
|
}
|
|
return checkStatus
|
|
}
|
|
}
|
|
|