This commit is contained in:
Niels Andriesse 2020-11-19 15:24:09 +11:00
parent 30370bf4d2
commit 72b4c3edaf
77 changed files with 596 additions and 1132 deletions

View File

@ -1,7 +0,0 @@
extension AppDelegate : SharedSenderKeysDelegate {
public func requestSenderKey(for groupPublicKey: String, senderPublicKey: String, using transaction: Any) {
ClosedGroupsProtocol.requestSenderKey(for: groupPublicKey, senderPublicKey: senderPublicKey, using: transaction as! YapDatabaseReadWriteTransaction)
}
}

View File

@ -167,7 +167,8 @@ final class ConversationTitleView : UIView {
}
@objc func updateSubtitleForCurrentStatus() {
DispatchQueue.main.async {
DispatchQueue.main.async { [weak self] in
guard let self = self else { return }
self.subtitleLabel.isHidden = false
let subtitle = NSMutableAttributedString()
if let muteEndDate = self.thread.mutedUntilDate, self.thread.isMuted {
@ -178,15 +179,12 @@ final class ConversationTitleView : UIView {
dateFormatter.dateStyle = .medium
subtitle.append(NSAttributedString(string: "Muted until " + dateFormatter.string(from: muteEndDate)))
} else if let thread = self.thread as? TSGroupThread {
let storage = OWSPrimaryStorage.shared()
var userCount: Int?
if thread.groupModel.groupType == .closedGroup {
userCount = GroupUtilities.getClosedGroupMemberCount(thread)
} else if thread.groupModel.groupType == .openGroup {
storage.dbReadConnection.read { transaction in
if let publicChat = LokiDatabaseUtilities.getPublicChat(for: self.thread.uniqueId!, in: transaction) {
userCount = storage.getUserCount(for: publicChat, in: transaction)
}
if let openGroup = Storage.shared.getOpenGroup(for: self.thread.uniqueId!) {
userCount = Storage.shared.getUserCount(forOpenGroupWithID: openGroup.id)
}
}
if let userCount = userCount {

View File

@ -18,7 +18,7 @@ final class PathStatusView : UIView {
layer.cornerRadius = Values.pathStatusViewSize / 2
layer.masksToBounds = false
if OnionRequestAPI.paths.isEmpty {
OnionRequestAPI.paths = Storage.getOnionRequestPaths()
OnionRequestAPI.paths = Storage.shared.getOnionRequestPaths()
}
let color = (!OnionRequestAPI.paths.isEmpty) ? Colors.accent : Colors.pathsBuilding
setColor(to: color, isAnimated: false)

View File

@ -67,7 +67,7 @@ final class VoiceMessageView : UIView {
guard let url = (voiceMessage as? TSAttachmentStream)?.originalMediaURL else {
return print("[Loki] Couldn't get URL for voice message.")
}
if let cachedVolumeSamples = Storage.getVolumeSamples(for: voiceMessage.uniqueId!), cachedVolumeSamples.count == targetSampleCount {
if let cachedVolumeSamples = Storage.shared.getVolumeSamples(for: voiceMessage.uniqueId!), cachedVolumeSamples.count == targetSampleCount {
self.hideLoader()
self.volumeSamples = cachedVolumeSamples
} else {
@ -78,7 +78,7 @@ final class VoiceMessageView : UIView {
self.isForcedAnimation = true
self.volumeSamples = volumeSamples
Storage.write { transaction in
Storage.setVolumeSamples(for: voiceMessageID, to: volumeSamples, using: transaction)
Storage.shared.setVolumeSamples(for: voiceMessageID, to: volumeSamples, using: transaction)
}
}.catch(on: DispatchQueue.main) { error in
print("[Loki] Couldn't sample audio file due to error: \(error).")

View File

@ -5,9 +5,6 @@ import SessionSnodeKit
@objc(SNConfiguration)
final class Configuration : NSObject {
private static let pnServerURL = "https://live.apns.getsession.org"
private static let pnServerPublicKey = "642a6585919742e5a2d4dc51244964fbcd8bcab2b75612407de58b810740d049"
@objc static func performMainSetup() {
SNMessagingKit.configure(
storage: Storage.shared,
@ -16,11 +13,11 @@ final class Configuration : NSObject {
identityKeyStore: OWSIdentityManager.shared(),
sessionRestorationImplementation: SessionRestorationImplementation(),
certificateValidator: SMKCertificateDefaultValidator(trustRoot: OWSUDManagerImpl.trustRoot()),
openGroupAPIDelegate: UIApplication.shared.delegate as! AppDelegate,
pnServerURL: pnServerURL,
pnServerPublicKey: pnServerURL
openGroupAPIDelegate: OpenGroupAPIDelegate.shared,
pnServerURL: PushNotificationManager.server,
pnServerPublicKey: PushNotificationManager.serverPublicKey
)
SessionProtocolKit.configure(storage: Storage.shared, sharedSenderKeysDelegate: UIApplication.shared.delegate as! AppDelegate)
SessionProtocolKit.configure(storage: Storage.shared, sharedSenderKeysDelegate: MessageSenderDelegate.shared)
SessionSnodeKit.configure(storage: Storage.shared)
}
}

View File

@ -1,24 +0,0 @@
extension Storage : SessionProtocolKitStorageProtocol {
private func getClosedGroupRatchetCollection(_ collection: ClosedGroupRatchetCollectionType, for groupPublicKey: String) -> String {
switch collection {
case .old: return "LokiOldClosedGroupRatchetCollection.\(groupPublicKey)"
case .current: return "LokiClosedGroupRatchetCollection.\(groupPublicKey)"
}
}
public func getClosedGroupRatchet(for groupPublicKey: String, senderPublicKey: String, from collection: ClosedGroupRatchetCollectionType = .current) -> ClosedGroupRatchet? {
let collection = getClosedGroupRatchetCollection(collection, for: groupPublicKey)
var result: ClosedGroupRatchet?
Storage.read { transaction in
result = transaction.object(forKey: senderPublicKey, inCollection: collection) as? ClosedGroupRatchet
}
return result
}
public func setClosedGroupRatchet(for groupPublicKey: String, senderPublicKey: String, ratchet: ClosedGroupRatchet, in collection: ClosedGroupRatchetCollectionType = .current, using transaction: Any) {
let collection = getClosedGroupRatchetCollection(collection, for: groupPublicKey)
(transaction as! YapDatabaseReadWriteTransaction).setObject(ratchet, forKey: senderPublicKey, inCollection: collection)
}
}

View File

@ -1,17 +0,0 @@
extension Storage {
static let volumeSamplesCollection = "LokiVolumeSamplesCollection"
static func getVolumeSamples(for attachment: String) -> [Float]? {
var result: [Float]?
read { transaction in
result = transaction.object(forKey: attachment, inCollection: volumeSamplesCollection) as? [Float]
}
return result
}
static func setVolumeSamples(for attachment: String, to volumeSamples: [Float], using transaction: YapDatabaseReadWriteTransaction) {
transaction.setObject(volumeSamples, forKey: attachment, inCollection: volumeSamplesCollection)
}
}

View File

@ -286,10 +286,7 @@ NS_ASSUME_NONNULL_BEGIN
// Loki: Show the moderator icon if needed
if (self.viewItem.isGroupThread) { // FIXME: This logic also shouldn't apply to closed groups
__block SNOpenGroup *publicChat;
[OWSPrimaryStorage.sharedManager.dbReadConnection readWithBlock:^(YapDatabaseReadTransaction *transaction) {
publicChat = [LKDatabaseUtilities getPublicChatForThreadID:self.viewItem.interaction.uniqueThreadId transaction: transaction];
}];
SNOpenGroup *publicChat = [LKStorage.shared getOpenGroupForThreadID:self.viewItem.interaction.uniqueThreadId];
if (publicChat != nil) {
BOOL isModerator = [SNOpenGroupAPI isUserModerator:incomingMessage.authorId forChannel:publicChat.channel onServer:publicChat.server];
UIImage *moderatorIcon = [UIImage imageNamed:@"Crown"];

View File

@ -553,8 +553,8 @@ const CGFloat kRemotelySourcedContentRowSpacing = 4;
__block NSString *quotedAuthor = [SSKEnvironment.shared.profileManager profileNameForRecipientWithID:self.quotedMessage.authorId];
if (quotedAuthor == self.quotedMessage.authorId) {
SNOpenGroup *publicChat = [LKStorage.shared getOpenGroupForThreadID:self.quotedMessage.threadId];
[OWSPrimaryStorage.sharedManager.dbReadConnection readWithBlock:^(YapDatabaseReadTransaction *transaction) {
SNOpenGroup *publicChat = [LKDatabaseUtilities getPublicChatForThreadID:self.quotedMessage.threadId transaction:transaction];
if (publicChat != nil) {
quotedAuthor = [LKUserDisplayNameUtilities getPublicChatDisplayNameFor:self.quotedMessage.authorId in:publicChat.channel on:publicChat.server using:transaction];
} else {

View File

@ -1089,10 +1089,7 @@ const CGFloat kMaxTextViewHeight = 120;
- (void)showMentionCandidateSelectionViewFor:(NSArray<LKMention *> *)mentionCandidates in:(TSThread *)thread
{
__block SNOpenGroup *publicChat;
[OWSPrimaryStorage.sharedManager.dbReadConnection readWithBlock:^(YapDatabaseReadTransaction *transaction) {
publicChat = [LKDatabaseUtilities getPublicChatForThreadID:thread.uniqueId transaction:transaction];
}];
SNOpenGroup *publicChat = [LKStorage.shared getOpenGroupForThreadID:thread.uniqueId];
if (publicChat != nil) {
self.mentionCandidateSelectionView.publicChatServer = publicChat.server;
[self.mentionCandidateSelectionView setPublicChatChannel:publicChat.channel];

View File

@ -605,10 +605,7 @@ typedef enum : NSUInteger {
if (self.thread.isGroupThread) {
TSGroupThread *thread = (TSGroupThread *)self.thread;
if (!thread.isOpenGroup) { return; }
__block SNOpenGroup *publicChat;
[OWSPrimaryStorage.sharedManager.dbReadConnection readWithBlock:^(YapDatabaseReadTransaction *transaction) {
publicChat = [LKDatabaseUtilities getPublicChatForThreadID:thread.uniqueId transaction:transaction];
}];
SNOpenGroup *publicChat = [LKStorage.shared getOpenGroupForThreadID:thread.uniqueId];
[SNOpenGroupAPI getInfoForChannelWithID:publicChat.channel onServer:publicChat.server]
.thenOn(dispatch_get_main_queue(), ^(id userCount) {
[self.headerView updateSubtitleForCurrentStatus];
@ -3823,8 +3820,14 @@ typedef enum : NSUInteger {
return;
}
// Limit outgoing text messages to 16kb.
//
// We convert large text messages to attachments
// which are presented as normal text messages.
SNVisibleMessage *message = [SNVisibleMessage new];
message.text = text;
message.quote = [SNQuote from:self.inputToolbar.quotedReply];
[LKStorage writeWithBlock:^(YapDatabaseReadWriteTransaction *transaction) {
[SNMessageSender send:message inThread:self.thread usingTransaction:transaction];
}];

View File

@ -1089,10 +1089,7 @@ NSString *NSStringForOWSMessageCellType(OWSMessageCellType cellType)
TSMessage *message = (TSMessage *)self.interaction;
if (!message.isOpenGroupMessage) return;
__block SNOpenGroup *publicChat;
[self.primaryStorage.dbReadConnection readWithBlock:^(YapDatabaseReadTransaction *transaction) {
publicChat = [LKDatabaseUtilities getPublicChatForThreadID:groupThread.uniqueId transaction: transaction];
}];
SNOpenGroup *publicChat = [LKStorage.shared getOpenGroupForThreadID:groupThread.uniqueId];
if (publicChat == nil) return;
// Delete the message
@ -1162,10 +1159,7 @@ NSString *NSStringForOWSMessageCellType(OWSMessageCellType cellType)
if (!message.isOpenGroupMessage) return true;
// Ensure we have the details needed to contact the server
__block SNOpenGroup *publicChat;
[self.primaryStorage.dbReadConnection readWithBlock:^(YapDatabaseReadTransaction *transaction) {
publicChat = [LKDatabaseUtilities getPublicChatForThreadID:groupThread.uniqueId transaction: transaction];
}];
SNOpenGroup *publicChat = [LKStorage.shared getOpenGroupForThreadID:groupThread.uniqueId];
if (publicChat == nil) return true;
if (interationType == OWSInteractionType_IncomingMessage) {

View File

@ -674,10 +674,7 @@ class MediaPageViewController: UIPageViewController, UIPageViewControllerDataSou
case let incomingMessage as TSIncomingMessage:
let hexEncodedPublicKey = incomingMessage.authorId
if incomingMessage.thread.isGroupThread() {
var publicChat: OpenGroup?
OWSPrimaryStorage.shared().dbReadConnection.read { transaction in
publicChat = LokiDatabaseUtilities.getPublicChat(for: incomingMessage.thread.uniqueId!, in: transaction)
}
let publicChat = Storage.shared.getOpenGroup(for: incomingMessage.thread.uniqueId!)
if let publicChat = publicChat {
return UserDisplayNameUtilities.getPublicChatDisplayName(for: hexEncodedPublicKey, in: publicChat.channel, on: publicChat.server) ?? hexEncodedPublicKey
} else {

View File

@ -13,12 +13,9 @@ public final class BackgroundPoller : NSObject {
// promises.append(AppEnvironment.shared.messageFetcherJob.run()) // FIXME: It'd be nicer to just use Poller directly
closedGroupPoller = ClosedGroupPoller()
promises.append(contentsOf: closedGroupPoller.pollOnce())
var openGroups: [String:OpenGroup] = [:]
Storage.read { transaction in
openGroups = LokiDatabaseUtilities.getAllPublicChats(in: transaction)
}
let openGroups: [String:OpenGroup] = Storage.shared.getAllUserOpenGroups()
openGroups.values.forEach { openGroup in
let poller = PublicChatPoller(for: openGroup)
let poller = OpenGroupPoller(for: openGroup)
poller.stop()
promises.append(poller.pollForNewMessages())
}

View File

@ -52,7 +52,7 @@ final class IP2Country {
func populateCacheIfNeeded() -> Bool {
if OnionRequestAPI.paths.isEmpty {
OnionRequestAPI.paths = Storage.getOnionRequestPaths()
OnionRequestAPI.paths = Storage.shared.getOnionRequestPaths()
}
let paths = OnionRequestAPI.paths
guard !paths.isEmpty else { return false }

View File

@ -10,9 +10,8 @@ public final class MentionUtilities : NSObject {
@objc public static func highlightMentions(in string: String, isOutgoingMessage: Bool, threadID: String, attributes: [NSAttributedString.Key:Any]) -> NSAttributedString {
let userPublicKey = getUserHexEncodedPublicKey()
var publicChat: OpenGroup?
let publicChat = Storage.shared.getOpenGroup(for: threadID)
OWSPrimaryStorage.shared().dbReadConnection.read { transaction in
publicChat = LokiDatabaseUtilities.getPublicChat(for: threadID, in: transaction)
MentionsManager.populateUserPublicKeyCacheIfNeeded(for: threadID, in: transaction)
}
var string = string

View File

@ -365,10 +365,7 @@ final class HomeVC : BaseVC, UITableViewDataSource, UITableViewDelegate, UIScrol
func tableView(_ tableView: UITableView, editActionsForRowAt indexPath: IndexPath) -> [UITableViewRowAction]? {
guard let thread = self.thread(at: indexPath.row) else { return [] }
var publicChat: OpenGroup?
OWSPrimaryStorage.shared().dbReadConnection.read { transaction in
publicChat = LokiDatabaseUtilities.getPublicChat(for: thread.uniqueId!, in: transaction)
}
let publicChat = Storage.shared.getOpenGroup(for: thread.uniqueId!)
let delete = UITableViewRowAction(style: .destructive, title: NSLocalizedString("TXT_DELETE_TITLE", comment: "")) { [weak self] _, _ in
let alert = UIAlertController(title: NSLocalizedString("CONVERSATION_DELETE_CONFIRMATION_ALERT_TITLE", comment: ""), message: NSLocalizedString("CONVERSATION_DELETE_CONFIRMATION_ALERT_MESSAGE", comment: ""), preferredStyle: .alert)
alert.addAction(UIAlertAction(title: NSLocalizedString("TXT_DELETE_TITLE", comment: ""), style: .destructive) { _ in

View File

@ -10,6 +10,8 @@ public extension VisibleMessage {
public var isValid: Bool { timestamp != nil && publicKey != nil && text != nil }
public override init() { super.init() }
internal init(timestamp: UInt64, publicKey: String, text: String) {
self.timestamp = timestamp
self.publicKey = publicKey

View File

@ -4,6 +4,7 @@ import SessionUtilitiesKit
// Threads don't show up on the first message; only on the second.
// Profile pictures aren't showing up.
// Check that message expiration works.
// Open group messages (sync messages).
internal enum MessageReceiver {

View File

@ -239,6 +239,7 @@
B82B4094239DF15900A248E7 /* ConversationTitleView.swift in Sources */ = {isa = PBXBuildFile; fileRef = B82B4093239DF15900A248E7 /* ConversationTitleView.swift */; };
B83F2B88240CB75A000A54AB /* UIImage+Scaling.swift in Sources */ = {isa = PBXBuildFile; fileRef = B83F2B87240CB75A000A54AB /* UIImage+Scaling.swift */; };
B84072962565E9F50037CB17 /* TSOutgoingMessage+Conversion.swift in Sources */ = {isa = PBXBuildFile; fileRef = B84072952565E9F50037CB17 /* TSOutgoingMessage+Conversion.swift */; };
B84072A02565F1670037CB17 /* Quote+Conversion.swift in Sources */ = {isa = PBXBuildFile; fileRef = B840729F2565F1670037CB17 /* Quote+Conversion.swift */; };
B84664F5235022F30083A1CD /* MentionUtilities.swift in Sources */ = {isa = PBXBuildFile; fileRef = B84664F4235022F30083A1CD /* MentionUtilities.swift */; };
B85357BF23A1AE0800AAF6CD /* SeedReminderView.swift in Sources */ = {isa = PBXBuildFile; fileRef = B85357BE23A1AE0800AAF6CD /* SeedReminderView.swift */; };
B85357C323A1BD1200AAF6CD /* SeedVC.swift in Sources */ = {isa = PBXBuildFile; fileRef = B85357C223A1BD1200AAF6CD /* SeedVC.swift */; };
@ -261,6 +262,20 @@
B8CCF63723961D6D0091D419 /* NewPrivateChatVC.swift in Sources */ = {isa = PBXBuildFile; fileRef = B8CCF63623961D6D0091D419 /* NewPrivateChatVC.swift */; };
B8CCF63F23975CFB0091D419 /* JoinPublicChatVC.swift in Sources */ = {isa = PBXBuildFile; fileRef = B8CCF63E23975CFB0091D419 /* JoinPublicChatVC.swift */; };
B8CCF6432397711F0091D419 /* SettingsVC.swift in Sources */ = {isa = PBXBuildFile; fileRef = B8CCF6422397711F0091D419 /* SettingsVC.swift */; };
B8D8F0F32565F98E0092EF10 /* LKUserDefaults.swift in Sources */ = {isa = PBXBuildFile; fileRef = C33FDB6B255A580F00E217F9 /* LKUserDefaults.swift */; };
B8D8F0F42565F98E0092EF10 /* PushNotificationManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = C33FDBDE255A581900E217F9 /* PushNotificationManager.swift */; };
B8D8F12E2565FC910092EF10 /* Storage.swift in Sources */ = {isa = PBXBuildFile; fileRef = C33FDB36255A580B00E217F9 /* Storage.swift */; };
B8D8F1382566120F0092EF10 /* Storage+ClosedGroups.swift in Sources */ = {isa = PBXBuildFile; fileRef = B8D8F1372566120F0092EF10 /* Storage+ClosedGroups.swift */; };
B8D8F16A256615DE0092EF10 /* Storage+Shared.swift in Sources */ = {isa = PBXBuildFile; fileRef = C3F0A5FD255C988A007BE2A3 /* Storage+Shared.swift */; };
B8D8F16B256615DE0092EF10 /* Storage+VolumeSamples.swift in Sources */ = {isa = PBXBuildFile; fileRef = C31F812525258FB000DD9FD9 /* Storage+VolumeSamples.swift */; };
B8D8F16C256615DE0092EF10 /* Storage+SnodeAPI.swift in Sources */ = {isa = PBXBuildFile; fileRef = C3F0A607255C98A6007BE2A3 /* Storage+SnodeAPI.swift */; };
B8D8F17725661AFA0092EF10 /* Storage+Jobs.swift in Sources */ = {isa = PBXBuildFile; fileRef = B8D8F17625661AFA0092EF10 /* Storage+Jobs.swift */; };
B8D8F18925661BA50092EF10 /* Storage+OpenGroups.swift in Sources */ = {isa = PBXBuildFile; fileRef = B8D8F18825661BA50092EF10 /* Storage+OpenGroups.swift */; };
B8D8F19325661BF80092EF10 /* Storage+Messaging.swift in Sources */ = {isa = PBXBuildFile; fileRef = B8D8F19225661BF80092EF10 /* Storage+Messaging.swift */; };
B8D8F1BD25661C6F0092EF10 /* Storage+OnionRequests.swift in Sources */ = {isa = PBXBuildFile; fileRef = B8D8F1BC25661C6F0092EF10 /* Storage+OnionRequests.swift */; };
B8D8F1E6256620DD0092EF10 /* MessageReceiverDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = C3D697492564DEDC004AF766 /* MessageReceiverDelegate.swift */; };
B8D8F1F0256621180092EF10 /* MessageSenderDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = B8D8F1EF256621180092EF10 /* MessageSenderDelegate.swift */; };
B8D8F21A25662A4D0092EF10 /* OpenGroupAPIDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = B8D8F21925662A4D0092EF10 /* OpenGroupAPIDelegate.swift */; };
B90418E6183E9DD40038554A /* DateUtil.m in Sources */ = {isa = PBXBuildFile; fileRef = B90418E5183E9DD40038554A /* DateUtil.m */; };
B9EB5ABD1884C002007CBB57 /* MessageUI.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = B9EB5ABC1884C002007CBB57 /* MessageUI.framework */; };
C300A5B22554AF9800555489 /* VisibleMessage+Profile.swift in Sources */ = {isa = PBXBuildFile; fileRef = C300A5B12554AF9800555489 /* VisibleMessage+Profile.swift */; };
@ -278,7 +293,6 @@
C31A6C5C247F2CF3001123EF /* CGRect+Utilities.swift in Sources */ = {isa = PBXBuildFile; fileRef = C31A6C5B247F2CF3001123EF /* CGRect+Utilities.swift */; };
C31D1DE32521718E005D4DA8 /* UserSelectionVC.swift in Sources */ = {isa = PBXBuildFile; fileRef = C31D1DE22521718E005D4DA8 /* UserSelectionVC.swift */; };
C31D1DE9252172D4005D4DA8 /* ContactUtilities.swift in Sources */ = {isa = PBXBuildFile; fileRef = C31D1DE8252172D4005D4DA8 /* ContactUtilities.swift */; };
C31F812625258FB000DD9FD9 /* Storage+VolumeSamples.swift in Sources */ = {isa = PBXBuildFile; fileRef = C31F812525258FB000DD9FD9 /* Storage+VolumeSamples.swift */; };
C31FFE57254A5FFE00F19441 /* KeyPairUtilities.swift in Sources */ = {isa = PBXBuildFile; fileRef = C31FFE56254A5FFE00F19441 /* KeyPairUtilities.swift */; };
C329FEEC24F7277900B1C64C /* LightModeSheet.swift in Sources */ = {isa = PBXBuildFile; fileRef = C329FEEB24F7277900B1C64C /* LightModeSheet.swift */; };
C33100082558FF6D00070591 /* NewConversationButtonSet.swift in Sources */ = {isa = PBXBuildFile; fileRef = B83F2B85240C7B8F000A54AB /* NewConversationButtonSet.swift */; };
@ -329,17 +343,15 @@
C33FDC33255A581F00E217F9 /* TSGroupThread.h in Headers */ = {isa = PBXBuildFile; fileRef = C33FDA79255A57FB00E217F9 /* TSGroupThread.h */; settings = {ATTRIBUTES = (Public, ); }; };
C33FDC34255A581F00E217F9 /* NSRegularExpression+SSK.swift in Sources */ = {isa = PBXBuildFile; fileRef = C33FDA7A255A57FB00E217F9 /* NSRegularExpression+SSK.swift */; };
C33FDC35255A581F00E217F9 /* TSInvalidIdentityKeyReceivingErrorMessage.h in Headers */ = {isa = PBXBuildFile; fileRef = C33FDA7B255A57FB00E217F9 /* TSInvalidIdentityKeyReceivingErrorMessage.h */; settings = {ATTRIBUTES = (Public, ); }; };
C33FDC36255A581F00E217F9 /* Debugging.swift in Sources */ = {isa = PBXBuildFile; fileRef = C33FDA7C255A57FB00E217F9 /* Debugging.swift */; };
C33FDC38255A581F00E217F9 /* Mention.swift in Sources */ = {isa = PBXBuildFile; fileRef = C33FDA7E255A57FB00E217F9 /* Mention.swift */; };
C33FDC39255A581F00E217F9 /* OWSRecordTranscriptJob.m in Sources */ = {isa = PBXBuildFile; fileRef = C33FDA7F255A57FC00E217F9 /* OWSRecordTranscriptJob.m */; };
C33FDC3A255A581F00E217F9 /* OWSDisappearingMessagesJob.h in Headers */ = {isa = PBXBuildFile; fileRef = C33FDA80255A57FC00E217F9 /* OWSDisappearingMessagesJob.h */; settings = {ATTRIBUTES = (Public, ); }; };
C33FDC3B255A581F00E217F9 /* MentionsManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = C33FDA81255A57FC00E217F9 /* MentionsManager.swift */; };
C33FDC3F255A581F00E217F9 /* OWSPrimaryStorage+Loki.swift in Sources */ = {isa = PBXBuildFile; fileRef = C33FDA85255A57FC00E217F9 /* OWSPrimaryStorage+Loki.swift */; };
C33FDC40255A581F00E217F9 /* OWSDisappearingMessagesFinder.m in Sources */ = {isa = PBXBuildFile; fileRef = C33FDA86255A57FC00E217F9 /* OWSDisappearingMessagesFinder.m */; };
C33FDC41255A581F00E217F9 /* TypingIndicators.swift in Sources */ = {isa = PBXBuildFile; fileRef = C33FDA87255A57FC00E217F9 /* TypingIndicators.swift */; };
C33FDC42255A581F00E217F9 /* YapDatabaseTransaction+OWS.h in Headers */ = {isa = PBXBuildFile; fileRef = C33FDA88255A57FD00E217F9 /* YapDatabaseTransaction+OWS.h */; settings = {ATTRIBUTES = (Public, ); }; };
C33FDC45255A581F00E217F9 /* AppVersion.m in Sources */ = {isa = PBXBuildFile; fileRef = C33FDA8B255A57FD00E217F9 /* AppVersion.m */; };
C33FDC46255A581F00E217F9 /* PublicChatPoller.swift in Sources */ = {isa = PBXBuildFile; fileRef = C33FDA8C255A57FD00E217F9 /* PublicChatPoller.swift */; };
C33FDC46255A581F00E217F9 /* OpenGroupPoller.swift in Sources */ = {isa = PBXBuildFile; fileRef = C33FDA8C255A57FD00E217F9 /* OpenGroupPoller.swift */; };
C33FDC48255A581F00E217F9 /* OWSFileSystem.m in Sources */ = {isa = PBXBuildFile; fileRef = C33FDA8E255A57FD00E217F9 /* OWSFileSystem.m */; };
C33FDC4A255A582000E217F9 /* TSYapDatabaseObject.m in Sources */ = {isa = PBXBuildFile; fileRef = C33FDA90255A57FD00E217F9 /* TSYapDatabaseObject.m */; };
C33FDC4F255A582000E217F9 /* OWSChunkedOutputStream.h in Headers */ = {isa = PBXBuildFile; fileRef = C33FDA95255A57FE00E217F9 /* OWSChunkedOutputStream.h */; settings = {ATTRIBUTES = (Public, ); }; };
@ -424,8 +436,6 @@
C33FDCEB255A582000E217F9 /* SSKEnvironment.h in Headers */ = {isa = PBXBuildFile; fileRef = C33FDB31255A580A00E217F9 /* SSKEnvironment.h */; settings = {ATTRIBUTES = (Public, ); }; };
C33FDCEC255A582000E217F9 /* SSKIncrementingIdFinder.swift in Sources */ = {isa = PBXBuildFile; fileRef = C33FDB32255A580A00E217F9 /* SSKIncrementingIdFinder.swift */; };
C33FDCEE255A582000E217F9 /* ClosedGroupPoller.swift in Sources */ = {isa = PBXBuildFile; fileRef = C33FDB34255A580B00E217F9 /* ClosedGroupPoller.swift */; };
C33FDCF0255A582000E217F9 /* Storage.swift in Sources */ = {isa = PBXBuildFile; fileRef = C33FDB36255A580B00E217F9 /* Storage.swift */; };
C33FDCF1255A582000E217F9 /* Storage+SnodeAPI.swift in Sources */ = {isa = PBXBuildFile; fileRef = C33FDB37255A580B00E217F9 /* Storage+SnodeAPI.swift */; };
C33FDCF2255A582000E217F9 /* OWSBackgroundTask.h in Headers */ = {isa = PBXBuildFile; fileRef = C33FDB38255A580B00E217F9 /* OWSBackgroundTask.h */; settings = {ATTRIBUTES = (Public, ); }; };
C33FDCF4255A582000E217F9 /* Poller.swift in Sources */ = {isa = PBXBuildFile; fileRef = C33FDB3A255A580B00E217F9 /* Poller.swift */; };
C33FDCF5255A582000E217F9 /* NSNotificationCenter+OWS.h in Headers */ = {isa = PBXBuildFile; fileRef = C33FDB3B255A580B00E217F9 /* NSNotificationCenter+OWS.h */; settings = {ATTRIBUTES = (Public, ); }; };
@ -461,7 +471,6 @@
C33FDD21255A582000E217F9 /* OWSMediaGalleryFinder.h in Headers */ = {isa = PBXBuildFile; fileRef = C33FDB67255A580F00E217F9 /* OWSMediaGalleryFinder.h */; settings = {ATTRIBUTES = (Public, ); }; };
C33FDD22255A582000E217F9 /* ContentProxy.swift in Sources */ = {isa = PBXBuildFile; fileRef = C33FDB68255A580F00E217F9 /* ContentProxy.swift */; };
C33FDD23255A582000E217F9 /* FeatureFlags.swift in Sources */ = {isa = PBXBuildFile; fileRef = C33FDB69255A580F00E217F9 /* FeatureFlags.swift */; };
C33FDD25255A582000E217F9 /* LKUserDefaults.swift in Sources */ = {isa = PBXBuildFile; fileRef = C33FDB6B255A580F00E217F9 /* LKUserDefaults.swift */; };
C33FDD26255A582000E217F9 /* NSNotificationCenter+OWS.m in Sources */ = {isa = PBXBuildFile; fileRef = C33FDB6C255A580F00E217F9 /* NSNotificationCenter+OWS.m */; };
C33FDD27255A582000E217F9 /* TSPreKeyManager.h in Headers */ = {isa = PBXBuildFile; fileRef = C33FDB6D255A580F00E217F9 /* TSPreKeyManager.h */; settings = {ATTRIBUTES = (Public, ); }; };
C33FDD29255A582000E217F9 /* OWSOutgoingReceiptManager.m in Sources */ = {isa = PBXBuildFile; fileRef = C33FDB6F255A580F00E217F9 /* OWSOutgoingReceiptManager.m */; };
@ -487,7 +496,6 @@
C33FDD4B255A582000E217F9 /* ProtoUtils.h in Headers */ = {isa = PBXBuildFile; fileRef = C33FDB91255A581200E217F9 /* ProtoUtils.h */; settings = {ATTRIBUTES = (Public, ); }; };
C33FDD4D255A582000E217F9 /* PreKeyBundle+jsonDict.m in Sources */ = {isa = PBXBuildFile; fileRef = C33FDB93255A581200E217F9 /* PreKeyBundle+jsonDict.m */; };
C33FDD4E255A582000E217F9 /* TSAccountManager.h in Headers */ = {isa = PBXBuildFile; fileRef = C33FDB94255A581300E217F9 /* TSAccountManager.h */; settings = {ATTRIBUTES = (Public, ); }; };
C33FDD4F255A582000E217F9 /* Storage+Collections.swift in Sources */ = {isa = PBXBuildFile; fileRef = C33FDB95255A581300E217F9 /* Storage+Collections.swift */; };
C33FDD53255A582000E217F9 /* OWSPrimaryStorage+keyFromIntLong.m in Sources */ = {isa = PBXBuildFile; fileRef = C33FDB99255A581300E217F9 /* OWSPrimaryStorage+keyFromIntLong.m */; };
C33FDD56255A582000E217F9 /* TSIncomingMessage.h in Headers */ = {isa = PBXBuildFile; fileRef = C33FDB9C255A581300E217F9 /* TSIncomingMessage.h */; settings = {ATTRIBUTES = (Public, ); }; };
C33FDD58255A582000E217F9 /* TSAttachmentPointer.m in Sources */ = {isa = PBXBuildFile; fileRef = C33FDB9E255A581400E217F9 /* TSAttachmentPointer.m */; };
@ -521,9 +529,7 @@
C33FDD91255A582000E217F9 /* OWSMessageUtils.m in Sources */ = {isa = PBXBuildFile; fileRef = C33FDBD7255A581900E217F9 /* OWSMessageUtils.m */; };
C33FDD92255A582000E217F9 /* SignalIOS.pb.swift in Sources */ = {isa = PBXBuildFile; fileRef = C33FDBD8255A581900E217F9 /* SignalIOS.pb.swift */; };
C33FDD97255A582000E217F9 /* OWSDisappearingMessagesJob.m in Sources */ = {isa = PBXBuildFile; fileRef = C33FDBDD255A581900E217F9 /* OWSDisappearingMessagesJob.m */; };
C33FDD98255A582000E217F9 /* LokiPushNotificationManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = C33FDBDE255A581900E217F9 /* LokiPushNotificationManager.swift */; };
C33FDD9B255A582000E217F9 /* LKGroupUtilities.m in Sources */ = {isa = PBXBuildFile; fileRef = C33FDBE1255A581A00E217F9 /* LKGroupUtilities.m */; };
C33FDDA2255A582000E217F9 /* Storage+OnionRequests.swift in Sources */ = {isa = PBXBuildFile; fileRef = C33FDBE8255A581A00E217F9 /* Storage+OnionRequests.swift */; };
C33FDDA3255A582000E217F9 /* TSInteraction.m in Sources */ = {isa = PBXBuildFile; fileRef = C33FDBE9255A581A00E217F9 /* TSInteraction.m */; };
C33FDDA5255A582000E217F9 /* OWSBlockingManager.h in Headers */ = {isa = PBXBuildFile; fileRef = C33FDBEB255A581B00E217F9 /* OWSBlockingManager.h */; settings = {ATTRIBUTES = (Public, ); }; };
C33FDDA6255A582000E217F9 /* OWSRecipientIdentity.m in Sources */ = {isa = PBXBuildFile; fileRef = C33FDBEC255A581B00E217F9 /* OWSRecipientIdentity.m */; };
@ -533,14 +539,12 @@
C33FDDB0255A582000E217F9 /* NSURLSessionDataTask+StatusCode.h in Headers */ = {isa = PBXBuildFile; fileRef = C33FDBF6255A581C00E217F9 /* NSURLSessionDataTask+StatusCode.h */; settings = {ATTRIBUTES = (Public, ); }; };
C33FDDB2255A582000E217F9 /* NSArray+OWS.h in Headers */ = {isa = PBXBuildFile; fileRef = C33FDBF8255A581C00E217F9 /* NSArray+OWS.h */; settings = {ATTRIBUTES = (Public, ); }; };
C33FDDB3255A582000E217F9 /* OWSError.h in Headers */ = {isa = PBXBuildFile; fileRef = C33FDBF9255A581C00E217F9 /* OWSError.h */; settings = {ATTRIBUTES = (Public, ); }; };
C33FDDB5255A582000E217F9 /* Storage+PublicChats.swift in Sources */ = {isa = PBXBuildFile; fileRef = C33FDBFB255A581C00E217F9 /* Storage+PublicChats.swift */; };
C33FDDB8255A582000E217F9 /* NSSet+Functional.h in Headers */ = {isa = PBXBuildFile; fileRef = C33FDBFE255A581C00E217F9 /* NSSet+Functional.h */; settings = {ATTRIBUTES = (Public, ); }; };
C33FDDBB255A582000E217F9 /* TSGroupThread.m in Sources */ = {isa = PBXBuildFile; fileRef = C33FDC01255A581C00E217F9 /* TSGroupThread.m */; };
C33FDDBC255A582000E217F9 /* OWSPrimaryStorage.m in Sources */ = {isa = PBXBuildFile; fileRef = C33FDC02255A581D00E217F9 /* OWSPrimaryStorage.m */; };
C33FDDBD255A582000E217F9 /* ByteParser.h in Headers */ = {isa = PBXBuildFile; fileRef = C33FDC03255A581D00E217F9 /* ByteParser.h */; settings = {ATTRIBUTES = (Public, ); }; };
C33FDDBF255A582000E217F9 /* OWSDisappearingMessagesFinder.h in Headers */ = {isa = PBXBuildFile; fileRef = C33FDC05255A581D00E217F9 /* OWSDisappearingMessagesFinder.h */; settings = {ATTRIBUTES = (Public, ); }; };
C33FDDC0255A582000E217F9 /* SignalAccount.m in Sources */ = {isa = PBXBuildFile; fileRef = C33FDC06255A581D00E217F9 /* SignalAccount.m */; };
C33FDDC1255A582000E217F9 /* Storage+ClosedGroups.swift in Sources */ = {isa = PBXBuildFile; fileRef = C33FDC07255A581D00E217F9 /* Storage+ClosedGroups.swift */; };
C33FDDC5255A582000E217F9 /* OWSError.m in Sources */ = {isa = PBXBuildFile; fileRef = C33FDC0B255A581D00E217F9 /* OWSError.m */; };
C33FDDC6255A582000E217F9 /* TSInfoMessage.m in Sources */ = {isa = PBXBuildFile; fileRef = C33FDC0C255A581E00E217F9 /* TSInfoMessage.m */; };
C33FDDCC255A582000E217F9 /* TSConstants.h in Headers */ = {isa = PBXBuildFile; fileRef = C33FDC12255A581E00E217F9 /* TSConstants.h */; settings = {ATTRIBUTES = (Public, ); }; };
@ -576,8 +580,6 @@
C3548F0624456447009433A8 /* PNModeVC.swift in Sources */ = {isa = PBXBuildFile; fileRef = C3548F0524456447009433A8 /* PNModeVC.swift */; };
C3548F0824456AB6009433A8 /* UIView+Wrapping.swift in Sources */ = {isa = PBXBuildFile; fileRef = C3548F0724456AB6009433A8 /* UIView+Wrapping.swift */; };
C354E75A23FE2A7600CE22E3 /* BaseVC.swift in Sources */ = {isa = PBXBuildFile; fileRef = C354E75923FE2A7600CE22E3 /* BaseVC.swift */; };
C3550A03255DD6D900194B6A /* AppDelegate+OpenGroupAPI.swift in Sources */ = {isa = PBXBuildFile; fileRef = C3550A02255DD6D900194B6A /* AppDelegate+OpenGroupAPI.swift */; };
C3550A1D255DD73500194B6A /* Storage+SessionMessagingKit.swift in Sources */ = {isa = PBXBuildFile; fileRef = C3550A1C255DD73500194B6A /* Storage+SessionMessagingKit.swift */; };
C35E8AA82485C85800ACB629 /* GeoLite2-Country-Locations-English.csv in Resources */ = {isa = PBXBuildFile; fileRef = C35E8AA52485C85400ACB629 /* GeoLite2-Country-Locations-English.csv */; };
C35E8AA92485C85800ACB629 /* GeoLite2-Country-Blocks-IPv4.csv in Resources */ = {isa = PBXBuildFile; fileRef = C35E8AA62485C85600ACB629 /* GeoLite2-Country-Blocks-IPv4.csv */; };
C35E8AAE2485E51D00ACB629 /* IP2Country.swift in Sources */ = {isa = PBXBuildFile; fileRef = C35E8AAD2485E51D00ACB629 /* IP2Country.swift */; };
@ -907,18 +909,12 @@
C3CA3B2F255CF84E00F4C6D4 /* NullMessage.swift in Sources */ = {isa = PBXBuildFile; fileRef = C3CA3B2E255CF84E00F4C6D4 /* NullMessage.swift */; };
C3D0972B2510499C00F6E3E4 /* BackgroundPoller.swift in Sources */ = {isa = PBXBuildFile; fileRef = C3D0972A2510499C00F6E3E4 /* BackgroundPoller.swift */; };
C3D697382564DCE6004AF766 /* MessageHandler.swift in Sources */ = {isa = PBXBuildFile; fileRef = C3D697372564DCE6004AF766 /* MessageHandler.swift */; };
C3D6974A2564DEDC004AF766 /* MessageReceiverDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = C3D697492564DEDC004AF766 /* MessageReceiverDelegate.swift */; };
C3DAB3242480CB2B00725F25 /* SRCopyableLabel.swift in Sources */ = {isa = PBXBuildFile; fileRef = C3DAB3232480CB2A00725F25 /* SRCopyableLabel.swift */; };
C3DFFAC623E96F0D0058DAF8 /* Sheet.swift in Sources */ = {isa = PBXBuildFile; fileRef = C3DFFAC523E96F0D0058DAF8 /* Sheet.swift */; };
C3E5C2FA251DBABB0040DFFC /* EditClosedGroupVC.swift in Sources */ = {isa = PBXBuildFile; fileRef = C3E5C2F9251DBABB0040DFFC /* EditClosedGroupVC.swift */; };
C3E7134F251C867C009649BB /* Sodium+Conversion.swift in Sources */ = {isa = PBXBuildFile; fileRef = C3E7134E251C867C009649BB /* Sodium+Conversion.swift */; };
C3EEA017256487B300C338BC /* LokiDatabaseUtilities.swift in Sources */ = {isa = PBXBuildFile; fileRef = C3EEA016256487B300C338BC /* LokiDatabaseUtilities.swift */; };
C3F0A530255C80BC007BE2A3 /* NoopNotificationsManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = C3F0A52F255C80BC007BE2A3 /* NoopNotificationsManager.swift */; };
C3F0A5EC255C970D007BE2A3 /* Configuration.swift in Sources */ = {isa = PBXBuildFile; fileRef = C3F0A5EB255C970D007BE2A3 /* Configuration.swift */; };
C3F0A5FE255C988A007BE2A3 /* Storage+Shared.swift in Sources */ = {isa = PBXBuildFile; fileRef = C3F0A5FD255C988A007BE2A3 /* Storage+Shared.swift */; };
C3F0A608255C98A6007BE2A3 /* Storage+SessionSnodeKit.swift in Sources */ = {isa = PBXBuildFile; fileRef = C3F0A607255C98A6007BE2A3 /* Storage+SessionSnodeKit.swift */; };
C3F0A61A255C9902007BE2A3 /* Storage+SessionProtocolKit.swift in Sources */ = {isa = PBXBuildFile; fileRef = C3F0A619255C9902007BE2A3 /* Storage+SessionProtocolKit.swift */; };
C3F0A62C255C9937007BE2A3 /* AppDelegate+SharedSenderKeys.swift in Sources */ = {isa = PBXBuildFile; fileRef = C3F0A62B255C9937007BE2A3 /* AppDelegate+SharedSenderKeys.swift */; };
D2179CFC16BB0B3A0006F3AB /* CoreTelephony.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = D2179CFB16BB0B3A0006F3AB /* CoreTelephony.framework */; };
D2179CFE16BB0B480006F3AB /* SystemConfiguration.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = D2179CFD16BB0B480006F3AB /* SystemConfiguration.framework */; };
D221A08E169C9E5E00537ABF /* UIKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = D221A08D169C9E5E00537ABF /* UIKit.framework */; };
@ -1346,6 +1342,7 @@
B83F2B85240C7B8F000A54AB /* NewConversationButtonSet.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NewConversationButtonSet.swift; sourceTree = "<group>"; };
B83F2B87240CB75A000A54AB /* UIImage+Scaling.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UIImage+Scaling.swift"; sourceTree = "<group>"; };
B84072952565E9F50037CB17 /* TSOutgoingMessage+Conversion.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "TSOutgoingMessage+Conversion.swift"; sourceTree = "<group>"; };
B840729F2565F1670037CB17 /* Quote+Conversion.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Quote+Conversion.swift"; sourceTree = "<group>"; };
B84664F4235022F30083A1CD /* MentionUtilities.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MentionUtilities.swift; sourceTree = "<group>"; };
B847570023D568EB00759540 /* SignalServiceKit.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; path = SignalServiceKit.framework; sourceTree = BUILT_PRODUCTS_DIR; };
B85357BE23A1AE0800AAF6CD /* SeedReminderView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SeedReminderView.swift; sourceTree = "<group>"; };
@ -1383,6 +1380,13 @@
B8CCF638239721E20091D419 /* TabBar.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TabBar.swift; sourceTree = "<group>"; };
B8CCF63E23975CFB0091D419 /* JoinPublicChatVC.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = JoinPublicChatVC.swift; sourceTree = "<group>"; };
B8CCF6422397711F0091D419 /* SettingsVC.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SettingsVC.swift; sourceTree = "<group>"; };
B8D8F1372566120F0092EF10 /* Storage+ClosedGroups.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Storage+ClosedGroups.swift"; sourceTree = "<group>"; };
B8D8F17625661AFA0092EF10 /* Storage+Jobs.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Storage+Jobs.swift"; sourceTree = "<group>"; };
B8D8F18825661BA50092EF10 /* Storage+OpenGroups.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Storage+OpenGroups.swift"; sourceTree = "<group>"; };
B8D8F19225661BF80092EF10 /* Storage+Messaging.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Storage+Messaging.swift"; sourceTree = "<group>"; };
B8D8F1BC25661C6F0092EF10 /* Storage+OnionRequests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Storage+OnionRequests.swift"; sourceTree = "<group>"; };
B8D8F1EF256621180092EF10 /* MessageSenderDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MessageSenderDelegate.swift; sourceTree = "<group>"; };
B8D8F21925662A4D0092EF10 /* OpenGroupAPIDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OpenGroupAPIDelegate.swift; sourceTree = "<group>"; };
B90418E4183E9DD40038554A /* DateUtil.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = DateUtil.h; sourceTree = "<group>"; };
B90418E5183E9DD40038554A /* DateUtil.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = DateUtil.m; sourceTree = "<group>"; };
B9EB5ABC1884C002007CBB57 /* MessageUI.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = MessageUI.framework; path = System/Library/Frameworks/MessageUI.framework; sourceTree = SDKROOT; };
@ -1431,17 +1435,15 @@
C33FDA79255A57FB00E217F9 /* TSGroupThread.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = TSGroupThread.h; sourceTree = "<group>"; };
C33FDA7A255A57FB00E217F9 /* NSRegularExpression+SSK.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "NSRegularExpression+SSK.swift"; sourceTree = "<group>"; };
C33FDA7B255A57FB00E217F9 /* TSInvalidIdentityKeyReceivingErrorMessage.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = TSInvalidIdentityKeyReceivingErrorMessage.h; sourceTree = "<group>"; };
C33FDA7C255A57FB00E217F9 /* Debugging.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Debugging.swift; sourceTree = "<group>"; };
C33FDA7E255A57FB00E217F9 /* Mention.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Mention.swift; sourceTree = "<group>"; };
C33FDA7F255A57FC00E217F9 /* OWSRecordTranscriptJob.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = OWSRecordTranscriptJob.m; sourceTree = "<group>"; };
C33FDA80255A57FC00E217F9 /* OWSDisappearingMessagesJob.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = OWSDisappearingMessagesJob.h; sourceTree = "<group>"; };
C33FDA81255A57FC00E217F9 /* MentionsManager.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MentionsManager.swift; sourceTree = "<group>"; };
C33FDA85255A57FC00E217F9 /* OWSPrimaryStorage+Loki.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "OWSPrimaryStorage+Loki.swift"; sourceTree = "<group>"; };
C33FDA86255A57FC00E217F9 /* OWSDisappearingMessagesFinder.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = OWSDisappearingMessagesFinder.m; sourceTree = "<group>"; };
C33FDA87255A57FC00E217F9 /* TypingIndicators.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TypingIndicators.swift; sourceTree = "<group>"; };
C33FDA88255A57FD00E217F9 /* YapDatabaseTransaction+OWS.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "YapDatabaseTransaction+OWS.h"; sourceTree = "<group>"; };
C33FDA8B255A57FD00E217F9 /* AppVersion.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = AppVersion.m; sourceTree = "<group>"; };
C33FDA8C255A57FD00E217F9 /* PublicChatPoller.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PublicChatPoller.swift; sourceTree = "<group>"; };
C33FDA8C255A57FD00E217F9 /* OpenGroupPoller.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = OpenGroupPoller.swift; sourceTree = "<group>"; };
C33FDA8E255A57FD00E217F9 /* OWSFileSystem.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = OWSFileSystem.m; sourceTree = "<group>"; };
C33FDA90255A57FD00E217F9 /* TSYapDatabaseObject.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = TSYapDatabaseObject.m; sourceTree = "<group>"; };
C33FDA95255A57FE00E217F9 /* OWSChunkedOutputStream.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = OWSChunkedOutputStream.h; sourceTree = "<group>"; };
@ -1527,7 +1529,6 @@
C33FDB32255A580A00E217F9 /* SSKIncrementingIdFinder.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SSKIncrementingIdFinder.swift; sourceTree = "<group>"; };
C33FDB34255A580B00E217F9 /* ClosedGroupPoller.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ClosedGroupPoller.swift; sourceTree = "<group>"; };
C33FDB36255A580B00E217F9 /* Storage.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Storage.swift; sourceTree = "<group>"; };
C33FDB37255A580B00E217F9 /* Storage+SnodeAPI.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "Storage+SnodeAPI.swift"; sourceTree = "<group>"; };
C33FDB38255A580B00E217F9 /* OWSBackgroundTask.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = OWSBackgroundTask.h; sourceTree = "<group>"; };
C33FDB3A255A580B00E217F9 /* Poller.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Poller.swift; sourceTree = "<group>"; };
C33FDB3B255A580B00E217F9 /* NSNotificationCenter+OWS.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "NSNotificationCenter+OWS.h"; sourceTree = "<group>"; };
@ -1589,7 +1590,6 @@
C33FDB91255A581200E217F9 /* ProtoUtils.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ProtoUtils.h; sourceTree = "<group>"; };
C33FDB93255A581200E217F9 /* PreKeyBundle+jsonDict.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "PreKeyBundle+jsonDict.m"; sourceTree = "<group>"; };
C33FDB94255A581300E217F9 /* TSAccountManager.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = TSAccountManager.h; sourceTree = "<group>"; };
C33FDB95255A581300E217F9 /* Storage+Collections.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "Storage+Collections.swift"; sourceTree = "<group>"; };
C33FDB99255A581300E217F9 /* OWSPrimaryStorage+keyFromIntLong.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "OWSPrimaryStorage+keyFromIntLong.m"; sourceTree = "<group>"; };
C33FDB9C255A581300E217F9 /* TSIncomingMessage.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = TSIncomingMessage.h; sourceTree = "<group>"; };
C33FDB9E255A581400E217F9 /* TSAttachmentPointer.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = TSAttachmentPointer.m; sourceTree = "<group>"; };
@ -1623,9 +1623,8 @@
C33FDBD7255A581900E217F9 /* OWSMessageUtils.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = OWSMessageUtils.m; sourceTree = "<group>"; };
C33FDBD8255A581900E217F9 /* SignalIOS.pb.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SignalIOS.pb.swift; sourceTree = "<group>"; };
C33FDBDD255A581900E217F9 /* OWSDisappearingMessagesJob.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = OWSDisappearingMessagesJob.m; sourceTree = "<group>"; };
C33FDBDE255A581900E217F9 /* LokiPushNotificationManager.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = LokiPushNotificationManager.swift; sourceTree = "<group>"; };
C33FDBDE255A581900E217F9 /* PushNotificationManager.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PushNotificationManager.swift; sourceTree = "<group>"; };
C33FDBE1255A581A00E217F9 /* LKGroupUtilities.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = LKGroupUtilities.m; sourceTree = "<group>"; };
C33FDBE8255A581A00E217F9 /* Storage+OnionRequests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "Storage+OnionRequests.swift"; sourceTree = "<group>"; };
C33FDBE9255A581A00E217F9 /* TSInteraction.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = TSInteraction.m; sourceTree = "<group>"; };
C33FDBEB255A581B00E217F9 /* OWSBlockingManager.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = OWSBlockingManager.h; sourceTree = "<group>"; };
C33FDBEC255A581B00E217F9 /* OWSRecipientIdentity.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = OWSRecipientIdentity.m; sourceTree = "<group>"; };
@ -1635,14 +1634,12 @@
C33FDBF6255A581C00E217F9 /* NSURLSessionDataTask+StatusCode.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "NSURLSessionDataTask+StatusCode.h"; sourceTree = "<group>"; };
C33FDBF8255A581C00E217F9 /* NSArray+OWS.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "NSArray+OWS.h"; sourceTree = "<group>"; };
C33FDBF9255A581C00E217F9 /* OWSError.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = OWSError.h; sourceTree = "<group>"; };
C33FDBFB255A581C00E217F9 /* Storage+PublicChats.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "Storage+PublicChats.swift"; sourceTree = "<group>"; };
C33FDBFE255A581C00E217F9 /* NSSet+Functional.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "NSSet+Functional.h"; sourceTree = "<group>"; };
C33FDC01255A581C00E217F9 /* TSGroupThread.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = TSGroupThread.m; sourceTree = "<group>"; };
C33FDC02255A581D00E217F9 /* OWSPrimaryStorage.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = OWSPrimaryStorage.m; sourceTree = "<group>"; };
C33FDC03255A581D00E217F9 /* ByteParser.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ByteParser.h; sourceTree = "<group>"; };
C33FDC05255A581D00E217F9 /* OWSDisappearingMessagesFinder.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = OWSDisappearingMessagesFinder.h; sourceTree = "<group>"; };
C33FDC06255A581D00E217F9 /* SignalAccount.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SignalAccount.m; sourceTree = "<group>"; };
C33FDC07255A581D00E217F9 /* Storage+ClosedGroups.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "Storage+ClosedGroups.swift"; sourceTree = "<group>"; };
C33FDC0B255A581D00E217F9 /* OWSError.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = OWSError.m; sourceTree = "<group>"; };
C33FDC0C255A581E00E217F9 /* TSInfoMessage.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = TSInfoMessage.m; sourceTree = "<group>"; };
C33FDC12255A581E00E217F9 /* TSConstants.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = TSConstants.h; sourceTree = "<group>"; };
@ -1676,8 +1673,6 @@
C3548F0524456447009433A8 /* PNModeVC.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PNModeVC.swift; sourceTree = "<group>"; };
C3548F0724456AB6009433A8 /* UIView+Wrapping.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UIView+Wrapping.swift"; sourceTree = "<group>"; };
C354E75923FE2A7600CE22E3 /* BaseVC.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BaseVC.swift; sourceTree = "<group>"; };
C3550A02255DD6D900194B6A /* AppDelegate+OpenGroupAPI.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "AppDelegate+OpenGroupAPI.swift"; sourceTree = "<group>"; };
C3550A1C255DD73500194B6A /* Storage+SessionMessagingKit.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Storage+SessionMessagingKit.swift"; sourceTree = "<group>"; };
C35E8AA22485C72300ACB629 /* SwiftCSV.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = SwiftCSV.framework; path = ThirdParty/Carthage/Build/iOS/SwiftCSV.framework; sourceTree = "<group>"; };
C35E8AA52485C85400ACB629 /* GeoLite2-Country-Locations-English.csv */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = "GeoLite2-Country-Locations-English.csv"; sourceTree = "<group>"; };
C35E8AA62485C85600ACB629 /* GeoLite2-Country-Blocks-IPv4.csv */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = "GeoLite2-Country-Blocks-IPv4.csv"; sourceTree = "<group>"; };
@ -1729,10 +1724,10 @@
C38EF2B2255B6D9C007E1867 /* UIView+Utilities.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = "UIView+Utilities.swift"; path = "SignalUtilitiesKit/UI/UIView+Utilities.swift"; sourceTree = SOURCE_ROOT; };
C38EF2BE255B6DA6007E1867 /* TSUnreadIndicatorInteraction.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = TSUnreadIndicatorInteraction.h; path = SignalUtilitiesKit/Messaging/TSUnreadIndicatorInteraction.h; sourceTree = SOURCE_ROOT; };
C38EF2C1255B6DA6007E1867 /* TSUnreadIndicatorInteraction.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = TSUnreadIndicatorInteraction.m; path = SignalUtilitiesKit/Messaging/TSUnreadIndicatorInteraction.m; sourceTree = SOURCE_ROOT; };
C38EF2CF255B6DAE007E1867 /* OWSProfileManager.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = OWSProfileManager.m; path = SignalUtilitiesKit/Remove/OWSProfileManager.m; sourceTree = SOURCE_ROOT; };
C38EF2D1255B6DAF007E1867 /* OWSUserProfile.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = OWSUserProfile.m; path = SignalUtilitiesKit/Remove/OWSUserProfile.m; sourceTree = SOURCE_ROOT; };
C38EF2D2255B6DAF007E1867 /* OWSProfileManager.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = OWSProfileManager.h; path = SignalUtilitiesKit/Remove/OWSProfileManager.h; sourceTree = SOURCE_ROOT; };
C38EF2D3255B6DAF007E1867 /* OWSUserProfile.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = OWSUserProfile.h; path = SignalUtilitiesKit/Remove/OWSUserProfile.h; sourceTree = SOURCE_ROOT; };
C38EF2CF255B6DAE007E1867 /* OWSProfileManager.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = OWSProfileManager.m; path = "SignalUtilitiesKit/To Do/OWSProfileManager.m"; sourceTree = SOURCE_ROOT; };
C38EF2D1255B6DAF007E1867 /* OWSUserProfile.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = OWSUserProfile.m; path = "SignalUtilitiesKit/To Do/OWSUserProfile.m"; sourceTree = SOURCE_ROOT; };
C38EF2D2255B6DAF007E1867 /* OWSProfileManager.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = OWSProfileManager.h; path = "SignalUtilitiesKit/To Do/OWSProfileManager.h"; sourceTree = SOURCE_ROOT; };
C38EF2D3255B6DAF007E1867 /* OWSUserProfile.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = OWSUserProfile.h; path = "SignalUtilitiesKit/To Do/OWSUserProfile.h"; sourceTree = SOURCE_ROOT; };
C38EF2E2255B6DB9007E1867 /* OWSScreenLock.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = OWSScreenLock.swift; path = SignalUtilitiesKit/OWSScreenLock.swift; sourceTree = SOURCE_ROOT; };
C38EF2E3255B6DB9007E1867 /* OWSUnreadIndicator.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = OWSUnreadIndicator.m; path = SignalUtilitiesKit/OWSUnreadIndicator.m; sourceTree = SOURCE_ROOT; };
C38EF2E4255B6DB9007E1867 /* FullTextSearcher.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = FullTextSearcher.swift; path = SignalUtilitiesKit/FullTextSearcher.swift; sourceTree = SOURCE_ROOT; };
@ -1817,7 +1812,7 @@
C38EF3D2255B6DEE007E1867 /* ThreadViewHelper.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = ThreadViewHelper.h; path = SignalUtilitiesKit/Database/ThreadViewHelper.h; sourceTree = SOURCE_ROOT; };
C38EF3D3255B6DEE007E1867 /* OWSSearchBar.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = OWSSearchBar.h; path = SignalUtilitiesKit/UI/OWSSearchBar.h; sourceTree = SOURCE_ROOT; };
C38EF3D4255B6DEE007E1867 /* DisappearingTimerConfigurationView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = DisappearingTimerConfigurationView.swift; path = SignalUtilitiesKit/UI/DisappearingTimerConfigurationView.swift; sourceTree = SOURCE_ROOT; };
C38EF3D6255B6DEF007E1867 /* ContactCellView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = ContactCellView.m; path = SignalUtilitiesKit/Remove/ContactCellView.m; sourceTree = SOURCE_ROOT; };
C38EF3D6255B6DEF007E1867 /* ContactCellView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = ContactCellView.m; path = "SignalUtilitiesKit/To Do/ContactCellView.m"; sourceTree = SOURCE_ROOT; };
C38EF3D7255B6DF0007E1867 /* OWSTextField.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = OWSTextField.h; path = SignalUtilitiesKit/UI/OWSTextField.h; sourceTree = SOURCE_ROOT; };
C38EF3D8255B6DF0007E1867 /* OWSTextView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = OWSTextView.h; path = SignalUtilitiesKit/UI/OWSTextView.h; sourceTree = SOURCE_ROOT; };
C38EF3D9255B6DF1007E1867 /* OWSNavigationBar.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = OWSNavigationBar.swift; path = SignalUtilitiesKit/UI/OWSNavigationBar.swift; sourceTree = SOURCE_ROOT; };
@ -1830,13 +1825,13 @@
C38EF3E2255B6DF3007E1867 /* GalleryRailView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = GalleryRailView.swift; path = SignalUtilitiesKit/UI/GalleryRailView.swift; sourceTree = SOURCE_ROOT; };
C38EF3E3255B6DF4007E1867 /* VideoPlayerView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = VideoPlayerView.swift; path = SignalUtilitiesKit/UI/VideoPlayerView.swift; sourceTree = SOURCE_ROOT; };
C38EF3E4255B6DF4007E1867 /* CommonStrings.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = CommonStrings.swift; path = SignalUtilitiesKit/CommonStrings.swift; sourceTree = SOURCE_ROOT; };
C38EF3E5255B6DF4007E1867 /* ContactCellView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = ContactCellView.h; path = SignalUtilitiesKit/Remove/ContactCellView.h; sourceTree = SOURCE_ROOT; };
C38EF3E6255B6DF4007E1867 /* ContactTableViewCell.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = ContactTableViewCell.h; path = SignalUtilitiesKit/Remove/ContactTableViewCell.h; sourceTree = SOURCE_ROOT; };
C38EF3E5255B6DF4007E1867 /* ContactCellView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = ContactCellView.h; path = "SignalUtilitiesKit/To Do/ContactCellView.h"; sourceTree = SOURCE_ROOT; };
C38EF3E6255B6DF4007E1867 /* ContactTableViewCell.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = ContactTableViewCell.h; path = "SignalUtilitiesKit/To Do/ContactTableViewCell.h"; sourceTree = SOURCE_ROOT; };
C38EF3E7255B6DF5007E1867 /* OWSButton.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = OWSButton.swift; path = SignalUtilitiesKit/UI/OWSButton.swift; sourceTree = SOURCE_ROOT; };
C38EF3E8255B6DF6007E1867 /* OWSAlerts.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = OWSAlerts.swift; path = SignalUtilitiesKit/UI/OWSAlerts.swift; sourceTree = SOURCE_ROOT; };
C38EF3E9255B6DF6007E1867 /* Toast.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = Toast.swift; path = SignalUtilitiesKit/UI/Toast.swift; sourceTree = SOURCE_ROOT; };
C38EF3EA255B6DF6007E1867 /* OWSSearchBar.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = OWSSearchBar.m; path = SignalUtilitiesKit/UI/OWSSearchBar.m; sourceTree = SOURCE_ROOT; };
C38EF3EB255B6DF6007E1867 /* ContactTableViewCell.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = ContactTableViewCell.m; path = SignalUtilitiesKit/Remove/ContactTableViewCell.m; sourceTree = SOURCE_ROOT; };
C38EF3EB255B6DF6007E1867 /* ContactTableViewCell.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = ContactTableViewCell.m; path = "SignalUtilitiesKit/To Do/ContactTableViewCell.m"; sourceTree = SOURCE_ROOT; };
C38EF3EC255B6DF6007E1867 /* OWSFlatButton.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = OWSFlatButton.swift; path = SignalUtilitiesKit/UI/OWSFlatButton.swift; sourceTree = SOURCE_ROOT; };
C38EF3ED255B6DF6007E1867 /* TappableStackView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = TappableStackView.swift; path = SignalUtilitiesKit/UI/TappableStackView.swift; sourceTree = SOURCE_ROOT; };
C38EF3EE255B6DF6007E1867 /* GradientView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = GradientView.swift; path = SignalUtilitiesKit/UI/GradientView.swift; sourceTree = SOURCE_ROOT; };
@ -2017,14 +2012,11 @@
C3DFFAC523E96F0D0058DAF8 /* Sheet.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Sheet.swift; sourceTree = "<group>"; };
C3E5C2F9251DBABB0040DFFC /* EditClosedGroupVC.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EditClosedGroupVC.swift; sourceTree = "<group>"; };
C3E7134E251C867C009649BB /* Sodium+Conversion.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Sodium+Conversion.swift"; sourceTree = "<group>"; };
C3EEA016256487B300C338BC /* LokiDatabaseUtilities.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LokiDatabaseUtilities.swift; sourceTree = "<group>"; };
C3F0A52F255C80BC007BE2A3 /* NoopNotificationsManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NoopNotificationsManager.swift; sourceTree = "<group>"; };
C3F0A5B2255C915C007BE2A3 /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/Localizable.strings; sourceTree = "<group>"; };
C3F0A5EB255C970D007BE2A3 /* Configuration.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Configuration.swift; sourceTree = "<group>"; };
C3F0A5FD255C988A007BE2A3 /* Storage+Shared.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Storage+Shared.swift"; sourceTree = "<group>"; };
C3F0A607255C98A6007BE2A3 /* Storage+SessionSnodeKit.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Storage+SessionSnodeKit.swift"; sourceTree = "<group>"; };
C3F0A619255C9902007BE2A3 /* Storage+SessionProtocolKit.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Storage+SessionProtocolKit.swift"; sourceTree = "<group>"; };
C3F0A62B255C9937007BE2A3 /* AppDelegate+SharedSenderKeys.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "AppDelegate+SharedSenderKeys.swift"; sourceTree = "<group>"; };
C3F0A607255C98A6007BE2A3 /* Storage+SnodeAPI.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Storage+SnodeAPI.swift"; sourceTree = "<group>"; };
C88965DE4F4EC4FC919BEC4E /* Pods-SessionUIKit.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-SessionUIKit.debug.xcconfig"; path = "Pods/Target Support Files/Pods-SessionUIKit/Pods-SessionUIKit.debug.xcconfig"; sourceTree = "<group>"; };
CB3724C70247A916D43271FE /* Pods_Session.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_Session.framework; sourceTree = BUILT_PRODUCTS_DIR; };
D2179CFB16BB0B3A0006F3AB /* CoreTelephony.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreTelephony.framework; path = System/Library/Frameworks/CoreTelephony.framework; sourceTree = SDKROOT; };
@ -2694,18 +2686,6 @@
path = Meta;
sourceTree = "<group>";
};
C31F812425258F9C00DD9FD9 /* Database */ = {
isa = PBXGroup;
children = (
C3550A1C255DD73500194B6A /* Storage+SessionMessagingKit.swift */,
C3F0A619255C9902007BE2A3 /* Storage+SessionProtocolKit.swift */,
C3F0A607255C98A6007BE2A3 /* Storage+SessionSnodeKit.swift */,
C3F0A5FD255C988A007BE2A3 /* Storage+Shared.swift */,
C31F812525258FB000DD9FD9 /* Storage+VolumeSamples.swift */,
);
path = Database;
sourceTree = "<group>";
};
C32B405424A961E1001117B5 /* Dependencies */ = {
isa = PBXGroup;
children = (
@ -2777,8 +2757,7 @@
isa = PBXGroup;
children = (
C33FD9B7255A54A300E217F9 /* Meta */,
C3851CE3256250FA0061EEB0 /* Remove */,
C38BBA17255E327A0041B9A3 /* Move to Session */,
C3851CE3256250FA0061EEB0 /* To Do */,
C3CA3B11255CF17200F4C6D4 /* Utilities */,
C3851CD225624B060061EEB0 /* UI */,
C38BBA0B255E31EC0041B9A3 /* Attachments */,
@ -2934,6 +2913,7 @@
C3851CD225624B060061EEB0 /* UI */ = {
isa = PBXGroup;
children = (
C38EF2A4255B6D93007E1867 /* ProfilePictureView.swift */,
B8C2B2C72563685C00551B4D /* CircleView.swift */,
C38EF23D255B6D66007E1867 /* UIView+OWS.h */,
C38EF23E255B6D66007E1867 /* UIView+OWS.m */,
@ -3006,7 +2986,6 @@
C38EF2A3255B6D93007E1867 /* PlaceholderIcon.swift */,
C38EF2B2255B6D9C007E1867 /* UIView+Utilities.swift */,
C38EF2B1255B6D9C007E1867 /* UIViewController+Utilities.swift */,
C38EF2A4255B6D93007E1867 /* ProfilePictureView.swift */,
C38EF212255B6D3A007E1867 /* Theme.h */,
C38EF214255B6D3A007E1867 /* Theme.m */,
C38EF307255B6DBE007E1867 /* UIGestureRecognizer+OWS.swift */,
@ -3028,7 +3007,7 @@
path = UI;
sourceTree = "<group>";
};
C3851CE3256250FA0061EEB0 /* Remove */ = {
C3851CE3256250FA0061EEB0 /* To Do */ = {
isa = PBXGroup;
children = (
C33FDB19255A580900E217F9 /* GroupUtilities.swift */,
@ -3038,6 +3017,7 @@
C33FDBB9255A581600E217F9 /* ProfileManagerProtocol.h */,
C33FDBA3255A581400E217F9 /* SessionManagementProtocol.swift */,
C33FDAFB255A580600E217F9 /* SessionMetaProtocol.swift */,
C33FDADF255A580400E217F9 /* PublicChatManager.swift */,
C38EF3E5255B6DF4007E1867 /* ContactCellView.h */,
C38EF3D6255B6DEF007E1867 /* ContactCellView.m */,
C38EF3E6255B6DF4007E1867 /* ContactTableViewCell.h */,
@ -3046,8 +3026,10 @@
C38EF2CF255B6DAE007E1867 /* OWSProfileManager.m */,
C38EF2D3255B6DAF007E1867 /* OWSUserProfile.h */,
C38EF2D1255B6DAF007E1867 /* OWSUserProfile.m */,
C33FDBBB255A581600E217F9 /* OWSPrimaryStorage+Loki.h */,
C33FDB58255A580E00E217F9 /* OWSPrimaryStorage+Loki.m */,
);
path = Remove;
path = "To Do";
sourceTree = "<group>";
};
C38BBA0B255E31EC0041B9A3 /* Attachments */ = {
@ -3084,12 +3066,25 @@
C38BBA0D255E321C0041B9A3 /* Messaging */ = {
isa = PBXGroup;
children = (
C33FDB3A255A580B00E217F9 /* Poller.swift */,
C33FDB34255A580B00E217F9 /* ClosedGroupPoller.swift */,
C33FDA8C255A57FD00E217F9 /* OpenGroupPoller.swift */,
C33FDBDE255A581900E217F9 /* PushNotificationManager.swift */,
C3CA3B12255CF18200F4C6D4 /* Destination+Conversion.swift */,
C38EEF09255B49A8007E1867 /* SNProtoEnvelope+Conversion.swift */,
C3B7845C25649DA600ADB2E7 /* TSIncomingMessage+Conversion.swift */,
B84072952565E9F50037CB17 /* TSOutgoingMessage+Conversion.swift */,
B840729F2565F1670037CB17 /* Quote+Conversion.swift */,
B8D8F1EF256621180092EF10 /* MessageSenderDelegate.swift */,
C3D697492564DEDC004AF766 /* MessageReceiverDelegate.swift */,
B8D8F21925662A4D0092EF10 /* OpenGroupAPIDelegate.swift */,
C3CA3B1C255CF3C800F4C6D4 /* MessageSender+Utilities.swift */,
C33FDA7E255A57FB00E217F9 /* Mention.swift */,
C33FDA81255A57FC00E217F9 /* MentionsManager.swift */,
C38EF398255B6DD9007E1867 /* OWSQuotedReplyModel.h */,
C38EF39A255B6DD9007E1867 /* OWSQuotedReplyModel.m */,
C38EF2BE255B6DA6007E1867 /* TSUnreadIndicatorInteraction.h */,
C38EF2C1255B6DA6007E1867 /* TSUnreadIndicatorInteraction.m */,
C33FDA7E255A57FB00E217F9 /* Mention.swift */,
C33FDA81255A57FC00E217F9 /* MentionsManager.swift */,
C33FDAD9255A580300E217F9 /* OWSDisappearingMessagesConfiguration.h */,
C33FDBA4255A581400E217F9 /* OWSDisappearingMessagesConfiguration.m */,
C33FDC05255A581D00E217F9 /* OWSDisappearingMessagesFinder.h */,
@ -3139,6 +3134,16 @@
C38BBA0E255E32440041B9A3 /* Database */ = {
isa = PBXGroup;
children = (
C33FDB36255A580B00E217F9 /* Storage.swift */,
B8D8F1372566120F0092EF10 /* Storage+ClosedGroups.swift */,
B8D8F17625661AFA0092EF10 /* Storage+Jobs.swift */,
B8D8F19225661BF80092EF10 /* Storage+Messaging.swift */,
B8D8F1BC25661C6F0092EF10 /* Storage+OnionRequests.swift */,
B8D8F18825661BA50092EF10 /* Storage+OpenGroups.swift */,
C33FDB8B255A581200E217F9 /* Storage+SessionManagement.swift */,
C3F0A5FD255C988A007BE2A3 /* Storage+Shared.swift */,
C3F0A607255C98A6007BE2A3 /* Storage+SnodeAPI.swift */,
C31F812525258FB000DD9FD9 /* Storage+VolumeSamples.swift */,
C38EF3D2255B6DEE007E1867 /* ThreadViewHelper.h */,
C38EF3D1255B6DEE007E1867 /* ThreadViewHelper.m */,
C38EF285255B6D84007E1867 /* SignalKeyingStorage.h */,
@ -3181,30 +3186,6 @@
path = Database;
sourceTree = "<group>";
};
C38BBA17255E327A0041B9A3 /* Move to Session */ = {
isa = PBXGroup;
children = (
C33FDB6B255A580F00E217F9 /* LKUserDefaults.swift */,
C33FDB34255A580B00E217F9 /* ClosedGroupPoller.swift */,
C33FDBDE255A581900E217F9 /* LokiPushNotificationManager.swift */,
C33FDB3A255A580B00E217F9 /* Poller.swift */,
C33FDADF255A580400E217F9 /* PublicChatManager.swift */,
C33FDA8C255A57FD00E217F9 /* PublicChatPoller.swift */,
C3EEA016256487B300C338BC /* LokiDatabaseUtilities.swift */,
C33FDBBB255A581600E217F9 /* OWSPrimaryStorage+Loki.h */,
C33FDB58255A580E00E217F9 /* OWSPrimaryStorage+Loki.m */,
C33FDA85255A57FC00E217F9 /* OWSPrimaryStorage+Loki.swift */,
C33FDB36255A580B00E217F9 /* Storage.swift */,
C33FDC07255A581D00E217F9 /* Storage+ClosedGroups.swift */,
C33FDB95255A581300E217F9 /* Storage+Collections.swift */,
C33FDBE8255A581A00E217F9 /* Storage+OnionRequests.swift */,
C33FDBFB255A581C00E217F9 /* Storage+PublicChats.swift */,
C33FDB8B255A581200E217F9 /* Storage+SessionManagement.swift */,
C33FDB37255A580B00E217F9 /* Storage+SnodeAPI.swift */,
);
path = "Move to Session";
sourceTree = "<group>";
};
C396DAE72518407300FF6DC5 /* SwiftCSV */ = {
isa = PBXGroup;
children = (
@ -3575,21 +3556,23 @@
C3CA3B11255CF17200F4C6D4 /* Utilities */ = {
isa = PBXGroup;
children = (
C33FDB6B255A580F00E217F9 /* LKUserDefaults.swift */,
C33FDAAF255A580000E217F9 /* String+Trimming.swift */,
C33FDB80255A581100E217F9 /* Notification+Loki.swift */,
C33FDBC1255A581700E217F9 /* GeneralUtilities.swift */,
C33FDA73255A57FA00E217F9 /* ECKeyPair+Hexadecimal.swift */,
C33FDAA8255A57FF00E217F9 /* BuildConfiguration.swift */,
C33FDBCA255A581700E217F9 /* LKGroupUtilities.h */,
C33FDBE1255A581A00E217F9 /* LKGroupUtilities.m */,
C33FDC16255A581E00E217F9 /* FunctionalUtil.h */,
C33FDB17255A580800E217F9 /* FunctionalUtil.m */,
C33FDB80255A581100E217F9 /* Notification+Loki.swift */,
C33FDB8F255A581200E217F9 /* ParamParser.swift */,
C33FDB32255A580A00E217F9 /* SSKIncrementingIdFinder.swift */,
C33FDADE255A580400E217F9 /* SwiftSingletons.swift */,
C33FDA73255A57FA00E217F9 /* ECKeyPair+Hexadecimal.swift */,
C33FDBC1255A581700E217F9 /* GeneralUtilities.swift */,
C33FDBCA255A581700E217F9 /* LKGroupUtilities.h */,
C33FDBE1255A581A00E217F9 /* LKGroupUtilities.m */,
C33FDB1C255A580900E217F9 /* UIImage+OWS.h */,
C33FDB81255A581100E217F9 /* UIImage+OWS.m */,
C33FDB49255A580C00E217F9 /* WeakTimer.swift */,
C33FDB3F255A580C00E217F9 /* String+SSK.swift */,
C33FDAAF255A580000E217F9 /* String+Trimming.swift */,
C33FDBC2255A581700E217F9 /* SSKAsserts.h */,
C33FDA9E255A57FF00E217F9 /* ReverseDispatchQueue.swift */,
C33FDB91255A581200E217F9 /* ProtoUtils.h */,
@ -3616,18 +3599,14 @@
C33FDAFC255A580600E217F9 /* MIMETypeUtil.h */,
C33FDB41255A580C00E217F9 /* MIMETypeUtil.m */,
C33FDAFD255A580600E217F9 /* LRUCache.swift */,
C33FDA7C255A57FB00E217F9 /* Debugging.swift */,
C33FDC03255A581D00E217F9 /* ByteParser.h */,
C33FDAE0255A580400E217F9 /* ByteParser.m */,
C33FDAA8255A57FF00E217F9 /* BuildConfiguration.swift */,
C38EF3DD255B6DF1007E1867 /* UIAlertController+OWS.swift */,
C38EF225255B6D5D007E1867 /* AttachmentSharing.h */,
C38EF223255B6D5D007E1867 /* AttachmentSharing.m */,
C38EF241255B6D67007E1867 /* Collection+OWS.swift */,
C38EF2F8255B6DBC007E1867 /* DebugLogger.h */,
C38EF2E6255B6DBA007E1867 /* DebugLogger.m */,
C3CA3B12255CF18200F4C6D4 /* Destination+Conversion.swift */,
C3CA3B1C255CF3C800F4C6D4 /* MessageSender+Utilities.swift */,
C38EF3AE255B6DE5007E1867 /* OrderedDictionary.swift */,
C38EF2E7255B6DBA007E1867 /* OWSScrubbingLogFormatter.h */,
C38EF2F6255B6DBC007E1867 /* OWSScrubbingLogFormatter.m */,
@ -3637,15 +3616,12 @@
C38EF305255B6DBE007E1867 /* OWSFormat.m */,
C38EF281255B6D84007E1867 /* OWSAudioSession.swift */,
C38EF226255B6D5D007E1867 /* ShareViewDelegate.swift */,
C38EEF09255B49A8007E1867 /* SNProtoEnvelope+Conversion.swift */,
C38EF227255B6D5D007E1867 /* OWSVideoPlayer.swift */,
C38EF2EF255B6DBB007E1867 /* Weak.swift */,
C38EF2FA255B6DBD007E1867 /* Bench.swift */,
C38EF23F255B6D67007E1867 /* NSAttributedString+OWS.h */,
C38EF23A255B6D66007E1867 /* NSAttributedString+OWS.m */,
C38EF237255B6D65007E1867 /* UIDevice+featureSupport.swift */,
C3B7845C25649DA600ADB2E7 /* TSIncomingMessage+Conversion.swift */,
B84072952565E9F50037CB17 /* TSOutgoingMessage+Conversion.swift */,
);
path = Utilities;
sourceTree = "<group>";
@ -3763,12 +3739,8 @@
isa = PBXGroup;
children = (
C3F0A58F255C8E3D007BE2A3 /* Meta */,
C3550A02255DD6D900194B6A /* AppDelegate+OpenGroupAPI.swift */,
C3F0A62B255C9937007BE2A3 /* AppDelegate+SharedSenderKeys.swift */,
C3F0A5EB255C970D007BE2A3 /* Configuration.swift */,
C3D697492564DEDC004AF766 /* MessageReceiverDelegate.swift */,
B8CCF63B239757C10091D419 /* Components */,
C31F812425258F9C00DD9FD9 /* Database */,
C32B405424A961E1001117B5 /* Dependencies */,
76EB03C118170B33006006FC /* Signal */,
B8CCF63C239757DB0091D419 /* Utilities */,
@ -4810,7 +4782,7 @@
C33FDD45255A582000E217F9 /* Storage+SessionManagement.swift in Sources */,
C33FDCA3255A582000E217F9 /* SSKMessageSenderJobRecord.m in Sources */,
C38EF3C3255B6DE7007E1867 /* ImageEditorTextItem.swift in Sources */,
C33FDDB5255A582000E217F9 /* Storage+PublicChats.swift in Sources */,
B8D8F16B256615DE0092EF10 /* Storage+VolumeSamples.swift in Sources */,
C33FDC41255A581F00E217F9 /* TypingIndicators.swift in Sources */,
C33FDC7D255A582000E217F9 /* OWSDispatch.m in Sources */,
C33FDC2E255A581F00E217F9 /* ClosedGroupsProtocol.swift in Sources */,
@ -4819,6 +4791,7 @@
C33FDC99255A582000E217F9 /* PublicChatManager.swift in Sources */,
C33FDCB9255A582000E217F9 /* DisplayNameUtilities.swift in Sources */,
C33FDD49255A582000E217F9 /* ParamParser.swift in Sources */,
B8D8F19325661BF80092EF10 /* Storage+Messaging.swift in Sources */,
C38EF35F255B6DCC007E1867 /* SelectRecipientViewController.m in Sources */,
C38EF3C5255B6DE7007E1867 /* OWSViewController+ImageEditor.swift in Sources */,
C33FDDCD255A582000E217F9 /* OWSAttachmentDownloads.m in Sources */,
@ -4840,7 +4813,7 @@
C33FDC87255A582000E217F9 /* SSKJobRecord.m in Sources */,
C33FDD23255A582000E217F9 /* FeatureFlags.swift in Sources */,
C38EF297255B6D86007E1867 /* OWSSounds.m in Sources */,
C33FDCF1255A582000E217F9 /* Storage+SnodeAPI.swift in Sources */,
B8D8F18925661BA50092EF10 /* Storage+OpenGroups.swift in Sources */,
C33FDCAE255A582000E217F9 /* SSKEnvironment.m in Sources */,
C33FDCF9255A582000E217F9 /* String+SSK.swift in Sources */,
C38EF389255B6DD2007E1867 /* AttachmentTextView.swift in Sources */,
@ -4853,16 +4826,17 @@
C33FDD26255A582000E217F9 /* NSNotificationCenter+OWS.m in Sources */,
C38EF36F255B6DCC007E1867 /* OWSViewController.m in Sources */,
C33FDD42255A582000E217F9 /* TSAccountManager.m in Sources */,
B8D8F1E6256620DD0092EF10 /* MessageReceiverDelegate.swift in Sources */,
C38EF3FB255B6DF7007E1867 /* UIAlertController+OWS.swift in Sources */,
C33FDC53255A582000E217F9 /* OutageDetection.swift in Sources */,
C38EF30C255B6DBF007E1867 /* OWSScreenLock.swift in Sources */,
C38EF363255B6DCC007E1867 /* ModalActivityIndicatorViewController.swift in Sources */,
C38EF38A255B6DD2007E1867 /* AttachmentCaptionToolbar.swift in Sources */,
C33FDD00255A582000E217F9 /* TSDatabaseView.m in Sources */,
C3EEA017256487B300C338BC /* LokiDatabaseUtilities.swift in Sources */,
C33FDD3B255A582000E217F9 /* UIImage+OWS.m in Sources */,
C33FDD83255A582000E217F9 /* CreatePreKeysOperation.swift in Sources */,
C33FDDD5255A582000E217F9 /* OWSBackgroundTask.m in Sources */,
B84072A02565F1670037CB17 /* Quote+Conversion.swift in Sources */,
C33FDC7E255A582000E217F9 /* TSAttachmentStream.m in Sources */,
C33FDC62255A582000E217F9 /* BuildConfiguration.swift in Sources */,
C38EF40A255B6DF7007E1867 /* OWSFlatButton.swift in Sources */,
@ -4871,7 +4845,6 @@
C38EF3C1255B6DE7007E1867 /* ImageEditorBrushViewController.swift in Sources */,
C33FDD3F255A582000E217F9 /* AppContext.m in Sources */,
C33FDCC7255A582000E217F9 /* NSArray+OWS.m in Sources */,
C33FDD25255A582000E217F9 /* LKUserDefaults.swift in Sources */,
C38EF3EF255B6DF7007E1867 /* ThreadViewHelper.m in Sources */,
C33FDD72255A582000E217F9 /* TSThread.m in Sources */,
C33FDC71255A582000E217F9 /* OWSFailedMessagesJob.m in Sources */,
@ -4891,10 +4864,9 @@
C33FDC29255A581F00E217F9 /* ReachabilityManager.swift in Sources */,
C38EF407255B6DF7007E1867 /* Toast.swift in Sources */,
C38EF38C255B6DD2007E1867 /* ApprovalRailCellView.swift in Sources */,
C33FDDA2255A582000E217F9 /* Storage+OnionRequests.swift in Sources */,
B8D8F0F32565F98E0092EF10 /* LKUserDefaults.swift in Sources */,
C38EF409255B6DF7007E1867 /* ContactTableViewCell.m in Sources */,
C38EF32A255B6DBF007E1867 /* UIUtil.m in Sources */,
C33FDC3F255A581F00E217F9 /* OWSPrimaryStorage+Loki.swift in Sources */,
C38EF333255B6DBF007E1867 /* DeviceSleepManager.swift in Sources */,
C33FDC52255A582000E217F9 /* RotateSignedKeyOperation.swift in Sources */,
C33FDCDA255A582000E217F9 /* TSDatabaseSecondaryIndexes.m in Sources */,
@ -4911,13 +4883,15 @@
C33FDC45255A581F00E217F9 /* AppVersion.m in Sources */,
C33FDC7C255A582000E217F9 /* TSAttachment.m in Sources */,
C38EF3C7255B6DE7007E1867 /* ImageEditorCanvasView.swift in Sources */,
C33FDC46255A581F00E217F9 /* PublicChatPoller.swift in Sources */,
B8D8F16A256615DE0092EF10 /* Storage+Shared.swift in Sources */,
C33FDC46255A581F00E217F9 /* OpenGroupPoller.swift in Sources */,
C38EF400255B6DF7007E1867 /* GalleryRailView.swift in Sources */,
C38EF32E255B6DBF007E1867 /* ImageCache.swift in Sources */,
C33FDC34255A581F00E217F9 /* NSRegularExpression+SSK.swift in Sources */,
C38EF32F255B6DBF007E1867 /* OWSFormat.m in Sources */,
C38EF218255B6D3B007E1867 /* Theme.m in Sources */,
C38EF3BA255B6DE7007E1867 /* ImageEditorItem.swift in Sources */,
B8D8F1382566120F0092EF10 /* Storage+ClosedGroups.swift in Sources */,
C33FDD5D255A582000E217F9 /* SessionManagementProtocol.swift in Sources */,
C38EF3F7255B6DF7007E1867 /* OWSNavigationBar.swift in Sources */,
C33FDD76255A582000E217F9 /* SSKKeychainStorage.swift in Sources */,
@ -4933,8 +4907,8 @@
C33FDD05255A582000E217F9 /* OWSChunkedOutputStream.m in Sources */,
C33FDD10255A582000E217F9 /* TSOutgoingMessage.m in Sources */,
C33FDCFA255A582000E217F9 /* SignalIOSProto.swift in Sources */,
B8D8F12E2565FC910092EF10 /* Storage.swift in Sources */,
C33FDCC3255A582000E217F9 /* NSError+MessageSending.m in Sources */,
C33FDCF0255A582000E217F9 /* Storage.swift in Sources */,
C33FDD2D255A582000E217F9 /* TSGroupModel.m in Sources */,
C33FDD9B255A582000E217F9 /* LKGroupUtilities.m in Sources */,
C38EF229255B6D5D007E1867 /* SignalAttachment.swift in Sources */,
@ -4948,17 +4922,16 @@
C38EF408255B6DF7007E1867 /* OWSSearchBar.m in Sources */,
C38EF327255B6DBF007E1867 /* BlockListUIUtils.m in Sources */,
C33FDCFF255A582000E217F9 /* NSString+SSK.m in Sources */,
C33FDC36255A581F00E217F9 /* Debugging.swift in Sources */,
C33FDD22255A582000E217F9 /* ContentProxy.swift in Sources */,
C38EF310255B6DBF007E1867 /* DebugLogger.m in Sources */,
C38EF38B255B6DD2007E1867 /* AttachmentPrepViewController.swift in Sources */,
C33FDD98255A582000E217F9 /* LokiPushNotificationManager.swift in Sources */,
C33FDD70255A582000E217F9 /* DataSource.m in Sources */,
C33FDD2F255A582000E217F9 /* AppReadiness.m in Sources */,
C33FDCEC255A582000E217F9 /* SSKIncrementingIdFinder.swift in Sources */,
C33FDC7B255A582000E217F9 /* NSSet+Functional.m in Sources */,
C33FDC69255A582000E217F9 /* String+Trimming.swift in Sources */,
C33FDCB7255A582000E217F9 /* LRUCache.swift in Sources */,
B8D8F1F0256621180092EF10 /* MessageSenderDelegate.swift in Sources */,
C38EF405255B6DF7007E1867 /* OWSButton.swift in Sources */,
C38EF3C4255B6DE7007E1867 /* ImageEditorContents.swift in Sources */,
C33FDC3B255A581F00E217F9 /* MentionsManager.swift in Sources */,
@ -4969,13 +4942,13 @@
C38EF387255B6DD2007E1867 /* AttachmentItemCollection.swift in Sources */,
C38EF22B255B6D5D007E1867 /* ShareViewDelegate.swift in Sources */,
C33FDC38255A581F00E217F9 /* Mention.swift in Sources */,
C33FDDC1255A582000E217F9 /* Storage+ClosedGroups.swift in Sources */,
C38EF3BF255B6DE7007E1867 /* ImageEditorView.swift in Sources */,
C38EF365255B6DCC007E1867 /* OWSTableViewController.m in Sources */,
C33FDC25255A581F00E217F9 /* OWSDisappearingConfigurationUpdateInfoMessage.m in Sources */,
C38EF36B255B6DCC007E1867 /* ScreenLockViewController.m in Sources */,
C33FDC39255A581F00E217F9 /* OWSRecordTranscriptJob.m in Sources */,
C38EF228255B6D5D007E1867 /* AttachmentSharing.m in Sources */,
B8D8F16C256615DE0092EF10 /* Storage+SnodeAPI.swift in Sources */,
C33FDD1E255A582000E217F9 /* PreKeyRefreshOperation.swift in Sources */,
C3B7845D25649DA600ADB2E7 /* TSIncomingMessage+Conversion.swift in Sources */,
C33FDCF4255A582000E217F9 /* Poller.swift in Sources */,
@ -4983,6 +4956,7 @@
C33FDC26255A581F00E217F9 /* ProtoUtils.m in Sources */,
C33FDC48255A581F00E217F9 /* OWSFileSystem.m in Sources */,
C38EF39E255B6DDA007E1867 /* OWSQuotedReplyModel.m in Sources */,
B8D8F17725661AFA0092EF10 /* Storage+Jobs.swift in Sources */,
C33FDD85255A582000E217F9 /* TSInvalidIdentityKeyReceivingErrorMessage.m in Sources */,
C33FDD2E255A582000E217F9 /* TSInvalidIdentityKeyErrorMessage.m in Sources */,
C33FDDBB255A582000E217F9 /* TSGroupThread.m in Sources */,
@ -4993,7 +4967,6 @@
C33FDD7B255A582000E217F9 /* GeneralUtilities.swift in Sources */,
C33FDCC1255A582000E217F9 /* OWSBackupFragment.m in Sources */,
C33FDDAE255A582000E217F9 /* DisplayNameUtilities2.swift in Sources */,
C33FDD4F255A582000E217F9 /* Storage+Collections.swift in Sources */,
C33FDD15255A582000E217F9 /* YapDatabaseTransaction+OWS.m in Sources */,
C33FDD63255A582000E217F9 /* OWSIdentityManager.m in Sources */,
C38EF35C255B6DCC007E1867 /* SelectThreadViewController.m in Sources */,
@ -5009,6 +4982,7 @@
C33FDC22255A581F00E217F9 /* OWSBlockingManager.m in Sources */,
C38EF3FE255B6DF7007E1867 /* OWSTextField.m in Sources */,
C33FDC40255A581F00E217F9 /* OWSDisappearingMessagesFinder.m in Sources */,
B8D8F21A25662A4D0092EF10 /* OpenGroupAPIDelegate.swift in Sources */,
C33FDCEE255A582000E217F9 /* ClosedGroupPoller.swift in Sources */,
C38EF3BD255B6DE7007E1867 /* ImageEditorTransform.swift in Sources */,
C38EF24F255B6D67007E1867 /* UIColor+OWS.m in Sources */,
@ -5018,6 +4992,7 @@
C33FDC58255A582000E217F9 /* ReverseDispatchQueue.swift in Sources */,
C38EF3F4255B6DF7007E1867 /* ContactCellView.m in Sources */,
C33FDD29255A582000E217F9 /* OWSOutgoingReceiptManager.m in Sources */,
B8D8F0F42565F98E0092EF10 /* PushNotificationManager.swift in Sources */,
C33FDC78255A582000E217F9 /* TSConstants.m in Sources */,
C38EF324255B6DBF007E1867 /* Bench.swift in Sources */,
C38EF292255B6D86007E1867 /* VersionMigrations.m in Sources */,
@ -5063,6 +5038,7 @@
C38EF31C255B6DBF007E1867 /* Searcher.swift in Sources */,
C38EF2B3255B6D9C007E1867 /* UIViewController+Utilities.swift in Sources */,
C38EF3BE255B6DE7007E1867 /* OrderedDictionary.swift in Sources */,
B8D8F1BD25661C6F0092EF10 /* Storage+OnionRequests.swift in Sources */,
C33FDD6E255A582000E217F9 /* NSURLSessionDataTask+StatusCode.m in Sources */,
C33FDD2B255A582000E217F9 /* OWSMediaGalleryFinder.m in Sources */,
C38EF2B4255B6D9C007E1867 /* UIView+Utilities.swift in Sources */,
@ -5228,7 +5204,6 @@
C396DAF52518408B00FF6DC5 /* CSV.swift in Sources */,
B8CCF63723961D6D0091D419 /* NewPrivateChatVC.swift in Sources */,
3461293E1FD1D72B00532771 /* ExperienceUpgradeFinder.swift in Sources */,
C3F0A61A255C9902007BE2A3 /* Storage+SessionProtocolKit.swift in Sources */,
34C4E2582118957600BEA353 /* WebRTCProto.swift in Sources */,
C396DAF12518408B00FF6DC5 /* EnumeratedView.swift in Sources */,
34D1F0BD1F8D108C0066283D /* AttachmentUploadView.m in Sources */,
@ -5241,7 +5216,6 @@
3496957421A301A100DCFE74 /* OWSBackupAPI.swift in Sources */,
C3E5C2FA251DBABB0040DFFC /* EditClosedGroupVC.swift in Sources */,
B8783E9E23EB948D00404FB8 /* UILabel+Interaction.swift in Sources */,
C3550A03255DD6D900194B6A /* AppDelegate+OpenGroupAPI.swift in Sources */,
B893063F2383961A005EAA8E /* ScanQRCodeWrapperVC.swift in Sources */,
B879D449247E1BE300DB3608 /* PathVC.swift in Sources */,
454A84042059C787008B8C75 /* MediaTileViewController.swift in Sources */,
@ -5303,7 +5277,6 @@
C3DFFAC623E96F0D0058DAF8 /* Sheet.swift in Sources */,
C31FFE57254A5FFE00F19441 /* KeyPairUtilities.swift in Sources */,
45C0DC1E1E69011F00E04C47 /* UIStoryboard+OWS.swift in Sources */,
C3F0A62C255C9937007BE2A3 /* AppDelegate+SharedSenderKeys.swift in Sources */,
45A6DAD61EBBF85500893231 /* ReminderView.swift in Sources */,
34D1F0881F8678AA0066283D /* ConversationViewLayout.m in Sources */,
B82B408E239DC00D00A248E7 /* DisplayNameVC.swift in Sources */,
@ -5320,17 +5293,14 @@
C331FFF32558FF0300070591 /* PathStatusView.swift in Sources */,
4CC1ECFB211A553000CC13BE /* AppUpdateNag.swift in Sources */,
34B6A903218B3F63007C4606 /* TypingIndicatorView.swift in Sources */,
C3F0A608255C98A6007BE2A3 /* Storage+SessionSnodeKit.swift in Sources */,
458E38371D668EBF0094BD24 /* OWSDeviceProvisioningURLParser.m in Sources */,
34B6A905218B4C91007C4606 /* TypingIndicatorInteraction.swift in Sources */,
2400888E239F30A600305217 /* SessionRestorationView.swift in Sources */,
C3D6974A2564DEDC004AF766 /* MessageReceiverDelegate.swift in Sources */,
B886B4A72398B23E00211ABE /* QRCodeVC.swift in Sources */,
34EA69402194933900702471 /* MediaDownloadView.swift in Sources */,
B8544E3323D50E4900299F14 /* AppearanceUtilities.swift in Sources */,
4C586926224FAB83003FD070 /* AVAudioSession+OWS.m in Sources */,
3496744D2076768700080B5F /* OWSMessageBubbleView.m in Sources */,
C3550A1D255DD73500194B6A /* Storage+SessionMessagingKit.swift in Sources */,
C331FFF42558FF0300070591 /* PNOptionView.swift in Sources */,
4C4AE6A1224AF35700D4AF6F /* SendMediaNavigationController.swift in Sources */,
45F32C222057297A00A300D5 /* MediaDetailViewController.m in Sources */,
@ -5360,7 +5330,6 @@
B84664F5235022F30083A1CD /* MentionUtilities.swift in Sources */,
34D1F0C01F8EC1760066283D /* MessageRecipientStatusUtils.swift in Sources */,
34277A5E20751BDC006049F2 /* OWSQuotedMessageView.m in Sources */,
C3F0A5FE255C988A007BE2A3 /* Storage+Shared.swift in Sources */,
B82B4090239DD75000A248E7 /* RestoreVC.swift in Sources */,
3488F9362191CC4000E524CC /* ConversationMediaView.swift in Sources */,
45F32C242057297A00A300D5 /* MessageDetailViewController.swift in Sources */,
@ -5391,7 +5360,6 @@
340FC8AC204DAC8D007AEB0F /* PrivacySettingsTableViewController.m in Sources */,
B88847BC23E10BC6009836D2 /* GroupMembersVC.swift in Sources */,
B85357BF23A1AE0800AAF6CD /* SeedReminderView.swift in Sources */,
C31F812625258FB000DD9FD9 /* Storage+VolumeSamples.swift in Sources */,
C35E8AAE2485E51D00ACB629 /* IP2Country.swift in Sources */,
340FC8AE204DAC8D007AEB0F /* OWSSoundSettingsViewController.m in Sources */,
340FC8B0204DAC8D007AEB0F /* AddToBlockListViewController.m in Sources */,

View File

@ -0,0 +1,86 @@
extension Storage {
// MARK: - Ratchets
private static func getClosedGroupRatchetCollection(_ collection: ClosedGroupRatchetCollectionType, for groupPublicKey: String) -> String {
switch collection {
case .old: return "LokiOldClosedGroupRatchetCollection.\(groupPublicKey)"
case .current: return "LokiClosedGroupRatchetCollection.\(groupPublicKey)"
}
}
public func getClosedGroupRatchet(for groupPublicKey: String, senderPublicKey: String, from collection: ClosedGroupRatchetCollectionType = .current) -> ClosedGroupRatchet? {
let collection = Storage.getClosedGroupRatchetCollection(collection, for: groupPublicKey)
var result: ClosedGroupRatchet?
Storage.read { transaction in
result = transaction.object(forKey: senderPublicKey, inCollection: collection) as? ClosedGroupRatchet
}
return result
}
public func setClosedGroupRatchet(for groupPublicKey: String, senderPublicKey: String, ratchet: ClosedGroupRatchet, in collection: ClosedGroupRatchetCollectionType = .current, using transaction: Any) {
let collection = Storage.getClosedGroupRatchetCollection(collection, for: groupPublicKey)
(transaction as! YapDatabaseReadWriteTransaction).setObject(ratchet, forKey: senderPublicKey, inCollection: collection)
}
public func getAllClosedGroupRatchets(for groupPublicKey: String, from collection: ClosedGroupRatchetCollectionType = .current) -> [(senderPublicKey: String, ratchet: ClosedGroupRatchet)] {
let collection = Storage.getClosedGroupRatchetCollection(collection, for: groupPublicKey)
var result: [(senderPublicKey: String, ratchet: ClosedGroupRatchet)] = []
Storage.read { transaction in
transaction.enumerateRows(inCollection: collection) { key, object, _, _ in
guard let ratchet = object as? ClosedGroupRatchet else { return }
let senderPublicKey = key
result.append((senderPublicKey: senderPublicKey, ratchet: ratchet))
}
}
return result
}
public func removeAllClosedGroupRatchets(for groupPublicKey: String, from collection: ClosedGroupRatchetCollectionType = .current, using transaction: Any) {
let collection = Storage.getClosedGroupRatchetCollection(collection, for: groupPublicKey)
(transaction as! YapDatabaseReadWriteTransaction).removeAllObjects(inCollection: collection)
}
// MARK: - Private Keys
private static let closedGroupPrivateKeyCollection = "LokiClosedGroupPrivateKeyCollection"
public func getClosedGroupPrivateKey(for publicKey: String) -> String? {
var result: String?
Storage.read { transaction in
result = transaction.object(forKey: publicKey, inCollection: Storage.closedGroupPrivateKeyCollection) as? String
}
return result
}
public func setClosedGroupPrivateKey(_ privateKey: String, for publicKey: String, using transaction: Any) {
(transaction as! YapDatabaseReadWriteTransaction).setObject(privateKey, forKey: publicKey, inCollection: Storage.closedGroupPrivateKeyCollection)
}
public func removeClosedGroupPrivateKey(for publicKey: String, using transaction: Any) {
(transaction as! YapDatabaseReadWriteTransaction).removeObject(forKey: publicKey, inCollection: Storage.closedGroupPrivateKeyCollection)
}
// MARK: - Convenience
public func getAllClosedGroupSenderKeys(for groupPublicKey: String, from collection: ClosedGroupRatchetCollectionType = .current) -> Set<ClosedGroupSenderKey> {
return Set(getAllClosedGroupRatchets(for: groupPublicKey, from: collection).map { senderPublicKey, ratchet in
ClosedGroupSenderKey(chainKey: Data(hex: ratchet.chainKey), keyIndex: ratchet.keyIndex, publicKey: Data(hex: senderPublicKey))
})
}
public func getUserClosedGroupPublicKeys() -> Set<String> {
var result: Set<String> = []
Storage.read { transaction in
result = Set(transaction.allKeys(inCollection: Storage.closedGroupPrivateKeyCollection))
}
return result
}
public func isClosedGroup(_ publicKey: String) -> Bool {
getUserClosedGroupPublicKeys().contains(publicKey)
}
}

View File

@ -0,0 +1,26 @@
extension Storage {
public func persist(_ job: Job, using transaction: Any) {
(transaction as! YapDatabaseReadWriteTransaction).setObject(job, forKey: job.id!, inCollection: type(of: job).collection)
}
public func markJobAsSucceeded(_ job: Job, using transaction: Any) {
(transaction as! YapDatabaseReadWriteTransaction).removeObject(forKey: job.id!, inCollection: type(of: job).collection)
}
public func markJobAsFailed(_ job: Job, using transaction: Any) {
(transaction as! YapDatabaseReadWriteTransaction).removeObject(forKey: job.id!, inCollection: type(of: job).collection)
}
public func getAllPendingJobs(of type: Job.Type) -> [Job] {
var result: [Job] = []
Storage.read { transaction in
transaction.enumerateRows(inCollection: type.collection) { _, object, _, _ in
guard let job = object as? Job else { return }
result.append(job)
}
}
return result
}
}

View File

@ -0,0 +1,35 @@
import PromiseKit
extension Storage {
public func getOrGenerateRegistrationID(using transaction: Any) -> UInt32 {
SSKEnvironment.shared.tsAccountManager.getOrGenerateRegistrationId(transaction as! YapDatabaseReadWriteTransaction)
}
public func getSenderCertificate(for publicKey: String) -> SMKSenderCertificate {
let (promise, seal) = Promise<SMKSenderCertificate>.pending()
SSKEnvironment.shared.udManager.ensureSenderCertificate { senderCertificate in
seal.fulfill(senderCertificate)
} failure: { error in
// Should never fail
}
return try! promise.wait()
}
/// Returns the ID of the thread the message was stored under along with the `TSIncomingMessage` that was constructed.
public func persist(_ message: VisibleMessage, groupPublicKey: String?, using transaction: Any) -> (String, Any)? {
let transaction = transaction as! YapDatabaseReadWriteTransaction
var threadOrNil: TSThread?
if let groupPublicKey = groupPublicKey {
guard Storage.shared.isClosedGroup(groupPublicKey) else { return nil }
let groupID = LKGroupUtilities.getEncodedClosedGroupIDAsData(groupPublicKey)
threadOrNil = TSGroupThread.fetch(uniqueId: TSGroupThread.threadId(fromGroupId: groupID), transaction: transaction)
} else {
threadOrNil = TSContactThread.getOrCreateThread(withContactId: message.sender!, transaction: transaction)
}
guard let thread = threadOrNil else { return nil }
let message = TSIncomingMessage.from(message, associatedWith: thread, using: transaction)
message.save(with: transaction)
return (thread.uniqueId!, message)
}
}

View File

@ -1,31 +1,12 @@
public extension Storage {
// MARK: Onion Request Paths
internal static let onionRequestPathCollection = "LokiOnionRequestPathCollection"
extension Storage {
internal static func setOnionRequestPaths(_ paths: [OnionRequestAPI.Path], using transaction: YapDatabaseReadWriteTransaction) {
let collection = onionRequestPathCollection
// FIXME: This approach assumes either 1 or 2 paths of length 3 each. We should do better than this.
clearOnionRequestPaths(using: transaction)
guard paths.count >= 1 else { return }
let path0 = paths[0]
guard path0.count == 3 else { return }
transaction.setObject(path0[0], forKey: "0-0", inCollection: collection)
transaction.setObject(path0[1], forKey: "0-1", inCollection: collection)
transaction.setObject(path0[2], forKey: "0-2", inCollection: collection)
guard paths.count >= 2 else { return }
let path1 = paths[1]
guard path1.count == 3 else { return }
transaction.setObject(path1[0], forKey: "1-0", inCollection: collection)
transaction.setObject(path1[1], forKey: "1-1", inCollection: collection)
transaction.setObject(path1[2], forKey: "1-2", inCollection: collection)
}
private static let onionRequestPathCollection = "LokiOnionRequestPathCollection"
public static func getOnionRequestPaths() -> [OnionRequestAPI.Path] {
let collection = onionRequestPathCollection
public func getOnionRequestPaths() -> [OnionRequestAPI.Path] {
let collection = Storage.onionRequestPathCollection
var result: [OnionRequestAPI.Path] = []
read { transaction in
Storage.read { transaction in
if
let path0Snode0 = transaction.object(forKey: "0-0", inCollection: collection) as? Snode,
let path0Snode1 = transaction.object(forKey: "0-1", inCollection: collection) as? Snode,
@ -42,7 +23,26 @@ public extension Storage {
return result
}
internal static func clearOnionRequestPaths(using transaction: YapDatabaseReadWriteTransaction) {
transaction.removeAllObjects(inCollection: onionRequestPathCollection)
public func setOnionRequestPaths(to paths: [OnionRequestAPI.Path], using transaction: Any) {
let collection = Storage.onionRequestPathCollection
// FIXME: This approach assumes either 1 or 2 paths of length 3 each. We should do better than this.
clearOnionRequestPaths(using: transaction)
guard let transaction = transaction as? YapDatabaseReadWriteTransaction else { return }
guard paths.count >= 1 else { return }
let path0 = paths[0]
guard path0.count == 3 else { return }
transaction.setObject(path0[0], forKey: "0-0", inCollection: collection)
transaction.setObject(path0[1], forKey: "0-1", inCollection: collection)
transaction.setObject(path0[2], forKey: "0-2", inCollection: collection)
guard paths.count >= 2 else { return }
let path1 = paths[1]
guard path1.count == 3 else { return }
transaction.setObject(path1[0], forKey: "1-0", inCollection: collection)
transaction.setObject(path1[1], forKey: "1-1", inCollection: collection)
transaction.setObject(path1[2], forKey: "1-2", inCollection: collection)
}
func clearOnionRequestPaths(using transaction: Any) {
(transaction as! YapDatabaseReadWriteTransaction).removeAllObjects(inCollection: Storage.onionRequestPathCollection)
}
}

View File

@ -1,87 +1,62 @@
import Foundation
import PromiseKit
extension Storage : SessionMessagingKitStorageProtocol {
// MARK: - Signal Protocol
extension Storage {
public func getOrGenerateRegistrationID(using transaction: Any) -> UInt32 {
SSKEnvironment.shared.tsAccountManager.getOrGenerateRegistrationId(transaction as! YapDatabaseReadWriteTransaction)
}
public func getSenderCertificate(for publicKey: String) -> SMKSenderCertificate {
let (promise, seal) = Promise<SMKSenderCertificate>.pending()
SSKEnvironment.shared.udManager.ensureSenderCertificate { senderCertificate in
seal.fulfill(senderCertificate)
} failure: { error in
// Should never fail
}
return try! promise.wait()
}
// MARK: - Shared Sender Keys
private static let closedGroupPrivateKeyCollection = "LokiClosedGroupPrivateKeyCollection"
public func getClosedGroupPrivateKey(for publicKey: String) -> String? {
var result: String?
// MARK: - Open Groups
private static let openGroupCollection = "LokiPublicChatCollection"
@objc public func getAllUserOpenGroups() -> [String:OpenGroup] {
var result = [String:OpenGroup]()
Storage.read { transaction in
result = transaction.object(forKey: publicKey, inCollection: Storage.closedGroupPrivateKeyCollection) as? String
}
return result
}
internal static func setClosedGroupPrivateKey(_ privateKey: String, for publicKey: String, using transaction: Any) {
(transaction as! YapDatabaseReadWriteTransaction).setObject(privateKey, forKey: publicKey, inCollection: Storage.closedGroupPrivateKeyCollection)
}
internal static func removeClosedGroupPrivateKey(for publicKey: String, using transaction: Any) {
(transaction as! YapDatabaseReadWriteTransaction).removeObject(forKey: publicKey, inCollection: Storage.closedGroupPrivateKeyCollection)
}
func getUserClosedGroupPublicKeys() -> Set<String> {
var result: Set<String> = []
Storage.read { transaction in
result = Set(transaction.allKeys(inCollection: Storage.closedGroupPrivateKeyCollection))
}
return result
}
public func isClosedGroup(_ publicKey: String) -> Bool {
getUserClosedGroupPublicKeys().contains(publicKey)
}
// MARK: - Jobs
public func persist(_ job: Job, using transaction: Any) {
(transaction as! YapDatabaseReadWriteTransaction).setObject(job, forKey: job.id!, inCollection: type(of: job).collection)
}
public func markJobAsSucceeded(_ job: Job, using transaction: Any) {
(transaction as! YapDatabaseReadWriteTransaction).removeObject(forKey: job.id!, inCollection: type(of: job).collection)
}
public func markJobAsFailed(_ job: Job, using transaction: Any) {
(transaction as! YapDatabaseReadWriteTransaction).removeObject(forKey: job.id!, inCollection: type(of: job).collection)
}
public func getAllPendingJobs(of type: Job.Type) -> [Job] {
var result: [Job] = []
Storage.read { transaction in
transaction.enumerateRows(inCollection: type.collection) { _, object, _, _ in
guard let job = object as? Job else { return }
result.append(job)
transaction.enumerateKeysAndObjects(inCollection: Storage.openGroupCollection) { threadID, object, _ in
guard let openGroup = object as? OpenGroup else { return }
result[threadID] = openGroup
}
}
return result
}
@objc(getOpenGroupForThreadID:)
public func getOpenGroup(for threadID: String) -> OpenGroup? {
var result: OpenGroup?
Storage.read { transaction in
result = transaction.object(forKey: threadID, inCollection: Storage.openGroupCollection) as? OpenGroup
}
return result
}
@objc(setOpenGroup:forThreadWithID:using:)
public func setOpenGroup(_ openGroup: OpenGroup, for threadID: String, using transaction: Any) {
(transaction as! YapDatabaseReadWriteTransaction).setObject(openGroup, forKey: threadID, inCollection: Storage.openGroupCollection)
}
@objc(removeOpenGroupForThreadID:using:)
public func removeOpenGroup(for threadID: String, using transaction: Any) {
(transaction as! YapDatabaseReadWriteTransaction).removeObject(forKey: threadID, inCollection: Storage.openGroupCollection)
}
// MARK: - Quotes
@objc(getServerIDForQuoteWithID:quoteeHexEncodedPublicKey:threadID:transaction:)
public func getServerID(quoteID: UInt64, quoteeHexEncodedPublicKey: String, threadID: String, transaction: YapDatabaseReadTransaction) -> UInt64 {
guard let message = TSInteraction.interactions(withTimestamp: quoteID, filter: { interaction in
let senderPublicKey: String
if let message = interaction as? TSIncomingMessage {
senderPublicKey = message.authorId
} else if interaction is TSOutgoingMessage {
senderPublicKey = getUserHexEncodedPublicKey()
} else {
return false
}
return (senderPublicKey == quoteeHexEncodedPublicKey) && (interaction.uniqueThreadId == threadID)
}, with: transaction).first as! TSMessage? else { return 0 }
return message.openGroupServerMessageID
}
// MARK: - Authorization
private static func getAuthTokenCollection(for server: String) -> String {
@ -109,7 +84,7 @@ extension Storage : SessionMessagingKitStorageProtocol {
// MARK: - Open Group Public Keys
// MARK: - Public Keys
private static let openGroupPublicKeyCollection = "LokiOpenGroupPublicKeyCollection"
@ -124,12 +99,26 @@ extension Storage : SessionMessagingKitStorageProtocol {
public func setOpenGroupPublicKey(for server: String, to newValue: String, using transaction: Any) {
(transaction as! YapDatabaseReadWriteTransaction).setObject(newValue, forKey: server, inCollection: Storage.openGroupPublicKeyCollection)
}
public func removeOpenGroupPublicKey(for server: String, using transaction: Any) {
(transaction as! YapDatabaseReadWriteTransaction).removeObject(forKey: server, inCollection: Storage.openGroupPublicKeyCollection)
}
// MARK: - Deletion
public func clearAllData(for group: UInt64, on server: String, using transaction: Any) {
removeLastMessageServerID(for: group, on: server, using: transaction)
removeLastDeletionServerID(for: group, on: server, using: transaction)
removeOpenGroupPublicKey(for: server, using: transaction)
}
// MARK: - Last Message Server ID
private static let lastMessageServerIDCollection = "LokiGroupChatLastMessageServerIDCollection"
public static let lastMessageServerIDCollection = "LokiGroupChatLastMessageServerIDCollection"
public func getLastMessageServerID(for group: UInt64, on server: String) -> UInt64? {
var result: UInt64? = nil
@ -151,7 +140,7 @@ extension Storage : SessionMessagingKitStorageProtocol {
// MARK: - Last Deletion Server ID
private static let lastDeletionServerIDCollection = "LokiGroupChatLastDeletionServerIDCollection"
public static let lastDeletionServerIDCollection = "LokiGroupChatLastDeletionServerIDCollection"
public func getLastDeletionServerID(for group: UInt64, on server: String) -> UInt64? {
var result: UInt64? = nil
@ -171,11 +160,20 @@ extension Storage : SessionMessagingKitStorageProtocol {
// MARK: - Open Group Metadata
// MARK: - Metadata
private static let openGroupUserCountCollection = "LokiPublicChatUserCountCollection"
private static let openGroupMessageIDCollection = "LKMessageIDCollection"
private static let openGroupProfilePictureURLCollection = "LokiPublicChatAvatarURLCollection"
public func getUserCount(forOpenGroupWithID openGroupID: String) -> Int? {
var result: Int?
Storage.read { transaction in
result = transaction.object(forKey: openGroupID, inCollection: Storage.openGroupUserCountCollection) as? Int
}
return result
}
public func setUserCount(to newValue: Int, forOpenGroupWithID openGroupID: String, using transaction: Any) {
(transaction as! YapDatabaseReadWriteTransaction).setObject(newValue, forKey: openGroupID, inCollection: Storage.openGroupUserCountCollection)
}
@ -196,25 +194,16 @@ extension Storage : SessionMessagingKitStorageProtocol {
public func setLastProfilePictureUploadDate(_ date: Date) {
UserDefaults.standard[.lastProfilePictureUpload] = date
}
// MARK: - Message Handling
/// Returns the ID of the thread the message was stored under along with the `TSIncomingMessage` that was constructed.
public func persist(_ message: VisibleMessage, groupPublicKey: String?, using transaction: Any) -> (String, Any)? {
let transaction = transaction as! YapDatabaseReadWriteTransaction
var threadOrNil: TSThread?
if let groupPublicKey = groupPublicKey {
guard Storage.shared.isClosedGroup(groupPublicKey) else { return nil }
let groupID = LKGroupUtilities.getEncodedClosedGroupIDAsData(groupPublicKey)
threadOrNil = TSGroupThread.fetch(uniqueId: TSGroupThread.threadId(fromGroupId: groupID), transaction: transaction)
} else {
threadOrNil = TSContactThread.getOrCreateThread(withContactId: message.sender!, transaction: transaction)
public func getProfilePictureURL(forOpenGroupWithID openGroupID: String) -> String? {
var result: String?
Storage.read { transaction in
result = transaction.object(forKey: openGroupID, inCollection: Storage.openGroupProfilePictureURLCollection) as? String
}
guard let thread = threadOrNil else { return nil }
let message = TSIncomingMessage.from(message, associatedWith: thread, using: transaction)
message.save(with: transaction)
return (thread.uniqueId!, message)
return result
}
public func setProfilePictureURL(to profilePictureURL: String?, forOpenGroupWithID openGroupID: String, using transaction: Any) {
(transaction as! YapDatabaseReadWriteTransaction).setObject(profilePictureURL, forKey: openGroupID, inCollection: Storage.openGroupProfilePictureURLCollection)
}
}

View File

@ -0,0 +1,30 @@
extension Storage {
private static let sessionRequestSentTimestampCollection = "LokiSessionRequestSentTimestampCollection"
private static let sessionRequestProcessedTimestampCollection = "LokiSessionRequestProcessedTimestampCollection"
public func getSessionRequestSentTimestamp(for publicKey: String) -> UInt64 {
var result: UInt64?
Storage.read { transaction in
result = transaction.object(forKey: publicKey, inCollection: Storage.sessionRequestSentTimestampCollection) as? UInt64
}
return result ?? 0
}
public func setSessionRequestSentTimestamp(for publicKey: String, to timestamp: UInt64, using transaction: Any) {
(transaction as! YapDatabaseReadWriteTransaction).setObject(timestamp, forKey: publicKey, inCollection: Storage.sessionRequestSentTimestampCollection)
}
public func getSessionRequestProcessedTimestamp(for publicKey: String) -> UInt64 {
var result: UInt64?
Storage.read { transaction in
result = transaction.object(forKey: publicKey, inCollection: Storage.sessionRequestProcessedTimestampCollection) as? UInt64
}
return result ?? 0
}
public func setSessionRequestProcessedTimestamp(for publicKey: String, to timestamp: UInt64, using transaction: Any) {
(transaction as! YapDatabaseReadWriteTransaction).setObject(timestamp, forKey: publicKey, inCollection: Storage.sessionRequestProcessedTimestampCollection)
}
}

View File

@ -1,8 +1,6 @@
extension Storage {
public static let shared = Storage()
public func with(_ work: @escaping (Any) -> Void) {
Storage.writeSync { work($0) }
}
@ -19,5 +17,7 @@ extension Storage {
return OWSIdentityManager.shared().identityKeyPair()
}
public func getUserDisplayName() -> String? { fatalError() }
public func getUserDisplayName() -> String? {
return SSKEnvironment.shared.profileManager.localProfileName()
}
}

View File

@ -1,56 +1,9 @@
extension Storage : SessionSnodeKitStorageProtocol {
// MARK: - Onion Request Paths
private static let onionRequestPathCollection = "LokiOnionRequestPathCollection"
public func getOnionRequestPaths() -> [OnionRequestAPI.Path] {
let collection = Storage.onionRequestPathCollection
var result: [OnionRequestAPI.Path] = []
Storage.read { transaction in
if
let path0Snode0 = transaction.object(forKey: "0-0", inCollection: collection) as? Snode,
let path0Snode1 = transaction.object(forKey: "0-1", inCollection: collection) as? Snode,
let path0Snode2 = transaction.object(forKey: "0-2", inCollection: collection) as? Snode {
result.append([ path0Snode0, path0Snode1, path0Snode2 ])
if
let path1Snode0 = transaction.object(forKey: "1-0", inCollection: collection) as? Snode,
let path1Snode1 = transaction.object(forKey: "1-1", inCollection: collection) as? Snode,
let path1Snode2 = transaction.object(forKey: "1-2", inCollection: collection) as? Snode {
result.append([ path1Snode0, path1Snode1, path1Snode2 ])
}
}
}
return result
}
public func setOnionRequestPaths(to paths: [OnionRequestAPI.Path], using transaction: Any) {
let collection = Storage.onionRequestPathCollection
// FIXME: This approach assumes either 1 or 2 paths of length 3 each. We should do better than this.
clearOnionRequestPaths(using: transaction)
guard let transaction = transaction as? YapDatabaseReadWriteTransaction else { return }
guard paths.count >= 1 else { return }
let path0 = paths[0]
guard path0.count == 3 else { return }
transaction.setObject(path0[0], forKey: "0-0", inCollection: collection)
transaction.setObject(path0[1], forKey: "0-1", inCollection: collection)
transaction.setObject(path0[2], forKey: "0-2", inCollection: collection)
guard paths.count >= 2 else { return }
let path1 = paths[1]
guard path1.count == 3 else { return }
transaction.setObject(path1[0], forKey: "1-0", inCollection: collection)
transaction.setObject(path1[1], forKey: "1-1", inCollection: collection)
transaction.setObject(path1[2], forKey: "1-2", inCollection: collection)
}
func clearOnionRequestPaths(using transaction: Any) {
(transaction as! YapDatabaseReadWriteTransaction).removeAllObjects(inCollection: Storage.onionRequestPathCollection)
}
extension Storage {
// MARK: - Snode Pool
private static let snodePoolCollection = "LokiSnodePoolCollection"
public func getSnodePool() -> Set<Snode> {
var result: Set<Snode> = []
@ -70,13 +23,17 @@ extension Storage : SessionSnodeKitStorageProtocol {
}
}
func clearSnodePool(in transaction: Any) {
public func clearSnodePool(in transaction: Any) {
(transaction as! YapDatabaseReadWriteTransaction).removeAllObjects(inCollection: Storage.snodePoolCollection)
}
// MARK: - Swarm
private static func getSwarmCollection(for publicKey: String) -> String {
return "LokiSwarmCollection-\(publicKey)"
}
public func getSwarm(for publicKey: String) -> Set<Snode> {
var result: Set<Snode> = []
@ -98,7 +55,7 @@ extension Storage : SessionSnodeKitStorageProtocol {
}
}
func clearSwarm(for publicKey: String, in transaction: Any) {
public func clearSwarm(for publicKey: String, in transaction: Any) {
let collection = Storage.getSwarmCollection(for: publicKey)
(transaction as! YapDatabaseReadWriteTransaction).removeAllObjects(inCollection: collection)
}
@ -109,7 +66,7 @@ extension Storage : SessionSnodeKitStorageProtocol {
private static let lastMessageHashCollection = "LokiLastMessageHashCollection"
func getLastMessageHashInfo(for snode: Snode, associatedWith publicKey: String) -> JSON? {
public func getLastMessageHashInfo(for snode: Snode, associatedWith publicKey: String) -> JSON? {
let key = "\(snode.address):\(snode.port).\(publicKey)"
var result: JSON?
Storage.read { transaction in
@ -141,7 +98,7 @@ extension Storage : SessionSnodeKitStorageProtocol {
}
}
func removeLastMessageHashInfo(for snode: Snode, associatedWith publicKey: String, using transaction: Any) {
public func removeLastMessageHashInfo(for snode: Snode, associatedWith publicKey: String, using transaction: Any) {
let key = "\(snode.address):\(snode.port).\(publicKey)"
(transaction as! YapDatabaseReadWriteTransaction).removeObject(forKey: key, inCollection: Storage.lastMessageHashCollection)
}

View File

@ -0,0 +1,17 @@
extension Storage {
private static let volumeSamplesCollection = "LokiVolumeSamplesCollection"
public func getVolumeSamples(for attachment: String) -> [Float]? {
var result: [Float]?
Storage.read { transaction in
result = transaction.object(forKey: attachment, inCollection: Storage.volumeSamplesCollection) as? [Float]
}
return result
}
public func setVolumeSamples(for attachment: String, to volumeSamples: [Float], using transaction: Any) {
(transaction as! YapDatabaseReadWriteTransaction).setObject(volumeSamples, forKey: attachment, inCollection: Storage.volumeSamplesCollection)
}
}

View File

@ -6,10 +6,12 @@ import PromiseKit
// Executing a write transaction from within a write transaction is NOT allowed.
@objc(LKStorage)
public final class Storage : NSObject {
public final class Storage : NSObject, SessionMessagingKitStorageProtocol, SessionProtocolKitStorageProtocol, SessionSnodeKitStorageProtocol {
public static let serialQueue = DispatchQueue(label: "Storage.serialQueue", qos: .userInitiated)
private static var owsStorage: OWSPrimaryStorage { OWSPrimaryStorage.shared() }
@objc public static let shared = Storage()
// MARK: Reading
@ -20,13 +22,6 @@ public final class Storage : NSObject {
@objc(readWithBlock:)
public static func read(with block: @escaping (YapDatabaseReadTransaction) -> Void) {
// FIXME: For some reason the code below appears to be causing crashes even though it *should*
// be in line with the YapDatabase docs
/*
let isMainThread = Thread.current.isMainThread
let connection = isMainThread ? owsStorage.uiDatabaseConnection : owsStorage.dbReadConnection
connection.read(block)
*/
owsStorage.dbReadConnection.read(block)
}
@ -68,7 +63,6 @@ public final class Storage : NSObject {
}
/// Blocks the calling thread until the write has finished.
@discardableResult
@objc(writeSyncWithBlock:)
public static func writeSync(with block: @escaping (YapDatabaseReadWriteTransaction) -> Void) {
try! write(with: block, completion: { }).wait() // The promise returned by write(with:completion:) never rejects

View File

@ -45,7 +45,7 @@ public final class ClosedGroupPoller : NSObject {
// MARK: Private API
private func poll() -> [Promise<Void>] {
guard isPolling else { return [] }
let publicKeys = Storage.getUserClosedGroupPublicKeys()
let publicKeys = Storage.shared.getUserClosedGroupPublicKeys()
return publicKeys.map { publicKey in
let promise = SnodeAPI.getSwarm(for: publicKey).then2 { [weak self] swarm -> Promise<[JSON]> in
// randomElement() uses the system's default random generator, which is cryptographically secure

View File

@ -9,10 +9,7 @@ public extension Message.Destination {
let groupPublicKey = LKGroupUtilities.getDecodedGroupID(groupID)
return .closedGroup(groupPublicKey: groupPublicKey)
} else if let thread = thread as? TSGroupThread, thread.isOpenGroup {
var openGroup: OpenGroup!
Storage.read { transaction in
openGroup = LokiDatabaseUtilities.getPublicChat(for: thread.uniqueId!, in: transaction)
}
let openGroup = Storage.shared.getOpenGroup(for: thread.uniqueId!)!
return .openGroup(channel: openGroup.channel, server: openGroup.server)
} else {
preconditionFailure("TODO: Handle legacy closed groups.")

View File

@ -30,19 +30,16 @@ public final class MentionsManager : NSObject {
guard let cache = userPublicKeyCache[threadID] else { return [] }
var candidates: [Mention] = []
// Gather candidates
var publicChat: OpenGroup?
storage.dbReadConnection.read { transaction in
publicChat = LokiDatabaseUtilities.getPublicChat(for: threadID, in: transaction)
}
let openGroup = Storage.shared.getOpenGroup(for: threadID)
storage.dbReadConnection.read { transaction in
candidates = cache.compactMap { publicKey in
let uncheckedDisplayName: String?
if let publicChat = publicChat {
uncheckedDisplayName = UserDisplayNameUtilities.getPublicChatDisplayName(for: publicKey, in: publicChat.channel, on: publicChat.server)
let displayNameOrNil: String?
if let openGroup = openGroup {
displayNameOrNil = UserDisplayNameUtilities.getPublicChatDisplayName(for: publicKey, in: openGroup.channel, on: openGroup.server)
} else {
uncheckedDisplayName = UserDisplayNameUtilities.getPrivateChatDisplayName(for: publicKey)
displayNameOrNil = UserDisplayNameUtilities.getPrivateChatDisplayName(for: publicKey)
}
guard let displayName = uncheckedDisplayName else { return nil }
guard let displayName = displayNameOrNil else { return nil }
guard !displayName.hasPrefix("Anonymous") else { return nil }
return Mention(publicKey: publicKey, displayName: displayName)
}

View File

@ -1,8 +1,8 @@
import SessionMessagingKit
final class MessageReceiverDelegate : SessionMessagingKit.MessageReceiverDelegate {
public final class MessageReceiverDelegate : SessionMessagingKit.MessageReceiverDelegate {
static let shared = MessageReceiverDelegate()
public static let shared = MessageReceiverDelegate()
@ -156,7 +156,7 @@ final class MessageReceiverDelegate : SessionMessagingKit.MessageReceiverDelegat
// MARK: - Closed Groups
func handleNewGroup(_ message: ClosedGroupUpdate, using transaction: Any) {
public func handleNewGroup(_ message: ClosedGroupUpdate, using transaction: Any) {
guard case let .new(groupPublicKeyAsData, name, groupPrivateKey, senderKeys, membersAsData, adminsAsData) = message.kind else { return }
let transaction = transaction as! YapDatabaseReadWriteTransaction
let groupPublicKey = groupPublicKeyAsData.toHexString()
@ -185,7 +185,7 @@ final class MessageReceiverDelegate : SessionMessagingKit.MessageReceiverDelegat
}
}
missingSenderKeys.subtracting([ userPublicKey ]).forEach { publicKey in
(UIApplication.shared.delegate as! AppDelegate).requestSenderKey(for: groupPublicKey, senderPublicKey: publicKey, using: transaction)
MessageSenderDelegate.shared.requestSenderKey(for: groupPublicKey, senderPublicKey: publicKey, using: transaction)
}
// Create the group
let groupID = LKGroupUtilities.getEncodedClosedGroupIDAsData(groupPublicKey)
@ -200,15 +200,15 @@ final class MessageReceiverDelegate : SessionMessagingKit.MessageReceiverDelegat
thread.save(with: transaction)
}
// Add the group to the user's set of public keys to poll for
Storage.setClosedGroupPrivateKey(groupPrivateKey.toHexString(), for: groupPublicKey, using: transaction)
Storage.shared.setClosedGroupPrivateKey(groupPrivateKey.toHexString(), for: groupPublicKey, using: transaction)
// Notify the PN server
let _ = LokiPushNotificationManager.performOperation(.subscribe, for: groupPublicKey, publicKey: getUserHexEncodedPublicKey())
let _ = PushNotificationManager.performOperation(.subscribe, for: groupPublicKey, publicKey: getUserHexEncodedPublicKey())
// Notify the user
let infoMessage = TSInfoMessage(timestamp: NSDate.ows_millisecondTimeStamp(), in: thread, messageType: .typeGroupUpdate)
infoMessage.save(with: transaction)
}
func handleGroupUpdate(_ message: ClosedGroupUpdate, using transaction: Any) {
public func handleGroupUpdate(_ message: ClosedGroupUpdate, using transaction: Any) {
guard case let .info(groupPublicKeyAsData, name, senderKeys, membersAsData, adminsAsData) = message.kind else { return }
let transaction = transaction as! YapDatabaseReadWriteTransaction
let groupPublicKey = groupPublicKeyAsData.toHexString()
@ -236,16 +236,16 @@ final class MessageReceiverDelegate : SessionMessagingKit.MessageReceiverDelegat
let userPublicKey = getUserHexEncodedPublicKey()
let wasUserRemoved = !members.contains(userPublicKey)
if Set(members).intersection(oldMembers) != Set(oldMembers) {
let allOldRatchets = Storage.getAllClosedGroupRatchets(for: groupPublicKey)
let allOldRatchets = Storage.shared.getAllClosedGroupRatchets(for: groupPublicKey)
for (senderPublicKey, oldRatchet) in allOldRatchets {
let collection = ClosedGroupRatchetCollectionType.old
Storage.shared.setClosedGroupRatchet(for: groupPublicKey, senderPublicKey: senderPublicKey, ratchet: oldRatchet, in: collection, using: transaction)
}
Storage.removeAllClosedGroupRatchets(for: groupPublicKey, using: transaction)
Storage.shared.removeAllClosedGroupRatchets(for: groupPublicKey, using: transaction)
if wasUserRemoved {
Storage.removeClosedGroupPrivateKey(for: groupPublicKey, using: transaction)
Storage.shared.removeClosedGroupPrivateKey(for: groupPublicKey, using: transaction)
// Notify the PN server
let _ = LokiPushNotificationManager.performOperation(.unsubscribe, for: groupPublicKey, publicKey: userPublicKey)
let _ = PushNotificationManager.performOperation(.unsubscribe, for: groupPublicKey, publicKey: userPublicKey)
} else {
let userRatchet = SharedSenderKeys.generateRatchet(for: groupPublicKey, senderPublicKey: userPublicKey, using: transaction)
let userSenderKey = ClosedGroupSenderKey(chainKey: Data(hex: userRatchet.chainKey), keyIndex: userRatchet.keyIndex, publicKey: Data(hex: userPublicKey))
@ -272,7 +272,7 @@ final class MessageReceiverDelegate : SessionMessagingKit.MessageReceiverDelegat
}
}
func handleSenderKeyRequest(_ message: ClosedGroupUpdate, using transaction: Any) {
public func handleSenderKeyRequest(_ message: ClosedGroupUpdate, using transaction: Any) {
guard case let .senderKeyRequest(groupPublicKeyAsData) = message.kind else { return }
let transaction = transaction as! YapDatabaseReadWriteTransaction
let userPublicKey = getUserHexEncodedPublicKey()
@ -301,7 +301,7 @@ final class MessageReceiverDelegate : SessionMessagingKit.MessageReceiverDelegat
MessageSender.send(closedGroupUpdate, in: thread, using: transaction)
}
func handleSenderKey(_ message: ClosedGroupUpdate, using transaction: Any) {
public func handleSenderKey(_ message: ClosedGroupUpdate, using transaction: Any) {
guard case let .senderKey(groupPublicKeyAsData, senderKey) = message.kind else { return }
let groupPublicKey = groupPublicKeyAsData.toHexString()
guard senderKey.publicKey.toHexString() == message.sender! else {

View File

@ -0,0 +1,16 @@
public final class MessageSenderDelegate : SharedSenderKeysDelegate {
public static let shared = MessageSenderDelegate()
public func requestSenderKey(for groupPublicKey: String, senderPublicKey: String, using transaction: Any) {
print("[Loki] Requesting sender key for group public key: \(groupPublicKey), sender public key: \(senderPublicKey).")
let transaction = transaction as! YapDatabaseReadWriteTransaction
let thread = TSContactThread.getOrCreateThread(withContactId: senderPublicKey, transaction: transaction)
thread.save(with: transaction)
let closedGroupUpdateKind = ClosedGroupUpdate.Kind.senderKeyRequest(groupPublicKey: Data(hex: groupPublicKey))
let closedGroupUpdate = ClosedGroupUpdate()
closedGroupUpdate.kind = closedGroupUpdateKind
MessageSender.send(closedGroupUpdate, in: thread, using: transaction)
}
}

View File

@ -80,7 +80,7 @@ NS_ASSUME_NONNULL_BEGIN
}
id<OWSReadTracking> unread = (id<OWSReadTracking>)object;
if (unread.read) {
[LKLogger print:@"Found an already read message in the * unread * messages list."];
NSLog(@"Found an already read message in the * unread * messages list.");
return;
}
count += 1;

View File

@ -1,24 +1,25 @@
extension AppDelegate : OpenGroupAPIDelegate {
public final class OpenGroupAPIDelegate : SessionMessagingKit.OpenGroupAPIDelegate {
public static let shared = OpenGroupAPIDelegate()
public func updateProfileIfNeeded(for channel: UInt64, on server: String, from info: OpenGroupInfo) {
let storage = OWSPrimaryStorage.shared()
let publicChatID = "\(server).\(channel)"
let openGroupID = "\(server).\(channel)"
Storage.write { transaction in
// Update user count
storage.setUserCount(info.memberCount, forPublicChatWithID: publicChatID, in: transaction)
let groupThread = TSGroupThread.getOrCreateThread(withGroupId: publicChatID.data(using: .utf8)!, groupType: .openGroup, transaction: transaction)
Storage.shared.setUserCount(to: info.memberCount, forOpenGroupWithID: openGroupID, using: transaction)
let thread = TSGroupThread.getOrCreateThread(withGroupId: openGroupID.data(using: .utf8)!, groupType: .openGroup, transaction: transaction)
// Update display name if needed
let groupModel = groupThread.groupModel
if groupModel.groupName != info.displayName {
let newGroupModel = TSGroupModel(title: info.displayName, memberIds: groupModel.groupMemberIds, image: groupModel.groupImage, groupId: groupModel.groupId, groupType: groupModel.groupType, adminIds: groupModel.groupAdminIds)
groupThread.groupModel = newGroupModel
groupThread.save(with: transaction)
let model = thread.groupModel
if model.groupName != info.displayName {
let newGroupModel = TSGroupModel(title: info.displayName, memberIds: model.groupMemberIds, image: model.groupImage, groupId: model.groupId, groupType: model.groupType, adminIds: model.groupAdminIds)
thread.groupModel = newGroupModel
thread.save(with: transaction)
}
// Download and update profile picture if needed
let oldProfilePictureURL = storage.getProfilePictureURL(forPublicChatWithID: publicChatID, in: transaction)
if oldProfilePictureURL != info.profilePictureURL || groupModel.groupImage == nil {
storage.setProfilePictureURL(info.profilePictureURL, forPublicChatWithID: publicChatID, in: transaction)
let oldProfilePictureURL = Storage.shared.getProfilePictureURL(forOpenGroupWithID: openGroupID)
if oldProfilePictureURL != info.profilePictureURL || model.groupImage == nil {
Storage.shared.setProfilePictureURL(to: info.profilePictureURL, forOpenGroupWithID: openGroupID, using: transaction)
if let profilePictureURL = info.profilePictureURL {
var sanitizedServerURL = server
var sanitizedProfilePictureURL = profilePictureURL
@ -28,7 +29,7 @@ extension AppDelegate : OpenGroupAPIDelegate {
FileServerAPI.downloadAttachment(from: url).map2 { data in
let attachmentStream = TSAttachmentStream(contentType: OWSMimeTypeImageJpeg, byteCount: UInt32(data.count), sourceFilename: nil, caption: nil, albumMessageId: nil)
try attachmentStream.write(data)
groupThread.updateAvatar(with: attachmentStream)
thread.updateAvatar(with: attachmentStream)
}
}
}

View File

@ -1,8 +1,8 @@
import PromiseKit
@objc(LKPublicChatPoller)
public final class PublicChatPoller : NSObject {
private let publicChat: OpenGroup
@objc(LKOpenGroupPoller)
public final class OpenGroupPoller : NSObject {
private let openGroup: OpenGroup
private var pollForNewMessagesTimer: Timer? = nil
private var pollForDeletedMessagesTimer: Timer? = nil
private var pollForModeratorsTimer: Timer? = nil
@ -17,9 +17,9 @@ public final class PublicChatPoller : NSObject {
private let pollForDisplayNamesInterval: TimeInterval = 60
// MARK: Lifecycle
@objc(initForPublicChat:)
public init(for publicChat: OpenGroup) {
self.publicChat = publicChat
@objc(initForOpenGroup:)
public init(for openGroup: OpenGroup) {
self.openGroup = openGroup
super.init()
}
@ -57,24 +57,23 @@ public final class PublicChatPoller : NSObject {
public func pollForNewMessages() -> Promise<Void> {
guard !self.isPolling else { return Promise.value(()) }
self.isPolling = true
let publicChat = self.publicChat
let openGroup = self.openGroup
let userPublicKey = getUserHexEncodedPublicKey()
return OpenGroupAPI.getMessages(for: publicChat.channel, on: publicChat.server).done(on: DispatchQueue.global(qos: .default)) { messages in
return OpenGroupAPI.getMessages(for: openGroup.channel, on: openGroup.server).done(on: DispatchQueue.global(qos: .default)) { messages in
self.isPolling = false
let storage = OWSPrimaryStorage.shared()
// Sorting the messages by timestamp before importing them fixes an issue where messages that quote older messages can't find those older messages
messages.sorted { $0.serverTimestamp < $1.serverTimestamp }.forEach { message in
let senderPublicKey = message.senderPublicKey
var wasSentByCurrentUser = (senderPublicKey == getUserHexEncodedPublicKey())
let wasSentByCurrentUser = (senderPublicKey == getUserHexEncodedPublicKey())
func generateDisplayName(from rawDisplayName: String) -> String {
let endIndex = senderPublicKey.endIndex
let cutoffIndex = senderPublicKey.index(endIndex, offsetBy: -8)
return "\(rawDisplayName) (...\(senderPublicKey[cutoffIndex..<endIndex]))"
}
let senderDisplayName = UserDisplayNameUtilities.getPublicChatDisplayName(for: senderPublicKey, in: publicChat.channel, on: publicChat.server) ?? generateDisplayName(from: NSLocalizedString("Anonymous", comment: ""))
let id = LKGroupUtilities.getEncodedOpenGroupIDAsData(publicChat.id)
let senderDisplayName = UserDisplayNameUtilities.getPublicChatDisplayName(for: senderPublicKey, in: openGroup.channel, on: openGroup.server) ?? generateDisplayName(from: NSLocalizedString("Anonymous", comment: ""))
let id = LKGroupUtilities.getEncodedOpenGroupIDAsData(openGroup.id)
let groupContext = SNProtoGroupContext.builder(id: id, type: .deliver)
groupContext.setName(publicChat.displayName)
groupContext.setName(openGroup.displayName)
let dataMessage = SNProtoDataMessage.builder()
let attachments: [SNProtoAttachmentPointer] = message.attachments.compactMap { attachment in
guard attachment.kind == .attachment else { return nil }
@ -126,9 +125,9 @@ public final class PublicChatPoller : NSObject {
let body = (message.body == message.timestamp.description) ? "" : message.body // Workaround for the fact that the back-end doesn't accept messages without a body
dataMessage.setBody(body)
if let messageServerID = message.serverID {
let publicChatInfo = SNProtoPublicChatInfo.builder()
publicChatInfo.setServerID(messageServerID)
dataMessage.setPublicChatInfo(try! publicChatInfo.build())
let openGroupInfo = SNProtoPublicChatInfo.builder()
openGroupInfo.setServerID(messageServerID)
dataMessage.setPublicChatInfo(try! openGroupInfo.build())
}
let content = SNProtoContent.builder()
if !wasSentByCurrentUser {
@ -148,8 +147,8 @@ public final class PublicChatPoller : NSObject {
envelope.setSourceDevice(1)
envelope.setContent(try! content.build().serializedData())
envelope.setServerTimestamp(message.serverTimestamp)
Storage.writeSync { transaction in
transaction.setObject(senderDisplayName, forKey: senderPublicKey, inCollection: publicChat.id)
Storage.write { transaction in
transaction.setObject(senderDisplayName, forKey: senderPublicKey, inCollection: openGroup.id)
let messageServerID = message.serverID
let job = MessageReceiveJob(data: try! envelope.buildSerializedData(), messageServerID: messageServerID)
Storage.write { transaction in
@ -161,8 +160,8 @@ public final class PublicChatPoller : NSObject {
}
private func pollForDeletedMessages() {
let publicChat = self.publicChat
let _ = OpenGroupAPI.getDeletedMessageServerIDs(for: publicChat.channel, on: publicChat.server).done(on: DispatchQueue.global(qos: .default)) { deletedMessageServerIDs in
let openGroup = self.openGroup
let _ = OpenGroupAPI.getDeletedMessageServerIDs(for: openGroup.channel, on: openGroup.server).done(on: DispatchQueue.global(qos: .default)) { deletedMessageServerIDs in
Storage.writeSync { transaction in
let deletedMessageIDs = deletedMessageServerIDs.compactMap { OWSPrimaryStorage.shared().getIDForMessage(withServerID: UInt($0), in: transaction) }
deletedMessageIDs.forEach { messageID in
@ -173,10 +172,10 @@ public final class PublicChatPoller : NSObject {
}
private func pollForModerators() {
let _ = OpenGroupAPI.getModerators(for: publicChat.channel, on: publicChat.server)
let _ = OpenGroupAPI.getModerators(for: openGroup.channel, on: openGroup.server)
}
private func pollForDisplayNames() {
let _ = OpenGroupAPI.getDisplayNames(for: publicChat.channel, on: publicChat.server)
let _ = OpenGroupAPI.getDisplayNames(for: openGroup.channel, on: openGroup.server)
}
}

View File

@ -1,15 +1,11 @@
import PromiseKit
@objc(LKPushNotificationManager)
public final class LokiPushNotificationManager : NSObject {
public final class PushNotificationManager : NSObject {
// MARK: Settings
#if DEBUG
private static let server = "https://live.apns.getsession.org"
#else
private static let server = "https://live.apns.getsession.org"
#endif
internal static let pnServerPublicKey = "642a6585919742e5a2d4dc51244964fbcd8bcab2b75612407de58b810740d049"
public static let server = "https://live.apns.getsession.org"
public static let serverPublicKey = "642a6585919742e5a2d4dc51244964fbcd8bcab2b75612407de58b810740d049"
private static let maxRetryCount: UInt = 4
private static let tokenExpirationInterval: TimeInterval = 12 * 60 * 60
@ -22,7 +18,6 @@ public final class LokiPushNotificationManager : NSObject {
private override init() { }
// MARK: Registration
/// Unregisters the user from push notifications. Only the user's device token is needed for this.
static func unregister(with token: Data, isForcedUpdate: Bool) -> Promise<Void> {
let hexEncodedToken = token.toHexString()
let parameters = [ "token" : hexEncodedToken ]
@ -30,7 +25,7 @@ public final class LokiPushNotificationManager : NSObject {
let request = TSRequest(url: url, method: "POST", parameters: parameters)
request.allHTTPHeaderFields = [ "Content-Type" : "application/json" ]
let promise: Promise<Void> = attempt(maxRetryCount: maxRetryCount, recoveringOn: DispatchQueue.global()) {
OnionRequestAPI.sendOnionRequest(request, to: server, target: "/loki/v2/lsrpc", using: pnServerPublicKey).map2 { response in
OnionRequestAPI.sendOnionRequest(request, to: server, target: "/loki/v2/lsrpc", using: serverPublicKey).map2 { response in
guard let json = response["body"] as? JSON else {
return print("[Loki] Couldn't unregister from push notifications.")
}
@ -43,20 +38,17 @@ public final class LokiPushNotificationManager : NSObject {
print("[Loki] Couldn't unregister from push notifications.")
}
// Unsubscribe from all closed groups
Storage.getUserClosedGroupPublicKeys().forEach { closedGroup in
Storage.shared.getUserClosedGroupPublicKeys().forEach { closedGroup in
performOperation(.unsubscribe, for: closedGroup, publicKey: getUserHexEncodedPublicKey())
}
return promise
}
/// Unregisters the user from push notifications. Only the user's device token is needed for this.
@objc(unregisterWithToken:isForcedUpdate:)
public static func objc_unregister(with token: Data, isForcedUpdate: Bool) -> AnyPromise {
return AnyPromise.from(unregister(with: token, isForcedUpdate: isForcedUpdate))
}
/// Registers the user for push notifications. Requires the user's device
/// token and their Session ID.
static func register(with token: Data, publicKey: String, isForcedUpdate: Bool) -> Promise<Void> {
let hexEncodedToken = token.toHexString()
let userDefaults = UserDefaults.standard
@ -67,12 +59,12 @@ public final class LokiPushNotificationManager : NSObject {
print("[Loki] Device token hasn't changed or expired; no need to re-upload.")
return Promise<Void> { $0.fulfill(()) }
}
let parameters = [ "token" : hexEncodedToken, "pubKey" : publicKey]
let parameters = [ "token" : hexEncodedToken, "pubKey" : publicKey ]
let url = URL(string: "\(server)/register")!
let request = TSRequest(url: url, method: "POST", parameters: parameters)
request.allHTTPHeaderFields = [ "Content-Type" : "application/json" ]
let promise: Promise<Void> = attempt(maxRetryCount: maxRetryCount, recoveringOn: DispatchQueue.global()) {
OnionRequestAPI.sendOnionRequest(request, to: server, target: "/loki/v2/lsrpc", using: pnServerPublicKey).map2 { response in
OnionRequestAPI.sendOnionRequest(request, to: server, target: "/loki/v2/lsrpc", using: serverPublicKey).map2 { response in
guard let json = response["body"] as? JSON else {
return print("[Loki] Couldn't register device token.")
}
@ -88,14 +80,12 @@ public final class LokiPushNotificationManager : NSObject {
print("[Loki] Couldn't register device token.")
}
// Subscribe to all closed groups
Storage.getUserClosedGroupPublicKeys().forEach { closedGroup in
Storage.shared.getUserClosedGroupPublicKeys().forEach { closedGroup in
performOperation(.subscribe, for: closedGroup, publicKey: publicKey)
}
return promise
}
/// Registers the user for push notifications. Requires the user's device
/// token and their Session ID.
@objc(registerWithToken:hexEncodedPublicKey:isForcedUpdate:)
public static func objc_register(with token: Data, publicKey: String, isForcedUpdate: Bool) -> AnyPromise {
return AnyPromise.from(register(with: token, publicKey: publicKey, isForcedUpdate: isForcedUpdate))
@ -105,12 +95,12 @@ public final class LokiPushNotificationManager : NSObject {
public static func performOperation(_ operation: ClosedGroupOperation, for closedGroupPublicKey: String, publicKey: String) -> Promise<Void> {
let isUsingFullAPNs = UserDefaults.standard[.isUsingFullAPNs]
guard isUsingFullAPNs else { return Promise<Void> { $0.fulfill(()) } }
let parameters = [ "closedGroupPublicKey" : closedGroupPublicKey, "pubKey" : publicKey]
let parameters = [ "closedGroupPublicKey" : closedGroupPublicKey, "pubKey" : publicKey ]
let url = URL(string: "\(server)/\(operation.rawValue)")!
let request = TSRequest(url: url, method: "POST", parameters: parameters)
request.allHTTPHeaderFields = [ "Content-Type" : "application/json" ]
let promise: Promise<Void> = attempt(maxRetryCount: maxRetryCount, recoveringOn: DispatchQueue.global()) {
OnionRequestAPI.sendOnionRequest(request, to: server, target: "/loki/v2/lsrpc", using: pnServerPublicKey).map2 { response in
OnionRequestAPI.sendOnionRequest(request, to: server, target: "/loki/v2/lsrpc", using: serverPublicKey).map2 { response in
guard let json = response["body"] as? JSON else {
return print("[Loki] Couldn't subscribe/unsubscribe closed group: \(closedGroupPublicKey).")
}

View File

@ -0,0 +1,13 @@
extension VisibleMessage.Quote {
@objc(from:)
public static func from(_ model: OWSQuotedReplyModel?) -> VisibleMessage.Quote? {
guard let model = model else { return nil }
let result = VisibleMessage.Quote()
result.timestamp = model.timestamp
result.publicKey = model.authorId
result.text = model.body
return result
}
}

View File

@ -52,6 +52,7 @@ FOUNDATION_EXPORT const unsigned char SignalUtilitiesKitVersionString[];
#import <SignalUtilitiesKit/OWSPrimaryStorage+SessionStore.h>
#import <SignalUtilitiesKit/OWSPrimaryStorage+SignedPreKeyStore.h>
#import <SignalUtilitiesKit/OWSProfileManager.h>
#import <SignalUtilitiesKit/OWSQuotedReplyModel.h>
#import <SignalUtilitiesKit/OWSReadReceiptManager.h>
#import <SignalUtilitiesKit/OWSSearchBar.h>
#import <SignalUtilitiesKit/OWSSyncManagerProtocol.h>

View File

@ -1,53 +0,0 @@
@objc(LKDatabaseUtilities)
public final class LokiDatabaseUtilities : NSObject {
private override init() { }
// MARK: - Quotes
@objc(getServerIDForQuoteWithID:quoteeHexEncodedPublicKey:threadID:transaction:)
public static func getServerID(quoteID: UInt64, quoteeHexEncodedPublicKey: String, threadID: String, transaction: YapDatabaseReadTransaction) -> UInt64 {
guard let message = TSInteraction.interactions(withTimestamp: quoteID, filter: { interaction in
let senderHexEncodedPublicKey: String
if let message = interaction as? TSIncomingMessage {
senderHexEncodedPublicKey = message.authorId
} else if let message = interaction as? TSOutgoingMessage {
senderHexEncodedPublicKey = getUserHexEncodedPublicKey()
} else {
return false
}
return (senderHexEncodedPublicKey == quoteeHexEncodedPublicKey) && (interaction.uniqueThreadId == threadID)
}, with: transaction).first as! TSMessage? else { return 0 }
return message.openGroupServerMessageID
}
// MARK: - Open Groups
private static let publicChatCollection = "LokiPublicChatCollection"
@objc(getAllPublicChats:)
public static func getAllPublicChats(in transaction: YapDatabaseReadTransaction) -> [String:OpenGroup] {
var result = [String:OpenGroup]()
transaction.enumerateKeysAndObjects(inCollection: publicChatCollection) { threadID, object, _ in
guard let publicChat = object as? OpenGroup else { return }
result[threadID] = publicChat
}
return result
}
@objc(getPublicChatForThreadID:transaction:)
public static func getPublicChat(for threadID: String, in transaction: YapDatabaseReadTransaction) -> OpenGroup? {
return transaction.object(forKey: threadID, inCollection: publicChatCollection) as? OpenGroup
}
@objc(setPublicChat:threadID:transaction:)
public static func setPublicChat(_ publicChat: OpenGroup, for threadID: String, in transaction: YapDatabaseReadWriteTransaction) {
transaction.setObject(publicChat, forKey: threadID, inCollection: publicChatCollection)
}
@objc(removePublicChatForThreadID:transaction:)
public static func removePublicChat(for threadID: String, in transaction: YapDatabaseReadWriteTransaction) {
transaction.removeObject(forKey: threadID, inCollection: publicChatCollection)
}
}

View File

@ -1,81 +0,0 @@
// TODO: Make this strongly typed like LKUserDefaults
public extension OWSPrimaryStorage {
// MARK: Snode Pool
public func setSnodePool(_ snodePool: Set<Snode>, in transaction: YapDatabaseReadWriteTransaction) {
clearSnodePool(in: transaction)
snodePool.forEach { snode in
transaction.setObject(snode, forKey: snode.description, inCollection: Storage.snodePoolCollection)
}
}
public func clearSnodePool(in transaction: YapDatabaseReadWriteTransaction) {
transaction.removeAllObjects(inCollection: Storage.snodePoolCollection)
}
public func getSnodePool(in transaction: YapDatabaseReadTransaction) -> Set<Snode> {
var result: Set<Snode> = []
transaction.enumerateKeysAndObjects(inCollection: Storage.snodePoolCollection) { _, object, _ in
guard let snode = object as? Snode else { return }
result.insert(snode)
}
return result
}
public func dropSnodeFromSnodePool(_ snode: Snode, in transaction: YapDatabaseReadWriteTransaction) {
transaction.removeObject(forKey: snode.description, inCollection: Storage.snodePoolCollection)
}
// MARK: Swarm
public func setSwarm(_ swarm: [Snode], for publicKey: String, in transaction: YapDatabaseReadWriteTransaction) {
print("[Loki] Caching swarm for: \(publicKey == getUserHexEncodedPublicKey() ? "self" : publicKey).")
clearSwarm(for: publicKey, in: transaction)
let collection = Storage.getSwarmCollection(for: publicKey)
swarm.forEach { snode in
transaction.setObject(snode, forKey: snode.description, inCollection: collection)
}
}
public func clearSwarm(for publicKey: String, in transaction: YapDatabaseReadWriteTransaction) {
let collection = Storage.getSwarmCollection(for: publicKey)
transaction.removeAllObjects(inCollection: collection)
}
public func getSwarm(for publicKey: String, in transaction: YapDatabaseReadTransaction) -> [Snode] {
var result: [Snode] = []
let collection = Storage.getSwarmCollection(for: publicKey)
transaction.enumerateKeysAndObjects(inCollection: collection) { _, object, _ in
guard let snode = object as? Snode else { return }
result.append(snode)
}
return result
}
// MARK: Session Requests
public func setSessionRequestTimestamp(for publicKey: String, to timestamp: Date, in transaction: YapDatabaseReadWriteTransaction) {
transaction.setDate(timestamp, forKey: publicKey, inCollection: Storage.sessionRequestTimestampCollection)
}
public func getSessionRequestTimestamp(for publicKey: String, in transaction: YapDatabaseReadTransaction) -> Date? {
transaction.date(forKey: publicKey, inCollection: Storage.sessionRequestTimestampCollection)
}
// MARK: Open Groups
public func getUserCount(for publicChat: OpenGroup, in transaction: YapDatabaseReadTransaction) -> Int? {
return transaction.object(forKey: publicChat.id, inCollection: Storage.openGroupUserCountCollection) as? Int
}
public func setUserCount(_ userCount: Int, forPublicChatWithID publicChatID: String, in transaction: YapDatabaseReadWriteTransaction) {
transaction.setObject(userCount, forKey: publicChatID, inCollection: Storage.openGroupUserCountCollection)
}
public func getProfilePictureURL(forPublicChatWithID publicChatID: String, in transaction: YapDatabaseReadTransaction) -> String? {
return transaction.object(forKey: publicChatID, inCollection: Storage.openGroupProfilePictureURLCollection) as? String
}
public func setProfilePictureURL(_ profilePictureURL: String?, forPublicChatWithID publicChatID: String, in transaction: YapDatabaseReadWriteTransaction) {
transaction.setObject(profilePictureURL, forKey: publicChatID, inCollection: Storage.openGroupProfilePictureURLCollection)
}
}

View File

@ -1,79 +0,0 @@
public extension Storage {
// MARK: Ratchets
internal static func getClosedGroupRatchetCollection(_ collection: ClosedGroupRatchetCollectionType, for groupPublicKey: String) -> String {
switch collection {
case .old: return "LokiOldClosedGroupRatchetCollection.\(groupPublicKey)"
case .current: return "LokiClosedGroupRatchetCollection.\(groupPublicKey)"
}
}
internal static func getClosedGroupRatchet(for groupPublicKey: String, senderPublicKey: String, from collection: ClosedGroupRatchetCollectionType = .current) -> ClosedGroupRatchet? {
let collection = getClosedGroupRatchetCollection(collection, for: groupPublicKey)
var result: ClosedGroupRatchet?
read { transaction in
result = transaction.object(forKey: senderPublicKey, inCollection: collection) as? ClosedGroupRatchet
}
return result
}
internal static func setClosedGroupRatchet(for groupPublicKey: String, senderPublicKey: String, ratchet: ClosedGroupRatchet, in collection: ClosedGroupRatchetCollectionType = .current, using transaction: YapDatabaseReadWriteTransaction) {
let collection = getClosedGroupRatchetCollection(collection, for: groupPublicKey)
transaction.setObject(ratchet, forKey: senderPublicKey, inCollection: collection)
}
public static func getAllClosedGroupRatchets(for groupPublicKey: String, from collection: ClosedGroupRatchetCollectionType = .current) -> [(senderPublicKey: String, ratchet: ClosedGroupRatchet)] {
let collection = getClosedGroupRatchetCollection(collection, for: groupPublicKey)
var result: [(senderPublicKey: String, ratchet: ClosedGroupRatchet)] = []
read { transaction in
transaction.enumerateRows(inCollection: collection) { key, object, _, _ in
guard let senderPublicKey = key as? String, let ratchet = object as? ClosedGroupRatchet else { return }
result.append((senderPublicKey: senderPublicKey, ratchet: ratchet))
}
}
return result
}
internal static func getAllClosedGroupSenderKeys(for groupPublicKey: String, from collection: ClosedGroupRatchetCollectionType = .current) -> Set<ClosedGroupSenderKey> {
return Set(getAllClosedGroupRatchets(for: groupPublicKey, from: collection).map { senderPublicKey, ratchet in
ClosedGroupSenderKey(chainKey: Data(hex: ratchet.chainKey), keyIndex: ratchet.keyIndex, publicKey: Data(hex: senderPublicKey))
})
}
public static func removeAllClosedGroupRatchets(for groupPublicKey: String, from collection: ClosedGroupRatchetCollectionType = .current, using transaction: YapDatabaseReadWriteTransaction) {
let collection = getClosedGroupRatchetCollection(collection, for: groupPublicKey)
transaction.removeAllObjects(inCollection: collection)
}
}
@objc public extension Storage {
// MARK: Private Keys
internal static let closedGroupPrivateKeyCollection = "LokiClosedGroupPrivateKeyCollection"
public static func getUserClosedGroupPublicKeys() -> Set<String> {
var result: Set<String> = []
read { transaction in
result = Set(transaction.allKeys(inCollection: closedGroupPrivateKeyCollection))
}
return result
}
@objc(getPrivateKeyForClosedGroupWithPublicKey:)
internal static func getClosedGroupPrivateKey(for publicKey: String) -> String? {
var result: String?
read { transaction in
result = transaction.object(forKey: publicKey, inCollection: closedGroupPrivateKeyCollection) as? String
}
return result
}
internal static func setClosedGroupPrivateKey(_ privateKey: String, for publicKey: String, using transaction: YapDatabaseReadWriteTransaction) {
transaction.setObject(privateKey, forKey: publicKey, inCollection: closedGroupPrivateKeyCollection)
}
internal static func removeClosedGroupPrivateKey(for publicKey: String, using transaction: YapDatabaseReadWriteTransaction) {
transaction.removeObject(forKey: publicKey, inCollection: closedGroupPrivateKeyCollection)
}
}

View File

@ -1,21 +0,0 @@
// TODO: Create an extension for each category, e.g. Storage+OpenGroups, Storage+SnodePool, etc.
@objc public extension Storage {
// TODO: Add remaining collections
@objc func getDeviceLinkCollection(for masterPublicKey: String) -> String {
return "LokiDeviceLinkCollection-\(masterPublicKey)"
}
@objc public static func getSwarmCollection(for publicKey: String) -> String {
return "LokiSwarmCollection-\(publicKey)"
}
@objc public static let openGroupCollection = "LokiPublicChatCollection"
@objc public static let openGroupProfilePictureURLCollection = "LokiPublicChatAvatarURLCollection"
@objc public static let openGroupUserCountCollection = "LokiPublicChatUserCountCollection"
@objc public static let sessionRequestTimestampCollection = "LokiSessionRequestTimestampCollection"
@objc public static let snodePoolCollection = "LokiSnodePoolCollection"
}

View File

@ -1,38 +0,0 @@
public extension Storage {
// MARK: Open Group Public Keys
internal static let openGroupPublicKeyCollection = "LokiOpenGroupPublicKeyCollection"
public static let lastMessageServerIDCollection = "LokiGroupChatLastMessageServerIDCollection"
public static let lastDeletionServerIDCollection = "LokiGroupChatLastDeletionServerIDCollection"
internal static func getOpenGroupPublicKey(for server: String) -> String? {
var result: String? = nil
read { transaction in
result = transaction.object(forKey: server, inCollection: openGroupPublicKeyCollection) as? String
}
return result
}
internal static func setOpenGroupPublicKey(for server: String, to publicKey: String, using transaction: YapDatabaseReadWriteTransaction) {
transaction.setObject(publicKey, forKey: server, inCollection: openGroupPublicKeyCollection)
}
internal static func removeOpenGroupPublicKey(for server: String, using transaction: YapDatabaseReadWriteTransaction) {
transaction.removeObject(forKey: server, inCollection: openGroupPublicKeyCollection)
}
private static func removeLastMessageServerID(for group: UInt64, on server: String, using transaction: YapDatabaseReadWriteTransaction) {
transaction.removeObject(forKey: "\(server).\(group)", inCollection: lastMessageServerIDCollection)
}
private static func removeLastDeletionServerID(for group: UInt64, on server: String, using transaction: YapDatabaseReadWriteTransaction) {
transaction.removeObject(forKey: "\(server).\(group)", inCollection: lastDeletionServerIDCollection)
}
internal static func clearAllData(for group: UInt64, on server: String, using transaction: YapDatabaseReadWriteTransaction) {
removeLastMessageServerID(for: group, on: server, using: transaction)
removeLastDeletionServerID(for: group, on: server, using: transaction)
Storage.removeOpenGroupPublicKey(for: server, using: transaction)
}
}

View File

@ -1,31 +0,0 @@
public extension Storage {
// MARK: Session Request Timestamps
internal static let sessionRequestSentTimestampCollection = "LokiSessionRequestSentTimestampCollection"
internal static let sessionRequestProcessedTimestampCollection = "LokiSessionRequestProcessedTimestampCollection"
internal static func getSessionRequestSentTimestamp(for publicKey: String) -> UInt64 {
var result: UInt64?
read { transaction in
result = transaction.object(forKey: publicKey, inCollection: sessionRequestSentTimestampCollection) as? UInt64
}
return result ?? 0
}
internal static func setSessionRequestSentTimestamp(for publicKey: String, to timestamp: UInt64, using transaction: YapDatabaseReadWriteTransaction) {
transaction.setObject(timestamp, forKey: publicKey, inCollection: sessionRequestSentTimestampCollection)
}
internal static func getSessionRequestProcessedTimestamp(for publicKey: String) -> UInt64 {
var result: UInt64?
read { transaction in
result = transaction.object(forKey: publicKey, inCollection: sessionRequestProcessedTimestampCollection) as? UInt64
}
return result ?? 0
}
internal static func setSessionRequestProcessedTimestamp(for publicKey: String, to timestamp: UInt64, using transaction: YapDatabaseReadWriteTransaction) {
transaction.setObject(timestamp, forKey: publicKey, inCollection: sessionRequestProcessedTimestampCollection)
}
}

View File

@ -1,58 +0,0 @@
internal extension Storage {
// MARK: Last Message Hash
private static let lastMessageHashCollection = "LokiLastMessageHashCollection"
internal static func getLastMessageHashInfo(for snode: Snode, associatedWith publicKey: String) -> JSON? {
let key = "\(snode.address):\(snode.port).\(publicKey)"
var result: JSON?
read { transaction in
result = transaction.object(forKey: key, inCollection: lastMessageHashCollection) as? JSON
}
if let result = result {
guard result["hash"] as? String != nil else { return nil }
guard result["expirationDate"] as? NSNumber != nil else { return nil }
}
return result
}
internal static func pruneLastMessageHashInfoIfExpired(for snode: Snode, associatedWith publicKey: String, using transaction: YapDatabaseReadWriteTransaction) {
guard let lastMessageHashInfo = getLastMessageHashInfo(for: snode, associatedWith: publicKey),
let hash = lastMessageHashInfo["hash"] as? String, let expirationDate = (lastMessageHashInfo["expirationDate"] as? NSNumber)?.uint64Value else { return }
let now = NSDate.ows_millisecondTimeStamp()
if now >= expirationDate {
removeLastMessageHashInfo(for: snode, associatedWith: publicKey, using: transaction)
}
}
internal static func getLastMessageHash(for snode: Snode, associatedWith publicKey: String) -> String? {
return getLastMessageHashInfo(for: snode, associatedWith: publicKey)?["hash"] as? String
}
internal static func removeLastMessageHashInfo(for snode: Snode, associatedWith publicKey: String, using transaction: YapDatabaseReadWriteTransaction) {
let key = "\(snode.address):\(snode.port).\(publicKey)"
transaction.removeObject(forKey: key, inCollection: lastMessageHashCollection)
}
internal static func setLastMessageHashInfo(for snode: Snode, associatedWith publicKey: String, to lastMessageHashInfo: JSON, using transaction: YapDatabaseReadWriteTransaction) {
let key = "\(snode.address):\(snode.port).\(publicKey)"
guard lastMessageHashInfo.count == 2 && lastMessageHashInfo["hash"] as? String != nil && lastMessageHashInfo["expirationDate"] as? NSNumber != nil else { return }
transaction.setObject(lastMessageHashInfo, forKey: key, inCollection: lastMessageHashCollection)
}
// MARK: Received Messages
private static let receivedMessagesCollection = "LokiReceivedMessagesCollection"
internal static func getReceivedMessages(for publicKey: String) -> Set<String>? {
var result: Set<String>?
read { transaction in
result = transaction.object(forKey: publicKey, inCollection: receivedMessagesCollection) as? Set<String>
}
return result
}
internal static func setReceivedMessages(to receivedMessages: Set<String>, for publicKey: String, using transaction: YapDatabaseReadWriteTransaction) {
transaction.setObject(receivedMessages, forKey: publicKey, inCollection: receivedMessagesCollection)
}
}

View File

@ -338,7 +338,7 @@ typedef void (^AttachmentDownloadFailure)(NSError *error);
- (void)continueDownloadIfPossible
{
if (self.attachmentDownloadJobQueue.count > 0) {
[LKLogger print:@"[Loki] Continuing unfinished attachment download tasks."];
NSLog(@"[Loki] Continuing unfinished attachment download tasks.");
[self startDownloadIfPossible];
}
}

View File

@ -80,10 +80,7 @@ static const CGFloat kAttachmentUploadProgressTheta = 0.001f;
[self fireNotificationWithProgress:0];
__block SNOpenGroup *publicChat;
[self.dbConnection readWithBlock:^(YapDatabaseReadTransaction *transaction) {
publicChat = [LKDatabaseUtilities getPublicChatForThreadID:self.threadID transaction:transaction];
}];
SNOpenGroup *publicChat = [LKStorage.shared getOpenGroupForThreadID:self.threadID];
NSString *server = (publicChat != nil) ? publicChat.server : SNFileServerAPI.server;
[[SNFileServerAPI uploadAttachment:attachmentStream withID:self.attachmentId toServer:server]

View File

@ -290,7 +290,7 @@ BOOL IsNoteToSelfEnabled(void)
}
id<OWSReadTracking> unread = (id<OWSReadTracking>)object;
if (unread.read) {
[LKLogger print:@"Found an already read message in the * unseen * messages list."];
NSLog(@"Found an already read message in the * unseen * messages list.");
return;
}
[messages addObject:unread];
@ -312,7 +312,7 @@ BOOL IsNoteToSelfEnabled(void)
}
id<OWSReadTracking> unread = (id<OWSReadTracking>)object;
if (unread.read) {
[LKLogger print:@"Found an already read message in the * unread * messages list."];
NSLog(@"Found an already read message in the * unread * messages list.");
return;
}
count += 1;

View File

@ -73,9 +73,9 @@ public final class ClosedGroupsProtocol : NSObject {
promises.append(promise)
}
// Add the group to the user's set of public keys to poll for
Storage.setClosedGroupPrivateKey(groupKeyPair.privateKey.toHexString(), for: groupPublicKey, using: transaction)
Storage.shared.setClosedGroupPrivateKey(groupKeyPair.privateKey.toHexString(), for: groupPublicKey, using: transaction)
// Notify the PN server
promises.append(LokiPushNotificationManager.performOperation(.subscribe, for: groupPublicKey, publicKey: userPublicKey))
promises.append(PushNotificationManager.performOperation(.subscribe, for: groupPublicKey, publicKey: userPublicKey))
// Notify the user
let infoMessage = TSInfoMessage(timestamp: NSDate.ows_millisecondTimeStamp(), in: thread, messageType: .typeGroupUpdate)
infoMessage.save(with: transaction)
@ -98,7 +98,7 @@ public final class ClosedGroupsProtocol : NSObject {
let membersAsData = members.map { Data(hex: $0) }
let admins = group.groupAdminIds
let adminsAsData = admins.map { Data(hex: $0) }
guard let groupPrivateKey = Storage.getClosedGroupPrivateKey(for: groupPublicKey) else {
guard let groupPrivateKey = Storage.shared.getClosedGroupPrivateKey(for: groupPublicKey) else {
print("[Loki] Couldn't get private key for closed group.")
return Promise(error: Error.noPrivateKey)
}
@ -126,19 +126,19 @@ public final class ClosedGroupsProtocol : NSObject {
when(resolved: promises).done2 { _ in seal.fulfill(()) }.catch2 { seal.reject($0) }
let _ = promise.done {
Storage.writeSync { transaction in
let allOldRatchets = Storage.getAllClosedGroupRatchets(for: groupPublicKey)
let allOldRatchets = Storage.shared.getAllClosedGroupRatchets(for: groupPublicKey)
for (senderPublicKey, oldRatchet) in allOldRatchets {
let collection = ClosedGroupRatchetCollectionType.old
Storage.setClosedGroupRatchet(for: groupPublicKey, senderPublicKey: senderPublicKey, ratchet: oldRatchet, in: collection, using: transaction)
Storage.shared.setClosedGroupRatchet(for: groupPublicKey, senderPublicKey: senderPublicKey, ratchet: oldRatchet, in: collection, using: transaction)
}
// Delete all ratchets (it's important that this happens * after * sending out the update)
Storage.removeAllClosedGroupRatchets(for: groupPublicKey, using: transaction)
Storage.shared.removeAllClosedGroupRatchets(for: groupPublicKey, using: transaction)
// Remove the group from the user's set of public keys to poll for if the user is leaving. Otherwise generate a new ratchet and
// send it out to all members (minus the removed ones) using established channels.
if isUserLeaving {
Storage.removeClosedGroupPrivateKey(for: groupPublicKey, using: transaction)
Storage.shared.removeClosedGroupPrivateKey(for: groupPublicKey, using: transaction)
// Notify the PN server
let _ = LokiPushNotificationManager.performOperation(.unsubscribe, for: groupPublicKey, publicKey: userPublicKey)
let _ = PushNotificationManager.performOperation(.unsubscribe, for: groupPublicKey, publicKey: userPublicKey)
} else {
// Send closed group update messages to any new members using established channels
for member in newMembers {
@ -181,7 +181,7 @@ public final class ClosedGroupsProtocol : NSObject {
// Establish sessions if needed
establishSessionsIfNeeded(with: [String](newMembers), using: transaction)
// Send closed group update messages to the new members using established channels
var allSenderKeys = Storage.getAllClosedGroupSenderKeys(for: groupPublicKey)
var allSenderKeys = Storage.shared.getAllClosedGroupSenderKeys(for: groupPublicKey)
allSenderKeys.formUnion(newSenderKeys)
for member in newMembers {
let thread = TSContactThread.getOrCreateThread(withContactId: member, transaction: transaction)
@ -194,7 +194,7 @@ public final class ClosedGroupsProtocol : NSObject {
}
} else {
seal.fulfill(())
let allSenderKeys = Storage.getAllClosedGroupSenderKeys(for: groupPublicKey)
let allSenderKeys = Storage.shared.getAllClosedGroupSenderKeys(for: groupPublicKey)
let closedGroupUpdateKind = ClosedGroupUpdate.Kind.info(groupPublicKey: Data(hex: groupPublicKey), name: name,
senderKeys: [ClosedGroupSenderKey](allSenderKeys), members: membersAsData, admins: adminsAsData)
let closedGroupUpdate = ClosedGroupUpdate()
@ -245,202 +245,6 @@ public final class ClosedGroupsProtocol : NSObject {
MessageSender.send(closedGroupUpdate, in: thread, using: transaction)
}
// MARK: - Receiving
@objc(handleSharedSenderKeysUpdateIfNeeded:from:transaction:)
public static func handleSharedSenderKeysUpdateIfNeeded(_ dataMessage: SNProtoDataMessage, from publicKey: String, using transaction: YapDatabaseReadWriteTransaction) {
// Note that `publicKey` is either the public key of the group or the public key of the
// sender, depending on how the message was sent
guard let closedGroupUpdate = dataMessage.closedGroupUpdate, isValid(closedGroupUpdate) else { return }
switch closedGroupUpdate.type {
case .new: handleNewGroupMessage(closedGroupUpdate, using: transaction)
case .info: handleInfoMessage(closedGroupUpdate, from: publicKey, using: transaction)
case .senderKeyRequest: handleSenderKeyRequestMessage(closedGroupUpdate, from: publicKey, using: transaction)
case .senderKey: handleSenderKeyMessage(closedGroupUpdate, from: publicKey, using: transaction)
}
}
private static func isValid(_ closedGroupUpdate: SNProtoDataMessageClosedGroupUpdate) -> Bool {
guard !closedGroupUpdate.groupPublicKey.isEmpty else { return false }
switch closedGroupUpdate.type {
case .new: return !(closedGroupUpdate.name ?? "").isEmpty && !(closedGroupUpdate.groupPrivateKey ?? Data()).isEmpty && !closedGroupUpdate.members.isEmpty
&& !closedGroupUpdate.admins.isEmpty // senderKeys may be empty
case .info: return !(closedGroupUpdate.name ?? "").isEmpty && !closedGroupUpdate.members.isEmpty && !closedGroupUpdate.admins.isEmpty // senderKeys may be empty
case .senderKeyRequest: return true
case .senderKey: return !closedGroupUpdate.senderKeys.isEmpty
}
}
private static func handleNewGroupMessage(_ closedGroupUpdate: SNProtoDataMessageClosedGroupUpdate, using transaction: YapDatabaseReadWriteTransaction) {
// Unwrap the message
let groupPublicKey = closedGroupUpdate.groupPublicKey.toHexString()
let name = closedGroupUpdate.name
let groupPrivateKey = closedGroupUpdate.groupPrivateKey!
let senderKeys = closedGroupUpdate.senderKeys
let members = closedGroupUpdate.members.map { $0.toHexString() }
let admins = closedGroupUpdate.admins.map { $0.toHexString() }
// Persist the ratchets
senderKeys.forEach { senderKey in
guard members.contains(senderKey.publicKey.toHexString()) else { return }
let ratchet = ClosedGroupRatchet(chainKey: senderKey.chainKey.toHexString(), keyIndex: UInt(senderKey.keyIndex), messageKeys: [])
Storage.setClosedGroupRatchet(for: groupPublicKey, senderPublicKey: senderKey.publicKey.toHexString(), ratchet: ratchet, using: transaction)
}
// Sort out any discrepancies between the provided sender keys and what's required
let missingSenderKeys = Set(members).subtracting(senderKeys.map { $0.publicKey.toHexString() })
let userPublicKey = getUserHexEncodedPublicKey()
if missingSenderKeys.contains(userPublicKey) {
establishSessionsIfNeeded(with: [String](members), using: transaction)
let userRatchet = SharedSenderKeys.generateRatchet(for: groupPublicKey, senderPublicKey: userPublicKey, using: transaction)
let userSenderKey = ClosedGroupSenderKey(chainKey: Data(hex: userRatchet.chainKey), keyIndex: userRatchet.keyIndex, publicKey: Data(hex: userPublicKey))
for member in members {
guard member != userPublicKey else { continue }
let thread = TSContactThread.getOrCreateThread(withContactId: member, transaction: transaction)
thread.save(with: transaction)
let closedGroupUpdateKind = ClosedGroupUpdate.Kind.senderKey(groupPublicKey: Data(hex: groupPublicKey), senderKey: userSenderKey)
let closedGroupUpdate = ClosedGroupUpdate()
closedGroupUpdate.kind = closedGroupUpdateKind
MessageSender.send(closedGroupUpdate, in: thread, using: transaction)
}
}
for publicKey in missingSenderKeys.subtracting([ userPublicKey ]) {
requestSenderKey(for: groupPublicKey, senderPublicKey: publicKey, using: transaction)
}
// Create the group
let groupID = LKGroupUtilities.getEncodedClosedGroupIDAsData(groupPublicKey)
let group = TSGroupModel(title: name, memberIds: members, image: nil, groupId: groupID, groupType: .closedGroup, adminIds: admins)
let thread: TSGroupThread
if let t = TSGroupThread.fetch(uniqueId: TSGroupThread.threadId(fromGroupId: groupID), transaction: transaction) {
thread = t
thread.setGroupModel(group, with: transaction)
} else {
thread = TSGroupThread.getOrCreateThread(with: group, transaction: transaction)
thread.usesSharedSenderKeys = true
thread.save(with: transaction)
}
// Add the group to the user's set of public keys to poll for
Storage.setClosedGroupPrivateKey(groupPrivateKey.toHexString(), for: groupPublicKey, using: transaction)
// Notify the PN server
let _ = LokiPushNotificationManager.performOperation(.subscribe, for: groupPublicKey, publicKey: getUserHexEncodedPublicKey())
// Notify the user
let infoMessage = TSInfoMessage(timestamp: NSDate.ows_millisecondTimeStamp(), in: thread, messageType: .typeGroupUpdate)
infoMessage.save(with: transaction)
// Establish sessions if needed
establishSessionsIfNeeded(with: members, using: transaction)
}
/// Invoked upon receiving a group update. A group update is sent out when a group's name is changed, when new users are added, when users leave or are
/// kicked, or if the group admins are changed.
private static func handleInfoMessage(_ closedGroupUpdate: SNProtoDataMessageClosedGroupUpdate, from senderPublicKey: String,
using transaction: YapDatabaseReadWriteTransaction) {
// Unwrap the message
let groupPublicKey = closedGroupUpdate.groupPublicKey.toHexString()
let name = closedGroupUpdate.name
let senderKeys = closedGroupUpdate.senderKeys
let members = closedGroupUpdate.members.map { $0.toHexString() }
let admins = closedGroupUpdate.admins.map { $0.toHexString() }
// Get the group
let groupID = LKGroupUtilities.getEncodedClosedGroupIDAsData(groupPublicKey)
guard let thread = TSGroupThread.fetch(uniqueId: TSGroupThread.threadId(fromGroupId: groupID), transaction: transaction) else {
return print("[Loki] Ignoring closed group info message for nonexistent group.")
}
let group = thread.groupModel
// Check that the sender is a member of the group (before the update)
guard Set(group.groupMemberIds).contains(senderPublicKey) else {
return print("[Loki] Ignoring closed group info message from non-member.")
}
// Store the ratchets for any new members (it's important that this happens before the code below)
senderKeys.forEach { senderKey in
let ratchet = ClosedGroupRatchet(chainKey: senderKey.chainKey.toHexString(), keyIndex: UInt(senderKey.keyIndex), messageKeys: [])
Storage.setClosedGroupRatchet(for: groupPublicKey, senderPublicKey: senderKey.publicKey.toHexString(), ratchet: ratchet, using: transaction)
}
// Delete all ratchets and either:
// Send out the user's new ratchet using established channels if other members of the group left or were removed
// Remove the group from the user's set of public keys to poll for if the current user was among the members that were removed
let oldMembers = group.groupMemberIds
let userPublicKey = getUserHexEncodedPublicKey()
let wasUserRemoved = !members.contains(userPublicKey)
if Set(members).intersection(oldMembers) != Set(oldMembers) {
let allOldRatchets = Storage.getAllClosedGroupRatchets(for: groupPublicKey)
for (senderPublicKey, oldRatchet) in allOldRatchets {
let collection = ClosedGroupRatchetCollectionType.old
Storage.setClosedGroupRatchet(for: groupPublicKey, senderPublicKey: senderPublicKey, ratchet: oldRatchet, in: collection, using: transaction)
}
Storage.removeAllClosedGroupRatchets(for: groupPublicKey, using: transaction)
if wasUserRemoved {
Storage.removeClosedGroupPrivateKey(for: groupPublicKey, using: transaction)
// Notify the PN server
let _ = LokiPushNotificationManager.performOperation(.unsubscribe, for: groupPublicKey, publicKey: userPublicKey)
} else {
establishSessionsIfNeeded(with: members, using: transaction)
let userRatchet = SharedSenderKeys.generateRatchet(for: groupPublicKey, senderPublicKey: userPublicKey, using: transaction)
let userSenderKey = ClosedGroupSenderKey(chainKey: Data(hex: userRatchet.chainKey), keyIndex: userRatchet.keyIndex, publicKey: Data(hex: userPublicKey))
for member in members {
guard member != userPublicKey else { continue }
let thread = TSContactThread.getOrCreateThread(withContactId: member, transaction: transaction)
thread.save(with: transaction)
let closedGroupUpdateKind = ClosedGroupUpdate.Kind.senderKey(groupPublicKey: Data(hex: groupPublicKey), senderKey: userSenderKey)
let closedGroupUpdate = ClosedGroupUpdate()
closedGroupUpdate.kind = closedGroupUpdateKind
MessageSender.send(closedGroupUpdate, in: thread, using: transaction)
}
}
}
// Update the group
let newGroupModel = TSGroupModel(title: name, memberIds: members, image: nil, groupId: groupID, groupType: .closedGroup, adminIds: admins)
thread.setGroupModel(newGroupModel, with: transaction)
// Notify the user if needed
if Set(members) != Set(oldMembers) || Set(admins) != Set(group.groupAdminIds) || name != group.groupName {
let infoMessageType: TSInfoMessageType = wasUserRemoved ? .typeGroupQuit : .typeGroupUpdate
let updateInfo = group.getInfoStringAboutUpdate(to: newGroupModel)
let infoMessage = TSInfoMessage(timestamp: NSDate.ows_millisecondTimeStamp(), in: thread, messageType: infoMessageType, customMessage: updateInfo)
infoMessage.save(with: transaction)
}
}
private static func handleSenderKeyRequestMessage(_ closedGroupUpdate: SNProtoDataMessageClosedGroupUpdate, from senderPublicKey: String, using transaction: YapDatabaseReadWriteTransaction) {
// Prepare
let userPublicKey = getUserHexEncodedPublicKey()
let groupPublicKey = closedGroupUpdate.groupPublicKey.toHexString()
let groupID = LKGroupUtilities.getEncodedClosedGroupIDAsData(groupPublicKey)
guard let groupThread = TSGroupThread.fetch(uniqueId: TSGroupThread.threadId(fromGroupId: groupID), transaction: transaction) else {
return print("[Loki] Ignoring closed group sender key request for nonexistent group.")
}
let group = groupThread.groupModel
// Check that the requesting user is a member of the group
let members = Set(group.groupMemberIds)
guard members.contains(senderPublicKey) else {
return print("[Loki] Ignoring closed group sender key request from non-member.")
}
// Respond to the request
print("[Loki] Responding to sender key request from: \(senderPublicKey).")
SessionManagementProtocol.sendSessionRequestIfNeeded(to: senderPublicKey, using: transaction)
let userRatchet = Storage.getClosedGroupRatchet(for: groupPublicKey, senderPublicKey: userPublicKey)
?? SharedSenderKeys.generateRatchet(for: groupPublicKey, senderPublicKey: userPublicKey, using: transaction)
let userSenderKey = ClosedGroupSenderKey(chainKey: Data(hex: userRatchet.chainKey), keyIndex: userRatchet.keyIndex, publicKey: Data(hex: userPublicKey))
let thread = TSContactThread.getOrCreateThread(withContactId: senderPublicKey, transaction: transaction)
thread.save(with: transaction)
let closedGroupUpdateKind = ClosedGroupUpdate.Kind.senderKey(groupPublicKey: Data(hex: groupPublicKey), senderKey: userSenderKey)
let closedGroupUpdate = ClosedGroupUpdate()
closedGroupUpdate.kind = closedGroupUpdateKind
MessageSender.send(closedGroupUpdate, in: thread, using: transaction)
}
/// Invoked upon receiving a sender key from another user.
private static func handleSenderKeyMessage(_ closedGroupUpdate: SNProtoDataMessageClosedGroupUpdate, from senderPublicKey: String, using transaction: YapDatabaseReadWriteTransaction) {
// Prepare
let groupPublicKey = closedGroupUpdate.groupPublicKey.toHexString()
guard let senderKey = closedGroupUpdate.senderKeys.first else {
return print("[Loki] Ignoring invalid closed group sender key.")
}
guard senderKey.publicKey.toHexString() == senderPublicKey else {
return print("[Loki] Ignoring invalid closed group sender key.")
}
// Store the sender key
print("[Loki] Received a sender key from: \(senderPublicKey).")
let ratchet = ClosedGroupRatchet(chainKey: senderKey.chainKey.toHexString(), keyIndex: UInt(senderKey.keyIndex), messageKeys: [])
Storage.setClosedGroupRatchet(for: groupPublicKey, senderPublicKey: senderPublicKey, ratchet: ratchet, using: transaction)
}
// MARK: - General
@objc(establishSessionsIfNeededWithClosedGroupMembers:transaction:)

View File

@ -9,11 +9,7 @@ public final class DisplayNameUtilities2 : NSObject {
// Case 1: The public key belongs to the user themselves
if publicKey == getUserHexEncodedPublicKey() { return SSKEnvironment.shared.profileManager.localProfileName() ?? publicKey }
// Case 2: The given thread is an open group
var openGroup: OpenGroup? = nil
Storage.read { transaction in
openGroup = LokiDatabaseUtilities.getPublicChat(for: threadID, in: transaction)
}
if let openGroup = openGroup {
if let openGroup = Storage.shared.getOpenGroup(for: threadID) {
var displayName: String? = nil
Storage.read { transaction in
displayName = transaction.object(forKey: publicKey, inCollection: openGroup.id) as! String?

View File

@ -64,7 +64,7 @@
}
- (PreKeyRecord *)generateAndStorePreKeyRecordForContact:(NSString *)hexEncodedPublicKey {
[LKLogger print:[NSString stringWithFormat:@"[Loki] Generating new pre key record for: %@.", hexEncodedPublicKey]];
NSLog([NSString stringWithFormat:@"[Loki] Generating new pre key record for: %@.", hexEncodedPublicKey]);
OWSAssertDebug(hexEncodedPublicKey.length > 0);
NSArray<PreKeyRecord *> *records = [self generatePreKeyRecords:1];
@ -94,7 +94,7 @@
[signedPreKeyRecord markAsAcceptedByService];
[self storeSignedPreKey:signedPreKeyRecord.Id signedPreKeyRecord:signedPreKeyRecord];
[self setCurrentSignedPrekeyId:signedPreKeyRecord.Id];
[LKLogger print:@"[Loki] Signed pre key refreshed successfully."];
NSLog(@"[Loki] Signed pre key refreshed successfully.");
}
SignedPreKeyRecord *_Nullable signedPreKey = self.currentSignedPreKey;
@ -127,14 +127,14 @@
data:preKeyBundle.signedPreKeyPublic]) {
@throw [NSException exceptionWithName:InvalidKeyException reason:@"KeyIsNotValidlySigned" userInfo:nil];
}
[LKLogger print:[NSString stringWithFormat:@"[Loki] Generated a new pre key bundle for: %@.", hexEncodedPublicKey]];
NSLog([NSString stringWithFormat:@"[Loki] Generated a new pre key bundle for: %@.", hexEncodedPublicKey]);
return preKeyBundle;
} @catch (NSException *exception) {
failureCount += 1;
forceClean = YES;
}
}
[LKLogger print:[NSString stringWithFormat:@"[Loki] Failed to generate a valid pre key bundle for: %@.", hexEncodedPublicKey]];
NSLog([NSString stringWithFormat:@"[Loki] Failed to generate a valid pre key bundle for: %@.", hexEncodedPublicKey]);
return nil;
}
@ -144,14 +144,14 @@
- (void)setPreKeyBundle:(PreKeyBundle *)bundle forContact:(NSString *)hexEncodedPublicKey transaction:(YapDatabaseReadWriteTransaction *)transaction {
[transaction setObject:bundle forKey:hexEncodedPublicKey inCollection:LKPreKeyBundleCollection];
[LKLogger print:[NSString stringWithFormat:@"[Loki] Stored pre key bundle from: %@.", hexEncodedPublicKey]];
NSLog([NSString stringWithFormat:@"[Loki] Stored pre key bundle from: %@.", hexEncodedPublicKey]);
// FIXME: I don't think the line below is good for anything
[transaction.connection flushTransactionsWithCompletionQueue:dispatch_get_main_queue() completionBlock:^{ }];
}
- (void)removePreKeyBundleForContact:(NSString *)hexEncodedPublicKey transaction:(YapDatabaseReadWriteTransaction *)transaction {
[transaction removeObjectForKey:hexEncodedPublicKey inCollection:LKPreKeyBundleCollection];
[LKLogger print:[NSString stringWithFormat:@"[Loki] Removed pre key bundle from: %@.", hexEncodedPublicKey]];
NSLog([NSString stringWithFormat:@"[Loki] Removed pre key bundle from: %@.", hexEncodedPublicKey]);
}
# pragma mark - Open Groups

View File

@ -408,10 +408,7 @@ typedef void (^ProfileManagerFailureBlock)(NSError *error);
OWSAssertDebug(successBlock);
OWSAssertDebug(failureBlock);
__block NSDictionary *publicChats;
[SSKEnvironment.shared.primaryStorage.dbReadConnection readWithBlock:^(YapDatabaseReadTransaction *transaction) {
publicChats = [LKDatabaseUtilities getAllPublicChats:transaction];
}];
NSDictionary *publicChats = [LKStorage.shared getAllUserOpenGroups];
NSSet *servers = [NSSet setWithArray:[publicChats.allValues map:^NSString *(SNOpenGroup *publicChat) { return publicChat.server; }]];

View File

@ -6,7 +6,7 @@ import PromiseKit
public final class PublicChatManager : NSObject {
private let storage = OWSPrimaryStorage.shared()
@objc public var chats: [String:OpenGroup] = [:]
private var pollers: [String:PublicChatPoller] = [:]
private var pollers: [String:OpenGroupPoller] = [:]
private var isPolling = false
private var userHexEncodedPublicKey: String? {
@ -35,7 +35,7 @@ public final class PublicChatManager : NSObject {
if let poller = pollers[threadID] {
poller.startIfNeeded()
} else {
let poller = PublicChatPoller(for: publicChat)
let poller = OpenGroupPoller(for: publicChat)
poller.startIfNeeded()
pollers[threadID] = poller
}
@ -73,7 +73,7 @@ public final class PublicChatManager : NSObject {
let thread = TSGroupThread.getOrCreateThread(with: model, transaction: transaction)
// Save the group chat
LokiDatabaseUtilities.setPublicChat(chat, for: thread.uniqueId!, in: transaction)
Storage.shared.setOpenGroup(chat, for: thread.uniqueId!, using: transaction)
}
// Update chats and pollers
@ -88,20 +88,18 @@ public final class PublicChatManager : NSObject {
}
@objc func refreshChatsAndPollers() {
storage.dbReadConnection.read { transaction in
let newChats = LokiDatabaseUtilities.getAllPublicChats(in: transaction)
// Remove any chats that don't exist in the database
let removedChatThreadIds = self.chats.keys.filter { !newChats.keys.contains($0) }
removedChatThreadIds.forEach { threadID in
let poller = self.pollers.removeValue(forKey: threadID)
poller?.stop()
}
// Only append to chats if we have a thread for the chat
self.chats = newChats.filter { (threadID, group) in
return TSGroupThread.fetch(uniqueId: threadID, transaction: transaction) != nil
}
let newChats = Storage.shared.getAllUserOpenGroups()
// Remove any chats that don't exist in the database
let removedChatThreadIds = self.chats.keys.filter { !newChats.keys.contains($0) }
removedChatThreadIds.forEach { threadID in
let poller = self.pollers.removeValue(forKey: threadID)
poller?.stop()
}
// Only append to chats if we have a thread for the chat
self.chats = newChats.filter { (threadID, group) in
return TSGroupThread.fetch(uniqueId: threadID) != nil
}
if (isPolling) { startPollersIfNeeded() }
@ -113,13 +111,13 @@ public final class PublicChatManager : NSObject {
// Reset the last message cache
if let chat = self.chats[threadId] {
Storage.write { transaction in
Storage.clearAllData(for: chat.channel, on: chat.server, using: transaction)
Storage.shared.clearAllData(for: chat.channel, on: chat.server, using: transaction)
}
}
// Remove the chat from the db
Storage.writeSync { transaction in
LokiDatabaseUtilities.removePublicChat(for: threadId, in: transaction)
Storage.write { transaction in
Storage.shared.removeOpenGroup(for: threadId, using: transaction)
}
refreshChatsAndPollers()

View File

@ -96,10 +96,10 @@ public final class SessionManagementProtocol : NSObject {
let hasSession = storage.containsSession(publicKey, deviceId: Int32(1), protocolContext: transaction)
guard !hasSession else { return }
// Check that we didn't already send a session request
let hasSentSessionRequest = (Storage.getSessionRequestSentTimestamp(for: publicKey) > 0)
let hasSentSessionRequest = (Storage.shared.getSessionRequestSentTimestamp(for: publicKey) > 0)
let hasSentSessionRequestExpired = SessionManagementProtocol.hasSentSessionRequestExpired(for: publicKey)
if hasSentSessionRequestExpired {
Storage.setSessionRequestSentTimestamp(for: publicKey, to: 0, using: transaction)
Storage.shared.setSessionRequestSentTimestamp(for: publicKey, to: 0, using: transaction)
}
guard !hasSentSessionRequest || hasSentSessionRequestExpired else { return }
// Create the thread if needed
@ -107,7 +107,7 @@ public final class SessionManagementProtocol : NSObject {
thread.save(with: transaction)
// Send the session request
print("[Loki] Sending session request to: \(publicKey).")
Storage.setSessionRequestSentTimestamp(for: publicKey, to: NSDate.ows_millisecondTimeStamp(), using: transaction)
Storage.shared.setSessionRequestSentTimestamp(for: publicKey, to: NSDate.ows_millisecondTimeStamp(), using: transaction)
let sessionRequest = SessionRequest()
sessionRequest.preKeyBundle = storage.generatePreKeyBundle(forContact: publicKey)
MessageSender.send(sessionRequest, in: thread, using: transaction)
@ -185,8 +185,8 @@ public final class SessionManagementProtocol : NSObject {
}
private static func shouldProcessSessionRequest(from publicKey: String, at timestamp: UInt64) -> Bool {
let sentTimestamp = Storage.getSessionRequestSentTimestamp(for: publicKey)
let processedTimestamp = Storage.getSessionRequestProcessedTimestamp(for: publicKey)
let sentTimestamp = Storage.shared.getSessionRequestSentTimestamp(for: publicKey)
let processedTimestamp = Storage.shared.getSessionRequestProcessedTimestamp(for: publicKey)
let restorationTimestamp = UInt64(storage.getRestorationTime() * 1000)
return timestamp > sentTimestamp && timestamp > processedTimestamp && timestamp > restorationTimestamp
}

View File

@ -27,12 +27,10 @@ public final class SessionMetaProtocol : NSObject {
guard let thread = thread as? TSGroupThread else { preconditionFailure("Can't get destinations for group message in non-group thread.") }
var result: Set<String> = []
if thread.isOpenGroup {
storage.dbReadConnection.read { transaction in
if let openGroup = LokiDatabaseUtilities.getPublicChat(for: thread.uniqueId!, in: transaction) {
result = [ openGroup.server ] // Aim the message at the open group server
} else {
// Should never occur
}
if let openGroup = Storage.shared.getOpenGroup(for: thread.uniqueId!) {
result = [ openGroup.server ] // Aim the message at the open group server
} else {
// Should never occur
}
} else {
if let groupThread = thread as? TSGroupThread, groupThread.usesSharedSenderKeys {

View File

@ -1,12 +0,0 @@
// For some reason NSLog doesn't seem to work from SignalServiceKit. This is a workaround to still allow debugging from Obj-C.
@objc(LKLogger)
public final class ObjC_Logger : NSObject {
private override init() { }
@objc public static func print(_ message: String) {
Swift.print(message)
}
}

View File

@ -1,11 +1,11 @@
public extension ECKeyPair {
@objc public var hexEncodedPrivateKey: String {
@objc var hexEncodedPrivateKey: String {
return privateKey.map { String(format: "%02hhx", $0) }.joined()
}
@objc public var hexEncodedPublicKey: String {
@objc var hexEncodedPublicKey: String {
// Prefixing with "05" is necessary for what seems to be a sort of Signal public key versioning system
return "05" + publicKey.map { String(format: "%02hhx", $0) }.joined()
}