diff --git a/Podfile b/Podfile index afcb71f..0036760 100644 --- a/Podfile +++ b/Podfile @@ -1,3 +1,3 @@ target 'pass' do - pod 'ObjectivePGP' + pod 'ObjectivePGP', :git => 'https://github.com/mssun/ObjectivePGP.git' end diff --git a/pass.xcodeproj/project.pbxproj b/pass.xcodeproj/project.pbxproj index 52dbf4a..80aac60 100644 --- a/pass.xcodeproj/project.pbxproj +++ b/pass.xcodeproj/project.pbxproj @@ -9,6 +9,7 @@ /* Begin PBXBuildFile section */ 94BA784B85E071D25EE89B59 /* libPods-pass.a in Frameworks */ = {isa = PBXBuildFile; fileRef = ADCE7A5C3CCC67D7D21BB3C4 /* libPods-pass.a */; }; A262A58D1E68749C006B0890 /* Base32.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = A262A58C1E68749C006B0890 /* Base32.framework */; }; + A27424D91E7C35960093F436 /* NotificationNames.swift in Sources */ = {isa = PBXBuildFile; fileRef = A27424D81E7C35960093F436 /* NotificationNames.swift */; }; A2802BF91E70813A00879216 /* SliderTableViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = A2802BF71E70813A00879216 /* SliderTableViewCell.swift */; }; A2802BFA1E70813A00879216 /* SliderTableViewCell.xib in Resources */ = {isa = PBXBuildFile; fileRef = A2802BF81E70813A00879216 /* SliderTableViewCell.xib */; }; DC037CA61E4B883900609409 /* OpenSourceComponentsTableViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = DC037CA51E4B883900609409 /* OpenSourceComponentsTableViewController.swift */; }; @@ -69,6 +70,7 @@ /* Begin PBXFileReference section */ 274CCFCF32444A2FF46BE7F4 /* Pods-pass.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-pass.debug.xcconfig"; path = "Pods/Target Support Files/Pods-pass/Pods-pass.debug.xcconfig"; sourceTree = ""; }; A262A58C1E68749C006B0890 /* Base32.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Base32.framework; path = Carthage/Build/iOS/Base32.framework; sourceTree = ""; }; + A27424D81E7C35960093F436 /* NotificationNames.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NotificationNames.swift; sourceTree = ""; }; A2802BF71E70813A00879216 /* SliderTableViewCell.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SliderTableViewCell.swift; sourceTree = ""; }; A2802BF81E70813A00879216 /* SliderTableViewCell.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = SliderTableViewCell.xib; sourceTree = ""; }; ADCE7A5C3CCC67D7D21BB3C4 /* libPods-pass.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-pass.a"; sourceTree = BUILT_PRODUCTS_DIR; }; @@ -204,6 +206,7 @@ DC4A746D1E30FBDE00E8EB18 /* Objective-CBridgingHeader.h */, DCA049971E33586A00522E8F /* DefaultsKeys.swift */, DC19400A1E4B36B60077E0A3 /* Utils.swift */, + A27424D81E7C35960093F436 /* NotificationNames.swift */, ); path = Helpers; sourceTree = ""; @@ -447,6 +450,7 @@ DC7E6EEA1E432E48006C2443 /* Password.swift in Sources */, DC4914961E434301007FF592 /* LabelTableViewCell.swift in Sources */, DC5F385B1E56AADB00C69ACA /* PGPKeyArmorSettingTableViewController.swift in Sources */, + A27424D91E7C35960093F436 /* NotificationNames.swift in Sources */, DCAAF7451E2FA66800AB94BC /* SettingsTableViewController.swift in Sources */, DCE6C2671E71261C003038C6 /* PasswordWithFolderTableViewCell.swift in Sources */, DCFB77A71E502DF9008DE471 /* EditPasswordTableViewController.swift in Sources */, diff --git a/pass/AppDelegate.swift b/pass/AppDelegate.swift index 9b635e7..66ee47f 100644 --- a/pass/AppDelegate.swift +++ b/pass/AppDelegate.swift @@ -38,7 +38,7 @@ class AppDelegate: UIResponder, UIApplicationDelegate { } func postSearchNotification() { - NotificationCenter.default.post(Notification(name: Notification.Name("search"))) + NotificationCenter.default.post(name: .passwordSearch, object: nil) } func application(_ application: UIApplication, performActionFor shortcutItem: UIApplicationShortcutItem, completionHandler: @escaping (Bool) -> Void) { diff --git a/pass/Controllers/AboutRepositoryTableViewController.swift b/pass/Controllers/AboutRepositoryTableViewController.swift index 36588ee..f6e7178 100644 --- a/pass/Controllers/AboutRepositoryTableViewController.swift +++ b/pass/Controllers/AboutRepositoryTableViewController.swift @@ -13,6 +13,7 @@ class AboutRepositoryTableViewController: BasicStaticTableViewController { var needRefresh = false var indicatorLabel: UILabel! var indicator: UIActivityIndicatorView! + let passwordStore = PasswordStore.shared override func viewDidLoad() { navigationItemTitle = "About Repository" @@ -31,7 +32,9 @@ class AboutRepositoryTableViewController: BasicStaticTableViewController { tableView.addSubview(indicatorLabel) setTableData() - addNotificationObservers() + + // all password store updates (including erase, discard) will trigger the refresh + NotificationCenter.default.addObserver(self, selector: #selector(setNeedRefresh), name: .passwordStoreUpdated, object: nil) } override func viewWillAppear(_ animated: Bool) { @@ -57,20 +60,20 @@ class AboutRepositoryTableViewController: BasicStaticTableViewController { numberFormatter.numberStyle = NumberFormatter.Style.decimal let fm = FileManager.default - let passwordEntities = PasswordStore.shared.fetchPasswordEntityCoreData(withDir: false) + let passwordEntities = self.passwordStore.fetchPasswordEntityCoreData(withDir: false) let numberOfPasswords = numberFormatter.string(from: NSNumber(value: passwordEntities.count))! var size = UInt64(0) do { - if fm.fileExists(atPath: PasswordStore.shared.storeURL.path) { - size = try fm.allocatedSizeOfDirectoryAtURL(directoryURL: PasswordStore.shared.storeURL) + if fm.fileExists(atPath: self.passwordStore.storeURL.path) { + size = try fm.allocatedSizeOfDirectoryAtURL(directoryURL: self.passwordStore.storeURL) } } catch { print(error) } let sizeOfRepository = ByteCountFormatter.string(fromByteCount: Int64(size), countStyle: ByteCountFormatter.CountStyle.file) - let numberOfCommits = PasswordStore.shared.storeRepository?.numberOfCommits(inCurrentBranch: NSErrorPointer(nilLiteral: ())) ?? 0 + let numberOfCommits = self.passwordStore.storeRepository?.numberOfCommits(inCurrentBranch: NSErrorPointer(nilLiteral: ())) ?? 0 let numberOfCommitsString = numberFormatter.string(from: NSNumber(value: numberOfCommits))! @@ -79,7 +82,7 @@ class AboutRepositoryTableViewController: BasicStaticTableViewController { self?.tableData = [ // section 0 [[.style: CellDataStyle.value1, .accessoryType: type, .title: "Passwords", .detailText: numberOfPasswords], - [.style: CellDataStyle.value1, .accessoryType: type, .title: "Size", .detailText: sizeOfRepository], [.style: CellDataStyle.value1, .accessoryType: type, .title: "Unsynced", .detailText: String(PasswordStore.shared.getNumberOfUnsyncedPasswords())], + [.style: CellDataStyle.value1, .accessoryType: type, .title: "Size", .detailText: sizeOfRepository], [.style: CellDataStyle.value1, .accessoryType: type, .title: "Unsynced", .detailText: String(self?.passwordStore.getNumberOfUnsyncedPasswords() ?? 0)], [.style: CellDataStyle.value1, .accessoryType: type, .title: "Last Synced", .detailText: Utils.getLastUpdatedTimeString()], [.style: CellDataStyle.value1, .accessoryType: type, .title: "Commits", .detailText: numberOfCommitsString], [.title: "Commit Logs", .action: "segue", .link: "showCommitLogsSegue"], @@ -91,11 +94,6 @@ class AboutRepositoryTableViewController: BasicStaticTableViewController { } } } - - private func addNotificationObservers() { - NotificationCenter.default.addObserver(self, selector: #selector(setNeedRefresh), name: NSNotification.Name(rawValue: "passwordUpdated"), object: nil) - NotificationCenter.default.addObserver(self, selector: #selector(setNeedRefresh), name: NSNotification.Name(rawValue: "passwordStoreErased"), object: nil) - } func setNeedRefresh() { needRefresh = true diff --git a/pass/Controllers/AddPasswordTableViewController.swift b/pass/Controllers/AddPasswordTableViewController.swift index 55119d8..d50afb1 100644 --- a/pass/Controllers/AddPasswordTableViewController.swift +++ b/pass/Controllers/AddPasswordTableViewController.swift @@ -13,6 +13,7 @@ class AddPasswordTableViewController: PasswordEditorTableViewController { var password: Password? var tempContent: String = "" + let passwordStore = PasswordStore.shared override func viewDidLoad() { tableData = [ @@ -27,7 +28,7 @@ class AddPasswordTableViewController: PasswordEditorTableViewController { override func shouldPerformSegue(withIdentifier identifier: String, sender: Any?) -> Bool { if identifier == "saveAddPasswordSegue" { // check PGP key - if Defaults[.pgpKeyID] == nil { + if passwordStore.privateKey == nil { let alertTitle = "Cannot Add Password" let alertMessage = "PGP Key is not set. Please set your PGP Key first." Utils.alert(title: alertTitle, message: alertMessage, controller: self, completion: nil) diff --git a/pass/Controllers/AdvancedSettingsTableViewController.swift b/pass/Controllers/AdvancedSettingsTableViewController.swift index b73305b..7b22c93 100644 --- a/pass/Controllers/AdvancedSettingsTableViewController.swift +++ b/pass/Controllers/AdvancedSettingsTableViewController.swift @@ -13,7 +13,8 @@ class AdvancedSettingsTableViewController: UITableViewController { @IBOutlet weak var eraseDataTableViewCell: UITableViewCell! @IBOutlet weak var discardChangesTableViewCell: UITableViewCell! - + let passwordStore = PasswordStore.shared + override func viewDidLoad() { super.viewDidLoad() } @@ -24,8 +25,7 @@ class AdvancedSettingsTableViewController: UITableViewController { let alert = UIAlertController(title: "Erase Password Store Data?", message: "This will delete all local data and settings. Password store data on your remote server will not be affected.", preferredStyle: UIAlertControllerStyle.alert) alert.addAction(UIAlertAction(title: "Erase Password Data", style: UIAlertActionStyle.destructive, handler: {[unowned self] (action) -> Void in SVProgressHUD.show(withStatus: "Erasing ...") - PasswordStore.shared.erase() - NotificationCenter.default.post(Notification(name: Notification.Name("passwordStoreErased"))) + self.passwordStore.erase() self.navigationController!.popViewController(animated: true) SVProgressHUD.showSuccess(withStatus: "Done") SVProgressHUD.dismiss(withDelay: 1) @@ -40,10 +40,7 @@ class AdvancedSettingsTableViewController: UITableViewController { SVProgressHUD.show(withStatus: "Resetting ...") DispatchQueue.main.async { do { - let numberDiscarded = try PasswordStore.shared.reset() - if numberDiscarded > 0 { - NotificationCenter.default.post(Notification(name: Notification.Name("passwordStoreChangeDiscarded"))) - } + let numberDiscarded = try self.passwordStore.reset() self.navigationController!.popViewController(animated: true) switch numberDiscarded { case 0: @@ -56,8 +53,7 @@ class AdvancedSettingsTableViewController: UITableViewController { SVProgressHUD.dismiss(withDelay: 1) } catch { DispatchQueue.main.async { - SVProgressHUD.showError(withStatus: error.localizedDescription) - SVProgressHUD.dismiss(withDelay: 1) + Utils.alert(title: "Error", message: error.localizedDescription, controller: self, completion: nil) } } } diff --git a/pass/Controllers/CommitLogsTableViewController.swift b/pass/Controllers/CommitLogsTableViewController.swift index 0c707dd..4a5088d 100644 --- a/pass/Controllers/CommitLogsTableViewController.swift +++ b/pass/Controllers/CommitLogsTableViewController.swift @@ -11,10 +11,11 @@ import ObjectiveGit class CommitLogsTableViewController: UITableViewController { var commits: [GTCommit] = [] + let passwordStore = PasswordStore.shared override func viewDidLoad() { super.viewDidLoad() - commits = PasswordStore.shared.getRecentCommits(count: 20) + commits = passwordStore.getRecentCommits(count: 20) navigationItem.title = "Recent Commit Logs" navigationController!.navigationBar.topItem!.title = "About" } diff --git a/pass/Controllers/GeneralSettingsTableViewController.swift b/pass/Controllers/GeneralSettingsTableViewController.swift index c94f3a0..9d83de8 100644 --- a/pass/Controllers/GeneralSettingsTableViewController.swift +++ b/pass/Controllers/GeneralSettingsTableViewController.swift @@ -10,7 +10,8 @@ import UIKit import SwiftyUserDefaults class GeneralSettingsTableViewController: BasicStaticTableViewController { - + let passwordStore = PasswordStore.shared + let hideUnknownSwitch: UISwitch = { let uiSwitch = UISwitch() uiSwitch.onTintColor = Globals.blue @@ -177,13 +178,13 @@ class GeneralSettingsTableViewController: BasicStaticTableViewController { func rememberPassphraseSwitchAction(_ sender: Any?) { Defaults[.isRememberPassphraseOn] = rememberPassphraseSwitch.isOn if rememberPassphraseSwitch.isOn == false { - PasswordStore.shared.pgpKeyPassphrase = nil + passwordStore.pgpKeyPassphrase = nil } } func showFolderSwitchAction(_ sender: Any?) { Defaults[.isShowFolderOn] = showFolderSwitch.isOn - NotificationCenter.default.post(Notification(name: Notification.Name("passwordUpdated"))) + NotificationCenter.default.post(name: .passwordStoreUpdated, object: nil) } } diff --git a/pass/Controllers/GitServerSettingTableViewController.swift b/pass/Controllers/GitServerSettingTableViewController.swift index a697bd7..21e29a0 100644 --- a/pass/Controllers/GitServerSettingTableViewController.swift +++ b/pass/Controllers/GitServerSettingTableViewController.swift @@ -15,6 +15,7 @@ class GitServerSettingTableViewController: UITableViewController { @IBOutlet weak var usernameTextField: UITextField! @IBOutlet weak var authSSHKeyCell: UITableViewCell! @IBOutlet weak var authPasswordCell: UITableViewCell! + let passwordStore = PasswordStore.shared var password: String? var authenticationMethod = Defaults[.gitRepositoryAuthenticationMethod] @@ -41,7 +42,7 @@ class GitServerSettingTableViewController: UITableViewController { gitRepositoryURLTextField.text = url.absoluteString } usernameTextField.text = Defaults[.gitRepositoryUsername] - password = PasswordStore.shared.gitRepositoryPassword + password = passwordStore.gitRepositoryPassword authenticationMethod = Defaults[.gitRepositoryAuthenticationMethod] // Grey out ssh option if ssh_key and ssh_key.pub are not present diff --git a/pass/Controllers/PGPKeyArmorSettingTableViewController.swift b/pass/Controllers/PGPKeyArmorSettingTableViewController.swift index e335755..16ba52b 100644 --- a/pass/Controllers/PGPKeyArmorSettingTableViewController.swift +++ b/pass/Controllers/PGPKeyArmorSettingTableViewController.swift @@ -13,12 +13,13 @@ class PGPKeyArmorSettingTableViewController: UITableViewController { @IBOutlet weak var armorPublicKeyTextView: UITextView! @IBOutlet weak var armorPrivateKeyTextView: UITextView! var pgpPassphrase: String? + let passwordStore = PasswordStore.shared override func viewDidLoad() { super.viewDidLoad() armorPublicKeyTextView.text = Defaults[.pgpPublicKeyArmor] armorPrivateKeyTextView.text = Defaults[.pgpPrivateKeyArmor] - pgpPassphrase = PasswordStore.shared.pgpKeyPassphrase + pgpPassphrase = passwordStore.pgpKeyPassphrase } private func createSavePassphraseAlert() -> UIAlertController { diff --git a/pass/Controllers/PGPKeySettingTableViewController.swift b/pass/Controllers/PGPKeySettingTableViewController.swift index 17d0ea3..f19422b 100644 --- a/pass/Controllers/PGPKeySettingTableViewController.swift +++ b/pass/Controllers/PGPKeySettingTableViewController.swift @@ -14,13 +14,14 @@ class PGPKeySettingTableViewController: UITableViewController { @IBOutlet weak var pgpPublicKeyURLTextField: UITextField! @IBOutlet weak var pgpPrivateKeyURLTextField: UITextField! var pgpPassphrase: String? + let passwordStore = PasswordStore.shared override func viewDidLoad() { super.viewDidLoad() tableView.rowHeight = UITableViewAutomaticDimension pgpPublicKeyURLTextField.text = Defaults[.pgpPublicKeyURL]?.absoluteString pgpPrivateKeyURLTextField.text = Defaults[.pgpPrivateKeyURL]?.absoluteString - pgpPassphrase = PasswordStore.shared.pgpKeyPassphrase + pgpPassphrase = passwordStore.pgpKeyPassphrase } override func shouldPerformSegue(withIdentifier identifier: String, sender: Any?) -> Bool { diff --git a/pass/Controllers/PasswordDetailTableViewController.swift b/pass/Controllers/PasswordDetailTableViewController.swift index a032a94..ba3f278 100644 --- a/pass/Controllers/PasswordDetailTableViewController.swift +++ b/pass/Controllers/PasswordDetailTableViewController.swift @@ -18,17 +18,7 @@ class PasswordDetailTableViewController: UITableViewController, UIGestureRecogni var passwordImage: UIImage? var oneTimePasswordIndexPath : IndexPath? var shouldPopCurrentView = false - - let indicatorLable: UILabel = { - let label = UILabel(frame: CGRect(x: 0, y: 0, width: UIScreen.main.bounds.width, height: 21)) - label.center = CGPoint(x: UIScreen.main.bounds.width / 2, y: UIScreen.main.bounds.height * 0.382 + 22) - label.backgroundColor = UIColor.clear - label.textColor = UIColor.gray - label.text = "decrypting password" - label.textAlignment = .center - label.font = UIFont.preferredFont(forTextStyle: .footnote) - return label - }() + let passwordStore = PasswordStore.shared let indicator: UIActivityIndicatorView = { let indicator = UIActivityIndicatorView(activityIndicatorStyle: .gray) @@ -81,7 +71,6 @@ class PasswordDetailTableViewController: UITableViewController, UIGestureRecogni indicator.startAnimating() tableView.addSubview(indicator) - tableView.addSubview(indicatorLable) editUIBarButtonItem.isEnabled = false navigationItem.rightBarButtonItem = editUIBarButtonItem @@ -91,8 +80,8 @@ class PasswordDetailTableViewController: UITableViewController, UIGestureRecogni } var passphrase = "" - if Defaults[.isRememberPassphraseOn] && PasswordStore.shared.pgpKeyPassphrase != nil { - passphrase = PasswordStore.shared.pgpKeyPassphrase! + if Defaults[.isRememberPassphraseOn] && self.passwordStore.pgpKeyPassphrase != nil { + passphrase = self.passwordStore.pgpKeyPassphrase! self.decryptThenShowPassword(passphrase: passphrase) } else { let alert = UIAlertController(title: "Passphrase", message: "Please fill in the passphrase of your PGP secret key.", preferredStyle: UIAlertControllerStyle.alert) @@ -114,7 +103,7 @@ class PasswordDetailTableViewController: UITableViewController, UIGestureRecogni func decryptThenShowPassword(passphrase: String) { if Defaults[.isRememberPassphraseOn] { - PasswordStore.shared.pgpKeyPassphrase = passphrase + self.passwordStore.pgpKeyPassphrase = passphrase } DispatchQueue.global(qos: .userInitiated).async { do { @@ -141,7 +130,6 @@ class PasswordDetailTableViewController: UITableViewController, UIGestureRecogni setTableData() self.tableView.reloadData() indicator.stopAnimating() - indicatorLable.isHidden = true editUIBarButtonItem.isEnabled = true if let urlString = password.getURLString() { if self.passwordEntity?.image == nil{ @@ -188,15 +176,14 @@ class PasswordDetailTableViewController: UITableViewController, UIGestureRecogni if self.password!.changed { SVProgressHUD.show(withStatus: "Saving") DispatchQueue.global(qos: .userInitiated).async { - PasswordStore.shared.update(passwordEntity: self.passwordEntity!, password: self.password!, progressBlock: { progress in + self.passwordStore.update(passwordEntity: self.passwordEntity!, password: self.password!, progressBlock: { progress in DispatchQueue.main.async { SVProgressHUD.showProgress(progress, status: "Encrypting") } }) DispatchQueue.main.async { self.passwordEntity!.synced = false - PasswordStore.shared.saveUpdated(passwordEntity: self.passwordEntity!) - NotificationCenter.default.post(Notification(name: Notification.Name("passwordUpdated"))) + self.passwordStore.saveUpdated(passwordEntity: self.passwordEntity!) self.setTableData() self.tableView.reloadData() SVProgressHUD.showSuccess(withStatus: "Success") @@ -296,7 +283,7 @@ class PasswordDetailTableViewController: UITableViewController, UIGestureRecogni self?.tableView.reloadRows(at: [indexPath], with: UITableViewRowAnimation.automatic) let imageData = UIImageJPEGRepresentation(image, 1) if let entity = self?.passwordEntity { - PasswordStore.shared.updateImage(passwordEntity: entity, image: imageData) + self?.passwordStore.updateImage(passwordEntity: entity, image: imageData) } case .failure(let error): print(error) @@ -373,7 +360,7 @@ class PasswordDetailTableViewController: UITableViewController, UIGestureRecogni footerLabel.numberOfLines = 0 footerLabel.font = UIFont.preferredFont(forTextStyle: .footnote) footerLabel.textColor = UIColor.gray - let dateString = PasswordStore.shared.getLatestUpdateInfo(filename: (passwordEntity?.path)!) + let dateString = self.passwordStore.getLatestUpdateInfo(filename: (passwordEntity?.path)!) footerLabel.text = "Last Updated: \(dateString)" view.addSubview(footerLabel) return view @@ -396,7 +383,7 @@ class PasswordDetailTableViewController: UITableViewController, UIGestureRecogni } private func addNotificationObservers() { - NotificationCenter.default.addObserver(self, selector: #selector(setShouldPopCurrentView), name: NSNotification.Name(rawValue: "passwordStoreChangeDiscarded"), object: nil) + NotificationCenter.default.addObserver(self, selector: #selector(setShouldPopCurrentView), name: .passwordStoreChangeDiscarded, object: nil) } func setShouldPopCurrentView() { diff --git a/pass/Controllers/PasswordsViewController.swift b/pass/Controllers/PasswordsViewController.swift index 1ef9648..035f889 100644 --- a/pass/Controllers/PasswordsViewController.swift +++ b/pass/Controllers/PasswordsViewController.swift @@ -25,6 +25,7 @@ class PasswordsViewController: UIViewController, UITableViewDataSource, UITableV private var passwordsTableEntries: [PasswordsTableEntry] = [] private var filteredPasswordsTableEntries: [PasswordsTableEntry] = [] private var parentPasswordEntity: PasswordEntity? = nil + let passwordStore = PasswordStore.shared private var tapTabBarTime: TimeInterval = 0 @@ -61,9 +62,9 @@ class PasswordsViewController: UIViewController, UITableViewDataSource, UITableV filteredPasswordsTableEntries.removeAll() var passwordEntities = [PasswordEntity]() if Defaults[.isShowFolderOn] { - passwordEntities = PasswordStore.shared.fetchPasswordEntityCoreData(parent: parent) + passwordEntities = self.passwordStore.fetchPasswordEntityCoreData(parent: parent) } else { - passwordEntities = PasswordStore.shared.fetchPasswordEntityCoreData(withDir: false) + passwordEntities = self.passwordStore.fetchPasswordEntityCoreData(withDir: false) } passwordsTableEntries = passwordEntities.map { @@ -82,7 +83,7 @@ class PasswordsViewController: UIViewController, UITableViewDataSource, UITableV SVProgressHUD.show(withStatus: "Saving") DispatchQueue.global(qos: .userInitiated).async { do { - try PasswordStore.shared.add(password: controller.password!, progressBlock: { progress in + try self.passwordStore.add(password: controller.password!, progressBlock: { progress in DispatchQueue.main.async { SVProgressHUD.showProgress(progress, status: "Encrypting") } @@ -91,12 +92,10 @@ class PasswordsViewController: UIViewController, UITableViewDataSource, UITableV DispatchQueue.main.async { SVProgressHUD.showSuccess(withStatus: "Done") SVProgressHUD.dismiss(withDelay: 1) - NotificationCenter.default.post(Notification(name: Notification.Name("passwordUpdated"))) } } catch { DispatchQueue.main.async { - SVProgressHUD.showError(withStatus: error.localizedDescription) - SVProgressHUD.dismiss(withDelay: 1) + Utils.alert(title: "Error", message: error.localizedDescription, controller: self, completion: nil) } } } @@ -107,26 +106,26 @@ class PasswordsViewController: UIViewController, UITableViewDataSource, UITableV SVProgressHUD.setDefaultMaskType(.black) SVProgressHUD.setDefaultStyle(.light) SVProgressHUD.show(withStatus: "Sync Password Store") - let numberOfUnsyncedPasswords = PasswordStore.shared.getNumberOfUnsyncedPasswords() + let numberOfUnsyncedPasswords = self.passwordStore.getNumberOfUnsyncedPasswords() DispatchQueue.global(qos: .userInitiated).async { [unowned self] in do { - try PasswordStore.shared.pullRepository(transferProgressBlock: {(git_transfer_progress, stop) in + try self.passwordStore.pullRepository(transferProgressBlock: {(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: "Pull Remote Repository") } }) if numberOfUnsyncedPasswords > 0 { - try PasswordStore.shared.pushRepository(transferProgressBlock: {(current, total, bytes, stop) in + try self.passwordStore.pushRepository(transferProgressBlock: {(current, total, bytes, stop) in DispatchQueue.main.async { SVProgressHUD.showProgress(Float(current)/Float(total), status: "Push Remote Repository") } }) } DispatchQueue.main.async { - PasswordStore.shared.updatePasswordEntityCoreData() + self.passwordStore.updatePasswordEntityCoreData() self.initPasswordsTableEntries(parent: nil) self.reloadTableView(data: self.passwordsTableEntries) - PasswordStore.shared.setAllSynced() + self.passwordStore.setAllSynced() self.setNavigationItemTitle() Defaults[.lastUpdatedTime] = Date() Defaults[.gitRepositoryPasswordAttempts] = 0 @@ -135,17 +134,15 @@ class PasswordsViewController: UIViewController, UITableViewDataSource, UITableV } } catch { DispatchQueue.main.async { - SVProgressHUD.showError(withStatus: error.localizedDescription) - SVProgressHUD.dismiss(withDelay: 1) + Utils.alert(title: "Error", message: error.localizedDescription, controller: self, completion: nil) } } } } private func addNotificationObservers() { - NotificationCenter.default.addObserver(self, selector: #selector(PasswordsViewController.actOnPasswordUpdatedNotification), name: NSNotification.Name(rawValue: "passwordUpdated"), object: nil) - NotificationCenter.default.addObserver(self, selector: #selector(PasswordsViewController.actOnPasswordStoreErasedNotification), name: NSNotification.Name(rawValue: "passwordStoreErased"), object: nil) - NotificationCenter.default.addObserver(self, selector: #selector(PasswordsViewController.actOnSearchNotification), name: NSNotification.Name(rawValue: "search"), object: nil) + NotificationCenter.default.addObserver(self, selector: #selector(PasswordsViewController.actOnPasswordStoreUpdatedNotification), name: .passwordStoreUpdated, object: nil) + NotificationCenter.default.addObserver(self, selector: #selector(PasswordsViewController.actOnSearchNotification), name: .passwordSearch, object: nil) } override func viewDidLoad() { @@ -283,15 +280,15 @@ class PasswordsViewController: UIViewController, UITableViewDataSource, UITableV } func copyToPasteboard(from indexPath: IndexPath) { - guard Defaults[.pgpKeyID] != nil else { + guard self.passwordStore.privateKey != nil else { Utils.alert(title: "Cannot Copy Password", message: "PGP Key is not set. Please set your PGP Key first.", controller: self, completion: nil) return } let password = getPasswordEntry(by: indexPath).passwordEntity! UIImpactFeedbackGenerator(style: .medium).impactOccurred() var passphrase = "" - if Defaults[.isRememberPassphraseOn] && PasswordStore.shared.pgpKeyPassphrase != nil { - passphrase = PasswordStore.shared.pgpKeyPassphrase! + if Defaults[.isRememberPassphraseOn] && self.passwordStore.pgpKeyPassphrase != nil { + passphrase = self.passwordStore.pgpKeyPassphrase! self.decryptThenCopyPassword(passwordEntity: password, passphrase: passphrase) } else { let alert = UIAlertController(title: "Passphrase", message: "Please fill in the passphrase of your PGP secret key.", preferredStyle: UIAlertControllerStyle.alert) @@ -324,8 +321,7 @@ class PasswordsViewController: UIViewController, UITableViewDataSource, UITableV } catch { print(error) DispatchQueue.main.async { - SVProgressHUD.showError(withStatus: error.localizedDescription) - SVProgressHUD.dismiss(withDelay: 1) + Utils.alert(title: "Error", message: error.localizedDescription, controller: self, completion: nil) } } } @@ -353,7 +349,7 @@ class PasswordsViewController: UIViewController, UITableViewDataSource, UITableV sections.append(newSection) } - func actOnPasswordUpdatedNotification() { + func actOnPasswordStoreUpdatedNotification() { initPasswordsTableEntries(parent: nil) reloadTableView(data: passwordsTableEntries) setNavigationItemTitle() @@ -366,7 +362,7 @@ class PasswordsViewController: UIViewController, UITableViewDataSource, UITableV } else { title = "Password Store" } - let numberOfUnsynced = PasswordStore.shared.getNumberOfUnsyncedPasswords() + let numberOfUnsynced = self.passwordStore.getNumberOfUnsyncedPasswords() if numberOfUnsynced == 0 { navigationItem.title = "\(title)" } else { @@ -374,12 +370,6 @@ class PasswordsViewController: UIViewController, UITableViewDataSource, UITableV } } - func actOnPasswordStoreErasedNotification() { - initPasswordsTableEntries(parent: nil) - reloadTableView(data: passwordsTableEntries) - setNavigationItemTitle() - } - func actOnSearchNotification() { searchController.searchBar.becomeFirstResponder() } @@ -387,7 +377,7 @@ class PasswordsViewController: UIViewController, UITableViewDataSource, UITableV override func shouldPerformSegue(withIdentifier identifier: String, sender: Any?) -> Bool { if identifier == "showPasswordDetail" { - if Defaults[.pgpKeyID] == nil { + guard self.passwordStore.privateKey != nil else { Utils.alert(title: "Cannot Show Password", message: "PGP Key is not set. Please set your PGP Key first.", controller: self, completion: nil) if let s = sender as? UITableViewCell { let selectedIndexPath = tableView.indexPath(for: s)! diff --git a/pass/Controllers/SSHKeySettingTableViewController.swift b/pass/Controllers/SSHKeySettingTableViewController.swift index d04fde0..ad14dd0 100644 --- a/pass/Controllers/SSHKeySettingTableViewController.swift +++ b/pass/Controllers/SSHKeySettingTableViewController.swift @@ -49,9 +49,7 @@ class SSHKeySettingTableViewController: UITableViewController { try Data(contentsOf: Defaults[.gitRepositorySSHPublicKeyURL]!).write(to: Globals.sshPublicKeyURL, options: .atomic) try Data(contentsOf: Defaults[.gitRepositorySSHPrivateKeyURL]!).write(to: Globals.sshPrivateKeyURL, options: .atomic) } catch { - SVProgressHUD.showError(withStatus: error.localizedDescription) - SVProgressHUD.dismiss(withDelay: 1) - print(error) + Utils.alert(title: "Error", message: error.localizedDescription, controller: self, completion: nil) } navigationController!.popViewController(animated: true) diff --git a/pass/Controllers/SettingsTableViewController.swift b/pass/Controllers/SettingsTableViewController.swift index 26bfba9..2d26a34 100644 --- a/pass/Controllers/SettingsTableViewController.swift +++ b/pass/Controllers/SettingsTableViewController.swift @@ -25,7 +25,8 @@ class SettingsTableViewController: UITableViewController { @IBOutlet weak var touchIDTableViewCell: UITableViewCell! @IBOutlet weak var passcodeTableViewCell: UITableViewCell! @IBOutlet weak var passwordRepositoryTableViewCell: UITableViewCell! - + let passwordStore = PasswordStore.shared + @IBAction func cancelPGPKey(segue: UIStoryboardSegue) { } @@ -33,7 +34,7 @@ class SettingsTableViewController: UITableViewController { if let controller = segue.source as? PGPKeySettingTableViewController { Defaults[.pgpPrivateKeyURL] = URL(string: controller.pgpPrivateKeyURLTextField.text!) Defaults[.pgpPublicKeyURL] = URL(string: controller.pgpPublicKeyURLTextField.text!) - PasswordStore.shared.pgpKeyPassphrase = controller.pgpPassphrase + self.passwordStore.pgpKeyPassphrase = controller.pgpPassphrase Defaults[.pgpKeySource] = "url" SVProgressHUD.setDefaultMaskType(.black) @@ -41,12 +42,10 @@ class SettingsTableViewController: UITableViewController { SVProgressHUD.show(withStatus: "Fetching PGP Key") DispatchQueue.global(qos: .userInitiated).async { [unowned self] in do { - try PasswordStore.shared.initPGP(pgpPublicKeyURL: Defaults[.pgpPublicKeyURL]!, - pgpPublicKeyLocalPath: Globals.pgpPublicKeyPath, - pgpPrivateKeyURL: Defaults[.pgpPrivateKeyURL]!, - pgpPrivateKeyLocalPath: Globals.pgpPrivateKeyPath) + try self.passwordStore.initPGPKey(from: Defaults[.pgpPublicKeyURL]!, keyType: .public) + try self.passwordStore.initPGPKey(from: Defaults[.pgpPrivateKeyURL]!, keyType: .secret) DispatchQueue.main.async { - self.pgpKeyTableViewCell.detailTextLabel?.text = Defaults[.pgpKeyID] + self.pgpKeyTableViewCell.detailTextLabel?.text = self.passwordStore.pgpKeyID SVProgressHUD.showSuccess(withStatus: "Success") SVProgressHUD.dismiss(withDelay: 1) Utils.alert(title: "Rememver to Remove the Key", message: "Remember to remove the key from the server.", controller: self, completion: nil) @@ -54,16 +53,14 @@ class SettingsTableViewController: UITableViewController { } catch { DispatchQueue.main.async { self.pgpKeyTableViewCell.detailTextLabel?.text = "Not Set" - Defaults[.pgpKeyID] = nil - SVProgressHUD.showError(withStatus: error.localizedDescription) - SVProgressHUD.dismiss(withDelay: 1) + Utils.alert(title: "Error", message: error.localizedDescription, controller: self, completion: nil) } } } } else if let controller = segue.source as? PGPKeyArmorSettingTableViewController { Defaults[.pgpKeySource] = "armor" - PasswordStore.shared.pgpKeyPassphrase = controller.pgpPassphrase + self.passwordStore.pgpKeyPassphrase = controller.pgpPassphrase if Defaults[.isRememberPassphraseOn] { Utils.addPasswordToKeychain(name: "pgpKeyPassphrase", password: controller.pgpPassphrase!) } @@ -76,21 +73,17 @@ class SettingsTableViewController: UITableViewController { SVProgressHUD.show(withStatus: "Fetching PGP Key") DispatchQueue.global(qos: .userInitiated).async { [unowned self] in do { - try PasswordStore.shared.initPGP(pgpPublicKeyArmor: controller.armorPublicKeyTextView.text!, - pgpPublicKeyLocalPath: Globals.pgpPublicKeyPath, - pgpPrivateKeyArmor: controller.armorPrivateKeyTextView.text!, - pgpPrivateKeyLocalPath: Globals.pgpPrivateKeyPath) + try self.passwordStore.initPGPKey(with: controller.armorPublicKeyTextView.text, keyType: .public) + try self.passwordStore.initPGPKey(with: controller.armorPrivateKeyTextView.text, keyType: .secret) DispatchQueue.main.async { - self.pgpKeyTableViewCell.detailTextLabel?.text = Defaults[.pgpKeyID] + self.pgpKeyTableViewCell.detailTextLabel?.text = self.passwordStore.pgpKeyID SVProgressHUD.showSuccess(withStatus: "Success") SVProgressHUD.dismiss(withDelay: 1) } } catch { DispatchQueue.main.async { self.pgpKeyTableViewCell.detailTextLabel?.text = "Not Set" - Defaults[.pgpKeyID] = nil - SVProgressHUD.showError(withStatus: error.localizedDescription) - SVProgressHUD.dismiss(withDelay: 1) + Utils.alert(title: "Error", message: error.localizedDescription, controller: self, completion: nil) } } } @@ -111,8 +104,8 @@ class SettingsTableViewController: UITableViewController { Defaults[.gitRepositoryURL]!.absoluteString != gitRepostiroyURL || auth != Defaults[.gitRepositoryAuthenticationMethod] || username != Defaults[.gitRepositoryUsername] || - password != PasswordStore.shared.gitRepositoryPassword || - PasswordStore.shared.repositoryExisted() == false { + password != self.passwordStore.gitRepositoryPassword || + self.passwordStore.repositoryExisted() == false { SVProgressHUD.setDefaultMaskType(.black) SVProgressHUD.setDefaultStyle(.light) @@ -134,7 +127,7 @@ class SettingsTableViewController: UITableViewController { let dispatchQueue = DispatchQueue.global(qos: .userInitiated) dispatchQueue.async { do { - try PasswordStore.shared.cloneRepository(remoteRepoURL: URL(string: gitRepostiroyURL)!, + try self.passwordStore.cloneRepository(remoteRepoURL: URL(string: gitRepostiroyURL)!, credential: gitCredential, transferProgressBlock:{ (git_transfer_progress, stop) in DispatchQueue.main.async { @@ -147,9 +140,8 @@ class SettingsTableViewController: UITableViewController { } }) DispatchQueue.main.async { - PasswordStore.shared.updatePasswordEntityCoreData() + self.passwordStore.updatePasswordEntityCoreData() Defaults[.lastUpdatedTime] = Date() - NotificationCenter.default.post(Notification(name: Notification.Name("passwordUpdated"))) Defaults[.gitRepositoryURL] = URL(string: gitRepostiroyURL) Defaults[.gitRepositoryUsername] = username Defaults[.gitRepositoryAuthenticationMethod] = auth @@ -160,9 +152,7 @@ class SettingsTableViewController: UITableViewController { } } catch { DispatchQueue.main.async { - print(error) - SVProgressHUD.showError(withStatus: error.localizedDescription) - SVProgressHUD.dismiss(withDelay: 1) + Utils.alert(title: "Error", message: error.localizedDescription, controller: self, completion: nil) } } @@ -173,7 +163,7 @@ class SettingsTableViewController: UITableViewController { override func viewDidLoad() { super.viewDidLoad() - NotificationCenter.default.addObserver(self, selector: #selector(SettingsTableViewController.actOnPasswordStoreErasedNotification), name: NSNotification.Name(rawValue: "passwordStoreErased"), object: nil) + NotificationCenter.default.addObserver(self, selector: #selector(SettingsTableViewController.actOnPasswordStoreErasedNotification), name: .passwordStoreErased, object: nil) self.passwordRepositoryTableViewCell.detailTextLabel?.text = Defaults[.gitRepositoryURL]?.host touchIDTableViewCell.accessoryView = touchIDSwitch setPGPKeyTableViewCellDetailText() @@ -193,10 +183,10 @@ class SettingsTableViewController: UITableViewController { } private func setPGPKeyTableViewCellDetailText() { - if Defaults[.pgpKeyID] == nil { - pgpKeyTableViewCell.detailTextLabel?.text = "Not Set" + if let pgpKeyID = self.passwordStore.pgpKeyID { + pgpKeyTableViewCell.detailTextLabel?.text = pgpKeyID } else { - pgpKeyTableViewCell.detailTextLabel?.text = Defaults[.pgpKeyID] + pgpKeyTableViewCell.detailTextLabel?.text = "Not Set" } } @@ -230,7 +220,7 @@ class SettingsTableViewController: UITableViewController { })) alert.addTextField(configurationHandler: {(textField: UITextField!) in - textField.text = PasswordStore.shared.gitRepositoryPassword + textField.text = self.passwordStore.gitRepositoryPassword textField.isSecureTextEntry = true }) @@ -343,12 +333,9 @@ class SettingsTableViewController: UITableViewController { DispatchQueue.main.async { - try? PasswordStore.shared.initPGP( - pgpPublicKeyLocalPath: Globals.pgpPublicKeyPath, - pgpPrivateKeyLocalPath: Globals.pgpPrivateKeyPath - ) + self.passwordStore.initPGPKeys() - let key: PGPKey = PasswordStore.shared.getPgpPrivateKey() + let key: PGPKey = self.passwordStore.getPgpPrivateKey() Defaults[.pgpKeySource] = "file" if (key.isEncrypted) { @@ -357,7 +344,7 @@ class SettingsTableViewController: UITableViewController { } SVProgressHUD.dismiss() - self.pgpKeyTableViewCell.detailTextLabel?.text = Defaults[.pgpKeyID] + self.pgpKeyTableViewCell.detailTextLabel?.text = self.passwordStore.pgpKeyID } } diff --git a/pass/Helpers/DefaultsKeys.swift b/pass/Helpers/DefaultsKeys.swift index a658818..a97b1a3 100644 --- a/pass/Helpers/DefaultsKeys.swift +++ b/pass/Helpers/DefaultsKeys.swift @@ -17,8 +17,6 @@ extension DefaultsKeys { static let pgpPublicKeyArmor = DefaultsKey("pgpPublicKeyArmor") static let pgpPrivateKeyArmor = DefaultsKey("pgpPrivateKeyArmor") - static let pgpKeyID = DefaultsKey("pgpKeyID") - static let pgpKeyUserID = DefaultsKey("pgpKeyUserID") static let gitRepositoryURL = DefaultsKey("gitRepositoryURL") static let gitRepositoryAuthenticationMethod = DefaultsKey("gitRepositoryAuthenticationMethod") diff --git a/pass/Helpers/NotificationNames.swift b/pass/Helpers/NotificationNames.swift new file mode 100644 index 0000000..e4bf3d8 --- /dev/null +++ b/pass/Helpers/NotificationNames.swift @@ -0,0 +1,16 @@ +// +// NotificationNames.swift +// pass +// +// Created by Yishi Lin on 17/3/17. +// Copyright © 2017 Yishi Lin, Bob Sun. All rights reserved. +// + +import Foundation + +extension Notification.Name { + static let passwordStoreUpdated = Notification.Name("passwordStoreUpdated") + static let passwordStoreErased = Notification.Name("passwordStoreErased") + static let passwordStoreChangeDiscarded = Notification.Name("passwordStoreChangeDiscarded") + static let passwordSearch = Notification.Name("passwordSearch") +} diff --git a/pass/Helpers/Utils.swift b/pass/Helpers/Utils.swift index 76902cc..726e107 100644 --- a/pass/Helpers/Utils.swift +++ b/pass/Helpers/Utils.swift @@ -10,6 +10,7 @@ import Foundation import SwiftyUserDefaults import KeychainAccess import UIKit +import SVProgressHUD class Utils { static func removeFileIfExists(atPath path: String) { @@ -65,6 +66,7 @@ class Utils { } static func alert(title: String, message: String, controller: UIViewController, completion: (() -> Void)?) { + SVProgressHUD.dismiss() let alert = UIAlertController(title: title, message: message, preferredStyle: UIAlertControllerStyle.alert) alert.addAction(UIAlertAction(title: "OK", style: UIAlertActionStyle.default, handler: nil)) controller.present(alert, animated: true, completion: completion) @@ -79,7 +81,6 @@ class Utils { Defaults.remove(.pgpPrivateKeyArmor) Defaults.remove(.pgpPrivateKeyURL) Defaults.remove(.pgpPublicKeyURL) - Defaults.remove(.pgpKeyID) Utils.removeKeychain(name: ".pgpKeyPassphrase") } diff --git a/pass/Info.plist b/pass/Info.plist index c59a698..f1ca210 100644 --- a/pass/Info.plist +++ b/pass/Info.plist @@ -17,9 +17,9 @@ CFBundlePackageType APPL CFBundleShortVersionString - 0.2 + 0.2.1 CFBundleVersion - 24 + 1 ITSAppUsesNonExemptEncryption LSRequiresIPhoneOS @@ -35,6 +35,8 @@ $(PRODUCT_BUNDLE_IDENTIFIER).search + UIFileSharingEnabled + UILaunchStoryboardName Main UIMainStoryboardFile @@ -43,8 +45,6 @@ armv7 - UIFileSharingEnabled - UISupportedInterfaceOrientations UIInterfaceOrientationPortrait diff --git a/pass/Models/PasswordStore.swift b/pass/Models/PasswordStore.swift index ddc2351..be28d0f 100644 --- a/pass/Models/PasswordStore.swift +++ b/pass/Models/PasswordStore.swift @@ -93,11 +93,23 @@ struct GitCredential { class PasswordStore { static let shared = PasswordStore() - let storeURL = URL(fileURLWithPath: "\(Globals.repositoryPath)") let tempStoreURL = URL(fileURLWithPath: "\(Globals.repositoryPath)-temp") + var storeRepository: GTRepository? var gitCredential: GitCredential? + var pgpKeyID: String? + var publicKey: PGPKey? { + didSet { + if publicKey != nil { + pgpKeyID = publicKey!.keyID!.shortKeyString + } else { + pgpKeyID = nil + } + } + } + var privateKey: PGPKey? + var gitSignatureForNow: GTSignature { get { return GTSignature(name: Defaults[.gitRepositoryUsername]!, email: Defaults[.gitRepositoryUsername]!+"@passforios", time: Date())! @@ -134,11 +146,7 @@ class PasswordStore { } catch { print(error) } - if Defaults[.pgpKeyID] != nil { - pgp.importKeys(fromFile: Globals.pgpPublicKeyPath, allowDuplicates: false) - pgp.importKeys(fromFile: Globals.pgpPrivateKeyPath, allowDuplicates: false) - - } + initPGPKeys() if Defaults[.gitRepositoryAuthenticationMethod] == "Password" { gitCredential = GitCredential(credential: GitCredential.Credential.http(userName: Defaults[.gitRepositoryUsername]!, password: Utils.getPasswordFromKeychain(name: "gitRepositoryPassword") ?? "")) } else if Defaults[.gitRepositoryAuthenticationMethod] == "SSH Key"{ @@ -157,28 +165,74 @@ class PasswordStore { } - func initPGP(pgpPublicKeyLocalPath: String, pgpPrivateKeyLocalPath: String) throws { - let pgpPublicKeyData = NSData(contentsOfFile: pgpPublicKeyLocalPath)! as Data - if pgpPublicKeyData.count == 0 { - throw NSError(domain: "me.mssun.pass.error", code: 2, userInfo: [NSLocalizedDescriptionKey: "Cannot import public key."]) + public func initPGPKeys() { + do { + try initPGPKey(.public) + try initPGPKey(.secret) + } catch { + print(error) } - pgp.importKeys(from: pgpPublicKeyData, allowDuplicates: false) - if pgp.getKeysOf(.public).count == 0 { - throw NSError(domain: "me.mssun.pass.error", code: 2, userInfo: [NSLocalizedDescriptionKey: "Cannot import public key."]) + } + + public func initPGPKey(_ keyType: PGPKeyType) throws { + var keyPath = "" + switch keyType { + case .public: + keyPath = Globals.pgpPublicKeyPath + case .secret: + keyPath = Globals.pgpPrivateKeyPath + default: + throw NSError(domain: "me.mssun.pass.error", code: 2, userInfo: [NSLocalizedDescriptionKey: "Cannot import key."]) } - let pgpPrivateKeyData = NSData(contentsOfFile: pgpPrivateKeyLocalPath)! as Data - if pgpPrivateKeyData.count == 0 { - throw NSError(domain: "me.mssun.pass.error", code: 2, userInfo: [NSLocalizedDescriptionKey: "Cannot import public key."]) + + if let key = importKey(from: keyPath) { + switch keyType { + case .public: + self.publicKey = key + case .secret: + self.privateKey = key + default: + throw NSError(domain: "me.mssun.pass.error", code: 2, userInfo: [NSLocalizedDescriptionKey: "Cannot import key."]) + } + } else { + throw NSError(domain: "me.mssun.pass.error", code: 2, userInfo: [NSLocalizedDescriptionKey: "Cannot import key."]) } - pgp.importKeys(from: pgpPrivateKeyData, allowDuplicates: false) - if pgp.getKeysOf(.secret).count == 0 { - throw NSError(domain: "me.mssun.pass.error", code: 2, userInfo: [NSLocalizedDescriptionKey: "Cannot import seceret key."]) + } + + public func initPGPKey(from url: URL, keyType: PGPKeyType) throws{ + var pgpKeyLocalPath = "" + if keyType == .public { + pgpKeyLocalPath = Globals.pgpPublicKeyPath + } else { + pgpKeyLocalPath = Globals.pgpPrivateKeyPath } - let key: PGPKey = getPgpPrivateKey() - Defaults[.pgpKeyID] = key.keyID!.shortKeyString - if let gpgUser = key.users[0] as? PGPUser { - Defaults[.pgpKeyUserID] = gpgUser.userID + let pgpKeyData = try Data(contentsOf: url) + try pgpKeyData.write(to: URL(fileURLWithPath: pgpKeyLocalPath), options: .atomic) + try initPGPKey(keyType) + } + + public func initPGPKey(with armorKey: String, keyType: PGPKeyType) throws { + var pgpKeyLocalPath = "" + if keyType == .public { + pgpKeyLocalPath = Globals.pgpPublicKeyPath + } else { + pgpKeyLocalPath = Globals.pgpPrivateKeyPath } + try armorKey.write(toFile: pgpKeyLocalPath, atomically: true, encoding: .ascii) + try initPGPKey(keyType) + } + + + private func importKey(from keyPath: String) -> PGPKey? { + let fm = FileManager.default + if fm.fileExists(atPath: keyPath) { + if let keys = pgp.importKeys(fromFile: keyPath, allowDuplicates: false) as? [PGPKey] { + if keys.count > 0 { + return keys[0] + } + } + } + return nil } func getPgpPrivateKey() -> PGPKey { @@ -207,20 +261,6 @@ class PasswordStore { return true } - func initPGP(pgpPublicKeyURL: URL, pgpPublicKeyLocalPath: String, pgpPrivateKeyURL: URL, pgpPrivateKeyLocalPath: String) throws { - let pgpPublicData = try Data(contentsOf: pgpPublicKeyURL) - try pgpPublicData.write(to: URL(fileURLWithPath: pgpPublicKeyLocalPath), options: .atomic) - let pgpPrivateData = try Data(contentsOf: pgpPrivateKeyURL) - try pgpPrivateData.write(to: URL(fileURLWithPath: pgpPrivateKeyLocalPath), options: .atomic) - try initPGP(pgpPublicKeyLocalPath: pgpPublicKeyLocalPath, pgpPrivateKeyLocalPath: pgpPrivateKeyLocalPath) - } - - func initPGP(pgpPublicKeyArmor: String, pgpPublicKeyLocalPath: String, pgpPrivateKeyArmor: String, pgpPrivateKeyLocalPath: String) throws { - try pgpPublicKeyArmor.write(toFile: pgpPublicKeyLocalPath, atomically: true, encoding: .ascii) - try pgpPrivateKeyArmor.write(toFile: pgpPrivateKeyLocalPath, atomically: true, encoding: .ascii) - try initPGP(pgpPublicKeyLocalPath: pgpPublicKeyLocalPath, pgpPrivateKeyLocalPath: pgpPrivateKeyLocalPath) - } - func cloneRepository(remoteRepoURL: URL, credential: GitCredential, transferProgressBlock: @escaping (UnsafePointer, UnsafeMutablePointer) -> Void, @@ -245,6 +285,7 @@ class PasswordStore { } storeRepository = try GTRepository(url: storeURL) gitCredential = credential + NotificationCenter.default.post(name: .passwordStoreUpdated, object: nil) } func pullRepository(transferProgressBlock: @escaping (UnsafePointer, UnsafeMutablePointer) -> Void) throws { @@ -257,6 +298,7 @@ class PasswordStore { ] let remote = try GTRemote(name: "origin", in: storeRepository!) try storeRepository?.pull((storeRepository?.currentBranch())!, from: remote, withOptions: options, progress: transferProgressBlock) + NotificationCenter.default.post(name: .passwordStoreUpdated, object: nil) } @@ -502,18 +544,22 @@ class PasswordStore { print(saveURL.path) let _ = createAddCommitInRepository(message: "Add password for \(passwordEntity.nameWithCategory) to store using Pass for iOS.", fileData: encryptedData, filename: saveURL.lastPathComponent, progressBlock: progressBlock) progressBlock(1.0) + NotificationCenter.default.post(name: .passwordStoreUpdated, object: nil) } catch { print(error) } } func update(passwordEntity: PasswordEntity, password: Password, progressBlock: (_ progress: Float) -> Void) { + progressBlock(0.0) do { let encryptedData = try passwordEntity.encrypt(password: password) let saveURL = storeURL.appendingPathComponent(passwordEntity.path!) try encryptedData.write(to: saveURL) progressBlock(0.3) let _ = createAddCommitInRepository(message: "Edit password for \(passwordEntity.nameWithCategory) using Pass for iOS.", fileData: encryptedData, filename: saveURL.lastPathComponent, progressBlock: progressBlock) + progressBlock(1.0) + NotificationCenter.default.post(name: .passwordStoreUpdated, object: nil) } catch { print(error) } @@ -564,6 +610,8 @@ class PasswordStore { } func erase() { + publicKey = nil + privateKey = nil Utils.removeFileIfExists(at: storeURL) Utils.removeFileIfExists(at: tempStoreURL) @@ -579,6 +627,9 @@ class PasswordStore { Defaults.removeAll() storeRepository = nil + + NotificationCenter.default.post(name: .passwordStoreUpdated, object: nil) + NotificationCenter.default.post(name: .passwordStoreErased, object: nil) } // return the number of discarded commits @@ -603,7 +654,8 @@ class PasswordStore { } try self.storeRepository?.reset(to: newHead, resetType: GTRepositoryResetType.hard) self.updatePasswordEntityCoreData() - NotificationCenter.default.post(Notification(name: Notification.Name("passwordUpdated"))) + NotificationCenter.default.post(name: .passwordStoreUpdated, object: nil) + NotificationCenter.default.post(name: .passwordStoreChangeDiscarded, object: nil) self.setAllSynced() return localCommits.count } else { diff --git a/pass/Views/LabelTableViewCell.swift b/pass/Views/LabelTableViewCell.swift index e01cd18..242ca3b 100644 --- a/pass/Views/LabelTableViewCell.swift +++ b/pass/Views/LabelTableViewCell.swift @@ -19,6 +19,7 @@ class LabelTableViewCell: UITableViewCell { @IBOutlet weak var contentLabel: UILabel! @IBOutlet weak var titleLabel: UILabel! + let passwordStore = PasswordStore.shared var isPasswordCell = false var isURLCell = false @@ -126,11 +127,10 @@ class LabelTableViewCell: UITableViewCell { // commit if password.changed { DispatchQueue.global(qos: .userInitiated).async { - PasswordStore.shared.update(passwordEntity: passwordEntity, password: password, progressBlock: {_ in }) + self.passwordStore.update(passwordEntity: passwordEntity, password: password, progressBlock: {_ in }) DispatchQueue.main.async { passwordEntity.synced = false - PasswordStore.shared.saveUpdated(passwordEntity: passwordEntity) - NotificationCenter.default.post(Notification(name: Notification.Name("passwordUpdated"))) + self.passwordStore.saveUpdated(passwordEntity: passwordEntity) // reload so that the "unsynced" symbol could be added self.passwordTableView?.tableView.reloadRows(at: [IndexPath(row: 0, section: 0)], with: UITableViewRowAnimation.automatic) SVProgressHUD.showSuccess(withStatus: "Password Copied\nCounter Updated")