Limit attachment caption length to 2k bytes

// FREEBIE
This commit is contained in:
Michael Kirk 2018-04-13 11:58:59 -04:00
parent 2a2f0bcadf
commit 13a432b9de
4 changed files with 169 additions and 12 deletions

View File

@ -48,9 +48,9 @@
34330A5C1E787A9800DF2FB9 /* dripicons-v2.ttf in Resources */ = {isa = PBXBuildFile; fileRef = 34330A5B1E787A9800DF2FB9 /* dripicons-v2.ttf */; };
34330A5E1E787BD800DF2FB9 /* ElegantIcons.ttf in Resources */ = {isa = PBXBuildFile; fileRef = 34330A5D1E787BD800DF2FB9 /* ElegantIcons.ttf */; };
34330AA31E79686200DF2FB9 /* OWSProgressView.m in Sources */ = {isa = PBXBuildFile; fileRef = 34330AA21E79686200DF2FB9 /* OWSProgressView.m */; };
34386A54207D271D009F5D9C /* NeverClearView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 34386A53207D271C009F5D9C /* NeverClearView.swift */; };
34386A51207D0C01009F5D9C /* HomeViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 34386A4D207D0C01009F5D9C /* HomeViewController.m */; };
34386A52207D0C01009F5D9C /* HomeViewCell.m in Sources */ = {isa = PBXBuildFile; fileRef = 34386A50207D0C01009F5D9C /* HomeViewCell.m */; };
34386A54207D271D009F5D9C /* NeverClearView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 34386A53207D271C009F5D9C /* NeverClearView.swift */; };
343A65951FC47D5E000477A1 /* DebugUISyncMessages.m in Sources */ = {isa = PBXBuildFile; fileRef = 343A65941FC47D5E000477A1 /* DebugUISyncMessages.m */; };
343A65981FC4CFE7000477A1 /* ConversationScrollButton.m in Sources */ = {isa = PBXBuildFile; fileRef = 343A65961FC4CFE6000477A1 /* ConversationScrollButton.m */; };
34480B361FD0929200BC14EF /* ShareAppExtensionContext.m in Sources */ = {isa = PBXBuildFile; fileRef = 34480B351FD0929200BC14EF /* ShareAppExtensionContext.m */; };
@ -281,6 +281,8 @@
4523D016206EDC2B00A2AB51 /* LRUCache.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4523D015206EDC2B00A2AB51 /* LRUCache.swift */; };
452C468F1E427E200087B011 /* OutboundCallInitiator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 452C468E1E427E200087B011 /* OutboundCallInitiator.swift */; };
452C7CA72037628B003D51A5 /* Weak.swift in Sources */ = {isa = PBXBuildFile; fileRef = 45F170D51E315310003FC1F2 /* Weak.swift */; };
452D1AF12081059C00A67F7F /* StringAdditionsTest.swift in Sources */ = {isa = PBXBuildFile; fileRef = 452D1AF02081059C00A67F7F /* StringAdditionsTest.swift */; };
452D1AF320810B6F00A67F7F /* String+OWS.swift in Sources */ = {isa = PBXBuildFile; fileRef = 452D1AF220810B6F00A67F7F /* String+OWS.swift */; };
452D1EE81DCA90D100A57EC4 /* MesssagesBubblesSizeCalculatorTest.swift in Sources */ = {isa = PBXBuildFile; fileRef = 452D1EE71DCA90D100A57EC4 /* MesssagesBubblesSizeCalculatorTest.swift */; };
452EA09E1EA7ABE00078744B /* AttachmentPointerView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 452EA09D1EA7ABE00078744B /* AttachmentPointerView.swift */; };
452EC6DF205E9E30000E787C /* MediaGalleryViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 452EC6DE205E9E30000E787C /* MediaGalleryViewController.swift */; };
@ -616,11 +618,11 @@
34330A5D1E787BD800DF2FB9 /* ElegantIcons.ttf */ = {isa = PBXFileReference; lastKnownFileType = file; path = ElegantIcons.ttf; sourceTree = "<group>"; };
34330AA11E79686200DF2FB9 /* OWSProgressView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = OWSProgressView.h; sourceTree = "<group>"; };
34330AA21E79686200DF2FB9 /* OWSProgressView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = OWSProgressView.m; sourceTree = "<group>"; };
34386A53207D271C009F5D9C /* NeverClearView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NeverClearView.swift; sourceTree = "<group>"; };
34386A4D207D0C01009F5D9C /* HomeViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = HomeViewController.m; sourceTree = "<group>"; };
34386A4E207D0C01009F5D9C /* HomeViewCell.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = HomeViewCell.h; sourceTree = "<group>"; };
34386A4F207D0C01009F5D9C /* HomeViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = HomeViewController.h; sourceTree = "<group>"; };
34386A50207D0C01009F5D9C /* HomeViewCell.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = HomeViewCell.m; sourceTree = "<group>"; };
34386A53207D271C009F5D9C /* NeverClearView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NeverClearView.swift; sourceTree = "<group>"; };
343A65931FC47D5D000477A1 /* DebugUISyncMessages.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = DebugUISyncMessages.h; sourceTree = "<group>"; };
343A65941FC47D5E000477A1 /* DebugUISyncMessages.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = DebugUISyncMessages.m; sourceTree = "<group>"; };
343A65961FC4CFE6000477A1 /* ConversationScrollButton.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ConversationScrollButton.m; sourceTree = "<group>"; };
@ -889,6 +891,8 @@
4523149F1F7E9E18003A428C /* DirectionalPanGestureRecognizer.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DirectionalPanGestureRecognizer.swift; sourceTree = "<group>"; };
4523D015206EDC2B00A2AB51 /* LRUCache.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LRUCache.swift; sourceTree = "<group>"; };
452C468E1E427E200087B011 /* OutboundCallInitiator.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = OutboundCallInitiator.swift; sourceTree = "<group>"; };
452D1AF02081059C00A67F7F /* StringAdditionsTest.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StringAdditionsTest.swift; sourceTree = "<group>"; };
452D1AF220810B6F00A67F7F /* String+OWS.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "String+OWS.swift"; sourceTree = "<group>"; };
452D1EE71DCA90D100A57EC4 /* MesssagesBubblesSizeCalculatorTest.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = MesssagesBubblesSizeCalculatorTest.swift; path = Models/MesssagesBubblesSizeCalculatorTest.swift; sourceTree = "<group>"; };
452EA09D1EA7ABE00078744B /* AttachmentPointerView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AttachmentPointerView.swift; sourceTree = "<group>"; };
452EC6DE205E9E30000E787C /* MediaGalleryViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MediaGalleryViewController.swift; sourceTree = "<group>"; };
@ -1434,6 +1438,7 @@
34480B601FD0A98800BC14EF /* UIView+OWS.m */,
346129D41FD20ADC00532771 /* UIViewController+OWS.h */,
346129D31FD20ADB00532771 /* UIViewController+OWS.m */,
452D1AF220810B6F00A67F7F /* String+OWS.swift */,
);
path = categories;
sourceTree = "<group>";
@ -2189,6 +2194,7 @@
45360B8F1F9527DA00FA666C /* SearcherTest.swift */,
B660F6B31C29868000687D6E /* UtilTest.h */,
B660F6B41C29868000687D6E /* UtilTest.m */,
452D1AF02081059C00A67F7F /* StringAdditionsTest.swift */,
);
path = util;
sourceTree = "<group>";
@ -3103,6 +3109,7 @@
344F248D2007CCD600CFB4F4 /* DisplayableText.swift in Sources */,
450998651FD8A34D00D89EB3 /* DeviceSleepManager.swift in Sources */,
3478506B1FD9B78A007B8332 /* NoopCallMessageHandler.swift in Sources */,
452D1AF320810B6F00A67F7F /* String+OWS.swift in Sources */,
451F8A3D1FD713CA005CB9DA /* ThreadViewHelper.m in Sources */,
346129AD1FD1F34E00532771 /* ImageCache.swift in Sources */,
452C7CA72037628B003D51A5 /* Weak.swift in Sources */,
@ -3349,6 +3356,7 @@
B660F6DA1C29868000687D6E /* ExceptionsTest.m in Sources */,
B660F6DB1C29868000687D6E /* FunctionalUtilTest.m in Sources */,
45E7A6A81E71CA7E00D44FB5 /* DisplayableTextFilterTest.swift in Sources */,
452D1AF12081059C00A67F7F /* StringAdditionsTest.swift in Sources */,
B660F6BB1C29868000687D6E /* OWSContactsManagerTest.m in Sources */,
B660F6D21C29868000687D6E /* PushManagerTest.m in Sources */,
455AC69E1F4F8B0300134004 /* ImageCacheTest.swift in Sources */,

