Ignore "GIF of death."

// FREEBIE
pull/1/head
Matthew Chen 8 years ago
parent 6a76fed3c1
commit 5fcf89dff1

@ -34,6 +34,7 @@
345671011E89A5F1006EE662 /* ThreadUtil.m in Sources */ = {isa = PBXBuildFile; fileRef = 345671001E89A5F1006EE662 /* ThreadUtil.m */; };
3456710A1E8A9F5D006EE662 /* TSGenericAttachmentAdapter.m in Sources */ = {isa = PBXBuildFile; fileRef = 345671091E8A9F5D006EE662 /* TSGenericAttachmentAdapter.m */; };
346B66311F4E29B200E5122F /* CropScaleImageViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 346B66301F4E29B200E5122F /* CropScaleImageViewController.swift */; };
346B66381F55E24900E5122F /* NSData+Image.m in Sources */ = {isa = PBXBuildFile; fileRef = 346B66371F55E24900E5122F /* NSData+Image.m */; };
3471B1DA1EB7C63600F6AEC8 /* NewNonContactConversationViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 3471B1D91EB7C63600F6AEC8 /* NewNonContactConversationViewController.m */; };
3472229F1EB22FFE00E53955 /* AddToGroupViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 3472229E1EB22FFE00E53955 /* AddToGroupViewController.m */; };
348F2EAE1F0D21BC00D4ECE0 /* DeviceSleepManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 348F2EAD1F0D21BC00D4ECE0 /* DeviceSleepManager.swift */; };
@ -445,6 +446,8 @@
345671081E8A9F5D006EE662 /* TSGenericAttachmentAdapter.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = TSGenericAttachmentAdapter.h; sourceTree = "<group>"; };
345671091E8A9F5D006EE662 /* TSGenericAttachmentAdapter.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = TSGenericAttachmentAdapter.m; sourceTree = "<group>"; };
346B66301F4E29B200E5122F /* CropScaleImageViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CropScaleImageViewController.swift; sourceTree = "<group>"; };
346B66361F55E24900E5122F /* NSData+Image.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "NSData+Image.h"; sourceTree = "<group>"; };
346B66371F55E24900E5122F /* NSData+Image.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "NSData+Image.m"; sourceTree = "<group>"; };
3471B1D81EB7C63600F6AEC8 /* NewNonContactConversationViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = NewNonContactConversationViewController.h; sourceTree = "<group>"; };
3471B1D91EB7C63600F6AEC8 /* NewNonContactConversationViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = NewNonContactConversationViewController.m; sourceTree = "<group>"; };
3472229D1EB22FFE00E53955 /* AddToGroupViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AddToGroupViewController.h; sourceTree = "<group>"; };
@ -1422,6 +1425,9 @@
45E615151E8C590B0018AD52 /* DisplayableTextFilter.swift */,
76EB04EA18170B33006006FC /* FunctionalUtil.h */,
76EB04EB18170B33006006FC /* FunctionalUtil.m */,
455AC69A1F4F79E500134004 /* ImageCache.swift */,
346B66361F55E24900E5122F /* NSData+Image.h */,
346B66371F55E24900E5122F /* NSData+Image.m */,
B62F5E0E1C2980B4000D370C /* NSData+ows_StripToken.h */,
B62F5E0F1C2980B4000D370C /* NSData+ows_StripToken.m */,
76EB04EC18170B33006006FC /* NumberUtil.h */,
@ -1455,7 +1461,6 @@
76EB04FB18170B33006006FC /* Util.h */,
45F170D51E315310003FC1F2 /* Weak.swift */,
45F170CB1E310E22003FC1F2 /* WeakTimer.swift */,
455AC69A1F4F79E500134004 /* ImageCache.swift */,
);
path = util;
sourceTree = "<group>";
@ -2236,6 +2241,7 @@
45794E861E00620000066731 /* CallUIAdapter.swift in Sources */,
4585C4681ED8F8D200896AEA /* SafetyNumberConfirmationAlert.swift in Sources */,
FCFA64B71A24F6730007FB87 /* UIFont+OWS.m in Sources */,
346B66381F55E24900E5122F /* NSData+Image.m in Sources */,
B6B9ECFC198B31BA00C620D3 /* PushManager.m in Sources */,
45DF5DF21DDB843F00C936C7 /* CompareSafetyNumbersActivity.swift in Sources */,
458DE9D91DEE7B360071BB03 /* OWSWebRTCDataProtos.pb.m in Sources */,
@ -2580,7 +2586,11 @@
"DEBUG=1",
"$(inherited)",
);
"GCC_PREPROCESSOR_DEFINITIONS[arch=*]" = "DEBUG=1 $(inherited) SSK_BUILDING_FOR_TESTS=1";
"GCC_PREPROCESSOR_DEFINITIONS[arch=*]" = (
"DEBUG=1",
"$(inherited)",
"SSK_BUILDING_FOR_TESTS=1",
);
GCC_TREAT_IMPLICIT_FUNCTION_DECLARATIONS_AS_ERRORS = YES;
GCC_TREAT_INCOMPATIBLE_POINTER_TYPE_WARNINGS_AS_ERRORS = YES;
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;

