Merge pull request #333 from oxen-io/multi-device

Multi Device & Account Restoration V2
This commit is contained in:
Niels Andriesse 2021-01-28 15:57:31 +11:00 committed by GitHub
commit 9a76e45c82
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
35 changed files with 1558 additions and 1068 deletions

View File

@ -289,6 +289,7 @@
B8CA011525A293800091AF73 /* Configuration.swift in Sources */ = {isa = PBXBuildFile; fileRef = B8CA011425A293800091AF73 /* Configuration.swift */; };
B8CA011F25A2939F0091AF73 /* SharedSenderKeys.swift in Sources */ = {isa = PBXBuildFile; fileRef = B8CA011E25A2939F0091AF73 /* SharedSenderKeys.swift */; };
B8CA014125A293EE0091AF73 /* Storage.swift in Sources */ = {isa = PBXBuildFile; fileRef = B8CA014025A293EE0091AF73 /* Storage.swift */; };
B8CADAE925AFADF400AAFA15 /* OpenGroupManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = C3AAFFCB25AE92150089E6DD /* OpenGroupManager.swift */; };
B8CCF6352396005F0091D419 /* SpaceMono-Regular.ttf in Resources */ = {isa = PBXBuildFile; fileRef = B8CCF6342396005F0091D419 /* SpaceMono-Regular.ttf */; };
B8CCF63723961D6D0091D419 /* NewPrivateChatVC.swift in Sources */ = {isa = PBXBuildFile; fileRef = B8CCF63623961D6D0091D419 /* NewPrivateChatVC.swift */; };
B8CCF63F23975CFB0091D419 /* JoinPublicChatVC.swift in Sources */ = {isa = PBXBuildFile; fileRef = B8CCF63E23975CFB0091D419 /* JoinPublicChatVC.swift */; };
@ -482,7 +483,6 @@
C33FDC95255A582000E217F9 /* OWSFailedMessagesJob.h in Headers */ = {isa = PBXBuildFile; fileRef = C33FDADB255A580400E217F9 /* OWSFailedMessagesJob.h */; settings = {ATTRIBUTES = (Public, ); }; };
C33FDC96255A582000E217F9 /* NSObject+Casting.h in Headers */ = {isa = PBXBuildFile; fileRef = C33FDADC255A580400E217F9 /* NSObject+Casting.h */; settings = {ATTRIBUTES = (Public, ); }; };
C33FDC98255A582000E217F9 /* SwiftSingletons.swift in Sources */ = {isa = PBXBuildFile; fileRef = C33FDADE255A580400E217F9 /* SwiftSingletons.swift */; };
C33FDC99255A582000E217F9 /* PublicChatManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = C33FDADF255A580400E217F9 /* PublicChatManager.swift */; };
C33FDC9A255A582000E217F9 /* ByteParser.m in Sources */ = {isa = PBXBuildFile; fileRef = C33FDAE0255A580400E217F9 /* ByteParser.m */; };
C33FDCA2255A582000E217F9 /* OWSMessageUtils.h in Headers */ = {isa = PBXBuildFile; fileRef = C33FDAE8255A580500E217F9 /* OWSMessageUtils.h */; settings = {ATTRIBUTES = (Public, ); }; };
C33FDCC7255A582000E217F9 /* NSArray+OWS.m in Sources */ = {isa = PBXBuildFile; fileRef = C33FDB0D255A580800E217F9 /* NSArray+OWS.m */; };
@ -544,6 +544,9 @@
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 */; };
C35D0DA125AE582D00B6BF49 /* MultiDeviceVC.swift in Sources */ = {isa = PBXBuildFile; fileRef = C35D0DA025AE582D00B6BF49 /* MultiDeviceVC.swift */; };
C35D0DAB25AE5BDE00B6BF49 /* SettingRow.swift in Sources */ = {isa = PBXBuildFile; fileRef = C35D0DAA25AE5BDE00B6BF49 /* SettingRow.swift */; };
C35D0DB525AE5F1200B6BF49 /* UIEdgeInsets.swift in Sources */ = {isa = PBXBuildFile; fileRef = C35D0DB425AE5F1200B6BF49 /* UIEdgeInsets.swift */; };
C35E8AAE2485E51D00ACB629 /* IP2Country.swift in Sources */ = {isa = PBXBuildFile; fileRef = C35E8AAD2485E51D00ACB629 /* IP2Country.swift */; };
C3645350252449260045C478 /* VoiceMessageView.swift in Sources */ = {isa = PBXBuildFile; fileRef = C364534F252449260045C478 /* VoiceMessageView.swift */; };
C364535C252467900045C478 /* AudioUtilities.swift in Sources */ = {isa = PBXBuildFile; fileRef = C364535B252467900045C478 /* AudioUtilities.swift */; };
@ -712,6 +715,8 @@
C3A7225E2558C38D0043A11F /* Promise+Retaining.swift in Sources */ = {isa = PBXBuildFile; fileRef = C3A7225D2558C38D0043A11F /* Promise+Retaining.swift */; };
C3A7229C2558E4310043A11F /* OpenGroupMessage+Conversion.swift in Sources */ = {isa = PBXBuildFile; fileRef = C3A7229B2558E4310043A11F /* OpenGroupMessage+Conversion.swift */; };
C3AABDDF2553ECF00042FF4C /* Array+Description.swift in Sources */ = {isa = PBXBuildFile; fileRef = C3C2A5D12553860800C340D1 /* Array+Description.swift */; };
C3AAFFE825AE975D0089E6DD /* ConfigurationMessage+Convenience.swift in Sources */ = {isa = PBXBuildFile; fileRef = C3AAFFDE25AE96FF0089E6DD /* ConfigurationMessage+Convenience.swift */; };
C3AAFFF225AE99710089E6DD /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = C3AAFFF125AE99710089E6DD /* AppDelegate.swift */; };
C3BBE0762554CDA60050F1E3 /* Configuration.swift in Sources */ = {isa = PBXBuildFile; fileRef = C3BBE0752554CDA60050F1E3 /* Configuration.swift */; };
C3BBE0802554CDD70050F1E3 /* Storage.swift in Sources */ = {isa = PBXBuildFile; fileRef = C3BBE07F2554CDD70050F1E3 /* Storage.swift */; };
C3BBE0A72554D4DE0050F1E3 /* Promise+Retrying.swift in Sources */ = {isa = PBXBuildFile; fileRef = C3C2A5D62553860B00C340D1 /* Promise+Retrying.swift */; };
@ -788,6 +793,7 @@
C3D9E4FD256778E30040E4F3 /* NSData+Image.h in Headers */ = {isa = PBXBuildFile; fileRef = C33FDB29255A580A00E217F9 /* NSData+Image.h */; settings = {ATTRIBUTES = (Public, ); }; };
C3D9E50E25677A510040E4F3 /* DataSource.h in Headers */ = {isa = PBXBuildFile; fileRef = C33FDB54255A580D00E217F9 /* DataSource.h */; settings = {ATTRIBUTES = (Public, ); }; };
C3D9E52725677DF20040E4F3 /* OWSThumbnailService.swift in Sources */ = {isa = PBXBuildFile; fileRef = C33FDAF1255A580500E217F9 /* OWSThumbnailService.swift */; };
C3DA9C0725AE7396008F7C7E /* ConfigurationMessage.swift in Sources */ = {isa = PBXBuildFile; fileRef = C3DA9C0625AE7396008F7C7E /* ConfigurationMessage.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 */; };
@ -1434,7 +1440,6 @@
C33FDADC255A580400E217F9 /* NSObject+Casting.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "NSObject+Casting.h"; sourceTree = "<group>"; };
C33FDADD255A580400E217F9 /* TSInfoMessage.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = TSInfoMessage.h; sourceTree = "<group>"; };
C33FDADE255A580400E217F9 /* SwiftSingletons.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SwiftSingletons.swift; sourceTree = "<group>"; };
C33FDADF255A580400E217F9 /* PublicChatManager.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PublicChatManager.swift; sourceTree = "<group>"; };
C33FDAE0255A580400E217F9 /* ByteParser.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ByteParser.m; sourceTree = "<group>"; };
C33FDAE1255A580400E217F9 /* OWSReadTracking.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = OWSReadTracking.h; sourceTree = "<group>"; };
C33FDAE4255A580400E217F9 /* TSAttachmentStream.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = TSAttachmentStream.h; sourceTree = "<group>"; };
@ -1588,6 +1593,9 @@
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>"; };
C35D0DA025AE582D00B6BF49 /* MultiDeviceVC.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MultiDeviceVC.swift; sourceTree = "<group>"; };
C35D0DAA25AE5BDE00B6BF49 /* SettingRow.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SettingRow.swift; sourceTree = "<group>"; };
C35D0DB425AE5F1200B6BF49 /* UIEdgeInsets.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UIEdgeInsets.swift; sourceTree = "<group>"; };
C35E8AA22485C72300ACB629 /* SwiftCSV.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = SwiftCSV.framework; path = ThirdParty/Carthage/Build/iOS/SwiftCSV.framework; sourceTree = "<group>"; };
C35E8AAD2485E51D00ACB629 /* IP2Country.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = IP2Country.swift; sourceTree = "<group>"; };
C364534F252449260045C478 /* VoiceMessageView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VoiceMessageView.swift; sourceTree = "<group>"; };
@ -1772,6 +1780,9 @@
C3A7225D2558C38D0043A11F /* Promise+Retaining.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Promise+Retaining.swift"; sourceTree = "<group>"; };
C3A7229B2558E4310043A11F /* OpenGroupMessage+Conversion.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "OpenGroupMessage+Conversion.swift"; sourceTree = "<group>"; };
C3AA6BB824CE8F1B002358B6 /* Migrating Translations from Android.md */ = {isa = PBXFileReference; lastKnownFileType = net.daringfireball.markdown; name = "Migrating Translations from Android.md"; path = "Meta/Translations/Migrating Translations from Android.md"; sourceTree = "<group>"; };
C3AAFFCB25AE92150089E6DD /* OpenGroupManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OpenGroupManager.swift; sourceTree = "<group>"; };
C3AAFFDE25AE96FF0089E6DD /* ConfigurationMessage+Convenience.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "ConfigurationMessage+Convenience.swift"; sourceTree = "<group>"; };
C3AAFFF125AE99710089E6DD /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = "<group>"; };
C3AECBEA24EF5244005743DE /* fa */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = fa; path = fa.lproj/Localizable.strings; sourceTree = "<group>"; };
C3B7845C25649DA600ADB2E7 /* TSIncomingMessage+Conversion.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "TSIncomingMessage+Conversion.swift"; sourceTree = "<group>"; };
C3BBE0752554CDA60050F1E3 /* Configuration.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Configuration.swift; sourceTree = "<group>"; };
@ -1830,6 +1841,7 @@
C3D9E40B25676C100040E4F3 /* Storage+Conformances.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Storage+Conformances.swift"; sourceTree = "<group>"; };
C3D9E41E25676C870040E4F3 /* OWSPrimaryStorageProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OWSPrimaryStorageProtocol.swift; sourceTree = "<group>"; };
C3D9E43025676D3D0040E4F3 /* Configuration.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Configuration.swift; sourceTree = "<group>"; };
C3DA9C0625AE7396008F7C7E /* ConfigurationMessage.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ConfigurationMessage.swift; sourceTree = "<group>"; };
C3DAB3232480CB2A00725F25 /* SRCopyableLabel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SRCopyableLabel.swift; sourceTree = "<group>"; };
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>"; };
@ -2321,6 +2333,7 @@
C33FDB3F255A580C00E217F9 /* String+SSK.swift */,
C3C2AC2D2553CBEB00C340D1 /* String+Trimming.swift */,
C38EF237255B6D65007E1867 /* UIDevice+featureSupport.swift */,
C35D0DB425AE5F1200B6BF49 /* UIEdgeInsets.swift */,
C38EF23D255B6D66007E1867 /* UIView+OWS.h */,
C38EF23E255B6D66007E1867 /* UIView+OWS.m */,
C38EF2EF255B6DBB007E1867 /* Weak.swift */,
@ -2367,6 +2380,7 @@
C31D1DE22521718E005D4DA8 /* UserSelectionVC.swift */,
34D2CCD82062E7D000CB1A14 /* OWSScreenLockUI.h */,
34D2CCD92062E7D000CB1A14 /* OWSScreenLockUI.m */,
C35D0DAA25AE5BDE00B6BF49 /* SettingRow.swift */,
);
path = Shared;
sourceTree = "<group>";
@ -2413,6 +2427,7 @@
C300A5D22554B05A00555489 /* TypingIndicator.swift */,
C34A977325A3E34A00852C71 /* ClosedGroupControlMessage.swift */,
C300A5E62554B07300555489 /* ExpirationTimerUpdate.swift */,
C3DA9C0625AE7396008F7C7E /* ConfigurationMessage.swift */,
);
path = "Control Messages";
sourceTree = "<group>";
@ -2789,6 +2804,7 @@
B886B4A62398B23E00211ABE /* QRCodeVC.swift */,
B86BD08523399CEF000F5AE3 /* SeedModal.swift */,
B8CCF6422397711F0091D419 /* SettingsVC.swift */,
C35D0DA025AE582D00B6BF49 /* MultiDeviceVC.swift */,
);
path = Settings;
sourceTree = "<group>";
@ -3143,7 +3159,6 @@
isa = PBXGroup;
children = (
C33FDB19255A580900E217F9 /* GroupUtilities.swift */,
C33FDADF255A580400E217F9 /* PublicChatManager.swift */,
C38EF3E5255B6DF4007E1867 /* ContactCellView.h */,
C38EF3D6255B6DEF007E1867 /* ContactCellView.m */,
C38EF3E6255B6DF4007E1867 /* ContactTableViewCell.h */,
@ -3177,6 +3192,7 @@
C38EF2E3255B6DB9007E1867 /* OWSUnreadIndicator.m */,
C33FDAE8255A580500E217F9 /* OWSMessageUtils.h */,
C33FDBD7255A581900E217F9 /* OWSMessageUtils.m */,
C3AAFFDE25AE96FF0089E6DD /* ConfigurationMessage+Convenience.swift */,
);
path = Messaging;
sourceTree = "<group>";
@ -3206,6 +3222,7 @@
C3A721362558BDFA0043A11F /* OpenGroupInfo.swift */,
C3A721342558BDF90043A11F /* OpenGroupMessage.swift */,
C3A7229B2558E4310043A11F /* OpenGroupMessage+Conversion.swift */,
C3AAFFCB25AE92150089E6DD /* OpenGroupManager.swift */,
);
path = "Open Groups";
sourceTree = "<group>";
@ -3528,6 +3545,7 @@
children = (
76EB03C218170B33006006FC /* AppDelegate.h */,
76EB03C318170B33006006FC /* AppDelegate.m */,
C3AAFFF125AE99710089E6DD /* AppDelegate.swift */,
34D99CE3217509C1000AFB39 /* AppEnvironment.swift */,
B8FF8E6025C10D8B004D1F22 /* Countries */,
34330A581E7875FB00DF2FB9 /* Fonts */,
@ -4637,7 +4655,6 @@
C38EF3C3255B6DE7007E1867 /* ImageEditorTextItem.swift in Sources */,
C33FDC7D255A582000E217F9 /* OWSDispatch.m in Sources */,
C38EF247255B6D67007E1867 /* NSAttributedString+OWS.m in Sources */,
C33FDC99255A582000E217F9 /* PublicChatManager.swift in Sources */,
C33FDD49255A582000E217F9 /* ParamParser.swift in Sources */,
C38EF35F255B6DCC007E1867 /* SelectRecipientViewController.m in Sources */,
C38EF3C5255B6DE7007E1867 /* OWSViewController+ImageEditor.swift in Sources */,
@ -4755,6 +4772,7 @@
B8C2B332256376F000551B4D /* ThreadUtil.m in Sources */,
C38EF40B255B6DF7007E1867 /* TappableStackView.swift in Sources */,
C38EF31D255B6DBF007E1867 /* UIImage+OWS.swift in Sources */,
C3AAFFE825AE975D0089E6DD /* ConfigurationMessage+Convenience.swift in Sources */,
C38EF359255B6DCC007E1867 /* SheetViewController.swift in Sources */,
C38EF386255B6DD2007E1867 /* AttachmentApprovalInputAccessoryView.swift in Sources */,
B8C2B2C82563685C00551B4D /* CircleView.swift in Sources */,
@ -4833,6 +4851,7 @@
C3D9E41F25676C870040E4F3 /* OWSPrimaryStorageProtocol.swift in Sources */,
C3BBE0A72554D4DE0050F1E3 /* Promise+Retrying.swift in Sources */,
B8856D7B256F14F4001CE70E /* UIView+OWS.m in Sources */,
C35D0DB525AE5F1200B6BF49 /* UIEdgeInsets.swift in Sources */,
C3D9E4F4256778AF0040E4F3 /* NSData+Image.m in Sources */,
C32C5E0C256DDAFA003C73A2 /* NSRegularExpression+SSK.swift in Sources */,
C3BBE0A92554D4DE0050F1E3 /* HTTP.swift in Sources */,
@ -4916,10 +4935,12 @@
B8AE760B25ABFB5A001A84D2 /* GeneralUtilities.m in Sources */,
C32C5C4F256DCC36003C73A2 /* Storage+OpenGroups.swift in Sources */,
B8B3207B258C22550020074B /* DisplayNameUtilities.swift in Sources */,
C3DA9C0725AE7396008F7C7E /* ConfigurationMessage.swift in Sources */,
B8856CEE256F1054001CE70E /* OWSAudioPlayer.m in Sources */,
C32C5EDC256DF501003C73A2 /* YapDatabaseConnection+OWS.m in Sources */,
C3BBE0762554CDA60050F1E3 /* Configuration.swift in Sources */,
B8B32033258B235D0020074B /* Storage+Contacts.swift in Sources */,
B8CADAE925AFADF400AAFA15 /* OpenGroupManager.swift in Sources */,
B8856D69256F141F001CE70E /* OWSWindowManager.m in Sources */,
C3D9E3BE25676AD70040E4F3 /* TSAttachmentPointer.m in Sources */,
C3ECBF7B257056B700EA7FCE /* Threading.swift in Sources */,
@ -5052,6 +5073,7 @@
B80A579F23DFF1F300876683 /* NewClosedGroupVC.swift in Sources */,
D221A09A169C9E5E00537ABF /* main.m in Sources */,
3496957221A301A100DCFE74 /* OWSBackup.m in Sources */,
C35D0DA125AE582D00B6BF49 /* MultiDeviceVC.swift in Sources */,
B86BD08623399CEF000F5AE3 /* SeedModal.swift in Sources */,
34E3E5681EC4B19400495BAC /* AudioProgressView.swift in Sources */,
34D1F0521F7E8EA30066283D /* GiphyDownloader.swift in Sources */,
@ -5060,6 +5082,7 @@
B82B408C239A068800A248E7 /* RegisterVC.swift in Sources */,
346129991FD1E4DA00532771 /* SignalApp.m in Sources */,
3496957121A301A100DCFE74 /* OWSBackupImportJob.m in Sources */,
C35D0DAB25AE5BDE00B6BF49 /* SettingRow.swift in Sources */,
34BECE301F7ABCF800D7438D /* GifPickerLayout.swift in Sources */,
C331FFFE2558FF3B00070591 /* ConversationCell.swift in Sources */,
C3DFFAC623E96F0D0058DAF8 /* Sheet.swift in Sources */,
@ -5134,6 +5157,7 @@
4CB5F26920F7D060004D1B42 /* MessageActions.swift in Sources */,
340FC8B5204DAC8D007AEB0F /* AboutTableViewController.m in Sources */,
C33100082558FF6D00070591 /* NewConversationButtonSet.swift in Sources */,
C3AAFFF225AE99710089E6DD /* AppDelegate.swift in Sources */,
B8BB82A5238F627000BA5194 /* HomeVC.swift in Sources */,
C31A6C5A247F214E001123EF /* UIView+Glow.swift in Sources */,
C31D1DE9252172D4005D4DA8 /* ContactUtilities.swift in Sources */,

View File

@ -174,6 +174,8 @@ final class NewClosedGroupVC : BaseVC, UITableViewDataSource, UITableViewDelegat
promise = MessageSender.createClosedGroup(name: name, members: selectedContacts, transaction: transaction)
}
let _ = promise.done(on: DispatchQueue.main) { thread in
let appDelegate = UIApplication.shared.delegate as! AppDelegate
appDelegate.forceSyncConfigurationNowIfNeeded().retainUntilComplete() // FIXME: It's probably cleaner to do this inside createClosedGroup(...)
self?.presentingViewController?.dismiss(animated: true, completion: nil)
SignalApp.shared().presentConversation(for: thread, action: .compose, animated: false)
}

