[MM-14836] Prevent sharing of images over max pixels (#2733)
* Prevent sharing of images over max image size * Prevent sharing of images over max image pixels * Move readableMaxImagePixels
This commit is contained in:
parent
e3b4191af9
commit
39587aa3ae
7 changed files with 90 additions and 17 deletions
|
|
@ -44,10 +44,16 @@ class ShareViewController: SLComposeServiceViewController {
|
|||
override func isContentValid() -> Bool {
|
||||
// Do validation of contentText and/or NSExtensionContext attachments here
|
||||
if (attachments.count > 0) {
|
||||
let maxImagePixels = store.getMaxImagePixels()
|
||||
if attachments.hasImageLargerThan(pixels: maxImagePixels) {
|
||||
let readableMaxImagePixels = formatImagePixels(pixels: maxImagePixels)
|
||||
showErrorMessage(title: "", message: "Image attachments shared in Mattermost must be less than \(readableMaxImagePixels).", VC: self)
|
||||
}
|
||||
|
||||
let maxFileSize = store.getMaxFileSize()
|
||||
if attachments.hasAttachementLargerThan(fileSize: maxFileSize) {
|
||||
let readableMaxSize = formatFileSize(bytes: Double(maxFileSize))
|
||||
showErrorMessage(title: "", message: "File attachments shared in Mattermost must be less than \(readableMaxSize).", VC: self)
|
||||
let readableMaxFileSize = formatFileSize(fileSize: maxFileSize)
|
||||
showErrorMessage(title: "", message: "File attachments shared in Mattermost must be less than \(readableMaxFileSize).", VC: self)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -169,6 +175,26 @@ class ShareViewController: SLComposeServiceViewController {
|
|||
}
|
||||
return section
|
||||
}
|
||||
|
||||
func getImagePixels(imageUrl: URL) -> UInt64 {
|
||||
guard let imageData = try? Data(contentsOf: imageUrl) else {
|
||||
return 0
|
||||
}
|
||||
|
||||
guard let image = UIImage.init(data: imageData) else {
|
||||
return 0
|
||||
}
|
||||
|
||||
return getImagePixels(image: image)
|
||||
}
|
||||
|
||||
func getImagePixels(image: UIImage) -> UInt64 {
|
||||
guard let cgImage = image.cgImage else {
|
||||
return 0
|
||||
}
|
||||
|
||||
return UInt64(cgImage.width * cgImage.height)
|
||||
}
|
||||
|
||||
func extractDataFromContext() {
|
||||
for item in extensionContext?.inputItems as! [NSExtensionItem] {
|
||||
|
|
@ -196,6 +222,7 @@ class ShareViewController: SLComposeServiceViewController {
|
|||
let attachment = self.saveAttachment(url: url)
|
||||
if (attachment != nil) {
|
||||
attachment?.type = kUTTypeImage as String
|
||||
attachment?.imagePixels = self.getImagePixels(imageUrl: url)
|
||||
self.attachments.append(attachment!)
|
||||
}
|
||||
} else if let image = item as? UIImage {
|
||||
|
|
@ -207,6 +234,7 @@ class ShareViewController: SLComposeServiceViewController {
|
|||
let attachment = self.saveAttachment(url: tempImageURL!)
|
||||
if (attachment != nil) {
|
||||
attachment?.type = kUTTypeImage as String
|
||||
attachment?.imagePixels = self.getImagePixels(image: image)
|
||||
self.attachments.append(attachment!)
|
||||
}
|
||||
}
|
||||
|
|
@ -370,6 +398,7 @@ class ShareViewController: SLComposeServiceViewController {
|
|||
attachment.fileName = fileName
|
||||
attachment.fileURL = tempFileURL
|
||||
attachment.fileSize = attr.fileSize()
|
||||
|
||||
return attachment
|
||||
} catch {
|
||||
return nil
|
||||
|
|
@ -388,22 +417,32 @@ class ShareViewController: SLComposeServiceViewController {
|
|||
VC.present(alert, animated: true, completion: nil)
|
||||
}
|
||||
|
||||
func formatFileSize(bytes: Double) -> String {
|
||||
guard bytes > 0 else {
|
||||
return "0 bytes"
|
||||
func formatImagePixels(pixels: UInt64) -> String {
|
||||
let suffixes = ["pixels", "KP", "MP", "GP", "TP", "PP", "EP", "ZP", "YP"]
|
||||
let k: Double = 1000
|
||||
return formatSize(size: Double(pixels), k: k, suffixes: suffixes)
|
||||
}
|
||||
|
||||
func formatFileSize(fileSize: UInt64) -> String {
|
||||
let suffixes = ["bytes", "KB", "MB", "GB", "TB", "PB", "EB", "ZB", "YB"]
|
||||
let k: Double = 1024
|
||||
return formatSize(size: Double(fileSize), k: k, suffixes: suffixes)
|
||||
}
|
||||
|
||||
func formatSize(size: Double, k: Double, suffixes: Array<String>) -> String {
|
||||
guard size > 0 else {
|
||||
return "0 \(suffixes[0])"
|
||||
}
|
||||
|
||||
// Adapted from http://stackoverflow.com/a/18650828
|
||||
let suffixes = ["bytes", "KB", "MB", "GB", "TB", "PB", "EB", "ZB", "YB"]
|
||||
let k: Double = 1024
|
||||
let i = floor(log(bytes) / log(k))
|
||||
let i = floor(log(size) / log(k))
|
||||
|
||||
// Format number with thousands separator and everything below 1 GB with no decimal places.
|
||||
// Format number with thousands separator and everything below 1 giga with no decimal places.
|
||||
let numberFormatter = NumberFormatter()
|
||||
numberFormatter.maximumFractionDigits = i < 3 ? 0 : 1
|
||||
numberFormatter.numberStyle = .decimal
|
||||
|
||||
let numberString = numberFormatter.string(from: NSNumber(value: bytes / pow(k, i))) ?? "Unknown"
|
||||
let numberString = numberFormatter.string(from: NSNumber(value: size / pow(k, i))) ?? "Unknown"
|
||||
let suffix = suffixes[Int(i)]
|
||||
return "\(numberString) \(suffix)"
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,3 +1,5 @@
|
|||
import MobileCoreServices
|
||||
|
||||
/// A thread-safe array.
|
||||
public class AttachmentArray<Element>: NSObject {
|
||||
fileprivate let queue = DispatchQueue(label: "com.mattermost.SynchronizedArray", attributes: .concurrent)
|
||||
|
|
@ -266,4 +268,17 @@ extension AttachmentArray {
|
|||
|
||||
return exceed
|
||||
}
|
||||
|
||||
public func hasImageLargerThan(pixels: UInt64) -> Bool {
|
||||
var exceed = false
|
||||
|
||||
self.queue.sync {
|
||||
exceed = self.array.contains { element in
|
||||
let attachment = element as! AttachmentItem
|
||||
return attachment.imagePixels > pixels
|
||||
}
|
||||
}
|
||||
|
||||
return exceed
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -5,6 +5,7 @@ public class AttachmentItem: NSObject {
|
|||
public var fileURL: URL?
|
||||
public var fileSize: UInt64 = 0
|
||||
public var type: String?
|
||||
public var imagePixels: UInt64 = 0
|
||||
|
||||
public override init() {}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -3,5 +3,6 @@
|
|||
|
||||
@interface Constants : NSObject
|
||||
extern NSString *APP_GROUP_ID;
|
||||
extern UInt64 DEFAULT_SERVER_MAX_IMAGE_PIXELS;
|
||||
extern UInt64 DEFAULT_SERVER_MAX_FILE_SIZE;
|
||||
@end
|
||||
|
|
|
|||
|
|
@ -2,5 +2,6 @@
|
|||
|
||||
@implementation Constants
|
||||
NSString *APP_GROUP_ID = @"group.com.mattermost.rnbeta";
|
||||
UInt64 DEFAULT_SERVER_MAX_IMAGE_PIXELS = 6048 * 4032;
|
||||
UInt64 DEFAULT_SERVER_MAX_FILE_SIZE = 50 * 1024 * 1024;
|
||||
@end
|
||||
|
|
|
|||
|
|
@ -14,6 +14,7 @@
|
|||
-(NSString *)getCurrentUserId;
|
||||
-(NSDictionary *)getDefaultChannel:(NSString *)forTeamId;
|
||||
-(NSDictionary *)getEntities:(BOOL)loadFromFile;
|
||||
-(UInt64)getMaxImagePixels;
|
||||
-(UInt64)getMaxFileSize;
|
||||
-(NSArray *)getMyTeams;
|
||||
-(NSString *)getServerUrl;
|
||||
|
|
|
|||
|
|
@ -150,16 +150,31 @@
|
|||
return [credentials objectForKey:@"token"];
|
||||
}
|
||||
|
||||
-(UInt64)scanValueFromConfig:(NSDictionary *)config key:(NSString *)key {
|
||||
NSString *value = [config objectForKey:key];
|
||||
NSScanner *scanner = [NSScanner scannerWithString:value];
|
||||
unsigned long long convertedValue = 0;
|
||||
[scanner scanUnsignedLongLong:&convertedValue];
|
||||
return convertedValue;
|
||||
}
|
||||
|
||||
-(UInt64)getMaxImagePixels {
|
||||
NSDictionary *config = [self getConfig];
|
||||
NSString *key = @"MaxImagePixels";
|
||||
if (config != nil && [config objectForKey:key]) {
|
||||
return [self scanValueFromConfig:config key:key];
|
||||
}
|
||||
|
||||
return DEFAULT_SERVER_MAX_IMAGE_PIXELS;
|
||||
}
|
||||
|
||||
-(UInt64)getMaxFileSize {
|
||||
NSDictionary *config = [self getConfig];
|
||||
if (config != nil && [config objectForKey:@"MaxFileSize"]) {
|
||||
NSString *maxFileSize = [config objectForKey:@"MaxFileSize"];
|
||||
NSScanner *scanner = [NSScanner scannerWithString:maxFileSize];
|
||||
unsigned long long convertedValue = 0;
|
||||
[scanner scanUnsignedLongLong:&convertedValue];
|
||||
return convertedValue;
|
||||
NSString *key = @"MaxFileSize";
|
||||
if (config != nil && [config objectForKey:key]) {
|
||||
return [self scanValueFromConfig:config key:key];
|
||||
}
|
||||
|
||||
|
||||
return DEFAULT_SERVER_MAX_FILE_SIZE;
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Reference in a new issue