2017-07-05 22:58:39 +02:00
|
|
|
//
|
|
|
|
// Copyright (c) 2017 Open Whisper Systems. All rights reserved.
|
|
|
|
//
|
2016-07-28 04:29:27 +02:00
|
|
|
|
|
|
|
#import "OWSOrphanedDataCleaner.h"
|
|
|
|
#import "TSAttachmentStream.h"
|
|
|
|
#import "TSInteraction.h"
|
|
|
|
#import "TSMessage.h"
|
|
|
|
#import "TSStorageManager.h"
|
|
|
|
#import "TSThread.h"
|
|
|
|
|
|
|
|
@implementation OWSOrphanedDataCleaner
|
|
|
|
|
|
|
|
- (void)removeOrphanedData
|
|
|
|
{
|
|
|
|
// Remove interactions whose threads have been deleted
|
|
|
|
for (NSString *interactionId in [self orphanedInteractionIds]) {
|
|
|
|
DDLogWarn(@"Removing orphaned interaction with id: %@", interactionId);
|
|
|
|
TSInteraction *interaction = [TSInteraction fetchObjectWithUniqueID:interactionId];
|
|
|
|
[interaction remove];
|
|
|
|
}
|
|
|
|
|
|
|
|
// Remove any lingering attachments
|
|
|
|
for (NSString *path in [self orphanedFilePaths]) {
|
|
|
|
DDLogWarn(@"Removing orphaned file attachment at path: %@", path);
|
|
|
|
NSError *error;
|
|
|
|
[[NSFileManager defaultManager] removeItemAtPath:path error:&error];
|
|
|
|
if (error) {
|
|
|
|
DDLogError(@"Unable to remove orphaned file attachment at path:%@", path);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
- (NSArray<NSString *> *)orphanedInteractionIds
|
|
|
|
{
|
|
|
|
NSMutableArray *interactionIds = [NSMutableArray new];
|
2017-07-05 22:58:39 +02:00
|
|
|
[[TSInteraction dbReadConnection] readWithBlock:^(YapDatabaseReadTransaction *_Nonnull transaction) {
|
2016-07-28 04:29:27 +02:00
|
|
|
[TSInteraction enumerateCollectionObjectsWithTransaction:transaction
|
|
|
|
usingBlock:^(TSInteraction *interaction, BOOL *stop) {
|
|
|
|
TSThread *thread = [TSThread
|
|
|
|
fetchObjectWithUniqueID:interaction.uniqueThreadId
|
|
|
|
transaction:transaction];
|
|
|
|
if (!thread) {
|
|
|
|
[interactionIds addObject:interaction.uniqueId];
|
|
|
|
}
|
|
|
|
}];
|
|
|
|
}];
|
|
|
|
|
|
|
|
|
|
|
|
return [interactionIds copy];
|
|
|
|
}
|
|
|
|
|
|
|
|
- (NSArray<NSString *> *)orphanedFilePaths
|
|
|
|
{
|
|
|
|
NSError *error;
|
|
|
|
NSMutableArray<NSString *> *filenames =
|
|
|
|
[[[NSFileManager defaultManager] contentsOfDirectoryAtPath:[TSAttachmentStream attachmentsFolder] error:&error]
|
|
|
|
mutableCopy];
|
|
|
|
if (error) {
|
|
|
|
DDLogError(@"error getting orphanedFilePaths:%@", error);
|
|
|
|
return @[];
|
|
|
|
}
|
|
|
|
|
|
|
|
NSMutableDictionary<NSString *, NSString *> *attachmentIdFilenames = [NSMutableDictionary new];
|
|
|
|
for (NSString *filename in filenames) {
|
|
|
|
// Remove extension from (e.g.) 1234.png to get the attachmentId "1234"
|
|
|
|
NSString *attachmentId = [filename stringByDeletingPathExtension];
|
|
|
|
attachmentIdFilenames[attachmentId] = filename;
|
|
|
|
}
|
|
|
|
|
|
|
|
[TSInteraction enumerateCollectionObjectsUsingBlock:^(TSInteraction *interaction, BOOL *stop) {
|
|
|
|
if ([interaction isKindOfClass:[TSMessage class]]) {
|
|
|
|
TSMessage *message = (TSMessage *)interaction;
|
|
|
|
if ([message hasAttachments]) {
|
|
|
|
for (NSString *attachmentId in message.attachmentIds) {
|
|
|
|
[attachmentIdFilenames removeObjectForKey:attachmentId];
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}];
|
|
|
|
|
|
|
|
NSArray<NSString *> *filenamesToDelete = [attachmentIdFilenames allValues];
|
|
|
|
NSMutableArray<NSString *> *absolutePathsToDelete = [NSMutableArray arrayWithCapacity:[filenamesToDelete count]];
|
|
|
|
for (NSString *filename in filenamesToDelete) {
|
|
|
|
NSString *absolutePath = [[TSAttachmentStream attachmentsFolder] stringByAppendingFormat:@"/%@", filename];
|
|
|
|
[absolutePathsToDelete addObject:absolutePath];
|
|
|
|
}
|
|
|
|
|
|
|
|
return [absolutePathsToDelete copy];
|
|
|
|
}
|
|
|
|
|
|
|
|
@end
|