View File

@ -354,45 +354,14 @@ final class HomeVC : BaseVC, UITableViewDataSource, UITableViewDelegate, UIViewC
func tableView(_ tableView: UITableView, editActionsForRowAt indexPath: IndexPath) -> [UITableViewRowAction]? {
guard let thread = self.thread(at: indexPath.row) else { return [] }
let openGroup = Storage.shared.getOpenGroup(for: thread.uniqueId!)
let delete = UITableViewRowAction(style: .destructive, title: NSLocalizedString("TXT_DELETE_TITLE", comment: "")) { [weak self] _, _ in
var message = NSLocalizedString("CONVERSATION_DELETE_CONFIRMATION_ALERT_MESSAGE", comment: "")
if let thread = thread as? TSGroupThread, thread.isClosedGroup, thread.groupModel.groupAdminIds.contains(getUserHexEncodedPublicKey()) {
message = "Because you are the creator of this group it will be deleted for everyone. This cannot be undone."
}
let alert = UIAlertController(title: NSLocalizedString("CONVERSATION_DELETE_CONFIRMATION_ALERT_TITLE", comment: ""), message: message, preferredStyle: .alert)
alert.addAction(UIAlertAction(title: NSLocalizedString("TXT_DELETE_TITLE", comment: ""), style: .destructive) { _ in
Storage.write { transaction in
Storage.shared.cancelPendingMessageSendJobs(for: thread.uniqueId!, using: transaction)
if let openGroup = openGroup {
var messageIDs: Set<String> = []
thread.enumerateInteractions(with: transaction) { interaction, _ in
messageIDs.insert(interaction.uniqueId!)
}
OWSPrimaryStorage.shared().updateMessageIDCollectionByPruningMessagesWithIDs(messageIDs, in: transaction)
transaction.removeObject(forKey: "\(openGroup.server).\(openGroup.channel)", inCollection: Storage.lastMessageServerIDCollection)
transaction.removeObject(forKey: "\(openGroup.server).\(openGroup.channel)", inCollection: Storage.lastDeletionServerIDCollection)
let _ = OpenGroupAPI.leave(openGroup.channel, on: openGroup.server)
thread.removeAllThreadInteractions(with: transaction)
thread.remove(with: transaction)
} else if let thread = thread as? TSGroupThread, thread.isClosedGroup == true {
let groupID = thread.groupModel.groupId
let groupPublicKey = LKGroupUtilities.getDecodedGroupID(groupID)
do {
try MessageSender.leave(groupPublicKey, using: transaction)
} catch {
// TODO: Handle
}
Storage.write { transaction in
thread.removeAllThreadInteractions(with: transaction)
thread.remove(with: transaction)
}
} else {
thread.removeAllThreadInteractions(with: transaction)
thread.remove(with: transaction)
}
}
NotificationCenter.default.post(name: .threadDeleted, object: nil, userInfo: [ "threadId" : thread.uniqueId! ])
alert.addAction(UIAlertAction(title: NSLocalizedString("TXT_DELETE_TITLE", comment: ""), style: .destructive) { [weak self] _ in
self?.delete(thread)
})
alert.addAction(UIAlertAction(title: NSLocalizedString("TXT_CANCEL_TITLE", comment: ""), style: .default) { _ in })
guard let self = self else { return }
@ -419,6 +388,29 @@ final class HomeVC : BaseVC, UITableViewDataSource, UITableViewDelegate, UIViewC
}
}
private func delete(_ thread: TSThread) {
let openGroup = Storage.shared.getOpenGroup(for: thread.uniqueId!)
Storage.write { transaction in
Storage.shared.cancelPendingMessageSendJobs(for: thread.uniqueId!, using: transaction)
if let openGroup = openGroup {
OpenGroupManager.shared.delete(openGroup, associatedWith: thread, using: transaction)
} else if let thread = thread as? TSGroupThread, thread.isClosedGroup == true {
let groupID = thread.groupModel.groupId
let groupPublicKey = LKGroupUtilities.getDecodedGroupID(groupID)
do {
try MessageSender.leave(groupPublicKey, using: transaction)
} catch {
// TODO: Handle
}
thread.removeAllThreadInteractions(with: transaction)
thread.remove(with: transaction)
} else {
thread.removeAllThreadInteractions(with: transaction)
thread.remove(with: transaction)
}
}
}
@objc private func openSettings() {
let settingsVC = SettingsVC()
let navigationController = OWSNavigationController(rootViewController: settingsVC)

View File

@ -429,6 +429,7 @@ static NSTimeInterval launchStartedAt;
if (CurrentAppContext().isMainApp) {
[SNJobQueue.shared resumePendingJobs];
[self syncConfigurationIfNeeded];
}
});
}
@ -753,10 +754,10 @@ static NSTimeInterval launchStartedAt;
- (void)startOpenGroupPollersIfNeeded
{
[LKPublicChatManager.shared startPollersIfNeeded];
[SNOpenGroupManager.shared startPolling];
}
- (void)stopOpenGroupPollers { [LKPublicChatManager.shared stopPollers]; }
- (void)stopOpenGroupPollers { [SNOpenGroupManager.shared stopPolling]; }
# pragma mark - App Mode
@ -810,7 +811,7 @@ static NSTimeInterval launchStartedAt;
[self stopPoller];
[self stopClosedGroupPoller];
[self stopOpenGroupPollers];
[LKPublicChatManager.shared stopPollers];
[SNOpenGroupManager.shared stopPolling];
BOOL wasUnlinked = [NSUserDefaults.standardUserDefaults boolForKey:@"wasUnlinked"];
[SignalApp resetAppData:^{
// Resetting the data clears the old user defaults. We need to restore the unlink default.

View File

@ -0,0 +1,32 @@
import PromiseKit
extension AppDelegate {
@objc(syncConfigurationIfNeeded)
func syncConfigurationIfNeeded() {
let userDefaults = UserDefaults.standard
let lastSync = userDefaults[.lastConfigurationSync] ?? .distantPast
guard Date().timeIntervalSince(lastSync) > 2 * 24 * 60 * 60 else { return } // Sync every 2 days
let configurationMessage = ConfigurationMessage.getCurrent()
let destination = Message.Destination.contact(publicKey: getUserHexEncodedPublicKey())
Storage.shared.write { transaction in
let job = MessageSendJob(message: configurationMessage, destination: destination)
JobQueue.shared.add(job, using: transaction)
}
userDefaults[.lastConfigurationSync] = Date()
}
func forceSyncConfigurationNowIfNeeded() -> Promise<Void> {
let configurationMessage = ConfigurationMessage.getCurrent()
let destination = Message.Destination.contact(publicKey: getUserHexEncodedPublicKey())
let (promise, seal) = Promise<Void>.pending()
Storage.writeSync { transaction in
MessageSender.send(configurationMessage, to: destination, using: transaction).done {
seal.fulfill(())
}.catch { _ in
seal.fulfill(()) // Fulfill even if this failed; the configuration in the swarm should be at most 2 days old
}.retainUntilComplete()
}
return promise
}
}

View File

@ -125,42 +125,33 @@ final class JoinPublicChatVC : BaseVC, UIPageViewControllerDataSource, UIPageVie
joinPublicChatIfPossible(with: chatURL)
}
fileprivate func joinPublicChatIfPossible(with chatURL: String) {
fileprivate func joinPublicChatIfPossible(with urlAsString: String) {
guard !isJoining else { return }
guard let url = URL(string: chatURL), let scheme = url.scheme, scheme == "https", url.host != nil else {
guard let url = URL(string: urlAsString), let scheme = url.scheme, scheme == "https", url.host != nil else {
return showError(title: NSLocalizedString("invalid_url", comment: ""), message: "Please check the URL you entered and try again")
}
isJoining = true
let channelID: UInt64 = 1
let urlAsString = url.absoluteString
let userPublicKey = getUserHexEncodedPublicKey()
let profileManager = OWSProfileManager.shared()
let displayName = profileManager.profileNameForRecipient(withID: userPublicKey)
let profilePictureURL = profileManager.profilePictureURL()
let profileKey = profileManager.localProfileKey().keyData
Storage.writeSync { transaction in
transaction.removeObject(forKey: "\(urlAsString).\(channelID)", inCollection: Storage.lastMessageServerIDCollection)
transaction.removeObject(forKey: "\(urlAsString).\(channelID)", inCollection: Storage.lastDeletionServerIDCollection)
}
ModalActivityIndicatorViewController.present(fromViewController: navigationController!, canCancel: false) { [weak self] _ in
PublicChatManager.shared.addChat(server: urlAsString, channel: channelID)
.done(on: DispatchQueue.main) { [weak self] _ in
let _ = OpenGroupAPI.setDisplayName(to: displayName, on: urlAsString)
let _ = OpenGroupAPI.setProfilePictureURL(to: profilePictureURL, using: profileKey, on: urlAsString)
let _ = OpenGroupAPI.join(channelID, on: urlAsString)
self?.presentingViewController!.dismiss(animated: true, completion: nil)
}
.catch(on: DispatchQueue.main) { [weak self] error in
self?.dismiss(animated: true, completion: nil) // Dismiss the loader
var title = "Couldn't Join"
var message = ""
if case OnionRequestAPI.Error.httpRequestFailedAtDestination(let statusCode, _) = error, statusCode == 401 || statusCode == 403 {
title = "Unauthorized"
message = "Please ask the open group operator to add you to the group."
Storage.shared.write(with: { transaction in
OpenGroupManager.shared.add(with: urlAsString, using: transaction)
.done(on: DispatchQueue.main) { [weak self] _ in
self?.presentingViewController!.dismiss(animated: true, completion: nil)
}
self?.isJoining = false
self?.showError(title: title, message: message)
}
.catch(on: DispatchQueue.main) { [weak self] error in
self?.dismiss(animated: true, completion: nil) // Dismiss the loader
var title = "Couldn't Join"
var message = ""
if case OnionRequestAPI.Error.httpRequestFailedAtDestination(let statusCode, _) = error, statusCode == 401 || statusCode == 403 {
title = "Unauthorized"
message = "Please ask the open group operator to add you to the group."
}
self?.isJoining = false
self?.showError(title: title, message: message)
}
}, completion: {
let appDelegate = UIApplication.shared.delegate as! AppDelegate
appDelegate.forceSyncConfigurationNowIfNeeded().retainUntilComplete() // FIXME: It's probably cleaner to do this inside addOpenGroup(...)
})
}
}

View File

@ -0,0 +1,165 @@
final class MultiDeviceVC : BaseVC {
private let mnemonic: String = {
let collection = OWSPrimaryStorageIdentityKeyStoreCollection
let hexEncodedSeed: String! = OWSIdentityManager.shared().dbConnection.object(forKey: "LKLokiSeed", inCollection: collection) as! String?
return Mnemonic.encode(hexEncodedString: hexEncodedSeed)
}()
// MARK: UI Components
private lazy var toggleLabel: UILabel = {
let result = UILabel()
result.textColor = Colors.text
result.font = .systemFont(ofSize: Values.mediumFontSize)
result.text = "Enable multi device"
return result
}()
private lazy var toggle: UISwitch = {
let result = UISwitch()
result.onTintColor = Colors.accent
result.isOn = UserDefaults.standard[.isUsingMultiDevice]
return result
}()
private lazy var stepsRow: SettingRow = {
let result = SettingRow(autoSize: true)
result.isHidden = true
return result
}()
private lazy var stepsLabel1: UILabel = {
let result = UILabel()
result.textColor = Colors.text
result.font = .systemFont(ofSize: Values.smallFontSize)
result.text = """
1. Clear your other device if it currently has an account on it (Settings > Clear Data).
2. On the landing page, click "Continue your Session".
3. Enter the following words when prompted:
"""
result.numberOfLines = 0
result.lineBreakMode = .byWordWrapping
return result
}()
private lazy var mnemonicLabel: UILabel = {
let result = UILabel()
result.textColor = Colors.text
result.font = Fonts.spaceMono(ofSize: Values.smallFontSize)
result.text = mnemonic
result.numberOfLines = 0
result.lineBreakMode = .byWordWrapping
result.textAlignment = .center
return result
}()
private lazy var copyButton: Button = {
let result = Button(style: .prominentOutline, size: .medium)
result.setTitle(NSLocalizedString("copy", comment: ""), for: UIControl.State.normal)
result.addTarget(self, action: #selector(copyMnemonic), for: UIControl.Event.touchUpInside)
return result
}()
private lazy var stepsLabel2: UILabel = {
let result = UILabel()
result.textColor = Colors.text
result.font = .systemFont(ofSize: Values.smallFontSize)
result.text = """
4. Enter your display name.
5. That's it!
"""
result.numberOfLines = 0
result.lineBreakMode = .byWordWrapping
return result
}()
// MARK: Initialization
override func viewDidLoad() {
super.viewDidLoad()
setUpUI()
}
private func setUpUI() {
setUpGradientBackground()
setUpNavBarStyle()
setNavBarTitle("Multi Device (Beta)")
// Back button
let backButton = UIBarButtonItem(title: "Back", style: .plain, target: nil, action: nil)
backButton.tintColor = Colors.text
navigationItem.backBarButtonItem = backButton
// Toggle
toggle.addTarget(self, action: #selector(handleToggle), for: UIControl.Event.valueChanged)
let toggleStackView = UIStackView(arrangedSubviews: [ toggleLabel, toggle ])
toggleStackView.axis = .horizontal
toggleStackView.spacing = Values.mediumSpacing
toggleStackView.alignment = .center
let toggleRow = SettingRow()
toggleRow.contentView.addSubview(toggleStackView)
toggleStackView.pin(to: toggleRow.contentView, withInset: Values.mediumSpacing)
// Steps
let mnemonicLabelContainer = UIView()
mnemonicLabelContainer.addSubview(mnemonicLabel)
mnemonicLabel.pin(to: mnemonicLabelContainer, withInset: isIPhone6OrSmaller ? 4 : Values.smallSpacing)
mnemonicLabelContainer.layer.cornerRadius = Values.textFieldCornerRadius
mnemonicLabelContainer.layer.borderWidth = Values.borderThickness
mnemonicLabelContainer.layer.borderColor = Colors.text.cgColor
let stepsLabel1Container = UIView()
stepsLabel1Container.addSubview(stepsLabel1)
stepsLabel1.pin(.leading, to: .leading, of: stepsLabel1Container, withInset: Values.smallSpacing)
stepsLabel1Container.pin(.trailing, to: .trailing, of: stepsLabel1, withInset: Values.smallSpacing)
stepsLabel1.pin([ UIView.VerticalEdge.top, UIView.VerticalEdge.bottom ], to: stepsLabel1Container)
let stepsLabel2Container = UIView()
stepsLabel2Container.addSubview(stepsLabel2)
stepsLabel2.pin(.leading, to: .leading, of: stepsLabel2Container, withInset: Values.smallSpacing)
stepsLabel2Container.pin(.trailing, to: .trailing, of: stepsLabel2, withInset: Values.smallSpacing)
stepsLabel2.pin([ UIView.VerticalEdge.top, UIView.VerticalEdge.bottom ], to: stepsLabel2Container)
let stepsStackView = UIStackView(arrangedSubviews: [ stepsLabel1Container, mnemonicLabelContainer, copyButton, stepsLabel2Container ])
stepsStackView.axis = .vertical
stepsStackView.spacing = Values.mediumSpacing
stepsRow.contentView.addSubview(stepsStackView)
stepsStackView.pin(to: stepsRow.contentView, withInset: Values.mediumSpacing)
// Main stack view
let mainStackView = UIStackView(arrangedSubviews: [ toggleRow, stepsRow ])
mainStackView.axis = .vertical
mainStackView.spacing = Values.mediumSpacing
mainStackView.isLayoutMarginsRelativeArrangement = true
mainStackView.layoutMargins = UIEdgeInsets(uniform: Values.mediumSpacing)
mainStackView.set(.width, to: UIScreen.main.bounds.width)
// Scroll view
let scrollView = UIScrollView()
scrollView.showsVerticalScrollIndicator = false
scrollView.addSubview(mainStackView)
mainStackView.pin(to: scrollView)
view.addSubview(scrollView)
scrollView.pin(to: view)
}
// MARK: Updating
@objc private func enableCopyButton() {
copyButton.isUserInteractionEnabled = true
UIView.transition(with: copyButton, duration: 0.25, options: .transitionCrossDissolve, animations: {
self.copyButton.setTitle(NSLocalizedString("copy", comment: ""), for: UIControl.State.normal)
}, completion: nil)
}
// MARK: Interaction
@objc private func handleToggle() {
stepsRow.isHidden = !toggle.isOn
UserDefaults.standard[.isUsingMultiDevice] = toggle.isOn
let appDelegate = UIApplication.shared.delegate as! AppDelegate
appDelegate.forceSyncConfigurationNowIfNeeded().retainUntilComplete()
}
@objc private func copyMnemonic() {
UIPasteboard.general.string = mnemonic
copyButton.isUserInteractionEnabled = false
UIView.transition(with: copyButton, duration: 0.25, options: .transitionCrossDissolve, animations: {
self.copyButton.setTitle("Copied", for: UIControl.State.normal)
}, completion: nil)
Timer.scheduledTimer(timeInterval: 4, target: self, selector: #selector(enableCopyButton), userInfo: nil, repeats: false)
}
}

View File

@ -50,8 +50,14 @@ final class NukeDataModal : Modal {
// MARK: Interaction
@objc private func nuke() {
func proceed() {
UserDefaults.removeAll() // Not done in the nuke data implementation as unlinking requires this to happen later
NotificationCenter.default.post(name: .dataNukeRequested, object: nil)
let appDelegate = UIApplication.shared.delegate as! AppDelegate
ModalActivityIndicatorViewController.present(fromViewController: self, canCancel: false) { [weak self] _ in
appDelegate.forceSyncConfigurationNowIfNeeded().done(on: DispatchQueue.main) {
self?.dismiss(animated: true, completion: nil) // Dismiss the loader
UserDefaults.removeAll() // Not done in the nuke data implementation as unlinking requires this to happen later
NotificationCenter.default.post(name: .dataNukeRequested, object: nil)
}.retainUntilComplete()
}
}
if KeyPairUtilities.hasV2KeyPair() {
proceed()

View File

@ -59,16 +59,8 @@ final class SeedModal : Modal {
buttonStackView.axis = .horizontal
buttonStackView.spacing = Values.mediumSpacing
buttonStackView.distribution = .fillEqually
// Set up explanation label
let disclaimerLabel = UILabel()
disclaimerLabel.textColor = Colors.text.withAlphaComponent(Values.unimportantElementOpacity)
disclaimerLabel.font = .systemFont(ofSize: 10)
disclaimerLabel.text = "It is not possible to use the same Session ID on multiple devices simultaneously"
disclaimerLabel.numberOfLines = 0
disclaimerLabel.lineBreakMode = .byWordWrapping
disclaimerLabel.textAlignment = .center
// Set up stack view
let stackView = UIStackView(arrangedSubviews: [ titleLabel, mnemonicLabelContainer, explanationLabel, buttonStackView, disclaimerLabel ])
let stackView = UIStackView(arrangedSubviews: [ titleLabel, mnemonicLabelContainer, explanationLabel, buttonStackView ])
stackView.axis = .vertical
stackView.spacing = Values.largeSpacing
contentView.addSubview(stackView)

View File

@ -181,6 +181,8 @@ final class SettingsVC : BaseVC, AvatarViewHelperDelegate {
getSeparator(),
getSettingButton(withTitle: NSLocalizedString("vc_settings_notifications_button_title", comment: ""), color: Colors.text, action: #selector(showNotificationSettings)),
getSeparator(),
getSettingButton(withTitle: "Multi Device (Beta)", color: Colors.text, action: #selector(showMultiDeviceOptions)),
getSeparator(),
getSettingButton(withTitle: "Invite", color: Colors.text, action: #selector(sendInvitation)),
getSeparator()
]
@ -405,6 +407,11 @@ final class SettingsVC : BaseVC, AvatarViewHelperDelegate {
navigationController!.pushViewController(notificationSettingsVC, animated: true)
}
@objc private func showMultiDeviceOptions() {
let multiDeviceVC = MultiDeviceVC()
navigationController!.pushViewController(multiDeviceVC, animated: true)
}
@objc private func sendInvitation() {
let invitation = "Hey, I've been using Session to chat with complete privacy and security. Come join me! Download it at https://getsession.org/. My Session ID is \(getUserHexEncodedPublicKey())!"
let shareVC = UIActivityViewController(activityItems: [ invitation ], applicationActivities: nil)

View File

@ -0,0 +1,51 @@
final class SettingRow : UIView {
private let autoSize: Bool
lazy var contentView: UIView = {
let result = UIView()
result.backgroundColor = Colors.buttonBackground
result.layer.cornerRadius = 8
result.layer.masksToBounds = true
return result
}()
init(autoSize: Bool) {
self.autoSize = autoSize
super.init(frame: CGRect.zero)
setUpUI()
}
override init(frame: CGRect) {
autoSize = false
super.init(frame: frame)
setUpUI()
}
required init?(coder: NSCoder) {
autoSize = false
super.init(coder: coder)
setUpUI()
}
private func setUpUI() {
// Height
if !autoSize {
let height = Values.defaultSettingRowHeight
set(.height, to: height)
}
// Shadow
layer.shadowColor = UIColor.black.cgColor
layer.shadowOffset = CGSize.zero
layer.shadowOpacity = 0.4
layer.shadowRadius = 4
// Content view
addSubview(contentView)
contentView.pin(to: self)
}
override func layoutSubviews() {
super.layoutSubviews()
layer.shadowPath = UIBezierPath(roundedRect: bounds, cornerRadius: 8).cgPath
}
}

View File

@ -27,16 +27,31 @@ extension Storage {
/// Returns the ID of the `TSIncomingMessage` that was constructed.
public func persist(_ message: VisibleMessage, quotedMessage: TSQuotedMessage?, linkPreview: OWSLinkPreview?, groupPublicKey: String?, openGroupID: String?, using transaction: Any) -> String? {
let transaction = transaction as! YapDatabaseReadWriteTransaction
guard let threadID = getOrCreateThread(for: message.sender!, groupPublicKey: groupPublicKey, openGroupID: openGroupID, using: transaction),
guard let threadID = getOrCreateThread(for: message.syncTarget ?? message.sender!, groupPublicKey: groupPublicKey, openGroupID: openGroupID, using: transaction),
let thread = TSThread.fetch(uniqueId: threadID, transaction: transaction) else { return nil }
let message = TSIncomingMessage.from(message, quotedMessage: quotedMessage, linkPreview: linkPreview, associatedWith: thread)
message.save(with: transaction)
message.attachments(with: transaction).forEach { attachment in
attachment.albumMessageId = message.uniqueId!
let tsMessage: TSMessage
if message.sender == getUserPublicKey() {
let tsOutgoingMessage = TSOutgoingMessage.from(message, associatedWith: thread, using: transaction)
var recipients: [String] = []
if let syncTarget = message.syncTarget {
recipients.append(syncTarget)
} else if let thread = thread as? TSGroupThread, thread.isClosedGroup {
recipients = thread.groupModel.groupMemberIds
}
recipients.forEach { recipient in
tsOutgoingMessage.update(withSentRecipient: recipient, wasSentByUD: true, transaction: transaction)
}
tsMessage = tsOutgoingMessage
} else {
tsMessage = TSIncomingMessage.from(message, quotedMessage: quotedMessage, linkPreview: linkPreview, associatedWith: thread)
}
tsMessage.save(with: transaction)
tsMessage.attachments(with: transaction).forEach { attachment in
attachment.albumMessageId = tsMessage.uniqueId!
attachment.save(with: transaction)
}
DispatchQueue.main.async { message.touch() } // FIXME: Hack for a thread updating issue
return message.uniqueId!
DispatchQueue.main.async { tsMessage.touch() } // FIXME: Hack for a thread updating issue
return tsMessage.uniqueId!
}
/// Returns the IDs of the saved attachments.
@ -49,22 +64,22 @@ extension Storage {
}
/// Also touches the associated message.
public func setAttachmentState(to state: TSAttachmentPointerState, for pointer: TSAttachmentPointer, associatedWith tsIncomingMessageID: String, using transaction: Any) {
public func setAttachmentState(to state: TSAttachmentPointerState, for pointer: TSAttachmentPointer, associatedWith tsMessageID: String, using transaction: Any) {
let transaction = transaction as! YapDatabaseReadWriteTransaction
// Workaround for some YapDatabase funkiness where pointer at this point can actually be a TSAttachmentStream
guard pointer.responds(to: #selector(setter: TSAttachmentPointer.state)) else { return }
pointer.state = state
pointer.save(with: transaction)
guard let tsIncomingMessage = TSIncomingMessage.fetch(uniqueId: tsIncomingMessageID, transaction: transaction) else { return }
tsIncomingMessage.touch(with: transaction)
guard let tsMessage = TSMessage.fetch(uniqueId: tsMessageID, transaction: transaction) else { return }
tsMessage.touch(with: transaction)
}
/// Also touches the associated message.
public func persist(_ stream: TSAttachmentStream, associatedWith tsIncomingMessageID: String, using transaction: Any) {
public func persist(_ stream: TSAttachmentStream, associatedWith tsMessageID: String, using transaction: Any) {
let transaction = transaction as! YapDatabaseReadWriteTransaction
stream.save(with: transaction)
guard let tsIncomingMessage = TSIncomingMessage.fetch(uniqueId: tsIncomingMessageID, transaction: transaction) else { return }
tsIncomingMessage.touch(with: transaction)
guard let tsMessage = TSMessage.fetch(uniqueId: tsMessageID, transaction: transaction) else { return }
tsMessage.touch(with: transaction)
}
private static let receivedMessageTimestampsCollection = "ReceivedMessageTimestampsCollection"
@ -78,6 +93,16 @@ extension Storage {
}
return result
}
public func removeReceivedMessageTimestamps(_ timestamps: Set<UInt64>, using transaction: Any) {
var receivedMessageTimestamps = getReceivedMessageTimestamps(using: transaction)
timestamps.forEach { timestamp in
guard let index = receivedMessageTimestamps.firstIndex(of: timestamp) else { return }
receivedMessageTimestamps.remove(at: index)
}
let transaction = transaction as! YapDatabaseReadWriteTransaction
transaction.setObject(receivedMessageTimestamps, forKey: "receivedMessageTimestamps", inCollection: Storage.receivedMessageTimestampsCollection)
}
public func addReceivedMessageTimestamp(_ timestamp: UInt64, using transaction: Any) {
var receivedMessageTimestamps = getReceivedMessageTimestamps(using: transaction)

View File

@ -116,16 +116,6 @@ extension Storage {
(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

View File

@ -4,7 +4,7 @@ import SignalCoreKit
public final class AttachmentDownloadJob : NSObject, Job, NSCoding { // NSObject/NSCoding conformance is needed for YapDatabase compatibility
public let attachmentID: String
public let tsIncomingMessageID: String
public let tsMessageID: String
public var delegate: JobDelegate?
public var id: String?
public var failureCount: UInt = 0
@ -24,25 +24,25 @@ public final class AttachmentDownloadJob : NSObject, Job, NSCoding { // NSObject
public static let maxFailureCount: UInt = 20
// MARK: Initialization
public init(attachmentID: String, tsIncomingMessageID: String) {
public init(attachmentID: String, tsMessageID: String) {
self.attachmentID = attachmentID
self.tsIncomingMessageID = tsIncomingMessageID
self.tsMessageID = tsMessageID
}
// MARK: Coding
public init?(coder: NSCoder) {
guard let attachmentID = coder.decodeObject(forKey: "attachmentID") as! String?,
let tsIncomingMessageID = coder.decodeObject(forKey: "tsIncomingMessageID") as! String?,
let tsMessageID = coder.decodeObject(forKey: "tsIncomingMessageID") as! String?,
let id = coder.decodeObject(forKey: "id") as! String? else { return nil }
self.attachmentID = attachmentID
self.tsIncomingMessageID = tsIncomingMessageID
self.tsMessageID = tsMessageID
self.id = id
self.failureCount = coder.decodeObject(forKey: "failureCount") as! UInt? ?? 0
}
public func encode(with coder: NSCoder) {
coder.encode(attachmentID, forKey: "attachmentID")
coder.encode(tsIncomingMessageID, forKey: "tsIncomingMessageID")
coder.encode(tsMessageID, forKey: "tsIncomingMessageID")
coder.encode(id, forKey: "id")
coder.encode(failureCount, forKey: "failureCount")
}
@ -54,21 +54,21 @@ public final class AttachmentDownloadJob : NSObject, Job, NSCoding { // NSObject
}
let storage = SNMessagingKitConfiguration.shared.storage
storage.write(with: { transaction in
storage.setAttachmentState(to: .downloading, for: pointer, associatedWith: self.tsIncomingMessageID, using: transaction)
storage.setAttachmentState(to: .downloading, for: pointer, associatedWith: self.tsMessageID, using: transaction)
}, completion: { })
let temporaryFilePath = URL(fileURLWithPath: OWSTemporaryDirectoryAccessibleAfterFirstAuth() + UUID().uuidString)
let handleFailure: (Swift.Error) -> Void = { error in // Intentionally capture self
OWSFileSystem.deleteFile(temporaryFilePath.absoluteString)
if let error = error as? Error, case .noAttachment = error {
storage.write(with: { transaction in
storage.setAttachmentState(to: .failed, for: pointer, associatedWith: self.tsIncomingMessageID, using: transaction)
storage.setAttachmentState(to: .failed, for: pointer, associatedWith: self.tsMessageID, using: transaction)
}, completion: { })
self.handlePermanentFailure(error: error)
} else if let error = error as? DotNetAPI.Error, case .parsingFailed = error {
// No need to retry if the response is invalid. Most likely this means we (incorrectly)
// got a "Cannot GET ..." error from the file server.
storage.write(with: { transaction in
storage.setAttachmentState(to: .failed, for: pointer, associatedWith: self.tsIncomingMessageID, using: transaction)
storage.setAttachmentState(to: .failed, for: pointer, associatedWith: self.tsMessageID, using: transaction)
}, completion: { })
self.handlePermanentFailure(error: error)
} else {
@ -99,7 +99,7 @@ public final class AttachmentDownloadJob : NSObject, Job, NSCoding { // NSObject
}
OWSFileSystem.deleteFile(temporaryFilePath.absoluteString)
storage.write { transaction in
storage.persist(stream, associatedWith: self.tsIncomingMessageID, using: transaction)
storage.persist(stream, associatedWith: self.tsMessageID, using: transaction)
}
}.catch(on: DispatchQueue.global()) { error in
handleFailure(error)

View File

@ -11,6 +11,13 @@ public final class ClosedGroupControlMessage : ControlMessage {
}
}
public override var isSelfSendValid: Bool {
switch kind {
case .new: return false
default: return true
}
}
// MARK: Kind
public enum Kind : CustomStringConvertible {
case new(publicKey: Data, name: String, encryptionKeyPair: ECKeyPair, members: [Data], admins: [Data])
@ -212,7 +219,7 @@ public final class ClosedGroupControlMessage : ControlMessage {
closedGroupControlMessage = SNProtoDataMessageClosedGroupControlMessage.builder(type: .new)
closedGroupControlMessage.setPublicKey(publicKey)
closedGroupControlMessage.setName(name)
let encryptionKeyPairAsProto = SNProtoDataMessageClosedGroupControlMessageKeyPair.builder(publicKey: encryptionKeyPair.publicKey, privateKey: encryptionKeyPair.privateKey)
let encryptionKeyPairAsProto = SNProtoKeyPair.builder(publicKey: encryptionKeyPair.publicKey, privateKey: encryptionKeyPair.privateKey)
do {
closedGroupControlMessage.setEncryptionKeyPair(try encryptionKeyPairAsProto.build())
} catch {

View File

@ -0,0 +1,148 @@
import SessionUtilitiesKit
@objc(SNConfigurationMessage)
public final class ConfigurationMessage : ControlMessage {
public var closedGroups: Set<ClosedGroup> = []
public var openGroups: Set<String> = []
public override var ttl: UInt64 { 4 * 24 * 60 * 60 * 1000 }
public override var isSelfSendValid: Bool { true }
// MARK: Initialization
public override init() { super.init() }
public init(closedGroups: Set<ClosedGroup>, openGroups: Set<String>) {
super.init()
self.closedGroups = closedGroups
self.openGroups = openGroups
}
// MARK: Coding
public required init?(coder: NSCoder) {
super.init(coder: coder)
if let closedGroups = coder.decodeObject(forKey: "closedGroups") as! Set<ClosedGroup>? { self.closedGroups = closedGroups }
if let openGroups = coder.decodeObject(forKey: "openGroups") as! Set<String>? { self.openGroups = openGroups }
}
public override func encode(with coder: NSCoder) {
super.encode(with: coder)
coder.encode(closedGroups, forKey: "closedGroups")
coder.encode(openGroups, forKey: "openGroups")
}
// MARK: Proto Conversion
public override class func fromProto(_ proto: SNProtoContent) -> ConfigurationMessage? {
guard let configurationProto = proto.configurationMessage else { return nil }
let closedGroups = Set(configurationProto.closedGroups.compactMap { ClosedGroup.fromProto($0) })
let openGroups = Set(configurationProto.openGroups)
return ConfigurationMessage(closedGroups: closedGroups, openGroups: openGroups)
}
public override func toProto(using transaction: YapDatabaseReadWriteTransaction) -> SNProtoContent? {
let configurationProto = SNProtoConfigurationMessage.builder()
configurationProto.setClosedGroups(closedGroups.compactMap { $0.toProto() })
configurationProto.setOpenGroups([String](openGroups))
let contentProto = SNProtoContent.builder()
do {
contentProto.setConfigurationMessage(try configurationProto.build())
return try contentProto.build()
} catch {
SNLog("Couldn't construct configuration proto from: \(self).")
return nil
}
}
// MARK: Description
public override var description: String {
"""
ConfigurationMessage(
closedGroups: \([ClosedGroup](closedGroups).prettifiedDescription)
openGroups: \([String](openGroups).prettifiedDescription)
)
"""
}
}
// MARK: Closed Group
extension ConfigurationMessage {
@objc(SNClosedGroup)
public final class ClosedGroup : NSObject, NSCoding { // NSObject/NSCoding conformance is needed for YapDatabase compatibility
public let publicKey: String
public let name: String
public let encryptionKeyPair: ECKeyPair
public let members: Set<String>
public let admins: Set<String>
public var isValid: Bool { !members.isEmpty && !admins.isEmpty }
public init(publicKey: String, name: String, encryptionKeyPair: ECKeyPair, members: Set<String>, admins: Set<String>) {
self.publicKey = publicKey
self.name = name
self.encryptionKeyPair = encryptionKeyPair
self.members = members
self.admins = admins
}
public required init?(coder: NSCoder) {
guard let publicKey = coder.decodeObject(forKey: "publicKey") as! String?,
let name = coder.decodeObject(forKey: "name") as! String?,
let encryptionKeyPair = coder.decodeObject(forKey: "encryptionKeyPair") as! ECKeyPair?,
let members = coder.decodeObject(forKey: "members") as! Set<String>?,
let admins = coder.decodeObject(forKey: "admins") as! Set<String>? else { return nil }
self.publicKey = publicKey
self.name = name
self.encryptionKeyPair = encryptionKeyPair
self.members = members
self.admins = admins
}
public func encode(with coder: NSCoder) {
coder.encode(publicKey, forKey: "publicKey")
coder.encode(name, forKey: "name")
coder.encode(encryptionKeyPair, forKey: "encryptionKeyPair")
coder.encode(members, forKey: "members")
coder.encode(admins, forKey: "admins")
}
public static func fromProto(_ proto: SNProtoConfigurationMessageClosedGroup) -> ClosedGroup? {
guard let publicKey = proto.publicKey?.toHexString(),
let name = proto.name,
let encryptionKeyPairAsProto = proto.encryptionKeyPair else { return nil }
let encryptionKeyPair: ECKeyPair
do {
encryptionKeyPair = try ECKeyPair(publicKeyData: encryptionKeyPairAsProto.publicKey, privateKeyData: encryptionKeyPairAsProto.privateKey)
} catch {
SNLog("Couldn't construct closed group from proto: \(self).")
return nil
}
let members = Set(proto.members.map { $0.toHexString() })
let admins = Set(proto.admins.map { $0.toHexString() })
return ClosedGroup(publicKey: publicKey, name: name, encryptionKeyPair: encryptionKeyPair, members: members, admins: admins)
}
public func toProto() -> SNProtoConfigurationMessageClosedGroup? {
let result = SNProtoConfigurationMessageClosedGroup.builder()
result.setPublicKey(Data(hex: publicKey))
result.setName(name)
do {
let encryptionKeyPairAsProto = try SNProtoKeyPair.builder(publicKey: encryptionKeyPair.publicKey, privateKey: encryptionKeyPair.privateKey).build()
result.setEncryptionKeyPair(encryptionKeyPairAsProto)
} catch {
SNLog("Couldn't construct closed group proto from: \(self).")
return nil
}
result.setMembers(members.map { Data(hex: $0) })
result.setAdmins(admins.map { Data(hex: $0) })
do {
return try result.build()
} catch {
SNLog("Couldn't construct closed group proto from: \(self).")
return nil
}
}
public override var description: String { name }
}
}

View File

@ -12,6 +12,7 @@ public class Message : NSObject, NSCoding { // NSObject/NSCoding conformance is
public var openGroupServerMessageID: UInt64?
public var ttl: UInt64 { 2 * 24 * 60 * 60 * 1000 }
public var isSelfSendValid: Bool { false }
public override init() { }

View File

@ -4,15 +4,25 @@ import SessionUtilitiesKit
@objc(from:associatedWith:)
static func from(_ visibleMessage: VisibleMessage, associatedWith thread: TSThread) -> TSOutgoingMessage {
return from(visibleMessage, associatedWith: thread, using: nil)
}
static func from(_ visibleMessage: VisibleMessage, associatedWith thread: TSThread, using transaction: YapDatabaseReadWriteTransaction? = nil) -> TSOutgoingMessage {
var expiration: UInt32 = 0
if let disappearingMessagesConfiguration = OWSDisappearingMessagesConfiguration.fetch(uniqueId: thread.uniqueId!) {
let disappearingMessagesConfigurationOrNil: OWSDisappearingMessagesConfiguration?
if let transaction = transaction {
disappearingMessagesConfigurationOrNil = OWSDisappearingMessagesConfiguration.fetch(uniqueId: thread.uniqueId!, transaction: transaction)
} else {
disappearingMessagesConfigurationOrNil = OWSDisappearingMessagesConfiguration.fetch(uniqueId: thread.uniqueId!)
}
if let disappearingMessagesConfiguration = disappearingMessagesConfigurationOrNil {
expiration = disappearingMessagesConfiguration.isEnabled ? disappearingMessagesConfiguration.durationSeconds : 0
}
return TSOutgoingMessage(
outgoingMessageWithTimestamp: visibleMessage.sentTimestamp!,
in: thread,
messageBody: visibleMessage.text,
attachmentIds: NSMutableArray(),
attachmentIds: NSMutableArray(array: visibleMessage.attachmentIDs),
expiresInSeconds: expiration,
expireStartedAt: 0,
isVoiceMessage: false,

View File

@ -2,6 +2,10 @@ import SessionUtilitiesKit
@objc(SNVisibleMessage)
public final class VisibleMessage : Message {
/// In the case of a sync message, the public key of the person the message was targeted at.
///
/// - Note: `nil` if this isn't a sync message.
public var syncTarget: String?
@objc public var text: String?
@objc public var attachmentIDs: [String] = []
@objc public var quote: Quote?
@ -9,6 +13,8 @@ public final class VisibleMessage : Message {
@objc public var contact: Contact?
@objc public var profile: Profile?
public override var isSelfSendValid: Bool { true }
// MARK: Initialization
public override init() { super.init() }
@ -23,6 +29,7 @@ public final class VisibleMessage : Message {
// MARK: Coding
public required init?(coder: NSCoder) {
super.init(coder: coder)
if let syncTarget = coder.decodeObject(forKey: "syncTarget") as! String? { self.syncTarget = syncTarget }
if let text = coder.decodeObject(forKey: "body") as! String? { self.text = text }
if let attachmentIDs = coder.decodeObject(forKey: "attachments") as! [String]? { self.attachmentIDs = attachmentIDs }
if let quote = coder.decodeObject(forKey: "quote") as! Quote? { self.quote = quote }
@ -33,6 +40,7 @@ public final class VisibleMessage : Message {
public override func encode(with coder: NSCoder) {
super.encode(with: coder)
coder.encode(syncTarget, forKey: "syncTarget")
coder.encode(text, forKey: "body")
coder.encode(attachmentIDs, forKey: "attachments")
coder.encode(quote, forKey: "quote")
@ -51,6 +59,7 @@ public final class VisibleMessage : Message {
if let linkPreviewProto = dataMessage.preview.first, let linkPreview = LinkPreview.fromProto(linkPreviewProto) { result.linkPreview = linkPreview }
// TODO: Contact
if let profile = Profile.fromProto(dataMessage) { result.profile = profile }
result.syncTarget = dataMessage.syncTarget
return result
}
@ -101,6 +110,10 @@ public final class VisibleMessage : Message {
SNLog("Couldn't construct visible message proto from: \(self).")
return nil
}
// Sync target
if let syncTarget = syncTarget {
dataMessage.setSyncTarget(syncTarget)
}
// Build
do {
proto.setDataMessage(try dataMessage.build())

View File

@ -0,0 +1,99 @@
import PromiseKit
@objc(SNOpenGroupManager)
public final class OpenGroupManager : NSObject {
private var pollers: [String:OpenGroupPoller] = [:]
private var isPolling = false
// MARK: Error
public enum Error : LocalizedError {
case invalidURL
public var errorDescription: String? {
switch self {
case .invalidURL: return "Invalid URL."
}
}
}
// MARK: Initialization
@objc public static let shared = OpenGroupManager()
private override init() { }
// MARK: Polling
@objc public func startPolling() {
guard !isPolling else { return }
isPolling = true
let openGroups = Storage.shared.getAllUserOpenGroups()
for (_, openGroup) in openGroups {
if let poller = pollers[openGroup.id] { poller.stop() } // Should never occur
let poller = OpenGroupPoller(for: openGroup)
poller.startIfNeeded()
pollers[openGroup.id] = poller
}
}
@objc public func stopPolling() {
pollers.forEach { (_, openGroupPoller) in openGroupPoller.stop() }
pollers.removeAll()
}
// MARK: Adding & Removing
public func add(with url: String, using transaction: Any) -> Promise<Void> {
guard let url = URL(string: url), let scheme = url.scheme, scheme == "https", url.host != nil else {
return Promise(error: Error.invalidURL)
}
let channel: UInt64 = 1
let server = url.absoluteString
let userPublicKey = getUserHexEncodedPublicKey()
let profileManager = SSKEnvironment.shared.profileManager
let displayName = profileManager.profileNameForRecipient(withID: userPublicKey)
let profilePictureURL = profileManager.profilePictureURL()
let profileKey = profileManager.localProfileKey().keyData
Storage.shared.removeLastMessageServerID(for: channel, on: server, using: transaction)
Storage.shared.removeLastDeletionServerID(for: channel, on: server, using: transaction)
return OpenGroupAPI.getInfo(for: channel, on: server).done { info in
let openGroup = OpenGroup(channel: channel, server: server, displayName: info.displayName, isDeletable: true)!
let groupID = LKGroupUtilities.getEncodedOpenGroupIDAsData(openGroup.id)
let model = TSGroupModel(title: openGroup.displayName, memberIds: [ userPublicKey ], image: nil, groupId: groupID, groupType: .openGroup, adminIds: [])
Storage.shared.write(with: { transaction in
let thread = TSGroupThread.getOrCreateThread(with: model, transaction: transaction as! YapDatabaseReadWriteTransaction)
Storage.shared.setOpenGroup(openGroup, for: thread.uniqueId!, using: transaction)
}, completion: {
let _ = OpenGroupAPI.setDisplayName(to: displayName, on: server)
let _ = OpenGroupAPI.setProfilePictureURL(to: profilePictureURL, using: profileKey, on: server)
let _ = OpenGroupAPI.join(channel, on: server)
if let poller = OpenGroupManager.shared.pollers[openGroup.id] {
poller.stop()
OpenGroupManager.shared.pollers[openGroup.id] = nil
}
let poller = OpenGroupPoller(for: openGroup)
poller.startIfNeeded()
OpenGroupManager.shared.pollers[openGroup.id] = poller
})
}
}
public func delete(_ openGroup: OpenGroup, associatedWith thread: TSThread, using transaction: YapDatabaseReadWriteTransaction) {
if let poller = pollers[openGroup.id] {
poller.stop()
pollers[openGroup.id] = nil
}
var messageIDs: Set<String> = []
var messageTimestamps: Set<UInt64> = []
thread.enumerateInteractions(with: transaction) { interaction, _ in
messageIDs.insert(interaction.uniqueId!)
messageTimestamps.insert(interaction.timestamp)
}
SNMessagingKitConfiguration.shared.storage.updateMessageIDCollectionByPruningMessagesWithIDs(messageIDs, using: transaction)
Storage.shared.removeReceivedMessageTimestamps(messageTimestamps, using: transaction)
Storage.shared.removeLastMessageServerID(for: openGroup.channel, on: openGroup.server, using: transaction)
Storage.shared.removeLastDeletionServerID(for: openGroup.channel, on: openGroup.server, using: transaction)
let _ = OpenGroupAPI.leave(openGroup.channel, on: openGroup.server)
Storage.shared.removeOpenGroupPublicKey(for: openGroup.server, using: transaction)
thread.removeAllThreadInteractions(with: transaction)
thread.remove(with: transaction)
Storage.shared.removeOpenGroup(for: thread.uniqueId!, using: transaction)
}
}

View File

@ -354,15 +354,15 @@ extension SNProtoTypingMessage.SNProtoTypingMessageBuilder {
if let _value = dataMessage {
builder.setDataMessage(_value)
}
if let _value = syncMessage {
builder.setSyncMessage(_value)
}
if let _value = receiptMessage {
builder.setReceiptMessage(_value)
}
if let _value = typingMessage {
builder.setTypingMessage(_value)
}
if let _value = configurationMessage {
builder.setConfigurationMessage(_value)
}
return builder
}
@ -376,10 +376,6 @@ extension SNProtoTypingMessage.SNProtoTypingMessageBuilder {
proto.dataMessage = valueParam.proto
}
@objc public func setSyncMessage(_ valueParam: SNProtoSyncMessage) {
proto.syncMessage = valueParam.proto
}
@objc public func setReceiptMessage(_ valueParam: SNProtoReceiptMessage) {
proto.receiptMessage = valueParam.proto
}
@ -388,6 +384,10 @@ extension SNProtoTypingMessage.SNProtoTypingMessageBuilder {
proto.typingMessage = valueParam.proto
}
@objc public func setConfigurationMessage(_ valueParam: SNProtoConfigurationMessage) {
proto.configurationMessage = valueParam.proto
}
@objc public func build() throws -> SNProtoContent {
return try SNProtoContent.parseProto(proto)
}
@ -401,22 +401,22 @@ extension SNProtoTypingMessage.SNProtoTypingMessageBuilder {
@objc public let dataMessage: SNProtoDataMessage?
@objc public let syncMessage: SNProtoSyncMessage?
@objc public let receiptMessage: SNProtoReceiptMessage?
@objc public let typingMessage: SNProtoTypingMessage?
@objc public let configurationMessage: SNProtoConfigurationMessage?
private init(proto: SessionProtos_Content,
dataMessage: SNProtoDataMessage?,
syncMessage: SNProtoSyncMessage?,
receiptMessage: SNProtoReceiptMessage?,
typingMessage: SNProtoTypingMessage?) {
typingMessage: SNProtoTypingMessage?,
configurationMessage: SNProtoConfigurationMessage?) {
self.proto = proto
self.dataMessage = dataMessage
self.syncMessage = syncMessage
self.receiptMessage = receiptMessage
self.typingMessage = typingMessage
self.configurationMessage = configurationMessage
}
@objc
@ -435,11 +435,6 @@ extension SNProtoTypingMessage.SNProtoTypingMessageBuilder {
dataMessage = try SNProtoDataMessage.parseProto(proto.dataMessage)
}
var syncMessage: SNProtoSyncMessage? = nil
if proto.hasSyncMessage {
syncMessage = try SNProtoSyncMessage.parseProto(proto.syncMessage)
}
var receiptMessage: SNProtoReceiptMessage? = nil
if proto.hasReceiptMessage {
receiptMessage = try SNProtoReceiptMessage.parseProto(proto.receiptMessage)
@ -450,15 +445,20 @@ extension SNProtoTypingMessage.SNProtoTypingMessageBuilder {
typingMessage = try SNProtoTypingMessage.parseProto(proto.typingMessage)
}
var configurationMessage: SNProtoConfigurationMessage? = nil
if proto.hasConfigurationMessage {
configurationMessage = try SNProtoConfigurationMessage.parseProto(proto.configurationMessage)
}
// MARK: - Begin Validation Logic for SNProtoContent -
// MARK: - End Validation Logic for SNProtoContent -
let result = SNProtoContent(proto: proto,
dataMessage: dataMessage,
syncMessage: syncMessage,
receiptMessage: receiptMessage,
typingMessage: typingMessage)
typingMessage: typingMessage,
configurationMessage: configurationMessage)
return result
}
@ -595,6 +595,118 @@ extension SNProtoClosedGroupCiphertextMessageWrapper.SNProtoClosedGroupCiphertex
#endif
// MARK: - SNProtoKeyPair
@objc public class SNProtoKeyPair: NSObject {
// MARK: - SNProtoKeyPairBuilder
@objc public class func builder(publicKey: Data, privateKey: Data) -> SNProtoKeyPairBuilder {
return SNProtoKeyPairBuilder(publicKey: publicKey, privateKey: privateKey)
}
// asBuilder() constructs a builder that reflects the proto's contents.
@objc public func asBuilder() -> SNProtoKeyPairBuilder {
let builder = SNProtoKeyPairBuilder(publicKey: publicKey, privateKey: privateKey)
return builder
}
@objc public class SNProtoKeyPairBuilder: NSObject {
private var proto = SessionProtos_KeyPair()
@objc fileprivate override init() {}
@objc fileprivate init(publicKey: Data, privateKey: Data) {
super.init()
setPublicKey(publicKey)
setPrivateKey(privateKey)
}
@objc public func setPublicKey(_ valueParam: Data) {
proto.publicKey = valueParam
}
@objc public func setPrivateKey(_ valueParam: Data) {
proto.privateKey = valueParam
}
@objc public func build() throws -> SNProtoKeyPair {
return try SNProtoKeyPair.parseProto(proto)
}
@objc public func buildSerializedData() throws -> Data {
return try SNProtoKeyPair.parseProto(proto).serializedData()
}
}
fileprivate let proto: SessionProtos_KeyPair
@objc public let publicKey: Data
@objc public let privateKey: Data
private init(proto: SessionProtos_KeyPair,
publicKey: Data,
privateKey: Data) {
self.proto = proto
self.publicKey = publicKey
self.privateKey = privateKey
}
@objc
public func serializedData() throws -> Data {
return try self.proto.serializedData()
}
@objc public class func parseData(_ serializedData: Data) throws -> SNProtoKeyPair {
let proto = try SessionProtos_KeyPair(serializedData: serializedData)
return try parseProto(proto)
}
fileprivate class func parseProto(_ proto: SessionProtos_KeyPair) throws -> SNProtoKeyPair {
guard proto.hasPublicKey else {
throw SNProtoError.invalidProtobuf(description: "\(logTag) missing required field: publicKey")
}
let publicKey = proto.publicKey
guard proto.hasPrivateKey else {
throw SNProtoError.invalidProtobuf(description: "\(logTag) missing required field: privateKey")
}
let privateKey = proto.privateKey
// MARK: - Begin Validation Logic for SNProtoKeyPair -
// MARK: - End Validation Logic for SNProtoKeyPair -
let result = SNProtoKeyPair(proto: proto,
publicKey: publicKey,
privateKey: privateKey)
return result
}
@objc public override var debugDescription: String {
return "\(proto)"
}
}
#if DEBUG
extension SNProtoKeyPair {
@objc public func serializedDataIgnoringErrors() -> Data? {
return try! self.serializedData()
}
}
extension SNProtoKeyPair.SNProtoKeyPairBuilder {
@objc public func buildIgnoringErrors() -> SNProtoKeyPair? {
return try! self.build()
}
}
#endif
// MARK: - SNProtoDataMessageQuoteQuotedAttachment
@objc public class SNProtoDataMessageQuoteQuotedAttachment: NSObject {
@ -2172,118 +2284,6 @@ extension SNProtoDataMessageLokiProfile.SNProtoDataMessageLokiProfileBuilder {
#endif
// MARK: - SNProtoDataMessageClosedGroupControlMessageKeyPair
@objc public class SNProtoDataMessageClosedGroupControlMessageKeyPair: NSObject {
// MARK: - SNProtoDataMessageClosedGroupControlMessageKeyPairBuilder
@objc public class func builder(publicKey: Data, privateKey: Data) -> SNProtoDataMessageClosedGroupControlMessageKeyPairBuilder {
return SNProtoDataMessageClosedGroupControlMessageKeyPairBuilder(publicKey: publicKey, privateKey: privateKey)
}
// asBuilder() constructs a builder that reflects the proto's contents.
@objc public func asBuilder() -> SNProtoDataMessageClosedGroupControlMessageKeyPairBuilder {
let builder = SNProtoDataMessageClosedGroupControlMessageKeyPairBuilder(publicKey: publicKey, privateKey: privateKey)
return builder
}
@objc public class SNProtoDataMessageClosedGroupControlMessageKeyPairBuilder: NSObject {
private var proto = SessionProtos_DataMessage.ClosedGroupControlMessage.KeyPair()
@objc fileprivate override init() {}
@objc fileprivate init(publicKey: Data, privateKey: Data) {
super.init()
setPublicKey(publicKey)
setPrivateKey(privateKey)
}
@objc public func setPublicKey(_ valueParam: Data) {
proto.publicKey = valueParam
}
@objc public func setPrivateKey(_ valueParam: Data) {
proto.privateKey = valueParam
}
@objc public func build() throws -> SNProtoDataMessageClosedGroupControlMessageKeyPair {
return try SNProtoDataMessageClosedGroupControlMessageKeyPair.parseProto(proto)
}
@objc public func buildSerializedData() throws -> Data {
return try SNProtoDataMessageClosedGroupControlMessageKeyPair.parseProto(proto).serializedData()
}
}
fileprivate let proto: SessionProtos_DataMessage.ClosedGroupControlMessage.KeyPair
@objc public let publicKey: Data
@objc public let privateKey: Data
private init(proto: SessionProtos_DataMessage.ClosedGroupControlMessage.KeyPair,
publicKey: Data,
privateKey: Data) {
self.proto = proto
self.publicKey = publicKey
self.privateKey = privateKey
}
@objc
public func serializedData() throws -> Data {
return try self.proto.serializedData()
}
@objc public class func parseData(_ serializedData: Data) throws -> SNProtoDataMessageClosedGroupControlMessageKeyPair {
let proto = try SessionProtos_DataMessage.ClosedGroupControlMessage.KeyPair(serializedData: serializedData)
return try parseProto(proto)
}
fileprivate class func parseProto(_ proto: SessionProtos_DataMessage.ClosedGroupControlMessage.KeyPair) throws -> SNProtoDataMessageClosedGroupControlMessageKeyPair {
guard proto.hasPublicKey else {
throw SNProtoError.invalidProtobuf(description: "\(logTag) missing required field: publicKey")
}
let publicKey = proto.publicKey
guard proto.hasPrivateKey else {
throw SNProtoError.invalidProtobuf(description: "\(logTag) missing required field: privateKey")
}
let privateKey = proto.privateKey
// MARK: - Begin Validation Logic for SNProtoDataMessageClosedGroupControlMessageKeyPair -
// MARK: - End Validation Logic for SNProtoDataMessageClosedGroupControlMessageKeyPair -
let result = SNProtoDataMessageClosedGroupControlMessageKeyPair(proto: proto,
publicKey: publicKey,
privateKey: privateKey)
return result
}
@objc public override var debugDescription: String {
return "\(proto)"
}
}
#if DEBUG
extension SNProtoDataMessageClosedGroupControlMessageKeyPair {
@objc public func serializedDataIgnoringErrors() -> Data? {
return try! self.serializedData()
}
}
extension SNProtoDataMessageClosedGroupControlMessageKeyPair.SNProtoDataMessageClosedGroupControlMessageKeyPairBuilder {
@objc public func buildIgnoringErrors() -> SNProtoDataMessageClosedGroupControlMessageKeyPair? {
return try! self.build()
}
}
#endif
// MARK: - SNProtoDataMessageClosedGroupControlMessageKeyPairWrapper
@objc public class SNProtoDataMessageClosedGroupControlMessageKeyPairWrapper: NSObject {
@ -2484,7 +2484,7 @@ extension SNProtoDataMessageClosedGroupControlMessageKeyPairWrapper.SNProtoDataM
proto.name = valueParam
}
@objc public func setEncryptionKeyPair(_ valueParam: SNProtoDataMessageClosedGroupControlMessageKeyPair) {
@objc public func setEncryptionKeyPair(_ valueParam: SNProtoKeyPair) {
proto.encryptionKeyPair = valueParam.proto
}
@ -2531,7 +2531,7 @@ extension SNProtoDataMessageClosedGroupControlMessageKeyPairWrapper.SNProtoDataM
@objc public let type: SNProtoDataMessageClosedGroupControlMessageType
@objc public let encryptionKeyPair: SNProtoDataMessageClosedGroupControlMessageKeyPair?
@objc public let encryptionKeyPair: SNProtoKeyPair?
@objc public let wrappers: [SNProtoDataMessageClosedGroupControlMessageKeyPairWrapper]
@ -2565,7 +2565,7 @@ extension SNProtoDataMessageClosedGroupControlMessageKeyPairWrapper.SNProtoDataM
private init(proto: SessionProtos_DataMessage.ClosedGroupControlMessage,
type: SNProtoDataMessageClosedGroupControlMessageType,
encryptionKeyPair: SNProtoDataMessageClosedGroupControlMessageKeyPair?,
encryptionKeyPair: SNProtoKeyPair?,
wrappers: [SNProtoDataMessageClosedGroupControlMessageKeyPairWrapper]) {
self.proto = proto
self.type = type
@ -2589,9 +2589,9 @@ extension SNProtoDataMessageClosedGroupControlMessageKeyPairWrapper.SNProtoDataM
}
let type = SNProtoDataMessageClosedGroupControlMessageTypeWrap(proto.type)
var encryptionKeyPair: SNProtoDataMessageClosedGroupControlMessageKeyPair? = nil
var encryptionKeyPair: SNProtoKeyPair? = nil
if proto.hasEncryptionKeyPair {
encryptionKeyPair = try SNProtoDataMessageClosedGroupControlMessageKeyPair.parseProto(proto.encryptionKeyPair)
encryptionKeyPair = try SNProtoKeyPair.parseProto(proto.encryptionKeyPair)
}
var wrappers: [SNProtoDataMessageClosedGroupControlMessageKeyPairWrapper] = []
@ -2690,6 +2690,9 @@ extension SNProtoDataMessageClosedGroupControlMessage.SNProtoDataMessageClosedGr
if let _value = closedGroupControlMessage {
builder.setClosedGroupControlMessage(_value)
}
if let _value = syncTarget {
builder.setSyncTarget(_value)
}
if let _value = publicChatInfo {
builder.setPublicChatInfo(_value)
}
@ -2768,6 +2771,10 @@ extension SNProtoDataMessageClosedGroupControlMessage.SNProtoDataMessageClosedGr
proto.closedGroupControlMessage = valueParam.proto
}
@objc public func setSyncTarget(_ valueParam: String) {
proto.syncTarget = valueParam
}
@objc public func setPublicChatInfo(_ valueParam: SNProtoPublicChatInfo) {
proto.publicChatInfo = valueParam.proto
}
@ -2840,6 +2847,16 @@ extension SNProtoDataMessageClosedGroupControlMessage.SNProtoDataMessageClosedGr
return proto.hasTimestamp
}
@objc public var syncTarget: String? {
guard proto.hasSyncTarget else {
return nil
}
return proto.syncTarget
}
@objc public var hasSyncTarget: Bool {
return proto.hasSyncTarget
}
private init(proto: SessionProtos_DataMessage,
attachments: [SNProtoAttachmentPointer],
group: SNProtoGroupContext?,
@ -2942,6 +2959,275 @@ extension SNProtoDataMessage.SNProtoDataMessageBuilder {
#endif
// MARK: - SNProtoConfigurationMessageClosedGroup
@objc public class SNProtoConfigurationMessageClosedGroup: NSObject {
// MARK: - SNProtoConfigurationMessageClosedGroupBuilder
@objc public class func builder() -> SNProtoConfigurationMessageClosedGroupBuilder {
return SNProtoConfigurationMessageClosedGroupBuilder()
}
// asBuilder() constructs a builder that reflects the proto's contents.
@objc public func asBuilder() -> SNProtoConfigurationMessageClosedGroupBuilder {
let builder = SNProtoConfigurationMessageClosedGroupBuilder()
if let _value = publicKey {
builder.setPublicKey(_value)
}
if let _value = name {
builder.setName(_value)
}
if let _value = encryptionKeyPair {
builder.setEncryptionKeyPair(_value)
}
builder.setMembers(members)
builder.setAdmins(admins)
return builder
}
@objc public class SNProtoConfigurationMessageClosedGroupBuilder: NSObject {
private var proto = SessionProtos_ConfigurationMessage.ClosedGroup()
@objc fileprivate override init() {}
@objc public func setPublicKey(_ valueParam: Data) {
proto.publicKey = valueParam
}
@objc public func setName(_ valueParam: String) {
proto.name = valueParam
}
@objc public func setEncryptionKeyPair(_ valueParam: SNProtoKeyPair) {
proto.encryptionKeyPair = valueParam.proto
}
@objc public func addMembers(_ valueParam: Data) {
var items = proto.members
items.append(valueParam)
proto.members = items
}
@objc public func setMembers(_ wrappedItems: [Data]) {
proto.members = wrappedItems
}
@objc public func addAdmins(_ valueParam: Data) {
var items = proto.admins
items.append(valueParam)
proto.admins = items
}
@objc public func setAdmins(_ wrappedItems: [Data]) {
proto.admins = wrappedItems
}
@objc public func build() throws -> SNProtoConfigurationMessageClosedGroup {
return try SNProtoConfigurationMessageClosedGroup.parseProto(proto)
}
@objc public func buildSerializedData() throws -> Data {
return try SNProtoConfigurationMessageClosedGroup.parseProto(proto).serializedData()
}
}
fileprivate let proto: SessionProtos_ConfigurationMessage.ClosedGroup
@objc public let encryptionKeyPair: SNProtoKeyPair?
@objc public var publicKey: Data? {
guard proto.hasPublicKey else {
return nil
}
return proto.publicKey
}
@objc public var hasPublicKey: Bool {
return proto.hasPublicKey
}
@objc public var name: String? {
guard proto.hasName else {
return nil
}
return proto.name
}
@objc public var hasName: Bool {
return proto.hasName
}
@objc public var members: [Data] {
return proto.members
}
@objc public var admins: [Data] {
return proto.admins
}
private init(proto: SessionProtos_ConfigurationMessage.ClosedGroup,
encryptionKeyPair: SNProtoKeyPair?) {
self.proto = proto
self.encryptionKeyPair = encryptionKeyPair
}
@objc
public func serializedData() throws -> Data {
return try self.proto.serializedData()
}
@objc public class func parseData(_ serializedData: Data) throws -> SNProtoConfigurationMessageClosedGroup {
let proto = try SessionProtos_ConfigurationMessage.ClosedGroup(serializedData: serializedData)
return try parseProto(proto)
}
fileprivate class func parseProto(_ proto: SessionProtos_ConfigurationMessage.ClosedGroup) throws -> SNProtoConfigurationMessageClosedGroup {
var encryptionKeyPair: SNProtoKeyPair? = nil
if proto.hasEncryptionKeyPair {
encryptionKeyPair = try SNProtoKeyPair.parseProto(proto.encryptionKeyPair)
}
// MARK: - Begin Validation Logic for SNProtoConfigurationMessageClosedGroup -
// MARK: - End Validation Logic for SNProtoConfigurationMessageClosedGroup -
let result = SNProtoConfigurationMessageClosedGroup(proto: proto,
encryptionKeyPair: encryptionKeyPair)
return result
}
@objc public override var debugDescription: String {
return "\(proto)"
}
}
#if DEBUG
extension SNProtoConfigurationMessageClosedGroup {
@objc public func serializedDataIgnoringErrors() -> Data? {
return try! self.serializedData()
}
}
extension SNProtoConfigurationMessageClosedGroup.SNProtoConfigurationMessageClosedGroupBuilder {
@objc public func buildIgnoringErrors() -> SNProtoConfigurationMessageClosedGroup? {
return try! self.build()
}
}
#endif
// MARK: - SNProtoConfigurationMessage
@objc public class SNProtoConfigurationMessage: NSObject {
// MARK: - SNProtoConfigurationMessageBuilder
@objc public class func builder() -> SNProtoConfigurationMessageBuilder {
return SNProtoConfigurationMessageBuilder()
}
// asBuilder() constructs a builder that reflects the proto's contents.
@objc public func asBuilder() -> SNProtoConfigurationMessageBuilder {
let builder = SNProtoConfigurationMessageBuilder()
builder.setClosedGroups(closedGroups)
builder.setOpenGroups(openGroups)
return builder
}
@objc public class SNProtoConfigurationMessageBuilder: NSObject {
private var proto = SessionProtos_ConfigurationMessage()
@objc fileprivate override init() {}
@objc public func addClosedGroups(_ valueParam: SNProtoConfigurationMessageClosedGroup) {
var items = proto.closedGroups
items.append(valueParam.proto)
proto.closedGroups = items
}
@objc public func setClosedGroups(_ wrappedItems: [SNProtoConfigurationMessageClosedGroup]) {
proto.closedGroups = wrappedItems.map { $0.proto }
}
@objc public func addOpenGroups(_ valueParam: String) {
var items = proto.openGroups
items.append(valueParam)
proto.openGroups = items
}
@objc public func setOpenGroups(_ wrappedItems: [String]) {
proto.openGroups = wrappedItems
}
@objc public func build() throws -> SNProtoConfigurationMessage {
return try SNProtoConfigurationMessage.parseProto(proto)
}
@objc public func buildSerializedData() throws -> Data {
return try SNProtoConfigurationMessage.parseProto(proto).serializedData()
}
}
fileprivate let proto: SessionProtos_ConfigurationMessage
@objc public let closedGroups: [SNProtoConfigurationMessageClosedGroup]
@objc public var openGroups: [String] {
return proto.openGroups
}
private init(proto: SessionProtos_ConfigurationMessage,
closedGroups: [SNProtoConfigurationMessageClosedGroup]) {
self.proto = proto
self.closedGroups = closedGroups
}
@objc
public func serializedData() throws -> Data {
return try self.proto.serializedData()
}
@objc public class func parseData(_ serializedData: Data) throws -> SNProtoConfigurationMessage {
let proto = try SessionProtos_ConfigurationMessage(serializedData: serializedData)
return try parseProto(proto)
}
fileprivate class func parseProto(_ proto: SessionProtos_ConfigurationMessage) throws -> SNProtoConfigurationMessage {
var closedGroups: [SNProtoConfigurationMessageClosedGroup] = []
closedGroups = try proto.closedGroups.map { try SNProtoConfigurationMessageClosedGroup.parseProto($0) }
// MARK: - Begin Validation Logic for SNProtoConfigurationMessage -
// MARK: - End Validation Logic for SNProtoConfigurationMessage -
let result = SNProtoConfigurationMessage(proto: proto,
closedGroups: closedGroups)
return result
}
@objc public override var debugDescription: String {
return "\(proto)"
}
}
#if DEBUG
extension SNProtoConfigurationMessage {
@objc public func serializedDataIgnoringErrors() -> Data? {
return try! self.serializedData()
}
}
extension SNProtoConfigurationMessage.SNProtoConfigurationMessageBuilder {
@objc public func buildIgnoringErrors() -> SNProtoConfigurationMessage? {
return try! self.build()
}
}
#endif
// MARK: - SNProtoReceiptMessage
@objc public class SNProtoReceiptMessage: NSObject {
@ -3075,270 +3361,6 @@ extension SNProtoReceiptMessage.SNProtoReceiptMessageBuilder {
#endif
// MARK: - SNProtoSyncMessageSent
@objc public class SNProtoSyncMessageSent: NSObject {
// MARK: - SNProtoSyncMessageSentBuilder
@objc public class func builder() -> SNProtoSyncMessageSentBuilder {
return SNProtoSyncMessageSentBuilder()
}
// asBuilder() constructs a builder that reflects the proto's contents.
@objc public func asBuilder() -> SNProtoSyncMessageSentBuilder {
let builder = SNProtoSyncMessageSentBuilder()
if let _value = destination {
builder.setDestination(_value)
}
if hasTimestamp {
builder.setTimestamp(timestamp)
}
if let _value = message {
builder.setMessage(_value)
}
if hasExpirationStartTimestamp {
builder.setExpirationStartTimestamp(expirationStartTimestamp)
}
if hasIsRecipientUpdate {
builder.setIsRecipientUpdate(isRecipientUpdate)
}
return builder
}
@objc public class SNProtoSyncMessageSentBuilder: NSObject {
private var proto = SessionProtos_SyncMessage.Sent()
@objc fileprivate override init() {}
@objc public func setDestination(_ valueParam: String) {
proto.destination = valueParam
}
@objc public func setTimestamp(_ valueParam: UInt64) {
proto.timestamp = valueParam
}
@objc public func setMessage(_ valueParam: SNProtoDataMessage) {
proto.message = valueParam.proto
}
@objc public func setExpirationStartTimestamp(_ valueParam: UInt64) {
proto.expirationStartTimestamp = valueParam
}
@objc public func setIsRecipientUpdate(_ valueParam: Bool) {
proto.isRecipientUpdate = valueParam
}
@objc public func build() throws -> SNProtoSyncMessageSent {
return try SNProtoSyncMessageSent.parseProto(proto)
}
@objc public func buildSerializedData() throws -> Data {
return try SNProtoSyncMessageSent.parseProto(proto).serializedData()
}
}
fileprivate let proto: SessionProtos_SyncMessage.Sent
@objc public let message: SNProtoDataMessage?
@objc public var destination: String? {
guard proto.hasDestination else {
return nil
}
return proto.destination
}
@objc public var hasDestination: Bool {
return proto.hasDestination
}
@objc public var timestamp: UInt64 {
return proto.timestamp
}
@objc public var hasTimestamp: Bool {
return proto.hasTimestamp
}
@objc public var expirationStartTimestamp: UInt64 {
return proto.expirationStartTimestamp
}
@objc public var hasExpirationStartTimestamp: Bool {
return proto.hasExpirationStartTimestamp
}
@objc public var isRecipientUpdate: Bool {
return proto.isRecipientUpdate
}
@objc public var hasIsRecipientUpdate: Bool {
return proto.hasIsRecipientUpdate
}
private init(proto: SessionProtos_SyncMessage.Sent,
message: SNProtoDataMessage?) {
self.proto = proto
self.message = message
}
@objc
public func serializedData() throws -> Data {
return try self.proto.serializedData()
}
@objc public class func parseData(_ serializedData: Data) throws -> SNProtoSyncMessageSent {
let proto = try SessionProtos_SyncMessage.Sent(serializedData: serializedData)
return try parseProto(proto)
}
fileprivate class func parseProto(_ proto: SessionProtos_SyncMessage.Sent) throws -> SNProtoSyncMessageSent {
var message: SNProtoDataMessage? = nil
if proto.hasMessage {
message = try SNProtoDataMessage.parseProto(proto.message)
}
// MARK: - Begin Validation Logic for SNProtoSyncMessageSent -
// MARK: - End Validation Logic for SNProtoSyncMessageSent -
let result = SNProtoSyncMessageSent(proto: proto,
message: message)
return result
}
@objc public override var debugDescription: String {
return "\(proto)"
}
}
#if DEBUG
extension SNProtoSyncMessageSent {
@objc public func serializedDataIgnoringErrors() -> Data? {
return try! self.serializedData()
}
}
extension SNProtoSyncMessageSent.SNProtoSyncMessageSentBuilder {
@objc public func buildIgnoringErrors() -> SNProtoSyncMessageSent? {
return try! self.build()
}
}
#endif
// MARK: - SNProtoSyncMessage
@objc public class SNProtoSyncMessage: NSObject {
// MARK: - SNProtoSyncMessageBuilder
@objc public class func builder() -> SNProtoSyncMessageBuilder {
return SNProtoSyncMessageBuilder()
}
// asBuilder() constructs a builder that reflects the proto's contents.
@objc public func asBuilder() -> SNProtoSyncMessageBuilder {
let builder = SNProtoSyncMessageBuilder()
if let _value = sent {
builder.setSent(_value)
}
if let _value = padding {
builder.setPadding(_value)
}
return builder
}
@objc public class SNProtoSyncMessageBuilder: NSObject {
private var proto = SessionProtos_SyncMessage()
@objc fileprivate override init() {}
@objc public func setSent(_ valueParam: SNProtoSyncMessageSent) {
proto.sent = valueParam.proto
}
@objc public func setPadding(_ valueParam: Data) {
proto.padding = valueParam
}
@objc public func build() throws -> SNProtoSyncMessage {
return try SNProtoSyncMessage.parseProto(proto)
}
@objc public func buildSerializedData() throws -> Data {
return try SNProtoSyncMessage.parseProto(proto).serializedData()
}
}
fileprivate let proto: SessionProtos_SyncMessage
@objc public let sent: SNProtoSyncMessageSent?
@objc public var padding: Data? {
guard proto.hasPadding else {
return nil
}
return proto.padding
}
@objc public var hasPadding: Bool {
return proto.hasPadding
}
private init(proto: SessionProtos_SyncMessage,
sent: SNProtoSyncMessageSent?) {
self.proto = proto
self.sent = sent
}
@objc
public func serializedData() throws -> Data {
return try self.proto.serializedData()
}
@objc public class func parseData(_ serializedData: Data) throws -> SNProtoSyncMessage {
let proto = try SessionProtos_SyncMessage(serializedData: serializedData)
return try parseProto(proto)
}
fileprivate class func parseProto(_ proto: SessionProtos_SyncMessage) throws -> SNProtoSyncMessage {
var sent: SNProtoSyncMessageSent? = nil
if proto.hasSent {
sent = try SNProtoSyncMessageSent.parseProto(proto.sent)
}
// MARK: - Begin Validation Logic for SNProtoSyncMessage -
// MARK: - End Validation Logic for SNProtoSyncMessage -
let result = SNProtoSyncMessage(proto: proto,
sent: sent)
return result
}
@objc public override var debugDescription: String {
return "\(proto)"
}
}
#if DEBUG
extension SNProtoSyncMessage {
@objc public func serializedDataIgnoringErrors() -> Data? {
return try! self.serializedData()
}
}
extension SNProtoSyncMessage.SNProtoSyncMessageBuilder {
@objc public func buildIgnoringErrors() -> SNProtoSyncMessage? {
return try! self.build()
}
}
#endif
// MARK: - SNProtoAttachmentPointer
@objc public class SNProtoAttachmentPointer: NSObject {

View File

@ -209,15 +209,6 @@ struct SessionProtos_Content {
/// Clears the value of `dataMessage`. Subsequent reads from it will return its default value.
mutating func clearDataMessage() {_uniqueStorage()._dataMessage = nil}
var syncMessage: SessionProtos_SyncMessage {
get {return _storage._syncMessage ?? SessionProtos_SyncMessage()}
set {_uniqueStorage()._syncMessage = newValue}
}
/// Returns true if `syncMessage` has been explicitly set.
var hasSyncMessage: Bool {return _storage._syncMessage != nil}
/// Clears the value of `syncMessage`. Subsequent reads from it will return its default value.
mutating func clearSyncMessage() {_uniqueStorage()._syncMessage = nil}
var receiptMessage: SessionProtos_ReceiptMessage {
get {return _storage._receiptMessage ?? SessionProtos_ReceiptMessage()}
set {_uniqueStorage()._receiptMessage = newValue}
@ -236,6 +227,15 @@ struct SessionProtos_Content {
/// Clears the value of `typingMessage`. Subsequent reads from it will return its default value.
mutating func clearTypingMessage() {_uniqueStorage()._typingMessage = nil}
var configurationMessage: SessionProtos_ConfigurationMessage {
get {return _storage._configurationMessage ?? SessionProtos_ConfigurationMessage()}
set {_uniqueStorage()._configurationMessage = newValue}
}
/// Returns true if `configurationMessage` has been explicitly set.
var hasConfigurationMessage: Bool {return _storage._configurationMessage != nil}
/// Clears the value of `configurationMessage`. Subsequent reads from it will return its default value.
mutating func clearConfigurationMessage() {_uniqueStorage()._configurationMessage = nil}
var unknownFields = SwiftProtobuf.UnknownStorage()
init() {}
@ -276,6 +276,39 @@ struct SessionProtos_ClosedGroupCiphertextMessageWrapper {
fileprivate var _ephemeralPublicKey: Data? = nil
}
struct SessionProtos_KeyPair {
// SwiftProtobuf.Message conformance is added in an extension below. See the
// `Message` and `Message+*Additions` files in the SwiftProtobuf library for
// methods supported on all messages.
/// @required
var publicKey: Data {
get {return _publicKey ?? SwiftProtobuf.Internal.emptyData}
set {_publicKey = newValue}
}
/// Returns true if `publicKey` has been explicitly set.
var hasPublicKey: Bool {return self._publicKey != nil}
/// Clears the value of `publicKey`. Subsequent reads from it will return its default value.
mutating func clearPublicKey() {self._publicKey = nil}
/// @required
var privateKey: Data {
get {return _privateKey ?? SwiftProtobuf.Internal.emptyData}
set {_privateKey = newValue}
}
/// Returns true if `privateKey` has been explicitly set.
var hasPrivateKey: Bool {return self._privateKey != nil}
/// Clears the value of `privateKey`. Subsequent reads from it will return its default value.
mutating func clearPrivateKey() {self._privateKey = nil}
var unknownFields = SwiftProtobuf.UnknownStorage()
init() {}
fileprivate var _publicKey: Data? = nil
fileprivate var _privateKey: Data? = nil
}
struct SessionProtos_DataMessage {
// SwiftProtobuf.Message conformance is added in an extension below. See the
// `Message` and `Message+*Additions` files in the SwiftProtobuf library for
@ -377,6 +410,15 @@ struct SessionProtos_DataMessage {
/// Clears the value of `closedGroupControlMessage`. Subsequent reads from it will return its default value.
mutating func clearClosedGroupControlMessage() {_uniqueStorage()._closedGroupControlMessage = nil}
var syncTarget: String {
get {return _storage._syncTarget ?? String()}
set {_uniqueStorage()._syncTarget = newValue}
}
/// Returns true if `syncTarget` has been explicitly set.
var hasSyncTarget: Bool {return _storage._syncTarget != nil}
/// Clears the value of `syncTarget`. Subsequent reads from it will return its default value.
mutating func clearSyncTarget() {_uniqueStorage()._syncTarget = nil}
var publicChatInfo: SessionProtos_PublicChatInfo {
get {return _storage._publicChatInfo ?? SessionProtos_PublicChatInfo()}
set {_uniqueStorage()._publicChatInfo = newValue}
@ -1062,8 +1104,8 @@ struct SessionProtos_DataMessage {
/// Clears the value of `name`. Subsequent reads from it will return its default value.
mutating func clearName() {_uniqueStorage()._name = nil}
var encryptionKeyPair: SessionProtos_DataMessage.ClosedGroupControlMessage.KeyPair {
get {return _storage._encryptionKeyPair ?? SessionProtos_DataMessage.ClosedGroupControlMessage.KeyPair()}
var encryptionKeyPair: SessionProtos_KeyPair {
get {return _storage._encryptionKeyPair ?? SessionProtos_KeyPair()}
set {_uniqueStorage()._encryptionKeyPair = newValue}
}
/// Returns true if `encryptionKeyPair` has been explicitly set.
@ -1141,39 +1183,6 @@ struct SessionProtos_DataMessage {
}
struct KeyPair {
// SwiftProtobuf.Message conformance is added in an extension below. See the
// `Message` and `Message+*Additions` files in the SwiftProtobuf library for
// methods supported on all messages.
/// @required
var publicKey: Data {
get {return _publicKey ?? SwiftProtobuf.Internal.emptyData}
set {_publicKey = newValue}
}
/// Returns true if `publicKey` has been explicitly set.
var hasPublicKey: Bool {return self._publicKey != nil}
/// Clears the value of `publicKey`. Subsequent reads from it will return its default value.
mutating func clearPublicKey() {self._publicKey = nil}
/// @required
var privateKey: Data {
get {return _privateKey ?? SwiftProtobuf.Internal.emptyData}
set {_privateKey = newValue}
}
/// Returns true if `privateKey` has been explicitly set.
var hasPrivateKey: Bool {return self._privateKey != nil}
/// Clears the value of `privateKey`. Subsequent reads from it will return its default value.
mutating func clearPrivateKey() {self._privateKey = nil}
var unknownFields = SwiftProtobuf.UnknownStorage()
init() {}
fileprivate var _publicKey: Data? = nil
fileprivate var _privateKey: Data? = nil
}
struct KeyPairWrapper {
// SwiftProtobuf.Message conformance is added in an extension below. See the
// `Message` and `Message+*Additions` files in the SwiftProtobuf library for
@ -1225,6 +1234,69 @@ extension SessionProtos_DataMessage.Flags: CaseIterable {
#endif // swift(>=4.2)
struct SessionProtos_ConfigurationMessage {
// SwiftProtobuf.Message conformance is added in an extension below. See the
// `Message` and `Message+*Additions` files in the SwiftProtobuf library for
// methods supported on all messages.
var closedGroups: [SessionProtos_ConfigurationMessage.ClosedGroup] = []
var openGroups: [String] = []
var unknownFields = SwiftProtobuf.UnknownStorage()
struct ClosedGroup {
// SwiftProtobuf.Message conformance is added in an extension below. See the
// `Message` and `Message+*Additions` files in the SwiftProtobuf library for
// methods supported on all messages.
var publicKey: Data {
get {return _storage._publicKey ?? SwiftProtobuf.Internal.emptyData}
set {_uniqueStorage()._publicKey = newValue}
}
/// Returns true if `publicKey` has been explicitly set.
var hasPublicKey: Bool {return _storage._publicKey != nil}
/// Clears the value of `publicKey`. Subsequent reads from it will return its default value.
mutating func clearPublicKey() {_uniqueStorage()._publicKey = nil}
var name: String {
get {return _storage._name ?? String()}
set {_uniqueStorage()._name = newValue}
}
/// Returns true if `name` has been explicitly set.
var hasName: Bool {return _storage._name != nil}
/// Clears the value of `name`. Subsequent reads from it will return its default value.
mutating func clearName() {_uniqueStorage()._name = nil}
var encryptionKeyPair: SessionProtos_KeyPair {
get {return _storage._encryptionKeyPair ?? SessionProtos_KeyPair()}
set {_uniqueStorage()._encryptionKeyPair = newValue}
}
/// Returns true if `encryptionKeyPair` has been explicitly set.
var hasEncryptionKeyPair: Bool {return _storage._encryptionKeyPair != nil}
/// Clears the value of `encryptionKeyPair`. Subsequent reads from it will return its default value.
mutating func clearEncryptionKeyPair() {_uniqueStorage()._encryptionKeyPair = nil}
var members: [Data] {
get {return _storage._members}
set {_uniqueStorage()._members = newValue}
}
var admins: [Data] {
get {return _storage._admins}
set {_uniqueStorage()._admins = newValue}
}
var unknownFields = SwiftProtobuf.UnknownStorage()
init() {}
fileprivate var _storage = _StorageClass.defaultInstance
}
init() {}
}
struct SessionProtos_ReceiptMessage {
// SwiftProtobuf.Message conformance is added in an extension below. See the
// `Message` and `Message+*Additions` files in the SwiftProtobuf library for
@ -1283,93 +1355,6 @@ extension SessionProtos_ReceiptMessage.TypeEnum: CaseIterable {
#endif // swift(>=4.2)
struct SessionProtos_SyncMessage {
// SwiftProtobuf.Message conformance is added in an extension below. See the
// `Message` and `Message+*Additions` files in the SwiftProtobuf library for
// methods supported on all messages.
var sent: SessionProtos_SyncMessage.Sent {
get {return _storage._sent ?? SessionProtos_SyncMessage.Sent()}
set {_uniqueStorage()._sent = newValue}
}
/// Returns true if `sent` has been explicitly set.
var hasSent: Bool {return _storage._sent != nil}
/// Clears the value of `sent`. Subsequent reads from it will return its default value.
mutating func clearSent() {_uniqueStorage()._sent = nil}
var padding: Data {
get {return _storage._padding ?? SwiftProtobuf.Internal.emptyData}
set {_uniqueStorage()._padding = newValue}
}
/// Returns true if `padding` has been explicitly set.
var hasPadding: Bool {return _storage._padding != nil}
/// Clears the value of `padding`. Subsequent reads from it will return its default value.
mutating func clearPadding() {_uniqueStorage()._padding = nil}
var unknownFields = SwiftProtobuf.UnknownStorage()
struct Sent {
// SwiftProtobuf.Message conformance is added in an extension below. See the
// `Message` and `Message+*Additions` files in the SwiftProtobuf library for
// methods supported on all messages.
var destination: String {
get {return _storage._destination ?? String()}
set {_uniqueStorage()._destination = newValue}
}
/// Returns true if `destination` has been explicitly set.
var hasDestination: Bool {return _storage._destination != nil}
/// Clears the value of `destination`. Subsequent reads from it will return its default value.
mutating func clearDestination() {_uniqueStorage()._destination = nil}
var timestamp: UInt64 {
get {return _storage._timestamp ?? 0}
set {_uniqueStorage()._timestamp = newValue}
}
/// Returns true if `timestamp` has been explicitly set.
var hasTimestamp: Bool {return _storage._timestamp != nil}
/// Clears the value of `timestamp`. Subsequent reads from it will return its default value.
mutating func clearTimestamp() {_uniqueStorage()._timestamp = nil}
var message: SessionProtos_DataMessage {
get {return _storage._message ?? SessionProtos_DataMessage()}
set {_uniqueStorage()._message = newValue}
}
/// Returns true if `message` has been explicitly set.
var hasMessage: Bool {return _storage._message != nil}
/// Clears the value of `message`. Subsequent reads from it will return its default value.
mutating func clearMessage() {_uniqueStorage()._message = nil}
var expirationStartTimestamp: UInt64 {
get {return _storage._expirationStartTimestamp ?? 0}
set {_uniqueStorage()._expirationStartTimestamp = newValue}
}
/// Returns true if `expirationStartTimestamp` has been explicitly set.
var hasExpirationStartTimestamp: Bool {return _storage._expirationStartTimestamp != nil}
/// Clears the value of `expirationStartTimestamp`. Subsequent reads from it will return its default value.
mutating func clearExpirationStartTimestamp() {_uniqueStorage()._expirationStartTimestamp = nil}
var isRecipientUpdate: Bool {
get {return _storage._isRecipientUpdate ?? false}
set {_uniqueStorage()._isRecipientUpdate = newValue}
}
/// Returns true if `isRecipientUpdate` has been explicitly set.
var hasIsRecipientUpdate: Bool {return _storage._isRecipientUpdate != nil}
/// Clears the value of `isRecipientUpdate`. Subsequent reads from it will return its default value.
mutating func clearIsRecipientUpdate() {_uniqueStorage()._isRecipientUpdate = nil}
var unknownFields = SwiftProtobuf.UnknownStorage()
init() {}
fileprivate var _storage = _StorageClass.defaultInstance
}
init() {}
fileprivate var _storage = _StorageClass.defaultInstance
}
struct SessionProtos_AttachmentPointer {
// SwiftProtobuf.Message conformance is added in an extension below. See the
// `Message` and `Message+*Additions` files in the SwiftProtobuf library for
@ -2012,16 +1997,16 @@ extension SessionProtos_Content: SwiftProtobuf.Message, SwiftProtobuf._MessageIm
static let protoMessageName: String = _protobuf_package + ".Content"
static let _protobuf_nameMap: SwiftProtobuf._NameMap = [
1: .same(proto: "dataMessage"),
2: .same(proto: "syncMessage"),
5: .same(proto: "receiptMessage"),
6: .same(proto: "typingMessage"),
7: .same(proto: "configurationMessage"),
]
fileprivate class _StorageClass {
var _dataMessage: SessionProtos_DataMessage? = nil
var _syncMessage: SessionProtos_SyncMessage? = nil
var _receiptMessage: SessionProtos_ReceiptMessage? = nil
var _typingMessage: SessionProtos_TypingMessage? = nil
var _configurationMessage: SessionProtos_ConfigurationMessage? = nil
static let defaultInstance = _StorageClass()
@ -2029,9 +2014,9 @@ extension SessionProtos_Content: SwiftProtobuf.Message, SwiftProtobuf._MessageIm
init(copying source: _StorageClass) {
_dataMessage = source._dataMessage
_syncMessage = source._syncMessage
_receiptMessage = source._receiptMessage
_typingMessage = source._typingMessage
_configurationMessage = source._configurationMessage
}
}
@ -2045,7 +2030,7 @@ extension SessionProtos_Content: SwiftProtobuf.Message, SwiftProtobuf._MessageIm
public var isInitialized: Bool {
return withExtendedLifetime(_storage) { (_storage: _StorageClass) in
if let v = _storage._dataMessage, !v.isInitialized {return false}
if let v = _storage._syncMessage, !v.isInitialized {return false}
if let v = _storage._configurationMessage, !v.isInitialized {return false}
return true
}
}
@ -2056,9 +2041,9 @@ extension SessionProtos_Content: SwiftProtobuf.Message, SwiftProtobuf._MessageIm
while let fieldNumber = try decoder.nextFieldNumber() {
switch fieldNumber {
case 1: try decoder.decodeSingularMessageField(value: &_storage._dataMessage)
case 2: try decoder.decodeSingularMessageField(value: &_storage._syncMessage)
case 5: try decoder.decodeSingularMessageField(value: &_storage._receiptMessage)
case 6: try decoder.decodeSingularMessageField(value: &_storage._typingMessage)
case 7: try decoder.decodeSingularMessageField(value: &_storage._configurationMessage)
default: break
}
}
@ -2070,15 +2055,15 @@ extension SessionProtos_Content: SwiftProtobuf.Message, SwiftProtobuf._MessageIm
if let v = _storage._dataMessage {
try visitor.visitSingularMessageField(value: v, fieldNumber: 1)
}
if let v = _storage._syncMessage {
try visitor.visitSingularMessageField(value: v, fieldNumber: 2)
}
if let v = _storage._receiptMessage {
try visitor.visitSingularMessageField(value: v, fieldNumber: 5)
}
if let v = _storage._typingMessage {
try visitor.visitSingularMessageField(value: v, fieldNumber: 6)
}
if let v = _storage._configurationMessage {
try visitor.visitSingularMessageField(value: v, fieldNumber: 7)
}
}
try unknownFields.traverse(visitor: &visitor)
}
@ -2089,9 +2074,9 @@ extension SessionProtos_Content: SwiftProtobuf.Message, SwiftProtobuf._MessageIm
let _storage = _args.0
let rhs_storage = _args.1
if _storage._dataMessage != rhs_storage._dataMessage {return false}
if _storage._syncMessage != rhs_storage._syncMessage {return false}
if _storage._receiptMessage != rhs_storage._receiptMessage {return false}
if _storage._typingMessage != rhs_storage._typingMessage {return false}
if _storage._configurationMessage != rhs_storage._configurationMessage {return false}
return true
}
if !storagesAreEqual {return false}
@ -2136,6 +2121,47 @@ extension SessionProtos_ClosedGroupCiphertextMessageWrapper: SwiftProtobuf.Messa
}
}
extension SessionProtos_KeyPair: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationBase, SwiftProtobuf._ProtoNameProviding {
static let protoMessageName: String = _protobuf_package + ".KeyPair"
static let _protobuf_nameMap: SwiftProtobuf._NameMap = [
1: .same(proto: "publicKey"),
2: .same(proto: "privateKey"),
]
public var isInitialized: Bool {
if self._publicKey == nil {return false}
if self._privateKey == nil {return false}
return true
}
mutating func decodeMessage<D: SwiftProtobuf.Decoder>(decoder: inout D) throws {
while let fieldNumber = try decoder.nextFieldNumber() {
switch fieldNumber {
case 1: try decoder.decodeSingularBytesField(value: &self._publicKey)
case 2: try decoder.decodeSingularBytesField(value: &self._privateKey)
default: break
}
}
}
func traverse<V: SwiftProtobuf.Visitor>(visitor: inout V) throws {
if let v = self._publicKey {
try visitor.visitSingularBytesField(value: v, fieldNumber: 1)
}
if let v = self._privateKey {
try visitor.visitSingularBytesField(value: v, fieldNumber: 2)
}
try unknownFields.traverse(visitor: &visitor)
}
static func ==(lhs: SessionProtos_KeyPair, rhs: SessionProtos_KeyPair) -> Bool {
if lhs._publicKey != rhs._publicKey {return false}
if lhs._privateKey != rhs._privateKey {return false}
if lhs.unknownFields != rhs.unknownFields {return false}
return true
}
}
extension SessionProtos_DataMessage: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationBase, SwiftProtobuf._ProtoNameProviding {
static let protoMessageName: String = _protobuf_package + ".DataMessage"
static let _protobuf_nameMap: SwiftProtobuf._NameMap = [
@ -2151,6 +2177,7 @@ extension SessionProtos_DataMessage: SwiftProtobuf.Message, SwiftProtobuf._Messa
10: .same(proto: "preview"),
101: .same(proto: "profile"),
104: .same(proto: "closedGroupControlMessage"),
105: .same(proto: "syncTarget"),
999: .same(proto: "publicChatInfo"),
]
@ -2167,6 +2194,7 @@ extension SessionProtos_DataMessage: SwiftProtobuf.Message, SwiftProtobuf._Messa
var _preview: [SessionProtos_DataMessage.Preview] = []
var _profile: SessionProtos_DataMessage.LokiProfile? = nil
var _closedGroupControlMessage: SessionProtos_DataMessage.ClosedGroupControlMessage? = nil
var _syncTarget: String? = nil
var _publicChatInfo: SessionProtos_PublicChatInfo? = nil
static let defaultInstance = _StorageClass()
@ -2186,6 +2214,7 @@ extension SessionProtos_DataMessage: SwiftProtobuf.Message, SwiftProtobuf._Messa
_preview = source._preview
_profile = source._profile
_closedGroupControlMessage = source._closedGroupControlMessage
_syncTarget = source._syncTarget
_publicChatInfo = source._publicChatInfo
}
}
@ -2221,6 +2250,7 @@ extension SessionProtos_DataMessage: SwiftProtobuf.Message, SwiftProtobuf._Messa
case 10: try decoder.decodeRepeatedMessageField(value: &_storage._preview)
case 101: try decoder.decodeSingularMessageField(value: &_storage._profile)
case 104: try decoder.decodeSingularMessageField(value: &_storage._closedGroupControlMessage)
case 105: try decoder.decodeSingularStringField(value: &_storage._syncTarget)
case 999: try decoder.decodeSingularMessageField(value: &_storage._publicChatInfo)
default: break
}
@ -2266,6 +2296,9 @@ extension SessionProtos_DataMessage: SwiftProtobuf.Message, SwiftProtobuf._Messa
if let v = _storage._closedGroupControlMessage {
try visitor.visitSingularMessageField(value: v, fieldNumber: 104)
}
if let v = _storage._syncTarget {
try visitor.visitSingularStringField(value: v, fieldNumber: 105)
}
if let v = _storage._publicChatInfo {
try visitor.visitSingularMessageField(value: v, fieldNumber: 999)
}
@ -2290,6 +2323,7 @@ extension SessionProtos_DataMessage: SwiftProtobuf.Message, SwiftProtobuf._Messa
if _storage._preview != rhs_storage._preview {return false}
if _storage._profile != rhs_storage._profile {return false}
if _storage._closedGroupControlMessage != rhs_storage._closedGroupControlMessage {return false}
if _storage._syncTarget != rhs_storage._syncTarget {return false}
if _storage._publicChatInfo != rhs_storage._publicChatInfo {return false}
return true
}
@ -2986,7 +3020,7 @@ extension SessionProtos_DataMessage.ClosedGroupControlMessage: SwiftProtobuf.Mes
var _type: SessionProtos_DataMessage.ClosedGroupControlMessage.TypeEnum? = nil
var _publicKey: Data? = nil
var _name: String? = nil
var _encryptionKeyPair: SessionProtos_DataMessage.ClosedGroupControlMessage.KeyPair? = nil
var _encryptionKeyPair: SessionProtos_KeyPair? = nil
var _members: [Data] = []
var _admins: [Data] = []
var _wrappers: [SessionProtos_DataMessage.ClosedGroupControlMessage.KeyPairWrapper] = []
@ -3100,47 +3134,6 @@ extension SessionProtos_DataMessage.ClosedGroupControlMessage.TypeEnum: SwiftPro
]
}
extension SessionProtos_DataMessage.ClosedGroupControlMessage.KeyPair: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationBase, SwiftProtobuf._ProtoNameProviding {
static let protoMessageName: String = SessionProtos_DataMessage.ClosedGroupControlMessage.protoMessageName + ".KeyPair"
static let _protobuf_nameMap: SwiftProtobuf._NameMap = [
1: .same(proto: "publicKey"),
2: .same(proto: "privateKey"),
]
public var isInitialized: Bool {
if self._publicKey == nil {return false}
if self._privateKey == nil {return false}
return true
}
mutating func decodeMessage<D: SwiftProtobuf.Decoder>(decoder: inout D) throws {
while let fieldNumber = try decoder.nextFieldNumber() {
switch fieldNumber {
case 1: try decoder.decodeSingularBytesField(value: &self._publicKey)
case 2: try decoder.decodeSingularBytesField(value: &self._privateKey)
default: break
}
}
}
func traverse<V: SwiftProtobuf.Visitor>(visitor: inout V) throws {
if let v = self._publicKey {
try visitor.visitSingularBytesField(value: v, fieldNumber: 1)
}
if let v = self._privateKey {
try visitor.visitSingularBytesField(value: v, fieldNumber: 2)
}
try unknownFields.traverse(visitor: &visitor)
}
static func ==(lhs: SessionProtos_DataMessage.ClosedGroupControlMessage.KeyPair, rhs: SessionProtos_DataMessage.ClosedGroupControlMessage.KeyPair) -> Bool {
if lhs._publicKey != rhs._publicKey {return false}
if lhs._privateKey != rhs._privateKey {return false}
if lhs.unknownFields != rhs.unknownFields {return false}
return true
}
}
extension SessionProtos_DataMessage.ClosedGroupControlMessage.KeyPairWrapper: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationBase, SwiftProtobuf._ProtoNameProviding {
static let protoMessageName: String = SessionProtos_DataMessage.ClosedGroupControlMessage.protoMessageName + ".KeyPairWrapper"
static let _protobuf_nameMap: SwiftProtobuf._NameMap = [
@ -3182,6 +3175,146 @@ extension SessionProtos_DataMessage.ClosedGroupControlMessage.KeyPairWrapper: Sw
}
}
extension SessionProtos_ConfigurationMessage: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationBase, SwiftProtobuf._ProtoNameProviding {
static let protoMessageName: String = _protobuf_package + ".ConfigurationMessage"
static let _protobuf_nameMap: SwiftProtobuf._NameMap = [
1: .same(proto: "closedGroups"),
2: .same(proto: "openGroups"),
]
public var isInitialized: Bool {
if !SwiftProtobuf.Internal.areAllInitialized(self.closedGroups) {return false}
return true
}
mutating func decodeMessage<D: SwiftProtobuf.Decoder>(decoder: inout D) throws {
while let fieldNumber = try decoder.nextFieldNumber() {
switch fieldNumber {
case 1: try decoder.decodeRepeatedMessageField(value: &self.closedGroups)
case 2: try decoder.decodeRepeatedStringField(value: &self.openGroups)
default: break
}
}
}
func traverse<V: SwiftProtobuf.Visitor>(visitor: inout V) throws {
if !self.closedGroups.isEmpty {
try visitor.visitRepeatedMessageField(value: self.closedGroups, fieldNumber: 1)
}
if !self.openGroups.isEmpty {
try visitor.visitRepeatedStringField(value: self.openGroups, fieldNumber: 2)
}
try unknownFields.traverse(visitor: &visitor)
}
static func ==(lhs: SessionProtos_ConfigurationMessage, rhs: SessionProtos_ConfigurationMessage) -> Bool {
if lhs.closedGroups != rhs.closedGroups {return false}
if lhs.openGroups != rhs.openGroups {return false}
if lhs.unknownFields != rhs.unknownFields {return false}
return true
}
}
extension SessionProtos_ConfigurationMessage.ClosedGroup: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationBase, SwiftProtobuf._ProtoNameProviding {
static let protoMessageName: String = SessionProtos_ConfigurationMessage.protoMessageName + ".ClosedGroup"
static let _protobuf_nameMap: SwiftProtobuf._NameMap = [
1: .same(proto: "publicKey"),
2: .same(proto: "name"),
3: .same(proto: "encryptionKeyPair"),
4: .same(proto: "members"),
5: .same(proto: "admins"),
]
fileprivate class _StorageClass {
var _publicKey: Data? = nil
var _name: String? = nil
var _encryptionKeyPair: SessionProtos_KeyPair? = nil
var _members: [Data] = []
var _admins: [Data] = []
static let defaultInstance = _StorageClass()
private init() {}
init(copying source: _StorageClass) {
_publicKey = source._publicKey
_name = source._name
_encryptionKeyPair = source._encryptionKeyPair
_members = source._members
_admins = source._admins
}
}
fileprivate mutating func _uniqueStorage() -> _StorageClass {
if !isKnownUniquelyReferenced(&_storage) {
_storage = _StorageClass(copying: _storage)
}
return _storage
}
public var isInitialized: Bool {
return withExtendedLifetime(_storage) { (_storage: _StorageClass) in
if let v = _storage._encryptionKeyPair, !v.isInitialized {return false}
return true
}
}
mutating func decodeMessage<D: SwiftProtobuf.Decoder>(decoder: inout D) throws {
_ = _uniqueStorage()
try withExtendedLifetime(_storage) { (_storage: _StorageClass) in
while let fieldNumber = try decoder.nextFieldNumber() {
switch fieldNumber {
case 1: try decoder.decodeSingularBytesField(value: &_storage._publicKey)
case 2: try decoder.decodeSingularStringField(value: &_storage._name)
case 3: try decoder.decodeSingularMessageField(value: &_storage._encryptionKeyPair)
case 4: try decoder.decodeRepeatedBytesField(value: &_storage._members)
case 5: try decoder.decodeRepeatedBytesField(value: &_storage._admins)
default: break
}
}
}
}
func traverse<V: SwiftProtobuf.Visitor>(visitor: inout V) throws {
try withExtendedLifetime(_storage) { (_storage: _StorageClass) in
if let v = _storage._publicKey {
try visitor.visitSingularBytesField(value: v, fieldNumber: 1)
}
if let v = _storage._name {
try visitor.visitSingularStringField(value: v, fieldNumber: 2)
}
if let v = _storage._encryptionKeyPair {
try visitor.visitSingularMessageField(value: v, fieldNumber: 3)
}
if !_storage._members.isEmpty {
try visitor.visitRepeatedBytesField(value: _storage._members, fieldNumber: 4)
}
if !_storage._admins.isEmpty {
try visitor.visitRepeatedBytesField(value: _storage._admins, fieldNumber: 5)
}
}
try unknownFields.traverse(visitor: &visitor)
}
static func ==(lhs: SessionProtos_ConfigurationMessage.ClosedGroup, rhs: SessionProtos_ConfigurationMessage.ClosedGroup) -> Bool {
if lhs._storage !== rhs._storage {
let storagesAreEqual: Bool = withExtendedLifetime((lhs._storage, rhs._storage)) { (_args: (_StorageClass, _StorageClass)) in
let _storage = _args.0
let rhs_storage = _args.1
if _storage._publicKey != rhs_storage._publicKey {return false}
if _storage._name != rhs_storage._name {return false}
if _storage._encryptionKeyPair != rhs_storage._encryptionKeyPair {return false}
if _storage._members != rhs_storage._members {return false}
if _storage._admins != rhs_storage._admins {return false}
return true
}
if !storagesAreEqual {return false}
}
if lhs.unknownFields != rhs.unknownFields {return false}
return true
}
}
extension SessionProtos_ReceiptMessage: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationBase, SwiftProtobuf._ProtoNameProviding {
static let protoMessageName: String = _protobuf_package + ".ReceiptMessage"
static let _protobuf_nameMap: SwiftProtobuf._NameMap = [
@ -3224,182 +3357,6 @@ extension SessionProtos_ReceiptMessage.TypeEnum: SwiftProtobuf._ProtoNameProvidi
]
}
extension SessionProtos_SyncMessage: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationBase, SwiftProtobuf._ProtoNameProviding {
static let protoMessageName: String = _protobuf_package + ".SyncMessage"
static let _protobuf_nameMap: SwiftProtobuf._NameMap = [
1: .same(proto: "sent"),
8: .same(proto: "padding"),
]
fileprivate class _StorageClass {
var _sent: SessionProtos_SyncMessage.Sent? = nil
var _padding: Data? = nil
static let defaultInstance = _StorageClass()
private init() {}
init(copying source: _StorageClass) {
_sent = source._sent
_padding = source._padding
}
}
fileprivate mutating func _uniqueStorage() -> _StorageClass {
if !isKnownUniquelyReferenced(&_storage) {
_storage = _StorageClass(copying: _storage)
}
return _storage
}
public var isInitialized: Bool {
return withExtendedLifetime(_storage) { (_storage: _StorageClass) in
if let v = _storage._sent, !v.isInitialized {return false}
return true
}
}
mutating func decodeMessage<D: SwiftProtobuf.Decoder>(decoder: inout D) throws {
_ = _uniqueStorage()
try withExtendedLifetime(_storage) { (_storage: _StorageClass) in
while let fieldNumber = try decoder.nextFieldNumber() {
switch fieldNumber {
case 1: try decoder.decodeSingularMessageField(value: &_storage._sent)
case 8: try decoder.decodeSingularBytesField(value: &_storage._padding)
default: break
}
}
}
}
func traverse<V: SwiftProtobuf.Visitor>(visitor: inout V) throws {
try withExtendedLifetime(_storage) { (_storage: _StorageClass) in
if let v = _storage._sent {
try visitor.visitSingularMessageField(value: v, fieldNumber: 1)
}
if let v = _storage._padding {
try visitor.visitSingularBytesField(value: v, fieldNumber: 8)
}
}
try unknownFields.traverse(visitor: &visitor)
}
static func ==(lhs: SessionProtos_SyncMessage, rhs: SessionProtos_SyncMessage) -> Bool {
if lhs._storage !== rhs._storage {
let storagesAreEqual: Bool = withExtendedLifetime((lhs._storage, rhs._storage)) { (_args: (_StorageClass, _StorageClass)) in
let _storage = _args.0
let rhs_storage = _args.1
if _storage._sent != rhs_storage._sent {return false}
if _storage._padding != rhs_storage._padding {return false}
return true
}
if !storagesAreEqual {return false}
}
if lhs.unknownFields != rhs.unknownFields {return false}
return true
}
}
extension SessionProtos_SyncMessage.Sent: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationBase, SwiftProtobuf._ProtoNameProviding {
static let protoMessageName: String = SessionProtos_SyncMessage.protoMessageName + ".Sent"
static let _protobuf_nameMap: SwiftProtobuf._NameMap = [
1: .same(proto: "destination"),
2: .same(proto: "timestamp"),
3: .same(proto: "message"),
4: .same(proto: "expirationStartTimestamp"),
6: .same(proto: "isRecipientUpdate"),
]
fileprivate class _StorageClass {
var _destination: String? = nil
var _timestamp: UInt64? = nil
var _message: SessionProtos_DataMessage? = nil
var _expirationStartTimestamp: UInt64? = nil
var _isRecipientUpdate: Bool? = nil
static let defaultInstance = _StorageClass()
private init() {}
init(copying source: _StorageClass) {
_destination = source._destination
_timestamp = source._timestamp
_message = source._message
_expirationStartTimestamp = source._expirationStartTimestamp
_isRecipientUpdate = source._isRecipientUpdate
}
}
fileprivate mutating func _uniqueStorage() -> _StorageClass {
if !isKnownUniquelyReferenced(&_storage) {
_storage = _StorageClass(copying: _storage)
}
return _storage
}
public var isInitialized: Bool {
return withExtendedLifetime(_storage) { (_storage: _StorageClass) in
if let v = _storage._message, !v.isInitialized {return false}
return true
}
}
mutating func decodeMessage<D: SwiftProtobuf.Decoder>(decoder: inout D) throws {
_ = _uniqueStorage()
try withExtendedLifetime(_storage) { (_storage: _StorageClass) in
while let fieldNumber = try decoder.nextFieldNumber() {
switch fieldNumber {
case 1: try decoder.decodeSingularStringField(value: &_storage._destination)
case 2: try decoder.decodeSingularUInt64Field(value: &_storage._timestamp)
case 3: try decoder.decodeSingularMessageField(value: &_storage._message)
case 4: try decoder.decodeSingularUInt64Field(value: &_storage._expirationStartTimestamp)
case 6: try decoder.decodeSingularBoolField(value: &_storage._isRecipientUpdate)
default: break
}
}
}
}
func traverse<V: SwiftProtobuf.Visitor>(visitor: inout V) throws {
try withExtendedLifetime(_storage) { (_storage: _StorageClass) in
if let v = _storage._destination {
try visitor.visitSingularStringField(value: v, fieldNumber: 1)
}
if let v = _storage._timestamp {
try visitor.visitSingularUInt64Field(value: v, fieldNumber: 2)
}
if let v = _storage._message {
try visitor.visitSingularMessageField(value: v, fieldNumber: 3)
}
if let v = _storage._expirationStartTimestamp {
try visitor.visitSingularUInt64Field(value: v, fieldNumber: 4)
}
if let v = _storage._isRecipientUpdate {
try visitor.visitSingularBoolField(value: v, fieldNumber: 6)
}
}
try unknownFields.traverse(visitor: &visitor)
}
static func ==(lhs: SessionProtos_SyncMessage.Sent, rhs: SessionProtos_SyncMessage.Sent) -> Bool {
if lhs._storage !== rhs._storage {
let storagesAreEqual: Bool = withExtendedLifetime((lhs._storage, rhs._storage)) { (_args: (_StorageClass, _StorageClass)) in
let _storage = _args.0
let rhs_storage = _args.1
if _storage._destination != rhs_storage._destination {return false}
if _storage._timestamp != rhs_storage._timestamp {return false}
if _storage._message != rhs_storage._message {return false}
if _storage._expirationStartTimestamp != rhs_storage._expirationStartTimestamp {return false}
if _storage._isRecipientUpdate != rhs_storage._isRecipientUpdate {return false}
return true
}
if !storagesAreEqual {return false}
}
if lhs.unknownFields != rhs.unknownFields {return false}
return true
}
}
extension SessionProtos_AttachmentPointer: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationBase, SwiftProtobuf._ProtoNameProviding {
static let protoMessageName: String = _protobuf_package + ".AttachmentPointer"
static let _protobuf_nameMap: SwiftProtobuf._NameMap = [

View File

@ -35,10 +35,10 @@ message TypingMessage {
}
message Content {
optional DataMessage dataMessage = 1;
optional SyncMessage syncMessage = 2;
optional ReceiptMessage receiptMessage = 5;
optional TypingMessage typingMessage = 6;
optional DataMessage dataMessage = 1;
optional ReceiptMessage receiptMessage = 5;
optional TypingMessage typingMessage = 6;
optional ConfigurationMessage configurationMessage = 7;
}
message ClosedGroupCiphertextMessageWrapper {
@ -48,6 +48,13 @@ message ClosedGroupCiphertextMessageWrapper {
optional bytes ephemeralPublicKey = 2;
}
message KeyPair {
// @required
required bytes publicKey = 1;
// @required
required bytes privateKey = 2;
}
message DataMessage {
enum Flags {
@ -171,13 +178,6 @@ message DataMessage {
MEMBER_LEFT = 7;
}
message KeyPair {
// @required
required bytes publicKey = 1;
// @required
required bytes privateKey = 2;
}
message KeyPairWrapper {
// @required
required bytes publicKey = 1; // The public key of the user the key pair is meant for
@ -207,9 +207,24 @@ message DataMessage {
repeated Preview preview = 10;
optional LokiProfile profile = 101;
optional ClosedGroupControlMessage closedGroupControlMessage = 104;
optional string syncTarget = 105;
optional PublicChatInfo publicChatInfo = 999;
}
message ConfigurationMessage {
message ClosedGroup {
optional bytes publicKey = 1;
optional string name = 2;
optional KeyPair encryptionKeyPair = 3;
repeated bytes members = 4;
repeated bytes admins = 5;
}
repeated ClosedGroup closedGroups = 1;
repeated string openGroups = 2;
}
message ReceiptMessage {
enum Type {
@ -222,20 +237,6 @@ message ReceiptMessage {
repeated uint64 timestamp = 2;
}
message SyncMessage {
message Sent {
optional string destination = 1;
optional uint64 timestamp = 2;
optional DataMessage message = 3;
optional uint64 expirationStartTimestamp = 4;
optional bool isRecipientUpdate = 6 [default = false];
}
optional Sent sent = 1;
optional bytes padding = 8;
}
message AttachmentPointer {
enum Flags {

View File

@ -13,6 +13,7 @@ extension MessageReceiver {
case let message as TypingIndicator: handleTypingIndicator(message, using: transaction)
case let message as ClosedGroupControlMessage: handleClosedGroupControlMessage(message, using: transaction)
case let message as ExpirationTimerUpdate: handleExpirationTimerUpdate(message, using: transaction)
case let message as ConfigurationMessage: handleConfigurationMessage(message, using: transaction)
case let message as VisibleMessage: try handleVisibleMessage(message, associatedWithProto: proto, openGroupID: openGroupID, isBackgroundPoll: isBackgroundPoll, using: transaction)
default: fatalError()
}
@ -141,6 +142,22 @@ extension MessageReceiver {
message.save(with: transaction)
SSKEnvironment.shared.disappearingMessagesJob.startIfNecessary()
}
private static func handleConfigurationMessage(_ message: ConfigurationMessage, using transaction: Any) {
guard message.sender == getUserHexEncodedPublicKey() else { return }
let storage = SNMessagingKitConfiguration.shared.storage
let allClosedGroupPublicKeys = storage.getUserClosedGroupPublicKeys()
for closedGroup in message.closedGroups {
guard !allClosedGroupPublicKeys.contains(closedGroup.publicKey) else { continue }
handleNewClosedGroup(groupPublicKey: closedGroup.publicKey, name: closedGroup.name, encryptionKeyPair: closedGroup.encryptionKeyPair,
members: [String](closedGroup.members), admins: [String](closedGroup.admins), using: transaction)
}
let allOpenGroups = Set(storage.getAllUserOpenGroups().keys)
for openGroupURL in message.openGroups {
guard !allOpenGroups.contains(openGroupURL) else { continue }
OpenGroupManager.shared.add(with: openGroupURL, using: transaction).retainUntilComplete()
}
}
@discardableResult
public static func handleVisibleMessage(_ message: VisibleMessage, associatedWithProto proto: SNProtoContent, openGroupID: String?, isBackgroundPoll: Bool, using transaction: Any) throws -> String {
@ -182,7 +199,7 @@ extension MessageReceiver {
}
}
// Get or create thread
guard let threadID = storage.getOrCreateThread(for: message.sender!, groupPublicKey: message.groupPublicKey, openGroupID: openGroupID, using: transaction) else { throw Error.noThread }
guard let threadID = storage.getOrCreateThread(for: message.syncTarget ?? message.sender!, groupPublicKey: message.groupPublicKey, openGroupID: openGroupID, using: transaction) else { throw Error.noThread }
// Parse quote if needed
var tsQuotedMessage: TSQuotedMessage? = nil
if message.quote != nil && proto.dataMessage?.quote != nil, let thread = TSThread.fetch(uniqueId: threadID, transaction: transaction) {
@ -200,12 +217,12 @@ extension MessageReceiver {
}
}
// Persist the message
guard let tsIncomingMessageID = storage.persist(message, quotedMessage: tsQuotedMessage, linkPreview: owsLinkPreview,
guard let tsMessageID = storage.persist(message, quotedMessage: tsQuotedMessage, linkPreview: owsLinkPreview,
groupPublicKey: message.groupPublicKey, openGroupID: openGroupID, using: transaction) else { throw Error.noThread }
message.threadID = threadID
// Start attachment downloads if needed
attachmentsToDownload.forEach { attachmentID in
let downloadJob = AttachmentDownloadJob(attachmentID: attachmentID, tsIncomingMessageID: tsIncomingMessageID)
let downloadJob = AttachmentDownloadJob(attachmentID: attachmentID, tsMessageID: tsMessageID)
if isMainAppAndActive {
JobQueue.shared.add(downloadJob, using: transaction)
} else {
@ -218,13 +235,13 @@ extension MessageReceiver {
}
// Keep track of the open group server message ID message ID relationship
if let serverID = message.openGroupServerMessageID {
storage.setIDForMessage(withServerID: serverID, to: tsIncomingMessageID, using: transaction)
storage.setIDForMessage(withServerID: serverID, to: tsMessageID, using: transaction)
}
// Notify the user if needed
guard (isMainAppAndActive || isBackgroundPoll), let tsIncomingMessage = TSIncomingMessage.fetch(uniqueId: tsIncomingMessageID, transaction: transaction),
let thread = TSThread.fetch(uniqueId: threadID, transaction: transaction) else { return tsIncomingMessageID }
guard (isMainAppAndActive || isBackgroundPoll), let tsIncomingMessage = TSMessage.fetch(uniqueId: tsMessageID, transaction: transaction) as? TSIncomingMessage,
let thread = TSThread.fetch(uniqueId: threadID, transaction: transaction) else { return tsMessageID }
SSKEnvironment.shared.notificationsManager!.notifyUser(for: tsIncomingMessage, in: thread, transaction: transaction)
return tsIncomingMessageID
return tsMessageID
}
private static func handleClosedGroupControlMessage(_ message: ClosedGroupControlMessage, using transaction: Any) {
@ -242,11 +259,14 @@ extension MessageReceiver {
private static func handleNewClosedGroup(_ message: ClosedGroupControlMessage, using transaction: Any) {
// Prepare
guard case let .new(publicKeyAsData, name, encryptionKeyPair, membersAsData, adminsAsData) = message.kind else { return }
let transaction = transaction as! YapDatabaseReadWriteTransaction
// Unwrap the message
let groupPublicKey = publicKeyAsData.toHexString()
let members = membersAsData.map { $0.toHexString() }
let admins = adminsAsData.map { $0.toHexString() }
handleNewClosedGroup(groupPublicKey: groupPublicKey, name: name, encryptionKeyPair: encryptionKeyPair, members: members, admins: admins, using: transaction)
}
private static func handleNewClosedGroup(groupPublicKey: String, name: String, encryptionKeyPair: ECKeyPair, members: [String], admins: [String], using transaction: Any) {
let transaction = transaction as! YapDatabaseReadWriteTransaction
// Create the group
let groupID = LKGroupUtilities.getEncodedClosedGroupIDAsData(groupPublicKey)
let group = TSGroupModel(title: name, memberIds: members, image: nil, groupId: groupID, groupType: .closedGroup, adminIds: admins)
@ -294,9 +314,9 @@ extension MessageReceiver {
return SNLog("Couldn't decrypt closed group encryption key pair.")
}
// Parse it
let proto: SNProtoDataMessageClosedGroupControlMessageKeyPair
let proto: SNProtoKeyPair
do {
proto = try SNProtoDataMessageClosedGroupControlMessageKeyPair.parseData(plaintext)
proto = try SNProtoKeyPair.parseData(plaintext)
} catch {
return SNLog("Couldn't parse closed group encryption key pair.")
}
@ -341,7 +361,7 @@ extension MessageReceiver {
infoMessage.save(with: transaction)
}
}
private static func handleClosedGroupMembersRemoved(_ message: ClosedGroupControlMessage, using transaction: Any) {
guard case let .membersRemoved(membersAsData) = message.kind else { return }
let transaction = transaction as! YapDatabaseReadWriteTransaction
@ -364,6 +384,7 @@ extension MessageReceiver {
let _ = PushNotificationAPI.performOperation(.unsubscribe, for: groupPublicKey, publicKey: userPublicKey)
}
// Generate and distribute a new encryption key pair if needed
// NOTE: If we're the admin we can be sure at this point that we weren't removed
let isCurrentUserAdmin = group.groupAdminIds.contains(getUserHexEncodedPublicKey())
if isCurrentUserAdmin {
do {

View File

@ -95,8 +95,6 @@ public enum MessageReceiver {
}
// Don't process the envelope any further if the sender is blocked
guard !isBlocked(sender) else { throw Error.senderBlocked }
// Ignore self sends
guard sender != userPublicKey else { throw Error.selfSend }
// Parse the proto
let proto: SNProtoContent
do {
@ -111,24 +109,33 @@ public enum MessageReceiver {
if let typingIndicator = TypingIndicator.fromProto(proto) { return typingIndicator }
if let closedGroupControlMessage = ClosedGroupControlMessage.fromProto(proto) { return closedGroupControlMessage }
if let expirationTimerUpdate = ExpirationTimerUpdate.fromProto(proto) { return expirationTimerUpdate }
if let configurationMessage = ConfigurationMessage.fromProto(proto) { return configurationMessage }
if let visibleMessage = VisibleMessage.fromProto(proto) { return visibleMessage }
return nil
}()
if let message = message {
// Ignore self sends if needed
if !message.isSelfSendValid {
guard sender != userPublicKey else { throw Error.selfSend }
}
// Guard against control messages in open groups
if isOpenGroupMessage {
guard message is VisibleMessage else { throw Error.invalidMessage }
}
// Finish parsing
message.sender = sender
message.recipient = userPublicKey
message.sentTimestamp = envelope.timestamp
message.receivedTimestamp = NSDate.millisecondTimestamp()
message.groupPublicKey = groupPublicKey
message.openGroupServerMessageID = openGroupMessageServerID
// Validate
var isValid = message.isValid
if message is VisibleMessage && !isValid && proto.dataMessage?.attachments.isEmpty == false {
isValid = true
}
guard isValid else { throw Error.invalidMessage }
// Return
return (message, proto)
} else {
throw Error.unknownMessage

View File

@ -62,7 +62,7 @@ extension MessageSender {
// Generate the new encryption key pair
let newKeyPair = Curve25519.generateKeyPair()
// Distribute it
let proto = try SNProtoDataMessageClosedGroupControlMessageKeyPair.builder(publicKey: newKeyPair.publicKey,
let proto = try SNProtoKeyPair.builder(publicKey: newKeyPair.publicKey,
privateKey: newKeyPair.privateKey).build()
let plaintext = try proto.serializedData()
let wrappers = try targetMembers.compactMap { publicKey -> ClosedGroupControlMessage.KeyPairWrapper in
@ -165,6 +165,7 @@ extension MessageSender {
public static func removeMembers(_ membersToRemove: Set<String>, to groupPublicKey: String, using transaction: YapDatabaseReadWriteTransaction) throws {
// Get the group, check preconditions & prepare
let userPublicKey = getUserHexEncodedPublicKey()
let groupID = LKGroupUtilities.getEncodedClosedGroupIDAsData(groupPublicKey)
let threadID = TSGroupThread.threadId(fromGroupId: groupID)
guard let thread = TSGroupThread.fetch(uniqueId: threadID, transaction: transaction) else {
@ -175,9 +176,13 @@ extension MessageSender {
SNLog("Invalid closed group update.")
throw Error.invalidClosedGroupUpdate
}
guard !membersToRemove.contains(userPublicKey) else {
SNLog("Invalid closed group update.")
throw Error.invalidClosedGroupUpdate
}
let group = thread.groupModel
let members = Set(group.groupMemberIds).subtracting(membersToRemove)
let isCurrentUserAdmin = group.groupAdminIds.contains(getUserHexEncodedPublicKey())
let isCurrentUserAdmin = group.groupAdminIds.contains(userPublicKey)
// Send the update to the group and generate + distribute a new encryption key pair if needed
let closedGroupControlMessage = ClosedGroupControlMessage(kind: .membersRemoved(members: membersToRemove.map { Data(hex: $0) }))
if isCurrentUserAdmin {

View File

@ -105,7 +105,7 @@ public final class MessageSender : NSObject {
}
// MARK: One-on-One Chats & Closed Groups
internal static func sendToSnodeDestination(_ destination: Message.Destination, message: Message, using transaction: Any) -> Promise<Void> {
internal static func sendToSnodeDestination(_ destination: Message.Destination, message: Message, using transaction: Any, isSyncMessage: Bool = false) -> Promise<Void> {
let (promise, seal) = Promise<Void>.pending()
let storage = SNMessagingKitConfiguration.shared.storage
let transaction = transaction as! YapDatabaseReadWriteTransaction
@ -137,8 +137,8 @@ public final class MessageSender : NSObject {
}
// Validate the message
guard message.isValid else { handleFailure(with: Error.invalidMessage, using: transaction); return promise }
// Stop here if this is a self-send
guard !isSelfSend else {
// Stop here if this is a self-send (unless it's a configuration message or a sync message)
guard !isSelfSend || message is ConfigurationMessage || isSyncMessage else {
storage.write(with: { transaction in
MessageSender.handleSuccessfulMessageSend(message, to: destination, using: transaction)
seal.fulfill(())
@ -240,8 +240,8 @@ public final class MessageSender : NSObject {
}
}
storage.write(with: { transaction in
MessageSender.handleSuccessfulMessageSend(message, to: destination, using: transaction)
var shouldNotify = (message is VisibleMessage)
MessageSender.handleSuccessfulMessageSend(message, to: destination, isSyncMessage: isSyncMessage, using: transaction)
var shouldNotify = (message is VisibleMessage && !isSyncMessage)
if let closedGroupControlMessage = message as? ClosedGroupControlMessage, case .new = closedGroupControlMessage.kind {
shouldNotify = true
}
@ -339,10 +339,12 @@ public final class MessageSender : NSObject {
}
// MARK: Success & Failure Handling
public static func handleSuccessfulMessageSend(_ message: Message, to destination: Message.Destination, using transaction: Any) {
public static func handleSuccessfulMessageSend(_ message: Message, to destination: Message.Destination, isSyncMessage: Bool = false, using transaction: Any) {
let storage = SNMessagingKitConfiguration.shared.storage
let transaction = transaction as! YapDatabaseReadWriteTransaction
guard let tsMessage = TSOutgoingMessage.find(withTimestamp: message.sentTimestamp!) else { return }
// Ignore future self-sends
Storage.shared.addReceivedMessageTimestamp(message.sentTimestamp!, using: transaction)
// Track the open group server message ID
tsMessage.openGroupServerMessageID = message.openGroupServerMessageID ?? 0
tsMessage.save(with: transaction)
@ -360,6 +362,15 @@ public final class MessageSender : NSObject {
}
// Start the disappearing messages timer if needed
OWSDisappearingMessagesJob.shared().startAnyExpiration(for: tsMessage, expirationStartedAt: NSDate.millisecondTimestamp(), transaction: transaction)
// Sync the message if:
// it's a visible message
// we didn't sync it already
let userPublicKey = getUserHexEncodedPublicKey()
if case .contact(let publicKey) = destination, !isSyncMessage, let message = message as? VisibleMessage {
message.syncTarget = publicKey
// FIXME: Make this a job
sendToSnodeDestination(.contact(publicKey: userPublicKey), message: message, using: transaction, isSyncMessage: true).retainUntilComplete()
}
}
public static func handleFailedMessageSend(_ message: Message, with error: Swift.Error, using transaction: Any) {

View File

@ -146,21 +146,13 @@ public final class OpenGroupPoller : NSObject {
let groupProto = SNProtoGroupContext.builder(id: id, type: .deliver)
groupProto.setName(openGroup.displayName)
dataMessageProto.setGroup(try! groupProto.build())
// Sync target
if wasSentByCurrentUser {
dataMessageProto.setSyncTarget(openGroup.id)
}
// Content
let content = SNProtoContent.builder()
if !wasSentByCurrentUser { // Incoming message
content.setDataMessage(try! dataMessageProto.build())
} else { // Outgoing message
// FIXME: This needs to be updated as we removed sync message handling
let syncMessageSentBuilder = SNProtoSyncMessageSent.builder()
syncMessageSentBuilder.setMessage(try! dataMessageProto.build())
syncMessageSentBuilder.setDestination(userPublicKey)
syncMessageSentBuilder.setTimestamp(message.timestamp)
let syncMessageSent = try! syncMessageSentBuilder.build()
let syncMessageBuilder = SNProtoSyncMessage.builder()
syncMessageBuilder.setSent(syncMessageSent)
content.setSyncMessage(try! syncMessageBuilder.build())
}
content.setDataMessage(try! dataMessageProto.build())
// Envelope
let envelope = SNProtoEnvelope.builder(type: .unidentifiedSender, timestamp: message.timestamp)
envelope.setSource(senderPublicKey)

View File

@ -28,6 +28,7 @@ public protocol SessionMessagingKitStorageProtocol {
// MARK: - Shared Sender Keys
func getClosedGroupPrivateKey(for publicKey: String) -> String?
func getUserClosedGroupPublicKeys() -> Set<String>
func isClosedGroup(_ publicKey: String) -> Bool
// MARK: - Jobs
@ -48,9 +49,11 @@ public protocol SessionMessagingKitStorageProtocol {
func removeAuthToken(for server: String, using transaction: Any)
// MARK: - Open Groups
func getAllUserOpenGroups() -> [String:OpenGroup]
func getOpenGroup(for threadID: String) -> OpenGroup?
func getThreadID(for openGroupID: String) -> String?
func updateMessageIDCollectionByPruningMessagesWithIDs(_ messageIDs: Set<String>, using transaction: Any)
// MARK: - Open Group Public Keys

View File

@ -39,6 +39,7 @@ public final class Values : NSObject {
@objc public static var separatorThickness: CGFloat { return 1 / UIScreen.main.scale }
@objc public static let tabBarHeight = isIPhone5OrSmaller ? CGFloat(32) : CGFloat(48)
@objc public static let settingButtonHeight = isIPhone5OrSmaller ? CGFloat(52) : CGFloat(75)
@objc public static let defaultSettingRowHeight = CGFloat(60)
@objc public static let modalCornerRadius = CGFloat(10)
@objc public static let modalButtonCornerRadius = CGFloat(5)
@objc public static let fakeChatBubbleWidth = CGFloat(224)

View File

@ -8,11 +8,13 @@ public enum SNUserDefaults {
case hasViewedSeed
case isUsingFullAPNs
case isMigratingToV2KeyPair
case isUsingMultiDevice
}
public enum Date : Swift.String {
case lastProfilePictureUpload
case lastKeyPairMigrationNudge
case lastConfigurationSync
}
public enum Double : Swift.String {

View File

@ -0,0 +1,8 @@
import UIKit
extension UIEdgeInsets {
public init(uniform value: CGFloat) {
self.init(top: value, left: value, bottom: value, right: value)
}
}

View File

@ -1,2 +1,8 @@
extension Storage : SessionMessagingKitStorageProtocol, SessionProtocolKitStorageProtocol, SessionSnodeKitStorageProtocol { }
extension Storage : SessionMessagingKitStorageProtocol, SessionProtocolKitStorageProtocol, SessionSnodeKitStorageProtocol {
public func updateMessageIDCollectionByPruningMessagesWithIDs(_ messageIDs: Set<String>, using transaction: Any) {
let transaction = transaction as! YapDatabaseReadWriteTransaction
OWSPrimaryStorage.shared().updateMessageIDCollectionByPruningMessagesWithIDs(messageIDs, in: transaction)
}
}

View File

@ -0,0 +1,29 @@
extension ConfigurationMessage {
public static func getCurrent() -> ConfigurationMessage {
var closedGroups: Set<ClosedGroup> = []
var openGroups: Set<String> = []
Storage.read { transaction in
TSGroupThread.enumerateCollectionObjects(with: transaction) { object, _ in
guard let thread = object as? TSGroupThread else { return }
switch thread.groupModel.groupType {
case .closedGroup:
guard thread.isCurrentUserMemberInGroup() else { return }
let groupID = thread.groupModel.groupId
let groupPublicKey = LKGroupUtilities.getDecodedGroupID(groupID)
guard Storage.shared.isClosedGroup(groupPublicKey),
let encryptionKeyPair = Storage.shared.getLatestClosedGroupEncryptionKeyPair(for: groupPublicKey) else { return }
let closedGroup = ClosedGroup(publicKey: groupPublicKey, name: thread.groupModel.groupName!, encryptionKeyPair: encryptionKeyPair,
members: Set(thread.groupModel.groupMemberIds), admins: Set(thread.groupModel.groupAdminIds))
closedGroups.insert(closedGroup)
case .openGroup:
guard let openGroup = Storage.shared.getOpenGroup(for: thread.uniqueId!) else { return }
openGroups.insert(openGroup.server)
default: break
}
}
}
return ConfigurationMessage(closedGroups: closedGroups, openGroups: openGroups)
}
}

View File

@ -1,131 +0,0 @@
import PromiseKit
// TODO: Clean
@objc(LKPublicChatManager)
public final class PublicChatManager : NSObject {
private let storage = OWSPrimaryStorage.shared()
@objc public var chats: [String:OpenGroup] = [:]
private var pollers: [String:OpenGroupPoller] = [:]
private var isPolling = false
private var userHexEncodedPublicKey: String? {
return OWSIdentityManager.shared().identityKeyPair()?.hexEncodedPublicKey
}
public enum Error : Swift.Error {
case chatCreationFailed
case userPublicKeyNotFound
}
@objc public static let shared = PublicChatManager()
private override init() {
super.init()
NotificationCenter.default.addObserver(self, selector: #selector(onThreadDeleted(_:)), name: .threadDeleted, object: nil)
refreshChatsAndPollers()
}
deinit {
NotificationCenter.default.removeObserver(self)
}
@objc public func startPollersIfNeeded() {
for (threadID, publicChat) in chats {
if let poller = pollers[threadID] {
poller.startIfNeeded()
} else {
let poller = OpenGroupPoller(for: publicChat)
poller.startIfNeeded()
pollers[threadID] = poller
}
}
isPolling = true
}
@objc public func stopPollers() {
for poller in pollers.values { poller.stop() }
isPolling = false
}
public func addChat(server: String, channel: UInt64) -> Promise<OpenGroup> {
if let existingChat = getChat(server: server, channel: channel) {
if let newChat = self.addChat(server: server, channel: channel, name: existingChat.displayName) {
return Promise.value(newChat)
} else {
return Promise(error: Error.chatCreationFailed)
}
}
return OpenGroupAPI.getInfo(for: channel, on: server).map2 { channelInfo -> OpenGroup in
guard let chat = self.addChat(server: server, channel: channel, name: channelInfo.displayName) else { throw Error.chatCreationFailed }
return chat
}
}
@discardableResult
@objc(addChatWithServer:channel:name:)
public func addChat(server: String, channel: UInt64, name: String) -> OpenGroup? {
guard let chat = OpenGroup(channel: channel, server: server, displayName: name, isDeletable: true) else { return nil }
let model = TSGroupModel(title: chat.displayName, memberIds: [userHexEncodedPublicKey!, chat.server], image: nil, groupId: LKGroupUtilities.getEncodedOpenGroupIDAsData(chat.id), groupType: .openGroup, adminIds: [])
// Store the group chat mapping
Storage.writeSync { transaction in
let thread = TSGroupThread.getOrCreateThread(with: model, transaction: transaction)
// Save the group chat
Storage.shared.setOpenGroup(chat, for: thread.uniqueId!, using: transaction)
}
// Update chats and pollers
self.refreshChatsAndPollers()
return chat
}
@objc(addChatWithServer:channel:)
public func objc_addChat(server: String, channel: UInt64) -> AnyPromise {
return AnyPromise.from(addChat(server: server, channel: channel))
}
@objc func refreshChatsAndPollers() {
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() }
}
@objc private func onThreadDeleted(_ notification: Notification) {
guard let threadId = notification.userInfo?["threadId"] as? String else { return }
// Reset the last message cache
if let chat = self.chats[threadId] {
Storage.write { transaction in
Storage.shared.clearAllData(for: chat.channel, on: chat.server, using: transaction)
}
}
// Remove the chat from the db
Storage.write { transaction in
Storage.shared.removeOpenGroup(for: threadId, using: transaction)
}
refreshChatsAndPollers()
}
public func getChat(server: String, channel: UInt64) -> OpenGroup? {
return chats.values.first { chat in
return chat.server == server && chat.channel == channel
}
}
}