Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
16 commits
Select commit Hold shift + click to select a range
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
4 changes: 0 additions & 4 deletions FlowCrypt.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -190,7 +190,6 @@
9FC4112E2595EA8B001180A8 /* Gmail+Search.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9FC4112D2595EA8B001180A8 /* Gmail+Search.swift */; };
9FC411352595EA94001180A8 /* Imap+Search.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9FC411342595EA94001180A8 /* Imap+Search.swift */; };
9FC4114C25961CEA001180A8 /* MailServiceProviderType.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9FC4114B25961CEA001180A8 /* MailServiceProviderType.swift */; };
9FC4116526811861004C0A69 /* BackupServiceMock.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9F4163EC266574CB00106194 /* BackupServiceMock.swift */; };
9FC4116B2681186D004C0A69 /* KeyMethodsTest.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9FA0157926565B7800CBBA05 /* KeyMethodsTest.swift */; };
9FC41171268118A7004C0A69 /* PassPhraseStorageTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9FC7EBA2266EB95300F3BF5D /* PassPhraseStorageTests.swift */; };
9FC4117D268118AE004C0A69 /* PassPhraseStorageMock.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9FC7EBCF266EBE1D00F3BF5D /* PassPhraseStorageMock.swift */; };
Expand Down Expand Up @@ -527,7 +526,6 @@
9F3EF33023B1785600FA0CEF /* MsgListViewConroller.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MsgListViewConroller.swift; sourceTree = "<group>"; };
9F4163B7265ED61C00106194 /* SetupInitialViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SetupInitialViewController.swift; sourceTree = "<group>"; };
9F4163E5266520B600106194 /* CommonNodesInputs.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CommonNodesInputs.swift; sourceTree = "<group>"; };
9F4163EC266574CB00106194 /* BackupServiceMock.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BackupServiceMock.swift; sourceTree = "<group>"; };
9F416427266575DC00106194 /* BackupServiceType.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BackupServiceType.swift; sourceTree = "<group>"; };
9F41FA27253B75F4003B970D /* BackupSelectKeyViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BackupSelectKeyViewController.swift; sourceTree = "<group>"; };
9F41FA2E253B7624003B970D /* BackupSelectKeyDecorator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BackupSelectKeyDecorator.swift; sourceTree = "<group>"; };
Expand Down Expand Up @@ -1312,7 +1310,6 @@
9F6F3C6326ADFBDB005BD9C6 /* Mocks */ = {
isa = PBXGroup;
children = (
9F4163EC266574CB00106194 /* BackupServiceMock.swift */,
9F6F3C6926ADFBEB005BD9C6 /* MessageGatewayMock.swift */,
9F6F3C3B26ADFBC7005BD9C6 /* CoreComposeMessageMock.swift */,
9F6F3C7526ADFC37005BD9C6 /* KeyStorageMock.swift */,
Expand Down Expand Up @@ -2425,7 +2422,6 @@
9F976490267E11880058419D /* ImapHelperTest.swift in Sources */,
9F5F501D26F90AE100294FA2 /* OrganisationalRulesServiceMock.swift in Sources */,
9F5F503C26FA6C5E00294FA2 /* CurrentUserEmailMock.swift in Sources */,
9FC4116526811861004C0A69 /* BackupServiceMock.swift in Sources */,
9FC413182683C492004C0A69 /* InMemoryPassPhraseStorageTest.swift in Sources */,
9F9764C5267E14AB0058419D /* GeneralConstantsTest.swift in Sources */,
9F976507267E165D0058419D /* ZBase32EncodingTests.swift in Sources */,
Expand Down
36 changes: 20 additions & 16 deletions FlowCrypt/Controllers/Compose/ComposeViewController.swift
Original file line number Diff line number Diff line change
Expand Up @@ -223,26 +223,30 @@ extension ComposeViewController {
showSpinner("sending_title".localized)
navigationItem.rightBarButtonItem?.isEnabled = false

composeMessageService.validateMessage(
let result = composeMessageService.validateMessage(
input: input,
contextToSend: contextToSend,
email: email
)
.publisher
.flatMap(encryptAndSend)
.receive(on: DispatchQueue.main)
.sinkFuture(
receiveValue: { [weak self] in
self?.handleSuccessfullySentMessage()
},
receiveError: { [weak self] error in
self?.handle(error: error)
})
.store(in: &cancellable)
}

private func encryptAndSend(_ message: SendableMsg) -> AnyPublisher<Void, ComposeMessageError> {
composeMessageService.encryptAndSend(message: message, threadId: input.threadId)
switch result {
case .success(let message):
encryptAndSend(message)
case .failure(let error):
handle(error: error)
}
}

private func encryptAndSend(_ message: SendableMsg) {
Task {
do {
try await composeMessageService.encryptAndSend(message: message, threadId: input.threadId)
handleSuccessfullySentMessage()
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Since every view controller is marked with @MainActor we could simply call methods from task and they will be invoked on the main thread.

} catch {
if let error = error as? ComposeMessageError {
handle(error: error)
}
}
}
}

private func handle(error: ComposeMessageError) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@

import AsyncDisplayKit
import FlowCryptUI
import Combine

enum BackupOption: Int, CaseIterable, Equatable {
case email, download
Expand Down Expand Up @@ -103,14 +104,16 @@ extension BackupOptionsViewController {

private func backupToInbox() {
showSpinner()
backupService.backupToInbox(keys: backups, for: userId)
.then(on: .main) { [weak self] in
self?.hideSpinner()
self?.navigationController?.popToRootViewController(animated: true)
}
.catch(on: .main) { [weak self] error in
self?.handleCommon(error: error)

Task {
do {
try await backupService.backupToInbox(keys: backups, for: userId)
hideSpinner()
navigationController?.popToRootViewController(animated: true)
} catch {
handleCommon(error: error)
}
}
}

private func backupAsFile() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -85,14 +85,15 @@ extension BackupSelectKeyViewController {
.filter { $0.1 == true }
.map(\.0)

backupService.backupToInbox(keys: backupsToSave, for: userId)
.then(on: .main) { [weak self] in
self?.hideSpinner()
self?.navigationController?.popToRootViewController(animated: true)
}
.catch(on: .main) { [weak self] error in
self?.handleCommon(error: error)
Task {
do {
try await backupService.backupToInbox(keys: backupsToSave, for: userId)
hideSpinner()
navigationController?.popToRootViewController(animated: true)
} catch {
handleCommon(error: error)
}
}
}

private func backupAsFile() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -85,7 +85,12 @@ extension SetupGenerateKeyViewController {

let encryptedPrv = try self.core.generateKey(passphrase: passPhrase, variant: .curve25519, userIds: [userId])

try awaitPromise(self.backupService.backupToInbox(keys: [encryptedPrv.key], for: self.user))
let semaphore = DispatchSemaphore(value: 0)
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This method uses a lot of other methods and too big for the first step. So I just wait for async task.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ok. Can do in another PR.

Task {
try await self.backupService.backupToInbox(keys: [encryptedPrv.key], for: self.user)
semaphore.signal()
}
semaphore.wait()

self.keyStorage.addKeys(keyDetails: [encryptedPrv.key],
passPhrase: self.storageMethod == .persistent ? passPhrase: nil,
Expand Down
38 changes: 14 additions & 24 deletions FlowCrypt/Core/Core.swift
Original file line number Diff line number Diff line change
Expand Up @@ -151,30 +151,20 @@ final class Core: KeyDecrypter, CoreComposeMessageType {
)
}

func composeEmail(msg: SendableMsg, fmt: MsgFmt, pubKeys: [String]?) -> Future<CoreRes.ComposeEmail, Error> {
Future<CoreRes.ComposeEmail, Error> { [weak self] promise in
guard let self = self else { return }
self.queue.async {
do {
let r = try self.call("composeEmail", jsonDict: [
"text": msg.text,
"to": msg.to,
"cc": msg.cc,
"bcc": msg.bcc,
"from": msg.from,
"subject": msg.subject,
"replyToMimeMsg": msg.replyToMimeMsg,
"atts": msg.atts.map { att in ["name": att.name, "type": att.type, "base64": att.base64] },
"format": fmt.rawValue,
"pubKeys": pubKeys,
], data: nil)
// this call returned no useful json data, only bytes
promise(.success(CoreRes.ComposeEmail(mimeEncoded: r.data)))
} catch {
promise(.failure(error))
}
}
}
func composeEmail(msg: SendableMsg, fmt: MsgFmt, pubKeys: [String]?) async throws -> CoreRes.ComposeEmail {
let r = try call("composeEmail", jsonDict: [
"text": msg.text,
"to": msg.to,
"cc": msg.cc,
"bcc": msg.bcc,
"from": msg.from,
"subject": msg.subject,
"replyToMimeMsg": msg.replyToMimeMsg,
"atts": msg.atts.map { att in ["name": att.name, "type": att.type, "base64": att.base64] },
"format": fmt.rawValue,
"pubKeys": pubKeys,
], data: nil)
return CoreRes.ComposeEmail(mimeEncoded: r.data)
}

func zxcvbnStrengthBar(passPhrase: String) throws -> CoreRes.ZxcvbnStrengthBar {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,10 +11,11 @@ import Foundation
import GoogleAPIClientForREST_Gmail

extension GmailService: MessageGateway {
func sendMail(input: MessageGatewayInput) -> Future<Void, Error> {
Future { promise in
func sendMail(input: MessageGatewayInput) async throws {
try await withCheckedThrowingContinuation { (continuation: CheckedContinuation<Void, Error>) in
guard let raw = GTLREncodeBase64(input.mime) else {
return promise(.failure(GmailServiceError.messageEncode))
continuation.resume(throwing: GmailServiceError.messageEncode)
return
}

let gtlMessage = GTLRGmail_Message()
Expand All @@ -27,11 +28,12 @@ extension GmailService: MessageGateway {
uploadParameters: nil
)

self.gmailService.executeQuery(querySend) { _, _, error in
gmailService.executeQuery(querySend) { _, _, error in
if let error = error {
return promise(.failure(GmailServiceError.providerError(error)))
continuation.resume(throwing: GmailServiceError.providerError(error))
} else {
continuation.resume(returning: ())
}
promise(.success(()))
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,14 +6,14 @@ import Combine
import Foundation

extension Imap: MessageGateway {
func sendMail(input: MessageGatewayInput) -> Future<Void, Error> {
Future { [smtpSess] promise in
func sendMail(input: MessageGatewayInput) async throws {
try await withCheckedThrowingContinuation { [smtpSess] (continuation: CheckedContinuation<Void, Error>) in
smtpSess?.sendOperation(with: input.mime)
.start { error in
if let error = error {
promise(.failure(error))
continuation.resume(throwing: error)
} else {
promise(.success(()))
continuation.resume(returning: ())
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,5 +15,5 @@ struct MessageGatewayInput {
}

protocol MessageGateway {
func sendMail(input: MessageGatewayInput) -> Future<Void, Error>
func sendMail(input: MessageGatewayInput) async throws
}
Original file line number Diff line number Diff line change
Expand Up @@ -45,53 +45,34 @@ extension BackupService: BackupServiceType {
}
}

func backupToInbox(keys: [KeyDetails], for userId: UserId) -> Promise<Void> {
Promise { [weak self] resolve, reject -> Void in
guard let self = self else { throw AppErr.nilSelf }

let isFullyEncryptedKeys = keys.map(\.isFullyDecrypted).contains(false)

guard isFullyEncryptedKeys else {
throw BackupServiceError.keyIsNotFullyEncrypted
}
func backupToInbox(keys: [KeyDetails], for userId: UserId) async throws {
let isFullyEncryptedKeys = keys.map(\.isFullyDecrypted).contains(false)

let privateKeyContext = keys
.compactMap { $0 }
.joinedPrivateKey

let privateKeyData = privateKeyContext.data().base64EncodedString()

let filename = "flowcrypt-backup-\(userId.email.withoutSpecialCharacters).key"
let attachments = [SendableMsg.Attachment(name: filename, type: "text/plain", base64: privateKeyData)]
let message = SendableMsg(
text: "setup_backup_email".localized,
to: [userId.toMime],
cc: [],
bcc: [],
from: userId.toMime,
subject: "Your FlowCrypt Backup",
replyToMimeMsg: nil,
atts: attachments,
pubKeys: nil)

self.core.composeEmail(msg: message, fmt: .plain, pubKeys: message.pubKeys)
.map({ MessageGatewayInput(mime: $0.mimeEncoded, threadId: nil) })
.flatMap(self.messageSender.sendMail)
.sink(
receiveCompletion: { result in
switch result {
case .failure(let error):
reject(error)
case .finished:
break
}
},
receiveValue: {
resolve(())
}
)
.store(in: &self.cancellable)
guard isFullyEncryptedKeys else {
throw BackupServiceError.keyIsNotFullyEncrypted
}

let privateKeyContext = keys
.compactMap { $0 }
.joinedPrivateKey

let privateKeyData = privateKeyContext.data().base64EncodedString()

let filename = "flowcrypt-backup-\(userId.email.withoutSpecialCharacters).key"
let attachments = [SendableMsg.Attachment(name: filename, type: "text/plain", base64: privateKeyData)]
let message = SendableMsg(
text: "setup_backup_email".localized,
to: [userId.toMime],
cc: [],
bcc: [],
from: userId.toMime,
subject: "Your FlowCrypt Backup",
replyToMimeMsg: nil,
atts: attachments,
pubKeys: nil)

let t = try await core.composeEmail(msg: message, fmt: .plain, pubKeys: message.pubKeys)
try await messageSender.sendMail(input: MessageGatewayInput(mime: t.mimeEncoded, threadId: nil))
}

func backupAsFile(keys: [KeyDetails], for viewController: UIViewController) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ protocol BackupServiceType {
/// get all existed backups
func fetchBackupsFromInbox(for userId: UserId) -> Promise<[KeyDetails]>
/// backup keys to user inbox
func backupToInbox(keys: [KeyDetails], for userId: UserId) -> Promise<Void>
func backupToInbox(keys: [KeyDetails], for userId: UserId) async throws
/// show activity sheet to save keys as file
func backupAsFile(keys: [KeyDetails], for viewController: UIViewController)
}
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ struct ComposeMessageRecipient {
}

protocol CoreComposeMessageType {
func composeEmail(msg: SendableMsg, fmt: MsgFmt, pubKeys: [String]?) -> Future<CoreRes.ComposeEmail, Error>
func composeEmail(msg: SendableMsg, fmt: MsgFmt, pubKeys: [String]?) async throws -> CoreRes.ComposeEmail
}

final class ComposeMessageService {
Expand Down Expand Up @@ -126,20 +126,17 @@ final class ComposeMessageService {
}

// MARK: - Encrypt and Send
func encryptAndSend(message: SendableMsg, threadId: String?) -> AnyPublisher<Void, ComposeMessageError> {
return encryptMessage(with: message, threadId: threadId)
.flatMap(messageGateway.sendMail)
.mapError { ComposeMessageError.gatewayError($0) }
.eraseToAnyPublisher()
}

private func encryptMessage(with msg: SendableMsg, threadId: String?) -> AnyPublisher<MessageGatewayInput, Error> {
return core.composeEmail(
msg: msg,
fmt: MsgFmt.encryptInline,
pubKeys: msg.pubKeys
)
.map({ MessageGatewayInput(mime: $0.mimeEncoded, threadId: threadId) })
.eraseToAnyPublisher()
func encryptAndSend(message: SendableMsg, threadId: String?) async throws {
do {
let r = try await core.composeEmail(
msg: message,
fmt: MsgFmt.encryptInline,
pubKeys: message.pubKeys
)

try await messageGateway.sendMail(input: MessageGatewayInput(mime: r.mimeEncoded, threadId: threadId))
} catch {
throw ComposeMessageError.gatewayError(error)
}
}
}
Loading