Image content types.

pull/1/head
Matthew Chen 7 years ago
parent c7714b09ab
commit 1607aa7f57

@ -645,7 +645,7 @@ NS_ASSUME_NONNULL_BEGIN
NSString *_Nullable filePath = [strongSelf.attachmentStream filePath]; NSString *_Nullable filePath = [strongSelf.attachmentStream filePath];
YYImage *_Nullable animatedImage = nil; YYImage *_Nullable animatedImage = nil;
if (filePath && [NSData ows_isValidImageAtPath:filePath]) { if (strongSelf.attachmentStream.isValidImage && filePath) {
animatedImage = [YYImage imageWithContentsOfFile:filePath]; animatedImage = [YYImage imageWithContentsOfFile:filePath];
} }
return animatedImage; return animatedImage;

@ -383,7 +383,7 @@ NS_ASSUME_NONNULL_BEGIN
dispatch_once(&onceToken, ^{ dispatch_once(&onceToken, ^{
instance = [DebugUIMessagesAssetLoader instance = [DebugUIMessagesAssetLoader
fakeAssetLoaderWithUrl:@"https://s3.amazonaws.com/ows-data/example_attachment_media/random-gif.gif" fakeAssetLoaderWithUrl:@"https://s3.amazonaws.com/ows-data/example_attachment_media/random-gif.gif"
mimeType:@"image/gif"]; mimeType:OWSMimeTypeImageGif];
}); });
return instance; return instance;
} }
@ -395,7 +395,7 @@ NS_ASSUME_NONNULL_BEGIN
dispatch_once(&onceToken, ^{ dispatch_once(&onceToken, ^{
instance = instance =
[DebugUIMessagesAssetLoader fakeAssetLoaderWithUrl:@"https://i.giphy.com/media/LTw0F3GAdaao8/source.gif" [DebugUIMessagesAssetLoader fakeAssetLoaderWithUrl:@"https://i.giphy.com/media/LTw0F3GAdaao8/source.gif"
mimeType:@"image/gif"]; mimeType:OWSMimeTypeImageGif];
}); });
return instance; return instance;
} }

