diff --git a/Signal/Signal-Info.plist b/Signal/Signal-Info.plist
index 7396df8e5..258a90a89 100644
--- a/Signal/Signal-Info.plist
+++ b/Signal/Signal-Info.plist
@@ -7,7 +7,7 @@
CarthageVersion
0.33.0
OSXVersion
- 10.14.4
+ 10.14.5
WebRTCCommit
1445d719bf05280270e9f77576f80f973fd847f8 M73
diff --git a/Signal/src/AppDelegate.m b/Signal/src/AppDelegate.m
index 1e39b61ea..d52da76a0 100644
--- a/Signal/src/AppDelegate.m
+++ b/Signal/src/AppDelegate.m
@@ -310,8 +310,8 @@ static NSTimeInterval launchStartedAt;
name:NSNotificationName_2FAStateDidChange
object:nil];
- // Loki Message received
- [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(receivedNewMessages:) name:NSNotification.receivedNewMessages object:nil];
+ // Loki - Observe messages received notifications
+ [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(handleNewMessagesReceived:) name:NSNotification.newMessagesReceived object:nil];
OWSLogInfo(@"application: didFinishLaunchingWithOptions completed.");
@@ -1413,17 +1413,17 @@ static NSTimeInterval launchStartedAt;
#pragma mark - Long polling
-- (void)receivedNewMessages:(NSNotification *)notification
+- (void)handleNewMessagesReceived:(NSNotification *)notification
{
NSArray *messages = (NSArray *)notification.userInfo[@"messages"];
- OWSLogInfo(@"[Loki] Received %lu messages from long polling", messages.count);
+ OWSLogInfo(@"[Loki] Received %lu messages through long polling.", messages.count);
for (SSKProtoEnvelope *envelope in messages) {
NSData *envelopeData = envelope.serializedDataIgnoringErrors;
if (envelopeData != nil) {
[SSKEnvironment.shared.messageReceiver handleReceivedEnvelopeData:envelopeData];
} else {
- OWSFailDebug(@"Failed to serialize envelope");
+ OWSFailDebug(@"Failed to deserialize envelope.");
}
}
}
diff --git a/SignalMessaging/Views/AvatarImageView.swift b/SignalMessaging/Views/AvatarImageView.swift
index 9e0d0d300..e08f6cd79 100644
--- a/SignalMessaging/Views/AvatarImageView.swift
+++ b/SignalMessaging/Views/AvatarImageView.swift
@@ -3,7 +3,6 @@
//
import UIKit
-import SignalServiceKit
@objc
public class AvatarImageView: UIImageView {
diff --git a/SignalServiceKit/src/Loki/API/LokiAPI+Polling.swift b/SignalServiceKit/src/Loki/API/LokiAPI+LongPolling.swift
similarity index 87%
rename from SignalServiceKit/src/Loki/API/LokiAPI+Polling.swift
rename to SignalServiceKit/src/Loki/API/LokiAPI+LongPolling.swift
index 94970545a..515554c9c 100644
--- a/SignalServiceKit/src/Loki/API/LokiAPI+Polling.swift
+++ b/SignalServiceKit/src/Loki/API/LokiAPI+LongPolling.swift
@@ -37,7 +37,7 @@ public extension LokiAPI {
// This is here so we can stop the infinite loop
guard !shouldStopPolling else { return }
- fetchSwarmIfNeeded(for: hexEncodedPublicKey).then { _ -> Guarantee<[Result]> in
+ getSwarm(for: hexEncodedPublicKey).then { _ -> Guarantee<[Result]> in
var promises = [Promise]()
let connections = 3
for i in 0.. [LokiAPITarget] {
- let snodes = getCachedSnodes(for: hexEncodedPublicKey)
+ let snodes = LokiAPI.swarmCache[hexEncodedPublicKey] ?? []
return snodes.filter { !usedSnodes.contains($0) }
}
@@ -84,15 +84,15 @@ public extension LokiAPI {
func getMessagesInfinitely(from target: LokiAPITarget) -> Promise {
// The only way to exit the infinite loop is to throw an error 3 times or cancel
- return getRawMessages(from: target).then { rawMessages -> Promise in
+ return getRawMessages(from: target, useLongPolling: true).then { rawResponse -> Promise in
// Check if we need to abort
guard !isCancelled else { throw PMKError.cancelled }
// Process the messages
- let messages = process(rawMessages: rawMessages, from: target)
+ let messages = parseRawMessagesResponse(rawResponse, from: target)
// Send our messages as a notification
- NotificationCenter.default.post(name: .receivedNewMessages, object: nil, userInfo: ["messages": messages])
+ NotificationCenter.default.post(name: .newMessagesReceived, object: nil, userInfo: ["messages": messages])
// Continue fetching if we haven't cancelled
return getMessagesInfinitely(from: target)
@@ -107,7 +107,7 @@ public extension LokiAPI {
// Connect to the next snode if we haven't cancelled
// We also need to remove the cached snode so we don't contact it again
- removeCachedSnode(nextSnode, for: hexEncodedPublicKey)
+ dropIfNeeded(nextSnode, hexEncodedPublicKey: hexEncodedPublicKey)
return connectToNextSnode()
}
}
diff --git a/SignalServiceKit/src/Loki/API/LokiAPI+Request.swift b/SignalServiceKit/src/Loki/API/LokiAPI+Request.swift
deleted file mode 100644
index 27d0a7f03..000000000
--- a/SignalServiceKit/src/Loki/API/LokiAPI+Request.swift
+++ /dev/null
@@ -1,20 +0,0 @@
-
-internal extension LokiAPI {
- internal struct Request {
- var method: LokiAPITarget.Method
- var target: LokiAPITarget
- var publicKey: String
- var parameters: [String:Any]
- var headers: [String:String]
- var timeout: TimeInterval?
-
- init(method: LokiAPITarget.Method, target: LokiAPITarget, publicKey: String, parameters: [String:Any] = [:]) {
- self.method = method
- self.target = target
- self.publicKey = publicKey
- self.parameters = parameters
- self.headers = [:]
- self.timeout = nil
- }
- }
-}
diff --git a/SignalServiceKit/src/Loki/API/LokiAPI+SwarmAPI.swift b/SignalServiceKit/src/Loki/API/LokiAPI+SwarmAPI.swift
index 21688868f..981d293a8 100644
--- a/SignalServiceKit/src/Loki/API/LokiAPI+SwarmAPI.swift
+++ b/SignalServiceKit/src/Loki/API/LokiAPI+SwarmAPI.swift
@@ -11,7 +11,7 @@ public extension LokiAPI {
private static let swarmCacheKey = "swarmCacheKey"
private static let swarmCacheCollection = "swarmCacheCollection"
- fileprivate static var swarmCache: [String:[LokiAPITarget]] {
+ internal static var swarmCache: [String:[LokiAPITarget]] {
get {
var result: [String:[LokiAPITarget]]? = nil
storage.dbReadConnection.read { transaction in
@@ -26,6 +26,14 @@ public extension LokiAPI {
}
}
+ internal static func dropIfNeeded(_ target: LokiAPITarget, hexEncodedPublicKey: String) {
+ let swarm = LokiAPI.swarmCache[hexEncodedPublicKey]
+ if var swarm = swarm, let index = swarm.firstIndex(of: target) {
+ swarm.remove(at: index)
+ LokiAPI.swarmCache[hexEncodedPublicKey] = swarm
+ }
+ }
+
// MARK: Internal API
private static func getRandomSnode() -> Promise {
return Promise { seal in
@@ -33,16 +41,7 @@ public extension LokiAPI {
}
}
- internal static func getCachedSnodes(for hexEncodedPublicKey: String) -> [LokiAPITarget] {
- return swarmCache[hexEncodedPublicKey] ?? []
- }
-
- internal static func removeCachedSnode(_ target: LokiAPITarget, for hexEncodedPublicKey: String) {
- guard let cache = swarmCache[hexEncodedPublicKey] else { return }
- swarmCache[hexEncodedPublicKey] = cache.filter { $0 != target }
- }
-
- internal static func fetchSwarmIfNeeded(for hexEncodedPublicKey: String) -> Promise<[LokiAPITarget]> {
+ internal static func getSwarm(for hexEncodedPublicKey: String) -> Promise<[LokiAPITarget]> {
if let cachedSwarm = swarmCache[hexEncodedPublicKey], cachedSwarm.count >= minimumSnodeCount {
return Promise<[LokiAPITarget]> { $0.fulfill(cachedSwarm) }
} else {
@@ -54,7 +53,7 @@ public extension LokiAPI {
// MARK: Public API
internal static func getTargetSnodes(for hexEncodedPublicKey: String) -> Promise<[LokiAPITarget]> {
// shuffled() uses the system's default random generator, which is cryptographically secure
- return fetchSwarmIfNeeded(for: hexEncodedPublicKey).map { Array($0.shuffled().prefix(targetSnodeCount)) }
+ return getSwarm(for: hexEncodedPublicKey).map { Array($0.shuffled().prefix(targetSnodeCount)) }
}
// MARK: Parsing
@@ -85,11 +84,7 @@ internal extension Promise {
case 421:
// The snode isn't associated with the given public key anymore
Logger.warn("[Loki] Invalidating swarm for: \(hexEncodedPublicKey).")
- let swarm = LokiAPI.swarmCache[hexEncodedPublicKey]
- if var swarm = swarm, let index = swarm.firstIndex(of: target) {
- swarm.remove(at: index)
- LokiAPI.swarmCache[hexEncodedPublicKey] = swarm
- }
+ LokiAPI.dropIfNeeded(target, hexEncodedPublicKey: hexEncodedPublicKey)
default: break
}
}
diff --git a/SignalServiceKit/src/Loki/API/LokiAPI.swift b/SignalServiceKit/src/Loki/API/LokiAPI.swift
index 0c583d592..a3ba6e67b 100644
--- a/SignalServiceKit/src/Loki/API/LokiAPI.swift
+++ b/SignalServiceKit/src/Loki/API/LokiAPI.swift
@@ -6,7 +6,8 @@ import PromiseKit
// MARK: Settings
private static let version = "v1"
private static let maxRetryCount: UInt = 3
- public static let defaultMessageTTL: UInt64 = 1 * 24 * 60 * 60 * 1000
+ private static let longPollingTimeout: TimeInterval = 40
+ public static let defaultMessageTTL: UInt64 = 24 * 60 * 60 * 1000
// MARK: Types
public typealias RawResponse = Any
@@ -31,56 +32,30 @@ import PromiseKit
override private init() { }
// MARK: Internal API
- internal static func invoke(_ method: LokiAPITarget.Method, on target: LokiAPITarget, associatedWith hexEncodedPublicKey: String, parameters: [String:Any]) -> RawResponsePromise {
- return invoke(request: Request(method: method, target: target, publicKey: hexEncodedPublicKey, parameters: parameters))
+ internal static func invoke(_ method: LokiAPITarget.Method, on target: LokiAPITarget, associatedWith hexEncodedPublicKey: String,
+ parameters: [String:Any], headers: [String:String]? = nil, timeout: TimeInterval? = nil) -> RawResponsePromise {
+ let url = URL(string: "\(target.address):\(target.port)/\(version)/storage_rpc")!
+ let request = TSRequest(url: url, method: "POST", parameters: [ "method" : method.rawValue, "params" : parameters ])
+ if let headers = headers { request.allHTTPHeaderFields = headers }
+ if let timeout = timeout { request.timeoutInterval = timeout }
+ return TSNetworkManager.shared().makePromise(request: request).map { $0.responseObject }
+ .handlingSwarmSpecificErrorsIfNeeded(for: target, associatedWith: hexEncodedPublicKey).recoveringNetworkErrorsIfNeeded()
}
- internal static func invoke(request: Request) -> RawResponsePromise {
- let url = URL(string: "\(request.target.address):\(request.target.port)/\(version)/storage_rpc")!
- let networkRequest = TSRequest(url: url, method: "POST", parameters: [ "method" : request.method.rawValue, "params" : request.parameters ])
- networkRequest.allHTTPHeaderFields = request.headers
- if let timeout = request.timeout {
- networkRequest.timeoutInterval = timeout
- }
-
- return TSNetworkManager.shared().makePromise(request: networkRequest).map { $0.responseObject }
- .handlingSwarmSpecificErrorsIfNeeded(for: request.target, associatedWith: request.publicKey).recoveringNetworkErrorsIfNeeded()
- }
-
-
- internal static func getMessages(from target: LokiAPITarget, longPolling: Bool = true) -> MessageListPromise {
- return getRawMessages(from: target, longPolling: longPolling).map { process(rawMessages: $0, from: target) }
- }
-
- internal static func getRawMessages(from target: LokiAPITarget, longPolling: Bool = true) -> Promise<[JSON]> {
+ internal static func getRawMessages(from target: LokiAPITarget, useLongPolling: Bool) -> RawResponsePromise {
let hexEncodedPublicKey = OWSIdentityManager.shared().identityKeyPair()!.hexEncodedPublicKey
let lastHashValue = getLastMessageHashValue(for: target) ?? ""
let parameters = [ "pubKey" : hexEncodedPublicKey, "lastHash" : lastHashValue ]
-
- var request = Request(method: .getMessages, target: target, publicKey: hexEncodedPublicKey, parameters: parameters)
- if (longPolling) {
- request.headers = ["X-Loki-Long-Poll" : "true"]
- request.timeout = 40 // 40 second timeout
- }
-
- return invoke(request: request).map { rawResponse in
- guard let json = rawResponse as? JSON, let rawMessages = json["messages"] as? [JSON] else { return [] }
- return rawMessages
- }
- }
-
- internal static func process(rawMessages: [JSON], from target: LokiAPITarget) -> [SSKProtoEnvelope] {
- updateLastMessageHashValueIfPossible(for: target, from: rawMessages)
- let newRawMessages = removeDuplicates(from: rawMessages)
- return parseProtoEnvelopes(from: newRawMessages)
+ let headers: [String:String]? = useLongPolling ? [ "X-Loki-Long-Poll" : "true" ] : nil
+ let timeout: TimeInterval? = useLongPolling ? longPollingTimeout : nil
+ return invoke(.getMessages, on: target, associatedWith: hexEncodedPublicKey, parameters: parameters, headers: headers, timeout: timeout)
}
// MARK: Public API
-
public static func getMessages() -> Promise> {
let hexEncodedPublicKey = OWSIdentityManager.shared().identityKeyPair()!.hexEncodedPublicKey
return getTargetSnodes(for: hexEncodedPublicKey).mapValues { targetSnode in
- return getMessages(from: targetSnode, longPolling: false)
+ return getRawMessages(from: targetSnode, useLongPolling: false).map { parseRawMessagesResponse($0, from: targetSnode) }
}.map { Set($0) }.retryingIfNeeded(maxRetryCount: maxRetryCount)
}
@@ -132,12 +107,19 @@ import PromiseKit
// The parsing utilities below use a best attempt approach to parsing; they warn for parsing failures but don't throw exceptions.
+ internal static func parseRawMessagesResponse(_ rawResponse: Any, from target: LokiAPITarget) -> [SSKProtoEnvelope] {
+ guard let json = rawResponse as? JSON, let rawMessages = json["messages"] as? [JSON] else { return [] }
+ updateLastMessageHashValueIfPossible(for: target, from: rawMessages)
+ let newRawMessages = removeDuplicates(from: rawMessages)
+ return parseProtoEnvelopes(from: newRawMessages)
+ }
+
private static func updateLastMessageHashValueIfPossible(for target: LokiAPITarget, from rawMessages: [JSON]) {
- guard let lastMessage = rawMessages.last, let hashValue = lastMessage["hash"] as? String, let expiresAt = lastMessage["expiration"] as? Int else {
- if rawMessages.count > 0 { Logger.warn("[Loki] Failed to update last message hash value from: \(rawMessages).") }
- return
+ if let lastMessage = rawMessages.last, let hashValue = lastMessage["hash"] as? String, let expirationDate = lastMessage["expiration"] as? Int {
+ setLastMessageHashValue(for: target, hashValue: hashValue, expiresAt: UInt64(expirationDate))
+ } else if (!rawMessages.isEmpty) {
+ Logger.warn("[Loki] Failed to update last message hash value from: \(rawMessages).")
}
- setLastMessageHashValue(for: target, hashValue: hashValue, expiresAt: UInt64(expiresAt))
}
private static func removeDuplicates(from rawMessages: [JSON]) -> [JSON] {
diff --git a/SignalServiceKit/src/Loki/API/LokiP2PManager.swift b/SignalServiceKit/src/Loki/API/LokiP2PManager.swift
index 28ca7ae0e..1ec75770e 100644
--- a/SignalServiceKit/src/Loki/API/LokiP2PManager.swift
+++ b/SignalServiceKit/src/Loki/API/LokiP2PManager.swift
@@ -174,12 +174,12 @@
AssertIsOnMainThread()
guard let message = onlineBroadcastMessage(forThread: thread) else {
- Logger.warn("[Loki] P2P Address not set")
+ Logger.warn("[Loki] P2P address not set.")
return
}
messageSender.sendPromise(message: message).catch { error in
- Logger.warn("Failed to send online status to \(thread.contactIdentifier())")
+ Logger.warn("Failed to send online status to \(thread.contactIdentifier()).")
}.retainUntilComplete()
}
@@ -198,7 +198,7 @@
private static func createLokiAddressMessage(for thread: TSThread, isPing: Bool) -> LokiAddressMessage? {
guard let ourAddress = ourP2PAddress else {
- Logger.error("P2P Address not set")
+ Logger.warn("[Loki] P2P address not set.")
return nil
}
diff --git a/SignalServiceKit/src/Loki/Utilities/Notification+Loki.swift b/SignalServiceKit/src/Loki/Utilities/Notification+Loki.swift
index 47216ad4e..4969f25b8 100644
--- a/SignalServiceKit/src/Loki/Utilities/Notification+Loki.swift
+++ b/SignalServiceKit/src/Loki/Utilities/Notification+Loki.swift
@@ -1,9 +1,7 @@
public extension Notification.Name {
public static let contactOnlineStatusChanged = Notification.Name("contactOnlineStatusChanged")
- public static let receivedNewMessages = Notification.Name("receivedNewMessages")
-
- // Friend request
+ public static let newMessagesReceived = Notification.Name("newMessagesReceived")
public static let threadFriendRequestStatusChanged = Notification.Name("threadFriendRequestStatusChanged")
public static let messageFriendRequestStatusChanged = Notification.Name("messageFriendRequestStatusChanged")
}
@@ -12,9 +10,7 @@ public extension Notification.Name {
@objc public extension NSNotification {
@objc public static let contactOnlineStatusChanged = Notification.Name.contactOnlineStatusChanged.rawValue as NSString
- @objc public static let receivedNewMessages = Notification.Name.receivedNewMessages.rawValue as NSString
-
- // Friend request
+ @objc public static let newMessagesReceived = Notification.Name.newMessagesReceived.rawValue as NSString
@objc public static let threadFriendRequestStatusChanged = Notification.Name.threadFriendRequestStatusChanged.rawValue as NSString
@objc public static let messageFriendRequestStatusChanged = Notification.Name.messageFriendRequestStatusChanged.rawValue as NSString
}