Merge branch 'dev' of https://github.com/loki-project/loki-messenger-ios into sync-closed-group
This commit is contained in:
commit
68bc25a00c
2
Pods
2
Pods
|
@ -1 +1 @@
|
|||
Subproject commit 0454d60e8db0ebe90ba21ee23d66b2a1e4ab0543
|
||||
Subproject commit 6fae72d48c06c35c8219ebfc58116450c473b8f1
|
|
@ -5,7 +5,7 @@
|
|||
<key>BuildDetails</key>
|
||||
<dict>
|
||||
<key>CarthageVersion</key>
|
||||
<string>0.34.0</string>
|
||||
<string>0.33.0</string>
|
||||
<key>OSXVersion</key>
|
||||
<string>10.15.3</string>
|
||||
<key>WebRTCCommit</key>
|
||||
|
|
|
@ -3919,8 +3919,6 @@ typedef enum : NSUInteger {
|
|||
|
||||
- (void)tryToSendAttachments:(NSArray<SignalAttachment *> *)attachments messageText:(NSString *_Nullable)messageText
|
||||
{
|
||||
OWSLogError(@"");
|
||||
|
||||
DispatchMainThreadSafe(^{
|
||||
__weak ConversationViewController *weakSelf = self;
|
||||
if ([self isBlockedConversation]) {
|
||||
|
|
|
@ -180,7 +180,7 @@ typedef void (^BuildOutgoingMessageCompletionBlock)(TSOutgoingMessage *savedMess
|
|||
[SignalAttachment attachmentWithDataSource:dataSource dataUTI:kOversizeTextAttachmentUTI];
|
||||
attachments = [mediaAttachments arrayByAddingObject:oversizeTextAttachment];
|
||||
} else {
|
||||
OWSFailDebug(@"dataSource was unexpectedly nil");
|
||||
OWSFailDebug(@"dataSource was unexpectedly nil.");
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -232,8 +232,7 @@ typedef void (^BuildOutgoingMessageCompletionBlock)(TSOutgoingMessage *savedMess
|
|||
|
||||
NSMutableArray<OWSOutgoingAttachmentInfo *> *attachmentInfos = [NSMutableArray new];
|
||||
for (SignalAttachment *attachment in attachments) {
|
||||
OWSOutgoingAttachmentInfo *attachmentInfo =
|
||||
[attachment buildOutgoingAttachmentInfoWithMessage:message];
|
||||
OWSOutgoingAttachmentInfo *attachmentInfo = [attachment buildOutgoingAttachmentInfoWithMessage:message];
|
||||
[attachmentInfos addObject:attachmentInfo];
|
||||
}
|
||||
completionBlock(message, attachmentInfos, writeTransaction);
|
||||
|
|
|
@ -10,24 +10,10 @@ public extension LokiAPI {
|
|||
fileprivate static let failureThreshold = 2
|
||||
|
||||
// MARK: Caching
|
||||
internal static var swarmCache: [String:[LokiAPITarget]] = [:]
|
||||
private static let swarmCacheKey = "swarmCacheKey"
|
||||
private static let swarmCacheCollection = "swarmCacheCollection"
|
||||
|
||||
internal static var swarmCache: [String:[LokiAPITarget]] {
|
||||
get {
|
||||
var result: [String:[LokiAPITarget]]? = nil
|
||||
storage.dbReadConnection.read { transaction in
|
||||
result = transaction.object(forKey: swarmCacheKey, inCollection: swarmCacheCollection) as! [String:[LokiAPITarget]]?
|
||||
}
|
||||
return result ?? [:]
|
||||
}
|
||||
set {
|
||||
storage.dbReadWriteConnection.readWrite { transaction in
|
||||
transaction.setObject(newValue, forKey: swarmCacheKey, inCollection: swarmCacheCollection)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
internal static func dropIfNeeded(_ target: LokiAPITarget, hexEncodedPublicKey: String) {
|
||||
let swarm = LokiAPI.swarmCache[hexEncodedPublicKey]
|
||||
if var swarm = swarm, let index = swarm.firstIndex(of: target) {
|
||||
|
|
|
@ -105,9 +105,17 @@ public final class LokiAPI : NSObject {
|
|||
}
|
||||
|
||||
public static func getDestinations(for hexEncodedPublicKey: String) -> Promise<[Destination]> {
|
||||
var result: Promise<[Destination]>!
|
||||
storage.dbReadConnection.readWrite { transaction in
|
||||
result = getDestinations(for: hexEncodedPublicKey, in: transaction)
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
public static func getDestinations(for hexEncodedPublicKey: String, in transaction: YapDatabaseReadWriteTransaction) -> Promise<[Destination]> {
|
||||
let (promise, seal) = Promise<[Destination]>.pending()
|
||||
func getDestinations() {
|
||||
storage.dbReadConnection.read { transaction in
|
||||
func getDestinations(in transaction: YapDatabaseReadTransaction? = nil) {
|
||||
func getDestinationsInternal(in transaction: YapDatabaseReadTransaction) {
|
||||
var destinations: [Destination] = []
|
||||
let masterHexEncodedPublicKey = storage.getMasterHexEncodedPublicKey(for: hexEncodedPublicKey, in: transaction) ?? hexEncodedPublicKey
|
||||
let masterDestination = Destination(hexEncodedPublicKey: masterHexEncodedPublicKey, kind: .master)
|
||||
|
@ -117,6 +125,13 @@ public final class LokiAPI : NSObject {
|
|||
destinations.append(contentsOf: slaveDestinations)
|
||||
seal.fulfill(destinations)
|
||||
}
|
||||
if let transaction = transaction {
|
||||
getDestinationsInternal(in: transaction)
|
||||
} else {
|
||||
storage.dbReadConnection.read { transaction in
|
||||
getDestinationsInternal(in: transaction)
|
||||
}
|
||||
}
|
||||
}
|
||||
let timeSinceLastUpdate: TimeInterval
|
||||
if let lastDeviceLinkUpdate = lastDeviceLinkUpdate[hexEncodedPublicKey] {
|
||||
|
@ -125,23 +140,21 @@ public final class LokiAPI : NSObject {
|
|||
timeSinceLastUpdate = .infinity
|
||||
}
|
||||
if timeSinceLastUpdate > deviceLinkUpdateInterval {
|
||||
storage.dbReadConnection.read { transaction in
|
||||
let masterHexEncodedPublicKey = storage.getMasterHexEncodedPublicKey(for: hexEncodedPublicKey, in: transaction) ?? hexEncodedPublicKey
|
||||
LokiFileServerAPI.getDeviceLinks(associatedWith: masterHexEncodedPublicKey).done(on: DispatchQueue.global()) { _ in
|
||||
getDestinations()
|
||||
let masterHexEncodedPublicKey = storage.getMasterHexEncodedPublicKey(for: hexEncodedPublicKey, in: transaction) ?? hexEncodedPublicKey
|
||||
LokiFileServerAPI.getDeviceLinks(associatedWith: masterHexEncodedPublicKey, in: transaction).done(on: DispatchQueue.global()) { _ in
|
||||
getDestinations()
|
||||
lastDeviceLinkUpdate[hexEncodedPublicKey] = Date()
|
||||
}.catch(on: DispatchQueue.global()) { error in
|
||||
if (error as? LokiDotNetAPI.LokiDotNetAPIError) == LokiDotNetAPI.LokiDotNetAPIError.parsingFailed {
|
||||
// Don't immediately re-fetch in case of failure due to a parsing error
|
||||
lastDeviceLinkUpdate[hexEncodedPublicKey] = Date()
|
||||
}.catch(on: DispatchQueue.global()) { error in
|
||||
if (error as? LokiDotNetAPI.LokiDotNetAPIError) == LokiDotNetAPI.LokiDotNetAPIError.parsingFailed {
|
||||
// Don't immediately re-fetch in case of failure due to a parsing error
|
||||
lastDeviceLinkUpdate[hexEncodedPublicKey] = Date()
|
||||
getDestinations()
|
||||
} else {
|
||||
seal.reject(error)
|
||||
}
|
||||
getDestinations()
|
||||
} else {
|
||||
seal.reject(error)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
getDestinations()
|
||||
getDestinations(in: transaction)
|
||||
}
|
||||
return promise
|
||||
}
|
||||
|
@ -205,6 +218,12 @@ public final class LokiAPI : NSObject {
|
|||
return AnyPromise.from(promise)
|
||||
}
|
||||
|
||||
@objc(getDestinationsFor:inTransaction:)
|
||||
public static func objc_getDestinations(for hexEncodedPublicKey: String, in transaction: YapDatabaseReadWriteTransaction) -> AnyPromise {
|
||||
let promise = getDestinations(for: hexEncodedPublicKey, in: transaction)
|
||||
return AnyPromise.from(promise)
|
||||
}
|
||||
|
||||
@objc(sendSignalMessage:onP2PSuccess:)
|
||||
public static func objc_sendSignalMessage(_ signalMessage: SignalMessage, onP2PSuccess: @escaping () -> Void) -> AnyPromise {
|
||||
let promise = sendSignalMessage(signalMessage, onP2PSuccess: onP2PSuccess).mapValues { AnyPromise.from($0) }.map { Set($0) }
|
||||
|
|
|
@ -26,18 +26,32 @@ public class LokiDotNetAPI : NSObject {
|
|||
/// To be overridden by subclasses.
|
||||
internal class var authTokenCollection: String { preconditionFailure("authTokenCollection is abstract and must be overridden.") }
|
||||
|
||||
private static func getAuthTokenFromDatabase(for server: String) -> String? {
|
||||
var result: String? = nil
|
||||
storage.dbReadConnection.read { transaction in
|
||||
result = transaction.object(forKey: server, inCollection: authTokenCollection) as! String?
|
||||
private static func getAuthTokenFromDatabase(for server: String, in transaction: YapDatabaseReadTransaction? = nil) -> String? {
|
||||
func getAuthTokenInternal(in transaction: YapDatabaseReadTransaction) -> String? {
|
||||
return transaction.object(forKey: server, inCollection: authTokenCollection) as! String?
|
||||
}
|
||||
if let transaction = transaction {
|
||||
return getAuthTokenInternal(in: transaction)
|
||||
} else {
|
||||
var result: String? = nil
|
||||
storage.dbReadConnection.read { transaction in
|
||||
result = getAuthTokenInternal(in: transaction)
|
||||
}
|
||||
return result
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
private static func setAuthToken(for server: String, to newValue: String) {
|
||||
storage.dbReadWriteConnection.readWrite { transaction in
|
||||
private static func setAuthToken(for server: String, to newValue: String, in transaction: YapDatabaseReadWriteTransaction? = nil) {
|
||||
func setAuthTokenInternal(in transaction: YapDatabaseReadWriteTransaction) {
|
||||
transaction.setObject(newValue, forKey: server, inCollection: authTokenCollection)
|
||||
}
|
||||
if let transaction = transaction {
|
||||
setAuthTokenInternal(in: transaction)
|
||||
} else {
|
||||
storage.dbReadWriteConnection.readWrite { transaction in
|
||||
setAuthTokenInternal(in: transaction)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: Lifecycle
|
||||
|
@ -146,12 +160,12 @@ public class LokiDotNetAPI : NSObject {
|
|||
}
|
||||
|
||||
// MARK: Internal API
|
||||
internal static func getAuthToken(for server: String) -> Promise<String> {
|
||||
if let token = getAuthTokenFromDatabase(for: server) {
|
||||
internal static func getAuthToken(for server: String, in transaction: YapDatabaseReadWriteTransaction? = nil) -> Promise<String> {
|
||||
if let token = getAuthTokenFromDatabase(for: server, in: transaction) {
|
||||
return Promise.value(token)
|
||||
} else {
|
||||
return requestNewAuthToken(for: server).then(on: DispatchQueue.global()) { submitAuthToken($0, for: server) }.map { token -> String in
|
||||
setAuthToken(for: server, to: token)
|
||||
setAuthToken(for: server, to: token, in: transaction)
|
||||
return token
|
||||
}
|
||||
}
|
||||
|
|
|
@ -19,20 +19,20 @@ public final class LokiFileServerAPI : LokiDotNetAPI {
|
|||
// MARK: Device Links (Public API)
|
||||
/// Gets the device links associated with the given hex encoded public key from the
|
||||
/// server and stores and returns the valid ones.
|
||||
public static func getDeviceLinks(associatedWith hexEncodedPublicKey: String) -> Promise<Set<DeviceLink>> {
|
||||
return getDeviceLinks(associatedWith: [ hexEncodedPublicKey ])
|
||||
public static func getDeviceLinks(associatedWith hexEncodedPublicKey: String, in transaction: YapDatabaseReadWriteTransaction? = nil) -> Promise<Set<DeviceLink>> {
|
||||
return getDeviceLinks(associatedWith: [ hexEncodedPublicKey ], in: transaction)
|
||||
}
|
||||
|
||||
/// Gets the device links associated with the given hex encoded public keys from the
|
||||
/// server and stores and returns the valid ones.
|
||||
public static func getDeviceLinks(associatedWith hexEncodedPublicKeys: Set<String>) -> Promise<Set<DeviceLink>> {
|
||||
public static func getDeviceLinks(associatedWith hexEncodedPublicKeys: Set<String>, in transaction: YapDatabaseReadWriteTransaction? = nil) -> Promise<Set<DeviceLink>> {
|
||||
let hexEncodedPublicKeysDescription = "[ \(hexEncodedPublicKeys.joined(separator: ", ")) ]"
|
||||
print("[Loki] Getting device links for: \(hexEncodedPublicKeysDescription).")
|
||||
return getAuthToken(for: server).then(on: DispatchQueue.global()) { token -> Promise<Set<DeviceLink>> in
|
||||
return getAuthToken(for: server, in: transaction).then(on: DispatchQueue.global()) { token -> Promise<Set<DeviceLink>> in
|
||||
let queryParameters = "ids=\(hexEncodedPublicKeys.map { "@\($0)" }.joined(separator: ","))&include_user_annotations=1"
|
||||
let url = URL(string: "\(server)/users?\(queryParameters)")!
|
||||
let request = TSRequest(url: url)
|
||||
return TSNetworkManager.shared().perform(request, withCompletionQueue: DispatchQueue.global()).map { $0.responseObject }.map { rawResponse -> Set<DeviceLink> in
|
||||
return TSNetworkManager.shared().perform(request, withCompletionQueue: DispatchQueue.global()).map(on: DispatchQueue.global()) { $0.responseObject }.map(on: DispatchQueue.global()) { rawResponse -> Set<DeviceLink> in
|
||||
guard let json = rawResponse as? JSON, let data = json["data"] as? [JSON] else {
|
||||
print("[Loki] Couldn't parse device links for users: \(hexEncodedPublicKeys) from: \(rawResponse).")
|
||||
throw LokiDotNetAPIError.parsingFailed
|
||||
|
@ -74,7 +74,7 @@ public final class LokiFileServerAPI : LokiDotNetAPI {
|
|||
return deviceLink
|
||||
}
|
||||
})
|
||||
}.map { deviceLinks -> Set<DeviceLink> in
|
||||
}.map(on: DispatchQueue.global()) { deviceLinks -> Set<DeviceLink> in
|
||||
storage.dbReadWriteConnection.readWrite { transaction in
|
||||
storage.setDeviceLinks(deviceLinks, in: transaction)
|
||||
}
|
||||
|
|
|
@ -83,13 +83,8 @@ public final class LokiLongPoller : NSObject {
|
|||
return LokiAPI.getRawMessages(from: target, usingLongPolling: true).then(on: DispatchQueue.global()) { [weak self] rawResponse -> Promise<Void> in
|
||||
guard let strongSelf = self, !strongSelf.hasStopped else { return Promise.value(()) }
|
||||
let messages = LokiAPI.parseRawMessagesResponse(rawResponse, from: target)
|
||||
let hexEncodedPublicKeys = Set(messages.compactMap { $0.source })
|
||||
let promises = hexEncodedPublicKeys.map { LokiAPI.getDestinations(for: $0) }
|
||||
return when(resolved: promises).then(on: DispatchQueue.global()) { _ -> Promise<Void> in
|
||||
guard let strongSelf = self, !strongSelf.hasStopped else { return Promise.value(()) }
|
||||
strongSelf.onMessagesReceived(messages)
|
||||
return strongSelf.longPoll(target, seal: seal)
|
||||
}
|
||||
strongSelf.onMessagesReceived(messages)
|
||||
return strongSelf.longPoll(target, seal: seal)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -27,9 +27,9 @@ NSString *const kNSNotificationKey_ContactPubKey = @"kNSNotificationKey_ContactP
|
|||
// Our state before we decrypt the message
|
||||
SessionState *_Nullable state = [self getCurrentState:protocolContext];
|
||||
|
||||
// Loki: Verify incoming friend request messages
|
||||
// Verify incoming friend request messages
|
||||
if (!state) {
|
||||
[self throws_verifyFriendRequestAcceptPreKeyForMessage:whisperMessage protocolContext:protocolContext];
|
||||
[self throws_validatePreKeysForFriendRequestAcceptance:whisperMessage protocolContext:protocolContext];
|
||||
}
|
||||
|
||||
// While decrypting our state may change internally
|
||||
|
@ -132,16 +132,16 @@ NSString *const kNSNotificationKey_ContactPubKey = @"kNSNotificationKey_ContactP
|
|||
[self.sessionStore storeSession:self.recipientId deviceId:self.deviceId session:record protocolContext:protocolContext];
|
||||
}
|
||||
|
||||
/// Check that we have matching prekeys in the case of a `PreKeyWhisperMessage`
|
||||
/// This is so that we don't trigger a false friend request accept on unknown contacts
|
||||
- (void)throws_verifyFriendRequestAcceptPreKeyForMessage:(id<CipherMessage>)whisperMessage protocolContext:(nullable id)protocolContext {
|
||||
/// Check that we have matching pre keys in the case of a `PreKeyWhisperMessage`.
|
||||
/// This is so that we don't trigger a false friend request accept on unknown contacts.
|
||||
- (void)throws_validatePreKeysForFriendRequestAcceptance:(id<CipherMessage>)whisperMessage protocolContext:(nullable id)protocolContext {
|
||||
OWSAssertDebug([protocolContext isKindOfClass:[YapDatabaseReadTransaction class]]);
|
||||
YapDatabaseReadTransaction *transaction = protocolContext;
|
||||
|
||||
// We only want to look at `PreKeyWhisperMessage`
|
||||
// Ignore anything that isn't a `PreKeyWhisperMessage`
|
||||
if (![whisperMessage isKindOfClass:[PreKeyWhisperMessage class]]) { return; }
|
||||
|
||||
// We need the primary storage to access contact prekeys
|
||||
// Check the pre key store
|
||||
if (![self.prekeyStore isKindOfClass:[OWSPrimaryStorage class]]) { return; }
|
||||
|
||||
PreKeyWhisperMessage *preKeyMessage = whisperMessage;
|
||||
|
@ -149,11 +149,11 @@ NSString *const kNSNotificationKey_ContactPubKey = @"kNSNotificationKey_ContactP
|
|||
|
||||
PreKeyRecord *_Nullable storedPreKey = [primaryStorage getPreKeyForContact:self.recipientId transaction:transaction];
|
||||
if (!storedPreKey) {
|
||||
OWSRaiseException(@"LokiInvalidPreKey", @"Received a friend request from a public key for which no prekey bundle was created.");
|
||||
OWSRaiseException(@"Loki", @"Received a friend request from a public key for which no pre key bundle was created.");
|
||||
}
|
||||
|
||||
if (storedPreKey.Id != preKeyMessage.prekeyID) {
|
||||
OWSRaiseException(@"LokiPreKeyIdsDontMatch", @"Received a PreKeyWhisperMessage (friend request accept) from an unknown source.");
|
||||
OWSRaiseException(@"Loki", @"Received a PreKeyWhisperMessage (friend request accept) from an unknown source.");
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -759,14 +759,10 @@ NSString *NSStringForOutgoingMessageRecipientState(OWSOutgoingMessageRecipientSt
|
|||
|
||||
[self applyChangeToSelfAndLatestCopy:transaction
|
||||
changeBlock:^(TSOutgoingMessage *message) {
|
||||
TSOutgoingMessageRecipientState *_Nullable recipientState
|
||||
= message.recipientStateMap[recipientId];
|
||||
if (!recipientState) {
|
||||
// OWSFailDebug(@"Missing recipient state for delivered recipient: %@", recipientId);
|
||||
return;
|
||||
}
|
||||
TSOutgoingMessageRecipientState *_Nullable recipientState = message.recipientStateMap[recipientId];
|
||||
if (!recipientState) { return; }
|
||||
if (recipientState.state != OWSOutgoingMessageRecipientStateSent) {
|
||||
OWSLogWarn(@"marking unsent message as delivered.");
|
||||
OWSLogWarn(@"Marking unsent message as delivered.");
|
||||
}
|
||||
recipientState.state = OWSOutgoingMessageRecipientStateSent;
|
||||
recipientState.readTimestamp = @(readTimestamp);
|
||||
|
|
|
@ -9,11 +9,19 @@ NS_ASSUME_NONNULL_BEGIN
|
|||
@class SSKProtoEnvelope;
|
||||
@class YapDatabaseReadWriteTransaction;
|
||||
|
||||
@interface OWSMessageContentQueue : NSObject
|
||||
|
||||
- (dispatch_queue_t)serialQueue;
|
||||
|
||||
@end
|
||||
|
||||
// This class is used to write incoming (decrypted, unprocessed)
|
||||
// messages to a durable queue and then process them in batches,
|
||||
// in the order in which they were received.
|
||||
@interface OWSBatchMessageProcessor : NSObject
|
||||
|
||||
@property (nonatomic, readonly) OWSMessageContentQueue *processingQueue;
|
||||
|
||||
- (instancetype)init NS_UNAVAILABLE;
|
||||
- (instancetype)initWithPrimaryStorage:(OWSPrimaryStorage *)primaryStorage NS_DESIGNATED_INITIALIZER;
|
||||
|
||||
|
|
|
@ -237,7 +237,7 @@ NSString *const OWSMessageContentJobFinderExtensionGroup = @"OWSMessageContentJo
|
|||
|
||||
#pragma mark - Queue Processing
|
||||
|
||||
@interface OWSMessageContentQueue : NSObject
|
||||
@interface OWSMessageContentQueue ()
|
||||
|
||||
@property (nonatomic, readonly) YapDatabaseConnection *dbConnection;
|
||||
@property (nonatomic, readonly) OWSMessageContentJobFinder *finder;
|
||||
|
@ -365,20 +365,12 @@ NSString *const OWSMessageContentJobFinderExtensionGroup = @"OWSMessageContentJo
|
|||
{
|
||||
OWSAssertDebug(AppReadiness.isAppReady);
|
||||
|
||||
// Don't process incoming messages in app extensions.
|
||||
if (!CurrentAppContext().isMainApp) {
|
||||
return;
|
||||
}
|
||||
if (!self.tsAccountManager.isRegisteredAndReady) {
|
||||
return;
|
||||
}
|
||||
if (!CurrentAppContext().isMainApp) { return; }
|
||||
if (!self.tsAccountManager.isRegisteredAndReady) { return; }
|
||||
|
||||
dispatch_async(self.serialQueue, ^{
|
||||
if (self.isDrainingQueue) {
|
||||
return;
|
||||
}
|
||||
if (self.isDrainingQueue) { return; }
|
||||
self.isDrainingQueue = YES;
|
||||
|
||||
[self drainQueueWorkStep];
|
||||
});
|
||||
}
|
||||
|
@ -387,7 +379,7 @@ NSString *const OWSMessageContentJobFinderExtensionGroup = @"OWSMessageContentJo
|
|||
{
|
||||
AssertOnDispatchQueue(self.serialQueue);
|
||||
|
||||
// We want a value that is just high enough to yield perf benefits.
|
||||
// We want a value that is just high enough to yield performance benefits
|
||||
const NSUInteger kIncomingMessageBatchSize = 32;
|
||||
|
||||
NSArray<OWSMessageContentJob *> *batchJobs = [self.finder nextJobsForBatchSize:kIncomingMessageBatchSize];
|
||||
|
@ -431,10 +423,8 @@ NSString *const OWSMessageContentJobFinderExtensionGroup = @"OWSMessageContentJo
|
|||
|
||||
void (^reportFailure)(YapDatabaseReadWriteTransaction *transaction) = ^(
|
||||
YapDatabaseReadWriteTransaction *transaction) {
|
||||
// TODO: Add analytics.
|
||||
TSErrorMessage *errorMessage = [TSErrorMessage corruptedMessageInUnknownThread];
|
||||
[SSKEnvironment.shared.notificationsManager notifyUserForThreadlessErrorMessage:errorMessage
|
||||
transaction:transaction];
|
||||
[SSKEnvironment.shared.notificationsManager notifyUserForThreadlessErrorMessage:errorMessage transaction:transaction];
|
||||
};
|
||||
|
||||
@try {
|
||||
|
@ -449,9 +439,9 @@ NSString *const OWSMessageContentJobFinderExtensionGroup = @"OWSMessageContentJo
|
|||
serverID:0];
|
||||
}
|
||||
} @catch (NSException *exception) {
|
||||
// OWSFailDebug(@"Received an invalid envelope: %@", exception.debugDescription);
|
||||
reportFailure(transaction);
|
||||
}
|
||||
|
||||
[processedJobs addObject:job];
|
||||
|
||||
if (self.isAppInBackground) {
|
||||
|
@ -473,7 +463,6 @@ NSString *const OWSMessageContentJobFinderExtensionGroup = @"OWSMessageContentJo
|
|||
|
||||
@interface OWSBatchMessageProcessor ()
|
||||
|
||||
@property (nonatomic, readonly) OWSMessageContentQueue *processingQueue;
|
||||
@property (nonatomic, readonly) YapDatabaseConnection *dbConnection;
|
||||
|
||||
@end
|
||||
|
@ -528,7 +517,7 @@ NSString *const OWSMessageContentJobFinderExtensionGroup = @"OWSMessageContentJo
|
|||
transaction:(YapDatabaseReadWriteTransaction *)transaction
|
||||
{
|
||||
if (envelopeData.length < 1) {
|
||||
OWSFailDebug(@"Empty envelope.");
|
||||
OWSFailDebug(@"Received an empty envelope.");
|
||||
return;
|
||||
}
|
||||
OWSAssert(transaction);
|
||||
|
|
|
@ -177,7 +177,7 @@ NSError *EnsureDecryptError(NSError *_Nullable error, NSString *fallbackErrorDes
|
|||
OWSMessageDecryptResult *result, YapDatabaseReadWriteTransaction *transaction) {
|
||||
// Ensure all blocked messages are discarded.
|
||||
if ([self isEnvelopeSenderBlocked:envelope]) {
|
||||
OWSLogInfo(@"Ignoring blocked envelope: %@", envelope.source);
|
||||
OWSLogInfo(@"Ignoring blocked envelope from: %@.", envelope.source);
|
||||
return failureBlock();
|
||||
}
|
||||
|
||||
|
@ -195,21 +195,21 @@ NSError *EnsureDecryptError(NSError *_Nullable error, NSString *fallbackErrorDes
|
|||
};
|
||||
|
||||
@try {
|
||||
OWSLogInfo(@"decrypting envelope: %@", [self descriptionForEnvelope:envelope]);
|
||||
OWSLogInfo(@"Decrypting envelope: %@.", [self descriptionForEnvelope:envelope]);
|
||||
|
||||
if (envelope.type != SSKProtoEnvelopeTypeUnidentifiedSender) {
|
||||
if (!envelope.hasSource || envelope.source.length < 1 || ![ECKeyPair isValidHexEncodedPublicKeyWithCandidate:envelope.source]) {
|
||||
OWSFailDebug(@"incoming envelope has invalid source");
|
||||
OWSFailDebug(@"Incoming envelope with invalid source.");
|
||||
return failureBlock();
|
||||
}
|
||||
if (!envelope.hasSourceDevice || envelope.sourceDevice < 1) {
|
||||
OWSFailDebug(@"incoming envelope has invalid source device");
|
||||
OWSFailDebug(@"Incoming envelope with invalid source device.");
|
||||
return failureBlock();
|
||||
}
|
||||
|
||||
// We block UD messages later, after they are decrypted.
|
||||
if ([self isEnvelopeSenderBlocked:envelope]) {
|
||||
OWSLogInfo(@"ignoring blocked envelope: %@", envelope.source);
|
||||
OWSLogInfo(@"Ignoring blocked envelope from: %@.", envelope.source);
|
||||
return failureBlock();
|
||||
}
|
||||
}
|
||||
|
@ -224,7 +224,7 @@ NSError *EnsureDecryptError(NSError *_Nullable error, NSString *fallbackErrorDes
|
|||
successBlock(result, transaction);
|
||||
}
|
||||
failureBlock:^(NSError * _Nullable error) {
|
||||
OWSLogError(@"Decrypting friend request message from address: %@ failed with error: %@.",
|
||||
OWSLogError(@"Decrypting friend request message from: %@ failed with error: %@.",
|
||||
envelopeAddress(envelope),
|
||||
error);
|
||||
failureBlock();
|
||||
|
@ -237,11 +237,11 @@ NSError *EnsureDecryptError(NSError *_Nullable error, NSString *fallbackErrorDes
|
|||
[self throws_decryptSecureMessage:envelope
|
||||
envelopeData:envelopeData
|
||||
successBlock:^(OWSMessageDecryptResult *result, YapDatabaseReadWriteTransaction *transaction) {
|
||||
OWSLogDebug(@"decrypted secure message.");
|
||||
OWSLogDebug(@"Decrypted secure message.");
|
||||
successBlock(result, transaction);
|
||||
}
|
||||
failureBlock:^(NSError *_Nullable error) {
|
||||
OWSLogError(@"decrypting secure message from address: %@ failed with error: %@",
|
||||
OWSLogError(@"Decrypting secure message from: %@ failed with error: %@.",
|
||||
envelopeAddress(envelope),
|
||||
error);
|
||||
OWSProdError([OWSAnalyticsEvents messageManagerErrorCouldNotHandleSecureMessage]);
|
||||
|
@ -254,12 +254,11 @@ NSError *EnsureDecryptError(NSError *_Nullable error, NSString *fallbackErrorDes
|
|||
[self throws_decryptPreKeyBundle:envelope
|
||||
envelopeData:envelopeData
|
||||
successBlock:^(OWSMessageDecryptResult *result, YapDatabaseReadWriteTransaction *transaction) {
|
||||
OWSLogDebug(@"decrypted pre-key whisper message");
|
||||
OWSLogDebug(@"Decrypted pre key bundle message.");
|
||||
successBlock(result, transaction);
|
||||
}
|
||||
failureBlock:^(NSError *_Nullable error) {
|
||||
OWSLogError(@"decrypting pre-key whisper message from address: %@ failed "
|
||||
@"with error: %@",
|
||||
OWSLogError(@"Decrypting pre key bundle message from: %@ failed with error: %@.",
|
||||
envelopeAddress(envelope),
|
||||
error);
|
||||
OWSProdError([OWSAnalyticsEvents messageManagerErrorCouldNotHandlePrekeyBundle]);
|
||||
|
@ -287,12 +286,11 @@ NSError *EnsureDecryptError(NSError *_Nullable error, NSString *fallbackErrorDes
|
|||
case SSKProtoEnvelopeTypeUnidentifiedSender: {
|
||||
[self decryptUnidentifiedSender:envelope
|
||||
successBlock:^(OWSMessageDecryptResult *result, YapDatabaseReadWriteTransaction *transaction) {
|
||||
OWSLogDebug(@"decrypted unidentified sender message");
|
||||
OWSLogDebug(@"Decrypted unidentified sender message.");
|
||||
successBlock(result, transaction);
|
||||
}
|
||||
failureBlock:^(NSError *_Nullable error) {
|
||||
OWSLogError(@"decrypting unidentified sender message from address: %@ failed "
|
||||
@"with error: %@",
|
||||
OWSLogError(@"Decrypting unidentified sender message from: %@ failed with error: %@.",
|
||||
envelopeAddress(envelope),
|
||||
error);
|
||||
OWSProdError([OWSAnalyticsEvents messageManagerErrorCouldNotHandleUnidentifiedSenderMessage]);
|
||||
|
@ -302,11 +300,11 @@ NSError *EnsureDecryptError(NSError *_Nullable error, NSString *fallbackErrorDes
|
|||
return;
|
||||
}
|
||||
default:
|
||||
OWSLogWarn(@"Received unhandled envelope type: %d", (int)envelope.type);
|
||||
OWSLogWarn(@"Received unhandled envelope type: %d.", (int)envelope.type);
|
||||
break;
|
||||
}
|
||||
} @catch (NSException *exception) {
|
||||
OWSFailDebug(@"Received an invalid envelope: %@", exception.debugDescription);
|
||||
OWSFailDebug(@"Received an invalid envelope: %@.", exception.debugDescription);
|
||||
OWSProdFail([OWSAnalyticsEvents messageManagerErrorInvalidProtocolMessage]);
|
||||
|
||||
[[self.primaryStorage newDatabaseConnection]
|
||||
|
@ -342,7 +340,7 @@ NSError *EnsureDecryptError(NSError *_Nullable error, NSString *fallbackErrorDes
|
|||
|
||||
NSData *_Nullable plaintextData = [[cipher decryptWithMessage:encryptedData] removePadding];
|
||||
if (!plaintextData) {
|
||||
NSString *errorString = [NSString stringWithFormat:@"Failed to decrypt friend request message for: %@.", recipientId];
|
||||
NSString *errorString = [NSString stringWithFormat:@"Failed to decrypt friend request message from: %@.", recipientId];
|
||||
NSError *error = OWSErrorWithCodeDescription(OWSErrorCodeFailedToDecryptMessage, errorString);
|
||||
return failureBlock(error);
|
||||
}
|
||||
|
@ -419,7 +417,7 @@ NSError *EnsureDecryptError(NSError *_Nullable error, NSString *fallbackErrorDes
|
|||
NSData *encryptedData = envelope.content ?: envelope.legacyMessage;
|
||||
if (!encryptedData) {
|
||||
OWSProdFail([OWSAnalyticsEvents messageManagerErrorMessageEnvelopeHasNoContent]);
|
||||
NSError *error = OWSErrorWithCodeDescription(OWSErrorCodeFailedToDecryptMessage, @"Envelope has no content");
|
||||
NSError *error = OWSErrorWithCodeDescription(OWSErrorCodeFailedToDecryptMessage, @"Envelope has no content.");
|
||||
return failureBlock(error);
|
||||
}
|
||||
|
||||
|
@ -435,15 +433,8 @@ NSError *EnsureDecryptError(NSError *_Nullable error, NSString *fallbackErrorDes
|
|||
deviceId:deviceId];
|
||||
|
||||
// plaintextData may be nil for some envelope types.
|
||||
NSData *_Nullable plaintextData =
|
||||
[[cipher throws_lokiDecrypt:cipherMessage protocolContext:transaction] removePadding];
|
||||
|
||||
/* Loki: Original code
|
||||
* ================
|
||||
NSData *_Nullable plaintextData =
|
||||
[[cipher throws_decrypt:cipherMessage protocolContext:transaction] removePadding];
|
||||
* ================
|
||||
*/
|
||||
NSData *_Nullable plaintextData = [[cipher throws_lokiDecrypt:cipherMessage protocolContext:transaction] removePadding];
|
||||
|
||||
OWSMessageDecryptResult *result = [OWSMessageDecryptResult resultWithEnvelopeData:envelopeData
|
||||
plaintextData:plaintextData
|
||||
source:envelope.source
|
||||
|
@ -454,7 +445,7 @@ NSError *EnsureDecryptError(NSError *_Nullable error, NSString *fallbackErrorDes
|
|||
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
|
||||
[self processException:exception envelope:envelope];
|
||||
NSString *errorDescription = [NSString
|
||||
stringWithFormat:@"Exception while decrypting %@: %@", cipherTypeName, exception.description];
|
||||
stringWithFormat:@"Exception while decrypting %@: %@.", cipherTypeName, exception.description];
|
||||
OWSLogError(@"%@", errorDescription);
|
||||
NSError *error = OWSErrorWithCodeDescription(OWSErrorCodeFailedToDecryptMessage, errorDescription);
|
||||
failureBlock(error);
|
||||
|
@ -497,8 +488,8 @@ NSError *EnsureDecryptError(NSError *_Nullable error, NSString *fallbackErrorDes
|
|||
identityStore:self.identityManager
|
||||
error:&cipherError];
|
||||
if (cipherError || !cipher) {
|
||||
OWSFailDebug(@"Could not create secret session cipher: %@", cipherError);
|
||||
cipherError = EnsureDecryptError(cipherError, @"Could not create secret session cipher");
|
||||
OWSFailDebug(@"Could not create secret session cipher: %@.", cipherError);
|
||||
cipherError = EnsureDecryptError(cipherError, @"Could not create secret session cipher.");
|
||||
return failureBlock(cipherError);
|
||||
}
|
||||
|
||||
|
@ -514,9 +505,9 @@ NSError *EnsureDecryptError(NSError *_Nullable error, NSString *fallbackErrorDes
|
|||
|
||||
if (!decryptResult) {
|
||||
if (!decryptError) {
|
||||
OWSFailDebug(@"Caller should provide specific error");
|
||||
OWSFailDebug(@"Caller should provide specific error.");
|
||||
NSError *error = OWSErrorWithCodeDescription(
|
||||
OWSErrorCodeFailedToDecryptUDMessage, @"Could not decrypt UD message");
|
||||
OWSErrorCodeFailedToDecryptUDMessage, @"Could not decrypt UD message.");
|
||||
return failureBlock(error);
|
||||
}
|
||||
|
||||
|
@ -565,7 +556,7 @@ NSError *EnsureDecryptError(NSError *_Nullable error, NSString *fallbackErrorDes
|
|||
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
|
||||
[self processException:underlyingException envelope:identifiedEnvelope];
|
||||
NSString *errorDescription = [NSString
|
||||
stringWithFormat:@"Exception while decrypting ud message: %@", underlyingException.description];
|
||||
stringWithFormat:@"Exception while decrypting UD message: %@.", underlyingException.description];
|
||||
OWSLogError(@"%@", errorDescription);
|
||||
NSError *error = OWSErrorWithCodeDescription(OWSErrorCodeFailedToDecryptMessage, errorDescription);
|
||||
failureBlock(error);
|
||||
|
@ -580,7 +571,7 @@ NSError *EnsureDecryptError(NSError *_Nullable error, NSString *fallbackErrorDes
|
|||
return;
|
||||
}
|
||||
|
||||
OWSFailDebug(@"Could not decrypt UD message: %@", underlyingError);
|
||||
OWSFailDebug(@"Could not decrypt UD message: %@.", underlyingError);
|
||||
failureBlock(underlyingError);
|
||||
return;
|
||||
}
|
||||
|
|
|
@ -59,6 +59,8 @@
|
|||
#import <YapDatabase/YapDatabase.h>
|
||||
#import <SignalServiceKit/SignalServiceKit-Swift.h>
|
||||
#import "OWSDispatch.h"
|
||||
#import "OWSBatchMessageProcessor.h"
|
||||
#import "OWSQueues.h"
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
|
@ -258,25 +260,33 @@ NS_ASSUME_NONNULL_BEGIN
|
|||
return;
|
||||
}
|
||||
if (!CurrentAppContext().isMainApp) {
|
||||
OWSFail(@"Not main app.");
|
||||
OWSFail(@"Not the main app.");
|
||||
return;
|
||||
}
|
||||
|
||||
OWSLogInfo(@"handling decrypted envelope: %@", [self descriptionForEnvelope:envelope]);
|
||||
OWSLogInfo(@"Handling decrypted envelope: %@.", [self descriptionForEnvelope:envelope]);
|
||||
|
||||
if (!wasReceivedByUD) {
|
||||
if (!envelope.hasSource || envelope.source.length < 1) {
|
||||
OWSFailDebug(@"incoming envelope has invalid source");
|
||||
OWSFailDebug(@"Incoming envelope with invalid source.");
|
||||
return;
|
||||
}
|
||||
if (!envelope.hasSourceDevice || envelope.sourceDevice < 1) {
|
||||
OWSFailDebug(@"incoming envelope has invalid source device");
|
||||
OWSFailDebug(@"Incoming envelope with invalid source device.");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
OWSAssertDebug(![self isEnvelopeSenderBlocked:envelope]);
|
||||
|
||||
// Loki: Ignore any friend requests from before restoration
|
||||
// The envelope type is set during UD decryption.
|
||||
uint64_t restorationTime = [NSNumber numberWithDouble:[OWSPrimaryStorage.sharedManager getRestorationTime]].unsignedLongLongValue;
|
||||
if (envelope.type == SSKProtoEnvelopeTypeFriendRequest && envelope.timestamp < restorationTime * 1000) {
|
||||
[LKLogger print:@"[Loki] Ignoring friend request received before restoration."];
|
||||
return;
|
||||
}
|
||||
|
||||
[self checkForUnknownLinkedDevice:envelope transaction:transaction];
|
||||
|
||||
switch (envelope.type) {
|
||||
|
@ -408,7 +418,7 @@ NS_ASSUME_NONNULL_BEGIN
|
|||
return;
|
||||
}
|
||||
if (envelope.sourceDevice < 1) {
|
||||
OWSFailDebug(@"Invaid source device.");
|
||||
OWSFailDebug(@"Invalid source device.");
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -417,23 +427,24 @@ NS_ASSUME_NONNULL_BEGIN
|
|||
sourceDeviceId:envelope.sourceDevice
|
||||
transaction:transaction];
|
||||
if (duplicateEnvelope) {
|
||||
OWSLogInfo(@"Ignoring previously received envelope from %@ with timestamp: %llu",
|
||||
OWSLogInfo(@"Ignoring previously received envelope from: %@ with timestamp: %llu.",
|
||||
envelopeAddress(envelope),
|
||||
envelope.timestamp);
|
||||
return;
|
||||
}
|
||||
|
||||
// Loki: Handle friend request acceptance if needed
|
||||
// The envelope type is set during UD decryption.
|
||||
[self handleFriendRequestAcceptanceIfNeededWithEnvelope:envelope transaction:transaction];
|
||||
|
||||
if (envelope.content != nil) {
|
||||
NSError *error;
|
||||
SSKProtoContent *_Nullable contentProto = [SSKProtoContent parseData:plaintextData error:&error];
|
||||
if (error || !contentProto) {
|
||||
OWSFailDebug(@"could not parse proto: %@", error);
|
||||
OWSFailDebug(@"Could not parse proto due to error: %@.", error);
|
||||
return;
|
||||
}
|
||||
OWSLogInfo(@"handling content: <Content: %@>", [self descriptionForContent:contentProto]);
|
||||
OWSLogInfo(@"Handling content: <Content: %@>.", [self descriptionForContent:contentProto]);
|
||||
|
||||
// Loki: Workaround for duplicate sync transcript issue
|
||||
if (contentProto.syncMessage != nil && contentProto.syncMessage.sent != nil) {
|
||||
|
@ -451,7 +462,8 @@ NS_ASSUME_NONNULL_BEGIN
|
|||
}
|
||||
[self.primaryStorage setPreKeyBundle:bundle forContact:envelope.source transaction:transaction];
|
||||
|
||||
// Loki: If we received a friend request, but we were already friends with this user, then reset the session
|
||||
// Loki: If we received a friend request, but we were already friends with this user, reset the session
|
||||
// The envelope type is set during UD decryption.
|
||||
if (envelope.type == SSKProtoEnvelopeTypeFriendRequest) {
|
||||
TSContactThread *thread = [TSContactThread getThreadWithContactId:envelope.source transaction:transaction];
|
||||
if (thread && thread.isContactFriend) {
|
||||
|
@ -549,13 +561,13 @@ NS_ASSUME_NONNULL_BEGIN
|
|||
return;
|
||||
}
|
||||
|
||||
// Loki: Don't process session request messages
|
||||
// Loki: Don't process session request messages any further
|
||||
if ((dataMessage.flags & SSKProtoDataMessageFlagsSessionRequest) != 0) { return; }
|
||||
// Loki: Don't process session restore messages
|
||||
// Loki: Don't process session restore messages any further
|
||||
if ((dataMessage.flags & SSKProtoDataMessageFlagsSessionRestore) != 0) { return; }
|
||||
|
||||
if ([self isDataMessageBlocked:dataMessage envelope:envelope]) {
|
||||
NSString *logMessage = [NSString stringWithFormat:@"Ignoring blocked message from sender: %@", envelope.source];
|
||||
NSString *logMessage = [NSString stringWithFormat:@"Ignoring blocked message from sender: %@.", envelope.source];
|
||||
if (dataMessage.group) {
|
||||
logMessage = [logMessage stringByAppendingFormat:@" in group: %@", dataMessage.group.id];
|
||||
}
|
||||
|
@ -565,14 +577,12 @@ NS_ASSUME_NONNULL_BEGIN
|
|||
|
||||
if (dataMessage.hasTimestamp) {
|
||||
if (dataMessage.timestamp <= 0) {
|
||||
OWSFailDebug(@"Ignoring message with invalid data message timestamp: %@", envelope.source);
|
||||
// TODO: Add analytics.
|
||||
OWSFailDebug(@"Ignoring data message with invalid timestamp: %@.", envelope.source);
|
||||
return;
|
||||
}
|
||||
// This prevents replay attacks by the service.
|
||||
if (dataMessage.timestamp != envelope.timestamp) {
|
||||
OWSFailDebug(@"Ignoring message with non-matching data message timestamp: %@", envelope.source);
|
||||
// TODO: Add analytics.
|
||||
OWSFailDebug(@"Ignoring data message with non-matching timestamp: %@.", envelope.source);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
@ -598,7 +608,7 @@ NS_ASSUME_NONNULL_BEGIN
|
|||
[self sendGroupInfoRequest:dataMessage.group.id envelope:envelope transaction:transaction];
|
||||
return;
|
||||
} else {
|
||||
OWSLogInfo(@"Ignoring group message for unknown group from: %@", envelope.source);
|
||||
OWSLogInfo(@"Ignoring group message for unknown group from: %@.", envelope.source);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
@ -881,7 +891,7 @@ NS_ASSUME_NONNULL_BEGIN
|
|||
|
||||
TSThread *_Nullable thread = [self threadForEnvelope:envelope dataMessage:dataMessage transaction:transaction];
|
||||
if (!thread) {
|
||||
OWSFailDebug(@"ignoring media message for unknown group.");
|
||||
OWSFailDebug(@"Ignoring media message for unknown group.");
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -896,17 +906,17 @@ NS_ASSUME_NONNULL_BEGIN
|
|||
|
||||
[message saveWithTransaction:transaction];
|
||||
|
||||
OWSLogDebug(@"incoming attachment message: %@", message.debugDescription);
|
||||
OWSLogDebug(@"Incoming attachment message: %@.", message.debugDescription);
|
||||
|
||||
[self.attachmentDownloads downloadAttachmentsForMessage:message
|
||||
transaction:transaction
|
||||
success:^(NSArray<TSAttachmentStream *> *attachmentStreams) {
|
||||
OWSLogDebug(@"successfully fetched attachments: %lu for message: %@",
|
||||
OWSLogDebug(@"Successfully fetched attachments: %lu for message: %@.",
|
||||
(unsigned long)attachmentStreams.count,
|
||||
message);
|
||||
}
|
||||
failure:^(NSError *error) {
|
||||
OWSLogError(@"failed to fetch attachments for message: %@ with error: %@", message, error);
|
||||
OWSLogError(@"Failed to fetch attachments for message: %@ with error: %@.", message, error);
|
||||
}];
|
||||
}
|
||||
|
||||
|
@ -1434,6 +1444,21 @@ NS_ASSUME_NONNULL_BEGIN
|
|||
return nil;
|
||||
}
|
||||
|
||||
dispatch_queue_t messageProcessingQueue = SSKEnvironment.shared.batchMessageProcessor.processingQueue.serialQueue;
|
||||
AssertOnDispatchQueue(messageProcessingQueue);
|
||||
|
||||
// The envelope source is set during UD decryption.
|
||||
|
||||
if ([ECKeyPair isValidHexEncodedPublicKeyWithCandidate:envelope.source]) {
|
||||
dispatch_semaphore_t semaphore = dispatch_semaphore_create(0);
|
||||
[[LKAPI getDestinationsFor:envelope.source inTransaction:transaction].ensureOn(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^() {
|
||||
dispatch_semaphore_signal(semaphore);
|
||||
}).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, 10 * NSEC_PER_SEC));
|
||||
}
|
||||
|
||||
if (groupId.length > 0) {
|
||||
NSMutableSet *newMemberIds = [NSMutableSet setWithArray:dataMessage.group.members];
|
||||
NSMutableSet *removedMemberIds = [NSMutableSet new];
|
||||
|
@ -1453,7 +1478,7 @@ NS_ASSUME_NONNULL_BEGIN
|
|||
// We distinguish between the old group state (if any) and the new group state.
|
||||
TSGroupThread *_Nullable oldGroupThread = [TSGroupThread threadWithGroupId:groupId transaction:transaction];
|
||||
if (oldGroupThread) {
|
||||
// Loki: Try to figure out removed members
|
||||
// Loki: Determine removed members
|
||||
removedMemberIds = [NSMutableSet setWithArray:oldGroupThread.groupModel.groupMemberIds];
|
||||
[removedMemberIds minusSet:newMemberIds];
|
||||
[removedMemberIds removeObject:hexEncodedPublicKey];
|
||||
|
@ -1583,7 +1608,7 @@ NS_ASSUME_NONNULL_BEGIN
|
|||
|
||||
// Loki: Don't process friend requests in group chats
|
||||
if (body.length == 0 && attachmentPointers.count < 1 && !contact) {
|
||||
OWSLogWarn(@"ignoring empty incoming message from: %@ for group: %@ with timestamp: %lu",
|
||||
OWSLogWarn(@"Ignoring empty incoming message from: %@ for group: %@ with timestamp: %lu.",
|
||||
hexEncodedPublicKey,
|
||||
groupId,
|
||||
(unsigned long)timestamp);
|
||||
|
@ -1612,14 +1637,14 @@ NS_ASSUME_NONNULL_BEGIN
|
|||
return incomingMessage;
|
||||
}
|
||||
default: {
|
||||
OWSLogWarn(@"Ignoring unknown group message type: %d", (int)dataMessage.group.type);
|
||||
OWSLogWarn(@"Ignoring unknown group message type: %d.", (int)dataMessage.group.type);
|
||||
return nil;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
|
||||
// Loki: A message from a secondary device should appear as if it came from the primary device; the underlying
|
||||
// friend request logic, however, should still be specific to the secondary device.
|
||||
// Loki: A message from a slave device should appear as if it came from the master device; the underlying
|
||||
// friend request logic, however, should still be specific to the slave device.
|
||||
|
||||
// Loki: Get the master hex encoded public key and thread
|
||||
NSString *hexEncodedPublicKey = envelope.source;
|
||||
|
@ -1627,7 +1652,7 @@ NS_ASSUME_NONNULL_BEGIN
|
|||
TSContactThread *thread = [TSContactThread getOrCreateThreadWithContactId:hexEncodedPublicKey transaction:transaction];
|
||||
TSContactThread *masterThread = [TSContactThread getOrCreateThreadWithContactId:masterHexEncodedPublicKey transaction:transaction];
|
||||
|
||||
OWSLogDebug(@"incoming message from: %@ with timestamp: %lu", hexEncodedPublicKey, (unsigned long)timestamp);
|
||||
OWSLogDebug(@"Incoming message from: %@ with timestamp: %lu.", hexEncodedPublicKey, (unsigned long)timestamp);
|
||||
|
||||
[[OWSDisappearingMessagesJob sharedJob] becomeConsistentWithDisappearingDuration:dataMessage.expireTimer
|
||||
thread:masterThread
|
||||
|
@ -1686,7 +1711,7 @@ NS_ASSUME_NONNULL_BEGIN
|
|||
[self handleFriendRequestMessageIfNeededWithEnvelope:envelope data:dataMessage message:incomingMessage thread:thread transaction:transaction];
|
||||
|
||||
if (body.length == 0 && attachmentPointers.count < 1 && !contact) {
|
||||
OWSLogWarn(@"ignoring empty incoming message from: %@ with timestamp: %lu",
|
||||
OWSLogWarn(@"Ignoring empty incoming message from: %@ with timestamp: %lu.",
|
||||
hexEncodedPublicKey,
|
||||
(unsigned long)timestamp);
|
||||
return nil;
|
||||
|
@ -1724,13 +1749,11 @@ NS_ASSUME_NONNULL_BEGIN
|
|||
if (profileKey.length == kAES256_KeyByteLength) {
|
||||
[self.profileManager setProfileKeyData:profileKey forRecipientId:recipientId avatarURL:url];
|
||||
} else {
|
||||
OWSFailDebug(
|
||||
@"Unexpected profile key length:%lu on message from:%@", (unsigned long)profileKey.length, recipientId);
|
||||
OWSFailDebug(@"Unexpected profile key length:%lu on message from:%@", (unsigned long)profileKey.length, recipientId);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Loki: Establish a session if there is no session between the memebers of a group
|
||||
- (void)establishSessionsWithMembersIfNeeded:(NSArray *)members forThread:(TSGroupThread *)thread transaction:(YapDatabaseReadWriteTransaction *)transaction
|
||||
{
|
||||
NSString *userHexEncodedPublicKey = OWSIdentityManager.sharedManager.identityKeyPair.hexEncodedPublicKey;
|
||||
|
@ -1780,6 +1803,7 @@ NS_ASSUME_NONNULL_BEGIN
|
|||
if (envelope.isGroupChatMessage) {
|
||||
return NSLog(@"[Loki] Ignoring friend request in group chat.", @"");
|
||||
}
|
||||
// The envelope type is set during UD decryption.
|
||||
if (envelope.type != SSKProtoEnvelopeTypeFriendRequest) {
|
||||
return NSLog(@"[Loki] Ignoring friend request logic for non friend request type envelope.");
|
||||
}
|
||||
|
@ -1825,6 +1849,7 @@ NS_ASSUME_NONNULL_BEGIN
|
|||
- (void)handleFriendRequestAcceptanceIfNeededWithEnvelope:(SSKProtoEnvelope *)envelope transaction:(YapDatabaseReadWriteTransaction *)transaction {
|
||||
// If we get an envelope that isn't a friend request, then we can infer that we had to use
|
||||
// Signal cipher decryption and thus that we have a session with the other person.
|
||||
// The envelope type is set during UD decryption.
|
||||
if (envelope.isGroupChatMessage || envelope.type == SSKProtoEnvelopeTypeFriendRequest) return;
|
||||
// Currently this uses `envelope.source` but with sync messages we'll need to use the message sender ID
|
||||
TSContactThread *thread = [TSContactThread getOrCreateThreadWithContactId:envelope.source transaction:transaction];
|
||||
|
@ -1917,7 +1942,7 @@ NS_ASSUME_NONNULL_BEGIN
|
|||
}];
|
||||
}
|
||||
failure:^(NSError *error) {
|
||||
OWSLogWarn(@"failed to download attachment for message: %lu with error: %@",
|
||||
OWSLogWarn(@"Failed to download attachment for message: %lu with error: %@.",
|
||||
(unsigned long)incomingMessage.timestamp,
|
||||
error);
|
||||
}];
|
||||
|
@ -2038,7 +2063,7 @@ NS_ASSUME_NONNULL_BEGIN
|
|||
}
|
||||
}
|
||||
|
||||
# pragma mark - Loki Session
|
||||
# pragma mark - Loki Session Handling
|
||||
|
||||
- (void)handleNewSessionAdopted:(NSNotification *)notification {
|
||||
NSString *hexEncodedPublicKey = notification.userInfo[kNSNotificationKey_ContactPubKey];
|
||||
|
|
|
@ -323,20 +323,12 @@ NSString *const OWSMessageDecryptJobFinderExtensionGroup = @"OWSMessageProcessin
|
|||
{
|
||||
OWSAssertDebug(AppReadiness.isAppReady);
|
||||
|
||||
// Don't decrypt messages in app extensions.
|
||||
if (!CurrentAppContext().isMainApp) {
|
||||
return;
|
||||
}
|
||||
if (!self.tsAccountManager.isRegisteredAndReady) {
|
||||
return;
|
||||
}
|
||||
if (!CurrentAppContext().isMainApp) { return; }
|
||||
if (!self.tsAccountManager.isRegisteredAndReady) { return; }
|
||||
|
||||
dispatch_async(self.serialQueue, ^{
|
||||
if (self.isDrainingQueue) {
|
||||
return;
|
||||
}
|
||||
if (self.isDrainingQueue) { return; }
|
||||
self.isDrainingQueue = YES;
|
||||
|
||||
[self drainQueueWorkStep];
|
||||
});
|
||||
}
|
||||
|
@ -346,6 +338,7 @@ NSString *const OWSMessageDecryptJobFinderExtensionGroup = @"OWSMessageProcessin
|
|||
AssertOnDispatchQueue(self.serialQueue);
|
||||
|
||||
OWSMessageDecryptJob *_Nullable job = [self.finder nextJob];
|
||||
|
||||
if (!job) {
|
||||
self.isDrainingQueue = NO;
|
||||
OWSLogVerbose(@"Queue is drained.");
|
||||
|
@ -369,8 +362,7 @@ NSString *const OWSMessageDecryptJobFinderExtensionGroup = @"OWSMessageProcessin
|
|||
|
||||
- (BOOL)wasReceivedByUD:(SSKProtoEnvelope *)envelope
|
||||
{
|
||||
return (
|
||||
envelope.type == SSKProtoEnvelopeTypeUnidentifiedSender && (!envelope.hasSource || envelope.source.length < 1));
|
||||
return (envelope.type == SSKProtoEnvelopeTypeUnidentifiedSender && (!envelope.hasSource || envelope.source.length < 1));
|
||||
}
|
||||
|
||||
- (void)processJob:(OWSMessageDecryptJob *)job completion:(void (^)(BOOL))completion
|
||||
|
@ -379,9 +371,9 @@ NSString *const OWSMessageDecryptJobFinderExtensionGroup = @"OWSMessageProcessin
|
|||
OWSAssertDebug(job);
|
||||
|
||||
SSKProtoEnvelope *_Nullable envelope = job.envelopeProto;
|
||||
|
||||
if (!envelope) {
|
||||
OWSFailDebug(@"Could not parse proto.");
|
||||
// TODO: Add analytics.
|
||||
OWSFailDebug(@"Couldn't parse proto.");
|
||||
|
||||
[self.dbConnection readWriteWithBlock:^(YapDatabaseReadWriteTransaction *transaction) {
|
||||
TSErrorMessage *errorMessage = [TSErrorMessage corruptedMessageInUnknownThread];
|
||||
|
@ -392,25 +384,7 @@ NSString *const OWSMessageDecryptJobFinderExtensionGroup = @"OWSMessageProcessin
|
|||
dispatch_async(self.serialQueue, ^{
|
||||
completion(NO);
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
// Loki: Don't process any messages from ourself
|
||||
ECKeyPair *_Nullable keyPair = OWSIdentityManager.sharedManager.identityKeyPair;
|
||||
if (keyPair && [envelope.source isEqualToString:keyPair.hexEncodedPublicKey]) {
|
||||
dispatch_async(self.serialQueue, ^{
|
||||
completion(YES);
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
// Loki: Ignore any friend requests that we got before restoration
|
||||
uint64_t restorationTime = [NSNumber numberWithDouble:[OWSPrimaryStorage.sharedManager getRestorationTime]].unsignedLongLongValue;
|
||||
if (envelope.type == SSKProtoEnvelopeTypeFriendRequest && envelope.timestamp < restorationTime * 1000) {
|
||||
[LKLogger print:@"[Loki] Ignoring friend request received before restoration."];
|
||||
dispatch_async(self.serialQueue, ^{
|
||||
completion(YES);
|
||||
});
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -423,6 +397,15 @@ NSString *const OWSMessageDecryptJobFinderExtensionGroup = @"OWSMessageProcessin
|
|||
successBlock:^(OWSMessageDecryptResult *result, YapDatabaseReadWriteTransaction *transaction) {
|
||||
OWSAssertDebug(transaction);
|
||||
|
||||
// Loki: Don't process any messages from ourself
|
||||
ECKeyPair *_Nullable keyPair = OWSIdentityManager.sharedManager.identityKeyPair;
|
||||
if (keyPair && [result.source isEqualToString:keyPair.hexEncodedPublicKey]) {
|
||||
dispatch_async(self.serialQueue, ^{
|
||||
completion(YES);
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
// We persist the decrypted envelope data in the same transaction within which
|
||||
// it was decrypted to prevent data loss. If the new job isn't persisted,
|
||||
// the session state side effects of its decryption are also rolled back.
|
||||
|
@ -472,8 +455,7 @@ NSString *const OWSMessageDecryptJobFinderExtensionGroup = @"OWSMessageProcessin
|
|||
// For coherency we use the same dbConnection to persist and read the unprocessed envelopes
|
||||
YapDatabaseConnection *dbConnection = [primaryStorage newDatabaseConnection];
|
||||
OWSMessageDecryptJobFinder *finder = [[OWSMessageDecryptJobFinder alloc] initWithDBConnection:dbConnection];
|
||||
OWSMessageDecryptQueue *processingQueue =
|
||||
[[OWSMessageDecryptQueue alloc] initWithDBConnection:dbConnection finder:finder];
|
||||
OWSMessageDecryptQueue *processingQueue = [[OWSMessageDecryptQueue alloc] initWithDBConnection:dbConnection finder:finder];
|
||||
|
||||
_processingQueue = processingQueue;
|
||||
|
||||
|
@ -503,7 +485,7 @@ NSString *const OWSMessageDecryptJobFinderExtensionGroup = @"OWSMessageProcessin
|
|||
- (void)handleReceivedEnvelopeData:(NSData *)envelopeData
|
||||
{
|
||||
if (envelopeData.length < 1) {
|
||||
OWSFailDebug(@"Empty envelope.");
|
||||
OWSFailDebug(@"Received an empty envelope.");
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -511,7 +493,7 @@ NSString *const OWSMessageDecryptJobFinderExtensionGroup = @"OWSMessageProcessin
|
|||
NSUInteger kMaxEnvelopeByteCount = 250 * 1024;
|
||||
if (envelopeData.length > kMaxEnvelopeByteCount) {
|
||||
OWSProdError([OWSAnalyticsEvents messageReceiverErrorOversizeMessage]);
|
||||
OWSFailDebug(@"Oversize message.");
|
||||
OWSFailDebug(@"Received an oversized message.");
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -520,7 +502,7 @@ NSString *const OWSMessageDecryptJobFinderExtensionGroup = @"OWSMessageProcessin
|
|||
NSUInteger kLargeEnvelopeWarningByteCount = 25 * 1024;
|
||||
if (envelopeData.length > kLargeEnvelopeWarningByteCount) {
|
||||
OWSProdError([OWSAnalyticsEvents messageReceiverErrorLargeMessage]);
|
||||
OWSFailDebug(@"Unexpectedly large message.");
|
||||
OWSFailDebug(@"Received an unexpectedly large message.");
|
||||
}
|
||||
|
||||
[self.processingQueue enqueueEnvelopeData:envelopeData];
|
||||
|
|
|
@ -186,9 +186,7 @@ void AssertIsOnSendingQueue()
|
|||
- (nullable NSError *)checkForPreconditionError
|
||||
{
|
||||
__block NSError *_Nullable error = [super checkForPreconditionError];
|
||||
if (error) {
|
||||
return error;
|
||||
}
|
||||
if (error) { return error; }
|
||||
|
||||
// Sanity check preconditions
|
||||
if (self.message.hasAttachments) {
|
||||
|
@ -214,9 +212,8 @@ void AssertIsOnSendingQueue()
|
|||
{
|
||||
// If the message has been deleted, abort send.
|
||||
if (self.message.shouldBeSaved && ![TSOutgoingMessage fetchObjectWithUniqueID:self.message.uniqueId]) {
|
||||
OWSLogInfo(@"aborting message send; message deleted.");
|
||||
NSError *error = OWSErrorWithCodeDescription(
|
||||
OWSErrorCodeMessageDeletedBeforeSent, @"Message was deleted before it could be sent.");
|
||||
OWSLogInfo(@"Aborting message send; message deleted.");
|
||||
NSError *error = OWSErrorWithCodeDescription(OWSErrorCodeMessageDeletedBeforeSent, @"Message was deleted before it could be sent.");
|
||||
error.isFatal = YES;
|
||||
[self reportError:error];
|
||||
return;
|
||||
|
@ -233,16 +230,12 @@ void AssertIsOnSendingQueue()
|
|||
|
||||
- (void)didSucceed
|
||||
{
|
||||
if (self.message.messageState != TSOutgoingMessageStateSent) {
|
||||
// OWSFailDebug(@"unexpected message status: %@", self.message.statusDescription);
|
||||
}
|
||||
|
||||
self.successHandler();
|
||||
}
|
||||
|
||||
- (void)didFailWithError:(NSError *)error
|
||||
{
|
||||
OWSLogError(@"failed with error: %@", error);
|
||||
OWSLogError(@"Message failed to send due to error: %@.", error);
|
||||
self.failureHandler(error);
|
||||
}
|
||||
|
||||
|
@ -370,10 +363,10 @@ NSString *const OWSMessageSenderRateLimitedException = @"RateLimitedException";
|
|||
//
|
||||
// That's key - we don't want to send any messages in response
|
||||
// to an incoming message until processing of that batch of messages
|
||||
// is complete. For example, we wouldn't want to auto-reply to a
|
||||
// is complete. For example, we wouldn't want to auto-reply to a
|
||||
// group info request before that group info request's batch was
|
||||
// finished processing. Otherwise, we might receive a delivery
|
||||
// notice for a group update we hadn't yet saved to the db.
|
||||
// finished processing. Otherwise, we might receive a delivery
|
||||
// notice for a group update we hadn't yet saved to the database.
|
||||
//
|
||||
// So we're using YDB behavior to ensure this invariant, which is a bit
|
||||
// unorthodox.
|
||||
|
@ -634,7 +627,7 @@ NSString *const OWSMessageSenderRateLimitedException = @"RateLimitedException";
|
|||
successHandlerParam();
|
||||
}
|
||||
failure:^(NSError *error) {
|
||||
OWSLogError(@"Error sending sync message for message: %@ timestamp: %llu",
|
||||
OWSLogError(@"Error sending sync message for message: %@ timestamp: %llu.",
|
||||
message.class,
|
||||
message.timestamp);
|
||||
|
||||
|
@ -650,13 +643,12 @@ NSString *const OWSMessageSenderRateLimitedException = @"RateLimitedException";
|
|||
failureHandlerParam(error);
|
||||
}
|
||||
failure:^(NSError *syncError) {
|
||||
OWSLogError(@"Error sending sync message for message: %@ timestamp: %llu, %@",
|
||||
OWSLogError(@"Error sending sync message for message: %@ timestamp: %llu, %@.",
|
||||
message.class,
|
||||
message.timestamp,
|
||||
syncError);
|
||||
|
||||
// Discard the "sync message" error in favor of the
|
||||
// original error.
|
||||
// Discard the sync message error in favor of the original error
|
||||
failureHandlerParam(error);
|
||||
}];
|
||||
});
|
||||
|
@ -1063,7 +1055,7 @@ NSString *const OWSMessageSenderRateLimitedException = @"RateLimitedException";
|
|||
return messageSend.success();
|
||||
}
|
||||
|
||||
OWSLogInfo(@"attempting to send message: %@, timestamp: %llu, recipient: %@",
|
||||
OWSLogInfo(@"Attempting to send message: %@, timestamp: %llu, recipient: %@.",
|
||||
message.class,
|
||||
message.timestamp,
|
||||
recipient.uniqueId);
|
||||
|
@ -1079,13 +1071,13 @@ NSString *const OWSMessageSenderRateLimitedException = @"RateLimitedException";
|
|||
// re-enable message sending.
|
||||
[TSPreKeyManager
|
||||
rotateSignedPreKeyWithSuccess:^{
|
||||
OWSLogInfo(@"New prekeys registered with server.");
|
||||
OWSLogInfo(@"New pre keys registered with server.");
|
||||
NSError *error = OWSErrorMakeMessageSendDisabledDueToPreKeyUpdateFailuresError();
|
||||
[error setIsRetryable:YES];
|
||||
return messageSend.failure(error);
|
||||
}
|
||||
failure:^(NSError *error) {
|
||||
OWSLogWarn(@"Failed to update prekeys with the server: %@", error);
|
||||
OWSLogWarn(@"Failed to update pre keys with the server due to error: %@.", error);
|
||||
return messageSend.failure(error);
|
||||
}];
|
||||
}
|
||||
|
@ -1208,16 +1200,10 @@ NSString *const OWSMessageSenderRateLimitedException = @"RateLimitedException";
|
|||
// Loki: TSFriendRequestMessageType represents a Loki friend request
|
||||
NSArray *validMessageTypes = @[ @(TSEncryptedWhisperMessageType), @(TSPreKeyWhisperMessageType), @(TSFriendRequestMessageType) ];
|
||||
hasValidMessageType = [validMessageTypes containsObject:messageType];
|
||||
|
||||
/* Loki: Original code
|
||||
* ========
|
||||
hasValidMessageType = ([messageType isEqualToNumber:@(TSEncryptedWhisperMessageType)] || [messageType isEqualToNumber:@(TSPreKeyWhisperMessageType)]);
|
||||
* ========
|
||||
*/
|
||||
}
|
||||
|
||||
if (!hasValidMessageType) {
|
||||
OWSFailDebug(@"Invalid message type: %@", messageType);
|
||||
OWSFailDebug(@"Invalid message type: %@.", messageType);
|
||||
NSError *error = OWSErrorMakeFailedToSendOutgoingMessageError();
|
||||
[error setIsRetryable:NO];
|
||||
return messageSend.failure(error);
|
||||
|
@ -1303,7 +1289,7 @@ NSString *const OWSMessageSenderRateLimitedException = @"RateLimitedException";
|
|||
}];
|
||||
[self messageSendDidSucceed:messageSend deviceMessages:deviceMessages wasSentByUD:messageSend.isUDSend wasSentByWebsocket:false];
|
||||
})
|
||||
.catchOn(OWSDispatch.sendingQueue, ^(NSError *error) { // The snode is unreachable
|
||||
.catchOn(OWSDispatch.sendingQueue, ^(NSError *error) {
|
||||
failedMessageSend(error);
|
||||
}) retainUntilComplete];
|
||||
} else {
|
||||
|
@ -1359,7 +1345,7 @@ NSString *const OWSMessageSenderRateLimitedException = @"RateLimitedException";
|
|||
// Handle the error
|
||||
failedMessageSend(error);
|
||||
};
|
||||
// Send the message using the Loki API
|
||||
// Send the message
|
||||
[[LKAPI sendSignalMessage:signalMessage onP2PSuccess:onP2PSuccess]
|
||||
.thenOn(OWSDispatch.sendingQueue, ^(id result) {
|
||||
NSSet<AnyPromise *> *promises = (NSSet<AnyPromise *> *)result;
|
||||
|
@ -1735,12 +1721,11 @@ NSString *const OWSMessageSenderRateLimitedException = @"RateLimitedException";
|
|||
|
||||
NSData *_Nullable plainText = [messageSend.message buildPlainTextData:recipient];
|
||||
if (!plainText) {
|
||||
OWSRaiseException(InvalidMessageException, @"Failed to build message proto");
|
||||
OWSRaiseException(InvalidMessageException, @"Failed to build message proto.");
|
||||
}
|
||||
OWSLogDebug(
|
||||
@"built message: %@ plainTextData.length: %lu", [messageSend.message class], (unsigned long)plainText.length);
|
||||
OWSLogDebug(@"Built message: %@ plainTextData.length: %lu", [messageSend.message class], (unsigned long)plainText.length);
|
||||
|
||||
OWSLogVerbose(@"building device messages for: %@ %@ (isLocalNumber: %d, isUDSend: %d)",
|
||||
OWSLogVerbose(@"Building device messages for: %@ %@ (isLocalNumber: %d, isUDSend: %d).",
|
||||
recipient.recipientId,
|
||||
recipient.devices,
|
||||
messageSend.isLocalNumber,
|
||||
|
@ -1754,7 +1739,7 @@ NSString *const OWSMessageSenderRateLimitedException = @"RateLimitedException";
|
|||
// This may involve blocking network requests, so we do it _before_
|
||||
// we open a transaction.
|
||||
|
||||
// Loki: Both for friend request messages and device link messages we don't require a session
|
||||
// Loki: We don't require a session for friend requests, session requests and device link requests
|
||||
BOOL isFriendRequest = [messageSend.message isKindOfClass:LKFriendRequestMessage.class];
|
||||
BOOL isSessionRequest = [messageSend.message isKindOfClass:LKSessionRequestMessage.class];
|
||||
BOOL isDeviceLinkMessage = [messageSend.message isKindOfClass:LKDeviceLinkMessage.class];
|
||||
|
@ -1777,14 +1762,14 @@ NSString *const OWSMessageSenderRateLimitedException = @"RateLimitedException";
|
|||
}];
|
||||
|
||||
if (encryptionException) {
|
||||
OWSLogInfo(@"Exception during encryption: %@", encryptionException);
|
||||
OWSLogInfo(@"Exception during encryption: %@.", encryptionException);
|
||||
@throw encryptionException;
|
||||
}
|
||||
|
||||
if (messageDict) {
|
||||
[messagesArray addObject:messageDict];
|
||||
} else {
|
||||
OWSRaiseException(InvalidMessageException, @"Failed to encrypt message");
|
||||
OWSRaiseException(InvalidMessageException, @"Failed to encrypt message.");
|
||||
}
|
||||
} @catch (NSException *exception) {
|
||||
if ([exception.name isEqualToString:OWSMessageSenderInvalidDeviceException]) {
|
||||
|
@ -2001,16 +1986,16 @@ NSString *const OWSMessageSenderRateLimitedException = @"RateLimitedException";
|
|||
OWSPrimaryStorage *storage = self.primaryStorage;
|
||||
TSOutgoingMessage *message = messageSend.message;
|
||||
|
||||
// Loki: Both for friend request messages and device link messages we use fallback encryption as we don't necessarily have a session yet
|
||||
// Loki: Use fallback encryption for friend requests, session requests and device link requests
|
||||
BOOL isFriendRequest = [messageSend.message isKindOfClass:LKFriendRequestMessage.class];
|
||||
BOOL isSessionRequest = [messageSend.message isKindOfClass:LKSessionRequestMessage.class];
|
||||
BOOL isDeviceLinkMessage = [messageSend.message isKindOfClass:LKDeviceLinkMessage.class] && ((LKDeviceLinkMessage *)messageSend.message).kind == LKDeviceLinkMessageKindRequest;
|
||||
|
||||
// This may throw an exception.
|
||||
// This may throw an exception
|
||||
if (!isFriendRequest && !isSessionRequest && !isDeviceLinkMessage && ![storage containsSession:recipientID deviceId:@(OWSDevicePrimaryDeviceId).intValue protocolContext:transaction]) {
|
||||
NSString *missingSessionException = @"missingSessionException";
|
||||
OWSRaiseException(missingSessionException,
|
||||
@"Unexpectedly missing session for recipient: %@, device: %@",
|
||||
@"Unexpectedly missing session for recipient: %@, device: %@.",
|
||||
recipientID,
|
||||
@(OWSDevicePrimaryDeviceId));
|
||||
}
|
||||
|
@ -2043,14 +2028,15 @@ NSString *const OWSMessageSenderRateLimitedException = @"RateLimitedException";
|
|||
protocolContext:transaction
|
||||
isFriendRequest:isFriendRequest || isDeviceLinkMessage
|
||||
error:&error];
|
||||
|
||||
SCKRaiseIfExceptionWrapperError(error);
|
||||
if (!serializedMessage || error) {
|
||||
OWSFailDebug(@"error while UD encrypting message: %@", error);
|
||||
OWSFailDebug(@"Error while UD encrypting message: %@.", error);
|
||||
return nil;
|
||||
}
|
||||
messageType = TSUnidentifiedSenderMessageType;
|
||||
} else {
|
||||
// This may throw an exception.
|
||||
// This may throw an exception
|
||||
id<CipherMessage> encryptedMessage =
|
||||
[cipher throws_encryptMessage:[plainText paddedMessageBody] protocolContext:transaction];
|
||||
serializedMessage = encryptedMessage.serialized;
|
||||
|
@ -2185,7 +2171,7 @@ NSString *const OWSMessageSenderRateLimitedException = @"RateLimitedException";
|
|||
if ([attachment isKindOfClass:[TSAttachmentStream class]]) {
|
||||
[attachmentIds addObject:attachment.uniqueId];
|
||||
} else {
|
||||
OWSFailDebug(@"unexpected avatarAttachment: %@", attachment);
|
||||
OWSFailDebug(@"Unexpected avatarAttachment: %@.", attachment);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -2195,7 +2181,7 @@ NSString *const OWSMessageSenderRateLimitedException = @"RateLimitedException";
|
|||
if ([attachment isKindOfClass:[TSAttachmentStream class]]) {
|
||||
[attachmentIds addObject:attachment.uniqueId];
|
||||
} else {
|
||||
OWSFailDebug(@"unexpected attachment: %@", attachment);
|
||||
OWSFailDebug(@"Unexpected attachment: %@.", attachment);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -2223,6 +2209,7 @@ NSString *const OWSMessageSenderRateLimitedException = @"RateLimitedException";
|
|||
sourceFilename:attachmentInfo.sourceFilename
|
||||
caption:attachmentInfo.caption
|
||||
albumMessageId:attachmentInfo.albumMessageId];
|
||||
|
||||
if (outgoingMessage.isVoiceMessage) {
|
||||
attachmentStream.attachmentType = TSAttachmentTypeVoiceMessage;
|
||||
}
|
||||
|
|
|
@ -425,14 +425,14 @@ public class OWSUDManagerImpl: NSObject, OWSUDManager {
|
|||
|
||||
private func generateSenderCertificate() -> Promise<(certificateData: Data, certificate: SMKSenderCertificate)> {
|
||||
return Promise<(certificateData: Data, certificate: SMKSenderCertificate)> { seal in
|
||||
//Loki: Generate a sender certifate locally
|
||||
// Loki: Generate a sender certifate locally
|
||||
let sender = OWSIdentityManager.shared().identityKeyPair()?.hexEncodedPublicKey
|
||||
let certificate = SMKSenderCertificate(senderDeviceId: OWSDevicePrimaryDeviceId, senderRecipientId: sender!)
|
||||
let certificateData = try certificate.serialized()
|
||||
guard self.isValidCertificate(certificate) else {
|
||||
throw OWSUDError.invalidData(description: "Invalid sender certificate returned by server")
|
||||
let certificateAsData = try certificate.serialized()
|
||||
guard isValidCertificate(certificate) else {
|
||||
throw OWSUDError.invalidData(description: "Invalid sender certificate.")
|
||||
}
|
||||
seal.fulfill((certificateData: certificateData, certificate: certificate))
|
||||
seal.fulfill((certificateData: certificateAsData, certificate: certificate))
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -34,8 +34,6 @@ public class MessageSenderJobQueue: NSObject, JobQueue {
|
|||
}
|
||||
}
|
||||
|
||||
// MARK:
|
||||
|
||||
@objc(addMessage:transaction:)
|
||||
public func add(message: TSOutgoingMessage, transaction: YapDatabaseReadWriteTransaction) {
|
||||
self.add(message: message, removeMessageAfterSending: false, transaction: transaction)
|
||||
|
@ -71,7 +69,7 @@ public class MessageSenderJobQueue: NSObject, JobQueue {
|
|||
do {
|
||||
jobRecord = try SSKMessageSenderJobRecord(message: message, removeMessageAfterSending: false, label: self.jobRecordLabel)
|
||||
} catch {
|
||||
owsFailDebug("failed to build job: \(error)")
|
||||
owsFailDebug("Failed to build job due to error: \(error).")
|
||||
return
|
||||
}
|
||||
self.add(jobRecord: jobRecord, transaction: transaction)
|
||||
|
@ -121,7 +119,7 @@ public class MessageSenderJobQueue: NSObject, JobQueue {
|
|||
message = fetchedMessage
|
||||
} else {
|
||||
assert(jobRecord.messageId != nil)
|
||||
throw JobError.obsolete(description: "message no longer exists")
|
||||
throw JobError.obsolete(description: "Message no longer exists.")
|
||||
}
|
||||
|
||||
return MessageSenderOperation(message: message, jobRecord: jobRecord)
|
||||
|
@ -205,8 +203,6 @@ public class MessageSenderOperation: OWSOperation, DurableOperation {
|
|||
}
|
||||
|
||||
override public func didReportError(_ error: Error) {
|
||||
Logger.debug("remainingRetries: \(self.remainingRetries)")
|
||||
|
||||
self.dbConnection.readWrite { transaction in
|
||||
self.durableOperationDelegate?.durableOperation(self, didReportError: error, transaction: transaction)
|
||||
}
|
||||
|
|
|
@ -38,7 +38,7 @@ class SAEFailedViewController: UIViewController {
|
|||
self.navigationItem.leftBarButtonItem = UIBarButtonItem(barButtonSystemItem: .cancel,
|
||||
target: self,
|
||||
action: #selector(cancelPressed))
|
||||
self.navigationItem.title = "Signal"
|
||||
self.navigationItem.title = "Session"
|
||||
|
||||
self.view.backgroundColor = UIColor.ows_signalBrandBlue
|
||||
|
||||
|
|
|
@ -59,7 +59,7 @@ class SAELoadViewController: UIViewController {
|
|||
self.navigationItem.leftBarButtonItem = UIBarButtonItem(barButtonSystemItem: .cancel,
|
||||
target: self,
|
||||
action: #selector(cancelPressed))
|
||||
self.navigationItem.title = "Signal"
|
||||
self.navigationItem.title = "Session"
|
||||
|
||||
self.view.backgroundColor = UIColor.ows_signalBrandBlue
|
||||
|
||||
|
|
Loading…
Reference in New Issue