@ -224,7 +224,7 @@ NS_ASSUME_NONNULL_BEGIN
[scrollView autoPinToSuperviewEdges]; [scrollView autoPinToSuperviewEdges];
if (self.isAnimated) { if (self.isAnimated) {
if ([self.fileData ows_isValidImage]) { if (self.attachmentStream.isValidImage) {
YYImage *animatedGif = [YYImage imageWithData:self.fileData]; YYImage *animatedGif = [YYImage imageWithData:self.fileData];
YYAnimatedImageView *animatedView = [YYAnimatedImageView new]; YYAnimatedImageView *animatedView = [YYAnimatedImageView new];
animatedView.image = animatedGif; animatedView.image = animatedGif;

@ -76,12 +76,12 @@
- (ConversationViewItem *)stillImageViewItem - (ConversationViewItem *)stillImageViewItem
{ {
return [self viewItemWithAttachmentMimetype:@"image/jpeg" filename:@"test-jpg.jpg"]; return [self viewItemWithAttachmentMimetype:OWSMimeTypeImageJpeg filename:@"test-jpg.jpg"];
} }
- (ConversationViewItem *)animatedImageViewItem - (ConversationViewItem *)animatedImageViewItem
{ {
return [self viewItemWithAttachmentMimetype:@"image/gif" filename:@"test-gif.gif"]; return [self viewItemWithAttachmentMimetype:OWSMimeTypeImageGif filename:@"test-gif.gif"];
} }
- (ConversationViewItem *)videoViewItem - (ConversationViewItem *)videoViewItem

@ -2270,7 +2270,7 @@
"VERIFICATION_STATE_CHANGE_GENERIC" = "Verification state changed."; "VERIFICATION_STATE_CHANGE_GENERIC" = "Verification state changed.";
/* Label for button or row which allows users to verify the safety number of another user. */ /* Label for button or row which allows users to verify the safety number of another user. */
"VERIFY_PRIVACY" = "Show Safety Number"; "VERIFY_PRIVACY" = "View Safety Number";
/* Label for button or row which allows users to verify the safety numbers of multiple users. */ /* Label for button or row which allows users to verify the safety numbers of multiple users. */
"VERIFY_PRIVACY_MULTIPLE" = "Review Safety Numbers"; "VERIFY_PRIVACY_MULTIPLE" = "Review Safety Numbers";

@ -74,6 +74,10 @@ NS_ASSUME_NONNULL_BEGIN
// Non-nil for attachments which need "lazy backup restore." // Non-nil for attachments which need "lazy backup restore."
- (nullable OWSBackupFragment *)lazyRestoreFragment; - (nullable OWSBackupFragment *)lazyRestoreFragment;
#pragma mark - Image Validation
- (BOOL)isValidImage;
#pragma mark - Update With... Methods #pragma mark - Update With... Methods
// Marks attachment as needing "lazy backup restore." // Marks attachment as needing "lazy backup restore."

@ -312,6 +312,25 @@ NS_ASSUME_NONNULL_BEGIN
return [MIMETypeUtil isAudio:self.contentType]; return [MIMETypeUtil isAudio:self.contentType];
} }
#pragma mark - Image Validation
- (BOOL)isValidImageWithData:(NSData *)data
{
OWSAssert(self.isImage || self.isAnimated);
OWSAssert(data);
return [data ows_isValidImageWithMimeType:self.contentType];
}
- (BOOL)isValidImage
{
OWSAssert(self.isImage || self.isAnimated);
return [NSData ows_isValidImageAtPath:self.filePath mimeType:self.contentType];
}
#pragma mark -
- (nullable UIImage *)image - (nullable UIImage *)image
{ {
if ([self isVideo]) { if ([self isVideo]) {
@ -322,7 +341,7 @@ NS_ASSUME_NONNULL_BEGIN
return nil; return nil;
} }
NSData *data = [NSData dataWithContentsOfURL:mediaUrl]; NSData *data = [NSData dataWithContentsOfURL:mediaUrl];
if (![data ows_isValidImage]) { if (![self isValidImageWithData:data]) {
return nil; return nil;
} }
return [UIImage imageWithData:data]; return [UIImage imageWithData:data];
@ -348,7 +367,7 @@ NS_ASSUME_NONNULL_BEGIN
} }
NSData *data = [NSData dataWithContentsOfURL:mediaUrl]; NSData *data = [NSData dataWithContentsOfURL:mediaUrl];
if (![data ows_isValidImage]) { if (![self isValidImageWithData:data]) {
return nil; return nil;
} }
@ -422,7 +441,7 @@ NS_ASSUME_NONNULL_BEGIN
UIImage *_Nullable result; UIImage *_Nullable result;
if (self.isImage || self.isAnimated) { if (self.isImage || self.isAnimated) {
if (![NSData ows_isValidImageAtPath:self.filePath]) { if (![self isValidImage]) {
DDLogWarn(@"%@ skipping thumbnail generation for invalid image at path: %@", self.logTag, self.filePath); DDLogWarn(@"%@ skipping thumbnail generation for invalid image at path: %@", self.logTag, self.filePath);
return; return;
} }
@ -516,7 +535,7 @@ NS_ASSUME_NONNULL_BEGIN
if (!mediaUrl) { if (!mediaUrl) {
return CGSizeZero; return CGSizeZero;
} }
if (![NSData ows_isValidImageAtPath:mediaUrl.path]) { if (![self isValidImage]) {
return CGSizeZero; return CGSizeZero;
} }

@ -72,7 +72,7 @@ NS_ASSUME_NONNULL_BEGIN
// if ows_isValidImage is given a file path, it will // if ows_isValidImage is given a file path, it will
// avoid loading most of the data into memory, which // avoid loading most of the data into memory, which
// is considerably more performant, so try to do that. // is considerably more performant, so try to do that.
return [NSData ows_isValidImageAtPath:dataPath]; return [NSData ows_isValidImageAtPath:dataPath mimeType:self.mimeType];
} }
NSData *data = [self data]; NSData *data = [self data];
return [data ows_isValidImage]; return [data ows_isValidImage];
@ -83,6 +83,14 @@ NS_ASSUME_NONNULL_BEGIN
_sourceFilename = sourceFilename.filterFilename; _sourceFilename = sourceFilename.filterFilename;
} }
// Returns the MIME type, if known.
- (nullable NSString *)mimeType
{
OWS_ABSTRACT_METHOD();
return nil;
}
@end @end
#pragma mark - #pragma mark -
@ -222,6 +230,11 @@ NS_ASSUME_NONNULL_BEGIN
} }
} }
- (nullable NSString *)mimeType
{
return (self.fileExtension ? [MIMETypeUtil mimeTypeForFileExtension:self.fileExtension] : nil);
}
@end @end
#pragma mark - #pragma mark -
@ -372,6 +385,12 @@ NS_ASSUME_NONNULL_BEGIN
} }
} }
- (nullable NSString *)mimeType
{
NSString *_Nullable fileExtension = self.filePath.pathExtension;
return (fileExtension ? [MIMETypeUtil mimeTypeForFileExtension:fileExtension] : nil);
}
@end @end
NS_ASSUME_NONNULL_END NS_ASSUME_NONNULL_END

