From 78a689943b965a5b74a3a6bbb4d8e41c7709d6b4 Mon Sep 17 00:00:00 2001 From: Kee Jefferys Date: Wed, 28 Aug 2019 11:07:25 +1000 Subject: [PATCH 01/12] Add Beta privacy policy --- privacy-policy.md | 53 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 53 insertions(+) create mode 100644 privacy-policy.md diff --git a/privacy-policy.md b/privacy-policy.md new file mode 100644 index 000000000..851cc6c97 --- /dev/null +++ b/privacy-policy.md @@ -0,0 +1,53 @@ + +## Privacy Policy + +Loki Messenger is a client application that interacts with the “Service Node” network, which is not wholly owned or run by LAG Foundation Ltd (the publishers of Loki Messenger). This privacy policy can only apply between the user and LAG Foundation Ltd and is limited to any information that LAG Foundation Ltd collects by providing the Loki Messenger client application. Your messages are always encrypted when they travel over the internet, so they can never be shared or viewed (in plaintext) by anyone but yourself and the intended recipients. + +### Information you provide + +Messages. LAG Foundation and the Service Node network cannot decrypt or otherwise access the content of your messages. The Service Node network queues end-to-end encrypted messages on its servers for delivery to devices that are temporarily offline (e.g. a phone whose battery has died) or can not otherwise be reached. Your message history is stored on your own device or devices. + +User Support. If you contact any member of the Loki Messenger team, any personal data you may share with us is kept only for the purposes of researching the issue and contacting you about your case. + +### Information that is or may be automatically collected + +Messages. Due to the decentralised nature of the Loki Messenger, your encrypted messages will be stored on the Service Node network. Although no one, including the Service Nodes, can see the contents of the messages, in beta non Lokinet integrated Loki Messenger your metadata may be collected by Service Nodes on the network. This may include who messages are being sent to, who they are being sent by, IP addresses, and public key information. No personally identifying information is included in messages, however if Service Node operators are able to identify which is your specific public key (account number) through some other means (for instance, by posting it on Twitter), they may be able to link you to your Messenger identity. LAG Foundation Ltd is only responsible for a small subset of the Service Node network and can not guarantee that your metadata is not being tracked when using the Beta Loki Messenger version. In this version of the Loki Messenger, there is limited metadata privacy protection. We will ensure that this is made clear in the app, until an update which includes much stronger protections is released. + + + +Usage Information. the Loki Messenger beta may automatically send us usage information periodically. All personally identifying information is stripped from this data that you provide to us. This data is critical to us to understand how we can improve the Loki Messenger by understanding how users interact with the app, and what is going wrong when the app stops working. This data includes: how often you use the app, how many people you are talking to, how many messages you are sending, how long you spend in the app each day, and any error messages that the app encounters. We do not collect your IP address, public key, contact list, conversation history, or any other type of personal information. When the Full version of Loki messenger is released this data collection will become opt in. + + + +You are able to audit how this data is collected in the app by visiting [https://github.com/loki-project/loki-messenger-ios](https://github.com/loki-project/loki-messenger-ios) and inspecting the code of our analytics module. + +### Information we may share + +Instances where LAG Foundation Ltd may need to share your data: + +- To meet any applicable law, regulation, legal process or enforceable governmental request. + +- To enforce applicable Terms, including investigation of potential violations. + +- To detect, prevent, or otherwise address fraud, security, or technical issues. + +- To protect against harm to the rights, property, or safety of LAG Foundation Ltd, our users, or the public as required or permitted by law. + + +Note: The LAG can only share the data it has obtained, which is very minimal even in the beta program, when Loki Messenger is released live such data will be anonymised before collection limiting the usefulness of such data for purposes other than analysing our broad userbase. + +Third Parties + +LAG Foundation Ltd distributes the Loki messenger iOS application through two primary sources, the Apple Store and directly through Github. These third party service providers may also collect data on your download of and usage of Loki Messenger. This data collection is our of our hands, although it is information we can view and use as stated in this policy. For more information about how these platforms collect and use information, you should read their privacy policies prior to using them. + +You may opt out of this data collection by building the iOS binaries from the repository [https://github.com/loki-project/loki-messenger-ios](https://github.com/loki-project/loki-messenger-ios) and sideloading the app onto a modified Apple device. + +### Updates + +We will update this privacy policy as needed so that it is current, accurate, and as clear as possible. Your continued use of our Services confirms your acceptance of our updated Privacy Policy. + +### Contact Us + +If you have questions about our Privacy Policy please contact us at team@loki.network. + +Last Updated 28/08/2019 From 80ef8bc9f7f33da929474591cf38b7dbf2511c01 Mon Sep 17 00:00:00 2001 From: Niels Andriesse Date: Thu, 29 Aug 2019 10:35:02 +1000 Subject: [PATCH 02/12] Disable note to self --- Signal/src/Loki/NewConversationViewController.swift | 4 ++++ Signal/translations/en.lproj/Localizable.strings | 2 ++ 2 files changed, 6 insertions(+) diff --git a/Signal/src/Loki/NewConversationViewController.swift b/Signal/src/Loki/NewConversationViewController.swift index a7e89efcb..f47c05e5c 100644 --- a/Signal/src/Loki/NewConversationViewController.swift +++ b/Signal/src/Loki/NewConversationViewController.swift @@ -105,6 +105,10 @@ final class NewConversationViewController : OWSViewController, OWSQRScannerDeleg let alert = UIAlertController(title: NSLocalizedString("Invalid Public Key", comment: ""), message: NSLocalizedString("Please check the public key you entered and try again.", comment: ""), preferredStyle: .alert) alert.addAction(UIAlertAction(title: NSLocalizedString("OK", comment: ""), style: .default, handler: nil)) presentAlert(alert) + } else if OWSIdentityManager.shared().identityKeyPair()!.hexEncodedPublicKey == hexEncodedPublicKey { + let alert = UIAlertController(title: NSLocalizedString("Can't Start Conversation", comment: ""), message: NSLocalizedString("Please enter the public key of the person you'd like to message.", comment: ""), preferredStyle: .alert) + alert.addAction(UIAlertAction(title: NSLocalizedString("OK", comment: ""), style: .default, handler: nil)) + presentAlert(alert) } else { let thread = TSContactThread.getOrCreateThread(contactId: hexEncodedPublicKey) SignalApp.shared().presentConversation(for: thread, action: .compose, animated: false) diff --git a/Signal/translations/en.lproj/Localizable.strings b/Signal/translations/en.lproj/Localizable.strings index 94e785776..fe08c09f0 100644 --- a/Signal/translations/en.lproj/Localizable.strings +++ b/Signal/translations/en.lproj/Localizable.strings @@ -2617,3 +2617,5 @@ "Scan the QR code of the person you'd like to securely message. They can find their QR code by going into Loki Messenger's in-app settings and clicking \"Show QR Code\"." = "Scan the QR code of the person you'd like to securely message. They can find their QR code by going into Loki Messenger's in-app settings and clicking \"Show QR Code\"."; "Scan QR Code" = "Scan QR Code"; "Loki" = "Loki"; +"Can't Start Conversation" = "Can't Start Conversation"; +"Please enter the public key of the person you'd like to message." = "Please enter the public key of the person you'd like to message."; From fcc87cf47aa32d6526392615f57bc175a23d82be Mon Sep 17 00:00:00 2001 From: Niels Andriesse Date: Thu, 29 Aug 2019 11:53:11 +1000 Subject: [PATCH 03/12] Update build number & version number --- Signal/Signal-Info.plist | 4 ++-- SignalMessaging/Info.plist | 2 +- SignalShareExtension/Info.plist | 4 ++-- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/Signal/Signal-Info.plist b/Signal/Signal-Info.plist index 2bddfb004..a2747fa18 100644 --- a/Signal/Signal-Info.plist +++ b/Signal/Signal-Info.plist @@ -30,7 +30,7 @@ CFBundlePackageType APPL CFBundleShortVersionString - 1.0.0 + 1.1.0 CFBundleSignature ???? CFBundleURLTypes @@ -47,7 +47,7 @@ CFBundleVersion - 11 + 12 ITSAppUsesNonExemptEncryption LOGS_EMAIL diff --git a/SignalMessaging/Info.plist b/SignalMessaging/Info.plist index 4c0d21863..a5ae6f942 100644 --- a/SignalMessaging/Info.plist +++ b/SignalMessaging/Info.plist @@ -15,7 +15,7 @@ CFBundlePackageType FMWK CFBundleShortVersionString - 1.0.0 + 1.1.0 CFBundleVersion $(CURRENT_PROJECT_VERSION) NSPrincipalClass diff --git a/SignalShareExtension/Info.plist b/SignalShareExtension/Info.plist index cbb6f2007..89c45dc62 100644 --- a/SignalShareExtension/Info.plist +++ b/SignalShareExtension/Info.plist @@ -17,9 +17,9 @@ CFBundlePackageType XPC! CFBundleShortVersionString - 1.0.0 + 1.1.0 CFBundleVersion - 11 + 12 ITSAppUsesNonExemptEncryption NSAppTransportSecurity From d1d928ee89446b56b201c3deb81e397d7c7cf2b2 Mon Sep 17 00:00:00 2001 From: Niels Andriesse Date: Thu, 29 Aug 2019 15:04:02 +1000 Subject: [PATCH 04/12] Refactor LokiLongPoller --- .../src/Loki/API/LokiLongPoller.swift | 90 +++++++++++++++++++ 1 file changed, 90 insertions(+) create mode 100644 SignalServiceKit/src/Loki/API/LokiLongPoller.swift diff --git a/SignalServiceKit/src/Loki/API/LokiLongPoller.swift b/SignalServiceKit/src/Loki/API/LokiLongPoller.swift new file mode 100644 index 000000000..d5ee89ddd --- /dev/null +++ b/SignalServiceKit/src/Loki/API/LokiLongPoller.swift @@ -0,0 +1,90 @@ +import PromiseKit + +@objc(LKLongPoller) +public final class LokiLongPoller : NSObject { + private let onMessagesReceived: ([SSKProtoEnvelope]) -> Void + private let storage = OWSPrimaryStorage.shared() + private var hasStarted = false + private var hasStopped = false + private var connections = Set>() + private var usedSnodes = Set() + + // MARK: Settings + private let connectionCount = 3 + private let retryInterval: TimeInterval = 4 + + // MARK: Convenience + private var userHexEncodedPublicKey: String { return OWSIdentityManager.shared().identityKeyPair()!.hexEncodedPublicKey } + + // MARK: Initialization + public init(onMessagesReceived: @escaping ([SSKProtoEnvelope]) -> Void) { + self.onMessagesReceived = onMessagesReceived + super.init() + } + + // MARK: Public API + public func startIfNeeded() { + guard !hasStarted else { return } + print("[Loki] Started long polling.") + hasStarted = true + hasStopped = false + openConnections() + } + + public func stopIfNeeded() { + guard !hasStopped else { return } + print("[Loki] Stopped long polling.") + hasStarted = false + hasStopped = true + usedSnodes.removeAll() + } + + // MARK: Private API + private func openConnections() { + guard !hasStopped else { return } + LokiAPI.getSwarm(for: userHexEncodedPublicKey).then { [weak self] _ -> Guarantee<[Result]> in + guard let strongSelf = self else { return Guarantee.value([Result]()) } + strongSelf.usedSnodes.removeAll() + let connections: [Promise] = (0...pending() + strongSelf.openConnectionToNextSnode(seal: seal) + return promise + } + strongSelf.connections = Set(connections) + return when(resolved: connections) + }.ensure { [weak self] in + guard let strongSelf = self else { return } + Timer.scheduledTimer(withTimeInterval: strongSelf.retryInterval, repeats: false) { _ in + guard let strongSelf = self else { return } + strongSelf.openConnections() + } + } + } + + private func openConnectionToNextSnode(seal: Resolver) { + let swarm = LokiAPI.swarmCache[userHexEncodedPublicKey] ?? [] + let userHexEncodedPublicKey = self.userHexEncodedPublicKey + let unusedSnodes = Set(swarm).subtracting(usedSnodes) + if !unusedSnodes.isEmpty { + let nextSnode = unusedSnodes.randomElement()! + usedSnodes.insert(nextSnode) + print("[Loki] Opening long polling connection to \(nextSnode).") + longPoll(nextSnode, seal: seal).catch { [weak self] error in + print("[Loki] Long polling connection to \(nextSnode) failed; dropping it and switching to next snode.") + LokiAPI.dropIfNeeded(nextSnode, hexEncodedPublicKey: userHexEncodedPublicKey) + self?.openConnectionToNextSnode(seal: seal) + } + } else { + seal.fulfill(()) + } + } + + private func longPoll(_ target: LokiAPITarget, seal: Resolver) -> Promise { + return LokiAPI.getRawMessages(from: target, usingLongPolling: true).then { [weak self] rawResponse -> Promise in + guard let strongSelf = self, !strongSelf.hasStopped else { return Promise.value(()) } + let messages = LokiAPI.parseRawMessagesResponse(rawResponse, from: target) + strongSelf.onMessagesReceived(messages) + return strongSelf.longPoll(target, seal: seal) + } + } +} From 378a30e9d6962934ad038a1e61206c2a5c766f0c Mon Sep 17 00:00:00 2001 From: Niels Andriesse Date: Thu, 29 Aug 2019 15:21:45 +1000 Subject: [PATCH 05/12] Re-integrate LokiLongPoller --- Signal/src/AppDelegate.h | 1 + Signal/src/AppDelegate.m | 59 +++++---- .../AppSettings/AppSettingsViewController.m | 4 +- .../HomeView/HomeViewController.m | 3 +- .../src/Loki/API/LokiAPI+LongPolling.swift | 116 ------------------ .../src/Loki/API/LokiLongPoller.swift | 6 +- 6 files changed, 46 insertions(+), 143 deletions(-) delete mode 100644 SignalServiceKit/src/Loki/API/LokiAPI+LongPolling.swift diff --git a/Signal/src/AppDelegate.h b/Signal/src/AppDelegate.h index 0730d665f..762c9df57 100644 --- a/Signal/src/AppDelegate.h +++ b/Signal/src/AppDelegate.h @@ -8,6 +8,7 @@ extern NSString *const AppDelegateStoryboardMain; @interface AppDelegate : UIResponder +- (void)stopLongPollerIfNeeded; - (void)createGroupChatsIfNeeded; - (void)createRSSFeedsIfNeeded; - (void)startGroupChatPollersIfNeeded; diff --git a/Signal/src/AppDelegate.m b/Signal/src/AppDelegate.m index 2d28aebda..49c6150a7 100644 --- a/Signal/src/AppDelegate.m +++ b/Signal/src/AppDelegate.m @@ -63,7 +63,10 @@ static NSTimeInterval launchStartedAt; @property (nonatomic) BOOL hasInitialRootViewController; @property (nonatomic) BOOL areVersionMigrationsComplete; @property (nonatomic) BOOL didAppLaunchFail; + +// Loki @property (nonatomic) LKP2PServer *lokiP2PServer; +@property (nonatomic) LKLongPoller *lokiLongPoller; @property (nonatomic) LKGroupChatPoller *lokiPublicChatPoller; @property (nonatomic) LKRSSFeedPoller *lokiNewsFeedPoller; @property (nonatomic) LKRSSFeedPoller *lokiMessengerUpdatesFeedPoller; @@ -175,7 +178,7 @@ static NSTimeInterval launchStartedAt; [DDLog flushLog]; - [LKAPI stopLongPolling]; + [self stopLongPollerIfNeeded]; } - (void)applicationWillEnterForeground:(UIApplication *)application @@ -194,7 +197,8 @@ static NSTimeInterval launchStartedAt; [DDLog flushLog]; - [LKAPI stopLongPolling]; + [self stopLongPollerIfNeeded]; + if (self.lokiP2PServer) { [self.lokiP2PServer stop]; } } @@ -761,7 +765,7 @@ static NSTimeInterval launchStartedAt; [Environment.shared.contactsManager fetchSystemContactsOnceIfAlreadyAuthorized]; // Loki: Start long polling - [LKAPI startLongPollingIfNeeded]; + [self startLongPollerIfNeeded]; // Loki: Tell our friends that we are online [LKP2PAPI broadcastOnlineStatus]; @@ -1359,8 +1363,8 @@ static NSTimeInterval launchStartedAt; // For non-legacy users, read receipts are on by default. [self.readReceiptManager setAreReadReceiptsEnabled:YES]; - // Start long polling - [LKAPI startLongPollingIfNeeded]; + // Loki: Start long polling + [self startLongPollerIfNeeded]; } } @@ -1406,23 +1410,6 @@ static NSTimeInterval launchStartedAt; [UIViewController attemptRotationToDeviceOrientation]; } -#pragma mark - Long polling - -- (void)handleNewMessagesReceived:(NSNotification *)notification -{ - NSArray *messages = (NSArray *)notification.userInfo[@"messages"]; - NSLog(@"[Loki] Received %lu messages through long polling.", messages.count); - - for (SSKProtoEnvelope *envelope in messages) { - NSData *envelopeData = [envelope serializedDataAndReturnError:nil]; - if (envelopeData != nil) { - [SSKEnvironment.shared.messageReceiver handleReceivedEnvelopeData:envelopeData]; - } else { - OWSFailDebug(@"Failed to deserialize envelope."); - } - } -} - #pragma mark - status bar touches - (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event @@ -1487,6 +1474,34 @@ static NSTimeInterval launchStartedAt; #pragma mark - Loki +- (void)setUpLongPollerIfNeeded +{ + if (self.lokiLongPoller != nil) { return; } + NSString *userHexEncodedPublicKey = OWSIdentityManager.sharedManager.identityKeyPair.hexEncodedPublicKey; + if (userHexEncodedPublicKey == nil) { return; } + self.lokiLongPoller = [[LKLongPoller alloc] initOnMessagesReceived:^(NSArray *messages) { + for (SSKProtoEnvelope *message in messages) { + NSData *data = [message serializedDataAndReturnError:nil]; + if (data != nil) { + [SSKEnvironment.shared.messageReceiver handleReceivedEnvelopeData:data]; + } else { + NSLog(@"[Loki] Failed to deserialize envelope."); + } + } + }]; +} + +- (void)startLongPollerIfNeeded +{ + [self setUpLongPollerIfNeeded]; + [self.lokiLongPoller startIfNeeded]; +} + +- (void)stopLongPollerIfNeeded +{ + [self.lokiLongPoller stopIfNeeded]; +} + - (LKGroupChat *)lokiPublicChat { return [[LKGroupChat alloc] initWithServerID:@(LKGroupChatAPI.publicChatServerID).unsignedIntegerValue server:LKGroupChatAPI.publicChatServer displayName:NSLocalizedString(@"Loki Public Chat", @"") isDeletable:true]; diff --git a/Signal/src/ViewControllers/AppSettings/AppSettingsViewController.m b/Signal/src/ViewControllers/AppSettings/AppSettingsViewController.m index 893354032..c1119fd2c 100644 --- a/Signal/src/ViewControllers/AppSettings/AppSettingsViewController.m +++ b/Signal/src/ViewControllers/AppSettings/AppSettingsViewController.m @@ -2,6 +2,7 @@ // Copyright (c) 2019 Open Whisper Systems. All rights reserved. // +#import "AppDelegate.h" #import "AppSettingsViewController.h" #import "AboutTableViewController.h" #import "AdvancedSettingsTableViewController.h" @@ -533,7 +534,8 @@ [ThreadUtil deleteAllContent]; [SSKEnvironment.shared.identityManager clearIdentityKey]; [LKAPI clearRandomSnodePool]; - [LKAPI stopLongPolling]; + AppDelegate *appDelegate = (AppDelegate *)UIApplication.sharedApplication.delegate; + [appDelegate stopLongPollerIfNeeded]; [SSKEnvironment.shared.tsAccountManager resetForReregistration]; UIViewController *rootViewController = [[OnboardingController new] initialViewController]; OWSNavigationController *navigationController = [[OWSNavigationController alloc] initWithRootViewController:rootViewController]; diff --git a/Signal/src/ViewControllers/HomeView/HomeViewController.m b/Signal/src/ViewControllers/HomeView/HomeViewController.m index a43bb6a5e..d92171b60 100644 --- a/Signal/src/ViewControllers/HomeView/HomeViewController.m +++ b/Signal/src/ViewControllers/HomeView/HomeViewController.m @@ -680,7 +680,8 @@ typedef NS_ENUM(NSInteger, HomeViewControllerSection) { [ThreadUtil deleteAllContent]; [SSKEnvironment.shared.identityManager clearIdentityKey]; [LKAPI clearRandomSnodePool]; - [LKAPI stopLongPolling]; + AppDelegate *appDelegate = (AppDelegate *)UIApplication.sharedApplication.delegate; + [appDelegate stopLongPollerIfNeeded]; [SSKEnvironment.shared.tsAccountManager resetForReregistration]; UIViewController *rootViewController = [[OnboardingController new] initialViewController]; OWSNavigationController *navigationController = [[OWSNavigationController alloc] initWithRootViewController:rootViewController]; diff --git a/SignalServiceKit/src/Loki/API/LokiAPI+LongPolling.swift b/SignalServiceKit/src/Loki/API/LokiAPI+LongPolling.swift deleted file mode 100644 index 70554fd51..000000000 --- a/SignalServiceKit/src/Loki/API/LokiAPI+LongPolling.swift +++ /dev/null @@ -1,116 +0,0 @@ -import PromiseKit - -private typealias Callback = () -> Void - -public extension LokiAPI { - private static var isLongPolling = false - private static var shouldStopPolling = false - private static var usedSnodes = [LokiAPITarget]() - private static var cancels = [Callback]() - - /// Start long polling. - /// This will send a notification if new messages were received - @objc public static func startLongPollingIfNeeded() { - guard !isLongPolling else { return } - isLongPolling = true - shouldStopPolling = false - - print("[Loki] Started long polling.") - - longPoll() - } - - /// Stop long polling - @objc public static func stopLongPolling() { - shouldStopPolling = true - isLongPolling = false - usedSnodes.removeAll() - cancelAllPromises() - - print("[Loki] Stopped long polling.") - } - - /// The long polling loop - private static func longPoll() { - // This is here so we can stop the infinite loop - guard !shouldStopPolling else { return } - - getSwarm(for: userHexEncodedPublicKey).then { _ -> Guarantee<[Result]> in - var promises = [Promise]() - let connections = 3 - for i in 0.. [LokiAPITarget] { - let snodes = LokiAPI.swarmCache[userHexEncodedPublicKey] ?? [] - return snodes.filter { !usedSnodes.contains($0) } - } - - /// Open a connection to an unused snode and get messages from it - private static func openConnection() -> (Promise, cancel: Callback) { - var isCancelled = false - - let cancel = { - isCancelled = true - } - - func connectToNextSnode() -> Promise { - guard let nextSnode = getUnusedSnodes().first else { - // We don't have anymore unused snodes - return Promise.value(()) - } - - // Add the snode to the used array - usedSnodes.append(nextSnode) - - func getMessagesInfinitely(from target: LokiAPITarget) -> Promise { - // The only way to exit the infinite loop is to throw an error 3 times or cancel - return getRawMessages(from: target, usingLongPolling: true).then { rawResponse -> Promise in - // Check if we need to abort - guard !isCancelled else { throw PMKError.cancelled } - - // Process the messages - let messages = parseRawMessagesResponse(rawResponse, from: target) - - // Send our messages as a notification - NotificationCenter.default.post(name: .newMessagesReceived, object: nil, userInfo: ["messages": messages]) - - // Continue fetching if we haven't cancelled - return getMessagesInfinitely(from: target) - }.retryingIfNeeded(maxRetryCount: 3) - } - - // Keep getting messages for this snode - // If we errored out then connect to the next snode - return getMessagesInfinitely(from: nextSnode).recover { _ -> Promise in - // Cancelled, so just return successfully - guard !isCancelled else { return Promise.value(()) } - - // Connect to the next snode if we haven't cancelled - // We also need to remove the cached snode so we don't contact it again - dropIfNeeded(nextSnode, hexEncodedPublicKey: userHexEncodedPublicKey) - return connectToNextSnode() - } - } - - // Keep connecting to snodes - return (connectToNextSnode(), cancel) - } -} diff --git a/SignalServiceKit/src/Loki/API/LokiLongPoller.swift b/SignalServiceKit/src/Loki/API/LokiLongPoller.swift index d5ee89ddd..1acb6f7db 100644 --- a/SignalServiceKit/src/Loki/API/LokiLongPoller.swift +++ b/SignalServiceKit/src/Loki/API/LokiLongPoller.swift @@ -17,13 +17,13 @@ public final class LokiLongPoller : NSObject { private var userHexEncodedPublicKey: String { return OWSIdentityManager.shared().identityKeyPair()!.hexEncodedPublicKey } // MARK: Initialization - public init(onMessagesReceived: @escaping ([SSKProtoEnvelope]) -> Void) { + @objc public init(onMessagesReceived: @escaping ([SSKProtoEnvelope]) -> Void) { self.onMessagesReceived = onMessagesReceived super.init() } // MARK: Public API - public func startIfNeeded() { + @objc public func startIfNeeded() { guard !hasStarted else { return } print("[Loki] Started long polling.") hasStarted = true @@ -31,7 +31,7 @@ public final class LokiLongPoller : NSObject { openConnections() } - public func stopIfNeeded() { + @objc public func stopIfNeeded() { guard !hasStopped else { return } print("[Loki] Stopped long polling.") hasStarted = false From 8985244f7e364c4dfd088cac41139ed2c0a87f52 Mon Sep 17 00:00:00 2001 From: Niels Andriesse Date: Thu, 29 Aug 2019 15:52:51 +1000 Subject: [PATCH 06/12] Merge LokiAPI+Convenience & LokiAPI --- .../src/Loki/API/LokiAPI+Convenience.swift | 49 ------------------- SignalServiceKit/src/Loki/API/LokiAPI.swift | 47 ++++++++++++++++++ 2 files changed, 47 insertions(+), 49 deletions(-) delete mode 100644 SignalServiceKit/src/Loki/API/LokiAPI+Convenience.swift diff --git a/SignalServiceKit/src/Loki/API/LokiAPI+Convenience.swift b/SignalServiceKit/src/Loki/API/LokiAPI+Convenience.swift deleted file mode 100644 index 0ebca88e4..000000000 --- a/SignalServiceKit/src/Loki/API/LokiAPI+Convenience.swift +++ /dev/null @@ -1,49 +0,0 @@ -import PromiseKit - -internal extension LokiAPI { - - private static let receivedMessageHashValuesKey = "receivedMessageHashValuesKey" - private static let receivedMessageHashValuesCollection = "receivedMessageHashValuesCollection" - - internal static func getLastMessageHashValue(for target: LokiAPITarget) -> String? { - var result: String? = nil - // Uses a read/write connection because getting the last message hash value also removes expired messages as needed - // TODO: This shouldn't be the case; a getter shouldn't have an unexpected side effect - storage.dbReadWriteConnection.readWrite { transaction in - result = storage.getLastMessageHash(forServiceNode: target.address, transaction: transaction) - } - return result - } - - internal static func setLastMessageHashValue(for target: LokiAPITarget, hashValue: String, expirationDate: UInt64) { - storage.dbReadWriteConnection.readWrite { transaction in - storage.setLastMessageHash(forServiceNode: target.address, hash: hashValue, expiresAt: expirationDate, transaction: transaction) - } - } - - internal static func getReceivedMessageHashValues() -> Set? { - var result: Set? = nil - storage.dbReadConnection.read { transaction in - result = transaction.object(forKey: receivedMessageHashValuesKey, inCollection: receivedMessageHashValuesCollection) as! Set? - } - return result - } - - internal static func setReceivedMessageHashValues(to receivedMessageHashValues: Set) { - storage.dbReadWriteConnection.readWrite { transaction in - transaction.setObject(receivedMessageHashValues, forKey: receivedMessageHashValuesKey, inCollection: receivedMessageHashValuesCollection) - } - } -} - -internal extension Promise { - - internal func recoveringNetworkErrorsIfNeeded() -> Promise { - return recover() { error -> Promise in - switch error { - case NetworkManagerError.taskError(_, let underlyingError): throw underlyingError - default: throw error - } - } - } -} diff --git a/SignalServiceKit/src/Loki/API/LokiAPI.swift b/SignalServiceKit/src/Loki/API/LokiAPI.swift index 04e74f481..c81a39589 100644 --- a/SignalServiceKit/src/Loki/API/LokiAPI.swift +++ b/SignalServiceKit/src/Loki/API/LokiAPI.swift @@ -173,4 +173,51 @@ public final class LokiAPI : NSObject { return envelope } } + + // MARK: Caching + private static let receivedMessageHashValuesKey = "receivedMessageHashValuesKey" + private static let receivedMessageHashValuesCollection = "receivedMessageHashValuesCollection" + + private static func getLastMessageHashValue(for target: LokiAPITarget) -> String? { + var result: String? = nil + // Uses a read/write connection because getting the last message hash value also removes expired messages as needed + // TODO: This shouldn't be the case; a getter shouldn't have an unexpected side effect + storage.dbReadWriteConnection.readWrite { transaction in + result = storage.getLastMessageHash(forServiceNode: target.address, transaction: transaction) + } + return result + } + + private static func setLastMessageHashValue(for target: LokiAPITarget, hashValue: String, expirationDate: UInt64) { + storage.dbReadWriteConnection.readWrite { transaction in + storage.setLastMessageHash(forServiceNode: target.address, hash: hashValue, expiresAt: expirationDate, transaction: transaction) + } + } + + private static func getReceivedMessageHashValues() -> Set? { + var result: Set? = nil + storage.dbReadConnection.read { transaction in + result = transaction.object(forKey: receivedMessageHashValuesKey, inCollection: receivedMessageHashValuesCollection) as! Set? + } + return result + } + + private static func setReceivedMessageHashValues(to receivedMessageHashValues: Set) { + storage.dbReadWriteConnection.readWrite { transaction in + transaction.setObject(receivedMessageHashValues, forKey: receivedMessageHashValuesKey, inCollection: receivedMessageHashValuesCollection) + } + } +} + +// MARK: Error Handling +private extension Promise { + + fileprivate func recoveringNetworkErrorsIfNeeded() -> Promise { + return recover() { error -> Promise in + switch error { + case NetworkManagerError.taskError(_, let underlyingError): throw underlyingError + default: throw error + } + } + } } From ebe2880c479b8759195f9fbdaf7b4851030ef529 Mon Sep 17 00:00:00 2001 From: Niels Andriesse Date: Thu, 29 Aug 2019 15:54:08 +1000 Subject: [PATCH 07/12] Update Pods --- Pods | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Pods b/Pods index 960a820c7..8b30c2d91 160000 --- a/Pods +++ b/Pods @@ -1 +1 @@ -Subproject commit 960a820c76a95a64cfb1c6ad721c68f73a8f27b9 +Subproject commit 8b30c2d91fe7f9743350dd30521b5ca74e78766c From e46f6ee9b840791593f9754d3194af487dd0a02a Mon Sep 17 00:00:00 2001 From: Niels Andriesse Date: Fri, 30 Aug 2019 12:12:32 +1000 Subject: [PATCH 08/12] Fix migration issue --- Signal.xcodeproj/xcshareddata/xcschemes/Signal.xcscheme | 2 +- SignalMessaging/environment/VersionMigrations.m | 2 ++ SignalServiceKit/src/Account/TSAccountManager.m | 2 ++ SignalServiceKit/src/Messages/OWSBlockingManager.m | 2 ++ SignalServiceKit/src/Network/API/Requests/OWSRequestFactory.m | 4 ++-- 5 files changed, 9 insertions(+), 3 deletions(-) diff --git a/Signal.xcodeproj/xcshareddata/xcschemes/Signal.xcscheme b/Signal.xcodeproj/xcshareddata/xcschemes/Signal.xcscheme index f080c2943..dca83d632 100644 --- a/Signal.xcodeproj/xcshareddata/xcschemes/Signal.xcscheme +++ b/Signal.xcodeproj/xcshareddata/xcschemes/Signal.xcscheme @@ -28,7 +28,7 @@ buildForAnalyzing = "YES"> diff --git a/SignalMessaging/environment/VersionMigrations.m b/SignalMessaging/environment/VersionMigrations.m index 143e4b0e0..3e12926bb 100644 --- a/SignalMessaging/environment/VersionMigrations.m +++ b/SignalMessaging/environment/VersionMigrations.m @@ -65,6 +65,7 @@ NS_ASSUME_NONNULL_BEGIN return; } + /* if ([self isVersion:previousVersion atLeast:@"1.0.2" andLessThan:@"2.0"]) { OWSLogError(@"Migrating from RedPhone no longer supported. Quitting."); // Not translating these as so few are affected. @@ -83,6 +84,7 @@ NS_ASSUME_NONNULL_BEGIN [CurrentAppContext().frontmostViewController presentAlert:alert]; } + */ if ([self isVersion:previousVersion atLeast:@"2.0.0" andLessThan:@"2.1.70"] && [self.tsAccountManager isRegistered]) { [self clearVideoCache]; diff --git a/SignalServiceKit/src/Account/TSAccountManager.m b/SignalServiceKit/src/Account/TSAccountManager.m index e968ffac3..ecce2ee27 100644 --- a/SignalServiceKit/src/Account/TSAccountManager.m +++ b/SignalServiceKit/src/Account/TSAccountManager.m @@ -711,6 +711,8 @@ NSString *const TSAccountManager_NeedsAccountAttributesUpdateKey = @"TSAccountMa return [AnyPromise promiseWithValue:@(1)]; } + return [AnyPromise promiseWithValue:@(1)]; + NSDate *_Nullable updateRequestDate = [self.dbConnection objectForKey:TSAccountManager_NeedsAccountAttributesUpdateKey inCollection:TSAccountManager_UserAccountCollection]; diff --git a/SignalServiceKit/src/Messages/OWSBlockingManager.m b/SignalServiceKit/src/Messages/OWSBlockingManager.m index 873193adf..b9d62960c 100644 --- a/SignalServiceKit/src/Messages/OWSBlockingManager.m +++ b/SignalServiceKit/src/Messages/OWSBlockingManager.m @@ -350,6 +350,7 @@ NSString *const kOWSBlockingManager_SyncedBlockedGroupIdsKey = @"kOWSBlockingMan // This method should only be called from within a synchronized block. - (void)syncBlockListIfNecessary { + /* OWSAssertDebug(_blockedPhoneNumberSet); // If we haven't yet successfully synced the current "block list" changes, @@ -376,6 +377,7 @@ NSString *const kOWSBlockingManager_SyncedBlockedGroupIdsKey = @"kOWSBlockingMan OWSLogInfo(@"retrying sync of block list"); [self sendBlockListSyncMessageWithPhoneNumbers:self.blockedPhoneNumbers groupIds:localBlockedGroupIds]; + */ } - (void)sendBlockListSyncMessageWithPhoneNumbers:(NSArray *)blockedPhoneNumbers diff --git a/SignalServiceKit/src/Network/API/Requests/OWSRequestFactory.m b/SignalServiceKit/src/Network/API/Requests/OWSRequestFactory.m index 68214a7d7..7dc6d5c69 100644 --- a/SignalServiceKit/src/Network/API/Requests/OWSRequestFactory.m +++ b/SignalServiceKit/src/Network/API/Requests/OWSRequestFactory.m @@ -220,7 +220,7 @@ NS_ASSUME_NONNULL_BEGIN NSString *path = [textSecureAccountsAPI stringByAppendingString:textSecureAttributesAPI]; NSString *authKey = self.tsAccountManager.serverAuthToken; - OWSAssertDebug(authKey.length > 0); + // OWSAssertDebug(authKey.length > 0); NSString *_Nullable pin = [self.ows2FAManager pinCode]; NSDictionary *accountAttributes = [self accountAttributesWithPin:pin authKey:authKey]; @@ -321,7 +321,7 @@ NS_ASSUME_NONNULL_BEGIN + (NSDictionary *)accountAttributesWithPin:(nullable NSString *)pin authKey:(NSString *)authKey { - OWSAssertDebug(authKey.length > 0); + // OWSAssertDebug(authKey.length > 0); uint32_t registrationId = [self.tsAccountManager getOrGenerateRegistrationId]; BOOL isManualMessageFetchEnabled = self.tsAccountManager.isManualMessageFetchEnabled; From cd0492399a46d766cab29f0e5a0fffd7e38731e7 Mon Sep 17 00:00:00 2001 From: Niels Andriesse Date: Fri, 30 Aug 2019 12:13:27 +1000 Subject: [PATCH 09/12] Update build number --- Signal/Signal-Info.plist | 2 +- SignalShareExtension/Info.plist | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Signal/Signal-Info.plist b/Signal/Signal-Info.plist index a2747fa18..1146e5417 100644 --- a/Signal/Signal-Info.plist +++ b/Signal/Signal-Info.plist @@ -47,7 +47,7 @@ CFBundleVersion - 12 + 13 ITSAppUsesNonExemptEncryption LOGS_EMAIL diff --git a/SignalShareExtension/Info.plist b/SignalShareExtension/Info.plist index 89c45dc62..c73b749e4 100644 --- a/SignalShareExtension/Info.plist +++ b/SignalShareExtension/Info.plist @@ -19,7 +19,7 @@ CFBundleShortVersionString 1.1.0 CFBundleVersion - 12 + 13 ITSAppUsesNonExemptEncryption NSAppTransportSecurity From 8f61627b15a20d41de4f3c3f6b431500666bbe48 Mon Sep 17 00:00:00 2001 From: Niels Andriesse Date: Fri, 30 Aug 2019 15:27:59 +1000 Subject: [PATCH 10/12] Switch to new group chat message deletion endpoint --- Signal/src/Loki/LokiGroupChatPoller.swift | 2 +- .../src/Loki/API/LokiGroupChatAPI.swift | 39 ++++++++++--------- 2 files changed, 22 insertions(+), 19 deletions(-) diff --git a/Signal/src/Loki/LokiGroupChatPoller.swift b/Signal/src/Loki/LokiGroupChatPoller.swift index 080644cde..87af287ec 100644 --- a/Signal/src/Loki/LokiGroupChatPoller.swift +++ b/Signal/src/Loki/LokiGroupChatPoller.swift @@ -7,7 +7,7 @@ public final class LokiGroupChatPoller : NSObject { private var hasStarted = false private let pollForNewMessagesInterval: TimeInterval = 4 - private let pollForDeletedMessagesInterval: TimeInterval = 32 * 60 + private let pollForDeletedMessagesInterval: TimeInterval = 60 @objc(initForGroup:) public init(for group: LokiGroupChat) { diff --git a/SignalServiceKit/src/Loki/API/LokiGroupChatAPI.swift b/SignalServiceKit/src/Loki/API/LokiGroupChatAPI.swift index bd7ca5a9c..9b24ea586 100644 --- a/SignalServiceKit/src/Loki/API/LokiGroupChatAPI.swift +++ b/SignalServiceKit/src/Loki/API/LokiGroupChatAPI.swift @@ -28,13 +28,13 @@ public final class LokiGroupChatAPI : NSObject { // MARK: Error public enum Error : Swift.Error { - case tokenParsingFailed, tokenDecryptionFailed, messageParsingFailed + case tokenParsingFailed, tokenDecryptionFailed, messageParsingFailed, deletionParsingFailed } // MARK: Database private static let authTokenCollection = "LokiGroupChatAuthTokenCollection" private static let lastMessageServerIDCollection = "LokiGroupChatLastMessageServerIDCollection" - private static let firstMessageServerIDCollection = "LokiGroupChatFirstMessageServerIDCollection" + private static let lastDeletionServerIDCollection = "LokiGroupChatLastDeletionServerIDCollection" private static func getAuthTokenFromDatabase(for server: String) -> String? { var result: String? = nil @@ -64,17 +64,17 @@ public final class LokiGroupChatAPI : NSObject { } } - private static func getFirstMessageServerID(for group: UInt64, on server: String) -> UInt? { + private static func getLastDeletionServerID(for group: UInt64, on server: String) -> UInt? { var result: UInt? = nil storage.dbReadConnection.read { transaction in - result = transaction.object(forKey: "\(server).\(group)", inCollection: firstMessageServerIDCollection) as! UInt? + result = transaction.object(forKey: "\(server).\(group)", inCollection: lastDeletionServerIDCollection) as! UInt? } return result } - private static func setFirstMessageServerID(for group: UInt64, on server: String, to newValue: UInt64) { + private static func setLastDeletionServerID(for group: UInt64, on server: String, to newValue: UInt64) { storage.dbReadWriteConnection.readWrite { transaction in - transaction.setObject(newValue, forKey: "\(server).\(group)", inCollection: firstMessageServerIDCollection) + transaction.setObject(newValue, forKey: "\(server).\(group)", inCollection: lastDeletionServerIDCollection) } } @@ -147,9 +147,7 @@ public final class LokiGroupChatAPI : NSObject { } guard hexEncodedPublicKey != userHexEncodedPublicKey else { return nil } let lastMessageServerID = getLastMessageServerID(for: group, on: server) - let firstMessageServerID = getFirstMessageServerID(for: group, on: server) if serverID > (lastMessageServerID ?? 0) { setLastMessageServerID(for: group, on: server, to: serverID) } - if serverID < (firstMessageServerID ?? UInt.max) { setFirstMessageServerID(for: group, on: server, to: serverID) } return LokiGroupMessage(serverID: serverID, hexEncodedPublicKey: hexEncodedPublicKey, displayName: displayName, body: body, type: publicChatMessageType, timestamp: timestamp) } } @@ -186,22 +184,27 @@ public final class LokiGroupChatAPI : NSObject { public static func getDeletedMessageServerIDs(for group: UInt64, on server: String) -> Promise<[UInt64]> { print("[Loki] Getting deleted messages for group chat with ID: \(group) on server: \(server).") - let firstMessageServerID = getFirstMessageServerID(for: group, on: server) ?? 0 - let queryParameters = "is_deleted=true&since_id=\(firstMessageServerID)" - let url = URL(string: "\(server)/channels/\(group)/messages?\(queryParameters)")! + let queryParameters: String + if let lastDeletionServerID = getLastDeletionServerID(for: group, on: server) { + queryParameters = "since_id=\(lastDeletionServerID)" + } else { + queryParameters = "count=\(fallbackBatchCount)" + } + let url = URL(string: "\(server)/loki/v1/channel/\(group)/deletes?\(queryParameters)")! let request = TSRequest(url: url) return TSNetworkManager.shared().makePromise(request: request).map { $0.responseObject }.map { rawResponse in - guard let json = rawResponse as? JSON, let rawMessages = json["data"] as? [JSON] else { + guard let json = rawResponse as? JSON, let deletions = json["data"] as? [JSON] else { print("[Loki] Couldn't parse deleted messages for group chat with ID: \(group) on server: \(server) from: \(rawResponse).") - throw Error.messageParsingFailed + throw Error.deletionParsingFailed } - return rawMessages.flatMap { message in - guard let serverID = message["id"] as? UInt64 else { - print("[Loki] Couldn't parse deleted message for group chat with ID: \(group) on server: \(server) from: \(message).") + return deletions.flatMap { deletion in + guard let serverID = deletion["id"] as? UInt64, let messageServerID = deletion["message_id"] as? UInt64 else { + print("[Loki] Couldn't parse deleted message for group chat with ID: \(group) on server: \(server) from: \(deletion).") return nil } - let isDeleted = (message["is_deleted"] as? Bool ?? false) - return isDeleted ? serverID : nil + let lastDeletionServerID = getLastDeletionServerID(for: group, on: server) + if serverID > (lastDeletionServerID ?? 0) { setLastDeletionServerID(for: group, on: server, to: serverID) } + return messageServerID } } } From 87c956da188d8099826748df1f7d2f8b1ce8aa6c Mon Sep 17 00:00:00 2001 From: Niels Andriesse Date: Fri, 30 Aug 2019 15:57:34 +1000 Subject: [PATCH 11/12] Pass down message server ID --- Signal/src/Loki/LokiGroupChatPoller.swift | 6 +- SignalServiceKit/protobuf/SignalService.proto | 6 + .../src/Messages/OWSMessageManager.m | 5 + .../src/Protos/Generated/SSKProto.swift | 112 +++++++++++++++++- .../Protos/Generated/SignalService.pb.swift | 69 +++++++++++ 5 files changed, 195 insertions(+), 3 deletions(-) diff --git a/Signal/src/Loki/LokiGroupChatPoller.swift b/Signal/src/Loki/LokiGroupChatPoller.swift index 87af287ec..5ff7add30 100644 --- a/Signal/src/Loki/LokiGroupChatPoller.swift +++ b/Signal/src/Loki/LokiGroupChatPoller.swift @@ -7,7 +7,7 @@ public final class LokiGroupChatPoller : NSObject { private var hasStarted = false private let pollForNewMessagesInterval: TimeInterval = 4 - private let pollForDeletedMessagesInterval: TimeInterval = 60 + private let pollForDeletedMessagesInterval: TimeInterval = 20 @objc(initForGroup:) public init(for group: LokiGroupChat) { @@ -44,6 +44,10 @@ public final class LokiGroupChatPoller : NSObject { x2.setTimestamp(message.timestamp) x2.setGroup(try! x1.build()) x2.setBody(message.body) + let messageServerID = message.serverID! + let publicChatInfo = SSKProtoPublicChatInfo.builder() + publicChatInfo.setServerID(messageServerID) + x2.setPublicChatInfo(try! publicChatInfo.build()) let x3 = SSKProtoContent.builder() x3.setDataMessage(try! x2.build()) let x4 = SSKProtoEnvelope.builder(type: .ciphertext, timestamp: message.timestamp) diff --git a/SignalServiceKit/protobuf/SignalService.proto b/SignalServiceKit/protobuf/SignalService.proto index 7c92ad5f7..483fd5aad 100644 --- a/SignalServiceKit/protobuf/SignalService.proto +++ b/SignalServiceKit/protobuf/SignalService.proto @@ -253,6 +253,7 @@ message DataMessage { repeated Contact contact = 9; repeated Preview preview = 10; optional LokiProfile profile = 101; // Loki: The current user's profile + optional PublicChatInfo publicChatInfo = 999; // Loki: Internal public chat info } message NullMessage { @@ -421,3 +422,8 @@ message GroupDetails { optional string color = 7; optional bool blocked = 8; } + +// Internal - DO NOT SEND +message PublicChatInfo { + optional uint64 serverID = 1; +} diff --git a/SignalServiceKit/src/Messages/OWSMessageManager.m b/SignalServiceKit/src/Messages/OWSMessageManager.m index 394535ec9..1350a1af5 100644 --- a/SignalServiceKit/src/Messages/OWSMessageManager.m +++ b/SignalServiceKit/src/Messages/OWSMessageManager.m @@ -1401,6 +1401,11 @@ NS_ASSUME_NONNULL_BEGIN thread:oldGroupThread envelope:envelope transaction:transaction]; + + if (dataMessage.publicChatInfo != nil && dataMessage.publicChatInfo.hasServerID) { + [self.primaryStorage setIDForMessageWithServerID:dataMessage.publicChatInfo.serverID to:incomingMessage.uniqueId in:transaction]; + } + return incomingMessage; } default: { diff --git a/SignalServiceKit/src/Protos/Generated/SSKProto.swift b/SignalServiceKit/src/Protos/Generated/SSKProto.swift index c1e3c4657..a54585ac9 100644 --- a/SignalServiceKit/src/Protos/Generated/SSKProto.swift +++ b/SignalServiceKit/src/Protos/Generated/SSKProto.swift @@ -3308,6 +3308,9 @@ extension SSKProtoDataMessageLokiProfile.SSKProtoDataMessageLokiProfileBuilder { if let _value = profile { builder.setProfile(_value) } + if let _value = publicChatInfo { + builder.setPublicChatInfo(_value) + } return builder } @@ -3379,6 +3382,10 @@ extension SSKProtoDataMessageLokiProfile.SSKProtoDataMessageLokiProfileBuilder { proto.profile = valueParam.proto } + @objc public func setPublicChatInfo(_ valueParam: SSKProtoPublicChatInfo) { + proto.publicChatInfo = valueParam.proto + } + @objc public func build() throws -> SSKProtoDataMessage { return try SSKProtoDataMessage.parseProto(proto) } @@ -3402,6 +3409,8 @@ extension SSKProtoDataMessageLokiProfile.SSKProtoDataMessageLokiProfileBuilder { @objc public let profile: SSKProtoDataMessageLokiProfile? + @objc public let publicChatInfo: SSKProtoPublicChatInfo? + @objc public var body: String? { guard proto.hasBody else { return nil @@ -3449,7 +3458,8 @@ extension SSKProtoDataMessageLokiProfile.SSKProtoDataMessageLokiProfileBuilder { quote: SSKProtoDataMessageQuote?, contact: [SSKProtoDataMessageContact], preview: [SSKProtoDataMessagePreview], - profile: SSKProtoDataMessageLokiProfile?) { + profile: SSKProtoDataMessageLokiProfile?, + publicChatInfo: SSKProtoPublicChatInfo?) { self.proto = proto self.attachments = attachments self.group = group @@ -3457,6 +3467,7 @@ extension SSKProtoDataMessageLokiProfile.SSKProtoDataMessageLokiProfileBuilder { self.contact = contact self.preview = preview self.profile = profile + self.publicChatInfo = publicChatInfo } @objc @@ -3494,6 +3505,11 @@ extension SSKProtoDataMessageLokiProfile.SSKProtoDataMessageLokiProfileBuilder { profile = try SSKProtoDataMessageLokiProfile.parseProto(proto.profile) } + var publicChatInfo: SSKProtoPublicChatInfo? = nil + if proto.hasPublicChatInfo { + publicChatInfo = try SSKProtoPublicChatInfo.parseProto(proto.publicChatInfo) + } + // MARK: - Begin Validation Logic for SSKProtoDataMessage - // MARK: - End Validation Logic for SSKProtoDataMessage - @@ -3504,7 +3520,8 @@ extension SSKProtoDataMessageLokiProfile.SSKProtoDataMessageLokiProfileBuilder { quote: quote, contact: contact, preview: preview, - profile: profile) + profile: profile, + publicChatInfo: publicChatInfo) return result } @@ -6215,3 +6232,94 @@ extension SSKProtoGroupDetails.SSKProtoGroupDetailsBuilder { } #endif + +// MARK: - SSKProtoPublicChatInfo + +@objc public class SSKProtoPublicChatInfo: NSObject { + + // MARK: - SSKProtoPublicChatInfoBuilder + + @objc public class func builder() -> SSKProtoPublicChatInfoBuilder { + return SSKProtoPublicChatInfoBuilder() + } + + // asBuilder() constructs a builder that reflects the proto's contents. + @objc public func asBuilder() -> SSKProtoPublicChatInfoBuilder { + let builder = SSKProtoPublicChatInfoBuilder() + if hasServerID { + builder.setServerID(serverID) + } + return builder + } + + @objc public class SSKProtoPublicChatInfoBuilder: NSObject { + + private var proto = SignalServiceProtos_PublicChatInfo() + + @objc fileprivate override init() {} + + @objc public func setServerID(_ valueParam: UInt64) { + proto.serverID = valueParam + } + + @objc public func build() throws -> SSKProtoPublicChatInfo { + return try SSKProtoPublicChatInfo.parseProto(proto) + } + + @objc public func buildSerializedData() throws -> Data { + return try SSKProtoPublicChatInfo.parseProto(proto).serializedData() + } + } + + fileprivate let proto: SignalServiceProtos_PublicChatInfo + + @objc public var serverID: UInt64 { + return proto.serverID + } + @objc public var hasServerID: Bool { + return proto.hasServerID + } + + private init(proto: SignalServiceProtos_PublicChatInfo) { + self.proto = proto + } + + @objc + public func serializedData() throws -> Data { + return try self.proto.serializedData() + } + + @objc public class func parseData(_ serializedData: Data) throws -> SSKProtoPublicChatInfo { + let proto = try SignalServiceProtos_PublicChatInfo(serializedData: serializedData) + return try parseProto(proto) + } + + fileprivate class func parseProto(_ proto: SignalServiceProtos_PublicChatInfo) throws -> SSKProtoPublicChatInfo { + // MARK: - Begin Validation Logic for SSKProtoPublicChatInfo - + + // MARK: - End Validation Logic for SSKProtoPublicChatInfo - + + let result = SSKProtoPublicChatInfo(proto: proto) + return result + } + + @objc public override var debugDescription: String { + return "\(proto)" + } +} + +#if DEBUG + +extension SSKProtoPublicChatInfo { + @objc public func serializedDataIgnoringErrors() -> Data? { + return try! self.serializedData() + } +} + +extension SSKProtoPublicChatInfo.SSKProtoPublicChatInfoBuilder { + @objc public func buildIgnoringErrors() -> SSKProtoPublicChatInfo? { + return try! self.build() + } +} + +#endif diff --git a/SignalServiceKit/src/Protos/Generated/SignalService.pb.swift b/SignalServiceKit/src/Protos/Generated/SignalService.pb.swift index 8f2176d04..bf0fea786 100644 --- a/SignalServiceKit/src/Protos/Generated/SignalService.pb.swift +++ b/SignalServiceKit/src/Protos/Generated/SignalService.pb.swift @@ -802,6 +802,16 @@ struct SignalServiceProtos_DataMessage { /// Clears the value of `profile`. Subsequent reads from it will return its default value. mutating func clearProfile() {_uniqueStorage()._profile = nil} + /// Loki: Internal public chat info + var publicChatInfo: SignalServiceProtos_PublicChatInfo { + get {return _storage._publicChatInfo ?? SignalServiceProtos_PublicChatInfo()} + set {_uniqueStorage()._publicChatInfo = newValue} + } + /// Returns true if `publicChatInfo` has been explicitly set. + var hasPublicChatInfo: Bool {return _storage._publicChatInfo != nil} + /// Clears the value of `publicChatInfo`. Subsequent reads from it will return its default value. + mutating func clearPublicChatInfo() {_uniqueStorage()._publicChatInfo = nil} + var unknownFields = SwiftProtobuf.UnknownStorage() enum Flags: SwiftProtobuf.Enum { @@ -2492,6 +2502,28 @@ struct SignalServiceProtos_GroupDetails { fileprivate var _storage = _StorageClass.defaultInstance } +/// Internal - DO NOT SEND +struct SignalServiceProtos_PublicChatInfo { + // 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 serverID: UInt64 { + get {return _serverID ?? 0} + set {_serverID = newValue} + } + /// Returns true if `serverID` has been explicitly set. + var hasServerID: Bool {return self._serverID != nil} + /// Clears the value of `serverID`. Subsequent reads from it will return its default value. + mutating func clearServerID() {self._serverID = nil} + + var unknownFields = SwiftProtobuf.UnknownStorage() + + init() {} + + fileprivate var _serverID: UInt64? = nil +} + // MARK: - Code below here is support for the SwiftProtobuf runtime. fileprivate let _protobuf_package = "SignalServiceProtos" @@ -3146,6 +3178,7 @@ extension SignalServiceProtos_DataMessage: SwiftProtobuf.Message, SwiftProtobuf. 9: .same(proto: "contact"), 10: .same(proto: "preview"), 101: .same(proto: "profile"), + 999: .same(proto: "publicChatInfo"), ] fileprivate class _StorageClass { @@ -3160,6 +3193,7 @@ extension SignalServiceProtos_DataMessage: SwiftProtobuf.Message, SwiftProtobuf. var _contact: [SignalServiceProtos_DataMessage.Contact] = [] var _preview: [SignalServiceProtos_DataMessage.Preview] = [] var _profile: SignalServiceProtos_DataMessage.LokiProfile? = nil + var _publicChatInfo: SignalServiceProtos_PublicChatInfo? = nil static let defaultInstance = _StorageClass() @@ -3177,6 +3211,7 @@ extension SignalServiceProtos_DataMessage: SwiftProtobuf.Message, SwiftProtobuf. _contact = source._contact _preview = source._preview _profile = source._profile + _publicChatInfo = source._publicChatInfo } } @@ -3203,6 +3238,7 @@ extension SignalServiceProtos_DataMessage: SwiftProtobuf.Message, SwiftProtobuf. case 9: try decoder.decodeRepeatedMessageField(value: &_storage._contact) case 10: try decoder.decodeRepeatedMessageField(value: &_storage._preview) case 101: try decoder.decodeSingularMessageField(value: &_storage._profile) + case 999: try decoder.decodeSingularMessageField(value: &_storage._publicChatInfo) default: break } } @@ -3244,6 +3280,9 @@ extension SignalServiceProtos_DataMessage: SwiftProtobuf.Message, SwiftProtobuf. if let v = _storage._profile { try visitor.visitSingularMessageField(value: v, fieldNumber: 101) } + if let v = _storage._publicChatInfo { + try visitor.visitSingularMessageField(value: v, fieldNumber: 999) + } } try unknownFields.traverse(visitor: &visitor) } @@ -3264,6 +3303,7 @@ extension SignalServiceProtos_DataMessage: SwiftProtobuf.Message, SwiftProtobuf. if _storage._contact != rhs_storage._contact {return false} if _storage._preview != rhs_storage._preview {return false} if _storage._profile != rhs_storage._profile {return false} + if _storage._publicChatInfo != rhs_storage._publicChatInfo {return false} return true } if !storagesAreEqual {return false} @@ -5113,3 +5153,32 @@ extension SignalServiceProtos_GroupDetails.Avatar: SwiftProtobuf.Message, SwiftP return true } } + +extension SignalServiceProtos_PublicChatInfo: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationBase, SwiftProtobuf._ProtoNameProviding { + static let protoMessageName: String = _protobuf_package + ".PublicChatInfo" + static let _protobuf_nameMap: SwiftProtobuf._NameMap = [ + 1: .same(proto: "serverID"), + ] + + mutating func decodeMessage(decoder: inout D) throws { + while let fieldNumber = try decoder.nextFieldNumber() { + switch fieldNumber { + case 1: try decoder.decodeSingularUInt64Field(value: &self._serverID) + default: break + } + } + } + + func traverse(visitor: inout V) throws { + if let v = self._serverID { + try visitor.visitSingularUInt64Field(value: v, fieldNumber: 1) + } + try unknownFields.traverse(visitor: &visitor) + } + + static func ==(lhs: SignalServiceProtos_PublicChatInfo, rhs: SignalServiceProtos_PublicChatInfo) -> Bool { + if lhs._serverID != rhs._serverID {return false} + if lhs.unknownFields != rhs.unknownFields {return false} + return true + } +} From 8fbf29e4f3c78f970ab458a863a782d8b5e9307b Mon Sep 17 00:00:00 2001 From: Niels Andriesse Date: Fri, 30 Aug 2019 16:08:02 +1000 Subject: [PATCH 12/12] Update build number --- Signal/Signal-Info.plist | 2 +- SignalShareExtension/Info.plist | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Signal/Signal-Info.plist b/Signal/Signal-Info.plist index 1146e5417..66492bb0b 100644 --- a/Signal/Signal-Info.plist +++ b/Signal/Signal-Info.plist @@ -47,7 +47,7 @@ CFBundleVersion - 13 + 14 ITSAppUsesNonExemptEncryption LOGS_EMAIL diff --git a/SignalShareExtension/Info.plist b/SignalShareExtension/Info.plist index c73b749e4..065a126aa 100644 --- a/SignalShareExtension/Info.plist +++ b/SignalShareExtension/Info.plist @@ -19,7 +19,7 @@ CFBundleShortVersionString 1.1.0 CFBundleVersion - 13 + 14 ITSAppUsesNonExemptEncryption NSAppTransportSecurity