diff --git a/pass/Controllers/GitServerSettingTableViewController.swift b/pass/Controllers/GitServerSettingTableViewController.swift index 66eade5..77c0d71 100644 --- a/pass/Controllers/GitServerSettingTableViewController.swift +++ b/pass/Controllers/GitServerSettingTableViewController.swift @@ -24,7 +24,7 @@ class GitServerSettingTableViewController: UITableViewController { // MARK: - Properties - private var sshLabel: UILabel? = nil + private var sshLabel: UILabel? private let passwordStore = PasswordStore.shared private var gitAuthenticationMethod: GitAuthenticationMethod { get { SharedDefaults[.gitAuthenticationMethod] } @@ -57,7 +57,6 @@ class GitServerSettingTableViewController: UITableViewController { } } - // MARK: - View Controller Lifecycle override func viewDidLoad() { @@ -151,13 +150,12 @@ class GitServerSettingTableViewController: UITableViewController { alert.addAction(UIAlertAction(title: "Cancel".localize(), style: .cancel, handler: nil)) return alert }() - self.present(overwriteAlert, animated: true, completion: nil) + self.present(overwriteAlert, animated: true) } else { cloneAndSegueIfSuccess() } } - private func cloneAndSegueIfSuccess() { // Remember git credential password/passphrase temporarily, ask whether users want this after a successful clone. SharedDefaults[.isRememberGitCredentialPassphraseOn] = true @@ -169,7 +167,7 @@ class GitServerSettingTableViewController: UITableViewController { SVProgressHUD.showProgress(progress, status: "Cloning Remote Repository") } - let checkoutProgressBlock: (String?, UInt, UInt) -> Void = { (_, completedSteps, totalSteps) in + let checkoutProgressBlock: (String, UInt, UInt) -> Void = { (_, completedSteps, totalSteps) in let progress = Float(completedSteps) / Float(totalSteps) SVProgressHUD.showProgress(progress, status: "CheckingOutBranch".localize(self.gitBranchName)) } @@ -177,7 +175,7 @@ class GitServerSettingTableViewController: UITableViewController { try self.passwordStore.cloneRepository(remoteRepoURL: self.gitUrl, credential: self.gitCredential, branchName: self.gitBranchName, - requestGitPassword: self.requestGitPassword, + requestCredentialPassword: self.requestCredentialPassword, transferProgressBlock: transferProgressBlock, checkoutProgressBlock: checkoutProgressBlock) @@ -196,21 +194,25 @@ class GitServerSettingTableViewController: UITableViewController { }) return alert }() - self.present(savePassphraseAlert, animated: true, completion: nil) + DispatchQueue.main.async { + self.present(savePassphraseAlert, animated: true) + } } } catch { - SVProgressHUD.dismiss() - let error = error as NSError - var message = error.localizedDescription - if let underlyingError = error.userInfo[NSUnderlyingErrorKey] as? NSError { - message = "\(message)\n\("UnderlyingError".localize(underlyingError.localizedDescription))" + SVProgressHUD.dismiss() { + let error = error as NSError + var message = error.localizedDescription + if let underlyingError = error.userInfo[NSUnderlyingErrorKey] as? NSError { + message = "\(message)\n\("UnderlyingError".localize(underlyingError.localizedDescription))" + } + DispatchQueue.main.async { + Utils.alert(title: "Error".localize(), message: message, controller: self) + } } - Utils.alert(title: "Error".localize(), message: message, controller: self) } } } - // MARK: - Helper Functions private func showSSHKeyActionSheet() { @@ -280,41 +282,11 @@ class GitServerSettingTableViewController: UITableViewController { optionMenu.popoverPresentationController?.sourceView = authSSHKeyCell optionMenu.popoverPresentationController?.sourceRect = authSSHKeyCell.bounds - self.present(optionMenu, animated: true, completion: nil) + self.present(optionMenu, animated: true) } - private func requestGitPassword(credential: GitCredential.Credential, lastPassword: String?) -> String? { - let sem = DispatchSemaphore(value: 0) - var password: String? - let message: String = { - switch credential { - case .http: - return "FillInGitAccountPassword.".localize() - case .ssh: - return "FillInSshKeyPassphrase.".localize() - } - }() - - DispatchQueue.main.async { - SVProgressHUD.dismiss() - let alert = UIAlertController(title: "Password".localize(), message: message, preferredStyle: .alert) - alert.addTextField(configurationHandler: {(textField: UITextField!) in - textField.text = lastPassword ?? "" - textField.isSecureTextEntry = true - }) - alert.addAction(UIAlertAction(title: "Ok".localize(), style: .default, handler: {_ in - password = alert.textFields!.first!.text - sem.signal() - })) - alert.addAction(UIAlertAction(title: "Cancel".localize(), style: .cancel) { _ in - password = nil - sem.signal() - }) - self.present(alert, animated: true, completion: nil) - } - - let _ = sem.wait(timeout: .distantFuture) - return password + private func requestCredentialPassword(credential: GitCredential.Credential, lastPassword: String?) -> String? { + return passKit.requestCredentialPassword(credential: credential, lastPassword: lastPassword, controller: self) } private func updateAuthenticationMethodCheckView(for method: GitAuthenticationMethod) { diff --git a/pass/Controllers/PasswordsViewController.swift b/pass/Controllers/PasswordsViewController.swift index 4263e43..06f09f6 100644 --- a/pass/Controllers/PasswordsViewController.swift +++ b/pass/Controllers/PasswordsViewController.swift @@ -189,13 +189,13 @@ class PasswordsViewController: UIViewController, UITableViewDataSource, UITableV DispatchQueue.global(qos: .userInitiated).async { [unowned self] in do { - try self.passwordStore.pullRepository(credential: self.gitCredential, requestGitPassword: self.requestGitPassword(credential:lastPassword:), transferProgressBlock: {(git_transfer_progress, stop) in + try self.passwordStore.pullRepository(credential: self.gitCredential, requestCredentialPassword: self.requestCredentialPassword, progressBlock: {(git_transfer_progress, stop) in DispatchQueue.main.async { SVProgressHUD.showProgress(Float(git_transfer_progress.pointee.received_objects)/Float(git_transfer_progress.pointee.total_objects), status: "PullingFromRemoteRepository".localize()) } }) if self.passwordStore.numberOfLocalCommits > 0 { - try self.passwordStore.pushRepository(credential: self.gitCredential, requestGitPassword: self.requestGitPassword(credential:lastPassword:), transferProgressBlock: {(current, total, bytes, stop) in + try self.passwordStore.pushRepository(credential: self.gitCredential, requestCredentialPassword: self.requestCredentialPassword, transferProgressBlock: {(current, total, bytes, stop) in DispatchQueue.main.async { SVProgressHUD.showProgress(Float(current)/Float(total), status: "PushingToRemoteRepository".localize()) } @@ -703,37 +703,8 @@ class PasswordsViewController: UIViewController, UITableViewDataSource, UITableV return true } - private func requestGitPassword(credential: GitCredential.Credential, lastPassword: String?) -> String? { - let sem = DispatchSemaphore(value: 0) - var password: String? - var message = "" - switch credential { - case .http: - message = "FillInGitAccountPassword.".localize() - case .ssh: - message = "FillInSshKeyPassphrase.".localize() - } - - DispatchQueue.main.async { - SVProgressHUD.dismiss() - let alert = UIAlertController(title: "Password".localize(), message: message, preferredStyle: UIAlertController.Style.alert) - alert.addTextField(configurationHandler: {(textField: UITextField!) in - textField.text = lastPassword ?? "" - textField.isSecureTextEntry = true - }) - alert.addAction(UIAlertAction(title: "Ok".localize(), style: UIAlertAction.Style.default, handler: {_ in - password = alert.textFields!.first!.text - sem.signal() - })) - alert.addAction(UIAlertAction(title: "Cancel".localize(), style: .cancel) { _ in - password = nil - sem.signal() - }) - self.present(alert, animated: true, completion: nil) - } - - let _ = sem.wait(timeout: .distantFuture) - return password + private func requestCredentialPassword(credential: GitCredential.Credential, lastPassword: String?) -> String? { + return passKit.requestCredentialPassword(credential: credential, lastPassword: lastPassword, controller: self) } } diff --git a/passKit/Models/GitCredential.swift b/passKit/Models/GitCredential.swift index 1bff249..e16a67b 100644 --- a/passKit/Models/GitCredential.swift +++ b/passKit/Models/GitCredential.swift @@ -8,6 +8,43 @@ import Foundation import ObjectiveGit +import SVProgressHUD + +public func requestCredentialPassword(credential: GitCredential.Credential, + lastPassword: String?, + controller: UIViewController) -> String? { + let sem = DispatchSemaphore(value: 0) + var password: String? + let message: String = { + switch credential { + case .http: + return "FillInGitAccountPassword.".localize() + case .ssh: + return "FillInSshKeyPassphrase.".localize() + } + }() + + DispatchQueue.main.async { + SVProgressHUD.dismiss() + let alert = UIAlertController(title: "Password".localize(), message: message, preferredStyle: .alert) + alert.addTextField() { + $0.text = lastPassword ?? "" + $0.isSecureTextEntry = true + } + alert.addAction(UIAlertAction(title: "Ok".localize(), style: .default) { _ in + password = alert.textFields?.first?.text + sem.signal() + }) + alert.addAction(UIAlertAction(title: "Cancel".localize(), style: .cancel) { _ in + password = nil + sem.signal() + }) + controller.present(alert, animated: true) + } + + let _ = sem.wait(timeout: .distantFuture) + return password +} public struct GitCredential { private var credential: Credential @@ -22,7 +59,7 @@ public struct GitCredential { self.credential = credential } - public func credentialProvider(requestGitPassword: @escaping (Credential, String?) -> String?) throws -> GTCredentialProvider { + public func credentialProvider(requestCredentialPassword: @escaping (Credential, String?) -> String?) throws -> GTCredentialProvider { var attempts = 0 return GTCredentialProvider { (_, _, _) -> (GTCredential?) in var credential: GTCredential? = nil @@ -35,7 +72,7 @@ public struct GitCredential { } var lastPassword = self.passwordStore.gitPassword if lastPassword == nil || attempts != 0 { - if let requestedPassword = requestGitPassword(self.credential, lastPassword) { + if let requestedPassword = requestCredentialPassword(self.credential, lastPassword) { if SharedDefaults[.isRememberGitCredentialPassphraseOn] { self.passwordStore.gitPassword = requestedPassword } @@ -53,7 +90,7 @@ public struct GitCredential { } var lastPassword = self.passwordStore.gitSSHPrivateKeyPassphrase if lastPassword == nil || attempts != 0 { - if let requestedPassword = requestGitPassword(self.credential, lastPassword) { + if let requestedPassword = requestCredentialPassword(self.credential, lastPassword) { if SharedDefaults[.isRememberGitCredentialPassphraseOn] { self.passwordStore.gitSSHPrivateKeyPassphrase = requestedPassword } diff --git a/passKit/Models/PasswordStore.swift b/passKit/Models/PasswordStore.swift index 46324c4..2cb4b23 100644 --- a/passKit/Models/PasswordStore.swift +++ b/passKit/Models/PasswordStore.swift @@ -176,68 +176,71 @@ public class PasswordStore { public func cloneRepository(remoteRepoURL: URL, credential: GitCredential, branchName: String, - requestGitPassword: @escaping (GitCredential.Credential, String?) -> String?, + requestCredentialPassword: @escaping (GitCredential.Credential, String?) -> String?, transferProgressBlock: @escaping (UnsafePointer, UnsafeMutablePointer) -> Void, - checkoutProgressBlock: @escaping (String?, UInt, UInt) -> Void) throws { + checkoutProgressBlock: @escaping (String, UInt, UInt) -> Void) throws { try? fm.removeItem(at: storeURL) try? fm.removeItem(at: tempStoreURL) self.gitPassword = nil self.gitSSHPrivateKeyPassphrase = nil do { - let credentialProvider = try credential.credentialProvider(requestGitPassword: requestGitPassword) + let credentialProvider = try credential.credentialProvider(requestCredentialPassword: requestCredentialPassword) let options = [GTRepositoryCloneOptionsCredentialProvider: credentialProvider] - storeRepository = try GTRepository.clone(from: remoteRepoURL, toWorkingDirectory: tempStoreURL, options: options, transferProgressBlock:transferProgressBlock) + storeRepository = try GTRepository.clone(from: remoteRepoURL, + toWorkingDirectory: tempStoreURL, + options: options, + transferProgressBlock: transferProgressBlock) try fm.moveItem(at: tempStoreURL, to: storeURL) storeRepository = try GTRepository(url: storeURL) - if (try? storeRepository?.currentBranch().name) != branchName { - try checkoutAndChangeBranch(withName: branchName) + if (try storeRepository?.currentBranch().name) != branchName { + try checkoutAndChangeBranch(withName: branchName, progressBlock: checkoutProgressBlock) } } catch { credential.delete() + SharedDefaults[.lastSyncedTime] = nil + self.deleteCoreData(entityName: "PasswordEntity") DispatchQueue.main.async { - SharedDefaults[.lastSyncedTime] = nil - self.deleteCoreData(entityName: "PasswordEntity") NotificationCenter.default.post(name: .passwordStoreUpdated, object: nil) } throw(error) } + SharedDefaults[.lastSyncedTime] = Date() + self.updatePasswordEntityCoreData() DispatchQueue.main.async { - SharedDefaults[.lastSyncedTime] = Date() - self.updatePasswordEntityCoreData() NotificationCenter.default.post(name: .passwordStoreUpdated, object: nil) } } - private func checkoutAndChangeBranch(withName localBranchName: String) throws { + private func checkoutAndChangeBranch(withName localBranchName: String, progressBlock: @escaping (String, UInt, UInt) -> Void) throws { guard let storeRepository = storeRepository else { throw AppError.RepositoryNotSet } let remoteBranchName = "origin/\(localBranchName)" - guard let remoteBranch = try? storeRepository.lookUpBranch(withName: remoteBranchName, type: .remote, success: nil) else { - throw AppError.RepositoryRemoteBranchNotFound(remoteBranchName) - } + let remoteBranch = try storeRepository.lookUpBranch(withName: remoteBranchName, type: .remote, success: nil) guard let remoteBranchOid = remoteBranch.oid else { throw AppError.RepositoryRemoteBranchNotFound(remoteBranchName) } let localBranch = try storeRepository.createBranchNamed(localBranchName, from: remoteBranchOid, message: nil) try localBranch.updateTrackingBranch(remoteBranch) - let checkoutOptions = GTCheckoutOptions.init(strategy: .force) + let checkoutOptions = GTCheckoutOptions(strategy: .force, progressBlock: progressBlock) try storeRepository.checkoutReference(localBranch.reference, options: checkoutOptions) try storeRepository.moveHEAD(to: localBranch.reference) } - public func pullRepository(credential: GitCredential, requestGitPassword: @escaping (GitCredential.Credential, String?) -> String?, transferProgressBlock: @escaping (UnsafePointer, UnsafeMutablePointer) -> Void) throws { + public func pullRepository(credential: GitCredential, + requestCredentialPassword: @escaping (GitCredential.Credential, String?) -> String?, + progressBlock: @escaping (UnsafePointer, UnsafeMutablePointer) -> Void) throws { guard let storeRepository = storeRepository else { throw AppError.RepositoryNotSet } - let credentialProvider = try credential.credentialProvider(requestGitPassword: requestGitPassword) + let credentialProvider = try credential.credentialProvider(requestCredentialPassword: requestCredentialPassword) let options = [GTRepositoryRemoteOptionsCredentialProvider: credentialProvider] let remote = try GTRemote(name: "origin", in: storeRepository) - try storeRepository.pull(storeRepository.currentBranch(), from: remote, withOptions: options, progress: transferProgressBlock) + try storeRepository.pull(storeRepository.currentBranch(), from: remote, withOptions: options, progress: progressBlock) + SharedDefaults[.lastSyncedTime] = Date() + self.setAllSynced() + self.updatePasswordEntityCoreData() DispatchQueue.main.async { - SharedDefaults[.lastSyncedTime] = Date() - self.setAllSynced() - self.updatePasswordEntityCoreData() NotificationCenter.default.post(name: .passwordStoreUpdated, object: nil) } } @@ -245,7 +248,7 @@ public class PasswordStore { private func updatePasswordEntityCoreData() { deleteCoreData(entityName: "PasswordEntity") do { - var q = try fm.contentsOfDirectory(atPath: self.storeURL.path).filter{ + var q = try fm.contentsOfDirectory(atPath: self.storeURL.path).filter { !$0.hasPrefix(".") }.map { (filename) -> PasswordEntity in let passwordEntity = NSEntityDescription.insertNewObject(forEntityName: "PasswordEntity", into: context) as! PasswordEntity @@ -443,12 +446,12 @@ public class PasswordStore { return branches.first } - public func pushRepository(credential: GitCredential, requestGitPassword: @escaping (GitCredential.Credential, String?) -> String?, transferProgressBlock: @escaping (UInt32, UInt32, Int, UnsafeMutablePointer) -> Void) throws { + public func pushRepository(credential: GitCredential, requestCredentialPassword: @escaping (GitCredential.Credential, String?) -> String?, transferProgressBlock: @escaping (UInt32, UInt32, Int, UnsafeMutablePointer) -> Void) throws { guard let storeRepository = storeRepository else { throw AppError.RepositoryNotSet } do { - let credentialProvider = try credential.credentialProvider(requestGitPassword: requestGitPassword) + let credentialProvider = try credential.credentialProvider(requestCredentialPassword: requestCredentialPassword) let options = [GTRepositoryRemoteOptionsCredentialProvider: credentialProvider] if let branch = try getLocalBranch(withName: SharedDefaults[.gitBranchName]) { let remote = try GTRemote(name: "origin", in: storeRepository)