mirror of https://github.com/oxen-io/session-ios
Removed some more legacy code which has been refactored
parent
5de8d9c7a8
commit
49dd341b6d
@ -1,130 +0,0 @@
|
||||
//
|
||||
// Copyright (c) 2019 Open Whisper Systems. All rights reserved.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
import PromiseKit
|
||||
import SignalUtilitiesKit
|
||||
|
||||
/**
|
||||
* Signal is actually two services - textSecure for messages and red phone (for calls).
|
||||
* AccountManager delegates to both.
|
||||
*/
|
||||
@objc
|
||||
public class AccountManager: NSObject {
|
||||
|
||||
// MARK: - Dependencies
|
||||
|
||||
private var preferences: OWSPreferences {
|
||||
return Environment.shared.preferences
|
||||
}
|
||||
|
||||
private var tsAccountManager: TSAccountManager {
|
||||
return TSAccountManager.sharedInstance()
|
||||
}
|
||||
|
||||
// MARK: -
|
||||
|
||||
@objc
|
||||
public override init() {
|
||||
super.init()
|
||||
|
||||
SwiftSingletons.register(self)
|
||||
}
|
||||
|
||||
// MARK: registration
|
||||
|
||||
@objc func registerObjc(verificationCode: String,
|
||||
pin: String?) -> AnyPromise {
|
||||
return AnyPromise(register(verificationCode: verificationCode, pin: pin))
|
||||
}
|
||||
|
||||
func register(verificationCode: String,
|
||||
pin: String?) -> Promise<Void> {
|
||||
guard verificationCode.count > 0 else {
|
||||
let error = OWSErrorWithCodeDescription(.userError,
|
||||
NSLocalizedString("REGISTRATION_ERROR_BLANK_VERIFICATION_CODE",
|
||||
comment: "alert body during registration"))
|
||||
return Promise(error: error)
|
||||
}
|
||||
|
||||
Logger.debug("registering with signal server")
|
||||
let registrationPromise: Promise<Void> = firstly {
|
||||
return self.registerForTextSecure(verificationCode: verificationCode, pin: pin)
|
||||
}.then { _ -> Promise<Void> in
|
||||
return self.syncPushTokens().recover { (error) -> Promise<Void> in
|
||||
switch error {
|
||||
case PushRegistrationError.pushNotSupported(let description):
|
||||
// This can happen with:
|
||||
// - simulators, none of which support receiving push notifications
|
||||
// - on iOS11 devices which have disabled "Allow Notifications" and disabled "Enable Background Refresh" in the system settings.
|
||||
Logger.info("Recovered push registration error. Registering for manual message fetcher because push not supported: \(description)")
|
||||
return self.enableManualMessageFetching()
|
||||
default:
|
||||
throw error
|
||||
}
|
||||
}
|
||||
}.done { (_) -> Void in
|
||||
self.completeRegistration()
|
||||
}
|
||||
|
||||
registrationPromise.retainUntilComplete()
|
||||
|
||||
return registrationPromise
|
||||
}
|
||||
|
||||
private func registerForTextSecure(verificationCode: String,
|
||||
pin: String?) -> Promise<Void> {
|
||||
return Promise { resolver in
|
||||
tsAccountManager.verifyAccount(withCode: verificationCode,
|
||||
pin: pin,
|
||||
success: { resolver.fulfill(()) },
|
||||
failure: resolver.reject)
|
||||
}
|
||||
}
|
||||
|
||||
private func syncPushTokens() -> Promise<Void> {
|
||||
Logger.info("")
|
||||
|
||||
guard let job: Job = Job(
|
||||
variant: .syncPushTokens,
|
||||
details: SyncPushTokensJob.Details(
|
||||
uploadOnlyIfStale: false
|
||||
)
|
||||
)
|
||||
else { return Promise(error: GRDBStorageError.decodingFailed) }
|
||||
|
||||
let (promise, seal) = Promise<Void>.pending()
|
||||
|
||||
SyncPushTokensJob.run(
|
||||
job,
|
||||
success: { _, _ in seal.fulfill(()) },
|
||||
failure: { _, error, _ in seal.reject(error ?? GRDBStorageError.generic) },
|
||||
deferred: { _ in seal.reject(GRDBStorageError.generic) }
|
||||
)
|
||||
|
||||
return promise
|
||||
}
|
||||
|
||||
private func completeRegistration() {
|
||||
Logger.info("")
|
||||
tsAccountManager.didRegister()
|
||||
}
|
||||
|
||||
// MARK: Message Delivery
|
||||
|
||||
func updatePushTokens(pushToken: String, voipToken: String, isForcedUpdate: Bool) -> Promise<Void> {
|
||||
return Promise { resolver in
|
||||
tsAccountManager.registerForPushNotifications(pushToken: pushToken,
|
||||
voipToken: voipToken,
|
||||
isForcedUpdate: isForcedUpdate,
|
||||
success: { resolver.fulfill(()) },
|
||||
failure: resolver.reject)
|
||||
}
|
||||
}
|
||||
|
||||
func enableManualMessageFetching() -> Promise<Void> {
|
||||
let anyPromise = tsAccountManager.setIsManualMessageFetchEnabled(true)
|
||||
return Promise(anyPromise).asVoid()
|
||||
}
|
||||
}
|
||||
@ -1,167 +0,0 @@
|
||||
//
|
||||
// Copyright (c) 2019 Open Whisper Systems. All rights reserved.
|
||||
//
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
extern NSString *const TSRegistrationErrorDomain;
|
||||
extern NSString *const TSRegistrationErrorUserInfoHTTPStatus;
|
||||
extern NSString *const RegistrationStateDidChangeNotification;
|
||||
extern NSString *const kNSNotificationName_LocalNumberDidChange;
|
||||
|
||||
@class AnyPromise;
|
||||
@class OWSPrimaryStorage;
|
||||
@class TSNetworkManager;
|
||||
@class YapDatabaseReadTransaction;
|
||||
@class YapDatabaseReadWriteTransaction;
|
||||
|
||||
typedef NS_ENUM(NSUInteger, OWSRegistrationState) {
|
||||
OWSRegistrationState_Unregistered,
|
||||
OWSRegistrationState_PendingBackupRestore,
|
||||
OWSRegistrationState_Registered,
|
||||
OWSRegistrationState_Deregistered,
|
||||
OWSRegistrationState_Reregistering,
|
||||
};
|
||||
|
||||
@interface TSAccountManager : NSObject
|
||||
|
||||
@property (nonatomic, nullable) NSString *phoneNumberAwaitingVerification;
|
||||
|
||||
#pragma mark - Initializers
|
||||
|
||||
- (instancetype)init NS_UNAVAILABLE;
|
||||
|
||||
- (instancetype)initWithPrimaryStorage:(OWSPrimaryStorage *)primaryStorage NS_DESIGNATED_INITIALIZER;
|
||||
|
||||
+ (instancetype)sharedInstance;
|
||||
|
||||
- (OWSRegistrationState)registrationState;
|
||||
|
||||
/**
|
||||
* Returns if a user is registered or not
|
||||
*
|
||||
* @return registered or not
|
||||
*/
|
||||
- (BOOL)isRegistered;
|
||||
- (BOOL)isRegisteredAndReady;
|
||||
|
||||
/**
|
||||
* Returns current phone number for this device, which may not yet have been registered.
|
||||
*
|
||||
* @return E164 formatted phone number
|
||||
*/
|
||||
+ (nullable NSString *)localNumber;
|
||||
- (nullable NSString *)localNumber;
|
||||
|
||||
// A variant of localNumber that never opens a "sneaky" transaction.
|
||||
- (nullable NSString *)storedOrCachedLocalNumber:(YapDatabaseReadTransaction *)transaction;
|
||||
|
||||
/**
|
||||
* Symmetric key that's used to encrypt message payloads from the server,
|
||||
*
|
||||
* @return signaling key
|
||||
*/
|
||||
+ (nullable NSString *)signalingKey;
|
||||
- (nullable NSString *)signalingKey;
|
||||
|
||||
/**
|
||||
* The server auth token allows the Signal client to connect to the Signal server
|
||||
*
|
||||
* @return server authentication token
|
||||
*/
|
||||
+ (nullable NSString *)serverAuthToken;
|
||||
- (nullable NSString *)serverAuthToken;
|
||||
|
||||
/**
|
||||
* The registration ID is unique to an installation of TextSecure, it allows to know if the app was reinstalled
|
||||
*
|
||||
* @return registrationID;
|
||||
*/
|
||||
|
||||
+ (uint32_t)getOrGenerateRegistrationId:(YapDatabaseReadWriteTransaction *)transaction;
|
||||
- (uint32_t)getOrGenerateRegistrationId;
|
||||
- (uint32_t)getOrGenerateRegistrationId:(YapDatabaseReadWriteTransaction *)transaction;
|
||||
|
||||
#pragma mark - Register with phone number
|
||||
|
||||
- (void)registerWithPhoneNumber:(NSString *)phoneNumber
|
||||
captchaToken:(nullable NSString *)captchaToken
|
||||
success:(void (^)(void))successBlock
|
||||
failure:(void (^)(NSError *error))failureBlock
|
||||
smsVerification:(BOOL)isSMS;
|
||||
|
||||
- (void)rerequestSMSWithCaptchaToken:(nullable NSString *)captchaToken
|
||||
success:(void (^)(void))successBlock
|
||||
failure:(void (^)(NSError *error))failureBlock;
|
||||
|
||||
- (void)rerequestVoiceWithCaptchaToken:(nullable NSString *)captchaToken
|
||||
success:(void (^)(void))successBlock
|
||||
failure:(void (^)(NSError *error))failureBlock;
|
||||
|
||||
- (void)verifyAccountWithCode:(NSString *)verificationCode
|
||||
pin:(nullable NSString *)pin
|
||||
success:(void (^)(void))successBlock
|
||||
failure:(void (^)(NSError *error))failureBlock;
|
||||
|
||||
// Called once registration is complete - meaning the following have succeeded:
|
||||
// - obtained signal server credentials
|
||||
// - uploaded pre-keys
|
||||
// - uploaded push tokens
|
||||
- (void)didRegister;
|
||||
|
||||
#if TARGET_OS_IPHONE
|
||||
|
||||
/**
|
||||
* Register's the device's push notification token with the server
|
||||
*
|
||||
* @param pushToken Apple's Push Token
|
||||
*/
|
||||
- (void)registerForPushNotificationsWithPushToken:(NSString *)pushToken
|
||||
voipToken:(NSString *)voipToken
|
||||
isForcedUpdate:(BOOL)isForcedUpdate
|
||||
success:(void (^)(void))successHandler
|
||||
failure:(void (^)(NSError *error))failureHandler
|
||||
NS_SWIFT_NAME(registerForPushNotifications(pushToken:voipToken:isForcedUpdate:success:failure:));
|
||||
|
||||
#endif
|
||||
|
||||
+ (void)unregisterTextSecureWithSuccess:(void (^)(void))success failure:(void (^)(NSError *error))failureBlock;
|
||||
|
||||
#pragma mark - De-Registration
|
||||
|
||||
// De-registration reflects whether or not the "last known contact"
|
||||
// with the service was:
|
||||
//
|
||||
// * A 403 from the service, indicating de-registration.
|
||||
// * A successful auth'd request _or_ websocket connection indicating
|
||||
// valid registration.
|
||||
- (BOOL)isDeregistered;
|
||||
- (void)setIsDeregistered:(BOOL)isDeregistered;
|
||||
|
||||
#pragma mark - Re-registration
|
||||
|
||||
// Re-registration is the process of re-registering _with the same phone number_.
|
||||
|
||||
// Returns YES on success.
|
||||
- (nullable NSString *)reregisterationPhoneNumber;
|
||||
- (BOOL)isReregistering;
|
||||
|
||||
#pragma mark - Manual Message Fetch
|
||||
|
||||
- (BOOL)isManualMessageFetchEnabled;
|
||||
- (AnyPromise *)setIsManualMessageFetchEnabled:(BOOL)value __attribute__((warn_unused_result));
|
||||
|
||||
#ifdef DEBUG
|
||||
- (void)registerForTestsWithLocalNumber:(NSString *)localNumber;
|
||||
#endif
|
||||
|
||||
- (AnyPromise *)updateAccountAttributes __attribute__((warn_unused_result));
|
||||
|
||||
// This should only be used during the registration process.
|
||||
- (AnyPromise *)performUpdateAccountAttributes __attribute__((warn_unused_result));
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
@ -1,499 +0,0 @@
|
||||
//
|
||||
// Copyright (c) 2019 Open Whisper Systems. All rights reserved.
|
||||
//
|
||||
|
||||
#import "TSAccountManager.h"
|
||||
#import "AppContext.h"
|
||||
#import "AppReadiness.h"
|
||||
#import "NSNotificationCenter+OWS.h"
|
||||
#import "SSKEnvironment.h"
|
||||
#import "YapDatabaseConnection+OWS.h"
|
||||
#import "YapDatabaseTransaction+OWS.h"
|
||||
#import <PromiseKit/AnyPromise.h>
|
||||
#import <Reachability/Reachability.h>
|
||||
#import <SignalCoreKit/NSData+OWS.h>
|
||||
#import <SignalCoreKit/Randomness.h>
|
||||
#import <SessionMessagingKit/SessionMessagingKit-Swift.h>
|
||||
#import <YapDatabase/YapDatabase.h>
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
NSString *const TSRegistrationErrorDomain = @"TSRegistrationErrorDomain";
|
||||
NSString *const TSRegistrationErrorUserInfoHTTPStatus = @"TSHTTPStatus";
|
||||
NSString *const RegistrationStateDidChangeNotification = @"RegistrationStateDidChangeNotification";
|
||||
NSString *const kNSNotificationName_LocalNumberDidChange = @"kNSNotificationName_LocalNumberDidChange";
|
||||
|
||||
NSString *const TSAccountManager_RegisteredNumberKey = @"TSStorageRegisteredNumberKey";
|
||||
NSString *const TSAccountManager_IsDeregisteredKey = @"TSAccountManager_IsDeregisteredKey";
|
||||
NSString *const TSAccountManager_ReregisteringPhoneNumberKey = @"TSAccountManager_ReregisteringPhoneNumberKey";
|
||||
NSString *const TSAccountManager_LocalRegistrationIdKey = @"TSStorageLocalRegistrationId";
|
||||
NSString *const TSAccountManager_HasPendingRestoreDecisionKey = @"TSAccountManager_HasPendingRestoreDecisionKey";
|
||||
|
||||
NSString *const TSAccountManager_UserAccountCollection = @"TSStorageUserAccountCollection";
|
||||
NSString *const TSAccountManager_ServerAuthToken = @"TSStorageServerAuthToken";
|
||||
NSString *const TSAccountManager_ServerSignalingKey = @"TSStorageServerSignalingKey";
|
||||
NSString *const TSAccountManager_ManualMessageFetchKey = @"TSAccountManager_ManualMessageFetchKey";
|
||||
NSString *const TSAccountManager_NeedsAccountAttributesUpdateKey = @"TSAccountManager_NeedsAccountAttributesUpdateKey";
|
||||
|
||||
@interface TSAccountManager ()
|
||||
|
||||
@property (atomic, readonly) BOOL isRegistered;
|
||||
|
||||
@property (nonatomic, nullable) NSString *cachedLocalNumber;
|
||||
@property (nonatomic, readonly) YapDatabaseConnection *dbConnection;
|
||||
|
||||
@property (nonatomic, nullable) NSNumber *cachedIsDeregistered;
|
||||
|
||||
@property (nonatomic) Reachability *reachability;
|
||||
|
||||
@end
|
||||
|
||||
#pragma mark -
|
||||
|
||||
@implementation TSAccountManager
|
||||
|
||||
@synthesize isRegistered = _isRegistered;
|
||||
|
||||
- (instancetype)initWithPrimaryStorage:(OWSPrimaryStorage *)primaryStorage
|
||||
{
|
||||
self = [super init];
|
||||
if (!self) {
|
||||
return self;
|
||||
}
|
||||
|
||||
_dbConnection = [primaryStorage newDatabaseConnection];
|
||||
self.reachability = [Reachability reachabilityForInternetConnection];
|
||||
|
||||
if (!CurrentAppContext().isMainApp) {
|
||||
[[NSNotificationCenter defaultCenter] addObserver:self
|
||||
selector:@selector(yapDatabaseModifiedExternally:)
|
||||
name:YapDatabaseModifiedExternallyNotification
|
||||
object:nil];
|
||||
}
|
||||
|
||||
[AppReadiness runNowOrWhenAppDidBecomeReady:^{
|
||||
[[self updateAccountAttributesIfNecessary] retainUntilComplete];
|
||||
}];
|
||||
|
||||
[[NSNotificationCenter defaultCenter] addObserver:self
|
||||
selector:@selector(reachabilityChanged)
|
||||
name:kReachabilityChangedNotification
|
||||
object:nil];
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)dealloc
|
||||
{
|
||||
[[NSNotificationCenter defaultCenter] removeObserver:self];
|
||||
}
|
||||
|
||||
+ (instancetype)sharedInstance
|
||||
{
|
||||
return SSKEnvironment.shared.tsAccountManager;
|
||||
}
|
||||
|
||||
#pragma mark - Dependencies
|
||||
|
||||
- (id<ProfileManagerProtocol>)profileManager {
|
||||
return SSKEnvironment.shared.profileManager;
|
||||
}
|
||||
|
||||
#pragma mark -
|
||||
|
||||
- (void)setPhoneNumberAwaitingVerification:(NSString *_Nullable)phoneNumberAwaitingVerification
|
||||
{
|
||||
_phoneNumberAwaitingVerification = phoneNumberAwaitingVerification;
|
||||
|
||||
[[NSNotificationCenter defaultCenter] postNotificationNameAsync:kNSNotificationName_LocalNumberDidChange
|
||||
object:nil
|
||||
userInfo:nil];
|
||||
}
|
||||
|
||||
- (OWSRegistrationState)registrationState
|
||||
{
|
||||
if (!self.isRegistered) {
|
||||
return OWSRegistrationState_Unregistered;
|
||||
} else if (self.isDeregistered) {
|
||||
if (self.isReregistering) {
|
||||
return OWSRegistrationState_Reregistering;
|
||||
} else {
|
||||
return OWSRegistrationState_Deregistered;
|
||||
}
|
||||
} else if (self.isDeregistered) {
|
||||
return OWSRegistrationState_PendingBackupRestore;
|
||||
} else {
|
||||
return OWSRegistrationState_Registered;
|
||||
}
|
||||
}
|
||||
|
||||
- (BOOL)isRegistered
|
||||
{
|
||||
@synchronized (self) {
|
||||
if (_isRegistered) {
|
||||
return YES;
|
||||
} else {
|
||||
// Cache this once it's true since it's called alot, involves a dbLookup, and once set - it doesn't change.
|
||||
_isRegistered = [self storedLocalNumber] != nil;
|
||||
}
|
||||
return _isRegistered;
|
||||
}
|
||||
}
|
||||
|
||||
- (BOOL)isRegisteredAndReady
|
||||
{
|
||||
return self.registrationState == OWSRegistrationState_Registered;
|
||||
}
|
||||
|
||||
- (void)didRegister
|
||||
{
|
||||
NSString *phoneNumber = self.phoneNumberAwaitingVerification;
|
||||
|
||||
[self storeLocalNumber:phoneNumber];
|
||||
|
||||
// Warm these cached values.
|
||||
[self isRegistered];
|
||||
[self localNumber];
|
||||
[self isDeregistered];
|
||||
|
||||
[self postRegistrationStateDidChangeNotification];
|
||||
}
|
||||
|
||||
+ (nullable NSString *)localNumber
|
||||
{
|
||||
return [[self sharedInstance] localNumber];
|
||||
}
|
||||
|
||||
- (nullable NSString *)localNumber
|
||||
{
|
||||
NSString *awaitingVerif = self.phoneNumberAwaitingVerification;
|
||||
if (awaitingVerif) {
|
||||
return awaitingVerif;
|
||||
}
|
||||
|
||||
// Cache this since we access this a lot, and once set it will not change.
|
||||
@synchronized(self)
|
||||
{
|
||||
if (self.cachedLocalNumber == nil) {
|
||||
self.cachedLocalNumber = self.storedLocalNumber;
|
||||
}
|
||||
}
|
||||
|
||||
return self.cachedLocalNumber;
|
||||
}
|
||||
|
||||
- (nullable NSString *)storedLocalNumber
|
||||
{
|
||||
@synchronized (self) {
|
||||
return [self.dbConnection stringForKey:TSAccountManager_RegisteredNumberKey
|
||||
inCollection:TSAccountManager_UserAccountCollection];
|
||||
}
|
||||
}
|
||||
|
||||
- (nullable NSString *)storedOrCachedLocalNumber:(YapDatabaseReadTransaction *)transaction
|
||||
{
|
||||
@synchronized(self) {
|
||||
if (self.cachedLocalNumber) {
|
||||
return self.cachedLocalNumber;
|
||||
}
|
||||
}
|
||||
|
||||
return [transaction stringForKey:TSAccountManager_RegisteredNumberKey
|
||||
inCollection:TSAccountManager_UserAccountCollection];
|
||||
}
|
||||
|
||||
- (void)storeLocalNumber:(NSString *)localNumber
|
||||
{
|
||||
@synchronized (self) {
|
||||
[self.dbConnection setObject:localNumber
|
||||
forKey:TSAccountManager_RegisteredNumberKey
|
||||
inCollection:TSAccountManager_UserAccountCollection];
|
||||
|
||||
[self.dbConnection removeObjectForKey:TSAccountManager_ReregisteringPhoneNumberKey
|
||||
inCollection:TSAccountManager_UserAccountCollection];
|
||||
|
||||
self.phoneNumberAwaitingVerification = nil;
|
||||
|
||||
self.cachedLocalNumber = localNumber;
|
||||
}
|
||||
}
|
||||
|
||||
+ (uint32_t)getOrGenerateRegistrationId:(YapDatabaseReadWriteTransaction *)transaction
|
||||
{
|
||||
return [[self sharedInstance] getOrGenerateRegistrationId:transaction];
|
||||
}
|
||||
|
||||
- (uint32_t)getOrGenerateRegistrationId
|
||||
{
|
||||
__block uint32_t result;
|
||||
[LKStorage writeSyncWithBlock:^(YapDatabaseReadWriteTransaction *_Nonnull transaction) {
|
||||
result = [self getOrGenerateRegistrationId:transaction];
|
||||
}];
|
||||
return result;
|
||||
}
|
||||
|
||||
- (uint32_t)getOrGenerateRegistrationId:(YapDatabaseReadWriteTransaction *)transaction
|
||||
{
|
||||
// Unlike other methods in this class, there's no need for a `@synchronized` block
|
||||
// here, since we're already in a write transaction, and all writes occur on a serial queue.
|
||||
//
|
||||
// Since other code in this class which uses @synchronized(self) also needs to open write
|
||||
// transaction, using @synchronized(self) here, inside of a WriteTransaction risks deadlock.
|
||||
uint32_t registrationID = [[transaction objectForKey:TSAccountManager_LocalRegistrationIdKey
|
||||
inCollection:TSAccountManager_UserAccountCollection] unsignedIntValue];
|
||||
|
||||
if (registrationID == 0) {
|
||||
registrationID = (uint32_t)arc4random_uniform(16380) + 1;
|
||||
|
||||
[transaction setObject:[NSNumber numberWithUnsignedInteger:registrationID]
|
||||
forKey:TSAccountManager_LocalRegistrationIdKey
|
||||
inCollection:TSAccountManager_UserAccountCollection];
|
||||
}
|
||||
return registrationID;
|
||||
}
|
||||
|
||||
- (void)registerForPushNotificationsWithPushToken:(NSString *)pushToken
|
||||
voipToken:(NSString *)voipToken
|
||||
isForcedUpdate:(BOOL)isForcedUpdate
|
||||
success:(void (^)(void))successHandler
|
||||
failure:(void (^)(NSError *))failureHandler
|
||||
{
|
||||
[self registerForPushNotificationsWithPushToken:pushToken
|
||||
voipToken:voipToken
|
||||
isForcedUpdate:isForcedUpdate
|
||||
success:successHandler
|
||||
failure:failureHandler
|
||||
remainingRetries:3];
|
||||
}
|
||||
|
||||
- (void)registerForPushNotificationsWithPushToken:(NSString *)pushToken
|
||||
voipToken:(NSString *)voipToken
|
||||
isForcedUpdate:(BOOL)isForcedUpdate
|
||||
success:(void (^)(void))successHandler
|
||||
failure:(void (^)(NSError *))failureHandler
|
||||
remainingRetries:(int)remainingRetries
|
||||
{
|
||||
BOOL isUsingFullAPNs = [NSUserDefaults.standardUserDefaults boolForKey:@"isUsingFullAPNs"];
|
||||
NSData *pushTokenAsData = [NSData dataFromHexString:pushToken];
|
||||
AnyPromise *promise = isUsingFullAPNs ? [LKPushNotificationAPI registerWithToken:pushTokenAsData hexEncodedPublicKey:self.localNumber isForcedUpdate:isForcedUpdate]
|
||||
: [LKPushNotificationAPI unregisterToken:pushTokenAsData];
|
||||
promise
|
||||
.then(^() {
|
||||
successHandler();
|
||||
})
|
||||
.catch(^(NSError *error) {
|
||||
if (remainingRetries > 0) {
|
||||
[self registerForPushNotificationsWithPushToken:pushToken voipToken:voipToken isForcedUpdate:isForcedUpdate success:successHandler failure:failureHandler
|
||||
remainingRetries:remainingRetries - 1];
|
||||
} else {
|
||||
failureHandler(error);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
- (void)rerequestSMSWithCaptchaToken:(nullable NSString *)captchaToken
|
||||
success:(void (^)(void))successBlock
|
||||
failure:(void (^)(NSError *error))failureBlock
|
||||
{
|
||||
// TODO: Can we remove phoneNumberAwaitingVerification?
|
||||
NSString *number = self.phoneNumberAwaitingVerification;
|
||||
|
||||
[self registerWithPhoneNumber:number
|
||||
captchaToken:captchaToken
|
||||
success:successBlock
|
||||
failure:failureBlock
|
||||
smsVerification:YES];
|
||||
}
|
||||
|
||||
- (void)rerequestVoiceWithCaptchaToken:(nullable NSString *)captchaToken
|
||||
success:(void (^)(void))successBlock
|
||||
failure:(void (^)(NSError *error))failureBlock
|
||||
{
|
||||
NSString *number = self.phoneNumberAwaitingVerification;
|
||||
|
||||
[self registerWithPhoneNumber:number
|
||||
captchaToken:captchaToken
|
||||
success:successBlock
|
||||
failure:failureBlock
|
||||
smsVerification:NO];
|
||||
}
|
||||
|
||||
#pragma mark Server keying material
|
||||
|
||||
+ (NSString *)generateNewAccountAuthenticationToken {
|
||||
NSData *authToken = [Randomness generateRandomBytes:16];
|
||||
NSString *authTokenPrint = [[NSData dataWithData:authToken] hexadecimalString];
|
||||
return authTokenPrint;
|
||||
}
|
||||
|
||||
+ (nullable NSString *)signalingKey
|
||||
{
|
||||
return [[self sharedInstance] signalingKey];
|
||||
}
|
||||
|
||||
- (nullable NSString *)signalingKey
|
||||
{
|
||||
return [self.dbConnection stringForKey:TSAccountManager_ServerSignalingKey
|
||||
inCollection:TSAccountManager_UserAccountCollection];
|
||||
}
|
||||
|
||||
+ (nullable NSString *)serverAuthToken
|
||||
{
|
||||
return [[self sharedInstance] serverAuthToken];
|
||||
}
|
||||
|
||||
- (nullable NSString *)serverAuthToken
|
||||
{
|
||||
return [self.dbConnection stringForKey:TSAccountManager_ServerAuthToken
|
||||
inCollection:TSAccountManager_UserAccountCollection];
|
||||
}
|
||||
|
||||
- (void)storeServerAuthToken:(NSString *)authToken
|
||||
{
|
||||
[LKStorage writeSyncWithBlock:^(YapDatabaseReadWriteTransaction *transaction) {
|
||||
[transaction setObject:authToken
|
||||
forKey:TSAccountManager_ServerAuthToken
|
||||
inCollection:TSAccountManager_UserAccountCollection];
|
||||
}];
|
||||
}
|
||||
|
||||
- (void)yapDatabaseModifiedExternally:(NSNotification *)notification
|
||||
{
|
||||
// Any database write by the main app might reflect a deregistration,
|
||||
// so clear the cached "is registered" state. This will significantly
|
||||
// erode the value of this cache in the SAE.
|
||||
@synchronized(self)
|
||||
{
|
||||
_isRegistered = NO;
|
||||
}
|
||||
}
|
||||
|
||||
#pragma mark - De-Registration
|
||||
|
||||
- (BOOL)isDeregistered
|
||||
{
|
||||
// Cache this since we access this a lot, and once set it will not change.
|
||||
@synchronized(self) {
|
||||
if (self.cachedIsDeregistered == nil) {
|
||||
self.cachedIsDeregistered = @([self.dbConnection boolForKey:TSAccountManager_IsDeregisteredKey
|
||||
inCollection:TSAccountManager_UserAccountCollection
|
||||
defaultValue:NO]);
|
||||
}
|
||||
|
||||
return self.cachedIsDeregistered.boolValue;
|
||||
}
|
||||
}
|
||||
|
||||
- (void)setIsDeregistered:(BOOL)isDeregistered
|
||||
{
|
||||
@synchronized(self) {
|
||||
if (self.cachedIsDeregistered && self.cachedIsDeregistered.boolValue == isDeregistered) {
|
||||
return;
|
||||
}
|
||||
|
||||
self.cachedIsDeregistered = @(isDeregistered);
|
||||
}
|
||||
|
||||
[LKStorage writeWithBlock:^(YapDatabaseReadWriteTransaction *transaction) {
|
||||
[transaction setObject:@(isDeregistered)
|
||||
forKey:TSAccountManager_IsDeregisteredKey
|
||||
inCollection:TSAccountManager_UserAccountCollection];
|
||||
}];
|
||||
|
||||
[self postRegistrationStateDidChangeNotification];
|
||||
}
|
||||
|
||||
- (nullable NSString *)reregisterationPhoneNumber
|
||||
{
|
||||
NSString *_Nullable result = [self.dbConnection stringForKey:TSAccountManager_ReregisteringPhoneNumberKey
|
||||
inCollection:TSAccountManager_UserAccountCollection];
|
||||
return result;
|
||||
}
|
||||
|
||||
- (BOOL)isReregistering
|
||||
{
|
||||
return nil !=
|
||||
[self.dbConnection stringForKey:TSAccountManager_ReregisteringPhoneNumberKey
|
||||
inCollection:TSAccountManager_UserAccountCollection];
|
||||
}
|
||||
|
||||
- (BOOL)isManualMessageFetchEnabled
|
||||
{
|
||||
return [self.dbConnection boolForKey:TSAccountManager_ManualMessageFetchKey
|
||||
inCollection:TSAccountManager_UserAccountCollection
|
||||
defaultValue:NO];
|
||||
}
|
||||
|
||||
- (AnyPromise *)setIsManualMessageFetchEnabled:(BOOL)value {
|
||||
[self.dbConnection setBool:value
|
||||
forKey:TSAccountManager_ManualMessageFetchKey
|
||||
inCollection:TSAccountManager_UserAccountCollection];
|
||||
|
||||
// Try to update the account attributes to reflect this change.
|
||||
return [self updateAccountAttributes];
|
||||
}
|
||||
|
||||
- (void)registerForTestsWithLocalNumber:(NSString *)localNumber
|
||||
{
|
||||
[self storeLocalNumber:localNumber];
|
||||
}
|
||||
|
||||
#pragma mark - Account Attributes
|
||||
|
||||
- (AnyPromise *)updateAccountAttributes {
|
||||
// Enqueue a "account attribute update", recording the "request time".
|
||||
[self.dbConnection setObject:[NSDate new]
|
||||
forKey:TSAccountManager_NeedsAccountAttributesUpdateKey
|
||||
inCollection:TSAccountManager_UserAccountCollection];
|
||||
|
||||
return [self updateAccountAttributesIfNecessary];
|
||||
}
|
||||
|
||||
- (AnyPromise *)updateAccountAttributesIfNecessary {
|
||||
if (!self.isRegistered) {
|
||||
return [AnyPromise promiseWithValue:@(1)];
|
||||
}
|
||||
|
||||
return [AnyPromise promiseWithValue:@(1)];
|
||||
|
||||
NSDate *_Nullable updateRequestDate =
|
||||
[self.dbConnection objectForKey:TSAccountManager_NeedsAccountAttributesUpdateKey
|
||||
inCollection:TSAccountManager_UserAccountCollection];
|
||||
if (!updateRequestDate) {
|
||||
return [AnyPromise promiseWithValue:@(1)];
|
||||
}
|
||||
AnyPromise *promise = [self performUpdateAccountAttributes];
|
||||
promise = promise.then(^(id value) {
|
||||
[LKStorage writeSyncWithBlock:^(YapDatabaseReadWriteTransaction *transaction) {
|
||||
// Clear the update request unless a new update has been requested
|
||||
// while this update was in flight.
|
||||
NSDate *_Nullable latestUpdateRequestDate =
|
||||
[transaction objectForKey:TSAccountManager_NeedsAccountAttributesUpdateKey
|
||||
inCollection:TSAccountManager_UserAccountCollection];
|
||||
if (latestUpdateRequestDate && [latestUpdateRequestDate isEqual:updateRequestDate]) {
|
||||
[transaction removeObjectForKey:TSAccountManager_NeedsAccountAttributesUpdateKey
|
||||
inCollection:TSAccountManager_UserAccountCollection];
|
||||
}
|
||||
}];
|
||||
});
|
||||
return promise;
|
||||
}
|
||||
|
||||
- (void)reachabilityChanged {
|
||||
[AppReadiness runNowOrWhenAppDidBecomeReady:^{
|
||||
[[self updateAccountAttributesIfNecessary] retainUntilComplete];
|
||||
}];
|
||||
}
|
||||
|
||||
#pragma mark - Notifications
|
||||
|
||||
- (void)postRegistrationStateDidChangeNotification
|
||||
{
|
||||
[[NSNotificationCenter defaultCenter] postNotificationNameAsync:RegistrationStateDidChangeNotification
|
||||
object:nil
|
||||
userInfo:nil];
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
@ -1,47 +0,0 @@
|
||||
//
|
||||
// Copyright (c) 2018 Open Whisper Systems. All rights reserved.
|
||||
//
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
@class OWSPrimaryStorage;
|
||||
@class OWSStorage;
|
||||
@class TSMessage;
|
||||
@class TSThread;
|
||||
@class YapDatabaseReadTransaction;
|
||||
|
||||
@interface OWSDisappearingMessagesFinder : NSObject
|
||||
|
||||
- (void)enumerateExpiredMessagesWithBlock:(void (^_Nonnull)(TSMessage *message))block
|
||||
transaction:(YapDatabaseReadTransaction *)transaction;
|
||||
|
||||
- (void)enumerateUnstartedExpiringMessagesInThread:(TSThread *)thread
|
||||
block:(void (^_Nonnull)(TSMessage *message))block
|
||||
transaction:(YapDatabaseReadTransaction *)transaction;
|
||||
|
||||
- (void)enumerateMessagesWhichFailedToStartExpiringWithBlock:(void (^_Nonnull)(TSMessage *message))block
|
||||
transaction:(YapDatabaseReadTransaction *)transaction;
|
||||
|
||||
/**
|
||||
* @return
|
||||
* uint64_t millisecond timestamp wrapped in a number. Retrieve with `unsignedLongLongvalue`.
|
||||
* or nil if there are no upcoming expired messages
|
||||
*/
|
||||
- (nullable NSNumber *)nextExpirationTimestampWithTransaction:(YapDatabaseReadTransaction *_Nonnull)transaction;
|
||||
|
||||
+ (NSString *)databaseExtensionName;
|
||||
|
||||
+ (void)asyncRegisterDatabaseExtensions:(OWSStorage *)storage;
|
||||
|
||||
#ifdef DEBUG
|
||||
/**
|
||||
* Only use the sync version for testing, generally we'll want to register extensions async
|
||||
*/
|
||||
+ (void)blockingRegisterDatabaseExtensions:(OWSStorage *)storage;
|
||||
#endif
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
@ -1,241 +0,0 @@
|
||||
//
|
||||
// Copyright (c) 2018 Open Whisper Systems. All rights reserved.
|
||||
//
|
||||
|
||||
#import "OWSDisappearingMessagesFinder.h"
|
||||
#import "OWSPrimaryStorage.h"
|
||||
#import "TSIncomingMessage.h"
|
||||
#import "TSMessage.h"
|
||||
#import "TSOutgoingMessage.h"
|
||||
#import "TSThread.h"
|
||||
#import <SignalCoreKit/NSDate+OWS.h>
|
||||
#import <YapDatabase/YapDatabase.h>
|
||||
#import <YapDatabase/YapDatabaseQuery.h>
|
||||
#import <YapDatabase/YapDatabaseSecondaryIndex.h>
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
static NSString *const OWSDisappearingMessageFinderThreadIdColumn = @"thread_id";
|
||||
static NSString *const OWSDisappearingMessageFinderExpiresAtColumn = @"expires_at";
|
||||
static NSString *const OWSDisappearingMessageFinderExpiresAtIndex = @"index_messages_on_expires_at_and_thread_id_v2";
|
||||
|
||||
@implementation OWSDisappearingMessagesFinder
|
||||
|
||||
- (NSArray<NSString *> *)fetchUnstartedExpiringMessageIdsInThread:(TSThread *)thread
|
||||
transaction:(YapDatabaseReadTransaction *_Nonnull)transaction
|
||||
{
|
||||
NSMutableArray<NSString *> *messageIds = [NSMutableArray new];
|
||||
NSString *formattedString = [NSString stringWithFormat:@"WHERE %@ = 0 AND %@ = \"%@\"",
|
||||
OWSDisappearingMessageFinderExpiresAtColumn,
|
||||
OWSDisappearingMessageFinderThreadIdColumn,
|
||||
thread.uniqueId];
|
||||
|
||||
YapDatabaseQuery *query = [YapDatabaseQuery queryWithFormat:formattedString];
|
||||
[[transaction ext:OWSDisappearingMessageFinderExpiresAtIndex]
|
||||
enumerateKeysMatchingQuery:query
|
||||
usingBlock:^void(NSString *collection, NSString *key, BOOL *stop) {
|
||||
[messageIds addObject:key];
|
||||
}];
|
||||
|
||||
return [messageIds copy];
|
||||
}
|
||||
|
||||
- (NSArray<NSString *> *)fetchMessageIdsWhichFailedToStartExpiring:(YapDatabaseReadTransaction *_Nonnull)transaction
|
||||
{
|
||||
NSMutableArray<NSString *> *messageIds = [NSMutableArray new];
|
||||
NSString *formattedString =
|
||||
[NSString stringWithFormat:@"WHERE %@ = 0", OWSDisappearingMessageFinderExpiresAtColumn];
|
||||
|
||||
YapDatabaseQuery *query = [YapDatabaseQuery queryWithFormat:formattedString];
|
||||
[[transaction ext:OWSDisappearingMessageFinderExpiresAtIndex]
|
||||
enumerateKeysAndObjectsMatchingQuery:query
|
||||
usingBlock:^void(NSString *collection, NSString *key, id object, BOOL *stop) {
|
||||
if (![object isKindOfClass:[TSMessage class]]) {
|
||||
return;
|
||||
}
|
||||
|
||||
TSMessage *message = (TSMessage *)object;
|
||||
if ([message shouldStartExpireTimerWithTransaction:transaction]) {
|
||||
if ([message isKindOfClass:[TSIncomingMessage class]]) {
|
||||
TSIncomingMessage *incomingMessage = (TSIncomingMessage *)message;
|
||||
if (!incomingMessage.wasRead) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
[messageIds addObject:key];
|
||||
}
|
||||
}];
|
||||
|
||||
return [messageIds copy];
|
||||
}
|
||||
|
||||
- (NSArray<NSString *> *)fetchExpiredMessageIdsWithTransaction:(YapDatabaseReadTransaction *_Nonnull)transaction
|
||||
{
|
||||
NSMutableArray<NSString *> *messageIds = [NSMutableArray new];
|
||||
|
||||
uint64_t now = [NSDate ows_millisecondTimeStamp];
|
||||
// When (expiresAt == 0) the message SHOULD NOT expire. Careful ;)
|
||||
NSString *formattedString = [NSString stringWithFormat:@"WHERE %@ > 0 AND %@ <= %lld",
|
||||
OWSDisappearingMessageFinderExpiresAtColumn,
|
||||
OWSDisappearingMessageFinderExpiresAtColumn,
|
||||
now];
|
||||
YapDatabaseQuery *query = [YapDatabaseQuery queryWithFormat:formattedString];
|
||||
[[transaction ext:OWSDisappearingMessageFinderExpiresAtIndex]
|
||||
enumerateKeysMatchingQuery:query
|
||||
usingBlock:^void(NSString *collection, NSString *key, BOOL *stop) {
|
||||
[messageIds addObject:key];
|
||||
}];
|
||||
|
||||
return [messageIds copy];
|
||||
}
|
||||
|
||||
- (nullable NSNumber *)nextExpirationTimestampWithTransaction:(YapDatabaseReadTransaction *)transaction
|
||||
{
|
||||
NSString *formattedString = [NSString stringWithFormat:@"WHERE %@ > 0 ORDER BY %@ ASC",
|
||||
OWSDisappearingMessageFinderExpiresAtColumn,
|
||||
OWSDisappearingMessageFinderExpiresAtColumn];
|
||||
YapDatabaseQuery *query = [YapDatabaseQuery queryWithFormat:formattedString];
|
||||
|
||||
__block TSMessage *firstMessage;
|
||||
[[transaction ext:OWSDisappearingMessageFinderExpiresAtIndex]
|
||||
enumerateKeysAndObjectsMatchingQuery:query
|
||||
usingBlock:^void(NSString *collection, NSString *key, id object, BOOL *stop) {
|
||||
firstMessage = (TSMessage *)object;
|
||||
*stop = YES;
|
||||
}];
|
||||
|
||||
if (firstMessage && firstMessage.expiresAt > 0) {
|
||||
return [NSNumber numberWithUnsignedLongLong:firstMessage.expiresAt];
|
||||
}
|
||||
|
||||
return nil;
|
||||
}
|
||||
|
||||
- (void)enumerateUnstartedExpiringMessagesInThread:(TSThread *)thread
|
||||
block:(void (^_Nonnull)(TSMessage *message))block
|
||||
transaction:(YapDatabaseReadTransaction *)transaction
|
||||
{
|
||||
for (NSString *expiringMessageId in
|
||||
[self fetchUnstartedExpiringMessageIdsInThread:thread transaction:transaction]) {
|
||||
TSMessage *_Nullable message = [TSMessage fetchObjectWithUniqueID:expiringMessageId transaction:transaction];
|
||||
if ([message isKindOfClass:[TSMessage class]]) {
|
||||
block(message);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
- (void)enumerateMessagesWhichFailedToStartExpiringWithBlock:(void (^_Nonnull)(TSMessage *message))block
|
||||
transaction:(YapDatabaseReadTransaction *)transaction
|
||||
{
|
||||
for (NSString *expiringMessageId in [self fetchMessageIdsWhichFailedToStartExpiring:transaction]) {
|
||||
|
||||
TSMessage *_Nullable message = [TSMessage fetchObjectWithUniqueID:expiringMessageId transaction:transaction];
|
||||
if (![message isKindOfClass:[TSMessage class]]) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (![message shouldStartExpireTimerWithTransaction:transaction]) {
|
||||
continue;
|
||||
}
|
||||
|
||||
block(message);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Don't use this in production. Useful for testing.
|
||||
* We don't want to instantiate potentially many messages at once.
|
||||
*/
|
||||
- (NSArray<TSMessage *> *)fetchUnstartedExpiringMessagesInThread:(TSThread *)thread
|
||||
transaction:(YapDatabaseReadTransaction *)transaction
|
||||
{
|
||||
NSMutableArray<TSMessage *> *messages = [NSMutableArray new];
|
||||
[self enumerateUnstartedExpiringMessagesInThread:thread
|
||||
block:^(TSMessage *message) {
|
||||
[messages addObject:message];
|
||||
}
|
||||
transaction:transaction];
|
||||
|
||||
return [messages copy];
|
||||
}
|
||||
|
||||
|
||||
- (void)enumerateExpiredMessagesWithBlock:(void (^_Nonnull)(TSMessage *message))block
|
||||
transaction:(YapDatabaseReadTransaction *)transaction
|
||||
{
|
||||
// Since we can't directly mutate the enumerated expired messages, we store only their ids in hopes of saving a
|
||||
// little memory and then enumerate the (larger) TSMessage objects one at a time.
|
||||
for (NSString *expiredMessageId in [self fetchExpiredMessageIdsWithTransaction:transaction]) {
|
||||
TSMessage *_Nullable message = [TSMessage fetchObjectWithUniqueID:expiredMessageId transaction:transaction];
|
||||
if ([message isKindOfClass:[TSMessage class]]) {
|
||||
block(message);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Don't use this in production. Useful for testing.
|
||||
* We don't want to instantiate potentially many messages at once.
|
||||
*/
|
||||
- (NSArray<TSMessage *> *)fetchExpiredMessagesWithTransaction:(YapDatabaseReadTransaction *)transaction
|
||||
{
|
||||
NSMutableArray<TSMessage *> *messages = [NSMutableArray new];
|
||||
[self enumerateExpiredMessagesWithBlock:^(TSMessage *message) {
|
||||
[messages addObject:message];
|
||||
}
|
||||
transaction:transaction];
|
||||
|
||||
return [messages copy];
|
||||
}
|
||||
|
||||
#pragma mark - YapDatabaseExtension
|
||||
|
||||
+ (YapDatabaseSecondaryIndex *)indexDatabaseExtension
|
||||
{
|
||||
YapDatabaseSecondaryIndexSetup *setup = [YapDatabaseSecondaryIndexSetup new];
|
||||
[setup addColumn:OWSDisappearingMessageFinderExpiresAtColumn withType:YapDatabaseSecondaryIndexTypeInteger];
|
||||
[setup addColumn:OWSDisappearingMessageFinderThreadIdColumn withType:YapDatabaseSecondaryIndexTypeText];
|
||||
|
||||
YapDatabaseSecondaryIndexHandler *handler =
|
||||
[YapDatabaseSecondaryIndexHandler withObjectBlock:^(YapDatabaseReadTransaction *transaction,
|
||||
NSMutableDictionary *dict,
|
||||
NSString *collection,
|
||||
NSString *key,
|
||||
id object) {
|
||||
if (![object isKindOfClass:[TSMessage class]]) {
|
||||
return;
|
||||
}
|
||||
TSMessage *message = (TSMessage *)object;
|
||||
|
||||
if (![message shouldStartExpireTimerWithTransaction:transaction]) {
|
||||
return;
|
||||
}
|
||||
|
||||
dict[OWSDisappearingMessageFinderExpiresAtColumn] = @(message.expiresAt);
|
||||
dict[OWSDisappearingMessageFinderThreadIdColumn] = message.uniqueThreadId;
|
||||
}];
|
||||
|
||||
return [[YapDatabaseSecondaryIndex alloc] initWithSetup:setup handler:handler versionTag:@"1"];
|
||||
}
|
||||
|
||||
#ifdef DEBUG
|
||||
// Useful for tests, don't use in app startup path because it's slow.
|
||||
+ (void)blockingRegisterDatabaseExtensions:(OWSStorage *)storage
|
||||
{
|
||||
[storage registerExtension:[self indexDatabaseExtension] withName:OWSDisappearingMessageFinderExpiresAtIndex];
|
||||
}
|
||||
#endif
|
||||
|
||||
+ (NSString *)databaseExtensionName
|
||||
{
|
||||
return OWSDisappearingMessageFinderExpiresAtIndex;
|
||||
}
|
||||
|
||||
+ (void)asyncRegisterDatabaseExtensions:(OWSStorage *)storage
|
||||
{
|
||||
[storage asyncRegisterExtension:[self indexDatabaseExtension] withName:OWSDisappearingMessageFinderExpiresAtIndex];
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
@ -1,30 +0,0 @@
|
||||
//
|
||||
// Copyright (c) 2018 Open Whisper Systems. All rights reserved.
|
||||
//
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
@class OWSPrimaryStorage;
|
||||
@class OWSStorage;
|
||||
@class YapDatabaseReadTransaction;
|
||||
|
||||
@interface OWSIncomingMessageFinder : NSObject
|
||||
|
||||
- (instancetype)initWithPrimaryStorage:(OWSPrimaryStorage *)primaryStorage NS_DESIGNATED_INITIALIZER;
|
||||
|
||||
+ (NSString *)databaseExtensionName;
|
||||
+ (void)asyncRegisterExtensionWithPrimaryStorage:(OWSStorage *)storage;
|
||||
|
||||
/**
|
||||
* Detects existance of a duplicate incoming message.
|
||||
*/
|
||||
- (BOOL)existsMessageWithTimestamp:(uint64_t)timestamp
|
||||
sourceId:(NSString *)sourceId
|
||||
sourceDeviceId:(uint32_t)sourceDeviceId
|
||||
transaction:(YapDatabaseReadTransaction *)transaction;
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
@ -1,144 +0,0 @@
|
||||
//
|
||||
// Copyright (c) 2018 Open Whisper Systems. All rights reserved.
|
||||
//
|
||||
|
||||
#import "OWSIncomingMessageFinder.h"
|
||||
#import "OWSPrimaryStorage.h"
|
||||
#import "TSIncomingMessage.h"
|
||||
#import <YapDatabase/YapDatabase.h>
|
||||
#import <YapDatabase/YapDatabaseSecondaryIndex.h>
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
NSString *const OWSIncomingMessageFinderExtensionName = @"OWSIncomingMessageFinderExtensionName";
|
||||
|
||||
NSString *const OWSIncomingMessageFinderColumnTimestamp = @"OWSIncomingMessageFinderColumnTimestamp";
|
||||
NSString *const OWSIncomingMessageFinderColumnSourceId = @"OWSIncomingMessageFinderColumnSourceId";
|
||||
NSString *const OWSIncomingMessageFinderColumnSourceDeviceId = @"OWSIncomingMessageFinderColumnSourceDeviceId";
|
||||
|
||||
@interface OWSIncomingMessageFinder ()
|
||||
|
||||
@property (nonatomic, readonly) OWSPrimaryStorage *primaryStorage;
|
||||
@property (nonatomic, readonly) YapDatabaseConnection *dbConnection;
|
||||
|
||||
@end
|
||||
|
||||
@implementation OWSIncomingMessageFinder
|
||||
|
||||
@synthesize dbConnection = _dbConnection;
|
||||
|
||||
#pragma mark - init
|
||||
|
||||
- (instancetype)init
|
||||
{
|
||||
return [self initWithPrimaryStorage:[OWSPrimaryStorage sharedManager]];
|
||||
}
|
||||
|
||||
- (instancetype)initWithPrimaryStorage:(OWSPrimaryStorage *)primaryStorage
|
||||
{
|
||||
self = [super init];
|
||||
if (!self) {
|
||||
return self;
|
||||
}
|
||||
|
||||
_primaryStorage = primaryStorage;
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
#pragma mark - properties
|
||||
|
||||
- (YapDatabaseConnection *)dbConnection
|
||||
{
|
||||
@synchronized(self) {
|
||||
if (!_dbConnection) {
|
||||
_dbConnection = [self.primaryStorage newDatabaseConnection];
|
||||
}
|
||||
}
|
||||
return _dbConnection;
|
||||
}
|
||||
|
||||
#pragma mark - YAP integration
|
||||
|
||||
+ (YapDatabaseSecondaryIndex *)indexExtension
|
||||
{
|
||||
YapDatabaseSecondaryIndexSetup *setup = [YapDatabaseSecondaryIndexSetup new];
|
||||
|
||||
[setup addColumn:OWSIncomingMessageFinderColumnTimestamp withType:YapDatabaseSecondaryIndexTypeInteger];
|
||||
[setup addColumn:OWSIncomingMessageFinderColumnSourceId withType:YapDatabaseSecondaryIndexTypeText];
|
||||
[setup addColumn:OWSIncomingMessageFinderColumnSourceDeviceId withType:YapDatabaseSecondaryIndexTypeInteger];
|
||||
|
||||
YapDatabaseSecondaryIndexWithObjectBlock block = ^(YapDatabaseReadTransaction *transaction,
|
||||
NSMutableDictionary *dict,
|
||||
NSString *collection,
|
||||
NSString *key,
|
||||
id object) {
|
||||
if ([object isKindOfClass:[TSIncomingMessage class]]) {
|
||||
TSIncomingMessage *incomingMessage = (TSIncomingMessage *)object;
|
||||
|
||||
// On new messages authorId should be set on all incoming messages, but there was a time when authorId was
|
||||
// only set on incoming group messages.
|
||||
NSObject *authorIdOrNull = incomingMessage.authorId ? incomingMessage.authorId : [NSNull null];
|
||||
[dict setObject:@(incomingMessage.timestamp) forKey:OWSIncomingMessageFinderColumnTimestamp];
|
||||
[dict setObject:authorIdOrNull forKey:OWSIncomingMessageFinderColumnSourceId];
|
||||
[dict setObject:@(incomingMessage.sourceDeviceId) forKey:OWSIncomingMessageFinderColumnSourceDeviceId];
|
||||
}
|
||||
};
|
||||
|
||||
YapDatabaseSecondaryIndexHandler *handler = [YapDatabaseSecondaryIndexHandler withObjectBlock:block];
|
||||
|
||||
return [[YapDatabaseSecondaryIndex alloc] initWithSetup:setup handler:handler versionTag:nil];
|
||||
}
|
||||
|
||||
+ (NSString *)databaseExtensionName
|
||||
{
|
||||
return OWSIncomingMessageFinderExtensionName;
|
||||
}
|
||||
|
||||
+ (void)asyncRegisterExtensionWithPrimaryStorage:(OWSStorage *)storage
|
||||
{
|
||||
[storage asyncRegisterExtension:self.indexExtension withName:OWSIncomingMessageFinderExtensionName];
|
||||
}
|
||||
|
||||
#ifdef DEBUG
|
||||
// We should not normally hit this, as we should have prefer registering async, but it is useful for testing.
|
||||
- (void)registerExtension
|
||||
{
|
||||
[self.primaryStorage registerExtension:self.class.indexExtension withName:OWSIncomingMessageFinderExtensionName];
|
||||
}
|
||||
#endif
|
||||
|
||||
#pragma mark - instance methods
|
||||
|
||||
- (BOOL)existsMessageWithTimestamp:(uint64_t)timestamp
|
||||
sourceId:(NSString *)sourceId
|
||||
sourceDeviceId:(uint32_t)sourceDeviceId
|
||||
transaction:(YapDatabaseReadTransaction *)transaction
|
||||
{
|
||||
#ifdef DEBUG
|
||||
if (![self.primaryStorage registeredExtension:OWSIncomingMessageFinderExtensionName]) {
|
||||
|
||||
// we should be initializing this at startup rather than have an unexpectedly slow lazy setup at random.
|
||||
[self registerExtension];
|
||||
}
|
||||
#endif
|
||||
|
||||
NSString *queryFormat = [NSString stringWithFormat:@"WHERE %@ = ? AND %@ = ? AND %@ = ?",
|
||||
OWSIncomingMessageFinderColumnTimestamp,
|
||||
OWSIncomingMessageFinderColumnSourceId,
|
||||
OWSIncomingMessageFinderColumnSourceDeviceId];
|
||||
// YapDatabaseQuery params must be objects
|
||||
YapDatabaseQuery *query = [YapDatabaseQuery queryWithFormat:queryFormat, @(timestamp), sourceId, @(sourceDeviceId)];
|
||||
|
||||
NSUInteger count;
|
||||
BOOL success = [[transaction ext:OWSIncomingMessageFinderExtensionName] getNumberOfRows:&count matchingQuery:query];
|
||||
if (!success) {
|
||||
return NO;
|
||||
}
|
||||
|
||||
return count > 0;
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
@ -1,80 +0,0 @@
|
||||
//
|
||||
// Copyright (c) 2018 Open Whisper Systems. All rights reserved.
|
||||
//
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
@class ContactDiscoveryService;
|
||||
@class ContactsUpdater;
|
||||
@class OWS2FAManager;
|
||||
@class OWSAttachmentDownloads;
|
||||
@class OWSBatchMessageProcessor;
|
||||
@class OWSDisappearingMessagesJob;
|
||||
@class OWSIdentityManager;
|
||||
@class OWSMessageDecrypter;
|
||||
@class OWSMessageManager;
|
||||
@class OWSMessageReceiver;
|
||||
@class OWSMessageSender;
|
||||
@class OWSOutgoingReceiptManager;
|
||||
@class OWSPrimaryStorage;
|
||||
@class OWSReadReceiptManager;
|
||||
@class SSKMessageSenderJobQueue;
|
||||
@class TSAccountManager;
|
||||
@class TSSocketManager;
|
||||
@class YapDatabaseConnection;
|
||||
|
||||
@protocol ContactsManagerProtocol;
|
||||
@protocol NotificationsProtocol;
|
||||
@protocol OWSCallMessageHandler;
|
||||
@protocol ProfileManagerProtocol;
|
||||
@protocol OWSUDManager;
|
||||
@protocol SSKReachabilityManager;
|
||||
@protocol OWSSyncManagerProtocol;
|
||||
@protocol OWSTypingIndicators;
|
||||
|
||||
@interface SSKEnvironment : NSObject
|
||||
|
||||
- (instancetype)initWithPrimaryStorage:(OWSPrimaryStorage *)primaryStorage
|
||||
tsAccountManager:(TSAccountManager *)tsAccountManager
|
||||
disappearingMessagesJob:(OWSDisappearingMessagesJob *)disappearingMessagesJob
|
||||
readReceiptManager:(OWSReadReceiptManager *)readReceiptManager
|
||||
outgoingReceiptManager:(OWSOutgoingReceiptManager *)outgoingReceiptManager
|
||||
reachabilityManager:(id<SSKReachabilityManager>)reachabilityManager
|
||||
typingIndicators:(id<OWSTypingIndicators>)typingIndicators NS_DESIGNATED_INITIALIZER;
|
||||
|
||||
- (instancetype)init NS_UNAVAILABLE;
|
||||
|
||||
@property (nonatomic, readonly, class) SSKEnvironment *shared;
|
||||
|
||||
+ (void)setShared:(SSKEnvironment *)env;
|
||||
|
||||
#ifdef DEBUG
|
||||
// Should only be called by tests.
|
||||
+ (void)clearSharedForTests;
|
||||
#endif
|
||||
|
||||
@property (nonatomic, readonly) id<ProfileManagerProtocol> profileManager;
|
||||
@property (nonatomic, readonly) OWSPrimaryStorage *primaryStorage;
|
||||
@property (nonatomic, readonly) OWSIdentityManager *identityManager;
|
||||
@property (nonatomic, readonly) TSAccountManager *tsAccountManager;
|
||||
@property (nonatomic, readonly) OWSDisappearingMessagesJob *disappearingMessagesJob;
|
||||
@property (nonatomic, readonly) OWSReadReceiptManager *readReceiptManager;
|
||||
@property (nonatomic, readonly) OWSOutgoingReceiptManager *outgoingReceiptManager;
|
||||
@property (nonatomic, readonly) id<SSKReachabilityManager> reachabilityManager;
|
||||
@property (nonatomic, readonly) id<OWSTypingIndicators> typingIndicators;
|
||||
|
||||
// This property is configured after Environment is created.
|
||||
@property (atomic, nullable) id<NotificationsProtocol> notificationsManager;
|
||||
|
||||
@property (atomic, readonly) YapDatabaseConnection *objectReadWriteConnection;
|
||||
@property (atomic, readonly) YapDatabaseConnection *sessionStoreDBConnection;
|
||||
@property (atomic, readonly) YapDatabaseConnection *migrationDBConnection;
|
||||
@property (atomic, readonly) YapDatabaseConnection *analyticsDBConnection;
|
||||
|
||||
- (BOOL)isComplete;
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
@ -1,135 +0,0 @@
|
||||
//
|
||||
// Copyright (c) 2018 Open Whisper Systems. All rights reserved.
|
||||
//
|
||||
|
||||
#import "SSKEnvironment.h"
|
||||
#import "AppContext.h"
|
||||
#import "OWSPrimaryStorage.h"
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
static SSKEnvironment *sharedSSKEnvironment;
|
||||
|
||||
@interface SSKEnvironment ()
|
||||
|
||||
@property (nonatomic) OWSPrimaryStorage *primaryStorage;
|
||||
@property (nonatomic) TSAccountManager *tsAccountManager;
|
||||
@property (nonatomic) OWSDisappearingMessagesJob *disappearingMessagesJob;
|
||||
@property (nonatomic) OWSReadReceiptManager *readReceiptManager;
|
||||
@property (nonatomic) OWSOutgoingReceiptManager *outgoingReceiptManager;
|
||||
@property (nonatomic) id<SSKReachabilityManager> reachabilityManager;
|
||||
@property (nonatomic) id<OWSTypingIndicators> typingIndicators;
|
||||
|
||||
@end
|
||||
|
||||
#pragma mark -
|
||||
|
||||
@implementation SSKEnvironment
|
||||
|
||||
@synthesize notificationsManager = _notificationsManager;
|
||||
@synthesize objectReadWriteConnection = _objectReadWriteConnection;
|
||||
@synthesize sessionStoreDBConnection = _sessionStoreDBConnection;
|
||||
@synthesize migrationDBConnection = _migrationDBConnection;
|
||||
@synthesize analyticsDBConnection = _analyticsDBConnection;
|
||||
|
||||
- (instancetype)initWithPrimaryStorage:(OWSPrimaryStorage *)primaryStorage
|
||||
tsAccountManager:(TSAccountManager *)tsAccountManager
|
||||
disappearingMessagesJob:(OWSDisappearingMessagesJob *)disappearingMessagesJob
|
||||
readReceiptManager:(OWSReadReceiptManager *)readReceiptManager
|
||||
outgoingReceiptManager:(OWSOutgoingReceiptManager *)outgoingReceiptManager
|
||||
reachabilityManager:(id<SSKReachabilityManager>)reachabilityManager
|
||||
typingIndicators:(id<OWSTypingIndicators>)typingIndicators
|
||||
{
|
||||
self = [super init];
|
||||
|
||||
if (!self) {
|
||||
return self;
|
||||
}
|
||||
|
||||
_primaryStorage = primaryStorage;
|
||||
_tsAccountManager = tsAccountManager;
|
||||
_disappearingMessagesJob = disappearingMessagesJob;
|
||||
_readReceiptManager = readReceiptManager;
|
||||
_outgoingReceiptManager = outgoingReceiptManager;
|
||||
_reachabilityManager = reachabilityManager;
|
||||
_typingIndicators = typingIndicators;
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
+ (instancetype)shared
|
||||
{
|
||||
return sharedSSKEnvironment;
|
||||
}
|
||||
|
||||
+ (void)setShared:(SSKEnvironment *)env
|
||||
{
|
||||
sharedSSKEnvironment = env;
|
||||
}
|
||||
|
||||
+ (void)clearSharedForTests
|
||||
{
|
||||
sharedSSKEnvironment = nil;
|
||||
}
|
||||
|
||||
#pragma mark - Mutable Accessors
|
||||
|
||||
- (nullable id<NotificationsProtocol>)notificationsManager
|
||||
{
|
||||
@synchronized(self) {
|
||||
return _notificationsManager;
|
||||
}
|
||||
}
|
||||
|
||||
- (void)setNotificationsManager:(nullable id<NotificationsProtocol>)notificationsManager
|
||||
{
|
||||
@synchronized(self) {
|
||||
_notificationsManager = notificationsManager;
|
||||
}
|
||||
}
|
||||
|
||||
- (BOOL)isComplete
|
||||
{
|
||||
return self.notificationsManager != nil;
|
||||
}
|
||||
|
||||
- (YapDatabaseConnection *)objectReadWriteConnection
|
||||
{
|
||||
@synchronized(self) {
|
||||
if (!_objectReadWriteConnection) {
|
||||
_objectReadWriteConnection = self.primaryStorage.newDatabaseConnection;
|
||||
}
|
||||
return _objectReadWriteConnection;
|
||||
}
|
||||
}
|
||||
|
||||
- (YapDatabaseConnection *)sessionStoreDBConnection {
|
||||
@synchronized(self) {
|
||||
if (!_sessionStoreDBConnection) {
|
||||
_sessionStoreDBConnection = self.primaryStorage.newDatabaseConnection;
|
||||
}
|
||||
return _sessionStoreDBConnection;
|
||||
}
|
||||
}
|
||||
|
||||
- (YapDatabaseConnection *)migrationDBConnection {
|
||||
@synchronized(self) {
|
||||
if (!_migrationDBConnection) {
|
||||
_migrationDBConnection = self.primaryStorage.newDatabaseConnection;
|
||||
}
|
||||
return _migrationDBConnection;
|
||||
}
|
||||
}
|
||||
|
||||
- (YapDatabaseConnection *)analyticsDBConnection {
|
||||
@synchronized(self) {
|
||||
if (!_analyticsDBConnection) {
|
||||
_analyticsDBConnection = self.primaryStorage.newDatabaseConnection;
|
||||
}
|
||||
return _analyticsDBConnection;
|
||||
}
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
@ -1,31 +0,0 @@
|
||||
|
||||
final class ThreadUpdateBatcher {
|
||||
private var threadIDs: Set<String> = []
|
||||
|
||||
private lazy var timer = Timer.scheduledTimer(withTimeInterval: 0.25, repeats: true) { [weak self] _ in self?.touch() }
|
||||
|
||||
static let shared = ThreadUpdateBatcher()
|
||||
|
||||
private init() {
|
||||
DispatchQueue.main.async {
|
||||
SessionUtilitiesKit.touch(self.timer)
|
||||
}
|
||||
}
|
||||
|
||||
deinit { timer.invalidate() }
|
||||
|
||||
func touch(_ threadID: String) {
|
||||
threadIDs.insert(threadID)
|
||||
}
|
||||
|
||||
@objc private func touch() {
|
||||
let threadIDs = self.threadIDs
|
||||
self.threadIDs.removeAll()
|
||||
Storage.write { transaction in
|
||||
for threadID in threadIDs {
|
||||
guard let thread = TSThread.fetch(uniqueId: threadID, transaction: transaction) else { return }
|
||||
thread.touch(with: transaction)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Loading…
Reference in New Issue