@ -8,6 +8,11 @@ extern NSString *const OWSMimeTypeApplicationOctetStream;
extern NSString *const OWSMimeTypeApplicationZip; extern NSString *const OWSMimeTypeApplicationZip;
extern NSString *const OWSMimeTypeImagePng; extern NSString *const OWSMimeTypeImagePng;
extern NSString *const OWSMimeTypeImageJpeg; extern NSString *const OWSMimeTypeImageJpeg;
extern NSString *const OWSMimeTypeImageGif;
extern NSString *const OWSMimeTypeImageTiff1;
extern NSString *const OWSMimeTypeImageTiff2;
extern NSString *const OWSMimeTypeImageBmp1;
extern NSString *const OWSMimeTypeImageBmp2;
extern NSString *const OWSMimeTypeOversizeTextMessage; extern NSString *const OWSMimeTypeOversizeTextMessage;
extern NSString *const OWSMimeTypeUnknownForTests; extern NSString *const OWSMimeTypeUnknownForTests;

@ -18,6 +18,11 @@ NS_ASSUME_NONNULL_BEGIN
NSString *const OWSMimeTypeApplicationOctetStream = @"application/octet-stream"; NSString *const OWSMimeTypeApplicationOctetStream = @"application/octet-stream";
NSString *const OWSMimeTypeImagePng = @"image/png"; NSString *const OWSMimeTypeImagePng = @"image/png";
NSString *const OWSMimeTypeImageJpeg = @"image/jpeg"; NSString *const OWSMimeTypeImageJpeg = @"image/jpeg";
NSString *const OWSMimeTypeImageGif = @"image/gif";
NSString *const OWSMimeTypeImageTiff1 = @"image/tiff";
NSString *const OWSMimeTypeImageTiff2 = @"image/x-tiff";
NSString *const OWSMimeTypeImageBmp1 = @"image/bmp";
NSString *const OWSMimeTypeImageBmp2 = @"image/x-windows-bmp";
NSString *const OWSMimeTypeOversizeTextMessage = @"text/x-signal-plain"; NSString *const OWSMimeTypeOversizeTextMessage = @"text/x-signal-plain";
NSString *const OWSMimeTypeUnknownForTests = @"unknown/mimetype"; NSString *const OWSMimeTypeUnknownForTests = @"unknown/mimetype";
NSString *const OWSMimeTypeApplicationZip = @"application/zip"; NSString *const OWSMimeTypeApplicationZip = @"application/zip";
@ -76,7 +81,7 @@ NSString *const kSyncMessageFileExtension = @"bin";
static dispatch_once_t onceToken; static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{ dispatch_once(&onceToken, ^{
result = @{ result = @{
@"image/jpeg" : @"jpeg", OWSMimeTypeImageJpeg : @"jpeg",
@"image/pjpeg" : @"jpeg", @"image/pjpeg" : @"jpeg",
OWSMimeTypeImagePng : @"png", OWSMimeTypeImagePng : @"png",
@"image/tiff" : @"tif", @"image/tiff" : @"tif",
@ -93,7 +98,7 @@ NSString *const kSyncMessageFileExtension = @"bin";
static dispatch_once_t onceToken; static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{ dispatch_once(&onceToken, ^{
result = @{ result = @{
@"image/gif" : @"gif", OWSMimeTypeImageGif : @"gif",
}; };
}); });
return result; return result;
@ -183,7 +188,7 @@ NSString *const kSyncMessageFileExtension = @"bin";
static dispatch_once_t onceToken; static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{ dispatch_once(&onceToken, ^{
result = @{ result = @{
@"gif" : @"image/gif", @"gif" : OWSMimeTypeImageGif,
}; };
}); });
return result; return result;

@ -1,10 +1,14 @@
// //
// Copyright (c) 2017 Open Whisper Systems. All rights reserved. // Copyright (c) 2018 Open Whisper Systems. All rights reserved.
// //
@interface NSData (Image) @interface NSData (Image)
// If mimeType is non-nil, we ensure that the magic numbers agree with the
// mimeType.
+ (BOOL)ows_isValidImageAtPath:(NSString *)filePath; + (BOOL)ows_isValidImageAtPath:(NSString *)filePath;
+ (BOOL)ows_isValidImageAtPath:(NSString *)filePath mimeType:(nullable NSString *)mimeType;
- (BOOL)ows_isValidImage; - (BOOL)ows_isValidImage;
- (BOOL)ows_isValidImageWithMimeType:(nullable NSString *)mimeType;
@end @end

@ -1,7 +1,8 @@
// //
// Copyright (c) 2017 Open Whisper Systems. All rights reserved. // Copyright (c) 2018 Open Whisper Systems. All rights reserved.
// //
#import "MIMETypeUtil.h"
#import "NSData+Image.h" #import "NSData+Image.h"
typedef NS_ENUM(NSInteger, ImageFormat) { typedef NS_ENUM(NSInteger, ImageFormat) {
@ -16,6 +17,16 @@ typedef NS_ENUM(NSInteger, ImageFormat) {
@implementation NSData (Image) @implementation NSData (Image)
+ (BOOL)ows_isValidImageAtPath:(NSString *)filePath + (BOOL)ows_isValidImageAtPath:(NSString *)filePath
{
return [self ows_isValidImageAtPath:filePath mimeType:nil];
}
- (BOOL)ows_isValidImage
{
return [self ows_isValidImageWithMimeType:nil];
}
+ (BOOL)ows_isValidImageAtPath:(NSString *)filePath mimeType:(nullable NSString *)mimeType
{ {
NSError *error = nil; NSError *error = nil;
NSData *data = [NSData dataWithContentsOfFile:filePath options:NSDataReadingMappedIfSafe error:&error]; NSData *data = [NSData dataWithContentsOfFile:filePath options:NSDataReadingMappedIfSafe error:&error];
@ -23,16 +34,32 @@ typedef NS_ENUM(NSInteger, ImageFormat) {
DDLogError(@"%@ could not read image data: %@", self.logTag, error); DDLogError(@"%@ could not read image data: %@", self.logTag, error);
} }
return [data ows_isValidImage]; return [data ows_isValidImageWithMimeType:mimeType];
} }
- (BOOL)ows_isValidImage - (BOOL)ows_isValidImageWithMimeType:(nullable NSString *)mimeType
{ {
// Don't trust the file extension; iOS (e.g. UIKit, Core Graphics) will happily // Don't trust the file extension; iOS (e.g. UIKit, Core Graphics) will happily
// load a .gif with a .png file extension. // load a .gif with a .png file extension.
// //
// Instead, use the "magic numbers" in the file data to determine the image format. // Instead, use the "magic numbers" in the file data to determine the image format.
ImageFormat imageFormat = [self ows_guessImageFormat]; ImageFormat imageFormat = [self ows_guessImageFormat];
switch (imageFormat) {
case ImageFormat_Unknown:
return NO;
case ImageFormat_Png:
return (mimeType == nil || [mimeType isEqualToString:OWSMimeTypeImagePng]);
case ImageFormat_Gif:
return (mimeType == nil || [mimeType isEqualToString:OWSMimeTypeImageGif]);
case ImageFormat_Tiff:
return (mimeType == nil || [mimeType isEqualToString:OWSMimeTypeImageTiff1] ||
[mimeType isEqualToString:OWSMimeTypeImageTiff2]);
case ImageFormat_Jpeg:
return (mimeType == nil || [mimeType isEqualToString:OWSMimeTypeImageJpeg]);
case ImageFormat_Bmp:
return (mimeType == nil || [mimeType isEqualToString:OWSMimeTypeImageBmp1] ||
[mimeType isEqualToString:OWSMimeTypeImageBmp2]);
}
if (imageFormat == ImageFormat_Gif) { if (imageFormat == ImageFormat_Gif) {
return [self ows_hasValidGifSize]; return [self ows_hasValidGifSize];
} else if (imageFormat == ImageFormat_Unknown) { } else if (imageFormat == ImageFormat_Unknown) {

Loading…
Cancel
Save