Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 6 additions & 2 deletions FlowCrypt.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -317,6 +317,7 @@
A34D222A27294C67004E0220 /* PubLookupTest.swift in Sources */ = {isa = PBXBuildFile; fileRef = A34D222827294C67004E0220 /* PubLookupTest.swift */; };
A36108E9273C7A2E00A90E34 /* MockError.swift in Sources */ = {isa = PBXBuildFile; fileRef = A36108E8273C7A2E00A90E34 /* MockError.swift */; };
A3B7C31923F576BA0022D628 /* AppStartup.swift in Sources */ = {isa = PBXBuildFile; fileRef = A3B7C31823F576BA0022D628 /* AppStartup.swift */; };
AEC30B9A2DAF433A8783F432 /* HTMLSanitizationTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3BDC7CECAFB84E949FB2DAC2 /* HTMLSanitizationTests.swift */; };
C132B9B41EC2DBD800763715 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = C132B9B31EC2DBD800763715 /* AppDelegate.swift */; };
C132B9BB1EC2DBD800763715 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = C132B9BA1EC2DBD800763715 /* Assets.xcassets */; };
C132B9BE1EC2DBD800763715 /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = C132B9BC1EC2DBD800763715 /* LaunchScreen.storyboard */; };
Expand Down Expand Up @@ -566,6 +567,7 @@
32DCAC9C0512037018F434A1 /* BackendApi.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = BackendApi.swift; sourceTree = "<group>"; };
32DCAEFF16F5D91A35791730 /* DataExtensions.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DataExtensions.swift; sourceTree = "<group>"; };
32DCAF8424D0185FAA9401A7 /* Imap+send.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "Imap+send.swift"; sourceTree = "<group>"; };
3BDC7CECAFB84E949FB2DAC2 /* HTMLSanitizationTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HTMLSanitizationTests.swift; sourceTree = "<group>"; };
50531BE32629B9A80039BAE9 /* AttachmentNode.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AttachmentNode.swift; sourceTree = "<group>"; };
5109A77B272153B400D2CEB9 /* LeftAlignedCollectionViewFlowLayout.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LeftAlignedCollectionViewFlowLayout.swift; sourceTree = "<group>"; };
510BB63427BE92CC00B1011F /* RecipientBase.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RecipientBase.swift; sourceTree = "<group>"; };
Expand Down Expand Up @@ -1666,6 +1668,7 @@
9F7E902D26A1AD4C0021C07F /* Models */,
D254733324C597CD00DEE698 /* CoreTypesTest.swift */,
A3DAD5FD22E4574B00F2C4CD /* FlowCryptCoreTests.swift */,
3BDC7CECAFB84E949FB2DAC2 /* HTMLSanitizationTests.swift */,
21594C9526F1DBA900BE654C /* data.txt */,
);
path = Core;
Expand Down Expand Up @@ -2601,6 +2604,7 @@
9FC4117D268118AE004C0A69 /* PassPhraseStorageMock.swift in Sources */,
9F97650E267E16620058419D /* WKDURLsConstructorTests.swift in Sources */,
D7478BDE2D09113100D42659 /* PasswordProtectedMsgTest.swift in Sources */,
AEC30B9A2DAF433A8783F432 /* HTMLSanitizationTests.swift in Sources */,
9F976585267E194F0058419D /* FlowCryptCoreTests.swift in Sources */,
9F6F3C3C26ADFBC7005BD9C6 /* CoreComposeMessageMock.swift in Sources */,
9FC4116B2681186D004C0A69 /* KeyMethodsTest.swift in Sources */,
Expand Down Expand Up @@ -3917,15 +3921,15 @@
repositoryURL = "https://github.com/scinfu/SwiftSoup.git";
requirement = {
kind = upToNextMajorVersion;
minimumVersion = 2.13.5;
minimumVersion = 2.13.6;
};
};
95D83FB82A5D46C3006FDC33 /* XCRemoteSwiftPackageReference "SwiftLint" */ = {
isa = XCRemoteSwiftPackageReference;
repositoryURL = "https://github.com/realm/SwiftLint";
requirement = {
kind = upToNextMajorVersion;
minimumVersion = 0.63.3;
minimumVersion = 0.65.0;
};
};
95F55F982A7B89260000E50F /* XCRemoteSwiftPackageReference "ProgressHUD" */ = {
Expand Down

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,9 @@ final class ThreadDetailsViewController: TableNodeViewController {
}
}

