diff --git a/Podfile b/Podfile
index 20a697599..e1988cd15 100644
--- a/Podfile
+++ b/Podfile
@@ -22,7 +22,8 @@ def shared_pods
pod 'Curve25519Kit', git: 'https://github.com/signalapp/Curve25519Kit', testspecs: ["Tests"]
# pod 'Curve25519Kit', path: '../Curve25519Kit', testspecs: ["Tests"]
- pod 'SignalMetadataKit', git: 'https://github.com/signalapp/SignalMetadataKit', testspecs: ["Tests"]
+ # Don't update SignalMetadataKit. There's some Loki specific stuff in there that gets overwritten otherwise.
+ # pod 'SignalMetadataKit', git: 'https://github.com/signalapp/SignalMetadataKit', testspecs: ["Tests"]
# pod 'SignalMetadataKit', path: '../SignalMetadataKit', testspecs: ["Tests"]
pod 'SignalServiceKit', path: '.', testspecs: ["Tests"]
diff --git a/Signal.xcodeproj/xcshareddata/xcschemes/SignalMessaging.xcscheme b/Signal.xcodeproj/xcshareddata/xcschemes/SignalMessaging.xcscheme
new file mode 100644
index 000000000..8362675e7
--- /dev/null
+++ b/Signal.xcodeproj/xcshareddata/xcschemes/SignalMessaging.xcscheme
@@ -0,0 +1,67 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/Signal/Signal-Info.plist b/Signal/Signal-Info.plist
index 88d659202..fefaeb1ed 100644
--- a/Signal/Signal-Info.plist
+++ b/Signal/Signal-Info.plist
@@ -113,7 +113,7 @@
NSContactsUsageDescription
Signal uses your contacts to find users you know. We do not store your contacts on the server.
NSFaceIDUsageDescription
- Session's Screen Lock feature uses Face ID.
+ Session's Screen Lock feature uses Face ID.
NSMicrophoneUsageDescription
Session needs access to your microphone to record videos.
NSPhotoLibraryAddUsageDescription
diff --git a/SignalServiceKit/src/Loki/API/LokiHttpClient.swift b/SignalServiceKit/src/Loki/API/LokiHTTPClient.swift
similarity index 100%
rename from SignalServiceKit/src/Loki/API/LokiHttpClient.swift
rename to SignalServiceKit/src/Loki/API/LokiHTTPClient.swift
diff --git a/SignalServiceKit/src/Loki/Crypto/SessionCipher+Loki.m b/SignalServiceKit/src/Loki/Crypto/SessionCipher+Loki.m
index 467fe63a2..06e6b2e34 100644
--- a/SignalServiceKit/src/Loki/Crypto/SessionCipher+Loki.m
+++ b/SignalServiceKit/src/Loki/Crypto/SessionCipher+Loki.m
@@ -1,160 +1,160 @@
-// Loki: Refer to Docs/SessionReset.md for explanations
-
-#import "SessionCipher+Loki.h"
-#import "NSNotificationCenter+OWS.h"
-#import "PreKeyWhisperMessage.h"
-#import "OWSPrimaryStorage+Loki.h"
-#import "TSContactThread.h"
-#import
-
-NSString *const kNSNotificationName_SessionAdopted = @"kNSNotificationName_SessionAdopted";
-NSString *const kNSNotificationKey_ContactPubKey = @"kNSNotificationKey_ContactPubKey";
-
-@interface SessionCipher ()
-
-@property (nonatomic, readonly) NSString *recipientId;
-@property (nonatomic, readonly) int deviceId;
-
-@property (nonatomic, readonly) id sessionStore;
-@property (nonatomic, readonly) id prekeyStore;
-
-@end
-
-@implementation SessionCipher (Loki)
-
-- (NSData *)throws_lokiDecrypt:(id)whisperMessage protocolContext:(nullable id)protocolContext
-{
- // Our state before we decrypt the message
- SessionState *_Nullable state = [self getCurrentState:protocolContext];
-
- // Verify incoming friend request messages
- if (!state) {
- [self throws_validatePreKeysForFriendRequestAcceptance:whisperMessage protocolContext:protocolContext];
- }
-
- // While decrypting our state may change internally
- NSData *plainText = [self throws_decrypt:whisperMessage protocolContext:protocolContext];
-
-
- // Loki: Handle any session resets
- [self handleSessionReset:whisperMessage previousState:state protocolContext:protocolContext];
-
- return plainText;
-}
-
-/// Get the current session state
-- (SessionState *_Nullable)getCurrentState:(nullable id)protocolContext {
- SessionRecord *record = [self.sessionStore loadSession:self.recipientId deviceId:self.deviceId protocolContext:protocolContext];
- SessionState *state = record.sessionState;
-
- // Check if session is initialized
- if (!state.hasSenderChain) { return nil; }
-
- return state;
-}
-
-/// Handle any Loki session reset stuff
-- (void)handleSessionReset:(id)whisperMessage previousState:(SessionState *_Nullable)previousState protocolContext:(nullable id)protocolContext
-{
- // Don't bother doing anything if we didn't have a session before
- if (!previousState) { return; }
-
- OWSAssertDebug([protocolContext isKindOfClass:[YapDatabaseReadWriteTransaction class]]);
- YapDatabaseReadWriteTransaction *transaction = protocolContext;
-
- // Get the thread
- TSContactThread *thread = [TSContactThread getThreadWithContactId:self.recipientId transaction:transaction];
- if (!thread) { return; }
-
- // Bail early if no session reset is in progress
- if (thread.sessionResetState == TSContactThreadSessionResetStateNone) { return; }
-
- BOOL sessionResetReceived = thread.sessionResetState == TSContactThreadSessionResetStateRequestReceived;
- SessionState *_Nullable currentState = [self getCurrentState:protocolContext];
-
- // Check if our previous state and our current state differ
- if (!currentState || ![currentState.aliceBaseKey isEqualToData:previousState.aliceBaseKey]) {
-
- if (sessionResetReceived) {
- // The other user used an old session to contact us.
- // Wait for them to use a new one
- [self restoreSession:previousState protocolContext:protocolContext];
- } else {
- // Our session reset went through successfully
- // We had initiated a session reset and got a different session back from the user
- [self deleteAllSessionsExcept:currentState protocolContext:protocolContext];
- [self notifySessionAdopted];
- }
-
- } else if (sessionResetReceived) {
- // Our session reset went through successfully
- // We got a message with the same session from the other user
- [self deleteAllSessionsExcept:previousState protocolContext:protocolContext];
- [self notifySessionAdopted];
- }
-}
-
-/// Send a notification about a new session being adopted
-- (void)notifySessionAdopted
-{
- NSDictionary *userInfo = @{ kNSNotificationKey_ContactPubKey : self.recipientId };
- [NSNotificationCenter.defaultCenter postNotificationNameAsync:kNSNotificationName_SessionAdopted object:nil userInfo:userInfo];
-}
-
-/// Delete all other sessions except the given one
-- (void)deleteAllSessionsExcept:(SessionState *)state protocolContext:(nullable id)protocolContext
-{
- SessionRecord *record = [self.sessionStore loadSession:self.recipientId deviceId:self.deviceId protocolContext:protocolContext];
- [record removePreviousSessionStates];
-
- SessionState *newState = state == nil ? [SessionState new] : state;
- [record setState:newState];
-
- [self.sessionStore storeSession:self.recipientId deviceId:self.deviceId session:record protocolContext:protocolContext];
-}
-
-/// Set the given session as the active one while archiving the old one
-- (void)restoreSession:(SessionState *)state protocolContext:(nullable id)protocolContext
-{
- SessionRecord *record = [self.sessionStore loadSession:self.recipientId deviceId:self.deviceId protocolContext:protocolContext];
-
- // Remove the state from previous session states
- [record.previousSessionStates enumerateObjectsWithOptions:NSEnumerationReverse usingBlock:^(SessionState *obj, NSUInteger idx, BOOL *stop) {
- if ([state.aliceBaseKey isEqualToData:obj.aliceBaseKey]) {
- [record.previousSessionStates removeObjectAtIndex:idx];
- *stop = YES;
- }
- }];
-
- // Promote it so the previous state gets archived
- [record promoteState:state];
-
- [self.sessionStore storeSession:self.recipientId deviceId:self.deviceId session:record protocolContext: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)whisperMessage protocolContext:(nullable id)protocolContext {
- OWSAssertDebug([protocolContext isKindOfClass:[YapDatabaseReadTransaction class]]);
- YapDatabaseReadTransaction *transaction = protocolContext;
-
- // Ignore anything that isn't a `PreKeyWhisperMessage`
- if (![whisperMessage isKindOfClass:[PreKeyWhisperMessage class]]) { return; }
-
- // Check the pre key store
- if (![self.prekeyStore isKindOfClass:[OWSPrimaryStorage class]]) { return; }
-
- PreKeyWhisperMessage *preKeyMessage = whisperMessage;
- OWSPrimaryStorage *primaryStorage = self.prekeyStore;
-
- PreKeyRecord *_Nullable storedPreKey = [primaryStorage getPreKeyForContact:self.recipientId transaction:transaction];
- if (!storedPreKey) {
- OWSRaiseException(@"Loki", @"Received a friend request from a public key for which no pre key bundle was created.");
- }
-
- if (storedPreKey.Id != preKeyMessage.prekeyID) {
- OWSRaiseException(@"Loki", @"Received a PreKeyWhisperMessage (friend request accept) from an unknown source.");
- }
-}
-
-@end
+//// Loki: Refer to Docs/SessionReset.md for explanations
+//
+//#import "SessionCipher+Loki.h"
+//#import "NSNotificationCenter+OWS.h"
+//#import "PreKeyWhisperMessage.h"
+//#import "OWSPrimaryStorage+Loki.h"
+//#import "TSContactThread.h"
+//#import
+//
+//NSString *const kNSNotificationName_SessionAdopted = @"kNSNotificationName_SessionAdopted";
+//NSString *const kNSNotificationKey_ContactPubKey = @"kNSNotificationKey_ContactPubKey";
+//
+//@interface SessionCipher ()
+//
+//@property (nonatomic, readonly) NSString *recipientId;
+//@property (nonatomic, readonly) int deviceId;
+//
+//@property (nonatomic, readonly) id sessionStore;
+//@property (nonatomic, readonly) id prekeyStore;
+//
+//@end
+//
+//@implementation SessionCipher (Loki)
+//
+//- (NSData *)throws_lokiDecrypt:(id)whisperMessage protocolContext:(nullable id)protocolContext
+//{
+// // Our state before we decrypt the message
+// SessionState *_Nullable state = [self getCurrentState:protocolContext];
+//
+// // Verify incoming friend request messages
+// if (!state) {
+// [self throws_validatePreKeysForFriendRequestAcceptance:whisperMessage protocolContext:protocolContext];
+// }
+//
+// // While decrypting our state may change internally
+// NSData *plainText = [self throws_decrypt:whisperMessage protocolContext:protocolContext];
+//
+//
+// // Loki: Handle any session resets
+// [self handleSessionReset:whisperMessage previousState:state protocolContext:protocolContext];
+//
+// return plainText;
+//}
+//
+///// Get the current session state
+//- (SessionState *_Nullable)getCurrentState:(nullable id)protocolContext {
+// SessionRecord *record = [self.sessionStore loadSession:self.recipientId deviceId:self.deviceId protocolContext:protocolContext];
+// SessionState *state = record.sessionState;
+//
+// // Check if session is initialized
+// if (!state.hasSenderChain) { return nil; }
+//
+// return state;
+//}
+//
+///// Handle any Loki session reset stuff
+//- (void)handleSessionReset:(id)whisperMessage previousState:(SessionState *_Nullable)previousState protocolContext:(nullable id)protocolContext
+//{
+// // Don't bother doing anything if we didn't have a session before
+// if (!previousState) { return; }
+//
+// OWSAssertDebug([protocolContext isKindOfClass:[YapDatabaseReadWriteTransaction class]]);
+// YapDatabaseReadWriteTransaction *transaction = protocolContext;
+//
+// // Get the thread
+// TSContactThread *thread = [TSContactThread getThreadWithContactId:self.recipientId transaction:transaction];
+// if (!thread) { return; }
+//
+// // Bail early if no session reset is in progress
+// if (thread.sessionResetState == TSContactThreadSessionResetStateNone) { return; }
+//
+// BOOL sessionResetReceived = thread.sessionResetState == TSContactThreadSessionResetStateRequestReceived;
+// SessionState *_Nullable currentState = [self getCurrentState:protocolContext];
+//
+// // Check if our previous state and our current state differ
+// if (!currentState || ![currentState.aliceBaseKey isEqualToData:previousState.aliceBaseKey]) {
+//
+// if (sessionResetReceived) {
+// // The other user used an old session to contact us.
+// // Wait for them to use a new one
+// [self restoreSession:previousState protocolContext:protocolContext];
+// } else {
+// // Our session reset went through successfully
+// // We had initiated a session reset and got a different session back from the user
+// [self deleteAllSessionsExcept:currentState protocolContext:protocolContext];
+// [self notifySessionAdopted];
+// }
+//
+// } else if (sessionResetReceived) {
+// // Our session reset went through successfully
+// // We got a message with the same session from the other user
+// [self deleteAllSessionsExcept:previousState protocolContext:protocolContext];
+// [self notifySessionAdopted];
+// }
+//}
+//
+///// Send a notification about a new session being adopted
+//- (void)notifySessionAdopted
+//{
+// NSDictionary *userInfo = @{ kNSNotificationKey_ContactPubKey : self.recipientId };
+// [NSNotificationCenter.defaultCenter postNotificationNameAsync:kNSNotificationName_SessionAdopted object:nil userInfo:userInfo];
+//}
+//
+///// Delete all other sessions except the given one
+//- (void)deleteAllSessionsExcept:(SessionState *)state protocolContext:(nullable id)protocolContext
+//{
+// SessionRecord *record = [self.sessionStore loadSession:self.recipientId deviceId:self.deviceId protocolContext:protocolContext];
+// [record removePreviousSessionStates];
+//
+// SessionState *newState = state == nil ? [SessionState new] : state;
+// [record setState:newState];
+//
+// [self.sessionStore storeSession:self.recipientId deviceId:self.deviceId session:record protocolContext:protocolContext];
+//}
+//
+///// Set the given session as the active one while archiving the old one
+//- (void)restoreSession:(SessionState *)state protocolContext:(nullable id)protocolContext
+//{
+// SessionRecord *record = [self.sessionStore loadSession:self.recipientId deviceId:self.deviceId protocolContext:protocolContext];
+//
+// // Remove the state from previous session states
+// [record.previousSessionStates enumerateObjectsWithOptions:NSEnumerationReverse usingBlock:^(SessionState *obj, NSUInteger idx, BOOL *stop) {
+// if ([state.aliceBaseKey isEqualToData:obj.aliceBaseKey]) {
+// [record.previousSessionStates removeObjectAtIndex:idx];
+// *stop = YES;
+// }
+// }];
+//
+// // Promote it so the previous state gets archived
+// [record promoteState:state];
+//
+// [self.sessionStore storeSession:self.recipientId deviceId:self.deviceId session:record protocolContext: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)whisperMessage protocolContext:(nullable id)protocolContext {
+// OWSAssertDebug([protocolContext isKindOfClass:[YapDatabaseReadTransaction class]]);
+// YapDatabaseReadTransaction *transaction = protocolContext;
+//
+// // Ignore anything that isn't a `PreKeyWhisperMessage`
+// if (![whisperMessage isKindOfClass:[PreKeyWhisperMessage class]]) { return; }
+//
+// // Check the pre key store
+// if (![self.prekeyStore isKindOfClass:[OWSPrimaryStorage class]]) { return; }
+//
+// PreKeyWhisperMessage *preKeyMessage = whisperMessage;
+// OWSPrimaryStorage *primaryStorage = self.prekeyStore;
+//
+// PreKeyRecord *_Nullable storedPreKey = [primaryStorage getPreKeyForContact:self.recipientId transaction:transaction];
+// if (!storedPreKey) {
+// OWSRaiseException(@"Loki", @"Received a friend request from a public key for which no pre key bundle was created.");
+// }
+//
+// if (storedPreKey.Id != preKeyMessage.prekeyID) {
+// OWSRaiseException(@"Loki", @"Received a PreKeyWhisperMessage (friend request accept) from an unknown source.");
+// }
+//}
+//
+//@end