From 9f3daf5b2ff5c7c879af32ac2fbba8fef31b793b Mon Sep 17 00:00:00 2001 From: Bruno Muniz Azevedo Filho <bruno@elixxir.io> Date: Fri, 15 Jul 2022 02:02:36 -0300 Subject: [PATCH] Fixed issue w/ sftp by removing throttle --- .../BackupFeature/Service/BackupService.swift | 6 +- Sources/Integration/Client.swift | 7 +- Sources/Integration/Session/Session.swift | 11 +- .../ViewModels/RestoreViewModel.swift | 4 +- Sources/SFTPFeature/SFTPService.swift | 166 +++++------------- .../SFTPServiceBackupDownloader.swift | 54 ++++++ .../SFTPServiceBackupUploader.swift | 66 +++++++ 7 files changed, 182 insertions(+), 132 deletions(-) create mode 100644 Sources/SFTPFeature/SFTPServiceBackupDownloader.swift create mode 100644 Sources/SFTPFeature/SFTPServiceBackupUploader.swift diff --git a/Sources/BackupFeature/Service/BackupService.swift b/Sources/BackupFeature/Service/BackupService.swift index ed3b5b53..5e97919c 100644 --- a/Sources/BackupFeature/Service/BackupService.swift +++ b/Sources/BackupFeature/Service/BackupService.swift @@ -277,7 +277,7 @@ extension BackupService { .appendingPathComponent(UUID().uuidString) do { - try data.write(to: url) + try data.write(to: url, options: .atomic) } catch { print("Couldn't write to temp: \(error.localizedDescription)") return @@ -324,14 +324,14 @@ extension BackupService { } } case .sftp: - sftpService.uploadBackup(url, { + sftpService.uploadBackup(url: url) { switch $0 { case .success(let backup): self.settings.value.backups[.sftp] = backup case .failure(let error): print(error.localizedDescription) } - }) + } } } } diff --git a/Sources/Integration/Client.swift b/Sources/Integration/Client.swift index 7d53fb7e..2b488ef9 100644 --- a/Sources/Integration/Client.swift +++ b/Sources/Integration/Client.swift @@ -89,7 +89,12 @@ public class Client { // } public func addJson(_ string: String) { - guard let backupManager = backupManager else { return } + guard let backupManager = backupManager else { + fatalError() + } + + print("^^^ Set params: \(string) to backup") + backupManager.addJson(string) } diff --git a/Sources/Integration/Session/Session.swift b/Sources/Integration/Session/Session.swift index 8a1c2040..7c428f12 100644 --- a/Sources/Integration/Session/Session.swift +++ b/Sources/Integration/Session/Session.swift @@ -303,6 +303,11 @@ public final class Session: SessionType { ).jsonFormat client.addJson(params) + + guard username!.isEmpty == false else { + fatalError("Tried to build a backup with my username but an empty string was set to it") + } + backupService.performBackupIfAutomaticIsEnabled() } @@ -328,7 +333,6 @@ public final class Session: SessionType { .store(in: &cancellables) client.backup - .throttle(for: .seconds(5), scheduler: DispatchQueue.main, latest: true) .sink { [unowned self] in backupService.updateBackup(data: $0) } .store(in: &cancellables) @@ -349,7 +353,6 @@ public final class Session: SessionType { if $0 == true { guard let passphrase = backupService.passphrase else { client.resumeBackup() - updateFactsOnBackup() return } @@ -363,10 +366,6 @@ public final class Session: SessionType { } .store(in: &cancellables) - networkMonitor.statusPublisher - .sink { print($0) } - .store(in: &cancellables) - client.messages .sink { [unowned self] in if var contact = try? dbManager.fetchContacts(.init(id: [$0.senderId])).first { diff --git a/Sources/RestoreFeature/ViewModels/RestoreViewModel.swift b/Sources/RestoreFeature/ViewModels/RestoreViewModel.swift index 09147154..bea37e51 100644 --- a/Sources/RestoreFeature/ViewModels/RestoreViewModel.swift +++ b/Sources/RestoreFeature/ViewModels/RestoreViewModel.swift @@ -90,7 +90,7 @@ final class RestoreViewModel { } private func downloadBackupForSFTP(_ backup: Backup) { - sftpService.downloadBackup(backup.id, { [weak self] in + sftpService.downloadBackup(path: backup.id) { [weak self] in guard let self = self else { return } self.stepRelay.send(.downloading(backup.size, backup.size)) @@ -100,7 +100,7 @@ final class RestoreViewModel { case .failure(let error): self.stepRelay.send(.failDownload(error)) } - }) + } } private func downloadBackupForDropbox(_ backup: Backup) { diff --git a/Sources/SFTPFeature/SFTPService.swift b/Sources/SFTPFeature/SFTPService.swift index b040668b..25d882d9 100644 --- a/Sources/SFTPFeature/SFTPService.swift +++ b/Sources/SFTPFeature/SFTPService.swift @@ -15,11 +15,11 @@ public typealias SFTPAuthorizationParams = (UIViewController, () -> Void) public struct SFTPService { public var isAuthorized: () -> Bool - public var fetchMetadata: (SFTPFetchResult) -> Void - public var uploadBackup: (URL, SFTPUploadResult) -> Void + public var fetchMetadata: (@escaping SFTPFetchResult) -> Void + public var uploadBackup: SFTPServiceBackupUploader public var authorizeFlow: (SFTPAuthorizationParams) -> Void public var authenticate: (String, String, String) throws -> Void - public var downloadBackup: (String, SFTPDownloadResult) -> Void + public var downloadBackup: SFTPServiceBackupDownloader } public extension SFTPService { @@ -32,10 +32,7 @@ public extension SFTPService { print("^^^ Requested backup metadata on sftp service.") completion(.success(nil)) }, - uploadBackup: { url, completion in - print("^^^ Requested upload on sftp service") - print("^^^ URL path: \(url.path)") - }, + uploadBackup: .mock, authorizeFlow: { (_, completion) in print("^^^ Requested authorizing flow on sftp service.") completion() @@ -46,10 +43,7 @@ public extension SFTPService { print("^^^ Username: \(username)") print("^^^ Password: \(password)") }, - downloadBackup: { path, completion in - print("^^^ Requested backup download on sftp service.") - print("^^^ Path: \(path)") - } + downloadBackup: .mock ) static var live = SFTPService( @@ -64,89 +58,51 @@ public extension SFTPService { return false }, fetchMetadata: { completion in - do { - let keychain = try DependencyInjection.Container.shared.resolve() as KeychainHandling - let host = try keychain.get(key: .host) - let password = try keychain.get(key: .pwd) - let username = try keychain.get(key: .username) - - let ssh = try SSH(host: host!, port: 22) - try ssh.authenticate(username: username!, password: password!) - let sftp = try ssh.openSftp() - - if let files = try? sftp.listFiles(in: "backup"), - let backup = files.filter({ file in file.0 == "backup.xxm" }).first { - completion(.success(.init( - backup: .init( - id: "backup/backup.xxm", - date: backup.value.lastModified, - size: Float(backup.value.size) - ), - cloudService: .sftp - ))) - - return - } - - completion(.success(nil)) - } catch { - if let error = error as? SSHError { - print(error.kind) - print(error.message) - print(error.description) - } else if let error = error as? Socket.Error { - print(error.errorCode) - print(error.description) - print(error.errorReason) - print(error.localizedDescription) - } else { - print(error.localizedDescription) - } - - completion(.failure(error)) - } - }, - uploadBackup: { url, completion in - do { - let keychain = try DependencyInjection.Container.shared.resolve() as KeychainHandling - let host = try keychain.get(key: .host) - let password = try keychain.get(key: .pwd) - let username = try keychain.get(key: .username) - - let ssh = try SSH(host: host!, port: 22) - try ssh.authenticate(username: username!, password: password!) - let sftp = try ssh.openSftp() - - let data = try Data(contentsOf: url) - - if (try? sftp.listFiles(in: "backup")) == nil { - try sftp.createDirectory("backup") - } + DispatchQueue.global().async { + do { + let keychain = try DependencyInjection.Container.shared.resolve() as KeychainHandling + let host = try keychain.get(key: .host) + let password = try keychain.get(key: .pwd) + let username = try keychain.get(key: .username) + + let ssh = try SSH(host: host!, port: 22) + try ssh.authenticate(username: username!, password: password!) + let sftp = try ssh.openSftp() + + if let files = try? sftp.listFiles(in: "backup"), + let backup = files.filter({ file in file.0 == "backup.xxm" }).first { + completion(.success(.init( + backup: .init( + id: "backup/backup.xxm", + date: backup.value.lastModified, + size: Float(backup.value.size) + ), + cloudService: .sftp + ))) + + return + } - try sftp.upload(data: data, remotePath: "backup/backup.xxm") + completion(.success(nil)) + } catch { + if let error = error as? SSHError { + print(error.kind) + print(error.message) + print(error.description) + } else if let error = error as? Socket.Error { + print(error.errorCode) + print(error.description) + print(error.errorReason) + print(error.localizedDescription) + } else { + print(error.localizedDescription) + } - completion(.success(.init( - id: "backup/backup.xxm", - date: Date(), - size: Float(data.count) - ))) - } catch { - if let error = error as? SSHError { - print(error.kind) - print(error.message) - print(error.description) - } else if let error = error as? Socket.Error { - print(error.errorCode) - print(error.description) - print(error.errorReason) - print(error.localizedDescription) - } else { - print(error.localizedDescription) + completion(.failure(error)) } - - completion(.failure(error)) } }, + uploadBackup: .live , authorizeFlow: { controller, completion in var pushPresenter: Presenting = PushPresenter() pushPresenter.present(SFTPController(completion), from: controller) @@ -182,36 +138,6 @@ public extension SFTPService { throw error } }, - downloadBackup: { path, completion in - do { - let keychain = try DependencyInjection.Container.shared.resolve() as KeychainHandling - let host = try keychain.get(key: .host) - let password = try keychain.get(key: .pwd) - let username = try keychain.get(key: .username) - - let ssh = try SSH(host: host!, port: 22) - try ssh.authenticate(username: username!, password: password!) - let sftp = try ssh.openSftp() - - let localURL = FileManager.default - .containerURL(forSecurityApplicationGroupIdentifier: "group.elixxir.messenger")! - .appendingPathComponent("sftp") - - try sftp.download(remotePath: path, localURL: localURL) - - let data = try Data(contentsOf: localURL) - completion(.success(data)) - } catch { - completion(.failure(error)) - - if var error = error as? SSHError { - print(error.kind) - print(error.message) - print(error.description) - } else { - print(error.localizedDescription) - } - } - } + downloadBackup: .live ) } diff --git a/Sources/SFTPFeature/SFTPServiceBackupDownloader.swift b/Sources/SFTPFeature/SFTPServiceBackupDownloader.swift new file mode 100644 index 00000000..4c63ec26 --- /dev/null +++ b/Sources/SFTPFeature/SFTPServiceBackupDownloader.swift @@ -0,0 +1,54 @@ +import Shout +import Socket +import Keychain +import Foundation +import DependencyInjection + +public struct SFTPServiceBackupDownloader { + public var download: (String, @escaping SFTPDownloadResult) -> Void + + public func callAsFunction(path: String, completion: @escaping SFTPDownloadResult) { + download(path, completion) + } +} + +extension SFTPServiceBackupDownloader { + static let mock = SFTPServiceBackupDownloader { path, _ in + print("^^^ Requested backup download on sftp service.") + print("^^^ Path: \(path)") + } + + static let live = SFTPServiceBackupDownloader { path, completion in + DispatchQueue.global().async { + do { + let keychain = try DependencyInjection.Container.shared.resolve() as KeychainHandling + let host = try keychain.get(key: .host) + let password = try keychain.get(key: .pwd) + let username = try keychain.get(key: .username) + + let ssh = try SSH(host: host!, port: 22) + try ssh.authenticate(username: username!, password: password!) + let sftp = try ssh.openSftp() + + let localURL = FileManager.default + .containerURL(forSecurityApplicationGroupIdentifier: "group.elixxir.messenger")! + .appendingPathComponent("sftp") + + try sftp.download(remotePath: path, localURL: localURL) + + let data = try Data(contentsOf: localURL) + completion(.success(data)) + } catch { + completion(.failure(error)) + + if var error = error as? SSHError { + print(error.kind) + print(error.message) + print(error.description) + } else { + print(error.localizedDescription) + } + } + } + } +} diff --git a/Sources/SFTPFeature/SFTPServiceBackupUploader.swift b/Sources/SFTPFeature/SFTPServiceBackupUploader.swift new file mode 100644 index 00000000..2c6ecece --- /dev/null +++ b/Sources/SFTPFeature/SFTPServiceBackupUploader.swift @@ -0,0 +1,66 @@ +import Shout +import Socket +import Keychain +import Foundation +import DependencyInjection + +public struct SFTPServiceBackupUploader { + public var upload: (URL, @escaping SFTPUploadResult) -> Void + + public func callAsFunction(url: URL, completion: @escaping SFTPUploadResult) { + upload(url, completion) + } +} + +extension SFTPServiceBackupUploader { + static let mock = SFTPServiceBackupUploader( + upload: { url, _ in + print("^^^ Requested upload on sftp service") + print("^^^ URL path: \(url.path)") + } + ) + + static let live = SFTPServiceBackupUploader { url, completion in + DispatchQueue.global().async { + do { + let keychain = try DependencyInjection.Container.shared.resolve() as KeychainHandling + let host = try keychain.get(key: .host) + let password = try keychain.get(key: .pwd) + let username = try keychain.get(key: .username) + + let ssh = try SSH(host: host!, port: 22) + try ssh.authenticate(username: username!, password: password!) + let sftp = try ssh.openSftp() + + let data = try Data(contentsOf: url) + + if (try? sftp.listFiles(in: "backup")) == nil { + try sftp.createDirectory("backup") + } + + try sftp.upload(data: data, remotePath: "backup/backup.xxm") + + completion(.success(.init( + id: "backup/backup.xxm", + date: Date(), + size: Float(data.count) + ))) + } catch { + if let error = error as? SSHError { + print(error.kind) + print(error.message) + print(error.description) + } else if let error = error as? Socket.Error { + print(error.errorCode) + print(error.description) + print(error.errorReason) + print(error.localizedDescription) + } else { + print(error.localizedDescription) + } + + completion(.failure(error)) + } + } + } +} -- GitLab