@ -6,6 +6,7 @@
#import "AttachmentUploadView.h"
#import "FLAnimatedImage.h"
#import "JSQMediaItem+OWS.h"
#import "NSData+Image.h"
#import "TSAttachmentStream.h"
#import "UIColor+OWS.h"
#import <AssetsLibrary/AssetsLibrary.h>
@ -27,6 +28,8 @@ NS_ASSUME_NONNULL_BEGIN
// See comments on OWSMessageMediaAdapter.
@property (nonatomic, nullable, weak) id lastPresentingCell;
@property (nonatomic) NSNumber *isImageValid;
@end
#pragma mark -
@ -96,10 +99,21 @@ NS_ASSUME_NONNULL_BEGIN
#pragma mark - JSQMessageMediaData protocol
- (NSNumber *)isImageValid
{
if (!_isImageValid) {
_isImageValid = @([NSData isValidImageAtPath:[self.attachment mediaURL].path]);
}
return _isImageValid;
}
- (UIView *)mediaView {
OWSAssert([NSThread isMainThread]);
if (self.cachedImageView == nil) {
if (![self isImageValid]) {
return nil;
}
// Use Flipboard FLAnimatedImage library to display gifs
NSData *fileData = [NSData dataWithContentsOfURL:[self.attachment mediaURL]];
if (!fileData) {

@ -0,0 +1,12 @@
//
// Copyright (c) 2017 Open Whisper Systems. All rights reserved.
//
#import <Foundation/Foundation.h>
@interface NSData (Image)
+ (BOOL)isValidImageAtPath:(NSString *)filePath;
- (BOOL)isValidImage;
@end

@ -0,0 +1,139 @@
//
// Copyright (c) 2017 Open Whisper Systems. All rights reserved.
//
#import "NSData+Image.h"
typedef NS_ENUM(NSInteger, ImageFormat) {
ImageFormat_Unknown,
ImageFormat_Png,
ImageFormat_Gif,
ImageFormat_Tiff,
ImageFormat_Jpeg,
ImageFormat_Bmp,
};
@implementation NSData (Image)
+ (BOOL)isValidImageAtPath:(NSString *)filePath
{
NSError *error = nil;
NSData *data = [NSData dataWithContentsOfFile:filePath options:NSMappedRead error:&error];
if (error) {
DDLogError(@"%@ could not read image data: %@", self.tag, error);
}
return [data isValidImage];
}
- (BOOL)isValidImage
{
// Don't trust the file extension; iOS (e.g. UIKit, Core Graphics) will happily
// load a .gif with a .png file extension.
//
// Instead, use the "magic numbers" in the file data to determine the image format.
ImageFormat imageFormat = [self guessImageFormat];
if (imageFormat == ImageFormat_Gif) {
return [self hasValidGifSize];
} else if (imageFormat == ImageFormat_Unknown) {
return NO;
} else {
return YES;
}
}
- (ImageFormat)guessImageFormat
{
const NSUInteger kTwoBytesLength = 2;
if (self.length < kTwoBytesLength) {
return ImageFormat_Unknown;
}
unsigned char bytes[kTwoBytesLength];
[self getBytes:&bytes range:NSMakeRange(0, kTwoBytesLength)];
unsigned char byte0 = bytes[0];
unsigned char byte1 = bytes[1];
if (byte0 == 0x47 && byte1 == 0x49) {
return ImageFormat_Gif;
} else if (byte0 == 0x89 && byte1 == 0x50) {
return ImageFormat_Png;
} else if (byte0 == 0xff && byte1 == 0xd8) {
return ImageFormat_Jpeg;
} else if (byte0 == 0x42 && byte1 == 0x4d) {
return ImageFormat_Bmp;
} else if (byte0 == 0x4D && byte1 == 0x4D) {
// Motorola byte order TIFF
return ImageFormat_Tiff;
} else if (byte0 == 0x49 && byte1 == 0x49) {
// Intel byte order TIFF
return ImageFormat_Tiff;
}
return ImageFormat_Unknown;
}
+ (BOOL)areByteArraysEqual:(NSUInteger)length left:(unsigned char *)left right:(unsigned char *)right
{
for (NSUInteger i = 0; i < length; i++) {
if (left[i] != right[i]) {
return NO;
}
}
return YES;
}
// Parse the GIF header to prevent the "GIF of death" issue.
//
// See: https://blog.flanker017.me/cve-2017-2416-gif-remote-exec/
- (BOOL)hasValidGifSize
{
const NSUInteger kSignatureLength = 3;
const NSUInteger kVersionLength = 3;
const NSUInteger kWidthLength = 2;
const NSUInteger kHeightLength = 2;
const NSUInteger kPrefixLength = kSignatureLength + kVersionLength;
const NSUInteger kBufferLength = kSignatureLength + kVersionLength + kWidthLength + kHeightLength;
if (self.length < kBufferLength) {
return NO;
}
unsigned char bytes[kBufferLength];
[self getBytes:&bytes range:NSMakeRange(0, kBufferLength)];
unsigned char kGif87APrefix[kPrefixLength] = {
0x47, 0x49, 0x46, 0x38, 0x37, 0x61,
};
unsigned char kGif89APrefix[kPrefixLength] = {
0x47, 0x49, 0x46, 0x38, 0x39, 0x61,
};
if (![NSData areByteArraysEqual:kPrefixLength left:bytes right:kGif87APrefix]
&& ![NSData areByteArraysEqual:kPrefixLength left:bytes right:kGif89APrefix]) {
return NO;
}
NSUInteger width = ((NSUInteger)bytes[kPrefixLength + 0]) | (((NSUInteger)bytes[kPrefixLength + 1] << 8));
NSUInteger height = ((NSUInteger)bytes[kPrefixLength + 2]) | (((NSUInteger)bytes[kPrefixLength + 3] << 8));
// We need to ensure that the image size is "reasonable".
// We impose an arbitrary "very large" limit on image size
// to eliminate harmful values.
const NSUInteger kMaxValidSize = 1 << 18;
return (width > 0 && width < kMaxValidSize && height > 0 && height < kMaxValidSize);
}
#pragma mark - Logging
+ (NSString *)tag
{
return [NSString stringWithFormat:@"[%@]", self.class];
}
- (NSString *)tag
{
return self.class.tag;
}
@end

@ -3,7 +3,6 @@
//
#import "OWSOutgoingMessageCollectionViewCell.h"
#import "FLAnimatedImageView.h"
#import "OWSExpirationTimerView.h"
#import "UIView+OWS.h"
#import <JSQMessagesViewController/JSQMediaItem.h>

Loading…
Cancel
Save