mirror of
https://github.com/oxen-io/session-ios.git
synced 2023-12-13 21:30:14 +01:00
Merge branch 'charlesmchen/linkPreviews3'
This commit is contained in:
commit
4b3c43eed6
6 changed files with 293 additions and 23 deletions
2
Pods
2
Pods
|
@ -1 +1 @@
|
||||||
Subproject commit ea60f60ea01bc51fc2434248890b494e37da98a5
|
Subproject commit 527dca96c23f0ac15664e9762987ff017cabdf90
|
|
@ -45,8 +45,15 @@ NS_ASSUME_NONNULL_BEGIN
|
||||||
|
|
||||||
_quotedMessage = [TSQuotedMessage quotedMessageForDataMessage:_dataMessage thread:_thread transaction:transaction];
|
_quotedMessage = [TSQuotedMessage quotedMessageForDataMessage:_dataMessage thread:_thread transaction:transaction];
|
||||||
_contact = [OWSContacts contactForDataMessage:_dataMessage transaction:transaction];
|
_contact = [OWSContacts contactForDataMessage:_dataMessage transaction:transaction];
|
||||||
_linkPreview =
|
|
||||||
[OWSLinkPreview buildValidatedLinkPreviewWithDataMessage:_dataMessage body:_body transaction:transaction];
|
NSError *linkPreviewError;
|
||||||
|
_linkPreview = [OWSLinkPreview buildValidatedLinkPreviewWithDataMessage:_dataMessage
|
||||||
|
body:_body
|
||||||
|
transaction:transaction
|
||||||
|
error:&linkPreviewError];
|
||||||
|
if (linkPreviewError && ![OWSLinkPreview isNoPreviewError:linkPreviewError]) {
|
||||||
|
OWSLogError(@"linkPreviewError: %@", linkPreviewError);
|
||||||
|
}
|
||||||
|
|
||||||
if (sentProto.unidentifiedStatus.count > 0) {
|
if (sentProto.unidentifiedStatus.count > 0) {
|
||||||
NSMutableArray<NSString *> *nonUdRecipientIds = [NSMutableArray new];
|
NSMutableArray<NSString *> *nonUdRecipientIds = [NSMutableArray new];
|
||||||
|
|
|
@ -4,6 +4,13 @@
|
||||||
|
|
||||||
import Foundation
|
import Foundation
|
||||||
|
|
||||||
|
@objc
|
||||||
|
public enum LinkPreviewError: Int, Error {
|
||||||
|
case invalidInput
|
||||||
|
case assertionFailure
|
||||||
|
case noPreview
|
||||||
|
}
|
||||||
|
|
||||||
@objc(OWSLinkPreview)
|
@objc(OWSLinkPreview)
|
||||||
public class OWSLinkPreview: MTLModel {
|
public class OWSLinkPreview: MTLModel {
|
||||||
@objc
|
@objc
|
||||||
|
@ -13,13 +20,13 @@ public class OWSLinkPreview: MTLModel {
|
||||||
public var title: String?
|
public var title: String?
|
||||||
|
|
||||||
@objc
|
@objc
|
||||||
public var attachmentId: String?
|
public var imageAttachmentId: String?
|
||||||
|
|
||||||
@objc
|
@objc
|
||||||
public init(urlString: String, title: String?, attachmentId: String?) {
|
public init(urlString: String, title: String?, imageAttachmentId: String?) {
|
||||||
self.urlString = urlString
|
self.urlString = urlString
|
||||||
self.title = title
|
self.title = title
|
||||||
self.attachmentId = attachmentId
|
self.imageAttachmentId = imageAttachmentId
|
||||||
|
|
||||||
super.init()
|
super.init()
|
||||||
}
|
}
|
||||||
|
@ -34,28 +41,36 @@ public class OWSLinkPreview: MTLModel {
|
||||||
try super.init(dictionary: dictionaryValue)
|
try super.init(dictionary: dictionaryValue)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@objc
|
||||||
|
public class func isNoPreviewError(_ error: Error) -> Bool {
|
||||||
|
guard let error = error as? LinkPreviewError else {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
return error == .noPreview
|
||||||
|
}
|
||||||
|
|
||||||
@objc
|
@objc
|
||||||
public class func buildValidatedLinkPreview(dataMessage: SSKProtoDataMessage,
|
public class func buildValidatedLinkPreview(dataMessage: SSKProtoDataMessage,
|
||||||
body: String?,
|
body: String?,
|
||||||
transaction: YapDatabaseReadWriteTransaction) -> OWSLinkPreview? {
|
transaction: YapDatabaseReadWriteTransaction) throws -> OWSLinkPreview {
|
||||||
guard let previewProto = dataMessage.preview else {
|
guard let previewProto = dataMessage.preview else {
|
||||||
return nil
|
throw LinkPreviewError.noPreview
|
||||||
}
|
}
|
||||||
let urlString = previewProto.url
|
let urlString = previewProto.url
|
||||||
|
|
||||||
guard URL(string: urlString) != nil else {
|
guard URL(string: urlString) != nil else {
|
||||||
owsFailDebug("Could not parse preview URL.")
|
Logger.error("Could not parse preview URL.")
|
||||||
return nil
|
throw LinkPreviewError.invalidInput
|
||||||
}
|
}
|
||||||
|
|
||||||
guard let body = body else {
|
guard let body = body else {
|
||||||
owsFailDebug("Preview for message without body.")
|
Logger.error("Preview for message without body.")
|
||||||
return nil
|
throw LinkPreviewError.invalidInput
|
||||||
}
|
}
|
||||||
let bodyComponents = body.components(separatedBy: .whitespacesAndNewlines)
|
let bodyComponents = body.components(separatedBy: .whitespacesAndNewlines)
|
||||||
guard bodyComponents.contains(urlString) else {
|
guard bodyComponents.contains(urlString) else {
|
||||||
owsFailDebug("URL not present in body.")
|
Logger.error("URL not present in body.")
|
||||||
return nil
|
throw LinkPreviewError.invalidInput
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: Verify that url host is in whitelist.
|
// TODO: Verify that url host is in whitelist.
|
||||||
|
@ -68,7 +83,8 @@ public class OWSLinkPreview: MTLModel {
|
||||||
imageAttachmentPointer.save(with: transaction)
|
imageAttachmentPointer.save(with: transaction)
|
||||||
imageAttachmentId = imageAttachmentPointer.uniqueId
|
imageAttachmentId = imageAttachmentPointer.uniqueId
|
||||||
} else {
|
} else {
|
||||||
owsFailDebug("Could not parse image proto.")
|
Logger.error("Could not parse image proto.")
|
||||||
|
throw LinkPreviewError.invalidInput
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -78,23 +94,101 @@ public class OWSLinkPreview: MTLModel {
|
||||||
}
|
}
|
||||||
let hasImage = imageAttachmentId != nil
|
let hasImage = imageAttachmentId != nil
|
||||||
if !hasTitle && !hasImage {
|
if !hasTitle && !hasImage {
|
||||||
owsFailDebug("Preview has neither title nor image.")
|
Logger.error("Preview has neither title nor image.")
|
||||||
return nil
|
throw LinkPreviewError.invalidInput
|
||||||
}
|
}
|
||||||
|
|
||||||
return OWSLinkPreview(urlString: urlString, title: title, attachmentId: imageAttachmentId)
|
return OWSLinkPreview(urlString: urlString, title: title, imageAttachmentId: imageAttachmentId)
|
||||||
}
|
}
|
||||||
|
|
||||||
@objc
|
@objc
|
||||||
public func removeAttachment(transaction: YapDatabaseReadWriteTransaction) {
|
public func removeAttachment(transaction: YapDatabaseReadWriteTransaction) {
|
||||||
guard let attachmentId = attachmentId else {
|
guard let imageAttachmentId = imageAttachmentId else {
|
||||||
owsFailDebug("No attachment id.")
|
owsFailDebug("No attachment id.")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
guard let attachment = TSAttachment.fetch(uniqueId: attachmentId, transaction: transaction) else {
|
guard let attachment = TSAttachment.fetch(uniqueId: imageAttachmentId, transaction: transaction) else {
|
||||||
owsFailDebug("Could not load attachment.")
|
owsFailDebug("Could not load attachment.")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
attachment.remove(with: transaction)
|
attachment.remove(with: transaction)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// MARK: - Domain Whitelist
|
||||||
|
|
||||||
|
private static let linkDomainWhitelist = [
|
||||||
|
"youtube.com",
|
||||||
|
"reddit.com",
|
||||||
|
"imgur.com",
|
||||||
|
"instagram.com"
|
||||||
|
]
|
||||||
|
|
||||||
|
private static let mediaDomainWhitelist = [
|
||||||
|
"ytimg.com",
|
||||||
|
"cdninstagram.com"
|
||||||
|
]
|
||||||
|
|
||||||
|
private static let protocolWhitelist = [
|
||||||
|
"https"
|
||||||
|
]
|
||||||
|
|
||||||
|
@objc
|
||||||
|
public class func isValidLinkUrl(_ urlString: String) -> Bool {
|
||||||
|
guard let url = URL(string: urlString) else {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
return isUrlInDomainWhitelist(url: url,
|
||||||
|
domainWhitelist: OWSLinkPreview.linkDomainWhitelist)
|
||||||
|
}
|
||||||
|
|
||||||
|
@objc
|
||||||
|
public class func isValidMediaUrl(_ urlString: String) -> Bool {
|
||||||
|
guard let url = URL(string: urlString) else {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
return isUrlInDomainWhitelist(url: url,
|
||||||
|
domainWhitelist: OWSLinkPreview.linkDomainWhitelist + OWSLinkPreview.mediaDomainWhitelist)
|
||||||
|
}
|
||||||
|
|
||||||
|
private class func isUrlInDomainWhitelist(url: URL, domainWhitelist: [String]) -> Bool {
|
||||||
|
guard let urlProtocol = url.scheme?.lowercased() else {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
guard protocolWhitelist.contains(urlProtocol) else {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
guard let domain = url.host?.lowercased() else {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
// TODO: We need to verify:
|
||||||
|
//
|
||||||
|
// * The final domain whitelist.
|
||||||
|
// * The relationship between the "link" whitelist and the "media" whitelist.
|
||||||
|
// * Exact match or suffix-based?
|
||||||
|
// * Case-insensitive?
|
||||||
|
// * Protocol?
|
||||||
|
for whitelistedDomain in domainWhitelist {
|
||||||
|
if domain == whitelistedDomain.lowercased() ||
|
||||||
|
domain.hasSuffix("." + whitelistedDomain.lowercased()) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
// MARK: - Text Parsing
|
||||||
|
|
||||||
|
@objc
|
||||||
|
public class func previewUrl(forMessageBodyText body: String?) -> String? {
|
||||||
|
guard let body = body else {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
let components = body.components(separatedBy: .whitespacesAndNewlines)
|
||||||
|
for component in components {
|
||||||
|
if isValidLinkUrl(component) {
|
||||||
|
return component
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -351,7 +351,7 @@ static const NSUInteger OWSMessageSchemaVersion = 4;
|
||||||
[self.contactShare removeAvatarAttachmentWithTransaction:transaction];
|
[self.contactShare removeAvatarAttachmentWithTransaction:transaction];
|
||||||
}
|
}
|
||||||
|
|
||||||
if (self.linkPreview.attachmentId) {
|
if (self.linkPreview.imageAttachmentId) {
|
||||||
[self.linkPreview removeAttachmentWithTransaction:transaction];
|
[self.linkPreview removeAttachmentWithTransaction:transaction];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1287,10 +1287,15 @@ NS_ASSUME_NONNULL_BEGIN
|
||||||
thread:oldGroupThread
|
thread:oldGroupThread
|
||||||
transaction:transaction];
|
transaction:transaction];
|
||||||
|
|
||||||
|
NSError *linkPreviewError;
|
||||||
OWSLinkPreview *_Nullable linkPreview =
|
OWSLinkPreview *_Nullable linkPreview =
|
||||||
[OWSLinkPreview buildValidatedLinkPreviewWithDataMessage:dataMessage
|
[OWSLinkPreview buildValidatedLinkPreviewWithDataMessage:dataMessage
|
||||||
body:body
|
body:body
|
||||||
transaction:transaction];
|
transaction:transaction
|
||||||
|
error:&linkPreviewError];
|
||||||
|
if (linkPreviewError && ![OWSLinkPreview isNoPreviewError:linkPreviewError]) {
|
||||||
|
OWSLogError(@"linkPreviewError: %@", linkPreviewError);
|
||||||
|
}
|
||||||
|
|
||||||
OWSLogDebug(@"incoming message from: %@ for group: %@ with timestamp: %lu",
|
OWSLogDebug(@"incoming message from: %@ for group: %@ with timestamp: %lu",
|
||||||
envelopeAddress(envelope),
|
envelopeAddress(envelope),
|
||||||
|
@ -1355,8 +1360,15 @@ NS_ASSUME_NONNULL_BEGIN
|
||||||
thread:thread
|
thread:thread
|
||||||
transaction:transaction];
|
transaction:transaction];
|
||||||
|
|
||||||
|
NSError *linkPreviewError;
|
||||||
OWSLinkPreview *_Nullable linkPreview =
|
OWSLinkPreview *_Nullable linkPreview =
|
||||||
[OWSLinkPreview buildValidatedLinkPreviewWithDataMessage:dataMessage body:body transaction:transaction];
|
[OWSLinkPreview buildValidatedLinkPreviewWithDataMessage:dataMessage
|
||||||
|
body:body
|
||||||
|
transaction:transaction
|
||||||
|
error:&linkPreviewError];
|
||||||
|
if (linkPreviewError && ![OWSLinkPreview isNoPreviewError:linkPreviewError]) {
|
||||||
|
OWSLogError(@"linkPreviewError: %@", linkPreviewError);
|
||||||
|
}
|
||||||
|
|
||||||
// Legit usage of senderTimestamp when creating incoming message from received envelope
|
// Legit usage of senderTimestamp when creating incoming message from received envelope
|
||||||
TSIncomingMessage *incomingMessage =
|
TSIncomingMessage *incomingMessage =
|
||||||
|
|
157
SignalServiceKit/tests/Messages/OWSLinkPreviewTest.swift
Normal file
157
SignalServiceKit/tests/Messages/OWSLinkPreviewTest.swift
Normal file
|
@ -0,0 +1,157 @@
|
||||||
|
//
|
||||||
|
// Copyright (c) 2019 Open Whisper Systems. All rights reserved.
|
||||||
|
//
|
||||||
|
|
||||||
|
import Foundation
|
||||||
|
import SignalServiceKit
|
||||||
|
import XCTest
|
||||||
|
|
||||||
|
class OWSLinkPreviewTest: SSKBaseTestSwift {
|
||||||
|
|
||||||
|
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 testBuildValidatedLinkPreview_TitleAndImage() {
|
||||||
|
let url = "https://www.youtube.com/watch?v=tP-Ipsat90c"
|
||||||
|
let body = "\(url)"
|
||||||
|
let previewBuilder = SSKProtoDataMessagePreview.builder(url: url)
|
||||||
|
previewBuilder.setTitle("Some Youtube Video")
|
||||||
|
let imageAttachmentBuilder = SSKProtoAttachmentPointer.builder(id: 1)
|
||||||
|
imageAttachmentBuilder.setKey(Randomness.generateRandomBytes(32))
|
||||||
|
imageAttachmentBuilder.setContentType(OWSMimeTypeImageJpeg)
|
||||||
|
previewBuilder.setImage(try! imageAttachmentBuilder.build())
|
||||||
|
let dataBuilder = SSKProtoDataMessage.builder()
|
||||||
|
dataBuilder.setPreview(try! previewBuilder.build())
|
||||||
|
|
||||||
|
self.readWrite { (transaction) in
|
||||||
|
XCTAssertNotNil(try! OWSLinkPreview.buildValidatedLinkPreview(dataMessage: try! dataBuilder.build(),
|
||||||
|
body: body,
|
||||||
|
transaction: transaction))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func testBuildValidatedLinkPreview_Title() {
|
||||||
|
let url = "https://www.youtube.com/watch?v=tP-Ipsat90c"
|
||||||
|
let body = "\(url)"
|
||||||
|
let previewBuilder = SSKProtoDataMessagePreview.builder(url: url)
|
||||||
|
previewBuilder.setTitle("Some Youtube Video")
|
||||||
|
let dataBuilder = SSKProtoDataMessage.builder()
|
||||||
|
dataBuilder.setPreview(try! previewBuilder.build())
|
||||||
|
|
||||||
|
self.readWrite { (transaction) in
|
||||||
|
XCTAssertNotNil(try! OWSLinkPreview.buildValidatedLinkPreview(dataMessage: try! dataBuilder.build(),
|
||||||
|
body: body,
|
||||||
|
transaction: transaction))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func testBuildValidatedLinkPreview_Image() {
|
||||||
|
let url = "https://www.youtube.com/watch?v=tP-Ipsat90c"
|
||||||
|
let body = "\(url)"
|
||||||
|
let previewBuilder = SSKProtoDataMessagePreview.builder(url: url)
|
||||||
|
let imageAttachmentBuilder = SSKProtoAttachmentPointer.builder(id: 1)
|
||||||
|
imageAttachmentBuilder.setKey(Randomness.generateRandomBytes(32))
|
||||||
|
imageAttachmentBuilder.setContentType(OWSMimeTypeImageJpeg)
|
||||||
|
previewBuilder.setImage(try! imageAttachmentBuilder.build())
|
||||||
|
let dataBuilder = SSKProtoDataMessage.builder()
|
||||||
|
dataBuilder.setPreview(try! previewBuilder.build())
|
||||||
|
|
||||||
|
self.readWrite { (transaction) in
|
||||||
|
XCTAssertNotNil(try! OWSLinkPreview.buildValidatedLinkPreview(dataMessage: try! dataBuilder.build(),
|
||||||
|
body: body,
|
||||||
|
transaction: transaction))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func testBuildValidatedLinkPreview_NoTitleOrImage() {
|
||||||
|
let url = "https://www.youtube.com/watch?v=tP-Ipsat90c"
|
||||||
|
let body = "\(url)"
|
||||||
|
let previewBuilder = SSKProtoDataMessagePreview.builder(url: url)
|
||||||
|
let dataBuilder = SSKProtoDataMessage.builder()
|
||||||
|
dataBuilder.setPreview(try! previewBuilder.build())
|
||||||
|
|
||||||
|
self.readWrite { (transaction) in
|
||||||
|
do {
|
||||||
|
_ = try OWSLinkPreview.buildValidatedLinkPreview(dataMessage: try! dataBuilder.build(),
|
||||||
|
body: body,
|
||||||
|
transaction: transaction)
|
||||||
|
XCTFail("Missing expected error.")
|
||||||
|
} catch {
|
||||||
|
// Do nothing.
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func testIsValidLinkUrl() {
|
||||||
|
XCTAssertTrue(OWSLinkPreview.isValidLinkUrl("https://www.youtube.com/watch?v=tP-Ipsat90c"))
|
||||||
|
XCTAssertTrue(OWSLinkPreview.isValidLinkUrl("https://youtube.com/watch?v=tP-Ipsat90c"))
|
||||||
|
XCTAssertTrue(OWSLinkPreview.isValidLinkUrl("https://www.youtube.com"))
|
||||||
|
|
||||||
|
// Allow arbitrary subdomains.
|
||||||
|
XCTAssertTrue(OWSLinkPreview.isValidMediaUrl("https://some.random.subdomain.youtube.com/watch?v=tP-Ipsat90c"))
|
||||||
|
|
||||||
|
// Don't allow HTTP, only HTTPS
|
||||||
|
XCTAssertFalse(OWSLinkPreview.isValidLinkUrl("http://youtube.com/watch?v=tP-Ipsat90c"))
|
||||||
|
XCTAssertFalse(OWSLinkPreview.isValidLinkUrl("mailto://youtube.com/watch?v=tP-Ipsat90c"))
|
||||||
|
XCTAssertFalse(OWSLinkPreview.isValidLinkUrl("ftp://youtube.com/watch?v=tP-Ipsat90c"))
|
||||||
|
|
||||||
|
// Don't allow similar domains.
|
||||||
|
XCTAssertFalse(OWSLinkPreview.isValidLinkUrl("https://xyoutube.com/watch?v=tP-Ipsat90c"))
|
||||||
|
XCTAssertFalse(OWSLinkPreview.isValidLinkUrl("https://youtubex.com/watch?v=tP-Ipsat90c"))
|
||||||
|
XCTAssertFalse(OWSLinkPreview.isValidLinkUrl("https://youtube.comx/watch?v=tP-Ipsat90c"))
|
||||||
|
XCTAssertFalse(OWSLinkPreview.isValidLinkUrl("https://www.xyoutube.com/watch?v=tP-Ipsat90c"))
|
||||||
|
XCTAssertFalse(OWSLinkPreview.isValidLinkUrl("https://www.youtubex.com/watch?v=tP-Ipsat90c"))
|
||||||
|
XCTAssertFalse(OWSLinkPreview.isValidLinkUrl("https://www.youtube.comx/watch?v=tP-Ipsat90c"))
|
||||||
|
|
||||||
|
// Don't allow media domains.
|
||||||
|
XCTAssertFalse(OWSLinkPreview.isValidLinkUrl("https://i.ytimg.com/vi/tP-Ipsat90c/maxresdefault.jpg"))
|
||||||
|
}
|
||||||
|
|
||||||
|
func testIsValidMediaUrl() {
|
||||||
|
XCTAssertTrue(OWSLinkPreview.isValidMediaUrl("https://www.youtube.com/watch?v=tP-Ipsat90c"))
|
||||||
|
XCTAssertTrue(OWSLinkPreview.isValidMediaUrl("https://youtube.com/watch?v=tP-Ipsat90c"))
|
||||||
|
XCTAssertTrue(OWSLinkPreview.isValidMediaUrl("https://www.youtube.com"))
|
||||||
|
|
||||||
|
// Allow arbitrary subdomains.
|
||||||
|
XCTAssertTrue(OWSLinkPreview.isValidMediaUrl("https://some.random.subdomain.youtube.com/watch?v=tP-Ipsat90c"))
|
||||||
|
|
||||||
|
// Don't allow HTTP, only HTTPS
|
||||||
|
XCTAssertFalse(OWSLinkPreview.isValidMediaUrl("http://youtube.com/watch?v=tP-Ipsat90c"))
|
||||||
|
XCTAssertFalse(OWSLinkPreview.isValidMediaUrl("mailto://youtube.com/watch?v=tP-Ipsat90c"))
|
||||||
|
XCTAssertFalse(OWSLinkPreview.isValidMediaUrl("ftp://youtube.com/watch?v=tP-Ipsat90c"))
|
||||||
|
|
||||||
|
// Don't allow similar domains.
|
||||||
|
XCTAssertFalse(OWSLinkPreview.isValidMediaUrl("https://xyoutube.com/watch?v=tP-Ipsat90c"))
|
||||||
|
XCTAssertFalse(OWSLinkPreview.isValidMediaUrl("https://youtubex.com/watch?v=tP-Ipsat90c"))
|
||||||
|
XCTAssertFalse(OWSLinkPreview.isValidMediaUrl("https://youtube.comx/watch?v=tP-Ipsat90c"))
|
||||||
|
XCTAssertFalse(OWSLinkPreview.isValidMediaUrl("https://www.xyoutube.com/watch?v=tP-Ipsat90c"))
|
||||||
|
XCTAssertFalse(OWSLinkPreview.isValidMediaUrl("https://www.youtubex.com/watch?v=tP-Ipsat90c"))
|
||||||
|
XCTAssertFalse(OWSLinkPreview.isValidMediaUrl("https://www.youtube.comx/watch?v=tP-Ipsat90c"))
|
||||||
|
|
||||||
|
// Allow media domains.
|
||||||
|
XCTAssertTrue(OWSLinkPreview.isValidMediaUrl("https://i.ytimg.com/vi/tP-Ipsat90c/maxresdefault.jpg"))
|
||||||
|
}
|
||||||
|
|
||||||
|
func testPreviewUrlForMessageBodyText() {
|
||||||
|
XCTAssertNil(OWSLinkPreview.previewUrl(forMessageBodyText: ""))
|
||||||
|
XCTAssertNil(OWSLinkPreview.previewUrl(forMessageBodyText: "alice bob jim"))
|
||||||
|
XCTAssertNil(OWSLinkPreview.previewUrl(forMessageBodyText: "alice bob jim http://"))
|
||||||
|
XCTAssertNil(OWSLinkPreview.previewUrl(forMessageBodyText: "alice bob jim http://a.com"))
|
||||||
|
|
||||||
|
XCTAssertEqual(OWSLinkPreview.previewUrl(forMessageBodyText: "https://www.youtube.com/watch?v=tP-Ipsat90c"),
|
||||||
|
"https://www.youtube.com/watch?v=tP-Ipsat90c")
|
||||||
|
XCTAssertEqual(OWSLinkPreview.previewUrl(forMessageBodyText: "alice bob https://www.youtube.com/watch?v=tP-Ipsat90c jim"),
|
||||||
|
"https://www.youtube.com/watch?v=tP-Ipsat90c")
|
||||||
|
|
||||||
|
// If there are more than one, take the first.
|
||||||
|
XCTAssertEqual(OWSLinkPreview.previewUrl(forMessageBodyText: "alice bob https://www.youtube.com/watch?v=tP-Ipsat90c jim https://www.youtube.com/watch?v=other-url carol"),
|
||||||
|
"https://www.youtube.com/watch?v=tP-Ipsat90c")
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in a new issue