Merge branch 'dev' of https://github.com/loki-project/loki-messenger-ios into sync-closed-group

This commit is contained in:
Ryan ZHAO 2020-02-17 09:20:25 +11:00
commit 68bc25a00c
21 changed files with 243 additions and 258 deletions

2
Pods

@ -1 +1 @@
Subproject commit 0454d60e8db0ebe90ba21ee23d66b2a1e4ab0543
Subproject commit 6fae72d48c06c35c8219ebfc58116450c473b8f1

View File

@ -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>

View File

@ -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]) {

View File

@ -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);

View File

@ -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) {

View File

@ -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) }

View File

@ -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
}
}

View File

@ -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)
}

View File

@ -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)
}
}
}

View File

@ -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.");
}
}

View File

@ -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);

View File

@ -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;

View File

@ -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);

View File

@ -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;
}

View File

@ -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];

View File

@ -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];

View File

@ -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;
}

View File

@ -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))
}
}

View File

@ -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)
}

View File

@ -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

View File

@ -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