Signal-iOS/SignalServiceKit/Messages/Interactions/Quotes/TSQuotedMessage.m
2024-03-27 12:22:52 -05:00

261 lines
8.4 KiB
Objective-C

//
// Copyright 2018 Signal Messenger, LLC
// SPDX-License-Identifier: AGPL-3.0-only
//
#import "TSQuotedMessage.h"
#import "OWSPaymentMessage.h"
#import "TSAttachment.h"
#import "TSAttachmentPointer.h"
#import "TSAttachmentStream.h"
#import "TSIncomingMessage.h"
#import "TSInteraction.h"
#import "TSOutgoingMessage.h"
#import "TSThread.h"
#import <SignalServiceKit/SignalServiceKit-Swift.h>
NS_ASSUME_NONNULL_BEGIN
@interface OWSAttachmentInfo ()
@property (nonatomic, readonly, nullable) NSString *contentType;
@property (nonatomic, readonly, nullable) NSString *sourceFilename;
@end
@implementation OWSAttachmentInfo
- (nullable NSString *)attachmentId
{
return _rawAttachmentId.ows_nilIfEmpty;
}
- (nullable NSString *)stubMimeType
{
return _contentType;
}
- (nullable NSString *)stubSourceFilename
{
return _sourceFilename;
}
- (instancetype)initStubWithMimeType:(NSString *)mimeType sourceFilename:(NSString *_Nullable)sourceFilename
{
return [self initWithAttachmentId:nil
ofType:OWSAttachmentInfoReferenceUnset
contentType:mimeType
sourceFilename:sourceFilename];
}
- (instancetype)initForV2ThumbnailReference
{
return [self initWithAttachmentId:nil ofType:OWSAttachmentInfoReferenceV2 contentType:nil sourceFilename:nil];
}
- (instancetype)initWithLegacyAttachmentId:(NSString *)attachmentId ofType:(OWSAttachmentInfoReference)attachmentType
{
return [self initWithAttachmentId:attachmentId ofType:attachmentType contentType:nil sourceFilename:nil];
}
- (instancetype)initWithAttachmentId:(NSString *_Nullable)attachmentId
ofType:(OWSAttachmentInfoReference)attachmentType
contentType:(NSString *_Nullable)contentType
sourceFilename:(NSString *_Nullable)sourceFilename
{
self = [super init];
if (self) {
_schemaVersion = self.class.currentSchemaVersion;
_rawAttachmentId = attachmentId;
_attachmentType = attachmentType;
_contentType = contentType;
_sourceFilename = sourceFilename;
}
return self;
}
+ (NSUInteger)currentSchemaVersion
{
return 1;
}
- (nullable instancetype)initWithCoder:(NSCoder *)coder
{
self = [super initWithCoder:coder];
if (!self) {
return self;
}
if (_schemaVersion == 0) {
NSString *_Nullable oldStreamId = [coder decodeObjectOfClass:[NSString class]
forKey:@"thumbnailAttachmentStreamId"];
NSString *_Nullable oldPointerId = [coder decodeObjectOfClass:[NSString class]
forKey:@"thumbnailAttachmentPointerId"];
NSString *_Nullable oldSourceAttachmentId = [coder decodeObjectOfClass:[NSString class] forKey:@"attachmentId"];
// Before, we maintained each of these IDs in parallel, though in practice only one in use at a time.
// Migration codifies this behavior.
if (oldStreamId && [oldPointerId isEqualToString:oldStreamId]) {
_attachmentType = OWSAttachmentInfoReferenceThumbnail;
_rawAttachmentId = oldStreamId;
} else if (oldPointerId) {
_attachmentType = OWSAttachmentInfoReferenceUntrustedPointer;
_rawAttachmentId = oldPointerId;
} else if (oldStreamId) {
_attachmentType = OWSAttachmentInfoReferenceThumbnail;
_rawAttachmentId = oldStreamId;
} else if (oldSourceAttachmentId) {
_attachmentType = OWSAttachmentInfoReferenceOriginalForSend;
_rawAttachmentId = oldSourceAttachmentId;
} else {
_attachmentType = OWSAttachmentInfoReferenceUnset;
_rawAttachmentId = nil;
}
}
_schemaVersion = self.class.currentSchemaVersion;
return self;
}
@end
@interface TSQuotedMessage ()
@property (nonatomic, readonly) uint64_t timestamp;
@property (nonatomic, nullable) OWSAttachmentInfo *quotedAttachment;
@end
@implementation TSQuotedMessage
// Public
- (instancetype)initWithTimestamp:(uint64_t)timestamp
authorAddress:(SignalServiceAddress *)authorAddress
body:(nullable NSString *)body
bodyRanges:(nullable MessageBodyRanges *)bodyRanges
bodySource:(TSQuotedMessageContentSource)bodySource
receivedQuotedAttachmentInfo:(nullable OWSAttachmentInfo *)attachmentInfo
isGiftBadge:(BOOL)isGiftBadge
{
OWSAssertDebug(authorAddress.isValid);
self = [super init];
if (!self) {
return nil;
}
_timestamp = timestamp;
_authorAddress = authorAddress;
_body = body;
_bodyRanges = bodyRanges;
_bodySource = bodySource;
_quotedAttachment = attachmentInfo;
_isGiftBadge = isGiftBadge;
return self;
}
// Public
- (instancetype)initWithTimestamp:(nullable NSNumber *)timestamp
authorAddress:(SignalServiceAddress *)authorAddress
body:(nullable NSString *)body
bodyRanges:(nullable MessageBodyRanges *)bodyRanges
quotedAttachmentForSending:(nullable OWSAttachmentInfo *)attachmentInfo
isGiftBadge:(BOOL)isGiftBadge
{
OWSAssertDebug(authorAddress.isValid);
self = [super init];
if (!self) {
return nil;
}
if (timestamp) {
OWSAssertDebug(timestamp > 0);
_timestamp = [timestamp unsignedLongLongValue];
} else {
_timestamp = 0;
}
_authorAddress = authorAddress;
_body = body;
_bodyRanges = bodyRanges;
_bodySource = TSQuotedMessageContentSourceLocal;
_quotedAttachment = attachmentInfo;
_isGiftBadge = isGiftBadge;
return self;
}
- (id)initWithCoder:(NSCoder *)coder
{
self = [super initWithCoder:coder];
if (!self) {
return self;
}
if (_authorAddress == nil) {
NSString *phoneNumber = [coder decodeObjectForKey:@"authorId"];
_authorAddress = [SignalServiceAddress legacyAddressWithServiceIdString:nil phoneNumber:phoneNumber];
OWSAssertDebug(_authorAddress.isValid);
}
if (_quotedAttachment == nil) {
NSSet *expectedClasses = [NSSet setWithArray:@[ [NSArray class], [OWSAttachmentInfo class] ]];
NSArray *_Nullable attachmentInfos = [coder decodeObjectOfClasses:expectedClasses forKey:@"quotedAttachments"];
if ([attachmentInfos.firstObject isKindOfClass:[OWSAttachmentInfo class]]) {
// In practice, we only used the first item of this array
OWSAssertDebug(attachmentInfos.count <= 1);
_quotedAttachment = attachmentInfos.firstObject;
}
}
return self;
}
+ (instancetype)quotedMessageWithTargetMessageTimestamp:(nullable NSNumber *)timestamp
authorAddress:(SignalServiceAddress *)authorAddress
body:(NSString *)body
bodyRanges:(nullable MessageBodyRanges *)bodyRanges
bodySource:(TSQuotedMessageContentSource)bodySource
isGiftBadge:(BOOL)isGiftBadge
{
OWSAssertDebug(authorAddress.isValid);
uint64_t rawTimestamp;
if (timestamp) {
OWSAssertDebug(timestamp > 0);
rawTimestamp = [timestamp unsignedLongLongValue];
} else {
rawTimestamp = 0;
}
return [[TSQuotedMessage alloc] initWithTimestamp:rawTimestamp
authorAddress:authorAddress
body:body
bodyRanges:bodyRanges
bodySource:bodySource
receivedQuotedAttachmentInfo:nil
isGiftBadge:isGiftBadge];
}
- (nullable NSNumber *)getTimestampValue
{
if (_timestamp == 0) {
return nil;
}
return [[NSNumber alloc] initWithUnsignedLongLong:_timestamp];
}
#pragma mark - Attachment (not necessarily with a thumbnail)
- (nullable OWSAttachmentInfo *)attachmentInfo
{
return _quotedAttachment;
}
- (void)setLegacyThumbnailAttachmentStream:(TSAttachmentStream *)attachmentStream
{
self.quotedAttachment.attachmentType = OWSAttachmentInfoReferenceThumbnail;
self.quotedAttachment.rawAttachmentId = attachmentStream.uniqueId;
}
@end
NS_ASSUME_NONNULL_END