diff --git a/Signal/src/Models/SignalAttachment.swift b/Signal/src/Models/SignalAttachment.swift index 89efa0b09..b34dd353e 100644 --- a/Signal/src/Models/SignalAttachment.swift +++ b/Signal/src/Models/SignalAttachment.swift @@ -70,7 +70,7 @@ class SignalAttachment: NSObject { return dataSource.dataLength() } public var dataUrl: URL? { - return dataSource.dataUrl(fileExtensionNonNil) + return dataSource.dataUrl() } // Attachment types are identified using UTIs. @@ -79,11 +79,10 @@ class SignalAttachment: NSObject { let dataUTI: String // An optional field that indicates the filename, if known, for this attachment. + // + // TODO: Try to eliminate this property. let filename: String? - static let kOversizeTextAttachmentUTI = "org.whispersystems.oversize-text-attachment" - static let kUnknownTestAttachmentUTI = "org.whispersystems.unknown" - var error: SignalAttachmentError? { didSet { AssertIsOnMainThread() @@ -179,10 +178,10 @@ class SignalAttachment: NSObject { } } } - if dataUTI == SignalAttachment.kOversizeTextAttachmentUTI { + if dataUTI == kOversizeTextAttachmentUTI { return OWSMimeTypeOversizeTextMessage } - if dataUTI == SignalAttachment.kUnknownTestAttachmentUTI { + if dataUTI == kUnknownTestAttachmentUTI { return OWSMimeTypeUnknownForTests } guard let mimeType = UTTypeCopyPreferredTagWithClass(dataUTI as CFString, kUTTagClassMIMEType) else { @@ -221,10 +220,10 @@ class SignalAttachment: NSObject { return fileExtension } } - if dataUTI == SignalAttachment.kOversizeTextAttachmentUTI { - return "txt" + if dataUTI == kOversizeTextAttachmentUTI { + return kOversizeTextAttachmentFileExtension } - if dataUTI == SignalAttachment.kUnknownTestAttachmentUTI { + if dataUTI == kUnknownTestAttachmentUTI { return "unknown" } guard let fileExtension = MIMETypeUtil.fileExtension(forUTIType:dataUTI) else { @@ -379,7 +378,7 @@ class SignalAttachment: NSObject { owsFail("\(TAG) Missing expected pasteboard data for UTI: \(dataUTI)") return nil } - let dataSource = DataSourceValue.dataSource(with:data) + let dataSource = DataSourceValue.dataSource(with:data, utiType: dataUTI) return imageAttachment(dataSource : dataSource, dataUTI : dataUTI, filename: nil) } } @@ -389,7 +388,7 @@ class SignalAttachment: NSObject { owsFail("\(TAG) Missing expected pasteboard data for UTI: \(dataUTI)") return nil } - let dataSource = DataSourceValue.dataSource(with:data) + let dataSource = DataSourceValue.dataSource(with:data, utiType: dataUTI) return videoAttachment(dataSource : dataSource, dataUTI : dataUTI, filename: nil) } } @@ -399,7 +398,7 @@ class SignalAttachment: NSObject { owsFail("\(TAG) Missing expected pasteboard data for UTI: \(dataUTI)") return nil } - let dataSource = DataSourceValue.dataSource(with:data) + let dataSource = DataSourceValue.dataSource(with:data, utiType: dataUTI) return audioAttachment(dataSource : dataSource, dataUTI : dataUTI, filename: nil) } } @@ -409,7 +408,7 @@ class SignalAttachment: NSObject { owsFail("\(TAG) Missing expected pasteboard data for UTI: \(dataUTI)") return nil } - let dataSource = DataSourceValue.dataSource(with:data) + let dataSource = DataSourceValue.dataSource(with:data, utiType: dataUTI) return genericAttachment(dataSource : dataSource, dataUTI : dataUTI, filename: nil) } @@ -553,7 +552,7 @@ class SignalAttachment: NSObject { return attachment } - guard let dataSource = DataSourceValue.dataSource(with:jpgImageData) else { + guard let dataSource = DataSourceValue.dataSource(with:jpgImageData, fileExtension:"jpg") else { attachment.error = .couldNotConvertToJpeg return attachment } @@ -658,10 +657,7 @@ class SignalAttachment: NSObject { // NOTE: The attachment returned by this method may not be valid. // Check the attachment's error property. private class func oversizeTextAttachment(text: String?) -> SignalAttachment { - var dataSource: DataSource? = DataSourceValue.emptyDataSource() - if let data = text?.data(using: .utf8) { - dataSource = DataSourceValue.dataSource(with:data) - } + let dataSource = DataSourceValue.dataSource(withOversizeText:text) return newAttachment(dataSource : dataSource, dataUTI : kOversizeTextAttachmentUTI, validUTISet : nil, diff --git a/Signal/src/ViewControllers/ConversationView/ConversationViewController.m b/Signal/src/ViewControllers/ConversationView/ConversationViewController.m index 3ac5464f1..d2f03ccb7 100644 --- a/Signal/src/ViewControllers/ConversationView/ConversationViewController.m +++ b/Signal/src/ViewControllers/ConversationView/ConversationViewController.m @@ -1673,12 +1673,9 @@ typedef NS_ENUM(NSInteger, MessagesRangeSizeMode) { BOOL didAddToProfileWhitelist = [ThreadUtil addThreadToProfileWhitelistIfEmptyContactThread:self.thread]; TSOutgoingMessage *message; if ([text lengthOfBytesUsingEncoding:NSUTF8StringEncoding] >= kOversizeTextMessageSizeThreshold) { - id _Nullable dataSource = - [DataSourceValue dataSourceWithData:[text dataUsingEncoding:NSUTF8StringEncoding]]; + id _Nullable dataSource = [DataSourceValue dataSourceWithOversizeText:text]; SignalAttachment *attachment = - [SignalAttachment attachmentWithDataSource:dataSource - dataUTI:SignalAttachment.kOversizeTextAttachmentUTI - filename:nil]; + [SignalAttachment attachmentWithDataSource:dataSource dataUTI:kOversizeTextAttachmentUTI filename:nil]; message = [ThreadUtil sendMessageWithAttachment:attachment inThread:self.thread messageSender:self.messageSender]; } else { @@ -3377,7 +3374,8 @@ typedef NS_ENUM(NSInteger, MessagesRangeSizeMode) { } OWSAssert([NSThread isMainThread]); - id _Nullable dataSource = [DataSourceValue dataSourceWithData:imageData]; + id _Nullable dataSource = + [DataSourceValue dataSourceWithData:imageData utiType:dataUTI]; SignalAttachment *attachment = [SignalAttachment attachmentWithDataSource:dataSource dataUTI:dataUTI filename:filename]; [self dismissViewControllerAnimated:YES diff --git a/Signal/src/ViewControllers/DebugUI/DebugUIMessages.m b/Signal/src/ViewControllers/DebugUI/DebugUIMessages.m index badcd8a4b..760d3b17a 100644 --- a/Signal/src/ViewControllers/DebugUI/DebugUIMessages.m +++ b/Signal/src/ViewControllers/DebugUI/DebugUIMessages.m @@ -131,8 +131,7 @@ NS_ASSUME_NONNULL_BEGIN }], [OWSTableItem itemWithTitle:@"Send unknown mimetype" actionBlock:^{ - [DebugUIMessages sendRandomAttachment:thread - uti:SignalAttachment.kUnknownTestAttachmentUTI]; + [DebugUIMessages sendRandomAttachment:thread uti:kUnknownTestAttachmentUTI]; }], [OWSTableItem itemWithTitle:@"Send pdf" actionBlock:^{ @@ -588,12 +587,9 @@ NS_ASSUME_NONNULL_BEGIN @"lorem, in rhoncus nisi."]; } - id _Nullable dataSource = - [DataSourceValue dataSourceWithData:[message dataUsingEncoding:NSUTF8StringEncoding]]; + id _Nullable dataSource = [DataSourceValue dataSourceWithOversizeText:message]; SignalAttachment *attachment = - [SignalAttachment attachmentWithDataSource:dataSource - dataUTI:SignalAttachment.kOversizeTextAttachmentUTI - filename:nil]; + [SignalAttachment attachmentWithDataSource:dataSource dataUTI:kOversizeTextAttachmentUTI filename:nil]; [ThreadUtil sendMessageWithAttachment:attachment inThread:thread messageSender:messageSender]; } @@ -617,7 +613,8 @@ NS_ASSUME_NONNULL_BEGIN + (void)sendRandomAttachment:(TSThread *)thread uti:(NSString *)uti length:(NSUInteger)length { OWSMessageSender *messageSender = [Environment getCurrent].messageSender; - id _Nullable dataSource = [DataSourceValue dataSourceWithData:[self createRandomNSDataOfSize:length]]; + id _Nullable dataSource = + [DataSourceValue dataSourceWithData:[self createRandomNSDataOfSize:length] utiType:uti]; SignalAttachment *attachment = [SignalAttachment attachmentWithDataSource:dataSource dataUTI:uti filename:nil]; [ThreadUtil sendMessageWithAttachment:attachment inThread:thread messageSender:messageSender ignoreErrors:YES]; } diff --git a/SignalServiceKit/src/Util/DataSource.h b/SignalServiceKit/src/Util/DataSource.h index f5f1e2eb0..13c71da75 100755 --- a/SignalServiceKit/src/Util/DataSource.h +++ b/SignalServiceKit/src/Util/DataSource.h @@ -21,7 +21,7 @@ NS_ASSUME_NONNULL_BEGIN // Will only return nil in the error case. // // TODO: Try to remove the parameter. -- (nullable NSURL *)dataUrl:(NSString *)fileExtension; +- (nullable NSURL *)dataUrl; // The file path for the data. // @@ -30,7 +30,7 @@ NS_ASSUME_NONNULL_BEGIN // Will only return nil in the error case. // // TODO: Try to remove the parameter. -- (nullable NSString *)dataPath:(NSString *)fileExtension; +- (nullable NSString *)dataPath; // The file path for the data, if it already exists on disk. // @@ -48,7 +48,11 @@ NS_ASSUME_NONNULL_BEGIN @interface DataSourceValue : NSObject -+ (nullable id)dataSourceWithData:(NSData *)data; ++ (nullable id)dataSourceWithData:(NSData *)data fileExtension:(NSString *)fileExtension; + ++ (nullable id)dataSourceWithData:(NSData *)data utiType:(NSString *)utiType; + ++ (nullable id)dataSourceWithOversizeText:(NSString *_Nullable)text; + (id)emptyDataSource; @@ -64,12 +68,4 @@ NS_ASSUME_NONNULL_BEGIN @end -//#pragma mark - -// -//@interface DataSourceURL : NSObject -// -//+ (id)dataSourceWithURL:(NSURL *)fileUrl; -// -//@end - NS_ASSUME_NONNULL_END diff --git a/SignalServiceKit/src/Util/DataSource.m b/SignalServiceKit/src/Util/DataSource.m index 28bc684cc..bbff76989 100755 --- a/SignalServiceKit/src/Util/DataSource.m +++ b/SignalServiceKit/src/Util/DataSource.m @@ -3,6 +3,7 @@ // #import "DataSource.h" +#import "MIMETypeUtil.h" NS_ASSUME_NONNULL_BEGIN @@ -10,6 +11,8 @@ NS_ASSUME_NONNULL_BEGIN @property (nonatomic) NSData *dataValue; +@property (nonatomic) NSString *fileExtension; + // This property is lazy-populated. @property (nonatomic) NSString *cachedFilePath; @@ -19,21 +22,41 @@ NS_ASSUME_NONNULL_BEGIN @implementation DataSourceValue -+ (nullable id)dataSourceWithData:(NSData *)data ++ (nullable id)dataSourceWithData:(NSData *)data fileExtension:(NSString *)fileExtension { OWSAssert(data); + if (!data) { return nil; } DataSourceValue *instance = [DataSourceValue new]; instance.dataValue = data; + instance.fileExtension = fileExtension; return instance; } ++ (nullable id)dataSourceWithData:(NSData *)data utiType:(NSString *)utiType +{ + NSString *fileExtension = [MIMETypeUtil fileExtensionForUTIType:utiType]; + return [self dataSourceWithData:data fileExtension:fileExtension]; +} + ++ (nullable id)dataSourceWithOversizeText:(NSString *_Nullable)text +{ + OWSAssert(text); + + if (!text) { + return nil; + } + + NSData *data = [text dataUsingEncoding:NSUTF8StringEncoding]; + return [self dataSourceWithData:data fileExtension:kOversizeTextAttachmentFileExtension]; +} + + (id)emptyDataSource { - return [self dataSourceWithData:[NSData new]]; + return [self dataSourceWithData:[NSData new] fileExtension:@"bin"]; } - (NSData *)data @@ -43,13 +66,13 @@ NS_ASSUME_NONNULL_BEGIN return self.dataValue; } -- (nullable NSURL *)dataUrl:(NSString *)fileExtension +- (nullable NSURL *)dataUrl { - NSString *_Nullable path = [self dataPath:fileExtension]; + NSString *_Nullable path = [self dataPath]; return (path ? [NSURL fileURLWithPath:path] : nil); } -- (nullable NSString *)dataPath:(NSString *)fileExtension +- (nullable NSString *)dataPath { OWSAssert(self.dataValue); @@ -57,12 +80,12 @@ NS_ASSUME_NONNULL_BEGIN { if (!self.cachedFilePath) { NSString *dirPath = NSTemporaryDirectory(); - NSString *fileName = [[[NSUUID UUID] UUIDString] stringByAppendingPathExtension:fileExtension]; + NSString *fileName = [[[NSUUID UUID] UUIDString] stringByAppendingPathExtension:self.fileExtension]; NSString *filePath = [dirPath stringByAppendingPathComponent:fileName]; if ([self.dataValue writeToFile:fileName atomically:YES]) { self.cachedFilePath = filePath; } else { - OWSFail(@"%@ Could not write data to disk: %@", self.tag, fileExtension); + OWSFail(@"%@ Could not write data to disk: %@", self.tag, self.fileExtension); } } @@ -154,14 +177,14 @@ NS_ASSUME_NONNULL_BEGIN } } -- (nullable NSURL *)dataUrl:(NSString *)fileExtension +- (nullable NSURL *)dataUrl { OWSAssert(self.filePath); return [NSURL fileURLWithPath:self.filePath]; } -- (nullable NSString *)dataPath:(NSString *)fileExtension +- (nullable NSString *)dataPath { OWSAssert(self.filePath); @@ -211,226 +234,4 @@ NS_ASSUME_NONNULL_BEGIN @end -//#pragma mark - -// -//@interface DataSourceURL () -// -//@property (nonatomic) NSURL *fileUrl; -// -//// These properties are lazy-populated. -//@property (nonatomic) NSData *cachedData; -//@property (nonatomic) NSNumber *cachedDataLength; -// -//@end -// -//#pragma mark - -// -//@implementation DataSourceURL -// -//+ (id)dataSourceWithURL:(NSURL *)fileUrl; -//{ -// DataSourceValue *instance = [DataSourceValue new]; -// instance.fileUrl = fileUrl; -// return instance; -//} -// -//- (NSData *)data -//{ -// OWSAssert(self.filePath); -// -// @synchronized (self) { -// if (!self.cachedData) { -// self.cachedData = [NSData dataWithContentsOfFile:self.filePath]; -// } -// if (!self.cachedData) { -// OWSFail(@"%@ Could not read data from disk: %@", self.tag, self.filePath); -// self.cachedData = [NSData new]; -// } -// return self.cachedData; -// } -//} -// -//- (nullable NSURL *)dataUrl:(NSString *)fileExtension -//{ -// OWSAssert(self.filePath); -// -// return [NSURL fileURLWithPath:self.filePath]; -//} -// -//- (nullable NSString *)dataPath:(NSString *)fileExtension -//{ -// OWSAssert(self.filePath); -// -// return self.filePath; -//} -// -//- (nullable NSString *)dataPathIfOnDisk -//{ -// OWSAssert(self.filePath); -// -// return self.filePath; -//} -// -//- (NSUInteger)dataLength -//{ -// OWSAssert(self.filePath); -// -// @synchronized (self) { -// if (!self.cachedDataLength) { -// NSError *error; -// NSDictionary *_Nullable attributes = -// [[NSFileManager defaultManager] attributesOfItemAtPath:self.filePath error:&error]; -// if (!attributes || error) { -// OWSFail(@"%@ Could not read data length from disk: %@", self.tag, self.filePath); -// self.cachedDataLength = @(0); -// } else { -// uint64_t fileSize = [attributes fileSize]; -// self.cachedDataLength = @(fileSize); -// } -// } -// return [self.cachedDataLength unsignedIntegerValue]; -// } -//} -// -//#pragma mark - Logging -// -//+ (NSString *)tag -//{ -// return [NSString stringWithFormat:@"[%@]", self.class]; -//} -// -//- (NSString *)tag -//{ -// return self.class.tag; -//} -// -//@end -// -//#pragma mark - -// -// -//@objc class DataSourcePath : NSObject, DataSource { -// static let TAG = "[DataSourcePath]" -// -// private let path : String -// -// private var cachedData : Data -// ? -// -// private var cachedLength -// : Int -// ? -// -// // MARK: Constructor -// -// internal required init(_ path -// : String){ self.path = path super.init() } -// -// func -// data() -// ->Data -// { -// if -// let cachedData -// = cachedData{ return cachedData } Logger.error("\(DataSourcePath.TAG) reading data: \(path)") do -// { -// try -// cachedData = NSData(contentsOfFile : path) as Data -// } -// catch -// { -// owsFail("\(DataSourcePath.TAG) Could not read data from disk: \(path)") cachedData = Data() -// } -// return cachedData ! -// } -// -// return cachedLength ! -// } -//} -// -//@objc class DataSourceUrl : NSObject, -// DataSource { -// static let TAG = "[DataSourceUrl]" -// -// private let url : URL -// -// private var cachedData : Data -// ? -// -// private var cachedLength -// : Int -// ? -// -// // MARK: Constructor -// -// internal required -// init(_ url -// : URL) -// { -// if -// !url.isFileURL{ owsFail("\(DataSourceUrl.TAG) URL is not a file URL: \(url)") } self.url = url -// super.init() -// } -// -// func data()->Data -// { -// if -// let cachedData -// = cachedData{ return cachedData } guard url -// .isFileURL else { -// owsFail("\(DataSourceUrl.TAG) URL is not a file URL: \(url)") return Data() -// } Logger.error("\(DataSourceUrl.TAG) reading data: \(url)") do -// { -// try -// cachedData = Data(contentsOf : url) -// } -// catch -// { -// owsFail("\(DataSourceUrl.TAG) Could not read data from disk: \(url)") cachedData = Data() -// } -// return cachedData ! -// } -// -// func dataUrl(fileExtension -// : String) -// ->URL -// ? { return url } -// -// func dataPath(fileExtension -// : String) -// ->String -// ? { guard url -// .isFileURL else { -// owsFail("\(DataSourceUrl.TAG) URL is not a file URL: \(url)") return nil } return url.path } -// -// func dataPathIfOnDisk() -// ->String -// ? { guard url -// .isFileURL else { -// owsFail("\(DataSourceUrl.TAG) URL is not a file URL: \(url)") return nil } return url.path } -// -// func dataLength() -// ->Int -// { -// if -// let cachedLength = cachedLength{ return cachedLength } guard url.isFileURL else -// { -// owsFail("\(DataSourceUrl.TAG) URL is not a file URL: \(url)") return 0 -// } -// -// do { -// let fileAttributes = try -// FileManager.default.attributesOfItem(atPath -// : url.path) let fileSize -// = fileAttributes[FileAttributeKey.size] as !UInt64 cachedLength = Int(fileSize) -// } -// catch -// { -// owsFail("\(DataSourceUrl.TAG) Could not read data length from disk: \(url)") cachedLength = 0 -// } -// -// return cachedLength ! -// } -//} - NS_ASSUME_NONNULL_END diff --git a/SignalServiceKit/src/Util/MIMETypeUtil.h b/SignalServiceKit/src/Util/MIMETypeUtil.h index bd3b37bb3..ab1a13c30 100644 --- a/SignalServiceKit/src/Util/MIMETypeUtil.h +++ b/SignalServiceKit/src/Util/MIMETypeUtil.h @@ -9,6 +9,10 @@ extern NSString *const OWSMimeTypeImagePng; extern NSString *const OWSMimeTypeOversizeTextMessage; extern NSString *const OWSMimeTypeUnknownForTests; +extern NSString *const kOversizeTextAttachmentUTI; +extern NSString *const kOversizeTextAttachmentFileExtension; +extern NSString *const kUnknownTestAttachmentUTI; + @interface MIMETypeUtil : NSObject + (BOOL)isSupportedVideoMIMEType:(NSString *)contentType; diff --git a/SignalServiceKit/src/Util/MIMETypeUtil.m b/SignalServiceKit/src/Util/MIMETypeUtil.m index 4bcbe3ffc..698976cf8 100644 --- a/SignalServiceKit/src/Util/MIMETypeUtil.m +++ b/SignalServiceKit/src/Util/MIMETypeUtil.m @@ -16,6 +16,10 @@ NSString *const OWSMimeTypeImagePng = @"image/png"; NSString *const OWSMimeTypeOversizeTextMessage = @"text/x-signal-plain"; NSString *const OWSMimeTypeUnknownForTests = @"unknown/mimetype"; +NSString *const kOversizeTextAttachmentUTI = @"org.whispersystems.oversize-text-attachment"; +NSString *const kOversizeTextAttachmentFileExtension = @"txt"; +NSString *const kUnknownTestAttachmentUTI = @"org.whispersystems.unknown"; + @implementation MIMETypeUtil + (NSDictionary *)supportedVideoMIMETypesToExtensionTypes {