View File

@ -0,0 +1,100 @@
//
// Copyright (c) 2018 Open Whisper Systems. All rights reserved.
//
import XCTest
class StringAdditionsTest: XCTestCase {
override func setUp() {
super.setUp()
// Put setup code here. This method is called before the invocation of each test method in the class.
}
override func tearDown() {
// Put teardown code here. This method is called after the invocation of each test method in the class.
super.tearDown()
}
func testASCII() {
let originalString = "Hello World"
var truncatedString = originalString.truncated(toByteCount: 8)
XCTAssertEqual("Hello Wo", truncatedString)
truncatedString = originalString.truncated(toByteCount: 0)
XCTAssertEqual("", truncatedString)
truncatedString = originalString.truncated(toByteCount: 11)
XCTAssertEqual("Hello World", truncatedString)
truncatedString = originalString.truncated(toByteCount: 12)
XCTAssertEqual("Hello World", truncatedString)
truncatedString = originalString.truncated(toByteCount: 100)
XCTAssertEqual("Hello World", truncatedString)
}
func testMultiByte() {
let originalString = "🇨🇦🇨🇦🇨🇦🇨🇦"
var truncatedString = originalString.truncated(toByteCount: 0)
XCTAssertEqual("", truncatedString)
truncatedString = originalString.truncated(toByteCount: 1)
XCTAssertEqual("", truncatedString)
truncatedString = originalString.truncated(toByteCount: 7)
XCTAssertEqual("", truncatedString)
truncatedString = originalString.truncated(toByteCount: 8)
XCTAssertEqual("🇨🇦", truncatedString)
truncatedString = originalString.truncated(toByteCount: 9)
XCTAssertEqual("🇨🇦", truncatedString)
truncatedString = originalString.truncated(toByteCount: 15)
XCTAssertEqual("🇨🇦", truncatedString)
truncatedString = originalString.truncated(toByteCount: 16)
XCTAssertEqual("🇨🇦🇨🇦", truncatedString)
truncatedString = originalString.truncated(toByteCount: 17)
XCTAssertEqual("🇨🇦🇨🇦", truncatedString)
}
func testMixed() {
let originalString = "Oh🇨🇦Canada🇨🇦"
var truncatedString = originalString.truncated(toByteCount: 0)
XCTAssertEqual("", truncatedString)
truncatedString = originalString.truncated(toByteCount: 1)
XCTAssertEqual("O", truncatedString)
truncatedString = originalString.truncated(toByteCount: 7)
XCTAssertEqual("Oh", truncatedString)
truncatedString = originalString.truncated(toByteCount: 9)
XCTAssertEqual("Oh", truncatedString)
truncatedString = originalString.truncated(toByteCount: 10)
XCTAssertEqual("Oh🇨🇦", truncatedString)
truncatedString = originalString.truncated(toByteCount: 11)
XCTAssertEqual("Oh🇨🇦C", truncatedString)
truncatedString = originalString.truncated(toByteCount: 23)
XCTAssertEqual("Oh🇨🇦Canada", truncatedString)
truncatedString = originalString.truncated(toByteCount: 24)
XCTAssertEqual("Oh🇨🇦Canada🇨🇦", truncatedString)
truncatedString = originalString.truncated(toByteCount: 25)
XCTAssertEqual("Oh🇨🇦Canada🇨🇦", truncatedString)
truncatedString = originalString.truncated(toByteCount: 100)
XCTAssertEqual("Oh🇨🇦Canada🇨🇦", truncatedString)
}
}

