diff --git a/Session/Components/ConversationTitleView.swift b/Session/Components/ConversationTitleView.swift index f6c74d426..2f93f729e 100644 --- a/Session/Components/ConversationTitleView.swift +++ b/Session/Components/ConversationTitleView.swift @@ -50,11 +50,11 @@ final class ConversationTitleView : UIView { updateSubtitleForCurrentStatus() let notificationCenter = NotificationCenter.default notificationCenter.addObserver(self, selector: #selector(handleProfileChangedNotification(_:)), name: NSNotification.Name(rawValue: kNSNotificationName_OtherUsersProfileDidChange), object: nil) - notificationCenter.addObserver(self, selector: #selector(handleCalculatingPoWNotification(_:)), name: .calculatingPoW, object: nil) - notificationCenter.addObserver(self, selector: #selector(handleRoutingNotification(_:)), name: .routing, object: nil) + notificationCenter.addObserver(self, selector: #selector(handleCalculatingMessagePoWNotification(_:)), name: .calculatingMessagePoW, object: nil) + notificationCenter.addObserver(self, selector: #selector(handleEncryptingMessageNotification(_:)), name: .encryptingMessage, object: nil) notificationCenter.addObserver(self, selector: #selector(handleMessageSendingNotification(_:)), name: .messageSending, object: nil) notificationCenter.addObserver(self, selector: #selector(handleMessageSentNotification(_:)), name: .messageSent, object: nil) - notificationCenter.addObserver(self, selector: #selector(handleMessageFailedNotification(_:)), name: .messageFailed, object: nil) + notificationCenter.addObserver(self, selector: #selector(handleMessageSendingFailedNotification(_:)), name: .messageSendingFailed, object: nil) } override init(frame: CGRect) { @@ -112,12 +112,12 @@ final class ConversationTitleView : UIView { updateProfilePicture() } - @objc private func handleCalculatingPoWNotification(_ notification: Notification) { + @objc private func handleCalculatingMessagePoWNotification(_ notification: Notification) { guard let timestamp = notification.object as? NSNumber else { return } setStatusIfNeeded(to: .calculatingPoW, forMessageWithTimestamp: timestamp) } - @objc private func handleRoutingNotification(_ notification: Notification) { + @objc private func handleEncryptingMessageNotification(_ notification: Notification) { guard let timestamp = notification.object as? NSNumber else { return } setStatusIfNeeded(to: .routing, forMessageWithTimestamp: timestamp) } @@ -136,7 +136,7 @@ final class ConversationTitleView : UIView { } } - @objc private func handleMessageFailedNotification(_ notification: Notification) { + @objc private func handleMessageSendingFailedNotification(_ notification: Notification) { guard let timestamp = notification.object as? NSNumber else { return } clearStatusIfNeededForMessageWithTimestamp(timestamp) } @@ -149,14 +149,7 @@ final class ConversationTitleView : UIView { uncheckedTargetInteraction = interaction } guard let targetInteraction = uncheckedTargetInteraction, targetInteraction.interactionType() == .outgoingMessage, - status.rawValue > (currentStatus?.rawValue ?? 0), let hexEncodedPublicKey = targetInteraction.thread.contactIdentifier() else { return } - var masterHexEncodedPublicKey: String! - let storage = OWSPrimaryStorage.shared() - storage.dbReadConnection.read { transaction in - masterHexEncodedPublicKey = storage.getMasterHexEncodedPublicKey(for: hexEncodedPublicKey, in: transaction) ?? hexEncodedPublicKey - } - let isSlaveDevice = masterHexEncodedPublicKey != hexEncodedPublicKey - guard !isSlaveDevice else { return } + status.rawValue > (currentStatus?.rawValue ?? 0) else { return } currentStatus = status } @@ -184,7 +177,7 @@ final class ConversationTitleView : UIView { dateFormatter.timeStyle = .medium dateFormatter.dateStyle = .medium subtitle.append(NSAttributedString(string: "Muted until " + dateFormatter.string(from: muteEndDate))) - } else if let thread = self.thread as? TSGroupThread, !thread.isRSSFeed { + } else if let thread = self.thread as? TSGroupThread { let storage = OWSPrimaryStorage.shared() var userCount: Int? if thread.groupModel.groupType == .closedGroup { diff --git a/Session/Meta/Signal-Bridging-Header.h b/Session/Meta/Signal-Bridging-Header.h index 30d99c14c..860f774b2 100644 --- a/Session/Meta/Signal-Bridging-Header.h +++ b/Session/Meta/Signal-Bridging-Header.h @@ -16,10 +16,10 @@ #import "ConversationViewCell.h" #import "ConversationViewItem.h" #import "DateUtil.h" -#import "FingerprintViewController.h" + #import "MediaDetailViewController.h" #import "NotificationSettingsViewController.h" -#import "OWSAddToContactViewController.h" + #import "OWSAnyTouchGestureRecognizer.h" #import "OWSAudioPlayer.h" #import "OWSBackup.h" @@ -35,13 +35,12 @@ #import "OWSQuotedMessageView.h" #import "OWSSessionResetJobRecord.h" #import "OWSWindowManager.h" -#import "PinEntryView.h" #import "PrivacySettingsTableViewController.h" #import "RemoteVideoView.h" #import "OWSQRCodeScanningViewController.h" #import "SignalApp.h" #import "UIViewController+Permissions.h" -#import "ViewControllerUtils.h" + #import #import #import @@ -55,52 +54,36 @@ #import #import #import -#import -#import + + #import #import #import #import #import #import -#import #import #import #import #import #import #import -#import -#import #import #import #import #import #import -#import -#import -#import #import #import #import #import -#import -#import #import #import #import #import -#import -#import -#import -#import #import #import -#import #import -#import -#import -#import #import #import #import @@ -113,10 +96,10 @@ #import #import #import -#import + #import #import -#import + #import #import #import diff --git a/Session/Meta/Signal-Prefix.pch b/Session/Meta/Signal-Prefix.pch index 9faa8fd63..bede98c9f 100644 --- a/Session/Meta/Signal-Prefix.pch +++ b/Session/Meta/Signal-Prefix.pch @@ -18,7 +18,6 @@ #import #import #import - #import #import #import #import diff --git a/Session/Signal/AccountManager.swift b/Session/Signal/AccountManager.swift index a3b04c225..60a6ed448 100644 --- a/Session/Signal/AccountManager.swift +++ b/Session/Signal/AccountManager.swift @@ -19,10 +19,6 @@ public class AccountManager: NSObject { return OWSProfileManager.shared() } - private var networkManager: TSNetworkManager { - return SSKEnvironment.shared.networkManager - } - private var preferences: OWSPreferences { return Environment.shared.preferences } @@ -119,28 +115,4 @@ public class AccountManager: NSObject { let anyPromise = tsAccountManager.setIsManualMessageFetchEnabled(true) return Promise(anyPromise).asVoid() } - - // MARK: Turn Server - - func getTurnServerInfo() -> Promise { - return Promise { resolver in - self.networkManager.makeRequest(OWSRequestFactory.turnServerInfoRequest(), - success: { (_: URLSessionDataTask, responseObject: Any?) in - guard responseObject != nil else { - return resolver.reject(OWSErrorMakeUnableToProcessServerResponseError()) - } - - if let responseDictionary = responseObject as? [String: AnyObject] { - if let turnServerInfo = TurnServerInfo(attributes: responseDictionary) { - return resolver.fulfill(turnServerInfo) - } - Logger.error("unexpected server response:\(responseDictionary)") - } - return resolver.reject(OWSErrorMakeUnableToProcessServerResponseError()) - }, - failure: { (_: URLSessionDataTask, error: Error) in - return resolver.reject(error) - }) - } - } } diff --git a/Session/Signal/AddContactShareToExistingContactViewController.swift b/Session/Signal/AddContactShareToExistingContactViewController.swift deleted file mode 100644 index 0cc652d48..000000000 --- a/Session/Signal/AddContactShareToExistingContactViewController.swift +++ /dev/null @@ -1,145 +0,0 @@ -// -// Copyright (c) 2019 Open Whisper Systems. All rights reserved. -// - -import Foundation -import UIKit -import ContactsUI - -class AddContactShareToExistingContactViewController: ContactsPicker, ContactsPickerDelegate, CNContactViewControllerDelegate { - - // TODO - there are some hard coded assumptions in this VC that assume we are *pushed* onto a - // navigation controller. That seems fine for now, but if we need to be presented as a modal, - // or need to notify our presenter about our dismisall or other contact actions, a delegate - // would be helpful. It seems like this would require some broad changes to the ContactShareViewHelper, - // so I've left it as is for now, since it happens to work. - // weak var addToExistingContactDelegate: AddContactShareToExistingContactViewControllerDelegate? - - let contactShare: ContactShareViewModel - - required init(contactShare: ContactShareViewModel) { - self.contactShare = contactShare - super.init(allowsMultipleSelection: false, subtitleCellType: .none) - - self.contactsPickerDelegate = self - } - - required public init?(coder aDecoder: NSCoder) { - notImplemented() - } - - @objc required public init(allowsMultipleSelection: Bool, subtitleCellType: SubtitleCellValue) { - notImplemented() - } - - // MARK: - ContactsPickerDelegate - - func contactsPicker(_: ContactsPicker, contactFetchDidFail error: NSError) { - owsFailDebug("with error: \(error)") - - guard let navigationController = self.navigationController else { - owsFailDebug("navigationController was unexpectedly nil") - return - } - - navigationController.popViewController(animated: true) - } - - func contactsPickerDidCancel(_: ContactsPicker) { - Logger.debug("") - guard let navigationController = self.navigationController else { - owsFailDebug("navigationController was unexpectedly nil") - return - } - - navigationController.popViewController(animated: true) - } - - func contactsPicker(_: ContactsPicker, didSelectContact oldContact: Contact) { - Logger.debug("") - - let contactsManager = Environment.shared.contactsManager - guard let oldCNContact = contactsManager?.cnContact(withId: oldContact.cnContactId) else { - owsFailDebug("could not load old CNContact.") - return - } - guard let newCNContact = OWSContacts.systemContact(for: self.contactShare.dbRecord, imageData: self.contactShare.avatarImageData) else { - owsFailDebug("could not load new CNContact.") - return - } - merge(oldCNContact: oldCNContact, newCNContact: newCNContact) - } - - func merge(oldCNContact: CNContact, newCNContact: CNContact) { - Logger.debug("") - - let mergedCNContact: CNContact = Contact.merge(cnContact: oldCNContact, newCNContact: newCNContact) - - // Not actually a "new" contact, but this brings up the edit form rather than the "Read" form - // saving our users a tap in some cases when we already know they want to edit. - let contactViewController: CNContactViewController = CNContactViewController(forNewContact: mergedCNContact) - - // Default title is "New Contact". We could give a more descriptive title, but anything - // seems redundant - the context is sufficiently clear. - contactViewController.title = "" - contactViewController.allowsActions = false - contactViewController.allowsEditing = true - contactViewController.delegate = self - - let modal = OWSNavigationController(rootViewController: contactViewController) - self.present(modal, animated: true) - } - - func contactsPicker(_: ContactsPicker, didSelectMultipleContacts contacts: [Contact]) { - Logger.debug("") - owsFailDebug("only supports single contact select") - - guard let navigationController = self.navigationController else { - owsFailDebug("navigationController was unexpectedly nil") - return - } - - navigationController.popViewController(animated: true) - } - - func contactsPicker(_: ContactsPicker, shouldSelectContact contact: Contact) -> Bool { - return true - } - - // MARK: - CNContactViewControllerDelegate - - public func contactViewController(_ viewController: CNContactViewController, didCompleteWith contact: CNContact?) { - Logger.debug("") - - guard let navigationController = self.navigationController else { - owsFailDebug("navigationController was unexpectedly nil") - return - } - - // TODO this is weird - ideally we'd do something like - // self.delegate?.didFinishAddingContact - // and the delegate, which knows about our presentation context could do the right thing. - // - // As it is, we happen to always be *pushing* this view controller onto a navcontroller, so the - // following works in all current cases. - // - // If we ever wanted to do something different, like present this in a modal, we'd have to rethink. - - // We want to pop *this* view *and* the still presented CNContactViewController in a single animation. - // Note this happens for *cancel* and for *done*. Unfortunately, I don't know of a way to detect the difference - // between the two, since both just call this method. - guard let myIndex = navigationController.viewControllers.firstIndex(of: self) else { - owsFailDebug("myIndex was unexpectedly nil") - navigationController.popViewController(animated: true) - navigationController.popViewController(animated: true) - return - } - - let previousViewControllerIndex = navigationController.viewControllers.index(before: myIndex) - let previousViewController = navigationController.viewControllers[previousViewControllerIndex] - - self.dismiss(animated: false) { - navigationController.popToViewController(previousViewController, animated: true) - } - } -} diff --git a/Session/Signal/AddToBlockListViewController.m b/Session/Signal/AddToBlockListViewController.m index 9d67377fb..58b64c436 100644 --- a/Session/Signal/AddToBlockListViewController.m +++ b/Session/Signal/AddToBlockListViewController.m @@ -4,8 +4,8 @@ #import "AddToBlockListViewController.h" #import "BlockListUIUtils.h" -#import "ContactsViewHelper.h" -#import +#import +#import #import NS_ASSUME_NONNULL_BEGIN @@ -51,8 +51,7 @@ NS_ASSUME_NONNULL_BEGIN __weak AddToBlockListViewController *weakSelf = self; [BlockListUIUtils showBlockPhoneNumberActionSheet:phoneNumber fromViewController:self - blockingManager:self.contactsViewHelper.blockingManager - contactsManager:self.contactsViewHelper.contactsManager + blockingManager:SSKEnvironment.shared.blockingManager completionBlock:^(BOOL isBlocked) { if (isBlocked) { [weakSelf.navigationController popViewControllerAnimated:YES]; @@ -65,7 +64,7 @@ NS_ASSUME_NONNULL_BEGIN OWSAssertDebug(signalAccount); ContactsViewHelper *helper = self.contactsViewHelper; - return ![helper isRecipientIdBlocked:signalAccount.recipientId]; + return ![SSKEnvironment.shared.blockingManager isRecipientIdBlocked:signalAccount.recipientId]; } - (void)signalAccountWasSelected:(SignalAccount *)signalAccount @@ -73,15 +72,13 @@ NS_ASSUME_NONNULL_BEGIN OWSAssertDebug(signalAccount); __weak AddToBlockListViewController *weakSelf = self; - ContactsViewHelper *helper = self.contactsViewHelper; - if ([helper isRecipientIdBlocked:signalAccount.recipientId]) { + if ([SSKEnvironment.shared.blockingManager isRecipientIdBlocked:signalAccount.recipientId]) { OWSFailDebug(@"Cannot add already blocked user to block list."); return; } [BlockListUIUtils showBlockSignalAccountActionSheet:signalAccount fromViewController:self - blockingManager:helper.blockingManager - contactsManager:helper.contactsManager + blockingManager:SSKEnvironment.shared.blockingManager completionBlock:^(BOOL isBlocked) { if (isBlocked) { [weakSelf.navigationController popViewControllerAnimated:YES]; diff --git a/Session/Signal/AddToGroupViewController.m b/Session/Signal/AddToGroupViewController.m index 1ec474681..c021ce8c3 100644 --- a/Session/Signal/AddToGroupViewController.m +++ b/Session/Signal/AddToGroupViewController.m @@ -4,9 +4,9 @@ #import "AddToGroupViewController.h" #import "BlockListUIUtils.h" -#import "ContactsViewHelper.h" + #import "Session-Swift.h" -#import + #import NS_ASSUME_NONNULL_BEGIN @@ -52,12 +52,10 @@ NS_ASSUME_NONNULL_BEGIN __weak AddToGroupViewController *weakSelf = self; - ContactsViewHelper *helper = self.contactsViewHelper; - if ([helper isRecipientIdBlocked:phoneNumber]) { + if ([SSKEnvironment.shared.blockingManager isRecipientIdBlocked:phoneNumber]) { [BlockListUIUtils showUnblockPhoneNumberActionSheet:phoneNumber fromViewController:self - blockingManager:helper.blockingManager - contactsManager:helper.contactsManager + blockingManager:SSKEnvironment.shared.blockingManager completionBlock:^(BOOL isBlocked) { if (!isBlocked) { [weakSelf addToGroup:phoneNumber]; @@ -66,22 +64,6 @@ NS_ASSUME_NONNULL_BEGIN return; } - BOOL didShowSNAlert = [SafetyNumberConfirmationAlert - presentAlertIfNecessaryWithRecipientId:phoneNumber - confirmationText: - NSLocalizedString(@"SAFETY_NUMBER_CHANGED_CONFIRM_ADD_TO_GROUP_ACTION", - @"button title to confirm adding a recipient to a group when their safety " - @"number has recently changed") - contactsManager:helper.contactsManager - completion:^(BOOL didConfirmIdentity) { - if (didConfirmIdentity) { - [weakSelf addToGroup:phoneNumber]; - } - }]; - if (didShowSNAlert) { - return; - } - [self addToGroup:phoneNumber]; } @@ -97,17 +79,15 @@ NS_ASSUME_NONNULL_BEGIN OWSAssertDebug(signalAccount); __weak AddToGroupViewController *weakSelf = self; - ContactsViewHelper *helper = self.contactsViewHelper; if ([self.addToGroupDelegate isRecipientGroupMember:signalAccount.recipientId]) { OWSFailDebug(@"Cannot add user to group member if already a member."); return; } - if ([helper isRecipientIdBlocked:signalAccount.recipientId]) { + if ([SSKEnvironment.shared.blockingManager isRecipientIdBlocked:signalAccount.recipientId]) { [BlockListUIUtils showUnblockSignalAccountActionSheet:signalAccount fromViewController:self - blockingManager:helper.blockingManager - contactsManager:helper.contactsManager + blockingManager:SSKEnvironment.shared.blockingManager completionBlock:^(BOOL isBlocked) { if (!isBlocked) { [weakSelf addToGroup:signalAccount.recipientId]; @@ -116,22 +96,6 @@ NS_ASSUME_NONNULL_BEGIN return; } - BOOL didShowSNAlert = [SafetyNumberConfirmationAlert - presentAlertIfNecessaryWithRecipientId:signalAccount.recipientId - confirmationText: - NSLocalizedString(@"SAFETY_NUMBER_CHANGED_CONFIRM_ADD_TO_GROUP_ACTION", - @"button title to confirm adding a recipient to a group when their safety " - @"number has recently changed") - contactsManager:helper.contactsManager - completion:^(BOOL didConfirmIdentity) { - if (didConfirmIdentity) { - [weakSelf addToGroup:signalAccount.recipientId]; - } - }]; - if (didShowSNAlert) { - return; - } - [self addToGroup:signalAccount.recipientId]; } diff --git a/Session/Signal/AdvancedSettingsTableViewController.h b/Session/Signal/AdvancedSettingsTableViewController.h deleted file mode 100644 index f52659d84..000000000 --- a/Session/Signal/AdvancedSettingsTableViewController.h +++ /dev/null @@ -1,9 +0,0 @@ -// -// Copyright (c) 2018 Open Whisper Systems. All rights reserved. -// - -#import "OWSTableViewController.h" - -@interface AdvancedSettingsTableViewController : OWSTableViewController - -@end diff --git a/Session/Signal/AdvancedSettingsTableViewController.m b/Session/Signal/AdvancedSettingsTableViewController.m deleted file mode 100644 index cf25c0d8a..000000000 --- a/Session/Signal/AdvancedSettingsTableViewController.m +++ /dev/null @@ -1,312 +0,0 @@ -// -// Copyright (c) 2019 Open Whisper Systems. All rights reserved. -// - -#import "AdvancedSettingsTableViewController.h" -#import "DebugLogger.h" -#import "DomainFrontingCountryViewController.h" -#import "OWSCountryMetadata.h" -#import "Pastelog.h" -#import "Session-Swift.h" -#import "TSAccountManager.h" -#import -#import -#import -#import -#import - -NS_ASSUME_NONNULL_BEGIN - -@interface AdvancedSettingsTableViewController () - -@property (nonatomic) Reachability *reachability; - -@end - -#pragma mark - - -@implementation AdvancedSettingsTableViewController - -- (void)loadView -{ - [super loadView]; - - self.title = NSLocalizedString(@"SETTINGS_ADVANCED_TITLE", @""); - - self.reachability = [Reachability reachabilityForInternetConnection]; - - [self observeNotifications]; - - [self updateTableContents]; -} - -- (void)observeNotifications -{ - [[NSNotificationCenter defaultCenter] addObserver:self - selector:@selector(socketStateDidChange) - name:kNSNotification_OWSWebSocketStateDidChange - object:nil]; - [[NSNotificationCenter defaultCenter] addObserver:self - selector:@selector(reachabilityChanged) - name:kReachabilityChangedNotification - object:nil]; -} - -- (void)dealloc -{ - [[NSNotificationCenter defaultCenter] removeObserver:self]; -} - -- (void)socketStateDidChange -{ - OWSAssertIsOnMainThread(); - - [self updateTableContents]; -} - -- (void)reachabilityChanged -{ - OWSAssertIsOnMainThread(); - - [self updateTableContents]; -} - -- (void)viewWillAppear:(BOOL)animated -{ - [super viewWillAppear:animated]; - - [self updateTableContents]; -} - -#pragma mark - Table Contents - -- (void)updateTableContents -{ - OWSTableContents *contents = [OWSTableContents new]; - - __weak AdvancedSettingsTableViewController *weakSelf = self; - - OWSTableSection *loggingSection = [OWSTableSection new]; - loggingSection.headerTitle = NSLocalizedString(@"LOGGING_SECTION", nil); - [loggingSection addItem:[OWSTableItem switchItemWithText:NSLocalizedString(@"SETTINGS_ADVANCED_DEBUGLOG", @"") - accessibilityIdentifier:ACCESSIBILITY_IDENTIFIER_WITH_NAME(self, @"enable_debug_log") - isOnBlock:^{ - return [OWSPreferences isLoggingEnabled]; - } - isEnabledBlock:^{ - return YES; - } - target:weakSelf - selector:@selector(didToggleEnableLogSwitch:)]]; - - - if ([OWSPreferences isLoggingEnabled]) { - [loggingSection - addItem:[OWSTableItem actionItemWithText:NSLocalizedString(@"SETTINGS_ADVANCED_SUBMIT_DEBUGLOG", @"") - accessibilityIdentifier:ACCESSIBILITY_IDENTIFIER_WITH_NAME(self, @"submit_debug_log") - actionBlock:^{ - OWSLogInfo(@"Submitting debug logs"); - [DDLog flushLog]; - [Pastelog submitLogs]; - }]]; - } - - [contents addSection:loggingSection]; - - OWSTableSection *pushNotificationsSection = [OWSTableSection new]; - pushNotificationsSection.headerTitle - = NSLocalizedString(@"PUSH_REGISTER_TITLE", @"Used in table section header and alert view title contexts"); - [pushNotificationsSection addItem:[OWSTableItem actionItemWithText:NSLocalizedString(@"REREGISTER_FOR_PUSH", nil) - accessibilityIdentifier:ACCESSIBILITY_IDENTIFIER_WITH_NAME( - self, @"reregister_push_notifications") - actionBlock:^{ - [weakSelf syncPushTokens]; - }]]; - [contents addSection:pushNotificationsSection]; - - // Censorship circumvention has certain disadvantages so it should only be - // used if necessary. Therefore: - // - // * We disable this setting if the user has a phone number from a censored region - - // censorship circumvention will be auto-activated for this user. - // * We disable this setting if the user is already connected; they're not being - // censored. - // * We continue to show this setting so long as it is set to allow users to disable - // it, for example when they leave a censored region. - OWSTableSection *censorshipSection = [OWSTableSection new]; - censorshipSection.headerTitle = NSLocalizedString(@"SETTINGS_ADVANCED_CENSORSHIP_CIRCUMVENTION_HEADER", - @"Table header for the 'censorship circumvention' section."); - BOOL isAnySocketOpen = TSSocketManager.shared.highestSocketState == OWSWebSocketStateOpen; - if (OWSSignalService.sharedInstance.hasCensoredPhoneNumber) { - if (OWSSignalService.sharedInstance.isCensorshipCircumventionManuallyDisabled) { - censorshipSection.footerTitle - = NSLocalizedString(@"SETTINGS_ADVANCED_CENSORSHIP_CIRCUMVENTION_FOOTER_MANUALLY_DISABLED", - @"Table footer for the 'censorship circumvention' section shown when censorship circumvention has " - @"been manually disabled."); - } else { - censorshipSection.footerTitle = NSLocalizedString( - @"SETTINGS_ADVANCED_CENSORSHIP_CIRCUMVENTION_FOOTER_AUTO_ENABLED", - @"Table footer for the 'censorship circumvention' section shown when censorship circumvention has been " - @"auto-enabled based on local phone number."); - } - } else if (isAnySocketOpen) { - censorshipSection.footerTitle - = NSLocalizedString(@"SETTINGS_ADVANCED_CENSORSHIP_CIRCUMVENTION_FOOTER_WEBSOCKET_CONNECTED", - @"Table footer for the 'censorship circumvention' section shown when the app is connected to the " - @"Signal service."); - } else if (!self.reachability.isReachable) { - censorshipSection.footerTitle - = NSLocalizedString(@"SETTINGS_ADVANCED_CENSORSHIP_CIRCUMVENTION_FOOTER_NO_CONNECTION", - @"Table footer for the 'censorship circumvention' section shown when the app is not connected to the " - @"internet."); - } else { - censorshipSection.footerTitle = NSLocalizedString(@"SETTINGS_ADVANCED_CENSORSHIP_CIRCUMVENTION_FOOTER", - @"Table footer for the 'censorship circumvention' section when censorship circumvention can be manually " - @"enabled."); - } - - // Do enable if : - // - // * ...Censorship circumvention is already manually enabled (to allow users to disable it). - // - // Otherwise, don't enable if: - // - // * ...Censorship circumvention is already enabled based on the local phone number. - // * ...The websocket is connected, since that demonstrates that no censorship is in effect. - // * ...The internet is not reachable, since we don't want to let users to activate - // censorship circumvention unnecessarily, e.g. if they just don't have a valid - // internet connection. - OWSTableSwitchBlock isCensorshipCircumventionOnBlock = ^{ - return OWSSignalService.sharedInstance.isCensorshipCircumventionActive; - }; - Reachability *reachability = self.reachability; - OWSTableSwitchBlock isManualCensorshipCircumventionOnEnabledBlock = ^{ - OWSSignalService *service = OWSSignalService.sharedInstance; - if (service.isCensorshipCircumventionActive) { - return YES; - } else if (service.hasCensoredPhoneNumber && service.isCensorshipCircumventionManuallyDisabled) { - return YES; - } else if (TSSocketManager.shared.highestSocketState == OWSWebSocketStateOpen) { - return NO; - } else { - return reachability.isReachable; - } - }; - - [censorshipSection - addItem:[OWSTableItem switchItemWithText:NSLocalizedString(@"SETTINGS_ADVANCED_CENSORSHIP_CIRCUMVENTION", - @"Label for the 'manual censorship circumvention' switch.") - accessibilityIdentifier:ACCESSIBILITY_IDENTIFIER_WITH_NAME(self, @"censorship_circumvention") - isOnBlock:isCensorshipCircumventionOnBlock - isEnabledBlock:isManualCensorshipCircumventionOnEnabledBlock - target:weakSelf - selector:@selector(didToggleEnableCensorshipCircumventionSwitch:)]]; - - if (OWSSignalService.sharedInstance.isCensorshipCircumventionManuallyActivated) { - OWSCountryMetadata *manualCensorshipCircumventionCountry = - [weakSelf ensureManualCensorshipCircumventionCountry]; - OWSAssertDebug(manualCensorshipCircumventionCountry); - NSString *text = [NSString - stringWithFormat:NSLocalizedString(@"SETTINGS_ADVANCED_CENSORSHIP_CIRCUMVENTION_COUNTRY_FORMAT", - @"Label for the 'manual censorship circumvention' country. Embeds {{the manual " - @"censorship circumvention country}}."), - manualCensorshipCircumventionCountry.localizedCountryName]; - [censorshipSection addItem:[OWSTableItem disclosureItemWithText:text - actionBlock:^{ - [weakSelf showDomainFrontingCountryView]; - }]]; - } - [contents addSection:censorshipSection]; - - self.contents = contents; -} - -- (void)showDomainFrontingCountryView -{ - DomainFrontingCountryViewController *vc = [DomainFrontingCountryViewController new]; - [self.navigationController pushViewController:vc animated:YES]; -} - -- (OWSCountryMetadata *)ensureManualCensorshipCircumventionCountry -{ - OWSAssertIsOnMainThread(); - - OWSCountryMetadata *countryMetadata = nil; - NSString *countryCode = OWSSignalService.sharedInstance.manualCensorshipCircumventionCountryCode; - if (countryCode) { - countryMetadata = [OWSCountryMetadata countryMetadataForCountryCode:countryCode]; - } - - if (!countryMetadata) { - countryCode = [PhoneNumber defaultCountryCode]; - if (countryCode) { - countryMetadata = [OWSCountryMetadata countryMetadataForCountryCode:countryCode]; - } - } - - if (!countryMetadata) { - countryCode = @"US"; - countryMetadata = [OWSCountryMetadata countryMetadataForCountryCode:countryCode]; - OWSAssertDebug(countryMetadata); - } - - if (countryMetadata) { - // Ensure the "manual censorship circumvention" country state is in sync. - OWSSignalService.sharedInstance.manualCensorshipCircumventionCountryCode = countryCode; - } - - return countryMetadata; -} - -#pragma mark - Actions - -- (void)syncPushTokens -{ - OWSSyncPushTokensJob *job = - [[OWSSyncPushTokensJob alloc] initWithAccountManager:AppEnvironment.shared.accountManager - preferences:Environment.shared.preferences]; - job.uploadOnlyIfStale = NO; - [job run] - .then(^{ - [OWSAlerts showAlertWithTitle:NSLocalizedString(@"PUSH_REGISTER_SUCCESS", - @"Title of alert shown when push tokens sync job succeeds.")]; - }) - .catch(^(NSError *error) { - [OWSAlerts showAlertWithTitle:NSLocalizedString(@"REGISTRATION_BODY", - @"Title of alert shown when push tokens sync job fails.")]; - }); -} - -- (void)didToggleEnableLogSwitch:(UISwitch *)sender -{ - if (!sender.isOn) { - OWSLogInfo(@"disabling logging."); - [[DebugLogger sharedLogger] wipeLogs]; - [[DebugLogger sharedLogger] disableFileLogging]; - } else { - [[DebugLogger sharedLogger] enableFileLogging]; - OWSLogInfo(@"enabling logging."); - } - - [OWSPreferences setIsLoggingEnabled:sender.isOn]; - - [self updateTableContents]; -} - -- (void)didToggleEnableCensorshipCircumventionSwitch:(UISwitch *)sender -{ - OWSSignalService *service = OWSSignalService.sharedInstance; - if (sender.isOn) { - service.isCensorshipCircumventionManuallyDisabled = NO; - service.isCensorshipCircumventionManuallyActivated = YES; - } else { - service.isCensorshipCircumventionManuallyDisabled = YES; - service.isCensorshipCircumventionManuallyActivated = NO; - } - - [self updateTableContents]; -} - -@end - -NS_ASSUME_NONNULL_END diff --git a/Session/Signal/AppDelegate.m b/Session/Signal/AppDelegate.m index 3c7474414..1b838dbf9 100644 --- a/Session/Signal/AppDelegate.m +++ b/Session/Signal/AppDelegate.m @@ -8,31 +8,24 @@ #import "OWSBackup.h" #import "OWSOrphanDataCleaner.h" #import "OWSScreenLockUI.h" -#import "Pastelog.h" #import "Session-Swift.h" #import "SignalApp.h" -#import "SignalsNavigationController.h" -#import "ViewControllerUtils.h" #import #import #import #import -#import #import #import #import #import #import #import -#import -#import #import #import #import #import #import -#import -#import + #import #import #import @@ -40,7 +33,7 @@ #import #import #import -#import + #import #import @@ -118,20 +111,6 @@ static NSTimeInterval launchStartedAt; return SSKEnvironment.shared.disappearingMessagesJob; } -- (TSSocketManager *)socketManager -{ - OWSAssertDebug(SSKEnvironment.shared.socketManager); - - return SSKEnvironment.shared.socketManager; -} - -- (OWSMessageManager *)messageManager -{ - OWSAssertDebug(SSKEnvironment.shared.messageManager); - - return SSKEnvironment.shared.messageManager; -} - - (OWSWindowManager *)windowManager { return Environment.shared.windowManager; @@ -279,18 +258,12 @@ static NSTimeInterval launchStartedAt; selector:@selector(registrationStateDidChange) name:RegistrationStateDidChangeNotification object:nil]; - [[NSNotificationCenter defaultCenter] addObserver:self - selector:@selector(registrationLockDidChange:) - name:NSNotificationName_2FAStateDidChange - object:nil]; // Loki - Observe data nuke request notifications [NSNotificationCenter.defaultCenter addObserver:self selector:@selector(handleDataNukeRequested:) name:NSNotification.dataNukeRequested object:nil]; OWSLogInfo(@"application: didFinishLaunchingWithOptions completed."); - [OWSAnalytics appLaunchDidBegin]; - return YES; } @@ -394,41 +367,6 @@ static NSTimeInterval launchStartedAt; } } -- (void)showLaunchFailureUI:(NSError *)error -{ - // Disable normal functioning of app. - self.didAppLaunchFail = YES; - - // We perform a subset of the [application:didFinishLaunchingWithOptions:]. - [AppVersion sharedInstance]; - [self startupLogging]; - - self.window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]]; - - // Show the launch screen - self.window.rootViewController = - [[UIStoryboard storyboardWithName:@"Launch Screen" bundle:nil] instantiateInitialViewController]; - - [self.window makeKeyAndVisible]; - - UIAlertController *alert = - [UIAlertController alertControllerWithTitle:NSLocalizedString(@"APP_LAUNCH_FAILURE_ALERT_TITLE", - @"Title for the 'app launch failed' alert.") - message:NSLocalizedString(@"APP_LAUNCH_FAILURE_ALERT_MESSAGE", - @"Message for the 'app launch failed' alert.") - preferredStyle:UIAlertControllerStyleAlert]; - - [alert addAction:[UIAlertAction actionWithTitle:NSLocalizedString(@"SETTINGS_ADVANCED_SUBMIT_DEBUGLOG", nil) - style:UIAlertActionStyleDefault - handler:^(UIAlertAction *_Nonnull action) { - [Pastelog submitLogsWithCompletion:^{ - OWSFail(@"Exiting after sharing debug logs."); - }]; - }]]; - UIViewController *fromViewController = [[UIApplication sharedApplication] frontmostViewController]; - [fromViewController presentAlert:alert]; -} - - (void)startupLogging { OWSLogInfo(@"iOS Version: %@", [UIDevice currentDevice].systemVersion); @@ -498,16 +436,6 @@ static NSTimeInterval launchStartedAt; [[[OWSFailedMessagesJob alloc] initWithPrimaryStorage:self.primaryStorage] run]; [[[OWSFailedAttachmentDownloadsJob alloc] initWithPrimaryStorage:self.primaryStorage] run]; }); - } else { - OWSLogInfo(@"Running post launch block for unregistered user."); - - // Unregistered user should have no unread messages. e.g. if you delete your account. - [AppEnvironment.shared.notificationPresenter clearAllNotifications]; - - UITapGestureRecognizer *gesture = - [[UITapGestureRecognizer alloc] initWithTarget:[Pastelog class] action:@selector(submitLogs)]; - gesture.numberOfTapsRequired = 8; - [self.window addGestureRecognizer:gesture]; } }); // end dispatchOnce for first time we become active @@ -640,8 +568,6 @@ static NSTimeInterval launchStartedAt; [self ensureRootViewController]; - [self.messageManager startObserving]; - [self.udManager setup]; [self preheatDatabaseViews]; @@ -657,8 +583,6 @@ static NSTimeInterval launchStartedAt; if (appVersion.lastAppVersion.length > 0 && ![appVersion.lastAppVersion isEqualToString:appVersion.currentAppVersion]) { [[self.tsAccountManager updateAccountAttributes] retainUntilComplete]; - - [SSKEnvironment.shared.syncManager sendConfigurationSyncMessage]; } } } @@ -734,18 +658,6 @@ static NSTimeInterval launchStartedAt; [UIViewController attemptRotationToDeviceOrientation]; } -#pragma mark - Status Bar Interaction - -- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event -{ - [super touchesBegan:touches withEvent:event]; - CGPoint location = [[[event allTouches] anyObject] locationInView:[self window]]; - CGRect statusBarFrame = [UIApplication sharedApplication].statusBarFrame; - if (CGRectContainsPoint(statusBarFrame, location)) { - [[NSNotificationCenter defaultCenter] postNotificationName:TappedStatusBarNotification object:nil]; - } -} - #pragma mark - Notifications - (void)application:(UIApplication *)application didRegisterForRemoteNotificationsWithDeviceToken:(NSData *)deviceToken @@ -953,7 +865,6 @@ static NSTimeInterval launchStartedAt; [[LKPushNotificationManager unregisterWithToken:deviceToken isForcedUpdate:YES] retainUntilComplete]; } [ThreadUtil deleteAllContent]; - [SSKEnvironment.shared.messageSenderJobQueue clearAllJobs]; [SSKEnvironment.shared.identityManager clearIdentityKey]; [SNSnodeAPI clearSnodePool]; [self stopPoller]; diff --git a/Session/Signal/AppEnvironment.swift b/Session/Signal/AppEnvironment.swift index 5469779bc..14a57265e 100644 --- a/Session/Signal/AppEnvironment.swift +++ b/Session/Signal/AppEnvironment.swift @@ -34,9 +34,6 @@ import SignalUtilitiesKit // @objc // public var outboundCallInitiator: OutboundCallInitiator - @objc - public var messageFetcherJob: MessageFetcherJob - @objc public var accountManager: AccountManager @@ -81,9 +78,6 @@ import SignalUtilitiesKit private override init() { self.callMessageHandler = WebRTCCallMessageHandler() -// self.callService = CallService() -// self.outboundCallInitiator = OutboundCallInitiator() - self.messageFetcherJob = MessageFetcherJob() self.accountManager = AccountManager() self.notificationPresenter = NotificationPresenter() self.pushRegistrationManager = PushRegistrationManager() diff --git a/Session/Signal/AppNotifications.swift b/Session/Signal/AppNotifications.swift index 29b44b0c0..1b6128be9 100644 --- a/Session/Signal/AppNotifications.swift +++ b/Session/Signal/AppNotifications.swift @@ -120,14 +120,6 @@ protocol NotificationPresenterAdaptee: class { func cancelNotifications(threadId: String) func clearAllNotifications() - - var hasReceivedSyncMessageRecently: Bool { get } -} - -extension NotificationPresenterAdaptee { - var hasReceivedSyncMessageRecently: Bool { - return OWSDeviceManager.shared().hasReceivedSyncMessage(inLastSeconds: 60) - } } @objc(OWSNotificationPresenter) @@ -153,10 +145,6 @@ public class NotificationPresenter: NSObject, NotificationsProtocol { // MARK: - Dependencies - var contactsManager: OWSContactsManager { - return Environment.shared.contactsManager - } - var identityManager: OWSIdentityManager { return OWSIdentityManager.shared() } @@ -209,140 +197,6 @@ public class NotificationPresenter: NSObject, NotificationsProtocol { return adaptee.registerNotificationSettings() } -// func presentIncomingCall(_ call: SignalCall, callerName: String) { -// -// let notificationTitle: String? -// switch previewType { -// case .noNameNoPreview: -// notificationTitle = nil -// case .nameNoPreview, .namePreview: -// notificationTitle = callerName -// } -// let notificationBody = NotificationStrings.incomingCallBody -// -// let remotePhoneNumber = call.remotePhoneNumber -// let thread = TSContactThread.getOrCreateThread(contactId: remotePhoneNumber) -// -// guard let threadId = thread.uniqueId else { -// owsFailDebug("threadId was unexpectedly nil") -// return -// } -// -// let userInfo = [ -// AppNotificationUserInfoKey.threadId: threadId, -// AppNotificationUserInfoKey.localCallId: call.localId.uuidString -// ] -// -// DispatchQueue.main.async { -// self.adaptee.notify(category: .incomingCall, -// title: notificationTitle, -// body: notificationBody, -// userInfo: userInfo, -// sound: .defaultiOSIncomingRingtone, -// replacingIdentifier: call.localId.uuidString) -// } -// } -// -// func presentMissedCall(_ call: SignalCall, callerName: String) { -// let notificationTitle: String? -// switch previewType { -// case .noNameNoPreview: -// notificationTitle = nil -// case .nameNoPreview, .namePreview: -// notificationTitle = callerName -// } -// let notificationBody = NotificationStrings.missedCallBody -// -// let remotePhoneNumber = call.remotePhoneNumber -// let thread = TSContactThread.getOrCreateThread(contactId: remotePhoneNumber) -// -// guard let threadId = thread.uniqueId else { -// owsFailDebug("threadId was unexpectedly nil") -// return -// } -// -// let userInfo = [ -// AppNotificationUserInfoKey.threadId: threadId, -// AppNotificationUserInfoKey.callBackNumber: remotePhoneNumber -// ] -// -// DispatchQueue.main.async { -// let sound = self.requestSound(thread: thread) -// self.adaptee.notify(category: .missedCall, -// title: notificationTitle, -// body: notificationBody, -// userInfo: userInfo, -// sound: sound, -// replacingIdentifier: call.localId.uuidString) -// } -// } -// -// public func presentMissedCallBecauseOfNoLongerVerifiedIdentity(call: SignalCall, callerName: String) { -// let notificationTitle: String? -// switch previewType { -// case .noNameNoPreview: -// notificationTitle = nil -// case .nameNoPreview, .namePreview: -// notificationTitle = callerName -// } -// let notificationBody = NotificationStrings.missedCallBecauseOfIdentityChangeBody -// -// let remotePhoneNumber = call.remotePhoneNumber -// let thread = TSContactThread.getOrCreateThread(contactId: remotePhoneNumber) -// guard let threadId = thread.uniqueId else { -// owsFailDebug("threadId was unexpectedly nil") -// return -// } -// -// let userInfo = [ -// AppNotificationUserInfoKey.threadId: threadId -// ] -// -// DispatchQueue.main.async { -// let sound = self.requestSound(thread: thread) -// self.adaptee.notify(category: .missedCallFromNoLongerVerifiedIdentity, -// title: notificationTitle, -// body: notificationBody, -// userInfo: userInfo, -// sound: sound, -// replacingIdentifier: call.localId.uuidString) -// } -// } -// -// public func presentMissedCallBecauseOfNewIdentity(call: SignalCall, callerName: String) { -// let notificationTitle: String? -// switch previewType { -// case .noNameNoPreview: -// notificationTitle = nil -// case .nameNoPreview, .namePreview: -// notificationTitle = callerName -// } -// let notificationBody = NotificationStrings.missedCallBecauseOfIdentityChangeBody -// -// let remotePhoneNumber = call.remotePhoneNumber -// let thread = TSContactThread.getOrCreateThread(contactId: remotePhoneNumber) -// -// guard let threadId = thread.uniqueId else { -// owsFailDebug("threadId was unexpectedly nil") -// return -// } -// -// let userInfo = [ -// AppNotificationUserInfoKey.threadId: threadId, -// AppNotificationUserInfoKey.callBackNumber: remotePhoneNumber -// ] -// -// DispatchQueue.main.async { -// let sound = self.requestSound(thread: thread) -// self.adaptee.notify(category: .missedCall, -// title: notificationTitle, -// body: notificationBody, -// userInfo: userInfo, -// sound: sound, -// replacingIdentifier: call.localId.uuidString) -// } -// } - public func notifyUser(for incomingMessage: TSIncomingMessage, in thread: TSThread, transaction: YapDatabaseReadTransaction) { guard !thread.isMuted else { @@ -359,7 +213,7 @@ public class NotificationPresenter: NSObject, NotificationsProtocol { // for more details. let messageText = DisplayableText.filterNotificationText(rawMessageText) - let senderName = OWSUserProfile.fetch(uniqueId: incomingMessage.authorId, transaction: transaction)?.profileName ?? contactsManager.displayName(forPhoneIdentifier: incomingMessage.authorId) + let senderName = SSKEnvironment.shared.profileManager.profileNameForRecipient(withID: incomingMessage.authorId, avoidingWriteTransaction: true)! let notificationTitle: String? switch previewType { @@ -401,12 +255,6 @@ public class NotificationPresenter: NSObject, NotificationsProtocol { // Don't reply from lockscreen if anyone in this conversation is // "no longer verified". var category = AppNotificationCategory.incomingMessage - for recipientId in thread.recipientIdentifiers { - if self.identityManager.verificationState(forRecipientId: recipientId) == .noLongerVerified { - category = AppNotificationCategory.incomingMessageFromNoLongerVerifiedIdentity - break - } - } let userInfo = [ AppNotificationUserInfoKey.threadId: threadId @@ -554,14 +402,6 @@ class NotificationActionHandler { return SignalApp.shared() } - var messageSender: MessageSender { - return SSKEnvironment.shared.messageSender - } - -// var callUIAdapter: CallUIAdapter { -// return AppEnvironment.shared.callService.callUIAdapter -// } - var notificationPresenter: NotificationPresenter { return AppEnvironment.shared.notificationPresenter } @@ -572,41 +412,6 @@ class NotificationActionHandler { // MARK: - -// func answerCall(userInfo: [AnyHashable: Any]) throws -> Promise { -// guard let localCallIdString = userInfo[AppNotificationUserInfoKey.localCallId] as? String else { -// throw NotificationError.failDebug("localCallIdString was unexpectedly nil") -// } -// -// guard let localCallId = UUID(uuidString: localCallIdString) else { -// throw NotificationError.failDebug("unable to build localCallId. localCallIdString: \(localCallIdString)") -// } -// -// callUIAdapter.answerCall(localId: localCallId) -// return Promise.value(()) -// } -// -// func callBack(userInfo: [AnyHashable: Any]) throws -> Promise { -// guard let recipientId = userInfo[AppNotificationUserInfoKey.callBackNumber] as? String else { -// throw NotificationError.failDebug("recipientId was unexpectedly nil") -// } -// -// callUIAdapter.startAndShowOutgoingCall(recipientId: recipientId, hasLocalVideo: false) -// return Promise.value(()) -// } -// -// func declineCall(userInfo: [AnyHashable: Any]) throws -> Promise { -// guard let localCallIdString = userInfo[AppNotificationUserInfoKey.localCallId] as? String else { -// throw NotificationError.failDebug("localCallIdString was unexpectedly nil") -// } -// -// guard let localCallId = UUID(uuidString: localCallIdString) else { -// throw NotificationError.failDebug("unable to build localCallId. localCallIdString: \(localCallIdString)") -// } -// -// callUIAdapter.declineCall(localId: localCallId) -// return Promise.value(()) -// } - func markAsRead(userInfo: [AnyHashable: Any]) throws -> Promise { guard let threadId = userInfo[AppNotificationUserInfoKey.threadId] as? String else { throw NotificationError.failDebug("threadId was unexpectedly nil") @@ -629,15 +434,18 @@ class NotificationActionHandler { } return markAsRead(thread: thread).then { () -> Promise in - let sendPromise = ThreadUtil.sendMessageNonDurably(text: replyText, - thread: thread, - quotedReplyModel: nil, - messageSender: self.messageSender) + // TODO TODO TODO + +// let sendPromise = ThreadUtil.sendMessageNonDurably(text: replyText, +// thread: thread, +// quotedReplyModel: nil, +// messageSender: self.messageSender) - return sendPromise.recover { error in - Logger.warn("Failed to send reply message from notification with error: \(error)") - self.notificationPresenter.notifyForFailedSend(inThread: thread) - } +// return sendPromise.recover { error in +// Logger.warn("Failed to send reply message from notification with error: \(error)") +// self.notificationPresenter.notifyForFailedSend(inThread: thread) +// } + return Promise.value(()) } } @@ -666,25 +474,6 @@ class NotificationActionHandler { } } -extension ThreadUtil { - static var dbReadConnection: YapDatabaseConnection { - return OWSPrimaryStorage.shared().dbReadConnection - } - - class func sendMessageNonDurably(text: String, thread: TSThread, quotedReplyModel: OWSQuotedReplyModel?, messageSender: MessageSender) -> Promise { - return Promise { resolver in - self.dbReadConnection.read { transaction in - _ = self.sendMessageNonDurably(withText: text, - in: thread, - quotedReplyModel: quotedReplyModel, - transaction: transaction, - messageSender: messageSender, - completion: resolver.resolve) - } - } - } -} - enum NotificationError: Error { case assertionError(description: String) } diff --git a/Session/Signal/AvatarTableViewCell.swift b/Session/Signal/AvatarTableViewCell.swift index 955584d82..005cfaca0 100644 --- a/Session/Signal/AvatarTableViewCell.swift +++ b/Session/Signal/AvatarTableViewCell.swift @@ -9,7 +9,7 @@ public class AvatarTableViewCell: UITableViewCell { private let columns: UIStackView private let textRows: UIStackView - private let avatarView: AvatarImageView +// private let avatarView: AvatarImageView private let _textLabel: UILabel override public var textLabel: UILabel? { @@ -27,8 +27,8 @@ public class AvatarTableViewCell: UITableViewCell { @objc public override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) { - self.avatarView = AvatarImageView() - avatarView.autoSetDimensions(to: CGSize(width: CGFloat(kStandardAvatarSize), height: CGFloat(kStandardAvatarSize))) +// self.avatarView = AvatarImageView() +// avatarView.autoSetDimensions(to: CGSize(width: CGFloat(kStandardAvatarSize), height: CGFloat(kStandardAvatarSize))) self._textLabel = UILabel() self._detailTextLabel = UILabel() @@ -36,7 +36,7 @@ public class AvatarTableViewCell: UITableViewCell { self.textRows = UIStackView(arrangedSubviews: [_textLabel, _detailTextLabel]) textRows.axis = .vertical - self.columns = UIStackView(arrangedSubviews: [avatarView, textRows]) + self.columns = UIStackView(arrangedSubviews: [ textRows ]) columns.axis = .horizontal columns.spacing = CGFloat(kContactCellAvatarTextMargin) @@ -54,7 +54,7 @@ public class AvatarTableViewCell: UITableViewCell { @objc public func configure(image: UIImage?, text: String?, detailText: String?) { - self.avatarView.image = image +// self.avatarView.image = image self.textLabel?.text = text self.detailTextLabel?.text = detailText @@ -65,7 +65,7 @@ public class AvatarTableViewCell: UITableViewCell { public override func prepareForReuse() { super.prepareForReuse() - self.avatarView.image = nil +// self.avatarView.image = nil self.textLabel?.text = nil self.detailTextLabel?.text = nil } diff --git a/Session/Signal/AvatarViewHelper.m b/Session/Signal/AvatarViewHelper.m index 6a5e7f712..b5d4b7972 100644 --- a/Session/Signal/AvatarViewHelper.m +++ b/Session/Signal/AvatarViewHelper.m @@ -6,9 +6,9 @@ #import "OWSNavigationController.h" #import "Session-Swift.h" #import -#import + #import -#import + #import #import #import diff --git a/Session/Signal/BlockListViewController.h b/Session/Signal/BlockListViewController.h deleted file mode 100644 index 05fd68422..000000000 --- a/Session/Signal/BlockListViewController.h +++ /dev/null @@ -1,13 +0,0 @@ -// -// Copyright (c) 2018 Open Whisper Systems. All rights reserved. -// - -#import - -NS_ASSUME_NONNULL_BEGIN - -@interface BlockListViewController : OWSViewController - -@end - -NS_ASSUME_NONNULL_END diff --git a/Session/Signal/BlockListViewController.m b/Session/Signal/BlockListViewController.m deleted file mode 100644 index 1f6fc3ed0..000000000 --- a/Session/Signal/BlockListViewController.m +++ /dev/null @@ -1,175 +0,0 @@ -// -// Copyright (c) 2019 Open Whisper Systems. All rights reserved. -// - -#import "BlockListViewController.h" -#import "AddToBlockListViewController.h" -#import "BlockListUIUtils.h" -#import "ContactTableViewCell.h" -#import "ContactsViewHelper.h" -#import "OWSTableViewController.h" -#import "PhoneNumber.h" -#import "Session-Swift.h" -#import "UIFont+OWS.h" -#import "UIView+OWS.h" -#import -#import -#import -#import - -NS_ASSUME_NONNULL_BEGIN - -@interface BlockListViewController () - -@property (nonatomic, readonly) ContactsViewHelper *contactsViewHelper; - -@property (nonatomic, readonly) OWSTableViewController *tableViewController; - -@end - -#pragma mark - - -@implementation BlockListViewController - -- (OWSBlockingManager *)blockingManager -{ - return OWSBlockingManager.sharedManager; -} - -- (void)loadView -{ - [super loadView]; - - _contactsViewHelper = [[ContactsViewHelper alloc] initWithDelegate:self]; - - self.title - = NSLocalizedString(@"SETTINGS_BLOCK_LIST_TITLE", @"Label for the block list section of the settings view"); - - _tableViewController = [OWSTableViewController new]; - [self.view addSubview:self.tableViewController.view]; - [self addChildViewController:self.tableViewController]; - [_tableViewController.view autoPinEdgesToSuperviewEdges]; - self.tableViewController.tableView.rowHeight = UITableViewAutomaticDimension; - self.tableViewController.tableView.estimatedRowHeight = 60; - - [self updateTableContents]; -} - -#pragma mark - Table view data source - -- (void)updateTableContents -{ - OWSTableContents *contents = [OWSTableContents new]; - - __weak BlockListViewController *weakSelf = self; - ContactsViewHelper *helper = self.contactsViewHelper; - - // "Add" section - - OWSTableSection *addSection = [OWSTableSection new]; - addSection.footerTitle = NSLocalizedString( - @"BLOCK_USER_BEHAVIOR_EXPLANATION", @"An explanation of the consequences of blocking another user."); - - [addSection - addItem:[OWSTableItem - disclosureItemWithText:NSLocalizedString(@"SETTINGS_BLOCK_LIST_ADD_BUTTON", - @"A label for the 'add phone number' button in the block list table.") - accessibilityIdentifier:ACCESSIBILITY_IDENTIFIER_WITH_NAME(self, @"add") - actionBlock:^{ - AddToBlockListViewController *vc = [[AddToBlockListViewController alloc] init]; - [weakSelf.navigationController pushViewController:vc animated:YES]; - }]]; - [contents addSection:addSection]; - - // "Blocklist" section - - NSArray *blockedPhoneNumbers = - [self.blockingManager.blockedPhoneNumbers sortedArrayUsingSelector:@selector(compare:)]; - - if (blockedPhoneNumbers.count > 0) { - OWSTableSection *blockedContactsSection = [OWSTableSection new]; - blockedContactsSection.headerTitle = NSLocalizedString( - @"BLOCK_LIST_BLOCKED_USERS_SECTION", @"Section header for users that have been blocked"); - - for (NSString *phoneNumber in blockedPhoneNumbers) { - [blockedContactsSection addItem:[OWSTableItem - itemWithCustomCellBlock:^{ - ContactTableViewCell *cell = [ContactTableViewCell new]; - [cell configureWithRecipientId:phoneNumber]; - cell.accessibilityIdentifier = ACCESSIBILITY_IDENTIFIER_WITH_NAME( - BlockListViewController, @"user"); - return cell; - } - customRowHeight:UITableViewAutomaticDimension - actionBlock:^{ - [BlockListUIUtils - showUnblockPhoneNumberActionSheet:phoneNumber - fromViewController:weakSelf - blockingManager:helper.blockingManager - contactsManager:helper.contactsManager - completionBlock:^(BOOL isBlocked) { - [weakSelf updateTableContents]; - }]; - }]]; - } - [contents addSection:blockedContactsSection]; - } - - NSArray *blockedGroups = self.blockingManager.blockedGroups; - if (blockedGroups.count > 0) { - OWSTableSection *blockedGroupsSection = [OWSTableSection new]; - blockedGroupsSection.headerTitle = NSLocalizedString( - @"BLOCK_LIST_BLOCKED_GROUPS_SECTION", @"Section header for groups that have been blocked"); - - for (TSGroupModel *blockedGroup in blockedGroups) { - UIImage *_Nullable image = blockedGroup.groupImage; - if (!image) { - NSString *conversationColorName = - [TSGroupThread defaultConversationColorNameForGroupId:blockedGroup.groupId]; - image = [OWSGroupAvatarBuilder defaultAvatarForGroupId:blockedGroup.groupId - conversationColorName:conversationColorName - diameter:kStandardAvatarSize]; - } - NSString *groupName - = blockedGroup.groupName.length > 0 ? blockedGroup.groupName : TSGroupThread.defaultGroupName; - - [blockedGroupsSection addItem:[OWSTableItem - itemWithCustomCellBlock:^{ - OWSAvatarTableViewCell *cell = [OWSAvatarTableViewCell new]; - [cell configureWithImage:image - text:groupName - detailText:nil]; - return cell; - } - customRowHeight:UITableViewAutomaticDimension - actionBlock:^{ - [BlockListUIUtils showUnblockGroupActionSheet:blockedGroup - displayName:groupName - fromViewController:weakSelf - blockingManager:helper.blockingManager - completionBlock:^(BOOL isBlocked) { - [weakSelf updateTableContents]; - }]; - }]]; - } - [contents addSection:blockedGroupsSection]; - } - - self.tableViewController.contents = contents; -} - -#pragma mark - ContactsViewHelperDelegate - -- (void)contactsViewHelperDidUpdateContacts -{ - [self updateTableContents]; -} - -- (BOOL)shouldHideLocalNumber -{ - return YES; -} - -@end - -NS_ASSUME_NONNULL_END diff --git a/Session/Signal/ColorPickerViewController.swift b/Session/Signal/ColorPickerViewController.swift deleted file mode 100644 index 39e1c0256..000000000 --- a/Session/Signal/ColorPickerViewController.swift +++ /dev/null @@ -1,532 +0,0 @@ -// -// Copyright (c) 2019 Open Whisper Systems. All rights reserved. -// - -import Foundation - -@objc -class OWSColorPickerAccessoryView: NeverClearView { - override var intrinsicContentSize: CGSize { - return CGSize(width: kSwatchSize, height: kSwatchSize) - } - - override func sizeThatFits(_ size: CGSize) -> CGSize { - return self.intrinsicContentSize - } - - let kSwatchSize: CGFloat = 24 - - @objc - required init(color: UIColor) { - super.init(frame: .zero) - - let circleView = CircleView() - circleView.backgroundColor = color - addSubview(circleView) - circleView.autoSetDimensions(to: CGSize(width: kSwatchSize, height: kSwatchSize)) - circleView.autoPinEdgesToSuperviewEdges() - } - - required init?(coder aDecoder: NSCoder) { - fatalError("init(coder:) has not been implemented") - } -} - -@objc (OWSCircleView) -class CircleView: UIView { - override var bounds: CGRect { - didSet { - self.layer.cornerRadius = self.bounds.size.height / 2 - } - } -} - -protocol ColorViewDelegate: class { - func colorViewWasTapped(_ colorView: ColorView) -} - -class ColorView: UIView { - public weak var delegate: ColorViewDelegate? - public let conversationColor: OWSConversationColor - - private let swatchView: CircleView - private let selectedRing: CircleView - public var isSelected: Bool = false { - didSet { - self.selectedRing.isHidden = !isSelected - } - } - - required init(conversationColor: OWSConversationColor) { - self.conversationColor = conversationColor - self.swatchView = CircleView() - self.selectedRing = CircleView() - - super.init(frame: .zero) - self.addSubview(selectedRing) - self.addSubview(swatchView) - - // Selected Ring - let cellHeight: CGFloat = ScaleFromIPhone5(60) - selectedRing.autoSetDimensions(to: CGSize(width: cellHeight, height: cellHeight)) - - selectedRing.layer.borderColor = Theme.secondaryColor.cgColor - selectedRing.layer.borderWidth = 2 - selectedRing.autoPinEdgesToSuperviewEdges() - selectedRing.isHidden = true - - // Color Swatch - swatchView.backgroundColor = conversationColor.primaryColor - - let swatchSize: CGFloat = ScaleFromIPhone5(46) - swatchView.autoSetDimensions(to: CGSize(width: swatchSize, height: swatchSize)) - - swatchView.autoCenterInSuperview() - - // gestures - let tapGesture = UITapGestureRecognizer(target: self, action: #selector(didTap)) - self.addGestureRecognizer(tapGesture) - } - - required init?(coder aDecoder: NSCoder) { - notImplemented() - } - - // MARK: Actions - - @objc - func didTap() { - delegate?.colorViewWasTapped(self) - } -} - -@objc -protocol ColorPickerDelegate: class { - func colorPicker(_ colorPicker: ColorPicker, didPickConversationColor conversationColor: OWSConversationColor) -} - -@objc(OWSColorPicker) -class ColorPicker: NSObject, ColorPickerViewDelegate { - - @objc - public weak var delegate: ColorPickerDelegate? - - @objc - let sheetViewController: SheetViewController - - @objc - init(thread: TSThread) { - let colorName = thread.conversationColorName - let currentConversationColor = OWSConversationColor.conversationColorOrDefault(colorName: colorName) - sheetViewController = SheetViewController() - - super.init() - - let colorPickerView = ColorPickerView(thread: thread) - colorPickerView.delegate = self - colorPickerView.select(conversationColor: currentConversationColor) - - sheetViewController.contentView.addSubview(colorPickerView) - colorPickerView.autoPinEdgesToSuperviewEdges() - } - - // MARK: ColorPickerViewDelegate - - func colorPickerView(_ colorPickerView: ColorPickerView, didPickConversationColor conversationColor: OWSConversationColor) { - self.delegate?.colorPicker(self, didPickConversationColor: conversationColor) - } -} - -protocol ColorPickerViewDelegate: class { - func colorPickerView(_ colorPickerView: ColorPickerView, didPickConversationColor conversationColor: OWSConversationColor) -} - -class ColorPickerView: UIView, ColorViewDelegate { - - private let colorViews: [ColorView] - let conversationStyle: ConversationStyle - var outgoingMessageView = OWSMessageBubbleView(forAutoLayout: ()) - var incomingMessageView = OWSMessageBubbleView(forAutoLayout: ()) - weak var delegate: ColorPickerViewDelegate? - - // This is mostly a developer convenience - OWSMessageCell asserts at some point - // that the available method width is greater than 0. - // We ultimately use the width of the picker view which will be larger. - let kMinimumConversationWidth: CGFloat = 300 - override var bounds: CGRect { - didSet { - updateMockConversationView() - } - } - - let mockConversationView: UIView = UIView() - - init(thread: TSThread) { - let allConversationColors = OWSConversationColor.conversationColorNames.map { OWSConversationColor.conversationColorOrDefault(colorName: $0) } - - self.colorViews = allConversationColors.map { ColorView(conversationColor: $0) } - - self.conversationStyle = ConversationStyle(thread: thread) - - super.init(frame: .zero) - - colorViews.forEach { $0.delegate = self } - - let headerView = self.buildHeaderView() - mockConversationView.layoutMargins = UIEdgeInsets(top: 16, left: 16, bottom: 16, right: 16) - mockConversationView.backgroundColor = Theme.backgroundColor - self.updateMockConversationView() - - let paletteView = self.buildPaletteView(colorViews: colorViews) - - let rowsStackView = UIStackView(arrangedSubviews: [headerView, mockConversationView, paletteView]) - rowsStackView.axis = .vertical - addSubview(rowsStackView) - rowsStackView.autoPinEdgesToSuperviewEdges() - } - - required init?(coder aDecoder: NSCoder) { - notImplemented() - } - - // MARK: ColorViewDelegate - - func colorViewWasTapped(_ colorView: ColorView) { - self.select(conversationColor: colorView.conversationColor) - self.delegate?.colorPickerView(self, didPickConversationColor: colorView.conversationColor) - updateMockConversationView() - } - - fileprivate func select(conversationColor selectedConversationColor: OWSConversationColor) { - colorViews.forEach { colorView in - colorView.isSelected = colorView.conversationColor == selectedConversationColor - } - } - - // MARK: View Building - - private func buildHeaderView() -> UIView { - let headerView = UIView() - headerView.layoutMargins = UIEdgeInsets(top: 15, left: 16, bottom: 15, right: 16) - - let titleLabel = UILabel() - titleLabel.text = NSLocalizedString("COLOR_PICKER_SHEET_TITLE", comment: "Modal Sheet title when picking a conversation color.") - titleLabel.textAlignment = .center - titleLabel.font = UIFont.ows_dynamicTypeBody.ows_mediumWeight() - titleLabel.textColor = Theme.primaryColor - - headerView.addSubview(titleLabel) - titleLabel.ows_autoPinToSuperviewMargins() - - let bottomBorderView = UIView() - bottomBorderView.backgroundColor = Theme.hairlineColor - headerView.addSubview(bottomBorderView) - bottomBorderView.autoPinEdgesToSuperviewEdges(with: .zero, excludingEdge: .top) - bottomBorderView.autoSetDimension(.height, toSize: CGHairlineWidth()) - - return headerView - } - - private func updateMockConversationView() { - /* - conversationStyle.viewWidth = max(bounds.size.width, kMinimumConversationWidth) - mockConversationView.subviews.forEach { $0.removeFromSuperview() } - - // outgoing - outgoingMessageView = OWSMessageBubbleView(forAutoLayout: ()) - let outgoingItem = MockConversationViewItem() - let outgoingText = NSLocalizedString("COLOR_PICKER_DEMO_MESSAGE_1", comment: "The first of two messages demonstrating the chosen conversation color, by rendering this message in an outgoing message bubble.") - outgoingItem.interaction = MockOutgoingMessage(messageBody: outgoingText) - outgoingItem.displayableBodyText = DisplayableText.displayableText(outgoingText) - outgoingItem.interactionType = .outgoingMessage - - outgoingMessageView.viewItem = outgoingItem - outgoingMessageView.cellMediaCache = NSCache() - outgoingMessageView.conversationStyle = conversationStyle - outgoingMessageView.configureViews() - outgoingMessageView.loadContent() - let outgoingCell = UIView() - outgoingCell.addSubview(outgoingMessageView) - outgoingMessageView.autoPinEdgesToSuperviewEdges(with: .zero, excludingEdge: .leading) - let outgoingSize = outgoingMessageView.measureSize() - outgoingMessageView.autoSetDimensions(to: outgoingSize) - - // incoming - incomingMessageView = OWSMessageBubbleView(forAutoLayout: ()) - let incomingItem = MockConversationViewItem() - let incomingText = NSLocalizedString("COLOR_PICKER_DEMO_MESSAGE_2", comment: "The second of two messages demonstrating the chosen conversation color, by rendering this message in an incoming message bubble.") - incomingItem.interaction = MockIncomingMessage(messageBody: incomingText) - incomingItem.displayableBodyText = DisplayableText.displayableText(incomingText) - incomingItem.interactionType = .incomingMessage - - incomingMessageView.viewItem = incomingItem - incomingMessageView.cellMediaCache = NSCache() - incomingMessageView.conversationStyle = conversationStyle - incomingMessageView.configureViews() - incomingMessageView.loadContent() - let incomingCell = UIView() - incomingCell.addSubview(incomingMessageView) - incomingMessageView.autoPinEdgesToSuperviewEdges(with: .zero, excludingEdge: .trailing) - let incomingSize = incomingMessageView.measureSize() - incomingMessageView.autoSetDimensions(to: incomingSize) - - let messagesStackView = UIStackView(arrangedSubviews: [outgoingCell, incomingCell]) - messagesStackView.axis = .vertical - messagesStackView.spacing = 12 - - mockConversationView.addSubview(messagesStackView) - messagesStackView.autoPinEdgesToSuperviewMargins() - */ - } - - private func buildPaletteView(colorViews: [ColorView]) -> UIView { - let paletteView = UIView() - let paletteMargin = ScaleFromIPhone5(12) - paletteView.layoutMargins = UIEdgeInsets(top: paletteMargin, left: paletteMargin, bottom: 0, right: paletteMargin) - - let kRowLength = 4 - let rows: [UIView] = colorViews.chunked(by: kRowLength).map { colorViewsInRow in - let row = UIStackView(arrangedSubviews: colorViewsInRow) - row.distribution = UIStackView.Distribution.equalSpacing - return row - } - let rowsStackView = UIStackView(arrangedSubviews: rows) - rowsStackView.axis = .vertical - rowsStackView.spacing = ScaleFromIPhone5To7Plus(12, 30) - - paletteView.addSubview(rowsStackView) - rowsStackView.ows_autoPinToSuperviewMargins() - - // no-op gesture to keep taps from dismissing SheetView - paletteView.addGestureRecognizer(UITapGestureRecognizer(target: nil, action: nil)) - return paletteView - } -} - -// MARK: Mock Classes for rendering demo conversation - -/* -@objc -private class MockConversationViewItem: NSObject, ConversationViewItem { - var userCanDeleteGroupMessage: Bool = false - var isRSSFeed: Bool = false - var interaction: TSInteraction = TSMessage() - var interactionType: OWSInteractionType = OWSInteractionType.unknown - var quotedReply: OWSQuotedReplyModel? - var isGroupThread: Bool = false - var hasBodyText: Bool = true - var isQuotedReply: Bool = false - var hasQuotedAttachment: Bool = false - var hasQuotedText: Bool = false - var hasCellHeader: Bool = false - var isExpiringMessage: Bool = false - var shouldShowDate: Bool = false - var shouldShowSenderAvatar: Bool = false - var senderName: NSAttributedString? - var shouldHideFooter: Bool = false - var isFirstInCluster: Bool = true - var isLastInCluster: Bool = true - var unreadIndicator: OWSUnreadIndicator? - var lastAudioMessageView: OWSAudioMessageView? - var audioDurationSeconds: CGFloat = 0 - var audioProgressSeconds: CGFloat = 0 - var messageCellType: OWSMessageCellType = .textOnlyMessage - var displayableBodyText: DisplayableText? - var attachmentStream: TSAttachmentStream? - var attachmentPointer: TSAttachmentPointer? - var mediaSize: CGSize = .zero - var displayableQuotedText: DisplayableText? - var quotedAttachmentMimetype: String? - var quotedRecipientId: String? - var didCellMediaFailToLoad: Bool = false - var contactShare: ContactShareViewModel? - var systemMessageText: String? - var authorConversationColorName: String? - var hasBodyTextActionContent: Bool = false - var hasMediaActionContent: Bool = false - var mediaAlbumItems: [ConversationMediaAlbumItem]? - var hasCachedLayoutState: Bool = false - var linkPreview: OWSLinkPreview? - var linkPreviewAttachment: TSAttachment? - - override init() { - super.init() - } - - func itemId() -> String { - return interaction.uniqueId! - } - - func dequeueCell(for collectionView: UICollectionView, indexPath: IndexPath) -> ConversationViewCell { - owsFailDebug("unexpected invocation") - return ConversationViewCell(forAutoLayout: ()) - } - - func replace(_ interaction: TSInteraction, transaction: YapDatabaseReadTransaction) { - owsFailDebug("unexpected invocation") - return - } - - func clearCachedLayoutState() { - owsFailDebug("unexpected invocation") - return - } - - func copyMediaAction() { - owsFailDebug("unexpected invocation") - return - } - - func copyTextAction() { - owsFailDebug("unexpected invocation") - return - } - - func shareMediaAction() { - owsFailDebug("unexpected invocation") - return - } - - func shareTextAction() { - owsFailDebug("unexpected invocation") - return - } - - func saveMediaAction() { - owsFailDebug("unexpected invocation") - return - } - - func deleteAction() { - owsFailDebug("unexpected invocation") - return - } - - func canCopyMedia() -> Bool { - owsFailDebug("unexpected invocation") - return false - } - - func canSaveMedia() -> Bool { - owsFailDebug("unexpected invocation") - return false - } - - func audioPlaybackState() -> AudioPlaybackState { - owsFailDebug("unexpected invocation") - return AudioPlaybackState.paused - } - - func setAudioPlaybackState(_ state: AudioPlaybackState) { - owsFailDebug("unexpected invocation") - return - } - - func setAudioProgress(_ progress: CGFloat, duration: CGFloat) { - owsFailDebug("unexpected invocation") - return - } - - func cellSize() -> CGSize { - owsFailDebug("unexpected invocation") - return CGSize.zero - } - - func vSpacing(withPreviousLayoutItem previousLayoutItem: ConversationViewLayoutItem) -> CGFloat { - owsFailDebug("unexpected invocation") - return 2 - } - - func firstValidAlbumAttachment() -> TSAttachmentStream? { - owsFailDebug("unexpected invocation") - return nil - } - - func mediaAlbumHasFailedAttachment() -> Bool { - owsFailDebug("unexpected invocation") - return false - } -} - */ - -private class MockIncomingMessage: TSIncomingMessage { - init(messageBody: String) { - super.init(incomingMessageWithTimestamp: NSDate.ows_millisecondTimeStamp(), - in: TSThread(), - authorId: "+fake-id", - sourceDeviceId: 1, - messageBody: messageBody, - attachmentIds: [], - expiresInSeconds: 0, - quotedMessage: nil, - contactShare: nil, - linkPreview: nil, - serverTimestamp: nil, - wasReceivedByUD: false) - } - - required init(coder: NSCoder) { - fatalError("init(coder:) has not been implemented") - } - - required init(dictionary dictionaryValue: [String: Any]!) throws { - fatalError("init(dictionary:) has not been implemented") - } - - override func save(with transaction: YapDatabaseReadWriteTransaction) { - // no - op - owsFailDebug("shouldn't save mock message") - } -} - -private class MockOutgoingMessage: TSOutgoingMessage { - init(messageBody: String) { - super.init(outgoingMessageWithTimestamp: NSDate.ows_millisecondTimeStamp(), - in: nil, - messageBody: messageBody, - attachmentIds: [], - expiresInSeconds: 0, - expireStartedAt: 0, - isVoiceMessage: false, - groupMetaMessage: .unspecified, - quotedMessage: nil, - contactShare: nil, - linkPreview: nil) - } - - required init?(coder: NSCoder) { - fatalError("init(coder:) has not been implemented") - } - - required init(dictionary dictionaryValue: [String: Any]!) throws { - fatalError("init(dictionary:) has not been implemented") - } - - override func save(with transaction: YapDatabaseReadWriteTransaction) { - // no - op - owsFailDebug("shouldn't save mock message") - } - - class MockOutgoingMessageRecipientState: TSOutgoingMessageRecipientState { - override var state: OWSOutgoingMessageRecipientState { - return OWSOutgoingMessageRecipientState.sent - } - - override var deliveryTimestamp: NSNumber? { - return NSNumber(value: NSDate.ows_millisecondTimeStamp()) - } - - override var readTimestamp: NSNumber? { - return NSNumber(value: NSDate.ows_millisecondTimeStamp()) - } - } - - override func readRecipientIds() -> [String] { - // makes message appear as read - return ["fake-non-empty-id"] - } - - override func recipientState(forRecipientId recipientId: String) -> TSOutgoingMessageRecipientState? { - return MockOutgoingMessageRecipientState() - } -} diff --git a/Session/Signal/ContactCell.swift b/Session/Signal/ContactCell.swift deleted file mode 100644 index 198b56350..000000000 --- a/Session/Signal/ContactCell.swift +++ /dev/null @@ -1,162 +0,0 @@ -// -// Copyright (c) 2019 Open Whisper Systems. All rights reserved. -// - -import UIKit -import Contacts -import SignalUtilitiesKit - -class ContactCell: UITableViewCell { - - public static let kSeparatorHInset: CGFloat = CGFloat(kAvatarDiameter) + 16 + 8 - - static let kAvatarSpacing: CGFloat = 6 - static let kAvatarDiameter: UInt = 40 - - let contactImageView: AvatarImageView - let textStackView: UIStackView - let titleLabel: UILabel - var subtitleLabel: UILabel - - var contact: Contact? - var showsWhenSelected: Bool = false - - override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) { - self.contactImageView = AvatarImageView() - self.textStackView = UIStackView() - self.titleLabel = UILabel() - self.titleLabel.font = UIFont.ows_dynamicTypeBody - self.subtitleLabel = UILabel() - self.subtitleLabel.font = UIFont.ows_dynamicTypeSubheadline - - super.init(style: style, reuseIdentifier: reuseIdentifier) - - selectionStyle = UITableViewCell.SelectionStyle.none - - textStackView.axis = .vertical - textStackView.addArrangedSubview(titleLabel) - - contactImageView.autoSetDimensions(to: CGSize(width: CGFloat(ContactCell.kAvatarDiameter), height: CGFloat(ContactCell.kAvatarDiameter))) - - let contentColumns: UIStackView = UIStackView(arrangedSubviews: [contactImageView, textStackView]) - contentColumns.axis = .horizontal - contentColumns.spacing = ContactCell.kAvatarSpacing - contentColumns.alignment = .center - - self.contentView.addSubview(contentColumns) - contentColumns.autoPinEdgesToSuperviewMargins() - - NotificationCenter.default.addObserver(self, selector: #selector(self.didChangePreferredContentSize), name: UIContentSizeCategory.didChangeNotification, object: nil) - } - - required init?(coder aDecoder: NSCoder) { - notImplemented() - } - - override func prepareForReuse() { - accessoryType = .none - self.subtitleLabel.removeFromSuperview() - } - - override func setSelected(_ selected: Bool, animated: Bool) { - super.setSelected(selected, animated: animated) - if showsWhenSelected { - accessoryType = selected ? .checkmark : .none - } - } - - @objc func didChangePreferredContentSize() { - self.titleLabel.font = UIFont.ows_dynamicTypeBody - self.subtitleLabel.font = UIFont.ows_dynamicTypeSubheadline - } - - func configure(contact: Contact, subtitleType: SubtitleCellValue, showsWhenSelected: Bool, contactsManager: OWSContactsManager) { - - OWSTableItem.configureCell(self) - - self.contact = contact - self.showsWhenSelected = showsWhenSelected - - self.titleLabel.textColor = Theme.primaryColor - self.subtitleLabel.textColor = Theme.secondaryColor - - let cnContact = contactsManager.cnContact(withId: contact.cnContactId) - titleLabel.attributedText = cnContact?.formattedFullName(font: titleLabel.font) - updateSubtitle(subtitleType: subtitleType, contact: contact) - - if let contactImage = contactsManager.avatarImage(forCNContactId: contact.cnContactId) { - contactImageView.image = contactImage - } else { - let contactIdForDeterminingBackgroundColor: String - if let signalId = contact.parsedPhoneNumbers.first?.toE164() { - contactIdForDeterminingBackgroundColor = signalId - } else { - contactIdForDeterminingBackgroundColor = contact.fullName - } - - let avatarBuilder = OWSContactAvatarBuilder(nonSignalName: contact.fullName, - colorSeed: contactIdForDeterminingBackgroundColor, - diameter: ContactCell.kAvatarDiameter) - - contactImageView.image = avatarBuilder.build() - } - } - - func updateSubtitle(subtitleType: SubtitleCellValue, contact: Contact) { - switch subtitleType { - case .none: - assert(self.subtitleLabel.superview == nil) - break - case .phoneNumber: - self.textStackView.addArrangedSubview(self.subtitleLabel) - - if let firstPhoneNumber = contact.userTextPhoneNumbers.first { - self.subtitleLabel.text = firstPhoneNumber - } else { - self.subtitleLabel.text = NSLocalizedString("CONTACT_PICKER_NO_PHONE_NUMBERS_AVAILABLE", comment: "table cell subtitle when contact card has no known phone number") - } - case .email: - self.textStackView.addArrangedSubview(self.subtitleLabel) - - if let firstEmail = contact.emails.first { - self.subtitleLabel.text = firstEmail - } else { - self.subtitleLabel.text = NSLocalizedString("CONTACT_PICKER_NO_EMAILS_AVAILABLE", comment: "table cell subtitle when contact card has no email") - } - } - } -} - -fileprivate extension CNContact { - /** - * Bold the sorting portion of the name. e.g. if we sort by family name, bold the family name. - */ - func formattedFullName(font: UIFont) -> NSAttributedString? { - let keyToHighlight = ContactSortOrder == .familyName ? CNContactFamilyNameKey : CNContactGivenNameKey - - let boldDescriptor = font.fontDescriptor.withSymbolicTraits(.traitBold) - let boldAttributes = [ - NSAttributedString.Key.font: UIFont(descriptor: boldDescriptor!, size: 0) - ] - - if let attributedName = CNContactFormatter.attributedString(from: self, style: .fullName, defaultAttributes: nil) { - let highlightedName = attributedName.mutableCopy() as! NSMutableAttributedString - highlightedName.enumerateAttributes(in: NSRange(location: 0, length: highlightedName.length), options: [], using: { (attrs, range, _) in - if let property = attrs[NSAttributedString.Key(rawValue: CNContactPropertyAttribute)] as? String, property == keyToHighlight { - highlightedName.addAttributes(boldAttributes, range: range) - } - }) - return highlightedName - } - - if let emailAddress = self.emailAddresses.first?.value { - return NSAttributedString(string: emailAddress as String, attributes: boldAttributes) - } - - if let phoneNumber = self.phoneNumbers.first?.value.stringValue { - return NSAttributedString(string: phoneNumber, attributes: boldAttributes) - } - - return nil - } -} diff --git a/Session/Signal/ContactShareViewHelper.swift b/Session/Signal/ContactShareViewHelper.swift deleted file mode 100644 index e0dfc6774..000000000 --- a/Session/Signal/ContactShareViewHelper.swift +++ /dev/null @@ -1,215 +0,0 @@ -// -// Copyright (c) 2019 Open Whisper Systems. All rights reserved. -// - -import Foundation -import SignalUtilitiesKit -import ContactsUI -import MessageUI - -@objc -public protocol ContactShareViewHelperDelegate: class { - func didCreateOrEditContact() -} - -@objc -public class ContactShareViewHelper: NSObject, CNContactViewControllerDelegate { - - @objc - weak var delegate: ContactShareViewHelperDelegate? - - let contactsManager: OWSContactsManager - - @objc - public required init(contactsManager: OWSContactsManager) { - AssertIsOnMainThread() - - self.contactsManager = contactsManager - - super.init() - } - - // MARK: Actions - - @objc - public func sendMessage(contactShare: ContactShareViewModel, fromViewController: UIViewController) { - Logger.info("") - - presentThreadAndPeform(action: .compose, contactShare: contactShare, fromViewController: fromViewController) - } - - @objc - public func audioCall(contactShare: ContactShareViewModel, fromViewController: UIViewController) { - Logger.info("") - - presentThreadAndPeform(action: .audioCall, contactShare: contactShare, fromViewController: fromViewController) - } - - @objc - public func videoCall(contactShare: ContactShareViewModel, fromViewController: UIViewController) { - Logger.info("") - - presentThreadAndPeform(action: .videoCall, contactShare: contactShare, fromViewController: fromViewController) - } - - private func presentThreadAndPeform(action: ConversationViewAction, contactShare: ContactShareViewModel, fromViewController: UIViewController) { - // TODO: We're taking the first Signal account id. We might - // want to let the user select if there's more than one. - let phoneNumbers = contactShare.systemContactsWithSignalAccountPhoneNumbers(contactsManager) - guard phoneNumbers.count > 0 else { - owsFailDebug("missing Signal recipient id.") - return - } - guard phoneNumbers.count > 1 else { - let recipientId = phoneNumbers.first! - SignalApp.shared().presentConversation(forRecipientId: recipientId, action: action, animated: true) - return - } - - showPhoneNumberPicker(phoneNumbers: phoneNumbers, fromViewController: fromViewController, completion: { (recipientId) in - SignalApp.shared().presentConversation(forRecipientId: recipientId, action: action, animated: true) - }) - } - - @objc - public func showInviteContact(contactShare: ContactShareViewModel, fromViewController: UIViewController) { - Logger.info("") - - guard MFMessageComposeViewController.canSendText() else { - Logger.info("Device cannot send text") - OWSAlerts.showErrorAlert(message: NSLocalizedString("UNSUPPORTED_FEATURE_ERROR", comment: "")) - return - } - let phoneNumbers = contactShare.e164PhoneNumbers() - guard phoneNumbers.count > 0 else { - owsFailDebug("no phone numbers.") - return - } - - let inviteFlow = InviteFlow(presentingViewController: fromViewController) - inviteFlow.sendSMSTo(phoneNumbers: phoneNumbers) - } - - @objc - func showAddToContacts(contactShare: ContactShareViewModel, fromViewController: UIViewController) { - Logger.info("") - - let actionSheet = UIAlertController(title: nil, message: nil, preferredStyle: .actionSheet) - - actionSheet.addAction(UIAlertAction(title: NSLocalizedString("CONVERSATION_SETTINGS_NEW_CONTACT", - comment: "Label for 'new contact' button in conversation settings view."), - style: .default) { _ in - self.didPressCreateNewContact(contactShare: contactShare, fromViewController: fromViewController) - }) - actionSheet.addAction(UIAlertAction(title: NSLocalizedString("CONVERSATION_SETTINGS_ADD_TO_EXISTING_CONTACT", - comment: "Label for 'new contact' button in conversation settings view."), - style: .default) { _ in - self.didPressAddToExistingContact(contactShare: contactShare, fromViewController: fromViewController) - }) - actionSheet.addAction(OWSAlerts.cancelAction) - - fromViewController.presentAlert(actionSheet) - } - - private func showPhoneNumberPicker(phoneNumbers: [String], fromViewController: UIViewController, completion :@escaping ((String) -> Void)) { - - let actionSheet = UIAlertController(title: nil, message: nil, preferredStyle: .actionSheet) - - for phoneNumber in phoneNumbers { - actionSheet.addAction(UIAlertAction(title: PhoneNumber.bestEffortLocalizedPhoneNumber(withE164: phoneNumber), - style: .default) { _ in - completion(phoneNumber) - }) - } - actionSheet.addAction(OWSAlerts.cancelAction) - - fromViewController.presentAlert(actionSheet) - } - - func didPressCreateNewContact(contactShare: ContactShareViewModel, fromViewController: UIViewController) { - Logger.info("") - - presentNewContactView(contactShare: contactShare, fromViewController: fromViewController) - } - - func didPressAddToExistingContact(contactShare: ContactShareViewModel, fromViewController: UIViewController) { - Logger.info("") - - presentSelectAddToExistingContactView(contactShare: contactShare, fromViewController: fromViewController) - } - - // MARK: - - - private func presentNewContactView(contactShare: ContactShareViewModel, fromViewController: UIViewController) { - guard contactsManager.supportsContactEditing else { - owsFailDebug("Contact editing not supported") - return - } - - guard let systemContact = OWSContacts.systemContact(for: contactShare.dbRecord, imageData: contactShare.avatarImageData) else { - owsFailDebug("Could not derive system contact.") - return - } - - guard contactsManager.isSystemContactsAuthorized else { - ContactsViewHelper.presentMissingContactAccessAlertController(from: fromViewController) - return - } - - let contactViewController = CNContactViewController(forNewContact: systemContact) - contactViewController.delegate = self - contactViewController.allowsActions = false - contactViewController.allowsEditing = true - contactViewController.navigationItem.leftBarButtonItem = UIBarButtonItem(title: CommonStrings.cancelButton, - style: .plain, - target: self, - action: #selector(didFinishEditingContact)) - - let modal = OWSNavigationController(rootViewController: contactViewController) - fromViewController.present(modal, animated: true) - } - - private func presentSelectAddToExistingContactView(contactShare: ContactShareViewModel, fromViewController: UIViewController) { - guard contactsManager.supportsContactEditing else { - owsFailDebug("Contact editing not supported") - return - } - - guard contactsManager.isSystemContactsAuthorized else { - ContactsViewHelper.presentMissingContactAccessAlertController(from: fromViewController) - return - } - - guard let navigationController = fromViewController.navigationController else { - owsFailDebug("missing navigationController") - return - } - - let viewController = AddContactShareToExistingContactViewController(contactShare: contactShare) - navigationController.pushViewController(viewController, animated: true) - } - - // MARK: - CNContactViewControllerDelegate - - @objc public func contactViewController(_ viewController: CNContactViewController, didCompleteWith contact: CNContact?) { - Logger.info("") - - guard let delegate = delegate else { - owsFailDebug("missing delegate") - return - } - - delegate.didCreateOrEditContact() - } - - @objc public func didFinishEditingContact() { - Logger.info("") - - guard let delegate = delegate else { - owsFailDebug("missing delegate") - return - } - - delegate.didCreateOrEditContact() - } -} diff --git a/Session/Signal/ContactViewController.swift b/Session/Signal/ContactViewController.swift deleted file mode 100644 index bc2e23836..000000000 --- a/Session/Signal/ContactViewController.swift +++ /dev/null @@ -1,679 +0,0 @@ -// -// Copyright (c) 2019 Open Whisper Systems. All rights reserved. -// - -import Foundation -import SignalUtilitiesKit -import SignalUtilitiesKit -import Reachability -import ContactsUI -import MessageUI - -class ContactViewController: OWSViewController, ContactShareViewHelperDelegate { - - enum ContactViewMode { - case systemContactWithSignal, - systemContactWithoutSignal, - nonSystemContact, - noPhoneNumber, - unknown - } - - private var hasLoadedView = false - - private var viewMode = ContactViewMode.unknown { - didSet { - AssertIsOnMainThread() - - if oldValue != viewMode && hasLoadedView { - updateContent() - } - } - } - - private let contactsManager: OWSContactsManager - - private var reachability: Reachability? - - private let contactShare: ContactShareViewModel - - private var contactShareViewHelper: ContactShareViewHelper - - private weak var postDismissNavigationController: UINavigationController? - - // MARK: - Initializers - - @available(*, unavailable, message: "use init(call:) constructor instead.") - required init?(coder aDecoder: NSCoder) { - notImplemented() - } - - @objc - required init(contactShare: ContactShareViewModel) { - contactsManager = Environment.shared.contactsManager - self.contactShare = contactShare - self.contactShareViewHelper = ContactShareViewHelper(contactsManager: contactsManager) - - super.init(nibName: nil, bundle: nil) - - contactShareViewHelper.delegate = self - - updateMode() - - NotificationCenter.default.addObserver(forName: .OWSContactsManagerSignalAccountsDidChange, object: nil, queue: nil) { [weak self] _ in - guard let strongSelf = self else { return } - strongSelf.updateMode() - } - - reachability = Reachability.forInternetConnection() - - NotificationCenter.default.addObserver(forName: .reachabilityChanged, object: nil, queue: nil) { [weak self] _ in - guard let strongSelf = self else { return } - strongSelf.updateMode() - } - } - - // MARK: - View Lifecycle - - override func viewWillAppear(_ animated: Bool) { - super.viewWillAppear(animated) - - guard let navigationController = self.navigationController else { - owsFailDebug("navigationController was unexpectedly nil") - return - } - - // self.navigationController is nil in viewWillDisappear when transition via message/call buttons - // so we maintain our own reference to restore the navigation bars. - postDismissNavigationController = navigationController - navigationController.isNavigationBarHidden = true - - contactsManager.requestSystemContactsOnce(completion: { [weak self] _ in - guard let strongSelf = self else { return } - strongSelf.updateMode() - }) - } - - override func viewWillDisappear(_ animated: Bool) { - super.viewWillDisappear(animated) - - if self.presentedViewController == nil { - // No need to do this when we're disappearing due to a modal presentation. - // We'll eventually return to to this view and need to hide again. But also, there is a visible - // animation glitch where the navigation bar for this view controller starts to appear while - // the whole nav stack is about to be obscured by the modal we are presenting. - guard let postDismissNavigationController = self.postDismissNavigationController else { - owsFailDebug("postDismissNavigationController was unexpectedly nil") - return - } - - postDismissNavigationController.setNavigationBarHidden(false, animated: animated) - } - } - - override func loadView() { - super.loadView() - - self.view.preservesSuperviewLayoutMargins = false - self.view.backgroundColor = heroBackgroundColor() - - updateContent() - - hasLoadedView = true - } - - private func updateMode() { - AssertIsOnMainThread() - - guard contactShare.e164PhoneNumbers().count > 0 else { - viewMode = .noPhoneNumber - return - } - if systemContactsWithSignalAccountsForContact().count > 0 { - viewMode = .systemContactWithSignal - return - } - if systemContactsForContact().count > 0 { - viewMode = .systemContactWithoutSignal - return - } - - viewMode = .nonSystemContact - } - - private func systemContactsWithSignalAccountsForContact() -> [String] { - AssertIsOnMainThread() - - return contactShare.systemContactsWithSignalAccountPhoneNumbers(contactsManager) - } - - private func systemContactsForContact() -> [String] { - AssertIsOnMainThread() - - return contactShare.systemContactPhoneNumbers(contactsManager) - } - - private func updateContent() { - AssertIsOnMainThread() - - guard let rootView = self.view else { - owsFailDebug("missing root view.") - return - } - - for subview in rootView.subviews { - subview.removeFromSuperview() - } - - let topView = createTopView() - rootView.addSubview(topView) - topView.autoPinEdge(.top, to: .top, of: view) - topView.autoPinWidthToSuperview() - - // This view provides a background "below the fold". - let bottomView = UIView.container() - bottomView.backgroundColor = Theme.backgroundColor - self.view.addSubview(bottomView) - bottomView.layoutMargins = .zero - bottomView.autoPinWidthToSuperview() - bottomView.autoPinEdge(.top, to: .bottom, of: topView) - bottomView.autoPinEdge(toSuperviewEdge: .bottom) - - let scrollView = UIScrollView() - scrollView.preservesSuperviewLayoutMargins = false - self.view.addSubview(scrollView) - scrollView.layoutMargins = .zero - scrollView.autoPinWidthToSuperview() - scrollView.autoPinEdge(.top, to: .bottom, of: topView) - scrollView.autoPinEdge(toSuperviewEdge: .bottom) - - let fieldsView = createFieldsView() - - scrollView.addSubview(fieldsView) - fieldsView.autoPinLeadingToSuperviewMargin() - fieldsView.autoPinTrailingToSuperviewMargin() - fieldsView.autoPinEdge(toSuperviewEdge: .top) - fieldsView.autoPinEdge(toSuperviewEdge: .bottom) - } - - private func heroBackgroundColor() -> UIColor { - return (Theme.isDarkThemeEnabled - ? UIColor(rgbHex: 0x272727) - : UIColor(rgbHex: 0xefeff4)) - } - - private func createTopView() -> UIView { - AssertIsOnMainThread() - - let topView = UIView.container() - topView.backgroundColor = heroBackgroundColor() - topView.preservesSuperviewLayoutMargins = false - - // Back Button - let backButtonSize = CGFloat(50) - let backButton = TappableView(actionBlock: { [weak self] in - guard let strongSelf = self else { return } - strongSelf.didPressDismiss() - }) - backButton.autoSetDimension(.width, toSize: backButtonSize) - backButton.autoSetDimension(.height, toSize: backButtonSize) - topView.addSubview(backButton) - backButton.autoPinEdge(toSuperviewEdge: .top) - backButton.autoPinLeadingToSuperviewMargin() - - let backIconName = (CurrentAppContext().isRTL ? "system_disclosure_indicator" : "system_disclosure_indicator_rtl") - guard let backIconImage = UIImage(named: backIconName) else { - owsFailDebug("missing icon.") - return topView - } - let backIconView = UIImageView(image: backIconImage.withRenderingMode(.alwaysTemplate)) - backIconView.contentMode = .scaleAspectFit - backIconView.tintColor = Theme.primaryColor.withAlphaComponent(0.6) - backButton.addSubview(backIconView) - backIconView.autoCenterInSuperview() - - let avatarSize: CGFloat = 100 - let avatarView = AvatarImageView() - avatarView.image = contactShare.getAvatarImage(diameter: avatarSize, contactsManager: contactsManager) - topView.addSubview(avatarView) - avatarView.autoPinEdge(toSuperviewEdge: .top, withInset: 20) - avatarView.autoHCenterInSuperview() - avatarView.autoSetDimension(.width, toSize: avatarSize) - avatarView.autoSetDimension(.height, toSize: avatarSize) - - let nameLabel = UILabel() - nameLabel.text = contactShare.displayName - nameLabel.font = UIFont.ows_dynamicTypeTitle1 - nameLabel.textColor = Theme.primaryColor - nameLabel.lineBreakMode = .byTruncatingTail - nameLabel.textAlignment = .center - topView.addSubview(nameLabel) - nameLabel.autoPinEdge(.top, to: .bottom, of: avatarView, withOffset: 10) - nameLabel.autoPinLeadingToSuperviewMargin(withInset: hMargin) - nameLabel.autoPinTrailingToSuperviewMargin(withInset: hMargin) - - var lastView: UIView = nameLabel - - for phoneNumber in systemContactsWithSignalAccountsForContact() { - let phoneNumberLabel = UILabel() - phoneNumberLabel.text = PhoneNumber.bestEffortLocalizedPhoneNumber(withE164: phoneNumber) - phoneNumberLabel.font = UIFont.ows_dynamicTypeFootnote - phoneNumberLabel.textColor = Theme.primaryColor - phoneNumberLabel.lineBreakMode = .byTruncatingTail - phoneNumberLabel.textAlignment = .center - topView.addSubview(phoneNumberLabel) - phoneNumberLabel.autoPinEdge(.top, to: .bottom, of: lastView, withOffset: 5) - phoneNumberLabel.autoPinLeadingToSuperviewMargin(withInset: hMargin) - phoneNumberLabel.autoPinTrailingToSuperviewMargin(withInset: hMargin) - lastView = phoneNumberLabel - } - - switch viewMode { - case .systemContactWithSignal: - // Show actions buttons for system contacts with a Signal account. - let stackView = UIStackView() - stackView.axis = .horizontal - stackView.distribution = .fillEqually - stackView.addArrangedSubview(createCircleActionButton(text: NSLocalizedString("ACTION_SEND_MESSAGE", - comment: "Label for 'send message' button in contact view."), - imageName: "contact_view_message", - actionBlock: { [weak self] in - guard let strongSelf = self else { return } - strongSelf.didPressSendMessage() - })) - stackView.addArrangedSubview(createCircleActionButton(text: NSLocalizedString("ACTION_AUDIO_CALL", - comment: "Label for 'audio call' button in contact view."), - imageName: "contact_view_audio_call", - actionBlock: { [weak self] in - guard let strongSelf = self else { return } - strongSelf.didPressAudioCall() - })) - stackView.addArrangedSubview(createCircleActionButton(text: NSLocalizedString("ACTION_VIDEO_CALL", - comment: "Label for 'video call' button in contact view."), - imageName: "contact_view_video_call", - actionBlock: { [weak self] in - guard let strongSelf = self else { return } - strongSelf.didPressVideoCall() - })) - topView.addSubview(stackView) - stackView.autoPinEdge(.top, to: .bottom, of: lastView, withOffset: 20) - stackView.autoPinLeadingToSuperviewMargin(withInset: hMargin) - stackView.autoPinTrailingToSuperviewMargin(withInset: hMargin) - lastView = stackView - case .systemContactWithoutSignal: - // Show invite button for system contacts without a Signal account. - let inviteButton = createLargePillButton(text: NSLocalizedString("ACTION_INVITE", - comment: "Label for 'invite' button in contact view."), - actionBlock: { [weak self] in - guard let strongSelf = self else { return } - strongSelf.didPressInvite() - }) - topView.addSubview(inviteButton) - inviteButton.autoPinEdge(.top, to: .bottom, of: lastView, withOffset: 20) - inviteButton.autoPinLeadingToSuperviewMargin(withInset: 55) - inviteButton.autoPinTrailingToSuperviewMargin(withInset: 55) - lastView = inviteButton - case .nonSystemContact: - // Show no action buttons for non-system contacts. - break - case .noPhoneNumber: - // Show no action buttons for contacts without a phone number. - break - case .unknown: - let activityIndicator = UIActivityIndicatorView(style: .whiteLarge) - topView.addSubview(activityIndicator) - activityIndicator.autoPinEdge(.top, to: .bottom, of: lastView, withOffset: 10) - activityIndicator.autoHCenterInSuperview() - lastView = activityIndicator - break - } - - // Always show "add to contacts" button. - let addToContactsButton = createLargePillButton(text: NSLocalizedString("CONVERSATION_VIEW_ADD_TO_CONTACTS_OFFER", - comment: "Message shown in conversation view that offers to add an unknown user to your phone's contacts."), - actionBlock: { [weak self] in - guard let strongSelf = self else { return } - strongSelf.didPressAddToContacts() - }) - topView.addSubview(addToContactsButton) - addToContactsButton.autoPinEdge(.top, to: .bottom, of: lastView, withOffset: 20) - addToContactsButton.autoPinLeadingToSuperviewMargin(withInset: 55) - addToContactsButton.autoPinTrailingToSuperviewMargin(withInset: 55) - lastView = addToContactsButton - - lastView.autoPinEdge(toSuperviewEdge: .bottom, withInset: 15) - - return topView - } - - private func createFieldsView() -> UIView { - AssertIsOnMainThread() - - var rows = [UIView]() - - // TODO: Not designed yet. -// if viewMode == .systemContactWithSignal || -// viewMode == .systemContactWithoutSignal { -// addRow(createActionRow(labelText:NSLocalizedString("ACTION_SHARE_CONTACT", -// comment:"Label for 'share contact' button."), -// action:#selector(didPressShareContact))) -// } - - if let organizationName = contactShare.name.organizationName?.ows_stripped() { - if (contactShare.name.hasAnyNamePart() && - organizationName.count > 0) { - rows.append(ContactFieldView.contactFieldView(forOrganizationName: organizationName, - layoutMargins: UIEdgeInsets(top: 5, left: hMargin, bottom: 5, right: hMargin))) - } - } - - for phoneNumber in contactShare.phoneNumbers { - rows.append(ContactFieldView.contactFieldView(forPhoneNumber: phoneNumber, - layoutMargins: UIEdgeInsets(top: 5, left: hMargin, bottom: 5, right: hMargin), - actionBlock: { [weak self] in - guard let strongSelf = self else { return } - strongSelf.didPressPhoneNumber(phoneNumber: phoneNumber) - })) - } - - for email in contactShare.emails { - rows.append(ContactFieldView.contactFieldView(forEmail: email, - layoutMargins: UIEdgeInsets(top: 5, left: hMargin, bottom: 5, right: hMargin), - actionBlock: { [weak self] in - guard let strongSelf = self else { return } - strongSelf.didPressEmail(email: email) - })) - } - - for address in contactShare.addresses { - rows.append(ContactFieldView.contactFieldView(forAddress: address, - layoutMargins: UIEdgeInsets(top: 5, left: hMargin, bottom: 5, right: hMargin), - actionBlock: { [weak self] in - guard let strongSelf = self else { return } - strongSelf.didPressAddress(address: address) - })) - } - - return ContactFieldView(rows: rows, hMargin: hMargin) - } - - private let hMargin = CGFloat(16) - - private func createActionRow(labelText: String, action: Selector) -> UIView { - let row = UIView() - row.layoutMargins.left = 0 - row.layoutMargins.right = 0 - row.isUserInteractionEnabled = true - row.addGestureRecognizer(UITapGestureRecognizer(target: self, action: action)) - - let label = UILabel() - label.text = labelText - label.font = UIFont.ows_dynamicTypeBody - label.textColor = UIColor.ows_materialBlue - label.lineBreakMode = .byTruncatingTail - row.addSubview(label) - label.autoPinTopToSuperviewMargin() - label.autoPinBottomToSuperviewMargin() - label.autoPinLeadingToSuperviewMargin(withInset: hMargin) - label.autoPinTrailingToSuperviewMargin(withInset: hMargin) - - return row - } - - // TODO: Use real assets. - private func createCircleActionButton(text: String, imageName: String, actionBlock : @escaping () -> Void) -> UIView { - let buttonSize = CGFloat(50) - - let button = TappableView(actionBlock: actionBlock) - button.layoutMargins = .zero - button.autoSetDimension(.width, toSize: buttonSize, relation: .greaterThanOrEqual) - - let circleView = UIView() - circleView.backgroundColor = Theme.backgroundColor - circleView.autoSetDimension(.width, toSize: buttonSize) - circleView.autoSetDimension(.height, toSize: buttonSize) - circleView.layer.cornerRadius = buttonSize * 0.5 - button.addSubview(circleView) - circleView.autoPinEdge(toSuperviewEdge: .top) - circleView.autoHCenterInSuperview() - - guard let image = UIImage(named: imageName) else { - owsFailDebug("missing image.") - return button - } - let imageView = UIImageView(image: image.withRenderingMode(.alwaysTemplate)) - imageView.tintColor = Theme.primaryColor.withAlphaComponent(0.6) - circleView.addSubview(imageView) - imageView.autoCenterInSuperview() - - let label = UILabel() - label.text = text - label.font = UIFont.ows_dynamicTypeCaption2 - label.textColor = Theme.primaryColor - label.lineBreakMode = .byTruncatingTail - label.textAlignment = .center - button.addSubview(label) - label.autoPinEdge(.top, to: .bottom, of: circleView, withOffset: 3) - label.autoPinEdge(toSuperviewEdge: .bottom) - label.autoPinLeadingToSuperviewMargin() - label.autoPinTrailingToSuperviewMargin() - - return button - } - - private func createLargePillButton(text: String, actionBlock : @escaping () -> Void) -> UIView { - let button = TappableView(actionBlock: actionBlock) - button.backgroundColor = Theme.backgroundColor - button.layoutMargins = .zero - button.autoSetDimension(.height, toSize: 45) - button.layer.cornerRadius = 5 - - let label = UILabel() - label.text = text - label.font = UIFont.ows_dynamicTypeBody - label.textColor = UIColor.ows_materialBlue - label.lineBreakMode = .byTruncatingTail - label.textAlignment = .center - button.addSubview(label) - label.autoPinLeadingToSuperviewMargin(withInset: 20) - label.autoPinTrailingToSuperviewMargin(withInset: 20) - label.autoVCenterInSuperview() - label.autoPinEdge(toSuperviewEdge: .top, withInset: 0, relation: .greaterThanOrEqual) - label.autoPinEdge(toSuperviewEdge: .bottom, withInset: 0, relation: .greaterThanOrEqual) - - return button - } - - func didPressShareContact(sender: UIGestureRecognizer) { - Logger.info("") - - guard sender.state == .recognized else { - return - } - // TODO: - } - - func didPressSendMessage() { - Logger.info("") - - self.contactShareViewHelper.sendMessage(contactShare: self.contactShare, fromViewController: self) - } - - func didPressAudioCall() { - Logger.info("") - - self.contactShareViewHelper.audioCall(contactShare: self.contactShare, fromViewController: self) - } - - func didPressVideoCall() { - Logger.info("") - - self.contactShareViewHelper.videoCall(contactShare: self.contactShare, fromViewController: self) - } - - func didPressInvite() { - Logger.info("") - - self.contactShareViewHelper.showInviteContact(contactShare: self.contactShare, fromViewController: self) - } - - func didPressAddToContacts() { - Logger.info("") - - self.contactShareViewHelper.showAddToContacts(contactShare: self.contactShare, fromViewController: self) - } - - func didPressDismiss() { - Logger.info("") - - guard let navigationController = self.navigationController else { - owsFailDebug("navigationController was unexpectedly nil") - return - } - - navigationController.popViewController(animated: true) - } - - func didPressPhoneNumber(phoneNumber: OWSContactPhoneNumber) { - Logger.info("") - - let actionSheet = UIAlertController(title: nil, message: nil, preferredStyle: .actionSheet) - - if let e164 = phoneNumber.tryToConvertToE164() { - if contactShare.systemContactsWithSignalAccountPhoneNumbers(contactsManager).contains(e164) { - actionSheet.addAction(UIAlertAction(title: NSLocalizedString("ACTION_SEND_MESSAGE", - comment: "Label for 'send message' button in contact view."), - style: .default) { _ in - SignalApp.shared().presentConversation(forRecipientId: e164, action: .compose, animated: true) - }) - actionSheet.addAction(UIAlertAction(title: NSLocalizedString("ACTION_AUDIO_CALL", - comment: "Label for 'audio call' button in contact view."), - style: .default) { _ in - SignalApp.shared().presentConversation(forRecipientId: e164, action: .audioCall, animated: true) - }) - actionSheet.addAction(UIAlertAction(title: NSLocalizedString("ACTION_VIDEO_CALL", - comment: "Label for 'video call' button in contact view."), - style: .default) { _ in - SignalApp.shared().presentConversation(forRecipientId: e164, action: .videoCall, animated: true) - }) - } else { - // TODO: We could offer callPhoneNumberWithSystemCall. - } - } - actionSheet.addAction(UIAlertAction(title: NSLocalizedString("EDIT_ITEM_COPY_ACTION", - comment: "Short name for edit menu item to copy contents of media message."), - style: .default) { _ in - UIPasteboard.general.string = phoneNumber.phoneNumber - }) - actionSheet.addAction(OWSAlerts.cancelAction) - presentAlert(actionSheet) - } - - func callPhoneNumberWithSystemCall(phoneNumber: OWSContactPhoneNumber) { - Logger.info("") - - guard let url = NSURL(string: "tel:\(phoneNumber.phoneNumber)") else { - owsFailDebug("could not open phone number.") - return - } - UIApplication.shared.openURL(url as URL) - } - - func didPressEmail(email: OWSContactEmail) { - Logger.info("") - - let actionSheet = UIAlertController(title: nil, message: nil, preferredStyle: .actionSheet) - actionSheet.addAction(UIAlertAction(title: NSLocalizedString("CONTACT_VIEW_OPEN_EMAIL_IN_EMAIL_APP", - comment: "Label for 'open email in email app' button in contact view."), - style: .default) { [weak self] _ in - self?.openEmailInEmailApp(email: email) - }) - actionSheet.addAction(UIAlertAction(title: NSLocalizedString("EDIT_ITEM_COPY_ACTION", - comment: "Short name for edit menu item to copy contents of media message."), - style: .default) { _ in - UIPasteboard.general.string = email.email - }) - actionSheet.addAction(OWSAlerts.cancelAction) - presentAlert(actionSheet) - } - - func openEmailInEmailApp(email: OWSContactEmail) { - Logger.info("") - - guard let url = NSURL(string: "mailto:\(email.email)") else { - owsFailDebug("could not open email.") - return - } - UIApplication.shared.openURL(url as URL) - } - - func didPressAddress(address: OWSContactAddress) { - Logger.info("") - - let actionSheet = UIAlertController(title: nil, message: nil, preferredStyle: .actionSheet) - actionSheet.addAction(UIAlertAction(title: NSLocalizedString("CONTACT_VIEW_OPEN_ADDRESS_IN_MAPS_APP", - comment: "Label for 'open address in maps app' button in contact view."), - style: .default) { [weak self] _ in - self?.openAddressInMaps(address: address) - }) - actionSheet.addAction(UIAlertAction(title: NSLocalizedString("EDIT_ITEM_COPY_ACTION", - comment: "Short name for edit menu item to copy contents of media message."), - style: .default) { [weak self] _ in - guard let strongSelf = self else { return } - - UIPasteboard.general.string = strongSelf.formatAddressForQuery(address: address) - }) - actionSheet.addAction(OWSAlerts.cancelAction) - presentAlert(actionSheet) - } - - func openAddressInMaps(address: OWSContactAddress) { - Logger.info("") - - let mapAddress = formatAddressForQuery(address: address) - guard let escapedMapAddress = mapAddress.addingPercentEncoding(withAllowedCharacters: .urlQueryAllowed) else { - owsFailDebug("could not open address.") - return - } - // Note that we use "q" (i.e. query) rather than "address" since we can't assume - // this is a well-formed address. - guard let url = URL(string: "http://maps.apple.com/?q=\(escapedMapAddress)") else { - owsFailDebug("could not open address.") - return - } - - UIApplication.shared.openURL(url as URL) - } - - func formatAddressForQuery(address: OWSContactAddress) -> String { - Logger.info("") - - // Open address in Apple Maps app. - var addressParts = [String]() - let addAddressPart: ((String?) -> Void) = { (part) in - guard let part = part else { - return - } - guard part.count > 0 else { - return - } - addressParts.append(part) - } - addAddressPart(address.street) - addAddressPart(address.neighborhood) - addAddressPart(address.city) - addAddressPart(address.region) - addAddressPart(address.postcode) - addAddressPart(address.country) - return addressParts.joined(separator: ", ") - } - - // MARK: - ContactShareViewHelperDelegate - - public func didCreateOrEditContact() { - Logger.info("") - updateContent() - - self.dismiss(animated: true) - } -} diff --git a/Session/Signal/ContactsPicker.swift b/Session/Signal/ContactsPicker.swift deleted file mode 100644 index a2a3b2c98..000000000 --- a/Session/Signal/ContactsPicker.swift +++ /dev/null @@ -1,404 +0,0 @@ -// -// Copyright (c) 2019 Open Whisper Systems. All rights reserved. -// - -// Originally based on EPContacts -// -// Created by Prabaharan Elangovan on 12/10/15. -// Parts Copyright © 2015 Prabaharan Elangovan. All rights reserved - -import UIKit -import Contacts -import SignalUtilitiesKit - -@objc -public protocol ContactsPickerDelegate: class { - func contactsPicker(_: ContactsPicker, contactFetchDidFail error: NSError) - func contactsPickerDidCancel(_: ContactsPicker) - func contactsPicker(_: ContactsPicker, didSelectContact contact: Contact) - func contactsPicker(_: ContactsPicker, didSelectMultipleContacts contacts: [Contact]) - func contactsPicker(_: ContactsPicker, shouldSelectContact contact: Contact) -> Bool -} - -@objc -public enum SubtitleCellValue: Int { - case phoneNumber, email, none -} - -@objc -public class ContactsPicker: OWSViewController, UITableViewDelegate, UITableViewDataSource, UISearchBarDelegate { - - var tableView: UITableView! - var searchBar: UISearchBar! - - // MARK: - Properties - - private let contactCellReuseIdentifier = "contactCellReuseIdentifier" - - private var contactsManager: OWSContactsManager { - return Environment.shared.contactsManager - } - - // HACK: Though we don't have an input accessory view, the VC we are presented above (ConversationVC) does. - // If the app is backgrounded and then foregrounded, when OWSWindowManager calls mainWindow.makeKeyAndVisible - // the ConversationVC's inputAccessoryView will appear *above* us unless we'd previously become first responder. - override public var canBecomeFirstResponder: Bool { - Logger.debug("") - return true - } - - override public func becomeFirstResponder() -> Bool { - Logger.debug("") - return super.becomeFirstResponder() - } - - override public func resignFirstResponder() -> Bool { - Logger.debug("") - return super.resignFirstResponder() - } - - private let collation = UILocalizedIndexedCollation.current() - public var collationForTests: UILocalizedIndexedCollation { - get { - return collation - } - } - private let contactStore = CNContactStore() - - // Data Source State - private lazy var sections = [[CNContact]]() - private lazy var filteredSections = [[CNContact]]() - private lazy var selectedContacts = [Contact]() - - // Configuration - @objc - public weak var contactsPickerDelegate: ContactsPickerDelegate? - private let subtitleCellType: SubtitleCellValue - private let allowsMultipleSelection: Bool - private let allowedContactKeys: [CNKeyDescriptor] = ContactsFrameworkContactStoreAdaptee.allowedContactKeys - - // MARK: - Initializers - - @objc - required public init(allowsMultipleSelection: Bool, subtitleCellType: SubtitleCellValue) { - self.allowsMultipleSelection = allowsMultipleSelection - self.subtitleCellType = subtitleCellType - super.init(nibName: nil, bundle: nil) - } - - required public init?(coder aDecoder: NSCoder) { - notImplemented() - } - - // MARK: - Lifecycle Methods - - override public func loadView() { - self.view = UIView() - let tableView = UITableView() - self.tableView = tableView - self.tableView.separatorColor = Theme.cellSeparatorColor - - view.addSubview(tableView) - tableView.autoPinEdge(toSuperviewEdge: .top) - tableView.autoPinEdge(toSuperviewEdge: .bottom) - tableView.autoPinEdge(toSuperviewSafeArea: .leading) - tableView.autoPinEdge(toSuperviewSafeArea: .trailing) - tableView.delegate = self - tableView.dataSource = self - - let searchBar = OWSSearchBar() - self.searchBar = searchBar - searchBar.delegate = self - searchBar.sizeToFit() - - tableView.tableHeaderView = searchBar - } - - override open func viewDidLoad() { - super.viewDidLoad() - - self.view.backgroundColor = Theme.backgroundColor - self.tableView.backgroundColor = Theme.backgroundColor - - searchBar.placeholder = NSLocalizedString("INVITE_FRIENDS_PICKER_SEARCHBAR_PLACEHOLDER", comment: "Search") - - // Auto size cells for dynamic type - tableView.estimatedRowHeight = 60.0 - tableView.rowHeight = UITableView.automaticDimension - tableView.estimatedRowHeight = 60 - - tableView.allowsMultipleSelection = allowsMultipleSelection - - tableView.separatorInset = UIEdgeInsets(top: 0, left: ContactCell.kSeparatorHInset, bottom: 0, right: 16) - - registerContactCell() - initializeBarButtons() - reloadContacts() - updateSearchResults(searchText: "") - - NotificationCenter.default.addObserver(self, selector: #selector(self.didChangePreferredContentSize), name: UIContentSizeCategory.didChangeNotification, object: nil) - } - - @objc - public func didChangePreferredContentSize() { - self.tableView.reloadData() - } - - private func initializeBarButtons() { - let cancelButton = UIBarButtonItem(barButtonSystemItem: .cancel, target: self, action: #selector(onTouchCancelButton)) - self.navigationItem.leftBarButtonItem = cancelButton - - if allowsMultipleSelection { - let doneButton = UIBarButtonItem(barButtonSystemItem: .done, target: self, action: #selector(onTouchDoneButton)) - self.navigationItem.rightBarButtonItem = doneButton - } - } - - private func registerContactCell() { - tableView.register(ContactCell.self, forCellReuseIdentifier: contactCellReuseIdentifier) - } - - // MARK: - Contact Operations - - private func reloadContacts() { - getContacts( onError: { error in - Logger.error("failed to reload contacts with error:\(error)") - }) - } - - private func getContacts(onError errorHandler: @escaping (_ error: Error) -> Void) { - switch CNContactStore.authorizationStatus(for: CNEntityType.contacts) { - case CNAuthorizationStatus.denied, CNAuthorizationStatus.restricted: - let title = NSLocalizedString("INVITE_FLOW_REQUIRES_CONTACT_ACCESS_TITLE", comment: "Alert title when contacts disabled while trying to invite contacts to signal") - let body = NSLocalizedString("INVITE_FLOW_REQUIRES_CONTACT_ACCESS_BODY", comment: "Alert body when contacts disabled while trying to invite contacts to signal") - - let alert = UIAlertController(title: title, message: body, preferredStyle: .alert) - - let dismissText = CommonStrings.cancelButton - - let cancelAction = UIAlertAction(title: dismissText, style: .cancel, handler: { _ in - let error = NSError(domain: "contactsPickerErrorDomain", code: 1, userInfo: [NSLocalizedDescriptionKey: "No Contacts Access"]) - self.contactsPickerDelegate?.contactsPicker(self, contactFetchDidFail: error) - errorHandler(error) - }) - alert.addAction(cancelAction) - - let settingsText = CommonStrings.openSettingsButton - let openSettingsAction = UIAlertAction(title: settingsText, style: .default, handler: { (_) in - UIApplication.shared.openSystemSettings() - }) - alert.addAction(openSettingsAction) - - self.presentAlert(alert) - - case CNAuthorizationStatus.notDetermined: - //This case means the user is prompted for the first time for allowing contacts - contactStore.requestAccess(for: CNEntityType.contacts) { (granted, error) -> Void in - //At this point an alert is provided to the user to provide access to contacts. This will get invoked if a user responds to the alert - if granted { - self.getContacts(onError: errorHandler) - } else { - errorHandler(error!) - } - } - - case CNAuthorizationStatus.authorized: - //Authorization granted by user for this app. - var contacts = [CNContact]() - - do { - let contactFetchRequest = CNContactFetchRequest(keysToFetch: allowedContactKeys) - contactFetchRequest.sortOrder = .userDefault - try contactStore.enumerateContacts(with: contactFetchRequest) { (contact, _) -> Void in - contacts.append(contact) - } - self.sections = collatedContacts(contacts) - } catch let error as NSError { - Logger.error("Failed to fetch contacts with error:\(error)") - } - } - } - - func collatedContacts(_ contacts: [CNContact]) -> [[CNContact]] { - let selector: Selector = #selector(getter: CNContact.nameForCollating) - - var collated = Array(repeating: [CNContact](), count: collation.sectionTitles.count) - for contact in contacts { - let sectionNumber = collation.section(for: contact, collationStringSelector: selector) - collated[sectionNumber].append(contact) - } - return collated - } - - // MARK: - Table View DataSource - - open func numberOfSections(in tableView: UITableView) -> Int { - return self.collation.sectionTitles.count - } - - open func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { - let dataSource = filteredSections - - guard section < dataSource.count else { - return 0 - } - - return dataSource[section].count - } - - // MARK: - Table View Delegates - - open func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { - guard let cell = tableView.dequeueReusableCell(withIdentifier: contactCellReuseIdentifier, for: indexPath) as? ContactCell else { - owsFailDebug("cell had unexpected type") - return UITableViewCell() - } - - let dataSource = filteredSections - let cnContact = dataSource[indexPath.section][indexPath.row] - let contact = Contact(systemContact: cnContact) - - cell.configure(contact: contact, subtitleType: subtitleCellType, showsWhenSelected: self.allowsMultipleSelection, contactsManager: self.contactsManager) - let isSelected = selectedContacts.contains(where: { $0.uniqueId == contact.uniqueId }) - cell.isSelected = isSelected - - // Make sure we preserve selection across tableView.reloadData which happens when toggling between - // search controller - if (isSelected) { - self.tableView.selectRow(at: indexPath, animated: false, scrollPosition: .none) - } else { - self.tableView.deselectRow(at: indexPath, animated: false) - } - - return cell - } - - open func tableView(_ tableView: UITableView, didDeselectRowAt indexPath: IndexPath) { - let cell = tableView.cellForRow(at: indexPath) as! ContactCell - let deselectedContact = cell.contact! - - selectedContacts = selectedContacts.filter { - return $0.uniqueId != deselectedContact.uniqueId - } - } - - open func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) { - Logger.verbose("") - - let cell = tableView.cellForRow(at: indexPath) as! ContactCell - let selectedContact = cell.contact! - - guard (contactsPickerDelegate == nil || contactsPickerDelegate!.contactsPicker(self, shouldSelectContact: selectedContact)) else { - self.tableView.deselectRow(at: indexPath, animated: false) - return - } - - selectedContacts.append(selectedContact) - - if !allowsMultipleSelection { - // Single selection code - self.contactsPickerDelegate?.contactsPicker(self, didSelectContact: selectedContact) - } - } - - open func tableView(_ tableView: UITableView, sectionForSectionIndexTitle title: String, at index: Int) -> Int { - return collation.section(forSectionIndexTitle: index) - } - - open func sectionIndexTitles(for tableView: UITableView) -> [String]? { - return collation.sectionIndexTitles - } - - open func tableView(_ tableView: UITableView, titleForHeaderInSection section: Int) -> String? { - let dataSource = filteredSections - - guard section < dataSource.count else { - return nil - } - - // Don't show empty sections - if dataSource[section].count > 0 { - guard section < collation.sectionTitles.count else { - return nil - } - - return collation.sectionTitles[section] - } else { - return nil - } - } - - // MARK: - Button Actions - - @objc func onTouchCancelButton() { - contactsPickerDelegate?.contactsPickerDidCancel(self) - } - - @objc func onTouchDoneButton() { - contactsPickerDelegate?.contactsPicker(self, didSelectMultipleContacts: selectedContacts) - } - - // MARK: - Search Actions - open func searchBar(_ searchBar: UISearchBar, textDidChange searchText: String) { - updateSearchResults(searchText: searchText) - } - - open func updateSearchResults(searchText: String) { - let predicate: NSPredicate - if searchText.isEmpty { - filteredSections = sections - } else { - do { - predicate = CNContact.predicateForContacts(matchingName: searchText) - let filteredContacts = try contactStore.unifiedContacts(matching: predicate, keysToFetch: allowedContactKeys) - filteredSections = collatedContacts(filteredContacts) - } catch let error as NSError { - Logger.error("updating search results failed with error: \(error)") - } - } - self.tableView.reloadData() - } -} - -let ContactSortOrder = computeSortOrder() - -func computeSortOrder() -> CNContactSortOrder { - let comparator = CNContact.comparator(forNameSortOrder: .userDefault) - - let contact0 = CNMutableContact() - contact0.givenName = "A" - contact0.familyName = "Z" - - let contact1 = CNMutableContact() - contact1.givenName = "Z" - contact1.familyName = "A" - - let result = comparator(contact0, contact1) - - if result == .orderedAscending { - return .givenName - } else { - return .familyName - } -} - -fileprivate extension CNContact { - /** - * Sorting Key used by collation - */ - @objc var nameForCollating: String { - get { - if self.familyName.isEmpty && self.givenName.isEmpty { - return self.emailAddresses.first?.value as String? ?? "" - } - - let compositeName: String - if ContactSortOrder == .familyName { - compositeName = "\(self.familyName) \(self.givenName)" - } else { - compositeName = "\(self.givenName) \(self.familyName)" - } - return compositeName.trimmingCharacters(in: CharacterSet.whitespacesAndNewlines) - } - } -} diff --git a/Session/Signal/ConversationConfigurationSyncOperation.swift b/Session/Signal/ConversationConfigurationSyncOperation.swift deleted file mode 100644 index 0a47382b7..000000000 --- a/Session/Signal/ConversationConfigurationSyncOperation.swift +++ /dev/null @@ -1,97 +0,0 @@ -// -// Copyright (c) 2018 Open Whisper Systems. All rights reserved. -// - -import Foundation - -@objc -class ConversationConfigurationSyncOperation: OWSOperation { - - enum ColorSyncOperationError: Error { - case assertionError(description: String) - } - - private var dbConnection: YapDatabaseConnection { - return OWSPrimaryStorage.shared().dbReadConnection - } - - private var messageSenderJobQueue: MessageSenderJobQueue { - return SSKEnvironment.shared.messageSenderJobQueue - } - - private var contactsManager: OWSContactsManager { - return Environment.shared.contactsManager - } - - private var syncManager: OWSSyncManagerProtocol { - return SSKEnvironment.shared.syncManager - } - - private let thread: TSThread - - @objc - public init(thread: TSThread) { - self.thread = thread - super.init() - } - - override public func run() { - if let contactThread = thread as? TSContactThread { - sync(contactThread: contactThread) - } else if let groupThread = thread as? TSGroupThread { - sync(groupThread: groupThread) - } else { - self.reportAssertionError(description: "unknown thread type") - } - } - - private func reportAssertionError(description: String) { - let error = ColorSyncOperationError.assertionError(description: description) - self.reportError(error) - } - - private func sync(contactThread: TSContactThread) { - guard let signalAccount: SignalAccount = self.contactsManager.fetchSignalAccount(forRecipientId: contactThread.contactIdentifier()) else { - reportAssertionError(description: "unable to find signalAccount") - return - } - - syncManager.syncContacts(for: [signalAccount]).retainUntilComplete() - } - - private func sync(groupThread: TSGroupThread) { - // TODO sync only the affected group - // The current implementation works, but seems wasteful. - // Does desktop handle single group sync correctly? - // What does Android do? - let syncMessage: OWSSyncGroupsMessage = OWSSyncGroupsMessage(groupThread: groupThread) - - var dataSource: DataSource? - self.dbConnection.read { transaction in - guard let messageData: Data = syncMessage.buildPlainTextAttachmentData(with: transaction) else { - owsFailDebug("could not serialize sync groups data") - return - } - dataSource = DataSourceValue.dataSource(withSyncMessageData: messageData) - } - - guard let attachmentDataSource = dataSource else { - self.reportAssertionError(description: "unable to build attachment data source") - return - } - - self.sendConfiguration(attachmentDataSource: attachmentDataSource, syncMessage: syncMessage) - } - - private func sendConfiguration(attachmentDataSource: DataSource, syncMessage: OWSOutgoingSyncMessage) { - self.messageSenderJobQueue.add(mediaMessage: syncMessage, - dataSource: attachmentDataSource, - contentType: OWSMimeTypeApplicationOctetStream, - sourceFilename: nil, - caption: nil, - albumMessageId: nil, - isTemporaryAttachment: true) - self.reportSuccess() - } - -} diff --git a/Session/Signal/ConversationView/Cells/ConversationViewCell.h b/Session/Signal/ConversationView/Cells/ConversationViewCell.h index d8617b62f..8d7c7fd17 100644 --- a/Session/Signal/ConversationView/Cells/ConversationViewCell.h +++ b/Session/Signal/ConversationView/Cells/ConversationViewCell.h @@ -35,23 +35,9 @@ NS_ASSUME_NONNULL_BEGIN #pragma mark - System Cell -- (void)tappedNonBlockingIdentityChangeForRecipientId:(nullable NSString *)signalId; -- (void)tappedInvalidIdentityKeyErrorMessage:(TSInvalidIdentityKeyErrorMessage *)errorMessage; - (void)tappedCorruptedMessage:(TSErrorMessage *)message; - (void)resendGroupUpdateForErrorMessage:(TSErrorMessage *)message; -- (void)showFingerprintWithRecipientId:(NSString *)recipientId; - (void)showConversationSettings; -- (void)handleCallTap:(TSCall *)call; - -#pragma mark - Offers - -- (void)tappedUnknownContactBlockOfferMessage:(OWSContactOffersInteraction *)interaction; -- (void)tappedAddToContactsOfferMessage:(OWSContactOffersInteraction *)interaction; -- (void)tappedAddToProfileWhitelistOfferMessage:(OWSContactOffersInteraction *)interaction; - -#pragma mark - Formatting - -- (NSAttributedString *)attributedContactOrProfileNameForPhoneIdentifier:(NSString *)recipientId; #pragma mark - Caching @@ -61,10 +47,6 @@ NS_ASSUME_NONNULL_BEGIN - (void)didTapFailedOutgoingMessage:(TSOutgoingMessage *)message; -#pragma mark - Contacts - -- (OWSContactsManager *)contactsManager; - @end #pragma mark - diff --git a/Session/Signal/ConversationView/Cells/OWSContactOffersCell.h b/Session/Signal/ConversationView/Cells/OWSContactOffersCell.h deleted file mode 100644 index a06ca53e0..000000000 --- a/Session/Signal/ConversationView/Cells/OWSContactOffersCell.h +++ /dev/null @@ -1,19 +0,0 @@ -// -// Copyright (c) 2017 Open Whisper Systems. All rights reserved. -// - -#import "ConversationViewCell.h" - -NS_ASSUME_NONNULL_BEGIN - -@class OWSContactOffersInteraction; - -#pragma mark - - -@interface OWSContactOffersCell : ConversationViewCell - -+ (NSString *)cellReuseIdentifier; - -@end - -NS_ASSUME_NONNULL_END diff --git a/Session/Signal/ConversationView/Cells/OWSContactOffersCell.m b/Session/Signal/ConversationView/Cells/OWSContactOffersCell.m deleted file mode 100644 index edb01c26f..000000000 --- a/Session/Signal/ConversationView/Cells/OWSContactOffersCell.m +++ /dev/null @@ -1,263 +0,0 @@ -// -// Copyright (c) 2018 Open Whisper Systems. All rights reserved. -// - -#import "OWSContactOffersCell.h" -#import "ConversationViewItem.h" -#import "Session-Swift.h" -#import -#import -#import -#import - -NS_ASSUME_NONNULL_BEGIN - -@interface OWSContactOffersCell () - -@property (nonatomic) UILabel *titleLabel; -@property (nonatomic) UIButton *addToContactsButton; -@property (nonatomic) UIButton *addToProfileWhitelistButton; -@property (nonatomic) UIButton *blockButton; -@property (nonatomic) NSArray *layoutConstraints; -@property (nonatomic) UIStackView *stackView; -@property (nonatomic) UIStackView *buttonStackView; - -@end - -#pragma mark - - -@implementation OWSContactOffersCell - -// `[UIView init]` invokes `[self initWithFrame:...]`. -- (instancetype)initWithFrame:(CGRect)frame -{ - if (self = [super initWithFrame:frame]) { - [self commontInit]; - } - - return self; -} - -- (void)commontInit -{ - OWSAssertDebug(!self.titleLabel); - - self.layoutMargins = UIEdgeInsetsZero; - self.contentView.layoutMargins = UIEdgeInsetsZero; - self.layoutConstraints = @[]; - - self.titleLabel = [UILabel new]; - self.titleLabel.text = NSLocalizedString(@"CONVERSATION_VIEW_CONTACTS_OFFER_TITLE", - @"Title for the group of buttons show for unknown contacts offering to add them to contacts, etc."); - self.titleLabel.lineBreakMode = NSLineBreakByTruncatingTail; - self.titleLabel.textAlignment = NSTextAlignmentCenter; - - self.addToContactsButton = [self - createButtonWithTitle: - NSLocalizedString(@"CONVERSATION_VIEW_ADD_TO_CONTACTS_OFFER", - @"Message shown in conversation view that offers to add an unknown user to your phone's contacts.") - selector:@selector(addToContacts)]; - self.addToProfileWhitelistButton = [self - createButtonWithTitle:NSLocalizedString(@"CONVERSATION_VIEW_ADD_USER_TO_PROFILE_WHITELIST_OFFER", - @"Message shown in conversation view that offers to share your profile with a user.") - selector:@selector(addToProfileWhitelist)]; - self.blockButton = - [self createButtonWithTitle:NSLocalizedString(@"CONVERSATION_VIEW_UNKNOWN_CONTACT_BLOCK_OFFER", - @"Message shown in conversation view that offers to block an unknown user.") - selector:@selector(block)]; - - UIStackView *buttonStackView = [[UIStackView alloc] initWithArrangedSubviews:self.buttons]; - buttonStackView.axis = UILayoutConstraintAxisVertical; - buttonStackView.spacing = self.vSpacing; - self.buttonStackView = buttonStackView; - - self.stackView = [[UIStackView alloc] initWithArrangedSubviews:@[ - self.titleLabel, - buttonStackView, - ]]; - self.stackView.axis = UILayoutConstraintAxisVertical; - self.stackView.spacing = self.vSpacing; - self.stackView.alignment = UIStackViewAlignmentCenter; - [self.contentView addSubview:self.stackView]; -} - -- (void)configureFonts -{ - self.titleLabel.font = UIFont.ows_dynamicTypeSubheadlineFont; - - UIFont *buttonFont = UIFont.ows_dynamicTypeSubheadlineFont.ows_mediumWeight; - self.addToContactsButton.titleLabel.font = buttonFont; - self.addToProfileWhitelistButton.titleLabel.font = buttonFont; - self.blockButton.titleLabel.font = buttonFont; -} - -- (UIButton *)createButtonWithTitle:(NSString *)title selector:(SEL)selector -{ - UIButton *button = [UIButton buttonWithType:UIButtonTypeCustom]; - [button setTitle:title forState:UIControlStateNormal]; - button.titleLabel.textAlignment = NSTextAlignmentCenter; - button.layer.cornerRadius = 4.f; - [button addTarget:self action:selector forControlEvents:UIControlEventTouchUpInside]; - button.contentEdgeInsets = UIEdgeInsetsMake(0, 10.f, 0, 10.f); - return button; -} - -+ (NSString *)cellReuseIdentifier -{ - return NSStringFromClass([self class]); -} - -- (void)loadForDisplay -{ - OWSAssertDebug(self.conversationStyle); - OWSAssertDebug(self.conversationStyle.viewWidth > 0); - OWSAssertDebug(self.viewItem); - OWSAssertDebug([self.viewItem.interaction isKindOfClass:[OWSContactOffersInteraction class]]); - - self.backgroundColor = [Theme backgroundColor]; - - [self configureFonts]; - - self.titleLabel.textColor = Theme.secondaryColor; - for (UIButton *button in self.buttons) { - [button setTitleColor:[UIColor ows_signalBlueColor] forState:UIControlStateNormal]; - [button setBackgroundColor:Theme.conversationButtonBackgroundColor]; - } - - OWSContactOffersInteraction *interaction = (OWSContactOffersInteraction *)self.viewItem.interaction; - - OWSAssertDebug( - interaction.hasBlockOffer || interaction.hasAddToContactsOffer || interaction.hasAddToProfileWhitelistOffer); - - self.addToContactsButton.hidden = !interaction.hasAddToContactsOffer; - self.addToProfileWhitelistButton.hidden = !interaction.hasAddToProfileWhitelistOffer; - self.blockButton.hidden = !interaction.hasBlockOffer; - - [NSLayoutConstraint deactivateConstraints:self.layoutConstraints]; - self.layoutConstraints = @[ - [self.addToContactsButton autoSetDimension:ALDimensionHeight toSize:self.buttonHeight], - [self.addToProfileWhitelistButton autoSetDimension:ALDimensionHeight toSize:self.buttonHeight], - [self.blockButton autoSetDimension:ALDimensionHeight toSize:self.buttonHeight], - - [self.stackView autoPinEdgeToSuperviewEdge:ALEdgeTop withInset:self.topVMargin], - [self.stackView autoPinEdgeToSuperviewEdge:ALEdgeBottom withInset:self.bottomVMargin], - [self.stackView autoPinEdgeToSuperviewEdge:ALEdgeLeading - withInset:self.conversationStyle.fullWidthGutterLeading], - [self.stackView autoPinEdgeToSuperviewEdge:ALEdgeTrailing - withInset:self.conversationStyle.fullWidthGutterTrailing], - ]; - - // This hack fixes a bug that I don't understand. - // - // On an iPhone 5C running iOS 10.3.3, - // - // * Alice is a contact for which we should show some but not all contact offer buttons. - // * Delete thread with Alice. - // * Send yourself a message from Alice. - // * Open conversation with Alice. - // - // Expected: Some (but not all) offer buttons are displayed. - // Observed: All offer buttons are displayed, in a cramped layout. - for (UIButton *button in self.buttons) { - [button removeFromSuperview]; - } - for (UIButton *button in self.buttons) { - if (!button.hidden) { - [self.buttonStackView addArrangedSubview:button]; - } - } -} - -- (NSArray *)buttons -{ - return @[ - self.addToContactsButton, - self.addToProfileWhitelistButton, - self.blockButton, - ]; -} - -- (CGFloat)topVMargin -{ - return 0.f; -} - -- (CGFloat)bottomVMargin -{ - return 0.f; -} - -- (CGFloat)vSpacing -{ - return 8.f; -} - -- (CGFloat)buttonHeight -{ - return (24.f + self.addToContactsButton.titleLabel.font.lineHeight); -} - -- (CGSize)cellSize -{ - OWSAssertDebug(self.conversationStyle); - OWSAssertDebug(self.conversationStyle.viewWidth > 0); - OWSAssertDebug(self.viewItem); - OWSAssertDebug([self.viewItem.interaction isKindOfClass:[OWSContactOffersInteraction class]]); - - [self configureFonts]; - - OWSContactOffersInteraction *interaction = (OWSContactOffersInteraction *)self.viewItem.interaction; - - CGSize result = CGSizeMake(self.conversationStyle.viewWidth, 0); - result.height += self.topVMargin; - result.height += self.bottomVMargin; - - result.height += ceil([self.titleLabel sizeThatFits:CGSizeZero].height); - - int buttonCount = ((interaction.hasBlockOffer ? 1 : 0) + (interaction.hasAddToContactsOffer ? 1 : 0) - + (interaction.hasAddToProfileWhitelistOffer ? 1 : 0)); - result.height += buttonCount * (self.vSpacing + self.buttonHeight); - - return result; -} - -#pragma mark - Events - -- (nullable OWSContactOffersInteraction *)interaction -{ - OWSAssertDebug(self.viewItem); - OWSAssertDebug(self.viewItem.interaction); - if (![self.viewItem.interaction isKindOfClass:[OWSContactOffersInteraction class]]) { - OWSFailDebug(@"expected OWSContactOffersInteraction but found: %@", self.viewItem.interaction); - return nil; - } - return (OWSContactOffersInteraction *)self.viewItem.interaction; -} - -- (void)addToContacts -{ - OWSAssertDebug(self.delegate); - OWSAssertDebug(self.interaction); - - [self.delegate tappedAddToContactsOfferMessage:self.interaction]; -} - -- (void)addToProfileWhitelist -{ - OWSAssertDebug(self.delegate); - OWSAssertDebug(self.interaction); - - [self.delegate tappedAddToProfileWhitelistOfferMessage:self.interaction]; -} - -- (void)block -{ - OWSAssertDebug(self.delegate); - OWSAssertDebug(self.interaction); - - [self.delegate tappedUnknownContactBlockOfferMessage:self.interaction]; -} - -@end - -NS_ASSUME_NONNULL_END diff --git a/Session/Signal/ConversationView/Cells/OWSContactShareButtonsView.h b/Session/Signal/ConversationView/Cells/OWSContactShareButtonsView.h deleted file mode 100644 index 4997ef985..000000000 --- a/Session/Signal/ConversationView/Cells/OWSContactShareButtonsView.h +++ /dev/null @@ -1,33 +0,0 @@ -// -// Copyright (c) 2018 Open Whisper Systems. All rights reserved. -// - -NS_ASSUME_NONNULL_BEGIN - -@class ContactShareViewModel; - -@protocol OWSContactShareButtonsViewDelegate - -- (void)didTapSendMessageToContactShare:(ContactShareViewModel *)contactShare; -- (void)didTapSendInviteToContactShare:(ContactShareViewModel *)contactShare; -- (void)didTapShowAddToContactUIForContactShare:(ContactShareViewModel *)contactShare; - -@end - -#pragma mark - - -@interface OWSContactShareButtonsView : UIView - -- (instancetype)initWithContactShare:(ContactShareViewModel *)contactShare - delegate:(id)delegate; - -+ (CGFloat)bubbleHeight; - -// Returns YES IFF the tap was handled. -- (BOOL)handleTapGesture:(UITapGestureRecognizer *)sender; - -+ (BOOL)hasAnyButton:(ContactShareViewModel *)contactShare; - -@end - -NS_ASSUME_NONNULL_END diff --git a/Session/Signal/ConversationView/Cells/OWSContactShareButtonsView.m b/Session/Signal/ConversationView/Cells/OWSContactShareButtonsView.m deleted file mode 100644 index 782b81925..000000000 --- a/Session/Signal/ConversationView/Cells/OWSContactShareButtonsView.m +++ /dev/null @@ -1,169 +0,0 @@ -// -// Copyright (c) 2018 Open Whisper Systems. All rights reserved. -// - -#import "OWSContactShareButtonsView.h" -#import "Session-Swift.h" -#import "UIColor+OWS.h" -#import "UIFont+OWS.h" -#import "UIView+OWS.h" -#import -#import -#import -#import - -NS_ASSUME_NONNULL_BEGIN - -@interface OWSContactShareButtonsView () - -@property (nonatomic, readonly) ContactShareViewModel *contactShare; -@property (nonatomic, weak) id delegate; - -@property (nonatomic, readonly) OWSContactsManager *contactsManager; - -@property (nonatomic, nullable) UIView *buttonView; - -@end - -#pragma mark - - -@implementation OWSContactShareButtonsView - -- (instancetype)initWithContactShare:(ContactShareViewModel *)contactShare - delegate:(id)delegate -{ - self = [super init]; - - if (self) { - _delegate = delegate; - _contactShare = contactShare; - _contactsManager = Environment.shared.contactsManager; - - [self createContents]; - } - - return self; -} - -#pragma mark - - -+ (BOOL)hasSendTextButton:(ContactShareViewModel *)contactShare contactsManager:(OWSContactsManager *)contactsManager -{ - OWSAssertDebug(contactShare); - OWSAssertDebug(contactsManager); - - return [contactShare systemContactsWithSignalAccountPhoneNumbers:contactsManager].count > 0; -} - -+ (BOOL)hasInviteButton:(ContactShareViewModel *)contactShare contactsManager:(OWSContactsManager *)contactsManager -{ - OWSAssertDebug(contactShare); - OWSAssertDebug(contactsManager); - - return [contactShare systemContactPhoneNumbers:contactsManager].count > 0; -} - -+ (BOOL)hasAddToContactsButton:(ContactShareViewModel *)contactShare -{ - OWSAssertDebug(contactShare); - - return [contactShare e164PhoneNumbers].count > 0; -} - -+ (BOOL)hasAnyButton:(ContactShareViewModel *)contactShare -{ - OWSAssertDebug(contactShare); - - OWSContactsManager *contactsManager = Environment.shared.contactsManager; - - return [self hasAnyButton:contactShare contactsManager:contactsManager]; -} - -+ (BOOL)hasAnyButton:(ContactShareViewModel *)contactShare contactsManager:(OWSContactsManager *)contactsManager -{ - OWSAssertDebug(contactShare); - - return ([self hasSendTextButton:contactShare contactsManager:contactsManager] || - [self hasInviteButton:contactShare contactsManager:contactsManager] || - [self hasAddToContactsButton:contactShare]); -} - -+ (CGFloat)bubbleHeight -{ - return self.buttonHeight; -} - -+ (CGFloat)buttonHeight -{ - return MAX(44.f, self.buttonFont.lineHeight + self.buttonVMargin * 2); -} - -+ (UIFont *)buttonFont -{ - return [UIFont ows_dynamicTypeBodyFont].ows_mediumWeight; -} - -+ (CGFloat)buttonVMargin -{ - return 5; -} - -- (void)createContents -{ - OWSAssertDebug([OWSContactShareButtonsView hasAnyButton:self.contactShare contactsManager:self.contactsManager]); - - self.layoutMargins = UIEdgeInsetsZero; - self.backgroundColor = Theme.conversationButtonBackgroundColor; - - UILabel *label = [UILabel new]; - self.buttonView = label; - if ([OWSContactShareButtonsView hasSendTextButton:self.contactShare contactsManager:self.contactsManager]) { - label.text - = NSLocalizedString(@"ACTION_SEND_MESSAGE", @"Label for button that lets you send a message to a contact."); - } else if ([OWSContactShareButtonsView hasInviteButton:self.contactShare contactsManager:self.contactsManager]) { - label.text = NSLocalizedString(@"ACTION_INVITE", @"Label for 'invite' button in contact view."); - } else if ([OWSContactShareButtonsView hasAddToContactsButton:self.contactShare]) { - label.text = NSLocalizedString(@"CONVERSATION_VIEW_ADD_TO_CONTACTS_OFFER", - @"Message shown in conversation view that offers to add an unknown user to your phone's contacts."); - } else { - OWSFailDebug(@"unexpected button state."); - } - label.font = OWSContactShareButtonsView.buttonFont; - label.textColor = (Theme.isDarkThemeEnabled ? UIColor.ows_whiteColor : UIColor.ows_materialBlueColor); - label.textAlignment = NSTextAlignmentCenter; - [self addSubview:label]; - [label ows_autoPinToSuperviewEdges]; - [label autoSetDimension:ALDimensionHeight toSize:OWSContactShareButtonsView.buttonHeight]; - - self.userInteractionEnabled = YES; - UITapGestureRecognizer *tap = - [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(handleTapGesture:)]; - [self addGestureRecognizer:tap]; -} - -- (BOOL)handleTapGesture:(UITapGestureRecognizer *)sender -{ - if (!self.buttonView) { - return NO; - } - CGPoint location = [sender locationInView:self.buttonView]; - if (!CGRectContainsPoint(self.buttonView.bounds, location)) { - return NO; - } - - if ([OWSContactShareButtonsView hasSendTextButton:self.contactShare contactsManager:self.contactsManager]) { - [self.delegate didTapSendMessageToContactShare:self.contactShare]; - } else if ([OWSContactShareButtonsView hasInviteButton:self.contactShare contactsManager:self.contactsManager]) { - [self.delegate didTapSendInviteToContactShare:self.contactShare]; - } else if ([OWSContactShareButtonsView hasAddToContactsButton:self.contactShare]) { - [self.delegate didTapShowAddToContactUIForContactShare:self.contactShare]; - } else { - OWSFailDebug(@"unexpected button tap."); - } - - return YES; -} - -@end - -NS_ASSUME_NONNULL_END diff --git a/Session/Signal/ConversationView/Cells/OWSContactShareView.h b/Session/Signal/ConversationView/Cells/OWSContactShareView.h deleted file mode 100644 index b8cab42bc..000000000 --- a/Session/Signal/ConversationView/Cells/OWSContactShareView.h +++ /dev/null @@ -1,22 +0,0 @@ -// -// Copyright (c) 2018 Open Whisper Systems. All rights reserved. -// - -NS_ASSUME_NONNULL_BEGIN - -@class ContactShareViewModel; -@class ConversationStyle; - -@interface OWSContactShareView : UIView - -- (instancetype)initWithContactShare:(ContactShareViewModel *)contactShare - isIncoming:(BOOL)isIncoming - conversationStyle:(ConversationStyle *)conversationStyle; - -- (void)createContents; - -+ (CGFloat)bubbleHeight; - -@end - -NS_ASSUME_NONNULL_END diff --git a/Session/Signal/ConversationView/Cells/OWSContactShareView.m b/Session/Signal/ConversationView/Cells/OWSContactShareView.m deleted file mode 100644 index fad2ee026..000000000 --- a/Session/Signal/ConversationView/Cells/OWSContactShareView.m +++ /dev/null @@ -1,165 +0,0 @@ -// -// Copyright (c) 2018 Open Whisper Systems. All rights reserved. -// - -#import "OWSContactShareView.h" -#import "OWSContactAvatarBuilder.h" -#import "Session-Swift.h" -#import "UIColor+OWS.h" -#import "UIFont+OWS.h" -#import "UIView+OWS.h" -#import -#import -#import -#import - -NS_ASSUME_NONNULL_BEGIN - -@interface OWSContactShareView () - -@property (nonatomic, readonly) ContactShareViewModel *contactShare; - -@property (nonatomic, readonly) BOOL isIncoming; -@property (nonatomic, readonly) ConversationStyle *conversationStyle; -@property (nonatomic, readonly) OWSContactsManager *contactsManager; - -@end - -#pragma mark - - -@implementation OWSContactShareView - -- (instancetype)initWithContactShare:(ContactShareViewModel *)contactShare - isIncoming:(BOOL)isIncoming - conversationStyle:(ConversationStyle *)conversationStyle -{ - self = [super init]; - - if (self) { - _contactShare = contactShare; - _isIncoming = isIncoming; - _conversationStyle = conversationStyle; - _contactsManager = Environment.shared.contactsManager; - } - - return self; -} - -#pragma mark - - -- (CGFloat)hMargin -{ - return 12.f; -} - -+ (CGFloat)vMargin -{ - return 0.f; -} - -- (CGFloat)iconHSpacing -{ - return 8.f; -} - -+ (CGFloat)bubbleHeight -{ - return self.contentHeight; -} - -+ (CGFloat)contentHeight -{ - CGFloat labelsHeight = (self.nameFont.lineHeight + self.labelsVSpacing + self.subtitleFont.lineHeight); - CGFloat contentHeight = MAX(self.iconSize, labelsHeight); - contentHeight += OWSContactShareView.vMargin * 2; - return contentHeight; -} - -+ (CGFloat)iconSize -{ - return kStandardAvatarSize; -} - -- (CGFloat)iconSize -{ - return [OWSContactShareView iconSize]; -} - -+ (UIFont *)nameFont -{ - return [UIFont ows_dynamicTypeBodyFont]; -} - -+ (UIFont *)subtitleFont -{ - return [UIFont ows_dynamicTypeCaption1Font]; -} - -+ (CGFloat)labelsVSpacing -{ - return 2; -} - -- (void)createContents -{ - self.layoutMargins = UIEdgeInsetsZero; - - UIColor *textColor = [self.conversationStyle bubbleTextColorWithIsIncoming:self.isIncoming]; - - AvatarImageView *avatarView = [AvatarImageView new]; - avatarView.image = - [self.contactShare getAvatarImageWithDiameter:self.iconSize contactsManager:self.contactsManager]; - - [avatarView autoSetDimension:ALDimensionWidth toSize:self.iconSize]; - [avatarView autoSetDimension:ALDimensionHeight toSize:self.iconSize]; - [avatarView setCompressionResistanceHigh]; - [avatarView setContentHuggingHigh]; - - UILabel *topLabel = [UILabel new]; - topLabel.text = self.contactShare.displayName; - topLabel.textColor = textColor; - topLabel.lineBreakMode = NSLineBreakByTruncatingTail; - topLabel.font = OWSContactShareView.nameFont; - - UIStackView *labelsView = [UIStackView new]; - labelsView.axis = UILayoutConstraintAxisVertical; - labelsView.spacing = OWSContactShareView.labelsVSpacing; - [labelsView addArrangedSubview:topLabel]; - - NSString *_Nullable firstPhoneNumber = - [self.contactShare systemContactsWithSignalAccountPhoneNumbers:self.contactsManager].firstObject; - if (firstPhoneNumber.length > 0) { - UILabel *bottomLabel = [UILabel new]; - bottomLabel.text = [PhoneNumber bestEffortLocalizedPhoneNumberWithE164:firstPhoneNumber]; - bottomLabel.textColor = [self.conversationStyle bubbleSecondaryTextColorWithIsIncoming:self.isIncoming]; - bottomLabel.lineBreakMode = NSLineBreakByTruncatingTail; - bottomLabel.font = OWSContactShareView.subtitleFont; - [labelsView addArrangedSubview:bottomLabel]; - } - - UIImage *disclosureImage = - [UIImage imageNamed:(CurrentAppContext().isRTL ? @"small_chevron_left" : @"small_chevron_right")]; - OWSAssertDebug(disclosureImage); - UIImageView *disclosureImageView = [UIImageView new]; - disclosureImageView.image = [disclosureImage imageWithRenderingMode:UIImageRenderingModeAlwaysTemplate]; - disclosureImageView.tintColor = textColor; - [disclosureImageView setCompressionResistanceHigh]; - [disclosureImageView setContentHuggingHigh]; - - UIStackView *hStackView = [UIStackView new]; - hStackView.axis = UILayoutConstraintAxisHorizontal; - hStackView.spacing = self.iconHSpacing; - hStackView.alignment = UIStackViewAlignmentCenter; - hStackView.layoutMarginsRelativeArrangement = YES; - hStackView.layoutMargins - = UIEdgeInsetsMake(OWSContactShareView.vMargin, self.hMargin, OWSContactShareView.vMargin, self.hMargin); - [hStackView addArrangedSubview:avatarView]; - [hStackView addArrangedSubview:labelsView]; - [hStackView addArrangedSubview:disclosureImageView]; - [self addSubview:hStackView]; - [hStackView ows_autoPinToSuperviewEdges]; -} - -@end - -NS_ASSUME_NONNULL_END diff --git a/Session/Signal/ConversationView/Cells/OWSGenericAttachmentView.m b/Session/Signal/ConversationView/Cells/OWSGenericAttachmentView.m index b6b695c43..5782c329d 100644 --- a/Session/Signal/ConversationView/Cells/OWSGenericAttachmentView.m +++ b/Session/Signal/ConversationView/Cells/OWSGenericAttachmentView.m @@ -7,7 +7,7 @@ #import "Session-Swift.h" #import "UIFont+OWS.h" #import "UIView+OWS.h" -#import "ViewControllerUtils.h" + #import #import #import @@ -91,7 +91,7 @@ NS_ASSUME_NONNULL_BEGIN - (CGFloat)iconHeight { - return kStandardAvatarSize; + return 48.0f; } - (void)createContentsWithConversationStyle:(ConversationStyle *)conversationStyle diff --git a/Session/Signal/ConversationView/Cells/OWSMessageBubbleView.h b/Session/Signal/ConversationView/Cells/OWSMessageBubbleView.h index ba63b6774..4db47bec3 100644 --- a/Session/Signal/ConversationView/Cells/OWSMessageBubbleView.h +++ b/Session/Signal/ConversationView/Cells/OWSMessageBubbleView.h @@ -49,15 +49,6 @@ typedef NS_ENUM(NSUInteger, OWSMessageGestureLocation) { - (void)didTapConversationItem:(id)viewItem linkPreview:(OWSLinkPreview *)linkPreview; -- (void)didTapContactShareViewItem:(id)viewItem; - -- (void)didTapSendMessageToContactShare:(ContactShareViewModel *)contactShare - NS_SWIFT_NAME(didTapSendMessage(toContactShare:)); -- (void)didTapSendInviteToContactShare:(ContactShareViewModel *)contactShare - NS_SWIFT_NAME(didTapSendInvite(toContactShare:)); -- (void)didTapShowAddToContactUIForContactShare:(ContactShareViewModel *)contactShare - NS_SWIFT_NAME(didTapShowAddToContactUI(forContactShare:)); - @property (nonatomic, readonly, nullable) NSString *lastSearchedText; @end diff --git a/Session/Signal/ConversationView/Cells/OWSMessageBubbleView.m b/Session/Signal/ConversationView/Cells/OWSMessageBubbleView.m index 9ec3201a5..8c736cee4 100644 --- a/Session/Signal/ConversationView/Cells/OWSMessageBubbleView.m +++ b/Session/Signal/ConversationView/Cells/OWSMessageBubbleView.m @@ -7,8 +7,6 @@ #import "ConversationViewItem.h" #import "OWSBubbleShapeView.h" #import "OWSBubbleView.h" -#import "OWSContactShareButtonsView.h" -#import "OWSContactShareView.h" #import "OWSGenericAttachmentView.h" #import "OWSLabel.h" #import "OWSMessageFooterView.h" @@ -20,7 +18,7 @@ NS_ASSUME_NONNULL_BEGIN -@interface OWSMessageBubbleView () +@interface OWSMessageBubbleView () @property (nonatomic) OWSBubbleView *bubbleView; @@ -48,8 +46,6 @@ NS_ASSUME_NONNULL_BEGIN @property (nonatomic) OWSMessageFooterView *footerView; -@property (nonatomic, nullable) OWSContactShareButtonsView *contactShareButtonsView; - @end #pragma mark - @@ -267,9 +263,6 @@ NS_ASSUME_NONNULL_BEGIN case OWSMessageCellType_GenericAttachment: bodyMediaView = [self loadViewForGenericAttachment]; break; - case OWSMessageCellType_ContactShare: - bodyMediaView = [self loadViewForContactShare]; - break; case OWSMessageCellType_MediaMessage: bodyMediaView = [self loadViewForMediaAlbum]; break; @@ -300,24 +293,6 @@ NS_ASSUME_NONNULL_BEGIN if (self.hasBodyMediaWithThumbnail) { [self.stackView addArrangedSubview:bodyMediaView]; - } else { - OWSAssertDebug(self.cellType == OWSMessageCellType_ContactShare); - - if (self.contactShareHasSpacerTop) { - UIView *spacerView = [UIView containerView]; - [spacerView autoSetDimension:ALDimensionHeight toSize:self.contactShareVSpacing]; - [spacerView setCompressionResistanceHigh]; - [self.stackView addArrangedSubview:spacerView]; - } - - [self.stackView addArrangedSubview:bodyMediaView]; - - if (self.contactShareHasSpacerBottom) { - UIView *spacerView = [UIView containerView]; - [spacerView autoSetDimension:ALDimensionHeight toSize:self.contactShareVSpacing]; - [spacerView setCompressionResistanceHigh]; - [self.stackView addArrangedSubview:spacerView]; - } } } else { [textViews addObject:bodyMediaView]; @@ -414,86 +389,11 @@ NS_ASSUME_NONNULL_BEGIN addObject:[bodyMediaView autoSetDimension:ALDimensionHeight toSize:bodyMediaSize.CGSizeValue.height]]; } - [self insertContactShareButtonsIfNecessary]; - [self updateBubbleColor]; [self configureBubbleRounding]; } -- (void)insertContactShareButtonsIfNecessary -{ - if (self.cellType != OWSMessageCellType_ContactShare) { - return; - } - - if (![OWSContactShareButtonsView hasAnyButton:self.viewItem.contactShare]) { - return; - } - - OWSAssertDebug(self.viewItem.contactShare); - - OWSContactShareButtonsView *buttonsView = - [[OWSContactShareButtonsView alloc] initWithContactShare:self.viewItem.contactShare delegate:self]; - - NSValue *_Nullable actionButtonsSize = [self actionButtonsSize]; - OWSAssertDebug(actionButtonsSize); - [self.viewConstraints addObjectsFromArray:@[ - [buttonsView autoSetDimension:ALDimensionHeight toSize:actionButtonsSize.CGSizeValue.height], - ]]; - - // The "contact share" view casts a shadow "downward" onto adjacent views, - // so we use a "proxy" view to take its place within the v-stack - // view and then insert the "contact share" view above its proxy so that - // it floats above the other content of the bubble view. - - UIView *proxyView = [UIView new]; - [self.stackView addArrangedSubview:proxyView]; - - OWSBubbleShapeView *shadowView = [[OWSBubbleShapeView alloc] initShadow]; - OWSBubbleShapeView *clipView = [[OWSBubbleShapeView alloc] initClip]; - - [self addSubview:shadowView]; - [self addSubview:clipView]; - - [self.viewConstraints addObjectsFromArray:[shadowView autoPinToEdgesOfView:proxyView]]; - [self.viewConstraints addObjectsFromArray:[clipView autoPinToEdgesOfView:proxyView]]; - - [clipView addSubview:buttonsView]; - [self.viewConstraints addObjectsFromArray:[buttonsView ows_autoPinToSuperviewEdges]]; - - [self.bubbleView addPartnerView:shadowView]; - [self.bubbleView addPartnerView:clipView]; - - // Prevent the layer from animating changes. - [CATransaction begin]; - [CATransaction setDisableActions:YES]; - - OWSAssertDebug(buttonsView.backgroundColor); - shadowView.fillColor = buttonsView.backgroundColor; - shadowView.layer.shadowColor = Theme.boldColor.CGColor; - shadowView.layer.shadowOpacity = 0.12f; - shadowView.layer.shadowOffset = CGSizeZero; - shadowView.layer.shadowRadius = 1.f; - - [CATransaction commit]; -} - -- (BOOL)contactShareHasSpacerTop -{ - return (self.cellType == OWSMessageCellType_ContactShare && (self.isQuotedReply || !self.shouldShowSenderName)); -} - -- (BOOL)contactShareHasSpacerBottom -{ - return (self.cellType == OWSMessageCellType_ContactShare && !self.hasBottomFooter); -} - -- (CGFloat)contactShareVSpacing -{ - return 12.f; -} - - (CGFloat)senderNameBottomSpacing { return 0.f; @@ -557,7 +457,6 @@ NS_ASSUME_NONNULL_BEGIN case OWSMessageCellType_TextOnlyMessage: case OWSMessageCellType_Audio: case OWSMessageCellType_GenericAttachment: - case OWSMessageCellType_ContactShare: case OWSMessageCellType_OversizeTextDownloading: return NO; case OWSMessageCellType_MediaMessage: @@ -572,7 +471,6 @@ NS_ASSUME_NONNULL_BEGIN return NO; case OWSMessageCellType_Audio: case OWSMessageCellType_GenericAttachment: - case OWSMessageCellType_ContactShare: case OWSMessageCellType_MediaMessage: case OWSMessageCellType_OversizeTextDownloading: return YES; @@ -581,8 +479,7 @@ NS_ASSUME_NONNULL_BEGIN - (BOOL)hasFullWidthMediaView { - return (self.hasBodyMediaWithThumbnail || self.cellType == OWSMessageCellType_ContactShare - || self.cellType == OWSMessageCellType_MediaMessage); + return (self.hasBodyMediaWithThumbnail || self.cellType == OWSMessageCellType_MediaMessage); } - (BOOL)canFooterOverlayMedia @@ -876,26 +773,6 @@ NS_ASSUME_NONNULL_BEGIN return attachmentView; } -- (UIView *)loadViewForContactShare -{ - OWSAssertDebug(self.viewItem.contactShare); - - OWSContactShareView *contactShareView = [[OWSContactShareView alloc] initWithContactShare:self.viewItem.contactShare - isIncoming:self.isIncoming - conversationStyle:self.conversationStyle]; - [contactShareView createContents]; - // TODO: Should we change appearance if contact avatar is uploading? - - self.loadCellContentBlock = ^{ - // Do nothing. - }; - self.unloadCellContentBlock = ^{ - // Do nothing. - }; - - return contactShareView; -} - - (UIView *)loadViewForOversizeTextDownload { // We can use an empty view. The progress views will display download @@ -1079,11 +956,6 @@ NS_ASSUME_NONNULL_BEGIN result = [attachmentView measureSizeWithMaxMessageWidth:maxMessageWidth]; break; } - case OWSMessageCellType_ContactShare: - OWSAssertDebug(self.viewItem.contactShare); - - result = CGSizeMake(maxMessageWidth, [OWSContactShareView bubbleHeight]); - break; case OWSMessageCellType_MediaMessage: result = [OWSMediaAlbumCellView layoutSizeForMaxMessageWidth:maxMessageWidth items:self.viewItem.mediaAlbumItems]; @@ -1186,23 +1058,6 @@ NS_ASSUME_NONNULL_BEGIN return [NSValue valueWithCGSize:result]; } -- (nullable NSValue *)actionButtonsSize -{ - OWSAssertDebug(self.conversationStyle); - OWSAssertDebug(self.conversationStyle.maxMessageWidth > 0); - - if (self.cellType == OWSMessageCellType_ContactShare) { - OWSAssertDebug(self.viewItem.contactShare); - - if ([OWSContactShareButtonsView hasAnyButton:self.viewItem.contactShare]) { - CGSize buttonsSize = CGSizeCeil( - CGSizeMake(self.conversationStyle.maxMessageWidth, [OWSContactShareButtonsView bubbleHeight])); - return [NSValue valueWithCGSize:buttonsSize]; - } - } - return nil; -} - - (CGSize)measureSize { OWSAssertDebug(self.conversationStyle); @@ -1239,13 +1094,6 @@ NS_ASSUME_NONNULL_BEGIN [textViewSizes addObject:bodyMediaSize]; bodyMediaSize = nil; } - - if (self.contactShareHasSpacerTop) { - cellSize.height += self.contactShareVSpacing; - } - if (self.contactShareHasSpacerBottom) { - cellSize.height += self.contactShareVSpacing; - } } if (bodyMediaSize || quotedMessageSize) { @@ -1296,12 +1144,6 @@ NS_ASSUME_NONNULL_BEGIN cellSize.height += self.tapForMoreHeight + self.textViewVSpacing; } - NSValue *_Nullable actionButtonsSize = [self actionButtonsSize]; - if (actionButtonsSize) { - cellSize.width = MAX(cellSize.width, actionButtonsSize.CGSizeValue.width); - cellSize.height += actionButtonsSize.CGSizeValue.height; - } - cellSize = CGSizeCeil(cellSize); OWSAssertDebug(cellSize.width <= self.conversationStyle.maxMessageWidth); @@ -1395,9 +1237,6 @@ NS_ASSUME_NONNULL_BEGIN } } - [self.contactShareButtonsView removeFromSuperview]; - self.contactShareButtonsView = nil; - [self.linkPreviewView removeFromSuperview]; self.linkPreviewView.state = nil; } @@ -1430,12 +1269,6 @@ NS_ASSUME_NONNULL_BEGIN } } - if (self.contactShareButtonsView) { - if ([self.contactShareButtonsView handleTapGesture:sender]) { - return; - } - } - CGPoint locationInMessageBubble = [sender locationInView:self]; switch ([self gestureLocationForLocation:locationInMessageBubble]) { case OWSMessageGestureLocation_Default: @@ -1488,9 +1321,6 @@ NS_ASSUME_NONNULL_BEGIN [AttachmentSharing showShareUIForAttachment:self.viewItem.attachmentStream]; } break; - case OWSMessageCellType_ContactShare: - [self.delegate didTapContactShareViewItem:self.viewItem]; - break; case OWSMessageCellType_MediaMessage: { OWSAssertDebug(self.bodyMediaView); OWSAssertDebug(self.viewItem.mediaAlbumItems.count > 0); @@ -1603,32 +1433,6 @@ NS_ASSUME_NONNULL_BEGIN OWSFailDebug(@"Sent quoted replies should not be cancellable."); } -#pragma mark - OWSContactShareButtonsViewDelegate - -- (void)didTapSendMessageToContactShare:(ContactShareViewModel *)contactShare -{ - OWSAssertIsOnMainThread(); - OWSAssertDebug(contactShare); - - [self.delegate didTapSendMessageToContactShare:contactShare]; -} - -- (void)didTapSendInviteToContactShare:(ContactShareViewModel *)contactShare -{ - OWSAssertIsOnMainThread(); - OWSAssertDebug(contactShare); - - [self.delegate didTapSendInviteToContactShare:contactShare]; -} - -- (void)didTapShowAddToContactUIForContactShare:(ContactShareViewModel *)contactShare -{ - OWSAssertIsOnMainThread(); - OWSAssertDebug(contactShare); - - [self.delegate didTapShowAddToContactUIForContactShare:contactShare]; -} - @end NS_ASSUME_NONNULL_END diff --git a/Session/Signal/ConversationView/Cells/OWSMessageCell.m b/Session/Signal/ConversationView/Cells/OWSMessageCell.m index 443f4614e..6190bb0be 100644 --- a/Session/Signal/ConversationView/Cells/OWSMessageCell.m +++ b/Session/Signal/ConversationView/Cells/OWSMessageCell.m @@ -3,7 +3,6 @@ // #import "OWSMessageCell.h" -#import "OWSContactAvatarBuilder.h" #import "OWSMessageBubbleView.h" #import "OWSMessageHeaderView.h" #import "Session-Swift.h" @@ -286,7 +285,7 @@ NS_ASSUME_NONNULL_BEGIN [self.avatarView update]; // Loki: Show the moderator icon if needed - if (self.viewItem.isGroupThread && !self.viewItem.isRSSFeed) { // FIXME: This logic also shouldn't apply to closed groups + if (self.viewItem.isGroupThread) { // FIXME: This logic also shouldn't apply to closed groups __block SNOpenGroup *publicChat; [OWSPrimaryStorage.sharedManager.dbReadConnection readWithBlock:^(YapDatabaseReadTransaction *transaction) { publicChat = [LKDatabaseUtilities getPublicChatForThreadID:self.viewItem.interaction.uniqueThreadId transaction: transaction]; diff --git a/Session/Signal/ConversationView/Cells/OWSMessageFooterView.m b/Session/Signal/ConversationView/Cells/OWSMessageFooterView.m index c0f6fdf17..72ac15eb3 100644 --- a/Session/Signal/ConversationView/Cells/OWSMessageFooterView.m +++ b/Session/Signal/ConversationView/Cells/OWSMessageFooterView.m @@ -122,7 +122,8 @@ NS_ASSUME_NONNULL_BEGIN [OWSPrimaryStorage.sharedManager.dbReadConnection readWithBlock:^(YapDatabaseReadTransaction *transaction) { TSContactThread *thread = [outgoingMessage.thread as:TSContactThread.class]; if (thread != nil) { - isNoteToSelf = [LKDatabaseUtilities isUserLinkedDevice:thread.contactIdentifier in:transaction]; + NSString *userPublicKey = OWSIdentityManager.sharedManager.identityKeyPair.hexEncodedPublicKey; + isNoteToSelf = ([thread.contactIdentifier isEqual:userPublicKey]); } }]; diff --git a/Session/Signal/ConversationView/Cells/OWSMessageTimerView.m b/Session/Signal/ConversationView/Cells/OWSMessageTimerView.m index 67921dc3a..1b4bd621d 100644 --- a/Session/Signal/ConversationView/Cells/OWSMessageTimerView.m +++ b/Session/Signal/ConversationView/Cells/OWSMessageTimerView.m @@ -9,7 +9,7 @@ #import "UIView+OWS.h" #import #import -#import +#import NS_ASSUME_NONNULL_BEGIN diff --git a/Session/Signal/ConversationView/Cells/OWSQuotedMessageView.m b/Session/Signal/ConversationView/Cells/OWSQuotedMessageView.m index 475a78faf..199d35df7 100644 --- a/Session/Signal/ConversationView/Cells/OWSQuotedMessageView.m +++ b/Session/Signal/ConversationView/Cells/OWSQuotedMessageView.m @@ -8,7 +8,7 @@ #import "OWSBubbleView.h" #import "Session-Swift.h" #import -#import + #import #import #import @@ -550,8 +550,7 @@ const CGFloat kRemotelySourcedContentRowSpacing = 4; quotedAuthorText = NSLocalizedString(@"You", @""); } } else { - OWSContactsManager *contactsManager = Environment.shared.contactsManager; - __block NSString *quotedAuthor = [SSKEnvironment.shared.profileManager profileNameForRecipientWithID:self.quotedMessage.authorId] ?: [contactsManager contactOrProfileNameForPhoneIdentifier:self.quotedMessage.authorId]; + __block NSString *quotedAuthor = [SSKEnvironment.shared.profileManager profileNameForRecipientWithID:self.quotedMessage.authorId]; if (quotedAuthor == self.quotedMessage.authorId) { [OWSPrimaryStorage.sharedManager.dbReadConnection readWithBlock:^(YapDatabaseReadTransaction *transaction) { diff --git a/Session/Signal/ConversationView/Cells/OWSSystemMessageCell.m b/Session/Signal/ConversationView/Cells/OWSSystemMessageCell.m index 683c00b61..d3c18cf51 100644 --- a/Session/Signal/ConversationView/Cells/OWSSystemMessageCell.m +++ b/Session/Signal/ConversationView/Cells/OWSSystemMessageCell.m @@ -9,9 +9,8 @@ #import "UIColor+OWS.h" #import "UIFont+OWS.h" #import "UIView+OWS.h" +#import #import -#import -#import #import #import #import @@ -290,17 +289,6 @@ typedef void (^SystemMessageActionBlock)(void); : [UIImage imageNamed:@"system_message_disappearing_messages_disabled"]); break; } - case TSInfoMessageVerificationStateChange: - OWSAssertDebug([interaction isKindOfClass:[OWSVerificationStateChangeMessage class]]); - if ([interaction isKindOfClass:[OWSVerificationStateChangeMessage class]]) { - OWSVerificationStateChangeMessage *message = (OWSVerificationStateChangeMessage *)interaction; - BOOL isVerified = message.verificationState == OWSVerificationStateVerified; - if (!isVerified) { - return nil; - } - } - result = [UIImage imageNamed:@"system_message_verified"]; - break; } } else if ([interaction isKindOfClass:[TSCall class]]) { result = [UIImage imageNamed:@"system_message_call"]; @@ -398,8 +386,6 @@ typedef void (^SystemMessageActionBlock)(void); return [self actionForErrorMessage:(TSErrorMessage *)interaction]; } else if ([interaction isKindOfClass:[TSInfoMessage class]]) { return [self actionForInfoMessage:(TSInfoMessage *)interaction]; - } else if ([interaction isKindOfClass:[TSCall class]]) { - return [self actionForCall:(TSCall *)interaction]; } else { OWSFailDebug(@"Tap for system messages of unknown type: %@", [interaction class]); return nil; @@ -414,21 +400,6 @@ typedef void (^SystemMessageActionBlock)(void); switch (message.errorType) { case TSErrorMessageInvalidKeyException: return nil; - case TSErrorMessageNonBlockingIdentityChange: - return [SystemMessageAction - actionWithTitle:NSLocalizedString(@"SYSTEM_MESSAGE_ACTION_VERIFY_SAFETY_NUMBER", - @"Label for button to verify a user's safety number.") - block:^{ - [weakSelf.delegate tappedNonBlockingIdentityChangeForRecipientId:message.recipientId]; - }]; - case TSErrorMessageWrongTrustedIdentityKey: - return [SystemMessageAction - actionWithTitle:NSLocalizedString(@"SYSTEM_MESSAGE_ACTION_VERIFY_SAFETY_NUMBER", - @"Label for button to verify a user's safety number.") - block:^{ - [weakSelf.delegate - tappedInvalidIdentityKeyErrorMessage:(TSInvalidIdentityKeyErrorMessage *)message]; - }]; case TSErrorMessageMissingKeyId: case TSErrorMessageNoSession: case TSErrorMessageInvalidMessage: @@ -486,48 +457,12 @@ typedef void (^SystemMessageActionBlock)(void); block:^{ [weakSelf.delegate showConversationSettings]; }]; - case TSInfoMessageVerificationStateChange: - return [SystemMessageAction - actionWithTitle:NSLocalizedString(@"SHOW_SAFETY_NUMBER_ACTION", @"Action sheet item") - block:^{ - [weakSelf.delegate - showFingerprintWithRecipientId:((OWSVerificationStateChangeMessage *)message) - .recipientId]; - }]; } OWSLogInfo(@"Unhandled tap for info message: %@", message); return nil; } -- (nullable SystemMessageAction *)actionForCall:(TSCall *)call -{ - OWSAssertDebug(call); - - __weak OWSSystemMessageCell *weakSelf = self; - switch (call.callType) { - case RPRecentCallTypeIncoming: - case RPRecentCallTypeIncomingMissed: - case RPRecentCallTypeIncomingMissedBecauseOfChangedIdentity: - case RPRecentCallTypeIncomingDeclined: - return - [SystemMessageAction actionWithTitle:NSLocalizedString(@"CALLBACK_BUTTON_TITLE", @"notification action") - block:^{ - [weakSelf.delegate handleCallTap:call]; - }]; - case RPRecentCallTypeOutgoing: - case RPRecentCallTypeOutgoingMissed: - return [SystemMessageAction actionWithTitle:NSLocalizedString(@"CALL_AGAIN_BUTTON_TITLE", - @"Label for button that lets users call a contact again.") - block:^{ - [weakSelf.delegate handleCallTap:call]; - }]; - case RPRecentCallTypeOutgoingIncomplete: - case RPRecentCallTypeIncomingIncomplete: - return nil; - } -} - #pragma mark - Events - (void)handleLongPressGesture:(UILongPressGestureRecognizer *)longPress diff --git a/Session/Signal/ConversationView/Cells/TypingIndicatorCell.swift b/Session/Signal/ConversationView/Cells/TypingIndicatorCell.swift index 1de1235c6..064a05ec6 100644 --- a/Session/Signal/ConversationView/Cells/TypingIndicatorCell.swift +++ b/Session/Signal/ConversationView/Cells/TypingIndicatorCell.swift @@ -19,7 +19,7 @@ public class TypingIndicatorCell: ConversationViewCell { private let kAvatarSize: CGFloat = 36 private let kAvatarHSpacing: CGFloat = 8 - private let avatarView = AvatarImageView() +// private let avatarView = AvatarImageView() private let bubbleView = OWSBubbleView() private let typingIndicatorView = TypingIndicatorView() private var viewConstraints = [NSLayoutConstraint]() @@ -39,8 +39,8 @@ public class TypingIndicatorCell: ConversationViewCell { bubbleView.addSubview(typingIndicatorView) contentView.addSubview(bubbleView) - avatarView.autoSetDimension(.width, toSize: kAvatarSize) - avatarView.autoSetDimension(.height, toSize: kAvatarSize) +// avatarView.autoSetDimension(.width, toSize: kAvatarSize) +// avatarView.autoSetDimension(.height, toSize: kAvatarSize) } @objc @@ -65,16 +65,16 @@ public class TypingIndicatorCell: ConversationViewCell { typingIndicatorView.autoPinBottomToSuperviewMargin(withInset: conversationStyle.textInsetBottom) ]) - if let avatarView = configureAvatarView() { - contentView.addSubview(avatarView) - viewConstraints.append(contentsOf: [ - bubbleView.autoPinLeading(toTrailingEdgeOf: avatarView, offset: kAvatarHSpacing), - bubbleView.autoAlignAxis(.horizontal, toSameAxisOf: avatarView) - ]) - - } else { - avatarView.removeFromSuperview() - } +// if let avatarView = configureAvatarView() { +// contentView.addSubview(avatarView) +// viewConstraints.append(contentsOf: [ +// bubbleView.autoPinLeading(toTrailingEdgeOf: avatarView, offset: kAvatarHSpacing), +// bubbleView.autoAlignAxis(.horizontal, toSameAxisOf: avatarView) +// ]) +// +// } else { +// avatarView.removeFromSuperview() +// } } private func configureAvatarView() -> UIView? { @@ -93,15 +93,16 @@ public class TypingIndicatorCell: ConversationViewCell { owsFailDebug("Missing authorConversationColorName") return nil } - guard let authorAvatarImage = - OWSContactAvatarBuilder(signalId: typingIndicators.recipientId, - colorName: ConversationColorName(rawValue: colorName), - diameter: UInt(kAvatarSize)).build() else { - owsFailDebug("Could build avatar image") - return nil - } - avatarView.image = authorAvatarImage - return avatarView +// guard let authorAvatarImage = +// OWSContactAvatarBuilder(signalId: typingIndicators.recipientId, +// colorName: ConversationColorName(rawValue: colorName), +// diameter: UInt(kAvatarSize)).build() else { +// owsFailDebug("Could build avatar image") +// return nil +// } +// avatarView.image = authorAvatarImage +// return avatarView + return UIView() } private func shouldShowAvatar() -> Bool { @@ -140,8 +141,8 @@ public class TypingIndicatorCell: ConversationViewCell { NSLayoutConstraint.deactivate(viewConstraints) viewConstraints = [NSLayoutConstraint]() - avatarView.image = nil - avatarView.removeFromSuperview() +// avatarView.image = nil +// avatarView.removeFromSuperview() typingIndicatorView.stopAnimation() } diff --git a/Session/Signal/ConversationView/ConversationHeaderView.swift b/Session/Signal/ConversationView/ConversationHeaderView.swift deleted file mode 100644 index 680060ac6..000000000 --- a/Session/Signal/ConversationView/ConversationHeaderView.swift +++ /dev/null @@ -1,131 +0,0 @@ -// -// Copyright (c) 2019 Open Whisper Systems. All rights reserved. -// - -import Foundation - -@objc -public protocol ConversationHeaderViewDelegate { - func didTapConversationHeaderView(_ conversationHeaderView: ConversationHeaderView) -} - -@objc -public class ConversationHeaderView: UIStackView { - - @objc - public weak var delegate: ConversationHeaderViewDelegate? - - @objc - public var attributedTitle: NSAttributedString? { - get { - return self.titleLabel.attributedText - } - set { - self.titleLabel.attributedText = newValue - } - } - - @objc - public var attributedSubtitle: NSAttributedString? { - get { - return self.subtitleLabel.attributedText - } - set { - self.subtitleLabel.attributedText = newValue - self.subtitleLabel.isHidden = newValue == nil - } - } - - public var avatarImage: UIImage? { - get { - return self.avatarView.image - } - set { - self.avatarView.image = newValue - } - } - - @objc - public let titlePrimaryFont: UIFont = UIFont.ows_boldFont(withSize: 17) - @objc - public let titleSecondaryFont: UIFont = UIFont.ows_regularFont(withSize: 9) - @objc - public let subtitleFont: UIFont = UIFont.ows_regularFont(withSize: 12) - - private let titleLabel: UILabel - private let subtitleLabel: UILabel - private let avatarView: ConversationAvatarImageView - - @objc - public required init(thread: TSThread, contactsManager: OWSContactsManager) { - - let avatarView = ConversationAvatarImageView(thread: thread, diameter: 36, contactsManager: contactsManager) - self.avatarView = avatarView - - titleLabel = UILabel() - titleLabel.textColor = Theme.navbarTitleColor - titleLabel.lineBreakMode = .byTruncatingTail - titleLabel.font = titlePrimaryFont - titleLabel.setContentHuggingHigh() - - subtitleLabel = UILabel() - subtitleLabel.textColor = Theme.navbarTitleColor - subtitleLabel.lineBreakMode = .byTruncatingTail - subtitleLabel.font = subtitleFont - subtitleLabel.setContentHuggingHigh() - - let textRows = UIStackView(arrangedSubviews: [titleLabel, subtitleLabel]) - textRows.axis = .vertical - textRows.alignment = .leading - textRows.distribution = .fillProportionally - textRows.spacing = 0 - - textRows.layoutMargins = UIEdgeInsets(top: 0, left: 8, bottom: 0, right: 8) - textRows.isLayoutMarginsRelativeArrangement = true - - // low content hugging so that the text rows push container to the right bar button item(s) - textRows.setContentHuggingLow() - - super.init(frame: .zero) - - self.layoutMargins = UIEdgeInsets(top: 4, left: 2, bottom: 4, right: 2) - self.isLayoutMarginsRelativeArrangement = true - - self.axis = .horizontal - self.alignment = .center - self.spacing = 0 - self.addArrangedSubview(avatarView) - self.addArrangedSubview(textRows) - - let tapGesture = UITapGestureRecognizer(target: self, action: #selector(didTapView)) - self.addGestureRecognizer(tapGesture) - } - - required public init(coder: NSCoder) { - notImplemented() - } - - required public override init(frame: CGRect) { - notImplemented() - } - - public override var intrinsicContentSize: CGSize { - // Grow to fill as much of the navbar as possible. - return UIView.layoutFittingExpandedSize - } - - @objc - public func updateAvatar() { - self.avatarView.updateImage() - } - - // MARK: Delegate Methods - - @objc func didTapView(tapGesture: UITapGestureRecognizer) { - guard tapGesture.state == .recognized else { - return - } - - self.delegate?.didTapConversationHeaderView(self) - } -} diff --git a/Session/Signal/ConversationView/ConversationInputToolbar.m b/Session/Signal/ConversationView/ConversationInputToolbar.m index 8680e4947..48e28baf3 100644 --- a/Session/Signal/ConversationView/ConversationInputToolbar.m +++ b/Session/Signal/ConversationView/ConversationInputToolbar.m @@ -5,17 +5,14 @@ #import "ConversationInputToolbar.h" #import "ConversationInputTextView.h" #import "Environment.h" -#import "OWSContactsManager.h" #import "OWSMath.h" #import "Session-Swift.h" #import "UIColor+OWS.h" #import "UIFont+OWS.h" -#import "ViewControllerUtils.h" #import #import #import #import -#import #import NS_ASSUME_NONNULL_BEGIN diff --git a/Session/Signal/ConversationView/ConversationViewController.m b/Session/Signal/ConversationView/ConversationViewController.m index c9081489b..d58e70587 100644 --- a/Session/Signal/ConversationView/ConversationViewController.m +++ b/Session/Signal/ConversationView/ConversationViewController.m @@ -5,8 +5,6 @@ #import "ConversationViewController.h" #import "AppDelegate.h" #import -#import "BlockListViewController.h" -#import "ContactsViewHelper.h" #import "ConversationCollectionView.h" #import "ConversationInputTextView.h" #import "ConversationInputToolbar.h" @@ -16,10 +14,9 @@ #import "ConversationViewLayout.h" #import "ConversationViewModel.h" #import "DateUtil.h" -#import "FingerprintViewController.h" #import #import "OWSAudioPlayer.h" -#import "OWSContactOffersCell.h" + #import "OWSConversationSettingsViewController.h" #import "OWSConversationSettingsViewDelegate.h" #import "OWSDisappearingMessagesJob.h" @@ -40,7 +37,6 @@ #import #import "UIFont+OWS.h" #import "UIViewController+Permissions.h" -#import "ViewControllerUtils.h" #import #import #import @@ -50,38 +46,27 @@ #import #import #import -#import #import #import #import #import #import -#import #import #import -#import -#import #import #import -#import -#import -#import #import #import #import #import -#import -#import #import #import #import #import -#import #import #import #import #import -#import #import #import #import @@ -109,13 +94,8 @@ typedef enum : NSUInteger { #pragma mark - @interface ConversationViewController () )typingIndicators { return SSKEnvironment.shared.typingIndicators; @@ -428,11 +374,11 @@ typedef enum : NSUInteger { object:nil]; [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(handleCalculatingPoWNotification:) - name:NSNotification.calculatingPoW + name:NSNotification.calculatingMessagePoW object:nil]; [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(handleRoutingNotification:) - name:NSNotification.routing + name:NSNotification.encryptingMessage object:nil]; [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(handleMessageSendingNotification:) @@ -444,12 +390,7 @@ typedef enum : NSUInteger { object:nil]; [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(handleMessageFailedNotification:) - name:NSNotification.messageFailed - object:nil]; - // Device linking - [[NSNotificationCenter defaultCenter] addObserver:self - selector:@selector(handleUnexpectedDeviceLinkRequestReceivedNotification) - name:NSNotification.unexpectedDeviceLinkRequestReceived + name:NSNotification.messageSendingFailed object:nil]; } @@ -566,7 +507,7 @@ typedef enum : NSUInteger { _conversationStyle = [[ConversationStyle alloc] initWithThread:thread]; _conversationViewModel = - [[ConversationViewModel alloc] initWithThread:thread focusMessageIdOnOpen:focusMessageId isRSSFeed:self.isRSSFeed delegate:self]; + [[ConversationViewModel alloc] initWithThread:thread focusMessageIdOnOpen:focusMessageId delegate:self]; _searchController = [[ConversationSearchController alloc] initWithThread:thread]; _searchController.delegate = self; @@ -616,14 +557,7 @@ typedef enum : NSUInteger { } TSGroupThread *groupThread = (TSGroupThread *)self.thread; - return !groupThread.isLocalUserInGroup; -} - -- (BOOL)isRSSFeed -{ - if (![_thread isKindOfClass:[TSGroupThread class]]) { return NO; } - TSGroupThread *thread = (TSGroupThread *)self.thread; - return thread.isRSSFeed; + return !groupThread.isCurrentUserInGroup; } - (void)hideInputIfNeeded @@ -640,11 +574,6 @@ typedef enum : NSUInteger { } else { self.inputToolbar.hidden = NO; } - - // Loki: In RSS feeds, don't hide the input bar entirely; just hide the text field inside. - if (self.isRSSFeed) { - [self.inputToolbar hideInputMethod]; - } } - (void)viewDidLoad @@ -678,7 +607,7 @@ typedef enum : NSUInteger { if (self.thread.isGroupThread) { TSGroupThread *thread = (TSGroupThread *)self.thread; - if (!thread.isPublicChat) { return; } + if (!thread.isOpenGroup) { return; } __block SNOpenGroup *publicChat; [OWSPrimaryStorage.sharedManager.dbReadConnection readWithBlock:^(YapDatabaseReadTransaction *transaction) { publicChat = [LKDatabaseUtilities getPublicChatForThreadID:thread.uniqueId transaction:transaction]; @@ -795,8 +724,6 @@ typedef enum : NSUInteger { forCellWithReuseIdentifier:[OWSSystemMessageCell cellReuseIdentifier]]; [self.collectionView registerClass:[OWSTypingIndicatorCell class] forCellWithReuseIdentifier:[OWSTypingIndicatorCell cellReuseIdentifier]]; - [self.collectionView registerClass:[OWSContactOffersCell class] - forCellWithReuseIdentifier:[OWSContactOffersCell cellReuseIdentifier]]; [self.collectionView registerClass:[OWSMessageCell class] forCellWithReuseIdentifier:[OWSMessageCell cellReuseIdentifier]]; } @@ -866,10 +793,6 @@ typedef enum : NSUInteger { self.isViewVisible = YES; - // We should have already requested contact access at this point, so this should be a no-op - // unless it ever becomes possible to load this VC without going via the HomeViewController. - [self.contactsManager requestSystemContactsOnce]; - [self updateDisappearingMessagesConfiguration]; [self updateBarButtonItems]; @@ -1001,48 +924,35 @@ typedef enum : NSUInteger { [self ensureBannerState]; } -// Returns a collection of the group members who are "no longer verified". -- (NSArray *)noLongerVerifiedRecipientIds -{ - NSMutableArray *result = [NSMutableArray new]; - for (NSString *recipientId in self.thread.recipientIdentifiers) { - if ([[OWSIdentityManager sharedManager] verificationStateForRecipientId:recipientId] - == OWSVerificationStateNoLongerVerified) { - [result addObject:recipientId]; - } - } - return [result copy]; -} - - (void)updateSessionRestoreBanner { - BOOL isContactThread = [self.thread isKindOfClass:[TSContactThread class]]; - BOOL shouldDetachBanner = !isContactThread; - if (isContactThread) { - TSContactThread *thread = (TSContactThread *)self.thread; - if (thread.sessionRestoreDevices.count > 0) { - if (self.restoreSessionBannerView == nil) { - LKSessionRestorationView *bannerView = [[LKSessionRestorationView alloc] initWithThread:thread]; - [self.view addSubview:bannerView]; - [bannerView autoPinEdgeToSuperviewEdge:ALEdgeLeft withInset:LKValues.mediumSpacing]; - [bannerView autoPinEdgeToSuperviewEdge:ALEdgeTop withInset:LKValues.largeSpacing]; - [bannerView autoPinEdgeToSuperviewEdge:ALEdgeRight withInset:LKValues.mediumSpacing]; - [self.view layoutSubviews]; - self.restoreSessionBannerView = bannerView; - [bannerView setOnRestore:^{ - [self restoreSession]; - }]; - [bannerView setOnDismiss:^{ - [thread removeAllSessionRestoreDevicesWithTransaction:nil]; - }]; - } - } else { - shouldDetachBanner = true; - } - } - if (shouldDetachBanner && self.restoreSessionBannerView != nil) { - [self.restoreSessionBannerView removeFromSuperview]; - self.restoreSessionBannerView = nil; - } +// BOOL isContactThread = [self.thread isKindOfClass:[TSContactThread class]]; +// BOOL shouldDetachBanner = !isContactThread; +// if (isContactThread) { +// TSContactThread *thread = (TSContactThread *)self.thread; +// if (thread.sessionRestoreDevices.count > 0) { +// if (self.restoreSessionBannerView == nil) { +// LKSessionRestorationView *bannerView = [[LKSessionRestorationView alloc] initWithThread:thread]; +// [self.view addSubview:bannerView]; +// [bannerView autoPinEdgeToSuperviewEdge:ALEdgeLeft withInset:LKValues.mediumSpacing]; +// [bannerView autoPinEdgeToSuperviewEdge:ALEdgeTop withInset:LKValues.largeSpacing]; +// [bannerView autoPinEdgeToSuperviewEdge:ALEdgeRight withInset:LKValues.mediumSpacing]; +// [self.view layoutSubviews]; +// self.restoreSessionBannerView = bannerView; +// [bannerView setOnRestore:^{ +// [self restoreSession]; +// }]; +// [bannerView setOnDismiss:^{ +// [thread removeAllSessionRestoreDevicesWithTransaction:nil]; +// }]; +// } +// } else { +// shouldDetachBanner = true; +// } +// } +// if (shouldDetachBanner && self.restoreSessionBannerView != nil) { +// [self.restoreSessionBannerView removeFromSuperview]; +// self.restoreSessionBannerView = nil; +// } } - (void)ensureBannerState @@ -1056,32 +966,6 @@ typedef enum : NSUInteger { return; } - NSArray *noLongerVerifiedRecipientIds = [self noLongerVerifiedRecipientIds]; - - if (noLongerVerifiedRecipientIds.count > 0) { - NSString *message; - if (noLongerVerifiedRecipientIds.count > 1) { - message = NSLocalizedString(@"MESSAGES_VIEW_N_MEMBERS_NO_LONGER_VERIFIED", - @"Indicates that more than one member of this group conversation is no longer verified."); - } else { - NSString *recipientId = [noLongerVerifiedRecipientIds firstObject]; - NSString *displayName = [self.contactsManager displayNameForPhoneIdentifier:recipientId]; - NSString *format - = (self.isGroupConversation ? NSLocalizedString(@"MESSAGES_VIEW_1_MEMBER_NO_LONGER_VERIFIED_FORMAT", - @"Indicates that one member of this group conversation is no longer " - @"verified. Embeds {{user's name or phone number}}.") - : NSLocalizedString(@"MESSAGES_VIEW_CONTACT_NO_LONGER_VERIFIED_FORMAT", - @"Indicates that this 1:1 conversation is no longer verified. Embeds " - @"{{user's name or phone number}}.")); - message = [NSString stringWithFormat:format, displayName]; - } - - [self createBannerWithTitle:message - bannerColor:[UIColor ows_destructiveRedColor] - tapSelector:@selector(noLongerVerifiedBannerViewWasTapped:)]; - return; - } - NSString *blockStateMessage = nil; if ([self isBlockedConversation]) { if (self.isGroupConversation) { @@ -1093,20 +977,6 @@ typedef enum : NSUInteger { blockStateMessage = NSLocalizedString( @"MESSAGES_VIEW_CONTACT_BLOCKED", @"Indicates that this 1:1 conversation has been blocked."); } - } else if (self.isGroupConversation) { - /* - int blockedGroupMemberCount = [self blockedGroupMemberCount]; - if (blockedGroupMemberCount == 1) { - blockStateMessage = NSLocalizedString(@"MESSAGES_VIEW_GROUP_1_MEMBER_BLOCKED", - @"Indicates that a single member of this group has been blocked."); - } else if (blockedGroupMemberCount > 1) { - blockStateMessage = - [NSString stringWithFormat:NSLocalizedString(@"MESSAGES_VIEW_GROUP_N_MEMBERS_BLOCKED_FORMAT", - @"Indicates that some members of this group has been blocked. Embeds " - @"{{the number of blocked users in this group}}."), - [OWSFormat formatInt:blockedGroupMemberCount]]; - } - */ } if (blockStateMessage) { @@ -1115,17 +985,6 @@ typedef enum : NSUInteger { tapSelector:@selector(blockBannerViewWasTapped:)]; return; } - - /* - if ([ThreadUtil shouldShowGroupProfileBannerInThread:self.thread blockingManager:self.blockingManager]) { - [self createBannerWithTitle: - NSLocalizedString(@"MESSAGES_VIEW_GROUP_PROFILE_WHITELIST_BANNER", - @"Text for banner in group conversation view that offers to share your profile with this group.") - bannerColor:[UIColor ows_reminderDarkYellowColor] - tapSelector:@selector(groupProfileWhitelistBannerWasTapped:)]; - return; - } - */ } - (void)createBannerWithTitle:(NSString *)title bannerColor:(UIColor *)bannerColor tapSelector:(SEL)tapSelector @@ -1198,105 +1057,20 @@ typedef enum : NSUInteger { if ([self isBlockedConversation]) { // If this a blocked conversation, offer to unblock. [self showUnblockConversationUI:nil]; - } else if (self.isGroupConversation) { - // If this a group conversation with at least one blocked member, - // Show the block list view. - int blockedGroupMemberCount = [self blockedGroupMemberCount]; - if (blockedGroupMemberCount > 0) { - BlockListViewController *vc = [[BlockListViewController alloc] init]; - [self.navigationController pushViewController:vc animated:YES]; - } } } -- (void)groupProfileWhitelistBannerWasTapped:(UIGestureRecognizer *)sender -{ - if (sender.state != UIGestureRecognizerStateRecognized) { - return; - } - - [self presentAddThreadToProfileWhitelistWithSuccess:^{ - [self ensureBannerState]; - }]; -} - - (void)restoreSession { - if (![self.thread isKindOfClass:TSContactThread.class]) { return; } - TSContactThread *thread = (TSContactThread *)self.thread; - __weak ConversationViewController *weakSelf = self; - dispatch_async(dispatch_get_main_queue(), ^{ - [LKStorage writeSyncWithBlock:^(YapDatabaseReadWriteTransaction *transaction) { - [thread addSessionRestoreDevice:thread.contactIdentifier transaction:transaction]; - [LKSessionManagementProtocol startSessionResetInThread:thread transaction:transaction]; - }]; - [weakSelf updateSessionRestoreBanner]; - }); -} - -- (void)noLongerVerifiedBannerViewWasTapped:(UIGestureRecognizer *)sender -{ - if (sender.state == UIGestureRecognizerStateRecognized) { - NSArray *noLongerVerifiedRecipientIds = [self noLongerVerifiedRecipientIds]; - if (noLongerVerifiedRecipientIds.count < 1) { - return; - } - BOOL hasMultiple = noLongerVerifiedRecipientIds.count > 1; - - UIAlertController *actionSheet = [UIAlertController alertControllerWithTitle:nil - message:nil - preferredStyle:UIAlertControllerStyleActionSheet]; - - __weak ConversationViewController *weakSelf = self; - UIAlertAction *verifyAction = [UIAlertAction - actionWithTitle:(hasMultiple ? NSLocalizedString(@"VERIFY_PRIVACY_MULTIPLE", - @"Label for button or row which allows users to verify the safety " - @"numbers of multiple users.") - : NSLocalizedString(@"VERIFY_PRIVACY", - @"Label for button or row which allows users to verify the safety " - @"number of another user.")) - style:UIAlertActionStyleDefault - handler:^(UIAlertAction *action) { - [weakSelf showNoLongerVerifiedUI]; - }]; - [actionSheet addAction:verifyAction]; - - UIAlertAction *dismissAction = - [UIAlertAction actionWithTitle:CommonStrings.dismissButton - accessibilityIdentifier:ACCESSIBILITY_IDENTIFIER_WITH_NAME(self, @"dismiss") - style:UIAlertActionStyleCancel - handler:^(UIAlertAction *action) { - [weakSelf resetVerificationStateToDefault]; - }]; - [actionSheet addAction:dismissAction]; - - [self dismissKeyBoard]; - [self presentAlert:actionSheet]; - } -} - -- (void)resetVerificationStateToDefault -{ - OWSAssertIsOnMainThread(); - - NSArray *noLongerVerifiedRecipientIds = [self noLongerVerifiedRecipientIds]; - for (NSString *recipientId in noLongerVerifiedRecipientIds) { - OWSAssertDebug(recipientId.length > 0); - - OWSRecipientIdentity *_Nullable recipientIdentity = - [[OWSIdentityManager sharedManager] recipientIdentityForRecipientId:recipientId]; - OWSAssertDebug(recipientIdentity); - - NSData *identityKey = recipientIdentity.identityKey; - OWSAssertDebug(identityKey.length > 0); - if (identityKey.length < 1) { - continue; - } - - [OWSIdentityManager.sharedManager setVerificationState:OWSVerificationStateDefault - identityKey:identityKey - recipientId:recipientId - isUserInitiatedChange:YES]; - } +// if (![self.thread isKindOfClass:TSContactThread.class]) { return; } +// TSContactThread *thread = (TSContactThread *)self.thread; +// __weak ConversationViewController *weakSelf = self; +// dispatch_async(dispatch_get_main_queue(), ^{ +// [LKStorage writeSyncWithBlock:^(YapDatabaseReadWriteTransaction *transaction) { +// [thread addSessionRestoreDevice:thread.contactIdentifier transaction:transaction]; +// [LKSessionManagementProtocol startSessionResetInThread:thread transaction:transaction]; +// }]; +// [weakSelf updateSessionRestoreBanner]; +// }); } - (void)showUnblockConversationUI:(nullable BlockActionCompletionBlock)completionBlock @@ -1308,7 +1082,6 @@ typedef enum : NSUInteger { [BlockListUIUtils showUnblockThreadActionSheet:self.thread fromViewController:self blockingManager:self.blockingManager - contactsManager:self.contactsManager completionBlock:completionBlock]; [UIView setAnimationsEnabled:YES]; @@ -1370,7 +1143,6 @@ typedef enum : NSUInteger { // recover status bar when returning from PhotoPicker, which is dark (uses light status bar) [self setNeedsStatusBarAppearanceUpdate]; - [ProfileFetcherJob runWithThread:self.thread]; [self markVisibleMessagesAsRead]; [self startReadTimer]; [self autoLoadMoreIfNecessary]; @@ -1422,12 +1194,6 @@ typedef enum : NSUInteger { case ConversationViewActionCompose: [self popKeyBoard]; break; - case ConversationViewActionAudioCall: - [self startAudioCall]; - break; - case ConversationViewActionVideoCall: - [self startVideoCall]; - break; } // Clear the "on open" state after the view has been presented. @@ -1567,52 +1333,6 @@ typedef enum : NSUInteger { const CGFloat kBarButtonSize = 44; NSMutableArray *barButtons = [NSMutableArray new]; - if ([self canCall]) { - // We use UIButtons with [UIBarButtonItem initWithCustomView:...] instead of - // UIBarButtonItem in order to ensure that these buttons are spaced tightly. - // The contents of the navigation bar are cramped in this view. - UIButton *callButton = [UIButton buttonWithType:UIButtonTypeCustom]; - UIImage *image = [[UIImage imageNamed:@"button_phone_white"] imageWithRenderingMode:UIImageRenderingModeAlwaysTemplate]; - [callButton setImage:image forState:UIControlStateNormal]; - - if (OWSWindowManager.sharedManager.hasCall) { - callButton.enabled = NO; - callButton.userInteractionEnabled = NO; - callButton.tintColor = [Theme.navbarIconColor colorWithAlphaComponent:0.7]; - } else { - callButton.enabled = YES; - callButton.userInteractionEnabled = YES; - callButton.tintColor = Theme.navbarIconColor; - } - - UIEdgeInsets imageEdgeInsets = UIEdgeInsetsZero; - - // We normally would want to use left and right insets that ensure the button - // is square and the icon is centered. However UINavigationBar doesn't offer us - // control over the margins and spacing of its content, and the buttons end up - // too far apart and too far from the edge of the screen. So we use a smaller - // right inset tighten up the layout. - BOOL hasCompactHeader = self.traitCollection.verticalSizeClass == UIUserInterfaceSizeClassCompact; - if (!hasCompactHeader) { - imageEdgeInsets.left = round((kBarButtonSize - image.size.width) * 0.5f); - imageEdgeInsets.right = round((kBarButtonSize - (image.size.width + imageEdgeInsets.left)) * 0.5f); - imageEdgeInsets.top = round((kBarButtonSize - image.size.height) * 0.5f); - imageEdgeInsets.bottom = round(kBarButtonSize - (image.size.height + imageEdgeInsets.top)); - } - callButton.imageEdgeInsets = imageEdgeInsets; - callButton.accessibilityLabel = NSLocalizedString(@"CALL_LABEL", "Accessibility label for placing call button"); - [callButton addTarget:self action:@selector(startAudioCall) forControlEvents:UIControlEventTouchUpInside]; - callButton.frame = CGRectMake(0, - 0, - round(image.size.width + imageEdgeInsets.left + imageEdgeInsets.right), - round(image.size.height + imageEdgeInsets.top + imageEdgeInsets.bottom)); - // Loki: Original code - // ======== -// [barButtons -// addObject:[[UIBarButtonItem alloc] initWithCustomView:callButton -// accessibilityIdentifier:ACCESSIBILITY_IDENTIFIER_WITH_NAME(self, @"call")]]; - // ======== - } if (self.disappearingMessagesConfiguration.isEnabled) { DisappearingTimerConfigurationView *timerView = [[DisappearingTimerConfigurationView alloc] @@ -1638,102 +1358,6 @@ typedef enum : NSUInteger { self.navigationItem.rightBarButtonItems = [barButtons copy]; } -#pragma mark - Identity - -/** - * Shows confirmation dialog if at least one of the recipient id's is not confirmed. - * - * returns YES if an alert was shown - * NO if there were no unconfirmed identities - */ -- (BOOL)showSafetyNumberConfirmationIfNecessaryWithConfirmationText:(NSString *)confirmationText - completion:(void (^)(BOOL didConfirmIdentity))completionHandler -{ - return [SafetyNumberConfirmationAlert presentAlertIfNecessaryWithRecipientIds:self.thread.recipientIdentifiers - confirmationText:confirmationText - contactsManager:self.contactsManager - completion:^(BOOL didShowAlert) { - // Pre iOS-11, the keyboard and inputAccessoryView will obscure the alert if the keyboard is up when the - // alert is presented, so after hiding it, we regain first responder here. - if (@available(iOS 11.0, *)) { - // do nothing - } else { - [self becomeFirstResponder]; - } - completionHandler(didShowAlert); - } - beforePresentationHandler:^(void) { - if (@available(iOS 11.0, *)) { - // do nothing - } else { - // Pre iOS-11, the keyboard and inputAccessoryView will obscure the alert if the keyboard is up when the - // alert is presented. - [self dismissKeyBoard]; - [self resignFirstResponder]; - } - }]; -} - -- (void)showFingerprintWithRecipientId:(NSString *)recipientId -{ - // Ensure keyboard isn't hiding the "safety numbers changed" interaction when we - // return from FingerprintViewController. - [self dismissKeyBoard]; - - [FingerprintViewController presentFromViewController:self recipientId:recipientId]; -} - -#pragma mark - Calls - -- (void)startAudioCall -{ - [self callWithVideo:NO]; -} - -- (void)startVideoCall -{ - [self callWithVideo:YES]; -} - -- (void)callWithVideo:(BOOL)isVideo -{ - OWSAssertDebug([self.thread isKindOfClass:[TSContactThread class]]); - - if (![self canCall]) { - OWSLogWarn(@"Tried to initiate a call but thread is not callable."); - return; - } - - __weak ConversationViewController *weakSelf = self; - if ([self isBlockedConversation]) { - [self showUnblockConversationUI:^(BOOL isBlocked) { - if (!isBlocked) { - [weakSelf callWithVideo:isVideo]; - } - }]; - return; - } - - BOOL didShowSNAlert = - [self showSafetyNumberConfirmationIfNecessaryWithConfirmationText:[CallStrings confirmAndCallButtonTitle] - completion:^(BOOL didConfirmIdentity) { - if (didConfirmIdentity) { - [weakSelf callWithVideo:isVideo]; - } - }]; - if (didShowSNAlert) { - return; - } - -// [self.outboundCallInitiator initiateCallWithRecipientId:self.thread.contactIdentifier isVideo:isVideo]; -} - -- (BOOL)canCall -{ - return !(self.isGroupConversation || - [((TSContactThread *)self.thread).contactIdentifier isEqualToString:self.tsAccountManager.localNumber]); -} - #pragma mark - Dynamic Text /** @@ -1752,18 +1376,6 @@ typedef enum : NSUInteger { #pragma mark - Actions -- (void)showNoLongerVerifiedUI -{ - NSArray *noLongerVerifiedRecipientIds = [self noLongerVerifiedRecipientIds]; - if (noLongerVerifiedRecipientIds.count > 1) { - [self showConversationSettingsAndShowVerification:YES]; - } else if (noLongerVerifiedRecipientIds.count == 1) { - // Pick one in an arbitrary but deterministic manner. - NSString *recipientId = noLongerVerifiedRecipientIds.lastObject; - [self showFingerprintWithRecipientId:recipientId]; - } -} - - (void)showConversationSettings { [self showConversationSettingsAndShowVerification:NO]; @@ -1878,42 +1490,24 @@ typedef enum : NSUInteger { }]; [actionSheet addAction:deleteMessageAction]; - UIAlertAction *resendMessageAction = [UIAlertAction - actionWithTitle:NSLocalizedString(@"SEND_AGAIN_BUTTON", @"") - accessibilityIdentifier:ACCESSIBILITY_IDENTIFIER_WITH_NAME(self, @"send_again") - style:UIAlertActionStyleDefault - handler:^(UIAlertAction *action) { - [LKStorage writeWithBlock:^(YapDatabaseReadWriteTransaction *_Nonnull transaction) { - [self.messageSenderJobQueue addMessage:message transaction:transaction]; - }]; - }]; + // TODO TODO TODO + +// UIAlertAction *resendMessageAction = [UIAlertAction +// actionWithTitle:NSLocalizedString(@"SEND_AGAIN_BUTTON", @"") +// accessibilityIdentifier:ACCESSIBILITY_IDENTIFIER_WITH_NAME(self, @"send_again") +// style:UIAlertActionStyleDefault +// handler:^(UIAlertAction *action) { +// [LKStorage writeWithBlock:^(YapDatabaseReadWriteTransaction *_Nonnull transaction) { +// [self.messageSenderJobQueue addMessage:message transaction:transaction]; +// }]; +// }]; - [actionSheet addAction:resendMessageAction]; +// [actionSheet addAction:resendMessageAction]; [self dismissKeyBoard]; [self presentAlert:actionSheet]; } -- (void)tappedNonBlockingIdentityChangeForRecipientId:(nullable NSString *)signalIdParam -{ - if (signalIdParam == nil) { - if (self.thread.isGroupThread) { - // Before 2.13 we didn't track the recipient id in the identity change error. - OWSLogWarn(@"Ignoring tap on legacy nonblocking identity change since it has no signal id"); - return; - - } else { - OWSLogInfo(@"Assuming tap on legacy nonblocking identity change corresponds to current contact thread: %@", - self.thread.contactIdentifier); - signalIdParam = self.thread.contactIdentifier; - } - } - - NSString *signalId = signalIdParam; - - [self showFingerprintWithRecipientId:signalId]; -} - - (void)tappedCorruptedMessage:(TSErrorMessage *)message { NSString *alertMessage = [NSString @@ -1946,85 +1540,6 @@ typedef enum : NSUInteger { [self presentAlert:alert]; } -- (void)tappedInvalidIdentityKeyErrorMessage:(TSInvalidIdentityKeyErrorMessage *)errorMessage -{ - NSString *keyOwner = [self.contactsManager displayNameForPhoneIdentifier:errorMessage.theirSignalId]; - NSString *titleFormat = NSLocalizedString(@"SAFETY_NUMBERS_ACTIONSHEET_TITLE", @"Action sheet heading"); - NSString *titleText = [NSString stringWithFormat:titleFormat, keyOwner]; - - UIAlertController *actionSheet = [UIAlertController alertControllerWithTitle:titleText - message:nil - preferredStyle:UIAlertControllerStyleActionSheet]; - - [actionSheet addAction:[OWSAlerts cancelAction]]; - - UIAlertAction *showSafteyNumberAction = - [UIAlertAction actionWithTitle:NSLocalizedString(@"SHOW_SAFETY_NUMBER_ACTION", @"Action sheet item") - accessibilityIdentifier:ACCESSIBILITY_IDENTIFIER_WITH_NAME(self, @"show_safety_number") - style:UIAlertActionStyleDefault - handler:^(UIAlertAction *action) { - OWSLogInfo(@"Remote Key Changed actions: Show fingerprint display"); - [self showFingerprintWithRecipientId:errorMessage.theirSignalId]; - }]; - [actionSheet addAction:showSafteyNumberAction]; - - UIAlertAction *acceptSafetyNumberAction = - [UIAlertAction actionWithTitle:NSLocalizedString(@"ACCEPT_NEW_IDENTITY_ACTION", @"Action sheet item") - accessibilityIdentifier:ACCESSIBILITY_IDENTIFIER_WITH_NAME(self, @"accept_safety_number") - style:UIAlertActionStyleDefault - handler:^(UIAlertAction *action) { - OWSLogInfo(@"Remote Key Changed actions: Accepted new identity key"); - - // DEPRECATED: we're no longer creating these incoming SN error's per message, - // but there will be some legacy ones in the wild, behind which await - // as-of-yet-undecrypted messages -#pragma clang diagnostic push -#pragma clang diagnostic ignored "-Wdeprecated-declarations" - if ([errorMessage isKindOfClass:[TSInvalidIdentityKeyReceivingErrorMessage class]]) { - // Deliberately crash if the user fails to explicitly accept the new identity - // key. In practice we haven't been creating these messages in over a year. - [errorMessage throws_acceptNewIdentityKey]; -#pragma clang diagnostic pop - - } - }]; - [actionSheet addAction:acceptSafetyNumberAction]; - - [self dismissKeyBoard]; - [self presentAlert:actionSheet]; -} - -- (void)handleCallTap:(TSCall *)call -{ - OWSAssertDebug(call); - - if (![self.thread isKindOfClass:[TSContactThread class]]) { - OWSFailDebug(@"unexpected thread: %@", self.thread); - return; - } - - TSContactThread *contactThread = (TSContactThread *)self.thread; - NSString *displayName = [self.contactsManager displayNameForPhoneIdentifier:contactThread.contactIdentifier]; - - UIAlertController *alert = [UIAlertController - alertControllerWithTitle:[CallStrings callBackAlertTitle] - message:[NSString stringWithFormat:[CallStrings callBackAlertMessageFormat], displayName] - preferredStyle:UIAlertControllerStyleAlert]; - - __weak ConversationViewController *weakSelf = self; - UIAlertAction *callAction = [UIAlertAction actionWithTitle:[CallStrings callBackAlertCallButton] - accessibilityIdentifier:ACCESSIBILITY_IDENTIFIER_WITH_NAME(self, @"call_back") - style:UIAlertActionStyleDefault - handler:^(UIAlertAction *action) { - [weakSelf startAudioCall]; - }]; - [alert addAction:callAction]; - [alert addAction:[OWSAlerts cancelAction]]; - - [self dismissKeyBoard]; - [self presentAlert:alert]; -} - #pragma mark - MessageActionsDelegate - (void)messageActionsShowDetailsForItem:(id)conversationViewItem @@ -2276,104 +1791,6 @@ typedef enum : NSUInteger { [[OWSWindowManager sharedManager] showMenuActionsWindow:menuActionsViewController]; } -- (NSAttributedString *)attributedContactOrProfileNameForPhoneIdentifier:(NSString *)recipientId -{ - OWSAssertIsOnMainThread(); - OWSAssertDebug(recipientId.length > 0); - - return [self.contactsManager attributedContactOrProfileNameForPhoneIdentifier:recipientId]; -} - -- (void)tappedUnknownContactBlockOfferMessage:(OWSContactOffersInteraction *)interaction -{ - if (![self.thread isKindOfClass:[TSContactThread class]]) { - OWSFailDebug(@"unexpected thread: %@", self.thread); - return; - } - TSContactThread *contactThread = (TSContactThread *)self.thread; - - NSString *displayName = [self.contactsManager displayNameForPhoneIdentifier:interaction.recipientId]; - NSString *title = - [NSString stringWithFormat:NSLocalizedString(@"BLOCK_OFFER_ACTIONSHEET_TITLE_FORMAT", - @"Title format for action sheet that offers to block an unknown user." - @"Embeds {{the unknown user's name or phone number}}."), - [BlockListUIUtils formatDisplayNameForAlertTitle:displayName]]; - - UIAlertController *actionSheet = - [UIAlertController alertControllerWithTitle:title message:nil preferredStyle:UIAlertControllerStyleActionSheet]; - - [actionSheet addAction:[OWSAlerts cancelAction]]; - - UIAlertAction *blockAction = - [UIAlertAction actionWithTitle:NSLocalizedString(@"BLOCK_OFFER_ACTIONSHEET_BLOCK_ACTION", - @"Action sheet that will block an unknown user.") - accessibilityIdentifier:ACCESSIBILITY_IDENTIFIER_WITH_NAME(self, @"block_user") - style:UIAlertActionStyleDestructive - handler:^(UIAlertAction *action) { - OWSLogInfo(@"Blocking an unknown user."); - [self.blockingManager addBlockedPhoneNumber:interaction.recipientId]; - // Delete the offers. - [LKStorage writeSyncWithBlock:^(YapDatabaseReadWriteTransaction *transaction) { - contactThread.hasDismissedOffers = YES; - [contactThread saveWithTransaction:transaction]; - [interaction removeWithTransaction:transaction]; - }]; - }]; - [actionSheet addAction:blockAction]; - - [self dismissKeyBoard]; - [self presentAlert:actionSheet]; -} - -- (void)tappedAddToContactsOfferMessage:(OWSContactOffersInteraction *)interaction -{ - if (!self.contactsManager.supportsContactEditing) { - OWSFailDebug(@"Contact editing not supported"); - return; - } - if (![self.thread isKindOfClass:[TSContactThread class]]) { - OWSFailDebug(@"unexpected thread: %@", [self.thread class]); - return; - } - TSContactThread *contactThread = (TSContactThread *)self.thread; - [self.contactsViewHelper presentContactViewControllerForRecipientId:contactThread.contactIdentifier - fromViewController:self - editImmediately:YES]; - - // Delete the offers. - [LKStorage writeSyncWithBlock:^(YapDatabaseReadWriteTransaction *transaction) { - contactThread.hasDismissedOffers = YES; - [contactThread saveWithTransaction:transaction]; - [interaction removeWithTransaction:transaction]; - }]; -} - -- (void)tappedAddToProfileWhitelistOfferMessage:(OWSContactOffersInteraction *)interaction -{ - // This is accessed via the contact offer. Group whitelisting happens via a different interaction. - if (![self.thread isKindOfClass:[TSContactThread class]]) { - OWSFailDebug(@"unexpected thread: %@", [self.thread class]); - return; - } - TSContactThread *contactThread = (TSContactThread *)self.thread; - - [self presentAddThreadToProfileWhitelistWithSuccess:^() { - // Delete the offers. - [LKStorage writeSyncWithBlock:^(YapDatabaseReadWriteTransaction *transaction) { - contactThread.hasDismissedOffers = YES; - [contactThread saveWithTransaction:transaction]; - [interaction removeWithTransaction:transaction]; - }]; - }]; -} - -- (void)presentAddThreadToProfileWhitelistWithSuccess:(void (^)(void))successHandler -{ - [[OWSProfileManager sharedManager] presentAddThreadToProfileWhitelist:self.thread - fromViewController:self - success:successHandler]; -} - #pragma mark - OWSMessageBubbleViewDelegate - (void)didTapImageViewItem:(id)viewItem @@ -2471,41 +1888,6 @@ typedef enum : NSUInteger { [self.navigationController pushViewController:viewController animated:YES]; } -- (void)didTapContactShareViewItem:(id)conversationItem -{ - OWSAssertIsOnMainThread(); - OWSAssertDebug(conversationItem); - OWSAssertDebug(conversationItem.contactShare); - OWSAssertDebug([conversationItem.interaction isKindOfClass:[TSMessage class]]); - - ContactViewController *view = [[ContactViewController alloc] initWithContactShare:conversationItem.contactShare]; - [self.navigationController pushViewController:view animated:YES]; -} - -- (void)didTapSendMessageToContactShare:(ContactShareViewModel *)contactShare -{ - OWSAssertIsOnMainThread(); - OWSAssertDebug(contactShare); - - [self.contactShareViewHelper sendMessageWithContactShare:contactShare fromViewController:self]; -} - -- (void)didTapSendInviteToContactShare:(ContactShareViewModel *)contactShare -{ - OWSAssertIsOnMainThread(); - OWSAssertDebug(contactShare); - - [self.contactShareViewHelper showInviteContactWithContactShare:contactShare fromViewController:self]; -} - -- (void)didTapShowAddToContactUIForContactShare:(ContactShareViewModel *)contactShare -{ - OWSAssertIsOnMainThread(); - OWSAssertDebug(contactShare); - - [self.contactShareViewHelper showAddToContactsWithContactShare:contactShare fromViewController:self]; -} - - (void)didTapFailedIncomingAttachment:(id)viewItem { OWSAssertIsOnMainThread(); @@ -2753,22 +2135,6 @@ typedef enum : NSUInteger { self.scrollDownButton.hidden = !shouldShowScrollDownButton; } -#pragma mark - Attachment Picking: Contacts - -- (void)chooseContactForSending -{ - ContactsPicker *contactsPicker = - [[ContactsPicker alloc] initWithAllowsMultipleSelection:NO subtitleCellType:SubtitleCellValueNone]; - contactsPicker.contactsPickerDelegate = self; - contactsPicker.title - = NSLocalizedString(@"CONTACT_PICKER_TITLE", @"navbar title for contact picker when sharing a contact"); - - OWSNavigationController *navigationController = - [[OWSNavigationController alloc] initWithRootViewController:contactsPicker]; - [self dismissKeyBoard]; - [self presentViewController:navigationController animated:YES completion:nil]; -} - #pragma mark - Attachment Picking: Documents - (void)showAttachmentDocumentPickerMenu @@ -2815,7 +2181,7 @@ typedef enum : NSUInteger { - (void)showGifPicker { GifPickerViewController *view = - [[GifPickerViewController alloc] initWithThread:self.thread messageSender:self.messageSender]; + [[GifPickerViewController alloc] initWithThread:self.thread]; view.delegate = self; OWSNavigationController *navigationController = [[OWSNavigationController alloc] initWithRootViewController:view]; @@ -2831,7 +2197,6 @@ typedef enum : NSUInteger { [self showApprovalDialogForAttachment:attachment]; - [ThreadUtil addThreadToProfileWhitelistIfEmptyContactThread:self.thread]; [self.conversationViewModel ensureDynamicInteractionsAndUpdateIfNecessary:YES]; } @@ -3210,34 +2575,6 @@ typedef enum : NSUInteger { } } -- (void)sendContactShare:(ContactShareViewModel *)contactShare -{ - OWSAssertIsOnMainThread(); - OWSAssertDebug(contactShare); - - OWSLogVerbose(@"Sending contact share."); - - BOOL didAddToProfileWhitelist = [ThreadUtil addThreadToProfileWhitelistIfEmptyContactThread:self.thread]; - - [self.editingDatabaseConnection - asyncReadWriteWithBlock:^(YapDatabaseReadWriteTransaction *transaction) { - // TODO - in line with QuotedReply and other message attachments, saving should happen as part of sending - // preparation rather than duplicated here and in the SAE - if (contactShare.avatarImage) { - [contactShare.dbRecord saveAvatarImage:contactShare.avatarImage transaction:transaction]; - } - } - completionBlock:^{ - TSOutgoingMessage *message = - [ThreadUtil enqueueMessageWithContactShare:contactShare.dbRecord inThread:self.thread]; - [self messageWasSent:message]; - - if (didAddToProfileWhitelist) { - [self.conversationViewModel ensureDynamicInteractionsAndUpdateIfNecessary:YES]; - } - }]; -} - - (void)showApprovalDialogAfterProcessingVideoURL:(NSURL *)movieURL filename:(nullable NSString *)filename { OWSAssertIsOnMainThread(); @@ -3486,19 +2823,6 @@ typedef enum : NSUInteger { return; } - BOOL didShowSNAlert = - [self showSafetyNumberConfirmationIfNecessaryWithConfirmationText: - NSLocalizedString(@"CONFIRMATION_TITLE", @"Generic button text to proceed with an action") - completion:^(BOOL didConfirmIdentity) { - if (didConfirmIdentity) { - [weakSelf attachmentButtonPressed]; - } - }]; - if (didShowSNAlert) { - return; - } - - UIAlertController *actionSheet = [UIAlertController alertControllerWithTitle:nil message:nil preferredStyle:UIAlertControllerStyleActionSheet]; @@ -3682,7 +3006,7 @@ typedef enum : NSUInteger { groupThread = [TSGroupThread getOrCreateThreadWithGroupModel:newGroupModel transaction:transaction]; NSString *updateGroupInfo = - [groupThread.groupModel getInfoStringAboutUpdateTo:newGroupModel contactsManager:self.contactsManager]; + [groupThread.groupModel getInfoStringAboutUpdateTo:newGroupModel]; groupThread.groupModel = newGroupModel; [groupThread saveWithTransaction:transaction]; @@ -3702,32 +3026,38 @@ typedef enum : NSUInteger { // DURABLE CLEANUP - currently one caller uses the completion handler to delete the tappable error message // which causes this code to be called. Once we're more aggressive about durable sending retry, // we could get rid of this "retryable tappable error message". - [self.messageSender sendTemporaryAttachment:dataSource - contentType:OWSMimeTypeImagePng - inMessage:message - success:^{ - OWSLogDebug(@"Successfully sent group update with avatar"); - if (successCompletion) { - successCompletion(); - } - } - failure:^(NSError *error) { - OWSLogError(@"Failed to send group avatar update with error: %@", error); - }]; + + // TODO TODO TODO + +// [self.messageSender sendTemporaryAttachment:dataSource +// contentType:OWSMimeTypeImagePng +// inMessage:message +// success:^{ +// OWSLogDebug(@"Successfully sent group update with avatar"); +// if (successCompletion) { +// successCompletion(); +// } +// } +// failure:^(NSError *error) { +// OWSLogError(@"Failed to send group avatar update with error: %@", error); +// }]; } else { // DURABLE CLEANUP - currently one caller uses the completion handler to delete the tappable error message // which causes this code to be called. Once we're more aggressive about durable sending retry, // we could get rid of this "retryable tappable error message". - [self.messageSender sendMessage:message - success:^{ - OWSLogDebug(@"Successfully sent group update"); - if (successCompletion) { - successCompletion(); - } - } - failure:^(NSError *error) { - OWSLogError(@"Failed to send group update with error: %@", error); - }]; + + // TODO TODO TODO + +// [self.messageSender sendMessage:message +// success:^{ +// OWSLogDebug(@"Successfully sent group update"); +// if (successCompletion) { +// successCompletion(); +// } +// } +// failure:^(NSError *error) { +// OWSLogError(@"Failed to send group update with error: %@", error); +// }]; } self.thread = groupThread; @@ -3775,13 +3105,6 @@ typedef enum : NSUInteger { return @[]; } -#pragma mark - ConversationHeaderViewDelegate - -- (void)didTapConversationHeaderView:(ConversationHeaderView *)conversationHeaderView -{ - [self showConversationSettings]; -} - #ifdef USE_DEBUG_UI - (void)navigationTitleLongPressed:(UIGestureRecognizer *)gestureRecognizer { @@ -3909,18 +3232,6 @@ typedef enum : NSUInteger { return; } - BOOL didShowSNAlert = [self - showSafetyNumberConfirmationIfNecessaryWithConfirmationText:[SafetyNumberStrings confirmSendButton] - completion:^(BOOL didConfirmIdentity) { - if (didConfirmIdentity) { - [weakSelf tryToSendAttachments:attachments - messageText:messageText]; - } - }]; - if (didShowSNAlert) { - return; - } - for (SignalAttachment *attachment in attachments) { if ([attachment hasError]) { OWSLogWarn(@"Invalid attachment: %@.", attachment ? [attachment errorName] : @"Missing data"); @@ -3929,24 +3240,21 @@ typedef enum : NSUInteger { } } - BOOL didAddToProfileWhitelist = [ThreadUtil addThreadToProfileWhitelistIfEmptyContactThread:self.thread]; - __block TSOutgoingMessage *message; - [self.uiDatabaseConnection readWithBlock:^(YapDatabaseReadTransaction *_Nonnull transaction) { - message = [ThreadUtil enqueueMessageWithText:messageText - mediaAttachments:attachments - inThread:self.thread - quotedReplyModel:self.inputToolbar.quotedReply - linkPreviewDraft:nil - transaction:transaction]; - }]; + + // TODO TODO TODO + +// [self.uiDatabaseConnection readWithBlock:^(YapDatabaseReadTransaction *_Nonnull transaction) { +// message = [ThreadUtil enqueueMessageWithText:messageText +// mediaAttachments:attachments +// inThread:self.thread +// quotedReplyModel:self.inputToolbar.quotedReply +// linkPreviewDraft:nil +// transaction:transaction]; +// }]; [self messageWasSent:message]; - if (didAddToProfileWhitelist) { - [self.conversationViewModel ensureDynamicInteractionsAndUpdateIfNecessary:YES]; - } - if ([self.thread isKindOfClass:TSContactThread.class]) { [LKStorage writeWithBlock:^(YapDatabaseReadWriteTransaction *transaction) { [LKSessionManagementProtocol sendSessionRequestIfNeededToPublicKey:self.thread.contactIdentifier transaction:transaction]; @@ -4512,18 +3820,6 @@ typedef enum : NSUInteger { return; } - BOOL didShowSNAlert = - [self showSafetyNumberConfirmationIfNecessaryWithConfirmationText:[SafetyNumberStrings confirmSendButton] - completion:^(BOOL didConfirmIdentity) { - if (didConfirmIdentity) { - [weakSelf tryToSendTextMessage:text - updateKeyboardState:NO]; - } - }]; - if (didShowSNAlert) { - return; - } - text = [text ows_stripped]; if (text.length < 1) { @@ -4534,16 +3830,19 @@ typedef enum : NSUInteger { // // We convert large text messages to attachments // which are presented as normal text messages. - BOOL didAddToProfileWhitelist = [ThreadUtil addThreadToProfileWhitelistIfEmptyContactThread:self.thread]; + + // TODO TODO TODO + +// BOOL didAddToProfileWhitelist = [ThreadUtil addThreadToProfileWhitelistIfEmptyContactThread:self.thread]; __block TSOutgoingMessage *message; - [self.uiDatabaseConnection readWithBlock:^(YapDatabaseReadTransaction *_Nonnull transaction) { - message = [ThreadUtil enqueueMessageWithText:text - inThread:self.thread - quotedReplyModel:self.inputToolbar.quotedReply - linkPreviewDraft:self.inputToolbar.linkPreviewDraft - transaction:transaction]; - }]; - [self.conversationViewModel appendUnsavedOutgoingTextMessage:message]; +// [self.uiDatabaseConnection readWithBlock:^(YapDatabaseReadTransaction *_Nonnull transaction) { +// message = [ThreadUtil enqueueMessageWithText:text +// inThread:self.thread +// quotedReplyModel:self.inputToolbar.quotedReply +// linkPreviewDraft:self.inputToolbar.linkPreviewDraft +// transaction:transaction]; +// }]; +// [self.conversationViewModel appendUnsavedOutgoingTextMessage:message]; [self messageWasSent:message]; @@ -4572,10 +3871,6 @@ typedef enum : NSUInteger { [self.thread setDraft:@"" transaction:transaction]; }]; - if (didAddToProfileWhitelist) { - [self.conversationViewModel ensureDynamicInteractionsAndUpdateIfNecessary:YES]; - } - if ([self.thread isKindOfClass:TSContactThread.class]) { [LKStorage writeWithBlock:^(YapDatabaseReadWriteTransaction *transaction) { [LKSessionManagementProtocol sendSessionRequestIfNeededToPublicKey:self.thread.contactIdentifier transaction:transaction]; @@ -4891,104 +4186,6 @@ typedef enum : NSUInteger { return maxContentOffsetY; } -#pragma mark - ContactsPickerDelegate - -- (void)contactsPickerDidCancel:(ContactsPicker *)contactsPicker -{ - OWSLogDebug(@""); - [self dismissViewControllerAnimated:YES completion:nil]; -} - -- (void)contactsPicker:(ContactsPicker *)contactsPicker contactFetchDidFail:(NSError *)error -{ - OWSLogDebug(@"with error %@", error); - [self dismissViewControllerAnimated:YES completion:nil]; -} - -- (void)contactsPicker:(ContactsPicker *)contactsPicker didSelectContact:(Contact *)contact -{ - OWSAssertDebug(contact); - - CNContact *_Nullable cnContact = [self.contactsManager cnContactWithId:contact.cnContactId]; - if (!cnContact) { - OWSFailDebug(@"Could not load system contact."); - return; - } - - OWSLogDebug(@"with contact: %@", contact); - - OWSContact *_Nullable contactShareRecord = [OWSContacts contactForSystemContact:cnContact]; - if (!contactShareRecord) { - OWSFailDebug(@"Could not convert system contact."); - return; - } - - BOOL isProfileAvatar = NO; - NSData *_Nullable avatarImageData = [self.contactsManager avatarDataForCNContactId:cnContact.identifier]; - for (NSString *recipientId in contact.textSecureIdentifiers) { - if (avatarImageData) { - break; - } - avatarImageData = [self.contactsManager profileImageDataForPhoneIdentifier:recipientId]; - if (avatarImageData) { - isProfileAvatar = YES; - } - } - contactShareRecord.isProfileAvatar = isProfileAvatar; - - ContactShareViewModel *contactShare = - [[ContactShareViewModel alloc] initWithContactShareRecord:contactShareRecord avatarImageData:avatarImageData]; - - // TODO: We should probably show this in the same navigation view controller. - ContactShareApprovalViewController *approveContactShare = - [[ContactShareApprovalViewController alloc] initWithContactShare:contactShare - contactsManager:self.contactsManager - delegate:self]; - OWSAssertDebug(contactsPicker.navigationController); - [contactsPicker.navigationController pushViewController:approveContactShare animated:YES]; -} - -- (void)contactsPicker:(ContactsPicker *)contactsPicker didSelectMultipleContacts:(NSArray *)contacts -{ - OWSFailDebug(@"with contacts: %@", contacts); - [self dismissViewControllerAnimated:YES completion:nil]; -} - -- (BOOL)contactsPicker:(ContactsPicker *)contactsPicker shouldSelectContact:(Contact *)contact -{ - // Any reason to preclude contacts? - return YES; -} - -#pragma mark - ContactShareApprovalViewControllerDelegate - -- (void)approveContactShare:(ContactShareApprovalViewController *)approveContactShare - didApproveContactShare:(ContactShareViewModel *)contactShare -{ - OWSLogInfo(@""); - - [self dismissViewControllerAnimated:YES - completion:^{ - [self sendContactShare:contactShare]; - }]; -} - -- (void)approveContactShare:(ContactShareApprovalViewController *)approveContactShare - didCancelContactShare:(ContactShareViewModel *)contactShare -{ - OWSLogInfo(@""); - - [self dismissViewControllerAnimated:YES completion:nil]; -} - -#pragma mark - ContactShareViewHelperDelegate - -- (void)didCreateOrEditContact -{ - OWSLogInfo(@""); - [self dismissViewControllerAnimated:YES completion:nil]; -} - #pragma mark - Toast - (void)presentMissingQuotedReplyToast @@ -5424,12 +4621,6 @@ typedef enum : NSUInteger { if (targetInteraction == nil || targetInteraction.interactionType != OWSInteractionType_OutgoingMessage) { return; } NSString *hexEncodedPublicKey = targetInteraction.thread.contactIdentifier; if (hexEncodedPublicKey == nil) { return; } - __block NSString *masterHexEncodedPublicKey; - [OWSPrimaryStorage.sharedManager.dbReadConnection readWithBlock:^(YapDatabaseReadTransaction *transaction) { - masterHexEncodedPublicKey = [LKDatabaseUtilities getMasterHexEncodedPublicKeyFor:hexEncodedPublicKey in:transaction] ?: hexEncodedPublicKey; - }]; - BOOL isSlaveDevice = ![masterHexEncodedPublicKey isEqual:hexEncodedPublicKey]; - if (isSlaveDevice) { return; } if (progress <= self.progressIndicatorView.progress) { return; } self.progressIndicatorView.alpha = 1; [self.progressIndicatorView setProgress:progress animated:YES]; @@ -5454,16 +4645,6 @@ typedef enum : NSUInteger { }); } -- (void)handleUnexpectedDeviceLinkRequestReceivedNotification -{ - if (!LKDeviceLinkingUtilities.shouldShowUnexpectedDeviceLinkRequestReceivedAlert) { return; } - dispatch_async(dispatch_get_main_queue(), ^{ - UIAlertController *alert = [UIAlertController alertControllerWithTitle:@"Device Link Request Received" message:@"Open the device link screen by going to \"Settings\" > \"Devices\" > \"Link a Device\" to link your devices." preferredStyle:UIAlertControllerStyleAlert]; - [alert addAction:[UIAlertAction actionWithTitle:@"OK" style:UIAlertActionStyleDefault handler:nil]]; - [self presentViewController:alert animated:YES completion:nil]; - }); -} - @end NS_ASSUME_NONNULL_END diff --git a/Session/Signal/ConversationView/ConversationViewItem.h b/Session/Signal/ConversationView/ConversationViewItem.h index 984210640..3464d077d 100644 --- a/Session/Signal/ConversationView/ConversationViewItem.h +++ b/Session/Signal/ConversationView/ConversationViewItem.h @@ -12,7 +12,6 @@ typedef NS_ENUM(NSInteger, OWSMessageCellType) { OWSMessageCellType_TextOnlyMessage, OWSMessageCellType_Audio, OWSMessageCellType_GenericAttachment, - OWSMessageCellType_ContactShare, OWSMessageCellType_MediaMessage, OWSMessageCellType_OversizeTextDownloading, }; @@ -67,7 +66,6 @@ NSString *NSStringForOWSMessageCellType(OWSMessageCellType cellType); @property (nonatomic, readonly, nullable) OWSQuotedReplyModel *quotedReply; @property (nonatomic, readonly) BOOL isGroupThread; -@property (nonatomic, readonly) BOOL isRSSFeed; @property (nonatomic, readonly) BOOL userCanDeleteGroupMessage; @property (nonatomic, readonly) BOOL hasBodyText; @@ -163,7 +161,6 @@ NSString *NSStringForOWSMessageCellType(OWSMessageCellType cellType); - (instancetype)init NS_UNAVAILABLE; - (instancetype)initWithInteraction:(TSInteraction *)interaction isGroupThread:(BOOL)isGroupThread - isRSSFeed:(BOOL)isRSSFeed transaction:(YapDatabaseReadTransaction *)transaction conversationStyle:(ConversationStyle *)conversationStyle; diff --git a/Session/Signal/ConversationView/ConversationViewItem.m b/Session/Signal/ConversationView/ConversationViewItem.m index c3fa1eaca..593cf28a7 100644 --- a/Session/Signal/ConversationView/ConversationViewItem.m +++ b/Session/Signal/ConversationView/ConversationViewItem.m @@ -4,7 +4,7 @@ #import #import "ConversationViewItem.h" -#import "OWSContactOffersCell.h" + #import "OWSMessageCell.h" #import "OWSMessageHeaderView.h" #import "OWSSystemMessageCell.h" @@ -13,7 +13,7 @@ #import #import #import -#import + #import #import #import @@ -31,8 +31,6 @@ NSString *NSStringForOWSMessageCellType(OWSMessageCellType cellType) return @"OWSMessageCellType_GenericAttachment"; case OWSMessageCellType_Unknown: return @"OWSMessageCellType_Unknown"; - case OWSMessageCellType_ContactShare: - return @"OWSMessageCellType_ContactShare"; case OWSMessageCellType_MediaMessage: return @"OWSMessageCellType_MediaMessage"; case OWSMessageCellType_OversizeTextDownloading: @@ -119,7 +117,6 @@ NSString *NSStringForOWSMessageCellType(OWSMessageCellType cellType) @synthesize interaction = _interaction; @synthesize isFirstInCluster = _isFirstInCluster; @synthesize isGroupThread = _isGroupThread; -@synthesize isRSSFeed = _isRSSFeed; @synthesize isLastInCluster = _isLastInCluster; @synthesize lastAudioMessageView = _lastAudioMessageView; @synthesize senderName = _senderName; @@ -127,7 +124,6 @@ NSString *NSStringForOWSMessageCellType(OWSMessageCellType cellType) - (instancetype)initWithInteraction:(TSInteraction *)interaction isGroupThread:(BOOL)isGroupThread - isRSSFeed:(BOOL)isRSSFeed transaction:(YapDatabaseReadTransaction *)transaction conversationStyle:(ConversationStyle *)conversationStyle { @@ -143,11 +139,8 @@ NSString *NSStringForOWSMessageCellType(OWSMessageCellType cellType) _interaction = interaction; _isGroupThread = isGroupThread; - _isRSSFeed = isRSSFeed; _conversationStyle = conversationStyle; - [self updateAuthorConversationColorNameWithTransaction:transaction]; - [self ensureViewState:transaction]; return self; @@ -173,37 +166,11 @@ NSString *NSStringForOWSMessageCellType(OWSMessageCellType cellType) self.linkPreview = nil; self.linkPreviewAttachment = nil; - [self updateAuthorConversationColorNameWithTransaction:transaction]; - [self clearCachedLayoutState]; [self ensureViewState:transaction]; } -- (void)updateAuthorConversationColorNameWithTransaction:(YapDatabaseReadTransaction *)transaction -{ - OWSAssertDebug(transaction); - - switch (self.interaction.interactionType) { - case OWSInteractionType_TypingIndicator: { - OWSTypingIndicatorInteraction *typingIndicator = (OWSTypingIndicatorInteraction *)self.interaction; - _authorConversationColorName = - [TSContactThread conversationColorNameForRecipientId:typingIndicator.recipientId - transaction:transaction]; - break; - } - case OWSInteractionType_IncomingMessage: { - TSIncomingMessage *incomingMessage = (TSIncomingMessage *)self.interaction; - _authorConversationColorName = - [TSContactThread conversationColorNameForRecipientId:incomingMessage.authorId transaction:transaction]; - break; - } - default: - _authorConversationColorName = nil; - break; - } -} - - (OWSPrimaryStorage *)primaryStorage { return SSKEnvironment.shared.primaryStorage; @@ -385,9 +352,6 @@ NSString *NSStringForOWSMessageCellType(OWSMessageCellType cellType) case OWSInteractionType_Call: measurementCell = [OWSSystemMessageCell new]; break; - case OWSInteractionType_Offer: - measurementCell = [OWSContactOffersCell new]; - break; case OWSInteractionType_TypingIndicator: measurementCell = [OWSTypingIndicatorCell new]; break; @@ -445,10 +409,6 @@ NSString *NSStringForOWSMessageCellType(OWSMessageCellType cellType) case OWSInteractionType_Call: return [collectionView dequeueReusableCellWithReuseIdentifier:[OWSSystemMessageCell cellReuseIdentifier] forIndexPath:indexPath]; - case OWSInteractionType_Offer: - return [collectionView dequeueReusableCellWithReuseIdentifier:[OWSContactOffersCell cellReuseIdentifier] - forIndexPath:indexPath]; - case OWSInteractionType_TypingIndicator: return [collectionView dequeueReusableCellWithReuseIdentifier:[OWSTypingIndicatorCell cellReuseIdentifier] forIndexPath:indexPath]; @@ -609,12 +569,6 @@ NSString *NSStringForOWSMessageCellType(OWSMessageCellType cellType) self.hasViewState = YES; TSMessage *message = (TSMessage *)self.interaction; - if (message.contactShare) { - self.contactShare = - [[ContactShareViewModel alloc] initWithContactShareRecord:message.contactShare transaction:transaction]; - self.messageCellType = OWSMessageCellType_ContactShare; - return; - } // Check for quoted replies _before_ media album handling, // since that logic may exit early. @@ -804,35 +758,7 @@ NSString *NSStringForOWSMessageCellType(OWSMessageCellType cellType) } case OWSInteractionType_Info: { TSInfoMessage *infoMessage = (TSInfoMessage *)self.interaction; - if ([infoMessage isKindOfClass:[OWSVerificationStateChangeMessage class]]) { - OWSVerificationStateChangeMessage *verificationMessage - = (OWSVerificationStateChangeMessage *)infoMessage; - BOOL isVerified = verificationMessage.verificationState == OWSVerificationStateVerified; - NSString *displayName = - [Environment.shared.contactsManager displayNameForPhoneIdentifier:verificationMessage.recipientId]; - NSString *titleFormat = (isVerified - ? (verificationMessage.isLocalChange - ? NSLocalizedString(@"VERIFICATION_STATE_CHANGE_FORMAT_VERIFIED_LOCAL", - @"Format for info message indicating that the verification state was verified " - @"on " - @"this device. Embeds {{user's name or phone number}}.") - : NSLocalizedString(@"VERIFICATION_STATE_CHANGE_FORMAT_VERIFIED_OTHER_DEVICE", - @"Format for info message indicating that the verification state was verified " - @"on " - @"another device. Embeds {{user's name or phone number}}.")) - : (verificationMessage.isLocalChange - ? NSLocalizedString(@"VERIFICATION_STATE_CHANGE_FORMAT_NOT_VERIFIED_LOCAL", - @"Format for info message indicating that the verification state was " - @"unverified on " - @"this device. Embeds {{user's name or phone number}}.") - : NSLocalizedString(@"VERIFICATION_STATE_CHANGE_FORMAT_NOT_VERIFIED_OTHER_DEVICE", - @"Format for info message indicating that the verification state was " - @"unverified on " - @"another device. Embeds {{user's name or phone number}}."))); - return [NSString stringWithFormat:titleFormat, displayName]; - } else { - return [infoMessage previewTextWithTransaction:transaction]; - } + return [infoMessage previewTextWithTransaction:transaction]; } case OWSInteractionType_Call: { TSCall *call = (TSCall *)self.interaction; @@ -921,11 +847,6 @@ NSString *NSStringForOWSMessageCellType(OWSMessageCellType cellType) OWSFailDebug(@"No text to copy"); break; } - case OWSMessageCellType_ContactShare: { - // TODO: Implement copy contact. - OWSFailDebug(@"Not implemented yet"); - break; - } case OWSMessageCellType_OversizeTextDownloading: OWSFailDebug(@"Can't copy not-yet-downloaded attachment"); return; @@ -942,10 +863,6 @@ NSString *NSStringForOWSMessageCellType(OWSMessageCellType cellType) switch (self.messageCellType) { case OWSMessageCellType_Unknown: case OWSMessageCellType_TextOnlyMessage: - case OWSMessageCellType_ContactShare: { - OWSFailDebug(@"No media to copy"); - break; - } case OWSMessageCellType_Audio: case OWSMessageCellType_GenericAttachment: { [self copyAttachmentToPasteboard:self.attachmentStream]; @@ -996,9 +913,6 @@ NSString *NSStringForOWSMessageCellType(OWSMessageCellType cellType) switch (self.messageCellType) { case OWSMessageCellType_Unknown: case OWSMessageCellType_TextOnlyMessage: - case OWSMessageCellType_ContactShare: - OWSFailDebug(@"No media to share."); - break; case OWSMessageCellType_Audio: case OWSMessageCellType_GenericAttachment: [AttachmentSharing showShareUIForAttachment:self.attachmentStream]; @@ -1035,8 +949,6 @@ NSString *NSStringForOWSMessageCellType(OWSMessageCellType cellType) switch (self.messageCellType) { case OWSMessageCellType_Unknown: case OWSMessageCellType_TextOnlyMessage: - case OWSMessageCellType_ContactShare: - return NO; case OWSMessageCellType_Audio: return NO; case OWSMessageCellType_GenericAttachment: @@ -1064,8 +976,6 @@ NSString *NSStringForOWSMessageCellType(OWSMessageCellType cellType) switch (self.messageCellType) { case OWSMessageCellType_Unknown: case OWSMessageCellType_TextOnlyMessage: - case OWSMessageCellType_ContactShare: - return NO; case OWSMessageCellType_Audio: return NO; case OWSMessageCellType_GenericAttachment: @@ -1104,9 +1014,6 @@ NSString *NSStringForOWSMessageCellType(OWSMessageCellType cellType) switch (self.messageCellType) { case OWSMessageCellType_Unknown: case OWSMessageCellType_TextOnlyMessage: - case OWSMessageCellType_ContactShare: - OWSFailDebug(@"Cannot save text data."); - break; case OWSMessageCellType_Audio: OWSFailDebug(@"Cannot save media data."); break; @@ -1177,7 +1084,6 @@ NSString *NSStringForOWSMessageCellType(OWSMessageCellType cellType) if (self.isGroupThread) { // Skip if the thread is an RSS feed TSGroupThread *groupThread = (TSGroupThread *)self.interaction.thread; - if (groupThread.isRSSFeed) return; // Only allow deletion on incoming and outgoing messages OWSInteractionType interationType = self.interaction.interactionType; @@ -1217,8 +1123,6 @@ NSString *NSStringForOWSMessageCellType(OWSMessageCellType cellType) switch (self.messageCellType) { case OWSMessageCellType_Unknown: case OWSMessageCellType_TextOnlyMessage: - case OWSMessageCellType_ContactShare: - return NO; case OWSMessageCellType_Audio: case OWSMessageCellType_GenericAttachment: return self.attachmentStream != nil; @@ -1252,7 +1156,6 @@ NSString *NSStringForOWSMessageCellType(OWSMessageCellType cellType) // Ensure the thread is a public chat and not an RSS feed TSGroupThread *groupThread = (TSGroupThread *)self.interaction.thread; - if (groupThread.isRSSFeed) return false; // Only allow deletion on incoming and outgoing messages OWSInteractionType interationType = self.interaction.interactionType; diff --git a/Session/Signal/ConversationView/ConversationViewModel.h b/Session/Signal/ConversationView/ConversationViewModel.h index daa28db23..84ef4a6db 100644 --- a/Session/Signal/ConversationView/ConversationViewModel.h +++ b/Session/Signal/ConversationView/ConversationViewModel.h @@ -98,12 +98,10 @@ typedef NS_ENUM(NSUInteger, ConversationUpdateItemType) { @property (nonatomic, readonly) ConversationViewState *viewState; @property (nonatomic, nullable) NSString *focusMessageIdOnOpen; @property (nonatomic, readonly, nullable) ThreadDynamicInteractions *dynamicInteractions; -@property (nonatomic, readonly) BOOL isRSSFeed; - (instancetype)init NS_UNAVAILABLE; - (instancetype)initWithThread:(TSThread *)thread focusMessageIdOnOpen:(nullable NSString *)focusMessageIdOnOpen - isRSSFeed:(BOOL)isRSSFeed delegate:(id)delegate NS_DESIGNATED_INITIALIZER; - (void)ensureDynamicInteractionsAndUpdateIfNecessary:(BOOL)updateIfNecessary; diff --git a/Session/Signal/ConversationView/ConversationViewModel.m b/Session/Signal/ConversationView/ConversationViewModel.m index aa287a615..12a2c5cae 100644 --- a/Session/Signal/ConversationView/ConversationViewModel.m +++ b/Session/Signal/ConversationView/ConversationViewModel.m @@ -10,10 +10,10 @@ #import "Session-Swift.h" #import #import -#import + #import #import -#import + #import #import #import @@ -226,7 +226,6 @@ static const int kYapDatabaseRangeMaxLength = 25000; - (instancetype)initWithThread:(TSThread *)thread focusMessageIdOnOpen:(nullable NSString *)focusMessageIdOnOpen - isRSSFeed:(BOOL)isRSSFeed delegate:(id)delegate { self = [super init]; @@ -242,7 +241,6 @@ static const int kYapDatabaseRangeMaxLength = 25000; _persistedViewItems = @[]; _unsavedOutgoingMessages = @[]; self.focusMessageIdOnOpen = focusMessageIdOnOpen; - _isRSSFeed = isRSSFeed; _viewState = [[ConversationViewState alloc] initWithViewItems:@[]]; [self configure]; @@ -269,11 +267,6 @@ static const int kYapDatabaseRangeMaxLength = 25000; return self.primaryStorage.dbReadWriteConnection; } -- (OWSContactsManager *)contactsManager -{ - return (OWSContactsManager *)SSKEnvironment.shared.contactsManager; -} - - (OWSBlockingManager *)blockingManager { return OWSBlockingManager.sharedManager; @@ -304,10 +297,6 @@ static const int kYapDatabaseRangeMaxLength = 25000; selector:@selector(applicationDidEnterBackground:) name:OWSApplicationDidEnterBackgroundNotification object:nil]; - [[NSNotificationCenter defaultCenter] addObserver:self - selector:@selector(signalAccountsDidChange:) - name:OWSContactsManagerSignalAccountsDidChangeNotification - object:nil]; [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(typingIndicatorStateDidChange:) name:[OWSTypingIndicatorsImpl typingIndicatorStateDidChange] @@ -530,7 +519,6 @@ static const int kYapDatabaseRangeMaxLength = 25000; ThreadDynamicInteractions *dynamicInteractions = [ThreadUtil ensureDynamicInteractionsForThread:self.thread - contactsManager:self.contactsManager blockingManager:self.blockingManager dbConnection:self.editingDatabaseConnection hideUnreadMessagesIndicator:self.hasClearedUnreadMessagesIndicator @@ -1023,24 +1011,10 @@ static const int kYapDatabaseRangeMaxLength = 25000; return; } - // Many OWSProfileManager methods aren't safe to call from inside a database - // transaction, so do this work now. - // - // TODO: It'd be nice if these methods took a transaction. - BOOL hasLocalProfile = [self.profileManager hasLocalProfile]; - BOOL isThreadInProfileWhitelist = [self.profileManager isThreadInProfileWhitelist:self.thread]; - BOOL hasUnwhitelistedMember = NO; - for (NSString *recipientId in self.thread.recipientIdentifiers) { - if (![self.profileManager isUserInProfileWhitelist:recipientId]) { - hasUnwhitelistedMember = YES; - break; - } - } - ConversationProfileState *conversationProfileState = [ConversationProfileState new]; - conversationProfileState.hasLocalProfile = hasLocalProfile; - conversationProfileState.isThreadInProfileWhitelist = isThreadInProfileWhitelist; - conversationProfileState.hasUnwhitelistedMember = hasUnwhitelistedMember; + conversationProfileState.hasLocalProfile = YES; + conversationProfileState.isThreadInProfileWhitelist = YES; + conversationProfileState.hasUnwhitelistedMember = NO; self.conversationProfileState = conversationProfileState; } @@ -1136,15 +1110,6 @@ static const int kYapDatabaseRangeMaxLength = 25000; // Don't create profile whitelist offers for users which are not already blocked. shouldHaveAddToProfileWhitelistOffer = NO; } - - if ([self.contactsManager hasSignalAccountForRecipientId:recipientId]) { - // Only create "add to contacts" offers for non-contacts. - shouldHaveAddToContactsOffer = NO; - // Only create block offers for non-contacts. - shouldHaveBlockOffer = NO; - // Don't create profile whitelist offers for non-contacts. - shouldHaveAddToProfileWhitelistOffer = NO; - } } if (hasTooManyOutgoingMessagesToBlock) { @@ -1214,7 +1179,6 @@ static const int kYapDatabaseRangeMaxLength = 25000; NSArray *loadedUniqueIds = [self.messageMapping loadedUniqueIds]; BOOL isGroupThread = self.thread.isGroupThread; - BOOL isRSSFeed = self.isRSSFeed; ConversationStyle *conversationStyle = self.delegate.conversationStyle; [self ensureConversationProfileState]; @@ -1228,7 +1192,6 @@ static const int kYapDatabaseRangeMaxLength = 25000; if (!viewItem) { viewItem = [[ConversationInteractionViewItem alloc] initWithInteraction:interaction isGroupThread:isGroupThread - isRSSFeed:isRSSFeed transaction:transaction conversationStyle:conversationStyle]; } @@ -1236,7 +1199,7 @@ static const int kYapDatabaseRangeMaxLength = 25000; viewItemCache[interaction.uniqueId] = viewItem; [viewItems addObject:viewItem]; TSMessage *message = (TSMessage *)viewItem.interaction; - if (message.hasAttachmentsInNSE) { + if (message.hasUnfetchedAttachmentsFromPN) { [SSKEnvironment.shared.attachmentDownloads downloadAttachmentsForMessage:message transaction:transaction success:^(NSArray *attachmentStreams) { @@ -1534,8 +1497,7 @@ static const int kYapDatabaseRangeMaxLength = 25000; } if (shouldShowSenderName) { - senderName = [self.contactsManager attributedContactOrProfileNameForPhoneIdentifier:incomingSenderId primaryAttributes:[OWSMessageBubbleView senderNamePrimaryAttributes] - secondaryAttributes:[OWSMessageBubbleView senderNameSecondaryAttributes]]; + senderName = [[NSAttributedString alloc] initWithString:[SSKEnvironment.shared.profileManager profileNameForRecipientWithID:incomingSenderId avoidingWriteTransaction:YES]]; if ([self.thread isKindOfClass:[TSGroupThread class]]) { TSGroupThread *groupThread = (TSGroupThread *)self.thread; @@ -1558,9 +1520,7 @@ static const int kYapDatabaseRangeMaxLength = 25000; // the next message has the same sender avatar and // no "date break" separates us. shouldShowSenderAvatar = YES; - if (viewItem.isRSSFeed) { - shouldShowSenderAvatar = NO; - } else if (previousViewItem && previousViewItem.interaction.interactionType == interactionType) { + if (previousViewItem && previousViewItem.interaction.interactionType == interactionType) { shouldShowSenderAvatar = (![NSObject isNullableObject:previousIncomingSenderId equalTo:incomingSenderId]); } } diff --git a/Session/Signal/DomainFrontingCountryViewController.h b/Session/Signal/DomainFrontingCountryViewController.h deleted file mode 100644 index 77899f26b..000000000 --- a/Session/Signal/DomainFrontingCountryViewController.h +++ /dev/null @@ -1,13 +0,0 @@ -// -// Copyright (c) 2018 Open Whisper Systems. All rights reserved. -// - -#import - -NS_ASSUME_NONNULL_BEGIN - -@interface DomainFrontingCountryViewController : OWSViewController - -@end - -NS_ASSUME_NONNULL_END diff --git a/Session/Signal/DomainFrontingCountryViewController.m b/Session/Signal/DomainFrontingCountryViewController.m deleted file mode 100644 index 5497842f7..000000000 --- a/Session/Signal/DomainFrontingCountryViewController.m +++ /dev/null @@ -1,98 +0,0 @@ -// -// Copyright (c) 2019 Open Whisper Systems. All rights reserved. -// - -#import "DomainFrontingCountryViewController.h" -#import "OWSCountryMetadata.h" -#import "OWSTableViewController.h" -#import "UIColor+OWS.h" -#import "UIFont+OWS.h" -#import "UIView+OWS.h" -#import -#import - -NS_ASSUME_NONNULL_BEGIN - -#pragma mark - - -@interface DomainFrontingCountryViewController () - -@property (nonatomic, readonly) OWSTableViewController *tableViewController; - -@end - -#pragma mark - - -@implementation DomainFrontingCountryViewController - -- (void)loadView -{ - [super loadView]; - - self.title = NSLocalizedString( - @"CENSORSHIP_CIRCUMVENTION_COUNTRY_VIEW_TITLE", @"Title for the 'censorship circumvention country' view."); - - self.view.backgroundColor = Theme.backgroundColor; - - [self createViews]; -} - -- (void)createViews -{ - _tableViewController = [OWSTableViewController new]; - [self.view addSubview:self.tableViewController.view]; - [self.tableViewController.view autoPinEdgeToSuperviewSafeArea:ALEdgeLeading]; - [self.tableViewController.view autoPinEdgeToSuperviewSafeArea:ALEdgeTrailing]; - [_tableViewController.view autoPinEdge:ALEdgeTop toEdge:ALEdgeTop ofView:self.view withOffset:0.0f]; - [_tableViewController.view autoPinEdge:ALEdgeBottom toEdge:ALEdgeBottom ofView:self.view withOffset:0.0f]; - - [self updateTableContents]; -} - -#pragma mark - Table Contents - -- (void)updateTableContents -{ - OWSTableContents *contents = [OWSTableContents new]; - - NSString *currentCountryCode = OWSSignalService.sharedInstance.manualCensorshipCircumventionCountryCode; - - __weak DomainFrontingCountryViewController *weakSelf = self; - - OWSTableSection *section = [OWSTableSection new]; - section.headerTitle = NSLocalizedString( - @"DOMAIN_FRONTING_COUNTRY_VIEW_SECTION_HEADER", @"Section title for the 'domain fronting country' view."); - for (OWSCountryMetadata *countryMetadata in [OWSCountryMetadata allCountryMetadatas]) { - [section addItem:[OWSTableItem - itemWithCustomCellBlock:^{ - UITableViewCell *cell = [OWSTableItem newCell]; - [OWSTableItem configureCell:cell]; - cell.textLabel.text = countryMetadata.localizedCountryName; - - if ([countryMetadata.countryCode isEqualToString:currentCountryCode]) { - cell.accessoryType = UITableViewCellAccessoryCheckmark; - } - - return cell; - } - actionBlock:^{ - [weakSelf selectCountry:countryMetadata]; - }]]; - } - [contents addSection:section]; - - self.tableViewController.contents = contents; -} - -- (void)selectCountry:(OWSCountryMetadata *)countryMetadata -{ - OWSAssertDebug(countryMetadata); - - OWSSignalService.sharedInstance.manualCensorshipCircumventionCountryCode = countryMetadata.countryCode; - - [self.navigationController popViewControllerAnimated:YES]; -} - -@end - -NS_ASSUME_NONNULL_END diff --git a/Session/Signal/FingerprintViewController.h b/Session/Signal/FingerprintViewController.h deleted file mode 100644 index b99859937..000000000 --- a/Session/Signal/FingerprintViewController.h +++ /dev/null @@ -1,15 +0,0 @@ -// -// Copyright (c) 2018 Open Whisper Systems. All rights reserved. -// - -#import - -NS_ASSUME_NONNULL_BEGIN - -@interface FingerprintViewController : OWSViewController - -+ (void)presentFromViewController:(UIViewController *)viewController recipientId:(NSString *)recipientId; - -@end - -NS_ASSUME_NONNULL_END diff --git a/Session/Signal/FingerprintViewController.m b/Session/Signal/FingerprintViewController.m deleted file mode 100644 index 8c185f364..000000000 --- a/Session/Signal/FingerprintViewController.m +++ /dev/null @@ -1,558 +0,0 @@ -// -// Copyright (c) 2019 Open Whisper Systems. All rights reserved. -// - -#import "FingerprintViewController.h" -#import "FingerprintViewScanController.h" -#import "OWSBezierPathView.h" -#import "Session-Swift.h" -#import "UIColor+OWS.h" -#import "UIFont+OWS.h" -#import "UIView+OWS.h" -#import -#import -#import -#import -#import -#import -#import -#import -#import -#import -#import - -NS_ASSUME_NONNULL_BEGIN - -typedef void (^CustomLayoutBlock)(void); - -@interface CustomLayoutView : UIView - -@property (nonatomic) CustomLayoutBlock layoutBlock; - -@end - -#pragma mark - - -@implementation CustomLayoutView - -- (instancetype)init -{ - if (self = [super init]) { - self.translatesAutoresizingMaskIntoConstraints = NO; - } - return self; -} - -- (instancetype)initWithFrame:(CGRect)frame -{ - if (self = [super initWithFrame:frame]) { - self.translatesAutoresizingMaskIntoConstraints = NO; - } - return self; -} - -- (nullable instancetype)initWithCoder:(NSCoder *)aDecoder -{ - if (self = [super initWithCoder:aDecoder]) { - self.translatesAutoresizingMaskIntoConstraints = NO; - } - return self; -} - -- (void)layoutSubviews -{ - [super layoutSubviews]; - - self.layoutBlock(); -} - -@end - -#pragma mark - - -@interface FingerprintViewController () - -@property (nonatomic) NSString *recipientId; -@property (nonatomic) NSData *identityKey; -@property (nonatomic) TSAccountManager *accountManager; -@property (nonatomic) OWSFingerprint *fingerprint; -@property (nonatomic) NSString *contactName; - -@property (nonatomic) UIBarButtonItem *shareButton; - -@property (nonatomic) UILabel *verificationStateLabel; -@property (nonatomic) UILabel *verifyUnverifyButtonLabel; - -@end - -#pragma mark - - -@implementation FingerprintViewController - -+ (void)presentFromViewController:(UIViewController *)viewController recipientId:(NSString *)recipientId -{ - OWSAssertDebug(recipientId.length > 0); - - OWSRecipientIdentity *_Nullable recipientIdentity = - [[OWSIdentityManager sharedManager] recipientIdentityForRecipientId:recipientId]; - if (!recipientIdentity) { - [OWSAlerts showAlertWithTitle:NSLocalizedString(@"CANT_VERIFY_IDENTITY_ALERT_TITLE", - @"Title for alert explaining that a user cannot be verified.") - message:NSLocalizedString(@"CANT_VERIFY_IDENTITY_ALERT_MESSAGE", - @"Message for alert explaining that a user cannot be verified.")]; - return; - } - - FingerprintViewController *fingerprintViewController = [FingerprintViewController new]; - [fingerprintViewController configureWithRecipientId:recipientId]; - OWSNavigationController *navigationController = - [[OWSNavigationController alloc] initWithRootViewController:fingerprintViewController]; - [viewController presentViewController:navigationController animated:YES completion:nil]; -} - -- (instancetype)init -{ - self = [super init]; - - if (!self) { - return self; - } - - _accountManager = [TSAccountManager sharedInstance]; - - [self observeNotifications]; - - return self; -} - -- (void)dealloc -{ - [[NSNotificationCenter defaultCenter] removeObserver:self]; -} - -- (void)observeNotifications -{ - [[NSNotificationCenter defaultCenter] addObserver:self - selector:@selector(identityStateDidChange:) - name:kNSNotificationName_IdentityStateDidChange - object:nil]; -} - -- (void)configureWithRecipientId:(NSString *)recipientId -{ - OWSAssertDebug(recipientId.length > 0); - - self.recipientId = recipientId; - - OWSContactsManager *contactsManager = Environment.shared.contactsManager; - self.contactName = [contactsManager displayNameForPhoneIdentifier:recipientId]; - - OWSRecipientIdentity *_Nullable recipientIdentity = - [[OWSIdentityManager sharedManager] recipientIdentityForRecipientId:recipientId]; - OWSAssertDebug(recipientIdentity); - // By capturing the identity key when we enter these views, we prevent the edge case - // where the user verifies a key that we learned about while this view was open. - self.identityKey = recipientIdentity.identityKey; - - OWSFingerprintBuilder *builder = - [[OWSFingerprintBuilder alloc] initWithAccountManager:self.accountManager contactsManager:contactsManager]; - self.fingerprint = - [builder fingerprintWithTheirSignalId:recipientId theirIdentityKey:recipientIdentity.identityKey]; -} - -- (void)loadView -{ - [super loadView]; - - self.title = NSLocalizedString(@"PRIVACY_VERIFICATION_TITLE", @"Navbar title"); - - self.navigationItem.leftBarButtonItem = - [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemStop - target:self - action:@selector(closeButton) - accessibilityIdentifier:ACCESSIBILITY_IDENTIFIER_WITH_NAME(self, @"stop")]; - self.shareButton = - [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemAction - target:self - action:@selector(didTapShareButton) - accessibilityIdentifier:ACCESSIBILITY_IDENTIFIER_WITH_NAME(self, @"share")]; - self.navigationItem.rightBarButtonItem = self.shareButton; - - [self createViews]; -} - -- (void)createViews -{ - self.view.backgroundColor = Theme.backgroundColor; - - // Verify/Unverify Button - UIView *verifyUnverifyButton = [UIView new]; - [verifyUnverifyButton - addGestureRecognizer:[[UITapGestureRecognizer alloc] initWithTarget:self - action:@selector(verifyUnverifyButtonTapped:)]]; - [self.view addSubview:verifyUnverifyButton]; - [verifyUnverifyButton autoPinWidthToSuperview]; - [verifyUnverifyButton autoPinEdge:ALEdgeBottom toEdge:ALEdgeBottom ofView:self.view withOffset:0.0f]; - SET_SUBVIEW_ACCESSIBILITY_IDENTIFIER(self, verifyUnverifyButton); - - UIView *verifyUnverifyPillbox = [UIView new]; - verifyUnverifyPillbox.backgroundColor = [UIColor ows_materialBlueColor]; - verifyUnverifyPillbox.layer.cornerRadius = 3.f; - verifyUnverifyPillbox.clipsToBounds = YES; - [verifyUnverifyButton addSubview:verifyUnverifyPillbox]; - [verifyUnverifyPillbox autoHCenterInSuperview]; - [verifyUnverifyPillbox autoPinEdgeToSuperviewEdge:ALEdgeTop withInset:ScaleFromIPhone5To7Plus(10.f, 15.f)]; - [verifyUnverifyPillbox autoPinEdgeToSuperviewEdge:ALEdgeBottom withInset:ScaleFromIPhone5To7Plus(10.f, 20.f)]; - - UILabel *verifyUnverifyButtonLabel = [UILabel new]; - self.verifyUnverifyButtonLabel = verifyUnverifyButtonLabel; - verifyUnverifyButtonLabel.font = [UIFont ows_mediumFontWithSize:ScaleFromIPhone5To7Plus(14.f, 20.f)]; - verifyUnverifyButtonLabel.textColor = [UIColor whiteColor]; - verifyUnverifyButtonLabel.textAlignment = NSTextAlignmentCenter; - [verifyUnverifyPillbox addSubview:verifyUnverifyButtonLabel]; - [verifyUnverifyButtonLabel autoPinWidthToSuperviewWithMargin:ScaleFromIPhone5To7Plus(50.f, 50.f)]; - [verifyUnverifyButtonLabel autoPinHeightToSuperviewWithMargin:ScaleFromIPhone5To7Plus(8.f, 8.f)]; - - // Learn More - UIView *learnMoreButton = [UIView new]; - [learnMoreButton - addGestureRecognizer:[[UITapGestureRecognizer alloc] initWithTarget:self - action:@selector(learnMoreButtonTapped:)]]; - [self.view addSubview:learnMoreButton]; - [learnMoreButton autoPinWidthToSuperview]; - [learnMoreButton autoPinEdge:ALEdgeBottom toEdge:ALEdgeTop ofView:verifyUnverifyButton withOffset:0]; - SET_SUBVIEW_ACCESSIBILITY_IDENTIFIER(self, learnMoreButton); - - UILabel *learnMoreLabel = [UILabel new]; - learnMoreLabel.attributedText = [[NSAttributedString alloc] - initWithString:NSLocalizedString(@"PRIVACY_SAFETY_NUMBERS_LEARN_MORE", - @"Label for a link to more information about safety numbers and verification.") - attributes:@{ - NSUnderlineStyleAttributeName : @(NSUnderlineStyleSingle | NSUnderlinePatternSolid), - }]; - learnMoreLabel.font = [UIFont ows_regularFontWithSize:ScaleFromIPhone5To7Plus(13.f, 16.f)]; - learnMoreLabel.textColor = [UIColor ows_materialBlueColor]; - learnMoreLabel.textAlignment = NSTextAlignmentCenter; - [learnMoreButton addSubview:learnMoreLabel]; - [learnMoreLabel autoPinWidthToSuperviewWithMargin:16.f]; - [learnMoreLabel autoPinEdgeToSuperviewEdge:ALEdgeTop withInset:ScaleFromIPhone5To7Plus(5.f, 10.f)]; - [learnMoreLabel autoPinEdgeToSuperviewEdge:ALEdgeBottom withInset:ScaleFromIPhone5To7Plus(5.f, 10.f)]; - - // Instructions - NSString *instructionsFormat = NSLocalizedString(@"PRIVACY_VERIFICATION_INSTRUCTIONS", - @"Paragraph(s) shown alongside the safety number when verifying privacy with {{contact name}}"); - UILabel *instructionsLabel = [UILabel new]; - instructionsLabel.text = [NSString stringWithFormat:instructionsFormat, self.contactName]; - instructionsLabel.font = [UIFont ows_regularFontWithSize:ScaleFromIPhone5To7Plus(11.f, 14.f)]; - instructionsLabel.textColor = Theme.secondaryColor; - instructionsLabel.textAlignment = NSTextAlignmentCenter; - instructionsLabel.numberOfLines = 0; - instructionsLabel.lineBreakMode = NSLineBreakByWordWrapping; - [self.view addSubview:instructionsLabel]; - [instructionsLabel autoPinWidthToSuperviewWithMargin:16.f]; - [instructionsLabel autoPinEdge:ALEdgeBottom toEdge:ALEdgeTop ofView:learnMoreButton withOffset:0]; - - // Fingerprint Label - UILabel *fingerprintLabel = [UILabel new]; - fingerprintLabel.text = self.fingerprint.displayableText; - fingerprintLabel.font = [UIFont fontWithName:@"Menlo-Regular" size:ScaleFromIPhone5To7Plus(20.f, 23.f)]; - fingerprintLabel.textColor = Theme.secondaryColor; - fingerprintLabel.numberOfLines = 3; - fingerprintLabel.lineBreakMode = NSLineBreakByTruncatingTail; - fingerprintLabel.adjustsFontSizeToFitWidth = YES; - [fingerprintLabel - addGestureRecognizer:[[UITapGestureRecognizer alloc] initWithTarget:self - action:@selector(fingerprintLabelTapped:)]]; - fingerprintLabel.userInteractionEnabled = YES; - [self.view addSubview:fingerprintLabel]; - [fingerprintLabel autoPinWidthToSuperviewWithMargin:ScaleFromIPhone5To7Plus(50.f, 60.f)]; - [fingerprintLabel autoPinEdge:ALEdgeBottom - toEdge:ALEdgeTop - ofView:instructionsLabel - withOffset:-ScaleFromIPhone5To7Plus(8.f, 15.f)]; - SET_SUBVIEW_ACCESSIBILITY_IDENTIFIER(self, fingerprintLabel); - - // Fingerprint Image - CustomLayoutView *fingerprintView = [CustomLayoutView new]; - [self.view addSubview:fingerprintView]; - [fingerprintView autoPinWidthToSuperview]; - [fingerprintView autoPinEdge:ALEdgeBottom - toEdge:ALEdgeTop - ofView:fingerprintLabel - withOffset:-ScaleFromIPhone5To7Plus(10.f, 15.f)]; - [fingerprintView - addGestureRecognizer:[[UITapGestureRecognizer alloc] initWithTarget:self - action:@selector(fingerprintViewTapped:)]]; - fingerprintView.userInteractionEnabled = YES; - SET_SUBVIEW_ACCESSIBILITY_IDENTIFIER(self, fingerprintView); - - OWSBezierPathView *fingerprintCircle = [OWSBezierPathView new]; - [fingerprintCircle setConfigureShapeLayerBlock:^(CAShapeLayer *layer, CGRect bounds) { - layer.fillColor = Theme.offBackgroundColor.CGColor; - CGFloat size = MIN(bounds.size.width, bounds.size.height); - CGRect circle = CGRectMake((bounds.size.width - size) * 0.5f, (bounds.size.height - size) * 0.5f, size, size); - layer.path = [UIBezierPath bezierPathWithOvalInRect:circle].CGPath; - }]; - [fingerprintView addSubview:fingerprintCircle]; - [fingerprintCircle ows_autoPinToSuperviewEdges]; - - UIImageView *fingerprintImageView = [UIImageView new]; - fingerprintImageView.image = self.fingerprint.image; - // Don't antialias QR Codes. - fingerprintImageView.layer.magnificationFilter = kCAFilterNearest; - fingerprintImageView.layer.minificationFilter = kCAFilterNearest; - [fingerprintView addSubview:fingerprintImageView]; - - UILabel *scanLabel = [UILabel new]; - scanLabel.text = NSLocalizedString(@"PRIVACY_TAP_TO_SCAN", @"Button that shows the 'scan with camera' view."); - scanLabel.font = [UIFont ows_mediumFontWithSize:ScaleFromIPhone5To7Plus(14.f, 16.f)]; - scanLabel.textColor = Theme.secondaryColor; - [scanLabel sizeToFit]; - [fingerprintView addSubview:scanLabel]; - - fingerprintView.layoutBlock = ^{ - CGFloat size = round(MIN(fingerprintView.width, fingerprintView.height) * 0.675f); - fingerprintImageView.frame = CGRectMake( - round((fingerprintView.width - size) * 0.5f), round((fingerprintView.height - size) * 0.5f), size, size); - CGFloat scanY = round(fingerprintImageView.bottom - + ((fingerprintView.height - fingerprintImageView.bottom) - scanLabel.height) * 0.33f); - scanLabel.frame = CGRectMake( - round((fingerprintView.width - scanLabel.width) * 0.5f), scanY, scanLabel.width, scanLabel.height); - }; - - // Verification State - UILabel *verificationStateLabel = [UILabel new]; - self.verificationStateLabel = verificationStateLabel; - verificationStateLabel.font = [UIFont ows_mediumFontWithSize:ScaleFromIPhone5To7Plus(16.f, 20.f)]; - verificationStateLabel.textColor = Theme.secondaryColor; - verificationStateLabel.textAlignment = NSTextAlignmentCenter; - verificationStateLabel.numberOfLines = 0; - verificationStateLabel.lineBreakMode = NSLineBreakByWordWrapping; - [self.view addSubview:verificationStateLabel]; - [verificationStateLabel autoPinWidthToSuperviewWithMargin:16.f]; - // Bind height of label to height of two lines of text. - // This should always be sufficient, and will prevent the view's - // layout from changing if the user is marked as verified or not - // verified. - [verificationStateLabel autoSetDimension:ALDimensionHeight - toSize:round(verificationStateLabel.font.lineHeight * 2.25f)]; - [verificationStateLabel autoPinEdge:ALEdgeTop toEdge:ALEdgeTop ofView:self.view withOffset:ScaleFromIPhone5To7Plus(15.f, 20.f)]; - [verificationStateLabel autoPinEdge:ALEdgeBottom - toEdge:ALEdgeTop - ofView:fingerprintView - withOffset:-ScaleFromIPhone5To7Plus(10.f, 15.f)]; - - [self updateVerificationStateLabel]; -} - -- (void)updateVerificationStateLabel -{ - OWSAssertDebug(self.recipientId.length > 0); - - BOOL isVerified = [[OWSIdentityManager sharedManager] verificationStateForRecipientId:self.recipientId] - == OWSVerificationStateVerified; - - if (isVerified) { - NSMutableAttributedString *labelText = [NSMutableAttributedString new]; - - if (isVerified) { - // Show a "checkmark" if this user is verified. - [labelText - appendAttributedString:[[NSAttributedString alloc] - initWithString:LocalizationNotNeeded(@"\uf00c ") - attributes:@{ - NSFontAttributeName : [UIFont - ows_fontAwesomeFont:self.verificationStateLabel.font.pointSize], - }]]; - } - - [labelText - appendAttributedString: - [[NSAttributedString alloc] - initWithString:[NSString stringWithFormat:NSLocalizedString(@"PRIVACY_IDENTITY_IS_VERIFIED_FORMAT", - @"Label indicating that the user is verified. Embeds " - @"{{the user's name or phone number}}."), - self.contactName]]]; - self.verificationStateLabel.attributedText = labelText; - - self.verifyUnverifyButtonLabel.text = NSLocalizedString( - @"PRIVACY_UNVERIFY_BUTTON", @"Button that lets user mark another user's identity as unverified."); - } else { - self.verificationStateLabel.text = [NSString - stringWithFormat:NSLocalizedString(@"PRIVACY_IDENTITY_IS_NOT_VERIFIED_FORMAT", - @"Label indicating that the user is not verified. Embeds {{the user's name or phone " - @"number}}."), - self.contactName]; - - NSMutableAttributedString *buttonText = [NSMutableAttributedString new]; - // Show a "checkmark" if this user is not verified. - [buttonText - appendAttributedString:[[NSAttributedString alloc] - initWithString:LocalizationNotNeeded(@"\uf00c ") - attributes:@{ - NSFontAttributeName : [UIFont - ows_fontAwesomeFont:self.verifyUnverifyButtonLabel.font.pointSize], - }]]; - [buttonText appendAttributedString: - [[NSAttributedString alloc] - initWithString:NSLocalizedString(@"PRIVACY_VERIFY_BUTTON", - @"Button that lets user mark another user's identity as verified.")]]; - self.verifyUnverifyButtonLabel.attributedText = buttonText; - } - - [self.view setNeedsLayout]; -} - -#pragma mark - - -- (void)showSharingActivityWithCompletion:(nullable void (^)(void))completionHandler -{ - OWSLogDebug(@"Sharing safety numbers"); - - OWSCompareSafetyNumbersActivity *compareActivity = [[OWSCompareSafetyNumbersActivity alloc] initWithDelegate:self]; - - NSString *shareFormat = NSLocalizedString( - @"SAFETY_NUMBER_SHARE_FORMAT", @"Snippet to share {{safety number}} with a friend. sent e.g. via SMS"); - NSString *shareString = [NSString stringWithFormat:shareFormat, self.fingerprint.displayableText]; - - UIActivityViewController *activityController = - [[UIActivityViewController alloc] initWithActivityItems:@[ shareString ] - applicationActivities:@[ compareActivity ]]; - - activityController.completionWithItemsHandler = ^void(UIActivityType __nullable activityType, - BOOL completed, - NSArray *__nullable returnedItems, - NSError *__nullable activityError) { - if (completionHandler) { - completionHandler(); - } - }; - - // This value was extracted by inspecting `activityType` in the activityController.completionHandler - NSString *const iCloudActivityType = @"com.apple.CloudDocsUI.AddToiCloudDrive"; - activityController.excludedActivityTypes = @[ - UIActivityTypePostToFacebook, - UIActivityTypePostToWeibo, - UIActivityTypeAirDrop, - UIActivityTypePostToTwitter, - iCloudActivityType // This isn't being excluded. RADAR https://openradar.appspot.com/27493621 - ]; - - [self presentViewController:activityController animated:YES completion:nil]; -} - -#pragma mark - OWSCompareSafetyNumbersActivityDelegate - -- (void)compareSafetyNumbersActivitySucceededWithActivity:(OWSCompareSafetyNumbersActivity *)activity -{ - [self showVerificationSucceeded]; -} - -- (void)compareSafetyNumbersActivity:(OWSCompareSafetyNumbersActivity *)activity failedWithError:(NSError *)error -{ - [self showVerificationFailedWithError:error]; -} - -- (void)showVerificationSucceeded -{ - [FingerprintViewScanController showVerificationSucceeded:self - identityKey:self.identityKey - recipientId:self.recipientId - contactName:self.contactName - tag:self.logTag]; -} - -- (void)showVerificationFailedWithError:(NSError *)error -{ - - [FingerprintViewScanController showVerificationFailedWithError:error - viewController:self - retryBlock:nil - cancelBlock:^{ - // Do nothing. - } - tag:self.logTag]; -} - -#pragma mark - Action - -- (void)closeButton -{ - [self dismissViewControllerAnimated:YES completion:nil]; -} - -- (void)didTapShareButton -{ - [self showSharingActivityWithCompletion:nil]; -} - -- (void)showScanner -{ - FingerprintViewScanController *scanView = [FingerprintViewScanController new]; - [scanView configureWithRecipientId:self.recipientId]; - [self.navigationController pushViewController:scanView animated:YES]; -} - -- (void)learnMoreButtonTapped:(UIGestureRecognizer *)gestureRecognizer -{ - if (gestureRecognizer.state == UIGestureRecognizerStateRecognized) { - NSString *learnMoreURL = @"https://support.signal.org/hc/en-us/articles/" - @"213134107"; - [[UIApplication sharedApplication] openURL:[NSURL URLWithString:learnMoreURL]]; - } -} - -- (void)fingerprintLabelTapped:(UIGestureRecognizer *)gestureRecognizer -{ - if (gestureRecognizer.state == UIGestureRecognizerStateRecognized) { - [self showSharingActivityWithCompletion:nil]; - } -} - -- (void)fingerprintViewTapped:(UIGestureRecognizer *)gestureRecognizer -{ - if (gestureRecognizer.state == UIGestureRecognizerStateRecognized) { - [self showScanner]; - } -} - -- (void)verifyUnverifyButtonTapped:(UIGestureRecognizer *)gestureRecognizer -{ - if (gestureRecognizer.state == UIGestureRecognizerStateRecognized) { - [LKStorage writeSyncWithBlock:^(YapDatabaseReadWriteTransaction *transaction) { - BOOL isVerified = [[OWSIdentityManager sharedManager] verificationStateForRecipientId:self.recipientId - transaction:transaction] - == OWSVerificationStateVerified; - - OWSVerificationState newVerificationState - = (isVerified ? OWSVerificationStateDefault : OWSVerificationStateVerified); - [[OWSIdentityManager sharedManager] setVerificationState:newVerificationState - identityKey:self.identityKey - recipientId:self.recipientId - isUserInitiatedChange:YES - transaction:transaction]; - }]; - - [self dismissViewControllerAnimated:YES completion:nil]; - } -} - -#pragma mark - Notifications - -- (void)identityStateDidChange:(NSNotification *)notification -{ - OWSAssertIsOnMainThread(); - - [self updateVerificationStateLabel]; -} - -#pragma mark - Orientation - -- (UIInterfaceOrientationMask)supportedInterfaceOrientations -{ - return UIInterfaceOrientationMaskPortrait; -} - -@end - -NS_ASSUME_NONNULL_END diff --git a/Session/Signal/FingerprintViewScanController.h b/Session/Signal/FingerprintViewScanController.h deleted file mode 100644 index 8c54f0ebf..000000000 --- a/Session/Signal/FingerprintViewScanController.h +++ /dev/null @@ -1,27 +0,0 @@ -// -// Copyright (c) 2018 Open Whisper Systems. All rights reserved. -// - -#import - -NS_ASSUME_NONNULL_BEGIN - -@interface FingerprintViewScanController : OWSViewController - -- (void)configureWithRecipientId:(NSString *)recipientId NS_SWIFT_NAME(configure(recipientId:)); - -+ (void)showVerificationSucceeded:(UIViewController *)viewController - identityKey:(NSData *)identityKey - recipientId:(NSString *)recipientId - contactName:(NSString *)contactName - tag:(NSString *)tag; - -+ (void)showVerificationFailedWithError:(NSError *)error - viewController:(UIViewController *)viewController - retryBlock:(void (^_Nullable)(void))retryBlock - cancelBlock:(void (^_Nonnull)(void))cancelBlock - tag:(NSString *)tag; - -@end - -NS_ASSUME_NONNULL_END diff --git a/Session/Signal/FingerprintViewScanController.m b/Session/Signal/FingerprintViewScanController.m deleted file mode 100644 index 743ab3210..000000000 --- a/Session/Signal/FingerprintViewScanController.m +++ /dev/null @@ -1,258 +0,0 @@ -// -// Copyright (c) 2019 Open Whisper Systems. All rights reserved. -// - -#import "FingerprintViewScanController.h" -#import "OWSQRCodeScanningViewController.h" -#import "Session-Swift.h" -#import "UIColor+OWS.h" -#import "UIFont+OWS.h" -#import "UIView+OWS.h" -#import "UIViewController+Permissions.h" -#import -#import -#import -#import -#import -#import -#import - -NS_ASSUME_NONNULL_BEGIN - -@interface FingerprintViewScanController () - -@property (nonatomic) TSAccountManager *accountManager; -@property (nonatomic) NSString *recipientId; -@property (nonatomic) NSData *identityKey; -@property (nonatomic) OWSFingerprint *fingerprint; -@property (nonatomic) NSString *contactName; -@property (nonatomic) OWSQRCodeScanningViewController *qrScanningController; - -@end - -#pragma mark - - -@implementation FingerprintViewScanController - -- (void)configureWithRecipientId:(NSString *)recipientId -{ - OWSAssertDebug(recipientId.length > 0); - - self.recipientId = recipientId; - self.accountManager = [TSAccountManager sharedInstance]; - - OWSContactsManager *contactsManager = Environment.shared.contactsManager; - self.contactName = [contactsManager displayNameForPhoneIdentifier:recipientId]; - - OWSRecipientIdentity *_Nullable recipientIdentity = - [[OWSIdentityManager sharedManager] recipientIdentityForRecipientId:recipientId]; - OWSAssertDebug(recipientIdentity); - // By capturing the identity key when we enter these views, we prevent the edge case - // where the user verifies a key that we learned about while this view was open. - self.identityKey = recipientIdentity.identityKey; - - OWSFingerprintBuilder *builder = - [[OWSFingerprintBuilder alloc] initWithAccountManager:self.accountManager contactsManager:contactsManager]; - self.fingerprint = - [builder fingerprintWithTheirSignalId:recipientId theirIdentityKey:recipientIdentity.identityKey]; -} - -- (void)loadView -{ - [super loadView]; - - self.title = NSLocalizedString(@"SCAN_QR_CODE_VIEW_TITLE", @"Title for the 'scan QR code' view."); - - [self createViews]; -} - -- (void)createViews -{ - self.view.backgroundColor = UIColor.blackColor; - - self.qrScanningController = [OWSQRCodeScanningViewController new]; - self.qrScanningController.scanDelegate = self; - [self.view addSubview:self.qrScanningController.view]; - [self.qrScanningController.view autoPinWidthToSuperview]; - [self.qrScanningController.view autoPinEdge:ALEdgeTop toEdge:ALEdgeTop ofView:self.view withOffset:0.0f]; - - UIView *footer = [UIView new]; - footer.backgroundColor = [UIColor colorWithWhite:0.25f alpha:1.f]; - [self.view addSubview:footer]; - [footer autoPinWidthToSuperview]; - [footer autoPinEdgeToSuperviewEdge:ALEdgeBottom]; - [footer autoPinEdge:ALEdgeTop toEdge:ALEdgeBottom ofView:self.qrScanningController.view]; - - UILabel *cameraInstructionLabel = [UILabel new]; - cameraInstructionLabel.text - = NSLocalizedString(@"SCAN_CODE_INSTRUCTIONS", @"label presented once scanning (camera) view is visible."); - cameraInstructionLabel.font = [UIFont ows_regularFontWithSize:ScaleFromIPhone5To7Plus(14.f, 18.f)]; - cameraInstructionLabel.textColor = [UIColor whiteColor]; - cameraInstructionLabel.textAlignment = NSTextAlignmentCenter; - cameraInstructionLabel.numberOfLines = 0; - cameraInstructionLabel.lineBreakMode = NSLineBreakByWordWrapping; - [footer addSubview:cameraInstructionLabel]; - [cameraInstructionLabel autoPinWidthToSuperviewWithMargin:ScaleFromIPhone5To7Plus(16.f, 30.f)]; - CGFloat instructionsVMargin = ScaleFromIPhone5To7Plus(10.f, 20.f); - [cameraInstructionLabel autoPinEdge:ALEdgeBottom toEdge:ALEdgeBottom ofView:self.view withOffset:instructionsVMargin]; - [cameraInstructionLabel autoPinEdgeToSuperviewEdge:ALEdgeTop withInset:instructionsVMargin]; -} - -#pragma mark - Action - -- (void)viewDidAppear:(BOOL)animated -{ - [super viewDidAppear:animated]; - - [self ows_askForCameraPermissions:^(BOOL granted) { - if (granted) { - // Camera stops capturing when "sharing" while in capture mode. - // Also, it's less obvious whats being "shared" at this point, - // so just disable sharing when in capture mode. - - OWSLogInfo(@"Showing Scanner"); - - [self.qrScanningController startCapture]; - } else { - [self.navigationController popViewControllerAnimated:YES]; - } - }]; -} - -#pragma mark - OWSQRScannerDelegate - -- (void)controller:(OWSQRCodeScanningViewController *)controller didDetectQRCodeWithData:(NSData *)data -{ - [self verifyCombinedFingerprintData:data]; -} - -- (void)verifyCombinedFingerprintData:(NSData *)combinedFingerprintData -{ - NSError *error; - if ([self.fingerprint matchesLogicalFingerprintsData:combinedFingerprintData error:&error]) { - [self showVerificationSucceeded]; - } else { - [self showVerificationFailedWithError:error]; - } -} - -- (void)showVerificationSucceeded -{ - [self.class showVerificationSucceeded:self - identityKey:self.identityKey - recipientId:self.recipientId - contactName:self.contactName - tag:self.logTag]; -} - -- (void)showVerificationFailedWithError:(NSError *)error -{ - - [self.class showVerificationFailedWithError:error - viewController:self - retryBlock:^{ - [self.qrScanningController startCapture]; - } - cancelBlock:^{ - [self.navigationController popViewControllerAnimated:YES]; - } - tag:self.logTag]; -} - -+ (void)showVerificationSucceeded:(UIViewController *)viewController - identityKey:(NSData *)identityKey - recipientId:(NSString *)recipientId - contactName:(NSString *)contactName - tag:(NSString *)tag -{ - OWSAssertDebug(viewController); - OWSAssertDebug(identityKey.length > 0); - OWSAssertDebug(recipientId.length > 0); - OWSAssertDebug(contactName.length > 0); - OWSAssertDebug(tag.length > 0); - - OWSLogInfo(@"%@ Successfully verified safety numbers.", tag); - - NSString *successTitle = NSLocalizedString(@"SUCCESSFUL_VERIFICATION_TITLE", nil); - NSString *descriptionFormat = NSLocalizedString( - @"SUCCESSFUL_VERIFICATION_DESCRIPTION", @"Alert body after verifying privacy with {{other user's name}}"); - NSString *successDescription = [NSString stringWithFormat:descriptionFormat, contactName]; - UIAlertController *alert = [UIAlertController alertControllerWithTitle:successTitle - message:successDescription - preferredStyle:UIAlertControllerStyleAlert]; - [alert - addAction:[UIAlertAction - actionWithTitle:NSLocalizedString(@"FINGERPRINT_SCAN_VERIFY_BUTTON", - @"Button that marks user as verified after a successful fingerprint scan.") - style:UIAlertActionStyleDefault - handler:^(UIAlertAction *action) { - [OWSIdentityManager.sharedManager setVerificationState:OWSVerificationStateVerified - identityKey:identityKey - recipientId:recipientId - isUserInitiatedChange:YES]; - [viewController dismissViewControllerAnimated:true completion:nil]; - }]]; - UIAlertAction *dismissAction = - [UIAlertAction actionWithTitle:CommonStrings.dismissButton - style:UIAlertActionStyleDefault - handler:^(UIAlertAction *action) { - [viewController dismissViewControllerAnimated:true completion:nil]; - }]; - [alert addAction:dismissAction]; - - [viewController presentAlert:alert]; -} - -+ (void)showVerificationFailedWithError:(NSError *)error - viewController:(UIViewController *)viewController - retryBlock:(void (^_Nullable)(void))retryBlock - cancelBlock:(void (^_Nonnull)(void))cancelBlock - tag:(NSString *)tag -{ - OWSAssertDebug(viewController); - OWSAssertDebug(cancelBlock); - OWSAssertDebug(tag.length > 0); - - OWSLogInfo(@"%@ Failed to verify safety numbers.", tag); - - NSString *_Nullable failureTitle; - if (error.code != OWSErrorCodeUserError) { - failureTitle = NSLocalizedString(@"FAILED_VERIFICATION_TITLE", @"alert title"); - } // else no title. We don't want to show a big scary "VERIFICATION FAILED" when it's just user error. - - UIAlertController *alert = [UIAlertController alertControllerWithTitle:failureTitle - message:error.localizedDescription - preferredStyle:UIAlertControllerStyleAlert]; - - if (retryBlock) { - [alert addAction:[UIAlertAction actionWithTitle:[CommonStrings retryButton] - style:UIAlertActionStyleDefault - handler:^(UIAlertAction *action) { - retryBlock(); - }]]; - } - - [alert addAction:[OWSAlerts cancelAction]]; - - [viewController presentAlert:alert]; - - OWSLogWarn(@"%@ Identity verification failed with error: %@", tag, error); -} - -- (void)dismissViewControllerAnimated:(BOOL)animated completion:(nullable void (^)(void))completion -{ - self.qrScanningController.view.hidden = YES; - - [super dismissViewControllerAnimated:animated completion:completion]; -} - -#pragma mark - Orientation - -- (UIInterfaceOrientationMask)supportedInterfaceOrientations -{ - return UIInterfaceOrientationMaskPortrait; -} - -@end - -NS_ASSUME_NONNULL_END diff --git a/Session/Signal/GifPickerViewController.swift b/Session/Signal/GifPickerViewController.swift index 5e13ba1ab..f5c4711f5 100644 --- a/Session/Signal/GifPickerViewController.swift +++ b/Session/Signal/GifPickerViewController.swift @@ -35,7 +35,6 @@ class GifPickerViewController: OWSViewController, UISearchBarDelegate, UICollect public weak var delegate: GifPickerViewControllerDelegate? let thread: TSThread - let messageSender: MessageSender let searchBar: SearchBar let layout: GifPickerLayout @@ -60,9 +59,8 @@ class GifPickerViewController: OWSViewController, UISearchBarDelegate, UICollect } @objc - required init(thread: TSThread, messageSender: MessageSender) { + required init(thread: TSThread) { self.thread = thread - self.messageSender = messageSender self.searchBar = SearchBar() self.layout = GifPickerLayout() diff --git a/Session/Signal/GroupTableViewCell.swift b/Session/Signal/GroupTableViewCell.swift index 0f3e9871b..e8f9da488 100644 --- a/Session/Signal/GroupTableViewCell.swift +++ b/Session/Signal/GroupTableViewCell.swift @@ -7,15 +7,9 @@ import SignalUtilitiesKit @objc class GroupTableViewCell: UITableViewCell { - // MARK: - Dependencies - - private var contactsManager: OWSContactsManager { - return Environment.shared.contactsManager - } - // MARK: - - private let avatarView = AvatarImageView() +// private let avatarView = AvatarImageView() private let nameLabel = UILabel() private let subtitleLabel = UILabel() @@ -30,14 +24,14 @@ import SignalUtilitiesKit // Layout - avatarView.autoSetDimension(.width, toSize: CGFloat(kStandardAvatarSize)) - avatarView.autoPinToSquareAspectRatio() +// avatarView.autoSetDimension(.width, toSize: CGFloat(kStandardAvatarSize)) +// avatarView.autoPinToSquareAspectRatio() let textRows = UIStackView(arrangedSubviews: [nameLabel, subtitleLabel]) textRows.axis = .vertical textRows.alignment = .leading - let columns = UIStackView(arrangedSubviews: [avatarView, textRows]) + let columns = UIStackView(arrangedSubviews: [ textRows ]) columns.axis = .horizontal columns.alignment = .center columns.spacing = kContactCellAvatarTextMargin @@ -62,11 +56,11 @@ import SignalUtilitiesKit let groupMemberIds: [String] = thread.groupModel.groupMemberIds let groupMemberNames = groupMemberIds.map { (recipientId: String) in - contactsManager.displayName(forPhoneIdentifier: recipientId) + SSKEnvironment.shared.profileManager.profileNameForRecipient(withID: recipientId, avoidingWriteTransaction: true)! }.joined(separator: ", ") self.subtitleLabel.text = groupMemberNames - self.avatarView.image = OWSAvatarBuilder.buildImage(thread: thread, diameter: kStandardAvatarSize) +// self.avatarView.image = OWSAvatarBuilder.buildImage(thread: thread, diameter: kStandardAvatarSize) } } diff --git a/Session/Signal/InviteFlow.swift b/Session/Signal/InviteFlow.swift deleted file mode 100644 index 0ba2b678f..000000000 --- a/Session/Signal/InviteFlow.swift +++ /dev/null @@ -1,271 +0,0 @@ -// -// Copyright (c) 2019 Open Whisper Systems. All rights reserved. -// - -import Foundation -import Social -import ContactsUI -import MessageUI -import SignalUtilitiesKit - -@objc(OWSInviteFlow) -class InviteFlow: NSObject, MFMessageComposeViewControllerDelegate, MFMailComposeViewControllerDelegate, ContactsPickerDelegate { - enum Channel { - case message, mail, twitter - } - - let installUrl = "https://signal.org/install/" - let homepageUrl = "https://signal.org" - - @objc - let actionSheetController: UIAlertController - - @objc - let presentingViewController: UIViewController - - var channel: Channel? - - @objc - required init(presentingViewController: UIViewController) { - self.presentingViewController = presentingViewController - actionSheetController = UIAlertController(title: nil, message: nil, preferredStyle: .actionSheet) - - super.init() - - actionSheetController.addAction(dismissAction()) - - if let messageAction = messageAction() { - actionSheetController.addAction(messageAction) - } - - if let mailAction = mailAction() { - actionSheetController.addAction(mailAction) - } - - if let tweetAction = tweetAction() { - actionSheetController.addAction(tweetAction) - } - } - - deinit { - Logger.verbose("[InviteFlow] deinit") - } - - // MARK: Twitter - - func canTweet() -> Bool { - return SLComposeViewController.isAvailable(forServiceType: SLServiceTypeTwitter) - } - - func tweetAction() -> UIAlertAction? { - guard canTweet() else { - Logger.info("Twitter not supported.") - return nil - } - - guard let twitterViewController = SLComposeViewController(forServiceType: SLServiceTypeTwitter) else { - Logger.error("unable to build twitter controller.") - return nil - } - - let tweetString = NSLocalizedString("SETTINGS_INVITE_TWITTER_TEXT", comment: "content of tweet when inviting via twitter - please do not translate URL") - twitterViewController.setInitialText(tweetString) - - let tweetUrl = URL(string: installUrl) - twitterViewController.add(tweetUrl) - twitterViewController.add(#imageLiteral(resourceName: "twitter_sharing_image")) - - let tweetTitle = NSLocalizedString("SHARE_ACTION_TWEET", comment: "action sheet item") - return UIAlertAction(title: tweetTitle, style: .default) { _ in - Logger.debug("Chose tweet") - - self.presentingViewController.present(twitterViewController, animated: true, completion: nil) - } - } - - func dismissAction() -> UIAlertAction { - return UIAlertAction(title: CommonStrings.dismissButton, style: .cancel) - } - - // MARK: ContactsPickerDelegate - - func contactsPicker(_: ContactsPicker, didSelectMultipleContacts contacts: [Contact]) { - Logger.debug("didSelectContacts:\(contacts)") - - guard let inviteChannel = channel else { - Logger.error("unexpected nil channel after returning from contact picker.") - self.presentingViewController.dismiss(animated: true) - return - } - - switch inviteChannel { - case .message: - let phoneNumbers: [String] = contacts.map { $0.userTextPhoneNumbers.first }.filter { $0 != nil }.map { $0! } - dismissAndSendSMSTo(phoneNumbers: phoneNumbers) - case .mail: - let recipients: [String] = contacts.map { $0.emails.first }.filter { $0 != nil }.map { $0! } - sendMailTo(emails: recipients) - default: - Logger.error("unexpected channel after returning from contact picker: \(inviteChannel)") - } - } - - func contactsPicker(_: ContactsPicker, shouldSelectContact contact: Contact) -> Bool { - guard let inviteChannel = channel else { - Logger.error("unexpected nil channel in contact picker.") - return true - } - - switch inviteChannel { - case .message: - return contact.userTextPhoneNumbers.count > 0 - case .mail: - return contact.emails.count > 0 - default: - Logger.error("unexpected channel after returning from contact picker: \(inviteChannel)") - } - return true - } - - func contactsPicker(_: ContactsPicker, contactFetchDidFail error: NSError) { - Logger.error("with error: \(error)") - self.presentingViewController.dismiss(animated: true) { - OWSAlerts.showErrorAlert(message: NSLocalizedString("ERROR_COULD_NOT_FETCH_CONTACTS", comment: "Error indicating that the phone's contacts could not be retrieved.")) - } - } - - func contactsPickerDidCancel(_: ContactsPicker) { - Logger.debug("") - self.presentingViewController.dismiss(animated: true) - } - - func contactsPicker(_: ContactsPicker, didSelectContact contact: Contact) { - owsFailDebug("InviteFlow only supports multi-select") - self.presentingViewController.dismiss(animated: true) - } - - // MARK: SMS - - func messageAction() -> UIAlertAction? { - guard MFMessageComposeViewController.canSendText() else { - Logger.info("Device cannot send text") - return nil - } - - let messageTitle = NSLocalizedString("SHARE_ACTION_MESSAGE", comment: "action sheet item to open native messages app") - return UIAlertAction(title: messageTitle, style: .default) { _ in - Logger.debug("Chose message.") - self.channel = .message - let picker = ContactsPicker(allowsMultipleSelection: true, subtitleCellType: .phoneNumber) - picker.contactsPickerDelegate = self - picker.title = NSLocalizedString("INVITE_FRIENDS_PICKER_TITLE", comment: "Navbar title") - let navigationController = OWSNavigationController(rootViewController: picker) - self.presentingViewController.present(navigationController, animated: true) - } - } - - public func dismissAndSendSMSTo(phoneNumbers: [String]) { - self.presentingViewController.dismiss(animated: true) { - if phoneNumbers.count > 1 { - let warning = UIAlertController(title: nil, - message: NSLocalizedString("INVITE_WARNING_MULTIPLE_INVITES_BY_TEXT", - comment: "Alert warning that sending an invite to multiple users will create a group message whose recipients will be able to see each other."), - preferredStyle: .alert) - warning.addAction(UIAlertAction(title: NSLocalizedString("BUTTON_CONTINUE", - comment: "Label for 'continue' button."), - style: .default, handler: { _ in - self.sendSMSTo(phoneNumbers: phoneNumbers) - })) - warning.addAction(OWSAlerts.cancelAction) - self.presentingViewController.presentAlert(warning) - } else { - self.sendSMSTo(phoneNumbers: phoneNumbers) - } - } - } - - @objc - public func sendSMSTo(phoneNumbers: [String]) { - let messageComposeViewController = MFMessageComposeViewController() - messageComposeViewController.messageComposeDelegate = self - messageComposeViewController.recipients = phoneNumbers - - let inviteText = NSLocalizedString("SMS_INVITE_BODY", comment: "body sent to contacts when inviting to Install Signal") - messageComposeViewController.body = inviteText.appending(" \(self.installUrl)") - self.presentingViewController.present(messageComposeViewController, animated: true) - } - - // MARK: MessageComposeViewControllerDelegate - - func messageComposeViewController(_ controller: MFMessageComposeViewController, didFinishWith result: MessageComposeResult) { - self.presentingViewController.dismiss(animated: true) { - switch result { - case .failed: - let warning = UIAlertController(title: nil, message: NSLocalizedString("SEND_INVITE_FAILURE", comment: "Alert body after invite failed"), preferredStyle: .alert) - warning.addAction(UIAlertAction(title: CommonStrings.dismissButton, style: .default, handler: nil)) - self.presentingViewController.present(warning, animated: true, completion: nil) - case .sent: - Logger.debug("user successfully invited their friends via SMS.") - case .cancelled: - Logger.debug("user cancelled message invite") - } - } - } - - // MARK: Mail - - func mailAction() -> UIAlertAction? { - guard MFMailComposeViewController.canSendMail() else { - Logger.info("Device cannot send mail") - return nil - } - - let mailActionTitle = NSLocalizedString("SHARE_ACTION_MAIL", comment: "action sheet item to open native mail app") - return UIAlertAction(title: mailActionTitle, style: .default) { _ in - Logger.debug("Chose mail.") - self.channel = .mail - - let picker = ContactsPicker(allowsMultipleSelection: true, subtitleCellType: .email) - picker.contactsPickerDelegate = self - picker.title = NSLocalizedString("INVITE_FRIENDS_PICKER_TITLE", comment: "Navbar title") - let navigationController = OWSNavigationController(rootViewController: picker) - self.presentingViewController.present(navigationController, animated: true) - } - } - - func sendMailTo(emails recipientEmails: [String]) { - let mailComposeViewController = MFMailComposeViewController() - mailComposeViewController.mailComposeDelegate = self - mailComposeViewController.setBccRecipients(recipientEmails) - - let subject = NSLocalizedString("EMAIL_INVITE_SUBJECT", comment: "subject of email sent to contacts when inviting to install Signal") - let bodyFormat = NSLocalizedString("EMAIL_INVITE_BODY", comment: "body of email sent to contacts when inviting to install Signal. Embeds {{link to install Signal}} and {{link to the Signal home page}}") - let body = String.init(format: bodyFormat, installUrl, homepageUrl) - mailComposeViewController.setSubject(subject) - mailComposeViewController.setMessageBody(body, isHTML: false) - - self.presentingViewController.dismiss(animated: true) { - self.presentingViewController.present(mailComposeViewController, animated: true) - } - } - - // MARK: MailComposeViewControllerDelegate - - func mailComposeController(_ controller: MFMailComposeViewController, didFinishWith result: MFMailComposeResult, error: Error?) { - self.presentingViewController.dismiss(animated: true) { - switch result { - case .failed: - let warning = UIAlertController(title: nil, message: NSLocalizedString("SEND_INVITE_FAILURE", comment: "Alert body after invite failed"), preferredStyle: .alert) - warning.addAction(UIAlertAction(title: CommonStrings.dismissButton, style: .default, handler: nil)) - self.presentingViewController.present(warning, animated: true, completion: nil) - case .sent: - Logger.debug("user successfully invited their friends via mail.") - case .saved: - Logger.debug("user saved mail invite.") - case .cancelled: - Logger.debug("user cancelled mail invite.") - } - } - } - -} diff --git a/Session/Signal/LegacyNotificationsAdaptee.swift b/Session/Signal/LegacyNotificationsAdaptee.swift index 15bccc839..6d92bd199 100644 --- a/Session/Signal/LegacyNotificationsAdaptee.swift +++ b/Session/Signal/LegacyNotificationsAdaptee.swift @@ -176,7 +176,7 @@ extension LegacyNotificationPresenterAdaptee: NotificationPresenterAdaptee { } let checkForCancel = category == .incomingMessage - if checkForCancel && hasReceivedSyncMessageRecently { + if checkForCancel { assert(userInfo[AppNotificationUserInfoKey.threadId] != nil) notification.fireDate = Date(timeIntervalSinceNow: kNotificationDelayForRemoteRead) notification.timeZone = NSTimeZone.local diff --git a/Session/Signal/MediaPageViewController.swift b/Session/Signal/MediaPageViewController.swift index 591be5823..d7138511a 100644 --- a/Session/Signal/MediaPageViewController.swift +++ b/Session/Signal/MediaPageViewController.swift @@ -587,7 +587,6 @@ class MediaPageViewController: UIPageViewController, UIPageViewControllerDataSou let conversationStyle = ConversationStyle(thread: thread) fetchedItem = ConversationInteractionViewItem(interaction: message, isGroupThread: thread.isGroupThread(), - isRSSFeed: false, transaction: transaction, conversationStyle: conversationStyle) } @@ -670,10 +669,6 @@ class MediaPageViewController: UIPageViewController, UIPageViewControllerDataSou // MARK: Dynamic Header - private var contactsManager: OWSContactsManager { - return Environment.shared.contactsManager - } - private func senderName(message: TSMessage) -> String { switch message { case let incomingMessage as TSIncomingMessage: diff --git a/Session/Signal/MessageActions.swift b/Session/Signal/MessageActions.swift index b8eaffd2d..03bdcb109 100644 --- a/Session/Signal/MessageActions.swift +++ b/Session/Signal/MessageActions.swift @@ -93,9 +93,8 @@ class ConversationViewItemActions: NSObject { var actions: [MenuAction] = [] let isGroup = conversationViewItem.isGroupThread; - let isRSSFeed = conversationViewItem.isRSSFeed; - if shouldAllowReply && !isRSSFeed { + if shouldAllowReply { let replyAction = MessageActionBuilder.reply(conversationViewItem: conversationViewItem, delegate: delegate) actions.append(replyAction) } @@ -105,7 +104,7 @@ class ConversationViewItemActions: NSObject { actions.append(copyTextAction) } - if isGroup && !isRSSFeed && conversationViewItem.interaction is TSIncomingMessage { + if isGroup && conversationViewItem.interaction is TSIncomingMessage { let copyPublicKeyAction = MessageActionBuilder.copyPublicKey(conversationViewItem: conversationViewItem, delegate: delegate) actions.append(copyPublicKeyAction) } @@ -132,9 +131,8 @@ class ConversationViewItemActions: NSObject { var actions: [MenuAction] = [] let isGroup = conversationViewItem.isGroupThread; - let isRSSFeed = conversationViewItem.isRSSFeed; - if shouldAllowReply && !isRSSFeed { + if shouldAllowReply { let replyAction = MessageActionBuilder.reply(conversationViewItem: conversationViewItem, delegate: delegate) actions.append(replyAction) } @@ -150,7 +148,7 @@ class ConversationViewItemActions: NSObject { } } - if isGroup && !isRSSFeed && conversationViewItem.interaction is TSIncomingMessage { + if isGroup && conversationViewItem.interaction is TSIncomingMessage { let copyPublicKeyAction = MessageActionBuilder.copyPublicKey(conversationViewItem: conversationViewItem, delegate: delegate) actions.append(copyPublicKeyAction) } @@ -182,9 +180,8 @@ class ConversationViewItemActions: NSObject { } let isGroup = conversationViewItem.isGroupThread; - let isRSSFeed = conversationViewItem.isRSSFeed; - if isGroup && !isRSSFeed && conversationViewItem.interaction is TSIncomingMessage { + if isGroup && conversationViewItem.interaction is TSIncomingMessage { let copyPublicKeyAction = MessageActionBuilder.copyPublicKey(conversationViewItem: conversationViewItem, delegate: delegate) actions.append(copyPublicKeyAction) } diff --git a/Session/Signal/MessageDetailViewController.swift b/Session/Signal/MessageDetailViewController.swift index d6f82c5fd..f752c5391 100644 --- a/Session/Signal/MessageDetailViewController.swift +++ b/Session/Signal/MessageDetailViewController.swift @@ -18,7 +18,7 @@ protocol MessageDetailViewDelegate: AnyObject { } @objc -class MessageDetailViewController: OWSViewController, MediaGalleryDataSourceDelegate, OWSMessageBubbleViewDelegate, ContactShareViewHelperDelegate { +class MessageDetailViewController: OWSViewController, MediaGalleryDataSourceDelegate, OWSMessageBubbleViewDelegate { @objc weak var delegate: MessageDetailViewDelegate? @@ -52,18 +52,12 @@ class MessageDetailViewController: OWSViewController, MediaGalleryDataSourceDele var conversationStyle: ConversationStyle - private var contactShareViewHelper: ContactShareViewHelper! - // MARK: Dependencies var preferences: OWSPreferences { return Environment.shared.preferences } - var contactsManager: OWSContactsManager { - return Environment.shared.contactsManager - } - // MARK: Initializers @available(*, unavailable, message:"use other constructor instead.") @@ -86,8 +80,6 @@ class MessageDetailViewController: OWSViewController, MediaGalleryDataSourceDele override func viewDidLoad() { super.viewDidLoad() - self.contactShareViewHelper = ContactShareViewHelper(contactsManager: contactsManager) - contactShareViewHelper.delegate = self do { try updateMessageToLatest() @@ -614,27 +606,6 @@ class MessageDetailViewController: OWSViewController, MediaGalleryDataSourceDele mediaGallery.presentDetailView(fromViewController: self, mediaAttachment: attachmentStream, replacingView: imageView) } - func didTapContactShare(_ viewItem: ConversationViewItem) { - guard let contactShare = viewItem.contactShare else { - owsFailDebug("missing contact.") - return - } - let contactViewController = ContactViewController(contactShare: contactShare) - self.navigationController?.pushViewController(contactViewController, animated: true) - } - - func didTapSendMessage(toContactShare contactShare: ContactShareViewModel) { - contactShareViewHelper.sendMessage(contactShare: contactShare, fromViewController: self) - } - - func didTapSendInvite(toContactShare contactShare: ContactShareViewModel) { - contactShareViewHelper.showInviteContact(contactShare: contactShare, fromViewController: self) - } - - func didTapShowAddToContactUI(forContactShare contactShare: ContactShareViewModel) { - contactShareViewHelper.showAddToContacts(contactShare: contactShare, fromViewController: self) - } - var audioAttachmentPlayer: OWSAudioPlayer? func didTapAudioViewItem(_ viewItem: ConversationViewItem, attachmentStream: TSAttachmentStream) { diff --git a/Session/Signal/MessageFetcherJob.swift b/Session/Signal/MessageFetcherJob.swift deleted file mode 100644 index 408a4e0f9..000000000 --- a/Session/Signal/MessageFetcherJob.swift +++ /dev/null @@ -1,175 +0,0 @@ -// -// Copyright (c) 2019 Open Whisper Systems. All rights reserved. -// - -import Foundation -import PromiseKit -import SignalUtilitiesKit - -@objc(OWSMessageFetcherJob) -public class MessageFetcherJob: NSObject { - - private var timer: Timer? - - @objc - public override init() { - super.init() - - SwiftSingletons.register(self) - } - - // MARK: Singletons - - private var networkManager: TSNetworkManager { - return SSKEnvironment.shared.networkManager - } - - private var messageReceiver: OWSMessageReceiver { - return SSKEnvironment.shared.messageReceiver - } - - private var signalService: OWSSignalService { - return OWSSignalService.sharedInstance() - } - - // MARK: - - @discardableResult - public func run() -> Promise { - let promise = fetchUndeliveredMessages().then { promises -> Promise in - let promises = promises.map { promise -> Promise in - return promise.then { envelopes -> Promise in - for envelope in envelopes { - Logger.info("Envelope received.") - do { - let envelopeData = try envelope.serializedData() - self.messageReceiver.handleReceivedEnvelopeData(envelopeData) - } catch { - owsFailDebug("Failed to serialize envelope.") - } - self.acknowledgeDelivery(envelope: envelope) - } - return Promise.value(()) - } - } - return when(resolved: promises).asVoid() - } - promise.retainUntilComplete() - return promise - } - - @objc - @discardableResult - public func run() -> AnyPromise { - return AnyPromise(run() as Promise) - } - - // use in DEBUG or wherever you can't receive push notifications to poll for messages. - // Do not use in production. - public func startRunLoop(timeInterval: Double) { - Logger.error("Starting message fetch polling. This should not be used in production.") - timer = WeakTimer.scheduledTimer(timeInterval: timeInterval, target: self, userInfo: nil, repeats: true) {[weak self] _ in - let _: Promise? = self?.run() - return - } - } - - public func stopRunLoop() { - timer?.invalidate() - timer = nil - } - - private func parseMessagesResponse(responseObject: Any?) -> (envelopes: [SSKProtoEnvelope], more: Bool)? { - guard let responseObject = responseObject else { - Logger.error("response object was surpringly nil") - return nil - } - - guard let responseDict = responseObject as? [String: Any] else { - Logger.error("response object was not a dictionary") - return nil - } - - guard let messageDicts = responseDict["messages"] as? [[String: Any]] else { - Logger.error("messages object was not a list of dictionaries") - return nil - } - - let moreMessages = { () -> Bool in - if let responseMore = responseDict["more"] as? Bool { - return responseMore - } else { - Logger.warn("more object was not a bool. Assuming no more") - return false - } - }() - - let envelopes: [SSKProtoEnvelope] = messageDicts.compactMap { buildEnvelope(messageDict: $0) } - - return ( - envelopes: envelopes, - more: moreMessages - ) - } - - private func buildEnvelope(messageDict: [String: Any]) -> SSKProtoEnvelope? { - do { - let params = ParamParser(dictionary: messageDict) - - let typeInt: Int32 = try params.required(key: "type") - guard let type: SSKProtoEnvelope.SSKProtoEnvelopeType = SSKProtoEnvelope.SSKProtoEnvelopeType(rawValue: typeInt) else { - Logger.error("`type` was invalid: \(typeInt)") - throw ParamParser.ParseError.invalidFormat("type") - } - - guard let timestamp: UInt64 = try params.required(key: "timestamp") else { - Logger.error("`timestamp` was invalid: \(typeInt)") - throw ParamParser.ParseError.invalidFormat("timestamp") - } - - let builder = SSKProtoEnvelope.builder(type: type, timestamp: timestamp) - - if let source: String = try params.optional(key: "source") { - builder.setSource(source) - } - - if let sourceDevice: UInt32 = try params.optional(key: "sourceDevice") { - builder.setSourceDevice(sourceDevice) - } - - if let legacyMessage = try params.optionalBase64EncodedData(key: "message") { - builder.setLegacyMessage(legacyMessage) - } - if let content = try params.optionalBase64EncodedData(key: "content") { - builder.setContent(content) - } - if let serverTimestamp: UInt64 = try params.optional(key: "serverTimestamp") { - builder.setServerTimestamp(serverTimestamp) - } - if let serverGuid: String = try params.optional(key: "guid") { - builder.setServerGuid(serverGuid) - } - - return try builder.build() - } catch { - owsFailDebug("error building envelope: \(error)") - return nil - } - } - - private func fetchUndeliveredMessages() -> Promise>> { - let userPublickKey = getUserHexEncodedPublicKey() // Can be missing in rare cases - guard !userPublickKey.isEmpty else { return Promise.value(Set()) } - return SnodeAPI.getMessages(for: userPublickKey).map2 { promises -> Set> in - return Set(promises.map { promise -> Promise<[SSKProtoEnvelope]> in - return promise.map2 { rawMessages -> [SSKProtoEnvelope] in - return rawMessages.compactMap { SSKProtoEnvelope.from($0) } - } - }) - } - } - - private func acknowledgeDelivery(envelope: SSKProtoEnvelope) { - // Do nothing - } -} diff --git a/Session/Signal/MessageRecipientStatusUtils.swift b/Session/Signal/MessageRecipientStatusUtils.swift index dcf6daefe..5e7f8638e 100644 --- a/Session/Signal/MessageRecipientStatusUtils.swift +++ b/Session/Signal/MessageRecipientStatusUtils.swift @@ -110,10 +110,6 @@ public class MessageRecipientStatusUtils: NSObject { // Use the "long" version of this message here. return (.failed, NSLocalizedString("MESSAGE_STATUS_FAILED", comment: "status message for failed messages")) case .sending: - if outgoingMessage.isCalculatingPoW { - return (.calculatingPoW, NSLocalizedString("Calculating proof of work", comment: "")) - } - if outgoingMessage.hasAttachments() { return (.uploading, NSLocalizedString("MESSAGE_STATUS_UPLOADING", comment: "status message while attachment is uploading")) diff --git a/Session/Signal/OWSAddToContactViewController.h b/Session/Signal/OWSAddToContactViewController.h deleted file mode 100644 index 80d652900..000000000 --- a/Session/Signal/OWSAddToContactViewController.h +++ /dev/null @@ -1,15 +0,0 @@ -// -// Copyright (c) 2018 Open Whisper Systems. All rights reserved. -// - -#import "OWSTableViewController.h" - -NS_ASSUME_NONNULL_BEGIN - -@interface OWSAddToContactViewController : OWSTableViewController - -- (void)configureWithRecipientId:(NSString *)recipientId; - -@end - -NS_ASSUME_NONNULL_END diff --git a/Session/Signal/OWSAddToContactViewController.m b/Session/Signal/OWSAddToContactViewController.m deleted file mode 100644 index 46a2c9d8d..000000000 --- a/Session/Signal/OWSAddToContactViewController.m +++ /dev/null @@ -1,212 +0,0 @@ -// -// Copyright (c) 2019 Open Whisper Systems. All rights reserved. -// - -#import "OWSAddToContactViewController.h" -#import -#import -#import -#import - -@import ContactsUI; - -NS_ASSUME_NONNULL_BEGIN - -@interface OWSAddToContactViewController () - -@property (nonatomic) NSString *recipientId; - -@property (nonatomic, readonly) OWSContactsManager *contactsManager; -@property (nonatomic, readonly) ContactsViewHelper *contactsViewHelper; - -@end - -#pragma mark - - -@implementation OWSAddToContactViewController - -- (instancetype)init -{ - self = [super init]; - if (!self) { - return self; - } - - [self commonInit]; - - return self; -} - -- (nullable instancetype)initWithCoder:(NSCoder *)aDecoder -{ - self = [super initWithCoder:aDecoder]; - if (!self) { - return self; - } - - [self commonInit]; - - return self; -} - -- (instancetype)initWithNibName:(nullable NSString *)nibNameOrNil bundle:(nullable NSBundle *)nibBundleOrNil -{ - self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil]; - if (!self) { - return self; - } - - [self commonInit]; - - return self; -} - -- (void)commonInit -{ - _contactsManager = Environment.shared.contactsManager; - _contactsViewHelper = [[ContactsViewHelper alloc] initWithDelegate:self]; -} - -- (void)configureWithRecipientId:(NSString *)recipientId -{ - OWSAssertDebug(recipientId.length > 0); - - _recipientId = recipientId; -} - -#pragma mark - ContactEditingDelegate - -- (void)didFinishEditingContact -{ - OWSLogDebug(@""); - [self dismissViewControllerAnimated:NO - completion:^{ - [self.navigationController popViewControllerAnimated:YES]; - }]; -} - -#pragma mark - CNContactViewControllerDelegate - -- (void)contactViewController:(CNContactViewController *)viewController - didCompleteWithContact:(nullable CNContact *)contact -{ - if (contact) { - // Saving normally returns you to the "Show Contact" view - // which we're not interested in, so we skip it here. There is - // an unfortunate blip of the "Show Contact" view on slower devices. - OWSLogDebug(@"completed editing contact."); - [self dismissViewControllerAnimated:NO - completion:^{ - [self.navigationController popViewControllerAnimated:YES]; - }]; - } else { - OWSLogDebug(@"canceled editing contact."); - [self dismissViewControllerAnimated:YES - completion:^{ - [self.navigationController popViewControllerAnimated:YES]; - }]; - } -} - -#pragma mark - ContactsViewHelperDelegate - -- (void)contactsViewHelperDidUpdateContacts -{ - [self updateTableContents]; -} - -#pragma mark - View Lifecycle - -- (void)viewDidLoad -{ - [super viewDidLoad]; - - self.title = NSLocalizedString(@"CONVERSATION_SETTINGS_ADD_TO_EXISTING_CONTACT", - @"Label for 'new contact' button in conversation settings view."); - - [self updateTableContents]; -} - -- (nullable NSString *)displayNameForContact:(Contact *)contact -{ - OWSAssertDebug(contact); - - if (contact.fullName.length > 0) { - return contact.fullName; - } - - for (NSString *email in contact.emails) { - if (email.length > 0) { - return email; - } - } - for (NSString *phoneNumber in contact.userTextPhoneNumbers) { - if (phoneNumber.length > 0) { - return phoneNumber; - } - } - - return nil; -} - -- (void)updateTableContents -{ - OWSTableContents *contents = [OWSTableContents new]; - contents.title = NSLocalizedString(@"CONVERSATION_SETTINGS", @"title for conversation settings screen"); - - __weak OWSAddToContactViewController *weakSelf = self; - - OWSTableSection *section = [OWSTableSection new]; - section.headerTitle = NSLocalizedString( - @"EDIT_GROUP_CONTACTS_SECTION_TITLE", @"a title for the contacts section of the 'new/update group' view."); - - for (Contact *contact in self.contactsViewHelper.contactsManager.allContacts) { - NSString *_Nullable displayName = [self displayNameForContact:contact]; - if (displayName.length < 1) { - continue; - } - - // TODO: Confirm with nancy if this will work. - NSString *cellName = [NSString stringWithFormat:@"contact.%@", NSUUID.UUID.UUIDString]; - [section addItem:[OWSTableItem disclosureItemWithText:displayName - accessibilityIdentifier:ACCESSIBILITY_IDENTIFIER_WITH_NAME(self, cellName) - actionBlock:^{ - [weakSelf presentContactViewControllerForContact:contact]; - }]]; - } - [contents addSection:section]; - - self.contents = contents; - [self.tableView reloadData]; -} - -- (void)viewWillAppear:(BOOL)animated -{ - [super viewWillAppear:animated]; -} - -#pragma mark - Actions - -- (void)presentContactViewControllerForContact:(Contact *)contact -{ - OWSAssertDebug(contact); - OWSAssertDebug(self.recipientId); - - if (!self.contactsManager.supportsContactEditing) { - OWSFailDebug(@"Contact editing not supported"); - return; - } - CNContact *_Nullable cnContact = [self.contactsManager cnContactWithId:contact.cnContactId]; - if (!cnContact) { - OWSFailDebug(@"Could not load system contact."); - return; - } - [self.contactsViewHelper presentContactViewControllerForRecipientId:self.recipientId - fromViewController:self - editImmediately:YES - addToExistingCnContact:cnContact]; -} - -@end - -NS_ASSUME_NONNULL_END diff --git a/Session/Signal/OWSAnalytics.swift b/Session/Signal/OWSAnalytics.swift deleted file mode 100644 index d083a34ce..000000000 --- a/Session/Signal/OWSAnalytics.swift +++ /dev/null @@ -1,22 +0,0 @@ -// -// Copyright (c) 2017 Open Whisper Systems. All rights reserved. -// - -import Foundation -import SignalUtilitiesKit - -func FormatAnalyticsLocation(file: String, function: String) -> NSString { - return "\((file as NSString).lastPathComponent):\(function)" as NSString -} - -func OWSProdError(_ eventName: String, file: String, function: String, line: Int32) { - let location = FormatAnalyticsLocation(file: file, function: function) - OWSAnalytics - .logEvent(eventName, severity: .error, parameters: nil, location: location.utf8String!, line:line) -} - -func OWSProdInfo(_ eventName: String, file: String, function: String, line: Int32) { - let location = FormatAnalyticsLocation(file: file, function: function) - OWSAnalytics - .logEvent(eventName, severity: .info, parameters: nil, location: location.utf8String!, line:line) -} diff --git a/Session/Signal/OWSBackupExportJob.m b/Session/Signal/OWSBackupExportJob.m index 54669197c..46dcb1b99 100644 --- a/Session/Signal/OWSBackupExportJob.m +++ b/Session/Signal/OWSBackupExportJob.m @@ -411,25 +411,7 @@ NS_ASSUME_NONNULL_BEGIN self.backupIO = [[OWSBackupIO alloc] initWithJobTempDirPath:self.jobTempDirPath]; - // We need to verify that we have a valid account. - // Otherwise, if we re-register on another device, we - // continue to backup on our old device, overwriting - // backups from the new device. - // - // We use an arbitrary request that requires authentication - // to verify our account state. - return [AnyPromise promiseWithResolverBlock:^(PMKResolver resolve) { - TSRequest *currentSignedPreKey = [OWSRequestFactory currentSignedPreKeyRequest]; - [[TSNetworkManager sharedManager] makeRequest:currentSignedPreKey - success:^(NSURLSessionDataTask *task, NSDictionary *responseObject) { - resolve(@(1)); - } - failure:^(NSURLSessionDataTask *task, NSError *error) { - // TODO: We may want to surface this in the UI. - OWSLogError(@"could not verify account status: %@.", error); - resolve(error); - }]; - }]; + return [AnyPromise promiseWithValue:@(1)]; } - (AnyPromise *)fetchAllRecords diff --git a/Session/Signal/OWSBackupSettingsViewController.m b/Session/Signal/OWSBackupSettingsViewController.m index 6b9d33212..d00b30a4d 100644 --- a/Session/Signal/OWSBackupSettingsViewController.m +++ b/Session/Signal/OWSBackupSettingsViewController.m @@ -5,7 +5,7 @@ #import "OWSBackupSettingsViewController.h" #import "OWSBackup.h" #import "Session-Swift.h" -#import "ThreadUtil.h" + #import #import #import diff --git a/Session/Signal/OWSConversationSettingsViewController.m b/Session/Signal/OWSConversationSettingsViewController.m index 1d53d98d8..e10b9c7c6 100644 --- a/Session/Signal/OWSConversationSettingsViewController.m +++ b/Session/Signal/OWSConversationSettingsViewController.m @@ -4,22 +4,19 @@ #import "OWSConversationSettingsViewController.h" #import "BlockListUIUtils.h" -#import "ContactsViewHelper.h" -#import "FingerprintViewController.h" -#import "OWSAddToContactViewController.h" + + + #import "OWSBlockingManager.h" #import "OWSSoundSettingsViewController.h" -#import "PhoneNumber.h" -#import "ShowGroupMembersViewController.h" + #import "Session-Swift.h" #import "UIFont+OWS.h" #import "UIView+OWS.h" -#import "UpdateGroupViewController.h" #import #import #import -#import -#import + #import #import #import @@ -27,7 +24,7 @@ #import #import #import -#import + #import #import #import @@ -42,8 +39,7 @@ NS_ASSUME_NONNULL_BEGIN const CGFloat kIconViewLength = 24; -@interface OWSConversationSettingsViewController () 0); @@ -1031,24 +823,6 @@ static CGRect oldframe; return stackView; } -- (void)conversationNameTouched:(UIGestureRecognizer *)sender -{ - if (sender.state == UIGestureRecognizerStateRecognized) { - if (self.isGroupThread) { - CGPoint location = [sender locationInView:self.avatarView]; - if (CGRectContainsPoint(self.avatarView.bounds, location)) { - [self showUpdateGroupView:UpdateGroupMode_EditGroupAvatar]; - } else { - [self showUpdateGroupView:UpdateGroupMode_EditGroupName]; - } - } else { - if (self.contactsManager.supportsContactEditing) { - [self presentContactViewController]; - } - } - } -} - - (UIImageView *)viewForIconWithName:(NSString *)iconName { UIImage *icon = [UIImage imageNamed:iconName]; @@ -1102,11 +876,15 @@ static CGRect oldframe; createdInExistingGroup:NO]; [infoMessage saveWithTransaction:transaction]; + // TODO TODO TODO + + /* OWSDisappearingMessagesConfigurationMessage *message = [[OWSDisappearingMessagesConfigurationMessage alloc] initWithConfiguration:self.disappearingMessagesConfiguration thread:self.thread]; [self.messageSenderJobQueue addMessage:message transaction:transaction]; + */ }]; } } @@ -1122,14 +900,6 @@ static CGRect oldframe; }]; } -- (void)showVerificationView -{ - NSString *recipientId = self.thread.contactIdentifier; - OWSAssertDebug(recipientId.length > 0); - - [FingerprintViewController presentFromViewController:self recipientId:recipientId]; -} - - (void)showGroupMembersView { TSGroupThread *thread = (TSGroupThread *)self.thread; @@ -1137,57 +907,6 @@ static CGRect oldframe; [self.navigationController pushViewController:groupMembersVC animated:YES]; } -- (void)showUpdateGroupView:(UpdateGroupMode)mode -{ - OWSAssertDebug(self.conversationSettingsViewDelegate); - - UpdateGroupViewController *updateGroupViewController = [UpdateGroupViewController new]; - updateGroupViewController.conversationSettingsViewDelegate = self.conversationSettingsViewDelegate; - updateGroupViewController.thread = (TSGroupThread *)self.thread; - updateGroupViewController.mode = mode; - [self.navigationController pushViewController:updateGroupViewController animated:YES]; -} - -- (void)presentContactViewController -{ - if (!self.contactsManager.supportsContactEditing) { - OWSFailDebug(@"Contact editing not supported"); - return; - } - if (![self.thread isKindOfClass:[TSContactThread class]]) { - OWSFailDebug(@"unexpected thread: %@", [self.thread class]); - return; - } - - TSContactThread *contactThread = (TSContactThread *)self.thread; - [self.contactsViewHelper presentContactViewControllerForRecipientId:contactThread.contactIdentifier - fromViewController:self - editImmediately:YES]; -} - -- (void)presentAddToContactViewControllerWithRecipientId:(NSString *)recipientId -{ - if (!self.contactsManager.supportsContactEditing) { - // Should not expose UI that lets the user get here. - OWSFailDebug(@"Contact editing not supported."); - return; - } - - if (!self.contactsManager.isSystemContactsAuthorized) { - [self.contactsViewHelper presentMissingContactAccessAlertControllerFromViewController:self]; - return; - } - - OWSAddToContactViewController *viewController = [OWSAddToContactViewController new]; - [viewController configureWithRecipientId:recipientId]; - [self.navigationController pushViewController:viewController animated:YES]; -} - -- (void)didTapEditButton -{ - [self presentContactViewController]; -} - - (void)editGroup { LKEditClosedGroupVC *editClosedGroupVC = [[LKEditClosedGroupVC alloc] initWithThreadID:self.thread.uniqueId]; @@ -1218,7 +937,7 @@ static CGRect oldframe; { if (self.isGroupThread) { TSGroupThread *groupThread = (TSGroupThread *)self.thread; - return !groupThread.isLocalUserInGroup; + return !groupThread.isCurrentUserInGroup; } return NO; @@ -1233,14 +952,6 @@ static CGRect oldframe; [LKStorage writeSyncWithBlock:^(YapDatabaseReadWriteTransaction *_Nonnull transaction) { [[LKClosedGroupsProtocol leaveGroupWithPublicKey:groupPublicKey transaction:transaction] retainUntilComplete]; }]; - } else { - TSOutgoingMessage *message = - [TSOutgoingMessage outgoingMessageInThread:gThread groupMetaMessage:TSGroupMetaMessageQuit expiresInSeconds:0]; - - [LKStorage writeSyncWithBlock:^(YapDatabaseReadWriteTransaction *_Nonnull transaction) { - [self.messageSenderJobQueue addMessage:message transaction:transaction]; - [gThread leaveGroupWithTransaction:transaction]; - }]; } [self.navigationController popViewControllerAnimated:YES]; @@ -1273,8 +984,6 @@ static CGRect oldframe; [BlockListUIUtils showBlockThreadActionSheet:self.thread fromViewController:self blockingManager:self.blockingManager - contactsManager:self.contactsManager - messageSender:self.messageSender completionBlock:^(BOOL isBlocked) { // Update switch state if user cancels action. blockConversationSwitch.on = isBlocked; @@ -1290,7 +999,6 @@ static CGRect oldframe; [BlockListUIUtils showUnblockThreadActionSheet:self.thread fromViewController:self blockingManager:self.blockingManager - contactsManager:self.contactsManager completionBlock:^(BOOL isBlocked) { // Update switch state if user cancels action. blockConversationSwitch.on = isBlocked; @@ -1498,7 +1206,6 @@ static CGRect oldframe; [alert addAction:[UIAlertAction actionWithTitle:@"Reset" style:UIAlertActionStyleDefault handler:^(UIAlertAction *action) { dispatch_async(dispatch_get_main_queue(), ^{ [LKStorage writeSyncWithBlock:^(YapDatabaseReadWriteTransaction *transaction) { - [thread addSessionRestoreDevice:thread.contactIdentifier transaction:transaction]; [LKSessionManagementProtocol startSessionResetInThread:thread transaction:transaction]; }]; [weakSelf.navigationController popViewControllerAnimated:YES]; diff --git a/Session/Signal/OWSDeviceTableViewCell.h b/Session/Signal/OWSDeviceTableViewCell.h deleted file mode 100644 index 0db516612..000000000 --- a/Session/Signal/OWSDeviceTableViewCell.h +++ /dev/null @@ -1,20 +0,0 @@ -// -// Copyright (c) 2019 Open Whisper Systems. All rights reserved. -// - -#import -#import - -NS_ASSUME_NONNULL_BEGIN - -@interface OWSDeviceTableViewCell : UITableViewCell - -@property (nonatomic) UILabel *nameLabel; -@property (nonatomic) UILabel *linkedLabel; -@property (nonatomic) UILabel *lastSeenLabel; - -- (void)configureWithDevice:(OWSDevice *)device; - -@end - -NS_ASSUME_NONNULL_END diff --git a/Session/Signal/OWSDeviceTableViewCell.m b/Session/Signal/OWSDeviceTableViewCell.m deleted file mode 100644 index e967f5825..000000000 --- a/Session/Signal/OWSDeviceTableViewCell.m +++ /dev/null @@ -1,85 +0,0 @@ -// -// Copyright (c) 2019 Open Whisper Systems. All rights reserved. -// - -#import "OWSDeviceTableViewCell.h" -#import "DateUtil.h" -#import -#import -#import -#import - -NS_ASSUME_NONNULL_BEGIN - -@implementation OWSDeviceTableViewCell - -- (instancetype)initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(nullable NSString *)reuseIdentifier -{ - if (self = [super initWithStyle:style reuseIdentifier:reuseIdentifier]) { - [self configure]; - } - return self; -} - -- (void)configure -{ - self.preservesSuperviewLayoutMargins = YES; - self.contentView.preservesSuperviewLayoutMargins = YES; - - self.nameLabel = [UILabel new]; - self.linkedLabel = [UILabel new]; - self.lastSeenLabel = [UILabel new]; - - UIStackView *stackView = [[UIStackView alloc] initWithArrangedSubviews:@[ - self.nameLabel, - self.linkedLabel, - self.lastSeenLabel, - ]]; - stackView.axis = UILayoutConstraintAxisVertical; - stackView.alignment = UIStackViewAlignmentLeading; - stackView.spacing = 2; - [self.contentView addSubview:stackView]; - [stackView ows_autoPinToSuperviewMargins]; -} - -- (void)configureWithDevice:(OWSDevice *)device -{ - OWSAssertDebug(device); - - [OWSTableItem configureCell:self]; - - self.nameLabel.font = UIFont.ows_dynamicTypeBodyFont; - self.linkedLabel.font = UIFont.ows_dynamicTypeCaption1Font; - self.lastSeenLabel.font = UIFont.ows_dynamicTypeCaption1Font; - - self.nameLabel.textColor = Theme.primaryColor; - self.linkedLabel.textColor = Theme.secondaryColor; - self.lastSeenLabel.textColor = Theme.secondaryColor; - - self.nameLabel.text = device.displayName; - - NSString *linkedFormatString - = NSLocalizedString(@"DEVICE_LINKED_AT_LABEL", @"{{Short Date}} when device was linked."); - self.linkedLabel.text = - [NSString stringWithFormat:linkedFormatString, [DateUtil.dateFormatter stringFromDate:device.createdAt]]; - - NSString *lastSeenFormatString = NSLocalizedString( - @"DEVICE_LAST_ACTIVE_AT_LABEL", @"{{Short Date}} when device last communicated with Signal Server."); - - NSDate *displayedLastSeenAt; - // lastSeenAt is stored at day granularity. At midnight UTC. - // Making it likely that when you first link a device it will - // be "last seen" the day before it was created, which looks broken. - if ([device.lastSeenAt compare:device.createdAt] == NSOrderedDescending) { - displayedLastSeenAt = device.lastSeenAt; - } else { - displayedLastSeenAt = device.createdAt; - } - - self.lastSeenLabel.text = - [NSString stringWithFormat:lastSeenFormatString, [DateUtil.dateFormatter stringFromDate:displayedLastSeenAt]]; -} - -@end - -NS_ASSUME_NONNULL_END diff --git a/Session/Signal/OWSOrphanDataCleaner.m b/Session/Signal/OWSOrphanDataCleaner.m index f21ba3aff..6d6e1e909 100644 --- a/Session/Signal/OWSOrphanDataCleaner.m +++ b/Session/Signal/OWSOrphanDataCleaner.m @@ -9,7 +9,7 @@ #import #import #import -#import + #import #import #import diff --git a/Session/Signal/Pastelog.h b/Session/Signal/Pastelog.h deleted file mode 100644 index 0d46da066..000000000 --- a/Session/Signal/Pastelog.h +++ /dev/null @@ -1,18 +0,0 @@ -// -// Copyright (c) 2018 Open Whisper Systems. All rights reserved. -// - -NS_ASSUME_NONNULL_BEGIN - -typedef void (^SubmitDebugLogsCompletion)(void); - -@interface Pastelog : NSObject - -- (instancetype)init NS_UNAVAILABLE; - -+ (void)submitLogs; -+ (void)submitLogsWithCompletion:(nullable SubmitDebugLogsCompletion)completion; - -@end - -NS_ASSUME_NONNULL_END diff --git a/Session/Signal/Pastelog.m b/Session/Signal/Pastelog.m deleted file mode 100644 index 8596239ff..000000000 --- a/Session/Signal/Pastelog.m +++ /dev/null @@ -1,647 +0,0 @@ -// -// Copyright (c) 2019 Open Whisper Systems. All rights reserved. -// - -#import "Pastelog.h" -#import "Session-Swift.h" -#import "ThreadUtil.h" -#import "zlib.h" -#import -#import -#import -#import -#import -#import -#import -#import -#import -#import -#import -#import - -NS_ASSUME_NONNULL_BEGIN - -typedef void (^UploadDebugLogsSuccess)(NSURL *url); -typedef void (^UploadDebugLogsFailure)(NSString *localizedErrorMessage); - -#pragma mark - - -@class DebugLogUploader; - -typedef void (^DebugLogUploadSuccess)(DebugLogUploader *uploader, NSURL *url); -typedef void (^DebugLogUploadFailure)(DebugLogUploader *uploader, NSError *error); - -@interface DebugLogUploader : NSObject - -@property (nonatomic) NSURL *fileUrl; -@property (nonatomic) NSString *mimeType; -@property (nonatomic, nullable) DebugLogUploadSuccess success; -@property (nonatomic, nullable) DebugLogUploadFailure failure; - -@end - -#pragma mark - - -@implementation DebugLogUploader - -- (void)dealloc -{ - OWSLogVerbose(@""); -} - -- (void)uploadFileWithURL:(NSURL *)fileUrl - mimeType:(NSString *)mimeType - success:(DebugLogUploadSuccess)success - failure:(DebugLogUploadFailure)failure -{ - OWSAssertDebug(fileUrl); - OWSAssertDebug(mimeType.length > 0); - OWSAssertDebug(success); - OWSAssertDebug(failure); - - self.fileUrl = fileUrl; - self.mimeType = mimeType; - self.success = success; - self.failure = failure; - - [self getUploadParameters]; -} - -- (void)getUploadParameters -{ - __weak DebugLogUploader *weakSelf = self; - - NSURLSessionConfiguration *sessionConf = NSURLSessionConfiguration.ephemeralSessionConfiguration; - AFHTTPSessionManager *sessionManager = - [[AFHTTPSessionManager alloc] initWithBaseURL:nil sessionConfiguration:sessionConf]; - sessionManager.requestSerializer = [AFHTTPRequestSerializer serializer]; - sessionManager.responseSerializer = [AFJSONResponseSerializer serializer]; - NSString *urlString = @"https://debuglogs.org/"; - [sessionManager GET:urlString - parameters:nil - headers:nil - progress:nil - success:^(NSURLSessionDataTask *task, id _Nullable responseObject) { - DebugLogUploader *strongSelf = weakSelf; - if (!strongSelf) { - return; - } - - if (![responseObject isKindOfClass:[NSDictionary class]]) { - OWSLogError(@"Invalid response: %@, %@", urlString, responseObject); - [strongSelf - failWithError:OWSErrorWithCodeDescription(OWSErrorCodeDebugLogUploadFailed, @"Invalid response")]; - return; - } - NSString *uploadUrl = responseObject[@"url"]; - if (![uploadUrl isKindOfClass:[NSString class]] || uploadUrl.length < 1) { - OWSLogError(@"Invalid response: %@, %@", urlString, responseObject); - [strongSelf - failWithError:OWSErrorWithCodeDescription(OWSErrorCodeDebugLogUploadFailed, @"Invalid response")]; - return; - } - NSDictionary *fields = responseObject[@"fields"]; - if (![fields isKindOfClass:[NSDictionary class]] || fields.count < 1) { - OWSLogError(@"Invalid response: %@, %@", urlString, responseObject); - [strongSelf - failWithError:OWSErrorWithCodeDescription(OWSErrorCodeDebugLogUploadFailed, @"Invalid response")]; - return; - } - for (NSString *fieldName in fields) { - NSString *fieldValue = fields[fieldName]; - if (![fieldName isKindOfClass:[NSString class]] || fieldName.length < 1 - || ![fieldValue isKindOfClass:[NSString class]] || fieldValue.length < 1) { - OWSLogError(@"Invalid response: %@, %@", urlString, responseObject); - [strongSelf failWithError:OWSErrorWithCodeDescription( - OWSErrorCodeDebugLogUploadFailed, @"Invalid response")]; - return; - } - } - NSString *_Nullable uploadKey = fields[@"key"]; - if (![uploadKey isKindOfClass:[NSString class]] || uploadKey.length < 1) { - OWSLogError(@"Invalid response: %@, %@", urlString, responseObject); - [strongSelf - failWithError:OWSErrorWithCodeDescription(OWSErrorCodeDebugLogUploadFailed, @"Invalid response")]; - return; - } - - // Add a file extension to the upload's key. - NSString *fileExtension = strongSelf.fileUrl.lastPathComponent.pathExtension; - if (fileExtension.length < 1) { - OWSLogError(@"Invalid file url: %@, %@", urlString, responseObject); - [strongSelf - failWithError:OWSErrorWithCodeDescription(OWSErrorCodeDebugLogUploadFailed, @"Invalid file url")]; - return; - } - uploadKey = [uploadKey stringByAppendingPathExtension:fileExtension]; - NSMutableDictionary *updatedFields = [fields mutableCopy]; - updatedFields[@"key"] = uploadKey; - - [strongSelf uploadFileWithUploadUrl:uploadUrl fields:updatedFields uploadKey:uploadKey]; - } - failure:^(NSURLSessionDataTask *_Nullable task, NSError *error) { - OWSLogError(@"failed: %@", urlString); - [weakSelf failWithError:error]; - }]; -} - -- (void)uploadFileWithUploadUrl:(NSString *)uploadUrl fields:(NSDictionary *)fields uploadKey:(NSString *)uploadKey -{ - OWSAssertDebug(uploadUrl.length > 0); - OWSAssertDebug(fields); - OWSAssertDebug(uploadKey.length > 0); - - __weak DebugLogUploader *weakSelf = self; - NSURLSessionConfiguration *sessionConf = NSURLSessionConfiguration.ephemeralSessionConfiguration; - AFHTTPSessionManager *sessionManager = - [[AFHTTPSessionManager alloc] initWithBaseURL:nil sessionConfiguration:sessionConf]; - sessionManager.requestSerializer = [AFHTTPRequestSerializer serializer]; - sessionManager.responseSerializer = [AFHTTPResponseSerializer serializer]; - [sessionManager POST:uploadUrl - parameters:@{} - headers:nil - constructingBodyWithBlock:^(id formData) { - for (NSString *fieldName in fields) { - NSString *fieldValue = fields[fieldName]; - [formData appendPartWithFormData:[fieldValue dataUsingEncoding:NSUTF8StringEncoding] name:fieldName]; - } - [formData appendPartWithFormData:[weakSelf.mimeType dataUsingEncoding:NSUTF8StringEncoding] - name:@"content-type"]; - - NSError *error; - BOOL success = [formData appendPartWithFileURL:weakSelf.fileUrl - name:@"file" - fileName:weakSelf.fileUrl.lastPathComponent - mimeType:weakSelf.mimeType - error:&error]; - if (!success || error) { - OWSLogError(@"failed: %@, error: %@", uploadUrl, error); - } - } - progress:nil - success:^(NSURLSessionDataTask *task, id _Nullable responseObject) { - OWSLogVerbose(@"Response: %@, %@", uploadUrl, responseObject); - - NSString *urlString = [NSString stringWithFormat:@"https://debuglogs.org/%@", uploadKey]; - [self succeedWithUrl:[NSURL URLWithString:urlString]]; - } - failure:^(NSURLSessionDataTask *_Nullable task, NSError *error) { - OWSLogError(@"upload: %@ failed with error: %@", uploadUrl, error); - [weakSelf failWithError:error]; - }]; -} - -- (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response -{ - NSHTTPURLResponse *httpResponse = (NSHTTPURLResponse *)response; - - NSInteger statusCode = httpResponse.statusCode; - // We'll accept any 2xx status code. - NSInteger statusCodeClass = statusCode - (statusCode % 100); - if (statusCodeClass != 200) { - OWSLogError(@"statusCode: %zd, %zd", statusCode, statusCodeClass); - OWSLogError(@"headers: %@", httpResponse.allHeaderFields); - [self failWithError:[NSError errorWithDomain:@"PastelogKit" - code:10001 - userInfo:@{ NSLocalizedDescriptionKey : @"Invalid response code." }]]; - } -} - -- (void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error -{ - OWSLogVerbose(@""); - - [self failWithError:error]; -} - -- (void)failWithError:(NSError *)error -{ - OWSAssertDebug(error); - - OWSLogError(@"%@", error); - - DispatchMainThreadSafe(^{ - // Call the completions exactly once. - if (self.failure) { - self.failure(self, error); - } - self.success = nil; - self.failure = nil; - }); -} - -- (void)succeedWithUrl:(NSURL *)url -{ - OWSAssertDebug(url); - - OWSLogVerbose(@"%@", url); - - DispatchMainThreadSafe(^{ - // Call the completions exactly once. - if (self.success) { - self.success(self, url); - } - self.success = nil; - self.failure = nil; - }); -} - -@end - -#pragma mark - - -@interface Pastelog () - -@property (nonatomic) UIAlertController *loadingAlert; - -@property (nonatomic) DebugLogUploader *currentUploader; - -@end - -#pragma mark - - -@implementation Pastelog - -+ (instancetype)sharedManager -{ - static Pastelog *sharedMyManager = nil; - static dispatch_once_t onceToken; - dispatch_once(&onceToken, ^{ - sharedMyManager = [[self alloc] initDefault]; - }); - return sharedMyManager; -} - -- (instancetype)initDefault -{ - self = [super init]; - - if (!self) { - return self; - } - - OWSSingletonAssert(); - - return self; -} - -#pragma mark - Dependencies - -- (YapDatabaseConnection *)dbConnection -{ - return SSKEnvironment.shared.primaryStorage.dbReadWriteConnection; -} - -- (TSAccountManager *)tsAccountManager -{ - OWSAssertDebug(SSKEnvironment.shared.tsAccountManager); - - return SSKEnvironment.shared.tsAccountManager; -} - -#pragma mark - - -+ (void)submitLogs -{ - [self submitLogsWithCompletion:nil]; -} - -+ (void)submitLogsWithCompletion:(nullable SubmitDebugLogsCompletion)completionParam -{ - SubmitDebugLogsCompletion completion = ^{ - if (completionParam) { - // Wait a moment. If PasteLog opens a URL, it needs a moment to complete. - dispatch_after( - dispatch_time(DISPATCH_TIME_NOW, 2 * NSEC_PER_SEC), dispatch_get_main_queue(), completionParam); - } - }; - - [[self sharedManager] uploadLogsWithUIWithSuccess:^(NSURL *url) { - UIAlertController *alert = [UIAlertController - alertControllerWithTitle:NSLocalizedString(@"DEBUG_LOG_ALERT_TITLE", @"Title of the debug log alert.") - message:NSLocalizedString(@"DEBUG_LOG_ALERT_MESSAGE", @"Message of the debug log alert.") - preferredStyle:UIAlertControllerStyleAlert]; - [alert - addAction:[UIAlertAction - actionWithTitle:NSLocalizedString(@"DEBUG_LOG_ALERT_OPTION_EMAIL", - @"Label for the 'email debug log' option of the debug log alert.") - accessibilityIdentifier:ACCESSIBILITY_IDENTIFIER_WITH_NAME(self, @"send_email") - style:UIAlertActionStyleDefault - handler:^(UIAlertAction *action) { - [Pastelog.sharedManager submitEmail:url]; - - completion(); - }]]; - [alert addAction:[UIAlertAction actionWithTitle:NSLocalizedString(@"DEBUG_LOG_ALERT_OPTION_COPY_LINK", - @"Label for the 'copy link' option of the debug log alert.") - accessibilityIdentifier:ACCESSIBILITY_IDENTIFIER_WITH_NAME(self, @"copy_link") - style:UIAlertActionStyleDefault - handler:^(UIAlertAction *action) { - UIPasteboard *pb = [UIPasteboard generalPasteboard]; - [pb setString:url.absoluteString]; - - completion(); - }]]; -#ifdef DEBUG - [alert - addAction:[UIAlertAction actionWithTitle:NSLocalizedString(@"DEBUG_LOG_ALERT_OPTION_SEND_TO_SELF", - @"Label for the 'send to self' option of the debug log alert.") - accessibilityIdentifier:ACCESSIBILITY_IDENTIFIER_WITH_NAME(self, @"send_to_self") - style:UIAlertActionStyleDefault - handler:^(UIAlertAction *action) { - [Pastelog.sharedManager sendToSelf:url]; - }]]; - [alert addAction:[UIAlertAction actionWithTitle: - NSLocalizedString(@"DEBUG_LOG_ALERT_OPTION_SEND_TO_LAST_THREAD", - @"Label for the 'send to last thread' option of the debug log alert.") - accessibilityIdentifier:ACCESSIBILITY_IDENTIFIER_WITH_NAME(self, @"send_to_last_thread") - style:UIAlertActionStyleDefault - handler:^(UIAlertAction *action) { - [Pastelog.sharedManager sendToMostRecentThread:url]; - }]]; -#endif - [alert - addAction: - [UIAlertAction actionWithTitle:NSLocalizedString(@"DEBUG_LOG_ALERT_OPTION_BUG_REPORT", - @"Label for the 'Open a Bug Report' option of the debug log alert.") - accessibilityIdentifier:ACCESSIBILITY_IDENTIFIER_WITH_NAME(self, @"submit_bug_report") - style:UIAlertActionStyleDefault - handler:^(UIAlertAction *action) { - [Pastelog.sharedManager prepareRedirection:url completion:completion]; - }]]; - [alert addAction:[UIAlertAction actionWithTitle:NSLocalizedString(@"DEBUG_LOG_ALERT_OPTION_SHARE", - @"Label for the 'Share' option of the debug log alert.") - accessibilityIdentifier:ACCESSIBILITY_IDENTIFIER_WITH_NAME(self, @"share") - style:UIAlertActionStyleDefault - handler:^(UIAlertAction *action) { - [AttachmentSharing showShareUIForText:url.absoluteString - completion:completion]; - }]]; - [alert addAction:[OWSAlerts cancelAction]]; - UIViewController *presentingViewController - = UIApplication.sharedApplication.frontmostViewControllerIgnoringAlerts; - [presentingViewController presentAlert:alert animated:NO]; - }]; -} - -- (void)uploadLogsWithUIWithSuccess:(UploadDebugLogsSuccess)successParam { - OWSAssertIsOnMainThread(); - - [ModalActivityIndicatorViewController - presentFromViewController:UIApplication.sharedApplication.frontmostViewControllerIgnoringAlerts - canCancel:YES - backgroundBlock:^(ModalActivityIndicatorViewController *modalActivityIndicator) { - [self - uploadLogsWithSuccess:^(NSURL *url) { - OWSAssertIsOnMainThread(); - - if (modalActivityIndicator.wasCancelled) { - return; - } - - [modalActivityIndicator dismissWithCompletion:^{ - OWSAssertIsOnMainThread(); - - successParam(url); - }]; - } - failure:^(NSString *localizedErrorMessage) { - OWSAssertIsOnMainThread(); - - if (modalActivityIndicator.wasCancelled) { - return; - } - - [modalActivityIndicator dismissWithCompletion:^{ - OWSAssertIsOnMainThread(); - - [Pastelog showFailureAlertWithMessage:localizedErrorMessage]; - }]; - }]; - }]; -} - -- (void)uploadLogsWithSuccess:(UploadDebugLogsSuccess)successParam failure:(UploadDebugLogsFailure)failureParam { - OWSAssertDebug(successParam); - OWSAssertDebug(failureParam); - - // Ensure that we call the completions on the main thread. - UploadDebugLogsSuccess success = ^(NSURL *url) { - DispatchMainThreadSafe(^{ - successParam(url); - }); - }; - UploadDebugLogsFailure failure = ^(NSString *localizedErrorMessage) { - DispatchMainThreadSafe(^{ - failureParam(localizedErrorMessage); - }); - }; - - // Phase 1. Make a local copy of all of the log files. - NSDateFormatter *dateFormatter = [NSDateFormatter new]; - [dateFormatter setLocale:[NSLocale currentLocale]]; - [dateFormatter setDateFormat:@"yyyy.MM.dd hh.mm.ss"]; - NSString *dateString = [dateFormatter stringFromDate:[NSDate new]]; - NSString *logsName = [[dateString stringByAppendingString:@" "] stringByAppendingString:NSUUID.UUID.UUIDString]; - NSString *tempDirectory = OWSTemporaryDirectory(); - NSString *zipFilePath = - [tempDirectory stringByAppendingPathComponent:[logsName stringByAppendingPathExtension:@"zip"]]; - NSString *zipDirPath = [tempDirectory stringByAppendingPathComponent:logsName]; - [OWSFileSystem ensureDirectoryExists:zipDirPath]; - - NSArray *logFilePaths = DebugLogger.sharedLogger.allLogFilePaths; - if (logFilePaths.count < 1) { - failure(NSLocalizedString(@"DEBUG_LOG_ALERT_NO_LOGS", @"Error indicating that no debug logs could be found.")); - return; - } - - for (NSString *logFilePath in logFilePaths) { - NSString *copyFilePath = [zipDirPath stringByAppendingPathComponent:logFilePath.lastPathComponent]; - NSError *error; - [[NSFileManager defaultManager] copyItemAtPath:logFilePath toPath:copyFilePath error:&error]; - if (error) { - failure(NSLocalizedString( - @"DEBUG_LOG_ALERT_COULD_NOT_COPY_LOGS", @"Error indicating that the debug logs could not be copied.")); - return; - } - [OWSFileSystem protectFileOrFolderAtPath:copyFilePath]; - } - - // Phase 2. Zip up the log files. - BOOL zipSuccess = [SSZipArchive createZipFileAtPath:zipFilePath - withContentsOfDirectory:zipDirPath - keepParentDirectory:YES - compressionLevel:Z_DEFAULT_COMPRESSION - password:nil - AES:NO - progressHandler:nil]; - if (!zipSuccess) { - failure(NSLocalizedString( - @"DEBUG_LOG_ALERT_COULD_NOT_PACKAGE_LOGS", @"Error indicating that the debug logs could not be packaged.")); - return; - } - - [OWSFileSystem protectFileOrFolderAtPath:zipFilePath]; - [OWSFileSystem deleteFile:zipDirPath]; - - // Phase 3. Upload the log files. - - __weak Pastelog *weakSelf = self; - self.currentUploader = [DebugLogUploader new]; - [self.currentUploader uploadFileWithURL:[NSURL fileURLWithPath:zipFilePath] - mimeType:OWSMimeTypeApplicationZip - success:^(DebugLogUploader *uploader, NSURL *url) { - if (uploader != weakSelf.currentUploader) { - // Ignore events from obsolete uploaders. - return; - } - [OWSFileSystem deleteFile:zipFilePath]; - success(url); - } - failure:^(DebugLogUploader *uploader, NSError *error) { - if (uploader != weakSelf.currentUploader) { - // Ignore events from obsolete uploaders. - return; - } - [OWSFileSystem deleteFile:zipFilePath]; - failure(NSLocalizedString( - @"DEBUG_LOG_ALERT_ERROR_UPLOADING_LOG", @"Error indicating that a debug log could not be uploaded.")); - }]; -} - -+ (void)showFailureAlertWithMessage:(NSString *)message -{ - UIAlertController *alert = [UIAlertController - alertControllerWithTitle:NSLocalizedString(@"DEBUG_LOG_ALERT_TITLE", - @"Title of the alert shown for failures while uploading debug logs.") - message:message - preferredStyle:UIAlertControllerStyleAlert]; - [alert addAction:[UIAlertAction actionWithTitle:NSLocalizedString(@"OK", @"") - accessibilityIdentifier:ACCESSIBILITY_IDENTIFIER_WITH_NAME(self, @"ok") - style:UIAlertActionStyleDefault - handler:nil]]; - UIViewController *presentingViewController = UIApplication.sharedApplication.frontmostViewControllerIgnoringAlerts; - [presentingViewController presentAlert:alert animated:NO]; -} - -#pragma mark Logs submission - -- (void)submitEmail:(NSURL *)url -{ - NSString *emailAddress = [[NSBundle mainBundle] objectForInfoDictionaryKey:@"LOGS_EMAIL"]; - - NSMutableString *body = [NSMutableString new]; - - [body appendFormat:@"Tell us about the issue: \n\n\n"]; - - size_t size; - sysctlbyname("hw.machine", NULL, &size, NULL, 0); - char *machine = malloc(size); - sysctlbyname("hw.machine", machine, &size, NULL, 0); - NSString *platform = [NSString stringWithUTF8String:machine]; - free(machine); - - [body appendFormat:@"Device: %@ (%@)\n", UIDevice.currentDevice.model, platform]; - [body appendFormat:@"iOS Version: %@ \n", [UIDevice currentDevice].systemVersion]; - [body appendFormat:@"Signal Version: %@ \n", [[NSBundle mainBundle] objectForInfoDictionaryKey:@"CFBundleVersion"]]; - [body appendFormat:@"Log URL: %@ \n", url]; - - NSString *escapedBody = - [body stringByAddingPercentEncodingWithAllowedCharacters:NSCharacterSet.URLQueryAllowedCharacterSet]; - NSString *urlString = - [NSString stringWithFormat:@"mailto:%@?subject=iOS%%20Debug%%20Log&body=%@", emailAddress, escapedBody]; - - BOOL success = [UIApplication.sharedApplication openURL:[NSURL URLWithString:urlString]]; - if (!success) { - OWSLogError(@"Could not open Email app."); - [OWSAlerts showErrorAlertWithMessage:NSLocalizedString(@"DEBUG_LOG_COULD_NOT_EMAIL", - @"Error indicating that the app could not launch the Email app.")]; - } -} - -- (void)prepareRedirection:(NSURL *)url completion:(SubmitDebugLogsCompletion)completion -{ - OWSAssertDebug(completion); - - UIPasteboard *pb = [UIPasteboard generalPasteboard]; - [pb setString:url.absoluteString]; - - UIAlertController *alert = - [UIAlertController alertControllerWithTitle:NSLocalizedString(@"DEBUG_LOG_GITHUB_ISSUE_ALERT_TITLE", - @"Title of the alert before redirecting to GitHub Issues.") - message:NSLocalizedString(@"DEBUG_LOG_GITHUB_ISSUE_ALERT_MESSAGE", - @"Message of the alert before redirecting to GitHub Issues.") - preferredStyle:UIAlertControllerStyleAlert]; - [alert - addAction:[UIAlertAction - actionWithTitle:NSLocalizedString(@"OK", @"") - accessibilityIdentifier:ACCESSIBILITY_IDENTIFIER_WITH_NAME(self, @"ok") - style:UIAlertActionStyleDefault - handler:^(UIAlertAction *action) { - [UIApplication.sharedApplication - openURL:[NSURL - URLWithString:[[NSBundle mainBundle] - objectForInfoDictionaryKey:@"LOGS_URL"]]]; - - completion(); - }]]; - UIViewController *presentingViewController = UIApplication.sharedApplication.frontmostViewControllerIgnoringAlerts; - [presentingViewController presentAlert:alert animated:NO]; -} - -- (void)sendToSelf:(NSURL *)url -{ - if (![self.tsAccountManager isRegistered]) { - return; - } - NSString *recipientId = [TSAccountManager localNumber]; - - DispatchMainThreadSafe(^{ - __block TSThread *thread = nil; - [LKStorage writeSyncWithBlock:^(YapDatabaseReadWriteTransaction *transaction) { - thread = [TSContactThread getOrCreateThreadWithContactId:recipientId transaction:transaction]; - }]; - [LKStorage writeSyncWithBlock:^(YapDatabaseReadWriteTransaction *_Nonnull transaction) { - [ThreadUtil enqueueMessageWithText:url.absoluteString - inThread:thread - quotedReplyModel:nil - linkPreviewDraft:nil - transaction:transaction]; - }]; - }); - - // Also copy to pasteboard. - [[UIPasteboard generalPasteboard] setString:url.absoluteString]; -} - -- (void)sendToMostRecentThread:(NSURL *)url -{ - if (![self.tsAccountManager isRegistered]) { - return; - } - - __block TSThread *thread = nil; - [OWSPrimaryStorage.dbReadConnection readWithBlock:^(YapDatabaseReadTransaction *transaction) { - thread = [[transaction ext:TSThreadDatabaseViewExtensionName] firstObjectInGroup:TSInboxGroup]; - }]; - DispatchMainThreadSafe(^{ - if (thread) { - [LKStorage writeSyncWithBlock:^(YapDatabaseReadWriteTransaction *_Nonnull transaction) { - [ThreadUtil enqueueMessageWithText:url.absoluteString - inThread:thread - quotedReplyModel:nil - linkPreviewDraft:nil - transaction:transaction]; - }]; - } else { - [Pastelog showFailureAlertWithMessage:@"Could not find last thread."]; - } - }); - - // Also copy to pasteboard. - [[UIPasteboard generalPasteboard] setString:url.absoluteString]; -} - -@end - -NS_ASSUME_NONNULL_END diff --git a/Session/Signal/PinEntryView.h b/Session/Signal/PinEntryView.h deleted file mode 100644 index 83b562abd..000000000 --- a/Session/Signal/PinEntryView.h +++ /dev/null @@ -1,32 +0,0 @@ -// -// Copyright (c) 2018 Open Whisper Systems. All rights reserved. -// - -NS_ASSUME_NONNULL_BEGIN - -@class PinEntryView; - -@protocol PinEntryViewDelegate - -- (void)pinEntryView:(PinEntryView *)entryView submittedPinCode:(NSString *)pinCode; -- (void)pinEntryViewForgotPinLinkTapped:(PinEntryView *)entryView; - -@optional -- (void)pinEntryView:(PinEntryView *)entryView pinCodeDidChange:(NSString *)pinCode; - -@end - -@interface PinEntryView : UIView - -@property (nonatomic, weak, nullable) id delegate; -@property (nonatomic, readonly) BOOL hasValidPin; -@property (nullable, nonatomic) NSString *instructionsText; -@property (nullable, nonatomic) NSAttributedString *attributedInstructionsText; -@property (nonatomic, readonly) UIFont *boldLabelFont; - -- (void)clearText; -- (BOOL)makePinTextFieldFirstResponder; - -@end - -NS_ASSUME_NONNULL_END diff --git a/Session/Signal/PinEntryView.m b/Session/Signal/PinEntryView.m deleted file mode 100644 index 1ceaa224f..000000000 --- a/Session/Signal/PinEntryView.m +++ /dev/null @@ -1,227 +0,0 @@ -// -// Copyright (c) 2018 Open Whisper Systems. All rights reserved. -// - -#import "PinEntryView.h" -#import "Session-Swift.h" -#import -#import -#import -#import - -NS_ASSUME_NONNULL_BEGIN - -@interface PinEntryView () - -@property (nonatomic) UITextField *pinTextfield; -@property (nonatomic) OWSFlatButton *submitButton; -@property (nonatomic) UILabel *instructionsLabel; - -@end - -@implementation PinEntryView : UIView - -- (instancetype)initWithFrame:(CGRect)frame -{ - self = [super initWithFrame:frame]; - if (!self) { - return self; - } - - [self createContents]; - - return self; -} - -#pragma mark - view creation -- (UIFont *)labelFont -{ - return [UIFont ows_regularFontWithSize:ScaleFromIPhone5To7Plus(14.f, 16.f)]; -} - -- (UIFont *)boldLabelFont -{ - return [UIFont ows_boldFontWithSize:ScaleFromIPhone5To7Plus(14.f, 16.f)]; -} - -- (UILabel *)createLabelWithText:(nullable NSString *)text -{ - UILabel *label = [UILabel new]; - label.textColor = [Theme primaryColor]; - label.text = text; - label.font = self.labelFont; - label.numberOfLines = 0; - label.lineBreakMode = NSLineBreakByWordWrapping; - label.textAlignment = NSTextAlignmentCenter; - [self addSubview:label]; - return label; -} - -- (void)createPinTextfield -{ - if (UIDevice.currentDevice.isShorterThanIPhone5) { - self.pinTextfield = [DismissableTextField new]; - } else { - self.pinTextfield = [OWSTextField new]; - } - - self.pinTextfield.textColor = [Theme primaryColor]; - self.pinTextfield.font = [UIFont ows_mediumFontWithSize:ScaleFromIPhone5To7Plus(30.f, 36.f)]; - self.pinTextfield.textAlignment = NSTextAlignmentCenter; - self.pinTextfield.keyboardType = UIKeyboardTypeNumberPad; - self.pinTextfield.delegate = self; - self.pinTextfield.secureTextEntry = YES; - self.pinTextfield.textAlignment = NSTextAlignmentCenter; - [self addSubview:self.pinTextfield]; -} - -- (UILabel *)createForgotLink -{ - UILabel *label = [UILabel new]; - label.textColor = [UIColor ows_materialBlueColor]; - NSString *text = NSLocalizedString( - @"REGISTER_2FA_FORGOT_PIN", @"Label for 'I forgot my PIN' link in the 2FA registration view."); - label.attributedText = [[NSAttributedString alloc] - initWithString:text - attributes:@{ - NSForegroundColorAttributeName : [UIColor ows_materialBlueColor], - NSUnderlineStyleAttributeName : @(NSUnderlineStyleSingle | NSUnderlinePatternSolid) - }]; - label.font = [UIFont ows_regularFontWithSize:ScaleFromIPhone5To7Plus(14.f, 16.f)]; - label.numberOfLines = 0; - label.lineBreakMode = NSLineBreakByWordWrapping; - label.textAlignment = NSTextAlignmentCenter; - label.userInteractionEnabled = YES; - [label addGestureRecognizer:[[UITapGestureRecognizer alloc] initWithTarget:self - action:@selector(forgotPinLinkTapped:)]]; - [self addSubview:label]; - return label; -} - -- (void)createSubmitButton -{ - const CGFloat kSubmitButtonHeight = 47.f; - // NOTE: We use ows_signalBrandBlueColor instead of ows_materialBlueColor - // throughout the onboarding flow to be consistent with the headers. - OWSFlatButton *submitButton = - [OWSFlatButton buttonWithTitle:NSLocalizedString(@"REGISTER_2FA_SUBMIT_BUTTON", - @"Label for 'submit' button in the 2FA registration view.") - font:[OWSFlatButton fontForHeight:kSubmitButtonHeight] - titleColor:[UIColor whiteColor] - backgroundColor:[UIColor ows_signalBrandBlueColor] - target:self - selector:@selector(submitButtonWasPressed)]; - self.submitButton = submitButton; - [self addSubview:submitButton]; - [self.submitButton autoSetDimension:ALDimensionHeight toSize:kSubmitButtonHeight]; -} - -- (nullable NSString *)instructionsText -{ - return self.instructionsLabel.text; -} - -- (void)setInstructionsText:(nullable NSString *)instructionsText -{ - self.instructionsLabel.text = instructionsText; -} - -- (nullable NSAttributedString *)attributedInstructionsText -{ - return self.instructionsLabel.attributedText; -} - -- (void)setAttributedInstructionsText:(nullable NSAttributedString *)attributedInstructionsText -{ - self.instructionsLabel.attributedText = attributedInstructionsText; -} - -- (void)createContents -{ - const CGFloat kVSpacing = ScaleFromIPhone5To7Plus(12, 30); - - UILabel *instructionsLabel = [self createLabelWithText:nil]; - self.instructionsLabel = instructionsLabel; - [instructionsLabel autoPinTopToSuperviewMarginWithInset:kVSpacing]; - [instructionsLabel autoPinWidthToSuperview]; - - UILabel *createForgotLink = [self createForgotLink]; - [createForgotLink autoPinEdge:ALEdgeTop toEdge:ALEdgeBottom ofView:instructionsLabel withOffset:5]; - [createForgotLink autoPinWidthToSuperview]; - - [self createPinTextfield]; - [self.pinTextfield autoPinEdge:ALEdgeTop toEdge:ALEdgeBottom ofView:createForgotLink withOffset:kVSpacing]; - [self.pinTextfield autoPinWidthToSuperview]; - - UIView *underscoreView = [UIView new]; - underscoreView.backgroundColor = [UIColor colorWithWhite:0.5 alpha:1.f]; - [self addSubview:underscoreView]; - [underscoreView autoPinEdge:ALEdgeTop toEdge:ALEdgeBottom ofView:self.pinTextfield withOffset:3]; - [underscoreView autoPinWidthToSuperview]; - [underscoreView autoSetDimension:ALDimensionHeight toSize:1.f]; - - [self createSubmitButton]; - [self.submitButton autoPinEdge:ALEdgeTop toEdge:ALEdgeBottom ofView:underscoreView withOffset:kVSpacing]; - [self.submitButton autoPinWidthToSuperview]; - [self updateIsSubmitEnabled]; -} - -#pragma mark - UITextFieldDelegate - -- (BOOL)textField:(UITextField *)textField - shouldChangeCharactersInRange:(NSRange)range - replacementString:(NSString *)insertionText -{ - - [ViewControllerUtils ows2FAPINTextField:textField - shouldChangeCharactersInRange:range - replacementString:insertionText]; - - [self updateIsSubmitEnabled]; - - if (self.delegate && [self.delegate respondsToSelector:@selector(pinEntryView:pinCodeDidChange:)]) { - [self.delegate pinEntryView:self pinCodeDidChange:textField.text]; - } - - return NO; -} - -- (void)updateIsSubmitEnabled; -{ - [self.submitButton setEnabled:self.hasValidPin]; -} - -- (BOOL)makePinTextFieldFirstResponder -{ - return [self.pinTextfield becomeFirstResponder]; -} - -- (BOOL)hasValidPin -{ - return self.pinTextfield.text.length >= kMin2FAPinLength; -} - -- (void)clearText -{ - self.pinTextfield.text = @""; - [self updateIsSubmitEnabled]; -} - -#pragma mark - Events - -- (void)submitButtonWasPressed -{ - [self.delegate pinEntryView:self submittedPinCode:self.pinTextfield.text]; -} - -- (void)forgotPinLinkTapped:(UIGestureRecognizer *)sender -{ - if (sender.state == UIGestureRecognizerStateRecognized) { - [self.delegate pinEntryViewForgotPinLinkTapped:self]; - } -} - - -@end - -NS_ASSUME_NONNULL_END diff --git a/Session/Signal/PrivacySettingsTableViewController.m b/Session/Signal/PrivacySettingsTableViewController.m index 3a904983a..038ff6c60 100644 --- a/Session/Signal/PrivacySettingsTableViewController.m +++ b/Session/Signal/PrivacySettingsTableViewController.m @@ -3,16 +3,16 @@ // #import "PrivacySettingsTableViewController.h" -#import "BlockListViewController.h" + #import "Session-Swift.h" #import #import #import -#import + #import #import #import -#import +#import #import #import @@ -83,22 +83,7 @@ static NSString *const kSealedSenderInfoURL = @"https://signal.org/blog/sealed-s OWSTableContents *contents = [OWSTableContents new]; __weak PrivacySettingsTableViewController *weakSelf = self; - - // Loki: Original code - // ======== -// OWSTableSection *blocklistSection = [OWSTableSection new]; -// blocklistSection.headerTitle -// = NSLocalizedString(@"SETTINGS_BLOCK_LIST_TITLE", @"Label for the block list section of the settings view"); -// [blocklistSection -// addItem:[OWSTableItem disclosureItemWithText:NSLocalizedString(@"SETTINGS_BLOCK_LIST_TITLE", -// @"Label for the block list section of the settings view") -// accessibilityIdentifier:[NSString stringWithFormat:@"settings.privacy.%@", @"blocklist"] -// actionBlock:^{ -// [weakSelf showBlocklist]; -// }]]; -// [contents addSection:blocklistSection]; -// - // ======== + OWSTableSection *readReceiptsSection = [OWSTableSection new]; readReceiptsSection.headerTitle = NSLocalizedString(@"SETTINGS_READ_RECEIPT", @"Label for the 'read receipts' setting."); @@ -189,104 +174,6 @@ static NSString *const kSealedSenderInfoURL = @"https://signal.org/blog/sealed-s selector:@selector(didToggleScreenSecuritySwitch:)]]; [contents addSection:screenSecuritySection]; - // Loki: Original code - // ======== -// // Allow calls to connect directly vs. using TURN exclusively -// OWSTableSection *callingSection = [OWSTableSection new]; -// callingSection.headerTitle -// = NSLocalizedString(@"SETTINGS_SECTION_TITLE_CALLING", @"settings topic header for table section"); -// callingSection.footerTitle = NSLocalizedString(@"SETTINGS_CALLING_HIDES_IP_ADDRESS_PREFERENCE_TITLE_DETAIL", -// @"User settings section footer, a detailed explanation"); -// [callingSection addItem:[OWSTableItem switchItemWithText:NSLocalizedString( -// @"SETTINGS_CALLING_HIDES_IP_ADDRESS_PREFERENCE_TITLE", -// @"Table cell label") -// accessibilityIdentifier:[NSString stringWithFormat:@"settings.privacy.%@", -// @"calling_hide_ip_address"] -// isOnBlock:^{ -// return [Environment.shared.preferences doCallsHideIPAddress]; -// } -// isEnabledBlock:^{ -// return YES; -// } -// target:weakSelf -// selector:@selector(didToggleCallsHideIPAddressSwitch:)]]; -// [contents addSection:callingSection]; -// -// if (CallUIAdapter.isCallkitDisabledForLocale) { -// // Hide all CallKit-related prefs; CallKit is disabled. -// } else if (@available(iOS 11, *)) { -// OWSTableSection *callKitSection = [OWSTableSection new]; -// [callKitSection -// addItem:[OWSTableItem switchItemWithText:NSLocalizedString( -// @"SETTINGS_PRIVACY_CALLKIT_SYSTEM_CALL_LOG_PREFERENCE_TITLE", -// @"Short table cell label") -// accessibilityIdentifier:[NSString stringWithFormat:@"settings.privacy.%@", @"callkit_history"] -// isOnBlock:^{ -// return [Environment.shared.preferences isSystemCallLogEnabled]; -// } -// isEnabledBlock:^{ -// return YES; -// } -// target:weakSelf -// selector:@selector(didToggleEnableSystemCallLogSwitch:)]]; -// callKitSection.footerTitle = NSLocalizedString( -// @"SETTINGS_PRIVACY_CALLKIT_SYSTEM_CALL_LOG_PREFERENCE_DESCRIPTION", @"Settings table section footer."); -// [contents addSection:callKitSection]; -// } else if (@available(iOS 10, *)) { -// OWSTableSection *callKitSection = [OWSTableSection new]; -// callKitSection.footerTitle -// = NSLocalizedString(@"SETTINGS_SECTION_CALL_KIT_DESCRIPTION", @"Settings table section footer."); -// [callKitSection -// addItem:[OWSTableItem switchItemWithText:NSLocalizedString( -// @"SETTINGS_PRIVACY_CALLKIT_TITLE", @"Short table cell label") -// accessibilityIdentifier:[NSString stringWithFormat:@"settings.privacy.%@", @"callkit"] -// isOnBlock:^{ -// return [Environment.shared.preferences isCallKitEnabled]; -// } -// isEnabledBlock:^{ -// return YES; -// } -// target:weakSelf -// selector:@selector(didToggleEnableCallKitSwitch:)]]; -// if (self.preferences.isCallKitEnabled) { -// [callKitSection -// addItem:[OWSTableItem switchItemWithText:NSLocalizedString(@"SETTINGS_PRIVACY_CALLKIT_PRIVACY_TITLE", -// @"Label for 'CallKit privacy' preference") -// accessibilityIdentifier:[NSString -// stringWithFormat:@"settings.privacy.%@", @"callkit_privacy"] -// isOnBlock:^{ -// return (BOOL) ![Environment.shared.preferences isCallKitPrivacyEnabled]; -// } -// isEnabledBlock:^{ -// return YES; -// } -// target:weakSelf -// selector:@selector(didToggleEnableCallKitPrivacySwitch:)]]; -// } -// [contents addSection:callKitSection]; -// } -// -// OWSTableSection *twoFactorAuthSection = [OWSTableSection new]; -// twoFactorAuthSection.headerTitle = NSLocalizedString( -// @"SETTINGS_TWO_FACTOR_AUTH_TITLE", @"Title for the 'two factor auth' section of the privacy settings."); -// [twoFactorAuthSection -// addItem: -// [OWSTableItem -// disclosureItemWithText:NSLocalizedString(@"SETTINGS_TWO_FACTOR_AUTH_ITEM", -// @"Label for the 'two factor auth' item of the privacy settings.") -// detailText: -// ([OWS2FAManager.sharedManager is2FAEnabled] -// ? NSLocalizedString(@"SETTINGS_TWO_FACTOR_AUTH_ENABLED", -// @"Indicates that 'two factor auth' is enabled in the privacy settings.") -// : NSLocalizedString(@"SETTINGS_TWO_FACTOR_AUTH_DISABLED", -// @"Indicates that 'two factor auth' is disabled in the privacy settings.")) -// accessibilityIdentifier:[NSString stringWithFormat:@"settings.privacy.%@", @"2fa"] -// actionBlock:^{ -// [weakSelf show2FASettings]; -// }]]; -// [contents addSection:twoFactorAuthSection]; -// ======== - OWSTableSection *historyLogsSection = [OWSTableSection new]; historyLogsSection.headerTitle = NSLocalizedString(@"SETTINGS_HISTORYLOG_TITLE", @"Section header"); [historyLogsSection @@ -297,99 +184,6 @@ static NSString *const kSealedSenderInfoURL = @"https://signal.org/blog/sealed-s }]]; [contents addSection:historyLogsSection]; -// Loki: Original code -// ======== -// OWSTableSection *unidentifiedDeliveryIndicatorsSection = [OWSTableSection new]; -// unidentifiedDeliveryIndicatorsSection.headerTitle -// = NSLocalizedString(@"SETTINGS_UNIDENTIFIED_DELIVERY_SECTION_TITLE", @"table section label"); -// [unidentifiedDeliveryIndicatorsSection -// addItem:[OWSTableItem -// itemWithCustomCellBlock:^UITableViewCell * { -// UITableViewCell *cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleValue1 -// reuseIdentifier:@"UITableViewCellStyleValue1"]; -// [OWSTableItem configureCell:cell]; -// cell.preservesSuperviewLayoutMargins = YES; -// cell.contentView.preservesSuperviewLayoutMargins = YES; -// cell.selectionStyle = UITableViewCellSelectionStyleNone; -// -// UILabel *label = [UILabel new]; -// label.text -// = NSLocalizedString(@"SETTINGS_UNIDENTIFIED_DELIVERY_SHOW_INDICATORS", @"switch label"); -// label.font = [UIFont ows_regularFontWithSize:18.f]; -// label.textColor = [Theme primaryColor]; -// [label setContentHuggingHorizontalHigh]; -// -// UIImage *icon = [UIImage imageNamed:@"ic_secret_sender_indicator"]; -// UIImageView *iconView = [[UIImageView alloc] -// initWithImage:[icon imageWithRenderingMode:UIImageRenderingModeAlwaysTemplate]]; -// iconView.tintColor = Theme.secondaryColor; -// [iconView setContentHuggingHorizontalHigh]; -// -// UIView *spacer = [UIView new]; -// [spacer setContentHuggingHorizontalLow]; -// -// UISwitch *cellSwitch = [UISwitch new]; -// cell.accessoryView = cellSwitch; -// [cellSwitch setOn:Environment.shared.preferences.shouldShowUnidentifiedDeliveryIndicators]; -// [cellSwitch addTarget:weakSelf -// action:@selector(didToggleUDShowIndicatorsSwitch:) -// forControlEvents:UIControlEventValueChanged]; -// [cellSwitch setContentHuggingHorizontalHigh]; -// cellSwitch.accessibilityIdentifier = -// [NSString stringWithFormat:@"settings.privacy.%@", @"sealed_sender"]; -// -// UIStackView *stackView = -// [[UIStackView alloc] initWithArrangedSubviews:@[ label, iconView, spacer, cellSwitch ]]; -// stackView.axis = UILayoutConstraintAxisHorizontal; -// stackView.spacing = 10; -// stackView.alignment = UIStackViewAlignmentCenter; -// -// [cell.contentView addSubview:stackView]; -// [stackView ows_autoPinToSuperviewMargins]; -// return cell; -// } -// customRowHeight:UITableViewAutomaticDimension -// actionBlock:^{ -// NSURL *url = [NSURL URLWithString:kSealedSenderInfoURL]; -// OWSCAssertDebug(url); -// [UIApplication.sharedApplication openURL:url]; -// }]]; -// -// unidentifiedDeliveryIndicatorsSection.footerTitle -// = NSLocalizedString(@"SETTINGS_UNIDENTIFIED_DELIVERY_SHOW_INDICATORS_FOOTER", @"table section footer"); -// [contents addSection:unidentifiedDeliveryIndicatorsSection]; -// -// OWSTableSection *unidentifiedDeliveryUnrestrictedSection = [OWSTableSection new]; -// OWSTableItem *unrestrictedAccessItem = [OWSTableItem -// switchItemWithText:NSLocalizedString(@"SETTINGS_UNIDENTIFIED_DELIVERY_UNRESTRICTED_ACCESS", @"switch label") -// accessibilityIdentifier:[NSString stringWithFormat:@"settings.privacy.%@", @"sealed_sender_unrestricted"] -// isOnBlock:^{ -// return [SSKEnvironment.shared.udManager shouldAllowUnrestrictedAccessLocal]; -// } -// isEnabledBlock:^{ -// return YES; -// } -// target:weakSelf -// selector:@selector(didToggleUDUnrestrictedAccessSwitch:)]; -// [unidentifiedDeliveryUnrestrictedSection addItem:unrestrictedAccessItem]; -// unidentifiedDeliveryUnrestrictedSection.footerTitle -// = NSLocalizedString(@"SETTINGS_UNIDENTIFIED_DELIVERY_UNRESTRICTED_ACCESS_FOOTER", @"table section footer"); -// [contents addSection:unidentifiedDeliveryUnrestrictedSection]; -// -// OWSTableSection *unidentifiedDeliveryLearnMoreSection = [OWSTableSection new]; -// [unidentifiedDeliveryLearnMoreSection -// addItem:[OWSTableItem disclosureItemWithText:NSLocalizedString(@"SETTINGS_UNIDENTIFIED_DELIVERY_LEARN_MORE", -// @"Label for a link to more info about unidentified delivery.") -// accessibilityIdentifier:[NSString stringWithFormat:@"settings.privacy.%@", -// @"sealed_sender_learn_more"] -// actionBlock:^{ -// NSURL *url = [NSURL URLWithString:kSealedSenderInfoURL]; -// OWSCAssertDebug(url); -// [UIApplication.sharedApplication openURL:url]; -// }]]; -// [contents addSection:unidentifiedDeliveryLearnMoreSection]; -// ======== - OWSTableSection *linkPreviewsSection = [OWSTableSection new]; [linkPreviewsSection addItem:[OWSTableItem switchItemWithText:NSLocalizedString(@"SETTINGS_LINK_PREVIEWS", @@ -414,12 +208,6 @@ static NSString *const kSealedSenderInfoURL = @"https://signal.org/blog/sealed-s #pragma mark - Events -- (void)showBlocklist -{ - BlockListViewController *vc = [BlockListViewController new]; - [self.navigationController pushViewController:vc animated:YES]; -} - - (void)clearHistoryLogs { UIAlertController *alert = diff --git a/Session/Signal/PushRegistrationManager.swift b/Session/Signal/PushRegistrationManager.swift index ceee373c3..fc8b2f600 100644 --- a/Session/Signal/PushRegistrationManager.swift +++ b/Session/Signal/PushRegistrationManager.swift @@ -17,14 +17,10 @@ public enum PushRegistrationError: Error { /** * Singleton used to integrate with push notification services - registration and routing received remote notifications. */ -@objc public class PushRegistrationManager: NSObject, PKPushRegistryDelegate { +@objc public class PushRegistrationManager: NSObject { // MARK: - Dependencies - private var messageFetcherJob: MessageFetcherJob { - return AppEnvironment.shared.messageFetcherJob - } - private var notificationPresenter: NotificationPresenter { return AppEnvironment.shared.notificationPresenter } @@ -91,34 +87,6 @@ public enum PushRegistrationError: Error { vanillaTokenResolver.reject(error) } - // MARK: PKPushRegistryDelegate - voIP Push Token - - public func pushRegistry(_ registry: PKPushRegistry, didReceiveIncomingPushWith payload: PKPushPayload, for type: PKPushType) { - Logger.info("") - assert(type == .voIP) - AppReadiness.runNowOrWhenAppDidBecomeReady { - (self.messageFetcherJob.run() as Promise).retainUntilComplete() - } - } - - public func pushRegistry(_ registry: PKPushRegistry, didUpdate credentials: PKPushCredentials, for type: PKPushType) { - Logger.info("") - assert(type == .voIP) - assert(credentials.type == .voIP) - guard let voipTokenResolver = self.voipTokenResolver else { - owsFailDebug("fulfillVoipTokenPromise was unexpectedly nil") - return - } - - voipTokenResolver.fulfill(credentials.token) - } - - public func pushRegistry(_ registry: PKPushRegistry, didInvalidatePushTokenFor type: PKPushType) { - // It's not clear when this would happen. We've never previously handled it, but we should at - // least start learning if it happens. - owsFailDebug("Invalid state") - } - // MARK: helpers // User notification settings must be registered *before* AppDelegate will @@ -200,55 +168,6 @@ public enum PushRegistrationError: Error { self.vanillaTokenPromise = nil } } - - private func registerForVoipPushToken() -> Promise { - AssertIsOnMainThread() - Logger.info("") - - guard self.voipTokenPromise == nil else { - let promise = self.voipTokenPromise! - assert(promise.isPending) - return promise.map { $0.hexEncodedString } - } - - // No pending voip token yet. Create a new promise - let (promise, resolver) = Promise.pending() - self.voipTokenPromise = promise - self.voipTokenResolver = resolver - - if self.voipRegistry == nil { - // We don't create the voip registry in init, because it immediately requests the voip token, - // potentially before we're ready to handle it. - let voipRegistry = PKPushRegistry(queue: nil) - self.voipRegistry = voipRegistry - voipRegistry.desiredPushTypes = [.voIP] - voipRegistry.delegate = self - } - - guard let voipRegistry = self.voipRegistry else { - owsFailDebug("failed to initialize voipRegistry") - resolver.reject(PushRegistrationError.assertionError(description: "failed to initialize voipRegistry")) - return promise.map { _ in - // coerce expected type of returned promise - we don't really care about the value, - // since this promise has been rejected. In practice this shouldn't happen - String() - } - } - - // If we've already completed registering for a voip token, resolve it immediately, - // rather than waiting for the delegate method to be called. - if let voipTokenData = voipRegistry.pushToken(for: .voIP) { - Logger.info("using pre-registered voIP token") - resolver.fulfill(voipTokenData) - } - - return promise.map { (voipTokenData: Data) -> String in - Logger.info("successfully registered for voip push notifications") - return voipTokenData.hexEncodedString - }.ensure { - self.voipTokenPromise = nil - } - } } // We transmit pushToken data as hex encoded string to the server diff --git a/Session/Signal/SafetyNumberConfirmationAlert.swift b/Session/Signal/SafetyNumberConfirmationAlert.swift deleted file mode 100644 index 4167a99a3..000000000 --- a/Session/Signal/SafetyNumberConfirmationAlert.swift +++ /dev/null @@ -1,117 +0,0 @@ -// -// Copyright (c) 2019 Open Whisper Systems. All rights reserved. -// - -import Foundation -import SignalUtilitiesKit - -@objc -public class SafetyNumberConfirmationAlert: NSObject { - - private let contactsManager: OWSContactsManager - private let primaryStorage: OWSPrimaryStorage - - init(contactsManager: OWSContactsManager) { - self.contactsManager = contactsManager - self.primaryStorage = OWSPrimaryStorage.shared() - } - - @objc - public class func presentAlertIfNecessary(recipientId: String, confirmationText: String, contactsManager: OWSContactsManager, completion: @escaping (Bool) -> Void) -> Bool { - return self.presentAlertIfNecessary(recipientIds: [recipientId], confirmationText: confirmationText, contactsManager: contactsManager, completion: completion, beforePresentationHandler: nil) - } - - @objc - public class func presentAlertIfNecessary(recipientId: String, confirmationText: String, contactsManager: OWSContactsManager, completion: @escaping (Bool) -> Void, beforePresentationHandler: (() -> Void)? = nil) -> Bool { - return self.presentAlertIfNecessary(recipientIds: [recipientId], confirmationText: confirmationText, contactsManager: contactsManager, completion: completion, beforePresentationHandler: beforePresentationHandler) - } - - @objc - public class func presentAlertIfNecessary(recipientIds: [String], confirmationText: String, contactsManager: OWSContactsManager, completion: @escaping (Bool) -> Void) -> Bool { - return self.presentAlertIfNecessary(recipientIds: recipientIds, confirmationText: confirmationText, contactsManager: contactsManager, completion: completion, beforePresentationHandler: nil) - } - - @objc - public class func presentAlertIfNecessary(recipientIds: [String], confirmationText: String, contactsManager: OWSContactsManager, completion: @escaping (Bool) -> Void, beforePresentationHandler: (() -> Void)? = nil) -> Bool { - return SafetyNumberConfirmationAlert(contactsManager: contactsManager).presentIfNecessary(recipientIds: recipientIds, - confirmationText: confirmationText, - completion: completion, - beforePresentationHandler: beforePresentationHandler) - } - - /** - * Shows confirmation dialog if at least one of the recipient id's is not confirmed. - * - * @returns true if an alert was shown - * false if there were no unconfirmed identities - */ - public func presentIfNecessary(recipientIds: [String], confirmationText: String, completion: @escaping (Bool) -> Void, beforePresentationHandler: (() -> Void)? = nil) -> Bool { - - guard let untrustedIdentity = untrustedIdentityForSending(recipientIds: recipientIds) else { - // No identities to confirm, no alert to present. - return false - } - - let displayName = contactsManager.displayName(forPhoneIdentifier: untrustedIdentity.recipientId) - - let titleFormat = NSLocalizedString("CONFIRM_SENDING_TO_CHANGED_IDENTITY_TITLE_FORMAT", - comment: "Action sheet title presented when a user's SN has recently changed. Embeds {{contact's name or phone number}}") - let title = String(format: titleFormat, displayName) - - let bodyFormat = NSLocalizedString("CONFIRM_SENDING_TO_CHANGED_IDENTITY_BODY_FORMAT", - comment: "Action sheet body presented when a user's SN has recently changed. Embeds {{contact's name or phone number}}") - let body = String(format: bodyFormat, displayName) - - let actionSheet = UIAlertController(title: title, message: body, preferredStyle: .actionSheet) - - let confirmAction = UIAlertAction(title: confirmationText, style: .default) { _ in - Logger.info("Confirmed identity: \(untrustedIdentity)") - - self.primaryStorage.newDatabaseConnection().asyncReadWrite { (transaction) in - OWSIdentityManager.shared().setVerificationState(.default, identityKey: untrustedIdentity.identityKey, recipientId: untrustedIdentity.recipientId, isUserInitiatedChange: true, transaction: transaction) - DispatchQueue.main.async { - completion(true) - } - } - } - actionSheet.addAction(confirmAction) - - let showSafetyNumberAction = UIAlertAction(title: NSLocalizedString("VERIFY_PRIVACY", comment: "Label for button or row which allows users to verify the safety number of another user."), style: .default) { _ in - Logger.info("Opted to show Safety Number for identity: \(untrustedIdentity)") - - self.presentSafetyNumberViewController(theirIdentityKey: untrustedIdentity.identityKey, - theirRecipientId: untrustedIdentity.recipientId, - theirDisplayName: displayName, - completion: { completion(false) }) - - } - actionSheet.addAction(showSafetyNumberAction) - - // We can't use the default `OWSAlerts.cancelAction` because we need to specify that the completion - // handler is called. - let cancelAction = UIAlertAction(title: CommonStrings.cancelButton, style: .cancel) { _ in - Logger.info("user canceled.") - completion(false) - } - actionSheet.addAction(cancelAction) - - beforePresentationHandler?() - - UIApplication.shared.frontmostViewController?.presentAlert(actionSheet) - return true - } - - public func presentSafetyNumberViewController(theirIdentityKey: Data, theirRecipientId: String, theirDisplayName: String, completion: (() -> Void)? = nil) { - guard let fromViewController = UIApplication.shared.frontmostViewController else { - Logger.info("Missing frontmostViewController") - return - } - FingerprintViewController.present(from: fromViewController, recipientId: theirRecipientId) - } - - private func untrustedIdentityForSending(recipientIds: [String]) -> OWSRecipientIdentity? { - return recipientIds.compactMap { - OWSIdentityManager.shared().untrustedIdentityForSending(toRecipientId: $0) - }.first - } -} diff --git a/Session/Signal/SessionResetJob.swift b/Session/Signal/SessionResetJob.swift index 4f802addf..19df92994 100644 --- a/Session/Signal/SessionResetJob.swift +++ b/Session/Signal/SessionResetJob.swift @@ -97,10 +97,6 @@ public class SessionResetOperation: OWSOperation, DurableOperation { return SSKEnvironment.shared.primaryStorage } - var messageSender: MessageSender { - return SSKEnvironment.shared.messageSender - } - // MARK: var firstAttempt = true @@ -108,19 +104,7 @@ public class SessionResetOperation: OWSOperation, DurableOperation { override public func run() { assert(self.durableOperationDelegate != nil) - /* Loki: Original code - * We don't want to delete the session. Ref: SignalServiceKit/Loki/Docs/SessionReset.md - * ================ - if firstAttempt { - Storage.writeSync { transaction in - Logger.info("deleting sessions for recipient: \(self.recipientId)") - self.primaryStorage.deleteAllSessions(forContact: self.recipientId, protocolContext: transaction) - } - firstAttempt = false - } - * ================ - */ - + /* let endSessionMessage = EndSessionMessage(timestamp: NSDate.ows_millisecondTimeStamp(), in: self.contactThread) firstly { @@ -157,6 +141,7 @@ public class SessionResetOperation: OWSOperation, DurableOperation { Logger.error("sending error: \(error.localizedDescription)") self.reportError(error) }.retainUntilComplete() + */ } override public func didSucceed() { diff --git a/Session/Signal/ShowGroupMembersViewController.h b/Session/Signal/ShowGroupMembersViewController.h deleted file mode 100644 index de4c64267..000000000 --- a/Session/Signal/ShowGroupMembersViewController.h +++ /dev/null @@ -1,13 +0,0 @@ -// -// Copyright (c) 2018 Open Whisper Systems. All rights reserved. -// - -#import "OWSTableViewController.h" - -@class TSGroupThread; - -@interface ShowGroupMembersViewController : OWSTableViewController - -- (void)configWithThread:(TSGroupThread *)thread; - -@end diff --git a/Session/Signal/ShowGroupMembersViewController.m b/Session/Signal/ShowGroupMembersViewController.m deleted file mode 100644 index b42f5b096..000000000 --- a/Session/Signal/ShowGroupMembersViewController.m +++ /dev/null @@ -1,478 +0,0 @@ -// -// Copyright (c) 2019 Open Whisper Systems. All rights reserved. -// - -#import "ShowGroupMembersViewController.h" -#import "Session-Swift.h" -#import "SignalApp.h" -#import "ViewControllerUtils.h" -#import -#import -#import -#import -#import -#import -#import -#import -#import -#import - -@import ContactsUI; - -NS_ASSUME_NONNULL_BEGIN - -@interface ShowGroupMembersViewController () - -@property (nonatomic, readonly) TSGroupThread *thread; -@property (nonatomic, readonly) ContactsViewHelper *contactsViewHelper; - -@property (nonatomic, nullable) NSSet *memberRecipientIds; - -@end - -#pragma mark - - -@implementation ShowGroupMembersViewController - -- (instancetype)init -{ - self = [super init]; - if (!self) { - return self; - } - - [self commonInit]; - - return self; -} - -- (nullable instancetype)initWithCoder:(NSCoder *)aDecoder -{ - self = [super initWithCoder:aDecoder]; - if (!self) { - return self; - } - - [self commonInit]; - - return self; -} - - -- (void)commonInit -{ - _contactsViewHelper = [[ContactsViewHelper alloc] initWithDelegate:self]; - - self.tableView.rowHeight = UITableViewAutomaticDimension; - self.tableView.estimatedRowHeight = 60; - - [self observeNotifications]; -} - -- (void)dealloc -{ - [[NSNotificationCenter defaultCenter] removeObserver:self]; -} - -- (void)observeNotifications -{ - [[NSNotificationCenter defaultCenter] addObserver:self - selector:@selector(identityStateDidChange:) - name:kNSNotificationName_IdentityStateDidChange - object:nil]; -} - -- (void)configWithThread:(TSGroupThread *)thread -{ - - _thread = thread; - - OWSAssertDebug(self.thread); - OWSAssertDebug(self.thread.groupModel); - OWSAssertDebug(self.thread.groupModel.groupMemberIds); - - self.memberRecipientIds = [NSSet setWithArray:self.thread.groupModel.groupMemberIds]; -} - -- (void)viewDidLoad -{ - [super viewDidLoad]; - - OWSAssertDebug([self.navigationController isKindOfClass:[OWSNavigationController class]]); - - self.title = _thread.groupModel.groupName; - - self.tableView.rowHeight = UITableViewAutomaticDimension; - self.tableView.estimatedRowHeight = 45; - - [self updateTableContents]; -} - -#pragma mark - Table Contents - -- (void)updateTableContents -{ - OWSAssertDebug(self.thread); - - OWSTableContents *contents = [OWSTableContents new]; - - __weak ShowGroupMembersViewController *weakSelf = self; - ContactsViewHelper *helper = self.contactsViewHelper; - - OWSTableSection *membersSection = [OWSTableSection new]; - - // Group Members - - // If there are "no longer verified" members of the group, - // highlight them in a special section. - NSArray *noLongerVerifiedRecipientIds = [self noLongerVerifiedRecipientIds]; - if (noLongerVerifiedRecipientIds.count > 0) { - OWSTableSection *noLongerVerifiedSection = [OWSTableSection new]; - noLongerVerifiedSection.headerTitle = NSLocalizedString(@"GROUP_MEMBERS_SECTION_TITLE_NO_LONGER_VERIFIED", - @"Title for the 'no longer verified' section of the 'group members' view."); - membersSection.headerTitle = NSLocalizedString( - @"GROUP_MEMBERS_SECTION_TITLE_MEMBERS", @"Title for the 'members' section of the 'group members' view."); - [noLongerVerifiedSection - addItem:[OWSTableItem disclosureItemWithText:NSLocalizedString(@"GROUP_MEMBERS_RESET_NO_LONGER_VERIFIED", - @"Label for the button that clears all verification " - @"errors in the 'group members' view.") - accessibilityIdentifier:ACCESSIBILITY_IDENTIFIER_WITH_NAME(self, @"no_longer_verified") - customRowHeight:UITableViewAutomaticDimension - actionBlock:^{ - [weakSelf offerResetAllNoLongerVerified]; - }]]; - [self addMembers:noLongerVerifiedRecipientIds toSection:noLongerVerifiedSection useVerifyAction:YES]; - [contents addSection:noLongerVerifiedSection]; - } - - NSMutableSet *memberRecipientIds = [self.memberRecipientIds mutableCopy]; - [memberRecipientIds removeObject:[helper localNumber]]; - [self addMembers:memberRecipientIds.allObjects toSection:membersSection useVerifyAction:NO]; - [contents addSection:membersSection]; - - self.contents = contents; -} - -- (void)addMembers:(NSArray *)recipientIds - toSection:(OWSTableSection *)section - useVerifyAction:(BOOL)useVerifyAction -{ - OWSAssertDebug(recipientIds); - OWSAssertDebug(section); - - __weak ShowGroupMembersViewController *weakSelf = self; - ContactsViewHelper *helper = self.contactsViewHelper; - // Sort the group members using contacts manager. - NSArray *sortedRecipientIds = [recipientIds sortedArrayUsingComparator:^NSComparisonResult( - NSString *recipientIdA, NSString *recipientIdB) { - SignalAccount *signalAccountA = [helper.contactsManager fetchOrBuildSignalAccountForRecipientId:recipientIdA]; - SignalAccount *signalAccountB = [helper.contactsManager fetchOrBuildSignalAccountForRecipientId:recipientIdB]; - return [helper.contactsManager compareSignalAccount:signalAccountA withSignalAccount:signalAccountB]; - }]; - for (NSString *recipientId in sortedRecipientIds) { - [section addItem:[OWSTableItem - itemWithCustomCellBlock:^{ - ShowGroupMembersViewController *strongSelf = weakSelf; - OWSCAssertDebug(strongSelf); - - ContactTableViewCell *cell = [ContactTableViewCell new]; - OWSVerificationState verificationState = - [[OWSIdentityManager sharedManager] verificationStateForRecipientId:recipientId]; - BOOL isVerified = verificationState == OWSVerificationStateVerified; - BOOL isNoLongerVerified = verificationState == OWSVerificationStateNoLongerVerified; - BOOL isBlocked = [helper isRecipientIdBlocked:recipientId]; - if (isNoLongerVerified) { - cell.accessoryMessage = NSLocalizedString(@"CONTACT_CELL_IS_NO_LONGER_VERIFIED", - @"An indicator that a contact is no longer verified."); - } else if (isBlocked) { - cell.accessoryMessage = NSLocalizedString( - @"CONTACT_CELL_IS_BLOCKED", @"An indicator that a contact has been blocked."); - } - - [cell configureWithRecipientId:recipientId]; - - if (isVerified) { - [cell setAttributedSubtitle:cell.verifiedSubtitle]; - } else { - [cell setAttributedSubtitle:nil]; - } - - NSString *cellName = [NSString stringWithFormat:@"user.%@", recipientId]; - cell.accessibilityIdentifier - = ACCESSIBILITY_IDENTIFIER_WITH_NAME(ShowGroupMembersViewController, cellName); - - return cell; - } - customRowHeight:UITableViewAutomaticDimension - actionBlock:^{ - if (useVerifyAction) { - [weakSelf showSafetyNumberView:recipientId]; - } else { - [weakSelf didSelectRecipientId:recipientId]; - } - }]]; - } -} - -- (void)offerResetAllNoLongerVerified -{ - OWSAssertIsOnMainThread(); - - UIAlertController *actionSheet = [UIAlertController - alertControllerWithTitle:nil - message:NSLocalizedString(@"GROUP_MEMBERS_RESET_NO_LONGER_VERIFIED_ALERT_MESSAGE", - @"Label for the 'reset all no-longer-verified group members' confirmation alert.") - preferredStyle:UIAlertControllerStyleAlert]; - - __weak ShowGroupMembersViewController *weakSelf = self; - UIAlertAction *verifyAction = [UIAlertAction actionWithTitle:NSLocalizedString(@"OK", nil) - accessibilityIdentifier:ACCESSIBILITY_IDENTIFIER_WITH_NAME(self, @"ok") - style:UIAlertActionStyleDestructive - handler:^(UIAlertAction *_Nonnull action) { - [weakSelf resetAllNoLongerVerified]; - }]; - [actionSheet addAction:verifyAction]; - [actionSheet addAction:[OWSAlerts cancelAction]]; - - [self presentAlert:actionSheet]; -} - -- (void)resetAllNoLongerVerified -{ - OWSAssertIsOnMainThread(); - - OWSIdentityManager *identityManger = [OWSIdentityManager sharedManager]; - NSArray *recipientIds = [self noLongerVerifiedRecipientIds]; - for (NSString *recipientId in recipientIds) { - OWSVerificationState verificationState = [identityManger verificationStateForRecipientId:recipientId]; - if (verificationState == OWSVerificationStateNoLongerVerified) { - NSData *identityKey = [identityManger identityKeyForRecipientId:recipientId]; - if (identityKey.length < 1) { - OWSFailDebug(@"Missing identity key for: %@", recipientId); - continue; - } - [identityManger setVerificationState:OWSVerificationStateDefault - identityKey:identityKey - recipientId:recipientId - isUserInitiatedChange:YES]; - } - } - - [self updateTableContents]; -} - -// Returns a collection of the group members who are "no longer verified". -- (NSArray *)noLongerVerifiedRecipientIds -{ - NSMutableArray *result = [NSMutableArray new]; - for (NSString *recipientId in self.thread.recipientIdentifiers) { - if ([[OWSIdentityManager sharedManager] verificationStateForRecipientId:recipientId] - == OWSVerificationStateNoLongerVerified) { - [result addObject:recipientId]; - } - } - return [result copy]; -} - -- (void)didSelectRecipientId:(NSString *)recipientId -{ - OWSAssertDebug(recipientId.length > 0); - - ContactsViewHelper *helper = self.contactsViewHelper; - SignalAccount *_Nullable signalAccount = [helper fetchSignalAccountForRecipientId:recipientId]; - - UIAlertController *actionSheet = - [UIAlertController alertControllerWithTitle:nil message:nil preferredStyle:UIAlertControllerStyleActionSheet]; - - if (self.contactsViewHelper.contactsManager.supportsContactEditing) { - NSString *contactInfoTitle = signalAccount - ? NSLocalizedString(@"GROUP_MEMBERS_VIEW_CONTACT_INFO", @"Button label for the 'show contact info' button") - : NSLocalizedString( - @"GROUP_MEMBERS_ADD_CONTACT_INFO", @"Button label to add information to an unknown contact"); - [actionSheet - addAction:[UIAlertAction actionWithTitle:contactInfoTitle - accessibilityIdentifier:ACCESSIBILITY_IDENTIFIER_WITH_NAME(self, @"show_contact_info") - style:UIAlertActionStyleDefault - handler:^(UIAlertAction *_Nonnull action) { - [self showContactInfoViewForRecipientId:recipientId]; - }]]; - } - - BOOL isBlocked; - if (signalAccount) { - isBlocked = [helper isRecipientIdBlocked:signalAccount.recipientId]; - if (isBlocked) { - [actionSheet - addAction:[UIAlertAction actionWithTitle:NSLocalizedString(@"BLOCK_LIST_UNBLOCK_BUTTON", - @"Button label for the 'unblock' button") - accessibilityIdentifier:ACCESSIBILITY_IDENTIFIER_WITH_NAME(self, @"unblock") - style:UIAlertActionStyleDefault - handler:^(UIAlertAction *_Nonnull action) { - [BlockListUIUtils - showUnblockSignalAccountActionSheet:signalAccount - fromViewController:self - blockingManager:helper.blockingManager - contactsManager:helper.contactsManager - completionBlock:^(BOOL ignore) { - [self updateTableContents]; - }]; - }]]; - } else { - [actionSheet - addAction:[UIAlertAction actionWithTitle:NSLocalizedString(@"BLOCK_LIST_BLOCK_BUTTON", - @"Button label for the 'block' button") - accessibilityIdentifier:ACCESSIBILITY_IDENTIFIER_WITH_NAME(self, @"block") - style:UIAlertActionStyleDestructive - handler:^(UIAlertAction *_Nonnull action) { - [BlockListUIUtils - showBlockSignalAccountActionSheet:signalAccount - fromViewController:self - blockingManager:helper.blockingManager - contactsManager:helper.contactsManager - completionBlock:^(BOOL ignore) { - [self updateTableContents]; - }]; - }]]; - } - } else { - isBlocked = [helper isRecipientIdBlocked:recipientId]; - if (isBlocked) { - [actionSheet - addAction:[UIAlertAction actionWithTitle:NSLocalizedString(@"BLOCK_LIST_UNBLOCK_BUTTON", - @"Button label for the 'unblock' button") - accessibilityIdentifier:ACCESSIBILITY_IDENTIFIER_WITH_NAME(self, @"unblock") - style:UIAlertActionStyleDefault - handler:^(UIAlertAction *_Nonnull action) { - [BlockListUIUtils - showUnblockPhoneNumberActionSheet:recipientId - fromViewController:self - blockingManager:helper.blockingManager - contactsManager:helper.contactsManager - completionBlock:^(BOOL ignore) { - [self updateTableContents]; - }]; - }]]; - } else { - [actionSheet - addAction:[UIAlertAction actionWithTitle:NSLocalizedString(@"BLOCK_LIST_BLOCK_BUTTON", - @"Button label for the 'block' button") - accessibilityIdentifier:ACCESSIBILITY_IDENTIFIER_WITH_NAME(self, @"block") - style:UIAlertActionStyleDestructive - handler:^(UIAlertAction *_Nonnull action) { - [BlockListUIUtils - showBlockPhoneNumberActionSheet:recipientId - fromViewController:self - blockingManager:helper.blockingManager - contactsManager:helper.contactsManager - completionBlock:^(BOOL ignore) { - [self updateTableContents]; - }]; - }]]; - } - } - - if (!isBlocked) { - [actionSheet - addAction:[UIAlertAction actionWithTitle:NSLocalizedString(@"GROUP_MEMBERS_SEND_MESSAGE", - @"Button label for the 'send message to group member' button") - accessibilityIdentifier:ACCESSIBILITY_IDENTIFIER_WITH_NAME(self, @"send_message") - style:UIAlertActionStyleDefault - handler:^(UIAlertAction *_Nonnull action) { - [self showConversationViewForRecipientId:recipientId]; - }]]; - [actionSheet addAction:[UIAlertAction actionWithTitle:NSLocalizedString(@"GROUP_MEMBERS_CALL", - @"Button label for the 'call group member' button") - accessibilityIdentifier:ACCESSIBILITY_IDENTIFIER_WITH_NAME(self, @"call") - style:UIAlertActionStyleDefault - handler:^(UIAlertAction *_Nonnull action) { - [self callMember:recipientId]; - }]]; - [actionSheet - addAction:[UIAlertAction actionWithTitle:NSLocalizedString(@"VERIFY_PRIVACY", - @"Label for button or row which allows users to verify the " - @"safety number of another user.") - accessibilityIdentifier:ACCESSIBILITY_IDENTIFIER_WITH_NAME(self, @"safety_numbers") - style:UIAlertActionStyleDefault - handler:^(UIAlertAction *_Nonnull action) { - [self showSafetyNumberView:recipientId]; - }]]; - } - - [actionSheet addAction:[OWSAlerts cancelAction]]; - - [self presentAlert:actionSheet]; -} - -- (void)showContactInfoViewForRecipientId:(NSString *)recipientId -{ - OWSAssertDebug(recipientId.length > 0); - - [self.contactsViewHelper presentContactViewControllerForRecipientId:recipientId - fromViewController:self - editImmediately:NO]; -} - -- (void)showConversationViewForRecipientId:(NSString *)recipientId -{ - OWSAssertDebug(recipientId.length > 0); - - [SignalApp.sharedApp presentConversationForRecipientId:recipientId - action:ConversationViewActionCompose - animated:YES]; -} - -- (void)callMember:(NSString *)recipientId -{ - [SignalApp.sharedApp presentConversationForRecipientId:recipientId - action:ConversationViewActionAudioCall - animated:YES]; -} - -- (void)showSafetyNumberView:(NSString *)recipientId -{ - OWSAssertDebug(recipientId.length > 0); - - [FingerprintViewController presentFromViewController:self recipientId:recipientId]; -} - -#pragma mark - ContactsViewHelperDelegate - -- (void)contactsViewHelperDidUpdateContacts -{ - [self updateTableContents]; -} - -- (BOOL)shouldHideLocalNumber -{ - return YES; -} - -#pragma mark - ContactEditingDelegate - -- (void)didFinishEditingContact -{ - OWSLogDebug(@""); - [self dismissViewControllerAnimated:YES completion:nil]; -} - -#pragma mark - CNContactViewControllerDelegate - -- (void)contactViewController:(CNContactViewController *)viewController - didCompleteWithContact:(nullable CNContact *)contact -{ - OWSLogDebug(@"done editing contact."); - [self dismissViewControllerAnimated:YES completion:nil]; -} - -#pragma mark - Notifications - -- (void)identityStateDidChange:(NSNotification *)notification -{ - OWSAssertIsOnMainThread(); - - [self updateTableContents]; -} - -@end - -NS_ASSUME_NONNULL_END diff --git a/Session/Signal/SignalApp.m b/Session/Signal/SignalApp.m index cfa9e745c..bf5e6ef07 100644 --- a/Session/Signal/SignalApp.m +++ b/Session/Signal/SignalApp.m @@ -6,7 +6,6 @@ #import "AppDelegate.h" #import "ConversationViewController.h" #import "Session-Swift.h" -#import "SignalsNavigationController.h" #import #import #import @@ -178,8 +177,7 @@ NS_ASSUME_NONNULL_BEGIN - (void)showHomeView { HomeVC *homeView = [HomeVC new]; - SignalsNavigationController *navigationController = - [[SignalsNavigationController alloc] initWithRootViewController:homeView]; + UINavigationController *navigationController = [[UINavigationController alloc] initWithRootViewController:homeView]; AppDelegate *appDelegate = (AppDelegate *)[UIApplication sharedApplication].delegate; appDelegate.window.rootViewController = navigationController; OWSAssertDebug([navigationController.topViewController isKindOfClass:[HomeVC class]]); diff --git a/Session/Signal/SignalsNavigationController.h b/Session/Signal/SignalsNavigationController.h deleted file mode 100644 index 7d8b6a041..000000000 --- a/Session/Signal/SignalsNavigationController.h +++ /dev/null @@ -1,9 +0,0 @@ -// -// Copyright (c) 2017 Open Whisper Systems. All rights reserved. -// - -#import "OWSNavigationController.h" - -@interface SignalsNavigationController : OWSNavigationController - -@end diff --git a/Session/Signal/SignalsNavigationController.m b/Session/Signal/SignalsNavigationController.m deleted file mode 100644 index 66ac3f02e..000000000 --- a/Session/Signal/SignalsNavigationController.m +++ /dev/null @@ -1,123 +0,0 @@ -// -// Copyright (c) 2018 Open Whisper Systems. All rights reserved. -// - -#import "SignalsNavigationController.h" -#import "Session-Swift.h" -#import -#import -#import -#import - -static double const STALLED_PROGRESS = 0.9; - -@interface SignalsNavigationController () - -@property (nonatomic) UIProgressView *socketStatusView; -@property (nonatomic) NSTimer *updateStatusTimer; - -@end - -#pragma mark - - -@implementation SignalsNavigationController - -- (void)viewDidLoad { - [super viewDidLoad]; - - // Do any additional setup after loading the view. - [self initializeObserver]; - [self updateSocketStatusView]; -} - -- (void)initializeSocketStatusBar { - if (!_socketStatusView) { - _socketStatusView = [[UIProgressView alloc] initWithProgressViewStyle:UIProgressViewStyleDefault]; - } - - CGRect bar = self.navigationBar.frame; - _socketStatusView.frame = CGRectMake(0, bar.size.height - 1.0f, self.view.frame.size.width, 1.0f); - _socketStatusView.progress = 0.0f; - _socketStatusView.progressTintColor = [UIColor ows_fadedBlueColor]; - - /** Loki: Original code - if (![_socketStatusView superview]) { - [self.navigationBar addSubview:_socketStatusView]; - } - */ -} - -- (void)dealloc -{ - [[NSNotificationCenter defaultCenter] removeObserver:self]; -} - -#pragma mark - Socket Status Notifications - -- (void)initializeObserver { - [[NSNotificationCenter defaultCenter] addObserver:self - selector:@selector(OWSWebSocketStateDidChange) - name:kNSNotification_OWSWebSocketStateDidChange - object:nil]; - [[NSNotificationCenter defaultCenter] addObserver:self - selector:@selector(isCensorshipCircumventionActiveDidChange:) - name:kNSNotificationName_IsCensorshipCircumventionActiveDidChange - object:nil]; -} - -- (void)isCensorshipCircumventionActiveDidChange:(NSNotification *)notification -{ - OWSAssertIsOnMainThread(); - - [self updateSocketStatusView]; -} - -- (void)OWSWebSocketStateDidChange -{ - OWSAssertIsOnMainThread(); - - [self updateSocketStatusView]; -} - -- (void)updateSocketStatusView { - OWSAssertIsOnMainThread(); - - if ([OWSSignalService sharedInstance].isCensorshipCircumventionActive) { - [_updateStatusTimer invalidate]; - [_socketStatusView removeFromSuperview]; - _socketStatusView = nil; - return; - } - - switch (TSSocketManager.shared.highestSocketState) { - case OWSWebSocketStateClosed: - if (_socketStatusView == nil) { - [self initializeSocketStatusBar]; - [_updateStatusTimer invalidate]; - _updateStatusTimer = [NSTimer weakScheduledTimerWithTimeInterval:0.5 - target:self - selector:@selector(updateProgress) - userInfo:nil - repeats:YES]; - - } else if (_socketStatusView.progress >= STALLED_PROGRESS) { - [_updateStatusTimer invalidate]; - } - break; - case OWSWebSocketStateConnecting: - // Do nothing. - break; - case OWSWebSocketStateOpen: - [_updateStatusTimer invalidate]; - [_socketStatusView removeFromSuperview]; - _socketStatusView = nil; - break; - } -} - -- (void)updateProgress { - double progress = _socketStatusView.progress + 0.05; - _socketStatusView.progress = (float) MIN(progress, STALLED_PROGRESS); -} - -@end diff --git a/Session/Signal/UpdateGroupViewController.h b/Session/Signal/UpdateGroupViewController.h deleted file mode 100644 index efee19c7d..000000000 --- a/Session/Signal/UpdateGroupViewController.h +++ /dev/null @@ -1,29 +0,0 @@ -// -// Copyright (c) 2018 Open Whisper Systems. All rights reserved. -// - -#import "OWSConversationSettingsViewDelegate.h" -#import - -NS_ASSUME_NONNULL_BEGIN - -@class TSGroupThread; - -typedef NS_ENUM(NSUInteger, UpdateGroupMode) { - UpdateGroupMode_Default = 0, - UpdateGroupMode_EditGroupName, - UpdateGroupMode_EditGroupAvatar, -}; - -@interface UpdateGroupViewController : OWSViewController - -@property (nonatomic, weak) id conversationSettingsViewDelegate; - -// This property _must_ be set before the view is presented. -@property (nonatomic) TSGroupThread *thread; - -@property (nonatomic) UpdateGroupMode mode; - -@end - -NS_ASSUME_NONNULL_END diff --git a/Session/Signal/UpdateGroupViewController.m b/Session/Signal/UpdateGroupViewController.m deleted file mode 100644 index 26ffdd171..000000000 --- a/Session/Signal/UpdateGroupViewController.m +++ /dev/null @@ -1,558 +0,0 @@ -// -// Copyright (c) 2019 Open Whisper Systems. All rights reserved. -// - -#import "UpdateGroupViewController.h" -#import "AddToGroupViewController.h" -#import "AvatarViewHelper.h" -#import "OWSNavigationController.h" -#import "Session-Swift.h" -#import "ViewControllerUtils.h" -#import -#import -#import -#import -#import -#import -#import -#import -#import -#import -#import -#import -#import -#import -#import -#import -#import -#import - -NS_ASSUME_NONNULL_BEGIN - -@interface UpdateGroupViewController () - -@property (nonatomic, readonly) OWSMessageSender *messageSender; -@property (nonatomic, readonly) ContactsViewHelper *contactsViewHelper; -@property (nonatomic, readonly) AvatarViewHelper *avatarViewHelper; - -@property (nonatomic, readonly) OWSTableViewController *tableViewController; -@property (nonatomic, readonly) AvatarImageView *avatarView; -@property (nonatomic, readonly) UITextField *groupNameTextField; - -@property (nonatomic, nullable) UIImage *groupAvatar; -@property (nonatomic, nullable) NSSet *previousMemberRecipientIds; -@property (nonatomic) NSMutableSet *memberRecipientIds; -@property (nonatomic) NSMutableSet *removedRecipientIds; - -@property (nonatomic) BOOL hasUnsavedChanges; - -@end - -#pragma mark - - -@implementation UpdateGroupViewController - -- (instancetype)init -{ - self = [super init]; - if (!self) { - return self; - } - - [self commonInit]; - - return self; -} - -- (nullable instancetype)initWithCoder:(NSCoder *)aDecoder -{ - self = [super initWithCoder:aDecoder]; - if (!self) { - return self; - } - - [self commonInit]; - - return self; -} - -- (void)commonInit -{ - _messageSender = SSKEnvironment.shared.messageSender; - _contactsViewHelper = [[ContactsViewHelper alloc] initWithDelegate:self]; - _avatarViewHelper = [AvatarViewHelper new]; - _avatarViewHelper.delegate = self; - - self.memberRecipientIds = [NSMutableSet new]; - self.removedRecipientIds = [NSMutableSet new]; -} - -#pragma mark - View Lifecycle - -- (void)loadView -{ - [super loadView]; - - OWSAssertDebug(self.thread); - OWSAssertDebug(self.thread.groupModel); - OWSAssertDebug(self.thread.groupModel.groupMemberIds); - - self.view.backgroundColor = Theme.backgroundColor; - - [self.memberRecipientIds addObjectsFromArray:self.thread.groupModel.groupMemberIds]; - self.previousMemberRecipientIds = [NSSet setWithArray:self.thread.groupModel.groupMemberIds]; - - self.title = NSLocalizedString(@"EDIT_GROUP_DEFAULT_TITLE", @"The navbar title for the 'update group' view."); - - // First section. - - UIView *firstSection = [self firstSectionHeader]; - [self.view addSubview:firstSection]; - [firstSection autoSetDimension:ALDimensionHeight toSize:100.f]; - [firstSection autoPinWidthToSuperview]; - [firstSection autoPinEdge:ALEdgeTop toEdge:ALEdgeTop ofView:self.view withOffset:0.0f]; - - _tableViewController = [OWSTableViewController new]; - _tableViewController.delegate = self; - [self.view addSubview:self.tableViewController.view]; - [self.tableViewController.view autoPinEdgeToSuperviewSafeArea:ALEdgeLeading]; - [self.tableViewController.view autoPinEdgeToSuperviewSafeArea:ALEdgeTrailing]; - [_tableViewController.view autoPinEdge:ALEdgeTop toEdge:ALEdgeBottom ofView:firstSection]; - [self autoPinViewToBottomOfViewControllerOrKeyboard:self.tableViewController.view avoidNotch:NO]; - self.tableViewController.tableView.rowHeight = UITableViewAutomaticDimension; - self.tableViewController.tableView.estimatedRowHeight = 60; - - [self updateTableContents]; -} - -- (void)setHasUnsavedChanges:(BOOL)hasUnsavedChanges -{ - _hasUnsavedChanges = hasUnsavedChanges; - - [self updateNavigationBar]; -} - -- (void)updateNavigationBar -{ - self.navigationItem.rightBarButtonItem = (self.hasUnsavedChanges - ? [[UIBarButtonItem alloc] initWithTitle:NSLocalizedString(@"EDIT_GROUP_UPDATE_BUTTON", - @"The title for the 'update group' button.") - style:UIBarButtonItemStylePlain - target:self - action:@selector(updateGroupPressed) - accessibilityIdentifier:ACCESSIBILITY_IDENTIFIER_WITH_NAME(self, @"update")] - : nil); -} - -- (void)viewDidAppear:(BOOL)animated -{ - [super viewDidAppear:animated]; - - switch (self.mode) { - case UpdateGroupMode_EditGroupName: - [self.groupNameTextField becomeFirstResponder]; - break; - case UpdateGroupMode_EditGroupAvatar: - [self showChangeAvatarUI]; - break; - default: - break; - } - // Only perform these actions the first time the view appears. - self.mode = UpdateGroupMode_Default; -} - -- (UIView *)firstSectionHeader -{ - OWSAssertDebug(self.thread); - OWSAssertDebug(self.thread.groupModel); - - UIView *firstSectionHeader = [UIView new]; - firstSectionHeader.userInteractionEnabled = YES; - [firstSectionHeader - addGestureRecognizer:[[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(headerWasTapped:)]]; - firstSectionHeader.backgroundColor = [Theme backgroundColor]; - UIView *threadInfoView = [UIView new]; - [firstSectionHeader addSubview:threadInfoView]; - [threadInfoView autoPinWidthToSuperviewWithMargin:16.f]; - [threadInfoView autoPinHeightToSuperviewWithMargin:16.f]; - - AvatarImageView *avatarView = [AvatarImageView new]; - _avatarView = avatarView; - - [threadInfoView addSubview:avatarView]; - [avatarView autoVCenterInSuperview]; - [avatarView autoPinLeadingToSuperviewMargin]; - [avatarView autoSetDimension:ALDimensionWidth toSize:kLargeAvatarSize]; - [avatarView autoSetDimension:ALDimensionHeight toSize:kLargeAvatarSize]; - _groupAvatar = self.thread.groupModel.groupImage; - [self updateAvatarView]; - - UITextField *groupNameTextField = [OWSTextField new]; - _groupNameTextField = groupNameTextField; - self.groupNameTextField.text = [self.thread.groupModel.groupName ows_stripped]; - groupNameTextField.textColor = [Theme primaryColor]; - groupNameTextField.font = [UIFont ows_dynamicTypeTitle2Font]; - groupNameTextField.placeholder - = NSLocalizedString(@"NEW_GROUP_NAMEGROUP_REQUEST_DEFAULT", @"Placeholder text for group name field"); - groupNameTextField.delegate = self; - [groupNameTextField addTarget:self - action:@selector(groupNameDidChange:) - forControlEvents:UIControlEventEditingChanged]; - [threadInfoView addSubview:groupNameTextField]; - [groupNameTextField autoVCenterInSuperview]; - [groupNameTextField autoPinTrailingToSuperviewMargin]; - [groupNameTextField autoPinLeadingToTrailingEdgeOfView:avatarView offset:16.f]; - SET_SUBVIEW_ACCESSIBILITY_IDENTIFIER(self, groupNameTextField); - - [avatarView - addGestureRecognizer:[[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(avatarTouched:)]]; - avatarView.userInteractionEnabled = YES; - SET_SUBVIEW_ACCESSIBILITY_IDENTIFIER(self, avatarView); - - return firstSectionHeader; -} - -- (void)headerWasTapped:(UIGestureRecognizer *)sender -{ - if (sender.state == UIGestureRecognizerStateRecognized) { - [self.groupNameTextField becomeFirstResponder]; - } -} - -- (void)avatarTouched:(UIGestureRecognizer *)sender -{ - if (sender.state == UIGestureRecognizerStateRecognized) { - [self showChangeAvatarUI]; - } -} - -#pragma mark - Table Contents - -- (void)updateTableContents -{ - OWSAssertDebug(self.thread); - - OWSTableContents *contents = [OWSTableContents new]; - - __weak UpdateGroupViewController *weakSelf = self; - ContactsViewHelper *contactsViewHelper = self.contactsViewHelper; - - // Group Members - - OWSTableSection *section = [OWSTableSection new]; - section.headerTitle = NSLocalizedString( - @"EDIT_GROUP_MEMBERS_SECTION_TITLE", @"a title for the members section of the 'new/update group' view."); - - [section addItem:[OWSTableItem - disclosureItemWithText:NSLocalizedString(@"EDIT_GROUP_MEMBERS_ADD_MEMBER", - @"Label for the cell that lets you add a new member to a group.") - customRowHeight:UITableViewAutomaticDimension - actionBlock:^{ - AddToGroupViewController *viewController = [AddToGroupViewController new]; - viewController.addToGroupDelegate = weakSelf; - [weakSelf.navigationController pushViewController:viewController animated:YES]; - }]]; - - NSMutableSet *memberRecipientIds = [self.memberRecipientIds mutableCopy]; - [memberRecipientIds removeObject:[contactsViewHelper localNumber]]; - for (NSString *recipientId in [memberRecipientIds.allObjects sortedArrayUsingSelector:@selector(compare:)]) { - [section - addItem:[OWSTableItem - itemWithCustomCellBlock:^{ - UpdateGroupViewController *strongSelf = weakSelf; - OWSCAssertDebug(strongSelf); - - ContactTableViewCell *cell = [ContactTableViewCell new]; - BOOL isPreviousMember = [strongSelf.previousMemberRecipientIds containsObject:recipientId]; - BOOL isBlocked = [contactsViewHelper isRecipientIdBlocked:recipientId]; - if (isPreviousMember) { - if (isBlocked) { - cell.accessoryMessage = NSLocalizedString( - @"CONTACT_CELL_IS_BLOCKED", @"An indicator that a contact has been blocked."); - } else { - cell.selectionStyle = UITableViewCellSelectionStyleNone; - } - } else { - // In the "members" section, we label "new" members as such when editing an existing - // group. - // - // The only way a "new" member could be blocked is if we blocked them on a linked device - // while in this dialog. We don't need to worry about that edge case. - cell.accessoryMessage = NSLocalizedString(@"EDIT_GROUP_NEW_MEMBER_LABEL", - @"An indicator that a user is a new member of the group."); - } - - [cell configureWithRecipientId:recipientId]; - - // TODO: Confirm with nancy if this will work. - NSString *cellName = [NSString stringWithFormat:@"member.%@", recipientId]; - cell.accessibilityIdentifier - = ACCESSIBILITY_IDENTIFIER_WITH_NAME(UpdateGroupViewController, cellName); - - return cell; - } - customRowHeight:UITableViewAutomaticDimension - actionBlock:^{ - SignalAccount *_Nullable signalAccount = - [contactsViewHelper fetchSignalAccountForRecipientId:recipientId]; - BOOL isPreviousMember = [weakSelf.previousMemberRecipientIds containsObject:recipientId]; - BOOL isBlocked = [contactsViewHelper isRecipientIdBlocked:recipientId]; - if (isPreviousMember) { - if (isBlocked) { - if (signalAccount) { - [weakSelf showUnblockAlertForSignalAccount:signalAccount]; - } else { - [weakSelf showUnblockAlertForRecipientId:recipientId]; - } - } else { - [OWSAlerts - showAlertWithTitle: - NSLocalizedString(@"UPDATE_GROUP_CANT_REMOVE_MEMBERS_ALERT_TITLE", - @"Title for alert indicating that group members can't be removed.") - message:NSLocalizedString( - @"UPDATE_GROUP_CANT_REMOVE_MEMBERS_ALERT_MESSAGE", - @"Title for alert indicating that group members can't " - @"be removed.")]; - } - } else { - [weakSelf removeRecipientId:recipientId]; - } - }]]; - } - [contents addSection:section]; - - self.tableViewController.contents = contents; -} - -- (void)showUnblockAlertForSignalAccount:(SignalAccount *)signalAccount -{ - OWSAssertDebug(signalAccount); - - __weak UpdateGroupViewController *weakSelf = self; - [BlockListUIUtils showUnblockSignalAccountActionSheet:signalAccount - fromViewController:self - blockingManager:self.contactsViewHelper.blockingManager - contactsManager:self.contactsViewHelper.contactsManager - completionBlock:^(BOOL isBlocked) { - if (!isBlocked) { - [weakSelf updateTableContents]; - } - }]; -} - -- (void)showUnblockAlertForRecipientId:(NSString *)recipientId -{ - OWSAssertDebug(recipientId.length > 0); - - __weak UpdateGroupViewController *weakSelf = self; - [BlockListUIUtils showUnblockPhoneNumberActionSheet:recipientId - fromViewController:self - blockingManager:self.contactsViewHelper.blockingManager - contactsManager:self.contactsViewHelper.contactsManager - completionBlock:^(BOOL isBlocked) { - if (!isBlocked) { - [weakSelf updateTableContents]; - } - }]; -} - -- (void)removeRecipientId:(NSString *)recipientId -{ - OWSAssertDebug(recipientId.length > 0); - - [self.memberRecipientIds removeObject:recipientId]; - [self.removedRecipientIds addObject:recipientId]; - [self updateTableContents]; -} - -#pragma mark - Methods - -- (void)updateGroup -{ - OWSAssertDebug(self.conversationSettingsViewDelegate); - - NSString *groupName = [self.groupNameTextField.text ows_stripped]; - TSGroupModel *groupModel = [[TSGroupModel alloc] initWithTitle:groupName - memberIds:self.memberRecipientIds.allObjects - image:self.groupAvatar - groupId:self.thread.groupModel.groupId - groupType:self.thread.groupModel.groupType - adminIds:self.thread.groupModel.groupAdminIds]; - [self.conversationSettingsViewDelegate groupWasUpdated:groupModel]; -} - -#pragma mark - Group Avatar - -- (void)showChangeAvatarUI -{ - [self.groupNameTextField resignFirstResponder]; - - [self.avatarViewHelper showChangeAvatarUI]; -} - -- (void)setGroupAvatar:(nullable UIImage *)groupAvatar -{ - OWSAssertIsOnMainThread(); - - _groupAvatar = groupAvatar; - - self.hasUnsavedChanges = YES; - - [self updateAvatarView]; -} - -- (void)updateAvatarView -{ - UIImage *_Nullable groupAvatar = self.groupAvatar; - if (!groupAvatar) { - groupAvatar = [[[OWSGroupAvatarBuilder alloc] initWithThread:self.thread diameter:kLargeAvatarSize] build]; - } - self.avatarView.image = groupAvatar; -} - -#pragma mark - Event Handling - -- (void)backButtonPressed -{ - [self.groupNameTextField resignFirstResponder]; - - if (!self.hasUnsavedChanges) { - // If user made no changes, return to conversation settings view. - [self.navigationController popViewControllerAnimated:YES]; - return; - } - - UIAlertController *alert = [UIAlertController - alertControllerWithTitle:NSLocalizedString(@"EDIT_GROUP_VIEW_UNSAVED_CHANGES_TITLE", - @"The alert title if user tries to exit update group view without saving changes.") - message: - NSLocalizedString(@"EDIT_GROUP_VIEW_UNSAVED_CHANGES_MESSAGE", - @"The alert message if user tries to exit update group view without saving changes.") - preferredStyle:UIAlertControllerStyleAlert]; - [alert addAction:[UIAlertAction actionWithTitle:NSLocalizedString(@"ALERT_SAVE", - @"The label for the 'save' button in action sheets.") - accessibilityIdentifier:ACCESSIBILITY_IDENTIFIER_WITH_NAME(self, @"save") - style:UIAlertActionStyleDefault - handler:^(UIAlertAction *action) { - OWSAssertDebug(self.conversationSettingsViewDelegate); - - [self updateGroup]; - - [self.conversationSettingsViewDelegate - popAllConversationSettingsViewsWithCompletion:nil]; - }]]; - [alert addAction:[UIAlertAction actionWithTitle:NSLocalizedString(@"ALERT_DONT_SAVE", - @"The label for the 'don't save' button in action sheets.") - accessibilityIdentifier:ACCESSIBILITY_IDENTIFIER_WITH_NAME(self, @"dont_save") - style:UIAlertActionStyleDestructive - handler:^(UIAlertAction *action) { - [self.navigationController popViewControllerAnimated:YES]; - }]]; - [self presentAlert:alert]; -} - -- (void)updateGroupPressed -{ - OWSAssertDebug(self.conversationSettingsViewDelegate); - - [self updateGroup]; - - [self.conversationSettingsViewDelegate popAllConversationSettingsViewsWithCompletion:nil]; -} - -- (void)groupNameDidChange:(id)sender -{ - self.hasUnsavedChanges = YES; -} - -#pragma mark - Text Field Delegate - -- (BOOL)textFieldShouldReturn:(UITextField *)textField -{ - [self.groupNameTextField resignFirstResponder]; - return NO; -} - -#pragma mark - OWSTableViewControllerDelegate - -- (void)tableViewWillBeginDragging -{ - [self.groupNameTextField resignFirstResponder]; -} - -#pragma mark - ContactsViewHelperDelegate - -- (void)contactsViewHelperDidUpdateContacts -{ - [self updateTableContents]; -} - -- (BOOL)shouldHideLocalNumber -{ - return YES; -} - -#pragma mark - AvatarViewHelperDelegate - -- (nullable NSString *)avatarActionSheetTitle -{ - return NSLocalizedString( - @"NEW_GROUP_ADD_PHOTO_ACTION", @"Action Sheet title prompting the user for a group avatar"); -} - -- (void)avatarDidChange:(UIImage *)image -{ - OWSAssertIsOnMainThread(); - OWSAssertDebug(image); - - self.groupAvatar = image; -} - -- (UIViewController *)fromViewController -{ - return self; -} - -- (BOOL)hasClearAvatarAction -{ - return NO; -} - -#pragma mark - AddToGroupViewControllerDelegate - -- (void)recipientIdWasAdded:(NSString *)recipientId -{ - [self.memberRecipientIds addObject:recipientId]; - self.hasUnsavedChanges = YES; - [self updateTableContents]; -} - -- (BOOL)isRecipientGroupMember:(NSString *)recipientId -{ - OWSAssertDebug(recipientId.length > 0); - - return [self.memberRecipientIds containsObject:recipientId]; -} - -#pragma mark - OWSNavigationView - -- (BOOL)shouldCancelNavigationBack -{ - BOOL result = self.hasUnsavedChanges; - if (result) { - [self backButtonPressed]; - } - return result; -} - -@end - -NS_ASSUME_NONNULL_END diff --git a/Session/Signal/UserNotificationsAdaptee.swift b/Session/Signal/UserNotificationsAdaptee.swift index f434705af..e5b9b7409 100644 --- a/Session/Signal/UserNotificationsAdaptee.swift +++ b/Session/Signal/UserNotificationsAdaptee.swift @@ -125,7 +125,7 @@ extension UserNotificationPresenterAdaptee: NotificationPresenterAdaptee { let trigger: UNNotificationTrigger? let checkForCancel = category == .incomingMessage - if checkForCancel && hasReceivedSyncMessageRecently { + if checkForCancel { assert(userInfo[AppNotificationUserInfoKey.threadId] != nil) trigger = UNTimeIntervalNotificationTrigger(timeInterval: kNotificationDelayForRemoteRead, repeats: false) } else { diff --git a/Session/Signal/WebRTCCallMessageHandler.swift b/Session/Signal/WebRTCCallMessageHandler.swift index 918b9b49d..3c037e0a1 100644 --- a/Session/Signal/WebRTCCallMessageHandler.swift +++ b/Session/Signal/WebRTCCallMessageHandler.swift @@ -21,61 +21,8 @@ public class WebRTCCallMessageHandler: NSObject/*, OWSCallMessageHandler*/ { // MARK: - Dependencies - private var messageSender : MessageSender - { - return SSKEnvironment.shared.messageSender - } - private var accountManager : AccountManager { return AppEnvironment.shared.accountManager } - -// private var callService : CallService -// { -// return AppEnvironment.shared.callService -// } - - // MARK: - Call Handlers - -// public func receivedOffer(_ offer: SSKProtoCallMessageOffer, from callerId: String) { -// AssertIsOnMainThread() -// -// let thread = TSContactThread.getOrCreateThread(contactId: callerId) -// self.callService.handleReceivedOffer(thread: thread, callId: offer.id, sessionDescription: offer.sessionDescription) -// } -// -// public func receivedAnswer(_ answer: SSKProtoCallMessageAnswer, from callerId: String) { -// AssertIsOnMainThread() -// -// let thread = TSContactThread.getOrCreateThread(contactId: callerId) -// self.callService.handleReceivedAnswer(thread: thread, callId: answer.id, sessionDescription: answer.sessionDescription) -// } -// -// public func receivedIceUpdate(_ iceUpdate: SSKProtoCallMessageIceUpdate, from callerId: String) { -// AssertIsOnMainThread() -// -// let thread = TSContactThread.getOrCreateThread(contactId: callerId) -// -// // Discrepency between our protobuf's sdpMlineIndex, which is unsigned, -// // while the RTC iOS API requires a signed int. -// let lineIndex = Int32(iceUpdate.sdpMlineIndex) -// -// self.callService.handleRemoteAddedIceCandidate(thread: thread, callId: iceUpdate.id, sdp: iceUpdate.sdp, lineIndex: lineIndex, mid: iceUpdate.sdpMid) -// } -// -// public func receivedHangup(_ hangup: SSKProtoCallMessageHangup, from callerId: String) { -// AssertIsOnMainThread() -// -// let thread = TSContactThread.getOrCreateThread(contactId: callerId) -// self.callService.handleRemoteHangup(thread: thread, callId: hangup.id) -// } -// -// public func receivedBusy(_ busy: SSKProtoCallMessageBusy, from callerId: String) { -// AssertIsOnMainThread() -// -// let thread = TSContactThread.getOrCreateThread(contactId: callerId) -// self.callService.handleRemoteBusy(thread: thread, callId: busy.id) -// } - } diff --git a/Session/Utilities/BackgroundPoller.swift b/Session/Utilities/BackgroundPoller.swift index 5fb96a3e6..6786fd876 100644 --- a/Session/Utilities/BackgroundPoller.swift +++ b/Session/Utilities/BackgroundPoller.swift @@ -9,7 +9,8 @@ public final class BackgroundPoller : NSObject { @objc(pollWithCompletionHandler:) public static func poll(completionHandler: @escaping (UIBackgroundFetchResult) -> Void) { var promises: [Promise] = [] - promises.append(AppEnvironment.shared.messageFetcherJob.run()) // FIXME: It'd be nicer to just use Poller directly + // TODO TODO TODO +// promises.append(AppEnvironment.shared.messageFetcherJob.run()) // FIXME: It'd be nicer to just use Poller directly closedGroupPoller = ClosedGroupPoller() promises.append(contentsOf: closedGroupPoller.pollOnce()) var openGroups: [String:OpenGroup] = [:] diff --git a/Session/View Controllers/BaseVC.swift b/Session/View Controllers/BaseVC.swift index 9dd73ba23..ed234fead 100644 --- a/Session/View Controllers/BaseVC.swift +++ b/Session/View Controllers/BaseVC.swift @@ -24,7 +24,6 @@ class BaseVC : UIViewController { override func viewDidLoad() { setNeedsStatusBarAppearanceUpdate() - NotificationCenter.default.addObserver(self, selector: #selector(handleUnexpectedDeviceLinkRequestReceivedNotification(_:)), name: .unexpectedDeviceLinkRequestReceived, object: nil) NotificationCenter.default.addObserver(self, selector: #selector(handleAppModeChangedNotification(_:)), name: .appModeChanged, object: nil) } @@ -71,15 +70,6 @@ class BaseVC : UIViewController { NotificationCenter.default.removeObserver(self) } - @objc private func handleUnexpectedDeviceLinkRequestReceivedNotification(_ notification: Notification) { - guard DeviceLinkingUtilities.shouldShowUnexpectedDeviceLinkRequestReceivedAlert else { return } - DispatchQueue.main.async { - let alert = UIAlertController(title: "Device Link Request Received", message: "Open the device link screen by going to \"Settings\" > \"Devices\" > \"Link a Device\" to link your devices.", preferredStyle: .alert) - alert.addAction(UIAlertAction(title: NSLocalizedString("OK", comment: ""), style: .default, handler: nil)) - self.present(alert, animated: true, completion: nil) - } - } - override func traitCollectionDidChange(_ previousTraitCollection: UITraitCollection?) { // TODO: Post an appModeChanged notification? } diff --git a/Session/View Controllers/DeviceLinkingModal.swift b/Session/View Controllers/DeviceLinkingModal.swift deleted file mode 100644 index bddec1370..000000000 --- a/Session/View Controllers/DeviceLinkingModal.swift +++ /dev/null @@ -1,256 +0,0 @@ -import NVActivityIndicatorView - -@objc(LKDeviceLinkingModal) -final class DeviceLinkingModal : Modal, DeviceLinkingSessionDelegate { - private let mode: Mode - private let delegate: DeviceLinkingModalDelegate? - private var deviceLink: DeviceLink? - private var hasAuthorizedDeviceLink = false - - // MARK: Types - enum Mode : String { case master, slave } - - // MARK: Components - private lazy var spinner = NVActivityIndicatorView(frame: CGRect.zero, type: .circleStrokeSpin, color: Colors.text, padding: nil) - - private lazy var qrCodeImageViewContainer: UIView = { - let result = UIView() - result.addSubview(qrCodeImageView) - qrCodeImageView.pin(.top, to: .top, of: result) - qrCodeImageView.pin(.bottom, to: .bottom, of: result) - qrCodeImageView.center(.horizontal, in: result) - return result - }() - - private lazy var qrCodeImageView: UIImageView = { - let result = UIImageView() - result.contentMode = .scaleAspectFit - let size: CGFloat = 128 - result.set(.width, to: size) - result.set(.height, to: size) - return result - }() - - private lazy var titleLabel: UILabel = { - let result = UILabel() - result.textColor = Colors.text - result.font = .boldSystemFont(ofSize: Values.mediumFontSize) - result.numberOfLines = 0 - result.lineBreakMode = .byWordWrapping - result.textAlignment = .center - return result - }() - - private lazy var subtitleLabel: UILabel = { - let result = UILabel() - result.textColor = Colors.text - result.font = .systemFont(ofSize: Values.smallFontSize) - result.numberOfLines = 0 - result.lineBreakMode = .byWordWrapping - result.textAlignment = .center - return result - }() - - private lazy var mnemonicLabel: UILabel = { - let result = UILabel() - result.textColor = Colors.text - result.font = .systemFont(ofSize: Values.smallFontSize) - result.numberOfLines = 0 - result.lineBreakMode = .byWordWrapping - result.textAlignment = .center - return result - }() - - private lazy var buttonStackView: UIStackView = { - let result = UIStackView(arrangedSubviews: [ cancelButton, authorizeButton ]) - result.axis = .horizontal - result.spacing = Values.mediumSpacing - result.distribution = .fillEqually - return result - }() - - private lazy var authorizeButton: UIButton = { - let result = UIButton() - result.set(.height, to: Values.mediumButtonHeight) - result.layer.cornerRadius = Values.modalButtonCornerRadius - result.backgroundColor = Colors.accent - result.titleLabel!.font = .systemFont(ofSize: Values.smallFontSize) - result.setTitleColor(Colors.text, for: UIControl.State.normal) - result.setTitle(NSLocalizedString("modal_link_device_master_mode_authorize_button_title", comment: ""), for: UIControl.State.normal) - return result - }() - - private lazy var mainStackView: UIStackView = { - let result = UIStackView(arrangedSubviews: [ titleLabel, subtitleLabel, mnemonicLabel, buttonStackView ]) - result.spacing = Values.largeSpacing - result.axis = .vertical - return result - }() - - // MARK: Lifecycle - init(mode: Mode, delegate: DeviceLinkingModalDelegate?) { - self.mode = mode - if mode == .slave { - guard delegate != nil else { preconditionFailure("Missing delegate for device linking modal in slave mode.") } - } - self.delegate = delegate - super.init(nibName: nil, bundle: nil) - } - - @objc(initWithMode:delegate:) - convenience init(modeAsString: String, delegate: DeviceLinkingModalDelegate?) { - guard let mode = Mode(rawValue: modeAsString) else { preconditionFailure("Invalid mode: \(modeAsString).") } - self.init(mode: mode, delegate: delegate) - } - - required init?(coder: NSCoder) { preconditionFailure() } - override init(nibName: String?, bundle: Bundle?) { preconditionFailure() } - - override func viewDidLoad() { - super.viewDidLoad() - switch mode { - case .master: let _ = DeviceLinkingSession.startListeningForLinkingRequests(with: self) - case .slave: let _ = DeviceLinkingSession.startListeningForLinkingAuthorization(with: self) - } - } - - override func populateContentView() { - switch mode { - case .master: mainStackView.insertArrangedSubview(qrCodeImageViewContainer, at: 0) - case .slave: mainStackView.insertArrangedSubview(spinner, at: 0) - } - contentView.addSubview(mainStackView) - switch mode { - case .master: - let hexEncodedPublicKey = getUserHexEncodedPublicKey() - qrCodeImageView.image = QRCode.generate(for: hexEncodedPublicKey, hasBackground: true) - case .slave: - spinner.set(.height, to: 64) - spinner.startAnimating() - } - titleLabel.text = { - switch mode { - case .master: return NSLocalizedString("modal_link_device_master_mode_title_1", comment: "") - case .slave: return NSLocalizedString("modal_link_device_slave_mode_title_1", comment: "") - } - }() - subtitleLabel.text = { - switch mode { - case .master: return NSLocalizedString("modal_link_device_master_mode_explanation_1", comment: "") - case .slave: return NSLocalizedString("modal_link_device_slave_mode_explanation_1", comment: "") - } - }() - mnemonicLabel.isHidden = (mode == .master) - if mode == .slave { - let hexEncodedPublicKey = getUserHexEncodedPublicKey().removing05PrefixIfNeeded() - mnemonicLabel.text = Mnemonic.hash(hexEncodedString: hexEncodedPublicKey) - } - authorizeButton.addTarget(self, action: #selector(authorizeDeviceLink), for: UIControl.Event.touchUpInside) - authorizeButton.isHidden = true - mainStackView.pin(.leading, to: .leading, of: contentView, withInset: Values.largeSpacing) - mainStackView.pin(.top, to: .top, of: contentView, withInset: Values.largeSpacing) - contentView.pin(.trailing, to: .trailing, of: mainStackView, withInset: Values.largeSpacing) - contentView.pin(.bottom, to: .bottom, of: mainStackView, withInset: Values.largeSpacing) - } - - // MARK: Device Linking - func requestUserAuthorization(for deviceLink: DeviceLink) { - self.deviceLink = deviceLink - qrCodeImageViewContainer.isHidden = true - titleLabel.text = NSLocalizedString("modal_link_device_master_mode_title_2", comment: "") - subtitleLabel.text = NSLocalizedString("modal_link_device_master_mode_explanation_2", comment: "") - let hexEncodedPublicKey = deviceLink.slave.publicKey.removing05PrefixIfNeeded() - mnemonicLabel.text = Mnemonic.hash(hexEncodedString: hexEncodedPublicKey) - mnemonicLabel.isHidden = false - authorizeButton.isHidden = false - } - - @objc private func authorizeDeviceLink() { - guard !hasAuthorizedDeviceLink else { return } - hasAuthorizedDeviceLink = true - mainStackView.removeArrangedSubview(qrCodeImageViewContainer) - mainStackView.insertArrangedSubview(spinner, at: 0) - spinner.set(.height, to: 64) - spinner.startAnimating() - titleLabel.text = NSLocalizedString("modal_link_device_master_mode_title_3", comment: "") - subtitleLabel.text = NSLocalizedString("modal_link_device_master_mode_explanation_3", comment: "") - mnemonicLabel.isHidden = true - buttonStackView.isHidden = true - let deviceLink = self.deviceLink! - DeviceLinkingSession.current!.markLinkingRequestAsProcessed() - DeviceLinkingSession.current!.stopListeningForLinkingRequests() - let linkingAuthorizationMessage = DeviceLinkingUtilities.getLinkingAuthorizationMessage(for: deviceLink) - let master = DeviceLink.Device(publicKey: deviceLink.master.publicKey, signature: linkingAuthorizationMessage.masterSignature) - let signedDeviceLink = DeviceLink(between: master, and: deviceLink.slave) - FileServerAPI.addDeviceLink(signedDeviceLink).done(on: DispatchQueue.main) { [weak self] in - SSKEnvironment.shared.messageSender.send(linkingAuthorizationMessage, success: { - let slavePublicKey = deviceLink.slave.publicKey - Storage.writeSync { transaction in - let thread = TSContactThread.getOrCreateThread(withContactId: slavePublicKey, transaction: transaction) - thread.save(with: transaction) - } - let _ = SSKEnvironment.shared.syncManager.syncAllGroups().ensure { - // Closed groups first because we prefer the session request mechanism - // to the AFR mechanism - let _ = SSKEnvironment.shared.syncManager.syncAllContacts() - } - let _ = SSKEnvironment.shared.syncManager.syncAllOpenGroups() - DispatchQueue.main.async { - self?.dismiss(animated: true, completion: nil) - self?.delegate?.handleDeviceLinkAuthorized(signedDeviceLink) - } - }, failure: { error in - print("[Loki] Failed to send device link authorization message.") - let _ = FileServerAPI.removeDeviceLink(signedDeviceLink) // Attempt to roll back - DispatchQueue.main.async { - self?.close() - let alert = UIAlertController(title: "Device Linking Failed", message: "Please check your internet connection and try again", preferredStyle: .alert) - alert.addAction(UIAlertAction(title: NSLocalizedString("OK", comment: ""), style: .default, handler: nil)) - self?.presentingViewController?.present(alert, animated: true, completion: nil) - } - }) - }.catch { [weak self] error in - print("[Loki] Failed to add device link due to error: \(error).") - DispatchQueue.main.async { - self?.close() // TODO: Show a message to the user - let alert = UIAlertController(title: "Device Linking Failed", message: "Please check your internet connection and try again", preferredStyle: .alert) - alert.addAction(UIAlertAction(title: NSLocalizedString("OK", comment: ""), style: .default, handler: nil)) - self?.presentingViewController?.present(alert, animated: true, completion: nil) - } - } - } - - func handleDeviceLinkAuthorized(_ deviceLink: DeviceLink) { - let session = DeviceLinkingSession.current! - session.stopListeningForLinkingAuthorization() - spinner.stopAnimating() - spinner.isHidden = true - titleLabel.text = NSLocalizedString("modal_link_device_slave_mode_title_2", comment: "") - subtitleLabel.text = NSLocalizedString("modal_link_device_slave_mode_explanation_2", comment: "") - mnemonicLabel.isHidden = true - buttonStackView.isHidden = true - FileServerAPI.addDeviceLink(deviceLink).catch { error in - print("[Loki] Failed to add device link due to error: \(error).") - } - Timer.scheduledTimer(withTimeInterval: 2, repeats: false) { _ in - self.dismiss(animated: true) { - self.delegate?.handleDeviceLinkAuthorized(deviceLink) - } - } - } - - @objc override func close() { - guard let session = DeviceLinkingSession.current else { - return print("[Loki] Device linking session missing.") // Should never occur - } - session.stopListeningForLinkingRequests() - session.markLinkingRequestAsProcessed() // Only relevant in master mode - delegate?.handleDeviceLinkingModalDismissed() // Only relevant in slave mode - if let deviceLink = deviceLink { - Storage.writeSync { transaction in - OWSPrimaryStorage.shared().removePreKeyBundle(forContact: deviceLink.slave.publicKey, transaction: transaction) - } - } - dismiss(animated: true, completion: nil) - } -} diff --git a/Session/View Controllers/DeviceLinkingModalDelegate.swift b/Session/View Controllers/DeviceLinkingModalDelegate.swift deleted file mode 100644 index f8586401f..000000000 --- a/Session/View Controllers/DeviceLinkingModalDelegate.swift +++ /dev/null @@ -1,12 +0,0 @@ - -@objc protocol DeviceLinkingModalDelegate { - - func handleDeviceLinkAuthorized(_ deviceLink: DeviceLink) - func handleDeviceLinkingModalDismissed() -} - -extension DeviceLinkingModalDelegate { - - func handleDeviceLinkAuthorized(_ deviceLink: DeviceLink) { /* Do nothing */ } - func handleDeviceLinkingModalDismissed() { /* Do nothing */ } -} diff --git a/Session/View Controllers/DeviceLinksVC.swift b/Session/View Controllers/DeviceLinksVC.swift deleted file mode 100644 index 8eccf4017..000000000 --- a/Session/View Controllers/DeviceLinksVC.swift +++ /dev/null @@ -1,234 +0,0 @@ - -// MARK: - Device Links View Controller - -@objc(LKDeviceLinksVC) -final class DeviceLinksVC : BaseVC, UITableViewDataSource, UITableViewDelegate, DeviceLinkingModalDelegate, DeviceNameModalDelegate { - private var deviceLinks: [DeviceLink] = [] { didSet { updateUI() } } - - // MARK: Components - private lazy var tableView: UITableView = { - let result = UITableView() - result.dataSource = self - result.delegate = self - result.register(Cell.self, forCellReuseIdentifier: "Cell") - result.separatorStyle = .none - result.backgroundColor = .clear - return result - }() - - private lazy var callToActionView : UIStackView = { - let explanationLabel = UILabel() - explanationLabel.textColor = Colors.text - explanationLabel.font = .systemFont(ofSize: Values.smallFontSize) - explanationLabel.numberOfLines = 0 - explanationLabel.lineBreakMode = .byWordWrapping - explanationLabel.textAlignment = .center - explanationLabel.text = NSLocalizedString("vc_linked_devices_empty_state_message", comment: "") - let linkNewDeviceButton = Button(style: .prominentOutline, size: .large) - linkNewDeviceButton.setTitle(NSLocalizedString("vc_linked_devices_empty_state_button_title", comment: ""), for: UIControl.State.normal) - linkNewDeviceButton.addTarget(self, action: #selector(linkNewDevice), for: UIControl.Event.touchUpInside) - linkNewDeviceButton.set(.width, to: 196) - let result = UIStackView(arrangedSubviews: [ explanationLabel, linkNewDeviceButton ]) - result.axis = .vertical - result.spacing = Values.mediumSpacing - result.alignment = .center - return result - }() - - // MARK: Lifecycle - override func viewDidLoad() { - super.viewDidLoad() - setUpGradientBackground() - setUpNavBarStyle() - setNavBarTitle(NSLocalizedString("vc_linked_devices_title", comment: "")) - // Set up link new device button - let linkNewDeviceButton = UIBarButtonItem(barButtonSystemItem: .add, target: self, action: #selector(linkNewDevice)) - linkNewDeviceButton.tintColor = Colors.text - navigationItem.rightBarButtonItem = linkNewDeviceButton - // Set up constraints - view.addSubview(tableView) - tableView.pin(to: view) - view.addSubview(callToActionView) - callToActionView.center(.horizontal, in: view) - let verticalCenteringConstraint = callToActionView.center(.vertical, in: view) - verticalCenteringConstraint.constant = -16 // Makes things appear centered visually - // Perform initial update - updateDeviceLinks() - } - - // MARK: Data - func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { - return deviceLinks.count - } - - func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { - let cell = tableView.dequeueReusableCell(withIdentifier: "Cell") as! Cell - let selectedBackgroundView = UIView() - selectedBackgroundView.backgroundColor = Colors.cellSelected - cell.selectedBackgroundView = selectedBackgroundView - let device = deviceLinks[indexPath.row].other - cell.device = device - return cell - } - - // MARK: Updating - private func updateDeviceLinks() { - let storage = OWSPrimaryStorage.shared() - let userHexEncodedPublicKey = getUserHexEncodedPublicKey() - var deviceLinks: [DeviceLink] = [] - storage.dbReadConnection.read { transaction in - deviceLinks = storage.getDeviceLinks(for: userHexEncodedPublicKey, in: transaction).sorted { lhs, rhs in - return lhs.other.publicKey > rhs.other.publicKey - } - } - self.deviceLinks = deviceLinks - } - - private func updateUI() { - tableView.reloadData() - UIView.animate(withDuration: 0.25) { - self.callToActionView.isHidden = !self.deviceLinks.isEmpty - } - } - - func handleDeviceLinkAuthorized(_ deviceLink: DeviceLink) { - // The modal already dismisses itself - updateDeviceLinks() - } - - func handleDeviceLinkingModalDismissed() { - // Do nothing - } - - // MARK: Interaction - @objc private func linkNewDevice() { - if deviceLinks.isEmpty { - let deviceLinkingModal = DeviceLinkingModal(mode: .master, delegate: self) - deviceLinkingModal.modalPresentationStyle = .overFullScreen - deviceLinkingModal.modalTransitionStyle = .crossDissolve - present(deviceLinkingModal, animated: true, completion: nil) - } else { - let alert = UIAlertController(title: NSLocalizedString("vc_linked_devices_multi_device_limit_reached_modal_title", comment: ""), message: NSLocalizedString("It's currently not allowed to link more than one device.", comment: ""), preferredStyle: .alert) - alert.addAction(UIAlertAction(title: NSLocalizedString("OK", comment: ""), style: .default, handler: nil)) - present(alert, animated: true, completion: nil) - } - } - - func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) { - defer { tableView.deselectRow(at: indexPath, animated: true) } - let deviceLink = deviceLinks[indexPath.row] - let sheet = UIAlertController(title: nil, message: nil, preferredStyle: .actionSheet) - sheet.addAction(UIAlertAction(title: NSLocalizedString("vc_device_list_bottom_sheet_change_name_button_title", comment: ""), style: .default) { [weak self] _ in - guard let self = self else { return } - let deviceNameModal = DeviceNameModal() - deviceNameModal.device = deviceLink.other - deviceNameModal.delegate = self - deviceNameModal.modalPresentationStyle = .overFullScreen - deviceNameModal.modalTransitionStyle = .crossDissolve - self.present(deviceNameModal, animated: true, completion: nil) - }) - sheet.addAction(UIAlertAction(title: NSLocalizedString("vc_device_list_bottom_sheet_unlink_device_button_title", comment: ""), style: .destructive) { [weak self] _ in - self?.removeDeviceLink(deviceLink) - }) - sheet.addAction(UIAlertAction(title: NSLocalizedString("cancel", comment: ""), style: .cancel) { _ in }) - present(sheet, animated: true, completion: nil) - } - - @objc func handleDeviceNameChanged(to name: String, for device: DeviceLink.Device) { - dismiss(animated: true, completion: nil) - updateUI() - } - - private func removeDeviceLink(_ deviceLink: DeviceLink) { - FileServerAPI.removeDeviceLink(deviceLink).done { [weak self] in - let linkedDevicePublicKey = deviceLink.other.publicKey - guard let thread = TSContactThread.fetch(uniqueId: TSContactThread.threadId(fromContactId: linkedDevicePublicKey)) else { return } - let unlinkDeviceMessage = UnlinkDeviceMessage(thread: thread) - SSKEnvironment.shared.messageSender.send(unlinkDeviceMessage, success: { - let storage = OWSPrimaryStorage.shared() - Storage.writeSync { transaction in - storage.removePreKeyBundle(forContact: linkedDevicePublicKey, transaction: transaction) - storage.deleteAllSessions(forContact: linkedDevicePublicKey, protocolContext: transaction) - for groupPublicKey in Storage.getUserClosedGroupPublicKeys() { - // TODO: Possibly re-implement in the future - // ClosedGroupsProtocol.removeMembers([ linkedDevicePublicKey ], from: groupPublicKey, using: transaction) - } - } - }, failure: { _ in - print("[Loki] Failed to send unlink device message.") - let storage = OWSPrimaryStorage.shared() - Storage.writeSync { transaction in - storage.removePreKeyBundle(forContact: linkedDevicePublicKey, transaction: transaction) - storage.deleteAllSessions(forContact: linkedDevicePublicKey, protocolContext: transaction) - for groupPublicKey in Storage.getUserClosedGroupPublicKeys() { - // TODO: Possibly re-implement in the future - // ClosedGroupsProtocol.removeMembers([ linkedDevicePublicKey ], from: groupPublicKey, using: transaction) - } - } - }) - self?.updateDeviceLinks() - }.catch { [weak self] _ in - let alert = UIAlertController(title: "Couldn't Unlink Device", message: "Please check your internet connection and try again", preferredStyle: .alert) - alert.addAction(UIAlertAction(title: NSLocalizedString("OK", comment: ""), accessibilityIdentifier: nil, style: .default, handler: nil)) - self?.present(alert, animated: true, completion: nil) - } - } -} - -// MARK: - Cell - -private extension DeviceLinksVC { - - final class Cell : UITableViewCell { - var device: DeviceLink.Device! { didSet { update() } } - - // MARK: Components - private lazy var titleLabel: UILabel = { - let result = UILabel() - result.textColor = Colors.text - result.font = .boldSystemFont(ofSize: Values.mediumFontSize) - result.lineBreakMode = .byTruncatingTail - return result - }() - - private lazy var subtitleLabel: UILabel = { - let result = UILabel() - result.textColor = Colors.text - result.font = .systemFont(ofSize: Values.smallFontSize) - result.lineBreakMode = .byTruncatingTail - return result - }() - - // MARK: Initialization - override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) { - super.init(style: style, reuseIdentifier: reuseIdentifier) - setUpViewHierarchy() - } - - required init?(coder: NSCoder) { - super.init(coder: coder) - setUpViewHierarchy() - } - - private func setUpViewHierarchy() { - backgroundColor = Colors.cellBackground - let stackView = UIStackView(arrangedSubviews: [ titleLabel, subtitleLabel ]) - stackView.axis = .vertical - stackView.distribution = .equalCentering - stackView.spacing = Values.verySmallSpacing - stackView.set(.height, to: 44) - contentView.addSubview(stackView) - stackView.pin(.leading, to: .leading, of: contentView, withInset: Values.largeSpacing) - stackView.pin(.top, to: .top, of: contentView, withInset: 12) - contentView.pin(.trailing, to: .trailing, of: stackView, withInset: Values.largeSpacing) - contentView.pin(.bottom, to: .bottom, of: stackView, withInset: 12) - stackView.set(.width, to: UIScreen.main.bounds.width - 2 * Values.largeSpacing) - } - - // MARK: Updating - private func update() { - titleLabel.text = device.displayName - subtitleLabel.text = Mnemonic.hash(hexEncodedString: device.publicKey.removing05PrefixIfNeeded()) - } - } -} diff --git a/Session/View Controllers/DeviceNameModal.swift b/Session/View Controllers/DeviceNameModal.swift deleted file mode 100644 index 76b3f808b..000000000 --- a/Session/View Controllers/DeviceNameModal.swift +++ /dev/null @@ -1,103 +0,0 @@ - -@objc(LKDeviceNameModal) -final class DeviceNameModal : Modal { - @objc public var device: DeviceLink.Device! - @objc public var delegate: DeviceNameModalDelegate? - - // MARK: Components - private lazy var nameTextField: UITextField = { - let result = UITextField() - result.textColor = Colors.text - result.font = .systemFont(ofSize: Values.mediumFontSize) - result.textAlignment = .center - let placeholder = NSMutableAttributedString(string: NSLocalizedString("modal_edit_device_name_text_field_hint", comment: "")) - placeholder.addAttribute(.foregroundColor, value: Colors.text.withAlphaComponent(Values.unimportantElementOpacity), range: NSRange(location: 0, length: placeholder.length)) - result.attributedPlaceholder = placeholder - result.tintColor = Colors.accent - result.keyboardAppearance = .dark - return result - }() - - // MARK: Lifecycle - override func viewDidLoad() { - super.viewDidLoad() - let notificationCenter = NotificationCenter.default - notificationCenter.addObserver(self, selector: #selector(handleKeyboardWillChangeFrameNotification(_:)), name: UIResponder.keyboardWillChangeFrameNotification, object: nil) - notificationCenter.addObserver(self, selector: #selector(handleKeyboardWillHideNotification(_:)), name: UIResponder.keyboardWillHideNotification, object: nil) - } - - override func populateContentView() { - // Set up title label - let titleLabel = UILabel() - titleLabel.textColor = Colors.text - titleLabel.font = .boldSystemFont(ofSize: Values.mediumFontSize) - titleLabel.text = "Change Device Name" - titleLabel.numberOfLines = 0 - titleLabel.lineBreakMode = .byWordWrapping - titleLabel.textAlignment = .center - // Set up explanation label - let explanationLabel = UILabel() - explanationLabel.textColor = Colors.text - explanationLabel.font = .systemFont(ofSize: Values.smallFontSize) - explanationLabel.text = "Enter the new display name for your device below" - explanationLabel.numberOfLines = 0 - explanationLabel.textAlignment = .center - explanationLabel.lineBreakMode = .byWordWrapping - // Set up OK button - let okButton = UIButton() - okButton.set(.height, to: Values.mediumButtonHeight) - okButton.layer.cornerRadius = Values.modalButtonCornerRadius - okButton.backgroundColor = Colors.accent - okButton.titleLabel!.font = .systemFont(ofSize: Values.smallFontSize) - okButton.setTitleColor(Colors.text, for: UIControl.State.normal) - okButton.setTitle(NSLocalizedString("OK", comment: ""), for: UIControl.State.normal) - okButton.addTarget(self, action: #selector(changeName), for: UIControl.Event.touchUpInside) - // Set up button stack view - let buttonStackView = UIStackView(arrangedSubviews: [ cancelButton, okButton ]) - buttonStackView.axis = .horizontal - buttonStackView.spacing = Values.mediumSpacing - buttonStackView.distribution = .fillEqually - // Set up main stack view - let stackView = UIStackView(arrangedSubviews: [ titleLabel, explanationLabel, nameTextField, buttonStackView ]) - stackView.axis = .vertical - stackView.spacing = Values.largeSpacing - contentView.addSubview(stackView) - stackView.pin(.leading, to: .leading, of: contentView, withInset: Values.largeSpacing) - stackView.pin(.top, to: .top, of: contentView, withInset: Values.largeSpacing) - contentView.pin(.trailing, to: .trailing, of: stackView, withInset: Values.largeSpacing) - contentView.pin(.bottom, to: .bottom, of: stackView, withInset: Values.largeSpacing) - } - - deinit { - NotificationCenter.default.removeObserver(self) - } - - // MARK: Updating - @objc private func handleKeyboardWillChangeFrameNotification(_ notification: Notification) { - guard let newHeight = (notification.userInfo?[UIResponder.keyboardFrameEndUserInfoKey] as? NSValue)?.cgRectValue.size.height else { return } - verticalCenteringConstraint.constant = -(newHeight / 2) - UIView.animate(withDuration: 0.25) { - self.view.layoutIfNeeded() - } - } - - @objc private func handleKeyboardWillHideNotification(_ notification: Notification) { - verticalCenteringConstraint.constant = 0 - UIView.animate(withDuration: 0.25) { - self.view.layoutIfNeeded() - } - } - - // MARK: Interaction - @objc private func changeName() { - let name = nameTextField.text!.trimmingCharacters(in: CharacterSet.whitespacesAndNewlines) - if !name.isEmpty { - UserDefaults.standard[.slaveDeviceName(device.publicKey)] = name - delegate?.handleDeviceNameChanged(to: name, for: device) - } else { - let alert = UIAlertController(title: "Error", message: "Please pick a name", preferredStyle: .alert) - alert.addAction(UIAlertAction(title: NSLocalizedString("OK", comment: ""), accessibilityIdentifier: nil, style: .default, handler: nil)) - present(alert, animated: true, completion: nil) - } - } -} diff --git a/Session/View Controllers/DeviceNameModalDelegate.swift b/Session/View Controllers/DeviceNameModalDelegate.swift deleted file mode 100644 index ad67f8237..000000000 --- a/Session/View Controllers/DeviceNameModalDelegate.swift +++ /dev/null @@ -1,5 +0,0 @@ - -@objc protocol DeviceNameModalDelegate { - - func handleDeviceNameChanged(to name: String, for device: DeviceLink.Device) -} diff --git a/Session/View Controllers/HomeVC.swift b/Session/View Controllers/HomeVC.swift index 37cd7d0bc..faa2f36e2 100644 --- a/Session/View Controllers/HomeVC.swift +++ b/Session/View Controllers/HomeVC.swift @@ -96,8 +96,7 @@ final class HomeVC : BaseVC, UITableViewDataSource, UITableViewDelegate, UIScrol // Set up seed reminder view if needed let userDefaults = UserDefaults.standard let hasViewedSeed = userDefaults[.hasViewedSeed] - let isMasterDevice = userDefaults.isMasterDevice - if !hasViewedSeed && isMasterDevice { + if !hasViewedSeed { view.addSubview(seedReminderView) seedReminderView.pin(.leading, to: .leading, of: view) seedReminderView.pin(.top, to: .top, of: view) @@ -108,7 +107,7 @@ final class HomeVC : BaseVC, UITableViewDataSource, UITableViewDelegate, UIScrol tableView.delegate = self view.addSubview(tableView) tableView.pin(.leading, to: .leading, of: view) - if !hasViewedSeed && isMasterDevice { + if !hasViewedSeed { tableViewTopConstraint = tableView.pin(.top, to: .bottom, of: seedReminderView) } else { tableViewTopConstraint = tableView.pin(.top, to: .top, of: view, withInset: Values.smallSpacing) @@ -283,13 +282,7 @@ final class HomeVC : BaseVC, UITableViewDataSource, UITableViewDelegate, UIScrol let profilePictureSize = Values.verySmallProfilePictureSize let profilePictureView = ProfilePictureView() profilePictureView.size = profilePictureSize - let userHexEncodedPublicKey: String - if let masterHexEncodedPublicKey = UserDefaults.standard[.masterHexEncodedPublicKey] { - userHexEncodedPublicKey = masterHexEncodedPublicKey - } else { - userHexEncodedPublicKey = getUserHexEncodedPublicKey() - } - profilePictureView.hexEncodedPublicKey = userHexEncodedPublicKey + profilePictureView.hexEncodedPublicKey = getUserHexEncodedPublicKey() profilePictureView.update() profilePictureView.set(.width, to: profilePictureSize) profilePictureView.set(.height, to: profilePictureSize) @@ -413,10 +406,7 @@ final class HomeVC : BaseVC, UITableViewDataSource, UITableViewDelegate, UIScrol } delete.backgroundColor = Colors.destructive if thread is TSContactThread { - var publicKey: String! - Storage.read { transaction in - publicKey = OWSPrimaryStorage.shared().getMasterHexEncodedPublicKey(for: thread.contactIdentifier()!, in: transaction) ?? thread.contactIdentifier()! - } + let publicKey = thread.contactIdentifier()! let blockingManager = SSKEnvironment.shared.blockingManager let isBlocked = blockingManager.isRecipientIdBlocked(publicKey) let block = UITableViewRowAction(style: .normal, title: NSLocalizedString("BLOCK_LIST_BLOCK_BUTTON", comment: "")) { _, _ in diff --git a/Session/View Controllers/JoinPublicChatVC.swift b/Session/View Controllers/JoinPublicChatVC.swift index a816986d8..6697054f8 100644 --- a/Session/View Controllers/JoinPublicChatVC.swift +++ b/Session/View Controllers/JoinPublicChatVC.swift @@ -133,7 +133,7 @@ final class JoinPublicChatVC : BaseVC, UIPageViewControllerDataSource, UIPageVie isJoining = true let channelID: UInt64 = 1 let urlAsString = url.absoluteString - let userPublicKey = UserDefaults.standard[.masterHexEncodedPublicKey] ?? getUserHexEncodedPublicKey() + let userPublicKey = getUserHexEncodedPublicKey() let profileManager = OWSProfileManager.shared() let displayName = profileManager.profileNameForRecipient(withID: userPublicKey) let profilePictureURL = profileManager.profilePictureURL() @@ -148,8 +148,6 @@ final class JoinPublicChatVC : BaseVC, UIPageViewControllerDataSource, UIPageVie let _ = OpenGroupAPI.setDisplayName(to: displayName, on: urlAsString) let _ = OpenGroupAPI.setProfilePictureURL(to: profilePictureURL, using: profileKey, on: urlAsString) let _ = OpenGroupAPI.join(channelID, on: urlAsString) - let syncManager = SSKEnvironment.shared.syncManager - let _ = syncManager.syncAllOpenGroups() self?.presentingViewController!.dismiss(animated: true, completion: nil) } .catch(on: DispatchQueue.main) { [weak self] error in diff --git a/Session/View Controllers/LandingVC.swift b/Session/View Controllers/LandingVC.swift index f7f2a52c7..5bb68763d 100644 --- a/Session/View Controllers/LandingVC.swift +++ b/Session/View Controllers/LandingVC.swift @@ -1,5 +1,5 @@ -final class LandingVC : BaseVC, LinkDeviceVCDelegate, DeviceLinkingModalDelegate { +final class LandingVC : BaseVC { private var fakeChatViewContentOffset: CGPoint! // MARK: Components @@ -25,14 +25,6 @@ final class LandingVC : BaseVC, LinkDeviceVCDelegate, DeviceLinkingModalDelegate return result }() - private lazy var linkButton: Button = { - let result = Button(style: .regularBorderless, size: .small) - result.setTitle(NSLocalizedString("vc_landing_link_button_title", comment: ""), for: UIControl.State.normal) - result.titleLabel!.font = .systemFont(ofSize: Values.smallFontSize) - result.addTarget(self, action: #selector(linkDevice), for: UIControl.Event.touchUpInside) - return result - }() - // MARK: Lifecycle override func viewDidLoad() { super.viewDidLoad() @@ -59,11 +51,6 @@ final class LandingVC : BaseVC, LinkDeviceVCDelegate, DeviceLinkingModalDelegate // Set up link button container let linkButtonContainer = UIView() linkButtonContainer.set(.height, to: Values.onboardingButtonBottomOffset) -// linkButtonContainer.addSubview(linkButton) -// linkButton.pin(.leading, to: .leading, of: linkButtonContainer, withInset: Values.massiveSpacing) -// linkButton.pin(.top, to: .top, of: linkButtonContainer) -// linkButtonContainer.pin(.trailing, to: .trailing, of: linkButton, withInset: Values.massiveSpacing) -// linkButtonContainer.pin(.bottom, to: .bottom, of: linkButton, withInset: isIPhone5OrSmaller ? 6 : 10) // Set up button stack view let buttonStackView = UIStackView(arrangedSubviews: [ registerButton, restoreButton ]) buttonStackView.axis = .vertical @@ -83,13 +70,6 @@ final class LandingVC : BaseVC, LinkDeviceVCDelegate, DeviceLinkingModalDelegate view.addSubview(mainStackView) mainStackView.pin(to: view) topSpacer.heightAnchor.constraint(equalTo: bottomSpacer.heightAnchor, multiplier: 1).isActive = true - // Show device unlinked alert if needed - if UserDefaults.standard[.wasUnlinked] { - let alert = UIAlertController(title: "Device Unlinked", message: NSLocalizedString("vc_landing_device_unlinked_modal_title", comment: ""), preferredStyle: .alert) - alert.addAction(UIAlertAction(title: NSLocalizedString("OK", comment: ""), accessibilityIdentifier: nil, style: .default, handler: nil)) - present(alert, animated: true, completion: nil) - UserDefaults.removeAll() - } } override func viewDidDisappear(_ animated: Bool) { @@ -118,58 +98,9 @@ final class LandingVC : BaseVC, LinkDeviceVCDelegate, DeviceLinkingModalDelegate navigationController!.pushViewController(restoreVC, animated: true) } - @objc private func linkDevice() { - let linkDeviceVC = LinkDeviceVC() - linkDeviceVC.delegate = self - let navigationController = OWSNavigationController(rootViewController: linkDeviceVC) - present(navigationController, animated: true, completion: nil) - } - - // MARK: Device Linking - func requestDeviceLink(with hexEncodedPublicKey: String) { - guard ECKeyPair.isValidHexEncodedPublicKey(candidate: hexEncodedPublicKey) else { - let alert = UIAlertController(title: NSLocalizedString("invalid_session_id", comment: ""), message: "Please make sure the Session ID you entered is correct and try again.", preferredStyle: .alert) - alert.addAction(UIAlertAction(title: NSLocalizedString("OK", comment: ""), accessibilityIdentifier: nil, style: .default, handler: nil)) - return present(alert, animated: true, completion: nil) - } - let seed = Randomness.generateRandomBytes(16) - preconditionFailure("This code path shouldn't be invoked.") - let keyPair = Curve25519.generateKeyPair() - let identityManager = OWSIdentityManager.shared() - let databaseConnection = identityManager.value(forKey: "dbConnection") as! YapDatabaseConnection - databaseConnection.setObject(seed.toHexString(), forKey: "LKLokiSeed", inCollection: OWSPrimaryStorageIdentityKeyStoreCollection) - databaseConnection.setObject(keyPair, forKey: OWSPrimaryStorageIdentityKeyStoreIdentityKey, inCollection: OWSPrimaryStorageIdentityKeyStoreCollection) - TSAccountManager.sharedInstance().phoneNumberAwaitingVerification = keyPair.hexEncodedPublicKey - TSAccountManager.sharedInstance().didRegister() - let appDelegate = UIApplication.shared.delegate as! AppDelegate - appDelegate.startPollerIfNeeded() - let deviceLinkingModal = DeviceLinkingModal(mode: .slave, delegate: self) - deviceLinkingModal.modalPresentationStyle = .overFullScreen - deviceLinkingModal.modalTransitionStyle = .crossDissolve - self.present(deviceLinkingModal, animated: true, completion: nil) - let linkingRequestMessage = DeviceLinkingUtilities.getLinkingRequestMessage(for: hexEncodedPublicKey) - ThreadUtil.enqueue(linkingRequestMessage) - } - - func handleDeviceLinkAuthorized(_ deviceLink: DeviceLink) { - UserDefaults.standard[.masterHexEncodedPublicKey] = deviceLink.master.publicKey - fakeChatViewContentOffset = fakeChatView.contentOffset - DispatchQueue.main.async { - self.fakeChatView.contentOffset = self.fakeChatViewContentOffset - } - let homeVC = HomeVC() - navigationController!.setViewControllers([ homeVC ], animated: true) - } - - func handleDeviceLinkingModalDismissed() { - let appDelegate = UIApplication.shared.delegate as! AppDelegate - appDelegate.stopPoller() - TSAccountManager.sharedInstance().resetForReregistration() - } - // MARK: Convenience private func setUserInteractionEnabled(_ isEnabled: Bool) { - [ registerButton, restoreButton, linkButton ].forEach { + [ registerButton, restoreButton ].forEach { $0.isUserInteractionEnabled = isEnabled } } diff --git a/Session/View Controllers/NewClosedGroupVC.swift b/Session/View Controllers/NewClosedGroupVC.swift index b550b1edd..027b85f06 100644 --- a/Session/View Controllers/NewClosedGroupVC.swift +++ b/Session/View Controllers/NewClosedGroupVC.swift @@ -149,14 +149,6 @@ final class NewClosedGroupVC : BaseVC, UITableViewDataSource, UITableViewDelegat } @objc private func createClosedGroup() { - if ClosedGroupsProtocol.isSharedSenderKeysEnabled { - createSSKClosedGroup() - } else { - createLegacyClosedGroup() - } - } - - private func createSSKClosedGroup() { func showError(title: String, message: String = "") { let alert = UIAlertController(title: title, message: message, preferredStyle: .alert) alert.addAction(UIAlertAction(title: NSLocalizedString("OK", comment: ""), style: .default, handler: nil)) @@ -176,16 +168,15 @@ final class NewClosedGroupVC : BaseVC, UITableViewDataSource, UITableViewDelegat } let selectedContacts = self.selectedContacts ModalActivityIndicatorViewController.present(fromViewController: navigationController!, canCancel: false) { [weak self] _ in - FileServerAPI.getDeviceLinks(associatedWith: selectedContacts).then2 { _ -> Promise in - var promise: Promise! - Storage.writeSync { transaction in - promise = ClosedGroupsProtocol.createClosedGroup(name: name, members: selectedContacts, transaction: transaction) - } - return promise - }.done(on: DispatchQueue.main) { thread in + var promise: Promise! + Storage.writeSync { transaction in + promise = ClosedGroupsProtocol.createClosedGroup(name: name, members: selectedContacts, transaction: transaction) + } + let _ = promise.done(on: DispatchQueue.main) { thread in self?.presentingViewController?.dismiss(animated: true, completion: nil) SignalApp.shared().presentConversation(for: thread, action: .compose, animated: false) - }.catch(on: DispatchQueue.main) { _ in + } + promise.catch(on: DispatchQueue.main) { _ in self?.dismiss(animated: true, completion: nil) // Dismiss the loader let title = "Couldn't Create Group" let message = "Please check your internet connection and try again." @@ -195,57 +186,6 @@ final class NewClosedGroupVC : BaseVC, UITableViewDataSource, UITableViewDelegat } } } - - private func createLegacyClosedGroup() { - func showError(title: String, message: String = "") { - let alert = UIAlertController(title: title, message: message, preferredStyle: .alert) - alert.addAction(UIAlertAction(title: NSLocalizedString("OK", comment: ""), style: .default, handler: nil)) - presentAlert(alert) - } - guard let name = nameTextField.text?.trimmingCharacters(in: .whitespacesAndNewlines), name.count > 0 else { - return showError(title: NSLocalizedString("vc_create_closed_group_group_name_missing_error", comment: "")) - } - guard name.count < 64 else { - return showError(title: NSLocalizedString("vc_create_closed_group_group_name_too_long_error", comment: "")) - } - guard selectedContacts.count >= 1 else { - return showError(title: "Please pick at least 1 group member") - } - guard selectedContacts.count < 10 else { // Minus one because we're going to include self later - return showError(title: NSLocalizedString("vc_create_closed_group_too_many_group_members_error", comment: "")) - } - let userPublicKey = getUserHexEncodedPublicKey() - let storage = OWSPrimaryStorage.shared() - var masterPublicKey = "" - storage.dbReadConnection.read { transaction in - masterPublicKey = storage.getMasterHexEncodedPublicKey(for: userPublicKey, in: transaction) ?? userPublicKey - } - let members = selectedContacts + [ masterPublicKey ] - let admins = [ masterPublicKey ] - let groupID = LKGroupUtilities.getEncodedClosedGroupIDAsData(Randomness.generateRandomBytes(kGroupIdLength).toHexString()) - let group = TSGroupModel(title: name, memberIds: members, image: nil, groupId: groupID, groupType: .closedGroup, adminIds: admins) - let thread = TSGroupThread.getOrCreateThread(with: group) - OWSProfileManager.shared().addThread(toProfileWhitelist: thread) - ModalActivityIndicatorViewController.present(fromViewController: navigationController!, canCancel: false) { [weak self] modalActivityIndicator in - let message = TSOutgoingMessage(in: thread, groupMetaMessage: .new, expiresInSeconds: 0) - message.update(withCustomMessage: "Closed group created") - DispatchQueue.main.async { - SSKEnvironment.shared.messageSender.send(message, success: { - DispatchQueue.main.async { - self?.presentingViewController?.dismiss(animated: true, completion: nil) - SignalApp.shared().presentConversation(for: thread, action: .compose, animated: false) - } - }, failure: { error in - let message = TSErrorMessage(timestamp: NSDate.ows_millisecondTimeStamp(), in: thread, failedMessageType: .groupCreationFailed) - message.save() - DispatchQueue.main.async { - self?.presentingViewController?.dismiss(animated: true, completion: nil) - SignalApp.shared().presentConversation(for: thread, action: .compose, animated: false) - } - }) - } - } - } @objc private func createNewPrivateChat() { presentingViewController?.dismiss(animated: true, completion: nil) diff --git a/Session/View Controllers/NewPrivateChatVC.swift b/Session/View Controllers/NewPrivateChatVC.swift index 672d1c7da..9e0c46beb 100644 --- a/Session/View Controllers/NewPrivateChatVC.swift +++ b/Session/View Controllers/NewPrivateChatVC.swift @@ -147,14 +147,6 @@ final class NewPrivateChatVC : BaseVC, UIPageViewControllerDataSource, UIPageVie private final class EnterPublicKeyVC : UIViewController { weak var newPrivateChatVC: NewPrivateChatVC! - private lazy var userPublicKey: String = { - if let masterHexEncodedPublicKey = UserDefaults.standard[.masterHexEncodedPublicKey] { - return masterHexEncodedPublicKey - } else { - return getUserHexEncodedPublicKey() - } - }() - // MARK: Components private let publicKeyTextView = TextView(placeholder: NSLocalizedString("vc_enter_public_key_text_field_hint", comment: "")) @@ -186,7 +178,7 @@ private final class EnterPublicKeyVC : UIViewController { userPublicKeyLabel.numberOfLines = 0 userPublicKeyLabel.textAlignment = .center userPublicKeyLabel.lineBreakMode = .byCharWrapping - userPublicKeyLabel.text = userPublicKey + userPublicKeyLabel.text = getUserHexEncodedPublicKey() // Set up share button let shareButton = Button(style: .unimportant, size: .medium) shareButton.setTitle(NSLocalizedString("share", comment: ""), for: UIControl.State.normal) @@ -239,7 +231,7 @@ private final class EnterPublicKeyVC : UIViewController { // MARK: Interaction @objc private func copyPublicKey() { - UIPasteboard.general.string = userPublicKey + UIPasteboard.general.string = getUserHexEncodedPublicKey() copyButton.isUserInteractionEnabled = false UIView.transition(with: copyButton, duration: 0.25, options: .transitionCrossDissolve, animations: { self.copyButton.setTitle("Copied", for: UIControl.State.normal) @@ -248,7 +240,7 @@ private final class EnterPublicKeyVC : UIViewController { } @objc private func sharePublicKey() { - let shareVC = UIActivityViewController(activityItems: [ userPublicKey ], applicationActivities: nil) + let shareVC = UIActivityViewController(activityItems: [ getUserHexEncodedPublicKey() ], applicationActivities: nil) newPrivateChatVC.navigationController!.present(shareVC, animated: true, completion: nil) } diff --git a/Session/View Controllers/QRCodeVC.swift b/Session/View Controllers/QRCodeVC.swift index 6f347595a..3a24506bb 100644 --- a/Session/View Controllers/QRCodeVC.swift +++ b/Session/View Controllers/QRCodeVC.swift @@ -137,14 +137,6 @@ private final class ViewMyQRCodeVC : UIViewController { weak var qrCodeVC: QRCodeVC! private var bottomConstraint: NSLayoutConstraint! - private lazy var userHexEncodedPublicKey: String = { - if let masterHexEncodedPublicKey = UserDefaults.standard[.masterHexEncodedPublicKey] { - return masterHexEncodedPublicKey - } else { - return getUserHexEncodedPublicKey() - } - }() - // MARK: Lifecycle override func viewDidLoad() { // Remove background color @@ -160,7 +152,7 @@ private final class ViewMyQRCodeVC : UIViewController { titleLabel.set(.height, to: isIPhone5OrSmaller ? CGFloat(40) : Values.massiveFontSize) // Set up QR code image view let qrCodeImageView = UIImageView() - let qrCode = QRCode.generate(for: userHexEncodedPublicKey, hasBackground: true) + let qrCode = QRCode.generate(for: getUserHexEncodedPublicKey(), hasBackground: true) qrCodeImageView.image = qrCode qrCodeImageView.contentMode = .scaleAspectFit qrCodeImageView.set(.height, to: isIPhone5OrSmaller ? 180 : 240) @@ -218,7 +210,7 @@ private final class ViewMyQRCodeVC : UIViewController { // MARK: Interaction @objc private func shareQRCode() { - let qrCode = QRCode.generate(for: userHexEncodedPublicKey, hasBackground: true) + let qrCode = QRCode.generate(for: getUserHexEncodedPublicKey(), hasBackground: true) let shareVC = UIActivityViewController(activityItems: [ qrCode ], applicationActivities: nil) qrCodeVC.navigationController!.present(shareVC, animated: true, completion: nil) } diff --git a/Session/View Controllers/SettingsVC.swift b/Session/View Controllers/SettingsVC.swift index 35099394b..615645038 100644 --- a/Session/View Controllers/SettingsVC.swift +++ b/Session/View Controllers/SettingsVC.swift @@ -4,14 +4,6 @@ final class SettingsVC : BaseVC, AvatarViewHelperDelegate { private var displayNameToBeUploaded: String? private var isEditingDisplayName = false { didSet { handleIsEditingDisplayNameChanged() } } - private lazy var userHexEncodedPublicKey: String = { - if let masterHexEncodedPublicKey = UserDefaults.standard[.masterHexEncodedPublicKey] { - return masterHexEncodedPublicKey - } else { - return getUserHexEncodedPublicKey() - } - }() - // MARK: Components private lazy var profilePictureView: ProfilePictureView = { let result = ProfilePictureView() @@ -71,10 +63,10 @@ final class SettingsVC : BaseVC, AvatarViewHelperDelegate { // Set up profile picture view let profilePictureTapGestureRecognizer = UITapGestureRecognizer(target: self, action: #selector(showEditProfilePictureUI)) profilePictureView.addGestureRecognizer(profilePictureTapGestureRecognizer) - profilePictureView.hexEncodedPublicKey = userHexEncodedPublicKey + profilePictureView.hexEncodedPublicKey = getUserHexEncodedPublicKey() profilePictureView.update() // Set up display name label - displayNameLabel.text = OWSProfileManager.shared().profileNameForRecipient(withID: userHexEncodedPublicKey) + displayNameLabel.text = OWSProfileManager.shared().profileNameForRecipient(withID: getUserHexEncodedPublicKey()) // Set up display name container let displayNameContainer = UIView() displayNameContainer.addSubview(displayNameLabel) @@ -99,7 +91,7 @@ final class SettingsVC : BaseVC, AvatarViewHelperDelegate { publicKeyLabel.numberOfLines = 0 publicKeyLabel.textAlignment = .center publicKeyLabel.lineBreakMode = .byCharWrapping - publicKeyLabel.text = userHexEncodedPublicKey + publicKeyLabel.text = getUserHexEncodedPublicKey() // Set up share button let shareButton = Button(style: .regular, size: .medium) shareButton.setTitle(NSLocalizedString("share", comment: ""), for: UIControl.State.normal) @@ -178,23 +170,19 @@ final class SettingsVC : BaseVC, AvatarViewHelperDelegate { button.set(.height, to: Values.settingButtonHeight) return button } - var result = [ + return [ getSeparator(), getSettingButton(withTitle: NSLocalizedString("vc_settings_privacy_button_title", comment: ""), color: Colors.text, action: #selector(showPrivacySettings)), getSeparator(), getSettingButton(withTitle: NSLocalizedString("vc_settings_notifications_button_title", comment: ""), color: Colors.text, action: #selector(showNotificationSettings)), getSeparator(), - getSettingButton(withTitle: "Invite", color: Colors.text, action: #selector(sendInvitation)) + getSettingButton(withTitle: "Invite", color: Colors.text, action: #selector(sendInvitation)), + getSeparator(), + getSettingButton(withTitle: NSLocalizedString("vc_settings_recovery_phrase_button_title", comment: ""), color: Colors.text, action: #selector(showSeed)), + getSeparator(), + getSettingButton(withTitle: NSLocalizedString("vc_settings_clear_all_data_button_title", comment: ""), color: Colors.destructive, action: #selector(clearAllData)), + getSeparator() ] - let isMasterDevice = UserDefaults.standard.isMasterDevice - if isMasterDevice { - result.append(getSeparator()) - result.append(getSettingButton(withTitle: NSLocalizedString("vc_settings_recovery_phrase_button_title", comment: ""), color: Colors.text, action: #selector(showSeed))) - } - result.append(getSeparator()) - result.append(getSettingButton(withTitle: NSLocalizedString("vc_settings_clear_all_data_button_title", comment: ""), color: Colors.destructive, action: #selector(clearAllData))) - result.append(getSeparator()) - return result } // MARK: General @@ -283,8 +271,8 @@ final class SettingsVC : BaseVC, AvatarViewHelperDelegate { } private func updateProfile(isUpdatingDisplayName: Bool, isUpdatingProfilePicture: Bool) { - let displayName = displayNameToBeUploaded ?? OWSProfileManager.shared().profileNameForRecipient(withID: userHexEncodedPublicKey) - let profilePicture = profilePictureToBeUploaded ?? OWSProfileManager.shared().profileAvatar(forRecipientId: userHexEncodedPublicKey) + let displayName = displayNameToBeUploaded ?? OWSProfileManager.shared().profileNameForRecipient(withID: getUserHexEncodedPublicKey()) + let profilePicture = profilePictureToBeUploaded ?? OWSProfileManager.shared().profileAvatar(forRecipientId: getUserHexEncodedPublicKey()) ModalActivityIndicatorViewController.present(fromViewController: navigationController!, canCancel: false) { [weak self] modalActivityIndicator in OWSProfileManager.shared().updateLocalProfileName(displayName, avatarImage: profilePicture, success: { DispatchQueue.main.async { @@ -372,7 +360,7 @@ final class SettingsVC : BaseVC, AvatarViewHelperDelegate { } @objc private func copyPublicKey() { - UIPasteboard.general.string = userHexEncodedPublicKey + UIPasteboard.general.string = getUserHexEncodedPublicKey() copyButton.isUserInteractionEnabled = false UIView.transition(with: copyButton, duration: 0.25, options: .transitionCrossDissolve, animations: { self.copyButton.setTitle("Copied", for: UIControl.State.normal) @@ -381,7 +369,7 @@ final class SettingsVC : BaseVC, AvatarViewHelperDelegate { } @objc private func sharePublicKey() { - let shareVC = UIActivityViewController(activityItems: [ userHexEncodedPublicKey ], applicationActivities: nil) + let shareVC = UIActivityViewController(activityItems: [ getUserHexEncodedPublicKey() ], applicationActivities: nil) navigationController!.present(shareVC, animated: true, completion: nil) } @@ -394,14 +382,9 @@ final class SettingsVC : BaseVC, AvatarViewHelperDelegate { let notificationSettingsVC = NotificationSettingsViewController() navigationController!.pushViewController(notificationSettingsVC, animated: true) } - - @objc private func showLinkedDevices() { - let deviceLinksVC = DeviceLinksVC() - navigationController!.pushViewController(deviceLinksVC, 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 \(userHexEncodedPublicKey)!" + 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) navigationController!.present(shareVC, animated: true, completion: nil) } diff --git a/SessionMessagingKit/Sending & Receiving/Notification+MessageSender.swift b/SessionMessagingKit/Sending & Receiving/Notification+MessageSender.swift index b5f9d68b2..0f59dee57 100644 --- a/SessionMessagingKit/Sending & Receiving/Notification+MessageSender.swift +++ b/SessionMessagingKit/Sending & Receiving/Notification+MessageSender.swift @@ -7,3 +7,12 @@ public extension Notification.Name { static let messageSent = Notification.Name("messageSent") static let messageSendingFailed = Notification.Name("messageSendingFailed") } + +@objc public extension NSNotification { + + @objc static let encryptingMessage = Notification.Name.encryptingMessage.rawValue as NSString + @objc static let calculatingMessagePoW = Notification.Name.calculatingMessagePoW.rawValue as NSString + @objc static let messageSending = Notification.Name.messageSending.rawValue as NSString + @objc static let messageSent = Notification.Name.messageSent.rawValue as NSString + @objc static let messageSendingFailed = Notification.Name.messageSendingFailed.rawValue as NSString +} diff --git a/SessionProtocolKit/Shared Sender Keys/SharedSenderKeys.swift b/SessionProtocolKit/Shared Sender Keys/SharedSenderKeys.swift index 59725a6f7..653d24930 100644 --- a/SessionProtocolKit/Shared Sender Keys/SharedSenderKeys.swift +++ b/SessionProtocolKit/Shared Sender Keys/SharedSenderKeys.swift @@ -27,7 +27,7 @@ public enum SharedSenderKeys { } // MARK: Private/Internal API - internal static func generateRatchet(for groupPublicKey: String, senderPublicKey: String, using transaction: Any) -> ClosedGroupRatchet { + public static func generateRatchet(for groupPublicKey: String, senderPublicKey: String, using transaction: Any) -> ClosedGroupRatchet { let rootChainKey = Data.getSecureRandomData(ofSize: 32)!.toHexString() let ratchet = ClosedGroupRatchet(chainKey: rootChainKey, keyIndex: 0, messageKeys: []) Configuration.shared.storage.setClosedGroupRatchet(for: groupPublicKey, senderPublicKey: senderPublicKey, ratchet: ratchet, in: .current, using: transaction) diff --git a/SessionProtocolKit/Storage.swift b/SessionProtocolKit/Storage.swift index f684c90dc..b61c5bdb1 100644 --- a/SessionProtocolKit/Storage.swift +++ b/SessionProtocolKit/Storage.swift @@ -7,6 +7,7 @@ public protocol SessionProtocolKitStorageProtocol { func with(_ work: @escaping (Any) -> Void) + func getUserKeyPair() -> ECKeyPair? func getClosedGroupRatchet(for groupPublicKey: String, senderPublicKey: String, from collection: ClosedGroupRatchetCollectionType) -> ClosedGroupRatchet? func setClosedGroupRatchet(for groupPublicKey: String, senderPublicKey: String, ratchet: ClosedGroupRatchet, in collection: ClosedGroupRatchetCollectionType, using transaction: Any) } diff --git a/SessionPushNotificationExtension/NotificationServiceExtension.swift b/SessionPushNotificationExtension/NotificationServiceExtension.swift index b9ceead2e..55ae73836 100644 --- a/SessionPushNotificationExtension/NotificationServiceExtension.swift +++ b/SessionPushNotificationExtension/NotificationServiceExtension.swift @@ -13,7 +13,7 @@ final class NotificationServiceExtension : UNNotificationServiceExtension { override func didReceive(_ request: UNNotificationRequest, withContentHandler contentHandler: @escaping (UNNotificationContent) -> Void) { self.contentHandler = contentHandler - notificationContent = (request.content.mutableCopy() as? UNMutableNotificationContent) + notificationContent = request.content.mutableCopy() as? UNMutableNotificationContent var isMainAppActive = false if let sharedUserDefaults = UserDefaults(suiteName: "group.com.loki-project.loki-messenger") { @@ -31,10 +31,10 @@ final class NotificationServiceExtension : UNNotificationServiceExtension { // Modify the notification content here... let base64EncodedData = notificationContent.userInfo["ENCRYPTED_DATA"] as! String let data = Data(base64Encoded: base64EncodedData)! - let decrypter = SSKEnvironment.shared.messageDecrypter - let messageManager = SSKEnvironment.shared.messageManager if let envelope = try? MessageWrapper.unwrap(data: data), let data = try? envelope.serializedData() { - let wasReceivedByUD = self.wasReceivedByUD(envelope: envelope) + // TODO TODO TODO + + /* decrypter.decryptEnvelope(envelope, envelopeData: data, successBlock: { result, transaction in @@ -49,6 +49,7 @@ final class NotificationServiceExtension : UNNotificationServiceExtension { self.completeWithFailure(content: notificationContent) } ) + */ } else { self.completeWithFailure(content: notificationContent) } @@ -56,6 +57,7 @@ final class NotificationServiceExtension : UNNotificationServiceExtension { } } + /* func handleDecryptionResult(result: OWSMessageDecryptResult, notificationContent: UNMutableNotificationContent, transaction: YapDatabaseReadWriteTransaction) { let contentProto = try? SSKProtoContent.parseData(result.plaintextData!) var thread: TSThread @@ -131,6 +133,7 @@ final class NotificationServiceExtension : UNNotificationServiceExtension { self.contentHandler!(notificationContent) } } + */ func handleMentionIfNecessary(rawMessageBody: String, threadID: String, transaction: YapDatabaseReadWriteTransaction) -> String { var string = rawMessageBody diff --git a/SessionShareExtension/Meta/SignalShareExtension-Bridging-Header.h b/SessionShareExtension/Meta/SignalShareExtension-Bridging-Header.h index 75494b4e4..cd60f743f 100644 --- a/SessionShareExtension/Meta/SignalShareExtension-Bridging-Header.h +++ b/SessionShareExtension/Meta/SignalShareExtension-Bridging-Header.h @@ -13,7 +13,7 @@ #import #import #import -#import + #import #import #import @@ -23,5 +23,4 @@ #import #import #import -#import #import diff --git a/SessionShareExtension/ShareViewController.swift b/SessionShareExtension/ShareViewController.swift index c2b757a9a..675680472 100644 --- a/SessionShareExtension/ShareViewController.swift +++ b/SessionShareExtension/ShareViewController.swift @@ -207,8 +207,6 @@ public class ShareViewController: UIViewController, ShareViewDelegate, SAEFailed // We don't need to use the TSSocketManager in the SAE. - Environment.shared.contactsManager.fetchSystemContactsOnceIfAlreadyAuthorized() - // We don't need to fetch messages in the SAE. // We don't need to use OWSSyncPushTokensJob in the SAE. @@ -793,22 +791,6 @@ public class ShareViewController: UIViewController, ShareViewDelegate, SAEFailed if let data = value as? Data { let customFileName = "Contact.vcf" - var isConvertibleToContactShare = false - - // Although we don't support contacts _yet_, when we do we'll want to make - // sure they are shared with a reasonable filename. - if ShareViewController.itemMatchesSpecificUtiType(itemProvider: itemProvider, - utiType: kUTTypeVCard as String) { - - if Contact(vCardData: data) != nil { - isConvertibleToContactShare = true - } else { - Logger.error("could not parse vcard.") - let writeError = ShareViewControllerError.assertionError(description: "Could not parse vcard data.") - resolver.reject(writeError) - return - } - } let customFileExtension = MIMETypeUtil.fileExtension(forUTIType: srcUtiType) guard let tempFilePath = OWSFileSystem.writeData(toTemporaryFile: data, fileExtension: customFileExtension) else { @@ -821,7 +803,7 @@ public class ShareViewController: UIViewController, ShareViewDelegate, SAEFailed itemUrl: fileUrl, utiType: srcUtiType, customFileName: customFileName, - isConvertibleToContactShare: isConvertibleToContactShare)) + isConvertibleToContactShare: false)) } else if let string = value as? String { Logger.debug("string provider: \(string)") guard let data = string.filterStringForDisplay().data(using: String.Encoding.utf8) else { diff --git a/SessionUtilitiesKit/AnyPromise+Retaining.swift b/SessionUtilitiesKit/AnyPromise+Retaining.swift deleted file mode 100644 index a561f6ce2..000000000 --- a/SessionUtilitiesKit/AnyPromise+Retaining.swift +++ /dev/null @@ -1,13 +0,0 @@ -import PromiseKit - -public extension AnyPromise { - - @objc - func retainUntilComplete() { - var retainCycle: AnyPromise? = self - _ = self.ensure { - assert(retainCycle != nil) - retainCycle = nil - } - } -} diff --git a/SessionUtilitiesKit/Promise+Retaining.swift b/SessionUtilitiesKit/Promise+Retaining.swift new file mode 100644 index 000000000..fc1ef5d90 --- /dev/null +++ b/SessionUtilitiesKit/Promise+Retaining.swift @@ -0,0 +1,46 @@ +import PromiseKit + +public extension AnyPromise { + + @objc + func retainUntilComplete() { + var retainCycle: AnyPromise? = self + _ = self.ensure { + assert(retainCycle != nil) + retainCycle = nil + } + } +} + +public extension PMKFinalizer { + + func retainUntilComplete() { + var retainCycle: PMKFinalizer? = self + self.finally { + assert(retainCycle != nil) + retainCycle = nil + } + } +} + +public extension Promise { + + func retainUntilComplete() { + var retainCycle: Promise? = self + _ = self.ensure { + assert(retainCycle != nil) + retainCycle = nil + } + } +} + +public extension Guarantee { + + func retainUntilComplete() { + var retainCycle: Guarantee? = self + _ = self.done { _ in + assert(retainCycle != nil) + retainCycle = nil + } + } +} diff --git a/Signal.xcodeproj/project.pbxproj b/Signal.xcodeproj/project.pbxproj index 1b030eda4..419ee0233 100644 --- a/Signal.xcodeproj/project.pbxproj +++ b/Signal.xcodeproj/project.pbxproj @@ -9,25 +9,16 @@ /* Begin PBXBuildFile section */ 10AC6C7D50A0C865C5E4779B /* Pods_SessionUIKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 71CFEDD2D3C54277731012DF /* Pods_SessionUIKit.framework */; }; 2400888E239F30A600305217 /* SessionRestorationView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2400888D239F30A600305217 /* SessionRestorationView.swift */; }; - 3403B95D20EA9527001A1F44 /* OWSContactShareButtonsView.m in Sources */ = {isa = PBXBuildFile; fileRef = 3403B95B20EA9526001A1F44 /* OWSContactShareButtonsView.m */; }; 340FC8A9204DAC8D007AEB0F /* NotificationSettingsOptionsViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 340FC87B204DAC8C007AEB0F /* NotificationSettingsOptionsViewController.m */; }; 340FC8AA204DAC8D007AEB0F /* NotificationSettingsViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 340FC87C204DAC8C007AEB0F /* NotificationSettingsViewController.m */; }; - 340FC8AB204DAC8D007AEB0F /* DomainFrontingCountryViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 340FC87D204DAC8C007AEB0F /* DomainFrontingCountryViewController.m */; }; 340FC8AC204DAC8D007AEB0F /* PrivacySettingsTableViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 340FC87E204DAC8C007AEB0F /* PrivacySettingsTableViewController.m */; }; 340FC8AE204DAC8D007AEB0F /* OWSSoundSettingsViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 340FC883204DAC8C007AEB0F /* OWSSoundSettingsViewController.m */; }; 340FC8B0204DAC8D007AEB0F /* AddToBlockListViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 340FC886204DAC8C007AEB0F /* AddToBlockListViewController.m */; }; - 340FC8B1204DAC8D007AEB0F /* BlockListViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 340FC887204DAC8C007AEB0F /* BlockListViewController.m */; }; - 340FC8B2204DAC8D007AEB0F /* AdvancedSettingsTableViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 340FC88C204DAC8C007AEB0F /* AdvancedSettingsTableViewController.m */; }; 340FC8B4204DAC8D007AEB0F /* OWSBackupSettingsViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 340FC88E204DAC8C007AEB0F /* OWSBackupSettingsViewController.m */; }; 340FC8B5204DAC8D007AEB0F /* AboutTableViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 340FC893204DAC8C007AEB0F /* AboutTableViewController.m */; }; 340FC8B6204DAC8D007AEB0F /* OWSQRCodeScanningViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 340FC896204DAC8C007AEB0F /* OWSQRCodeScanningViewController.m */; }; 340FC8B7204DAC8D007AEB0F /* OWSConversationSettingsViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 340FC89A204DAC8D007AEB0F /* OWSConversationSettingsViewController.m */; }; 340FC8B8204DAC8D007AEB0F /* AddToGroupViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 340FC89B204DAC8D007AEB0F /* AddToGroupViewController.m */; }; - 340FC8B9204DAC8D007AEB0F /* UpdateGroupViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 340FC89C204DAC8D007AEB0F /* UpdateGroupViewController.m */; }; - 340FC8BA204DAC8D007AEB0F /* FingerprintViewScanController.m in Sources */ = {isa = PBXBuildFile; fileRef = 340FC89F204DAC8D007AEB0F /* FingerprintViewScanController.m */; }; - 340FC8BB204DAC8D007AEB0F /* OWSAddToContactViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 340FC8A1204DAC8D007AEB0F /* OWSAddToContactViewController.m */; }; - 340FC8BC204DAC8D007AEB0F /* FingerprintViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 340FC8A2204DAC8D007AEB0F /* FingerprintViewController.m */; }; - 340FC8BD204DAC8D007AEB0F /* ShowGroupMembersViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 340FC8A6204DAC8D007AEB0F /* ShowGroupMembersViewController.m */; }; 34129B8621EF877A005457A8 /* LinkPreviewView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 34129B8521EF8779005457A8 /* LinkPreviewView.swift */; }; 341341EF2187467A00192D59 /* ConversationViewModel.m in Sources */ = {isa = PBXBuildFile; fileRef = 341341EE2187467900192D59 /* ConversationViewModel.m */; }; 34277A5E20751BDC006049F2 /* OWSQuotedMessageView.m in Sources */ = {isa = PBXBuildFile; fileRef = 34277A5C20751BDC006049F2 /* OWSQuotedMessageView.m */; }; @@ -57,7 +48,6 @@ 347850571FD86544007B8332 /* SAEFailedViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 347850561FD86544007B8332 /* SAEFailedViewController.swift */; }; 348570A820F67575004FF32B /* OWSMessageHeaderView.m in Sources */ = {isa = PBXBuildFile; fileRef = 348570A620F67574004FF32B /* OWSMessageHeaderView.m */; }; 3488F9362191CC4000E524CC /* ConversationMediaView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3488F9352191CC4000E524CC /* ConversationMediaView.swift */; }; - 348BB25D20A0C5530047AEC2 /* ContactShareViewHelper.swift in Sources */ = {isa = PBXBuildFile; fileRef = 348BB25C20A0C5530047AEC2 /* ContactShareViewHelper.swift */; }; 3496744D2076768700080B5F /* OWSMessageBubbleView.m in Sources */ = {isa = PBXBuildFile; fileRef = 3496744C2076768700080B5F /* OWSMessageBubbleView.m */; }; 3496744F2076ACD000080B5F /* LongTextViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3496744E2076ACCE00080B5F /* LongTextViewController.swift */; }; 3496955C219B605E00DCFE74 /* ImagePickerController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 34969559219B605E00DCFE74 /* ImagePickerController.swift */; }; @@ -77,9 +67,6 @@ 34AC0A23211C829F00997B47 /* OWSLabel.m in Sources */ = {isa = PBXBuildFile; fileRef = 34AC0A21211C829E00997B47 /* OWSLabel.m */; }; 34B0796D1FCF46B100E248C2 /* MainAppContext.m in Sources */ = {isa = PBXBuildFile; fileRef = 34B0796B1FCF46B000E248C2 /* MainAppContext.m */; }; 34B3F8751E8DF1700035BE1A /* CallViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 34B3F83B1E8DF1700035BE1A /* CallViewController.swift */; }; - 34B3F8771E8DF1700035BE1A /* ContactsPicker.swift in Sources */ = {isa = PBXBuildFile; fileRef = 34B3F83E1E8DF1700035BE1A /* ContactsPicker.swift */; }; - 34B3F8801E8DF1700035BE1A /* InviteFlow.swift in Sources */ = {isa = PBXBuildFile; fileRef = 34B3F84C1E8DF1700035BE1A /* InviteFlow.swift */; }; - 34B3F8931E8DF1710035BE1A /* SignalsNavigationController.m in Sources */ = {isa = PBXBuildFile; fileRef = 34B3F86E1E8DF1700035BE1A /* SignalsNavigationController.m */; }; 34B6A903218B3F63007C4606 /* TypingIndicatorView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 34B6A902218B3F62007C4606 /* TypingIndicatorView.swift */; }; 34B6A905218B4C91007C4606 /* TypingIndicatorInteraction.swift in Sources */ = {isa = PBXBuildFile; fileRef = 34B6A904218B4C90007C4606 /* TypingIndicatorInteraction.swift */; }; 34B6A907218B5241007C4606 /* TypingIndicatorCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 34B6A906218B5240007C4606 /* TypingIndicatorCell.swift */; }; @@ -90,7 +77,6 @@ 34C3C78F2040A4F70000134C /* sonarping.mp3 in Resources */ = {isa = PBXBuildFile; fileRef = 34C3C78E2040A4F70000134C /* sonarping.mp3 */; }; 34C4E2572118957600BEA353 /* OWSWebRTCDataProtos.pb.swift in Sources */ = {isa = PBXBuildFile; fileRef = 34C4E2552118957600BEA353 /* OWSWebRTCDataProtos.pb.swift */; }; 34C4E2582118957600BEA353 /* WebRTCProto.swift in Sources */ = {isa = PBXBuildFile; fileRef = 34C4E2562118957600BEA353 /* WebRTCProto.swift */; }; - 34CA631B2097806F00E526A0 /* OWSContactShareView.m in Sources */ = {isa = PBXBuildFile; fileRef = 34CA631A2097806E00E526A0 /* OWSContactShareView.m */; }; 34CF0787203E6B78005C4D61 /* busy_tone_ansi.caf in Resources */ = {isa = PBXBuildFile; fileRef = 34CF0783203E6B77005C4D61 /* busy_tone_ansi.caf */; }; 34CF0788203E6B78005C4D61 /* ringback_tone_ansi.caf in Resources */ = {isa = PBXBuildFile; fileRef = 34CF0784203E6B77005C4D61 /* ringback_tone_ansi.caf */; }; 34CF078A203E6B78005C4D61 /* end_call_tone_cept.caf in Resources */ = {isa = PBXBuildFile; fileRef = 34CF0786203E6B78005C4D61 /* end_call_tone_cept.caf */; }; @@ -102,7 +88,6 @@ 34D1F0871F8678AA0066283D /* ConversationViewItem.m in Sources */ = {isa = PBXBuildFile; fileRef = 34D1F0701F8678AA0066283D /* ConversationViewItem.m */; }; 34D1F0881F8678AA0066283D /* ConversationViewLayout.m in Sources */ = {isa = PBXBuildFile; fileRef = 34D1F0721F8678AA0066283D /* ConversationViewLayout.m */; }; 34D1F0A91F867BFC0066283D /* ConversationViewCell.m in Sources */ = {isa = PBXBuildFile; fileRef = 34D1F0971F867BFC0066283D /* ConversationViewCell.m */; }; - 34D1F0AB1F867BFC0066283D /* OWSContactOffersCell.m in Sources */ = {isa = PBXBuildFile; fileRef = 34D1F09B1F867BFC0066283D /* OWSContactOffersCell.m */; }; 34D1F0AE1F867BFC0066283D /* OWSMessageCell.m in Sources */ = {isa = PBXBuildFile; fileRef = 34D1F0A21F867BFC0066283D /* OWSMessageCell.m */; }; 34D1F0B01F867BFC0066283D /* OWSSystemMessageCell.m in Sources */ = {isa = PBXBuildFile; fileRef = 34D1F0A61F867BFC0066283D /* OWSSystemMessageCell.m */; }; 34D1F0B41F86D31D0066283D /* ConversationCollectionView.m in Sources */ = {isa = PBXBuildFile; fileRef = 34D1F0B31F86D31D0066283D /* ConversationCollectionView.m */; }; @@ -112,13 +97,11 @@ 34D2CCDA2062E7D000CB1A14 /* OWSScreenLockUI.m in Sources */ = {isa = PBXBuildFile; fileRef = 34D2CCD92062E7D000CB1A14 /* OWSScreenLockUI.m */; }; 34D5CCA91EAE3D30005515DB /* AvatarViewHelper.m in Sources */ = {isa = PBXBuildFile; fileRef = 34D5CCA81EAE3D30005515DB /* AvatarViewHelper.m */; }; 34D920E720E179C200D51158 /* OWSMessageFooterView.m in Sources */ = {isa = PBXBuildFile; fileRef = 34D920E620E179C200D51158 /* OWSMessageFooterView.m */; }; - 34D99C931F2937CC00D284D6 /* OWSAnalytics.swift in Sources */ = {isa = PBXBuildFile; fileRef = 34D99C911F2937CC00D284D6 /* OWSAnalytics.swift */; }; 34D99CE4217509C2000AFB39 /* AppEnvironment.swift in Sources */ = {isa = PBXBuildFile; fileRef = 34D99CE3217509C1000AFB39 /* AppEnvironment.swift */; }; 34DBF003206BD5A500025978 /* OWSMessageTextView.m in Sources */ = {isa = PBXBuildFile; fileRef = 34DBEFFF206BD5A400025978 /* OWSMessageTextView.m */; }; 34DBF004206BD5A500025978 /* OWSBubbleView.m in Sources */ = {isa = PBXBuildFile; fileRef = 34DBF001206BD5A500025978 /* OWSBubbleView.m */; }; 34DBF007206C3CB200025978 /* OWSBubbleShapeView.m in Sources */ = {isa = PBXBuildFile; fileRef = 34DBF006206C3CB200025978 /* OWSBubbleShapeView.m */; }; 34E3E5681EC4B19400495BAC /* AudioProgressView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 34E3E5671EC4B19400495BAC /* AudioProgressView.swift */; }; - 34E88D262098C5AE00A608F4 /* ContactViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 34E88D252098C5AE00A608F4 /* ContactViewController.swift */; }; 34EA69402194933900702471 /* MediaDownloadView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 34EA693F2194933900702471 /* MediaDownloadView.swift */; }; 34EA69422194DE8000702471 /* MediaUploadView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 34EA69412194DE7F00702471 /* MediaUploadView.swift */; }; 34F308A21ECB469700BB7697 /* OWSBezierPathView.m in Sources */ = {isa = PBXBuildFile; fileRef = 34F308A11ECB469700BB7697 /* OWSBezierPathView.m */; }; @@ -131,14 +114,11 @@ 450DF2051E0D74AC003D14BE /* Platform.swift in Sources */ = {isa = PBXBuildFile; fileRef = 450DF2041E0D74AC003D14BE /* Platform.swift */; }; 450DF2091E0DD2C6003D14BE /* UserNotificationsAdaptee.swift in Sources */ = {isa = PBXBuildFile; fileRef = 450DF2081E0DD2C6003D14BE /* UserNotificationsAdaptee.swift */; }; 451166C01FD86B98000739BA /* AccountManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 451166BF1FD86B98000739BA /* AccountManager.swift */; }; - 4517642B1DE939FD00EDB8B9 /* ContactCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 451764291DE939FD00EDB8B9 /* ContactCell.swift */; }; 451A13B11E13DED2000A50FD /* AppNotifications.swift in Sources */ = {isa = PBXBuildFile; fileRef = 451A13B01E13DED2000A50FD /* AppNotifications.swift */; }; 4520D8D51D417D8E00123472 /* Photos.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 4520D8D41D417D8E00123472 /* Photos.framework */; }; 4521C3C01F59F3BA00B4C582 /* TextFieldHelper.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4521C3BF1F59F3BA00B4C582 /* TextFieldHelper.swift */; }; - 452B999020A34B6B006F2F9E /* AddContactShareToExistingContactViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 452B998F20A34B6B006F2F9E /* AddContactShareToExistingContactViewController.swift */; }; 452C468F1E427E200087B011 /* OutboundCallInitiator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 452C468E1E427E200087B011 /* OutboundCallInitiator.swift */; }; 452EC6DF205E9E30000E787C /* MediaGalleryViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 452EC6DE205E9E30000E787C /* MediaGalleryViewController.swift */; }; - 452ECA4D1E087E7200E2F016 /* MessageFetcherJob.swift in Sources */ = {isa = PBXBuildFile; fileRef = 452ECA4C1E087E7200E2F016 /* MessageFetcherJob.swift */; }; 4535186B1FC635DD00210559 /* ShareViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4535186A1FC635DD00210559 /* ShareViewController.swift */; }; 4535186E1FC635DD00210559 /* MainInterface.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 4535186C1FC635DD00210559 /* MainInterface.storyboard */; }; 453518721FC635DD00210559 /* SessionShareExtension.appex in Embed App Extensions */ = {isa = PBXBuildFile; fileRef = 453518681FC635DD00210559 /* SessionShareExtension.appex */; settings = {ATTRIBUTES = (RemoveHeadersOnCopy, ); }; }; @@ -148,14 +128,11 @@ 455A16DD1F1FEA0000F86704 /* Metal.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 455A16DB1F1FEA0000F86704 /* Metal.framework */; settings = {ATTRIBUTES = (Weak, ); }; }; 455A16DE1F1FEA0000F86704 /* MetalKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 455A16DC1F1FEA0000F86704 /* MetalKit.framework */; settings = {ATTRIBUTES = (Weak, ); }; }; 4574A5D61DD6704700C6B692 /* CallService.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4574A5D51DD6704700C6B692 /* CallService.swift */; }; - 4579431E1E7C8CE9008ED0C0 /* Pastelog.m in Sources */ = {isa = PBXBuildFile; fileRef = 4579431D1E7C8CE9008ED0C0 /* Pastelog.m */; }; 45794E861E00620000066731 /* CallUIAdapter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 45794E851E00620000066731 /* CallUIAdapter.swift */; }; 457F671B20746193000EABCD /* QuotedReplyPreview.swift in Sources */ = {isa = PBXBuildFile; fileRef = 457F671A20746193000EABCD /* QuotedReplyPreview.swift */; }; 45847E871E4283C30080EAB3 /* Intents.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 45847E861E4283C30080EAB3 /* Intents.framework */; settings = {ATTRIBUTES = (Weak, ); }; }; - 4585C4681ED8F8D200896AEA /* SafetyNumberConfirmationAlert.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4585C4671ED8F8D200896AEA /* SafetyNumberConfirmationAlert.swift */; }; 458DE9D61DEE3FD00071BB03 /* PeerConnectionClient.swift in Sources */ = {isa = PBXBuildFile; fileRef = 458DE9D51DEE3FD00071BB03 /* PeerConnectionClient.swift */; }; 458E38371D668EBF0094BD24 /* OWSDeviceProvisioningURLParser.m in Sources */ = {isa = PBXBuildFile; fileRef = 458E38361D668EBF0094BD24 /* OWSDeviceProvisioningURLParser.m */; }; - 459311FC1D75C948008DD4F0 /* OWSDeviceTableViewCell.m in Sources */ = {isa = PBXBuildFile; fileRef = 459311FB1D75C948008DD4F0 /* OWSDeviceTableViewCell.m */; }; 45A2F005204473A3002E978A /* NewMessage.aifc in Resources */ = {isa = PBXBuildFile; fileRef = 45A2F004204473A3002E978A /* NewMessage.aifc */; }; 45A663C51F92EC760027B59E /* GroupTableViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 45A663C41F92EC760027B59E /* GroupTableViewCell.swift */; }; 45A6DAD61EBBF85500893231 /* ReminderView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 45A6DAD51EBBF85500893231 /* ReminderView.swift */; }; @@ -192,8 +169,6 @@ 45CB2FA81CB7146C00E1B343 /* Launch Screen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 45CB2FA71CB7146C00E1B343 /* Launch Screen.storyboard */; }; 45CD81EF1DC030E7004C9430 /* SyncPushTokensJob.swift in Sources */ = {isa = PBXBuildFile; fileRef = 45CD81EE1DC030E7004C9430 /* SyncPushTokensJob.swift */; }; 45D231771DC7E8F10034FA89 /* SessionResetJob.swift in Sources */ = {isa = PBXBuildFile; fileRef = 45D231761DC7E8F10034FA89 /* SessionResetJob.swift */; }; - 45D308AD2049A439000189E4 /* PinEntryView.m in Sources */ = {isa = PBXBuildFile; fileRef = 45D308AC2049A439000189E4 /* PinEntryView.m */; }; - 45DDA6242090CEB500DE97F8 /* ConversationHeaderView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 45DDA6232090CEB500DE97F8 /* ConversationHeaderView.swift */; }; 45DF5DF21DDB843F00C936C7 /* CompareSafetyNumbersActivity.swift in Sources */ = {isa = PBXBuildFile; fileRef = 45DF5DF11DDB843F00C936C7 /* CompareSafetyNumbersActivity.swift */; }; 45E5A6991F61E6DE001E4A8A /* MarqueeLabel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 45E5A6981F61E6DD001E4A8A /* MarqueeLabel.swift */; }; 45F170BB1E2FC5D3003FC1F2 /* CallAudioService.swift in Sources */ = {isa = PBXBuildFile; fileRef = 45F170BA1E2FC5D3003FC1F2 /* CallAudioService.swift */; }; @@ -206,7 +181,6 @@ 45FBC5D11DF8592E00E9B410 /* SignalCall.swift in Sources */ = {isa = PBXBuildFile; fileRef = 45FBC5D01DF8592E00E9B410 /* SignalCall.swift */; }; 4C04392A220A9EC800BAEA63 /* VoiceNoteLock.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4C043929220A9EC800BAEA63 /* VoiceNoteLock.swift */; }; 4C090A1B210FD9C7001FD7F9 /* HapticFeedback.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4C090A1A210FD9C7001FD7F9 /* HapticFeedback.swift */; }; - 4C13C9F620E57BA30089A98B /* ColorPickerViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4C13C9F520E57BA30089A98B /* ColorPickerViewController.swift */; }; 4C1885D2218F8E1C00B67051 /* PhotoGridViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4C1885D1218F8E1C00B67051 /* PhotoGridViewCell.swift */; }; 4C21D5D6223A9DC500EF8A77 /* UIAlerts+iOS9.m in Sources */ = {isa = PBXBuildFile; fileRef = 4C21D5D5223A9DC500EF8A77 /* UIAlerts+iOS9.m */; }; 4C21D5D8223AC60F00EF8A77 /* PhotoCapture.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4C21D5D7223AC60F00EF8A77 /* PhotoCapture.swift */; }; @@ -222,7 +196,6 @@ 4CA485BB2232339F004B9E7D /* PhotoCaptureViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4CA485BA2232339F004B9E7D /* PhotoCaptureViewController.swift */; }; 4CB5F26720F6E1E2004D1B42 /* MenuActionsViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4CFF4C0920F55BBA005DA313 /* MenuActionsViewController.swift */; }; 4CB5F26920F7D060004D1B42 /* MessageActions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4CB5F26820F7D060004D1B42 /* MessageActions.swift */; }; - 4CC0B59C20EC5F2E00CF6EE0 /* ConversationConfigurationSyncOperation.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4CC0B59B20EC5F2E00CF6EE0 /* ConversationConfigurationSyncOperation.swift */; }; 4CC1ECF9211A47CE00CC13BE /* StoreKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 4CC1ECF8211A47CD00CC13BE /* StoreKit.framework */; settings = {ATTRIBUTES = (Weak, ); }; }; 4CC1ECFB211A553000CC13BE /* AppUpdateNag.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4CC1ECFA211A553000CC13BE /* AppUpdateNag.swift */; }; 4CC613362227A00400E21A3A /* ConversationSearch.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4CC613352227A00400E21A3A /* ConversationSearch.swift */; }; @@ -273,9 +246,6 @@ B6F509971AA53F760068F56A /* Localizable.strings in Resources */ = {isa = PBXBuildFile; fileRef = B6F509951AA53F760068F56A /* Localizable.strings */; }; B6FE7EB71ADD62FA00A6D22F /* PushKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = B6FE7EB61ADD62FA00A6D22F /* PushKit.framework */; }; B80A579F23DFF1F300876683 /* NewClosedGroupVC.swift in Sources */ = {isa = PBXBuildFile; fileRef = B80A579E23DFF1F300876683 /* NewClosedGroupVC.swift */; }; - B80C6B572384A56D00FDBC8B /* DeviceLinksVC.swift in Sources */ = {isa = PBXBuildFile; fileRef = B80C6B562384A56D00FDBC8B /* DeviceLinksVC.swift */; }; - B80C6B592384C4E700FDBC8B /* DeviceNameModal.swift in Sources */ = {isa = PBXBuildFile; fileRef = B80C6B582384C4E700FDBC8B /* DeviceNameModal.swift */; }; - B80C6B5B2384C7F900FDBC8B /* DeviceNameModalDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = B80C6B5A2384C7F900FDBC8B /* DeviceNameModalDelegate.swift */; }; B82B40882399EB0E00A248E7 /* LandingVC.swift in Sources */ = {isa = PBXBuildFile; fileRef = B82B40872399EB0E00A248E7 /* LandingVC.swift */; }; B82B408A2399EC0600A248E7 /* FakeChatView.swift in Sources */ = {isa = PBXBuildFile; fileRef = B82B40892399EC0600A248E7 /* FakeChatView.swift */; }; B82B408C239A068800A248E7 /* RegisterVC.swift in Sources */ = {isa = PBXBuildFile; fileRef = B82B408B239A068800A248E7 /* RegisterVC.swift */; }; @@ -293,15 +263,16 @@ B86BD08623399CEF000F5AE3 /* SeedModal.swift in Sources */ = {isa = PBXBuildFile; fileRef = B86BD08523399CEF000F5AE3 /* SeedModal.swift */; }; B8783E9E23EB948D00404FB8 /* UILabel+Interaction.swift in Sources */ = {isa = PBXBuildFile; fileRef = B8783E9D23EB948D00404FB8 /* UILabel+Interaction.swift */; }; B879D449247E1BE300DB3608 /* PathVC.swift in Sources */ = {isa = PBXBuildFile; fileRef = B879D448247E1BE300DB3608 /* PathVC.swift */; }; - B885D5F4233491AB00EE0D8E /* DeviceLinkingModal.swift in Sources */ = {isa = PBXBuildFile; fileRef = B885D5F3233491AB00EE0D8E /* DeviceLinkingModal.swift */; }; B886B4A72398B23E00211ABE /* QRCodeVC.swift in Sources */ = {isa = PBXBuildFile; fileRef = B886B4A62398B23E00211ABE /* QRCodeVC.swift */; }; B886B4A92398BA1500211ABE /* QRCode.swift in Sources */ = {isa = PBXBuildFile; fileRef = B886B4A82398BA1500211ABE /* QRCode.swift */; }; B88847BC23E10BC6009836D2 /* GroupMembersVC.swift in Sources */ = {isa = PBXBuildFile; fileRef = B88847BB23E10BC6009836D2 /* GroupMembersVC.swift */; }; B893063F2383961A005EAA8E /* ScanQRCodeWrapperVC.swift in Sources */ = {isa = PBXBuildFile; fileRef = B893063E2383961A005EAA8E /* ScanQRCodeWrapperVC.swift */; }; - B894D0712339D6F300B4D94D /* DeviceLinkingModalDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = B894D0702339D6F300B4D94D /* DeviceLinkingModalDelegate.swift */; }; B894D0752339EDCF00B4D94D /* NukeDataModal.swift in Sources */ = {isa = PBXBuildFile; fileRef = B894D0742339EDCF00B4D94D /* NukeDataModal.swift */; }; B8B26C8F234D629C004ED98C /* MentionCandidateSelectionView.swift in Sources */ = {isa = PBXBuildFile; fileRef = B8B26C8E234D629C004ED98C /* MentionCandidateSelectionView.swift */; }; B8BB82A5238F627000BA5194 /* HomeVC.swift in Sources */ = {isa = PBXBuildFile; fileRef = B8BB82A4238F627000BA5194 /* HomeVC.swift */; }; + B8C2B2C82563685C00551B4D /* CircleView.swift in Sources */ = {isa = PBXBuildFile; fileRef = B8C2B2C72563685C00551B4D /* CircleView.swift */; }; + B8C2B332256376F000551B4D /* ThreadUtil.m in Sources */ = {isa = PBXBuildFile; fileRef = B8C2B331256376F000551B4D /* ThreadUtil.m */; }; + B8C2B3442563782400551B4D /* ThreadUtil.h in Headers */ = {isa = PBXBuildFile; fileRef = B8C2B33B2563770800551B4D /* ThreadUtil.h */; settings = {ATTRIBUTES = (Public, ); }; }; 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 */; }; @@ -358,14 +329,12 @@ C33FD9C3255A54EF00E217F9 /* SessionProtocolKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = C3C2A8622553B41A00C340D1 /* SessionProtocolKit.framework */; }; C33FD9C4255A54EF00E217F9 /* SessionSnodeKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = C3C2A59F255385C100C340D1 /* SessionSnodeKit.framework */; }; C33FD9C5255A54EF00E217F9 /* SessionUtilitiesKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = C3C2A679255388CC00C340D1 /* SessionUtilitiesKit.framework */; }; - C33FDC20255A581F00E217F9 /* OWSOutgoingCallMessage.h in Headers */ = {isa = PBXBuildFile; fileRef = C33FDA66255A57F900E217F9 /* OWSOutgoingCallMessage.h */; settings = {ATTRIBUTES = (Public, ); }; }; C33FDC21255A581F00E217F9 /* OWSPrimaryStorage.h in Headers */ = {isa = PBXBuildFile; fileRef = C33FDA67255A57F900E217F9 /* OWSPrimaryStorage.h */; settings = {ATTRIBUTES = (Public, ); }; }; C33FDC22255A581F00E217F9 /* OWSBlockingManager.m in Sources */ = {isa = PBXBuildFile; fileRef = C33FDA68255A57F900E217F9 /* OWSBlockingManager.m */; }; C33FDC23255A581F00E217F9 /* SSKPreferences.swift in Sources */ = {isa = PBXBuildFile; fileRef = C33FDA69255A57F900E217F9 /* SSKPreferences.swift */; }; C33FDC25255A581F00E217F9 /* OWSDisappearingConfigurationUpdateInfoMessage.m in Sources */ = {isa = PBXBuildFile; fileRef = C33FDA6B255A57FA00E217F9 /* OWSDisappearingConfigurationUpdateInfoMessage.m */; }; C33FDC26255A581F00E217F9 /* ProtoUtils.m in Sources */ = {isa = PBXBuildFile; fileRef = C33FDA6C255A57FA00E217F9 /* ProtoUtils.m */; }; C33FDC27255A581F00E217F9 /* YapDatabase+Promise.swift in Sources */ = {isa = PBXBuildFile; fileRef = C33FDA6D255A57FA00E217F9 /* YapDatabase+Promise.swift */; }; - C33FDC28255A581F00E217F9 /* Array+Description.swift in Sources */ = {isa = PBXBuildFile; fileRef = C33FDA6E255A57FA00E217F9 /* Array+Description.swift */; }; C33FDC29255A581F00E217F9 /* ReachabilityManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = C33FDA6F255A57FA00E217F9 /* ReachabilityManager.swift */; }; C33FDC2A255A581F00E217F9 /* TSMessage.h in Headers */ = {isa = PBXBuildFile; fileRef = C33FDA70255A57FA00E217F9 /* TSMessage.h */; settings = {ATTRIBUTES = (Public, ); }; }; C33FDC2B255A581F00E217F9 /* OWSReadReceiptManager.m in Sources */ = {isa = PBXBuildFile; fileRef = C33FDA71255A57FA00E217F9 /* OWSReadReceiptManager.m */; }; @@ -373,70 +342,43 @@ C33FDC2D255A581F00E217F9 /* ECKeyPair+Hexadecimal.swift in Sources */ = {isa = PBXBuildFile; fileRef = C33FDA73255A57FA00E217F9 /* ECKeyPair+Hexadecimal.swift */; }; C33FDC2E255A581F00E217F9 /* ClosedGroupsProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = C33FDA74255A57FB00E217F9 /* ClosedGroupsProtocol.swift */; }; C33FDC2F255A581F00E217F9 /* OWSSyncManagerProtocol.h in Headers */ = {isa = PBXBuildFile; fileRef = C33FDA75255A57FB00E217F9 /* OWSSyncManagerProtocol.h */; settings = {ATTRIBUTES = (Public, ); }; }; - C33FDC30255A581F00E217F9 /* OWSSyncGroupsRequestMessage.m in Sources */ = {isa = PBXBuildFile; fileRef = C33FDA76255A57FB00E217F9 /* OWSSyncGroupsRequestMessage.m */; }; - C33FDC31255A581F00E217F9 /* Contact.m in Sources */ = {isa = PBXBuildFile; fileRef = C33FDA77255A57FB00E217F9 /* Contact.m */; }; - C33FDC32255A581F00E217F9 /* SSKWebSocket.swift in Sources */ = {isa = PBXBuildFile; fileRef = C33FDA78255A57FB00E217F9 /* SSKWebSocket.swift */; }; C33FDC33255A581F00E217F9 /* TSGroupThread.h in Headers */ = {isa = PBXBuildFile; fileRef = C33FDA79255A57FB00E217F9 /* TSGroupThread.h */; settings = {ATTRIBUTES = (Public, ); }; }; C33FDC34255A581F00E217F9 /* NSRegularExpression+SSK.swift in Sources */ = {isa = PBXBuildFile; fileRef = C33FDA7A255A57FB00E217F9 /* NSRegularExpression+SSK.swift */; }; C33FDC35255A581F00E217F9 /* TSInvalidIdentityKeyReceivingErrorMessage.h in Headers */ = {isa = PBXBuildFile; fileRef = C33FDA7B255A57FB00E217F9 /* TSInvalidIdentityKeyReceivingErrorMessage.h */; settings = {ATTRIBUTES = (Public, ); }; }; C33FDC36255A581F00E217F9 /* Debugging.swift in Sources */ = {isa = PBXBuildFile; fileRef = C33FDA7C255A57FB00E217F9 /* Debugging.swift */; }; - C33FDC37255A581F00E217F9 /* OWSCensorshipConfiguration.m in Sources */ = {isa = PBXBuildFile; fileRef = C33FDA7D255A57FB00E217F9 /* OWSCensorshipConfiguration.m */; }; C33FDC38255A581F00E217F9 /* Mention.swift in Sources */ = {isa = PBXBuildFile; fileRef = C33FDA7E255A57FB00E217F9 /* Mention.swift */; }; C33FDC39255A581F00E217F9 /* OWSRecordTranscriptJob.m in Sources */ = {isa = PBXBuildFile; fileRef = C33FDA7F255A57FC00E217F9 /* OWSRecordTranscriptJob.m */; }; C33FDC3A255A581F00E217F9 /* OWSDisappearingMessagesJob.h in Headers */ = {isa = PBXBuildFile; fileRef = C33FDA80255A57FC00E217F9 /* OWSDisappearingMessagesJob.h */; settings = {ATTRIBUTES = (Public, ); }; }; C33FDC3B255A581F00E217F9 /* MentionsManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = C33FDA81255A57FC00E217F9 /* MentionsManager.swift */; }; - C33FDC3D255A581F00E217F9 /* Promise+retainUntilComplete.swift in Sources */ = {isa = PBXBuildFile; fileRef = C33FDA83255A57FC00E217F9 /* Promise+retainUntilComplete.swift */; }; - C33FDC3E255A581F00E217F9 /* ContactsUpdater.m in Sources */ = {isa = PBXBuildFile; fileRef = C33FDA84255A57FC00E217F9 /* ContactsUpdater.m */; }; C33FDC3F255A581F00E217F9 /* OWSPrimaryStorage+Loki.swift in Sources */ = {isa = PBXBuildFile; fileRef = C33FDA85255A57FC00E217F9 /* OWSPrimaryStorage+Loki.swift */; }; C33FDC40255A581F00E217F9 /* OWSDisappearingMessagesFinder.m in Sources */ = {isa = PBXBuildFile; fileRef = C33FDA86255A57FC00E217F9 /* OWSDisappearingMessagesFinder.m */; }; C33FDC41255A581F00E217F9 /* TypingIndicators.swift in Sources */ = {isa = PBXBuildFile; fileRef = C33FDA87255A57FC00E217F9 /* TypingIndicators.swift */; }; C33FDC42255A581F00E217F9 /* YapDatabaseTransaction+OWS.h in Headers */ = {isa = PBXBuildFile; fileRef = C33FDA88255A57FD00E217F9 /* YapDatabaseTransaction+OWS.h */; settings = {ATTRIBUTES = (Public, ); }; }; - C33FDC43255A581F00E217F9 /* OWSAnalytics.m in Sources */ = {isa = PBXBuildFile; fileRef = C33FDA89255A57FD00E217F9 /* OWSAnalytics.m */; }; - C33FDC44255A581F00E217F9 /* PhoneNumber.m in Sources */ = {isa = PBXBuildFile; fileRef = C33FDA8A255A57FD00E217F9 /* PhoneNumber.m */; }; C33FDC45255A581F00E217F9 /* AppVersion.m in Sources */ = {isa = PBXBuildFile; fileRef = C33FDA8B255A57FD00E217F9 /* AppVersion.m */; }; C33FDC46255A581F00E217F9 /* PublicChatPoller.swift in Sources */ = {isa = PBXBuildFile; fileRef = C33FDA8C255A57FD00E217F9 /* PublicChatPoller.swift */; }; - C33FDC47255A581F00E217F9 /* OWSReceiptsForSenderMessage.h in Headers */ = {isa = PBXBuildFile; fileRef = C33FDA8D255A57FD00E217F9 /* OWSReceiptsForSenderMessage.h */; settings = {ATTRIBUTES = (Public, ); }; }; C33FDC48255A581F00E217F9 /* OWSFileSystem.m in Sources */ = {isa = PBXBuildFile; fileRef = C33FDA8E255A57FD00E217F9 /* OWSFileSystem.m */; }; - C33FDC49255A581F00E217F9 /* NSTimer+OWS.m in Sources */ = {isa = PBXBuildFile; fileRef = C33FDA8F255A57FD00E217F9 /* NSTimer+OWS.m */; }; C33FDC4A255A582000E217F9 /* TSYapDatabaseObject.m in Sources */ = {isa = PBXBuildFile; fileRef = C33FDA90255A57FD00E217F9 /* TSYapDatabaseObject.m */; }; - C33FDC4B255A582000E217F9 /* LKSyncOpenGroupsMessage.h in Headers */ = {isa = PBXBuildFile; fileRef = C33FDA91255A57FD00E217F9 /* LKSyncOpenGroupsMessage.h */; settings = {ATTRIBUTES = (Public, ); }; }; C33FDC4E255A582000E217F9 /* Data+Streaming.swift in Sources */ = {isa = PBXBuildFile; fileRef = C33FDA94255A57FE00E217F9 /* Data+Streaming.swift */; }; C33FDC4F255A582000E217F9 /* OWSChunkedOutputStream.h in Headers */ = {isa = PBXBuildFile; fileRef = C33FDA95255A57FE00E217F9 /* OWSChunkedOutputStream.h */; settings = {ATTRIBUTES = (Public, ); }; }; C33FDC50255A582000E217F9 /* OWSDispatch.h in Headers */ = {isa = PBXBuildFile; fileRef = C33FDA96255A57FE00E217F9 /* OWSDispatch.h */; settings = {ATTRIBUTES = (Public, ); }; }; C33FDC51255A582000E217F9 /* TSIncomingMessage.m in Sources */ = {isa = PBXBuildFile; fileRef = C33FDA97255A57FE00E217F9 /* TSIncomingMessage.m */; }; C33FDC52255A582000E217F9 /* RotateSignedKeyOperation.swift in Sources */ = {isa = PBXBuildFile; fileRef = C33FDA98255A57FE00E217F9 /* RotateSignedKeyOperation.swift */; }; C33FDC53255A582000E217F9 /* OutageDetection.swift in Sources */ = {isa = PBXBuildFile; fileRef = C33FDA99255A57FE00E217F9 /* OutageDetection.swift */; }; - C33FDC54255A582000E217F9 /* OWSLinkedDeviceReadReceipt.m in Sources */ = {isa = PBXBuildFile; fileRef = C33FDA9A255A57FE00E217F9 /* OWSLinkedDeviceReadReceipt.m */; }; - C33FDC55255A582000E217F9 /* OWSProvisioningMessage.m in Sources */ = {isa = PBXBuildFile; fileRef = C33FDA9B255A57FE00E217F9 /* OWSProvisioningMessage.m */; }; - C33FDC56255A582000E217F9 /* OWSSyncContactsMessage.m in Sources */ = {isa = PBXBuildFile; fileRef = C33FDA9C255A57FE00E217F9 /* OWSSyncContactsMessage.m */; }; C33FDC57255A582000E217F9 /* OWSContactsOutputStream.h in Headers */ = {isa = PBXBuildFile; fileRef = C33FDA9D255A57FF00E217F9 /* OWSContactsOutputStream.h */; settings = {ATTRIBUTES = (Public, ); }; }; C33FDC58255A582000E217F9 /* ReverseDispatchQueue.swift in Sources */ = {isa = PBXBuildFile; fileRef = C33FDA9E255A57FF00E217F9 /* ReverseDispatchQueue.swift */; }; - C33FDC59255A582000E217F9 /* NetworkManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = C33FDA9F255A57FF00E217F9 /* NetworkManager.swift */; }; C33FDC5A255A582000E217F9 /* OWSRecipientIdentity.h in Headers */ = {isa = PBXBuildFile; fileRef = C33FDAA0255A57FF00E217F9 /* OWSRecipientIdentity.h */; settings = {ATTRIBUTES = (Public, ); }; }; C33FDC5B255A582000E217F9 /* TSYapDatabaseObject.h in Headers */ = {isa = PBXBuildFile; fileRef = C33FDAA1255A57FF00E217F9 /* TSYapDatabaseObject.h */; settings = {ATTRIBUTES = (Public, ); }; }; - C33FDC5C255A582000E217F9 /* OWSAddToContactsOfferMessage.m in Sources */ = {isa = PBXBuildFile; fileRef = C33FDAA2255A57FF00E217F9 /* OWSAddToContactsOfferMessage.m */; }; - C33FDC5D255A582000E217F9 /* OWSAddToContactsOfferMessage.h in Headers */ = {isa = PBXBuildFile; fileRef = C33FDAA3255A57FF00E217F9 /* OWSAddToContactsOfferMessage.h */; settings = {ATTRIBUTES = (Public, ); }; }; C33FDC5E255A582000E217F9 /* SSKProto.swift in Sources */ = {isa = PBXBuildFile; fileRef = C33FDAA4255A57FF00E217F9 /* SSKProto.swift */; }; - C33FDC5F255A582000E217F9 /* OWSRequestMaker.swift in Sources */ = {isa = PBXBuildFile; fileRef = C33FDAA5255A57FF00E217F9 /* OWSRequestMaker.swift */; }; - C33FDC60255A582000E217F9 /* OWSLinkedDeviceReadReceipt.h in Headers */ = {isa = PBXBuildFile; fileRef = C33FDAA6255A57FF00E217F9 /* OWSLinkedDeviceReadReceipt.h */; settings = {ATTRIBUTES = (Public, ); }; }; C33FDC61255A582000E217F9 /* OWSPrimaryStorage+SignedPreKeyStore.h in Headers */ = {isa = PBXBuildFile; fileRef = C33FDAA7255A57FF00E217F9 /* OWSPrimaryStorage+SignedPreKeyStore.h */; settings = {ATTRIBUTES = (Public, ); }; }; C33FDC62255A582000E217F9 /* BuildConfiguration.swift in Sources */ = {isa = PBXBuildFile; fileRef = C33FDAA8255A57FF00E217F9 /* BuildConfiguration.swift */; }; - C33FDC63255A582000E217F9 /* Mnemonic.swift in Sources */ = {isa = PBXBuildFile; fileRef = C33FDAA9255A580000E217F9 /* Mnemonic.swift */; }; C33FDC64255A582000E217F9 /* NSObject+Casting.m in Sources */ = {isa = PBXBuildFile; fileRef = C33FDAAA255A580000E217F9 /* NSObject+Casting.m */; }; - C33FDC65255A582000E217F9 /* OWSWebSocket.m in Sources */ = {isa = PBXBuildFile; fileRef = C33FDAAB255A580000E217F9 /* OWSWebSocket.m */; }; - C33FDC67255A582000E217F9 /* OWSDeviceProvisioner.h in Headers */ = {isa = PBXBuildFile; fileRef = C33FDAAD255A580000E217F9 /* OWSDeviceProvisioner.h */; settings = {ATTRIBUTES = (Public, ); }; }; - C33FDC68255A582000E217F9 /* OWSReceiptsForSenderMessage.m in Sources */ = {isa = PBXBuildFile; fileRef = C33FDAAE255A580000E217F9 /* OWSReceiptsForSenderMessage.m */; }; C33FDC69255A582000E217F9 /* String+Trimming.swift in Sources */ = {isa = PBXBuildFile; fileRef = C33FDAAF255A580000E217F9 /* String+Trimming.swift */; }; C33FDC6A255A582000E217F9 /* ProvisioningProto.swift in Sources */ = {isa = PBXBuildFile; fileRef = C33FDAB0255A580000E217F9 /* ProvisioningProto.swift */; }; C33FDC6B255A582000E217F9 /* OWSStorage.m in Sources */ = {isa = PBXBuildFile; fileRef = C33FDAB1255A580000E217F9 /* OWSStorage.m */; }; - C33FDC6C255A582000E217F9 /* TSNetworkManager.m in Sources */ = {isa = PBXBuildFile; fileRef = C33FDAB2255A580000E217F9 /* TSNetworkManager.m */; }; C33FDC6D255A582000E217F9 /* TSContactThread.h in Headers */ = {isa = PBXBuildFile; fileRef = C33FDAB3255A580000E217F9 /* TSContactThread.h */; settings = {ATTRIBUTES = (Public, ); }; }; - C33FDC6F255A582000E217F9 /* TSNetworkManager.h in Headers */ = {isa = PBXBuildFile; fileRef = C33FDAB5255A580000E217F9 /* TSNetworkManager.h */; settings = {ATTRIBUTES = (Public, ); }; }; - C33FDC70255A582000E217F9 /* SyncMessagesProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = C33FDAB6255A580100E217F9 /* SyncMessagesProtocol.swift */; }; C33FDC71255A582000E217F9 /* OWSFailedMessagesJob.m in Sources */ = {isa = PBXBuildFile; fileRef = C33FDAB7255A580100E217F9 /* OWSFailedMessagesJob.m */; }; C33FDC72255A582000E217F9 /* NSArray+Functional.m in Sources */ = {isa = PBXBuildFile; fileRef = C33FDAB8255A580100E217F9 /* NSArray+Functional.m */; }; C33FDC73255A582000E217F9 /* OWSStorage+Subclass.h in Headers */ = {isa = PBXBuildFile; fileRef = C33FDAB9255A580100E217F9 /* OWSStorage+Subclass.h */; settings = {ATTRIBUTES = (Public, ); }; }; - C33FDC74255A582000E217F9 /* OWSWebSocket.h in Headers */ = {isa = PBXBuildFile; fileRef = C33FDABA255A580100E217F9 /* OWSWebSocket.h */; settings = {ATTRIBUTES = (Public, ); }; }; C33FDC75255A582000E217F9 /* OWSGroupsOutputStream.h in Headers */ = {isa = PBXBuildFile; fileRef = C33FDABB255A580100E217F9 /* OWSGroupsOutputStream.h */; settings = {ATTRIBUTES = (Public, ); }; }; C33FDC77255A582000E217F9 /* OWSOutgoingReceiptManager.h in Headers */ = {isa = PBXBuildFile; fileRef = C33FDABD255A580100E217F9 /* OWSOutgoingReceiptManager.h */; settings = {ATTRIBUTES = (Public, ); }; }; C33FDC78255A582000E217F9 /* TSConstants.m in Sources */ = {isa = PBXBuildFile; fileRef = C33FDABE255A580100E217F9 /* TSConstants.m */; }; @@ -445,24 +387,11 @@ C33FDC7C255A582000E217F9 /* TSAttachment.m in Sources */ = {isa = PBXBuildFile; fileRef = C33FDAC2255A580200E217F9 /* TSAttachment.m */; }; C33FDC7D255A582000E217F9 /* OWSDispatch.m in Sources */ = {isa = PBXBuildFile; fileRef = C33FDAC3255A580200E217F9 /* OWSDispatch.m */; }; C33FDC7E255A582000E217F9 /* TSAttachmentStream.m in Sources */ = {isa = PBXBuildFile; fileRef = C33FDAC4255A580200E217F9 /* TSAttachmentStream.m */; }; - C33FDC7F255A582000E217F9 /* OWSCountryMetadata.m in Sources */ = {isa = PBXBuildFile; fileRef = C33FDAC5255A580200E217F9 /* OWSCountryMetadata.m */; }; C33FDC80255A582000E217F9 /* Fingerprint.pb.swift in Sources */ = {isa = PBXBuildFile; fileRef = C33FDAC6255A580200E217F9 /* Fingerprint.pb.swift */; }; - C33FDC81255A582000E217F9 /* OWSSignalService.h in Headers */ = {isa = PBXBuildFile; fileRef = C33FDAC7255A580200E217F9 /* OWSSignalService.h */; settings = {ATTRIBUTES = (Public, ); }; }; - C33FDC82255A582000E217F9 /* OWSCensorshipConfiguration.h in Headers */ = {isa = PBXBuildFile; fileRef = C33FDAC8255A580200E217F9 /* OWSCensorshipConfiguration.h */; settings = {ATTRIBUTES = (Public, ); }; }; - C33FDC83255A582000E217F9 /* OWSContact.h in Headers */ = {isa = PBXBuildFile; fileRef = C33FDAC9255A580200E217F9 /* OWSContact.h */; settings = {ATTRIBUTES = (Public, ); }; }; - C33FDC84255A582000E217F9 /* LokiMessage.swift in Sources */ = {isa = PBXBuildFile; fileRef = C33FDACA255A580200E217F9 /* LokiMessage.swift */; }; - C33FDC85255A582000E217F9 /* CDSSigningCertificate.m in Sources */ = {isa = PBXBuildFile; fileRef = C33FDACB255A580200E217F9 /* CDSSigningCertificate.m */; }; C33FDC87255A582000E217F9 /* SSKJobRecord.m in Sources */ = {isa = PBXBuildFile; fileRef = C33FDACD255A580200E217F9 /* SSKJobRecord.m */; }; - C33FDC88255A582000E217F9 /* OWSVerificationStateSyncMessage.m in Sources */ = {isa = PBXBuildFile; fileRef = C33FDACE255A580300E217F9 /* OWSVerificationStateSyncMessage.m */; }; C33FDC89255A582000E217F9 /* OWSAttachmentDownloads.h in Headers */ = {isa = PBXBuildFile; fileRef = C33FDACF255A580300E217F9 /* OWSAttachmentDownloads.h */; settings = {ATTRIBUTES = (Public, ); }; }; - C33FDC8A255A582000E217F9 /* CDSQuote.m in Sources */ = {isa = PBXBuildFile; fileRef = C33FDAD0255A580300E217F9 /* CDSQuote.m */; }; - C33FDC8B255A582000E217F9 /* OWSDynamicOutgoingMessage.m in Sources */ = {isa = PBXBuildFile; fileRef = C33FDAD1255A580300E217F9 /* OWSDynamicOutgoingMessage.m */; }; - C33FDC8C255A582000E217F9 /* Contact.h in Headers */ = {isa = PBXBuildFile; fileRef = C33FDAD2255A580300E217F9 /* Contact.h */; settings = {ATTRIBUTES = (Public, ); }; }; C33FDC8D255A582000E217F9 /* TSThread.h in Headers */ = {isa = PBXBuildFile; fileRef = C33FDAD3255A580300E217F9 /* TSThread.h */; settings = {ATTRIBUTES = (Public, ); }; }; - C33FDC8E255A582000E217F9 /* EncryptionUtilities.swift in Sources */ = {isa = PBXBuildFile; fileRef = C33FDAD4255A580300E217F9 /* EncryptionUtilities.swift */; }; C33FDC8F255A582000E217F9 /* TSQuotedMessage.h in Headers */ = {isa = PBXBuildFile; fileRef = C33FDAD5255A580300E217F9 /* TSQuotedMessage.h */; settings = {ATTRIBUTES = (Public, ); }; }; - C33FDC90255A582000E217F9 /* OWSAnalytics.h in Headers */ = {isa = PBXBuildFile; fileRef = C33FDAD6255A580300E217F9 /* OWSAnalytics.h */; settings = {ATTRIBUTES = (Public, ); }; }; - C33FDC91255A582000E217F9 /* OWSDeviceProvisioner.m in Sources */ = {isa = PBXBuildFile; fileRef = C33FDAD7255A580300E217F9 /* OWSDeviceProvisioner.m */; }; C33FDC92255A582000E217F9 /* OWSGroupsOutputStream.m in Sources */ = {isa = PBXBuildFile; fileRef = C33FDAD8255A580300E217F9 /* OWSGroupsOutputStream.m */; }; C33FDC93255A582000E217F9 /* OWSDisappearingMessagesConfiguration.h in Headers */ = {isa = PBXBuildFile; fileRef = C33FDAD9255A580300E217F9 /* OWSDisappearingMessagesConfiguration.h */; settings = {ATTRIBUTES = (Public, ); }; }; C33FDC94255A582000E217F9 /* OWSDisappearingConfigurationUpdateInfoMessage.h in Headers */ = {isa = PBXBuildFile; fileRef = C33FDADA255A580400E217F9 /* OWSDisappearingConfigurationUpdateInfoMessage.h */; settings = {ATTRIBUTES = (Public, ); }; }; @@ -473,95 +402,63 @@ C33FDC99255A582000E217F9 /* PublicChatManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = C33FDADF255A580400E217F9 /* PublicChatManager.swift */; }; C33FDC9A255A582000E217F9 /* ByteParser.m in Sources */ = {isa = PBXBuildFile; fileRef = C33FDAE0255A580400E217F9 /* ByteParser.m */; }; C33FDC9B255A582000E217F9 /* OWSReadTracking.h in Headers */ = {isa = PBXBuildFile; fileRef = C33FDAE1255A580400E217F9 /* OWSReadTracking.h */; settings = {ATTRIBUTES = (Public, ); }; }; - C33FDC9C255A582000E217F9 /* OWSReadReceiptsForLinkedDevicesMessage.h in Headers */ = {isa = PBXBuildFile; fileRef = C33FDAE2255A580400E217F9 /* OWSReadReceiptsForLinkedDevicesMessage.h */; settings = {ATTRIBUTES = (Public, ); }; }; C33FDC9E255A582000E217F9 /* TSAttachmentStream.h in Headers */ = {isa = PBXBuildFile; fileRef = C33FDAE4255A580400E217F9 /* TSAttachmentStream.h */; settings = {ATTRIBUTES = (Public, ); }; }; - C33FDC9F255A582000E217F9 /* OWSAddToProfileWhitelistOfferMessage.m in Sources */ = {isa = PBXBuildFile; fileRef = C33FDAE5255A580400E217F9 /* OWSAddToProfileWhitelistOfferMessage.m */; }; C33FDCA0255A582000E217F9 /* TSInteraction.h in Headers */ = {isa = PBXBuildFile; fileRef = C33FDAE6255A580400E217F9 /* TSInteraction.h */; settings = {ATTRIBUTES = (Public, ); }; }; C33FDCA1255A582000E217F9 /* TSErrorMessage.m in Sources */ = {isa = PBXBuildFile; fileRef = C33FDAE7255A580500E217F9 /* TSErrorMessage.m */; }; C33FDCA2255A582000E217F9 /* OWSMessageUtils.h in Headers */ = {isa = PBXBuildFile; fileRef = C33FDAE8255A580500E217F9 /* OWSMessageUtils.h */; settings = {ATTRIBUTES = (Public, ); }; }; C33FDCA3255A582000E217F9 /* SSKMessageSenderJobRecord.m in Sources */ = {isa = PBXBuildFile; fileRef = C33FDAE9255A580500E217F9 /* SSKMessageSenderJobRecord.m */; }; C33FDCA4255A582000E217F9 /* OWSBackupFragment.h in Headers */ = {isa = PBXBuildFile; fileRef = C33FDAEA255A580500E217F9 /* OWSBackupFragment.h */; settings = {ATTRIBUTES = (Public, ); }; }; - C33FDCA5255A582000E217F9 /* OWSDeviceProvisioningCodeService.m in Sources */ = {isa = PBXBuildFile; fileRef = C33FDAEB255A580500E217F9 /* OWSDeviceProvisioningCodeService.m */; }; C33FDCA6255A582000E217F9 /* SignalRecipient.h in Headers */ = {isa = PBXBuildFile; fileRef = C33FDAEC255A580500E217F9 /* SignalRecipient.h */; settings = {ATTRIBUTES = (Public, ); }; }; C33FDCA7255A582000E217F9 /* SSKMessageSenderJobRecord.h in Headers */ = {isa = PBXBuildFile; fileRef = C33FDAED255A580500E217F9 /* SSKMessageSenderJobRecord.h */; settings = {ATTRIBUTES = (Public, ); }; }; - C33FDCA8255A582000E217F9 /* OWSFingerprintBuilder.m in Sources */ = {isa = PBXBuildFile; fileRef = C33FDAEE255A580500E217F9 /* OWSFingerprintBuilder.m */; }; C33FDCA9255A582000E217F9 /* NSData+Image.m in Sources */ = {isa = PBXBuildFile; fileRef = C33FDAEF255A580500E217F9 /* NSData+Image.m */; }; - C33FDCAA255A582000E217F9 /* ContactsUpdater.h in Headers */ = {isa = PBXBuildFile; fileRef = C33FDAF0255A580500E217F9 /* ContactsUpdater.h */; settings = {ATTRIBUTES = (Public, ); }; }; C33FDCAB255A582000E217F9 /* OWSThumbnailService.swift in Sources */ = {isa = PBXBuildFile; fileRef = C33FDAF1255A580500E217F9 /* OWSThumbnailService.swift */; }; C33FDCAC255A582000E217F9 /* ProxiedContentDownloader.swift in Sources */ = {isa = PBXBuildFile; fileRef = C33FDAF2255A580500E217F9 /* ProxiedContentDownloader.swift */; }; C33FDCAD255A582000E217F9 /* OWSPrimaryStorage+SessionStore.m in Sources */ = {isa = PBXBuildFile; fileRef = C33FDAF3255A580500E217F9 /* OWSPrimaryStorage+SessionStore.m */; }; C33FDCAE255A582000E217F9 /* SSKEnvironment.m in Sources */ = {isa = PBXBuildFile; fileRef = C33FDAF4255A580600E217F9 /* SSKEnvironment.m */; }; - C33FDCAF255A582000E217F9 /* OWSAnalyticsEvents.h in Headers */ = {isa = PBXBuildFile; fileRef = C33FDAF5255A580600E217F9 /* OWSAnalyticsEvents.h */; settings = {ATTRIBUTES = (Public, ); }; }; - C33FDCB0255A582000E217F9 /* OWSIncomingSentMessageTranscript.h in Headers */ = {isa = PBXBuildFile; fileRef = C33FDAF6255A580600E217F9 /* OWSIncomingSentMessageTranscript.h */; settings = {ATTRIBUTES = (Public, ); }; }; C33FDCB1255A582000E217F9 /* OWSPrimaryStorage+SignedPreKeyStore.m in Sources */ = {isa = PBXBuildFile; fileRef = C33FDAF7255A580600E217F9 /* OWSPrimaryStorage+SignedPreKeyStore.m */; }; - C33FDCB2255A582000E217F9 /* OWSSyncGroupsRequestMessage.h in Headers */ = {isa = PBXBuildFile; fileRef = C33FDAF8255A580600E217F9 /* OWSSyncGroupsRequestMessage.h */; settings = {ATTRIBUTES = (Public, ); }; }; C33FDCB3255A582000E217F9 /* TSContactThread.m in Sources */ = {isa = PBXBuildFile; fileRef = C33FDAF9255A580600E217F9 /* TSContactThread.m */; }; - C33FDCB4255A582000E217F9 /* LKDeviceLinkMessage.m in Sources */ = {isa = PBXBuildFile; fileRef = C33FDAFA255A580600E217F9 /* LKDeviceLinkMessage.m */; }; C33FDCB5255A582000E217F9 /* SessionMetaProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = C33FDAFB255A580600E217F9 /* SessionMetaProtocol.swift */; }; C33FDCB6255A582000E217F9 /* MIMETypeUtil.h in Headers */ = {isa = PBXBuildFile; fileRef = C33FDAFC255A580600E217F9 /* MIMETypeUtil.h */; settings = {ATTRIBUTES = (Public, ); }; }; C33FDCB7255A582000E217F9 /* LRUCache.swift in Sources */ = {isa = PBXBuildFile; fileRef = C33FDAFD255A580600E217F9 /* LRUCache.swift */; }; C33FDCB8255A582000E217F9 /* OWSStorage.h in Headers */ = {isa = PBXBuildFile; fileRef = C33FDAFE255A580600E217F9 /* OWSStorage.h */; settings = {ATTRIBUTES = (Public, ); }; }; C33FDCB9255A582000E217F9 /* DisplayNameUtilities.swift in Sources */ = {isa = PBXBuildFile; fileRef = C33FDAFF255A580600E217F9 /* DisplayNameUtilities.swift */; }; - C33FDCBA255A582000E217F9 /* OWSRequestBuilder.m in Sources */ = {isa = PBXBuildFile; fileRef = C33FDB00255A580600E217F9 /* OWSRequestBuilder.m */; }; C33FDCBB255A582000E217F9 /* AppReadiness.h in Headers */ = {isa = PBXBuildFile; fileRef = C33FDB01255A580700E217F9 /* AppReadiness.h */; settings = {ATTRIBUTES = (Public, ); }; }; C33FDCBC255A582000E217F9 /* TSCall.h in Headers */ = {isa = PBXBuildFile; fileRef = C33FDB02255A580700E217F9 /* TSCall.h */; settings = {ATTRIBUTES = (Public, ); }; }; C33FDCBD255A582000E217F9 /* OWSPrimaryStorage+SessionStore.h in Headers */ = {isa = PBXBuildFile; fileRef = C33FDB03255A580700E217F9 /* OWSPrimaryStorage+SessionStore.h */; settings = {ATTRIBUTES = (Public, ); }; }; - C33FDCBF255A582000E217F9 /* OWSFingerprint.m in Sources */ = {isa = PBXBuildFile; fileRef = C33FDB05255A580700E217F9 /* OWSFingerprint.m */; }; - C33FDCC0255A582000E217F9 /* OWSRequestBuilder.h in Headers */ = {isa = PBXBuildFile; fileRef = C33FDB06255A580700E217F9 /* OWSRequestBuilder.h */; settings = {ATTRIBUTES = (Public, ); }; }; C33FDCC1255A582000E217F9 /* OWSBackupFragment.m in Sources */ = {isa = PBXBuildFile; fileRef = C33FDB07255A580700E217F9 /* OWSBackupFragment.m */; }; - C33FDCC2255A582000E217F9 /* OWSProfileKeyMessage.h in Headers */ = {isa = PBXBuildFile; fileRef = C33FDB08255A580700E217F9 /* OWSProfileKeyMessage.h */; settings = {ATTRIBUTES = (Public, ); }; }; C33FDCC3255A582000E217F9 /* NSError+MessageSending.m in Sources */ = {isa = PBXBuildFile; fileRef = C33FDB09255A580700E217F9 /* NSError+MessageSending.m */; }; C33FDCC4255A582000E217F9 /* TSGroupModel.h in Headers */ = {isa = PBXBuildFile; fileRef = C33FDB0A255A580700E217F9 /* TSGroupModel.h */; settings = {ATTRIBUTES = (Public, ); }; }; - C33FDCC5255A582000E217F9 /* OWSVerificationStateChangeMessage.m in Sources */ = {isa = PBXBuildFile; fileRef = C33FDB0B255A580700E217F9 /* OWSVerificationStateChangeMessage.m */; }; - C33FDCC6255A582000E217F9 /* CDSQuote.h in Headers */ = {isa = PBXBuildFile; fileRef = C33FDB0C255A580700E217F9 /* CDSQuote.h */; settings = {ATTRIBUTES = (Public, ); }; }; C33FDCC7255A582000E217F9 /* NSArray+OWS.m in Sources */ = {isa = PBXBuildFile; fileRef = C33FDB0D255A580800E217F9 /* NSArray+OWS.m */; }; C33FDCC8255A582000E217F9 /* NSError+MessageSending.h in Headers */ = {isa = PBXBuildFile; fileRef = C33FDB0E255A580800E217F9 /* NSError+MessageSending.h */; settings = {ATTRIBUTES = (Public, ); }; }; - C33FDCC9255A582000E217F9 /* DeviceLinkingSession.swift in Sources */ = {isa = PBXBuildFile; fileRef = C33FDB0F255A580800E217F9 /* DeviceLinkingSession.swift */; }; - C33FDCCA255A582000E217F9 /* OWSBatchMessageProcessor.m in Sources */ = {isa = PBXBuildFile; fileRef = C33FDB10255A580800E217F9 /* OWSBatchMessageProcessor.m */; }; C33FDCCC255A582000E217F9 /* NSString+SSK.h in Headers */ = {isa = PBXBuildFile; fileRef = C33FDB12255A580800E217F9 /* NSString+SSK.h */; settings = {ATTRIBUTES = (Public, ); }; }; - C33FDCCD255A582000E217F9 /* LKUnlinkDeviceMessage.m in Sources */ = {isa = PBXBuildFile; fileRef = C33FDB13255A580800E217F9 /* LKUnlinkDeviceMessage.m */; }; C33FDCCE255A582000E217F9 /* OWSMath.h in Headers */ = {isa = PBXBuildFile; fileRef = C33FDB14255A580800E217F9 /* OWSMath.h */; settings = {ATTRIBUTES = (Public, ); }; }; - C33FDCD0255A582000E217F9 /* OWSDisappearingMessagesConfigurationMessage.h in Headers */ = {isa = PBXBuildFile; fileRef = C33FDB16255A580800E217F9 /* OWSDisappearingMessagesConfigurationMessage.h */; settings = {ATTRIBUTES = (Public, ); }; }; C33FDCD1255A582000E217F9 /* FunctionalUtil.m in Sources */ = {isa = PBXBuildFile; fileRef = C33FDB17255A580800E217F9 /* FunctionalUtil.m */; }; - C33FDCD2255A582000E217F9 /* OWSSignalService.m in Sources */ = {isa = PBXBuildFile; fileRef = C33FDB18255A580800E217F9 /* OWSSignalService.m */; }; C33FDCD3255A582000E217F9 /* GroupUtilities.swift in Sources */ = {isa = PBXBuildFile; fileRef = C33FDB19255A580900E217F9 /* GroupUtilities.swift */; }; C33FDCD4255A582000E217F9 /* OWSPrimaryStorage+Calling.h in Headers */ = {isa = PBXBuildFile; fileRef = C33FDB1A255A580900E217F9 /* OWSPrimaryStorage+Calling.h */; settings = {ATTRIBUTES = (Public, ); }; }; - C33FDCD5255A582000E217F9 /* OWSDeviceProvisioningCodeService.h in Headers */ = {isa = PBXBuildFile; fileRef = C33FDB1B255A580900E217F9 /* OWSDeviceProvisioningCodeService.h */; settings = {ATTRIBUTES = (Public, ); }; }; C33FDCD6255A582000E217F9 /* UIImage+OWS.h in Headers */ = {isa = PBXBuildFile; fileRef = C33FDB1C255A580900E217F9 /* UIImage+OWS.h */; settings = {ATTRIBUTES = (Public, ); }; }; C33FDCD7255A582000E217F9 /* OWSReadReceiptManager.h in Headers */ = {isa = PBXBuildFile; fileRef = C33FDB1D255A580900E217F9 /* OWSReadReceiptManager.h */; settings = {ATTRIBUTES = (Public, ); }; }; C33FDCD8255A582000E217F9 /* OWSIncomingMessageFinder.m in Sources */ = {isa = PBXBuildFile; fileRef = C33FDB1E255A580900E217F9 /* OWSIncomingMessageFinder.m */; }; - C33FDCD9255A582000E217F9 /* DeviceLinkingUtilities.swift in Sources */ = {isa = PBXBuildFile; fileRef = C33FDB1F255A580900E217F9 /* DeviceLinkingUtilities.swift */; }; C33FDCDA255A582000E217F9 /* TSDatabaseSecondaryIndexes.m in Sources */ = {isa = PBXBuildFile; fileRef = C33FDB20255A580900E217F9 /* TSDatabaseSecondaryIndexes.m */; }; - C33FDCDB255A582000E217F9 /* OWS2FAManager.h in Headers */ = {isa = PBXBuildFile; fileRef = C33FDB21255A580900E217F9 /* OWS2FAManager.h */; settings = {ATTRIBUTES = (Public, ); }; }; C33FDCDC255A582000E217F9 /* OWSMediaUtils.swift in Sources */ = {isa = PBXBuildFile; fileRef = C33FDB22255A580900E217F9 /* OWSMediaUtils.swift */; }; - C33FDCDE255A582000E217F9 /* OWSOutgoingSentMessageTranscript.h in Headers */ = {isa = PBXBuildFile; fileRef = C33FDB24255A580900E217F9 /* OWSOutgoingSentMessageTranscript.h */; settings = {ATTRIBUTES = (Public, ); }; }; C33FDCDF255A582000E217F9 /* TSDatabaseSecondaryIndexes.h in Headers */ = {isa = PBXBuildFile; fileRef = C33FDB25255A580900E217F9 /* TSDatabaseSecondaryIndexes.h */; settings = {ATTRIBUTES = (Public, ); }; }; C33FDCE0255A582000E217F9 /* FingerprintProto.swift in Sources */ = {isa = PBXBuildFile; fileRef = C33FDB26255A580A00E217F9 /* FingerprintProto.swift */; }; - C33FDCE1255A582000E217F9 /* OWSEndSessionMessage.m in Sources */ = {isa = PBXBuildFile; fileRef = C33FDB27255A580A00E217F9 /* OWSEndSessionMessage.m */; }; - C33FDCE2255A582000E217F9 /* OWSOutgoingCallMessage.m in Sources */ = {isa = PBXBuildFile; fileRef = C33FDB28255A580A00E217F9 /* OWSOutgoingCallMessage.m */; }; C33FDCE3255A582000E217F9 /* NSData+Image.h in Headers */ = {isa = PBXBuildFile; fileRef = C33FDB29255A580A00E217F9 /* NSData+Image.h */; settings = {ATTRIBUTES = (Public, ); }; }; C33FDCE4255A582000E217F9 /* OWSIncompleteCallsJob.h in Headers */ = {isa = PBXBuildFile; fileRef = C33FDB2A255A580A00E217F9 /* OWSIncompleteCallsJob.h */; settings = {ATTRIBUTES = (Public, ); }; }; C33FDCE5255A582000E217F9 /* OWSProvisioningCipher.m in Sources */ = {isa = PBXBuildFile; fileRef = C33FDB2B255A580A00E217F9 /* OWSProvisioningCipher.m */; }; C33FDCE6255A582000E217F9 /* TSDatabaseView.h in Headers */ = {isa = PBXBuildFile; fileRef = C33FDB2C255A580A00E217F9 /* TSDatabaseView.h */; settings = {ATTRIBUTES = (Public, ); }; }; - C33FDCE7255A582000E217F9 /* OWSDevice.h in Headers */ = {isa = PBXBuildFile; fileRef = C33FDB2D255A580A00E217F9 /* OWSDevice.h */; settings = {ATTRIBUTES = (Public, ); }; }; - C33FDCE8255A582000E217F9 /* OWSEndSessionMessage.h in Headers */ = {isa = PBXBuildFile; fileRef = C33FDB2E255A580A00E217F9 /* OWSEndSessionMessage.h */; settings = {ATTRIBUTES = (Public, ); }; }; C33FDCE9255A582000E217F9 /* ContactsManagerProtocol.h in Headers */ = {isa = PBXBuildFile; fileRef = C33FDB2F255A580A00E217F9 /* ContactsManagerProtocol.h */; settings = {ATTRIBUTES = (Public, ); }; }; - C33FDCEA255A582000E217F9 /* OWSDevice.m in Sources */ = {isa = PBXBuildFile; fileRef = C33FDB30255A580A00E217F9 /* OWSDevice.m */; }; C33FDCEB255A582000E217F9 /* SSKEnvironment.h in Headers */ = {isa = PBXBuildFile; fileRef = C33FDB31255A580A00E217F9 /* SSKEnvironment.h */; settings = {ATTRIBUTES = (Public, ); }; }; C33FDCEC255A582000E217F9 /* SSKIncrementingIdFinder.swift in Sources */ = {isa = PBXBuildFile; fileRef = C33FDB32255A580A00E217F9 /* SSKIncrementingIdFinder.swift */; }; C33FDCED255A582000E217F9 /* Provisioning.pb.swift in Sources */ = {isa = PBXBuildFile; fileRef = C33FDB33255A580B00E217F9 /* Provisioning.pb.swift */; }; C33FDCEE255A582000E217F9 /* ClosedGroupPoller.swift in Sources */ = {isa = PBXBuildFile; fileRef = C33FDB34255A580B00E217F9 /* ClosedGroupPoller.swift */; }; - C33FDCEF255A582000E217F9 /* OWSContactDiscoveryOperation.swift in Sources */ = {isa = PBXBuildFile; fileRef = C33FDB35255A580B00E217F9 /* OWSContactDiscoveryOperation.swift */; }; C33FDCF0255A582000E217F9 /* Storage.swift in Sources */ = {isa = PBXBuildFile; fileRef = C33FDB36255A580B00E217F9 /* Storage.swift */; }; C33FDCF1255A582000E217F9 /* Storage+SnodeAPI.swift in Sources */ = {isa = PBXBuildFile; fileRef = C33FDB37255A580B00E217F9 /* Storage+SnodeAPI.swift */; }; C33FDCF2255A582000E217F9 /* OWSBackgroundTask.h in Headers */ = {isa = PBXBuildFile; fileRef = C33FDB38255A580B00E217F9 /* OWSBackgroundTask.h */; settings = {ATTRIBUTES = (Public, ); }; }; C33FDCF4255A582000E217F9 /* Poller.swift in Sources */ = {isa = PBXBuildFile; fileRef = C33FDB3A255A580B00E217F9 /* Poller.swift */; }; C33FDCF5255A582000E217F9 /* NSNotificationCenter+OWS.h in Headers */ = {isa = PBXBuildFile; fileRef = C33FDB3B255A580B00E217F9 /* NSNotificationCenter+OWS.h */; settings = {ATTRIBUTES = (Public, ); }; }; - C33FDCF7255A582000E217F9 /* OWSProfileKeyMessage.m in Sources */ = {isa = PBXBuildFile; fileRef = C33FDB3D255A580B00E217F9 /* OWSProfileKeyMessage.m */; }; C33FDCF9255A582000E217F9 /* String+SSK.swift in Sources */ = {isa = PBXBuildFile; fileRef = C33FDB3F255A580C00E217F9 /* String+SSK.swift */; }; C33FDCFA255A582000E217F9 /* SignalIOSProto.swift in Sources */ = {isa = PBXBuildFile; fileRef = C33FDB40255A580C00E217F9 /* SignalIOSProto.swift */; }; C33FDCFB255A582000E217F9 /* MIMETypeUtil.m in Sources */ = {isa = PBXBuildFile; fileRef = C33FDB41255A580C00E217F9 /* MIMETypeUtil.m */; }; - C33FDCFC255A582000E217F9 /* OWSCountryMetadata.h in Headers */ = {isa = PBXBuildFile; fileRef = C33FDB42255A580C00E217F9 /* OWSCountryMetadata.h */; settings = {ATTRIBUTES = (Public, ); }; }; C33FDCFD255A582000E217F9 /* YapDatabaseConnection+OWS.m in Sources */ = {isa = PBXBuildFile; fileRef = C33FDB43255A580C00E217F9 /* YapDatabaseConnection+OWS.m */; }; C33FDCFE255A582000E217F9 /* OWSContactsOutputStream.m in Sources */ = {isa = PBXBuildFile; fileRef = C33FDB44255A580C00E217F9 /* OWSContactsOutputStream.m */; }; C33FDCFF255A582000E217F9 /* NSString+SSK.m in Sources */ = {isa = PBXBuildFile; fileRef = C33FDB45255A580C00E217F9 /* NSString+SSK.m */; }; @@ -569,7 +466,6 @@ C33FDD01255A582000E217F9 /* OWSPrimaryStorage+PreKeyStore.h in Headers */ = {isa = PBXBuildFile; fileRef = C33FDB47255A580C00E217F9 /* OWSPrimaryStorage+PreKeyStore.h */; settings = {ATTRIBUTES = (Public, ); }; }; C33FDD02255A582000E217F9 /* TSOutgoingMessage.h in Headers */ = {isa = PBXBuildFile; fileRef = C33FDB48255A580C00E217F9 /* TSOutgoingMessage.h */; settings = {ATTRIBUTES = (Public, ); }; }; C33FDD03255A582000E217F9 /* WeakTimer.swift in Sources */ = {isa = PBXBuildFile; fileRef = C33FDB49255A580C00E217F9 /* WeakTimer.swift */; }; - C33FDD04255A582000E217F9 /* SignalServiceClient.swift in Sources */ = {isa = PBXBuildFile; fileRef = C33FDB4A255A580C00E217F9 /* SignalServiceClient.swift */; }; C33FDD05255A582000E217F9 /* OWSChunkedOutputStream.m in Sources */ = {isa = PBXBuildFile; fileRef = C33FDB4B255A580C00E217F9 /* OWSChunkedOutputStream.m */; }; C33FDD06255A582000E217F9 /* AppVersion.h in Headers */ = {isa = PBXBuildFile; fileRef = C33FDB4C255A580D00E217F9 /* AppVersion.h */; settings = {ATTRIBUTES = (Public, ); }; }; C33FDD09255A582000E217F9 /* SSKJobRecord.h in Headers */ = {isa = PBXBuildFile; fileRef = C33FDB4F255A580D00E217F9 /* SSKJobRecord.h */; settings = {ATTRIBUTES = (Public, ); }; }; @@ -580,22 +476,16 @@ C33FDD0E255A582000E217F9 /* DataSource.h in Headers */ = {isa = PBXBuildFile; fileRef = C33FDB54255A580D00E217F9 /* DataSource.h */; settings = {ATTRIBUTES = (Public, ); }; }; C33FDD0F255A582000E217F9 /* TSInvalidIdentityKeySendingErrorMessage.m in Sources */ = {isa = PBXBuildFile; fileRef = C33FDB55255A580D00E217F9 /* TSInvalidIdentityKeySendingErrorMessage.m */; }; C33FDD10255A582000E217F9 /* TSOutgoingMessage.m in Sources */ = {isa = PBXBuildFile; fileRef = C33FDB56255A580D00E217F9 /* TSOutgoingMessage.m */; }; - C33FDD11255A582000E217F9 /* OWSSyncContactsMessage.h in Headers */ = {isa = PBXBuildFile; fileRef = C33FDB57255A580D00E217F9 /* OWSSyncContactsMessage.h */; settings = {ATTRIBUTES = (Public, ); }; }; C33FDD12255A582000E217F9 /* OWSPrimaryStorage+Loki.m in Sources */ = {isa = PBXBuildFile; fileRef = C33FDB58255A580E00E217F9 /* OWSPrimaryStorage+Loki.m */; }; C33FDD13255A582000E217F9 /* OWSFailedAttachmentDownloadsJob.m in Sources */ = {isa = PBXBuildFile; fileRef = C33FDB59255A580E00E217F9 /* OWSFailedAttachmentDownloadsJob.m */; }; C33FDD14255A582000E217F9 /* OWSUDManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = C33FDB5A255A580E00E217F9 /* OWSUDManager.swift */; }; C33FDD15255A582000E217F9 /* YapDatabaseTransaction+OWS.m in Sources */ = {isa = PBXBuildFile; fileRef = C33FDB5B255A580E00E217F9 /* YapDatabaseTransaction+OWS.m */; }; C33FDD16255A582000E217F9 /* NSArray+Functional.h in Headers */ = {isa = PBXBuildFile; fileRef = C33FDB5C255A580E00E217F9 /* NSArray+Functional.h */; settings = {ATTRIBUTES = (Public, ); }; }; C33FDD17255A582000E217F9 /* TSErrorMessage_privateConstructor.h in Headers */ = {isa = PBXBuildFile; fileRef = C33FDB5D255A580E00E217F9 /* TSErrorMessage_privateConstructor.h */; settings = {ATTRIBUTES = (Public, ); }; }; - C33FDD18255A582000E217F9 /* ContactParser.swift in Sources */ = {isa = PBXBuildFile; fileRef = C33FDB5E255A580E00E217F9 /* ContactParser.swift */; }; C33FDD19255A582000E217F9 /* YapDatabaseConnection+OWS.h in Headers */ = {isa = PBXBuildFile; fileRef = C33FDB5F255A580E00E217F9 /* YapDatabaseConnection+OWS.h */; settings = {ATTRIBUTES = (Public, ); }; }; C33FDD1A255A582000E217F9 /* TSMessage.m in Sources */ = {isa = PBXBuildFile; fileRef = C33FDB60255A580E00E217F9 /* TSMessage.m */; }; - C33FDD1B255A582000E217F9 /* LKUnlinkDeviceMessage.h in Headers */ = {isa = PBXBuildFile; fileRef = C33FDB61255A580E00E217F9 /* LKUnlinkDeviceMessage.h */; settings = {ATTRIBUTES = (Public, ); }; }; C33FDD1C255A582000E217F9 /* OWSProvisioningCipher.h in Headers */ = {isa = PBXBuildFile; fileRef = C33FDB62255A580E00E217F9 /* OWSProvisioningCipher.h */; settings = {ATTRIBUTES = (Public, ); }; }; - C33FDD1D255A582000E217F9 /* OWSDynamicOutgoingMessage.h in Headers */ = {isa = PBXBuildFile; fileRef = C33FDB63255A580E00E217F9 /* OWSDynamicOutgoingMessage.h */; settings = {ATTRIBUTES = (Public, ); }; }; C33FDD1E255A582000E217F9 /* PreKeyRefreshOperation.swift in Sources */ = {isa = PBXBuildFile; fileRef = C33FDB64255A580E00E217F9 /* PreKeyRefreshOperation.swift */; }; - C33FDD1F255A582000E217F9 /* OWSSyncConfigurationMessage.h in Headers */ = {isa = PBXBuildFile; fileRef = C33FDB65255A580F00E217F9 /* OWSSyncConfigurationMessage.h */; settings = {ATTRIBUTES = (Public, ); }; }; - C33FDD20255A582000E217F9 /* ContactDiscoveryService.h in Headers */ = {isa = PBXBuildFile; fileRef = C33FDB66255A580F00E217F9 /* ContactDiscoveryService.h */; settings = {ATTRIBUTES = (Public, ); }; }; C33FDD21255A582000E217F9 /* OWSMediaGalleryFinder.h in Headers */ = {isa = PBXBuildFile; fileRef = C33FDB67255A580F00E217F9 /* OWSMediaGalleryFinder.h */; settings = {ATTRIBUTES = (Public, ); }; }; C33FDD22255A582000E217F9 /* ContentProxy.swift in Sources */ = {isa = PBXBuildFile; fileRef = C33FDB68255A580F00E217F9 /* ContentProxy.swift */; }; C33FDD23255A582000E217F9 /* FeatureFlags.swift in Sources */ = {isa = PBXBuildFile; fileRef = C33FDB69255A580F00E217F9 /* FeatureFlags.swift */; }; @@ -603,55 +493,37 @@ C33FDD25255A582000E217F9 /* LKUserDefaults.swift in Sources */ = {isa = PBXBuildFile; fileRef = C33FDB6B255A580F00E217F9 /* LKUserDefaults.swift */; }; C33FDD26255A582000E217F9 /* NSNotificationCenter+OWS.m in Sources */ = {isa = PBXBuildFile; fileRef = C33FDB6C255A580F00E217F9 /* NSNotificationCenter+OWS.m */; }; C33FDD27255A582000E217F9 /* TSPreKeyManager.h in Headers */ = {isa = PBXBuildFile; fileRef = C33FDB6D255A580F00E217F9 /* TSPreKeyManager.h */; settings = {ATTRIBUTES = (Public, ); }; }; - C33FDD28255A582000E217F9 /* SSKProtoPrekeyBundleMessage+Loki.swift in Sources */ = {isa = PBXBuildFile; fileRef = C33FDB6E255A580F00E217F9 /* SSKProtoPrekeyBundleMessage+Loki.swift */; }; C33FDD29255A582000E217F9 /* OWSOutgoingReceiptManager.m in Sources */ = {isa = PBXBuildFile; fileRef = C33FDB6F255A580F00E217F9 /* OWSOutgoingReceiptManager.m */; }; C33FDD2A255A582000E217F9 /* OWSMessageServiceParams.m in Sources */ = {isa = PBXBuildFile; fileRef = C33FDB70255A580F00E217F9 /* OWSMessageServiceParams.m */; }; C33FDD2B255A582000E217F9 /* OWSMediaGalleryFinder.m in Sources */ = {isa = PBXBuildFile; fileRef = C33FDB71255A581000E217F9 /* OWSMediaGalleryFinder.m */; }; - C33FDD2C255A582000E217F9 /* DeviceLinkIndex.swift in Sources */ = {isa = PBXBuildFile; fileRef = C33FDB72255A581000E217F9 /* DeviceLinkIndex.swift */; }; C33FDD2D255A582000E217F9 /* TSGroupModel.m in Sources */ = {isa = PBXBuildFile; fileRef = C33FDB73255A581000E217F9 /* TSGroupModel.m */; }; C33FDD2E255A582000E217F9 /* TSInvalidIdentityKeyErrorMessage.m in Sources */ = {isa = PBXBuildFile; fileRef = C33FDB74255A581000E217F9 /* TSInvalidIdentityKeyErrorMessage.m */; }; C33FDD2F255A582000E217F9 /* AppReadiness.m in Sources */ = {isa = PBXBuildFile; fileRef = C33FDB75255A581000E217F9 /* AppReadiness.m */; }; - C33FDD30255A582000E217F9 /* ClosedGroupUtilities.swift in Sources */ = {isa = PBXBuildFile; fileRef = C33FDB76255A581000E217F9 /* ClosedGroupUtilities.swift */; }; C33FDD31255A582000E217F9 /* NSUserDefaults+OWS.m in Sources */ = {isa = PBXBuildFile; fileRef = C33FDB77255A581000E217F9 /* NSUserDefaults+OWS.m */; }; C33FDD32255A582000E217F9 /* OWSOperation.m in Sources */ = {isa = PBXBuildFile; fileRef = C33FDB78255A581000E217F9 /* OWSOperation.m */; }; - C33FDD33255A582000E217F9 /* PhoneNumberUtil.m in Sources */ = {isa = PBXBuildFile; fileRef = C33FDB79255A581000E217F9 /* PhoneNumberUtil.m */; }; C33FDD34255A582000E217F9 /* NotificationsProtocol.h in Headers */ = {isa = PBXBuildFile; fileRef = C33FDB7A255A581000E217F9 /* NotificationsProtocol.h */; settings = {ATTRIBUTES = (Public, ); }; }; - C33FDD36255A582000E217F9 /* OWSVerificationStateChangeMessage.h in Headers */ = {isa = PBXBuildFile; fileRef = C33FDB7C255A581000E217F9 /* OWSVerificationStateChangeMessage.h */; settings = {ATTRIBUTES = (Public, ); }; }; C33FDD38255A582000E217F9 /* TSPreKeyManager.m in Sources */ = {isa = PBXBuildFile; fileRef = C33FDB7E255A581100E217F9 /* TSPreKeyManager.m */; }; C33FDD39255A582000E217F9 /* FullTextSearchFinder.swift in Sources */ = {isa = PBXBuildFile; fileRef = C33FDB7F255A581100E217F9 /* FullTextSearchFinder.swift */; }; C33FDD3A255A582000E217F9 /* Notification+Loki.swift in Sources */ = {isa = PBXBuildFile; fileRef = C33FDB80255A581100E217F9 /* Notification+Loki.swift */; }; C33FDD3B255A582000E217F9 /* UIImage+OWS.m in Sources */ = {isa = PBXBuildFile; fileRef = C33FDB81255A581100E217F9 /* UIImage+OWS.m */; }; - C33FDD3C255A582000E217F9 /* MessageWrapper.swift in Sources */ = {isa = PBXBuildFile; fileRef = C33FDB82255A581100E217F9 /* MessageWrapper.swift */; }; C33FDD3D255A582000E217F9 /* TSQuotedMessage.m in Sources */ = {isa = PBXBuildFile; fileRef = C33FDB83255A581100E217F9 /* TSQuotedMessage.m */; }; - C33FDD3E255A582000E217F9 /* OWSIncomingSentMessageTranscript.m in Sources */ = {isa = PBXBuildFile; fileRef = C33FDB84255A581100E217F9 /* OWSIncomingSentMessageTranscript.m */; }; C33FDD3F255A582000E217F9 /* AppContext.m in Sources */ = {isa = PBXBuildFile; fileRef = C33FDB85255A581100E217F9 /* AppContext.m */; }; - C33FDD40255A582000E217F9 /* OWSRequestFactory.m in Sources */ = {isa = PBXBuildFile; fileRef = C33FDB86255A581100E217F9 /* OWSRequestFactory.m */; }; C33FDD41255A582000E217F9 /* JobQueue.swift in Sources */ = {isa = PBXBuildFile; fileRef = C33FDB87255A581100E217F9 /* JobQueue.swift */; }; C33FDD42255A582000E217F9 /* TSAccountManager.m in Sources */ = {isa = PBXBuildFile; fileRef = C33FDB88255A581200E217F9 /* TSAccountManager.m */; }; - C33FDD43255A582000E217F9 /* FileServerAPI+Deprecated.swift in Sources */ = {isa = PBXBuildFile; fileRef = C33FDB89255A581200E217F9 /* FileServerAPI+Deprecated.swift */; }; C33FDD44255A582000E217F9 /* AppContext.h in Headers */ = {isa = PBXBuildFile; fileRef = C33FDB8A255A581200E217F9 /* AppContext.h */; settings = {ATTRIBUTES = (Public, ); }; }; C33FDD45255A582000E217F9 /* Storage+SessionManagement.swift in Sources */ = {isa = PBXBuildFile; fileRef = C33FDB8B255A581200E217F9 /* Storage+SessionManagement.swift */; }; C33FDD46255A582000E217F9 /* TSInvalidIdentityKeyErrorMessage.h in Headers */ = {isa = PBXBuildFile; fileRef = C33FDB8C255A581200E217F9 /* TSInvalidIdentityKeyErrorMessage.h */; settings = {ATTRIBUTES = (Public, ); }; }; - C33FDD47255A582000E217F9 /* DeviceLinkingSessionDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = C33FDB8D255A581200E217F9 /* DeviceLinkingSessionDelegate.swift */; }; - C33FDD48255A582000E217F9 /* OWSContact+Private.h in Headers */ = {isa = PBXBuildFile; fileRef = C33FDB8E255A581200E217F9 /* OWSContact+Private.h */; settings = {ATTRIBUTES = (Public, ); }; }; C33FDD49255A582000E217F9 /* ParamParser.swift in Sources */ = {isa = PBXBuildFile; fileRef = C33FDB8F255A581200E217F9 /* ParamParser.swift */; }; C33FDD4B255A582000E217F9 /* ProtoUtils.h in Headers */ = {isa = PBXBuildFile; fileRef = C33FDB91255A581200E217F9 /* ProtoUtils.h */; settings = {ATTRIBUTES = (Public, ); }; }; - C33FDD4C255A582000E217F9 /* OWSDeviceProvisioningService.m in Sources */ = {isa = PBXBuildFile; fileRef = C33FDB92255A581200E217F9 /* OWSDeviceProvisioningService.m */; }; C33FDD4D255A582000E217F9 /* PreKeyBundle+jsonDict.m in Sources */ = {isa = PBXBuildFile; fileRef = C33FDB93255A581200E217F9 /* PreKeyBundle+jsonDict.m */; }; C33FDD4E255A582000E217F9 /* TSAccountManager.h in Headers */ = {isa = PBXBuildFile; fileRef = C33FDB94255A581300E217F9 /* TSAccountManager.h */; settings = {ATTRIBUTES = (Public, ); }; }; C33FDD4F255A582000E217F9 /* Storage+Collections.swift in Sources */ = {isa = PBXBuildFile; fileRef = C33FDB95255A581300E217F9 /* Storage+Collections.swift */; }; - C33FDD50255A582000E217F9 /* OWSReadReceiptsForLinkedDevicesMessage.m in Sources */ = {isa = PBXBuildFile; fileRef = C33FDB96255A581300E217F9 /* OWSReadReceiptsForLinkedDevicesMessage.m */; }; - C33FDD51255A582000E217F9 /* OWSDisappearingMessagesConfigurationMessage.m in Sources */ = {isa = PBXBuildFile; fileRef = C33FDB97255A581300E217F9 /* OWSDisappearingMessagesConfigurationMessage.m */; }; - C33FDD52255A582000E217F9 /* DeviceNames.swift in Sources */ = {isa = PBXBuildFile; fileRef = C33FDB98255A581300E217F9 /* DeviceNames.swift */; }; C33FDD53255A582000E217F9 /* OWSPrimaryStorage+keyFromIntLong.m in Sources */ = {isa = PBXBuildFile; fileRef = C33FDB99255A581300E217F9 /* OWSPrimaryStorage+keyFromIntLong.m */; }; - C33FDD54255A582000E217F9 /* OWS2FAManager.m in Sources */ = {isa = PBXBuildFile; fileRef = C33FDB9A255A581300E217F9 /* OWS2FAManager.m */; }; C33FDD56255A582000E217F9 /* TSIncomingMessage.h in Headers */ = {isa = PBXBuildFile; fileRef = C33FDB9C255A581300E217F9 /* TSIncomingMessage.h */; settings = {ATTRIBUTES = (Public, ); }; }; C33FDD57255A582000E217F9 /* OWSCallMessageHandler.h in Headers */ = {isa = PBXBuildFile; fileRef = C33FDB9D255A581300E217F9 /* OWSCallMessageHandler.h */; settings = {ATTRIBUTES = (Public, ); }; }; C33FDD58255A582000E217F9 /* TSAttachmentPointer.m in Sources */ = {isa = PBXBuildFile; fileRef = C33FDB9E255A581400E217F9 /* TSAttachmentPointer.m */; }; - C33FDD59255A582000E217F9 /* TTLUtilities.swift in Sources */ = {isa = PBXBuildFile; fileRef = C33FDB9F255A581400E217F9 /* TTLUtilities.swift */; }; C33FDD5A255A582000E217F9 /* TSStorageHeaders.h in Headers */ = {isa = PBXBuildFile; fileRef = C33FDBA0255A581400E217F9 /* TSStorageHeaders.h */; settings = {ATTRIBUTES = (Public, ); }; }; C33FDD5B255A582000E217F9 /* OWSOperation.h in Headers */ = {isa = PBXBuildFile; fileRef = C33FDBA1255A581400E217F9 /* OWSOperation.h */; settings = {ATTRIBUTES = (Public, ); }; }; - C33FDD5C255A582000E217F9 /* PhoneNumber.h in Headers */ = {isa = PBXBuildFile; fileRef = C33FDBA2255A581400E217F9 /* PhoneNumber.h */; settings = {ATTRIBUTES = (Public, ); }; }; C33FDD5D255A582000E217F9 /* SessionManagementProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = C33FDBA3255A581400E217F9 /* SessionManagementProtocol.swift */; }; C33FDD5E255A582000E217F9 /* OWSDisappearingMessagesConfiguration.m in Sources */ = {isa = PBXBuildFile; fileRef = C33FDBA4255A581400E217F9 /* OWSDisappearingMessagesConfiguration.m */; }; C33FDD5F255A582000E217F9 /* SignalServiceProfile.swift in Sources */ = {isa = PBXBuildFile; fileRef = C33FDBA5255A581400E217F9 /* SignalServiceProfile.swift */; }; @@ -659,15 +531,10 @@ C33FDD62255A582000E217F9 /* OWSLinkPreview.swift in Sources */ = {isa = PBXBuildFile; fileRef = C33FDBA8255A581500E217F9 /* OWSLinkPreview.swift */; }; C33FDD63255A582000E217F9 /* OWSIdentityManager.m in Sources */ = {isa = PBXBuildFile; fileRef = C33FDBA9255A581500E217F9 /* OWSIdentityManager.m */; }; C33FDD65255A582000E217F9 /* OWSFileSystem.h in Headers */ = {isa = PBXBuildFile; fileRef = C33FDBAB255A581500E217F9 /* OWSFileSystem.h */; settings = {ATTRIBUTES = (Public, ); }; }; - C33FDD66255A582000E217F9 /* Data+SecureRandom.swift in Sources */ = {isa = PBXBuildFile; fileRef = C33FDBAC255A581500E217F9 /* Data+SecureRandom.swift */; }; C33FDD67255A582000E217F9 /* OWSRecordTranscriptJob.h in Headers */ = {isa = PBXBuildFile; fileRef = C33FDBAD255A581500E217F9 /* OWSRecordTranscriptJob.h */; settings = {ATTRIBUTES = (Public, ); }; }; C33FDD68255A582000E217F9 /* SignalAccount.h in Headers */ = {isa = PBXBuildFile; fileRef = C33FDBAE255A581500E217F9 /* SignalAccount.h */; settings = {ATTRIBUTES = (Public, ); }; }; - C33FDD69255A582000E217F9 /* OWSAnalyticsEvents.m in Sources */ = {isa = PBXBuildFile; fileRef = C33FDBAF255A581500E217F9 /* OWSAnalyticsEvents.m */; }; C33FDD6A255A582000E217F9 /* TSErrorMessage.h in Headers */ = {isa = PBXBuildFile; fileRef = C33FDBB0255A581500E217F9 /* TSErrorMessage.h */; settings = {ATTRIBUTES = (Public, ); }; }; - C33FDD6B255A582000E217F9 /* TSSocketManager.h in Headers */ = {isa = PBXBuildFile; fileRef = C33FDBB1255A581500E217F9 /* TSSocketManager.h */; settings = {ATTRIBUTES = (Public, ); }; }; - C33FDD6D255A582000E217F9 /* OWSOutgoingNullMessage.h in Headers */ = {isa = PBXBuildFile; fileRef = C33FDBB3255A581500E217F9 /* OWSOutgoingNullMessage.h */; settings = {ATTRIBUTES = (Public, ); }; }; C33FDD6E255A582000E217F9 /* NSURLSessionDataTask+StatusCode.m in Sources */ = {isa = PBXBuildFile; fileRef = C33FDBB4255A581600E217F9 /* NSURLSessionDataTask+StatusCode.m */; }; - C33FDD6F255A582000E217F9 /* OWSSyncGroupsMessage.h in Headers */ = {isa = PBXBuildFile; fileRef = C33FDBB5255A581600E217F9 /* OWSSyncGroupsMessage.h */; settings = {ATTRIBUTES = (Public, ); }; }; C33FDD70255A582000E217F9 /* DataSource.m in Sources */ = {isa = PBXBuildFile; fileRef = C33FDBB6255A581600E217F9 /* DataSource.m */; }; C33FDD71255A582000E217F9 /* SignalRecipient.m in Sources */ = {isa = PBXBuildFile; fileRef = C33FDBB7255A581600E217F9 /* SignalRecipient.m */; }; C33FDD72255A582000E217F9 /* TSThread.m in Sources */ = {isa = PBXBuildFile; fileRef = C33FDBB8255A581600E217F9 /* TSThread.m */; }; @@ -675,94 +542,55 @@ C33FDD74255A582000E217F9 /* OWSPrimaryStorage+keyFromIntLong.h in Headers */ = {isa = PBXBuildFile; fileRef = C33FDBBA255A581600E217F9 /* OWSPrimaryStorage+keyFromIntLong.h */; settings = {ATTRIBUTES = (Public, ); }; }; C33FDD75255A582000E217F9 /* OWSPrimaryStorage+Loki.h in Headers */ = {isa = PBXBuildFile; fileRef = C33FDBBB255A581600E217F9 /* OWSPrimaryStorage+Loki.h */; settings = {ATTRIBUTES = (Public, ); }; }; C33FDD76255A582000E217F9 /* SSKKeychainStorage.swift in Sources */ = {isa = PBXBuildFile; fileRef = C33FDBBC255A581600E217F9 /* SSKKeychainStorage.swift */; }; - C33FDD77255A582000E217F9 /* OWSAddToProfileWhitelistOfferMessage.h in Headers */ = {isa = PBXBuildFile; fileRef = C33FDBBD255A581600E217F9 /* OWSAddToProfileWhitelistOfferMessage.h */; settings = {ATTRIBUTES = (Public, ); }; }; C33FDD79255A582000E217F9 /* OWSHTTPSecurityPolicy.m in Sources */ = {isa = PBXBuildFile; fileRef = C33FDBBF255A581700E217F9 /* OWSHTTPSecurityPolicy.m */; }; - C33FDD7A255A582000E217F9 /* OWSRequestFactory.h in Headers */ = {isa = PBXBuildFile; fileRef = C33FDBC0255A581700E217F9 /* OWSRequestFactory.h */; settings = {ATTRIBUTES = (Public, ); }; }; C33FDD7B255A582000E217F9 /* GeneralUtilities.swift in Sources */ = {isa = PBXBuildFile; fileRef = C33FDBC1255A581700E217F9 /* GeneralUtilities.swift */; }; C33FDD7C255A582000E217F9 /* SSKAsserts.h in Headers */ = {isa = PBXBuildFile; fileRef = C33FDBC2255A581700E217F9 /* SSKAsserts.h */; settings = {ATTRIBUTES = (Public, ); }; }; - C33FDD7D255A582000E217F9 /* AnyPromise+Conversion.swift in Sources */ = {isa = PBXBuildFile; fileRef = C33FDBC3255A581700E217F9 /* AnyPromise+Conversion.swift */; }; - C33FDD7E255A582000E217F9 /* TypingIndicatorMessage.swift in Sources */ = {isa = PBXBuildFile; fileRef = C33FDBC4255A581700E217F9 /* TypingIndicatorMessage.swift */; }; - C33FDD82255A582000E217F9 /* OWSFingerprint.h in Headers */ = {isa = PBXBuildFile; fileRef = C33FDBC8255A581700E217F9 /* OWSFingerprint.h */; settings = {ATTRIBUTES = (Public, ); }; }; C33FDD83255A582000E217F9 /* CreatePreKeysOperation.swift in Sources */ = {isa = PBXBuildFile; fileRef = C33FDBC9255A581700E217F9 /* CreatePreKeysOperation.swift */; }; C33FDD84255A582000E217F9 /* LKGroupUtilities.h in Headers */ = {isa = PBXBuildFile; fileRef = C33FDBCA255A581700E217F9 /* LKGroupUtilities.h */; settings = {ATTRIBUTES = (Public, ); }; }; C33FDD85255A582000E217F9 /* TSInvalidIdentityKeyReceivingErrorMessage.m in Sources */ = {isa = PBXBuildFile; fileRef = C33FDBCB255A581800E217F9 /* TSInvalidIdentityKeyReceivingErrorMessage.m */; }; C33FDD88255A582000E217F9 /* OWSMessageServiceParams.h in Headers */ = {isa = PBXBuildFile; fileRef = C33FDBCE255A581800E217F9 /* OWSMessageServiceParams.h */; settings = {ATTRIBUTES = (Public, ); }; }; - C33FDD89255A582000E217F9 /* OWSFingerprintBuilder.h in Headers */ = {isa = PBXBuildFile; fileRef = C33FDBCF255A581800E217F9 /* OWSFingerprintBuilder.h */; settings = {ATTRIBUTES = (Public, ); }; }; - C33FDD8A255A582000E217F9 /* OnionRequestAPI+Encryption.swift in Sources */ = {isa = PBXBuildFile; fileRef = C33FDBD0255A581800E217F9 /* OnionRequestAPI+Encryption.swift */; }; - C33FDD8B255A582000E217F9 /* DeviceLink.swift in Sources */ = {isa = PBXBuildFile; fileRef = C33FDBD1255A581800E217F9 /* DeviceLink.swift */; }; - C33FDD8C255A582000E217F9 /* OWSUnknownContactBlockOfferMessage.h in Headers */ = {isa = PBXBuildFile; fileRef = C33FDBD2255A581800E217F9 /* OWSUnknownContactBlockOfferMessage.h */; settings = {ATTRIBUTES = (Public, ); }; }; C33FDD8D255A582000E217F9 /* OWSSignalAddress.swift in Sources */ = {isa = PBXBuildFile; fileRef = C33FDBD3255A581800E217F9 /* OWSSignalAddress.swift */; }; - C33FDD8E255A582000E217F9 /* OWSBatchMessageProcessor.h in Headers */ = {isa = PBXBuildFile; fileRef = C33FDBD4255A581900E217F9 /* OWSBatchMessageProcessor.h */; settings = {ATTRIBUTES = (Public, ); }; }; - C33FDD8F255A582000E217F9 /* OWSOutgoingSentMessageTranscript.m in Sources */ = {isa = PBXBuildFile; fileRef = C33FDBD5255A581900E217F9 /* OWSOutgoingSentMessageTranscript.m */; }; C33FDD90255A582000E217F9 /* OWSUploadOperation.h in Headers */ = {isa = PBXBuildFile; fileRef = C33FDBD6255A581900E217F9 /* OWSUploadOperation.h */; settings = {ATTRIBUTES = (Public, ); }; }; C33FDD91255A582000E217F9 /* OWSMessageUtils.m in Sources */ = {isa = PBXBuildFile; fileRef = C33FDBD7255A581900E217F9 /* OWSMessageUtils.m */; }; C33FDD92255A582000E217F9 /* SignalIOS.pb.swift in Sources */ = {isa = PBXBuildFile; fileRef = C33FDBD8255A581900E217F9 /* SignalIOS.pb.swift */; }; - C33FDD93255A582000E217F9 /* OWSVerificationStateSyncMessage.h in Headers */ = {isa = PBXBuildFile; fileRef = C33FDBD9255A581900E217F9 /* OWSVerificationStateSyncMessage.h */; settings = {ATTRIBUTES = (Public, ); }; }; C33FDD94255A582000E217F9 /* Dictionary+Description.swift in Sources */ = {isa = PBXBuildFile; fileRef = C33FDBDA255A581900E217F9 /* Dictionary+Description.swift */; }; - C33FDD95255A582000E217F9 /* OWSDevicesService.h in Headers */ = {isa = PBXBuildFile; fileRef = C33FDBDB255A581900E217F9 /* OWSDevicesService.h */; settings = {ATTRIBUTES = (Public, ); }; }; - C33FDD96255A582000E217F9 /* ContactDiscoveryService.m in Sources */ = {isa = PBXBuildFile; fileRef = C33FDBDC255A581900E217F9 /* ContactDiscoveryService.m */; }; C33FDD97255A582000E217F9 /* OWSDisappearingMessagesJob.m in Sources */ = {isa = PBXBuildFile; fileRef = C33FDBDD255A581900E217F9 /* OWSDisappearingMessagesJob.m */; }; C33FDD98255A582000E217F9 /* LokiPushNotificationManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = C33FDBDE255A581900E217F9 /* LokiPushNotificationManager.swift */; }; - C33FDD99255A582000E217F9 /* LKSyncOpenGroupsMessage.m in Sources */ = {isa = PBXBuildFile; fileRef = C33FDBDF255A581A00E217F9 /* LKSyncOpenGroupsMessage.m */; }; - C33FDD9A255A582000E217F9 /* OWSBlockedPhoneNumbersMessage.m in Sources */ = {isa = PBXBuildFile; fileRef = C33FDBE0255A581A00E217F9 /* OWSBlockedPhoneNumbersMessage.m */; }; C33FDD9B255A582000E217F9 /* LKGroupUtilities.m in Sources */ = {isa = PBXBuildFile; fileRef = C33FDBE1255A581A00E217F9 /* LKGroupUtilities.m */; }; - C33FDD9C255A582000E217F9 /* OWSUnknownContactBlockOfferMessage.m in Sources */ = {isa = PBXBuildFile; fileRef = C33FDBE2255A581A00E217F9 /* OWSUnknownContactBlockOfferMessage.m */; }; - C33FDD9D255A582000E217F9 /* CDSSigningCertificate.h in Headers */ = {isa = PBXBuildFile; fileRef = C33FDBE3255A581A00E217F9 /* CDSSigningCertificate.h */; settings = {ATTRIBUTES = (Public, ); }; }; - C33FDD9E255A582000E217F9 /* OWSOutgoingSyncMessage.h in Headers */ = {isa = PBXBuildFile; fileRef = C33FDBE4255A581A00E217F9 /* OWSOutgoingSyncMessage.h */; settings = {ATTRIBUTES = (Public, ); }; }; - C33FDD9F255A582000E217F9 /* OWSDevicesService.m in Sources */ = {isa = PBXBuildFile; fileRef = C33FDBE5255A581A00E217F9 /* OWSDevicesService.m */; }; - C33FDDA0255A582000E217F9 /* OWSOutgoingNullMessage.m in Sources */ = {isa = PBXBuildFile; fileRef = C33FDBE6255A581A00E217F9 /* OWSOutgoingNullMessage.m */; }; - C33FDDA1255A582000E217F9 /* NSTimer+OWS.h in Headers */ = {isa = PBXBuildFile; fileRef = C33FDBE7255A581A00E217F9 /* NSTimer+OWS.h */; settings = {ATTRIBUTES = (Public, ); }; }; C33FDDA2255A582000E217F9 /* Storage+OnionRequests.swift in Sources */ = {isa = PBXBuildFile; fileRef = C33FDBE8255A581A00E217F9 /* Storage+OnionRequests.swift */; }; C33FDDA3255A582000E217F9 /* TSInteraction.m in Sources */ = {isa = PBXBuildFile; fileRef = C33FDBE9255A581A00E217F9 /* TSInteraction.m */; }; - C33FDDA4255A582000E217F9 /* SessionRequestMessage.swift in Sources */ = {isa = PBXBuildFile; fileRef = C33FDBEA255A581A00E217F9 /* SessionRequestMessage.swift */; }; C33FDDA5255A582000E217F9 /* OWSBlockingManager.h in Headers */ = {isa = PBXBuildFile; fileRef = C33FDBEB255A581B00E217F9 /* OWSBlockingManager.h */; settings = {ATTRIBUTES = (Public, ); }; }; C33FDDA6255A582000E217F9 /* OWSRecipientIdentity.m in Sources */ = {isa = PBXBuildFile; fileRef = C33FDBEC255A581B00E217F9 /* OWSRecipientIdentity.m */; }; - C33FDDA7255A582000E217F9 /* ClosedGroupParser.swift in Sources */ = {isa = PBXBuildFile; fileRef = C33FDBED255A581B00E217F9 /* ClosedGroupParser.swift */; }; - C33FDDA8255A582000E217F9 /* ClosedGroupUpdateMessage.swift in Sources */ = {isa = PBXBuildFile; fileRef = C33FDBEE255A581B00E217F9 /* ClosedGroupUpdateMessage.swift */; }; C33FDDA9255A582000E217F9 /* TSStorageKeys.h in Headers */ = {isa = PBXBuildFile; fileRef = C33FDBEF255A581B00E217F9 /* TSStorageKeys.h */; settings = {ATTRIBUTES = (Public, ); }; }; C33FDDAA255A582000E217F9 /* LokiDatabaseUtilities.swift in Sources */ = {isa = PBXBuildFile; fileRef = C33FDBF0255A581B00E217F9 /* LokiDatabaseUtilities.swift */; }; C33FDDAB255A582000E217F9 /* OWSIdentityManager.h in Headers */ = {isa = PBXBuildFile; fileRef = C33FDBF1255A581B00E217F9 /* OWSIdentityManager.h */; settings = {ATTRIBUTES = (Public, ); }; }; - C33FDDAD255A582000E217F9 /* OWSBlockedPhoneNumbersMessage.h in Headers */ = {isa = PBXBuildFile; fileRef = C33FDBF3255A581B00E217F9 /* OWSBlockedPhoneNumbersMessage.h */; }; C33FDDAE255A582000E217F9 /* DisplayNameUtilities2.swift in Sources */ = {isa = PBXBuildFile; fileRef = C33FDBF4255A581B00E217F9 /* DisplayNameUtilities2.swift */; }; - C33FDDAF255A582000E217F9 /* OWSDeviceProvisioningService.h in Headers */ = {isa = PBXBuildFile; fileRef = C33FDBF5255A581B00E217F9 /* OWSDeviceProvisioningService.h */; settings = {ATTRIBUTES = (Public, ); }; }; C33FDDB0255A582000E217F9 /* NSURLSessionDataTask+StatusCode.h in Headers */ = {isa = PBXBuildFile; fileRef = C33FDBF6255A581C00E217F9 /* NSURLSessionDataTask+StatusCode.h */; settings = {ATTRIBUTES = (Public, ); }; }; C33FDDB1255A582000E217F9 /* OWSIncompleteCallsJob.m in Sources */ = {isa = PBXBuildFile; fileRef = C33FDBF7255A581C00E217F9 /* OWSIncompleteCallsJob.m */; }; C33FDDB2255A582000E217F9 /* NSArray+OWS.h in Headers */ = {isa = PBXBuildFile; fileRef = C33FDBF8255A581C00E217F9 /* NSArray+OWS.h */; settings = {ATTRIBUTES = (Public, ); }; }; C33FDDB3255A582000E217F9 /* OWSError.h in Headers */ = {isa = PBXBuildFile; fileRef = C33FDBF9255A581C00E217F9 /* OWSError.h */; settings = {ATTRIBUTES = (Public, ); }; }; - C33FDDB4255A582000E217F9 /* PhoneNumberUtil.h in Headers */ = {isa = PBXBuildFile; fileRef = C33FDBFA255A581C00E217F9 /* PhoneNumberUtil.h */; settings = {ATTRIBUTES = (Public, ); }; }; C33FDDB5255A582000E217F9 /* Storage+PublicChats.swift in Sources */ = {isa = PBXBuildFile; fileRef = C33FDBFB255A581C00E217F9 /* Storage+PublicChats.swift */; }; C33FDDB7255A582000E217F9 /* OWSPrimaryStorage+Calling.m in Sources */ = {isa = PBXBuildFile; fileRef = C33FDBFD255A581C00E217F9 /* OWSPrimaryStorage+Calling.m */; }; C33FDDB8255A582000E217F9 /* NSSet+Functional.h in Headers */ = {isa = PBXBuildFile; fileRef = C33FDBFE255A581C00E217F9 /* NSSet+Functional.h */; settings = {ATTRIBUTES = (Public, ); }; }; - C33FDDB9255A582000E217F9 /* OWSOutgoingSyncMessage.m in Sources */ = {isa = PBXBuildFile; fileRef = C33FDBFF255A581C00E217F9 /* OWSOutgoingSyncMessage.m */; }; - C33FDDBA255A582000E217F9 /* OWSSyncGroupsMessage.m in Sources */ = {isa = PBXBuildFile; fileRef = C33FDC00255A581C00E217F9 /* OWSSyncGroupsMessage.m */; }; C33FDDBB255A582000E217F9 /* TSGroupThread.m in Sources */ = {isa = PBXBuildFile; fileRef = C33FDC01255A581C00E217F9 /* TSGroupThread.m */; }; C33FDDBC255A582000E217F9 /* OWSPrimaryStorage.m in Sources */ = {isa = PBXBuildFile; fileRef = C33FDC02255A581D00E217F9 /* OWSPrimaryStorage.m */; }; C33FDDBD255A582000E217F9 /* ByteParser.h in Headers */ = {isa = PBXBuildFile; fileRef = C33FDC03255A581D00E217F9 /* ByteParser.h */; settings = {ATTRIBUTES = (Public, ); }; }; - C33FDDBE255A582000E217F9 /* DecryptionUtilities.swift in Sources */ = {isa = PBXBuildFile; fileRef = C33FDC04255A581D00E217F9 /* DecryptionUtilities.swift */; }; C33FDDBF255A582000E217F9 /* OWSDisappearingMessagesFinder.h in Headers */ = {isa = PBXBuildFile; fileRef = C33FDC05255A581D00E217F9 /* OWSDisappearingMessagesFinder.h */; settings = {ATTRIBUTES = (Public, ); }; }; C33FDDC0255A582000E217F9 /* SignalAccount.m in Sources */ = {isa = PBXBuildFile; fileRef = C33FDC06255A581D00E217F9 /* SignalAccount.m */; }; C33FDDC1255A582000E217F9 /* Storage+ClosedGroups.swift in Sources */ = {isa = PBXBuildFile; fileRef = C33FDC07255A581D00E217F9 /* Storage+ClosedGroups.swift */; }; C33FDDC2255A582000E217F9 /* SignalService.pb.swift in Sources */ = {isa = PBXBuildFile; fileRef = C33FDC08255A581D00E217F9 /* SignalService.pb.swift */; }; - C33FDDC3255A582000E217F9 /* AccountServiceClient.swift in Sources */ = {isa = PBXBuildFile; fileRef = C33FDC09255A581D00E217F9 /* AccountServiceClient.swift */; }; - C33FDDC4255A582000E217F9 /* OWSContact.m in Sources */ = {isa = PBXBuildFile; fileRef = C33FDC0A255A581D00E217F9 /* OWSContact.m */; }; C33FDDC5255A582000E217F9 /* OWSError.m in Sources */ = {isa = PBXBuildFile; fileRef = C33FDC0B255A581D00E217F9 /* OWSError.m */; }; C33FDDC6255A582000E217F9 /* TSInfoMessage.m in Sources */ = {isa = PBXBuildFile; fileRef = C33FDC0C255A581E00E217F9 /* TSInfoMessage.m */; }; - C33FDDC7255A582000E217F9 /* OWSProvisioningMessage.h in Headers */ = {isa = PBXBuildFile; fileRef = C33FDC0D255A581E00E217F9 /* OWSProvisioningMessage.h */; settings = {ATTRIBUTES = (Public, ); }; }; - C33FDDC9255A582000E217F9 /* LKDeviceLinkMessage.h in Headers */ = {isa = PBXBuildFile; fileRef = C33FDC0F255A581E00E217F9 /* LKDeviceLinkMessage.h */; settings = {ATTRIBUTES = (Public, ); }; }; - C33FDDCA255A582000E217F9 /* ProofOfWork.swift in Sources */ = {isa = PBXBuildFile; fileRef = C33FDC10255A581E00E217F9 /* ProofOfWork.swift */; }; - C33FDDCB255A582000E217F9 /* TSSocketManager.m in Sources */ = {isa = PBXBuildFile; fileRef = C33FDC11255A581E00E217F9 /* TSSocketManager.m */; }; C33FDDCC255A582000E217F9 /* TSConstants.h in Headers */ = {isa = PBXBuildFile; fileRef = C33FDC12255A581E00E217F9 /* TSConstants.h */; settings = {ATTRIBUTES = (Public, ); }; }; C33FDDCD255A582000E217F9 /* OWSAttachmentDownloads.m in Sources */ = {isa = PBXBuildFile; fileRef = C33FDC13255A581E00E217F9 /* OWSAttachmentDownloads.m */; }; C33FDDCF255A582000E217F9 /* TSAttachment.h in Headers */ = {isa = PBXBuildFile; fileRef = C33FDC15255A581E00E217F9 /* TSAttachment.h */; settings = {ATTRIBUTES = (Public, ); }; }; C33FDDD0255A582000E217F9 /* FunctionalUtil.h in Headers */ = {isa = PBXBuildFile; fileRef = C33FDC16255A581E00E217F9 /* FunctionalUtil.h */; settings = {ATTRIBUTES = (Public, ); }; }; - C33FDDD1255A582000E217F9 /* SharedSenderKeysImplementation.swift in Sources */ = {isa = PBXBuildFile; fileRef = C33FDC17255A581F00E217F9 /* SharedSenderKeysImplementation.swift */; }; C33FDDD2255A582000E217F9 /* TSAttachmentPointer.h in Headers */ = {isa = PBXBuildFile; fileRef = C33FDC18255A581F00E217F9 /* TSAttachmentPointer.h */; settings = {ATTRIBUTES = (Public, ); }; }; C33FDDD3255A582000E217F9 /* OWSQueues.h in Headers */ = {isa = PBXBuildFile; fileRef = C33FDC19255A581F00E217F9 /* OWSQueues.h */; settings = {ATTRIBUTES = (Public, ); }; }; C33FDDD5255A582000E217F9 /* OWSBackgroundTask.m in Sources */ = {isa = PBXBuildFile; fileRef = C33FDC1B255A581F00E217F9 /* OWSBackgroundTask.m */; }; C33FDDD6255A582000E217F9 /* TSCall.m in Sources */ = {isa = PBXBuildFile; fileRef = C33FDC1C255A581F00E217F9 /* TSCall.m */; }; - C33FDDD7255A582000E217F9 /* OWSSyncConfigurationMessage.m in Sources */ = {isa = PBXBuildFile; fileRef = C33FDC1D255A581F00E217F9 /* OWSSyncConfigurationMessage.m */; }; C33FDDD8255A582000E217F9 /* OWSUploadOperation.m in Sources */ = {isa = PBXBuildFile; fileRef = C33FDC1E255A581F00E217F9 /* OWSUploadOperation.m */; }; - C33FDDD9255A582000E217F9 /* LokiSessionResetImplementation.swift in Sources */ = {isa = PBXBuildFile; fileRef = C33FDC1F255A581F00E217F9 /* LokiSessionResetImplementation.swift */; }; + C33FDDD9255A582000E217F9 /* LokiSessionRestorationImplementation.swift in Sources */ = {isa = PBXBuildFile; fileRef = C33FDC1F255A581F00E217F9 /* LokiSessionRestorationImplementation.swift */; }; C33FDEF8255A656D00E217F9 /* Promise+Delaying.swift in Sources */ = {isa = PBXBuildFile; fileRef = C3C2A5D32553860900C340D1 /* Promise+Delaying.swift */; }; C3402FE52559036600EA6424 /* SessionUIKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = C331FF1B2558F9D300070591 /* SessionUIKit.framework */; }; C3471ECB2555356A00297E91 /* MessageSender+Encryption.swift in Sources */ = {isa = PBXBuildFile; fileRef = C3471ECA2555356A00297E91 /* MessageSender+Encryption.swift */; }; @@ -804,14 +632,11 @@ C37F54BA255BB2D8002AEA92 /* SessionProtocolKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = C3C2A8622553B41A00C340D1 /* SessionProtocolKit.framework */; }; C37F54CB255BB53F002AEA92 /* SessionProtocolKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = C3C2A8622553B41A00C340D1 /* SessionProtocolKit.framework */; }; C37F54DC255BB84A002AEA92 /* SessionSnodeKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = C3C2A59F255385C100C340D1 /* SessionSnodeKit.framework */; }; - C38EEF0A255B49A8007E1867 /* SSKProtoEnvelope+Conversion.swift in Sources */ = {isa = PBXBuildFile; fileRef = C38EEF09255B49A8007E1867 /* SSKProtoEnvelope+Conversion.swift */; }; - C38EEFD6255B5BA2007E1867 /* OldSnodeAPI.swift in Sources */ = {isa = PBXBuildFile; fileRef = C38EEFD5255B5BA2007E1867 /* OldSnodeAPI.swift */; }; + C38EEF0A255B49A8007E1867 /* SNProtoEnvelope+Conversion.swift in Sources */ = {isa = PBXBuildFile; fileRef = C38EEF09255B49A8007E1867 /* SNProtoEnvelope+Conversion.swift */; }; C38EF00C255B61CC007E1867 /* SignalUtilitiesKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = C33FD9AB255A548A00E217F9 /* SignalUtilitiesKit.framework */; }; C38EF00E255B61DC007E1867 /* SignalUtilitiesKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = C33FD9AB255A548A00E217F9 /* SignalUtilitiesKit.framework */; }; C38EF216255B6D3B007E1867 /* Theme.h in Headers */ = {isa = PBXBuildFile; fileRef = C38EF212255B6D3A007E1867 /* Theme.h */; settings = {ATTRIBUTES = (Public, ); }; }; - C38EF217255B6D3B007E1867 /* OWSConversationColor.m in Sources */ = {isa = PBXBuildFile; fileRef = C38EF213255B6D3A007E1867 /* OWSConversationColor.m */; }; C38EF218255B6D3B007E1867 /* Theme.m in Sources */ = {isa = PBXBuildFile; fileRef = C38EF214255B6D3A007E1867 /* Theme.m */; }; - C38EF219255B6D3B007E1867 /* OWSConversationColor.h in Headers */ = {isa = PBXBuildFile; fileRef = C38EF215255B6D3A007E1867 /* OWSConversationColor.h */; settings = {ATTRIBUTES = (Public, ); }; }; C38EF228255B6D5D007E1867 /* AttachmentSharing.m in Sources */ = {isa = PBXBuildFile; fileRef = C38EF223255B6D5D007E1867 /* AttachmentSharing.m */; }; C38EF229255B6D5D007E1867 /* SignalAttachment.swift in Sources */ = {isa = PBXBuildFile; fileRef = C38EF224255B6D5D007E1867 /* SignalAttachment.swift */; }; C38EF22A255B6D5D007E1867 /* AttachmentSharing.h in Headers */ = {isa = PBXBuildFile; fileRef = C38EF225255B6D5D007E1867 /* AttachmentSharing.h */; settings = {ATTRIBUTES = (Public, ); }; }; @@ -830,10 +655,6 @@ C38EF24D255B6D67007E1867 /* UIView+OWS.swift in Sources */ = {isa = PBXBuildFile; fileRef = C38EF240255B6D67007E1867 /* UIView+OWS.swift */; }; C38EF24E255B6D67007E1867 /* Collection+OWS.swift in Sources */ = {isa = PBXBuildFile; fileRef = C38EF241255B6D67007E1867 /* Collection+OWS.swift */; }; C38EF24F255B6D67007E1867 /* UIColor+OWS.m in Sources */ = {isa = PBXBuildFile; fileRef = C38EF242255B6D67007E1867 /* UIColor+OWS.m */; }; - C38EF25F255B6D6F007E1867 /* OWSSyncManager.h in Headers */ = {isa = PBXBuildFile; fileRef = C38EF25A255B6D6E007E1867 /* OWSSyncManager.h */; settings = {ATTRIBUTES = (Public, ); }; }; - C38EF260255B6D6F007E1867 /* OWSContactsManager.m in Sources */ = {isa = PBXBuildFile; fileRef = C38EF25B255B6D6E007E1867 /* OWSContactsManager.m */; }; - C38EF261255B6D6F007E1867 /* OWSSyncManager.m in Sources */ = {isa = PBXBuildFile; fileRef = C38EF25C255B6D6E007E1867 /* OWSSyncManager.m */; }; - C38EF262255B6D6F007E1867 /* OWSContactsManager.h in Headers */ = {isa = PBXBuildFile; fileRef = C38EF25D255B6D6E007E1867 /* OWSContactsManager.h */; settings = {ATTRIBUTES = (Public, ); }; }; C38EF272255B6D7A007E1867 /* OWSResaveCollectionDBMigration.m in Sources */ = {isa = PBXBuildFile; fileRef = C38EF26C255B6D79007E1867 /* OWSResaveCollectionDBMigration.m */; }; C38EF273255B6D7A007E1867 /* OWSDatabaseMigrationRunner.m in Sources */ = {isa = PBXBuildFile; fileRef = C38EF26D255B6D79007E1867 /* OWSDatabaseMigrationRunner.m */; }; C38EF274255B6D7A007E1867 /* OWSResaveCollectionDBMigration.h in Headers */ = {isa = PBXBuildFile; fileRef = C38EF26E255B6D79007E1867 /* OWSResaveCollectionDBMigration.h */; settings = {ATTRIBUTES = (Public, ); }; }; @@ -860,7 +681,6 @@ C38EF2C4255B6DA6007E1867 /* OWSContactOffersInteraction.m in Sources */ = {isa = PBXBuildFile; fileRef = C38EF2C0255B6DA6007E1867 /* OWSContactOffersInteraction.m */; }; C38EF2C5255B6DA6007E1867 /* TSUnreadIndicatorInteraction.m in Sources */ = {isa = PBXBuildFile; fileRef = C38EF2C1255B6DA6007E1867 /* TSUnreadIndicatorInteraction.m */; }; C38EF2D4255B6DAF007E1867 /* OWSProfileManager.m in Sources */ = {isa = PBXBuildFile; fileRef = C38EF2CF255B6DAE007E1867 /* OWSProfileManager.m */; }; - C38EF2D5255B6DAF007E1867 /* ProfileFetcherJob.swift in Sources */ = {isa = PBXBuildFile; fileRef = C38EF2D0255B6DAE007E1867 /* ProfileFetcherJob.swift */; }; C38EF2D6255B6DAF007E1867 /* OWSUserProfile.m in Sources */ = {isa = PBXBuildFile; fileRef = C38EF2D1255B6DAF007E1867 /* OWSUserProfile.m */; }; C38EF2D7255B6DAF007E1867 /* OWSProfileManager.h in Headers */ = {isa = PBXBuildFile; fileRef = C38EF2D2255B6DAF007E1867 /* OWSProfileManager.h */; settings = {ATTRIBUTES = (Public, ); }; }; C38EF2D8255B6DAF007E1867 /* OWSUserProfile.h in Headers */ = {isa = PBXBuildFile; fileRef = C38EF2D3255B6DAF007E1867 /* OWSUserProfile.h */; settings = {ATTRIBUTES = (Public, ); }; }; @@ -870,13 +690,9 @@ C38EF30F255B6DBF007E1867 /* AppPreferences.swift in Sources */ = {isa = PBXBuildFile; fileRef = C38EF2E5255B6DB9007E1867 /* AppPreferences.swift */; }; C38EF310255B6DBF007E1867 /* DebugLogger.m in Sources */ = {isa = PBXBuildFile; fileRef = C38EF2E6255B6DBA007E1867 /* DebugLogger.m */; }; C38EF311255B6DBF007E1867 /* OWSScrubbingLogFormatter.h in Headers */ = {isa = PBXBuildFile; fileRef = C38EF2E7255B6DBA007E1867 /* OWSScrubbingLogFormatter.h */; settings = {ATTRIBUTES = (Public, ); }; }; - C38EF312255B6DBF007E1867 /* OWSGroupAvatarBuilder.m in Sources */ = {isa = PBXBuildFile; fileRef = C38EF2E8255B6DBA007E1867 /* OWSGroupAvatarBuilder.m */; }; C38EF313255B6DBF007E1867 /* OWSUnreadIndicator.h in Headers */ = {isa = PBXBuildFile; fileRef = C38EF2E9255B6DBA007E1867 /* OWSUnreadIndicator.h */; settings = {ATTRIBUTES = (Public, ); }; }; - C38EF314255B6DBF007E1867 /* OWSAvatarBuilder.h in Headers */ = {isa = PBXBuildFile; fileRef = C38EF2EA255B6DBA007E1867 /* OWSAvatarBuilder.h */; settings = {ATTRIBUTES = (Public, ); }; }; - C38EF315255B6DBF007E1867 /* OWSGroupAvatarBuilder.h in Headers */ = {isa = PBXBuildFile; fileRef = C38EF2EB255B6DBA007E1867 /* OWSGroupAvatarBuilder.h */; settings = {ATTRIBUTES = (Public, ); }; }; C38EF316255B6DBF007E1867 /* ProximityMonitoringManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = C38EF2EC255B6DBA007E1867 /* ProximityMonitoringManager.swift */; }; C38EF317255B6DBF007E1867 /* DisplayableText.swift in Sources */ = {isa = PBXBuildFile; fileRef = C38EF2ED255B6DBB007E1867 /* DisplayableText.swift */; }; - C38EF318255B6DBF007E1867 /* OWSAvatarBuilder.m in Sources */ = {isa = PBXBuildFile; fileRef = C38EF2EE255B6DBB007E1867 /* OWSAvatarBuilder.m */; }; C38EF319255B6DBF007E1867 /* Weak.swift in Sources */ = {isa = PBXBuildFile; fileRef = C38EF2EF255B6DBB007E1867 /* Weak.swift */; }; C38EF31A255B6DBF007E1867 /* OWSAnyTouchGestureRecognizer.m in Sources */ = {isa = PBXBuildFile; fileRef = C38EF2F0255B6DBB007E1867 /* OWSAnyTouchGestureRecognizer.m */; }; C38EF31B255B6DBF007E1867 /* OWSPreferences.h in Headers */ = {isa = PBXBuildFile; fileRef = C38EF2F1255B6DBB007E1867 /* OWSPreferences.h */; settings = {ATTRIBUTES = (Public, ); }; }; @@ -885,12 +701,10 @@ C38EF320255B6DBF007E1867 /* OWSScrubbingLogFormatter.m in Sources */ = {isa = PBXBuildFile; fileRef = C38EF2F6255B6DBC007E1867 /* OWSScrubbingLogFormatter.m */; }; C38EF321255B6DBF007E1867 /* OWSAudioPlayer.m in Sources */ = {isa = PBXBuildFile; fileRef = C38EF2F7255B6DBC007E1867 /* OWSAudioPlayer.m */; }; C38EF322255B6DBF007E1867 /* DebugLogger.h in Headers */ = {isa = PBXBuildFile; fileRef = C38EF2F8255B6DBC007E1867 /* DebugLogger.h */; settings = {ATTRIBUTES = (Public, ); }; }; - C38EF323255B6DBF007E1867 /* OWSContactAvatarBuilder.m in Sources */ = {isa = PBXBuildFile; fileRef = C38EF2F9255B6DBC007E1867 /* OWSContactAvatarBuilder.m */; }; C38EF324255B6DBF007E1867 /* Bench.swift in Sources */ = {isa = PBXBuildFile; fileRef = C38EF2FA255B6DBD007E1867 /* Bench.swift */; }; C38EF325255B6DBF007E1867 /* OWSWindowManager.h in Headers */ = {isa = PBXBuildFile; fileRef = C38EF2FB255B6DBD007E1867 /* OWSWindowManager.h */; settings = {ATTRIBUTES = (Public, ); }; }; C38EF326255B6DBF007E1867 /* ConversationStyle.swift in Sources */ = {isa = PBXBuildFile; fileRef = C38EF2FC255B6DBD007E1867 /* ConversationStyle.swift */; }; C38EF327255B6DBF007E1867 /* BlockListUIUtils.m in Sources */ = {isa = PBXBuildFile; fileRef = C38EF2FD255B6DBD007E1867 /* BlockListUIUtils.m */; }; - C38EF328255B6DBF007E1867 /* OWSContactAvatarBuilder.h in Headers */ = {isa = PBXBuildFile; fileRef = C38EF2FE255B6DBD007E1867 /* OWSContactAvatarBuilder.h */; settings = {ATTRIBUTES = (Public, ); }; }; C38EF32A255B6DBF007E1867 /* UIUtil.m in Sources */ = {isa = PBXBuildFile; fileRef = C38EF300255B6DBD007E1867 /* UIUtil.m */; }; C38EF32B255B6DBF007E1867 /* OWSFormat.h in Headers */ = {isa = PBXBuildFile; fileRef = C38EF301255B6DBD007E1867 /* OWSFormat.h */; settings = {ATTRIBUTES = (Public, ); }; }; C38EF32D255B6DBF007E1867 /* BlockListUIUtils.h in Headers */ = {isa = PBXBuildFile; fileRef = C38EF303255B6DBE007E1867 /* BlockListUIUtils.h */; settings = {ATTRIBUTES = (Public, ); }; }; @@ -903,26 +717,18 @@ C38EF334255B6DBF007E1867 /* UIUtil.h in Headers */ = {isa = PBXBuildFile; fileRef = C38EF30A255B6DBE007E1867 /* UIUtil.h */; settings = {ATTRIBUTES = (Public, ); }; }; C38EF335255B6DBF007E1867 /* BlockListCache.swift in Sources */ = {isa = PBXBuildFile; fileRef = C38EF30B255B6DBE007E1867 /* BlockListCache.swift */; }; C38EF359255B6DCC007E1867 /* SheetViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = C38EF33F255B6DC5007E1867 /* SheetViewController.swift */; }; - C38EF35A255B6DCC007E1867 /* ViewControllerUtils.m in Sources */ = {isa = PBXBuildFile; fileRef = C38EF340255B6DC5007E1867 /* ViewControllerUtils.m */; }; C38EF35B255B6DCC007E1867 /* SelectThreadViewController.h in Headers */ = {isa = PBXBuildFile; fileRef = C38EF341255B6DC5007E1867 /* SelectThreadViewController.h */; settings = {ATTRIBUTES = (Public, ); }; }; C38EF35C255B6DCC007E1867 /* SelectThreadViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = C38EF342255B6DC5007E1867 /* SelectThreadViewController.m */; }; C38EF35D255B6DCC007E1867 /* OWSNavigationController.h in Headers */ = {isa = PBXBuildFile; fileRef = C38EF343255B6DC5007E1867 /* OWSNavigationController.h */; settings = {ATTRIBUTES = (Public, ); }; }; C38EF35E255B6DCC007E1867 /* OWSViewController.h in Headers */ = {isa = PBXBuildFile; fileRef = C38EF344255B6DC5007E1867 /* OWSViewController.h */; settings = {ATTRIBUTES = (Public, ); }; }; C38EF35F255B6DCC007E1867 /* SelectRecipientViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = C38EF345255B6DC6007E1867 /* SelectRecipientViewController.m */; }; - C38EF360255B6DCC007E1867 /* ReturnToCallViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = C38EF346255B6DC6007E1867 /* ReturnToCallViewController.swift */; }; - C38EF361255B6DCC007E1867 /* EditContactShareNameViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = C38EF347255B6DC6007E1867 /* EditContactShareNameViewController.swift */; }; - C38EF362255B6DCC007E1867 /* ContactShareApprovalViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = C38EF348255B6DC7007E1867 /* ContactShareApprovalViewController.swift */; }; C38EF363255B6DCC007E1867 /* ModalActivityIndicatorViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = C38EF349255B6DC7007E1867 /* ModalActivityIndicatorViewController.swift */; }; - C38EF364255B6DCC007E1867 /* NewNonContactConversationViewController.h in Headers */ = {isa = PBXBuildFile; fileRef = C38EF34A255B6DC7007E1867 /* NewNonContactConversationViewController.h */; settings = {ATTRIBUTES = (Public, ); }; }; C38EF365255B6DCC007E1867 /* OWSTableViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = C38EF34B255B6DC8007E1867 /* OWSTableViewController.m */; }; C38EF366255B6DCC007E1867 /* ScreenLockViewController.h in Headers */ = {isa = PBXBuildFile; fileRef = C38EF34C255B6DC8007E1867 /* ScreenLockViewController.h */; settings = {ATTRIBUTES = (Public, ); }; }; C38EF367255B6DCC007E1867 /* OWSTableViewController.h in Headers */ = {isa = PBXBuildFile; fileRef = C38EF34D255B6DC8007E1867 /* OWSTableViewController.h */; settings = {ATTRIBUTES = (Public, ); }; }; - C38EF369255B6DCC007E1867 /* ViewControllerUtils.h in Headers */ = {isa = PBXBuildFile; fileRef = C38EF34F255B6DC9007E1867 /* ViewControllerUtils.h */; settings = {ATTRIBUTES = (Public, ); }; }; - C38EF36A255B6DCC007E1867 /* NewNonContactConversationViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = C38EF350255B6DC9007E1867 /* NewNonContactConversationViewController.m */; }; C38EF36B255B6DCC007E1867 /* ScreenLockViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = C38EF351255B6DC9007E1867 /* ScreenLockViewController.m */; }; C38EF36C255B6DCC007E1867 /* SharingThreadPickerViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = C38EF352255B6DC9007E1867 /* SharingThreadPickerViewController.m */; }; C38EF36D255B6DCC007E1867 /* SharingThreadPickerViewController.h in Headers */ = {isa = PBXBuildFile; fileRef = C38EF353255B6DCB007E1867 /* SharingThreadPickerViewController.h */; settings = {ATTRIBUTES = (Public, ); }; }; - C38EF36E255B6DCC007E1867 /* ContactFieldView.swift in Sources */ = {isa = PBXBuildFile; fileRef = C38EF354255B6DCB007E1867 /* ContactFieldView.swift */; }; C38EF36F255B6DCC007E1867 /* OWSViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = C38EF355255B6DCB007E1867 /* OWSViewController.m */; }; C38EF370255B6DCC007E1867 /* OWSNavigationController.m in Sources */ = {isa = PBXBuildFile; fileRef = C38EF356255B6DCB007E1867 /* OWSNavigationController.m */; }; C38EF371255B6DCC007E1867 /* MessageApprovalViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = C38EF357255B6DCC007E1867 /* MessageApprovalViewController.swift */; }; @@ -938,7 +744,6 @@ C38EF38D255B6DD2007E1867 /* AttachmentCaptionViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = C38EF384255B6DD2007E1867 /* AttachmentCaptionViewController.swift */; }; C38EF39B255B6DDA007E1867 /* ThreadViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = C38EF397255B6DD9007E1867 /* ThreadViewModel.swift */; }; C38EF39C255B6DDA007E1867 /* OWSQuotedReplyModel.h in Headers */ = {isa = PBXBuildFile; fileRef = C38EF398255B6DD9007E1867 /* OWSQuotedReplyModel.h */; settings = {ATTRIBUTES = (Public, ); }; }; - C38EF39D255B6DDA007E1867 /* ContactShareViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = C38EF399255B6DD9007E1867 /* ContactShareViewModel.swift */; }; C38EF39E255B6DDA007E1867 /* OWSQuotedReplyModel.m in Sources */ = {isa = PBXBuildFile; fileRef = C38EF39A255B6DD9007E1867 /* OWSQuotedReplyModel.m */; }; C38EF3B8255B6DE7007E1867 /* ImageEditorTextViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = C38EF3A8255B6DE4007E1867 /* ImageEditorTextViewController.swift */; }; C38EF3B9255B6DE7007E1867 /* ImageEditorPinchGestureRecognizer.swift in Sources */ = {isa = PBXBuildFile; fileRef = C38EF3A9255B6DE4007E1867 /* ImageEditorPinchGestureRecognizer.swift */; }; @@ -960,16 +765,13 @@ C38EF3F0255B6DF7007E1867 /* ThreadViewHelper.h in Headers */ = {isa = PBXBuildFile; fileRef = C38EF3D2255B6DEE007E1867 /* ThreadViewHelper.h */; settings = {ATTRIBUTES = (Public, ); }; }; C38EF3F1255B6DF7007E1867 /* OWSSearchBar.h in Headers */ = {isa = PBXBuildFile; fileRef = C38EF3D3255B6DEE007E1867 /* OWSSearchBar.h */; settings = {ATTRIBUTES = (Public, ); }; }; C38EF3F2255B6DF7007E1867 /* DisappearingTimerConfigurationView.swift in Sources */ = {isa = PBXBuildFile; fileRef = C38EF3D4255B6DEE007E1867 /* DisappearingTimerConfigurationView.swift */; }; - C38EF3F3255B6DF7007E1867 /* ContactsViewHelper.m in Sources */ = {isa = PBXBuildFile; fileRef = C38EF3D5255B6DEF007E1867 /* ContactsViewHelper.m */; }; C38EF3F4255B6DF7007E1867 /* ContactCellView.m in Sources */ = {isa = PBXBuildFile; fileRef = C38EF3D6255B6DEF007E1867 /* ContactCellView.m */; }; C38EF3F5255B6DF7007E1867 /* OWSTextField.h in Headers */ = {isa = PBXBuildFile; fileRef = C38EF3D7255B6DF0007E1867 /* OWSTextField.h */; settings = {ATTRIBUTES = (Public, ); }; }; C38EF3F6255B6DF7007E1867 /* OWSTextView.h in Headers */ = {isa = PBXBuildFile; fileRef = C38EF3D8255B6DF0007E1867 /* OWSTextView.h */; settings = {ATTRIBUTES = (Public, ); }; }; C38EF3F7255B6DF7007E1867 /* OWSNavigationBar.swift in Sources */ = {isa = PBXBuildFile; fileRef = C38EF3D9255B6DF1007E1867 /* OWSNavigationBar.swift */; }; - C38EF3F8255B6DF7007E1867 /* ContactsViewHelper.h in Headers */ = {isa = PBXBuildFile; fileRef = C38EF3DA255B6DF1007E1867 /* ContactsViewHelper.h */; settings = {ATTRIBUTES = (Public, ); }; }; C38EF3F9255B6DF7007E1867 /* OWSLayerView.swift in Sources */ = {isa = PBXBuildFile; fileRef = C38EF3DB255B6DF1007E1867 /* OWSLayerView.swift */; }; C38EF3FA255B6DF7007E1867 /* DirectionalPanGestureRecognizer.swift in Sources */ = {isa = PBXBuildFile; fileRef = C38EF3DC255B6DF1007E1867 /* DirectionalPanGestureRecognizer.swift */; }; C38EF3FB255B6DF7007E1867 /* UIAlertController+OWS.swift in Sources */ = {isa = PBXBuildFile; fileRef = C38EF3DD255B6DF1007E1867 /* UIAlertController+OWS.swift */; }; - C38EF3FC255B6DF7007E1867 /* AvatarImageView.swift in Sources */ = {isa = PBXBuildFile; fileRef = C38EF3DE255B6DF2007E1867 /* AvatarImageView.swift */; }; C38EF3FD255B6DF7007E1867 /* OWSTextView.m in Sources */ = {isa = PBXBuildFile; fileRef = C38EF3DF255B6DF2007E1867 /* OWSTextView.m */; }; C38EF3FE255B6DF7007E1867 /* OWSTextField.m in Sources */ = {isa = PBXBuildFile; fileRef = C38EF3E0255B6DF3007E1867 /* OWSTextField.m */; }; C38EF3FF255B6DF7007E1867 /* TappableView.swift in Sources */ = {isa = PBXBuildFile; fileRef = C38EF3E1255B6DF3007E1867 /* TappableView.swift */; }; @@ -986,7 +788,6 @@ C38EF40A255B6DF7007E1867 /* OWSFlatButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = C38EF3EC255B6DF6007E1867 /* OWSFlatButton.swift */; }; C38EF40B255B6DF7007E1867 /* TappableStackView.swift in Sources */ = {isa = PBXBuildFile; fileRef = C38EF3ED255B6DF6007E1867 /* TappableStackView.swift */; }; C38EF40C255B6DF7007E1867 /* GradientView.swift in Sources */ = {isa = PBXBuildFile; fileRef = C38EF3EE255B6DF6007E1867 /* GradientView.swift */; }; - C38EF481255B752E007E1867 /* SystemContactsFetcher.swift in Sources */ = {isa = PBXBuildFile; fileRef = C38EF259255B6D6E007E1867 /* SystemContactsFetcher.swift */; }; C38EF48A255B7E3F007E1867 /* SessionUIKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = C331FF1B2558F9D300070591 /* SessionUIKit.framework */; }; C396DAEF2518408B00FF6DC5 /* ParsingState.swift in Sources */ = {isa = PBXBuildFile; fileRef = C396DAE82518408900FF6DC5 /* ParsingState.swift */; }; C396DAF02518408B00FF6DC5 /* String+Lines.swift in Sources */ = {isa = PBXBuildFile; fileRef = C396DAE92518408A00FF6DC5 /* String+Lines.swift */; }; @@ -1023,7 +824,7 @@ C3A721902558C0CD0043A11F /* FileServerAPI.swift in Sources */ = {isa = PBXBuildFile; fileRef = C3A7218F2558C0CD0043A11F /* FileServerAPI.swift */; }; C3A7219A2558C1660043A11F /* AnyPromise+Conversion.swift in Sources */ = {isa = PBXBuildFile; fileRef = C3A721992558C1660043A11F /* AnyPromise+Conversion.swift */; }; C3A7222A2558C1E40043A11F /* DotNetAPI.swift in Sources */ = {isa = PBXBuildFile; fileRef = C3A722292558C1E40043A11F /* DotNetAPI.swift */; }; - C3A7225E2558C38D0043A11F /* AnyPromise+Retaining.swift in Sources */ = {isa = PBXBuildFile; fileRef = C3A7225D2558C38D0043A11F /* AnyPromise+Retaining.swift */; }; + C3A7225E2558C38D0043A11F /* Promise+Retaining.swift in Sources */ = {isa = PBXBuildFile; fileRef = C3A7225D2558C38D0043A11F /* Promise+Retaining.swift */; }; C3A722802558C4E10043A11F /* AttachmentStream.swift in Sources */ = {isa = PBXBuildFile; fileRef = C3A7227F2558C4E10043A11F /* AttachmentStream.swift */; }; C3A722922558C8940043A11F /* OpenGroupAPIDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = C3A722912558C8940043A11F /* OpenGroupAPIDelegate.swift */; }; C3A7229C2558E4310043A11F /* OpenGroupMessage+Conversion.swift in Sources */ = {isa = PBXBuildFile; fileRef = C3A7229B2558E4310043A11F /* OpenGroupMessage+Conversion.swift */; }; @@ -1274,45 +1075,27 @@ 264033E641846B67E0CB21B0 /* Pods-SessionUtilitiesKit.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-SessionUtilitiesKit.debug.xcconfig"; path = "Pods/Target Support Files/Pods-SessionUtilitiesKit/Pods-SessionUtilitiesKit.debug.xcconfig"; sourceTree = ""; }; 264242150E87D10A357DB07B /* Pods_SignalMessaging.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_SignalMessaging.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 3303495F6651CE2F3CC9693B /* Pods-SessionUtilities.app store release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-SessionUtilities.app store release.xcconfig"; path = "Pods/Target Support Files/Pods-SessionUtilities/Pods-SessionUtilities.app store release.xcconfig"; sourceTree = ""; }; - 3403B95B20EA9526001A1F44 /* OWSContactShareButtonsView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = OWSContactShareButtonsView.m; sourceTree = ""; }; - 3403B95C20EA9527001A1F44 /* OWSContactShareButtonsView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = OWSContactShareButtonsView.h; sourceTree = ""; }; 340FC87B204DAC8C007AEB0F /* NotificationSettingsOptionsViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = NotificationSettingsOptionsViewController.m; sourceTree = ""; }; 340FC87C204DAC8C007AEB0F /* NotificationSettingsViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = NotificationSettingsViewController.m; sourceTree = ""; }; - 340FC87D204DAC8C007AEB0F /* DomainFrontingCountryViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = DomainFrontingCountryViewController.m; sourceTree = ""; }; 340FC87E204DAC8C007AEB0F /* PrivacySettingsTableViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = PrivacySettingsTableViewController.m; sourceTree = ""; }; 340FC87F204DAC8C007AEB0F /* OWSBackupSettingsViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = OWSBackupSettingsViewController.h; sourceTree = ""; }; - 340FC881204DAC8C007AEB0F /* AdvancedSettingsTableViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AdvancedSettingsTableViewController.h; sourceTree = ""; }; 340FC883204DAC8C007AEB0F /* OWSSoundSettingsViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = OWSSoundSettingsViewController.m; sourceTree = ""; }; 340FC884204DAC8C007AEB0F /* AboutTableViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AboutTableViewController.h; sourceTree = ""; }; 340FC886204DAC8C007AEB0F /* AddToBlockListViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = AddToBlockListViewController.m; sourceTree = ""; }; - 340FC887204DAC8C007AEB0F /* BlockListViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = BlockListViewController.m; sourceTree = ""; }; 340FC888204DAC8C007AEB0F /* OWSQRCodeScanningViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = OWSQRCodeScanningViewController.h; sourceTree = ""; }; - 340FC889204DAC8C007AEB0F /* DomainFrontingCountryViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = DomainFrontingCountryViewController.h; sourceTree = ""; }; 340FC88A204DAC8C007AEB0F /* NotificationSettingsViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = NotificationSettingsViewController.h; sourceTree = ""; }; 340FC88B204DAC8C007AEB0F /* NotificationSettingsOptionsViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = NotificationSettingsOptionsViewController.h; sourceTree = ""; }; - 340FC88C204DAC8C007AEB0F /* AdvancedSettingsTableViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = AdvancedSettingsTableViewController.m; sourceTree = ""; }; 340FC88E204DAC8C007AEB0F /* OWSBackupSettingsViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = OWSBackupSettingsViewController.m; sourceTree = ""; }; 340FC88F204DAC8C007AEB0F /* PrivacySettingsTableViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PrivacySettingsTableViewController.h; sourceTree = ""; }; - 340FC890204DAC8C007AEB0F /* BlockListViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = BlockListViewController.h; sourceTree = ""; }; 340FC892204DAC8C007AEB0F /* AddToBlockListViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AddToBlockListViewController.h; sourceTree = ""; }; 340FC893204DAC8C007AEB0F /* AboutTableViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = AboutTableViewController.m; sourceTree = ""; }; 340FC894204DAC8C007AEB0F /* OWSSoundSettingsViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = OWSSoundSettingsViewController.h; sourceTree = ""; }; 340FC896204DAC8C007AEB0F /* OWSQRCodeScanningViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = OWSQRCodeScanningViewController.m; sourceTree = ""; }; - 340FC898204DAC8D007AEB0F /* OWSAddToContactViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = OWSAddToContactViewController.h; sourceTree = ""; }; 340FC899204DAC8D007AEB0F /* OWSConversationSettingsViewDelegate.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = OWSConversationSettingsViewDelegate.h; sourceTree = ""; }; 340FC89A204DAC8D007AEB0F /* OWSConversationSettingsViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = OWSConversationSettingsViewController.m; sourceTree = ""; }; 340FC89B204DAC8D007AEB0F /* AddToGroupViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = AddToGroupViewController.m; sourceTree = ""; }; - 340FC89C204DAC8D007AEB0F /* UpdateGroupViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = UpdateGroupViewController.m; sourceTree = ""; }; - 340FC89D204DAC8D007AEB0F /* FingerprintViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FingerprintViewController.h; sourceTree = ""; }; - 340FC89E204DAC8D007AEB0F /* ShowGroupMembersViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ShowGroupMembersViewController.h; sourceTree = ""; }; - 340FC89F204DAC8D007AEB0F /* FingerprintViewScanController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = FingerprintViewScanController.m; sourceTree = ""; }; 340FC8A0204DAC8D007AEB0F /* OWSConversationSettingsViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = OWSConversationSettingsViewController.h; sourceTree = ""; }; - 340FC8A1204DAC8D007AEB0F /* OWSAddToContactViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = OWSAddToContactViewController.m; sourceTree = ""; }; - 340FC8A2204DAC8D007AEB0F /* FingerprintViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = FingerprintViewController.m; sourceTree = ""; }; - 340FC8A3204DAC8D007AEB0F /* UpdateGroupViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = UpdateGroupViewController.h; sourceTree = ""; }; 340FC8A4204DAC8D007AEB0F /* AddToGroupViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AddToGroupViewController.h; sourceTree = ""; }; - 340FC8A5204DAC8D007AEB0F /* FingerprintViewScanController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FingerprintViewScanController.h; sourceTree = ""; }; - 340FC8A6204DAC8D007AEB0F /* ShowGroupMembersViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ShowGroupMembersViewController.m; sourceTree = ""; }; 34129B8521EF8779005457A8 /* LinkPreviewView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = LinkPreviewView.swift; sourceTree = ""; }; 341341ED2187467900192D59 /* ConversationViewModel.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ConversationViewModel.h; sourceTree = ""; }; 341341EE2187467900192D59 /* ConversationViewModel.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ConversationViewModel.m; sourceTree = ""; }; @@ -1349,7 +1132,6 @@ 348570A620F67574004FF32B /* OWSMessageHeaderView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = OWSMessageHeaderView.m; sourceTree = ""; }; 348570A720F67574004FF32B /* OWSMessageHeaderView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = OWSMessageHeaderView.h; sourceTree = ""; }; 3488F9352191CC4000E524CC /* ConversationMediaView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ConversationMediaView.swift; sourceTree = ""; }; - 348BB25C20A0C5530047AEC2 /* ContactShareViewHelper.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ContactShareViewHelper.swift; sourceTree = ""; }; 3496744B2076768600080B5F /* OWSMessageBubbleView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = OWSMessageBubbleView.h; sourceTree = ""; }; 3496744C2076768700080B5F /* OWSMessageBubbleView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = OWSMessageBubbleView.m; sourceTree = ""; }; 3496744E2076ACCE00080B5F /* LongTextViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = LongTextViewController.swift; sourceTree = ""; }; @@ -1378,10 +1160,6 @@ 34B0796C1FCF46B000E248C2 /* MainAppContext.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MainAppContext.h; sourceTree = ""; }; 34B0796E1FD07B1E00E248C2 /* SignalShareExtension.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = SignalShareExtension.entitlements; sourceTree = ""; }; 34B3F83B1E8DF1700035BE1A /* CallViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CallViewController.swift; sourceTree = ""; }; - 34B3F83E1E8DF1700035BE1A /* ContactsPicker.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ContactsPicker.swift; sourceTree = ""; }; - 34B3F84C1E8DF1700035BE1A /* InviteFlow.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = InviteFlow.swift; sourceTree = ""; }; - 34B3F86D1E8DF1700035BE1A /* SignalsNavigationController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SignalsNavigationController.h; sourceTree = ""; }; - 34B3F86E1E8DF1700035BE1A /* SignalsNavigationController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SignalsNavigationController.m; sourceTree = ""; }; 34B6A902218B3F62007C4606 /* TypingIndicatorView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TypingIndicatorView.swift; sourceTree = ""; }; 34B6A904218B4C90007C4606 /* TypingIndicatorInteraction.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TypingIndicatorInteraction.swift; sourceTree = ""; }; 34B6A906218B5240007C4606 /* TypingIndicatorCell.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TypingIndicatorCell.swift; sourceTree = ""; }; @@ -1393,8 +1171,6 @@ 34C4E2552118957600BEA353 /* OWSWebRTCDataProtos.pb.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = OWSWebRTCDataProtos.pb.swift; sourceTree = ""; }; 34C4E2562118957600BEA353 /* WebRTCProto.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = WebRTCProto.swift; sourceTree = ""; }; 34CA1C261F7156F300E51C51 /* MessageDetailViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MessageDetailViewController.swift; sourceTree = ""; }; - 34CA63192097806E00E526A0 /* OWSContactShareView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = OWSContactShareView.h; sourceTree = ""; }; - 34CA631A2097806E00E526A0 /* OWSContactShareView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = OWSContactShareView.m; sourceTree = ""; }; 34CF0783203E6B77005C4D61 /* busy_tone_ansi.caf */ = {isa = PBXFileReference; lastKnownFileType = file; name = busy_tone_ansi.caf; path = Session/Meta/AudioFiles/busy_tone_ansi.caf; sourceTree = SOURCE_ROOT; }; 34CF0784203E6B77005C4D61 /* ringback_tone_ansi.caf */ = {isa = PBXFileReference; lastKnownFileType = file; name = ringback_tone_ansi.caf; path = Session/Meta/AudioFiles/ringback_tone_ansi.caf; sourceTree = SOURCE_ROOT; }; 34CF0786203E6B78005C4D61 /* end_call_tone_cept.caf */ = {isa = PBXFileReference; lastKnownFileType = file; name = end_call_tone_cept.caf; path = Session/Meta/AudioFiles/end_call_tone_cept.caf; sourceTree = SOURCE_ROOT; }; @@ -1412,8 +1188,6 @@ 34D1F0721F8678AA0066283D /* ConversationViewLayout.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ConversationViewLayout.m; sourceTree = ""; }; 34D1F0961F867BFC0066283D /* ConversationViewCell.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ConversationViewCell.h; sourceTree = ""; }; 34D1F0971F867BFC0066283D /* ConversationViewCell.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ConversationViewCell.m; sourceTree = ""; }; - 34D1F09A1F867BFC0066283D /* OWSContactOffersCell.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = OWSContactOffersCell.h; sourceTree = ""; }; - 34D1F09B1F867BFC0066283D /* OWSContactOffersCell.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = OWSContactOffersCell.m; sourceTree = ""; }; 34D1F0A11F867BFC0066283D /* OWSMessageCell.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = OWSMessageCell.h; sourceTree = ""; }; 34D1F0A21F867BFC0066283D /* OWSMessageCell.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = OWSMessageCell.m; sourceTree = ""; }; 34D1F0A51F867BFC0066283D /* OWSSystemMessageCell.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = OWSSystemMessageCell.h; sourceTree = ""; }; @@ -1431,7 +1205,6 @@ 34D5CCA81EAE3D30005515DB /* AvatarViewHelper.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = AvatarViewHelper.m; sourceTree = ""; }; 34D920E520E179C100D51158 /* OWSMessageFooterView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = OWSMessageFooterView.h; sourceTree = ""; }; 34D920E620E179C200D51158 /* OWSMessageFooterView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = OWSMessageFooterView.m; sourceTree = ""; }; - 34D99C911F2937CC00D284D6 /* OWSAnalytics.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = OWSAnalytics.swift; sourceTree = ""; }; 34D99CE3217509C1000AFB39 /* AppEnvironment.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AppEnvironment.swift; sourceTree = ""; }; 34DBEFFF206BD5A400025978 /* OWSMessageTextView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = OWSMessageTextView.m; sourceTree = ""; }; 34DBF000206BD5A400025978 /* OWSMessageTextView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = OWSMessageTextView.h; sourceTree = ""; }; @@ -1440,7 +1213,6 @@ 34DBF005206C3CB100025978 /* OWSBubbleShapeView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = OWSBubbleShapeView.h; sourceTree = ""; }; 34DBF006206C3CB200025978 /* OWSBubbleShapeView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = OWSBubbleShapeView.m; sourceTree = ""; }; 34E3E5671EC4B19400495BAC /* AudioProgressView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AudioProgressView.swift; sourceTree = ""; }; - 34E88D252098C5AE00A608F4 /* ContactViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ContactViewController.swift; sourceTree = ""; }; 34EA693F2194933900702471 /* MediaDownloadView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MediaDownloadView.swift; sourceTree = ""; }; 34EA69412194DE7F00702471 /* MediaUploadView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MediaUploadView.swift; sourceTree = ""; }; 34F308A01ECB469700BB7697 /* OWSBezierPathView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = OWSBezierPathView.h; sourceTree = ""; }; @@ -1457,14 +1229,11 @@ 450DF2041E0D74AC003D14BE /* Platform.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Platform.swift; sourceTree = ""; }; 450DF2081E0DD2C6003D14BE /* UserNotificationsAdaptee.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; lineEnding = 0; path = UserNotificationsAdaptee.swift; sourceTree = ""; xcLanguageSpecificationIdentifier = xcode.lang.swift; }; 451166BF1FD86B98000739BA /* AccountManager.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AccountManager.swift; sourceTree = ""; }; - 451764291DE939FD00EDB8B9 /* ContactCell.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ContactCell.swift; sourceTree = ""; }; 451A13B01E13DED2000A50FD /* AppNotifications.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; lineEnding = 0; path = AppNotifications.swift; sourceTree = ""; xcLanguageSpecificationIdentifier = xcode.lang.swift; }; 4520D8D41D417D8E00123472 /* Photos.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Photos.framework; path = System/Library/Frameworks/Photos.framework; sourceTree = SDKROOT; }; 4521C3BF1F59F3BA00B4C582 /* TextFieldHelper.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TextFieldHelper.swift; sourceTree = ""; }; - 452B998F20A34B6B006F2F9E /* AddContactShareToExistingContactViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AddContactShareToExistingContactViewController.swift; sourceTree = ""; }; 452C468E1E427E200087B011 /* OutboundCallInitiator.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = OutboundCallInitiator.swift; sourceTree = ""; }; 452EC6DE205E9E30000E787C /* MediaGalleryViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MediaGalleryViewController.swift; sourceTree = ""; }; - 452ECA4C1E087E7200E2F016 /* MessageFetcherJob.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MessageFetcherJob.swift; sourceTree = ""; }; 453518681FC635DD00210559 /* SessionShareExtension.appex */ = {isa = PBXFileReference; explicitFileType = "wrapper.app-extension"; includeInIndex = 0; path = SessionShareExtension.appex; sourceTree = BUILT_PRODUCTS_DIR; }; 4535186A1FC635DD00210559 /* ShareViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ShareViewController.swift; sourceTree = ""; }; 4535186D1FC635DD00210559 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/MainInterface.storyboard; sourceTree = ""; }; @@ -1475,17 +1244,12 @@ 455A16DB1F1FEA0000F86704 /* Metal.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Metal.framework; path = System/Library/Frameworks/Metal.framework; sourceTree = SDKROOT; }; 455A16DC1F1FEA0000F86704 /* MetalKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = MetalKit.framework; path = System/Library/Frameworks/MetalKit.framework; sourceTree = SDKROOT; }; 4574A5D51DD6704700C6B692 /* CallService.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; lineEnding = 0; path = CallService.swift; sourceTree = ""; xcLanguageSpecificationIdentifier = xcode.lang.swift; }; - 4579431C1E7C8CE9008ED0C0 /* Pastelog.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Pastelog.h; sourceTree = ""; }; - 4579431D1E7C8CE9008ED0C0 /* Pastelog.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = Pastelog.m; sourceTree = ""; }; 45794E851E00620000066731 /* CallUIAdapter.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CallUIAdapter.swift; sourceTree = ""; }; 457F671A20746193000EABCD /* QuotedReplyPreview.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = QuotedReplyPreview.swift; sourceTree = ""; }; 45847E861E4283C30080EAB3 /* Intents.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Intents.framework; path = System/Library/Frameworks/Intents.framework; sourceTree = SDKROOT; }; - 4585C4671ED8F8D200896AEA /* SafetyNumberConfirmationAlert.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SafetyNumberConfirmationAlert.swift; sourceTree = ""; }; 458DE9D51DEE3FD00071BB03 /* PeerConnectionClient.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PeerConnectionClient.swift; sourceTree = ""; }; 458E38351D668EBF0094BD24 /* OWSDeviceProvisioningURLParser.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = OWSDeviceProvisioningURLParser.h; sourceTree = ""; }; 458E38361D668EBF0094BD24 /* OWSDeviceProvisioningURLParser.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = OWSDeviceProvisioningURLParser.m; sourceTree = ""; }; - 459311FA1D75C948008DD4F0 /* OWSDeviceTableViewCell.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = OWSDeviceTableViewCell.h; sourceTree = ""; }; - 459311FB1D75C948008DD4F0 /* OWSDeviceTableViewCell.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = OWSDeviceTableViewCell.m; sourceTree = ""; }; 45A2F004204473A3002E978A /* NewMessage.aifc */ = {isa = PBXFileReference; lastKnownFileType = file; name = NewMessage.aifc; path = Session/Meta/AudioFiles/NewMessage.aifc; sourceTree = SOURCE_ROOT; }; 45A663C41F92EC760027B59E /* GroupTableViewCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GroupTableViewCell.swift; sourceTree = ""; }; 45A6DAD51EBBF85500893231 /* ReminderView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ReminderView.swift; sourceTree = ""; }; @@ -1525,9 +1289,6 @@ 45CB2FA71CB7146C00E1B343 /* Launch Screen.storyboard */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.storyboard; name = "Launch Screen.storyboard"; path = "Session/Signal/Launch Screen.storyboard"; sourceTree = SOURCE_ROOT; }; 45CD81EE1DC030E7004C9430 /* SyncPushTokensJob.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SyncPushTokensJob.swift; sourceTree = ""; }; 45D231761DC7E8F10034FA89 /* SessionResetJob.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SessionResetJob.swift; sourceTree = ""; }; - 45D308AB2049A439000189E4 /* PinEntryView.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = PinEntryView.h; sourceTree = ""; }; - 45D308AC2049A439000189E4 /* PinEntryView.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = PinEntryView.m; sourceTree = ""; }; - 45DDA6232090CEB500DE97F8 /* ConversationHeaderView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ConversationHeaderView.swift; sourceTree = ""; }; 45DF5DF11DDB843F00C936C7 /* CompareSafetyNumbersActivity.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CompareSafetyNumbersActivity.swift; sourceTree = ""; }; 45E5A6981F61E6DD001E4A8A /* MarqueeLabel.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MarqueeLabel.swift; sourceTree = ""; }; 45F170BA1E2FC5D3003FC1F2 /* CallAudioService.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CallAudioService.swift; sourceTree = ""; }; @@ -1538,7 +1299,6 @@ 45FBC5D01DF8592E00E9B410 /* SignalCall.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SignalCall.swift; sourceTree = ""; }; 4C043929220A9EC800BAEA63 /* VoiceNoteLock.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VoiceNoteLock.swift; sourceTree = ""; }; 4C090A1A210FD9C7001FD7F9 /* HapticFeedback.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HapticFeedback.swift; sourceTree = ""; }; - 4C13C9F520E57BA30089A98B /* ColorPickerViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ColorPickerViewController.swift; sourceTree = ""; }; 4C1885D1218F8E1C00B67051 /* PhotoGridViewCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PhotoGridViewCell.swift; sourceTree = ""; }; 4C1D2337218B6BA000A0598F /* it */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = it; path = it.lproj/Localizable.strings; sourceTree = ""; }; 4C21D5D5223A9DC500EF8A77 /* UIAlerts+iOS9.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = "UIAlerts+iOS9.m"; sourceTree = ""; }; @@ -1555,7 +1315,6 @@ 4CA46F4B219CCC630038ABDE /* CaptionView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CaptionView.swift; sourceTree = ""; }; 4CA485BA2232339F004B9E7D /* PhotoCaptureViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PhotoCaptureViewController.swift; sourceTree = ""; }; 4CB5F26820F7D060004D1B42 /* MessageActions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MessageActions.swift; sourceTree = ""; }; - 4CC0B59B20EC5F2E00CF6EE0 /* ConversationConfigurationSyncOperation.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ConversationConfigurationSyncOperation.swift; sourceTree = ""; }; 4CC1ECF8211A47CD00CC13BE /* StoreKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = StoreKit.framework; path = System/Library/Frameworks/StoreKit.framework; sourceTree = SDKROOT; }; 4CC1ECFA211A553000CC13BE /* AppUpdateNag.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppUpdateNag.swift; sourceTree = ""; }; 4CC613352227A00400E21A3A /* ConversationSearch.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ConversationSearch.swift; sourceTree = ""; }; @@ -1632,9 +1391,6 @@ B6B226961BE4B7D200860F4D /* ContactsUI.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = ContactsUI.framework; path = System/Library/Frameworks/ContactsUI.framework; sourceTree = SDKROOT; }; B6FE7EB61ADD62FA00A6D22F /* PushKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = PushKit.framework; path = System/Library/Frameworks/PushKit.framework; sourceTree = SDKROOT; }; B80A579E23DFF1F300876683 /* NewClosedGroupVC.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NewClosedGroupVC.swift; sourceTree = ""; }; - B80C6B562384A56D00FDBC8B /* DeviceLinksVC.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DeviceLinksVC.swift; sourceTree = ""; }; - B80C6B582384C4E700FDBC8B /* DeviceNameModal.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DeviceNameModal.swift; sourceTree = ""; }; - B80C6B5A2384C7F900FDBC8B /* DeviceNameModalDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DeviceNameModalDelegate.swift; sourceTree = ""; }; B82B40872399EB0E00A248E7 /* LandingVC.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LandingVC.swift; sourceTree = ""; }; B82B40892399EC0600A248E7 /* FakeChatView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FakeChatView.swift; sourceTree = ""; }; B82B408B239A068800A248E7 /* RegisterVC.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RegisterVC.swift; sourceTree = ""; }; @@ -1656,13 +1412,11 @@ B8783E9D23EB948D00404FB8 /* UILabel+Interaction.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UILabel+Interaction.swift"; sourceTree = ""; }; B879D448247E1BE300DB3608 /* PathVC.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PathVC.swift; sourceTree = ""; }; B879D44A247E1D9200DB3608 /* PathStatusView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PathStatusView.swift; sourceTree = ""; }; - B885D5F3233491AB00EE0D8E /* DeviceLinkingModal.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DeviceLinkingModal.swift; sourceTree = ""; }; B885D5F52334A32100EE0D8E /* UIView+Constraints.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UIView+Constraints.swift"; sourceTree = ""; }; B886B4A62398B23E00211ABE /* QRCodeVC.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = QRCodeVC.swift; sourceTree = ""; }; B886B4A82398BA1500211ABE /* QRCode.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = QRCode.swift; sourceTree = ""; }; B88847BB23E10BC6009836D2 /* GroupMembersVC.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GroupMembersVC.swift; sourceTree = ""; }; B893063E2383961A005EAA8E /* ScanQRCodeWrapperVC.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ScanQRCodeWrapperVC.swift; sourceTree = ""; }; - B894D0702339D6F300B4D94D /* DeviceLinkingModalDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DeviceLinkingModalDelegate.swift; sourceTree = ""; }; B894D0742339EDCF00B4D94D /* NukeDataModal.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NukeDataModal.swift; sourceTree = ""; }; B8B26C8E234D629C004ED98C /* MentionCandidateSelectionView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MentionCandidateSelectionView.swift; sourceTree = ""; }; B8B5BCEB2394D869003823C9 /* Button.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Button.swift; sourceTree = ""; }; @@ -1675,6 +1429,9 @@ B8BB82B423947F2D00BA5194 /* TextField.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TextField.swift; sourceTree = ""; }; B8BB82B82394911B00BA5194 /* Separator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Separator.swift; sourceTree = ""; }; B8BB82BD2394D4CE00BA5194 /* Fonts.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Fonts.swift; sourceTree = ""; }; + B8C2B2C72563685C00551B4D /* CircleView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CircleView.swift; sourceTree = ""; }; + B8C2B331256376F000551B4D /* ThreadUtil.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = ThreadUtil.m; sourceTree = ""; }; + B8C2B33B2563770800551B4D /* ThreadUtil.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = ThreadUtil.h; sourceTree = ""; }; B8C9689023FA1401005F64E0 /* AppMode.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppMode.swift; sourceTree = ""; }; B8CCF6342396005F0091D419 /* SpaceMono-Regular.ttf */ = {isa = PBXFileReference; lastKnownFileType = file; path = "SpaceMono-Regular.ttf"; sourceTree = ""; }; B8CCF63623961D6D0091D419 /* NewPrivateChatVC.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NewPrivateChatVC.swift; sourceTree = ""; }; @@ -1713,14 +1470,12 @@ C33FD9AB255A548A00E217F9 /* SignalUtilitiesKit.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = SignalUtilitiesKit.framework; sourceTree = BUILT_PRODUCTS_DIR; }; C33FD9AD255A548A00E217F9 /* SignalUtilitiesKit.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = SignalUtilitiesKit.h; sourceTree = ""; }; C33FD9AE255A548A00E217F9 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; - C33FDA66255A57F900E217F9 /* OWSOutgoingCallMessage.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = OWSOutgoingCallMessage.h; sourceTree = ""; }; C33FDA67255A57F900E217F9 /* OWSPrimaryStorage.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = OWSPrimaryStorage.h; sourceTree = ""; }; C33FDA68255A57F900E217F9 /* OWSBlockingManager.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = OWSBlockingManager.m; sourceTree = ""; }; C33FDA69255A57F900E217F9 /* SSKPreferences.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SSKPreferences.swift; sourceTree = ""; }; C33FDA6B255A57FA00E217F9 /* OWSDisappearingConfigurationUpdateInfoMessage.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = OWSDisappearingConfigurationUpdateInfoMessage.m; sourceTree = ""; }; C33FDA6C255A57FA00E217F9 /* ProtoUtils.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ProtoUtils.m; sourceTree = ""; }; C33FDA6D255A57FA00E217F9 /* YapDatabase+Promise.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "YapDatabase+Promise.swift"; sourceTree = ""; }; - C33FDA6E255A57FA00E217F9 /* Array+Description.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "Array+Description.swift"; sourceTree = ""; }; C33FDA6F255A57FA00E217F9 /* ReachabilityManager.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ReachabilityManager.swift; sourceTree = ""; }; C33FDA70255A57FA00E217F9 /* TSMessage.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = TSMessage.h; sourceTree = ""; }; C33FDA71255A57FA00E217F9 /* OWSReadReceiptManager.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = OWSReadReceiptManager.m; sourceTree = ""; }; @@ -1728,70 +1483,43 @@ C33FDA73255A57FA00E217F9 /* ECKeyPair+Hexadecimal.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "ECKeyPair+Hexadecimal.swift"; sourceTree = ""; }; C33FDA74255A57FB00E217F9 /* ClosedGroupsProtocol.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ClosedGroupsProtocol.swift; sourceTree = ""; }; C33FDA75255A57FB00E217F9 /* OWSSyncManagerProtocol.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = OWSSyncManagerProtocol.h; sourceTree = ""; }; - C33FDA76255A57FB00E217F9 /* OWSSyncGroupsRequestMessage.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = OWSSyncGroupsRequestMessage.m; sourceTree = ""; }; - C33FDA77255A57FB00E217F9 /* Contact.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = Contact.m; sourceTree = ""; }; - C33FDA78255A57FB00E217F9 /* SSKWebSocket.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SSKWebSocket.swift; sourceTree = ""; }; C33FDA79255A57FB00E217F9 /* TSGroupThread.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = TSGroupThread.h; sourceTree = ""; }; C33FDA7A255A57FB00E217F9 /* NSRegularExpression+SSK.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "NSRegularExpression+SSK.swift"; sourceTree = ""; }; C33FDA7B255A57FB00E217F9 /* TSInvalidIdentityKeyReceivingErrorMessage.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = TSInvalidIdentityKeyReceivingErrorMessage.h; sourceTree = ""; }; C33FDA7C255A57FB00E217F9 /* Debugging.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Debugging.swift; sourceTree = ""; }; - C33FDA7D255A57FB00E217F9 /* OWSCensorshipConfiguration.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = OWSCensorshipConfiguration.m; sourceTree = ""; }; C33FDA7E255A57FB00E217F9 /* Mention.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Mention.swift; sourceTree = ""; }; C33FDA7F255A57FC00E217F9 /* OWSRecordTranscriptJob.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = OWSRecordTranscriptJob.m; sourceTree = ""; }; C33FDA80255A57FC00E217F9 /* OWSDisappearingMessagesJob.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = OWSDisappearingMessagesJob.h; sourceTree = ""; }; C33FDA81255A57FC00E217F9 /* MentionsManager.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MentionsManager.swift; sourceTree = ""; }; - C33FDA83255A57FC00E217F9 /* Promise+retainUntilComplete.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "Promise+retainUntilComplete.swift"; sourceTree = ""; }; - C33FDA84255A57FC00E217F9 /* ContactsUpdater.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ContactsUpdater.m; sourceTree = ""; }; C33FDA85255A57FC00E217F9 /* OWSPrimaryStorage+Loki.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "OWSPrimaryStorage+Loki.swift"; sourceTree = ""; }; C33FDA86255A57FC00E217F9 /* OWSDisappearingMessagesFinder.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = OWSDisappearingMessagesFinder.m; sourceTree = ""; }; C33FDA87255A57FC00E217F9 /* TypingIndicators.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TypingIndicators.swift; sourceTree = ""; }; C33FDA88255A57FD00E217F9 /* YapDatabaseTransaction+OWS.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "YapDatabaseTransaction+OWS.h"; sourceTree = ""; }; - C33FDA89255A57FD00E217F9 /* OWSAnalytics.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = OWSAnalytics.m; sourceTree = ""; }; - C33FDA8A255A57FD00E217F9 /* PhoneNumber.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = PhoneNumber.m; sourceTree = ""; }; C33FDA8B255A57FD00E217F9 /* AppVersion.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = AppVersion.m; sourceTree = ""; }; C33FDA8C255A57FD00E217F9 /* PublicChatPoller.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PublicChatPoller.swift; sourceTree = ""; }; - C33FDA8D255A57FD00E217F9 /* OWSReceiptsForSenderMessage.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = OWSReceiptsForSenderMessage.h; sourceTree = ""; }; C33FDA8E255A57FD00E217F9 /* OWSFileSystem.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = OWSFileSystem.m; sourceTree = ""; }; - C33FDA8F255A57FD00E217F9 /* NSTimer+OWS.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "NSTimer+OWS.m"; sourceTree = ""; }; C33FDA90255A57FD00E217F9 /* TSYapDatabaseObject.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = TSYapDatabaseObject.m; sourceTree = ""; }; - C33FDA91255A57FD00E217F9 /* LKSyncOpenGroupsMessage.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = LKSyncOpenGroupsMessage.h; sourceTree = ""; }; C33FDA94255A57FE00E217F9 /* Data+Streaming.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "Data+Streaming.swift"; sourceTree = ""; }; C33FDA95255A57FE00E217F9 /* OWSChunkedOutputStream.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = OWSChunkedOutputStream.h; sourceTree = ""; }; C33FDA96255A57FE00E217F9 /* OWSDispatch.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = OWSDispatch.h; sourceTree = ""; }; C33FDA97255A57FE00E217F9 /* TSIncomingMessage.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = TSIncomingMessage.m; sourceTree = ""; }; C33FDA98255A57FE00E217F9 /* RotateSignedKeyOperation.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RotateSignedKeyOperation.swift; sourceTree = ""; }; C33FDA99255A57FE00E217F9 /* OutageDetection.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = OutageDetection.swift; sourceTree = ""; }; - C33FDA9A255A57FE00E217F9 /* OWSLinkedDeviceReadReceipt.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = OWSLinkedDeviceReadReceipt.m; sourceTree = ""; }; - C33FDA9B255A57FE00E217F9 /* OWSProvisioningMessage.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = OWSProvisioningMessage.m; sourceTree = ""; }; - C33FDA9C255A57FE00E217F9 /* OWSSyncContactsMessage.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = OWSSyncContactsMessage.m; sourceTree = ""; }; C33FDA9D255A57FF00E217F9 /* OWSContactsOutputStream.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = OWSContactsOutputStream.h; sourceTree = ""; }; C33FDA9E255A57FF00E217F9 /* ReverseDispatchQueue.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ReverseDispatchQueue.swift; sourceTree = ""; }; - C33FDA9F255A57FF00E217F9 /* NetworkManager.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NetworkManager.swift; sourceTree = ""; }; C33FDAA0255A57FF00E217F9 /* OWSRecipientIdentity.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = OWSRecipientIdentity.h; sourceTree = ""; }; C33FDAA1255A57FF00E217F9 /* TSYapDatabaseObject.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = TSYapDatabaseObject.h; sourceTree = ""; }; - C33FDAA2255A57FF00E217F9 /* OWSAddToContactsOfferMessage.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = OWSAddToContactsOfferMessage.m; sourceTree = ""; }; - C33FDAA3255A57FF00E217F9 /* OWSAddToContactsOfferMessage.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = OWSAddToContactsOfferMessage.h; sourceTree = ""; }; C33FDAA4255A57FF00E217F9 /* SSKProto.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SSKProto.swift; sourceTree = ""; }; - C33FDAA5255A57FF00E217F9 /* OWSRequestMaker.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = OWSRequestMaker.swift; sourceTree = ""; }; - C33FDAA6255A57FF00E217F9 /* OWSLinkedDeviceReadReceipt.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = OWSLinkedDeviceReadReceipt.h; sourceTree = ""; }; C33FDAA7255A57FF00E217F9 /* OWSPrimaryStorage+SignedPreKeyStore.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "OWSPrimaryStorage+SignedPreKeyStore.h"; sourceTree = ""; }; C33FDAA8255A57FF00E217F9 /* BuildConfiguration.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = BuildConfiguration.swift; sourceTree = ""; }; - C33FDAA9255A580000E217F9 /* Mnemonic.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Mnemonic.swift; sourceTree = ""; }; C33FDAAA255A580000E217F9 /* NSObject+Casting.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "NSObject+Casting.m"; sourceTree = ""; }; - C33FDAAB255A580000E217F9 /* OWSWebSocket.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = OWSWebSocket.m; sourceTree = ""; }; - C33FDAAD255A580000E217F9 /* OWSDeviceProvisioner.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = OWSDeviceProvisioner.h; sourceTree = ""; }; - C33FDAAE255A580000E217F9 /* OWSReceiptsForSenderMessage.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = OWSReceiptsForSenderMessage.m; sourceTree = ""; }; C33FDAAF255A580000E217F9 /* String+Trimming.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "String+Trimming.swift"; sourceTree = ""; }; C33FDAB0255A580000E217F9 /* ProvisioningProto.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ProvisioningProto.swift; sourceTree = ""; }; C33FDAB1255A580000E217F9 /* OWSStorage.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = OWSStorage.m; sourceTree = ""; }; - C33FDAB2255A580000E217F9 /* TSNetworkManager.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = TSNetworkManager.m; sourceTree = ""; }; C33FDAB3255A580000E217F9 /* TSContactThread.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = TSContactThread.h; sourceTree = ""; }; - C33FDAB5255A580000E217F9 /* TSNetworkManager.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = TSNetworkManager.h; sourceTree = ""; }; - C33FDAB6255A580100E217F9 /* SyncMessagesProtocol.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SyncMessagesProtocol.swift; sourceTree = ""; }; C33FDAB7255A580100E217F9 /* OWSFailedMessagesJob.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = OWSFailedMessagesJob.m; sourceTree = ""; }; C33FDAB8255A580100E217F9 /* NSArray+Functional.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "NSArray+Functional.m"; sourceTree = ""; }; C33FDAB9255A580100E217F9 /* OWSStorage+Subclass.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "OWSStorage+Subclass.h"; sourceTree = ""; }; - C33FDABA255A580100E217F9 /* OWSWebSocket.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = OWSWebSocket.h; sourceTree = ""; }; C33FDABB255A580100E217F9 /* OWSGroupsOutputStream.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = OWSGroupsOutputStream.h; sourceTree = ""; }; C33FDABD255A580100E217F9 /* OWSOutgoingReceiptManager.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = OWSOutgoingReceiptManager.h; sourceTree = ""; }; C33FDABE255A580100E217F9 /* TSConstants.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = TSConstants.m; sourceTree = ""; }; @@ -1800,24 +1528,11 @@ C33FDAC2255A580200E217F9 /* TSAttachment.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = TSAttachment.m; sourceTree = ""; }; C33FDAC3255A580200E217F9 /* OWSDispatch.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = OWSDispatch.m; sourceTree = ""; }; C33FDAC4255A580200E217F9 /* TSAttachmentStream.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = TSAttachmentStream.m; sourceTree = ""; }; - C33FDAC5255A580200E217F9 /* OWSCountryMetadata.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = OWSCountryMetadata.m; sourceTree = ""; }; C33FDAC6255A580200E217F9 /* Fingerprint.pb.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Fingerprint.pb.swift; sourceTree = ""; }; - C33FDAC7255A580200E217F9 /* OWSSignalService.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = OWSSignalService.h; sourceTree = ""; }; - C33FDAC8255A580200E217F9 /* OWSCensorshipConfiguration.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = OWSCensorshipConfiguration.h; sourceTree = ""; }; - C33FDAC9255A580200E217F9 /* OWSContact.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = OWSContact.h; sourceTree = ""; }; - C33FDACA255A580200E217F9 /* LokiMessage.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = LokiMessage.swift; sourceTree = ""; }; - C33FDACB255A580200E217F9 /* CDSSigningCertificate.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = CDSSigningCertificate.m; sourceTree = ""; }; C33FDACD255A580200E217F9 /* SSKJobRecord.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SSKJobRecord.m; sourceTree = ""; }; - C33FDACE255A580300E217F9 /* OWSVerificationStateSyncMessage.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = OWSVerificationStateSyncMessage.m; sourceTree = ""; }; C33FDACF255A580300E217F9 /* OWSAttachmentDownloads.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = OWSAttachmentDownloads.h; sourceTree = ""; }; - C33FDAD0255A580300E217F9 /* CDSQuote.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = CDSQuote.m; sourceTree = ""; }; - C33FDAD1255A580300E217F9 /* OWSDynamicOutgoingMessage.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = OWSDynamicOutgoingMessage.m; sourceTree = ""; }; - C33FDAD2255A580300E217F9 /* Contact.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Contact.h; sourceTree = ""; }; C33FDAD3255A580300E217F9 /* TSThread.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = TSThread.h; sourceTree = ""; }; - C33FDAD4255A580300E217F9 /* EncryptionUtilities.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = EncryptionUtilities.swift; sourceTree = ""; }; C33FDAD5255A580300E217F9 /* TSQuotedMessage.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = TSQuotedMessage.h; sourceTree = ""; }; - C33FDAD6255A580300E217F9 /* OWSAnalytics.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = OWSAnalytics.h; sourceTree = ""; }; - C33FDAD7255A580300E217F9 /* OWSDeviceProvisioner.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = OWSDeviceProvisioner.m; sourceTree = ""; }; C33FDAD8255A580300E217F9 /* OWSGroupsOutputStream.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = OWSGroupsOutputStream.m; sourceTree = ""; }; C33FDAD9255A580300E217F9 /* OWSDisappearingMessagesConfiguration.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = OWSDisappearingMessagesConfiguration.h; sourceTree = ""; }; C33FDADA255A580400E217F9 /* OWSDisappearingConfigurationUpdateInfoMessage.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = OWSDisappearingConfigurationUpdateInfoMessage.h; sourceTree = ""; }; @@ -1828,95 +1543,63 @@ C33FDADF255A580400E217F9 /* PublicChatManager.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PublicChatManager.swift; sourceTree = ""; }; C33FDAE0255A580400E217F9 /* ByteParser.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ByteParser.m; sourceTree = ""; }; C33FDAE1255A580400E217F9 /* OWSReadTracking.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = OWSReadTracking.h; sourceTree = ""; }; - C33FDAE2255A580400E217F9 /* OWSReadReceiptsForLinkedDevicesMessage.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = OWSReadReceiptsForLinkedDevicesMessage.h; sourceTree = ""; }; C33FDAE4255A580400E217F9 /* TSAttachmentStream.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = TSAttachmentStream.h; sourceTree = ""; }; - C33FDAE5255A580400E217F9 /* OWSAddToProfileWhitelistOfferMessage.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = OWSAddToProfileWhitelistOfferMessage.m; sourceTree = ""; }; C33FDAE6255A580400E217F9 /* TSInteraction.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = TSInteraction.h; sourceTree = ""; }; C33FDAE7255A580500E217F9 /* TSErrorMessage.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = TSErrorMessage.m; sourceTree = ""; }; C33FDAE8255A580500E217F9 /* OWSMessageUtils.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = OWSMessageUtils.h; sourceTree = ""; }; C33FDAE9255A580500E217F9 /* SSKMessageSenderJobRecord.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SSKMessageSenderJobRecord.m; sourceTree = ""; }; C33FDAEA255A580500E217F9 /* OWSBackupFragment.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = OWSBackupFragment.h; sourceTree = ""; }; - C33FDAEB255A580500E217F9 /* OWSDeviceProvisioningCodeService.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = OWSDeviceProvisioningCodeService.m; sourceTree = ""; }; C33FDAEC255A580500E217F9 /* SignalRecipient.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SignalRecipient.h; sourceTree = ""; }; C33FDAED255A580500E217F9 /* SSKMessageSenderJobRecord.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SSKMessageSenderJobRecord.h; sourceTree = ""; }; - C33FDAEE255A580500E217F9 /* OWSFingerprintBuilder.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = OWSFingerprintBuilder.m; sourceTree = ""; }; C33FDAEF255A580500E217F9 /* NSData+Image.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "NSData+Image.m"; sourceTree = ""; }; - C33FDAF0255A580500E217F9 /* ContactsUpdater.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ContactsUpdater.h; sourceTree = ""; }; C33FDAF1255A580500E217F9 /* OWSThumbnailService.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = OWSThumbnailService.swift; sourceTree = ""; }; C33FDAF2255A580500E217F9 /* ProxiedContentDownloader.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ProxiedContentDownloader.swift; sourceTree = ""; }; C33FDAF3255A580500E217F9 /* OWSPrimaryStorage+SessionStore.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "OWSPrimaryStorage+SessionStore.m"; sourceTree = ""; }; C33FDAF4255A580600E217F9 /* SSKEnvironment.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SSKEnvironment.m; sourceTree = ""; }; - C33FDAF5255A580600E217F9 /* OWSAnalyticsEvents.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = OWSAnalyticsEvents.h; sourceTree = ""; }; - C33FDAF6255A580600E217F9 /* OWSIncomingSentMessageTranscript.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = OWSIncomingSentMessageTranscript.h; sourceTree = ""; }; C33FDAF7255A580600E217F9 /* OWSPrimaryStorage+SignedPreKeyStore.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "OWSPrimaryStorage+SignedPreKeyStore.m"; sourceTree = ""; }; - C33FDAF8255A580600E217F9 /* OWSSyncGroupsRequestMessage.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = OWSSyncGroupsRequestMessage.h; sourceTree = ""; }; C33FDAF9255A580600E217F9 /* TSContactThread.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = TSContactThread.m; sourceTree = ""; }; - C33FDAFA255A580600E217F9 /* LKDeviceLinkMessage.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = LKDeviceLinkMessage.m; sourceTree = ""; }; C33FDAFB255A580600E217F9 /* SessionMetaProtocol.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SessionMetaProtocol.swift; sourceTree = ""; }; C33FDAFC255A580600E217F9 /* MIMETypeUtil.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MIMETypeUtil.h; sourceTree = ""; }; C33FDAFD255A580600E217F9 /* LRUCache.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = LRUCache.swift; sourceTree = ""; }; C33FDAFE255A580600E217F9 /* OWSStorage.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = OWSStorage.h; sourceTree = ""; }; C33FDAFF255A580600E217F9 /* DisplayNameUtilities.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DisplayNameUtilities.swift; sourceTree = ""; }; - C33FDB00255A580600E217F9 /* OWSRequestBuilder.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = OWSRequestBuilder.m; sourceTree = ""; }; C33FDB01255A580700E217F9 /* AppReadiness.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AppReadiness.h; sourceTree = ""; }; C33FDB02255A580700E217F9 /* TSCall.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = TSCall.h; sourceTree = ""; }; C33FDB03255A580700E217F9 /* OWSPrimaryStorage+SessionStore.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "OWSPrimaryStorage+SessionStore.h"; sourceTree = ""; }; - C33FDB05255A580700E217F9 /* OWSFingerprint.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = OWSFingerprint.m; sourceTree = ""; }; - C33FDB06255A580700E217F9 /* OWSRequestBuilder.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = OWSRequestBuilder.h; sourceTree = ""; }; C33FDB07255A580700E217F9 /* OWSBackupFragment.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = OWSBackupFragment.m; sourceTree = ""; }; - C33FDB08255A580700E217F9 /* OWSProfileKeyMessage.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = OWSProfileKeyMessage.h; sourceTree = ""; }; C33FDB09255A580700E217F9 /* NSError+MessageSending.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "NSError+MessageSending.m"; sourceTree = ""; }; C33FDB0A255A580700E217F9 /* TSGroupModel.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = TSGroupModel.h; sourceTree = ""; }; - C33FDB0B255A580700E217F9 /* OWSVerificationStateChangeMessage.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = OWSVerificationStateChangeMessage.m; sourceTree = ""; }; - C33FDB0C255A580700E217F9 /* CDSQuote.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CDSQuote.h; sourceTree = ""; }; C33FDB0D255A580800E217F9 /* NSArray+OWS.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "NSArray+OWS.m"; sourceTree = ""; }; C33FDB0E255A580800E217F9 /* NSError+MessageSending.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "NSError+MessageSending.h"; sourceTree = ""; }; - C33FDB0F255A580800E217F9 /* DeviceLinkingSession.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DeviceLinkingSession.swift; sourceTree = ""; }; - C33FDB10255A580800E217F9 /* OWSBatchMessageProcessor.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = OWSBatchMessageProcessor.m; sourceTree = ""; }; C33FDB12255A580800E217F9 /* NSString+SSK.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "NSString+SSK.h"; sourceTree = ""; }; - C33FDB13255A580800E217F9 /* LKUnlinkDeviceMessage.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = LKUnlinkDeviceMessage.m; sourceTree = ""; }; C33FDB14255A580800E217F9 /* OWSMath.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = OWSMath.h; sourceTree = ""; }; - C33FDB16255A580800E217F9 /* OWSDisappearingMessagesConfigurationMessage.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = OWSDisappearingMessagesConfigurationMessage.h; sourceTree = ""; }; C33FDB17255A580800E217F9 /* FunctionalUtil.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = FunctionalUtil.m; sourceTree = ""; }; - C33FDB18255A580800E217F9 /* OWSSignalService.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = OWSSignalService.m; sourceTree = ""; }; C33FDB19255A580900E217F9 /* GroupUtilities.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = GroupUtilities.swift; sourceTree = ""; }; C33FDB1A255A580900E217F9 /* OWSPrimaryStorage+Calling.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "OWSPrimaryStorage+Calling.h"; sourceTree = ""; }; - C33FDB1B255A580900E217F9 /* OWSDeviceProvisioningCodeService.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = OWSDeviceProvisioningCodeService.h; sourceTree = ""; }; C33FDB1C255A580900E217F9 /* UIImage+OWS.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "UIImage+OWS.h"; sourceTree = ""; }; C33FDB1D255A580900E217F9 /* OWSReadReceiptManager.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = OWSReadReceiptManager.h; sourceTree = ""; }; C33FDB1E255A580900E217F9 /* OWSIncomingMessageFinder.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = OWSIncomingMessageFinder.m; sourceTree = ""; }; - C33FDB1F255A580900E217F9 /* DeviceLinkingUtilities.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DeviceLinkingUtilities.swift; sourceTree = ""; }; C33FDB20255A580900E217F9 /* TSDatabaseSecondaryIndexes.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = TSDatabaseSecondaryIndexes.m; sourceTree = ""; }; - C33FDB21255A580900E217F9 /* OWS2FAManager.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = OWS2FAManager.h; sourceTree = ""; }; C33FDB22255A580900E217F9 /* OWSMediaUtils.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = OWSMediaUtils.swift; sourceTree = ""; }; - C33FDB24255A580900E217F9 /* OWSOutgoingSentMessageTranscript.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = OWSOutgoingSentMessageTranscript.h; sourceTree = ""; }; C33FDB25255A580900E217F9 /* TSDatabaseSecondaryIndexes.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = TSDatabaseSecondaryIndexes.h; sourceTree = ""; }; C33FDB26255A580A00E217F9 /* FingerprintProto.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = FingerprintProto.swift; sourceTree = ""; }; - C33FDB27255A580A00E217F9 /* OWSEndSessionMessage.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = OWSEndSessionMessage.m; sourceTree = ""; }; - C33FDB28255A580A00E217F9 /* OWSOutgoingCallMessage.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = OWSOutgoingCallMessage.m; sourceTree = ""; }; C33FDB29255A580A00E217F9 /* NSData+Image.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "NSData+Image.h"; sourceTree = ""; }; C33FDB2A255A580A00E217F9 /* OWSIncompleteCallsJob.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = OWSIncompleteCallsJob.h; sourceTree = ""; }; C33FDB2B255A580A00E217F9 /* OWSProvisioningCipher.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = OWSProvisioningCipher.m; sourceTree = ""; }; C33FDB2C255A580A00E217F9 /* TSDatabaseView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = TSDatabaseView.h; sourceTree = ""; }; - C33FDB2D255A580A00E217F9 /* OWSDevice.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = OWSDevice.h; sourceTree = ""; }; - C33FDB2E255A580A00E217F9 /* OWSEndSessionMessage.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = OWSEndSessionMessage.h; sourceTree = ""; }; C33FDB2F255A580A00E217F9 /* ContactsManagerProtocol.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ContactsManagerProtocol.h; sourceTree = ""; }; - C33FDB30255A580A00E217F9 /* OWSDevice.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = OWSDevice.m; sourceTree = ""; }; C33FDB31255A580A00E217F9 /* SSKEnvironment.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SSKEnvironment.h; sourceTree = ""; }; C33FDB32255A580A00E217F9 /* SSKIncrementingIdFinder.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SSKIncrementingIdFinder.swift; sourceTree = ""; }; C33FDB33255A580B00E217F9 /* Provisioning.pb.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Provisioning.pb.swift; sourceTree = ""; }; C33FDB34255A580B00E217F9 /* ClosedGroupPoller.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ClosedGroupPoller.swift; sourceTree = ""; }; - C33FDB35255A580B00E217F9 /* OWSContactDiscoveryOperation.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = OWSContactDiscoveryOperation.swift; sourceTree = ""; }; C33FDB36255A580B00E217F9 /* Storage.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Storage.swift; sourceTree = ""; }; C33FDB37255A580B00E217F9 /* Storage+SnodeAPI.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "Storage+SnodeAPI.swift"; sourceTree = ""; }; C33FDB38255A580B00E217F9 /* OWSBackgroundTask.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = OWSBackgroundTask.h; sourceTree = ""; }; C33FDB3A255A580B00E217F9 /* Poller.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Poller.swift; sourceTree = ""; }; C33FDB3B255A580B00E217F9 /* NSNotificationCenter+OWS.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "NSNotificationCenter+OWS.h"; sourceTree = ""; }; - C33FDB3D255A580B00E217F9 /* OWSProfileKeyMessage.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = OWSProfileKeyMessage.m; sourceTree = ""; }; C33FDB3F255A580C00E217F9 /* String+SSK.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "String+SSK.swift"; sourceTree = ""; }; C33FDB40255A580C00E217F9 /* SignalIOSProto.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SignalIOSProto.swift; sourceTree = ""; }; C33FDB41255A580C00E217F9 /* MIMETypeUtil.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MIMETypeUtil.m; sourceTree = ""; }; - C33FDB42255A580C00E217F9 /* OWSCountryMetadata.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = OWSCountryMetadata.h; sourceTree = ""; }; C33FDB43255A580C00E217F9 /* YapDatabaseConnection+OWS.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "YapDatabaseConnection+OWS.m"; sourceTree = ""; }; C33FDB44255A580C00E217F9 /* OWSContactsOutputStream.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = OWSContactsOutputStream.m; sourceTree = ""; }; C33FDB45255A580C00E217F9 /* NSString+SSK.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "NSString+SSK.m"; sourceTree = ""; }; @@ -1924,7 +1607,6 @@ C33FDB47255A580C00E217F9 /* OWSPrimaryStorage+PreKeyStore.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "OWSPrimaryStorage+PreKeyStore.h"; sourceTree = ""; }; C33FDB48255A580C00E217F9 /* TSOutgoingMessage.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = TSOutgoingMessage.h; sourceTree = ""; }; C33FDB49255A580C00E217F9 /* WeakTimer.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = WeakTimer.swift; sourceTree = ""; }; - C33FDB4A255A580C00E217F9 /* SignalServiceClient.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SignalServiceClient.swift; sourceTree = ""; }; C33FDB4B255A580C00E217F9 /* OWSChunkedOutputStream.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = OWSChunkedOutputStream.m; sourceTree = ""; }; C33FDB4C255A580D00E217F9 /* AppVersion.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AppVersion.h; sourceTree = ""; }; C33FDB4F255A580D00E217F9 /* SSKJobRecord.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SSKJobRecord.h; sourceTree = ""; }; @@ -1935,22 +1617,16 @@ C33FDB54255A580D00E217F9 /* DataSource.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = DataSource.h; sourceTree = ""; }; C33FDB55255A580D00E217F9 /* TSInvalidIdentityKeySendingErrorMessage.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = TSInvalidIdentityKeySendingErrorMessage.m; sourceTree = ""; }; C33FDB56255A580D00E217F9 /* TSOutgoingMessage.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = TSOutgoingMessage.m; sourceTree = ""; }; - C33FDB57255A580D00E217F9 /* OWSSyncContactsMessage.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = OWSSyncContactsMessage.h; sourceTree = ""; }; C33FDB58255A580E00E217F9 /* OWSPrimaryStorage+Loki.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "OWSPrimaryStorage+Loki.m"; sourceTree = ""; }; C33FDB59255A580E00E217F9 /* OWSFailedAttachmentDownloadsJob.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = OWSFailedAttachmentDownloadsJob.m; sourceTree = ""; }; C33FDB5A255A580E00E217F9 /* OWSUDManager.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = OWSUDManager.swift; sourceTree = ""; }; C33FDB5B255A580E00E217F9 /* YapDatabaseTransaction+OWS.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "YapDatabaseTransaction+OWS.m"; sourceTree = ""; }; C33FDB5C255A580E00E217F9 /* NSArray+Functional.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "NSArray+Functional.h"; sourceTree = ""; }; C33FDB5D255A580E00E217F9 /* TSErrorMessage_privateConstructor.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = TSErrorMessage_privateConstructor.h; sourceTree = ""; }; - C33FDB5E255A580E00E217F9 /* ContactParser.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ContactParser.swift; sourceTree = ""; }; C33FDB5F255A580E00E217F9 /* YapDatabaseConnection+OWS.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "YapDatabaseConnection+OWS.h"; sourceTree = ""; }; C33FDB60255A580E00E217F9 /* TSMessage.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = TSMessage.m; sourceTree = ""; }; - C33FDB61255A580E00E217F9 /* LKUnlinkDeviceMessage.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = LKUnlinkDeviceMessage.h; sourceTree = ""; }; C33FDB62255A580E00E217F9 /* OWSProvisioningCipher.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = OWSProvisioningCipher.h; sourceTree = ""; }; - C33FDB63255A580E00E217F9 /* OWSDynamicOutgoingMessage.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = OWSDynamicOutgoingMessage.h; sourceTree = ""; }; C33FDB64255A580E00E217F9 /* PreKeyRefreshOperation.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PreKeyRefreshOperation.swift; sourceTree = ""; }; - C33FDB65255A580F00E217F9 /* OWSSyncConfigurationMessage.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = OWSSyncConfigurationMessage.h; sourceTree = ""; }; - C33FDB66255A580F00E217F9 /* ContactDiscoveryService.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ContactDiscoveryService.h; sourceTree = ""; }; C33FDB67255A580F00E217F9 /* OWSMediaGalleryFinder.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = OWSMediaGalleryFinder.h; sourceTree = ""; }; C33FDB68255A580F00E217F9 /* ContentProxy.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ContentProxy.swift; sourceTree = ""; }; C33FDB69255A580F00E217F9 /* FeatureFlags.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = FeatureFlags.swift; sourceTree = ""; }; @@ -1958,55 +1634,37 @@ C33FDB6B255A580F00E217F9 /* LKUserDefaults.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = LKUserDefaults.swift; sourceTree = ""; }; C33FDB6C255A580F00E217F9 /* NSNotificationCenter+OWS.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "NSNotificationCenter+OWS.m"; sourceTree = ""; }; C33FDB6D255A580F00E217F9 /* TSPreKeyManager.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = TSPreKeyManager.h; sourceTree = ""; }; - C33FDB6E255A580F00E217F9 /* SSKProtoPrekeyBundleMessage+Loki.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "SSKProtoPrekeyBundleMessage+Loki.swift"; sourceTree = ""; }; C33FDB6F255A580F00E217F9 /* OWSOutgoingReceiptManager.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = OWSOutgoingReceiptManager.m; sourceTree = ""; }; C33FDB70255A580F00E217F9 /* OWSMessageServiceParams.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = OWSMessageServiceParams.m; sourceTree = ""; }; C33FDB71255A581000E217F9 /* OWSMediaGalleryFinder.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = OWSMediaGalleryFinder.m; sourceTree = ""; }; - C33FDB72255A581000E217F9 /* DeviceLinkIndex.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DeviceLinkIndex.swift; sourceTree = ""; }; C33FDB73255A581000E217F9 /* TSGroupModel.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = TSGroupModel.m; sourceTree = ""; }; C33FDB74255A581000E217F9 /* TSInvalidIdentityKeyErrorMessage.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = TSInvalidIdentityKeyErrorMessage.m; sourceTree = ""; }; C33FDB75255A581000E217F9 /* AppReadiness.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = AppReadiness.m; sourceTree = ""; }; - C33FDB76255A581000E217F9 /* ClosedGroupUtilities.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ClosedGroupUtilities.swift; sourceTree = ""; }; C33FDB77255A581000E217F9 /* NSUserDefaults+OWS.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "NSUserDefaults+OWS.m"; sourceTree = ""; }; C33FDB78255A581000E217F9 /* OWSOperation.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = OWSOperation.m; sourceTree = ""; }; - C33FDB79255A581000E217F9 /* PhoneNumberUtil.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = PhoneNumberUtil.m; sourceTree = ""; }; C33FDB7A255A581000E217F9 /* NotificationsProtocol.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = NotificationsProtocol.h; sourceTree = ""; }; - C33FDB7C255A581000E217F9 /* OWSVerificationStateChangeMessage.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = OWSVerificationStateChangeMessage.h; sourceTree = ""; }; C33FDB7E255A581100E217F9 /* TSPreKeyManager.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = TSPreKeyManager.m; sourceTree = ""; }; C33FDB7F255A581100E217F9 /* FullTextSearchFinder.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = FullTextSearchFinder.swift; sourceTree = ""; }; C33FDB80255A581100E217F9 /* Notification+Loki.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "Notification+Loki.swift"; sourceTree = ""; }; C33FDB81255A581100E217F9 /* UIImage+OWS.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "UIImage+OWS.m"; sourceTree = ""; }; - C33FDB82255A581100E217F9 /* MessageWrapper.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MessageWrapper.swift; sourceTree = ""; }; C33FDB83255A581100E217F9 /* TSQuotedMessage.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = TSQuotedMessage.m; sourceTree = ""; }; - C33FDB84255A581100E217F9 /* OWSIncomingSentMessageTranscript.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = OWSIncomingSentMessageTranscript.m; sourceTree = ""; }; C33FDB85255A581100E217F9 /* AppContext.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = AppContext.m; sourceTree = ""; }; - C33FDB86255A581100E217F9 /* OWSRequestFactory.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = OWSRequestFactory.m; sourceTree = ""; }; C33FDB87255A581100E217F9 /* JobQueue.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = JobQueue.swift; sourceTree = ""; }; C33FDB88255A581200E217F9 /* TSAccountManager.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = TSAccountManager.m; sourceTree = ""; }; - C33FDB89255A581200E217F9 /* FileServerAPI+Deprecated.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "FileServerAPI+Deprecated.swift"; sourceTree = ""; }; C33FDB8A255A581200E217F9 /* AppContext.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AppContext.h; sourceTree = ""; }; C33FDB8B255A581200E217F9 /* Storage+SessionManagement.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "Storage+SessionManagement.swift"; sourceTree = ""; }; C33FDB8C255A581200E217F9 /* TSInvalidIdentityKeyErrorMessage.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = TSInvalidIdentityKeyErrorMessage.h; sourceTree = ""; }; - C33FDB8D255A581200E217F9 /* DeviceLinkingSessionDelegate.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DeviceLinkingSessionDelegate.swift; sourceTree = ""; }; - C33FDB8E255A581200E217F9 /* OWSContact+Private.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "OWSContact+Private.h"; sourceTree = ""; }; C33FDB8F255A581200E217F9 /* ParamParser.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ParamParser.swift; sourceTree = ""; }; C33FDB91255A581200E217F9 /* ProtoUtils.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ProtoUtils.h; sourceTree = ""; }; - C33FDB92255A581200E217F9 /* OWSDeviceProvisioningService.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = OWSDeviceProvisioningService.m; sourceTree = ""; }; C33FDB93255A581200E217F9 /* PreKeyBundle+jsonDict.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "PreKeyBundle+jsonDict.m"; sourceTree = ""; }; C33FDB94255A581300E217F9 /* TSAccountManager.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = TSAccountManager.h; sourceTree = ""; }; C33FDB95255A581300E217F9 /* Storage+Collections.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "Storage+Collections.swift"; sourceTree = ""; }; - C33FDB96255A581300E217F9 /* OWSReadReceiptsForLinkedDevicesMessage.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = OWSReadReceiptsForLinkedDevicesMessage.m; sourceTree = ""; }; - C33FDB97255A581300E217F9 /* OWSDisappearingMessagesConfigurationMessage.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = OWSDisappearingMessagesConfigurationMessage.m; sourceTree = ""; }; - C33FDB98255A581300E217F9 /* DeviceNames.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DeviceNames.swift; sourceTree = ""; }; C33FDB99255A581300E217F9 /* OWSPrimaryStorage+keyFromIntLong.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "OWSPrimaryStorage+keyFromIntLong.m"; sourceTree = ""; }; - C33FDB9A255A581300E217F9 /* OWS2FAManager.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = OWS2FAManager.m; sourceTree = ""; }; C33FDB9C255A581300E217F9 /* TSIncomingMessage.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = TSIncomingMessage.h; sourceTree = ""; }; C33FDB9D255A581300E217F9 /* OWSCallMessageHandler.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = OWSCallMessageHandler.h; sourceTree = ""; }; C33FDB9E255A581400E217F9 /* TSAttachmentPointer.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = TSAttachmentPointer.m; sourceTree = ""; }; - C33FDB9F255A581400E217F9 /* TTLUtilities.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TTLUtilities.swift; sourceTree = ""; }; C33FDBA0255A581400E217F9 /* TSStorageHeaders.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = TSStorageHeaders.h; sourceTree = ""; }; C33FDBA1255A581400E217F9 /* OWSOperation.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = OWSOperation.h; sourceTree = ""; }; - C33FDBA2255A581400E217F9 /* PhoneNumber.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PhoneNumber.h; sourceTree = ""; }; C33FDBA3255A581400E217F9 /* SessionManagementProtocol.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SessionManagementProtocol.swift; sourceTree = ""; }; C33FDBA4255A581400E217F9 /* OWSDisappearingMessagesConfiguration.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = OWSDisappearingMessagesConfiguration.m; sourceTree = ""; }; C33FDBA5255A581400E217F9 /* SignalServiceProfile.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SignalServiceProfile.swift; sourceTree = ""; }; @@ -2014,15 +1672,10 @@ C33FDBA8255A581500E217F9 /* OWSLinkPreview.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = OWSLinkPreview.swift; sourceTree = ""; }; C33FDBA9255A581500E217F9 /* OWSIdentityManager.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = OWSIdentityManager.m; sourceTree = ""; }; C33FDBAB255A581500E217F9 /* OWSFileSystem.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = OWSFileSystem.h; sourceTree = ""; }; - C33FDBAC255A581500E217F9 /* Data+SecureRandom.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "Data+SecureRandom.swift"; sourceTree = ""; }; C33FDBAD255A581500E217F9 /* OWSRecordTranscriptJob.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = OWSRecordTranscriptJob.h; sourceTree = ""; }; C33FDBAE255A581500E217F9 /* SignalAccount.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SignalAccount.h; sourceTree = ""; }; - C33FDBAF255A581500E217F9 /* OWSAnalyticsEvents.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = OWSAnalyticsEvents.m; sourceTree = ""; }; C33FDBB0255A581500E217F9 /* TSErrorMessage.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = TSErrorMessage.h; sourceTree = ""; }; - C33FDBB1255A581500E217F9 /* TSSocketManager.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = TSSocketManager.h; sourceTree = ""; }; - C33FDBB3255A581500E217F9 /* OWSOutgoingNullMessage.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = OWSOutgoingNullMessage.h; sourceTree = ""; }; C33FDBB4255A581600E217F9 /* NSURLSessionDataTask+StatusCode.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "NSURLSessionDataTask+StatusCode.m"; sourceTree = ""; }; - C33FDBB5255A581600E217F9 /* OWSSyncGroupsMessage.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = OWSSyncGroupsMessage.h; sourceTree = ""; }; C33FDBB6255A581600E217F9 /* DataSource.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = DataSource.m; sourceTree = ""; }; C33FDBB7255A581600E217F9 /* SignalRecipient.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SignalRecipient.m; sourceTree = ""; }; C33FDBB8255A581600E217F9 /* TSThread.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = TSThread.m; sourceTree = ""; }; @@ -2030,94 +1683,55 @@ C33FDBBA255A581600E217F9 /* OWSPrimaryStorage+keyFromIntLong.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "OWSPrimaryStorage+keyFromIntLong.h"; sourceTree = ""; }; C33FDBBB255A581600E217F9 /* OWSPrimaryStorage+Loki.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "OWSPrimaryStorage+Loki.h"; sourceTree = ""; }; C33FDBBC255A581600E217F9 /* SSKKeychainStorage.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SSKKeychainStorage.swift; sourceTree = ""; }; - C33FDBBD255A581600E217F9 /* OWSAddToProfileWhitelistOfferMessage.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = OWSAddToProfileWhitelistOfferMessage.h; sourceTree = ""; }; C33FDBBF255A581700E217F9 /* OWSHTTPSecurityPolicy.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = OWSHTTPSecurityPolicy.m; sourceTree = ""; }; - C33FDBC0255A581700E217F9 /* OWSRequestFactory.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = OWSRequestFactory.h; sourceTree = ""; }; C33FDBC1255A581700E217F9 /* GeneralUtilities.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = GeneralUtilities.swift; sourceTree = ""; }; C33FDBC2255A581700E217F9 /* SSKAsserts.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SSKAsserts.h; sourceTree = ""; }; - C33FDBC3255A581700E217F9 /* AnyPromise+Conversion.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "AnyPromise+Conversion.swift"; sourceTree = ""; }; - C33FDBC4255A581700E217F9 /* TypingIndicatorMessage.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TypingIndicatorMessage.swift; sourceTree = ""; }; - C33FDBC8255A581700E217F9 /* OWSFingerprint.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = OWSFingerprint.h; sourceTree = ""; }; C33FDBC9255A581700E217F9 /* CreatePreKeysOperation.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CreatePreKeysOperation.swift; sourceTree = ""; }; C33FDBCA255A581700E217F9 /* LKGroupUtilities.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = LKGroupUtilities.h; sourceTree = ""; }; C33FDBCB255A581800E217F9 /* TSInvalidIdentityKeyReceivingErrorMessage.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = TSInvalidIdentityKeyReceivingErrorMessage.m; sourceTree = ""; }; C33FDBCE255A581800E217F9 /* OWSMessageServiceParams.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = OWSMessageServiceParams.h; sourceTree = ""; }; - C33FDBCF255A581800E217F9 /* OWSFingerprintBuilder.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = OWSFingerprintBuilder.h; sourceTree = ""; }; - C33FDBD0255A581800E217F9 /* OnionRequestAPI+Encryption.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "OnionRequestAPI+Encryption.swift"; sourceTree = ""; }; - C33FDBD1255A581800E217F9 /* DeviceLink.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DeviceLink.swift; sourceTree = ""; }; - C33FDBD2255A581800E217F9 /* OWSUnknownContactBlockOfferMessage.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = OWSUnknownContactBlockOfferMessage.h; sourceTree = ""; }; C33FDBD3255A581800E217F9 /* OWSSignalAddress.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = OWSSignalAddress.swift; sourceTree = ""; }; - C33FDBD4255A581900E217F9 /* OWSBatchMessageProcessor.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = OWSBatchMessageProcessor.h; sourceTree = ""; }; - C33FDBD5255A581900E217F9 /* OWSOutgoingSentMessageTranscript.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = OWSOutgoingSentMessageTranscript.m; sourceTree = ""; }; C33FDBD6255A581900E217F9 /* OWSUploadOperation.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = OWSUploadOperation.h; sourceTree = ""; }; C33FDBD7255A581900E217F9 /* OWSMessageUtils.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = OWSMessageUtils.m; sourceTree = ""; }; C33FDBD8255A581900E217F9 /* SignalIOS.pb.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SignalIOS.pb.swift; sourceTree = ""; }; - C33FDBD9255A581900E217F9 /* OWSVerificationStateSyncMessage.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = OWSVerificationStateSyncMessage.h; sourceTree = ""; }; C33FDBDA255A581900E217F9 /* Dictionary+Description.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "Dictionary+Description.swift"; sourceTree = ""; }; - C33FDBDB255A581900E217F9 /* OWSDevicesService.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = OWSDevicesService.h; sourceTree = ""; }; - C33FDBDC255A581900E217F9 /* ContactDiscoveryService.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ContactDiscoveryService.m; sourceTree = ""; }; C33FDBDD255A581900E217F9 /* OWSDisappearingMessagesJob.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = OWSDisappearingMessagesJob.m; sourceTree = ""; }; C33FDBDE255A581900E217F9 /* LokiPushNotificationManager.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = LokiPushNotificationManager.swift; sourceTree = ""; }; - C33FDBDF255A581A00E217F9 /* LKSyncOpenGroupsMessage.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = LKSyncOpenGroupsMessage.m; sourceTree = ""; }; - C33FDBE0255A581A00E217F9 /* OWSBlockedPhoneNumbersMessage.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = OWSBlockedPhoneNumbersMessage.m; sourceTree = ""; }; C33FDBE1255A581A00E217F9 /* LKGroupUtilities.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = LKGroupUtilities.m; sourceTree = ""; }; - C33FDBE2255A581A00E217F9 /* OWSUnknownContactBlockOfferMessage.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = OWSUnknownContactBlockOfferMessage.m; sourceTree = ""; }; - C33FDBE3255A581A00E217F9 /* CDSSigningCertificate.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CDSSigningCertificate.h; sourceTree = ""; }; - C33FDBE4255A581A00E217F9 /* OWSOutgoingSyncMessage.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = OWSOutgoingSyncMessage.h; sourceTree = ""; }; - C33FDBE5255A581A00E217F9 /* OWSDevicesService.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = OWSDevicesService.m; sourceTree = ""; }; - C33FDBE6255A581A00E217F9 /* OWSOutgoingNullMessage.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = OWSOutgoingNullMessage.m; sourceTree = ""; }; - C33FDBE7255A581A00E217F9 /* NSTimer+OWS.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "NSTimer+OWS.h"; sourceTree = ""; }; C33FDBE8255A581A00E217F9 /* Storage+OnionRequests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "Storage+OnionRequests.swift"; sourceTree = ""; }; C33FDBE9255A581A00E217F9 /* TSInteraction.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = TSInteraction.m; sourceTree = ""; }; - C33FDBEA255A581A00E217F9 /* SessionRequestMessage.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SessionRequestMessage.swift; sourceTree = ""; }; C33FDBEB255A581B00E217F9 /* OWSBlockingManager.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = OWSBlockingManager.h; sourceTree = ""; }; C33FDBEC255A581B00E217F9 /* OWSRecipientIdentity.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = OWSRecipientIdentity.m; sourceTree = ""; }; - C33FDBED255A581B00E217F9 /* ClosedGroupParser.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ClosedGroupParser.swift; sourceTree = ""; }; - C33FDBEE255A581B00E217F9 /* ClosedGroupUpdateMessage.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ClosedGroupUpdateMessage.swift; sourceTree = ""; }; C33FDBEF255A581B00E217F9 /* TSStorageKeys.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = TSStorageKeys.h; sourceTree = ""; }; C33FDBF0255A581B00E217F9 /* LokiDatabaseUtilities.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = LokiDatabaseUtilities.swift; sourceTree = ""; }; C33FDBF1255A581B00E217F9 /* OWSIdentityManager.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = OWSIdentityManager.h; sourceTree = ""; }; - C33FDBF3255A581B00E217F9 /* OWSBlockedPhoneNumbersMessage.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = OWSBlockedPhoneNumbersMessage.h; sourceTree = ""; }; C33FDBF4255A581B00E217F9 /* DisplayNameUtilities2.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DisplayNameUtilities2.swift; sourceTree = ""; }; - C33FDBF5255A581B00E217F9 /* OWSDeviceProvisioningService.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = OWSDeviceProvisioningService.h; sourceTree = ""; }; C33FDBF6255A581C00E217F9 /* NSURLSessionDataTask+StatusCode.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "NSURLSessionDataTask+StatusCode.h"; sourceTree = ""; }; C33FDBF7255A581C00E217F9 /* OWSIncompleteCallsJob.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = OWSIncompleteCallsJob.m; sourceTree = ""; }; C33FDBF8255A581C00E217F9 /* NSArray+OWS.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "NSArray+OWS.h"; sourceTree = ""; }; C33FDBF9255A581C00E217F9 /* OWSError.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = OWSError.h; sourceTree = ""; }; - C33FDBFA255A581C00E217F9 /* PhoneNumberUtil.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PhoneNumberUtil.h; sourceTree = ""; }; C33FDBFB255A581C00E217F9 /* Storage+PublicChats.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "Storage+PublicChats.swift"; sourceTree = ""; }; C33FDBFD255A581C00E217F9 /* OWSPrimaryStorage+Calling.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "OWSPrimaryStorage+Calling.m"; sourceTree = ""; }; C33FDBFE255A581C00E217F9 /* NSSet+Functional.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "NSSet+Functional.h"; sourceTree = ""; }; - C33FDBFF255A581C00E217F9 /* OWSOutgoingSyncMessage.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = OWSOutgoingSyncMessage.m; sourceTree = ""; }; - C33FDC00255A581C00E217F9 /* OWSSyncGroupsMessage.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = OWSSyncGroupsMessage.m; sourceTree = ""; }; C33FDC01255A581C00E217F9 /* TSGroupThread.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = TSGroupThread.m; sourceTree = ""; }; C33FDC02255A581D00E217F9 /* OWSPrimaryStorage.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = OWSPrimaryStorage.m; sourceTree = ""; }; C33FDC03255A581D00E217F9 /* ByteParser.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ByteParser.h; sourceTree = ""; }; - C33FDC04255A581D00E217F9 /* DecryptionUtilities.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DecryptionUtilities.swift; sourceTree = ""; }; C33FDC05255A581D00E217F9 /* OWSDisappearingMessagesFinder.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = OWSDisappearingMessagesFinder.h; sourceTree = ""; }; C33FDC06255A581D00E217F9 /* SignalAccount.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SignalAccount.m; sourceTree = ""; }; C33FDC07255A581D00E217F9 /* Storage+ClosedGroups.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "Storage+ClosedGroups.swift"; sourceTree = ""; }; C33FDC08255A581D00E217F9 /* SignalService.pb.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SignalService.pb.swift; sourceTree = ""; }; - C33FDC09255A581D00E217F9 /* AccountServiceClient.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AccountServiceClient.swift; sourceTree = ""; }; - C33FDC0A255A581D00E217F9 /* OWSContact.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = OWSContact.m; sourceTree = ""; }; C33FDC0B255A581D00E217F9 /* OWSError.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = OWSError.m; sourceTree = ""; }; C33FDC0C255A581E00E217F9 /* TSInfoMessage.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = TSInfoMessage.m; sourceTree = ""; }; - C33FDC0D255A581E00E217F9 /* OWSProvisioningMessage.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = OWSProvisioningMessage.h; sourceTree = ""; }; - C33FDC0F255A581E00E217F9 /* LKDeviceLinkMessage.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = LKDeviceLinkMessage.h; sourceTree = ""; }; - C33FDC10255A581E00E217F9 /* ProofOfWork.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ProofOfWork.swift; sourceTree = ""; }; - C33FDC11255A581E00E217F9 /* TSSocketManager.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = TSSocketManager.m; sourceTree = ""; }; C33FDC12255A581E00E217F9 /* TSConstants.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = TSConstants.h; sourceTree = ""; }; C33FDC13255A581E00E217F9 /* OWSAttachmentDownloads.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = OWSAttachmentDownloads.m; sourceTree = ""; }; C33FDC15255A581E00E217F9 /* TSAttachment.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = TSAttachment.h; sourceTree = ""; }; C33FDC16255A581E00E217F9 /* FunctionalUtil.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FunctionalUtil.h; sourceTree = ""; }; - C33FDC17255A581F00E217F9 /* SharedSenderKeysImplementation.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SharedSenderKeysImplementation.swift; sourceTree = ""; }; C33FDC18255A581F00E217F9 /* TSAttachmentPointer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = TSAttachmentPointer.h; sourceTree = ""; }; C33FDC19255A581F00E217F9 /* OWSQueues.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = OWSQueues.h; sourceTree = ""; }; C33FDC1B255A581F00E217F9 /* OWSBackgroundTask.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = OWSBackgroundTask.m; sourceTree = ""; }; C33FDC1C255A581F00E217F9 /* TSCall.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = TSCall.m; sourceTree = ""; }; - C33FDC1D255A581F00E217F9 /* OWSSyncConfigurationMessage.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = OWSSyncConfigurationMessage.m; sourceTree = ""; }; C33FDC1E255A581F00E217F9 /* OWSUploadOperation.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = OWSUploadOperation.m; sourceTree = ""; }; - C33FDC1F255A581F00E217F9 /* LokiSessionResetImplementation.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = LokiSessionResetImplementation.swift; sourceTree = ""; }; + C33FDC1F255A581F00E217F9 /* LokiSessionRestorationImplementation.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = LokiSessionRestorationImplementation.swift; sourceTree = ""; }; C3471ECA2555356A00297E91 /* MessageSender+Encryption.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "MessageSender+Encryption.swift"; sourceTree = ""; }; C3471F4125553A4D00297E91 /* Threading.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Threading.swift; sourceTree = ""; }; C3471F4B25553AB000297E91 /* MessageReceiver+Decryption.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "MessageReceiver+Decryption.swift"; sourceTree = ""; }; @@ -2150,42 +1764,34 @@ C364535B252467900045C478 /* AudioUtilities.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AudioUtilities.swift; sourceTree = ""; }; C37F53E8255BA9BB002AEA92 /* Environment.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = Environment.h; sourceTree = ""; }; C37F5402255BA9ED002AEA92 /* Environment.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = Environment.m; sourceTree = ""; }; - C38EEF09255B49A8007E1867 /* SSKProtoEnvelope+Conversion.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "SSKProtoEnvelope+Conversion.swift"; sourceTree = ""; }; - C38EEFD5255B5BA2007E1867 /* OldSnodeAPI.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OldSnodeAPI.swift; sourceTree = ""; }; - C38EF212255B6D3A007E1867 /* Theme.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = Theme.h; path = SignalUtilitiesKit/Theme.h; sourceTree = SOURCE_ROOT; }; - C38EF213255B6D3A007E1867 /* OWSConversationColor.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = OWSConversationColor.m; path = SignalUtilitiesKit/OWSConversationColor.m; sourceTree = SOURCE_ROOT; }; - C38EF214255B6D3A007E1867 /* Theme.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = Theme.m; path = SignalUtilitiesKit/Theme.m; sourceTree = SOURCE_ROOT; }; - C38EF215255B6D3A007E1867 /* OWSConversationColor.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = OWSConversationColor.h; path = SignalUtilitiesKit/OWSConversationColor.h; sourceTree = SOURCE_ROOT; }; - C38EF223255B6D5D007E1867 /* AttachmentSharing.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = AttachmentSharing.m; path = SignalUtilitiesKit/AttachmentSharing.m; sourceTree = SOURCE_ROOT; }; - C38EF224255B6D5D007E1867 /* SignalAttachment.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = SignalAttachment.swift; path = SignalUtilitiesKit/SignalAttachment.swift; sourceTree = SOURCE_ROOT; }; - C38EF225255B6D5D007E1867 /* AttachmentSharing.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = AttachmentSharing.h; path = SignalUtilitiesKit/AttachmentSharing.h; sourceTree = SOURCE_ROOT; }; - C38EF226255B6D5D007E1867 /* ShareViewDelegate.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = ShareViewDelegate.swift; path = SignalUtilitiesKit/ShareViewDelegate.swift; sourceTree = SOURCE_ROOT; }; - C38EF227255B6D5D007E1867 /* OWSVideoPlayer.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = OWSVideoPlayer.swift; path = SignalUtilitiesKit/OWSVideoPlayer.swift; sourceTree = SOURCE_ROOT; }; - C38EF236255B6D65007E1867 /* UIViewController+OWS.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = "UIViewController+OWS.h"; path = "SignalUtilitiesKit/UIViewController+OWS.h"; sourceTree = SOURCE_ROOT; }; - C38EF237255B6D65007E1867 /* UIDevice+featureSupport.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = "UIDevice+featureSupport.swift"; path = "SignalUtilitiesKit/UIDevice+featureSupport.swift"; sourceTree = SOURCE_ROOT; }; - C38EF238255B6D66007E1867 /* UIFont+OWS.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = "UIFont+OWS.m"; path = "SignalUtilitiesKit/UIFont+OWS.m"; sourceTree = SOURCE_ROOT; }; - C38EF239255B6D66007E1867 /* UIFont+OWS.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = "UIFont+OWS.h"; path = "SignalUtilitiesKit/UIFont+OWS.h"; sourceTree = SOURCE_ROOT; }; - C38EF23A255B6D66007E1867 /* NSAttributedString+OWS.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = "NSAttributedString+OWS.m"; path = "SignalUtilitiesKit/NSAttributedString+OWS.m"; sourceTree = SOURCE_ROOT; }; - C38EF23B255B6D66007E1867 /* UIViewController+OWS.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = "UIViewController+OWS.m"; path = "SignalUtilitiesKit/UIViewController+OWS.m"; sourceTree = SOURCE_ROOT; }; - C38EF23C255B6D66007E1867 /* UIColor+OWS.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = "UIColor+OWS.h"; path = "SignalUtilitiesKit/UIColor+OWS.h"; sourceTree = SOURCE_ROOT; }; - C38EF23D255B6D66007E1867 /* UIView+OWS.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = "UIView+OWS.h"; path = "SignalUtilitiesKit/UIView+OWS.h"; sourceTree = SOURCE_ROOT; }; - C38EF23E255B6D66007E1867 /* UIView+OWS.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = "UIView+OWS.m"; path = "SignalUtilitiesKit/UIView+OWS.m"; sourceTree = SOURCE_ROOT; }; + C38EEF09255B49A8007E1867 /* SNProtoEnvelope+Conversion.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "SNProtoEnvelope+Conversion.swift"; sourceTree = ""; }; + C38EF212255B6D3A007E1867 /* Theme.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = Theme.h; path = SignalUtilitiesKit/UI/Theme.h; sourceTree = SOURCE_ROOT; }; + C38EF214255B6D3A007E1867 /* Theme.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = Theme.m; path = SignalUtilitiesKit/UI/Theme.m; sourceTree = SOURCE_ROOT; }; + C38EF223255B6D5D007E1867 /* AttachmentSharing.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = AttachmentSharing.m; path = SignalUtilitiesKit/Utilities/AttachmentSharing.m; sourceTree = SOURCE_ROOT; }; + C38EF224255B6D5D007E1867 /* SignalAttachment.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = SignalAttachment.swift; path = SignalUtilitiesKit/Attachments/SignalAttachment.swift; sourceTree = SOURCE_ROOT; }; + C38EF225255B6D5D007E1867 /* AttachmentSharing.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = AttachmentSharing.h; path = SignalUtilitiesKit/Utilities/AttachmentSharing.h; sourceTree = SOURCE_ROOT; }; + C38EF226255B6D5D007E1867 /* ShareViewDelegate.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = ShareViewDelegate.swift; path = SignalUtilitiesKit/Utilities/ShareViewDelegate.swift; sourceTree = SOURCE_ROOT; }; + C38EF227255B6D5D007E1867 /* OWSVideoPlayer.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = OWSVideoPlayer.swift; path = SignalUtilitiesKit/Utilities/OWSVideoPlayer.swift; sourceTree = SOURCE_ROOT; }; + C38EF236255B6D65007E1867 /* UIViewController+OWS.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = "UIViewController+OWS.h"; path = "SignalUtilitiesKit/UI/UIViewController+OWS.h"; sourceTree = SOURCE_ROOT; }; + C38EF237255B6D65007E1867 /* UIDevice+featureSupport.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = "UIDevice+featureSupport.swift"; path = "SignalUtilitiesKit/Utilities/UIDevice+featureSupport.swift"; sourceTree = SOURCE_ROOT; }; + C38EF238255B6D66007E1867 /* UIFont+OWS.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = "UIFont+OWS.m"; path = "SignalUtilitiesKit/UI/UIFont+OWS.m"; sourceTree = SOURCE_ROOT; }; + C38EF239255B6D66007E1867 /* UIFont+OWS.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = "UIFont+OWS.h"; path = "SignalUtilitiesKit/UI/UIFont+OWS.h"; sourceTree = SOURCE_ROOT; }; + C38EF23A255B6D66007E1867 /* NSAttributedString+OWS.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = "NSAttributedString+OWS.m"; path = "SignalUtilitiesKit/Utilities/NSAttributedString+OWS.m"; sourceTree = SOURCE_ROOT; }; + C38EF23B255B6D66007E1867 /* UIViewController+OWS.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = "UIViewController+OWS.m"; path = "SignalUtilitiesKit/UI/UIViewController+OWS.m"; sourceTree = SOURCE_ROOT; }; + C38EF23C255B6D66007E1867 /* UIColor+OWS.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = "UIColor+OWS.h"; path = "SignalUtilitiesKit/UI/UIColor+OWS.h"; sourceTree = SOURCE_ROOT; }; + C38EF23D255B6D66007E1867 /* UIView+OWS.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = "UIView+OWS.h"; path = "SignalUtilitiesKit/UI/UIView+OWS.h"; sourceTree = SOURCE_ROOT; }; + C38EF23E255B6D66007E1867 /* UIView+OWS.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = "UIView+OWS.m"; path = "SignalUtilitiesKit/UI/UIView+OWS.m"; sourceTree = SOURCE_ROOT; }; C38EF23F255B6D67007E1867 /* NSAttributedString+OWS.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = "NSAttributedString+OWS.h"; path = "SignalUtilitiesKit/NSAttributedString+OWS.h"; sourceTree = SOURCE_ROOT; }; - C38EF240255B6D67007E1867 /* UIView+OWS.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = "UIView+OWS.swift"; path = "SignalUtilitiesKit/UIView+OWS.swift"; sourceTree = SOURCE_ROOT; }; - C38EF241255B6D67007E1867 /* Collection+OWS.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = "Collection+OWS.swift"; path = "SignalUtilitiesKit/Collection+OWS.swift"; sourceTree = SOURCE_ROOT; }; - C38EF242255B6D67007E1867 /* UIColor+OWS.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = "UIColor+OWS.m"; path = "SignalUtilitiesKit/UIColor+OWS.m"; sourceTree = SOURCE_ROOT; }; - C38EF259255B6D6E007E1867 /* SystemContactsFetcher.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = SystemContactsFetcher.swift; path = SignalUtilitiesKit/SystemContactsFetcher.swift; sourceTree = SOURCE_ROOT; }; - C38EF25A255B6D6E007E1867 /* OWSSyncManager.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = OWSSyncManager.h; path = SignalUtilitiesKit/OWSSyncManager.h; sourceTree = SOURCE_ROOT; }; - C38EF25B255B6D6E007E1867 /* OWSContactsManager.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = OWSContactsManager.m; path = SignalUtilitiesKit/OWSContactsManager.m; sourceTree = SOURCE_ROOT; }; - C38EF25C255B6D6E007E1867 /* OWSSyncManager.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = OWSSyncManager.m; path = SignalUtilitiesKit/OWSSyncManager.m; sourceTree = SOURCE_ROOT; }; - C38EF25D255B6D6E007E1867 /* OWSContactsManager.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = OWSContactsManager.h; path = SignalUtilitiesKit/OWSContactsManager.h; sourceTree = SOURCE_ROOT; }; - C38EF26C255B6D79007E1867 /* OWSResaveCollectionDBMigration.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = OWSResaveCollectionDBMigration.m; path = SignalUtilitiesKit/OWSResaveCollectionDBMigration.m; sourceTree = SOURCE_ROOT; }; - C38EF26D255B6D79007E1867 /* OWSDatabaseMigrationRunner.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = OWSDatabaseMigrationRunner.m; path = SignalUtilitiesKit/OWSDatabaseMigrationRunner.m; sourceTree = SOURCE_ROOT; }; - C38EF26E255B6D79007E1867 /* OWSResaveCollectionDBMigration.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = OWSResaveCollectionDBMigration.h; path = SignalUtilitiesKit/OWSResaveCollectionDBMigration.h; sourceTree = SOURCE_ROOT; }; - C38EF26F255B6D79007E1867 /* OWSDatabaseMigrationRunner.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = OWSDatabaseMigrationRunner.h; path = SignalUtilitiesKit/OWSDatabaseMigrationRunner.h; sourceTree = SOURCE_ROOT; }; - C38EF270255B6D79007E1867 /* OWSDatabaseMigration.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = OWSDatabaseMigration.m; path = SignalUtilitiesKit/OWSDatabaseMigration.m; sourceTree = SOURCE_ROOT; }; - C38EF271255B6D79007E1867 /* OWSDatabaseMigration.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = OWSDatabaseMigration.h; path = SignalUtilitiesKit/OWSDatabaseMigration.h; sourceTree = SOURCE_ROOT; }; - C38EF281255B6D84007E1867 /* OWSAudioSession.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = OWSAudioSession.swift; path = SignalUtilitiesKit/OWSAudioSession.swift; sourceTree = SOURCE_ROOT; }; + C38EF240255B6D67007E1867 /* UIView+OWS.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = "UIView+OWS.swift"; path = "SignalUtilitiesKit/UI/UIView+OWS.swift"; sourceTree = SOURCE_ROOT; }; + C38EF241255B6D67007E1867 /* Collection+OWS.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = "Collection+OWS.swift"; path = "SignalUtilitiesKit/Utilities/Collection+OWS.swift"; sourceTree = SOURCE_ROOT; }; + C38EF242255B6D67007E1867 /* UIColor+OWS.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = "UIColor+OWS.m"; path = "SignalUtilitiesKit/UI/UIColor+OWS.m"; sourceTree = SOURCE_ROOT; }; + C38EF26C255B6D79007E1867 /* OWSResaveCollectionDBMigration.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = OWSResaveCollectionDBMigration.m; path = SignalUtilitiesKit/Database/OWSResaveCollectionDBMigration.m; sourceTree = SOURCE_ROOT; }; + C38EF26D255B6D79007E1867 /* OWSDatabaseMigrationRunner.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = OWSDatabaseMigrationRunner.m; path = SignalUtilitiesKit/Database/OWSDatabaseMigrationRunner.m; sourceTree = SOURCE_ROOT; }; + C38EF26E255B6D79007E1867 /* OWSResaveCollectionDBMigration.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = OWSResaveCollectionDBMigration.h; path = SignalUtilitiesKit/Database/OWSResaveCollectionDBMigration.h; sourceTree = SOURCE_ROOT; }; + C38EF26F255B6D79007E1867 /* OWSDatabaseMigrationRunner.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = OWSDatabaseMigrationRunner.h; path = SignalUtilitiesKit/Database/OWSDatabaseMigrationRunner.h; sourceTree = SOURCE_ROOT; }; + C38EF270255B6D79007E1867 /* OWSDatabaseMigration.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = OWSDatabaseMigration.m; path = SignalUtilitiesKit/Database/OWSDatabaseMigration.m; sourceTree = SOURCE_ROOT; }; + C38EF271255B6D79007E1867 /* OWSDatabaseMigration.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = OWSDatabaseMigration.h; path = SignalUtilitiesKit/Database/OWSDatabaseMigration.h; sourceTree = SOURCE_ROOT; }; + C38EF281255B6D84007E1867 /* OWSAudioSession.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = OWSAudioSession.swift; path = SignalUtilitiesKit/Utilities/OWSAudioSession.swift; sourceTree = SOURCE_ROOT; }; C38EF282255B6D84007E1867 /* SignalKeyingStorage.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = SignalKeyingStorage.m; path = SignalUtilitiesKit/SignalKeyingStorage.m; sourceTree = SOURCE_ROOT; }; C38EF283255B6D84007E1867 /* VersionMigrations.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = VersionMigrations.h; path = SignalUtilitiesKit/VersionMigrations.h; sourceTree = SOURCE_ROOT; }; C38EF284255B6D84007E1867 /* AppSetup.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = AppSetup.h; path = SignalUtilitiesKit/AppSetup.h; sourceTree = SOURCE_ROOT; }; @@ -2195,145 +1801,126 @@ C38EF288255B6D85007E1867 /* OWSSounds.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = OWSSounds.h; path = SignalUtilitiesKit/OWSSounds.h; sourceTree = SOURCE_ROOT; }; C38EF289255B6D85007E1867 /* NoopCallMessageHandler.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = NoopCallMessageHandler.swift; path = SignalUtilitiesKit/NoopCallMessageHandler.swift; sourceTree = SOURCE_ROOT; }; C38EF28B255B6D86007E1867 /* OWSSounds.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = OWSSounds.m; path = SignalUtilitiesKit/OWSSounds.m; sourceTree = SOURCE_ROOT; }; - C38EF2A2255B6D93007E1867 /* Identicon+ObjC.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = "Identicon+ObjC.swift"; path = "SignalUtilitiesKit/Identicon+ObjC.swift"; sourceTree = SOURCE_ROOT; }; - C38EF2A3255B6D93007E1867 /* PlaceholderIcon.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = PlaceholderIcon.swift; path = SignalUtilitiesKit/PlaceholderIcon.swift; sourceTree = SOURCE_ROOT; }; - C38EF2A4255B6D93007E1867 /* ProfilePictureView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = ProfilePictureView.swift; path = SignalUtilitiesKit/ProfilePictureView.swift; sourceTree = SOURCE_ROOT; }; - C38EF2B1255B6D9C007E1867 /* UIViewController+Utilities.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = "UIViewController+Utilities.swift"; path = "SignalUtilitiesKit/UIViewController+Utilities.swift"; sourceTree = SOURCE_ROOT; }; - C38EF2B2255B6D9C007E1867 /* UIView+Utilities.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = "UIView+Utilities.swift"; path = "SignalUtilitiesKit/UIView+Utilities.swift"; sourceTree = SOURCE_ROOT; }; + C38EF2A2255B6D93007E1867 /* Identicon+ObjC.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = "Identicon+ObjC.swift"; path = "SignalUtilitiesKit/UI/Identicon+ObjC.swift"; sourceTree = SOURCE_ROOT; }; + C38EF2A3255B6D93007E1867 /* PlaceholderIcon.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = PlaceholderIcon.swift; path = SignalUtilitiesKit/UI/PlaceholderIcon.swift; sourceTree = SOURCE_ROOT; }; + C38EF2A4255B6D93007E1867 /* ProfilePictureView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = ProfilePictureView.swift; path = SignalUtilitiesKit/UI/ProfilePictureView.swift; sourceTree = SOURCE_ROOT; }; + C38EF2B1255B6D9C007E1867 /* UIViewController+Utilities.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = "UIViewController+Utilities.swift"; path = "SignalUtilitiesKit/UI/UIViewController+Utilities.swift"; sourceTree = SOURCE_ROOT; }; + C38EF2B2255B6D9C007E1867 /* UIView+Utilities.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = "UIView+Utilities.swift"; path = "SignalUtilitiesKit/UI/UIView+Utilities.swift"; sourceTree = SOURCE_ROOT; }; C38EF2BE255B6DA6007E1867 /* TSUnreadIndicatorInteraction.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = TSUnreadIndicatorInteraction.h; path = SignalUtilitiesKit/TSUnreadIndicatorInteraction.h; sourceTree = SOURCE_ROOT; }; C38EF2BF255B6DA6007E1867 /* OWSContactOffersInteraction.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = OWSContactOffersInteraction.h; path = SignalUtilitiesKit/OWSContactOffersInteraction.h; sourceTree = SOURCE_ROOT; }; C38EF2C0255B6DA6007E1867 /* OWSContactOffersInteraction.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = OWSContactOffersInteraction.m; path = SignalUtilitiesKit/OWSContactOffersInteraction.m; sourceTree = SOURCE_ROOT; }; C38EF2C1255B6DA6007E1867 /* TSUnreadIndicatorInteraction.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = TSUnreadIndicatorInteraction.m; path = SignalUtilitiesKit/TSUnreadIndicatorInteraction.m; sourceTree = SOURCE_ROOT; }; - C38EF2CF255B6DAE007E1867 /* OWSProfileManager.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = OWSProfileManager.m; path = SignalUtilitiesKit/OWSProfileManager.m; sourceTree = SOURCE_ROOT; }; - C38EF2D0255B6DAE007E1867 /* ProfileFetcherJob.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = ProfileFetcherJob.swift; path = SignalUtilitiesKit/ProfileFetcherJob.swift; sourceTree = SOURCE_ROOT; }; - C38EF2D1255B6DAF007E1867 /* OWSUserProfile.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = OWSUserProfile.m; path = SignalUtilitiesKit/OWSUserProfile.m; sourceTree = SOURCE_ROOT; }; - C38EF2D2255B6DAF007E1867 /* OWSProfileManager.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = OWSProfileManager.h; path = SignalUtilitiesKit/OWSProfileManager.h; sourceTree = SOURCE_ROOT; }; - C38EF2D3255B6DAF007E1867 /* OWSUserProfile.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = OWSUserProfile.h; path = SignalUtilitiesKit/OWSUserProfile.h; sourceTree = SOURCE_ROOT; }; + C38EF2CF255B6DAE007E1867 /* OWSProfileManager.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = OWSProfileManager.m; path = "SignalUtilitiesKit/Remove Later/OWSProfileManager.m"; sourceTree = SOURCE_ROOT; }; + C38EF2D1255B6DAF007E1867 /* OWSUserProfile.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = OWSUserProfile.m; path = "SignalUtilitiesKit/Remove Later/OWSUserProfile.m"; sourceTree = SOURCE_ROOT; }; + C38EF2D2255B6DAF007E1867 /* OWSProfileManager.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = OWSProfileManager.h; path = "SignalUtilitiesKit/Remove Later/OWSProfileManager.h"; sourceTree = SOURCE_ROOT; }; + C38EF2D3255B6DAF007E1867 /* OWSUserProfile.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = OWSUserProfile.h; path = "SignalUtilitiesKit/Remove Later/OWSUserProfile.h"; sourceTree = SOURCE_ROOT; }; C38EF2E2255B6DB9007E1867 /* OWSScreenLock.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = OWSScreenLock.swift; path = SignalUtilitiesKit/OWSScreenLock.swift; sourceTree = SOURCE_ROOT; }; C38EF2E3255B6DB9007E1867 /* OWSUnreadIndicator.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = OWSUnreadIndicator.m; path = SignalUtilitiesKit/OWSUnreadIndicator.m; sourceTree = SOURCE_ROOT; }; C38EF2E4255B6DB9007E1867 /* FullTextSearcher.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = FullTextSearcher.swift; path = SignalUtilitiesKit/FullTextSearcher.swift; sourceTree = SOURCE_ROOT; }; C38EF2E5255B6DB9007E1867 /* AppPreferences.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = AppPreferences.swift; path = SignalUtilitiesKit/AppPreferences.swift; sourceTree = SOURCE_ROOT; }; - C38EF2E6255B6DBA007E1867 /* DebugLogger.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = DebugLogger.m; path = SignalUtilitiesKit/DebugLogger.m; sourceTree = SOURCE_ROOT; }; - C38EF2E7255B6DBA007E1867 /* OWSScrubbingLogFormatter.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = OWSScrubbingLogFormatter.h; path = SignalUtilitiesKit/OWSScrubbingLogFormatter.h; sourceTree = SOURCE_ROOT; }; - C38EF2E8255B6DBA007E1867 /* OWSGroupAvatarBuilder.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = OWSGroupAvatarBuilder.m; path = SignalUtilitiesKit/OWSGroupAvatarBuilder.m; sourceTree = SOURCE_ROOT; }; + C38EF2E6255B6DBA007E1867 /* DebugLogger.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = DebugLogger.m; path = SignalUtilitiesKit/Utilities/DebugLogger.m; sourceTree = SOURCE_ROOT; }; + C38EF2E7255B6DBA007E1867 /* OWSScrubbingLogFormatter.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = OWSScrubbingLogFormatter.h; path = SignalUtilitiesKit/Utilities/OWSScrubbingLogFormatter.h; sourceTree = SOURCE_ROOT; }; C38EF2E9255B6DBA007E1867 /* OWSUnreadIndicator.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = OWSUnreadIndicator.h; path = SignalUtilitiesKit/OWSUnreadIndicator.h; sourceTree = SOURCE_ROOT; }; - C38EF2EA255B6DBA007E1867 /* OWSAvatarBuilder.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = OWSAvatarBuilder.h; path = SignalUtilitiesKit/OWSAvatarBuilder.h; sourceTree = SOURCE_ROOT; }; - C38EF2EB255B6DBA007E1867 /* OWSGroupAvatarBuilder.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = OWSGroupAvatarBuilder.h; path = SignalUtilitiesKit/OWSGroupAvatarBuilder.h; sourceTree = SOURCE_ROOT; }; C38EF2EC255B6DBA007E1867 /* ProximityMonitoringManager.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = ProximityMonitoringManager.swift; path = SignalUtilitiesKit/ProximityMonitoringManager.swift; sourceTree = SOURCE_ROOT; }; - C38EF2ED255B6DBB007E1867 /* DisplayableText.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = DisplayableText.swift; path = SignalUtilitiesKit/DisplayableText.swift; sourceTree = SOURCE_ROOT; }; - C38EF2EE255B6DBB007E1867 /* OWSAvatarBuilder.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = OWSAvatarBuilder.m; path = SignalUtilitiesKit/OWSAvatarBuilder.m; sourceTree = SOURCE_ROOT; }; - C38EF2EF255B6DBB007E1867 /* Weak.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = Weak.swift; path = SignalUtilitiesKit/Weak.swift; sourceTree = SOURCE_ROOT; }; - C38EF2F0255B6DBB007E1867 /* OWSAnyTouchGestureRecognizer.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = OWSAnyTouchGestureRecognizer.m; path = SignalUtilitiesKit/OWSAnyTouchGestureRecognizer.m; sourceTree = SOURCE_ROOT; }; + C38EF2ED255B6DBB007E1867 /* DisplayableText.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = DisplayableText.swift; path = SignalUtilitiesKit/UI/DisplayableText.swift; sourceTree = SOURCE_ROOT; }; + C38EF2EF255B6DBB007E1867 /* Weak.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = Weak.swift; path = SignalUtilitiesKit/Utilities/Weak.swift; sourceTree = SOURCE_ROOT; }; + C38EF2F0255B6DBB007E1867 /* OWSAnyTouchGestureRecognizer.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = OWSAnyTouchGestureRecognizer.m; path = SignalUtilitiesKit/UI/OWSAnyTouchGestureRecognizer.m; sourceTree = SOURCE_ROOT; }; C38EF2F1255B6DBB007E1867 /* OWSPreferences.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = OWSPreferences.h; path = SignalUtilitiesKit/OWSPreferences.h; sourceTree = SOURCE_ROOT; }; C38EF2F2255B6DBC007E1867 /* Searcher.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = Searcher.swift; path = SignalUtilitiesKit/Searcher.swift; sourceTree = SOURCE_ROOT; }; - C38EF2F3255B6DBC007E1867 /* UIImage+OWS.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = "UIImage+OWS.swift"; path = "SignalUtilitiesKit/UIImage+OWS.swift"; sourceTree = SOURCE_ROOT; }; - C38EF2F5255B6DBC007E1867 /* OWSAudioPlayer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = OWSAudioPlayer.h; path = SignalUtilitiesKit/OWSAudioPlayer.h; sourceTree = SOURCE_ROOT; }; - C38EF2F6255B6DBC007E1867 /* OWSScrubbingLogFormatter.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = OWSScrubbingLogFormatter.m; path = SignalUtilitiesKit/OWSScrubbingLogFormatter.m; sourceTree = SOURCE_ROOT; }; - C38EF2F7255B6DBC007E1867 /* OWSAudioPlayer.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = OWSAudioPlayer.m; path = SignalUtilitiesKit/OWSAudioPlayer.m; sourceTree = SOURCE_ROOT; }; - C38EF2F8255B6DBC007E1867 /* DebugLogger.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = DebugLogger.h; path = SignalUtilitiesKit/DebugLogger.h; sourceTree = SOURCE_ROOT; }; - C38EF2F9255B6DBC007E1867 /* OWSContactAvatarBuilder.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = OWSContactAvatarBuilder.m; path = SignalUtilitiesKit/OWSContactAvatarBuilder.m; sourceTree = SOURCE_ROOT; }; - C38EF2FA255B6DBD007E1867 /* Bench.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = Bench.swift; path = SignalUtilitiesKit/Bench.swift; sourceTree = SOURCE_ROOT; }; - C38EF2FB255B6DBD007E1867 /* OWSWindowManager.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = OWSWindowManager.h; path = SignalUtilitiesKit/OWSWindowManager.h; sourceTree = SOURCE_ROOT; }; - C38EF2FC255B6DBD007E1867 /* ConversationStyle.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = ConversationStyle.swift; path = SignalUtilitiesKit/ConversationStyle.swift; sourceTree = SOURCE_ROOT; }; - C38EF2FD255B6DBD007E1867 /* BlockListUIUtils.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = BlockListUIUtils.m; path = SignalUtilitiesKit/BlockListUIUtils.m; sourceTree = SOURCE_ROOT; }; - C38EF2FE255B6DBD007E1867 /* OWSContactAvatarBuilder.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = OWSContactAvatarBuilder.h; path = SignalUtilitiesKit/OWSContactAvatarBuilder.h; sourceTree = SOURCE_ROOT; }; - C38EF300255B6DBD007E1867 /* UIUtil.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = UIUtil.m; path = SignalUtilitiesKit/UIUtil.m; sourceTree = SOURCE_ROOT; }; - C38EF301255B6DBD007E1867 /* OWSFormat.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = OWSFormat.h; path = SignalUtilitiesKit/OWSFormat.h; sourceTree = SOURCE_ROOT; }; - C38EF302255B6DBE007E1867 /* OWSAnyTouchGestureRecognizer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = OWSAnyTouchGestureRecognizer.h; path = SignalUtilitiesKit/OWSAnyTouchGestureRecognizer.h; sourceTree = SOURCE_ROOT; }; - C38EF303255B6DBE007E1867 /* BlockListUIUtils.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = BlockListUIUtils.h; path = SignalUtilitiesKit/BlockListUIUtils.h; sourceTree = SOURCE_ROOT; }; + C38EF2F3255B6DBC007E1867 /* UIImage+OWS.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = "UIImage+OWS.swift"; path = "SignalUtilitiesKit/UI/UIImage+OWS.swift"; sourceTree = SOURCE_ROOT; }; + C38EF2F5255B6DBC007E1867 /* OWSAudioPlayer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = OWSAudioPlayer.h; path = SignalUtilitiesKit/Utilities/OWSAudioPlayer.h; sourceTree = SOURCE_ROOT; }; + C38EF2F6255B6DBC007E1867 /* OWSScrubbingLogFormatter.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = OWSScrubbingLogFormatter.m; path = SignalUtilitiesKit/Utilities/OWSScrubbingLogFormatter.m; sourceTree = SOURCE_ROOT; }; + C38EF2F7255B6DBC007E1867 /* OWSAudioPlayer.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = OWSAudioPlayer.m; path = SignalUtilitiesKit/Utilities/OWSAudioPlayer.m; sourceTree = SOURCE_ROOT; }; + C38EF2F8255B6DBC007E1867 /* DebugLogger.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = DebugLogger.h; path = SignalUtilitiesKit/Utilities/DebugLogger.h; sourceTree = SOURCE_ROOT; }; + C38EF2FA255B6DBD007E1867 /* Bench.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = Bench.swift; path = SignalUtilitiesKit/Utilities/Bench.swift; sourceTree = SOURCE_ROOT; }; + C38EF2FB255B6DBD007E1867 /* OWSWindowManager.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = OWSWindowManager.h; path = SignalUtilitiesKit/UI/OWSWindowManager.h; sourceTree = SOURCE_ROOT; }; + C38EF2FC255B6DBD007E1867 /* ConversationStyle.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = ConversationStyle.swift; path = SignalUtilitiesKit/UI/ConversationStyle.swift; sourceTree = SOURCE_ROOT; }; + C38EF2FD255B6DBD007E1867 /* BlockListUIUtils.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = BlockListUIUtils.m; path = SignalUtilitiesKit/UI/BlockListUIUtils.m; sourceTree = SOURCE_ROOT; }; + C38EF300255B6DBD007E1867 /* UIUtil.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = UIUtil.m; path = SignalUtilitiesKit/UI/UIUtil.m; sourceTree = SOURCE_ROOT; }; + C38EF301255B6DBD007E1867 /* OWSFormat.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = OWSFormat.h; path = SignalUtilitiesKit/Utilities/OWSFormat.h; sourceTree = SOURCE_ROOT; }; + C38EF302255B6DBE007E1867 /* OWSAnyTouchGestureRecognizer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = OWSAnyTouchGestureRecognizer.h; path = SignalUtilitiesKit/UI/OWSAnyTouchGestureRecognizer.h; sourceTree = SOURCE_ROOT; }; + C38EF303255B6DBE007E1867 /* BlockListUIUtils.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = BlockListUIUtils.h; path = SignalUtilitiesKit/UI/BlockListUIUtils.h; sourceTree = SOURCE_ROOT; }; C38EF304255B6DBE007E1867 /* ImageCache.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = ImageCache.swift; path = SignalUtilitiesKit/ImageCache.swift; sourceTree = SOURCE_ROOT; }; - C38EF305255B6DBE007E1867 /* OWSFormat.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = OWSFormat.m; path = SignalUtilitiesKit/OWSFormat.m; sourceTree = SOURCE_ROOT; }; - C38EF306255B6DBE007E1867 /* OWSWindowManager.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = OWSWindowManager.m; path = SignalUtilitiesKit/OWSWindowManager.m; sourceTree = SOURCE_ROOT; }; - C38EF307255B6DBE007E1867 /* UIGestureRecognizer+OWS.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = "UIGestureRecognizer+OWS.swift"; path = "SignalUtilitiesKit/UIGestureRecognizer+OWS.swift"; sourceTree = SOURCE_ROOT; }; + C38EF305255B6DBE007E1867 /* OWSFormat.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = OWSFormat.m; path = SignalUtilitiesKit/Utilities/OWSFormat.m; sourceTree = SOURCE_ROOT; }; + C38EF306255B6DBE007E1867 /* OWSWindowManager.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = OWSWindowManager.m; path = SignalUtilitiesKit/UI/OWSWindowManager.m; sourceTree = SOURCE_ROOT; }; + C38EF307255B6DBE007E1867 /* UIGestureRecognizer+OWS.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = "UIGestureRecognizer+OWS.swift"; path = "SignalUtilitiesKit/UI/UIGestureRecognizer+OWS.swift"; sourceTree = SOURCE_ROOT; }; C38EF308255B6DBE007E1867 /* OWSPreferences.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = OWSPreferences.m; path = SignalUtilitiesKit/OWSPreferences.m; sourceTree = SOURCE_ROOT; }; C38EF309255B6DBE007E1867 /* DeviceSleepManager.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = DeviceSleepManager.swift; path = SignalUtilitiesKit/DeviceSleepManager.swift; sourceTree = SOURCE_ROOT; }; - C38EF30A255B6DBE007E1867 /* UIUtil.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = UIUtil.h; path = SignalUtilitiesKit/UIUtil.h; sourceTree = SOURCE_ROOT; }; + C38EF30A255B6DBE007E1867 /* UIUtil.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = UIUtil.h; path = SignalUtilitiesKit/UI/UIUtil.h; sourceTree = SOURCE_ROOT; }; C38EF30B255B6DBE007E1867 /* BlockListCache.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = BlockListCache.swift; path = SignalUtilitiesKit/BlockListCache.swift; sourceTree = SOURCE_ROOT; }; - C38EF33F255B6DC5007E1867 /* SheetViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = SheetViewController.swift; path = SignalUtilitiesKit/SheetViewController.swift; sourceTree = SOURCE_ROOT; }; - C38EF340255B6DC5007E1867 /* ViewControllerUtils.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = ViewControllerUtils.m; path = SignalUtilitiesKit/ViewControllerUtils.m; sourceTree = SOURCE_ROOT; }; - C38EF341255B6DC5007E1867 /* SelectThreadViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = SelectThreadViewController.h; path = SignalUtilitiesKit/SelectThreadViewController.h; sourceTree = SOURCE_ROOT; }; - C38EF342255B6DC5007E1867 /* SelectThreadViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = SelectThreadViewController.m; path = SignalUtilitiesKit/SelectThreadViewController.m; sourceTree = SOURCE_ROOT; }; - C38EF343255B6DC5007E1867 /* OWSNavigationController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = OWSNavigationController.h; path = SignalUtilitiesKit/OWSNavigationController.h; sourceTree = SOURCE_ROOT; }; - C38EF344255B6DC5007E1867 /* OWSViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = OWSViewController.h; path = SignalUtilitiesKit/OWSViewController.h; sourceTree = SOURCE_ROOT; }; - C38EF345255B6DC6007E1867 /* SelectRecipientViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = SelectRecipientViewController.m; path = SignalUtilitiesKit/SelectRecipientViewController.m; sourceTree = SOURCE_ROOT; }; - C38EF346255B6DC6007E1867 /* ReturnToCallViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = ReturnToCallViewController.swift; path = SignalUtilitiesKit/ReturnToCallViewController.swift; sourceTree = SOURCE_ROOT; }; - C38EF347255B6DC6007E1867 /* EditContactShareNameViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = EditContactShareNameViewController.swift; path = SignalUtilitiesKit/EditContactShareNameViewController.swift; sourceTree = SOURCE_ROOT; }; - C38EF348255B6DC7007E1867 /* ContactShareApprovalViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = ContactShareApprovalViewController.swift; path = SignalUtilitiesKit/ContactShareApprovalViewController.swift; sourceTree = SOURCE_ROOT; }; - C38EF349255B6DC7007E1867 /* ModalActivityIndicatorViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = ModalActivityIndicatorViewController.swift; path = SignalUtilitiesKit/ModalActivityIndicatorViewController.swift; sourceTree = SOURCE_ROOT; }; - C38EF34A255B6DC7007E1867 /* NewNonContactConversationViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = NewNonContactConversationViewController.h; path = SignalUtilitiesKit/NewNonContactConversationViewController.h; sourceTree = SOURCE_ROOT; }; - C38EF34B255B6DC8007E1867 /* OWSTableViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = OWSTableViewController.m; path = SignalUtilitiesKit/OWSTableViewController.m; sourceTree = SOURCE_ROOT; }; - C38EF34C255B6DC8007E1867 /* ScreenLockViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = ScreenLockViewController.h; path = SignalUtilitiesKit/ScreenLockViewController.h; sourceTree = SOURCE_ROOT; }; - C38EF34D255B6DC8007E1867 /* OWSTableViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = OWSTableViewController.h; path = SignalUtilitiesKit/OWSTableViewController.h; sourceTree = SOURCE_ROOT; }; - C38EF34E255B6DC8007E1867 /* SelectRecipientViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = SelectRecipientViewController.h; path = SignalUtilitiesKit/SelectRecipientViewController.h; sourceTree = SOURCE_ROOT; }; - C38EF34F255B6DC9007E1867 /* ViewControllerUtils.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = ViewControllerUtils.h; path = SignalUtilitiesKit/ViewControllerUtils.h; sourceTree = SOURCE_ROOT; }; - C38EF350255B6DC9007E1867 /* NewNonContactConversationViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = NewNonContactConversationViewController.m; path = SignalUtilitiesKit/NewNonContactConversationViewController.m; sourceTree = SOURCE_ROOT; }; - C38EF351255B6DC9007E1867 /* ScreenLockViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = ScreenLockViewController.m; path = SignalUtilitiesKit/ScreenLockViewController.m; sourceTree = SOURCE_ROOT; }; - C38EF352255B6DC9007E1867 /* SharingThreadPickerViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = SharingThreadPickerViewController.m; path = SignalUtilitiesKit/SharingThreadPickerViewController.m; sourceTree = SOURCE_ROOT; }; - C38EF353255B6DCB007E1867 /* SharingThreadPickerViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = SharingThreadPickerViewController.h; path = SignalUtilitiesKit/SharingThreadPickerViewController.h; sourceTree = SOURCE_ROOT; }; - C38EF354255B6DCB007E1867 /* ContactFieldView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = ContactFieldView.swift; path = SignalUtilitiesKit/ContactFieldView.swift; sourceTree = SOURCE_ROOT; }; - C38EF355255B6DCB007E1867 /* OWSViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = OWSViewController.m; path = SignalUtilitiesKit/OWSViewController.m; sourceTree = SOURCE_ROOT; }; - C38EF356255B6DCB007E1867 /* OWSNavigationController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = OWSNavigationController.m; path = SignalUtilitiesKit/OWSNavigationController.m; sourceTree = SOURCE_ROOT; }; - C38EF357255B6DCC007E1867 /* MessageApprovalViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = MessageApprovalViewController.swift; path = SignalUtilitiesKit/MessageApprovalViewController.swift; sourceTree = SOURCE_ROOT; }; - C38EF358255B6DCC007E1867 /* MediaMessageView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = MediaMessageView.swift; path = SignalUtilitiesKit/MediaMessageView.swift; sourceTree = SOURCE_ROOT; }; - C38EF37C255B6DCF007E1867 /* AttachmentTextToolbar.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = AttachmentTextToolbar.swift; path = SignalUtilitiesKit/AttachmentTextToolbar.swift; sourceTree = SOURCE_ROOT; }; - C38EF37D255B6DCF007E1867 /* AttachmentApprovalInputAccessoryView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = AttachmentApprovalInputAccessoryView.swift; path = SignalUtilitiesKit/AttachmentApprovalInputAccessoryView.swift; sourceTree = SOURCE_ROOT; }; - C38EF37E255B6DD0007E1867 /* AttachmentItemCollection.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = AttachmentItemCollection.swift; path = SignalUtilitiesKit/AttachmentItemCollection.swift; sourceTree = SOURCE_ROOT; }; - C38EF37F255B6DD0007E1867 /* AttachmentApprovalViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = AttachmentApprovalViewController.swift; path = SignalUtilitiesKit/AttachmentApprovalViewController.swift; sourceTree = SOURCE_ROOT; }; - C38EF380255B6DD0007E1867 /* AttachmentTextView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = AttachmentTextView.swift; path = SignalUtilitiesKit/AttachmentTextView.swift; sourceTree = SOURCE_ROOT; }; - C38EF381255B6DD1007E1867 /* AttachmentCaptionToolbar.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = AttachmentCaptionToolbar.swift; path = SignalUtilitiesKit/AttachmentCaptionToolbar.swift; sourceTree = SOURCE_ROOT; }; - C38EF382255B6DD1007E1867 /* AttachmentPrepViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = AttachmentPrepViewController.swift; path = SignalUtilitiesKit/AttachmentPrepViewController.swift; sourceTree = SOURCE_ROOT; }; - C38EF383255B6DD1007E1867 /* ApprovalRailCellView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = ApprovalRailCellView.swift; path = SignalUtilitiesKit/ApprovalRailCellView.swift; sourceTree = SOURCE_ROOT; }; - C38EF384255B6DD2007E1867 /* AttachmentCaptionViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = AttachmentCaptionViewController.swift; path = SignalUtilitiesKit/AttachmentCaptionViewController.swift; sourceTree = SOURCE_ROOT; }; + C38EF33F255B6DC5007E1867 /* SheetViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = SheetViewController.swift; path = SignalUtilitiesKit/UI/SheetViewController.swift; sourceTree = SOURCE_ROOT; }; + C38EF341255B6DC5007E1867 /* SelectThreadViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = SelectThreadViewController.h; path = SignalUtilitiesKit/UI/SelectThreadViewController.h; sourceTree = SOURCE_ROOT; }; + C38EF342255B6DC5007E1867 /* SelectThreadViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = SelectThreadViewController.m; path = SignalUtilitiesKit/UI/SelectThreadViewController.m; sourceTree = SOURCE_ROOT; }; + C38EF343255B6DC5007E1867 /* OWSNavigationController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = OWSNavigationController.h; path = SignalUtilitiesKit/UI/OWSNavigationController.h; sourceTree = SOURCE_ROOT; }; + C38EF344255B6DC5007E1867 /* OWSViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = OWSViewController.h; path = SignalUtilitiesKit/UI/OWSViewController.h; sourceTree = SOURCE_ROOT; }; + C38EF345255B6DC6007E1867 /* SelectRecipientViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = SelectRecipientViewController.m; path = SignalUtilitiesKit/UI/SelectRecipientViewController.m; sourceTree = SOURCE_ROOT; }; + C38EF349255B6DC7007E1867 /* ModalActivityIndicatorViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = ModalActivityIndicatorViewController.swift; path = SignalUtilitiesKit/UI/ModalActivityIndicatorViewController.swift; sourceTree = SOURCE_ROOT; }; + C38EF34B255B6DC8007E1867 /* OWSTableViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = OWSTableViewController.m; path = SignalUtilitiesKit/UI/OWSTableViewController.m; sourceTree = SOURCE_ROOT; }; + C38EF34C255B6DC8007E1867 /* ScreenLockViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = ScreenLockViewController.h; path = SignalUtilitiesKit/UI/ScreenLockViewController.h; sourceTree = SOURCE_ROOT; }; + C38EF34D255B6DC8007E1867 /* OWSTableViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = OWSTableViewController.h; path = SignalUtilitiesKit/UI/OWSTableViewController.h; sourceTree = SOURCE_ROOT; }; + C38EF34E255B6DC8007E1867 /* SelectRecipientViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = SelectRecipientViewController.h; path = SignalUtilitiesKit/UI/SelectRecipientViewController.h; sourceTree = SOURCE_ROOT; }; + C38EF351255B6DC9007E1867 /* ScreenLockViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = ScreenLockViewController.m; path = SignalUtilitiesKit/UI/ScreenLockViewController.m; sourceTree = SOURCE_ROOT; }; + C38EF352255B6DC9007E1867 /* SharingThreadPickerViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = SharingThreadPickerViewController.m; path = SignalUtilitiesKit/UI/SharingThreadPickerViewController.m; sourceTree = SOURCE_ROOT; }; + C38EF353255B6DCB007E1867 /* SharingThreadPickerViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = SharingThreadPickerViewController.h; path = SignalUtilitiesKit/UI/SharingThreadPickerViewController.h; sourceTree = SOURCE_ROOT; }; + C38EF355255B6DCB007E1867 /* OWSViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = OWSViewController.m; path = SignalUtilitiesKit/UI/OWSViewController.m; sourceTree = SOURCE_ROOT; }; + C38EF356255B6DCB007E1867 /* OWSNavigationController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = OWSNavigationController.m; path = SignalUtilitiesKit/UI/OWSNavigationController.m; sourceTree = SOURCE_ROOT; }; + C38EF357255B6DCC007E1867 /* MessageApprovalViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = MessageApprovalViewController.swift; path = SignalUtilitiesKit/UI/MessageApprovalViewController.swift; sourceTree = SOURCE_ROOT; }; + C38EF358255B6DCC007E1867 /* MediaMessageView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = MediaMessageView.swift; path = SignalUtilitiesKit/UI/MediaMessageView.swift; sourceTree = SOURCE_ROOT; }; + C38EF37C255B6DCF007E1867 /* AttachmentTextToolbar.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = AttachmentTextToolbar.swift; path = SignalUtilitiesKit/UI/AttachmentTextToolbar.swift; sourceTree = SOURCE_ROOT; }; + C38EF37D255B6DCF007E1867 /* AttachmentApprovalInputAccessoryView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = AttachmentApprovalInputAccessoryView.swift; path = SignalUtilitiesKit/UI/AttachmentApprovalInputAccessoryView.swift; sourceTree = SOURCE_ROOT; }; + C38EF37E255B6DD0007E1867 /* AttachmentItemCollection.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = AttachmentItemCollection.swift; path = SignalUtilitiesKit/UI/AttachmentItemCollection.swift; sourceTree = SOURCE_ROOT; }; + C38EF37F255B6DD0007E1867 /* AttachmentApprovalViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = AttachmentApprovalViewController.swift; path = SignalUtilitiesKit/UI/AttachmentApprovalViewController.swift; sourceTree = SOURCE_ROOT; }; + C38EF380255B6DD0007E1867 /* AttachmentTextView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = AttachmentTextView.swift; path = SignalUtilitiesKit/UI/AttachmentTextView.swift; sourceTree = SOURCE_ROOT; }; + C38EF381255B6DD1007E1867 /* AttachmentCaptionToolbar.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = AttachmentCaptionToolbar.swift; path = SignalUtilitiesKit/UI/AttachmentCaptionToolbar.swift; sourceTree = SOURCE_ROOT; }; + C38EF382255B6DD1007E1867 /* AttachmentPrepViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = AttachmentPrepViewController.swift; path = SignalUtilitiesKit/UI/AttachmentPrepViewController.swift; sourceTree = SOURCE_ROOT; }; + C38EF383255B6DD1007E1867 /* ApprovalRailCellView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = ApprovalRailCellView.swift; path = SignalUtilitiesKit/UI/ApprovalRailCellView.swift; sourceTree = SOURCE_ROOT; }; + C38EF384255B6DD2007E1867 /* AttachmentCaptionViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = AttachmentCaptionViewController.swift; path = SignalUtilitiesKit/UI/AttachmentCaptionViewController.swift; sourceTree = SOURCE_ROOT; }; C38EF397255B6DD9007E1867 /* ThreadViewModel.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = ThreadViewModel.swift; path = SignalUtilitiesKit/ThreadViewModel.swift; sourceTree = SOURCE_ROOT; }; C38EF398255B6DD9007E1867 /* OWSQuotedReplyModel.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = OWSQuotedReplyModel.h; path = SignalUtilitiesKit/OWSQuotedReplyModel.h; sourceTree = SOURCE_ROOT; }; - C38EF399255B6DD9007E1867 /* ContactShareViewModel.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = ContactShareViewModel.swift; path = SignalUtilitiesKit/ContactShareViewModel.swift; sourceTree = SOURCE_ROOT; }; C38EF39A255B6DD9007E1867 /* OWSQuotedReplyModel.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = OWSQuotedReplyModel.m; path = SignalUtilitiesKit/OWSQuotedReplyModel.m; sourceTree = SOURCE_ROOT; }; - C38EF3A8255B6DE4007E1867 /* ImageEditorTextViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = ImageEditorTextViewController.swift; path = SignalUtilitiesKit/ImageEditorTextViewController.swift; sourceTree = SOURCE_ROOT; }; - C38EF3A9255B6DE4007E1867 /* ImageEditorPinchGestureRecognizer.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = ImageEditorPinchGestureRecognizer.swift; path = SignalUtilitiesKit/ImageEditorPinchGestureRecognizer.swift; sourceTree = SOURCE_ROOT; }; - C38EF3AA255B6DE4007E1867 /* ImageEditorItem.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = ImageEditorItem.swift; path = SignalUtilitiesKit/ImageEditorItem.swift; sourceTree = SOURCE_ROOT; }; - C38EF3AB255B6DE4007E1867 /* ImageEditorStrokeItem.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = ImageEditorStrokeItem.swift; path = SignalUtilitiesKit/ImageEditorStrokeItem.swift; sourceTree = SOURCE_ROOT; }; - C38EF3AC255B6DE4007E1867 /* ImageEditorPanGestureRecognizer.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = ImageEditorPanGestureRecognizer.swift; path = SignalUtilitiesKit/ImageEditorPanGestureRecognizer.swift; sourceTree = SOURCE_ROOT; }; - C38EF3AD255B6DE4007E1867 /* ImageEditorTransform.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = ImageEditorTransform.swift; path = SignalUtilitiesKit/ImageEditorTransform.swift; sourceTree = SOURCE_ROOT; }; - C38EF3AE255B6DE5007E1867 /* OrderedDictionary.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = OrderedDictionary.swift; path = SignalUtilitiesKit/OrderedDictionary.swift; sourceTree = SOURCE_ROOT; }; - C38EF3AF255B6DE5007E1867 /* ImageEditorView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = ImageEditorView.swift; path = SignalUtilitiesKit/ImageEditorView.swift; sourceTree = SOURCE_ROOT; }; - C38EF3B0255B6DE5007E1867 /* ImageEditorCropViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = ImageEditorCropViewController.swift; path = SignalUtilitiesKit/ImageEditorCropViewController.swift; sourceTree = SOURCE_ROOT; }; - C38EF3B1255B6DE5007E1867 /* ImageEditorBrushViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = ImageEditorBrushViewController.swift; path = SignalUtilitiesKit/ImageEditorBrushViewController.swift; sourceTree = SOURCE_ROOT; }; - C38EF3B2255B6DE5007E1867 /* ImageEditorPaletteView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = ImageEditorPaletteView.swift; path = SignalUtilitiesKit/ImageEditorPaletteView.swift; sourceTree = SOURCE_ROOT; }; - C38EF3B3255B6DE6007E1867 /* ImageEditorTextItem.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = ImageEditorTextItem.swift; path = SignalUtilitiesKit/ImageEditorTextItem.swift; sourceTree = SOURCE_ROOT; }; - C38EF3B4255B6DE6007E1867 /* ImageEditorContents.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = ImageEditorContents.swift; path = SignalUtilitiesKit/ImageEditorContents.swift; sourceTree = SOURCE_ROOT; }; - C38EF3B5255B6DE6007E1867 /* OWSViewController+ImageEditor.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = "OWSViewController+ImageEditor.swift"; path = "SignalUtilitiesKit/OWSViewController+ImageEditor.swift"; sourceTree = SOURCE_ROOT; }; - C38EF3B6255B6DE6007E1867 /* ImageEditorModel.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = ImageEditorModel.swift; path = SignalUtilitiesKit/ImageEditorModel.swift; sourceTree = SOURCE_ROOT; }; - C38EF3B7255B6DE6007E1867 /* ImageEditorCanvasView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = ImageEditorCanvasView.swift; path = SignalUtilitiesKit/ImageEditorCanvasView.swift; sourceTree = SOURCE_ROOT; }; + C38EF3A8255B6DE4007E1867 /* ImageEditorTextViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = ImageEditorTextViewController.swift; path = SignalUtilitiesKit/UI/ImageEditorTextViewController.swift; sourceTree = SOURCE_ROOT; }; + C38EF3A9255B6DE4007E1867 /* ImageEditorPinchGestureRecognizer.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = ImageEditorPinchGestureRecognizer.swift; path = SignalUtilitiesKit/UI/ImageEditorPinchGestureRecognizer.swift; sourceTree = SOURCE_ROOT; }; + C38EF3AA255B6DE4007E1867 /* ImageEditorItem.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = ImageEditorItem.swift; path = SignalUtilitiesKit/UI/ImageEditorItem.swift; sourceTree = SOURCE_ROOT; }; + C38EF3AB255B6DE4007E1867 /* ImageEditorStrokeItem.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = ImageEditorStrokeItem.swift; path = SignalUtilitiesKit/UI/ImageEditorStrokeItem.swift; sourceTree = SOURCE_ROOT; }; + C38EF3AC255B6DE4007E1867 /* ImageEditorPanGestureRecognizer.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = ImageEditorPanGestureRecognizer.swift; path = SignalUtilitiesKit/UI/ImageEditorPanGestureRecognizer.swift; sourceTree = SOURCE_ROOT; }; + C38EF3AD255B6DE4007E1867 /* ImageEditorTransform.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = ImageEditorTransform.swift; path = SignalUtilitiesKit/UI/ImageEditorTransform.swift; sourceTree = SOURCE_ROOT; }; + C38EF3AE255B6DE5007E1867 /* OrderedDictionary.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = OrderedDictionary.swift; path = SignalUtilitiesKit/Utilities/OrderedDictionary.swift; sourceTree = SOURCE_ROOT; }; + C38EF3AF255B6DE5007E1867 /* ImageEditorView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = ImageEditorView.swift; path = SignalUtilitiesKit/UI/ImageEditorView.swift; sourceTree = SOURCE_ROOT; }; + C38EF3B0255B6DE5007E1867 /* ImageEditorCropViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = ImageEditorCropViewController.swift; path = SignalUtilitiesKit/UI/ImageEditorCropViewController.swift; sourceTree = SOURCE_ROOT; }; + C38EF3B1255B6DE5007E1867 /* ImageEditorBrushViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = ImageEditorBrushViewController.swift; path = SignalUtilitiesKit/UI/ImageEditorBrushViewController.swift; sourceTree = SOURCE_ROOT; }; + C38EF3B2255B6DE5007E1867 /* ImageEditorPaletteView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = ImageEditorPaletteView.swift; path = SignalUtilitiesKit/UI/ImageEditorPaletteView.swift; sourceTree = SOURCE_ROOT; }; + C38EF3B3255B6DE6007E1867 /* ImageEditorTextItem.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = ImageEditorTextItem.swift; path = SignalUtilitiesKit/UI/ImageEditorTextItem.swift; sourceTree = SOURCE_ROOT; }; + C38EF3B4255B6DE6007E1867 /* ImageEditorContents.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = ImageEditorContents.swift; path = SignalUtilitiesKit/UI/ImageEditorContents.swift; sourceTree = SOURCE_ROOT; }; + C38EF3B5255B6DE6007E1867 /* OWSViewController+ImageEditor.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = "OWSViewController+ImageEditor.swift"; path = "SignalUtilitiesKit/UI/OWSViewController+ImageEditor.swift"; sourceTree = SOURCE_ROOT; }; + C38EF3B6255B6DE6007E1867 /* ImageEditorModel.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = ImageEditorModel.swift; path = SignalUtilitiesKit/UI/ImageEditorModel.swift; sourceTree = SOURCE_ROOT; }; + C38EF3B7255B6DE6007E1867 /* ImageEditorCanvasView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = ImageEditorCanvasView.swift; path = SignalUtilitiesKit/UI/ImageEditorCanvasView.swift; sourceTree = SOURCE_ROOT; }; C38EF3D1255B6DEE007E1867 /* ThreadViewHelper.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = ThreadViewHelper.m; path = SignalUtilitiesKit/ThreadViewHelper.m; sourceTree = SOURCE_ROOT; }; C38EF3D2255B6DEE007E1867 /* ThreadViewHelper.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = ThreadViewHelper.h; path = SignalUtilitiesKit/ThreadViewHelper.h; sourceTree = SOURCE_ROOT; }; - C38EF3D3255B6DEE007E1867 /* OWSSearchBar.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = OWSSearchBar.h; path = SignalUtilitiesKit/OWSSearchBar.h; sourceTree = SOURCE_ROOT; }; - C38EF3D4255B6DEE007E1867 /* DisappearingTimerConfigurationView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = DisappearingTimerConfigurationView.swift; path = SignalUtilitiesKit/DisappearingTimerConfigurationView.swift; sourceTree = SOURCE_ROOT; }; - C38EF3D5255B6DEF007E1867 /* ContactsViewHelper.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = ContactsViewHelper.m; path = SignalUtilitiesKit/ContactsViewHelper.m; sourceTree = SOURCE_ROOT; }; - C38EF3D6255B6DEF007E1867 /* ContactCellView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = ContactCellView.m; path = SignalUtilitiesKit/ContactCellView.m; sourceTree = SOURCE_ROOT; }; - C38EF3D7255B6DF0007E1867 /* OWSTextField.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = OWSTextField.h; path = SignalUtilitiesKit/OWSTextField.h; sourceTree = SOURCE_ROOT; }; - C38EF3D8255B6DF0007E1867 /* OWSTextView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = OWSTextView.h; path = SignalUtilitiesKit/OWSTextView.h; sourceTree = SOURCE_ROOT; }; - C38EF3D9255B6DF1007E1867 /* OWSNavigationBar.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = OWSNavigationBar.swift; path = SignalUtilitiesKit/OWSNavigationBar.swift; sourceTree = SOURCE_ROOT; }; - C38EF3DA255B6DF1007E1867 /* ContactsViewHelper.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = ContactsViewHelper.h; path = SignalUtilitiesKit/ContactsViewHelper.h; sourceTree = SOURCE_ROOT; }; - C38EF3DB255B6DF1007E1867 /* OWSLayerView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = OWSLayerView.swift; path = SignalUtilitiesKit/OWSLayerView.swift; sourceTree = SOURCE_ROOT; }; - C38EF3DC255B6DF1007E1867 /* DirectionalPanGestureRecognizer.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = DirectionalPanGestureRecognizer.swift; path = SignalUtilitiesKit/DirectionalPanGestureRecognizer.swift; sourceTree = SOURCE_ROOT; }; - C38EF3DD255B6DF1007E1867 /* UIAlertController+OWS.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = "UIAlertController+OWS.swift"; path = "SignalUtilitiesKit/UIAlertController+OWS.swift"; sourceTree = SOURCE_ROOT; }; - C38EF3DE255B6DF2007E1867 /* AvatarImageView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = AvatarImageView.swift; path = SignalUtilitiesKit/AvatarImageView.swift; sourceTree = SOURCE_ROOT; }; - C38EF3DF255B6DF2007E1867 /* OWSTextView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = OWSTextView.m; path = SignalUtilitiesKit/OWSTextView.m; sourceTree = SOURCE_ROOT; }; - C38EF3E0255B6DF3007E1867 /* OWSTextField.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = OWSTextField.m; path = SignalUtilitiesKit/OWSTextField.m; sourceTree = SOURCE_ROOT; }; - C38EF3E1255B6DF3007E1867 /* TappableView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = TappableView.swift; path = SignalUtilitiesKit/TappableView.swift; sourceTree = SOURCE_ROOT; }; - C38EF3E2255B6DF3007E1867 /* GalleryRailView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = GalleryRailView.swift; path = SignalUtilitiesKit/GalleryRailView.swift; sourceTree = SOURCE_ROOT; }; - C38EF3E3255B6DF4007E1867 /* VideoPlayerView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = VideoPlayerView.swift; path = SignalUtilitiesKit/VideoPlayerView.swift; sourceTree = SOURCE_ROOT; }; + C38EF3D3255B6DEE007E1867 /* OWSSearchBar.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = OWSSearchBar.h; path = SignalUtilitiesKit/UI/OWSSearchBar.h; sourceTree = SOURCE_ROOT; }; + C38EF3D4255B6DEE007E1867 /* DisappearingTimerConfigurationView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = DisappearingTimerConfigurationView.swift; path = SignalUtilitiesKit/UI/DisappearingTimerConfigurationView.swift; sourceTree = SOURCE_ROOT; }; + C38EF3D6255B6DEF007E1867 /* ContactCellView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = ContactCellView.m; path = "SignalUtilitiesKit/Remove Later/ContactCellView.m"; sourceTree = SOURCE_ROOT; }; + C38EF3D7255B6DF0007E1867 /* OWSTextField.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = OWSTextField.h; path = SignalUtilitiesKit/UI/OWSTextField.h; sourceTree = SOURCE_ROOT; }; + C38EF3D8255B6DF0007E1867 /* OWSTextView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = OWSTextView.h; path = SignalUtilitiesKit/UI/OWSTextView.h; sourceTree = SOURCE_ROOT; }; + C38EF3D9255B6DF1007E1867 /* OWSNavigationBar.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = OWSNavigationBar.swift; path = SignalUtilitiesKit/UI/OWSNavigationBar.swift; sourceTree = SOURCE_ROOT; }; + C38EF3DB255B6DF1007E1867 /* OWSLayerView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = OWSLayerView.swift; path = SignalUtilitiesKit/UI/OWSLayerView.swift; sourceTree = SOURCE_ROOT; }; + C38EF3DC255B6DF1007E1867 /* DirectionalPanGestureRecognizer.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = DirectionalPanGestureRecognizer.swift; path = SignalUtilitiesKit/UI/DirectionalPanGestureRecognizer.swift; sourceTree = SOURCE_ROOT; }; + C38EF3DD255B6DF1007E1867 /* UIAlertController+OWS.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = "UIAlertController+OWS.swift"; path = "SignalUtilitiesKit/Utilities/UIAlertController+OWS.swift"; sourceTree = SOURCE_ROOT; }; + C38EF3DF255B6DF2007E1867 /* OWSTextView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = OWSTextView.m; path = SignalUtilitiesKit/UI/OWSTextView.m; sourceTree = SOURCE_ROOT; }; + C38EF3E0255B6DF3007E1867 /* OWSTextField.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = OWSTextField.m; path = SignalUtilitiesKit/UI/OWSTextField.m; sourceTree = SOURCE_ROOT; }; + C38EF3E1255B6DF3007E1867 /* TappableView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = TappableView.swift; path = SignalUtilitiesKit/UI/TappableView.swift; sourceTree = SOURCE_ROOT; }; + C38EF3E2255B6DF3007E1867 /* GalleryRailView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = GalleryRailView.swift; path = SignalUtilitiesKit/UI/GalleryRailView.swift; sourceTree = SOURCE_ROOT; }; + C38EF3E3255B6DF4007E1867 /* VideoPlayerView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = VideoPlayerView.swift; path = SignalUtilitiesKit/UI/VideoPlayerView.swift; sourceTree = SOURCE_ROOT; }; C38EF3E4255B6DF4007E1867 /* CommonStrings.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = CommonStrings.swift; path = SignalUtilitiesKit/CommonStrings.swift; sourceTree = SOURCE_ROOT; }; - C38EF3E5255B6DF4007E1867 /* ContactCellView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = ContactCellView.h; path = SignalUtilitiesKit/ContactCellView.h; sourceTree = SOURCE_ROOT; }; - C38EF3E6255B6DF4007E1867 /* ContactTableViewCell.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = ContactTableViewCell.h; path = SignalUtilitiesKit/ContactTableViewCell.h; sourceTree = SOURCE_ROOT; }; - C38EF3E7255B6DF5007E1867 /* OWSButton.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = OWSButton.swift; path = SignalUtilitiesKit/OWSButton.swift; sourceTree = SOURCE_ROOT; }; - C38EF3E8255B6DF6007E1867 /* OWSAlerts.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = OWSAlerts.swift; path = SignalUtilitiesKit/OWSAlerts.swift; sourceTree = SOURCE_ROOT; }; - C38EF3E9255B6DF6007E1867 /* Toast.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = Toast.swift; path = SignalUtilitiesKit/Toast.swift; sourceTree = SOURCE_ROOT; }; - C38EF3EA255B6DF6007E1867 /* OWSSearchBar.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = OWSSearchBar.m; path = SignalUtilitiesKit/OWSSearchBar.m; sourceTree = SOURCE_ROOT; }; - C38EF3EB255B6DF6007E1867 /* ContactTableViewCell.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = ContactTableViewCell.m; path = SignalUtilitiesKit/ContactTableViewCell.m; sourceTree = SOURCE_ROOT; }; - C38EF3EC255B6DF6007E1867 /* OWSFlatButton.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = OWSFlatButton.swift; path = SignalUtilitiesKit/OWSFlatButton.swift; sourceTree = SOURCE_ROOT; }; - C38EF3ED255B6DF6007E1867 /* TappableStackView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = TappableStackView.swift; path = SignalUtilitiesKit/TappableStackView.swift; sourceTree = SOURCE_ROOT; }; - C38EF3EE255B6DF6007E1867 /* GradientView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = GradientView.swift; path = SignalUtilitiesKit/GradientView.swift; sourceTree = SOURCE_ROOT; }; + C38EF3E5255B6DF4007E1867 /* ContactCellView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = ContactCellView.h; path = "SignalUtilitiesKit/Remove Later/ContactCellView.h"; sourceTree = SOURCE_ROOT; }; + C38EF3E6255B6DF4007E1867 /* ContactTableViewCell.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = ContactTableViewCell.h; path = "SignalUtilitiesKit/Remove Later/ContactTableViewCell.h"; sourceTree = SOURCE_ROOT; }; + C38EF3E7255B6DF5007E1867 /* OWSButton.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = OWSButton.swift; path = SignalUtilitiesKit/UI/OWSButton.swift; sourceTree = SOURCE_ROOT; }; + C38EF3E8255B6DF6007E1867 /* OWSAlerts.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = OWSAlerts.swift; path = SignalUtilitiesKit/UI/OWSAlerts.swift; sourceTree = SOURCE_ROOT; }; + C38EF3E9255B6DF6007E1867 /* Toast.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = Toast.swift; path = SignalUtilitiesKit/UI/Toast.swift; sourceTree = SOURCE_ROOT; }; + C38EF3EA255B6DF6007E1867 /* OWSSearchBar.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = OWSSearchBar.m; path = SignalUtilitiesKit/UI/OWSSearchBar.m; sourceTree = SOURCE_ROOT; }; + C38EF3EB255B6DF6007E1867 /* ContactTableViewCell.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = ContactTableViewCell.m; path = "SignalUtilitiesKit/Remove Later/ContactTableViewCell.m"; sourceTree = SOURCE_ROOT; }; + C38EF3EC255B6DF6007E1867 /* OWSFlatButton.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = OWSFlatButton.swift; path = SignalUtilitiesKit/UI/OWSFlatButton.swift; sourceTree = SOURCE_ROOT; }; + C38EF3ED255B6DF6007E1867 /* TappableStackView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = TappableStackView.swift; path = SignalUtilitiesKit/UI/TappableStackView.swift; sourceTree = SOURCE_ROOT; }; + C38EF3EE255B6DF6007E1867 /* GradientView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = GradientView.swift; path = SignalUtilitiesKit/UI/GradientView.swift; sourceTree = SOURCE_ROOT; }; C38EF458255B710A007E1867 /* SignalUtilitiesKit-Prefix.pch */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "SignalUtilitiesKit-Prefix.pch"; sourceTree = ""; }; C396469C2509D3ED00B0B9F5 /* pl */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = pl; path = pl.lproj/Localizable.strings; sourceTree = ""; }; C396469D2509D3F400B0B9F5 /* ja */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ja; path = ja.lproj/Localizable.strings; sourceTree = ""; }; @@ -2375,7 +1962,7 @@ C3A7218F2558C0CD0043A11F /* FileServerAPI.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = FileServerAPI.swift; sourceTree = ""; }; C3A721992558C1660043A11F /* AnyPromise+Conversion.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "AnyPromise+Conversion.swift"; sourceTree = ""; }; C3A722292558C1E40043A11F /* DotNetAPI.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DotNetAPI.swift; sourceTree = ""; }; - C3A7225D2558C38D0043A11F /* AnyPromise+Retaining.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "AnyPromise+Retaining.swift"; sourceTree = ""; }; + C3A7225D2558C38D0043A11F /* Promise+Retaining.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Promise+Retaining.swift"; sourceTree = ""; }; C3A7227F2558C4E10043A11F /* AttachmentStream.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AttachmentStream.swift; sourceTree = ""; }; C3A722912558C8940043A11F /* OpenGroupAPIDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OpenGroupAPIDelegate.swift; sourceTree = ""; }; C3A7229B2558E4310043A11F /* OpenGroupMessage+Conversion.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "OpenGroupMessage+Conversion.swift"; sourceTree = ""; }; @@ -2739,7 +2326,6 @@ 34D1F0951F867BFC0066283D /* Cells */, 34D1F0B21F86D31D0066283D /* ConversationCollectionView.h */, 34D1F0B31F86D31D0066283D /* ConversationCollectionView.m */, - 45DDA6232090CEB500DE97F8 /* ConversationHeaderView.swift */, 34D1F0671F8678AA0066283D /* ConversationInputTextView.h */, 34D1F0681F8678AA0066283D /* ConversationInputTextView.m */, 34D1F0691F8678AA0066283D /* ConversationInputToolbar.h */, @@ -2784,12 +2370,6 @@ 34DBF006206C3CB200025978 /* OWSBubbleShapeView.m */, 34DBF002206BD5A500025978 /* OWSBubbleView.h */, 34DBF001206BD5A500025978 /* OWSBubbleView.m */, - 34D1F09A1F867BFC0066283D /* OWSContactOffersCell.h */, - 34D1F09B1F867BFC0066283D /* OWSContactOffersCell.m */, - 3403B95C20EA9527001A1F44 /* OWSContactShareButtonsView.h */, - 3403B95B20EA9526001A1F44 /* OWSContactShareButtonsView.m */, - 34CA63192097806E00E526A0 /* OWSContactShareView.h */, - 34CA631A2097806E00E526A0 /* OWSContactShareView.m */, 34D1F0B51F87F8850066283D /* OWSGenericAttachmentView.h */, 34D1F0B61F87F8850066283D /* OWSGenericAttachmentView.m */, 34AC0A22211C829E00997B47 /* OWSLabel.h */, @@ -2857,12 +2437,10 @@ 34C4E2562118957600BEA353 /* WebRTCProto.swift */, 45D231761DC7E8F10034FA89 /* SessionResetJob.swift */, 45CD81EE1DC030E7004C9430 /* SyncPushTokensJob.swift */, - 452ECA4C1E087E7200E2F016 /* MessageFetcherJob.swift */, 45CB2FA71CB7146C00E1B343 /* Launch Screen.storyboard */, A5509EC91A69AB8B00ABA4BC /* Main.storyboard */, 3430FE171F7751D4000EC51B /* GiphyAPI.swift */, 34D1F0511F7E8EA30066283D /* GiphyDownloader.swift */, - 4CC0B59B20EC5F2E00CF6EE0 /* ConversationConfigurationSyncOperation.swift */, 4CEB78C72178EBAB00F315D2 /* OWSSessionResetJobRecord.h */, 4CEB78C82178EBAB00F315D2 /* OWSSessionResetJobRecord.m */, 451166BF1FD86B98000739BA /* AccountManager.swift */, @@ -2880,12 +2458,6 @@ 340FC893204DAC8C007AEB0F /* AboutTableViewController.m */, 340FC892204DAC8C007AEB0F /* AddToBlockListViewController.h */, 340FC886204DAC8C007AEB0F /* AddToBlockListViewController.m */, - 340FC881204DAC8C007AEB0F /* AdvancedSettingsTableViewController.h */, - 340FC88C204DAC8C007AEB0F /* AdvancedSettingsTableViewController.m */, - 340FC890204DAC8C007AEB0F /* BlockListViewController.h */, - 340FC887204DAC8C007AEB0F /* BlockListViewController.m */, - 340FC889204DAC8C007AEB0F /* DomainFrontingCountryViewController.h */, - 340FC87D204DAC8C007AEB0F /* DomainFrontingCountryViewController.m */, 340FC88B204DAC8C007AEB0F /* NotificationSettingsOptionsViewController.h */, 340FC87B204DAC8C007AEB0F /* NotificationSettingsOptionsViewController.m */, 340FC88A204DAC8C007AEB0F /* NotificationSettingsViewController.h */, @@ -2900,15 +2472,10 @@ 340FC87E204DAC8C007AEB0F /* PrivacySettingsTableViewController.m */, 34D5CCA71EAE3D30005515DB /* AvatarViewHelper.h */, 34D5CCA81EAE3D30005515DB /* AvatarViewHelper.m */, - 4C13C9F520E57BA30089A98B /* ColorPickerViewController.swift */, - 348BB25C20A0C5530047AEC2 /* ContactShareViewHelper.swift */, - 34B3F83E1E8DF1700035BE1A /* ContactsPicker.swift */, - 34E88D252098C5AE00A608F4 /* ContactViewController.swift */, 346B66301F4E29B200E5122F /* CropScaleImageViewController.swift */, 34D1F04F1F7D45A60066283D /* GifPickerCell.swift */, 34BECE2F1F7ABCF800D7438D /* GifPickerLayout.swift */, 34BECE2D1F7ABCE000D7438D /* GifPickerViewController.swift */, - 34B3F84C1E8DF1700035BE1A /* InviteFlow.swift */, 4542DF53208D40AC007B4E76 /* LoadingViewController.swift */, 3496744E2076ACCE00080B5F /* LongTextViewController.swift */, 45B9EE9A200E91FB005D2F2D /* MediaDetailViewController.h */, @@ -2918,7 +2485,6 @@ 454A84032059C787008B8C75 /* MediaTileViewController.swift */, 4CFF4C0920F55BBA005DA313 /* MenuActionsViewController.swift */, 34CA1C261F7156F300E51C51 /* MessageDetailViewController.swift */, - 452B998F20A34B6B006F2F9E /* AddContactShareToExistingContactViewController.swift */, 4C4AE69F224AF21900D4AF6F /* SendMediaNavigationController.swift */, 34969559219B605E00DCFE74 /* ImagePickerController.swift */, 3496955A219B605E00DCFE74 /* PhotoCollectionPickerController.swift */, @@ -2927,22 +2493,12 @@ 4C21D5D7223AC60F00EF8A77 /* PhotoCapture.swift */, 3441FD9E21A3604F00BB9542 /* BackupRestoreViewController.swift */, 3448E1652215B313004B052E /* OnboardingCaptchaViewController.swift */, - 4585C4671ED8F8D200896AEA /* SafetyNumberConfirmationAlert.swift */, - 34B3F86D1E8DF1700035BE1A /* SignalsNavigationController.h */, - 34B3F86E1E8DF1700035BE1A /* SignalsNavigationController.m */, 340FC8A4204DAC8D007AEB0F /* AddToGroupViewController.h */, 340FC89B204DAC8D007AEB0F /* AddToGroupViewController.m */, - 340FC89D204DAC8D007AEB0F /* FingerprintViewController.h */, - 340FC8A2204DAC8D007AEB0F /* FingerprintViewController.m */, - 340FC8A5204DAC8D007AEB0F /* FingerprintViewScanController.h */, - 340FC89F204DAC8D007AEB0F /* FingerprintViewScanController.m */, - 340FC898204DAC8D007AEB0F /* OWSAddToContactViewController.h */, - 340FC8A1204DAC8D007AEB0F /* OWSAddToContactViewController.m */, 340FC8A0204DAC8D007AEB0F /* OWSConversationSettingsViewController.h */, 34E3E5671EC4B19400495BAC /* AudioProgressView.swift */, 4C2F454E214C00E1004871FF /* AvatarTableViewCell.swift */, 4CA46F4B219CCC630038ABDE /* CaptionView.swift */, - 451764291DE939FD00EDB8B9 /* ContactCell.swift */, 4C4AEC4420EC343B0020E72B /* DismissableTextField.swift */, 45A663C41F92EC760027B59E /* GroupTableViewCell.swift */, 34129B8521EF8779005457A8 /* LinkPreviewView.swift */, @@ -2950,13 +2506,9 @@ 34386A53207D271C009F5D9C /* NeverClearView.swift */, 34F308A01ECB469700BB7697 /* OWSBezierPathView.h */, 34F308A11ECB469700BB7697 /* OWSBezierPathView.m */, - 459311FA1D75C948008DD4F0 /* OWSDeviceTableViewCell.h */, - 459311FB1D75C948008DD4F0 /* OWSDeviceTableViewCell.m */, 34330AA11E79686200DF2FB9 /* OWSProgressView.h */, 34330AA21E79686200DF2FB9 /* OWSProgressView.m */, 4C1885D1218F8E1C00B67051 /* PhotoGridViewCell.swift */, - 45D308AB2049A439000189E4 /* PinEntryView.h */, - 45D308AC2049A439000189E4 /* PinEntryView.m */, 457F671A20746193000EABCD /* QuotedReplyPreview.swift */, 45A6DAD51EBBF85500893231 /* ReminderView.swift */, 450D19111F85236600970622 /* RemoteVideoView.h */, @@ -2980,7 +2532,6 @@ B90418E5183E9DD40038554A /* DateUtil.m */, 34B0796C1FCF46B000E248C2 /* MainAppContext.h */, 34B0796B1FCF46B000E248C2 /* MainAppContext.m */, - 34D99C911F2937CC00D284D6 /* OWSAnalytics.swift */, 344825C4211390C700DB4BD8 /* OWSOrphanDataCleaner.h */, 344825C5211390C800DB4BD8 /* OWSOrphanDataCleaner.m */, 34D2CCD82062E7D000CB1A14 /* OWSScreenLockUI.h */, @@ -2993,17 +2544,11 @@ 45B5360D206DD8BB00D61655 /* UIResponder+OWS.swift */, 4C586924224FAB83003FD070 /* AVAudioSession+OWS.h */, 4C586925224FAB83003FD070 /* AVAudioSession+OWS.m */, - 4579431C1E7C8CE9008ED0C0 /* Pastelog.h */, - 4579431D1E7C8CE9008ED0C0 /* Pastelog.m */, 450DF2041E0D74AC003D14BE /* Platform.swift */, 4521C3BF1F59F3BA00B4C582 /* TextFieldHelper.swift */, 4CC613352227A00400E21A3A /* ConversationSearch.swift */, 340FC89A204DAC8D007AEB0F /* OWSConversationSettingsViewController.m */, 340FC899204DAC8D007AEB0F /* OWSConversationSettingsViewDelegate.h */, - 340FC89E204DAC8D007AEB0F /* ShowGroupMembersViewController.h */, - 340FC8A6204DAC8D007AEB0F /* ShowGroupMembersViewController.m */, - 340FC8A3204DAC8D007AEB0F /* UpdateGroupViewController.h */, - 340FC89C204DAC8D007AEB0F /* UpdateGroupViewController.m */, 34D1F0BF1F8EC1760066283D /* MessageRecipientStatusUtils.swift */, 3448BFC01EDF0EA7005B2D69 /* ConversationView */, 34A6C27F21E503E600B5B12E /* OWSImagePickerController.swift */, @@ -3146,11 +2691,6 @@ isa = PBXGroup; children = ( C354E75923FE2A7600CE22E3 /* BaseVC.swift */, - B885D5F3233491AB00EE0D8E /* DeviceLinkingModal.swift */, - B894D0702339D6F300B4D94D /* DeviceLinkingModalDelegate.swift */, - B80C6B562384A56D00FDBC8B /* DeviceLinksVC.swift */, - B80C6B582384C4E700FDBC8B /* DeviceNameModal.swift */, - B80C6B5A2384C7F900FDBC8B /* DeviceNameModalDelegate.swift */, B82B408D239DC00D00A248E7 /* DisplayNameVC.swift */, C3E5C2F9251DBABB0040DFFC /* EditClosedGroupVC.swift */, B88847BB23E10BC6009836D2 /* GroupMembersVC.swift */, @@ -3330,240 +2870,67 @@ C33FD9AC255A548A00E217F9 /* SignalUtilitiesKit */ = { isa = PBXGroup; children = ( + C33FD9B7255A54A300E217F9 /* Meta */, C3CA3B11255CF17200F4C6D4 /* Utilities */, - C33FDC09255A581D00E217F9 /* AccountServiceClient.swift */, - C33FDBC3255A581700E217F9 /* AnyPromise+Conversion.swift */, + C3851CD225624B060061EEB0 /* UI */, + C3851CE3256250FA0061EEB0 /* Remove Later */, + C38BBA17255E327A0041B9A3 /* Move to main app */, + C38BBA0B255E31EC0041B9A3 /* Attachments */, + C38BBA0C255E32020041B9A3 /* Threads */, + C38BBA0D255E321C0041B9A3 /* Messages */, + C38BBA0E255E32440041B9A3 /* Database */, C33FDB8A255A581200E217F9 /* AppContext.h */, C33FDB85255A581100E217F9 /* AppContext.m */, C33FDB01255A580700E217F9 /* AppReadiness.h */, C33FDB75255A581000E217F9 /* AppReadiness.m */, C33FDB4C255A580D00E217F9 /* AppVersion.h */, - C38EF215255B6D3A007E1867 /* OWSConversationColor.h */, - C38EF213255B6D3A007E1867 /* OWSConversationColor.m */, - C38EF212255B6D3A007E1867 /* Theme.h */, - C38EF225255B6D5D007E1867 /* AttachmentSharing.h */, - C38EF223255B6D5D007E1867 /* AttachmentSharing.m */, - C38EF227255B6D5D007E1867 /* OWSVideoPlayer.swift */, - C38EF226255B6D5D007E1867 /* ShareViewDelegate.swift */, - C38EF241255B6D67007E1867 /* Collection+OWS.swift */, C38EF23F255B6D67007E1867 /* NSAttributedString+OWS.h */, - C38EF3DE255B6DF2007E1867 /* AvatarImageView.swift */, C38EF3E4255B6DF4007E1867 /* CommonStrings.swift */, - C38EF3E5255B6DF4007E1867 /* ContactCellView.h */, - C38EF3D6255B6DEF007E1867 /* ContactCellView.m */, - C38EF3DA255B6DF1007E1867 /* ContactsViewHelper.h */, - C38EF3D5255B6DEF007E1867 /* ContactsViewHelper.m */, - C38EF3E6255B6DF4007E1867 /* ContactTableViewCell.h */, - C38EF3EB255B6DF6007E1867 /* ContactTableViewCell.m */, - C38EF3DC255B6DF1007E1867 /* DirectionalPanGestureRecognizer.swift */, - C38EF3D4255B6DEE007E1867 /* DisappearingTimerConfigurationView.swift */, - C38EF3E2255B6DF3007E1867 /* GalleryRailView.swift */, - C38EF3EE255B6DF6007E1867 /* GradientView.swift */, - C38EF3E8255B6DF6007E1867 /* OWSAlerts.swift */, - C38EF3E7255B6DF5007E1867 /* OWSButton.swift */, - C38EF3EC255B6DF6007E1867 /* OWSFlatButton.swift */, - C38EF3DB255B6DF1007E1867 /* OWSLayerView.swift */, - C38EF3D9255B6DF1007E1867 /* OWSNavigationBar.swift */, - C38EF3D3255B6DEE007E1867 /* OWSSearchBar.h */, - C38EF3EA255B6DF6007E1867 /* OWSSearchBar.m */, - C38EF3D7255B6DF0007E1867 /* OWSTextField.h */, - C38EF3E0255B6DF3007E1867 /* OWSTextField.m */, - C38EF3D8255B6DF0007E1867 /* OWSTextView.h */, - C38EF3DF255B6DF2007E1867 /* OWSTextView.m */, - C38EF3ED255B6DF6007E1867 /* TappableStackView.swift */, - C38EF3E1255B6DF3007E1867 /* TappableView.swift */, C38EF3D2255B6DEE007E1867 /* ThreadViewHelper.h */, C38EF3D1255B6DEE007E1867 /* ThreadViewHelper.m */, - C38EF3E9255B6DF6007E1867 /* Toast.swift */, - C38EF3DD255B6DF1007E1867 /* UIAlertController+OWS.swift */, - C38EF3E3255B6DF4007E1867 /* VideoPlayerView.swift */, - C38EF3B1255B6DE5007E1867 /* ImageEditorBrushViewController.swift */, - C38EF3B7255B6DE6007E1867 /* ImageEditorCanvasView.swift */, - C38EF3B4255B6DE6007E1867 /* ImageEditorContents.swift */, - C38EF3B0255B6DE5007E1867 /* ImageEditorCropViewController.swift */, - C38EF3AA255B6DE4007E1867 /* ImageEditorItem.swift */, - C38EF3B6255B6DE6007E1867 /* ImageEditorModel.swift */, - C38EF3B2255B6DE5007E1867 /* ImageEditorPaletteView.swift */, - C38EF3AC255B6DE4007E1867 /* ImageEditorPanGestureRecognizer.swift */, - C38EF3A9255B6DE4007E1867 /* ImageEditorPinchGestureRecognizer.swift */, - C38EF3AB255B6DE4007E1867 /* ImageEditorStrokeItem.swift */, - C38EF3B3255B6DE6007E1867 /* ImageEditorTextItem.swift */, - C38EF3A8255B6DE4007E1867 /* ImageEditorTextViewController.swift */, - C38EF3AD255B6DE4007E1867 /* ImageEditorTransform.swift */, - C38EF3AF255B6DE5007E1867 /* ImageEditorView.swift */, - C38EF3AE255B6DE5007E1867 /* OrderedDictionary.swift */, - C38EF3B5255B6DE6007E1867 /* OWSViewController+ImageEditor.swift */, - C38EF25D255B6D6E007E1867 /* OWSContactsManager.h */, - C38EF25B255B6D6E007E1867 /* OWSContactsManager.m */, - C38EF399255B6DD9007E1867 /* ContactShareViewModel.swift */, C38EF398255B6DD9007E1867 /* OWSQuotedReplyModel.h */, C38EF39A255B6DD9007E1867 /* OWSQuotedReplyModel.m */, C38EF397255B6DD9007E1867 /* ThreadViewModel.swift */, - C38EF354255B6DCB007E1867 /* ContactFieldView.swift */, - C38EF383255B6DD1007E1867 /* ApprovalRailCellView.swift */, - C38EF37D255B6DCF007E1867 /* AttachmentApprovalInputAccessoryView.swift */, - C38EF37F255B6DD0007E1867 /* AttachmentApprovalViewController.swift */, - C38EF381255B6DD1007E1867 /* AttachmentCaptionToolbar.swift */, - C38EF384255B6DD2007E1867 /* AttachmentCaptionViewController.swift */, - C38EF37E255B6DD0007E1867 /* AttachmentItemCollection.swift */, - C38EF382255B6DD1007E1867 /* AttachmentPrepViewController.swift */, - C38EF37C255B6DCF007E1867 /* AttachmentTextToolbar.swift */, - C38EF380255B6DD0007E1867 /* AttachmentTextView.swift */, - C38EF348255B6DC7007E1867 /* ContactShareApprovalViewController.swift */, - C38EF347255B6DC6007E1867 /* EditContactShareNameViewController.swift */, - C38EF358255B6DCC007E1867 /* MediaMessageView.swift */, - C38EF357255B6DCC007E1867 /* MessageApprovalViewController.swift */, - C38EF349255B6DC7007E1867 /* ModalActivityIndicatorViewController.swift */, - C38EF34A255B6DC7007E1867 /* NewNonContactConversationViewController.h */, - C38EF350255B6DC9007E1867 /* NewNonContactConversationViewController.m */, - C38EF343255B6DC5007E1867 /* OWSNavigationController.h */, - C38EF356255B6DCB007E1867 /* OWSNavigationController.m */, - C38EF34D255B6DC8007E1867 /* OWSTableViewController.h */, - C38EF34B255B6DC8007E1867 /* OWSTableViewController.m */, - C38EF344255B6DC5007E1867 /* OWSViewController.h */, - C38EF355255B6DCB007E1867 /* OWSViewController.m */, - C38EF346255B6DC6007E1867 /* ReturnToCallViewController.swift */, - C38EF34C255B6DC8007E1867 /* ScreenLockViewController.h */, - C38EF351255B6DC9007E1867 /* ScreenLockViewController.m */, - C38EF34E255B6DC8007E1867 /* SelectRecipientViewController.h */, - C38EF345255B6DC6007E1867 /* SelectRecipientViewController.m */, - C38EF341255B6DC5007E1867 /* SelectThreadViewController.h */, - C38EF342255B6DC5007E1867 /* SelectThreadViewController.m */, - C38EF353255B6DCB007E1867 /* SharingThreadPickerViewController.h */, - C38EF352255B6DC9007E1867 /* SharingThreadPickerViewController.m */, - C38EF33F255B6DC5007E1867 /* SheetViewController.swift */, - C38EF34F255B6DC9007E1867 /* ViewControllerUtils.h */, - C38EF340255B6DC5007E1867 /* ViewControllerUtils.m */, C38EF2E5255B6DB9007E1867 /* AppPreferences.swift */, - C38EF2FA255B6DBD007E1867 /* Bench.swift */, C38EF30B255B6DBE007E1867 /* BlockListCache.swift */, - C38EF303255B6DBE007E1867 /* BlockListUIUtils.h */, - C38EF2FD255B6DBD007E1867 /* BlockListUIUtils.m */, - C38EF2FC255B6DBD007E1867 /* ConversationStyle.swift */, - C38EF2F8255B6DBC007E1867 /* DebugLogger.h */, - C38EF2E6255B6DBA007E1867 /* DebugLogger.m */, C38EF309255B6DBE007E1867 /* DeviceSleepManager.swift */, - C38EF2ED255B6DBB007E1867 /* DisplayableText.swift */, C38EF2E4255B6DB9007E1867 /* FullTextSearcher.swift */, C38EF304255B6DBE007E1867 /* ImageCache.swift */, - C38EF302255B6DBE007E1867 /* OWSAnyTouchGestureRecognizer.h */, - C38EF2F0255B6DBB007E1867 /* OWSAnyTouchGestureRecognizer.m */, - C38EF2F5255B6DBC007E1867 /* OWSAudioPlayer.h */, - C38EF2F7255B6DBC007E1867 /* OWSAudioPlayer.m */, - C38EF2EA255B6DBA007E1867 /* OWSAvatarBuilder.h */, - C38EF2EE255B6DBB007E1867 /* OWSAvatarBuilder.m */, - C38EF2FE255B6DBD007E1867 /* OWSContactAvatarBuilder.h */, - C38EF2F9255B6DBC007E1867 /* OWSContactAvatarBuilder.m */, - C38EF301255B6DBD007E1867 /* OWSFormat.h */, - C38EF305255B6DBE007E1867 /* OWSFormat.m */, - C38EF2EB255B6DBA007E1867 /* OWSGroupAvatarBuilder.h */, - C38EF2E8255B6DBA007E1867 /* OWSGroupAvatarBuilder.m */, C38EF2F1255B6DBB007E1867 /* OWSPreferences.h */, C38EF308255B6DBE007E1867 /* OWSPreferences.m */, C38EF2E2255B6DB9007E1867 /* OWSScreenLock.swift */, - C38EF2E7255B6DBA007E1867 /* OWSScrubbingLogFormatter.h */, - C38EF2F6255B6DBC007E1867 /* OWSScrubbingLogFormatter.m */, C38EF2E9255B6DBA007E1867 /* OWSUnreadIndicator.h */, C38EF2E3255B6DB9007E1867 /* OWSUnreadIndicator.m */, - C38EF2FB255B6DBD007E1867 /* OWSWindowManager.h */, - C38EF306255B6DBE007E1867 /* OWSWindowManager.m */, C38EF2EC255B6DBA007E1867 /* ProximityMonitoringManager.swift */, C38EF2F2255B6DBC007E1867 /* Searcher.swift */, - C38EF307255B6DBE007E1867 /* UIGestureRecognizer+OWS.swift */, - C38EF2F3255B6DBC007E1867 /* UIImage+OWS.swift */, - C38EF30A255B6DBE007E1867 /* UIUtil.h */, - C38EF300255B6DBD007E1867 /* UIUtil.m */, - C38EF2EF255B6DBB007E1867 /* Weak.swift */, - C38EF2D2255B6DAF007E1867 /* OWSProfileManager.h */, - C38EF2CF255B6DAE007E1867 /* OWSProfileManager.m */, - C38EF2D3255B6DAF007E1867 /* OWSUserProfile.h */, - C38EF2D1255B6DAF007E1867 /* OWSUserProfile.m */, - C38EF2D0255B6DAE007E1867 /* ProfileFetcherJob.swift */, - C38EF25A255B6D6E007E1867 /* OWSSyncManager.h */, - C38EF25C255B6D6E007E1867 /* OWSSyncManager.m */, - C38EF259255B6D6E007E1867 /* SystemContactsFetcher.swift */, - C38EF23A255B6D66007E1867 /* NSAttributedString+OWS.m */, - C38EF23C255B6D66007E1867 /* UIColor+OWS.h */, - C38EF242255B6D67007E1867 /* UIColor+OWS.m */, - C38EF2A2255B6D93007E1867 /* Identicon+ObjC.swift */, C38EF2BF255B6DA6007E1867 /* OWSContactOffersInteraction.h */, C38EF2C0255B6DA6007E1867 /* OWSContactOffersInteraction.m */, C38EF2BE255B6DA6007E1867 /* TSUnreadIndicatorInteraction.h */, C38EF2C1255B6DA6007E1867 /* TSUnreadIndicatorInteraction.m */, - C38EF2A3255B6D93007E1867 /* PlaceholderIcon.swift */, - C38EF2B2255B6D9C007E1867 /* UIView+Utilities.swift */, - C38EF2B1255B6D9C007E1867 /* UIViewController+Utilities.swift */, - C38EF2A4255B6D93007E1867 /* ProfilePictureView.swift */, - C38EF237255B6D65007E1867 /* UIDevice+featureSupport.swift */, - C38EF239255B6D66007E1867 /* UIFont+OWS.h */, - C38EF238255B6D66007E1867 /* UIFont+OWS.m */, C38EF284255B6D84007E1867 /* AppSetup.h */, C38EF287255B6D85007E1867 /* AppSetup.m */, C38EF289255B6D85007E1867 /* NoopCallMessageHandler.swift */, C3F0A52F255C80BC007BE2A3 /* NoopNotificationsManager.swift */, - C38EF281255B6D84007E1867 /* OWSAudioSession.swift */, C38EF288255B6D85007E1867 /* OWSSounds.h */, C38EF28B255B6D86007E1867 /* OWSSounds.m */, C38EF285255B6D84007E1867 /* SignalKeyingStorage.h */, C38EF282255B6D84007E1867 /* SignalKeyingStorage.m */, C38EF283255B6D84007E1867 /* VersionMigrations.h */, C38EF286255B6D85007E1867 /* VersionMigrations.m */, - C38EF271255B6D79007E1867 /* OWSDatabaseMigration.h */, - C38EF270255B6D79007E1867 /* OWSDatabaseMigration.m */, - C38EF26F255B6D79007E1867 /* OWSDatabaseMigrationRunner.h */, - C38EF26D255B6D79007E1867 /* OWSDatabaseMigrationRunner.m */, - C38EF26E255B6D79007E1867 /* OWSResaveCollectionDBMigration.h */, - C38EF26C255B6D79007E1867 /* OWSResaveCollectionDBMigration.m */, - C38EF23D255B6D66007E1867 /* UIView+OWS.h */, - C38EF23E255B6D66007E1867 /* UIView+OWS.m */, - C38EF240255B6D67007E1867 /* UIView+OWS.swift */, - C38EF236255B6D65007E1867 /* UIViewController+OWS.h */, - C38EF23B255B6D66007E1867 /* UIViewController+OWS.m */, - C38EF224255B6D5D007E1867 /* SignalAttachment.swift */, - C38EF214255B6D3A007E1867 /* Theme.m */, C33FDA8B255A57FD00E217F9 /* AppVersion.m */, - C33FDA6E255A57FA00E217F9 /* Array+Description.swift */, - C33FDAA8255A57FF00E217F9 /* BuildConfiguration.swift */, - C33FDC03255A581D00E217F9 /* ByteParser.h */, - C33FDAE0255A580400E217F9 /* ByteParser.m */, - C33FDB0C255A580700E217F9 /* CDSQuote.h */, - C33FDAD0255A580300E217F9 /* CDSQuote.m */, - C33FDBE3255A581A00E217F9 /* CDSSigningCertificate.h */, - C33FDACB255A580200E217F9 /* CDSSigningCertificate.m */, - C33FDBED255A581B00E217F9 /* ClosedGroupParser.swift */, C33FDB34255A580B00E217F9 /* ClosedGroupPoller.swift */, C33FDA74255A57FB00E217F9 /* ClosedGroupsProtocol.swift */, - C33FDBEE255A581B00E217F9 /* ClosedGroupUpdateMessage.swift */, - C33FDB76255A581000E217F9 /* ClosedGroupUtilities.swift */, - C33FDAD2255A580300E217F9 /* Contact.h */, - C33FDA77255A57FB00E217F9 /* Contact.m */, - C33FDB66255A580F00E217F9 /* ContactDiscoveryService.h */, - C33FDBDC255A581900E217F9 /* ContactDiscoveryService.m */, - C33FDB5E255A580E00E217F9 /* ContactParser.swift */, C33FDB2F255A580A00E217F9 /* ContactsManagerProtocol.h */, - C33FDAF0255A580500E217F9 /* ContactsUpdater.h */, - C33FDA84255A57FC00E217F9 /* ContactsUpdater.m */, C33FDB68255A580F00E217F9 /* ContentProxy.swift */, C33FDBC9255A581700E217F9 /* CreatePreKeysOperation.swift */, - C33FDBAC255A581500E217F9 /* Data+SecureRandom.swift */, C33FDA94255A57FE00E217F9 /* Data+Streaming.swift */, C33FDB54255A580D00E217F9 /* DataSource.h */, C33FDBB6255A581600E217F9 /* DataSource.m */, - C33FDA7C255A57FB00E217F9 /* Debugging.swift */, - C33FDC04255A581D00E217F9 /* DecryptionUtilities.swift */, - C33FDBD1255A581800E217F9 /* DeviceLink.swift */, - C33FDB72255A581000E217F9 /* DeviceLinkIndex.swift */, - C33FDB0F255A580800E217F9 /* DeviceLinkingSession.swift */, - C33FDB8D255A581200E217F9 /* DeviceLinkingSessionDelegate.swift */, - C33FDB1F255A580900E217F9 /* DeviceLinkingUtilities.swift */, - C33FDB98255A581300E217F9 /* DeviceNames.swift */, C33FDBDA255A581900E217F9 /* Dictionary+Description.swift */, C33FDAFF255A580600E217F9 /* DisplayNameUtilities.swift */, C33FDBF4255A581B00E217F9 /* DisplayNameUtilities2.swift */, C33FDA73255A57FA00E217F9 /* ECKeyPair+Hexadecimal.swift */, - C33FDAD4255A580300E217F9 /* EncryptionUtilities.swift */, C33FDB69255A580F00E217F9 /* FeatureFlags.swift */, - C33FDB89255A581200E217F9 /* FileServerAPI+Deprecated.swift */, C33FDAC6255A580200E217F9 /* Fingerprint.pb.swift */, C33FDB26255A580A00E217F9 /* FingerprintProto.swift */, C33FDB7F255A581100E217F9 /* FullTextSearchFinder.swift */, @@ -3572,115 +2939,44 @@ C33FDBC1255A581700E217F9 /* GeneralUtilities.swift */, C33FDB19255A580900E217F9 /* GroupUtilities.swift */, C33FDB87255A581100E217F9 /* JobQueue.swift */, - C33FDC0F255A581E00E217F9 /* LKDeviceLinkMessage.h */, - C33FDAFA255A580600E217F9 /* LKDeviceLinkMessage.m */, C33FDBCA255A581700E217F9 /* LKGroupUtilities.h */, C33FDBE1255A581A00E217F9 /* LKGroupUtilities.m */, - C33FDA91255A57FD00E217F9 /* LKSyncOpenGroupsMessage.h */, - C33FDBDF255A581A00E217F9 /* LKSyncOpenGroupsMessage.m */, - C33FDB61255A580E00E217F9 /* LKUnlinkDeviceMessage.h */, - C33FDB13255A580800E217F9 /* LKUnlinkDeviceMessage.m */, C33FDB6B255A580F00E217F9 /* LKUserDefaults.swift */, C33FDBF0255A581B00E217F9 /* LokiDatabaseUtilities.swift */, - C33FDACA255A580200E217F9 /* LokiMessage.swift */, C33FDBDE255A581900E217F9 /* LokiPushNotificationManager.swift */, - C33FDC1F255A581F00E217F9 /* LokiSessionResetImplementation.swift */, - C33FDAFD255A580600E217F9 /* LRUCache.swift */, + C33FDC1F255A581F00E217F9 /* LokiSessionRestorationImplementation.swift */, C33FDA7E255A57FB00E217F9 /* Mention.swift */, C33FDA81255A57FC00E217F9 /* MentionsManager.swift */, - C33FDB82255A581100E217F9 /* MessageWrapper.swift */, - C33FDAFC255A580600E217F9 /* MIMETypeUtil.h */, - C33FDB41255A580C00E217F9 /* MIMETypeUtil.m */, - C33FDAA9255A580000E217F9 /* Mnemonic.swift */, - C33FDA9F255A57FF00E217F9 /* NetworkManager.swift */, C33FDB80255A581100E217F9 /* Notification+Loki.swift */, C33FDB7A255A581000E217F9 /* NotificationsProtocol.h */, - C33FDB5C255A580E00E217F9 /* NSArray+Functional.h */, - C33FDAB8255A580100E217F9 /* NSArray+Functional.m */, - C33FDBF8255A581C00E217F9 /* NSArray+OWS.h */, - C33FDB0D255A580800E217F9 /* NSArray+OWS.m */, - C33FDB29255A580A00E217F9 /* NSData+Image.h */, - C33FDAEF255A580500E217F9 /* NSData+Image.m */, C33FDB0E255A580800E217F9 /* NSError+MessageSending.h */, C33FDB09255A580700E217F9 /* NSError+MessageSending.m */, C33FDB3B255A580B00E217F9 /* NSNotificationCenter+OWS.h */, C33FDB6C255A580F00E217F9 /* NSNotificationCenter+OWS.m */, - C33FDADC255A580400E217F9 /* NSObject+Casting.h */, - C33FDAAA255A580000E217F9 /* NSObject+Casting.m */, - C33FDA7A255A57FB00E217F9 /* NSRegularExpression+SSK.swift */, - C33FDBFE255A581C00E217F9 /* NSSet+Functional.h */, - C33FDAC1255A580100E217F9 /* NSSet+Functional.m */, - C33FDB12255A580800E217F9 /* NSString+SSK.h */, - C33FDB45255A580C00E217F9 /* NSString+SSK.m */, - C33FDBE7255A581A00E217F9 /* NSTimer+OWS.h */, - C33FDA8F255A57FD00E217F9 /* NSTimer+OWS.m */, - C33FDBF6255A581C00E217F9 /* NSURLSessionDataTask+StatusCode.h */, - C33FDBB4255A581600E217F9 /* NSURLSessionDataTask+StatusCode.m */, - C33FDB51255A580D00E217F9 /* NSUserDefaults+OWS.h */, - C33FDB77255A581000E217F9 /* NSUserDefaults+OWS.m */, - C33FDBD0255A581800E217F9 /* OnionRequestAPI+Encryption.swift */, C33FDA99255A57FE00E217F9 /* OutageDetection.swift */, - C33FDB21255A580900E217F9 /* OWS2FAManager.h */, - C33FDB9A255A581300E217F9 /* OWS2FAManager.m */, - C33FDAA3255A57FF00E217F9 /* OWSAddToContactsOfferMessage.h */, - C33FDAA2255A57FF00E217F9 /* OWSAddToContactsOfferMessage.m */, - C33FDBBD255A581600E217F9 /* OWSAddToProfileWhitelistOfferMessage.h */, - C33FDAE5255A580400E217F9 /* OWSAddToProfileWhitelistOfferMessage.m */, - C33FDAD6255A580300E217F9 /* OWSAnalytics.h */, - C33FDA89255A57FD00E217F9 /* OWSAnalytics.m */, - C33FDAF5255A580600E217F9 /* OWSAnalyticsEvents.h */, - C33FDBAF255A581500E217F9 /* OWSAnalyticsEvents.m */, C33FDACF255A580300E217F9 /* OWSAttachmentDownloads.h */, C33FDC13255A581E00E217F9 /* OWSAttachmentDownloads.m */, C33FDB38255A580B00E217F9 /* OWSBackgroundTask.h */, C33FDC1B255A581F00E217F9 /* OWSBackgroundTask.m */, C33FDAEA255A580500E217F9 /* OWSBackupFragment.h */, C33FDB07255A580700E217F9 /* OWSBackupFragment.m */, - C33FDBD4255A581900E217F9 /* OWSBatchMessageProcessor.h */, - C33FDB10255A580800E217F9 /* OWSBatchMessageProcessor.m */, - C33FDBF3255A581B00E217F9 /* OWSBlockedPhoneNumbersMessage.h */, - C33FDBE0255A581A00E217F9 /* OWSBlockedPhoneNumbersMessage.m */, C33FDBEB255A581B00E217F9 /* OWSBlockingManager.h */, C33FDA68255A57F900E217F9 /* OWSBlockingManager.m */, C33FDB9D255A581300E217F9 /* OWSCallMessageHandler.h */, - C33FDAC8255A580200E217F9 /* OWSCensorshipConfiguration.h */, - C33FDA7D255A57FB00E217F9 /* OWSCensorshipConfiguration.m */, C33FDA95255A57FE00E217F9 /* OWSChunkedOutputStream.h */, C33FDB4B255A580C00E217F9 /* OWSChunkedOutputStream.m */, - C33FDAC9255A580200E217F9 /* OWSContact.h */, - C33FDC0A255A581D00E217F9 /* OWSContact.m */, - C33FDB8E255A581200E217F9 /* OWSContact+Private.h */, - C33FDB35255A580B00E217F9 /* OWSContactDiscoveryOperation.swift */, C33FDA9D255A57FF00E217F9 /* OWSContactsOutputStream.h */, C33FDB44255A580C00E217F9 /* OWSContactsOutputStream.m */, - C33FDB42255A580C00E217F9 /* OWSCountryMetadata.h */, - C33FDAC5255A580200E217F9 /* OWSCountryMetadata.m */, - C33FDB2D255A580A00E217F9 /* OWSDevice.h */, - C33FDB30255A580A00E217F9 /* OWSDevice.m */, - C33FDAAD255A580000E217F9 /* OWSDeviceProvisioner.h */, - C33FDAD7255A580300E217F9 /* OWSDeviceProvisioner.m */, - C33FDB1B255A580900E217F9 /* OWSDeviceProvisioningCodeService.h */, - C33FDAEB255A580500E217F9 /* OWSDeviceProvisioningCodeService.m */, - C33FDBF5255A581B00E217F9 /* OWSDeviceProvisioningService.h */, - C33FDB92255A581200E217F9 /* OWSDeviceProvisioningService.m */, - C33FDBDB255A581900E217F9 /* OWSDevicesService.h */, - C33FDBE5255A581A00E217F9 /* OWSDevicesService.m */, C33FDADA255A580400E217F9 /* OWSDisappearingConfigurationUpdateInfoMessage.h */, C33FDA6B255A57FA00E217F9 /* OWSDisappearingConfigurationUpdateInfoMessage.m */, C33FDAD9255A580300E217F9 /* OWSDisappearingMessagesConfiguration.h */, C33FDBA4255A581400E217F9 /* OWSDisappearingMessagesConfiguration.m */, - C33FDB16255A580800E217F9 /* OWSDisappearingMessagesConfigurationMessage.h */, - C33FDB97255A581300E217F9 /* OWSDisappearingMessagesConfigurationMessage.m */, C33FDC05255A581D00E217F9 /* OWSDisappearingMessagesFinder.h */, C33FDA86255A57FC00E217F9 /* OWSDisappearingMessagesFinder.m */, C33FDA80255A57FC00E217F9 /* OWSDisappearingMessagesJob.h */, C33FDBDD255A581900E217F9 /* OWSDisappearingMessagesJob.m */, C33FDA96255A57FE00E217F9 /* OWSDispatch.h */, C33FDAC3255A580200E217F9 /* OWSDispatch.m */, - C33FDB63255A580E00E217F9 /* OWSDynamicOutgoingMessage.h */, - C33FDAD1255A580300E217F9 /* OWSDynamicOutgoingMessage.m */, - C33FDB2E255A580A00E217F9 /* OWSEndSessionMessage.h */, - C33FDB27255A580A00E217F9 /* OWSEndSessionMessage.m */, C33FDBF9255A581C00E217F9 /* OWSError.h */, C33FDC0B255A581D00E217F9 /* OWSError.m */, C33FDA72255A57FA00E217F9 /* OWSFailedAttachmentDownloadsJob.h */, @@ -3689,10 +2985,6 @@ C33FDAB7255A580100E217F9 /* OWSFailedMessagesJob.m */, C33FDBAB255A581500E217F9 /* OWSFileSystem.h */, C33FDA8E255A57FD00E217F9 /* OWSFileSystem.m */, - C33FDBC8255A581700E217F9 /* OWSFingerprint.h */, - C33FDB05255A580700E217F9 /* OWSFingerprint.m */, - C33FDBCF255A581800E217F9 /* OWSFingerprintBuilder.h */, - C33FDAEE255A580500E217F9 /* OWSFingerprintBuilder.m */, C33FDABB255A580100E217F9 /* OWSGroupsOutputStream.h */, C33FDAD8255A580300E217F9 /* OWSGroupsOutputStream.m */, C33FDB52255A580D00E217F9 /* OWSHTTPSecurityPolicy.h */, @@ -3701,125 +2993,48 @@ C33FDBA9255A581500E217F9 /* OWSIdentityManager.m */, C33FDAC0255A580100E217F9 /* OWSIncomingMessageFinder.h */, C33FDB1E255A580900E217F9 /* OWSIncomingMessageFinder.m */, - C33FDAF6255A580600E217F9 /* OWSIncomingSentMessageTranscript.h */, - C33FDB84255A581100E217F9 /* OWSIncomingSentMessageTranscript.m */, C33FDB2A255A580A00E217F9 /* OWSIncompleteCallsJob.h */, C33FDBF7255A581C00E217F9 /* OWSIncompleteCallsJob.m */, - C33FDAA6255A57FF00E217F9 /* OWSLinkedDeviceReadReceipt.h */, - C33FDA9A255A57FE00E217F9 /* OWSLinkedDeviceReadReceipt.m */, C33FDBA8255A581500E217F9 /* OWSLinkPreview.swift */, - C33FDB14255A580800E217F9 /* OWSMath.h */, C33FDB67255A580F00E217F9 /* OWSMediaGalleryFinder.h */, C33FDB71255A581000E217F9 /* OWSMediaGalleryFinder.m */, - C33FDB22255A580900E217F9 /* OWSMediaUtils.swift */, C33FDBCE255A581800E217F9 /* OWSMessageServiceParams.h */, C33FDB70255A580F00E217F9 /* OWSMessageServiceParams.m */, C33FDAE8255A580500E217F9 /* OWSMessageUtils.h */, C33FDBD7255A581900E217F9 /* OWSMessageUtils.m */, C33FDBA1255A581400E217F9 /* OWSOperation.h */, C33FDB78255A581000E217F9 /* OWSOperation.m */, - C33FDA66255A57F900E217F9 /* OWSOutgoingCallMessage.h */, - C33FDB28255A580A00E217F9 /* OWSOutgoingCallMessage.m */, - C33FDBB3255A581500E217F9 /* OWSOutgoingNullMessage.h */, - C33FDBE6255A581A00E217F9 /* OWSOutgoingNullMessage.m */, C33FDABD255A580100E217F9 /* OWSOutgoingReceiptManager.h */, C33FDB6F255A580F00E217F9 /* OWSOutgoingReceiptManager.m */, - C33FDB24255A580900E217F9 /* OWSOutgoingSentMessageTranscript.h */, - C33FDBD5255A581900E217F9 /* OWSOutgoingSentMessageTranscript.m */, - C33FDBE4255A581A00E217F9 /* OWSOutgoingSyncMessage.h */, - C33FDBFF255A581C00E217F9 /* OWSOutgoingSyncMessage.m */, - C33FDA67255A57F900E217F9 /* OWSPrimaryStorage.h */, - C33FDC02255A581D00E217F9 /* OWSPrimaryStorage.m */, - C33FDB1A255A580900E217F9 /* OWSPrimaryStorage+Calling.h */, - C33FDBFD255A581C00E217F9 /* OWSPrimaryStorage+Calling.m */, - C33FDBBA255A581600E217F9 /* OWSPrimaryStorage+keyFromIntLong.h */, - C33FDB99255A581300E217F9 /* OWSPrimaryStorage+keyFromIntLong.m */, - C33FDBBB255A581600E217F9 /* OWSPrimaryStorage+Loki.h */, - C33FDB58255A580E00E217F9 /* OWSPrimaryStorage+Loki.m */, - C33FDA85255A57FC00E217F9 /* OWSPrimaryStorage+Loki.swift */, - C33FDB47255A580C00E217F9 /* OWSPrimaryStorage+PreKeyStore.h */, - C33FDB50255A580D00E217F9 /* OWSPrimaryStorage+PreKeyStore.m */, - C33FDB03255A580700E217F9 /* OWSPrimaryStorage+SessionStore.h */, - C33FDAF3255A580500E217F9 /* OWSPrimaryStorage+SessionStore.m */, - C33FDAA7255A57FF00E217F9 /* OWSPrimaryStorage+SignedPreKeyStore.h */, - C33FDAF7255A580600E217F9 /* OWSPrimaryStorage+SignedPreKeyStore.m */, - C33FDB08255A580700E217F9 /* OWSProfileKeyMessage.h */, - C33FDB3D255A580B00E217F9 /* OWSProfileKeyMessage.m */, C33FDB62255A580E00E217F9 /* OWSProvisioningCipher.h */, C33FDB2B255A580A00E217F9 /* OWSProvisioningCipher.m */, - C33FDC0D255A581E00E217F9 /* OWSProvisioningMessage.h */, - C33FDA9B255A57FE00E217F9 /* OWSProvisioningMessage.m */, C33FDC19255A581F00E217F9 /* OWSQueues.h */, C33FDB1D255A580900E217F9 /* OWSReadReceiptManager.h */, C33FDA71255A57FA00E217F9 /* OWSReadReceiptManager.m */, - C33FDAE2255A580400E217F9 /* OWSReadReceiptsForLinkedDevicesMessage.h */, - C33FDB96255A581300E217F9 /* OWSReadReceiptsForLinkedDevicesMessage.m */, C33FDAE1255A580400E217F9 /* OWSReadTracking.h */, - C33FDA8D255A57FD00E217F9 /* OWSReceiptsForSenderMessage.h */, - C33FDAAE255A580000E217F9 /* OWSReceiptsForSenderMessage.m */, C33FDAA0255A57FF00E217F9 /* OWSRecipientIdentity.h */, C33FDBEC255A581B00E217F9 /* OWSRecipientIdentity.m */, C33FDBAD255A581500E217F9 /* OWSRecordTranscriptJob.h */, C33FDA7F255A57FC00E217F9 /* OWSRecordTranscriptJob.m */, - C38EEFD5255B5BA2007E1867 /* OldSnodeAPI.swift */, - C33FDB06255A580700E217F9 /* OWSRequestBuilder.h */, - C33FDB00255A580600E217F9 /* OWSRequestBuilder.m */, - C33FDBC0255A581700E217F9 /* OWSRequestFactory.h */, - C33FDB86255A581100E217F9 /* OWSRequestFactory.m */, - C33FDAA5255A57FF00E217F9 /* OWSRequestMaker.swift */, C33FDBD3255A581800E217F9 /* OWSSignalAddress.swift */, - C33FDAC7255A580200E217F9 /* OWSSignalService.h */, - C33FDB18255A580800E217F9 /* OWSSignalService.m */, - C33FDAFE255A580600E217F9 /* OWSStorage.h */, - C33FDAB1255A580000E217F9 /* OWSStorage.m */, C33FDAB9255A580100E217F9 /* OWSStorage+Subclass.h */, - C33FDB65255A580F00E217F9 /* OWSSyncConfigurationMessage.h */, - C33FDC1D255A581F00E217F9 /* OWSSyncConfigurationMessage.m */, - C33FDB57255A580D00E217F9 /* OWSSyncContactsMessage.h */, - C33FDA9C255A57FE00E217F9 /* OWSSyncContactsMessage.m */, - C33FDBB5255A581600E217F9 /* OWSSyncGroupsMessage.h */, - C33FDC00255A581C00E217F9 /* OWSSyncGroupsMessage.m */, - C33FDAF8255A580600E217F9 /* OWSSyncGroupsRequestMessage.h */, - C33FDA76255A57FB00E217F9 /* OWSSyncGroupsRequestMessage.m */, C33FDA75255A57FB00E217F9 /* OWSSyncManagerProtocol.h */, C33FDAF1255A580500E217F9 /* OWSThumbnailService.swift */, C33FDB5A255A580E00E217F9 /* OWSUDManager.swift */, - C33FDBD2255A581800E217F9 /* OWSUnknownContactBlockOfferMessage.h */, - C33FDBE2255A581A00E217F9 /* OWSUnknownContactBlockOfferMessage.m */, C33FDBD6255A581900E217F9 /* OWSUploadOperation.h */, C33FDC1E255A581F00E217F9 /* OWSUploadOperation.m */, - C33FDB7C255A581000E217F9 /* OWSVerificationStateChangeMessage.h */, - C33FDB0B255A580700E217F9 /* OWSVerificationStateChangeMessage.m */, - C33FDBD9255A581900E217F9 /* OWSVerificationStateSyncMessage.h */, - C33FDACE255A580300E217F9 /* OWSVerificationStateSyncMessage.m */, - C33FDABA255A580100E217F9 /* OWSWebSocket.h */, - C33FDAAB255A580000E217F9 /* OWSWebSocket.m */, C33FDB8F255A581200E217F9 /* ParamParser.swift */, - C33FDBA2255A581400E217F9 /* PhoneNumber.h */, - C33FDA8A255A57FD00E217F9 /* PhoneNumber.m */, - C33FDBFA255A581C00E217F9 /* PhoneNumberUtil.h */, - C33FDB79255A581000E217F9 /* PhoneNumberUtil.m */, C33FDB3A255A580B00E217F9 /* Poller.swift */, C33FDB53255A580D00E217F9 /* PreKeyBundle+jsonDict.h */, C33FDB93255A581200E217F9 /* PreKeyBundle+jsonDict.m */, C33FDB64255A580E00E217F9 /* PreKeyRefreshOperation.swift */, - C33FDBB9255A581600E217F9 /* ProfileManagerProtocol.h */, - C33FDA83255A57FC00E217F9 /* Promise+retainUntilComplete.swift */, - C33FDC10255A581E00E217F9 /* ProofOfWork.swift */, - C33FDB91255A581200E217F9 /* ProtoUtils.h */, - C33FDA6C255A57FA00E217F9 /* ProtoUtils.m */, C33FDB33255A580B00E217F9 /* Provisioning.pb.swift */, C33FDAB0255A580000E217F9 /* ProvisioningProto.swift */, C33FDAF2255A580500E217F9 /* ProxiedContentDownloader.swift */, C33FDADF255A580400E217F9 /* PublicChatManager.swift */, C33FDA8C255A57FD00E217F9 /* PublicChatPoller.swift */, C33FDA6F255A57FA00E217F9 /* ReachabilityManager.swift */, - C33FDA9E255A57FF00E217F9 /* ReverseDispatchQueue.swift */, C33FDA98255A57FE00E217F9 /* RotateSignedKeyOperation.swift */, - C33FDBA3255A581400E217F9 /* SessionManagementProtocol.swift */, - C33FDAFB255A580600E217F9 /* SessionMetaProtocol.swift */, - C33FDBEA255A581A00E217F9 /* SessionRequestMessage.swift */, - C33FDC17255A581F00E217F9 /* SharedSenderKeysImplementation.swift */, C33FDBAE255A581500E217F9 /* SignalAccount.h */, C33FDC06255A581D00E217F9 /* SignalAccount.m */, C33FDBD8255A581900E217F9 /* SignalIOS.pb.swift */, @@ -3828,102 +3043,36 @@ C33FDAEC255A580500E217F9 /* SignalRecipient.h */, C33FDBB7255A581600E217F9 /* SignalRecipient.m */, C33FDC08255A581D00E217F9 /* SignalService.pb.swift */, - C33FDB4A255A580C00E217F9 /* SignalServiceClient.swift */, C33FDBA5255A581400E217F9 /* SignalServiceProfile.swift */, - C33FDBC2255A581700E217F9 /* SSKAsserts.h */, C33FDB31255A580A00E217F9 /* SSKEnvironment.h */, C33FDAF4255A580600E217F9 /* SSKEnvironment.m */, C33FDB32255A580A00E217F9 /* SSKIncrementingIdFinder.swift */, C33FDB4F255A580D00E217F9 /* SSKJobRecord.h */, C33FDACD255A580200E217F9 /* SSKJobRecord.m */, - C33FDBBC255A581600E217F9 /* SSKKeychainStorage.swift */, C33FDAED255A580500E217F9 /* SSKMessageSenderJobRecord.h */, C33FDAE9255A580500E217F9 /* SSKMessageSenderJobRecord.m */, C33FDA69255A57F900E217F9 /* SSKPreferences.swift */, C33FDAA4255A57FF00E217F9 /* SSKProto.swift */, - C33FDB6E255A580F00E217F9 /* SSKProtoPrekeyBundleMessage+Loki.swift */, - C33FDA78255A57FB00E217F9 /* SSKWebSocket.swift */, - C33FDB36255A580B00E217F9 /* Storage.swift */, - C33FDC07255A581D00E217F9 /* Storage+ClosedGroups.swift */, - C33FDB95255A581300E217F9 /* Storage+Collections.swift */, - C33FDBE8255A581A00E217F9 /* Storage+OnionRequests.swift */, - C33FDBFB255A581C00E217F9 /* Storage+PublicChats.swift */, - C33FDB8B255A581200E217F9 /* Storage+SessionManagement.swift */, - C33FDB37255A580B00E217F9 /* Storage+SnodeAPI.swift */, - C33FDB3F255A580C00E217F9 /* String+SSK.swift */, - C33FDAAF255A580000E217F9 /* String+Trimming.swift */, C33FDADE255A580400E217F9 /* SwiftSingletons.swift */, - C33FDAB6255A580100E217F9 /* SyncMessagesProtocol.swift */, C33FDB94255A581300E217F9 /* TSAccountManager.h */, C33FDB88255A581200E217F9 /* TSAccountManager.m */, - C33FDC15255A581E00E217F9 /* TSAttachment.h */, - C33FDAC2255A580200E217F9 /* TSAttachment.m */, - C33FDC18255A581F00E217F9 /* TSAttachmentPointer.h */, - C33FDB9E255A581400E217F9 /* TSAttachmentPointer.m */, - C33FDAE4255A580400E217F9 /* TSAttachmentStream.h */, - C33FDAC4255A580200E217F9 /* TSAttachmentStream.m */, C33FDB02255A580700E217F9 /* TSCall.h */, C33FDC1C255A581F00E217F9 /* TSCall.m */, C33FDC12255A581E00E217F9 /* TSConstants.h */, C33FDABE255A580100E217F9 /* TSConstants.m */, - C33FDAB3255A580000E217F9 /* TSContactThread.h */, - C33FDAF9255A580600E217F9 /* TSContactThread.m */, C33FDB25255A580900E217F9 /* TSDatabaseSecondaryIndexes.h */, C33FDB20255A580900E217F9 /* TSDatabaseSecondaryIndexes.m */, C33FDB2C255A580A00E217F9 /* TSDatabaseView.h */, C33FDB46255A580C00E217F9 /* TSDatabaseView.m */, - C33FDB5D255A580E00E217F9 /* TSErrorMessage_privateConstructor.h */, - C33FDBB0255A581500E217F9 /* TSErrorMessage.h */, - C33FDAE7255A580500E217F9 /* TSErrorMessage.m */, - C33FDB0A255A580700E217F9 /* TSGroupModel.h */, - C33FDB73255A581000E217F9 /* TSGroupModel.m */, - C33FDA79255A57FB00E217F9 /* TSGroupThread.h */, - C33FDC01255A581C00E217F9 /* TSGroupThread.m */, - C33FDB9C255A581300E217F9 /* TSIncomingMessage.h */, - C33FDA97255A57FE00E217F9 /* TSIncomingMessage.m */, - C33FDADD255A580400E217F9 /* TSInfoMessage.h */, - C33FDC0C255A581E00E217F9 /* TSInfoMessage.m */, - C33FDAE6255A580400E217F9 /* TSInteraction.h */, - C33FDBE9255A581A00E217F9 /* TSInteraction.m */, - C33FDB8C255A581200E217F9 /* TSInvalidIdentityKeyErrorMessage.h */, - C33FDB74255A581000E217F9 /* TSInvalidIdentityKeyErrorMessage.m */, - C33FDA7B255A57FB00E217F9 /* TSInvalidIdentityKeyReceivingErrorMessage.h */, - C33FDBCB255A581800E217F9 /* TSInvalidIdentityKeyReceivingErrorMessage.m */, - C33FDBA6255A581400E217F9 /* TSInvalidIdentityKeySendingErrorMessage.h */, - C33FDB55255A580D00E217F9 /* TSInvalidIdentityKeySendingErrorMessage.m */, - C33FDA70255A57FA00E217F9 /* TSMessage.h */, - C33FDB60255A580E00E217F9 /* TSMessage.m */, - C33FDAB5255A580000E217F9 /* TSNetworkManager.h */, - C33FDAB2255A580000E217F9 /* TSNetworkManager.m */, - C33FDB48255A580C00E217F9 /* TSOutgoingMessage.h */, - C33FDB56255A580D00E217F9 /* TSOutgoingMessage.m */, C33FDB6D255A580F00E217F9 /* TSPreKeyManager.h */, C33FDB7E255A581100E217F9 /* TSPreKeyManager.m */, - C33FDAD5255A580300E217F9 /* TSQuotedMessage.h */, - C33FDB83255A581100E217F9 /* TSQuotedMessage.m */, - C33FDBB1255A581500E217F9 /* TSSocketManager.h */, - C33FDC11255A581E00E217F9 /* TSSocketManager.m */, C33FDBA0255A581400E217F9 /* TSStorageHeaders.h */, C33FDBEF255A581B00E217F9 /* TSStorageKeys.h */, - C33FDAD3255A580300E217F9 /* TSThread.h */, - C33FDBB8255A581600E217F9 /* TSThread.m */, - C33FDAA1255A57FF00E217F9 /* TSYapDatabaseObject.h */, - C33FDA90255A57FD00E217F9 /* TSYapDatabaseObject.m */, - C33FDB9F255A581400E217F9 /* TTLUtilities.swift */, - C33FDBC4255A581700E217F9 /* TypingIndicatorMessage.swift */, C33FDA87255A57FC00E217F9 /* TypingIndicators.swift */, - C33FDB1C255A580900E217F9 /* UIImage+OWS.h */, - C33FDB81255A581100E217F9 /* UIImage+OWS.m */, - C33FDB49255A580C00E217F9 /* WeakTimer.swift */, C37F53E8255BA9BB002AEA92 /* Environment.h */, C37F5402255BA9ED002AEA92 /* Environment.m */, - C33FDA6D255A57FA00E217F9 /* YapDatabase+Promise.swift */, - C33FDB5F255A580E00E217F9 /* YapDatabaseConnection+OWS.h */, - C33FDB43255A580C00E217F9 /* YapDatabaseConnection+OWS.m */, - C33FDA88255A57FD00E217F9 /* YapDatabaseTransaction+OWS.h */, - C33FDB5B255A580E00E217F9 /* YapDatabaseTransaction+OWS.m */, - C38EEF09255B49A8007E1867 /* SSKProtoEnvelope+Conversion.swift */, - C33FD9B7255A54A300E217F9 /* Meta */, + B8C2B33B2563770800551B4D /* ThreadUtil.h */, + B8C2B331256376F000551B4D /* ThreadUtil.m */, ); path = SignalUtilitiesKit; sourceTree = ""; @@ -3962,6 +3111,230 @@ path = CSV; sourceTree = ""; }; + C3851CD225624B060061EEB0 /* UI */ = { + isa = PBXGroup; + children = ( + B8C2B2C72563685C00551B4D /* CircleView.swift */, + C38EF23D255B6D66007E1867 /* UIView+OWS.h */, + C38EF23E255B6D66007E1867 /* UIView+OWS.m */, + C38EF240255B6D67007E1867 /* UIView+OWS.swift */, + C38EF236255B6D65007E1867 /* UIViewController+OWS.h */, + C38EF23B255B6D66007E1867 /* UIViewController+OWS.m */, + C38EF23C255B6D66007E1867 /* UIColor+OWS.h */, + C38EF242255B6D67007E1867 /* UIColor+OWS.m */, + C38EF2A2255B6D93007E1867 /* Identicon+ObjC.swift */, + C38EF37D255B6DCF007E1867 /* AttachmentApprovalInputAccessoryView.swift */, + C38EF37F255B6DD0007E1867 /* AttachmentApprovalViewController.swift */, + C38EF381255B6DD1007E1867 /* AttachmentCaptionToolbar.swift */, + C38EF384255B6DD2007E1867 /* AttachmentCaptionViewController.swift */, + C38EF37E255B6DD0007E1867 /* AttachmentItemCollection.swift */, + C38EF382255B6DD1007E1867 /* AttachmentPrepViewController.swift */, + C38EF37C255B6DCF007E1867 /* AttachmentTextToolbar.swift */, + C38EF380255B6DD0007E1867 /* AttachmentTextView.swift */, + C38EF358255B6DCC007E1867 /* MediaMessageView.swift */, + C38EF357255B6DCC007E1867 /* MessageApprovalViewController.swift */, + C38EF349255B6DC7007E1867 /* ModalActivityIndicatorViewController.swift */, + C38EF343255B6DC5007E1867 /* OWSNavigationController.h */, + C38EF356255B6DCB007E1867 /* OWSNavigationController.m */, + C38EF34D255B6DC8007E1867 /* OWSTableViewController.h */, + C38EF34B255B6DC8007E1867 /* OWSTableViewController.m */, + C38EF344255B6DC5007E1867 /* OWSViewController.h */, + C38EF355255B6DCB007E1867 /* OWSViewController.m */, + C38EF34C255B6DC8007E1867 /* ScreenLockViewController.h */, + C38EF351255B6DC9007E1867 /* ScreenLockViewController.m */, + C38EF34E255B6DC8007E1867 /* SelectRecipientViewController.h */, + C38EF345255B6DC6007E1867 /* SelectRecipientViewController.m */, + C38EF341255B6DC5007E1867 /* SelectThreadViewController.h */, + C38EF342255B6DC5007E1867 /* SelectThreadViewController.m */, + C38EF353255B6DCB007E1867 /* SharingThreadPickerViewController.h */, + C38EF352255B6DC9007E1867 /* SharingThreadPickerViewController.m */, + C38EF33F255B6DC5007E1867 /* SheetViewController.swift */, + C38EF3B5255B6DE6007E1867 /* OWSViewController+ImageEditor.swift */, + C38EF383255B6DD1007E1867 /* ApprovalRailCellView.swift */, + C38EF3E8255B6DF6007E1867 /* OWSAlerts.swift */, + C38EF3E7255B6DF5007E1867 /* OWSButton.swift */, + C38EF3EC255B6DF6007E1867 /* OWSFlatButton.swift */, + C38EF3DB255B6DF1007E1867 /* OWSLayerView.swift */, + C38EF3D9255B6DF1007E1867 /* OWSNavigationBar.swift */, + C38EF3D3255B6DEE007E1867 /* OWSSearchBar.h */, + C38EF3EA255B6DF6007E1867 /* OWSSearchBar.m */, + C38EF3D7255B6DF0007E1867 /* OWSTextField.h */, + C38EF3E0255B6DF3007E1867 /* OWSTextField.m */, + C38EF3D8255B6DF0007E1867 /* OWSTextView.h */, + C38EF3DF255B6DF2007E1867 /* OWSTextView.m */, + C38EF3E9255B6DF6007E1867 /* Toast.swift */, + C38EF3B1255B6DE5007E1867 /* ImageEditorBrushViewController.swift */, + C38EF3B7255B6DE6007E1867 /* ImageEditorCanvasView.swift */, + C38EF3B4255B6DE6007E1867 /* ImageEditorContents.swift */, + C38EF3B0255B6DE5007E1867 /* ImageEditorCropViewController.swift */, + C38EF3AA255B6DE4007E1867 /* ImageEditorItem.swift */, + C38EF3B6255B6DE6007E1867 /* ImageEditorModel.swift */, + C38EF3B2255B6DE5007E1867 /* ImageEditorPaletteView.swift */, + C38EF3AC255B6DE4007E1867 /* ImageEditorPanGestureRecognizer.swift */, + C38EF3A9255B6DE4007E1867 /* ImageEditorPinchGestureRecognizer.swift */, + C38EF3AB255B6DE4007E1867 /* ImageEditorStrokeItem.swift */, + C38EF3B3255B6DE6007E1867 /* ImageEditorTextItem.swift */, + C38EF3A8255B6DE4007E1867 /* ImageEditorTextViewController.swift */, + C38EF3AD255B6DE4007E1867 /* ImageEditorTransform.swift */, + C38EF3AF255B6DE5007E1867 /* ImageEditorView.swift */, + C38EF302255B6DBE007E1867 /* OWSAnyTouchGestureRecognizer.h */, + C38EF2F0255B6DBB007E1867 /* OWSAnyTouchGestureRecognizer.m */, + C38EF303255B6DBE007E1867 /* BlockListUIUtils.h */, + C38EF2FD255B6DBD007E1867 /* BlockListUIUtils.m */, + C38EF2FC255B6DBD007E1867 /* ConversationStyle.swift */, + C38EF2ED255B6DBB007E1867 /* DisplayableText.swift */, + C38EF2A3255B6D93007E1867 /* PlaceholderIcon.swift */, + C38EF2B2255B6D9C007E1867 /* UIView+Utilities.swift */, + C38EF2B1255B6D9C007E1867 /* UIViewController+Utilities.swift */, + C38EF2A4255B6D93007E1867 /* ProfilePictureView.swift */, + C38EF212255B6D3A007E1867 /* Theme.h */, + C38EF214255B6D3A007E1867 /* Theme.m */, + C38EF307255B6DBE007E1867 /* UIGestureRecognizer+OWS.swift */, + C38EF2F3255B6DBC007E1867 /* UIImage+OWS.swift */, + C38EF30A255B6DBE007E1867 /* UIUtil.h */, + C38EF300255B6DBD007E1867 /* UIUtil.m */, + C38EF3DC255B6DF1007E1867 /* DirectionalPanGestureRecognizer.swift */, + C38EF3D4255B6DEE007E1867 /* DisappearingTimerConfigurationView.swift */, + C38EF3E2255B6DF3007E1867 /* GalleryRailView.swift */, + C38EF3EE255B6DF6007E1867 /* GradientView.swift */, + C38EF3E3255B6DF4007E1867 /* VideoPlayerView.swift */, + C38EF3ED255B6DF6007E1867 /* TappableStackView.swift */, + C38EF3E1255B6DF3007E1867 /* TappableView.swift */, + C38EF2FB255B6DBD007E1867 /* OWSWindowManager.h */, + C38EF306255B6DBE007E1867 /* OWSWindowManager.m */, + C38EF239255B6D66007E1867 /* UIFont+OWS.h */, + C38EF238255B6D66007E1867 /* UIFont+OWS.m */, + ); + path = UI; + sourceTree = ""; + }; + C3851CE3256250FA0061EEB0 /* Remove Later */ = { + isa = PBXGroup; + children = ( + C33FDBB9255A581600E217F9 /* ProfileManagerProtocol.h */, + C33FDBA3255A581400E217F9 /* SessionManagementProtocol.swift */, + C33FDAFB255A580600E217F9 /* SessionMetaProtocol.swift */, + C38EF3E5255B6DF4007E1867 /* ContactCellView.h */, + C38EF3D6255B6DEF007E1867 /* ContactCellView.m */, + C38EF3E6255B6DF4007E1867 /* ContactTableViewCell.h */, + C38EF3EB255B6DF6007E1867 /* ContactTableViewCell.m */, + C38EF2D2255B6DAF007E1867 /* OWSProfileManager.h */, + C38EF2CF255B6DAE007E1867 /* OWSProfileManager.m */, + C38EF2D3255B6DAF007E1867 /* OWSUserProfile.h */, + C38EF2D1255B6DAF007E1867 /* OWSUserProfile.m */, + ); + path = "Remove Later"; + sourceTree = ""; + }; + C38BBA0B255E31EC0041B9A3 /* Attachments */ = { + isa = PBXGroup; + children = ( + C38EF224255B6D5D007E1867 /* SignalAttachment.swift */, + C33FDC15255A581E00E217F9 /* TSAttachment.h */, + C33FDAC2255A580200E217F9 /* TSAttachment.m */, + C33FDC18255A581F00E217F9 /* TSAttachmentPointer.h */, + C33FDB9E255A581400E217F9 /* TSAttachmentPointer.m */, + C33FDAE4255A580400E217F9 /* TSAttachmentStream.h */, + C33FDAC4255A580200E217F9 /* TSAttachmentStream.m */, + ); + path = Attachments; + sourceTree = ""; + }; + C38BBA0C255E32020041B9A3 /* Threads */ = { + isa = PBXGroup; + children = ( + C33FDAB3255A580000E217F9 /* TSContactThread.h */, + C33FDAF9255A580600E217F9 /* TSContactThread.m */, + C33FDB0A255A580700E217F9 /* TSGroupModel.h */, + C33FDB73255A581000E217F9 /* TSGroupModel.m */, + C33FDA79255A57FB00E217F9 /* TSGroupThread.h */, + C33FDC01255A581C00E217F9 /* TSGroupThread.m */, + C33FDAD3255A580300E217F9 /* TSThread.h */, + C33FDBB8255A581600E217F9 /* TSThread.m */, + ); + path = Threads; + sourceTree = ""; + }; + C38BBA0D255E321C0041B9A3 /* Messages */ = { + isa = PBXGroup; + children = ( + C33FDB5D255A580E00E217F9 /* TSErrorMessage_privateConstructor.h */, + C33FDBB0255A581500E217F9 /* TSErrorMessage.h */, + C33FDAE7255A580500E217F9 /* TSErrorMessage.m */, + C33FDB9C255A581300E217F9 /* TSIncomingMessage.h */, + C33FDA97255A57FE00E217F9 /* TSIncomingMessage.m */, + C33FDADD255A580400E217F9 /* TSInfoMessage.h */, + C33FDC0C255A581E00E217F9 /* TSInfoMessage.m */, + C33FDAE6255A580400E217F9 /* TSInteraction.h */, + C33FDBE9255A581A00E217F9 /* TSInteraction.m */, + C33FDB8C255A581200E217F9 /* TSInvalidIdentityKeyErrorMessage.h */, + C33FDB74255A581000E217F9 /* TSInvalidIdentityKeyErrorMessage.m */, + C33FDA7B255A57FB00E217F9 /* TSInvalidIdentityKeyReceivingErrorMessage.h */, + C33FDBCB255A581800E217F9 /* TSInvalidIdentityKeyReceivingErrorMessage.m */, + C33FDBA6255A581400E217F9 /* TSInvalidIdentityKeySendingErrorMessage.h */, + C33FDB55255A580D00E217F9 /* TSInvalidIdentityKeySendingErrorMessage.m */, + C33FDA70255A57FA00E217F9 /* TSMessage.h */, + C33FDB60255A580E00E217F9 /* TSMessage.m */, + C33FDB48255A580C00E217F9 /* TSOutgoingMessage.h */, + C33FDB56255A580D00E217F9 /* TSOutgoingMessage.m */, + C33FDAD5255A580300E217F9 /* TSQuotedMessage.h */, + C33FDB83255A581100E217F9 /* TSQuotedMessage.m */, + ); + path = Messages; + sourceTree = ""; + }; + C38BBA0E255E32440041B9A3 /* Database */ = { + isa = PBXGroup; + children = ( + C33FDBBC255A581600E217F9 /* SSKKeychainStorage.swift */, + C38EF271255B6D79007E1867 /* OWSDatabaseMigration.h */, + C38EF270255B6D79007E1867 /* OWSDatabaseMigration.m */, + C38EF26F255B6D79007E1867 /* OWSDatabaseMigrationRunner.h */, + C38EF26D255B6D79007E1867 /* OWSDatabaseMigrationRunner.m */, + C38EF26E255B6D79007E1867 /* OWSResaveCollectionDBMigration.h */, + C38EF26C255B6D79007E1867 /* OWSResaveCollectionDBMigration.m */, + C33FDA67255A57F900E217F9 /* OWSPrimaryStorage.h */, + C33FDC02255A581D00E217F9 /* OWSPrimaryStorage.m */, + C33FDB1A255A580900E217F9 /* OWSPrimaryStorage+Calling.h */, + C33FDBFD255A581C00E217F9 /* OWSPrimaryStorage+Calling.m */, + C33FDBBA255A581600E217F9 /* OWSPrimaryStorage+keyFromIntLong.h */, + C33FDB99255A581300E217F9 /* OWSPrimaryStorage+keyFromIntLong.m */, + C33FDB47255A580C00E217F9 /* OWSPrimaryStorage+PreKeyStore.h */, + C33FDB50255A580D00E217F9 /* OWSPrimaryStorage+PreKeyStore.m */, + C33FDB03255A580700E217F9 /* OWSPrimaryStorage+SessionStore.h */, + C33FDAF3255A580500E217F9 /* OWSPrimaryStorage+SessionStore.m */, + C33FDAA7255A57FF00E217F9 /* OWSPrimaryStorage+SignedPreKeyStore.h */, + C33FDAF7255A580600E217F9 /* OWSPrimaryStorage+SignedPreKeyStore.m */, + C33FDAFE255A580600E217F9 /* OWSStorage.h */, + C33FDAB1255A580000E217F9 /* OWSStorage.m */, + C33FDAA1255A57FF00E217F9 /* TSYapDatabaseObject.h */, + C33FDA90255A57FD00E217F9 /* TSYapDatabaseObject.m */, + C33FDA6D255A57FA00E217F9 /* YapDatabase+Promise.swift */, + C33FDB5F255A580E00E217F9 /* YapDatabaseConnection+OWS.h */, + C33FDB43255A580C00E217F9 /* YapDatabaseConnection+OWS.m */, + C33FDA88255A57FD00E217F9 /* YapDatabaseTransaction+OWS.h */, + C33FDB5B255A580E00E217F9 /* YapDatabaseTransaction+OWS.m */, + ); + path = Database; + sourceTree = ""; + }; + C38BBA17255E327A0041B9A3 /* Move to main app */ = { + isa = PBXGroup; + children = ( + C33FDBBB255A581600E217F9 /* OWSPrimaryStorage+Loki.h */, + C33FDB58255A580E00E217F9 /* OWSPrimaryStorage+Loki.m */, + C33FDA85255A57FC00E217F9 /* OWSPrimaryStorage+Loki.swift */, + C33FDB36255A580B00E217F9 /* Storage.swift */, + C33FDC07255A581D00E217F9 /* Storage+ClosedGroups.swift */, + C33FDB95255A581300E217F9 /* Storage+Collections.swift */, + C33FDBE8255A581A00E217F9 /* Storage+OnionRequests.swift */, + C33FDBFB255A581C00E217F9 /* Storage+PublicChats.swift */, + C33FDB8B255A581200E217F9 /* Storage+SessionManagement.swift */, + C33FDB37255A580B00E217F9 /* Storage+SnodeAPI.swift */, + ); + path = "Move to main app"; + sourceTree = ""; + }; C396DAE72518407300FF6DC5 /* SwiftCSV */ = { isa = PBXGroup; children = ( @@ -4075,7 +3448,6 @@ C3C2A68B255388D500C340D1 /* Meta */, C3C2A5D72553860B00C340D1 /* AESGCM.swift */, C3A721992558C1660043A11F /* AnyPromise+Conversion.swift */, - C3A7225D2558C38D0043A11F /* AnyPromise+Retaining.swift */, C3C2A5D12553860800C340D1 /* Array+Description.swift */, C3C2ABD12553C6C900C340D1 /* Data+SecureRandom.swift */, C3C2A5D52553860A00C340D1 /* Dictionary+Description.swift */, @@ -4089,6 +3461,7 @@ C352A3762557859C00338F3E /* NSTimer+Proxying.h */, C352A36C2557858D00338F3E /* NSTimer+Proxying.m */, C3C2A5D32553860900C340D1 /* Promise+Delaying.swift */, + C3A7225D2558C38D0043A11F /* Promise+Retaining.swift */, C3C2A5D62553860B00C340D1 /* Promise+Retrying.swift */, C3C2AC2D2553CBEB00C340D1 /* String+Trimming.swift */, C352A3A42557B5F000338F3E /* TSRequest.h */, @@ -4332,8 +3705,64 @@ C3CA3B11255CF17200F4C6D4 /* Utilities */ = { isa = PBXGroup; children = ( + C33FDB1C255A580900E217F9 /* UIImage+OWS.h */, + C33FDB81255A581100E217F9 /* UIImage+OWS.m */, + C33FDB49255A580C00E217F9 /* WeakTimer.swift */, + C33FDB3F255A580C00E217F9 /* String+SSK.swift */, + C33FDAAF255A580000E217F9 /* String+Trimming.swift */, + C33FDBC2255A581700E217F9 /* SSKAsserts.h */, + C33FDA9E255A57FF00E217F9 /* ReverseDispatchQueue.swift */, + C33FDB91255A581200E217F9 /* ProtoUtils.h */, + C33FDA6C255A57FA00E217F9 /* ProtoUtils.m */, + C33FDB22255A580900E217F9 /* OWSMediaUtils.swift */, + C33FDB14255A580800E217F9 /* OWSMath.h */, + C33FDADC255A580400E217F9 /* NSObject+Casting.h */, + C33FDAAA255A580000E217F9 /* NSObject+Casting.m */, + C33FDA7A255A57FB00E217F9 /* NSRegularExpression+SSK.swift */, + C33FDBFE255A581C00E217F9 /* NSSet+Functional.h */, + C33FDAC1255A580100E217F9 /* NSSet+Functional.m */, + C33FDB12255A580800E217F9 /* NSString+SSK.h */, + C33FDB45255A580C00E217F9 /* NSString+SSK.m */, + C33FDBF6255A581C00E217F9 /* NSURLSessionDataTask+StatusCode.h */, + C33FDBB4255A581600E217F9 /* NSURLSessionDataTask+StatusCode.m */, + C33FDB51255A580D00E217F9 /* NSUserDefaults+OWS.h */, + C33FDB77255A581000E217F9 /* NSUserDefaults+OWS.m */, + C33FDB5C255A580E00E217F9 /* NSArray+Functional.h */, + C33FDAB8255A580100E217F9 /* NSArray+Functional.m */, + C33FDBF8255A581C00E217F9 /* NSArray+OWS.h */, + C33FDB0D255A580800E217F9 /* NSArray+OWS.m */, + C33FDB29255A580A00E217F9 /* NSData+Image.h */, + C33FDAEF255A580500E217F9 /* NSData+Image.m */, + C33FDAFC255A580600E217F9 /* MIMETypeUtil.h */, + C33FDB41255A580C00E217F9 /* MIMETypeUtil.m */, + C33FDAFD255A580600E217F9 /* LRUCache.swift */, + C33FDA7C255A57FB00E217F9 /* Debugging.swift */, + C33FDC03255A581D00E217F9 /* ByteParser.h */, + C33FDAE0255A580400E217F9 /* ByteParser.m */, + C33FDAA8255A57FF00E217F9 /* BuildConfiguration.swift */, + C38EF3DD255B6DF1007E1867 /* UIAlertController+OWS.swift */, + C38EF225255B6D5D007E1867 /* AttachmentSharing.h */, + C38EF223255B6D5D007E1867 /* AttachmentSharing.m */, + C38EF241255B6D67007E1867 /* Collection+OWS.swift */, + C38EF2F8255B6DBC007E1867 /* DebugLogger.h */, + C38EF2E6255B6DBA007E1867 /* DebugLogger.m */, C3CA3B12255CF18200F4C6D4 /* Destination+Conversion.swift */, C3CA3B1C255CF3C800F4C6D4 /* MessageSender+Utilities.swift */, + C38EF3AE255B6DE5007E1867 /* OrderedDictionary.swift */, + C38EF2E7255B6DBA007E1867 /* OWSScrubbingLogFormatter.h */, + C38EF2F6255B6DBC007E1867 /* OWSScrubbingLogFormatter.m */, + C38EF2F5255B6DBC007E1867 /* OWSAudioPlayer.h */, + C38EF2F7255B6DBC007E1867 /* OWSAudioPlayer.m */, + C38EF301255B6DBD007E1867 /* OWSFormat.h */, + C38EF305255B6DBE007E1867 /* OWSFormat.m */, + C38EF281255B6D84007E1867 /* OWSAudioSession.swift */, + C38EF226255B6D5D007E1867 /* ShareViewDelegate.swift */, + C38EEF09255B49A8007E1867 /* SNProtoEnvelope+Conversion.swift */, + C38EF227255B6D5D007E1867 /* OWSVideoPlayer.swift */, + C38EF2EF255B6DBB007E1867 /* Weak.swift */, + C38EF2FA255B6DBD007E1867 /* Bench.swift */, + C38EF23A255B6D66007E1867 /* NSAttributedString+OWS.m */, + C38EF237255B6D65007E1867 /* UIDevice+featureSupport.swift */, ); path = Utilities; sourceTree = ""; @@ -4480,35 +3909,27 @@ buildActionMask = 2147483647; files = ( C33FDD74255A582000E217F9 /* OWSPrimaryStorage+keyFromIntLong.h in Headers */, - C33FDCDB255A582000E217F9 /* OWS2FAManager.h in Headers */, C33FDDB0255A582000E217F9 /* NSURLSessionDataTask+StatusCode.h in Headers */, - C33FDD6B255A582000E217F9 /* TSSocketManager.h in Headers */, C33FDCCE255A582000E217F9 /* OWSMath.h in Headers */, - C33FDC47255A581F00E217F9 /* OWSReceiptsForSenderMessage.h in Headers */, C33FDD46255A582000E217F9 /* TSInvalidIdentityKeyErrorMessage.h in Headers */, C33FDDD0255A582000E217F9 /* FunctionalUtil.h in Headers */, C33FDCE3255A582000E217F9 /* NSData+Image.h in Headers */, - C33FDC5D255A582000E217F9 /* OWSAddToContactsOfferMessage.h in Headers */, C38EF311255B6DBF007E1867 /* OWSScrubbingLogFormatter.h in Headers */, C33FDC77255A582000E217F9 /* OWSOutgoingReceiptManager.h in Headers */, + B8C2B3442563782400551B4D /* ThreadUtil.h in Headers */, C33FDCBB255A582000E217F9 /* AppReadiness.h in Headers */, C38EF334255B6DBF007E1867 /* UIUtil.h in Headers */, - C33FDD82255A582000E217F9 /* OWSFingerprint.h in Headers */, C38EF32D255B6DBF007E1867 /* BlockListUIUtils.h in Headers */, C33FDD5B255A582000E217F9 /* OWSOperation.h in Headers */, C38EF291255B6D86007E1867 /* SignalKeyingStorage.h in Headers */, - C33FDD6F255A582000E217F9 /* OWSSyncGroupsMessage.h in Headers */, C33FDC89255A582000E217F9 /* OWSAttachmentDownloads.h in Headers */, C33FDD73255A582000E217F9 /* ProfileManagerProtocol.h in Headers */, - C33FDD11255A582000E217F9 /* OWSSyncContactsMessage.h in Headers */, C38EF2D8255B6DAF007E1867 /* OWSUserProfile.h in Headers */, C33FDCB6255A582000E217F9 /* MIMETypeUtil.h in Headers */, - C33FDCAF255A582000E217F9 /* OWSAnalyticsEvents.h in Headers */, C33FDD17255A582000E217F9 /* TSErrorMessage_privateConstructor.h in Headers */, C33FDC93255A582000E217F9 /* OWSDisappearingMessagesConfiguration.h in Headers */, C38EF2C2255B6DA6007E1867 /* TSUnreadIndicatorInteraction.h in Headers */, C33FDCCC255A582000E217F9 /* NSString+SSK.h in Headers */, - C33FDCB0255A582000E217F9 /* OWSIncomingSentMessageTranscript.h in Headers */, C38EF313255B6DBF007E1867 /* OWSUnreadIndicator.h in Headers */, C33FDD7C255A582000E217F9 /* SSKAsserts.h in Headers */, C33FDC9B255A582000E217F9 /* OWSReadTracking.h in Headers */, @@ -4516,29 +3937,20 @@ C33FDC97255A582000E217F9 /* TSInfoMessage.h in Headers */, C33FDD65255A582000E217F9 /* OWSFileSystem.h in Headers */, C33FDCD6255A582000E217F9 /* UIImage+OWS.h in Headers */, - C38EF219255B6D3B007E1867 /* OWSConversationColor.h in Headers */, - C38EF369255B6DCC007E1867 /* ViewControllerUtils.h in Headers */, C33FDC6D255A582000E217F9 /* TSContactThread.h in Headers */, - C33FDD7A255A582000E217F9 /* OWSRequestFactory.h in Headers */, C33FDC3A255A581F00E217F9 /* OWSDisappearingMessagesJob.h in Headers */, C33FDD6A255A582000E217F9 /* TSErrorMessage.h in Headers */, C33FDDA9255A582000E217F9 /* TSStorageKeys.h in Headers */, C33FDC7A255A582000E217F9 /* OWSIncomingMessageFinder.h in Headers */, C38EF3F6255B6DF7007E1867 /* OWSTextView.h in Headers */, - C33FDC8C255A582000E217F9 /* Contact.h in Headers */, C33FDCB8255A582000E217F9 /* OWSStorage.h in Headers */, C38EF24C255B6D67007E1867 /* NSAttributedString+OWS.h in Headers */, C38EF32B255B6DBF007E1867 /* OWSFormat.h in Headers */, C33FDD4E255A582000E217F9 /* TSAccountManager.h in Headers */, - C33FDD77255A582000E217F9 /* OWSAddToProfileWhitelistOfferMessage.h in Headers */, C33FDDAB255A582000E217F9 /* OWSIdentityManager.h in Headers */, - C33FDC82255A582000E217F9 /* OWSCensorshipConfiguration.h in Headers */, C33FDC2C255A581F00E217F9 /* OWSFailedAttachmentDownloadsJob.h in Headers */, - C33FDCD5255A582000E217F9 /* OWSDeviceProvisioningCodeService.h in Headers */, C38EF22A255B6D5D007E1867 /* AttachmentSharing.h in Headers */, - C33FDDB4255A582000E217F9 /* PhoneNumberUtil.h in Headers */, C33FDD67255A582000E217F9 /* OWSRecordTranscriptJob.h in Headers */, - C33FDCAA255A582000E217F9 /* ContactsUpdater.h in Headers */, C33FDDCF255A582000E217F9 /* TSAttachment.h in Headers */, C33FDDB8255A582000E217F9 /* NSSet+Functional.h in Headers */, C33FDC2F255A581F00E217F9 /* OWSSyncManagerProtocol.h in Headers */, @@ -4547,76 +3959,49 @@ C33FDDBD255A582000E217F9 /* ByteParser.h in Headers */, C33FDCBC255A582000E217F9 /* TSCall.h in Headers */, C38EF2C3255B6DA6007E1867 /* OWSContactOffersInteraction.h in Headers */, - C33FDD95255A582000E217F9 /* OWSDevicesService.h in Headers */, C38EF243255B6D67007E1867 /* UIViewController+OWS.h in Headers */, C33FDCD4255A582000E217F9 /* OWSPrimaryStorage+Calling.h in Headers */, C38EF36D255B6DCC007E1867 /* SharingThreadPickerViewController.h in Headers */, C33FDC5B255A582000E217F9 /* TSYapDatabaseObject.h in Headers */, - C33FDD8C255A582000E217F9 /* OWSUnknownContactBlockOfferMessage.h in Headers */, - C33FDD93255A582000E217F9 /* OWSVerificationStateSyncMessage.h in Headers */, C38EF35D255B6DCC007E1867 /* OWSNavigationController.h in Headers */, C33FDD88255A582000E217F9 /* OWSMessageServiceParams.h in Headers */, C33FDDBF255A582000E217F9 /* OWSDisappearingMessagesFinder.h in Headers */, C33FDCC4255A582000E217F9 /* TSGroupModel.h in Headers */, C38EF249255B6D67007E1867 /* UIColor+OWS.h in Headers */, - C33FDC6F255A582000E217F9 /* TSNetworkManager.h in Headers */, C33FDD4B255A582000E217F9 /* ProtoUtils.h in Headers */, C33FDD19255A582000E217F9 /* YapDatabaseConnection+OWS.h in Headers */, - C33FDD1F255A582000E217F9 /* OWSSyncConfigurationMessage.h in Headers */, C38EF216255B6D3B007E1867 /* Theme.h in Headers */, C38EF31B255B6DBF007E1867 /* OWSPreferences.h in Headers */, C33FDD0C255A582000E217F9 /* OWSHTTPSecurityPolicy.h in Headers */, C33FDC57255A582000E217F9 /* OWSContactsOutputStream.h in Headers */, - C38EF314255B6DBF007E1867 /* OWSAvatarBuilder.h in Headers */, C33FDCBD255A582000E217F9 /* OWSPrimaryStorage+SessionStore.h in Headers */, C33FDDD2255A582000E217F9 /* TSAttachmentPointer.h in Headers */, C38EF3F0255B6DF7007E1867 /* ThreadViewHelper.h in Headers */, - C33FDD1D255A582000E217F9 /* OWSDynamicOutgoingMessage.h in Headers */, C33FDCD7255A582000E217F9 /* OWSReadReceiptManager.h in Headers */, C33FDD21255A582000E217F9 /* OWSMediaGalleryFinder.h in Headers */, - C33FDCC6255A582000E217F9 /* CDSQuote.h in Headers */, C38EF274255B6D7A007E1867 /* OWSResaveCollectionDBMigration.h in Headers */, C33FDD02255A582000E217F9 /* TSOutgoingMessage.h in Headers */, C33FDC95255A582000E217F9 /* OWSFailedMessagesJob.h in Headers */, - C33FDD20255A582000E217F9 /* ContactDiscoveryService.h in Headers */, - C33FDD1B255A582000E217F9 /* LKUnlinkDeviceMessage.h in Headers */, C38EF3F1255B6DF7007E1867 /* OWSSearchBar.h in Headers */, C33FDD75255A582000E217F9 /* OWSPrimaryStorage+Loki.h in Headers */, - C33FDD8E255A582000E217F9 /* OWSBatchMessageProcessor.h in Headers */, C33FDC5A255A582000E217F9 /* OWSRecipientIdentity.h in Headers */, C33FDD1C255A582000E217F9 /* OWSProvisioningCipher.h in Headers */, C33FDC42255A581F00E217F9 /* YapDatabaseTransaction+OWS.h in Headers */, - C33FDD48255A582000E217F9 /* OWSContact+Private.h in Headers */, C33FDCA6255A582000E217F9 /* SignalRecipient.h in Headers */, - C33FDDA1255A582000E217F9 /* NSTimer+OWS.h in Headers */, C38EF277255B6D7A007E1867 /* OWSDatabaseMigration.h in Headers */, C38EF294255B6D86007E1867 /* OWSSounds.h in Headers */, C38EF3F5255B6DF7007E1867 /* OWSTextField.h in Headers */, - C33FDC74255A582000E217F9 /* OWSWebSocket.h in Headers */, C33FDC9E255A582000E217F9 /* TSAttachmentStream.h in Headers */, - C33FDC20255A581F00E217F9 /* OWSOutgoingCallMessage.h in Headers */, C38EF275255B6D7A007E1867 /* OWSDatabaseMigrationRunner.h in Headers */, - C38EF3F8255B6DF7007E1867 /* ContactsViewHelper.h in Headers */, - C33FDC83255A582000E217F9 /* OWSContact.h in Headers */, - C33FDCB2255A582000E217F9 /* OWSSyncGroupsRequestMessage.h in Headers */, C33FDCA7255A582000E217F9 /* SSKMessageSenderJobRecord.h in Headers */, - C38EF262255B6D6F007E1867 /* OWSContactsManager.h in Headers */, - C33FDCC2255A582000E217F9 /* OWSProfileKeyMessage.h in Headers */, - C33FDCDE255A582000E217F9 /* OWSOutgoingSentMessageTranscript.h in Headers */, C33FDD84255A582000E217F9 /* LKGroupUtilities.h in Headers */, - C33FDDC7255A582000E217F9 /* OWSProvisioningMessage.h in Headers */, C33FDC8D255A582000E217F9 /* TSThread.h in Headers */, - C33FDC81255A582000E217F9 /* OWSSignalService.h in Headers */, - C33FDC4B255A582000E217F9 /* LKSyncOpenGroupsMessage.h in Headers */, C33FDDA5255A582000E217F9 /* OWSBlockingManager.h in Headers */, C38EF366255B6DCC007E1867 /* ScreenLockViewController.h in Headers */, - C33FDD9D255A582000E217F9 /* CDSSigningCertificate.h in Headers */, C33FDD16255A582000E217F9 /* NSArray+Functional.h in Headers */, C38EF35B255B6DCC007E1867 /* SelectThreadViewController.h in Headers */, C33FDCE9255A582000E217F9 /* ContactsManagerProtocol.h in Headers */, - C33FDCD0255A582000E217F9 /* OWSDisappearingMessagesConfigurationMessage.h in Headers */, C33FDCE4255A582000E217F9 /* OWSIncompleteCallsJob.h in Headers */, - C33FDCC0255A582000E217F9 /* OWSRequestBuilder.h in Headers */, C33FDDD3255A582000E217F9 /* OWSQueues.h in Headers */, C33FDC96255A582000E217F9 /* NSObject+Casting.h in Headers */, C33FDC75255A582000E217F9 /* OWSGroupsOutputStream.h in Headers */, @@ -4624,20 +4009,16 @@ C33FDD57255A582000E217F9 /* OWSCallMessageHandler.h in Headers */, C33FDCC8255A582000E217F9 /* NSError+MessageSending.h in Headers */, C38EF403255B6DF7007E1867 /* ContactCellView.h in Headers */, - C33FDD6D255A582000E217F9 /* OWSOutgoingNullMessage.h in Headers */, C33FDCA0255A582000E217F9 /* TSInteraction.h in Headers */, C33FDD68255A582000E217F9 /* SignalAccount.h in Headers */, - C38EF315255B6DBF007E1867 /* OWSGroupAvatarBuilder.h in Headers */, C38EF35E255B6DCC007E1867 /* OWSViewController.h in Headers */, C33FDC4F255A582000E217F9 /* OWSChunkedOutputStream.h in Headers */, - C33FDCE7255A582000E217F9 /* OWSDevice.h in Headers */, C37F5396255B95BD002AEA92 /* OWSAnyTouchGestureRecognizer.h in Headers */, C33FDD0E255A582000E217F9 /* DataSource.h in Headers */, C38EF325255B6DBF007E1867 /* OWSWindowManager.h in Headers */, C33FDCF2255A582000E217F9 /* OWSBackgroundTask.h in Headers */, C38EF39C255B6DDA007E1867 /* OWSQuotedReplyModel.h in Headers */, C38EF404255B6DF7007E1867 /* ContactTableViewCell.h in Headers */, - C33FDD5C255A582000E217F9 /* PhoneNumber.h in Headers */, C38EF24A255B6D67007E1867 /* UIView+OWS.h in Headers */, C33FDDB2255A582000E217F9 /* NSArray+OWS.h in Headers */, C38EF322255B6DBF007E1867 /* DebugLogger.h in Headers */, @@ -4645,45 +4026,30 @@ C33FDD09255A582000E217F9 /* SSKJobRecord.h in Headers */, C33FDC94255A582000E217F9 /* OWSDisappearingConfigurationUpdateInfoMessage.h in Headers */, C38EF2D7255B6DAF007E1867 /* OWSProfileManager.h in Headers */, - C38EF364255B6DCC007E1867 /* NewNonContactConversationViewController.h in Headers */, C33FDC21255A581F00E217F9 /* OWSPrimaryStorage.h in Headers */, C38EF290255B6D86007E1867 /* AppSetup.h in Headers */, C37F5385255B94F6002AEA92 /* SelectRecipientViewController.h in Headers */, - C38EF328255B6DBF007E1867 /* OWSContactAvatarBuilder.h in Headers */, C33FDC61255A582000E217F9 /* OWSPrimaryStorage+SignedPreKeyStore.h in Headers */, C38EF367255B6DCC007E1867 /* OWSTableViewController.h in Headers */, C33FDCEB255A582000E217F9 /* SSKEnvironment.h in Headers */, C38EF246255B6D67007E1867 /* UIFont+OWS.h in Headers */, C37F53A7255B96E0002AEA92 /* OWSAudioPlayer.h in Headers */, C33FDC35255A581F00E217F9 /* TSInvalidIdentityKeyReceivingErrorMessage.h in Headers */, - C33FDDC9255A582000E217F9 /* LKDeviceLinkMessage.h in Headers */, - C33FDD89255A582000E217F9 /* OWSFingerprintBuilder.h in Headers */, C33FDD0D255A582000E217F9 /* PreKeyBundle+jsonDict.h in Headers */, C33FDD60255A582000E217F9 /* TSInvalidIdentityKeySendingErrorMessage.h in Headers */, C33FDCF5255A582000E217F9 /* NSNotificationCenter+OWS.h in Headers */, C33FDD34255A582000E217F9 /* NotificationsProtocol.h in Headers */, - C33FDC90255A582000E217F9 /* OWSAnalytics.h in Headers */, - C33FDD36255A582000E217F9 /* OWSVerificationStateChangeMessage.h in Headers */, C33FD9AF255A548A00E217F9 /* SignalUtilitiesKit.h in Headers */, - C33FDD9E255A582000E217F9 /* OWSOutgoingSyncMessage.h in Headers */, C33FDC50255A582000E217F9 /* OWSDispatch.h in Headers */, C33FDD06255A582000E217F9 /* AppVersion.h in Headers */, C33FDD90255A582000E217F9 /* OWSUploadOperation.h in Headers */, C33FDC8F255A582000E217F9 /* TSQuotedMessage.h in Headers */, C33FDC73255A582000E217F9 /* OWSStorage+Subclass.h in Headers */, C33FDCA4255A582000E217F9 /* OWSBackupFragment.h in Headers */, - C33FDCFC255A582000E217F9 /* OWSCountryMetadata.h in Headers */, - C33FDC60255A582000E217F9 /* OWSLinkedDeviceReadReceipt.h in Headers */, C33FDD27255A582000E217F9 /* TSPreKeyManager.h in Headers */, - C33FDC67255A582000E217F9 /* OWSDeviceProvisioner.h in Headers */, - C33FDCE8255A582000E217F9 /* OWSEndSessionMessage.h in Headers */, - C38EF25F255B6D6F007E1867 /* OWSSyncManager.h in Headers */, - C33FDDAD255A582000E217F9 /* OWSBlockedPhoneNumbersMessage.h in Headers */, C33FDD0B255A582000E217F9 /* NSUserDefaults+OWS.h in Headers */, C33FDD56255A582000E217F9 /* TSIncomingMessage.h in Headers */, - C33FDC9C255A582000E217F9 /* OWSReadReceiptsForLinkedDevicesMessage.h in Headers */, C37F53E9255BA9CE002AEA92 /* Environment.h in Headers */, - C33FDDAF255A582000E217F9 /* OWSDeviceProvisioningService.h in Headers */, C33FDCE6255A582000E217F9 /* TSDatabaseView.h in Headers */, C33FDD5A255A582000E217F9 /* TSStorageHeaders.h in Headers */, C33FDCA2255A582000E217F9 /* OWSMessageUtils.h in Headers */, @@ -5560,14 +4926,10 @@ buildActionMask = 2147483647; files = ( C38EF30D255B6DBF007E1867 /* OWSUnreadIndicator.m in Sources */, - C33FDD40255A582000E217F9 /* OWSRequestFactory.m in Sources */, - C33FDD96255A582000E217F9 /* ContactDiscoveryService.m in Sources */, C38EF3FD255B6DF7007E1867 /* OWSTextView.m in Sources */, C33FDCA9255A582000E217F9 /* NSData+Image.m in Sources */, C38EF3C6255B6DE7007E1867 /* ImageEditorModel.swift in Sources */, C38EF317255B6DBF007E1867 /* DisplayableText.swift in Sources */, - C33FDC8B255A582000E217F9 /* OWSDynamicOutgoingMessage.m in Sources */, - C38EEFD6255B5BA2007E1867 /* OldSnodeAPI.swift in Sources */, C38EF30F255B6DBF007E1867 /* AppPreferences.swift in Sources */, C33FDD45255A582000E217F9 /* Storage+SessionManagement.swift in Sources */, C33FDCA3255A582000E217F9 /* SSKMessageSenderJobRecord.m in Sources */, @@ -5579,28 +4941,21 @@ C38EF247255B6D67007E1867 /* NSAttributedString+OWS.m in Sources */, C33FDC99255A582000E217F9 /* PublicChatManager.swift in Sources */, C33FDCB9255A582000E217F9 /* DisplayNameUtilities.swift in Sources */, - C33FDDA7255A582000E217F9 /* ClosedGroupParser.swift in Sources */, C33FDD49255A582000E217F9 /* ParamParser.swift in Sources */, C38EF35F255B6DCC007E1867 /* SelectRecipientViewController.m in Sources */, C38EF3C5255B6DE7007E1867 /* OWSViewController+ImageEditor.swift in Sources */, C33FDDCD255A582000E217F9 /* OWSAttachmentDownloads.m in Sources */, - C33FDC32255A581F00E217F9 /* SSKWebSocket.swift in Sources */, C33FDD97255A582000E217F9 /* OWSDisappearingMessagesJob.m in Sources */, C33FDD0F255A582000E217F9 /* TSInvalidIdentityKeySendingErrorMessage.m in Sources */, C38EF320255B6DBF007E1867 /* OWSScrubbingLogFormatter.m in Sources */, - C33FDC44255A581F00E217F9 /* PhoneNumber.m in Sources */, C38EF39B255B6DDA007E1867 /* ThreadViewModel.swift in Sources */, C38EF2A5255B6D93007E1867 /* Identicon+ObjC.swift in Sources */, C38EF330255B6DBF007E1867 /* OWSWindowManager.m in Sources */, C33FDD79255A582000E217F9 /* OWSHTTPSecurityPolicy.m in Sources */, C33FDDB7255A582000E217F9 /* OWSPrimaryStorage+Calling.m in Sources */, - C33FDD69255A582000E217F9 /* OWSAnalyticsEvents.m in Sources */, C33FDD3D255A582000E217F9 /* TSQuotedMessage.m in Sources */, C38EF273255B6D7A007E1867 /* OWSDatabaseMigrationRunner.m in Sources */, - C33FDCB4255A582000E217F9 /* LKDeviceLinkMessage.m in Sources */, - C33FDC70255A582000E217F9 /* SyncMessagesProtocol.swift in Sources */, C33FDD12255A582000E217F9 /* OWSPrimaryStorage+Loki.m in Sources */, - C33FDC5C255A582000E217F9 /* OWSAddToContactsOfferMessage.m in Sources */, C38EF31A255B6DBF007E1867 /* OWSAnyTouchGestureRecognizer.m in Sources */, C33FDCFB255A582000E217F9 /* MIMETypeUtil.m in Sources */, C38EF36C255B6DCC007E1867 /* SharingThreadPickerViewController.m in Sources */, @@ -5608,27 +4963,18 @@ C38EF385255B6DD2007E1867 /* AttachmentTextToolbar.swift in Sources */, C33FDC87255A582000E217F9 /* SSKJobRecord.m in Sources */, C33FDD23255A582000E217F9 /* FeatureFlags.swift in Sources */, - C33FDD8A255A582000E217F9 /* OnionRequestAPI+Encryption.swift in Sources */, - C38EF36E255B6DCC007E1867 /* ContactFieldView.swift in Sources */, C38EF297255B6D86007E1867 /* OWSSounds.m in Sources */, - C38EF39D255B6DDA007E1867 /* ContactShareViewModel.swift in Sources */, - C33FDD3C255A582000E217F9 /* MessageWrapper.swift in Sources */, C33FDCF1255A582000E217F9 /* Storage+SnodeAPI.swift in Sources */, C33FDCAE255A582000E217F9 /* SSKEnvironment.m in Sources */, - C33FDDA8255A582000E217F9 /* ClosedGroupUpdateMessage.swift in Sources */, C33FDCF9255A582000E217F9 /* String+SSK.swift in Sources */, C38EF389255B6DD2007E1867 /* AttachmentTextView.swift in Sources */, - C38EF36A255B6DCC007E1867 /* NewNonContactConversationViewController.m in Sources */, C38EF321255B6DBF007E1867 /* OWSAudioPlayer.m in Sources */, C33FDD0A255A582000E217F9 /* OWSPrimaryStorage+PreKeyStore.m in Sources */, - C33FDDC3255A582000E217F9 /* AccountServiceClient.swift in Sources */, - C33FDC9F255A582000E217F9 /* OWSAddToProfileWhitelistOfferMessage.m in Sources */, C33FDC4E255A582000E217F9 /* Data+Streaming.swift in Sources */, C33FDCE5255A582000E217F9 /* OWSProvisioningCipher.m in Sources */, C33FDDD6255A582000E217F9 /* TSCall.m in Sources */, C33FDD1A255A582000E217F9 /* TSMessage.m in Sources */, C38EF3FF255B6DF7007E1867 /* TappableView.swift in Sources */, - C38EF360255B6DCC007E1867 /* ReturnToCallViewController.swift in Sources */, C38EF3C2255B6DE7007E1867 /* ImageEditorPaletteView.swift in Sources */, C38EF245255B6D67007E1867 /* UIFont+OWS.m in Sources */, C33FDD26255A582000E217F9 /* NSNotificationCenter+OWS.m in Sources */, @@ -5650,7 +4996,6 @@ C38EF40A255B6DF7007E1867 /* OWSFlatButton.swift in Sources */, C33FDCD1255A582000E217F9 /* FunctionalUtil.m in Sources */, C38EF402255B6DF7007E1867 /* CommonStrings.swift in Sources */, - C33FDD51255A582000E217F9 /* OWSDisappearingMessagesConfigurationMessage.m in Sources */, C38EF3C1255B6DE7007E1867 /* ImageEditorBrushViewController.swift in Sources */, C33FDD3F255A582000E217F9 /* AppContext.m in Sources */, C33FDCC7255A582000E217F9 /* NSArray+OWS.m in Sources */, @@ -5659,113 +5004,78 @@ C33FDD72255A582000E217F9 /* TSThread.m in Sources */, C33FDC71255A582000E217F9 /* OWSFailedMessagesJob.m in Sources */, C33FDD32255A582000E217F9 /* OWSOperation.m in Sources */, - C33FDD9C255A582000E217F9 /* OWSUnknownContactBlockOfferMessage.m in Sources */, C33FDC4A255A582000E217F9 /* TSYapDatabaseObject.m in Sources */, - C33FDC65255A582000E217F9 /* OWSWebSocket.m in Sources */, C3F0A530255C80BC007BE2A3 /* NoopNotificationsManager.swift in Sources */, C33FDD8D255A582000E217F9 /* OWSSignalAddress.swift in Sources */, - C33FDCEF255A582000E217F9 /* OWSContactDiscoveryOperation.swift in Sources */, - C38EF217255B6D3B007E1867 /* OWSConversationColor.m in Sources */, C33FDCB1255A582000E217F9 /* OWSPrimaryStorage+SignedPreKeyStore.m in Sources */, - C33FDD59255A582000E217F9 /* TTLUtilities.swift in Sources */, C38EF2C5255B6DA6007E1867 /* TSUnreadIndicatorInteraction.m in Sources */, - C33FDD7E255A582000E217F9 /* TypingIndicatorMessage.swift in Sources */, C33FDC64255A582000E217F9 /* NSObject+Casting.m in Sources */, - C33FDC85255A582000E217F9 /* CDSSigningCertificate.m in Sources */, C38EF2C4255B6DA6007E1867 /* OWSContactOffersInteraction.m in Sources */, - C33FDC68255A582000E217F9 /* OWSReceiptsForSenderMessage.m in Sources */, C38EF388255B6DD2007E1867 /* AttachmentApprovalViewController.swift in Sources */, C33FDD53255A582000E217F9 /* OWSPrimaryStorage+keyFromIntLong.m in Sources */, - C33FDD66255A582000E217F9 /* Data+SecureRandom.swift in Sources */, C33FDC72255A582000E217F9 /* NSArray+Functional.m in Sources */, C33FDD38255A582000E217F9 /* TSPreKeyManager.m in Sources */, - C33FDCCD255A582000E217F9 /* LKUnlinkDeviceMessage.m in Sources */, - C38EF261255B6D6F007E1867 /* OWSSyncManager.m in Sources */, C33FDD94255A582000E217F9 /* Dictionary+Description.swift in Sources */, C38EF2A7255B6D93007E1867 /* ProfilePictureView.swift in Sources */, C38EF22C255B6D5D007E1867 /* OWSVideoPlayer.swift in Sources */, - C33FDD18255A582000E217F9 /* ContactParser.swift in Sources */, - C33FDD2C255A582000E217F9 /* DeviceLinkIndex.swift in Sources */, C33FDC29255A581F00E217F9 /* ReachabilityManager.swift in Sources */, C38EF407255B6DF7007E1867 /* Toast.swift in Sources */, C38EF38C255B6DD2007E1867 /* ApprovalRailCellView.swift in Sources */, C33FDDA2255A582000E217F9 /* Storage+OnionRequests.swift in Sources */, C38EF409255B6DF7007E1867 /* ContactTableViewCell.m in Sources */, - C33FDDBE255A582000E217F9 /* DecryptionUtilities.swift in Sources */, C38EF32A255B6DBF007E1867 /* UIUtil.m in Sources */, C33FDC3F255A581F00E217F9 /* OWSPrimaryStorage+Loki.swift in Sources */, - C33FDDB9255A582000E217F9 /* OWSOutgoingSyncMessage.m in Sources */, C38EF333255B6DBF007E1867 /* DeviceSleepManager.swift in Sources */, C33FDC52255A582000E217F9 /* RotateSignedKeyOperation.swift in Sources */, - C38EF481255B752E007E1867 /* SystemContactsFetcher.swift in Sources */, C33FDCDA255A582000E217F9 /* TSDatabaseSecondaryIndexes.m in Sources */, C38EF335255B6DBF007E1867 /* BlockListCache.swift in Sources */, C38EF2A6255B6D93007E1867 /* PlaceholderIcon.swift in Sources */, C38EF316255B6DBF007E1867 /* ProximityMonitoringManager.swift in Sources */, - C33FDC6C255A582000E217F9 /* TSNetworkManager.m in Sources */, C38EF371255B6DCC007E1867 /* MessageApprovalViewController.swift in Sources */, C33FDD71255A582000E217F9 /* SignalRecipient.m in Sources */, C33FDD58255A582000E217F9 /* TSAttachmentPointer.m in Sources */, C33FDD39255A582000E217F9 /* FullTextSearchFinder.swift in Sources */, - C33FDDA4255A582000E217F9 /* SessionRequestMessage.swift in Sources */, - C33FDC5F255A582000E217F9 /* OWSRequestMaker.swift in Sources */, - C38EEF0A255B49A8007E1867 /* SSKProtoEnvelope+Conversion.swift in Sources */, + C38EEF0A255B49A8007E1867 /* SNProtoEnvelope+Conversion.swift in Sources */, C33FDD92255A582000E217F9 /* SignalIOS.pb.swift in Sources */, C33FDCB3255A582000E217F9 /* TSContactThread.m in Sources */, C33FDC45255A581F00E217F9 /* AppVersion.m in Sources */, C33FDD2A255A582000E217F9 /* OWSMessageServiceParams.m in Sources */, - C33FDC7F255A582000E217F9 /* OWSCountryMetadata.m in Sources */, - C33FDC84255A582000E217F9 /* LokiMessage.swift in Sources */, C33FDC7C255A582000E217F9 /* TSAttachment.m in Sources */, C38EF3C7255B6DE7007E1867 /* ImageEditorCanvasView.swift in Sources */, - C38EF3F3255B6DF7007E1867 /* ContactsViewHelper.m in Sources */, C33FDC46255A581F00E217F9 /* PublicChatPoller.swift in Sources */, C38EF400255B6DF7007E1867 /* GalleryRailView.swift in Sources */, C38EF32E255B6DBF007E1867 /* ImageCache.swift in Sources */, - C33FDCD9255A582000E217F9 /* DeviceLinkingUtilities.swift in Sources */, C33FDC34255A581F00E217F9 /* NSRegularExpression+SSK.swift in Sources */, - C33FDD7D255A582000E217F9 /* AnyPromise+Conversion.swift in Sources */, C38EF32F255B6DBF007E1867 /* OWSFormat.m in Sources */, - C33FDD8B255A582000E217F9 /* DeviceLink.swift in Sources */, C38EF218255B6D3B007E1867 /* Theme.m in Sources */, C38EF3BA255B6DE7007E1867 /* ImageEditorItem.swift in Sources */, C33FDD5D255A582000E217F9 /* SessionManagementProtocol.swift in Sources */, C38EF3F7255B6DF7007E1867 /* OWSNavigationBar.swift in Sources */, C33FDD76255A582000E217F9 /* SSKKeychainStorage.swift in Sources */, - C33FDD33255A582000E217F9 /* PhoneNumberUtil.m in Sources */, C33FDDA6255A582000E217F9 /* OWSRecipientIdentity.m in Sources */, C37F5403255BA9ED002AEA92 /* Environment.m in Sources */, C38EF2D4255B6DAF007E1867 /* OWSProfileManager.m in Sources */, C38EF248255B6D67007E1867 /* UIViewController+OWS.m in Sources */, C38EF272255B6D7A007E1867 /* OWSResaveCollectionDBMigration.m in Sources */, - C38EF35A255B6DCC007E1867 /* ViewControllerUtils.m in Sources */, C33FDD3A255A582000E217F9 /* Notification+Loki.swift in Sources */, C38EF276255B6D7A007E1867 /* OWSDatabaseMigration.m in Sources */, C38EF370255B6DCC007E1867 /* OWSNavigationController.m in Sources */, C38EF24E255B6D67007E1867 /* Collection+OWS.swift in Sources */, - C33FDD3E255A582000E217F9 /* OWSIncomingSentMessageTranscript.m in Sources */, C33FDD05255A582000E217F9 /* OWSChunkedOutputStream.m in Sources */, - C33FDCC5255A582000E217F9 /* OWSVerificationStateChangeMessage.m in Sources */, C33FDD10255A582000E217F9 /* TSOutgoingMessage.m in Sources */, - C33FDCD2255A582000E217F9 /* OWSSignalService.m in Sources */, C33FDCFA255A582000E217F9 /* SignalIOSProto.swift in Sources */, - C33FDC28255A581F00E217F9 /* Array+Description.swift in Sources */, C33FDCC3255A582000E217F9 /* NSError+MessageSending.m in Sources */, C33FDCED255A582000E217F9 /* Provisioning.pb.swift in Sources */, C33FDCF0255A582000E217F9 /* Storage.swift in Sources */, C33FDD2D255A582000E217F9 /* TSGroupModel.m in Sources */, - C33FDD50255A582000E217F9 /* OWSReadReceiptsForLinkedDevicesMessage.m in Sources */, C33FDD9B255A582000E217F9 /* LKGroupUtilities.m in Sources */, C38EF229255B6D5D007E1867 /* SignalAttachment.swift in Sources */, C33FDDC2255A582000E217F9 /* SignalService.pb.swift in Sources */, - C38EF3FC255B6DF7007E1867 /* AvatarImageView.swift in Sources */, C33FDD13255A582000E217F9 /* OWSFailedAttachmentDownloadsJob.m in Sources */, C38EF24D255B6D67007E1867 /* UIView+OWS.swift in Sources */, C3CA3B13255CF18200F4C6D4 /* Destination+Conversion.swift in Sources */, C33FDCB5255A582000E217F9 /* SessionMetaProtocol.swift in Sources */, - C33FDC91255A582000E217F9 /* OWSDeviceProvisioner.m in Sources */, C33FDC6A255A582000E217F9 /* ProvisioningProto.swift in Sources */, - C33FDC37255A581F00E217F9 /* OWSCensorshipConfiguration.m in Sources */, C33FDD4D255A582000E217F9 /* PreKeyBundle+jsonDict.m in Sources */, C38EF24B255B6D67007E1867 /* UIView+OWS.m in Sources */, C33FDC51255A582000E217F9 /* TSIncomingMessage.m in Sources */, @@ -5787,42 +5097,23 @@ C38EF405255B6DF7007E1867 /* OWSButton.swift in Sources */, C38EF3C4255B6DE7007E1867 /* ImageEditorContents.swift in Sources */, C33FDC3B255A581F00E217F9 /* MentionsManager.swift in Sources */, - C33FDC49255A581F00E217F9 /* NSTimer+OWS.m in Sources */, - C38EF260255B6D6F007E1867 /* OWSContactsManager.m in Sources */, C33FDCFD255A582000E217F9 /* YapDatabaseConnection+OWS.m in Sources */, C38EF3BC255B6DE7007E1867 /* ImageEditorPanGestureRecognizer.swift in Sources */, C33FDC23255A581F00E217F9 /* SSKPreferences.swift in Sources */, C38EF372255B6DCC007E1867 /* MediaMessageView.swift in Sources */, - C33FDD4C255A582000E217F9 /* OWSDeviceProvisioningService.m in Sources */, - C33FDC31255A581F00E217F9 /* Contact.m in Sources */, - C38EF2D5255B6DAF007E1867 /* ProfileFetcherJob.swift in Sources */, C38EF387255B6DD2007E1867 /* AttachmentItemCollection.swift in Sources */, - C38EF318255B6DBF007E1867 /* OWSAvatarBuilder.m in Sources */, C38EF22B255B6D5D007E1867 /* ShareViewDelegate.swift in Sources */, - C33FDC8A255A582000E217F9 /* CDSQuote.m in Sources */, - C33FDCA5255A582000E217F9 /* OWSDeviceProvisioningCodeService.m in Sources */, C33FDC38255A581F00E217F9 /* Mention.swift in Sources */, - C33FDD8F255A582000E217F9 /* OWSOutgoingSentMessageTranscript.m in Sources */, C33FDDC1255A582000E217F9 /* Storage+ClosedGroups.swift in Sources */, - C33FDC63255A582000E217F9 /* Mnemonic.swift in Sources */, - C38EF323255B6DBF007E1867 /* OWSContactAvatarBuilder.m in Sources */, C38EF3BF255B6DE7007E1867 /* ImageEditorView.swift in Sources */, C38EF365255B6DCC007E1867 /* OWSTableViewController.m in Sources */, C33FDC25255A581F00E217F9 /* OWSDisappearingConfigurationUpdateInfoMessage.m in Sources */, - C33FDC59255A582000E217F9 /* NetworkManager.swift in Sources */, C38EF36B255B6DCC007E1867 /* ScreenLockViewController.m in Sources */, - C33FDDCB255A582000E217F9 /* TSSocketManager.m in Sources */, C33FDC39255A581F00E217F9 /* OWSRecordTranscriptJob.m in Sources */, C38EF228255B6D5D007E1867 /* AttachmentSharing.m in Sources */, - C33FDCCA255A582000E217F9 /* OWSBatchMessageProcessor.m in Sources */, C33FDD1E255A582000E217F9 /* PreKeyRefreshOperation.swift in Sources */, - C33FDDCA255A582000E217F9 /* ProofOfWork.swift in Sources */, C33FDCF4255A582000E217F9 /* Poller.swift in Sources */, C38EF332255B6DBF007E1867 /* OWSPreferences.m in Sources */, - C33FDDBA255A582000E217F9 /* OWSSyncGroupsMessage.m in Sources */, - C33FDDD7255A582000E217F9 /* OWSSyncConfigurationMessage.m in Sources */, - C33FDC43255A581F00E217F9 /* OWSAnalytics.m in Sources */, - C33FDD04255A582000E217F9 /* SignalServiceClient.swift in Sources */, C33FDC26255A581F00E217F9 /* ProtoUtils.m in Sources */, C33FDC48255A581F00E217F9 /* OWSFileSystem.m in Sources */, C33FDC5E255A582000E217F9 /* SSKProto.swift in Sources */, @@ -5835,95 +5126,69 @@ C33FDC6B255A582000E217F9 /* OWSStorage.m in Sources */, C38EF28D255B6D86007E1867 /* OWSAudioSession.swift in Sources */, C33FDD7B255A582000E217F9 /* GeneralUtilities.swift in Sources */, - C33FDD47255A582000E217F9 /* DeviceLinkingSessionDelegate.swift in Sources */, C33FDCC1255A582000E217F9 /* OWSBackupFragment.m in Sources */, C33FDDAE255A582000E217F9 /* DisplayNameUtilities2.swift in Sources */, C33FDD4F255A582000E217F9 /* Storage+Collections.swift in Sources */, C33FDD15255A582000E217F9 /* YapDatabaseTransaction+OWS.m in Sources */, C33FDD63255A582000E217F9 /* OWSIdentityManager.m in Sources */, - C33FDCC9255A582000E217F9 /* DeviceLinkingSession.swift in Sources */, C38EF35C255B6DCC007E1867 /* SelectThreadViewController.m in Sources */, C38EF2D6255B6DAF007E1867 /* OWSUserProfile.m in Sources */, C38EF30E255B6DBF007E1867 /* FullTextSearcher.swift in Sources */, - C33FDC54255A582000E217F9 /* OWSLinkedDeviceReadReceipt.m in Sources */, - C33FDDD9255A582000E217F9 /* LokiSessionResetImplementation.swift in Sources */, + C33FDDD9255A582000E217F9 /* LokiSessionRestorationImplementation.swift in Sources */, C33FDD31255A582000E217F9 /* NSUserDefaults+OWS.m in Sources */, C38EF3FA255B6DF7007E1867 /* DirectionalPanGestureRecognizer.swift in Sources */, C38EF3BB255B6DE7007E1867 /* ImageEditorStrokeItem.swift in Sources */, C33FDCA1255A582000E217F9 /* TSErrorMessage.m in Sources */, C38EF295255B6D86007E1867 /* NoopCallMessageHandler.swift in Sources */, - C33FDDD1255A582000E217F9 /* SharedSenderKeysImplementation.swift in Sources */, - C33FDCBF255A582000E217F9 /* OWSFingerprint.m in Sources */, C38EF3C0255B6DE7007E1867 /* ImageEditorCropViewController.swift in Sources */, C38EF401255B6DF7007E1867 /* VideoPlayerView.swift in Sources */, C33FDC22255A581F00E217F9 /* OWSBlockingManager.m in Sources */, C38EF3FE255B6DF7007E1867 /* OWSTextField.m in Sources */, - C33FDCA8255A582000E217F9 /* OWSFingerprintBuilder.m in Sources */, C33FDC40255A581F00E217F9 /* OWSDisappearingMessagesFinder.m in Sources */, - C33FDD28255A582000E217F9 /* SSKProtoPrekeyBundleMessage+Loki.swift in Sources */, C33FDD24255A582000E217F9 /* SignalMessage.swift in Sources */, C33FDCEE255A582000E217F9 /* ClosedGroupPoller.swift in Sources */, C38EF3BD255B6DE7007E1867 /* ImageEditorTransform.swift in Sources */, - C38EF361255B6DCC007E1867 /* EditContactShareNameViewController.swift in Sources */, C38EF24F255B6D67007E1867 /* UIColor+OWS.m in Sources */, - C33FDC8E255A582000E217F9 /* EncryptionUtilities.swift in Sources */, C33FDC9A255A582000E217F9 /* ByteParser.m in Sources */, C38EF293255B6D86007E1867 /* AppSetup.m in Sources */, - C33FDC55255A582000E217F9 /* OWSProvisioningMessage.m in Sources */, C33FDDC0255A582000E217F9 /* SignalAccount.m in Sources */, C33FDC58255A582000E217F9 /* ReverseDispatchQueue.swift in Sources */, C38EF3F4255B6DF7007E1867 /* ContactCellView.m in Sources */, C33FDD29255A582000E217F9 /* OWSOutgoingReceiptManager.m in Sources */, C33FDC78255A582000E217F9 /* TSConstants.m in Sources */, - C33FDD52255A582000E217F9 /* DeviceNames.swift in Sources */, C33FDCE0255A582000E217F9 /* FingerprintProto.swift in Sources */, - C33FDDA0255A582000E217F9 /* OWSOutgoingNullMessage.m in Sources */, C38EF324255B6DBF007E1867 /* Bench.swift in Sources */, C38EF292255B6D86007E1867 /* VersionMigrations.m in Sources */, C33FDD5E255A582000E217F9 /* OWSDisappearingMessagesConfiguration.m in Sources */, C38EF3F2255B6DF7007E1867 /* DisappearingTimerConfigurationView.swift in Sources */, C33FDCD8255A582000E217F9 /* OWSIncomingMessageFinder.m in Sources */, - C33FDCE2255A582000E217F9 /* OWSOutgoingCallMessage.m in Sources */, C38EF3F9255B6DF7007E1867 /* OWSLayerView.swift in Sources */, - C33FDD9A255A582000E217F9 /* OWSBlockedPhoneNumbersMessage.m in Sources */, - C33FDD99255A582000E217F9 /* LKSyncOpenGroupsMessage.m in Sources */, C33FDCAC255A582000E217F9 /* ProxiedContentDownloader.swift in Sources */, C33FDD03255A582000E217F9 /* WeakTimer.swift in Sources */, C33FDCDC255A582000E217F9 /* OWSMediaUtils.swift in Sources */, C33FDD41255A582000E217F9 /* JobQueue.swift in Sources */, - C33FDC30255A581F00E217F9 /* OWSSyncGroupsRequestMessage.m in Sources */, - C33FDD9F255A582000E217F9 /* OWSDevicesService.m in Sources */, - C33FDD43255A582000E217F9 /* FileServerAPI+Deprecated.swift in Sources */, C38EF3B9255B6DE7007E1867 /* ImageEditorPinchGestureRecognizer.swift in Sources */, - C33FDD30255A582000E217F9 /* ClosedGroupUtilities.swift in Sources */, C33FDC98255A582000E217F9 /* SwiftSingletons.swift in Sources */, C33FDC27255A581F00E217F9 /* YapDatabase+Promise.swift in Sources */, - C33FDC3E255A581F00E217F9 /* ContactsUpdater.m in Sources */, C33FDCFE255A582000E217F9 /* OWSContactsOutputStream.m in Sources */, - C33FDC88255A582000E217F9 /* OWSVerificationStateSyncMessage.m in Sources */, C33FDDBC255A582000E217F9 /* OWSPrimaryStorage.m in Sources */, - C33FDD54255A582000E217F9 /* OWS2FAManager.m in Sources */, C33FDCD3255A582000E217F9 /* GroupUtilities.swift in Sources */, C33FDDAA255A582000E217F9 /* LokiDatabaseUtilities.swift in Sources */, - C33FDCBA255A582000E217F9 /* OWSRequestBuilder.m in Sources */, C33FDC2B255A581F00E217F9 /* OWSReadReceiptManager.m in Sources */, C38EF326255B6DBF007E1867 /* ConversationStyle.swift in Sources */, - C38EF312255B6DBF007E1867 /* OWSGroupAvatarBuilder.m in Sources */, C38EF3B8255B6DE7007E1867 /* ImageEditorTextViewController.swift in Sources */, C33FDD91255A582000E217F9 /* OWSMessageUtils.m in Sources */, + B8C2B332256376F000551B4D /* ThreadUtil.m in Sources */, C38EF40B255B6DF7007E1867 /* TappableStackView.swift in Sources */, - C33FDCF7255A582000E217F9 /* OWSProfileKeyMessage.m in Sources */, C3CA3B1D255CF3C800F4C6D4 /* MessageSender+Utilities.swift in Sources */, C38EF31D255B6DBF007E1867 /* UIImage+OWS.swift in Sources */, C38EF359255B6DCC007E1867 /* SheetViewController.swift in Sources */, - C38EF362255B6DCC007E1867 /* ContactShareApprovalViewController.swift in Sources */, C33FDCAD255A582000E217F9 /* OWSPrimaryStorage+SessionStore.m in Sources */, C38EF386255B6DD2007E1867 /* AttachmentApprovalInputAccessoryView.swift in Sources */, C33FDC92255A582000E217F9 /* OWSGroupsOutputStream.m in Sources */, - C33FDC56255A582000E217F9 /* OWSSyncContactsMessage.m in Sources */, C38EF28E255B6D86007E1867 /* SignalKeyingStorage.m in Sources */, + B8C2B2C82563685C00551B4D /* CircleView.swift in Sources */, C38EF406255B6DF7007E1867 /* OWSAlerts.swift in Sources */, - C33FDC3D255A581F00E217F9 /* Promise+retainUntilComplete.swift in Sources */, C33FDD62255A582000E217F9 /* OWSLinkPreview.swift in Sources */, C33FDDA3255A582000E217F9 /* TSInteraction.m in Sources */, C38EF331255B6DBF007E1867 /* UIGestureRecognizer+OWS.swift in Sources */, @@ -5935,10 +5200,7 @@ C33FDDC6255A582000E217F9 /* TSInfoMessage.m in Sources */, C38EF319255B6DBF007E1867 /* Weak.swift in Sources */, C38EF31C255B6DBF007E1867 /* Searcher.swift in Sources */, - C33FDDC4255A582000E217F9 /* OWSContact.m in Sources */, - C33FDCE1255A582000E217F9 /* OWSEndSessionMessage.m in Sources */, C38EF2B3255B6D9C007E1867 /* UIViewController+Utilities.swift in Sources */, - C33FDCEA255A582000E217F9 /* OWSDevice.m in Sources */, C38EF3BE255B6DE7007E1867 /* OrderedDictionary.swift in Sources */, C33FDD6E255A582000E217F9 /* NSURLSessionDataTask+StatusCode.m in Sources */, C33FDD2B255A582000E217F9 /* OWSMediaGalleryFinder.m in Sources */, @@ -5975,7 +5237,7 @@ C352A36D2557858E00338F3E /* NSTimer+Proxying.m in Sources */, C3C2ABD22553C6C900C340D1 /* Data+SecureRandom.swift in Sources */, C3A7211A2558BCA10043A11F /* DiffieHellman.swift in Sources */, - C3A7225E2558C38D0043A11F /* AnyPromise+Retaining.swift in Sources */, + C3A7225E2558C38D0043A11F /* Promise+Retaining.swift in Sources */, C3BBE0AA2554D4DE0050F1E3 /* Dictionary+Description.swift in Sources */, C3C2AC2E2553CBEB00C340D1 /* String+Trimming.swift in Sources */, C352A3A62557B60D00338F3E /* TSRequest.m in Sources */, @@ -6103,7 +5365,6 @@ files = ( C396DAF52518408B00FF6DC5 /* CSV.swift in Sources */, B8CCF63723961D6D0091D419 /* NewPrivateChatVC.swift in Sources */, - 4CC0B59C20EC5F2E00CF6EE0 /* ConversationConfigurationSyncOperation.swift in Sources */, 3461293E1FD1D72B00532771 /* ExperienceUpgradeFinder.swift in Sources */, C3F0A61A255C9902007BE2A3 /* Storage+SessionProtocolKit.swift in Sources */, 34C4E2582118957600BEA353 /* WebRTCProto.swift in Sources */, @@ -6118,9 +5379,7 @@ 34D1F0501F7D45A60066283D /* GifPickerCell.swift in Sources */, 3496957421A301A100DCFE74 /* OWSBackupAPI.swift in Sources */, C3E5C2FA251DBABB0040DFFC /* EditClosedGroupVC.swift in Sources */, - 34D99C931F2937CC00D284D6 /* OWSAnalytics.swift in Sources */, B8783E9E23EB948D00404FB8 /* UILabel+Interaction.swift in Sources */, - B80C6B5B2384C7F900FDBC8B /* DeviceNameModalDelegate.swift in Sources */, C3550A03255DD6D900194B6A /* AppDelegate+OpenGroupAPI.swift in Sources */, 340FC8B8204DAC8D007AEB0F /* AddToGroupViewController.m in Sources */, B893063F2383961A005EAA8E /* ScanQRCodeWrapperVC.swift in Sources */, @@ -6135,7 +5394,6 @@ 450DF2091E0DD2C6003D14BE /* UserNotificationsAdaptee.swift in Sources */, 34B6A907218B5241007C4606 /* TypingIndicatorCell.swift in Sources */, 4CFD151D22415AA400F2450F /* CallVideoHintView.swift in Sources */, - 34D1F0AB1F867BFC0066283D /* OWSContactOffersCell.m in Sources */, C3548F0824456AB6009433A8 /* UIView+Wrapping.swift in Sources */, B82B408A2399EC0600A248E7 /* FakeChatView.swift in Sources */, 343A65981FC4CFE7000477A1 /* ConversationScrollButton.m in Sources */, @@ -6146,29 +5404,22 @@ 45CD81EF1DC030E7004C9430 /* SyncPushTokensJob.swift in Sources */, 4CEB78C92178EBAB00F315D2 /* OWSSessionResetJobRecord.m in Sources */, 45794E861E00620000066731 /* CallUIAdapter.swift in Sources */, - 340FC8BA204DAC8D007AEB0F /* FingerprintViewScanController.m in Sources */, C396DAF32518408B00FF6DC5 /* Description.swift in Sources */, - 4585C4681ED8F8D200896AEA /* SafetyNumberConfirmationAlert.swift in Sources */, 450D19131F85236600970622 /* RemoteVideoView.m in Sources */, 34129B8621EF877A005457A8 /* LinkPreviewView.swift in Sources */, 34386A54207D271D009F5D9C /* NeverClearView.swift in Sources */, - B885D5F4233491AB00EE0D8E /* DeviceLinkingModal.swift in Sources */, 45DF5DF21DDB843F00C936C7 /* CompareSafetyNumbersActivity.swift in Sources */, 451166C01FD86B98000739BA /* AccountManager.swift in Sources */, B83F2B88240CB75A000A54AB /* UIImage+Scaling.swift in Sources */, 3430FE181F7751D4000EC51B /* GiphyAPI.swift in Sources */, 4C2F454F214C00E1004871FF /* AvatarTableViewCell.swift in Sources */, 340FC8AA204DAC8D007AEB0F /* NotificationSettingsViewController.m in Sources */, - B80C6B592384C4E700FDBC8B /* DeviceNameModal.swift in Sources */, 4C090A1B210FD9C7001FD7F9 /* HapticFeedback.swift in Sources */, 3496744F2076ACD000080B5F /* LongTextViewController.swift in Sources */, - 34B3F8931E8DF1710035BE1A /* SignalsNavigationController.m in Sources */, 34F308A21ECB469700BB7697 /* OWSBezierPathView.m in Sources */, B886B4A92398BA1500211ABE /* QRCode.swift in Sources */, 3496955D219B605E00DCFE74 /* PhotoCollectionPickerController.swift in Sources */, - 3403B95D20EA9527001A1F44 /* OWSContactShareButtonsView.m in Sources */, 34B0796D1FCF46B100E248C2 /* MainAppContext.m in Sources */, - B80C6B572384A56D00FDBC8B /* DeviceLinksVC.swift in Sources */, 34A8B3512190A40E00218A25 /* MediaAlbumCellView.swift in Sources */, 34D1F0AE1F867BFC0066283D /* OWSMessageCell.m in Sources */, C396DAF42518408B00FF6DC5 /* Parser.swift in Sources */, @@ -6185,11 +5436,8 @@ B86BD08623399CEF000F5AE3 /* SeedModal.swift in Sources */, 34E3E5681EC4B19400495BAC /* AudioProgressView.swift in Sources */, 34D1F0521F7E8EA30066283D /* GiphyDownloader.swift in Sources */, - 340FC8BC204DAC8D007AEB0F /* FingerprintViewController.m in Sources */, 450DF2051E0D74AC003D14BE /* Platform.swift in Sources */, 4CC613362227A00400E21A3A /* ConversationSearch.swift in Sources */, - 340FC8B2204DAC8D007AEB0F /* AdvancedSettingsTableViewController.m in Sources */, - 452B999020A34B6B006F2F9E /* AddContactShareToExistingContactViewController.swift in Sources */, B82B408C239A068800A248E7 /* RegisterVC.swift in Sources */, 346129991FD1E4DA00532771 /* SignalApp.m in Sources */, 3496957121A301A100DCFE74 /* OWSBackupImportJob.m in Sources */, @@ -6199,7 +5447,6 @@ C31FFE57254A5FFE00F19441 /* KeyPairUtilities.swift in Sources */, 45C0DC1E1E69011F00E04C47 /* UIStoryboard+OWS.swift in Sources */, C3F0A62C255C9937007BE2A3 /* AppDelegate+SharedSenderKeys.swift in Sources */, - 452ECA4D1E087E7200E2F016 /* MessageFetcherJob.swift in Sources */, 45A6DAD61EBBF85500893231 /* ReminderView.swift in Sources */, 34D1F0881F8678AA0066283D /* ConversationViewLayout.m in Sources */, B82B408E239DC00D00A248E7 /* DisplayNameVC.swift in Sources */, @@ -6212,12 +5459,9 @@ 34D1F0B71F87F8850066283D /* OWSGenericAttachmentView.m in Sources */, 34D920E720E179C200D51158 /* OWSMessageFooterView.m in Sources */, 341341EF2187467A00192D59 /* ConversationViewModel.m in Sources */, - 348BB25D20A0C5530047AEC2 /* ContactShareViewHelper.swift in Sources */, - 34B3F8801E8DF1700035BE1A /* InviteFlow.swift in Sources */, B85357C523A1F13800AAF6CD /* LinkDeviceVC.swift in Sources */, 4C21D5D8223AC60F00EF8A77 /* PhotoCapture.swift in Sources */, C331FFF32558FF0300070591 /* PathStatusView.swift in Sources */, - 4C13C9F620E57BA30089A98B /* ColorPickerViewController.swift in Sources */, 4CC1ECFB211A553000CC13BE /* AppUpdateNag.swift in Sources */, 34B6A903218B3F63007C4606 /* TypingIndicatorView.swift in Sources */, C3F0A608255C98A6007BE2A3 /* Storage+SessionSnodeKit.swift in Sources */, @@ -6225,10 +5469,8 @@ 34B6A905218B4C91007C4606 /* TypingIndicatorInteraction.swift in Sources */, 2400888E239F30A600305217 /* SessionRestorationView.swift in Sources */, B886B4A72398B23E00211ABE /* QRCodeVC.swift in Sources */, - 4517642B1DE939FD00EDB8B9 /* ContactCell.swift in Sources */, 34EA69402194933900702471 /* MediaDownloadView.swift in Sources */, B8544E3323D50E4900299F14 /* AppearanceUtilities.swift in Sources */, - 340FC8AB204DAC8D007AEB0F /* DomainFrontingCountryViewController.m in Sources */, 4C586926224FAB83003FD070 /* AVAudioSession+OWS.m in Sources */, 3496744D2076768700080B5F /* OWSMessageBubbleView.m in Sources */, C3550A1D255DD73500194B6A /* Storage+SessionMessagingKit.swift in Sources */, @@ -6243,9 +5485,7 @@ 34ABC0E421DD20C500ED9469 /* ConversationMessageMapping.swift in Sources */, 34DBF003206BD5A500025978 /* OWSMessageTextView.m in Sources */, 34D1F0B41F86D31D0066283D /* ConversationCollectionView.m in Sources */, - 45D308AD2049A439000189E4 /* PinEntryView.m in Sources */, B85357C323A1BD1200AAF6CD /* SeedVC.swift in Sources */, - 340FC8B1204DAC8D007AEB0F /* BlockListViewController.m in Sources */, 45B5360E206DD8BB00D61655 /* UIResponder+OWS.swift in Sources */, 4CFE6B6C21F92BA700006701 /* LegacyNotificationsAdaptee.swift in Sources */, B8CCF6432397711F0091D419 /* SettingsVC.swift in Sources */, @@ -6253,13 +5493,11 @@ 3441FD9F21A3604F00BB9542 /* BackupRestoreViewController.swift in Sources */, 45F659821E1BE77000444429 /* NonCallKitCallUIAdaptee.swift in Sources */, 45AE48511E0732D6004D96C2 /* TurnServerInfo.swift in Sources */, - 34B3F8771E8DF1700035BE1A /* ContactsPicker.swift in Sources */, 45C0DC1B1E68FE9000E04C47 /* UIApplication+OWS.swift in Sources */, 45FBC5C81DF8575700E9B410 /* CallKitCallManager.swift in Sources */, 4539B5861F79348F007141FF /* PushRegistrationManager.swift in Sources */, 45FBC5D11DF8592E00E9B410 /* SignalCall.swift in Sources */, C396DAF22518408B00FF6DC5 /* NamedView.swift in Sources */, - 340FC8BB204DAC8D007AEB0F /* OWSAddToContactViewController.m in Sources */, 45F32C232057297A00A300D5 /* MediaPageViewController.swift in Sources */, 452C468F1E427E200087B011 /* OutboundCallInitiator.swift in Sources */, B82B4094239DF15900A248E7 /* ConversationTitleView.swift in Sources */, @@ -6273,7 +5511,6 @@ 45F659731E1BD99C00444429 /* CallKitCallUIAdaptee.swift in Sources */, 34277A5E20751BDC006049F2 /* OWSQuotedMessageView.m in Sources */, 458DE9D61DEE3FD00071BB03 /* PeerConnectionClient.swift in Sources */, - 45DDA6242090CEB500DE97F8 /* ConversationHeaderView.swift in Sources */, C3F0A5FE255C988A007BE2A3 /* Storage+Shared.swift in Sources */, B82B4090239DD75000A248E7 /* RestoreVC.swift in Sources */, 3488F9362191CC4000E524CC /* ConversationMediaView.swift in Sources */, @@ -6289,7 +5526,6 @@ 4C21D5D6223A9DC500EF8A77 /* UIAlerts+iOS9.m in Sources */, 34DBF004206BD5A500025978 /* OWSBubbleView.m in Sources */, 3496957021A301A100DCFE74 /* OWSBackupIO.m in Sources */, - 34E88D262098C5AE00A608F4 /* ContactViewController.swift in Sources */, 34AC0A23211C829F00997B47 /* OWSLabel.m in Sources */, 34EA69422194DE8000702471 /* MediaUploadView.swift in Sources */, 76EB054018170B33006006FC /* AppDelegate.m in Sources */, @@ -6299,7 +5535,6 @@ C3E7134F251C867C009649BB /* Sodium+Conversion.swift in Sources */, 340FC8B5204DAC8D007AEB0F /* AboutTableViewController.m in Sources */, C33100082558FF6D00070591 /* NewConversationButtonSet.swift in Sources */, - 340FC8B9204DAC8D007AEB0F /* UpdateGroupViewController.m in Sources */, B8BB82A5238F627000BA5194 /* HomeVC.swift in Sources */, C31A6C5A247F214E001123EF /* UIView+Glow.swift in Sources */, C31D1DE9252172D4005D4DA8 /* ContactUtilities.swift in Sources */, @@ -6313,7 +5548,6 @@ B85357C723A1FB5100AAF6CD /* LinkDeviceVCDelegate.swift in Sources */, C35E8AAE2485E51D00ACB629 /* IP2Country.swift in Sources */, 340FC8AE204DAC8D007AEB0F /* OWSSoundSettingsViewController.m in Sources */, - 4579431E1E7C8CE9008ED0C0 /* Pastelog.m in Sources */, 340FC8B0204DAC8D007AEB0F /* AddToBlockListViewController.m in Sources */, 3496957321A301A100DCFE74 /* OWSBackupJob.m in Sources */, 34C4E2572118957600BEA353 /* OWSWebRTCDataProtos.pb.swift in Sources */, @@ -6322,17 +5556,13 @@ 45E5A6991F61E6DE001E4A8A /* MarqueeLabel.swift in Sources */, 34D1F0B01F867BFC0066283D /* OWSSystemMessageCell.m in Sources */, 45A663C51F92EC760027B59E /* GroupTableViewCell.swift in Sources */, - B894D0712339D6F300B4D94D /* DeviceLinkingModalDelegate.swift in Sources */, - 34CA631B2097806F00E526A0 /* OWSContactShareView.m in Sources */, B86BD08423399ACF000F5AE3 /* Modal.swift in Sources */, 34D1F0861F8678AA0066283D /* ConversationViewController.m in Sources */, 3427C64320F500E000EEC730 /* OWSMessageTimerView.m in Sources */, B90418E6183E9DD40038554A /* DateUtil.m in Sources */, - 340FC8BD204DAC8D007AEB0F /* ShowGroupMembersViewController.m in Sources */, C33100092558FF6D00070591 /* UserCell.swift in Sources */, C3645350252449260045C478 /* VoiceMessageView.swift in Sources */, 3496956F21A301A100DCFE74 /* OWSBackupLazyRestore.swift in Sources */, - 459311FC1D75C948008DD4F0 /* OWSDeviceTableViewCell.m in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; diff --git a/Signal.xcodeproj/xcshareddata/xcschemes/Signal-Internal.xcscheme b/Signal.xcodeproj/xcshareddata/xcschemes/Signal-Internal.xcscheme deleted file mode 100644 index e0d5ca92c..000000000 --- a/Signal.xcodeproj/xcshareddata/xcschemes/Signal-Internal.xcscheme +++ /dev/null @@ -1,99 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/Signal.xcodeproj/xcshareddata/xcschemes/SignalMessaging.xcscheme b/Signal.xcodeproj/xcshareddata/xcschemes/SignalMessaging.xcscheme deleted file mode 100644 index 8362675e7..000000000 --- a/Signal.xcodeproj/xcshareddata/xcschemes/SignalMessaging.xcscheme +++ /dev/null @@ -1,67 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/SignalUtilitiesKit/AccountServiceClient.swift b/SignalUtilitiesKit/AccountServiceClient.swift deleted file mode 100644 index da9c5ee06..000000000 --- a/SignalUtilitiesKit/AccountServiceClient.swift +++ /dev/null @@ -1,34 +0,0 @@ -// -// Copyright (c) 2018 Open Whisper Systems. All rights reserved. -// - -import Foundation -import PromiseKit - -// TODO define actual type, and validate length -public typealias IdentityKey = Data - -/// based on libsignal-service-java's AccountManager class -@objc(SSKAccountServiceClient) -public class AccountServiceClient: NSObject { - - static var shared = AccountServiceClient() - - private let serviceClient: SignalServiceClient - - override init() { - self.serviceClient = SignalServiceRestClient() - } - - public func getPreKeysCount() -> Promise { - return serviceClient.getAvailablePreKeys() - } - - public func setPreKeys(identityKey: IdentityKey, signedPreKeyRecord: SignedPreKeyRecord, preKeyRecords: [PreKeyRecord]) -> Promise { - return serviceClient.registerPreKeys(identityKey: identityKey, signedPreKeyRecord: signedPreKeyRecord, preKeyRecords: preKeyRecords) - } - - public func setSignedPreKey(_ signedPreKey: SignedPreKeyRecord) -> Promise { - return serviceClient.setCurrentSignedPreKey(signedPreKey) - } -} diff --git a/SignalUtilitiesKit/AnyPromise+Conversion.swift b/SignalUtilitiesKit/AnyPromise+Conversion.swift deleted file mode 100644 index 1c15fc554..000000000 --- a/SignalUtilitiesKit/AnyPromise+Conversion.swift +++ /dev/null @@ -1,10 +0,0 @@ -import PromiseKit - -public extension AnyPromise { - - public static func from(_ promise: Promise) -> AnyPromise { - let result = AnyPromise(promise) - result.retainUntilComplete() - return result - } -} diff --git a/SignalUtilitiesKit/AppContext.h b/SignalUtilitiesKit/AppContext.h index c1674864c..f9327db8a 100755 --- a/SignalUtilitiesKit/AppContext.h +++ b/SignalUtilitiesKit/AppContext.h @@ -39,8 +39,7 @@ NSString *NSStringForUIApplicationState(UIApplicationState value); @property (nonatomic, readonly) BOOL isMainApp; @property (nonatomic, readonly) BOOL isMainAppAndActive; -/// Whether the app was woken up by a silent push notification. This is important for -/// determining whether attachments should be downloaded or not. +/// Whether the app was woken up by a silent push notification. This is important for determining whether attachments should be downloaded or not. @property (nonatomic) BOOL wasWokenUpByPushNotification; // Whether the user is using a right-to-left language like Arabic. diff --git a/SignalUtilitiesKit/AppContext.m b/SignalUtilitiesKit/AppContext.m index ece6f2865..39423c3cc 100755 --- a/SignalUtilitiesKit/AppContext.m +++ b/SignalUtilitiesKit/AppContext.m @@ -3,7 +3,6 @@ // #import "AppContext.h" -#import NS_ASSUME_NONNULL_BEGIN diff --git a/SignalUtilitiesKit/AppReadiness.m b/SignalUtilitiesKit/AppReadiness.m index 0153594ab..4424e177e 100755 --- a/SignalUtilitiesKit/AppReadiness.m +++ b/SignalUtilitiesKit/AppReadiness.m @@ -3,7 +3,6 @@ // #import "AppReadiness.h" -#import #import "AppContext.h" #import "SSKAsserts.h" diff --git a/SignalUtilitiesKit/AppSetup.m b/SignalUtilitiesKit/AppSetup.m index f50fa10c0..ee84e9235 100644 --- a/SignalUtilitiesKit/AppSetup.m +++ b/SignalUtilitiesKit/AppSetup.m @@ -8,25 +8,20 @@ #import #import #import -#import -#import + + #import #import -#import #import #import #import -#import -#import -#import #import #import #import #import #import -#import #import -#import + NS_ASSUME_NONNULL_BEGIN @@ -61,31 +56,17 @@ NS_ASSUME_NONNULL_BEGIN OWSPreferences *preferences = [OWSPreferences new]; - TSNetworkManager *networkManager = [[TSNetworkManager alloc] initDefault]; - OWSContactsManager *contactsManager = [[OWSContactsManager alloc] initWithPrimaryStorage:primaryStorage]; - ContactsUpdater *contactsUpdater = [ContactsUpdater new]; - OWSMessageSender *messageSender = [[OWSMessageSender alloc] initWithPrimaryStorage:primaryStorage]; - SSKMessageSenderJobQueue *messageSenderJobQueue = [SSKMessageSenderJobQueue new]; OWSProfileManager *profileManager = [[OWSProfileManager alloc] initWithPrimaryStorage:primaryStorage]; - OWSMessageManager *messageManager = [[OWSMessageManager alloc] initWithPrimaryStorage:primaryStorage]; OWSBlockingManager *blockingManager = [[OWSBlockingManager alloc] initWithPrimaryStorage:primaryStorage]; OWSIdentityManager *identityManager = [[OWSIdentityManager alloc] initWithPrimaryStorage:primaryStorage]; id udManager = [[OWSUDManagerImpl alloc] initWithPrimaryStorage:primaryStorage]; - OWSMessageDecrypter *messageDecrypter = [[OWSMessageDecrypter alloc] initWithPrimaryStorage:primaryStorage]; - OWSBatchMessageProcessor *batchMessageProcessor = - [[OWSBatchMessageProcessor alloc] initWithPrimaryStorage:primaryStorage]; - OWSMessageReceiver *messageReceiver = [[OWSMessageReceiver alloc] initWithPrimaryStorage:primaryStorage]; - TSSocketManager *socketManager = [[TSSocketManager alloc] init]; TSAccountManager *tsAccountManager = [[TSAccountManager alloc] initWithPrimaryStorage:primaryStorage]; - OWS2FAManager *ows2FAManager = [[OWS2FAManager alloc] initWithPrimaryStorage:primaryStorage]; OWSDisappearingMessagesJob *disappearingMessagesJob = [[OWSDisappearingMessagesJob alloc] initWithPrimaryStorage:primaryStorage]; - ContactDiscoveryService *contactDiscoveryService = [[ContactDiscoveryService alloc] initDefault]; OWSReadReceiptManager *readReceiptManager = [[OWSReadReceiptManager alloc] initWithPrimaryStorage:primaryStorage]; OWSOutgoingReceiptManager *outgoingReceiptManager = [[OWSOutgoingReceiptManager alloc] initWithPrimaryStorage:primaryStorage]; - OWSSyncManager *syncManager = [[OWSSyncManager alloc] initDefault]; id reachabilityManager = [SSKReachabilityManagerImpl new]; id typingIndicators = [[OWSTypingIndicatorsImpl alloc] init]; OWSAttachmentDownloads *attachmentDownloads = [[OWSAttachmentDownloads alloc] init]; @@ -101,31 +82,18 @@ NS_ASSUME_NONNULL_BEGIN sounds:sounds windowManager:windowManager]]; - [SSKEnvironment setShared:[[SSKEnvironment alloc] initWithContactsManager:contactsManager - messageSender:messageSender - messageSenderJobQueue:messageSenderJobQueue - profileManager:profileManager - primaryStorage:primaryStorage - contactsUpdater:contactsUpdater - networkManager:networkManager - messageManager:messageManager - blockingManager:blockingManager - identityManager:identityManager - udManager:udManager - messageDecrypter:messageDecrypter - batchMessageProcessor:batchMessageProcessor - messageReceiver:messageReceiver - socketManager:socketManager - tsAccountManager:tsAccountManager - ows2FAManager:ows2FAManager - disappearingMessagesJob:disappearingMessagesJob - contactDiscoveryService:contactDiscoveryService - readReceiptManager:readReceiptManager - outgoingReceiptManager:outgoingReceiptManager - reachabilityManager:reachabilityManager - syncManager:syncManager - typingIndicators:typingIndicators - attachmentDownloads:attachmentDownloads]]; + [SSKEnvironment setShared:[[SSKEnvironment alloc] initWithProfileManager:profileManager + primaryStorage:primaryStorage + blockingManager:blockingManager + identityManager:identityManager + udManager:udManager + tsAccountManager:tsAccountManager + disappearingMessagesJob:disappearingMessagesJob + readReceiptManager:readReceiptManager + outgoingReceiptManager:outgoingReceiptManager + reachabilityManager:reachabilityManager + typingIndicators:typingIndicators + attachmentDownloads:attachmentDownloads]]; appSpecificSingletonBlock(); diff --git a/SignalUtilitiesKit/Array+Description.swift b/SignalUtilitiesKit/Array+Description.swift deleted file mode 100644 index 3d58476dc..000000000 --- a/SignalUtilitiesKit/Array+Description.swift +++ /dev/null @@ -1,7 +0,0 @@ - -public extension Array where Element : CustomStringConvertible { - - public var prettifiedDescription: String { - return "[ " + map { $0.description }.joined(separator: ", ") + " ]" - } -} diff --git a/SignalUtilitiesKit/SignalAttachment.swift b/SignalUtilitiesKit/Attachments/SignalAttachment.swift similarity index 98% rename from SignalUtilitiesKit/SignalAttachment.swift rename to SignalUtilitiesKit/Attachments/SignalAttachment.swift index 0a9aa7385..d5eadc9e4 100644 --- a/SignalUtilitiesKit/SignalAttachment.swift +++ b/SignalUtilitiesKit/Attachments/SignalAttachment.swift @@ -244,16 +244,6 @@ public class SignalAttachment: NSObject { return errorDescription } -// @objc -// public func buildOutgoingAttachmentInfo(message: TSMessage) -> OutgoingAttachmentInfo { -// assert(message.uniqueId != nil) -// return OutgoingAttachmentInfo(dataSource: dataSource, -// contentType: mimeType, -// sourceFilename: filenameOrDefault, -// caption: captionText, -// albumMessageId: message.uniqueId) -// } - @objc public func staticThumbnail() -> UIImage? { if isAnimatedImage { diff --git a/SignalUtilitiesKit/TSAttachment.h b/SignalUtilitiesKit/Attachments/TSAttachment.h similarity index 100% rename from SignalUtilitiesKit/TSAttachment.h rename to SignalUtilitiesKit/Attachments/TSAttachment.h diff --git a/SignalUtilitiesKit/TSAttachment.m b/SignalUtilitiesKit/Attachments/TSAttachment.m similarity index 100% rename from SignalUtilitiesKit/TSAttachment.m rename to SignalUtilitiesKit/Attachments/TSAttachment.m diff --git a/SignalUtilitiesKit/TSAttachmentPointer.h b/SignalUtilitiesKit/Attachments/TSAttachmentPointer.h similarity index 100% rename from SignalUtilitiesKit/TSAttachmentPointer.h rename to SignalUtilitiesKit/Attachments/TSAttachmentPointer.h diff --git a/SignalUtilitiesKit/TSAttachmentPointer.m b/SignalUtilitiesKit/Attachments/TSAttachmentPointer.m similarity index 99% rename from SignalUtilitiesKit/TSAttachmentPointer.m rename to SignalUtilitiesKit/Attachments/TSAttachmentPointer.m index 7cbcb181d..fe2b1fb4a 100644 --- a/SignalUtilitiesKit/TSAttachmentPointer.m +++ b/SignalUtilitiesKit/Attachments/TSAttachmentPointer.m @@ -5,7 +5,7 @@ #import "TSAttachmentPointer.h" #import "OWSBackupFragment.h" #import "TSAttachmentStream.h" -#import +#import #import #import #import diff --git a/SignalUtilitiesKit/TSAttachmentStream.h b/SignalUtilitiesKit/Attachments/TSAttachmentStream.h similarity index 100% rename from SignalUtilitiesKit/TSAttachmentStream.h rename to SignalUtilitiesKit/Attachments/TSAttachmentStream.h diff --git a/SignalUtilitiesKit/TSAttachmentStream.m b/SignalUtilitiesKit/Attachments/TSAttachmentStream.m similarity index 100% rename from SignalUtilitiesKit/TSAttachmentStream.m rename to SignalUtilitiesKit/Attachments/TSAttachmentStream.m diff --git a/SignalUtilitiesKit/AvatarImageView.swift b/SignalUtilitiesKit/AvatarImageView.swift deleted file mode 100644 index b52a0d4af..000000000 --- a/SignalUtilitiesKit/AvatarImageView.swift +++ /dev/null @@ -1,264 +0,0 @@ -// -// Copyright (c) 2019 Open Whisper Systems. All rights reserved. -// - -import UIKit - -@objc -public class AvatarImageView: UIImageView { - private let shadowLayer = CAShapeLayer() - @objc var contactID: String = "" - - public init() { - super.init(frame: .zero) - self.initialize() - } - - override init(frame: CGRect) { - super.init(frame: frame) - self.initialize() - } - - required public init?(coder aDecoder: NSCoder) { - super.init(coder: aDecoder) - self.initialize() - } - - override init(image: UIImage?) { - super.init(image: image) - self.initialize() - } - - func initialize() { - // Loki: Used to indicate a contact's online status - layer.borderWidth = 2 - - // Loki: Observe online status changes - NotificationCenter.default.addObserver(self, selector: #selector(handleContactOnlineStatusChangedNotification), name: .contactOnlineStatusChanged, object: nil) - - // Set up UI - self.autoPinToSquareAspectRatio() - - self.layer.minificationFilter = .trilinear - self.layer.magnificationFilter = .trilinear - self.layer.masksToBounds = true - - self.layer.addSublayer(self.shadowLayer) - - self.contentMode = .scaleToFill - } - - override public func layoutSubviews() { - self.layer.cornerRadius = self.frame.size.width / 2 - - // Loki: Update the online status indicator if needed - updateOnlineStatusIndicator() - - // Inner shadow. - // This should usually not be visible; it is used to distinguish - // profile pics from the background if they are similar. - self.shadowLayer.frame = self.bounds - self.shadowLayer.masksToBounds = true - let shadowBounds = self.bounds - let shadowPath = UIBezierPath(ovalIn: shadowBounds) - // This can be any value large enough to cast a sufficiently large shadow. - let shadowInset: CGFloat = -3 - shadowPath.append(UIBezierPath(rect: shadowBounds.insetBy(dx: shadowInset, dy: shadowInset))) - // This can be any color since the fill should be clipped. - self.shadowLayer.fillColor = UIColor.black.cgColor - self.shadowLayer.path = shadowPath.cgPath - self.shadowLayer.fillRule = .evenOdd - self.shadowLayer.shadowColor = (Theme.isDarkThemeEnabled ? UIColor.white : UIColor.black).cgColor - self.shadowLayer.shadowRadius = 0.5 - self.shadowLayer.shadowOpacity = 0.15 - self.shadowLayer.shadowOffset = .zero - } - - deinit { - NotificationCenter.default.removeObserver(self) - } - - @objc private func handleContactOnlineStatusChangedNotification(_ notification: Notification) { - let contactID = notification.object as! String - guard contactID == self.contactID else { return } - updateOnlineStatusIndicator() - } - - @objc func updateOnlineStatusIndicator() { - let color = UIColor.lokiGray() - let currentUserID = getUserHexEncodedPublicKey() - let isCurrentUser = (contactID == currentUserID) - layer.borderColor = isCurrentUser ? UIColor.clear.cgColor : color.cgColor - } -} - -/// Avatar View which updates itself as necessary when the profile, contact, or group picture changes. -@objc -public class ConversationAvatarImageView: AvatarImageView { - - let thread: TSThread - let diameter: UInt - let contactsManager: OWSContactsManager - - // nil if group avatar - let recipientId: String? - - // nil if contact avatar - let groupThreadId: String? - - required public init(thread: TSThread, diameter: UInt, contactsManager: OWSContactsManager) { - self.thread = thread - self.diameter = diameter - self.contactsManager = contactsManager - - switch thread { - case let contactThread as TSContactThread: - self.recipientId = contactThread.contactIdentifier() - self.groupThreadId = nil - case let groupThread as TSGroupThread: - self.recipientId = nil - self.groupThreadId = groupThread.uniqueId - default: - owsFailDebug("unexpected thread type: \(thread)") - self.recipientId = nil - self.groupThreadId = nil - } - - super.init(frame: .zero) - - if recipientId != nil { - self.contactID = recipientId! // Loki - NotificationCenter.default.addObserver(self, selector: #selector(handleOtherUsersProfileChanged(notification:)), name: NSNotification.Name(rawValue: kNSNotificationName_OtherUsersProfileDidChange), object: nil) - - NotificationCenter.default.addObserver(self, selector: #selector(handleSignalAccountsChanged(notification:)), name: NSNotification.Name.OWSContactsManagerSignalAccountsDidChange, object: nil) - } - - if groupThreadId != nil { - NotificationCenter.default.addObserver(self, selector: #selector(handleGroupAvatarChanged(notification:)), name: .TSGroupThreadAvatarChanged, object: nil) - } - - // TODO group avatar changed - self.updateImage() - } - - required public init?(coder aDecoder: NSCoder) { - notImplemented() - } - - @objc func handleSignalAccountsChanged(notification: Notification) { - Logger.debug("") - - // PERF: It would be nice if we could do this only if *this* user's SignalAccount changed, - // but currently this is only a course grained notification. - - self.updateImage() - } - - @objc func handleOtherUsersProfileChanged(notification: Notification) { - Logger.debug("") - - guard let changedRecipientId = notification.userInfo?[kNSNotificationKey_ProfileRecipientId] as? String else { - owsFailDebug("recipientId was unexpectedly nil") - return - } - - guard let recipientId = self.recipientId else { - // shouldn't call this for group threads - owsFailDebug("contactId was unexpectedly nil") - return - } - - guard recipientId == changedRecipientId else { - // not this avatar - return - } - - self.updateImage() - } - - @objc func handleGroupAvatarChanged(notification: Notification) { - Logger.debug("") - - guard let changedGroupThreadId = notification.userInfo?[TSGroupThread_NotificationKey_UniqueId] as? String else { - owsFailDebug("groupThreadId was unexpectedly nil") - return - } - - guard let groupThreadId = self.groupThreadId else { - // shouldn't call this for contact threads - owsFailDebug("groupThreadId was unexpectedly nil") - return - } - - guard groupThreadId == changedGroupThreadId else { - // not this avatar - return - } - - thread.reload() - - self.updateImage() - } - - public func updateImage() { - Logger.debug("updateImage") - - self.image = OWSAvatarBuilder.buildImage(thread: thread, diameter: diameter) - } -} - -@objc -public class AvatarImageButton: UIButton { - private let shadowLayer = CAShapeLayer() - - // MARK: - Button Overrides - - override public func layoutSubviews() { - super.layoutSubviews() - - layer.cornerRadius = frame.size.width / 2 - - // Inner shadow. - // This should usually not be visible; it is used to distinguish - // profile pics from the background if they are similar. - shadowLayer.frame = bounds - shadowLayer.masksToBounds = true - let shadowBounds = bounds - let shadowPath = UIBezierPath(ovalIn: shadowBounds) - // This can be any value large enough to cast a sufficiently large shadow. - let shadowInset: CGFloat = -3 - shadowPath.append(UIBezierPath(rect: shadowBounds.insetBy(dx: shadowInset, dy: shadowInset))) - // This can be any color since the fill should be clipped. - shadowLayer.fillColor = UIColor.black.cgColor - shadowLayer.path = shadowPath.cgPath - shadowLayer.fillRule = .evenOdd - shadowLayer.shadowColor = (Theme.isDarkThemeEnabled ? UIColor.white : UIColor.black).cgColor - shadowLayer.shadowRadius = 0.5 - shadowLayer.shadowOpacity = 0.15 - shadowLayer.shadowOffset = .zero - } - - override public func setImage(_ image: UIImage?, for state: UIControl.State) { - ensureViewConfigured() - super.setImage(image, for: state) - } - - // MARK: Private - - var hasBeenConfigured = false - func ensureViewConfigured() { - guard !hasBeenConfigured else { - return - } - hasBeenConfigured = true - - autoPinToSquareAspectRatio() - - layer.minificationFilter = .trilinear - layer.magnificationFilter = .trilinear - layer.masksToBounds = true - layer.addSublayer(shadowLayer) - - contentMode = .scaleToFill - } -} diff --git a/SignalUtilitiesKit/CDSQuote.h b/SignalUtilitiesKit/CDSQuote.h deleted file mode 100644 index 7ba472c3b..000000000 --- a/SignalUtilitiesKit/CDSQuote.h +++ /dev/null @@ -1,34 +0,0 @@ -// -// Copyright (c) 2018 Open Whisper Systems. All rights reserved. -// - -#import - -NS_ASSUME_NONNULL_BEGIN - -@interface CDSQuote : NSObject - -@property (nonatomic, readonly) uint16_t version; -@property (nonatomic, readonly) uint16_t signType; -@property (nonatomic, readonly) BOOL isSigLinkable; -@property (nonatomic, readonly) uint32_t gid; -@property (nonatomic, readonly) uint16_t qeSvn; -@property (nonatomic, readonly) uint16_t pceSvn; -@property (nonatomic, readonly) NSData *basename; -@property (nonatomic, readonly) NSData *cpuSvn; -@property (nonatomic, readonly) uint64_t flags; -@property (nonatomic, readonly) uint64_t xfrm; -@property (nonatomic, readonly) NSData *mrenclave; -@property (nonatomic, readonly) NSData *mrsigner; -@property (nonatomic, readonly) uint16_t isvProdId; -@property (nonatomic, readonly) uint16_t isvSvn; -@property (nonatomic, readonly) NSData *reportData; -@property (nonatomic, readonly) NSData *signature; - -+ (nullable CDSQuote *)parseQuoteFromData:(NSData *)quoteData; - -- (BOOL)isDebugQuote; - -@end - -NS_ASSUME_NONNULL_END diff --git a/SignalUtilitiesKit/CDSQuote.m b/SignalUtilitiesKit/CDSQuote.m deleted file mode 100644 index 08d6d59e6..000000000 --- a/SignalUtilitiesKit/CDSQuote.m +++ /dev/null @@ -1,192 +0,0 @@ -// -// Copyright (c) 2018 Open Whisper Systems. All rights reserved. -// - -#import "CDSQuote.h" -#import "ByteParser.h" -#import - -NS_ASSUME_NONNULL_BEGIN - -static const long SGX_FLAGS_INITTED = 0x0000000000000001L; -static const long SGX_FLAGS_DEBUG = 0x0000000000000002L; -static const long SGX_FLAGS_MODE64BIT = 0x0000000000000004L; -static const long __unused SGX_FLAGS_PROVISION_KEY = 0x0000000000000004L; -static const long __unused SGX_FLAGS_EINITTOKEN_KEY = 0x0000000000000004L; -static const long SGX_FLAGS_RESERVED = 0xFFFFFFFFFFFFFFC8L; -static const long __unused SGX_XFRM_LEGACY = 0x0000000000000003L; -static const long __unused SGX_XFRM_AVX = 0x0000000000000006L; -static const long SGX_XFRM_RESERVED = 0xFFFFFFFFFFFFFFF8L; - -#pragma mark - - -@interface CDSQuote () - -@property (nonatomic) uint16_t version; -@property (nonatomic) uint16_t signType; -@property (nonatomic) BOOL isSigLinkable; -@property (nonatomic) uint32_t gid; -@property (nonatomic) uint16_t qeSvn; -@property (nonatomic) uint16_t pceSvn; -@property (nonatomic) NSData *basename; -@property (nonatomic) NSData *cpuSvn; -@property (nonatomic) uint64_t flags; -@property (nonatomic) uint64_t xfrm; -@property (nonatomic) NSData *mrenclave; -@property (nonatomic) NSData *mrsigner; -@property (nonatomic) uint16_t isvProdId; -@property (nonatomic) uint16_t isvSvn; -@property (nonatomic) NSData *reportData; -@property (nonatomic) NSData *signature; - -@end - -#pragma mark - - -@implementation CDSQuote - -+ (nullable CDSQuote *)parseQuoteFromData:(NSData *)quoteData -{ - ByteParser *_Nullable parser = [[ByteParser alloc] initWithData:quoteData littleEndian:YES]; - - // NOTE: This version is separate from and does _NOT_ match the signature body entity version. - uint16_t version = parser.nextShort; - if (version < 1 || version > 2) { - OWSFailDebug(@"unexpected quote version: %d", (int)version); - return nil; - } - - uint16_t signType = parser.nextShort; - if ((signType & ~1) != 0) { - OWSFailDebug(@"invalid signType: %d", (int)signType); - return nil; - } - - BOOL isSigLinkable = signType == 1; - uint32_t gid = parser.nextInt; - uint16_t qeSvn = parser.nextShort; - - uint16_t pceSvn = 0; - if (version > 1) { - pceSvn = parser.nextShort; - } else { - if (![parser readZero:2]) { - OWSFailDebug(@"non-zero pceSvn."); - return nil; - } - } - - if (![parser readZero:4]) { - OWSFailDebug(@"non-zero xeid."); - return nil; - } - - NSData *_Nullable basename = [parser readBytes:32]; - if (!basename) { - OWSFailDebug(@"couldn't read basename."); - return nil; - } - - // report_body - - NSData *_Nullable cpuSvn = [parser readBytes:16]; - if (!cpuSvn) { - OWSFailDebug(@"couldn't read cpuSvn."); - return nil; - } - if (![parser readZero:4]) { - OWSFailDebug(@"non-zero misc_select."); - return nil; - } - if (![parser readZero:28]) { - OWSFailDebug(@"non-zero reserved1."); - return nil; - } - - uint64_t flags = parser.nextLong; - if ((flags & SGX_FLAGS_RESERVED) != 0 || (flags & SGX_FLAGS_INITTED) == 0 || (flags & SGX_FLAGS_MODE64BIT) == 0) { - OWSFailDebug(@"invalid flags."); - return nil; - } - - uint64_t xfrm = parser.nextLong; - if ((xfrm & SGX_XFRM_RESERVED) != 0) { - OWSFailDebug(@"invalid xfrm."); - return nil; - } - - NSData *_Nullable mrenclave = [parser readBytes:32]; - if (!mrenclave) { - OWSFailDebug(@"couldn't read mrenclave."); - return nil; - } - if (![parser readZero:32]) { - OWSFailDebug(@"non-zero reserved2."); - return nil; - } - NSData *_Nullable mrsigner = [parser readBytes:32]; - if (!mrsigner) { - OWSFailDebug(@"couldn't read mrsigner."); - return nil; - } - if (![parser readZero:96]) { - OWSFailDebug(@"non-zero reserved3."); - return nil; - } - uint16_t isvProdId = parser.nextShort; - uint16_t isvSvn = parser.nextShort; - if (![parser readZero:60]) { - OWSFailDebug(@"non-zero reserved4."); - return nil; - } - NSData *_Nullable reportData = [parser readBytes:64]; - if (!reportData) { - OWSFailDebug(@"couldn't read reportData."); - return nil; - } - - // quote signature - uint32_t signatureLength = parser.nextInt; - if (signatureLength != quoteData.length - 436) { - OWSFailDebug(@"invalid signatureLength."); - return nil; - } - NSData *_Nullable signature = [parser readBytes:signatureLength]; - if (!signature) { - OWSFailDebug(@"couldn't read signature."); - return nil; - } - - if (parser.hasError) { - return nil; - } - - CDSQuote *quote = [CDSQuote new]; - quote.version = version; - quote.signType = signType; - quote.isSigLinkable = isSigLinkable; - quote.gid = gid; - quote.qeSvn = qeSvn; - quote.pceSvn = pceSvn; - quote.basename = basename; - quote.cpuSvn = cpuSvn; - quote.flags = flags; - quote.xfrm = xfrm; - quote.mrenclave = mrenclave; - quote.mrsigner = mrsigner; - quote.isvProdId = isvProdId; - quote.isvSvn = isvSvn; - quote.reportData = reportData; - quote.signature = signature; - - return quote; -} - -- (BOOL)isDebugQuote -{ - return (self.flags & SGX_FLAGS_DEBUG) != 0; -} - -@end - -NS_ASSUME_NONNULL_END diff --git a/SignalUtilitiesKit/CDSSigningCertificate.h b/SignalUtilitiesKit/CDSSigningCertificate.h deleted file mode 100644 index 3fd3ccf29..000000000 --- a/SignalUtilitiesKit/CDSSigningCertificate.h +++ /dev/null @@ -1,32 +0,0 @@ -// -// Copyright (c) 2018 Open Whisper Systems. All rights reserved. -// - -#import - -NS_ASSUME_NONNULL_BEGIN - -typedef NS_ENUM(NSUInteger, CDSSigningCertificateErrorCode) { - // AssertionError's indicate either developer or some serious system error that should never happen. - // - // Do not use this for an "expected" error, e.g. something that could be induced by user input which - // we specifically need to handle gracefull. - CDSSigningCertificateError_AssertionError = 1, - - CDSSigningCertificateError_InvalidPEMSupplied, - CDSSigningCertificateError_CouldNotExtractLeafCertificate, - CDSSigningCertificateError_InvalidDistinguishedName, - CDSSigningCertificateError_UntrustedCertificate -}; - -NSError *CDSSigningCertificateErrorMake(CDSSigningCertificateErrorCode code, NSString *localizedDescription); - -@interface CDSSigningCertificate : NSObject - -+ (nullable CDSSigningCertificate *)parseCertificateFromPem:(NSString *)certificatePem error:(NSError **)error; - -- (BOOL)verifySignatureOfBody:(NSString *)body signature:(NSData *)theirSignature; - -@end - -NS_ASSUME_NONNULL_END diff --git a/SignalUtilitiesKit/CDSSigningCertificate.m b/SignalUtilitiesKit/CDSSigningCertificate.m deleted file mode 100644 index edaa1e51b..000000000 --- a/SignalUtilitiesKit/CDSSigningCertificate.m +++ /dev/null @@ -1,391 +0,0 @@ -// -// Copyright (c) 2018 Open Whisper Systems. All rights reserved. -// - -#import "CDSSigningCertificate.h" -#import -#import -#import -#import - -NS_ASSUME_NONNULL_BEGIN - -NSError *CDSSigningCertificateErrorMake(CDSSigningCertificateErrorCode code, NSString *localizedDescription) -{ - return [NSError errorWithDomain:@"CDSSigningCertificate" - code:code - userInfo:@{ NSLocalizedDescriptionKey : localizedDescription }]; -} - -@interface CDSSigningCertificate () - -@property (nonatomic) SecPolicyRef policy; -@property (nonatomic) SecTrustRef trust; -@property (nonatomic) SecKeyRef publicKey; - -@end - -#pragma mark - - -@implementation CDSSigningCertificate - -- (instancetype)init -{ - if (self = [super init]) { - _policy = NULL; - _trust = NULL; - _publicKey = NULL; - } - - return self; -} - -- (void)dealloc -{ - if (_policy) { - CFRelease(_policy); - _policy = NULL; - } - if (_trust) { - CFRelease(_trust); - _trust = NULL; - } - if (_publicKey) { - CFRelease(_publicKey); - _publicKey = NULL; - } -} - -+ (nullable CDSSigningCertificate *)parseCertificateFromPem:(NSString *)certificatePem error:(NSError **)error -{ - OWSAssertDebug(certificatePem); - *error = nil; - - CDSSigningCertificate *signingCertificate = [CDSSigningCertificate new]; - - NSArray *_Nullable anchorCertificates = [self anchorCertificates]; - if (anchorCertificates.count < 1) { - OWSFailDebug(@"Could not load anchor certificates."); - *error = CDSSigningCertificateErrorMake( - CDSSigningCertificateError_AssertionError, @"Could not load anchor certificates."); - return nil; - } - - NSArray *_Nullable certificateDerDatas = [self convertPemToDer:certificatePem]; - - if (certificateDerDatas.count < 1) { - OWSFailDebug(@"Could not parse PEM."); - *error = CDSSigningCertificateErrorMake(CDSSigningCertificateError_InvalidPEMSupplied, @"Could not parse PEM."); - return nil; - } - - // The leaf is always the first certificate. - NSData *_Nullable leafCertificateData = [certificateDerDatas firstObject]; - if (!leafCertificateData) { - OWSFailDebug(@"Could not extract leaf certificate data."); - *error = CDSSigningCertificateErrorMake( - CDSSigningCertificateError_CouldNotExtractLeafCertificate, @"Could not extract leaf certificate data."); - return nil; - } - if (![self verifyDistinguishedNameOfCertificate:leafCertificateData]) { - *error = CDSSigningCertificateErrorMake( - CDSSigningCertificateError_InvalidDistinguishedName, @"Could not extract leaf certificate data."); - return nil; - } - - NSMutableArray *certificates = [NSMutableArray new]; - for (NSData *certificateDerData in certificateDerDatas) { - SecCertificateRef certificate = SecCertificateCreateWithData(NULL, (__bridge CFDataRef)(certificateDerData)); - if (!certificate) { - OWSFailDebug(@"Could not create SecCertificate."); - *error = CDSSigningCertificateErrorMake( - CDSSigningCertificateError_AssertionError, @"Could not create SecCertificate."); - return nil; - } - [certificates addObject:(__bridge_transfer id)certificate]; - } - - SecPolicyRef policy = SecPolicyCreateBasicX509(); - signingCertificate.policy = policy; - if (!policy) { - OWSFailDebug(@"Could not create policy."); - *error = CDSSigningCertificateErrorMake(CDSSigningCertificateError_AssertionError, @"Could not create policy."); - return nil; - } - - SecTrustRef trust; - OSStatus status = SecTrustCreateWithCertificates((__bridge CFTypeRef)certificates, policy, &trust); - signingCertificate.trust = trust; - if (status != errSecSuccess) { - OWSFailDebug(@"Creating trust did not succeed."); - *error = CDSSigningCertificateErrorMake( - CDSSigningCertificateError_AssertionError, @"Creating trust did not succeed."); - return nil; - } - if (!trust) { - OWSFailDebug(@"Could not create trust."); - *error = CDSSigningCertificateErrorMake(CDSSigningCertificateError_AssertionError, @"Could not create trust."); - return nil; - } - - status = SecTrustSetNetworkFetchAllowed(trust, NO); - if (status != errSecSuccess) { - OWSFailDebug(@"trust fetch could not be configured."); - *error = CDSSigningCertificateErrorMake( - CDSSigningCertificateError_AssertionError, @"trust fetch could not be configured."); - return nil; - } - - status = SecTrustSetAnchorCertificatesOnly(trust, YES); - if (status != errSecSuccess) { - OWSFailDebug(@"trust anchor certs could not be configured."); - *error = CDSSigningCertificateErrorMake( - CDSSigningCertificateError_AssertionError, @"trust anchor certs could not be configured."); - return nil; - } - - NSMutableArray *pinnedCertificates = [NSMutableArray array]; - for (NSData *certificateData in anchorCertificates) { - SecCertificateRef certificate = SecCertificateCreateWithData(NULL, (__bridge CFDataRef)(certificateData)); - if (!certificate) { - OWSFailDebug(@"Could not create pinned SecCertificate."); - *error = CDSSigningCertificateErrorMake( - CDSSigningCertificateError_AssertionError, @"Could not create pinned SecCertificate."); - return nil; - } - - [pinnedCertificates addObject:(__bridge_transfer id)certificate]; - } - status = SecTrustSetAnchorCertificates(trust, (__bridge CFArrayRef)pinnedCertificates); - if (status != errSecSuccess) { - OWSFailDebug(@"The anchor certificates couldn't be set."); - *error = CDSSigningCertificateErrorMake( - CDSSigningCertificateError_AssertionError, @"The anchor certificates couldn't be set."); - return nil; - } - - SecTrustResultType result; - status = SecTrustEvaluate(trust, &result); - if (status != errSecSuccess) { - OWSFailDebug(@"Could not evaluate certificates."); - *error = CDSSigningCertificateErrorMake( - CDSSigningCertificateError_AssertionError, @"Could not evaluate certificates."); - return nil; - } - - // `kSecTrustResultUnspecified` is confusingly named. It indicates success. - // See the comments in the header where it is defined. - BOOL isValid = (result == kSecTrustResultUnspecified || result == kSecTrustResultProceed); - if (!isValid) { - OWSFailDebug(@"Certificate was not trusted."); - *error = CDSSigningCertificateErrorMake( - CDSSigningCertificateError_UntrustedCertificate, @"Certificate was not trusted."); - return nil; - } - - SecKeyRef publicKey = SecTrustCopyPublicKey(trust); - signingCertificate.publicKey = publicKey; - if (!publicKey) { - OWSFailDebug(@"Could not extract public key."); - *error = CDSSigningCertificateErrorMake( - CDSSigningCertificateError_AssertionError, @"Could not extract public key."); - return nil; - } - - return signingCertificate; -} - -// PEM is just a series of blocks of base-64 encoded DER data. -// -// https://en.wikipedia.org/wiki/Privacy-Enhanced_Mail -+ (nullable NSArray *)convertPemToDer:(NSString *)pemString -{ - NSMutableArray *certificateDatas = [NSMutableArray new]; - - NSError *error; - // We use ? for non-greedy matching. - NSRegularExpression *_Nullable regex = [NSRegularExpression - regularExpressionWithPattern:@"-----BEGIN.*?-----(.+?)-----END.*?-----" - options:NSRegularExpressionCaseInsensitive | NSRegularExpressionDotMatchesLineSeparators - error:&error]; - if (!regex || error) { - OWSFailDebug(@"could parse regex: %@.", error); - return nil; - } - - [regex enumerateMatchesInString:pemString - options:0 - range:NSMakeRange(0, pemString.length) - usingBlock:^(NSTextCheckingResult *_Nullable result, NSMatchingFlags flags, BOOL *stop) { - if (result.numberOfRanges != 2) { - OWSFailDebug(@"invalid PEM regex match."); - return; - } - NSString *_Nullable derString = [pemString substringWithRange:[result rangeAtIndex:1]]; - if (derString.length < 1) { - OWSFailDebug(@"empty PEM match."); - return; - } - // dataFromBase64String will ignore whitespace, which is - // necessary. - NSData *_Nullable derData = [NSData dataFromBase64String:derString]; - if (derData.length < 1) { - return; - } - [certificateDatas addObject:derData]; - }]; - - return certificateDatas; -} - -+ (nullable NSArray *)anchorCertificates -{ - static NSArray *anchorCertificates = nil; - static dispatch_once_t onceToken; - dispatch_once(&onceToken, ^{ - // We need to use an Intel certificate as the anchor for IAS verification. - NSData *_Nullable anchorCertificate = [self certificateDataForService:@"ias-root"]; - if (!anchorCertificate) { - OWSFail(@"could not load anchor certificate."); - } else { - anchorCertificates = @[ anchorCertificate ]; - } - }); - return anchorCertificates; -} - -+ (nullable NSData *)certificateDataForService:(NSString *)service -{ - NSBundle *bundle = [NSBundle bundleForClass:self.class]; - NSString *path = [bundle pathForResource:service ofType:@"cer"]; - - if (![[NSFileManager defaultManager] fileExistsAtPath:path]) { - OWSFailDebug(@"could not locate certificate file."); - return nil; - } - - NSData *_Nullable certificateData = [NSData dataWithContentsOfFile:path]; - return certificateData; -} - -- (BOOL)verifySignatureOfBody:(NSString *)body signature:(NSData *)signature -{ - OWSAssertDebug(self.publicKey); - - NSData *bodyData = [body dataUsingEncoding:NSUTF8StringEncoding]; - - size_t signedHashBytesSize = SecKeyGetBlockSize(self.publicKey); - const void *signedHashBytes = [signature bytes]; - - NSData *_Nullable hashData = [Cryptography computeSHA256Digest:bodyData]; - if (hashData.length != CC_SHA256_DIGEST_LENGTH) { - OWSFailDebug(@"could not SHA256 for signature verification."); - return NO; - } - size_t hashBytesSize = CC_SHA256_DIGEST_LENGTH; - const void *hashBytes = [hashData bytes]; - - OSStatus status = SecKeyRawVerify( - self.publicKey, kSecPaddingPKCS1SHA256, hashBytes, hashBytesSize, signedHashBytes, signedHashBytesSize); - - BOOL isValid = status == errSecSuccess; - if (!isValid) { - return NO; - } - return YES; -} - -+ (BOOL)verifyDistinguishedNameOfCertificate:(NSData *)certificateData -{ - OWSAssertDebug(certificateData); - - // The Security framework doesn't offer access to certificate properties - // with API available on iOS 9. We use OpenSSL to extract the name. - NSDictionary *_Nullable properties = [self propertiesForCertificate:certificateData]; - if (!properties) { - OWSFailDebug(@"Could not retrieve certificate properties."); - return NO; - } - // NSString *expectedDistinguishedName - // = @"CN=Intel SGX Attestation Report Signing,O=Intel Corporation,L=Santa Clara,ST=CA,C=US"; - NSDictionary *expectedProperties = @{ - @(SN_commonName) : // "CN" - @"Intel SGX Attestation Report Signing", - @(SN_organizationName) : // "O" - @"Intel Corporation", - @(SN_localityName) : // "L" - @"Santa Clara", - @(SN_stateOrProvinceName) : // "ST" - @"CA", - @(SN_countryName) : // "C" - @"US", - }; - - if (![properties isEqualToDictionary:expectedProperties]) { - return NO; - } - return YES; -} - -+ (nullable NSDictionary *)propertiesForCertificate:(NSData *)certificateData -{ - OWSAssertDebug(certificateData); - - if (certificateData.length >= UINT32_MAX) { - OWSFailDebug(@"certificate data is too long."); - return nil; - } - const unsigned char *certificateDataBytes = (const unsigned char *)[certificateData bytes]; - X509 *_Nullable certificateX509 = d2i_X509(NULL, &certificateDataBytes, [certificateData length]); - if (!certificateX509) { - OWSFailDebug(@"could not parse certificate."); - return nil; - } - - X509_NAME *_Nullable subjectName = X509_get_subject_name(certificateX509); - if (!subjectName) { - OWSFailDebug(@"could not extract subject name."); - return nil; - } - - NSMutableDictionary *certificateProperties = [NSMutableDictionary new]; - for (NSString *oid in @[ - @(SN_commonName), // "CN" - @(SN_organizationName), // "O" - @(SN_localityName), // "L" - @(SN_stateOrProvinceName), // "ST" - @(SN_countryName), // "C" - ]) { - int nid = OBJ_txt2nid(oid.UTF8String); - int index = X509_NAME_get_index_by_NID(subjectName, nid, -1); - - X509_NAME_ENTRY *_Nullable entry = X509_NAME_get_entry(subjectName, index); - if (!entry) { - OWSFailDebug(@"could not extract entry."); - return nil; - } - - ASN1_STRING *_Nullable entryData = X509_NAME_ENTRY_get_data(entry); - if (!entryData) { - OWSFailDebug(@"could not extract entry data."); - return nil; - } - - unsigned char *entryName = ASN1_STRING_data(entryData); - if (entryName == NULL) { - OWSFailDebug(@"could not extract entry string."); - return nil; - } - NSString *_Nullable entryString = [NSString stringWithUTF8String:(char *)entryName]; - if (!entryString) { - OWSFailDebug(@"could not parse entry name data."); - return nil; - } - certificateProperties[oid] = entryString; - } - return certificateProperties; -} - -@end - -NS_ASSUME_NONNULL_END diff --git a/SignalUtilitiesKit/ClosedGroupParser.swift b/SignalUtilitiesKit/ClosedGroupParser.swift deleted file mode 100644 index 3118f569d..000000000 --- a/SignalUtilitiesKit/ClosedGroupParser.swift +++ /dev/null @@ -1,30 +0,0 @@ - -@objc public final class ClosedGroupParser : NSObject { - private let data: Data - - @objc public init(data: Data) { - self.data = data - } - - @objc public func parseGroupModels() -> [TSGroupModel] { - var index = 0 - var result: [TSGroupModel] = [] - while index < data.endIndex { - var uncheckedSize: UInt32? = try? data[index..<(index+4)].withUnsafeBytes { $0.pointee } - if let size = uncheckedSize, size >= data.count, let intermediate = try? data[index..<(index+4)].reversed() { - uncheckedSize = Data(intermediate).withUnsafeBytes { $0.pointee } - } - guard let size = uncheckedSize, size < data.count else { break } - let sizeAsInt = Int(size) - index += 4 - guard index + sizeAsInt <= data.count else { break } - let protoAsData = data[index..<(index+sizeAsInt)] - guard let proto = try? SSKProtoGroupDetails.parseData(protoAsData) else { break } - index += sizeAsInt - var groupModel = TSGroupModel(title: proto.name, memberIds: proto.members, image: nil, - groupId: proto.id, groupType: GroupType.closedGroup, adminIds: proto.admins) - result.append(groupModel) - } - return result - } -} diff --git a/SignalUtilitiesKit/ClosedGroupPoller.swift b/SignalUtilitiesKit/ClosedGroupPoller.swift index 0444b8656..01fb5f26a 100644 --- a/SignalUtilitiesKit/ClosedGroupPoller.swift +++ b/SignalUtilitiesKit/ClosedGroupPoller.swift @@ -61,7 +61,7 @@ public final class ClosedGroupPoller : NSObject { print("[Loki] Received \(messages.count) new message(s) in closed group with public key: \(publicKey).") } messages.forEach { json in - guard let envelope = SSKProtoEnvelope.from(json) else { return } + guard let envelope = SNProtoEnvelope.from(json) else { return } do { let data = try envelope.serializedData() let job = MessageReceiveJob(data: data) diff --git a/SignalUtilitiesKit/ClosedGroupUpdateMessage.swift b/SignalUtilitiesKit/ClosedGroupUpdateMessage.swift deleted file mode 100644 index 41006d707..000000000 --- a/SignalUtilitiesKit/ClosedGroupUpdateMessage.swift +++ /dev/null @@ -1,132 +0,0 @@ - -@objc(LKClosedGroupUpdateMessage) -internal final class ClosedGroupUpdateMessage : TSOutgoingMessage { - private let kind: Kind - - // MARK: Settings - @objc internal override var ttl: UInt32 { return UInt32(TTLUtilities.getTTL(for: .closedGroupUpdate)) } - - @objc internal override func shouldBeSaved() -> Bool { return false } - @objc internal override func shouldSyncTranscript() -> Bool { return false } - - // MARK: Kind - internal enum Kind { - case new(groupPublicKey: Data, name: String, groupPrivateKey: Data, senderKeys: [ClosedGroupSenderKey], members: [Data], admins: [Data]) - case info(groupPublicKey: Data, name: String, senderKeys: [ClosedGroupSenderKey], members: [Data], admins: [Data]) - case senderKeyRequest(groupPublicKey: Data) - case senderKey(groupPublicKey: Data, senderKey: ClosedGroupSenderKey) - } - - // MARK: Initialization - internal init(thread: TSThread, kind: Kind) { - self.kind = kind - super.init(outgoingMessageWithTimestamp: NSDate.ows_millisecondTimeStamp(), in: thread, messageBody: "", - attachmentIds: NSMutableArray(), expiresInSeconds: 0, expireStartedAt: 0, isVoiceMessage: false, - groupMetaMessage: .unspecified, quotedMessage: nil, contactShare: nil, linkPreview: nil) - } - - required init(dictionary: [String:Any]) throws { - preconditionFailure("Use init(thread:kind:) instead.") - } - - // MARK: Coding - internal required init?(coder: NSCoder) { - guard let thread = coder.decodeObject(forKey: "thread") as? TSThread, - let timestamp = coder.decodeObject(forKey: "timestamp") as? UInt64, - let groupPublicKey = coder.decodeObject(forKey: "groupPublicKey") as? Data, - let rawKind = coder.decodeObject(forKey: "kind") as? String else { return nil } - switch rawKind { - case "new": - guard let name = coder.decodeObject(forKey: "name") as? String, - let groupPrivateKey = coder.decodeObject(forKey: "groupPrivateKey") as? Data, - let senderKeys = coder.decodeObject(forKey: "senderKeys") as? [ClosedGroupSenderKey], - let members = coder.decodeObject(forKey: "members") as? [Data], - let admins = coder.decodeObject(forKey: "admins") as? [Data] else { return nil } - self.kind = .new(groupPublicKey: groupPublicKey, name: name, groupPrivateKey: groupPrivateKey, senderKeys: senderKeys, members: members, admins: admins) - case "info": - guard let name = coder.decodeObject(forKey: "name") as? String, - let senderKeys = coder.decodeObject(forKey: "senderKeys") as? [ClosedGroupSenderKey], - let members = coder.decodeObject(forKey: "members") as? [Data], - let admins = coder.decodeObject(forKey: "admins") as? [Data] else { return nil } - self.kind = .info(groupPublicKey: groupPublicKey, name: name, senderKeys: senderKeys, members: members, admins: admins) - case "senderKeyRequest": - self.kind = .senderKeyRequest(groupPublicKey: groupPublicKey) - case "senderKey": - guard let senderKeys = coder.decodeObject(forKey: "senderKeys") as? [ClosedGroupSenderKey], - let senderKey = senderKeys.first else { return nil } - self.kind = .senderKey(groupPublicKey: groupPublicKey, senderKey: senderKey) - default: return nil - } - super.init(outgoingMessageWithTimestamp: timestamp, in: thread, messageBody: "", - attachmentIds: NSMutableArray(), expiresInSeconds: 0, expireStartedAt: 0, isVoiceMessage: false, - groupMetaMessage: .unspecified, quotedMessage: nil, contactShare: nil, linkPreview: nil) - } - - internal override func encode(with coder: NSCoder) { - coder.encode(thread, forKey: "thread") - coder.encode(timestamp, forKey: "timestamp") - switch kind { - case .new(let groupPublicKey, let name, let groupPrivateKey, let senderKeys, let members, let admins): - coder.encode("new", forKey: "kind") - coder.encode(groupPublicKey, forKey: "groupPublicKey") - coder.encode(name, forKey: "name") - coder.encode(groupPrivateKey, forKey: "groupPrivateKey") - coder.encode(senderKeys, forKey: "senderKeys") - coder.encode(members, forKey: "members") - coder.encode(admins, forKey: "admins") - case .info(let groupPublicKey, let name, let senderKeys, let members, let admins): - coder.encode("info", forKey: "kind") - coder.encode(groupPublicKey, forKey: "groupPublicKey") - coder.encode(name, forKey: "name") - coder.encode(senderKeys, forKey: "senderKeys") - coder.encode(members, forKey: "members") - coder.encode(admins, forKey: "admins") - case .senderKeyRequest(let groupPublicKey): - coder.encode(groupPublicKey, forKey: "groupPublicKey") - case .senderKey(let groupPublicKey, let senderKey): - coder.encode("senderKey", forKey: "kind") - coder.encode(groupPublicKey, forKey: "groupPublicKey") - coder.encode([ senderKey ], forKey: "senderKeys") - } - } - - // MARK: Building - @objc internal override func dataMessageBuilder() -> Any? { - guard let builder = super.dataMessageBuilder() as? SSKProtoDataMessage.SSKProtoDataMessageBuilder else { return nil } - do { - let closedGroupUpdate: SSKProtoDataMessageClosedGroupUpdate.SSKProtoDataMessageClosedGroupUpdateBuilder - switch kind { - case .new(let groupPublicKey, let name, let groupPrivateKey, let senderKeys, let members, let admins): - closedGroupUpdate = SSKProtoDataMessageClosedGroupUpdate.builder(groupPublicKey: groupPublicKey, type: .new) - closedGroupUpdate.setName(name) - closedGroupUpdate.setGroupPrivateKey(groupPrivateKey) - closedGroupUpdate.setSenderKeys(try senderKeys.map { try $0.toProto() }) - closedGroupUpdate.setMembers(members) - closedGroupUpdate.setAdmins(admins) - case .info(let groupPublicKey, let name, let senderKeys, let members, let admins): - closedGroupUpdate = SSKProtoDataMessageClosedGroupUpdate.builder(groupPublicKey: groupPublicKey, type: .info) - closedGroupUpdate.setName(name) - closedGroupUpdate.setSenderKeys(try senderKeys.map { try $0.toProto() }) - closedGroupUpdate.setMembers(members) - closedGroupUpdate.setAdmins(admins) - case .senderKeyRequest(let groupPublicKey): - closedGroupUpdate = SSKProtoDataMessageClosedGroupUpdate.builder(groupPublicKey: groupPublicKey, type: .senderKeyRequest) - case .senderKey(let groupPublicKey, let senderKey): - closedGroupUpdate = SSKProtoDataMessageClosedGroupUpdate.builder(groupPublicKey: groupPublicKey, type: .senderKey) - closedGroupUpdate.setSenderKeys([ try senderKey.toProto() ]) - } - builder.setClosedGroupUpdate(try closedGroupUpdate.build()) - } catch { - owsFailDebug("Failed to build closed group update due to error: \(error).") - return nil - } - return builder - } -} - -private extension ClosedGroupSenderKey { - - func toProto() throws -> SSKProtoDataMessageClosedGroupUpdateSenderKey { - return try SSKProtoDataMessageClosedGroupUpdateSenderKey.builder(chainKey: chainKey, keyIndex: UInt32(keyIndex), publicKey: publicKey).build() - } -} diff --git a/SignalUtilitiesKit/ClosedGroupUtilities.swift b/SignalUtilitiesKit/ClosedGroupUtilities.swift deleted file mode 100644 index 4521eb444..000000000 --- a/SignalUtilitiesKit/ClosedGroupUtilities.swift +++ /dev/null @@ -1,70 +0,0 @@ -import CryptoSwift - - -@objc(LKClosedGroupUtilities) -public final class ClosedGroupUtilities : NSObject { - - @objc(LKSSKDecryptionError) - public class SSKDecryptionError : NSError { // Not called `Error` for Obj-C interoperablity - - @objc public static let invalidGroupPublicKey = SSKDecryptionError(domain: "SSKErrorDomain", code: 1, userInfo: [ NSLocalizedDescriptionKey : "Invalid group public key." ]) - @objc public static let noData = SSKDecryptionError(domain: "SSKErrorDomain", code: 2, userInfo: [ NSLocalizedDescriptionKey : "Received an empty envelope." ]) - @objc public static let noGroupPrivateKey = SSKDecryptionError(domain: "SSKErrorDomain", code: 3, userInfo: [ NSLocalizedDescriptionKey : "Missing group private key." ]) - @objc public static let selfSend = SSKDecryptionError(domain: "SSKErrorDomain", code: 4, userInfo: [ NSLocalizedDescriptionKey : "Message addressed at self." ]) - } - - @objc(encryptData:usingGroupPublicKey:transaction:error:) - public static func encrypt(data: Data, groupPublicKey: String, transaction: YapDatabaseReadWriteTransaction) throws -> Data { - // 1. ) Encrypt the data with the user's sender key - guard let userPublicKey = OWSIdentityManager.shared().identityKeyPair()?.hexEncodedPublicKey else { - throw SMKError.assertionError(description: "[Loki] Couldn't find user key pair.") - } - let ciphertextAndKeyIndex = try SharedSenderKeysImplementation.shared.encrypt(data, forGroupWithPublicKey: groupPublicKey, - senderPublicKey: userPublicKey, protocolContext: transaction) - let ivAndCiphertext = ciphertextAndKeyIndex[0] as! Data - let keyIndex = ciphertextAndKeyIndex[1] as! UInt - let encryptedMessage = ClosedGroupCiphertextMessage(_throws_withIVAndCiphertext: ivAndCiphertext, senderPublicKey: Data(hex: userPublicKey), keyIndex: UInt32(keyIndex)) - // 2. ) Encrypt the result for the group's public key to hide the sender public key and key index - let (ciphertext, _, ephemeralPublicKey) = try EncryptionUtilities.encrypt(encryptedMessage.serialized, using: groupPublicKey.removing05PrefixIfNeeded()) - // 3. ) Wrap the result - return try SSKProtoClosedGroupCiphertextMessageWrapper.builder(ciphertext: ciphertext, ephemeralPublicKey: ephemeralPublicKey).build().serializedData() - } - - @objc(decryptEnvelope:transaction:error:) - public static func decrypt(envelope: SSKProtoEnvelope, transaction: YapDatabaseReadWriteTransaction) throws -> [Any] { - let (plaintext, senderPublicKey) = try decrypt(envelope: envelope, transaction: transaction) - return [ plaintext, senderPublicKey ] - } - - public static func decrypt(envelope: SSKProtoEnvelope, transaction: YapDatabaseReadWriteTransaction) throws -> (plaintext: Data, senderPublicKey: String) { - // 1. ) Check preconditions - guard let groupPublicKey = envelope.source, SharedSenderKeysImplementation.shared.isClosedGroup(groupPublicKey) else { - throw SSKDecryptionError.invalidGroupPublicKey - } - guard let data = envelope.content else { - throw SSKDecryptionError.noData - } - guard let hexEncodedGroupPrivateKey = Storage.getClosedGroupPrivateKey(for: groupPublicKey) else { - throw SSKDecryptionError.noGroupPrivateKey - } - let groupPrivateKey = Data(hex: hexEncodedGroupPrivateKey) - // 2. ) Parse the wrapper - let wrapper = try SSKProtoClosedGroupCiphertextMessageWrapper.parseData(data) - let ivAndCiphertext = wrapper.ciphertext - let ephemeralPublicKey = wrapper.ephemeralPublicKey - // 3. ) Decrypt the data inside - let ephemeralSharedSecret = try Curve25519.generateSharedSecret(fromPublicKey: ephemeralPublicKey, privateKey: groupPrivateKey) - let salt = "LOKI" - let symmetricKey = try HMAC(key: salt.bytes, variant: .sha256).authenticate(ephemeralSharedSecret.bytes) - let closedGroupCiphertextMessageAsData = try DecryptionUtilities.decrypt(ivAndCiphertext, usingAESGCMWithSymmetricKey: Data(symmetricKey)) - // 4. ) Parse the closed group ciphertext message - let closedGroupCiphertextMessage = ClosedGroupCiphertextMessage(_throws_with: closedGroupCiphertextMessageAsData) - let senderPublicKey = closedGroupCiphertextMessage.senderPublicKey.toHexString() - guard senderPublicKey != getUserHexEncodedPublicKey() else { throw SSKDecryptionError.selfSend } - // 5. ) Use the info inside the closed group ciphertext message to decrypt the actual message content - let plaintext = try SharedSenderKeysImplementation.shared.decrypt(closedGroupCiphertextMessage.ivAndCiphertext, forGroupWithPublicKey: groupPublicKey, - senderPublicKey: senderPublicKey, keyIndex: UInt(closedGroupCiphertextMessage.keyIndex), protocolContext: transaction) - // 6. ) Return - return (plaintext, senderPublicKey) - } -} diff --git a/SignalUtilitiesKit/ClosedGroupsProtocol.swift b/SignalUtilitiesKit/ClosedGroupsProtocol.swift index 067bdfed6..7a458ae86 100644 --- a/SignalUtilitiesKit/ClosedGroupsProtocol.swift +++ b/SignalUtilitiesKit/ClosedGroupsProtocol.swift @@ -41,34 +41,27 @@ public final class ClosedGroupsProtocol : NSObject { // Generate a key pair for the group let groupKeyPair = Curve25519.generateKeyPair() let groupPublicKey = groupKeyPair.hexEncodedPublicKey // Includes the "05" prefix - // Ensure the current user's master device is the one that's included in the member list - members.remove(userPublicKey) - members.insert(UserDefaults.standard[.masterHexEncodedPublicKey] ?? userPublicKey) + // Ensure the current user is included in the member list + members.insert(userPublicKey) let membersAsData = members.map { Data(hex: $0) } - // Create ratchets for all members (and their linked devices) - var membersAndLinkedDevices: Set = members - for member in members { - let deviceLinks = OWSPrimaryStorage.shared().getDeviceLinks(for: member, in: transaction) - membersAndLinkedDevices.formUnion(deviceLinks.flatMap { [ $0.master.publicKey, $0.slave.publicKey ] }) - } - let senderKeys: [ClosedGroupSenderKey] = membersAndLinkedDevices.map { publicKey in - let ratchet = SharedSenderKeysImplementation.shared.generateRatchet(for: groupPublicKey, senderPublicKey: publicKey, using: transaction) + // Create ratchets for all members + let senderKeys: [ClosedGroupSenderKey] = members.map { publicKey in + let ratchet = SharedSenderKeys.generateRatchet(for: groupPublicKey, senderPublicKey: publicKey, using: transaction) return ClosedGroupSenderKey(chainKey: Data(hex: ratchet.chainKey), keyIndex: ratchet.keyIndex, publicKey: Data(hex: publicKey)) } // Create the group - let admins = [ UserDefaults.standard[.masterHexEncodedPublicKey] ?? userPublicKey ] + let admins = [ userPublicKey ] let adminsAsData = admins.map { Data(hex: $0) } let groupID = LKGroupUtilities.getEncodedClosedGroupIDAsData(groupPublicKey) let group = TSGroupModel(title: name, memberIds: [String](members), image: nil, groupId: groupID, groupType: .closedGroup, adminIds: admins) let thread = TSGroupThread.getOrCreateThread(with: group, transaction: transaction) thread.usesSharedSenderKeys = true thread.save(with: transaction) - SSKEnvironment.shared.profileManager.addThread(toProfileWhitelist: thread) // Establish sessions if needed - establishSessionsIfNeeded(with: [String](members), using: transaction) // Not `membersAndLinkedDevices` as this internally takes care of multi device already - // Send a closed group update message to all members (and their linked devices) using established channels + establishSessionsIfNeeded(with: [String](members), using: transaction) + // Send a closed group update message to all members using established channels var promises: [Promise] = [] - for member in members { // Not `membersAndLinkedDevices` as this internally takes care of multi device already + for member in members { guard member != userPublicKey else { continue } let thread = TSContactThread.getOrCreateThread(withContactId: member, transaction: transaction) thread.save(with: transaction) @@ -158,7 +151,7 @@ public final class ClosedGroupsProtocol : NSObject { MessageSender.send(closedGroupUpdate, in: thread, using: transaction) } // Send out the user's new ratchet to all members (minus the removed ones) using established channels - let userRatchet = SharedSenderKeysImplementation.shared.generateRatchet(for: groupPublicKey, senderPublicKey: userPublicKey, using: transaction) + let userRatchet = SharedSenderKeys.generateRatchet(for: groupPublicKey, senderPublicKey: userPublicKey, using: transaction) let userSenderKey = ClosedGroupSenderKey(chainKey: Data(hex: userRatchet.chainKey), keyIndex: userRatchet.keyIndex, publicKey: Data(hex: userPublicKey)) for member in members { guard member != userPublicKey else { continue } @@ -176,7 +169,7 @@ public final class ClosedGroupsProtocol : NSObject { seal.fulfill(()) // Generate ratchets for any new members newSenderKeys = newMembers.map { publicKey in - let ratchet = SharedSenderKeysImplementation.shared.generateRatchet(for: groupPublicKey, senderPublicKey: publicKey, using: transaction) + let ratchet = SharedSenderKeys.generateRatchet(for: groupPublicKey, senderPublicKey: publicKey, using: transaction) return ClosedGroupSenderKey(chainKey: Data(hex: ratchet.chainKey), keyIndex: ratchet.keyIndex, publicKey: Data(hex: publicKey)) } // Send a closed group update message to the existing members with the new members' ratchets (this message is aimed at the group) @@ -212,7 +205,7 @@ public final class ClosedGroupsProtocol : NSObject { let newGroupModel = TSGroupModel(title: name, memberIds: [String](members), image: nil, groupId: groupID, groupType: .closedGroup, adminIds: admins) thread.setGroupModel(newGroupModel, with: transaction) // Notify the user - let updateInfo = group.getInfoStringAboutUpdate(to: newGroupModel, contactsManager: SSKEnvironment.shared.contactsManager) + let updateInfo = group.getInfoStringAboutUpdate(to: newGroupModel) let infoMessage = TSInfoMessage(timestamp: NSDate.ows_millisecondTimeStamp(), in: thread, messageType: .typeGroupUpdate, customMessage: updateInfo) infoMessage.save(with: transaction) // Return @@ -227,7 +220,7 @@ public final class ClosedGroupsProtocol : NSObject { /// The returned promise is fulfilled when the group update message has been sent. It doesn't wait for the user's new ratchet to be distributed. public static func leave(_ groupPublicKey: String, using transaction: YapDatabaseReadWriteTransaction) -> Promise { - let userPublicKey = UserDefaults.standard[.masterHexEncodedPublicKey] ?? getUserHexEncodedPublicKey() + let userPublicKey = getUserHexEncodedPublicKey() let groupID = LKGroupUtilities.getEncodedClosedGroupIDAsData(groupPublicKey) guard let thread = TSGroupThread.fetch(uniqueId: TSGroupThread.threadId(fromGroupId: groupID), transaction: transaction) else { print("[Loki] Can't leave nonexistent closed group.") @@ -288,7 +281,7 @@ public final class ClosedGroupsProtocol : NSObject { let admins = closedGroupUpdate.admins.map { $0.toHexString() } // Persist the ratchets senderKeys.forEach { senderKey in - guard members.contains(senderKey.publicKey.toHexString()) else { return } // TODO: This currently doesn't take into account multi device + guard members.contains(senderKey.publicKey.toHexString()) else { return } let ratchet = ClosedGroupRatchet(chainKey: senderKey.chainKey.toHexString(), keyIndex: UInt(senderKey.keyIndex), messageKeys: []) Storage.setClosedGroupRatchet(for: groupPublicKey, senderPublicKey: senderKey.publicKey.toHexString(), ratchet: ratchet, using: transaction) } @@ -297,7 +290,7 @@ public final class ClosedGroupsProtocol : NSObject { let userPublicKey = getUserHexEncodedPublicKey() if missingSenderKeys.contains(userPublicKey) { establishSessionsIfNeeded(with: [String](members), using: transaction) - let userRatchet = SharedSenderKeysImplementation.shared.generateRatchet(for: groupPublicKey, senderPublicKey: userPublicKey, using: transaction) + let userRatchet = SharedSenderKeys.generateRatchet(for: groupPublicKey, senderPublicKey: userPublicKey, using: transaction) let userSenderKey = ClosedGroupSenderKey(chainKey: Data(hex: userRatchet.chainKey), keyIndex: userRatchet.keyIndex, publicKey: Data(hex: userPublicKey)) for member in members { guard member != userPublicKey else { continue } @@ -324,7 +317,6 @@ public final class ClosedGroupsProtocol : NSObject { thread.usesSharedSenderKeys = true thread.save(with: transaction) } - SSKEnvironment.shared.profileManager.addThread(toProfileWhitelist: thread) // Add the group to the user's set of public keys to poll for Storage.setClosedGroupPrivateKey(groupPrivateKey.toHexString(), for: groupPublicKey, using: transaction) // Notify the PN server @@ -333,7 +325,7 @@ public final class ClosedGroupsProtocol : NSObject { let infoMessage = TSInfoMessage(timestamp: NSDate.ows_millisecondTimeStamp(), in: thread, messageType: .typeGroupUpdate) infoMessage.save(with: transaction) // Establish sessions if needed - establishSessionsIfNeeded(with: members, using: transaction) // This internally takes care of multi device + establishSessionsIfNeeded(with: members, using: transaction) } /// Invoked upon receiving a group update. A group update is sent out when a group's name is changed, when new users are added, when users leave or are @@ -353,12 +345,7 @@ public final class ClosedGroupsProtocol : NSObject { } let group = thread.groupModel // Check that the sender is a member of the group (before the update) - var membersAndLinkedDevices: Set = Set(group.groupMemberIds) - for member in group.groupMemberIds { - let deviceLinks = OWSPrimaryStorage.shared().getDeviceLinks(for: member, in: transaction) - membersAndLinkedDevices.formUnion(deviceLinks.flatMap { [ $0.master.publicKey, $0.slave.publicKey ] }) - } - guard membersAndLinkedDevices.contains(senderPublicKey) else { + guard Set(group.groupMemberIds).contains(senderPublicKey) else { return print("[Loki] Ignoring closed group info message from non-member.") } // Store the ratchets for any new members (it's important that this happens before the code below) @@ -370,7 +357,7 @@ public final class ClosedGroupsProtocol : NSObject { // • Send out the user's new ratchet using established channels if other members of the group left or were removed // • Remove the group from the user's set of public keys to poll for if the current user was among the members that were removed let oldMembers = group.groupMemberIds - let userPublicKey = UserDefaults.standard[.masterHexEncodedPublicKey] ?? getUserHexEncodedPublicKey() + let userPublicKey = getUserHexEncodedPublicKey() let wasUserRemoved = !members.contains(userPublicKey) if Set(members).intersection(oldMembers) != Set(oldMembers) { let allOldRatchets = Storage.getAllClosedGroupRatchets(for: groupPublicKey) @@ -384,8 +371,8 @@ public final class ClosedGroupsProtocol : NSObject { // Notify the PN server let _ = LokiPushNotificationManager.performOperation(.unsubscribe, for: groupPublicKey, publicKey: userPublicKey) } else { - establishSessionsIfNeeded(with: members, using: transaction) // This internally takes care of multi device - let userRatchet = SharedSenderKeysImplementation.shared.generateRatchet(for: groupPublicKey, senderPublicKey: userPublicKey, using: transaction) + establishSessionsIfNeeded(with: members, using: transaction) + let userRatchet = SharedSenderKeys.generateRatchet(for: groupPublicKey, senderPublicKey: userPublicKey, using: transaction) let userSenderKey = ClosedGroupSenderKey(chainKey: Data(hex: userRatchet.chainKey), keyIndex: userRatchet.keyIndex, publicKey: Data(hex: userPublicKey)) for member in members { guard member != userPublicKey else { continue } @@ -401,10 +388,10 @@ public final class ClosedGroupsProtocol : NSObject { // Update the group let newGroupModel = TSGroupModel(title: name, memberIds: members, image: nil, groupId: groupID, groupType: .closedGroup, adminIds: admins) thread.setGroupModel(newGroupModel, with: transaction) - // Notify the user if needed (don't notify them if the message just contained linked device sender keys) + // Notify the user if needed if Set(members) != Set(oldMembers) || Set(admins) != Set(group.groupAdminIds) || name != group.groupName { let infoMessageType: TSInfoMessageType = wasUserRemoved ? .typeGroupQuit : .typeGroupUpdate - let updateInfo = group.getInfoStringAboutUpdate(to: newGroupModel, contactsManager: SSKEnvironment.shared.contactsManager) + let updateInfo = group.getInfoStringAboutUpdate(to: newGroupModel) let infoMessage = TSInfoMessage(timestamp: NSDate.ows_millisecondTimeStamp(), in: thread, messageType: infoMessageType, customMessage: updateInfo) infoMessage.save(with: transaction) } @@ -420,19 +407,15 @@ public final class ClosedGroupsProtocol : NSObject { } let group = groupThread.groupModel // Check that the requesting user is a member of the group - var membersAndLinkedDevices: Set = Set(group.groupMemberIds) - for member in group.groupMemberIds { - let deviceLinks = OWSPrimaryStorage.shared().getDeviceLinks(for: member, in: transaction) - membersAndLinkedDevices.formUnion(deviceLinks.flatMap { [ $0.master.publicKey, $0.slave.publicKey ] }) - } - guard membersAndLinkedDevices.contains(senderPublicKey) else { + let members = Set(group.groupMemberIds) + guard members.contains(senderPublicKey) else { return print("[Loki] Ignoring closed group sender key request from non-member.") } // Respond to the request print("[Loki] Responding to sender key request from: \(senderPublicKey).") - SessionManagementProtocol.sendSessionRequestIfNeeded(to: senderPublicKey, using: transaction) // This internally takes care of multi device + SessionManagementProtocol.sendSessionRequestIfNeeded(to: senderPublicKey, using: transaction) let userRatchet = Storage.getClosedGroupRatchet(for: groupPublicKey, senderPublicKey: userPublicKey) - ?? SharedSenderKeysImplementation.shared.generateRatchet(for: groupPublicKey, senderPublicKey: userPublicKey, using: transaction) + ?? SharedSenderKeys.generateRatchet(for: groupPublicKey, senderPublicKey: userPublicKey, using: transaction) let userSenderKey = ClosedGroupSenderKey(chainKey: Data(hex: userRatchet.chainKey), keyIndex: userRatchet.keyIndex, publicKey: Data(hex: userPublicKey)) let thread = TSContactThread.getOrCreateThread(withContactId: senderPublicKey, transaction: transaction) thread.save(with: transaction) @@ -471,11 +454,7 @@ public final class ClosedGroupsProtocol : NSObject { public static func shouldIgnoreClosedGroupMessage(_ dataMessage: SSKProtoDataMessage, in thread: TSGroupThread, wrappedIn envelope: SSKProtoEnvelope) -> Bool { guard thread.groupModel.groupType == .closedGroup else { return true } let publicKey = envelope.source! // Set during UD decryption - var result = false - Storage.read { transaction in - result = !thread.isUserMember(inGroup: publicKey, transaction: transaction) - } - return result + return !thread.isUserMember(inGroup: publicKey) } /// - Note: Deprecated. @@ -483,10 +462,6 @@ public final class ClosedGroupsProtocol : NSObject { public static func shouldIgnoreClosedGroupUpdateMessage(_ dataMessage: SSKProtoDataMessage, in thread: TSGroupThread, wrappedIn envelope: SSKProtoEnvelope) -> Bool { guard thread.groupModel.groupType == .closedGroup else { return true } let publicKey = envelope.source! // Set during UD decryption - var result = false - Storage.read { transaction in - result = !thread.isUserAdmin(inGroup: publicKey, transaction: transaction) - } - return result + return !thread.isUserAdmin(inGroup: publicKey) } } diff --git a/SignalUtilitiesKit/Contact.h b/SignalUtilitiesKit/Contact.h deleted file mode 100644 index ff7455f8d..000000000 --- a/SignalUtilitiesKit/Contact.h +++ /dev/null @@ -1,58 +0,0 @@ -// -// Copyright (c) 2018 Open Whisper Systems. All rights reserved. -// - -#import - -NS_ASSUME_NONNULL_BEGIN - -/** - * An adapter for the system contacts - */ - -@class CNContact; -@class PhoneNumber; -@class SignalRecipient; -@class UIImage; -@class YapDatabaseReadTransaction; - -@interface Contact : MTLModel - -@property (nullable, readonly, nonatomic) NSString *firstName; -@property (nullable, readonly, nonatomic) NSString *lastName; -@property (readonly, nonatomic) NSString *fullName; -@property (readonly, nonatomic) NSString *comparableNameFirstLast; -@property (readonly, nonatomic) NSString *comparableNameLastFirst; -@property (readonly, nonatomic) NSArray *parsedPhoneNumbers; -@property (readonly, nonatomic) NSArray *userTextPhoneNumbers; -@property (readonly, nonatomic) NSArray *emails; -@property (readonly, nonatomic) NSString *uniqueId; -@property (nonatomic, readonly) BOOL isSignalContact; -@property (nonatomic, readonly) NSString *cnContactId; - -- (NSArray *)signalRecipientsWithTransaction:(YapDatabaseReadTransaction *)transaction; -// TODO: Remove this method. -- (NSArray *)textSecureIdentifiers; - -#if TARGET_OS_IOS - -- (instancetype)initWithSystemContact:(CNContact *)cnContact NS_AVAILABLE_IOS(9_0); -+ (nullable Contact *)contactWithVCardData:(NSData *)data; -+ (nullable CNContact *)cnContactWithVCardData:(NSData *)data; - -- (NSString *)nameForPhoneNumber:(NSString *)recipientId; - -#endif // TARGET_OS_IOS - -+ (NSComparator)comparatorSortingNamesByFirstThenLast:(BOOL)firstNameOrdering; -+ (NSString *)formattedFullNameWithCNContact:(CNContact *)cnContact NS_SWIFT_NAME(formattedFullName(cnContact:)); -+ (nullable NSString *)localizedStringForCNLabel:(nullable NSString *)cnLabel; - -+ (CNContact *)mergeCNContact:(CNContact *)oldCNContact - newCNContact:(CNContact *)newCNContact NS_SWIFT_NAME(merge(cnContact:newCNContact:)); - -+ (nullable NSData *)avatarDataForCNContact:(nullable CNContact *)cnContact; - -@end - -NS_ASSUME_NONNULL_END diff --git a/SignalUtilitiesKit/Contact.m b/SignalUtilitiesKit/Contact.m deleted file mode 100644 index c16a43c85..000000000 --- a/SignalUtilitiesKit/Contact.m +++ /dev/null @@ -1,434 +0,0 @@ -// -// Copyright (c) 2019 Open Whisper Systems. All rights reserved. -// - -#import "Contact.h" -#import "OWSPrimaryStorage.h" -#import "PhoneNumber.h" -#import "SSKEnvironment.h" -#import "SignalRecipient.h" -#import "TSAccountManager.h" -#import -#import -#import - -@import Contacts; - -NS_ASSUME_NONNULL_BEGIN - -@interface Contact () - -@property (nonatomic, readonly) NSMutableDictionary *phoneNumberNameMap; -@property (nonatomic, readonly) NSUInteger imageHash; - -@end - -#pragma mark - - -@implementation Contact - -@synthesize comparableNameFirstLast = _comparableNameFirstLast; -@synthesize comparableNameLastFirst = _comparableNameLastFirst; - -#if TARGET_OS_IOS - -- (instancetype)initWithSystemContact:(CNContact *)cnContact -{ - self = [super init]; - if (!self) { - return self; - } - - _cnContactId = cnContact.identifier; - _firstName = cnContact.givenName.ows_stripped; - _lastName = cnContact.familyName.ows_stripped; - _fullName = [Contact formattedFullNameWithCNContact:cnContact]; - - NSMutableArray *phoneNumbers = [NSMutableArray new]; - NSMutableDictionary *phoneNumberNameMap = [NSMutableDictionary new]; - const NSUInteger kMaxPhoneNumbersConsidered = 50; - - NSArray *consideredPhoneNumbers; - if (cnContact.phoneNumbers.count <= kMaxPhoneNumbersConsidered) { - consideredPhoneNumbers = cnContact.phoneNumbers; - } else { - OWSLogInfo(@"For perf, only considering the first %lu phone numbers for contact with many numbers.", (unsigned long)kMaxPhoneNumbersConsidered); - consideredPhoneNumbers = [cnContact.phoneNumbers subarrayWithRange:NSMakeRange(0, kMaxPhoneNumbersConsidered)]; - } - for (CNLabeledValue *phoneNumberField in consideredPhoneNumbers) { - if ([phoneNumberField.value isKindOfClass:[CNPhoneNumber class]]) { - CNPhoneNumber *phoneNumber = (CNPhoneNumber *)phoneNumberField.value; - [phoneNumbers addObject:phoneNumber.stringValue]; - if ([phoneNumberField.label isEqualToString:CNLabelHome]) { - phoneNumberNameMap[phoneNumber.stringValue] - = NSLocalizedString(@"PHONE_NUMBER_TYPE_HOME", @"Label for 'Home' phone numbers."); - } else if ([phoneNumberField.label isEqualToString:CNLabelWork]) { - phoneNumberNameMap[phoneNumber.stringValue] - = NSLocalizedString(@"PHONE_NUMBER_TYPE_WORK", @"Label for 'Work' phone numbers."); - } else if ([phoneNumberField.label isEqualToString:CNLabelPhoneNumberiPhone]) { - phoneNumberNameMap[phoneNumber.stringValue] - = NSLocalizedString(@"PHONE_NUMBER_TYPE_IPHONE", @"Label for 'iPhone' phone numbers."); - } else if ([phoneNumberField.label isEqualToString:CNLabelPhoneNumberMobile]) { - phoneNumberNameMap[phoneNumber.stringValue] - = NSLocalizedString(@"PHONE_NUMBER_TYPE_MOBILE", @"Label for 'Mobile' phone numbers."); - } else if ([phoneNumberField.label isEqualToString:CNLabelPhoneNumberMain]) { - phoneNumberNameMap[phoneNumber.stringValue] - = NSLocalizedString(@"PHONE_NUMBER_TYPE_MAIN", @"Label for 'Main' phone numbers."); - } else if ([phoneNumberField.label isEqualToString:CNLabelPhoneNumberHomeFax]) { - phoneNumberNameMap[phoneNumber.stringValue] - = NSLocalizedString(@"PHONE_NUMBER_TYPE_HOME_FAX", @"Label for 'HomeFAX' phone numbers."); - } else if ([phoneNumberField.label isEqualToString:CNLabelPhoneNumberWorkFax]) { - phoneNumberNameMap[phoneNumber.stringValue] - = NSLocalizedString(@"PHONE_NUMBER_TYPE_WORK_FAX", @"Label for 'Work FAX' phone numbers."); - } else if ([phoneNumberField.label isEqualToString:CNLabelPhoneNumberOtherFax]) { - phoneNumberNameMap[phoneNumber.stringValue] - = NSLocalizedString(@"PHONE_NUMBER_TYPE_OTHER_FAX", @"Label for 'Other FAX' phone numbers."); - } else if ([phoneNumberField.label isEqualToString:CNLabelPhoneNumberPager]) { - phoneNumberNameMap[phoneNumber.stringValue] - = NSLocalizedString(@"PHONE_NUMBER_TYPE_PAGER", @"Label for 'Pager' phone numbers."); - } else if ([phoneNumberField.label isEqualToString:CNLabelOther]) { - phoneNumberNameMap[phoneNumber.stringValue] - = NSLocalizedString(@"PHONE_NUMBER_TYPE_OTHER", @"Label for 'Other' phone numbers."); - } else if (phoneNumberField.label.length > 0 && ![phoneNumberField.label hasPrefix:@"_$"]) { - // We'll reach this case for: - // - // * User-defined custom labels, which we want to display. - // * Labels like "_$!!$_", which I'm guessing are synced from other platforms. - // We don't want to display these labels. Even some of iOS' default labels (like Radio) show - // up this way. - phoneNumberNameMap[phoneNumber.stringValue] = phoneNumberField.label; - } - } - } - - _userTextPhoneNumbers = [phoneNumbers copy]; - _phoneNumberNameMap = [NSMutableDictionary new]; - _parsedPhoneNumbers = - [self parsedPhoneNumbersFromUserTextPhoneNumbers:phoneNumbers phoneNumberNameMap:phoneNumberNameMap]; - - NSMutableArray *emailAddresses = [NSMutableArray new]; - for (CNLabeledValue *emailField in cnContact.emailAddresses) { - if ([emailField.value isKindOfClass:[NSString class]]) { - [emailAddresses addObject:(NSString *)emailField.value]; - } - } - _emails = [emailAddresses copy]; - - NSData *_Nullable avatarData = [Contact avatarDataForCNContact:cnContact]; - if (avatarData) { - NSUInteger hashValue = 0; - NSData *_Nullable hashData = [Cryptography computeSHA256Digest:avatarData truncatedToBytes:sizeof(hashValue)]; - if (!hashData) { - OWSFailDebug(@"could not compute hash for avatar."); - } - [hashData getBytes:&hashValue length:sizeof(hashValue)]; - _imageHash = hashValue; - } else { - _imageHash = 0; - } - - return self; -} - -- (NSString *)uniqueId -{ - return self.cnContactId; -} - -+ (nullable Contact *)contactWithVCardData:(NSData *)data -{ - CNContact *_Nullable cnContact = [self cnContactWithVCardData:data]; - - if (!cnContact) { - OWSLogError(@"Could not parse vcard data."); - return nil; - } - - return [[self alloc] initWithSystemContact:cnContact]; -} - -#endif // TARGET_OS_IOS - -- (NSArray *)parsedPhoneNumbersFromUserTextPhoneNumbers:(NSArray *)userTextPhoneNumbers - phoneNumberNameMap:(nullable NSDictionary *) - phoneNumberNameMap -{ - OWSAssertDebug(self.phoneNumberNameMap); - - NSMutableDictionary *parsedPhoneNumberMap = [NSMutableDictionary new]; - NSMutableArray *parsedPhoneNumbers = [NSMutableArray new]; - for (NSString *phoneNumberString in userTextPhoneNumbers) { - for (PhoneNumber *phoneNumber in - [PhoneNumber tryParsePhoneNumbersFromsUserSpecifiedText:phoneNumberString - clientPhoneNumber:[TSAccountManager localNumber]]) { - [parsedPhoneNumbers addObject:phoneNumber]; - parsedPhoneNumberMap[phoneNumber.toE164] = phoneNumber; - NSString *phoneNumberName = phoneNumberNameMap[phoneNumberString]; - if (phoneNumberName) { - self.phoneNumberNameMap[phoneNumber.toE164] = phoneNumberName; - } - } - } - return [parsedPhoneNumbers sortedArrayUsingSelector:@selector(compare:)]; -} - -- (NSString *)comparableNameFirstLast { - if (_comparableNameFirstLast == nil) { - // Combine the two names with a tab separator, which has a lower ascii code than space, so that first names - // that contain a space ("Mary Jo\tCatlett") will sort after those that do not ("Mary\tOliver") - _comparableNameFirstLast = [self combineLeftName:_firstName withRightName:_lastName usingSeparator:@"\t"]; - } - - return _comparableNameFirstLast; -} - -- (NSString *)comparableNameLastFirst { - if (_comparableNameLastFirst == nil) { - // Combine the two names with a tab separator, which has a lower ascii code than space, so that last names - // that contain a space ("Van Der Beek\tJames") will sort after those that do not ("Van\tJames") - _comparableNameLastFirst = [self combineLeftName:_lastName withRightName:_firstName usingSeparator:@"\t"]; - } - - return _comparableNameLastFirst; -} - -- (NSString *)combineLeftName:(NSString *)leftName withRightName:(NSString *)rightName usingSeparator:(NSString *)separator { - const BOOL leftNameNonEmpty = (leftName.length > 0); - const BOOL rightNameNonEmpty = (rightName.length > 0); - - if (leftNameNonEmpty && rightNameNonEmpty) { - return [NSString stringWithFormat:@"%@%@%@", leftName, separator, rightName]; - } else if (leftNameNonEmpty) { - return [leftName copy]; - } else if (rightNameNonEmpty) { - return [rightName copy]; - } else { - return @""; - } -} - -- (NSString *)description { - return [NSString stringWithFormat:@"%@: %@", self.fullName, self.userTextPhoneNumbers]; -} - -- (BOOL)isSignalContact { - NSArray *identifiers = [self textSecureIdentifiers]; - - return [identifiers count] > 0; -} - -- (NSArray *)signalRecipientsWithTransaction:(YapDatabaseReadTransaction *)transaction -{ - __block NSMutableArray *result = [NSMutableArray array]; - - for (PhoneNumber *number in [self.parsedPhoneNumbers sortedArrayUsingSelector:@selector(compare:)]) { - SignalRecipient *_Nullable signalRecipient = [SignalRecipient registeredRecipientForRecipientId:number.toE164 - mustHaveDevices:YES - transaction:transaction]; - if (signalRecipient) { - [result addObject:signalRecipient]; - } - } - - return [result copy]; -} - -- (NSArray *)textSecureIdentifiers { - __block NSMutableArray *identifiers = [NSMutableArray array]; - - [OWSPrimaryStorage.dbReadConnection readWithBlock:^(YapDatabaseReadTransaction *transaction) { - for (PhoneNumber *number in self.parsedPhoneNumbers) { - if ([SignalRecipient isRegisteredRecipient:number.toE164 transaction:transaction]) { - [identifiers addObject:number.toE164]; - } - } - }]; - return [identifiers copy]; -} - -+ (NSComparator)comparatorSortingNamesByFirstThenLast:(BOOL)firstNameOrdering { - return ^NSComparisonResult(id obj1, id obj2) { - Contact *contact1 = (Contact *)obj1; - Contact *contact2 = (Contact *)obj2; - - if (firstNameOrdering) { - return [contact1.comparableNameFirstLast caseInsensitiveCompare:contact2.comparableNameFirstLast]; - } else { - return [contact1.comparableNameLastFirst caseInsensitiveCompare:contact2.comparableNameLastFirst]; - } - }; -} - -+ (NSString *)formattedFullNameWithCNContact:(CNContact *)cnContact -{ - return [CNContactFormatter stringFromContact:cnContact style:CNContactFormatterStyleFullName].ows_stripped; -} - -- (NSString *)nameForPhoneNumber:(NSString *)recipientId -{ - OWSAssertDebug(recipientId.length > 0); - OWSAssertDebug([self.textSecureIdentifiers containsObject:recipientId]); - - NSString *value = self.phoneNumberNameMap[recipientId]; - OWSAssertDebug(value); - if (!value) { - return NSLocalizedString(@"PHONE_NUMBER_TYPE_UNKNOWN", - @"Label used when we don't what kind of phone number it is (e.g. mobile/work/home)."); - } - return value; -} - -+ (nullable NSData *)avatarDataForCNContact:(nullable CNContact *)cnContact -{ - if (cnContact.thumbnailImageData) { - return cnContact.thumbnailImageData.copy; - } else if (cnContact.imageData) { - // This only occurs when sharing a contact via the share extension - return cnContact.imageData.copy; - } else { - return nil; - } -} - -// This method is used to de-bounce system contact fetch notifications -// by checking for changes in the contact data. -- (NSUInteger)hash -{ - // base hash is some arbitrary number - NSUInteger hash = 1825038313; - - hash = hash ^ self.fullName.hash; - - hash = hash ^ self.imageHash; - - for (PhoneNumber *phoneNumber in self.parsedPhoneNumbers) { - hash = hash ^ phoneNumber.toE164.hash; - } - - for (NSString *email in self.emails) { - hash = hash ^ email.hash; - } - - return hash; -} - -#pragma mark - CNContactConverters - -+ (nullable CNContact *)cnContactWithVCardData:(NSData *)data -{ - OWSAssertDebug(data); - - NSError *error; - NSArray *_Nullable contacts = [CNContactVCardSerialization contactsWithData:data error:&error]; - if (!contacts || error) { - OWSFailDebug(@"could not parse vcard: %@", error); - return nil; - } - if (contacts.count < 1) { - OWSFailDebug(@"empty vcard: %@", error); - return nil; - } - if (contacts.count > 1) { - OWSFailDebug(@"more than one contact in vcard: %@", error); - } - return contacts.firstObject; -} - -+ (CNContact *)mergeCNContact:(CNContact *)oldCNContact newCNContact:(CNContact *)newCNContact -{ - OWSAssertDebug(oldCNContact); - OWSAssertDebug(newCNContact); - - Contact *oldContact = [[Contact alloc] initWithSystemContact:oldCNContact]; - - CNMutableContact *_Nullable mergedCNContact = [oldCNContact mutableCopy]; - if (!mergedCNContact) { - OWSFailDebug(@"mergedCNContact was unexpectedly nil"); - return [CNContact new]; - } - - // Name - NSString *formattedFullName = [self.class formattedFullNameWithCNContact:mergedCNContact]; - - // merged all or nothing - do not try to piece-meal merge. - if (formattedFullName.length == 0) { - mergedCNContact.namePrefix = newCNContact.namePrefix.ows_stripped; - mergedCNContact.givenName = newCNContact.givenName.ows_stripped; - mergedCNContact.middleName = newCNContact.middleName.ows_stripped; - mergedCNContact.familyName = newCNContact.familyName.ows_stripped; - mergedCNContact.nameSuffix = newCNContact.nameSuffix.ows_stripped; - } - - if (mergedCNContact.organizationName.ows_stripped.length < 1) { - mergedCNContact.organizationName = newCNContact.organizationName.ows_stripped; - } - - // Phone Numbers - NSSet *existingParsedPhoneNumberSet = [NSSet setWithArray:oldContact.parsedPhoneNumbers]; - NSSet *existingUnparsedPhoneNumberSet = [NSSet setWithArray:oldContact.userTextPhoneNumbers]; - - NSMutableArray *> *mergedPhoneNumbers = [mergedCNContact.phoneNumbers mutableCopy]; - for (CNLabeledValue *labeledPhoneNumber in newCNContact.phoneNumbers) { - NSString *_Nullable unparsedPhoneNumber = labeledPhoneNumber.value.stringValue; - if ([existingUnparsedPhoneNumberSet containsObject:unparsedPhoneNumber]) { - // Skip phone number if "unparsed" form is a duplicate. - continue; - } - PhoneNumber *_Nullable parsedPhoneNumber = [PhoneNumber tryParsePhoneNumberFromUserSpecifiedText:labeledPhoneNumber.value.stringValue]; - if (parsedPhoneNumber && [existingParsedPhoneNumberSet containsObject:parsedPhoneNumber]) { - // Skip phone number if "parsed" form is a duplicate. - continue; - } - [mergedPhoneNumbers addObject:labeledPhoneNumber]; - } - mergedCNContact.phoneNumbers = mergedPhoneNumbers; - - // Emails - NSSet *existingEmailSet = [NSSet setWithArray:oldContact.emails]; - NSMutableArray *> *mergedEmailAddresses = [mergedCNContact.emailAddresses mutableCopy]; - for (CNLabeledValue *labeledEmail in newCNContact.emailAddresses) { - NSString *normalizedValue = labeledEmail.value.ows_stripped; - if (![existingEmailSet containsObject:normalizedValue]) { - [mergedEmailAddresses addObject:labeledEmail]; - } - } - mergedCNContact.emailAddresses = mergedEmailAddresses; - - // Address - // merged all or nothing - do not try to piece-meal merge. - if (mergedCNContact.postalAddresses.count == 0) { - mergedCNContact.postalAddresses = newCNContact.postalAddresses; - } - - // Avatar - if (!mergedCNContact.imageData) { - mergedCNContact.imageData = newCNContact.imageData; - } - - return [mergedCNContact copy]; -} - -+ (nullable NSString *)localizedStringForCNLabel:(nullable NSString *)cnLabel -{ - if (cnLabel.length == 0) { - return nil; - } - - NSString *_Nullable localizedLabel = [CNLabeledValue localizedStringForLabel:cnLabel]; - - // Docs for localizedStringForLabel say it returns: - // > The localized string if a Contacts framework defined label, otherwise just returns the label. - // But in practice, at least on iOS11, if the label is not one of CNContacts known labels (like CNLabelHome) - // kUnlocalizedStringLabel is returned, rather than the unadultered label. - NSString *const kUnlocalizedStringLabel = @"__ABUNLOCALIZEDSTRING"; - - if ([localizedLabel isEqual:kUnlocalizedStringLabel]) { - return cnLabel; - } - - return localizedLabel; -} - -@end - -NS_ASSUME_NONNULL_END diff --git a/SignalUtilitiesKit/ContactDiscoveryService.h b/SignalUtilitiesKit/ContactDiscoveryService.h deleted file mode 100644 index da414fbfd..000000000 --- a/SignalUtilitiesKit/ContactDiscoveryService.h +++ /dev/null @@ -1,65 +0,0 @@ -// -// Copyright (c) 2019 Open Whisper Systems. All rights reserved. -// - -#import - -NS_ASSUME_NONNULL_BEGIN - -extern NSErrorUserInfoKey const ContactDiscoveryServiceErrorKey_Reason; -extern NSErrorDomain const ContactDiscoveryServiceErrorDomain; -typedef NS_ERROR_ENUM(ContactDiscoveryServiceErrorDomain, ContactDiscoveryServiceError){ - ContactDiscoveryServiceErrorAttestationFailed = 100, ContactDiscoveryServiceErrorAssertionError = 101 -}; - -@class ECKeyPair; -@class OWSAES256Key; - -@interface RemoteAttestationAuth : NSObject - -@property (nonatomic, readonly) NSString *username; -@property (nonatomic, readonly) NSString *password; - -@end - -#pragma mark - - -@interface RemoteAttestationKeys : NSObject - -@property (nonatomic, readonly) ECKeyPair *keyPair; -@property (nonatomic, readonly) NSData *serverEphemeralPublic; -@property (nonatomic, readonly) NSData *serverStaticPublic; - -@property (nonatomic, readonly) OWSAES256Key *clientKey; -@property (nonatomic, readonly) OWSAES256Key *serverKey; - -@end - -#pragma mark - - -@interface RemoteAttestation : NSObject - -@property (nonatomic, readonly) RemoteAttestationKeys *keys; -@property (nonatomic, readonly) NSArray *cookies; -@property (nonatomic, readonly) NSData *requestId; -@property (nonatomic, readonly) NSString *enclaveId; -@property (nonatomic, readonly) RemoteAttestationAuth *auth; - -@end - -#pragma mark - - -@interface ContactDiscoveryService : NSObject - -- (instancetype)init NS_UNAVAILABLE; - -- (instancetype)initDefault NS_DESIGNATED_INITIALIZER; - -+ (instancetype)shared; - -- (void)testService; -- (void)performRemoteAttestationWithSuccess:(void (^)(RemoteAttestation *_Nonnull remoteAttestation))successHandler - failure:(void (^)(NSError *_Nonnull error))failureHandler; -@end - -NS_ASSUME_NONNULL_END diff --git a/SignalUtilitiesKit/ContactDiscoveryService.m b/SignalUtilitiesKit/ContactDiscoveryService.m deleted file mode 100644 index 4b51a5980..000000000 --- a/SignalUtilitiesKit/ContactDiscoveryService.m +++ /dev/null @@ -1,774 +0,0 @@ -// -// Copyright (c) 2019 Open Whisper Systems. All rights reserved. -// - -#import "ContactDiscoveryService.h" -#import "CDSQuote.h" -#import "CDSSigningCertificate.h" -#import "NSError+MessageSending.h" -#import "OWSError.h" -#import "OWSRequestFactory.h" -#import "SSKEnvironment.h" -#import "TSNetworkManager.h" -#import -#import -#import "SSKAsserts.h" -#import - -NS_ASSUME_NONNULL_BEGIN - -NSErrorUserInfoKey const ContactDiscoveryServiceErrorKey_Reason = @"ContactDiscoveryServiceErrorKey_Reason"; -NSErrorDomain const ContactDiscoveryServiceErrorDomain = @"SignalServiceKit.ContactDiscoveryService"; - -NSError *ContactDiscoveryServiceErrorMakeWithReason(NSInteger code, NSString *reason) -{ - OWSCFailDebug(@"Error: %@", reason); - - return [NSError errorWithDomain:ContactDiscoveryServiceErrorDomain - code:code - userInfo:@{ ContactDiscoveryServiceErrorKey_Reason : reason }]; -} - -@interface RemoteAttestationAuth () - -@property (nonatomic) NSString *username; -@property (nonatomic) NSString *password; - -@end - -#pragma mark - - -@implementation RemoteAttestationAuth - -@end - -#pragma mark - - -@interface RemoteAttestationKeys () - -@property (nonatomic) ECKeyPair *keyPair; -@property (nonatomic) NSData *serverEphemeralPublic; -@property (nonatomic) NSData *serverStaticPublic; - -@property (nonatomic) OWSAES256Key *clientKey; -@property (nonatomic) OWSAES256Key *serverKey; - -@end - -#pragma mark - - -@implementation RemoteAttestationKeys - -+ (nullable RemoteAttestationKeys *)keysForKeyPair:(ECKeyPair *)keyPair - serverEphemeralPublic:(NSData *)serverEphemeralPublic - serverStaticPublic:(NSData *)serverStaticPublic - error:(NSError **)error -{ - if (!keyPair) { - *error = ContactDiscoveryServiceErrorMakeWithReason( - ContactDiscoveryServiceErrorAssertionError, @"Missing keyPair"); - return nil; - } - if (serverEphemeralPublic.length < 1) { - *error = ContactDiscoveryServiceErrorMakeWithReason( - ContactDiscoveryServiceErrorAssertionError, @"Invalid serverEphemeralPublic"); - return nil; - } - if (serverStaticPublic.length < 1) { - *error = ContactDiscoveryServiceErrorMakeWithReason( - ContactDiscoveryServiceErrorAssertionError, @"Invalid serverStaticPublic"); - return nil; - } - RemoteAttestationKeys *keys = [RemoteAttestationKeys new]; - keys.keyPair = keyPair; - keys.serverEphemeralPublic = serverEphemeralPublic; - keys.serverStaticPublic = serverStaticPublic; - if (![keys deriveKeys]) { - *error = ContactDiscoveryServiceErrorMakeWithReason( - ContactDiscoveryServiceErrorAssertionError, @"failed to derive keys"); - return nil; - } - return keys; -} - -// Returns YES on success. -- (BOOL)deriveKeys -{ - NSData *ephemeralToEphemeral; - NSData *ephemeralToStatic; - @try { - ephemeralToEphemeral = - [Curve25519 throws_generateSharedSecretFromPublicKey:self.serverEphemeralPublic privateKey:self.keyPair.privateKey]; - ephemeralToStatic = - [Curve25519 throws_generateSharedSecretFromPublicKey:self.serverStaticPublic privateKey:self.keyPair.privateKey]; - } @catch (NSException *exception) { - OWSFailDebug(@"could not generate shared secrets: %@", exception); - return NO; - } - - NSData *masterSecret = [ephemeralToEphemeral dataByAppendingData:ephemeralToStatic]; - NSData *publicKeys = [NSData join:@[ - self.keyPair.publicKey, - self.serverEphemeralPublic, - self.serverStaticPublic, - ]]; - - NSData *_Nullable derivedMaterial; - @try { - derivedMaterial = - [HKDFKit deriveKey:masterSecret info:nil salt:publicKeys outputSize:(int)kAES256_KeyByteLength * 2]; - } @catch (NSException *exception) { - OWSFailDebug(@"could not derive service key: %@", exception); - return NO; - } - - if (!derivedMaterial) { - OWSFailDebug(@"missing derived service key."); - return NO; - } - if (derivedMaterial.length != kAES256_KeyByteLength * 2) { - OWSFailDebug(@"derived service key has unexpected length."); - return NO; - } - - NSData *_Nullable clientKeyData = - [derivedMaterial subdataWithRange:NSMakeRange(kAES256_KeyByteLength * 0, kAES256_KeyByteLength)]; - OWSAES256Key *_Nullable clientKey = [OWSAES256Key keyWithData:clientKeyData]; - if (!clientKey) { - OWSFailDebug(@"clientKey has unexpected length."); - return NO; - } - - NSData *_Nullable serverKeyData = - [derivedMaterial subdataWithRange:NSMakeRange(kAES256_KeyByteLength * 1, kAES256_KeyByteLength)]; - OWSAES256Key *_Nullable serverKey = [OWSAES256Key keyWithData:serverKeyData]; - if (!serverKey) { - OWSFailDebug(@"serverKey has unexpected length."); - return NO; - } - - self.clientKey = clientKey; - self.serverKey = serverKey; - - return YES; -} - -@end - -#pragma mark - - -@interface RemoteAttestation () - -@property (nonatomic) RemoteAttestationKeys *keys; -@property (nonatomic) NSArray *cookies; -@property (nonatomic) NSData *requestId; -@property (nonatomic) NSString *enclaveId; -@property (nonatomic) RemoteAttestationAuth *auth; - -@end - -#pragma mark - - -@implementation RemoteAttestation - -@end - -#pragma mark - - -@interface SignatureBodyEntity : NSObject - -@property (nonatomic) NSData *isvEnclaveQuoteBody; -@property (nonatomic) NSString *isvEnclaveQuoteStatus; -@property (nonatomic) NSString *timestamp; -@property (nonatomic) NSNumber *version; - -@end - -#pragma mark - - -@implementation SignatureBodyEntity - -@end - -#pragma mark - - -@interface NSDictionary (CDS) - -@end - -#pragma mark - - -@implementation NSDictionary (CDS) - -- (nullable NSString *)stringForKey:(NSString *)key -{ - NSString *_Nullable valueString = self[key]; - if (![valueString isKindOfClass:[NSString class]]) { - OWSFailDebug(@"couldn't parse string for key: %@", key); - return nil; - } - return valueString; -} - -- (nullable NSNumber *)numberForKey:(NSString *)key -{ - NSNumber *_Nullable value = self[key]; - if (![value isKindOfClass:[NSNumber class]]) { - OWSFailDebug(@"couldn't parse number for key: %@", key); - return nil; - } - return value; -} - -- (nullable NSData *)base64DataForKey:(NSString *)key -{ - NSString *_Nullable valueString = self[key]; - if (![valueString isKindOfClass:[NSString class]]) { - OWSFailDebug(@"couldn't parse base 64 value for key: %@", key); - return nil; - } - NSData *_Nullable valueData = [[NSData alloc] initWithBase64EncodedString:valueString options:0]; - if (!valueData) { - OWSFailDebug(@"couldn't decode base 64 value for key: %@", key); - return nil; - } - return valueData; -} - -- (nullable NSData *)base64DataForKey:(NSString *)key expectedLength:(NSUInteger)expectedLength -{ - NSData *_Nullable valueData = [self base64DataForKey:key]; - if (valueData && valueData.length != expectedLength) { - OWSLogDebug(@"decoded base 64 value for key: %@, has unexpected length: %lu != %lu", - key, - (unsigned long)valueData.length, - (unsigned long)expectedLength); - OWSFailDebug(@"decoded base 64 value for key has unexpected length: %lu != %lu", - (unsigned long)valueData.length, - (unsigned long)expectedLength); - return nil; - } - return valueData; -} - -@end - -#pragma mark - - -@implementation ContactDiscoveryService - -+ (instancetype)shared -{ - OWSAssertDebug(SSKEnvironment.shared.contactDiscoveryService); - - return SSKEnvironment.shared.contactDiscoveryService; -} - -- (instancetype)initDefault -{ - self = [super init]; - if (!self) { - return self; - } - - OWSSingletonAssert(); - - return self; -} - -- (void)testService -{ - dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ - [self - performRemoteAttestationWithSuccess:^(RemoteAttestation *remoteAttestation) { - OWSLogDebug(@"succeeded"); - } - failure:^(NSError *error) { - OWSLogDebug(@"failed with error: %@", error); - }]; - }); -} - -- (void)performRemoteAttestationWithSuccess:(void (^)(RemoteAttestation *remoteAttestation))successHandler - failure:(void (^)(NSError *error))failureHandler -{ - [self - getRemoteAttestationAuthWithSuccess:^(RemoteAttestationAuth *auth) { - [self performRemoteAttestationWithAuth:auth success:successHandler failure:failureHandler]; - } - failure:failureHandler]; -} - -- (void)getRemoteAttestationAuthWithSuccess:(void (^)(RemoteAttestationAuth *))successHandler - failure:(void (^)(NSError *error))failureHandler -{ - TSRequest *request = [OWSRequestFactory remoteAttestationAuthRequest]; - [[TSNetworkManager sharedManager] makeRequest:request - success:^(NSURLSessionDataTask *task, id responseDict) { - dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ - RemoteAttestationAuth *_Nullable auth = [self parseAuthParams:responseDict]; - if (!auth) { - OWSLogError(@"remote attestation auth could not be parsed: %@", responseDict); - NSError *error = OWSErrorMakeUnableToProcessServerResponseError(); - failureHandler(error); - return; - } - - successHandler(auth); - }); - } - failure:^(NSURLSessionDataTask *task, NSError *error) { - NSHTTPURLResponse *response = (NSHTTPURLResponse *)task.response; - OWSLogVerbose(@"remote attestation auth failure: %lu", (unsigned long)response.statusCode); - failureHandler(error); - }]; -} - -- (nullable RemoteAttestationAuth *)parseAuthParams:(id)response -{ - if (![response isKindOfClass:[NSDictionary class]]) { - return nil; - } - - NSDictionary *responseDict = response; - NSString *_Nullable password = [responseDict stringForKey:@"password"]; - if (password.length < 1) { - OWSFailDebug(@"missing or empty password."); - return nil; - } - - NSString *_Nullable username = [responseDict stringForKey:@"username"]; - if (username.length < 1) { - OWSFailDebug(@"missing or empty username."); - return nil; - } - - RemoteAttestationAuth *result = [RemoteAttestationAuth new]; - result.username = username; - result.password = password; - return result; -} - -- (void)performRemoteAttestationWithAuth:(RemoteAttestationAuth *)auth - success:(void (^)(RemoteAttestation *remoteAttestation))successHandler - failure:(void (^)(NSError *error))failureHandler -{ - return; // Loki: Do nothing - - ECKeyPair *keyPair = [Curve25519 generateKeyPair]; - - NSString *enclaveId = @"cd6cfc342937b23b1bdd3bbf9721aa5615ac9ff50a75c5527d441cd3276826c9"; - - TSRequest *request = [OWSRequestFactory remoteAttestationRequest:keyPair - enclaveId:enclaveId - authUsername:auth.username - authPassword:auth.password]; - - [[TSNetworkManager sharedManager] makeRequest:request - success:^(NSURLSessionDataTask *task, id responseJson) { - dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ - NSError *_Nullable error; - RemoteAttestation *_Nullable attestation = [self parseAttestationResponseJson:responseJson - response:task.response - keyPair:keyPair - enclaveId:enclaveId - auth:auth - error:&error]; - - if (!attestation) { - if (!error) { - OWSFailDebug(@"error was unexpectedly nil"); - error = ContactDiscoveryServiceErrorMakeWithReason(ContactDiscoveryServiceErrorAssertionError, - @"failure when parsing attestation - no reason given"); - } else { - OWSFailDebug(@"error with attestation: %@", error); - } - error.isRetryable = NO; - failureHandler(error); - return; - } - - successHandler(attestation); - }); - } - failure:^(NSURLSessionDataTask *task, NSError *error) { - failureHandler(error); - }]; -} - -- (nullable RemoteAttestation *)parseAttestationResponseJson:(id)responseJson - response:(NSURLResponse *)response - keyPair:(ECKeyPair *)keyPair - enclaveId:(NSString *)enclaveId - auth:(RemoteAttestationAuth *)auth - error:(NSError **)error -{ - OWSAssertDebug(responseJson); - OWSAssertDebug(response); - OWSAssertDebug(keyPair); - OWSAssertDebug(enclaveId.length > 0); - - if (![response isKindOfClass:[NSHTTPURLResponse class]]) { - *error = ContactDiscoveryServiceErrorMakeWithReason(ContactDiscoveryServiceErrorAssertionError, @"unexpected response type."); - return nil; - } - NSHTTPURLResponse *httpResponse = (NSHTTPURLResponse *)response; - NSArray *cookies = - [NSHTTPCookie cookiesWithResponseHeaderFields:httpResponse.allHeaderFields forURL:httpResponse.URL]; - if (cookies.count < 1) { - *error = ContactDiscoveryServiceErrorMakeWithReason( - ContactDiscoveryServiceErrorAssertionError, @"couldn't parse cookie."); - return nil; - } - - if (![responseJson isKindOfClass:[NSDictionary class]]) { - *error = ContactDiscoveryServiceErrorMakeWithReason( - ContactDiscoveryServiceErrorAssertionError, @"invalid json response"); - return nil; - } - NSDictionary *responseDict = responseJson; - NSData *_Nullable serverEphemeralPublic = - [responseDict base64DataForKey:@"serverEphemeralPublic" expectedLength:32]; - if (!serverEphemeralPublic) { - *error = ContactDiscoveryServiceErrorMakeWithReason( - ContactDiscoveryServiceErrorAssertionError, @"couldn't parse serverEphemeralPublic."); - return nil; - } - NSData *_Nullable serverStaticPublic = [responseDict base64DataForKey:@"serverStaticPublic" expectedLength:32]; - if (!serverStaticPublic) { - *error = ContactDiscoveryServiceErrorMakeWithReason( - ContactDiscoveryServiceErrorAssertionError, @"couldn't parse serverStaticPublic."); - return nil; - } - NSData *_Nullable encryptedRequestId = [responseDict base64DataForKey:@"ciphertext"]; - if (!encryptedRequestId) { - *error = ContactDiscoveryServiceErrorMakeWithReason( - ContactDiscoveryServiceErrorAssertionError, @"couldn't parse encryptedRequestId."); - return nil; - } - NSData *_Nullable encryptedRequestIv = [responseDict base64DataForKey:@"iv" expectedLength:12]; - if (!encryptedRequestIv) { - *error = ContactDiscoveryServiceErrorMakeWithReason( - ContactDiscoveryServiceErrorAssertionError, @"couldn't parse encryptedRequestIv."); - return nil; - } - NSData *_Nullable encryptedRequestTag = [responseDict base64DataForKey:@"tag" expectedLength:16]; - if (!encryptedRequestTag) { - *error = ContactDiscoveryServiceErrorMakeWithReason( - ContactDiscoveryServiceErrorAssertionError, @"couldn't parse encryptedRequestTag."); - return nil; - } - NSData *_Nullable quoteData = [responseDict base64DataForKey:@"quote"]; - if (!quoteData) { - *error = ContactDiscoveryServiceErrorMakeWithReason( - ContactDiscoveryServiceErrorAssertionError, @"couldn't parse quote data."); - return nil; - } - NSString *_Nullable signatureBody = [responseDict stringForKey:@"signatureBody"]; - if (![signatureBody isKindOfClass:[NSString class]]) { - *error = ContactDiscoveryServiceErrorMakeWithReason( - ContactDiscoveryServiceErrorAssertionError, @"couldn't parse signatureBody."); - return nil; - } - NSData *_Nullable signature = [responseDict base64DataForKey:@"signature"]; - if (!signature) { - *error = ContactDiscoveryServiceErrorMakeWithReason( - ContactDiscoveryServiceErrorAssertionError, @"couldn't parse signature."); - return nil; - } - NSString *_Nullable encodedCertificates = [responseDict stringForKey:@"certificates"]; - if (![encodedCertificates isKindOfClass:[NSString class]]) { - *error = ContactDiscoveryServiceErrorMakeWithReason( - ContactDiscoveryServiceErrorAssertionError, @"couldn't parse encodedCertificates."); - return nil; - } - NSString *_Nullable certificates = [encodedCertificates stringByRemovingPercentEncoding]; - if (!certificates) { - *error = ContactDiscoveryServiceErrorMakeWithReason( - ContactDiscoveryServiceErrorAssertionError, @"couldn't parse certificates."); - return nil; - } - - RemoteAttestationKeys *_Nullable keys = [RemoteAttestationKeys keysForKeyPair:keyPair - serverEphemeralPublic:serverEphemeralPublic - serverStaticPublic:serverStaticPublic - error:error]; - if (!keys || *error != nil) { - if (*error == nil) { - OWSFailDebug(@"missing error specifics"); - *error = ContactDiscoveryServiceErrorMakeWithReason( - ContactDiscoveryServiceErrorAssertionError, @"Couldn't derive keys. No reason given"); - } - return nil; - } - - CDSQuote *_Nullable quote = [CDSQuote parseQuoteFromData:quoteData]; - if (!quote) { - OWSFailDebug(@"couldn't parse quote."); - return nil; - } - NSData *_Nullable requestId = [self decryptRequestId:encryptedRequestId - encryptedRequestIv:encryptedRequestIv - encryptedRequestTag:encryptedRequestTag - keys:keys]; - if (!requestId) { - *error = ContactDiscoveryServiceErrorMakeWithReason( - ContactDiscoveryServiceErrorAssertionError, @"couldn't decrypt request id."); - return nil; - } - - if (![self verifyServerQuote:quote keys:keys enclaveId:enclaveId]) { - *error = ContactDiscoveryServiceErrorMakeWithReason( - ContactDiscoveryServiceErrorAttestationFailed, @"couldn't verify quote."); - return nil; - } - - if (![self verifyIasSignatureWithCertificates:certificates - signatureBody:signatureBody - signature:signature - quoteData:quoteData - error:error]) { - - if (*error == nil) { - OWSFailDebug(@"missing error specifics"); - *error = ContactDiscoveryServiceErrorMakeWithReason(ContactDiscoveryServiceErrorAssertionError, - @"verifyIasSignatureWithCertificates failed. No reason given"); - } - return nil; - } - - RemoteAttestation *result = [RemoteAttestation new]; - result.cookies = cookies; - result.keys = keys; - result.requestId = requestId; - result.enclaveId = enclaveId; - result.auth = auth; - - OWSLogVerbose(@"remote attestation complete."); - - return result; -} - -- (BOOL)verifyIasSignatureWithCertificates:(NSString *)certificates - signatureBody:(NSString *)signatureBody - signature:(NSData *)signature - quoteData:(NSData *)quoteData - error:(NSError **)error -{ - OWSAssertDebug(certificates.length > 0); - OWSAssertDebug(signatureBody.length > 0); - OWSAssertDebug(signature.length > 0); - OWSAssertDebug(quoteData); - - NSError *signingError; - CDSSigningCertificate *_Nullable certificate = - [CDSSigningCertificate parseCertificateFromPem:certificates error:&signingError]; - if (signingError) { - *error = signingError; - return NO; - } - - if (!certificate) { - *error = ContactDiscoveryServiceErrorMakeWithReason( - ContactDiscoveryServiceErrorAssertionError, @"could not parse signing certificate."); - return NO; - } - if (![certificate verifySignatureOfBody:signatureBody signature:signature]) { - *error = ContactDiscoveryServiceErrorMakeWithReason( - ContactDiscoveryServiceErrorAttestationFailed, @"could not verify signature."); - return NO; - } - - SignatureBodyEntity *_Nullable signatureBodyEntity = [self parseSignatureBodyEntity:signatureBody]; - if (!signatureBodyEntity) { - *error = ContactDiscoveryServiceErrorMakeWithReason( - ContactDiscoveryServiceErrorAssertionError, @"could not parse signature body."); - return NO; - } - - // Compare the first N bytes of the quote data with the signed quote body. - const NSUInteger kQuoteBodyComparisonLength = 432; - if (signatureBodyEntity.isvEnclaveQuoteBody.length < kQuoteBodyComparisonLength) { - *error = ContactDiscoveryServiceErrorMakeWithReason( - ContactDiscoveryServiceErrorAssertionError, @"isvEnclaveQuoteBody has unexpected length."); - return NO; - } - // NOTE: This version is separate from and does _NOT_ match the CDS quote version. - const NSUInteger kSignatureBodyVersion = 3; - if (![signatureBodyEntity.version isEqual:@(kSignatureBodyVersion)]) { - *error = ContactDiscoveryServiceErrorMakeWithReason( - ContactDiscoveryServiceErrorAssertionError, @"signatureBodyEntity has unexpected version."); - return NO; - } - if (quoteData.length < kQuoteBodyComparisonLength) { - *error = ContactDiscoveryServiceErrorMakeWithReason( - ContactDiscoveryServiceErrorAssertionError, @"quoteData has unexpected length."); - return NO; - } - NSData *isvEnclaveQuoteBodyForComparison = - [signatureBodyEntity.isvEnclaveQuoteBody subdataWithRange:NSMakeRange(0, kQuoteBodyComparisonLength)]; - NSData *quoteDataForComparison = [quoteData subdataWithRange:NSMakeRange(0, kQuoteBodyComparisonLength)]; - if (![isvEnclaveQuoteBodyForComparison ows_constantTimeIsEqualToData:quoteDataForComparison]) { - *error = ContactDiscoveryServiceErrorMakeWithReason( - ContactDiscoveryServiceErrorAttestationFailed, @"isvEnclaveQuoteBody and quoteData do not match."); - return NO; - } - - if (![@"OK" isEqualToString:signatureBodyEntity.isvEnclaveQuoteStatus]) { - NSString *reason = - [NSString stringWithFormat:@"invalid isvEnclaveQuoteStatus: %@", signatureBodyEntity.isvEnclaveQuoteStatus]; - *error = ContactDiscoveryServiceErrorMakeWithReason(ContactDiscoveryServiceErrorAttestationFailed, reason); - return NO; - } - - NSDateFormatter *dateFormatter = [[NSDateFormatter alloc] init]; - NSTimeZone *timeZone = [NSTimeZone timeZoneWithName:@"UTC"]; - [dateFormatter setTimeZone:timeZone]; - [dateFormatter setDateFormat:@"yyyy-MM-dd'T'HH:mm:ss.SSSSSS"]; - - // Specify parsing locale - // from: https://developer.apple.com/library/archive/qa/qa1480/_index.html - // Q: I'm using NSDateFormatter to parse an Internet-style date, but this fails for some users in some regions. - // I've set a specific date format string; shouldn't that force NSDateFormatter to work independently of the user's - // region settings? A: No. While setting a date format string will appear to work for most users, it's not the right - // solution to this problem. There are many places where format strings behave in unexpected ways. [...] - NSLocale *enUSPOSIXLocale = [NSLocale localeWithLocaleIdentifier:@"en_US_POSIX"]; - [dateFormatter setLocale:enUSPOSIXLocale]; - NSDate *timestampDate = [dateFormatter dateFromString:signatureBodyEntity.timestamp]; - if (!timestampDate) { - OWSFailDebug(@"Could not parse signature body timestamp: %@", signatureBodyEntity.timestamp); - *error = ContactDiscoveryServiceErrorMakeWithReason( - ContactDiscoveryServiceErrorAssertionError, @"could not parse signature body timestamp."); - return NO; - } - - // Only accept signatures from the last 24 hours. - NSDateComponents *dayComponent = [[NSDateComponents alloc] init]; - dayComponent.day = 1; - NSCalendar *calendar = [NSCalendar currentCalendar]; - NSDate *timestampDatePlus1Day = [calendar dateByAddingComponents:dayComponent toDate:timestampDate options:0]; - - NSDate *now = [NSDate new]; - BOOL isExpired = [now isAfterDate:timestampDatePlus1Day]; - - if (isExpired) { - OWSFailDebug(@"Signature is expired: %@", signatureBodyEntity.timestamp); - *error = ContactDiscoveryServiceErrorMakeWithReason( - ContactDiscoveryServiceErrorAttestationFailed, @"Signature is expired."); - return NO; - } - - return YES; -} - -- (nullable SignatureBodyEntity *)parseSignatureBodyEntity:(NSString *)signatureBody -{ - OWSAssertDebug(signatureBody.length > 0); - - NSError *error = nil; - NSDictionary *_Nullable jsonDict = - [NSJSONSerialization JSONObjectWithData:[signatureBody dataUsingEncoding:NSUTF8StringEncoding] - options:0 - error:&error]; - if (error || ![jsonDict isKindOfClass:[NSDictionary class]]) { - OWSFailDebug(@"could not parse signature body JSON: %@.", error); - return nil; - } - NSString *_Nullable timestamp = [jsonDict stringForKey:@"timestamp"]; - if (timestamp.length < 1) { - OWSFailDebug(@"could not parse signature timestamp."); - return nil; - } - NSData *_Nullable isvEnclaveQuoteBody = [jsonDict base64DataForKey:@"isvEnclaveQuoteBody"]; - if (isvEnclaveQuoteBody.length < 1) { - OWSFailDebug(@"could not parse signature isvEnclaveQuoteBody."); - return nil; - } - NSString *_Nullable isvEnclaveQuoteStatus = [jsonDict stringForKey:@"isvEnclaveQuoteStatus"]; - if (isvEnclaveQuoteStatus.length < 1) { - OWSFailDebug(@"could not parse signature isvEnclaveQuoteStatus."); - return nil; - } - NSNumber *_Nullable version = [jsonDict numberForKey:@"version"]; - if (!version) { - OWSFailDebug(@"could not parse signature version."); - return nil; - } - - SignatureBodyEntity *result = [SignatureBodyEntity new]; - result.isvEnclaveQuoteBody = isvEnclaveQuoteBody; - result.isvEnclaveQuoteStatus = isvEnclaveQuoteStatus; - result.timestamp = timestamp; - result.version = version; - return result; -} - -- (BOOL)verifyServerQuote:(CDSQuote *)quote keys:(RemoteAttestationKeys *)keys enclaveId:(NSString *)enclaveId -{ - OWSAssertDebug(quote); - OWSAssertDebug(keys); - OWSAssertDebug(enclaveId.length > 0); - - if (quote.reportData.length < keys.serverStaticPublic.length) { - OWSFailDebug(@"reportData has unexpected length: %lu != %lu.", - (unsigned long)quote.reportData.length, - (unsigned long)keys.serverStaticPublic.length); - return NO; - } - - NSData *_Nullable theirServerPublicStatic = - [quote.reportData subdataWithRange:NSMakeRange(0, keys.serverStaticPublic.length)]; - if (theirServerPublicStatic.length != keys.serverStaticPublic.length) { - OWSFailDebug(@"could not extract server public static."); - return NO; - } - if (![keys.serverStaticPublic ows_constantTimeIsEqualToData:theirServerPublicStatic]) { - OWSFailDebug(@"server public statics do not match."); - return NO; - } - // It's easier to compare as hex data than parsing hexadecimal. - NSData *_Nullable ourEnclaveIdHexData = [enclaveId dataUsingEncoding:NSUTF8StringEncoding]; - NSData *_Nullable theirEnclaveIdHexData = - [quote.mrenclave.hexadecimalString dataUsingEncoding:NSUTF8StringEncoding]; - if (!ourEnclaveIdHexData || !theirEnclaveIdHexData - || ![ourEnclaveIdHexData ows_constantTimeIsEqualToData:theirEnclaveIdHexData]) { - OWSFailDebug(@"enclave ids do not match."); - return NO; - } - if (quote.isDebugQuote) { - OWSFailDebug(@"quote has invalid isDebugQuote value."); - return NO; - } - return YES; -} - -- (nullable NSData *)decryptRequestId:(NSData *)encryptedRequestId - encryptedRequestIv:(NSData *)encryptedRequestIv - encryptedRequestTag:(NSData *)encryptedRequestTag - keys:(RemoteAttestationKeys *)keys -{ - OWSAssertDebug(encryptedRequestId.length > 0); - OWSAssertDebug(encryptedRequestIv.length > 0); - OWSAssertDebug(encryptedRequestTag.length > 0); - OWSAssertDebug(keys); - - OWSAES256Key *_Nullable key = keys.serverKey; - if (!key) { - OWSFailDebug(@"invalid server key."); - return nil; - } - NSData *_Nullable decryptedData = [Cryptography decryptAESGCMWithInitializationVector:encryptedRequestIv - ciphertext:encryptedRequestId - additionalAuthenticatedData:nil - authTag:encryptedRequestTag - key:key]; - if (!decryptedData) { - OWSFailDebug(@"couldn't decrypt request id."); - return nil; - } - return decryptedData; -} - -@end - -NS_ASSUME_NONNULL_END diff --git a/SignalUtilitiesKit/ContactFieldView.swift b/SignalUtilitiesKit/ContactFieldView.swift deleted file mode 100644 index 064565303..000000000 --- a/SignalUtilitiesKit/ContactFieldView.swift +++ /dev/null @@ -1,205 +0,0 @@ -// -// Copyright (c) 2018 Open Whisper Systems. All rights reserved. -// - -import Foundation - -public class ContactFieldView: UIView { - - @available(*, unavailable, message: "use other constructor instead.") - public required init?(coder aDecoder: NSCoder) { - notImplemented() - } - - public required init(rows: [UIView], hMargin: CGFloat) { - super.init(frame: CGRect.zero) - - self.layoutMargins = .zero - self.preservesSuperviewLayoutMargins = false - - addRows(rows: rows, hMargin: hMargin) - } - - private func addRows(rows: [UIView], hMargin: CGFloat) { - - var lastRow: UIView? - - let addSpacerRow = { - guard let prevRow = lastRow else { - owsFailDebug("missing last row") - return - } - let row = UIView() - row.backgroundColor = Theme.hairlineColor - self.addSubview(row) - row.autoSetDimension(.height, toSize: CGHairlineWidth()) - row.autoPinLeadingToSuperviewMargin(withInset: hMargin) - row.autoPinTrailingToSuperviewMargin() - row.autoPinEdge(.top, to: .bottom, of: prevRow, withOffset: 0) - lastRow = row - } - - let addRow: ((UIView) -> Void) = { (row) in - if lastRow != nil { - addSpacerRow() - } - self.addSubview(row) - row.autoPinLeadingToSuperviewMargin() - row.autoPinTrailingToSuperviewMargin() - if let lastRow = lastRow { - row.autoPinEdge(.top, to: .bottom, of: lastRow, withOffset: 0) - } else { - row.autoPinEdge(toSuperviewEdge: .top, withInset: 0) - } - lastRow = row - } - - for row in rows { - addRow(row) - } - - lastRow?.autoPinEdge(toSuperviewEdge: .bottom, withInset: 0) - } - - public class func contactFieldView(forAvatarImage avatarImage: UIImage, layoutMargins: UIEdgeInsets, actionBlock : (() -> Void)?) -> UIView { - var stackView: UIStackView - if let actionBlock = actionBlock { - stackView = TappableStackView(actionBlock: actionBlock) - } else { - stackView = UIStackView() - } - stackView.axis = .vertical - stackView.alignment = .leading - stackView.spacing = 3 - stackView.layoutMargins = layoutMargins - stackView.isLayoutMarginsRelativeArrangement = true - - let avatarView = AvatarImageView() - avatarView.image = avatarImage - let avatarSize = CGFloat(50) - avatarView.autoSetDimension(.width, toSize: avatarSize) - avatarView.autoSetDimension(.height, toSize: avatarSize) - avatarView.setCompressionResistanceHigh() - avatarView.setContentHuggingHigh() - stackView.addArrangedSubview(avatarView) - - return stackView - } - - public class func contactFieldView(forOrganizationName organizationName: String, layoutMargins: UIEdgeInsets) -> UIView { - return simpleFieldView(name: NSLocalizedString("CONTACT_FIELD_ORGANIZATION", - comment: "Label for the 'organization' field of a contact."), - value: organizationName, - layoutMargins: layoutMargins, actionBlock: nil) - } - - public class func contactFieldView(forPhoneNumber phoneNumber: OWSContactPhoneNumber, layoutMargins: UIEdgeInsets, actionBlock : (() -> Void)?) -> UIView { - let formattedPhoneNumber = PhoneNumber.bestEffortLocalizedPhoneNumber(withE164: phoneNumber.phoneNumber) - return simpleFieldView(name: phoneNumber.localizedLabel(), value: formattedPhoneNumber, layoutMargins: layoutMargins, actionBlock: actionBlock) - } - - public class func contactFieldView(forEmail email: OWSContactEmail, layoutMargins: UIEdgeInsets, actionBlock : (() -> Void)?) -> UIView { - return simpleFieldView(name: email.localizedLabel(), value: email.email, layoutMargins: layoutMargins, actionBlock: actionBlock) - } - - private class func simpleFieldView(name: String, value: String?, layoutMargins: UIEdgeInsets, actionBlock : (() -> Void)?) -> UIView { - var stackView: UIStackView - if let actionBlock = actionBlock { - stackView = TappableStackView(actionBlock: actionBlock) - } else { - stackView = UIStackView() - } - stackView.axis = .vertical - stackView.alignment = .leading - stackView.spacing = 3 - stackView.layoutMargins = layoutMargins - stackView.isLayoutMarginsRelativeArrangement = true - - let nameLabel = UILabel() - nameLabel.text = name.lowercased() - nameLabel.font = UIFont.ows_dynamicTypeSubheadline - nameLabel.textColor = Theme.secondaryColor - nameLabel.lineBreakMode = .byTruncatingTail - stackView.addArrangedSubview(nameLabel) - - let valueLabel = UILabel() - valueLabel.text = value - valueLabel.font = UIFont.ows_dynamicTypeBody - valueLabel.textColor = Theme.primaryColor - valueLabel.lineBreakMode = .byTruncatingTail - stackView.addArrangedSubview(valueLabel) - - return stackView - } - - public class func contactFieldView(forAddress address: OWSContactAddress, layoutMargins: UIEdgeInsets, actionBlock : (() -> Void)?) -> UIView { - var stackView: UIStackView - if let actionBlock = actionBlock { - stackView = TappableStackView(actionBlock: actionBlock) - } else { - stackView = UIStackView() - } - stackView.axis = .vertical - stackView.alignment = .leading - stackView.spacing = 3 - stackView.layoutMargins = layoutMargins - stackView.isLayoutMarginsRelativeArrangement = true - - let nameLabel = UILabel() - nameLabel.text = address.localizedLabel() - nameLabel.font = UIFont.ows_dynamicTypeSubheadline - nameLabel.textColor = Theme.secondaryColor - nameLabel.lineBreakMode = .byTruncatingTail - stackView.addArrangedSubview(nameLabel) - - let tryToAddNameValue: ((String, String?) -> Void) = { (propertyName, propertyValue) in - guard let propertyValue = propertyValue else { - return - } - guard propertyValue.count > 0 else { - return - } - - let row = UIStackView() - row.axis = .horizontal - row.alignment = .leading - row.spacing = 10 - row.layoutMargins = .zero - - let nameLabel = UILabel() - nameLabel.text = propertyName - nameLabel.font = UIFont.ows_dynamicTypeBody - nameLabel.textColor = Theme.secondaryColor - nameLabel.lineBreakMode = .byTruncatingTail - row.addArrangedSubview(nameLabel) - nameLabel.setContentHuggingHigh() - nameLabel.setCompressionResistanceHigh() - - let valueLabel = UILabel() - valueLabel.text = propertyValue - valueLabel.font = UIFont.ows_dynamicTypeBody - valueLabel.textColor = Theme.primaryColor - valueLabel.lineBreakMode = .byTruncatingTail - row.addArrangedSubview(valueLabel) - - stackView.addArrangedSubview(row) - } - - tryToAddNameValue(NSLocalizedString("CONTACT_FIELD_ADDRESS_STREET", comment: "Label for the 'street' field of a contact's address."), - address.street) - tryToAddNameValue(NSLocalizedString("CONTACT_FIELD_ADDRESS_POBOX", comment: "Label for the 'pobox' field of a contact's address."), - address.pobox) - tryToAddNameValue(NSLocalizedString("CONTACT_FIELD_ADDRESS_NEIGHBORHOOD", comment: "Label for the 'neighborhood' field of a contact's address."), - address.neighborhood) - tryToAddNameValue(NSLocalizedString("CONTACT_FIELD_ADDRESS_CITY", comment: "Label for the 'city' field of a contact's address."), - address.city) - tryToAddNameValue(NSLocalizedString("CONTACT_FIELD_ADDRESS_REGION", comment: "Label for the 'region' field of a contact's address."), - address.region) - tryToAddNameValue(NSLocalizedString("CONTACT_FIELD_ADDRESS_POSTCODE", comment: "Label for the 'postcode' field of a contact's address."), - address.postcode) - tryToAddNameValue(NSLocalizedString("CONTACT_FIELD_ADDRESS_COUNTRY", comment: "Label for the 'country' field of a contact's address."), - address.country) - - return stackView - } -} diff --git a/SignalUtilitiesKit/ContactParser.swift b/SignalUtilitiesKit/ContactParser.swift deleted file mode 100644 index 035bb580f..000000000 --- a/SignalUtilitiesKit/ContactParser.swift +++ /dev/null @@ -1,28 +0,0 @@ - -public final class ContactParser { - private let data: Data - - public init(data: Data) { - self.data = data - } - - public func parse() -> [(publicKey: String, isBlocked: Bool)] { - var index = 0 - var result: [(String, Bool)] = [] - while index < data.endIndex { - var uncheckedSize: UInt32? = try? data[index..<(index+4)].withUnsafeBytes { $0.pointee } - if let size = uncheckedSize, size >= data.count, let intermediate = try? data[index..<(index+4)].reversed() { - uncheckedSize = Data(intermediate).withUnsafeBytes { $0.pointee } - } - guard let size = uncheckedSize, size < data.count else { break } - let sizeAsInt = Int(size) - index += 4 - guard index + sizeAsInt <= data.count else { break } - let protoAsData = data[index..<(index+sizeAsInt)] - guard let proto = try? SSKProtoContactDetails.parseData(protoAsData) else { break } - index += sizeAsInt - result.append((publicKey: proto.number, isBlocked: proto.blocked)) - } - return result - } -} diff --git a/SignalUtilitiesKit/ContactShareApprovalViewController.swift b/SignalUtilitiesKit/ContactShareApprovalViewController.swift deleted file mode 100644 index 7b73f2ebc..000000000 --- a/SignalUtilitiesKit/ContactShareApprovalViewController.swift +++ /dev/null @@ -1,513 +0,0 @@ -// -// Copyright (c) 2019 Open Whisper Systems. All rights reserved. -// - -import Foundation - - -@objc -public protocol ContactShareApprovalViewControllerDelegate: class { - func approveContactShare(_ approveContactShare: ContactShareApprovalViewController, - didApproveContactShare contactShare: ContactShareViewModel) - func approveContactShare(_ approveContactShare: ContactShareApprovalViewController, - didCancelContactShare contactShare: ContactShareViewModel) -} - -protocol ContactShareField: class { - - var isAvatar: Bool { get } - - func localizedLabel() -> String - - func isIncluded() -> Bool - - func setIsIncluded(_ isIncluded: Bool) - - func applyToContact(contact: ContactShareViewModel) -} - -// MARK: - - -class ContactShareFieldBase: NSObject, ContactShareField { - - let value: ContactFieldType - - private var isIncludedFlag = true - - var isAvatar: Bool { return false } - - required init(_ value: ContactFieldType) { - self.value = value - - super.init() - } - - func localizedLabel() -> String { - return value.localizedLabel() - } - - func isIncluded() -> Bool { - return isIncludedFlag - } - - func setIsIncluded(_ isIncluded: Bool) { - isIncludedFlag = isIncluded - } - - func applyToContact(contact: ContactShareViewModel) { - preconditionFailure("This method must be overridden") - } -} - -// MARK: - - -class ContactSharePhoneNumber: ContactShareFieldBase { - - override func applyToContact(contact: ContactShareViewModel) { - assert(isIncluded()) - - var values = [OWSContactPhoneNumber]() - values += contact.phoneNumbers - values.append(value) - contact.phoneNumbers = values - } -} - -// MARK: - - -class ContactShareEmail: ContactShareFieldBase { - - override func applyToContact(contact: ContactShareViewModel) { - assert(isIncluded()) - - var values = [OWSContactEmail]() - values += contact.emails - values.append(value) - contact.emails = values - } -} - -// MARK: - - -class ContactShareAddress: ContactShareFieldBase { - - override func applyToContact(contact: ContactShareViewModel) { - assert(isIncluded()) - - var values = [OWSContactAddress]() - values += contact.addresses - values.append(value) - contact.addresses = values - } -} - -// Stub class so that avatars conform to OWSContactField. -class OWSContactAvatar: NSObject, OWSContactField { - - public let avatarImage: UIImage - public let avatarData: Data - - required init(avatarImage: UIImage, avatarData: Data) { - self.avatarImage = avatarImage - self.avatarData = avatarData - - super.init() - } - - public func ows_isValid() -> Bool { - return true - } - - public func localizedLabel() -> String { - return "" - } - - override public var debugDescription: String { - return "Avatar" - } -} - -class ContactShareAvatarField: ContactShareFieldBase { - override var isAvatar: Bool { return true } - - override func applyToContact(contact: ContactShareViewModel) { - assert(isIncluded()) - - contact.avatarImageData = value.avatarData - } -} - -// MARK: - - -protocol ContactShareFieldViewDelegate: class { - func contactShareFieldViewDidChangeSelectedState() -} - -// MARK: - - -class ContactShareFieldView: UIStackView { - - weak var delegate: ContactShareFieldViewDelegate? - - let field: ContactShareField - - let previewViewBlock : (() -> UIView) - - private var checkbox: UIButton! - - // MARK: - Initializers - - @available(*, unavailable, message: "use init(call:) constructor instead.") - required init(coder aDecoder: NSCoder) { - notImplemented() - } - - required init(field: ContactShareField, previewViewBlock : @escaping (() -> UIView), delegate: ContactShareFieldViewDelegate) { - self.field = field - self.previewViewBlock = previewViewBlock - self.delegate = delegate - - super.init(frame: CGRect.zero) - - self.isUserInteractionEnabled = true - self.addGestureRecognizer(UITapGestureRecognizer(target: self, action: #selector(wasTapped))) - - createContents() - } - - let hSpacing = CGFloat(10) - let hMargin = CGFloat(16) - - func createContents() { - self.axis = .horizontal - self.spacing = hSpacing - self.alignment = .center - self.layoutMargins = UIEdgeInsets(top: 0, left: hMargin, bottom: 0, right: hMargin) - self.isLayoutMarginsRelativeArrangement = true - - let checkbox = UIButton(type: .custom) - self.checkbox = checkbox - - let checkedIcon = #imageLiteral(resourceName: "contact_checkbox_checked") - let uncheckedIcon = #imageLiteral(resourceName: "contact_checkbox_unchecked") - checkbox.setImage(uncheckedIcon, for: .normal) - checkbox.setImage(checkedIcon, for: .selected) - checkbox.isSelected = field.isIncluded() - // Disable the checkbox; the entire row is hot. - checkbox.isUserInteractionEnabled = false - self.addArrangedSubview(checkbox) - checkbox.setCompressionResistanceHigh() - checkbox.setContentHuggingHigh() - - let previewView = previewViewBlock() - self.addArrangedSubview(previewView) - } - - @objc func wasTapped(sender: UIGestureRecognizer) { - Logger.info("") - - guard sender.state == .recognized else { - return - } - field.setIsIncluded(!field.isIncluded()) - checkbox.isSelected = field.isIncluded() - - delegate?.contactShareFieldViewDidChangeSelectedState() - } -} - -// MARK: - - -// TODO: Rename to ContactShareApprovalViewController -@objc -public class ContactShareApprovalViewController: OWSViewController, EditContactShareNameViewControllerDelegate, ContactShareFieldViewDelegate { - - weak var delegate: ContactShareApprovalViewControllerDelegate? - - let contactsManager: OWSContactsManager - - var contactShare: ContactShareViewModel - - var fieldViews = [ContactShareFieldView]() - - var nameLabel: UILabel! - - // MARK: Initializers - - @available(*, unavailable, message:"use other constructor instead.") - required public init?(coder aDecoder: NSCoder) { - notImplemented() - } - - @objc - required public init(contactShare: ContactShareViewModel, contactsManager: OWSContactsManager, delegate: ContactShareApprovalViewControllerDelegate) { - self.contactsManager = contactsManager - self.contactShare = contactShare - self.delegate = delegate - - super.init(nibName: nil, bundle: nil) - - buildFields() - } - - func buildFields() { - var fieldViews = [ContactShareFieldView]() - - let previewInsets = UIEdgeInsets(top: 5, left: 0, bottom: 5, right: 0) - - if let avatarData = contactShare.avatarImageData { - if let avatarImage = contactShare.avatarImage { - let field = ContactShareAvatarField(OWSContactAvatar(avatarImage: avatarImage, avatarData: avatarData)) - let fieldView = ContactShareFieldView(field: field, previewViewBlock: { - return ContactFieldView.contactFieldView(forAvatarImage: avatarImage, layoutMargins: previewInsets, actionBlock: nil) - }, - delegate: self) - fieldViews.append(fieldView) - } else { - owsFailDebug("could not load avatar image.") - } - } - - for phoneNumber in contactShare.phoneNumbers { - let field = ContactSharePhoneNumber(phoneNumber) - let fieldView = ContactShareFieldView(field: field, previewViewBlock: { - return ContactFieldView.contactFieldView(forPhoneNumber: phoneNumber, layoutMargins: previewInsets, actionBlock: nil) - }, - delegate: self) - fieldViews.append(fieldView) - } - - for email in contactShare.emails { - let field = ContactShareEmail(email) - let fieldView = ContactShareFieldView(field: field, previewViewBlock: { - return ContactFieldView.contactFieldView(forEmail: email, layoutMargins: previewInsets, actionBlock: nil) - }, - delegate: self) - fieldViews.append(fieldView) - } - - for address in contactShare.addresses { - let field = ContactShareAddress(address) - let fieldView = ContactShareFieldView(field: field, previewViewBlock: { - return ContactFieldView.contactFieldView(forAddress: address, layoutMargins: previewInsets, actionBlock: nil) - }, - delegate: self) - fieldViews.append(fieldView) - } - - self.fieldViews = fieldViews - } - - // MARK: - View Lifecycle - - override public func viewWillAppear(_ animated: Bool) { - super.viewWillAppear(animated) - - updateNavigationBar() - } - - override public func viewDidAppear(_ animated: Bool) { - super.viewDidAppear(animated) - } - - override public func viewWillDisappear(_ animated: Bool) { - super.viewWillDisappear(animated) - } - - override public func viewDidDisappear(_ animated: Bool) { - super.viewDidDisappear(animated) - } - - override public func loadView() { - super.loadView() - - self.navigationItem.title = NSLocalizedString("CONTACT_SHARE_APPROVAL_VIEW_TITLE", - comment: "Title for the 'Approve contact share' view.") - - self.view.backgroundColor = Theme.backgroundColor - - updateContent() - - updateNavigationBar() - } - - func isAtLeastOneFieldSelected() -> Bool { - for fieldView in fieldViews { - if fieldView.field.isIncluded(), !fieldView.field.isAvatar { - return true - } - } - return false - } - - func updateNavigationBar() { - self.navigationItem.leftBarButtonItem = UIBarButtonItem(barButtonSystemItem: .cancel, - target: self, - action: #selector(didPressCancel)) - - self.navigationItem.rightBarButtonItem = UIBarButtonItem(title: NSLocalizedString("ATTACHMENT_APPROVAL_SEND_BUTTON", - comment: "Label for 'send' button in the 'attachment approval' dialog."), - style: .plain, target: self, action: #selector(didPressSendButton)) - } - - private func updateContent() { - AssertIsOnMainThread() - - guard let rootView = self.view else { - owsFailDebug("missing root view.") - return - } - - for subview in rootView.subviews { - subview.removeFromSuperview() - } - - let scrollView = UIScrollView() - scrollView.preservesSuperviewLayoutMargins = false - self.view.addSubview(scrollView) - scrollView.layoutMargins = .zero - scrollView.autoPinEdge(toSuperviewSafeArea: .leading) - scrollView.autoPinEdge(toSuperviewSafeArea: .trailing) - scrollView.autoPinEdge(.top, to: .top, of: view) - scrollView.autoPinEdge(toSuperviewEdge: .bottom) - - let fieldsView = createFieldsView() - - scrollView.addSubview(fieldsView) - // Use layoutMarginsGuide for views inside UIScrollView - // that should have same width as scroll view. - fieldsView.autoPinLeadingToSuperviewMargin() - fieldsView.autoPinTrailingToSuperviewMargin() - fieldsView.autoPinEdge(toSuperviewEdge: .top) - fieldsView.autoPinEdge(toSuperviewEdge: .bottom) - fieldsView.setContentHuggingHorizontalLow() - } - - private func createFieldsView() -> UIView { - AssertIsOnMainThread() - - var rows = [UIView]() - - rows.append(createNameRow()) - - for fieldView in fieldViews { - rows.append(fieldView) - } - - return ContactFieldView(rows: rows, hMargin: hMargin) - } - - private let hMargin = CGFloat(16) - - func createNameRow() -> UIView { - let nameVMargin = CGFloat(16) - - let stackView = TappableStackView(actionBlock: { [weak self] in - guard let strongSelf = self else { return } - strongSelf.didPressEditName() - }) - - stackView.axis = .horizontal - stackView.alignment = .center - stackView.layoutMargins = UIEdgeInsets(top: nameVMargin, left: hMargin, bottom: nameVMargin, right: hMargin) - stackView.spacing = 10 - stackView.isLayoutMarginsRelativeArrangement = true - - let nameLabel = UILabel() - self.nameLabel = nameLabel - nameLabel.text = contactShare.name.displayName - nameLabel.font = UIFont.ows_dynamicTypeBody.ows_mediumWeight() - nameLabel.textColor = Theme.primaryColor - nameLabel.lineBreakMode = .byTruncatingTail - stackView.addArrangedSubview(nameLabel) - - let editNameLabel = UILabel() - editNameLabel.text = NSLocalizedString("CONTACT_EDIT_NAME_BUTTON", comment: "Label for the 'edit name' button in the contact share approval view.") - editNameLabel.font = UIFont.ows_dynamicTypeBody - editNameLabel.textColor = UIColor.ows_materialBlue - stackView.addArrangedSubview(editNameLabel) - editNameLabel.setContentHuggingHigh() - editNameLabel.setCompressionResistanceHigh() - - return stackView - } - - // MARK: - - - func filteredContactShare() -> ContactShareViewModel { - let result = self.contactShare.newContact(withName: self.contactShare.name) - - for fieldView in fieldViews { - if fieldView.field.isIncluded() { - fieldView.field.applyToContact(contact: result) - } - } - - return result - } - - // MARK: - - - @objc func didPressSendButton() { - AssertIsOnMainThread() - - guard isAtLeastOneFieldSelected() else { - OWSAlerts.showErrorAlert(message: NSLocalizedString("CONTACT_SHARE_NO_FIELDS_SELECTED", - comment: "Error indicating that at least one contact field must be selected before sharing a contact.")) - return - } - guard contactShare.ows_isValid else { - OWSAlerts.showErrorAlert(message: NSLocalizedString("CONTACT_SHARE_INVALID_CONTACT", - comment: "Error indicating that an invalid contact cannot be shared.")) - return - } - - Logger.info("") - - guard let delegate = self.delegate else { - owsFailDebug("missing delegate.") - return - } - - let filteredContactShare = self.filteredContactShare() - - assert(filteredContactShare.ows_isValid) - - delegate.approveContactShare(self, didApproveContactShare: filteredContactShare) - } - - @objc func didPressCancel() { - Logger.info("") - - guard let delegate = self.delegate else { - owsFailDebug("missing delegate.") - return - } - - delegate.approveContactShare(self, didCancelContactShare: contactShare) - } - - func didPressEditName() { - Logger.info("") - - let view = EditContactShareNameViewController(contactShare: contactShare, delegate: self) - self.navigationController?.pushViewController(view, animated: true) - } - - // MARK: - EditContactShareNameViewControllerDelegate - - public func editContactShareNameView(_ editContactShareNameView: EditContactShareNameViewController, - didEditContactShare contactShare: ContactShareViewModel) { - self.contactShare = contactShare - - nameLabel.text = contactShare.name.displayName - - self.updateNavigationBar() - } - - // MARK: - ContactShareFieldViewDelegate - - public func contactShareFieldViewDidChangeSelectedState() { - self.updateNavigationBar() - } -} diff --git a/SignalUtilitiesKit/ContactShareViewModel.swift b/SignalUtilitiesKit/ContactShareViewModel.swift deleted file mode 100644 index 4397f0045..000000000 --- a/SignalUtilitiesKit/ContactShareViewModel.swift +++ /dev/null @@ -1,168 +0,0 @@ -// -// Copyright (c) 2018 Open Whisper Systems. All rights reserved. -// - -import Foundation - -@objc -public class ContactShareViewModel: NSObject { - - @objc - public let dbRecord: OWSContact - - @objc - public var avatarImageData: Data? { - didSet { - self.cachedAvatarImage = nil - } - } - - private var cachedAvatarImage: UIImage? - - @objc - public var avatarImage: UIImage? { - if self.cachedAvatarImage != nil { - return self.cachedAvatarImage - } - - guard let avatarImageData = self.avatarImageData else { - return nil - } - - self.cachedAvatarImage = UIImage(data: avatarImageData) - return cachedAvatarImage - } - - @objc - public required init(contactShareRecord: OWSContact, avatarImageData: Data?) { - self.dbRecord = contactShareRecord - self.avatarImageData = avatarImageData - } - - @objc - public convenience init(contactShareRecord: OWSContact, transaction: YapDatabaseReadTransaction) { - if let avatarAttachment = contactShareRecord.avatarAttachment(with: transaction) as? TSAttachmentStream { - self.init(contactShareRecord: contactShareRecord, avatarImageData: avatarAttachment.validStillImageData()) - } else { - self.init(contactShareRecord: contactShareRecord, avatarImageData: nil) - } - } - - @objc - public func getAvatarImage(diameter: CGFloat, contactsManager: OWSContactsManager) -> UIImage? { - if let avatarImage = avatarImage { - return avatarImage - } - - var colorSeed = name.displayName - let recipientIds = systemContactsWithSignalAccountPhoneNumbers(contactsManager) - if let firstRecipientId = recipientIds.first { - // Try to use the first signal id as the default - // avatar's color seed, so that it is as consistent - // as possible with the user's avatar in other views. - colorSeed = firstRecipientId - } - - let avatarBuilder = OWSContactAvatarBuilder(nonSignalName: displayName, - colorSeed: colorSeed, - diameter: UInt(diameter)) - // Note: we use buildDefaultImage() and not build() so that contact - // share views always reflect the contents of the contact share. - // build() might return an avatar from a corresponding system - // contact or profile. This could mislead the user into thinking - // that an avatar they did not share was in fact included in the - // contact share. - return avatarBuilder.buildDefaultImage() - } - - // MARK: Delegated -> dbRecord - - @objc - public var name: OWSContactName { - get { - return dbRecord.name - } - set { - return dbRecord.name = newValue - } - } - - @objc - public var addresses: [OWSContactAddress] { - get { - return dbRecord.addresses - } - set { - return dbRecord.addresses = newValue - } - } - - @objc - public var emails: [OWSContactEmail] { - get { - return dbRecord.emails - } - set { - dbRecord.emails = newValue - } - } - - @objc - public var phoneNumbers: [OWSContactPhoneNumber] { - get { - return dbRecord.phoneNumbers - } - set { - dbRecord.phoneNumbers = newValue - } - } - - @objc - public func systemContactsWithSignalAccountPhoneNumbers(_ contactsManager: ContactsManagerProtocol) -> [String] { - return dbRecord.systemContactsWithSignalAccountPhoneNumbers(contactsManager) - } - - @objc - public func systemContactPhoneNumbers(_ contactsManager: ContactsManagerProtocol) -> [String] { - return dbRecord.systemContactPhoneNumbers(contactsManager) - } - - @objc - public func e164PhoneNumbers() -> [String] { - return dbRecord.e164PhoneNumbers() - } - - @objc - public var displayName: String { - return dbRecord.name.displayName - } - - @objc - public var ows_isValid: Bool { - return dbRecord.ows_isValid() - } - - @objc - public var isProfileAvatar: Bool { - return dbRecord.isProfileAvatar - } - - @objc - public func copy(withName name: OWSContactName) -> ContactShareViewModel { - - // TODO move the `copy` logic into the view model? - let newDbRecord = dbRecord.copy(with: name) - - return ContactShareViewModel(contactShareRecord: newDbRecord, avatarImageData: self.avatarImageData) - } - - @objc - public func newContact(withName name: OWSContactName) -> ContactShareViewModel { - - // TODO move the `newContact` logic into the view model? - let newDbRecord = dbRecord.newContact(with: name) - - // If we want to keep the avatar image, the caller will need to re-apply it. - return ContactShareViewModel(contactShareRecord: newDbRecord, avatarImageData: nil) - } -} diff --git a/SignalUtilitiesKit/ContactsUpdater.h b/SignalUtilitiesKit/ContactsUpdater.h deleted file mode 100644 index 7fc59f2bc..000000000 --- a/SignalUtilitiesKit/ContactsUpdater.h +++ /dev/null @@ -1,25 +0,0 @@ -// -// Copyright (c) 2018 Open Whisper Systems. All rights reserved. -// - -#import - -NS_ASSUME_NONNULL_BEGIN - -@interface ContactsUpdater : NSObject - -+ (instancetype)sharedUpdater; - -// This asynchronously tries to verify whether or not a group of possible -// contact ids correspond to service accounts. -// -// The failure callback is only invoked if the lookup fails. Otherwise, -// the success callback is invoked with the (possibly empty) set of contacts -// that were found. -- (void)lookupIdentifiers:(NSArray *)identifiers - success:(void (^)(NSArray *recipients))success - failure:(void (^)(NSError *error))failure; - -@end - -NS_ASSUME_NONNULL_END diff --git a/SignalUtilitiesKit/ContactsUpdater.m b/SignalUtilitiesKit/ContactsUpdater.m deleted file mode 100644 index c033d07a1..000000000 --- a/SignalUtilitiesKit/ContactsUpdater.m +++ /dev/null @@ -1,120 +0,0 @@ -// -// Copyright (c) 2018 Open Whisper Systems. All rights reserved. -// - -#import "ContactsUpdater.h" -#import "OWSError.h" -#import "OWSPrimaryStorage.h" -#import "OWSRequestFactory.h" -#import "PhoneNumber.h" -#import "SSKEnvironment.h" -#import "TSNetworkManager.h" -#import -#import -#import -#import -#import "SSKAsserts.h" - -NS_ASSUME_NONNULL_BEGIN - -@interface ContactsUpdater () - -@property (nonatomic, readonly) NSOperationQueue *contactIntersectionQueue; - -@end - -#pragma mark - - -@implementation ContactsUpdater - -+ (instancetype)sharedUpdater { - OWSAssertDebug(SSKEnvironment.shared.contactsUpdater); - - return SSKEnvironment.shared.contactsUpdater; -} - -- (instancetype)init -{ - self = [super init]; - if (!self) { - return self; - } - - _contactIntersectionQueue = [NSOperationQueue new]; - _contactIntersectionQueue.maxConcurrentOperationCount = 1; - _contactIntersectionQueue.name = self.logTag; - - OWSSingletonAssert(); - - return self; -} - -- (void)lookupIdentifiers:(NSArray *)identifiers - success:(void (^)(NSArray *recipients))success - failure:(void (^)(NSError *error))failure -{ - if (identifiers.count < 1) { - OWSFailDebug(@"Cannot lookup zero identifiers"); - DispatchMainThreadSafe(^{ - failure( - OWSErrorWithCodeDescription(OWSErrorCodeInvalidMethodParameters, @"Cannot lookup zero identifiers")); - }); - return; - } - - [self contactIntersectionWithSet:[NSSet setWithArray:identifiers] - success:^(NSSet *recipients) { - if (recipients.count == 0) { - OWSLogInfo(@"no contacts are Signal users"); - } - DispatchMainThreadSafe(^{ - success(recipients.allObjects); - }); - } - failure:^(NSError *error) { - DispatchMainThreadSafe(^{ - failure(error); - }); - }]; -} - -- (void)contactIntersectionWithSet:(NSSet *)recipientIdsToLookup - success:(void (^)(NSSet *recipients))success - failure:(void (^)(NSError *error))failure -{ - OWSLegacyContactDiscoveryOperation *operation = - [[OWSLegacyContactDiscoveryOperation alloc] initWithRecipientIdsToLookup:recipientIdsToLookup.allObjects]; - - dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ - NSArray *operationAndDependencies = [operation.dependencies arrayByAddingObject:operation]; - [self.contactIntersectionQueue addOperations:operationAndDependencies waitUntilFinished:YES]; - - if (operation.failingError != nil) { - failure(operation.failingError); - return; - } - - NSSet *registeredRecipientIds = operation.registeredRecipientIds; - - NSMutableSet *recipients = [NSMutableSet new]; - [LKStorage writeSyncWithBlock:^(YapDatabaseReadWriteTransaction *transaction) { - for (NSString *recipientId in recipientIdsToLookup) { - if ([registeredRecipientIds containsObject:recipientId]) { - SignalRecipient *recipient = - [SignalRecipient markRecipientAsRegisteredAndGet:recipientId transaction:transaction]; - [recipients addObject:recipient]; - } else { - [SignalRecipient markRecipientAsUnregistered:recipientId transaction:transaction]; - } - } - }]; - - dispatch_async(dispatch_get_main_queue(), ^{ - success([recipients copy]); - }); - }); -} - -@end - -NS_ASSUME_NONNULL_END diff --git a/SignalUtilitiesKit/ContactsViewHelper.h b/SignalUtilitiesKit/ContactsViewHelper.h deleted file mode 100644 index c4b73cb69..000000000 --- a/SignalUtilitiesKit/ContactsViewHelper.h +++ /dev/null @@ -1,90 +0,0 @@ -// -// Copyright (c) 2019 Open Whisper Systems. All rights reserved. -// - -NS_ASSUME_NONNULL_BEGIN - -@class Contact; -@class ContactsViewHelper; -@class SignalAccount; -@class TSThread; - -@protocol CNContactViewControllerDelegate; - -@protocol ContactsViewHelperDelegate - -- (void)contactsViewHelperDidUpdateContacts; - -@optional - -- (BOOL)shouldHideLocalNumber; - -@end - -@protocol ContactEditingDelegate - -- (void)didFinishEditingContact; - -@end - -#pragma mark - - -@class CNContact; -@class OWSBlockingManager; -@class OWSContactsManager; - -@interface ContactsViewHelper : NSObject - -@property (nonatomic, readonly, weak) id delegate; - -@property (nonatomic, readonly) OWSContactsManager *contactsManager; -@property (nonatomic, readonly) OWSBlockingManager *blockingManager; - -@property (nonatomic, readonly) NSDictionary *signalAccountMap; -@property (nonatomic, readonly) NSArray *signalAccounts; - -// Useful to differentiate between having no signal accounts vs. haven't checked yet -@property (nonatomic, readonly) BOOL hasUpdatedContactsAtLeastOnce; - -// Suitable when the user tries to perform an action which is not possible due to the user having -// previously denied contact access. -- (void)presentMissingContactAccessAlertControllerFromViewController:(UIViewController *)viewController; - -- (instancetype)init NS_UNAVAILABLE; - -- (instancetype)initWithDelegate:(id)delegate; - -- (nullable SignalAccount *)fetchSignalAccountForRecipientId:(NSString *)recipientId; -- (SignalAccount *)fetchOrBuildSignalAccountForRecipientId:(NSString *)recipientId; - -// This method is faster than OWSBlockingManager but -// is only safe to be called on the main thread. -- (BOOL)isRecipientIdBlocked:(NSString *)recipientId; - -// This method is faster than OWSBlockingManager but -// is only safe to be called on the main thread. -- (BOOL)isThreadBlocked:(TSThread *)thread; - -// NOTE: This method uses a transaction. -- (NSString *)localNumber; - -- (NSArray *)signalAccountsMatchingSearchString:(NSString *)searchText; - -- (void)warmNonSignalContactsCacheAsync; -- (NSArray *)nonSignalContactsMatchingSearchString:(NSString *)searchText; - -- (void)presentContactViewControllerForRecipientId:(NSString *)recipientId - fromViewController:(UIViewController *)fromViewController - editImmediately:(BOOL)shouldEditImmediately; - -// This method can be used to edit existing contacts. -- (void)presentContactViewControllerForRecipientId:(NSString *)recipientId - fromViewController:(UIViewController *)fromViewController - editImmediately:(BOOL)shouldEditImmediately - addToExistingCnContact:(CNContact *_Nullable)cnContact; - -+ (void)presentMissingContactAccessAlertControllerFromViewController:(UIViewController *)viewController; - -@end - -NS_ASSUME_NONNULL_END diff --git a/SignalUtilitiesKit/ContactsViewHelper.m b/SignalUtilitiesKit/ContactsViewHelper.m deleted file mode 100644 index 58ab0c5e5..000000000 --- a/SignalUtilitiesKit/ContactsViewHelper.m +++ /dev/null @@ -1,463 +0,0 @@ -// -// Copyright (c) 2019 Open Whisper Systems. All rights reserved. -// - -#import "ContactsViewHelper.h" -#import "Environment.h" -#import "UIUtil.h" -#import -#import -#import -#import -#import -#import -#import -#import -#import - -@import ContactsUI; - -NS_ASSUME_NONNULL_BEGIN - -@interface ContactsViewHelper () - -// This property is a cached value that is lazy-populated. -@property (nonatomic, nullable) NSArray *nonSignalContacts; - -@property (nonatomic) NSDictionary *signalAccountMap; -@property (nonatomic) NSArray *signalAccounts; - -@property (nonatomic, readonly) OWSBlockListCache *blockListCache; - -@property (nonatomic) BOOL shouldNotifyDelegateOfUpdatedContacts; -@property (nonatomic) BOOL hasUpdatedContactsAtLeastOnce; -@property (nonatomic) OWSProfileManager *profileManager; -@property (nonatomic, readonly) FullTextSearcher *fullTextSearcher; - -@end - -#pragma mark - - -@implementation ContactsViewHelper - -- (instancetype)initWithDelegate:(id)delegate -{ - self = [super init]; - if (!self) { - return self; - } - - OWSAssertDebug(delegate); - _delegate = delegate; - - _blockingManager = [OWSBlockingManager sharedManager]; - _blockListCache = [OWSBlockListCache new]; - [_blockListCache startObservingAndSyncStateWithDelegate:self]; - - _fullTextSearcher = FullTextSearcher.shared; - - _contactsManager = Environment.shared.contactsManager; - _profileManager = [OWSProfileManager sharedManager]; - - [self updateContacts]; - - [self observeNotifications]; - - return self; -} - -- (void)observeNotifications -{ - [[NSNotificationCenter defaultCenter] addObserver:self - selector:@selector(signalAccountsDidChange:) - name:OWSContactsManagerSignalAccountsDidChangeNotification - object:nil]; -} - -- (void)dealloc -{ - [[NSNotificationCenter defaultCenter] removeObserver:self]; -} - -- (void)signalAccountsDidChange:(NSNotification *)notification -{ - OWSAssertIsOnMainThread(); - - [self updateContacts]; -} - -#pragma mark - Contacts - -- (nullable SignalAccount *)fetchSignalAccountForRecipientId:(NSString *)recipientId -{ - OWSAssertIsOnMainThread(); - OWSAssertDebug(recipientId.length > 0); - - return self.signalAccountMap[recipientId]; -} - -- (SignalAccount *)fetchOrBuildSignalAccountForRecipientId:(NSString *)recipientId -{ - OWSAssertDebug(recipientId.length > 0); - - SignalAccount *_Nullable signalAccount = [self fetchSignalAccountForRecipientId:recipientId]; - return (signalAccount ?: [[SignalAccount alloc] initWithRecipientId:recipientId]); -} - -- (BOOL)isSignalAccountHidden:(SignalAccount *)signalAccount -{ - OWSAssertIsOnMainThread(); - - if ([self.delegate respondsToSelector:@selector(shouldHideLocalNumber)] && [self.delegate shouldHideLocalNumber] && - [self isCurrentUser:signalAccount]) { - - return YES; - } - - return NO; -} - -- (BOOL)isCurrentUser:(SignalAccount *)signalAccount -{ - OWSAssertIsOnMainThread(); - - NSString *localNumber = [TSAccountManager localNumber]; - if ([signalAccount.recipientId isEqualToString:localNumber]) { - return YES; - } - - for (PhoneNumber *phoneNumber in signalAccount.contact.parsedPhoneNumbers) { - if ([[phoneNumber toE164] isEqualToString:localNumber]) { - return YES; - } - } - - return NO; -} - -- (NSString *)localNumber -{ - return [TSAccountManager localNumber]; -} - -- (BOOL)isRecipientIdBlocked:(NSString *)recipientId -{ - OWSAssertIsOnMainThread(); - - return [self.blockListCache isRecipientIdBlocked:recipientId]; -} - -- (BOOL)isGroupIdBlocked:(NSData *)groupId -{ - OWSAssertIsOnMainThread(); - - return [self.blockListCache isGroupIdBlocked:groupId]; -} - -- (BOOL)isThreadBlocked:(TSThread *)thread -{ - if ([thread isKindOfClass:[TSContactThread class]]) { - TSContactThread *contactThread = (TSContactThread *)thread; - return [self isRecipientIdBlocked:contactThread.contactIdentifier]; - } else if ([thread isKindOfClass:[TSGroupThread class]]) { - TSGroupThread *groupThread = (TSGroupThread *)thread; - return [self isGroupIdBlocked:groupThread.groupModel.groupId]; - } else { - OWSFailDebug(@"%@ failure: unexpected thread: %@", self.logTag, thread.class); - return NO; - } -} - -- (void)updateContacts -{ - OWSAssertIsOnMainThread(); - - NSMutableDictionary *signalAccountMap = [NSMutableDictionary new]; - NSMutableArray *signalAccounts = [NSMutableArray new]; - for (SignalAccount *signalAccount in self.contactsManager.signalAccounts) { - if (![self isSignalAccountHidden:signalAccount]) { - signalAccountMap[signalAccount.recipientId] = signalAccount; - [signalAccounts addObject:signalAccount]; - } - } - self.signalAccountMap = [signalAccountMap copy]; - self.signalAccounts = [signalAccounts copy]; - self.nonSignalContacts = nil; - - // Don't fire delegate "change" events during initialization. - if (self.shouldNotifyDelegateOfUpdatedContacts) { - [self.delegate contactsViewHelperDidUpdateContacts]; - self.hasUpdatedContactsAtLeastOnce = YES; - } -} - -- (NSArray *)searchTermsForSearchString:(NSString *)searchText -{ - return [[[searchText ows_stripped] - componentsSeparatedByCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]] - filteredArrayUsingPredicate:[NSPredicate predicateWithBlock:^BOOL(NSString *_Nullable searchTerm, - NSDictionary *_Nullable bindings) { - return searchTerm.length > 0; - }]]; -} - -- (NSArray *)signalAccountsMatchingSearchString:(NSString *)searchText -{ - // Check for matches against "Note to Self". - NSMutableArray *signalAccountsToSearch = [self.signalAccounts mutableCopy]; - SignalAccount *selfAccount = [[SignalAccount alloc] initWithRecipientId:self.localNumber]; - [signalAccountsToSearch addObject:selfAccount]; - return [self.fullTextSearcher filterSignalAccounts:signalAccountsToSearch withSearchText:searchText]; -} - -- (BOOL)doesContact:(Contact *)contact matchSearchTerm:(NSString *)searchTerm -{ - OWSAssertDebug(contact); - OWSAssertDebug(searchTerm.length > 0); - - if ([contact.fullName.lowercaseString containsString:searchTerm.lowercaseString]) { - return YES; - } - - NSString *asPhoneNumber = [PhoneNumber removeFormattingCharacters:searchTerm]; - if (asPhoneNumber.length > 0) { - for (PhoneNumber *phoneNumber in contact.parsedPhoneNumbers) { - if ([phoneNumber.toE164 containsString:asPhoneNumber]) { - return YES; - } - } - } - - return NO; -} - -- (BOOL)doesContact:(Contact *)contact matchSearchTerms:(NSArray *)searchTerms -{ - OWSAssertDebug(contact); - OWSAssertDebug(searchTerms.count > 0); - - for (NSString *searchTerm in searchTerms) { - if (![self doesContact:contact matchSearchTerm:searchTerm]) { - return NO; - } - } - - return YES; -} - -- (NSArray *)nonSignalContactsMatchingSearchString:(NSString *)searchText -{ - NSArray *searchTerms = [self searchTermsForSearchString:searchText]; - - if (searchTerms.count < 1) { - return [NSArray new]; - } - - return [self.nonSignalContacts filteredArrayUsingPredicate:[NSPredicate predicateWithBlock:^BOOL(Contact *contact, - NSDictionary *_Nullable bindings) { - return [self doesContact:contact matchSearchTerms:searchTerms]; - }]]; -} - -- (void)warmNonSignalContactsCacheAsync -{ - OWSAssertIsOnMainThread(); - if (self.nonSignalContacts != nil) { - return; - } - - NSMutableSet *nonSignalContactSet = [NSMutableSet new]; - __block NSArray *nonSignalContacts; - - [OWSPrimaryStorage.dbReadConnection - asyncReadWithBlock:^(YapDatabaseReadTransaction *transaction) { - for (Contact *contact in self.contactsManager.allContactsMap.allValues) { - NSArray *signalRecipients = [contact signalRecipientsWithTransaction:transaction]; - if (signalRecipients.count < 1) { - [nonSignalContactSet addObject:contact]; - } - } - nonSignalContacts = [nonSignalContactSet.allObjects - sortedArrayUsingComparator:^NSComparisonResult(Contact *_Nonnull left, Contact *_Nonnull right) { - return [left.fullName compare:right.fullName]; - }]; - } - completionBlock:^{ - self.nonSignalContacts = nonSignalContacts; - }]; -} - -- (nullable NSArray *)nonSignalContacts -{ - OWSAssertIsOnMainThread(); - if (!_nonSignalContacts) { - NSMutableSet *nonSignalContacts = [NSMutableSet new]; - [OWSPrimaryStorage.dbReadConnection readWithBlock:^(YapDatabaseReadTransaction *transaction) { - for (Contact *contact in self.contactsManager.allContactsMap.allValues) { - NSArray *signalRecipients = [contact signalRecipientsWithTransaction:transaction]; - if (signalRecipients.count < 1) { - [nonSignalContacts addObject:contact]; - } - } - }]; - _nonSignalContacts = [nonSignalContacts.allObjects - sortedArrayUsingComparator:^NSComparisonResult(Contact *_Nonnull left, Contact *_Nonnull right) { - return [left.fullName compare:right.fullName]; - }]; - } - - return _nonSignalContacts; -} - -#pragma mark - Editing - -- (void)presentMissingContactAccessAlertControllerFromViewController:(UIViewController *)viewController -{ - [ContactsViewHelper presentMissingContactAccessAlertControllerFromViewController:viewController]; -} - -+ (void)presentMissingContactAccessAlertControllerFromViewController:(UIViewController *)viewController -{ - UIAlertController *alert = [UIAlertController - alertControllerWithTitle:NSLocalizedString(@"EDIT_CONTACT_WITHOUT_CONTACTS_PERMISSION_ALERT_TITLE", comment - : @"Alert title for when the user has just tried to edit a " - @"contacts after declining to give Signal contacts " - @"permissions") - message:NSLocalizedString(@"EDIT_CONTACT_WITHOUT_CONTACTS_PERMISSION_ALERT_BODY", comment - : @"Alert body for when the user has just tried to edit a " - @"contacts after declining to give Signal contacts " - @"permissions") - preferredStyle:UIAlertControllerStyleAlert]; - - [alert addAction:[UIAlertAction actionWithTitle:NSLocalizedString(@"AB_PERMISSION_MISSING_ACTION_NOT_NOW", - @"Button text to dismiss missing contacts permission alert") - accessibilityIdentifier:ACCESSIBILITY_IDENTIFIER_WITH_NAME(self, @"not_now") - style:UIAlertActionStyleCancel - handler:nil]]; - - UIAlertAction *_Nullable openSystemSettingsAction = CurrentAppContext().openSystemSettingsAction; - if (openSystemSettingsAction) { - [alert addAction:openSystemSettingsAction]; - } - - [viewController presentAlert:alert]; -} - -- (void)presentContactViewControllerForRecipientId:(NSString *)recipientId - fromViewController:(UIViewController *)fromViewController - editImmediately:(BOOL)shouldEditImmediately -{ - return; - /** - [self presentContactViewControllerForRecipientId:recipientId - fromViewController:fromViewController - editImmediately:shouldEditImmediately - addToExistingCnContact:nil]; - */ -} - -- (void)presentContactViewControllerForRecipientId:(NSString *)recipientId - fromViewController:(UIViewController *)fromViewController - editImmediately:(BOOL)shouldEditImmediately - addToExistingCnContact:(CNContact *_Nullable)existingContact -{ - SignalAccount *signalAccount = [self fetchSignalAccountForRecipientId:recipientId]; - - if (!self.contactsManager.supportsContactEditing) { - // Should not expose UI that lets the user get here. - OWSFailDebug(@"Contact editing not supported."); - return; - } - - if (!self.contactsManager.isSystemContactsAuthorized) { - [self presentMissingContactAccessAlertControllerFromViewController:fromViewController]; - return; - } - - CNContactViewController *_Nullable contactViewController; - CNContact *_Nullable cnContact = nil; - if (existingContact) { - CNMutableContact *updatedContact = [existingContact mutableCopy]; - NSMutableArray *phoneNumbers - = (updatedContact.phoneNumbers ? [updatedContact.phoneNumbers mutableCopy] : [NSMutableArray new]); - // Only add recipientId as a phone number for the existing contact - // if its not already present. - BOOL hasPhoneNumber = NO; - for (CNLabeledValue *existingPhoneNumber in phoneNumbers) { - CNPhoneNumber *phoneNumber = existingPhoneNumber.value; - if ([phoneNumber.stringValue isEqualToString:recipientId]) { - OWSFailDebug(@"We currently only should the 'add to existing contact' UI for phone numbers that don't " - @"correspond to an existing user."); - hasPhoneNumber = YES; - break; - } - } - if (!hasPhoneNumber) { - CNPhoneNumber *phoneNumber = [CNPhoneNumber phoneNumberWithStringValue:recipientId]; - CNLabeledValue *labeledPhoneNumber = - [CNLabeledValue labeledValueWithLabel:CNLabelPhoneNumberMain value:phoneNumber]; - [phoneNumbers addObject:labeledPhoneNumber]; - updatedContact.phoneNumbers = phoneNumbers; - - // When adding a phone number to an existing contact, immediately enter - // "edit" mode. - shouldEditImmediately = YES; - } - cnContact = updatedContact; - } - if (signalAccount && !cnContact) { - cnContact = [self.contactsManager cnContactWithId:signalAccount.contact.cnContactId]; - } - if (cnContact) { - if (shouldEditImmediately) { - // Not actually a "new" contact, but this brings up the edit form rather than the "Read" form - // saving our users a tap in some cases when we already know they want to edit. - contactViewController = [CNContactViewController viewControllerForNewContact:cnContact]; - - // Default title is "New Contact". We could give a more descriptive title, but anything - // seems redundant - the context is sufficiently clear. - contactViewController.title = @""; - } else { - contactViewController = [CNContactViewController viewControllerForContact:cnContact]; - } - } - - if (!contactViewController) { - CNMutableContact *newContact = [CNMutableContact new]; - CNPhoneNumber *phoneNumber = [CNPhoneNumber phoneNumberWithStringValue:recipientId]; - CNLabeledValue *labeledPhoneNumber = - [CNLabeledValue labeledValueWithLabel:CNLabelPhoneNumberMain value:phoneNumber]; - newContact.phoneNumbers = @[ labeledPhoneNumber ]; - - newContact.givenName = [self.profileManager profileNameForRecipientWithID:recipientId]; - - contactViewController = [CNContactViewController viewControllerForNewContact:newContact]; - } - - contactViewController.delegate = fromViewController; - contactViewController.allowsActions = NO; - contactViewController.allowsEditing = YES; - contactViewController.navigationItem.leftBarButtonItem = - [[UIBarButtonItem alloc] initWithTitle:CommonStrings.cancelButton - style:UIBarButtonItemStylePlain - target:fromViewController - action:@selector(didFinishEditingContact)]; - - OWSNavigationController *modal = [[OWSNavigationController alloc] initWithRootViewController:contactViewController]; - - // We want the presentation to imply a "replacement" in this case. - if (shouldEditImmediately) { - modal.modalTransitionStyle = UIModalTransitionStyleCrossDissolve; - } - [fromViewController presentViewController:modal animated:YES completion:nil]; -} - -- (void)blockListCacheDidUpdate:(OWSBlockListCache *)blocklistCache -{ - OWSAssertIsOnMainThread(); - [self updateContacts]; -} - -@end - -NS_ASSUME_NONNULL_END diff --git a/SignalUtilitiesKit/CreatePreKeysOperation.swift b/SignalUtilitiesKit/CreatePreKeysOperation.swift index 5a8c75bcf..c74f96d13 100644 --- a/SignalUtilitiesKit/CreatePreKeysOperation.swift +++ b/SignalUtilitiesKit/CreatePreKeysOperation.swift @@ -8,10 +8,6 @@ import PromiseKit @objc(SSKCreatePreKeysOperation) public class CreatePreKeysOperation: OWSOperation { - private var accountServiceClient: AccountServiceClient { - return AccountServiceClient.shared - } - private var primaryStorage: OWSPrimaryStorage { return OWSPrimaryStorage.shared() } @@ -29,29 +25,5 @@ public class CreatePreKeysOperation: OWSOperation { SessionManagementProtocol.createPreKeys() reportSuccess() - - /* Loki: Original code - * ================ - let identityKey: Data = self.identityKeyManager.identityKeyPair()!.publicKey - let signedPreKeyRecord: SignedPreKeyRecord = self.primaryStorage.generateRandomSignedRecord() - let preKeyRecords: [PreKeyRecord] = self.primaryStorage.generatePreKeyRecords() - - self.primaryStorage.storeSignedPreKey(signedPreKeyRecord.id, signedPreKeyRecord: signedPreKeyRecord) - self.primaryStorage.storePreKeyRecords(preKeyRecords) - - firstly { - self.accountServiceClient.setPreKeys(identityKey: identityKey, signedPreKeyRecord: signedPreKeyRecord, preKeyRecords: preKeyRecords) - }.done { - signedPreKeyRecord.markAsAcceptedByService() - self.primaryStorage.storeSignedPreKey(signedPreKeyRecord.id, signedPreKeyRecord: signedPreKeyRecord) - self.primaryStorage.setCurrentSignedPrekeyId(signedPreKeyRecord.id) - - Logger.debug("done") - self.reportSuccess() - }.catch { error in - self.reportError(error) - }.retainUntilComplete() - * ================ - */ } } diff --git a/SignalUtilitiesKit/Data+SecureRandom.swift b/SignalUtilitiesKit/Data+SecureRandom.swift deleted file mode 100644 index e520e4553..000000000 --- a/SignalUtilitiesKit/Data+SecureRandom.swift +++ /dev/null @@ -1,12 +0,0 @@ - -public extension Data { - - /// Returns `size` bytes of random data generated using the default secure random number generator. See - /// [SecRandomCopyBytes](https://developer.apple.com/documentation/security/1399291-secrandomcopybytes) for more information. - public static func getSecureRandomData(ofSize size: UInt) -> Data? { - var data = Data(count: Int(size)) - let result = data.withUnsafeMutableBytes { SecRandomCopyBytes(kSecRandomDefault, Int(size), $0.baseAddress!) } - guard result == errSecSuccess else { return nil } - return data - } -} diff --git a/SignalUtilitiesKit/OWSDatabaseMigration.h b/SignalUtilitiesKit/Database/OWSDatabaseMigration.h similarity index 100% rename from SignalUtilitiesKit/OWSDatabaseMigration.h rename to SignalUtilitiesKit/Database/OWSDatabaseMigration.h diff --git a/SignalUtilitiesKit/OWSDatabaseMigration.m b/SignalUtilitiesKit/Database/OWSDatabaseMigration.m similarity index 100% rename from SignalUtilitiesKit/OWSDatabaseMigration.m rename to SignalUtilitiesKit/Database/OWSDatabaseMigration.m diff --git a/SignalUtilitiesKit/OWSDatabaseMigrationRunner.h b/SignalUtilitiesKit/Database/OWSDatabaseMigrationRunner.h similarity index 100% rename from SignalUtilitiesKit/OWSDatabaseMigrationRunner.h rename to SignalUtilitiesKit/Database/OWSDatabaseMigrationRunner.h diff --git a/SignalUtilitiesKit/OWSDatabaseMigrationRunner.m b/SignalUtilitiesKit/Database/OWSDatabaseMigrationRunner.m similarity index 100% rename from SignalUtilitiesKit/OWSDatabaseMigrationRunner.m rename to SignalUtilitiesKit/Database/OWSDatabaseMigrationRunner.m diff --git a/SignalUtilitiesKit/OWSPrimaryStorage+Calling.h b/SignalUtilitiesKit/Database/OWSPrimaryStorage+Calling.h similarity index 100% rename from SignalUtilitiesKit/OWSPrimaryStorage+Calling.h rename to SignalUtilitiesKit/Database/OWSPrimaryStorage+Calling.h diff --git a/SignalUtilitiesKit/OWSPrimaryStorage+Calling.m b/SignalUtilitiesKit/Database/OWSPrimaryStorage+Calling.m similarity index 100% rename from SignalUtilitiesKit/OWSPrimaryStorage+Calling.m rename to SignalUtilitiesKit/Database/OWSPrimaryStorage+Calling.m diff --git a/SignalUtilitiesKit/OWSPrimaryStorage+PreKeyStore.h b/SignalUtilitiesKit/Database/OWSPrimaryStorage+PreKeyStore.h similarity index 100% rename from SignalUtilitiesKit/OWSPrimaryStorage+PreKeyStore.h rename to SignalUtilitiesKit/Database/OWSPrimaryStorage+PreKeyStore.h diff --git a/SignalUtilitiesKit/OWSPrimaryStorage+PreKeyStore.m b/SignalUtilitiesKit/Database/OWSPrimaryStorage+PreKeyStore.m similarity index 100% rename from SignalUtilitiesKit/OWSPrimaryStorage+PreKeyStore.m rename to SignalUtilitiesKit/Database/OWSPrimaryStorage+PreKeyStore.m diff --git a/SignalUtilitiesKit/OWSPrimaryStorage+SessionStore.h b/SignalUtilitiesKit/Database/OWSPrimaryStorage+SessionStore.h similarity index 100% rename from SignalUtilitiesKit/OWSPrimaryStorage+SessionStore.h rename to SignalUtilitiesKit/Database/OWSPrimaryStorage+SessionStore.h diff --git a/SignalUtilitiesKit/OWSPrimaryStorage+SessionStore.m b/SignalUtilitiesKit/Database/OWSPrimaryStorage+SessionStore.m similarity index 100% rename from SignalUtilitiesKit/OWSPrimaryStorage+SessionStore.m rename to SignalUtilitiesKit/Database/OWSPrimaryStorage+SessionStore.m diff --git a/SignalUtilitiesKit/OWSPrimaryStorage+SignedPreKeyStore.h b/SignalUtilitiesKit/Database/OWSPrimaryStorage+SignedPreKeyStore.h similarity index 100% rename from SignalUtilitiesKit/OWSPrimaryStorage+SignedPreKeyStore.h rename to SignalUtilitiesKit/Database/OWSPrimaryStorage+SignedPreKeyStore.h diff --git a/SignalUtilitiesKit/OWSPrimaryStorage+SignedPreKeyStore.m b/SignalUtilitiesKit/Database/OWSPrimaryStorage+SignedPreKeyStore.m similarity index 100% rename from SignalUtilitiesKit/OWSPrimaryStorage+SignedPreKeyStore.m rename to SignalUtilitiesKit/Database/OWSPrimaryStorage+SignedPreKeyStore.m diff --git a/SignalUtilitiesKit/OWSPrimaryStorage+keyFromIntLong.h b/SignalUtilitiesKit/Database/OWSPrimaryStorage+keyFromIntLong.h similarity index 100% rename from SignalUtilitiesKit/OWSPrimaryStorage+keyFromIntLong.h rename to SignalUtilitiesKit/Database/OWSPrimaryStorage+keyFromIntLong.h diff --git a/SignalUtilitiesKit/OWSPrimaryStorage+keyFromIntLong.m b/SignalUtilitiesKit/Database/OWSPrimaryStorage+keyFromIntLong.m similarity index 100% rename from SignalUtilitiesKit/OWSPrimaryStorage+keyFromIntLong.m rename to SignalUtilitiesKit/Database/OWSPrimaryStorage+keyFromIntLong.m diff --git a/SignalUtilitiesKit/OWSPrimaryStorage.h b/SignalUtilitiesKit/Database/OWSPrimaryStorage.h similarity index 100% rename from SignalUtilitiesKit/OWSPrimaryStorage.h rename to SignalUtilitiesKit/Database/OWSPrimaryStorage.h diff --git a/SignalUtilitiesKit/OWSPrimaryStorage.m b/SignalUtilitiesKit/Database/OWSPrimaryStorage.m similarity index 97% rename from SignalUtilitiesKit/OWSPrimaryStorage.m rename to SignalUtilitiesKit/Database/OWSPrimaryStorage.m index a72fb259e..965eb093e 100644 --- a/SignalUtilitiesKit/OWSPrimaryStorage.m +++ b/SignalUtilitiesKit/Database/OWSPrimaryStorage.m @@ -4,8 +4,6 @@ #import "OWSPrimaryStorage.h" #import "AppContext.h" -#import "OWSAnalytics.h" -#import "OWSBatchMessageProcessor.h" #import "OWSDisappearingMessagesFinder.h" #import "OWSFailedAttachmentDownloadsJob.h" #import "OWSFailedMessagesJob.h" @@ -13,7 +11,6 @@ #import "OWSIncomingMessageFinder.h" #import "OWSIncompleteCallsJob.h" #import "OWSMediaGalleryFinder.h" -#import "OWSMessageReceiver.h" #import "OWSStorage+Subclass.h" #import "SSKEnvironment.h" #import "TSDatabaseSecondaryIndexes.h" @@ -198,16 +195,12 @@ void VerifyRegistrationsForPrimaryStorage(OWSStorage *storage) [self asyncRegisterExtension:[TSDatabaseSecondaryIndexes registerTimeStampIndex] withName:[TSDatabaseSecondaryIndexes registerTimeStampIndexExtensionName]]; - [OWSMessageReceiver asyncRegisterDatabaseExtension:self]; - [OWSBatchMessageProcessor asyncRegisterDatabaseExtension:self]; - [TSDatabaseView asyncRegisterUnseenDatabaseView:self]; [TSDatabaseView asyncRegisterThreadOutgoingMessagesDatabaseView:self]; [TSDatabaseView asyncRegisterThreadSpecialMessagesDatabaseView:self]; [FullTextSearchFinder asyncRegisterDatabaseExtensionWithStorage:self]; [OWSIncomingMessageFinder asyncRegisterExtensionWithPrimaryStorage:self]; - [TSDatabaseView asyncRegisterSecondaryDevicesDatabaseView:self]; [OWSDisappearingMessagesFinder asyncRegisterDatabaseExtensions:self]; [OWSFailedMessagesJob asyncRegisterDatabaseExtensionsWithPrimaryStorage:self]; [OWSIncompleteCallsJob asyncRegisterDatabaseExtensionsWithPrimaryStorage:self]; @@ -216,9 +209,6 @@ void VerifyRegistrationsForPrimaryStorage(OWSStorage *storage) [TSDatabaseView asyncRegisterLazyRestoreAttachmentsDatabaseView:self]; [SSKJobRecordFinder asyncRegisterDatabaseExtensionObjCWithStorage:self]; - // Loki - [LKDeviceLinkIndex asyncRegisterDatabaseExtensions:self]; - [self.database flushExtensionRequestsWithCompletionQueue:dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0) completionBlock:^{ diff --git a/SignalUtilitiesKit/OWSResaveCollectionDBMigration.h b/SignalUtilitiesKit/Database/OWSResaveCollectionDBMigration.h similarity index 100% rename from SignalUtilitiesKit/OWSResaveCollectionDBMigration.h rename to SignalUtilitiesKit/Database/OWSResaveCollectionDBMigration.h diff --git a/SignalUtilitiesKit/OWSResaveCollectionDBMigration.m b/SignalUtilitiesKit/Database/OWSResaveCollectionDBMigration.m similarity index 100% rename from SignalUtilitiesKit/OWSResaveCollectionDBMigration.m rename to SignalUtilitiesKit/Database/OWSResaveCollectionDBMigration.m diff --git a/SignalUtilitiesKit/OWSStorage.h b/SignalUtilitiesKit/Database/OWSStorage.h similarity index 100% rename from SignalUtilitiesKit/OWSStorage.h rename to SignalUtilitiesKit/Database/OWSStorage.h diff --git a/SignalUtilitiesKit/OWSStorage.m b/SignalUtilitiesKit/Database/OWSStorage.m similarity index 100% rename from SignalUtilitiesKit/OWSStorage.m rename to SignalUtilitiesKit/Database/OWSStorage.m diff --git a/SignalUtilitiesKit/SSKKeychainStorage.swift b/SignalUtilitiesKit/Database/SSKKeychainStorage.swift similarity index 100% rename from SignalUtilitiesKit/SSKKeychainStorage.swift rename to SignalUtilitiesKit/Database/SSKKeychainStorage.swift diff --git a/SignalUtilitiesKit/TSYapDatabaseObject.h b/SignalUtilitiesKit/Database/TSYapDatabaseObject.h similarity index 100% rename from SignalUtilitiesKit/TSYapDatabaseObject.h rename to SignalUtilitiesKit/Database/TSYapDatabaseObject.h diff --git a/SignalUtilitiesKit/TSYapDatabaseObject.m b/SignalUtilitiesKit/Database/TSYapDatabaseObject.m similarity index 100% rename from SignalUtilitiesKit/TSYapDatabaseObject.m rename to SignalUtilitiesKit/Database/TSYapDatabaseObject.m diff --git a/SignalUtilitiesKit/YapDatabase+Promise.swift b/SignalUtilitiesKit/Database/YapDatabase+Promise.swift similarity index 100% rename from SignalUtilitiesKit/YapDatabase+Promise.swift rename to SignalUtilitiesKit/Database/YapDatabase+Promise.swift diff --git a/SignalUtilitiesKit/YapDatabaseConnection+OWS.h b/SignalUtilitiesKit/Database/YapDatabaseConnection+OWS.h similarity index 100% rename from SignalUtilitiesKit/YapDatabaseConnection+OWS.h rename to SignalUtilitiesKit/Database/YapDatabaseConnection+OWS.h diff --git a/SignalUtilitiesKit/YapDatabaseConnection+OWS.m b/SignalUtilitiesKit/Database/YapDatabaseConnection+OWS.m similarity index 100% rename from SignalUtilitiesKit/YapDatabaseConnection+OWS.m rename to SignalUtilitiesKit/Database/YapDatabaseConnection+OWS.m diff --git a/SignalUtilitiesKit/YapDatabaseTransaction+OWS.h b/SignalUtilitiesKit/Database/YapDatabaseTransaction+OWS.h similarity index 100% rename from SignalUtilitiesKit/YapDatabaseTransaction+OWS.h rename to SignalUtilitiesKit/Database/YapDatabaseTransaction+OWS.h diff --git a/SignalUtilitiesKit/YapDatabaseTransaction+OWS.m b/SignalUtilitiesKit/Database/YapDatabaseTransaction+OWS.m similarity index 98% rename from SignalUtilitiesKit/YapDatabaseTransaction+OWS.m rename to SignalUtilitiesKit/Database/YapDatabaseTransaction+OWS.m index 9596f2841..ef505701e 100644 --- a/SignalUtilitiesKit/YapDatabaseTransaction+OWS.m +++ b/SignalUtilitiesKit/Database/YapDatabaseTransaction+OWS.m @@ -70,6 +70,9 @@ NS_ASSUME_NONNULL_BEGIN - (nullable PreKeyBundle *)preKeyBundleForKey:(NSString *)key inCollection:(NSString *)collection { + OWSAssertDebug(key.length > 0); + OWSAssertDebug(collection.length > 0); + return [self objectForKey:key inCollection:collection ofExpectedType:PreKeyBundle.class]; } diff --git a/SignalUtilitiesKit/DecryptionUtilities.swift b/SignalUtilitiesKit/DecryptionUtilities.swift deleted file mode 100644 index 974451235..000000000 --- a/SignalUtilitiesKit/DecryptionUtilities.swift +++ /dev/null @@ -1,18 +0,0 @@ -import CryptoSwift - -enum DecryptionUtilities { - - /// - Note: Sync. Don't call from the main thread. - internal static func decrypt(_ ivAndCiphertext: Data, usingAESGCMWithSymmetricKey symmetricKey: Data) throws -> Data { - if Thread.isMainThread { - #if DEBUG - preconditionFailure("It's illegal to call decrypt(_:usingAESGCMWithSymmetricKey:) from the main thread.") - #endif - } - let iv = ivAndCiphertext[0.. Bool { - guard let other = other as? Device else { return false } - return publicKey == other.publicKey && signature == other.signature - } - - @objc override public var hash: Int { // Override NSObject.hash and not Hashable.hashValue or Hashable.hash(into:) - var result = publicKey.hashValue - if let signature = signature { result = result ^ signature.hashValue } - return result - } - - @objc override public var description: String { return publicKey } - } - - // MARK: Lifecycle - @objc public init(between master: Device, and slave: Device) { - self.master = master - self.slave = slave - } - - // MARK: Coding - @objc public init?(coder: NSCoder) { - master = coder.decodeObject(forKey: "master") as! Device - slave = coder.decodeObject(forKey: "slave") as! Device - super.init() - } - - @objc public func encode(with coder: NSCoder) { - coder.encode(master, forKey: "master") - coder.encode(slave, forKey: "slave") - } - - // MARK: JSON - public func toJSON() -> JSON { - var result = [ "primaryDevicePubKey" : master.publicKey, "secondaryDevicePubKey" : slave.publicKey ] - if let masterSignature = master.signature { result["grantSignature"] = masterSignature.base64EncodedString() } - if let slaveSignature = slave.signature { result["requestSignature"] = slaveSignature.base64EncodedString() } - return result - } - - // MARK: Equality - @objc override public func isEqual(_ other: Any?) -> Bool { - guard let other = other as? DeviceLink else { return false } - return master == other.master && slave == other.slave - } - - // MARK: Hashing - @objc override public var hash: Int { // Override NSObject.hash and not Hashable.hashValue or Hashable.hash(into:) - return master.hash ^ slave.hash - } - - // MARK: Description - @objc override public var description: String { return "\(master) - \(slave)" } -} diff --git a/SignalUtilitiesKit/DeviceLinkIndex.swift b/SignalUtilitiesKit/DeviceLinkIndex.swift deleted file mode 100644 index 15c3fd0ec..000000000 --- a/SignalUtilitiesKit/DeviceLinkIndex.swift +++ /dev/null @@ -1,43 +0,0 @@ - -@objc(LKDeviceLinkIndex) -public final class DeviceLinkIndex : NSObject { - - private static let name = "loki_device_link_index" - - @objc public static let masterPublicKey = "master_hex_encoded_public_key" - @objc public static let slavePublicKey = "slave_hex_encoded_public_key" - @objc public static let isAuthorized = "is_authorized" - - @objc public static let indexDatabaseExtension: YapDatabaseSecondaryIndex = { - let setup = YapDatabaseSecondaryIndexSetup() - setup.addColumn(masterPublicKey, with: .text) - setup.addColumn(slavePublicKey, with: .text) - setup.addColumn(isAuthorized, with: .integer) - let handler = YapDatabaseSecondaryIndexHandler.withObjectBlock { _, map, _, _, object in - guard let deviceLink = object as? DeviceLink else { return } - map[masterPublicKey] = deviceLink.master.publicKey - map[slavePublicKey] = deviceLink.slave.publicKey - map[isAuthorized] = deviceLink.isAuthorized - } - return YapDatabaseSecondaryIndex(setup: setup, handler: handler) - }() - - @objc public static let databaseExtensionName: String = name - - @objc public static func asyncRegisterDatabaseExtensions(_ storage: OWSStorage) { - storage.asyncRegister(indexDatabaseExtension, withName: name) - } - - @objc public static func getDeviceLinks(for query: YapDatabaseQuery, in transaction: YapDatabaseReadTransaction) -> [DeviceLink] { - guard let ext = transaction.ext(DeviceLinkIndex.name) as? YapDatabaseSecondaryIndexTransaction else { - print("[Loki] Couldn't get device link index database extension.") - return [] - } - var result: [DeviceLink] = [] - ext.enumerateKeysAndObjects(matching: query) { _, _, object, _ in - guard let deviceLink = object as? DeviceLink else { return } - result.append(deviceLink) - } - return result - } -} diff --git a/SignalUtilitiesKit/DeviceLinkingSession.swift b/SignalUtilitiesKit/DeviceLinkingSession.swift deleted file mode 100644 index b3b0bd1e1..000000000 --- a/SignalUtilitiesKit/DeviceLinkingSession.swift +++ /dev/null @@ -1,69 +0,0 @@ -import Curve25519Kit -import PromiseKit - -@objc (LKDeviceLinkingSession) -public final class DeviceLinkingSession : NSObject { - private let delegate: DeviceLinkingSessionDelegate - @objc public var isListeningForLinkingRequests = false - @objc public var isProcessingLinkingRequest = false - @objc public var isListeningForLinkingAuthorization = false - - // MARK: Lifecycle - @objc public static var current: DeviceLinkingSession? - - private init(delegate: DeviceLinkingSessionDelegate) { - self.delegate = delegate - } - - // MARK: Public API - public static func startListeningForLinkingRequests(with delegate: DeviceLinkingSessionDelegate) -> DeviceLinkingSession { - let session = DeviceLinkingSession(delegate: delegate) - session.isListeningForLinkingRequests = true - DeviceLinkingSession.current = session - return session - } - - public static func startListeningForLinkingAuthorization(with delegate: DeviceLinkingSessionDelegate) -> DeviceLinkingSession { - let session = DeviceLinkingSession(delegate: delegate) - session.isListeningForLinkingAuthorization = true - DeviceLinkingSession.current = session - return session - } - - @objc public func processLinkingRequest(from slavePublicKey: String, to masterPublicKey: String, with slaveSignature: Data) { - guard isListeningForLinkingRequests, !isProcessingLinkingRequest, masterPublicKey == getUserHexEncodedPublicKey() else { return } - let master = DeviceLink.Device(publicKey: masterPublicKey) - let slave = DeviceLink.Device(publicKey: slavePublicKey, signature: slaveSignature) - let deviceLink = DeviceLink(between: master, and: slave) - guard DeviceLinkingUtilities.hasValidSlaveSignature(deviceLink) else { return } - isProcessingLinkingRequest = true - DispatchQueue.main.async { - self.delegate.requestUserAuthorization(for: deviceLink) - } - } - - @objc public func processLinkingAuthorization(from masterPublicKey: String, for slavePublicKey: String, masterSignature: Data, slaveSignature: Data) { - guard isListeningForLinkingAuthorization, slavePublicKey == getUserHexEncodedPublicKey() else { return } - let master = DeviceLink.Device(publicKey: masterPublicKey, signature: masterSignature) - let slave = DeviceLink.Device(publicKey: slavePublicKey, signature: slaveSignature) - let deviceLink = DeviceLink(between: master, and: slave) - guard DeviceLinkingUtilities.hasValidSlaveSignature(deviceLink) && DeviceLinkingUtilities.hasValidMasterSignature(deviceLink) else { return } - DispatchQueue.main.async { - self.delegate.handleDeviceLinkAuthorized(deviceLink) - } - } - - public func stopListeningForLinkingRequests() { - DeviceLinkingSession.current = nil - isListeningForLinkingRequests = false - } - - public func stopListeningForLinkingAuthorization() { - DeviceLinkingSession.current = nil - isListeningForLinkingAuthorization = false - } - - public func markLinkingRequestAsProcessed() { - isProcessingLinkingRequest = false - } -} diff --git a/SignalUtilitiesKit/DeviceLinkingSessionDelegate.swift b/SignalUtilitiesKit/DeviceLinkingSessionDelegate.swift deleted file mode 100644 index ab806251c..000000000 --- a/SignalUtilitiesKit/DeviceLinkingSessionDelegate.swift +++ /dev/null @@ -1,6 +0,0 @@ - -public protocol DeviceLinkingSessionDelegate { - - func requestUserAuthorization(for deviceLink: DeviceLink) - func handleDeviceLinkAuthorized(_ deviceLink: DeviceLink) -} diff --git a/SignalUtilitiesKit/DeviceLinkingUtilities.swift b/SignalUtilitiesKit/DeviceLinkingUtilities.swift deleted file mode 100644 index 8893a1106..000000000 --- a/SignalUtilitiesKit/DeviceLinkingUtilities.swift +++ /dev/null @@ -1,57 +0,0 @@ - -@objc(LKDeviceLinkingUtilities) -public final class DeviceLinkingUtilities : NSObject { - private static var lastUnexpectedDeviceLinkRequestDate: Date? = nil - - private override init() { } - - @objc public static var shouldShowUnexpectedDeviceLinkRequestReceivedAlert: Bool { - let now = Date() - if let lastUnexpectedDeviceLinkRequestDate = lastUnexpectedDeviceLinkRequestDate { - if now.timeIntervalSince(lastUnexpectedDeviceLinkRequestDate) < 30 { return false } - } - lastUnexpectedDeviceLinkRequestDate = now - return true - } - - // When requesting a device link, the slave device signs the master device's public key. When authorizing - // a device link, the master device signs the slave device's public key. - - public static func getLinkingRequestMessage(for masterPublicKey: String) -> DeviceLinkMessage { - let slaveKeyPair = OWSIdentityManager.shared().identityKeyPair()! - let slavePublicKey = slaveKeyPair.hexEncodedPublicKey - var kind = UInt8(LKDeviceLinkMessageKind.request.rawValue) - let data = Data(hex: masterPublicKey) + Data(bytes: &kind, count: MemoryLayout.size(ofValue: kind)) - let slaveSignature = try! Ed25519.sign(data, with: slaveKeyPair) - let thread = TSContactThread.getOrCreateThread(contactId: masterPublicKey) - return DeviceLinkMessage(in: thread, masterPublicKey: masterPublicKey, slavePublicKey: slavePublicKey, masterSignature: nil, slaveSignature: slaveSignature) - } - - public static func getLinkingAuthorizationMessage(for deviceLink: DeviceLink) -> DeviceLinkMessage { - let masterKeyPair = OWSIdentityManager.shared().identityKeyPair()! - let masterPublicKey = masterKeyPair.hexEncodedPublicKey - let slavePublicKey = deviceLink.slave.publicKey - var kind = UInt8(LKDeviceLinkMessageKind.authorization.rawValue) - let data = Data(hex: slavePublicKey) + Data(bytes: &kind, count: MemoryLayout.size(ofValue: kind)) - let masterSignature = try! Ed25519.sign(data, with: masterKeyPair) - let slaveSignature = deviceLink.slave.signature! - let thread = TSContactThread.getOrCreateThread(contactId: slavePublicKey) - return DeviceLinkMessage(in: thread, masterPublicKey: masterPublicKey, slavePublicKey: slavePublicKey, masterSignature: masterSignature, slaveSignature: slaveSignature) - } - - public static func hasValidSlaveSignature(_ deviceLink: DeviceLink) -> Bool { - guard let slaveSignature = deviceLink.slave.signature else { return false } - let slavePublicKey = Data(hex: deviceLink.slave.publicKey.removing05PrefixIfNeeded()) - var kind = UInt8(LKDeviceLinkMessageKind.request.rawValue) - let data = Data(hex: deviceLink.master.publicKey) + Data(bytes: &kind, count: MemoryLayout.size(ofValue: kind)) - return (try? Ed25519.verifySignature(slaveSignature, publicKey: slavePublicKey, data: data)) ?? false - } - - public static func hasValidMasterSignature(_ deviceLink: DeviceLink) -> Bool { - guard let masterSignature = deviceLink.master.signature else { return false } - let masterPublicKey = Data(hex: deviceLink.master.publicKey.removing05PrefixIfNeeded()) - var kind = UInt8(LKDeviceLinkMessageKind.authorization.rawValue) - let data = Data(hex: deviceLink.slave.publicKey) + Data(bytes: &kind, count: MemoryLayout.size(ofValue: kind)) - return (try? Ed25519.verifySignature(masterSignature, publicKey: masterPublicKey, data: data)) ?? false - } -} diff --git a/SignalUtilitiesKit/DeviceNames.swift b/SignalUtilitiesKit/DeviceNames.swift deleted file mode 100644 index e349a56f4..000000000 --- a/SignalUtilitiesKit/DeviceNames.swift +++ /dev/null @@ -1,218 +0,0 @@ -// -// Copyright (c) 2018 Open Whisper Systems. All rights reserved. -// - -import Foundation -import Curve25519Kit -import SignalCoreKit - -@objc -public enum DeviceNameError: Int, Error { - case assertionFailure - case invalidInput -} - -@objc -public class DeviceNames: NSObject { - // Never instantiate this class. - private override init() {} - - private static let syntheticIVLength: UInt = 16 - - @objc - public class func encryptDeviceName(plaintext: String, - identityKeyPair: ECKeyPair) throws -> Data { - - guard let plaintextData = plaintext.data(using: .utf8) else { - owsFailDebug("Could not convert text to UTF-8.") - throw DeviceNameError.invalidInput - } - - let ephemeralKeyPair = Curve25519.generateKeyPair() - - // master_secret = ECDH(ephemeral_private, identity_public). - let masterSecret: Data - do { - masterSecret = try Curve25519.generateSharedSecret(fromPublicKey: identityKeyPair.publicKey, privateKey: ephemeralKeyPair.privateKey) - } catch { - Logger.error("Could not generate shared secret: \(error)") - throw error - } - - // synthetic_iv = HmacSHA256(key=HmacSHA256(key=master_secret, input=“auth”), input=plaintext)[0:16] - let syntheticIV = try computeSyntheticIV(masterSecret: masterSecret, - plaintextData: plaintextData) - - // cipher_key = HmacSHA256(key=HmacSHA256(key=master_secret, “cipher”), input=synthetic_iv) - let cipherKey = try computeCipherKey(masterSecret: masterSecret, syntheticIV: syntheticIV) - - // cipher_text = AES-CTR(key=cipher_key, input=plaintext, counter=0) - // - // An all-zeros IV corresponds to an AES CTR counter of zero. - let ciphertextIV = Data(count: Int(kAES256CTR_IVLength)) - guard let ciphertextKey = OWSAES256Key(data: cipherKey) else { - owsFailDebug("Invalid cipher key.") - throw DeviceNameError.assertionFailure - } - guard let ciphertext: AES256CTREncryptionResult = Cryptography.encryptAESCTR(plaintextData: plaintextData, initializationVector: ciphertextIV, key: ciphertextKey) else { - owsFailDebug("Could not encrypt cipher text.") - throw DeviceNameError.assertionFailure - } - - guard let keyData = (ephemeralKeyPair.publicKey as NSData).prependKeyType() else { - owsFailDebug("Could not prepend key type.") - throw DeviceNameError.assertionFailure - } - let protoBuilder = SignalIOSProtoDeviceName.builder(ephemeralPublic: keyData as Data, - syntheticIv: syntheticIV, - ciphertext: ciphertext.ciphertext) - let protoData = try protoBuilder.buildSerializedData() - - // NOTE: This uses Data's foundation method rather than the NSData's SSK method. - let protoDataBase64 = protoData.base64EncodedData() - - return protoDataBase64 - } - - private class func computeSyntheticIV(masterSecret: Data, - plaintextData: Data) throws -> Data { - // synthetic_iv = HmacSHA256(key=HmacSHA256(key=master_secret, input=“auth”), input=plaintext)[0:16] - guard let syntheticIVInput = "auth".data(using: .utf8) else { - owsFailDebug("Could not convert text to UTF-8.") - throw DeviceNameError.assertionFailure - } - guard let syntheticIVKey = Cryptography.computeSHA256HMAC(syntheticIVInput, withHMACKey: masterSecret) else { - owsFailDebug("Could not compute synthetic IV key.") - throw DeviceNameError.assertionFailure - } - guard let syntheticIV = Cryptography.truncatedSHA256HMAC(plaintextData, withHMACKey: syntheticIVKey, truncation: syntheticIVLength) else { - owsFailDebug("Could not compute synthetic IV.") - throw DeviceNameError.assertionFailure - } - return syntheticIV - } - - private class func computeCipherKey(masterSecret: Data, - syntheticIV: Data) throws -> Data { - // cipher_key = HmacSHA256(key=HmacSHA256(key=master_secret, “cipher”), input=synthetic_iv) - guard let cipherKeyInput = "cipher".data(using: .utf8) else { - owsFailDebug("Could not convert text to UTF-8.") - throw DeviceNameError.assertionFailure - } - guard let cipherKeyKey = Cryptography.computeSHA256HMAC(cipherKeyInput, withHMACKey: masterSecret) else { - owsFailDebug("Could not compute cipher key key.") - throw DeviceNameError.assertionFailure - } - guard let cipherKey = Cryptography.computeSHA256HMAC(syntheticIV, withHMACKey: cipherKeyKey) else { - owsFailDebug("Could not compute cipher key.") - throw DeviceNameError.assertionFailure - } - return cipherKey - } - - @objc - public class func decryptDeviceName(base64String: String, - identityKeyPair: ECKeyPair) throws -> String { - - guard let protoData = Data(base64Encoded: base64String) else { - // Not necessarily an error; might be a legacy device name. - throw DeviceNameError.invalidInput - } - - return try decryptDeviceName(protoData: protoData, - identityKeyPair: identityKeyPair) - } - - @objc - public class func decryptDeviceName(base64Data: Data, - identityKeyPair: ECKeyPair) throws -> String { - - guard let protoData = Data(base64Encoded: base64Data) else { - // Not necessarily an error; might be a legacy device name. - throw DeviceNameError.invalidInput - } - - return try decryptDeviceName(protoData: protoData, - identityKeyPair: identityKeyPair) - } - - @objc - public class func decryptDeviceName(protoData: Data, - identityKeyPair: ECKeyPair) throws -> String { - - let proto: SignalIOSProtoDeviceName - do { - proto = try SignalIOSProtoDeviceName.parseData(protoData) - } catch { - // Not necessarily an error; might be a legacy device name. - Logger.error("failed to parse proto") - throw DeviceNameError.invalidInput - } - - let ephemeralPublicData = proto.ephemeralPublic - let receivedSyntheticIV = proto.syntheticIv - let ciphertext = proto.ciphertext - - let ephemeralPublic: Data - do { - ephemeralPublic = try (ephemeralPublicData as NSData).removeKeyType() as Data - } catch { - owsFailDebug("failed to remove key type") - throw DeviceNameError.invalidInput - } - - guard ephemeralPublic.count > 0 else { - owsFailDebug("Invalid ephemeral public.") - throw DeviceNameError.assertionFailure - } - guard receivedSyntheticIV.count == syntheticIVLength else { - owsFailDebug("Invalid synthetic IV.") - throw DeviceNameError.assertionFailure - } - guard ciphertext.count > 0 else { - owsFailDebug("Invalid cipher text.") - throw DeviceNameError.assertionFailure - } - - // master_secret = ECDH(identity_private, ephemeral_public) - let masterSecret: Data - do { - masterSecret = try Curve25519.generateSharedSecret(fromPublicKey: ephemeralPublic, privateKey: identityKeyPair.privateKey) - } catch { - Logger.error("Could not generate shared secret: \(error)") - throw error - } - - // cipher_key = HmacSHA256(key=HmacSHA256(key=master_secret, input=“cipher”), input=synthetic_iv) - let cipherKey = try computeCipherKey(masterSecret: masterSecret, syntheticIV: receivedSyntheticIV) - - // plaintext = AES-CTR(key=cipher_key, input=ciphertext, counter=0) - // - // An all-zeros IV corresponds to an AES CTR counter of zero. - let ciphertextIV = Data(count: Int(kAES256CTR_IVLength)) - guard let ciphertextKey = OWSAES256Key(data: cipherKey) else { - owsFailDebug("Invalid cipher key.") - throw DeviceNameError.assertionFailure - } - guard let plaintextData = Cryptography.decryptAESCTR(cipherText: ciphertext, initializationVector: ciphertextIV, key: ciphertextKey) else { - owsFailDebug("Could not decrypt cipher text.") - throw DeviceNameError.assertionFailure - } - - // Verify the synthetic IV was correct. - // constant_time_compare(HmacSHA256(key=HmacSHA256(key=master_secret, input=”auth”), input=plaintext)[0:16], synthetic_iv) == true - let computedSyntheticIV = try computeSyntheticIV(masterSecret: masterSecret, - plaintextData: plaintextData) - guard receivedSyntheticIV.ows_constantTimeIsEqual(to: computedSyntheticIV) else { - owsFailDebug("Synthetic IV did not match.") - throw DeviceNameError.assertionFailure - } - - guard let plaintext = String(bytes: plaintextData, encoding: .utf8) else { - owsFailDebug("Invalid plaintext.") - throw DeviceNameError.invalidInput - } - - return plaintext - } -} diff --git a/SignalUtilitiesKit/EditContactShareNameViewController.swift b/SignalUtilitiesKit/EditContactShareNameViewController.swift deleted file mode 100644 index 9cd8b4fb3..000000000 --- a/SignalUtilitiesKit/EditContactShareNameViewController.swift +++ /dev/null @@ -1,332 +0,0 @@ -// -// Copyright (c) 2018 Open Whisper Systems. All rights reserved. -// - -import Foundation - -@objc -public protocol ContactNameFieldViewDelegate: class { - func nameFieldDidChange() -} - -// MARK: - - -class ContactNameFieldView: UIView { - weak var delegate: ContactNameFieldViewDelegate? - - let name: String - let initialValue: String? - - var valueView: UITextField! - - var hasUnsavedChanges = false - - // MARK: - Initializers - - @available(*, unavailable, message: "use other constructor instead.") - required init?(coder aDecoder: NSCoder) { - notImplemented() - } - - required init(name: String, value: String?, delegate: ContactNameFieldViewDelegate) { - self.name = name - self.initialValue = value - self.delegate = delegate - - super.init(frame: CGRect.zero) - - self.isUserInteractionEnabled = true - self.addGestureRecognizer(UITapGestureRecognizer(target: self, action: #selector(wasTapped))) - - createContents() - } - - private let hMargin = CGFloat(16) - private let vMargin = CGFloat(10) - - func createContents() { - self.layoutMargins = UIEdgeInsets(top: vMargin, left: hMargin, bottom: vMargin, right: hMargin) - - let stackView = UIStackView() - stackView.axis = .horizontal - stackView.alignment = .center - stackView.layoutMargins = .zero - stackView.spacing = 10 - self.addSubview(stackView) - stackView.autoPinEdgesToSuperviewMargins() - - let nameLabel = UILabel() - nameLabel.text = name - nameLabel.font = UIFont.ows_dynamicTypeBody - nameLabel.textColor = UIColor.ows_materialBlue - nameLabel.lineBreakMode = .byTruncatingTail - stackView.addArrangedSubview(nameLabel) - nameLabel.setContentHuggingHigh() - nameLabel.setCompressionResistanceHigh() - - valueView = UITextField() - if let initialValue = initialValue { - valueView.text = initialValue - } - valueView.font = UIFont.ows_dynamicTypeBody - valueView.textColor = Theme.primaryColor - stackView.addArrangedSubview(valueView) - - valueView.addTarget(self, action: #selector(textFieldDidChange), for: .editingChanged) - } - - @objc func wasTapped(sender: UIGestureRecognizer) { - Logger.info("") - - guard sender.state == .recognized else { - return - } - - valueView.becomeFirstResponder() - } - - @objc func textFieldDidChange(sender: UITextField) { - Logger.info("") - - hasUnsavedChanges = true - - guard let delegate = self.delegate else { - owsFailDebug("missing delegate.") - return - } - - delegate.nameFieldDidChange() - } - - public func value() -> String { - guard let value = valueView.text else { - return "" - } - return value - } -} - -// MARK: - - -@objc -public protocol EditContactShareNameViewControllerDelegate: class { - func editContactShareNameView(_ editContactShareNameView: EditContactShareNameViewController, - didEditContactShare contactShare: ContactShareViewModel) -} - -// MARK: - - -@objc -public class EditContactShareNameViewController: OWSViewController, ContactNameFieldViewDelegate { - weak var delegate: EditContactShareNameViewControllerDelegate? - - let contactShare: ContactShareViewModel - - var namePrefixView: ContactNameFieldView! - var givenNameView: ContactNameFieldView! - var middleNameView: ContactNameFieldView! - var familyNameView: ContactNameFieldView! - var nameSuffixView: ContactNameFieldView! - var organizationNameView: ContactNameFieldView! - - var fieldViews = [ContactNameFieldView]() - - // MARK: Initializers - - @available(*, unavailable, message:"use other constructor instead.") - required public init?(coder aDecoder: NSCoder) { - notImplemented() - } - - @objc - required public init(contactShare: ContactShareViewModel, delegate: EditContactShareNameViewControllerDelegate) { - self.contactShare = contactShare - self.delegate = delegate - - super.init(nibName: nil, bundle: nil) - - buildFields() - } - - func buildFields() { - namePrefixView = ContactNameFieldView(name: NSLocalizedString("CONTACT_FIELD_NAME_PREFIX", - comment: "Label for the 'name prefix' field of a contact."), - value: contactShare.name.namePrefix, delegate: self) - givenNameView = ContactNameFieldView(name: NSLocalizedString("CONTACT_FIELD_GIVEN_NAME", - comment: "Label for the 'given name' field of a contact."), - value: contactShare.name.givenName, delegate: self) - middleNameView = ContactNameFieldView(name: NSLocalizedString("CONTACT_FIELD_MIDDLE_NAME", - comment: "Label for the 'middle name' field of a contact."), - value: contactShare.name.middleName, delegate: self) - familyNameView = ContactNameFieldView(name: NSLocalizedString("CONTACT_FIELD_FAMILY_NAME", - comment: "Label for the 'family name' field of a contact."), - value: contactShare.name.familyName, delegate: self) - nameSuffixView = ContactNameFieldView(name: NSLocalizedString("CONTACT_FIELD_NAME_SUFFIX", - comment: "Label for the 'name suffix' field of a contact."), - value: contactShare.name.nameSuffix, delegate: self) - organizationNameView = ContactNameFieldView(name: NSLocalizedString("CONTACT_FIELD_ORGANIZATION", - comment: "Label for the 'organization' field of a contact."), - value: contactShare.name.organizationName, delegate: self) - fieldViews = [ - namePrefixView , - givenNameView , - middleNameView , - familyNameView , - nameSuffixView, - organizationNameView - ] - } - - override public var canBecomeFirstResponder: Bool { - return true - } - - // MARK: - View Lifecycle - - override public func viewWillAppear(_ animated: Bool) { - super.viewWillAppear(animated) - - updateNavigationBar() - } - - override public func viewDidAppear(_ animated: Bool) { - super.viewDidAppear(animated) - } - - override public func viewWillDisappear(_ animated: Bool) { - super.viewWillDisappear(animated) - } - - override public func viewDidDisappear(_ animated: Bool) { - super.viewDidDisappear(animated) - } - - override public func loadView() { - super.loadView() - - self.navigationItem.title = NSLocalizedString("CONTACT_SHARE_EDIT_NAME_VIEW_TITLE", - comment: "Title for the 'edit contact share name' view.") - - self.view.preservesSuperviewLayoutMargins = false - self.view.backgroundColor = Theme.backgroundColor - - updateContent() - - updateNavigationBar() - } - - func hasUnsavedChanges() -> Bool { - for fieldView in fieldViews { - if fieldView.hasUnsavedChanges { - return true - } - } - return false - } - - func updateNavigationBar() { - self.navigationItem.leftBarButtonItem = UIBarButtonItem(barButtonSystemItem: .cancel, - target: self, - action: #selector(didPressCancel)) - - if hasUnsavedChanges() { - self.navigationItem.rightBarButtonItem = - UIBarButtonItem(barButtonSystemItem: .save, - target: self, - action: #selector(didPressSave)) - } else { - self.navigationItem.rightBarButtonItem = nil - } - } - - private func updateContent() { - AssertIsOnMainThread() - - guard let rootView = self.view else { - owsFailDebug("missing root view.") - return - } - - for subview in rootView.subviews { - subview.removeFromSuperview() - } - - let scrollView = UIScrollView() - scrollView.preservesSuperviewLayoutMargins = false - self.view.addSubview(scrollView) - scrollView.layoutMargins = .zero - scrollView.autoPinWidthToSuperview() - scrollView.autoPinEdge(.top, to: .top, of: view) - scrollView.autoPinEdge(toSuperviewEdge: .bottom) - - let fieldsView = createFieldsView() - - scrollView.addSubview(fieldsView) - fieldsView.autoPinLeadingToSuperviewMargin() - fieldsView.autoPinTrailingToSuperviewMargin() - fieldsView.autoPinEdge(toSuperviewEdge: .top) - fieldsView.autoPinEdge(toSuperviewEdge: .bottom) - } - - private func createFieldsView() -> UIView { - AssertIsOnMainThread() - - var rows = [UIView]() - - for fieldView in fieldViews { - rows.append(fieldView) - } - - return ContactFieldView(rows: rows, hMargin: hMargin) - } - - private let hMargin = CGFloat(16) - - // MARK: - - - @objc func didPressSave() { - Logger.info("") - - guard let newName = OWSContactName() else { - owsFailDebug("could not create a new name.") - return - } - newName.namePrefix = namePrefixView.value().ows_stripped() - newName.givenName = givenNameView.value().ows_stripped() - newName.middleName = middleNameView.value().ows_stripped() - newName.familyName = familyNameView.value().ows_stripped() - newName.nameSuffix = nameSuffixView.value().ows_stripped() - newName.organizationName = organizationNameView.value().ows_stripped() - - let modifiedContactShare = contactShare.copy(withName: newName) - - guard let delegate = self.delegate else { - owsFailDebug("missing delegate.") - return - } - - delegate.editContactShareNameView(self, didEditContactShare: modifiedContactShare) - - guard let navigationController = self.navigationController else { - owsFailDebug("Missing navigationController.") - return - } - navigationController.popViewController(animated: true) - } - - @objc func didPressCancel() { - Logger.info("") - - guard let navigationController = self.navigationController else { - owsFailDebug("Missing navigationController.") - return - } - navigationController.popViewController(animated: true) - } - - // MARK: - ContactNameFieldViewDelegate - - public func nameFieldDidChange() { - updateNavigationBar() - } -} diff --git a/SignalUtilitiesKit/EncryptionUtilities.swift b/SignalUtilitiesKit/EncryptionUtilities.swift deleted file mode 100644 index cdb4db90f..000000000 --- a/SignalUtilitiesKit/EncryptionUtilities.swift +++ /dev/null @@ -1,38 +0,0 @@ -import CryptoSwift - -internal typealias EncryptionResult = (ciphertext: Data, symmetricKey: Data, ephemeralPublicKey: Data) - -enum EncryptionUtilities { - internal static let gcmTagSize: UInt = 16 - internal static let ivSize: UInt = 12 - - /// - Note: Sync. Don't call from the main thread. - internal static func encrypt(_ plaintext: Data, usingAESGCMWithSymmetricKey symmetricKey: Data) throws -> Data { - if Thread.isMainThread { - #if DEBUG - preconditionFailure("It's illegal to call encrypt(_:usingAESGCMWithSymmetricKey:) from the main thread.") - #endif - } - let iv = Data.getSecureRandomData(ofSize: ivSize)! - let gcm = GCM(iv: iv.bytes, tagLength: Int(gcmTagSize), mode: .combined) - let aes = try AES(key: symmetricKey.bytes, blockMode: gcm, padding: .noPadding) - let ciphertext = try aes.encrypt(plaintext.bytes) - return iv + Data(ciphertext) - } - - /// - Note: Sync. Don't call from the main thread. - internal static func encrypt(_ plaintext: Data, using hexEncodedX25519PublicKey: String) throws -> EncryptionResult { - if Thread.isMainThread { - #if DEBUG - preconditionFailure("It's illegal to call encrypt(_:forSnode:) from the main thread.") - #endif - } - let x25519PublicKey = Data(hex: hexEncodedX25519PublicKey) - let ephemeralKeyPair = Curve25519.generateKeyPair() - let ephemeralSharedSecret = try! Curve25519.generateSharedSecret(fromPublicKey: x25519PublicKey, privateKey: ephemeralKeyPair.privateKey) - let salt = "LOKI" - let symmetricKey = try HMAC(key: salt.bytes, variant: .sha256).authenticate(ephemeralSharedSecret.bytes) - let ciphertext = try encrypt(plaintext, usingAESGCMWithSymmetricKey: Data(bytes: symmetricKey)) - return (ciphertext, Data(bytes: symmetricKey), ephemeralKeyPair.publicKey) - } -} diff --git a/SignalUtilitiesKit/Environment.h b/SignalUtilitiesKit/Environment.h index 10b08c09f..64d7ddc61 100644 --- a/SignalUtilitiesKit/Environment.h +++ b/SignalUtilitiesKit/Environment.h @@ -1,7 +1,6 @@ #import @class OWSAudioSession; -@class OWSContactsManager; @class OWSPreferences; @class OWSSounds; @class OWSWindowManager; @@ -26,7 +25,6 @@ windowManager:(OWSWindowManager *)windowManager; @property (nonatomic, readonly) OWSAudioSession *audioSession; -@property (nonatomic, readonly) OWSContactsManager *contactsManager; @property (nonatomic, readonly) id proximityMonitoringManager; @property (nonatomic, readonly) OWSPreferences *preferences; @property (nonatomic, readonly) OWSSounds *sounds; diff --git a/SignalUtilitiesKit/Environment.m b/SignalUtilitiesKit/Environment.m index 0e591e592..2f282d3cc 100644 --- a/SignalUtilitiesKit/Environment.m +++ b/SignalUtilitiesKit/Environment.m @@ -1,6 +1,6 @@ + #import #import "SSKAsserts.h" -#import "OWSContactsManager.h" #import "OWSWindowManager.h" #import #import "OWSPreferences.h" @@ -11,7 +11,6 @@ static Environment *sharedEnvironment = nil; @interface Environment () @property (nonatomic) OWSAudioSession *audioSession; -@property (nonatomic) OWSContactsManager *contactsManager; @property (nonatomic) OWSPreferences *preferences; @property (nonatomic) id proximityMonitoringManager; @property (nonatomic) OWSSounds *sounds; @@ -54,6 +53,7 @@ static Environment *sharedEnvironment = nil; windowManager:(OWSWindowManager *)windowManager { self = [super init]; + if (!self) { return self; } @@ -75,9 +75,4 @@ static Environment *sharedEnvironment = nil; return self; } -- (OWSContactsManager *)contactsManager -{ - return (OWSContactsManager *)SSKEnvironment.shared.contactsManager; -} - @end diff --git a/SignalUtilitiesKit/FileServerAPI+Deprecated.swift b/SignalUtilitiesKit/FileServerAPI+Deprecated.swift deleted file mode 100644 index 1fd608a6f..000000000 --- a/SignalUtilitiesKit/FileServerAPI+Deprecated.swift +++ /dev/null @@ -1,148 +0,0 @@ -import PromiseKit - -public extension FileServerAPI { - - /// Gets the device links associated with the given hex encoded public key from the - /// server and stores and returns the valid ones. - /// - /// - Note: Deprecated. - public static func getDeviceLinks(associatedWith hexEncodedPublicKey: String) -> Promise> { - return getDeviceLinks(associatedWith: [ hexEncodedPublicKey ]) - } - - /// Gets the device links associated with the given hex encoded public keys from the - /// server and stores and returns the valid ones. - /// - /// - Note: Deprecated. - public static func getDeviceLinks(associatedWith hexEncodedPublicKeys: Set) -> Promise> { - return Promise.value([]) - /* - let hexEncodedPublicKeysDescription = "[ \(hexEncodedPublicKeys.joined(separator: ", ")) ]" - print("[Loki] Getting device links for: \(hexEncodedPublicKeysDescription).") - return getAuthToken(for: server).then2 { token -> Promise> in - let queryParameters = "ids=\(hexEncodedPublicKeys.map { "@\($0)" }.joined(separator: ","))&include_user_annotations=1" - let url = URL(string: "\(server)/users?\(queryParameters)")! - let request = TSRequest(url: url) - return OnionRequestAPI.sendOnionRequest(request, to: server, using: fileServerPublicKey).map2 { rawResponse -> Set in - guard let data = rawResponse["data"] as? [JSON] else { - print("[Loki] Couldn't parse device links for users: \(hexEncodedPublicKeys) from: \(rawResponse).") - throw DotNetAPIError.parsingFailed - } - return Set(data.flatMap { data -> [DeviceLink] in - guard let annotations = data["annotations"] as? [JSON], !annotations.isEmpty else { return [] } - guard let annotation = annotations.first(where: { $0["type"] as? String == deviceLinkType }), - let value = annotation["value"] as? JSON, let rawDeviceLinks = value["authorisations"] as? [JSON], - let hexEncodedPublicKey = data["username"] as? String else { - print("[Loki] Couldn't parse device links from: \(rawResponse).") - return [] - } - return rawDeviceLinks.compactMap { rawDeviceLink in - guard let masterPublicKey = rawDeviceLink["primaryDevicePubKey"] as? String, let slavePublicKey = rawDeviceLink["secondaryDevicePubKey"] as? String, - let base64EncodedSlaveSignature = rawDeviceLink["requestSignature"] as? String else { - print("[Loki] Couldn't parse device link for user: \(hexEncodedPublicKey) from: \(rawResponse).") - return nil - } - let masterSignature: Data? - if let base64EncodedMasterSignature = rawDeviceLink["grantSignature"] as? String { - masterSignature = Data(base64Encoded: base64EncodedMasterSignature) - } else { - masterSignature = nil - } - let slaveSignature = Data(base64Encoded: base64EncodedSlaveSignature) - let master = DeviceLink.Device(publicKey: masterPublicKey, signature: masterSignature) - let slave = DeviceLink.Device(publicKey: slavePublicKey, signature: slaveSignature) - let deviceLink = DeviceLink(between: master, and: slave) - if let masterSignature = masterSignature { - guard DeviceLinkingUtilities.hasValidMasterSignature(deviceLink) else { - print("[Loki] Received a device link with an invalid master signature.") - return nil - } - } - guard DeviceLinkingUtilities.hasValidSlaveSignature(deviceLink) else { - print("[Loki] Received a device link with an invalid slave signature.") - return nil - } - return deviceLink - } - }) - }.map2 { deviceLinks in - storage.setDeviceLinks(deviceLinks) - return deviceLinks - } - }.handlingInvalidAuthTokenIfNeeded(for: server) - */ - } - - /// - Note: Deprecated. - public static func setDeviceLinks(_ deviceLinks: Set) -> Promise { - return Promise.value(()) - /* - print("[Loki] Updating device links.") - return getAuthToken(for: server).then2 { token -> Promise in - let isMaster = deviceLinks.contains { $0.master.publicKey == getUserHexEncodedPublicKey() } - let deviceLinksAsJSON = deviceLinks.map { $0.toJSON() } - let value = !deviceLinksAsJSON.isEmpty ? [ "isPrimary" : isMaster ? 1 : 0, "authorisations" : deviceLinksAsJSON ] : nil - let annotation: JSON = [ "type" : deviceLinkType, "value" : value ] - let parameters: JSON = [ "annotations" : [ annotation ] ] - let url = URL(string: "\(server)/users/me")! - let request = TSRequest(url: url, method: "PATCH", parameters: parameters) - request.allHTTPHeaderFields = [ "Content-Type" : "application/json", "Authorization" : "Bearer \(token)" ] - return attempt(maxRetryCount: 8, recoveringOn: SnodeAPI.workQueue) { - OnionRequestAPI.sendOnionRequest(request, to: server, using: fileServerPublicKey).map2 { _ in } - }.handlingInvalidAuthTokenIfNeeded(for: server).recover2 { error in - print("[Loki] Couldn't update device links due to error: \(error).") - throw error - } - } - */ - } - - /// Adds the given device link to the user's device mapping on the server. - /// - /// - Note: Deprecated. - public static func addDeviceLink(_ deviceLink: DeviceLink) -> Promise { - return Promise.value(()) - /* - var deviceLinks: Set = [] - storage.dbReadConnection.read { transaction in - deviceLinks = storage.getDeviceLinks(for: getUserHexEncodedPublicKey(), in: transaction) - } - deviceLinks.insert(deviceLink) - return setDeviceLinks(deviceLinks).map2 { _ in - storage.addDeviceLink(deviceLink) - } - */ - } - - /// Removes the given device link from the user's device mapping on the server. - /// - /// - Note: Deprecated. - public static func removeDeviceLink(_ deviceLink: DeviceLink) -> Promise { - return Promise.value(()) - /* - var deviceLinks: Set = [] - storage.dbReadConnection.read { transaction in - deviceLinks = storage.getDeviceLinks(for: getUserHexEncodedPublicKey(), in: transaction) - } - deviceLinks.remove(deviceLink) - return setDeviceLinks(deviceLinks).map2 { _ in - storage.removeDeviceLink(deviceLink) - } - */ - } -} - -@objc public extension FileServerAPI { - - /// - Note: Deprecated. - @objc(getDeviceLinksAssociatedWithHexEncodedPublicKey:) - public static func objc_getDeviceLinks(associatedWith hexEncodedPublicKey: String) -> AnyPromise { - return AnyPromise.from(getDeviceLinks(associatedWith: hexEncodedPublicKey)) - } - - /// - Note: Deprecated. - @objc(getDeviceLinksAssociatedWithHexEncodedPublicKeys:) - public static func objc_getDeviceLinks(associatedWith hexEncodedPublicKeys: Set) -> AnyPromise { - return AnyPromise.from(getDeviceLinks(associatedWith: hexEncodedPublicKeys)) - } -} diff --git a/SignalUtilitiesKit/FullTextSearchFinder.swift b/SignalUtilitiesKit/FullTextSearchFinder.swift index 1fcd44e96..d6a3913b1 100644 --- a/SignalUtilitiesKit/FullTextSearchFinder.swift +++ b/SignalUtilitiesKit/FullTextSearchFinder.swift @@ -155,10 +155,6 @@ public class FullTextSearchFinder: NSObject { // MARK: - Index Building - private class var contactsManager: ContactsManagerProtocol { - return SSKEnvironment.shared.contactsManager - } - private static let groupThreadIndexer: SearchIndexer = SearchIndexer { (groupThread: TSGroupThread, transaction: YapDatabaseReadTransaction) in let groupName = groupThread.groupModel.groupName ?? "" @@ -185,24 +181,9 @@ public class FullTextSearchFinder: NSObject { } private static let recipientIndexer: SearchIndexer = SearchIndexer { (recipientId: String, transaction: YapDatabaseReadTransaction) in - let displayName = contactsManager.displayName(forPhoneIdentifier: recipientId, transaction: transaction) + let displayName = SSKEnvironment.shared.profileManager.profileNameForRecipient(withID: recipientId, avoidingWriteTransaction: true) - let nationalNumber: String = { (recipientId: String) -> String in - - guard let phoneNumber = PhoneNumber(fromE164: recipientId) else { - owsFailDebug("unexpected unparseable recipientId: \(recipientId)") - return "" - } - - guard let digitScalars = phoneNumber.nationalNumber?.unicodeScalars.filter({ CharacterSet.decimalDigits.contains($0) }) else { - owsFailDebug("unexpected unparseable recipientId: \(recipientId)") - return "" - } - - return String(String.UnicodeScalarView(digitScalars)) - }(recipientId) - - return "\(recipientId) \(nationalNumber) \(displayName)" + return "\(recipientId) \(displayName)" } private static let messageIndexer: SearchIndexer = SearchIndexer { (message: TSMessage, transaction: YapDatabaseReadTransaction) in diff --git a/SignalUtilitiesKit/FullTextSearcher.swift b/SignalUtilitiesKit/FullTextSearcher.swift index 285b5c332..d711bd614 100644 --- a/SignalUtilitiesKit/FullTextSearcher.swift +++ b/SignalUtilitiesKit/FullTextSearcher.swift @@ -438,14 +438,9 @@ public class FullTextSearcher: NSObject { return result } - private var contactsManager: OWSContactsManager { - return Environment.shared.contactsManager - } - private func indexingString(recipientId: String) -> String { - let contactName = contactsManager.displayName(forPhoneIdentifier: recipientId) - let profileName = contactsManager.profileName(forRecipientId: recipientId) + let profileName = SSKEnvironment.shared.profileManager.profileNameForRecipient(withID: recipientId, avoidingWriteTransaction: true) - return "\(recipientId) \(contactName) \(profileName ?? "")" + return "\(recipientId) \(profileName ?? "")" } } diff --git a/SignalUtilitiesKit/GroupUtilities.swift b/SignalUtilitiesKit/GroupUtilities.swift index 69b5f9ff1..51f6640a5 100644 --- a/SignalUtilitiesKit/GroupUtilities.swift +++ b/SignalUtilitiesKit/GroupUtilities.swift @@ -10,9 +10,7 @@ public enum GroupUtilities { } public static func getClosedGroupMembers(_ closedGroup: TSGroupThread, with transaction: YapDatabaseReadTransaction) -> [String] { - return closedGroup.groupModel.groupMemberIds.filter { member in - OWSPrimaryStorage.shared().getMasterHexEncodedPublicKey(for: member, in: transaction) == nil // Don't show slave devices - } + return closedGroup.groupModel.groupMemberIds } public static func getClosedGroupMemberCount(_ closedGroup: TSGroupThread) -> Int { diff --git a/SignalUtilitiesKit/LKDeviceLinkMessage.h b/SignalUtilitiesKit/LKDeviceLinkMessage.h deleted file mode 100644 index 783078a42..000000000 --- a/SignalUtilitiesKit/LKDeviceLinkMessage.h +++ /dev/null @@ -1,19 +0,0 @@ -#import "TSOutgoingMessage.h" - -typedef NS_ENUM(NSUInteger, LKDeviceLinkMessageKind) { - LKDeviceLinkMessageKindRequest = 1, - LKDeviceLinkMessageKindAuthorization = 2, -}; - -NS_SWIFT_NAME(DeviceLinkMessage) -@interface LKDeviceLinkMessage : TSOutgoingMessage - -@property (nonatomic, readonly) NSString *masterPublicKey; -@property (nonatomic, readonly) NSString *slavePublicKey; -@property (nonatomic, readonly) NSData *masterSignature; // nil for device linking requests -@property (nonatomic, readonly) NSData *slaveSignature; -@property (nonatomic, readonly) LKDeviceLinkMessageKind kind; - -- (instancetype)initInThread:(TSThread *)thread masterPublicKey:(NSString *)masterHexEncodedPublicKey slavePublicKey:(NSString *)slaveHexEncodedPublicKey masterSignature:(NSData * _Nullable)masterSignature slaveSignature:(NSData *)slaveSignature; - -@end diff --git a/SignalUtilitiesKit/LKDeviceLinkMessage.m b/SignalUtilitiesKit/LKDeviceLinkMessage.m deleted file mode 100644 index e1836a1fd..000000000 --- a/SignalUtilitiesKit/LKDeviceLinkMessage.m +++ /dev/null @@ -1,89 +0,0 @@ -#import "LKDeviceLinkMessage.h" -#import "OWSIdentityManager.h" -#import "OWSPrimaryStorage+Loki.h" -#import "ProfileManagerProtocol.h" -#import "ProtoUtils.h" -#import "SSKEnvironment.h" -#import "SignalRecipient.h" -#import -#import -#import - -@implementation LKDeviceLinkMessage - -#pragma mark Convenience -- (LKDeviceLinkMessageKind)kind { - if (self.masterSignature != nil) { - return LKDeviceLinkMessageKindAuthorization; - } else { - return LKDeviceLinkMessageKindRequest; - } -} - -#pragma mark Initialization -- (instancetype)initInThread:(TSThread *)thread masterPublicKey:(NSString *)masterHexEncodedPublicKey slavePublicKey:(NSString *)slaveHexEncodedPublicKey masterSignature:(NSData * _Nullable)masterSignature slaveSignature:(NSData *)slaveSignature { - self = [self initOutgoingMessageWithTimestamp:NSDate.ows_millisecondTimeStamp inThread:thread messageBody:@"" attachmentIds:[NSMutableArray new] - expiresInSeconds:0 expireStartedAt:0 isVoiceMessage:NO groupMetaMessage:TSGroupMetaMessageUnspecified quotedMessage:nil contactShare:nil linkPreview:nil]; - if (self) { - _masterPublicKey = masterHexEncodedPublicKey; - _slavePublicKey = slaveHexEncodedPublicKey; - _masterSignature = masterSignature; - _slaveSignature = slaveSignature; - } - return self; -} - -#pragma mark Building -- (nullable id)prepareCustomContentBuilder:(SignalRecipient *)recipient { - SSKProtoContentBuilder *contentBuilder = [super prepareCustomContentBuilder:recipient]; - NSError *error; - if (self.kind == LKDeviceLinkMessageKindRequest) { - // The slave device attaches a pre key bundle with the request it sends so that a - // session can be established with the master device. - PreKeyBundle *preKeyBundle = [OWSPrimaryStorage.sharedManager generatePreKeyBundleForContact:recipient.recipientId]; - SSKProtoPrekeyBundleMessageBuilder *preKeyBundleMessageBuilder = [SSKProtoPrekeyBundleMessage builderFromPreKeyBundle:preKeyBundle]; - SSKProtoPrekeyBundleMessage *preKeyBundleMessage = [preKeyBundleMessageBuilder buildAndReturnError:&error]; - if (error || preKeyBundleMessage == nil) { - OWSFailDebug(@"Failed to build pre key bundle message for: %@ due to error: %@.", recipient.recipientId, error); - return nil; - } else { - [contentBuilder setPrekeyBundleMessage:preKeyBundleMessage]; - } - } else { - // The master device attaches its display name and profile picture URL to the device link - // authorization message so that the slave device is in sync with these things as soon - // as possible. - id profileManager = SSKEnvironment.shared.profileManager; - NSString *displayName = profileManager.localProfileName; - NSString *profilePictureURL = profileManager.profilePictureURL; - SSKProtoDataMessageLokiProfileBuilder *profileBuilder = [SSKProtoDataMessageLokiProfile builder]; - [profileBuilder setDisplayName:displayName]; - [profileBuilder setProfilePicture:profilePictureURL ?: @""]; - SSKProtoDataMessageBuilder *messageBuilder = [SSKProtoDataMessage builder]; - [messageBuilder setProfile:[profileBuilder buildAndReturnError:nil]]; - [ProtoUtils addLocalProfileKeyToDataMessageBuilder:messageBuilder]; - [contentBuilder setDataMessage:[messageBuilder buildIgnoringErrors]]; - } - // Build the device link message - SSKProtoLokiDeviceLinkMessageBuilder *deviceLinkMessageBuilder = [SSKProtoLokiDeviceLinkMessage builder]; - [deviceLinkMessageBuilder setMasterPublicKey:self.masterPublicKey]; - [deviceLinkMessageBuilder setSlavePublicKey:self.slavePublicKey]; - if (self.masterSignature != nil) { [deviceLinkMessageBuilder setMasterSignature:self.masterSignature]; } - [deviceLinkMessageBuilder setSlaveSignature:self.slaveSignature]; - SSKProtoLokiDeviceLinkMessage *deviceLinkMessage = [deviceLinkMessageBuilder buildAndReturnError:&error]; - if (error || deviceLinkMessage == nil) { - OWSFailDebug(@"Failed to build device link message for: %@ due to error: %@.", recipient.recipientId, error); - return nil; - } else { - [contentBuilder setLokiDeviceLinkMessage:deviceLinkMessage]; - } - // Return - return contentBuilder; -} - -#pragma mark Settings -- (uint)ttl { return (uint)[LKTTLUtilities getTTLFor:LKMessageTypeLinkDevice]; } -- (BOOL)shouldSyncTranscript { return NO; } -- (BOOL)shouldBeSaved { return NO; } - -@end diff --git a/SignalUtilitiesKit/LKGroupUtilities.h b/SignalUtilitiesKit/LKGroupUtilities.h index b613a37aa..ac74482f4 100644 --- a/SignalUtilitiesKit/LKGroupUtilities.h +++ b/SignalUtilitiesKit/LKGroupUtilities.h @@ -7,9 +7,6 @@ NS_ASSUME_NONNULL_BEGIN +(NSString *)getEncodedOpenGroupID:(NSString *)groupID; +(NSData *)getEncodedOpenGroupIDAsData:(NSString *)groupID; -+(NSString *)getEncodedRSSFeedID:(NSString *)groupID; -+(NSData *)getEncodedRSSFeedIDAsData:(NSString *)groupID; - +(NSString *)getEncodedClosedGroupID:(NSString *)groupID; +(NSData *)getEncodedClosedGroupIDAsData:(NSString *)groupID; diff --git a/SignalUtilitiesKit/LKGroupUtilities.m b/SignalUtilitiesKit/LKGroupUtilities.m index f0cd741ad..9cced30f0 100644 --- a/SignalUtilitiesKit/LKGroupUtilities.m +++ b/SignalUtilitiesKit/LKGroupUtilities.m @@ -3,10 +3,9 @@ @implementation LKGroupUtilities -#define ClosedGroupPrefix @"__textsecure_group__!" // a.k.a. private group chat +#define ClosedGroupPrefix @"__textsecure_group__!" #define MMSGroupPrefix @"__signal_mms_group__!" -#define OpenGroupPrefix @"__loki_public_chat_group__!" // a.k.a. public group chat -#define RSSFeedPrefix @"__loki_rss_feed_group__!" +#define OpenGroupPrefix @"__loki_public_chat_group__!" +(NSString *)getEncodedOpenGroupID:(NSString *)groupID { @@ -18,16 +17,6 @@ return [[OpenGroupPrefix stringByAppendingString:groupID] dataUsingEncoding:NSUTF8StringEncoding]; } -+(NSString *)getEncodedRSSFeedID:(NSString *)groupID -{ - return [RSSFeedPrefix stringByAppendingString:groupID]; -} - -+(NSData *)getEncodedRSSFeedIDAsData:(NSString *)groupID -{ - return [[RSSFeedPrefix stringByAppendingString:groupID] dataUsingEncoding:NSUTF8StringEncoding]; -} - +(NSString *)getEncodedClosedGroupID:(NSString *)groupID { return [ClosedGroupPrefix stringByAppendingString:groupID]; diff --git a/SignalUtilitiesKit/LKSyncOpenGroupsMessage.h b/SignalUtilitiesKit/LKSyncOpenGroupsMessage.h deleted file mode 100644 index 4c4969128..000000000 --- a/SignalUtilitiesKit/LKSyncOpenGroupsMessage.h +++ /dev/null @@ -1,14 +0,0 @@ -#import - -NS_ASSUME_NONNULL_BEGIN - -NS_SWIFT_NAME(SyncOpenGroupsMessage) -@interface LKSyncOpenGroupsMessage : OWSOutgoingSyncMessage - -- (instancetype)init NS_DESIGNATED_INITIALIZER; - -- (nullable instancetype)initWithCoder:(NSCoder *)coder NS_DESIGNATED_INITIALIZER; - -@end - -NS_ASSUME_NONNULL_END diff --git a/SignalUtilitiesKit/LKSyncOpenGroupsMessage.m b/SignalUtilitiesKit/LKSyncOpenGroupsMessage.m deleted file mode 100644 index 8881ec3e1..000000000 --- a/SignalUtilitiesKit/LKSyncOpenGroupsMessage.m +++ /dev/null @@ -1,43 +0,0 @@ -#import "LKSyncOpenGroupsMessage.h" -#import "OWSPrimaryStorage.h" -#import - -NS_ASSUME_NONNULL_BEGIN - -@implementation LKSyncOpenGroupsMessage - -- (instancetype)init -{ - return [super init]; -} - -- (nullable instancetype)initWithCoder:(NSCoder *)coder -{ - return [super initWithCoder:coder]; -} - -- (nullable SSKProtoSyncMessageBuilder *)syncMessageBuilder -{ - NSError *error; - NSMutableArray *openGroupSyncMessages = @[].mutableCopy; - __block NSDictionary *openGroups; - [OWSPrimaryStorage.sharedManager.dbReadConnection readWithBlock:^(YapDatabaseReadTransaction *transaction) { - openGroups = [LKDatabaseUtilities getAllPublicChats:transaction]; - }]; - for (SNOpenGroup *openGroup in openGroups.allValues) { - SSKProtoSyncMessageOpenGroupDetailsBuilder *openGroupSyncMessageBuilder = [SSKProtoSyncMessageOpenGroupDetails builderWithUrl:openGroup.server channelID:openGroup.channel]; - SSKProtoSyncMessageOpenGroupDetails *_Nullable openGroupSyncMessage = [openGroupSyncMessageBuilder buildAndReturnError:&error]; - if (error || !openGroupSyncMessage) { - OWSFailDebug(@"Couldn't build protobuf due to error: %@.", error); - return nil; - } - [openGroupSyncMessages addObject:openGroupSyncMessage]; - } - SSKProtoSyncMessageBuilder *syncMessageBuilder = [SSKProtoSyncMessage builder]; - [syncMessageBuilder setOpenGroups:openGroupSyncMessages]; - return syncMessageBuilder; -} - -@end - -NS_ASSUME_NONNULL_END diff --git a/SignalUtilitiesKit/LKUnlinkDeviceMessage.h b/SignalUtilitiesKit/LKUnlinkDeviceMessage.h deleted file mode 100644 index 4999120e7..000000000 --- a/SignalUtilitiesKit/LKUnlinkDeviceMessage.h +++ /dev/null @@ -1,12 +0,0 @@ -#import - -NS_ASSUME_NONNULL_BEGIN - -NS_SWIFT_NAME(UnlinkDeviceMessage) -@interface LKUnlinkDeviceMessage : TSOutgoingMessage - -- (instancetype)initWithThread:(TSThread *)thread; - -@end - -NS_ASSUME_NONNULL_END diff --git a/SignalUtilitiesKit/LKUnlinkDeviceMessage.m b/SignalUtilitiesKit/LKUnlinkDeviceMessage.m deleted file mode 100644 index d1a7594e6..000000000 --- a/SignalUtilitiesKit/LKUnlinkDeviceMessage.m +++ /dev/null @@ -1,27 +0,0 @@ -#import "LKUnlinkDeviceMessage.h" -#import -#import - -@implementation LKUnlinkDeviceMessage - -#pragma mark Initialization -- (instancetype)initWithThread:(TSThread *)thread { - return [self initOutgoingMessageWithTimestamp:NSDate.ows_millisecondTimeStamp inThread:thread messageBody:@"" attachmentIds:[NSMutableArray new] - expiresInSeconds:0 expireStartedAt:0 isVoiceMessage:NO groupMetaMessage:TSGroupMetaMessageUnspecified quotedMessage:nil contactShare:nil linkPreview:nil]; -} - -#pragma mark Building -- (nullable id)dataMessageBuilder -{ - SSKProtoDataMessageBuilder *builder = super.dataMessageBuilder; - if (builder == nil) { return nil; } - [builder setFlags:SSKProtoDataMessageFlagsUnlinkDevice]; - return builder; -} - -#pragma mark Settings -- (uint)ttl { return (uint)[LKTTLUtilities getTTLFor:LKMessageTypeUnlinkDevice]; } -- (BOOL)shouldSyncTranscript { return NO; } -- (BOOL)shouldBeSaved { return NO; } - -@end diff --git a/SignalUtilitiesKit/LKUserDefaults.swift b/SignalUtilitiesKit/LKUserDefaults.swift index 0ce6bea81..077c7c2a9 100644 --- a/SignalUtilitiesKit/LKUserDefaults.swift +++ b/SignalUtilitiesKit/LKUserDefaults.swift @@ -7,8 +7,6 @@ public enum LKUserDefaults { case hasSeenGIFMetadataWarning case hasViewedSeed case isUsingFullAPNs - /// Whether the device was unlinked as a slave device (used to notify the user on the landing screen). - case wasUnlinked } public enum Date : Swift.String { @@ -16,7 +14,6 @@ public enum LKUserDefaults { } public enum Double : Swift.String { - /// - Note: Deprecated case lastDeviceTokenUpload = "lastDeviceTokenUploadTime" } @@ -24,50 +21,35 @@ public enum LKUserDefaults { case appMode } - public enum String { - case slaveDeviceName(Swift.String) + public enum String : Swift.String { case deviceToken - /// `nil` if this is a master device or if the user hasn't linked a device. - case masterHexEncodedPublicKey - - public var key: Swift.String { - switch self { - case .slaveDeviceName(let hexEncodedPublicKey): return "\(hexEncodedPublicKey)_display_name" - case .deviceToken: return "deviceToken" - case .masterHexEncodedPublicKey: return "masterDeviceHexEncodedPublicKey" - } - } } } public extension UserDefaults { - public subscript(bool: LKUserDefaults.Bool) -> Bool { + subscript(bool: LKUserDefaults.Bool) -> Bool { get { return self.bool(forKey: bool.rawValue) } set { set(newValue, forKey: bool.rawValue) } } - public subscript(date: LKUserDefaults.Date) -> Date? { + subscript(date: LKUserDefaults.Date) -> Date? { get { return self.object(forKey: date.rawValue) as? Date } set { set(newValue, forKey: date.rawValue) } } - public subscript(double: LKUserDefaults.Double) -> Double { + subscript(double: LKUserDefaults.Double) -> Double { get { return self.double(forKey: double.rawValue) } set { set(newValue, forKey: double.rawValue) } } - public subscript(int: LKUserDefaults.Int) -> Int { + subscript(int: LKUserDefaults.Int) -> Int { get { return self.integer(forKey: int.rawValue) } set { set(newValue, forKey: int.rawValue) } } - public subscript(string: LKUserDefaults.String) -> String? { - get { return self.string(forKey: string.key) } - set { set(newValue, forKey: string.key) } - } - - public var isMasterDevice: Bool { - return (self[.masterHexEncodedPublicKey] == nil) + subscript(string: LKUserDefaults.String) -> String? { + get { return self.string(forKey: string.rawValue) } + set { set(newValue, forKey: string.rawValue) } } } diff --git a/SignalUtilitiesKit/LokiDatabaseUtilities.swift b/SignalUtilitiesKit/LokiDatabaseUtilities.swift index 8655eb10c..42d51545f 100644 --- a/SignalUtilitiesKit/LokiDatabaseUtilities.swift +++ b/SignalUtilitiesKit/LokiDatabaseUtilities.swift @@ -23,51 +23,6 @@ public final class LokiDatabaseUtilities : NSObject { - // MARK: - Device Links - @objc(getLinkedDeviceHexEncodedPublicKeysFor:in:) - public static func getLinkedDeviceHexEncodedPublicKeys(for hexEncodedPublicKey: String, in transaction: YapDatabaseReadTransaction) -> Set { - return [ hexEncodedPublicKey ] - /* - let storage = OWSPrimaryStorage.shared() - let masterHexEncodedPublicKey = storage.getMasterHexEncodedPublicKey(for: hexEncodedPublicKey, in: transaction) ?? hexEncodedPublicKey - var result = Set(storage.getDeviceLinks(for: masterHexEncodedPublicKey, in: transaction).flatMap { deviceLink in - return [ deviceLink.master.publicKey, deviceLink.slave.publicKey ] - }) - result.insert(hexEncodedPublicKey) - return result - */ - } - - @objc(getLinkedDeviceThreadsFor:in:) - public static func getLinkedDeviceThreads(for hexEncodedPublicKey: String, in transaction: YapDatabaseReadTransaction) -> Set { - return Set([ TSContactThread.getWithContactId(hexEncodedPublicKey, transaction: transaction) ].compactMap { $0 }) -// return Set(getLinkedDeviceHexEncodedPublicKeys(for: hexEncodedPublicKey, in: transaction).compactMap { TSContactThread.getWithContactId($0, transaction: transaction) }) - } - - @objc(isUserLinkedDevice:in:) - public static func isUserLinkedDevice(_ hexEncodedPublicKey: String, transaction: YapDatabaseReadTransaction) -> Bool { - return hexEncodedPublicKey == getUserHexEncodedPublicKey() - /* - let userHexEncodedPublicKey = getUserHexEncodedPublicKey() - let userLinkedDeviceHexEncodedPublicKeys = getLinkedDeviceHexEncodedPublicKeys(for: userHexEncodedPublicKey, in: transaction) - return userLinkedDeviceHexEncodedPublicKeys.contains(hexEncodedPublicKey) - */ - } - - @objc(getMasterHexEncodedPublicKeyFor:in:) - public static func objc_getMasterHexEncodedPublicKey(for slaveHexEncodedPublicKey: String, in transaction: YapDatabaseReadTransaction) -> String? { - return nil -// return OWSPrimaryStorage.shared().getMasterHexEncodedPublicKey(for: slaveHexEncodedPublicKey, in: transaction) - } - - @objc(getDeviceLinksFor:in:) - public static func objc_getDeviceLinks(for masterHexEncodedPublicKey: String, in transaction: YapDatabaseReadTransaction) -> Set { - return [] -// return OWSPrimaryStorage.shared().getDeviceLinks(for: masterHexEncodedPublicKey, in: transaction) - } - - - // MARK: - Open Groups private static let publicChatCollection = "LokiPublicChatCollection" diff --git a/SignalUtilitiesKit/LokiMessage.swift b/SignalUtilitiesKit/LokiMessage.swift deleted file mode 100644 index c2048e763..000000000 --- a/SignalUtilitiesKit/LokiMessage.swift +++ /dev/null @@ -1,75 +0,0 @@ -import PromiseKit - -public struct LokiMessage { - /// The hex encoded public key of the recipient. - let recipientPublicKey: String - /// The content of the message. - let data: LosslessStringConvertible - /// The time to live for the message in milliseconds. - let ttl: UInt64 - /// Whether this message is a ping. - /// - /// - Note: The concept of pinging only applies to P2P messaging. - let isPing: Bool - /// When the proof of work was calculated, if applicable (P2P messages don't require proof of work). - /// - /// - Note: Expressed as milliseconds since 00:00:00 UTC on 1 January 1970. - private(set) var timestamp: UInt64? = nil - /// The base 64 encoded proof of work, if applicable (P2P messages don't require proof of work). - private(set) var nonce: String? = nil - - private init(destination: String, data: LosslessStringConvertible, ttl: UInt64, isPing: Bool) { - self.recipientPublicKey = destination - self.data = data - self.ttl = ttl - self.isPing = isPing - } - - /// Construct a `LokiMessage` from a `SignalMessage`. - /// - /// - Note: `timestamp` is the original message timestamp (i.e. `TSOutgoingMessage.timestamp`). - public static func from(signalMessage: SignalMessage) -> LokiMessage? { - // To match the desktop application, we have to wrap the data in an envelope and then wrap that in a websocket object - do { - let wrappedMessage = try MessageWrapper.wrap(message: signalMessage) - let data = wrappedMessage.base64EncodedString() - let destination = signalMessage.recipientPublicKey - var ttl = TTLUtilities.fallbackMessageTTL - if let messageTTL = signalMessage.ttl, messageTTL > 0 { ttl = UInt64(messageTTL) } - let isPing = signalMessage.isPing - return LokiMessage(destination: destination, data: data, ttl: ttl, isPing: isPing) - } catch let error { - print("[Loki] Failed to convert Signal message to Loki message: \(signalMessage).") - return nil - } - } - - /// Calculate the proof of work for this message. - /// - /// - Returns: The promise of a new message with its `timestamp` and `nonce` set. - public func calculatePoW() -> Promise { - return Promise { seal in - DispatchQueue.global(qos: .userInitiated).async { - let now = NSDate.ows_millisecondTimeStamp() - let dataAsString = self.data as! String // Safe because of how from(signalMessage:with:) is implemented - if let nonce = ProofOfWork.calculate(data: dataAsString, pubKey: self.recipientPublicKey, timestamp: now, ttl: self.ttl) { - var result = self - result.timestamp = now - result.nonce = nonce - seal.fulfill(result) - } else { - seal.reject(SessionMessagingKit.MessageSender.Error.proofOfWorkCalculationFailed) - } - } - } - } - - public func toJSON() -> JSON { - var result = [ "pubKey" : recipientPublicKey, "data" : data.description, "ttl" : String(ttl) ] - if let timestamp = timestamp, let nonce = nonce { - result["timestamp"] = String(timestamp) - result["nonce"] = nonce - } - return result - } -} diff --git a/SignalUtilitiesKit/LokiPushNotificationManager.swift b/SignalUtilitiesKit/LokiPushNotificationManager.swift index 1d1c47095..50b172d6f 100644 --- a/SignalUtilitiesKit/LokiPushNotificationManager.swift +++ b/SignalUtilitiesKit/LokiPushNotificationManager.swift @@ -100,7 +100,8 @@ public final class LokiPushNotificationManager : NSObject { public static func objc_register(with token: Data, publicKey: String, isForcedUpdate: Bool) -> AnyPromise { return AnyPromise.from(register(with: token, publicKey: publicKey, isForcedUpdate: isForcedUpdate)) } - + + @discardableResult static func performOperation(_ operation: ClosedGroupOperation, for closedGroupPublicKey: String, publicKey: String) -> Promise { let isUsingFullAPNs = UserDefaults.standard[.isUsingFullAPNs] guard isUsingFullAPNs else { return Promise { $0.fulfill(()) } } @@ -123,31 +124,4 @@ public final class LokiPushNotificationManager : NSObject { } return promise } - - static func notify(for signalMessage: SignalMessage) -> Promise { - let message = LokiMessage.from(signalMessage: signalMessage)! - let parameters = [ "data" : message.data.description, "send_to" : message.recipientPublicKey] - let url = URL(string: "\(server)/notify")! - let request = TSRequest(url: url, method: "POST", parameters: parameters) - request.allHTTPHeaderFields = [ "Content-Type" : "application/json" ] - let promise: Promise = attempt(maxRetryCount: maxRetryCount, recoveringOn: DispatchQueue.global()) { - OnionRequestAPI.sendOnionRequest(request, to: server, target: "/loki/v2/lsrpc", using: pnServerPublicKey).map2 { response in - guard let json = response["body"] as? JSON else { - return print("[Loki] Couldn't notify PN server.") - } - guard json["code"] as? Int != 0 else { - return print("[Loki] Couldn't notify PN server due to error: \(json["message"] as? String ?? "nil").") - } - } - } - promise.catch2 { error in - print("[Loki] Couldn't notify PN server.") - } - return promise - } - - @objc(notifyForMessage:) - public static func objc_notify(for signalMessage: SignalMessage) -> AnyPromise { - return AnyPromise.from(notify(for: signalMessage)) - } } diff --git a/SignalUtilitiesKit/LokiSessionResetImplementation.swift b/SignalUtilitiesKit/LokiSessionRestorationImplementation.swift similarity index 95% rename from SignalUtilitiesKit/LokiSessionResetImplementation.swift rename to SignalUtilitiesKit/LokiSessionRestorationImplementation.swift index 64569eb62..50271146c 100644 --- a/SignalUtilitiesKit/LokiSessionResetImplementation.swift +++ b/SignalUtilitiesKit/LokiSessionRestorationImplementation.swift @@ -29,7 +29,7 @@ public final class SessionRestorationImplementation : NSObject, SessionRestorati Storage.read { transaction in thread = TSContactThread.getWithContactId(publicKey, transaction: transaction) } - return thread?.sessionResetStatus ?? .none + return thread?.sessionRestorationStatus ?? .none } public func handleNewSessionAdopted(for publicKey: String, using transaction: Any) { @@ -42,7 +42,7 @@ public final class SessionRestorationImplementation : NSObject, SessionRestorati let infoMessage = TSInfoMessage(timestamp: NSDate.ows_millisecondTimeStamp(), in: thread, messageType: .typeLokiSessionResetDone) infoMessage.save(with: transaction) // Update the session reset status - thread.sessionResetStatus = .none + thread.sessionRestorationStatus = .none thread.save(with: transaction) } } diff --git a/SignalUtilitiesKit/MentionsManager.swift b/SignalUtilitiesKit/MentionsManager.swift index bc1588e0c..969038463 100644 --- a/SignalUtilitiesKit/MentionsManager.swift +++ b/SignalUtilitiesKit/MentionsManager.swift @@ -35,7 +35,7 @@ public final class MentionsManager : NSObject { publicChat = LokiDatabaseUtilities.getPublicChat(for: threadID, in: transaction) } storage.dbReadConnection.read { transaction in - candidates = cache.flatMap { publicKey in + candidates = cache.compactMap { publicKey in let uncheckedDisplayName: String? if let publicChat = publicChat { uncheckedDisplayName = UserDisplayNameUtilities.getPublicChatDisplayName(for: publicKey, in: publicChat.channel, on: publicChat.server) diff --git a/SignalUtilitiesKit/MessageWrapper.swift b/SignalUtilitiesKit/MessageWrapper.swift deleted file mode 100644 index f19626906..000000000 --- a/SignalUtilitiesKit/MessageWrapper.swift +++ /dev/null @@ -1,72 +0,0 @@ - -public enum MessageWrapper { - - public enum Error : LocalizedError { - case failedToWrapData - case failedToWrapMessageInEnvelope - case failedToWrapEnvelopeInWebSocketMessage - case failedToUnwrapData - - public var errorDescription: String? { - switch self { - case .failedToWrapData: return "Failed to wrap data." - case .failedToWrapMessageInEnvelope: return "Failed to wrap message in envelope." - case .failedToWrapEnvelopeInWebSocketMessage: return "Failed to wrap envelope in web socket message." - case .failedToUnwrapData: return "Failed to unwrap data." - } - } - } - - /// Wraps `message` in an `SSKProtoEnvelope` and then a `WebSocketProtoWebSocketMessage` to match the desktop application. - public static func wrap(message: SignalMessage) throws -> Data { - do { - let envelope = try createEnvelope(around: message) - let webSocketMessage = try createWebSocketMessage(around: envelope) - return try webSocketMessage.serializedData() - } catch let error { - throw error as? Error ?? Error.failedToWrapData - } - } - - private static func createEnvelope(around message: SignalMessage) throws -> SSKProtoEnvelope { - do { - let builder = SSKProtoEnvelope.builder(type: message.type, timestamp: message.timestamp) - builder.setSource(message.senderPublicKey) - builder.setSourceDevice(message.senderDeviceID) - if let content = try Data(base64Encoded: message.content, options: .ignoreUnknownCharacters) { - builder.setContent(content) - } else { - throw Error.failedToWrapMessageInEnvelope - } - return try builder.build() - } catch let error { - print("[Loki] Failed to wrap message in envelope: \(error).") - throw Error.failedToWrapMessageInEnvelope - } - } - - private static func createWebSocketMessage(around envelope: SSKProtoEnvelope) throws -> WebSocketProtoWebSocketMessage { - do { - let requestBuilder = WebSocketProtoWebSocketRequestMessage.builder(verb: "PUT", path: "/api/v1/message", requestID: UInt64.random(in: 1.. SSKProtoEnvelope { - do { - let webSocketMessage = try WebSocketProtoWebSocketMessage.parseData(data) - let envelope = webSocketMessage.request!.body! - return try SSKProtoEnvelope.parseData(envelope) - } catch let error { - print("[Loki] Failed to unwrap data: \(error).") - throw Error.failedToUnwrapData - } - } -} diff --git a/SignalUtilitiesKit/TSErrorMessage.h b/SignalUtilitiesKit/Messages/TSErrorMessage.h similarity index 100% rename from SignalUtilitiesKit/TSErrorMessage.h rename to SignalUtilitiesKit/Messages/TSErrorMessage.h diff --git a/SignalUtilitiesKit/TSErrorMessage.m b/SignalUtilitiesKit/Messages/TSErrorMessage.m similarity index 94% rename from SignalUtilitiesKit/TSErrorMessage.m rename to SignalUtilitiesKit/Messages/TSErrorMessage.m index 12b29e7b7..90f30198e 100644 --- a/SignalUtilitiesKit/TSErrorMessage.m +++ b/SignalUtilitiesKit/Messages/TSErrorMessage.m @@ -4,7 +4,6 @@ #import "TSErrorMessage.h" #import "ContactsManagerProtocol.h" -#import "OWSMessageManager.h" #import "SSKEnvironment.h" #import "TSContactThread.h" #import "TSErrorMessage_privateConstructor.h" @@ -89,7 +88,7 @@ NSUInteger TSErrorMessageSchemaVersion = 1; withTransaction:(YapDatabaseReadWriteTransaction *)transaction failedMessageType:(TSErrorMessageType)errorMessageType { - NSString *source = [LKDatabaseUtilities getMasterHexEncodedPublicKeyFor:envelope.source in:transaction] ?: envelope.source; + NSString *source = envelope.source; TSContactThread *contactThread = [TSContactThread getOrCreateThreadWithContactId:source transaction:transaction]; @@ -123,9 +122,7 @@ NSUInteger TSErrorMessageSchemaVersion = 1; NSString *messageFormat = NSLocalizedString(@"ERROR_MESSAGE_NON_BLOCKING_IDENTITY_CHANGE_FORMAT", @"Shown when signal users safety numbers changed, embeds the user's {{name or phone number}}"); - NSString *recipientDisplayName = - [SSKEnvironment.shared.contactsManager displayNameForPhoneIdentifier:self.recipientId - transaction:transaction]; + NSString *recipientDisplayName = [SSKEnvironment.shared.profileManager profileNameForRecipientWithID:self.recipientId avoidingWriteTransaction:YES]; return [NSString stringWithFormat:messageFormat, recipientDisplayName]; } else { // recipientId will be nil for legacy errors diff --git a/SignalUtilitiesKit/TSErrorMessage_privateConstructor.h b/SignalUtilitiesKit/Messages/TSErrorMessage_privateConstructor.h similarity index 100% rename from SignalUtilitiesKit/TSErrorMessage_privateConstructor.h rename to SignalUtilitiesKit/Messages/TSErrorMessage_privateConstructor.h diff --git a/SignalUtilitiesKit/TSIncomingMessage.h b/SignalUtilitiesKit/Messages/TSIncomingMessage.h similarity index 100% rename from SignalUtilitiesKit/TSIncomingMessage.h rename to SignalUtilitiesKit/Messages/TSIncomingMessage.h diff --git a/SignalUtilitiesKit/TSIncomingMessage.m b/SignalUtilitiesKit/Messages/TSIncomingMessage.m similarity index 100% rename from SignalUtilitiesKit/TSIncomingMessage.m rename to SignalUtilitiesKit/Messages/TSIncomingMessage.m diff --git a/SignalUtilitiesKit/TSInfoMessage.h b/SignalUtilitiesKit/Messages/TSInfoMessage.h similarity index 98% rename from SignalUtilitiesKit/TSInfoMessage.h rename to SignalUtilitiesKit/Messages/TSInfoMessage.h index d01fa2c9f..53e462619 100644 --- a/SignalUtilitiesKit/TSInfoMessage.h +++ b/SignalUtilitiesKit/Messages/TSInfoMessage.h @@ -18,7 +18,6 @@ typedef NS_ENUM(NSInteger, TSInfoMessageType) { TSInfoMessageTypeGroupQuit, TSInfoMessageTypeDisappearingMessagesUpdate, TSInfoMessageAddToContactsOffer, - TSInfoMessageVerificationStateChange, TSInfoMessageAddUserToProfileWhitelistOffer, TSInfoMessageAddGroupToProfileWhitelistOffer, TSInfoMessageTypeLokiSessionResetInProgress, diff --git a/SignalUtilitiesKit/TSInfoMessage.m b/SignalUtilitiesKit/Messages/TSInfoMessage.m similarity index 91% rename from SignalUtilitiesKit/TSInfoMessage.m rename to SignalUtilitiesKit/Messages/TSInfoMessage.m index 3a7d5be6e..fb9cd0dc7 100644 --- a/SignalUtilitiesKit/TSInfoMessage.m +++ b/SignalUtilitiesKit/Messages/TSInfoMessage.m @@ -127,9 +127,7 @@ NSUInteger TSInfoMessageSchemaVersion = 1; return NSLocalizedString(@"UNSUPPORTED_ATTACHMENT", nil); case TSInfoMessageUserNotRegistered: if (self.unregisteredRecipientId.length > 0) { - id contactsManager = SSKEnvironment.shared.contactsManager; - NSString *recipientName = [contactsManager displayNameForPhoneIdentifier:self.unregisteredRecipientId - transaction:transaction]; + NSString *recipientName = @""; return [NSString stringWithFormat:NSLocalizedString(@"ERROR_UNREGISTERED_USER_FORMAT", @"Format string for 'unregistered user' error. Embeds {{the " @"unregistered user's name or signal id}}."), @@ -144,9 +142,6 @@ NSUInteger TSInfoMessageSchemaVersion = 1; case TSInfoMessageAddToContactsOffer: return NSLocalizedString(@"ADD_TO_CONTACTS_OFFER", @"Message shown in conversation view that offers to add an unknown user to your phone's contacts."); - case TSInfoMessageVerificationStateChange: - return NSLocalizedString(@"VERIFICATION_STATE_CHANGE_GENERIC", - @"Generic message indicating that verification state changed for a given user."); case TSInfoMessageAddUserToProfileWhitelistOffer: return NSLocalizedString(@"ADD_USER_TO_PROFILE_WHITELIST_OFFER", @"Message shown in conversation view that offers to share your profile with a user."); diff --git a/SignalUtilitiesKit/TSInteraction.h b/SignalUtilitiesKit/Messages/TSInteraction.h similarity index 100% rename from SignalUtilitiesKit/TSInteraction.h rename to SignalUtilitiesKit/Messages/TSInteraction.h diff --git a/SignalUtilitiesKit/TSInteraction.m b/SignalUtilitiesKit/Messages/TSInteraction.m similarity index 99% rename from SignalUtilitiesKit/TSInteraction.m rename to SignalUtilitiesKit/Messages/TSInteraction.m index 1a94cdba6..6bc729a9e 100644 --- a/SignalUtilitiesKit/TSInteraction.m +++ b/SignalUtilitiesKit/Messages/TSInteraction.m @@ -214,7 +214,7 @@ NSString *NSStringFromOWSInteractionType(OWSInteractionType value) // effectively the same as sorting by server timestamp. if (self.thread.isGroupThread) { TSGroupThread *thread = (TSGroupThread *)self.thread; - if (thread.isPublicChat) { + if (thread.isOpenGroup) { sortId1 = self.sortId; sortId2 = other.sortId; } diff --git a/SignalUtilitiesKit/TSInvalidIdentityKeyErrorMessage.h b/SignalUtilitiesKit/Messages/TSInvalidIdentityKeyErrorMessage.h similarity index 100% rename from SignalUtilitiesKit/TSInvalidIdentityKeyErrorMessage.h rename to SignalUtilitiesKit/Messages/TSInvalidIdentityKeyErrorMessage.h diff --git a/SignalUtilitiesKit/TSInvalidIdentityKeyErrorMessage.m b/SignalUtilitiesKit/Messages/TSInvalidIdentityKeyErrorMessage.m similarity index 100% rename from SignalUtilitiesKit/TSInvalidIdentityKeyErrorMessage.m rename to SignalUtilitiesKit/Messages/TSInvalidIdentityKeyErrorMessage.m diff --git a/SignalUtilitiesKit/TSInvalidIdentityKeyReceivingErrorMessage.h b/SignalUtilitiesKit/Messages/TSInvalidIdentityKeyReceivingErrorMessage.h similarity index 100% rename from SignalUtilitiesKit/TSInvalidIdentityKeyReceivingErrorMessage.h rename to SignalUtilitiesKit/Messages/TSInvalidIdentityKeyReceivingErrorMessage.h diff --git a/SignalUtilitiesKit/TSInvalidIdentityKeyReceivingErrorMessage.m b/SignalUtilitiesKit/Messages/TSInvalidIdentityKeyReceivingErrorMessage.m similarity index 99% rename from SignalUtilitiesKit/TSInvalidIdentityKeyReceivingErrorMessage.m rename to SignalUtilitiesKit/Messages/TSInvalidIdentityKeyReceivingErrorMessage.m index 034853307..8b745f029 100644 --- a/SignalUtilitiesKit/TSInvalidIdentityKeyReceivingErrorMessage.m +++ b/SignalUtilitiesKit/Messages/TSInvalidIdentityKeyReceivingErrorMessage.m @@ -3,7 +3,6 @@ // #import "TSInvalidIdentityKeyReceivingErrorMessage.h" -#import "OWSFingerprint.h" #import "OWSIdentityManager.h" #import "OWSPrimaryStorage+SessionStore.h" #import "OWSPrimaryStorage.h" diff --git a/SignalUtilitiesKit/TSInvalidIdentityKeySendingErrorMessage.h b/SignalUtilitiesKit/Messages/TSInvalidIdentityKeySendingErrorMessage.h similarity index 100% rename from SignalUtilitiesKit/TSInvalidIdentityKeySendingErrorMessage.h rename to SignalUtilitiesKit/Messages/TSInvalidIdentityKeySendingErrorMessage.h diff --git a/SignalUtilitiesKit/TSInvalidIdentityKeySendingErrorMessage.m b/SignalUtilitiesKit/Messages/TSInvalidIdentityKeySendingErrorMessage.m similarity index 98% rename from SignalUtilitiesKit/TSInvalidIdentityKeySendingErrorMessage.m rename to SignalUtilitiesKit/Messages/TSInvalidIdentityKeySendingErrorMessage.m index 78379f6d2..b3649a09d 100644 --- a/SignalUtilitiesKit/TSInvalidIdentityKeySendingErrorMessage.m +++ b/SignalUtilitiesKit/Messages/TSInvalidIdentityKeySendingErrorMessage.m @@ -3,7 +3,6 @@ // #import "TSInvalidIdentityKeySendingErrorMessage.h" -#import "OWSFingerprint.h" #import "OWSIdentityManager.h" #import "OWSPrimaryStorage+SessionStore.h" #import "OWSPrimaryStorage.h" diff --git a/SignalUtilitiesKit/TSMessage.h b/SignalUtilitiesKit/Messages/TSMessage.h similarity index 93% rename from SignalUtilitiesKit/TSMessage.h rename to SignalUtilitiesKit/Messages/TSMessage.h index bef830cad..7702f8655 100644 --- a/SignalUtilitiesKit/TSMessage.h +++ b/SignalUtilitiesKit/Messages/TSMessage.h @@ -29,13 +29,11 @@ NS_ASSUME_NONNULL_BEGIN @property (nonatomic, readonly, nullable) OWSContact *contactShare; @property (nonatomic, nullable) OWSLinkPreview *linkPreview; @property BOOL skipSave; -// P2P -@property (nonatomic) BOOL isP2P; // Open groups @property (nonatomic) uint64_t openGroupServerMessageID; @property (nonatomic, readonly) BOOL isOpenGroupMessage; // Push notifications -@property (nonatomic) BOOL hasAttachmentsInNSE; +@property (nonatomic) BOOL hasUnfetchedAttachmentsFromPN; - (instancetype)initInteractionWithTimestamp:(uint64_t)timestamp inThread:(TSThread *)thread NS_UNAVAILABLE; @@ -77,10 +75,6 @@ NS_ASSUME_NONNULL_BEGIN - (void)updateWithLinkPreview:(OWSLinkPreview *)linkPreview transaction:(YapDatabaseReadWriteTransaction *)transaction; -#pragma mark - Open Groups - -- (void)saveOpenGroupServerMessageID:(uint64_t)serverMessageID in:(YapDatabaseReadWriteTransaction *_Nullable)transaction; - @end NS_ASSUME_NONNULL_END diff --git a/SignalUtilitiesKit/TSMessage.m b/SignalUtilitiesKit/Messages/TSMessage.m similarity index 94% rename from SignalUtilitiesKit/TSMessage.m rename to SignalUtilitiesKit/Messages/TSMessage.m index e582b2080..0003614aa 100644 --- a/SignalUtilitiesKit/TSMessage.m +++ b/SignalUtilitiesKit/Messages/TSMessage.m @@ -6,7 +6,7 @@ #import "AppContext.h" #import "MIMETypeUtil.h" #import "NSString+SSK.h" -#import "OWSContact.h" + #import "OWSDisappearingMessagesConfiguration.h" #import "TSAttachment.h" #import "TSAttachmentStream.h" @@ -196,10 +196,6 @@ static const NSUInteger OWSMessageSchemaVersion = 4; [result addObjectsFromArray:self.quotedMessage.thumbnailAttachmentStreamIds]; } - if (self.contactShare.avatarAttachmentId) { - [result addObject:self.contactShare.avatarAttachmentId]; - } - if (self.linkPreview.imageAttachmentId) { [result addObject:self.linkPreview.imageAttachmentId]; } @@ -359,12 +355,6 @@ static const NSUInteger OWSMessageSchemaVersion = 4; return bodyDescription; } else if (attachmentDescription.length > 0) { return attachmentDescription; - } else if (self.contactShare) { - if (CurrentAppContext().isRTL) { - return [self.contactShare.name.displayName stringByAppendingString:@" 👤"]; - } else { - return [@"👤 " stringByAppendingString:self.contactShare.name.displayName]; - } } else { // TODO: We should do better here. return @""; @@ -450,17 +440,6 @@ static const NSUInteger OWSMessageSchemaVersion = 4; return self.openGroupServerMessageID > 0; } -- (void)saveOpenGroupServerMessageID:(uint64_t)serverMessageID in:(YapDatabaseReadWriteTransaction *_Nullable)transaction { - self.openGroupServerMessageID = serverMessageID; - if (transaction == nil) { - [self save]; - [self.dbReadWriteConnection flushTransactionsWithCompletionQueue:dispatch_get_main_queue() completionBlock:^{}]; - } else { - [self saveWithTransaction:transaction]; - [transaction.connection flushTransactionsWithCompletionQueue:dispatch_get_main_queue() completionBlock:^{}]; - } -} - @end NS_ASSUME_NONNULL_END diff --git a/SignalUtilitiesKit/TSOutgoingMessage.h b/SignalUtilitiesKit/Messages/TSOutgoingMessage.h similarity index 97% rename from SignalUtilitiesKit/TSOutgoingMessage.h rename to SignalUtilitiesKit/Messages/TSOutgoingMessage.h index 7a734f521..0d33b756a 100644 --- a/SignalUtilitiesKit/TSOutgoingMessage.h +++ b/SignalUtilitiesKit/Messages/TSOutgoingMessage.h @@ -142,9 +142,6 @@ typedef NS_ENUM(NSInteger, TSGroupMetaMessage) { @property (nonatomic, readonly) BOOL isOnline; -/// Loki: Whether proof of work is being calculated for this message. -@property (atomic, readonly) BOOL isCalculatingPoW; - /// Loki: Time to live for the message in milliseconds. @property (nonatomic, readonly) uint ttl; @@ -197,9 +194,6 @@ typedef NS_ENUM(NSInteger, TSGroupMetaMessage) { #pragma mark - Update With... Methods -// When sending a message, when proof of work calculation is started, we should mark it as such -- (void)saveIsCalculatingProofOfWork:(BOOL)isCalculatingPoW withTransaction:(YapDatabaseReadWriteTransaction *)transaction; - // This method is used to record a successful send to one recipient. - (void)updateWithSentRecipient:(NSString *)recipientId wasSentByUD:(BOOL)wasSentByUD diff --git a/SignalUtilitiesKit/TSOutgoingMessage.m b/SignalUtilitiesKit/Messages/TSOutgoingMessage.m similarity index 96% rename from SignalUtilitiesKit/TSOutgoingMessage.m rename to SignalUtilitiesKit/Messages/TSOutgoingMessage.m index f681ccb6b..cff531a5b 100644 --- a/SignalUtilitiesKit/TSOutgoingMessage.m +++ b/SignalUtilitiesKit/Messages/TSOutgoingMessage.m @@ -6,8 +6,7 @@ #import "TSOutgoingMessage.h" #import "NSString+SSK.h" -#import "OWSContact.h" -#import "OWSOutgoingSyncMessage.h" + #import "OWSPrimaryStorage.h" #import "ProfileManagerProtocol.h" #import "ProtoUtils.h" @@ -93,8 +92,6 @@ NSString *NSStringForOutgoingMessageRecipientState(OWSOutgoingMessageRecipientSt @property (atomic, nullable) NSDictionary *recipientStateMap; -@property (atomic) BOOL isCalculatingPoW; - @end #pragma mark - @@ -327,7 +324,6 @@ NSString *NSStringForOutgoingMessageRecipientState(OWSOutgoingMessageRecipientSt } _hasSyncedTranscript = NO; - _isCalculatingPoW = NO; if ([thread isKindOfClass:TSGroupThread.class]) { // Unless specified, we assume group messages are "Delivery" i.e. normal messages. @@ -350,15 +346,15 @@ NSString *NSStringForOutgoingMessageRecipientState(OWSOutgoingMessageRecipientSt // recipient list from current thread state. NSMutableDictionary *recipientStateMap = [NSMutableDictionary new]; NSArray *recipientIds; - if ([self isKindOfClass:[OWSOutgoingSyncMessage class]]) { - NSString *_Nullable localNumber = [TSAccountManager localNumber]; - OWSAssertDebug(localNumber); - recipientIds = @[ - localNumber, - ]; - } else { - recipientIds = [thread recipientIdentifiers]; - } +// if ([self isKindOfClass:[OWSOutgoingSyncMessage class]]) { +// NSString *_Nullable localNumber = [TSAccountManager localNumber]; +// OWSAssertDebug(localNumber); +// recipientIds = @[ +// localNumber, +// ]; +// } else { +// recipientIds = [thread recipientIdentifiers]; +// } for (NSString *recipientId in recipientIds) { TSOutgoingMessageRecipientState *recipientState = [TSOutgoingMessageRecipientState new]; recipientState.state = OWSOutgoingMessageRecipientStateSending; @@ -612,7 +608,6 @@ NSString *NSStringForOutgoingMessageRecipientState(OWSOutgoingMessageRecipientSt } } [message setMostRecentFailureText:error.localizedDescription]; - [message setIsCalculatingPoW:NO]; }]; } @@ -629,7 +624,6 @@ NSString *NSStringForOutgoingMessageRecipientState(OWSOutgoingMessageRecipientSt recipientState.state = OWSOutgoingMessageRecipientStateFailed; } } - [message setIsCalculatingPoW:NO]; }]; } @@ -676,14 +670,6 @@ NSString *NSStringForOutgoingMessageRecipientState(OWSOutgoingMessageRecipientSt }]; } -- (void)saveIsCalculatingProofOfWork:(BOOL)isCalculatingPoW withTransaction:(YapDatabaseReadWriteTransaction *)transaction -{ - OWSAssertDebug(transaction); - [self applyChangeToSelfAndLatestCopy:transaction changeBlock:^(TSOutgoingMessage *message) { - [message setIsCalculatingPoW:isCalculatingPoW]; - }]; -} - - (void)updateWithSentRecipient:(NSString *)recipientId wasSentByUD:(BOOL)wasSentByUD transaction:(YapDatabaseReadWriteTransaction *)transaction { @@ -697,7 +683,6 @@ NSString *NSStringForOutgoingMessageRecipientState(OWSOutgoingMessageRecipientSt if (!recipientState) { return; } recipientState.state = OWSOutgoingMessageRecipientStateSent; recipientState.wasSentByUD = wasSentByUD; - [message setIsCalculatingPoW:NO]; }]; } @@ -712,7 +697,6 @@ NSString *NSStringForOutgoingMessageRecipientState(OWSOutgoingMessageRecipientSt = message.recipientStateMap[recipientId]; if (!recipientState) { return; } recipientState.state = OWSOutgoingMessageRecipientStateSkipped; - [message setIsCalculatingPoW:NO]; }]; } @@ -741,7 +725,6 @@ NSString *NSStringForOutgoingMessageRecipientState(OWSOutgoingMessageRecipientSt } recipientState.state = OWSOutgoingMessageRecipientStateSent; recipientState.deliveryTimestamp = deliveryTimestamp; - [message setIsCalculatingPoW:NO]; }]; } @@ -761,7 +744,6 @@ NSString *NSStringForOutgoingMessageRecipientState(OWSOutgoingMessageRecipientSt } recipientState.state = OWSOutgoingMessageRecipientStateSent; recipientState.readTimestamp = @(readTimestamp); - [message setIsCalculatingPoW:NO]; }]; } @@ -831,8 +813,6 @@ NSString *NSStringForOutgoingMessageRecipientState(OWSOutgoingMessageRecipientSt } } - [message setIsCalculatingPoW:NO]; - if (!isSentUpdate) { [message setIsFromLinkedDevice:YES]; } @@ -992,17 +972,6 @@ NSString *NSStringForOutgoingMessageRecipientState(OWSOutgoingMessageRecipientSt [builder setQuote:quoteProto]; } - // Contact Share - if (self.contactShare) { - SSKProtoDataMessageContact *_Nullable contactProto = - [OWSContacts protoForContact:self.contactShare]; - if (contactProto) { - [builder addContact:contactProto]; - } else { - OWSFailDebug(@"contactProto was unexpectedly nil"); - } - } - // Link Preview if (self.linkPreview) { SSKProtoDataMessagePreviewBuilder *previewBuilder = @@ -1164,7 +1133,7 @@ NSString *NSStringForOutgoingMessageRecipientState(OWSOutgoingMessageRecipientSt return [result copy]; } -- (uint)ttl { return (uint)[LKTTLUtilities getTTLFor:LKMessageTypeRegular]; } +- (uint)ttl { return 2 * 24 * 60 * 60 * 1000; } @end diff --git a/SignalUtilitiesKit/TSQuotedMessage.h b/SignalUtilitiesKit/Messages/TSQuotedMessage.h similarity index 100% rename from SignalUtilitiesKit/TSQuotedMessage.h rename to SignalUtilitiesKit/Messages/TSQuotedMessage.h diff --git a/SignalUtilitiesKit/TSQuotedMessage.m b/SignalUtilitiesKit/Messages/TSQuotedMessage.m similarity index 100% rename from SignalUtilitiesKit/TSQuotedMessage.m rename to SignalUtilitiesKit/Messages/TSQuotedMessage.m diff --git a/SignalUtilitiesKit/Meta/SignalUtilitiesKit.h b/SignalUtilitiesKit/Meta/SignalUtilitiesKit.h index 18c71fc06..085cc69ad 100644 --- a/SignalUtilitiesKit/Meta/SignalUtilitiesKit.h +++ b/SignalUtilitiesKit/Meta/SignalUtilitiesKit.h @@ -13,16 +13,10 @@ FOUNDATION_EXPORT const unsigned char SignalUtilitiesKitVersionString[]; #import #import #import -#import -#import -#import #import #import #import -#import #import -#import -#import #import #import #import @@ -31,12 +25,8 @@ FOUNDATION_EXPORT const unsigned char SignalUtilitiesKitVersionString[]; #import #import #import -#import #import #import -#import -#import -#import #import #import #import @@ -44,31 +34,18 @@ FOUNDATION_EXPORT const unsigned char SignalUtilitiesKitVersionString[]; #import #import #import -#import -#import -#import #import -#import -#import -#import #import -#import -#import #import #import -#import #import #import #import -#import #import #import -#import #import #import #import -#import -#import #import #import #import @@ -77,20 +54,15 @@ FOUNDATION_EXPORT const unsigned char SignalUtilitiesKitVersionString[]; #import #import #import -#import #import #import #import #import -#import -#import #import #import #import -#import #import #import -#import #import #import #import @@ -102,6 +74,7 @@ FOUNDATION_EXPORT const unsigned char SignalUtilitiesKitVersionString[]; #import #import #import +#import #import #import #import @@ -112,13 +85,9 @@ FOUNDATION_EXPORT const unsigned char SignalUtilitiesKitVersionString[]; #import #import #import -#import -#import -#import #import #import #import -#import #import #import #import @@ -126,6 +95,5 @@ FOUNDATION_EXPORT const unsigned char SignalUtilitiesKitVersionString[]; #import #import #import -#import #import #import diff --git a/SignalUtilitiesKit/Mnemonic.swift b/SignalUtilitiesKit/Mnemonic.swift deleted file mode 100644 index 9cbaa1588..000000000 --- a/SignalUtilitiesKit/Mnemonic.swift +++ /dev/null @@ -1,161 +0,0 @@ -import CryptoSwift - -/// Based on [mnemonic.js](https://github.com/loki-project/loki-messenger/blob/development/libloki/modules/mnemonic.js) . -public enum Mnemonic { - - public struct Language : Hashable { - fileprivate let filename: String - fileprivate let prefixLength: UInt - - public static let english = Language(filename: "english", prefixLength: 3) - public static let japanese = Language(filename: "japanese", prefixLength: 3) - public static let portuguese = Language(filename: "portuguese", prefixLength: 4) - public static let spanish = Language(filename: "spanish", prefixLength: 4) - - private static var wordSetCache: [Language:[String]] = [:] - private static var truncatedWordSetCache: [Language:[String]] = [:] - - private init(filename: String, prefixLength: UInt) { - self.filename = filename - self.prefixLength = prefixLength - } - - fileprivate func loadWordSet() -> [String] { - if let cachedResult = Language.wordSetCache[self] { - return cachedResult - } else { - let url = Bundle.main.url(forResource: filename, withExtension: "txt")! - let contents = try! String(contentsOf: url) - let result = contents.split(separator: ",").map { String($0) } - Language.wordSetCache[self] = result - return result - } - } - - fileprivate func loadTruncatedWordSet() -> [String] { - if let cachedResult = Language.truncatedWordSetCache[self] { - return cachedResult - } else { - let result = loadWordSet().map { $0.prefix(length: prefixLength) } - Language.truncatedWordSetCache[self] = result - return result - } - } - } - - public enum DecodingError : LocalizedError { - case generic, inputTooShort, missingLastWord, invalidWord, verificationFailed - - public var errorDescription: String? { - switch self { - case .generic: return NSLocalizedString("Something went wrong. Please check your recovery phrase and try again.", comment: "") - case .inputTooShort: return NSLocalizedString("Looks like you didn't enter enough words. Please check your recovery phrase and try again.", comment: "") - case .missingLastWord: return NSLocalizedString("You seem to be missing the last word of your recovery phrase. Please check what you entered and try again.", comment: "") - case .invalidWord: return NSLocalizedString("There appears to be an invalid word in your recovery phrase. Please check what you entered and try again.", comment: "") - case .verificationFailed: return NSLocalizedString("Your recovery phrase couldn't be verified. Please check what you entered and try again.", comment: "") - } - } - } - - public static func hash(hexEncodedString string: String, language: Language = .english) -> String { - return encode(hexEncodedString: string).split(separator: " ")[0..<3].joined(separator: " ") - } - - public static func encode(hexEncodedString string: String, language: Language = .english) -> String { - var string = string - let wordSet = language.loadWordSet() - let prefixLength = language.prefixLength - var result: [String] = [] - let n = wordSet.count - let characterCount = string.indices.count // Safe for this particular case - for chunkStartIndexAsInt in stride(from: 0, to: characterCount, by: 8) { - let chunkStartIndex = string.index(string.startIndex, offsetBy: chunkStartIndexAsInt) - let chunkEndIndex = string.index(chunkStartIndex, offsetBy: 8) - let p1 = string[string.startIndex.. String { - var words = mnemonic.split(separator: " ").map { String($0) } - let truncatedWordSet = language.loadTruncatedWordSet() - let prefixLength = language.prefixLength - var result = "" - let n = truncatedWordSet.count - // Check preconditions - guard words.count >= 12 else { throw DecodingError.inputTooShort } - guard !words.count.isMultiple(of: 3) else { throw DecodingError.missingLastWord } - // Get checksum word - let checksumWord = words.popLast()! - // Decode - for chunkStartIndex in stride(from: 0, to: words.count, by: 3) { - guard let w1 = truncatedWordSet.firstIndex(of: words[chunkStartIndex].prefix(length: prefixLength)), - let w2 = truncatedWordSet.firstIndex(of: words[chunkStartIndex + 1].prefix(length: prefixLength)), - let w3 = truncatedWordSet.firstIndex(of: words[chunkStartIndex + 2].prefix(length: prefixLength)) else { throw DecodingError.invalidWord } - let x = w1 + n * ((n - w1 + w2) % n) + n * n * ((n - w2 + w3) % n) - guard x % n == w1 else { throw DecodingError.generic } - let string = "0000000" + String(x, radix: 16) - result += swap(String(string[string.index(string.endIndex, offsetBy: -8).. String { - func toStringIndex(_ indexAsInt: Int) -> String.Index { - return x.index(x.startIndex, offsetBy: indexAsInt) - } - let p1 = x[toStringIndex(6).. Int { - let checksum = Array(x.map { $0.prefix(length: prefixLength) }.joined().utf8).crc32() - return Int(checksum) % x.count - } -} - -private extension String { - - func prefix(length: UInt) -> String { - return String(self[startIndex.. String { - return Mnemonic.hash(hexEncodedString: string) - } - - @objc(encodeHexEncodedString:) - public static func encode(hexEncodedString string: String) -> String { - return Mnemonic.encode(hexEncodedString: string) - } -} diff --git a/SignalUtilitiesKit/OWSPrimaryStorage+Loki.h b/SignalUtilitiesKit/Move to main app/OWSPrimaryStorage+Loki.h similarity index 100% rename from SignalUtilitiesKit/OWSPrimaryStorage+Loki.h rename to SignalUtilitiesKit/Move to main app/OWSPrimaryStorage+Loki.h diff --git a/SignalUtilitiesKit/OWSPrimaryStorage+Loki.m b/SignalUtilitiesKit/Move to main app/OWSPrimaryStorage+Loki.m similarity index 99% rename from SignalUtilitiesKit/OWSPrimaryStorage+Loki.m rename to SignalUtilitiesKit/Move to main app/OWSPrimaryStorage+Loki.m index 68f65931f..ab8d0bc1a 100644 --- a/SignalUtilitiesKit/OWSPrimaryStorage+Loki.m +++ b/SignalUtilitiesKit/Move to main app/OWSPrimaryStorage+Loki.m @@ -2,7 +2,7 @@ #import "OWSPrimaryStorage+PreKeyStore.h" #import "OWSPrimaryStorage+SignedPreKeyStore.h" #import "OWSPrimaryStorage+keyFromIntLong.h" -#import "OWSDevice.h" + #import "OWSIdentityManager.h" #import "NSDate+OWS.h" #import "TSAccountManager.h" @@ -106,7 +106,7 @@ uint32_t registrationID = [self.accountManager getOrGenerateRegistrationId]; PreKeyBundle *bundle = [[PreKeyBundle alloc] initWithRegistrationId:registrationID - deviceId:OWSDevicePrimaryDeviceId + deviceId:(uint32_t)1 preKeyId:preKey.Id preKeyPublic:preKey.keyPair.publicKey.prependKeyType signedPreKeyPublic:signedPreKey.keyPair.publicKey.prependKeyType diff --git a/SignalUtilitiesKit/OWSPrimaryStorage+Loki.swift b/SignalUtilitiesKit/Move to main app/OWSPrimaryStorage+Loki.swift similarity index 85% rename from SignalUtilitiesKit/OWSPrimaryStorage+Loki.swift rename to SignalUtilitiesKit/Move to main app/OWSPrimaryStorage+Loki.swift index d9fb7896f..c46360579 100644 --- a/SignalUtilitiesKit/OWSPrimaryStorage+Loki.swift +++ b/SignalUtilitiesKit/Move to main app/OWSPrimaryStorage+Loki.swift @@ -62,14 +62,6 @@ public extension OWSPrimaryStorage { transaction.date(forKey: publicKey, inCollection: Storage.sessionRequestTimestampCollection) } - // MARK: Multi Device - public func setDeviceLinks(_ deviceLinks: Set) { } - public func addDeviceLink(_ deviceLink: DeviceLink) { } - public func removeDeviceLink(_ deviceLink: DeviceLink) { } - public func getDeviceLinks(for masterHexEncodedPublicKey: String, in transaction: YapDatabaseReadTransaction) -> Set { return [] } - public func getDeviceLink(for slaveHexEncodedPublicKey: String, in transaction: YapDatabaseReadTransaction) -> DeviceLink? { return nil } - public func getMasterHexEncodedPublicKey(for slaveHexEncodedPublicKey: String, in transaction: YapDatabaseReadTransaction) -> String? { return nil } - // MARK: Open Groups public func getUserCount(for publicChat: OpenGroup, in transaction: YapDatabaseReadTransaction) -> Int? { return transaction.object(forKey: publicChat.id, inCollection: Storage.openGroupUserCountCollection) as? Int diff --git a/SignalUtilitiesKit/Storage+ClosedGroups.swift b/SignalUtilitiesKit/Move to main app/Storage+ClosedGroups.swift similarity index 100% rename from SignalUtilitiesKit/Storage+ClosedGroups.swift rename to SignalUtilitiesKit/Move to main app/Storage+ClosedGroups.swift diff --git a/SignalUtilitiesKit/Storage+Collections.swift b/SignalUtilitiesKit/Move to main app/Storage+Collections.swift similarity index 100% rename from SignalUtilitiesKit/Storage+Collections.swift rename to SignalUtilitiesKit/Move to main app/Storage+Collections.swift diff --git a/SignalUtilitiesKit/Storage+OnionRequests.swift b/SignalUtilitiesKit/Move to main app/Storage+OnionRequests.swift similarity index 100% rename from SignalUtilitiesKit/Storage+OnionRequests.swift rename to SignalUtilitiesKit/Move to main app/Storage+OnionRequests.swift diff --git a/SignalUtilitiesKit/Storage+PublicChats.swift b/SignalUtilitiesKit/Move to main app/Storage+PublicChats.swift similarity index 100% rename from SignalUtilitiesKit/Storage+PublicChats.swift rename to SignalUtilitiesKit/Move to main app/Storage+PublicChats.swift diff --git a/SignalUtilitiesKit/Storage+SessionManagement.swift b/SignalUtilitiesKit/Move to main app/Storage+SessionManagement.swift similarity index 100% rename from SignalUtilitiesKit/Storage+SessionManagement.swift rename to SignalUtilitiesKit/Move to main app/Storage+SessionManagement.swift diff --git a/SignalUtilitiesKit/Storage+SnodeAPI.swift b/SignalUtilitiesKit/Move to main app/Storage+SnodeAPI.swift similarity index 100% rename from SignalUtilitiesKit/Storage+SnodeAPI.swift rename to SignalUtilitiesKit/Move to main app/Storage+SnodeAPI.swift diff --git a/SignalUtilitiesKit/Storage.swift b/SignalUtilitiesKit/Move to main app/Storage.swift similarity index 100% rename from SignalUtilitiesKit/Storage.swift rename to SignalUtilitiesKit/Move to main app/Storage.swift diff --git a/SignalUtilitiesKit/NSTimer+OWS.h b/SignalUtilitiesKit/NSTimer+OWS.h deleted file mode 100644 index b77332dc1..000000000 --- a/SignalUtilitiesKit/NSTimer+OWS.h +++ /dev/null @@ -1,21 +0,0 @@ -// -// Copyright (c) 2019 Open Whisper Systems. All rights reserved. -// - -#import - -NS_ASSUME_NONNULL_BEGIN - -@interface NSTimer (OWS) - -// This method avoids the classic NSTimer retain cycle bug -// by using a weak reference to the target. -+ (NSTimer *)weakScheduledTimerWithTimeInterval:(NSTimeInterval)timeInterval - target:(id)target - selector:(SEL)selector - userInfo:(nullable id)userInfo - repeats:(BOOL)repeats; - -@end - -NS_ASSUME_NONNULL_END diff --git a/SignalUtilitiesKit/NSTimer+OWS.m b/SignalUtilitiesKit/NSTimer+OWS.m deleted file mode 100644 index 5af85fcb7..000000000 --- a/SignalUtilitiesKit/NSTimer+OWS.m +++ /dev/null @@ -1,70 +0,0 @@ -// -// Copyright (c) 2019 Open Whisper Systems. All rights reserved. -// - -#import "NSTimer+OWS.h" -#import -#import - -NS_ASSUME_NONNULL_BEGIN - -@interface NSTimerProxy : NSObject - -@property (nonatomic, weak) id target; -@property (nonatomic) SEL selector; - -@end - -#pragma mark - - -@implementation NSTimerProxy - -- (void)timerFired:(NSDictionary *)userInfo -{ -#pragma clang diagnostic push -#pragma clang diagnostic ignored "-Warc-performSelector-leaks" - [self.target performSelector:self.selector withObject:userInfo]; -#pragma clang diagnostic pop -} - -@end - -#pragma mark - - -static void *kNSTimer_OWS_Proxy = &kNSTimer_OWS_Proxy; - -@implementation NSTimer (OWS) - -- (NSTimerProxy *)ows_proxy -{ - return objc_getAssociatedObject(self, kNSTimer_OWS_Proxy); -} - -- (void)ows_setProxy:(NSTimerProxy *)proxy -{ - OWSAssertDebug(proxy); - - objc_setAssociatedObject(self, kNSTimer_OWS_Proxy, proxy, OBJC_ASSOCIATION_RETAIN); -} - -+ (NSTimer *)weakScheduledTimerWithTimeInterval:(NSTimeInterval)timeInterval - target:(id)target - selector:(SEL)selector - userInfo:(nullable id)userInfo - repeats:(BOOL)repeats -{ - NSTimerProxy *proxy = [NSTimerProxy new]; - proxy.target = target; - proxy.selector = selector; - NSTimer *timer = [NSTimer scheduledTimerWithTimeInterval:timeInterval - target:proxy - selector:@selector(timerFired:) - userInfo:userInfo - repeats:repeats]; - [timer ows_setProxy:proxy]; - return timer; -} - -@end - -NS_ASSUME_NONNULL_END diff --git a/SignalUtilitiesKit/NetworkManager.swift b/SignalUtilitiesKit/NetworkManager.swift deleted file mode 100644 index c1892b669..000000000 --- a/SignalUtilitiesKit/NetworkManager.swift +++ /dev/null @@ -1,53 +0,0 @@ -// -// Copyright (c) 2018 Open Whisper Systems. All rights reserved. -// - -import Foundation -import PromiseKit - -enum NetworkManagerError: Error { - /// Wraps TSNetworkManager failure callback params in a single throwable error - case taskError(task: URLSessionDataTask, underlyingError: Error) -} - -extension NetworkManagerError { - var isNetworkError: Bool { - switch self { - case .taskError(_, let underlyingError): - return IsNSErrorNetworkFailure(underlyingError) - } - } - - var statusCode: Int { - switch self { - case .taskError(let task, _): - return task.statusCode() - } - } -} - -extension TSNetworkManager { - public typealias NetworkManagerResult = (task: URLSessionDataTask, responseObject: Any?) - - public func perform(_ request: TSRequest, withCompletionQueue queue: DispatchQueue = DispatchQueue.main) -> Promise { - return makePromise(request: request, queue: queue) - } - - public func makePromise(request: TSRequest, queue: DispatchQueue = DispatchQueue.main) -> Promise { - let (promise, resolver) = Promise.pending() - - self.makeRequest(request, - completionQueue: queue, - success: { task, responseObject in - resolver.fulfill((task: task, responseObject: responseObject)) - }, - failure: { task, error in - let nmError = NetworkManagerError.taskError(task: task, underlyingError: error) - let nsError: NSError = nmError as NSError - nsError.isRetryable = (error as NSError).isRetryable - resolver.reject(nsError) - }) - - return promise - } -} diff --git a/SignalUtilitiesKit/NewNonContactConversationViewController.h b/SignalUtilitiesKit/NewNonContactConversationViewController.h deleted file mode 100644 index 58aaeff38..000000000 --- a/SignalUtilitiesKit/NewNonContactConversationViewController.h +++ /dev/null @@ -1,19 +0,0 @@ -// -// Copyright (c) 2018 Open Whisper Systems. All rights reserved. -// - -#import - -@protocol NewNonContactConversationViewControllerDelegate - -- (void)recipientIdWasSelected:(NSString *)recipientId; - -@end - -#pragma mark - - -@interface NewNonContactConversationViewController : SelectRecipientViewController - -@property (nonatomic, weak) id nonContactConversationDelegate; - -@end diff --git a/SignalUtilitiesKit/NewNonContactConversationViewController.m b/SignalUtilitiesKit/NewNonContactConversationViewController.m deleted file mode 100644 index a69f007d5..000000000 --- a/SignalUtilitiesKit/NewNonContactConversationViewController.m +++ /dev/null @@ -1,101 +0,0 @@ -// -// Copyright (c) 2018 Open Whisper Systems. All rights reserved. -// - -#import "NewNonContactConversationViewController.h" -#import "BlockListUIUtils.h" -#import "ContactsViewHelper.h" -#import -#import - -NS_ASSUME_NONNULL_BEGIN - -@interface NewNonContactConversationViewController () - -@end - -#pragma mark - - -@implementation NewNonContactConversationViewController - -- (void)loadView -{ - self.delegate = self; - - [super loadView]; - - self.title = NSLocalizedString( - @"NEW_NONCONTACT_CONVERSATION_VIEW_TITLE", @"Title for the 'new non-contact conversation' view."); -} - -- (NSString *)phoneNumberSectionTitle -{ - return nil; -} - -- (NSString *)phoneNumberButtonText -{ - return NSLocalizedString(@"NEW_NONCONTACT_CONVERSATION_VIEW_BUTTON", - @"A label for the 'add by phone number' button in the 'new non-contact conversation' view"); -} - -- (NSString *)contactsSectionTitle -{ - OWSFailDebug(@"Method should never be called."); - - return nil; -} - -- (void)phoneNumberWasSelected:(NSString *)phoneNumber -{ - OWSAssertDebug(phoneNumber.length > 0); - - [self selectRecipient:phoneNumber]; -} - -- (void)signalAccountWasSelected:(SignalAccount *)signalAccount -{ - OWSAssertDebug(signalAccount); - - [self selectRecipient:signalAccount.recipientId]; -} - -- (void)selectRecipient:(NSString *)recipientId -{ - OWSAssertDebug(recipientId.length > 0); - - [self.nonContactConversationDelegate recipientIdWasSelected:recipientId]; -} - -- (BOOL)shouldHideLocalNumber -{ - return NO; -} - -- (BOOL)shouldHideContacts -{ - return YES; -} - -- (BOOL)shouldValidatePhoneNumbers -{ - return YES; -} - -- (BOOL)canSignalAccountBeSelected:(SignalAccount *)signalAccount -{ - OWSFailDebug(@"Method should never be called."); - - return NO; -} - -- (nullable NSString *)accessoryMessageForSignalAccount:(SignalAccount *)signalAccount -{ - OWSFailDebug(@"Method should never be called."); - - return nil; -} - -@end - -NS_ASSUME_NONNULL_END diff --git a/SignalUtilitiesKit/Notification+Loki.swift b/SignalUtilitiesKit/Notification+Loki.swift index da6894d6d..e696caf26 100644 --- a/SignalUtilitiesKit/Notification+Loki.swift +++ b/SignalUtilitiesKit/Notification+Loki.swift @@ -2,51 +2,27 @@ public extension Notification.Name { // State changes - public static let blockedContactsUpdated = Notification.Name("blockedContactsUpdated") - public static let contactOnlineStatusChanged = Notification.Name("contactOnlineStatusChanged") - public static let groupThreadUpdated = Notification.Name("groupThreadUpdated") - public static let threadDeleted = Notification.Name("threadDeleted") - public static let threadSessionRestoreDevicesChanged = Notification.Name("threadSessionRestoreDevicesChanged") - // Message status changes - public static let calculatingPoW = Notification.Name("calculatingPoW") - public static let routing = Notification.Name("routing") - public static let messageSending = Notification.Name("messageSending") - public static let messageSent = Notification.Name("messageSent") - public static let messageFailed = Notification.Name("messageFailed") + static let blockedContactsUpdated = Notification.Name("blockedContactsUpdated") + static let contactOnlineStatusChanged = Notification.Name("contactOnlineStatusChanged") + static let groupThreadUpdated = Notification.Name("groupThreadUpdated") + static let threadDeleted = Notification.Name("threadDeleted") + static let threadSessionRestoreDevicesChanged = Notification.Name("threadSessionRestoreDevicesChanged") // Onboarding - public static let seedViewed = Notification.Name("seedViewed") + static let seedViewed = Notification.Name("seedViewed") // Interaction - public static let dataNukeRequested = Notification.Name("dataNukeRequested") - // Device linking - public static let unexpectedDeviceLinkRequestReceived = Notification.Name("unexpectedDeviceLinkRequestReceived") - // Onion requests - public static let buildingPaths = Notification.Name("buildingPaths") - public static let pathsBuilt = Notification.Name("pathsBuilt") - public static let onionRequestPathCountriesLoaded = Notification.Name("onionRequestPathCountriesLoaded") + static let dataNukeRequested = Notification.Name("dataNukeRequested") } @objc public extension NSNotification { // State changes - @objc public static let blockedContactsUpdated = Notification.Name.blockedContactsUpdated.rawValue as NSString - @objc public static let contactOnlineStatusChanged = Notification.Name.contactOnlineStatusChanged.rawValue as NSString - @objc public static let groupThreadUpdated = Notification.Name.groupThreadUpdated.rawValue as NSString - @objc public static let threadDeleted = Notification.Name.threadDeleted.rawValue as NSString - @objc public static let threadSessionRestoreDevicesChanged = Notification.Name.threadSessionRestoreDevicesChanged.rawValue as NSString - // Message statuses - @objc public static let calculatingPoW = Notification.Name.calculatingPoW.rawValue as NSString - @objc public static let routing = Notification.Name.routing.rawValue as NSString - @objc public static let messageSending = Notification.Name.messageSending.rawValue as NSString - @objc public static let messageSent = Notification.Name.messageSent.rawValue as NSString - @objc public static let messageFailed = Notification.Name.messageFailed.rawValue as NSString + @objc static let blockedContactsUpdated = Notification.Name.blockedContactsUpdated.rawValue as NSString + @objc static let contactOnlineStatusChanged = Notification.Name.contactOnlineStatusChanged.rawValue as NSString + @objc static let groupThreadUpdated = Notification.Name.groupThreadUpdated.rawValue as NSString + @objc static let threadDeleted = Notification.Name.threadDeleted.rawValue as NSString + @objc static let threadSessionRestoreDevicesChanged = Notification.Name.threadSessionRestoreDevicesChanged.rawValue as NSString // Onboarding - @objc public static let seedViewed = Notification.Name.seedViewed.rawValue as NSString + @objc static let seedViewed = Notification.Name.seedViewed.rawValue as NSString // Interaction - @objc public static let dataNukeRequested = Notification.Name.dataNukeRequested.rawValue as NSString - // Device linking - @objc public static let unexpectedDeviceLinkRequestReceived = Notification.Name.unexpectedDeviceLinkRequestReceived.rawValue as NSString - // Onion requests - @objc public static let buildingPaths = Notification.Name.buildingPaths.rawValue as NSString - @objc public static let pathsBuilt = Notification.Name.pathsBuilt.rawValue as NSString - @objc public static let onionRequestPathCountriesLoaded = Notification.Name.onionRequestPathCountriesLoaded.rawValue as NSString + @objc static let dataNukeRequested = Notification.Name.dataNukeRequested.rawValue as NSString } diff --git a/SignalUtilitiesKit/OWS2FAManager.h b/SignalUtilitiesKit/OWS2FAManager.h deleted file mode 100644 index a41eb6ebc..000000000 --- a/SignalUtilitiesKit/OWS2FAManager.h +++ /dev/null @@ -1,47 +0,0 @@ -// -// Copyright (c) 2018 Open Whisper Systems. All rights reserved. -// - -#import - -NS_ASSUME_NONNULL_BEGIN - -extern NSString *const NSNotificationName_2FAStateDidChange; - -typedef void (^OWS2FASuccess)(void); -typedef void (^OWS2FAFailure)(NSError *error); - -@class OWSPrimaryStorage; - -// This class can be safely accessed and used from any thread. -@interface OWS2FAManager : NSObject - -- (instancetype)init NS_UNAVAILABLE; - -- (instancetype)initWithPrimaryStorage:(OWSPrimaryStorage *)primaryStorage NS_DESIGNATED_INITIALIZER; - -+ (instancetype)sharedManager; - -@property (nullable, nonatomic, readonly) NSString *pinCode; - -- (BOOL)is2FAEnabled; -- (BOOL)isDueForReminder; - -// Request with service -- (void)requestEnable2FAWithPin:(NSString *)pin - success:(nullable OWS2FASuccess)success - failure:(nullable OWS2FAFailure)failure; - -// Sore local settings if, used during registration -- (void)mark2FAAsEnabledWithPin:(NSString *)pin; - -- (void)disable2FAWithSuccess:(nullable OWS2FASuccess)success failure:(nullable OWS2FAFailure)failure; - -- (void)updateRepetitionIntervalWithWasSuccessful:(BOOL)wasSuccessful; - -// used for testing -- (void)setDefaultRepetitionInterval; - -@end - -NS_ASSUME_NONNULL_END diff --git a/SignalUtilitiesKit/OWS2FAManager.m b/SignalUtilitiesKit/OWS2FAManager.m deleted file mode 100644 index 24b208b9e..000000000 --- a/SignalUtilitiesKit/OWS2FAManager.m +++ /dev/null @@ -1,271 +0,0 @@ -// -// Copyright (c) 2018 Open Whisper Systems. All rights reserved. -// - -#import "OWS2FAManager.h" -#import "NSNotificationCenter+OWS.h" -#import "OWSPrimaryStorage.h" -#import "OWSRequestFactory.h" -#import "SSKEnvironment.h" -#import "TSAccountManager.h" -#import "TSNetworkManager.h" -#import "YapDatabaseConnection+OWS.h" -#import "SSKAsserts.h" -#import - -NS_ASSUME_NONNULL_BEGIN - -NSString *const NSNotificationName_2FAStateDidChange = @"NSNotificationName_2FAStateDidChange"; - -NSString *const kOWS2FAManager_Collection = @"kOWS2FAManager_Collection"; -NSString *const kOWS2FAManager_LastSuccessfulReminderDateKey = @"kOWS2FAManager_LastSuccessfulReminderDateKey"; -NSString *const kOWS2FAManager_PinCode = @"kOWS2FAManager_PinCode"; -NSString *const kOWS2FAManager_RepetitionInterval = @"kOWS2FAManager_RepetitionInterval"; - -const NSUInteger kHourSecs = 60 * 60; -const NSUInteger kDaySecs = kHourSecs * 24; - -@interface OWS2FAManager () - -@property (nonatomic, readonly) YapDatabaseConnection *dbConnection; - -@end - -#pragma mark - - -@implementation OWS2FAManager - -+ (instancetype)sharedManager -{ - OWSAssertDebug(SSKEnvironment.shared.ows2FAManager); - - return SSKEnvironment.shared.ows2FAManager; -} - -- (instancetype)initWithPrimaryStorage:(OWSPrimaryStorage *)primaryStorage -{ - self = [super init]; - - if (!self) { - return self; - } - - OWSAssertDebug(primaryStorage); - - _dbConnection = primaryStorage.newDatabaseConnection; - - OWSSingletonAssert(); - - return self; -} - -#pragma mark - Dependencies - -- (TSNetworkManager *)networkManager { - OWSAssertDebug(SSKEnvironment.shared.networkManager); - - return SSKEnvironment.shared.networkManager; -} - -- (TSAccountManager *)tsAccountManager { - return TSAccountManager.sharedInstance; -} - -#pragma mark - - -- (nullable NSString *)pinCode -{ - return [self.dbConnection objectForKey:kOWS2FAManager_PinCode inCollection:kOWS2FAManager_Collection]; -} - -- (BOOL)is2FAEnabled -{ - return self.pinCode != nil; -} - -- (void)set2FANotEnabled -{ - [self.dbConnection removeObjectForKey:kOWS2FAManager_PinCode inCollection:kOWS2FAManager_Collection]; - - [[NSNotificationCenter defaultCenter] postNotificationNameAsync:NSNotificationName_2FAStateDidChange - object:nil - userInfo:nil]; - - [[self.tsAccountManager updateAccountAttributes] retainUntilComplete]; -} - -- (void)mark2FAAsEnabledWithPin:(NSString *)pin -{ - OWSAssertDebug(pin.length > 0); - - [self.dbConnection setObject:pin forKey:kOWS2FAManager_PinCode inCollection:kOWS2FAManager_Collection]; - - // Schedule next reminder relative to now - self.lastSuccessfulReminderDate = [NSDate new]; - - [[NSNotificationCenter defaultCenter] postNotificationNameAsync:NSNotificationName_2FAStateDidChange - object:nil - userInfo:nil]; - - [[self.tsAccountManager updateAccountAttributes] retainUntilComplete]; -} - -- (void)requestEnable2FAWithPin:(NSString *)pin - success:(nullable OWS2FASuccess)success - failure:(nullable OWS2FAFailure)failure -{ - OWSAssertDebug(pin.length > 0); - OWSAssertDebug(success); - OWSAssertDebug(failure); - - TSRequest *request = [OWSRequestFactory enable2FARequestWithPin:pin]; - [self.networkManager makeRequest:request - success:^(NSURLSessionDataTask *task, id responseObject) { - OWSAssertIsOnMainThread(); - - [self mark2FAAsEnabledWithPin:pin]; - - if (success) { - success(); - } - } - failure:^(NSURLSessionDataTask *task, NSError *error) { - OWSAssertIsOnMainThread(); - - if (failure) { - failure(error); - } - }]; -} - -- (void)disable2FAWithSuccess:(nullable OWS2FASuccess)success failure:(nullable OWS2FAFailure)failure -{ - TSRequest *request = [OWSRequestFactory disable2FARequest]; - [self.networkManager makeRequest:request - success:^(NSURLSessionDataTask *task, id responseObject) { - OWSAssertIsOnMainThread(); - - [self set2FANotEnabled]; - - if (success) { - success(); - } - } - failure:^(NSURLSessionDataTask *task, NSError *error) { - OWSAssertIsOnMainThread(); - - if (failure) { - failure(error); - } - }]; -} - - -#pragma mark - Reminders - -- (nullable NSDate *)lastSuccessfulReminderDate -{ - return [self.dbConnection dateForKey:kOWS2FAManager_LastSuccessfulReminderDateKey - inCollection:kOWS2FAManager_Collection]; -} - -- (void)setLastSuccessfulReminderDate:(nullable NSDate *)date -{ - OWSLogDebug(@"Seting setLastSuccessfulReminderDate:%@", date); - [self.dbConnection setDate:date - forKey:kOWS2FAManager_LastSuccessfulReminderDateKey - inCollection:kOWS2FAManager_Collection]; -} - -- (BOOL)isDueForReminder -{ - if (!self.is2FAEnabled) { - return NO; - } - - return self.nextReminderDate.timeIntervalSinceNow < 0; -} - -- (NSDate *)nextReminderDate -{ - NSDate *lastSuccessfulReminderDate = self.lastSuccessfulReminderDate ?: [NSDate distantPast]; - - return [lastSuccessfulReminderDate dateByAddingTimeInterval:self.repetitionInterval]; -} - -- (NSArray *)allRepetitionIntervals -{ - // Keep sorted monotonically increasing. - return @[ - @(6 * kHourSecs), - @(12 * kHourSecs), - @(1 * kDaySecs), - @(3 * kDaySecs), - @(7 * kDaySecs), - ]; -} - -- (double)defaultRepetitionInterval -{ - return self.allRepetitionIntervals.firstObject.doubleValue; -} - -- (NSTimeInterval)repetitionInterval -{ - return [self.dbConnection doubleForKey:kOWS2FAManager_RepetitionInterval - inCollection:kOWS2FAManager_Collection - defaultValue:self.defaultRepetitionInterval]; -} - -- (void)updateRepetitionIntervalWithWasSuccessful:(BOOL)wasSuccessful -{ - if (wasSuccessful) { - self.lastSuccessfulReminderDate = [NSDate new]; - } - - NSTimeInterval oldInterval = self.repetitionInterval; - NSTimeInterval newInterval = [self adjustRepetitionInterval:oldInterval wasSuccessful:wasSuccessful]; - - OWSLogInfo(@"%@ guess. Updating repetition interval: %f -> %f", - (wasSuccessful ? @"successful" : @"failed"), - oldInterval, - newInterval); - [self.dbConnection setDouble:newInterval - forKey:kOWS2FAManager_RepetitionInterval - inCollection:kOWS2FAManager_Collection]; -} - -- (NSTimeInterval)adjustRepetitionInterval:(NSTimeInterval)oldInterval wasSuccessful:(BOOL)wasSuccessful -{ - NSArray *allIntervals = self.allRepetitionIntervals; - - NSUInteger oldIndex = - [allIntervals indexOfObjectPassingTest:^BOOL(NSNumber *_Nonnull obj, NSUInteger idx, BOOL *_Nonnull stop) { - return oldInterval <= (NSTimeInterval)obj.doubleValue; - }]; - - NSUInteger newIndex; - if (wasSuccessful) { - newIndex = oldIndex + 1; - } else { - // prevent overflow - newIndex = oldIndex <= 0 ? 0 : oldIndex - 1; - } - - // clamp to be valid - newIndex = MAX(0, MIN(allIntervals.count - 1, newIndex)); - - NSTimeInterval newInterval = allIntervals[newIndex].doubleValue; - return newInterval; -} - -- (void)setDefaultRepetitionInterval -{ - [self.dbConnection setDouble:self.defaultRepetitionInterval - forKey:kOWS2FAManager_RepetitionInterval - inCollection:kOWS2FAManager_Collection]; -} - -@end - -NS_ASSUME_NONNULL_END diff --git a/SignalUtilitiesKit/OWSAddToContactsOfferMessage.h b/SignalUtilitiesKit/OWSAddToContactsOfferMessage.h deleted file mode 100644 index 8f5a4daa7..000000000 --- a/SignalUtilitiesKit/OWSAddToContactsOfferMessage.h +++ /dev/null @@ -1,21 +0,0 @@ -// -// Copyright (c) 2018 Open Whisper Systems. All rights reserved. -// - -#import - -NS_ASSUME_NONNULL_BEGIN - -// This is a deprecated class, we're keeping it around to avoid YapDB serialization errors -// TODO - remove this class, clean up existing instances, ensure any missed ones don't explode (UnknownDBObject) -__attribute__((deprecated)) @interface OWSAddToContactsOfferMessage : TSInfoMessage - -+ (instancetype)addToContactsOfferMessageWithTimestamp:(uint64_t)timestamp - thread:(TSThread *)thread - contactId:(NSString *)contactId; - -@property (nonatomic, readonly) NSString *contactId; - -@end - -NS_ASSUME_NONNULL_END diff --git a/SignalUtilitiesKit/OWSAddToContactsOfferMessage.m b/SignalUtilitiesKit/OWSAddToContactsOfferMessage.m deleted file mode 100644 index af1b648c8..000000000 --- a/SignalUtilitiesKit/OWSAddToContactsOfferMessage.m +++ /dev/null @@ -1,56 +0,0 @@ -// -// Copyright (c) 2018 Open Whisper Systems. All rights reserved. -// - -#import "OWSAddToContactsOfferMessage.h" - -NS_ASSUME_NONNULL_BEGIN - -@interface OWSAddToContactsOfferMessage () - -@property (nonatomic) NSString *contactId; - -@end - -#pragma mark - - -// This is a deprecated class, we're keeping it around to avoid YapDB serialization errors -// TODO - remove this class, clean up existing instances, ensure any missed ones don't explode (UnknownDBObject) -#pragma clang diagnostic push -#pragma clang diagnostic ignored "-Wdeprecated-implementations" -@implementation OWSAddToContactsOfferMessage -#pragma clang diagnostic pop - -+ (instancetype)addToContactsOfferMessageWithTimestamp:(uint64_t)timestamp - thread:(TSThread *)thread - contactId:(NSString *)contactId -{ - return [[OWSAddToContactsOfferMessage alloc] initWithTimestamp:timestamp thread:thread contactId:contactId]; -} - -- (instancetype)initWithTimestamp:(uint64_t)timestamp thread:(TSThread *)thread contactId:(NSString *)contactId -{ - self = [super initWithTimestamp:timestamp inThread:thread messageType:TSInfoMessageAddToContactsOffer]; - - if (self) { - _contactId = contactId; - } - - return self; -} - -- (BOOL)shouldUseReceiptDateForSorting -{ - // Use the timestamp, not the "received at" timestamp to sort, - // since we're creating these interactions after the fact and back-dating them. - return NO; -} - -- (BOOL)isDynamicInteraction -{ - return YES; -} - -@end - -NS_ASSUME_NONNULL_END diff --git a/SignalUtilitiesKit/OWSAddToProfileWhitelistOfferMessage.h b/SignalUtilitiesKit/OWSAddToProfileWhitelistOfferMessage.h deleted file mode 100644 index 143ec7ba0..000000000 --- a/SignalUtilitiesKit/OWSAddToProfileWhitelistOfferMessage.h +++ /dev/null @@ -1,19 +0,0 @@ -// -// Copyright (c) 2018 Open Whisper Systems. All rights reserved. -// - -#import - -NS_ASSUME_NONNULL_BEGIN - -// This is a deprecated class, we're keeping it around to avoid YapDB serialization errors -// TODO - remove this class, clean up existing instances, ensure any missed ones don't explode (UnknownDBObject) -__attribute__((deprecated)) @interface OWSAddToProfileWhitelistOfferMessage : TSInfoMessage - -+ (instancetype)addToProfileWhitelistOfferMessageWithTimestamp:(uint64_t)timestamp thread:(TSThread *)thread; - -@property (nonatomic, readonly) NSString *contactId; - -@end - -NS_ASSUME_NONNULL_END diff --git a/SignalUtilitiesKit/OWSAddToProfileWhitelistOfferMessage.m b/SignalUtilitiesKit/OWSAddToProfileWhitelistOfferMessage.m deleted file mode 100644 index 0ca611409..000000000 --- a/SignalUtilitiesKit/OWSAddToProfileWhitelistOfferMessage.m +++ /dev/null @@ -1,40 +0,0 @@ -// -// Copyright (c) 2018 Open Whisper Systems. All rights reserved. -// - -#import "OWSAddToProfileWhitelistOfferMessage.h" -#import "TSThread.h" - -NS_ASSUME_NONNULL_BEGIN - -// This is a deprecated class, we're keeping it around to avoid YapDB serialization errors -// TODO - remove this class, clean up existing instances, ensure any missed ones don't explode (UnknownDBObject) -#pragma clang diagnostic push -#pragma clang diagnostic ignored "-Wdeprecated-implementations" -@implementation OWSAddToProfileWhitelistOfferMessage -#pragma clang diagnostic pop - -+ (instancetype)addToProfileWhitelistOfferMessageWithTimestamp:(uint64_t)timestamp thread:(TSThread *)thread -{ - return [[OWSAddToProfileWhitelistOfferMessage alloc] - initWithTimestamp:timestamp - inThread:thread - messageType:(thread.isGroupThread ? TSInfoMessageAddGroupToProfileWhitelistOffer - : TSInfoMessageAddUserToProfileWhitelistOffer)]; -} - -- (BOOL)shouldUseReceiptDateForSorting -{ - // Use the timestamp, not the "received at" timestamp to sort, - // since we're creating these interactions after the fact and back-dating them. - return NO; -} - -- (BOOL)isDynamicInteraction -{ - return YES; -} - -@end - -NS_ASSUME_NONNULL_END diff --git a/SignalUtilitiesKit/OWSAnalytics.h b/SignalUtilitiesKit/OWSAnalytics.h deleted file mode 100755 index 789bafa61..000000000 --- a/SignalUtilitiesKit/OWSAnalytics.h +++ /dev/null @@ -1,165 +0,0 @@ -// -// Copyright (c) 2018 Open Whisper Systems. All rights reserved. -// - -#import - -NS_ASSUME_NONNULL_BEGIN - -// TODO: We probably don't need all of these levels. -typedef NS_ENUM(NSUInteger, OWSAnalyticsSeverity) { - // Info events are routine. - // - // It's safe to discard a large fraction of these events. - OWSAnalyticsSeverityInfo = 1, - // Error events should never be discarded. - OWSAnalyticsSeverityError = 3, - // Critical events are special. They are submitted immediately - // and not persisted, since the database may not be working. - OWSAnalyticsSeverityCritical = 4 -}; - -// This is a placeholder. We don't yet serialize or transmit analytics events. -// -// If/when we take this on, we'll want to develop a solution that can be used -// report user activity - especially serious bugs - without compromising user -// privacy in any way. We must _never_ include any identifying information. -@interface OWSAnalytics : NSObject - -// description: A non-empty string without any leading whitespace. -// This should conform to our analytics event naming conventions. -// "category_event_name", e.g. "database_error_no_database_file_found". -// parameters: Optional. -// If non-nil, the keys should all be non-empty NSStrings. -// Values should be NSStrings or NSNumbers. -+ (void)logEvent:(NSString *)eventName - severity:(OWSAnalyticsSeverity)severity - parameters:(nullable NSDictionary *)parameters - location:(const char *)location - line:(int)line; - -+ (void)appLaunchDidBegin; - -+ (long)orderOfMagnitudeOf:(long)value; - -@end - -typedef NSDictionary *_Nonnull (^OWSProdAssertParametersBlock)(void); - -// These methods should be used to assert errors for which we want to fire analytics events. -// -// In production, returns __Value, the assert value, so that we can handle this case. -// In debug builds, asserts. -// -// parametersBlock is of type OWSProdAssertParametersBlock. -// The "C" variants (e.g. OWSProdAssert() vs. OWSProdCAssert() should be used in free functions, -// where there is no self. They can also be used in blocks to avoid capturing a reference to self. -#define OWSProdAssertWParamsTemplate(__value, __eventName, __parametersBlock, __assertMacro) \ - { \ - if (!(BOOL)(__value)) { \ - NSDictionary *__eventParameters = (__parametersBlock ? __parametersBlock() : nil); \ - [DDLog flushLog]; \ - [OWSAnalytics logEvent:__eventName \ - severity:OWSAnalyticsSeverityError \ - parameters:__eventParameters \ - location:__PRETTY_FUNCTION__ \ - line:__LINE__]; \ - } \ - __assertMacro(__value); \ - return (BOOL)(__value); \ - } - -#define OWSProdAssertWParams(__value, __eventName, __parametersBlock) \ - OWSProdAssertWParamsTemplate(__value, __eventName, __parametersBlock, OWSAssert) - -#define OWSProdCAssertWParams(__value, __eventName, __parametersBlock) \ - OWSProdAssertWParamsTemplate(__value, __eventName, __parametersBlock, OWSCAssert) - -#define OWSProdAssert(__value, __eventName) OWSProdAssertWParams(__value, __eventName, nil) - -#define OWSProdCAssert(__value, __eventName) OWSProdCAssertWParams(__value, __eventName, nil) - -#define OWSProdFailWParamsTemplate(__eventName, __parametersBlock, __failMacro) \ - { \ - NSDictionary *__eventParameters \ - = (__parametersBlock ? ((OWSProdAssertParametersBlock)__parametersBlock)() : nil); \ - [OWSAnalytics logEvent:__eventName \ - severity:OWSAnalyticsSeverityCritical \ - parameters:__eventParameters \ - location:__PRETTY_FUNCTION__ \ - line:__LINE__]; \ - __failMacro(__eventName); \ - } - -#define OWSProdFailWParams(__eventName, __parametersBlock) \ - OWSProdFailWParamsTemplate(__eventName, __parametersBlock, OWSFailNoFormat) -#define OWSProdCFailWParams(__eventName, __parametersBlock) \ - OWSProdFailWParamsTemplate(__eventName, __parametersBlock, OWSCFailNoFormat) - -#define OWSProdFail(__eventName) OWSProdFailWParams(__eventName, nil) - -#define OWSProdCFail(__eventName) OWSProdCFailWParams(__eventName, nil) - -#define OWSProdCFail(__eventName) OWSProdCFailWParams(__eventName, nil) - -#define OWSProdEventWParams(__severityLevel, __eventName, __parametersBlock) \ - { \ - NSDictionary *__eventParameters \ - = (__parametersBlock ? ((OWSProdAssertParametersBlock)__parametersBlock)() : nil); \ - [OWSAnalytics logEvent:__eventName \ - severity:__severityLevel \ - parameters:__eventParameters \ - location:__PRETTY_FUNCTION__ \ - line:__LINE__]; \ - } - -#pragma mark - Info Events - -#define OWSProdInfoWParams(__eventName, __parametersBlock) \ - OWSProdEventWParams(OWSAnalyticsSeverityInfo, __eventName, __parametersBlock) - -#define OWSProdInfo(__eventName) OWSProdEventWParams(OWSAnalyticsSeverityInfo, __eventName, nil) - -#pragma mark - Error Events - -#define OWSProdErrorWParams(__eventName, __parametersBlock) \ - OWSProdEventWParams(OWSAnalyticsSeverityError, __eventName, __parametersBlock) - -#define OWSProdError(__eventName) OWSProdEventWParams(OWSAnalyticsSeverityError, __eventName, nil) - -#pragma mark - Critical Events - -#define OWSProdCriticalWParams(__eventName, __parametersBlock) \ - OWSProdEventWParams(OWSAnalyticsSeverityCritical, __eventName, __parametersBlock) - -#define OWSProdCritical(__eventName) OWSProdEventWParams(OWSAnalyticsSeverityCritical, __eventName, nil) - -#pragma mark - OWSMessageManager macros -// Defined here rather than in OWSMessageManager so that our analytic event extraction script -// can properly detect the event names. -// -// The debug logs can be more verbose than the analytics events. -// -// In this case `descriptionForEnvelope` is valuable enough to -// log but too dangerous to include in the analytics event. -#define OWSProdErrorWEnvelope(__analyticsEventName, __envelope) \ - { \ - OWSLogError(@"%s:%d %@: %@", \ - __PRETTY_FUNCTION__, \ - __LINE__, \ - __analyticsEventName, \ - [self descriptionForEnvelope:__envelope]); \ - OWSProdError(__analyticsEventName) \ - } - -#define OWSProdInfoWEnvelope(__analyticsEventName, __envelope) \ - { \ - OWSLogInfo(@"%s:%d %@: %@", \ - __PRETTY_FUNCTION__, \ - __LINE__, \ - __analyticsEventName, \ - [self descriptionForEnvelope:__envelope]); \ - OWSProdInfo(__analyticsEventName) \ - } - -NS_ASSUME_NONNULL_END diff --git a/SignalUtilitiesKit/OWSAnalytics.m b/SignalUtilitiesKit/OWSAnalytics.m deleted file mode 100755 index c5d896915..000000000 --- a/SignalUtilitiesKit/OWSAnalytics.m +++ /dev/null @@ -1,426 +0,0 @@ -// -// Copyright (c) 2018 Open Whisper Systems. All rights reserved. -// - -#import "OWSAnalytics.h" -#import "AppContext.h" -#import "OWSBackgroundTask.h" -#import "OWSPrimaryStorage.h" -#import "OWSQueues.h" -#import "SSKEnvironment.h" -#import "YapDatabaseConnection+OWS.h" -#import -#import -#import -#import -#import -#import "SSKAsserts.h" - -NS_ASSUME_NONNULL_BEGIN - -#ifdef DEBUG - -#define NO_SIGNAL_ANALYTICS - -#endif - -NSString *const kOWSAnalytics_EventsCollection = @"kOWSAnalytics_EventsCollection"; - -// Percentage of analytics events to discard. 0 <= x <= 100. -const int kOWSAnalytics_DiscardFrequency = 0; - -NSString *NSStringForOWSAnalyticsSeverity(OWSAnalyticsSeverity severity) -{ - switch (severity) { - case OWSAnalyticsSeverityInfo: - return @"Info"; - case OWSAnalyticsSeverityError: - return @"Error"; - case OWSAnalyticsSeverityCritical: - return @"Critical"; - } -} - -@interface OWSAnalytics () - -@property (nonatomic, readonly) Reachability *reachability; -@property (nonatomic, readonly) YapDatabaseConnection *dbConnection; - -@property (atomic) BOOL hasRequestInFlight; - -@end - -#pragma mark - - -@implementation OWSAnalytics - -+ (instancetype)sharedInstance -{ - static OWSAnalytics *instance = nil; - static dispatch_once_t onceToken; - dispatch_once(&onceToken, ^{ - instance = [[self alloc] initDefault]; - }); - return instance; -} - -// We lazy-create the analytics DB connection, so that we can handle -// errors that occur while initializing OWSPrimaryStorage. -+ (YapDatabaseConnection *)dbConnection -{ - return SSKEnvironment.shared.analyticsDBConnection; -} - -- (instancetype)initDefault -{ - self = [super init]; - - if (!self) { - return self; - } - - _reachability = [Reachability reachabilityForInternetConnection]; - - [self observeNotifications]; - - OWSSingletonAssert(); - - return self; -} - -- (void)observeNotifications -{ - [[NSNotificationCenter defaultCenter] addObserver:self - selector:@selector(reachabilityChanged) - name:kReachabilityChangedNotification - object:nil]; - [[NSNotificationCenter defaultCenter] addObserver:self - selector:@selector(applicationDidBecomeActive) - name:OWSApplicationDidBecomeActiveNotification - object:nil]; -} - -- (void)dealloc -{ - [[NSNotificationCenter defaultCenter] removeObserver:self]; -} - -- (void)reachabilityChanged -{ - OWSAssertIsOnMainThread(); - - [self tryToSyncEvents]; -} - -- (void)applicationDidBecomeActive -{ - OWSAssertIsOnMainThread(); - - [self tryToSyncEvents]; -} - -- (void)tryToSyncEvents -{ - return; // Loki: Do nothing - dispatch_async(self.serialQueue, ^{ - // Don't try to sync if: - // - // * There's no network available. - // * There's already a sync request in flight. - if (!self.reachability.isReachable) { - OWSLogVerbose(@"Not reachable"); - return; - } - if (self.hasRequestInFlight) { - return; - } - - __block NSString *firstEventKey = nil; - __block NSDictionary *firstEventDictionary = nil; - [self.dbConnection readWithBlock:^(YapDatabaseReadTransaction *transaction) { - // Take any event. We don't need to deliver them in any particular order. - [transaction enumerateKeysInCollection:kOWSAnalytics_EventsCollection - usingBlock:^(NSString *key, BOOL *_Nonnull stop) { - firstEventKey = key; - *stop = YES; - }]; - if (!firstEventKey) { - return; - } - - firstEventDictionary = [transaction objectForKey:firstEventKey inCollection:kOWSAnalytics_EventsCollection]; - OWSAssertDebug(firstEventDictionary); - OWSAssertDebug([firstEventDictionary isKindOfClass:[NSDictionary class]]); - }]; - - if (firstEventDictionary) { - [self sendEvent:firstEventDictionary eventKey:firstEventKey isCritical:NO]; - } - }); -} - -- (void)sendEvent:(NSDictionary *)eventDictionary eventKey:(NSString *)eventKey isCritical:(BOOL)isCritical -{ - return; // Loki: Do nothing - OWSAssertDebug(eventDictionary); - OWSAssertDebug(eventKey); - AssertOnDispatchQueue(self.serialQueue); - - if (isCritical) { - [self submitEvent:eventDictionary - eventKey:eventKey - success:^{ - OWSLogDebug(@"sendEvent[critical] succeeded: %@", eventKey); - } - failure:^{ - OWSLogError(@"sendEvent[critical] failed: %@", eventKey); - }]; - } else { - self.hasRequestInFlight = YES; - __block BOOL isComplete = NO; - [self submitEvent:eventDictionary - eventKey:eventKey - success:^{ - if (isComplete) { - return; - } - isComplete = YES; - OWSLogDebug(@"sendEvent succeeded: %@", eventKey); - dispatch_async(self.serialQueue, ^{ - self.hasRequestInFlight = NO; - - [LKStorage writeSyncWithBlock:^(YapDatabaseReadWriteTransaction *transaction) { - // Remove from queue. - [transaction removeObjectForKey:eventKey inCollection:kOWSAnalytics_EventsCollection]; - }]; - - // Wait a second between network requests / retries. - dispatch_after( - dispatch_time(DISPATCH_TIME_NOW, (int64_t)(1.f * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{ - [self tryToSyncEvents]; - }); - }); - } - failure:^{ - if (isComplete) { - return; - } - isComplete = YES; - OWSLogError(@"sendEvent failed: %@", eventKey); - dispatch_async(self.serialQueue, ^{ - self.hasRequestInFlight = NO; - - // Wait a second between network requests / retries. - dispatch_after( - dispatch_time(DISPATCH_TIME_NOW, (int64_t)(1.f * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{ - [self tryToSyncEvents]; - }); - }); - }]; - } -} - -- (void)submitEvent:(NSDictionary *)eventDictionary - eventKey:(NSString *)eventKey - success:(void (^_Nonnull)(void))successBlock - failure:(void (^_Nonnull)(void))failureBlock -{ - return; // Loki: Do nothing - OWSAssertDebug(eventDictionary); - OWSAssertDebug(eventKey); - AssertOnDispatchQueue(self.serialQueue); - - OWSLogDebug(@"submitting: %@", eventKey); - - __block OWSBackgroundTask *backgroundTask = - [OWSBackgroundTask backgroundTaskWithLabelStr:__PRETTY_FUNCTION__ - completionBlock:^(BackgroundTaskState backgroundTaskState) { - if (backgroundTaskState == BackgroundTaskState_Success) { - successBlock(); - } else { - failureBlock(); - } - }]; - - // Until we integrate with an analytics platform, behave as though all event delivery succeeds. - dispatch_async(self.serialQueue, ^{ - backgroundTask = nil; - }); -} - -- (dispatch_queue_t)serialQueue -{ - static dispatch_queue_t queue = nil; - static dispatch_once_t onceToken; - dispatch_once(&onceToken, ^{ - queue = dispatch_queue_create("org.whispersystems.analytics.serial", DISPATCH_QUEUE_SERIAL); - }); - return queue; -} - -- (NSString *)operatingSystemVersionString -{ - static NSString *result = nil; - static dispatch_once_t onceToken; - dispatch_once(&onceToken, ^{ - NSOperatingSystemVersion operatingSystemVersion = [[NSProcessInfo processInfo] operatingSystemVersion]; - result = [NSString stringWithFormat:@"%lu.%lu.%lu", - (unsigned long)operatingSystemVersion.majorVersion, - (unsigned long)operatingSystemVersion.minorVersion, - (unsigned long)operatingSystemVersion.patchVersion]; - }); - return result; -} - -- (NSDictionary *)eventSuperProperties -{ - NSMutableDictionary *result = [NSMutableDictionary new]; - result[@"app_version"] = [[NSBundle mainBundle] objectForInfoDictionaryKey:@"CFBundleShortVersionString"]; - result[@"platform"] = @"ios"; - result[@"ios_version"] = self.operatingSystemVersionString; - return result; -} - -- (long)orderOfMagnitudeOf:(long)value -{ - return [OWSAnalytics orderOfMagnitudeOf:value]; -} - -+ (long)orderOfMagnitudeOf:(long)value -{ - if (value <= 0) { - return 0; - } - return (long)round(pow(10, floor(log10(value)))); -} - -- (void)addEvent:(NSString *)eventName severity:(OWSAnalyticsSeverity)severity properties:(NSDictionary *)properties -{ - return; // Loki: Do nothing - OWSAssertDebug(eventName.length > 0); - OWSAssertDebug(properties); - -#ifndef NO_SIGNAL_ANALYTICS - BOOL isError = severity == OWSAnalyticsSeverityError; - BOOL isCritical = severity == OWSAnalyticsSeverityCritical; - - uint32_t discardValue = arc4random_uniform(101); - if (!isError && !isCritical && discardValue < kOWSAnalytics_DiscardFrequency) { - OWSLogVerbose(@"Discarding event: %@", eventName); - return; - } - - void (^addEvent)(void) = ^{ - // Add super properties. - NSMutableDictionary *eventProperties = (properties ? [properties mutableCopy] : [NSMutableDictionary new]); - [eventProperties addEntriesFromDictionary:self.eventSuperProperties]; - - NSDictionary *eventDictionary = [eventProperties copy]; - OWSAssertDebug(eventDictionary); - NSString *eventKey = [NSUUID UUID].UUIDString; - OWSLogDebug(@"enqueuing event: %@", eventKey); - - if (isCritical) { - // Critical events should not be serialized or enqueued - they should be submitted immediately. - [self sendEvent:eventDictionary eventKey:eventKey isCritical:YES]; - } else { - // Add to queue. - [LKStorage writeSyncWithBlock:^(YapDatabaseReadWriteTransaction *transaction) { - const int kMaxQueuedEvents = 5000; - if ([transaction numberOfKeysInCollection:kOWSAnalytics_EventsCollection] > kMaxQueuedEvents) { - OWSLogError(@"Event queue overflow."); - return; - } - - [transaction setObject:eventDictionary forKey:eventKey inCollection:kOWSAnalytics_EventsCollection]; - }]; - - [self tryToSyncEvents]; - } - }; - - if ([self shouldReportAsync:severity]) { - dispatch_async(self.serialQueue, addEvent); - } else { - dispatch_sync(self.serialQueue, addEvent); - } -#endif -} - -+ (void)logEvent:(NSString *)eventName - severity:(OWSAnalyticsSeverity)severity - parameters:(nullable NSDictionary *)parameters - location:(const char *)location - line:(int)line -{ - [[self sharedInstance] logEvent:eventName severity:severity parameters:parameters location:location line:line]; -} - -- (void)logEvent:(NSString *)eventName - severity:(OWSAnalyticsSeverity)severity - parameters:(nullable NSDictionary *)parameters - location:(const char *)location - line:(int)line -{ - return; // Loki: Do nothing - DDLogFlag logFlag; - switch (severity) { - case OWSAnalyticsSeverityInfo: - logFlag = DDLogFlagInfo; - break; - case OWSAnalyticsSeverityError: - logFlag = DDLogFlagError; - break; - case OWSAnalyticsSeverityCritical: - logFlag = DDLogFlagError; - break; - default: - OWSFailDebug(@"Unknown severity."); - logFlag = DDLogFlagDebug; - break; - } - - // Log the event. - NSString *logString = [NSString stringWithFormat:@"%s:%d %@", location, line, eventName]; - if (!parameters) { - LOG_MAYBE([self shouldReportAsync:severity], LOG_LEVEL_DEF, logFlag, 0, nil, location, @"%@", logString); - } else { - LOG_MAYBE([self shouldReportAsync:severity], - LOG_LEVEL_DEF, - logFlag, - 0, - nil, - location, - @"%@ %@", - logString, - parameters); - } - if (![self shouldReportAsync:severity]) { - [DDLog flushLog]; - } - - NSMutableDictionary *eventProperties = (parameters ? [parameters mutableCopy] : [NSMutableDictionary new]); - eventProperties[@"event_location"] = [NSString stringWithFormat:@"%s:%d", location, line]; - [self addEvent:eventName severity:severity properties:eventProperties]; -} - -- (BOOL)shouldReportAsync:(OWSAnalyticsSeverity)severity -{ - return severity != OWSAnalyticsSeverityCritical; -} - -#pragma mark - Logging - -+ (void)appLaunchDidBegin -{ - [self.sharedInstance appLaunchDidBegin]; -} - -- (void)appLaunchDidBegin -{ - OWSProdInfo([OWSAnalyticsEvents appLaunch]); -} - -@end - -NS_ASSUME_NONNULL_END diff --git a/SignalUtilitiesKit/OWSAnalyticsEvents.h b/SignalUtilitiesKit/OWSAnalyticsEvents.h deleted file mode 100755 index 3f7b33d59..000000000 --- a/SignalUtilitiesKit/OWSAnalyticsEvents.h +++ /dev/null @@ -1,239 +0,0 @@ -// -// Copyright (c) 2018 Open Whisper Systems. All rights reserved. -// - -#import - -NS_ASSUME_NONNULL_BEGIN - -@interface OWSAnalyticsEvents : NSObject - -- (instancetype)init NS_UNAVAILABLE; - -// The code between these markers is code-generated by: -// SignalServiceKit/Utilities/extract_analytics_event_names.py -// To add an event, insert your logging event as a string e.g.: -// -// OWSProdFail(@"messageSenderErrorMissingNewPreKeyBundle"); -// -// Then run SignalServiceKit/Utilities/extract_analytics_event_names.py, which -// will extract the string into a named method in this class. -#pragma mark - Code Generation Marker - -+ (NSString *)accountsErrorRegisterPushTokensFailed; - -+ (NSString *)accountsErrorUnregisterAccountRequestFailed; - -+ (NSString *)accountsErrorVerificationCodeRequestFailed; - -+ (NSString *)accountsErrorVerifyAccountRequestFailed; - -+ (NSString *)appDelegateErrorFailedToRegisterForRemoteNotifications; - -+ (NSString *)appLaunch; - -+ (NSString *)appLaunchComplete; - -+ (NSString *)callServiceCallAlreadySet; - -+ (NSString *)callServiceCallIdMismatch; - -+ (NSString *)callServiceCallMismatch; - -+ (NSString *)callServiceCallMissing; - -+ (NSString *)callServiceCallUnexpectedlyIdle; - -+ (NSString *)callServiceCallViewCouldNotPresent; - -+ (NSString *)callServiceCouldNotCreatePeerConnectionClientPromise; - -+ (NSString *)callServiceCouldNotCreateReadyToSendIceUpdatesPromise; - -+ (NSString *)callServiceErrorHandleLocalAddedIceCandidate; - -+ (NSString *)callServiceErrorHandleLocalHungupCall; - -+ (NSString *)callServiceErrorHandleReceivedErrorExternal; - -+ (NSString *)callServiceErrorHandleReceivedErrorInternal; - -+ (NSString *)callServiceErrorHandleRemoteAddedIceCandidate; - -+ (NSString *)callServiceErrorIncomingConnectionFailedExternal; - -+ (NSString *)callServiceErrorIncomingConnectionFailedInternal; - -+ (NSString *)callServiceErrorOutgoingConnectionFailedExternal; - -+ (NSString *)callServiceErrorOutgoingConnectionFailedInternal; - -+ (NSString *)callServiceErrorTimeoutWhileConnectingIncoming; - -+ (NSString *)callServiceErrorTimeoutWhileConnectingOutgoing; - -+ (NSString *)callServiceMissingFulfillReadyToSendIceUpdatesPromise; - -+ (NSString *)callServicePeerConnectionAlreadySet; - -+ (NSString *)callServicePeerConnectionMissing; - -+ (NSString *)callServiceCallDataMissing; - -+ (NSString *)contactsErrorContactsIntersectionFailed; - -+ (NSString *)errorAttachmentRequestFailed; - -+ (NSString *)errorCouldNotPresentViewDueToCall; - -+ (NSString *)errorEnableVideoCallingRequestFailed; - -+ (NSString *)errorGetDevicesFailed; - -+ (NSString *)errorPrekeysAvailablePrekeysRequestFailed; - -+ (NSString *)errorPrekeysCurrentSignedPrekeyRequestFailed; - -+ (NSString *)errorPrekeysUpdateFailedJustSigned; - -+ (NSString *)errorPrekeysUpdateFailedSignedAndOnetime; - -+ (NSString *)errorProvisioningCodeRequestFailed; - -+ (NSString *)errorProvisioningRequestFailed; - -+ (NSString *)errorUnlinkDeviceFailed; - -+ (NSString *)errorUpdateAttributesRequestFailed; - -+ (NSString *)messageSenderErrorMissingNewPreKeyBundle; - -+ (NSString *)messageManagerErrorCallMessageNoActionablePayload; - -+ (NSString *)messageManagerErrorCorruptMessage; - -+ (NSString *)messageManagerErrorCouldNotHandlePrekeyBundle; - -+ (NSString *)messageManagerErrorCouldNotHandleUnidentifiedSenderMessage; - -+ (NSString *)messageManagerErrorCouldNotHandleSecureMessage; - -+ (NSString *)messageManagerErrorEnvelopeNoActionablePayload; - -+ (NSString *)messageManagerErrorEnvelopeTypeKeyExchange; - -+ (NSString *)messageManagerErrorEnvelopeTypeOther; - -+ (NSString *)messageManagerErrorEnvelopeTypeUnknown; - -+ (NSString *)messageManagerErrorInvalidKey; - -+ (NSString *)messageManagerErrorInvalidKeyId; - -+ (NSString *)messageManagerErrorInvalidMessageVersion; - -+ (NSString *)messageManagerErrorInvalidProtocolMessage; - -+ (NSString *)messageManagerErrorMessageEnvelopeHasNoContent; - -+ (NSString *)messageManagerErrorNoSession; - -+ (NSString *)messageManagerErrorOversizeMessage; - -+ (NSString *)messageManagerErrorSyncMessageFromUnknownSource; - -+ (NSString *)messageManagerErrorUntrustedIdentityKeyException; - -+ (NSString *)messageReceiverErrorLargeMessage; - -+ (NSString *)messageReceiverErrorOversizeMessage; - -+ (NSString *)messageSendErrorCouldNotSerializeMessageJson; - -+ (NSString *)messageSendErrorFailedDueToPrekeyUpdateFailures; - -+ (NSString *)messageSendErrorFailedDueToUntrustedKey; - -+ (NSString *)messageSenderErrorCouldNotFindContacts1; - -+ (NSString *)messageSenderErrorCouldNotFindContacts2; - -+ (NSString *)messageSenderErrorCouldNotFindContacts3; - -+ (NSString *)messageSenderErrorCouldNotLoadAttachment; - -+ (NSString *)messageSenderErrorCouldNotParseMismatchedDevicesJson; - -+ (NSString *)messageSenderErrorCouldNotWriteAttachment; - -+ (NSString *)messageSenderErrorGenericSendFailure; - -+ (NSString *)messageSenderErrorInvalidIdentityKeyLength; - -+ (NSString *)messageSenderErrorInvalidIdentityKeyType; - -+ (NSString *)messageSenderErrorNoMissingOrExtraDevices; - -+ (NSString *)messageSenderErrorRecipientPrekeyRequestFailed; - -+ (NSString *)messageSenderErrorSendOperationDidNotComplete; - -+ (NSString *)messageSenderErrorUnexpectedKeyBundle; - -+ (NSString *)peerConnectionClientErrorSendDataChannelMessageFailed; - -+ (NSString *)prekeysDeletedOldAcceptedSignedPrekey; - -+ (NSString *)prekeysDeletedOldSignedPrekey; - -+ (NSString *)prekeysDeletedOldUnacceptedSignedPrekey; - -+ (NSString *)profileManagerErrorAvatarUploadFormInvalidAcl; - -+ (NSString *)profileManagerErrorAvatarUploadFormInvalidAlgorithm; - -+ (NSString *)profileManagerErrorAvatarUploadFormInvalidCredential; - -+ (NSString *)profileManagerErrorAvatarUploadFormInvalidDate; - -+ (NSString *)profileManagerErrorAvatarUploadFormInvalidKey; - -+ (NSString *)profileManagerErrorAvatarUploadFormInvalidPolicy; - -+ (NSString *)profileManagerErrorAvatarUploadFormInvalidResponse; - -+ (NSString *)profileManagerErrorAvatarUploadFormInvalidSignature; - -+ (NSString *)registrationBegan; - -+ (NSString *)registrationRegisteredPhoneNumber; - -+ (NSString *)registrationRegisteringCode; - -+ (NSString *)registrationRegisteringRequestedNewCodeBySms; - -+ (NSString *)registrationRegisteringRequestedNewCodeByVoice; - -+ (NSString *)registrationRegisteringSubmittedCode; - -+ (NSString *)registrationRegistrationFailed; - -+ (NSString *)registrationVerificationBack; - -+ (NSString *)storageErrorCouldNotDecodeClass; - -+ (NSString *)storageErrorCouldNotLoadDatabase; - -+ (NSString *)storageErrorCouldNotLoadDatabaseSecondAttempt; - -+ (NSString *)storageErrorCouldNotStoreKeychainValue; - -+ (NSString *)storageErrorDeserialization; - -+ (NSString *)storageErrorFileProtection; - -#pragma mark - Code Generation Marker - -@end - -NS_ASSUME_NONNULL_END diff --git a/SignalUtilitiesKit/OWSAnalyticsEvents.m b/SignalUtilitiesKit/OWSAnalyticsEvents.m deleted file mode 100755 index 1a4b0cb0c..000000000 --- a/SignalUtilitiesKit/OWSAnalyticsEvents.m +++ /dev/null @@ -1,549 +0,0 @@ -// -// Copyright (c) 2018 Open Whisper Systems. All rights reserved. -// - -#import "OWSAnalyticsEvents.h" - -NS_ASSUME_NONNULL_BEGIN - -@implementation OWSAnalyticsEvents - -// The code between these markers is code-generated by: -// SignalServiceKit/Utilities/extract_analytics_event_names.py -#pragma mark - Code Generation Marker - -+ (NSString *)accountsErrorRegisterPushTokensFailed -{ - return @"accounts_error_register_push_tokens_failed"; -} - -+ (NSString *)accountsErrorUnregisterAccountRequestFailed -{ - return @"accounts_error_unregister_account_request_failed"; -} - -+ (NSString *)accountsErrorVerificationCodeRequestFailed -{ - return @"accounts_error_verification_code_request_failed"; -} - -+ (NSString *)accountsErrorVerifyAccountRequestFailed -{ - return @"accounts_error_verify_account_request_failed"; -} - -+ (NSString *)appDelegateErrorFailedToRegisterForRemoteNotifications -{ - return @"app_delegate_error_failed_to_register_for_remote_notifications"; -} - -+ (NSString *)appLaunch -{ - return @"app_launch"; -} - -+ (NSString *)appLaunchComplete -{ - return @"app_launch_complete"; -} - -+ (NSString *)callServiceCallAlreadySet -{ - return @"call_service_call_already_set"; -} - -+ (NSString *)callServiceCallIdMismatch -{ - return @"call_service_call_id_mismatch"; -} - -+ (NSString *)callServiceCallMismatch -{ - return @"call_service_call_mismatch"; -} - -+ (NSString *)callServiceCallMissing -{ - return @"call_service_call_missing"; -} - -+ (NSString *)callServiceCallUnexpectedlyIdle -{ - return @"call_service_call_unexpectedly_idle"; -} - -+ (NSString *)callServiceCallViewCouldNotPresent -{ - return @"call_service_call_view_could_not_present"; -} - -+ (NSString *)callServiceCouldNotCreatePeerConnectionClientPromise -{ - return @"call_service_could_not_create_peer_connection_client_promise"; -} - -+ (NSString *)callServiceCouldNotCreateReadyToSendIceUpdatesPromise -{ - return @"call_service_could_not_create_ready_to_send_ice_updates_promise"; -} - -+ (NSString *)callServiceErrorHandleLocalAddedIceCandidate -{ - return @"call_service_error_handle_local_added_ice_candidate"; -} - -+ (NSString *)callServiceErrorHandleLocalHungupCall -{ - return @"call_service_error_handle_local_hungup_call"; -} - -+ (NSString *)callServiceErrorHandleReceivedErrorExternal -{ - return @"call_service_error_handle_received_error_external"; -} - -+ (NSString *)callServiceErrorHandleReceivedErrorInternal -{ - return @"call_service_error_handle_received_error_internal"; -} - -+ (NSString *)callServiceErrorHandleRemoteAddedIceCandidate -{ - return @"call_service_error_handle_remote_added_ice_candidate"; -} - -+ (NSString *)callServiceErrorIncomingConnectionFailedExternal -{ - return @"call_service_error_incoming_connection_failed_external"; -} - -+ (NSString *)callServiceErrorIncomingConnectionFailedInternal -{ - return @"call_service_error_incoming_connection_failed_internal"; -} - -+ (NSString *)callServiceErrorOutgoingConnectionFailedExternal -{ - return @"call_service_error_outgoing_connection_failed_external"; -} - -+ (NSString *)callServiceErrorOutgoingConnectionFailedInternal -{ - return @"call_service_error_outgoing_connection_failed_internal"; -} - -+ (NSString *)callServiceErrorTimeoutWhileConnectingIncoming -{ - return @"call_service_error_timeout_while_connecting_incoming"; -} - -+ (NSString *)callServiceErrorTimeoutWhileConnectingOutgoing -{ - return @"call_service_error_timeout_while_connecting_outgoing"; -} - -+ (NSString *)callServiceMissingFulfillReadyToSendIceUpdatesPromise -{ - return @"call_service_missing_fulfill_ready_to_send_ice_updates_promise"; -} - -+ (NSString *)callServicePeerConnectionAlreadySet -{ - return @"call_service_peer_connection_already_set"; -} - -+ (NSString *)callServicePeerConnectionMissing -{ - return @"call_service_peer_connection_missing"; -} - -+ (NSString *)callServiceCallDataMissing -{ - return @"call_service_call_data_missing"; -} - -+ (NSString *)contactsErrorContactsIntersectionFailed -{ - return @"contacts_error_contacts_intersection_failed"; -} - -+ (NSString *)errorAttachmentRequestFailed -{ - return @"error_attachment_request_failed"; -} - -+ (NSString *)errorCouldNotPresentViewDueToCall -{ - return @"error_could_not_present_view_due_to_call"; -} - -+ (NSString *)errorEnableVideoCallingRequestFailed -{ - return @"error_enable_video_calling_request_failed"; -} - -+ (NSString *)errorGetDevicesFailed -{ - return @"error_get_devices_failed"; -} - -+ (NSString *)errorPrekeysAvailablePrekeysRequestFailed -{ - return @"error_prekeys_available_prekeys_request_failed"; -} - -+ (NSString *)errorPrekeysCurrentSignedPrekeyRequestFailed -{ - return @"error_prekeys_current_signed_prekey_request_failed"; -} - -+ (NSString *)errorPrekeysUpdateFailedJustSigned -{ - return @"error_prekeys_update_failed_just_signed"; -} - -+ (NSString *)errorPrekeysUpdateFailedSignedAndOnetime -{ - return @"error_prekeys_update_failed_signed_and_onetime"; -} - -+ (NSString *)errorProvisioningCodeRequestFailed -{ - return @"error_provisioning_code_request_failed"; -} - -+ (NSString *)errorProvisioningRequestFailed -{ - return @"error_provisioning_request_failed"; -} - -+ (NSString *)errorUnlinkDeviceFailed -{ - return @"error_unlink_device_failed"; -} - -+ (NSString *)errorUpdateAttributesRequestFailed -{ - return @"error_update_attributes_request_failed"; -} - -+ (NSString *)messageSenderErrorMissingNewPreKeyBundle -{ - return @"messageSenderErrorMissingNewPreKeyBundle"; -} - -+ (NSString *)messageManagerErrorCallMessageNoActionablePayload -{ - return @"message_manager_error_call_message_no_actionable_payload"; -} - -+ (NSString *)messageManagerErrorCorruptMessage -{ - return @"message_manager_error_corrupt_message"; -} - -+ (NSString *)messageManagerErrorCouldNotHandlePrekeyBundle -{ - return @"message_manager_error_could_not_handle_prekey_bundle"; -} - -+ (NSString *)messageManagerErrorCouldNotHandleUnidentifiedSenderMessage -{ - return @"message_manager_error_could_not_handle_unidentified_sender_message"; -} - -+ (NSString *)messageManagerErrorCouldNotHandleSecureMessage -{ - return @"message_manager_error_could_not_handle_secure_message"; -} - -+ (NSString *)messageManagerErrorEnvelopeNoActionablePayload -{ - return @"message_manager_error_envelope_no_actionable_payload"; -} - -+ (NSString *)messageManagerErrorEnvelopeTypeKeyExchange -{ - return @"message_manager_error_envelope_type_key_exchange"; -} - -+ (NSString *)messageManagerErrorEnvelopeTypeOther -{ - return @"message_manager_error_envelope_type_other"; -} - -+ (NSString *)messageManagerErrorEnvelopeTypeUnknown -{ - return @"message_manager_error_envelope_type_unknown"; -} - -+ (NSString *)messageManagerErrorInvalidKey -{ - return @"message_manager_error_invalid_key"; -} - -+ (NSString *)messageManagerErrorInvalidKeyId -{ - return @"message_manager_error_invalid_key_id"; -} - -+ (NSString *)messageManagerErrorInvalidMessageVersion -{ - return @"message_manager_error_invalid_message_version"; -} - -+ (NSString *)messageManagerErrorInvalidProtocolMessage -{ - return @"message_manager_error_invalid_protocol_message"; -} - -+ (NSString *)messageManagerErrorMessageEnvelopeHasNoContent -{ - return @"message_manager_error_message_envelope_has_no_content"; -} - -+ (NSString *)messageManagerErrorNoSession -{ - return @"message_manager_error_no_session"; -} - -+ (NSString *)messageManagerErrorOversizeMessage -{ - return @"message_manager_error_oversize_message"; -} - -+ (NSString *)messageManagerErrorSyncMessageFromUnknownSource -{ - return @"message_manager_error_sync_message_from_unknown_source"; -} - -+ (NSString *)messageManagerErrorUntrustedIdentityKeyException -{ - return @"message_manager_error_untrusted_identity_key_exception"; -} - -+ (NSString *)messageReceiverErrorLargeMessage -{ - return @"message_receiver_error_large_message"; -} - -+ (NSString *)messageReceiverErrorOversizeMessage -{ - return @"message_receiver_error_oversize_message"; -} - -+ (NSString *)messageSendErrorCouldNotSerializeMessageJson -{ - return @"message_send_error_could_not_serialize_message_json"; -} - -+ (NSString *)messageSendErrorFailedDueToPrekeyUpdateFailures -{ - return @"message_send_error_failed_due_to_prekey_update_failures"; -} - -+ (NSString *)messageSendErrorFailedDueToUntrustedKey -{ - return @"message_send_error_failed_due_to_untrusted_key"; -} - -+ (NSString *)messageSenderErrorCouldNotFindContacts1 -{ - return @"message_sender_error_could_not_find_contacts_1"; -} - -+ (NSString *)messageSenderErrorCouldNotFindContacts2 -{ - return @"message_sender_error_could_not_find_contacts_2"; -} - -+ (NSString *)messageSenderErrorCouldNotFindContacts3 -{ - return @"message_sender_error_could_not_find_contacts_3"; -} - -+ (NSString *)messageSenderErrorCouldNotLoadAttachment -{ - return @"message_sender_error_could_not_load_attachment"; -} - -+ (NSString *)messageSenderErrorCouldNotParseMismatchedDevicesJson -{ - return @"message_sender_error_could_not_parse_mismatched_devices_json"; -} - -+ (NSString *)messageSenderErrorCouldNotWriteAttachment -{ - return @"message_sender_error_could_not_write_attachment"; -} - -+ (NSString *)messageSenderErrorGenericSendFailure -{ - return @"message_sender_error_generic_send_failure"; -} - -+ (NSString *)messageSenderErrorInvalidIdentityKeyLength -{ - return @"message_sender_error_invalid_identity_key_length"; -} - -+ (NSString *)messageSenderErrorInvalidIdentityKeyType -{ - return @"message_sender_error_invalid_identity_key_type"; -} - -+ (NSString *)messageSenderErrorNoMissingOrExtraDevices -{ - return @"message_sender_error_no_missing_or_extra_devices"; -} - -+ (NSString *)messageSenderErrorRecipientPrekeyRequestFailed -{ - return @"message_sender_error_recipient_prekey_request_failed"; -} - -+ (NSString *)messageSenderErrorSendOperationDidNotComplete -{ - return @"message_sender_error_send_operation_did_not_complete"; -} - -+ (NSString *)messageSenderErrorUnexpectedKeyBundle -{ - return @"message_sender_error_unexpected_key_bundle"; -} - -+ (NSString *)peerConnectionClientErrorSendDataChannelMessageFailed -{ - return @"peer_connection_client_error_send_data_channel_message_failed"; -} - -+ (NSString *)prekeysDeletedOldAcceptedSignedPrekey -{ - return @"prekeys_deleted_old_accepted_signed_prekey"; -} - -+ (NSString *)prekeysDeletedOldSignedPrekey -{ - return @"prekeys_deleted_old_signed_prekey"; -} - -+ (NSString *)prekeysDeletedOldUnacceptedSignedPrekey -{ - return @"prekeys_deleted_old_unaccepted_signed_prekey"; -} - -+ (NSString *)profileManagerErrorAvatarUploadFormInvalidAcl -{ - return @"profile_manager_error_avatar_upload_form_invalid_acl"; -} - -+ (NSString *)profileManagerErrorAvatarUploadFormInvalidAlgorithm -{ - return @"profile_manager_error_avatar_upload_form_invalid_algorithm"; -} - -+ (NSString *)profileManagerErrorAvatarUploadFormInvalidCredential -{ - return @"profile_manager_error_avatar_upload_form_invalid_credential"; -} - -+ (NSString *)profileManagerErrorAvatarUploadFormInvalidDate -{ - return @"profile_manager_error_avatar_upload_form_invalid_date"; -} - -+ (NSString *)profileManagerErrorAvatarUploadFormInvalidKey -{ - return @"profile_manager_error_avatar_upload_form_invalid_key"; -} - -+ (NSString *)profileManagerErrorAvatarUploadFormInvalidPolicy -{ - return @"profile_manager_error_avatar_upload_form_invalid_policy"; -} - -+ (NSString *)profileManagerErrorAvatarUploadFormInvalidResponse -{ - return @"profile_manager_error_avatar_upload_form_invalid_response"; -} - -+ (NSString *)profileManagerErrorAvatarUploadFormInvalidSignature -{ - return @"profile_manager_error_avatar_upload_form_invalid_signature"; -} - -+ (NSString *)registrationBegan -{ - return @"registration_began"; -} - -+ (NSString *)registrationRegisteredPhoneNumber -{ - return @"registration_registered_phone_number"; -} - -+ (NSString *)registrationRegisteringCode -{ - return @"registration_registering_code"; -} - -+ (NSString *)registrationRegisteringRequestedNewCodeBySms -{ - return @"registration_registering_requested_new_code_by_sms"; -} - -+ (NSString *)registrationRegisteringRequestedNewCodeByVoice -{ - return @"registration_registering_requested_new_code_by_voice"; -} - -+ (NSString *)registrationRegisteringSubmittedCode -{ - return @"registration_registering_submitted_code"; -} - -+ (NSString *)registrationRegistrationFailed -{ - return @"registration_registration_failed"; -} - -+ (NSString *)registrationVerificationBack -{ - return @"registration_verification_back"; -} - -+ (NSString *)storageErrorCouldNotDecodeClass -{ - return @"storage_error_could_not_decode_class"; -} - -+ (NSString *)storageErrorCouldNotLoadDatabase -{ - return @"storage_error_could_not_load_database"; -} - -+ (NSString *)storageErrorCouldNotLoadDatabaseSecondAttempt -{ - return @"storage_error_could_not_load_database_second_attempt"; -} - -+ (NSString *)storageErrorCouldNotStoreKeychainValue -{ - return @"storage_error_could_not_store_keychain_value"; -} - -+ (NSString *)storageErrorDeserialization -{ - return @"storage_error_deserialization"; -} - -+ (NSString *)storageErrorFileProtection -{ - return @"storage_error_file_protection"; -} - -#pragma mark - Code Generation Marker - -@end - -NS_ASSUME_NONNULL_END diff --git a/SignalUtilitiesKit/OWSAttachmentDownloads.m b/SignalUtilitiesKit/OWSAttachmentDownloads.m index 908dba719..740a622aa 100644 --- a/SignalUtilitiesKit/OWSAttachmentDownloads.m +++ b/SignalUtilitiesKit/OWSAttachmentDownloads.m @@ -11,7 +11,7 @@ #import "OWSError.h" #import "OWSFileSystem.h" #import "OWSPrimaryStorage.h" -#import "OWSRequestFactory.h" + #import "SSKEnvironment.h" #import "TSAttachmentPointer.h" #import "TSAttachmentStream.h" @@ -19,7 +19,7 @@ #import "TSGroupThread.h" #import "TSInfoMessage.h" #import "TSMessage.h" -#import "TSNetworkManager.h" + #import "TSThread.h" #import #import @@ -95,12 +95,6 @@ typedef void (^AttachmentDownloadFailure)(NSError *error); return SSKEnvironment.shared.primaryStorage; } -- (TSNetworkManager *)networkManager -{ - return SSKEnvironment.shared.networkManager; -} - - #pragma mark - - (instancetype)init @@ -171,9 +165,7 @@ typedef void (^AttachmentDownloadFailure)(NSError *error); OWSAssertDebug(attachmentPointer); [self enqueueJobsForAttachmentStreams:@[] - attachmentPointers:@[ - attachmentPointer, - ] + attachmentPointers:@[ attachmentPointer ] message:nil success:success failure:failure]; @@ -290,11 +282,7 @@ typedef void (^AttachmentDownloadFailure)(NSError *error); [job.attachmentPointer saveWithTransaction:transaction]; if (job.message) { - if (!CurrentAppContext().isMainApp) { - job.message.hasAttachmentsInNSE = true; - } else { - job.message.hasAttachmentsInNSE = false; - } + job.message.hasUnfetchedAttachmentsFromPN = !CurrentAppContext().isMainApp; [job.message saveWithTransaction:transaction]; [job.message touchWithTransaction:transaction]; diff --git a/SignalUtilitiesKit/OWSAvatarBuilder.h b/SignalUtilitiesKit/OWSAvatarBuilder.h deleted file mode 100644 index 81e7bd785..000000000 --- a/SignalUtilitiesKit/OWSAvatarBuilder.h +++ /dev/null @@ -1,35 +0,0 @@ -// -// Copyright (c) 2018 Open Whisper Systems. All rights reserved. -// - -NS_ASSUME_NONNULL_BEGIN - -extern const NSUInteger kStandardAvatarSize; -extern const NSUInteger kLargeAvatarSize; - -@class TSThread; -@class UIImage; - -@interface OWSAvatarBuilder : NSObject - -+ (nullable UIImage *)buildImageForThread:(TSThread *)thread - diameter:(NSUInteger)diameter NS_SWIFT_NAME(buildImage(thread:diameter:)); - -+ (nullable UIImage *)buildRandomAvatarWithDiameter:(NSUInteger)diameter; - -- (nullable UIImage *)buildSavedImage; -- (nullable UIImage *)buildDefaultImage; -- (nullable UIImage *)build; - -+ (nullable UIImage *)avatarImageWithInitials:(NSString *)initials - backgroundColor:(UIColor *)backgroundColor - diameter:(NSUInteger)diameter; - -+ (nullable UIImage *)avatarImageWithIcon:(UIImage *)icon - iconSize:(CGSize)iconSize - backgroundColor:(UIColor *)backgroundColor - diameter:(NSUInteger)diameter; - -@end - -NS_ASSUME_NONNULL_END diff --git a/SignalUtilitiesKit/OWSAvatarBuilder.m b/SignalUtilitiesKit/OWSAvatarBuilder.m deleted file mode 100644 index 65553c79e..000000000 --- a/SignalUtilitiesKit/OWSAvatarBuilder.m +++ /dev/null @@ -1,296 +0,0 @@ -// -// Copyright (c) 2018 Open Whisper Systems. All rights reserved. -// - -#import "OWSAvatarBuilder.h" -#import "OWSContactAvatarBuilder.h" -#import "OWSGroupAvatarBuilder.h" -#import "TSContactThread.h" -#import "TSGroupThread.h" -#import "Theme.h" -#import "UIColor+OWS.h" -#import "UIFont+OWS.h" -#import "UIView+OWS.h" - - -NS_ASSUME_NONNULL_BEGIN - -const NSUInteger kStandardAvatarSize = 48; -const NSUInteger kLargeAvatarSize = 68; - -typedef void (^OWSAvatarDrawBlock)(CGContextRef context); - -@implementation OWSAvatarBuilder - -+ (nullable UIImage *)buildImageForThread:(TSThread *)thread - diameter:(NSUInteger)diameter -{ - OWSAssertDebug(thread); - - OWSAvatarBuilder *avatarBuilder; - if ([thread isKindOfClass:[TSContactThread class]]) { - TSContactThread *contactThread = (TSContactThread *)thread; - avatarBuilder = [[OWSContactAvatarBuilder alloc] initWithSignalId:contactThread.contactIdentifier colorName:contactThread.conversationColorName diameter:diameter]; - } else if ([thread isKindOfClass:[TSGroupThread class]]) { - avatarBuilder = [[OWSGroupAvatarBuilder alloc] initWithThread:(TSGroupThread *)thread diameter:diameter]; - } else { - OWSLogError(@"called with unsupported thread: %@", thread); - } - return [avatarBuilder build]; -} - -+ (nullable UIImage *)buildRandomAvatarWithDiameter:(NSUInteger)diameter -{ - NSArray *eyes = @[ @":", @"=", @"8", @"B" ]; - NSArray *mouths = @[ @"3", @")", @"(", @"|", @"\\", @"P", @"D", @"o" ]; - // eyebrows are rare - NSArray *eyebrows = @[ @">", @"", @"", @"", @"" ]; - - NSString *randomEye = eyes[arc4random_uniform((uint32_t)eyes.count)]; - NSString *randomMouth = mouths[arc4random_uniform((uint32_t)mouths.count)]; - NSString *randomEyebrow = eyebrows[arc4random_uniform((uint32_t)eyebrows.count)]; - NSString *face = [NSString stringWithFormat:@"%@%@%@", randomEyebrow, randomEye, randomMouth]; - - UIColor *backgroundColor = [UIColor colorWithRGBHex:0xaca6633]; - - return [self avatarImageWithDiameter:diameter - backgroundColor:backgroundColor - drawBlock:^(CGContextRef context) { - CGContextTranslateCTM(context, diameter / 2, diameter / 2); - CGContextRotateCTM(context, (CGFloat)M_PI_2); - CGContextTranslateCTM(context, -diameter / 2, -diameter / 2); - - [self drawInitialsInAvatar:face - textColor:self.avatarForegroundColor - font:[self avatarTextFontForDiameter:diameter] - diameter:diameter]; - }]; -} - -+ (UIColor *)avatarForegroundColor -{ - return (Theme.isDarkThemeEnabled ? UIColor.ows_gray05Color : UIColor.ows_whiteColor); -} - -+ (UIFont *)avatarTextFontForDiameter:(NSUInteger)diameter -{ - // Adapt the font size to reflect the diameter. - CGFloat fontSize = 20.f * diameter / kStandardAvatarSize; - return [UIFont ows_mediumFontWithSize:fontSize]; -} - -+ (nullable UIImage *)avatarImageWithInitials:(NSString *)initials - backgroundColor:(UIColor *)backgroundColor - diameter:(NSUInteger)diameter -{ - return [self avatarImageWithInitials:initials - backgroundColor:backgroundColor - textColor:self.avatarForegroundColor - font:[self avatarTextFontForDiameter:diameter] - diameter:diameter]; -} - -+ (nullable UIImage *)avatarImageWithInitials:(NSString *)initials - backgroundColor:(UIColor *)backgroundColor - textColor:(UIColor *)textColor - font:(UIFont *)font - diameter:(NSUInteger)diameter -{ - OWSAssertDebug(initials); - OWSAssertDebug(textColor); - OWSAssertDebug(font); - - return [self avatarImageWithDiameter:diameter - backgroundColor:backgroundColor - drawBlock:^(CGContextRef context) { - [self drawInitialsInAvatar:initials textColor:textColor font:font diameter:diameter]; - }]; -} - -+ (nullable UIImage *)avatarImageWithIcon:(UIImage *)icon - iconSize:(CGSize)iconSize - backgroundColor:(UIColor *)backgroundColor - diameter:(NSUInteger)diameter -{ - return [self avatarImageWithIcon:icon - iconSize:iconSize - iconColor:self.avatarForegroundColor - backgroundColor:backgroundColor - diameter:diameter]; -} - -+ (nullable UIImage *)avatarImageWithIcon:(UIImage *)icon - iconSize:(CGSize)iconSize - iconColor:(UIColor *)iconColor - backgroundColor:(UIColor *)backgroundColor - diameter:(NSUInteger)diameter -{ - OWSAssertDebug(icon); - OWSAssertDebug(iconColor); - - return [self avatarImageWithDiameter:diameter - backgroundColor:backgroundColor - drawBlock:^(CGContextRef context) { - [self drawIconInAvatar:icon - iconSize:iconSize - iconColor:iconColor - diameter:diameter - context:context]; - }]; -} - -+ (nullable UIImage *)avatarImageWithDiameter:(NSUInteger)diameter - backgroundColor:(UIColor *)backgroundColor - drawBlock:(OWSAvatarDrawBlock)drawBlock -{ - OWSAssertDebug(drawBlock); - OWSAssertDebug(backgroundColor); - OWSAssertDebug(diameter > 0); - - CGRect frame = CGRectMake(0.0f, 0.0f, diameter, diameter); - - UIGraphicsBeginImageContextWithOptions(frame.size, NO, [UIScreen mainScreen].scale); - CGContextRef _Nullable context = UIGraphicsGetCurrentContext(); - if (!context) { - return nil; - } - - CGContextSetFillColorWithColor(context, backgroundColor.CGColor); - CGContextFillRect(context, frame); - - // Gradient - CGColorSpaceRef colorspace = CGColorSpaceCreateDeviceRGB(); - CGFloat gradientLocations[] = { 0.0, 1.0 }; - CGGradientRef _Nullable gradient = CGGradientCreateWithColors(colorspace, - (__bridge CFArrayRef) @[ - (id)[UIColor colorWithWhite:0.f alpha:0.f].CGColor, - (id)[UIColor colorWithWhite:0.f alpha:0.15f].CGColor, - ], - gradientLocations); - if (!gradient) { - return nil; - } - CGPoint startPoint = CGPointMake(diameter * 0.5f, 0); - CGPoint endPoint = CGPointMake(diameter * 0.5f, diameter); - CGContextDrawLinearGradient(context, - gradient, - startPoint, - endPoint, - kCGGradientDrawsBeforeStartLocation | kCGGradientDrawsAfterEndLocation); - CFRelease(gradient); - - CGContextSaveGState(context); - drawBlock(context); - CGContextRestoreGState(context); - - UIImage *_Nullable image = UIGraphicsGetImageFromCurrentImageContext(); - - UIGraphicsEndImageContext(); - - return image; -} - -+ (void)drawInitialsInAvatar:(NSString *)initials - textColor:(UIColor *)textColor - font:(UIFont *)font - diameter:(NSUInteger)diameter -{ - OWSAssertDebug(initials); - OWSAssertDebug(textColor); - OWSAssertDebug(font); - OWSAssertDebug(diameter > 0); - - CGRect frame = CGRectMake(0.0f, 0.0f, diameter, diameter); - - NSDictionary *textAttributes = @{ - NSFontAttributeName : font, - NSForegroundColorAttributeName : textColor, - }; - CGSize textSize = - [initials boundingRectWithSize:frame.size - options:(NSStringDrawingUsesLineFragmentOrigin | NSStringDrawingUsesFontLeading) - attributes:textAttributes - context:nil] - .size; - // Ensure that the text fits within the avatar bounds, with a margin. - if (textSize.width > 0 && textSize.height > 0) { - CGFloat textDiameter = (CGFloat)sqrt(textSize.width * textSize.width + textSize.height * textSize.height); - // Leave a 10% margin. - CGFloat maxTextDiameter = diameter * 0.9f; - if (textDiameter > maxTextDiameter) { - font = [font fontWithSize:font.pointSize * maxTextDiameter / textDiameter]; - textAttributes = @{ - NSFontAttributeName : font, - NSForegroundColorAttributeName : textColor, - }; - textSize = - [initials boundingRectWithSize:frame.size - options:(NSStringDrawingUsesLineFragmentOrigin | NSStringDrawingUsesFontLeading) - attributes:textAttributes - context:nil] - .size; - } - } else { - OWSFailDebug(@"Text has invalid bounds."); - } - - CGPoint drawPoint = CGPointMake((diameter - textSize.width) * 0.5f, (diameter - textSize.height) * 0.5f); - - [initials drawAtPoint:drawPoint withAttributes:textAttributes]; -} - -+ (void)drawIconInAvatar:(UIImage *)icon - iconSize:(CGSize)iconSize - iconColor:(UIColor *)iconColor - diameter:(NSUInteger)diameter - context:(CGContextRef)context -{ - OWSAssertDebug(icon); - OWSAssertDebug(iconColor); - OWSAssertDebug(diameter > 0); - OWSAssertDebug(context); - - // UIKit uses an ULO coordinate system (upper-left-origin). - // Core Graphics uses an LLO coordinate system (lower-left-origin). - CGAffineTransform flipVertical = CGAffineTransformMake(1, 0, 0, -1, 0, diameter); - CGContextConcatCTM(context, flipVertical); - - CGRect imageRect = CGRectZero; - imageRect.size = CGSizeMake(diameter, diameter); - - // The programmatic equivalent of UIImageRenderingModeAlwaysTemplate/tintColor. - CGContextSetBlendMode(context, kCGBlendModeNormal); - CGRect maskRect = CGRectZero; - maskRect.origin = CGPointScale( - CGPointSubtract(CGPointMake(diameter, diameter), CGPointMake(iconSize.width, iconSize.height)), 0.5f); - maskRect.size = iconSize; - CGContextClipToMask(context, maskRect, icon.CGImage); - CGContextSetFillColor(context, CGColorGetComponents(iconColor.CGColor)); - CGContextFillRect(context, imageRect); -} - -- (nullable UIImage *)build -{ - UIImage *_Nullable savedImage = [self buildSavedImage]; - if (savedImage) { - return savedImage; - } else { - return [self buildDefaultImage]; - } -} - -- (nullable UIImage *)buildSavedImage -{ - OWSAbstractMethod(); - return nil; -} - -- (nullable UIImage *)buildDefaultImage -{ - OWSAbstractMethod(); - return nil; -} - -@end - -NS_ASSUME_NONNULL_END diff --git a/SignalUtilitiesKit/OWSBackgroundTask.m b/SignalUtilitiesKit/OWSBackgroundTask.m index 7384c063c..b9d8b17cc 100644 --- a/SignalUtilitiesKit/OWSBackgroundTask.m +++ b/SignalUtilitiesKit/OWSBackgroundTask.m @@ -4,7 +4,6 @@ #import "OWSBackgroundTask.h" #import "AppContext.h" -#import "NSTimer+OWS.h" #import #import "SSKAsserts.h" diff --git a/SignalUtilitiesKit/OWSBatchMessageProcessor.h b/SignalUtilitiesKit/OWSBatchMessageProcessor.h deleted file mode 100644 index ae364752f..000000000 --- a/SignalUtilitiesKit/OWSBatchMessageProcessor.h +++ /dev/null @@ -1,40 +0,0 @@ -// -// Copyright (c) 2018 Open Whisper Systems. All rights reserved. -// - -#import - -NS_ASSUME_NONNULL_BEGIN - -@class OWSPrimaryStorage; -@class OWSStorage; -@class SSKProtoEnvelope; -@class YapDatabaseReadWriteTransaction; - -@interface OWSMessageContentQueue : NSObject - -- (dispatch_queue_t)serialQueue; - -@end - -// This class is used to write incoming (decrypted, unprocessed) -// messages to a durable queue and then process them in batches, -// in the order in which they were received. -@interface OWSBatchMessageProcessor : NSObject - -@property (nonatomic, readonly) OWSMessageContentQueue *processingQueue; - -- (instancetype)init NS_UNAVAILABLE; -- (instancetype)initWithPrimaryStorage:(OWSPrimaryStorage *)primaryStorage NS_DESIGNATED_INITIALIZER; - -+ (NSString *)databaseExtensionName; -+ (void)asyncRegisterDatabaseExtension:(OWSStorage *)storage; - -- (void)enqueueEnvelopeData:(NSData *)envelopeData - plaintextData:(NSData *_Nullable)plaintextData - wasReceivedByUD:(BOOL)wasReceivedByUD - transaction:(YapDatabaseReadWriteTransaction *)transaction; - -@end - -NS_ASSUME_NONNULL_END diff --git a/SignalUtilitiesKit/OWSBatchMessageProcessor.m b/SignalUtilitiesKit/OWSBatchMessageProcessor.m deleted file mode 100644 index 7b26795eb..000000000 --- a/SignalUtilitiesKit/OWSBatchMessageProcessor.m +++ /dev/null @@ -1,546 +0,0 @@ -// -// Copyright (c) 2018 Open Whisper Systems. All rights reserved. -// - -#import "OWSBatchMessageProcessor.h" -#import "AppContext.h" -#import "AppReadiness.h" -#import "NSArray+OWS.h" -#import "NotificationsProtocol.h" -#import "OWSBackgroundTask.h" -#import "OWSMessageManager.h" -#import "OWSPrimaryStorage+SessionStore.h" -#import "OWSPrimaryStorage.h" -#import "OWSQueues.h" -#import "OWSStorage.h" -#import "SSKEnvironment.h" -#import "TSAccountManager.h" -#import "TSDatabaseView.h" -#import "TSErrorMessage.h" -#import "TSYapDatabaseObject.h" -#import -#import -#import -#import -#import -#import -#import "SSKAsserts.h" - -NS_ASSUME_NONNULL_BEGIN - -#pragma mark - Persisted data model - -@interface OWSMessageContentJob : TSYapDatabaseObject - -@property (nonatomic, readonly) NSDate *createdAt; -@property (nonatomic, readonly) NSData *envelopeData; -@property (nonatomic, readonly, nullable) NSData *plaintextData; -@property (nonatomic, readonly) BOOL wasReceivedByUD; - -- (instancetype)initWithEnvelopeData:(NSData *)envelopeData - plaintextData:(NSData *_Nullable)plaintextData - wasReceivedByUD:(BOOL)wasReceivedByUD NS_DESIGNATED_INITIALIZER; -- (nullable instancetype)initWithCoder:(NSCoder *)coder NS_DESIGNATED_INITIALIZER; -- (instancetype)initWithUniqueId:(NSString *_Nullable)uniqueId NS_UNAVAILABLE; - -@property (nonatomic, readonly, nullable) SSKProtoEnvelope *envelope; - -@end - -#pragma mark - - -@implementation OWSMessageContentJob - -+ (NSString *)collection -{ - return @"OWSBatchMessageProcessingJob"; -} - -- (instancetype)initWithEnvelopeData:(NSData *)envelopeData - plaintextData:(NSData *_Nullable)plaintextData - wasReceivedByUD:(BOOL)wasReceivedByUD -{ - OWSAssertDebug(envelopeData); - - self = [super initWithUniqueId:[NSUUID new].UUIDString]; - - if (!self) { - return self; - } - - _envelopeData = envelopeData; - _plaintextData = plaintextData; - _wasReceivedByUD = wasReceivedByUD; - _createdAt = [NSDate new]; - - return self; -} - -- (nullable instancetype)initWithCoder:(NSCoder *)coder -{ - return [super initWithCoder:coder]; -} - -- (nullable SSKProtoEnvelope *)envelope -{ - NSError *error; - SSKProtoEnvelope *_Nullable result = [SSKProtoEnvelope parseData:self.envelopeData error:&error]; - - if (error) { - OWSFailDebug(@"paring SSKProtoEnvelope failed with error: %@", error); - return nil; - } - - return result; -} - -@end - -#pragma mark - Finder - -NSString *const OWSMessageContentJobFinderExtensionName = @"OWSMessageContentJobFinderExtensionName2"; -NSString *const OWSMessageContentJobFinderExtensionGroup = @"OWSMessageContentJobFinderExtensionGroup2"; - -@interface OWSMessageContentJobFinder : NSObject - -@end - -#pragma mark - - -@interface OWSMessageContentJobFinder () - -@property (nonatomic, readonly) YapDatabaseConnection *dbConnection; - -@end - -#pragma mark - - -@implementation OWSMessageContentJobFinder - -- (instancetype)initWithDBConnection:(YapDatabaseConnection *)dbConnection -{ - OWSSingletonAssert(); - - self = [super init]; - if (!self) { - return self; - } - - _dbConnection = dbConnection; - - return self; -} - -- (NSArray *)nextJobsForBatchSize:(NSUInteger)maxBatchSize -{ - NSMutableArray *jobs = [NSMutableArray new]; - [self.dbConnection readWithBlock:^(YapDatabaseReadTransaction *_Nonnull transaction) { - YapDatabaseViewTransaction *viewTransaction = [transaction ext:OWSMessageContentJobFinderExtensionName]; - OWSAssertDebug(viewTransaction != nil); - [viewTransaction enumerateKeysAndObjectsInGroup:OWSMessageContentJobFinderExtensionGroup - usingBlock:^(NSString *_Nonnull collection, - NSString *_Nonnull key, - id _Nonnull object, - NSUInteger index, - BOOL *_Nonnull stop) { - OWSMessageContentJob *job = object; - [jobs addObject:job]; - if (jobs.count >= maxBatchSize) { - *stop = YES; - } - }]; - }]; - - return [jobs copy]; -} - -- (void)addJobWithEnvelopeData:(NSData *)envelopeData - plaintextData:(NSData *_Nullable)plaintextData - wasReceivedByUD:(BOOL)wasReceivedByUD - transaction:(YapDatabaseReadWriteTransaction *)transaction -{ - OWSAssertDebug(envelopeData); - OWSAssertDebug(transaction); - - OWSMessageContentJob *job = [[OWSMessageContentJob alloc] initWithEnvelopeData:envelopeData - plaintextData:plaintextData - wasReceivedByUD:wasReceivedByUD]; - [job saveWithTransaction:transaction]; -} - -- (void)removeJobsWithIds:(NSArray *)uniqueIds -{ - [LKStorage writeSyncWithBlock:^(YapDatabaseReadWriteTransaction *_Nonnull transaction) { - [transaction removeObjectsForKeys:uniqueIds inCollection:[OWSMessageContentJob collection]]; - }]; -} - -+ (YapDatabaseView *)databaseExtension -{ - YapDatabaseViewSorting *sorting = - [YapDatabaseViewSorting withObjectBlock:^NSComparisonResult(YapDatabaseReadTransaction *transaction, - NSString *group, - NSString *collection1, - NSString *key1, - id object1, - NSString *collection2, - NSString *key2, - id object2) { - - if (![object1 isKindOfClass:[OWSMessageContentJob class]]) { - OWSFailDebug(@"Unexpected object: %@ in collection: %@", [object1 class], collection1); - return NSOrderedSame; - } - OWSMessageContentJob *job1 = (OWSMessageContentJob *)object1; - - if (![object2 isKindOfClass:[OWSMessageContentJob class]]) { - OWSFailDebug(@"Unexpected object: %@ in collection: %@", [object2 class], collection2); - return NSOrderedSame; - } - OWSMessageContentJob *job2 = (OWSMessageContentJob *)object2; - - return [job1.createdAt compare:job2.createdAt]; - }]; - - YapDatabaseViewGrouping *grouping = - [YapDatabaseViewGrouping withObjectBlock:^NSString *_Nullable(YapDatabaseReadTransaction *_Nonnull transaction, - NSString *_Nonnull collection, - NSString *_Nonnull key, - id _Nonnull object) { - if (![object isKindOfClass:[OWSMessageContentJob class]]) { - OWSFailDebug(@"Unexpected object: %@ in collection: %@", object, collection); - return nil; - } - - // Arbitrary string - all in the same group. We're only using the view for sorting. - return OWSMessageContentJobFinderExtensionGroup; - }]; - - YapDatabaseViewOptions *options = [YapDatabaseViewOptions new]; - options.allowedCollections = - [[YapWhitelistBlacklist alloc] initWithWhitelist:[NSSet setWithObject:[OWSMessageContentJob collection]]]; - - return [[YapDatabaseAutoView alloc] initWithGrouping:grouping sorting:sorting versionTag:@"1" options:options]; -} - - -+ (void)asyncRegisterDatabaseExtension:(OWSStorage *)storage -{ - YapDatabaseView *existingView = [storage registeredExtension:OWSMessageContentJobFinderExtensionName]; - if (existingView) { - OWSFailDebug(@"%@ was already initialized.", OWSMessageContentJobFinderExtensionName); - // already initialized - return; - } - [storage asyncRegisterExtension:[self databaseExtension] withName:OWSMessageContentJobFinderExtensionName]; -} - -@end - -#pragma mark - Queue Processing - -@interface OWSMessageContentQueue () - -@property (nonatomic, readonly) YapDatabaseConnection *dbConnection; -@property (nonatomic, readonly) OWSMessageContentJobFinder *finder; -@property (nonatomic) BOOL isDrainingQueue; -@property (atomic) BOOL isAppInBackground; - -- (instancetype)initWithDBConnection:(YapDatabaseConnection *)dbConnection - finder:(OWSMessageContentJobFinder *)finder NS_DESIGNATED_INITIALIZER; -- (instancetype)init NS_UNAVAILABLE; - -@end - -#pragma mark - - -@implementation OWSMessageContentQueue - -- (instancetype)initWithDBConnection:(YapDatabaseConnection *)dbConnection finder:(OWSMessageContentJobFinder *)finder -{ - OWSSingletonAssert(); - - self = [super init]; - - if (!self) { - return self; - } - - _dbConnection = dbConnection; - _finder = finder; - _isDrainingQueue = NO; - - [[NSNotificationCenter defaultCenter] addObserver:self - selector:@selector(applicationWillEnterForeground:) - name:OWSApplicationWillEnterForegroundNotification - object:nil]; - [[NSNotificationCenter defaultCenter] addObserver:self - selector:@selector(applicationDidEnterBackground:) - name:OWSApplicationDidEnterBackgroundNotification - object:nil]; - [[NSNotificationCenter defaultCenter] addObserver:self - selector:@selector(registrationStateDidChange:) - name:RegistrationStateDidChangeNotification - object:nil]; - - // Start processing. - [AppReadiness runNowOrWhenAppDidBecomeReady:^{ - if (CurrentAppContext().isMainApp) { - [self drainQueue]; - } - }]; - - return self; -} - -- (void)dealloc -{ - [[NSNotificationCenter defaultCenter] removeObserver:self]; -} - -#pragma mark - Singletons - -- (OWSMessageManager *)messageManager -{ - OWSAssertDebug(SSKEnvironment.shared.messageManager); - - return SSKEnvironment.shared.messageManager; -} - -- (TSAccountManager *)tsAccountManager -{ - OWSAssertDebug(SSKEnvironment.shared.tsAccountManager); - - return SSKEnvironment.shared.tsAccountManager; -} - -#pragma mark - Notifications - -- (void)applicationWillEnterForeground:(NSNotification *)notification -{ - self.isAppInBackground = NO; -} - -- (void)applicationDidEnterBackground:(NSNotification *)notification -{ - self.isAppInBackground = YES; -} - -- (void)registrationStateDidChange:(NSNotification *)notification -{ - OWSAssertIsOnMainThread(); - - [AppReadiness runNowOrWhenAppDidBecomeReady:^{ - if (CurrentAppContext().isMainApp) { - [self drainQueue]; - } - }]; -} - -#pragma mark - instance methods - -- (dispatch_queue_t)serialQueue -{ - static dispatch_queue_t queue = nil; - static dispatch_once_t onceToken; - dispatch_once(&onceToken, ^{ - queue = dispatch_queue_create("org.whispersystems.message.process", DISPATCH_QUEUE_SERIAL); - }); - return queue; -} - -- (void)enqueueEnvelopeData:(NSData *)envelopeData - plaintextData:(NSData *_Nullable)plaintextData - wasReceivedByUD:(BOOL)wasReceivedByUD - transaction:(YapDatabaseReadWriteTransaction *)transaction -{ - OWSAssertDebug(envelopeData); - OWSAssertDebug(transaction); - - // We need to persist the decrypted envelope data ASAP to prevent data loss. - [self.finder addJobWithEnvelopeData:envelopeData - plaintextData:plaintextData - wasReceivedByUD:wasReceivedByUD - transaction:transaction]; -} - -- (void)drainQueue -{ - OWSAssertDebug(AppReadiness.isAppReady); - - if (!CurrentAppContext().isMainApp) { return; } - if (!self.tsAccountManager.isRegisteredAndReady) { return; } - - dispatch_async(self.serialQueue, ^{ - if (self.isDrainingQueue) { return; } - self.isDrainingQueue = YES; - [self drainQueueWorkStep]; - }); -} - -- (void)drainQueueWorkStep -{ - AssertOnDispatchQueue(self.serialQueue); - - // We want a value that is just high enough to yield performance benefits - const NSUInteger kIncomingMessageBatchSize = 32; - - NSArray *batchJobs = [self.finder nextJobsForBatchSize:kIncomingMessageBatchSize]; - OWSAssertDebug(batchJobs); - if (batchJobs.count < 1) { - self.isDrainingQueue = NO; - OWSLogVerbose(@"Queue is drained"); - return; - } - - OWSBackgroundTask *_Nullable backgroundTask = [OWSBackgroundTask backgroundTaskWithLabelStr:__PRETTY_FUNCTION__]; - - NSArray *processedJobs = [self processJobs:batchJobs]; - - [self.finder removeJobsWithIds:processedJobs.uniqueIds]; - - OWSAssertDebug(backgroundTask); - backgroundTask = nil; - - OWSLogVerbose(@"completed %lu/%lu jobs. %lu jobs left.", - (unsigned long)processedJobs.count, - (unsigned long)batchJobs.count, - (unsigned long)[OWSMessageContentJob numberOfKeysInCollection]); - - // Wait a bit in hopes of increasing the batch size. - // This delay won't affect the first message to arrive when this queue is idle, - // so by definition we're receiving more than one message and can benefit from - // batching. - dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0.5f * NSEC_PER_SEC)), self.serialQueue, ^{ - [self drainQueueWorkStep]; - }); -} - -- (NSArray *)processJobs:(NSArray *)jobs -{ - AssertOnDispatchQueue(self.serialQueue); - - NSMutableArray *processedJobs = [NSMutableArray new]; - - [LKStorage writeSyncWithBlock:^(YapDatabaseReadWriteTransaction *transaction) { - for (OWSMessageContentJob *job in jobs) { - - void (^reportFailure)(YapDatabaseReadWriteTransaction *transaction) = ^( - YapDatabaseReadWriteTransaction *transaction) { - TSErrorMessage *errorMessage = [TSErrorMessage corruptedMessageInUnknownThread]; - [SSKEnvironment.shared.notificationsManager notifyUserForThreadlessErrorMessage:errorMessage transaction:transaction]; - }; - - @try { - SSKProtoEnvelope *_Nullable envelope = job.envelope; - if (!envelope) { - reportFailure(transaction); - } else { - [self.messageManager throws_processEnvelope:envelope - plaintextData:job.plaintextData - wasReceivedByUD:job.wasReceivedByUD - transaction:transaction - serverID:0]; - } - } @catch (NSException *exception) { - reportFailure(transaction); - } - - [processedJobs addObject:job]; - - if (self.isAppInBackground) { - // If the app is in the background, stop processing this batch. - // - // Since this check is done after processing jobs, we'll continue - // to process jobs in batches of 1. This reduces the cost of - // being interrupted and rolled back if app is suspended. - break; - } - } - }]; - - return processedJobs; -} - -@end - -#pragma mark - OWSBatchMessageProcessor - -@interface OWSBatchMessageProcessor () - -@property (nonatomic, readonly) YapDatabaseConnection *dbConnection; - -@end - -#pragma mark - - -@implementation OWSBatchMessageProcessor - -- (instancetype)initWithPrimaryStorage:(OWSPrimaryStorage *)primaryStorage -{ - OWSSingletonAssert(); - - self = [super init]; - if (!self) { - return self; - } - - // For coherency we use the same dbConnection to persist and read the unprocessed envelopes - YapDatabaseConnection *dbConnection = [primaryStorage newDatabaseConnection]; - OWSMessageContentJobFinder *finder = [[OWSMessageContentJobFinder alloc] initWithDBConnection:dbConnection]; - OWSMessageContentQueue *processingQueue = - [[OWSMessageContentQueue alloc] initWithDBConnection:dbConnection finder:finder]; - - _processingQueue = processingQueue; - - [AppReadiness runNowOrWhenAppDidBecomeReady:^{ - if (CurrentAppContext().isMainApp) { - [self.processingQueue drainQueue]; - } - }]; - - return self; -} - -#pragma mark - class methods - -+ (NSString *)databaseExtensionName -{ - return OWSMessageContentJobFinderExtensionName; -} - -+ (void)asyncRegisterDatabaseExtension:(OWSStorage *)storage -{ - [OWSMessageContentJobFinder asyncRegisterDatabaseExtension:storage]; -} - -#pragma mark - instance methods - -- (void)enqueueEnvelopeData:(NSData *)envelopeData - plaintextData:(NSData *_Nullable)plaintextData - wasReceivedByUD:(BOOL)wasReceivedByUD - transaction:(YapDatabaseReadWriteTransaction *)transaction -{ - if (envelopeData.length < 1) { - OWSFailDebug(@"Received an empty envelope."); - return; - } - OWSAssert(transaction); - - // We need to persist the decrypted envelope data ASAP to prevent data loss. - [self.processingQueue enqueueEnvelopeData:envelopeData - plaintextData:plaintextData - wasReceivedByUD:wasReceivedByUD - transaction:transaction]; - - // The new envelope won't be visible to the finder until this transaction commits, - // so drainQueue in the transaction completion. - [transaction addCompletionQueue:dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0) - completionBlock:^{ - [self.processingQueue drainQueue]; - }]; -} - -@end - -NS_ASSUME_NONNULL_END diff --git a/SignalUtilitiesKit/OWSBlockedPhoneNumbersMessage.h b/SignalUtilitiesKit/OWSBlockedPhoneNumbersMessage.h deleted file mode 100644 index 59cb02b07..000000000 --- a/SignalUtilitiesKit/OWSBlockedPhoneNumbersMessage.h +++ /dev/null @@ -1,19 +0,0 @@ -// -// Copyright (c) 2018 Open Whisper Systems. All rights reserved. -// - -#import - -NS_ASSUME_NONNULL_BEGIN - -@interface OWSBlockedPhoneNumbersMessage : OWSOutgoingSyncMessage - -- (instancetype)init NS_UNAVAILABLE; - -- (instancetype)initWithPhoneNumbers:(NSArray *)phoneNumbers - groupIds:(NSArray *)groupIds NS_DESIGNATED_INITIALIZER; -- (nullable instancetype)initWithCoder:(NSCoder *)coder NS_DESIGNATED_INITIALIZER; - -@end - -NS_ASSUME_NONNULL_END diff --git a/SignalUtilitiesKit/OWSBlockedPhoneNumbersMessage.m b/SignalUtilitiesKit/OWSBlockedPhoneNumbersMessage.m deleted file mode 100644 index a089a9aab..000000000 --- a/SignalUtilitiesKit/OWSBlockedPhoneNumbersMessage.m +++ /dev/null @@ -1,57 +0,0 @@ -// -// Copyright (c) 2018 Open Whisper Systems. All rights reserved. -// - -#import -#import - -NS_ASSUME_NONNULL_BEGIN - -@interface OWSBlockedPhoneNumbersMessage () - -@property (nonatomic, readonly) NSArray *phoneNumbers; -@property (nonatomic, readonly) NSArray *groupIds; - -@end - -@implementation OWSBlockedPhoneNumbersMessage - -- (nullable instancetype)initWithCoder:(NSCoder *)coder -{ - return [super initWithCoder:coder]; -} - -- (instancetype)initWithPhoneNumbers:(NSArray *)phoneNumbers groupIds:(NSArray *)groupIds -{ - self = [super init]; - if (!self) { - return self; - } - - _phoneNumbers = [phoneNumbers copy]; - _groupIds = [groupIds copy]; - - return self; -} - -- (nullable SSKProtoSyncMessageBuilder *)syncMessageBuilder -{ - SSKProtoSyncMessageBlockedBuilder *blockedBuilder = [SSKProtoSyncMessageBlocked builder]; - [blockedBuilder setNumbers:_phoneNumbers]; - [blockedBuilder setGroupIds:_groupIds]; - - NSError *error; - SSKProtoSyncMessageBlocked *_Nullable blockedProto = [blockedBuilder buildAndReturnError:&error]; - if (error || !blockedProto) { - OWSFailDebug(@"could not build protobuf: %@", error); - return nil; - } - - SSKProtoSyncMessageBuilder *syncMessageBuilder = [SSKProtoSyncMessage builder]; - [syncMessageBuilder setBlocked:blockedProto]; - return syncMessageBuilder; -} - -@end - -NS_ASSUME_NONNULL_END diff --git a/SignalUtilitiesKit/OWSBlockingManager.m b/SignalUtilitiesKit/OWSBlockingManager.m index 3bb675757..34a1767f4 100644 --- a/SignalUtilitiesKit/OWSBlockingManager.m +++ b/SignalUtilitiesKit/OWSBlockingManager.m @@ -6,8 +6,6 @@ #import "AppContext.h" #import "AppReadiness.h" #import "NSNotificationCenter+OWS.h" -#import "OWSBlockedPhoneNumbersMessage.h" -#import "OWSMessageSender.h" #import "OWSPrimaryStorage.h" #import "SSKEnvironment.h" #import "TSContactThread.h" @@ -83,13 +81,6 @@ NSString *const kOWSBlockingManager_SyncedBlockedGroupIdsKey = @"kOWSBlockingMan object:nil]; } -- (OWSMessageSender *)messageSender -{ - OWSAssertDebug(SSKEnvironment.shared.messageSender); - - return SSKEnvironment.shared.messageSender; -} - #pragma mark - - (BOOL)isThreadBlocked:(TSThread *)thread @@ -183,11 +174,7 @@ NSString *const kOWSBlockingManager_SyncedBlockedGroupIdsKey = @"kOWSBlockingMan - (BOOL)isRecipientIdBlocked:(NSString *)recipientId { - __block NSString *masterPublicKey; - [OWSPrimaryStorage.sharedManager.dbReadConnection readWithBlock:^(YapDatabaseReadTransaction * _Nonnull transaction) { - masterPublicKey = [LKDatabaseUtilities getMasterHexEncodedPublicKeyFor:recipientId in:transaction] ?: recipientId; - }]; - return [self.blockedPhoneNumbers containsObject:masterPublicKey]; + return [self.blockedPhoneNumbers containsObject:recipientId]; } #pragma mark - Group Blocking @@ -356,7 +343,6 @@ 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, @@ -383,12 +369,14 @@ NSString *const kOWSBlockingManager_SyncedBlockedGroupIdsKey = @"kOWSBlockingMan OWSLogInfo(@"retrying sync of block list"); [self sendBlockListSyncMessageWithPhoneNumbers:self.blockedPhoneNumbers groupIds:localBlockedGroupIds]; - */ } - (void)sendBlockListSyncMessageWithPhoneNumbers:(NSArray *)blockedPhoneNumbers groupIds:(NSArray *)blockedGroupIds { + // TODO TODO TODO + + /* OWSAssertDebug(blockedPhoneNumbers); OWSAssertDebug(blockedGroupIds); @@ -406,6 +394,7 @@ NSString *const kOWSBlockingManager_SyncedBlockedGroupIdsKey = @"kOWSBlockingMan failure:^(NSError *error) { OWSLogError(@"Failed to send blocked phone numbers sync message with error: %@", error); }]; + */ } /// Records the last block list which we successfully synced. diff --git a/SignalUtilitiesKit/OWSCensorshipConfiguration.h b/SignalUtilitiesKit/OWSCensorshipConfiguration.h deleted file mode 100644 index 4d1aac625..000000000 --- a/SignalUtilitiesKit/OWSCensorshipConfiguration.h +++ /dev/null @@ -1,35 +0,0 @@ -// -// Copyright (c) 2019 Open Whisper Systems. All rights reserved. -// - -#import - -NS_ASSUME_NONNULL_BEGIN - -@class AFSecurityPolicy; - -extern NSString *const OWSFrontingHost_GoogleEgypt; -extern NSString *const OWSFrontingHost_GoogleUAE; -extern NSString *const OWSFrontingHost_GoogleOman; -extern NSString *const OWSFrontingHost_GoogleQatar; - -@interface OWSCensorshipConfiguration : NSObject - -// returns nil if phone number is not known to be censored -+ (nullable instancetype)censorshipConfigurationWithPhoneNumber:(NSString *)e164PhoneNumber; - -// returns best censorship configuration for country code. Will return a default if one hasn't -// been specifically configured. -+ (instancetype)censorshipConfigurationWithCountryCode:(NSString *)countryCode; -+ (instancetype)defaultConfiguration; - -+ (BOOL)isCensoredPhoneNumber:(NSString *)e164PhoneNumber; - -@property (nonatomic, readonly) NSString *signalServiceReflectorHost; -@property (nonatomic, readonly) NSString *CDNReflectorHost; -@property (nonatomic, readonly) NSURL *domainFrontBaseURL; -@property (nonatomic, readonly) AFSecurityPolicy *domainFrontSecurityPolicy; - -@end - -NS_ASSUME_NONNULL_END diff --git a/SignalUtilitiesKit/OWSCensorshipConfiguration.m b/SignalUtilitiesKit/OWSCensorshipConfiguration.m deleted file mode 100644 index 6a59e715f..000000000 --- a/SignalUtilitiesKit/OWSCensorshipConfiguration.m +++ /dev/null @@ -1,247 +0,0 @@ -// -// Copyright (c) 2019 Open Whisper Systems. All rights reserved. -// - -#import "OWSCensorshipConfiguration.h" -#import "OWSCountryMetadata.h" -#import "OWSError.h" -#import "OWSPrimaryStorage.h" -#import "TSConstants.h" -#import -#import - -NS_ASSUME_NONNULL_BEGIN - -NSString *const OWSFrontingHost_GoogleEgypt = @"www.google.com.eg"; -NSString *const OWSFrontingHost_GoogleUAE = @"www.google.ae"; -NSString *const OWSFrontingHost_GoogleOman = @"www.google.com.om"; -NSString *const OWSFrontingHost_GoogleQatar = @"www.google.com.qa"; -NSString *const OWSFrontingHost_Default = @"www.google.com"; - -@implementation OWSCensorshipConfiguration - -// returns nil if phone number is not known to be censored -+ (nullable instancetype)censorshipConfigurationWithPhoneNumber:(NSString *)e164PhoneNumber -{ - NSString *countryCode = [self censoredCountryCodeWithPhoneNumber:e164PhoneNumber]; - if (countryCode.length == 0) { - return nil; - } - - return [self censorshipConfigurationWithCountryCode:countryCode]; -} - -// returns best censorship configuration for country code. Will return a default if one hasn't -// been specifically configured. -+ (instancetype)censorshipConfigurationWithCountryCode:(NSString *)countryCode -{ - OWSCountryMetadata *countryMetadadata = [OWSCountryMetadata countryMetadataForCountryCode:countryCode]; - OWSAssertDebug(countryMetadadata); - - NSString *_Nullable specifiedDomain = countryMetadadata.frontingDomain; - if (specifiedDomain.length == 0) { - return self.defaultConfiguration; - } - - NSString *frontingURLString = [NSString stringWithFormat:@"https://%@", specifiedDomain]; - NSURL *_Nullable baseURL = [NSURL URLWithString:frontingURLString]; - if (baseURL == nil) { - OWSFailDebug(@"baseURL was unexpectedly nil with specifiedDomain: %@", specifiedDomain); - return self.defaultConfiguration; - } - AFSecurityPolicy *securityPolicy = [self securityPolicyForDomain:specifiedDomain]; - OWSAssertDebug(securityPolicy); - - return [[OWSCensorshipConfiguration alloc] initWithDomainFrontBaseURL:baseURL securityPolicy:securityPolicy]; -} - -+ (instancetype)defaultConfiguration -{ - NSString *frontingURLString = [NSString stringWithFormat:@"https://%@", OWSFrontingHost_Default]; - NSURL *baseURL = [NSURL URLWithString:frontingURLString]; - AFSecurityPolicy *securityPolicy = [self securityPolicyForDomain:OWSFrontingHost_Default]; - - return [[OWSCensorshipConfiguration alloc] initWithDomainFrontBaseURL:baseURL securityPolicy:securityPolicy]; -} - -- (instancetype)initWithDomainFrontBaseURL:(NSURL *)domainFrontBaseURL securityPolicy:(AFSecurityPolicy *)securityPolicy -{ - OWSAssertDebug(domainFrontBaseURL); - OWSAssertDebug(securityPolicy); - - self = [super init]; - if (!self) { - return self; - } - - _domainFrontBaseURL = domainFrontBaseURL; - _domainFrontSecurityPolicy = securityPolicy; - - return self; -} - -// MARK: Public Getters - -- (NSString *)signalServiceReflectorHost -{ - return textSecureServiceReflectorHost; -} - -- (NSString *)CDNReflectorHost -{ - return textSecureCDNReflectorHost; -} - -// MARK: Util - -+ (NSDictionary *)censoredCountryCodes -{ - // The set of countries for which domain fronting should be automatically enabled. - // - // If you want to use a domain front other than the default, specify the domain front - // in OWSCountryMetadata, and ensure we have a Security Policy for that domain in - // `securityPolicyForDomain:` - return @{ - // Egypt - @"+20" : @"EG", - // Oman - @"+968" : @"OM", - // Qatar - @"+974" : @"QA", - // UAE - @"+971" : @"AE", - }; -} - -+ (BOOL)isCensoredPhoneNumber:(NSString *)e164PhoneNumber; -{ - return [self censoredCountryCodeWithPhoneNumber:e164PhoneNumber].length > 0; -} - -// Returns nil if the phone number is not known to be censored -+ (nullable NSString *)censoredCountryCodeWithPhoneNumber:(NSString *)e164PhoneNumber -{ - NSDictionary *censoredCountryCodes = self.censoredCountryCodes; - - for (NSString *callingCode in censoredCountryCodes) { - if ([e164PhoneNumber hasPrefix:callingCode]) { - return censoredCountryCodes[callingCode]; - } - } - - return nil; -} - -#pragma mark - Reflector Pinning Policy - -// When using censorship circumvention, we pin to the fronted domain host. -// Adding a new domain front entails adding a corresponding AFSecurityPolicy -// and pinning to it's CA. -// If the security policy requires new certificates, include them in the SSK bundle -+ (AFSecurityPolicy *)securityPolicyForDomain:(NSString *)domain -{ - if ([domain isEqualToString:OWSFrontingHost_GoogleEgypt]) { - return self.googlePinningPolicy; - } else if ([domain isEqualToString:OWSFrontingHost_GoogleQatar]) { - return self.googlePinningPolicy; - } else if ([domain isEqualToString:OWSFrontingHost_GoogleOman]) { - return self.googlePinningPolicy; - } else if ([domain isEqualToString:OWSFrontingHost_GoogleUAE]) { - return self.googlePinningPolicy; - } else { - OWSFailDebug(@"unknown pinning domain."); - return self.googlePinningPolicy; - } -} - -+ (AFSecurityPolicy *)pinningPolicyWithCertNames:(NSArray *)certNames -{ - NSMutableSet *certificates = [NSMutableSet new]; - for (NSString *certName in certNames) { - NSError *error; - NSData *certData = [self certificateDataWithName:certName error:&error]; - if (error) { - OWSFail(@"reading data for certificate: %@ failed with error: %@", certName, error); - } - - if (!certData) { - OWSFail(@"reading data for certificate: %@ failed with error: %@", certName, error); - } - [certificates addObject:certData]; - } - - return [AFSecurityPolicy policyWithPinningMode:AFSSLPinningModeCertificate withPinnedCertificates:certificates]; -} - -+ (nullable NSData *)certificateDataWithName:(NSString *)name error:(NSError **)error -{ - if (!name.length) { - NSString *failureDescription = [NSString stringWithFormat:@"%@ expected name with length > 0", self.logTag]; - *error = OWSErrorMakeAssertionError(failureDescription); - return nil; - } - - NSBundle *bundle = [NSBundle bundleForClass:self.class]; - NSString *path = [bundle pathForResource:name ofType:@"crt"]; - if (![[NSFileManager defaultManager] fileExistsAtPath:path]) { - NSString *failureDescription = - [NSString stringWithFormat:@"%@ Missing certificate for name: %@", self.logTag, name]; - *error = OWSErrorMakeAssertionError(failureDescription); - return nil; - } - - NSData *_Nullable certData = [NSData dataWithContentsOfFile:path options:0 error:error]; - - if (*error != nil) { - OWSFailDebug(@"Failed to read cert file with path: %@", path); - return nil; - } - - if (certData.length == 0) { - OWSFailDebug(@"empty certData for name: %@", name); - return nil; - } - - OWSLogVerbose(@"read cert data with name: %@ length: %lu", name, (unsigned long)certData.length); - return certData; -} - -+ (AFSecurityPolicy *)yahooViewPinningPolicy_deprecated -{ - static AFSecurityPolicy *securityPolicy = nil; - static dispatch_once_t onceToken; - dispatch_once(&onceToken, ^{ - // DigiCertGlobalRootG2 - view.yahoo.com - NSArray *certNames = @[ @"DigiCertSHA2HighAssuranceServerCA" ]; - securityPolicy = [self pinningPolicyWithCertNames:certNames]; - }); - return securityPolicy; -} - -+ (AFSecurityPolicy *)souqPinningPolicy_deprecated -{ - static AFSecurityPolicy *securityPolicy = nil; - static dispatch_once_t onceToken; - dispatch_once(&onceToken, ^{ - // SFSRootCAG2 - cms.souqcdn.com - NSArray *certNames = @[ @"SFSRootCAG2" ]; - securityPolicy = [self pinningPolicyWithCertNames:certNames]; - }); - return securityPolicy; -} - -+ (AFSecurityPolicy *)googlePinningPolicy -{ - static AFSecurityPolicy *securityPolicy = nil; - static dispatch_once_t onceToken; - dispatch_once(&onceToken, ^{ - // GIAG2 cert plus root certs from pki.goog - NSArray *certNames = @[ @"GIAG2", @"GSR2", @"GSR4", @"GTSR1", @"GTSR2", @"GTSR3", @"GTSR4" ]; - securityPolicy = [self pinningPolicyWithCertNames:certNames]; - }); - return securityPolicy; -} - -@end - -NS_ASSUME_NONNULL_END diff --git a/SignalUtilitiesKit/OWSContact+Private.h b/SignalUtilitiesKit/OWSContact+Private.h deleted file mode 100644 index 6f922e60b..000000000 --- a/SignalUtilitiesKit/OWSContact+Private.h +++ /dev/null @@ -1,60 +0,0 @@ -// -// Copyright (c) 2018 Open Whisper Systems. All rights reserved. -// - -#import - -NS_ASSUME_NONNULL_BEGIN - -// These private interfaces expose setter accessors to facilitate -// construction of fake messages, etc. -@interface OWSContactPhoneNumber (Private) - -@property (nonatomic) OWSContactPhoneType phoneType; -@property (nonatomic, nullable) NSString *label; - -@property (nonatomic) NSString *phoneNumber; - -@end - -#pragma mark - - -@interface OWSContactEmail (Private) - -@property (nonatomic) OWSContactEmailType emailType; -@property (nonatomic, nullable) NSString *label; - -@property (nonatomic) NSString *email; - -@end - -#pragma mark - - -@interface OWSContactAddress (Private) - -@property (nonatomic) OWSContactAddressType addressType; -@property (nonatomic, nullable) NSString *label; - -@property (nonatomic, nullable) NSString *street; -@property (nonatomic, nullable) NSString *pobox; -@property (nonatomic, nullable) NSString *neighborhood; -@property (nonatomic, nullable) NSString *city; -@property (nonatomic, nullable) NSString *region; -@property (nonatomic, nullable) NSString *postcode; -@property (nonatomic, nullable) NSString *country; - -@end - -#pragma mark - - -@interface OWSContact (Private) - -@property (nonatomic) NSArray *phoneNumbers; -@property (nonatomic) NSArray *emails; -@property (nonatomic) NSArray *addresses; - -@property (nonatomic) BOOL isProfileAvatar; - -@end - -NS_ASSUME_NONNULL_END diff --git a/SignalUtilitiesKit/OWSContact.h b/SignalUtilitiesKit/OWSContact.h deleted file mode 100644 index ca76dd5ca..000000000 --- a/SignalUtilitiesKit/OWSContact.h +++ /dev/null @@ -1,183 +0,0 @@ -// -// Copyright (c) 2018 Open Whisper Systems. All rights reserved. -// - -#import -#import - -NS_ASSUME_NONNULL_BEGIN - -@class CNContact; -@class OWSAttachmentInfo; -@class SSKProtoDataMessage; -@class SSKProtoDataMessageContact; -@class TSAttachment; -@class TSAttachmentStream; -@class YapDatabaseReadTransaction; -@class YapDatabaseReadWriteTransaction; - -extern BOOL kIsSendingContactSharesEnabled; - -typedef NS_ENUM(NSUInteger, OWSContactPhoneType) { - OWSContactPhoneType_Home = 1, - OWSContactPhoneType_Mobile, - OWSContactPhoneType_Work, - OWSContactPhoneType_Custom, -}; - -NSString *NSStringForContactPhoneType(OWSContactPhoneType value); - -@protocol OWSContactField - -- (BOOL)ows_isValid; - -- (NSString *)localizedLabel; - -@end - -#pragma mark - - -@interface OWSContactPhoneNumber : MTLModel - -@property (nonatomic, readonly) OWSContactPhoneType phoneType; -// Applies in the OWSContactPhoneType_Custom case. -@property (nonatomic, readonly, nullable) NSString *label; - -@property (nonatomic, readonly) NSString *phoneNumber; - -- (nullable NSString *)tryToConvertToE164; - -@end - -#pragma mark - - -typedef NS_ENUM(NSUInteger, OWSContactEmailType) { - OWSContactEmailType_Home = 1, - OWSContactEmailType_Mobile, - OWSContactEmailType_Work, - OWSContactEmailType_Custom, -}; - -NSString *NSStringForContactEmailType(OWSContactEmailType value); - -@interface OWSContactEmail : MTLModel - -@property (nonatomic, readonly) OWSContactEmailType emailType; -// Applies in the OWSContactEmailType_Custom case. -@property (nonatomic, readonly, nullable) NSString *label; - -@property (nonatomic, readonly) NSString *email; - -@end - -#pragma mark - - -typedef NS_ENUM(NSUInteger, OWSContactAddressType) { - OWSContactAddressType_Home = 1, - OWSContactAddressType_Work, - OWSContactAddressType_Custom, -}; - -NSString *NSStringForContactAddressType(OWSContactAddressType value); - -@interface OWSContactAddress : MTLModel - -@property (nonatomic, readonly) OWSContactAddressType addressType; -// Applies in the OWSContactAddressType_Custom case. -@property (nonatomic, readonly, nullable) NSString *label; - -@property (nonatomic, readonly, nullable) NSString *street; -@property (nonatomic, readonly, nullable) NSString *pobox; -@property (nonatomic, readonly, nullable) NSString *neighborhood; -@property (nonatomic, readonly, nullable) NSString *city; -@property (nonatomic, readonly, nullable) NSString *region; -@property (nonatomic, readonly, nullable) NSString *postcode; -@property (nonatomic, readonly, nullable) NSString *country; - -@end - -#pragma mark - - -@interface OWSContactName : MTLModel - -// The "name parts". -@property (nonatomic, nullable) NSString *givenName; -@property (nonatomic, nullable) NSString *familyName; -@property (nonatomic, nullable) NSString *nameSuffix; -@property (nonatomic, nullable) NSString *namePrefix; -@property (nonatomic, nullable) NSString *middleName; - -@property (nonatomic, nullable) NSString *organizationName; - -@property (nonatomic) NSString *displayName; - -// Returns true if any of the name parts (which doesn't include -// organization name) is non-empty. -- (BOOL)hasAnyNamePart; - -@end - -#pragma mark - - -@interface OWSContact : MTLModel - -@property (nonatomic) OWSContactName *name; - -@property (nonatomic, readonly) NSArray *phoneNumbers; -@property (nonatomic, readonly) NSArray *emails; -@property (nonatomic, readonly) NSArray *addresses; - -@property (nonatomic, readonly, nullable) NSString *avatarAttachmentId; -- (nullable TSAttachment *)avatarAttachmentWithTransaction:(YapDatabaseReadTransaction *)transaction; -- (void)removeAvatarAttachmentWithTransaction:(YapDatabaseReadWriteTransaction *)transaction; - -- (void)saveAvatarImage:(UIImage *)image transaction:(YapDatabaseReadWriteTransaction *)transaction; -// "Profile" avatars should _not_ be saved to device contacts. -@property (nonatomic, readonly) BOOL isProfileAvatar; - -- (instancetype)init NS_UNAVAILABLE; - -- (void)normalize; - -- (BOOL)ows_isValid; - -- (NSString *)debugDescription; - -#pragma mark - Creation and Derivation - -- (OWSContact *)newContactWithName:(OWSContactName *)name; - -- (OWSContact *)copyContactWithName:(OWSContactName *)name; - -#pragma mark - Phone Numbers and Recipient IDs - -- (NSArray *)systemContactsWithSignalAccountPhoneNumbers:(id)contactsManager - NS_SWIFT_NAME(systemContactsWithSignalAccountPhoneNumbers(_:)); -- (NSArray *)systemContactPhoneNumbers:(id)contactsManager - NS_SWIFT_NAME(systemContactPhoneNumbers(_:)); -- (NSArray *)e164PhoneNumbers NS_SWIFT_NAME(e164PhoneNumbers()); - -@end - -#pragma mark - - -// TODO: Move to separate source file, rename to OWSContactConversion. -@interface OWSContacts : NSObject - -#pragma mark - System Contact Conversion - -// `contactForSystemContact` does *not* handle avatars. That must be delt with by the caller -+ (nullable OWSContact *)contactForSystemContact:(CNContact *)systemContact; - -+ (nullable CNContact *)systemContactForContact:(OWSContact *)contact imageData:(nullable NSData *)imageData; - -#pragma mark - Proto Serialization - -+ (nullable SSKProtoDataMessageContact *)protoForContact:(OWSContact *)contact; - -+ (nullable OWSContact *)contactForDataMessage:(SSKProtoDataMessage *)dataMessage - transaction:(YapDatabaseReadWriteTransaction *)transaction; - -@end - -NS_ASSUME_NONNULL_END diff --git a/SignalUtilitiesKit/OWSContact.m b/SignalUtilitiesKit/OWSContact.m deleted file mode 100644 index 9c5f9479a..000000000 --- a/SignalUtilitiesKit/OWSContact.m +++ /dev/null @@ -1,1126 +0,0 @@ -// -// Copyright (c) 2019 Open Whisper Systems. All rights reserved. -// - -#import "OWSContact.h" -#import "Contact.h" -#import "MimeTypeUtil.h" -#import "NSString+SSK.h" -#import "OWSContact+Private.h" -#import "PhoneNumber.h" -#import "TSAttachment.h" -#import "TSAttachmentPointer.h" -#import "TSAttachmentStream.h" -#import -#import - -@import Contacts; - -NS_ASSUME_NONNULL_BEGIN - -// NOTE: When changing the value of this feature flag, you also need -// to update the filtering in the SAE's info.plist. -BOOL kIsSendingContactSharesEnabled = YES; - -NSString *NSStringForContactPhoneType(OWSContactPhoneType value) -{ - switch (value) { - case OWSContactPhoneType_Home: - return @"Home"; - case OWSContactPhoneType_Mobile: - return @"Mobile"; - case OWSContactPhoneType_Work: - return @"Work"; - case OWSContactPhoneType_Custom: - return @"Custom"; - } -} - -@interface OWSContactPhoneNumber () - -@property (nonatomic) OWSContactPhoneType phoneType; -@property (nonatomic, nullable) NSString *label; - -@property (nonatomic) NSString *phoneNumber; - -@end - -#pragma mark - - -@implementation OWSContactPhoneNumber - -- (BOOL)ows_isValid -{ - if (self.phoneNumber.ows_stripped.length < 1) { - OWSLogWarn(@"invalid phone number: %@.", self.phoneNumber); - return NO; - } - return YES; -} - -- (NSString *)localizedLabel -{ - switch (self.phoneType) { - case OWSContactPhoneType_Home: - return [CNLabeledValue localizedStringForLabel:CNLabelHome]; - case OWSContactPhoneType_Mobile: - return [CNLabeledValue localizedStringForLabel:CNLabelPhoneNumberMobile]; - case OWSContactPhoneType_Work: - return [CNLabeledValue localizedStringForLabel:CNLabelWork]; - default: - if (self.label.ows_stripped.length < 1) { - return NSLocalizedString(@"CONTACT_PHONE", @"Label for a contact's phone number."); - } - return self.label.ows_stripped; - } -} - -- (NSString *)debugDescription -{ - NSMutableString *result = [NSMutableString new]; - [result appendFormat:@"[Phone Number: %@, ", NSStringForContactPhoneType(self.phoneType)]; - - if (self.label.length > 0) { - [result appendFormat:@"label: %@, ", self.label]; - } - if (self.phoneNumber.length > 0) { - [result appendFormat:@"phoneNumber: %@, ", self.phoneNumber]; - } - - [result appendString:@"]"]; - return result; -} - -- (nullable NSString *)tryToConvertToE164 -{ - PhoneNumber *_Nullable parsedPhoneNumber; - parsedPhoneNumber = [PhoneNumber tryParsePhoneNumberFromE164:self.phoneNumber]; - if (!parsedPhoneNumber) { - parsedPhoneNumber = [PhoneNumber tryParsePhoneNumberFromUserSpecifiedText:self.phoneNumber]; - } - if (parsedPhoneNumber) { - return parsedPhoneNumber.toE164; - } - return nil; -} - -@end - -#pragma mark - - -NSString *NSStringForContactEmailType(OWSContactEmailType value) -{ - switch (value) { - case OWSContactEmailType_Home: - return @"Home"; - case OWSContactEmailType_Mobile: - return @"Mobile"; - case OWSContactEmailType_Work: - return @"Work"; - case OWSContactEmailType_Custom: - return @"Custom"; - } -} - -@interface OWSContactEmail () - -@property (nonatomic) OWSContactEmailType emailType; -@property (nonatomic, nullable) NSString *label; - -@property (nonatomic) NSString *email; - -@end - -#pragma mark - - -@implementation OWSContactEmail - -- (BOOL)ows_isValid -{ - if (self.email.ows_stripped.length < 1) { - OWSLogWarn(@"invalid email: %@.", self.email); - return NO; - } - return YES; -} - -- (NSString *)localizedLabel -{ - switch (self.emailType) { - case OWSContactEmailType_Home: - return [CNLabeledValue localizedStringForLabel:CNLabelHome]; - case OWSContactEmailType_Mobile: - return [CNLabeledValue localizedStringForLabel:CNLabelPhoneNumberMobile]; - case OWSContactEmailType_Work: - return [CNLabeledValue localizedStringForLabel:CNLabelWork]; - default: - if (self.label.ows_stripped.length < 1) { - return NSLocalizedString(@"CONTACT_EMAIL", @"Label for a contact's email address."); - } - return self.label.ows_stripped; - } -} - -- (NSString *)debugDescription -{ - NSMutableString *result = [NSMutableString new]; - [result appendFormat:@"[Email: %@, ", NSStringForContactEmailType(self.emailType)]; - - if (self.label.length > 0) { - [result appendFormat:@"label: %@, ", self.label]; - } - if (self.email.length > 0) { - [result appendFormat:@"email: %@, ", self.email]; - } - - [result appendString:@"]"]; - return result; -} - -@end - -#pragma mark - - -NSString *NSStringForContactAddressType(OWSContactAddressType value) -{ - switch (value) { - case OWSContactAddressType_Home: - return @"Home"; - case OWSContactAddressType_Work: - return @"Work"; - case OWSContactAddressType_Custom: - return @"Custom"; - } -} -@interface OWSContactAddress () - -@property (nonatomic) OWSContactAddressType addressType; -@property (nonatomic, nullable) NSString *label; - -@property (nonatomic, nullable) NSString *street; -@property (nonatomic, nullable) NSString *pobox; -@property (nonatomic, nullable) NSString *neighborhood; -@property (nonatomic, nullable) NSString *city; -@property (nonatomic, nullable) NSString *region; -@property (nonatomic, nullable) NSString *postcode; -@property (nonatomic, nullable) NSString *country; - -@end - -#pragma mark - - -@implementation OWSContactAddress - -- (BOOL)ows_isValid -{ - if (self.street.ows_stripped.length < 1 && self.pobox.ows_stripped.length < 1 - && self.neighborhood.ows_stripped.length < 1 && self.city.ows_stripped.length < 1 - && self.region.ows_stripped.length < 1 && self.postcode.ows_stripped.length < 1 - && self.country.ows_stripped.length < 1) { - OWSLogWarn(@"invalid address; empty."); - return NO; - } - return YES; -} - -- (NSString *)localizedLabel -{ - switch (self.addressType) { - case OWSContactAddressType_Home: - return [CNLabeledValue localizedStringForLabel:CNLabelHome]; - case OWSContactAddressType_Work: - return [CNLabeledValue localizedStringForLabel:CNLabelWork]; - default: - if (self.label.ows_stripped.length < 1) { - return NSLocalizedString(@"CONTACT_ADDRESS", @"Label for a contact's postal address."); - } - return self.label.ows_stripped; - } -} - -- (NSString *)debugDescription -{ - NSMutableString *result = [NSMutableString new]; - [result appendFormat:@"[Address: %@, ", NSStringForContactAddressType(self.addressType)]; - - if (self.label.length > 0) { - [result appendFormat:@"label: %@, ", self.label]; - } - if (self.street.length > 0) { - [result appendFormat:@"street: %@, ", self.street]; - } - if (self.pobox.length > 0) { - [result appendFormat:@"pobox: %@, ", self.pobox]; - } - if (self.neighborhood.length > 0) { - [result appendFormat:@"neighborhood: %@, ", self.neighborhood]; - } - if (self.city.length > 0) { - [result appendFormat:@"city: %@, ", self.city]; - } - if (self.region.length > 0) { - [result appendFormat:@"region: %@, ", self.region]; - } - if (self.postcode.length > 0) { - [result appendFormat:@"postcode: %@, ", self.postcode]; - } - if (self.country.length > 0) { - [result appendFormat:@"country: %@, ", self.country]; - } - - [result appendString:@"]"]; - return result; -} - -@end - -#pragma mark - - -@implementation OWSContactName - -- (NSString *)logDescription -{ - NSMutableString *result = [NSMutableString new]; - [result appendString:@"["]; - - if (self.givenName.length > 0) { - [result appendFormat:@"givenName: %@, ", self.givenName]; - } - if (self.familyName.length > 0) { - [result appendFormat:@"familyName: %@, ", self.familyName]; - } - if (self.middleName.length > 0) { - [result appendFormat:@"middleName: %@, ", self.middleName]; - } - if (self.namePrefix.length > 0) { - [result appendFormat:@"namePrefix: %@, ", self.namePrefix]; - } - if (self.nameSuffix.length > 0) { - [result appendFormat:@"nameSuffix: %@, ", self.nameSuffix]; - } - if (self.displayName.length > 0) { - [result appendFormat:@"displayName: %@, ", self.displayName]; - } - - [result appendString:@"]"]; - return result; -} - -- (NSString *)displayName -{ - [self ensureDisplayName]; - - if (_displayName.length < 1) { - OWSFailDebug(@"could not derive a valid display name."); - return NSLocalizedString(@"CONTACT_WITHOUT_NAME", @"Indicates that a contact has no name."); - } - return _displayName; -} - -- (void)ensureDisplayName -{ - if (_displayName.length < 1) { - CNContact *_Nullable cnContact = [self systemContactForName]; - _displayName = [CNContactFormatter stringFromContact:cnContact style:CNContactFormatterStyleFullName]; - } - if (_displayName.length < 1) { - // Fall back to using the organization name. - _displayName = self.organizationName; - } -} - -- (void)updateDisplayName -{ - _displayName = nil; - - [self ensureDisplayName]; -} - -- (nullable CNContact *)systemContactForName -{ - CNMutableContact *systemContact = [CNMutableContact new]; - systemContact.givenName = self.givenName.ows_stripped; - systemContact.middleName = self.middleName.ows_stripped; - systemContact.familyName = self.familyName.ows_stripped; - systemContact.namePrefix = self.namePrefix.ows_stripped; - systemContact.nameSuffix = self.nameSuffix.ows_stripped; - // We don't need to set display name, it's implicit for system contacts. - systemContact.organizationName = self.organizationName.ows_stripped; - return systemContact; -} - -- (BOOL)hasAnyNamePart -{ - return (self.givenName.ows_stripped.length > 0 || self.middleName.ows_stripped.length > 0 - || self.familyName.ows_stripped.length > 0 || self.namePrefix.ows_stripped.length > 0 - || self.nameSuffix.ows_stripped.length > 0); -} - -@end - -#pragma mark - - -@interface OWSContact () - -@property (nonatomic) NSArray *phoneNumbers; -@property (nonatomic) NSArray *emails; -@property (nonatomic) NSArray *addresses; - -@property (nonatomic, nullable) NSString *avatarAttachmentId; -@property (nonatomic) BOOL isProfileAvatar; - -@property (nonatomic, nullable) NSArray *e164PhoneNumbersCached; - -@end - -#pragma mark - - -@implementation OWSContact - -- (instancetype)init -{ - if (self = [super init]) { - _name = [OWSContactName new]; - _phoneNumbers = @[]; - _emails = @[]; - _addresses = @[]; - } - - return self; -} - -- (void)normalize -{ - self.phoneNumbers = [self.phoneNumbers - filteredArrayUsingPredicate:[NSPredicate predicateWithBlock:^BOOL(OWSContactPhoneNumber *value, - NSDictionary *_Nullable bindings) { - return value.ows_isValid; - }]]; - self.emails = [self.emails filteredArrayUsingPredicate:[NSPredicate predicateWithBlock:^BOOL(OWSContactEmail *value, - NSDictionary *_Nullable bindings) { - return value.ows_isValid; - }]]; - self.addresses = - [self.addresses filteredArrayUsingPredicate:[NSPredicate predicateWithBlock:^BOOL(OWSContactAddress *value, - NSDictionary *_Nullable bindings) { - return value.ows_isValid; - }]]; -} - -- (BOOL)ows_isValid -{ - if (self.name.displayName.ows_stripped.length < 1) { - OWSLogWarn(@"invalid contact; no display name."); - return NO; - } - BOOL hasValue = NO; - for (OWSContactPhoneNumber *phoneNumber in self.phoneNumbers) { - if (!phoneNumber.ows_isValid) { - return NO; - } - hasValue = YES; - } - for (OWSContactEmail *email in self.emails) { - if (!email.ows_isValid) { - return NO; - } - hasValue = YES; - } - for (OWSContactAddress *address in self.addresses) { - if (!address.ows_isValid) { - return NO; - } - hasValue = YES; - } - return hasValue; -} - -- (NSString *)debugDescription -{ - NSMutableString *result = [NSMutableString new]; - [result appendString:@"["]; - - [result appendFormat:@"%@, ", self.name.logDescription]; - - for (OWSContactPhoneNumber *phoneNumber in self.phoneNumbers) { - [result appendFormat:@"%@, ", phoneNumber.debugDescription]; - } - for (OWSContactEmail *email in self.emails) { - [result appendFormat:@"%@, ", email.debugDescription]; - } - for (OWSContactAddress *address in self.addresses) { - [result appendFormat:@"%@, ", address.debugDescription]; - } - - [result appendString:@"]"]; - return result; -} - -- (OWSContact *)newContactWithName:(OWSContactName *)name -{ - OWSAssertDebug(name); - - OWSContact *newContact = [OWSContact new]; - - newContact.name = name; - - [name updateDisplayName]; - - return newContact; -} - -- (OWSContact *)copyContactWithName:(OWSContactName *)name -{ - OWSAssertDebug(name); - - OWSContact *contactCopy = [self copy]; - - contactCopy.name = name; - - [name updateDisplayName]; - - return contactCopy; -} - -#pragma mark - Avatar - -- (nullable TSAttachment *)avatarAttachmentWithTransaction:(YapDatabaseReadTransaction *)transaction -{ - return [TSAttachment fetchObjectWithUniqueID:self.avatarAttachmentId transaction:transaction]; -} - -- (void)saveAvatarImage:(UIImage *)image transaction:(YapDatabaseReadWriteTransaction *)transaction -{ - NSData *imageData = UIImageJPEGRepresentation(image, (CGFloat)0.9); - - TSAttachmentStream *attachmentStream = [[TSAttachmentStream alloc] initWithContentType:OWSMimeTypeImageJpeg - byteCount:(UInt32)imageData.length - sourceFilename:nil - caption:nil - albumMessageId:nil]; - - NSError *error; - BOOL success = [attachmentStream writeData:imageData error:&error]; - OWSAssertDebug(success && !error); - - [attachmentStream saveWithTransaction:transaction]; - self.avatarAttachmentId = attachmentStream.uniqueId; -} - -- (void)removeAvatarAttachmentWithTransaction:(YapDatabaseReadWriteTransaction *)transaction -{ - TSAttachment *_Nullable attachment = - [TSAttachment fetchObjectWithUniqueID:self.avatarAttachmentId transaction:transaction]; - [attachment removeWithTransaction:transaction]; -} - -#pragma mark - Phone Numbers and Recipient IDs - -- (NSArray *)systemContactsWithSignalAccountPhoneNumbers:(id)contactsManager -{ - OWSAssertDebug(contactsManager); - - return [self.e164PhoneNumbers - filteredArrayUsingPredicate:[NSPredicate predicateWithBlock:^BOOL(NSString *_Nullable recipientId, - NSDictionary *_Nullable bindings) { - return [contactsManager isSystemContactWithSignalAccount:recipientId]; - }]]; -} - -- (NSArray *)systemContactPhoneNumbers:(id)contactsManager -{ - OWSAssertDebug(contactsManager); - - return [self.e164PhoneNumbers - filteredArrayUsingPredicate:[NSPredicate predicateWithBlock:^BOOL(NSString *_Nullable recipientId, - NSDictionary *_Nullable bindings) { - return [contactsManager isSystemContact:recipientId]; - }]]; -} - -- (NSArray *)e164PhoneNumbers -{ - if (self.e164PhoneNumbersCached) { - return self.e164PhoneNumbersCached; - } - NSMutableArray *e164PhoneNumbers = [NSMutableArray new]; - for (OWSContactPhoneNumber *phoneNumber in self.phoneNumbers) { - PhoneNumber *_Nullable parsedPhoneNumber; - parsedPhoneNumber = [PhoneNumber tryParsePhoneNumberFromE164:phoneNumber.phoneNumber]; - if (!parsedPhoneNumber) { - parsedPhoneNumber = [PhoneNumber tryParsePhoneNumberFromUserSpecifiedText:phoneNumber.phoneNumber]; - } - if (parsedPhoneNumber) { - [e164PhoneNumbers addObject:parsedPhoneNumber.toE164]; - } - } - self.e164PhoneNumbersCached = e164PhoneNumbers; - return e164PhoneNumbers; -} - -@end - -#pragma mark - - -@implementation OWSContacts - -#pragma mark - System Contact Conversion - -// `contactForSystemContact` does *not* handle avatars. That must be delt with by the caller -+ (nullable OWSContact *)contactForSystemContact:(CNContact *)systemContact -{ - if (!systemContact) { - OWSFailDebug(@"Missing contact."); - return nil; - } - - OWSContact *contact = [OWSContact new]; - - OWSContactName *contactName = [OWSContactName new]; - contactName.givenName = systemContact.givenName.ows_stripped; - contactName.middleName = systemContact.middleName.ows_stripped; - contactName.familyName = systemContact.familyName.ows_stripped; - contactName.namePrefix = systemContact.namePrefix.ows_stripped; - contactName.nameSuffix = systemContact.nameSuffix.ows_stripped; - contactName.organizationName = systemContact.organizationName.ows_stripped; - [contactName ensureDisplayName]; - contact.name = contactName; - - NSMutableArray *phoneNumbers = [NSMutableArray new]; - for (CNLabeledValue *phoneNumberField in systemContact.phoneNumbers) { - OWSContactPhoneNumber *phoneNumber = [OWSContactPhoneNumber new]; - - // Make a best effort to parse the phone number to e164. - NSString *unparsedPhoneNumber = phoneNumberField.value.stringValue; - PhoneNumber *_Nullable parsedPhoneNumber; - parsedPhoneNumber = [PhoneNumber tryParsePhoneNumberFromE164:unparsedPhoneNumber]; - if (!parsedPhoneNumber) { - parsedPhoneNumber = [PhoneNumber tryParsePhoneNumberFromUserSpecifiedText:unparsedPhoneNumber]; - } - if (parsedPhoneNumber) { - phoneNumber.phoneNumber = parsedPhoneNumber.toE164; - } else { - phoneNumber.phoneNumber = unparsedPhoneNumber; - } - - if ([phoneNumberField.label isEqualToString:CNLabelHome]) { - phoneNumber.phoneType = OWSContactPhoneType_Home; - } else if ([phoneNumberField.label isEqualToString:CNLabelWork]) { - phoneNumber.phoneType = OWSContactPhoneType_Work; - } else if ([phoneNumberField.label isEqualToString:CNLabelPhoneNumberMobile]) { - phoneNumber.phoneType = OWSContactPhoneType_Mobile; - } else { - phoneNumber.phoneType = OWSContactPhoneType_Custom; - phoneNumber.label = [Contact localizedStringForCNLabel:phoneNumberField.label]; - } - [phoneNumbers addObject:phoneNumber]; - } - contact.phoneNumbers = phoneNumbers; - - NSMutableArray *emails = [NSMutableArray new]; - for (CNLabeledValue *emailField in systemContact.emailAddresses) { - OWSContactEmail *email = [OWSContactEmail new]; - email.email = emailField.value; - if ([emailField.label isEqualToString:CNLabelHome]) { - email.emailType = OWSContactEmailType_Home; - } else if ([emailField.label isEqualToString:CNLabelWork]) { - email.emailType = OWSContactEmailType_Work; - } else { - email.emailType = OWSContactEmailType_Custom; - email.label = [Contact localizedStringForCNLabel:emailField.label]; - } - [emails addObject:email]; - } - contact.emails = emails; - - NSMutableArray *addresses = [NSMutableArray new]; - for (CNLabeledValue *addressField in systemContact.postalAddresses) { - OWSContactAddress *address = [OWSContactAddress new]; - address.street = addressField.value.street; - // TODO: Is this the correct mapping? - // address.neighborhood = addressField.value.subLocality; - address.city = addressField.value.city; - // TODO: Is this the correct mapping? - // address.region = addressField.value.subAdministrativeArea; - address.region = addressField.value.state; - address.postcode = addressField.value.postalCode; - // TODO: Should we be using 2-letter codes, 3-letter codes or names? - address.country = addressField.value.ISOCountryCode; - - if ([addressField.label isEqualToString:CNLabelHome]) { - address.addressType = OWSContactAddressType_Home; - } else if ([addressField.label isEqualToString:CNLabelWork]) { - address.addressType = OWSContactAddressType_Work; - } else { - address.addressType = OWSContactAddressType_Custom; - address.label = [Contact localizedStringForCNLabel:addressField.label]; - } - [addresses addObject:address]; - } - contact.addresses = addresses; - - return contact; -} - -+ (nullable CNContact *)systemContactForContact:(OWSContact *)contact imageData:(nullable NSData *)imageData -{ - if (!contact) { - OWSFailDebug(@"Missing contact."); - return nil; - } - - CNMutableContact *systemContact = [CNMutableContact new]; - - systemContact.givenName = contact.name.givenName; - systemContact.middleName = contact.name.middleName; - systemContact.familyName = contact.name.familyName; - systemContact.namePrefix = contact.name.namePrefix; - systemContact.nameSuffix = contact.name.nameSuffix; - // We don't need to set display name, it's implicit for system contacts. - systemContact.organizationName = contact.name.organizationName; - - NSMutableArray *> *systemPhoneNumbers = [NSMutableArray new]; - for (OWSContactPhoneNumber *phoneNumber in contact.phoneNumbers) { - switch (phoneNumber.phoneType) { - case OWSContactPhoneType_Home: - [systemPhoneNumbers - addObject:[CNLabeledValue - labeledValueWithLabel:CNLabelHome - value:[CNPhoneNumber - phoneNumberWithStringValue:phoneNumber.phoneNumber]]]; - break; - case OWSContactPhoneType_Mobile: - [systemPhoneNumbers - addObject:[CNLabeledValue - labeledValueWithLabel:CNLabelPhoneNumberMobile - value:[CNPhoneNumber - phoneNumberWithStringValue:phoneNumber.phoneNumber]]]; - break; - case OWSContactPhoneType_Work: - [systemPhoneNumbers - addObject:[CNLabeledValue - labeledValueWithLabel:CNLabelWork - value:[CNPhoneNumber - phoneNumberWithStringValue:phoneNumber.phoneNumber]]]; - break; - case OWSContactPhoneType_Custom: - [systemPhoneNumbers - addObject:[CNLabeledValue - labeledValueWithLabel:phoneNumber.label - value:[CNPhoneNumber - phoneNumberWithStringValue:phoneNumber.phoneNumber]]]; - break; - } - } - systemContact.phoneNumbers = systemPhoneNumbers; - - NSMutableArray *> *systemEmails = [NSMutableArray new]; - for (OWSContactEmail *email in contact.emails) { - switch (email.emailType) { - case OWSContactEmailType_Home: - [systemEmails addObject:[CNLabeledValue labeledValueWithLabel:CNLabelHome value:email.email]]; - break; - case OWSContactEmailType_Mobile: - [systemEmails addObject:[CNLabeledValue labeledValueWithLabel:@"Mobile" value:email.email]]; - break; - case OWSContactEmailType_Work: - [systemEmails addObject:[CNLabeledValue labeledValueWithLabel:CNLabelWork value:email.email]]; - break; - case OWSContactEmailType_Custom: - [systemEmails addObject:[CNLabeledValue labeledValueWithLabel:email.label value:email.email]]; - break; - } - } - systemContact.emailAddresses = systemEmails; - - NSMutableArray *> *systemAddresses = [NSMutableArray new]; - for (OWSContactAddress *address in contact.addresses) { - CNMutablePostalAddress *systemAddress = [CNMutablePostalAddress new]; - systemAddress.street = address.street; - // TODO: Is this the correct mapping? - // systemAddress.subLocality = address.neighborhood; - systemAddress.city = address.city; - // TODO: Is this the correct mapping? - // systemAddress.subAdministrativeArea = address.region; - systemAddress.state = address.region; - systemAddress.postalCode = address.postcode; - // TODO: Should we be using 2-letter codes, 3-letter codes or names? - systemAddress.ISOCountryCode = address.country; - - switch (address.addressType) { - case OWSContactAddressType_Home: - [systemAddresses addObject:[CNLabeledValue labeledValueWithLabel:CNLabelHome value:systemAddress]]; - break; - case OWSContactAddressType_Work: - [systemAddresses addObject:[CNLabeledValue labeledValueWithLabel:CNLabelWork value:systemAddress]]; - break; - case OWSContactAddressType_Custom: - [systemAddresses addObject:[CNLabeledValue labeledValueWithLabel:address.label value:systemAddress]]; - break; - } - } - systemContact.postalAddresses = systemAddresses; - systemContact.imageData = imageData; - - return systemContact; -} - -#pragma mark - Proto Serialization - -+ (nullable SSKProtoDataMessageContact *)protoForContact:(OWSContact *)contact -{ - OWSAssertDebug(contact); - - SSKProtoDataMessageContactBuilder *contactBuilder = [SSKProtoDataMessageContact builder]; - - SSKProtoDataMessageContactNameBuilder *nameBuilder = [SSKProtoDataMessageContactName builder]; - - OWSContactName *contactName = contact.name; - if (contactName.givenName.ows_stripped.length > 0) { - nameBuilder.givenName = contactName.givenName.ows_stripped; - } - if (contactName.familyName.ows_stripped.length > 0) { - nameBuilder.familyName = contactName.familyName.ows_stripped; - } - if (contactName.middleName.ows_stripped.length > 0) { - nameBuilder.middleName = contactName.middleName.ows_stripped; - } - if (contactName.namePrefix.ows_stripped.length > 0) { - nameBuilder.prefix = contactName.namePrefix.ows_stripped; - } - if (contactName.nameSuffix.ows_stripped.length > 0) { - nameBuilder.suffix = contactName.nameSuffix.ows_stripped; - } - if (contactName.organizationName.ows_stripped.length > 0) { - contactBuilder.organization = contactName.organizationName.ows_stripped; - } - nameBuilder.displayName = contactName.displayName; - - NSError *error; - SSKProtoDataMessageContactName *_Nullable nameProto = [nameBuilder buildAndReturnError:&error]; - if (error || !nameProto) { - OWSLogError(@"could not build protobuf: %@", error); - } else { - [contactBuilder setName:nameProto]; - } - - for (OWSContactPhoneNumber *phoneNumber in contact.phoneNumbers) { - SSKProtoDataMessageContactPhoneBuilder *phoneBuilder = [SSKProtoDataMessageContactPhone builder]; - phoneBuilder.value = phoneNumber.phoneNumber; - if (phoneNumber.label.ows_stripped.length > 0) { - phoneBuilder.label = phoneNumber.label.ows_stripped; - } - switch (phoneNumber.phoneType) { - case OWSContactPhoneType_Home: - phoneBuilder.type = SSKProtoDataMessageContactPhoneTypeHome; - break; - case OWSContactPhoneType_Mobile: - phoneBuilder.type = SSKProtoDataMessageContactPhoneTypeMobile; - break; - case OWSContactPhoneType_Work: - phoneBuilder.type = SSKProtoDataMessageContactPhoneTypeWork; - break; - case OWSContactPhoneType_Custom: - phoneBuilder.type = SSKProtoDataMessageContactPhoneTypeCustom; - break; - } - SSKProtoDataMessageContactPhone *_Nullable numberProto = [phoneBuilder buildAndReturnError:&error]; - if (error || !numberProto) { - OWSLogError(@"could not build protobuf: %@", error); - } else { - [contactBuilder addNumber:numberProto]; - } - } - - for (OWSContactEmail *email in contact.emails) { - SSKProtoDataMessageContactEmailBuilder *emailBuilder = [SSKProtoDataMessageContactEmail builder]; - emailBuilder.value = email.email; - if (email.label.ows_stripped.length > 0) { - emailBuilder.label = email.label.ows_stripped; - } - switch (email.emailType) { - case OWSContactEmailType_Home: - emailBuilder.type = SSKProtoDataMessageContactEmailTypeHome; - break; - case OWSContactEmailType_Mobile: - emailBuilder.type = SSKProtoDataMessageContactEmailTypeMobile; - break; - case OWSContactEmailType_Work: - emailBuilder.type = SSKProtoDataMessageContactEmailTypeWork; - break; - case OWSContactEmailType_Custom: - emailBuilder.type = SSKProtoDataMessageContactEmailTypeCustom; - break; - } - SSKProtoDataMessageContactEmail *_Nullable emailProto = [emailBuilder buildAndReturnError:&error]; - if (error || !emailProto) { - OWSLogError(@"could not build protobuf: %@", error); - } else { - [contactBuilder addEmail:emailProto]; - } - } - - for (OWSContactAddress *address in contact.addresses) { - SSKProtoDataMessageContactPostalAddressBuilder *addressBuilder = - [SSKProtoDataMessageContactPostalAddress builder]; - if (address.label.ows_stripped.length > 0) { - addressBuilder.label = address.label.ows_stripped; - } - if (address.street.ows_stripped.length > 0) { - addressBuilder.street = address.street.ows_stripped; - } - if (address.pobox.ows_stripped.length > 0) { - addressBuilder.pobox = address.pobox.ows_stripped; - } - if (address.neighborhood.ows_stripped.length > 0) { - addressBuilder.neighborhood = address.neighborhood.ows_stripped; - } - if (address.city.ows_stripped.length > 0) { - addressBuilder.city = address.city.ows_stripped; - } - if (address.region.ows_stripped.length > 0) { - addressBuilder.region = address.region.ows_stripped; - } - if (address.postcode.ows_stripped.length > 0) { - addressBuilder.postcode = address.postcode.ows_stripped; - } - if (address.country.ows_stripped.length > 0) { - addressBuilder.country = address.country.ows_stripped; - } - SSKProtoDataMessageContactPostalAddress *_Nullable addressProto = [addressBuilder buildAndReturnError:&error]; - if (error || !addressProto) { - OWSLogError(@"could not build protobuf: %@", error); - } else { - [contactBuilder addAddress:addressProto]; - } - } - - if (contact.avatarAttachmentId) { - SSKProtoAttachmentPointer *_Nullable attachmentProto = - [TSAttachmentStream buildProtoForAttachmentId:contact.avatarAttachmentId]; - if (!attachmentProto) { - OWSLogError(@"could not build protobuf: %@", error); - } else { - SSKProtoDataMessageContactAvatarBuilder *avatarBuilder = [SSKProtoDataMessageContactAvatar builder]; - avatarBuilder.avatar = attachmentProto; - SSKProtoDataMessageContactAvatar *_Nullable avatarProto = [avatarBuilder buildAndReturnError:&error]; - if (error || !avatarProto) { - OWSLogError(@"could not build protobuf: %@", error); - } else { - contactBuilder.avatar = avatarProto; - } - } - } - - SSKProtoDataMessageContact *_Nullable contactProto = [contactBuilder buildAndReturnError:&error]; - if (error || !contactProto) { - OWSFailDebug(@"could not build protobuf: %@", error); - return nil; - } - if (contactProto.number.count < 1 && contactProto.email.count < 1 && contactProto.address.count < 1) { - OWSFailDebug(@"contact has neither phone, email or address."); - return nil; - } - return contactProto; -} - -+ (nullable OWSContact *)contactForDataMessage:(SSKProtoDataMessage *)dataMessage - transaction:(YapDatabaseReadWriteTransaction *)transaction -{ - OWSAssertDebug(dataMessage); - - if (dataMessage.contact.count < 1) { - return nil; - } - OWSAssertDebug(dataMessage.contact.count == 1); - SSKProtoDataMessageContact *contactProto = dataMessage.contact.firstObject; - - OWSContact *contact = [OWSContact new]; - - OWSContactName *contactName = [OWSContactName new]; - if (contactProto.name) { - SSKProtoDataMessageContactName *nameProto = contactProto.name; - - if (nameProto.givenName) { - contactName.givenName = nameProto.givenName.ows_stripped; - } - if (nameProto.familyName) { - contactName.familyName = nameProto.familyName.ows_stripped; - } - if (nameProto.prefix) { - contactName.namePrefix = nameProto.prefix.ows_stripped; - } - if (nameProto.suffix) { - contactName.nameSuffix = nameProto.suffix.ows_stripped; - } - if (nameProto.middleName) { - contactName.middleName = nameProto.middleName.ows_stripped; - } - if (nameProto.displayName) { - contactName.displayName = nameProto.displayName.ows_stripped; - } - } - if (contactProto.organization) { - contactName.organizationName = contactProto.organization.ows_stripped; - } - [contactName ensureDisplayName]; - contact.name = contactName; - - NSMutableArray *phoneNumbers = [NSMutableArray new]; - for (SSKProtoDataMessageContactPhone *phoneNumberProto in contactProto.number) { - OWSContactPhoneNumber *_Nullable phoneNumber = [self phoneNumberForProto:phoneNumberProto]; - if (phoneNumber) { - [phoneNumbers addObject:phoneNumber]; - } - } - contact.phoneNumbers = [phoneNumbers copy]; - - NSMutableArray *emails = [NSMutableArray new]; - for (SSKProtoDataMessageContactEmail *emailProto in contactProto.email) { - OWSContactEmail *_Nullable email = [self emailForProto:emailProto]; - if (email) { - [emails addObject:email]; - } - } - contact.emails = [emails copy]; - - NSMutableArray *addresses = [NSMutableArray new]; - for (SSKProtoDataMessageContactPostalAddress *addressProto in contactProto.address) { - OWSContactAddress *_Nullable address = [self addressForProto:addressProto]; - if (address) { - [addresses addObject:address]; - } - } - contact.addresses = [addresses copy]; - - if (contactProto.avatar) { - SSKProtoDataMessageContactAvatar *avatarInfo = contactProto.avatar; - - if (avatarInfo.avatar) { - SSKProtoAttachmentPointer *avatarAttachment = avatarInfo.avatar; - - TSAttachmentPointer *_Nullable attachmentPointer = - [TSAttachmentPointer attachmentPointerFromProto:avatarAttachment albumMessage:nil]; - if (attachmentPointer) { - [attachmentPointer saveWithTransaction:transaction]; - contact.avatarAttachmentId = attachmentPointer.uniqueId; - contact.isProfileAvatar = avatarInfo.isProfile; - } else { - OWSFailDebug(@"Invalid avatar attachment."); - } - } else { - OWSFailDebug(@"avatarInfo.hasAvatar was unexpectedly false"); - } - } - - - return contact; -} - -+ (nullable OWSContactPhoneNumber *)phoneNumberForProto: - (SSKProtoDataMessageContactPhone *)phoneNumberProto -{ - OWSContactPhoneNumber *result = [OWSContactPhoneNumber new]; - result.phoneType = OWSContactPhoneType_Custom; - if (phoneNumberProto.hasType) { - switch (phoneNumberProto.type) { - case SSKProtoDataMessageContactPhoneTypeHome: - result.phoneType = OWSContactPhoneType_Home; - break; - case SSKProtoDataMessageContactPhoneTypeMobile: - result.phoneType = OWSContactPhoneType_Mobile; - break; - case SSKProtoDataMessageContactPhoneTypeWork: - result.phoneType = OWSContactPhoneType_Work; - break; - default: - break; - } - } - if (phoneNumberProto.hasLabel) { - result.label = phoneNumberProto.label.ows_stripped; - } - if (phoneNumberProto.hasValue) { - result.phoneNumber = phoneNumberProto.value.ows_stripped; - } else { - return nil; - } - return result; -} - -+ (nullable OWSContactEmail *)emailForProto:(SSKProtoDataMessageContactEmail *)emailProto -{ - OWSContactEmail *result = [OWSContactEmail new]; - result.emailType = OWSContactEmailType_Custom; - if (emailProto.hasType) { - switch (emailProto.type) { - case SSKProtoDataMessageContactEmailTypeHome: - result.emailType = OWSContactEmailType_Home; - break; - case SSKProtoDataMessageContactEmailTypeMobile: - result.emailType = OWSContactEmailType_Mobile; - break; - case SSKProtoDataMessageContactEmailTypeWork: - result.emailType = OWSContactEmailType_Work; - break; - default: - break; - } - } - if (emailProto.hasLabel) { - result.label = emailProto.label.ows_stripped; - } - if (emailProto.hasValue) { - result.email = emailProto.value.ows_stripped; - } else { - return nil; - } - return result; -} - -+ (nullable OWSContactAddress *)addressForProto:(SSKProtoDataMessageContactPostalAddress *)addressProto -{ - OWSContactAddress *result = [OWSContactAddress new]; - result.addressType = OWSContactAddressType_Custom; - if (addressProto.hasType) { - switch (addressProto.type) { - case SSKProtoDataMessageContactPostalAddressTypeHome: - result.addressType = OWSContactAddressType_Home; - break; - case SSKProtoDataMessageContactPostalAddressTypeWork: - result.addressType = OWSContactAddressType_Work; - break; - default: - break; - } - } - if (addressProto.hasLabel) { - result.label = addressProto.label.ows_stripped; - } - if (addressProto.hasStreet) { - result.street = addressProto.street.ows_stripped; - } - if (addressProto.hasPobox) { - result.pobox = addressProto.pobox.ows_stripped; - } - if (addressProto.hasNeighborhood) { - result.neighborhood = addressProto.neighborhood.ows_stripped; - } - if (addressProto.hasCity) { - result.city = addressProto.city.ows_stripped; - } - if (addressProto.hasRegion) { - result.region = addressProto.region.ows_stripped; - } - if (addressProto.hasPostcode) { - result.postcode = addressProto.postcode.ows_stripped; - } - if (addressProto.hasCountry) { - result.country = addressProto.country.ows_stripped; - } - return result; -} - -@end - -NS_ASSUME_NONNULL_END diff --git a/SignalUtilitiesKit/OWSContactAvatarBuilder.h b/SignalUtilitiesKit/OWSContactAvatarBuilder.h deleted file mode 100644 index 4d2d14da1..000000000 --- a/SignalUtilitiesKit/OWSContactAvatarBuilder.h +++ /dev/null @@ -1,32 +0,0 @@ -// -// Copyright (c) 2018 Open Whisper Systems. All rights reserved. -// - -#import -#import - -NS_ASSUME_NONNULL_BEGIN - -@class TSContactThread; - -@interface OWSContactAvatarBuilder : OWSAvatarBuilder - -/** - * Build an avatar for a Signal recipient - */ -- (instancetype)initWithSignalId:(NSString *)signalId - colorName:(ConversationColorName)colorName - diameter:(NSUInteger)diameter; - -/** - * Build an avatar for a non-Signal recipient - */ -- (instancetype)initWithNonSignalName:(NSString *)nonSignalName - colorSeed:(NSString *)colorSeed - diameter:(NSUInteger)diameter; - -- (instancetype)initForLocalUserWithDiameter:(NSUInteger)diameter; - -@end - -NS_ASSUME_NONNULL_END diff --git a/SignalUtilitiesKit/OWSContactAvatarBuilder.m b/SignalUtilitiesKit/OWSContactAvatarBuilder.m deleted file mode 100644 index 1af9d9e47..000000000 --- a/SignalUtilitiesKit/OWSContactAvatarBuilder.m +++ /dev/null @@ -1,227 +0,0 @@ -// -// Copyright (c) 2019 Open Whisper Systems. All rights reserved. -// - -#import "OWSContactAvatarBuilder.h" -#import "OWSContactsManager.h" -#import "TSContactThread.h" -#import "TSGroupThread.h" -#import "TSThread.h" -#import "UIColor+OWS.h" -#import "UIFont+OWS.h" -#import -#import - -NS_ASSUME_NONNULL_BEGIN - -@interface OWSContactAvatarBuilder () - -@property (nonatomic, readonly) NSString *signalId; -@property (nonatomic, readonly) NSString *contactName; -@property (nonatomic, readonly) ConversationColorName colorName; -@property (nonatomic, readonly) NSUInteger diameter; - -@end - -@implementation OWSContactAvatarBuilder - -#pragma mark - Initializers - -- (instancetype)initWithContactId:(NSString *)contactId - name:(NSString *)name - colorName:(ConversationColorName)colorName - diameter:(NSUInteger)diameter -{ - self = [super init]; - if (!self) { - return self; - } - - OWSAssertDebug(colorName.length > 0); - - _signalId = contactId; - _contactName = name; - _colorName = colorName; - _diameter = diameter; - - return self; -} - -- (instancetype)initWithSignalId:(NSString *)signalId - colorName:(ConversationColorName)colorName - diameter:(NSUInteger)diameter -{ - // Name for avatar initials. - NSString *_Nullable name = [OWSContactAvatarBuilder.contactsManager nameFromSystemContactsForRecipientId:signalId]; - if (name.length == 0) { - name = [OWSContactAvatarBuilder.contactsManager profileNameForRecipientId:signalId]; - } - if (name.length == 0) { - name = signalId; - } - return [self initWithContactId:signalId name:name colorName:colorName diameter:diameter]; -} - -- (instancetype)initWithNonSignalName:(NSString *)nonSignalName - colorSeed:(NSString *)colorSeed - diameter:(NSUInteger)diameter -{ - ConversationColorName colorName = [TSThread stableColorNameForNewConversationWithString:colorSeed]; - return [self initWithContactId:colorSeed name:nonSignalName colorName:(NSString *)colorName diameter:diameter]; -} - -- (instancetype)initForLocalUserWithDiameter:(NSUInteger)diameter -{ - NSString *localNumber = [NSUserDefaults.standardUserDefaults stringForKey:@"masterDeviceHexEncodedPublicKey"]; - if (localNumber == nil) { - localNumber = [TSAccountManager localNumber]; - } - OWSAssertDebug(localNumber.length > 0); - OWSAssertDebug(diameter > 0); - - return [self initWithSignalId:localNumber colorName:kConversationColorName_Default diameter:diameter]; -} - -#pragma mark - Dependencies - -+ (OWSContactsManager *)contactsManager -{ - return (OWSContactsManager *)SSKEnvironment.shared.contactsManager; -} - -#pragma mark - Instance methods - -- (nullable UIImage *)buildSavedImage -{ - NSString *masterDeviceHexEncodedPublicKey = [NSUserDefaults.standardUserDefaults stringForKey:@"masterDeviceHexEncodedPublicKey"]; - if ([self.signalId isEqualToString:TSAccountManager.localNumber] || (masterDeviceHexEncodedPublicKey != nil && [self.signalId isEqualToString:masterDeviceHexEncodedPublicKey])) { - NSString *noteToSelfCacheKey = [NSString stringWithFormat:@"%@:note-to-self", self.cacheKey]; - UIImage *_Nullable cachedAvatar = - [OWSContactAvatarBuilder.contactsManager.avatarCache imageForKey:noteToSelfCacheKey - diameter:(CGFloat)self.diameter]; - if (cachedAvatar) { - return cachedAvatar; - } - - UIImage *image = [self noteToSelfImageWithConversationColorName:self.colorName]; - if (!image) { - OWSFailDebug(@"Could not generate avatar."); - return nil; - } - - [OWSContactAvatarBuilder.contactsManager.avatarCache setImage:image - forKey:noteToSelfCacheKey - diameter:self.diameter]; - return image; - } - - return [OWSContactAvatarBuilder.contactsManager imageForPhoneIdentifier:self.signalId]; -} - -- (id)cacheKey -{ - return [NSString stringWithFormat:@"%@-%d", self.signalId, Theme.isDarkThemeEnabled]; -} - -- (nullable UIImage *)buildDefaultImage -{ - UIImage *_Nullable cachedAvatar = - [OWSContactAvatarBuilder.contactsManager.avatarCache imageForKey:self.cacheKey diameter:(CGFloat)self.diameter]; - if (cachedAvatar) { - return cachedAvatar; - } - - /** - NSMutableString *initials = [NSMutableString string]; - - NSRange rangeOfLetters = [self.contactName rangeOfCharacterFromSet:[NSCharacterSet letterCharacterSet]]; - if (rangeOfLetters.location != NSNotFound) { - // Contact name contains letters, so it's probably not just a phone number. - // Make an image from the contact's initials - NSCharacterSet *excludeAlphanumeric = [NSCharacterSet alphanumericCharacterSet].invertedSet; - NSArray *words = - [self.contactName componentsSeparatedByCharactersInSet:[NSCharacterSet whitespaceCharacterSet]]; - for (NSString *word in words) { - NSString *trimmedWord = [word stringByTrimmingCharactersInSet:excludeAlphanumeric]; - if (trimmedWord.length > 0) { - NSString *firstLetter = [trimmedWord substringToIndex:1]; - [initials appendString:firstLetter.localizedUppercaseString]; - } - } - - NSRange stringRange = { 0, MIN([initials length], (NSUInteger)3) }; // Rendering max 3 letters. - initials = [[initials substringWithRange:stringRange] mutableCopy]; - } - - UIColor *color = [OWSConversationColor conversationColorOrDefaultForColorName:self.colorName].themeColor; - OWSAssertDebug(color); - - UIImage *_Nullable image; - if (initials.length == 0) { - // We don't have a name for this contact, so we can't make an "initials" image. - - UIImage *icon; - if (self.diameter > kStandardAvatarSize) { - icon = [UIImage imageNamed:@"contact-avatar-1024"]; - } else { - icon = [UIImage imageNamed:@"contact-avatar-84"]; - } - CGFloat assetWidthPixels = CGImageGetWidth(icon.CGImage); - // The contact-avatar asset is designed to be 28pt if the avatar is kStandardAvatarSize. - // Adjust its size to reflect the actual output diameter. - // We use an oversize 1024px version of the asset to ensure quality results for larger avatars. - CGFloat scaling = (self.diameter / (CGFloat)kStandardAvatarSize) * (28 / assetWidthPixels); - - CGSize iconSize = CGSizeScale(icon.size, scaling); - image = - [OWSAvatarBuilder avatarImageWithIcon:icon iconSize:iconSize backgroundColor:color diameter:self.diameter]; - } else { - image = [OWSAvatarBuilder avatarImageWithInitials:initials backgroundColor:color diameter:self.diameter]; - } - - if (!image) { - OWSFailDebug(@"Could not generate avatar."); - return nil; - } - */ - - UIImage *image = [LKIdenticon generatePlaceholderIconWithSeed:self.signalId text:@"0" size:((CGFloat)self.diameter)]; - [OWSContactAvatarBuilder.contactsManager.avatarCache setImage:image forKey:self.cacheKey diameter:self.diameter]; - return image; -} - -- (nullable UIImage *)noteToSelfImageWithConversationColorName:(ConversationColorName)conversationColorName -{ - UIImage *baseImage = [[UIImage imageNamed:@"note-to-self-avatar"] asTintedImageWithColor:UIColor.whiteColor]; - UIColor *backgroundColor = [OWSConversationColor conversationColorOrDefaultForColorName:conversationColorName].themeColor; - - CGFloat paddingFactor = 1.6; - CGFloat paddedWidth = baseImage.size.width * paddingFactor; - CGFloat paddedheight = baseImage.size.height * paddingFactor; - - UIGraphicsBeginImageContextWithOptions(CGSizeMake(paddedWidth, paddedheight), NO, 0.0); - CGContextRef _Nullable context = UIGraphicsGetCurrentContext(); - if (context == nil) { - OWSFailDebug(@"failure: context was unexpectedly nil"); - return nil; - } - [backgroundColor setFill]; - CGContextFillRect(context, CGRectMake(0, 0, paddedWidth, paddedheight)); - - CGPoint origin = CGPointMake((paddedWidth - baseImage.size.width) / 2.0f, - (paddedheight - baseImage.size.height) / 2.0f); - [baseImage drawAtPoint:origin]; - - UIImage *paddedImage = UIGraphicsGetImageFromCurrentImageContext(); - if (paddedImage == nil) { - OWSFailDebug(@"failure: paddedImage was unexpectedly nil"); - return nil; - } - UIGraphicsEndImageContext(); - - return paddedImage; -} - -@end - -NS_ASSUME_NONNULL_END diff --git a/SignalUtilitiesKit/OWSContactDiscoveryOperation.swift b/SignalUtilitiesKit/OWSContactDiscoveryOperation.swift deleted file mode 100644 index 8a71e43a7..000000000 --- a/SignalUtilitiesKit/OWSContactDiscoveryOperation.swift +++ /dev/null @@ -1,537 +0,0 @@ -// -// Copyright (c) 2019 Open Whisper Systems. All rights reserved. -// - -import Foundation -import SignalCoreKit - -@objc(OWSLegacyContactDiscoveryOperation) -public class LegacyContactDiscoveryBatchOperation: OWSOperation { - - @objc - public var registeredRecipientIds: Set - - private let recipientIdsToLookup: [String] - private var networkManager: TSNetworkManager { - return TSNetworkManager.shared() - } - - // MARK: Initializers - - @objc - public required init(recipientIdsToLookup: [String]) { - self.recipientIdsToLookup = recipientIdsToLookup - self.registeredRecipientIds = Set() - - super.init() - - Logger.debug("with recipientIdsToLookup: \(recipientIdsToLookup.count)") - } - - // MARK: OWSOperation Overrides - - // Called every retry, this is where the bulk of the operation's work should go. - override public func run() { - Logger.debug("") - - guard !isCancelled else { - Logger.info("no work to do, since we were canceled") - self.reportCancelled() - return - } - - var phoneNumbersByHashes: [String: String] = [:] - - for recipientId in recipientIdsToLookup { - guard let hash = Cryptography.truncatedSHA1Base64EncodedWithoutPadding(recipientId) else { - owsFailDebug("could not hash recipient id: \(recipientId)") - continue - } - assert(phoneNumbersByHashes[hash] == nil) - phoneNumbersByHashes[hash] = recipientId - } - - let hashes: [String] = Array(phoneNumbersByHashes.keys) - - let request = OWSRequestFactory.contactsIntersectionRequest(withHashesArray: hashes) - - self.networkManager.makeRequest(request, - success: { (task, responseDict) in - do { - self.registeredRecipientIds = try self.parse(response: responseDict, phoneNumbersByHashes: phoneNumbersByHashes) - self.reportSuccess() - } catch { - self.reportError(error) - } - }, - failure: { (task, error) in - guard let response = task.response as? HTTPURLResponse else { - let responseError: NSError = OWSErrorMakeUnableToProcessServerResponseError() as NSError - responseError.isRetryable = true - self.reportError(responseError) - return - } - - guard response.statusCode != 413 else { - let rateLimitError = OWSErrorWithCodeDescription(OWSErrorCode.contactsUpdaterRateLimit, "Contacts Intersection Rate Limit") - self.reportError(rateLimitError) - return - } - - self.reportError(error) - }) - } - - // Called at most one time. - override public func didSucceed() { - // Compare against new CDS service - let modernCDSOperation = CDSOperation(recipientIdsToLookup: self.recipientIdsToLookup) - let cdsFeedbackOperation = CDSFeedbackOperation(legacyRegisteredRecipientIds: self.registeredRecipientIds) - cdsFeedbackOperation.addDependency(modernCDSOperation) - - let operations = modernCDSOperation.dependencies + [modernCDSOperation, cdsFeedbackOperation] - CDSOperation.operationQueue.addOperations(operations, waitUntilFinished: false) - } - - // MARK: Private Helpers - - private func parse(response: Any?, phoneNumbersByHashes: [String: String]) throws -> Set { - - guard let responseDict = response as? [String: AnyObject] else { - let responseError: NSError = OWSErrorMakeUnableToProcessServerResponseError() as NSError - responseError.isRetryable = true - - throw responseError - } - - guard let contactDicts = responseDict["contacts"] as? [[String: AnyObject]] else { - let responseError: NSError = OWSErrorMakeUnableToProcessServerResponseError() as NSError - responseError.isRetryable = true - - throw responseError - } - - var registeredRecipientIds: Set = Set() - - for contactDict in contactDicts { - guard let hash = contactDict["token"] as? String, hash.count > 0 else { - owsFailDebug("hash was unexpectedly nil") - continue - } - - guard let recipientId = phoneNumbersByHashes[hash], recipientId.count > 0 else { - owsFailDebug("recipientId was unexpectedly nil") - continue - } - - guard recipientIdsToLookup.contains(recipientId) else { - owsFailDebug("unexpected recipientId") - continue - } - - registeredRecipientIds.insert(recipientId) - } - - return registeredRecipientIds - } - -} - -enum ContactDiscoveryError: Error { - case parseError(description: String) - case assertionError(description: String) - case clientError(underlyingError: Error) - case serverError(underlyingError: Error) -} - -@objc(OWSCDSOperation) -class CDSOperation: OWSOperation { - - let batchSize = 2048 - static let operationQueue: OperationQueue = { - let queue = OperationQueue() - queue.maxConcurrentOperationCount = 5 - queue.name = CDSOperation.logTag() - return queue - }() - - let recipientIdsToLookup: [String] - - @objc - var registeredRecipientIds: Set - - @objc - required init(recipientIdsToLookup: [String]) { - self.recipientIdsToLookup = recipientIdsToLookup - self.registeredRecipientIds = Set() - - super.init() - - Logger.debug("with recipientIdsToLookup: \(recipientIdsToLookup.count)") - for batchIds in recipientIdsToLookup.chunked(by: batchSize) { - let batchOperation = CDSBatchOperation(recipientIdsToLookup: batchIds) - self.addDependency(batchOperation) - } - } - - // MARK: Mandatory overrides - - // Called every retry, this is where the bulk of the operation's work should go. - override func run() { - Logger.debug("") - - for dependency in self.dependencies { - guard let batchOperation = dependency as? CDSBatchOperation else { - owsFailDebug("unexpected dependency: \(dependency)") - continue - } - - self.registeredRecipientIds.formUnion(batchOperation.registeredRecipientIds) - } - - self.reportSuccess() - } - -} - -public -class CDSBatchOperation: OWSOperation { - - private let recipientIdsToLookup: [String] - private(set) var registeredRecipientIds: Set - - private var networkManager: TSNetworkManager { - return TSNetworkManager.shared() - } - - private var contactDiscoveryService: ContactDiscoveryService { - return ContactDiscoveryService.shared() - } - - // MARK: Initializers - - public required init(recipientIdsToLookup: [String]) { - self.recipientIdsToLookup = Set(recipientIdsToLookup).map { $0 } - self.registeredRecipientIds = Set() - - super.init() - - Logger.debug("with recipientIdsToLookup: \(recipientIdsToLookup.count)") - } - - // MARK: OWSOperationOverrides - - // Called every retry, this is where the bulk of the operation's work should go. - override public func run() { - Logger.debug("") - - guard !isCancelled else { - Logger.info("no work to do, since we were canceled") - self.reportCancelled() - return - } - - contactDiscoveryService.performRemoteAttestation(success: { (remoteAttestation: RemoteAttestation) in - self.makeContactDiscoveryRequest(remoteAttestation: remoteAttestation) - }, - failure: self.reportError) - } - - private func makeContactDiscoveryRequest(remoteAttestation: RemoteAttestation) { - return // Loki: Do nothing - - guard !isCancelled else { - Logger.info("no work to do, since we were canceled") - self.reportCancelled() - return - } - - let encryptionResult: AES25GCMEncryptionResult - do { - encryptionResult = try encryptAddresses(recipientIds: recipientIdsToLookup, remoteAttestation: remoteAttestation) - } catch { - reportError(error) - return - } - - let request = OWSRequestFactory.enclaveContactDiscoveryRequest(withId: remoteAttestation.requestId, - addressCount: UInt(recipientIdsToLookup.count), - encryptedAddressData: encryptionResult.ciphertext, - cryptIv: encryptionResult.initializationVector, - cryptMac: encryptionResult.authTag, - enclaveId: remoteAttestation.enclaveId, - authUsername: remoteAttestation.auth.username, - authPassword: remoteAttestation.auth.password, - cookies: remoteAttestation.cookies) - - self.networkManager.makeRequest(request, - success: { (task, responseDict) in - do { - self.registeredRecipientIds = try self.handle(response: responseDict, remoteAttestation: remoteAttestation) - self.reportSuccess() - } catch { - self.reportError(error) - } - }, - failure: { (task, error) in - guard let response = task.response as? HTTPURLResponse else { - let responseError = OWSErrorMakeUnableToProcessServerResponseError() as NSError - responseError.isRetryable = true - self.reportError(responseError) - return - } - - guard response.statusCode != 413 else { - let rateLimitError: NSError = OWSErrorWithCodeDescription(OWSErrorCode.contactsUpdaterRateLimit, "Contacts Intersection Rate Limit") as NSError - - // TODO CDS ratelimiting, handle Retry-After header if available - rateLimitError.isRetryable = false - self.reportError(rateLimitError) - return - } - - guard response.statusCode / 100 != 4 else { - let clientError: NSError = ContactDiscoveryError.clientError(underlyingError: error) as NSError - clientError.isRetryable = (error as NSError).isRetryable - self.reportError(clientError) - return - } - - guard response.statusCode / 100 != 5 else { - let serverError = ContactDiscoveryError.serverError(underlyingError: error) as NSError - serverError.isRetryable = (error as NSError).isRetryable - - // TODO CDS ratelimiting, handle Retry-After header if available - self.reportError(serverError) - return - } - - self.reportError(error) - }) - } - - func encryptAddresses(recipientIds: [String], remoteAttestation: RemoteAttestation) throws -> AES25GCMEncryptionResult { - - let addressPlainTextData = try type(of: self).encodePhoneNumbers(recipientIds: recipientIds) - - guard let encryptionResult = Cryptography.encryptAESGCM(plainTextData: addressPlainTextData, - initializationVector: Cryptography.generateRandomBytes(kAESGCM256_DefaultIVLength), - additionalAuthenticatedData: remoteAttestation.requestId, - key: remoteAttestation.keys.clientKey) else { - - throw ContactDiscoveryError.assertionError(description: "Encryption failure") - } - - return encryptionResult - } - - class func encodePhoneNumbers(recipientIds: [String]) throws -> Data { - var output = Data() - - for recipientId in recipientIds { - guard recipientId.prefix(1) == "+" else { - throw ContactDiscoveryError.assertionError(description: "unexpected id format") - } - - let numericPortionIndex = recipientId.index(after: recipientId.startIndex) - let numericPortion = recipientId.suffix(from: numericPortionIndex) - - guard let numericIdentifier = UInt64(numericPortion), numericIdentifier > 99 else { - throw ContactDiscoveryError.assertionError(description: "unexpectedly short identifier") - } - - var bigEndian: UInt64 = CFSwapInt64HostToBig(numericIdentifier) - let buffer = UnsafeBufferPointer(start: &bigEndian, count: 1) - output.append(buffer) - } - - return output - } - - func handle(response: Any?, remoteAttestation: RemoteAttestation) throws -> Set { - let isIncludedData: Data = try parseAndDecrypt(response: response, remoteAttestation: remoteAttestation) - guard let isIncluded: [Bool] = type(of: self).boolArray(data: isIncludedData) else { - throw ContactDiscoveryError.assertionError(description: "isIncluded was unexpectedly nil") - } - - return try match(recipientIds: self.recipientIdsToLookup, isIncluded: isIncluded) - } - - class func boolArray(data: Data) -> [Bool]? { - var bools: [Bool]? - data.withUnsafeBytes { (bytes: UnsafePointer) -> Void in - let buffer = UnsafeBufferPointer(start: bytes, count: data.count) - bools = Array(buffer) - } - - return bools - } - - func match(recipientIds: [String], isIncluded: [Bool]) throws -> Set { - guard recipientIds.count == isIncluded.count else { - throw ContactDiscoveryError.assertionError(description: "length mismatch for isIncluded/recipientIds") - } - - let includedRecipientIds: [String] = (0.. Data { - - guard let params = ParamParser(responseObject: response) else { - throw ContactDiscoveryError.parseError(description: "missing response dict") - } - - let cipherText = try params.requiredBase64EncodedData(key: "data") - let initializationVector = try params.requiredBase64EncodedData(key: "iv") - let authTag = try params.requiredBase64EncodedData(key: "mac") - - guard let plainText = Cryptography.decryptAESGCM(withInitializationVector: initializationVector, - ciphertext: cipherText, - additionalAuthenticatedData: nil, - authTag: authTag, - key: remoteAttestation.keys.serverKey) else { - throw ContactDiscoveryError.parseError(description: "decryption failed") - } - - return plainText - } -} - -class CDSFeedbackOperation: OWSOperation { - - enum FeedbackResult { - case ok - case mismatch - case attestationError(reason: String) - case unexpectedError(reason: String) - } - - private let legacyRegisteredRecipientIds: Set - - var networkManager: TSNetworkManager { - return TSNetworkManager.shared() - } - - // MARK: Initializers - - required init(legacyRegisteredRecipientIds: Set) { - self.legacyRegisteredRecipientIds = legacyRegisteredRecipientIds - - super.init() - - Logger.debug("") - } - - // MARK: OWSOperation Overrides - - override func checkForPreconditionError() -> Error? { - // override super with no-op - // In this rare case, we want to proceed even though our dependency might have an - // error so we can report the details of that error to the feedback service. - return nil - } - - // Called every retry, this is where the bulk of the operation's work should go. - override func run() { - - guard !isCancelled else { - Logger.info("no work to do, since we were canceled") - self.reportCancelled() - return - } - - guard let cdsOperation = dependencies.first as? CDSOperation else { - let error = OWSErrorMakeAssertionError("cdsOperation was unexpectedly nil") - self.reportError(error) - return - } - - if let error = cdsOperation.failingError { - switch error { - case TSNetworkManagerError.failedConnection: - // Don't submit feedback for connectivity errors - self.reportSuccess() - case ContactDiscoveryError.serverError, ContactDiscoveryError.clientError: - // Server already has this information, no need submit feedback - self.reportSuccess() - case let cdsError as ContactDiscoveryServiceError: - let reason = cdsError.reason - switch cdsError.code { - case .assertionError: - self.makeRequest(result: .unexpectedError(reason: "CDS assertionError: \(reason ?? "unknown")")) - case .attestationFailed: - self.makeRequest(result: .attestationError(reason: "CDS attestationFailed: \(reason ?? "unknown")")) - } - case ContactDiscoveryError.assertionError(let assertionDescription): - self.makeRequest(result: .unexpectedError(reason: "assertionError: \(assertionDescription)")) - case ContactDiscoveryError.parseError(description: let parseErrorDescription): - self.makeRequest(result: .unexpectedError(reason: "parseError: \(parseErrorDescription)")) - default: - let nsError = error as NSError - let reason = "unexpectedError code:\(nsError.code)" - self.makeRequest(result: .unexpectedError(reason: reason)) - } - - return - } - - if cdsOperation.registeredRecipientIds == legacyRegisteredRecipientIds { - self.makeRequest(result: .ok) - return - } else { - self.makeRequest(result: .mismatch) - return - } - } - - func makeRequest(result: FeedbackResult) { - let reason: String? - switch result { - case .ok: - reason = nil - case .mismatch: - reason = nil - case .attestationError(let attestationErrorReason): - reason = attestationErrorReason - case .unexpectedError(let unexpectedErrorReason): - reason = unexpectedErrorReason - } - let request = OWSRequestFactory.cdsFeedbackRequest(status: result.statusPath, reason: reason) - self.networkManager.makeRequest(request, - success: { _, _ in self.reportSuccess() }, - failure: { _, error in self.reportError(error) }) - } -} - -extension Array { -// func chunked(by chunkSize: Int) -> [[Element]] { -// return stride(from: 0, to: self.count, by: chunkSize).map { -// Array(self[$0.. -#import - -NS_ASSUME_NONNULL_BEGIN - -extern NSString *const OWSContactsManagerSignalAccountsDidChangeNotification; - -@class ImageCache; -@class OWSPrimaryStorage; -@class SignalAccount; -@class UIFont; - -/** - * Get latest Signal contacts, and be notified when they change. - */ -@interface OWSContactsManager : NSObject - -#pragma mark - Setup - -- (instancetype)init NS_UNAVAILABLE; - -- (id)initWithPrimaryStorage:(OWSPrimaryStorage *)primaryStorage; - -#pragma mark - Accessors - -@property (nonnull, readonly) ImageCache *avatarCache; - -@property (atomic, readonly) NSArray *allContacts; - -@property (atomic, readonly) NSDictionary *allContactsMap; - -// order of the signalAccounts array respects the systems contact sorting preference -@property (atomic, readonly) NSArray *signalAccounts; - -// This will return an instance of SignalAccount for _known_ signal accounts. -- (nullable SignalAccount *)fetchSignalAccountForRecipientId:(NSString *)recipientId; -// This will always return an instance of SignalAccount. -- (SignalAccount *)fetchOrBuildSignalAccountForRecipientId:(NSString *)recipientId; -- (BOOL)hasSignalAccountForRecipientId:(NSString *)recipientId; - -#pragma mark - System Contact Fetching - -// Must call `requestSystemContactsOnce` before accessing this method -@property (nonatomic, readonly) BOOL isSystemContactsAuthorized; -@property (nonatomic, readonly) BOOL isSystemContactsDenied; -@property (nonatomic, readonly) BOOL systemContactsHaveBeenRequestedAtLeastOnce; - -@property (nonatomic, readonly) BOOL supportsContactEditing; - -@property (atomic, readonly) BOOL isSetup; - -// Request systems contacts and start syncing changes. The user will see an alert -// if they haven't previously. -- (void)requestSystemContactsOnce; -- (void)requestSystemContactsOnceWithCompletion:(void (^_Nullable)(NSError *_Nullable error))completion; - -// Ensure's the app has the latest contacts, but won't prompt the user for contact -// access if they haven't granted it. -- (void)fetchSystemContactsOnceIfAlreadyAuthorized; - -// This variant will fetch system contacts if contact access has already been granted, -// but not prompt for contact access. Also, it will always notify delegates, even if -// contacts haven't changed, and will clear out any stale cached SignalAccounts -- (void)userRequestedSystemContactsRefreshWithCompletion:(void (^)(NSError *_Nullable error))completionHandler; - -#pragma mark - Util - -- (BOOL)isSystemContact:(NSString *)recipientId; -- (BOOL)isSystemContactWithSignalAccount:(NSString *)recipientId; -- (BOOL)hasNameInSystemContactsForRecipientId:(NSString *)recipientId; -- (NSString *)displayNameForSignalAccount:(SignalAccount *)signalAccount; - -/** - * Used for sorting, respects system contacts name sort order preference. - */ -- (NSString *)comparableNameForSignalAccount:(SignalAccount *)signalAccount; - -// Generally we prefer the formattedProfileName over the raw profileName so as to -// distinguish a profile name apart from a name pulled from the system's contacts. -// This helps clarify when the remote person chooses a potentially confusing profile name. -- (nullable NSString *)formattedProfileNameForRecipientId:(NSString *)recipientId; -- (nullable NSString *)profileNameForRecipientId:(NSString *)recipientId; -- (nullable NSString *)nameFromSystemContactsForRecipientId:(NSString *)recipientId; -- (NSString *)stringForConversationTitleWithPhoneIdentifier:(NSString *)recipientId; - -- (nullable UIImage *)systemContactImageForPhoneIdentifier:(nullable NSString *)identifier; -- (nullable UIImage *)profileImageForPhoneIdentifier:(nullable NSString *)identifier; -- (nullable NSData *)profileImageDataForPhoneIdentifier:(nullable NSString *)identifier; - -- (nullable UIImage *)imageForPhoneIdentifier:(nullable NSString *)identifier; -- (NSAttributedString *)formattedDisplayNameForSignalAccount:(SignalAccount *)signalAccount font:(UIFont *)font; -- (NSAttributedString *)formattedFullNameForRecipientId:(NSString *)recipientId font:(UIFont *)font; -- (NSString *)contactOrProfileNameForPhoneIdentifier:(NSString *)recipientId; -- (NSAttributedString *)attributedContactOrProfileNameForPhoneIdentifier:(NSString *)recipientId; -- (NSAttributedString *)attributedContactOrProfileNameForPhoneIdentifier:(NSString *)recipientId - primaryFont:(UIFont *)primaryFont - secondaryFont:(UIFont *)secondaryFont; -- (NSAttributedString *)attributedContactOrProfileNameForPhoneIdentifier:(NSString *)recipientId - primaryAttributes:(NSDictionary *)primaryAttributes - secondaryAttributes:(NSDictionary *)secondaryAttributes; -@end - -NS_ASSUME_NONNULL_END diff --git a/SignalUtilitiesKit/OWSContactsManager.m b/SignalUtilitiesKit/OWSContactsManager.m deleted file mode 100644 index e7c0024f9..000000000 --- a/SignalUtilitiesKit/OWSContactsManager.m +++ /dev/null @@ -1,1133 +0,0 @@ -// -// Copyright (c) 2019 Open Whisper Systems. All rights reserved. -// - -#import "OWSContactsManager.h" -#import "Environment.h" -#import "NSAttributedString+OWS.h" -#import "OWSFormat.h" -#import "OWSProfileManager.h" -#import "OWSUserProfile.h" -#import "ViewControllerUtils.h" -#import -#import -#import -#import -#import -#import -#import -#import -#import -#import - -@import Contacts; - -NS_ASSUME_NONNULL_BEGIN - -NSString *const OWSContactsManagerSignalAccountsDidChangeNotification - = @"OWSContactsManagerSignalAccountsDidChangeNotification"; - -NSString *const OWSContactsManagerCollection = @"OWSContactsManagerCollection"; -NSString *const OWSContactsManagerKeyLastKnownContactPhoneNumbers - = @"OWSContactsManagerKeyLastKnownContactPhoneNumbers"; -NSString *const OWSContactsManagerKeyNextFullIntersectionDate = @"OWSContactsManagerKeyNextFullIntersectionDate2"; - -@interface OWSContactsManager () - -@property (nonatomic) BOOL isContactsUpdateInFlight; -// This reflects the contents of the device phone book and includes -// contacts that do not correspond to any signal account. -@property (atomic) NSArray *allContacts; -@property (atomic) NSDictionary *allContactsMap; -@property (atomic) NSArray *signalAccounts; -@property (atomic) NSDictionary *signalAccountMap; -@property (nonatomic, readonly) SystemContactsFetcher *systemContactsFetcher; -@property (nonatomic, readonly) YapDatabaseConnection *dbReadConnection; -@property (nonatomic, readonly) YapDatabaseConnection *dbWriteConnection; -@property (nonatomic, readonly) NSCache *cnContactCache; -@property (nonatomic, readonly) NSCache *cnContactAvatarCache; -@property (atomic) BOOL isSetup; - -@end - -#pragma mark - - -@implementation OWSContactsManager - -- (id)initWithPrimaryStorage:(OWSPrimaryStorage *)primaryStorage -{ - self = [super init]; - if (!self) { - return self; - } - - // TODO: We need to configure the limits of this cache. - _avatarCache = [ImageCache new]; - - _dbReadConnection = primaryStorage.newDatabaseConnection; - _dbWriteConnection = primaryStorage.newDatabaseConnection; - - _allContacts = @[]; - _allContactsMap = @{}; - _signalAccountMap = @{}; - _signalAccounts = @[]; - _systemContactsFetcher = [SystemContactsFetcher new]; - _systemContactsFetcher.delegate = self; - _cnContactCache = [NSCache new]; - _cnContactCache.countLimit = 50; - _cnContactAvatarCache = [NSCache new]; - _cnContactAvatarCache.countLimit = 25; - - OWSSingletonAssert(); - - [AppReadiness runNowOrWhenAppWillBecomeReady:^{ - [self setup]; - - [self startObserving]; - }]; - - return self; -} - -- (void)setup { - __block NSMutableArray *signalAccounts; - [self.dbReadConnection readWithBlock:^(YapDatabaseReadTransaction *transaction) { - NSUInteger signalAccountCount = [SignalAccount numberOfKeysInCollectionWithTransaction:transaction]; - OWSLogInfo(@"loading %lu signal accounts from cache.", (unsigned long)signalAccountCount); - - signalAccounts = [[NSMutableArray alloc] initWithCapacity:signalAccountCount]; - - [SignalAccount enumerateCollectionObjectsWithTransaction:transaction usingBlock:^(SignalAccount *signalAccount, BOOL * _Nonnull stop) { - [signalAccounts addObject:signalAccount]; - }]; - }]; - [signalAccounts sortUsingComparator:self.signalAccountComparator]; - - [self updateSignalAccounts:signalAccounts]; -} - -- (dispatch_queue_t)serialQueue -{ - static dispatch_queue_t _serialQueue; - static dispatch_once_t onceToken; - dispatch_once(&onceToken, ^{ - _serialQueue = dispatch_queue_create("org.whispersystems.contacts.buildSignalAccount", DISPATCH_QUEUE_SERIAL); - }); - - return _serialQueue; -} - -#pragma mark - System Contact Fetching - -// Request contacts access if you haven't asked recently. -- (void)requestSystemContactsOnce -{ - [self requestSystemContactsOnceWithCompletion:nil]; -} - -- (void)requestSystemContactsOnceWithCompletion:(void (^_Nullable)(NSError *_Nullable error))completion -{ - [self.systemContactsFetcher requestOnceWithCompletion:completion]; -} - -- (void)fetchSystemContactsOnceIfAlreadyAuthorized -{ - [self.systemContactsFetcher fetchOnceIfAlreadyAuthorized]; -} - -- (void)userRequestedSystemContactsRefreshWithCompletion:(void (^)(NSError *_Nullable error))completionHandler -{ - [self.systemContactsFetcher userRequestedRefreshWithCompletion:completionHandler]; -} - -- (BOOL)isSystemContactsAuthorized -{ - return self.systemContactsFetcher.isAuthorized; -} - -- (BOOL)isSystemContactsDenied -{ - return self.systemContactsFetcher.isDenied; -} - -- (BOOL)systemContactsHaveBeenRequestedAtLeastOnce -{ - return self.systemContactsFetcher.systemContactsHaveBeenRequestedAtLeastOnce; -} - -- (BOOL)supportsContactEditing -{ - return self.systemContactsFetcher.supportsContactEditing; -} - -#pragma mark - CNContacts - -- (nullable CNContact *)cnContactWithId:(nullable NSString *)contactId -{ - OWSAssertDebug(self.cnContactCache); - - if (!contactId) { - return nil; - } - - CNContact *_Nullable cnContact; - @synchronized(self.cnContactCache) { - cnContact = [self.cnContactCache objectForKey:contactId]; - if (!cnContact) { - cnContact = [self.systemContactsFetcher fetchCNContactWithContactId:contactId]; - if (cnContact) { - [self.cnContactCache setObject:cnContact forKey:contactId]; - } - } - } - - return cnContact; -} - -- (nullable NSData *)avatarDataForCNContactId:(nullable NSString *)contactId -{ - // Don't bother to cache avatar data. - CNContact *_Nullable cnContact = [self cnContactWithId:contactId]; - return [Contact avatarDataForCNContact:cnContact]; -} - -- (nullable UIImage *)avatarImageForCNContactId:(nullable NSString *)contactId -{ - OWSAssertDebug(self.cnContactAvatarCache); - - if (!contactId) { - return nil; - } - - UIImage *_Nullable avatarImage; - @synchronized(self.cnContactAvatarCache) { - avatarImage = [self.cnContactAvatarCache objectForKey:contactId]; - if (!avatarImage) { - NSData *_Nullable avatarData = [self avatarDataForCNContactId:contactId]; - if (avatarData && [avatarData ows_isValidImage]) { - avatarImage = [UIImage imageWithData:avatarData]; - } - if (avatarImage) { - [self.cnContactAvatarCache setObject:avatarImage forKey:contactId]; - } - } - } - - return avatarImage; -} - -#pragma mark - SystemContactsFetcherDelegate - -- (void)systemContactsFetcher:(SystemContactsFetcher *)systemsContactsFetcher - updatedContacts:(NSArray *)contacts - isUserRequested:(BOOL)isUserRequested -{ - BOOL shouldClearStaleCache = YES; - [self updateWithContacts:contacts isUserRequested:isUserRequested shouldClearStaleCache:shouldClearStaleCache]; -} - -- (void)systemContactsFetcher:(SystemContactsFetcher *)systemContactsFetcher - hasAuthorizationStatus:(enum ContactStoreAuthorizationStatus)authorizationStatus -{ - if (authorizationStatus == ContactStoreAuthorizationStatusRestricted - || authorizationStatus == ContactStoreAuthorizationStatusDenied) { - // Clear the contacts cache if access to the system contacts is revoked. - [self updateWithContacts:@[] isUserRequested:NO shouldClearStaleCache:YES]; - } -} - -#pragma mark - Intersection - -- (NSSet *)recipientIdsForIntersectionWithContacts:(NSArray *)contacts -{ - OWSAssertDebug(contacts); - - NSMutableSet *recipientIds = [NSMutableSet set]; - - for (Contact *contact in contacts) { - for (PhoneNumber *phoneNumber in contact.parsedPhoneNumbers) { - [recipientIds addObject:phoneNumber.toE164]; - } - } - - return recipientIds; -} - -- (void)intersectContacts:(NSArray *)contacts - isUserRequested:(BOOL)isUserRequested - completion:(void (^)(NSError *_Nullable error))completion -{ - OWSAssertDebug(contacts); - OWSAssertDebug(completion); - - dispatch_async(self.serialQueue, ^{ - __block BOOL isFullIntersection = YES; - __block NSSet *allContactRecipientIds; - __block NSSet *recipientIdsForIntersection; - [self.dbReadConnection readWithBlock:^(YapDatabaseReadTransaction *transaction) { - // Contact updates initiated by the user should always do a full intersection. - if (!isUserRequested) { - NSDate *_Nullable nextFullIntersectionDate = - [transaction dateForKey:OWSContactsManagerKeyNextFullIntersectionDate - inCollection:OWSContactsManagerCollection]; - if (nextFullIntersectionDate && [nextFullIntersectionDate isAfterNow]) { - isFullIntersection = NO; - } - } - - allContactRecipientIds = [self recipientIdsForIntersectionWithContacts:contacts]; - recipientIdsForIntersection = allContactRecipientIds; - - if (!isFullIntersection) { - // Do a "delta" intersection instead of a "full" intersection: - // only intersect new contacts which were not in the last successful - // "full" intersection. - NSSet *_Nullable lastKnownContactPhoneNumbers = - [transaction objectForKey:OWSContactsManagerKeyLastKnownContactPhoneNumbers - inCollection:OWSContactsManagerCollection]; - if (lastKnownContactPhoneNumbers) { - // Do a "delta" sync which only intersects recipient ids not included - // in the last full intersection. - NSMutableSet *newRecipientIds = [allContactRecipientIds mutableCopy]; - [newRecipientIds minusSet:lastKnownContactPhoneNumbers]; - recipientIdsForIntersection = newRecipientIds; - } else { - // Without a list of "last known" contact phone numbers, we'll have to do a full intersection. - isFullIntersection = YES; - } - } - }]; - OWSAssertDebug(recipientIdsForIntersection); - - if (recipientIdsForIntersection.count < 1) { - OWSLogInfo(@"Skipping intersection; no contacts to intersect."); - dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ - completion(nil); - }); - return; - } else if (isFullIntersection) { - OWSLogInfo(@"Doing full intersection with %zu contacts.", recipientIdsForIntersection.count); - } else { - OWSLogInfo(@"Doing delta intersection with %zu contacts.", recipientIdsForIntersection.count); - } - - [self intersectContacts:recipientIdsForIntersection - retryDelaySeconds:1.0 - success:^(NSSet *registeredRecipients) { - [self markIntersectionAsComplete:allContactRecipientIds isFullIntersection:isFullIntersection]; - - completion(nil); - } - failure:^(NSError *error) { - completion(error); - }]; - }); -} - -- (void)markIntersectionAsComplete:(NSSet *)recipientIdsForIntersection - isFullIntersection:(BOOL)isFullIntersection -{ - OWSAssertDebug(recipientIdsForIntersection.count > 0); - - dispatch_async(self.serialQueue, ^{ - [LKStorage writeSyncWithBlock:^(YapDatabaseReadWriteTransaction *transaction) { - [transaction setObject:recipientIdsForIntersection - forKey:OWSContactsManagerKeyLastKnownContactPhoneNumbers - inCollection:OWSContactsManagerCollection]; - - if (isFullIntersection) { - // Don't do a full intersection more often than once every 6 hours. - const NSTimeInterval kMinFullIntersectionInterval = 6 * kHourInterval; - NSDate *nextFullIntersectionDate = [NSDate - dateWithTimeIntervalSince1970:[NSDate new].timeIntervalSince1970 + kMinFullIntersectionInterval]; - [transaction setDate:nextFullIntersectionDate - forKey:OWSContactsManagerKeyNextFullIntersectionDate - inCollection:OWSContactsManagerCollection]; - } - }]; - }); -} - -- (void)intersectContacts:(NSSet *)recipientIds - retryDelaySeconds:(double)retryDelaySeconds - success:(void (^)(NSSet *))successParameter - failure:(void (^)(NSError *))failureParameter -{ - OWSAssertDebug(recipientIds.count > 0); - OWSAssertDebug(retryDelaySeconds > 0); - OWSAssertDebug(successParameter); - OWSAssertDebug(failureParameter); - - void (^success)(NSArray *) = ^(NSArray *registeredRecipientIds) { - OWSLogInfo(@"Successfully intersected contacts."); - successParameter([NSSet setWithArray:registeredRecipientIds]); - }; - void (^failure)(NSError *) = ^(NSError *error) { - if ([error.domain isEqualToString:OWSSignalServiceKitErrorDomain] - && error.code == OWSErrorCodeContactsUpdaterRateLimit) { - OWSLogError(@"Contact intersection hit rate limit with error: %@", error); - failureParameter(error); - return; - } - - OWSLogWarn(@"Failed to intersect contacts with error: %@. Rescheduling", error); - - // Retry with exponential backoff. - // - // TODO: Abort if another contact intersection succeeds in the meantime. - dispatch_after( - dispatch_time(DISPATCH_TIME_NOW, (int64_t)(retryDelaySeconds * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{ - [self intersectContacts:recipientIds - retryDelaySeconds:retryDelaySeconds * 2.0 - success:successParameter - failure:failureParameter]; - }); - }; - [[ContactsUpdater sharedUpdater] lookupIdentifiers:recipientIds.allObjects success:success failure:failure]; -} - -- (void)startObserving -{ - [[NSNotificationCenter defaultCenter] addObserver:self - selector:@selector(otherUsersProfileWillChange:) - name:kNSNotificationName_OtherUsersProfileWillChange - object:nil]; -} - -- (void)otherUsersProfileWillChange:(NSNotification *)notification -{ - OWSAssertIsOnMainThread(); - - [AppReadiness runNowOrWhenAppDidBecomeReady:^{ - NSString *recipientId = notification.userInfo[kNSNotificationKey_ProfileRecipientId]; - OWSAssertDebug(recipientId.length > 0); - - [self.avatarCache removeAllImagesForKey:recipientId]; - }]; -} - -- (void)updateWithContacts:(NSArray *)contacts - isUserRequested:(BOOL)isUserRequested - shouldClearStaleCache:(BOOL)shouldClearStaleCache -{ - dispatch_async(self.serialQueue, ^{ - NSMutableDictionary *allContactsMap = [NSMutableDictionary new]; - for (Contact *contact in contacts) { - for (PhoneNumber *phoneNumber in contact.parsedPhoneNumbers) { - NSString *phoneNumberE164 = phoneNumber.toE164; - if (phoneNumberE164.length > 0) { - allContactsMap[phoneNumberE164] = contact; - } - } - } - - dispatch_async(dispatch_get_main_queue(), ^{ - self.allContacts = contacts; - self.allContactsMap = [allContactsMap copy]; - [self.cnContactCache removeAllObjects]; - [self.cnContactAvatarCache removeAllObjects]; - - [self.avatarCache removeAllImages]; - - [self intersectContacts:contacts - isUserRequested:isUserRequested - completion:^(NSError *_Nullable error) { - // TODO: Should we do this on error? - [self buildSignalAccountsAndClearStaleCache:shouldClearStaleCache]; - }]; - }); - }); -} - -- (void)buildSignalAccountsAndClearStaleCache:(BOOL)shouldClearStaleCache -{ - dispatch_async(self.serialQueue, ^{ - NSMutableArray *signalAccounts = [NSMutableArray new]; - NSArray *contacts = self.allContacts; - - // We use a transaction only to load the SignalRecipients for each contact, - // in order to avoid database deadlock. - NSMutableDictionary *> *contactIdToSignalRecipientsMap = - [NSMutableDictionary new]; - [self.dbReadConnection readWithBlock:^(YapDatabaseReadTransaction *transaction) { - for (Contact *contact in contacts) { - NSArray *signalRecipients = [contact signalRecipientsWithTransaction:transaction]; - contactIdToSignalRecipientsMap[contact.uniqueId] = signalRecipients; - } - }]; - - NSMutableSet *seenRecipientIds = [NSMutableSet new]; - for (Contact *contact in contacts) { - NSArray *signalRecipients = contactIdToSignalRecipientsMap[contact.uniqueId]; - for (SignalRecipient *signalRecipient in [signalRecipients sortedArrayUsingSelector:@selector((compare:))]) { - if ([seenRecipientIds containsObject:signalRecipient.recipientId]) { - OWSLogDebug(@"Ignoring duplicate contact: %@, %@", signalRecipient.recipientId, contact.fullName); - continue; - } - [seenRecipientIds addObject:signalRecipient.recipientId]; - - SignalAccount *signalAccount = [[SignalAccount alloc] initWithSignalRecipient:signalRecipient]; - signalAccount.contact = contact; - if (signalRecipients.count > 1) { - signalAccount.hasMultipleAccountContact = YES; - signalAccount.multipleAccountLabelText = - [[self class] accountLabelForContact:contact recipientId:signalRecipient.recipientId]; - } - [signalAccounts addObject:signalAccount]; - } - } - - NSMutableDictionary *oldSignalAccounts = [NSMutableDictionary new]; - [self.dbReadConnection readWithBlock:^(YapDatabaseReadTransaction *transaction) { - [SignalAccount - enumerateCollectionObjectsWithTransaction:transaction - usingBlock:^(id _Nonnull object, BOOL *_Nonnull stop) { - OWSAssertDebug([object isKindOfClass:[SignalAccount class]]); - SignalAccount *oldSignalAccount = (SignalAccount *)object; - - oldSignalAccounts[oldSignalAccount.uniqueId] = oldSignalAccount; - }]; - }]; - - NSMutableArray *accountsToSave = [NSMutableArray new]; - for (SignalAccount *signalAccount in signalAccounts) { - SignalAccount *_Nullable oldSignalAccount = oldSignalAccounts[signalAccount.uniqueId]; - - // keep track of which accounts are still relevant, so we can clean up orphans - [oldSignalAccounts removeObjectForKey:signalAccount.uniqueId]; - - if (oldSignalAccount == nil) { - // new Signal Account - [accountsToSave addObject:signalAccount]; - continue; - } - - if ([oldSignalAccount isEqual:signalAccount]) { - // Same value, no need to save. - continue; - } - - // value changed, save account - [accountsToSave addObject:signalAccount]; - } - - // Update cached SignalAccounts on disk - [LKStorage writeSyncWithBlock:^(YapDatabaseReadWriteTransaction *_Nonnull transaction) { - OWSLogInfo(@"Saving %lu SignalAccounts", (unsigned long)accountsToSave.count); - for (SignalAccount *signalAccount in accountsToSave) { - OWSLogVerbose(@"Saving SignalAccount: %@", signalAccount); - [signalAccount saveWithTransaction:transaction]; - } - - if (shouldClearStaleCache) { - OWSLogInfo(@"Removing %lu old SignalAccounts.", (unsigned long)oldSignalAccounts.count); - for (SignalAccount *signalAccount in oldSignalAccounts.allValues) { - OWSLogVerbose(@"Removing old SignalAccount: %@", signalAccount); - [signalAccount removeWithTransaction:transaction]; - } - } else { - // In theory we want to remove SignalAccounts if the user deletes the corresponding system contact. - // However, as of iOS11.2 CNContactStore occasionally gives us only a subset of the system contacts. - // Because of that, it's not safe to clear orphaned accounts. - // Because we still want to give users a way to clear their stale accounts, if they pull-to-refresh - // their contacts we'll clear the cached ones. - // RADAR: https://bugreport.apple.com/web/?problemID=36082946 - if (oldSignalAccounts.allValues.count > 0) { - OWSLogWarn(@"NOT Removing %lu old SignalAccounts.", (unsigned long)oldSignalAccounts.count); - for (SignalAccount *signalAccount in oldSignalAccounts.allValues) { - OWSLogVerbose(@"Ensuring old SignalAccount is not inadvertently lost: %@", signalAccount); - [signalAccounts addObject:signalAccount]; - } - - // re-sort signal accounts since we've appended some orphans - [signalAccounts sortUsingComparator:self.signalAccountComparator]; - } - } - }]; - - dispatch_async(dispatch_get_main_queue(), ^{ - [self updateSignalAccounts:signalAccounts]; - }); - }); -} - -- (void)updateSignalAccounts:(NSArray *)signalAccounts -{ - OWSAssertIsOnMainThread(); - - if ([signalAccounts isEqual:self.signalAccounts]) { - OWSLogDebug(@"SignalAccounts unchanged."); - return; - } - - NSMutableDictionary *signalAccountMap = [NSMutableDictionary new]; - for (SignalAccount *signalAccount in signalAccounts) { - signalAccountMap[signalAccount.recipientId] = signalAccount; - } - - self.signalAccountMap = [signalAccountMap copy]; - self.signalAccounts = [signalAccounts copy]; - [self.profileManager setContactRecipientIds:signalAccountMap.allKeys]; - - self.isSetup = YES; - - [[NSNotificationCenter defaultCenter] - postNotificationNameAsync:OWSContactsManagerSignalAccountsDidChangeNotification - object:nil]; -} - -// TODO dependency inject, avoid circular dependencies. -- (OWSProfileManager *)profileManager -{ - return [OWSProfileManager sharedManager]; -} - -- (NSString *_Nullable)cachedContactNameForRecipientId:(NSString *)recipientId -{ - SignalAccount *_Nullable signalAccount = [self fetchSignalAccountForRecipientId:recipientId]; - return [self cachedContactNameForRecipientId:recipientId signalAccount:signalAccount]; -} - -- (NSString *_Nullable)cachedContactNameForRecipientId:(NSString *)recipientId - transaction:(YapDatabaseReadTransaction *)transaction -{ - OWSAssertDebug(recipientId.length > 0); - OWSAssertDebug(transaction); - - SignalAccount *_Nullable signalAccount = - [self fetchSignalAccountForRecipientId:recipientId transaction:transaction]; - return [self cachedContactNameForRecipientId:recipientId signalAccount:signalAccount]; -} - -- (NSString *_Nullable)cachedContactNameForRecipientId:(NSString *)recipientId - signalAccount:(nullable SignalAccount *)signalAccount -{ - OWSAssertDebug(recipientId.length > 0); - - if (!signalAccount) { - // search system contacts for no-longer-registered signal users, for which there will be no SignalAccount - Contact *_Nullable nonSignalContact = self.allContactsMap[recipientId]; - if (!nonSignalContact) { - return nil; - } - return nonSignalContact.fullName; - } - - NSString *fullName = signalAccount.contactFullName; - if (fullName.length == 0) { - return nil; - } - - NSString *multipleAccountLabelText = signalAccount.multipleAccountLabelText; - if (multipleAccountLabelText.length == 0) { - return fullName; - } - - return [NSString stringWithFormat:@"%@ (%@)", fullName, multipleAccountLabelText]; -} - -- (NSString *_Nullable)cachedFirstNameForRecipientId:(NSString *)recipientId -{ - OWSAssertDebug(recipientId.length > 0); - - SignalAccount *_Nullable signalAccount = [self fetchSignalAccountForRecipientId:recipientId]; - return signalAccount.contact.firstName.filterStringForDisplay; -} - -- (NSString *_Nullable)cachedLastNameForRecipientId:(NSString *)recipientId -{ - OWSAssertDebug(recipientId.length > 0); - - SignalAccount *_Nullable signalAccount = [self fetchSignalAccountForRecipientId:recipientId]; - return signalAccount.contact.lastName.filterStringForDisplay; -} - -#pragma mark - View Helpers - -// TODO move into Contact class. -+ (NSString *)accountLabelForContact:(Contact *)contact recipientId:(NSString *)recipientId -{ - OWSAssertDebug(contact); - OWSAssertDebug(recipientId.length > 0); - OWSAssertDebug([contact.textSecureIdentifiers containsObject:recipientId]); - - if (contact.textSecureIdentifiers.count <= 1) { - return nil; - } - - // 1. Find the phone number type of this account. - NSString *phoneNumberLabel = [contact nameForPhoneNumber:recipientId]; - - // 2. Find all phone numbers for this contact of the same type. - NSMutableArray *phoneNumbersWithTheSameName = [NSMutableArray new]; - for (NSString *textSecureIdentifier in contact.textSecureIdentifiers) { - if ([phoneNumberLabel isEqualToString:[contact nameForPhoneNumber:textSecureIdentifier]]) { - [phoneNumbersWithTheSameName addObject:textSecureIdentifier]; - } - } - - OWSAssertDebug([phoneNumbersWithTheSameName containsObject:recipientId]); - if (phoneNumbersWithTheSameName.count > 1) { - NSUInteger index = - [[phoneNumbersWithTheSameName sortedArrayUsingSelector:@selector((compare:))] indexOfObject:recipientId]; - NSString *indexText = [OWSFormat formatInt:(int)index + 1]; - phoneNumberLabel = - [NSString stringWithFormat:NSLocalizedString(@"PHONE_NUMBER_TYPE_AND_INDEX_NAME_FORMAT", - @"Format for phone number label with an index. Embeds {{Phone number label " - @"(e.g. 'home')}} and {{index, e.g. 2}}."), - phoneNumberLabel, - indexText]; - } - - return phoneNumberLabel.filterStringForDisplay; -} - -- (BOOL)phoneNumber:(PhoneNumber *)phoneNumber1 matchesNumber:(PhoneNumber *)phoneNumber2 -{ - return [phoneNumber1.toE164 isEqualToString:phoneNumber2.toE164]; -} - -#pragma mark - Whisper User Management - -- (BOOL)isSystemContact:(NSString *)recipientId -{ - OWSAssertDebug(recipientId.length > 0); - - return self.allContactsMap[recipientId] != nil; -} - -- (BOOL)isSystemContactWithSignalAccount:(NSString *)recipientId -{ - OWSAssertDebug(recipientId.length > 0); - - return [self hasSignalAccountForRecipientId:recipientId]; -} - -- (BOOL)hasNameInSystemContactsForRecipientId:(NSString *)recipientId -{ - return [self cachedContactNameForRecipientId:recipientId].length > 0; -} - -- (NSString *)unknownContactName -{ - return NSLocalizedString( - @"UNKNOWN_CONTACT_NAME", @"Displayed if for some reason we can't determine a contacts phone number *or* name"); -} - -- (nullable NSString *)formattedProfileNameForRecipientId:(NSString *)recipientId -{ - NSString *_Nullable profileName = [self.profileManager profileNameForRecipientWithID:recipientId]; - if (profileName.length == 0) { - return nil; - } - - NSString *profileNameFormatString = NSLocalizedString(@"PROFILE_NAME_LABEL_FORMAT", - @"Prepend a simple marker to differentiate the profile name, embeds the contact's {{profile name}}."); - - return profileName; -} - -- (nullable NSString *)profileNameForRecipientId:(NSString *)recipientId -{ - return [self.profileManager profileNameForRecipientWithID:recipientId]; -} - -- (nullable NSString *)nameFromSystemContactsForRecipientId:(NSString *)recipientId -{ - return [self cachedContactNameForRecipientId:recipientId]; -} - -- (nullable NSString *)nameFromSystemContactsForRecipientId:(NSString *)recipientId - transaction:(YapDatabaseReadTransaction *)transaction -{ - OWSAssertDebug(recipientId.length > 0); - OWSAssertDebug(transaction); - - return [self cachedContactNameForRecipientId:recipientId transaction:transaction]; -} - -- (NSString *)displayNameForPhoneIdentifier:(NSString *_Nullable)recipientId -{ - OWSAssertDebug(recipientId.length > 0); - - if (!recipientId) { - return self.unknownContactName; - } - - NSString *_Nullable displayName = [self nameFromSystemContactsForRecipientId:recipientId]; - - // Fall back to just using their recipientId - if (displayName.length < 1) { - displayName = recipientId; - } - - return displayName; -} - -- (NSString *)displayNameForPhoneIdentifier:(NSString *_Nullable)recipientId - transaction:(YapDatabaseReadTransaction *)transaction -{ - OWSAssertDebug(recipientId.length > 0); - OWSAssertDebug(transaction); - - if (!recipientId) { - return self.unknownContactName; - } - - NSString *_Nullable displayName = [self nameFromSystemContactsForRecipientId:recipientId transaction:transaction]; - - // Fall back to just using their recipientId - if (displayName.length < 1) { - displayName = recipientId; - } - - return displayName; -} - -- (NSString *_Nonnull)displayNameForSignalAccount:(SignalAccount *)signalAccount -{ - OWSAssertDebug(signalAccount); - - return [self displayNameForPhoneIdentifier:signalAccount.recipientId]; -} - -- (NSAttributedString *_Nonnull)formattedDisplayNameForSignalAccount:(SignalAccount *)signalAccount font:(UIFont *)font -{ - OWSAssertDebug(signalAccount); - OWSAssertDebug(font); - - return [self formattedFullNameForRecipientId:signalAccount.recipientId font:font]; -} - -- (NSAttributedString *)formattedFullNameForRecipientId:(NSString *)recipientId font:(UIFont *)font -{ - OWSAssertDebug(recipientId.length > 0); - OWSAssertDebug(font); - - UIFont *boldFont = [UIFont ows_mediumFontWithSize:font.pointSize]; - - NSDictionary *boldFontAttributes = - @{ NSFontAttributeName : boldFont, NSForegroundColorAttributeName : [Theme boldColor] }; - NSDictionary *normalFontAttributes = - @{ NSFontAttributeName : font, NSForegroundColorAttributeName : [Theme primaryColor] }; - NSDictionary *firstNameAttributes - = (self.shouldSortByGivenName ? boldFontAttributes : normalFontAttributes); - NSDictionary *lastNameAttributes - = (self.shouldSortByGivenName ? normalFontAttributes : boldFontAttributes); - - NSString *cachedFirstName = [self cachedFirstNameForRecipientId:recipientId]; - NSString *cachedLastName = [self cachedLastNameForRecipientId:recipientId]; - - NSMutableAttributedString *formattedName = [NSMutableAttributedString new]; - - if (cachedFirstName.length > 0 && cachedLastName.length > 0) { - NSAttributedString *firstName = - [[NSAttributedString alloc] initWithString:cachedFirstName attributes:firstNameAttributes]; - NSAttributedString *lastName = - [[NSAttributedString alloc] initWithString:cachedLastName attributes:lastNameAttributes]; - - NSString *_Nullable cnContactId = self.allContactsMap[recipientId].cnContactId; - CNContact *_Nullable cnContact = [self cnContactWithId:cnContactId]; - if (!cnContact) { - // If we don't have a CNContact for this recipient id, make one. - // Presumably [CNContactFormatter nameOrderForContact:] tries - // to localizes its result based on the languages/scripts used - // in the contact's fields. - CNMutableContact *formatContact = [CNMutableContact new]; - formatContact.givenName = firstName.string; - formatContact.familyName = lastName.string; - cnContact = formatContact; - } - CNContactDisplayNameOrder nameOrder = [CNContactFormatter nameOrderForContact:cnContact]; - NSAttributedString *_Nullable leftName, *_Nullable rightName; - if (nameOrder == CNContactDisplayNameOrderGivenNameFirst) { - leftName = firstName; - rightName = lastName; - } else { - leftName = lastName; - rightName = firstName; - } - - [formattedName appendAttributedString:leftName]; - [formattedName - appendAttributedString:[[NSAttributedString alloc] initWithString:@" " attributes:normalFontAttributes]]; - [formattedName appendAttributedString:rightName]; - } else if (cachedFirstName.length > 0) { - [formattedName appendAttributedString:[[NSAttributedString alloc] initWithString:cachedFirstName - attributes:firstNameAttributes]]; - } else if (cachedLastName.length > 0) { - [formattedName appendAttributedString:[[NSAttributedString alloc] initWithString:cachedLastName - attributes:lastNameAttributes]]; - } else { - // Else, fall back to using just their recipientId - NSString *phoneString = - [PhoneNumber bestEffortFormatPartialUserSpecifiedTextToLookLikeAPhoneNumber:recipientId]; - return [[NSAttributedString alloc] initWithString:phoneString attributes:normalFontAttributes]; - } - - // Append unique label for contacts with multiple Signal accounts - SignalAccount *_Nullable signalAccount = [self fetchSignalAccountForRecipientId:recipientId]; - if (signalAccount && signalAccount.multipleAccountLabelText) { - OWSAssertDebug(signalAccount.multipleAccountLabelText.length > 0); - - [formattedName - appendAttributedString:[[NSAttributedString alloc] initWithString:@" (" attributes:normalFontAttributes]]; - [formattedName - appendAttributedString:[[NSAttributedString alloc] initWithString:signalAccount.multipleAccountLabelText - attributes:normalFontAttributes]]; - [formattedName - appendAttributedString:[[NSAttributedString alloc] initWithString:@")" attributes:normalFontAttributes]]; - } - - return formattedName; -} - -- (NSString *)contactOrProfileNameForPhoneIdentifier:(NSString *)recipientId -{ - // Prefer a saved name from system contacts, if available - NSString *_Nullable savedContactName = [self cachedContactNameForRecipientId:recipientId]; - if (savedContactName.length > 0) { - return savedContactName; - } - - NSString *_Nullable profileName = [self.profileManager profileNameForRecipientWithID:recipientId]; - if (profileName.length > 0) { - NSString *numberAndProfileNameFormat = NSLocalizedString(@"PROFILE_NAME_AND_PHONE_NUMBER_LABEL_FORMAT", - @"Label text combining the phone number and profile name separated by a simple demarcation character. " - @"Phone number should be most prominent. '%1$@' is replaced with {{phone number}} and '%2$@' is replaced " - @"with {{profile name}}"); - - NSString *numberAndProfileName = - [NSString stringWithFormat:numberAndProfileNameFormat, recipientId, profileName]; - return numberAndProfileName; - } - - // else fall back to recipient id - return recipientId; -} - -- (NSAttributedString *)attributedContactOrProfileNameForPhoneIdentifier:(NSString *)recipientId -{ - return [[NSAttributedString alloc] initWithString:[self contactOrProfileNameForPhoneIdentifier:recipientId]]; -} - -- (NSAttributedString *)attributedContactOrProfileNameForPhoneIdentifier:(NSString *)recipientId - primaryFont:(UIFont *)primaryFont - secondaryFont:(UIFont *)secondaryFont -{ - OWSAssertDebug(primaryFont); - OWSAssertDebug(secondaryFont); - - return [self attributedContactOrProfileNameForPhoneIdentifier:(NSString *)recipientId - primaryAttributes:@{ - NSFontAttributeName : primaryFont, - } - secondaryAttributes:@{ - NSFontAttributeName : secondaryFont, - }]; -} - -- (NSAttributedString *)attributedContactOrProfileNameForPhoneIdentifier:(NSString *)recipientId - primaryAttributes:(NSDictionary *)primaryAttributes - secondaryAttributes:(NSDictionary *)secondaryAttributes -{ - OWSAssertDebug(primaryAttributes.count > 0); - OWSAssertDebug(secondaryAttributes.count > 0); - - // Prefer a saved name from system contacts, if available - NSString *_Nullable savedContactName = [self cachedContactNameForRecipientId:recipientId]; - if (savedContactName.length > 0) { - return [[NSAttributedString alloc] initWithString:savedContactName attributes:primaryAttributes]; - } - - NSString *_Nullable profileName = [self.profileManager profileNameForRecipientWithID:recipientId]; - if (profileName.length > 0) { - return [[NSAttributedString alloc] initWithString:profileName]; - // Loki: Original code - // ======== -// NSAttributedString *result = -// [[NSAttributedString alloc] initWithString:recipientId attributes:primaryAttributes]; -// result = [result rtlSafeAppend:[[NSAttributedString alloc] initWithString:@" "]]; -// result = [result rtlSafeAppend:[[NSAttributedString alloc] initWithString:@"~" attributes:secondaryAttributes]]; -// result = [result -// rtlSafeAppend:[[NSAttributedString alloc] initWithString:profileName attributes:secondaryAttributes]]; -// return [result copy]; - // ======== - } - - // else fall back to recipient id - return [[NSAttributedString alloc] initWithString:recipientId attributes:primaryAttributes]; -} - -// TODO refactor attributed counterparts to use this as a helper method? -- (NSString *)stringForConversationTitleWithPhoneIdentifier:(NSString *)recipientId -{ - // Prefer a saved name from system contacts, if available - NSString *_Nullable savedContactName = [self cachedContactNameForRecipientId:recipientId]; - if (savedContactName.length > 0) { - return savedContactName; - } - - NSString *formattedPhoneNumber = - [PhoneNumber bestEffortFormatPartialUserSpecifiedTextToLookLikeAPhoneNumber:recipientId]; - NSString *_Nullable profileName = [self.profileManager profileNameForRecipientWithID:recipientId]; - if (profileName.length > 0) { - NSString *numberAndProfileNameFormat = NSLocalizedString(@"PROFILE_NAME_AND_PHONE_NUMBER_LABEL_FORMAT", - @"Label text combining the phone number and profile name separated by a simple demarcation character. " - @"Phone number should be most prominent. '%1$@' is replaced with {{phone number}} and '%2$@' is replaced " - @"with {{profile name}}"); - - NSString *numberAndProfileName = - [NSString stringWithFormat:numberAndProfileNameFormat, formattedPhoneNumber, profileName]; - - return numberAndProfileName; - } - - // else fall back phone number - return formattedPhoneNumber; -} - -- (nullable SignalAccount *)fetchSignalAccountForRecipientId:(NSString *)recipientId -{ - OWSAssertDebug(recipientId.length > 0); - - __block SignalAccount *signalAccount = self.signalAccountMap[recipientId]; - - // If contact intersection hasn't completed, it might exist on disk - // even if it doesn't exist in memory yet. - if (!signalAccount) { - [self.dbReadConnection readWithBlock:^(YapDatabaseReadTransaction *transaction) { - signalAccount = [SignalAccount fetchObjectWithUniqueID:recipientId transaction:transaction]; - }]; - } - - return signalAccount; -} - -- (nullable SignalAccount *)fetchSignalAccountForRecipientId:(NSString *)recipientId - transaction:(YapDatabaseReadTransaction *)transaction -{ - OWSAssertDebug(recipientId.length > 0); - OWSAssertDebug(transaction); - - __block SignalAccount *signalAccount = self.signalAccountMap[recipientId]; - - // If contact intersection hasn't completed, it might exist on disk - // even if it doesn't exist in memory yet. - if (!signalAccount) { - signalAccount = [SignalAccount fetchObjectWithUniqueID:recipientId transaction:transaction]; - } - - return signalAccount; -} - -- (SignalAccount *)fetchOrBuildSignalAccountForRecipientId:(NSString *)recipientId -{ - OWSAssertDebug(recipientId.length > 0); - - SignalAccount *_Nullable signalAccount = [self fetchSignalAccountForRecipientId:recipientId]; - return (signalAccount ?: [[SignalAccount alloc] initWithRecipientId:recipientId]); -} - -- (BOOL)hasSignalAccountForRecipientId:(NSString *)recipientId -{ - return [self fetchSignalAccountForRecipientId:recipientId] != nil; -} - -- (UIImage *_Nullable)systemContactImageForPhoneIdentifier:(NSString *_Nullable)identifier -{ - if (identifier.length == 0) { - return nil; - } - - Contact *contact = self.allContactsMap[identifier]; - if (!contact) { - // If we haven't loaded system contacts yet, we may have a cached - // copy in the db - contact = [self fetchSignalAccountForRecipientId:identifier].contact; - } - - return [self avatarImageForCNContactId:contact.cnContactId]; -} - -- (nullable UIImage *)profileImageForPhoneIdentifier:(nullable NSString *)identifier -{ - if (identifier.length == 0) { - return nil; - } - - return [self.profileManager profileAvatarForRecipientId:identifier]; -} - -- (nullable NSData *)profileImageDataForPhoneIdentifier:(nullable NSString *)identifier -{ - if (identifier.length == 0) { - return nil; - } - - return [self.profileManager profileAvatarDataForRecipientId:identifier]; -} - -- (UIImage *_Nullable)imageForPhoneIdentifier:(NSString *_Nullable)identifier -{ - if (identifier.length == 0) { - return nil; - } - - // Prefer the contact image from the local address book if available - UIImage *_Nullable image = [self systemContactImageForPhoneIdentifier:identifier]; - - // Else try to use the image from their profile - if (image == nil) { - image = [self profileImageForPhoneIdentifier:identifier]; - } - - return image; -} - -- (NSComparisonResult)compareSignalAccount:(SignalAccount *)left withSignalAccount:(SignalAccount *)right -{ - return self.signalAccountComparator(left, right); -} - -- (NSComparisonResult (^)(SignalAccount *left, SignalAccount *right))signalAccountComparator -{ - return ^NSComparisonResult(SignalAccount *left, SignalAccount *right) { - NSString *leftName = [self comparableNameForSignalAccount:left]; - NSString *rightName = [self comparableNameForSignalAccount:right]; - - NSComparisonResult nameComparison = [leftName caseInsensitiveCompare:rightName]; - if (nameComparison == NSOrderedSame) { - return [left.recipientId compare:right.recipientId]; - } - - return nameComparison; - }; -} - -- (BOOL)shouldSortByGivenName -{ - return [[CNContactsUserDefaults sharedDefaults] sortOrder] == CNContactSortOrderGivenName; -} - -- (NSString *)comparableNameForSignalAccount:(SignalAccount *)signalAccount -{ - NSString *_Nullable name; - if (signalAccount.contact) { - if (self.shouldSortByGivenName) { - name = signalAccount.contact.comparableNameFirstLast; - } else { - name = signalAccount.contact.comparableNameLastFirst; - } - } - - if (name.length < 1) { - name = signalAccount.recipientId; - } - - return name; -} - -NS_ASSUME_NONNULL_END - -@end diff --git a/SignalUtilitiesKit/OWSContactsOutputStream.m b/SignalUtilitiesKit/OWSContactsOutputStream.m index 722641472..9162b76e0 100644 --- a/SignalUtilitiesKit/OWSContactsOutputStream.m +++ b/SignalUtilitiesKit/OWSContactsOutputStream.m @@ -3,7 +3,7 @@ // #import "OWSContactsOutputStream.h" -#import "Contact.h" + #import "ContactsManagerProtocol.h" #import "MIMETypeUtil.h" #import "NSData+keyVersionByte.h" @@ -47,27 +47,6 @@ disappearingMessagesConfiguration:(nullable OWSDisappearingMessagesConfiguration contactBuilder.verified = verified; } - /* - UIImage *_Nullable rawAvatar = [contactsManager avatarImageForCNContactId:signalAccount.contact.cnContactId]; - NSData *_Nullable avatarPng; - if (rawAvatar) { - avatarPng = UIImagePNGRepresentation(rawAvatar); - if (avatarPng) { - SSKProtoContactDetailsAvatarBuilder *avatarBuilder = [SSKProtoContactDetailsAvatar builder]; - [avatarBuilder setContentType:OWSMimeTypeImagePng]; - [avatarBuilder setLength:(uint32_t)avatarPng.length]; - - NSError *error; - SSKProtoContactDetailsAvatar *_Nullable avatar = [avatarBuilder buildAndReturnError:&error]; - if (error || !avatar) { - OWSLogError(@"could not build protobuf: %@", error); - return; - } - [contactBuilder setAvatar:avatar]; - } - } - */ - if (profileKeyData) { OWSAssertDebug(profileKeyData.length == kAES256_KeyByteLength); [contactBuilder setProfileKey:profileKeyData]; @@ -96,12 +75,6 @@ disappearingMessagesConfiguration:(nullable OWSDisappearingMessagesConfiguration uint32_t contactDataLength = (uint32_t)contactData.length; [self writeUInt32:contactDataLength]; [self writeData:contactData]; - - /* - if (avatarPng) { - [self writeData:avatarPng]; - } - */ } @end diff --git a/SignalUtilitiesKit/OWSConversationColor.h b/SignalUtilitiesKit/OWSConversationColor.h deleted file mode 100644 index 0af19bf4b..000000000 --- a/SignalUtilitiesKit/OWSConversationColor.h +++ /dev/null @@ -1,82 +0,0 @@ -// -// Copyright (c) 2018 Open Whisper Systems. All rights reserved. -// - -#import -#import - -NS_ASSUME_NONNULL_BEGIN - -@interface OWSConversationColor : NSObject - -@property (nonatomic, readonly) ConversationColorName name; -@property (nonatomic, readonly) UIColor *primaryColor; -@property (nonatomic, readonly) UIColor *shadeColor; -@property (nonatomic, readonly) UIColor *tintColor; - -@property (nonatomic, readonly) UIColor *themeColor; - -+ (OWSConversationColor *)conversationColorWithName:(ConversationColorName)name - primaryColor:(UIColor *)primaryColor - shadeColor:(UIColor *)shadeColor - tintColor:(UIColor *)tintColor; -#pragma mark - Conversation Colors - -@property (class, readonly, nonatomic) UIColor *ows_crimsonColor; -@property (class, readonly, nonatomic) UIColor *ows_vermilionColor; -@property (class, readonly, nonatomic) UIColor *ows_burlapColor; -@property (class, readonly, nonatomic) UIColor *ows_forestColor; -@property (class, readonly, nonatomic) UIColor *ows_wintergreenColor; -@property (class, readonly, nonatomic) UIColor *ows_tealColor; -@property (class, readonly, nonatomic) UIColor *ows_blueColor; -@property (class, readonly, nonatomic) UIColor *ows_indigoColor; -@property (class, readonly, nonatomic) UIColor *ows_violetColor; -@property (class, readonly, nonatomic) UIColor *ows_plumColor; -@property (class, readonly, nonatomic) UIColor *ows_taupeColor; -@property (class, readonly, nonatomic) UIColor *ows_steelColor; - -#pragma mark - Conversation Colors (Tint) - -@property (class, readonly, nonatomic) UIColor *ows_crimsonTintColor; -@property (class, readonly, nonatomic) UIColor *ows_vermilionTintColor; -@property (class, readonly, nonatomic) UIColor *ows_burlapTintColor; -@property (class, readonly, nonatomic) UIColor *ows_forestTintColor; -@property (class, readonly, nonatomic) UIColor *ows_wintergreenTintColor; -@property (class, readonly, nonatomic) UIColor *ows_tealTintColor; -@property (class, readonly, nonatomic) UIColor *ows_blueTintColor; -@property (class, readonly, nonatomic) UIColor *ows_indigoTintColor; -@property (class, readonly, nonatomic) UIColor *ows_violetTintColor; -@property (class, readonly, nonatomic) UIColor *ows_plumTintColor; -@property (class, readonly, nonatomic) UIColor *ows_taupeTintColor; -@property (class, readonly, nonatomic) UIColor *ows_steelTintColor; - -#pragma mark - Conversation Colors (Shade) - -@property (class, readonly, nonatomic) UIColor *ows_crimsonShadeColor; -@property (class, readonly, nonatomic) UIColor *ows_vermilionShadeColor; -@property (class, readonly, nonatomic) UIColor *ows_burlapShadeColor; -@property (class, readonly, nonatomic) UIColor *ows_forestShadeColor; -@property (class, readonly, nonatomic) UIColor *ows_wintergreenShadeColor; -@property (class, readonly, nonatomic) UIColor *ows_tealShadeColor; -@property (class, readonly, nonatomic) UIColor *ows_blueShadeColor; -@property (class, readonly, nonatomic) UIColor *ows_indigoShadeColor; -@property (class, readonly, nonatomic) UIColor *ows_violetShadeColor; -@property (class, readonly, nonatomic) UIColor *ows_plumShadeColor; -@property (class, readonly, nonatomic) UIColor *ows_taupeShadeColor; -@property (class, readonly, nonatomic) UIColor *ows_steelShadeColor; - -#pragma mark - Conversation Colors - -+ (nullable OWSConversationColor *)conversationColorForColorName:(ConversationColorName)colorName - NS_SWIFT_NAME(conversationColor(colorName:)); - -// If the conversation color name is valid, return its colors. -// Otherwise return the "default" conversation colors. -+ (OWSConversationColor *)conversationColorOrDefaultForColorName:(ConversationColorName)conversationColorName - NS_SWIFT_NAME(conversationColorOrDefault(colorName:)); - -@property (class, readonly, nonatomic) NSArray *conversationColorNames; - -@end - -NS_ASSUME_NONNULL_END diff --git a/SignalUtilitiesKit/OWSConversationColor.m b/SignalUtilitiesKit/OWSConversationColor.m deleted file mode 100644 index f53cff8a5..000000000 --- a/SignalUtilitiesKit/OWSConversationColor.m +++ /dev/null @@ -1,359 +0,0 @@ -// -// Copyright (c) 2018 Open Whisper Systems. All rights reserved. -// - -#import "OWSConversationColor.h" -#import "Theme.h" -#import "UIColor+OWS.h" -#import - -NS_ASSUME_NONNULL_BEGIN - -@interface OWSConversationColor () - -@property (nonatomic) ConversationColorName name; -@property (nonatomic) UIColor *primaryColor; -@property (nonatomic) UIColor *shadeColor; -@property (nonatomic) UIColor *tintColor; - -@end - -#pragma mark - - -@implementation OWSConversationColor - -+ (OWSConversationColor *)conversationColorWithName:(ConversationColorName)name - primaryColor:(UIColor *)primaryColor - shadeColor:(UIColor *)shadeColor - tintColor:(UIColor *)tintColor -{ - OWSConversationColor *instance = [OWSConversationColor new]; - instance.name = name; - instance.primaryColor = primaryColor; - instance.shadeColor = shadeColor; - instance.tintColor = tintColor; - return instance; -} - -#pragma mark - - -- (UIColor *)themeColor -{ - return Theme.isDarkThemeEnabled ? self.shadeColor : self.primaryColor; -} - -- (BOOL)isEqual:(id)other -{ - if (![other isKindOfClass:[OWSConversationColor class]]) { - return NO; - } - - OWSConversationColor *otherColor = (OWSConversationColor *)other; - return [self.name isEqual:otherColor.name]; -} - -#pragma mark - Conversation Color (Primary) - -+ (UIColor *)ows_crimsonColor -{ - return [UIColor colorWithRGBHex:0xCC163D]; -} - -+ (UIColor *)ows_vermilionColor -{ - return [UIColor colorWithRGBHex:0xC73800]; -} - -+ (UIColor *)ows_burlapColor -{ - return [UIColor colorWithRGBHex:0x746C53]; -} - -+ (UIColor *)ows_forestColor -{ - return [UIColor colorWithRGBHex:0x3B7845]; -} - -+ (UIColor *)ows_wintergreenColor -{ - return [UIColor colorWithRGBHex:0x1C8260]; -} - -+ (UIColor *)ows_tealColor -{ - return [UIColor colorWithRGBHex:0x067589]; -} - -+ (UIColor *)ows_blueColor -{ - return [UIColor colorWithRGBHex:0x336BA3]; -} - -+ (UIColor *)ows_indigoColor -{ - return [UIColor colorWithRGBHex:0x5951C8]; -} - -+ (UIColor *)ows_violetColor -{ - return [UIColor colorWithRGBHex:0x862CAF]; -} - -+ (UIColor *)ows_plumColor -{ - return [UIColor colorWithRGBHex:0xA23474]; -} - -+ (UIColor *)ows_taupeColor -{ - return [UIColor colorWithRGBHex:0x895D66]; -} - -+ (UIColor *)ows_steelColor -{ - return [UIColor colorWithRGBHex:0x6B6B78]; -} - -#pragma mark - Conversation Colors (Tint) - -+ (UIColor *)ows_crimsonTintColor -{ - return [UIColor colorWithRGBHex:0xEDA6AE]; -} - -+ (UIColor *)ows_vermilionTintColor -{ - return [UIColor colorWithRGBHex:0xEBA78E]; -} - -+ (UIColor *)ows_burlapTintColor -{ - return [UIColor colorWithRGBHex:0xC4B997]; -} - -+ (UIColor *)ows_forestTintColor -{ - return [UIColor colorWithRGBHex:0x8FCC9A]; -} - -+ (UIColor *)ows_wintergreenTintColor -{ - return [UIColor colorWithRGBHex:0x9BCFBD]; -} - -+ (UIColor *)ows_tealTintColor -{ - return [UIColor colorWithRGBHex:0xA5CAD5]; -} - -+ (UIColor *)ows_blueTintColor -{ - return [UIColor colorWithRGBHex:0xADC8E1]; -} - -+ (UIColor *)ows_indigoTintColor -{ - return [UIColor colorWithRGBHex:0xC2C1E7]; -} - -+ (UIColor *)ows_violetTintColor -{ - return [UIColor colorWithRGBHex:0xCDADDC]; -} - -+ (UIColor *)ows_plumTintColor -{ - return [UIColor colorWithRGBHex:0xDCB2CA]; -} - -+ (UIColor *)ows_taupeTintColor -{ - return [UIColor colorWithRGBHex:0xCFB5BB]; -} - -+ (UIColor *)ows_steelTintColor -{ - return [UIColor colorWithRGBHex:0xBEBEC6]; -} - -#pragma mark - Conversation Colors (Shade) - -+ (UIColor *)ows_crimsonShadeColor -{ - return [UIColor colorWithRGBHex:0x8A0F29]; -} - -+ (UIColor *)ows_vermilionShadeColor -{ - return [UIColor colorWithRGBHex:0x872600]; -} - -+ (UIColor *)ows_burlapShadeColor -{ - return [UIColor colorWithRGBHex:0x58513C]; -} - -+ (UIColor *)ows_forestShadeColor -{ - return [UIColor colorWithRGBHex:0x2B5934]; -} - -+ (UIColor *)ows_wintergreenShadeColor -{ - return [UIColor colorWithRGBHex:0x36544A]; -} - -+ (UIColor *)ows_tealShadeColor -{ - return [UIColor colorWithRGBHex:0x055968]; -} - -+ (UIColor *)ows_blueShadeColor -{ - return [UIColor colorWithRGBHex:0x285480]; -} - -+ (UIColor *)ows_indigoShadeColor -{ - return [UIColor colorWithRGBHex:0x4840A0]; -} - -+ (UIColor *)ows_violetShadeColor -{ - return [UIColor colorWithRGBHex:0x6B248A]; -} - -+ (UIColor *)ows_plumShadeColor -{ - return [UIColor colorWithRGBHex:0x881B5B]; -} - -+ (UIColor *)ows_taupeShadeColor -{ - return [UIColor colorWithRGBHex:0x6A4E54]; -} - -+ (UIColor *)ows_steelShadeColor -{ - return [UIColor colorWithRGBHex:0x5A5A63]; -} - -+ (NSArray *)allConversationColors -{ - static NSArray *allConversationColors; - static dispatch_once_t onceToken; - - dispatch_once(&onceToken, ^{ - // Order here affects the order in the conversation color picker. - allConversationColors = @[ - [OWSConversationColor conversationColorWithName:ConversationColorNameCrimson - primaryColor:self.ows_crimsonColor - shadeColor:self.ows_crimsonShadeColor - tintColor:self.ows_crimsonTintColor], - [OWSConversationColor conversationColorWithName:ConversationColorNameVermilion - primaryColor:self.ows_vermilionColor - shadeColor:self.ows_vermilionShadeColor - tintColor:self.ows_vermilionTintColor], - [OWSConversationColor conversationColorWithName:ConversationColorNameBurlap - primaryColor:self.ows_burlapColor - shadeColor:self.ows_burlapShadeColor - tintColor:self.ows_burlapTintColor], - [OWSConversationColor conversationColorWithName:ConversationColorNameForest - primaryColor:self.ows_forestColor - shadeColor:self.ows_forestShadeColor - tintColor:self.ows_forestTintColor], - [OWSConversationColor conversationColorWithName:ConversationColorNameWintergreen - primaryColor:self.ows_wintergreenColor - shadeColor:self.ows_wintergreenShadeColor - tintColor:self.ows_wintergreenTintColor], - [OWSConversationColor conversationColorWithName:ConversationColorNameTeal - primaryColor:self.ows_tealColor - shadeColor:self.ows_tealShadeColor - tintColor:self.ows_tealTintColor], - [OWSConversationColor conversationColorWithName:ConversationColorNameBlue - primaryColor:self.ows_blueColor - shadeColor:self.ows_blueShadeColor - tintColor:self.ows_blueTintColor], - [OWSConversationColor conversationColorWithName:ConversationColorNameIndigo - primaryColor:self.ows_indigoColor - shadeColor:self.ows_indigoShadeColor - tintColor:self.ows_indigoTintColor], - [OWSConversationColor conversationColorWithName:ConversationColorNameViolet - primaryColor:self.ows_violetColor - shadeColor:self.ows_violetShadeColor - tintColor:self.ows_violetTintColor], - [OWSConversationColor conversationColorWithName:ConversationColorNamePlum - primaryColor:self.ows_plumColor - shadeColor:self.ows_plumShadeColor - tintColor:self.ows_plumTintColor], - [OWSConversationColor conversationColorWithName:ConversationColorNameTaupe - primaryColor:self.ows_taupeColor - shadeColor:self.ows_taupeShadeColor - tintColor:self.ows_taupeTintColor], - [OWSConversationColor conversationColorWithName:ConversationColorNameSteel - primaryColor:self.ows_steelColor - shadeColor:self.ows_steelShadeColor - tintColor:self.ows_steelTintColor], - ]; - }); - - return allConversationColors; -} - -+ (NSDictionary *)conversationColorMap -{ - static NSDictionary *colorMap; - static dispatch_once_t onceToken; - dispatch_once(&onceToken, ^{ - NSMutableDictionary *mutableColorMap = [NSMutableDictionary new]; - for (OWSConversationColor *conversationColor in self.allConversationColors) { - mutableColorMap[conversationColor.name] = conversationColor; - } - colorMap = [mutableColorMap copy]; - }); - - return colorMap; -} - -+ (NSArray *)conversationColorNames -{ - NSMutableArray *names = [NSMutableArray new]; - for (OWSConversationColor *conversationColor in self.allConversationColors) { - [names addObject:conversationColor.name]; - } -#ifdef DEBUG - NSSet *colorNameSet = [NSSet setWithArray:names]; - // These constants are duplicated in two places. So this canary exists to make sure they stay in sync. - NSSet *threadColorNameSet = [NSSet setWithArray:TSThread.conversationColorNames]; - OWSAssertDebug([colorNameSet isEqual:threadColorNameSet]); -#endif - return [names copy]; -} - -+ (nullable OWSConversationColor *)conversationColorForColorName:(ConversationColorName)conversationColorName -{ - OWSConversationColor *_Nullable result = self.conversationColorMap[conversationColorName]; - - // Any mapping to colorNames should be done in TSThread before this method is called. - OWSAssertDebug(result != nil); - - return result; -} - -+ (OWSConversationColor *)conversationColorOrDefaultForColorName:(ConversationColorName)conversationColorName -{ - OWSConversationColor *_Nullable conversationColor = [self conversationColorForColorName:conversationColorName]; - if (conversationColor) { - return conversationColor; - } - return [self defaultConversationColor]; -} - -+ (OWSConversationColor *)defaultConversationColor -{ - return [self conversationColorForColorName:kConversationColorName_Default]; -} - -@end - -NS_ASSUME_NONNULL_END diff --git a/SignalUtilitiesKit/OWSCountryMetadata.h b/SignalUtilitiesKit/OWSCountryMetadata.h deleted file mode 100644 index f2ce56d8c..000000000 --- a/SignalUtilitiesKit/OWSCountryMetadata.h +++ /dev/null @@ -1,23 +0,0 @@ -// -// Copyright (c) 2018 Open Whisper Systems. All rights reserved. -// - -#import - -NS_ASSUME_NONNULL_BEGIN - -@interface OWSCountryMetadata : NSObject - -@property (nonatomic) NSString *name; -@property (nonatomic) NSString *tld; -@property (nonatomic, nullable) NSString *frontingDomain; -@property (nonatomic) NSString *countryCode; -@property (nonatomic) NSString *localizedCountryName; - -+ (OWSCountryMetadata *)countryMetadataForCountryCode:(NSString *)countryCode; - -+ (NSArray *)allCountryMetadatas; - -@end - -NS_ASSUME_NONNULL_END diff --git a/SignalUtilitiesKit/OWSCountryMetadata.m b/SignalUtilitiesKit/OWSCountryMetadata.m deleted file mode 100644 index 0c0987457..000000000 --- a/SignalUtilitiesKit/OWSCountryMetadata.m +++ /dev/null @@ -1,379 +0,0 @@ -// -// Copyright (c) 2019 Open Whisper Systems. All rights reserved. -// - -#import "OWSCountryMetadata.h" -#import "OWSCensorshipConfiguration.h" -#import - -NS_ASSUME_NONNULL_BEGIN - -@implementation OWSCountryMetadata - -+ (OWSCountryMetadata *)countryMetadataWithName:(NSString *)name - tld:(NSString *)tld - frontingDomain:(nullable NSString *)frontingDomain - countryCode:(NSString *)countryCode -{ - OWSAssertDebug(name.length > 0); - OWSAssertDebug(tld.length > 0); - OWSAssertDebug(countryCode.length > 0); - - OWSCountryMetadata *instance = [OWSCountryMetadata new]; - instance.name = name; - instance.tld = tld; - instance.frontingDomain = frontingDomain; - instance.countryCode = countryCode; - - NSString *localizedCountryName = [[NSLocale currentLocale] displayNameForKey:NSLocaleCountryCode value:countryCode]; - if (localizedCountryName.length < 1) { - localizedCountryName = name; - } - instance.localizedCountryName = localizedCountryName; - - return instance; -} - -+ (OWSCountryMetadata *)countryMetadataForCountryCode:(NSString *)countryCode -{ - OWSAssertDebug(countryCode.length > 0); - - return [self countryCodeToCountryMetadataMap][countryCode]; -} - -+ (NSDictionary *)countryCodeToCountryMetadataMap -{ - static NSDictionary *cachedValue = nil; - static dispatch_once_t onceToken; - dispatch_once(&onceToken, ^{ - NSMutableDictionary *map = [NSMutableDictionary new]; - for (OWSCountryMetadata *metadata in [self allCountryMetadatas]) { - map[metadata.countryCode] = metadata; - } - cachedValue = map; - }); - return cachedValue; -} - -+ (NSArray *)allCountryMetadatas -{ - static NSArray *cachedValue = nil; - static dispatch_once_t onceToken; - dispatch_once(&onceToken, ^{ - cachedValue = @[ - [OWSCountryMetadata countryMetadataWithName:@"Andorra" tld:@".ad" frontingDomain:nil countryCode:@"AD"], - [OWSCountryMetadata countryMetadataWithName:@"United Arab Emirates" - tld:@".ae" - frontingDomain:OWSFrontingHost_GoogleUAE - countryCode:@"AE"], - [OWSCountryMetadata countryMetadataWithName:@"Afghanistan" tld:@".af" frontingDomain:nil countryCode:@"AF"], - [OWSCountryMetadata countryMetadataWithName:@"Antigua and Barbuda" - tld:@".ag" - frontingDomain:nil - countryCode:@"AG"], - [OWSCountryMetadata countryMetadataWithName:@"Anguilla" tld:@".ai" frontingDomain:nil countryCode:@"AI"], - [OWSCountryMetadata countryMetadataWithName:@"Albania" tld:@".al" frontingDomain:nil countryCode:@"AL"], - [OWSCountryMetadata countryMetadataWithName:@"Armenia" tld:@".am" frontingDomain:nil countryCode:@"AM"], - [OWSCountryMetadata countryMetadataWithName:@"Angola" tld:@".ao" frontingDomain:nil countryCode:@"AO"], - [OWSCountryMetadata countryMetadataWithName:@"Argentina" tld:@".ar" frontingDomain:nil countryCode:@"AR"], - [OWSCountryMetadata countryMetadataWithName:@"American Samoa" - tld:@".as" - frontingDomain:nil - countryCode:@"AS"], - [OWSCountryMetadata countryMetadataWithName:@"Austria" tld:@".at" frontingDomain:nil countryCode:@"AT"], - [OWSCountryMetadata countryMetadataWithName:@"Australia" tld:@".au" frontingDomain:nil countryCode:@"AU"], - [OWSCountryMetadata countryMetadataWithName:@"Azerbaijan" tld:@".az" frontingDomain:nil countryCode:@"AZ"], - [OWSCountryMetadata countryMetadataWithName:@"Bosnia and Herzegovina" - tld:@".ba" - frontingDomain:nil - countryCode:@"BA"], - [OWSCountryMetadata countryMetadataWithName:@"Bangladesh" tld:@".bd" frontingDomain:nil countryCode:@"BD"], - [OWSCountryMetadata countryMetadataWithName:@"Belgium" tld:@".be" frontingDomain:nil countryCode:@"BE"], - [OWSCountryMetadata countryMetadataWithName:@"Burkina Faso" - tld:@".bf" - frontingDomain:nil - countryCode:@"BF"], - [OWSCountryMetadata countryMetadataWithName:@"Bulgaria" tld:@".bg" frontingDomain:nil countryCode:@"BG"], - [OWSCountryMetadata countryMetadataWithName:@"Bahrain" tld:@".bh" frontingDomain:nil countryCode:@"BH"], - [OWSCountryMetadata countryMetadataWithName:@"Burundi" tld:@".bi" frontingDomain:nil countryCode:@"BI"], - [OWSCountryMetadata countryMetadataWithName:@"Benin" tld:@".bj" frontingDomain:nil countryCode:@"BJ"], - [OWSCountryMetadata countryMetadataWithName:@"Brunei" tld:@".bn" frontingDomain:nil countryCode:@"BN"], - [OWSCountryMetadata countryMetadataWithName:@"Bolivia" tld:@".bo" frontingDomain:nil countryCode:@"BO"], - [OWSCountryMetadata countryMetadataWithName:@"Brazil" tld:@".br" frontingDomain:nil countryCode:@"BR"], - [OWSCountryMetadata countryMetadataWithName:@"Bahamas" tld:@".bs" frontingDomain:nil countryCode:@"BS"], - [OWSCountryMetadata countryMetadataWithName:@"Bhutan" tld:@".bt" frontingDomain:nil countryCode:@"BT"], - [OWSCountryMetadata countryMetadataWithName:@"Botswana" tld:@".bw" frontingDomain:nil countryCode:@"BW"], - [OWSCountryMetadata countryMetadataWithName:@"Belarus" tld:@".by" frontingDomain:nil countryCode:@"BY"], - [OWSCountryMetadata countryMetadataWithName:@"Belize" tld:@".bz" frontingDomain:nil countryCode:@"BZ"], - [OWSCountryMetadata countryMetadataWithName:@"Canada" tld:@".ca" frontingDomain:nil countryCode:@"CA"], - [OWSCountryMetadata countryMetadataWithName:@"Cambodia" tld:@".kh" frontingDomain:nil countryCode:@"KH"], - [OWSCountryMetadata countryMetadataWithName:@"Cocos (Keeling) Islands" - tld:@".cc" - frontingDomain:nil - countryCode:@"CC"], - [OWSCountryMetadata countryMetadataWithName:@"Democratic Republic of the Congo" - tld:@".cd" - frontingDomain:nil - countryCode:@"CD"], - [OWSCountryMetadata countryMetadataWithName:@"Central African Republic" - tld:@".cf" - frontingDomain:nil - countryCode:@"CF"], - [OWSCountryMetadata countryMetadataWithName:@"Republic of the Congo" - tld:@".cg" - frontingDomain:nil - countryCode:@"CG"], - [OWSCountryMetadata countryMetadataWithName:@"Switzerland" tld:@".ch" frontingDomain:nil countryCode:@"CH"], - [OWSCountryMetadata countryMetadataWithName:@"Ivory Coast" tld:@".ci" frontingDomain:nil countryCode:@"CI"], - [OWSCountryMetadata countryMetadataWithName:@"Cook Islands" - tld:@".ck" - frontingDomain:nil - countryCode:@"CK"], - [OWSCountryMetadata countryMetadataWithName:@"Chile" tld:@".cl" frontingDomain:nil countryCode:@"CL"], - [OWSCountryMetadata countryMetadataWithName:@"Cameroon" tld:@".cm" frontingDomain:nil countryCode:@"CM"], - [OWSCountryMetadata countryMetadataWithName:@"China" tld:@".cn" frontingDomain:nil countryCode:@"CN"], - [OWSCountryMetadata countryMetadataWithName:@"Colombia" tld:@".co" frontingDomain:nil countryCode:@"CO"], - [OWSCountryMetadata countryMetadataWithName:@"Costa Rica" tld:@".cr" frontingDomain:nil countryCode:@"CR"], - [OWSCountryMetadata countryMetadataWithName:@"Cuba" tld:@".cu" frontingDomain:nil countryCode:@"CU"], - [OWSCountryMetadata countryMetadataWithName:@"Cape Verde" tld:@".cv" frontingDomain:nil countryCode:@"CV"], - [OWSCountryMetadata countryMetadataWithName:@"Christmas Island" - tld:@".cx" - frontingDomain:nil - countryCode:@"CX"], - [OWSCountryMetadata countryMetadataWithName:@"Cyprus" tld:@".cy" frontingDomain:nil countryCode:@"CY"], - [OWSCountryMetadata countryMetadataWithName:@"Czech Republic" - tld:@".cz" - frontingDomain:nil - countryCode:@"CZ"], - [OWSCountryMetadata countryMetadataWithName:@"Germany" tld:@".de" frontingDomain:nil countryCode:@"DE"], - [OWSCountryMetadata countryMetadataWithName:@"Djibouti" tld:@".dj" frontingDomain:nil countryCode:@"DJ"], - [OWSCountryMetadata countryMetadataWithName:@"Denmark" tld:@".dk" frontingDomain:nil countryCode:@"DK"], - [OWSCountryMetadata countryMetadataWithName:@"Dominica" tld:@".dm" frontingDomain:nil countryCode:@"DM"], - [OWSCountryMetadata countryMetadataWithName:@"Dominican Republic" - tld:@".do" - frontingDomain:nil - countryCode:@"DO"], - [OWSCountryMetadata countryMetadataWithName:@"Algeria" tld:@".dz" frontingDomain:nil countryCode:@"DZ"], - [OWSCountryMetadata countryMetadataWithName:@"Ecuador" tld:@".ec" frontingDomain:nil countryCode:@"EC"], - [OWSCountryMetadata countryMetadataWithName:@"Estonia" tld:@".ee" frontingDomain:nil countryCode:@"EE"], - [OWSCountryMetadata countryMetadataWithName:@"Egypt" - tld:@".eg" - frontingDomain:OWSFrontingHost_GoogleEgypt - countryCode:@"EG"], - [OWSCountryMetadata countryMetadataWithName:@"Spain" tld:@".es" frontingDomain:nil countryCode:@"ES"], - [OWSCountryMetadata countryMetadataWithName:@"Ethiopia" tld:@".et" frontingDomain:nil countryCode:@"ET"], - [OWSCountryMetadata countryMetadataWithName:@"Finland" tld:@".fi" frontingDomain:nil countryCode:@"FI"], - [OWSCountryMetadata countryMetadataWithName:@"Fiji" tld:@".fj" frontingDomain:nil countryCode:@"FJ"], - [OWSCountryMetadata countryMetadataWithName:@"Federated States of Micronesia" - tld:@".fm" - frontingDomain:nil - countryCode:@"FM"], - [OWSCountryMetadata countryMetadataWithName:@"France" tld:@".fr" frontingDomain:nil countryCode:@"FR"], - [OWSCountryMetadata countryMetadataWithName:@"Gabon" tld:@".ga" frontingDomain:nil countryCode:@"GA"], - [OWSCountryMetadata countryMetadataWithName:@"Georgia" tld:@".ge" frontingDomain:nil countryCode:@"GE"], - [OWSCountryMetadata countryMetadataWithName:@"French Guiana" - tld:@".gf" - frontingDomain:nil - countryCode:@"GF"], - [OWSCountryMetadata countryMetadataWithName:@"Guernsey" tld:@".gg" frontingDomain:nil countryCode:@"GG"], - [OWSCountryMetadata countryMetadataWithName:@"Ghana" tld:@".gh" frontingDomain:nil countryCode:@"GH"], - [OWSCountryMetadata countryMetadataWithName:@"Gibraltar" tld:@".gi" frontingDomain:nil countryCode:@"GI"], - [OWSCountryMetadata countryMetadataWithName:@"Greenland" tld:@".gl" frontingDomain:nil countryCode:@"GL"], - [OWSCountryMetadata countryMetadataWithName:@"Gambia" tld:@".gm" frontingDomain:nil countryCode:@"GM"], - [OWSCountryMetadata countryMetadataWithName:@"Guadeloupe" tld:@".gp" frontingDomain:nil countryCode:@"GP"], - [OWSCountryMetadata countryMetadataWithName:@"Greece" tld:@".gr" frontingDomain:nil countryCode:@"GR"], - [OWSCountryMetadata countryMetadataWithName:@"Guatemala" tld:@".gt" frontingDomain:nil countryCode:@"GT"], - [OWSCountryMetadata countryMetadataWithName:@"Guyana" tld:@".gy" frontingDomain:nil countryCode:@"GY"], - [OWSCountryMetadata countryMetadataWithName:@"Hong Kong" tld:@".hk" frontingDomain:nil countryCode:@"HK"], - [OWSCountryMetadata countryMetadataWithName:@"Honduras" tld:@".hn" frontingDomain:nil countryCode:@"HN"], - [OWSCountryMetadata countryMetadataWithName:@"Croatia" tld:@".hr" frontingDomain:nil countryCode:@"HR"], - [OWSCountryMetadata countryMetadataWithName:@"Haiti" tld:@".ht" frontingDomain:nil countryCode:@"HT"], - [OWSCountryMetadata countryMetadataWithName:@"Hungary" tld:@".hu" frontingDomain:nil countryCode:@"HU"], - [OWSCountryMetadata countryMetadataWithName:@"Indonesia" tld:@".id" frontingDomain:nil countryCode:@"ID"], - [OWSCountryMetadata countryMetadataWithName:@"Iraq" tld:@".iq" frontingDomain:nil countryCode:@"IQ"], - [OWSCountryMetadata countryMetadataWithName:@"Ireland" tld:@".ie" frontingDomain:nil countryCode:@"IE"], - [OWSCountryMetadata countryMetadataWithName:@"Israel" tld:@".il" frontingDomain:nil countryCode:@"IL"], - [OWSCountryMetadata countryMetadataWithName:@"Isle of Man" tld:@".im" frontingDomain:nil countryCode:@"IM"], - [OWSCountryMetadata countryMetadataWithName:@"India" tld:@".in" frontingDomain:nil countryCode:@"IN"], - [OWSCountryMetadata countryMetadataWithName:@"British Indian Ocean Territory" - tld:@".io" - frontingDomain:nil - countryCode:@"IO"], - [OWSCountryMetadata countryMetadataWithName:@"Iceland" tld:@".is" frontingDomain:nil countryCode:@"IS"], - [OWSCountryMetadata countryMetadataWithName:@"Italy" tld:@".it" frontingDomain:nil countryCode:@"IT"], - [OWSCountryMetadata countryMetadataWithName:@"Jersey" tld:@".je" frontingDomain:nil countryCode:@"JE"], - [OWSCountryMetadata countryMetadataWithName:@"Jamaica" tld:@".jm" frontingDomain:nil countryCode:@"JM"], - [OWSCountryMetadata countryMetadataWithName:@"Jordan" tld:@".jo" frontingDomain:nil countryCode:@"JO"], - [OWSCountryMetadata countryMetadataWithName:@"Japan" tld:@".jp" frontingDomain:nil countryCode:@"JP"], - [OWSCountryMetadata countryMetadataWithName:@"Kenya" tld:@".ke" frontingDomain:nil countryCode:@"KE"], - [OWSCountryMetadata countryMetadataWithName:@"Kiribati" tld:@".ki" frontingDomain:nil countryCode:@"KI"], - [OWSCountryMetadata countryMetadataWithName:@"Kyrgyzstan" tld:@".kg" frontingDomain:nil countryCode:@"KG"], - [OWSCountryMetadata countryMetadataWithName:@"South Korea" tld:@".kr" frontingDomain:nil countryCode:@"KR"], - [OWSCountryMetadata countryMetadataWithName:@"Kuwait" tld:@".kw" frontingDomain:nil countryCode:@"KW"], - [OWSCountryMetadata countryMetadataWithName:@"Kazakhstan" tld:@".kz" frontingDomain:nil countryCode:@"KZ"], - [OWSCountryMetadata countryMetadataWithName:@"Laos" tld:@".la" frontingDomain:nil countryCode:@"LA"], - [OWSCountryMetadata countryMetadataWithName:@"Lebanon" tld:@".lb" frontingDomain:nil countryCode:@"LB"], - [OWSCountryMetadata countryMetadataWithName:@"Saint Lucia" tld:@".lc" frontingDomain:nil countryCode:@"LC"], - [OWSCountryMetadata countryMetadataWithName:@"Liechtenstein" - tld:@".li" - frontingDomain:nil - countryCode:@"LI"], - [OWSCountryMetadata countryMetadataWithName:@"Sri Lanka" tld:@".lk" frontingDomain:nil countryCode:@"LK"], - [OWSCountryMetadata countryMetadataWithName:@"Lesotho" tld:@".ls" frontingDomain:nil countryCode:@"LS"], - [OWSCountryMetadata countryMetadataWithName:@"Lithuania" tld:@".lt" frontingDomain:nil countryCode:@"LT"], - [OWSCountryMetadata countryMetadataWithName:@"Luxembourg" tld:@".lu" frontingDomain:nil countryCode:@"LU"], - [OWSCountryMetadata countryMetadataWithName:@"Latvia" tld:@".lv" frontingDomain:nil countryCode:@"LV"], - [OWSCountryMetadata countryMetadataWithName:@"Libya" tld:@".ly" frontingDomain:nil countryCode:@"LY"], - [OWSCountryMetadata countryMetadataWithName:@"Morocco" tld:@".ma" frontingDomain:nil countryCode:@"MA"], - [OWSCountryMetadata countryMetadataWithName:@"Moldova" tld:@".md" frontingDomain:nil countryCode:@"MD"], - [OWSCountryMetadata countryMetadataWithName:@"Montenegro" tld:@".me" frontingDomain:nil countryCode:@"ME"], - [OWSCountryMetadata countryMetadataWithName:@"Madagascar" tld:@".mg" frontingDomain:nil countryCode:@"MG"], - [OWSCountryMetadata countryMetadataWithName:@"Macedonia" tld:@".mk" frontingDomain:nil countryCode:@"MK"], - [OWSCountryMetadata countryMetadataWithName:@"Mali" tld:@".ml" frontingDomain:nil countryCode:@"ML"], - [OWSCountryMetadata countryMetadataWithName:@"Myanmar" tld:@".mm" frontingDomain:nil countryCode:@"MM"], - [OWSCountryMetadata countryMetadataWithName:@"Mongolia" tld:@".mn" frontingDomain:nil countryCode:@"MN"], - [OWSCountryMetadata countryMetadataWithName:@"Montserrat" tld:@".ms" frontingDomain:nil countryCode:@"MS"], - [OWSCountryMetadata countryMetadataWithName:@"Malta" tld:@".mt" frontingDomain:nil countryCode:@"MT"], - [OWSCountryMetadata countryMetadataWithName:@"Mauritius" tld:@".mu" frontingDomain:nil countryCode:@"MU"], - [OWSCountryMetadata countryMetadataWithName:@"Maldives" tld:@".mv" frontingDomain:nil countryCode:@"MV"], - [OWSCountryMetadata countryMetadataWithName:@"Malawi" tld:@".mw" frontingDomain:nil countryCode:@"MW"], - [OWSCountryMetadata countryMetadataWithName:@"Mexico" tld:@".mx" frontingDomain:nil countryCode:@"MX"], - [OWSCountryMetadata countryMetadataWithName:@"Malaysia" tld:@".my" frontingDomain:nil countryCode:@"MY"], - [OWSCountryMetadata countryMetadataWithName:@"Mozambique" tld:@".mz" frontingDomain:nil countryCode:@"MZ"], - [OWSCountryMetadata countryMetadataWithName:@"Namibia" tld:@".na" frontingDomain:nil countryCode:@"NA"], - [OWSCountryMetadata countryMetadataWithName:@"Niger" tld:@".ne" frontingDomain:nil countryCode:@"NE"], - [OWSCountryMetadata countryMetadataWithName:@"Norfolk Island" - tld:@".nf" - frontingDomain:nil - countryCode:@"NF"], - [OWSCountryMetadata countryMetadataWithName:@"Nigeria" tld:@".ng" frontingDomain:nil countryCode:@"NG"], - [OWSCountryMetadata countryMetadataWithName:@"Nicaragua" tld:@".ni" frontingDomain:nil countryCode:@"NI"], - [OWSCountryMetadata countryMetadataWithName:@"Netherlands" tld:@".nl" frontingDomain:nil countryCode:@"NL"], - [OWSCountryMetadata countryMetadataWithName:@"Norway" tld:@".no" frontingDomain:nil countryCode:@"NO"], - [OWSCountryMetadata countryMetadataWithName:@"Nepal" tld:@".np" frontingDomain:nil countryCode:@"NP"], - [OWSCountryMetadata countryMetadataWithName:@"Nauru" tld:@".nr" frontingDomain:nil countryCode:@"NR"], - [OWSCountryMetadata countryMetadataWithName:@"Niue" tld:@".nu" frontingDomain:nil countryCode:@"NU"], - [OWSCountryMetadata countryMetadataWithName:@"New Zealand" tld:@".nz" frontingDomain:nil countryCode:@"NZ"], - [OWSCountryMetadata countryMetadataWithName:@"Oman" - tld:@".om" - frontingDomain:OWSFrontingHost_GoogleOman - countryCode:@"OM"], - [OWSCountryMetadata countryMetadataWithName:@"Pakistan" tld:@".pk" frontingDomain:nil countryCode:@"PK"], - [OWSCountryMetadata countryMetadataWithName:@"Panama" tld:@".pa" frontingDomain:nil countryCode:@"PA"], - [OWSCountryMetadata countryMetadataWithName:@"Peru" tld:@".pe" frontingDomain:nil countryCode:@"PE"], - [OWSCountryMetadata countryMetadataWithName:@"Philippines" tld:@".ph" frontingDomain:nil countryCode:@"PH"], - [OWSCountryMetadata countryMetadataWithName:@"Poland" tld:@".pl" frontingDomain:nil countryCode:@"PL"], - [OWSCountryMetadata countryMetadataWithName:@"Papua New Guinea" - tld:@".pg" - frontingDomain:nil - countryCode:@"PG"], - [OWSCountryMetadata countryMetadataWithName:@"Pitcairn Islands" - tld:@".pn" - frontingDomain:nil - countryCode:@"PN"], - [OWSCountryMetadata countryMetadataWithName:@"Puerto Rico" tld:@".pr" frontingDomain:nil countryCode:@"PR"], - [OWSCountryMetadata countryMetadataWithName:@"Palestine[4]" - tld:@".ps" - frontingDomain:nil - countryCode:@"PS"], - [OWSCountryMetadata countryMetadataWithName:@"Portugal" tld:@".pt" frontingDomain:nil countryCode:@"PT"], - [OWSCountryMetadata countryMetadataWithName:@"Paraguay" tld:@".py" frontingDomain:nil countryCode:@"PY"], - [OWSCountryMetadata countryMetadataWithName:@"Qatar" - tld:@".qa" - frontingDomain:OWSFrontingHost_GoogleQatar - countryCode:@"QA"], - [OWSCountryMetadata countryMetadataWithName:@"Romania" tld:@".ro" frontingDomain:nil countryCode:@"RO"], - [OWSCountryMetadata countryMetadataWithName:@"Serbia" tld:@".rs" frontingDomain:nil countryCode:@"RS"], - [OWSCountryMetadata countryMetadataWithName:@"Russia" tld:@".ru" frontingDomain:nil countryCode:@"RU"], - [OWSCountryMetadata countryMetadataWithName:@"Rwanda" tld:@".rw" frontingDomain:nil countryCode:@"RW"], - [OWSCountryMetadata countryMetadataWithName:@"Saudi Arabia" - tld:@".sa" - frontingDomain:nil - countryCode:@"SA"], - [OWSCountryMetadata countryMetadataWithName:@"Solomon Islands" - tld:@".sb" - frontingDomain:nil - countryCode:@"SB"], - [OWSCountryMetadata countryMetadataWithName:@"Seychelles" tld:@".sc" frontingDomain:nil countryCode:@"SC"], - [OWSCountryMetadata countryMetadataWithName:@"Sweden" tld:@".se" frontingDomain:nil countryCode:@"SE"], - [OWSCountryMetadata countryMetadataWithName:@"Singapore" tld:@".sg" frontingDomain:nil countryCode:@"SG"], - [OWSCountryMetadata countryMetadataWithName:@"Saint Helena, Ascension and Tristan da Cunha" - tld:@".sh" - frontingDomain:nil - countryCode:@"SH"], - [OWSCountryMetadata countryMetadataWithName:@"Slovenia" tld:@".si" frontingDomain:nil countryCode:@"SI"], - [OWSCountryMetadata countryMetadataWithName:@"Slovakia" tld:@".sk" frontingDomain:nil countryCode:@"SK"], - [OWSCountryMetadata countryMetadataWithName:@"Sierra Leone" - tld:@".sl" - frontingDomain:nil - countryCode:@"SL"], - [OWSCountryMetadata countryMetadataWithName:@"Senegal" tld:@".sn" frontingDomain:nil countryCode:@"SN"], - [OWSCountryMetadata countryMetadataWithName:@"San Marino" tld:@".sm" frontingDomain:nil countryCode:@"SM"], - [OWSCountryMetadata countryMetadataWithName:@"Somalia" tld:@".so" frontingDomain:nil countryCode:@"SO"], - [OWSCountryMetadata countryMetadataWithName:@"São Tomé and Príncipe" - tld:@".st" - frontingDomain:nil - countryCode:@"ST"], - [OWSCountryMetadata countryMetadataWithName:@"Suriname" tld:@".sr" frontingDomain:nil countryCode:@"SR"], - [OWSCountryMetadata countryMetadataWithName:@"El Salvador" tld:@".sv" frontingDomain:nil countryCode:@"SV"], - [OWSCountryMetadata countryMetadataWithName:@"Chad" tld:@".td" frontingDomain:nil countryCode:@"TD"], - [OWSCountryMetadata countryMetadataWithName:@"Togo" tld:@".tg" frontingDomain:nil countryCode:@"TG"], - [OWSCountryMetadata countryMetadataWithName:@"Thailand" tld:@".th" frontingDomain:nil countryCode:@"TH"], - [OWSCountryMetadata countryMetadataWithName:@"Tajikistan" tld:@".tj" frontingDomain:nil countryCode:@"TJ"], - [OWSCountryMetadata countryMetadataWithName:@"Tokelau" tld:@".tk" frontingDomain:nil countryCode:@"TK"], - [OWSCountryMetadata countryMetadataWithName:@"Timor-Leste" tld:@".tl" frontingDomain:nil countryCode:@"TL"], - [OWSCountryMetadata countryMetadataWithName:@"Turkmenistan" - tld:@".tm" - frontingDomain:nil - countryCode:@"TM"], - [OWSCountryMetadata countryMetadataWithName:@"Tonga" tld:@".to" frontingDomain:nil countryCode:@"TO"], - [OWSCountryMetadata countryMetadataWithName:@"Tunisia" tld:@".tn" frontingDomain:nil countryCode:@"TN"], - [OWSCountryMetadata countryMetadataWithName:@"Turkey" tld:@".tr" frontingDomain:nil countryCode:@"TR"], - [OWSCountryMetadata countryMetadataWithName:@"Trinidad and Tobago" - tld:@".tt" - frontingDomain:nil - countryCode:@"TT"], - [OWSCountryMetadata countryMetadataWithName:@"Taiwan" tld:@".tw" frontingDomain:nil countryCode:@"TW"], - [OWSCountryMetadata countryMetadataWithName:@"Tanzania" tld:@".tz" frontingDomain:nil countryCode:@"TZ"], - [OWSCountryMetadata countryMetadataWithName:@"Ukraine" tld:@".ua" frontingDomain:nil countryCode:@"UA"], - [OWSCountryMetadata countryMetadataWithName:@"Uganda" tld:@".ug" frontingDomain:nil countryCode:@"UG"], - [OWSCountryMetadata countryMetadataWithName:@"United States" - tld:@".com" - frontingDomain:nil - countryCode:@"US"], - [OWSCountryMetadata countryMetadataWithName:@"Uruguay" tld:@".uy" frontingDomain:nil countryCode:@"UY"], - [OWSCountryMetadata countryMetadataWithName:@"Uzbekistan" tld:@".uz" frontingDomain:nil countryCode:@"UZ"], - [OWSCountryMetadata countryMetadataWithName:@"Saint Vincent and the Grenadines" - tld:@".vc" - frontingDomain:nil - countryCode:@"VC"], - [OWSCountryMetadata countryMetadataWithName:@"Venezuela" tld:@".ve" frontingDomain:nil countryCode:@"VE"], - [OWSCountryMetadata countryMetadataWithName:@"British Virgin Islands" - tld:@".vg" - frontingDomain:nil - countryCode:@"VG"], - [OWSCountryMetadata countryMetadataWithName:@"United States Virgin Islands" - tld:@".vi" - frontingDomain:nil - countryCode:@"VI"], - [OWSCountryMetadata countryMetadataWithName:@"Vietnam" tld:@".vn" frontingDomain:nil countryCode:@"VN"], - [OWSCountryMetadata countryMetadataWithName:@"Vanuatu" tld:@".vu" frontingDomain:nil countryCode:@"VU"], - [OWSCountryMetadata countryMetadataWithName:@"Samoa" tld:@".ws" frontingDomain:nil countryCode:@"WS"], - [OWSCountryMetadata countryMetadataWithName:@"South Africa" - tld:@".za" - frontingDomain:nil - countryCode:@"ZA"], - [OWSCountryMetadata countryMetadataWithName:@"Zambia" tld:@".zm" frontingDomain:nil countryCode:@"ZM"], - [OWSCountryMetadata countryMetadataWithName:@"Zimbabwe" tld:@".zw" frontingDomain:nil countryCode:@"ZW"], - ]; - cachedValue = [cachedValue sortedArrayUsingComparator:^NSComparisonResult( - OWSCountryMetadata *_Nonnull left, OWSCountryMetadata *_Nonnull right) { - return [left.localizedCountryName compare:right.localizedCountryName]; - }]; - }); - return cachedValue; -} - -@end - -NS_ASSUME_NONNULL_END diff --git a/SignalUtilitiesKit/OWSDevice.h b/SignalUtilitiesKit/OWSDevice.h deleted file mode 100644 index 24408a3fb..000000000 --- a/SignalUtilitiesKit/OWSDevice.h +++ /dev/null @@ -1,78 +0,0 @@ -// -// Copyright (c) 2018 Open Whisper Systems. All rights reserved. -// - -#import -#import - -NS_ASSUME_NONNULL_BEGIN - -extern uint32_t const OWSDevicePrimaryDeviceId; - -@interface OWSDeviceManager : NSObject - -- (instancetype)init NS_UNAVAILABLE; - -+ (instancetype)sharedManager; - -- (BOOL)mayHaveLinkedDevices:(YapDatabaseConnection *)dbConnection; -- (void)setMayHaveLinkedDevices; -- (void)clearMayHaveLinkedDevices; - -- (BOOL)hasReceivedSyncMessageInLastSeconds:(NSTimeInterval)intervalSeconds; -- (void)setHasReceivedSyncMessage; - -@end - -#pragma mark - - -@interface OWSDevice : TSYapDatabaseObject - -@property (nonatomic, readonly) NSInteger deviceId; -@property (nonatomic, readonly, nullable) NSString *name; -@property (nonatomic, readonly) NSDate *createdAt; -@property (nonatomic, readonly) NSDate *lastSeenAt; - -+ (nullable instancetype)deviceFromJSONDictionary:(NSDictionary *)deviceAttributes error:(NSError **)error; - -+ (NSArray *)currentDevicesWithTransaction:(YapDatabaseReadTransaction *)transaction; - -/** - * Set local database of devices to `devices`. - * - * This will create missing devices, update existing devices, and delete stale devices. - * @param devices Removes any existing devices, replacing them with `devices` - * - * Returns YES if any devices were added or removed. - */ -+ (BOOL)replaceAll:(NSArray *)devices; - -/** - * The id of the device currently running this application - */ -+ (uint32_t)currentDeviceId; - -/** - * - * @param transaction yapTransaction - * @return - * If the user has any linked devices (apart from the device this app is running on). - */ -+ (BOOL)hasSecondaryDevicesWithTransaction:(YapDatabaseReadTransaction *)transaction; - -- (NSString *)displayName; -- (BOOL)isPrimaryDevice; - -/** - * Assign attributes to this device from another. - * - * @param other - * OWSDevice whose attributes to copy to this device - * @return - * YES if any values on self changed, else NO - */ -- (BOOL)updateAttributesWithDevice:(OWSDevice *)other; - -@end - -NS_ASSUME_NONNULL_END diff --git a/SignalUtilitiesKit/OWSDevice.m b/SignalUtilitiesKit/OWSDevice.m deleted file mode 100644 index 869b2bf9c..000000000 --- a/SignalUtilitiesKit/OWSDevice.m +++ /dev/null @@ -1,353 +0,0 @@ -// -// Copyright (c) 2018 Open Whisper Systems. All rights reserved. -// - -#import "OWSDevice.h" -#import "OWSError.h" -#import "OWSPrimaryStorage.h" -#import "ProfileManagerProtocol.h" -#import "SSKEnvironment.h" -#import "TSAccountManager.h" -#import "YapDatabaseConnection+OWS.h" -#import "YapDatabaseConnection.h" -#import "YapDatabaseTransaction.h" -#import -#import -#import -#import - -NS_ASSUME_NONNULL_BEGIN - -uint32_t const OWSDevicePrimaryDeviceId = 1; -NSString *const kOWSPrimaryStorage_OWSDeviceCollection = @"kTSStorageManager_OWSDeviceCollection"; -NSString *const kOWSPrimaryStorage_MayHaveLinkedDevices = @"kTSStorageManager_MayHaveLinkedDevices"; - -@interface OWSDeviceManager () - -@property (atomic) NSDate *lastReceivedSyncMessage; - -@end - -#pragma mark - - -@implementation OWSDeviceManager - -+ (instancetype)sharedManager -{ - static OWSDeviceManager *instance = nil; - static dispatch_once_t onceToken; - dispatch_once(&onceToken, ^{ - instance = [[self alloc] initDefault]; - }); - return instance; -} - -- (instancetype)initDefault -{ - return [super init]; -} - -- (BOOL)mayHaveLinkedDevices:(YapDatabaseConnection *)dbConnection -{ - OWSAssertDebug(dbConnection); - - return [dbConnection boolForKey:kOWSPrimaryStorage_MayHaveLinkedDevices - inCollection:kOWSPrimaryStorage_OWSDeviceCollection - defaultValue:YES]; -} - -// In order to avoid skipping necessary sync messages, the default value -// for mayHaveLinkedDevices is YES. Once we've successfully sent a -// sync message with no device messages (e.g. the service has confirmed -// that we have no linked devices), we can set mayHaveLinkedDevices to NO -// to avoid unnecessary message sends for sync messages until we learn -// of a linked device (e.g. through the device linking UI or by receiving -// a sync message, etc.). -- (void)clearMayHaveLinkedDevices -{ - // Note that we write async to avoid opening transactions within transactions. - [LKStorage writeWithBlock:^(YapDatabaseReadWriteTransaction *_Nonnull transaction) { - [transaction setObject:@(NO) - forKey:kOWSPrimaryStorage_MayHaveLinkedDevices - inCollection:kOWSPrimaryStorage_OWSDeviceCollection]; - }]; -} - -- (void)setMayHaveLinkedDevices -{ - // Note that we write async to avoid opening transactions within transactions. - [LKStorage writeWithBlock:^(YapDatabaseReadWriteTransaction *_Nonnull transaction) { - [transaction setObject:@(YES) - forKey:kOWSPrimaryStorage_MayHaveLinkedDevices - inCollection:kOWSPrimaryStorage_OWSDeviceCollection]; - }]; -} - -- (BOOL)hasReceivedSyncMessageInLastSeconds:(NSTimeInterval)intervalSeconds -{ - return (self.lastReceivedSyncMessage && fabs(self.lastReceivedSyncMessage.timeIntervalSinceNow) < intervalSeconds); -} - -- (void)setHasReceivedSyncMessage -{ - self.lastReceivedSyncMessage = [NSDate new]; - - [self setMayHaveLinkedDevices]; -} - -@end - -#pragma mark - - -@interface OWSDevice () - -@property (nonatomic) NSInteger deviceId; -@property (nonatomic, nullable) NSString *name; -@property (nonatomic) NSDate *createdAt; -@property (nonatomic) NSDate *lastSeenAt; - -@end - -#pragma mark - - -@implementation OWSDevice - -#pragma mark - Dependencies - -+ (id)profileManager -{ - return SSKEnvironment.shared.profileManager; -} - -+ (id)udManager -{ - return SSKEnvironment.shared.udManager; -} - -+ (TSAccountManager *)tsAccountManager -{ - return TSAccountManager.sharedInstance; -} - -- (OWSIdentityManager *)identityManager -{ - OWSAssertDebug(SSKEnvironment.shared.identityManager); - - return SSKEnvironment.shared.identityManager; -} - -#pragma mark - - -- (void)saveWithTransaction:(YapDatabaseReadWriteTransaction *)transaction -{ - [super saveWithTransaction:transaction]; -} - -+ (nullable instancetype)deviceFromJSONDictionary:(NSDictionary *)deviceAttributes error:(NSError **)error -{ - OWSDevice *device = [MTLJSONAdapter modelOfClass:[self class] fromJSONDictionary:deviceAttributes error:error]; - if (device.deviceId < OWSDevicePrimaryDeviceId) { - OWSFailDebug(@"Invalid device id: %lu", (unsigned long)device.deviceId); - *error = OWSErrorWithCodeDescription(OWSErrorCodeFailedToDecodeJson, @"Invalid device id."); - return nil; - } - return device; -} - -+ (NSDictionary *)JSONKeyPathsByPropertyKey -{ - return @{ - @"createdAt": @"created", - @"lastSeenAt": @"lastSeen", - @"deviceId": @"id", - @"name": @"name" - }; -} - -+ (MTLValueTransformer *)createdAtJSONTransformer -{ - return self.millisecondTimestampToDateTransformer; -} - -+ (MTLValueTransformer *)lastSeenAtJSONTransformer -{ - return self.millisecondTimestampToDateTransformer; -} - -+ (NSArray *)currentDevicesWithTransaction:(YapDatabaseReadTransaction *)transaction -{ - OWSAssertDebug(transaction); - - NSMutableArray *result = [NSMutableArray new]; - [transaction enumerateKeysAndObjectsInCollection:OWSDevice.collection - usingBlock:^(NSString *key, OWSDevice *object, BOOL *stop) { - if (![object isKindOfClass:[OWSDevice class]]) { - OWSFailDebug(@"Unexpected object in collection: %@", object.class); - return; - } - [result addObject:object]; - }]; - return result; -} - -+ (BOOL)replaceAll:(NSArray *)currentDevices -{ - BOOL didAddOrRemove = NO; - NSMutableArray *existingDevices = [[self allObjectsInCollection] mutableCopy]; - for (OWSDevice *currentDevice in currentDevices) { - NSUInteger existingDeviceIndex = [existingDevices indexOfObject:currentDevice]; - if (existingDeviceIndex == NSNotFound) { - // New Device - OWSLogInfo(@"Adding device: %@", currentDevice); - [currentDevice save]; - didAddOrRemove = YES; - } else { - OWSDevice *existingDevice = existingDevices[existingDeviceIndex]; - if ([existingDevice updateAttributesWithDevice:currentDevice]) { - [existingDevice save]; - } - [existingDevices removeObjectAtIndex:existingDeviceIndex]; - } - } - - // Since we removed existing devices as we went, only stale devices remain - for (OWSDevice *staleDevice in existingDevices) { - OWSLogVerbose(@"Removing device: %@", staleDevice); - [staleDevice remove]; - didAddOrRemove = YES; - } - - if (didAddOrRemove) { - dispatch_async(dispatch_get_main_queue(), ^{ - // Device changes can affect the UD access mode for a recipient, - // so we need to fetch the profile for this user to update UD access mode. - [self.profileManager fetchLocalUsersProfile]; - }); - return YES; - } else { - return NO; - } -} - -+ (MTLValueTransformer *)millisecondTimestampToDateTransformer -{ - static MTLValueTransformer *instance = nil; - static dispatch_once_t onceToken; - dispatch_once(&onceToken, ^{ - instance = [MTLValueTransformer transformerUsingForwardBlock:^id(id value, BOOL *success, NSError **error) { - if ([value isKindOfClass:[NSNumber class]]) { - NSNumber *number = (NSNumber *)value; - NSDate *result = [NSDate ows_dateWithMillisecondsSince1970:[number longLongValue]]; - if (result) { - *success = YES; - return result; - } - } - *success = NO; - OWSLogError(@"unable to decode date from %@", value); - *error = OWSErrorWithCodeDescription(OWSErrorCodeFailedToDecodeJson, @"Unable to decode date from JSON."); - return nil; - } - reverseBlock:^id(id value, BOOL *success, NSError **error) { - if ([value isKindOfClass:[NSDate class]]) { - NSDate *date = (NSDate *)value; - NSNumber *result = [NSNumber numberWithLongLong:[NSDate ows_millisecondsSince1970ForDate:date]]; - if (result) { - *success = YES; - return result; - } - } - OWSLogError(@"unable to encode date from %@", value); - *error = OWSErrorWithCodeDescription(OWSErrorCodeFailedToEncodeJson, @"Unable to encode date to JSON."); - *success = NO; - return nil; - }]; - }); - return instance; -} - -+ (uint32_t)currentDeviceId -{ - // Someday it may be possible to have a non-primary iOS device, but for now - // any iOS device must be the primary device. - return OWSDevicePrimaryDeviceId; -} - -- (BOOL)isPrimaryDevice -{ - return self.deviceId == OWSDevicePrimaryDeviceId; -} - -- (NSString *)displayName -{ - if (self.name) { - ECKeyPair *_Nullable identityKeyPair = self.identityManager.identityKeyPair; - OWSAssertDebug(identityKeyPair); - if (identityKeyPair) { - NSError *error; - NSString *_Nullable decryptedName = - [DeviceNames decryptDeviceNameWithBase64String:self.name identityKeyPair:identityKeyPair error:&error]; - if (error) { - // Not necessarily an error; might be a legacy device name. - OWSLogError(@"Could not decrypt device name: %@", error); - } else if (decryptedName) { - return decryptedName; - } - } - - return self.name; - } - - if (self.deviceId == OWSDevicePrimaryDeviceId) { - return @"This Device"; - } - return NSLocalizedString(@"UNNAMED_DEVICE", @"Label text in device manager for a device with no name"); -} - -- (BOOL)updateAttributesWithDevice:(OWSDevice *)other -{ - BOOL changed = NO; - if (![self.lastSeenAt isEqual:other.lastSeenAt]) { - self.lastSeenAt = other.lastSeenAt; - changed = YES; - } - - if (![self.createdAt isEqual:other.createdAt]) { - self.createdAt = other.createdAt; - changed = YES; - } - - if (![self.name isEqual:other.name]) { - self.name = other.name; - changed = YES; - } - - return changed; -} - -+ (BOOL)hasSecondaryDevicesWithTransaction:(YapDatabaseReadTransaction *)transaction -{ - return [self numberOfKeysInCollectionWithTransaction:transaction] > 1; -} - -- (BOOL)isEqual:(id)object -{ - if (self == object) { - return YES; - } - - if (![object isKindOfClass:[OWSDevice class]]) { - return NO; - } - - return [self isEqualToDevice:(OWSDevice *)object]; -} - -- (BOOL)isEqualToDevice:(OWSDevice *)device -{ - return self.deviceId == device.deviceId; -} - -@end - -NS_ASSUME_NONNULL_END diff --git a/SignalUtilitiesKit/OWSDeviceProvisioner.h b/SignalUtilitiesKit/OWSDeviceProvisioner.h deleted file mode 100644 index f9f93217f..000000000 --- a/SignalUtilitiesKit/OWSDeviceProvisioner.h +++ /dev/null @@ -1,38 +0,0 @@ -// -// Copyright (c) 2017 Open Whisper Systems. All rights reserved. -// - -#import - -NS_ASSUME_NONNULL_BEGIN - -@class OWSDeviceProvisioningCodeService; -@class OWSDeviceProvisioningService; - -@interface OWSDeviceProvisioner : NSObject - -- (instancetype)init NS_UNAVAILABLE; - -- (instancetype)initWithMyPublicKey:(NSData *)myPublicKey - myPrivateKey:(NSData *)myPrivateKey - theirPublicKey:(NSData *)theirPublicKey - theirEphemeralDeviceId:(NSString *)ephemeralDeviceId - accountIdentifier:(NSString *)accountIdentifier - profileKey:(NSData *)profileKey - readReceiptsEnabled:(BOOL)areReadReceiptsEnabled - provisioningCodeService:(OWSDeviceProvisioningCodeService *)provisioningCodeService - provisioningService:(OWSDeviceProvisioningService *)provisioningService NS_DESIGNATED_INITIALIZER; - -- (instancetype)initWithMyPublicKey:(NSData *)myPublicKey - myPrivateKey:(NSData *)myPrivateKey - theirPublicKey:(NSData *)theirPublicKey - theirEphemeralDeviceId:(NSString *)ephemeralDeviceId - accountIdentifier:(NSString *)accountIdentifier - profileKey:(NSData *)profileKey - readReceiptsEnabled:(BOOL)areReadReceiptsEnabled; - -- (void)provisionWithSuccess:(void (^)(void))successCallback failure:(void (^)(NSError *))failureCallback; - -@end - -NS_ASSUME_NONNULL_END diff --git a/SignalUtilitiesKit/OWSDeviceProvisioner.m b/SignalUtilitiesKit/OWSDeviceProvisioner.m deleted file mode 100644 index 2deefb9b1..000000000 --- a/SignalUtilitiesKit/OWSDeviceProvisioner.m +++ /dev/null @@ -1,123 +0,0 @@ -// -// Copyright (c) 2018 Open Whisper Systems. All rights reserved. -// - -#import "OWSDeviceProvisioner.h" -#import "OWSDeviceProvisioningCodeService.h" -#import "OWSDeviceProvisioningService.h" -#import "OWSError.h" -#import "OWSProvisioningMessage.h" -#import - -NS_ASSUME_NONNULL_BEGIN - -@interface OWSDeviceProvisioner () - -@property (nonatomic, readonly) NSData *myPublicKey; -@property (nonatomic, readonly) NSData *myPrivateKey; -@property (nonatomic, readonly) NSData *theirPublicKey; -@property (nonatomic, readonly) NSString *accountIdentifier; -@property (nonatomic, readonly) NSData *profileKey; -@property (nonatomic, nullable) NSString *ephemeralDeviceId; -@property (nonatomic, readonly) BOOL areReadReceiptsEnabled; -@property (nonatomic, readonly) OWSDeviceProvisioningCodeService *provisioningCodeService; -@property (nonatomic, readonly) OWSDeviceProvisioningService *provisioningService; - -@end - -@implementation OWSDeviceProvisioner - -- (instancetype)initWithMyPublicKey:(NSData *)myPublicKey - myPrivateKey:(NSData *)myPrivateKey - theirPublicKey:(NSData *)theirPublicKey - theirEphemeralDeviceId:(NSString *)ephemeralDeviceId - accountIdentifier:(NSString *)accountIdentifier - profileKey:(NSData *)profileKey - readReceiptsEnabled:(BOOL)areReadReceiptsEnabled - provisioningCodeService:(OWSDeviceProvisioningCodeService *)provisioningCodeService - provisioningService:(OWSDeviceProvisioningService *)provisioningService -{ - self = [super init]; - if (!self) { - return self; - } - - _myPublicKey = myPublicKey; - _myPrivateKey = myPrivateKey; - _theirPublicKey = theirPublicKey; - _accountIdentifier = accountIdentifier; - _profileKey = profileKey; - _ephemeralDeviceId = ephemeralDeviceId; - _areReadReceiptsEnabled = areReadReceiptsEnabled; - _provisioningCodeService = provisioningCodeService; - _provisioningService = provisioningService; - - return self; -} - -- (instancetype)initWithMyPublicKey:(NSData *)myPublicKey - myPrivateKey:(NSData *)myPrivateKey - theirPublicKey:(NSData *)theirPublicKey - theirEphemeralDeviceId:(NSString *)ephemeralDeviceId - accountIdentifier:(NSString *)accountIdentifier - profileKey:(NSData *)profileKey - readReceiptsEnabled:(BOOL)areReadReceiptsEnabled -{ - return [self initWithMyPublicKey:myPublicKey - myPrivateKey:myPrivateKey - theirPublicKey:theirPublicKey - theirEphemeralDeviceId:ephemeralDeviceId - accountIdentifier:accountIdentifier - profileKey:profileKey - readReceiptsEnabled:areReadReceiptsEnabled - provisioningCodeService:[OWSDeviceProvisioningCodeService new] - provisioningService:[OWSDeviceProvisioningService new]]; -} - -- (void)provisionWithSuccess:(void (^)(void))successCallback failure:(void (^)(NSError *_Nonnull))failureCallback -{ - [self.provisioningCodeService - requestProvisioningCodeWithSuccess:^(NSString *provisioningCode) { - OWSLogInfo(@"Retrieved provisioning code."); - [self provisionWithCode:provisioningCode success:successCallback failure:failureCallback]; - } - failure:^(NSError *error) { - OWSLogError(@"Failed to get provisioning code with error: %@", error); - failureCallback(error); - }]; -} - -- (void)provisionWithCode:(NSString *)provisioningCode - success:(void (^)(void))successCallback - failure:(void (^)(NSError *_Nonnull))failureCallback -{ - OWSProvisioningMessage *message = [[OWSProvisioningMessage alloc] initWithMyPublicKey:self.myPublicKey - myPrivateKey:self.myPrivateKey - theirPublicKey:self.theirPublicKey - accountIdentifier:self.accountIdentifier - profileKey:self.profileKey - readReceiptsEnabled:self.areReadReceiptsEnabled - provisioningCode:provisioningCode]; - - NSData *_Nullable messageBody = [message buildEncryptedMessageBody]; - if (messageBody == nil) { - NSError *error = OWSErrorWithCodeDescription(OWSErrorCodeFailedToEncryptMessage, @"Failed building provisioning message"); - failureCallback(error); - return; - } - - [self.provisioningService provisionWithMessageBody:messageBody - ephemeralDeviceId:self.ephemeralDeviceId - success:^{ - OWSLogInfo(@"ProvisioningService SUCCEEDED"); - successCallback(); - } - failure:^(NSError *error) { - OWSLogError(@"ProvisioningService FAILED with error:%@", error); - failureCallback(error); - }]; -} - -@end - -NS_ASSUME_NONNULL_END diff --git a/SignalUtilitiesKit/OWSDeviceProvisioningCodeService.h b/SignalUtilitiesKit/OWSDeviceProvisioningCodeService.h deleted file mode 100644 index bd0792f4b..000000000 --- a/SignalUtilitiesKit/OWSDeviceProvisioningCodeService.h +++ /dev/null @@ -1,18 +0,0 @@ -// Copyright © 2016 Open Whisper Systems. All rights reserved. - -#import - -NS_ASSUME_NONNULL_BEGIN - -@class TSNetworkManager; - -@interface OWSDeviceProvisioningCodeService : NSObject - -- (instancetype)initWithNetworkManager:(TSNetworkManager *)networkManager NS_DESIGNATED_INITIALIZER; - -- (void)requestProvisioningCodeWithSuccess:(void (^)(NSString *))successCallback - failure:(void (^)(NSError *))failureCallback; - -@end - -NS_ASSUME_NONNULL_END diff --git a/SignalUtilitiesKit/OWSDeviceProvisioningCodeService.m b/SignalUtilitiesKit/OWSDeviceProvisioningCodeService.m deleted file mode 100644 index 02d967355..000000000 --- a/SignalUtilitiesKit/OWSDeviceProvisioningCodeService.m +++ /dev/null @@ -1,62 +0,0 @@ -// -// Copyright (c) 2018 Open Whisper Systems. All rights reserved. -// - -#import "OWSDeviceProvisioningCodeService.h" -#import "OWSRequestFactory.h" -#import "TSNetworkManager.h" -#import - -NS_ASSUME_NONNULL_BEGIN - -NSString *const OWSDeviceProvisioningCodeServiceProvisioningCodeKey = @"verificationCode"; - -@interface OWSDeviceProvisioningCodeService () - -@property (readonly) TSNetworkManager *networkManager; - -@end - -@implementation OWSDeviceProvisioningCodeService - -- (instancetype)initWithNetworkManager:(TSNetworkManager *)networkManager -{ - - self = [super init]; - if (!self) { - return self; - } - - _networkManager = networkManager; - - return self; -} - -- (instancetype)init -{ - return [self initWithNetworkManager:[TSNetworkManager sharedManager]]; -} - -- (void)requestProvisioningCodeWithSuccess:(void (^)(NSString *))successCallback - failure:(void (^)(NSError *))failureCallback -{ - TSRequest *request = [OWSRequestFactory deviceProvisioningCodeRequest]; - [self.networkManager makeRequest:request - success:^(NSURLSessionDataTask *task, id responseObject) { - OWSLogVerbose(@"ProvisioningCode request succeeded"); - if ([(NSObject *)responseObject isKindOfClass:[NSDictionary class]]) { - NSDictionary *responseDict = (NSDictionary *)responseObject; - NSString *provisioningCode = - [responseDict objectForKey:OWSDeviceProvisioningCodeServiceProvisioningCodeKey]; - successCallback(provisioningCode); - } - } - failure:^(NSURLSessionDataTask *task, NSError *error) { - OWSLogVerbose(@"ProvisioningCode request failed with error: %@", error); - failureCallback(error); - }]; -} - -@end - -NS_ASSUME_NONNULL_END diff --git a/SignalUtilitiesKit/OWSDeviceProvisioningService.h b/SignalUtilitiesKit/OWSDeviceProvisioningService.h deleted file mode 100644 index fa28b686d..000000000 --- a/SignalUtilitiesKit/OWSDeviceProvisioningService.h +++ /dev/null @@ -1,22 +0,0 @@ -// -// Copyright (c) 2017 Open Whisper Systems. All rights reserved. -// - -#import - -NS_ASSUME_NONNULL_BEGIN - -@class TSNetworkManager; - -@interface OWSDeviceProvisioningService : NSObject - -- (instancetype)initWithNetworkManager:(TSNetworkManager *)networkManager; - -- (void)provisionWithMessageBody:(NSData *)messageBody - ephemeralDeviceId:(NSString *)deviceId - success:(void (^)(void))successCallback - failure:(void (^)(NSError *))failureCallback; - -@end - -NS_ASSUME_NONNULL_END diff --git a/SignalUtilitiesKit/OWSDeviceProvisioningService.m b/SignalUtilitiesKit/OWSDeviceProvisioningService.m deleted file mode 100644 index 4f8dca0e7..000000000 --- a/SignalUtilitiesKit/OWSDeviceProvisioningService.m +++ /dev/null @@ -1,57 +0,0 @@ -// -// Copyright (c) 2018 Open Whisper Systems. All rights reserved. -// - -#import "OWSDeviceProvisioningService.h" -#import "OWSRequestFactory.h" -#import "TSNetworkManager.h" -#import - -NS_ASSUME_NONNULL_BEGIN - -@interface OWSDeviceProvisioningService () - -@property (nonatomic, readonly) TSNetworkManager *networkManager; - -@end - -@implementation OWSDeviceProvisioningService - -- (instancetype)initWithNetworkManager:(TSNetworkManager *)networkManager -{ - self = [super init]; - if (!self) { - return self; - } - - _networkManager = networkManager; - - return self; -} - -- (instancetype)init -{ - return [self initWithNetworkManager:[TSNetworkManager sharedManager]]; -} - -- (void)provisionWithMessageBody:(NSData *)messageBody - ephemeralDeviceId:(NSString *)deviceId - success:(void (^)(void))successCallback - failure:(void (^)(NSError *))failureCallback -{ - TSRequest *request = - [OWSRequestFactory deviceProvisioningRequestWithMessageBody:messageBody ephemeralDeviceId:deviceId]; - [self.networkManager makeRequest:request - success:^(NSURLSessionDataTask *task, id responseObject) { - OWSLogVerbose(@"Provisioning request succeeded"); - successCallback(); - } - failure:^(NSURLSessionDataTask *task, NSError *error) { - OWSLogVerbose(@"Provisioning request failed with error: %@", error); - failureCallback(error); - }]; -} - -@end - -NS_ASSUME_NONNULL_END diff --git a/SignalUtilitiesKit/OWSDevicesService.h b/SignalUtilitiesKit/OWSDevicesService.h deleted file mode 100644 index eed731e2a..000000000 --- a/SignalUtilitiesKit/OWSDevicesService.h +++ /dev/null @@ -1,25 +0,0 @@ -// -// Copyright (c) 2018 Open Whisper Systems. All rights reserved. -// - -#import - -NS_ASSUME_NONNULL_BEGIN - -extern NSString *const NSNotificationName_DeviceListUpdateSucceeded; -extern NSString *const NSNotificationName_DeviceListUpdateFailed; -extern NSString *const NSNotificationName_DeviceListUpdateModifiedDeviceList; - -@class OWSDevice; - -@interface OWSDevicesService : NSObject - -+ (void)refreshDevices; - -+ (void)unlinkDevice:(OWSDevice *)device - success:(void (^)(void))successCallback - failure:(void (^)(NSError *))failureCallback; - -@end - -NS_ASSUME_NONNULL_END diff --git a/SignalUtilitiesKit/OWSDevicesService.m b/SignalUtilitiesKit/OWSDevicesService.m deleted file mode 100644 index e84163d2c..000000000 --- a/SignalUtilitiesKit/OWSDevicesService.m +++ /dev/null @@ -1,126 +0,0 @@ -// -// Copyright (c) 2018 Open Whisper Systems. All rights reserved. -// - -#import "OWSDevicesService.h" -#import "NSNotificationCenter+OWS.h" -#import "OWSDevice.h" -#import "OWSError.h" -#import "OWSRequestFactory.h" -#import "TSNetworkManager.h" -#import -#import - -NS_ASSUME_NONNULL_BEGIN - -NSString *const NSNotificationName_DeviceListUpdateSucceeded = @"NSNotificationName_DeviceListUpdateSucceeded"; -NSString *const NSNotificationName_DeviceListUpdateFailed = @"NSNotificationName_DeviceListUpdateFailed"; -NSString *const NSNotificationName_DeviceListUpdateModifiedDeviceList - = @"NSNotificationName_DeviceListUpdateModifiedDeviceList"; - -@implementation OWSDevicesService - -+ (void)refreshDevices -{ - dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ - [self - getDevicesWithSuccess:^(NSArray *devices) { - // If we have more than one device; we may have a linked device. - if (devices.count > 1) { - // Setting this flag here shouldn't be necessary, but we do so - // because the "cost" is low and it will improve robustness. - [OWSDeviceManager.sharedManager setMayHaveLinkedDevices]; - } - - BOOL didAddOrRemove = [OWSDevice replaceAll:devices]; - - [NSNotificationCenter.defaultCenter - postNotificationNameAsync:NSNotificationName_DeviceListUpdateSucceeded - object:nil]; - - if (didAddOrRemove) { - [NSNotificationCenter.defaultCenter - postNotificationNameAsync:NSNotificationName_DeviceListUpdateModifiedDeviceList - object:nil]; - } - } - failure:^(NSError *error) { - OWSLogError(@"Request device list failed with error: %@", error); - - [NSNotificationCenter.defaultCenter postNotificationNameAsync:NSNotificationName_DeviceListUpdateFailed - object:error]; - }]; - }); -} - -+ (void)getDevicesWithSuccess:(void (^)(NSArray *))successCallback - failure:(void (^)(NSError *))failureCallback -{ - TSRequest *request = [OWSRequestFactory getDevicesRequest]; - [[TSNetworkManager sharedManager] makeRequest:request - success:^(NSURLSessionDataTask *task, id responseObject) { - OWSLogVerbose(@"Get devices request succeeded"); - NSArray *devices = [self parseResponse:responseObject]; - - if (devices) { - successCallback(devices); - } else { - OWSLogError(@"unable to parse devices response:%@", responseObject); - NSError *error = OWSErrorMakeUnableToProcessServerResponseError(); - failureCallback(error); - } - } - failure:^(NSURLSessionDataTask *task, NSError *error) { - OWSLogVerbose(@"Get devices request failed with error: %@", error); - failureCallback(error); - }]; -} - -+ (void)unlinkDevice:(OWSDevice *)device - success:(void (^)(void))successCallback - failure:(void (^)(NSError *))failureCallback -{ - TSRequest *request = [OWSRequestFactory deleteDeviceRequestWithDevice:device]; - - [[TSNetworkManager sharedManager] makeRequest:request - success:^(NSURLSessionDataTask *task, id responseObject) { - OWSLogVerbose(@"Delete device request succeeded"); - successCallback(); - } - failure:^(NSURLSessionDataTask *task, NSError *error) { - OWSLogVerbose(@"Get devices request failed with error: %@", error); - failureCallback(error); - }]; -} - -+ (NSArray *)parseResponse:(id)responseObject -{ - if (![responseObject isKindOfClass:[NSDictionary class]]) { - OWSLogError(@"Device response was not a dictionary."); - return nil; - } - NSDictionary *response = (NSDictionary *)responseObject; - - NSArray *devicesAttributes = response[@"devices"]; - if (!devicesAttributes) { - OWSLogError(@"Device response had no devices."); - return nil; - } - - NSMutableArray *devices = [NSMutableArray new]; - for (NSDictionary *deviceAttributes in devicesAttributes) { - NSError *error; - OWSDevice *_Nullable device = [OWSDevice deviceFromJSONDictionary:deviceAttributes error:&error]; - if (error || !device) { - OWSLogError(@"Failed to build device from dictionary with error: %@", error); - } else { - [devices addObject:device]; - } - } - - return [devices copy]; -} - -@end - -NS_ASSUME_NONNULL_END diff --git a/SignalUtilitiesKit/OWSDisappearingMessagesConfigurationMessage.h b/SignalUtilitiesKit/OWSDisappearingMessagesConfigurationMessage.h deleted file mode 100644 index 200a18e06..000000000 --- a/SignalUtilitiesKit/OWSDisappearingMessagesConfigurationMessage.h +++ /dev/null @@ -1,30 +0,0 @@ -// -// Copyright (c) 2019 Open Whisper Systems. All rights reserved. -// - -#import - -NS_ASSUME_NONNULL_BEGIN - -@class OWSDisappearingMessagesConfiguration; - -@interface OWSDisappearingMessagesConfigurationMessage : TSOutgoingMessage - -// MJK TODO - remove senderTimestamp -- (instancetype)initOutgoingMessageWithTimestamp:(uint64_t)timestamp - inThread:(nullable TSThread *)thread - messageBody:(nullable NSString *)body - attachmentIds:(NSMutableArray *)attachmentIds - expiresInSeconds:(uint32_t)expiresInSeconds - expireStartedAt:(uint64_t)expireStartedAt - isVoiceMessage:(BOOL)isVoiceMessage - groupMetaMessage:(TSGroupMetaMessage)groupMetaMessage - quotedMessage:(nullable TSQuotedMessage *)quotedMessage - contactShare:(nullable OWSContact *)contactShare - linkPreview:(nullable OWSLinkPreview *)linkPreview NS_UNAVAILABLE; - -- (instancetype)initWithConfiguration:(OWSDisappearingMessagesConfiguration *)configuration thread:(TSThread *)thread; - -@end - -NS_ASSUME_NONNULL_END diff --git a/SignalUtilitiesKit/OWSDisappearingMessagesConfigurationMessage.m b/SignalUtilitiesKit/OWSDisappearingMessagesConfigurationMessage.m deleted file mode 100644 index e787fc317..000000000 --- a/SignalUtilitiesKit/OWSDisappearingMessagesConfigurationMessage.m +++ /dev/null @@ -1,72 +0,0 @@ -// -// Copyright (c) 2019 Open Whisper Systems. All rights reserved. -// - -#import "OWSDisappearingMessagesConfigurationMessage.h" -#import "OWSDisappearingMessagesConfiguration.h" -#import -#import - -NS_ASSUME_NONNULL_BEGIN - -@interface OWSDisappearingMessagesConfigurationMessage () - -@property (nonatomic, readonly) OWSDisappearingMessagesConfiguration *configuration; - -@end - -#pragma mark - - -@implementation OWSDisappearingMessagesConfigurationMessage - -- (BOOL)shouldBeSaved -{ - return NO; -} - -- (uint)ttl { return (uint)[LKTTLUtilities getTTLFor:LKMessageTypeDisappearingMessagesConfiguration]; } - -- (instancetype)initWithConfiguration:(OWSDisappearingMessagesConfiguration *)configuration thread:(TSThread *)thread -{ - // MJK TODO - remove sender timestamp - self = [super initOutgoingMessageWithTimestamp:[NSDate ows_millisecondTimeStamp] - inThread:thread - messageBody:nil - attachmentIds:[NSMutableArray new] - expiresInSeconds:0 - expireStartedAt:0 - isVoiceMessage:NO - groupMetaMessage:TSGroupMetaMessageUnspecified - quotedMessage:nil - contactShare:nil - linkPreview:nil]; - if (!self) { - return self; - } - - _configuration = configuration; - - return self; -} - - -- (nullable id)dataMessageBuilder -{ - SSKProtoDataMessageBuilder *_Nullable dataMessageBuilder = [super dataMessageBuilder]; - if (!dataMessageBuilder) { - return nil; - } - [dataMessageBuilder setTimestamp:self.timestamp]; - [dataMessageBuilder setFlags:SSKProtoDataMessageFlagsExpirationTimerUpdate]; - if (self.configuration.isEnabled) { - [dataMessageBuilder setExpireTimer:self.configuration.durationSeconds]; - } else { - [dataMessageBuilder setExpireTimer:0]; - } - - return dataMessageBuilder; -} - -@end - -NS_ASSUME_NONNULL_END diff --git a/SignalUtilitiesKit/OWSDisappearingMessagesJob.m b/SignalUtilitiesKit/OWSDisappearingMessagesJob.m index 0a3dec690..7f987a1fc 100644 --- a/SignalUtilitiesKit/OWSDisappearingMessagesJob.m +++ b/SignalUtilitiesKit/OWSDisappearingMessagesJob.m @@ -6,7 +6,6 @@ #import "AppContext.h" #import "AppReadiness.h" #import "ContactsManagerProtocol.h" -#import "NSTimer+OWS.h" #import "OWSBackgroundTask.h" #import "OWSDisappearingConfigurationUpdateInfoMessage.h" #import "OWSDisappearingMessagesConfiguration.h" @@ -111,13 +110,6 @@ void AssertIsOnDisappearingMessagesQueue() return queue; } -#pragma mark - Dependencies - -- (id)contactsManager -{ - return SSKEnvironment.shared.contactsManager; -} - #pragma mark - - (NSUInteger)deleteExpiredMessages @@ -218,8 +210,7 @@ void AssertIsOnDisappearingMessagesQueue() NSString *_Nullable remoteContactName = nil; if (remoteRecipientId) { - remoteContactName = [self.contactsManager displayNameForPhoneIdentifier:remoteRecipientId - transaction:transaction]; + remoteContactName = [SSKEnvironment.shared.profileManager profileNameForRecipientWithID:remoteRecipientId avoidingWriteTransaction:YES]; } // Become eventually consistent in the case that the remote changed their settings at the same time. diff --git a/SignalUtilitiesKit/OWSDynamicOutgoingMessage.h b/SignalUtilitiesKit/OWSDynamicOutgoingMessage.h deleted file mode 100644 index ec0e0dc20..000000000 --- a/SignalUtilitiesKit/OWSDynamicOutgoingMessage.h +++ /dev/null @@ -1,36 +0,0 @@ -// -// Copyright (c) 2019 Open Whisper Systems. All rights reserved. -// - -#import - -NS_ASSUME_NONNULL_BEGIN - -@class SSKProtoDataMessageBuilder; -@class SignalRecipient; - -typedef NSData *_Nonnull (^DynamicOutgoingMessageBlock)(SignalRecipient *); - -/// This class is only used in debug tools -@interface OWSDynamicOutgoingMessage : TSOutgoingMessage - -- (instancetype)initOutgoingMessageWithTimestamp:(uint64_t)timestamp - inThread:(nullable TSThread *)thread - messageBody:(nullable NSString *)body - attachmentIds:(NSMutableArray *)attachmentIds - expiresInSeconds:(uint32_t)expiresInSeconds - expireStartedAt:(uint64_t)expireStartedAt - isVoiceMessage:(BOOL)isVoiceMessage - groupMetaMessage:(TSGroupMetaMessage)groupMetaMessage - quotedMessage:(nullable TSQuotedMessage *)quotedMessage - contactShare:(nullable OWSContact *)contactShare - linkPreview:(nullable OWSLinkPreview *)linkPreview NS_UNAVAILABLE; - -- (instancetype)initWithPlainTextDataBlock:(DynamicOutgoingMessageBlock)block thread:(nullable TSThread *)thread; -- (instancetype)initWithPlainTextDataBlock:(DynamicOutgoingMessageBlock)block - timestamp:(uint64_t)timestamp - thread:(nullable TSThread *)thread; - -@end - -NS_ASSUME_NONNULL_END diff --git a/SignalUtilitiesKit/OWSDynamicOutgoingMessage.m b/SignalUtilitiesKit/OWSDynamicOutgoingMessage.m deleted file mode 100644 index 00d212809..000000000 --- a/SignalUtilitiesKit/OWSDynamicOutgoingMessage.m +++ /dev/null @@ -1,64 +0,0 @@ -// -// Copyright (c) 2019 Open Whisper Systems. All rights reserved. -// - -#import "OWSDynamicOutgoingMessage.h" -#import - - -NS_ASSUME_NONNULL_BEGIN - -@interface OWSDynamicOutgoingMessage () - -@property (nonatomic, readonly) DynamicOutgoingMessageBlock block; - -@end - -#pragma mark - - -@implementation OWSDynamicOutgoingMessage - -- (instancetype)initWithPlainTextDataBlock:(DynamicOutgoingMessageBlock)block thread:(nullable TSThread *)thread -{ - return [self initWithPlainTextDataBlock:block timestamp:[NSDate ows_millisecondTimeStamp] thread:thread]; -} - -// MJK TODO can we remove sender timestamp? -- (instancetype)initWithPlainTextDataBlock:(DynamicOutgoingMessageBlock)block - timestamp:(uint64_t)timestamp - thread:(nullable TSThread *)thread -{ - self = [super initOutgoingMessageWithTimestamp:timestamp - inThread:thread - messageBody:nil - attachmentIds:[NSMutableArray new] - expiresInSeconds:0 - expireStartedAt:0 - isVoiceMessage:NO - groupMetaMessage:TSGroupMetaMessageUnspecified - quotedMessage:nil - contactShare:nil - linkPreview:nil]; - - if (self) { - _block = block; - } - - return self; -} - -- (BOOL)shouldBeSaved -{ - return NO; -} - -- (nullable NSData *)buildPlainTextData:(SignalRecipient *)recipient -{ - NSData *plainTextData = self.block(recipient); - OWSAssertDebug(plainTextData); - return plainTextData; -} - -@end - -NS_ASSUME_NONNULL_END diff --git a/SignalUtilitiesKit/OWSEndSessionMessage.h b/SignalUtilitiesKit/OWSEndSessionMessage.h deleted file mode 100644 index ea65a0443..000000000 --- a/SignalUtilitiesKit/OWSEndSessionMessage.h +++ /dev/null @@ -1,30 +0,0 @@ -// -// Copyright (c) 2019 Open Whisper Systems. All rights reserved. -// - -#import - -NS_ASSUME_NONNULL_BEGIN - -NS_SWIFT_NAME(EndSessionMessage) -@interface OWSEndSessionMessage : TSOutgoingMessage - -- (instancetype)initOutgoingMessageWithTimestamp:(uint64_t)timestamp - inThread:(nullable TSThread *)thread - messageBody:(nullable NSString *)body - attachmentIds:(NSMutableArray *)attachmentIds - expiresInSeconds:(uint32_t)expiresInSeconds - expireStartedAt:(uint64_t)expireStartedAt - isVoiceMessage:(BOOL)isVoiceMessage - groupMetaMessage:(TSGroupMetaMessage)groupMetaMessage - quotedMessage:(nullable TSQuotedMessage *)quotedMessage - contactShare:(nullable OWSContact *)contactShare - linkPreview:(nullable OWSLinkPreview *)linkPreview NS_UNAVAILABLE; - -// MJK TODO can we remove the sender timestamp? -- (instancetype)initWithTimestamp:(uint64_t)timestamp inThread:(nullable TSThread *)thread NS_DESIGNATED_INITIALIZER; -- (instancetype)initWithCoder:(NSCoder *)coder NS_DESIGNATED_INITIALIZER; - -@end - -NS_ASSUME_NONNULL_END diff --git a/SignalUtilitiesKit/OWSEndSessionMessage.m b/SignalUtilitiesKit/OWSEndSessionMessage.m deleted file mode 100644 index 287bda45d..000000000 --- a/SignalUtilitiesKit/OWSEndSessionMessage.m +++ /dev/null @@ -1,74 +0,0 @@ -// -// Copyright (c) 2019 Open Whisper Systems. All rights reserved. -// - -#import "OWSEndSessionMessage.h" -#import "OWSPrimaryStorage+Loki.h" -#import "SignalRecipient.h" -#import -#import - -NS_ASSUME_NONNULL_BEGIN - -@implementation OWSEndSessionMessage - -- (instancetype)initWithCoder:(NSCoder *)coder -{ - return [super initWithCoder:coder]; -} - -- (instancetype)initWithTimestamp:(uint64_t)timestamp inThread:(nullable TSThread *)thread -{ - return [super initOutgoingMessageWithTimestamp:timestamp - inThread:thread - messageBody:nil - attachmentIds:[NSMutableArray new] - expiresInSeconds:0 - expireStartedAt:0 - isVoiceMessage:NO - groupMetaMessage:TSGroupMetaMessageUnspecified - quotedMessage:nil - contactShare:nil - linkPreview:nil]; -} - -- (BOOL)shouldBeSaved -{ - return NO; -} - -- (uint)ttl { return (uint)[LKTTLUtilities getTTLFor:LKMessageTypeEphemeral]; } - -- (nullable id)dataMessageBuilder -{ - SSKProtoDataMessageBuilder *_Nullable builder = [super dataMessageBuilder]; - if (!builder) { - return nil; - } - [builder setTimestamp:self.timestamp]; - [builder setFlags:SSKProtoDataMessageFlagsEndSession]; - - return builder; -} - -- (nullable id)prepareCustomContentBuilder:(SignalRecipient *)recipient { - SSKProtoContentBuilder *builder = [super prepareCustomContentBuilder:recipient]; - - PreKeyBundle *bundle = [OWSPrimaryStorage.sharedManager generatePreKeyBundleForContact:recipient.recipientId]; - SSKProtoPrekeyBundleMessageBuilder *preKeyBuilder = [SSKProtoPrekeyBundleMessage builderFromPreKeyBundle:bundle]; - - // Build the pre key bundle message - NSError *error; - SSKProtoPrekeyBundleMessage *_Nullable message = [preKeyBuilder buildAndReturnError:&error]; - if (error || !message) { - OWSFailDebug(@"Failed to build pre key bundle for: %@ due to error: %@.", recipient.recipientId, error); - } else { - [builder setPrekeyBundleMessage:message]; - } - - return builder; -} - -@end - -NS_ASSUME_NONNULL_END diff --git a/SignalUtilitiesKit/OWSFingerprint.h b/SignalUtilitiesKit/OWSFingerprint.h deleted file mode 100644 index 36904a41d..000000000 --- a/SignalUtilitiesKit/OWSFingerprint.h +++ /dev/null @@ -1,52 +0,0 @@ -// Created by Michael Kirk on 9/14/16. -// Copyright © 2016 Open Whisper Systems. All rights reserved. - -#import - -NS_ASSUME_NONNULL_BEGIN - -@class UIImage; - -@interface OWSFingerprint : NSObject - -#pragma mark - Initializers - -- (instancetype)init NS_UNAVAILABLE; - -- (instancetype)initWithMyStableId:(NSString *)myStableId - myIdentityKey:(NSData *)myIdentityKeyWithoutKeyType - theirStableId:(NSString *)theirStableId - theirIdentityKey:(NSData *)theirIdentityKeyWithoutKeyType - theirName:(NSString *)theirName - hashIterations:(uint32_t)hashIterations NS_DESIGNATED_INITIALIZER; - -+ (instancetype)fingerprintWithMyStableId:(NSString *)myStableId - myIdentityKey:(NSData *)myIdentityKeyWithoutKeyType - theirStableId:(NSString *)theirStableId - theirIdentityKey:(NSData *)theirIdentityKeyWithoutKeyType - theirName:(NSString *)theirName - hashIterations:(uint32_t)hashIterations; - -+ (instancetype)fingerprintWithMyStableId:(NSString *)myStableId - myIdentityKey:(NSData *)myIdentityKeyWithoutKeyType - theirStableId:(NSString *)theirStableId - theirIdentityKey:(NSData *)theirIdentityKeyWithoutKeyType - theirName:(NSString *)theirName; - -#pragma mark - Properties - -@property (nonatomic, readonly) NSData *myStableIdData; -@property (nonatomic, readonly) NSData *myIdentityKey; -@property (nonatomic, readonly) NSString *theirStableId; -@property (nonatomic, readonly) NSData *theirStableIdData; -@property (nonatomic, readonly) NSData *theirIdentityKey; -@property (nonatomic, readonly) NSString *displayableText; -@property (nullable, nonatomic, readonly) UIImage *image; - -#pragma mark - Instance Methods - -- (BOOL)matchesLogicalFingerprintsData:(NSData *)data error:(NSError **)error; - -@end - -NS_ASSUME_NONNULL_END diff --git a/SignalUtilitiesKit/OWSFingerprint.m b/SignalUtilitiesKit/OWSFingerprint.m deleted file mode 100644 index 20c777d31..000000000 --- a/SignalUtilitiesKit/OWSFingerprint.m +++ /dev/null @@ -1,334 +0,0 @@ -// -// Copyright (c) 2018 Open Whisper Systems. All rights reserved. -// - -#import "OWSFingerprint.h" -#import "OWSError.h" -#import -#import -#import -#import -#import - -NS_ASSUME_NONNULL_BEGIN - -static uint32_t const OWSFingerprintHashingVersion = 0; -static uint32_t const OWSFingerprintScannableFormatVersion = 1; -static uint32_t const OWSFingerprintDefaultHashIterations = 5200; - -@interface OWSFingerprint () - -@property (nonatomic, readonly) NSUInteger hashIterations; -@property (nonatomic, readonly) NSString *text; -@property (nonatomic, readonly) NSData *myFingerprintData; -@property (nonatomic, readonly) NSData *theirFingerprintData; -@property (nonatomic, readonly) NSString *theirName; - -@end - -#pragma mark - - -@implementation OWSFingerprint - -- (instancetype)initWithMyStableId:(NSString *)myStableId - myIdentityKey:(NSData *)myIdentityKeyWithoutKeyType - theirStableId:(NSString *)theirStableId - theirIdentityKey:(NSData *)theirIdentityKeyWithoutKeyType - theirName:(NSString *)theirName - hashIterations:(uint32_t)hashIterations -{ - OWSAssertDebug(theirIdentityKeyWithoutKeyType.length == 32); - OWSAssertDebug(myIdentityKeyWithoutKeyType.length == 32); - - self = [super init]; - if (!self) { - return self; - } - - _myStableIdData = [myStableId dataUsingEncoding:NSUTF8StringEncoding]; - _myIdentityKey = [myIdentityKeyWithoutKeyType prependKeyType]; - _theirStableId = theirStableId; - _theirStableIdData = [theirStableId dataUsingEncoding:NSUTF8StringEncoding]; - _theirIdentityKey = [theirIdentityKeyWithoutKeyType prependKeyType]; - _theirName = theirName; - _hashIterations = hashIterations; - - _myFingerprintData = [self dataForStableId:_myStableIdData publicKey:_myIdentityKey]; - _theirFingerprintData = [self dataForStableId:_theirStableIdData publicKey:_theirIdentityKey]; - - return self; -} - -+ (instancetype)fingerprintWithMyStableId:(NSString *)myStableId - myIdentityKey:(NSData *)myIdentityKeyWithoutKeyType - theirStableId:(NSString *)theirStableId - theirIdentityKey:(NSData *)theirIdentityKeyWithoutKeyType - theirName:(NSString *)theirName - hashIterations:(uint32_t)hashIterations -{ - return [[self alloc] initWithMyStableId:myStableId - myIdentityKey:myIdentityKeyWithoutKeyType - theirStableId:theirStableId - theirIdentityKey:theirIdentityKeyWithoutKeyType - theirName:theirName - hashIterations:hashIterations]; -} - -+ (instancetype)fingerprintWithMyStableId:(NSString *)myStableId - myIdentityKey:(NSData *)myIdentityKeyWithoutKeyType - theirStableId:(NSString *)theirStableId - theirIdentityKey:(NSData *)theirIdentityKeyWithoutKeyType - theirName:(NSString *)theirName -{ - return [[self alloc] initWithMyStableId:myStableId - myIdentityKey:myIdentityKeyWithoutKeyType - theirStableId:theirStableId - theirIdentityKey:theirIdentityKeyWithoutKeyType - theirName:theirName - hashIterations:OWSFingerprintDefaultHashIterations]; -} - -- (BOOL)matchesLogicalFingerprintsData:(NSData *)data error:(NSError **)error -{ - OWSAssertDebug(data.length > 0); - OWSAssertDebug(error); - - *error = nil; - FingerprintProtoLogicalFingerprints *_Nullable logicalFingerprints; - logicalFingerprints = [FingerprintProtoLogicalFingerprints parseData:data error:error]; - if (!logicalFingerprints || *error) { - OWSFailDebug(@"fingerprint failure: %@", *error); - - NSString *description = NSLocalizedString(@"PRIVACY_VERIFICATION_FAILURE_INVALID_QRCODE", @"alert body"); - *error = OWSErrorWithCodeDescription(OWSErrorCodePrivacyVerificationFailure, description); - return NO; - } - - if (logicalFingerprints.version < OWSFingerprintScannableFormatVersion) { - OWSLogWarn(@"Verification failed. They're running an old version."); - NSString *description - = NSLocalizedString(@"PRIVACY_VERIFICATION_FAILED_WITH_OLD_REMOTE_VERSION", @"alert body"); - *error = OWSErrorWithCodeDescription(OWSErrorCodePrivacyVerificationFailure, description); - return NO; - } - - if (logicalFingerprints.version > OWSFingerprintScannableFormatVersion) { - OWSLogWarn(@"Verification failed. We're running an old version."); - NSString *description = NSLocalizedString(@"PRIVACY_VERIFICATION_FAILED_WITH_OLD_LOCAL_VERSION", @"alert body"); - *error = OWSErrorWithCodeDescription(OWSErrorCodePrivacyVerificationFailure, description); - return NO; - } - - // Their local is *our* remote. - FingerprintProtoLogicalFingerprint *localFingerprint = logicalFingerprints.remoteFingerprint; - FingerprintProtoLogicalFingerprint *remoteFingerprint = logicalFingerprints.localFingerprint; - - if (![remoteFingerprint.identityData isEqual:[self scannableData:self.theirFingerprintData]]) { - OWSLogWarn(@"Verification failed. We have the wrong fingerprint for them"); - NSString *descriptionFormat = NSLocalizedString(@"PRIVACY_VERIFICATION_FAILED_I_HAVE_WRONG_KEY_FOR_THEM", - @"Alert body when verifying with {{contact name}}"); - NSString *description = [NSString stringWithFormat:descriptionFormat, self.theirName]; - *error = OWSErrorWithCodeDescription(OWSErrorCodePrivacyVerificationFailure, description); - return NO; - } - - if (![localFingerprint.identityData isEqual:[self scannableData:self.myFingerprintData]]) { - OWSLogWarn(@"Verification failed. They have the wrong fingerprint for us"); - NSString *descriptionFormat = NSLocalizedString(@"PRIVACY_VERIFICATION_FAILED_THEY_HAVE_WRONG_KEY_FOR_ME", - @"Alert body when verifying with {{contact name}}"); - NSString *description = [NSString stringWithFormat:descriptionFormat, self.theirName]; - *error = OWSErrorWithCodeDescription(OWSErrorCodePrivacyVerificationFailure, description); - return NO; - } - - OWSLogWarn(@"Verification Succeeded."); - return YES; -} - -- (NSString *)text -{ - NSString *myDisplayString = [self stringForFingerprintData:self.myFingerprintData]; - NSString *theirDisplayString = [self stringForFingerprintData:self.theirFingerprintData]; - - if ([theirDisplayString compare:myDisplayString] == NSOrderedAscending) { - return [NSString stringWithFormat:@"%@%@", theirDisplayString, myDisplayString]; - } else { - return [NSString stringWithFormat:@"%@%@", myDisplayString, theirDisplayString]; - } -} - -/** - * Formats numeric fingerprint, 3 lines in groups of 5 digits. - */ -- (NSString *)displayableText -{ - NSString *input = self.text; - - NSMutableArray *lines = [NSMutableArray new]; - - NSUInteger lineLength = self.text.length / 3; - for (uint i = 0; i < 3; i++) { - NSString *line = [input substringWithRange:NSMakeRange(i * lineLength, lineLength)]; - - NSMutableArray *chunks = [NSMutableArray new]; - for (uint i = 0; i < line.length / 5; i++) { - NSString *nextChunk = [line substringWithRange:NSMakeRange(i * 5, 5)]; - [chunks addObject:nextChunk]; - } - [lines addObject:[chunks componentsJoinedByString:@" "]]; - } - - return [lines componentsJoinedByString:@"\n"]; -} - - -- (NSData *)dataFromShort:(uint32_t)aShort -{ - uint8_t bytes[] = { - ((uint8_t)(aShort & 0xFF00) >> 8), - (uint8_t)(aShort & 0x00FF) - }; - - return [NSData dataWithBytes:bytes length:2]; -} - -/** - * An identifier for a mutable public key, belonging to an immutable identifier (stableId). - * - * This method is intended to be somewhat expensive to produce in order to be brute force adverse. - * - * @param stableIdData - * Immutable global identifier e.g. Signal Identifier, an e164 formatted phone number encoded as UTF-8 data - * @param publicKey - * The current public key for - * @return - * All-number textual representation - */ -- (NSData *)dataForStableId:(NSData *)stableIdData publicKey:(NSData *)publicKey -{ - OWSAssertDebug(stableIdData); - OWSAssertDebug(publicKey); - - NSData *versionData = [self dataFromShort:OWSFingerprintHashingVersion]; - NSMutableData *hash = [versionData mutableCopy]; - [hash appendData:publicKey]; - [hash appendData:stableIdData]; - - NSMutableData *_Nullable digestData = [[NSMutableData alloc] initWithLength:CC_SHA512_DIGEST_LENGTH]; - if (!digestData) { - @throw [NSException exceptionWithName:NSGenericException reason:@"Couldn't allocate buffer." userInfo:nil]; - } - for (int i = 0; i < self.hashIterations; i++) { - [hash appendData:publicKey]; - - if (hash.length >= UINT32_MAX) { - @throw [NSException exceptionWithName:@"Oversize Data" reason:@"Oversize hash." userInfo:nil]; - } - - CC_SHA512(hash.bytes, (uint32_t)hash.length, digestData.mutableBytes); - // TODO get rid of this loop-allocation - hash = [digestData mutableCopy]; - } - - return [hash copy]; -} - - -- (NSString *)stringForFingerprintData:(NSData *)data -{ - OWSAssertDebug(data); - - return [NSString stringWithFormat:@"%@%@%@%@%@%@", - [self encodedChunkFromData:data offset:0], - [self encodedChunkFromData:data offset:5], - [self encodedChunkFromData:data offset:10], - [self encodedChunkFromData:data offset:15], - [self encodedChunkFromData:data offset:20], - [self encodedChunkFromData:data offset:25]]; -} - -- (NSString *)encodedChunkFromData:(NSData *)data offset:(uint)offset -{ - OWSAssertDebug(data); - - uint8_t fiveBytes[5]; - [data getBytes:fiveBytes range:NSMakeRange(offset, 5)]; - - int chunk = [self uint64From5Bytes:fiveBytes] % 100000; - return [NSString stringWithFormat:@"%05d", chunk]; -} - -- (int64_t)uint64From5Bytes:(uint8_t[])bytes -{ - int64_t result = ((bytes[0] & 0xffLL) << 32) | - ((bytes[1] & 0xffLL) << 24) | - ((bytes[2] & 0xffLL) << 16) | - ((bytes[3] & 0xffLL) << 8) | - ((bytes[4] & 0xffLL)); - - return result; -} - -- (NSData *)scannableData:(NSData *)data -{ - return [data subdataWithRange:NSMakeRange(0, 32)]; -} - -- (nullable UIImage *)image -{ - FingerprintProtoLogicalFingerprintBuilder *remoteFingerprintBuilder = - [FingerprintProtoLogicalFingerprint builderWithIdentityData:[self scannableData:self.theirFingerprintData]]; - NSError *error; - FingerprintProtoLogicalFingerprint *_Nullable remoteFingerprint = - [remoteFingerprintBuilder buildAndReturnError:&error]; - if (!remoteFingerprint || error) { - OWSFailDebug(@"could not build proto: %@", error); - return nil; - } - - FingerprintProtoLogicalFingerprintBuilder *localFingerprintBuilder = - [FingerprintProtoLogicalFingerprint builderWithIdentityData:[self scannableData:self.myFingerprintData]]; - FingerprintProtoLogicalFingerprint *_Nullable localFingerprint = - [localFingerprintBuilder buildAndReturnError:&error]; - if (!localFingerprint || error) { - OWSFailDebug(@"could not build proto: %@", error); - return nil; - } - - FingerprintProtoLogicalFingerprintsBuilder *logicalFingerprintsBuilder = - [FingerprintProtoLogicalFingerprints builderWithVersion:OWSFingerprintScannableFormatVersion - localFingerprint:localFingerprint - remoteFingerprint:remoteFingerprint]; - - // Build ByteMode QR (Latin-1 encodable data) - NSData *_Nullable fingerprintData = [logicalFingerprintsBuilder buildSerializedDataAndReturnError:&error]; - if (!fingerprintData || error) { - OWSFailDebug(@"could not serialize proto: %@", error); - return nil; - } - - OWSLogDebug(@"Building fingerprint with data: %@", fingerprintData); - - CIFilter *filter = [CIFilter filterWithName:@"CIQRCodeGenerator"]; - [filter setDefaults]; - [filter setValue:fingerprintData forKey:@"inputMessage"]; - - CIImage *ciImage = [filter outputImage]; - if (!ciImage) { - OWSLogError(@"Failed to create QR image from fingerprint text: %@", self.text); - return nil; - } - - // UIImages backed by a CIImage won't render without antialiasing, so we convert the backign image to a CGImage, - // which can be scaled crisply. - CIContext *context = [CIContext contextWithOptions:nil]; - CGImageRef cgImage = [context createCGImage:ciImage fromRect:ciImage.extent]; - UIImage *qrImage = [UIImage imageWithCGImage:cgImage]; - CGImageRelease(cgImage); - - return qrImage; -} - -@end - -NS_ASSUME_NONNULL_END diff --git a/SignalUtilitiesKit/OWSFingerprintBuilder.h b/SignalUtilitiesKit/OWSFingerprintBuilder.h deleted file mode 100644 index 0f0b2bb0a..000000000 --- a/SignalUtilitiesKit/OWSFingerprintBuilder.h +++ /dev/null @@ -1,32 +0,0 @@ -// -// Copyright (c) 2017 Open Whisper Systems. All rights reserved. -// - -#import - -NS_ASSUME_NONNULL_BEGIN - -@class TSAccountManager; -@class OWSFingerprint; -@protocol ContactsManagerProtocol; - -@interface OWSFingerprintBuilder : NSObject - -- (instancetype)init NS_UNAVAILABLE; -- (instancetype)initWithAccountManager:(TSAccountManager *)accountManager - contactsManager:(id)contactsManager NS_DESIGNATED_INITIALIZER; - -/** - * Builds a fingerprint combining your current credentials with their most recently accepted credentials. - */ -- (nullable OWSFingerprint *)fingerprintWithTheirSignalId:(NSString *)theirSignalId; - -/** - * Builds a fingerprint combining your current credentials with the specified identity key. - * You can use this to present a new identity key for verification. - */ -- (OWSFingerprint *)fingerprintWithTheirSignalId:(NSString *)theirSignalId theirIdentityKey:(NSData *)theirIdentityKey; - -@end - -NS_ASSUME_NONNULL_END diff --git a/SignalUtilitiesKit/OWSFingerprintBuilder.m b/SignalUtilitiesKit/OWSFingerprintBuilder.m deleted file mode 100644 index 2b3df6d24..000000000 --- a/SignalUtilitiesKit/OWSFingerprintBuilder.m +++ /dev/null @@ -1,66 +0,0 @@ -// -// Copyright (c) 2018 Open Whisper Systems. All rights reserved. -// - -#import "OWSFingerprintBuilder.h" -#import "ContactsManagerProtocol.h" -#import "OWSFingerprint.h" -#import "OWSIdentityManager.h" -#import "TSAccountManager.h" -#import -#import - -NS_ASSUME_NONNULL_BEGIN - -@interface OWSFingerprintBuilder () - -@property (nonatomic, readonly) TSAccountManager *accountManager; -@property (nonatomic, readonly) id contactsManager; - -@end - -@implementation OWSFingerprintBuilder - -- (instancetype)initWithAccountManager:(TSAccountManager *)accountManager - contactsManager:(id)contactsManager -{ - self = [super init]; - if (!self) { - return self; - } - - _accountManager = accountManager; - _contactsManager = contactsManager; - - return self; -} - -- (nullable OWSFingerprint *)fingerprintWithTheirSignalId:(NSString *)theirSignalId -{ - NSData *_Nullable theirIdentityKey = [[OWSIdentityManager sharedManager] identityKeyForRecipientId:theirSignalId]; - - if (theirIdentityKey == nil) { - OWSFailDebug(@"Missing their identity key"); - return nil; - } - - return [self fingerprintWithTheirSignalId:theirSignalId theirIdentityKey:theirIdentityKey]; -} - -- (OWSFingerprint *)fingerprintWithTheirSignalId:(NSString *)theirSignalId theirIdentityKey:(NSData *)theirIdentityKey -{ - NSString *theirName = [self.contactsManager displayNameForPhoneIdentifier:theirSignalId]; - - NSString *mySignalId = [self.accountManager localNumber]; - NSData *myIdentityKey = [[OWSIdentityManager sharedManager] identityKeyPair].publicKey; - - return [OWSFingerprint fingerprintWithMyStableId:mySignalId - myIdentityKey:myIdentityKey - theirStableId:theirSignalId - theirIdentityKey:theirIdentityKey - theirName:theirName]; -} - -@end - -NS_ASSUME_NONNULL_END diff --git a/SignalUtilitiesKit/OWSGroupAvatarBuilder.h b/SignalUtilitiesKit/OWSGroupAvatarBuilder.h deleted file mode 100644 index db48c77ba..000000000 --- a/SignalUtilitiesKit/OWSGroupAvatarBuilder.h +++ /dev/null @@ -1,21 +0,0 @@ -// -// Copyright (c) 2018 Open Whisper Systems. All rights reserved. -// - -#import - -NS_ASSUME_NONNULL_BEGIN - -@class TSGroupThread; - -@interface OWSGroupAvatarBuilder : OWSAvatarBuilder - -- (instancetype)initWithThread:(TSGroupThread *)thread diameter:(NSUInteger)diameter; - -+ (nullable UIImage *)defaultAvatarForGroupId:(NSData *)groupId - conversationColorName:(NSString *)conversationColorName - diameter:(NSUInteger)diameter; - -@end - -NS_ASSUME_NONNULL_END diff --git a/SignalUtilitiesKit/OWSGroupAvatarBuilder.m b/SignalUtilitiesKit/OWSGroupAvatarBuilder.m deleted file mode 100644 index 09cd5749b..000000000 --- a/SignalUtilitiesKit/OWSGroupAvatarBuilder.m +++ /dev/null @@ -1,101 +0,0 @@ -// -// Copyright (c) 2018 Open Whisper Systems. All rights reserved. -// - -#import -#import "OWSGroupAvatarBuilder.h" -#import "OWSContactsManager.h" -#import "TSGroupThread.h" -#import "UIColor+OWS.h" -#import "Theme.h" -#import -#import - -NS_ASSUME_NONNULL_BEGIN - -@interface OWSGroupAvatarBuilder () - -@property (nonatomic, readonly) TSGroupThread *thread; -@property (nonatomic, readonly) NSUInteger diameter; - -@end - -@implementation OWSGroupAvatarBuilder - -- (instancetype)initWithThread:(TSGroupThread *)thread diameter:(NSUInteger)diameter -{ - self = [super init]; - if (!self) { - return self; - } - - _thread = thread; - _diameter = diameter; - - return self; -} - -#pragma mark - Dependencies - -+ (OWSContactsManager *)contactsManager -{ - return (OWSContactsManager *)SSKEnvironment.shared.contactsManager; -} - -#pragma mark - - -- (nullable UIImage *)buildSavedImage -{ - return self.thread.groupModel.groupImage; -} - -- (nullable UIImage *)buildDefaultImage -{ - return [self.class defaultAvatarForGroupId:self.thread.groupModel.groupId - conversationColorName:self.thread.conversationColorName - diameter:self.diameter]; -} - -+ (nullable UIImage *)defaultAvatarForGroupId:(NSData *)groupId - conversationColorName:(NSString *)conversationColorName - diameter:(NSUInteger)diameter -{ - NSString *cacheKey = [NSString stringWithFormat:@"%@-%d", groupId.hexadecimalString, Theme.isDarkThemeEnabled]; - - UIImage *_Nullable cachedAvatar = - [OWSGroupAvatarBuilder.contactsManager.avatarCache imageForKey:cacheKey diameter:(CGFloat)diameter]; - if (cachedAvatar) { - return cachedAvatar; - } - -#ifdef SHOW_COLOR_PICKER - UIColor *backgroundColor = - [OWSConversationColor conversationColorOrDefaultForColorName:conversationColorName].themeColor; -#else - UIColor *backgroundColor = UIColor.ows_darkSkyBlueColor; -#endif - UIImage *_Nullable image = - [OWSGroupAvatarBuilder groupAvatarImageWithBackgroundColor:backgroundColor diameter:diameter]; - if (!image) { - OWSFailDebug(@"Could not create group avatar."); - return nil; - } - - [OWSGroupAvatarBuilder.contactsManager.avatarCache setImage:image forKey:cacheKey diameter:diameter]; - return image; -} - -+ (nullable UIImage *)groupAvatarImageWithBackgroundColor:(UIColor *)backgroundColor diameter:(NSUInteger)diameter -{ - UIImage *icon = [UIImage imageNamed:@"group-avatar"]; - // The group-avatar asset is designed for the kStandardAvatarSize. - // Adjust its size to reflect the actual output diameter. - CGFloat scaling = diameter / (CGFloat)kStandardAvatarSize; - CGSize iconSize = CGSizeScale(icon.size, scaling); - return - [OWSAvatarBuilder avatarImageWithIcon:icon iconSize:iconSize backgroundColor:backgroundColor diameter:diameter]; -} - -@end - -NS_ASSUME_NONNULL_END diff --git a/SignalUtilitiesKit/OWSGroupsOutputStream.m b/SignalUtilitiesKit/OWSGroupsOutputStream.m index 7c7a2af23..cd6941758 100644 --- a/SignalUtilitiesKit/OWSGroupsOutputStream.m +++ b/SignalUtilitiesKit/OWSGroupsOutputStream.m @@ -25,29 +25,11 @@ NS_ASSUME_NONNULL_BEGIN SSKProtoGroupDetailsBuilder *groupBuilder = [SSKProtoGroupDetails builderWithId:group.groupId]; [groupBuilder setName:group.groupName]; [groupBuilder setMembers:group.groupMemberIds]; - [groupBuilder setColor:groupThread.conversationColorName]; [groupBuilder setAdmins:group.groupAdminIds]; if ([OWSBlockingManager.sharedManager isGroupIdBlocked:group.groupId]) { [groupBuilder setBlocked:YES]; } - /* - NSData *avatarPng; - if (group.groupImage) { - SSKProtoGroupDetailsAvatarBuilder *avatarBuilder = [SSKProtoGroupDetailsAvatar builder]; - - [avatarBuilder setContentType:OWSMimeTypeImagePng]; - avatarPng = UIImagePNGRepresentation(group.groupImage); - [avatarBuilder setLength:(uint32_t)avatarPng.length]; - - NSError *error; - SSKProtoGroupDetailsAvatar *_Nullable avatarProto = [avatarBuilder buildAndReturnError:&error]; - if (error || !avatarProto) { - OWSFailDebug(@"could not build protobuf: %@", error); - } else { - [groupBuilder setAvatar:avatarProto]; - } - } */ OWSDisappearingMessagesConfiguration *_Nullable disappearingMessagesConfiguration = [OWSDisappearingMessagesConfiguration fetchObjectWithUniqueID:groupThread.uniqueId transaction:transaction]; @@ -72,12 +54,6 @@ NS_ASSUME_NONNULL_BEGIN [self writeUInt32:groupDataLength]; [self writeData:groupData]; - - /* - if (avatarPng) { - [self writeData:avatarPng]; - } - */ } @end diff --git a/SignalUtilitiesKit/OWSIdentityManager.h b/SignalUtilitiesKit/OWSIdentityManager.h index 49140fba6..c2e4e61a9 100644 --- a/SignalUtilitiesKit/OWSIdentityManager.h +++ b/SignalUtilitiesKit/OWSIdentityManager.h @@ -44,21 +44,6 @@ extern const NSUInteger kStoredIdentityKeyLength; - (void)generateNewIdentityKeyPair; - (void)clearIdentityKey; -- (void)setVerificationState:(OWSVerificationState)verificationState - identityKey:(NSData *)identityKey - recipientId:(NSString *)recipientId - isUserInitiatedChange:(BOOL)isUserInitiatedChange - transaction:(YapDatabaseReadWriteTransaction *)transaction; - -- (OWSVerificationState)verificationStateForRecipientId:(NSString *)recipientId; -- (OWSVerificationState)verificationStateForRecipientId:(NSString *)recipientId - transaction:(YapDatabaseReadTransaction *)transaction; - -- (void)setVerificationState:(OWSVerificationState)verificationState - identityKey:(NSData *)identityKey - recipientId:(NSString *)recipientId - isUserInitiatedChange:(BOOL)isUserInitiatedChange; - - (nullable OWSRecipientIdentity *)recipientIdentityForRecipientId:(NSString *)recipientId; /** diff --git a/SignalUtilitiesKit/OWSIdentityManager.m b/SignalUtilitiesKit/OWSIdentityManager.m index c8cff86fb..9da61ac84 100644 --- a/SignalUtilitiesKit/OWSIdentityManager.m +++ b/SignalUtilitiesKit/OWSIdentityManager.m @@ -9,13 +9,9 @@ #import "NotificationsProtocol.h" #import "OWSError.h" #import "OWSFileSystem.h" -#import "OWSMessageSender.h" -#import "OWSOutgoingNullMessage.h" -#import "OWSPrimaryStorage+sessionStore.h" +#import "OWSPrimaryStorage+SessionStore.h" #import "OWSPrimaryStorage.h" #import "OWSRecipientIdentity.h" -#import "OWSVerificationStateChangeMessage.h" -#import "OWSVerificationStateSyncMessage.h" #import "SSKEnvironment.h" #import "TSAccountManager.h" #import "TSContactThread.h" @@ -101,15 +97,6 @@ NSString *const kNSNotificationName_IdentityStateDidChange = @"kNSNotificationNa [[NSNotificationCenter defaultCenter] removeObserver:self]; } -#pragma mark - Dependencies - -- (OWSMessageSender *)messageSender -{ - OWSAssertDebug(SSKEnvironment.shared.messageSender); - - return SSKEnvironment.shared.messageSender; -} - #pragma mark - - (void)observeNotifications @@ -283,115 +270,6 @@ NSString *const kNSNotificationName_IdentityStateDidChange = @"kNSNotificationNa return NO; } -- (void)setVerificationState:(OWSVerificationState)verificationState - identityKey:(NSData *)identityKey - recipientId:(NSString *)recipientId - isUserInitiatedChange:(BOOL)isUserInitiatedChange -{ - OWSAssertDebug(identityKey.length == kStoredIdentityKeyLength); - OWSAssertDebug(recipientId.length > 0); - - [LKStorage writeSyncWithBlock:^(YapDatabaseReadWriteTransaction *_Nonnull transaction) { - [self setVerificationState:verificationState - identityKey:identityKey - recipientId:recipientId - isUserInitiatedChange:isUserInitiatedChange - transaction:transaction]; - }]; -} - -- (void)setVerificationState:(OWSVerificationState)verificationState - identityKey:(NSData *)identityKey - recipientId:(NSString *)recipientId - isUserInitiatedChange:(BOOL)isUserInitiatedChange - protocolContext:(nullable id)protocolContext -{ - OWSAssertDebug(identityKey.length == kStoredIdentityKeyLength); - OWSAssertDebug(recipientId.length > 0); - OWSAssertDebug([protocolContext isKindOfClass:[YapDatabaseReadWriteTransaction class]]); - - YapDatabaseReadWriteTransaction *transaction = protocolContext; - - [self setVerificationState:verificationState - identityKey:identityKey - recipientId:recipientId - isUserInitiatedChange:isUserInitiatedChange - transaction:transaction]; -} - -- (void)setVerificationState:(OWSVerificationState)verificationState - identityKey:(NSData *)identityKey - recipientId:(NSString *)recipientId - isUserInitiatedChange:(BOOL)isUserInitiatedChange - transaction:(YapDatabaseReadWriteTransaction *)transaction -{ - OWSAssertDebug(identityKey.length == kStoredIdentityKeyLength); - OWSAssertDebug(recipientId.length > 0); - OWSAssertDebug(transaction); - - // Ensure a remote identity exists for this key. We may be learning about - // it for the first time. - [self saveRemoteIdentity:identityKey recipientId:recipientId protocolContext:transaction]; - - OWSRecipientIdentity *recipientIdentity = - [OWSRecipientIdentity fetchObjectWithUniqueID:recipientId transaction:transaction]; - - if (recipientIdentity == nil) { - OWSFailDebug(@"Missing expected identity: %@", recipientId); - return; - } - - if (recipientIdentity.verificationState == verificationState) { - return; - } - - OWSLogInfo(@"setVerificationState: %@ (%@ -> %@)", - recipientId, - OWSVerificationStateToString(recipientIdentity.verificationState), - OWSVerificationStateToString(verificationState)); - - [recipientIdentity updateWithVerificationState:verificationState transaction:transaction]; - - if (isUserInitiatedChange) { - [self saveChangeMessagesForRecipientId:recipientId - verificationState:verificationState - isLocalChange:YES - transaction:transaction]; - [self enqueueSyncMessageForVerificationStateForRecipientId:recipientId transaction:transaction]; - } else { - // Cancel any pending verification state sync messages for this recipient. - [self clearSyncMessageForRecipientId:recipientId transaction:transaction]; - } - - [self fireIdentityStateChangeNotification]; -} - -- (OWSVerificationState)verificationStateForRecipientId:(NSString *)recipientId -{ - __block OWSVerificationState result; - [self.dbConnection readWithBlock:^(YapDatabaseReadTransaction *_Nonnull transaction) { - result = [self verificationStateForRecipientId:recipientId transaction:transaction]; - }]; - return result; -} - -- (OWSVerificationState)verificationStateForRecipientId:(NSString *)recipientId - transaction:(YapDatabaseReadTransaction *)transaction -{ - OWSAssertDebug(recipientId.length > 0); - OWSAssertDebug(transaction); - - OWSRecipientIdentity *_Nullable currentIdentity = - [OWSRecipientIdentity fetchObjectWithUniqueID:recipientId transaction:transaction]; - - if (!currentIdentity) { - // We might not know the identity for this recipient yet. - return OWSVerificationStateDefault; - } - - return currentIdentity.verificationState; -} - - (nullable OWSRecipientIdentity *)recipientIdentityForRecipientId:(NSString *)recipientId { OWSAssertDebug(recipientId.length > 0); @@ -585,6 +463,7 @@ NSString *const kNSNotificationName_IdentityStateDidChange = @"kNSNotificationNa - (void)syncQueuedVerificationStates { + /* dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ NSMutableArray *recipientIds = [NSMutableArray new]; [self.dbConnection readWithBlock:^(YapDatabaseReadTransaction *transaction) { @@ -636,50 +515,7 @@ NSString *const kNSNotificationName_IdentityStateDidChange = @"kNSNotificationNa } } }); -} - -- (void)sendSyncVerificationStateMessage:(OWSVerificationStateSyncMessage *)message -{ - OWSAssertDebug(message); - OWSAssertDebug(message.verificationForRecipientId.length > 0); - - TSContactThread *contactThread = [TSContactThread getOrCreateThreadWithContactId:message.verificationForRecipientId]; - - // Send null message to appear as though we're sending a normal message to cover the sync messsage sent - // subsequently - OWSOutgoingNullMessage *nullMessage = [[OWSOutgoingNullMessage alloc] initWithContactThread:contactThread - verificationStateSyncMessage:message]; - - // DURABLE CLEANUP - we could replace the custom durability logic in this class - // with a durable JobQueue. - [self.messageSender sendMessage:nullMessage - success:^{ - OWSLogInfo(@"Successfully sent verification state NullMessage"); - [self.messageSender sendMessage:message - success:^{ - OWSLogInfo(@"Successfully sent verification state sync message"); - - // Record that this verification state was successfully synced. - [LKStorage writeSyncWithBlock:^(YapDatabaseReadWriteTransaction *transaction) { - [self clearSyncMessageForRecipientId:message.verificationForRecipientId - transaction:transaction]; - }]; - } - failure:^(NSError *error) { - OWSLogError(@"Failed to send verification state sync message with error: %@", error); - }]; - } - failure:^(NSError *_Nonnull error) { - OWSLogError(@"Failed to send verification state NullMessage with error: %@", error); - if (error.code == OWSErrorCodeNoSuchSignalRecipient) { - OWSLogInfo(@"Removing retries for syncing verification state, since user is no longer registered: %@", - message.verificationForRecipientId); - // Otherwise this will fail forever. - [LKStorage writeSyncWithBlock:^(YapDatabaseReadWriteTransaction *transaction) { - [self clearSyncMessageForRecipientId:message.verificationForRecipientId transaction:transaction]; - }]; - } - }]; + */ } - (void)clearSyncMessageForRecipientId:(NSString *)recipientId @@ -848,50 +684,6 @@ NSString *const kNSNotificationName_IdentityStateDidChange = @"kNSNotificationNa [recipientIdentity updateWithVerificationState:verificationState transaction:transaction]; - - [self saveChangeMessagesForRecipientId:recipientId - verificationState:verificationState - isLocalChange:NO - transaction:transaction]; - } -} - -// We only want to create change messages in response to user activity, -// on any of their devices. -- (void)saveChangeMessagesForRecipientId:(NSString *)recipientId - verificationState:(OWSVerificationState)verificationState - isLocalChange:(BOOL)isLocalChange - transaction:(YapDatabaseReadWriteTransaction *)transaction -{ - OWSAssertDebug(recipientId.length > 0); - OWSAssertDebug(transaction); - - NSMutableArray *messages = [NSMutableArray new]; - - TSContactThread *contactThread = - [TSContactThread getOrCreateThreadWithContactId:recipientId transaction:transaction]; - OWSAssertDebug(contactThread); - // MJK TODO - should be safe to remove senderTimestamp - [messages addObject:[[OWSVerificationStateChangeMessage alloc] initWithTimestamp:[NSDate ows_millisecondTimeStamp] - thread:contactThread - recipientId:recipientId - verificationState:verificationState - isLocalChange:isLocalChange]]; - - for (TSGroupThread *groupThread in - [TSGroupThread groupThreadsWithRecipientId:recipientId transaction:transaction]) { - // MJK TODO - should be safe to remove senderTimestamp - [messages - addObject:[[OWSVerificationStateChangeMessage alloc] initWithTimestamp:[NSDate ows_millisecondTimeStamp] - thread:groupThread - recipientId:recipientId - verificationState:verificationState - isLocalChange:isLocalChange]]; - } - - // MJK TODO - why not save in-line, vs storing in an array and saving the array? - for (TSMessage *message in messages) { - [message saveWithTransaction:transaction]; } } diff --git a/SignalUtilitiesKit/OWSIncomingSentMessageTranscript.h b/SignalUtilitiesKit/OWSIncomingSentMessageTranscript.h deleted file mode 100644 index 02da7d4c0..000000000 --- a/SignalUtilitiesKit/OWSIncomingSentMessageTranscript.h +++ /dev/null @@ -1,52 +0,0 @@ -// -// Copyright (c) 2019 Open Whisper Systems. All rights reserved. -// - -#import - -NS_ASSUME_NONNULL_BEGIN - -@class OWSContact; -@class OWSLinkPreview; -@class SSKProtoAttachmentPointer; -@class SSKProtoDataMessage; -@class SSKProtoSyncMessageSent; -@class TSQuotedMessage; -@class TSThread; -@class YapDatabaseReadWriteTransaction; - -/** - * Represents notification of a message sent on our behalf from another device. - * E.g. When we send a message from Signal-Desktop we want to see it in our conversation on iPhone. - */ -@interface OWSIncomingSentMessageTranscript : NSObject - -- (instancetype)initWithProto:(SSKProtoSyncMessageSent *)sentProto - transaction:(YapDatabaseReadWriteTransaction *)transaction; - -@property (nonatomic, readonly) SSKProtoDataMessage *dataMessage; -@property (nonatomic, readonly) NSString *recipientId; -@property (nonatomic, readonly) uint64_t timestamp; -@property (nonatomic, readonly) uint64_t expirationStartedAt; -@property (nonatomic, readonly) uint32_t expirationDuration; -@property (nonatomic, readonly) BOOL isGroupUpdate; -@property (nonatomic, readonly) BOOL isGroupQuit; -@property (nonatomic, readonly) BOOL isExpirationTimerUpdate; -@property (nonatomic, readonly) BOOL isEndSessionMessage; -@property (nonatomic, readonly, nullable) NSData *groupId; -@property (nonatomic, readonly) NSString *body; -@property (nonatomic, readonly) NSArray *attachmentPointerProtos; -@property (nonatomic, readonly, nullable) TSThread *thread; -@property (nonatomic, readonly, nullable) TSQuotedMessage *quotedMessage; -@property (nonatomic, readonly, nullable) OWSContact *contact; -@property (nonatomic, readonly, nullable) OWSLinkPreview *linkPreview; -@property (nonatomic, readonly) BOOL isRecipientUpdate; - -// If either nonUdRecipientIds or udRecipientIds is nil, -// this is either a legacy transcript or it reflects a legacy sync message. -@property (nonatomic, readonly, nullable) NSArray *nonUdRecipientIds; -@property (nonatomic, readonly, nullable) NSArray *udRecipientIds; - -@end - -NS_ASSUME_NONNULL_END diff --git a/SignalUtilitiesKit/OWSIncomingSentMessageTranscript.m b/SignalUtilitiesKit/OWSIncomingSentMessageTranscript.m deleted file mode 100644 index 5c73f7347..000000000 --- a/SignalUtilitiesKit/OWSIncomingSentMessageTranscript.m +++ /dev/null @@ -1,108 +0,0 @@ -// -// Copyright (c) 2019 Open Whisper Systems. All rights reserved. -// - -#import "OWSIncomingSentMessageTranscript.h" -#import "OWSContact.h" -#import "OWSMessageManager.h" -#import "OWSPrimaryStorage.h" -#import "TSContactThread.h" -#import "TSGroupModel.h" -#import "TSGroupThread.h" -#import "TSOutgoingMessage.h" -#import "TSQuotedMessage.h" -#import "TSThread.h" -#import - -NS_ASSUME_NONNULL_BEGIN - -@implementation OWSIncomingSentMessageTranscript - -- (instancetype)initWithProto:(SSKProtoSyncMessageSent *)sentProto - transaction:(YapDatabaseReadWriteTransaction *)transaction -{ - self = [super init]; - if (!self) { - return self; - } - - _dataMessage = sentProto.message; - _recipientId = sentProto.destination; - _timestamp = sentProto.timestamp; - _expirationStartedAt = sentProto.expirationStartTimestamp; - _expirationDuration = sentProto.message.expireTimer; - _body = _dataMessage.body; - _groupId = _dataMessage.group.id; - _isGroupUpdate = _dataMessage.group != nil && (_dataMessage.group.type == SSKProtoGroupContextTypeUpdate); - _isGroupQuit = _dataMessage.group != nil && (_dataMessage.group.type == SSKProtoGroupContextTypeQuit); - _isExpirationTimerUpdate = (_dataMessage.flags & SSKProtoDataMessageFlagsExpirationTimerUpdate) != 0; - _isEndSessionMessage = (_dataMessage.flags & SSKProtoDataMessageFlagsEndSession) != 0; - _isRecipientUpdate = sentProto.isRecipientUpdate; - - if (self.isRecipientUpdate) { - // Fetch, don't create. We don't want recipient updates to resurrect messages or threads. - if (self.dataMessage.group) { - _thread = [TSGroupThread threadWithGroupId:_dataMessage.group.id transaction:transaction]; - } else { - OWSFailDebug(@"We should never receive a 'recipient update' for messages in contact threads."); - } - // Skip the other processing for recipient updates. - } else { - if (self.dataMessage.group) { - _thread = [TSGroupThread getOrCreateThreadWithGroupId:_dataMessage.group.id groupType:closedGroup transaction:transaction]; - } else { - _thread = [TSContactThread getOrCreateThreadWithContactId:_recipientId transaction:transaction]; - } - - _quotedMessage = - [TSQuotedMessage quotedMessageForDataMessage:_dataMessage thread:_thread transaction:transaction]; - _contact = [OWSContacts contactForDataMessage:_dataMessage transaction:transaction]; - - NSError *linkPreviewError; - _linkPreview = [OWSLinkPreview buildValidatedLinkPreviewWithDataMessage:_dataMessage - body:_body - transaction:transaction - error:&linkPreviewError]; - if (linkPreviewError && ![OWSLinkPreview isNoPreviewError:linkPreviewError]) { - OWSLogError(@"linkPreviewError: %@", linkPreviewError); - } - } - - if (sentProto.unidentifiedStatus.count > 0) { - NSMutableArray *nonUdRecipientIds = [NSMutableArray new]; - NSMutableArray *udRecipientIds = [NSMutableArray new]; - for (SSKProtoSyncMessageSentUnidentifiedDeliveryStatus *statusProto in sentProto.unidentifiedStatus) { - if (!statusProto.hasDestination || statusProto.destination.length < 1) { - OWSFailDebug(@"Delivery status proto is missing destination."); - continue; - } - if (!statusProto.hasUnidentified) { - OWSFailDebug(@"Delivery status proto is missing value."); - continue; - } - NSString *recipientId = statusProto.destination; - if (statusProto.unidentified) { - [udRecipientIds addObject:recipientId]; - } else { - [nonUdRecipientIds addObject:recipientId]; - } - } - _nonUdRecipientIds = [nonUdRecipientIds copy]; - _udRecipientIds = [udRecipientIds copy]; - } - - return self; -} - -- (NSArray *)attachmentPointerProtos -{ - if (self.isGroupUpdate && self.dataMessage.group.avatar) { - return @[ self.dataMessage.group.avatar ]; - } else { - return self.dataMessage.attachments; - } -} - -@end - -NS_ASSUME_NONNULL_END diff --git a/SignalUtilitiesKit/OWSLinkedDeviceReadReceipt.h b/SignalUtilitiesKit/OWSLinkedDeviceReadReceipt.h deleted file mode 100644 index b9858bb3b..000000000 --- a/SignalUtilitiesKit/OWSLinkedDeviceReadReceipt.h +++ /dev/null @@ -1,26 +0,0 @@ -// -// Copyright (c) 2018 Open Whisper Systems. All rights reserved. -// - -#import - -NS_ASSUME_NONNULL_BEGIN - -@interface OWSLinkedDeviceReadReceipt : TSYapDatabaseObject - -@property (nonatomic, readonly) NSString *senderId; -@property (nonatomic, readonly) uint64_t messageIdTimestamp; -@property (nonatomic, readonly) uint64_t readTimestamp; - -- (instancetype)initWithSenderId:(NSString *)senderId - messageIdTimestamp:(uint64_t)messageIdtimestamp - readTimestamp:(uint64_t)readTimestamp; - -+ (nullable OWSLinkedDeviceReadReceipt *)findLinkedDeviceReadReceiptWithSenderId:(NSString *)senderId - messageIdTimestamp:(uint64_t)messageIdTimestamp - transaction: - (YapDatabaseReadTransaction *)transaction; - -@end - -NS_ASSUME_NONNULL_END diff --git a/SignalUtilitiesKit/OWSLinkedDeviceReadReceipt.m b/SignalUtilitiesKit/OWSLinkedDeviceReadReceipt.m deleted file mode 100644 index e113afa68..000000000 --- a/SignalUtilitiesKit/OWSLinkedDeviceReadReceipt.m +++ /dev/null @@ -1,77 +0,0 @@ -// -// Copyright (c) 2018 Open Whisper Systems. All rights reserved. -// - -#import "OWSLinkedDeviceReadReceipt.h" -#import - -NS_ASSUME_NONNULL_BEGIN - -@implementation OWSLinkedDeviceReadReceipt - -- (instancetype)initWithSenderId:(NSString *)senderId - messageIdTimestamp:(uint64_t)messageIdTimestamp - readTimestamp:(uint64_t)readTimestamp -{ - OWSAssertDebug(senderId.length > 0 && messageIdTimestamp > 0); - - NSString *receiptId = - [OWSLinkedDeviceReadReceipt uniqueIdForSenderId:senderId messageIdTimestamp:messageIdTimestamp]; - self = [super initWithUniqueId:receiptId]; - if (!self) { - return self; - } - - _senderId = senderId; - _messageIdTimestamp = messageIdTimestamp; - _readTimestamp = readTimestamp; - - return self; -} - -- (nullable instancetype)initWithCoder:(NSCoder *)coder -{ - self = [super initWithCoder:coder]; - if (!self) { - return self; - } - - // renamed timestamp -> messageIdTimestamp - if (!_messageIdTimestamp) { - NSNumber *_Nullable legacyTimestamp = (NSNumber *)[coder decodeObjectForKey:@"timestamp"]; - OWSAssertDebug(legacyTimestamp.unsignedLongLongValue > 0); - _messageIdTimestamp = legacyTimestamp.unsignedLongLongValue; - } - - // For legacy objects, before we were tracking read time, use the original messages "sent" timestamp - // as the local read time. This will always be at least a little bit earlier than the message was - // actually read, which isn't ideal, but safer than persisting a disappearing message too long, especially - // since we know they read it on their linked desktop. - if (_readTimestamp == 0) { - _readTimestamp = _messageIdTimestamp; - } - - return self; -} - -+ (NSString *)uniqueIdForSenderId:(NSString *)senderId messageIdTimestamp:(uint64_t)messageIdTimestamp -{ - OWSAssertDebug(senderId.length > 0 && messageIdTimestamp > 0); - - return [NSString stringWithFormat:@"%@-%llu", senderId, messageIdTimestamp]; -} - -+ (nullable OWSLinkedDeviceReadReceipt *)findLinkedDeviceReadReceiptWithSenderId:(NSString *)senderId - messageIdTimestamp:(uint64_t)messageIdTimestamp - transaction: - (YapDatabaseReadTransaction *)transaction -{ - OWSAssertDebug(transaction); - NSString *receiptId = - [OWSLinkedDeviceReadReceipt uniqueIdForSenderId:senderId messageIdTimestamp:messageIdTimestamp]; - return [OWSLinkedDeviceReadReceipt fetchObjectWithUniqueID:receiptId transaction:transaction]; -} - -@end - -NS_ASSUME_NONNULL_END diff --git a/SignalUtilitiesKit/OWSMessageServiceParams.h b/SignalUtilitiesKit/OWSMessageServiceParams.h index 3c4639e4d..42872d680 100644 --- a/SignalUtilitiesKit/OWSMessageServiceParams.h +++ b/SignalUtilitiesKit/OWSMessageServiceParams.h @@ -24,21 +24,13 @@ NS_ASSUME_NONNULL_BEGIN @property (nonatomic, readonly) BOOL silent; @property (nonatomic, readonly) BOOL online; -// Loki: Message ttl -@property (nonatomic, readonly) uint ttl; - -// Loki: Wether this message is a p2p ping -@property (nonatomic, readonly) BOOL isPing; - - (instancetype)initWithType:(TSWhisperMessageType)type recipientId:(NSString *)destination device:(int)deviceId content:(NSData *)content isSilent:(BOOL)isSilent isOnline:(BOOL)isOnline - registrationId:(int)registrationId - ttl:(uint)ttl - isPing:(BOOL)isPing; + registrationId:(int)registrationId; @end diff --git a/SignalUtilitiesKit/OWSMessageServiceParams.m b/SignalUtilitiesKit/OWSMessageServiceParams.m index d7a0cbf12..a2a66121c 100644 --- a/SignalUtilitiesKit/OWSMessageServiceParams.m +++ b/SignalUtilitiesKit/OWSMessageServiceParams.m @@ -22,8 +22,6 @@ NS_ASSUME_NONNULL_BEGIN isSilent:(BOOL)isSilent isOnline:(BOOL)isOnline registrationId:(int)registrationId - ttl:(uint)ttl - isPing:(BOOL)isPing { self = [super init]; @@ -38,8 +36,6 @@ NS_ASSUME_NONNULL_BEGIN _content = [content base64EncodedString]; _silent = isSilent; _online = isOnline; - _ttl = ttl; - _isPing = isPing; return self; } diff --git a/SignalUtilitiesKit/OWSMessageUtils.m b/SignalUtilitiesKit/OWSMessageUtils.m index c95c0ad1f..52ad56c4b 100644 --- a/SignalUtilitiesKit/OWSMessageUtils.m +++ b/SignalUtilitiesKit/OWSMessageUtils.m @@ -5,7 +5,7 @@ #import "OWSMessageUtils.h" #import "AppContext.h" #import "MIMETypeUtil.h" -#import "OWSMessageSender.h" + #import "OWSPrimaryStorage.h" #import "TSAccountManager.h" #import "TSAttachment.h" diff --git a/SignalUtilitiesKit/OWSOperation.m b/SignalUtilitiesKit/OWSOperation.m index 6bdfc712c..8cbd5f6e9 100644 --- a/SignalUtilitiesKit/OWSOperation.m +++ b/SignalUtilitiesKit/OWSOperation.m @@ -4,7 +4,6 @@ #import "OWSOperation.h" #import "NSError+MessageSending.h" -#import "NSTimer+OWS.h" #import "OWSBackgroundTask.h" #import "OWSError.h" #import diff --git a/SignalUtilitiesKit/OWSOutgoingCallMessage.h b/SignalUtilitiesKit/OWSOutgoingCallMessage.h deleted file mode 100644 index b7ef2f942..000000000 --- a/SignalUtilitiesKit/OWSOutgoingCallMessage.h +++ /dev/null @@ -1,48 +0,0 @@ -// -// Copyright (c) 2019 Open Whisper Systems. All rights reserved. -// - -#import - -NS_ASSUME_NONNULL_BEGIN - -@class SSKProtoCallMessageAnswer; -@class SSKProtoCallMessageBusy; -@class SSKProtoCallMessageHangup; -@class SSKProtoCallMessageIceUpdate; -@class SSKProtoCallMessageOffer; -@class TSThread; - -/** - * WebRTC call signaling sent out of band, via the Signal Service - */ -@interface OWSOutgoingCallMessage : TSOutgoingMessage - -- (instancetype)initOutgoingMessageWithTimestamp:(uint64_t)timestamp - inThread:(nullable TSThread *)thread - messageBody:(nullable NSString *)body - attachmentIds:(NSMutableArray *)attachmentIds - expiresInSeconds:(uint32_t)expiresInSeconds - expireStartedAt:(uint64_t)expireStartedAt - isVoiceMessage:(BOOL)isVoiceMessage - groupMetaMessage:(TSGroupMetaMessage)groupMetaMessage - quotedMessage:(nullable TSQuotedMessage *)quotedMessage - contactShare:(nullable OWSContact *)contactShare - linkPreview:(nullable OWSLinkPreview *)linkPreview NS_UNAVAILABLE; - -- (instancetype)initWithThread:(TSThread *)thread offerMessage:(SSKProtoCallMessageOffer *)offerMessage; -- (instancetype)initWithThread:(TSThread *)thread answerMessage:(SSKProtoCallMessageAnswer *)answerMessage; -- (instancetype)initWithThread:(TSThread *)thread - iceUpdateMessages:(NSArray *)iceUpdateMessage; -- (instancetype)initWithThread:(TSThread *)thread hangupMessage:(SSKProtoCallMessageHangup *)hangupMessage; -- (instancetype)initWithThread:(TSThread *)thread busyMessage:(SSKProtoCallMessageBusy *)busyMessage; - -@property (nullable, nonatomic, readonly) SSKProtoCallMessageOffer *offerMessage; -@property (nullable, nonatomic, readonly) SSKProtoCallMessageAnswer *answerMessage; -@property (nullable, nonatomic, readonly) NSArray *iceUpdateMessages; -@property (nullable, nonatomic, readonly) SSKProtoCallMessageHangup *hangupMessage; -@property (nullable, nonatomic, readonly) SSKProtoCallMessageBusy *busyMessage; - -@end - -NS_ASSUME_NONNULL_END diff --git a/SignalUtilitiesKit/OWSOutgoingCallMessage.m b/SignalUtilitiesKit/OWSOutgoingCallMessage.m deleted file mode 100644 index 7a59d300f..000000000 --- a/SignalUtilitiesKit/OWSOutgoingCallMessage.m +++ /dev/null @@ -1,196 +0,0 @@ -// -// Copyright (c) 2019 Open Whisper Systems. All rights reserved. -// - -#import "OWSOutgoingCallMessage.h" -#import "ProtoUtils.h" -#import "SignalRecipient.h" -#import "TSContactThread.h" -#import -#import - -NS_ASSUME_NONNULL_BEGIN - -@implementation OWSOutgoingCallMessage - -- (instancetype)initWithThread:(TSThread *)thread -{ - // MJK TODO - safe to remove senderTimestamp - self = [super initOutgoingMessageWithTimestamp:[NSDate ows_millisecondTimeStamp] - inThread:thread - messageBody:nil - attachmentIds:[NSMutableArray new] - expiresInSeconds:0 - expireStartedAt:0 - isVoiceMessage:NO - groupMetaMessage:TSGroupMetaMessageUnspecified - quotedMessage:nil - contactShare:nil - linkPreview:nil]; - if (!self) { - return self; - } - - return self; -} - -- (instancetype)initWithThread:(TSThread *)thread offerMessage:(SSKProtoCallMessageOffer *)offerMessage -{ - self = [self initWithThread:thread]; - if (!self) { - return self; - } - - _offerMessage = offerMessage; - - return self; -} - -- (instancetype)initWithThread:(TSThread *)thread answerMessage:(SSKProtoCallMessageAnswer *)answerMessage -{ - self = [self initWithThread:thread]; - if (!self) { - return self; - } - - _answerMessage = answerMessage; - - return self; -} - -- (instancetype)initWithThread:(TSThread *)thread - iceUpdateMessages:(NSArray *)iceUpdateMessages -{ - self = [self initWithThread:thread]; - if (!self) { - return self; - } - - _iceUpdateMessages = iceUpdateMessages; - - return self; -} - -- (instancetype)initWithThread:(TSThread *)thread hangupMessage:(SSKProtoCallMessageHangup *)hangupMessage -{ - self = [self initWithThread:thread]; - if (!self) { - return self; - } - - _hangupMessage = hangupMessage; - - return self; -} - -- (instancetype)initWithThread:(TSThread *)thread busyMessage:(SSKProtoCallMessageBusy *)busyMessage -{ - self = [self initWithThread:thread]; - if (!self) { - return self; - } - - _busyMessage = busyMessage; - - return self; -} - -#pragma mark - TSOutgoingMessage overrides - -- (BOOL)shouldSyncTranscript -{ - return NO; -} - -- (BOOL)isSilent -{ - // Avoid "phantom messages" for "outgoing call messages". - - return YES; -} - -- (nullable NSData *)buildPlainTextData:(SignalRecipient *)recipient -{ - OWSAssertDebug(recipient); - - SSKProtoContentBuilder *builder = [SSKProtoContent builder]; - [builder setCallMessage:[self buildCallMessage:recipient.recipientId]]; - - NSError *error; - NSData *_Nullable data = [builder buildSerializedDataAndReturnError:&error]; - if (error || !data) { - OWSFailDebug(@"could not serialize protobuf: %@", error); - return nil; - } - return data; -} - -- (nullable SSKProtoCallMessage *)buildCallMessage:(NSString *)recipientId -{ - SSKProtoCallMessageBuilder *builder = [SSKProtoCallMessage builder]; - - if (self.offerMessage) { - [builder setOffer:self.offerMessage]; - } - - if (self.answerMessage) { - [builder setAnswer:self.answerMessage]; - } - - if (self.iceUpdateMessages.count > 0) { - [builder setIceUpdate:self.iceUpdateMessages]; - } - - if (self.hangupMessage) { - [builder setHangup:self.hangupMessage]; - } - - if (self.busyMessage) { - [builder setBusy:self.busyMessage]; - } - - [ProtoUtils addLocalProfileKeyIfNecessary:self.thread recipientId:recipientId callMessageBuilder:builder]; - - NSError *error; - SSKProtoCallMessage *_Nullable result = [builder buildAndReturnError:&error]; - if (error || !result) { - OWSFailDebug(@"could not build protobuf: %@", error); - return nil; - } - return result; -} - -#pragma mark - TSYapDatabaseObject overrides - -- (uint)ttl { return (uint)[LKTTLUtilities getTTLFor:LKMessageTypeCall]; } - -- (BOOL)shouldBeSaved -{ - return NO; -} - -- (NSString *)debugDescription -{ - NSString *className = NSStringFromClass([self class]); - - NSString *payload; - if (self.offerMessage) { - payload = @"offerMessage"; - } else if (self.answerMessage) { - payload = @"answerMessage"; - } else if (self.iceUpdateMessages.count > 0) { - payload = [NSString stringWithFormat:@"iceUpdateMessages: %lu", (unsigned long)self.iceUpdateMessages.count]; - } else if (self.hangupMessage) { - payload = @"hangupMessage"; - } else if (self.busyMessage) { - payload = @"busyMessage"; - } else { - payload = @"none"; - } - - return [NSString stringWithFormat:@"%@ with payload: %@", className, payload]; -} - -@end - -NS_ASSUME_NONNULL_END diff --git a/SignalUtilitiesKit/OWSOutgoingNullMessage.h b/SignalUtilitiesKit/OWSOutgoingNullMessage.h deleted file mode 100644 index 1f767fb5d..000000000 --- a/SignalUtilitiesKit/OWSOutgoingNullMessage.h +++ /dev/null @@ -1,31 +0,0 @@ -// -// Copyright (c) 2019 Open Whisper Systems. All rights reserved. -// - -#import - -NS_ASSUME_NONNULL_BEGIN - -@class OWSVerificationStateSyncMessage; -@class TSContactThread; - -@interface OWSOutgoingNullMessage : TSOutgoingMessage - -- (instancetype)initOutgoingMessageWithTimestamp:(uint64_t)timestamp - inThread:(nullable TSThread *)thread - messageBody:(nullable NSString *)body - attachmentIds:(NSMutableArray *)attachmentIds - expiresInSeconds:(uint32_t)expiresInSeconds - expireStartedAt:(uint64_t)expireStartedAt - isVoiceMessage:(BOOL)isVoiceMessage - groupMetaMessage:(TSGroupMetaMessage)groupMetaMessage - quotedMessage:(nullable TSQuotedMessage *)quotedMessage - contactShare:(nullable OWSContact *)contactShare - linkPreview:(nullable OWSLinkPreview *)linkPreview; - -- (instancetype)initWithContactThread:(TSContactThread *)contactThread - verificationStateSyncMessage:(OWSVerificationStateSyncMessage *)verificationStateSyncMessage; - -@end - -NS_ASSUME_NONNULL_END diff --git a/SignalUtilitiesKit/OWSOutgoingNullMessage.m b/SignalUtilitiesKit/OWSOutgoingNullMessage.m deleted file mode 100644 index 9e27356fc..000000000 --- a/SignalUtilitiesKit/OWSOutgoingNullMessage.m +++ /dev/null @@ -1,107 +0,0 @@ -// -// Copyright (c) 2019 Open Whisper Systems. All rights reserved. -// - -#import "OWSOutgoingNullMessage.h" -#import "OWSVerificationStateSyncMessage.h" -#import "TSContactThread.h" -#import -#import -#import - -NS_ASSUME_NONNULL_BEGIN - -@interface OWSOutgoingNullMessage () - -@property (nonatomic, readonly) OWSVerificationStateSyncMessage *verificationStateSyncMessage; - -@end - -#pragma mark - - -@implementation OWSOutgoingNullMessage - -- (instancetype)initWithContactThread:(TSContactThread *)contactThread - verificationStateSyncMessage:(OWSVerificationStateSyncMessage *)verificationStateSyncMessage -{ - // MJK TODO - remove senderTimestamp - self = [super initOutgoingMessageWithTimestamp:[NSDate ows_millisecondTimeStamp] - inThread:contactThread - messageBody:nil - attachmentIds:[NSMutableArray new] - expiresInSeconds:0 - expireStartedAt:0 - isVoiceMessage:NO - groupMetaMessage:TSGroupMetaMessageUnspecified - quotedMessage:nil - contactShare:nil - linkPreview:nil]; - if (!self) { - return self; - } - - _verificationStateSyncMessage = verificationStateSyncMessage; - - return self; -} - -#pragma mark - override TSOutgoingMessage - -- (nullable NSData *)buildPlainTextData:(SignalRecipient *)recipient -{ - SSKProtoNullMessageBuilder *nullMessageBuilder = [SSKProtoNullMessage builder]; - - NSUInteger contentLength; - if (self.verificationStateSyncMessage != nil) { - contentLength = self.verificationStateSyncMessage.unpaddedVerifiedLength; - - OWSAssertDebug(self.verificationStateSyncMessage.paddingBytesLength > 0); - - // We add the same amount of padding in the VerificationStateSync message and it's coresponding NullMessage so that - // the sync message is indistinguishable from an outgoing Sent transcript corresponding to the NullMessage. We pad - // the NullMessage so as to obscure it's content. The sync message (like all sync messages) will be *additionally* - // padded by the superclass while being sent. The end result is we send a NullMessage of a non-distinct size, and a - // verification sync which is ~1-512 bytes larger then that. - contentLength += self.verificationStateSyncMessage.paddingBytesLength; - } else { - contentLength = arc4random_uniform(512); - } - - OWSAssertDebug(contentLength > 0); - - nullMessageBuilder.padding = [Cryptography generateRandomBytes:contentLength]; - - NSError *error; - SSKProtoNullMessage *_Nullable nullMessage = [nullMessageBuilder buildAndReturnError:&error]; - if (error != nil || nullMessage == nil) { - OWSFailDebug(@"Couldn't build protobuf due to error: %@.", error); - return nil; - } - - SSKProtoContentBuilder *contentBuilder = [SSKProtoContent builder]; - contentBuilder.nullMessage = nullMessage; - - NSData *_Nullable contentData = [contentBuilder buildSerializedDataAndReturnError:&error]; - if (error != nil || contentData == nil) { - OWSFailDebug(@"Couldn't serialize protobuf due to error: %@.", error); - return nil; - } - - return contentData; -} - -- (uint)ttl { return (uint)[LKTTLUtilities getTTLFor:LKMessageTypeEphemeral]; } - -- (BOOL)shouldSyncTranscript -{ - return NO; -} - -- (BOOL)shouldBeSaved -{ - return NO; -} - -@end - -NS_ASSUME_NONNULL_END diff --git a/SignalUtilitiesKit/OWSOutgoingReceiptManager.m b/SignalUtilitiesKit/OWSOutgoingReceiptManager.m index 64376c7a9..c66e7419a 100644 --- a/SignalUtilitiesKit/OWSOutgoingReceiptManager.m +++ b/SignalUtilitiesKit/OWSOutgoingReceiptManager.m @@ -5,9 +5,8 @@ #import "OWSOutgoingReceiptManager.h" #import "AppReadiness.h" #import "OWSError.h" -#import "OWSMessageSender.h" +#import #import "OWSPrimaryStorage.h" -#import "OWSReceiptsForSenderMessage.h" #import "SSKEnvironment.h" #import "TSContactThread.h" #import "TSYapDatabaseObject.h" @@ -79,15 +78,6 @@ NSString *const kOutgoingReadReceiptManagerCollection = @"kOutgoingReadReceiptMa [[NSNotificationCenter defaultCenter] removeObserver:self]; } -#pragma mark - Dependencies - -- (OWSMessageSender *)messageSender -{ - OWSAssertDebug(SSKEnvironment.shared.messageSender); - - return SSKEnvironment.shared.messageSender; -} - #pragma mark - - (dispatch_queue_t)serialQueue @@ -178,7 +168,8 @@ NSString *const kOutgoingReadReceiptManagerCollection = @"kOutgoingReadReceiptMa if (![LKSessionMetaProtocol shouldSendReceiptInThread:thread]) { continue; } - + + /* OWSReceiptsForSenderMessage *message; NSString *receiptName; switch (receiptType) { @@ -220,6 +211,7 @@ NSString *const kOutgoingReadReceiptManagerCollection = @"kOutgoingReadReceiptMa }]; }]; [sendPromises addObject:sendPromise]; + */ } return [sendPromises copy]; diff --git a/SignalUtilitiesKit/OWSOutgoingSentMessageTranscript.h b/SignalUtilitiesKit/OWSOutgoingSentMessageTranscript.h deleted file mode 100644 index e100b72e9..000000000 --- a/SignalUtilitiesKit/OWSOutgoingSentMessageTranscript.h +++ /dev/null @@ -1,25 +0,0 @@ -// -// Copyright (c) 2019 Open Whisper Systems. All rights reserved. -// - -#import - -NS_ASSUME_NONNULL_BEGIN - -@class TSOutgoingMessage; - -/** - * Notifies your other registered devices (if you have any) that you've sent a message. - * This way the message you just sent can appear on all your devices. - */ -@interface OWSOutgoingSentMessageTranscript : OWSOutgoingSyncMessage - -- (instancetype)init NS_UNAVAILABLE; - -- (instancetype)initWithOutgoingMessage:(TSOutgoingMessage *)message - isRecipientUpdate:(BOOL)isRecipientUpdate NS_DESIGNATED_INITIALIZER; -- (nullable instancetype)initWithCoder:(NSCoder *)coder NS_DESIGNATED_INITIALIZER; - -@end - -NS_ASSUME_NONNULL_END diff --git a/SignalUtilitiesKit/OWSOutgoingSentMessageTranscript.m b/SignalUtilitiesKit/OWSOutgoingSentMessageTranscript.m deleted file mode 100644 index 96b96b9c7..000000000 --- a/SignalUtilitiesKit/OWSOutgoingSentMessageTranscript.m +++ /dev/null @@ -1,118 +0,0 @@ -// -// Copyright (c) 2019 Open Whisper Systems. All rights reserved. -// - -#import "OWSOutgoingSentMessageTranscript.h" -#import "TSOutgoingMessage.h" -#import "TSThread.h" -#import - -NS_ASSUME_NONNULL_BEGIN - -@interface TSOutgoingMessage (OWSOutgoingSentMessageTranscript) - -/** - * Normally this is private, but we need to embed this - * data structure within our own. - * - * recipientId is nil when building "sent" sync messages for messages - * sent to groups. - */ -- (nullable SSKProtoDataMessage *)buildDataMessage:(NSString *_Nullable)recipientId; - -@end - -#pragma mark - - -@interface OWSOutgoingSentMessageTranscript () - -@property (nonatomic, readonly) TSOutgoingMessage *message; - -// sentRecipientId is the recipient of message, for contact thread messages. -// It is used to identify the thread/conversation to desktop. -@property (nonatomic, readonly, nullable) NSString *sentRecipientId; - -@property (nonatomic, readonly) BOOL isRecipientUpdate; - -@end - -#pragma mark - - -@implementation OWSOutgoingSentMessageTranscript - -- (instancetype)initWithOutgoingMessage:(TSOutgoingMessage *)message isRecipientUpdate:(BOOL)isRecipientUpdate -{ - self = [super initWithTimestamp:message.timestamp]; - - if (!self) { - return self; - } - - _message = message; - // This will be nil for groups. - _sentRecipientId = message.thread.contactIdentifier; - _isRecipientUpdate = isRecipientUpdate; - - return self; -} - -- (nullable instancetype)initWithCoder:(NSCoder *)coder -{ - return [super initWithCoder:coder]; -} - -- (nullable SSKProtoSyncMessageBuilder *)syncMessageBuilder -{ - SSKProtoSyncMessageSentBuilder *sentBuilder = [SSKProtoSyncMessageSent builder]; - [sentBuilder setTimestamp:self.message.timestamp]; - [sentBuilder setDestination:self.sentRecipientId]; - [sentBuilder setIsRecipientUpdate:self.isRecipientUpdate]; - - SSKProtoDataMessage *_Nullable dataMessage = [self.message buildDataMessage:self.sentRecipientId]; - if (!dataMessage) { - OWSFailDebug(@"could not build protobuf."); - return nil; - } - [sentBuilder setMessage:dataMessage]; - [sentBuilder setExpirationStartTimestamp:self.message.timestamp]; - - for (NSString *recipientId in self.message.sentRecipientIds) { - TSOutgoingMessageRecipientState *_Nullable recipientState = - [self.message recipientStateForRecipientId:recipientId]; - if (!recipientState) { - continue; - } - if (recipientState.state != OWSOutgoingMessageRecipientStateSent) { - OWSFailDebug(@"unexpected recipient state for: %@", recipientId); - continue; - } - - NSError *error; - SSKProtoSyncMessageSentUnidentifiedDeliveryStatusBuilder *statusBuilder = - [SSKProtoSyncMessageSentUnidentifiedDeliveryStatus builder]; - [statusBuilder setDestination:recipientId]; - [statusBuilder setUnidentified:recipientState.wasSentByUD]; - SSKProtoSyncMessageSentUnidentifiedDeliveryStatus *_Nullable status = - [statusBuilder buildAndReturnError:&error]; - if (error || !status) { - OWSFailDebug(@"Couldn't build UD status proto: %@", error); - continue; - } - [sentBuilder addUnidentifiedStatus:status]; - } - - NSError *error; - SSKProtoSyncMessageSent *_Nullable sentProto = [sentBuilder buildAndReturnError:&error]; - if (error || !sentProto) { - OWSFailDebug(@"could not build protobuf: %@", error); - return nil; - } - - SSKProtoSyncMessageBuilder *syncMessageBuilder = [SSKProtoSyncMessage builder]; - [syncMessageBuilder setSent:sentProto]; - return syncMessageBuilder; -} - -@end - -NS_ASSUME_NONNULL_END diff --git a/SignalUtilitiesKit/OWSOutgoingSyncMessage.h b/SignalUtilitiesKit/OWSOutgoingSyncMessage.h deleted file mode 100644 index e5b2cf094..000000000 --- a/SignalUtilitiesKit/OWSOutgoingSyncMessage.h +++ /dev/null @@ -1,35 +0,0 @@ -// -// Copyright (c) 2019 Open Whisper Systems. All rights reserved. -// - -#import - -NS_ASSUME_NONNULL_BEGIN - -/** - * Abstract base class used for the family of sync messages which take care - * of keeping your multiple registered devices consistent. E.g. sharing contacts, sharing groups, - * notifiying your devices of sent messages, and "read" receipts. - */ -@interface OWSOutgoingSyncMessage : TSOutgoingMessage - -- (instancetype)initOutgoingMessageWithTimestamp:(uint64_t)timestamp - inThread:(nullable TSThread *)thread - messageBody:(nullable NSString *)body - attachmentIds:(NSMutableArray *)attachmentIds - expiresInSeconds:(uint32_t)expiresInSeconds - expireStartedAt:(uint64_t)expireStartedAt - isVoiceMessage:(BOOL)isVoiceMessage - groupMetaMessage:(TSGroupMetaMessage)groupMetaMessage - quotedMessage:(nullable TSQuotedMessage *)quotedMessage - contactShare:(nullable OWSContact *)contactShare - linkPreview:(nullable OWSLinkPreview *)linkPreview NS_UNAVAILABLE; - -- (instancetype)initWithTimestamp:(uint64_t)timestamp; - -- (instancetype)init NS_DESIGNATED_INITIALIZER; -- (nullable instancetype)initWithCoder:(NSCoder *)coder NS_DESIGNATED_INITIALIZER; - -@end - -NS_ASSUME_NONNULL_END diff --git a/SignalUtilitiesKit/OWSOutgoingSyncMessage.m b/SignalUtilitiesKit/OWSOutgoingSyncMessage.m deleted file mode 100644 index a45c7b457..000000000 --- a/SignalUtilitiesKit/OWSOutgoingSyncMessage.m +++ /dev/null @@ -1,125 +0,0 @@ -// -// Copyright (c) 2019 Open Whisper Systems. All rights reserved. -// - -#import "OWSOutgoingSyncMessage.h" -#import "ProtoUtils.h" -#import -#import -#import - -NS_ASSUME_NONNULL_BEGIN - -@implementation OWSOutgoingSyncMessage - -- (nullable instancetype)initWithCoder:(NSCoder *)coder -{ - return [super initWithCoder:coder]; -} - -- (instancetype)init -{ - // MJK TODO - remove SenderTimestamp - self = [super initOutgoingMessageWithTimestamp:[NSDate ows_millisecondTimeStamp] - inThread:nil - messageBody:nil - attachmentIds:[NSMutableArray new] - expiresInSeconds:0 - expireStartedAt:0 - isVoiceMessage:NO - groupMetaMessage:TSGroupMetaMessageUnspecified - quotedMessage:nil - contactShare:nil - linkPreview:nil]; - - if (!self) { - return self; - } - - return self; -} - -- (instancetype)initWithTimestamp:(uint64_t)timestamp -{ - self = [super initOutgoingMessageWithTimestamp:timestamp - inThread:nil - messageBody:nil - attachmentIds:[NSMutableArray new] - expiresInSeconds:0 - expireStartedAt:0 - isVoiceMessage:NO - groupMetaMessage:TSGroupMetaMessageUnspecified - quotedMessage:nil - contactShare:nil - linkPreview:nil]; - - if (!self) { - return self; - } - - return self; -} - -- (uint)ttl { return (uint)[LKTTLUtilities getTTLFor:LKMessageTypeSync]; } - -- (BOOL)shouldBeSaved -{ - return NO; -} - -- (BOOL)shouldSyncTranscript -{ - return NO; -} - -// This method should not be overridden, since we want to add random padding to *every* sync message -- (nullable SSKProtoSyncMessage *)buildSyncMessage -{ - SSKProtoSyncMessageBuilder *_Nullable builder = [self syncMessageBuilder]; - if (!builder) { - return nil; - } - - // Add a random 1-512 bytes to obscure sync message type - size_t paddingBytesLength = arc4random_uniform(512) + 1; - builder.padding = [Cryptography generateRandomBytes:paddingBytesLength]; - - NSError *error; - SSKProtoSyncMessage *_Nullable proto = [builder buildAndReturnError:&error]; - if (error || !proto) { - OWSFailDebug(@"could not build protobuf: %@", error); - return nil; - } - return proto; -} - -- (nullable SSKProtoSyncMessageBuilder *)syncMessageBuilder -{ - OWSAbstractMethod(); - - return [SSKProtoSyncMessage builder]; -} - -- (nullable NSData *)buildPlainTextData:(SignalRecipient *)recipient -{ - SSKProtoSyncMessage *_Nullable syncMessage = [self buildSyncMessage]; - if (!syncMessage) { - return nil; - } - - SSKProtoContentBuilder *contentBuilder = [SSKProtoContent builder]; - [contentBuilder setSyncMessage:syncMessage]; - - NSError *error; - NSData *_Nullable data = [contentBuilder buildSerializedDataAndReturnError:&error]; - if (error || !data) { - OWSFailDebug(@"could not serialize protobuf: %@", error); - return nil; - } - - return data; -} - -@end - -NS_ASSUME_NONNULL_END diff --git a/SignalUtilitiesKit/OWSPreferences.m b/SignalUtilitiesKit/OWSPreferences.m index 41054c2bd..c72f9ecd1 100644 --- a/SignalUtilitiesKit/OWSPreferences.m +++ b/SignalUtilitiesKit/OWSPreferences.m @@ -215,8 +215,6 @@ NSString *const OWSPreferencesKeySystemCallLogEnabled = @"OWSPreferencesKeySyste - (void)setShouldShowUnidentifiedDeliveryIndicators:(BOOL)value { [self setValueForKey:OWSPreferencesKeyShouldShowUnidentifiedDeliveryIndicators toValue:@(value)]; - - [SSKEnvironment.shared.syncManager sendConfigurationSyncMessage]; } #pragma mark - Calling diff --git a/SignalUtilitiesKit/OWSProfileKeyMessage.h b/SignalUtilitiesKit/OWSProfileKeyMessage.h deleted file mode 100644 index ea4ce83bb..000000000 --- a/SignalUtilitiesKit/OWSProfileKeyMessage.h +++ /dev/null @@ -1,28 +0,0 @@ -// -// Copyright (c) 2019 Open Whisper Systems. All rights reserved. -// - -#import - -NS_ASSUME_NONNULL_BEGIN - -@interface OWSProfileKeyMessage : TSOutgoingMessage - -- (instancetype)initOutgoingMessageWithTimestamp:(uint64_t)timestamp - inThread:(nullable TSThread *)thread - messageBody:(nullable NSString *)body - attachmentIds:(NSMutableArray *)attachmentIds - expiresInSeconds:(uint32_t)expiresInSeconds - expireStartedAt:(uint64_t)expireStartedAt - isVoiceMessage:(BOOL)isVoiceMessage - groupMetaMessage:(TSGroupMetaMessage)groupMetaMessage - quotedMessage:(nullable TSQuotedMessage *)quotedMessage - contactShare:(nullable OWSContact *)contactShare - linkPreview:(nullable OWSLinkPreview *)linkPreview NS_UNAVAILABLE; - -- (instancetype)initWithTimestamp:(uint64_t)timestamp inThread:(nullable TSThread *)thread NS_DESIGNATED_INITIALIZER; -- (nullable instancetype)initWithCoder:(NSCoder *)coder NS_DESIGNATED_INITIALIZER; - -@end - -NS_ASSUME_NONNULL_END diff --git a/SignalUtilitiesKit/OWSProfileKeyMessage.m b/SignalUtilitiesKit/OWSProfileKeyMessage.m deleted file mode 100644 index 8682f01f4..000000000 --- a/SignalUtilitiesKit/OWSProfileKeyMessage.m +++ /dev/null @@ -1,78 +0,0 @@ -// -// Copyright (c) 2019 Open Whisper Systems. All rights reserved. -// - -#import "OWSProfileKeyMessage.h" -#import "ProfileManagerProtocol.h" -#import "ProtoUtils.h" -#import "SSKEnvironment.h" -#import - -NS_ASSUME_NONNULL_BEGIN - -@implementation OWSProfileKeyMessage - -- (instancetype)initWithTimestamp:(uint64_t)timestamp inThread:(nullable TSThread *)thread -{ - return [super initOutgoingMessageWithTimestamp:timestamp - inThread:thread - messageBody:nil - attachmentIds:[NSMutableArray new] - expiresInSeconds:0 - expireStartedAt:0 - isVoiceMessage:NO - groupMetaMessage:TSGroupMetaMessageUnspecified - quotedMessage:nil - contactShare:nil - linkPreview:nil]; -} - -- (nullable instancetype)initWithCoder:(NSCoder *)coder -{ - return [super initWithCoder:coder]; -} - -- (uint)ttl { return (uint)[LKTTLUtilities getTTLFor:LKMessageTypeProfileKey]; } - -- (BOOL)shouldBeSaved -{ - return NO; -} - -- (BOOL)shouldSyncTranscript -{ - return NO; -} - -- (nullable SSKProtoDataMessage *)buildDataMessage:(NSString *_Nullable)recipientId -{ - OWSAssertDebug(self.thread); - - SSKProtoDataMessageBuilder *_Nullable builder = [self dataMessageBuilder]; - if (!builder) { - OWSFailDebug(@"could not build protobuf."); - return nil; - } - [builder setTimestamp:self.timestamp]; - [ProtoUtils addLocalProfileKeyToDataMessageBuilder:builder]; - [builder setFlags:SSKProtoDataMessageFlagsProfileKeyUpdate]; - - if (recipientId.length > 0) { - // Once we've shared our profile key with a user (perhaps due to being - // a member of a whitelisted group), make sure they're whitelisted. - id profileManager = SSKEnvironment.shared.profileManager; - [profileManager addUserToProfileWhitelist:recipientId]; - } - - NSError *error; - SSKProtoDataMessage *_Nullable dataProto = [builder buildAndReturnError:&error]; - if (error || !dataProto) { - OWSFailDebug(@"could not build protobuf: %@", error); - return nil; - } - return dataProto; -} - -@end - -NS_ASSUME_NONNULL_END diff --git a/SignalUtilitiesKit/OWSProvisioningMessage.h b/SignalUtilitiesKit/OWSProvisioningMessage.h deleted file mode 100644 index e4b22748f..000000000 --- a/SignalUtilitiesKit/OWSProvisioningMessage.h +++ /dev/null @@ -1,23 +0,0 @@ -// -// Copyright (c) 2017 Open Whisper Systems. All rights reserved. -// - -#import - -NS_ASSUME_NONNULL_BEGIN - -@interface OWSProvisioningMessage : NSObject - -- (instancetype)initWithMyPublicKey:(NSData *)myPublicKey - myPrivateKey:(NSData *)myPrivateKey - theirPublicKey:(NSData *)theirPublicKey - accountIdentifier:(NSString *)accountIdentifier - profileKey:(NSData *)profileKey - readReceiptsEnabled:(BOOL)areReadReceiptsEnabled - provisioningCode:(NSString *)provisioningCode; - -- (nullable NSData *)buildEncryptedMessageBody; - -@end - -NS_ASSUME_NONNULL_END diff --git a/SignalUtilitiesKit/OWSProvisioningMessage.m b/SignalUtilitiesKit/OWSProvisioningMessage.m deleted file mode 100644 index 91af10564..000000000 --- a/SignalUtilitiesKit/OWSProvisioningMessage.m +++ /dev/null @@ -1,93 +0,0 @@ -// -// Copyright (c) 2018 Open Whisper Systems. All rights reserved. -// - -#import "OWSProvisioningMessage.h" -#import "OWSProvisioningCipher.h" -#import -#import -#import -#import - -NS_ASSUME_NONNULL_BEGIN - -@interface OWSProvisioningMessage () - -@property (nonatomic, readonly) NSData *myPublicKey; -@property (nonatomic, readonly) NSData *myPrivateKey; -@property (nonatomic, readonly) NSString *accountIdentifier; -@property (nonatomic, readonly) NSData *theirPublicKey; -@property (nonatomic, readonly) NSData *profileKey; -@property (nonatomic, readonly) BOOL areReadReceiptsEnabled; -@property (nonatomic, readonly) NSString *provisioningCode; - -@end - -@implementation OWSProvisioningMessage - -- (instancetype)initWithMyPublicKey:(NSData *)myPublicKey - myPrivateKey:(NSData *)myPrivateKey - theirPublicKey:(NSData *)theirPublicKey - accountIdentifier:(NSString *)accountIdentifier - profileKey:(NSData *)profileKey - readReceiptsEnabled:(BOOL)areReadReceiptsEnabled - provisioningCode:(NSString *)provisioningCode -{ - self = [super init]; - if (!self) { - return self; - } - - _myPublicKey = myPublicKey; - _myPrivateKey = myPrivateKey; - _theirPublicKey = theirPublicKey; - _accountIdentifier = accountIdentifier; - _profileKey = profileKey; - _areReadReceiptsEnabled = areReadReceiptsEnabled; - _provisioningCode = provisioningCode; - - return self; -} - -- (nullable NSData *)buildEncryptedMessageBody -{ - ProvisioningProtoProvisionMessageBuilder *messageBuilder = - [ProvisioningProtoProvisionMessage builderWithIdentityKeyPublic:self.myPublicKey - identityKeyPrivate:self.myPrivateKey - number:self.accountIdentifier - provisioningCode:self.provisioningCode - userAgent:@"OWI" - profileKey:self.profileKey - readReceipts:self.areReadReceiptsEnabled]; - - NSError *error; - NSData *_Nullable plainTextProvisionMessage = [messageBuilder buildSerializedDataAndReturnError:&error]; - if (!plainTextProvisionMessage || error) { - OWSFailDebug(@"could not serialize proto: %@.", error); - return nil; - } - - OWSProvisioningCipher *cipher = [[OWSProvisioningCipher alloc] initWithTheirPublicKey:self.theirPublicKey]; - NSData *_Nullable encryptedProvisionMessage = [cipher encrypt:plainTextProvisionMessage]; - if (encryptedProvisionMessage == nil) { - OWSFailDebug(@"Failed to encrypt provision message"); - return nil; - } - - // Note that this is a one-time-use *cipher* public key, not our Signal *identity* public key - ProvisioningProtoProvisionEnvelopeBuilder *envelopeBuilder = - [ProvisioningProtoProvisionEnvelope builderWithPublicKey:[cipher.ourPublicKey prependKeyType] - body:encryptedProvisionMessage]; - - NSData *_Nullable envelopeData = [envelopeBuilder buildSerializedDataAndReturnError:&error]; - if (!envelopeData || error) { - OWSFailDebug(@"could not serialize proto: %@.", error); - return nil; - } - - return envelopeData; -} - -@end - -NS_ASSUME_NONNULL_END diff --git a/SignalUtilitiesKit/OWSQuotedReplyModel.m b/SignalUtilitiesKit/OWSQuotedReplyModel.m index a655b96a8..18a1aed67 100644 --- a/SignalUtilitiesKit/OWSQuotedReplyModel.m +++ b/SignalUtilitiesKit/OWSQuotedReplyModel.m @@ -6,7 +6,7 @@ #import "ConversationViewItem.h" #import #import -#import + #import #import #import @@ -149,26 +149,6 @@ NS_ASSUME_NONNULL_BEGIN } }(); OWSAssertDebug(authorId.length > 0); - - if (conversationItem.contactShare) { - ContactShareViewModel *contactShare = conversationItem.contactShare; - - // TODO We deliberately always pass `nil` for `thumbnailImage`, even though we might have a contactShare.avatarImage - // because the QuotedReplyViewModel has some hardcoded assumptions that only quoted attachments have - // thumbnails. Until we address that we want to be consistent about neither showing nor sending the - // contactShare avatar in the quoted reply. - return [[self alloc] initWithTimestamp:timestamp - authorId:authorId - body:[@"👤 " stringByAppendingString:contactShare.displayName] - bodySource:TSQuotedMessageContentSourceLocal - thumbnailImage:nil - contentType:nil - sourceFilename:nil - attachmentStream:nil - thumbnailAttachmentPointer:nil - thumbnailDownloadFailed:NO - threadId:@""]; - } NSString *_Nullable quotedText = message.body; BOOL hasText = quotedText.length > 0; diff --git a/SignalUtilitiesKit/OWSReadReceiptManager.m b/SignalUtilitiesKit/OWSReadReceiptManager.m index 934ce270a..161637226 100644 --- a/SignalUtilitiesKit/OWSReadReceiptManager.m +++ b/SignalUtilitiesKit/OWSReadReceiptManager.m @@ -4,12 +4,8 @@ #import "OWSReadReceiptManager.h" #import "AppReadiness.h" -#import "OWSLinkedDeviceReadReceipt.h" -#import "OWSMessageSender.h" #import "OWSOutgoingReceiptManager.h" #import "OWSPrimaryStorage.h" -#import "OWSReadReceiptsForLinkedDevicesMessage.h" -#import "OWSReceiptsForSenderMessage.h" #import "OWSStorage.h" #import "SSKEnvironment.h" #import "TSAccountManager.h" @@ -122,7 +118,7 @@ NSString *const OWSReadReceiptManagerAreReadReceiptsEnabled = @"areReadReceiptsE // we will send to our linked devices. // // Should only be accessed while synchronized on the OWSReadReceiptManager. -@property (nonatomic, readonly) NSMutableDictionary *toLinkedDevicesReadReceiptMap; +// @property (nonatomic, readonly) NSMutableDictionary *toLinkedDevicesReadReceiptMap; // Should only be accessed while synchronized on the OWSReadReceiptManager. @property (nonatomic) BOOL isProcessing; @@ -152,8 +148,6 @@ NSString *const OWSReadReceiptManagerAreReadReceiptsEnabled = @"areReadReceiptsE _dbConnection = primaryStorage.newDatabaseConnection; - _toLinkedDevicesReadReceiptMap = [NSMutableDictionary new]; - OWSSingletonAssert(); // Start processing. @@ -171,11 +165,6 @@ NSString *const OWSReadReceiptManagerAreReadReceiptsEnabled = @"areReadReceiptsE #pragma mark - Dependencies -- (SSKMessageSenderJobQueue *)messageSenderJobQueue -{ - return SSKEnvironment.shared.messageSenderJobQueue; -} - - (OWSOutgoingReceiptManager *)outgoingReceiptManager { OWSAssertDebug(SSKEnvironment.shared.outgoingReceiptManager); @@ -206,6 +195,9 @@ NSString *const OWSReadReceiptManagerAreReadReceiptsEnabled = @"areReadReceiptsE - (void)process { + // TODO TODO TODO + + /* @synchronized(self) { OWSLogVerbose(@"Processing read receipts."); @@ -241,6 +233,7 @@ NSString *const OWSReadReceiptManagerAreReadReceiptsEnabled = @"areReadReceiptsE self.isProcessing = NO; } } + */ } #pragma mark - Mark as Read Locally @@ -262,6 +255,9 @@ NSString *const OWSReadReceiptManagerAreReadReceiptsEnabled = @"areReadReceiptsE - (void)messageWasReadLocally:(TSIncomingMessage *)message { + // TODO TODO TODO + + /* dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ @synchronized(self) { @@ -298,6 +294,7 @@ NSString *const OWSReadReceiptManagerAreReadReceiptsEnabled = @"areReadReceiptsE [self scheduleProcessing]; } }); + */ } #pragma mark - Read Receipts From Recipient @@ -375,6 +372,7 @@ NSString *const OWSReadReceiptManagerAreReadReceiptsEnabled = @"areReadReceiptsE - (void)applyEarlyReadReceiptsForIncomingMessage:(TSIncomingMessage *)message transaction:(YapDatabaseReadWriteTransaction *)transaction { + /* OWSAssertDebug(message); OWSAssertDebug(transaction); @@ -395,12 +393,16 @@ NSString *const OWSReadReceiptManagerAreReadReceiptsEnabled = @"areReadReceiptsE [message markAsReadAtTimestamp:readReceipt.readTimestamp sendReadReceipt:NO transaction:transaction]; [readReceipt removeWithTransaction:transaction]; + */ } - (void)processReadReceiptsFromLinkedDevice:(NSArray *)readReceiptProtos readTimestamp:(uint64_t)readTimestamp transaction:(YapDatabaseReadWriteTransaction *)transaction { + // TODO TODO TODO + + /* OWSAssertDebug(readReceiptProtos); OWSAssertDebug(transaction); @@ -439,6 +441,7 @@ NSString *const OWSReadReceiptManagerAreReadReceiptsEnabled = @"areReadReceiptsE [readReceipt saveWithTransaction:transaction]; } } + */ } - (void)markAsReadOnLinkedDevice:(TSIncomingMessage *)message @@ -542,8 +545,6 @@ NSString *const OWSReadReceiptManagerAreReadReceiptsEnabled = @"areReadReceiptsE forKey:OWSReadReceiptManagerAreReadReceiptsEnabled inCollection:OWSReadReceiptManagerCollection]; - [SSKEnvironment.shared.syncManager sendConfigurationSyncMessage]; - self.areReadReceiptsEnabledCached = @(value); } diff --git a/SignalUtilitiesKit/OWSReadReceiptsForLinkedDevicesMessage.h b/SignalUtilitiesKit/OWSReadReceiptsForLinkedDevicesMessage.h deleted file mode 100644 index cac1dc39a..000000000 --- a/SignalUtilitiesKit/OWSReadReceiptsForLinkedDevicesMessage.h +++ /dev/null @@ -1,20 +0,0 @@ -// -// Copyright (c) 2018 Open Whisper Systems. All rights reserved. -// - -#import - -NS_ASSUME_NONNULL_BEGIN - -@class OWSLinkedDeviceReadReceipt; - -@interface OWSReadReceiptsForLinkedDevicesMessage : OWSOutgoingSyncMessage - -- (instancetype)init NS_UNAVAILABLE; - -- (instancetype)initWithReadReceipts:(NSArray *)readReceipts NS_DESIGNATED_INITIALIZER; -- (nullable instancetype)initWithCoder:(NSCoder *)coder NS_DESIGNATED_INITIALIZER; - -@end - -NS_ASSUME_NONNULL_END diff --git a/SignalUtilitiesKit/OWSReadReceiptsForLinkedDevicesMessage.m b/SignalUtilitiesKit/OWSReadReceiptsForLinkedDevicesMessage.m deleted file mode 100644 index 72bc0dec8..000000000 --- a/SignalUtilitiesKit/OWSReadReceiptsForLinkedDevicesMessage.m +++ /dev/null @@ -1,56 +0,0 @@ -// -// Copyright (c) 2018 Open Whisper Systems. All rights reserved. -// - -#import "OWSReadReceiptsForLinkedDevicesMessage.h" -#import "OWSLinkedDeviceReadReceipt.h" -#import - -NS_ASSUME_NONNULL_BEGIN - -@interface OWSReadReceiptsForLinkedDevicesMessage () - -@property (nonatomic, readonly) NSArray *readReceipts; - -@end - -@implementation OWSReadReceiptsForLinkedDevicesMessage - -- (instancetype)initWithReadReceipts:(NSArray *)readReceipts -{ - self = [super init]; - if (!self) { - return self; - } - - _readReceipts = [readReceipts copy]; - - return self; -} - -- (nullable instancetype)initWithCoder:(NSCoder *)coder -{ - return [super initWithCoder:coder]; -} - -- (nullable SSKProtoSyncMessageBuilder *)syncMessageBuilder -{ - SSKProtoSyncMessageBuilder *syncMessageBuilder = [SSKProtoSyncMessage builder]; - for (OWSLinkedDeviceReadReceipt *readReceipt in self.readReceipts) { - SSKProtoSyncMessageReadBuilder *readProtoBuilder = - [SSKProtoSyncMessageRead builderWithSender:readReceipt.senderId timestamp:readReceipt.messageIdTimestamp]; - - NSError *error; - SSKProtoSyncMessageRead *_Nullable readProto = [readProtoBuilder buildAndReturnError:&error]; - if (error || !readProto) { - OWSFailDebug(@"could not build protobuf: %@", error); - return nil; - } - [syncMessageBuilder addRead:readProto]; - } - return syncMessageBuilder; -} - -@end - -NS_ASSUME_NONNULL_END diff --git a/SignalUtilitiesKit/OWSReceiptsForSenderMessage.h b/SignalUtilitiesKit/OWSReceiptsForSenderMessage.h deleted file mode 100644 index 30953ae87..000000000 --- a/SignalUtilitiesKit/OWSReceiptsForSenderMessage.h +++ /dev/null @@ -1,33 +0,0 @@ -// -// Copyright (c) 2019 Open Whisper Systems. All rights reserved. -// - -#import - -NS_ASSUME_NONNULL_BEGIN - -@class OWSDeliveryReceipt; - -@interface OWSReceiptsForSenderMessage : TSOutgoingMessage - -- (instancetype)initOutgoingMessageWithTimestamp:(uint64_t)timestamp - inThread:(nullable TSThread *)thread - messageBody:(nullable NSString *)body - attachmentIds:(NSMutableArray *)attachmentIds - expiresInSeconds:(uint32_t)expiresInSeconds - expireStartedAt:(uint64_t)expireStartedAt - isVoiceMessage:(BOOL)isVoiceMessage - groupMetaMessage:(TSGroupMetaMessage)groupMetaMessage - quotedMessage:(nullable TSQuotedMessage *)quotedMessage - contactShare:(nullable OWSContact *)contactShare - linkPreview:(nullable OWSLinkPreview *)linkPreview NS_UNAVAILABLE; - -+ (OWSReceiptsForSenderMessage *)deliveryReceiptsForSenderMessageWithThread:(nullable TSThread *)thread - messageTimestamps:(NSArray *)messageTimestamps; - -+ (OWSReceiptsForSenderMessage *)readReceiptsForSenderMessageWithThread:(nullable TSThread *)thread - messageTimestamps:(NSArray *)messageTimestamps; - -@end - -NS_ASSUME_NONNULL_END diff --git a/SignalUtilitiesKit/OWSReceiptsForSenderMessage.m b/SignalUtilitiesKit/OWSReceiptsForSenderMessage.m deleted file mode 100644 index 0aca5877a..000000000 --- a/SignalUtilitiesKit/OWSReceiptsForSenderMessage.m +++ /dev/null @@ -1,139 +0,0 @@ -// -// Copyright (c) 2019 Open Whisper Systems. All rights reserved. -// - -#import "OWSReceiptsForSenderMessage.h" -#import "SignalRecipient.h" -#import -#import - -NS_ASSUME_NONNULL_BEGIN - -@interface OWSReceiptsForSenderMessage () - -@property (nonatomic, readonly) NSArray *messageTimestamps; - -@property (nonatomic, readonly) SSKProtoReceiptMessageType receiptType; - -@end - -#pragma mark - - -@implementation OWSReceiptsForSenderMessage - -+ (OWSReceiptsForSenderMessage *)deliveryReceiptsForSenderMessageWithThread:(nullable TSThread *)thread - messageTimestamps:(NSArray *)messageTimestamps -{ - return [[OWSReceiptsForSenderMessage alloc] initWithThread:thread - messageTimestamps:messageTimestamps - receiptType:SSKProtoReceiptMessageTypeDelivery]; -} - -+ (OWSReceiptsForSenderMessage *)readReceiptsForSenderMessageWithThread:(nullable TSThread *)thread - messageTimestamps:(NSArray *)messageTimestamps -{ - return [[OWSReceiptsForSenderMessage alloc] initWithThread:thread - messageTimestamps:messageTimestamps - receiptType:SSKProtoReceiptMessageTypeRead]; -} - -- (instancetype)initWithThread:(nullable TSThread *)thread - messageTimestamps:(NSArray *)messageTimestamps - receiptType:(SSKProtoReceiptMessageType)receiptType -{ - // MJK TODO - remove senderTimestamp - self = [super initOutgoingMessageWithTimestamp:[NSDate ows_millisecondTimeStamp] - inThread:thread - messageBody:nil - attachmentIds:[NSMutableArray new] - expiresInSeconds:0 - expireStartedAt:0 - isVoiceMessage:NO - groupMetaMessage:TSGroupMetaMessageUnspecified - quotedMessage:nil - contactShare:nil - linkPreview:nil]; - if (!self) { - return self; - } - - _messageTimestamps = [messageTimestamps copy]; - _receiptType = receiptType; - - return self; -} - -#pragma mark - TSOutgoingMessage overrides - -- (BOOL)shouldSyncTranscript -{ - return NO; -} - -- (BOOL)isSilent -{ - // Avoid "phantom messages" for "recipient read receipts". - - return YES; -} - -- (nullable NSData *)buildPlainTextData:(SignalRecipient *)recipient -{ - OWSAssertDebug(recipient); - - SSKProtoReceiptMessage *_Nullable receiptMessage = [self buildReceiptMessage:recipient.recipientId]; - if (!receiptMessage) { - OWSFailDebug(@"could not build protobuf."); - return nil; - } - - SSKProtoContentBuilder *contentBuilder = [SSKProtoContent builder]; - [contentBuilder setReceiptMessage:receiptMessage]; - - NSError *error; - NSData *_Nullable contentData = [contentBuilder buildSerializedDataAndReturnError:&error]; - if (error || !contentData) { - OWSFailDebug(@"could not serialize protobuf: %@", error); - return nil; - } - return contentData; -} - -- (nullable SSKProtoReceiptMessage *)buildReceiptMessage:(NSString *)recipientId -{ - SSKProtoReceiptMessageBuilder *builder = [SSKProtoReceiptMessage builderWithType:self.receiptType]; - - OWSAssertDebug(self.messageTimestamps.count > 0); - for (NSNumber *messageTimestamp in self.messageTimestamps) { - [builder addTimestamp:[messageTimestamp unsignedLongLongValue]]; - } - - NSError *error; - SSKProtoReceiptMessage *_Nullable receiptMessage = [builder buildAndReturnError:&error]; - if (error || !receiptMessage) { - OWSFailDebug(@"could not build protobuf: %@", error); - return nil; - } - return receiptMessage; -} - -#pragma mark - TSYapDatabaseObject overrides - -- (BOOL)shouldBeSaved -{ - return NO; -} - -- (NSString *)debugDescription -{ - return [NSString - stringWithFormat:@"%@ with message timestamps: %lu", self.logTag, (unsigned long)self.messageTimestamps.count]; -} - -#pragma mark - Other - -- (uint)ttl { return (uint)[LKTTLUtilities getTTLFor:LKMessageTypeReceipt]; } - -@end - -NS_ASSUME_NONNULL_END diff --git a/SignalUtilitiesKit/OWSRecordTranscriptJob.m b/SignalUtilitiesKit/OWSRecordTranscriptJob.m index 6308e76ca..165734e75 100644 --- a/SignalUtilitiesKit/OWSRecordTranscriptJob.m +++ b/SignalUtilitiesKit/OWSRecordTranscriptJob.m @@ -5,14 +5,12 @@ #import "OWSRecordTranscriptJob.h" #import "OWSAttachmentDownloads.h" #import "OWSDisappearingMessagesJob.h" -#import "OWSIncomingSentMessageTranscript.h" #import "OWSPrimaryStorage+SessionStore.h" #import "OWSReadReceiptManager.h" #import "SSKEnvironment.h" #import "TSAttachmentPointer.h" #import "TSGroupThread.h" #import "TSInfoMessage.h" -#import "TSNetworkManager.h" #import "TSOutgoingMessage.h" #import "TSQuotedMessage.h" #import "TSThread.h" @@ -32,13 +30,6 @@ NS_ASSUME_NONNULL_BEGIN return SSKEnvironment.shared.primaryStorage; } -+ (TSNetworkManager *)networkManager -{ - OWSAssertDebug(SSKEnvironment.shared.networkManager); - - return SSKEnvironment.shared.networkManager; -} - + (OWSReadReceiptManager *)readReceiptManager { OWSAssert(SSKEnvironment.shared.readReceiptManager); @@ -46,13 +37,6 @@ NS_ASSUME_NONNULL_BEGIN return SSKEnvironment.shared.readReceiptManager; } -+ (id)contactsManager -{ - OWSAssertDebug(SSKEnvironment.shared.contactsManager); - - return SSKEnvironment.shared.contactsManager; -} - + (OWSAttachmentDownloads *)attachmentDownloads { return SSKEnvironment.shared.attachmentDownloads; @@ -67,6 +51,9 @@ NS_ASSUME_NONNULL_BEGIN NSArray *attachmentStreams))attachmentHandler transaction:(YapDatabaseReadWriteTransaction *)transaction { + // TODO TODO TODO + + /* OWSAssertDebug(transcript); OWSAssertDebug(transaction); @@ -108,7 +95,7 @@ NS_ASSUME_NONNULL_BEGIN if (transcript.thread.isGroupThread) { TSGroupThread *thread = (TSGroupThread *)transcript.thread; - if (thread.isPublicChat) { + if (thread.isOpenGroup) { [outgoingMessage setServerTimestampToReceivedTimestamp:serverTimestamp]; } } @@ -195,6 +182,7 @@ NS_ASSUME_NONNULL_BEGIN @"failed to fetch transcripts attachments for message: %@", outgoingMessage); }]; } + */ } #pragma mark - @@ -202,6 +190,9 @@ NS_ASSUME_NONNULL_BEGIN + (void)processRecipientUpdateWithTranscript:(OWSIncomingSentMessageTranscript *)transcript transaction:(YapDatabaseReadWriteTransaction *)transaction { + // TODO TODO TODO + + /* OWSAssertDebug(transcript); OWSAssertDebug(transaction); @@ -284,6 +275,7 @@ NS_ASSUME_NONNULL_BEGIN // This message may have disappeared. OWSLogError(@"No matching message with timestamp: %llu.", timestamp); } + */ } @end diff --git a/SignalUtilitiesKit/OWSRequestBuilder.h b/SignalUtilitiesKit/OWSRequestBuilder.h deleted file mode 100644 index b4879dc55..000000000 --- a/SignalUtilitiesKit/OWSRequestBuilder.h +++ /dev/null @@ -1,17 +0,0 @@ -// -// Copyright (c) 2017 Open Whisper Systems. All rights reserved. -// - -#import - -NS_ASSUME_NONNULL_BEGIN - -@class TSRequest; - -@interface OWSRequestBuilder : NSObject - -+ (TSRequest *)profileNameSetRequestWithEncryptedPaddedName:(nullable NSData *)encryptedPaddedName; - -@end - -NS_ASSUME_NONNULL_END diff --git a/SignalUtilitiesKit/OWSRequestBuilder.m b/SignalUtilitiesKit/OWSRequestBuilder.m deleted file mode 100644 index 74830bff1..000000000 --- a/SignalUtilitiesKit/OWSRequestBuilder.m +++ /dev/null @@ -1,43 +0,0 @@ -// -// Copyright (c) 2018 Open Whisper Systems. All rights reserved. -// - -#import "OWSRequestBuilder.h" -#import "TSConstants.h" -#import "TSRequest.h" -#import - -NS_ASSUME_NONNULL_BEGIN - -const NSUInteger kEncodedNameLength = 72; - -@implementation OWSRequestBuilder - -+ (TSRequest *)profileNameSetRequestWithEncryptedPaddedName:(nullable NSData *)encryptedPaddedName -{ - NSString *urlString; - - NSString *base64EncodedName = [encryptedPaddedName base64EncodedString]; - // name length must match exactly - if (base64EncodedName.length == kEncodedNameLength) { - // Remove any "/" in the base64 (all other base64 chars are URL safe. - // Apples built-in `stringByAddingPercentEncodingWithAllowedCharacters:[NSCharacterSet URL*]]` doesn't offer a - // flavor for encoding "/". - NSString *urlEncodedName = [base64EncodedName stringByReplacingOccurrencesOfString:@"/" withString:@"%2F"]; - urlString = [NSString stringWithFormat:textSecureSetProfileNameAPIFormat, urlEncodedName]; - } else { - // if name length doesn't match exactly, assume blank name - OWSAssertDebug(encryptedPaddedName == nil); - urlString = [NSString stringWithFormat:textSecureSetProfileNameAPIFormat, @""]; - } - - NSURL *url = [NSURL URLWithString:urlString]; - TSRequest *request = [[TSRequest alloc] initWithURL:url]; - request.HTTPMethod = @"PUT"; - - return request; -} - -@end - -NS_ASSUME_NONNULL_END diff --git a/SignalUtilitiesKit/OWSRequestFactory.h b/SignalUtilitiesKit/OWSRequestFactory.h deleted file mode 100644 index b3c6a28c7..000000000 --- a/SignalUtilitiesKit/OWSRequestFactory.h +++ /dev/null @@ -1,117 +0,0 @@ -// -// Copyright (c) 2019 Open Whisper Systems. All rights reserved. -// - -#import - -NS_ASSUME_NONNULL_BEGIN - -@class ECKeyPair; -@class OWSDevice; -@class PreKeyRecord; -@class SMKUDAccessKey; -@class SignedPreKeyRecord; -@class TSRequest; - -typedef NS_ENUM(NSUInteger, TSVerificationTransport) { TSVerificationTransportVoice = 1, TSVerificationTransportSMS }; - -@interface OWSRequestFactory : NSObject - -- (instancetype)init NS_UNAVAILABLE; - -+ (TSRequest *)enable2FARequestWithPin:(NSString *)pin; - -+ (TSRequest *)disable2FARequest; - -+ (TSRequest *)acknowledgeMessageDeliveryRequestWithSource:(NSString *)source timestamp:(UInt64)timestamp; - -+ (TSRequest *)acknowledgeMessageDeliveryRequestWithServerGuid:(NSString *)serverGuid; - -+ (TSRequest *)deleteDeviceRequestWithDevice:(OWSDevice *)device; - -+ (TSRequest *)deviceProvisioningCodeRequest; - -+ (TSRequest *)deviceProvisioningRequestWithMessageBody:(NSData *)messageBody ephemeralDeviceId:(NSString *)deviceId; - -+ (TSRequest *)getDevicesRequest; - -+ (TSRequest *)getMessagesRequest; - -+ (TSRequest *)getProfileRequestWithRecipientId:(NSString *)recipientId - udAccessKey:(nullable SMKUDAccessKey *)udAccessKey - NS_SWIFT_NAME(getProfileRequest(recipientId:udAccessKey:)); - -+ (TSRequest *)turnServerInfoRequest; - -+ (TSRequest *)allocAttachmentRequest; - -+ (TSRequest *)attachmentRequestWithAttachmentId:(UInt64)attachmentId; - -+ (TSRequest *)contactsIntersectionRequestWithHashesArray:(NSArray *)hashes; - -+ (TSRequest *)profileAvatarUploadFormRequest; - -+ (TSRequest *)registerForPushRequestWithPushIdentifier:(NSString *)identifier voipIdentifier:(NSString *)voipId; - -+ (TSRequest *)updateAttributesRequest; - -+ (TSRequest *)unregisterAccountRequest; - -+ (TSRequest *)requestVerificationCodeRequestWithPhoneNumber:(NSString *)phoneNumber - captchaToken:(nullable NSString *)captchaToken - transport:(TSVerificationTransport)transport; - -+ (TSRequest *)submitMessageRequestWithRecipient:(NSString *)recipientId - messages:(NSArray *)messages - timeStamp:(uint64_t)timeStamp - udAccessKey:(nullable SMKUDAccessKey *)udAccessKey; - -+ (TSRequest *)verifyCodeRequestWithVerificationCode:(NSString *)verificationCode - forNumber:(NSString *)phoneNumber - pin:(nullable NSString *)pin - authKey:(NSString *)authKey; - -#pragma mark - Prekeys - -+ (TSRequest *)availablePreKeysCountRequest; - -+ (TSRequest *)currentSignedPreKeyRequest; - -+ (TSRequest *)recipientPrekeyRequestWithRecipient:(NSString *)recipientNumber - deviceId:(NSString *)deviceId - udAccessKey:(nullable SMKUDAccessKey *)udAccessKey; - -+ (TSRequest *)registerSignedPrekeyRequestWithSignedPreKeyRecord:(SignedPreKeyRecord *)signedPreKey; - -+ (TSRequest *)registerPrekeysRequestWithPrekeyArray:(NSArray *)prekeys - identityKey:(NSData *)identityKeyPublic - signedPreKey:(SignedPreKeyRecord *)signedPreKey; - -#pragma mark - CDS - -+ (TSRequest *)remoteAttestationRequest:(ECKeyPair *)keyPair - enclaveId:(NSString *)enclaveId - authUsername:(NSString *)authUsername - authPassword:(NSString *)authPassword; - -+ (TSRequest *)enclaveContactDiscoveryRequestWithId:(NSData *)requestId - addressCount:(NSUInteger)addressCount - encryptedAddressData:(NSData *)encryptedAddressData - cryptIv:(NSData *)cryptIv - cryptMac:(NSData *)cryptMac - enclaveId:(NSString *)enclaveId - authUsername:(NSString *)authUsername - authPassword:(NSString *)authPassword - cookies:(NSArray *)cookies; - -+ (TSRequest *)remoteAttestationAuthRequest; -+ (TSRequest *)cdsFeedbackRequestWithStatus:(NSString *)status - reason:(nullable NSString *)reason NS_SWIFT_NAME(cdsFeedbackRequest(status:reason:)); - -#pragma mark - UD - -+ (TSRequest *)udSenderCertificateRequest; - -@end - -NS_ASSUME_NONNULL_END diff --git a/SignalUtilitiesKit/OWSRequestFactory.m b/SignalUtilitiesKit/OWSRequestFactory.m deleted file mode 100644 index 182fd35b8..000000000 --- a/SignalUtilitiesKit/OWSRequestFactory.m +++ /dev/null @@ -1,543 +0,0 @@ -// -// Copyright (c) 2019 Open Whisper Systems. All rights reserved. -// - -#import "OWSRequestFactory.h" -#import "OWS2FAManager.h" -#import "OWSDevice.h" -#import "ProfileManagerProtocol.h" -#import "SSKEnvironment.h" -#import "TSAccountManager.h" -#import "TSConstants.h" -#import "TSRequest.h" -#import -#import -#import -#import -#import -#import - -NS_ASSUME_NONNULL_BEGIN - -@implementation OWSRequestFactory - -#pragma mark - Dependencies - -+ (TSAccountManager *)tsAccountManager -{ - return TSAccountManager.sharedInstance; -} - -+ (OWS2FAManager *)ows2FAManager -{ - return OWS2FAManager.sharedManager; -} - -+ (id)profileManager -{ - return SSKEnvironment.shared.profileManager; -} - -+ (id)udManager -{ - return SSKEnvironment.shared.udManager; -} - -#pragma mark - - -+ (TSRequest *)enable2FARequestWithPin:(NSString *)pin -{ - OWSAssertDebug(pin.length > 0); - - return [TSRequest requestWithUrl:[NSURL URLWithString:textSecure2FAAPI] - method:@"PUT" - parameters:@{ - @"pin" : pin, - }]; -} - -+ (TSRequest *)disable2FARequest -{ - return [TSRequest requestWithUrl:[NSURL URLWithString:textSecure2FAAPI] method:@"DELETE" parameters:@{}]; -} - -+ (TSRequest *)acknowledgeMessageDeliveryRequestWithSource:(NSString *)source timestamp:(UInt64)timestamp -{ - OWSAssertDebug(timestamp > 0); - - NSString *path = [NSString stringWithFormat:@"v1/messages/%@/%llu", source, timestamp]; - - return [TSRequest requestWithUrl:[NSURL URLWithString:path] method:@"DELETE" parameters:@{}]; -} - -+ (TSRequest *)acknowledgeMessageDeliveryRequestWithServerGuid:(NSString *)serverGuid -{ - OWSAssertDebug(serverGuid.length > 0); - - NSString *path = [NSString stringWithFormat:@"v1/messages/uuid/%@", serverGuid]; - - return [TSRequest requestWithUrl:[NSURL URLWithString:path] method:@"DELETE" parameters:@{}]; -} - -+ (TSRequest *)deleteDeviceRequestWithDevice:(OWSDevice *)device -{ - OWSAssertDebug(device); - - NSString *path = [NSString stringWithFormat:textSecureDevicesAPIFormat, @(device.deviceId)]; - - return [TSRequest requestWithUrl:[NSURL URLWithString:path] method:@"DELETE" parameters:@{}]; -} - -+ (TSRequest *)deviceProvisioningCodeRequest -{ - return [TSRequest requestWithUrl:[NSURL URLWithString:textSecureDeviceProvisioningCodeAPI] - method:@"GET" - parameters:@{}]; -} - -+ (TSRequest *)deviceProvisioningRequestWithMessageBody:(NSData *)messageBody ephemeralDeviceId:(NSString *)deviceId -{ - OWSAssertDebug(messageBody.length > 0); - OWSAssertDebug(deviceId.length > 0); - - NSString *path = [NSString stringWithFormat:textSecureDeviceProvisioningAPIFormat, deviceId]; - return [TSRequest requestWithUrl:[NSURL URLWithString:path] - method:@"PUT" - parameters:@{ - @"body" : [messageBody base64EncodedString], - }]; -} - -+ (TSRequest *)getDevicesRequest -{ - NSString *path = [NSString stringWithFormat:textSecureDevicesAPIFormat, @""]; - return [TSRequest requestWithUrl:[NSURL URLWithString:path] method:@"GET" parameters:@{}]; -} - -+ (TSRequest *)getMessagesRequest -{ - return [TSRequest requestWithUrl:[NSURL URLWithString:@"v1/messages"] method:@"GET" parameters:@{}]; -} - -+ (TSRequest *)getProfileRequestWithRecipientId:(NSString *)recipientId - udAccessKey:(nullable SMKUDAccessKey *)udAccessKey -{ - OWSAssertDebug(recipientId.length > 0); - - NSString *path = [NSString stringWithFormat:textSecureProfileAPIFormat, recipientId]; - TSRequest *request = [TSRequest requestWithUrl:[NSURL URLWithString:path] method:@"GET" parameters:@{}]; - if (udAccessKey != nil) { - [self useUDAuthWithRequest:request accessKey:udAccessKey]; - } - return request; -} - -+ (TSRequest *)turnServerInfoRequest -{ - return [TSRequest requestWithUrl:[NSURL URLWithString:@"v1/accounts/turn"] method:@"GET" parameters:@{}]; -} - -+ (TSRequest *)allocAttachmentRequest -{ - NSString *path = [NSString stringWithFormat:@"%@", textSecureAttachmentsAPI]; - return [TSRequest requestWithUrl:[NSURL URLWithString:path] method:@"GET" parameters:@{}]; -} - -+ (TSRequest *)attachmentRequestWithAttachmentId:(UInt64)attachmentId -{ - OWSAssertDebug(attachmentId > 0); - - NSString *path = [NSString stringWithFormat:@"%@/%llu", textSecureAttachmentsAPI, attachmentId]; - - return [TSRequest requestWithUrl:[NSURL URLWithString:path] method:@"GET" parameters:@{}]; -} - -+ (TSRequest *)availablePreKeysCountRequest -{ - NSString *path = [NSString stringWithFormat:@"%@", textSecureKeysAPI]; - return [TSRequest requestWithUrl:[NSURL URLWithString:path] method:@"GET" parameters:@{}]; -} - -+ (TSRequest *)contactsIntersectionRequestWithHashesArray:(NSArray *)hashes -{ - OWSAssertDebug(hashes.count > 0); - - NSString *path = [NSString stringWithFormat:@"%@/%@", textSecureDirectoryAPI, @"tokens"]; - return [TSRequest requestWithUrl:[NSURL URLWithString:path] - method:@"PUT" - parameters:@{ - @"contacts" : hashes, - }]; -} - -+ (TSRequest *)currentSignedPreKeyRequest -{ - NSString *path = textSecureSignedKeysAPI; - return [TSRequest requestWithUrl:[NSURL URLWithString:path] method:@"GET" parameters:@{}]; -} - -+ (TSRequest *)profileAvatarUploadFormRequest -{ - NSString *path = textSecureProfileAvatarFormAPI; - return [TSRequest requestWithUrl:[NSURL URLWithString:path] method:@"GET" parameters:@{}]; -} - -+ (TSRequest *)recipientPrekeyRequestWithRecipient:(NSString *)recipientNumber - deviceId:(NSString *)deviceId - udAccessKey:(nullable SMKUDAccessKey *)udAccessKey -{ - OWSAssertDebug(recipientNumber.length > 0); - OWSAssertDebug(deviceId.length > 0); - - NSString *path = [NSString stringWithFormat:@"%@/%@/%@", textSecureKeysAPI, recipientNumber, deviceId]; - - TSRequest *request = [TSRequest requestWithUrl:[NSURL URLWithString:path] method:@"GET" parameters:@{}]; - if (udAccessKey != nil) { - [self useUDAuthWithRequest:request accessKey:udAccessKey]; - } - return request; -} - -+ (TSRequest *)registerForPushRequestWithPushIdentifier:(NSString *)identifier voipIdentifier:(NSString *)voipId -{ - OWSAssertDebug(identifier.length > 0); - OWSAssertDebug(voipId.length > 0); - - NSString *path = [NSString stringWithFormat:@"%@/%@", textSecureAccountsAPI, @"apn"]; - OWSAssertDebug(voipId); - return [TSRequest requestWithUrl:[NSURL URLWithString:path] - method:@"PUT" - parameters:@{ - @"apnRegistrationId" : identifier, - @"voipRegistrationId" : voipId ?: @"", - }]; -} - -+ (TSRequest *)updateAttributesRequest -{ - NSString *path = [textSecureAccountsAPI stringByAppendingString:textSecureAttributesAPI]; - - NSString *authKey = self.tsAccountManager.serverAuthToken; - NSString *_Nullable pin = [self.ows2FAManager pinCode]; - - NSDictionary *accountAttributes = [self accountAttributesWithPin:pin authKey:authKey]; - - return [TSRequest requestWithUrl:[NSURL URLWithString:path] method:@"PUT" parameters:accountAttributes]; -} - -+ (TSRequest *)unregisterAccountRequest -{ - NSString *path = [NSString stringWithFormat:@"%@/%@", textSecureAccountsAPI, @"apn"]; - return [TSRequest requestWithUrl:[NSURL URLWithString:path] method:@"DELETE" parameters:@{}]; -} - -+ (TSRequest *)requestVerificationCodeRequestWithPhoneNumber:(NSString *)phoneNumber - captchaToken:(nullable NSString *)captchaToken - transport:(TSVerificationTransport)transport -{ - OWSAssertDebug(phoneNumber.length > 0); - - NSString *querystring = @"client=ios"; - if (captchaToken.length > 0) { - querystring = [NSString stringWithFormat:@"%@&captcha=%@", querystring, captchaToken]; - } - - NSString *path = [NSString stringWithFormat:@"%@/%@/code/%@?%@", - textSecureAccountsAPI, - [self stringForTransport:transport], - phoneNumber, - querystring]; - TSRequest *request = [TSRequest requestWithUrl:[NSURL URLWithString:path] method:@"GET" parameters:@{}]; -// request.shouldHaveAuthorizationHeaders = NO; - - if (transport == TSVerificationTransportVoice) { - NSString *_Nullable localizationHeader = [self voiceCodeLocalizationHeader]; - if (localizationHeader.length > 0) { - [request setValue:localizationHeader forHTTPHeaderField:@"Accept-Language"]; - } - } - - return request; -} - -+ (nullable NSString *)voiceCodeLocalizationHeader -{ - NSLocale *locale = [NSLocale currentLocale]; - NSString *_Nullable languageCode = [locale objectForKey:NSLocaleLanguageCode]; - NSString *_Nullable countryCode = [locale objectForKey:NSLocaleCountryCode]; - - if (!languageCode) { - return nil; - } - - OWSAssertDebug([languageCode rangeOfString:@"-"].location == NSNotFound); - - if (!countryCode) { - // In the absence of a country code, just send a language code. - return languageCode; - } - - OWSAssertDebug(languageCode.length == 2); - OWSAssertDebug(countryCode.length == 2); - return [NSString stringWithFormat:@"%@-%@", languageCode, countryCode]; -} - -+ (NSString *)stringForTransport:(TSVerificationTransport)transport -{ - switch (transport) { - case TSVerificationTransportSMS: - return @"sms"; - case TSVerificationTransportVoice: - return @"voice"; - } -} - -+ (TSRequest *)verifyCodeRequestWithVerificationCode:(NSString *)verificationCode - forNumber:(NSString *)phoneNumber - pin:(nullable NSString *)pin - authKey:(NSString *)authKey -{ - OWSAssertDebug(verificationCode.length > 0); - OWSAssertDebug(phoneNumber.length > 0); - OWSAssertDebug(authKey.length > 0); - - NSString *path = [NSString stringWithFormat:@"%@/code/%@", textSecureAccountsAPI, verificationCode]; - - NSMutableDictionary *accountAttributes = - [[self accountAttributesWithPin:pin authKey:authKey] mutableCopy]; - [accountAttributes removeObjectForKey:@"AuthKey"]; - - TSRequest *request = - [TSRequest requestWithUrl:[NSURL URLWithString:path] method:@"PUT" parameters:accountAttributes]; - // The "verify code" request handles auth differently. -// request.authUsername = phoneNumber; -// request.authPassword = authKey; - return request; -} - -+ (NSDictionary *)accountAttributesWithPin:(nullable NSString *)pin - authKey:(NSString *)authKey -{ - uint32_t registrationId = [self.tsAccountManager getOrGenerateRegistrationId]; - - BOOL isManualMessageFetchEnabled = self.tsAccountManager.isManualMessageFetchEnabled; - - OWSAES256Key *profileKey = [self.profileManager localProfileKey]; - NSError *error; - SMKUDAccessKey *_Nullable udAccessKey = [[SMKUDAccessKey alloc] initWithProfileKey:profileKey.keyData error:&error]; - if (error || udAccessKey.keyData.length < 1) { - // Crash app if UD cannot be enabled. - OWSFail(@"Could not determine UD access key: %@.", error); - } - BOOL allowUnrestrictedUD = [self.udManager shouldAllowUnrestrictedAccessLocal] && udAccessKey != nil; - - // We no longer include the signalingKey. - NSMutableDictionary *accountAttributes = [@{ - @"AuthKey" : authKey, - @"voice" : @(YES), // all Signal-iOS clients support voice - @"video" : @(YES), // all Signal-iOS clients support WebRTC-based voice and video calls. - @"fetchesMessages" : @(isManualMessageFetchEnabled), // devices that don't support push must tell the server - // they fetch messages manually - @"registrationId" : [NSString stringWithFormat:@"%i", registrationId], - @"unidentifiedAccessKey" : udAccessKey.keyData.base64EncodedString, - @"unrestrictedUnidentifiedAccess" : @(allowUnrestrictedUD), - } mutableCopy]; - - if (pin.length > 0) { - accountAttributes[@"pin"] = pin; - } - - return [accountAttributes copy]; -} - -+ (TSRequest *)submitMessageRequestWithRecipient:(NSString *)recipientId - messages:(NSArray *)messages - timeStamp:(uint64_t)timeStamp - udAccessKey:(nullable SMKUDAccessKey *)udAccessKey -{ - // NOTE: messages may be empty; See comments in OWSDeviceManager. - OWSAssertDebug(recipientId.length > 0); - OWSAssertDebug(timeStamp > 0); - - NSString *path = [textSecureMessagesAPI stringByAppendingString:recipientId]; - NSDictionary *parameters = @{ - @"messages" : messages, - @"timestamp" : @(timeStamp), - }; - - TSRequest *request = [TSRequest requestWithUrl:[NSURL URLWithString:path] method:@"PUT" parameters:parameters]; - if (udAccessKey != nil) { - [self useUDAuthWithRequest:request accessKey:udAccessKey]; - } - return request; -} - -+ (TSRequest *)registerSignedPrekeyRequestWithSignedPreKeyRecord:(SignedPreKeyRecord *)signedPreKey -{ - OWSAssertDebug(signedPreKey); - - NSString *path = textSecureSignedKeysAPI; - return [TSRequest requestWithUrl:[NSURL URLWithString:path] - method:@"PUT" - parameters:[self dictionaryFromSignedPreKey:signedPreKey]]; -} - -+ (TSRequest *)registerPrekeysRequestWithPrekeyArray:(NSArray *)prekeys - identityKey:(NSData *)identityKeyPublic - signedPreKey:(SignedPreKeyRecord *)signedPreKey -{ - OWSAssertDebug(prekeys.count > 0); - OWSAssertDebug(identityKeyPublic.length > 0); - OWSAssertDebug(signedPreKey); - - NSString *path = textSecureKeysAPI; - NSString *publicIdentityKey = [[identityKeyPublic prependKeyType] base64EncodedStringWithOptions:0]; - NSMutableArray *serializedPrekeyList = [NSMutableArray array]; - for (PreKeyRecord *preKey in prekeys) { - [serializedPrekeyList addObject:[self dictionaryFromPreKey:preKey]]; - } - return [TSRequest requestWithUrl:[NSURL URLWithString:path] - method:@"PUT" - parameters:@{ - @"preKeys" : serializedPrekeyList, - @"signedPreKey" : [self dictionaryFromSignedPreKey:signedPreKey], - @"identityKey" : publicIdentityKey - }]; -} - -+ (NSDictionary *)dictionaryFromPreKey:(PreKeyRecord *)preKey -{ - return @{ - @"keyId" : @(preKey.Id), - @"publicKey" : [[preKey.keyPair.publicKey prependKeyType] base64EncodedStringWithOptions:0], - }; -} - -+ (NSDictionary *)dictionaryFromSignedPreKey:(SignedPreKeyRecord *)preKey -{ - return @{ - @"keyId" : @(preKey.Id), - @"publicKey" : [[preKey.keyPair.publicKey prependKeyType] base64EncodedStringWithOptions:0], - @"signature" : [preKey.signature base64EncodedStringWithOptions:0] - }; -} - -+ (TSRequest *)remoteAttestationRequest:(ECKeyPair *)keyPair - enclaveId:(NSString *)enclaveId - authUsername:(NSString *)authUsername - authPassword:(NSString *)authPassword -{ - OWSAssertDebug(keyPair); - OWSAssertDebug(enclaveId.length > 0); - OWSAssertDebug(authUsername.length > 0); - OWSAssertDebug(authPassword.length > 0); - - NSString *path = [NSString stringWithFormat:@"%@/v1/attestation/%@", contactDiscoveryURL, enclaveId]; - TSRequest *request = [TSRequest requestWithUrl:[NSURL URLWithString:path] - method:@"PUT" - parameters:@{ - // We DO NOT prepend the "key type" byte. - @"clientPublic" : [keyPair.publicKey base64EncodedStringWithOptions:0], - }]; -// request.authUsername = authUsername; -// request.authPassword = authPassword; - - // Don't bother with the default cookie store; - // these cookies are ephemeral. - // - // NOTE: TSNetworkManager now separately disables default cookie handling for all requests. - [request setHTTPShouldHandleCookies:NO]; - - return request; -} - -+ (TSRequest *)enclaveContactDiscoveryRequestWithId:(NSData *)requestId - addressCount:(NSUInteger)addressCount - encryptedAddressData:(NSData *)encryptedAddressData - cryptIv:(NSData *)cryptIv - cryptMac:(NSData *)cryptMac - enclaveId:(NSString *)enclaveId - authUsername:(NSString *)authUsername - authPassword:(NSString *)authPassword - cookies:(NSArray *)cookies -{ - NSString *path = [NSString stringWithFormat:@"%@/v1/discovery/%@", contactDiscoveryURL, enclaveId]; - - TSRequest *request = [TSRequest requestWithUrl:[NSURL URLWithString:path] - method:@"PUT" - parameters:@{ - @"requestId" : requestId.base64EncodedString, - @"addressCount" : @(addressCount), - @"data" : encryptedAddressData.base64EncodedString, - @"iv" : cryptIv.base64EncodedString, - @"mac" : cryptMac.base64EncodedString, - }]; - -// request.authUsername = authUsername; -// request.authPassword = authPassword; - - // Don't bother with the default cookie store; - // these cookies are ephemeral. - // - // NOTE: TSNetworkManager now separately disables default cookie handling for all requests. - [request setHTTPShouldHandleCookies:NO]; - // Set the cookie header. - OWSAssertDebug(request.allHTTPHeaderFields.count == 0); - [request setAllHTTPHeaderFields:[NSHTTPCookie requestHeaderFieldsWithCookies:cookies]]; - - return request; -} - -+ (TSRequest *)remoteAttestationAuthRequest -{ - NSString *path = @"/v1/directory/auth"; - return [TSRequest requestWithUrl:[NSURL URLWithString:path] method:@"GET" parameters:@{}]; -} - -+ (TSRequest *)cdsFeedbackRequestWithStatus:(NSString *)status - reason:(nullable NSString *)reason -{ - - NSDictionary *parameters; - if (reason == nil) { - parameters = @{}; - } else { - const NSUInteger kServerReasonLimit = 1000; - NSString *limitedReason; - if (reason.length < kServerReasonLimit) { - limitedReason = reason; - } else { - OWSFailDebug(@"failure: reason should be under 1000"); - limitedReason = [reason substringToIndex:kServerReasonLimit - 1]; - } - parameters = @{ @"reason": limitedReason }; - } - NSString *path = [NSString stringWithFormat:@"/v1/directory/feedback-v3/%@", status]; - return [TSRequest requestWithUrl:[NSURL URLWithString:path] method:@"PUT" parameters:parameters]; -} - -#pragma mark - UD - -+ (TSRequest *)udSenderCertificateRequest -{ - NSString *path = @"/v1/certificate/delivery"; - return [TSRequest requestWithUrl:[NSURL URLWithString:path] method:@"GET" parameters:@{}]; -} - -+ (void)useUDAuthWithRequest:(TSRequest *)request accessKey:(SMKUDAccessKey *)udAccessKey -{ - OWSAssertDebug(request); - OWSAssertDebug(udAccessKey); - - // Suppress normal auth headers. -// request.shouldHaveAuthorizationHeaders = NO; - - // Add UD auth header. - [request setValue:[udAccessKey.keyData base64EncodedString] forHTTPHeaderField:@"Unidentified-Access-Key"]; - -// request.isUDRequest = YES; -} - -@end - -NS_ASSUME_NONNULL_END diff --git a/SignalUtilitiesKit/OWSRequestMaker.swift b/SignalUtilitiesKit/OWSRequestMaker.swift deleted file mode 100644 index f87707b02..000000000 --- a/SignalUtilitiesKit/OWSRequestMaker.swift +++ /dev/null @@ -1,247 +0,0 @@ -// -// Copyright (c) 2019 Open Whisper Systems. All rights reserved. -// - -import Foundation -import PromiseKit - -@objc -public enum RequestMakerUDAuthError: Int, Error { - case udAuthFailure -} - -public enum RequestMakerError: Error { - case websocketRequestError(statusCode : Int, responseData : Data?, underlyingError : Error) -} - -@objc(OWSRequestMakerResult) -public class RequestMakerResult: NSObject { - @objc - public let responseObject: Any? - - @objc - public let wasSentByUD: Bool - - @objc - public let wasSentByWebsocket: Bool - - @objc - public init(responseObject: Any?, - wasSentByUD: Bool, - wasSentByWebsocket: Bool) { - self.responseObject = responseObject - self.wasSentByUD = wasSentByUD - self.wasSentByWebsocket = wasSentByWebsocket - } -} - -// A utility class that handles: -// -// * UD auth-to-Non-UD auth failover. -// * Websocket-to-REST failover. -@objc(OWSRequestMaker) -public class RequestMaker: NSObject { - - public typealias RequestFactoryBlock = (SMKUDAccessKey?) -> TSRequest - public typealias UDAuthFailureBlock = () -> Void - public typealias WebsocketFailureBlock = () -> Void - - private let label: String - private let requestFactoryBlock: RequestFactoryBlock - private let udAuthFailureBlock: UDAuthFailureBlock - private let websocketFailureBlock: WebsocketFailureBlock - private let recipientId: String - private let udAccess: OWSUDAccess? - private let canFailoverUDAuth: Bool - - @objc - public init(label: String, - requestFactoryBlock : @escaping RequestFactoryBlock, - udAuthFailureBlock : @escaping UDAuthFailureBlock, - websocketFailureBlock : @escaping WebsocketFailureBlock, - recipientId: String, - udAccess: OWSUDAccess?, - canFailoverUDAuth: Bool) { - self.label = label - self.requestFactoryBlock = requestFactoryBlock - self.udAuthFailureBlock = udAuthFailureBlock - self.websocketFailureBlock = websocketFailureBlock - self.recipientId = recipientId - self.udAccess = udAccess - self.canFailoverUDAuth = canFailoverUDAuth - } - - // MARK: - Dependencies - - private var socketManager: TSSocketManager { - return SSKEnvironment.shared.socketManager - } - - private var networkManager: TSNetworkManager { - return SSKEnvironment.shared.networkManager - } - - private var udManager: OWSUDManager { - return SSKEnvironment.shared.udManager - } - - private var profileManager: ProfileManagerProtocol { - return SSKEnvironment.shared.profileManager - } - - // MARK: - - - @objc - public func makeRequestObjc() -> AnyPromise { - let promise = makeRequest() - .recover(on: DispatchQueue.global()) { (error: Error) -> Promise in - switch error { - case NetworkManagerError.taskError(_, let underlyingError): - throw underlyingError - default: - throw error - } - } - let anyPromise = AnyPromise(promise) - anyPromise.retainUntilComplete() - return anyPromise - } - - public func makeRequest() -> Promise { - return makeRequestInternal(skipUD: false, skipWebsocket: false) - } - - private func makeRequestInternal(skipUD: Bool, skipWebsocket: Bool) -> Promise { - var udAccessForRequest: OWSUDAccess? - if !skipUD { - udAccessForRequest = udAccess - } - let isUDRequest: Bool = udAccessForRequest != nil - let request: TSRequest = requestFactoryBlock(udAccessForRequest?.udAccessKey) - let canMakeWebsocketRequests = (socketManager.canMakeRequests() && !skipWebsocket && !isUDRequest) - - if canMakeWebsocketRequests { - return Promise { resolver in - socketManager.make(request, success: { (responseObject: Any?) in - if self.udManager.isUDVerboseLoggingEnabled() { - if isUDRequest { - Logger.debug("UD websocket request '\(self.label)' succeeded.") - } else { - Logger.debug("Non-UD websocket request '\(self.label)' succeeded.") - } - } - - self.requestSucceeded(udAccess: udAccessForRequest) - - resolver.fulfill(RequestMakerResult(responseObject: responseObject, - wasSentByUD: isUDRequest, - wasSentByWebsocket: true)) - }) { (statusCode: Int, responseData: Data?, error: Error) in - resolver.reject(RequestMakerError.websocketRequestError(statusCode: statusCode, responseData: responseData, underlyingError: error)) - } - }.recover { (error: Error) -> Promise in - switch error { - case RequestMakerError.websocketRequestError(let statusCode, _, _): - if isUDRequest && (statusCode == 401 || statusCode == 403) { - // If a UD request fails due to service response (as opposed to network - // failure), mark recipient as _not_ in UD mode, then retry. - self.udManager.setUnidentifiedAccessMode(.disabled, recipientId: self.recipientId) - self.profileManager.fetchProfile(forRecipientId: self.recipientId) - self.udAuthFailureBlock() - - if self.canFailoverUDAuth { - Logger.info("UD websocket request '\(self.label)' auth failed; failing over to non-UD websocket request.") - return self.makeRequestInternal(skipUD: true, skipWebsocket: skipWebsocket) - } else { - Logger.info("UD websocket request '\(self.label)' auth failed; aborting.") - throw RequestMakerUDAuthError.udAuthFailure - } - } - break - default: - break - } - - self.websocketFailureBlock() - if isUDRequest { - Logger.info("UD Web socket request '\(self.label)' failed; failing over to REST request: \(error).") - } else { - Logger.info("Non-UD Web socket request '\(self.label)' failed; failing over to REST request: \(error).") - } - return self.makeRequestInternal(skipUD: skipUD, skipWebsocket: true) - } - } else { - return self.networkManager.makePromise(request: request) - .map(on: DispatchQueue.global()) { (networkManagerResult: TSNetworkManager.NetworkManagerResult) -> RequestMakerResult in - if self.udManager.isUDVerboseLoggingEnabled() { - if isUDRequest { - Logger.debug("UD REST request '\(self.label)' succeeded.") - } else { - Logger.debug("Non-UD REST request '\(self.label)' succeeded.") - } - } - - self.requestSucceeded(udAccess: udAccessForRequest) - - // Unwrap the network manager promise into a request maker promise. - return RequestMakerResult(responseObject: networkManagerResult.responseObject, - wasSentByUD: isUDRequest, - wasSentByWebsocket: false) - }.recover { (error: Error) -> Promise in - switch error { - case NetworkManagerError.taskError(let task, _): - let statusCode = task.statusCode() - if isUDRequest && (statusCode == 401 || statusCode == 403) { - // If a UD request fails due to service response (as opposed to network - // failure), mark recipient as _not_ in UD mode, then retry. - self.udManager.setUnidentifiedAccessMode(.disabled, recipientId: self.recipientId) - self.profileManager.fetchProfile(forRecipientId: self.recipientId) - self.udAuthFailureBlock() - - if self.canFailoverUDAuth { - Logger.info("UD REST request '\(self.label)' auth failed; failing over to non-UD REST request.") - return self.makeRequestInternal(skipUD: true, skipWebsocket: skipWebsocket) - } else { - Logger.info("UD REST request '\(self.label)' auth failed; aborting.") - throw RequestMakerUDAuthError.udAuthFailure - } - } - break - default: - break - } - - if isUDRequest { - Logger.debug("UD REST request '\(self.label)' failed: \(error).") - } else { - Logger.debug("Non-UD REST request '\(self.label)' failed: \(error).") - } - throw error - } - } - } - - private func requestSucceeded(udAccess: OWSUDAccess?) { - // If this was a UD request... - guard let udAccess = udAccess else { - return - } - // ...made for a user in "unknown" UD access mode... - guard udAccess.udAccessMode == .unknown else { - return - } - - if udAccess.isRandomKey { - // If a UD request succeeds for an unknown user with a random key, - // mark recipient as .unrestricted. - udManager.setUnidentifiedAccessMode(.unrestricted, recipientId: recipientId) - } else { - // If a UD request succeeds for an unknown user with a non-random key, - // mark recipient as .enabled. - udManager.setUnidentifiedAccessMode(.enabled, recipientId: recipientId) - } - DispatchQueue.main.async { - self.profileManager.fetchProfile(forRecipientId: self.recipientId) - } - } -} diff --git a/SignalUtilitiesKit/OWSSignalService.h b/SignalUtilitiesKit/OWSSignalService.h deleted file mode 100644 index 32d4b9583..000000000 --- a/SignalUtilitiesKit/OWSSignalService.h +++ /dev/null @@ -1,37 +0,0 @@ -// -// Copyright (c) 2019 Open Whisper Systems. All rights reserved. -// - -#import - -NS_ASSUME_NONNULL_BEGIN - -extern NSString *const kNSNotificationName_IsCensorshipCircumventionActiveDidChange; - -@class AFHTTPSessionManager; -@class OWSPrimaryStorage; -@class TSAccountManager; - -@interface OWSSignalService : NSObject - -/// For uploading avatar assets. -@property (nonatomic, readonly) AFHTTPSessionManager *CDNSessionManager; - -+ (instancetype)sharedInstance; - -- (instancetype)init NS_UNAVAILABLE; - -#pragma mark - Censorship Circumvention - -@property (atomic, readonly) BOOL isCensorshipCircumventionActive; -@property (atomic, readonly) BOOL hasCensoredPhoneNumber; -@property (atomic) BOOL isCensorshipCircumventionManuallyActivated; -@property (atomic) BOOL isCensorshipCircumventionManuallyDisabled; -@property (atomic, nullable) NSString *manualCensorshipCircumventionCountryCode; - -/// For interacting with the Signal Service -- (AFHTTPSessionManager *)buildSignalServiceSessionManager; - -@end - -NS_ASSUME_NONNULL_END diff --git a/SignalUtilitiesKit/OWSSignalService.m b/SignalUtilitiesKit/OWSSignalService.m deleted file mode 100644 index 15d312f74..000000000 --- a/SignalUtilitiesKit/OWSSignalService.m +++ /dev/null @@ -1,328 +0,0 @@ -// -// Copyright (c) 2019 Open Whisper Systems. All rights reserved. -// - -#import "OWSSignalService.h" -#import "NSNotificationCenter+OWS.h" -#import "OWSCensorshipConfiguration.h" -#import "OWSError.h" -#import "OWSHTTPSecurityPolicy.h" -#import "OWSPrimaryStorage.h" -#import "TSAccountManager.h" -#import "TSConstants.h" -#import "YapDatabaseConnection+OWS.h" -#import -#import -#import "SSKAsserts.h" - -NS_ASSUME_NONNULL_BEGIN - -NSString *const kOWSPrimaryStorage_OWSSignalService = @"kTSStorageManager_OWSSignalService"; -NSString *const kOWSPrimaryStorage_isCensorshipCircumventionManuallyActivated - = @"kTSStorageManager_isCensorshipCircumventionManuallyActivated"; -NSString *const kOWSPrimaryStorage_isCensorshipCircumventionManuallyDisabled - = @"kTSStorageManager_isCensorshipCircumventionManuallyDisabled"; -NSString *const kOWSPrimaryStorage_ManualCensorshipCircumventionDomain - = @"kTSStorageManager_ManualCensorshipCircumventionDomain"; -NSString *const kOWSPrimaryStorage_ManualCensorshipCircumventionCountryCode - = @"kTSStorageManager_ManualCensorshipCircumventionCountryCode"; - -NSString *const kNSNotificationName_IsCensorshipCircumventionActiveDidChange = - @"kNSNotificationName_IsCensorshipCircumventionActiveDidChange"; - -@interface OWSSignalService () - -@property (atomic) BOOL hasCensoredPhoneNumber; - -@property (atomic) BOOL isCensorshipCircumventionActive; - -@end - -#pragma mark - - -@implementation OWSSignalService - -@synthesize isCensorshipCircumventionActive = _isCensorshipCircumventionActive; - -+ (instancetype)sharedInstance -{ - static OWSSignalService *sharedInstance = nil; - static dispatch_once_t onceToken; - dispatch_once(&onceToken, ^{ - sharedInstance = [[self alloc] initDefault]; - }); - return sharedInstance; -} - -- (instancetype)initDefault -{ - self = [super init]; - if (!self) { - return self; - } - - [self observeNotifications]; - - [self updateHasCensoredPhoneNumber]; - [self updateIsCensorshipCircumventionActive]; - - OWSSingletonAssert(); - - return self; -} - -- (void)observeNotifications -{ - [[NSNotificationCenter defaultCenter] addObserver:self - selector:@selector(registrationStateDidChange:) - name:RegistrationStateDidChangeNotification - object:nil]; - [[NSNotificationCenter defaultCenter] addObserver:self - selector:@selector(localNumberDidChange:) - name:kNSNotificationName_LocalNumberDidChange - object:nil]; -} - -- (void)dealloc -{ - [[NSNotificationCenter defaultCenter] removeObserver:self]; -} - -- (void)updateHasCensoredPhoneNumber -{ - NSString *localNumber = [TSAccountManager localNumber]; - - if (localNumber) { - self.hasCensoredPhoneNumber = [OWSCensorshipConfiguration isCensoredPhoneNumber:localNumber]; - } else { - OWSLogError(@"no known phone number to check for censorship."); - self.hasCensoredPhoneNumber = NO; - } - - [self updateIsCensorshipCircumventionActive]; -} - -- (BOOL)isCensorshipCircumventionManuallyActivated -{ - return - [[OWSPrimaryStorage dbReadConnection] boolForKey:kOWSPrimaryStorage_isCensorshipCircumventionManuallyActivated - inCollection:kOWSPrimaryStorage_OWSSignalService - defaultValue:NO]; -} - -- (void)setIsCensorshipCircumventionManuallyActivated:(BOOL)value -{ - [[OWSPrimaryStorage dbReadWriteConnection] setObject:@(value) - forKey:kOWSPrimaryStorage_isCensorshipCircumventionManuallyActivated - inCollection:kOWSPrimaryStorage_OWSSignalService]; - - [self updateIsCensorshipCircumventionActive]; -} - -- (BOOL)isCensorshipCircumventionManuallyDisabled -{ - return [[OWSPrimaryStorage dbReadConnection] boolForKey:kOWSPrimaryStorage_isCensorshipCircumventionManuallyDisabled - inCollection:kOWSPrimaryStorage_OWSSignalService - defaultValue:NO]; -} - -- (void)setIsCensorshipCircumventionManuallyDisabled:(BOOL)value -{ - [[OWSPrimaryStorage dbReadWriteConnection] setObject:@(value) - forKey:kOWSPrimaryStorage_isCensorshipCircumventionManuallyDisabled - inCollection:kOWSPrimaryStorage_OWSSignalService]; - - [self updateIsCensorshipCircumventionActive]; -} - - -- (void)updateIsCensorshipCircumventionActive -{ - if (self.isCensorshipCircumventionManuallyDisabled) { - self.isCensorshipCircumventionActive = NO; - } else if (self.isCensorshipCircumventionManuallyActivated) { - self.isCensorshipCircumventionActive = YES; - } else if (self.hasCensoredPhoneNumber) { - self.isCensorshipCircumventionActive = YES; - } else { - self.isCensorshipCircumventionActive = NO; - } -} - -- (void)setIsCensorshipCircumventionActive:(BOOL)isCensorshipCircumventionActive -{ - @synchronized(self) - { - if (_isCensorshipCircumventionActive == isCensorshipCircumventionActive) { - return; - } - - _isCensorshipCircumventionActive = isCensorshipCircumventionActive; - } - - [[NSNotificationCenter defaultCenter] - postNotificationNameAsync:kNSNotificationName_IsCensorshipCircumventionActiveDidChange - object:nil - userInfo:nil]; -} - -- (BOOL)isCensorshipCircumventionActive -{ - @synchronized(self) - { - return _isCensorshipCircumventionActive; - } -} - -- (AFHTTPSessionManager *)buildSignalServiceSessionManager -{ - if (self.isCensorshipCircumventionActive) { - OWSCensorshipConfiguration *censorshipConfiguration = [self buildCensorshipConfiguration]; - OWSLogInfo(@"using reflector HTTPSessionManager via: %@", censorshipConfiguration.domainFrontBaseURL); - return [self reflectorSignalServiceSessionManagerWithCensorshipConfiguration:censorshipConfiguration]; - } else { - return self.defaultSignalServiceSessionManager; - } -} - -- (AFHTTPSessionManager *)defaultSignalServiceSessionManager -{ - NSURLSessionConfiguration *configuration = NSURLSessionConfiguration.ephemeralSessionConfiguration; - AFHTTPSessionManager *sessionManager = [[AFHTTPSessionManager alloc] initWithSessionConfiguration:configuration]; - AFSecurityPolicy *securityPolicy = AFSecurityPolicy.defaultPolicy; - // Snode to snode communication uses self-signed certificates but clients can safely ignore this - securityPolicy.allowInvalidCertificates = YES; - securityPolicy.validatesDomainName = NO; - sessionManager.securityPolicy = securityPolicy; - sessionManager.requestSerializer = [AFJSONRequestSerializer serializer]; - sessionManager.requestSerializer.HTTPShouldHandleCookies = NO; - sessionManager.responseSerializer = [AFJSONResponseSerializer serializerWithReadingOptions:NSJSONReadingAllowFragments]; - NSMutableSet *acceptableContentTypes = sessionManager.responseSerializer.acceptableContentTypes.mutableCopy; - [acceptableContentTypes addObject:@"text/plain"]; - sessionManager.responseSerializer.acceptableContentTypes = acceptableContentTypes; - return sessionManager; -} - -- (AFHTTPSessionManager *)reflectorSignalServiceSessionManagerWithCensorshipConfiguration: - (OWSCensorshipConfiguration *)censorshipConfiguration -{ - NSURLSessionConfiguration *sessionConf = NSURLSessionConfiguration.ephemeralSessionConfiguration; - AFHTTPSessionManager *sessionManager = - [[AFHTTPSessionManager alloc] initWithBaseURL:censorshipConfiguration.domainFrontBaseURL - sessionConfiguration:sessionConf]; - - sessionManager.securityPolicy = censorshipConfiguration.domainFrontSecurityPolicy; - - sessionManager.requestSerializer = [AFJSONRequestSerializer serializer]; - [sessionManager.requestSerializer setValue:censorshipConfiguration.signalServiceReflectorHost - forHTTPHeaderField:@"Host"]; - sessionManager.responseSerializer = [AFJSONResponseSerializer serializer]; - // Disable default cookie handling for all requests. - sessionManager.requestSerializer.HTTPShouldHandleCookies = NO; - - return sessionManager; -} - -#pragma mark - Profile Uploading - -- (AFHTTPSessionManager *)CDNSessionManager -{ - if (self.isCensorshipCircumventionActive) { - OWSCensorshipConfiguration *censorshipConfiguration = [self buildCensorshipConfiguration]; - OWSLogInfo(@"using reflector CDNSessionManager via: %@", censorshipConfiguration.domainFrontBaseURL); - return [self reflectorCDNSessionManagerWithCensorshipConfiguration:censorshipConfiguration]; - } else { - return self.defaultCDNSessionManager; - } -} - -- (AFHTTPSessionManager *)defaultCDNSessionManager -{ - NSURLSessionConfiguration *sessionConf = NSURLSessionConfiguration.ephemeralSessionConfiguration; - AFHTTPSessionManager *sessionManager = - [[AFHTTPSessionManager alloc] initWithBaseURL:nil sessionConfiguration:sessionConf]; - - sessionManager.securityPolicy = [OWSHTTPSecurityPolicy sharedPolicy]; - - // Default acceptable content headers are rejected by AWS - sessionManager.responseSerializer.acceptableContentTypes = nil; - - return sessionManager; -} - -- (AFHTTPSessionManager *)reflectorCDNSessionManagerWithCensorshipConfiguration: - (OWSCensorshipConfiguration *)censorshipConfiguration -{ - NSURLSessionConfiguration *sessionConf = NSURLSessionConfiguration.ephemeralSessionConfiguration; - - AFHTTPSessionManager *sessionManager = - [[AFHTTPSessionManager alloc] initWithBaseURL:censorshipConfiguration.domainFrontBaseURL - sessionConfiguration:sessionConf]; - - sessionManager.securityPolicy = censorshipConfiguration.domainFrontSecurityPolicy; - - sessionManager.requestSerializer = [AFJSONRequestSerializer serializer]; - [sessionManager.requestSerializer setValue:censorshipConfiguration.CDNReflectorHost forHTTPHeaderField:@"Host"]; - - sessionManager.responseSerializer = [AFJSONResponseSerializer serializer]; - - return sessionManager; -} - -#pragma mark - Events - -- (void)registrationStateDidChange:(NSNotification *)notification -{ - [self updateHasCensoredPhoneNumber]; -} - -- (void)localNumberDidChange:(NSNotification *)notification -{ - [self updateHasCensoredPhoneNumber]; -} - -#pragma mark - Manual Censorship Circumvention - -- (OWSCensorshipConfiguration *)buildCensorshipConfiguration -{ - OWSAssertDebug(self.isCensorshipCircumventionActive); - - if (self.isCensorshipCircumventionManuallyActivated) { - NSString *countryCode = self.manualCensorshipCircumventionCountryCode; - if (countryCode.length == 0) { - OWSFailDebug(@"manualCensorshipCircumventionCountryCode was unexpectedly 0"); - } - - OWSCensorshipConfiguration *configuration = - [OWSCensorshipConfiguration censorshipConfigurationWithCountryCode:countryCode]; - OWSAssertDebug(configuration); - - return configuration; - } - - OWSCensorshipConfiguration *_Nullable configuration = - [OWSCensorshipConfiguration censorshipConfigurationWithPhoneNumber:TSAccountManager.localNumber]; - if (configuration != nil) { - return configuration; - } - - return OWSCensorshipConfiguration.defaultConfiguration; -} - -- (nullable NSString *)manualCensorshipCircumventionCountryCode -{ - return - [[OWSPrimaryStorage dbReadConnection] objectForKey:kOWSPrimaryStorage_ManualCensorshipCircumventionCountryCode - inCollection:kOWSPrimaryStorage_OWSSignalService]; -} - -- (void)setManualCensorshipCircumventionCountryCode:(nullable NSString *)value -{ - [[OWSPrimaryStorage dbReadWriteConnection] setObject:value - forKey:kOWSPrimaryStorage_ManualCensorshipCircumventionCountryCode - inCollection:kOWSPrimaryStorage_OWSSignalService]; -} - -@end - -NS_ASSUME_NONNULL_END diff --git a/SignalUtilitiesKit/OWSSyncConfigurationMessage.h b/SignalUtilitiesKit/OWSSyncConfigurationMessage.h deleted file mode 100644 index d30025fa0..000000000 --- a/SignalUtilitiesKit/OWSSyncConfigurationMessage.h +++ /dev/null @@ -1,22 +0,0 @@ -// -// Copyright (c) 2019 Open Whisper Systems. All rights reserved. -// - -#import - -NS_ASSUME_NONNULL_BEGIN - -@interface OWSSyncConfigurationMessage : OWSOutgoingSyncMessage - -- (instancetype)init NS_UNAVAILABLE; - -- (instancetype)initWithReadReceiptsEnabled:(BOOL)readReceiptsEnabled - showUnidentifiedDeliveryIndicators:(BOOL)showUnidentifiedDeliveryIndicators - showTypingIndicators:(BOOL)showTypingIndicators - sendLinkPreviews:(BOOL)sendLinkPreviews NS_DESIGNATED_INITIALIZER; - -- (nullable instancetype)initWithCoder:(NSCoder *)coder NS_DESIGNATED_INITIALIZER; - -@end - -NS_ASSUME_NONNULL_END diff --git a/SignalUtilitiesKit/OWSSyncConfigurationMessage.m b/SignalUtilitiesKit/OWSSyncConfigurationMessage.m deleted file mode 100644 index 1254b7856..000000000 --- a/SignalUtilitiesKit/OWSSyncConfigurationMessage.m +++ /dev/null @@ -1,66 +0,0 @@ -// -// Copyright (c) 2019 Open Whisper Systems. All rights reserved. -// - -#import "OWSSyncConfigurationMessage.h" -#import - -NS_ASSUME_NONNULL_BEGIN - -@interface OWSSyncConfigurationMessage () - -@property (nonatomic, readonly) BOOL areReadReceiptsEnabled; -@property (nonatomic, readonly) BOOL showUnidentifiedDeliveryIndicators; -@property (nonatomic, readonly) BOOL showTypingIndicators; -@property (nonatomic, readonly) BOOL sendLinkPreviews; - -@end - -@implementation OWSSyncConfigurationMessage - -- (instancetype)initWithReadReceiptsEnabled:(BOOL)areReadReceiptsEnabled - showUnidentifiedDeliveryIndicators:(BOOL)showUnidentifiedDeliveryIndicators - showTypingIndicators:(BOOL)showTypingIndicators - sendLinkPreviews:(BOOL)sendLinkPreviews -{ - self = [super init]; - if (!self) { - return nil; - } - - _areReadReceiptsEnabled = areReadReceiptsEnabled; - _showUnidentifiedDeliveryIndicators = showUnidentifiedDeliveryIndicators; - _showTypingIndicators = showTypingIndicators; - _sendLinkPreviews = sendLinkPreviews; - - return self; -} - -- (nullable instancetype)initWithCoder:(NSCoder *)coder -{ - return [super initWithCoder:coder]; -} - -- (nullable SSKProtoSyncMessageBuilder *)syncMessageBuilder -{ - SSKProtoSyncMessageConfigurationBuilder *configurationBuilder = [SSKProtoSyncMessageConfiguration builder]; - configurationBuilder.readReceipts = self.areReadReceiptsEnabled; - configurationBuilder.unidentifiedDeliveryIndicators = self.showUnidentifiedDeliveryIndicators; - configurationBuilder.typingIndicators = self.showTypingIndicators; - configurationBuilder.linkPreviews = self.sendLinkPreviews; - - NSError *error; - SSKProtoSyncMessageConfiguration *_Nullable configurationProto = [configurationBuilder buildAndReturnError:&error]; - if (error || !configurationProto) { - OWSFailDebug(@"could not build protobuf: %@", error); - return nil; - } - - SSKProtoSyncMessageBuilder *builder = [SSKProtoSyncMessage builder]; - builder.configuration = configurationProto; - return builder; -} - -@end - -NS_ASSUME_NONNULL_END diff --git a/SignalUtilitiesKit/OWSSyncContactsMessage.h b/SignalUtilitiesKit/OWSSyncContactsMessage.h deleted file mode 100644 index b91471516..000000000 --- a/SignalUtilitiesKit/OWSSyncContactsMessage.h +++ /dev/null @@ -1,28 +0,0 @@ -// -// Copyright (c) 2018 Open Whisper Systems. All rights reserved. -// - -#import - -NS_ASSUME_NONNULL_BEGIN - -@protocol ProfileManagerProtocol; - -@class OWSIdentityManager; -@class SignalAccount; - -@interface OWSSyncContactsMessage : OWSOutgoingSyncMessage - -- (instancetype)init NS_UNAVAILABLE; - -- (instancetype)initWithSignalAccounts:(NSArray *)signalAccounts - identityManager:(OWSIdentityManager *)identityManager - profileManager:(id)profileManager NS_DESIGNATED_INITIALIZER; - -- (nullable instancetype)initWithCoder:(NSCoder *)coder NS_DESIGNATED_INITIALIZER; - -- (nullable NSData *)buildPlainTextAttachmentDataWithTransaction:(YapDatabaseReadTransaction *)transaction; - -@end - -NS_ASSUME_NONNULL_END diff --git a/SignalUtilitiesKit/OWSSyncContactsMessage.m b/SignalUtilitiesKit/OWSSyncContactsMessage.m deleted file mode 100644 index 8747fd31e..000000000 --- a/SignalUtilitiesKit/OWSSyncContactsMessage.m +++ /dev/null @@ -1,157 +0,0 @@ -// -// Copyright (c) 2018 Open Whisper Systems. All rights reserved. -// - -#import "OWSSyncContactsMessage.h" -#import "Contact.h" -#import "ContactsManagerProtocol.h" -#import "OWSContactsOutputStream.h" -#import "OWSIdentityManager.h" -#import "ProfileManagerProtocol.h" -#import "ProtoUtils.h" -#import "SSKEnvironment.h" -#import "SignalAccount.h" -#import "TSAccountManager.h" -#import "TSAttachment.h" -#import "TSAttachmentStream.h" -#import "TSContactThread.h" -#import -#import -#import "OWSPrimaryStorage.h" - -@import Contacts; - -NS_ASSUME_NONNULL_BEGIN - -@interface OWSSyncContactsMessage () - -@property (nonatomic, readonly) NSArray *signalAccounts; -@property (nonatomic, readonly) OWSIdentityManager *identityManager; -@property (nonatomic, readonly) id profileManager; - -@end - -@implementation OWSSyncContactsMessage - -- (instancetype)initWithSignalAccounts:(NSArray *)signalAccounts - identityManager:(OWSIdentityManager *)identityManager - profileManager:(id)profileManager -{ - self = [super init]; - if (!self) { - return self; - } - - _signalAccounts = signalAccounts; - _identityManager = identityManager; - _profileManager = profileManager; - - return self; -} - -- (nullable instancetype)initWithCoder:(NSCoder *)coder -{ - return [super initWithCoder:coder]; -} - -#pragma mark - Dependencies - -- (id)contactsManager { - return SSKEnvironment.shared.contactsManager; -} - -- (TSAccountManager *)tsAccountManager { - return TSAccountManager.sharedInstance; -} - -#pragma mark - - -- (nullable SSKProtoSyncMessageBuilder *)syncMessageBuilder -{ - NSError *error; - if (self.attachmentIds.count > 1) { - OWSLogError(@"Expected sync contact message to have one or zero attachments, but found %lu.", (unsigned long)self.attachmentIds.count); - } - - SSKProtoSyncMessageContactsBuilder *contactsBuilder; - if (self.attachmentIds.count == 0) { - SSKProtoAttachmentPointerBuilder *attachmentProtoBuilder = [SSKProtoAttachmentPointer builderWithId:0]; - SSKProtoAttachmentPointer *attachmentProto = [attachmentProtoBuilder buildAndReturnError:&error]; - contactsBuilder = [SSKProtoSyncMessageContacts builder]; - [contactsBuilder setBlob:attachmentProto]; - __block NSData *data; - [OWSPrimaryStorage.sharedManager.dbReadConnection readWithBlock:^(YapDatabaseReadTransaction *transaction) { - data = [self buildPlainTextAttachmentDataWithTransaction:transaction]; - }]; - [contactsBuilder setData:data]; - } else { - SSKProtoAttachmentPointer *attachmentProto = [TSAttachmentStream buildProtoForAttachmentId:self.attachmentIds.firstObject]; - if (attachmentProto == nil) { - OWSFailDebug(@"Couldn't build protobuf."); - return nil; - } - contactsBuilder = [SSKProtoSyncMessageContacts builder]; - [contactsBuilder setBlob:attachmentProto]; - } - [contactsBuilder setIsComplete:YES]; - - SSKProtoSyncMessageContacts *contactsProto = [contactsBuilder buildAndReturnError:&error]; - if (error || contactsProto == nil) { - OWSFailDebug(@"Couldn't build protobuf due to error: %@.", error); - return nil; - } - SSKProtoSyncMessageBuilder *syncMessageBuilder = [SSKProtoSyncMessage builder]; - [syncMessageBuilder setContacts:contactsProto]; - - return syncMessageBuilder; -} - -- (nullable NSData *)buildPlainTextAttachmentDataWithTransaction:(YapDatabaseReadTransaction *)transaction -{ - NSMutableArray *signalAccounts = [self.signalAccounts mutableCopy]; - - // TODO use temp file stream to avoid loading everything into memory at once - // First though, we need to re-engineer our attachment process to accept streams (encrypting with stream, - // and uploading with streams). - NSOutputStream *dataOutputStream = [NSOutputStream outputStreamToMemory]; - [dataOutputStream open]; - OWSContactsOutputStream *contactsOutputStream = - [[OWSContactsOutputStream alloc] initWithOutputStream:dataOutputStream]; - - for (SignalAccount *signalAccount in signalAccounts) { - OWSRecipientIdentity *_Nullable recipientIdentity = - [self.identityManager recipientIdentityForRecipientId:signalAccount.recipientId]; - NSData *_Nullable profileKeyData = [self.profileManager profileKeyDataForRecipientId:signalAccount.recipientId]; - - OWSDisappearingMessagesConfiguration *_Nullable disappearingMessagesConfiguration; - NSString *conversationColorName; - - TSContactThread *_Nullable contactThread = [TSContactThread getThreadWithContactId:signalAccount.recipientId transaction:transaction]; - if (contactThread) { - conversationColorName = contactThread.conversationColorName; - disappearingMessagesConfiguration = [contactThread disappearingMessagesConfigurationWithTransaction:transaction]; - } else { - conversationColorName = [TSThread stableColorNameForNewConversationWithString:signalAccount.recipientId]; - } - - [contactsOutputStream writeSignalAccount:signalAccount - recipientIdentity:recipientIdentity - profileKeyData:profileKeyData - contactsManager:self.contactsManager - conversationColorName:conversationColorName - disappearingMessagesConfiguration:disappearingMessagesConfiguration]; - } - - [dataOutputStream close]; - - if (contactsOutputStream.hasError) { - OWSFailDebug(@"Could not write contacts sync stream."); - return nil; - } - - return [dataOutputStream propertyForKey:NSStreamDataWrittenToMemoryStreamKey]; -} - -@end - -NS_ASSUME_NONNULL_END diff --git a/SignalUtilitiesKit/OWSSyncGroupsMessage.h b/SignalUtilitiesKit/OWSSyncGroupsMessage.h deleted file mode 100644 index 59f105718..000000000 --- a/SignalUtilitiesKit/OWSSyncGroupsMessage.h +++ /dev/null @@ -1,24 +0,0 @@ -// -// Copyright (c) 2018 Open Whisper Systems. All rights reserved. -// - -#import - -NS_ASSUME_NONNULL_BEGIN - -@class YapDatabaseReadTransaction; -@class TSGroupThread; - -@interface OWSSyncGroupsMessage : OWSOutgoingSyncMessage - -- (instancetype)init NS_UNAVAILABLE; - -- (instancetype)initWithGroupThread:(TSGroupThread *)thread NS_DESIGNATED_INITIALIZER; - -- (nullable instancetype)initWithCoder:(NSCoder *)coder NS_DESIGNATED_INITIALIZER; - -- (nullable NSData *)buildPlainTextAttachmentDataWithTransaction:(YapDatabaseReadTransaction *)transaction; - -@end - -NS_ASSUME_NONNULL_END diff --git a/SignalUtilitiesKit/OWSSyncGroupsMessage.m b/SignalUtilitiesKit/OWSSyncGroupsMessage.m deleted file mode 100644 index f4440de44..000000000 --- a/SignalUtilitiesKit/OWSSyncGroupsMessage.m +++ /dev/null @@ -1,104 +0,0 @@ -// -// Copyright (c) 2018 Open Whisper Systems. All rights reserved. -// - -#import "OWSSyncGroupsMessage.h" -#import "OWSGroupsOutputStream.h" -#import "TSAttachment.h" -#import "TSAttachmentStream.h" -#import "TSContactThread.h" -#import "TSGroupModel.h" -#import "TSGroupThread.h" -#import -#import -#import "OWSPrimaryStorage.h" - -NS_ASSUME_NONNULL_BEGIN - -@interface OWSSyncGroupsMessage () - -@property (nonatomic, readonly) TSGroupThread *groupThread; - -@end - -@implementation OWSSyncGroupsMessage - -- (instancetype)initWithGroupThread:(TSGroupThread *)thread -{ - self = [super init]; - if (!self) { - return self; - } - - _groupThread = thread; - - return self; -} - -- (nullable instancetype)initWithCoder:(NSCoder *)coder -{ - return [super initWithCoder:coder]; -} - -- (nullable SSKProtoSyncMessageBuilder *)syncMessageBuilder -{ - NSError *error; - if (self.attachmentIds.count > 1) { - OWSLogError(@"Expected sync group message to have one or zero attachments, but found %lu.", (unsigned long)self.attachmentIds.count); - } - - SSKProtoSyncMessageGroupsBuilder *groupsBuilder; - if (self.attachmentIds.count == 0) { - SSKProtoAttachmentPointerBuilder *attachmentProtoBuilder = [SSKProtoAttachmentPointer builderWithId:0]; - SSKProtoAttachmentPointer *attachmentProto = [attachmentProtoBuilder buildAndReturnError:&error]; - groupsBuilder = [SSKProtoSyncMessageGroups builder]; - [groupsBuilder setBlob:attachmentProto]; - __block NSData *data; - [OWSPrimaryStorage.sharedManager.dbReadConnection readWithBlock:^(YapDatabaseReadTransaction *transaction) { - data = [self buildPlainTextAttachmentDataWithTransaction:transaction]; - }]; - [groupsBuilder setData:data]; - } else { - SSKProtoAttachmentPointer *attachmentProto = [TSAttachmentStream buildProtoForAttachmentId:self.attachmentIds.firstObject]; - if (attachmentProto == nil) { - OWSFailDebug(@"Couldn't build protobuf."); - return nil; - } - groupsBuilder = [SSKProtoSyncMessageGroups builder]; - [groupsBuilder setBlob:attachmentProto]; - } - - SSKProtoSyncMessageGroups *_Nullable groupsProto = [groupsBuilder buildAndReturnError:&error]; - if (error || !groupsProto) { - OWSFailDebug(@"Couldn't build protobuf due to error: %@.", error); - return nil; - } - - SSKProtoSyncMessageBuilder *syncMessageBuilder = [SSKProtoSyncMessage builder]; - [syncMessageBuilder setGroups:groupsProto]; - - return syncMessageBuilder; -} - -- (nullable NSData *)buildPlainTextAttachmentDataWithTransaction:(YapDatabaseReadTransaction *)transaction -{ - // TODO use temp file stream to avoid loading everything into memory at once - // First though, we need to re-engineer our attachment process to accept streams (encrypting with stream, - // and uploading with streams). - NSOutputStream *dataOutputStream = [NSOutputStream outputStreamToMemory]; - [dataOutputStream open]; - OWSGroupsOutputStream *groupsOutputStream = [[OWSGroupsOutputStream alloc] initWithOutputStream:dataOutputStream]; - [groupsOutputStream writeGroup:self.groupThread transaction:transaction]; - [dataOutputStream close]; - - if (groupsOutputStream.hasError) { - OWSFailDebug(@"Could not write groups sync stream."); - return nil; - } - - return [dataOutputStream propertyForKey:NSStreamDataWrittenToMemoryStreamKey]; -} - -@end - -NS_ASSUME_NONNULL_END diff --git a/SignalUtilitiesKit/OWSSyncGroupsRequestMessage.h b/SignalUtilitiesKit/OWSSyncGroupsRequestMessage.h deleted file mode 100644 index fd1a14541..000000000 --- a/SignalUtilitiesKit/OWSSyncGroupsRequestMessage.h +++ /dev/null @@ -1,27 +0,0 @@ -// -// Copyright (c) 2019 Open Whisper Systems. All rights reserved. -// - -#import - -NS_ASSUME_NONNULL_BEGIN - -@interface OWSSyncGroupsRequestMessage : TSOutgoingMessage - -- (instancetype)initOutgoingMessageWithTimestamp:(uint64_t)timestamp - inThread:(nullable TSThread *)thread - messageBody:(nullable NSString *)body - attachmentIds:(NSMutableArray *)attachmentIds - expiresInSeconds:(uint32_t)expiresInSeconds - expireStartedAt:(uint64_t)expireStartedAt - isVoiceMessage:(BOOL)isVoiceMessage - groupMetaMessage:(TSGroupMetaMessage)groupMetaMessage - quotedMessage:(nullable TSQuotedMessage *)quotedMessage - contactShare:(nullable OWSContact *)contactShare - linkPreview:(nullable OWSLinkPreview *)linkPreview NS_UNAVAILABLE; - -- (instancetype)initWithThread:(nullable TSThread *)thread groupId:(NSData *)groupId; - -@end - -NS_ASSUME_NONNULL_END diff --git a/SignalUtilitiesKit/OWSSyncGroupsRequestMessage.m b/SignalUtilitiesKit/OWSSyncGroupsRequestMessage.m deleted file mode 100644 index 41962325c..000000000 --- a/SignalUtilitiesKit/OWSSyncGroupsRequestMessage.m +++ /dev/null @@ -1,85 +0,0 @@ -// -// Copyright (c) 2019 Open Whisper Systems. All rights reserved. -// - -#import "OWSSyncGroupsRequestMessage.h" -#import -#import - -NS_ASSUME_NONNULL_BEGIN - -@interface OWSSyncGroupsRequestMessage () - -@property (nonatomic) NSData *groupId; - -@end - -#pragma mark - - -@implementation OWSSyncGroupsRequestMessage - -- (instancetype)initWithThread:(nullable TSThread *)thread groupId:(NSData *)groupId -{ - // MJK TODO - remove senderTimestamp - self = [super initOutgoingMessageWithTimestamp:[NSDate ows_millisecondTimeStamp] - inThread:thread - messageBody:nil - attachmentIds:[NSMutableArray new] - expiresInSeconds:0 - expireStartedAt:0 - isVoiceMessage:NO - groupMetaMessage:TSGroupMetaMessageUnspecified - quotedMessage:nil - contactShare:nil - linkPreview:nil]; - if (!self) { - return self; - } - - OWSAssertDebug(groupId.length > 0); - _groupId = groupId; - - return self; -} - -- (uint)ttl { return (uint)[LKTTLUtilities getTTLFor:LKMessageTypeSync]; } - -- (BOOL)shouldBeSaved -{ - return NO; -} - -- (BOOL)shouldSyncTranscript -{ - return NO; -} - -- (BOOL)isSilent -{ - // Avoid "phantom messages" - - return YES; -} - -- (nullable id)dataMessageBuilder -{ - SSKProtoGroupContextBuilder *groupContextBuilder = - [SSKProtoGroupContext builderWithId:self.groupId type:SSKProtoGroupContextTypeRequestInfo]; - - NSError *error; - SSKProtoGroupContext *_Nullable groupContextProto = [groupContextBuilder buildAndReturnError:&error]; - if (error || !groupContextProto) { - OWSFailDebug(@"could not build protobuf: %@", error); - return nil; - } - - SSKProtoDataMessageBuilder *builder = [SSKProtoDataMessage builder]; - [builder setTimestamp:self.timestamp]; - [builder setGroup:groupContextProto]; - - return builder; -} - -@end - -NS_ASSUME_NONNULL_END diff --git a/SignalUtilitiesKit/OWSSyncManager.h b/SignalUtilitiesKit/OWSSyncManager.h deleted file mode 100644 index cdea1f23c..000000000 --- a/SignalUtilitiesKit/OWSSyncManager.h +++ /dev/null @@ -1,25 +0,0 @@ -// -// Copyright (c) 2018 Open Whisper Systems. All rights reserved. -// - -#import - -NS_ASSUME_NONNULL_BEGIN - -@class AnyPromise; -@class OWSContactsManager; -@class OWSIdentityManager; -@class OWSMessageSender; -@class OWSProfileManager; - -@interface OWSSyncManager : NSObject - -- (instancetype)init NS_UNAVAILABLE; - -- (instancetype)initDefault NS_DESIGNATED_INITIALIZER; - -+ (instancetype)shared; - -@end - -NS_ASSUME_NONNULL_END diff --git a/SignalUtilitiesKit/OWSSyncManager.m b/SignalUtilitiesKit/OWSSyncManager.m deleted file mode 100644 index d7e043956..000000000 --- a/SignalUtilitiesKit/OWSSyncManager.m +++ /dev/null @@ -1,337 +0,0 @@ -// -// Copyright (c) 2019 Open Whisper Systems. All rights reserved. -// - -#import "OWSSyncManager.h" -#import "Environment.h" -#import "OWSContactsManager.h" -#import "OWSPreferences.h" -#import "OWSProfileManager.h" -#import "OWSReadReceiptManager.h" -#import -#import -#import -#import -#import -#import -#import -#import -#import -#import -#import -#import -#import -#import -#import -#import -#import -#import "SSKAsserts.h" - -NS_ASSUME_NONNULL_BEGIN - -NSString *const kSyncManagerCollection = @"kTSStorageManagerOWSSyncManagerCollection"; -NSString *const kSyncManagerLastContactSyncKey = @"kTSStorageManagerOWSSyncManagerLastMessageKey"; - -@interface OWSSyncManager () - -@property (nonatomic, readonly) dispatch_queue_t serialQueue; - -@property (nonatomic) BOOL isRequestInFlight; - -@end - -@implementation OWSSyncManager - -+ (instancetype)shared { - OWSAssertDebug(SSKEnvironment.shared.syncManager); - - return SSKEnvironment.shared.syncManager; -} - -- (instancetype)initDefault { - self = [super init]; - - if (!self) { - return self; - } - - OWSSingletonAssert(); - - [[NSNotificationCenter defaultCenter] addObserver:self - selector:@selector(signalAccountsDidChange:) - name:OWSContactsManagerSignalAccountsDidChangeNotification - object:nil]; - [[NSNotificationCenter defaultCenter] addObserver:self - selector:@selector(profileKeyDidChange:) - name:kNSNotificationName_ProfileKeyDidChange - object:nil]; - - return self; -} - -- (void)dealloc { - [[NSNotificationCenter defaultCenter] removeObserver:self]; -} - -#pragma mark - Dependencies - -- (OWSContactsManager *)contactsManager { - OWSAssertDebug(Environment.shared.contactsManager); - - return Environment.shared.contactsManager; -} - -- (OWSIdentityManager *)identityManager { - OWSAssertDebug(SSKEnvironment.shared.identityManager); - - return SSKEnvironment.shared.identityManager; -} - -- (OWSMessageSender *)messageSender { - OWSAssertDebug(SSKEnvironment.shared.messageSender); - - return SSKEnvironment.shared.messageSender; -} - -- (SSKMessageSenderJobQueue *)messageSenderJobQueue -{ - OWSAssertDebug(SSKEnvironment.shared.messageSenderJobQueue); - - return SSKEnvironment.shared.messageSenderJobQueue; -} - -- (OWSProfileManager *)profileManager { - OWSAssertDebug(SSKEnvironment.shared.profileManager); - - return SSKEnvironment.shared.profileManager; -} - -- (TSAccountManager *)tsAccountManager -{ - return TSAccountManager.sharedInstance; -} - -- (id)typingIndicators -{ - return SSKEnvironment.shared.typingIndicators; -} - -#pragma mark - Notifications - -- (void)signalAccountsDidChange:(id)notification { - OWSAssertIsOnMainThread(); - - [self sendSyncContactsMessageIfPossible]; -} - -- (void)profileKeyDidChange:(id)notification { - OWSAssertIsOnMainThread(); - - [self sendSyncContactsMessageIfPossible]; -} - -#pragma mark - - -- (YapDatabaseConnection *)editingDatabaseConnection -{ - return OWSPrimaryStorage.sharedManager.dbReadWriteConnection; -} - -- (YapDatabaseConnection *)readDatabaseConnection -{ - return OWSPrimaryStorage.sharedManager.dbReadConnection; -} - -#pragma mark - Methods - -- (void)sendSyncContactsMessageIfNecessary { - OWSAssertIsOnMainThread(); - - if (!self.serialQueue) { - _serialQueue = dispatch_queue_create("org.whispersystems.contacts.syncing", DISPATCH_QUEUE_SERIAL); - } - - dispatch_async(self.serialQueue, ^{ - if (self.isRequestInFlight) { - // De-bounce. It's okay if we ignore some new changes; - // `sendSyncContactsMessageIfPossible` is called fairly - // often so we'll sync soon. - return; - } - - OWSSyncContactsMessage *syncContactsMessage = - [[OWSSyncContactsMessage alloc] initWithSignalAccounts:self.contactsManager.signalAccounts - identityManager:self.identityManager - profileManager:self.profileManager]; - - __block NSData *_Nullable messageData; - __block NSData *_Nullable lastMessageData; - [self.readDatabaseConnection readWithBlock:^(YapDatabaseReadTransaction *transaction) { - messageData = [syncContactsMessage buildPlainTextAttachmentDataWithTransaction:transaction]; - lastMessageData = [transaction objectForKey:kSyncManagerLastContactSyncKey - inCollection:kSyncManagerCollection]; - }]; - - if (!messageData) { - OWSFailDebug(@"Failed to serialize contacts sync message."); - return; - } - - if (lastMessageData && [lastMessageData isEqual:messageData]) { - // Ignore redundant contacts sync message. - return; - } - - self.isRequestInFlight = YES; - - // DURABLE CLEANUP - we could replace the custom durability logic in this class - // with a durable JobQueue. - DataSource *dataSource = [DataSourceValue dataSourceWithSyncMessageData:messageData]; - [self.messageSender sendTemporaryAttachment:dataSource - contentType:OWSMimeTypeApplicationOctetStream - inMessage:syncContactsMessage - success:^{ - OWSLogInfo(@"Successfully sent contacts sync message."); - - [self.editingDatabaseConnection setObject:messageData - forKey:kSyncManagerLastContactSyncKey - inCollection:kSyncManagerCollection]; - - dispatch_async(self.serialQueue, ^{ - self.isRequestInFlight = NO; - }); - } - failure:^(NSError *error) { - OWSLogError(@"Failed to send contacts sync message with error: %@", error); - - dispatch_async(self.serialQueue, ^{ - self.isRequestInFlight = NO; - }); - }]; - }); -} - -- (void)sendSyncContactsMessageIfPossible { - OWSAssertIsOnMainThread(); - - if (!self.contactsManager.isSetup) { - // Don't bother if the contacts manager hasn't finished setup. - return; - } - - if ([TSAccountManager sharedInstance].isRegisteredAndReady) { - [self sendSyncContactsMessageIfNecessary]; - } -} - -- (void)sendConfigurationSyncMessage { - [AppReadiness runNowOrWhenAppDidBecomeReady:^{ - if (!self.tsAccountManager.isRegisteredAndReady) { - return; - } - - [self sendConfigurationSyncMessage_AppReady]; - }]; -} - -- (void)sendConfigurationSyncMessage_AppReady { - DDLogInfo(@""); - - if (![TSAccountManager sharedInstance].isRegisteredAndReady) { - return; - } - - BOOL areReadReceiptsEnabled = SSKEnvironment.shared.readReceiptManager.areReadReceiptsEnabled; - BOOL showUnidentifiedDeliveryIndicators = Environment.shared.preferences.shouldShowUnidentifiedDeliveryIndicators; - BOOL showTypingIndicators = self.typingIndicators.areTypingIndicatorsEnabled; - BOOL sendLinkPreviews = SSKPreferences.areLinkPreviewsEnabled; - - OWSSyncConfigurationMessage *syncConfigurationMessage = - [[OWSSyncConfigurationMessage alloc] initWithReadReceiptsEnabled:areReadReceiptsEnabled - showUnidentifiedDeliveryIndicators:showUnidentifiedDeliveryIndicators - showTypingIndicators:showTypingIndicators - sendLinkPreviews:sendLinkPreviews]; - - [LKStorage writeSyncWithBlock:^(YapDatabaseReadWriteTransaction *_Nonnull transaction) { - [self.messageSenderJobQueue addMessage:syncConfigurationMessage transaction:transaction]; - }]; -} - -#pragma mark - Local Sync - -- (AnyPromise *)syncLocalContact -{ - NSString *localNumber = self.tsAccountManager.localNumber; - SignalAccount *signalAccount = [[SignalAccount alloc] initWithRecipientId:localNumber]; - signalAccount.contact = [Contact new]; - - return [self syncContactsForSignalAccounts:@[ signalAccount ]]; -} - -- (AnyPromise *)syncContact:(NSString *)hexEncodedPubKey transaction:(YapDatabaseReadTransaction *)transaction -{ - return [LKSyncMessagesProtocol syncContactWithPublicKey:hexEncodedPubKey]; -} - -- (AnyPromise *)syncAllContacts -{ - return [LKSyncMessagesProtocol syncAllContacts]; -} - -- (AnyPromise *)syncContactsForSignalAccounts:(NSArray *)signalAccounts -{ - OWSSyncContactsMessage *syncContactsMessage = [[OWSSyncContactsMessage alloc] initWithSignalAccounts:signalAccounts identityManager:self.identityManager profileManager:self.profileManager]; - AnyPromise *promise = [AnyPromise promiseWithResolverBlock:^(PMKResolver resolve) { - [self.messageSender sendMessage:syncContactsMessage - success:^{ - OWSLogInfo(@"Successfully sent contacts sync message."); - resolve(@(1)); - } - failure:^(NSError *error) { - OWSLogError(@"Failed to send contacts sync message with error: %@.", error); - resolve(error); - }]; - }]; - [promise retainUntilComplete]; - return promise; -} - -- (AnyPromise *)syncAllGroups -{ - return [LKSyncMessagesProtocol syncAllClosedGroups]; -} - -- (AnyPromise *)syncGroupForThread:(TSGroupThread *)thread -{ - if (thread.usesSharedSenderKeys) { - __block AnyPromise *promise; - [LKStorage writeSyncWithBlock:^(YapDatabaseReadWriteTransaction *transaction) { - promise = [LKSyncMessagesProtocol syncClosedGroup:thread transaction:transaction]; - }]; - return promise; - } else { - OWSSyncGroupsMessage *syncGroupsMessage = [[OWSSyncGroupsMessage alloc] initWithGroupThread:thread]; - AnyPromise *promise = [AnyPromise promiseWithResolverBlock:^(PMKResolver resolve) { - [self.messageSender sendMessage:syncGroupsMessage - success:^{ - OWSLogInfo(@"Successfully sent group sync message."); - resolve(@(1)); - } - failure:^(NSError *error) { - OWSLogError(@"Failed to send group sync message due to error: %@.", error); - resolve(error); - }]; - }]; - [promise retainUntilComplete]; - return promise; - } -} - -- (AnyPromise *)syncAllOpenGroups -{ - return [LKSyncMessagesProtocol syncAllOpenGroups]; -} - -@end - -NS_ASSUME_NONNULL_END diff --git a/SignalUtilitiesKit/OWSUDManager.swift b/SignalUtilitiesKit/OWSUDManager.swift index 7d2138f75..8dc897062 100644 --- a/SignalUtilitiesKit/OWSUDManager.swift +++ b/SignalUtilitiesKit/OWSUDManager.swift @@ -77,13 +77,13 @@ public class OWSUDAccess: NSObject { func setUnidentifiedAccessMode(_ mode: UnidentifiedAccessMode, recipientId: String) @objc - func unidentifiedAccessMode(forRecipientId recipientId: RecipientIdentifier) -> UnidentifiedAccessMode + func unidentifiedAccessMode(forRecipientId recipientId: String) -> UnidentifiedAccessMode @objc - func udAccessKey(forRecipientId recipientId: RecipientIdentifier) -> SMKUDAccessKey? + func udAccessKey(forRecipientId recipientId: String) -> SMKUDAccessKey? @objc - func udAccess(forRecipientId recipientId: RecipientIdentifier, + func udAccess(forRecipientId recipientId: String, requireSyncAccess: Bool) -> OWSUDAccess? // MARK: Sender Certificate @@ -203,7 +203,7 @@ public class OWSUDManagerImpl: NSObject, OWSUDManager { return SMKUDAccessKey(randomKeyData: ()) } - private func unidentifiedAccessMode(forRecipientId recipientId: RecipientIdentifier, + private func unidentifiedAccessMode(forRecipientId recipientId: String, isLocalNumber: Bool, transaction: YapDatabaseReadTransaction) -> UnidentifiedAccessMode { let defaultValue: UnidentifiedAccessMode = isLocalNumber ? .enabled : .unknown @@ -218,7 +218,7 @@ public class OWSUDManagerImpl: NSObject, OWSUDManager { } @objc - public func unidentifiedAccessMode(forRecipientId recipientId: RecipientIdentifier) -> UnidentifiedAccessMode { + public func unidentifiedAccessMode(forRecipientId recipientId: String) -> UnidentifiedAccessMode { var isLocalNumber = false if let localNumber = tsAccountManager.localNumber() { isLocalNumber = recipientId == localNumber @@ -255,7 +255,7 @@ public class OWSUDManagerImpl: NSObject, OWSUDManager { // Returns the UD access key for a given recipient // if we have a valid profile key for them. @objc - public func udAccessKey(forRecipientId recipientId: RecipientIdentifier) -> SMKUDAccessKey? { + public func udAccessKey(forRecipientId recipientId: String) -> SMKUDAccessKey? { guard let profileKey = profileManager.profileKeyData(forRecipientId: recipientId) else { // Mark as "not a UD recipient". return nil @@ -271,7 +271,7 @@ public class OWSUDManagerImpl: NSObject, OWSUDManager { // Returns the UD access key for sending to a given recipient. @objc - public func udAccess(forRecipientId recipientId: RecipientIdentifier, + public func udAccess(forRecipientId recipientId: String, requireSyncAccess: Bool) -> OWSUDAccess? { if requireSyncAccess { guard let localNumber = tsAccountManager.localNumber() else { @@ -427,7 +427,7 @@ public class OWSUDManagerImpl: NSObject, OWSUDManager { return Promise<(certificateData: Data, certificate: SMKSenderCertificate)> { seal in // Loki: Generate a sender certificate locally let sender = OWSIdentityManager.shared().identityKeyPair()!.hexEncodedPublicKey - let certificate = SMKSenderCertificate(senderDeviceId: OWSDevicePrimaryDeviceId, senderRecipientId: sender) + let certificate = SMKSenderCertificate(senderDeviceId: 1, senderRecipientId: sender) let certificateAsData = try certificate.serialized() guard isValidCertificate(certificate) else { throw OWSUDError.invalidData(description: "Invalid sender certificate.") @@ -440,7 +440,7 @@ public class OWSUDManagerImpl: NSObject, OWSUDManager { public func getSenderCertificate() -> SMKSenderCertificate? { do { let sender = OWSIdentityManager.shared().identityKeyPair()!.hexEncodedPublicKey - let certificate = SMKSenderCertificate(senderDeviceId: OWSDevicePrimaryDeviceId, senderRecipientId: sender) + let certificate = SMKSenderCertificate(senderDeviceId: 1, senderRecipientId: sender) guard self.isValidCertificate(certificate) else { throw OWSUDError.invalidData(description: "Invalid sender certificate returned by server") } @@ -451,20 +451,6 @@ public class OWSUDManagerImpl: NSObject, OWSUDManager { } } - private func requestSenderCertificate() -> Promise<(certificateData: Data, certificate: SMKSenderCertificate)> { - return firstly { - SignalServiceRestClient().requestUDSenderCertificate() - }.map { certificateData -> (certificateData: Data, certificate: SMKSenderCertificate) in - let certificate = try SMKSenderCertificate.parse(data: certificateData) - - guard self.isValidCertificate(certificate) else { - throw OWSUDError.invalidData(description: "Invalid sender certificate returned by server") - } - - return (certificateData: certificateData, certificate: certificate) - } - } - private func isValidCertificate(_ certificate: SMKSenderCertificate) -> Bool { // Ensure that the certificate will not expire in the next hour. // We want a threshold long enough to ensure that any outgoing message diff --git a/SignalUtilitiesKit/OWSUnknownContactBlockOfferMessage.h b/SignalUtilitiesKit/OWSUnknownContactBlockOfferMessage.h deleted file mode 100644 index 2af96d497..000000000 --- a/SignalUtilitiesKit/OWSUnknownContactBlockOfferMessage.h +++ /dev/null @@ -1,17 +0,0 @@ -// -// Copyright (c) 2018 Open Whisper Systems. All rights reserved. -// - -#import - -NS_ASSUME_NONNULL_BEGIN - -// This is a deprecated class, we're keeping it around to avoid YapDB serialization errors -// TODO - remove this class, clean up existing instances, ensure any missed ones don't explode (UnknownDBObject) -__attribute__((deprecated)) @interface OWSUnknownContactBlockOfferMessage : TSErrorMessage - -@property (nonatomic, readonly) NSString *contactId; - -@end - -NS_ASSUME_NONNULL_END diff --git a/SignalUtilitiesKit/OWSUnknownContactBlockOfferMessage.m b/SignalUtilitiesKit/OWSUnknownContactBlockOfferMessage.m deleted file mode 100644 index 3d11291cf..000000000 --- a/SignalUtilitiesKit/OWSUnknownContactBlockOfferMessage.m +++ /dev/null @@ -1,38 +0,0 @@ -// -// Copyright (c) 2018 Open Whisper Systems. All rights reserved. -// - -#import "OWSUnknownContactBlockOfferMessage.h" - -NS_ASSUME_NONNULL_BEGIN - -@interface OWSUnknownContactBlockOfferMessage () - -@property (nonatomic) NSString *contactId; - -@end - -#pragma mark - - -// This is a deprecated class, we're keeping it around to avoid YapDB serialization errors -// TODO - remove this class, clean up existing instances, ensure any missed ones don't explode (UnknownDBObject) -#pragma clang diagnostic push -#pragma clang diagnostic ignored "-Wdeprecated-implementations" -@implementation OWSUnknownContactBlockOfferMessage -#pragma clang diagnostic pop - -- (BOOL)shouldUseReceiptDateForSorting -{ - // Use the timestamp, not the "received at" timestamp to sort, - // since we're creating these interactions after the fact and back-dating them. - return NO; -} - -- (BOOL)isDynamicInteraction -{ - return YES; -} - -@end - -NS_ASSUME_NONNULL_END diff --git a/SignalUtilitiesKit/OWSUploadOperation.m b/SignalUtilitiesKit/OWSUploadOperation.m index d7063c66f..56267ccde 100644 --- a/SignalUtilitiesKit/OWSUploadOperation.m +++ b/SignalUtilitiesKit/OWSUploadOperation.m @@ -9,10 +9,10 @@ #import "OWSDispatch.h" #import "OWSError.h" #import "OWSOperation.h" -#import "OWSRequestFactory.h" +#import #import "SSKEnvironment.h" #import "TSAttachmentStream.h" -#import "TSNetworkManager.h" + #import #import #import @@ -57,11 +57,6 @@ static const CGFloat kAttachmentUploadProgressTheta = 0.001f; return self; } -- (TSNetworkManager *)networkManager -{ - return SSKEnvironment.shared.networkManager; -} - - (void)run { __block TSAttachmentStream *attachmentStream; @@ -100,79 +95,6 @@ static const CGFloat kAttachmentUploadProgressTheta = 0.001f; }) retainUntilComplete]; } -- (void)uploadWithServerId:(UInt64)serverId - location:(NSString *)location - attachmentStream:(TSAttachmentStream *)attachmentStream -{ - OWSLogDebug(@"started uploading data for attachment: %@", self.attachmentId); - NSError *error; - NSData *attachmentData = [attachmentStream readDataFromFileAndReturnError:&error]; - if (error) { - OWSLogError(@"Failed to read attachment data with error: %@", error); - error.isRetryable = YES; - [self reportError:error]; - return; - } - - NSData *encryptionKey; - NSData *digest; - NSData *_Nullable encryptedAttachmentData = - [Cryptography encryptAttachmentData:attachmentData shouldPad:YES outKey:&encryptionKey outDigest:&digest]; - if (!encryptedAttachmentData) { - OWSFailDebug(@"could not encrypt attachment data."); - error = OWSErrorMakeFailedToSendOutgoingMessageError(); - error.isRetryable = YES; - [self reportError:error]; - return; - } - attachmentStream.encryptionKey = encryptionKey; - attachmentStream.digest = digest; - - NSMutableURLRequest *request = [[NSMutableURLRequest alloc] initWithURL:[NSURL URLWithString:location]]; - request.HTTPMethod = @"PUT"; - [request setValue:OWSMimeTypeApplicationOctetStream forHTTPHeaderField:@"Content-Type"]; - - AFURLSessionManager *manager = [[AFURLSessionManager alloc] - initWithSessionConfiguration:[NSURLSessionConfiguration defaultSessionConfiguration]]; - - NSURLSessionUploadTask *uploadTask; - uploadTask = [manager uploadTaskWithRequest:request - fromData:encryptedAttachmentData - progress:^(NSProgress *_Nonnull uploadProgress) { - [self fireNotificationWithProgress:uploadProgress.fractionCompleted]; - } - completionHandler:^(NSURLResponse *_Nonnull response, id _Nullable responseObject, NSError *_Nullable error) { - OWSAssertIsOnMainThread(); - if (error) { - error.isRetryable = YES; - [self reportError:error]; - return; - } - - NSInteger statusCode = ((NSHTTPURLResponse *)response).statusCode; - BOOL isValidResponse = (statusCode >= 200) && (statusCode < 400); - if (!isValidResponse) { - OWSLogError(@"Unexpected server response: %d", (int)statusCode); - NSError *invalidResponseError = OWSErrorMakeUnableToProcessServerResponseError(); - invalidResponseError.isRetryable = YES; - [self reportError:invalidResponseError]; - return; - } - - OWSLogInfo(@"Uploaded attachment: %p serverId: %llu, byteCount: %u", - attachmentStream.uniqueId, - attachmentStream.serverId, - attachmentStream.byteCount); - attachmentStream.serverId = serverId; - attachmentStream.isUploaded = YES; - [attachmentStream saveAsyncWithCompletionBlock:^{ - [self reportSuccess]; - }]; - }]; - - [uploadTask resume]; -} - - (void)fireNotificationWithProgress:(CGFloat)aProgress { NSNotificationCenter *notificationCenter = [NSNotificationCenter defaultCenter]; diff --git a/SignalUtilitiesKit/OWSVerificationStateChangeMessage.h b/SignalUtilitiesKit/OWSVerificationStateChangeMessage.h deleted file mode 100644 index c3330aed8..000000000 --- a/SignalUtilitiesKit/OWSVerificationStateChangeMessage.h +++ /dev/null @@ -1,26 +0,0 @@ -// -// Copyright (c) 2018 Open Whisper Systems. All rights reserved. -// - -#import -#import - -NS_ASSUME_NONNULL_BEGIN - -@class TSThread; - -@interface OWSVerificationStateChangeMessage : TSInfoMessage - -@property (nonatomic, readonly) NSString *recipientId; -@property (nonatomic, readonly) OWSVerificationState verificationState; -@property (nonatomic, readonly) BOOL isLocalChange; - -- (instancetype)initWithTimestamp:(uint64_t)timestamp - thread:(TSThread *)thread - recipientId:(NSString *)recipientId - verificationState:(OWSVerificationState)verificationState - isLocalChange:(BOOL)isLocalChange; - -@end - -NS_ASSUME_NONNULL_END diff --git a/SignalUtilitiesKit/OWSVerificationStateChangeMessage.m b/SignalUtilitiesKit/OWSVerificationStateChangeMessage.m deleted file mode 100644 index 8f4402281..000000000 --- a/SignalUtilitiesKit/OWSVerificationStateChangeMessage.m +++ /dev/null @@ -1,35 +0,0 @@ -// -// Copyright (c) 2018 Open Whisper Systems. All rights reserved. -// - -#import "OWSVerificationStateChangeMessage.h" -#import "OWSDisappearingMessagesConfiguration.h" -#import - -NS_ASSUME_NONNULL_BEGIN - -@implementation OWSVerificationStateChangeMessage - -- (instancetype)initWithTimestamp:(uint64_t)timestamp - thread:(TSThread *)thread - recipientId:(NSString *)recipientId - verificationState:(OWSVerificationState)verificationState - isLocalChange:(BOOL)isLocalChange -{ - OWSAssertDebug(recipientId.length > 0); - - self = [super initWithTimestamp:timestamp inThread:thread messageType:TSInfoMessageVerificationStateChange]; - if (!self) { - return self; - } - - _recipientId = recipientId; - _verificationState = verificationState; - _isLocalChange = isLocalChange; - - return self; -} - -@end - -NS_ASSUME_NONNULL_END diff --git a/SignalUtilitiesKit/OWSVerificationStateSyncMessage.h b/SignalUtilitiesKit/OWSVerificationStateSyncMessage.h deleted file mode 100644 index c381eaebc..000000000 --- a/SignalUtilitiesKit/OWSVerificationStateSyncMessage.h +++ /dev/null @@ -1,27 +0,0 @@ -// -// Copyright (c) 2018 Open Whisper Systems. All rights reserved. -// - -#import -#import - -NS_ASSUME_NONNULL_BEGIN - -@interface OWSVerificationStateSyncMessage : OWSOutgoingSyncMessage - -- (instancetype)init NS_UNAVAILABLE; - -- (instancetype)initWithVerificationState:(OWSVerificationState)verificationState - identityKey:(NSData *)identityKey - verificationForRecipientId:(NSString *)recipientId NS_DESIGNATED_INITIALIZER; -- (nullable instancetype)initWithCoder:(NSCoder *)coder NS_DESIGNATED_INITIALIZER; - -// This is a clunky name, but we want to differentiate it from `recipientIdentifier` inherited from `TSOutgoingMessage` -@property (nonatomic, readonly) NSString *verificationForRecipientId; - -@property (nonatomic, readonly) size_t paddingBytesLength; -@property (nonatomic, readonly) size_t unpaddedVerifiedLength; - -@end - -NS_ASSUME_NONNULL_END diff --git a/SignalUtilitiesKit/OWSVerificationStateSyncMessage.m b/SignalUtilitiesKit/OWSVerificationStateSyncMessage.m deleted file mode 100644 index db224c086..000000000 --- a/SignalUtilitiesKit/OWSVerificationStateSyncMessage.m +++ /dev/null @@ -1,111 +0,0 @@ -// -// Copyright (c) 2018 Open Whisper Systems. All rights reserved. -// - -#import "OWSVerificationStateSyncMessage.h" -#import "OWSIdentityManager.h" -#import -#import - -NS_ASSUME_NONNULL_BEGIN - -#pragma mark - - -@interface OWSVerificationStateSyncMessage () - -@property (nonatomic, readonly) OWSVerificationState verificationState; -@property (nonatomic, readonly) NSData *identityKey; - -@end - -#pragma mark - - -@implementation OWSVerificationStateSyncMessage - -- (instancetype)initWithVerificationState:(OWSVerificationState)verificationState - identityKey:(NSData *)identityKey - verificationForRecipientId:(NSString *)verificationForRecipientId -{ - OWSAssertDebug(identityKey.length == kIdentityKeyLength); - OWSAssertDebug(verificationForRecipientId.length > 0); - - // we only sync user's marking as un/verified. Never sync the conflicted state, the sibling device - // will figure that out on it's own. - OWSAssertDebug(verificationState != OWSVerificationStateNoLongerVerified); - - self = [super init]; - if (!self) { - return self; - } - - _verificationState = verificationState; - _identityKey = identityKey; - _verificationForRecipientId = verificationForRecipientId; - - // This sync message should be 1-512 bytes longer than the corresponding NullMessage - // we store this values so the corresponding NullMessage can subtract it from the total length. - _paddingBytesLength = arc4random_uniform(512) + 1; - - return self; -} - -- (nullable instancetype)initWithCoder:(NSCoder *)coder -{ - return [super initWithCoder:coder]; -} - -- (nullable SSKProtoSyncMessageBuilder *)syncMessageBuilder -{ - OWSAssertDebug(self.identityKey.length == kIdentityKeyLength); - OWSAssertDebug(self.verificationForRecipientId.length > 0); - - // we only sync user's marking as un/verified. Never sync the conflicted state, the sibling device - // will figure that out on it's own. - OWSAssertDebug(self.verificationState != OWSVerificationStateNoLongerVerified); - - // We add the same amount of padding in the VerificationStateSync message and it's coresponding NullMessage so that - // the sync message is indistinguishable from an outgoing Sent transcript corresponding to the NullMessage. We pad - // the NullMessage so as to obscure it's content. The sync message (like all sync messages) will be *additionally* - // padded by the superclass while being sent. The end result is we send a NullMessage of a non-distinct size, and a - // verification sync which is ~1-512 bytes larger then that. - OWSAssertDebug(self.paddingBytesLength != 0); - - SSKProtoVerified *_Nullable verifiedProto = BuildVerifiedProtoWithRecipientId( - self.verificationForRecipientId, self.identityKey, self.verificationState, self.paddingBytesLength); - if (!verifiedProto) { - OWSFailDebug(@"could not build protobuf."); - return nil; - } - - SSKProtoSyncMessageBuilder *syncMessageBuilder = [SSKProtoSyncMessage builder]; - [syncMessageBuilder setVerified:verifiedProto]; - return syncMessageBuilder; -} - -- (size_t)unpaddedVerifiedLength -{ - OWSAssertDebug(self.identityKey.length == kIdentityKeyLength); - OWSAssertDebug(self.verificationForRecipientId.length > 0); - - // we only sync user's marking as un/verified. Never sync the conflicted state, the sibling device - // will figure that out on it's own. - OWSAssertDebug(self.verificationState != OWSVerificationStateNoLongerVerified); - - SSKProtoVerified *_Nullable verifiedProto = BuildVerifiedProtoWithRecipientId( - self.verificationForRecipientId, self.identityKey, self.verificationState, 0); - if (!verifiedProto) { - OWSFailDebug(@"could not build protobuf."); - return 0; - } - NSError *error; - NSData *_Nullable verifiedData = [verifiedProto serializedDataAndReturnError:&error]; - if (error || !verifiedData) { - OWSFailDebug(@"could not serialize protobuf."); - return 0; - } - return verifiedData.length; -} - -@end - -NS_ASSUME_NONNULL_END diff --git a/SignalUtilitiesKit/OWSWebSocket.h b/SignalUtilitiesKit/OWSWebSocket.h deleted file mode 100644 index 062bf0364..000000000 --- a/SignalUtilitiesKit/OWSWebSocket.h +++ /dev/null @@ -1,55 +0,0 @@ -// -// Copyright (c) 2018 Open Whisper Systems. All rights reserved. -// - -#import - -NS_ASSUME_NONNULL_BEGIN - -static void *OWSWebSocketStateObservationContext = &OWSWebSocketStateObservationContext; - -extern NSString *const kNSNotification_OWSWebSocketStateDidChange; - -typedef NS_ENUM(NSUInteger, OWSWebSocketState) { - OWSWebSocketStateClosed, - OWSWebSocketStateConnecting, - OWSWebSocketStateOpen, -}; - -typedef void (^TSSocketMessageSuccess)(id _Nullable responseObject); -// statusCode is zero by default, if request never made or failed. -typedef void (^TSSocketMessageFailure)(NSInteger statusCode, NSData *_Nullable responseData, NSError *error); - -@class TSRequest; - -@interface OWSWebSocket : NSObject - -@property (nonatomic, readonly) OWSWebSocketState state; - -- (instancetype)init NS_DESIGNATED_INITIALIZER; - -// If the app is in the foreground, we'll try to open the socket unless it's already -// open or connecting. -// -// If the app is in the background, we'll try to open the socket unless it's already -// open or connecting _and_ keep it open for at least N seconds. -// If the app is in the background and the socket is already open or connecting this -// might prolong how long we keep the socket open. -// -// This method can be called from any thread. -- (void)requestSocketOpen; - -// This can be used to force the socket to close and re-open, if it is open. -- (void)cycleSocket; - -#pragma mark - Message Sending - -@property (atomic, readonly) BOOL canMakeRequests; - -- (void)makeRequest:(TSRequest *)request - success:(TSSocketMessageSuccess)success - failure:(TSSocketMessageFailure)failure; - -@end - -NS_ASSUME_NONNULL_END diff --git a/SignalUtilitiesKit/OWSWebSocket.m b/SignalUtilitiesKit/OWSWebSocket.m deleted file mode 100644 index d7bea5bca..000000000 --- a/SignalUtilitiesKit/OWSWebSocket.m +++ /dev/null @@ -1,1141 +0,0 @@ -// -// Copyright (c) 2019 Open Whisper Systems. All rights reserved. -// - -#import "OWSWebSocket.h" -#import "AppContext.h" -#import "AppReadiness.h" -#import "NSNotificationCenter+OWS.h" -#import "NSTimer+OWS.h" -#import "NotificationsProtocol.h" -#import "OWSBackgroundTask.h" -#import "OWSDevicesService.h" -#import "OWSError.h" -#import "OWSMessageManager.h" -#import "OWSMessageReceiver.h" -#import "OWSPrimaryStorage.h" -#import "OWSSignalService.h" -#import "SSKEnvironment.h" -#import "TSAccountManager.h" -#import "TSConstants.h" -#import "TSErrorMessage.h" -#import "TSRequest.h" -#import -#import -#import - -NS_ASSUME_NONNULL_BEGIN - -static const CGFloat kSocketHeartbeatPeriodSeconds = 30.f; -static const CGFloat kSocketReconnectDelaySeconds = 5.f; - -// If the app is in the background, it should keep the -// websocket open if: -// -// a) It has received a notification in the last 25 seconds. -static const CGFloat kBackgroundOpenSocketDurationSeconds = 25.f; -// b) It has received a message over the socket in the last 15 seconds. -static const CGFloat kBackgroundKeepSocketAliveDurationSeconds = 15.f; -// c) It is in the process of making a request. -static const CGFloat kMakeRequestKeepSocketAliveDurationSeconds = 30.f; - -NSString *const kNSNotification_OWSWebSocketStateDidChange = @"kNSNotification_OWSWebSocketStateDidChange"; - -@interface TSSocketMessage : NSObject - -@property (nonatomic, readonly) UInt64 requestId; -@property (nonatomic, nullable) TSSocketMessageSuccess success; -@property (nonatomic, nullable) TSSocketMessageFailure failure; -@property (nonatomic) BOOL hasCompleted; -@property (nonatomic, readonly) OWSBackgroundTask *backgroundTask; - -- (instancetype)init NS_UNAVAILABLE; - -@end - -#pragma mark - - -@implementation TSSocketMessage - -- (instancetype)initWithRequestId:(UInt64)requestId - success:(TSSocketMessageSuccess)success - failure:(TSSocketMessageFailure)failure -{ - if (self = [super init]) { - OWSAssertDebug(success); - OWSAssertDebug(failure); - - _requestId = requestId; - _success = success; - _failure = failure; - _backgroundTask = [OWSBackgroundTask backgroundTaskWithLabelStr:__PRETTY_FUNCTION__]; - } - - return self; -} - -- (void)didSucceedWithResponseObject:(id _Nullable)responseObject -{ - @synchronized(self) { - if (self.hasCompleted) { - return; - } - self.hasCompleted = YES; - } - - OWSAssertDebug(self.success); - OWSAssertDebug(self.failure); - - TSSocketMessageSuccess success = self.success; - dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ - success(responseObject); - }); - - self.success = nil; - self.failure = nil; -} - -- (void)timeoutIfNecessary -{ - NSError *error = OWSErrorWithCodeDescription(OWSErrorCodeMessageRequestFailed, - NSLocalizedString( - @"ERROR_DESCRIPTION_REQUEST_TIMED_OUT", @"Error indicating that a socket request timed out.")); - - [self didFailWithStatusCode:0 responseData:nil error:error]; -} - -- (void)didFailBeforeSending -{ - NSError *error = OWSErrorWithCodeDescription(OWSErrorCodeMessageRequestFailed, - NSLocalizedString(@"ERROR_DESCRIPTION_REQUEST_FAILED", @"Error indicating that a socket request failed.")); - - [self didFailWithStatusCode:0 responseData:nil error:error]; -} - -- (void)didFailWithStatusCode:(NSInteger)statusCode responseData:(nullable NSData *)responseData error:(NSError *)error -{ - OWSAssertDebug(error); - - @synchronized(self) { - if (self.hasCompleted) { - return; - } - self.hasCompleted = YES; - } - - OWSLogError(@"didFailWithStatusCode: %zd, %@", statusCode, error); - - OWSAssertDebug(self.success); - OWSAssertDebug(self.failure); - - TSSocketMessageFailure failure = self.failure; - dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ - failure(statusCode, responseData, error); - }); - - self.success = nil; - self.failure = nil; -} - -@end - -#pragma mark - - -// OWSWebSocket's properties should only be accessed from the main thread. -@interface OWSWebSocket () - -// This class has a few "tiers" of state. -// -// The first tier is the actual websocket and the timers used -// to keep it alive and connected. -@property (nonatomic, nullable) id websocket; -@property (nonatomic, nullable) NSTimer *heartbeatTimer; -@property (nonatomic, nullable) NSTimer *reconnectTimer; - -#pragma mark - - -// The second tier is the state property. We initiate changes -// to the websocket by changing this property's value, and delegate -// events from the websocket also update this value as the websocket's -// state changes. -// -// Due to concurrency, this property can fall out of sync with the -// websocket's actual state, so we're defensive and distrustful of -// this property. -// -// We only ever access this state on the main thread. -@property (nonatomic) OWSWebSocketState state; - -#pragma mark - - -// The third tier is the state that is used to determine what the -// "desired" state of the websocket is. -// -// If we're keeping the socket open in the background, all three of these -// properties will be set. Otherwise (if the app is active or if we're not -// trying to keep the socket open), all three should be clear. -// -// This represents how long we're trying to keep the socket open. -@property (nonatomic, nullable) NSDate *backgroundKeepAliveUntilDate; -// This timer is used to check periodically whether we should -// close the socket. -@property (nonatomic, nullable) NSTimer *backgroundKeepAliveTimer; -// This is used to manage the iOS "background task" used to -// keep the app alive in the background. -@property (nonatomic, nullable) OWSBackgroundTask *backgroundTask; - -// We cache this value instead of consulting [UIApplication sharedApplication].applicationState, -// because UIKit only provides a "will resign active" notification, not a "did resign active" -// notification. -@property (nonatomic) BOOL appIsActive; - -@property (nonatomic) BOOL hasObservedNotifications; - -// This property should only be accessed while synchronized on the socket manager. -@property (nonatomic, readonly) NSMutableDictionary *socketMessageMap; - -@property (atomic) BOOL canMakeRequests; - -@end - -#pragma mark - - -@implementation OWSWebSocket - -- (instancetype)init -{ - self = [super init]; - - if (!self) { - return self; - } - - OWSAssertIsOnMainThread(); - - _state = OWSWebSocketStateClosed; - _socketMessageMap = [NSMutableDictionary new]; - - return self; -} - -- (void)dealloc -{ - [[NSNotificationCenter defaultCenter] removeObserver:self]; -} - -#pragma mark - Dependencies - -- (OWSSignalService *)signalService -{ - return [OWSSignalService sharedInstance]; -} - -- (OWSMessageReceiver *)messageReceiver -{ - return SSKEnvironment.shared.messageReceiver; -} - -- (TSAccountManager *)tsAccountManager -{ - return TSAccountManager.sharedInstance; -} - -- (OutageDetection *)outageDetection -{ - return OutageDetection.sharedManager; -} - -- (OWSPrimaryStorage *)primaryStorage -{ - return SSKEnvironment.shared.primaryStorage; -} - -- (id)notificationsManager -{ - return SSKEnvironment.shared.notificationsManager; -} - -- (id)udManager { - return SSKEnvironment.shared.udManager; -} - -#pragma mark - - -// We want to observe these notifications lazily to avoid accessing -// the data store in [application: didFinishLaunchingWithOptions:]. -- (void)observeNotificationsIfNecessary -{ - if (self.hasObservedNotifications) { - return; - } - self.hasObservedNotifications = YES; - - self.appIsActive = CurrentAppContext().isMainAppAndActive; - - [[NSNotificationCenter defaultCenter] addObserver:self - selector:@selector(applicationDidBecomeActive:) - name:OWSApplicationDidBecomeActiveNotification - object:nil]; - [[NSNotificationCenter defaultCenter] addObserver:self - selector:@selector(applicationWillResignActive:) - name:OWSApplicationWillResignActiveNotification - object:nil]; - [[NSNotificationCenter defaultCenter] addObserver:self - selector:@selector(registrationStateDidChange:) - name:RegistrationStateDidChangeNotification - object:nil]; - [[NSNotificationCenter defaultCenter] addObserver:self - selector:@selector(isCensorshipCircumventionActiveDidChange:) - name:kNSNotificationName_IsCensorshipCircumventionActiveDidChange - object:nil]; - [[NSNotificationCenter defaultCenter] addObserver:self - selector:@selector(deviceListUpdateModifiedDeviceList:) - name:NSNotificationName_DeviceListUpdateModifiedDeviceList - object:nil]; -} - -#pragma mark - Manage Socket - -- (void)ensureWebsocketIsOpen -{ - OWSAssertIsOnMainThread(); - OWSAssertDebug(!self.signalService.isCensorshipCircumventionActive); - - // Try to reuse the existing socket (if any) if it is in a valid state. - if (self.websocket) { - switch (self.websocket.state) { - case SSKWebSocketStateOpen: - self.state = OWSWebSocketStateOpen; - return; - case SSKWebSocketStateConnecting: - OWSLogVerbose(@"WebSocket is already connecting"); - self.state = OWSWebSocketStateConnecting; - return; - default: - break; - } - } - - OWSLogWarn(@"Creating new websocket"); - - // If socket is not already open or connecting, connect now. - // - // First we need to close the existing websocket, if any. - // The websocket delegate methods are invoked _after_ the websocket - // state changes, so we may be just learning about a socket failure - // or close event now. - self.state = OWSWebSocketStateClosed; - // Now open a new socket. - self.state = OWSWebSocketStateConnecting; -} - -- (NSString *)stringFromOWSWebSocketState:(OWSWebSocketState)state -{ - switch (state) { - case OWSWebSocketStateClosed: - return @"Closed"; - case OWSWebSocketStateOpen: - return @"Open"; - case OWSWebSocketStateConnecting: - return @"Connecting"; - } -} - -// We need to keep websocket state and class state tightly aligned. -// -// Sometimes we'll need to update class state to reflect changes -// in socket state; sometimes we'll need to update socket state -// and class state to reflect changes in app state. -// -// We learn about changes to socket state through websocket -// delegate methods. These delegate methods are sometimes -// invoked _after_ web socket state changes, so we sometimes learn -// about changes to socket state in [ensureWebsocket]. Put another way, -// it's not safe to assume we'll learn of changes to websocket state -// in the websocket delegate methods. -// -// Therefore, we use the [setState:] setter to ensure alignment between -// websocket state and class state. -- (void)setState:(OWSWebSocketState)state -{ - OWSAssertIsOnMainThread(); - - // If this state update is redundant, verify that - // class state and socket state are aligned. - // - // Note: it's not safe to check the socket's readyState here as - // it may have been just updated on another thread. If so, - // we'll learn of that state change soon. - if (_state == state) { - switch (state) { - case OWSWebSocketStateClosed: - OWSAssertDebug(!self.websocket); - break; - case OWSWebSocketStateOpen: - OWSAssertDebug(self.websocket); - break; - case OWSWebSocketStateConnecting: - OWSAssertDebug(self.websocket); - break; - } - return; - } - - OWSLogWarn( - @"Socket state: %@ -> %@", [self stringFromOWSWebSocketState:_state], [self stringFromOWSWebSocketState:state]); - - // If this state update is _not_ redundant, - // update class state to reflect the new state. - switch (state) { - case OWSWebSocketStateClosed: { - [self resetSocket]; - break; - } - case OWSWebSocketStateOpen: { - OWSAssertDebug(self.state == OWSWebSocketStateConnecting); - - self.heartbeatTimer = [NSTimer timerWithTimeInterval:kSocketHeartbeatPeriodSeconds - target:self - selector:@selector(webSocketHeartBeat) - userInfo:nil - repeats:YES]; - - // Additionally, we want the ping timer to work in the background too. - [[NSRunLoop mainRunLoop] addTimer:self.heartbeatTimer forMode:NSDefaultRunLoopMode]; - - // If the socket is open, we don't need to worry about reconnecting. - [self clearReconnect]; - break; - } - case OWSWebSocketStateConnecting: { - // Discard the old socket which is already closed or is closing. - [self resetSocket]; - - // Create a new web socket. - NSString *webSocketConnect = - [textSecureWebSocketAPI stringByAppendingString:[self webSocketAuthenticationString]]; - NSURL *webSocketConnectURL = [NSURL URLWithString:webSocketConnect]; - NSMutableURLRequest *request = [[NSMutableURLRequest alloc] initWithURL:webSocketConnectURL]; - - id socket = [SSKWebSocketManager buildSocketWithRequest:request]; - socket.delegate = self; - - [self setWebsocket:socket]; - - // `connect` could hypothetically call a delegate method (e.g. if - // the socket failed immediately for some reason), so we update the state - // _before_ calling it, not after. - _state = state; - self.canMakeRequests = state == OWSWebSocketStateOpen; - [socket connect]; - [self failAllPendingSocketMessagesIfNecessary]; - return; - } - } - - _state = state; - self.canMakeRequests = state == OWSWebSocketStateOpen; - - [self failAllPendingSocketMessagesIfNecessary]; - [self notifyStatusChange]; -} - -- (void)notifyStatusChange -{ - [[NSNotificationCenter defaultCenter] postNotificationNameAsync:kNSNotification_OWSWebSocketStateDidChange - object:nil - userInfo:nil]; -} - -#pragma mark - - -- (void)resetSocket -{ - OWSAssertIsOnMainThread(); - - self.websocket.delegate = nil; - [self.websocket disconnect]; - self.websocket = nil; - [self.heartbeatTimer invalidate]; - self.heartbeatTimer = nil; -} - -- (void)closeWebSocket -{ - OWSAssertIsOnMainThread(); - - if (self.websocket) { - OWSLogWarn(@"closeWebSocket."); - } - - self.state = OWSWebSocketStateClosed; -} - -#pragma mark - Message Sending - -- (void)makeRequest:(TSRequest *)request success:(TSSocketMessageSuccess)success failure:(TSSocketMessageFailure)failure -{ - OWSAssertDebug(request); - OWSAssertDebug(request.HTTPMethod.length > 0); - OWSAssertDebug(success); - OWSAssertDebug(failure); - - TSSocketMessage *socketMessage = [[TSSocketMessage alloc] initWithRequestId:[Cryptography randomUInt64] - success:success - failure:failure]; - - @synchronized(self) { - self.socketMessageMap[@(socketMessage.requestId)] = socketMessage; - } - - NSURL *requestUrl = request.URL; - NSString *requestPath = [@"/" stringByAppendingString:requestUrl.path]; - - NSData *_Nullable jsonData = nil; - if (request.parameters) { - NSError *error; - jsonData = - [NSJSONSerialization dataWithJSONObject:request.parameters options:(NSJSONWritingOptions)0 error:&error]; - if (!jsonData || error) { - OWSFailDebug(@"could not serialize request JSON: %@", error); - [socketMessage didFailBeforeSending]; - return; - } - } - - WebSocketProtoWebSocketRequestMessageBuilder *requestBuilder = - [WebSocketProtoWebSocketRequestMessage builderWithVerb:request.HTTPMethod - path:requestPath - requestID:socketMessage.requestId]; - if (jsonData) { - // TODO: Do we need body & headers for requests with no parameters? - [requestBuilder setBody:jsonData]; - [requestBuilder addHeaders:@"content-type:application/json"]; - } - - for (NSString *headerField in request.allHTTPHeaderFields) { - NSString *headerValue = request.allHTTPHeaderFields[headerField]; - - OWSAssertDebug([headerField isKindOfClass:[NSString class]]); - OWSAssertDebug([headerValue isKindOfClass:[NSString class]]); - [requestBuilder addHeaders:[NSString stringWithFormat:@"%@:%@", headerField, headerValue]]; - } - - NSError *error; - WebSocketProtoWebSocketRequestMessage *_Nullable requestProto = [requestBuilder buildAndReturnError:&error]; - if (!requestProto || error) { - OWSFailDebug(@"could not build proto: %@", error); - return; - } - - WebSocketProtoWebSocketMessageBuilder *messageBuilder = - [WebSocketProtoWebSocketMessage builderWithType:WebSocketProtoWebSocketMessageTypeRequest]; - [messageBuilder setRequest:requestProto]; - - NSData *_Nullable messageData = [messageBuilder buildSerializedDataAndReturnError:&error]; - if (!messageData || error) { - OWSFailDebug(@"could not serialize proto: %@.", error); - [socketMessage didFailBeforeSending]; - return; - } - - if (!self.canMakeRequests) { - OWSLogError(@"makeRequest: socket not open."); - [socketMessage didFailBeforeSending]; - return; - } - - BOOL wasScheduled = [self.websocket writeData:messageData error:&error]; - if (!wasScheduled || error) { - OWSFailDebug(@"could not send socket request: %@", error); - [socketMessage didFailBeforeSending]; - return; - } - OWSLogInfo(@"making request: %llu, %@: %@, jsonData.length: %zd", - socketMessage.requestId, - request.HTTPMethod, - requestPath, - jsonData.length); - - const int64_t kSocketTimeoutSeconds = 10; - __weak TSSocketMessage *weakSocketMessage = socketMessage; - dispatch_after(dispatch_time(DISPATCH_TIME_NOW, kSocketTimeoutSeconds * NSEC_PER_SEC), - dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), - ^{ - [weakSocketMessage timeoutIfNecessary]; - }); -} - -- (void)processWebSocketResponseMessage:(WebSocketProtoWebSocketResponseMessage *)message -{ - OWSAssertIsOnMainThread(); - OWSAssertDebug(message); - - dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ - [self processWebSocketResponseMessageAsync:message]; - }); -} - -- (void)processWebSocketResponseMessageAsync:(WebSocketProtoWebSocketResponseMessage *)message -{ - OWSAssertDebug(message); - - OWSLogInfo(@"received WebSocket response requestId: %llu, status: %u", message.requestID, message.status); - - DispatchMainThreadSafe(^{ - [self requestSocketAliveForAtLeastSeconds:kMakeRequestKeepSocketAliveDurationSeconds]; - }); - - UInt64 requestId = message.requestID; - UInt32 responseStatus = message.status; - NSString *_Nullable responseMessage; - if (message.hasMessage) { - responseMessage = message.message; - } - NSData *_Nullable responseData; - if (message.hasBody) { - responseData = message.body; - } - - BOOL hasValidResponse = YES; - id responseObject = responseData; - if (responseData) { - NSError *error; - id _Nullable responseJson = - [NSJSONSerialization JSONObjectWithData:responseData options:(NSJSONReadingOptions)0 error:&error]; - if (!responseJson || error) { - OWSFailDebug(@"could not parse WebSocket response JSON: %@.", error); - hasValidResponse = NO; - } else { - responseObject = responseJson; - } - } - - TSSocketMessage *_Nullable socketMessage; - @synchronized(self) { - socketMessage = self.socketMessageMap[@(requestId)]; - [self.socketMessageMap removeObjectForKey:@(requestId)]; - } - - if (!socketMessage) { - OWSLogError(@"received response to unknown request."); - } else { - BOOL hasSuccessStatus = 200 <= responseStatus && responseStatus <= 299; - BOOL didSucceed = hasSuccessStatus && hasValidResponse; - if (didSucceed) { - [self.tsAccountManager setIsDeregistered:NO]; - - [socketMessage didSucceedWithResponseObject:responseObject]; - } else { - if (responseStatus == 403) { - // This should be redundant with our check for the socket - // failing due to 403, but let's be thorough. - if (self.tsAccountManager.isRegisteredAndReady) { - [self.tsAccountManager setIsDeregistered:YES]; - } else { - OWSFailDebug(@"Ignoring auth failure; not registered and ready."); - } - } - - NSError *error = OWSErrorWithCodeDescription(OWSErrorCodeMessageResponseFailed, - NSLocalizedString( - @"ERROR_DESCRIPTION_RESPONSE_FAILED", @"Error indicating that a socket response failed.")); - [socketMessage didFailWithStatusCode:(NSInteger)responseStatus responseData:responseData error:error]; - } - } -} - -- (void)failAllPendingSocketMessagesIfNecessary -{ - if (!self.canMakeRequests) { - [self failAllPendingSocketMessages]; - } -} - -- (void)failAllPendingSocketMessages -{ - NSArray *socketMessages; - @synchronized(self) { - socketMessages = self.socketMessageMap.allValues; - [self.socketMessageMap removeAllObjects]; - } - - OWSLogInfo(@"failAllPendingSocketMessages: %zd.", socketMessages.count); - - for (TSSocketMessage *socketMessage in socketMessages) { - [socketMessage didFailBeforeSending]; - } -} - -#pragma mark - SSKWebSocketDelegate - -- (void)websocketDidConnectWithSocket:(id)websocket -{ - OWSAssertIsOnMainThread(); - OWSAssertDebug(websocket); - if (websocket != self.websocket) { - // Ignore events from obsolete web sockets. - return; - } - - self.state = OWSWebSocketStateOpen; - - // If socket opens, we know we're not de-registered. - [self.tsAccountManager setIsDeregistered:NO]; - - [self.outageDetection reportConnectionSuccess]; -} - -- (void)websocketDidDisconnectWithSocket:(id)websocket error:(nullable NSError *)error -{ - OWSAssertIsOnMainThread(); - OWSAssertDebug(websocket); - if (websocket != self.websocket) { - // Ignore events from obsolete web sockets. - return; - } - - OWSLogError(@"Websocket did fail with error: %@", error); - if ([error.domain isEqualToString:SSKWebSocketError.errorDomain]) { - NSNumber *_Nullable statusCode = error.userInfo[SSKWebSocketError.kStatusCodeKey]; - if (statusCode.unsignedIntegerValue == 403) { - if (self.tsAccountManager.isRegisteredAndReady) { - [self.tsAccountManager setIsDeregistered:YES]; - } else { - OWSLogWarn(@"Ignoring auth failure; not registered and ready."); - } - } - } - - [self handleSocketFailure]; -} - -- (void)websocketDidReceiveDataWithSocket:(id)websocket data:(NSData *)data -{ - OWSAssertIsOnMainThread(); - OWSAssertDebug(websocket); - - if (websocket != self.websocket) { - // Ignore events from obsolete web sockets. - return; - } - - // If we receive a response, we know we're not de-registered. - [self.tsAccountManager setIsDeregistered:NO]; - - NSError *error; - WebSocketProtoWebSocketMessage *_Nullable wsMessage = [WebSocketProtoWebSocketMessage parseData:data error:&error]; - if (!wsMessage || error) { - OWSFailDebug(@"could not parse proto: %@", error); - return; - } - - if (wsMessage.type == WebSocketProtoWebSocketMessageTypeRequest) { - [self processWebSocketRequestMessage:wsMessage.request]; - } else if (wsMessage.type == WebSocketProtoWebSocketMessageTypeResponse) { - [self processWebSocketResponseMessage:wsMessage.response]; - } else { - OWSLogWarn(@"webSocket:didReceiveMessage: unknown."); - } -} - -#pragma mark - - -- (dispatch_queue_t)serialQueue -{ - static dispatch_queue_t _serialQueue; - static dispatch_once_t onceToken; - dispatch_once(&onceToken, ^{ - _serialQueue = dispatch_queue_create("org.signal.websocket", DISPATCH_QUEUE_SERIAL); - }); - - return _serialQueue; -} - -- (void)processWebSocketRequestMessage:(WebSocketProtoWebSocketRequestMessage *)message -{ - OWSAssertIsOnMainThread(); - - OWSLogInfo(@"Got message with verb: %@ and path: %@", message.verb, message.path); - - // If we receive a message over the socket while the app is in the background, - // prolong how long the socket stays open. - [self requestSocketAliveForAtLeastSeconds:kBackgroundKeepSocketAliveDurationSeconds]; - - if ([message.path isEqualToString:@"/api/v1/message"] && [message.verb isEqualToString:@"PUT"]) { - - __block OWSBackgroundTask *_Nullable backgroundTask = - [OWSBackgroundTask backgroundTaskWithLabelStr:__PRETTY_FUNCTION__]; - - dispatch_async(self.serialQueue, ^{ - BOOL success = NO; - @try { - BOOL useSignalingKey = [message.headers containsObject:@"X-Signal-Key: true"]; - NSData *_Nullable decryptedPayload; - if (useSignalingKey) { - NSString *_Nullable signalingKey = TSAccountManager.signalingKey; - OWSAssertDebug(signalingKey); - decryptedPayload = - [Cryptography decryptAppleMessagePayload:message.body withSignalingKey:signalingKey]; - } else { - OWSAssertDebug([message.headers containsObject:@"X-Signal-Key: false"]); - - decryptedPayload = message.body; - } - - if (!decryptedPayload) { - OWSLogWarn(@"Failed to decrypt incoming payload or bad HMAC"); - } else { - [self.messageReceiver handleReceivedEnvelopeData:decryptedPayload]; - success = YES; - } - } @catch (NSException *exception) { - OWSFailDebug(@"Received an invalid envelope: %@", exception.debugDescription); - // TODO: Add analytics. - } - - if (!success) { - [LKStorage writeSyncWithBlock:^(YapDatabaseReadWriteTransaction *transaction) { - TSErrorMessage *errorMessage = [TSErrorMessage corruptedMessageInUnknownThread]; - [self.notificationsManager notifyUserForThreadlessErrorMessage:errorMessage - transaction:transaction]; - }]; - } - - dispatch_async(dispatch_get_main_queue(), ^{ - [self sendWebSocketMessageAcknowledgement:message]; - OWSAssertDebug(backgroundTask); - backgroundTask = nil; - }); - }); - } else if ([message.path isEqualToString:@"/api/v1/queue/empty"]) { - // Queue is drained. - - [self sendWebSocketMessageAcknowledgement:message]; - } else { - OWSLogWarn(@"Unsupported WebSocket Request"); - - [self sendWebSocketMessageAcknowledgement:message]; - } -} - -- (void)sendWebSocketMessageAcknowledgement:(WebSocketProtoWebSocketRequestMessage *)request -{ - OWSAssertIsOnMainThread(); - - NSError *error; - - WebSocketProtoWebSocketResponseMessageBuilder *responseBuilder = - [WebSocketProtoWebSocketResponseMessage builderWithRequestID:request.requestID status:200]; - [responseBuilder setMessage:@"OK"]; - WebSocketProtoWebSocketResponseMessage *_Nullable response = [responseBuilder buildAndReturnError:&error]; - if (!response || error) { - OWSFailDebug(@"could not build proto: %@", error); - return; - } - - WebSocketProtoWebSocketMessageBuilder *messageBuilder = - [WebSocketProtoWebSocketMessage builderWithType:WebSocketProtoWebSocketMessageTypeResponse]; - [messageBuilder setResponse:response]; - - NSData *_Nullable messageData = [messageBuilder buildSerializedDataAndReturnError:&error]; - if (!messageData || error) { - OWSFailDebug(@"could not serialize proto: %@", error); - return; - } - - [self.websocket writeData:messageData error:&error]; - if (error) { - OWSLogWarn(@"Error while trying to write on websocket %@", error); - [self handleSocketFailure]; - } -} - -- (void)cycleSocket -{ - OWSAssertIsOnMainThread(); - - [self closeWebSocket]; - - [self applyDesiredSocketState]; -} - -- (void)handleSocketFailure -{ - OWSAssertIsOnMainThread(); - - [self closeWebSocket]; - - if ([self shouldSocketBeOpen]) { - // If we should retry, use `ensureReconnect` to - // reconnect after a delay. - [self ensureReconnect]; - } else { - // Otherwise clean up and align state. - [self applyDesiredSocketState]; - } - - [self.outageDetection reportConnectionFailure]; -} - -- (void)webSocketHeartBeat -{ - OWSAssertIsOnMainThread(); - - if ([self shouldSocketBeOpen]) { - NSError *error; - [self.websocket writePingAndReturnError:&error]; - if (error) { - OWSLogWarn(@"Error in websocket heartbeat: %@", error.localizedDescription); - [self handleSocketFailure]; - } - } else { - OWSLogWarn(@"webSocketHeartBeat closing web socket"); - [self closeWebSocket]; - [self applyDesiredSocketState]; - } -} - -- (NSString *)webSocketAuthenticationString -{ - return [NSString stringWithFormat:@"?login=%@&password=%@", - [[TSAccountManager localNumber] stringByReplacingOccurrencesOfString:@"+" withString:@"%2B"], - [TSAccountManager serverAuthToken]]; -} - -#pragma mark - Socket LifeCycle - -- (BOOL)shouldSocketBeOpen -{ - OWSAssertIsOnMainThread(); - - // Loki: Since we don't use web sockets, disable them - return NO; - - // Don't open socket in app extensions. - if (!CurrentAppContext().isMainApp) { - return NO; - } - - if (![self.tsAccountManager isRegisteredAndReady]) { - return NO; - } - - if (self.signalService.isCensorshipCircumventionActive) { - OWSLogWarn(@"Skipping opening of websocket due to censorship circumvention."); - return NO; - } - - if (self.appIsActive) { - // If app is active, keep web socket alive. - return YES; - } else if (self.backgroundKeepAliveUntilDate && [self.backgroundKeepAliveUntilDate timeIntervalSinceNow] > 0.f) { - OWSAssertDebug(self.backgroundKeepAliveTimer); - // If app is doing any work in the background, keep web socket alive. - return YES; - } else { - return NO; - } -} - -- (void)requestSocketAliveForAtLeastSeconds:(CGFloat)durationSeconds -{ - OWSAssertIsOnMainThread(); - OWSAssertDebug(durationSeconds > 0.f); - - if (self.appIsActive) { - // If app is active, clean up state used to keep socket alive in background. - [self clearBackgroundState]; - } else if (!self.backgroundKeepAliveUntilDate) { - OWSAssertDebug(!self.backgroundKeepAliveUntilDate); - OWSAssertDebug(!self.backgroundKeepAliveTimer); - - OWSLogInfo(@"activating socket in the background"); - - // Set up state used to keep socket alive in background. - self.backgroundKeepAliveUntilDate = [NSDate dateWithTimeIntervalSinceNow:durationSeconds]; - - // To be defensive, clean up any existing backgroundKeepAliveTimer. - [self.backgroundKeepAliveTimer invalidate]; - // Start a new timer that will fire every second while the socket is open in the background. - // This timer will ensure we close the websocket when the time comes. - self.backgroundKeepAliveTimer = [NSTimer weakScheduledTimerWithTimeInterval:1.f - target:self - selector:@selector(backgroundKeepAliveFired) - userInfo:nil - repeats:YES]; - // Additionally, we want the reconnect timer to work in the background too. - [[NSRunLoop mainRunLoop] addTimer:self.backgroundKeepAliveTimer forMode:NSDefaultRunLoopMode]; - - __weak typeof(self) weakSelf = self; - self.backgroundTask = - [OWSBackgroundTask backgroundTaskWithLabelStr:__PRETTY_FUNCTION__ - completionBlock:^(BackgroundTaskState backgroundTaskState) { - OWSAssertIsOnMainThread(); - __strong typeof(self) strongSelf = weakSelf; - if (!strongSelf) { - return; - } - - if (backgroundTaskState == BackgroundTaskState_Expired) { - [strongSelf clearBackgroundState]; - } - [strongSelf applyDesiredSocketState]; - }]; - } else { - OWSAssertDebug(self.backgroundKeepAliveUntilDate); - OWSAssertDebug(self.backgroundKeepAliveTimer); - OWSAssertDebug([self.backgroundKeepAliveTimer isValid]); - - if ([self.backgroundKeepAliveUntilDate timeIntervalSinceNow] < durationSeconds) { - // Update state used to keep socket alive in background. - self.backgroundKeepAliveUntilDate = [NSDate dateWithTimeIntervalSinceNow:durationSeconds]; - } - } - - [self applyDesiredSocketState]; -} - -- (void)backgroundKeepAliveFired -{ - OWSAssertIsOnMainThread(); - - [self applyDesiredSocketState]; -} - -- (void)requestSocketOpen -{ - DispatchMainThreadSafe(^{ - [self observeNotificationsIfNecessary]; - - // If the app is active and the user is registered, this will - // simply open the websocket. - // - // If the app is inactive, it will open the websocket for a - // period of time. - [self requestSocketAliveForAtLeastSeconds:kBackgroundOpenSocketDurationSeconds]; - }); -} - -// This method aligns the socket state with the "desired" socket state. -- (void)applyDesiredSocketState -{ - OWSAssertIsOnMainThread(); - -#ifdef DEBUG - if (CurrentAppContext().isRunningTests) { - OWSLogWarn(@"Suppressing socket in tests."); - return; - } -#endif - - if (!AppReadiness.isAppReady) { - static dispatch_once_t onceToken; - dispatch_once(&onceToken, ^{ - [AppReadiness runNowOrWhenAppDidBecomeReady:^{ - [self applyDesiredSocketState]; - }]; - }); - return; - } - - if ([self shouldSocketBeOpen]) { - if (self.state != OWSWebSocketStateOpen) { - // If we want the socket to be open and it's not open, - // start up the reconnect timer immediately (don't wait for an error). - // There's little harm in it and this will make us more robust to edge - // cases. - [self ensureReconnect]; - } - [self ensureWebsocketIsOpen]; - } else { - [self clearBackgroundState]; - [self clearReconnect]; - [self closeWebSocket]; - } -} - -- (void)clearBackgroundState -{ - OWSAssertIsOnMainThread(); - - self.backgroundKeepAliveUntilDate = nil; - [self.backgroundKeepAliveTimer invalidate]; - self.backgroundKeepAliveTimer = nil; - self.backgroundTask = nil; -} - -#pragma mark - Reconnect - -- (void)ensureReconnect -{ - OWSAssertIsOnMainThread(); - OWSAssertDebug([self shouldSocketBeOpen]); - - if (self.reconnectTimer) { - OWSAssertDebug([self.reconnectTimer isValid]); - } else { - // TODO: It'd be nice to do exponential backoff. - self.reconnectTimer = [NSTimer timerWithTimeInterval:kSocketReconnectDelaySeconds - target:self - selector:@selector(applyDesiredSocketState) - userInfo:nil - repeats:YES]; - // Additionally, we want the reconnect timer to work in the background too. - [[NSRunLoop mainRunLoop] addTimer:self.reconnectTimer forMode:NSDefaultRunLoopMode]; - } -} - -- (void)clearReconnect -{ - OWSAssertIsOnMainThread(); - - [self.reconnectTimer invalidate]; - self.reconnectTimer = nil; -} - -#pragma mark - Notifications - -- (void)applicationDidBecomeActive:(NSNotification *)notification -{ - OWSAssertIsOnMainThread(); - - self.appIsActive = YES; - [self applyDesiredSocketState]; -} - -- (void)applicationWillResignActive:(NSNotification *)notification -{ - OWSAssertIsOnMainThread(); - - self.appIsActive = NO; - // TODO: It might be nice to use `requestSocketAliveForAtLeastSeconds:` to - // keep the socket open for a few seconds after the app is - // inactivated. - [self applyDesiredSocketState]; -} - -- (void)registrationStateDidChange:(NSNotification *)notification -{ - OWSAssertIsOnMainThread(); - - [self applyDesiredSocketState]; -} - -- (void)isCensorshipCircumventionActiveDidChange:(NSNotification *)notification -{ - OWSAssertIsOnMainThread(); - - [self applyDesiredSocketState]; -} - -- (void)deviceListUpdateModifiedDeviceList:(NSNotification *)notification -{ - OWSAssertIsOnMainThread(); - - [self cycleSocket]; -} - -@end - -NS_ASSUME_NONNULL_END diff --git a/SignalUtilitiesKit/OldSnodeAPI.swift b/SignalUtilitiesKit/OldSnodeAPI.swift deleted file mode 100644 index fa3809a8e..000000000 --- a/SignalUtilitiesKit/OldSnodeAPI.swift +++ /dev/null @@ -1,44 +0,0 @@ -import PromiseKit - -@objc(LKSnodeAPI) -public final class OldSnodeAPI : NSObject { - - // MARK: Sending - @objc(sendSignalMessage:) - public static func objc_sendSignalMessage(_ signalMessage: SignalMessage) -> AnyPromise { - let promise = sendSignalMessage(signalMessage).mapValues2 { AnyPromise.from($0) }.map2 { Set($0) } - return AnyPromise.from(promise) - } - - public static func sendSignalMessage(_ signalMessage: SignalMessage) -> Promise>> { - // Convert the message to a Loki message - guard let lokiMessage = LokiMessage.from(signalMessage: signalMessage) else { return Promise(error: SnodeAPI.Error.generic) } - let publicKey = lokiMessage.recipientPublicKey - let notificationCenter = NotificationCenter.default - notificationCenter.post(name: .calculatingPoW, object: NSNumber(value: signalMessage.timestamp)) - // Calculate proof of work - return lokiMessage.calculatePoW().then2 { lokiMessageWithPoW -> Promise>> in - notificationCenter.post(name: .routing, object: NSNumber(value: signalMessage.timestamp)) - // Get the target snodes - return SnodeAPI.getTargetSnodes(for: publicKey).map2 { snodes in - notificationCenter.post(name: .messageSending, object: NSNumber(value: signalMessage.timestamp)) - let parameters = lokiMessageWithPoW.toJSON() - return Set(snodes.map { snode in - // Send the message to the target snode - return attempt(maxRetryCount: 4, recoveringOn: SnodeAPI.workQueue) { - SnodeAPI.invoke(.sendMessage, on: snode, associatedWith: publicKey, parameters: parameters) - }.map2 { rawResponse in - if let json = rawResponse as? JSON, let powDifficulty = json["difficulty"] as? Int { - guard powDifficulty != SnodeAPI.powDifficulty, powDifficulty < 100 else { return rawResponse } - print("[Loki] Setting proof of work difficulty to \(powDifficulty).") - SnodeAPI.powDifficulty = UInt(powDifficulty) - } else { - print("[Loki] Failed to update proof of work difficulty from: \(rawResponse).") - } - return rawResponse - } - }) - } - } - } -} diff --git a/SignalUtilitiesKit/OnionRequestAPI+Encryption.swift b/SignalUtilitiesKit/OnionRequestAPI+Encryption.swift deleted file mode 100644 index 86dfec1f0..000000000 --- a/SignalUtilitiesKit/OnionRequestAPI+Encryption.swift +++ /dev/null @@ -1,72 +0,0 @@ -import CryptoSwift -import PromiseKit - -extension OnionRequestAPI { - - internal static func encode(ciphertext: Data, json: JSON) throws -> Data { - // The encoding of V2 onion requests looks like: | 4 bytes: size N of ciphertext | N bytes: ciphertext | json as utf8 | - guard JSONSerialization.isValidJSONObject(json) else { throw HTTP.Error.invalidJSON } - let jsonAsData = try JSONSerialization.data(withJSONObject: json, options: [ .fragmentsAllowed ]) - let ciphertextSize = Int32(ciphertext.count).littleEndian - let ciphertextSizeAsData = withUnsafePointer(to: ciphertextSize) { Data(bytes: $0, count: MemoryLayout.size) } - return ciphertextSizeAsData + ciphertext + jsonAsData - } - - /// Encrypts `payload` for `destination` and returns the result. Use this to build the core of an onion request. - internal static func encrypt(_ payload: JSON, for destination: Destination) -> Promise { - let (promise, seal) = Promise.pending() - DispatchQueue.global(qos: .userInitiated).async { - do { - guard JSONSerialization.isValidJSONObject(payload) else { return seal.reject(HTTP.Error.invalidJSON) } - // Wrapping isn't needed for file server or open group onion requests - switch destination { - case .snode(let snode): - let snodeX25519PublicKey = snode.publicKeySet.x25519Key - let payloadAsData = try JSONSerialization.data(withJSONObject: payload, options: [ .fragmentsAllowed ]) - let plaintext = try encode(ciphertext: payloadAsData, json: [ "headers" : "" ]) - let result = try EncryptionUtilities.encrypt(plaintext, using: snodeX25519PublicKey) - seal.fulfill(result) - case .server(_, _, let serverX25519PublicKey): - let plaintext = try JSONSerialization.data(withJSONObject: payload, options: [ .fragmentsAllowed ]) - let result = try EncryptionUtilities.encrypt(plaintext, using: serverX25519PublicKey) - seal.fulfill(result) - } - } catch (let error) { - seal.reject(error) - } - } - return promise - } - - /// Encrypts the previous encryption result (i.e. that of the hop after this one) for this hop. Use this to build the layers of an onion request. - internal static func encryptHop(from lhs: Destination, to rhs: Destination, using previousEncryptionResult: EncryptionResult) -> Promise { - let (promise, seal) = Promise.pending() - DispatchQueue.global(qos: .userInitiated).async { - var parameters: JSON - switch rhs { - case .snode(let snode): - let snodeED25519PublicKey = snode.publicKeySet.ed25519Key - parameters = [ "destination" : snodeED25519PublicKey ] - case .server(let host, let target, _): - parameters = [ "host" : host, "target" : target, "method" : "POST" ] - } - parameters["ephemeral_key"] = previousEncryptionResult.ephemeralPublicKey.toHexString() - let x25519PublicKey: String - switch lhs { - case .snode(let snode): - let snodeX25519PublicKey = snode.publicKeySet.x25519Key - x25519PublicKey = snodeX25519PublicKey - case .server(_, _, let serverX25519PublicKey): - x25519PublicKey = serverX25519PublicKey - } - do { - let plaintext = try encode(ciphertext: previousEncryptionResult.ciphertext, json: parameters) - let result = try EncryptionUtilities.encrypt(plaintext, using: x25519PublicKey) - seal.fulfill(result) - } catch (let error) { - seal.reject(error) - } - } - return promise - } -} diff --git a/SignalUtilitiesKit/OutageDetection.swift b/SignalUtilitiesKit/OutageDetection.swift index 2717dc570..8f680505f 100644 --- a/SignalUtilitiesKit/OutageDetection.swift +++ b/SignalUtilitiesKit/OutageDetection.swift @@ -27,9 +27,8 @@ public class OutageDetection: NSObject { } private var shouldCheckForOutage = false { didSet { - // Loki: Don't check for outages -// AssertIsOnMainThread() -// ensureCheckTimer() + AssertIsOnMainThread() + ensureCheckTimer() } } diff --git a/SignalUtilitiesKit/PhoneNumber.h b/SignalUtilitiesKit/PhoneNumber.h deleted file mode 100644 index 0e7cb9e30..000000000 --- a/SignalUtilitiesKit/PhoneNumber.h +++ /dev/null @@ -1,52 +0,0 @@ -// -// Copyright (c) 2019 Open Whisper Systems. All rights reserved. -// - -#import - -NS_ASSUME_NONNULL_BEGIN - -#define COUNTRY_CODE_PREFIX @"+" - -/** - * - * PhoneNumber is used to deal with the nitty details of parsing/canonicalizing phone numbers. - * Everything that expects a valid phone number should take a PhoneNumber, not a string, to avoid stringly typing. - * - */ -@interface PhoneNumber : NSObject - -+ (nullable PhoneNumber *)phoneNumberFromE164:(NSString *)text; - -+ (nullable PhoneNumber *)tryParsePhoneNumberFromUserSpecifiedText:(NSString *)text; -+ (nullable PhoneNumber *)tryParsePhoneNumberFromE164:(NSString *)text; - -// This will try to parse the input text as a phone number using -// the default region and the country code for this client's phone -// number. -// -// Order matters; better results will appear first. -+ (NSArray *)tryParsePhoneNumbersFromsUserSpecifiedText:(NSString *)text - clientPhoneNumber:(NSString *)clientPhoneNumber; - -+ (NSString *)removeFormattingCharacters:(NSString *)inputString; -+ (NSString *)bestEffortFormatPartialUserSpecifiedTextToLookLikeAPhoneNumber:(NSString *)input; -+ (NSString *)bestEffortFormatPartialUserSpecifiedTextToLookLikeAPhoneNumber:(NSString *)input - withSpecifiedCountryCodeString:(NSString *)countryCodeString; -+ (NSString *)bestEffortLocalizedPhoneNumberWithE164:(NSString *)phoneNumber; - -+ (NSString *)regionCodeFromCountryCodeString:(NSString *)countryCodeString; - -- (NSURL *)toSystemDialerURL; -- (NSString *)toE164; -- (nullable NSNumber *)getCountryCode; -@property (nonatomic, readonly, nullable) NSString *nationalNumber; -- (BOOL)isValid; - -- (NSComparisonResult)compare:(PhoneNumber *)other; - -+ (NSString *)defaultCountryCode; - -@end - -NS_ASSUME_NONNULL_END diff --git a/SignalUtilitiesKit/PhoneNumber.m b/SignalUtilitiesKit/PhoneNumber.m deleted file mode 100644 index e279f5053..000000000 --- a/SignalUtilitiesKit/PhoneNumber.m +++ /dev/null @@ -1,584 +0,0 @@ -// -// Copyright (c) 2019 Open Whisper Systems. All rights reserved. -// - -#import "PhoneNumber.h" -#import "PhoneNumberUtil.h" -#import "AppContext.h" -#import -#import -#import -#import -#import -#import - -NS_ASSUME_NONNULL_BEGIN - -static NSString *const RPDefaultsKeyPhoneNumberString = @"RPDefaultsKeyPhoneNumberString"; -static NSString *const RPDefaultsKeyPhoneNumberCanonical = @"RPDefaultsKeyPhoneNumberCanonical"; - -@interface PhoneNumber () - -@property (nonatomic, readonly) NBPhoneNumber *phoneNumber; -@property (nonatomic, readonly) NSString *e164; - -@end - -#pragma mark - - -@implementation PhoneNumber - -- (instancetype)initWithPhoneNumber:(NBPhoneNumber *)phoneNumber e164:(NSString *)e164 -{ - if (self = [self init]) { - OWSAssertDebug(phoneNumber); - OWSAssertDebug(e164.length > 0); - - _phoneNumber = phoneNumber; - _e164 = e164; - } - return self; -} - -+ (nullable PhoneNumber *)phoneNumberFromText:(NSString *)text andRegion:(NSString *)regionCode { - OWSAssertDebug(text != nil); - OWSAssertDebug(regionCode != nil); - - PhoneNumberUtil *phoneUtil = [PhoneNumberUtil sharedThreadLocal]; - - NSError *parseError = nil; - NBPhoneNumber *number = [phoneUtil parse:text defaultRegion:regionCode error:&parseError]; - - if (parseError) { - return nil; - } - - NSError *toE164Error; - NSString *e164 = [phoneUtil format:number numberFormat:NBEPhoneNumberFormatE164 error:&toE164Error]; - if (toE164Error) { - OWSLogDebug(@"Issue while formatting number: %@", [toE164Error description]); - return nil; - } - - return [[PhoneNumber alloc] initWithPhoneNumber:number e164:e164]; -} - -+ (nullable PhoneNumber *)phoneNumberFromUserSpecifiedText:(NSString *)text { - OWSAssertDebug(text != nil); - - return [PhoneNumber phoneNumberFromText:text andRegion:[self defaultCountryCode]]; -} - -+ (NSString *)defaultCountryCode -{ - NSLocale *locale = [NSLocale currentLocale]; - - NSString *_Nullable countryCode = nil; -#if TARGET_OS_IPHONE - countryCode = [[PhoneNumberUtil sharedThreadLocal].nbPhoneNumberUtil countryCodeByCarrier]; - - if ([countryCode isEqualToString:@"ZZ"]) { - countryCode = [locale objectForKey:NSLocaleCountryCode]; - } -#else - countryCode = [locale objectForKey:NSLocaleCountryCode]; -#endif - if (!countryCode) { - OWSFailDebug(@"Could not identify country code for locale: %@", locale); - countryCode = @"US"; - } - return countryCode; -} - -+ (nullable PhoneNumber *)phoneNumberFromE164:(NSString *)text { - return [[PhoneNumber alloc] initWithPhoneNumber:[NBPhoneNumber new] e164:text]; - // Loki: Original code: - // ======== -// OWSAssertDebug(text != nil); -// OWSAssertDebug([text hasPrefix:COUNTRY_CODE_PREFIX]); -// PhoneNumber *number = [PhoneNumber phoneNumberFromText:text andRegion:@"ZZ"]; -// OWSAssertDebug(number != nil); -// return number; - // ======== -} - -+ (NSString *)bestEffortFormatPartialUserSpecifiedTextToLookLikeAPhoneNumber:(NSString *)input { - return [PhoneNumber bestEffortFormatPartialUserSpecifiedTextToLookLikeAPhoneNumber:input - withSpecifiedRegionCode:[self defaultCountryCode]]; -} - -+ (NSString *)bestEffortFormatPartialUserSpecifiedTextToLookLikeAPhoneNumber:(NSString *)input - withSpecifiedCountryCodeString:(NSString *)countryCodeString { - return [PhoneNumber - bestEffortFormatPartialUserSpecifiedTextToLookLikeAPhoneNumber:input - withSpecifiedRegionCode: - [PhoneNumber regionCodeFromCountryCodeString:countryCodeString]]; -} - -+ (NSString *)bestEffortFormatPartialUserSpecifiedTextToLookLikeAPhoneNumber:(NSString *)input - withSpecifiedRegionCode:(NSString *)regionCode { - NBAsYouTypeFormatter *formatter = [[NBAsYouTypeFormatter alloc] initWithRegionCode:regionCode]; - - NSString *result = input; - for (NSUInteger i = 0; i < input.length; i++) { - result = [formatter inputDigit:[input substringWithRange:NSMakeRange(i, 1)]]; - } - return result; -} - -+ (NSString *)formatIntAsEN:(int)value -{ - static NSNumberFormatter *formatter = nil; - static dispatch_once_t onceToken; - dispatch_once(&onceToken, ^{ - formatter = [NSNumberFormatter new]; - formatter.locale = [NSLocale localeWithLocaleIdentifier:@"en_US"]; - }); - return [formatter stringFromNumber:@(value)]; -} - -+ (NSString *)bestEffortLocalizedPhoneNumberWithE164:(NSString *)phoneNumber -{ - OWSAssertDebug(phoneNumber); - - if (![phoneNumber hasPrefix:COUNTRY_CODE_PREFIX]) { - return phoneNumber; - } - - PhoneNumber *_Nullable parsedPhoneNumber = [self tryParsePhoneNumberFromE164:phoneNumber]; - if (!parsedPhoneNumber) { - OWSLogWarn(@"could not parse phone number."); - return phoneNumber; - } - NSNumber *_Nullable countryCode = [parsedPhoneNumber getCountryCode]; - if (!countryCode) { - OWSLogWarn(@"parsed phone number has no country code."); - return phoneNumber; - } - NSString *countryCodeString = [self formatIntAsEN:countryCode.intValue]; - if (countryCodeString.length < 1) { - OWSLogWarn(@"invalid country code."); - return phoneNumber; - } - NSString *_Nullable formattedPhoneNumber = - [self bestEffortFormatPartialUserSpecifiedTextToLookLikeAPhoneNumber:phoneNumber - withSpecifiedRegionCode:countryCodeString]; - if (!countryCode) { - OWSLogWarn(@"could not format phone number."); - return phoneNumber; - } - return formattedPhoneNumber; -} - -+ (NSString *)regionCodeFromCountryCodeString:(NSString *)countryCodeString { - NBPhoneNumberUtil *phoneUtil = [PhoneNumberUtil sharedThreadLocal].nbPhoneNumberUtil; - NSString *regionCode = - [phoneUtil getRegionCodeForCountryCode:@([[countryCodeString substringFromIndex:1] integerValue])]; - return regionCode; -} - -+ (nullable PhoneNumber *)tryParsePhoneNumberFromUserSpecifiedText:(NSString *)text { - OWSAssertDebug(text != nil); - - if ([text isEqualToString:@""]) { - return nil; - } - NSString *sanitizedString = [self removeFormattingCharacters:text]; - - return [self phoneNumberFromUserSpecifiedText:sanitizedString]; -} - -+ (nullable NSString *)nationalPrefixTransformRuleForDefaultRegion -{ - static NSString *result = nil; - static dispatch_once_t onceToken; - dispatch_once(&onceToken, ^{ - NSString *defaultCountryCode = [self defaultCountryCode]; - NBMetadataHelper *helper = [[NBMetadataHelper alloc] init]; - NBPhoneMetaData *defaultRegionMetadata = [helper getMetadataForRegion:defaultCountryCode]; - result = defaultRegionMetadata.nationalPrefixTransformRule; - }); - return result; -} - -// clientPhoneNumber is the local user's phone number and should never change. -+ (nullable NSString *)nationalPrefixTransformRuleForClientPhoneNumber:(NSString *)clientPhoneNumber -{ - if (clientPhoneNumber.length < 1) { - return nil; - } - static NSString *result = nil; - static NSString *cachedClientPhoneNumber = nil; - static dispatch_once_t onceToken; - - // clientPhoneNumber is the local user's phone number and should never change. - static void (^updateCachedClientPhoneNumber)(void); - updateCachedClientPhoneNumber = ^(void) { - NSNumber *localCallingCode = [[PhoneNumber phoneNumberFromE164:clientPhoneNumber] getCountryCode]; - if (localCallingCode != nil) { - NSString *localCallingCodePrefix = [NSString stringWithFormat:@"+%@", localCallingCode]; - NSString *localCountryCode = - [PhoneNumberUtil.sharedThreadLocal probableCountryCodeForCallingCode:localCallingCodePrefix]; - if (localCountryCode && ![localCountryCode isEqualToString:[self defaultCountryCode]]) { - NBMetadataHelper *helper = [[NBMetadataHelper alloc] init]; - NBPhoneMetaData *localNumberRegionMetadata = [helper getMetadataForRegion:localCountryCode]; - result = localNumberRegionMetadata.nationalPrefixTransformRule; - } else { - result = nil; - } - } - cachedClientPhoneNumber = [clientPhoneNumber copy]; - }; - -#ifdef DEBUG - // For performance, we want to cahce this result, but it breaks tests since local number - // can change. - if (CurrentAppContext().isRunningTests) { - updateCachedClientPhoneNumber(); - } else { - dispatch_once(&onceToken, ^{ - updateCachedClientPhoneNumber(); - }); - } -#else - dispatch_once(&onceToken, ^{ - updateCachedClientPhoneNumber(); - }); - OWSAssertDebug([cachedClientPhoneNumber isEqualToString:clientPhoneNumber]); -#endif - - return result; -} - -+ (NSArray *)tryParsePhoneNumbersFromsUserSpecifiedText:(NSString *)text - clientPhoneNumber:(NSString *)clientPhoneNumber -{ - NSMutableArray *result = - [[self tryParsePhoneNumbersFromNormalizedText:text clientPhoneNumber:clientPhoneNumber] mutableCopy]; - - // A handful of countries (Mexico, Argentina, etc.) require a "national" prefix after - // their country calling code. - // - // It's a bit hacky, but we reconstruct these national prefixes from libPhoneNumber's - // parsing logic. It's okay if we botch this a little. The risk is that we end up with - // some misformatted numbers with extra non-numeric regex syntax. These erroneously - // parsed numbers will never be presented to the user, since they'll never survive the - // contacts intersection. - // - // 1. Try to apply a "national prefix" using the phone's region. - NSString *nationalPrefixTransformRuleForDefaultRegion = [self nationalPrefixTransformRuleForDefaultRegion]; - if ([nationalPrefixTransformRuleForDefaultRegion containsString:@"$1"]) { - NSString *normalizedText = - [nationalPrefixTransformRuleForDefaultRegion stringByReplacingOccurrencesOfString:@"$1" withString:text]; - if (![normalizedText containsString:@"$"]) { - [result addObjectsFromArray:[self tryParsePhoneNumbersFromNormalizedText:normalizedText - clientPhoneNumber:clientPhoneNumber]]; - } - } - - // 2. Try to apply a "national prefix" using the region that corresponds to the - // calling code for the local phone number. - NSString *nationalPrefixTransformRuleForClientPhoneNumber = - [self nationalPrefixTransformRuleForClientPhoneNumber:clientPhoneNumber]; - if ([nationalPrefixTransformRuleForClientPhoneNumber containsString:@"$1"]) { - NSString *normalizedText = - [nationalPrefixTransformRuleForClientPhoneNumber stringByReplacingOccurrencesOfString:@"$1" - withString:text]; - if (![normalizedText containsString:@"$"]) { - [result addObjectsFromArray:[self tryParsePhoneNumbersFromNormalizedText:normalizedText - clientPhoneNumber:clientPhoneNumber]]; - } - } - - return [result copy]; -} - -+ (NSArray *)tryParsePhoneNumbersFromNormalizedText:(NSString *)text - clientPhoneNumber:(NSString *)clientPhoneNumber -{ - OWSAssertDebug(text != nil); - - text = [text stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceCharacterSet]]; - if ([text isEqualToString:@""]) { - return nil; - } - - NSString *sanitizedString = [self removeFormattingCharacters:text]; - OWSAssertDebug(sanitizedString != nil); - - NSMutableArray *result = [NSMutableArray new]; - NSMutableSet *phoneNumberSet = [NSMutableSet new]; - void (^tryParsingWithCountryCode)(NSString *, NSString *) = ^(NSString *text, - NSString *countryCode) { - PhoneNumber *phoneNumber = [PhoneNumber phoneNumberFromText:text - andRegion:countryCode]; - if (phoneNumber && [phoneNumber toE164] && ![phoneNumberSet containsObject:[phoneNumber toE164]]) { - [result addObject:phoneNumber]; - [phoneNumberSet addObject:[phoneNumber toE164]]; - } - }; - - tryParsingWithCountryCode(sanitizedString, [self defaultCountryCode]); - - if ([sanitizedString hasPrefix:@"+"]) { - // If the text starts with "+", don't try prepending - // anything else. - return result; - } - - // Try just adding "+" and parsing it. - tryParsingWithCountryCode([NSString stringWithFormat:@"+%@", sanitizedString], [self defaultCountryCode]); - - // Order matters; better results should appear first so prefer - // matches with the same country code as this client's phone number. - if (clientPhoneNumber.length == 0) { - OWSFailDebug(@"clientPhoneNumber had unexpected length"); - return result; - } - - // Note that NBPhoneNumber uses "country code" to refer to what we call a - // "calling code" (i.e. 44 in +44123123). Within SSK we use "country code" - // (and sometimes "region code") to refer to a country's ISO 2-letter code - // (ISO 3166-1 alpha-2). - NSNumber *callingCodeForLocalNumber = [[PhoneNumber phoneNumberFromE164:clientPhoneNumber] getCountryCode]; - if (callingCodeForLocalNumber == nil) { - OWSFailDebug(@"callingCodeForLocalNumber was unexpectedly nil"); - return result; - } - - NSString *callingCodePrefix = [NSString stringWithFormat:@"+%@", callingCodeForLocalNumber]; - - tryParsingWithCountryCode([callingCodePrefix stringByAppendingString:sanitizedString], [self defaultCountryCode]); - - // Try to determine what the country code is for the local phone number - // and also try parsing the phone number using that country code if it - // differs from the device's region code. - // - // For example, a French person living in Italy might have an - // Italian phone number but use French region/language for their - // phone. They're likely to have both Italian and French contacts. - NSString *localCountryCode = - [PhoneNumberUtil.sharedThreadLocal probableCountryCodeForCallingCode:callingCodePrefix]; - if (localCountryCode && ![localCountryCode isEqualToString:[self defaultCountryCode]]) { - tryParsingWithCountryCode([callingCodePrefix stringByAppendingString:sanitizedString], localCountryCode); - } - - NSString *_Nullable phoneNumberByApplyingMissingAreaCode = - [self applyMissingAreaCodeWithCallingCodeForReferenceNumber:callingCodeForLocalNumber - referenceNumber:clientPhoneNumber - sanitizedInputText:sanitizedString]; - if (phoneNumberByApplyingMissingAreaCode) { - tryParsingWithCountryCode(phoneNumberByApplyingMissingAreaCode, localCountryCode); - } - - return result; -} - -#pragma mark - missing area code - -+ (nullable NSString *)applyMissingAreaCodeWithCallingCodeForReferenceNumber:(NSNumber *)callingCodeForReferenceNumber - referenceNumber:(NSString *)referenceNumber - sanitizedInputText:(NSString *)sanitizedInputText -{ - if ([callingCodeForReferenceNumber isEqual:@(55)]) { - return - [self applyMissingBrazilAreaCodeWithReferenceNumber:referenceNumber sanitizedInputText:sanitizedInputText]; - } else if ([callingCodeForReferenceNumber isEqual:@(1)]) { - return [self applyMissingUnitedStatesAreaCodeWithReferenceNumber:referenceNumber - sanitizedInputText:sanitizedInputText]; - } else { - return nil; - } -} - -#pragma mark - missing brazil area code - -+ (nullable NSString *)applyMissingBrazilAreaCodeWithReferenceNumber:(NSString *)referenceNumber - sanitizedInputText:(NSString *)sanitizedInputText -{ - NSError *error; - NSRegularExpression *missingAreaCodeRegex = - [[NSRegularExpression alloc] initWithPattern:@"^(9?\\d{8})$" options:0 error:&error]; - if (error) { - OWSFailDebug(@"failure: %@", error); - return nil; - } - - if ([missingAreaCodeRegex firstMatchInString:sanitizedInputText - options:0 - range:NSMakeRange(0, sanitizedInputText.length)] - == nil) { - } - - NSString *_Nullable referenceAreaCode = [self brazilAreaCodeFromReferenceNumberE164:referenceNumber]; - if (!referenceAreaCode) { - return nil; - } - return [NSString stringWithFormat:@"+55%@%@", referenceAreaCode, sanitizedInputText]; -} - -+ (nullable NSString *)brazilAreaCodeFromReferenceNumberE164:(NSString *)referenceNumberE164 -{ - NSError *error; - NSRegularExpression *areaCodeRegex = - [[NSRegularExpression alloc] initWithPattern:@"^\\+55(\\d{2})9?\\d{8}" options:0 error:&error]; - if (error) { - OWSFailDebug(@"failure: %@", error); - return nil; - } - - NSArray *matches = - [areaCodeRegex matchesInString:referenceNumberE164 options:0 range:NSMakeRange(0, referenceNumberE164.length)]; - if (matches.count == 0) { - OWSFailDebug(@"failure: unexpectedly unable to extract area code from US number"); - return nil; - } - NSTextCheckingResult *match = matches[0]; - - NSRange firstCaptureRange = [match rangeAtIndex:1]; - return [referenceNumberE164 substringWithRange:firstCaptureRange]; -} - -#pragma mark - missing US area code - -+ (nullable NSString *)applyMissingUnitedStatesAreaCodeWithReferenceNumber:(NSString *)referenceNumber - sanitizedInputText:(NSString *)sanitizedInputText -{ - NSError *error; - NSRegularExpression *missingAreaCodeRegex = - [[NSRegularExpression alloc] initWithPattern:@"^(\\d{7})$" options:0 error:&error]; - if (error) { - OWSFailDebug(@"failure: %@", error); - return nil; - } - - if ([missingAreaCodeRegex firstMatchInString:sanitizedInputText - options:0 - range:NSMakeRange(0, sanitizedInputText.length)] - == nil) { - // area code isn't missing - return nil; - } - - NSString *_Nullable referenceAreaCode = [self unitedStateAreaCodeFromReferenceNumberE164:referenceNumber]; - if (!referenceAreaCode) { - return nil; - } - return [NSString stringWithFormat:@"+1%@%@", referenceAreaCode, sanitizedInputText]; -} - -+ (nullable NSString *)unitedStateAreaCodeFromReferenceNumberE164:(NSString *)referenceNumberE164 -{ - NSError *error; - NSRegularExpression *areaCodeRegex = - [[NSRegularExpression alloc] initWithPattern:@"^\\+1(\\d{3})" options:0 error:&error]; - if (error) { - OWSFailDebug(@"failure: %@", error); - return nil; - } - - NSArray *matches = - [areaCodeRegex matchesInString:referenceNumberE164 options:0 range:NSMakeRange(0, referenceNumberE164.length)]; - if (matches.count == 0) { - OWSFailDebug(@"failure: unexpectedly unable to extract area code from US number"); - return nil; - } - NSTextCheckingResult *match = matches[0]; - - NSRange firstCaptureRange = [match rangeAtIndex:1]; - return [referenceNumberE164 substringWithRange:firstCaptureRange]; -} - -#pragma mark - - -+ (NSString *)removeFormattingCharacters:(NSString *)inputString { - char outputString[inputString.length + 1]; - - int outputLength = 0; - for (NSUInteger i = 0; i < inputString.length; i++) { - unichar c = [inputString characterAtIndex:i]; - if (c == '+' || (c >= '0' && c <= '9')) { - outputString[outputLength++] = (char)c; - } - } - - outputString[outputLength] = 0; - return [NSString stringWithUTF8String:(void *)outputString]; -} - -+ (nullable PhoneNumber *)tryParsePhoneNumberFromE164:(NSString *)text { - OWSAssertDebug(text != nil); - if (![text hasPrefix:COUNTRY_CODE_PREFIX]) { - return nil; - } - - return [self phoneNumberFromE164:text]; -} - -- (NSURL *)toSystemDialerURL { - NSString *link = [NSString stringWithFormat:@"telprompt://%@", self.e164]; - return [NSURL URLWithString:link]; -} - -- (NSString *)toE164 { - return self.e164; -} - -- (nullable NSNumber *)getCountryCode { - return self.phoneNumber.countryCode; -} - -- (nullable NSString *)nationalNumber -{ - NSError *error; - NSString *nationalNumber = [[PhoneNumberUtil sharedThreadLocal] format:self.phoneNumber - numberFormat:NBEPhoneNumberFormatNATIONAL - error:&error]; - if (error) { - OWSLogVerbose(@"error parsing number into national format: %@", error); - return nil; - } - - return nationalNumber; -} - -- (BOOL)isValid -{ - return [[PhoneNumberUtil sharedThreadLocal].nbPhoneNumberUtil isValidNumber:self.phoneNumber]; -} - -- (NSString *)description { - return self.e164; -} - -- (void)encodeWithCoder:(NSCoder *)encoder { - [encoder encodeObject:self.phoneNumber forKey:RPDefaultsKeyPhoneNumberString]; - [encoder encodeObject:self.e164 forKey:RPDefaultsKeyPhoneNumberCanonical]; -} - -- (id)initWithCoder:(NSCoder *)decoder { - if ((self = [super init])) { - _phoneNumber = [decoder decodeObjectForKey:RPDefaultsKeyPhoneNumberString]; - _e164 = [decoder decodeObjectForKey:RPDefaultsKeyPhoneNumberCanonical]; - } - return self; -} - -- (NSComparisonResult)compare:(PhoneNumber *)other -{ - return [self.toE164 compare:other.toE164]; -} - -- (BOOL)isEqual:(id)other -{ - if (![other isMemberOfClass:[self class]]) { - return NO; - } - PhoneNumber *otherPhoneNumber = (PhoneNumber *)other; - - return [self.phoneNumber isEqual:otherPhoneNumber.phoneNumber]; -} - -@end - -NS_ASSUME_NONNULL_END diff --git a/SignalUtilitiesKit/PhoneNumberUtil.h b/SignalUtilitiesKit/PhoneNumberUtil.h deleted file mode 100644 index 1d5c3ef37..000000000 --- a/SignalUtilitiesKit/PhoneNumberUtil.h +++ /dev/null @@ -1,44 +0,0 @@ -// -// Copyright (c) 2019 Open Whisper Systems. All rights reserved. -// - -#import -#import - -NS_ASSUME_NONNULL_BEGIN - -@interface PhoneNumberUtil : NSObject - -@property (nonatomic, retain) NBPhoneNumberUtil *nbPhoneNumberUtil; - -- (instancetype)init NS_UNAVAILABLE; - -+ (instancetype)sharedThreadLocal; - -+ (BOOL)name:(NSString *)nameString matchesQuery:(NSString *)queryString; - -+ (NSString *)callingCodeFromCountryCode:(NSString *)countryCode; -+ (nullable NSString *)countryNameFromCountryCode:(NSString *)countryCode; -+ (NSArray *)countryCodesForSearchTerm:(nullable NSString *)searchTerm; - -// Returns a list of country codes for a calling code in descending -// order of population. -- (NSArray *)countryCodesFromCallingCode:(NSString *)callingCode; -// Returns the most likely country code for a calling code based on population. -- (NSString *)probableCountryCodeForCallingCode:(NSString *)callingCode; - -+ (NSUInteger)translateCursorPosition:(NSUInteger)offset - from:(NSString *)source - to:(NSString *)target - stickingRightward:(bool)preferHigh; - -+ (NSString *)examplePhoneNumberForCountryCode:(NSString *)countryCode; - -- (nullable NBPhoneNumber *)parse:(NSString *)numberToParse defaultRegion:(NSString *)defaultRegion error:(NSError **)error; -- (NSString *)format:(NBPhoneNumber *)phoneNumber - numberFormat:(NBEPhoneNumberFormat)numberFormat - error:(NSError **)error; - -@end - -NS_ASSUME_NONNULL_END diff --git a/SignalUtilitiesKit/PhoneNumberUtil.m b/SignalUtilitiesKit/PhoneNumberUtil.m deleted file mode 100644 index 16bb85545..000000000 --- a/SignalUtilitiesKit/PhoneNumberUtil.m +++ /dev/null @@ -1,610 +0,0 @@ -// -// Copyright (c) 2019 Open Whisper Systems. All rights reserved. -// - -#import "PhoneNumberUtil.h" -#import "ContactsManagerProtocol.h" -#import "FunctionalUtil.h" -#import -#import - -NS_ASSUME_NONNULL_BEGIN - -@interface PhoneNumberUtil () - -@property (nonatomic, readonly) NSMutableDictionary *countryCodesFromCallingCodeCache; -@property (nonatomic, readonly) NSCache *parsedPhoneNumberCache; - -@end - -#pragma mark - - -@implementation PhoneNumberUtil - -+ (PhoneNumberUtil *)sharedThreadLocal -{ - NSString *key = PhoneNumberUtil.logTag; - PhoneNumberUtil *_Nullable threadLocal = NSThread.currentThread.threadDictionary[key]; - if (!threadLocal) { - threadLocal = [PhoneNumberUtil new]; - NSThread.currentThread.threadDictionary[key] = threadLocal; - } - return threadLocal; -} - -- (instancetype)init { - self = [super init]; - - if (self) { - _nbPhoneNumberUtil = [[NBPhoneNumberUtil alloc] init]; - _countryCodesFromCallingCodeCache = [NSMutableDictionary new]; - _parsedPhoneNumberCache = [NSCache new]; - } - - return self; -} - -- (nullable NBPhoneNumber *)parse:(NSString *)numberToParse - defaultRegion:(NSString *)defaultRegion - error:(NSError **)error -{ - NSString *hashKey = [NSString stringWithFormat:@"numberToParse:%@defaultRegion:%@", numberToParse, defaultRegion]; - - NBPhoneNumber *result = [self.parsedPhoneNumberCache objectForKey:hashKey]; - - if (!result) { - result = [self.nbPhoneNumberUtil parse:numberToParse defaultRegion:defaultRegion error:error]; - if (error && *error) { - OWSAssertDebug(!result); - return nil; - } - - OWSAssertDebug(result); - - if (result) { - [self.parsedPhoneNumberCache setObject:result forKey:hashKey]; - } else { - [self.parsedPhoneNumberCache setObject:[NSNull null] forKey:hashKey]; - } - } - - if ([result class] == [NSNull class]) { - return nil; - } else { - return result; - } -} - -- (NSString *)format:(NBPhoneNumber *)phoneNumber - numberFormat:(NBEPhoneNumberFormat)numberFormat - error:(NSError **)error -{ - return [self.nbPhoneNumberUtil format:phoneNumber numberFormat:numberFormat error:error]; -} - -// country code -> country name -+ (nullable NSString *)countryNameFromCountryCode:(NSString *)countryCode -{ - OWSAssertDebug(countryCode); - - NSDictionary *countryCodeComponent = @{NSLocaleCountryCode : countryCode}; - NSString *identifier = [NSLocale localeIdentifierFromComponents:countryCodeComponent]; - NSString *countryName = [NSLocale.currentLocale displayNameForKey:NSLocaleIdentifier value:identifier]; - if (countryName.length < 1) { - countryName = [NSLocale.systemLocale displayNameForKey:NSLocaleIdentifier value:identifier]; - } - if (countryName.length < 1) { - countryName = NSLocalizedString(@"UNKNOWN_VALUE", "Indicates an unknown or unrecognizable value."); - } - return countryName; -} - -// country code -> calling code -+ (NSString *)callingCodeFromCountryCode:(NSString *)countryCode -{ - if ([countryCode isEqualToString:@"AQ"]) { - // Antarctica - return @"+672"; - } else if ([countryCode isEqualToString:@"BV"]) { - // Bouvet Island - return @"+55"; - } else if ([countryCode isEqualToString:@"IC"]) { - // Canary Islands - return @"+34"; - } else if ([countryCode isEqualToString:@"EA"]) { - // Ceuta & Melilla - return @"+34"; - } else if ([countryCode isEqualToString:@"CP"]) { - // Clipperton Island - // - // This country code should be filtered - it does not appear to have a calling code. - return nil; - } else if ([countryCode isEqualToString:@"DG"]) { - // Diego Garcia - return @"+246"; - } else if ([countryCode isEqualToString:@"TF"]) { - // French Southern Territories - return @"+262"; - } else if ([countryCode isEqualToString:@"HM"]) { - // Heard & McDonald Islands - return @"+672"; - } else if ([countryCode isEqualToString:@"XK"]) { - // Kosovo - return @"+383"; - } else if ([countryCode isEqualToString:@"PN"]) { - // Pitcairn Islands - return @"+64"; - } else if ([countryCode isEqualToString:@"GS"]) { - // So. Georgia & So. Sandwich Isl. - return @"+500"; - } else if ([countryCode isEqualToString:@"UM"]) { - // U.S. Outlying Islands - return @"+1"; - } - - NSString *callingCode = - [NSString stringWithFormat:@"%@%@", - COUNTRY_CODE_PREFIX, - [[[self sharedThreadLocal] nbPhoneNumberUtil] getCountryCodeForRegion:countryCode]]; - return callingCode; -} - -- (NSDictionary *)countryCodeToPopulationMap -{ - static dispatch_once_t onceToken; - static NSDictionary *instance = nil; - dispatch_once(&onceToken, ^{ - instance = @{ - @"AD" : @(84000), - @"AE" : @(4975593), - @"AF" : @(29121286), - @"AG" : @(86754), - @"AI" : @(13254), - @"AL" : @(2986952), - @"AM" : @(2968000), - @"AN" : @(300000), - @"AO" : @(13068161), - @"AQ" : @(0), - @"AR" : @(41343201), - @"AS" : @(57881), - @"AT" : @(8205000), - @"AU" : @(21515754), - @"AW" : @(71566), - @"AX" : @(26711), - @"AZ" : @(8303512), - @"BA" : @(4590000), - @"BB" : @(285653), - @"BD" : @(156118464), - @"BE" : @(10403000), - @"BF" : @(16241811), - @"BG" : @(7148785), - @"BH" : @(738004), - @"BI" : @(9863117), - @"BJ" : @(9056010), - @"BL" : @(8450), - @"BM" : @(65365), - @"BN" : @(395027), - @"BO" : @(9947418), - @"BQ" : @(18012), - @"BR" : @(201103330), - @"BS" : @(301790), - @"BT" : @(699847), - @"BV" : @(0), - @"BW" : @(2029307), - @"BY" : @(9685000), - @"BZ" : @(314522), - @"CA" : @(33679000), - @"CC" : @(628), - @"CD" : @(70916439), - @"CF" : @(4844927), - @"CG" : @(3039126), - @"CH" : @(7581000), - @"CI" : @(21058798), - @"CK" : @(21388), - @"CL" : @(16746491), - @"CM" : @(19294149), - @"CN" : @(1330044000), - @"CO" : @(47790000), - @"CR" : @(4516220), - @"CS" : @(10829175), - @"CU" : @(11423000), - @"CV" : @(508659), - @"CW" : @(141766), - @"CX" : @(1500), - @"CY" : @(1102677), - @"CZ" : @(10476000), - @"DE" : @(81802257), - @"DJ" : @(740528), - @"DK" : @(5484000), - @"DM" : @(72813), - @"DO" : @(9823821), - @"DZ" : @(34586184), - @"EC" : @(14790608), - @"EE" : @(1291170), - @"EG" : @(80471869), - @"EH" : @(273008), - @"ER" : @(5792984), - @"ES" : @(46505963), - @"ET" : @(88013491), - @"FI" : @(5244000), - @"FJ" : @(875983), - @"FK" : @(2638), - @"FM" : @(107708), - @"FO" : @(48228), - @"FR" : @(64768389), - @"GA" : @(1545255), - @"GB" : @(62348447), - @"GD" : @(107818), - @"GE" : @(4630000), - @"GF" : @(195506), - @"GG" : @(65228), - @"GH" : @(24339838), - @"GI" : @(27884), - @"GL" : @(56375), - @"GM" : @(1593256), - @"GN" : @(10324025), - @"GP" : @(443000), - @"GQ" : @(1014999), - @"GR" : @(11000000), - @"GS" : @(30), - @"GT" : @(13550440), - @"GU" : @(159358), - @"GW" : @(1565126), - @"GY" : @(748486), - @"HK" : @(6898686), - @"HM" : @(0), - @"HN" : @(7989415), - @"HR" : @(4284889), - @"HT" : @(9648924), - @"HU" : @(9982000), - @"ID" : @(242968342), - @"IE" : @(4622917), - @"IL" : @(7353985), - @"IM" : @(75049), - @"IN" : @(1173108018), - @"IO" : @(4000), - @"IQ" : @(29671605), - @"IR" : @(76923300), - @"IS" : @(308910), - @"IT" : @(60340328), - @"JE" : @(90812), - @"JM" : @(2847232), - @"JO" : @(6407085), - @"JP" : @(127288000), - @"KE" : @(40046566), - @"KG" : @(5776500), - @"KH" : @(14453680), - @"KI" : @(92533), - @"KM" : @(773407), - @"KN" : @(51134), - @"KP" : @(22912177), - @"KR" : @(48422644), - @"KW" : @(2789132), - @"KY" : @(44270), - @"KZ" : @(15340000), - @"LA" : @(6368162), - @"LB" : @(4125247), - @"LC" : @(160922), - @"LI" : @(35000), - @"LK" : @(21513990), - @"LR" : @(3685076), - @"LS" : @(1919552), - @"LT" : @(2944459), - @"LU" : @(497538), - @"LV" : @(2217969), - @"LY" : @(6461454), - @"MA" : @(33848242), - @"MC" : @(32965), - @"MD" : @(4324000), - @"ME" : @(666730), - @"MF" : @(35925), - @"MG" : @(21281844), - @"MH" : @(65859), - @"MK" : @(2062294), - @"ML" : @(13796354), - @"MM" : @(53414374), - @"MN" : @(3086918), - @"MO" : @(449198), - @"MP" : @(53883), - @"MQ" : @(432900), - @"MR" : @(3205060), - @"MS" : @(9341), - @"MT" : @(403000), - @"MU" : @(1294104), - @"MV" : @(395650), - @"MW" : @(15447500), - @"MX" : @(112468855), - @"MY" : @(28274729), - @"MZ" : @(22061451), - @"NA" : @(2128471), - @"NC" : @(216494), - @"NE" : @(15878271), - @"NF" : @(1828), - @"NG" : @(154000000), - @"NI" : @(5995928), - @"NL" : @(16645000), - @"NO" : @(5009150), - @"NP" : @(28951852), - @"NR" : @(10065), - @"NU" : @(2166), - @"NZ" : @(4252277), - @"OM" : @(2967717), - @"PA" : @(3410676), - @"PE" : @(29907003), - @"PF" : @(270485), - @"PG" : @(6064515), - @"PH" : @(99900177), - @"PK" : @(184404791), - @"PL" : @(38500000), - @"PM" : @(7012), - @"PN" : @(46), - @"PR" : @(3916632), - @"PS" : @(3800000), - @"PT" : @(10676000), - @"PW" : @(19907), - @"PY" : @(6375830), - @"QA" : @(840926), - @"RE" : @(776948), - @"RO" : @(21959278), - @"RS" : @(7344847), - @"RU" : @(140702000), - @"RW" : @(11055976), - @"SA" : @(25731776), - @"SB" : @(559198), - @"SC" : @(88340), - @"SD" : @(35000000), - @"SE" : @(9828655), - @"SG" : @(4701069), - @"SH" : @(7460), - @"SI" : @(2007000), - @"SJ" : @(2550), - @"SK" : @(5455000), - @"SL" : @(5245695), - @"SM" : @(31477), - @"SN" : @(12323252), - @"SO" : @(10112453), - @"SR" : @(492829), - @"SS" : @(8260490), - @"ST" : @(175808), - @"SV" : @(6052064), - @"SX" : @(37429), - @"SY" : @(22198110), - @"SZ" : @(1354051), - @"TC" : @(20556), - @"TD" : @(10543464), - @"TF" : @(140), - @"TG" : @(6587239), - @"TH" : @(67089500), - @"TJ" : @(7487489), - @"TK" : @(1466), - @"TL" : @(1154625), - @"TM" : @(4940916), - @"TN" : @(10589025), - @"TO" : @(122580), - @"TR" : @(77804122), - @"TT" : @(1328019), - @"TV" : @(10472), - @"TW" : @(22894384), - @"TZ" : @(41892895), - @"UA" : @(45415596), - @"UG" : @(33398682), - @"UM" : @(0), - @"US" : @(310232863), - @"UY" : @(3477000), - @"UZ" : @(27865738), - @"VA" : @(921), - @"VC" : @(104217), - @"VE" : @(27223228), - @"VG" : @(21730), - @"VI" : @(108708), - @"VN" : @(89571130), - @"VU" : @(221552), - @"WF" : @(16025), - @"WS" : @(192001), - @"XK" : @(1800000), - @"YE" : @(23495361), - @"YT" : @(159042), - @"ZA" : @(49000000), - @"ZM" : @(13460305), - @"ZW" : @(13061000), - }; - }); - return instance; -} - -- (NSArray *)countryCodesSortedByPopulationDescending -{ - NSDictionary *countryCodeToPopulationMap = [self countryCodeToPopulationMap]; - NSArray *result = [NSLocale.ISOCountryCodes - sortedArrayUsingComparator:^NSComparisonResult(NSString *_Nonnull left, NSString *_Nonnull right) { - int leftPopulation = [countryCodeToPopulationMap[left] intValue]; - int rightPopulation = [countryCodeToPopulationMap[right] intValue]; - // Invert the values for a descending sort. - return [@(-leftPopulation) compare:@(-rightPopulation)]; - }]; - return result; -} - -- (NSArray *)countryCodesFromCallingCode:(NSString *)callingCode -{ - @synchronized(self) - { - OWSAssertDebug(callingCode.length > 0); - - NSArray *result = self.countryCodesFromCallingCodeCache[callingCode]; - if (!result) { - NSMutableArray *countryCodes = [NSMutableArray new]; - for (NSString *countryCode in [self countryCodesSortedByPopulationDescending]) { - NSString *callingCodeForCountryCode = [PhoneNumberUtil callingCodeFromCountryCode:countryCode]; - if ([callingCode isEqualToString:callingCodeForCountryCode]) { - [countryCodes addObject:countryCode]; - } - } - result = [countryCodes copy]; - self.countryCodesFromCallingCodeCache[callingCode] = result; - } - return result; - } -} - -- (NSString *)probableCountryCodeForCallingCode:(NSString *)callingCode -{ - OWSAssertDebug(callingCode.length > 0); - - NSArray *countryCodes = [self countryCodesFromCallingCode:callingCode]; - return (countryCodes.count > 0 ? countryCodes[0] : nil); -} - -+ (BOOL)name:(NSString *)nameString matchesQuery:(NSString *)queryString { - NSCharacterSet *whitespaceSet = NSCharacterSet.whitespaceCharacterSet; - NSArray *queryStrings = [queryString componentsSeparatedByCharactersInSet:whitespaceSet]; - NSArray *nameStrings = [nameString componentsSeparatedByCharactersInSet:whitespaceSet]; - - return [queryStrings all:^int(NSString *query) { - if (query.length == 0) - return YES; - return [nameStrings any:^int(NSString *nameWord) { - NSStringCompareOptions searchOpts = NSCaseInsensitiveSearch | NSAnchoredSearch; - return [nameWord rangeOfString:query options:searchOpts].location != NSNotFound; - }]; - }]; -} - -// search term -> country codes -+ (NSArray *)countryCodesForSearchTerm:(nullable NSString *)searchTerm { - searchTerm = [searchTerm stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceCharacterSet]]; - - NSArray *countryCodes = NSLocale.ISOCountryCodes; - - countryCodes = [countryCodes filter:^int(NSString *countryCode) { - NSString *countryName = [self countryNameFromCountryCode:countryCode]; - NSString *callingCode = [self callingCodeFromCountryCode:countryCode]; - - if (countryName.length < 1 || callingCode.length < 1 || [callingCode isEqualToString:@"+0"]) { - // Filter out countries without a valid calling code. - return NO; - } - - if (searchTerm.length < 1) { - return YES; - } - - if ([self name:countryName matchesQuery:searchTerm]) { - return YES; - } - - if ([self name:countryCode matchesQuery:searchTerm]) { - return YES; - } - - // We rely on the already internationalized string; as that is what - // the user would see entered (i.e. with COUNTRY_CODE_PREFIX). - - if ([callingCode containsString:searchTerm]) { - return YES; - } - - return NO; - }]; - - return [self sortedCountryCodesByName:countryCodes]; -} - -+ (NSArray *)sortedCountryCodesByName:(NSArray *)countryCodesByISOCode { - return [countryCodesByISOCode sortedArrayUsingComparator:^NSComparisonResult(id obj1, id obj2) { - return [[self countryNameFromCountryCode:obj1] caseInsensitiveCompare:[self countryNameFromCountryCode:obj2]]; - }]; -} - -// black magic -+ (NSUInteger)translateCursorPosition:(NSUInteger)offset - from:(NSString *)source - to:(NSString *)target - stickingRightward:(bool)preferHigh { - OWSAssertDebug(source != nil); - OWSAssertDebug(target != nil); - OWSAssertDebug(offset <= source.length); - - NSUInteger n = source.length; - NSUInteger m = target.length; - - int moves[n + 1][m + 1]; - { - // Wagner-Fischer algorithm for computing edit distance, with a tweaks: - // - Tracks best moves at each location, to allow reconstruction of edit path - // - Does not allow substitutions - // - Over-values digits relative to other characters, so they're "harder" to delete or insert - const int DIGIT_VALUE = 10; - NSUInteger scores[n + 1][m + 1]; - moves[0][0] = 0; // (match) move up and left - scores[0][0] = 0; - for (NSUInteger i = 1; i <= n; i++) { - scores[i][0] = i; - moves[i][0] = -1; // (deletion) move left - } - for (NSUInteger j = 1; j <= m; j++) { - scores[0][j] = j; - moves[0][j] = +1; // (insertion) move up - } - - NSCharacterSet *digits = NSCharacterSet.decimalDigitCharacterSet; - for (NSUInteger i = 1; i <= n; i++) { - unichar c1 = [source characterAtIndex:i - 1]; - bool isDigit1 = [digits characterIsMember:c1]; - for (NSUInteger j = 1; j <= m; j++) { - unichar c2 = [target characterAtIndex:j - 1]; - bool isDigit2 = [digits characterIsMember:c2]; - if (c1 == c2) { - scores[i][j] = scores[i - 1][j - 1]; - moves[i][j] = 0; // move up-and-left - } else { - NSUInteger del = scores[i - 1][j] + (isDigit1 ? DIGIT_VALUE : 1); - NSUInteger ins = scores[i][j - 1] + (isDigit2 ? DIGIT_VALUE : 1); - bool isDel = del < ins; - scores[i][j] = isDel ? del : ins; - moves[i][j] = isDel ? -1 : +1; - } - } - } - } - - // Backtrack to find desired corresponding offset - for (NSUInteger i = n, j = m;; i -= 1) { - if (i == offset && preferHigh) - return j; // early exit - while (moves[i][j] == +1) - j -= 1; // zip upward - if (i == offset) - return j; // late exit - if (moves[i][j] == 0) - j -= 1; - } -} - -+ (NSString *)examplePhoneNumberForCountryCode:(NSString *)countryCode -{ - PhoneNumberUtil *sharedUtil = [self sharedThreadLocal]; - - // Signal users are very likely using mobile devices, so prefer that kind of example. - NSError *error; - NBPhoneNumber *nbPhoneNumber = - [sharedUtil.nbPhoneNumberUtil getExampleNumberForType:countryCode type:NBEPhoneNumberTypeMOBILE error:&error]; - OWSAssertDebug(!error); - if (!nbPhoneNumber) { - // For countries that with similar mobile and land lines, use "line or mobile" - // examples. - nbPhoneNumber = [sharedUtil.nbPhoneNumberUtil getExampleNumberForType:countryCode - type:NBEPhoneNumberTypeFIXED_LINE_OR_MOBILE - error:&error]; - OWSAssertDebug(!error); - } - NSString *result = (nbPhoneNumber - ? [sharedUtil.nbPhoneNumberUtil format:nbPhoneNumber numberFormat:NBEPhoneNumberFormatE164 error:&error] - : nil); - OWSAssertDebug(!error); - return result; -} - -@end - -NS_ASSUME_NONNULL_END diff --git a/SignalUtilitiesKit/Poller.swift b/SignalUtilitiesKit/Poller.swift index 8387361ca..9a819c981 100644 --- a/SignalUtilitiesKit/Poller.swift +++ b/SignalUtilitiesKit/Poller.swift @@ -93,7 +93,7 @@ public final class Poller : NSObject { print("[Loki] Received \(messages.count) new message(s).") } messages.forEach { json in - guard let envelope = SSKProtoEnvelope.from(json) else { return } + guard let envelope = SNProtoEnvelope.from(json) else { return } do { let data = try envelope.serializedData() let job = MessageReceiveJob(data: data) diff --git a/SignalUtilitiesKit/PreKeyRefreshOperation.swift b/SignalUtilitiesKit/PreKeyRefreshOperation.swift index a23349651..ed440fabe 100644 --- a/SignalUtilitiesKit/PreKeyRefreshOperation.swift +++ b/SignalUtilitiesKit/PreKeyRefreshOperation.swift @@ -16,10 +16,6 @@ public class RefreshPreKeysOperation: OWSOperation { return TSAccountManager.sharedInstance() } - private var accountServiceClient: AccountServiceClient { - return AccountServiceClient.shared - } - private var primaryStorage: OWSPrimaryStorage { return OWSPrimaryStorage.shared() } @@ -36,48 +32,10 @@ public class RefreshPreKeysOperation: OWSOperation { return } - // Loki: Doing this on the global queue to match Signal DispatchQueue.global().async { SessionManagementProtocol.refreshSignedPreKey() self.reportSuccess() } - - /* Loki: Original code - * ================ - firstly { - self.accountServiceClient.getPreKeysCount() - }.then(on: DispatchQueue.global()) { preKeysCount -> Promise in - Logger.debug("preKeysCount: \(preKeysCount)") - guard preKeysCount < kEphemeralPreKeysMinimumCount || self.primaryStorage.currentSignedPrekeyId() == nil else { - Logger.debug("Available keys sufficient: \(preKeysCount)") - return Promise.value(()) - } - - let identityKey: Data = self.identityKeyManager.identityKeyPair()!.publicKey - let signedPreKeyRecord: SignedPreKeyRecord = self.primaryStorage.generateRandomSignedRecord() - let preKeyRecords: [PreKeyRecord] = self.primaryStorage.generatePreKeyRecords() - - self.primaryStorage.storeSignedPreKey(signedPreKeyRecord.id, signedPreKeyRecord: signedPreKeyRecord) - self.primaryStorage.storePreKeyRecords(preKeyRecords) - - return firstly { - self.accountServiceClient.setPreKeys(identityKey: identityKey, signedPreKeyRecord: signedPreKeyRecord, preKeyRecords: preKeyRecords) - }.done { - signedPreKeyRecord.markAsAcceptedByService() - self.primaryStorage.storeSignedPreKey(signedPreKeyRecord.id, signedPreKeyRecord: signedPreKeyRecord) - self.primaryStorage.setCurrentSignedPrekeyId(signedPreKeyRecord.id) - - TSPreKeyManager.clearPreKeyUpdateFailureCount() - TSPreKeyManager.clearSignedPreKeyRecords() - } - }.done { - Logger.debug("done") - self.reportSuccess() - }.catch { error in - self.reportError(error) - }.retainUntilComplete() - * ================ - */ } public override func didSucceed() { @@ -85,21 +43,6 @@ public class RefreshPreKeysOperation: OWSOperation { } override public func didFail(error: Error) { - switch error { - case let networkManagerError as NetworkManagerError: - guard !networkManagerError.isNetworkError else { - Logger.debug("Don't report SPK rotation failure w/ network error") - return - } - - guard networkManagerError.statusCode >= 400 && networkManagerError.statusCode <= 599 else { - Logger.debug("Don't report SPK rotation failure w/ non application error") - return - } - - TSPreKeyManager.incrementPreKeyUpdateFailureCount() - default: - Logger.debug("Don't report SPK rotation failure w/ non NetworkManager error: \(error)") - } + Logger.debug("Don't report SPK rotation failure w/ non NetworkManager error: \(error)") } } diff --git a/SignalUtilitiesKit/ProfileFetcherJob.swift b/SignalUtilitiesKit/ProfileFetcherJob.swift deleted file mode 100644 index 89e11bf99..000000000 --- a/SignalUtilitiesKit/ProfileFetcherJob.swift +++ /dev/null @@ -1,241 +0,0 @@ -// -// Copyright (c) 2019 Open Whisper Systems. All rights reserved. -// - -import Foundation -import PromiseKit - -@objc -public class ProfileFetcherJob: NSObject { - - // This property is only accessed on the main queue. - static var fetchDateMap = [String: Date]() - - let ignoreThrottling: Bool - - var backgroundTask: OWSBackgroundTask? - - @objc - public class func run(thread: TSThread) { - guard CurrentAppContext().isMainApp else { - return - } - - ProfileFetcherJob().run(recipientIds: thread.recipientIdentifiers) - } - - @objc - public class func run(recipientId: String, ignoreThrottling: Bool) { - guard CurrentAppContext().isMainApp else { - return - } - - ProfileFetcherJob(ignoreThrottling: ignoreThrottling).run(recipientIds: [recipientId]) - } - - public init(ignoreThrottling: Bool = false) { - self.ignoreThrottling = ignoreThrottling - } - - // MARK: - Dependencies - - private var networkManager: TSNetworkManager { - return SSKEnvironment.shared.networkManager - } - - private var socketManager: TSSocketManager { - return TSSocketManager.shared - } - - private var primaryStorage: OWSPrimaryStorage { - return SSKEnvironment.shared.primaryStorage - } - - private var udManager: OWSUDManager { - return SSKEnvironment.shared.udManager - } - - private var profileManager: OWSProfileManager { - return OWSProfileManager.shared() - } - - private var identityManager: OWSIdentityManager { - return SSKEnvironment.shared.identityManager - } - - private var signalServiceClient: SignalServiceClient { - // TODO hang on SSKEnvironment - return SignalServiceRestClient() - } - - private var tsAccountManager: TSAccountManager { - return SSKEnvironment.shared.tsAccountManager - } - - // MARK: - - - public func run(recipientIds: [String]) { - AssertIsOnMainThread() - - /* Loki: Original code - * Disabled as we don't have an endpoint for fetching profiles - * ================ - guard CurrentAppContext().isMainApp else { - // Only refresh profiles in the MainApp to decrease the chance of missed SN notifications - // in the AppExtension for our users who choose not to verify contacts. - owsFailDebug("Should only fetch profiles in the main app") - return - } - - backgroundTask = OWSBackgroundTask(label: "\(#function)", completionBlock: { [weak self] status in - AssertIsOnMainThread() - - guard status == .expired else { - return - } - guard let _ = self else { - return - } - Logger.error("background task time ran out before profile fetch completed.") - }) - - DispatchQueue.global().async { - for recipientId in recipientIds { - self.getAndUpdateProfile(recipientId: recipientId) - } - } - * ================ - */ - } - - enum ProfileFetcherJobError: Error { - case throttled(lastTimeInterval: TimeInterval) - } - - public func getAndUpdateProfile(recipientId: String, remainingRetries: Int = 3) { - self.getProfile(recipientId: recipientId).map(on: DispatchQueue.global()) { profile in - self.updateProfile(signalServiceProfile: profile) - }.catch(on: DispatchQueue.global()) { error in - switch error { - case ProfileFetcherJobError.throttled(let lastTimeInterval): - // skipping - break - case let error as SignalServiceProfile.ValidationError: - Logger.warn("skipping updateProfile retry. Invalid profile for: \(recipientId) error: \(error)") - default: - if remainingRetries > 0 { - self.getAndUpdateProfile(recipientId: recipientId, remainingRetries: remainingRetries - 1) - } else { - Logger.error("failed to get profile with error: \(error)") - } - } - }.retainUntilComplete() - } - - public func getProfile(recipientId: String) -> Promise { - if !ignoreThrottling { - if let lastDate = ProfileFetcherJob.fetchDateMap[recipientId] { - let lastTimeInterval = fabs(lastDate.timeIntervalSinceNow) - // Don't check a profile more often than every N seconds. - // - // Throttle less in debug to make it easier to test problems - // with our fetching logic. - let kGetProfileMaxFrequencySeconds = _isDebugAssertConfiguration() ? 60 : 60.0 * 5.0 - guard lastTimeInterval > kGetProfileMaxFrequencySeconds else { - return Promise(error: ProfileFetcherJobError.throttled(lastTimeInterval: lastTimeInterval)) - } - } - } - ProfileFetcherJob.fetchDateMap[recipientId] = Date() - - Logger.error("getProfile: \(recipientId)") - - // Don't use UD for "self" profile fetches. - var udAccess: OWSUDAccess? - if recipientId != tsAccountManager.localNumber() { - udAccess = udManager.udAccess(forRecipientId: recipientId, - requireSyncAccess: false) - } - - return requestProfile(recipientId: recipientId, - udAccess: udAccess, - canFailoverUDAuth: true) - } - - private func requestProfile(recipientId: String, - udAccess: OWSUDAccess?, - canFailoverUDAuth: Bool) -> Promise { - let requestMaker = RequestMaker(label: "Profile Fetch", - requestFactoryBlock: { (udAccessKeyForRequest) -> TSRequest in - return OWSRequestFactory.getProfileRequest(recipientId: recipientId, udAccessKey: udAccessKeyForRequest) - }, udAuthFailureBlock: { - // Do nothing - }, websocketFailureBlock: { - // Do nothing - }, recipientId: recipientId, - udAccess: udAccess, - canFailoverUDAuth: canFailoverUDAuth) - return requestMaker.makeRequest() - .map(on: DispatchQueue.global()) { (result: RequestMakerResult) -> SignalServiceProfile in - try SignalServiceProfile(recipientId: recipientId, responseObject: result.responseObject) - } - } - - private func updateProfile(signalServiceProfile: SignalServiceProfile) { - let recipientId = signalServiceProfile.recipientId - verifyIdentityUpToDateAsync(recipientId: recipientId, latestIdentityKey: signalServiceProfile.identityKey) - - profileManager.updateProfile(forRecipientId: recipientId, - profileNameEncrypted: signalServiceProfile.profileNameEncrypted, - avatarUrlPath: signalServiceProfile.avatarUrlPath) - - updateUnidentifiedAccess(recipientId: recipientId, - verifier: signalServiceProfile.unidentifiedAccessVerifier, - hasUnrestrictedAccess: signalServiceProfile.hasUnrestrictedUnidentifiedAccess) - } - - private func updateUnidentifiedAccess(recipientId: String, verifier: Data?, hasUnrestrictedAccess: Bool) { - guard let verifier = verifier else { - // If there is no verifier, at least one of this user's devices - // do not support UD. - udManager.setUnidentifiedAccessMode(.disabled, recipientId: recipientId) - return - } - - if hasUnrestrictedAccess { - udManager.setUnidentifiedAccessMode(.unrestricted, recipientId: recipientId) - return - } - - guard let udAccessKey = udManager.udAccessKey(forRecipientId: recipientId) else { - udManager.setUnidentifiedAccessMode(.disabled, recipientId: recipientId) - return - } - - let dataToVerify = Data(count: 32) - guard let expectedVerifier = Cryptography.computeSHA256HMAC(dataToVerify, withHMACKey: udAccessKey.keyData) else { - owsFailDebug("could not compute verification") - udManager.setUnidentifiedAccessMode(.disabled, recipientId: recipientId) - return - } - - guard expectedVerifier.ows_constantTimeIsEqual(to: verifier) else { - Logger.verbose("verifier mismatch, new profile key?") - udManager.setUnidentifiedAccessMode(.disabled, recipientId: recipientId) - return - } - - udManager.setUnidentifiedAccessMode(.enabled, recipientId: recipientId) - } - - private func verifyIdentityUpToDateAsync(recipientId: String, latestIdentityKey: Data) { - primaryStorage.newDatabaseConnection().asyncReadWrite { (transaction) in - if self.identityManager.saveRemoteIdentity(latestIdentityKey, recipientId: recipientId, protocolContext: transaction) { - Logger.info("updated identity key with fetched profile for recipient: \(recipientId)") - self.primaryStorage.archiveAllSessions(forContact: recipientId, protocolContext: transaction) - } else { - // no change in identity. - } - } - } -} diff --git a/SignalUtilitiesKit/Promise+retainUntilComplete.swift b/SignalUtilitiesKit/Promise+retainUntilComplete.swift deleted file mode 100644 index 34a9f60c0..000000000 --- a/SignalUtilitiesKit/Promise+retainUntilComplete.swift +++ /dev/null @@ -1,63 +0,0 @@ -// -// Copyright (c) 2018 Open Whisper Systems. All rights reserved. -// - -import PromiseKit - -@objc -public extension AnyPromise { - /** - * Sometimes there isn't a straight forward candidate to retain a promise, in that case we tell the - * promise to self retain, until it completes to avoid the risk it's GC'd before completion. - */ - @objc - func retainUntilComplete() { - var retainCycle: AnyPromise? = self - _ = self.ensure { - assert(retainCycle != nil) - retainCycle = nil - } - } -} - -public extension PMKFinalizer { - /** - * Sometimes there isn't a straight forward candidate to retain a promise, in that case we tell the - * promise to self retain, until it completes to avoid the risk it's GC'd before completion. - */ - func retainUntilComplete() { - var retainCycle: PMKFinalizer? = self - _ = self.finally { - assert(retainCycle != nil) - retainCycle = nil - } - } -} - -public extension Promise { - /** - * Sometimes there isn't a straight forward candidate to retain a promise, in that case we tell the - * promise to self retain, until it completes to avoid the risk it's GC'd before completion. - */ - func retainUntilComplete() { - var retainCycle: Promise? = self - _ = self.ensure { - assert(retainCycle != nil) - retainCycle = nil - } - } -} - -public extension Guarantee { - /** - * Sometimes there isn't a straight forward candidate to retain a promise, in that case we tell the - * promise to self retain, until it completes to avoid the risk it's GC'd before completion. - */ - func retainUntilComplete() { - var retainCycle: Guarantee? = self - _ = self.done { _ in - assert(retainCycle != nil) - retainCycle = nil - } - } -} diff --git a/SignalUtilitiesKit/ProofOfWork.swift b/SignalUtilitiesKit/ProofOfWork.swift deleted file mode 100644 index cbcfb5e4e..000000000 --- a/SignalUtilitiesKit/ProofOfWork.swift +++ /dev/null @@ -1,109 +0,0 @@ -import CryptoSwift - -private extension UInt64 { - - fileprivate init(_ decimal: Decimal) { - self.init(truncating: decimal as NSDecimalNumber) - } - - // Convert a UInt8 array to a UInt64 - fileprivate init(_ bytes: [UInt8]) { - precondition(bytes.count <= MemoryLayout.size) - var value: UInt64 = 0 - for byte in bytes { - value <<= 8 - value |= UInt64(byte) - } - self.init(value) - } -} - -private extension MutableCollection where Element == UInt8, Index == Int { - - /// Increment every element by the given amount - /// - /// - Parameter amount: The amount to increment by - /// - Returns: The incremented collection - fileprivate func increment(by amount: Int) -> Self { - var result = self - var increment = amount - for i in (0.. 0 else { break } - let sum = Int(result[i]) + increment - result[i] = UInt8(sum % 256) - increment = sum / 256 - } - return result - } -} - -/** - * The main proof of work logic. - * - * This was copied from the desktop messenger. - * Ref: libloki/proof-of-work.js - */ -public enum ProofOfWork { - - // If this changes then we also have to use something other than UInt64 to support the new length - private static let nonceLength = 8 - - /// Calculate a proof of work with the given configuration - /// - /// Ref: https://bitmessage.org/wiki/Proof_of_work - /// - /// - Parameters: - /// - data: The message data - /// - pubKey: The message recipient - /// - timestamp: The timestamp - /// - ttl: The message time to live in milliseconds - /// - Returns: A nonce string or `nil` if it failed - public static func calculate(data: String, pubKey: String, timestamp: UInt64, ttl: UInt64) -> String? { - let payload = createPayload(pubKey: pubKey, data: data, timestamp: timestamp, ttl: ttl) - let target = calcTarget(ttl: ttl, payloadLength: payload.count, nonceTrials: Int(SnodeAPI.powDifficulty)) - - // Start with the max value - var trialValue = UInt64.max - - let initialHash = payload.sha512() - var nonce = [UInt8](repeating: 0, count: nonceLength) - - while trialValue > target { - nonce = nonce.increment(by: 1) - - // This is different to the bitmessage PoW - // resultHash = hash(nonce + hash(data)) ==> hash(nonce + initialHash) - let resultHash = (nonce + initialHash).sha512() - let trialValueArray = Array(resultHash[0..<8]) - trialValue = UInt64(trialValueArray) - } - - return nonce.toBase64() - } - - /// Get the proof of work payload - private static func createPayload(pubKey: String, data: String, timestamp: UInt64, ttl: UInt64) -> [UInt8] { - let timestampString = String(timestamp) - let ttlString = String(ttl) - let payloadString = timestampString + ttlString + pubKey + data - return payloadString.bytes - } - - /// Calculate the target we need to reach - private static func calcTarget(ttl: UInt64, payloadLength: Int, nonceTrials: Int) -> UInt64 { - let two16 = UInt64(pow(2, 16) - 1) - let two64 = UInt64(pow(2, 64) - 1) - - // Do all the calculations - let totalLength = UInt64(payloadLength + nonceLength) - let ttlInSeconds = ttl / 1000 - let ttlMult = ttlInSeconds * totalLength - - // UInt64 values - let innerFrac = ttlMult / two16 - let lenPlusInnerFrac = totalLength + innerFrac - let denominator = UInt64(nonceTrials) * lenPlusInnerFrac - - return two64 / denominator - } -} diff --git a/SignalUtilitiesKit/PublicChatPoller.swift b/SignalUtilitiesKit/PublicChatPoller.swift index 9d1dbf10c..27ca5dc4d 100644 --- a/SignalUtilitiesKit/PublicChatPoller.swift +++ b/SignalUtilitiesKit/PublicChatPoller.swift @@ -61,171 +61,102 @@ public final class PublicChatPoller : NSObject { let userPublicKey = getUserHexEncodedPublicKey() return OpenGroupAPI.getMessages(for: publicChat.channel, on: publicChat.server).done(on: DispatchQueue.global(qos: .default)) { messages in self.isPolling = false - let uniquePublicKeys = Set(messages.map { $0.senderPublicKey }) - func proceed() { - let storage = OWSPrimaryStorage.shared() - /* - var newDisplayNameUpdatees: Set = [] - storage.dbReadConnection.read { transaction in - newDisplayNameUpdatees = Set(uniquePublicKeys.filter { storage.getMasterHexEncodedPublicKey(for: $0, in: transaction) != $0 }.compactMap { storage.getMasterHexEncodedPublicKey(for: $0, in: transaction) }) + let storage = OWSPrimaryStorage.shared() + // Sorting the messages by timestamp before importing them fixes an issue where messages that quote older messages can't find those older messages + messages.sorted { $0.serverTimestamp < $1.serverTimestamp }.forEach { message in + let senderPublicKey = message.senderPublicKey + var wasSentByCurrentUser = (senderPublicKey == getUserHexEncodedPublicKey()) + func generateDisplayName(from rawDisplayName: String) -> String { + let endIndex = senderPublicKey.endIndex + let cutoffIndex = senderPublicKey.index(endIndex, offsetBy: -8) + return "\(rawDisplayName) (...\(senderPublicKey[cutoffIndex.. String { - let endIndex = senderPublicKey.endIndex - let cutoffIndex = senderPublicKey.index(endIndex, offsetBy: -8) - return "\(rawDisplayName) (...\(senderPublicKey[cutoffIndex.. 0) { - SSKEnvironment.shared.profileManager.updateProfileForContact(withID: masterPublicKey!, displayName: message.displayName, with: transaction) - } - SSKEnvironment.shared.profileManager.updateService(withProfileName: message.displayName, avatarURL: profilePicture.url) - SSKEnvironment.shared.profileManager.setProfileKeyData(profilePicture.profileKey, forRecipientId: masterPublicKey!, avatarURL: profilePicture.url) - } + dataMessage.setAttachments(attachments) + if let linkPreview = message.attachments.first(where: { $0.kind == .linkPreview }) { + let signalLinkPreview = SSKProtoDataMessagePreview.builder(url: linkPreview.linkPreviewURL!) + signalLinkPreview.setTitle(linkPreview.linkPreviewTitle!) + let attachment = SSKProtoAttachmentPointer.builder(id: linkPreview.serverID) + attachment.setContentType(linkPreview.contentType) + attachment.setSize(UInt32(linkPreview.size)) + attachment.setFileName(linkPreview.fileName) + attachment.setFlags(UInt32(linkPreview.flags)) + attachment.setWidth(UInt32(linkPreview.width)) + attachment.setHeight(UInt32(linkPreview.height)) + if let caption = linkPreview.caption { + attachment.setCaption(caption) } + attachment.setUrl(linkPreview.url) + signalLinkPreview.setImage(try! attachment.build()) + dataMessage.setPreview([ try! signalLinkPreview.build() ]) } - } - /* - let hexEncodedPublicKeysToUpdate = uniquePublicKeys.filter { hexEncodedPublicKey in - let timeSinceLastUpdate: TimeInterval - if let lastDeviceLinkUpdate = MultiDeviceProtocol.lastDeviceLinkUpdate[hexEncodedPublicKey] { - timeSinceLastUpdate = Date().timeIntervalSince(lastDeviceLinkUpdate) + let profile = SSKProtoDataMessageLokiProfile.builder() + profile.setDisplayName(message.displayName) + if let profilePicture = message.profilePicture { + profile.setProfilePicture(profilePicture.url) + dataMessage.setProfileKey(profilePicture.profileKey) + } + dataMessage.setProfile(try! profile.build()) + dataMessage.setTimestamp(message.timestamp) + dataMessage.setGroup(try! groupContext.build()) + if let quote = message.quote { + let signalQuote = SSKProtoDataMessageQuote.builder(id: quote.quotedMessageTimestamp, author: quote.quoteePublicKey) + signalQuote.setText(quote.quotedMessageBody) + dataMessage.setQuote(try! signalQuote.build()) + } + let body = (message.body == message.timestamp.description) ? "" : message.body // Workaround for the fact that the back-end doesn't accept messages without a body + dataMessage.setBody(body) + if let messageServerID = message.serverID { + let publicChatInfo = SSKProtoPublicChatInfo.builder() + publicChatInfo.setServerID(messageServerID) + dataMessage.setPublicChatInfo(try! publicChatInfo.build()) + } + let content = SSKProtoContent.builder() + if !wasSentByCurrentUser { + content.setDataMessage(try! dataMessage.build()) } else { - timeSinceLastUpdate = .infinity + let syncMessageSentBuilder = SSKProtoSyncMessageSent.builder() + syncMessageSentBuilder.setMessage(try! dataMessage.build()) + syncMessageSentBuilder.setDestination(userPublicKey) + syncMessageSentBuilder.setTimestamp(message.timestamp) + let syncMessageSent = try! syncMessageSentBuilder.build() + let syncMessageBuilder = SSKProtoSyncMessage.builder() + syncMessageBuilder.setSent(syncMessageSent) + content.setSyncMessage(try! syncMessageBuilder.build()) } - return timeSinceLastUpdate > MultiDeviceProtocol.deviceLinkUpdateInterval - } - if !hexEncodedPublicKeysToUpdate.isEmpty { - FileServerAPI.getDeviceLinks(associatedWith: hexEncodedPublicKeysToUpdate).done(on: DispatchQueue.global(qos: .default)) { _ in - proceed() - hexEncodedPublicKeysToUpdate.forEach { - MultiDeviceProtocol.lastDeviceLinkUpdate[$0] = Date() // TODO: Doing this from a global queue seems a bit iffy + let envelope = SSKProtoEnvelope.builder(type: .ciphertext, timestamp: message.timestamp) + envelope.setSource(senderPublicKey) + envelope.setSourceDevice(1) + envelope.setContent(try! content.build().serializedData()) + envelope.setServerTimestamp(message.serverTimestamp) + Storage.writeSync { transaction in + transaction.setObject(senderDisplayName, forKey: senderPublicKey, inCollection: publicChat.id) + let messageServerID = message.serverID + let job = MessageReceiveJob(data: try! envelope.buildSerializedData(), messageServerID: messageServerID) + Storage.write { transaction in + SessionMessagingKit.JobQueue.shared.add(job, using: transaction) } - }.catch(on: DispatchQueue.global(qos: .default)) { error in - if (error as? DotNetAPI.DotNetAPIError) == DotNetAPI.DotNetAPIError.parsingFailed { - // Don't immediately re-fetch in case of failure due to a parsing error - hexEncodedPublicKeysToUpdate.forEach { - MultiDeviceProtocol.lastDeviceLinkUpdate[$0] = Date() // TODO: Doing this from a global queue seems a bit iffy - } - } - proceed() } - } else { - */ - DispatchQueue.global(qos: .default).async { - proceed() - } - /* } - */ } } diff --git a/SignalUtilitiesKit/ContactCellView.h b/SignalUtilitiesKit/Remove Later/ContactCellView.h similarity index 100% rename from SignalUtilitiesKit/ContactCellView.h rename to SignalUtilitiesKit/Remove Later/ContactCellView.h diff --git a/SignalUtilitiesKit/ContactCellView.m b/SignalUtilitiesKit/Remove Later/ContactCellView.m similarity index 96% rename from SignalUtilitiesKit/ContactCellView.m rename to SignalUtilitiesKit/Remove Later/ContactCellView.m index 0819889f7..38a45d2cb 100644 --- a/SignalUtilitiesKit/ContactCellView.m +++ b/SignalUtilitiesKit/Remove Later/ContactCellView.m @@ -3,8 +3,6 @@ // #import "ContactCellView.h" -#import "OWSContactAvatarBuilder.h" -#import "OWSContactsManager.h" #import "UIFont+OWS.h" #import "UIView+OWS.h" #import @@ -48,13 +46,6 @@ const CGFloat kContactCellAvatarTextMargin = 12; #pragma mark - Dependencies -- (OWSContactsManager *)contactsManager -{ - OWSAssertDebug(Environment.shared.contactsManager); - - return Environment.shared.contactsManager; -} - - (OWSPrimaryStorage *)primaryStorage { OWSAssertDebug(SSKEnvironment.shared.primaryStorage); @@ -150,8 +141,7 @@ const CGFloat kContactCellAvatarTextMargin = 12; NSFontAttributeName : self.nameLabel.font, }]; } else { - self.nameLabel.attributedText = - [self.contactsManager formattedFullNameForRecipientId:recipientId font:self.nameLabel.font]; + self.nameLabel.text = [SSKEnvironment.shared.profileManager profileNameForRecipientWithID:recipientId avoidingWriteTransaction:YES]; } [[NSNotificationCenter defaultCenter] addObserver:self diff --git a/SignalUtilitiesKit/ContactTableViewCell.h b/SignalUtilitiesKit/Remove Later/ContactTableViewCell.h similarity index 100% rename from SignalUtilitiesKit/ContactTableViewCell.h rename to SignalUtilitiesKit/Remove Later/ContactTableViewCell.h diff --git a/SignalUtilitiesKit/ContactTableViewCell.m b/SignalUtilitiesKit/Remove Later/ContactTableViewCell.m similarity index 100% rename from SignalUtilitiesKit/ContactTableViewCell.m rename to SignalUtilitiesKit/Remove Later/ContactTableViewCell.m diff --git a/SignalUtilitiesKit/OWSProfileManager.h b/SignalUtilitiesKit/Remove Later/OWSProfileManager.h similarity index 100% rename from SignalUtilitiesKit/OWSProfileManager.h rename to SignalUtilitiesKit/Remove Later/OWSProfileManager.h diff --git a/SignalUtilitiesKit/OWSProfileManager.m b/SignalUtilitiesKit/Remove Later/OWSProfileManager.m similarity index 94% rename from SignalUtilitiesKit/OWSProfileManager.m rename to SignalUtilitiesKit/Remove Later/OWSProfileManager.m index 3f1a35349..c503fffce 100644 --- a/SignalUtilitiesKit/OWSProfileManager.m +++ b/SignalUtilitiesKit/Remove Later/OWSProfileManager.m @@ -7,8 +7,6 @@ #import "OWSUserProfile.h" #import #import - - #import "UIUtil.h" #import #import @@ -18,15 +16,10 @@ #import #import #import -#import #import -#import -#import -#import #import #import #import -#import #import #import #import @@ -128,36 +121,16 @@ typedef void (^ProfileManagerFailureBlock)(NSError *error); return TSAccountManager.sharedInstance; } -- (AFHTTPSessionManager *)avatarHTTPManager -{ - return [OWSSignalService sharedInstance].CDNSessionManager; -} - - (OWSIdentityManager *)identityManager { return SSKEnvironment.shared.identityManager; } -- (SSKMessageSenderJobQueue *)messageSenderJobQueue -{ - return SSKEnvironment.shared.messageSenderJobQueue; -} - -- (TSNetworkManager *)networkManager -{ - return SSKEnvironment.shared.networkManager; -} - - (OWSBlockingManager *)blockingManager { return SSKEnvironment.shared.blockingManager; } -- (id)syncManager -{ - return SSKEnvironment.shared.syncManager; -} - - (id)udManager { OWSAssertDebug(SSKEnvironment.shared.udManager); @@ -249,22 +222,6 @@ typedef void (^ProfileManagerFailureBlock)(NSError *error); // Ensure that the success and failure blocks are called on the main thread. void (^failureBlock)(NSError *) = ^(NSError *error) { OWSLogError(@"Updating service with profile failed."); - - /* - // We use a "self-only" contact sync to indicate to desktop - // that we've changed our profile and that it should do a - // profile fetch for "self". - // - // NOTE: We also inform the desktop in the failure case, - // since that _may have_ affected service state. - if (requiresSync) { - [[self.syncManager syncLocalContact] retainUntilComplete]; - } - */ - - if (requiresSync) { - [LKSyncMessagesProtocol syncProfile]; - } dispatch_async(dispatch_get_main_queue(), ^{ failureBlockParameter(error); @@ -272,19 +229,6 @@ typedef void (^ProfileManagerFailureBlock)(NSError *error); }; void (^successBlock)(void) = ^{ OWSLogInfo(@"Successfully updated service with profile."); - - /* - // We use a "self-only" contact sync to indicate to desktop - // that we've changed our profile and that it should do a - // profile fetch for "self". - if (requiresSync) { - [[self.syncManager syncLocalContact] retainUntilComplete]; - } - */ - - if (requiresSync) { - [LKSyncMessagesProtocol syncProfile]; - } dispatch_async(dispatch_get_main_queue(), ^{ successBlockParameter(); @@ -483,24 +427,6 @@ typedef void (^ProfileManagerFailureBlock)(NSError *error); [self updateServiceWithProfileName:localProfileName avatarUrl:avatarURL success:^{} failure:^(NSError * _Nonnull error) {}]; } -- (void)fetchLocalUsersProfile -{ - OWSAssertIsOnMainThread(); - - NSString *_Nullable localNumber = self.tsAccountManager.localNumber; - if (!localNumber) { - return; - } - [self fetchProfileForRecipientId:localNumber]; -} - -- (void)fetchProfileForRecipientId:(NSString *)recipientId -{ - OWSAssertIsOnMainThread(); - - [ProfileFetcherJob runWithRecipientId:recipientId ignoreThrottling:YES]; -} - #pragma mark - Profile Key Rotation - (nullable NSString *)groupKeyForGroupId:(NSData *)groupId { @@ -735,11 +661,6 @@ typedef void (^ProfileManagerFailureBlock)(NSError *error); return @(1); }); - // Sync local profile key. - promise = promise.then(^(id value) { - return [self.syncManager syncLocalContact]; - }); - promise = promise.then(^(id value) { [[NSNotificationCenter defaultCenter] postNotificationNameAsync:kNSNotificationName_ProfileKeyDidChange object:nil @@ -1191,12 +1112,6 @@ typedef void (^ProfileManagerFailureBlock)(NSError *error); OWSLogError(@"avatar image for %@ could not be loaded.", userProfile.recipientId); } else { [self updateProfileAvatarCache:image filename:fileName]; - - [latestUserProfile updateWithAvatarFileName:fileName dbConnection:self.dbConnection completion:^{ - [[NSNotificationCenter defaultCenter] - postNotificationNameAsync:OWSContactsManagerSignalAccountsDidChangeNotification - object:nil]; - }]; } // If we're updating the profile that corresponds to our local number, @@ -1454,7 +1369,6 @@ typedef void (^ProfileManagerFailureBlock)(NSError *error); accessibilityIdentifier:ACCESSIBILITY_IDENTIFIER_WITH_NAME(self, @"share_profile") style:UIAlertActionStyleDefault handler:^(UIAlertAction *_Nonnull action) { - [self userAddedThreadToProfileWhitelist:thread]; successHandler(); }]]; [alert addAction:[OWSAlerts cancelAction]]; @@ -1462,27 +1376,6 @@ typedef void (^ProfileManagerFailureBlock)(NSError *error); [fromViewController presentAlert:alert]; } -- (void)userAddedThreadToProfileWhitelist:(TSThread *)thread -{ - OWSAssertIsOnMainThread(); - - BOOL isFeatureEnabled = NO; - if (!isFeatureEnabled) { - OWSLogWarn(@"skipping sending profile-key message because the feature is not yet fully available."); - [OWSProfileManager.sharedManager addThreadToProfileWhitelist:thread]; - return; - } - - // MJK TODO - should be safe to remove this senderTimestamp - OWSProfileKeyMessage *message = - [[OWSProfileKeyMessage alloc] initWithTimestamp:[NSDate ows_millisecondTimeStamp] inThread:thread]; - [OWSProfileManager.sharedManager addThreadToProfileWhitelist:thread]; - - [LKStorage writeSyncWithBlock:^(YapDatabaseReadWriteTransaction *_Nonnull transaction) { - [self.messageSenderJobQueue addMessage:message transaction:transaction]; - }]; -} - #pragma mark - Notifications - (void)applicationDidBecomeActive:(NSNotification *)notification diff --git a/SignalUtilitiesKit/OWSUserProfile.h b/SignalUtilitiesKit/Remove Later/OWSUserProfile.h similarity index 100% rename from SignalUtilitiesKit/OWSUserProfile.h rename to SignalUtilitiesKit/Remove Later/OWSUserProfile.h diff --git a/SignalUtilitiesKit/OWSUserProfile.m b/SignalUtilitiesKit/Remove Later/OWSUserProfile.m similarity index 96% rename from SignalUtilitiesKit/OWSUserProfile.m rename to SignalUtilitiesKit/Remove Later/OWSUserProfile.m index 973fe3295..bedb81b7d 100644 --- a/SignalUtilitiesKit/OWSUserProfile.m +++ b/SignalUtilitiesKit/Remove Later/OWSUserProfile.m @@ -118,11 +118,6 @@ NSString *const kLocalProfileUniqueId = @"kLocalProfileUniqueId"; #pragma mark - Dependencies -- (id)syncManager -{ - return SSKEnvironment.shared.syncManager; -} - - (TSAccountManager *)tsAccountManager { OWSAssertDebug(SSKEnvironment.shared.tsAccountManager); @@ -250,16 +245,6 @@ NSString *const kLocalProfileUniqueId = @"kLocalProfileUniqueId"; dispatch_async(dispatch_get_main_queue(), ^{ if (isLocalUserProfile) { - // We populate an initial (empty) profile on launch of a new install, but until - // we have a registered account, syncing will fail (and there could not be any - // linked device to sync to at this point anyway). - - NSUserDefaults *userDefaults = NSUserDefaults.standardUserDefaults; - BOOL hasLaunchedOnce = [userDefaults boolForKey:@"hasLaunchedOnce"]; - if ([self.tsAccountManager isRegistered] && CurrentAppContext().isMainApp && hasLaunchedOnce) { - [[self.syncManager syncLocalContact] retainUntilComplete]; - } - [[NSNotificationCenter defaultCenter] postNotificationNameAsync:kNSNotificationName_LocalProfileDidChange object:nil userInfo:nil]; diff --git a/SignalUtilitiesKit/ProfileManagerProtocol.h b/SignalUtilitiesKit/Remove Later/ProfileManagerProtocol.h similarity index 79% rename from SignalUtilitiesKit/ProfileManagerProtocol.h rename to SignalUtilitiesKit/Remove Later/ProfileManagerProtocol.h index 01694420a..ae4204dac 100644 --- a/SignalUtilitiesKit/ProfileManagerProtocol.h +++ b/SignalUtilitiesKit/Remove Later/ProfileManagerProtocol.h @@ -23,18 +23,6 @@ NS_ASSUME_NONNULL_BEGIN - (void)setProfileKeyData:(NSData *)profileKeyData forRecipientId:(NSString *)recipientId; - (void)setProfileKeyData:(NSData *)profileKeyData forRecipientId:(NSString *)recipientId avatarURL:(nullable NSString *)avatarURL; -- (BOOL)isUserInProfileWhitelist:(NSString *)recipientId; - -- (BOOL)isThreadInProfileWhitelist:(TSThread *)thread; - -- (void)addUserToProfileWhitelist:(NSString *)recipientId; -- (void)addGroupIdToProfileWhitelist:(NSData *)groupId; -- (void)addThreadToProfileWhitelist:(TSThread *)thread; - -- (void)fetchLocalUsersProfile; - -- (void)fetchProfileForRecipientId:(NSString *)recipientId; - - (void)updateProfileForContactWithID:(NSString *)contactID displayName:(NSString *)displayName with:(YapDatabaseReadWriteTransaction *)transaction; - (void)updateServiceWithProfileName:(nullable NSString *)localProfileName avatarURL:(nullable NSString *)avatarURL; diff --git a/SignalUtilitiesKit/SessionManagementProtocol.swift b/SignalUtilitiesKit/Remove Later/SessionManagementProtocol.swift similarity index 61% rename from SignalUtilitiesKit/SessionManagementProtocol.swift rename to SignalUtilitiesKit/Remove Later/SessionManagementProtocol.swift index f8f5b13a4..1455a715d 100644 --- a/SignalUtilitiesKit/SessionManagementProtocol.swift +++ b/SignalUtilitiesKit/Remove Later/SessionManagementProtocol.swift @@ -62,27 +62,30 @@ public final class SessionManagementProtocol : NSObject { @objc(isSessionRequiredForMessage:recipientID:transaction:) public static func isSessionRequired(for message: TSOutgoingMessage, recipientID: String, transaction: YapDatabaseReadWriteTransaction) -> Bool { - if SharedSenderKeysImplementation.shared.isClosedGroup(recipientID) { - return false - } else { - return !shouldUseFallbackEncryption(for: message, recipientID: recipientID, transaction: transaction) - } + return false +// if SharedSenderKeysImplementation.shared.isClosedGroup(recipientID) { +// return false +// } else { +// return !shouldUseFallbackEncryption(for: message, recipientID: recipientID, transaction: transaction) +// } } @objc(shouldUseFallbackEncryptionForMessage:recipientID:transaction:) public static func shouldUseFallbackEncryption(for message: TSOutgoingMessage, recipientID: String, transaction: YapDatabaseReadWriteTransaction) -> Bool { - if SharedSenderKeysImplementation.shared.isClosedGroup(recipientID) { return false } - else if message is SessionRequestMessage { return true } - else if message is EndSessionMessage { return true } - else if let message = message as? DeviceLinkMessage, message.kind == .request { return true } - else if message is OWSOutgoingNullMessage { return false } - return !storage.containsSession(recipientID, deviceId: Int32(OWSDevicePrimaryDeviceId), protocolContext: transaction) + return true +// if SharedSenderKeysImplementation.shared.isClosedGroup(recipientID) { return false } +// else if message is SessionRequestMessage { return true } +// else if message is EndSessionMessage { return true } +// else if let message = message as? DeviceLinkMessage, message.kind == .request { return true } +// else if message is OWSOutgoingNullMessage { return false } +// return !storage.containsSession(recipientID, deviceId: Int32(OWSDevicePrimaryDeviceId), protocolContext: transaction) } private static func hasSentSessionRequestExpired(for publicKey: String) -> Bool { - let timestamp = Storage.getSessionRequestSentTimestamp(for: publicKey) - let expiration = timestamp + TTLUtilities.getTTL(for: .sessionRequest) - return NSDate.ows_millisecondTimeStamp() > expiration + return false +// let timestamp = Storage.getSessionRequestSentTimestamp(for: publicKey) +// let expiration = timestamp + TTLUtilities.getTTL(for: .sessionRequest) +// return NSDate.ows_millisecondTimeStamp() > expiration } @objc(sendSessionRequestIfNeededToPublicKey:transaction:) @@ -90,7 +93,7 @@ public final class SessionManagementProtocol : NSObject { // It's never necessary to establish a session with self guard publicKey != getUserHexEncodedPublicKey() else { return } // Check that we don't already have a session - let hasSession = storage.containsSession(publicKey, deviceId: Int32(OWSDevicePrimaryDeviceId), protocolContext: transaction) + let hasSession = storage.containsSession(publicKey, deviceId: Int32(1), protocolContext: transaction) guard !hasSession else { return } // Check that we didn't already send a session request let hasSentSessionRequest = (Storage.getSessionRequestSentTimestamp(for: publicKey) > 0) @@ -134,51 +137,51 @@ public final class SessionManagementProtocol : NSObject { @objc(startSessionResetInThread:transaction:) public static func startSessionReset(in thread: TSThread, using transaction: YapDatabaseReadWriteTransaction) { - // Check preconditions - guard let thread = thread as? TSContactThread else { - return print("[Loki] Can't restore session for non contact thread.") - } - // Send end session messages to the devices requiring session restoration - let devices = thread.sessionRestoreDevices // TODO: Rename this to something that reads better - for device in devices { - guard ECKeyPair.isValidHexEncodedPublicKey(candidate: device) else { continue } - let thread = TSContactThread.getOrCreateThread(withContactId: device, transaction: transaction) - thread.save(with: transaction) - /* - let endSessionMessage = EndSessionMessage(timestamp: NSDate.ows_millisecondTimeStamp(), in: thread) - let messageSenderJobQueue = SSKEnvironment.shared.messageSenderJobQueue - messageSenderJobQueue.add(message: endSessionMessage, transaction: transaction) - */ - } - thread.removeAllSessionRestoreDevices(with: transaction) - // Notify the user - let infoMessage = TSInfoMessage(timestamp: NSDate.ows_millisecondTimeStamp(), in: thread, messageType: .typeLokiSessionResetInProgress) - infoMessage.save(with: transaction) - // Update the session reset status - thread.sessionResetStatus = .initiated - thread.save(with: transaction) +// // Check preconditions +// guard let thread = thread as? TSContactThread else { +// return print("[Loki] Can't restore session for non contact thread.") +// } +// // Send end session messages to the devices requiring session restoration +// let devices = thread.sessionRestoreDevices // TODO: Rename this to something that reads better +// for device in devices { +// guard ECKeyPair.isValidHexEncodedPublicKey(candidate: device) else { continue } +// let thread = TSContactThread.getOrCreateThread(withContactId: device, transaction: transaction) +// thread.save(with: transaction) +// /* +// let endSessionMessage = EndSessionMessage(timestamp: NSDate.ows_millisecondTimeStamp(), in: thread) +// let messageSenderJobQueue = SSKEnvironment.shared.messageSenderJobQueue +// messageSenderJobQueue.add(message: endSessionMessage, transaction: transaction) +// */ +// } +// thread.removeAllSessionRestoreDevices(with: transaction) +// // Notify the user +// let infoMessage = TSInfoMessage(timestamp: NSDate.ows_millisecondTimeStamp(), in: thread, messageType: .typeLokiSessionResetInProgress) +// infoMessage.save(with: transaction) +// // Update the session reset status +// thread.sessionResetStatus = .initiated +// thread.save(with: transaction) } // MARK: - Receiving @objc(handleDecryptionError:forPublicKey:transaction:) public static func handleDecryptionError(_ errorMessage: TSErrorMessage, for publicKey: String, using transaction: YapDatabaseReadWriteTransaction) { - let type = errorMessage.errorType - let masterPublicKey = storage.getMasterHexEncodedPublicKey(for: publicKey, in: transaction) ?? publicKey - let thread = TSContactThread.getOrCreateThread(withContactId: masterPublicKey, transaction: transaction) - let restorationTimeInMs = UInt64(storage.getRestorationTime() * 1000) - // Show the session reset prompt upon certain errors - switch type { - case .noSession, .invalidMessage, .invalidKeyException: - if restorationTimeInMs > errorMessage.timestamp { - // Automatically rebuild session after restoration - sendSessionRequestIfNeeded(to: publicKey, using: transaction) - } else { - // Store the source device's public key in case it was a secondary device - thread.addSessionRestoreDevice(publicKey, transaction: transaction) - } - default: break - } +// let type = errorMessage.errorType +// let masterPublicKey = storage.getMasterHexEncodedPublicKey(for: publicKey, in: transaction) ?? publicKey +// let thread = TSContactThread.getOrCreateThread(withContactId: masterPublicKey, transaction: transaction) +// let restorationTimeInMs = UInt64(storage.getRestorationTime() * 1000) +// // Show the session reset prompt upon certain errors +// switch type { +// case .noSession, .invalidMessage, .invalidKeyException: +// if restorationTimeInMs > errorMessage.timestamp { +// // Automatically rebuild session after restoration +// sendSessionRequestIfNeeded(to: publicKey, using: transaction) +// } else { +// // Store the source device's public key in case it was a secondary device +// thread.addSessionRestoreDevice(publicKey, transaction: transaction) +// } +// default: break +// } } private static func shouldProcessSessionRequest(from publicKey: String, at timestamp: UInt64) -> Bool { @@ -190,33 +193,33 @@ public final class SessionManagementProtocol : NSObject { @objc(handlePreKeyBundleMessageIfNeeded:wrappedIn:transaction:) public static func handlePreKeyBundleMessageIfNeeded(_ protoContent: SSKProtoContent, wrappedIn envelope: SSKProtoEnvelope, using transaction: YapDatabaseReadWriteTransaction) { - let publicKey = envelope.source! // Set during UD decryption - guard let preKeyBundleMessage = protoContent.prekeyBundleMessage else { return } - print("[Loki] Received a pre key bundle message from: \(publicKey).") - guard let preKeyBundle = preKeyBundleMessage.getPreKeyBundle(with: transaction) else { - return print("[Loki] Couldn't parse pre key bundle received from: \(publicKey).") - } - if !shouldProcessSessionRequest(from: publicKey, at: envelope.timestamp) { - return print("[Loki] Ignoring session request from: \(publicKey).") - } - storage.setPreKeyBundle(preKeyBundle, forContact: publicKey, transaction: transaction) - Storage.setSessionRequestProcessedTimestamp(for: publicKey, to: NSDate.ows_millisecondTimeStamp(), using: transaction) - sendNullMessage(to: publicKey, in: transaction) +// let publicKey = envelope.source! // Set during UD decryption +// guard let preKeyBundleMessage = protoContent.prekeyBundleMessage else { return } +// print("[Loki] Received a pre key bundle message from: \(publicKey).") +// guard let preKeyBundle = preKeyBundleMessage.getPreKeyBundle(with: transaction) else { +// return print("[Loki] Couldn't parse pre key bundle received from: \(publicKey).") +// } +// if !shouldProcessSessionRequest(from: publicKey, at: envelope.timestamp) { +// return print("[Loki] Ignoring session request from: \(publicKey).") +// } +// storage.setPreKeyBundle(preKeyBundle, forContact: publicKey, transaction: transaction) +// Storage.setSessionRequestProcessedTimestamp(for: publicKey, to: NSDate.ows_millisecondTimeStamp(), using: transaction) +// sendNullMessage(to: publicKey, in: transaction) } @objc(handleEndSessionMessageReceivedInThread:using:) public static func handleEndSessionMessageReceived(in thread: TSContactThread, using transaction: YapDatabaseReadWriteTransaction) { - let publicKey = thread.contactIdentifier() - print("[Loki] End session message received from: \(publicKey).") - // Notify the user - let infoMessage = TSInfoMessage(timestamp: NSDate.ows_millisecondTimeStamp(), in: thread, messageType: .typeLokiSessionResetInProgress) - infoMessage.save(with: transaction) - // Archive all sessions - storage.archiveAllSessions(forContact: publicKey, protocolContext: transaction) - // Update the session reset status - thread.sessionResetStatus = .requestReceived - thread.save(with: transaction) - // Send a null message - sendNullMessage(to: publicKey, in: transaction) +// let publicKey = thread.contactIdentifier() +// print("[Loki] End session message received from: \(publicKey).") +// // Notify the user +// let infoMessage = TSInfoMessage(timestamp: NSDate.ows_millisecondTimeStamp(), in: thread, messageType: .typeLokiSessionResetInProgress) +// infoMessage.save(with: transaction) +// // Archive all sessions +// storage.archiveAllSessions(forContact: publicKey, protocolContext: transaction) +// // Update the session reset status +// thread.sessionResetStatus = .requestReceived +// thread.save(with: transaction) +// // Send a null message +// sendNullMessage(to: publicKey, in: transaction) } } diff --git a/SignalUtilitiesKit/SessionMetaProtocol.swift b/SignalUtilitiesKit/Remove Later/SessionMetaProtocol.swift similarity index 98% rename from SignalUtilitiesKit/SessionMetaProtocol.swift rename to SignalUtilitiesKit/Remove Later/SessionMetaProtocol.swift index d15e4802b..bcbc2ee13 100644 --- a/SignalUtilitiesKit/SessionMetaProtocol.swift +++ b/SignalUtilitiesKit/Remove Later/SessionMetaProtocol.swift @@ -26,7 +26,7 @@ public final class SessionMetaProtocol : NSObject { public static func getDestinations(for outgoingGroupMessage: TSOutgoingMessage, in thread: TSThread) -> NSMutableSet { guard let thread = thread as? TSGroupThread else { preconditionFailure("Can't get destinations for group message in non-group thread.") } var result: Set = [] - if thread.isPublicChat { + if thread.isOpenGroup { storage.dbReadConnection.read { transaction in if let openGroup = LokiDatabaseUtilities.getPublicChat(for: thread.uniqueId!, in: transaction) { result = [ openGroup.server ] // Aim the message at the open group server @@ -65,7 +65,7 @@ public final class SessionMetaProtocol : NSObject { @objc(shouldSendTranscriptForMessage:inThread:) public static func shouldSendTranscript(for message: TSOutgoingMessage, in thread: TSThread) -> Bool { guard message.shouldSyncTranscript() else { return false } - let isOpenGroupMessage = (thread as? TSGroupThread)?.isPublicChat == true + let isOpenGroupMessage = (thread as? TSGroupThread)?.isOpenGroup == true let wouldSignalRequireTranscript = (AreRecipientUpdatesEnabled() || !message.hasSyncedTranscript) guard wouldSignalRequireTranscript && !isOpenGroupMessage else { return false } return false diff --git a/SignalUtilitiesKit/ReturnToCallViewController.swift b/SignalUtilitiesKit/ReturnToCallViewController.swift deleted file mode 100644 index 9fb941cdb..000000000 --- a/SignalUtilitiesKit/ReturnToCallViewController.swift +++ /dev/null @@ -1,88 +0,0 @@ -// -// Copyright (c) 2018 Open Whisper Systems. All rights reserved. -// - -import Foundation -import PureLayout - -@objc -public protocol ReturnToCallViewControllerDelegate: class { - func returnToCallWasTapped(_ viewController: ReturnToCallViewController) -} - -@objc -public class ReturnToCallViewController: UIViewController { - - @objc - public weak var delegate: ReturnToCallViewControllerDelegate? - - let returnToCallLabel = UILabel() - - @objc - public func startAnimating() { - NotificationCenter.default.addObserver(self, selector: #selector(didTapStatusBar(notification:)), name: .TappedStatusBar, object: nil) - self.returnToCallLabel.layer.removeAllAnimations() - self.returnToCallLabel.alpha = 1 - UIView.animate(withDuration: 1, - delay: 0, - options: [.repeat, .autoreverse], - animations: { self.returnToCallLabel.alpha = 0 }, - completion: { _ in self.returnToCallLabel.alpha = 1 }) - } - - @objc - public func stopAnimating() { - NotificationCenter.default.removeObserver(self, name: .TappedStatusBar, object: nil) - self.returnToCallLabel.layer.removeAllAnimations() - } - - override public func loadView() { - self.view = UIView() - - // This is the color of the iOS "return to call" banner. - view.backgroundColor = UIColor(rgbHex: 0x4cd964) - let tapGesture = UITapGestureRecognizer(target: self, action: #selector(didTapView)) - view.addGestureRecognizer(tapGesture) - - view.addSubview(returnToCallLabel) - - // System UI doesn't use dynamic type for status bar; neither do we. - returnToCallLabel.font = UIFont.ows_regularFont(withSize: 14) - returnToCallLabel.text = NSLocalizedString("CALL_WINDOW_RETURN_TO_CALL", comment: "Label for the 'return to call' banner.") - returnToCallLabel.textColor = .white - - returnToCallLabel.autoPinEdge(toSuperviewEdge: .bottom, withInset: 2) - returnToCallLabel.setCompressionResistanceHigh() - returnToCallLabel.setContentHuggingHigh() - returnToCallLabel.autoHCenterInSuperview() - } - - @objc - public func didTapView(gestureRecognizer: UITapGestureRecognizer) { - self.delegate?.returnToCallWasTapped(self) - } - - @objc - public func didTapStatusBar(notification: Notification) { - self.delegate?.returnToCallWasTapped(self) - } - - override public func viewWillLayoutSubviews() { - Logger.debug("") - - super.viewWillLayoutSubviews() - } - - override public func viewDidLayoutSubviews() { - Logger.debug("") - - super.viewDidLayoutSubviews() - } - - // MARK: Orientation - - public override var supportedInterfaceOrientations: UIInterfaceOrientationMask { - return .portrait - } - -} diff --git a/SignalUtilitiesKit/RotateSignedKeyOperation.swift b/SignalUtilitiesKit/RotateSignedKeyOperation.swift index 1fc9368dd..578cb77f7 100644 --- a/SignalUtilitiesKit/RotateSignedKeyOperation.swift +++ b/SignalUtilitiesKit/RotateSignedKeyOperation.swift @@ -11,10 +11,6 @@ public class RotateSignedPreKeyOperation: OWSOperation { return TSAccountManager.sharedInstance() } - private var accountServiceClient: AccountServiceClient { - return AccountServiceClient.shared - } - private var primaryStorage: OWSPrimaryStorage { return OWSPrimaryStorage.shared() } @@ -27,53 +23,13 @@ public class RotateSignedPreKeyOperation: OWSOperation { return } - // Loki: Doing this on the global queue to match Signal DispatchQueue.global().async { SessionManagementProtocol.rotateSignedPreKey() self.reportSuccess() } - - /* Loki: Original code - * ================ - let signedPreKeyRecord: SignedPreKeyRecord = self.primaryStorage.generateRandomSignedRecord() - - self.primaryStorage.storeSignedPreKey(signedPreKeyRecord.id, signedPreKeyRecord: signedPreKeyRecord) - firstly { - return self.accountServiceClient.setSignedPreKey(signedPreKeyRecord) - }.done(on: DispatchQueue.global()) { - Logger.info("Successfully uploaded signed PreKey") - signedPreKeyRecord.markAsAcceptedByService() - self.primaryStorage.storeSignedPreKey(signedPreKeyRecord.id, signedPreKeyRecord: signedPreKeyRecord) - self.primaryStorage.setCurrentSignedPrekeyId(signedPreKeyRecord.id) - - TSPreKeyManager.clearPreKeyUpdateFailureCount() - TSPreKeyManager.clearSignedPreKeyRecords() - - Logger.debug("done") - self.reportSuccess() - }.catch { error in - self.reportError(error) - }.retainUntilComplete() - * ================ - */ } override public func didFail(error: Error) { - switch error { - case let networkManagerError as NetworkManagerError: - guard !networkManagerError.isNetworkError else { - Logger.debug("don't report SPK rotation failure w/ network error") - return - } - - guard networkManagerError.statusCode >= 400 && networkManagerError.statusCode <= 599 else { - Logger.debug("don't report SPK rotation failure w/ non application error") - return - } - - TSPreKeyManager.incrementPreKeyUpdateFailureCount() - default: - Logger.debug("don't report SPK rotation failure w/ non NetworkManager error: \(error)") - } + Logger.debug("don't report SPK rotation failure w/ non NetworkManager error: \(error)") } } diff --git a/SignalUtilitiesKit/SSKEnvironment.h b/SignalUtilitiesKit/SSKEnvironment.h index a81965968..4ac8cae53 100644 --- a/SignalUtilitiesKit/SSKEnvironment.h +++ b/SignalUtilitiesKit/SSKEnvironment.h @@ -23,7 +23,6 @@ NS_ASSUME_NONNULL_BEGIN @class OWSReadReceiptManager; @class SSKMessageSenderJobQueue; @class TSAccountManager; -@class TSNetworkManager; @class TSSocketManager; @class YapDatabaseConnection; @@ -38,31 +37,18 @@ NS_ASSUME_NONNULL_BEGIN @interface SSKEnvironment : NSObject -- (instancetype)initWithContactsManager:(id)contactsManager - messageSender:(OWSMessageSender *)messageSender - messageSenderJobQueue:(SSKMessageSenderJobQueue *)messageSenderJobQueue - profileManager:(id)profileManager - primaryStorage:(OWSPrimaryStorage *)primaryStorage - contactsUpdater:(ContactsUpdater *)contactsUpdater - networkManager:(TSNetworkManager *)networkManager - messageManager:(OWSMessageManager *)messageManager - blockingManager:(OWSBlockingManager *)blockingManager - identityManager:(OWSIdentityManager *)identityManager - udManager:(id)udManager - messageDecrypter:(OWSMessageDecrypter *)messageDecrypter - batchMessageProcessor:(OWSBatchMessageProcessor *)batchMessageProcessor - messageReceiver:(OWSMessageReceiver *)messageReceiver - socketManager:(TSSocketManager *)socketManager - tsAccountManager:(TSAccountManager *)tsAccountManager - ows2FAManager:(OWS2FAManager *)ows2FAManager - disappearingMessagesJob:(OWSDisappearingMessagesJob *)disappearingMessagesJob - contactDiscoveryService:(ContactDiscoveryService *)contactDiscoveryService - readReceiptManager:(OWSReadReceiptManager *)readReceiptManager - outgoingReceiptManager:(OWSOutgoingReceiptManager *)outgoingReceiptManager - reachabilityManager:(id)reachabilityManager - syncManager:(id)syncManager - typingIndicators:(id)typingIndicators - attachmentDownloads:(OWSAttachmentDownloads *)attachmentDownloads NS_DESIGNATED_INITIALIZER; +- (instancetype)initWithProfileManager:(id)profileManager + primaryStorage:(OWSPrimaryStorage *)primaryStorage + blockingManager:(OWSBlockingManager *)blockingManager + identityManager:(OWSIdentityManager *)identityManager + udManager:(id)udManager + tsAccountManager:(TSAccountManager *)tsAccountManager + disappearingMessagesJob:(OWSDisappearingMessagesJob *)disappearingMessagesJob + readReceiptManager:(OWSReadReceiptManager *)readReceiptManager + outgoingReceiptManager:(OWSOutgoingReceiptManager *)outgoingReceiptManager + reachabilityManager:(id)reachabilityManager + typingIndicators:(id)typingIndicators + attachmentDownloads:(OWSAttachmentDownloads *)attachmentDownloads NS_DESIGNATED_INITIALIZER; - (instancetype)init NS_UNAVAILABLE; @@ -75,28 +61,15 @@ NS_ASSUME_NONNULL_BEGIN + (void)clearSharedForTests; #endif -@property (nonatomic, readonly) id contactsManager; -@property (nonatomic, readonly) OWSMessageSender *messageSender; -@property (nonatomic, readonly) SSKMessageSenderJobQueue *messageSenderJobQueue; @property (nonatomic, readonly) id profileManager; @property (nonatomic, readonly) OWSPrimaryStorage *primaryStorage; -@property (nonatomic, readonly) ContactsUpdater *contactsUpdater; -@property (nonatomic, readonly) TSNetworkManager *networkManager; -@property (nonatomic, readonly) OWSMessageManager *messageManager; @property (nonatomic, readonly) OWSBlockingManager *blockingManager; @property (nonatomic, readonly) OWSIdentityManager *identityManager; @property (nonatomic, readonly) id udManager; -@property (nonatomic, readonly) OWSMessageDecrypter *messageDecrypter; -@property (nonatomic, readonly) OWSBatchMessageProcessor *batchMessageProcessor; -@property (nonatomic, readonly) OWSMessageReceiver *messageReceiver; -@property (nonatomic, readonly) TSSocketManager *socketManager; @property (nonatomic, readonly) TSAccountManager *tsAccountManager; -@property (nonatomic, readonly) OWS2FAManager *ows2FAManager; @property (nonatomic, readonly) OWSDisappearingMessagesJob *disappearingMessagesJob; -@property (nonatomic, readonly) ContactDiscoveryService *contactDiscoveryService; @property (nonatomic, readonly) OWSReadReceiptManager *readReceiptManager; @property (nonatomic, readonly) OWSOutgoingReceiptManager *outgoingReceiptManager; -@property (nonatomic, readonly) id syncManager; @property (nonatomic, readonly) id reachabilityManager; @property (nonatomic, readonly) id typingIndicators; @property (nonatomic, readonly) OWSAttachmentDownloads *attachmentDownloads; diff --git a/SignalUtilitiesKit/SSKEnvironment.m b/SignalUtilitiesKit/SSKEnvironment.m index f690a5391..b0e233b16 100644 --- a/SignalUtilitiesKit/SSKEnvironment.m +++ b/SignalUtilitiesKit/SSKEnvironment.m @@ -13,27 +13,15 @@ static SSKEnvironment *sharedSSKEnvironment; @interface SSKEnvironment () -@property (nonatomic) id contactsManager; -@property (nonatomic) OWSMessageSender *messageSender; @property (nonatomic) id profileManager; @property (nonatomic) OWSPrimaryStorage *primaryStorage; -@property (nonatomic) ContactsUpdater *contactsUpdater; -@property (nonatomic) TSNetworkManager *networkManager; -@property (nonatomic) OWSMessageManager *messageManager; @property (nonatomic) OWSBlockingManager *blockingManager; @property (nonatomic) OWSIdentityManager *identityManager; @property (nonatomic) id udManager; -@property (nonatomic) OWSMessageDecrypter *messageDecrypter; -@property (nonatomic) OWSBatchMessageProcessor *batchMessageProcessor; -@property (nonatomic) OWSMessageReceiver *messageReceiver; -@property (nonatomic) TSSocketManager *socketManager; @property (nonatomic) TSAccountManager *tsAccountManager; -@property (nonatomic) OWS2FAManager *ows2FAManager; @property (nonatomic) OWSDisappearingMessagesJob *disappearingMessagesJob; -@property (nonatomic) ContactDiscoveryService *contactDiscoveryService; @property (nonatomic) OWSReadReceiptManager *readReceiptManager; @property (nonatomic) OWSOutgoingReceiptManager *outgoingReceiptManager; -@property (nonatomic) id syncManager; @property (nonatomic) id reachabilityManager; @property (nonatomic) id typingIndicators; @property (nonatomic) OWSAttachmentDownloads *attachmentDownloads; @@ -51,85 +39,47 @@ static SSKEnvironment *sharedSSKEnvironment; @synthesize migrationDBConnection = _migrationDBConnection; @synthesize analyticsDBConnection = _analyticsDBConnection; -- (instancetype)initWithContactsManager:(id)contactsManager - messageSender:(OWSMessageSender *)messageSender - messageSenderJobQueue:(SSKMessageSenderJobQueue *)messageSenderJobQueue - profileManager:(id)profileManager - primaryStorage:(OWSPrimaryStorage *)primaryStorage - contactsUpdater:(ContactsUpdater *)contactsUpdater - networkManager:(TSNetworkManager *)networkManager - messageManager:(OWSMessageManager *)messageManager - blockingManager:(OWSBlockingManager *)blockingManager - identityManager:(OWSIdentityManager *)identityManager - udManager:(id)udManager - messageDecrypter:(OWSMessageDecrypter *)messageDecrypter - batchMessageProcessor:(OWSBatchMessageProcessor *)batchMessageProcessor - messageReceiver:(OWSMessageReceiver *)messageReceiver - socketManager:(TSSocketManager *)socketManager - tsAccountManager:(TSAccountManager *)tsAccountManager - ows2FAManager:(OWS2FAManager *)ows2FAManager - disappearingMessagesJob:(OWSDisappearingMessagesJob *)disappearingMessagesJob - contactDiscoveryService:(ContactDiscoveryService *)contactDiscoveryService - readReceiptManager:(OWSReadReceiptManager *)readReceiptManager - outgoingReceiptManager:(OWSOutgoingReceiptManager *)outgoingReceiptManager - reachabilityManager:(id)reachabilityManager - syncManager:(id)syncManager - typingIndicators:(id)typingIndicators - attachmentDownloads:(OWSAttachmentDownloads *)attachmentDownloads +- (instancetype)initWithProfileManager:(id)profileManager + primaryStorage:(OWSPrimaryStorage *)primaryStorage + blockingManager:(OWSBlockingManager *)blockingManager + identityManager:(OWSIdentityManager *)identityManager + udManager:(id)udManager + tsAccountManager:(TSAccountManager *)tsAccountManager + disappearingMessagesJob:(OWSDisappearingMessagesJob *)disappearingMessagesJob + readReceiptManager:(OWSReadReceiptManager *)readReceiptManager + outgoingReceiptManager:(OWSOutgoingReceiptManager *)outgoingReceiptManager + reachabilityManager:(id)reachabilityManager + typingIndicators:(id)typingIndicators + attachmentDownloads:(OWSAttachmentDownloads *)attachmentDownloads { self = [super init]; + if (!self) { return self; } - OWSAssertDebug(contactsManager); - OWSAssertDebug(messageSender); - OWSAssertDebug(messageSenderJobQueue); OWSAssertDebug(profileManager); OWSAssertDebug(primaryStorage); - OWSAssertDebug(contactsUpdater); - OWSAssertDebug(networkManager); - OWSAssertDebug(messageManager); OWSAssertDebug(blockingManager); OWSAssertDebug(identityManager); OWSAssertDebug(udManager); - OWSAssertDebug(messageDecrypter); - OWSAssertDebug(batchMessageProcessor); - OWSAssertDebug(messageReceiver); - OWSAssertDebug(socketManager); OWSAssertDebug(tsAccountManager); - OWSAssertDebug(ows2FAManager); OWSAssertDebug(disappearingMessagesJob); - OWSAssertDebug(contactDiscoveryService); OWSAssertDebug(readReceiptManager); OWSAssertDebug(outgoingReceiptManager); - OWSAssertDebug(syncManager); OWSAssertDebug(reachabilityManager); OWSAssertDebug(typingIndicators); OWSAssertDebug(attachmentDownloads); - _contactsManager = contactsManager; - _messageSender = messageSender; - _messageSenderJobQueue = messageSenderJobQueue; _profileManager = profileManager; _primaryStorage = primaryStorage; - _contactsUpdater = contactsUpdater; - _networkManager = networkManager; - _messageManager = messageManager; _blockingManager = blockingManager; _identityManager = identityManager; _udManager = udManager; - _messageDecrypter = messageDecrypter; - _batchMessageProcessor = batchMessageProcessor; - _messageReceiver = messageReceiver; - _socketManager = socketManager; _tsAccountManager = tsAccountManager; - _ows2FAManager = ows2FAManager; _disappearingMessagesJob = disappearingMessagesJob; - _contactDiscoveryService = contactDiscoveryService; _readReceiptManager = readReceiptManager; _outgoingReceiptManager = outgoingReceiptManager; - _syncManager = syncManager; _reachabilityManager = reachabilityManager; _typingIndicators = typingIndicators; _attachmentDownloads = attachmentDownloads; @@ -158,26 +108,6 @@ static SSKEnvironment *sharedSSKEnvironment; } #pragma mark - Mutable Accessors -/* -- (nullable id)callMessageHandler -{ - @synchronized(self) { - OWSAssertDebug(_callMessageHandler); - - return _callMessageHandler; - } -} - -- (void)setCallMessageHandler:(nullable id)callMessageHandler -{ - @synchronized(self) { - OWSAssertDebug(callMessageHandler); - OWSAssertDebug(!_callMessageHandler); - - _callMessageHandler = callMessageHandler; - } -} - */ - (nullable id)notificationsManager { diff --git a/SignalUtilitiesKit/SSKPreferences.swift b/SignalUtilitiesKit/SSKPreferences.swift index 655903fd2..9d4c34f5d 100644 --- a/SignalUtilitiesKit/SSKPreferences.swift +++ b/SignalUtilitiesKit/SSKPreferences.swift @@ -22,8 +22,6 @@ public class SSKPreferences: NSObject { } set { setBool(newValue, key: areLinkPreviewsEnabledKey) - - SSKEnvironment.shared.syncManager.sendConfigurationSyncMessage() } } diff --git a/SignalUtilitiesKit/SSKProtoPrekeyBundleMessage+Loki.swift b/SignalUtilitiesKit/SSKProtoPrekeyBundleMessage+Loki.swift deleted file mode 100644 index 387bf4415..000000000 --- a/SignalUtilitiesKit/SSKProtoPrekeyBundleMessage+Loki.swift +++ /dev/null @@ -1,23 +0,0 @@ - -@objc public extension SSKProtoPrekeyBundleMessage { - - @objc(builderFromPreKeyBundle:) - public static func builder(from preKeyBundle: PreKeyBundle) -> SSKProtoPrekeyBundleMessageBuilder { - let builder = self.builder() - builder.setIdentityKey(preKeyBundle.identityKey) - builder.setDeviceID(UInt32(preKeyBundle.deviceId)) - builder.setPrekeyID(UInt32(preKeyBundle.preKeyId)) - builder.setPrekey(preKeyBundle.preKeyPublic) - builder.setSignedKeyID(UInt32(preKeyBundle.signedPreKeyId)) - builder.setSignedKey(preKeyBundle.signedPreKeyPublic) - builder.setSignature(preKeyBundle.signedPreKeySignature) - return builder - } - - @objc(getPreKeyBundleWithTransaction:) - public func getPreKeyBundle(with transaction: YapDatabaseReadWriteTransaction) -> PreKeyBundle? { - let registrationId = TSAccountManager.sharedInstance().getOrGenerateRegistrationId(transaction) - return PreKeyBundle(registrationId: Int32(registrationId), deviceId: Int32(deviceID), preKeyId: Int32(prekeyID), preKeyPublic: prekey, - signedPreKeyPublic: signedKey, signedPreKeyId: Int32(signedKeyID), signedPreKeySignature: signature, identityKey: identityKey) - } -} diff --git a/SignalUtilitiesKit/SSKWebSocket.swift b/SignalUtilitiesKit/SSKWebSocket.swift deleted file mode 100644 index b72a680ad..000000000 --- a/SignalUtilitiesKit/SSKWebSocket.swift +++ /dev/null @@ -1,185 +0,0 @@ -// -// Copyright (c) 2019 Open Whisper Systems. All rights reserved. -// - -import Foundation -import Starscream - -@objc -public enum SSKWebSocketState: UInt { - case open, connecting, disconnected -} - -@objc -public class SSKWebSocketError: NSObject, CustomNSError { - - init(underlyingError: Starscream.WSError) { - self.underlyingError = underlyingError - } - - // MARK: - CustomNSError - - @objc - public static let errorDomain = "SignalServiceKit.SSKWebSocketError" - - public var errorUserInfo: [String: Any] { - return [ - type(of: self).kStatusCodeKey: underlyingError.code, - NSUnderlyingErrorKey: (underlyingError as NSError) - ] - } - - // MARK: - - - @objc - public static let kStatusCodeKey = "SSKWebSocketErrorStatusCode" - - let underlyingError: Starscream.WSError -} - -@objc -public protocol SSKWebSocket { - - @objc - var delegate: SSKWebSocketDelegate? { get set } - - @objc - var state: SSKWebSocketState { get } - - @objc - func connect() - - @objc - func disconnect() - - @objc(writeData:error:) - func write(data: Data) throws - - @objc - func writePing() throws -} - -@objc -public protocol SSKWebSocketDelegate: class { - func websocketDidConnect(socket: SSKWebSocket) - - func websocketDidDisconnect(socket: SSKWebSocket, error: Error?) - - func websocketDidReceiveData(socket: SSKWebSocket, data: Data) - - @objc optional func websocketDidReceiveMessage(socket: SSKWebSocket, text: String) -} - -@objc -public class SSKWebSocketManager: NSObject { - - @objc - public class func buildSocket(request: URLRequest) -> SSKWebSocket { - return SSKWebSocketImpl(request: request) - } -} - -class SSKWebSocketImpl: SSKWebSocket { - - private let socket: Starscream.WebSocket - - init(request: URLRequest) { - let socket = WebSocket(request: request) - - socket.disableSSLCertValidation = true - socket.socketSecurityLevel = StreamSocketSecurityLevel.tlSv1_2 - let security = SSLSecurity(certs: [TextSecureCertificate()], usePublicKeys: false) - security.validateEntireChain = false - socket.security = security - - // TODO cipher suite selection - // socket.enabledSSLCipherSuites = [TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384, TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256] - - self.socket = socket - - socket.delegate = self - } - - // MARK: - SSKWebSocket - - weak var delegate: SSKWebSocketDelegate? - - var hasEverConnected = false - var state: SSKWebSocketState { - if socket.isConnected { - return .open - } - - if hasEverConnected { - return .disconnected - } - - return .connecting - } - - func connect() { - socket.connect() - } - - func disconnect() { - socket.disconnect() - } - - func write(data: Data) throws { - socket.write(data: data) - } - - func writePing() throws { - socket.write(ping: Data()) - } -} - -extension SSKWebSocketImpl: WebSocketDelegate { - func websocketDidConnect(socket: WebSocketClient) { - hasEverConnected = true - delegate?.websocketDidConnect(socket: self) - } - - func websocketDidDisconnect(socket: WebSocketClient, error: Error?) { - let websocketError: Error? - switch error { - case let wsError as WSError: - websocketError = SSKWebSocketError(underlyingError: wsError) - case let nsError as NSError: - // Assert that error is either a Starscream.WSError or an OS level networking error - if #available(iOS 10, *) { - let networkDownCode = 50 - assert(nsError.domain == "NSPOSIXErrorDomain" && nsError.code == networkDownCode) - } else { - assert(nsError.domain == kCFErrorDomainCFNetwork as String) - } - websocketError = error - default: - assert(error == nil, "unexpected error type: \(String(describing: error))") - websocketError = error - } - - delegate?.websocketDidDisconnect(socket: self, error: websocketError) - } - - func websocketDidReceiveMessage(socket: WebSocketClient, text: String) { - if let websocketDidReceiveMessage = self.delegate?.websocketDidReceiveMessage { - websocketDidReceiveMessage(self, text) - } - } - - func websocketDidReceiveData(socket: WebSocketClient, data: Data) { - delegate?.websocketDidReceiveData(socket: self, data: data) - } -} - -private func TextSecureCertificate() -> SSLCert { - let data = SSKTextSecureServiceCertificateData() - return SSLCert(data: data) -} - -private extension StreamSocketSecurityLevel { - static var tlSv1_2: StreamSocketSecurityLevel { - return StreamSocketSecurityLevel(rawValue: "kCFStreamSocketSecurityLevelTLSv1_2") - } -} diff --git a/SignalUtilitiesKit/SessionRequestMessage.swift b/SignalUtilitiesKit/SessionRequestMessage.swift deleted file mode 100644 index b612b1608..000000000 --- a/SignalUtilitiesKit/SessionRequestMessage.swift +++ /dev/null @@ -1,51 +0,0 @@ - -@objc(LKSessionRequestMessage) -internal final class SessionRequestMessage : TSOutgoingMessage { - - @objc internal override var ttl: UInt32 { return UInt32(TTLUtilities.getTTL(for: .sessionRequest)) } - - @objc internal override func shouldBeSaved() -> Bool { return false } - @objc internal override func shouldSyncTranscript() -> Bool { return false } - - @objc internal init(thread: TSThread) { - super.init(outgoingMessageWithTimestamp: NSDate.ows_millisecondTimeStamp(), in: thread, messageBody: "", - attachmentIds: NSMutableArray(), expiresInSeconds: 0, expireStartedAt: 0, isVoiceMessage: false, - groupMetaMessage: .unspecified, quotedMessage: nil, contactShare: nil, linkPreview: nil) - } - - required init?(coder: NSCoder) { - super.init(coder: coder) - } - - required init(dictionary: [String:Any]) throws { - try super.init(dictionary: dictionary) - } - - override func prepareCustomContentBuilder(_ recipient: SignalRecipient) -> Any? { - guard let contentBuilder = super.prepareCustomContentBuilder(recipient) as? SSKProtoContent.SSKProtoContentBuilder else { return nil } - // Attach a null message - let nullMessageBuilder = SSKProtoNullMessage.builder() - let paddingSize = UInt.random(in: 0..<512) // random(in:) uses the system's default random generator, which is cryptographically secure - let padding = Cryptography.generateRandomBytes(paddingSize) - nullMessageBuilder.setPadding(padding) - do { - let nullMessage = try nullMessageBuilder.build() - contentBuilder.setNullMessage(nullMessage) - } catch { - owsFailDebug("Failed to build session request message for: \(recipient.recipientId()) due to error: \(error).") - return nil - } - // Generate a pre key bundle for the recipient and attach it - let preKeyBundle = OWSPrimaryStorage.shared().generatePreKeyBundle(forContact: recipient.recipientId()) - let preKeyBundleMessageBuilder = SSKProtoPrekeyBundleMessage.builder(from: preKeyBundle) - do { - let preKeyBundleMessage = try preKeyBundleMessageBuilder.build() - contentBuilder.setPrekeyBundleMessage(preKeyBundleMessage) - } catch { - owsFailDebug("Failed to build session request message for: \(recipient.recipientId()) due to error: \(error).") - return nil - } - // Return - return contentBuilder - } -} diff --git a/SignalUtilitiesKit/SharedSenderKeysImplementation.swift b/SignalUtilitiesKit/SharedSenderKeysImplementation.swift deleted file mode 100644 index 6f5f8bc0d..000000000 --- a/SignalUtilitiesKit/SharedSenderKeysImplementation.swift +++ /dev/null @@ -1,220 +0,0 @@ -import CryptoSwift -import PromiseKit - - -@objc(LKSharedSenderKeysImplementation) -public final class SharedSenderKeysImplementation : NSObject { - private static let gcmTagSize: UInt = 16 - private static let ivSize: UInt = 12 - - // MARK: Documentation - // A quick overview of how shared sender key based closed groups work: - // - // • When a user creates a group, they generate a key pair for the group along with a ratchet for - // every member of the group. They bundle this together with some other group info such as the group - // name in a `ClosedGroupUpdateMessage` and send that using established channels to every member of - // the group. Note that because a user can only pick from their existing contacts when selecting - // the group members they shouldn't need to establish sessions before being able to send the - // `ClosedGroupUpdateMessage`. Another way to optimize the performance of the group creation process - // is to batch fetch the device links of all members involved ahead of time, rather than letting - // the sending pipeline do it separately for every user the `ClosedGroupUpdateMessage` is sent to. - // • After the group is created, every user polls for the public key associated with the group. - // • Upon receiving a `ClosedGroupUpdateMessage` of type `.new`, a user sends session requests to all - // other members of the group they don't yet have a session with for reasons outlined below. - // • When a user sends a message they step their ratchet and use the resulting message key to encrypt - // the message. - // • When another user receives that message, they step the ratchet associated with the sender and - // use the resulting message key to decrypt the message. - // • When a user leaves or is kicked from a group, all members must generate new ratchets to ensure that - // removed users can't decrypt messages going forward. To this end every user deletes all ratchets - // associated with the group in question upon receiving a group update message that indicates that - // a user left. They then generate a new ratchet for themselves and send it out to all members of - // the group (again fetching device links ahead of time). The user should already have established - // sessions with all other members at this point because of the behavior outlined a few points above. - // • When a user adds a new member to the group, they generate a ratchet for that new member and - // send that bundled in a `ClosedGroupUpdateMessage` to the group. They send a - // `ClosedGroupUpdateMessage` with the newly generated ratchet but also the existing ratchets of - // every other member of the group to the user that joined. - - // MARK: Ratcheting Error - public enum RatchetingError : LocalizedError { - case loadingFailed(groupPublicKey: String, senderPublicKey: String) - case messageKeyMissing(targetKeyIndex: UInt, groupPublicKey: String, senderPublicKey: String) - case generic - - public var errorDescription: String? { - switch self { - case .loadingFailed(let groupPublicKey, let senderPublicKey): return "Couldn't get ratchet for closed group with public key: \(groupPublicKey), sender public key: \(senderPublicKey)." - case .messageKeyMissing(let targetKeyIndex, let groupPublicKey, let senderPublicKey): return "Couldn't find message key for old key index: \(targetKeyIndex), public key: \(groupPublicKey), sender public key: \(senderPublicKey)." - case .generic: return "An error occurred" - } - } - } - - // MARK: Initialization - @objc public static let shared = SharedSenderKeysImplementation() - - private override init() { } - - // MARK: Private/Internal API - internal func generateRatchet(for groupPublicKey: String, senderPublicKey: String, using transaction: YapDatabaseReadWriteTransaction) -> ClosedGroupRatchet { - let rootChainKey = Data.getSecureRandomData(ofSize: 32)!.toHexString() - let ratchet = ClosedGroupRatchet(chainKey: rootChainKey, keyIndex: 0, messageKeys: []) - Storage.setClosedGroupRatchet(for: groupPublicKey, senderPublicKey: senderPublicKey, ratchet: ratchet, using: transaction) - return ratchet - } - - private func step(_ ratchet: ClosedGroupRatchet) throws -> ClosedGroupRatchet { - let nextMessageKey = try HMAC(key: Data(hex: ratchet.chainKey).bytes, variant: .sha256).authenticate([ UInt8(1) ]) - let nextChainKey = try HMAC(key: Data(hex: ratchet.chainKey).bytes, variant: .sha256).authenticate([ UInt8(2) ]) - let nextKeyIndex = ratchet.keyIndex + 1 - let messageKeys = ratchet.messageKeys + [ nextMessageKey.toHexString() ] - return ClosedGroupRatchet(chainKey: nextChainKey.toHexString(), keyIndex: nextKeyIndex, messageKeys: messageKeys) - } - - /// - Note: Sync. Don't call from the main thread. - private func stepRatchetOnce(for groupPublicKey: String, senderPublicKey: String, using transaction: YapDatabaseReadWriteTransaction) throws -> ClosedGroupRatchet { - #if DEBUG - assert(!Thread.isMainThread) - #endif - guard let ratchet = Storage.getClosedGroupRatchet(for: groupPublicKey, senderPublicKey: senderPublicKey) else { - let error = RatchetingError.loadingFailed(groupPublicKey: groupPublicKey, senderPublicKey: senderPublicKey) - print("[Loki] \(error.errorDescription!)") - throw error - } - do { - let result = try step(ratchet) - Storage.setClosedGroupRatchet(for: groupPublicKey, senderPublicKey: senderPublicKey, ratchet: result, using: transaction) - return result - } catch { - print("[Loki] Couldn't step ratchet due to error: \(error).") - throw error - } - } - - /// - Note: Sync. Don't call from the main thread. - private func stepRatchet(for groupPublicKey: String, senderPublicKey: String, until targetKeyIndex: UInt, using transaction: YapDatabaseReadWriteTransaction, isRetry: Bool = false) throws -> ClosedGroupRatchet { - #if DEBUG - assert(!Thread.isMainThread) - #endif - let collection: Storage.ClosedGroupRatchetCollectionType = (isRetry) ? .old : .current - guard let ratchet = Storage.getClosedGroupRatchet(for: groupPublicKey, senderPublicKey: senderPublicKey, from: collection) else { - let error = RatchetingError.loadingFailed(groupPublicKey: groupPublicKey, senderPublicKey: senderPublicKey) - print("[Loki] \(error.errorDescription!)") - throw error - } - if targetKeyIndex < ratchet.keyIndex { - // There's no need to advance the ratchet if this is invoked for an old key index - guard ratchet.messageKeys.count > targetKeyIndex else { - let error = RatchetingError.messageKeyMissing(targetKeyIndex: targetKeyIndex, groupPublicKey: groupPublicKey, senderPublicKey: senderPublicKey) - print("[Loki] \(error.errorDescription!)") - throw error - } - return ratchet - } else { - var currentKeyIndex = ratchet.keyIndex - var result = ratchet - while currentKeyIndex < targetKeyIndex { - do { - result = try step(result) - currentKeyIndex = result.keyIndex - } catch { - print("[Loki] Couldn't step ratchet due to error: \(error).") - throw error - } - } - let collection: Storage.ClosedGroupRatchetCollectionType = (isRetry) ? .old : .current - Storage.setClosedGroupRatchet(for: groupPublicKey, senderPublicKey: senderPublicKey, ratchet: result, in: collection, using: transaction) - return result - } - } - - // MARK: Public API - @objc(encrypt:forGroupWithPublicKey:senderPublicKey:protocolContext:error:) - public func encrypt(_ plaintext: Data, forGroupWithPublicKey groupPublicKey: String, senderPublicKey: String, protocolContext: Any) throws -> [Any] { - let transaction = protocolContext as! YapDatabaseReadWriteTransaction - let (ivAndCiphertext, keyIndex) = try encrypt(plaintext, for: groupPublicKey, senderPublicKey: senderPublicKey, using: transaction) - return [ ivAndCiphertext, NSNumber(value: keyIndex) ] - } - - public func encrypt(_ plaintext: Data, for groupPublicKey: String, senderPublicKey: String, using transaction: YapDatabaseReadWriteTransaction) throws -> (ivAndCiphertext: Data, keyIndex: UInt) { - let ratchet: ClosedGroupRatchet - do { - ratchet = try stepRatchetOnce(for: groupPublicKey, senderPublicKey: senderPublicKey, using: transaction) - } catch { - // FIXME: It'd be cleaner to handle this in OWSMessageDecrypter (where all the other decryption errors are handled), but this was a lot more - // convenient because there's an easy way to get the sender public key from here. - if case RatchetingError.loadingFailed(_, _) = error { - ClosedGroupsProtocol.requestSenderKey(for: groupPublicKey, senderPublicKey: senderPublicKey, using: transaction) - } - throw error - } - let iv = Data.getSecureRandomData(ofSize: SharedSenderKeysImplementation.ivSize)! - let gcm = GCM(iv: iv.bytes, tagLength: Int(SharedSenderKeysImplementation.gcmTagSize), mode: .combined) - let messageKey = ratchet.messageKeys.last! - let aes = try AES(key: Data(hex: messageKey).bytes, blockMode: gcm, padding: .noPadding) - let ciphertext = try aes.encrypt(plaintext.bytes) - return (ivAndCiphertext: iv + Data(bytes: ciphertext), ratchet.keyIndex) - } - - @objc(decrypt:forGroupWithPublicKey:senderPublicKey:keyIndex:protocolContext:error:) - public func decrypt(_ ivAndCiphertext: Data, forGroupWithPublicKey groupPublicKey: String, senderPublicKey: String, keyIndex: UInt, protocolContext: Any) throws -> Data { - let transaction = protocolContext as! YapDatabaseReadWriteTransaction - return try decrypt(ivAndCiphertext, for: groupPublicKey, senderPublicKey: senderPublicKey, keyIndex: keyIndex, using: transaction) - } - - public func decrypt(_ ivAndCiphertext: Data, for groupPublicKey: String, senderPublicKey: String, keyIndex: UInt, using transaction: YapDatabaseReadWriteTransaction, isRetry: Bool = false) throws -> Data { - let ratchet: ClosedGroupRatchet - do { - ratchet = try stepRatchet(for: groupPublicKey, senderPublicKey: senderPublicKey, until: keyIndex, using: transaction, isRetry: isRetry) - } catch { - if !isRetry { - return try decrypt(ivAndCiphertext, for: groupPublicKey, senderPublicKey: senderPublicKey, keyIndex: keyIndex, using: transaction, isRetry: true) - } else { - // FIXME: It'd be cleaner to handle this in OWSMessageDecrypter (where all the other decryption errors are handled), but this was a lot more - // convenient because there's an easy way to get the sender public key from here. - if case RatchetingError.loadingFailed(_, _) = error { - ClosedGroupsProtocol.requestSenderKey(for: groupPublicKey, senderPublicKey: senderPublicKey, using: transaction) - } - throw error - } - } - let iv = ivAndCiphertext[0.. 16 { // Pick an arbitrary number of message keys to try; this helps resolve issues caused by messages arriving out of order - lastNMessageKeys = [String](messageKeys[messageKeys.index(messageKeys.endIndex, offsetBy: -16).. Bool { - return Storage.getUserClosedGroupPublicKeys().contains(publicKey) - } - - public func getKeyPair(forGroupWithPublicKey groupPublicKey: String) -> ECKeyPair { - let privateKey = Storage.getClosedGroupPrivateKey(for: groupPublicKey)! - return try! ECKeyPair(publicKeyData: Data(hex: groupPublicKey.removing05PrefixIfNeeded()), privateKeyData: Data(hex: privateKey)) - } -} diff --git a/SignalUtilitiesKit/SignalAccount.h b/SignalUtilitiesKit/SignalAccount.h index cd7f91f8d..e8b54290e 100644 --- a/SignalUtilitiesKit/SignalAccount.h +++ b/SignalUtilitiesKit/SignalAccount.h @@ -33,8 +33,6 @@ NS_ASSUME_NONNULL_BEGIN // this is a label for the account. @property (nonatomic) NSString *multipleAccountLabelText; -- (nullable NSString *)contactFullName; - - (instancetype)init NS_UNAVAILABLE; - (instancetype)initWithSignalRecipient:(SignalRecipient *)signalRecipient; diff --git a/SignalUtilitiesKit/SignalAccount.m b/SignalUtilitiesKit/SignalAccount.m index 3e837f2c3..36fad15a5 100644 --- a/SignalUtilitiesKit/SignalAccount.m +++ b/SignalUtilitiesKit/SignalAccount.m @@ -3,7 +3,7 @@ // #import "SignalAccount.h" -#import "Contact.h" + #import "NSString+SSK.h" #import "OWSPrimaryStorage.h" #import "SignalRecipient.h" @@ -42,11 +42,6 @@ NS_ASSUME_NONNULL_BEGIN return _recipientId; } -- (nullable NSString *)contactFullName -{ - return self.contact.fullName.filterStringForDisplay; -} - - (NSString *)multipleAccountLabelText { return _multipleAccountLabelText.filterStringForDisplay; diff --git a/SignalUtilitiesKit/SignalRecipient.m b/SignalUtilitiesKit/SignalRecipient.m index 17e01c84e..60932e04b 100644 --- a/SignalUtilitiesKit/SignalRecipient.m +++ b/SignalUtilitiesKit/SignalRecipient.m @@ -3,11 +3,10 @@ // #import "SignalRecipient.h" -#import "OWSDevice.h" + #import "ProfileManagerProtocol.h" #import "SSKEnvironment.h" #import "TSAccountManager.h" -#import "TSSocketManager.h" #import @@ -42,13 +41,6 @@ NS_ASSUME_NONNULL_BEGIN return SSKEnvironment.shared.tsAccountManager; } -- (TSSocketManager *)socketManager -{ - OWSAssertDebug(SSKEnvironment.shared.socketManager); - - return SSKEnvironment.shared.socketManager; -} - #pragma mark - + (instancetype)getOrBuildUnsavedRecipientForRecipientId:(NSString *)recipientId @@ -72,7 +64,7 @@ NS_ASSUME_NONNULL_BEGIN return self; } - _devices = [NSOrderedSet orderedSetWithObject:@(OWSDevicePrimaryDeviceId)]; + _devices = [NSOrderedSet orderedSetWithObject:@(1)]; return self; } @@ -90,10 +82,10 @@ NS_ASSUME_NONNULL_BEGIN // Since we use device count to determine whether a user is registered or not, // ensure the local user always has at least *this* device. - if (![_devices containsObject:@(OWSDevicePrimaryDeviceId)]) { + if (![_devices containsObject:@(1)]) { if ([self.uniqueId isEqualToString:self.tsAccountManager.localNumber]) { DDLogInfo(@"Adding primary device to self recipient."); - [self addDevices:[NSSet setWithObject:@(OWSDevicePrimaryDeviceId)]]; + [self addDevices:[NSSet setWithObject:@(1)]]; } } @@ -147,17 +139,6 @@ NS_ASSUME_NONNULL_BEGIN if (devicesToRemove.count > 0) { [self removeDevicesFromRecipient:[NSSet setWithArray:devicesToRemove] transaction:transaction]; } - - // Device changes - dispatch_async(dispatch_get_main_queue(), ^{ - // Device changes can affect the UD access mode for a recipient, - // so we need to fetch the profile for this user to update UD access mode. - [self.profileManager fetchProfileForRecipientId:self.recipientId]; - - if ([self.recipientId isEqualToString:self.tsAccountManager.localNumber]) { - [self.socketManager cycleSocket]; - } - }); } - (void)addDevicesToRegisteredRecipient:(NSSet *)devices transaction:(YapDatabaseReadWriteTransaction *)transaction diff --git a/SignalUtilitiesKit/SignalServiceClient.swift b/SignalUtilitiesKit/SignalServiceClient.swift deleted file mode 100644 index b7bd546b4..000000000 --- a/SignalUtilitiesKit/SignalServiceClient.swift +++ /dev/null @@ -1,94 +0,0 @@ -// -// Copyright (c) 2018 Open Whisper Systems. All rights reserved. -// - -import Foundation -import PromiseKit - - -public typealias RecipientIdentifier = String - -@objc -public protocol SignalServiceClientObjC { - @objc func updateAccountAttributesObjC() -> AnyPromise -} - -public protocol SignalServiceClient: SignalServiceClientObjC { - func getAvailablePreKeys() -> Promise - func registerPreKeys(identityKey: IdentityKey, signedPreKeyRecord: SignedPreKeyRecord, preKeyRecords: [PreKeyRecord]) -> Promise - func setCurrentSignedPreKey(_ signedPreKey: SignedPreKeyRecord) -> Promise - func requestUDSenderCertificate() -> Promise - func updateAccountAttributes() -> Promise -} - -/// Based on libsignal-service-java's PushServiceSocket class -@objc -public class SignalServiceRestClient: NSObject, SignalServiceClient { - - var networkManager: TSNetworkManager { - return TSNetworkManager.shared() - } - - private var udManager: OWSUDManager { - return SSKEnvironment.shared.udManager - } - - func unexpectedServerResponseError() -> Error { - return OWSErrorMakeUnableToProcessServerResponseError() - } - - public func getAvailablePreKeys() -> Promise { - Logger.debug("") - - let request = OWSRequestFactory.availablePreKeysCountRequest() - return firstly { - networkManager.makePromise(request: request) - }.map { _, responseObject in - Logger.debug("got response") - guard let params = ParamParser(responseObject: responseObject) else { - throw self.unexpectedServerResponseError() - } - - let count: Int = try params.required(key: "count") - - return count - } - } - - public func registerPreKeys(identityKey: IdentityKey, signedPreKeyRecord: SignedPreKeyRecord, preKeyRecords: [PreKeyRecord]) -> Promise { - Logger.debug("") - - let request = OWSRequestFactory.registerPrekeysRequest(withPrekeyArray: preKeyRecords, identityKey: identityKey, signedPreKey: signedPreKeyRecord) - return networkManager.makePromise(request: request).asVoid() - } - - public func setCurrentSignedPreKey(_ signedPreKey: SignedPreKeyRecord) -> Promise { - Logger.debug("") - - let request = OWSRequestFactory.registerSignedPrekeyRequest(with: signedPreKey) - return networkManager.makePromise(request: request).asVoid() - } - - public func requestUDSenderCertificate() -> Promise { - let request = OWSRequestFactory.udSenderCertificateRequest() - return firstly { - self.networkManager.makePromise(request: request) - }.map { _, responseObject in - guard let parser = ParamParser(responseObject: responseObject) else { - throw OWSUDError.invalidData(description: "Invalid sender certificate response") - } - - return try parser.requiredBase64EncodedData(key: "certificate") - } - } - - @objc - public func updateAccountAttributesObjC() -> AnyPromise { - return AnyPromise(updateAccountAttributes()) - } - - public func updateAccountAttributes() -> Promise { - let request = OWSRequestFactory.updateAttributesRequest() - return networkManager.makePromise(request: request).asVoid() - } -} diff --git a/SignalUtilitiesKit/SyncMessagesProtocol.swift b/SignalUtilitiesKit/SyncMessagesProtocol.swift deleted file mode 100644 index 077354442..000000000 --- a/SignalUtilitiesKit/SyncMessagesProtocol.swift +++ /dev/null @@ -1,293 +0,0 @@ -import PromiseKit - -// A few notes about making changes in this file: -// -// • Don't use a database transaction if you can avoid it. -// • If you do need to use a database transaction, use a read transaction if possible. -// • For write transactions, consider making it the caller's responsibility to manage the database transaction (this helps avoid unnecessary transactions). -// • Think carefully about adding a function; there might already be one for what you need. -// • Document the expected cases in which a function will be used -// • Express those cases in tests. - -@objc(LKSyncMessagesProtocol) -public final class SyncMessagesProtocol : NSObject { - - /// Only ever modified from the message processing queue (`OWSBatchMessageProcessor.processingQueue`). - private static var syncMessageTimestamps: [String:Set] = [:] - - internal static var storage: OWSPrimaryStorage { OWSPrimaryStorage.shared() } - - // MARK: - Error - - @objc(LKSyncMessagesProtocolError) - public class SyncMessagesProtocolError : NSError { // Not called `Error` for Obj-C interoperablity - - @objc public static let privateKeyMissing = SyncMessagesProtocolError(domain: "SyncMessagesProtocolErrorDomain", code: 1, userInfo: [ NSLocalizedDescriptionKey : "Couldn't get private key for SSK based closed group." ]) - } - - // MARK: - Sending - - @objc public static func syncProfile() { - /* - Storage.writeSync { transaction in - let userPublicKey = getUserHexEncodedPublicKey() - let userLinkedDevices = LokiDatabaseUtilities.getLinkedDeviceHexEncodedPublicKeys(for: userPublicKey, in: transaction) - for device in userLinkedDevices { - guard device != userPublicKey else { continue } - let thread = TSContactThread.getOrCreateThread(withContactId: device, transaction: transaction) - thread.save(with: transaction) - let syncMessage = OWSOutgoingSyncMessage(in: thread, messageBody: "", attachmentId: nil) - syncMessage.save(with: transaction) - let messageSenderJobQueue = SSKEnvironment.shared.messageSenderJobQueue - messageSenderJobQueue.add(message: syncMessage, transaction: transaction) - } - } - */ - } - - @objc(syncContactWithPublicKey:) - public static func syncContact(_ publicKey: String) -> AnyPromise { - let syncManager = SSKEnvironment.shared.syncManager - return syncManager.syncContacts(for: [ SignalAccount(recipientId: publicKey) ]) - } - - private static func getContactsToSync(using transaction: YapDatabaseReadTransaction) -> Set { - return Set(TSContactThread.allObjectsInCollection().compactMap { $0 as? TSContactThread } - .filter { $0.shouldThreadBeVisible } - .map { $0.contactIdentifier() } - .filter { ECKeyPair.isValidHexEncodedPublicKey(candidate: $0) } - .filter { storage.getMasterHexEncodedPublicKey(for: $0, in: transaction) == nil } // Exclude secondary devices - .filter { !LokiDatabaseUtilities.isUserLinkedDevice($0, transaction: transaction) }) - } - - @objc public static func syncAllContacts() -> AnyPromise { - var publicKeys: [String] = [] - storage.dbReadConnection.read { transaction in - publicKeys = [String](getContactsToSync(using: transaction)) - } - let accounts = Set(publicKeys).map { SignalAccount(recipientId: $0) } - let syncManager = SSKEnvironment.shared.syncManager - let promises = accounts.chunked(by: 3).map { accounts -> Promise in // TODO: Does this always fit? - return Promise(syncManager.syncContacts(for: accounts)).map2 { _ in } - } - return AnyPromise.from(when(fulfilled: promises)) - } - - @objc(syncClosedGroup:transaction:) - public static func syncClosedGroup(_ thread: TSGroupThread, using transaction: YapDatabaseReadWriteTransaction) -> AnyPromise { - /* - // Prepare - let messageSenderJobQueue = SSKEnvironment.shared.messageSenderJobQueue - let group = thread.groupModel - let groupPublicKey = LKGroupUtilities.getDecodedGroupID(group.groupId) - let name = group.groupName! - let members = group.groupMemberIds.map { Data(hex: $0) } - let admins = group.groupAdminIds.map { Data(hex: $0) } - guard let groupPrivateKey = Storage.getClosedGroupPrivateKey(for: groupPublicKey) else { - print("[Loki] Couldn't get private key for SSK based closed group.") - return AnyPromise.from(Promise(error: SyncMessagesProtocolError.privateKeyMissing)) - } - // Generate ratchets for the user's linked devices - let userPublicKey = getUserHexEncodedPublicKey() - let masterPublicKey = UserDefaults.standard[.masterHexEncodedPublicKey] ?? userPublicKey - let deviceLinks = storage.getDeviceLinks(for: masterPublicKey, in: transaction) - let linkedDevices = deviceLinks.flatMap { [ $0.master.publicKey, $0.slave.publicKey ] }.filter { $0 != userPublicKey } - let senderKeys: [ClosedGroupSenderKey] = linkedDevices.map { publicKey in - let ratchet = SharedSenderKeysImplementation.shared.generateRatchet(for: groupPublicKey, senderPublicKey: publicKey, using: transaction) - return ClosedGroupSenderKey(chainKey: Data(hex: ratchet.chainKey), keyIndex: ratchet.keyIndex, publicKey: Data(hex: publicKey)) - } - // Send a closed group update message to the existing members with the linked devices' ratchets (this message is aimed at the group) - func sendMessageToGroup() { - let closedGroupUpdateMessageKind = ClosedGroupUpdateMessage.Kind.info(groupPublicKey: Data(hex: groupPublicKey), name: name, senderKeys: senderKeys, - members: members, admins: admins) - let closedGroupUpdateMessage = ClosedGroupUpdateMessage(thread: thread, kind: closedGroupUpdateMessageKind) - messageSenderJobQueue.add(message: closedGroupUpdateMessage, transaction: transaction) - } - sendMessageToGroup() - // Send closed group update messages to the linked devices using established channels - func sendMessageToLinkedDevices() { - var allSenderKeys = Storage.getAllClosedGroupSenderKeys(for: groupPublicKey) - allSenderKeys.formUnion(senderKeys) - let thread = TSContactThread.getOrCreateThread(withContactId: masterPublicKey, transaction: transaction) - thread.save(with: transaction) - let closedGroupUpdateMessageKind = ClosedGroupUpdateMessage.Kind.new(groupPublicKey: Data(hex: groupPublicKey), name: name, - groupPrivateKey: Data(hex: groupPrivateKey), senderKeys: [ClosedGroupSenderKey](allSenderKeys), members: members, admins: admins) - let closedGroupUpdateMessage = ClosedGroupUpdateMessage(thread: thread, kind: closedGroupUpdateMessageKind) - messageSenderJobQueue.add(message: closedGroupUpdateMessage, transaction: transaction) // This internally takes care of multi device - } - sendMessageToLinkedDevices() - */ - // Return a dummy promise - return AnyPromise.from(Promise { $0.fulfill(()) }) - } - - @objc public static func syncAllClosedGroups() -> AnyPromise { - var closedGroups: [TSGroupThread] = [] - TSGroupThread.enumerateCollectionObjects { object, _ in - guard let closedGroup = object as? TSGroupThread, closedGroup.groupModel.groupType == .closedGroup, - closedGroup.shouldThreadBeVisible else { return } - closedGroups.append(closedGroup) - } - let syncManager = SSKEnvironment.shared.syncManager - let promises = closedGroups.map { group -> Promise in - return Promise(syncManager.syncGroup(for: group)).map2 { _ in } - } - return AnyPromise.from(when(fulfilled: promises)) - } - - @objc public static func syncAllOpenGroups() -> AnyPromise { - fatalError("Not implemented.") - } - - // MARK: - Receiving - - @objc(isValidSyncMessage:transaction:) - public static func isValidSyncMessage(_ envelope: SSKProtoEnvelope, transaction: YapDatabaseReadTransaction) -> Bool { - let publicKey = envelope.source! // Set during UD decryption - return LokiDatabaseUtilities.isUserLinkedDevice(publicKey, transaction: transaction) - } - - public static func dropFromSyncMessageTimestampCache(_ timestamp: UInt64, for publicKey: String) { - var timestamps: Set = syncMessageTimestamps[publicKey] ?? [] - if timestamps.contains(timestamp) { timestamps.remove(timestamp) } - syncMessageTimestamps[publicKey] = timestamps - } - - @objc(isDuplicateSyncMessage:fromPublicKey:) - public static func isDuplicateSyncMessage(_ protoContent: SSKProtoContent, from publicKey: String) -> Bool { - guard let syncMessage = protoContent.syncMessage?.sent else { return false } - var timestamps: Set = syncMessageTimestamps[publicKey] ?? [] - let hasTimestamp = syncMessage.timestamp != 0 - guard hasTimestamp else { return false } - let result = timestamps.contains(syncMessage.timestamp) - timestamps.insert(syncMessage.timestamp) - syncMessageTimestamps[publicKey] = timestamps - return result - } - - @objc(updateProfileFromSyncMessageIfNeeded:wrappedIn:transaction:) - public static func updateProfileFromSyncMessageIfNeeded(_ dataMessage: SSKProtoDataMessage, wrappedIn envelope: SSKProtoEnvelope, using transaction: YapDatabaseReadWriteTransaction) { - let publicKey = envelope.source! // Set during UD decryption - guard let userMasterPublicKey = storage.getMasterHexEncodedPublicKey(for: getUserHexEncodedPublicKey(), in: transaction) else { return } - let wasSentByMasterDevice = (userMasterPublicKey == publicKey) - guard wasSentByMasterDevice else { return } - SessionMetaProtocol.updateDisplayNameIfNeeded(for: userMasterPublicKey, using: dataMessage, in: transaction) - SessionMetaProtocol.updateProfileKeyIfNeeded(for: userMasterPublicKey, using: dataMessage) - } - - /// - Note: Deprecated. - @objc(handleClosedGroupUpdateSyncMessageIfNeeded:wrappedIn:transaction:) - public static func handleClosedGroupUpdateSyncMessageIfNeeded(_ transcript: OWSIncomingSentMessageTranscript, wrappedIn envelope: SSKProtoEnvelope, using transaction: YapDatabaseReadWriteTransaction) { - // Check preconditions - let publicKey = envelope.source! // Set during UD decryption - let userLinkedDevices = LokiDatabaseUtilities.getLinkedDeviceHexEncodedPublicKeys(for: getUserHexEncodedPublicKey(), in: transaction) - let wasSentByLinkedDevice = userLinkedDevices.contains(publicKey) - guard wasSentByLinkedDevice, let group = transcript.dataMessage.group, let name = group.name else { return } - // Create or update the group - let id = group.id - let members = group.members - let newGroupThread = TSGroupThread.getOrCreateThread(withGroupId: id, groupType: .closedGroup, transaction: transaction) - let newGroupModel = TSGroupModel(title: name, memberIds: members, image: nil, groupId: id, groupType: .closedGroup, adminIds: group.admins) - newGroupThread.save(with: transaction) - newGroupThread.setGroupModel(newGroupModel, with: transaction) - OWSDisappearingMessagesJob.shared().becomeConsistent(withDisappearingDuration: transcript.dataMessage.expireTimer, thread: newGroupThread, createdByRemoteRecipientId: nil, createdInExistingGroup: true, transaction: transaction) - // Try to establish sessions with all members for which none exists yet when a group is created or updated - ClosedGroupsProtocol.establishSessionsIfNeeded(with: members, using: transaction) - // Notify the user - let contactsManager = SSKEnvironment.shared.contactsManager - let infoMessageText = newGroupThread.groupModel.getInfoStringAboutUpdate(to: newGroupModel, contactsManager: contactsManager) - let infoMessage = TSInfoMessage(timestamp: NSDate.ows_millisecondTimeStamp(), in: newGroupThread, messageType: .typeGroupUpdate, customMessage: infoMessageText) - infoMessage.save(with: transaction) - } - - /// - Note: Deprecated. - @objc(handleClosedGroupQuitSyncMessageIfNeeded:wrappedIn:transaction:) - public static func handleClosedGroupQuitSyncMessageIfNeeded(_ transcript: OWSIncomingSentMessageTranscript, wrappedIn envelope: SSKProtoEnvelope, using transaction: YapDatabaseReadWriteTransaction) { - // Check preconditions - let publicKey = envelope.source! // Set during UD decryption - let userLinkedDevices = LokiDatabaseUtilities.getLinkedDeviceHexEncodedPublicKeys(for: getUserHexEncodedPublicKey(), in: transaction) - let wasSentByLinkedDevice = userLinkedDevices.contains(publicKey) - guard wasSentByLinkedDevice, let group = transcript.dataMessage.group else { return } - // Leave the group - let groupThread = TSGroupThread.getOrCreateThread(withGroupId: group.id, groupType: .closedGroup, transaction: transaction) - groupThread.save(with: transaction) - groupThread.leaveGroup(with: transaction) - // Notify the user - let infoMessage = TSInfoMessage(timestamp: NSDate.ows_millisecondTimeStamp(), in: groupThread, messageType: .typeGroupQuit, customMessage: NSLocalizedString("GROUP_YOU_LEFT", comment: "")) - infoMessage.save(with: transaction) - } - - @objc(handleContactSyncMessageIfNeeded:wrappedIn:transaction:) - public static func handleContactSyncMessageIfNeeded(_ syncMessage: SSKProtoSyncMessage, wrappedIn envelope: SSKProtoEnvelope, using transaction: YapDatabaseReadWriteTransaction) { - let publicKey = envelope.source! // Set during UD decryption - let userLinkedDevices = LokiDatabaseUtilities.getLinkedDeviceHexEncodedPublicKeys(for: getUserHexEncodedPublicKey(), in: transaction) - let wasSentByLinkedDevice = userLinkedDevices.contains(publicKey) - guard wasSentByLinkedDevice, let contacts = syncMessage.contacts, let contactsAsData = contacts.data, !contactsAsData.isEmpty else { return } - print("[Loki] Contact sync message received.") - handleContactSyncMessageData(contactsAsData, using: transaction) - } - - public static func handleContactSyncMessageData(_ data: Data, using transaction: YapDatabaseReadWriteTransaction) { - let parser = ContactParser(data: data) - let tuples = parser.parse() - let blockedPublicKeys = tuples.filter { $0.isBlocked }.map { $0.publicKey } - let userPublicKey = getUserHexEncodedPublicKey() - let userLinkedDevices = LokiDatabaseUtilities.getLinkedDeviceHexEncodedPublicKeys(for: userPublicKey, in: transaction) - // Try to establish sessions - for (publicKey, isBlocked) in tuples { - guard !userLinkedDevices.contains(publicKey) else { continue } // Skip self and linked devices - let thread = TSContactThread.getOrCreateThread(withContactId: publicKey, transaction: transaction) - thread.shouldThreadBeVisible = true - thread.save(with: transaction) - if !isBlocked { - SessionManagementProtocol.sendSessionRequestIfNeeded(to: publicKey, using: transaction) - } - } - // Update the blocked contacts list - transaction.addCompletionQueue(DispatchQueue.main) { - SSKEnvironment.shared.blockingManager.setBlockedPhoneNumbers(blockedPublicKeys, sendSyncMessage: false) - NotificationCenter.default.post(name: .blockedContactsUpdated, object: nil) - } - } - - /// - Note: Deprecated. - @objc(handleClosedGroupSyncMessageIfNeeded:wrappedIn:transaction:) - public static func handleClosedGroupSyncMessageIfNeeded(_ syncMessage: SSKProtoSyncMessage, wrappedIn envelope: SSKProtoEnvelope, using transaction: YapDatabaseReadWriteTransaction) { - let publicKey = envelope.source! // Set during UD decryption - let userLinkedDevices = LokiDatabaseUtilities.getLinkedDeviceHexEncodedPublicKeys(for: getUserHexEncodedPublicKey(), in: transaction) - let wasSentByLinkedDevice = userLinkedDevices.contains(publicKey) - guard wasSentByLinkedDevice, let groups = syncMessage.groups, let groupsAsData = groups.data, !groupsAsData.isEmpty else { return } - print("[Loki] Closed group sync message received.") - let parser = ClosedGroupParser(data: groupsAsData) - let closedGroups = parser.parseGroupModels() - for closedGroup in closedGroups { - var thread: TSGroupThread! = TSGroupThread(groupId: closedGroup.groupId, transaction: transaction) - if thread == nil { - thread = TSGroupThread.getOrCreateThread(with: closedGroup, transaction: transaction) - thread.shouldThreadBeVisible = true - thread.save(with: transaction) - } - ClosedGroupsProtocol.establishSessionsIfNeeded(with: closedGroup.groupMemberIds, using: transaction) - } - } - - @objc(handleOpenGroupSyncMessageIfNeeded:wrappedIn:transaction:) - public static func handleOpenGroupSyncMessageIfNeeded(_ syncMessage: SSKProtoSyncMessage, wrappedIn envelope: SSKProtoEnvelope, using transaction: YapDatabaseReadWriteTransaction) { - let publicKey = envelope.source! // Set during UD decryption - let userLinkedDevices = LokiDatabaseUtilities.getLinkedDeviceHexEncodedPublicKeys(for: getUserHexEncodedPublicKey(), in: transaction) - let wasSentByLinkedDevice = userLinkedDevices.contains(publicKey) - guard wasSentByLinkedDevice else { return } - let openGroups = syncMessage.openGroups - guard !openGroups.isEmpty else { return } - print("[Loki] Open group sync message received.") - let openGroupManager = PublicChatManager.shared - let userPublicKey = UserDefaults.standard[.masterHexEncodedPublicKey] ?? getUserHexEncodedPublicKey() - let userDisplayName = SSKEnvironment.shared.profileManager.profileNameForRecipient(withID: userPublicKey, transaction: transaction) - for openGroup in openGroups { - guard openGroupManager.getChat(server: openGroup.url, channel: openGroup.channelID) == nil else { return } - openGroupManager.addChat(server: openGroup.url, channel: openGroup.channelID) - OpenGroupAPI.setDisplayName(to: userDisplayName, on: openGroup.url) - // TODO: Should we also set the profile picture here? - } - } -} diff --git a/SignalUtilitiesKit/SystemContactsFetcher.swift b/SignalUtilitiesKit/SystemContactsFetcher.swift deleted file mode 100644 index ea02c3e35..000000000 --- a/SignalUtilitiesKit/SystemContactsFetcher.swift +++ /dev/null @@ -1,411 +0,0 @@ -// -// Copyright (c) 2018 Open Whisper Systems. All rights reserved. -// - -import Foundation -import Contacts -import ContactsUI - - -enum Result { - case success(T) - case error(ErrorType) -} - -protocol ContactStoreAdaptee { - var authorizationStatus: ContactStoreAuthorizationStatus { get } - var supportsContactEditing: Bool { get } - func requestAccess(completionHandler: @escaping (Bool, Error?) -> Void) - func fetchContacts() -> Result<[Contact], Error> - func fetchCNContact(contactId: String) -> CNContact? - func startObservingChanges(changeHandler: @escaping () -> Void) -} - -public -class ContactsFrameworkContactStoreAdaptee: NSObject, ContactStoreAdaptee { - private let contactStore = CNContactStore() - private var changeHandler: (() -> Void)? - private var initializedObserver = false - private var lastSortOrder: CNContactSortOrder? - - let supportsContactEditing = true - - public static let allowedContactKeys: [CNKeyDescriptor] = [ - CNContactFormatter.descriptorForRequiredKeys(for: .fullName), - CNContactThumbnailImageDataKey as CNKeyDescriptor, // TODO full image instead of thumbnail? - CNContactPhoneNumbersKey as CNKeyDescriptor, - CNContactEmailAddressesKey as CNKeyDescriptor, - CNContactPostalAddressesKey as CNKeyDescriptor, - CNContactViewController.descriptorForRequiredKeys(), - CNContactVCardSerialization.descriptorForRequiredKeys() - ] - - var authorizationStatus: ContactStoreAuthorizationStatus { - switch CNContactStore.authorizationStatus(for: CNEntityType.contacts) { - case .notDetermined: - return .notDetermined - case .restricted: - return .restricted - case .denied: - return .denied - case .authorized: - return .authorized - } - } - - func startObservingChanges(changeHandler: @escaping () -> Void) { - // should only call once - assert(self.changeHandler == nil) - self.changeHandler = changeHandler - self.lastSortOrder = CNContactsUserDefaults.shared().sortOrder - NotificationCenter.default.addObserver(self, selector: #selector(runChangeHandler), name: .CNContactStoreDidChange, object: nil) - NotificationCenter.default.addObserver(self, selector: #selector(didBecomeActive), name: .OWSApplicationDidBecomeActive, object: nil) - } - - @objc - func didBecomeActive() { - AppReadiness.runNowOrWhenAppDidBecomeReady { - let currentSortOrder = CNContactsUserDefaults.shared().sortOrder - - guard currentSortOrder != self.lastSortOrder else { - // sort order unchanged - return - } - - Logger.info("sort order changed: \(String(describing: self.lastSortOrder)) -> \(String(describing: currentSortOrder))") - self.lastSortOrder = currentSortOrder - self.runChangeHandler() - } - } - - @objc - func runChangeHandler() { - guard let changeHandler = self.changeHandler else { - owsFailDebug("trying to run change handler before it was registered") - return - } - changeHandler() - } - - func requestAccess(completionHandler: @escaping (Bool, Error?) -> Void) { - self.contactStore.requestAccess(for: .contacts, completionHandler: completionHandler) - } - - func fetchContacts() -> Result<[Contact], Error> { - var systemContacts = [CNContact]() - do { - let contactFetchRequest = CNContactFetchRequest(keysToFetch: ContactsFrameworkContactStoreAdaptee.allowedContactKeys) - contactFetchRequest.sortOrder = .userDefault - try self.contactStore.enumerateContacts(with: contactFetchRequest) { (contact, _) -> Void in - systemContacts.append(contact) - } - } catch let error as NSError { - owsFailDebug("Failed to fetch contacts with error:\(error)") - return .error(error) - } - - let contacts = systemContacts.map { Contact(systemContact: $0) } - return .success(contacts) - } - - func fetchCNContact(contactId: String) -> CNContact? { - var result: CNContact? - do { - let contactFetchRequest = CNContactFetchRequest(keysToFetch: ContactsFrameworkContactStoreAdaptee.allowedContactKeys) - contactFetchRequest.sortOrder = .userDefault - contactFetchRequest.predicate = CNContact.predicateForContacts(withIdentifiers: [contactId]) - - try self.contactStore.enumerateContacts(with: contactFetchRequest) { (contact, _) -> Void in - guard result == nil else { - owsFailDebug("More than one contact with contact id.") - return - } - result = contact - } - } catch let error as NSError { - owsFailDebug("Failed to fetch contact with error:\(error)") - return nil - } - - return result - } -} - -@objc -public enum ContactStoreAuthorizationStatus: UInt { - case notDetermined, - restricted, - denied, - authorized -} - -@objc public protocol SystemContactsFetcherDelegate: class { - func systemContactsFetcher(_ systemContactsFetcher: SystemContactsFetcher, updatedContacts contacts: [Contact], isUserRequested: Bool) - func systemContactsFetcher(_ systemContactsFetcher: SystemContactsFetcher, hasAuthorizationStatus authorizationStatus: ContactStoreAuthorizationStatus) -} - -@objc -public class SystemContactsFetcher: NSObject { - - private let serialQueue = DispatchQueue(label: "SystemContactsFetcherQueue") - - var lastContactUpdateHash: Int? - var lastDelegateNotificationDate: Date? - let contactStoreAdapter: ContactsFrameworkContactStoreAdaptee - - @objc - public weak var delegate: SystemContactsFetcherDelegate? - - public var authorizationStatus: ContactStoreAuthorizationStatus { - return contactStoreAdapter.authorizationStatus - } - - @objc - public var isAuthorized: Bool { - guard self.authorizationStatus != .notDetermined else { - owsFailDebug("should have called `requestOnce` before checking authorization status.") - return false - } - - return self.authorizationStatus == .authorized - } - - @objc - public var isDenied: Bool { - return self.authorizationStatus == .denied - } - - @objc - public private(set) var systemContactsHaveBeenRequestedAtLeastOnce = false - private var hasSetupObservation = false - - @objc public override init() { - self.contactStoreAdapter = ContactsFrameworkContactStoreAdaptee() - - super.init() - - SwiftSingletons.register(self) - } - - @objc - public var supportsContactEditing: Bool { - return self.contactStoreAdapter.supportsContactEditing - } - - private func setupObservationIfNecessary() { - AssertIsOnMainThread() - guard !hasSetupObservation else { - return - } - hasSetupObservation = true - self.contactStoreAdapter.startObservingChanges { [weak self] in - DispatchQueue.main.async { - self?.refreshAfterContactsChange() - } - } - } - - /** - * Ensures we've requested access for system contacts. This can be used in multiple places, - * where we might need contact access, but will ensure we don't wastefully reload contacts - * if we have already fetched contacts. - * - * @param completionParam completion handler is called on main thread. - */ - @objc - public func requestOnce(completion completionParam: ((Error?) -> Void)?) { - AssertIsOnMainThread() - - // Ensure completion is invoked on main thread. - let completion = { error in - DispatchMainThreadSafe({ - completionParam?(error) - }) - } - - guard !systemContactsHaveBeenRequestedAtLeastOnce else { - completion(nil) - return - } - setupObservationIfNecessary() - - switch authorizationStatus { - case .notDetermined: - return completion(nil) - case .authorized: - self.updateContacts(completion: completion) - case .denied, .restricted: - Logger.debug("contacts were \(self.authorizationStatus)") - self.delegate?.systemContactsFetcher(self, hasAuthorizationStatus: authorizationStatus) - completion(nil) - } - } - - @objc - public func fetchOnceIfAlreadyAuthorized() { - AssertIsOnMainThread() - guard authorizationStatus == .authorized else { - self.delegate?.systemContactsFetcher(self, hasAuthorizationStatus: authorizationStatus) - return - } - guard !systemContactsHaveBeenRequestedAtLeastOnce else { - return - } - - updateContacts(completion: nil, isUserRequested: false) - } - - @objc - public func userRequestedRefresh(completion: @escaping (Error?) -> Void) { - AssertIsOnMainThread() - - guard authorizationStatus == .authorized else { - owsFailDebug("should have already requested contact access") - self.delegate?.systemContactsFetcher(self, hasAuthorizationStatus: authorizationStatus) - completion(nil) - return - } - - updateContacts(completion: completion, isUserRequested: true) - } - - @objc - public func refreshAfterContactsChange() { - AssertIsOnMainThread() - - guard authorizationStatus == .authorized else { - Logger.info("ignoring contacts change; no access.") - self.delegate?.systemContactsFetcher(self, hasAuthorizationStatus: authorizationStatus) - return - } - - updateContacts(completion: nil, isUserRequested: false) - } - - private func updateContacts(completion completionParam: ((Error?) -> Void)?, isUserRequested: Bool = false) { - AssertIsOnMainThread() - - var backgroundTask: OWSBackgroundTask? = OWSBackgroundTask(label: "\(#function)", completionBlock: { [weak self] status in - AssertIsOnMainThread() - - guard status == .expired else { - return - } - - guard let _ = self else { - return - } - Logger.error("background task time ran out before contacts fetch completed.") - }) - - // Ensure completion is invoked on main thread. - let completion: (Error?) -> Void = { error in - DispatchMainThreadSafe({ - completionParam?(error) - - assert(backgroundTask != nil) - backgroundTask = nil - }) - } - - systemContactsHaveBeenRequestedAtLeastOnce = true - setupObservationIfNecessary() - - serialQueue.async { - - Logger.info("fetching contacts") - - var fetchedContacts: [Contact]? - switch self.contactStoreAdapter.fetchContacts() { - case .success(let result): - fetchedContacts = result - case .error(let error): - completion(error) - return - } - - guard let contacts = fetchedContacts else { - owsFailDebug("contacts was unexpectedly not set.") - return completion(nil) - } - - Logger.info("fetched \(contacts.count) contacts.") - let contactsHash = HashableArray(contacts).hashValue - - DispatchQueue.main.async { - var shouldNotifyDelegate = false - - if self.lastContactUpdateHash != contactsHash { - Logger.info("contact hash changed. new contactsHash: \(contactsHash)") - shouldNotifyDelegate = true - } else if isUserRequested { - Logger.info("ignoring debounce due to user request") - shouldNotifyDelegate = true - } else { - - // If nothing has changed, only notify delegate (to perform contact intersection) every N hours - if let lastDelegateNotificationDate = self.lastDelegateNotificationDate { - let kDebounceInterval = TimeInterval(12 * 60 * 60) - - let expiresAtDate = Date(timeInterval: kDebounceInterval, since: lastDelegateNotificationDate) - if Date() > expiresAtDate { - Logger.info("debounce interval expired at: \(expiresAtDate)") - shouldNotifyDelegate = true - } else { - Logger.info("ignoring since debounce interval hasn't expired") - } - } else { - Logger.info("first contact fetch. contactsHash: \(contactsHash)") - shouldNotifyDelegate = true - } - } - - guard shouldNotifyDelegate else { - Logger.info("no reason to notify delegate.") - - completion(nil) - - return - } - - self.lastDelegateNotificationDate = Date() - self.lastContactUpdateHash = contactsHash - - self.delegate?.systemContactsFetcher(self, updatedContacts: contacts, isUserRequested: isUserRequested) - completion(nil) - } - } - } - - @objc - public func fetchCNContact(contactId: String) -> CNContact? { - guard authorizationStatus == .authorized else { - Logger.error("contact fetch failed; no access.") - return nil - } - - return contactStoreAdapter.fetchCNContact(contactId: contactId) - } -} - -struct HashableArray: Hashable { - var elements: [Element] - init(_ elements: [Element]) { - self.elements = elements - } - - var hashValue: Int { - // random generated 32bit number - let base = 224712574 - var position = 0 - return elements.reduce(base) { (result, element) -> Int in - // Make sure change in sort order invalidates hash - position += 1 - return result ^ element.hashValue + position - } - } - - static func == (lhs: HashableArray, rhs: HashableArray) -> Bool { - return lhs.hashValue == rhs.hashValue - } -} diff --git a/SignalUtilitiesKit/TSAccountManager.m b/SignalUtilitiesKit/TSAccountManager.m index 724cdfbad..685b76fb5 100644 --- a/SignalUtilitiesKit/TSAccountManager.m +++ b/SignalUtilitiesKit/TSAccountManager.m @@ -9,10 +9,10 @@ #import "NSURLSessionDataTask+StatusCode.h" #import "OWSError.h" #import "OWSPrimaryStorage+SessionStore.h" -#import "OWSRequestFactory.h" + #import "ProfileManagerProtocol.h" #import "SSKEnvironment.h" -#import "TSNetworkManager.h" + #import "TSPreKeyManager.h" #import "YapDatabaseConnection+OWS.h" #import "YapDatabaseTransaction+OWS.h" @@ -107,13 +107,6 @@ NSString *const TSAccountManager_NeedsAccountAttributesUpdateKey = @"TSAccountMa #pragma mark - Dependencies -- (TSNetworkManager *)networkManager -{ - OWSAssertDebug(SSKEnvironment.shared.networkManager); - - return SSKEnvironment.shared.networkManager; -} - - (id)profileManager { OWSAssertDebug(SSKEnvironment.shared.profileManager); @@ -318,41 +311,6 @@ NSString *const TSAccountManager_NeedsAccountAttributesUpdateKey = @"TSAccountMa }); } -- (void)registerWithPhoneNumber:(NSString *)phoneNumber - captchaToken:(nullable NSString *)captchaToken - success:(void (^)(void))successBlock - failure:(void (^)(NSError *error))failureBlock - smsVerification:(BOOL)isSMS - -{ - if ([self isRegistered]) { - failureBlock([NSError errorWithDomain:@"tsaccountmanager.verify" code:4000 userInfo:nil]); - return; - } - - // The country code of TSAccountManager.phoneNumberAwaitingVerification is used to - // determine whether or not to use domain fronting, so it needs to be set _before_ - // we make our verification code request. - self.phoneNumberAwaitingVerification = phoneNumber; - - TSRequest *request = - [OWSRequestFactory requestVerificationCodeRequestWithPhoneNumber:phoneNumber - captchaToken:captchaToken - transport:(isSMS ? TSVerificationTransportSMS - : TSVerificationTransportVoice)]; - [[TSNetworkManager sharedManager] makeRequest:request - success:^(NSURLSessionDataTask *task, id responseObject) { - OWSLogInfo(@"Successfully requested verification code request for number: %@ method:%@", - phoneNumber, - isSMS ? @"SMS" : @"Voice"); - successBlock(); - } - failure:^(NSURLSessionDataTask *task, NSError *error) { - OWSLogError(@"Failed to request verification code request with error:%@", error); - failureBlock(error); - }]; -} - - (void)rerequestSMSWithCaptchaToken:(nullable NSString *)captchaToken success:(void (^)(void))successBlock failure:(void (^)(NSError *error))failureBlock @@ -382,107 +340,6 @@ NSString *const TSAccountManager_NeedsAccountAttributesUpdateKey = @"TSAccountMa smsVerification:NO]; } -- (void)verifyAccountWithCode:(NSString *)verificationCode - pin:(nullable NSString *)pin - success:(void (^)(void))successBlock - failure:(void (^)(NSError *error))failureBlock -{ - NSString *authToken = [[self class] generateNewAccountAuthenticationToken]; - NSString *phoneNumber = self.phoneNumberAwaitingVerification; - - OWSAssertDebug(authToken); - OWSAssertDebug(phoneNumber); - - TSRequest *request = [OWSRequestFactory verifyCodeRequestWithVerificationCode:verificationCode - forNumber:phoneNumber - pin:pin - authKey:authToken]; - - [self.networkManager makeRequest:request - success:^(NSURLSessionDataTask *task, id responseObject) { - NSHTTPURLResponse *response = (NSHTTPURLResponse *)task.response; - long statuscode = response.statusCode; - - switch (statuscode) { - case 200: - case 204: { - OWSLogInfo(@"Verification code accepted."); - - [self storeServerAuthToken:authToken]; - - [[[SignalServiceRestClient new] updateAccountAttributesObjC] - .thenInBackground(^{ - return [AnyPromise promiseWithResolverBlock:^(PMKResolver resolve) { - [TSPreKeyManager - createPreKeysWithSuccess:^{ - resolve(@(1)); - } - failure:^(NSError *error) { - resolve(error); - }]; - }]; - }) - .then(^{ - [self.profileManager fetchLocalUsersProfile]; - }) - .then(^{ - successBlock(); - }) - .catchInBackground(^(NSError *error) { - OWSLogError(@"Error: %@", error); - failureBlock(error); - }) retainUntilComplete]; - - break; - } - default: { - OWSLogError(@"Unexpected status while verifying code: %ld", statuscode); - NSError *error = OWSErrorMakeUnableToProcessServerResponseError(); - failureBlock(error); - break; - } - } - } - failure:^(NSURLSessionDataTask *task, NSError *error) { - OWSAssertDebug([error.domain isEqualToString:TSNetworkManagerErrorDomain]); - - OWSLogWarn(@"Error verifying code: %@", error.debugDescription); - - switch (error.code) { - case 403: { - NSError *userError = OWSErrorWithCodeDescription(OWSErrorCodeUserError, - NSLocalizedString(@"REGISTRATION_VERIFICATION_FAILED_WRONG_CODE_DESCRIPTION", - "Error message indicating that registration failed due to a missing or incorrect " - "verification code.")); - failureBlock(userError); - break; - } - case 413: { - // In the case of the "rate limiting" error, we want to show the - // "recovery suggestion", not the error's "description." - NSError *userError - = OWSErrorWithCodeDescription(OWSErrorCodeUserError, error.localizedRecoverySuggestion); - failureBlock(userError); - break; - } - case 423: { - NSString *localizedMessage = NSLocalizedString(@"REGISTRATION_VERIFICATION_FAILED_WRONG_PIN", - "Error message indicating that registration failed due to a missing or incorrect 2FA PIN."); - OWSLogError(@"2FA PIN required: %ld", (long)error.code); - NSError *error - = OWSErrorWithCodeDescription(OWSErrorCodeRegistrationMissing2FAPIN, localizedMessage); - failureBlock(error); - break; - } - default: { - OWSLogError(@"verifying code failed with unknown error: %@", error); - failureBlock(error); - break; - } - } - }]; -} - #pragma mark Server keying material + (NSString *)generateNewAccountAuthenticationToken { @@ -522,28 +379,6 @@ NSString *const TSAccountManager_NeedsAccountAttributesUpdateKey = @"TSAccountMa }]; } -+ (void)unregisterTextSecureWithSuccess:(void (^)(void))success failure:(void (^)(NSError *error))failureBlock -{ - TSRequest *request = [OWSRequestFactory unregisterAccountRequest]; - [[TSNetworkManager sharedManager] makeRequest:request - success:^(NSURLSessionDataTask *task, id responseObject) { - OWSLogInfo(@"Successfully unregistered"); - success(); - - // This is called from `[AppSettingsViewController proceedToUnregistration]` whose - // success handler calls `[Environment resetAppData]`. - // This method, after calling that success handler, fires - // `RegistrationStateDidChangeNotification` which is only safe to fire after - // the data store is reset. - - [self.sharedInstance postRegistrationStateDidChangeNotification]; - } - failure:^(NSURLSessionDataTask *task, NSError *error) { - OWSLogError(@"Failed to unregister with error: %@", error); - failureBlock(error); - }]; -} - - (void)yapDatabaseModifiedExternally:(NSNotification *)notification { OWSAssertIsOnMainThread(); @@ -727,20 +562,6 @@ NSString *const TSAccountManager_NeedsAccountAttributesUpdateKey = @"TSAccountMa return promise; } -- (AnyPromise *)performUpdateAccountAttributes -{ - AnyPromise *promise = [[SignalServiceRestClient new] updateAccountAttributesObjC]; - promise = promise.then(^(id value) { - // Fetch the local profile, as we may have changed its - // account attributes. Specifically, we need to determine - // if all devices for our account now support UD for sync - // messages. - [self.profileManager fetchLocalUsersProfile]; - }); - [promise retainUntilComplete]; - return promise; -} - - (void)reachabilityChanged { OWSAssertIsOnMainThread(); diff --git a/SignalUtilitiesKit/TSConstants.h b/SignalUtilitiesKit/TSConstants.h index 8ddac95c1..3d8542585 100644 --- a/SignalUtilitiesKit/TSConstants.h +++ b/SignalUtilitiesKit/TSConstants.h @@ -19,7 +19,7 @@ typedef NS_ENUM(NSInteger, TSWhisperMessageType) { TSUnencryptedWhisperMessageType = 4, TSUnidentifiedSenderMessageType = 6, TSClosedGroupCiphertextMessageType = 7, - TSFallbackMessageType = 101 // Loki: Encrypted using the fallback session cipher. Contains a pre key bundle if it's a session request. + TSFallbackMessageType = 101 }; #pragma mark Server Address diff --git a/SignalUtilitiesKit/TSDatabaseView.h b/SignalUtilitiesKit/TSDatabaseView.h index 554d5b985..bdd52ee8b 100644 --- a/SignalUtilitiesKit/TSDatabaseView.h +++ b/SignalUtilitiesKit/TSDatabaseView.h @@ -65,8 +65,6 @@ extern NSString *const TSLazyRestoreAttachmentsDatabaseViewExtensionName; + (void)asyncRegisterThreadSpecialMessagesDatabaseView:(OWSStorage *)storage; -+ (void)asyncRegisterSecondaryDevicesDatabaseView:(OWSStorage *)storage; - + (void)asyncRegisterLazyRestoreAttachmentsDatabaseView:(OWSStorage *)storage; @end diff --git a/SignalUtilitiesKit/TSDatabaseView.m b/SignalUtilitiesKit/TSDatabaseView.m index e7d0f15f7..86b1658ec 100644 --- a/SignalUtilitiesKit/TSDatabaseView.m +++ b/SignalUtilitiesKit/TSDatabaseView.m @@ -3,7 +3,7 @@ // #import "TSDatabaseView.h" -#import "OWSDevice.h" + #import "OWSReadTracking.h" #import "TSAttachment.h" #import "TSAttachmentPointer.h" @@ -282,7 +282,6 @@ NSString *const TSLazyRestoreAttachmentsGroup = @"TSLazyRestoreAttachmentsGroup" return nil; } TSThread *thread = (TSThread *)object; - if (thread.isSlaveThread) { return nil; } if (thread.shouldThreadBeVisible) { // Do nothing; we never hide threads that have ever had a message. @@ -371,56 +370,6 @@ NSString *const TSLazyRestoreAttachmentsGroup = @"TSLazyRestoreAttachmentsGroup" }]; } -+ (void)asyncRegisterSecondaryDevicesDatabaseView:(OWSStorage *)storage -{ - YapDatabaseViewGrouping *viewGrouping = [YapDatabaseViewGrouping withObjectBlock:^NSString *_Nullable( - YapDatabaseReadTransaction *transaction, NSString *collection, NSString *key, id object) { - if (![object isKindOfClass:[OWSDevice class]]) { - OWSFailDebug(@"Unexpected entity %@ in collection: %@", [object class], collection); - return nil; - } - OWSDevice *device = (OWSDevice *)object; - if (![device isPrimaryDevice]) { - return TSSecondaryDevicesGroup; - } - return nil; - }]; - - YapDatabaseViewSorting *viewSorting = [YapDatabaseViewSorting withObjectBlock:^NSComparisonResult( - YapDatabaseReadTransaction *transaction, - NSString *group, - NSString *collection1, - NSString *key1, - id object1, - NSString *collection2, - NSString *key2, - id object2) { - if (![object1 isKindOfClass:[OWSDevice class]]) { - OWSFailDebug(@"Unexpected entity %@ in collection: %@", [object1 class], collection1); - return NSOrderedSame; - } - if (![object2 isKindOfClass:[OWSDevice class]]) { - OWSFailDebug(@"Unexpected entity %@ in collection: %@", [object2 class], collection2); - return NSOrderedSame; - } - OWSDevice *device1 = (OWSDevice *)object1; - OWSDevice *device2 = (OWSDevice *)object2; - - return [device2.createdAt compare:device1.createdAt]; - }]; - - YapDatabaseViewOptions *options = [YapDatabaseViewOptions new]; - options.isPersistent = YES; - - NSSet *deviceCollection = [NSSet setWithObject:[OWSDevice collection]]; - options.allowedCollections = [[YapWhitelistBlacklist alloc] initWithWhitelist:deviceCollection]; - - YapDatabaseView *view = - [[YapDatabaseAutoView alloc] initWithGrouping:viewGrouping sorting:viewSorting versionTag:@"3" options:options]; - - [storage asyncRegisterExtension:view withName:TSSecondaryDevicesDatabaseViewExtensionName]; -} - + (void)asyncRegisterLazyRestoreAttachmentsDatabaseView:(OWSStorage *)storage { YapDatabaseViewGrouping *viewGrouping = [YapDatabaseViewGrouping withObjectBlock:^NSString *_Nullable( diff --git a/SignalUtilitiesKit/TSNetworkManager.h b/SignalUtilitiesKit/TSNetworkManager.h deleted file mode 100644 index 0dda2495a..000000000 --- a/SignalUtilitiesKit/TSNetworkManager.h +++ /dev/null @@ -1,45 +0,0 @@ -// -// Copyright (c) 2018 Open Whisper Systems. All rights reserved. -// - -#import - -NS_ASSUME_NONNULL_BEGIN - -extern NSErrorDomain const TSNetworkManagerErrorDomain; -typedef NS_ERROR_ENUM(TSNetworkManagerErrorDomain, TSNetworkManagerError){ - // It's a shame to use 0 as an enum value for anything other than something like default or unknown, because it's - // indistinguishable from "not set" in Objc. - // However this value was existing behavior for connectivity errors, and since we might be using this in other - // places I didn't want to change it out of hand - TSNetworkManagerErrorFailedConnection = 0, - // Other TSNetworkManagerError's use HTTP status codes (e.g. 404, etc) -}; - -BOOL IsNSErrorNetworkFailure(NSError *_Nullable error); - -typedef void (^TSNetworkManagerSuccess)(NSURLSessionDataTask *task, _Nullable id responseObject); -typedef void (^TSNetworkManagerFailure)(NSURLSessionDataTask *task, NSError *error); - -@class TSRequest; - -@interface TSNetworkManager : NSObject - -- (instancetype)init NS_UNAVAILABLE; - -- (instancetype)initDefault; - -+ (instancetype)sharedManager; - -- (void)makeRequest:(TSRequest *)request - success:(TSNetworkManagerSuccess)success - failure:(TSNetworkManagerFailure)failure NS_SWIFT_NAME(makeRequest(_:success:failure:)); - -- (void)makeRequest:(TSRequest *)request - completionQueue:(dispatch_queue_t)completionQueue - success:(TSNetworkManagerSuccess)success - failure:(TSNetworkManagerFailure)failure NS_SWIFT_NAME(makeRequest(_:completionQueue:success:failure:)); - -@end - -NS_ASSUME_NONNULL_END diff --git a/SignalUtilitiesKit/TSNetworkManager.m b/SignalUtilitiesKit/TSNetworkManager.m deleted file mode 100644 index 20c39fb0b..000000000 --- a/SignalUtilitiesKit/TSNetworkManager.m +++ /dev/null @@ -1,588 +0,0 @@ -// -// Copyright (c) 2019 Open Whisper Systems. All rights reserved. -// - -#import "TSNetworkManager.h" -#import "AppContext.h" -#import "NSError+messageSending.h" -#import "NSURLSessionDataTask+StatusCode.h" -#import "OWSError.h" -#import "OWSQueues.h" -#import "OWSSignalService.h" -#import "SSKEnvironment.h" -#import "TSAccountManager.h" -#import "TSRequest.h" -#import -#import -#import -#import "SSKAsserts.h" - -NS_ASSUME_NONNULL_BEGIN - -NSErrorDomain const TSNetworkManagerErrorDomain = @"SignalServiceKit.TSNetworkManager"; - -BOOL IsNSErrorNetworkFailure(NSError *_Nullable error) -{ - return ([error.domain isEqualToString:TSNetworkManagerErrorDomain] - && error.code == TSNetworkManagerErrorFailedConnection); -} - -dispatch_queue_t NetworkManagerQueue() -{ - static dispatch_queue_t serialQueue; - static dispatch_once_t onceToken; - dispatch_once(&onceToken, ^{ - serialQueue = dispatch_queue_create("org.whispersystems.networkManager", DISPATCH_QUEUE_SERIAL); - }); - return serialQueue; -} - -#pragma mark - - -@interface OWSSessionManager : NSObject - -@property (nonatomic, readonly) AFHTTPSessionManager *sessionManager; -@property (nonatomic, readonly) NSDictionary *defaultHeaders; - -@end - -#pragma mark - - -@implementation OWSSessionManager - -#pragma mark - Dependencies - -- (OWSSignalService *)signalService -{ - return [OWSSignalService sharedInstance]; -} - -#pragma mark - - -- (instancetype)init -{ - AssertOnDispatchQueue(NetworkManagerQueue()); - - self = [super init]; - if (!self) { - return self; - } - - _sessionManager = [self.signalService buildSignalServiceSessionManager]; - self.sessionManager.completionQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0); - // NOTE: We could enable HTTPShouldUsePipelining here. - // Make a copy of the default headers for this session manager. - _defaultHeaders = [self.sessionManager.requestSerializer.HTTPRequestHeaders copy]; - - return self; -} - -// TSNetworkManager.serialQueue -- (void)performRequest:(TSRequest *)request - canUseAuth:(BOOL)canUseAuth - success:(TSNetworkManagerSuccess)success - failure:(TSNetworkManagerFailure)failure -{ - AssertOnDispatchQueue(NetworkManagerQueue()); - OWSAssertDebug(request); - OWSAssertDebug(success); - OWSAssertDebug(failure); - - // Clear all headers so that we don't retain headers from previous requests. - for (NSString *headerField in self.sessionManager.requestSerializer.HTTPRequestHeaders.allKeys.copy) { - [self.sessionManager.requestSerializer setValue:nil forHTTPHeaderField:headerField]; - } - - // Apply the default headers for this session manager. - for (NSString *headerField in self.defaultHeaders) { - NSString *headerValue = self.defaultHeaders[headerField]; - [self.sessionManager.requestSerializer setValue:headerValue forHTTPHeaderField:headerField]; - } - -// if (canUseAuth && request.shouldHaveAuthorizationHeaders) { -// [self.sessionManager.requestSerializer setAuthorizationHeaderFieldWithUsername:request.authUsername -// password:request.authPassword]; -// } - - // Honor the request's headers. - for (NSString *headerField in request.allHTTPHeaderFields) { - NSString *headerValue = request.allHTTPHeaderFields[headerField]; - [self.sessionManager.requestSerializer setValue:headerValue forHTTPHeaderField:headerField]; - } - - self.sessionManager.requestSerializer.timeoutInterval = request.timeoutInterval; - - if ([request.HTTPMethod isEqualToString:@"GET"]) { - [self.sessionManager GET:request.URL.absoluteString - parameters:request.parameters - headers:request.allHTTPHeaderFields - progress:nil - success:success - failure:failure]; - } else if ([request.HTTPMethod isEqualToString:@"POST"]) { - [self.sessionManager POST:request.URL.absoluteString - parameters:request.parameters - headers:request.allHTTPHeaderFields - progress:nil - success:success - failure:failure]; - } else if ([request.HTTPMethod isEqualToString:@"PUT"]) { - [self.sessionManager PUT:request.URL.absoluteString - parameters:request.parameters - headers:request.allHTTPHeaderFields - success:success - failure:failure]; - } else if ([request.HTTPMethod isEqualToString:@"DELETE"]) { - [self.sessionManager DELETE:request.URL.absoluteString - parameters:request.parameters - headers:request.allHTTPHeaderFields - success:success - failure:failure]; - } else if ([request.HTTPMethod isEqualToString:@"PATCH"]) { - [self.sessionManager PATCH:request.URL.absoluteString - parameters:request.parameters - headers:request.allHTTPHeaderFields - success:success - failure:failure]; - } else { - OWSLogError(@"Trying to perform HTTP operation with unknown verb: %@", request.HTTPMethod); - } -} - -@end - -#pragma mark - - -// You might be asking: "why use a pool at all? We're only using the session manager -// on the serial queue, so can't we just have two session managers (1 UD, 1 non-UD) -// that we use for all requests?" -// -// That assumes that the session managers are not stateful in a way where concurrent -// requests can interfere with each other. I audited the AFNetworking codebase and my -// reading is that sessions managers are safe to use in that way - that the state of -// their properties (e.g. header values) is only used when building the request and -// can be safely changed after performRequest is complete. -// -// But I decided that I didn't want to (silently) bake that assumption into the -// codebase, since the stakes are high. The session managers aren't expensive. IMO -// better to use a pool and not re-use a session manager until its request succeeds -// or fails. -@interface OWSSessionManagerPool : NSObject - -@property (nonatomic) NSMutableArray *pool; - -@end - -#pragma mark - - -@implementation OWSSessionManagerPool - -- (instancetype)init -{ - self = [super init]; - if (!self) { - return self; - } - - self.pool = [NSMutableArray new]; - - return self; -} - -- (OWSSessionManager *)get -{ - AssertOnDispatchQueue(NetworkManagerQueue()); - - OWSSessionManager *_Nullable sessionManager = [self.pool lastObject]; - if (sessionManager) { - [self.pool removeLastObject]; - } else { - sessionManager = [OWSSessionManager new]; - } - OWSAssertDebug(sessionManager); - return sessionManager; -} - -- (void)returnToPool:(OWSSessionManager *)sessionManager -{ - AssertOnDispatchQueue(NetworkManagerQueue()); - - OWSAssertDebug(sessionManager); - const NSUInteger kMaxPoolSize = 3; - if (self.pool.count >= kMaxPoolSize) { - // Discard - return; - } - [self.pool addObject:sessionManager]; -} - -@end - -#pragma mark - - -@interface TSNetworkManager () - -// These properties should only be accessed on serialQueue. -@property (atomic, readonly) OWSSessionManagerPool *udSessionManagerPool; -@property (atomic, readonly) OWSSessionManagerPool *nonUdSessionManagerPool; - -@end - -#pragma mark - - -@implementation TSNetworkManager - -#pragma mark - Dependencies - -+ (TSAccountManager *)tsAccountManager -{ - return TSAccountManager.sharedInstance; -} - -#pragma mark - Singleton - -+ (instancetype)sharedManager -{ - OWSAssertDebug(SSKEnvironment.shared.networkManager); - - return SSKEnvironment.shared.networkManager; -} - -- (instancetype)initDefault -{ - self = [super init]; - if (!self) { - return self; - } - - _udSessionManagerPool = [OWSSessionManagerPool new]; - _nonUdSessionManagerPool = [OWSSessionManagerPool new]; - - OWSSingletonAssert(); - - return self; -} - -#pragma mark Manager Methods - -- (void)makeRequest:(TSRequest *)request - success:(TSNetworkManagerSuccess)success - failure:(TSNetworkManagerFailure)failure -{ - return [self makeRequest:request completionQueue:dispatch_get_main_queue() success:success failure:failure]; -} - -- (void)makeRequest:(TSRequest *)request - completionQueue:(dispatch_queue_t)completionQueue - success:(TSNetworkManagerSuccess)success - failure:(TSNetworkManagerFailure)failure -{ - OWSAssertDebug(request); - OWSAssertDebug(success); - OWSAssertDebug(failure); - - dispatch_async(NetworkManagerQueue(), ^{ - [self makeRequestSync:request completionQueue:completionQueue success:success failure:failure]; - }); -} - -- (void)makeRequestSync:(TSRequest *)request - completionQueue:(dispatch_queue_t)completionQueue - success:(TSNetworkManagerSuccess)successParam - failure:(TSNetworkManagerFailure)failureParam -{ - OWSAssertDebug(request); - OWSAssertDebug(successParam); - OWSAssertDebug(failureParam); - -// BOOL isUDRequest = request.isUDRequest; - NSString *label = @"UD request"; -// BOOL canUseAuth = !isUDRequest; -// if (isUDRequest) { -// OWSAssert(!request.shouldHaveAuthorizationHeaders); -// } -// OWSLogInfo(@"Making %@: %@", label, request); - - OWSSessionManagerPool *sessionManagerPool = self.udSessionManagerPool; - OWSSessionManager *sessionManager = [sessionManagerPool get]; - - TSNetworkManagerSuccess success = ^(NSURLSessionDataTask *task, _Nullable id responseObject) { - dispatch_async(NetworkManagerQueue(), ^{ - [sessionManagerPool returnToPool:sessionManager]; - }); - - dispatch_async(completionQueue, ^{ - OWSLogInfo(@"%@ succeeded : %@", label, request); - -// if (canUseAuth && request.shouldHaveAuthorizationHeaders) { -// [TSNetworkManager.tsAccountManager setIsDeregistered:NO]; -// } - - successParam(task, responseObject); - - [OutageDetection.sharedManager reportConnectionSuccess]; - }); - }; - TSNetworkManagerFailure failure = ^(NSURLSessionDataTask *task, NSError *error) { - dispatch_async(NetworkManagerQueue(), ^{ - [sessionManagerPool returnToPool:sessionManager]; - }); - - [TSNetworkManager - handleNetworkFailure:^(NSURLSessionDataTask *task, NSError *error) { - dispatch_async(completionQueue, ^{ - failureParam(task, error); - }); - } - request:request - task:task - error:error]; - }; - - [sessionManager performRequest:request canUseAuth:NO success:success failure:failure]; -} - -#ifdef DEBUG -+ (void)logCurlForTask:(NSURLSessionDataTask *)task -{ - NSMutableArray *curlComponents = [NSMutableArray new]; - [curlComponents addObject:@"curl"]; - // Verbose - [curlComponents addObject:@"-v"]; - // Insecure - [curlComponents addObject:@"-k"]; - // Method, e.g. GET - [curlComponents addObject:@"-X"]; - [curlComponents addObject:task.originalRequest.HTTPMethod]; - // Headers - for (NSString *header in task.originalRequest.allHTTPHeaderFields) { - NSString *headerValue = task.originalRequest.allHTTPHeaderFields[header]; - // We don't yet support escaping header values. - // If these asserts trip, we'll need to add that. - OWSAssertDebug([header rangeOfString:@"'"].location == NSNotFound); - OWSAssertDebug([headerValue rangeOfString:@"'"].location == NSNotFound); - - [curlComponents addObject:@"-H"]; - [curlComponents addObject:[NSString stringWithFormat:@"'%@: %@'", header, headerValue]]; - } - // Body/parameters (e.g. JSON payload) - if (task.originalRequest.HTTPBody) { - NSString *jsonBody = - [[NSString alloc] initWithData:task.originalRequest.HTTPBody encoding:NSUTF8StringEncoding]; - // We don't yet support escaping JSON. - // If these asserts trip, we'll need to add that. - OWSAssertDebug([jsonBody rangeOfString:@"'"].location == NSNotFound); - [curlComponents addObject:@"--data-ascii"]; - [curlComponents addObject:[NSString stringWithFormat:@"'%@'", jsonBody]]; - } - // TODO: Add support for cookies. - [curlComponents addObject:task.originalRequest.URL.absoluteString]; - NSString *curlCommand = [curlComponents componentsJoinedByString:@" "]; - OWSLogVerbose(@"curl for failed request: %@", curlCommand); -} -#endif - -+ (void)handleNetworkFailure:(TSNetworkManagerFailure)failureBlock - request:(TSRequest *)request - task:(NSURLSessionDataTask *)task - error:(NSError *)networkError -{ - OWSAssertDebug(failureBlock); - OWSAssertDebug(request); - OWSAssertDebug(networkError); - - NSInteger statusCode = [task statusCode]; - -#ifdef DEBUG - [TSNetworkManager logCurlForTask:task]; -#endif - - [OutageDetection.sharedManager reportConnectionFailure]; - - NSError *error = [self errorWithHTTPCode:statusCode - description:nil - failureReason:nil - recoverySuggestion:nil - fallbackError:networkError]; - - switch (statusCode) { - case 0: { - NSError *connectivityError = - [self errorWithHTTPCode:TSNetworkManagerErrorFailedConnection - description:NSLocalizedString(@"ERROR_DESCRIPTION_NO_INTERNET", - @"Generic error used whenever Signal can't contact the server") - failureReason:networkError.localizedFailureReason - recoverySuggestion:NSLocalizedString(@"NETWORK_ERROR_RECOVERY", nil) - fallbackError:networkError]; - connectivityError.isRetryable = YES; - - OWSLogWarn(@"The network request failed because of a connectivity error: %@", request); - failureBlock(task, connectivityError); - break; - } - case 400: { - OWSLogError(@"The request contains an invalid parameter : %@, %@", networkError.debugDescription, request); - - error.isRetryable = NO; - - failureBlock(task, error); - break; - } - case 401: { - OWSLogError(@"The server returned an error about the authorization header: %@, %@", - networkError.debugDescription, - request); - error.isRetryable = NO; - [self deregisterAfterAuthErrorIfNecessary:task request:request statusCode:statusCode]; - failureBlock(task, error); - break; - } - case 403: { - OWSLogError( - @"The server returned an authentication failure: %@, %@", networkError.debugDescription, request); - error.isRetryable = NO; - [self deregisterAfterAuthErrorIfNecessary:task request:request statusCode:statusCode]; - failureBlock(task, error); - break; - } - case 404: { - OWSLogError(@"The requested resource could not be found: %@, %@", networkError.debugDescription, request); - error.isRetryable = NO; - failureBlock(task, error); - break; - } - case 411: { - OWSLogInfo(@"Multi-device pairing: %ld, %@, %@", (long)statusCode, networkError.debugDescription, request); - NSError *customError = [self errorWithHTTPCode:statusCode - description:NSLocalizedString(@"MULTIDEVICE_PAIRING_MAX_DESC", - @"alert title: cannot link - reached max linked devices") - failureReason:networkError.localizedFailureReason - recoverySuggestion:NSLocalizedString(@"MULTIDEVICE_PAIRING_MAX_RECOVERY", - @"alert body: cannot link - reached max linked devices") - fallbackError:networkError]; - customError.isRetryable = NO; - failureBlock(task, customError); - break; - } - case 413: { - OWSLogWarn(@"Rate limit exceeded: %@", request); - NSError *customError = [self errorWithHTTPCode:statusCode - description:NSLocalizedString(@"REGISTER_RATE_LIMITING_ERROR", nil) - failureReason:networkError.localizedFailureReason - recoverySuggestion:NSLocalizedString(@"REGISTER_RATE_LIMITING_BODY", nil) - fallbackError:networkError]; - customError.isRetryable = NO; - failureBlock(task, customError); - break; - } - case 417: { - // TODO: Is this response code obsolete? - OWSLogWarn(@"The number is already registered on a relay. Please unregister there first: %@", request); - NSError *customError = [self errorWithHTTPCode:statusCode - description:NSLocalizedString(@"REGISTRATION_ERROR", nil) - failureReason:networkError.localizedFailureReason - recoverySuggestion:NSLocalizedString(@"RELAY_REGISTERED_ERROR_RECOVERY", nil) - fallbackError:networkError]; - customError.isRetryable = NO; - failureBlock(task, customError); - break; - } - case 422: { - OWSLogError(@"The registration was requested over an unknown transport: %@, %@", - networkError.debugDescription, - request); - error.isRetryable = NO; - failureBlock(task, error); - break; - } - default: { - OWSLogWarn(@"Unknown error: %ld, %@, %@", (long)statusCode, networkError.debugDescription, request); - error.isRetryable = NO; - failureBlock(task, error); - break; - } - } -} - -+ (void)deregisterAfterAuthErrorIfNecessary:(NSURLSessionDataTask *)task - request:(TSRequest *)request - statusCode:(NSInteger)statusCode { - /* Loki: Original code - * We don't care about invalid auth - * ======== - - OWSLogVerbose(@"Invalid auth: %@", task.originalRequest.allHTTPHeaderFields); - - // We only want to de-register for: - // - // * Auth errors... - // * ...received from Signal service... - // * ...that used standard authorization. - // - // * We don't want want to deregister for: - // - // * CDS requests. - // * Requests using UD auth. - // * etc. - if ([task.originalRequest.URL.absoluteString hasPrefix:textSecureServerURL] - && request.shouldHaveAuthorizationHeaders) { - dispatch_async(dispatch_get_main_queue(), ^{ - if (self.tsAccountManager.isRegisteredAndReady) { - [self.tsAccountManager setIsDeregistered:YES]; - } else { - OWSLogWarn( - @"Ignoring auth failure; not registered and ready: %@.", task.originalRequest.URL.absoluteString); - } - }); - } else { - OWSLogWarn(@"Ignoring %d for URL: %@", (int)statusCode, task.originalRequest.URL.absoluteString); - } - - * ======== - */ -} - -+ (NSError *)errorWithHTTPCode:(NSInteger)code - description:(nullable NSString *)description - failureReason:(nullable NSString *)failureReason - recoverySuggestion:(nullable NSString *)recoverySuggestion - fallbackError:(NSError *)fallbackError -{ - OWSAssertDebug(fallbackError); - - if (!description) { - description = fallbackError.localizedDescription; - } - if (!failureReason) { - failureReason = fallbackError.localizedFailureReason; - } - if (!recoverySuggestion) { - recoverySuggestion = fallbackError.localizedRecoverySuggestion; - } - - NSMutableDictionary *dict = [NSMutableDictionary dictionary]; - - if (description) { - [dict setObject:description forKey:NSLocalizedDescriptionKey]; - } - if (failureReason) { - [dict setObject:failureReason forKey:NSLocalizedFailureReasonErrorKey]; - } - if (recoverySuggestion) { - [dict setObject:recoverySuggestion forKey:NSLocalizedRecoverySuggestionErrorKey]; - } - - NSData *failureData = fallbackError.userInfo[AFNetworkingOperationFailingURLResponseDataErrorKey]; - - if (failureData) { - [dict setObject:failureData forKey:AFNetworkingOperationFailingURLResponseDataErrorKey]; - } - - dict[NSUnderlyingErrorKey] = fallbackError; - - return [NSError errorWithDomain:TSNetworkManagerErrorDomain code:code userInfo:dict]; -} - -@end - -NS_ASSUME_NONNULL_END diff --git a/SignalUtilitiesKit/TSPreKeyManager.m b/SignalUtilitiesKit/TSPreKeyManager.m index acf3e5905..612265b73 100644 --- a/SignalUtilitiesKit/TSPreKeyManager.m +++ b/SignalUtilitiesKit/TSPreKeyManager.m @@ -8,7 +8,7 @@ #import "OWSIdentityManager.h" #import "OWSPrimaryStorage+SignedPreKeyStore.h" #import "SSKEnvironment.h" -#import "TSNetworkManager.h" + #import "TSStorageHeaders.h" #import #import diff --git a/SignalUtilitiesKit/TSSocketManager.h b/SignalUtilitiesKit/TSSocketManager.h deleted file mode 100644 index 29353c0c2..000000000 --- a/SignalUtilitiesKit/TSSocketManager.h +++ /dev/null @@ -1,50 +0,0 @@ -// -// Copyright (c) 2018 Open Whisper Systems. All rights reserved. -// - -#import - -NS_ASSUME_NONNULL_BEGIN - -@class TSRequest; - -@interface TSSocketManager : NSObject - -@property (class, readonly, nonatomic) TSSocketManager *shared; - -- (instancetype)init NS_DESIGNATED_INITIALIZER; - -// Returns the "best" state of any of the sockets. -// -// We surface the socket state in various places in the UI. -// We generally are trying to indicate/help resolve network -// connectivity issues. We want to show the "best" or "highest" -// socket state of the sockets. e.g. the UI should reflect -// "open" if any of the sockets is open. -- (OWSWebSocketState)highestSocketState; - -// If the app is in the foreground, we'll try to open the socket unless it's already -// open or connecting. -// -// If the app is in the background, we'll try to open the socket unless it's already -// open or connecting _and_ keep it open for at least N seconds. -// If the app is in the background and the socket is already open or connecting this -// might prolong how long we keep the socket open. -// -// This method can be called from any thread. -- (void)requestSocketOpen; - -// This can be used to force the socket to close and re-open, if it is open. -- (void)cycleSocket; - -#pragma mark - Message Sending - -- (BOOL)canMakeRequests; - -- (void)makeRequest:(TSRequest *)request - success:(TSSocketMessageSuccess)success - failure:(TSSocketMessageFailure)failure; - -@end - -NS_ASSUME_NONNULL_END diff --git a/SignalUtilitiesKit/TSSocketManager.m b/SignalUtilitiesKit/TSSocketManager.m deleted file mode 100644 index 4fee91411..000000000 --- a/SignalUtilitiesKit/TSSocketManager.m +++ /dev/null @@ -1,80 +0,0 @@ -// -// Copyright (c) 2018 Open Whisper Systems. All rights reserved. -// - -#import "TSSocketManager.h" -#import "SSKEnvironment.h" -#import "SSKAsserts.h" -#import - -NS_ASSUME_NONNULL_BEGIN - -@interface TSSocketManager () - -@property (nonatomic) OWSWebSocket *websocket; - -@end - -#pragma mark - - -@implementation TSSocketManager - -- (instancetype)init -{ - self = [super init]; - - if (!self) { - return self; - } - - OWSAssertIsOnMainThread(); - - _websocket = [[OWSWebSocket alloc] init]; - - OWSSingletonAssert(); - - return self; -} - -- (void)dealloc -{ - [[NSNotificationCenter defaultCenter] removeObserver:self]; -} - -+ (instancetype)shared -{ - OWSAssert(SSKEnvironment.shared.socketManager); - - return SSKEnvironment.shared.socketManager; -} - -- (BOOL)canMakeRequests -{ - return self.websocket.canMakeRequests; -} - -- (void)makeRequest:(TSRequest *)request - success:(TSSocketMessageSuccess)success - failure:(TSSocketMessageFailure)failure -{ - [self.websocket makeRequest:request success:success failure:failure]; -} - -- (void)requestSocketOpen -{ - [self.websocket requestSocketOpen]; -} - -- (void)cycleSocket -{ - [self.websocket cycleSocket]; -} - -- (OWSWebSocketState)highestSocketState -{ - return self.websocket.state; -} - -@end - -NS_ASSUME_NONNULL_END diff --git a/SignalUtilitiesKit/TTLUtilities.swift b/SignalUtilitiesKit/TTLUtilities.swift deleted file mode 100644 index ebe8219db..000000000 --- a/SignalUtilitiesKit/TTLUtilities.swift +++ /dev/null @@ -1,32 +0,0 @@ - -@objc(LKTTLUtilities) -public final class TTLUtilities : NSObject { - - /// If a message type specifies an invalid TTL, this will be used. - public static let fallbackMessageTTL: UInt64 = 2 * kDayInMs - - @objc(LKMessageType) - public enum MessageType : Int { - // Unimportant control messages - case call, typingIndicator - // Somewhat important control messages - case linkDevice - // Important control messages - case closedGroupUpdate, disappearingMessagesConfiguration, ephemeral, profileKey, receipt, sessionRequest, sync, unlinkDevice - // Visible messages - case regular - } - - @objc public static func getTTL(for messageType: MessageType) -> UInt64 { - switch messageType { - // Unimportant control messages - case .call, .typingIndicator: return 1 * kMinuteInMs - // Somewhat important control messages - case .linkDevice: return 1 * kHourInMs - // Important control messages - case .closedGroupUpdate, .disappearingMessagesConfiguration, .ephemeral, .profileKey, .receipt, .sessionRequest, .sync, .unlinkDevice: return 2 * kDayInMs - 1 * kHourInMs - // Visible messages - case .regular: return 2 * kDayInMs - } - } -} diff --git a/SignalUtilitiesKit/ThreadUtil.h b/SignalUtilitiesKit/ThreadUtil.h new file mode 100644 index 000000000..28ca1f27e --- /dev/null +++ b/SignalUtilitiesKit/ThreadUtil.h @@ -0,0 +1,83 @@ +// +// Copyright (c) 2019 Open Whisper Systems. All rights reserved. +// + +NS_ASSUME_NONNULL_BEGIN + +@class OWSBlockingManager; +@class OWSLinkPreviewDraft; +@class OWSQuotedReplyModel; +@class OWSUnreadIndicator; +@class SignalAttachment; +@class TSContactThread; +@class TSGroupThread; +@class TSInteraction; +@class TSOutgoingMessage; +@class TSThread; +@class YapDatabaseConnection; +@class YapDatabaseReadTransaction; +@class YapDatabaseReadWriteTransaction; + +@interface ThreadDynamicInteractions : NSObject + +// Represents the "reverse index" of the focus message, if any. +// The "reverse index" is the distance of this interaction from +// the last interaction in the thread. Therefore the last interaction +// will have a "reverse index" of zero. +// +// We use "reverse indices" because (among other uses) we use this to +// determine the initial load window size. +@property (nonatomic, nullable, readonly) NSNumber *focusMessagePosition; + +@property (nonatomic, nullable, readonly) OWSUnreadIndicator *unreadIndicator; + +- (void)clearUnreadIndicatorState; + +@end + +#pragma mark - + +@interface ThreadUtil : NSObject + +#pragma mark - dynamic interactions + +// This method will create and/or remove any offers and indicators +// necessary for this thread. This includes: +// +// * Block offers. +// * "Add to contacts" offers. +// * Unread indicators. +// +// Parameters: +// +// * hideUnreadMessagesIndicator: If YES, the "unread indicator" has +// been cleared and should not be shown. +// * firstUnseenInteractionTimestamp: A snapshot of unseen message state +// when we entered the conversation view. See comments on +// ThreadOffersAndIndicators. +// * maxRangeSize: Loading a lot of messages in conversation view is +// slow and unwieldy. This number represents the maximum current +// size of the "load window" in that view. The unread indicator should +// always be inserted within that window. ++ (ThreadDynamicInteractions *)ensureDynamicInteractionsForThread:(TSThread *)thread + blockingManager:(OWSBlockingManager *)blockingManager + dbConnection:(YapDatabaseConnection *)dbConnection + hideUnreadMessagesIndicator:(BOOL)hideUnreadMessagesIndicator + lastUnreadIndicator:(nullable OWSUnreadIndicator *)lastUnreadIndicator + focusMessageId:(nullable NSString *)focusMessageId + maxRangeSize:(int)maxRangeSize; + +#pragma mark - Delete Content + ++ (void)deleteAllContent; + +#pragma mark - Find Content + ++ (nullable TSInteraction *)findInteractionInThreadByTimestamp:(uint64_t)timestamp + authorId:(NSString *)authorId + threadUniqueId:(NSString *)threadUniqueId + transaction:(YapDatabaseReadTransaction *)transaction; + +@end + +NS_ASSUME_NONNULL_END diff --git a/SignalUtilitiesKit/ThreadUtil.m b/SignalUtilitiesKit/ThreadUtil.m new file mode 100644 index 000000000..b69fded03 --- /dev/null +++ b/SignalUtilitiesKit/ThreadUtil.m @@ -0,0 +1,409 @@ +// +// Copyright (c) 2019 Open Whisper Systems. All rights reserved. +// + +#import "ThreadUtil.h" +#import "OWSQuotedReplyModel.h" +#import "OWSUnreadIndicator.h" +#import "TSUnreadIndicatorInteraction.h" +#import +#import +#import +#import +#import +#import +#import +#import +#import +#import +#import +#import +#import + + +NS_ASSUME_NONNULL_BEGIN + +@interface ThreadDynamicInteractions () + +@property (nonatomic, nullable) NSNumber *focusMessagePosition; + +@property (nonatomic, nullable) OWSUnreadIndicator *unreadIndicator; + +@end + +#pragma mark - + +@implementation ThreadDynamicInteractions + +- (void)clearUnreadIndicatorState +{ + self.unreadIndicator = nil; +} + +- (BOOL)isEqual:(id)object +{ + if (self == object) { + return YES; + } + + if (![object isKindOfClass:[ThreadDynamicInteractions class]]) { + return NO; + } + + ThreadDynamicInteractions *other = (ThreadDynamicInteractions *)object; + return ([NSObject isNullableObject:self.focusMessagePosition equalTo:other.focusMessagePosition] && + [NSObject isNullableObject:self.unreadIndicator equalTo:other.unreadIndicator]); +} + +@end + +@implementation ThreadUtil + +#pragma mark - Dependencies + ++ (YapDatabaseConnection *)dbConnection +{ + return SSKEnvironment.shared.primaryStorage.dbReadWriteConnection; +} + +#pragma mark - Dynamic Interactions + ++ (ThreadDynamicInteractions *)ensureDynamicInteractionsForThread:(TSThread *)thread + blockingManager:(OWSBlockingManager *)blockingManager + dbConnection:(YapDatabaseConnection *)dbConnection + hideUnreadMessagesIndicator:(BOOL)hideUnreadMessagesIndicator + lastUnreadIndicator:(nullable OWSUnreadIndicator *)lastUnreadIndicator + focusMessageId:(nullable NSString *)focusMessageId + maxRangeSize:(int)maxRangeSize +{ + OWSAssertDebug(thread); + OWSAssertDebug(dbConnection); + OWSAssertDebug(blockingManager); + OWSAssertDebug(maxRangeSize > 0); + + ThreadDynamicInteractions *result = [ThreadDynamicInteractions new]; + + [dbConnection readWithBlock:^(YapDatabaseReadTransaction *transaction) { + // Find any "dynamic" interactions and safety number changes. + // + // We use different views for performance reasons. + NSMutableArray *blockingSafetyNumberChanges = [NSMutableArray new]; + NSMutableArray *nonBlockingSafetyNumberChanges = [NSMutableArray new]; + [[TSDatabaseView threadSpecialMessagesDatabaseView:transaction] + enumerateKeysAndObjectsInGroup:thread.uniqueId + usingBlock:^( + NSString *collection, NSString *key, id object, NSUInteger index, BOOL *stop) { + if ([object isKindOfClass:[TSInvalidIdentityKeyErrorMessage class]]) { + [blockingSafetyNumberChanges addObject:object]; + } else if ([object isKindOfClass:[TSErrorMessage class]]) { + TSErrorMessage *errorMessage = (TSErrorMessage *)object; + OWSAssertDebug( + errorMessage.errorType == TSErrorMessageNonBlockingIdentityChange); + [nonBlockingSafetyNumberChanges addObject:errorMessage]; + } else { + OWSFailDebug(@"Unexpected dynamic interaction type: %@", [object class]); + } + }]; + + // Determine if there are "unread" messages in this conversation. + // If we've been passed a firstUnseenInteractionTimestampParameter, + // just use that value in order to preserve continuity of the + // unread messages indicator after all messages in the conversation + // have been marked as read. + // + // IFF this variable is non-null, there are unseen messages in the thread. + NSNumber *_Nullable firstUnseenSortId = nil; + if (lastUnreadIndicator) { + firstUnseenSortId = @(lastUnreadIndicator.firstUnseenSortId); + } else { + TSInteraction *_Nullable firstUnseenInteraction = + [[TSDatabaseView unseenDatabaseViewExtension:transaction] firstObjectInGroup:thread.uniqueId]; + if (firstUnseenInteraction && firstUnseenInteraction.sortId != NULL) { + firstUnseenSortId = @(firstUnseenInteraction.sortId); + } + } + + [self ensureUnreadIndicator:result + thread:thread + transaction:transaction + maxRangeSize:maxRangeSize + blockingSafetyNumberChanges:blockingSafetyNumberChanges + nonBlockingSafetyNumberChanges:nonBlockingSafetyNumberChanges + hideUnreadMessagesIndicator:hideUnreadMessagesIndicator + firstUnseenSortId:firstUnseenSortId]; + + // Determine the position of the focus message _after_ performing any mutations + // around dynamic interactions. + if (focusMessageId != nil) { + result.focusMessagePosition = + [self focusMessagePositionForThread:thread transaction:transaction focusMessageId:focusMessageId]; + } + }]; + + return result; +} + ++ (void)ensureUnreadIndicator:(ThreadDynamicInteractions *)dynamicInteractions + thread:(TSThread *)thread + transaction:(YapDatabaseReadTransaction *)transaction + maxRangeSize:(int)maxRangeSize + blockingSafetyNumberChanges:(NSArray *)blockingSafetyNumberChanges + nonBlockingSafetyNumberChanges:(NSArray *)nonBlockingSafetyNumberChanges + hideUnreadMessagesIndicator:(BOOL)hideUnreadMessagesIndicator + firstUnseenSortId:(nullable NSNumber *)firstUnseenSortId +{ + OWSAssertDebug(dynamicInteractions); + OWSAssertDebug(thread); + OWSAssertDebug(transaction); + OWSAssertDebug(blockingSafetyNumberChanges); + OWSAssertDebug(nonBlockingSafetyNumberChanges); + + if (hideUnreadMessagesIndicator) { + return; + } + if (!firstUnseenSortId) { + // If there are no unseen interactions, don't show an unread indicator. + return; + } + + YapDatabaseViewTransaction *threadMessagesTransaction = [transaction ext:TSMessageDatabaseViewExtensionName]; + OWSAssertDebug([threadMessagesTransaction isKindOfClass:[YapDatabaseViewTransaction class]]); + + // Determine unread indicator position, if necessary. + // + // Enumerate in reverse to count the number of messages + // after the unseen messages indicator. Not all of + // them are unnecessarily unread, but we need to tell + // the messages view the position of the unread indicator, + // so that it can widen its "load window" to always show + // the unread indicator. + __block long visibleUnseenMessageCount = 0; + __block TSInteraction *interactionAfterUnreadIndicator = nil; + __block BOOL hasMoreUnseenMessages = NO; + [threadMessagesTransaction + enumerateKeysAndObjectsInGroup:thread.uniqueId + withOptions:NSEnumerationReverse + usingBlock:^(NSString *collection, NSString *key, id object, NSUInteger index, BOOL *stop) { + if (![object isKindOfClass:[TSInteraction class]]) { + OWSFailDebug(@"Expected a TSInteraction: %@", [object class]); + return; + } + + TSInteraction *interaction = (TSInteraction *)object; + + if (interaction.isDynamicInteraction) { + // Ignore dynamic interactions, if any. + return; + } + + if (interaction.sortId < firstUnseenSortId.unsignedLongLongValue) { + // By default we want the unread indicator to appear just before + // the first unread message. + *stop = YES; + return; + } + + visibleUnseenMessageCount++; + + interactionAfterUnreadIndicator = interaction; + + if (visibleUnseenMessageCount + 1 >= maxRangeSize) { + // If there are more unseen messages than can be displayed in the + // messages view, show the unread indicator at the top of the + // displayed messages. + *stop = YES; + hasMoreUnseenMessages = YES; + } + }]; + + if (!interactionAfterUnreadIndicator) { + // If we can't find an interaction after the unread indicator, + // don't show it. All unread messages may have been deleted or + // expired. + return; + } + OWSAssertDebug(visibleUnseenMessageCount > 0); + + NSUInteger missingUnseenSafetyNumberChangeCount = 0; + if (hasMoreUnseenMessages) { + NSMutableSet *missingUnseenSafetyNumberChanges = [NSMutableSet set]; + for (TSInvalidIdentityKeyErrorMessage *safetyNumberChange in blockingSafetyNumberChanges) { + BOOL isUnseen = safetyNumberChange.sortId >= firstUnseenSortId.unsignedLongLongValue; + if (!isUnseen) { + continue; + } + + BOOL isMissing = safetyNumberChange.sortId < interactionAfterUnreadIndicator.sortId; + if (!isMissing) { + continue; + } + + @try { + NSData *_Nullable newIdentityKey = [safetyNumberChange throws_newIdentityKey]; + if (newIdentityKey == nil) { + OWSFailDebug(@"Safety number change was missing it's new identity key."); + continue; + } + + [missingUnseenSafetyNumberChanges addObject:newIdentityKey]; + } @catch (NSException *exception) { + OWSFailDebug(@"exception: %@", exception); + } + } + + // Count the de-duplicated "blocking" safety number changes and all + // of the "non-blocking" safety number changes. + missingUnseenSafetyNumberChangeCount + = (missingUnseenSafetyNumberChanges.count + nonBlockingSafetyNumberChanges.count); + } + + NSInteger unreadIndicatorPosition = visibleUnseenMessageCount; + + dynamicInteractions.unreadIndicator = + [[OWSUnreadIndicator alloc] initWithFirstUnseenSortId:firstUnseenSortId.unsignedLongLongValue + hasMoreUnseenMessages:hasMoreUnseenMessages + missingUnseenSafetyNumberChangeCount:missingUnseenSafetyNumberChangeCount + unreadIndicatorPosition:unreadIndicatorPosition]; + OWSLogInfo(@"Creating Unread Indicator: %llu", dynamicInteractions.unreadIndicator.firstUnseenSortId); +} + ++ (nullable NSNumber *)focusMessagePositionForThread:(TSThread *)thread + transaction:(YapDatabaseReadTransaction *)transaction + focusMessageId:(NSString *)focusMessageId +{ + OWSAssertDebug(thread); + OWSAssertDebug(transaction); + OWSAssertDebug(focusMessageId); + + YapDatabaseViewTransaction *databaseView = [transaction ext:TSMessageDatabaseViewExtensionName]; + + NSString *_Nullable group = nil; + NSUInteger index; + BOOL success = + [databaseView getGroup:&group index:&index forKey:focusMessageId inCollection:TSInteraction.collection]; + if (!success) { + // This might happen if the focus message has disappeared + // before this view could appear. + OWSFailDebug(@"failed to find focus message index."); + return nil; + } + if (![group isEqualToString:thread.uniqueId]) { + OWSFailDebug(@"focus message has invalid group."); + return nil; + } + NSUInteger count = [databaseView numberOfItemsInGroup:thread.uniqueId]; + if (index >= count) { + OWSFailDebug(@"focus message has invalid index."); + return nil; + } + NSUInteger position = (count - index) - 1; + return @(position); +} + +#pragma mark - Delete Content + ++ (void)deleteAllContent +{ + OWSLogInfo(@""); + + [LKStorage writeSyncWithBlock:^(YapDatabaseReadWriteTransaction *transaction) { + [self removeAllObjectsInCollection:[TSThread collection] + class:[TSThread class] + transaction:transaction]; + [self removeAllObjectsInCollection:[TSInteraction collection] + class:[TSInteraction class] + transaction:transaction]; + [self removeAllObjectsInCollection:[TSAttachment collection] + class:[TSAttachment class] + transaction:transaction]; + @try { + [self removeAllObjectsInCollection:[SignalRecipient collection] + class:[SignalRecipient class] + transaction:transaction]; + } @catch (NSException *exception) { + // Do nothing + } + }]; + [TSAttachmentStream deleteAttachments]; +} + ++ (void)removeAllObjectsInCollection:(NSString *)collection + class:(Class) class + transaction:(YapDatabaseReadWriteTransaction *)transaction { + OWSAssertDebug(collection.length > 0); + OWSAssertDebug(class); + OWSAssertDebug(transaction); + + NSArray *_Nullable uniqueIds = [transaction allKeysInCollection:collection]; + if (!uniqueIds) { + OWSFailDebug(@"couldn't load uniqueIds for collection: %@.", collection); + return; + } + OWSLogInfo(@"Deleting %lu objects from: %@", (unsigned long)uniqueIds.count, collection); + NSUInteger count = 0; + for (NSString *uniqueId in uniqueIds) { + // We need to fetch each object, since [TSYapDatabaseObject removeWithTransaction:] sometimes does important + // work. + TSYapDatabaseObject *_Nullable object = [class fetchObjectWithUniqueID:uniqueId transaction:transaction]; + if (!object) { + OWSFailDebug(@"couldn't load object for deletion: %@.", collection); + continue; + } + [object removeWithTransaction:transaction]; + count++; + }; + OWSLogInfo(@"Deleted %lu/%lu objects from: %@", (unsigned long)count, (unsigned long)uniqueIds.count, collection); +} + +#pragma mark - Find Content + ++ (nullable TSInteraction *)findInteractionInThreadByTimestamp:(uint64_t)timestamp + authorId:(NSString *)authorId + threadUniqueId:(NSString *)threadUniqueId + transaction:(YapDatabaseReadTransaction *)transaction +{ + OWSAssertDebug(timestamp > 0); + OWSAssertDebug(authorId.length > 0); + + NSString *localNumber = [TSAccountManager localNumber]; + if (localNumber.length < 1) { + OWSFailDebug(@"missing long number."); + return nil; + } + + NSArray *interactions = + [TSInteraction interactionsWithTimestamp:timestamp + filter:^(TSInteraction *interaction) { + NSString *_Nullable messageAuthorId = nil; + if ([interaction isKindOfClass:[TSIncomingMessage class]]) { + TSIncomingMessage *incomingMessage = (TSIncomingMessage *)interaction; + messageAuthorId = incomingMessage.authorId; + } else if ([interaction isKindOfClass:[TSOutgoingMessage class]]) { + messageAuthorId = localNumber; + } + if (messageAuthorId.length < 1) { + return NO; + } + + if (![authorId isEqualToString:messageAuthorId]) { + return NO; + } + if (![interaction.uniqueThreadId isEqualToString:threadUniqueId]) { + return NO; + } + return YES; + } + withTransaction:transaction]; + if (interactions.count < 1) { + return nil; + } + if (interactions.count > 1) { + // In case of collision, take the first. + OWSLogError(@"more than one matching interaction in thread."); + } + return interactions.firstObject; +} + +@end + +NS_ASSUME_NONNULL_END diff --git a/SignalUtilitiesKit/ThreadViewHelper.m b/SignalUtilitiesKit/ThreadViewHelper.m index 9b570dd23..54dede175 100644 --- a/SignalUtilitiesKit/ThreadViewHelper.m +++ b/SignalUtilitiesKit/ThreadViewHelper.m @@ -204,9 +204,7 @@ NS_ASSUME_NONNULL_BEGIN if ([thread isKindOfClass:TSContactThread.class]) { NSString *publicKey = thread.contactIdentifier; if ([LKUserDisplayNameUtilities getPrivateChatDisplayNameFor:publicKey] == nil) { continue; } - if ([LKDatabaseUtilities getMasterHexEncodedPublicKeyFor:publicKey in:transaction] == nil) { - [threads addObject:thread]; - } + [threads addObject:thread]; } else { [threads addObject:thread]; } diff --git a/SignalUtilitiesKit/TSContactThread.h b/SignalUtilitiesKit/Threads/TSContactThread.h similarity index 59% rename from SignalUtilitiesKit/TSContactThread.h rename to SignalUtilitiesKit/Threads/TSContactThread.h index 72a5262fe..15c63d31f 100644 --- a/SignalUtilitiesKit/TSContactThread.h +++ b/SignalUtilitiesKit/Threads/TSContactThread.h @@ -12,9 +12,7 @@ typedef NS_ENUM(NSInteger, SNSessionRestorationStatus); @interface TSContactThread : TSThread -@property (atomic) SNSessionRestorationStatus sessionResetStatus; -@property (atomic, readonly) NSArray *sessionRestoreDevices; - +@property (atomic) SNSessionRestorationStatus sessionRestorationStatus; @property (nonatomic) BOOL hasDismissedOffers; - (instancetype)initWithContactId:(NSString *)contactId; @@ -33,17 +31,6 @@ typedef NS_ENUM(NSInteger, SNSessionRestorationStatus); + (NSString *)threadIdFromContactId:(NSString *)contactId; -// This method can be used to get the conversation color for a given -// recipient without using a read/write transaction to create a -// contact thread. -+ (NSString *)conversationColorNameForRecipientId:(NSString *)recipientId - transaction:(YapDatabaseReadTransaction *)transaction; - -#pragma mark - Loki Session Restore - -- (void)addSessionRestoreDevice:(NSString *)hexEncodedPublicKey transaction:(YapDatabaseReadWriteTransaction *_Nullable)transaction; -- (void)removeAllSessionRestoreDevicesWithTransaction:(YapDatabaseReadWriteTransaction *_Nullable)transaction; - @end NS_ASSUME_NONNULL_END diff --git a/SignalUtilitiesKit/TSContactThread.m b/SignalUtilitiesKit/Threads/TSContactThread.m similarity index 53% rename from SignalUtilitiesKit/TSContactThread.m rename to SignalUtilitiesKit/Threads/TSContactThread.m index 6b0d5e219..e7896f555 100644 --- a/SignalUtilitiesKit/TSContactThread.m +++ b/SignalUtilitiesKit/Threads/TSContactThread.m @@ -4,7 +4,6 @@ #import "TSContactThread.h" #import "ContactsManagerProtocol.h" -#import "ContactsUpdater.h" #import "NotificationsProtocol.h" #import "OWSIdentityManager.h" #import "SSKEnvironment.h" @@ -24,11 +23,9 @@ NSString *const TSContactThreadPrefix = @"c"; OWSAssertDebug(contactId.length > 0); self = [super initWithUniqueId:uniqueIdentifier]; - - // No session reset ongoing - _sessionResetStatus = SNSessionRestorationStatusNone; - _sessionRestoreDevices = @[]; + _sessionRestorationStatus = SNSessionRestorationStatusNone; + return self; } @@ -82,12 +79,6 @@ NSString *const TSContactThreadPrefix = @"c"; return !![[OWSIdentityManager sharedManager] identityKeyForRecipientId:self.contactIdentifier]; } -// TODO deprecate this? seems weird to access the displayName in the DB model -- (NSString *)name -{ - return [SSKEnvironment.shared.contactsManager displayNameForPhoneIdentifier:self.contactIdentifier]; -} - + (NSString *)threadIdFromContactId:(NSString *)contactId { return [TSContactThreadPrefix stringByAppendingString:contactId]; } @@ -96,47 +87,6 @@ NSString *const TSContactThreadPrefix = @"c"; return [threadId substringWithRange:NSMakeRange(1, threadId.length - 1)]; } -+ (NSString *)conversationColorNameForRecipientId:(NSString *)recipientId - transaction:(YapDatabaseReadTransaction *)transaction -{ - OWSAssertDebug(recipientId.length > 0); - - TSContactThread *_Nullable contactThread = - [TSContactThread getThreadWithContactId:recipientId transaction:transaction]; - if (contactThread) { - return contactThread.conversationColorName; - } - return [self stableColorNameForNewConversationWithString:recipientId]; -} - -#pragma mark - Loki Session Restore - -- (void)addSessionRestoreDevice:(NSString *)hexEncodedPublicKey transaction:(YapDatabaseReadWriteTransaction *_Nullable)transaction -{ - NSMutableSet *set = [[NSMutableSet alloc] initWithArray:_sessionRestoreDevices]; - [set addObject:hexEncodedPublicKey]; - [self setSessionRestoreDevices:set.allObjects transaction:transaction]; -} - -- (void)removeAllSessionRestoreDevicesWithTransaction:(YapDatabaseReadWriteTransaction *_Nullable)transaction -{ - [self setSessionRestoreDevices:@[] transaction:transaction]; -} - -- (void)setSessionRestoreDevices:(NSArray *)sessionRestoreDevices transaction:(YapDatabaseReadWriteTransaction *_Nullable)transaction { - _sessionRestoreDevices = sessionRestoreDevices; - void (^postNotification)() = ^() { - [NSNotificationCenter.defaultCenter postNotificationName:NSNotification.threadSessionRestoreDevicesChanged object:self.uniqueId]; - }; - if (transaction == nil) { - [self save]; - [self.dbReadWriteConnection flushTransactionsWithCompletionQueue:dispatch_get_main_queue() completionBlock:^{ postNotification(); }]; - } else { - [self saveWithTransaction:transaction]; - [transaction.connection flushTransactionsWithCompletionQueue:dispatch_get_main_queue() completionBlock:^{ postNotification(); }]; - } -} - @end NS_ASSUME_NONNULL_END diff --git a/SignalUtilitiesKit/TSGroupModel.h b/SignalUtilitiesKit/Threads/TSGroupModel.h similarity index 84% rename from SignalUtilitiesKit/TSGroupModel.h rename to SignalUtilitiesKit/Threads/TSGroupModel.h index e80d99425..bf567fb81 100644 --- a/SignalUtilitiesKit/TSGroupModel.h +++ b/SignalUtilitiesKit/Threads/TSGroupModel.h @@ -10,9 +10,8 @@ NS_ASSUME_NONNULL_BEGIN typedef NS_ENUM(NSInteger, GroupType) { - closedGroup = 0, // a.k.a. private group chat - openGroup = 1, // a.k.a. public group chat - rssFeed = 2 + closedGroup = 0, + openGroup = 1, }; extern const int32_t kGroupIdLength; @@ -38,7 +37,7 @@ extern const int32_t kGroupIdLength; - (BOOL)isEqual:(id)other; - (BOOL)isEqualToGroupModel:(TSGroupModel *)model; -- (NSString *)getInfoStringAboutUpdateTo:(TSGroupModel *)model contactsManager:(id)contactsManager; +- (NSString *)getInfoStringAboutUpdateTo:(TSGroupModel *)model; - (void)updateGroupId: (NSData *)newGroupId; #endif diff --git a/SignalUtilitiesKit/TSGroupModel.m b/SignalUtilitiesKit/Threads/TSGroupModel.m similarity index 99% rename from SignalUtilitiesKit/TSGroupModel.m rename to SignalUtilitiesKit/Threads/TSGroupModel.m index 5b0564b1b..fac56262a 100644 --- a/SignalUtilitiesKit/TSGroupModel.m +++ b/SignalUtilitiesKit/Threads/TSGroupModel.m @@ -100,7 +100,7 @@ const int32_t kGroupIdLength = 16; return YES; } -- (NSString *)getInfoStringAboutUpdateTo:(TSGroupModel *)newModel contactsManager:(id)contactsManager { +- (NSString *)getInfoStringAboutUpdateTo:(TSGroupModel *)newModel { NSString *updatedGroupInfoString = @""; if (self == newModel) { return NSLocalizedString(@"GROUP_UPDATED", @""); diff --git a/SignalUtilitiesKit/TSGroupThread.h b/SignalUtilitiesKit/Threads/TSGroupThread.h similarity index 81% rename from SignalUtilitiesKit/TSGroupThread.h rename to SignalUtilitiesKit/Threads/TSGroupThread.h index 54bfea575..141247a2b 100644 --- a/SignalUtilitiesKit/TSGroupThread.h +++ b/SignalUtilitiesKit/Threads/TSGroupThread.h @@ -17,8 +17,7 @@ extern NSString *const TSGroupThread_NotificationKey_UniqueId; @interface TSGroupThread : TSThread @property (nonatomic, strong) TSGroupModel *groupModel; -@property (nonatomic, readonly) BOOL isRSSFeed; -@property (nonatomic, readonly) BOOL isPublicChat; +@property (nonatomic, readonly) BOOL isOpenGroup; @property (nonatomic) BOOL usesSharedSenderKeys; + (instancetype)getOrCreateThreadWithGroupModel:(TSGroupModel *)groupModel; @@ -37,10 +36,9 @@ extern NSString *const TSGroupThread_NotificationKey_UniqueId; + (NSString *)defaultGroupName; -- (BOOL)isLocalUserInGroup; -- (BOOL)isCurrentUserInGroupWithTransaction:(YapDatabaseReadTransaction *)transaction; -- (BOOL)isUserMemberInGroup:(NSString *)hexEncodedPublicKey transaction:(YapDatabaseReadTransaction *)transaction; -- (BOOL)isUserAdminInGroup:(NSString *)hexEncodedPublicKey transaction:(YapDatabaseReadTransaction *)transaction; +- (BOOL)isCurrentUserInGroup; +- (BOOL)isUserMemberInGroup:(NSString *)publicKey; +- (BOOL)isUserAdminInGroup:(NSString *)publicKey; // all group threads containing recipient as a member + (NSArray *)groupThreadsWithRecipientId:(NSString *)recipientId @@ -60,8 +58,6 @@ extern NSString *const TSGroupThread_NotificationKey_UniqueId; - (void)fireAvatarChangedNotification; -+ (ConversationColorName)defaultConversationColorNameForGroupId:(NSData *)groupId; - @end NS_ASSUME_NONNULL_END diff --git a/SignalUtilitiesKit/TSGroupThread.m b/SignalUtilitiesKit/Threads/TSGroupThread.m similarity index 78% rename from SignalUtilitiesKit/TSGroupThread.m rename to SignalUtilitiesKit/Threads/TSGroupThread.m index 938e198bf..e100cb933 100644 --- a/SignalUtilitiesKit/TSGroupThread.m +++ b/SignalUtilitiesKit/Threads/TSGroupThread.m @@ -189,50 +189,27 @@ NSString *const TSGroupThread_NotificationKey_UniqueId = @"TSGroupThread_Notific return true; } -- (BOOL)isPublicChat +- (BOOL)isOpenGroup { return (self.groupModel.groupType == openGroup); } -- (BOOL)isRSSFeed +- (BOOL)isCurrentUserMemberInGroup { - return (self.groupModel.groupType == rssFeed); + NSString *userPublicKey = OWSIdentityManager.sharedManager.identityKeyPair.hexEncodedPublicKey; + return [self isUserMemberInGroup:userPublicKey]; } -- (BOOL)isContactFriend +- (BOOL)isUserMemberInGroup:(NSString *)publicKey { - return false; + if (publicKey == nil) { return NO; } + return [self.groupModel.groupMemberIds containsObject:publicKey]; } -- (BOOL)isLocalUserInGroup +- (BOOL)isUserAdminInGroup:(NSString *)publicKey { - __block BOOL result = NO; - - [OWSPrimaryStorage.sharedManager.dbReadConnection readWithBlock:^(YapDatabaseReadTransaction *transaction) { - result = [self isCurrentUserInGroupWithTransaction:transaction]; - }]; - - return result; -} - -- (BOOL)isCurrentUserInGroupWithTransaction:(YapDatabaseReadTransaction *)transaction -{ - NSString *userHexEncodedPublicKey = TSAccountManager.localNumber; - return [self isUserMemberInGroup:userHexEncodedPublicKey transaction:transaction]; -} - -- (BOOL)isUserMemberInGroup:(NSString *)hexEncodedPublicKey transaction:(YapDatabaseReadTransaction *)transaction -{ - if (hexEncodedPublicKey == nil) { return NO; } - NSSet *linkedDeviceHexEncodedPublicKeys = [LKDatabaseUtilities getLinkedDeviceHexEncodedPublicKeysFor:hexEncodedPublicKey in:transaction]; - return [linkedDeviceHexEncodedPublicKeys intersectsSet:[NSSet setWithArray:self.groupModel.groupMemberIds]]; -} - -- (BOOL)isUserAdminInGroup:(NSString *)hexEncodedPublicKey transaction:(YapDatabaseReadTransaction *)transaction -{ - if (hexEncodedPublicKey == nil) { return NO; } - NSSet *linkedDeviceHexEncodedPublicKeys = [LKDatabaseUtilities getLinkedDeviceHexEncodedPublicKeysFor:hexEncodedPublicKey in:transaction]; - return [linkedDeviceHexEncodedPublicKeys intersectsSet:[NSSet setWithArray:self.groupModel.groupAdminIds]]; + if (publicKey == nil) { return NO; } + return [self.groupModel.groupAdminIds containsObject:publicKey]; } - (NSString *)name @@ -269,16 +246,16 @@ NSString *const TSGroupThread_NotificationKey_UniqueId = @"TSGroupThread_Notific - (void)leaveGroupWithTransaction:(YapDatabaseReadWriteTransaction *)transaction { - NSMutableSet *newGroupMemberIDs = [NSMutableSet setWithArray:self.groupModel.groupMemberIds]; - NSString *userPublicKey = TSAccountManager.localNumber; - if (userPublicKey == nil) { return; } - NSSet *userLinkedDevices = [LKDatabaseUtilities getLinkedDeviceHexEncodedPublicKeysFor:userPublicKey in:transaction]; - [newGroupMemberIDs minusSet:userLinkedDevices]; - self.groupModel.groupMemberIds = newGroupMemberIDs.allObjects; - [self saveWithTransaction:transaction]; - [transaction addCompletionQueue:dispatch_get_main_queue() completionBlock:^{ - [NSNotificationCenter.defaultCenter postNotificationName:NSNotification.groupThreadUpdated object:self.uniqueId]; - }]; +// NSMutableSet *newGroupMemberIDs = [NSMutableSet setWithArray:self.groupModel.groupMemberIds]; +// NSString *userPublicKey = TSAccountManager.localNumber; +// if (userPublicKey == nil) { return; } +// NSSet *userLinkedDevices = [LKDatabaseUtilities getLinkedDeviceHexEncodedPublicKeysFor:userPublicKey in:transaction]; +// [newGroupMemberIDs minusSet:userLinkedDevices]; +// self.groupModel.groupMemberIds = newGroupMemberIDs.allObjects; +// [self saveWithTransaction:transaction]; +// [transaction addCompletionQueue:dispatch_get_main_queue() completionBlock:^{ +// [NSNotificationCenter.defaultCenter postNotificationName:NSNotification.groupThreadUpdated object:self.uniqueId]; +// }]; } - (void)softDeleteGroupThreadWithTransaction:(YapDatabaseReadWriteTransaction *)transaction @@ -327,13 +304,6 @@ NSString *const TSGroupThread_NotificationKey_UniqueId = @"TSGroupThread_Notific userInfo:userInfo]; } -+ (ConversationColorName)defaultConversationColorNameForGroupId:(NSData *)groupId -{ - OWSAssertDebug(groupId.length > 0); - - return [self.class stableColorNameForNewConversationWithString:[self threadIdFromGroupId:groupId]]; -} - @end NS_ASSUME_NONNULL_END diff --git a/SignalUtilitiesKit/TSThread.h b/SignalUtilitiesKit/Threads/TSThread.h similarity index 78% rename from SignalUtilitiesKit/TSThread.h rename to SignalUtilitiesKit/Threads/TSThread.h index bcf077447..08e8f8ecb 100644 --- a/SignalUtilitiesKit/TSThread.h +++ b/SignalUtilitiesKit/Threads/TSThread.h @@ -12,23 +12,6 @@ BOOL IsNoteToSelfEnabled(void); @class TSInteraction; @class TSInvalidIdentityKeyReceivingErrorMessage; -typedef NSString *ConversationColorName NS_STRING_ENUM; - -extern ConversationColorName const ConversationColorNameCrimson; -extern ConversationColorName const ConversationColorNameVermilion; -extern ConversationColorName const ConversationColorNameBurlap; -extern ConversationColorName const ConversationColorNameForest; -extern ConversationColorName const ConversationColorNameWintergreen; -extern ConversationColorName const ConversationColorNameTeal; -extern ConversationColorName const ConversationColorNameBlue; -extern ConversationColorName const ConversationColorNameIndigo; -extern ConversationColorName const ConversationColorNameViolet; -extern ConversationColorName const ConversationColorNamePlum; -extern ConversationColorName const ConversationColorNameTaupe; -extern ConversationColorName const ConversationColorNameSteel; - -extern ConversationColorName const kConversationColorName_Default; - /** * TSThread is the superclass of TSContactThread and TSGroupThread */ @@ -53,13 +36,6 @@ extern ConversationColorName const kConversationColorName_Default; */ - (NSString *)name; -@property (nonatomic, readonly) ConversationColorName conversationColorName; - -- (void)updateConversationColorName:(ConversationColorName)colorName - transaction:(YapDatabaseReadWriteTransaction *)transaction; -+ (ConversationColorName)stableColorNameForNewConversationWithString:(NSString *)colorSeed; -@property (class, nonatomic, readonly) NSArray *conversationColorNames; - /** * @returns * Signal Id (e164) of the contact if it's a contact thread. @@ -95,8 +71,6 @@ extern ConversationColorName const kConversationColorName_Default; - (NSUInteger)unreadMessageCountWithTransaction:(YapDatabaseReadTransaction *)transaction NS_SWIFT_NAME(unreadMessageCount(transaction:)); -- (BOOL)hasSafetyNumbers; - - (void)markAllAsReadWithTransaction:(YapDatabaseReadWriteTransaction *)transaction; /** diff --git a/SignalUtilitiesKit/TSThread.m b/SignalUtilitiesKit/Threads/TSThread.m similarity index 68% rename from SignalUtilitiesKit/TSThread.m rename to SignalUtilitiesKit/Threads/TSThread.m index 773c00a1c..6bd591aa7 100644 --- a/SignalUtilitiesKit/TSThread.m +++ b/SignalUtilitiesKit/Threads/TSThread.m @@ -27,25 +27,9 @@ BOOL IsNoteToSelfEnabled(void) return YES; } -ConversationColorName const ConversationColorNameCrimson = @"red"; -ConversationColorName const ConversationColorNameVermilion = @"orange"; -ConversationColorName const ConversationColorNameBurlap = @"brown"; -ConversationColorName const ConversationColorNameForest = @"green"; -ConversationColorName const ConversationColorNameWintergreen = @"light_green"; -ConversationColorName const ConversationColorNameTeal = @"teal"; -ConversationColorName const ConversationColorNameBlue = @"blue"; -ConversationColorName const ConversationColorNameIndigo = @"indigo"; -ConversationColorName const ConversationColorNameViolet = @"purple"; -ConversationColorName const ConversationColorNamePlum = @"pink"; -ConversationColorName const ConversationColorNameTaupe = @"blue_grey"; -ConversationColorName const ConversationColorNameSteel = @"grey"; - -ConversationColorName const kConversationColorName_Default = ConversationColorNameSteel; - @interface TSThread () @property (nonatomic) NSDate *creationDate; -@property (nonatomic) NSString *conversationColorName; @property (nonatomic, nullable) NSNumber *archivedAsOfMessageSortId; @property (nonatomic, copy, nullable) NSString *messageDraft; @property (atomic, nullable) NSDate *mutedUntilDate; @@ -84,14 +68,6 @@ ConversationColorName const kConversationColorName_Default = ConversationColorNa if (self) { _creationDate = [NSDate date]; _messageDraft = nil; - - NSString *_Nullable contactId = self.contactIdentifier; - if (contactId.length > 0) { - // To be consistent with colors synced to desktop - _conversationColorName = [self.class stableColorNameForNewConversationWithString:contactId]; - } else { - _conversationColorName = [self.class stableColorNameForNewConversationWithString:self.uniqueId]; - } } return self; @@ -113,36 +89,6 @@ ConversationColorName const kConversationColorName_Default = ConversationColorNa } } - if (_conversationColorName.length == 0) { - NSString *_Nullable colorSeed = self.contactIdentifier; - if (colorSeed.length > 0) { - // group threads - colorSeed = self.uniqueId; - } - - // To be consistent with colors synced to desktop - ConversationColorName colorName = [self.class stableColorNameForLegacyConversationWithString:colorSeed]; - OWSAssertDebug(colorName); - - _conversationColorName = colorName; - } else if (![[[self class] conversationColorNames] containsObject:_conversationColorName]) { - // If we'd persisted a non-mapped color name - ConversationColorName _Nullable mappedColorName = self.class.legacyConversationColorMap[_conversationColorName]; - - if (!mappedColorName) { - // We previously used the wrong values for the new colors, it's possible we persited them. - // map them to the proper value - mappedColorName = self.class.legacyFixupConversationColorMap[_conversationColorName]; - } - - if (!mappedColorName) { - OWSFailDebug(@"failure: unexpected unmappable conversationColorName: %@", _conversationColorName); - mappedColorName = kConversationColorName_Default; - } - - _conversationColorName = mappedColorName; - } - NSDate *_Nullable lastMessageDate = [coder decodeObjectOfClass:NSDate.class forKey:@"lastMessageDate"]; NSDate *_Nullable archivalDate = [coder decodeObjectOfClass:NSDate.class forKey:@"archivalDate"]; _isArchivedByLegacyTimestampForSorting = @@ -206,10 +152,9 @@ ConversationColorName const kConversationColorName_Default = ConversationColorNa - (BOOL)isNoteToSelf { - if (!IsNoteToSelfEnabled()) { - return NO; - } - return [LKSessionMetaProtocol isThreadNoteToSelf:self]; + if (!IsNoteToSelfEnabled()) { return NO; } + if (![self isKindOfClass:TSContactThread.class]) { return NO; } + return [self.contactIdentifier isEqual:OWSIdentityManager.sharedManager.identityKeyPair.hexEncodedPublicKey]; } #pragma mark - To be subclassed. @@ -239,11 +184,6 @@ ConversationColorName const kConversationColorName_Default = ConversationColorNa return @[]; } -- (BOOL)hasSafetyNumbers -{ - return NO; -} - #pragma mark - Interactions /** @@ -451,11 +391,6 @@ ConversationColorName const kConversationColorName_Default = ConversationColorNa // there was no meaningful interaction. return NO; } - } else if ([interaction isKindOfClass:[TSInfoMessage class]]) { - TSInfoMessage *infoMessage = (TSInfoMessage *)interaction; - if (infoMessage.messageType == TSInfoMessageVerificationStateChange) { - return NO; - } } return YES; @@ -579,157 +514,6 @@ ConversationColorName const kConversationColorName_Default = ConversationColorNa }]; } -#pragma mark - Conversation Color - -- (ConversationColorName)conversationColorName -{ - OWSAssertDebug([self.class.conversationColorNames containsObject:_conversationColorName]); - return _conversationColorName; -} - -+ (NSArray *)colorNamesForNewConversation -{ - // all conversation colors except "steel" - return @[ - ConversationColorNameCrimson, - ConversationColorNameVermilion, - ConversationColorNameBurlap, - ConversationColorNameForest, - ConversationColorNameWintergreen, - ConversationColorNameTeal, - ConversationColorNameBlue, - ConversationColorNameIndigo, - ConversationColorNameViolet, - ConversationColorNamePlum, - ConversationColorNameTaupe, - ]; -} - -+ (NSArray *)conversationColorNames -{ - return [self.colorNamesForNewConversation arrayByAddingObject:kConversationColorName_Default]; -} - -+ (ConversationColorName)stableConversationColorNameForString:(NSString *)colorSeed - colorNames:(NSArray *)colorNames -{ - NSData *contactData = [colorSeed dataUsingEncoding:NSUTF8StringEncoding]; - - unsigned long long hash = 0; - NSUInteger hashingLength = sizeof(hash); - NSData *_Nullable hashData = [Cryptography computeSHA256Digest:contactData truncatedToBytes:hashingLength]; - if (hashData) { - [hashData getBytes:&hash length:hashingLength]; - } else { - OWSFailDebug(@"could not compute hash for color seed."); - } - - NSUInteger index = (hash % colorNames.count); - return [colorNames objectAtIndex:index]; -} - -+ (ConversationColorName)stableColorNameForNewConversationWithString:(NSString *)colorSeed -{ - return [self stableConversationColorNameForString:colorSeed colorNames:self.colorNamesForNewConversation]; -} - -// After introducing new conversation colors, we want to try to maintain as close as possible to the old color for an -// existing thread. -+ (ConversationColorName)stableColorNameForLegacyConversationWithString:(NSString *)colorSeed -{ - NSString *legacyColorName = - [self stableConversationColorNameForString:colorSeed colorNames:self.legacyConversationColorNames]; - ConversationColorName _Nullable mappedColorName = self.class.legacyConversationColorMap[legacyColorName]; - - if (!mappedColorName) { - OWSFailDebug(@"failure: unexpected unmappable legacyColorName: %@", legacyColorName); - return kConversationColorName_Default; - } - - return mappedColorName; -} - -+ (NSArray *)legacyConversationColorNames -{ - return @[ - @"red", - @"pink", - @"purple", - @"indigo", - @"blue", - @"cyan", - @"teal", - @"green", - @"deep_orange", - @"grey" - ]; -} - -+ (NSDictionary *)legacyConversationColorMap -{ - static NSDictionary *colorMap; - static dispatch_once_t onceToken; - dispatch_once(&onceToken, ^{ - colorMap = @{ - @"red" : ConversationColorNameCrimson, - @"deep_orange" : ConversationColorNameCrimson, - @"orange" : ConversationColorNameVermilion, - @"amber" : ConversationColorNameVermilion, - @"brown" : ConversationColorNameBurlap, - @"yellow" : ConversationColorNameBurlap, - @"pink" : ConversationColorNamePlum, - @"purple" : ConversationColorNameViolet, - @"deep_purple" : ConversationColorNameViolet, - @"indigo" : ConversationColorNameIndigo, - @"blue" : ConversationColorNameBlue, - @"light_blue" : ConversationColorNameBlue, - @"cyan" : ConversationColorNameTeal, - @"teal" : ConversationColorNameTeal, - @"green" : ConversationColorNameForest, - @"light_green" : ConversationColorNameWintergreen, - @"lime" : ConversationColorNameWintergreen, - @"blue_grey" : ConversationColorNameTaupe, - @"grey" : ConversationColorNameSteel, - }; - }); - - return colorMap; -} - -// we temporarily used the wrong value for the new color names. -+ (NSDictionary *)legacyFixupConversationColorMap -{ - static NSDictionary *colorMap; - static dispatch_once_t onceToken; - dispatch_once(&onceToken, ^{ - colorMap = @{ - @"crimson" : ConversationColorNameCrimson, - @"vermilion" : ConversationColorNameVermilion, - @"burlap" : ConversationColorNameBurlap, - @"forest" : ConversationColorNameForest, - @"wintergreen" : ConversationColorNameWintergreen, - @"teal" : ConversationColorNameTeal, - @"blue" : ConversationColorNameBlue, - @"indigo" : ConversationColorNameIndigo, - @"violet" : ConversationColorNameViolet, - @"plum" : ConversationColorNamePlum, - @"taupe" : ConversationColorNameTaupe, - @"steel" : ConversationColorNameSteel, - }; - }); - - return colorMap; -} - -- (void)updateConversationColorName:(ConversationColorName)colorName - transaction:(YapDatabaseReadWriteTransaction *)transaction -{ - [self applyChangeToSelfAndLatestCopy:transaction - changeBlock:^(TSThread *thread) { - thread.conversationColorName = colorName; - }]; -} - @end NS_ASSUME_NONNULL_END diff --git a/SignalUtilitiesKit/TypingIndicatorMessage.swift b/SignalUtilitiesKit/TypingIndicatorMessage.swift deleted file mode 100644 index 1788289c1..000000000 --- a/SignalUtilitiesKit/TypingIndicatorMessage.swift +++ /dev/null @@ -1,122 +0,0 @@ -// -// Copyright (c) 2019 Open Whisper Systems. All rights reserved. -// - -import Foundation - -@objc(OWSTypingIndicatorAction) -public enum TypingIndicatorAction: Int { - case started - case stopped -} - -@objc(OWSTypingIndicatorMessage) -public class TypingIndicatorMessage: TSOutgoingMessage { - private let action: TypingIndicatorAction - - // MARK: Initializers - - @objc - public init(thread: TSThread, - action: TypingIndicatorAction) { - self.action = action - - super.init(outgoingMessageWithTimestamp: NSDate.millisecondTimestamp(), - in: thread, - messageBody: nil, - attachmentIds: NSMutableArray(), - expiresInSeconds: 0, - expireStartedAt: 0, - isVoiceMessage: false, - groupMetaMessage: .unspecified, - quotedMessage: nil, - contactShare: nil, - linkPreview: nil) - } - - @objc - public required init!(coder: NSCoder) { - self.action = .started - super.init(coder: coder) - } - - @objc - public required init(dictionary dictionaryValue: [String: Any]!) throws { - self.action = .started - try super.init(dictionary: dictionaryValue) - } - - @objc - public override func shouldSyncTranscript() -> Bool { - return false - } - - @objc - public override var isSilent: Bool { - return true - } - - @objc - public override var isOnline: Bool { - return true - } - - private func protoAction(forAction action: TypingIndicatorAction) -> SSKProtoTypingMessage.SSKProtoTypingMessageAction { - switch action { - case .started: - return .started - case .stopped: - return .stopped - } - } - - @objc - public override func buildPlainTextData(_ recipient: SignalRecipient) -> Data? { - - let typingBuilder = SSKProtoTypingMessage.builder(timestamp: self.timestamp, - action: protoAction(forAction: action)) - - if let groupThread = self.thread as? TSGroupThread { - typingBuilder.setGroupID(groupThread.groupModel.groupId) - } - - let contentBuilder = SSKProtoContent.builder() - - do { - contentBuilder.setTypingMessage(try typingBuilder.build()) - - let data = try contentBuilder.buildSerializedData() - return data - } catch let error { - owsFailDebug("failed to build content: \(error)") - return nil - } - } - - // MARK: TSYapDatabaseObject overrides - - @objc - public override func shouldBeSaved() -> Bool { - return false - } - - @objc - public override var ttl: UInt32 { return UInt32(TTLUtilities.getTTL(for: .typingIndicator)) } - - @objc - public override var debugDescription: String { - return "typingIndicatorMessage" - } - - // MARK: - - @objc(stringForTypingIndicatorAction:) - public class func string(forTypingIndicatorAction action: TypingIndicatorAction) -> String { - switch action { - case .started: - return "started" - case .stopped: - return "stopped" - } - } -} diff --git a/SignalUtilitiesKit/TypingIndicators.swift b/SignalUtilitiesKit/TypingIndicators.swift index e05cb43ff..18b5325fb 100644 --- a/SignalUtilitiesKit/TypingIndicators.swift +++ b/SignalUtilitiesKit/TypingIndicators.swift @@ -73,10 +73,6 @@ public class TypingIndicatorsImpl: NSObject, TypingIndicators { return SSKEnvironment.shared.primaryStorage } - private var syncManager: OWSSyncManagerProtocol { - return SSKEnvironment.shared.syncManager - } - // MARK: - @objc @@ -87,8 +83,6 @@ public class TypingIndicatorsImpl: NSObject, TypingIndicators { primaryStorage.dbReadWriteConnection.setBool(value, forKey: kDatabaseKey_TypingIndicatorsEnabled, inCollection: kDatabaseCollection) - syncManager.sendConfigurationSyncMessage() - NotificationCenter.default.postNotificationNameAsync(TypingIndicatorsImpl.typingIndicatorStateDidChange, object: nil) } @@ -303,9 +297,7 @@ public class TypingIndicatorsImpl: NSObject, TypingIndicators { sendPauseTimer = nil } - private func sendTypingMessageIfNecessary(forThread thread: TSThread, action: TypingIndicatorAction) { - Logger.verbose("\(TypingIndicatorMessage.string(forTypingIndicatorAction: action))") - + private func sendTypingMessageIfNecessary(forThread thread: TSThread, action: TypingIndicator.Kind) { guard let delegate = delegate else { owsFailDebug("Missing delegate.") return @@ -320,12 +312,7 @@ public class TypingIndicatorsImpl: NSObject, TypingIndicators { if !SessionMetaProtocol.shouldSendTypingIndicator(in: thread) { return } let typingIndicator = TypingIndicator() - typingIndicator.kind = { - switch action { - case .started: return .started - case .stopped: return .stopped - } - }() + typingIndicator.kind = action typingIndicator.threadID = thread.uniqueId! let destination = Message.Destination.from(thread) let job = MessageSendJob(message: typingIndicator, destination: destination) diff --git a/SignalUtilitiesKit/ApprovalRailCellView.swift b/SignalUtilitiesKit/UI/ApprovalRailCellView.swift similarity index 100% rename from SignalUtilitiesKit/ApprovalRailCellView.swift rename to SignalUtilitiesKit/UI/ApprovalRailCellView.swift diff --git a/SignalUtilitiesKit/AttachmentApprovalInputAccessoryView.swift b/SignalUtilitiesKit/UI/AttachmentApprovalInputAccessoryView.swift similarity index 100% rename from SignalUtilitiesKit/AttachmentApprovalInputAccessoryView.swift rename to SignalUtilitiesKit/UI/AttachmentApprovalInputAccessoryView.swift diff --git a/SignalUtilitiesKit/AttachmentApprovalViewController.swift b/SignalUtilitiesKit/UI/AttachmentApprovalViewController.swift similarity index 100% rename from SignalUtilitiesKit/AttachmentApprovalViewController.swift rename to SignalUtilitiesKit/UI/AttachmentApprovalViewController.swift diff --git a/SignalUtilitiesKit/AttachmentCaptionToolbar.swift b/SignalUtilitiesKit/UI/AttachmentCaptionToolbar.swift similarity index 100% rename from SignalUtilitiesKit/AttachmentCaptionToolbar.swift rename to SignalUtilitiesKit/UI/AttachmentCaptionToolbar.swift diff --git a/SignalUtilitiesKit/AttachmentCaptionViewController.swift b/SignalUtilitiesKit/UI/AttachmentCaptionViewController.swift similarity index 100% rename from SignalUtilitiesKit/AttachmentCaptionViewController.swift rename to SignalUtilitiesKit/UI/AttachmentCaptionViewController.swift diff --git a/SignalUtilitiesKit/AttachmentItemCollection.swift b/SignalUtilitiesKit/UI/AttachmentItemCollection.swift similarity index 100% rename from SignalUtilitiesKit/AttachmentItemCollection.swift rename to SignalUtilitiesKit/UI/AttachmentItemCollection.swift diff --git a/SignalUtilitiesKit/AttachmentPrepViewController.swift b/SignalUtilitiesKit/UI/AttachmentPrepViewController.swift similarity index 100% rename from SignalUtilitiesKit/AttachmentPrepViewController.swift rename to SignalUtilitiesKit/UI/AttachmentPrepViewController.swift diff --git a/SignalUtilitiesKit/AttachmentTextToolbar.swift b/SignalUtilitiesKit/UI/AttachmentTextToolbar.swift similarity index 100% rename from SignalUtilitiesKit/AttachmentTextToolbar.swift rename to SignalUtilitiesKit/UI/AttachmentTextToolbar.swift diff --git a/SignalUtilitiesKit/AttachmentTextView.swift b/SignalUtilitiesKit/UI/AttachmentTextView.swift similarity index 100% rename from SignalUtilitiesKit/AttachmentTextView.swift rename to SignalUtilitiesKit/UI/AttachmentTextView.swift diff --git a/SignalUtilitiesKit/BlockListUIUtils.h b/SignalUtilitiesKit/UI/BlockListUIUtils.h similarity index 82% rename from SignalUtilitiesKit/BlockListUIUtils.h rename to SignalUtilitiesKit/UI/BlockListUIUtils.h index 77a7ec23a..c1dad0286 100644 --- a/SignalUtilitiesKit/BlockListUIUtils.h +++ b/SignalUtilitiesKit/UI/BlockListUIUtils.h @@ -8,8 +8,6 @@ NS_ASSUME_NONNULL_BEGIN @class Contact; @class OWSBlockingManager; -@class OWSContactsManager; -@class OWSMessageSender; @class SignalAccount; @class TSGroupModel; @class TSThread; @@ -25,20 +23,16 @@ typedef void (^BlockActionCompletionBlock)(BOOL isBlocked); + (void)showBlockThreadActionSheet:(TSThread *)thread fromViewController:(UIViewController *)fromViewController blockingManager:(OWSBlockingManager *)blockingManager - contactsManager:(OWSContactsManager *)contactsManager - messageSender:(OWSMessageSender *)messageSender completionBlock:(nullable BlockActionCompletionBlock)completionBlock; + (void)showBlockPhoneNumberActionSheet:(NSString *)phoneNumber fromViewController:(UIViewController *)fromViewController blockingManager:(OWSBlockingManager *)blockingManager - contactsManager:(OWSContactsManager *)contactsManager completionBlock:(nullable BlockActionCompletionBlock)completionBlock; + (void)showBlockSignalAccountActionSheet:(SignalAccount *)signalAccount fromViewController:(UIViewController *)fromViewController blockingManager:(OWSBlockingManager *)blockingManager - contactsManager:(OWSContactsManager *)contactsManager completionBlock:(nullable BlockActionCompletionBlock)completionBlock; #pragma mark - Unblock @@ -46,19 +40,16 @@ typedef void (^BlockActionCompletionBlock)(BOOL isBlocked); + (void)showUnblockThreadActionSheet:(TSThread *)thread fromViewController:(UIViewController *)fromViewController blockingManager:(OWSBlockingManager *)blockingManager - contactsManager:(OWSContactsManager *)contactsManager completionBlock:(nullable BlockActionCompletionBlock)completionBlock; + (void)showUnblockPhoneNumberActionSheet:(NSString *)phoneNumber fromViewController:(UIViewController *)fromViewController blockingManager:(OWSBlockingManager *)blockingManager - contactsManager:(OWSContactsManager *)contactsManager completionBlock:(nullable BlockActionCompletionBlock)completionBlock; + (void)showUnblockSignalAccountActionSheet:(SignalAccount *)signalAccount fromViewController:(UIViewController *)fromViewController blockingManager:(OWSBlockingManager *)blockingManager - contactsManager:(OWSContactsManager *)contactsManager completionBlock:(nullable BlockActionCompletionBlock)completionBlock; + (void)showUnblockGroupActionSheet:(TSGroupModel *)groupModel diff --git a/SignalUtilitiesKit/BlockListUIUtils.m b/SignalUtilitiesKit/UI/BlockListUIUtils.m similarity index 95% rename from SignalUtilitiesKit/BlockListUIUtils.m rename to SignalUtilitiesKit/UI/BlockListUIUtils.m index ab6508375..179384411 100644 --- a/SignalUtilitiesKit/BlockListUIUtils.m +++ b/SignalUtilitiesKit/UI/BlockListUIUtils.m @@ -3,16 +3,12 @@ // #import "BlockListUIUtils.h" -#import "OWSContactsManager.h" -#import "PhoneNumber.h" #import "TSContactThread.h" -#import #import #import #import #import #import -#import #import #import "UIView+OWS.h" @@ -27,8 +23,6 @@ typedef void (^BlockAlertCompletionBlock)(UIAlertAction *action); + (void)showBlockThreadActionSheet:(TSThread *)thread fromViewController:(UIViewController *)fromViewController blockingManager:(OWSBlockingManager *)blockingManager - contactsManager:(OWSContactsManager *)contactsManager - messageSender:(OWSMessageSender *)messageSender completionBlock:(nullable BlockActionCompletionBlock)completionBlock { if ([thread isKindOfClass:[TSContactThread class]]) { @@ -36,14 +30,12 @@ typedef void (^BlockAlertCompletionBlock)(UIAlertAction *action); [self showBlockPhoneNumberActionSheet:contactThread.contactIdentifier fromViewController:fromViewController blockingManager:blockingManager - contactsManager:contactsManager completionBlock:completionBlock]; } else if ([thread isKindOfClass:[TSGroupThread class]]) { TSGroupThread *groupThread = (TSGroupThread *)thread; [self showBlockGroupActionSheet:groupThread fromViewController:fromViewController blockingManager:blockingManager - messageSender:messageSender completionBlock:completionBlock]; } else { OWSFailDebug(@"unexpected thread type: %@", thread.class); @@ -53,10 +45,9 @@ typedef void (^BlockAlertCompletionBlock)(UIAlertAction *action); + (void)showBlockPhoneNumberActionSheet:(NSString *)phoneNumber fromViewController:(UIViewController *)fromViewController blockingManager:(OWSBlockingManager *)blockingManager - contactsManager:(OWSContactsManager *)contactsManager completionBlock:(nullable BlockActionCompletionBlock)completionBlock { - NSString *displayName = [contactsManager displayNameForPhoneIdentifier:phoneNumber]; + NSString *displayName = [SSKEnvironment.shared.profileManager profileNameForRecipientWithID:phoneNumber avoidingWriteTransaction:YES]; [self showBlockPhoneNumbersActionSheet:@[ phoneNumber ] displayName:displayName fromViewController:fromViewController @@ -67,10 +58,9 @@ typedef void (^BlockAlertCompletionBlock)(UIAlertAction *action); + (void)showBlockSignalAccountActionSheet:(SignalAccount *)signalAccount fromViewController:(UIViewController *)fromViewController blockingManager:(OWSBlockingManager *)blockingManager - contactsManager:(OWSContactsManager *)contactsManager completionBlock:(nullable BlockActionCompletionBlock)completionBlock { - NSString *displayName = [contactsManager displayNameForSignalAccount:signalAccount]; + NSString *displayName = [SSKEnvironment.shared.profileManager profileNameForRecipientWithID:signalAccount.recipientId avoidingWriteTransaction:YES]; [self showBlockPhoneNumbersActionSheet:@[ signalAccount.recipientId ] displayName:displayName fromViewController:fromViewController @@ -152,7 +142,6 @@ typedef void (^BlockAlertCompletionBlock)(UIAlertAction *action); + (void)showBlockGroupActionSheet:(TSGroupThread *)groupThread fromViewController:(UIViewController *)fromViewController blockingManager:(OWSBlockingManager *)blockingManager - messageSender:(OWSMessageSender *)messageSender completionBlock:(nullable BlockActionCompletionBlock)completionBlock { OWSAssertDebug(groupThread); @@ -179,7 +168,6 @@ typedef void (^BlockAlertCompletionBlock)(UIAlertAction *action); [self blockGroup:groupThread fromViewController:fromViewController blockingManager:blockingManager - messageSender:messageSender completionBlock:^(UIAlertAction *ignore) { if (completionBlock) { completionBlock(YES); @@ -230,7 +218,6 @@ typedef void (^BlockAlertCompletionBlock)(UIAlertAction *action); + (void)blockGroup:(TSGroupThread *)groupThread fromViewController:(UIViewController *)fromViewController blockingManager:(OWSBlockingManager *)blockingManager - messageSender:(OWSMessageSender *)messageSender completionBlock:(BlockAlertCompletionBlock)completionBlock { OWSAssertDebug(groupThread); @@ -244,7 +231,9 @@ typedef void (^BlockAlertCompletionBlock)(UIAlertAction *action); // via params and instead have to create our own sneaky transaction here. [groupThread leaveGroupWithSneakyTransaction]; - [ThreadUtil enqueueLeaveGroupMessageInThread:groupThread]; + // TODO TODO TODO + +// [ThreadUtil enqueueLeaveGroupMessageInThread:groupThread]; NSString *groupName = groupThread.name.length > 0 ? groupThread.name : TSGroupThread.defaultGroupName; @@ -266,7 +255,6 @@ typedef void (^BlockAlertCompletionBlock)(UIAlertAction *action); + (void)showUnblockThreadActionSheet:(TSThread *)thread fromViewController:(UIViewController *)fromViewController blockingManager:(OWSBlockingManager *)blockingManager - contactsManager:(OWSContactsManager *)contactsManager completionBlock:(nullable BlockActionCompletionBlock)completionBlock { if ([thread isKindOfClass:[TSContactThread class]]) { @@ -274,7 +262,6 @@ typedef void (^BlockAlertCompletionBlock)(UIAlertAction *action); [self showUnblockPhoneNumberActionSheet:contactThread.contactIdentifier fromViewController:fromViewController blockingManager:blockingManager - contactsManager:contactsManager completionBlock:completionBlock]; } else if ([thread isKindOfClass:[TSGroupThread class]]) { TSGroupThread *groupThread = (TSGroupThread *)thread; @@ -292,7 +279,6 @@ typedef void (^BlockAlertCompletionBlock)(UIAlertAction *action); + (void)showUnblockPhoneNumberActionSheet:(NSString *)phoneNumber fromViewController:(UIViewController *)fromViewController blockingManager:(OWSBlockingManager *)blockingManager - contactsManager:(OWSContactsManager *)contactsManager completionBlock:(nullable BlockActionCompletionBlock)completionBlock { NSString *displayName = [LKUserDisplayNameUtilities getPrivateChatDisplayNameFor:phoneNumber] ?: phoneNumber; @@ -306,10 +292,9 @@ typedef void (^BlockAlertCompletionBlock)(UIAlertAction *action); + (void)showUnblockSignalAccountActionSheet:(SignalAccount *)signalAccount fromViewController:(UIViewController *)fromViewController blockingManager:(OWSBlockingManager *)blockingManager - contactsManager:(OWSContactsManager *)contactsManager completionBlock:(nullable BlockActionCompletionBlock)completionBlock { - NSString *displayName = [contactsManager displayNameForSignalAccount:signalAccount]; + NSString *displayName = [SSKEnvironment.shared.profileManager profileNameForRecipientWithID:signalAccount.recipientId avoidingWriteTransaction:YES]; [self showUnblockPhoneNumbersActionSheet:@[ signalAccount.recipientId ] displayName:displayName fromViewController:fromViewController diff --git a/SignalUtilitiesKit/UI/CircleView.swift b/SignalUtilitiesKit/UI/CircleView.swift new file mode 100644 index 000000000..3cec26ba5 --- /dev/null +++ b/SignalUtilitiesKit/UI/CircleView.swift @@ -0,0 +1,82 @@ +// +// Copyright (c) 2020 Open Whisper Systems. All rights reserved. +// +import UIKit + +@objc (OWSCircleView) +public class CircleView: UIView { + + @available(*, unavailable, message:"use other constructor instead.") + required public init?(coder aDecoder: NSCoder) { + notImplemented() + } + + @objc + public required init() { + super.init(frame: .zero) + } + + @objc + public required init(diameter: CGFloat) { + super.init(frame: .zero) + + autoSetDimensions(to: CGSize(width: diameter, height: diameter)) + } + + @objc + override public var frame: CGRect { + didSet { + updateRadius() + } + } + + @objc + override public var bounds: CGRect { + didSet { + updateRadius() + } + } + + private func updateRadius() { + self.layer.cornerRadius = self.bounds.size.height / 2 + } +} + +@objc (OWSPillView) +public class PillView: UIView { + + public override init(frame: CGRect) { + super.init(frame: frame) + + // Constrain to be a pill that is at least a circle, and maybe wider. + autoPin(toAspectRatio: 1.0, relation: .greaterThanOrEqual) + + // low priority contstraint to ensure the pill + // is no taller than necessary + NSLayoutConstraint.autoSetPriority(.defaultLow) { + self.autoSetDimension(.height, toSize: 0) + } + } + + required init?(coder aDecoder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + + @objc + override public var frame: CGRect { + didSet { + updateRadius() + } + } + + @objc + override public var bounds: CGRect { + didSet { + updateRadius() + } + } + + private func updateRadius() { + layer.cornerRadius = bounds.size.height / 2 + } +} diff --git a/SignalUtilitiesKit/ConversationStyle.swift b/SignalUtilitiesKit/UI/ConversationStyle.swift similarity index 92% rename from SignalUtilitiesKit/ConversationStyle.swift rename to SignalUtilitiesKit/UI/ConversationStyle.swift index c93742cbb..a24d00639 100644 --- a/SignalUtilitiesKit/ConversationStyle.swift +++ b/SignalUtilitiesKit/UI/ConversationStyle.swift @@ -65,7 +65,6 @@ public class ConversationStyle: NSObject { public required init(thread: TSThread) { self.thread = thread - self.conversationColor = ConversationStyle.conversationColor(thread: thread) super.init() @@ -92,7 +91,7 @@ public class ConversationStyle: NSObject { @objc public func updateProperties() { if thread.isGroupThread() { - if let thread = thread as? TSGroupThread, thread.isRSSFeed { + if thread is TSGroupThread { gutterLeading = 16 gutterTrailing = 16 } else { @@ -109,7 +108,7 @@ public class ConversationStyle: NSObject { headerGutterTrailing = 16 errorGutterTrailing = 16 - if let thread = thread as? TSGroupThread, thread.isRSSFeed { + if thread is TSGroupThread { maxMessageWidth = floor(contentWidth) } else { maxMessageWidth = floor(contentWidth - 32) @@ -130,21 +129,10 @@ public class ConversationStyle: NSObject { textInsetHorizontal = 12 lastTextLineAxis = CGFloat(round(baseFontOffset + messageTextFont.capHeight * 0.5)) - - self.conversationColor = ConversationStyle.conversationColor(thread: thread) } // MARK: Colors - @objc - public var conversationColor: OWSConversationColor - - private class func conversationColor(thread: TSThread) -> OWSConversationColor { - let colorName = thread.conversationColorName - - return OWSConversationColor.conversationColorOrDefault(colorName: colorName) - } - @objc private static var defaultBubbleColorIncoming: UIColor { return Colors.receivedMessageBackground diff --git a/SignalUtilitiesKit/DirectionalPanGestureRecognizer.swift b/SignalUtilitiesKit/UI/DirectionalPanGestureRecognizer.swift similarity index 100% rename from SignalUtilitiesKit/DirectionalPanGestureRecognizer.swift rename to SignalUtilitiesKit/UI/DirectionalPanGestureRecognizer.swift diff --git a/SignalUtilitiesKit/DisappearingTimerConfigurationView.swift b/SignalUtilitiesKit/UI/DisappearingTimerConfigurationView.swift similarity index 100% rename from SignalUtilitiesKit/DisappearingTimerConfigurationView.swift rename to SignalUtilitiesKit/UI/DisappearingTimerConfigurationView.swift diff --git a/SignalUtilitiesKit/DisplayableText.swift b/SignalUtilitiesKit/UI/DisplayableText.swift similarity index 100% rename from SignalUtilitiesKit/DisplayableText.swift rename to SignalUtilitiesKit/UI/DisplayableText.swift diff --git a/SignalUtilitiesKit/GalleryRailView.swift b/SignalUtilitiesKit/UI/GalleryRailView.swift similarity index 100% rename from SignalUtilitiesKit/GalleryRailView.swift rename to SignalUtilitiesKit/UI/GalleryRailView.swift diff --git a/SignalUtilitiesKit/GradientView.swift b/SignalUtilitiesKit/UI/GradientView.swift similarity index 100% rename from SignalUtilitiesKit/GradientView.swift rename to SignalUtilitiesKit/UI/GradientView.swift diff --git a/SignalUtilitiesKit/Identicon+ObjC.swift b/SignalUtilitiesKit/UI/Identicon+ObjC.swift similarity index 100% rename from SignalUtilitiesKit/Identicon+ObjC.swift rename to SignalUtilitiesKit/UI/Identicon+ObjC.swift diff --git a/SignalUtilitiesKit/ImageEditorBrushViewController.swift b/SignalUtilitiesKit/UI/ImageEditorBrushViewController.swift similarity index 100% rename from SignalUtilitiesKit/ImageEditorBrushViewController.swift rename to SignalUtilitiesKit/UI/ImageEditorBrushViewController.swift diff --git a/SignalUtilitiesKit/ImageEditorCanvasView.swift b/SignalUtilitiesKit/UI/ImageEditorCanvasView.swift similarity index 100% rename from SignalUtilitiesKit/ImageEditorCanvasView.swift rename to SignalUtilitiesKit/UI/ImageEditorCanvasView.swift diff --git a/SignalUtilitiesKit/ImageEditorContents.swift b/SignalUtilitiesKit/UI/ImageEditorContents.swift similarity index 100% rename from SignalUtilitiesKit/ImageEditorContents.swift rename to SignalUtilitiesKit/UI/ImageEditorContents.swift diff --git a/SignalUtilitiesKit/ImageEditorCropViewController.swift b/SignalUtilitiesKit/UI/ImageEditorCropViewController.swift similarity index 100% rename from SignalUtilitiesKit/ImageEditorCropViewController.swift rename to SignalUtilitiesKit/UI/ImageEditorCropViewController.swift diff --git a/SignalUtilitiesKit/ImageEditorItem.swift b/SignalUtilitiesKit/UI/ImageEditorItem.swift similarity index 100% rename from SignalUtilitiesKit/ImageEditorItem.swift rename to SignalUtilitiesKit/UI/ImageEditorItem.swift diff --git a/SignalUtilitiesKit/ImageEditorModel.swift b/SignalUtilitiesKit/UI/ImageEditorModel.swift similarity index 100% rename from SignalUtilitiesKit/ImageEditorModel.swift rename to SignalUtilitiesKit/UI/ImageEditorModel.swift diff --git a/SignalUtilitiesKit/ImageEditorPaletteView.swift b/SignalUtilitiesKit/UI/ImageEditorPaletteView.swift similarity index 100% rename from SignalUtilitiesKit/ImageEditorPaletteView.swift rename to SignalUtilitiesKit/UI/ImageEditorPaletteView.swift diff --git a/SignalUtilitiesKit/ImageEditorPanGestureRecognizer.swift b/SignalUtilitiesKit/UI/ImageEditorPanGestureRecognizer.swift similarity index 100% rename from SignalUtilitiesKit/ImageEditorPanGestureRecognizer.swift rename to SignalUtilitiesKit/UI/ImageEditorPanGestureRecognizer.swift diff --git a/SignalUtilitiesKit/ImageEditorPinchGestureRecognizer.swift b/SignalUtilitiesKit/UI/ImageEditorPinchGestureRecognizer.swift similarity index 100% rename from SignalUtilitiesKit/ImageEditorPinchGestureRecognizer.swift rename to SignalUtilitiesKit/UI/ImageEditorPinchGestureRecognizer.swift diff --git a/SignalUtilitiesKit/ImageEditorStrokeItem.swift b/SignalUtilitiesKit/UI/ImageEditorStrokeItem.swift similarity index 100% rename from SignalUtilitiesKit/ImageEditorStrokeItem.swift rename to SignalUtilitiesKit/UI/ImageEditorStrokeItem.swift diff --git a/SignalUtilitiesKit/ImageEditorTextItem.swift b/SignalUtilitiesKit/UI/ImageEditorTextItem.swift similarity index 100% rename from SignalUtilitiesKit/ImageEditorTextItem.swift rename to SignalUtilitiesKit/UI/ImageEditorTextItem.swift diff --git a/SignalUtilitiesKit/ImageEditorTextViewController.swift b/SignalUtilitiesKit/UI/ImageEditorTextViewController.swift similarity index 100% rename from SignalUtilitiesKit/ImageEditorTextViewController.swift rename to SignalUtilitiesKit/UI/ImageEditorTextViewController.swift diff --git a/SignalUtilitiesKit/ImageEditorTransform.swift b/SignalUtilitiesKit/UI/ImageEditorTransform.swift similarity index 100% rename from SignalUtilitiesKit/ImageEditorTransform.swift rename to SignalUtilitiesKit/UI/ImageEditorTransform.swift diff --git a/SignalUtilitiesKit/ImageEditorView.swift b/SignalUtilitiesKit/UI/ImageEditorView.swift similarity index 100% rename from SignalUtilitiesKit/ImageEditorView.swift rename to SignalUtilitiesKit/UI/ImageEditorView.swift diff --git a/SignalUtilitiesKit/MediaMessageView.swift b/SignalUtilitiesKit/UI/MediaMessageView.swift similarity index 100% rename from SignalUtilitiesKit/MediaMessageView.swift rename to SignalUtilitiesKit/UI/MediaMessageView.swift diff --git a/SignalUtilitiesKit/MessageApprovalViewController.swift b/SignalUtilitiesKit/UI/MessageApprovalViewController.swift similarity index 92% rename from SignalUtilitiesKit/MessageApprovalViewController.swift rename to SignalUtilitiesKit/UI/MessageApprovalViewController.swift index 1cdf49b71..a50083776 100644 --- a/SignalUtilitiesKit/MessageApprovalViewController.swift +++ b/SignalUtilitiesKit/UI/MessageApprovalViewController.swift @@ -19,7 +19,6 @@ public class MessageApprovalViewController: OWSViewController, UITextViewDelegat let thread: TSThread let initialMessageText: String - let contactsManager: OWSContactsManager private(set) var textView: UITextView! private var sendButton: UIBarButtonItem! @@ -32,10 +31,9 @@ public class MessageApprovalViewController: OWSViewController, UITextViewDelegat } @objc - required public init(messageText: String, thread: TSThread, contactsManager: OWSContactsManager, delegate: MessageApprovalViewControllerDelegate) { + required public init(messageText: String, thread: TSThread, delegate: MessageApprovalViewControllerDelegate) { self.initialMessageText = messageText self.thread = thread - self.contactsManager = contactsManager self.delegate = delegate super.init(nibName: nil, bundle: nil) @@ -164,7 +162,8 @@ public class MessageApprovalViewController: OWSViewController, UITextViewDelegat return recipientRow } - nameLabel.attributedText = contactsManager.formattedFullName(forRecipientId: contactThread.contactIdentifier(), font: font) + let publicKey = thread.contactIdentifier()! + nameLabel.text = SSKEnvironment.shared.profileManager.profileNameForRecipient(withID: publicKey, avoidingWriteTransaction: true) nameLabel.textColor = Theme.primaryColor if let profileName = self.profileName(contactThread: contactThread) { @@ -189,13 +188,8 @@ public class MessageApprovalViewController: OWSViewController, UITextViewDelegat } private func profileName(contactThread: TSContactThread) -> String? { - let recipientId = contactThread.contactIdentifier() - - if contactsManager.hasNameInSystemContacts(forRecipientId: recipientId) { - // Don't display profile name when we have a veritas name in system Contacts - return nil - } - return contactsManager.formattedProfileName(forRecipientId: recipientId) + let publicKey = contactThread.contactIdentifier() + return SSKEnvironment.shared.profileManager.profileNameForRecipient(withID: publicKey, avoidingWriteTransaction: true) } // MARK: - Event Handlers diff --git a/SignalUtilitiesKit/ModalActivityIndicatorViewController.swift b/SignalUtilitiesKit/UI/ModalActivityIndicatorViewController.swift similarity index 100% rename from SignalUtilitiesKit/ModalActivityIndicatorViewController.swift rename to SignalUtilitiesKit/UI/ModalActivityIndicatorViewController.swift diff --git a/SignalUtilitiesKit/OWSAlerts.swift b/SignalUtilitiesKit/UI/OWSAlerts.swift similarity index 100% rename from SignalUtilitiesKit/OWSAlerts.swift rename to SignalUtilitiesKit/UI/OWSAlerts.swift diff --git a/SignalUtilitiesKit/OWSAnyTouchGestureRecognizer.h b/SignalUtilitiesKit/UI/OWSAnyTouchGestureRecognizer.h similarity index 100% rename from SignalUtilitiesKit/OWSAnyTouchGestureRecognizer.h rename to SignalUtilitiesKit/UI/OWSAnyTouchGestureRecognizer.h diff --git a/SignalUtilitiesKit/OWSAnyTouchGestureRecognizer.m b/SignalUtilitiesKit/UI/OWSAnyTouchGestureRecognizer.m similarity index 100% rename from SignalUtilitiesKit/OWSAnyTouchGestureRecognizer.m rename to SignalUtilitiesKit/UI/OWSAnyTouchGestureRecognizer.m diff --git a/SignalUtilitiesKit/OWSButton.swift b/SignalUtilitiesKit/UI/OWSButton.swift similarity index 100% rename from SignalUtilitiesKit/OWSButton.swift rename to SignalUtilitiesKit/UI/OWSButton.swift diff --git a/SignalUtilitiesKit/OWSFlatButton.swift b/SignalUtilitiesKit/UI/OWSFlatButton.swift similarity index 100% rename from SignalUtilitiesKit/OWSFlatButton.swift rename to SignalUtilitiesKit/UI/OWSFlatButton.swift diff --git a/SignalUtilitiesKit/OWSLayerView.swift b/SignalUtilitiesKit/UI/OWSLayerView.swift similarity index 100% rename from SignalUtilitiesKit/OWSLayerView.swift rename to SignalUtilitiesKit/UI/OWSLayerView.swift diff --git a/SignalUtilitiesKit/OWSNavigationBar.swift b/SignalUtilitiesKit/UI/OWSNavigationBar.swift similarity index 86% rename from SignalUtilitiesKit/OWSNavigationBar.swift rename to SignalUtilitiesKit/UI/OWSNavigationBar.swift index 7ba0c450d..707023a3b 100644 --- a/SignalUtilitiesKit/OWSNavigationBar.swift +++ b/SignalUtilitiesKit/UI/OWSNavigationBar.swift @@ -91,31 +91,6 @@ public class OWSNavigationBar: UINavigationBar { let color = Theme.navbarBackgroundColor let backgroundImage = UIImage(color: color) self.setBackgroundImage(backgroundImage, for: .default) - - /* - let blurEffect = Theme.barBlurEffect - - let blurEffectView: UIVisualEffectView = { - if let existingBlurEffectView = self.blurEffectView { - existingBlurEffectView.isHidden = false - return existingBlurEffectView - } - - let blurEffectView = UIVisualEffectView() - blurEffectView.isUserInteractionEnabled = false - - self.blurEffectView = blurEffectView - self.insertSubview(blurEffectView, at: 0) - - // navbar frame doesn't account for statusBar, so, same as the built-in navbar background, we need to exceed - // the navbar bounds to have the blur extend up and behind the status bar. - blurEffectView.autoPinEdgesToSuperviewEdges(with: UIEdgeInsets(top: -statusBarHeight, left: 0, bottom: 0, right: 0)) - - return blurEffectView - }() - - blurEffectView.effect = blurEffect - */ // remove hairline below bar. self.shadowImage = UIImage() diff --git a/SignalUtilitiesKit/OWSNavigationController.h b/SignalUtilitiesKit/UI/OWSNavigationController.h similarity index 100% rename from SignalUtilitiesKit/OWSNavigationController.h rename to SignalUtilitiesKit/UI/OWSNavigationController.h diff --git a/SignalUtilitiesKit/OWSNavigationController.m b/SignalUtilitiesKit/UI/OWSNavigationController.m similarity index 100% rename from SignalUtilitiesKit/OWSNavigationController.m rename to SignalUtilitiesKit/UI/OWSNavigationController.m diff --git a/SignalUtilitiesKit/OWSSearchBar.h b/SignalUtilitiesKit/UI/OWSSearchBar.h similarity index 100% rename from SignalUtilitiesKit/OWSSearchBar.h rename to SignalUtilitiesKit/UI/OWSSearchBar.h diff --git a/SignalUtilitiesKit/OWSSearchBar.m b/SignalUtilitiesKit/UI/OWSSearchBar.m similarity index 100% rename from SignalUtilitiesKit/OWSSearchBar.m rename to SignalUtilitiesKit/UI/OWSSearchBar.m diff --git a/SignalUtilitiesKit/OWSTableViewController.h b/SignalUtilitiesKit/UI/OWSTableViewController.h similarity index 100% rename from SignalUtilitiesKit/OWSTableViewController.h rename to SignalUtilitiesKit/UI/OWSTableViewController.h diff --git a/SignalUtilitiesKit/OWSTableViewController.m b/SignalUtilitiesKit/UI/OWSTableViewController.m similarity index 100% rename from SignalUtilitiesKit/OWSTableViewController.m rename to SignalUtilitiesKit/UI/OWSTableViewController.m diff --git a/SignalUtilitiesKit/OWSTextField.h b/SignalUtilitiesKit/UI/OWSTextField.h similarity index 100% rename from SignalUtilitiesKit/OWSTextField.h rename to SignalUtilitiesKit/UI/OWSTextField.h diff --git a/SignalUtilitiesKit/OWSTextField.m b/SignalUtilitiesKit/UI/OWSTextField.m similarity index 100% rename from SignalUtilitiesKit/OWSTextField.m rename to SignalUtilitiesKit/UI/OWSTextField.m diff --git a/SignalUtilitiesKit/OWSTextView.h b/SignalUtilitiesKit/UI/OWSTextView.h similarity index 100% rename from SignalUtilitiesKit/OWSTextView.h rename to SignalUtilitiesKit/UI/OWSTextView.h diff --git a/SignalUtilitiesKit/OWSTextView.m b/SignalUtilitiesKit/UI/OWSTextView.m similarity index 100% rename from SignalUtilitiesKit/OWSTextView.m rename to SignalUtilitiesKit/UI/OWSTextView.m diff --git a/SignalUtilitiesKit/OWSViewController+ImageEditor.swift b/SignalUtilitiesKit/UI/OWSViewController+ImageEditor.swift similarity index 100% rename from SignalUtilitiesKit/OWSViewController+ImageEditor.swift rename to SignalUtilitiesKit/UI/OWSViewController+ImageEditor.swift diff --git a/SignalUtilitiesKit/OWSViewController.h b/SignalUtilitiesKit/UI/OWSViewController.h similarity index 100% rename from SignalUtilitiesKit/OWSViewController.h rename to SignalUtilitiesKit/UI/OWSViewController.h diff --git a/SignalUtilitiesKit/OWSViewController.m b/SignalUtilitiesKit/UI/OWSViewController.m similarity index 100% rename from SignalUtilitiesKit/OWSViewController.m rename to SignalUtilitiesKit/UI/OWSViewController.m diff --git a/SignalUtilitiesKit/OWSWindowManager.h b/SignalUtilitiesKit/UI/OWSWindowManager.h similarity index 100% rename from SignalUtilitiesKit/OWSWindowManager.h rename to SignalUtilitiesKit/UI/OWSWindowManager.h diff --git a/SignalUtilitiesKit/OWSWindowManager.m b/SignalUtilitiesKit/UI/OWSWindowManager.m similarity index 87% rename from SignalUtilitiesKit/OWSWindowManager.m rename to SignalUtilitiesKit/UI/OWSWindowManager.m index b4ee977e5..faf6a286e 100644 --- a/SignalUtilitiesKit/OWSWindowManager.m +++ b/SignalUtilitiesKit/UI/OWSWindowManager.m @@ -127,15 +127,11 @@ const UIWindowLevel UIWindowLevel_MessageActions(void) #pragma mark - -@interface OWSWindowManager () +@interface OWSWindowManager () // UIWindowLevelNormal @property (nonatomic) UIWindow *rootWindow; -// UIWindowLevel_ReturnToCall -@property (nonatomic) UIWindow *returnToCallWindow; -@property (nonatomic) ReturnToCallViewController *returnToCallViewController; - // UIWindowLevel_CallView @property (nonatomic) UIWindow *callViewWindow; @property (nonatomic) UINavigationController *callNavigationController; @@ -190,7 +186,6 @@ const UIWindowLevel UIWindowLevel_MessageActions(void) self.rootWindow = rootWindow; self.screenBlockingWindow = screenBlockingWindow; - self.returnToCallWindow = [self createReturnToCallWindow:rootWindow]; self.callViewWindow = [self createCallViewWindow:rootWindow]; self.menuActionsWindow = [self createMenuActionsWindowWithRoowWindow:rootWindow]; @@ -209,18 +204,7 @@ const UIWindowLevel UIWindowLevel_MessageActions(void) - (void)didChangeStatusBarFrame:(NSNotification *)notification { - // Apple bug? Upon returning from landscape, this method *is* fired, but both the notification and [UIApplication - // sharedApplication].statusBarFrame still show a height of 0. So to work around we also call - // `ensureReturnToCallWindowFrame` before showing the call banner. - [self ensureReturnToCallWindowFrame]; -} - -- (void)ensureReturnToCallWindowFrame -{ - CGRect newFrame = self.returnToCallWindow.frame; - newFrame.size.height = OWSWindowManagerCallBannerHeight(); - OWSLogDebug(@"returnToCallWindowFrame: %@", NSStringFromCGRect(newFrame)); - self.returnToCallWindow.frame = newFrame; + } - (void)applicationWillResignActive:(NSNotification *)notification @@ -228,28 +212,6 @@ const UIWindowLevel UIWindowLevel_MessageActions(void) [self hideMenuActionsWindow]; } -- (UIWindow *)createReturnToCallWindow:(UIWindow *)rootWindow -{ - OWSAssertIsOnMainThread(); - OWSAssertDebug(rootWindow); - - // "Return to call" should remain at the top of the screen. - CGRect windowFrame = UIScreen.mainScreen.bounds; - windowFrame.size.height = OWSWindowManagerCallBannerHeight(); - UIWindow *window = [[UIWindow alloc] initWithFrame:windowFrame]; - window.hidden = YES; - window.windowLevel = UIWindowLevel_ReturnToCall(); - window.opaque = YES; - - ReturnToCallViewController *viewController = [ReturnToCallViewController new]; - self.returnToCallViewController = viewController; - viewController.delegate = self; - - window.rootViewController = viewController; - - return window; -} - - (UIWindow *)createMenuActionsWindowWithRoowWindow:(UIWindow *)rootWindow { UIWindow *window; @@ -318,7 +280,7 @@ const UIWindowLevel UIWindowLevel_MessageActions(void) { OWSAssertDebug(window); - return (window == self.rootWindow || window == self.returnToCallWindow || window == self.callViewWindow + return (window == self.rootWindow || window == self.callViewWindow || window == self.menuActionsWindow || window == self.screenBlockingWindow); } @@ -435,7 +397,6 @@ const UIWindowLevel UIWindowLevel_MessageActions(void) { OWSAssertIsOnMainThread(); OWSAssertDebug(self.rootWindow); - OWSAssertDebug(self.returnToCallWindow); OWSAssertDebug(self.callViewWindow); OWSAssertDebug(self.screenBlockingWindow); @@ -448,7 +409,6 @@ const UIWindowLevel UIWindowLevel_MessageActions(void) // Show Screen Block. [self ensureRootWindowHidden]; - [self ensureReturnToCallWindowHidden]; [self ensureCallViewWindowHidden]; [self ensureMessageActionsWindowHidden]; [self ensureScreenBlockWindowShown]; @@ -456,7 +416,6 @@ const UIWindowLevel UIWindowLevel_MessageActions(void) // Show Call View. [self ensureRootWindowHidden]; - [self ensureReturnToCallWindowHidden]; [self ensureCallViewWindowShown]; [self ensureMessageActionsWindowHidden]; [self ensureScreenBlockWindowHidden]; @@ -467,14 +426,6 @@ const UIWindowLevel UIWindowLevel_MessageActions(void) [self ensureCallViewWindowHidden]; [self ensureScreenBlockWindowHidden]; - if (self.callViewController) { - // Add "Return to Call" banner - - [self ensureReturnToCallWindowShown]; - } else { - [self ensureReturnToCallWindowHidden]; - } - if (self.menuActionsViewController) { // Add "Message Actions" action sheet @@ -515,33 +466,6 @@ const UIWindowLevel UIWindowLevel_MessageActions(void) self.rootWindow.hidden = YES; } -- (void)ensureReturnToCallWindowShown -{ - OWSAssertIsOnMainThread(); - - if (!self.returnToCallWindow.hidden) { - return; - } - - [self ensureReturnToCallWindowFrame]; - OWSLogInfo(@"showing 'return to call' window."); - self.returnToCallWindow.hidden = NO; - [self.returnToCallViewController startAnimating]; -} - -- (void)ensureReturnToCallWindowHidden -{ - OWSAssertIsOnMainThread(); - - if (self.returnToCallWindow.hidden) { - return; - } - - OWSLogInfo(@"hiding 'return to call' window."); - self.returnToCallWindow.hidden = YES; - [self.returnToCallViewController stopAnimating]; -} - - (void)ensureCallViewWindowShown { OWSAssertIsOnMainThread(); @@ -613,13 +537,6 @@ const UIWindowLevel UIWindowLevel_MessageActions(void) self.screenBlockingWindow.windowLevel = UIWindowLevel_Background; } -#pragma mark - ReturnToCallViewControllerDelegate - -- (void)returnToCallWasTapped:(ReturnToCallViewController *)viewController -{ - [self showCallView]; -} - #pragma mark - Fixit - (void)fixit_workAroundRotationIssue diff --git a/SignalUtilitiesKit/PlaceholderIcon.swift b/SignalUtilitiesKit/UI/PlaceholderIcon.swift similarity index 100% rename from SignalUtilitiesKit/PlaceholderIcon.swift rename to SignalUtilitiesKit/UI/PlaceholderIcon.swift diff --git a/SignalUtilitiesKit/ProfilePictureView.swift b/SignalUtilitiesKit/UI/ProfilePictureView.swift similarity index 97% rename from SignalUtilitiesKit/ProfilePictureView.swift rename to SignalUtilitiesKit/UI/ProfilePictureView.swift index 4b90a25d8..a53f34532 100644 --- a/SignalUtilitiesKit/ProfilePictureView.swift +++ b/SignalUtilitiesKit/UI/ProfilePictureView.swift @@ -68,8 +68,7 @@ public final class ProfilePictureView : UIView { self.openGroupProfilePicture = openGroupProfilePicture isRSSFeed = false hasTappableProfilePicture = true - } else if thread.groupModel.groupType == .openGroup - || thread.groupModel.groupType == .rssFeed { // An open group without a profile picture or an RSS feed + } else if thread.groupModel.groupType == .openGroup { // An open group without a profile picture or an RSS feed hexEncodedPublicKey = "" isRSSFeed = true } else { // A closed group diff --git a/SignalUtilitiesKit/ScreenLockViewController.h b/SignalUtilitiesKit/UI/ScreenLockViewController.h similarity index 100% rename from SignalUtilitiesKit/ScreenLockViewController.h rename to SignalUtilitiesKit/UI/ScreenLockViewController.h diff --git a/SignalUtilitiesKit/ScreenLockViewController.m b/SignalUtilitiesKit/UI/ScreenLockViewController.m similarity index 100% rename from SignalUtilitiesKit/ScreenLockViewController.m rename to SignalUtilitiesKit/UI/ScreenLockViewController.m diff --git a/SignalUtilitiesKit/SelectRecipientViewController.h b/SignalUtilitiesKit/UI/SelectRecipientViewController.h similarity index 100% rename from SignalUtilitiesKit/SelectRecipientViewController.h rename to SignalUtilitiesKit/UI/SelectRecipientViewController.h diff --git a/SignalUtilitiesKit/SelectRecipientViewController.m b/SignalUtilitiesKit/UI/SelectRecipientViewController.m similarity index 51% rename from SignalUtilitiesKit/SelectRecipientViewController.m rename to SignalUtilitiesKit/UI/SelectRecipientViewController.m index 4e7b96e5a..a592b7e1e 100644 --- a/SignalUtilitiesKit/SelectRecipientViewController.m +++ b/SignalUtilitiesKit/UI/SelectRecipientViewController.m @@ -3,18 +3,14 @@ // #import -#import "PhoneNumber.h" -#import "ViewControllerUtils.h" + #import -#import #import #import #import #import #import #import -#import -#import #import #import #import @@ -26,7 +22,6 @@ NSString *const kSelectRecipientViewControllerCellIdentifier = @"kSelectRecipien #pragma mark - @interface SelectRecipientViewController () @@ -54,12 +49,8 @@ NSString *const kSelectRecipientViewControllerCellIdentifier = @"kSelectRecipien self.view.backgroundColor = [Theme backgroundColor]; - _contactsViewHelper = [[ContactsViewHelper alloc] initWithDelegate:self]; - [self createViews]; - [self populateDefaultCountryNameAndCode]; - if (self.delegate.shouldHideContacts) { self.tableViewController.tableView.scrollEnabled = NO; } @@ -98,8 +89,6 @@ NSString *const kSelectRecipientViewControllerCellIdentifier = @"kSelectRecipien _tableViewController.view.backgroundColor = [Theme backgroundColor]; [self updateTableContents]; - - [self updatePhoneNumberButtonEnabling]; } - (UILabel *)countryCodeLabel @@ -209,225 +198,18 @@ NSString *const kSelectRecipientViewControllerCellIdentifier = @"kSelectRecipien return row; } -#pragma mark - Country - -- (void)populateDefaultCountryNameAndCode -{ - PhoneNumber *localNumber = [PhoneNumber phoneNumberFromE164:[TSAccountManager localNumber]]; - OWSAssertDebug(localNumber); - - NSString *countryCode; - NSNumber *callingCode; - if (localNumber) { - callingCode = [localNumber getCountryCode]; - OWSAssertDebug(callingCode); - if (callingCode) { - NSString *prefix = [NSString stringWithFormat:@"+%d", callingCode.intValue]; - countryCode = [[PhoneNumberUtil sharedThreadLocal] probableCountryCodeForCallingCode:prefix]; - } - } - - if (!countryCode || !callingCode) { - countryCode = [PhoneNumber defaultCountryCode]; - callingCode = [[PhoneNumberUtil sharedThreadLocal].nbPhoneNumberUtil getCountryCodeForRegion:countryCode]; - } - - NSString *countryName = [PhoneNumberUtil countryNameFromCountryCode:countryCode]; - - [self updateCountryWithName:countryName - callingCode:[NSString stringWithFormat:@"%@%@", COUNTRY_CODE_PREFIX, callingCode] - countryCode:countryCode]; -} - -- (void)updateCountryWithName:(NSString *)countryName - callingCode:(NSString *)callingCode - countryCode:(NSString *)countryCode -{ - _callingCode = callingCode; - - NSString *titleFormat = (CurrentAppContext().isRTL ? @"(%2$@) %1$@" : @"%1$@ (%2$@)"); - NSString *title = [NSString stringWithFormat:titleFormat, callingCode, countryCode.localizedUppercaseString]; - [self.countryCodeButton setTitle:title forState:UIControlStateNormal]; - [self.countryCodeButton layoutSubviews]; - - self.examplePhoneNumberLabel.text = - [ViewControllerUtils examplePhoneNumberForCountryCode:countryCode callingCode:callingCode]; - [self.examplePhoneNumberLabel.superview layoutSubviews]; -} - -- (void)setCallingCode:(NSString *)callingCode -{ - _callingCode = callingCode; - - [self updatePhoneNumberButtonEnabling]; -} - -#pragma mark - Actions - -- (void)showCountryCodeView:(nullable id)sender -{ - -} - -- (void)phoneNumberButtonPressed -{ - [self tryToSelectPhoneNumber]; -} - -- (void)tryToSelectPhoneNumber -{ - OWSAssertDebug(self.delegate); - - if (![self hasValidPhoneNumber]) { - OWSFailDebug(@"Invalid phone number was selected."); - return; - } - - NSString *rawPhoneNumber = [self.callingCode stringByAppendingString:self.phoneNumberTextField.text.digitsOnly]; - - NSMutableArray *possiblePhoneNumbers = [NSMutableArray new]; - for (PhoneNumber *phoneNumber in - [PhoneNumber tryParsePhoneNumbersFromsUserSpecifiedText:rawPhoneNumber - clientPhoneNumber:[TSAccountManager localNumber]]) { - [possiblePhoneNumbers addObject:phoneNumber.toE164]; - } - if ([possiblePhoneNumbers count] < 1) { - OWSFailDebug(@"Couldn't parse phone number."); - return; - } - - [self.phoneNumberTextField resignFirstResponder]; - - // There should only be one phone number, since we're explicitly specifying - // a country code and therefore parsing a number in e164 format. - OWSAssertDebug([possiblePhoneNumbers count] == 1); - - if ([self.delegate shouldValidatePhoneNumbers]) { - // Show an alert while validating the recipient. - - __weak SelectRecipientViewController *weakSelf = self; - [ModalActivityIndicatorViewController - presentFromViewController:self - canCancel:YES - backgroundBlock:^(ModalActivityIndicatorViewController *modalActivityIndicator) { - [[ContactsUpdater sharedUpdater] lookupIdentifiers:possiblePhoneNumbers - success:^(NSArray *recipients) { - OWSAssertIsOnMainThread(); - if (modalActivityIndicator.wasCancelled) { - return; - } - - if (recipients.count == 0) { - [modalActivityIndicator - dismissViewControllerAnimated:NO - completion:^{ - NSError *error - = OWSErrorMakeNoSuchSignalRecipientError(); - [OWSAlerts showErrorAlertWithMessage: - error.localizedDescription]; - }]; - return; - } - - NSString *recipientId = recipients[0].uniqueId; - [modalActivityIndicator - dismissViewControllerAnimated:NO - completion:^{ - [weakSelf.delegate phoneNumberWasSelected:recipientId]; - }]; - } - failure:^(NSError *error) { - OWSAssertIsOnMainThread(); - if (modalActivityIndicator.wasCancelled) { - return; - } - [modalActivityIndicator - dismissViewControllerAnimated:NO - completion:^{ - [OWSAlerts - showErrorAlertWithMessage:error.localizedDescription]; - }]; - }]; - }]; - } else { - NSString *recipientId = possiblePhoneNumbers[0]; - [self.delegate phoneNumberWasSelected:recipientId]; - } -} - -- (void)textFieldDidChange:(id)sender -{ - [self updatePhoneNumberButtonEnabling]; -} - -// TODO: We could also do this in registration view. -- (BOOL)hasValidPhoneNumber -{ - if (!self.callingCode) { - return NO; - } - NSString *possiblePhoneNumber = - [self.callingCode stringByAppendingString:self.phoneNumberTextField.text.digitsOnly]; - NSArray *parsePhoneNumbers = - [PhoneNumber tryParsePhoneNumbersFromsUserSpecifiedText:possiblePhoneNumber - clientPhoneNumber:[TSAccountManager localNumber]]; - if (parsePhoneNumbers.count < 1) { - return NO; - } - PhoneNumber *parsedPhoneNumber = parsePhoneNumbers[0]; - // It'd be nice to use [PhoneNumber isValid] but it always returns false for some countries - // (like afghanistan) and there doesn't seem to be a good way to determine beforehand - // which countries it can validate for without forking libPhoneNumber. - return parsedPhoneNumber.toE164.length > 1; -} - -- (void)updatePhoneNumberButtonEnabling -{ - BOOL isEnabled = [self hasValidPhoneNumber]; - self.phoneNumberButton.enabled = isEnabled; - [self.phoneNumberButton - setBackgroundColorsWithUpColor:(isEnabled ? [UIColor ows_signalBrandBlueColor] : [Theme secondaryColor])]; -} - -#pragma mark - CountryCodeViewControllerDelegate - -//- (void)countryCodeViewController:(CountryCodeViewController *)vc -// didSelectCountryCode:(NSString *)countryCode -// countryName:(NSString *)countryName -// callingCode:(NSString *)callingCode -//{ -// OWSAssertDebug(countryCode.length > 0); -// OWSAssertDebug(countryName.length > 0); -// OWSAssertDebug(callingCode.length > 0); -// -// [self updateCountryWithName:countryName callingCode:callingCode countryCode:countryCode]; -// -// // Trigger the formatting logic with a no-op edit. -// [self textField:self.phoneNumberTextField shouldChangeCharactersInRange:NSMakeRange(0, 0) replacementString:@""]; -//} - #pragma mark - UITextFieldDelegate - (BOOL)textField:(UITextField *)textField shouldChangeCharactersInRange:(NSRange)range replacementString:(NSString *)insertionText { - [ViewControllerUtils phoneNumberTextField:textField - shouldChangeCharactersInRange:range - replacementString:insertionText - callingCode:_callingCode]; - - [self updatePhoneNumberButtonEnabling]; - return NO; // inform our caller that we took care of performing the change } - (BOOL)textFieldShouldReturn:(UITextField *)textField { [textField resignFirstResponder]; - if ([self hasValidPhoneNumber]) { - [self tryToSelectPhoneNumber]; - } return NO; } @@ -511,55 +293,6 @@ NSString *const kSelectRecipientViewControllerCellIdentifier = @"kSelectRecipien actionBlock:nil]]; [contents addSection:phoneNumberSection]; - if (![self.delegate shouldHideContacts]) { - OWSTableSection *contactsSection = [OWSTableSection new]; - contactsSection.headerTitle = [self.delegate contactsSectionTitle]; - NSArray *signalAccounts = helper.signalAccounts; - if (signalAccounts.count == 0) { - // No Contacts - [contactsSection - addItem:[OWSTableItem softCenterLabelItemWithText: - NSLocalizedString(@"SETTINGS_BLOCK_LIST_NO_CONTACTS", - @"A label that indicates the user has no Signal contacts.")]]; - } else { - // Contacts - - for (SignalAccount *signalAccount in signalAccounts) { - [contactsSection - addItem:[OWSTableItem - itemWithCustomCellBlock:^{ - SelectRecipientViewController *strongSelf = weakSelf; - OWSCAssertDebug(strongSelf); - - ContactTableViewCell *cell = [ContactTableViewCell new]; - BOOL isBlocked = [helper isRecipientIdBlocked:signalAccount.recipientId]; - if (isBlocked) { - cell.accessoryMessage = NSLocalizedString(@"CONTACT_CELL_IS_BLOCKED", - @"An indicator that a contact has been blocked."); - } else { - cell.accessoryMessage = - [weakSelf.delegate accessoryMessageForSignalAccount:signalAccount]; - } - [cell configureWithRecipientId:signalAccount.recipientId]; - - if (![weakSelf.delegate canSignalAccountBeSelected:signalAccount]) { - cell.selectionStyle = UITableViewCellSelectionStyleNone; - } - - return cell; - } - customRowHeight:UITableViewAutomaticDimension - actionBlock:^{ - if (![weakSelf.delegate canSignalAccountBeSelected:signalAccount]) { - return; - } - [weakSelf.delegate signalAccountWasSelected:signalAccount]; - }]]; - } - } - [contents addSection:contactsSection]; - } - self.tableViewController.contents = contents; } @@ -572,9 +305,7 @@ NSString *const kSelectRecipientViewControllerCellIdentifier = @"kSelectRecipien - (void)countryRowTouched:(UIGestureRecognizer *)sender { - if (sender.state == UIGestureRecognizerStateRecognized) { - [self showCountryCodeView:nil]; - } + } #pragma mark - OWSTableViewControllerDelegate diff --git a/SignalUtilitiesKit/SelectThreadViewController.h b/SignalUtilitiesKit/UI/SelectThreadViewController.h similarity index 100% rename from SignalUtilitiesKit/SelectThreadViewController.h rename to SignalUtilitiesKit/UI/SelectThreadViewController.h diff --git a/SignalUtilitiesKit/SelectThreadViewController.m b/SignalUtilitiesKit/UI/SelectThreadViewController.m similarity index 67% rename from SignalUtilitiesKit/SelectThreadViewController.m rename to SignalUtilitiesKit/UI/SelectThreadViewController.m index 149045246..c697301cf 100644 --- a/SignalUtilitiesKit/SelectThreadViewController.m +++ b/SignalUtilitiesKit/UI/SelectThreadViewController.m @@ -5,9 +5,6 @@ #import "SelectThreadViewController.h" #import "BlockListUIUtils.h" #import "ContactTableViewCell.h" -#import "ContactsViewHelper.h" -#import "NewNonContactConversationViewController.h" -#import "OWSContactsManager.h" #import "OWSSearchBar.h" #import "OWSTableViewController.h" #import "ThreadViewHelper.h" @@ -16,7 +13,6 @@ #import "UIView+OWS.h" #import #import -#import #import #import #import @@ -28,9 +24,7 @@ NS_ASSUME_NONNULL_BEGIN @interface SelectThreadViewController () + UISearchBarDelegate> @property (nonatomic, readonly) ContactsViewHelper *contactsViewHelper; @property (nonatomic, readonly) FullTextSearcher *fullTextSearcher; @@ -55,7 +49,6 @@ NS_ASSUME_NONNULL_BEGIN closeButton.tintColor = LKColors.text; self.navigationItem.leftBarButtonItem = closeButton; - _contactsViewHelper = [[ContactsViewHelper alloc] initWithDelegate:self]; _fullTextSearcher = FullTextSearcher.shared; _threadViewHelper = [ThreadViewHelper new]; _threadViewHelper.delegate = self; @@ -100,25 +93,6 @@ NS_ASSUME_NONNULL_BEGIN { OWSAssertDebug(self.selectThreadViewDelegate); - // Search - /* - UISearchBar *searchBar = [OWSSearchBar new]; - _searchBar = searchBar; - searchBar.delegate = self; - searchBar.placeholder = NSLocalizedString(@"Search by public key", @""); - [searchBar sizeToFit]; - - UIView *header = [self.selectThreadViewDelegate createHeaderWithSearchBar:searchBar]; - if (!header) { - header = searchBar; - } - [self.view addSubview:header]; - [header autoPinWidthToSuperview]; - [header autoPinEdge:ALEdgeTop toEdge:ALEdgeTop ofView:self.view withOffset:0.0f]; - [header setCompressionResistanceVerticalHigh]; - [header setContentHuggingVerticalHigh]; - */ - // Table _tableViewController = [OWSTableViewController new]; _tableViewController.delegate = self; @@ -187,27 +161,10 @@ NS_ASSUME_NONNULL_BEGIN ContactsViewHelper *helper = self.contactsViewHelper; OWSTableContents *contents = [OWSTableContents new]; - /* - OWSTableSection *findByPhoneSection = [OWSTableSection new]; - [findByPhoneSection - addItem:[OWSTableItem disclosureItemWithText:NSLocalizedString(@"NEW_CONVERSATION_FIND_BY_PHONE_NUMBER", - @"A label the cell that lets you add a new member to a group.") - customRowHeight:UITableViewAutomaticDimension - actionBlock:^{ - NewNonContactConversationViewController *viewController = - [NewNonContactConversationViewController new]; - viewController.nonContactConversationDelegate = weakSelf; - viewController.isPresentedInNavigationController = YES; - [weakSelf.navigationController pushViewController:viewController - animated:YES]; - }]]; - [contents addSection:findByPhoneSection]; - */ - // Existing threads are listed first, ordered by most recently active OWSTableSection *recentChatsSection = [OWSTableSection new]; recentChatsSection.headerTitle = NSLocalizedString(@"SELECT_THREAD_TABLE_RECENT_CHATS_TITLE", @""); - for (TSThread *thread in [self filteredThreadsWithSearchText]) { + for (TSThread *thread in self.threadViewHelper.threads) { [recentChatsSection addItem:[OWSTableItem itemWithCustomCellBlock:^{ @@ -218,7 +175,7 @@ NS_ASSUME_NONNULL_BEGIN // instead of HomeViewCell to present contacts and threads. ContactTableViewCell *cell = [ContactTableViewCell new]; - BOOL isBlocked = [helper isThreadBlocked:thread]; + BOOL isBlocked = [SSKEnvironment.shared.blockingManager isThreadBlocked:thread]; if (isBlocked) { cell.accessoryMessage = NSLocalizedString( @"CONTACT_CELL_IS_BLOCKED", @"An indicator that a contact has been blocked."); @@ -257,13 +214,12 @@ NS_ASSUME_NONNULL_BEGIN return; } - BOOL isBlocked = [helper isThreadBlocked:thread]; + BOOL isBlocked = [SSKEnvironment.shared.blockingManager isThreadBlocked:thread]; if (isBlocked && ![strongSelf.selectThreadViewDelegate canSelectBlockedContact]) { [BlockListUIUtils showUnblockThreadActionSheet:thread fromViewController:strongSelf - blockingManager:helper.blockingManager - contactsManager:helper.contactsManager + blockingManager:SSKEnvironment.shared.blockingManager completionBlock:^(BOOL isStillBlocked) { if (!isStillBlocked) { [strongSelf.selectThreadViewDelegate threadWasSelected:thread]; @@ -280,37 +236,7 @@ NS_ASSUME_NONNULL_BEGIN [contents addSection:recentChatsSection]; } - // Contacts who don't yet have a thread are listed last - OWSTableSection *otherContactsSection = [OWSTableSection new]; - otherContactsSection.headerTitle = @"Other Chats"; - NSArray *filteredSignalAccounts = [self filteredSignalAccountsWithSearchText]; - for (SignalAccount *signalAccount in filteredSignalAccounts) { - [otherContactsSection - addItem:[OWSTableItem - itemWithCustomCellBlock:^{ - SelectThreadViewController *strongSelf = weakSelf; - OWSCAssertDebug(strongSelf); - - ContactTableViewCell *cell = [ContactTableViewCell new]; - BOOL isBlocked = [helper isRecipientIdBlocked:signalAccount.recipientId]; - if (isBlocked) { - cell.accessoryMessage = NSLocalizedString( - @"CONTACT_CELL_IS_BLOCKED", @"An indicator that a contact has been blocked."); - } - [cell configureWithRecipientId:signalAccount.recipientId]; - return cell; - } - customRowHeight:UITableViewAutomaticDimension - actionBlock:^{ - [weakSelf signalAccountWasSelected:signalAccount]; - }]]; - } - - if (otherContactsSection.itemCount > 0) { - [contents addSection:otherContactsSection]; - } - - if (recentChatsSection.itemCount + otherContactsSection.itemCount < 1) { + if (recentChatsSection.itemCount < 1) { OWSTableSection *emptySection = [OWSTableSection new]; [emptySection addItem:[OWSTableItem @@ -327,16 +253,13 @@ NS_ASSUME_NONNULL_BEGIN OWSAssertDebug(signalAccount); OWSAssertDebug(self.selectThreadViewDelegate); - ContactsViewHelper *helper = self.contactsViewHelper; - - if ([helper isRecipientIdBlocked:signalAccount.recipientId] + if ([SSKEnvironment.shared.blockingManager isRecipientIdBlocked:signalAccount.recipientId] && ![self.selectThreadViewDelegate canSelectBlockedContact]) { __weak SelectThreadViewController *weakSelf = self; [BlockListUIUtils showUnblockSignalAccountActionSheet:signalAccount fromViewController:self - blockingManager:helper.blockingManager - contactsManager:helper.contactsManager + blockingManager:SSKEnvironment.shared.blockingManager completionBlock:^(BOOL isBlocked) { if (!isBlocked) { [weakSelf signalAccountWasSelected:signalAccount]; @@ -354,37 +277,6 @@ NS_ASSUME_NONNULL_BEGIN [self.selectThreadViewDelegate threadWasSelected:thread]; } -#pragma mark - Filter - -- (NSArray *)filteredThreadsWithSearchText -{ - return self.threadViewHelper.threads; -} - -- (NSArray *)filteredSignalAccountsWithSearchText -{ - // We don't want to show a 1:1 thread with Alice and Alice's contact, - // so we de-duplicate by recipientId. - NSArray *threads = self.threadViewHelper.threads; - NSMutableSet *contactIdsToIgnore = [NSMutableSet new]; - for (TSThread *thread in threads) { - if ([thread isKindOfClass:[TSContactThread class]]) { - TSContactThread *contactThread = (TSContactThread *)thread; - [contactIdsToIgnore addObject:contactThread.contactIdentifier]; - } - } - - NSString *searchString = self.searchBar.text; - NSArray *matchingAccounts = - [self.contactsViewHelper signalAccountsMatchingSearchString:searchString]; - - return [matchingAccounts - filteredArrayUsingPredicate:[NSPredicate predicateWithBlock:^BOOL(SignalAccount *signalAccount, - NSDictionary *_Nullable bindings) { - return ![contactIdsToIgnore containsObject:signalAccount.recipientId]; - }]]; -} - #pragma mark - Events - (void)dismissPressed:(id)sender @@ -419,14 +311,6 @@ NS_ASSUME_NONNULL_BEGIN return NO; } -#pragma mark - NewNonContactConversationViewControllerDelegate - -- (void)recipientIdWasSelected:(NSString *)recipientId -{ - SignalAccount *signalAccount = [self.contactsViewHelper fetchOrBuildSignalAccountForRecipientId:recipientId]; - [self signalAccountWasSelected:signalAccount]; -} - @end NS_ASSUME_NONNULL_END diff --git a/SignalUtilitiesKit/SharingThreadPickerViewController.h b/SignalUtilitiesKit/UI/SharingThreadPickerViewController.h similarity index 100% rename from SignalUtilitiesKit/SharingThreadPickerViewController.h rename to SignalUtilitiesKit/UI/SharingThreadPickerViewController.h diff --git a/SignalUtilitiesKit/SharingThreadPickerViewController.m b/SignalUtilitiesKit/UI/SharingThreadPickerViewController.m similarity index 65% rename from SignalUtilitiesKit/SharingThreadPickerViewController.m rename to SignalUtilitiesKit/UI/SharingThreadPickerViewController.m index 9a85d0f96..50e9c85ae 100644 --- a/SignalUtilitiesKit/SharingThreadPickerViewController.m +++ b/SignalUtilitiesKit/UI/SharingThreadPickerViewController.m @@ -20,10 +20,8 @@ typedef void (^SendMessageBlock)(SendCompletionBlock completion); @interface SharingThreadPickerViewController () + MessageApprovalViewControllerDelegate> -@property (nonatomic, readonly) OWSContactsManager *contactsManager; @property (nonatomic, readonly) OWSMessageSender *messageSender; @property (nonatomic) TSThread *thread; @property (nonatomic, readonly, weak) id shareViewDelegate; @@ -67,9 +65,6 @@ typedef void (^SendMessageBlock)(SendCompletionBlock completion); { [super loadView]; - _contactsManager = Environment.shared.contactsManager; - _messageSender = SSKEnvironment.shared.messageSender; - _progressView = [[UIProgressView alloc] initWithProgressViewStyle:UIProgressViewStyleDefault]; self.title = NSLocalizedString(@"SHARE_EXTENSION_VIEW_TITLE", @"Title for the 'share extension' view."); } @@ -173,10 +168,6 @@ typedef void (^SendMessageBlock)(SendCompletionBlock completion); return; } - if ([self tryToShareAsContactShare]) { - return; - } - OWSNavigationController *approvalModal = [AttachmentApprovalViewController wrappedInNavControllerWithAttachments:self.attachments approvalDelegate:self]; [self presentViewController:approvalModal animated:YES completion:nil]; @@ -194,69 +185,12 @@ typedef void (^SendMessageBlock)(SendCompletionBlock completion); MessageApprovalViewController *approvalVC = [[MessageApprovalViewController alloc] initWithMessageText:messageText thread:self.thread - contactsManager:self.contactsManager delegate:self]; [self.navigationController pushViewController:approvalVC animated:YES]; return YES; } -- (BOOL)tryToShareAsContactShare -{ - OWSAssertDebug(self.attachments.count > 0); - - if (self.attachments.count > 1) { - return NO; - } - OWSAssertDebug(self.attachments.count == 1); - SignalAttachment *attachment = self.attachments.firstObject; - if (!attachment.isConvertibleToContactShare) { - return NO; - } - - [self showContactShareApproval:attachment]; - return YES; -} - -- (void)showContactShareApproval:(SignalAttachment *)attachment -{ - OWSAssertDebug(attachment); - OWSAssertDebug(self.thread); - OWSAssertDebug(attachment.isConvertibleToContactShare); - - NSData *data = attachment.data; - - CNContact *_Nullable cnContact = [Contact cnContactWithVCardData:data]; - Contact *_Nullable contact = [[Contact alloc] initWithSystemContact:cnContact]; - OWSContact *_Nullable contactShareRecord = [OWSContacts contactForSystemContact:cnContact]; - if (!contactShareRecord) { - OWSLogError(@"Could not convert system contact."); - return; - } - - BOOL isProfileAvatar = NO; - NSData *_Nullable avatarImageData = [self.contactsManager avatarDataForCNContactId:contact.cnContactId]; - for (NSString *recipientId in contact.textSecureIdentifiers) { - if (avatarImageData) { - break; - } - avatarImageData = [self.contactsManager profileImageDataForPhoneIdentifier:recipientId]; - if (avatarImageData) { - isProfileAvatar = YES; - } - } - contactShareRecord.isProfileAvatar = isProfileAvatar; - - ContactShareViewModel *contactShare = - [[ContactShareViewModel alloc] initWithContactShareRecord:contactShareRecord avatarImageData:avatarImageData]; - - ContactShareApprovalViewController *approvalVC = - [[ContactShareApprovalViewController alloc] initWithContactShare:contactShare - contactsManager:self.contactsManager - delegate:self]; - [self.navigationController pushViewController:approvalVC animated:YES]; -} - // override - (void)dismissPressed:(id)sender { @@ -281,7 +215,6 @@ typedef void (^SendMessageBlock)(SendCompletionBlock completion); didApproveAttachments:(NSArray *_Nonnull)attachments messageText:(NSString *_Nullable)messageText { - [ThreadUtil addThreadToProfileWhitelistIfEmptyContactThread:self.thread]; [self tryToSendMessageWithBlock:^(SendCompletionBlock sendCompletion) { OWSAssertIsOnMainThread(); @@ -293,15 +226,18 @@ typedef void (^SendMessageBlock)(SendCompletionBlock completion); // TODO ALBUMS - send album via SAE [self.dbReadConnection readWithBlock:^(YapDatabaseReadTransaction *_Nonnull transaction) { - outgoingMessage = [ThreadUtil sendMessageNonDurablyWithText:messageText - mediaAttachments:attachments - inThread:self.thread - quotedReplyModel:nil - transaction:transaction - messageSender:self.messageSender - completion:^(NSError *_Nullable error) { - sendCompletion(error, outgoingMessage); - }]; + + // TODO TODO TODO + +// outgoingMessage = [ThreadUtil sendMessageNonDurablyWithText:messageText +// mediaAttachments:attachments +// inThread:self.thread +// quotedReplyModel:nil +// transaction:transaction +// messageSender:self.messageSender +// completion:^(NSError *_Nullable error) { +// sendCompletion(error, outgoingMessage); +// }]; }]; // This is necessary to show progress. @@ -328,7 +264,6 @@ typedef void (^SendMessageBlock)(SendCompletionBlock completion); { OWSAssertDebug(messageText.length > 0); - [ThreadUtil addThreadToProfileWhitelistIfEmptyContactThread:self.thread]; [self tryToSendMessageWithBlock:^(SendCompletionBlock sendCompletion) { OWSAssertIsOnMainThread(); @@ -337,18 +272,21 @@ typedef void (^SendMessageBlock)(SendCompletionBlock completion); // the sending operation. Alternatively, we could use a durable send, but do more to make sure the // SAE runs as long as it needs. [self.dbReadConnection readWithBlock:^(YapDatabaseReadTransaction *_Nonnull transaction) { - outgoingMessage = [ThreadUtil sendMessageNonDurablyWithText:messageText - inThread:self.thread - quotedReplyModel:nil - transaction:transaction - messageSender:self.messageSender - completion:^(NSError *_Nullable error) { - if (error) { - sendCompletion(error, outgoingMessage); - } else { - sendCompletion(nil, outgoingMessage); - } - }]; + + // TODO TODO TODO +// +// outgoingMessage = [ThreadUtil sendMessageNonDurablyWithText:messageText +// inThread:self.thread +// quotedReplyModel:nil +// transaction:transaction +// messageSender:self.messageSender +// completion:^(NSError *_Nullable error) { +// if (error) { +// sendCompletion(error, outgoingMessage); +// } else { +// sendCompletion(nil, outgoingMessage); +// } +// }]; // This is necessary to show progress. self.outgoingMessage = outgoingMessage; }]; @@ -361,49 +299,6 @@ typedef void (^SendMessageBlock)(SendCompletionBlock completion); [self cancelShareExperience]; } -#pragma mark - ContactShareApprovalViewControllerDelegate - -- (void)approveContactShare:(ContactShareApprovalViewController *)approvalViewController - didApproveContactShare:(ContactShareViewModel *)contactShare -{ - OWSLogInfo(@""); - - [ThreadUtil addThreadToProfileWhitelistIfEmptyContactThread:self.thread]; - [self tryToSendMessageWithBlock:^(SendCompletionBlock sendCompletion) { - OWSAssertIsOnMainThread(); - // TODO - in line with QuotedReply and other message attachments, saving should happen as part of sending - // preparation rather than duplicated here and in the SAE - - [LKStorage writeWithBlock:^(YapDatabaseReadWriteTransaction *_Nonnull transaction) { - if (contactShare.avatarImage) { - [contactShare.dbRecord saveAvatarImage:contactShare.avatarImage transaction:transaction]; - } - } - completion:^{ - __block TSOutgoingMessage *outgoingMessage = nil; - outgoingMessage = [ThreadUtil sendMessageNonDurablyWithContactShare:contactShare.dbRecord - inThread:self.thread - messageSender:self.messageSender - completion:^(NSError *_Nullable error) { - sendCompletion(error, outgoingMessage); - }]; - // This is necessary to show progress. - self.outgoingMessage = outgoingMessage; - }]; - - - } - fromViewController:approvalViewController]; -} - -- (void)approveContactShare:(ContactShareApprovalViewController *)approvalViewController - didCancelContactShare:(ContactShareViewModel *)contactShare -{ - OWSLogInfo(@""); - - [self cancelShareExperience]; -} - #pragma mark - Helpers - (void)tryToSendMessageWithBlock:(SendMessageBlock)sendMessageBlock @@ -475,7 +370,7 @@ typedef void (^SendMessageBlock)(SendCompletionBlock completion); NSString *failureFormat = NSLocalizedString(@"SHARE_EXTENSION_FAILED_SENDING_BECAUSE_UNTRUSTED_IDENTITY_FORMAT", @"alert body when sharing file failed because of untrusted/changed identity keys"); - NSString *displayName = [self.contactsManager displayNameForPhoneIdentifier:untrustedRecipientId]; + NSString *displayName = [SSKEnvironment.shared.profileManager profileNameForRecipientWithID:untrustedRecipientId avoidingWriteTransaction:YES]; NSString *failureMessage = [NSString stringWithFormat:failureFormat, displayName]; UIAlertController *failureAlert = [UIAlertController alertControllerWithTitle:failureTitle @@ -544,41 +439,9 @@ typedef void (^SendMessageBlock)(SendCompletionBlock completion); OWSLogDebug(@"Confirming identity for recipient: %@", recipientId); - [LKStorage writeWithBlock:^(YapDatabaseReadWriteTransaction *transaction) { - OWSVerificationState verificationState = - [[OWSIdentityManager sharedManager] verificationStateForRecipientId:recipientId transaction:transaction]; - switch (verificationState) { - case OWSVerificationStateVerified: { - OWSFailDebug(@"Shouldn't need to confirm identity if it was already verified"); - break; - } - case OWSVerificationStateDefault: { - // If we learned of a changed SN during send, then we've already recorded the new identity - // and there's nothing else we need to do for the resend to succeed. - // We don't want to redundantly set status to "default" because we would create a - // "You marked Alice as unverified" notice, which wouldn't make sense if Alice was never - // marked as "Verified". - OWSLogInfo(@"recipient has acceptable verification status. Next send will succeed."); - break; - } - case OWSVerificationStateNoLongerVerified: { - OWSLogInfo(@"marked recipient: %@ as default verification status.", recipientId); - NSData *identityKey = [[OWSIdentityManager sharedManager] identityKeyForRecipientId:recipientId - protocolContext:transaction]; - OWSAssertDebug(identityKey); - [[OWSIdentityManager sharedManager] setVerificationState:OWSVerificationStateDefault - identityKey:identityKey - recipientId:recipientId - isUserInitiatedChange:YES - transaction:transaction]; - break; - } - } - - dispatch_async(dispatch_get_main_queue(), ^(void) { - [self resendMessage:message fromViewController:fromViewController]; - }); - }]; + dispatch_async(dispatch_get_main_queue(), ^(void) { + [self resendMessage:message fromViewController:fromViewController]; + }); } - (void)resendMessage:(TSOutgoingMessage *)message fromViewController:(UIViewController *)fromViewController @@ -602,25 +465,28 @@ typedef void (^SendMessageBlock)(SendCompletionBlock completion); [fromViewController presentAlert:progressAlert completion:^{ - [self.messageSender sendMessage:message - success:^{ - OWSLogInfo(@"Resending attachment succeeded."); - dispatch_async(dispatch_get_main_queue(), ^{ - [self.shareViewDelegate shareViewWasCompleted]; - }); - } - failure:^(NSError *error) { - dispatch_async(dispatch_get_main_queue(), ^{ - [fromViewController - dismissViewControllerAnimated:YES - completion:^{ - OWSLogInfo(@"Sending attachment failed with error: %@", error); - [self showSendFailureAlertWithError:error - message:message - fromViewController:fromViewController]; - }]; - }); - }]; + + // TODO TODO TODO + +// [self.messageSender sendMessage:message +// success:^{ +// OWSLogInfo(@"Resending attachment succeeded."); +// dispatch_async(dispatch_get_main_queue(), ^{ +// [self.shareViewDelegate shareViewWasCompleted]; +// }); +// } +// failure:^(NSError *error) { +// dispatch_async(dispatch_get_main_queue(), ^{ +// [fromViewController +// dismissViewControllerAnimated:YES +// completion:^{ +// OWSLogInfo(@"Sending attachment failed with error: %@", error); +// [self showSendFailureAlertWithError:error +// message:message +// fromViewController:fromViewController]; +// }]; +// }); +// }]; }]; } diff --git a/SignalUtilitiesKit/SheetViewController.swift b/SignalUtilitiesKit/UI/SheetViewController.swift similarity index 100% rename from SignalUtilitiesKit/SheetViewController.swift rename to SignalUtilitiesKit/UI/SheetViewController.swift diff --git a/SignalUtilitiesKit/TappableStackView.swift b/SignalUtilitiesKit/UI/TappableStackView.swift similarity index 100% rename from SignalUtilitiesKit/TappableStackView.swift rename to SignalUtilitiesKit/UI/TappableStackView.swift diff --git a/SignalUtilitiesKit/TappableView.swift b/SignalUtilitiesKit/UI/TappableView.swift similarity index 100% rename from SignalUtilitiesKit/TappableView.swift rename to SignalUtilitiesKit/UI/TappableView.swift diff --git a/SignalUtilitiesKit/Theme.h b/SignalUtilitiesKit/UI/Theme.h similarity index 100% rename from SignalUtilitiesKit/Theme.h rename to SignalUtilitiesKit/UI/Theme.h diff --git a/SignalUtilitiesKit/Theme.m b/SignalUtilitiesKit/UI/Theme.m similarity index 100% rename from SignalUtilitiesKit/Theme.m rename to SignalUtilitiesKit/UI/Theme.m diff --git a/SignalUtilitiesKit/Toast.swift b/SignalUtilitiesKit/UI/Toast.swift similarity index 100% rename from SignalUtilitiesKit/Toast.swift rename to SignalUtilitiesKit/UI/Toast.swift diff --git a/SignalUtilitiesKit/UIColor+OWS.h b/SignalUtilitiesKit/UI/UIColor+OWS.h similarity index 100% rename from SignalUtilitiesKit/UIColor+OWS.h rename to SignalUtilitiesKit/UI/UIColor+OWS.h diff --git a/SignalUtilitiesKit/UIColor+OWS.m b/SignalUtilitiesKit/UI/UIColor+OWS.m similarity index 100% rename from SignalUtilitiesKit/UIColor+OWS.m rename to SignalUtilitiesKit/UI/UIColor+OWS.m diff --git a/SignalUtilitiesKit/UIFont+OWS.h b/SignalUtilitiesKit/UI/UIFont+OWS.h similarity index 100% rename from SignalUtilitiesKit/UIFont+OWS.h rename to SignalUtilitiesKit/UI/UIFont+OWS.h diff --git a/SignalUtilitiesKit/UIFont+OWS.m b/SignalUtilitiesKit/UI/UIFont+OWS.m similarity index 100% rename from SignalUtilitiesKit/UIFont+OWS.m rename to SignalUtilitiesKit/UI/UIFont+OWS.m diff --git a/SignalUtilitiesKit/UIGestureRecognizer+OWS.swift b/SignalUtilitiesKit/UI/UIGestureRecognizer+OWS.swift similarity index 100% rename from SignalUtilitiesKit/UIGestureRecognizer+OWS.swift rename to SignalUtilitiesKit/UI/UIGestureRecognizer+OWS.swift diff --git a/SignalUtilitiesKit/UIImage+OWS.swift b/SignalUtilitiesKit/UI/UIImage+OWS.swift similarity index 100% rename from SignalUtilitiesKit/UIImage+OWS.swift rename to SignalUtilitiesKit/UI/UIImage+OWS.swift diff --git a/SignalUtilitiesKit/UIUtil.h b/SignalUtilitiesKit/UI/UIUtil.h similarity index 100% rename from SignalUtilitiesKit/UIUtil.h rename to SignalUtilitiesKit/UI/UIUtil.h diff --git a/SignalUtilitiesKit/UIUtil.m b/SignalUtilitiesKit/UI/UIUtil.m similarity index 100% rename from SignalUtilitiesKit/UIUtil.m rename to SignalUtilitiesKit/UI/UIUtil.m diff --git a/SignalUtilitiesKit/UIView+OWS.h b/SignalUtilitiesKit/UI/UIView+OWS.h similarity index 100% rename from SignalUtilitiesKit/UIView+OWS.h rename to SignalUtilitiesKit/UI/UIView+OWS.h diff --git a/SignalUtilitiesKit/UIView+OWS.m b/SignalUtilitiesKit/UI/UIView+OWS.m similarity index 100% rename from SignalUtilitiesKit/UIView+OWS.m rename to SignalUtilitiesKit/UI/UIView+OWS.m diff --git a/SignalUtilitiesKit/UIView+OWS.swift b/SignalUtilitiesKit/UI/UIView+OWS.swift similarity index 100% rename from SignalUtilitiesKit/UIView+OWS.swift rename to SignalUtilitiesKit/UI/UIView+OWS.swift diff --git a/SignalUtilitiesKit/UIView+Utilities.swift b/SignalUtilitiesKit/UI/UIView+Utilities.swift similarity index 100% rename from SignalUtilitiesKit/UIView+Utilities.swift rename to SignalUtilitiesKit/UI/UIView+Utilities.swift diff --git a/SignalUtilitiesKit/UIViewController+OWS.h b/SignalUtilitiesKit/UI/UIViewController+OWS.h similarity index 100% rename from SignalUtilitiesKit/UIViewController+OWS.h rename to SignalUtilitiesKit/UI/UIViewController+OWS.h diff --git a/SignalUtilitiesKit/UIViewController+OWS.m b/SignalUtilitiesKit/UI/UIViewController+OWS.m similarity index 100% rename from SignalUtilitiesKit/UIViewController+OWS.m rename to SignalUtilitiesKit/UI/UIViewController+OWS.m diff --git a/SignalUtilitiesKit/UIViewController+Utilities.swift b/SignalUtilitiesKit/UI/UIViewController+Utilities.swift similarity index 100% rename from SignalUtilitiesKit/UIViewController+Utilities.swift rename to SignalUtilitiesKit/UI/UIViewController+Utilities.swift diff --git a/SignalUtilitiesKit/VideoPlayerView.swift b/SignalUtilitiesKit/UI/VideoPlayerView.swift similarity index 100% rename from SignalUtilitiesKit/VideoPlayerView.swift rename to SignalUtilitiesKit/UI/VideoPlayerView.swift diff --git a/SignalUtilitiesKit/AttachmentSharing.h b/SignalUtilitiesKit/Utilities/AttachmentSharing.h similarity index 100% rename from SignalUtilitiesKit/AttachmentSharing.h rename to SignalUtilitiesKit/Utilities/AttachmentSharing.h diff --git a/SignalUtilitiesKit/AttachmentSharing.m b/SignalUtilitiesKit/Utilities/AttachmentSharing.m similarity index 91% rename from SignalUtilitiesKit/AttachmentSharing.m rename to SignalUtilitiesKit/Utilities/AttachmentSharing.m index 264cdcd74..d1b883d9c 100644 --- a/SignalUtilitiesKit/AttachmentSharing.m +++ b/SignalUtilitiesKit/Utilities/AttachmentSharing.m @@ -4,7 +4,6 @@ #import "AttachmentSharing.h" #import "UIUtil.h" - #import #import @@ -41,9 +40,7 @@ NS_ASSUME_NONNULL_BEGIN { OWSAssertDebug(url); - [AttachmentSharing showShareUIForActivityItems:@[ - url, - ] + [AttachmentSharing showShareUIForActivityItems:@[ url ] completion:completion]; } @@ -64,9 +61,7 @@ NS_ASSUME_NONNULL_BEGIN { OWSAssertDebug(text); - [AttachmentSharing showShareUIForActivityItems:@[ - text, - ] + [AttachmentSharing showShareUIForActivityItems:@[ text, ] completion:completion]; } @@ -75,9 +70,7 @@ NS_ASSUME_NONNULL_BEGIN { OWSAssertDebug(image); - [AttachmentSharing showShareUIForActivityItems:@[ - image, - ] + [AttachmentSharing showShareUIForActivityItems:@[ image, ] completion:nil]; } #endif diff --git a/SignalUtilitiesKit/Bench.swift b/SignalUtilitiesKit/Utilities/Bench.swift similarity index 100% rename from SignalUtilitiesKit/Bench.swift rename to SignalUtilitiesKit/Utilities/Bench.swift diff --git a/SignalUtilitiesKit/BuildConfiguration.swift b/SignalUtilitiesKit/Utilities/BuildConfiguration.swift similarity index 100% rename from SignalUtilitiesKit/BuildConfiguration.swift rename to SignalUtilitiesKit/Utilities/BuildConfiguration.swift diff --git a/SignalUtilitiesKit/ByteParser.h b/SignalUtilitiesKit/Utilities/ByteParser.h similarity index 100% rename from SignalUtilitiesKit/ByteParser.h rename to SignalUtilitiesKit/Utilities/ByteParser.h diff --git a/SignalUtilitiesKit/ByteParser.m b/SignalUtilitiesKit/Utilities/ByteParser.m similarity index 100% rename from SignalUtilitiesKit/ByteParser.m rename to SignalUtilitiesKit/Utilities/ByteParser.m diff --git a/SignalUtilitiesKit/Collection+OWS.swift b/SignalUtilitiesKit/Utilities/Collection+OWS.swift similarity index 90% rename from SignalUtilitiesKit/Collection+OWS.swift rename to SignalUtilitiesKit/Utilities/Collection+OWS.swift index 5f1f1c8a8..c2e7959cb 100644 --- a/SignalUtilitiesKit/Collection+OWS.swift +++ b/SignalUtilitiesKit/Utilities/Collection+OWS.swift @@ -5,12 +5,13 @@ public extension Collection { /// Returns the element at the specified index iff it is within bounds, otherwise nil. - public subscript (safe index: Index) -> Element? { + subscript (safe index: Index) -> Element? { return indices.contains(index) ? self[index] : nil } } public extension Array { + func chunked(by chunkSize: Int) -> [[Element]] { return stride(from: 0, to: self.count, by: chunkSize).map { Array(self[$0.. Bool { + func hasMatch(input: String) -> Bool { return self.firstMatch(in: input, options: [], range: NSRange(location: 0, length: input.utf16.count)) != nil } @objc - public class func parseFirstMatch(pattern: String, + class func parseFirstMatch(pattern: String, text: String, options: NSRegularExpression.Options = []) -> String? { do { @@ -37,7 +37,7 @@ public extension NSRegularExpression { } @objc - public func parseFirstMatch(inText text: String, + func parseFirstMatch(inText text: String, options: NSRegularExpression.Options = []) -> String? { guard let match = self.firstMatch(in: text, options: [], diff --git a/SignalUtilitiesKit/NSSet+Functional.h b/SignalUtilitiesKit/Utilities/NSSet+Functional.h similarity index 100% rename from SignalUtilitiesKit/NSSet+Functional.h rename to SignalUtilitiesKit/Utilities/NSSet+Functional.h diff --git a/SignalUtilitiesKit/NSSet+Functional.m b/SignalUtilitiesKit/Utilities/NSSet+Functional.m similarity index 100% rename from SignalUtilitiesKit/NSSet+Functional.m rename to SignalUtilitiesKit/Utilities/NSSet+Functional.m diff --git a/SignalUtilitiesKit/NSString+SSK.h b/SignalUtilitiesKit/Utilities/NSString+SSK.h similarity index 100% rename from SignalUtilitiesKit/NSString+SSK.h rename to SignalUtilitiesKit/Utilities/NSString+SSK.h diff --git a/SignalUtilitiesKit/NSString+SSK.m b/SignalUtilitiesKit/Utilities/NSString+SSK.m similarity index 100% rename from SignalUtilitiesKit/NSString+SSK.m rename to SignalUtilitiesKit/Utilities/NSString+SSK.m diff --git a/SignalUtilitiesKit/NSURLSessionDataTask+StatusCode.h b/SignalUtilitiesKit/Utilities/NSURLSessionDataTask+StatusCode.h similarity index 100% rename from SignalUtilitiesKit/NSURLSessionDataTask+StatusCode.h rename to SignalUtilitiesKit/Utilities/NSURLSessionDataTask+StatusCode.h diff --git a/SignalUtilitiesKit/NSURLSessionDataTask+StatusCode.m b/SignalUtilitiesKit/Utilities/NSURLSessionDataTask+StatusCode.m similarity index 100% rename from SignalUtilitiesKit/NSURLSessionDataTask+StatusCode.m rename to SignalUtilitiesKit/Utilities/NSURLSessionDataTask+StatusCode.m diff --git a/SignalUtilitiesKit/NSUserDefaults+OWS.h b/SignalUtilitiesKit/Utilities/NSUserDefaults+OWS.h similarity index 100% rename from SignalUtilitiesKit/NSUserDefaults+OWS.h rename to SignalUtilitiesKit/Utilities/NSUserDefaults+OWS.h diff --git a/SignalUtilitiesKit/NSUserDefaults+OWS.m b/SignalUtilitiesKit/Utilities/NSUserDefaults+OWS.m similarity index 100% rename from SignalUtilitiesKit/NSUserDefaults+OWS.m rename to SignalUtilitiesKit/Utilities/NSUserDefaults+OWS.m diff --git a/SignalUtilitiesKit/OWSAudioPlayer.h b/SignalUtilitiesKit/Utilities/OWSAudioPlayer.h similarity index 100% rename from SignalUtilitiesKit/OWSAudioPlayer.h rename to SignalUtilitiesKit/Utilities/OWSAudioPlayer.h diff --git a/SignalUtilitiesKit/OWSAudioPlayer.m b/SignalUtilitiesKit/Utilities/OWSAudioPlayer.m similarity index 100% rename from SignalUtilitiesKit/OWSAudioPlayer.m rename to SignalUtilitiesKit/Utilities/OWSAudioPlayer.m diff --git a/SignalUtilitiesKit/OWSAudioSession.swift b/SignalUtilitiesKit/Utilities/OWSAudioSession.swift similarity index 100% rename from SignalUtilitiesKit/OWSAudioSession.swift rename to SignalUtilitiesKit/Utilities/OWSAudioSession.swift diff --git a/SignalUtilitiesKit/OWSFormat.h b/SignalUtilitiesKit/Utilities/OWSFormat.h similarity index 100% rename from SignalUtilitiesKit/OWSFormat.h rename to SignalUtilitiesKit/Utilities/OWSFormat.h diff --git a/SignalUtilitiesKit/OWSFormat.m b/SignalUtilitiesKit/Utilities/OWSFormat.m similarity index 100% rename from SignalUtilitiesKit/OWSFormat.m rename to SignalUtilitiesKit/Utilities/OWSFormat.m diff --git a/SignalUtilitiesKit/OWSMath.h b/SignalUtilitiesKit/Utilities/OWSMath.h similarity index 100% rename from SignalUtilitiesKit/OWSMath.h rename to SignalUtilitiesKit/Utilities/OWSMath.h diff --git a/SignalUtilitiesKit/OWSMediaUtils.swift b/SignalUtilitiesKit/Utilities/OWSMediaUtils.swift similarity index 100% rename from SignalUtilitiesKit/OWSMediaUtils.swift rename to SignalUtilitiesKit/Utilities/OWSMediaUtils.swift diff --git a/SignalUtilitiesKit/OWSScrubbingLogFormatter.h b/SignalUtilitiesKit/Utilities/OWSScrubbingLogFormatter.h similarity index 100% rename from SignalUtilitiesKit/OWSScrubbingLogFormatter.h rename to SignalUtilitiesKit/Utilities/OWSScrubbingLogFormatter.h diff --git a/SignalUtilitiesKit/OWSScrubbingLogFormatter.m b/SignalUtilitiesKit/Utilities/OWSScrubbingLogFormatter.m similarity index 100% rename from SignalUtilitiesKit/OWSScrubbingLogFormatter.m rename to SignalUtilitiesKit/Utilities/OWSScrubbingLogFormatter.m diff --git a/SignalUtilitiesKit/OWSVideoPlayer.swift b/SignalUtilitiesKit/Utilities/OWSVideoPlayer.swift similarity index 100% rename from SignalUtilitiesKit/OWSVideoPlayer.swift rename to SignalUtilitiesKit/Utilities/OWSVideoPlayer.swift diff --git a/SignalUtilitiesKit/OrderedDictionary.swift b/SignalUtilitiesKit/Utilities/OrderedDictionary.swift similarity index 100% rename from SignalUtilitiesKit/OrderedDictionary.swift rename to SignalUtilitiesKit/Utilities/OrderedDictionary.swift diff --git a/SignalUtilitiesKit/ProtoUtils.h b/SignalUtilitiesKit/Utilities/ProtoUtils.h similarity index 100% rename from SignalUtilitiesKit/ProtoUtils.h rename to SignalUtilitiesKit/Utilities/ProtoUtils.h diff --git a/SignalUtilitiesKit/ProtoUtils.m b/SignalUtilitiesKit/Utilities/ProtoUtils.m similarity index 58% rename from SignalUtilitiesKit/ProtoUtils.m rename to SignalUtilitiesKit/Utilities/ProtoUtils.m index a48a0cd0c..9f330d66a 100644 --- a/SignalUtilitiesKit/ProtoUtils.m +++ b/SignalUtilitiesKit/Utilities/ProtoUtils.m @@ -29,19 +29,7 @@ NS_ASSUME_NONNULL_BEGIN + (BOOL)shouldMessageHaveLocalProfileKey:(TSThread *)thread recipientId:(NSString *_Nullable)recipientId { OWSAssertDebug(thread); - - // For 1:1 threads, we want to include the profile key IFF the - // contact is in the whitelist. - // - // For Group threads, we want to include the profile key IFF the - // recipient OR the group is in the whitelist. - if (recipientId.length > 0 && [self.profileManager isUserInProfileWhitelist:recipientId]) { - return YES; - } else if ([self.profileManager isThreadInProfileWhitelist:thread]) { - return YES; - } - - return NO; + return YES; } + (void)addLocalProfileKeyIfNecessary:(TSThread *)thread @@ -53,15 +41,6 @@ NS_ASSUME_NONNULL_BEGIN if ([self shouldMessageHaveLocalProfileKey:thread recipientId:recipientId]) { [dataMessageBuilder setProfileKey:self.localProfileKey.keyData]; - - if (recipientId.length > 0) { - // Once we've shared our profile key with a user (perhaps due to being - // a member of a whitelisted group), make sure they're whitelisted. - // FIXME PERF avoid this dispatch. It's going to happen for *each* recipient in a group message. - dispatch_async(dispatch_get_main_queue(), ^{ - [self.profileManager addUserToProfileWhitelist:recipientId]; - }); - } } } @@ -82,13 +61,6 @@ NS_ASSUME_NONNULL_BEGIN if ([self shouldMessageHaveLocalProfileKey:thread recipientId:recipientId]) { [callMessageBuilder setProfileKey:self.localProfileKey.keyData]; - - // Once we've shared our profile key with a user (perhaps due to being - // a member of a whitelisted group), make sure they're whitelisted. - // FIXME PERF avoid this dispatch. It's going to happen for *each* recipient in a group message. - dispatch_async(dispatch_get_main_queue(), ^{ - [self.profileManager addUserToProfileWhitelist:recipientId]; - }); } } diff --git a/SignalUtilitiesKit/ReverseDispatchQueue.swift b/SignalUtilitiesKit/Utilities/ReverseDispatchQueue.swift similarity index 100% rename from SignalUtilitiesKit/ReverseDispatchQueue.swift rename to SignalUtilitiesKit/Utilities/ReverseDispatchQueue.swift diff --git a/SignalUtilitiesKit/SSKProtoEnvelope+Conversion.swift b/SignalUtilitiesKit/Utilities/SNProtoEnvelope+Conversion.swift similarity index 82% rename from SignalUtilitiesKit/SSKProtoEnvelope+Conversion.swift rename to SignalUtilitiesKit/Utilities/SNProtoEnvelope+Conversion.swift index b5f6cb7c1..724b2698f 100644 --- a/SignalUtilitiesKit/SSKProtoEnvelope+Conversion.swift +++ b/SignalUtilitiesKit/Utilities/SNProtoEnvelope+Conversion.swift @@ -1,7 +1,7 @@ -public extension SSKProtoEnvelope { +public extension SNProtoEnvelope { - static func from(_ json: JSON) -> SSKProtoEnvelope? { + static func from(_ json: JSON) -> SNProtoEnvelope? { guard let base64EncodedData = json["data"] as? String, let data = Data(base64Encoded: base64EncodedData) else { print("[Loki] Failed to decode data for message: \(json).") return nil diff --git a/SignalUtilitiesKit/SSKAsserts.h b/SignalUtilitiesKit/Utilities/SSKAsserts.h similarity index 100% rename from SignalUtilitiesKit/SSKAsserts.h rename to SignalUtilitiesKit/Utilities/SSKAsserts.h diff --git a/SignalUtilitiesKit/ShareViewDelegate.swift b/SignalUtilitiesKit/Utilities/ShareViewDelegate.swift similarity index 100% rename from SignalUtilitiesKit/ShareViewDelegate.swift rename to SignalUtilitiesKit/Utilities/ShareViewDelegate.swift diff --git a/SignalUtilitiesKit/String+SSK.swift b/SignalUtilitiesKit/Utilities/String+SSK.swift similarity index 100% rename from SignalUtilitiesKit/String+SSK.swift rename to SignalUtilitiesKit/Utilities/String+SSK.swift diff --git a/SignalUtilitiesKit/String+Trimming.swift b/SignalUtilitiesKit/Utilities/String+Trimming.swift similarity index 100% rename from SignalUtilitiesKit/String+Trimming.swift rename to SignalUtilitiesKit/Utilities/String+Trimming.swift diff --git a/SignalUtilitiesKit/UIAlertController+OWS.swift b/SignalUtilitiesKit/Utilities/UIAlertController+OWS.swift similarity index 100% rename from SignalUtilitiesKit/UIAlertController+OWS.swift rename to SignalUtilitiesKit/Utilities/UIAlertController+OWS.swift diff --git a/SignalUtilitiesKit/UIDevice+featureSupport.swift b/SignalUtilitiesKit/Utilities/UIDevice+featureSupport.swift similarity index 100% rename from SignalUtilitiesKit/UIDevice+featureSupport.swift rename to SignalUtilitiesKit/Utilities/UIDevice+featureSupport.swift diff --git a/SignalUtilitiesKit/UIImage+OWS.h b/SignalUtilitiesKit/Utilities/UIImage+OWS.h similarity index 100% rename from SignalUtilitiesKit/UIImage+OWS.h rename to SignalUtilitiesKit/Utilities/UIImage+OWS.h diff --git a/SignalUtilitiesKit/UIImage+OWS.m b/SignalUtilitiesKit/Utilities/UIImage+OWS.m similarity index 100% rename from SignalUtilitiesKit/UIImage+OWS.m rename to SignalUtilitiesKit/Utilities/UIImage+OWS.m diff --git a/SignalUtilitiesKit/Weak.swift b/SignalUtilitiesKit/Utilities/Weak.swift similarity index 100% rename from SignalUtilitiesKit/Weak.swift rename to SignalUtilitiesKit/Utilities/Weak.swift diff --git a/SignalUtilitiesKit/WeakTimer.swift b/SignalUtilitiesKit/Utilities/WeakTimer.swift similarity index 100% rename from SignalUtilitiesKit/WeakTimer.swift rename to SignalUtilitiesKit/Utilities/WeakTimer.swift diff --git a/SignalUtilitiesKit/VersionMigrations.m b/SignalUtilitiesKit/VersionMigrations.m index af13af3d7..ceeced936 100644 --- a/SignalUtilitiesKit/VersionMigrations.m +++ b/SignalUtilitiesKit/VersionMigrations.m @@ -10,9 +10,9 @@ #import #import #import -#import + #import -#import + #import #import #import @@ -67,27 +67,6 @@ 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. - UIAlertController *alert = [UIAlertController - alertControllerWithTitle:@"You must reinstall Signal" - message: - @"Sorry, your installation is too old for us to update. You'll have to start fresh." - preferredStyle:UIAlertControllerStyleAlert]; - - UIAlertAction *quitAction = [UIAlertAction actionWithTitle:@"Quit" - style:UIAlertActionStyleDefault - handler:^(UIAlertAction *_Nonnull action) { - OWSFail(@"Obsolete install."); - }]; - [alert addAction:quitAction]; - - [CurrentAppContext().frontmostViewController presentAlert:alert]; - } - */ - if ([self isVersion:previousVersion atLeast:@"2.0.0" andLessThan:@"2.1.70"] && [self.tsAccountManager isRegistered]) { [self clearVideoCache]; } diff --git a/SignalUtilitiesKit/ViewControllerUtils.h b/SignalUtilitiesKit/ViewControllerUtils.h deleted file mode 100644 index f027db313..000000000 --- a/SignalUtilitiesKit/ViewControllerUtils.h +++ /dev/null @@ -1,35 +0,0 @@ -// -// Copyright (c) 2019 Open Whisper Systems. All rights reserved. -// - -#import - -NS_ASSUME_NONNULL_BEGIN - -extern const NSUInteger kMin2FAPinLength; -extern const NSUInteger kMax2FAPinLength; -extern NSString *const TappedStatusBarNotification; - -@interface ViewControllerUtils : NSObject - -- (instancetype)init NS_UNAVAILABLE; - -// This convenience function can be used to reformat the contents of -// a phone number text field as the user modifies its text by typing, -// pasting, etc. -// -// "callingCode" should be of the form: "+1". -+ (void)phoneNumberTextField:(UITextField *)textField - shouldChangeCharactersInRange:(NSRange)range - replacementString:(NSString *)insertionText - callingCode:(NSString *)callingCode; - -+ (void)ows2FAPINTextField:(UITextField *)textField - shouldChangeCharactersInRange:(NSRange)range - replacementString:(NSString *)insertionText; - -+ (NSString *)examplePhoneNumberForCountryCode:(NSString *)countryCode callingCode:(NSString *)callingCode; - -@end - -NS_ASSUME_NONNULL_END diff --git a/SignalUtilitiesKit/ViewControllerUtils.m b/SignalUtilitiesKit/ViewControllerUtils.m deleted file mode 100644 index 5d26d87ee..000000000 --- a/SignalUtilitiesKit/ViewControllerUtils.m +++ /dev/null @@ -1,158 +0,0 @@ -// -// Copyright (c) 2019 Open Whisper Systems. All rights reserved. -// - -#import "ViewControllerUtils.h" -#import "PhoneNumber.h" -#import -#import -#import - -NS_ASSUME_NONNULL_BEGIN - -NSString *const TappedStatusBarNotification = @"TappedStatusBarNotification"; - -const NSUInteger kMin2FAPinLength = 4; -const NSUInteger kMax2FAPinLength = 16; - -@implementation ViewControllerUtils - -+ (void)phoneNumberTextField:(UITextField *)textField - shouldChangeCharactersInRange:(NSRange)range - replacementString:(NSString *)insertionText - callingCode:(NSString *)callingCode -{ - // Phone numbers takes many forms. - // - // * We only want to let the user enter decimal digits. - // * The user shouldn't have to enter hyphen, parentheses or whitespace; - // the phone number should be formatted automatically. - // * The user should be able to copy and paste freely. - // * Invalid input should be simply ignored. - // - // We accomplish this by being permissive and trying to "take as much of the user - // input as possible". - // - // * Always accept deletes. - // * Ignore invalid input. - // * Take partial input if possible. - - NSString *oldText = textField.text; - - // Construct the new contents of the text field by: - // 1. Determining the "left" substring: the contents of the old text _before_ the deletion range. - // Filtering will remove non-decimal digit characters like hyphen "-". - NSString *left = [oldText substringToIndex:range.location].digitsOnly; - // 2. Determining the "right" substring: the contents of the old text _after_ the deletion range. - NSString *right = [oldText substringFromIndex:range.location + range.length].digitsOnly; - // 3. Determining the "center" substring: the contents of the new insertion text. - NSString *center = insertionText.digitsOnly; - - // 3a. If user hits backspace, they should always delete a _digit_ to the - // left of the cursor, even if the text _immediately_ to the left of - // cursor is "formatting text" (e.g. whitespace, a hyphen or a - // parentheses). - bool isJustDeletion = insertionText.length == 0; - if (isJustDeletion) { - NSString *deletedText = [oldText substringWithRange:range]; - BOOL didDeleteFormatting = (deletedText.length == 1 && deletedText.digitsOnly.length < 1); - if (didDeleteFormatting && left.length > 0) { - left = [left substringToIndex:left.length - 1]; - } - } - - // 4. Construct the "raw" new text by concatenating left, center and right. - NSString *textAfterChange = [[left stringByAppendingString:center] stringByAppendingString:right]; - // 4a. Ensure we don't exceed the maximum length for a e164 phone number, - // 15 digits, per: https://en.wikipedia.org/wiki/E.164 - // - // NOTE: The actual limit is 18, not 15, because of certain invalid phone numbers in Germany. - // https://github.com/googlei18n/libphonenumber/blob/master/FALSEHOODS.md - const int kMaxPhoneNumberLength = 18; - if (textAfterChange.length > kMaxPhoneNumberLength) { - textAfterChange = [textAfterChange substringToIndex:kMaxPhoneNumberLength]; - } - // 5. Construct the "formatted" new text by inserting a hyphen if necessary. - // reformat the phone number, trying to keep the cursor beside the inserted or deleted digit - NSUInteger cursorPositionAfterChange = MIN(left.length + center.length, textAfterChange.length); - - NSString *textToFormat = textAfterChange; - NSString *formattedText = [PhoneNumber bestEffortFormatPartialUserSpecifiedTextToLookLikeAPhoneNumber:textToFormat - withSpecifiedCountryCodeString:callingCode]; - NSUInteger cursorPositionAfterReformat = [PhoneNumberUtil translateCursorPosition:cursorPositionAfterChange - from:textToFormat - to:formattedText - stickingRightward:isJustDeletion]; - - textField.text = formattedText; - UITextPosition *pos = - [textField positionFromPosition:textField.beginningOfDocument offset:(NSInteger)cursorPositionAfterReformat]; - [textField setSelectedTextRange:[textField textRangeFromPosition:pos toPosition:pos]]; -} - -+ (void)ows2FAPINTextField:(UITextField *)textField - shouldChangeCharactersInRange:(NSRange)range - replacementString:(NSString *)insertionText -{ - // * We only want to let the user enter decimal digits. - // * The user should be able to copy and paste freely. - // * Invalid input should be simply ignored. - // - // We accomplish this by being permissive and trying to "take as much of the user - // input as possible". - // - // * Always accept deletes. - // * Ignore invalid input. - // * Take partial input if possible. - - NSString *oldText = textField.text; - // Construct the new contents of the text field by: - // 1. Determining the "left" substring: the contents of the old text _before_ the deletion range. - // Filtering will remove non-decimal digit characters. - NSString *left = [oldText substringToIndex:range.location].digitsOnly; - // 2. Determining the "right" substring: the contents of the old text _after_ the deletion range. - NSString *right = [oldText substringFromIndex:range.location + range.length].digitsOnly; - // 3. Determining the "center" substring: the contents of the new insertion text. - NSString *center = insertionText.digitsOnly; - // 4. Construct the "raw" new text by concatenating left, center and right. - NSString *textAfterChange = [[left stringByAppendingString:center] stringByAppendingString:right]; - // 5. Ensure we don't exceed the maximum length for a PIN. - if (textAfterChange.length > kMax2FAPinLength) { - textAfterChange = [textAfterChange substringToIndex:kMax2FAPinLength]; - } - // 6. Construct the final text. - textField.text = textAfterChange; - NSUInteger cursorPositionAfterChange = MIN(left.length + center.length, textAfterChange.length); - UITextPosition *pos = - [textField positionFromPosition:textField.beginningOfDocument offset:(NSInteger)cursorPositionAfterChange]; - [textField setSelectedTextRange:[textField textRangeFromPosition:pos toPosition:pos]]; -} - -+ (NSString *)examplePhoneNumberForCountryCode:(NSString *)countryCode callingCode:(NSString *)callingCode -{ - OWSAssertDebug(countryCode.length > 0); - OWSAssertDebug(callingCode.length > 0); - - NSString *examplePhoneNumber = [PhoneNumberUtil examplePhoneNumberForCountryCode:countryCode]; - OWSAssertDebug(!examplePhoneNumber || [examplePhoneNumber hasPrefix:callingCode]); - if (examplePhoneNumber && [examplePhoneNumber hasPrefix:callingCode]) { - NSString *formattedPhoneNumber = - [PhoneNumber bestEffortFormatPartialUserSpecifiedTextToLookLikeAPhoneNumber:examplePhoneNumber - withSpecifiedCountryCodeString:countryCode]; - if (formattedPhoneNumber.length > 0) { - examplePhoneNumber = formattedPhoneNumber; - } - - return [NSString - stringWithFormat: - NSLocalizedString(@"PHONE_NUMBER_EXAMPLE_FORMAT", - @"A format for a label showing an example phone number. Embeds {{the example phone number}}."), - [examplePhoneNumber substringFromIndex:callingCode.length]]; - } else { - return @""; - } -} - -@end - -NS_ASSUME_NONNULL_END