From 9149282e94493494cc6ee23f6c5f7df1cf753e8c Mon Sep 17 00:00:00 2001 From: Matthew Chen Date: Thu, 24 Jan 2019 13:28:54 -0500 Subject: [PATCH] Resize link preview images if necessary. --- Signal.xcodeproj/project.pbxproj | 4 - Signal/src/AppDelegate.m | 2 +- .../ViewControllers/HomeView/HomeViewCell.m | 2 +- Signal/src/views/OWSProgressView.m | 4 +- SignalMessaging/SignalMessaging.h | 1 - SignalMessaging/categories/UIView+OWS.h | 2 +- .../Interactions/OWSLinkPreview.swift | 76 ++++++++++++++----- .../src/Util}/OWSMath.h | 2 +- .../SignalShareExtension-Bridging-Header.h | 4 +- 9 files changed, 66 insertions(+), 31 deletions(-) rename {SignalMessaging/utils => SignalServiceKit/src/Util}/OWSMath.h (97%) diff --git a/Signal.xcodeproj/project.pbxproj b/Signal.xcodeproj/project.pbxproj index 0b624001c..aad3871f8 100644 --- a/Signal.xcodeproj/project.pbxproj +++ b/Signal.xcodeproj/project.pbxproj @@ -60,7 +60,6 @@ 343A65981FC4CFE7000477A1 /* ConversationScrollButton.m in Sources */ = {isa = PBXBuildFile; fileRef = 343A65961FC4CFE6000477A1 /* ConversationScrollButton.m */; }; 3441FD9F21A3604F00BB9542 /* BackupRestoreViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3441FD9E21A3604F00BB9542 /* BackupRestoreViewController.swift */; }; 34480B361FD0929200BC14EF /* ShareAppExtensionContext.m in Sources */ = {isa = PBXBuildFile; fileRef = 34480B351FD0929200BC14EF /* ShareAppExtensionContext.m */; }; - 34480B491FD0A60200BC14EF /* OWSMath.h in Headers */ = {isa = PBXBuildFile; fileRef = 34480B481FD0A60200BC14EF /* OWSMath.h */; settings = {ATTRIBUTES = (Public, ); }; }; 34480B551FD0A7A400BC14EF /* DebugLogger.h in Headers */ = {isa = PBXBuildFile; fileRef = 34480B4D1FD0A7A300BC14EF /* DebugLogger.h */; settings = {ATTRIBUTES = (Public, ); }; }; 34480B561FD0A7A400BC14EF /* DebugLogger.m in Sources */ = {isa = PBXBuildFile; fileRef = 34480B4E1FD0A7A300BC14EF /* DebugLogger.m */; }; 34480B571FD0A7A400BC14EF /* OWSScrubbingLogFormatter.h in Headers */ = {isa = PBXBuildFile; fileRef = 34480B4F1FD0A7A300BC14EF /* OWSScrubbingLogFormatter.h */; }; @@ -710,7 +709,6 @@ 34480B351FD0929200BC14EF /* ShareAppExtensionContext.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ShareAppExtensionContext.m; sourceTree = ""; }; 34480B371FD092A900BC14EF /* SignalShareExtension-Bridging-Header.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "SignalShareExtension-Bridging-Header.h"; sourceTree = ""; }; 34480B381FD092E300BC14EF /* SignalShareExtension-Prefix.pch */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "SignalShareExtension-Prefix.pch"; sourceTree = ""; }; - 34480B481FD0A60200BC14EF /* OWSMath.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = OWSMath.h; sourceTree = ""; }; 34480B4D1FD0A7A300BC14EF /* DebugLogger.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = DebugLogger.h; sourceTree = ""; }; 34480B4E1FD0A7A300BC14EF /* DebugLogger.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = DebugLogger.m; sourceTree = ""; }; 34480B4F1FD0A7A300BC14EF /* OWSScrubbingLogFormatter.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = OWSScrubbingLogFormatter.h; sourceTree = ""; }; @@ -1564,7 +1562,6 @@ 346129AA1FD1F0EE00532771 /* OWSFormat.m */, 45666EC71D994C0D008FE134 /* OWSGroupAvatarBuilder.h */, 45666EC81D994C0D008FE134 /* OWSGroupAvatarBuilder.m */, - 34480B481FD0A60200BC14EF /* OWSMath.h */, 346129371FD1B47200532771 /* OWSPreferences.h */, 346129381FD1B47200532771 /* OWSPreferences.m */, 34641E172088D7E900E2EDE5 /* OWSScreenLock.swift */, @@ -2606,7 +2603,6 @@ isa = PBXHeadersBuildPhase; buildActionMask = 2147483647; files = ( - 34480B491FD0A60200BC14EF /* OWSMath.h in Headers */, 346129E71FD5C0C600532771 /* OWSDatabaseMigrationRunner.h in Headers */, 34AC09F9211B39B100997B47 /* CountryCodeViewController.h in Headers */, 34ABB2C52090C59700C727A6 /* OWSResaveCollectionDBMigration.h in Headers */, diff --git a/Signal/src/AppDelegate.m b/Signal/src/AppDelegate.m index e4a613128..00e35e94d 100644 --- a/Signal/src/AppDelegate.m +++ b/Signal/src/AppDelegate.m @@ -24,7 +24,6 @@ #import #import #import -#import #import #import #import @@ -38,6 +37,7 @@ #import #import #import +#import #import #import #import diff --git a/Signal/src/ViewControllers/HomeView/HomeViewCell.m b/Signal/src/ViewControllers/HomeView/HomeViewCell.m index 820598db2..8b6b58093 100644 --- a/Signal/src/ViewControllers/HomeView/HomeViewCell.m +++ b/Signal/src/ViewControllers/HomeView/HomeViewCell.m @@ -6,9 +6,9 @@ #import "OWSAvatarBuilder.h" #import "Signal-Swift.h" #import -#import #import #import +#import #import #import #import diff --git a/Signal/src/views/OWSProgressView.m b/Signal/src/views/OWSProgressView.m index 881638399..7e8f1985a 100644 --- a/Signal/src/views/OWSProgressView.m +++ b/Signal/src/views/OWSProgressView.m @@ -1,10 +1,10 @@ // -// Copyright (c) 2018 Open Whisper Systems. All rights reserved. +// Copyright (c) 2019 Open Whisper Systems. All rights reserved. // #import "OWSProgressView.h" -#import #import +#import NS_ASSUME_NONNULL_BEGIN diff --git a/SignalMessaging/SignalMessaging.h b/SignalMessaging/SignalMessaging.h index bd2a2b93c..5416e1aae 100644 --- a/SignalMessaging/SignalMessaging.h +++ b/SignalMessaging/SignalMessaging.h @@ -31,7 +31,6 @@ FOUNDATION_EXPORT const unsigned char SignalMessagingVersionString[]; #import #import #import -#import #import #import #import diff --git a/SignalMessaging/categories/UIView+OWS.h b/SignalMessaging/categories/UIView+OWS.h index bfb091e79..2a7f63224 100644 --- a/SignalMessaging/categories/UIView+OWS.h +++ b/SignalMessaging/categories/UIView+OWS.h @@ -2,8 +2,8 @@ // Copyright (c) 2019 Open Whisper Systems. All rights reserved. // -#import "OWSMath.h" #import +#import #import NS_ASSUME_NONNULL_BEGIN diff --git a/SignalServiceKit/src/Messages/Interactions/OWSLinkPreview.swift b/SignalServiceKit/src/Messages/Interactions/OWSLinkPreview.swift index 078fd50cb..9a1eda7dc 100644 --- a/SignalServiceKit/src/Messages/Interactions/OWSLinkPreview.swift +++ b/SignalServiceKit/src/Messages/Interactions/OWSLinkPreview.swift @@ -12,6 +12,7 @@ public enum LinkPreviewError: Int, Error { case assertionFailure case couldNotDownload case featureDisabled + case invalidContent } // MARK: - OWSLinkPreviewDraft @@ -540,7 +541,7 @@ public class OWSLinkPreview: MTLModel { return promise } - private class func downloadImage(url urlString: String) -> Promise { + private class func downloadImage(url urlString: String, imageMimeType: String) -> Promise { Logger.verbose("url: \(urlString)") @@ -566,8 +567,32 @@ public class OWSLinkPreview: MTLModel { } return promise.then(on: DispatchQueue.global()) { (asset: ProxiedContentAsset) -> Promise in do { + let imageSize = NSData.imageSize(forFilePath: asset.filePath, mimeType: imageMimeType) + guard imageSize.width > 0, imageSize.height > 0 else { + Logger.error("Link preview is invalid or has invalid size.") + return Promise(error: LinkPreviewError.invalidContent) + } let data = try Data(contentsOf: URL(fileURLWithPath: asset.filePath)) - return Promise.value(data) + + let maxImageSize: CGFloat = 1024 + let shouldResize = imageSize.width > maxImageSize || imageSize.height > maxImageSize + guard shouldResize else { + return Promise.value(data) + } + + guard let srcImage = UIImage(data: data) else { + Logger.error("Could not parse image.") + return Promise(error: LinkPreviewError.invalidContent) + } + guard let dstImage = srcImage.resized(withMaxDimensionPoints: maxImageSize) else { + Logger.error("Could not resize image.") + return Promise(error: LinkPreviewError.invalidContent) + } + guard let dstData = UIImageJPEGRepresentation(dstImage, 0.8) else { + Logger.error("Could not write resized image.") + return Promise(error: LinkPreviewError.invalidContent) + } + return Promise.value(dstData) } catch { owsFailDebug("Could not load asset data: \(type(of: asset.filePath)).") return Promise(error: LinkPreviewError.assertionFailure) @@ -617,27 +642,16 @@ public class OWSLinkPreview: MTLModel { Logger.error("Invalid image URL.") return Promise.value(OWSLinkPreviewDraft(urlString: linkUrlString, title: title)) } - Logger.verbose("imageUrlString: \(imageUrlString)") - guard let imageUrl = URL(string: imageUrlString) else { - Logger.error("Could not parse image URL.") - return Promise.value(OWSLinkPreviewDraft(urlString: linkUrlString, title: title)) - } - let imageFilename = imageUrl.lastPathComponent - let imageFileExtension = (imageFilename as NSString).pathExtension.lowercased() - guard let imageMimeType = MIMETypeUtil.mimeType(forFileExtension: imageFileExtension) else { - Logger.error("Image URL has unknown content type: \(imageFileExtension).") + guard let imageFileExtension = fileExtension(forImageUrl: imageUrlString) else { + Logger.error("Image URL has unknown or invalid file extension: \(imageUrlString).") return Promise.value(OWSLinkPreviewDraft(urlString: linkUrlString, title: title)) } - let kValidMimeTypes = [ - OWSMimeTypeImagePng, - OWSMimeTypeImageJpeg - ] - guard kValidMimeTypes.contains(imageMimeType) else { - Logger.error("Image URL has invalid content type: \(imageMimeType).") + guard let imageMimeType = mimetype(forImageFileExtension: imageFileExtension) else { + Logger.error("Image URL has unknown or invalid content type: \(imageUrlString).") return Promise.value(OWSLinkPreviewDraft(urlString: linkUrlString, title: title)) } - return downloadImage(url: imageUrlString) + return downloadImage(url: imageUrlString, imageMimeType: imageMimeType) .then(on: DispatchQueue.global()) { (imageData: Data) -> Promise in let imageFilePath = OWSFileSystem.temporaryFilePath(withFileExtension: imageFileExtension) do { @@ -665,6 +679,32 @@ public class OWSLinkPreview: MTLModel { } } + private class func fileExtension(forImageUrl urlString: String) -> String? { + guard let imageUrl = URL(string: urlString) else { + Logger.error("Could not parse image URL.") + return nil + } + let imageFilename = imageUrl.lastPathComponent + let imageFileExtension = (imageFilename as NSString).pathExtension.lowercased() + return imageFileExtension + } + + private class func mimetype(forImageFileExtension imageFileExtension: String) -> String? { + guard let imageMimeType = MIMETypeUtil.mimeType(forFileExtension: imageFileExtension) else { + Logger.error("Image URL has unknown content type: \(imageFileExtension).") + return nil + } + let kValidMimeTypes = [ + OWSMimeTypeImagePng, + OWSMimeTypeImageJpeg + ] + guard kValidMimeTypes.contains(imageMimeType) else { + Logger.error("Image URL has invalid content type: \(imageMimeType).") + return nil + } + return imageMimeType + } + private class func decodeHTMLEntities(inString value: String) -> String? { guard let data = value.data(using: .utf8) else { return nil diff --git a/SignalMessaging/utils/OWSMath.h b/SignalServiceKit/src/Util/OWSMath.h similarity index 97% rename from SignalMessaging/utils/OWSMath.h rename to SignalServiceKit/src/Util/OWSMath.h index d159c9f36..1a9ec40cb 100644 --- a/SignalMessaging/utils/OWSMath.h +++ b/SignalServiceKit/src/Util/OWSMath.h @@ -1,5 +1,5 @@ // -// Copyright (c) 2018 Open Whisper Systems. All rights reserved. +// Copyright (c) 2019 Open Whisper Systems. All rights reserved. // NS_ASSUME_NONNULL_BEGIN diff --git a/SignalShareExtension/SignalShareExtension-Bridging-Header.h b/SignalShareExtension/SignalShareExtension-Bridging-Header.h index c7a9dd1b8..bc241fe7c 100644 --- a/SignalShareExtension/SignalShareExtension-Bridging-Header.h +++ b/SignalShareExtension/SignalShareExtension-Bridging-Header.h @@ -1,5 +1,5 @@ // -// Copyright (c) 2018 Open Whisper Systems. All rights reserved. +// Copyright (c) 2019 Open Whisper Systems. All rights reserved. // #import @@ -14,7 +14,6 @@ #import #import #import -#import #import #import #import @@ -23,5 +22,6 @@ #import #import #import +#import #import #import