View File

@ -634,19 +634,19 @@ class CaptioningToolbar: UIView, UITextViewDelegate {
public func textView(_ textView: UITextView, shouldChangeTextIn range: NSRange, replacementText text: String) -> Bool {
// Limit caption character count. We do this in characters, not bytes.
// This character limit will be safely below our byte limit (16k) for almost all uses.
// Because the captioning interface doesn't allow newlines, in practice design pressures users to leave relatively short captions.
let maxCharacterCount = 2000
guard textView.text.count + text.count - range.length <= maxCharacterCount else {
let existingText: String = textView.text ?? ""
let proposedText: String = (existingText as NSString).replacingCharacters(in: range, with: text)
guard proposedText.utf8.count <= kOversizeTextMessageSizeThreshold else {
Logger.debug("\(self.logTag) in \(#function) long text was truncated")
self.lengthLimitLabel.isHidden = false
// Accept as much of the input as we can
let remainingSpace = maxCharacterCount - textView.text.count
if (remainingSpace) > 0 {
let acceptableAddition = text.substring(to: text.startIndex.advanced(by: remainingSpace))
textView.text = "\(textView.text ?? "")\(acceptableAddition)"
updateHeight(textView: textView)
let byteBudget = kOversizeTextMessageSizeThreshold + range.length - existingText.utf8.count
if byteBudget >= 0, let acceptableNewText = text.truncated(toByteCount: byteBudget) {
textView.text = (existingText as NSString).replacingCharacters(in: range, with: acceptableNewText)
}
return false
}
self.lengthLimitLabel.isHidden = true

View File

@ -0,0 +1,49 @@
//
// Copyright (c) 2018 Open Whisper Systems. All rights reserved.
//
import Foundation
public extension String {
// Truncates string to be less than or equal to byteCount, while ensuring we never truncate partial characters for multibyte characters.
public func truncated(toByteCount byteCount: UInt) -> String? {
var lowerBoundCharCount = 0
var upperBoundCharCount = self.count
while (lowerBoundCharCount < upperBoundCharCount) {
guard let upperBoundData = self.prefix(upperBoundCharCount).data(using: .utf8) else {
owsFail("in \(#function) upperBoundData was unexpectedly nil")
return nil
}
if upperBoundData.count <= byteCount {
break
}
// converge
if upperBoundCharCount - lowerBoundCharCount == 1 {
upperBoundCharCount = lowerBoundCharCount
break
}
let midpointCharCount = (lowerBoundCharCount + upperBoundCharCount) / 2
let midpointString = self.prefix(midpointCharCount)
guard let midpointData = midpointString.data(using: .utf8) else {
owsFail("in \(#function) midpointData was unexpectedly nil")
return nil
}
let midpointByteCount = midpointData.count
if midpointByteCount < byteCount {
lowerBoundCharCount = midpointCharCount
} else {
upperBoundCharCount = midpointCharCount
}
}
return String(self.prefix(upperBoundCharCount))
}
}