// // Copyright (c) 2021 Open Whisper Systems. All rights reserved. // #import "ThreadUtil.h" #import "OWSProfileManager.h" #import #import #import #import #import #import #import #import #import #import #import #import #import #import NS_ASSUME_NONNULL_BEGIN @implementation ThreadUtil #pragma mark - Durable Message Enqueue + (TSOutgoingMessage *)enqueueMessageWithInstalledSticker:(StickerInfo *)stickerInfo thread:(TSThread *)thread { OWSAssertIsOnMainThread(); OWSAssertDebug(stickerInfo != nil); OWSAssertDebug(thread != nil); TSOutgoingMessage *message = [self buildOutgoingMessageForSticker:stickerInfo thread:thread]; dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ // Load the sticker data async. StickerMetadata *_Nullable stickerMetadata; stickerMetadata = [StickerManager installedStickerMetadataWithSneakyTransaction:stickerInfo]; if (stickerMetadata == nil) { OWSFailDebug(@"Could not find sticker file."); return; } NSData *_Nullable stickerData = [NSData dataWithContentsOfURL:stickerMetadata.stickerDataUrl]; if (!stickerData) { OWSFailDebug(@"Couldn't load sticker data."); return; } MessageStickerDraft *stickerDraft = [[MessageStickerDraft alloc] initWithInfo:stickerInfo stickerData:stickerData stickerType:stickerMetadata.stickerType emoji:stickerMetadata.firstEmoji]; [self enqueueMessage:message stickerDraft:stickerDraft thread:thread]; }); return message; } + (TSOutgoingMessage *)enqueueMessageWithUninstalledSticker:(StickerMetadata *)stickerMetadata stickerData:(NSData *)stickerData thread:(TSThread *)thread { OWSAssertIsOnMainThread(); OWSAssertDebug(stickerMetadata != nil); OWSAssertDebug(stickerData.length > 0); OWSAssertDebug(thread != nil); TSOutgoingMessage *message = [self buildOutgoingMessageForSticker:stickerMetadata.stickerInfo thread:thread]; MessageStickerDraft *stickerDraft = [[MessageStickerDraft alloc] initWithInfo:stickerMetadata.stickerInfo stickerData:stickerData stickerType:stickerMetadata.stickerType emoji:stickerMetadata.firstEmoji]; [self enqueueMessage:message stickerDraft:stickerDraft thread:thread]; return message; } + (TSOutgoingMessage *)buildOutgoingMessageForSticker:(StickerInfo *)stickerInfo thread:(TSThread *)thread { OWSAssertIsOnMainThread(); OWSAssertDebug(stickerInfo != nil); OWSAssertDebug(thread != nil); __block OWSDisappearingMessagesConfiguration *configuration; [self.databaseStorage readWithBlock:^(SDSAnyReadTransaction *transaction) { configuration = [thread disappearingMessagesConfigurationWithTransaction:transaction]; }]; uint32_t expiresInSeconds = (configuration.isEnabled ? configuration.durationSeconds : 0); TSOutgoingMessageBuilder *builder = [TSOutgoingMessageBuilder outgoingMessageBuilderWithThread:thread]; builder.expiresInSeconds = expiresInSeconds; return [builder build]; } + (void)enqueueMessage:(TSOutgoingMessage *)message stickerDraft:(MessageStickerDraft *)stickerDraft thread:(TSThread *)thread { OWSAssertDebug(message != nil); OWSAssertDebug(stickerDraft != nil); OWSAssertDebug(thread != nil); [self enqueueSendAsyncWrite:^(SDSAnyWriteTransaction *transaction) { MessageSticker *_Nullable messageSticker = [self messageStickerForStickerDraft:stickerDraft transaction:transaction]; if (!messageSticker) { OWSFailDebug(@"Couldn't send sticker."); return; } [message anyInsertWithTransaction:transaction]; [message updateWithMessageSticker:messageSticker transaction:transaction]; [self.messageSenderJobQueue addMessage:message.asPreparer transaction:transaction]; [thread donateSendMessageIntentWithTransaction:transaction]; }]; } + (nullable MessageSticker *)messageStickerForStickerDraft:(MessageStickerDraft *)stickerDraft transaction:(SDSAnyWriteTransaction *)transaction { OWSAssertDebug(transaction); NSError *error; MessageSticker *_Nullable messageSticker = [MessageSticker buildValidatedMessageStickerFromDraft:stickerDraft transaction:transaction error:&error]; if (error && ![MessageSticker isNoStickerError:error]) { OWSFailDebug(@"error: %@", error); } return messageSticker; } #pragma mark - Profile Whitelist + (BOOL)addThreadToProfileWhitelistIfEmptyOrPendingRequestAndSetDefaultTimerWithSneakyTransaction:(TSThread *)thread { OWSAssertDebug(thread); __block BOOL hasPendingMessageRequest; __block BOOL needsDefaultTimerSet; __block DisappearingMessageToken *defaultTimerToken; [self.databaseStorage readWithBlock:^(SDSAnyReadTransaction *transaction) { hasPendingMessageRequest = [thread hasPendingMessageRequestWithTransaction:transaction.unwrapGrdbRead]; defaultTimerToken = [OWSDisappearingMessagesConfiguration fetchOrBuildDefaultUniversalConfigurationWithTransaction:transaction] .asToken; needsDefaultTimerSet = [GRDBThreadFinder shouldSetDefaultDisappearingMessageTimerWithThread:thread transaction:transaction.unwrapGrdbRead]; }]; if (needsDefaultTimerSet) { DatabaseStorageWrite(self.databaseStorage, ^(SDSAnyWriteTransaction *transaction) { OWSDisappearingMessagesConfiguration *configuration = [OWSDisappearingMessagesConfiguration applyToken:defaultTimerToken toThread:thread transaction:transaction]; OWSDisappearingConfigurationUpdateInfoMessage *infoMessage = [[OWSDisappearingConfigurationUpdateInfoMessage alloc] initWithThread:thread configuration:configuration createdByRemoteName:nil createdInExistingGroup:NO]; [infoMessage anyInsertWithTransaction:transaction]; }); } // If we're creating this thread or we have a pending message request, // any action we trigger should share our profile. if (!thread.shouldThreadBeVisible || hasPendingMessageRequest) { [OWSProfileManager.shared addThreadToProfileWhitelist:thread]; return YES; } return NO; } + (BOOL)addThreadToProfileWhitelistIfEmptyOrPendingRequestAndSetDefaultTimer:(TSThread *)thread transaction:(SDSAnyWriteTransaction *)transaction { OWSAssertDebug(thread); DisappearingMessageToken *defaultTimerToken = [OWSDisappearingMessagesConfiguration fetchOrBuildDefaultUniversalConfigurationWithTransaction:transaction] .asToken; BOOL needsDefaultTimerSet = [GRDBThreadFinder shouldSetDefaultDisappearingMessageTimerWithThread:thread transaction:transaction.unwrapGrdbRead]; if (needsDefaultTimerSet) { OWSDisappearingMessagesConfiguration *configuration = [OWSDisappearingMessagesConfiguration applyToken:defaultTimerToken toThread:thread transaction:transaction]; OWSDisappearingConfigurationUpdateInfoMessage *infoMessage = [[OWSDisappearingConfigurationUpdateInfoMessage alloc] initWithThread:thread configuration:configuration createdByRemoteName:nil createdInExistingGroup:NO]; [infoMessage anyInsertWithTransaction:transaction]; } BOOL hasPendingMessageRequest = [thread hasPendingMessageRequestWithTransaction:transaction.unwrapGrdbRead]; // If we're creating this thread or we have a pending message request, // any action we trigger should share our profile. if (!thread.shouldThreadBeVisible || hasPendingMessageRequest) { [OWSProfileManager.shared addThreadToProfileWhitelist:thread transaction:transaction]; return YES; } return NO; } #pragma mark - Delete Content + (void)deleteAllContent { OWSLogInfo(@""); DatabaseStorageWrite(self.databaseStorage, ^(SDSAnyWriteTransaction *transaction) { [TSThread anyEnumerateWithTransaction:transaction batched:YES block:^(TSThread *thread, BOOL *stop) { [thread softDeleteThreadWithTransaction:transaction]; }]; [TSInteraction anyRemoveAllWithInstantationWithTransaction:transaction]; [TSAttachment anyRemoveAllWithInstantationWithTransaction:transaction]; // Deleting attachments above should be enough to remove any gallery items, but // we redunantly clean up *all* gallery items to be safe. [MediaGalleryManager didRemoveAllContentWithTransaction:transaction]; }); [TSAttachmentStream deleteAttachmentsFromDisk]; } @end NS_ASSUME_NONNULL_END