|
|
@ -120,7 +120,7 @@ public class OWSLinkPreview: MTLModel {
|
|
|
|
Logger.error("Discarding link preview; message has attachments.")
|
|
|
|
Logger.error("Discarding link preview; message has attachments.")
|
|
|
|
throw LinkPreviewError.invalidInput
|
|
|
|
throw LinkPreviewError.invalidInput
|
|
|
|
}
|
|
|
|
}
|
|
|
|
let urlString = previewProto.url
|
|
|
|
let urlString = stripPossibleLinkUrl(previewProto.url)
|
|
|
|
|
|
|
|
|
|
|
|
guard URL(string: urlString) != nil else {
|
|
|
|
guard URL(string: urlString) != nil else {
|
|
|
|
Logger.error("Could not parse preview URL.")
|
|
|
|
Logger.error("Could not parse preview URL.")
|
|
|
@ -265,7 +265,7 @@ public class OWSLinkPreview: MTLModel {
|
|
|
|
let maxCharacterCount = 2048
|
|
|
|
let maxCharacterCount = 2048
|
|
|
|
if result.count > maxCharacterCount {
|
|
|
|
if result.count > maxCharacterCount {
|
|
|
|
let endIndex = result.index(result.startIndex, offsetBy: maxCharacterCount)
|
|
|
|
let endIndex = result.index(result.startIndex, offsetBy: maxCharacterCount)
|
|
|
|
result = String(result[...endIndex])
|
|
|
|
result = String(result[..<endIndex])
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return result.filterStringForDisplay()
|
|
|
|
return result.filterStringForDisplay()
|
|
|
|
}
|
|
|
|
}
|
|
|
@ -307,6 +307,10 @@ public class OWSLinkPreview: MTLModel {
|
|
|
|
owsFailDebug("Invalid url.")
|
|
|
|
owsFailDebug("Invalid url.")
|
|
|
|
return nil
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
guard url.path.count > 0 else {
|
|
|
|
|
|
|
|
owsFailDebug("Invalid url (empty path).")
|
|
|
|
|
|
|
|
return nil
|
|
|
|
|
|
|
|
}
|
|
|
|
guard let result = whitelistedDomain(forUrl: url,
|
|
|
|
guard let result = whitelistedDomain(forUrl: url,
|
|
|
|
domainWhitelist: OWSLinkPreview.linkDomainWhitelist) else {
|
|
|
|
domainWhitelist: OWSLinkPreview.linkDomainWhitelist) else {
|
|
|
|
owsFailDebug("Missing domain.")
|
|
|
|
owsFailDebug("Missing domain.")
|
|
|
@ -315,6 +319,16 @@ public class OWSLinkPreview: MTLModel {
|
|
|
|
return result
|
|
|
|
return result
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
private class func stripPossibleLinkUrl(_ urlString: String) -> String {
|
|
|
|
|
|
|
|
var result = urlString.ows_stripped()
|
|
|
|
|
|
|
|
let suffixToStrip = ","
|
|
|
|
|
|
|
|
while result.hasSuffix(suffixToStrip) {
|
|
|
|
|
|
|
|
let endIndex = result.index(result.endIndex, offsetBy: -suffixToStrip.count)
|
|
|
|
|
|
|
|
result = String(result[..<endIndex]).ows_stripped()
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
return result
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
@objc
|
|
|
|
@objc
|
|
|
|
public class func isValidLinkUrl(_ urlString: String) -> Bool {
|
|
|
|
public class func isValidLinkUrl(_ urlString: String) -> Bool {
|
|
|
|
guard let url = URL(string: urlString) else {
|
|
|
|
guard let url = URL(string: urlString) else {
|
|
|
@ -396,11 +410,14 @@ public class OWSLinkPreview: MTLModel {
|
|
|
|
}
|
|
|
|
}
|
|
|
|
let components = body.components(separatedBy: .whitespacesAndNewlines)
|
|
|
|
let components = body.components(separatedBy: .whitespacesAndNewlines)
|
|
|
|
for component in components {
|
|
|
|
for component in components {
|
|
|
|
if isValidLinkUrl(component) {
|
|
|
|
let urlString = stripPossibleLinkUrl(component)
|
|
|
|
previewUrlCache.setObject(component as AnyObject, forKey: body as AnyObject)
|
|
|
|
if isValidLinkUrl(urlString) {
|
|
|
|
return component
|
|
|
|
previewUrlCache.setObject(urlString as AnyObject, forKey: body as AnyObject)
|
|
|
|
|
|
|
|
return urlString
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Use empty string to indicate "no preview URL" in the cache.
|
|
|
|
|
|
|
|
previewUrlCache.setObject("" as AnyObject, forKey: body as AnyObject)
|
|
|
|
return nil
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
@ -546,7 +563,7 @@ public class OWSLinkPreview: MTLModel {
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
var title: String?
|
|
|
|
var title: String?
|
|
|
|
if let rawTitle = NSRegularExpression.parseFirstMatch(pattern: "<meta\\s+property=\"og:title\"\\s+content=\"([^\"]+)\"\\s*/?>", text: linkText) {
|
|
|
|
if let rawTitle = NSRegularExpression.parseFirstMatch(pattern: "<meta\\s+property\\s*=\\s*\"og:title\"\\s+content\\s*=\\s*\"(.*?)\"\\s*/?>", text: linkText) {
|
|
|
|
if let decodedTitle = decodeHTMLEntities(inString: rawTitle) {
|
|
|
|
if let decodedTitle = decodeHTMLEntities(inString: rawTitle) {
|
|
|
|
let normalizedTitle = OWSLinkPreview.normalizeTitle(title: decodedTitle)
|
|
|
|
let normalizedTitle = OWSLinkPreview.normalizeTitle(title: decodedTitle)
|
|
|
|
if normalizedTitle.count > 0 {
|
|
|
|
if normalizedTitle.count > 0 {
|
|
|
@ -557,12 +574,16 @@ public class OWSLinkPreview: MTLModel {
|
|
|
|
|
|
|
|
|
|
|
|
Logger.verbose("title: \(String(describing: title))")
|
|
|
|
Logger.verbose("title: \(String(describing: title))")
|
|
|
|
|
|
|
|
|
|
|
|
guard let rawImageUrlString = NSRegularExpression.parseFirstMatch(pattern: "<meta\\s+property=\"og:image\"\\s+content=\"([^\"]+)\"\\s*/?>", text: linkText) else {
|
|
|
|
guard let rawImageUrlString = NSRegularExpression.parseFirstMatch(pattern: "<meta\\s+property\\s*=\\s*\"og:image\"\\s+content\\s*=\\s*\"(.*?)\"\\s*/?>", text: linkText) else {
|
|
|
|
return completion(OWSLinkPreviewDraft(urlString: linkUrlString, title: title))
|
|
|
|
return completion(OWSLinkPreviewDraft(urlString: linkUrlString, title: title))
|
|
|
|
}
|
|
|
|
}
|
|
|
|
guard let imageUrlString = decodeHTMLEntities(inString: rawImageUrlString)?.ows_stripped() else {
|
|
|
|
guard let imageUrlString = decodeHTMLEntities(inString: rawImageUrlString)?.ows_stripped() else {
|
|
|
|
return completion(OWSLinkPreviewDraft(urlString: linkUrlString, title: title))
|
|
|
|
return completion(OWSLinkPreviewDraft(urlString: linkUrlString, title: title))
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
guard isValidMediaUrl(imageUrlString) else {
|
|
|
|
|
|
|
|
Logger.error("Invalid image URL.")
|
|
|
|
|
|
|
|
return completion(OWSLinkPreviewDraft(urlString: linkUrlString, title: title))
|
|
|
|
|
|
|
|
}
|
|
|
|
Logger.verbose("imageUrlString: \(imageUrlString)")
|
|
|
|
Logger.verbose("imageUrlString: \(imageUrlString)")
|
|
|
|
guard let imageUrl = URL(string: imageUrlString) else {
|
|
|
|
guard let imageUrl = URL(string: imageUrlString) else {
|
|
|
|
Logger.error("Could not parse image URL.")
|
|
|
|
Logger.error("Could not parse image URL.")
|
|
|
|