|
|
|
@ -31,6 +31,11 @@
|
|
|
|
// * Orphan attachments (with no message).
|
|
|
|
// * Orphan attachments (with no message).
|
|
|
|
// * Orphan attachment files (with no attachment).
|
|
|
|
// * Orphan attachment files (with no attachment).
|
|
|
|
// * Missing attachment files (cannot be cleaned up).
|
|
|
|
// * Missing attachment files (cannot be cleaned up).
|
|
|
|
|
|
|
|
// These are attachments which have no file on disk. They should be extremely rare -
|
|
|
|
|
|
|
|
// the only cases I have seen are probably due to debugging.
|
|
|
|
|
|
|
|
// They can't be cleaned up - we don't want to delete the TSAttachmentStream or
|
|
|
|
|
|
|
|
// its corresponding message. Better that the broken message shows up in the
|
|
|
|
|
|
|
|
// conversation view.
|
|
|
|
+ (void)auditAndCleanup:(BOOL)shouldCleanup
|
|
|
|
+ (void)auditAndCleanup:(BOOL)shouldCleanup
|
|
|
|
{
|
|
|
|
{
|
|
|
|
NSString *attachmentsFolder = [TSAttachmentStream attachmentsFolder];
|
|
|
|
NSString *attachmentsFolder = [TSAttachmentStream attachmentsFolder];
|
|
|
|
@ -152,59 +157,61 @@
|
|
|
|
// being created/written, so we don't clean up anything recent.
|
|
|
|
// being created/written, so we don't clean up anything recent.
|
|
|
|
const NSTimeInterval kMinimumOrphanAge = 15 * 60.f;
|
|
|
|
const NSTimeInterval kMinimumOrphanAge = 15 * 60.f;
|
|
|
|
|
|
|
|
|
|
|
|
if (shouldCleanup) {
|
|
|
|
if (!shouldCleanup) {
|
|
|
|
[databaseConnection readWriteWithBlock:^(YapDatabaseReadWriteTransaction *_Nonnull transaction) {
|
|
|
|
return;
|
|
|
|
for (NSString *interactionId in orphanInteractionIds) {
|
|
|
|
}
|
|
|
|
TSInteraction *interaction = [TSInteraction fetchObjectWithUniqueID:interactionId];
|
|
|
|
|
|
|
|
if (!interaction) {
|
|
|
|
[databaseConnection readWriteWithBlock:^(YapDatabaseReadWriteTransaction *_Nonnull transaction) {
|
|
|
|
// This could just be a race condition, but it should be very unlikely.
|
|
|
|
for (NSString *interactionId in orphanInteractionIds) {
|
|
|
|
OWSFail(@"Could not load interaction: %@", interactionId);
|
|
|
|
TSInteraction *interaction = [TSInteraction fetchObjectWithUniqueID:interactionId];
|
|
|
|
continue;
|
|
|
|
if (!interaction) {
|
|
|
|
}
|
|
|
|
// This could just be a race condition, but it should be very unlikely.
|
|
|
|
DDLogInfo(@"Removing orphan message: %@", interaction.uniqueId);
|
|
|
|
OWSFail(@"Could not load interaction: %@", interactionId);
|
|
|
|
[interaction removeWithTransaction:transaction];
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
for (NSString *attachmentId in orphanAttachmentIds) {
|
|
|
|
DDLogInfo(@"Removing orphan message: %@", interaction.uniqueId);
|
|
|
|
TSAttachment *attachment = [TSAttachment fetchObjectWithUniqueID:attachmentId];
|
|
|
|
[interaction removeWithTransaction:transaction];
|
|
|
|
if (!attachment) {
|
|
|
|
}
|
|
|
|
// This could just be a race condition, but it should be very unlikely.
|
|
|
|
for (NSString *attachmentId in orphanAttachmentIds) {
|
|
|
|
OWSFail(@"Could not load attachment: %@", attachmentId);
|
|
|
|
TSAttachment *attachment = [TSAttachment fetchObjectWithUniqueID:attachmentId];
|
|
|
|
continue;
|
|
|
|
if (!attachment) {
|
|
|
|
}
|
|
|
|
// This could just be a race condition, but it should be very unlikely.
|
|
|
|
if (![attachment isKindOfClass:[TSAttachmentStream class]]) {
|
|
|
|
OWSFail(@"Could not load attachment: %@", attachmentId);
|
|
|
|
continue;
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
TSAttachmentStream *attachmentStream = (TSAttachmentStream *)attachment;
|
|
|
|
|
|
|
|
// Don't delete attachments which were created in the last N minutes.
|
|
|
|
|
|
|
|
if (fabs([attachmentStream.creationTimestamp timeIntervalSinceNow]) < kMinimumOrphanAge) {
|
|
|
|
|
|
|
|
DDLogInfo(@"Skipping orphan attachment due to age: %f",
|
|
|
|
|
|
|
|
fabs([attachmentStream.creationTimestamp timeIntervalSinceNow]));
|
|
|
|
|
|
|
|
continue;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
DDLogInfo(@"Removing orphan attachment: %@", attachmentStream.uniqueId);
|
|
|
|
|
|
|
|
[attachmentStream removeWithTransaction:transaction];
|
|
|
|
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}];
|
|
|
|
if (![attachment isKindOfClass:[TSAttachmentStream class]]) {
|
|
|
|
|
|
|
|
|
|
|
|
for (NSString *filePath in orphanDiskFilePaths) {
|
|
|
|
|
|
|
|
NSError *error;
|
|
|
|
|
|
|
|
NSDictionary *attributes = [[NSFileManager defaultManager] attributesOfItemAtPath:filePath error:&error];
|
|
|
|
|
|
|
|
if (!attributes || error) {
|
|
|
|
|
|
|
|
OWSFail(@"Could not get attributes of file at: %@", filePath);
|
|
|
|
|
|
|
|
continue;
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
// Don't delete files which were created in the last N minutes.
|
|
|
|
TSAttachmentStream *attachmentStream = (TSAttachmentStream *)attachment;
|
|
|
|
if (fabs([attributes.fileModificationDate timeIntervalSinceNow]) < kMinimumOrphanAge) {
|
|
|
|
// Don't delete attachments which were created in the last N minutes.
|
|
|
|
DDLogInfo(@"Skipping orphan attachment file due to age: %f",
|
|
|
|
if (fabs([attachmentStream.creationTimestamp timeIntervalSinceNow]) < kMinimumOrphanAge) {
|
|
|
|
fabs([attributes.fileModificationDate timeIntervalSinceNow]));
|
|
|
|
DDLogInfo(@"Skipping orphan attachment due to age: %f",
|
|
|
|
|
|
|
|
fabs([attachmentStream.creationTimestamp timeIntervalSinceNow]));
|
|
|
|
continue;
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
DDLogInfo(@"Removing orphan attachment: %@", attachmentStream.uniqueId);
|
|
|
|
|
|
|
|
[attachmentStream removeWithTransaction:transaction];
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
}];
|
|
|
|
|
|
|
|
|
|
|
|
DDLogInfo(@"Removing orphan attachment file: %@", filePath);
|
|
|
|
for (NSString *filePath in orphanDiskFilePaths) {
|
|
|
|
[[NSFileManager defaultManager] removeItemAtPath:filePath error:&error];
|
|
|
|
NSError *error;
|
|
|
|
if (error) {
|
|
|
|
NSDictionary *attributes = [[NSFileManager defaultManager] attributesOfItemAtPath:filePath error:&error];
|
|
|
|
OWSFail(@"Could not remove orphan file at: %@", filePath);
|
|
|
|
if (!attributes || error) {
|
|
|
|
}
|
|
|
|
OWSFail(@"Could not get attributes of file at: %@", filePath);
|
|
|
|
|
|
|
|
continue;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
// Don't delete files which were created in the last N minutes.
|
|
|
|
|
|
|
|
if (fabs([attributes.fileModificationDate timeIntervalSinceNow]) < kMinimumOrphanAge) {
|
|
|
|
|
|
|
|
DDLogInfo(@"Skipping orphan attachment file due to age: %f",
|
|
|
|
|
|
|
|
fabs([attributes.fileModificationDate timeIntervalSinceNow]));
|
|
|
|
|
|
|
|
continue;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
DDLogInfo(@"Removing orphan attachment file: %@", filePath);
|
|
|
|
|
|
|
|
[[NSFileManager defaultManager] removeItemAtPath:filePath error:&error];
|
|
|
|
|
|
|
|
if (error) {
|
|
|
|
|
|
|
|
OWSFail(@"Could not remove orphan file at: %@", filePath);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|