From b1250380e6e5f146097a16a7244bb7e0daf3172d Mon Sep 17 00:00:00 2001
From: Bruno Muniz Azevedo Filho <bruno@elixxir.io>
Date: Wed, 13 Jul 2022 18:47:16 -0300
Subject: [PATCH] Fixing sftp download

---
 Sources/InputField/OutlinedInputField.swift   |   1 +
 Sources/LaunchFeature/LaunchViewModel.swift   |   4 +
 .../ViewModels/RestoreViewModel.swift         |  16 ++-
 Sources/SFTPFeature/SFTPService.swift         | 121 +++++++++++++-----
 4 files changed, 103 insertions(+), 39 deletions(-)

diff --git a/Sources/InputField/OutlinedInputField.swift b/Sources/InputField/OutlinedInputField.swift
index 01e1511d..3bb8ad30 100644
--- a/Sources/InputField/OutlinedInputField.swift
+++ b/Sources/InputField/OutlinedInputField.swift
@@ -24,6 +24,7 @@ public final class OutlinedInputField: UIView {
 
         textField.delegate = self
         textField.backgroundColor = .clear
+        textField.textColor = Asset.neutralDark.color
         placeholderLabel.textColor = Asset.neutralWeak.color
         placeholderLabel.font = Fonts.Mulish.regular.font(size: 16.0)
 
diff --git a/Sources/LaunchFeature/LaunchViewModel.swift b/Sources/LaunchFeature/LaunchViewModel.swift
index 054432b4..4e1b25ea 100644
--- a/Sources/LaunchFeature/LaunchViewModel.swift
+++ b/Sources/LaunchFeature/LaunchViewModel.swift
@@ -5,6 +5,7 @@ import Models
 import Combine
 import Defaults
 import XXModels
+import Keychain
 import Foundation
 import Integration
 import Permissions
@@ -31,6 +32,7 @@ final class LaunchViewModel {
     @Dependency private var network: XXNetworking
     @Dependency private var versionChecker: VersionChecker
     @Dependency private var dropboxService: DropboxInterface
+    @Dependency private var keychainHandler: KeychainHandling
     @Dependency private var permissionHandler: PermissionHandling
 
     @KeyObject(.username, defaultValue: nil) var username: String?
@@ -90,6 +92,7 @@ final class LaunchViewModel {
                     self.hudSubject.send(.none)
                     self.routeSubject.send(.onboarding(ndf))
                     self.dropboxService.unlink()
+                    try? self.keychainHandler.clear()
                     return
                 }
 
@@ -98,6 +101,7 @@ final class LaunchViewModel {
                     self.hudSubject.send(.none)
                     self.routeSubject.send(.onboarding(ndf))
                     self.dropboxService.unlink()
+                    try? self.keychainHandler.clear()
                     return
                 }
 
diff --git a/Sources/RestoreFeature/ViewModels/RestoreViewModel.swift b/Sources/RestoreFeature/ViewModels/RestoreViewModel.swift
index 5f1d4414..09147154 100644
--- a/Sources/RestoreFeature/ViewModels/RestoreViewModel.swift
+++ b/Sources/RestoreFeature/ViewModels/RestoreViewModel.swift
@@ -90,11 +90,17 @@ final class RestoreViewModel {
     }
 
     private func downloadBackupForSFTP(_ backup: Backup) {
-        do {
-            try sftpService.downloadBackup(backup.id)
-        } catch {
-            print(error.localizedDescription)
-        }
+        sftpService.downloadBackup(backup.id, { [weak self] in
+            guard let self = self else { return }
+            self.stepRelay.send(.downloading(backup.size, backup.size))
+
+            switch $0 {
+            case .success(let data):
+                self.continueRestoring(data: data)
+            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 aca3e200..b751f396 100644
--- a/Sources/SFTPFeature/SFTPService.swift
+++ b/Sources/SFTPFeature/SFTPService.swift
@@ -1,5 +1,6 @@
 import UIKit
 import Shout
+import Socket
 import Models
 import Combine
 import Keychain
@@ -7,16 +8,17 @@ import Foundation
 import Presentation
 import DependencyInjection
 
-public typealias SFTPFetchResult = (Result<RestoreSettings?, Error>) -> Void
+public typealias SFTPDownloadResult = (Result<Data, Error>) -> Void
 public typealias SFTPAuthorizationParams = (UIViewController, () -> Void)
+public typealias SFTPFetchResult = (Result<RestoreSettings?, Error>) -> Void
 
 public struct SFTPService {
     public var isAuthorized: () -> Bool
     public var uploadBackup: (URL) throws -> Void
-    public var downloadBackup: (String) throws -> Void
     public var fetchMetadata: (SFTPFetchResult) -> Void
-    public var authenticate: (String, String, String) throws -> Void
     public var authorizeFlow: (SFTPAuthorizationParams) -> Void
+    public var authenticate: (String, String, String) throws -> Void
+    public var downloadBackup: (String, SFTPDownloadResult) -> Void
 }
 
 public extension SFTPService {
@@ -29,23 +31,23 @@ public extension SFTPService {
             print("^^^ Requested upload on sftp service")
             print("^^^ URL path: \(url.path)")
         },
-        downloadBackup: { path in
-            print("^^^ Requested backup download on sftp service.")
-            print("^^^ Path: \(path)")
-        },
         fetchMetadata: { completion in
             print("^^^ Requested backup metadata on sftp service.")
             completion(.success(nil))
         },
+        authorizeFlow: { (_, completion) in
+            print("^^^ Requested authorizing flow on sftp service.")
+            completion()
+        },
         authenticate: { host, username, password in
             print("^^^ Requested authentication on sftp service.")
             print("^^^ Host: \(host)")
             print("^^^ Username: \(username)")
             print("^^^ Password: \(password)")
         },
-        authorizeFlow: { (_, completion) in
-            print("^^^ Requested authorizing flow on sftp service.")
-            completion()
+        downloadBackup: { path, completion in
+            print("^^^ Requested backup download on sftp service.")
+            print("^^^ Path: \(path)")
         }
     )
 
@@ -72,20 +74,6 @@ public extension SFTPService {
 
             try sftp.upload(localURL: url, remotePath: "backup/backup.xxm")
         },
-        downloadBackup: { path in
-            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 temp = NSTemporaryDirectory()
-            try sftp.download(remotePath: path, localURL: URL(string: temp)!)
-            print(FileManager.default.fileExists(atPath: temp))
-        },
         fetchMetadata: { completion in
             do {
                 let keychain = try DependencyInjection.Container.shared.resolve() as KeychainHandling
@@ -113,22 +101,87 @@ public extension SFTPService {
 
                 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))
             }
         },
-        authenticate: { host, username, password in
-            let ssh = try SSH(host: host, port: 22)
-            try ssh.authenticate(username: username, password: password)
-            let sftp = try ssh.openSftp()
-
-            let keychain = try DependencyInjection.Container.shared.resolve() as KeychainHandling
-            try keychain.store(key: .host, value: host)
-            try keychain.store(key: .pwd, value: password)
-            try keychain.store(key: .username, value: username)
-        },
         authorizeFlow: { controller, completion in
             var pushPresenter: Presenting = PushPresenter()
             pushPresenter.present(SFTPController(completion), from: controller)
+        },
+        authenticate: { host, username, password in
+            do {
+                try SSH.connect(
+                    host: host,
+                    port: 22,
+                    username: username,
+                    authMethod: SSHPassword(password)) { ssh in
+                        _ = try ssh.openSftp()
+
+                        let keychain = try DependencyInjection.Container.shared.resolve() as KeychainHandling
+                        try keychain.store(key: .host, value: host)
+                        try keychain.store(key: .pwd, value: password)
+                        try keychain.store(key: .username, value: username)
+                    }
+            } 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)
+                }
+
+                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)
+                }
+            }
         }
     )
 }
-- 
GitLab