mirror of https://github.com/oxen-io/session-ios
				
				
				
			
			You cannot select more than 25 topics
			Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
		
		
		
		
		
			
		
			
	
	
		
			242 lines
		
	
	
		
			10 KiB
		
	
	
	
		
			Matlab
		
	
		
		
			
		
	
	
			242 lines
		
	
	
		
			10 KiB
		
	
	
	
		
			Matlab
		
	
| 
								 
											5 years ago
										 
									 | 
							
								//
							 | 
						||
| 
								 | 
							
								//  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"
							 | 
						||
| 
								 
											5 years ago
										 
									 | 
							
								#import <SignalCoreKit/NSDate+OWS.h>
							 | 
						||
| 
								 
											5 years ago
										 
									 | 
							
								#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
							 |