From c68eee5bfecd0ca78b9144feee85e94fd0973e2b Mon Sep 17 00:00:00 2001 From: Matthew Chen Date: Fri, 25 Jan 2019 12:48:48 -0500 Subject: [PATCH] Accept newlines in link preview titles. --- .../Models/CompareSafetyNumbersActivity.swift | 6 +++--- Signal/src/call/PeerConnectionClient.swift | 8 ++++---- Signal/src/util/Backup/OWSBackupAPI.swift | 4 ++-- .../Interactions/OWSLinkPreview.swift | 4 +++- .../src/Util/NSRegularExpression+SSK.swift | 7 ++++--- .../tests/Messages/OWSLinkPreviewTest.swift | 20 +++++++++++++++++++ 6 files changed, 36 insertions(+), 13 deletions(-) diff --git a/Signal/src/Models/CompareSafetyNumbersActivity.swift b/Signal/src/Models/CompareSafetyNumbersActivity.swift index d89be5c87..7fdeefc90 100644 --- a/Signal/src/Models/CompareSafetyNumbersActivity.swift +++ b/Signal/src/Models/CompareSafetyNumbersActivity.swift @@ -1,5 +1,5 @@ // -// Copyright (c) 2018 Open Whisper Systems. All rights reserved. +// Copyright (c) 2019 Open Whisper Systems. All rights reserved. // import Foundation @@ -85,13 +85,13 @@ class CompareSafetyNumbersActivity: UIActivity { // MARK: Helpers func numericOnly(string: String?) -> String? { - guard (string != nil) else { + guard let string = string else { return nil } var numericOnly: String? if let regex = try? NSRegularExpression(pattern: "\\D", options: .caseInsensitive) { - numericOnly = regex.stringByReplacingMatches(in: string!, options: .withTransparentBounds, range: NSRange(location: 0, length: string!.count), withTemplate: "") + numericOnly = regex.stringByReplacingMatches(in: string, options: .withTransparentBounds, range: NSRange(location: 0, length: string.utf16.count), withTemplate: "") } return numericOnly diff --git a/Signal/src/call/PeerConnectionClient.swift b/Signal/src/call/PeerConnectionClient.swift index b2f5db84b..15d85c73f 100644 --- a/Signal/src/call/PeerConnectionClient.swift +++ b/Signal/src/call/PeerConnectionClient.swift @@ -1,5 +1,5 @@ // -// Copyright (c) 2018 Open Whisper Systems. All rights reserved. +// Copyright (c) 2019 Open Whisper Systems. All rights reserved. // import Foundation @@ -1107,12 +1107,12 @@ class HardenedRTCSessionDescription { // Enforce Constant bit rate. let cbrRegex = try! NSRegularExpression(pattern: "(a=fmtp:111 ((?!cbr=).)*)\r?\n", options: .caseInsensitive) - description = cbrRegex.stringByReplacingMatches(in: description, options: [], range: NSRange(location: 0, length: description.count), withTemplate: "$1;cbr=1\r\n") + description = cbrRegex.stringByReplacingMatches(in: description, options: [], range: NSRange(location: 0, length: description.utf16.count), withTemplate: "$1;cbr=1\r\n") // Strip plaintext audio-level details // https://tools.ietf.org/html/rfc6464 let audioLevelRegex = try! NSRegularExpression(pattern: ".+urn:ietf:params:rtp-hdrext:ssrc-audio-level.*\r?\n", options: .caseInsensitive) - description = audioLevelRegex.stringByReplacingMatches(in: description, options: [], range: NSRange(location: 0, length: description.count), withTemplate: "") + description = audioLevelRegex.stringByReplacingMatches(in: description, options: [], range: NSRange(location: 0, length: description.utf16.count), withTemplate: "") return RTCSessionDescription.init(type: rtcSessionDescription.type, sdp: description) } @@ -1161,7 +1161,7 @@ class HardenedRTCSessionDescription { do { let regex = try NSRegularExpression(pattern: "[\\da-f]*:[\\da-f]*:[\\da-f:\\.]*", options: .caseInsensitive) - return regex.stringByReplacingMatches(in: sdp, options: [], range: NSRange(location: 0, length: sdp.count), withTemplate: "[ REDACTED_IPV6_ADDRESS ]") + return regex.stringByReplacingMatches(in: sdp, options: [], range: NSRange(location: 0, length: sdp.utf16.count), withTemplate: "[ REDACTED_IPV6_ADDRESS ]") } catch { owsFailDebug("Could not redact IPv6 addresses.") return "[Could not redact IPv6 addresses.]" diff --git a/Signal/src/util/Backup/OWSBackupAPI.swift b/Signal/src/util/Backup/OWSBackupAPI.swift index 27bf5aeea..aceea9ab5 100644 --- a/Signal/src/util/Backup/OWSBackupAPI.swift +++ b/Signal/src/util/Backup/OWSBackupAPI.swift @@ -1,5 +1,5 @@ // -// Copyright (c) 2018 Open Whisper Systems. All rights reserved. +// Copyright (c) 2019 Open Whisper Systems. All rights reserved. // import Foundation @@ -94,7 +94,7 @@ import PromiseKit var recipientIds = [String]() for recordName in recordNames { let regex = recordNamePrefixRegex - guard let match: NSTextCheckingResult = regex.firstMatch(in: recordName, options: [], range: NSRange(location: 0, length: recordName.count)) else { + guard let match: NSTextCheckingResult = regex.firstMatch(in: recordName, options: [], range: NSRange(location: 0, length: recordName.utf16.count)) else { Logger.warn("no match: \(recordName)") continue } diff --git a/SignalServiceKit/src/Messages/Interactions/OWSLinkPreview.swift b/SignalServiceKit/src/Messages/Interactions/OWSLinkPreview.swift index 784ba87d0..80c1515fa 100644 --- a/SignalServiceKit/src/Messages/Interactions/OWSLinkPreview.swift +++ b/SignalServiceKit/src/Messages/Interactions/OWSLinkPreview.swift @@ -751,7 +751,9 @@ public class OWSLinkPreview: MTLModel { } var title: String? - if let rawTitle = NSRegularExpression.parseFirstMatch(pattern: "", text: linkText) { + if let rawTitle = NSRegularExpression.parseFirstMatch(pattern: "", + text: linkText, + options: .dotMatchesLineSeparators) { if let decodedTitle = decodeHTMLEntities(inString: rawTitle) { let normalizedTitle = OWSLinkPreview.normalizeTitle(title: decodedTitle) if normalizedTitle.count > 0 { diff --git a/SignalServiceKit/src/Util/NSRegularExpression+SSK.swift b/SignalServiceKit/src/Util/NSRegularExpression+SSK.swift index b8e2fda49..e9b398d38 100644 --- a/SignalServiceKit/src/Util/NSRegularExpression+SSK.swift +++ b/SignalServiceKit/src/Util/NSRegularExpression+SSK.swift @@ -14,12 +14,13 @@ public extension NSRegularExpression { @objc public class func parseFirstMatch(pattern: String, - text: String) -> String? { + text: String, + options: NSRegularExpression.Options = []) -> String? { do { - let regex = try NSRegularExpression(pattern: pattern) + let regex = try NSRegularExpression(pattern: pattern, options: options) guard let match = regex.firstMatch(in: text, options: [], - range: NSRange(location: 0, length: text.count)) else { + range: NSRange(location: 0, length: text.utf16.count)) else { return nil } let matchRange = match.range(at: 1) diff --git a/SignalServiceKit/tests/Messages/OWSLinkPreviewTest.swift b/SignalServiceKit/tests/Messages/OWSLinkPreviewTest.swift index 979dbaca8..db5a1743c 100644 --- a/SignalServiceKit/tests/Messages/OWSLinkPreviewTest.swift +++ b/SignalServiceKit/tests/Messages/OWSLinkPreviewTest.swift @@ -434,4 +434,24 @@ class OWSLinkPreviewTest: SSKBaseTestSwift { self.waitForExpectations(timeout: 5.0, handler: nil) } + + // When using regular expressions to parse link titles, we need to use + // String.utf16.count, not String.count in the range. + func testRegexRanges() { + let regex = try! NSRegularExpression(pattern: "bob", options: []) + var text = "bob" + XCTAssertNotNil(regex.firstMatch(in: text, + options: [], + range: NSRange(location: 0, length: text.count))) + XCTAssertNotNil(regex.firstMatch(in: text, + options: [], + range: NSRange(location: 0, length: text.utf16.count))) + text = "😂😘🙂 bob" + XCTAssertNil(regex.firstMatch(in: text, + options: [], + range: NSRange(location: 0, length: text.count))) + XCTAssertNotNil(regex.firstMatch(in: text, + options: [], + range: NSRange(location: 0, length: text.utf16.count))) + } }