var currentFolderPath: String { inboxItem.folderPath }
var currentFolderPath: String {
inboxItem.folderPath
}

let onComposeMessageAction: ((ComposeMessageAction) -> Void)?
let onComplete: MessageActionCompletion
Expand Down Expand Up @@ -266,9 +268,11 @@ final class ThreadDetailsViewController: TableNodeViewController {
isUsingKeyManager: appContext.clientConfigurationProvider.configuration.isUsingKeyManager
)

let sanitizedText = try await Core.shared.sanitizeHtml(html: decryptedText)

let processedMessage = ProcessedMessage(
message: data.rawMessage,
text: decryptedText,
text: sanitizedText,
type: .plain
)
handle(processedMessage: processedMessage, at: indexPath)
Expand Down
6 changes: 3 additions & 3 deletions FlowCrypt/Core/Core.swift
Original file line number Diff line number Diff line change
Expand Up @@ -262,11 +262,11 @@ class Core: KeyDecrypter, KeyParser, CoreComposeMessageType {
return try r.json.decodeJson(as: CoreRes.ZxcvbnStrengthBar.self)
}

private func waitUntilJavascirptReady(timeout: TimeInterval = 1) async throws {
func waitUntilJavaScriptReady(timeout: TimeInterval = 1) async throws {
let functionName = "handleRequestFromHost"
let deadline = Date().addingTimeInterval(timeout)
while Date() < deadline {
if await (try? webView.callAsyncJavaScript(
if let webView, await (try? webView.callAsyncJavaScript(
"return typeof \(functionName) === 'function';",
arguments: [:],
contentWorld: .page
Expand All @@ -285,7 +285,7 @@ class Core: KeyDecrypter, KeyParser, CoreComposeMessageType {
let requestData = [UInt8](data)

do {
try await waitUntilJavascirptReady()
try await waitUntilJavaScriptReady()
let response = try await webView.callAsyncJavaScript(
"return handleRequestFromHost(\"\(endpoint)\", \(paramsData), \(requestData))",
arguments: [:],
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -130,7 +130,9 @@ final class MessageHelper {
}

private func fetchOrDownloadData(for attachment: MessageAttachment, messageId: Identifier) async throws -> Data {
if let data = attachment.data { return data }
if let data = attachment.data {
return data
}
return try await download(attachment: attachment, messageId: messageId, progressHandler: nil)
}

Expand Down Expand Up @@ -232,6 +234,31 @@ final class MessageHelper {
private func process(
message: Message,
with decrypted: CoreRes.ParseDecryptMsg
) async throws -> ProcessedMessage {
var attachments: [MessageAttachment] = if message.raw != nil || message.attachments.isEmpty {
decrypted.blocks.compactMap(\.attMeta).compactMap(MessageAttachment.init)
} else {
message.attachments
}

let keyDetails = try await getKeyDetailsFromAttachment(
attachments: &attachments,
messageId: message.identifier
)

return try await Self.makeProcessedMessage(
message: message,
decrypted: decrypted,
attachments: attachments,
keyDetails: keyDetails
)
}

static func makeProcessedMessage(
message: Message,
decrypted: CoreRes.ParseDecryptMsg,
attachments: [MessageAttachment],
keyDetails: [KeyDetails]
) async throws -> ProcessedMessage {
let firstBlockParseErr = decrypted.blocks.first { $0.type == .blockParseErr }
let firstDecryptErrBlock = decrypted.blocks.first { $0.type == .decryptErr }
Expand Down Expand Up @@ -260,25 +287,17 @@ final class MessageHelper {
signature = nil
} else {
// decrypt / process success
text = decrypted.text
text = try await Core.shared.sanitizeHtml(html: decrypted.text)
messageType = decrypted.replyType == ReplyType.encrypted ? .encrypted : .plain
signature = await evaluateSignatureVerificationResult(
signature = evaluateSignatureVerificationResult(
signature: decrypted.blocks.first?.verifyRes
)
}

var attachments: [MessageAttachment] = if message.raw != nil || message.attachments.isEmpty {
decrypted.blocks.compactMap(\.attMeta).compactMap(MessageAttachment.init)
} else {
message.attachments
}

let keyDetails: [KeyDetails] = try await getKeyDetailsFromAttachment(attachments: &attachments, messageId: message.identifier)

// Also extract keyDetails from publicKey blocks (for encrypted messages)
let publicKeyBlockDetails: [KeyDetails] = decrypted.blocks
.filter { $0.type == .publicKey }
.compactMap { $0.keyDetails }
.compactMap(\.keyDetails)

return ProcessedMessage(
message: message,
Expand Down Expand Up @@ -343,7 +362,9 @@ extension MessageHelper {
guard let sender else { return [] }

let pubKeys = try localContactsProvider.retrievePubKeys(for: sender.email, shouldUpdateLastUsed: false).map(\.armored)
if pubKeys.isNotEmpty || onlyLocal { return pubKeys }
if pubKeys.isNotEmpty || onlyLocal {
return pubKeys
}

// try? because we may ignore update remote result
try? await pubLookup.fetchRemoteUpdateLocal(with: sender)
Expand All @@ -353,9 +374,9 @@ extension MessageHelper {
return contact.pubKeys.map(\.armored)
}

private func evaluateSignatureVerificationResult(
private static func evaluateSignatureVerificationResult(
signature: MsgBlock.VerifyRes?
) async -> ProcessedMessage.MessageSignature {
) -> ProcessedMessage.MessageSignature {
guard let signature else { return .unsigned }

if let error = signature.error {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -115,8 +115,7 @@ extension ProcessedMessage {
let (text, quote) = Self.parseHtmlQuote(from: html)
self.text = try await Core.shared.sanitizeHtml(html: text)
if let quote {
// SanitizeHtml replaces > with &gt; so need to convert it back
self.quote = try await (Core.shared.sanitizeHtml(html: quote)).replacingOccurrences(of: "&gt;", with: ">")
self.quote = try await Core.shared.sanitizeHtml(html: quote)
} else {
self.quote = nil
}
Expand Down Expand Up @@ -201,7 +200,7 @@ extension ProcessedMessage {
guard let lastLine = lines.popLast() else { break }

let trimmedLine = lastLine.trimmingCharacters(in: .whitespaces)
if trimmedLine.isEmpty || trimmedLine.hasPrefix(">") {
if trimmedLine.isEmpty || trimmedLine.hasPrefix(">") || trimmedLine.hasPrefix("&gt;") {
quoteLines.insert(lastLine, at: 0)
} else {
if trimmedLine.hasPrefix("On "), trimmedLine.hasSuffix(" wrote:") {
Expand All @@ -223,11 +222,17 @@ extension ProcessedMessage {
}

var attributedMessage: NSAttributedString {
String(text.prefix(maxLength)).attributed(color: type.textColor)
Self.decodeHTMLEntities(in: String(text.prefix(maxLength)))
.attributed(color: type.textColor)
}

var attributedQuote: NSAttributedString? {
guard let quote else { return nil }
return String(quote.prefix(maxLength)).attributed(color: type.textColor.withAlphaComponent(0.8))
return Self.decodeHTMLEntities(in: String(quote.prefix(maxLength)))
.attributed(color: type.textColor.withAlphaComponent(0.8))
}

private static func decodeHTMLEntities(in text: String) -> String {
(try? Entities.unescape(text)) ?? text
}
}
Loading
Loading