2017-03-15 14:17:41 +01:00
|
|
|
//
|
2019-02-23 01:37:03 +01:00
|
|
|
// Copyright (c) 2019 Open Whisper Systems. All rights reserved.
|
2017-03-15 14:17:41 +01:00
|
|
|
//
|
2015-12-07 03:31:43 +01:00
|
|
|
|
|
|
|
#import "TSAttachment.h"
|
2016-07-28 01:58:49 +02:00
|
|
|
#import "MIMETypeUtil.h"
|
2018-12-19 22:35:43 +01:00
|
|
|
#import "NSString+SSK.h"
|
2018-11-19 20:28:28 +01:00
|
|
|
#import "TSAttachmentPointer.h"
|
2018-11-07 18:00:34 +01:00
|
|
|
#import "TSMessage.h"
|
2018-09-21 21:41:10 +02:00
|
|
|
#import <SignalCoreKit/iOSVersions.h>
|
2015-12-07 03:31:43 +01:00
|
|
|
|
2016-10-14 23:00:29 +02:00
|
|
|
NS_ASSUME_NONNULL_BEGIN
|
|
|
|
|
2017-10-10 22:13:54 +02:00
|
|
|
NSUInteger const TSAttachmentSchemaVersion = 4;
|
2016-10-14 23:00:29 +02:00
|
|
|
|
|
|
|
@interface TSAttachment ()
|
|
|
|
|
|
|
|
@property (nonatomic, readonly) NSUInteger attachmentSchemaVersion;
|
|
|
|
|
2018-02-16 02:45:28 +01:00
|
|
|
@property (nonatomic, nullable) NSString *sourceFilename;
|
|
|
|
|
2018-02-16 17:34:08 +01:00
|
|
|
@property (nonatomic) NSString *contentType;
|
|
|
|
|
2016-10-14 23:00:29 +02:00
|
|
|
@end
|
|
|
|
|
2015-12-07 03:31:43 +01:00
|
|
|
@implementation TSAttachment
|
|
|
|
|
2017-03-13 22:33:54 +01:00
|
|
|
// This constructor is used for new instances of TSAttachmentPointer,
|
|
|
|
// i.e. undownloaded incoming attachments.
|
2016-10-14 23:00:29 +02:00
|
|
|
- (instancetype)initWithServerId:(UInt64)serverId
|
2019-10-21 00:32:28 +02:00
|
|
|
encryptionKey:(nullable NSData *)encryptionKey
|
2017-10-27 00:08:25 +02:00
|
|
|
byteCount:(UInt32)byteCount
|
2016-10-14 23:00:29 +02:00
|
|
|
contentType:(NSString *)contentType
|
2017-05-15 15:53:16 +02:00
|
|
|
sourceFilename:(nullable NSString *)sourceFilename
|
2018-11-06 23:30:09 +01:00
|
|
|
caption:(nullable NSString *)caption
|
2018-11-07 17:42:28 +01:00
|
|
|
albumMessageId:(nullable NSString *)albumMessageId
|
2016-10-14 23:00:29 +02:00
|
|
|
{
|
2018-09-06 19:01:24 +02:00
|
|
|
OWSAssertDebug(serverId > 0);
|
2019-10-21 00:32:28 +02:00
|
|
|
// OWSAssertDebug(encryptionKey.length > 0);
|
2017-10-27 00:08:25 +02:00
|
|
|
if (byteCount <= 0) {
|
|
|
|
// This will fail with legacy iOS clients which don't upload attachment size.
|
2018-08-27 18:51:32 +02:00
|
|
|
OWSLogWarn(@"Missing byteCount for attachment with serverId: %lld", serverId);
|
2017-10-27 00:08:25 +02:00
|
|
|
}
|
2017-11-03 20:03:17 +01:00
|
|
|
if (contentType.length < 1) {
|
2018-08-27 18:51:32 +02:00
|
|
|
OWSLogWarn(@"incoming attachment has invalid content type");
|
2017-11-03 20:03:17 +01:00
|
|
|
|
2017-11-03 20:24:22 +01:00
|
|
|
contentType = OWSMimeTypeApplicationOctetStream;
|
2017-11-03 20:03:17 +01:00
|
|
|
}
|
2018-09-06 19:01:24 +02:00
|
|
|
OWSAssertDebug(contentType.length > 0);
|
2017-10-27 00:08:25 +02:00
|
|
|
|
2016-10-14 23:00:29 +02:00
|
|
|
self = [super init];
|
|
|
|
if (!self) {
|
|
|
|
return self;
|
|
|
|
}
|
|
|
|
|
|
|
|
_serverId = serverId;
|
|
|
|
_encryptionKey = encryptionKey;
|
2017-10-27 00:08:25 +02:00
|
|
|
_byteCount = byteCount;
|
2016-10-14 23:00:29 +02:00
|
|
|
_contentType = contentType;
|
2017-05-15 15:53:16 +02:00
|
|
|
_sourceFilename = sourceFilename;
|
2018-11-06 23:30:09 +01:00
|
|
|
_caption = caption;
|
2018-11-09 22:09:26 +01:00
|
|
|
_albumMessageId = albumMessageId;
|
2016-10-14 23:00:29 +02:00
|
|
|
|
2017-10-27 00:08:25 +02:00
|
|
|
_attachmentSchemaVersion = TSAttachmentSchemaVersion;
|
|
|
|
|
2016-10-14 23:00:29 +02:00
|
|
|
return self;
|
|
|
|
}
|
|
|
|
|
2018-11-19 17:38:29 +01:00
|
|
|
// This constructor is used for new instances of TSAttachmentPointer,
|
|
|
|
// i.e. undownloaded restoring attachments.
|
2018-11-21 18:25:24 +01:00
|
|
|
- (instancetype)initForRestoreWithUniqueId:(NSString *)uniqueId
|
|
|
|
contentType:(NSString *)contentType
|
|
|
|
sourceFilename:(nullable NSString *)sourceFilename
|
|
|
|
caption:(nullable NSString *)caption
|
|
|
|
albumMessageId:(nullable NSString *)albumMessageId
|
2018-11-19 17:38:29 +01:00
|
|
|
{
|
2018-11-19 20:28:28 +01:00
|
|
|
OWSAssertDebug(uniqueId.length > 0);
|
2018-11-19 17:38:29 +01:00
|
|
|
if (contentType.length < 1) {
|
|
|
|
OWSLogWarn(@"incoming attachment has invalid content type");
|
|
|
|
|
|
|
|
contentType = OWSMimeTypeApplicationOctetStream;
|
|
|
|
}
|
|
|
|
OWSAssertDebug(contentType.length > 0);
|
|
|
|
|
2018-11-21 18:25:24 +01:00
|
|
|
// If saved, this AttachmentPointer would replace the AttachmentStream in the attachments collection.
|
|
|
|
// However we only use this AttachmentPointer should only be used during the export process so it
|
|
|
|
// won't be saved until we restore the backup (when there will be no AttachmentStream to replace).
|
2018-11-19 20:28:28 +01:00
|
|
|
self = [super initWithUniqueId:uniqueId];
|
2018-11-19 17:38:29 +01:00
|
|
|
if (!self) {
|
|
|
|
return self;
|
|
|
|
}
|
|
|
|
|
|
|
|
_contentType = contentType;
|
|
|
|
_sourceFilename = sourceFilename;
|
|
|
|
_caption = caption;
|
|
|
|
_albumMessageId = albumMessageId;
|
|
|
|
|
|
|
|
_attachmentSchemaVersion = TSAttachmentSchemaVersion;
|
|
|
|
|
|
|
|
return self;
|
|
|
|
}
|
|
|
|
|
2017-03-13 22:33:54 +01:00
|
|
|
// This constructor is used for new instances of TSAttachmentStream
|
|
|
|
// that represent new, un-uploaded outgoing attachments.
|
2017-10-27 00:08:25 +02:00
|
|
|
- (instancetype)initWithContentType:(NSString *)contentType
|
|
|
|
byteCount:(UInt32)byteCount
|
|
|
|
sourceFilename:(nullable NSString *)sourceFilename
|
2018-11-06 23:30:09 +01:00
|
|
|
caption:(nullable NSString *)caption
|
2018-11-07 17:42:28 +01:00
|
|
|
albumMessageId:(nullable NSString *)albumMessageId
|
2017-03-13 22:33:54 +01:00
|
|
|
{
|
2017-11-03 20:03:17 +01:00
|
|
|
if (contentType.length < 1) {
|
2018-08-27 18:51:32 +02:00
|
|
|
OWSLogWarn(@"outgoing attachment has invalid content type");
|
2017-11-03 20:03:17 +01:00
|
|
|
|
2017-11-03 20:24:22 +01:00
|
|
|
contentType = OWSMimeTypeApplicationOctetStream;
|
2017-11-03 20:03:17 +01:00
|
|
|
}
|
2018-09-06 19:01:24 +02:00
|
|
|
OWSAssertDebug(contentType.length > 0);
|
2017-10-27 00:08:25 +02:00
|
|
|
|
2017-03-13 22:33:54 +01:00
|
|
|
self = [super init];
|
|
|
|
if (!self) {
|
|
|
|
return self;
|
|
|
|
}
|
2018-08-27 18:51:32 +02:00
|
|
|
OWSLogVerbose(@"init attachment with uniqueId: %@", self.uniqueId);
|
2017-03-13 22:33:54 +01:00
|
|
|
|
|
|
|
_contentType = contentType;
|
2017-10-27 00:08:25 +02:00
|
|
|
_byteCount = byteCount;
|
2017-05-15 15:53:16 +02:00
|
|
|
_sourceFilename = sourceFilename;
|
2018-11-06 23:30:09 +01:00
|
|
|
_caption = caption;
|
2018-11-07 17:42:28 +01:00
|
|
|
_albumMessageId = albumMessageId;
|
2017-03-13 22:33:54 +01:00
|
|
|
|
2017-10-27 00:08:25 +02:00
|
|
|
_attachmentSchemaVersion = TSAttachmentSchemaVersion;
|
|
|
|
|
2017-03-13 22:33:54 +01:00
|
|
|
return self;
|
|
|
|
}
|
|
|
|
|
|
|
|
// This constructor is used for new instances of TSAttachmentStream
|
|
|
|
// that represent downloaded incoming attachments.
|
2018-11-19 20:28:28 +01:00
|
|
|
- (instancetype)initWithPointer:(TSAttachmentPointer *)pointer
|
2017-03-13 22:33:54 +01:00
|
|
|
{
|
2018-11-19 20:28:28 +01:00
|
|
|
if (!pointer.lazyRestoreFragment) {
|
|
|
|
OWSAssertDebug(pointer.serverId > 0);
|
2019-10-21 01:12:39 +02:00
|
|
|
// OWSAssertDebug(pointer.encryptionKey.length > 0);
|
2018-11-19 20:28:28 +01:00
|
|
|
if (pointer.byteCount <= 0) {
|
|
|
|
// This will fail with legacy iOS clients which don't upload attachment size.
|
|
|
|
OWSLogWarn(@"Missing pointer.byteCount for attachment with serverId: %lld", pointer.serverId);
|
|
|
|
}
|
2017-10-27 00:08:25 +02:00
|
|
|
}
|
2018-09-06 19:01:24 +02:00
|
|
|
OWSAssertDebug(pointer.contentType.length > 0);
|
2017-10-27 00:08:25 +02:00
|
|
|
|
2017-03-13 22:33:54 +01:00
|
|
|
// Once saved, this AttachmentStream will replace the AttachmentPointer in the attachments collection.
|
|
|
|
self = [super initWithUniqueId:pointer.uniqueId];
|
|
|
|
if (!self) {
|
|
|
|
return self;
|
|
|
|
}
|
|
|
|
|
|
|
|
_serverId = pointer.serverId;
|
|
|
|
_encryptionKey = pointer.encryptionKey;
|
2017-10-27 00:08:25 +02:00
|
|
|
_byteCount = pointer.byteCount;
|
2017-05-15 15:53:16 +02:00
|
|
|
_sourceFilename = pointer.sourceFilename;
|
2017-11-03 20:03:17 +01:00
|
|
|
NSString *contentType = pointer.contentType;
|
|
|
|
if (contentType.length < 1) {
|
2018-08-27 18:51:32 +02:00
|
|
|
OWSLogWarn(@"incoming attachment has invalid content type");
|
2017-11-03 20:03:17 +01:00
|
|
|
|
2017-11-03 20:24:22 +01:00
|
|
|
contentType = OWSMimeTypeApplicationOctetStream;
|
2017-11-03 20:03:17 +01:00
|
|
|
}
|
|
|
|
_contentType = contentType;
|
2018-11-06 23:30:09 +01:00
|
|
|
_caption = pointer.caption;
|
2018-11-09 22:09:26 +01:00
|
|
|
_albumMessageId = pointer.albumMessageId;
|
2017-10-27 00:08:25 +02:00
|
|
|
|
2017-03-13 22:33:54 +01:00
|
|
|
_attachmentSchemaVersion = TSAttachmentSchemaVersion;
|
|
|
|
|
|
|
|
return self;
|
|
|
|
}
|
|
|
|
|
2016-10-14 23:00:29 +02:00
|
|
|
- (nullable instancetype)initWithCoder:(NSCoder *)coder
|
|
|
|
{
|
|
|
|
self = [super initWithCoder:coder];
|
|
|
|
if (!self) {
|
|
|
|
return self;
|
|
|
|
}
|
2015-12-07 03:31:43 +01:00
|
|
|
|
2017-03-15 14:17:41 +01:00
|
|
|
if (_attachmentSchemaVersion < TSAttachmentSchemaVersion) {
|
|
|
|
[self upgradeFromAttachmentSchemaVersion:_attachmentSchemaVersion];
|
|
|
|
_attachmentSchemaVersion = TSAttachmentSchemaVersion;
|
|
|
|
}
|
|
|
|
|
2017-05-15 15:53:16 +02:00
|
|
|
if (!_sourceFilename) {
|
|
|
|
// renamed _filename to _sourceFilename
|
|
|
|
_sourceFilename = [coder decodeObjectForKey:@"filename"];
|
2018-09-06 19:01:24 +02:00
|
|
|
OWSAssertDebug(!_sourceFilename || [_sourceFilename isKindOfClass:[NSString class]]);
|
2017-05-15 15:53:16 +02:00
|
|
|
}
|
|
|
|
|
2017-11-03 20:03:17 +01:00
|
|
|
if (_contentType.length < 1) {
|
2018-08-27 18:51:32 +02:00
|
|
|
OWSLogWarn(@"legacy attachment has invalid content type");
|
2017-11-03 20:03:17 +01:00
|
|
|
|
2017-11-03 20:24:22 +01:00
|
|
|
_contentType = OWSMimeTypeApplicationOctetStream;
|
2017-11-03 20:03:17 +01:00
|
|
|
}
|
|
|
|
|
2017-03-15 14:17:41 +01:00
|
|
|
return self;
|
|
|
|
}
|
|
|
|
|
|
|
|
- (void)upgradeFromAttachmentSchemaVersion:(NSUInteger)attachmentSchemaVersion
|
|
|
|
{
|
2017-03-15 16:32:58 +01:00
|
|
|
// This method is overridden by the base classes TSAttachmentPointer and
|
|
|
|
// TSAttachmentStream.
|
2015-12-07 03:31:43 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
+ (NSString *)collection {
|
|
|
|
return @"TSAttachements";
|
|
|
|
}
|
|
|
|
|
|
|
|
- (NSString *)description {
|
|
|
|
NSString *attachmentString = NSLocalizedString(@"ATTACHMENT", nil);
|
|
|
|
|
2018-04-02 23:02:47 +02:00
|
|
|
if ([MIMETypeUtil isAudio:self.contentType]) {
|
2017-05-15 15:53:16 +02:00
|
|
|
// a missing filename is the legacy way to determine if an audio attachment is
|
|
|
|
// a voice note vs. other arbitrary audio attachments.
|
|
|
|
if (self.isVoiceMessage || !self.sourceFilename || self.sourceFilename.length == 0) {
|
2017-05-12 22:05:28 +02:00
|
|
|
attachmentString = NSLocalizedString(@"ATTACHMENT_TYPE_VOICE_MESSAGE",
|
2018-03-26 15:37:43 +02:00
|
|
|
@"Short text label for a voice message attachment, used for thread preview and on the lock screen");
|
2017-05-12 22:05:28 +02:00
|
|
|
return [NSString stringWithFormat:@"🎤 %@", attachmentString];
|
|
|
|
}
|
2015-12-07 03:31:43 +01:00
|
|
|
}
|
|
|
|
|
2018-04-02 23:02:47 +02:00
|
|
|
return [NSString stringWithFormat:@"%@ %@", [TSAttachment emojiForMimeType:self.contentType], attachmentString];
|
|
|
|
}
|
|
|
|
|
|
|
|
+ (NSString *)emojiForMimeType:(NSString *)contentType
|
|
|
|
{
|
|
|
|
if ([MIMETypeUtil isImage:contentType]) {
|
|
|
|
return @"📷";
|
|
|
|
} else if ([MIMETypeUtil isVideo:contentType]) {
|
2018-04-03 20:59:27 +02:00
|
|
|
return @"🎥";
|
2018-04-02 23:02:47 +02:00
|
|
|
} else if ([MIMETypeUtil isAudio:contentType]) {
|
2018-04-05 16:23:56 +02:00
|
|
|
if (SYSTEM_VERSION_GREATER_THAN_OR_EQUAL_TO(11, 0)) {
|
|
|
|
return @"🎧";
|
|
|
|
} else {
|
|
|
|
return @"📻";
|
|
|
|
}
|
2018-04-02 23:02:47 +02:00
|
|
|
} else if ([MIMETypeUtil isAnimated:contentType]) {
|
|
|
|
return @"🎡";
|
|
|
|
} else {
|
|
|
|
return @"📎";
|
|
|
|
}
|
2015-12-07 03:31:43 +01:00
|
|
|
}
|
|
|
|
|
2018-11-05 15:53:48 +01:00
|
|
|
- (BOOL)isImage
|
|
|
|
{
|
|
|
|
return [MIMETypeUtil isImage:self.contentType];
|
|
|
|
}
|
|
|
|
|
|
|
|
- (BOOL)isVideo
|
|
|
|
{
|
|
|
|
return [MIMETypeUtil isVideo:self.contentType];
|
|
|
|
}
|
|
|
|
|
|
|
|
- (BOOL)isAudio
|
|
|
|
{
|
|
|
|
return [MIMETypeUtil isAudio:self.contentType];
|
|
|
|
}
|
|
|
|
|
|
|
|
- (BOOL)isAnimated
|
|
|
|
{
|
|
|
|
return [MIMETypeUtil isAnimated:self.contentType];
|
|
|
|
}
|
|
|
|
|
2017-05-09 15:30:54 +02:00
|
|
|
- (BOOL)isVoiceMessage
|
|
|
|
{
|
|
|
|
return self.attachmentType == TSAttachmentTypeVoiceMessage;
|
|
|
|
}
|
|
|
|
|
2018-11-05 17:07:00 +01:00
|
|
|
- (BOOL)isVisualMedia
|
|
|
|
{
|
2018-11-07 17:42:28 +01:00
|
|
|
return [MIMETypeUtil isVisualMedia:self.contentType];
|
2018-11-05 17:07:00 +01:00
|
|
|
}
|
|
|
|
|
2019-02-23 01:37:03 +01:00
|
|
|
- (BOOL)isOversizeText
|
|
|
|
{
|
|
|
|
return [self.contentType isEqualToString:OWSMimeTypeOversizeTextMessage];
|
|
|
|
}
|
|
|
|
|
2018-02-16 02:45:28 +01:00
|
|
|
- (nullable NSString *)sourceFilename
|
|
|
|
{
|
2018-02-22 23:56:21 +01:00
|
|
|
return _sourceFilename.filterFilename;
|
2018-02-16 02:45:28 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
- (NSString *)contentType
|
|
|
|
{
|
2018-02-22 23:56:21 +01:00
|
|
|
return _contentType.filterFilename;
|
2018-02-16 02:45:28 +01:00
|
|
|
}
|
|
|
|
|
2018-11-07 18:00:34 +01:00
|
|
|
#pragma mark - Relationships
|
|
|
|
|
|
|
|
- (nullable TSMessage *)fetchAlbumMessageWithTransaction:(YapDatabaseReadTransaction *)transaction
|
|
|
|
{
|
|
|
|
if (self.albumMessageId == nil) {
|
|
|
|
return nil;
|
|
|
|
}
|
|
|
|
return [TSMessage fetchObjectWithUniqueID:self.albumMessageId transaction:transaction];
|
|
|
|
}
|
|
|
|
|
2018-11-07 20:52:09 +01:00
|
|
|
- (void)migrateAlbumMessageId:(NSString *)albumMesssageId
|
|
|
|
{
|
|
|
|
_albumMessageId = albumMesssageId;
|
|
|
|
}
|
|
|
|
|
2015-12-07 03:31:43 +01:00
|
|
|
@end
|
2016-10-14 23:00:29 +02:00
|
|
|
|
|
|
|
NS_ASSUME_NONNULL_END
|