Merge pull request #187 from loki-project/closed-groups

Closed Group Bug Fixes
pull/188/head
Niels Andriesse 5 years ago committed by GitHub
commit 0fb2e33b61
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

@ -14,7 +14,6 @@ public final class LokiAPI : NSObject {
internal static let workQueue = DispatchQueue(label: "LokiAPI.workQueue", qos: .userInitiated)
internal static var storage: OWSPrimaryStorage { OWSPrimaryStorage.shared() }
internal static var userHexEncodedPublicKey: String { getUserHexEncodedPublicKey() }
// MARK: Settings
private static let maxRetryCount: UInt = 4
@ -63,16 +62,16 @@ public final class LokiAPI : NSObject {
internal static func getRawMessages(from target: LokiAPITarget, usingLongPolling useLongPolling: Bool) -> RawResponsePromise {
let lastHashValue = getLastMessageHashValue(for: target) ?? ""
let parameters = [ "pubKey" : userHexEncodedPublicKey, "lastHash" : lastHashValue ]
let parameters = [ "pubKey" : getUserHexEncodedPublicKey(), "lastHash" : lastHashValue ]
let headers: [String:String]? = useLongPolling ? [ "X-Loki-Long-Poll" : "true" ] : nil
let timeout: TimeInterval? = useLongPolling ? longPollingTimeout : nil
return invoke(.getMessages, on: target, associatedWith: userHexEncodedPublicKey, parameters: parameters, headers: headers, timeout: timeout)
return invoke(.getMessages, on: target, associatedWith: getUserHexEncodedPublicKey(), parameters: parameters, headers: headers, timeout: timeout)
}
// MARK: Public API
public static func getMessages() -> Promise<Set<MessageListPromise>> {
return attempt(maxRetryCount: maxRetryCount, recoveringOn: workQueue) {
getTargetSnodes(for: userHexEncodedPublicKey).mapValues { targetSnode in
getTargetSnodes(for: getUserHexEncodedPublicKey()).mapValues { targetSnode in
getRawMessages(from: targetSnode, usingLongPolling: false).map { parseRawMessagesResponse($0, from: targetSnode) }
}.map { Set($0) }
}
@ -157,7 +156,7 @@ public final class LokiAPI : NSObject {
setLastMessageHashValue(for: target, hashValue: hashValue, expirationDate: UInt64(expirationDate))
// FIXME: Move this out of here
if UserDefaults.standard[.isUsingFullAPNs] {
LokiPushNotificationManager.acknowledgeDelivery(forMessageWithHash: hashValue, expiration: expirationDate, hexEncodedPublicKey: userHexEncodedPublicKey)
LokiPushNotificationManager.acknowledgeDelivery(forMessageWithHash: hashValue, expiration: expirationDate, hexEncodedPublicKey: getUserHexEncodedPublicKey())
}
} else if (!rawMessages.isEmpty) {
print("[Loki] Failed to update last message hash value from: \(rawMessages).")

@ -6,7 +6,6 @@ public class LokiDotNetAPI : NSObject {
internal static var storage: OWSPrimaryStorage { OWSPrimaryStorage.shared() }
internal static var userKeyPair: ECKeyPair { OWSIdentityManager.shared().identityKeyPair()! }
internal static var userHexEncodedPublicKey: String { userKeyPair.hexEncodedPublicKey }
// MARK: Settings
private static let attachmentType = "network.loki"
@ -60,7 +59,7 @@ public class LokiDotNetAPI : NSObject {
// MARK: Private API
private static func requestNewAuthToken(for server: String) -> Promise<String> {
print("[Loki] Requesting auth token for server: \(server).")
let queryParameters = "pubKey=\(userHexEncodedPublicKey)"
let queryParameters = "pubKey=\(getUserHexEncodedPublicKey())"
let url = URL(string: "\(server)/loki/v1/get_challenge?\(queryParameters)")!
let request = TSRequest(url: url)
return LokiFileServerProxy(for: server).perform(request, withCompletionQueue: LokiAPI.workQueue).map(on: LokiAPI.workQueue) { rawResponse in
@ -85,7 +84,7 @@ public class LokiDotNetAPI : NSObject {
private static func submitAuthToken(_ token: String, for server: String) -> Promise<String> {
print("[Loki] Submitting auth token for server: \(server).")
let url = URL(string: "\(server)/loki/v1/submit_challenge")!
let parameters = [ "pubKey" : userHexEncodedPublicKey, "token" : token ]
let parameters = [ "pubKey" : getUserHexEncodedPublicKey(), "token" : token ]
let request = TSRequest(url: url, method: "POST", parameters: parameters)
return LokiFileServerProxy(for: server).perform(request, withCompletionQueue: DispatchQueue.global()).map(on: DispatchQueue.global()) { _ in token }
}

@ -84,14 +84,18 @@ public final class LokiFileServerAPI : LokiDotNetAPI {
return deviceLink
}
})
}.map(on: DispatchQueue.global()) { deviceLinks in
}.then(on: DispatchQueue.global()) { deviceLinks -> Promise<Set<DeviceLink>> in
let (promise, seal) = Promise<Set<DeviceLink>>.pending()
// Dispatch async on the main queue to avoid nested write transactions
DispatchQueue.main.async {
storage.dbReadWriteConnection.readWrite { transaction in
storage.setDeviceLinks(deviceLinks, in: transaction)
}
// We have to wait for the device links to be stored because a lot of our logic relies
// on them being in the database
seal.fulfill(deviceLinks)
}
return deviceLinks
return promise
}
}
}
@ -99,7 +103,7 @@ public final class LokiFileServerAPI : LokiDotNetAPI {
public static func setDeviceLinks(_ deviceLinks: Set<DeviceLink>) -> Promise<Void> {
print("[Loki] Updating device links.")
return getAuthToken(for: server).then { token -> Promise<Void> in
let isMaster = deviceLinks.contains { $0.master.hexEncodedPublicKey == userHexEncodedPublicKey }
let isMaster = deviceLinks.contains { $0.master.hexEncodedPublicKey == getUserHexEncodedPublicKey() }
let deviceLinksAsJSON = deviceLinks.map { $0.toJSON() }
let value = !deviceLinksAsJSON.isEmpty ? [ "isPrimary" : isMaster ? 1 : 0, "authorisations" : deviceLinksAsJSON ] : nil
let annotation: JSON = [ "type" : deviceLinkType, "value" : value ]
@ -120,7 +124,7 @@ public final class LokiFileServerAPI : LokiDotNetAPI {
public static func addDeviceLink(_ deviceLink: DeviceLink) -> Promise<Void> {
var deviceLinks: Set<DeviceLink> = []
storage.dbReadConnection.read { transaction in
deviceLinks = storage.getDeviceLinks(for: userHexEncodedPublicKey, in: transaction)
deviceLinks = storage.getDeviceLinks(for: getUserHexEncodedPublicKey(), in: transaction)
}
deviceLinks.insert(deviceLink)
return setDeviceLinks(deviceLinks).then(on: LokiAPI.workQueue) { _ -> Promise<Void> in
@ -140,7 +144,7 @@ public final class LokiFileServerAPI : LokiDotNetAPI {
public static func removeDeviceLink(_ deviceLink: DeviceLink) -> Promise<Void> {
var deviceLinks: Set<DeviceLink> = []
storage.dbReadConnection.read { transaction in
deviceLinks = storage.getDeviceLinks(for: userHexEncodedPublicKey, in: transaction)
deviceLinks = storage.getDeviceLinks(for: getUserHexEncodedPublicKey(), in: transaction)
}
deviceLinks.remove(deviceLink)
return setDeviceLinks(deviceLinks).then(on: LokiAPI.workQueue) { _ -> Promise<Void> in

@ -33,7 +33,7 @@ extension OnionRequestAPI {
/// Encrypts `payload` for `snode` and returns the result. Use this to build the core of an onion request.
internal static func encrypt(_ payload: JSON, forTargetSnode snode: LokiAPITarget) -> Promise<EncryptionResult> {
let (promise, seal) = Promise<EncryptionResult>.pending()
DispatchQueue.global().async {
DispatchQueue.global(qos: .userInitiated).async {
do {
guard JSONSerialization.isValidJSONObject(payload) else { return seal.reject(HTTP.Error.invalidJSON) }
let payloadAsData = try JSONSerialization.data(withJSONObject: payload, options: [])
@ -53,7 +53,7 @@ extension OnionRequestAPI {
/// Encrypts the previous encryption result (i.e. that of the hop after this one) for this hop. Use this to build the layers of an onion request.
internal static func encryptHop(from lhs: LokiAPITarget, to rhs: LokiAPITarget, using previousEncryptionResult: EncryptionResult) -> Promise<EncryptionResult> {
let (promise, seal) = Promise<EncryptionResult>.pending()
DispatchQueue.global().async {
DispatchQueue.global(qos: .userInitiated).async {
let parameters: JSON = [
"ciphertext" : previousEncryptionResult.ciphertext.base64EncodedString(),
"ephemeral_key" : previousEncryptionResult.ephemeralPublicKey.toHexString(),

@ -19,7 +19,7 @@ public final class LokiPublicChatAPI : LokiDotNetAPI {
// MARK: Convenience
private static var userDisplayName: String {
return SSKEnvironment.shared.contactsManager.displayName(forPhoneIdentifier: userHexEncodedPublicKey) ?? "Anonymous"
return SSKEnvironment.shared.contactsManager.displayName(forPhoneIdentifier: getUserHexEncodedPublicKey()) ?? "Anonymous"
}
// MARK: Database
@ -187,7 +187,7 @@ public final class LokiPublicChatAPI : LokiDotNetAPI {
throw LokiDotNetAPIError.parsingFailed
}
let timestamp = UInt64(date.timeIntervalSince1970) * 1000
return LokiPublicChatMessage(serverID: serverID, hexEncodedPublicKey: userHexEncodedPublicKey, displayName: displayName, profilePicture: signedMessage.profilePicture, body: body, type: publicChatMessageType, timestamp: timestamp, quote: signedMessage.quote, attachments: signedMessage.attachments, signature: signedMessage.signature)
return LokiPublicChatMessage(serverID: serverID, hexEncodedPublicKey: getUserHexEncodedPublicKey(), displayName: displayName, profilePicture: signedMessage.profilePicture, body: body, type: publicChatMessageType, timestamp: timestamp, quote: signedMessage.quote, attachments: signedMessage.attachments, signature: signedMessage.signature)
}
}.recover { error -> Promise<LokiPublicChatMessage> in
if let error = error as? NetworkManagerError, error.statusCode == 401 {

@ -139,6 +139,19 @@ public final class SessionManagementProtocol : NSObject {
return promise
}
@objc(repairSessionIfNeededForMessage:to:)
public static func repairSessionIfNeeded(for message: TSOutgoingMessage, to hexEncodedPublicKey: String) {
guard (message.thread as? TSGroupThread)?.groupModel.groupType == .closedGroup else { return }
DispatchQueue.main.async {
storage.dbReadWriteConnection.readWrite { transaction in
let thread = TSContactThread.getOrCreateThread(withContactId: hexEncodedPublicKey, transaction: transaction)
let sessionRequestMessage = SessionRequestMessage(thread: thread)
let messageSenderJobQueue = SSKEnvironment.shared.messageSenderJobQueue
messageSenderJobQueue.add(message: sessionRequestMessage, transaction: transaction)
}
}
}
// MARK: - Receiving
@objc(handleDecryptionError:forHexEncodedPublicKey:using:)
public static func handleDecryptionError(_ rawValue: Int32, for hexEncodedPublicKey: String, using transaction: YapDatabaseReadWriteTransaction) {
@ -166,6 +179,27 @@ public final class SessionManagementProtocol : NSObject {
return dataMessage.flags & UInt32(sessionRequestFlag.rawValue) != 0
}
@objc(handleSessionRequestMessage:wrappedIn:using:)
public static func handleSessionRequestMessage(_ dataMessage: SSKProtoDataMessage, wrappedIn envelope: SSKProtoEnvelope, using transaction: YapDatabaseReadWriteTransaction) {
// The envelope source is set during UD decryption
// FIXME: Device links should probably update here if they're out of date
let hexEncodedPublicKey = envelope.source!
var validHEPKs: Set<String> = []
TSGroupThread.enumerateCollectionObjects(with: transaction) { object, _ in
guard let group = object as? TSGroupThread, group.groupModel.groupType == .closedGroup,
group.shouldThreadBeVisible else { return }
let closedGroupMembersIncludingLinkedDevices = group.groupModel.groupMemberIds.flatMap {
LokiDatabaseUtilities.getLinkedDeviceHexEncodedPublicKeys(for: $0, in: transaction)
}
validHEPKs.formUnion(closedGroupMembersIncludingLinkedDevices)
}
guard validHEPKs.contains(hexEncodedPublicKey) else { return }
let thread = TSContactThread.getOrCreateThread(withContactId: hexEncodedPublicKey, transaction: transaction)
let ephemeralMessage = EphemeralMessage(in: thread)
let messageSenderJobQueue = SSKEnvironment.shared.messageSenderJobQueue
messageSenderJobQueue.add(message: ephemeralMessage, transaction: transaction)
}
// TODO: This needs an explanation of when we expect pre key bundles to be attached
@objc(handlePreKeyBundleMessageIfNeeded:wrappedIn:using:)
public static func handlePreKeyBundleMessageIfNeeded(_ protoContent: SSKProtoContent, wrappedIn envelope: SSKProtoEnvelope, using transaction: YapDatabaseReadWriteTransaction) {
@ -181,7 +215,7 @@ public final class SessionManagementProtocol : NSObject {
// If we received a friend request (i.e. also a new pre key bundle), but we were already friends with the other user, reset the session.
// The envelope type is set during UD decryption.
// TODO: Should this ignore session requests?
if envelope.type == .friendRequest,
if envelope.type == .friendRequest, // TODO: Should this check that the envelope doesn't have the session request flag set?
let thread = TSContactThread.getWithContactId(hexEncodedPublicKey, transaction: transaction), // TODO: Should this be getOrCreate?
thread.isContactFriend {
receiving_startSessionReset(in: thread, using: transaction)

@ -94,8 +94,7 @@ public final class SyncMessagesProtocol : NSObject {
public static func isValidSyncMessage(_ envelope: SSKProtoEnvelope, in transaction: YapDatabaseReadTransaction) -> Bool {
// The envelope source is set during UD decryption
let hexEncodedPublicKey = envelope.source!
let linkedDeviceHexEncodedPublicKeys = LokiDatabaseUtilities.getLinkedDeviceHexEncodedPublicKeys(for: getUserHexEncodedPublicKey(), in: transaction)
return linkedDeviceHexEncodedPublicKeys.contains(hexEncodedPublicKey)
return LokiDatabaseUtilities.isUserLinkedDevice(hexEncodedPublicKey, transaction: transaction)
}
// TODO: We should probably look at why sync messages are being duplicated rather than doing this

@ -468,7 +468,10 @@ NS_ASSUME_NONNULL_BEGIN
} else if (contentProto.dataMessage) {
// Loki: Don't process session request messages any further
if ([LKSessionManagementProtocol isSessionRequestMessage:contentProto.dataMessage]) { return; }
if ([LKSessionManagementProtocol isSessionRequestMessage:contentProto.dataMessage]) {
[LKSessionManagementProtocol handleSessionRequestMessage:contentProto.dataMessage wrappedIn:envelope using:transaction];
return;
}
// Loki: Don't process session restore messages any further
if ([LKSessionManagementProtocol isSessionRestoreMessage:contentProto.dataMessage]) { return; }
@ -1271,7 +1274,7 @@ NS_ASSUME_NONNULL_BEGIN
}).catchOn(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^(NSError *error) {
dispatch_semaphore_signal(semaphore);
}) retainUntilComplete];
dispatch_semaphore_wait(semaphore, dispatch_time(DISPATCH_TIME_NOW, 8 * NSEC_PER_SEC));
dispatch_semaphore_wait(semaphore, dispatch_time(DISPATCH_TIME_NOW, 10 * NSEC_PER_SEC));
}
// FIXME: ========

@ -1705,6 +1705,7 @@ NSString *const OWSMessageSenderRateLimitedException = @"RateLimitedException";
*/
if (!bundle) {
[LKSessionManagementProtocol repairSessionIfNeededForMessage:messageSend.message to:recipientID];
NSString *missingPrekeyBundleException = @"missingPrekeyBundleException";
OWSRaiseException(missingPrekeyBundleException, @"Missing pre key bundle for: %@.", recipientID);
} else {

Loading…
Cancel
Save