lint: delete trailing whitespaces

This commit is contained in:
Mingshen Sun 2018-12-09 16:59:07 -08:00
parent 2ba6917710
commit ed387069a4
No known key found for this signature in database
GPG key ID: 1F86BA2052FED3B4
59 changed files with 624 additions and 623 deletions

View file

@ -1,4 +1,4 @@
<img src="icon/icon_round.png" width="76"/> <img src="icon/icon_round.png" width="76"/>
# Pass # Pass
[![GitHub release](https://img.shields.io/github/release/mssun/passforios.svg)](https://github.com/mssun/passforios/releases) [![GitHub release](https://img.shields.io/github/release/mssun/passforios.svg)](https://github.com/mssun/passforios/releases)

View file

@ -1 +1,2 @@
theme: jekyll-theme-minimal theme: jekyll-theme-minimal

View file

@ -23,7 +23,7 @@ class AppDelegate: UIResponder, UIApplicationDelegate {
let presenter = PasscodeLockPresenter(mainWindow: self.window) let presenter = PasscodeLockPresenter(mainWindow: self.window)
return presenter return presenter
}() }()
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool { func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {
// Override point for customization after application launch. // Override point for customization after application launch.
SVProgressHUD.setMinimumSize(CGSize(width: 150, height: 100)) SVProgressHUD.setMinimumSize(CGSize(width: 150, height: 100))
@ -35,18 +35,18 @@ class AppDelegate: UIResponder, UIApplicationDelegate {
} }
return true return true
} }
func application(_ application: UIApplication, supportedInterfaceOrientationsFor window: UIWindow?) -> UIInterfaceOrientationMask { func application(_ application: UIApplication, supportedInterfaceOrientationsFor window: UIWindow?) -> UIInterfaceOrientationMask {
if let _ = window?.rootViewController as? PasscodeLockViewController { if let _ = window?.rootViewController as? PasscodeLockViewController {
window?.frame = UIScreen.main.bounds window?.frame = UIScreen.main.bounds
} }
return .all return .all
} }
@objc func postSearchNotification() { @objc func postSearchNotification() {
NotificationCenter.default.post(name: .passwordSearch, object: nil) NotificationCenter.default.post(name: .passwordSearch, object: nil)
} }
func application(_ application: UIApplication, performActionFor shortcutItem: UIApplicationShortcutItem, completionHandler: @escaping (Bool) -> Void) { func application(_ application: UIApplication, performActionFor shortcutItem: UIApplicationShortcutItem, completionHandler: @escaping (Bool) -> Void) {
if shortcutItem.type == Globals.bundleIdentifier + ".search" { if shortcutItem.type == Globals.bundleIdentifier + ".search" {
let tabBarController = self.window!.rootViewController as! UITabBarController let tabBarController = self.window!.rootViewController as! UITabBarController
@ -60,7 +60,7 @@ class AppDelegate: UIResponder, UIApplicationDelegate {
func applicationWillResignActive(_ application: UIApplication) { func applicationWillResignActive(_ application: UIApplication) {
// Sent when the application is about to move from active to inactive state. This can occur for certain types of temporary interruptions (such as an incoming phone call or SMS message) or when the user quits the application and it begins the transition to the background state. // Sent when the application is about to move from active to inactive state. This can occur for certain types of temporary interruptions (such as an incoming phone call or SMS message) or when the user quits the application and it begins the transition to the background state.
// Use this method to pause ongoing tasks, disable timers, and invalidate graphics rendering callbacks. Games should use this method to pause the game. // Use this method to pause ongoing tasks, disable timers, and invalidate graphics rendering callbacks. Games should use this method to pause the game.
// Display a blur effect view // Display a blur effect view
let blurEffect = UIBlurEffect(style: UIBlurEffectStyle.light) let blurEffect = UIBlurEffect(style: UIBlurEffectStyle.light)
let blurEffectView = UIVisualEffectView(effect: blurEffect) let blurEffectView = UIVisualEffectView(effect: blurEffect)
@ -68,7 +68,7 @@ class AppDelegate: UIResponder, UIApplicationDelegate {
blurEffectView.tag = ViewTag.blur.rawValue blurEffectView.tag = ViewTag.blur.rawValue
blurEffectView.autoresizingMask = [.flexibleWidth, .flexibleHeight] blurEffectView.autoresizingMask = [.flexibleWidth, .flexibleHeight]
self.window?.addSubview(blurEffectView) self.window?.addSubview(blurEffectView)
// Display the Pass icon in the middle of the screen // Display the Pass icon in the middle of the screen
let iconsDictionary = Bundle.main.infoDictionary?["CFBundleIcons"] as? NSDictionary let iconsDictionary = Bundle.main.infoDictionary?["CFBundleIcons"] as? NSDictionary
let primaryIconsDictionary = iconsDictionary?["CFBundlePrimaryIcon"] as? NSDictionary let primaryIconsDictionary = iconsDictionary?["CFBundlePrimaryIcon"] as? NSDictionary
@ -85,7 +85,7 @@ class AppDelegate: UIResponder, UIApplicationDelegate {
func applicationDidEnterBackground(_ application: UIApplication) { func applicationDidEnterBackground(_ application: UIApplication) {
// Use this method to release shared resources, save user data, invalidate timers, and store enough application state information to restore your application to its current state in case it is terminated later. // Use this method to release shared resources, save user data, invalidate timers, and store enough application state information to restore your application to its current state in case it is terminated later.
// If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits. // If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits.
} }
func applicationWillEnterForeground(_ application: UIApplication) { func applicationWillEnterForeground(_ application: UIApplication) {
@ -95,7 +95,7 @@ class AppDelegate: UIResponder, UIApplicationDelegate {
func applicationDidBecomeActive(_ application: UIApplication) { func applicationDidBecomeActive(_ application: UIApplication) {
// Restart any tasks that were paused (or not yet started) while the application was inactive. If the application was previously in the background, optionally refresh the user interface. // Restart any tasks that were paused (or not yet started) while the application was inactive. If the application was previously in the background, optionally refresh the user interface.
self.window?.viewWithTag(ViewTag.appicon.rawValue)?.removeFromSuperview() self.window?.viewWithTag(ViewTag.appicon.rawValue)?.removeFromSuperview()
self.window?.viewWithTag(ViewTag.blur.rawValue)?.removeFromSuperview() self.window?.viewWithTag(ViewTag.blur.rawValue)?.removeFromSuperview()
} }
@ -107,7 +107,7 @@ class AppDelegate: UIResponder, UIApplicationDelegate {
} }
// MARK: - Core Data stack // MARK: - Core Data stack
lazy var persistentContainer: NSPersistentContainer = { lazy var persistentContainer: NSPersistentContainer = {
/* /*
The persistent container for the application. This implementation The persistent container for the application. This implementation
@ -126,7 +126,7 @@ class AppDelegate: UIResponder, UIApplicationDelegate {
if let error = error as NSError? { if let error = error as NSError? {
// Replace this implementation with code to handle the error appropriately. // Replace this implementation with code to handle the error appropriately.
// fatalError() causes the application to generate a crash log and terminate. You should not use this function in a shipping application, although it may be useful during development. // fatalError() causes the application to generate a crash log and terminate. You should not use this function in a shipping application, although it may be useful during development.
/* /*
Typical reasons for an error here include: Typical reasons for an error here include:
* The parent directory does not exist, cannot be created, or disallows writing. * The parent directory does not exist, cannot be created, or disallows writing.
@ -140,9 +140,9 @@ class AppDelegate: UIResponder, UIApplicationDelegate {
}) })
return container return container
}() }()
// MARK: - Core Data Saving support // MARK: - Core Data Saving support
func saveContext () { func saveContext () {
let context = persistentContainer.viewContext let context = persistentContainer.viewContext
if context.hasChanges { if context.hasChanges {

View file

@ -10,7 +10,7 @@ import UIKit
import passKit import passKit
class AboutRepositoryTableViewController: BasicStaticTableViewController { class AboutRepositoryTableViewController: BasicStaticTableViewController {
private var needRefresh = false private var needRefresh = false
private var indicator: UIActivityIndicatorView = { private var indicator: UIActivityIndicatorView = {
let indicator = UIActivityIndicatorView(activityIndicatorStyle: .gray) let indicator = UIActivityIndicatorView(activityIndicatorStyle: .gray)
@ -23,13 +23,13 @@ class AboutRepositoryTableViewController: BasicStaticTableViewController {
indicator.center = CGPoint(x: view.bounds.midX, y: view.bounds.height * 0.382) indicator.center = CGPoint(x: view.bounds.midX, y: view.bounds.height * 0.382)
tableView.addSubview(indicator) tableView.addSubview(indicator)
setTableData() setTableData()
// all password store updates (including erase, discard) will trigger the refresh // all password store updates (including erase, discard) will trigger the refresh
NotificationCenter.default.addObserver(self, selector: #selector(setNeedRefresh), name: .passwordStoreUpdated, object: nil) NotificationCenter.default.addObserver(self, selector: #selector(setNeedRefresh), name: .passwordStoreUpdated, object: nil)
} }
override func viewWillAppear(_ animated: Bool) { override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated) super.viewWillAppear(animated)
if needRefresh { if needRefresh {
@ -37,14 +37,14 @@ class AboutRepositoryTableViewController: BasicStaticTableViewController {
needRefresh = false needRefresh = false
} }
} }
private func setTableData() { private func setTableData() {
// clear current contents (if any) // clear current contents (if any)
self.tableData.removeAll(keepingCapacity: true) self.tableData.removeAll(keepingCapacity: true)
self.tableView.reloadData() self.tableView.reloadData()
indicator.startAnimating() indicator.startAnimating()
// reload the table // reload the table
DispatchQueue.global(qos: .userInitiated).async { DispatchQueue.global(qos: .userInitiated).async {
let passwords = self.numberOfPasswordsString() let passwords = self.numberOfPasswordsString()
@ -52,7 +52,7 @@ class AboutRepositoryTableViewController: BasicStaticTableViewController {
let localCommits = self.numberOfLocalCommitsString() let localCommits = self.numberOfLocalCommitsString()
let lastSynced = self.lastSyncedTimeString() let lastSynced = self.lastSyncedTimeString()
let commits = self.numberOfPasswordsString() let commits = self.numberOfPasswordsString()
DispatchQueue.main.async { [weak self] in DispatchQueue.main.async { [weak self] in
guard let strongSelf = self else { guard let strongSelf = self else {
return return
@ -97,7 +97,7 @@ class AboutRepositoryTableViewController: BasicStaticTableViewController {
formatter.timeStyle = .short formatter.timeStyle = .short
return formatter.string(from: date) return formatter.string(from: date)
} }
@objc func setNeedRefresh() { @objc func setNeedRefresh() {
needRefresh = true needRefresh = true
} }

View file

@ -9,21 +9,21 @@
import UIKit import UIKit
class AboutTableViewController: BasicStaticTableViewController { class AboutTableViewController: BasicStaticTableViewController {
override func viewDidLoad() { override func viewDidLoad() {
tableData = [ tableData = [
// section 0 // section 0
[[.title: "Website", .action: "link", .link: "https://github.com/mssun/pass-ios.git"], [[.title: "Website", .action: "link", .link: "https://github.com/mssun/pass-ios.git"],
[.title: "Help", .action: "link", .link: "https://github.com/mssun/passforios/wiki"], [.title: "Help", .action: "link", .link: "https://github.com/mssun/passforios/wiki"],
[.title: "Contact Developer", .action: "link", .link: "mailto:developer@passforios.mssun.me?subject=Pass%20for%20iOS"],], [.title: "Contact Developer", .action: "link", .link: "mailto:developer@passforios.mssun.me?subject=Pass%20for%20iOS"],],
// section 1, // section 1,
[[.title: "Open Source Components", .action: "segue", .link: "showOpenSourceComponentsSegue"], [[.title: "Open Source Components", .action: "segue", .link: "showOpenSourceComponentsSegue"],
[.title: "Special Thanks", .action: "segue", .link: "showSpecialThanksSegue"],], [.title: "Special Thanks", .action: "segue", .link: "showSpecialThanksSegue"],],
] ]
super.viewDidLoad() super.viewDidLoad()
} }
override func tableView(_ tableView: UITableView, viewForFooterInSection section: Int) -> UIView? { override func tableView(_ tableView: UITableView, viewForFooterInSection section: Int) -> UIView? {
if section == tableData.count - 1 { if section == tableData.count - 1 {
let view = UIView() let view = UIView()
@ -38,7 +38,7 @@ class AboutTableViewController: BasicStaticTableViewController {
} }
return nil return nil
} }
override func tableView(_ tableView: UITableView, titleForHeaderInSection section: Int) -> String? { override func tableView(_ tableView: UITableView, titleForHeaderInSection section: Int) -> String? {
if section == 1 { if section == 1 {
return "Acknowledgements".uppercased() return "Acknowledgements".uppercased()

View file

@ -27,7 +27,7 @@ class AddPasswordTableViewController: PasswordEditorTableViewController {
tableData[0][0][PasswordEditorCellKey.content] = defaultDirPrefix tableData[0][0][PasswordEditorCellKey.content] = defaultDirPrefix
super.viewDidLoad() super.viewDidLoad()
} }
override func shouldPerformSegue(withIdentifier identifier: String, sender: Any?) -> Bool { override func shouldPerformSegue(withIdentifier identifier: String, sender: Any?) -> Bool {
if identifier == "saveAddPasswordSegue" { if identifier == "saveAddPasswordSegue" {
// check PGP key // check PGP key
@ -37,7 +37,7 @@ class AddPasswordTableViewController: PasswordEditorTableViewController {
Utils.alert(title: alertTitle, message: alertMessage, controller: self, completion: nil) Utils.alert(title: alertTitle, message: alertMessage, controller: self, completion: nil)
return false return false
} }
// check name // check name
guard checkName() == true else { guard checkName() == true else {
return false return false
@ -45,7 +45,7 @@ class AddPasswordTableViewController: PasswordEditorTableViewController {
} }
return true return true
} }
override func prepare(for segue: UIStoryboardSegue, sender: Any?) { override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
super.prepare(for: segue, sender: sender) super.prepare(for: segue, sender: sender)
if segue.identifier == "saveAddPasswordSegue" { if segue.identifier == "saveAddPasswordSegue" {

View file

@ -17,7 +17,7 @@ class AdvancedSettingsTableViewController: UITableViewController {
@IBOutlet weak var eraseDataTableViewCell: UITableViewCell! @IBOutlet weak var eraseDataTableViewCell: UITableViewCell!
@IBOutlet weak var discardChangesTableViewCell: UITableViewCell! @IBOutlet weak var discardChangesTableViewCell: UITableViewCell!
let passwordStore = PasswordStore.shared let passwordStore = PasswordStore.shared
let encryptInASCIIArmoredSwitch: UISwitch = { let encryptInASCIIArmoredSwitch: UISwitch = {
let uiSwitch = UISwitch() let uiSwitch = UISwitch()
uiSwitch.onTintColor = Globals.blue uiSwitch.onTintColor = Globals.blue
@ -33,7 +33,7 @@ class AdvancedSettingsTableViewController: UITableViewController {
encryptInASCIIArmoredTableViewCell.selectionStyle = .none encryptInASCIIArmoredTableViewCell.selectionStyle = .none
setGitSignatureText() setGitSignatureText()
} }
private func setGitSignatureText() { private func setGitSignatureText() {
let gitSignatureName = passwordStore.gitSignatureForNow.name! let gitSignatureName = passwordStore.gitSignatureForNow.name!
let gitSignatureEmail = passwordStore.gitSignatureForNow.email! let gitSignatureEmail = passwordStore.gitSignatureForNow.email!
@ -77,17 +77,17 @@ class AdvancedSettingsTableViewController: UITableViewController {
} catch { } catch {
Utils.alert(title: "Error", message: error.localizedDescription, controller: self, completion: nil) Utils.alert(title: "Error", message: error.localizedDescription, controller: self, completion: nil)
} }
})) }))
alert.addAction(UIAlertAction(title: "Dismiss", style: UIAlertActionStyle.cancel, handler:nil)) alert.addAction(UIAlertAction(title: "Dismiss", style: UIAlertActionStyle.cancel, handler:nil))
self.present(alert, animated: true, completion: nil) self.present(alert, animated: true, completion: nil)
} }
} }
@objc func encryptInASCIIArmoredAction(_ sender: Any?) { @objc func encryptInASCIIArmoredAction(_ sender: Any?) {
SharedDefaults[.encryptInArmored] = encryptInASCIIArmoredSwitch.isOn SharedDefaults[.encryptInArmored] = encryptInASCIIArmoredSwitch.isOn
} }
@IBAction func saveGitConfigSetting(segue: UIStoryboardSegue) { @IBAction func saveGitConfigSetting(segue: UIStoryboardSegue) {
if let controller = segue.source as? GitConfigSettingTableViewController { if let controller = segue.source as? GitConfigSettingTableViewController {
if let gitSignatureName = controller.nameTextField.text, if let gitSignatureName = controller.nameTextField.text,

View file

@ -27,18 +27,18 @@ enum CellDataKey {
class BasicStaticTableViewController: UITableViewController, MFMailComposeViewControllerDelegate { class BasicStaticTableViewController: UITableViewController, MFMailComposeViewControllerDelegate {
var tableData = [[Dictionary<CellDataKey, Any>]]() var tableData = [[Dictionary<CellDataKey, Any>]]()
var navigationItemTitle: String? var navigationItemTitle: String?
override func viewDidLoad() { override func viewDidLoad() {
super.viewDidLoad() super.viewDidLoad()
if navigationItemTitle != nil { if navigationItemTitle != nil {
navigationItem.title = navigationItemTitle navigationItem.title = navigationItemTitle
} }
} }
override func numberOfSections(in tableView: UITableView) -> Int { override func numberOfSections(in tableView: UITableView) -> Int {
return tableData.count return tableData.count
} }
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return tableData[section].count return tableData[section].count
} }
@ -47,13 +47,13 @@ class BasicStaticTableViewController: UITableViewController, MFMailComposeViewCo
super.didReceiveMemoryWarning() super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated. // Dispose of any resources that can be recreated.
} }
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cellData = tableData[indexPath.section][indexPath.row] let cellData = tableData[indexPath.section][indexPath.row]
let cellDataStyle = cellData[CellDataKey.style] as? CellDataStyle let cellDataStyle = cellData[CellDataKey.style] as? CellDataStyle
var cell: UITableViewCell? var cell: UITableViewCell?
switch cellDataStyle ?? .defaultStyle { switch cellDataStyle ?? .defaultStyle {
case .value1: case .value1:
cell = UITableViewCell(style: .value1, reuseIdentifier: "value1 cell") cell = UITableViewCell(style: .value1, reuseIdentifier: "value1 cell")
@ -61,7 +61,7 @@ class BasicStaticTableViewController: UITableViewController, MFMailComposeViewCo
default: default:
cell = UITableViewCell(style: .default, reuseIdentifier: "default cell") cell = UITableViewCell(style: .default, reuseIdentifier: "default cell")
} }
if let detailText = cellData[CellDataKey.detailText] as? String { if let detailText = cellData[CellDataKey.detailText] as? String {
cell?.detailTextLabel?.text = detailText cell?.detailTextLabel?.text = detailText
} }
@ -71,17 +71,17 @@ class BasicStaticTableViewController: UITableViewController, MFMailComposeViewCo
cell?.accessoryType = .disclosureIndicator cell?.accessoryType = .disclosureIndicator
cell?.selectionStyle = .default cell?.selectionStyle = .default
} }
cell?.textLabel?.text = cellData[CellDataKey.title] as? String cell?.textLabel?.text = cellData[CellDataKey.title] as? String
return cell ?? UITableViewCell() return cell ?? UITableViewCell()
} }
override func tableView(_ tableView: UITableView, accessoryButtonTappedForRowWith indexPath: IndexPath) { override func tableView(_ tableView: UITableView, accessoryButtonTappedForRowWith indexPath: IndexPath) {
let cellData = tableData[indexPath.section][indexPath.row] let cellData = tableData[indexPath.section][indexPath.row]
let selector = cellData[CellDataKey.detailDisclosureAction] as? Selector let selector = cellData[CellDataKey.detailDisclosureAction] as? Selector
perform(selector, with: cellData[CellDataKey.detailDisclosureData]) perform(selector, with: cellData[CellDataKey.detailDisclosureData])
} }
override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) { override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
tableView.deselectRow(at: indexPath, animated: true) tableView.deselectRow(at: indexPath, animated: true)
let cellData = tableData[indexPath.section][indexPath.row] let cellData = tableData[indexPath.section][indexPath.row]
@ -116,7 +116,7 @@ class BasicStaticTableViewController: UITableViewController, MFMailComposeViewCo
break break
} }
} }
func sendEmail(toRecipients recipients: [String], subject: String) { func sendEmail(toRecipients recipients: [String], subject: String) {
let mailVC = MFMailComposeViewController() let mailVC = MFMailComposeViewController()
mailVC.mailComposeDelegate = self mailVC.mailComposeDelegate = self
@ -125,7 +125,7 @@ class BasicStaticTableViewController: UITableViewController, MFMailComposeViewCo
mailVC.setMessageBody("", isHTML: false) mailVC.setMessageBody("", isHTML: false)
self.present(mailVC, animated: true, completion: nil) self.present(mailVC, animated: true, completion: nil)
} }
func mailComposeController(_ controller: MFMailComposeViewController, didFinishWith result: MFMailComposeResult, error: Error?) { func mailComposeController(_ controller: MFMailComposeViewController, didFinishWith result: MFMailComposeResult, error: Error?) {
controller.dismiss(animated: true) controller.dismiss(animated: true)
} }

View file

@ -13,7 +13,7 @@ import passKit
class CommitLogsTableViewController: UITableViewController { class CommitLogsTableViewController: UITableViewController {
var commits: [GTCommit] = [] var commits: [GTCommit] = []
let passwordStore = PasswordStore.shared let passwordStore = PasswordStore.shared
override func viewDidLoad() { override func viewDidLoad() {
super.viewDidLoad() super.viewDidLoad()
NotificationCenter.default.addObserver(self, selector: #selector(updateCommitLogs), name: .passwordStoreUpdated, object: nil) NotificationCenter.default.addObserver(self, selector: #selector(updateCommitLogs), name: .passwordStoreUpdated, object: nil)
@ -25,14 +25,14 @@ class CommitLogsTableViewController: UITableViewController {
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return commits.count return commits.count
} }
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "commitLogCell", for: indexPath) let cell = tableView.dequeueReusableCell(withIdentifier: "commitLogCell", for: indexPath)
let formatter = DateFormatter() let formatter = DateFormatter()
formatter.dateStyle = DateFormatter.Style.medium formatter.dateStyle = DateFormatter.Style.medium
formatter.timeStyle = .medium formatter.timeStyle = .medium
let dateString = formatter.string(from: commits[indexPath.row].commitDate) let dateString = formatter.string(from: commits[indexPath.row].commitDate)
let author = cell.contentView.viewWithTag(200) as? UILabel let author = cell.contentView.viewWithTag(200) as? UILabel
let dateLabel = cell.contentView.viewWithTag(201) as? UILabel let dateLabel = cell.contentView.viewWithTag(201) as? UILabel
let messageLabel = cell.contentView.viewWithTag(202) as? UILabel let messageLabel = cell.contentView.viewWithTag(202) as? UILabel
@ -41,12 +41,12 @@ class CommitLogsTableViewController: UITableViewController {
messageLabel?.text = commits[indexPath.row].message?.trimmed messageLabel?.text = commits[indexPath.row].message?.trimmed
return cell return cell
} }
@objc func updateCommitLogs() { @objc func updateCommitLogs() {
commits = getCommitLogs() commits = getCommitLogs()
tableView.reloadData() tableView.reloadData()
} }
private func getCommitLogs() -> [GTCommit] { private func getCommitLogs() -> [GTCommit] {
do { do {
return try passwordStore.getRecentCommits(count: 20) return try passwordStore.getRecentCommits(count: 20)

View file

@ -24,7 +24,7 @@ class EditPasswordTableViewController: PasswordEditorTableViewController {
tableData[1].append([.type: PasswordEditorCellType.memorablePasswordGeneratorCell]) tableData[1].append([.type: PasswordEditorCellType.memorablePasswordGeneratorCell])
super.viewDidLoad() super.viewDidLoad()
} }
override func shouldPerformSegue(withIdentifier identifier: String, sender: Any?) -> Bool { override func shouldPerformSegue(withIdentifier identifier: String, sender: Any?) -> Bool {
if identifier == "saveEditPasswordSegue" { if identifier == "saveEditPasswordSegue" {
// check name // check name
@ -34,7 +34,7 @@ class EditPasswordTableViewController: PasswordEditorTableViewController {
} }
return true return true
} }
override func prepare(for segue: UIStoryboardSegue, sender: Any?) { override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
super.prepare(for: segue, sender: sender) super.prepare(for: segue, sender: sender)
if segue.identifier == "saveEditPasswordSegue" { if segue.identifier == "saveEditPasswordSegue" {

View file

@ -19,7 +19,7 @@ class GeneralSettingsTableViewController: BasicStaticTableViewController {
uiSwitch.addTarget(self, action: #selector(hideUnknownSwitchAction(_:)), for: UIControlEvents.valueChanged) uiSwitch.addTarget(self, action: #selector(hideUnknownSwitchAction(_:)), for: UIControlEvents.valueChanged)
return uiSwitch return uiSwitch
}() }()
let hideOTPSwitch: UISwitch = { let hideOTPSwitch: UISwitch = {
let uiSwitch = UISwitch() let uiSwitch = UISwitch()
uiSwitch.onTintColor = Globals.blue uiSwitch.onTintColor = Globals.blue
@ -27,7 +27,7 @@ class GeneralSettingsTableViewController: BasicStaticTableViewController {
uiSwitch.addTarget(self, action: #selector(hideOTPSwitchAction(_:)), for: UIControlEvents.valueChanged) uiSwitch.addTarget(self, action: #selector(hideOTPSwitchAction(_:)), for: UIControlEvents.valueChanged)
return uiSwitch return uiSwitch
}() }()
let rememberPGPPassphraseSwitch: UISwitch = { let rememberPGPPassphraseSwitch: UISwitch = {
let uiSwitch = UISwitch() let uiSwitch = UISwitch()
uiSwitch.onTintColor = Globals.blue uiSwitch.onTintColor = Globals.blue
@ -36,7 +36,7 @@ class GeneralSettingsTableViewController: BasicStaticTableViewController {
uiSwitch.isOn = SharedDefaults[.isRememberPGPPassphraseOn] uiSwitch.isOn = SharedDefaults[.isRememberPGPPassphraseOn]
return uiSwitch return uiSwitch
}() }()
let rememberGitCredentialPassphraseSwitch: UISwitch = { let rememberGitCredentialPassphraseSwitch: UISwitch = {
let uiSwitch = UISwitch() let uiSwitch = UISwitch()
uiSwitch.onTintColor = Globals.blue uiSwitch.onTintColor = Globals.blue
@ -45,7 +45,7 @@ class GeneralSettingsTableViewController: BasicStaticTableViewController {
uiSwitch.isOn = SharedDefaults[.isRememberGitCredentialPassphraseOn] uiSwitch.isOn = SharedDefaults[.isRememberGitCredentialPassphraseOn]
return uiSwitch return uiSwitch
}() }()
let showFolderSwitch: UISwitch = { let showFolderSwitch: UISwitch = {
let uiSwitch = UISwitch() let uiSwitch = UISwitch()
uiSwitch.onTintColor = Globals.blue uiSwitch.onTintColor = Globals.blue
@ -59,12 +59,12 @@ class GeneralSettingsTableViewController: BasicStaticTableViewController {
tableData = [ tableData = [
// section 0 // section 0
[[.title: "About Repository", .action: "segue", .link: "showAboutRepositorySegue"],], [[.title: "About Repository", .action: "segue", .link: "showAboutRepositorySegue"],],
// section 1 // section 1
[ [
[.title: "Password Generator Flavor", .action: "none", .style: CellDataStyle.value1], [.title: "Password Generator Flavor", .action: "none", .style: CellDataStyle.value1],
], ],
// section 2 // section 2
[ [
[.title: "Remember PGP Key Passphrase", .action: "none",], [.title: "Remember PGP Key Passphrase", .action: "none",],
@ -78,9 +78,9 @@ class GeneralSettingsTableViewController: BasicStaticTableViewController {
] ]
super.viewDidLoad() super.viewDidLoad()
} }
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = super.tableView(tableView, cellForRowAt: indexPath) let cell = super.tableView(tableView, cellForRowAt: indexPath)
switch cell.textLabel!.text! { switch cell.textLabel!.text! {
@ -127,7 +127,7 @@ class GeneralSettingsTableViewController: BasicStaticTableViewController {
} }
return cell return cell
} }
override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) { override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
super.tableView(tableView, didSelectRowAt: indexPath) super.tableView(tableView, didSelectRowAt: indexPath)
let cell = tableView.cellForRow(at: indexPath)! let cell = tableView.cellForRow(at: indexPath)!
@ -136,7 +136,7 @@ class GeneralSettingsTableViewController: BasicStaticTableViewController {
showPasswordGeneratorFlavorActionSheet(sourceCell: cell) showPasswordGeneratorFlavorActionSheet(sourceCell: cell)
} }
} }
func showPasswordGeneratorFlavorActionSheet(sourceCell: UITableViewCell) { func showPasswordGeneratorFlavorActionSheet(sourceCell: UITableViewCell) {
let optionMenu = UIAlertController(title: nil, message: nil, preferredStyle: .actionSheet) let optionMenu = UIAlertController(title: nil, message: nil, preferredStyle: .actionSheet)
var randomFlavorActionTitle = "" var randomFlavorActionTitle = ""
@ -152,12 +152,12 @@ class GeneralSettingsTableViewController: BasicStaticTableViewController {
SharedDefaults[.passwordGeneratorFlavor] = "Random" SharedDefaults[.passwordGeneratorFlavor] = "Random"
sourceCell.detailTextLabel?.text = "Random" sourceCell.detailTextLabel?.text = "Random"
} }
let appleFlavorAction = UIAlertAction(title: appleFlavorActionTitle, style: .default) { _ in let appleFlavorAction = UIAlertAction(title: appleFlavorActionTitle, style: .default) { _ in
SharedDefaults[.passwordGeneratorFlavor] = "Apple" SharedDefaults[.passwordGeneratorFlavor] = "Apple"
sourceCell.detailTextLabel?.text = "Apple" sourceCell.detailTextLabel?.text = "Apple"
} }
let cancelAction = UIAlertAction(title: "Cancel", style: .cancel, handler: nil) let cancelAction = UIAlertAction(title: "Cancel", style: .cancel, handler: nil)
optionMenu.addAction(randomFlavorAction) optionMenu.addAction(randomFlavorAction)
optionMenu.addAction(appleFlavorAction) optionMenu.addAction(appleFlavorAction)
@ -166,37 +166,37 @@ class GeneralSettingsTableViewController: BasicStaticTableViewController {
optionMenu.popoverPresentationController?.sourceRect = sourceCell.bounds optionMenu.popoverPresentationController?.sourceRect = sourceCell.bounds
self.present(optionMenu, animated: true, completion: nil) self.present(optionMenu, animated: true, completion: nil)
} }
@objc func tapHideUnknownSwitchDetailButton(_ sender: Any?) { @objc func tapHideUnknownSwitchDetailButton(_ sender: Any?) {
let alertMessage = "Only \"key: value\" format in additional fields is supported. Unsupported fields will be given \"unknown\" keys. Turn on this switch to hide unsupported fields." let alertMessage = "Only \"key: value\" format in additional fields is supported. Unsupported fields will be given \"unknown\" keys. Turn on this switch to hide unsupported fields."
let alertTitle = "Hide Unknown Fields" let alertTitle = "Hide Unknown Fields"
Utils.alert(title: alertTitle, message: alertMessage, controller: self, completion: nil) Utils.alert(title: alertTitle, message: alertMessage, controller: self, completion: nil)
} }
@objc func tapHideOTPSwitchDetailButton(_ sender: Any?) { @objc func tapHideOTPSwitchDetailButton(_ sender: Any?) {
let keywordsString = Constants.OTP_KEYWORDS.joined(separator: ",") let keywordsString = Constants.OTP_KEYWORDS.joined(separator: ",")
let alertMessage = "Turn on this switch to hide the fields related to one time passwords (i.e., \(keywordsString))." let alertMessage = "Turn on this switch to hide the fields related to one time passwords (i.e., \(keywordsString))."
let alertTitle = "Hide One Time Password Fields" let alertTitle = "Hide One Time Password Fields"
Utils.alert(title: alertTitle, message: alertMessage, controller: self, completion: nil) Utils.alert(title: alertTitle, message: alertMessage, controller: self, completion: nil)
} }
@objc func hideUnknownSwitchAction(_ sender: Any?) { @objc func hideUnknownSwitchAction(_ sender: Any?) {
SharedDefaults[.isHideUnknownOn] = hideUnknownSwitch.isOn SharedDefaults[.isHideUnknownOn] = hideUnknownSwitch.isOn
NotificationCenter.default.post(name: .passwordDetailDisplaySettingChanged, object: nil) NotificationCenter.default.post(name: .passwordDetailDisplaySettingChanged, object: nil)
} }
@objc func hideOTPSwitchAction(_ sender: Any?) { @objc func hideOTPSwitchAction(_ sender: Any?) {
SharedDefaults[.isHideOTPOn] = hideOTPSwitch.isOn SharedDefaults[.isHideOTPOn] = hideOTPSwitch.isOn
NotificationCenter.default.post(name: .passwordDetailDisplaySettingChanged, object: nil) NotificationCenter.default.post(name: .passwordDetailDisplaySettingChanged, object: nil)
} }
@objc func rememberPGPPassphraseSwitchAction(_ sender: Any?) { @objc func rememberPGPPassphraseSwitchAction(_ sender: Any?) {
SharedDefaults[.isRememberPGPPassphraseOn] = rememberPGPPassphraseSwitch.isOn SharedDefaults[.isRememberPGPPassphraseOn] = rememberPGPPassphraseSwitch.isOn
if rememberPGPPassphraseSwitch.isOn == false { if rememberPGPPassphraseSwitch.isOn == false {
passwordStore.pgpKeyPassphrase = nil passwordStore.pgpKeyPassphrase = nil
} }
} }
@objc func rememberGitCredentialPassphraseSwitchAction(_ sender: Any?) { @objc func rememberGitCredentialPassphraseSwitchAction(_ sender: Any?) {
SharedDefaults[.isRememberGitCredentialPassphraseOn] = rememberGitCredentialPassphraseSwitch.isOn SharedDefaults[.isRememberGitCredentialPassphraseOn] = rememberGitCredentialPassphraseSwitch.isOn
if rememberGitCredentialPassphraseSwitch.isOn == false { if rememberGitCredentialPassphraseSwitch.isOn == false {
@ -204,10 +204,10 @@ class GeneralSettingsTableViewController: BasicStaticTableViewController {
passwordStore.gitPassword = nil passwordStore.gitPassword = nil
} }
} }
@objc func showFolderSwitchAction(_ sender: Any?) { @objc func showFolderSwitchAction(_ sender: Any?) {
SharedDefaults[.isShowFolderOn] = showFolderSwitch.isOn SharedDefaults[.isShowFolderOn] = showFolderSwitch.isOn
NotificationCenter.default.post(name: .passwordDisplaySettingChanged, object: nil) NotificationCenter.default.post(name: .passwordDisplaySettingChanged, object: nil)
} }
} }

View file

@ -12,21 +12,21 @@ import passKit
class GitConfigSettingTableViewController: UITableViewController { class GitConfigSettingTableViewController: UITableViewController {
let passwordStore = PasswordStore.shared let passwordStore = PasswordStore.shared
@IBOutlet weak var nameTextField: UITextField! @IBOutlet weak var nameTextField: UITextField!
@IBOutlet weak var emailTextField: UITextField! @IBOutlet weak var emailTextField: UITextField!
override func viewDidLoad() { override func viewDidLoad() {
super.viewDidLoad() super.viewDidLoad()
tableView.rowHeight = UITableViewAutomaticDimension tableView.rowHeight = UITableViewAutomaticDimension
let signature = passwordStore.gitSignatureForNow let signature = passwordStore.gitSignatureForNow
nameTextField.placeholder = signature.name nameTextField.placeholder = signature.name
emailTextField.placeholder = signature.email emailTextField.placeholder = signature.email
nameTextField.text = SharedDefaults[.gitSignatureName] nameTextField.text = SharedDefaults[.gitSignatureName]
emailTextField.text = SharedDefaults[.gitSignatureEmail] emailTextField.text = SharedDefaults[.gitSignatureEmail]
} }
override func shouldPerformSegue(withIdentifier identifier: String, sender: Any?) -> Bool { override func shouldPerformSegue(withIdentifier identifier: String, sender: Any?) -> Bool {
if identifier == "saveGitConfigSettingSegue" { if identifier == "saveGitConfigSettingSegue" {
let name = nameTextField.text!.isEmpty ? Globals.gitSignatureDefaultName : nameTextField.text! let name = nameTextField.text!.isEmpty ? Globals.gitSignatureDefaultName : nameTextField.text!

View file

@ -12,10 +12,10 @@ import passKit
class GitSSHKeyArmorSettingTableViewController: UITableViewController, UITextViewDelegate, QRScannerControllerDelegate { class GitSSHKeyArmorSettingTableViewController: UITableViewController, UITextViewDelegate, QRScannerControllerDelegate {
@IBOutlet weak var armorPrivateKeyTextView: UITextView! @IBOutlet weak var armorPrivateKeyTextView: UITextView!
@IBOutlet weak var scanPrivateKeyCell: UITableViewCell! @IBOutlet weak var scanPrivateKeyCell: UITableViewCell!
var gitSSHPrivateKeyPassphrase: String? var gitSSHPrivateKeyPassphrase: String?
let passwordStore = PasswordStore.shared let passwordStore = PasswordStore.shared
class ScannedSSHKey { class ScannedSSHKey {
static let maxNumberOfGif = 100 static let maxNumberOfGif = 100
var numberOfSegments = 0 var numberOfSegments = 0
@ -24,7 +24,7 @@ class GitSSHKeyArmorSettingTableViewController: UITableViewController, UITextVie
var message = "" var message = ""
var hasStarted = false var hasStarted = false
var isDone = false var isDone = false
func reset() { func reset() {
numberOfSegments = 0 numberOfSegments = 0
previousSegment = "" previousSegment = ""
@ -33,14 +33,14 @@ class GitSSHKeyArmorSettingTableViewController: UITableViewController, UITextVie
hasStarted = false hasStarted = false
isDone = false isDone = false
} }
func addSegment(segment: String) { func addSegment(segment: String) {
// skip duplicated segments // skip duplicated segments
guard segment != previousSegment else { guard segment != previousSegment else {
return return
} }
previousSegment = segment previousSegment = segment
// check whether we have found the first block // check whether we have found the first block
if hasStarted == false { if hasStarted == false {
hasStarted = segment.contains("-----BEGIN") hasStarted = segment.contains("-----BEGIN")
@ -48,38 +48,38 @@ class GitSSHKeyArmorSettingTableViewController: UITableViewController, UITextVie
guard hasStarted == true else { guard hasStarted == true else {
return return
} }
// check the number of segments // check the number of segments
numberOfSegments = numberOfSegments + 1 numberOfSegments = numberOfSegments + 1
guard numberOfSegments <= ScannedSSHKey.maxNumberOfGif else { guard numberOfSegments <= ScannedSSHKey.maxNumberOfGif else {
key = "Too many QR codes" key = "Too many QR codes"
return return
} }
// update full text and check whether we are done // update full text and check whether we are done
key.append(segment) key.append(segment)
if let index1 = key.range(of: "-----END")?.lowerBound, if let index1 = key.range(of: "-----END")?.lowerBound,
let _ = key.suffix(from: index1).range(of: "KEY-----")?.lowerBound { let _ = key.suffix(from: index1).range(of: "KEY-----")?.lowerBound {
isDone = true isDone = true
} }
// update message // update message
message = "\(numberOfSegments) scanned QR codes." message = "\(numberOfSegments) scanned QR codes."
} }
} }
var scanned = ScannedSSHKey() var scanned = ScannedSSHKey()
override func viewDidLoad() { override func viewDidLoad() {
super.viewDidLoad() super.viewDidLoad()
armorPrivateKeyTextView.text = SharedDefaults[.gitSSHPrivateKeyArmor] armorPrivateKeyTextView.text = SharedDefaults[.gitSSHPrivateKeyArmor]
armorPrivateKeyTextView.delegate = self armorPrivateKeyTextView.delegate = self
scanPrivateKeyCell?.textLabel?.text = "Scan Private Key QR Codes" scanPrivateKeyCell?.textLabel?.text = "Scan Private Key QR Codes"
scanPrivateKeyCell?.textLabel?.textColor = Globals.blue scanPrivateKeyCell?.textLabel?.textColor = Globals.blue
scanPrivateKeyCell?.selectionStyle = .default scanPrivateKeyCell?.selectionStyle = .default
scanPrivateKeyCell?.accessoryType = .disclosureIndicator scanPrivateKeyCell?.accessoryType = .disclosureIndicator
} }
@IBAction func doneButtonTapped(_ sender: Any) { @IBAction func doneButtonTapped(_ sender: Any) {
SharedDefaults[.gitSSHPrivateKeyArmor] = armorPrivateKeyTextView.text SharedDefaults[.gitSSHPrivateKeyArmor] = armorPrivateKeyTextView.text
do { do {
@ -90,7 +90,7 @@ class GitSSHKeyArmorSettingTableViewController: UITableViewController, UITextVie
SharedDefaults[.gitSSHKeySource] = "armor" SharedDefaults[.gitSSHKeySource] = "armor"
self.navigationController!.popViewController(animated: true) self.navigationController!.popViewController(animated: true)
} }
func textView(_ textView: UITextView, shouldChangeTextIn range: NSRange, replacementText text: String) -> Bool { func textView(_ textView: UITextView, shouldChangeTextIn range: NSRange, replacementText text: String) -> Bool {
if text == UIPasteboard.general.string { if text == UIPasteboard.general.string {
// user pastes something, do the copy here again and clear the pasteboard in 45s // user pastes something, do the copy here again and clear the pasteboard in 45s
@ -98,7 +98,7 @@ class GitSSHKeyArmorSettingTableViewController: UITableViewController, UITextVie
} }
return true return true
} }
override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) { override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
let selectedCell = tableView.cellForRow(at: indexPath) let selectedCell = tableView.cellForRow(at: indexPath)
if selectedCell == scanPrivateKeyCell { if selectedCell == scanPrivateKeyCell {
@ -107,8 +107,8 @@ class GitSSHKeyArmorSettingTableViewController: UITableViewController, UITextVie
} }
tableView.deselectRow(at: indexPath, animated: true) tableView.deselectRow(at: indexPath, animated: true)
} }
// MARK: - QRScannerControllerDelegate Methods // MARK: - QRScannerControllerDelegate Methods
func checkScannedOutput(line: String) -> (accept: Bool, message: String) { func checkScannedOutput(line: String) -> (accept: Bool, message: String) {
scanned.addSegment(segment: line) scanned.addSegment(segment: line)
@ -118,12 +118,12 @@ class GitSSHKeyArmorSettingTableViewController: UITableViewController, UITextVie
return (accept: false, message: scanned.message) return (accept: false, message: scanned.message)
} }
} }
// MARK: - QRScannerControllerDelegate Methods // MARK: - QRScannerControllerDelegate Methods
func handleScannedOutput(line: String) { func handleScannedOutput(line: String) {
armorPrivateKeyTextView.text = scanned.key armorPrivateKeyTextView.text = scanned.key
} }
override func prepare(for segue: UIStoryboardSegue, sender: Any?) { override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == "showSSHScannerSegue" { if segue.identifier == "showSSHScannerSegue" {
if let navController = segue.destination as? UINavigationController { if let navController = segue.destination as? UINavigationController {
@ -135,9 +135,9 @@ class GitSSHKeyArmorSettingTableViewController: UITableViewController, UITextVie
} }
} }
} }
@IBAction private func cancelSSHScanner(segue: UIStoryboardSegue) { @IBAction private func cancelSSHScanner(segue: UIStoryboardSegue) {
} }
} }

View file

@ -37,7 +37,7 @@ class GitServerSettingTableViewController: UITableViewController {
sshKeyCheckView.isHidden = true sshKeyCheckView.isHidden = true
} }
} }
override func viewWillAppear(_ animated: Bool) { override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated) super.viewWillAppear(animated)
// Grey out ssh option if ssh_key is not present // Grey out ssh option if ssh_key is not present
@ -55,30 +55,30 @@ class GitServerSettingTableViewController: UITableViewController {
checkAuthenticationMethod(method: authenticationMethod) checkAuthenticationMethod(method: authenticationMethod)
authSSHKeyCell.accessoryType = .detailButton authSSHKeyCell.accessoryType = .detailButton
} }
override func tableView(_ tableView: UITableView, accessoryButtonTappedForRowWith indexPath: IndexPath) { override func tableView(_ tableView: UITableView, accessoryButtonTappedForRowWith indexPath: IndexPath) {
let cell = tableView.cellForRow(at: indexPath) let cell = tableView.cellForRow(at: indexPath)
if cell == authSSHKeyCell { if cell == authSSHKeyCell {
showSSHKeyActionSheet() showSSHKeyActionSheet()
} }
} }
override func viewDidAppear(_ animated: Bool) { override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated) super.viewDidAppear(animated)
} }
override func viewWillDisappear(_ animated: Bool) { override func viewWillDisappear(_ animated: Bool) {
super.viewWillDisappear(animated) super.viewWillDisappear(animated)
view.endEditing(true) view.endEditing(true)
} }
private func cloneAndSegueIfSuccess() { private func cloneAndSegueIfSuccess() {
// try to clone // try to clone
let gitRepostiroyURL = gitURLTextField.text!.trimmed let gitRepostiroyURL = gitURLTextField.text!.trimmed
let username = usernameTextField.text! let username = usernameTextField.text!
let auth = authenticationMethod let auth = authenticationMethod
SVProgressHUD.setDefaultMaskType(.black) SVProgressHUD.setDefaultMaskType(.black)
SVProgressHUD.setDefaultStyle(.light) SVProgressHUD.setDefaultStyle(.light)
SVProgressHUD.show(withStatus: "Prepare Repository") SVProgressHUD.show(withStatus: "Prepare Repository")
@ -160,15 +160,15 @@ class GitServerSettingTableViewController: UITableViewController {
checkAuthenticationMethod(method: authenticationMethod) checkAuthenticationMethod(method: authenticationMethod)
tableView.deselectRow(at: indexPath, animated: true) tableView.deselectRow(at: indexPath, animated: true)
} }
@IBAction func save(_ sender: Any) { @IBAction func save(_ sender: Any) {
// some sanity checks // some sanity checks
guard let gitURL = URL(string: gitURLTextField.text!) else { guard let gitURL = URL(string: gitURLTextField.text!) else {
Utils.alert(title: "Cannot Save", message: "Please set the Git repository URL.", controller: self, completion: nil) Utils.alert(title: "Cannot Save", message: "Please set the Git repository URL.", controller: self, completion: nil)
return return
} }
switch gitURL.scheme { switch gitURL.scheme {
case let val where val == "https": case let val where val == "https":
break break
@ -188,7 +188,7 @@ class GitServerSettingTableViewController: UITableViewController {
Utils.alert(title: "Cannot Save", message: "Please specify the scheme of the Git repository URL (https or ssh).", controller: self, completion: nil) Utils.alert(title: "Cannot Save", message: "Please specify the scheme of the Git repository URL (https or ssh).", controller: self, completion: nil)
return return
} }
if passwordStore.repositoryExisted() { if passwordStore.repositoryExisted() {
let alert = UIAlertController(title: "Overwrite?", message: "This operation will overwrite your current password store data (repository). Data on your remote server will not be affected.", preferredStyle: UIAlertControllerStyle.alert) let alert = UIAlertController(title: "Overwrite?", message: "This operation will overwrite your current password store data (repository). Data on your remote server will not be affected.", preferredStyle: UIAlertControllerStyle.alert)
alert.addAction(UIAlertAction(title: "Overwrite", style: UIAlertActionStyle.destructive, handler: { _ in alert.addAction(UIAlertAction(title: "Overwrite", style: UIAlertActionStyle.destructive, handler: { _ in
@ -202,13 +202,13 @@ class GitServerSettingTableViewController: UITableViewController {
cloneAndSegueIfSuccess() cloneAndSegueIfSuccess()
} }
} }
func showSSHKeyActionSheet() { func showSSHKeyActionSheet() {
let optionMenu = UIAlertController(title: nil, message: nil, preferredStyle: .actionSheet) let optionMenu = UIAlertController(title: nil, message: nil, preferredStyle: .actionSheet)
var urlActionTitle = "Download from URL" var urlActionTitle = "Download from URL"
var armorActionTitle = "ASCII-Armor Encrypted Key" var armorActionTitle = "ASCII-Armor Encrypted Key"
var fileActionTitle = "iTunes File Sharing" var fileActionTitle = "iTunes File Sharing"
if SharedDefaults[.gitSSHKeySource] == "url" { if SharedDefaults[.gitSSHKeySource] == "url" {
urlActionTitle = "\(urlActionTitle)" urlActionTitle = "\(urlActionTitle)"
} else if SharedDefaults[.gitSSHKeySource] == "armor" { } else if SharedDefaults[.gitSSHKeySource] == "armor" {
@ -225,7 +225,7 @@ class GitServerSettingTableViewController: UITableViewController {
let cancelAction = UIAlertAction(title: "Cancel", style: .cancel, handler: nil) let cancelAction = UIAlertAction(title: "Cancel", style: .cancel, handler: nil)
optionMenu.addAction(urlAction) optionMenu.addAction(urlAction)
optionMenu.addAction(armorAction) optionMenu.addAction(armorAction)
if passwordStore.gitSSHKeyExists(inFileSharing: true) { if passwordStore.gitSSHKeyExists(inFileSharing: true) {
// might keys updated via iTunes, or downloaded/pasted inside the app // might keys updated via iTunes, or downloaded/pasted inside the app
fileActionTitle.append(" (Import)") fileActionTitle.append(" (Import)")
@ -249,7 +249,7 @@ class GitServerSettingTableViewController: UITableViewController {
} }
optionMenu.addAction(fileAction) optionMenu.addAction(fileAction)
} }
if SharedDefaults[.gitSSHKeySource] != nil { if SharedDefaults[.gitSSHKeySource] != nil {
let deleteAction = UIAlertAction(title: "Remove Git SSH Keys", style: .destructive) { _ in let deleteAction = UIAlertAction(title: "Remove Git SSH Keys", style: .destructive) { _ in
self.passwordStore.removeGitSSHKeys() self.passwordStore.removeGitSSHKeys()
@ -266,7 +266,7 @@ class GitServerSettingTableViewController: UITableViewController {
optionMenu.popoverPresentationController?.sourceRect = authSSHKeyCell.bounds optionMenu.popoverPresentationController?.sourceRect = authSSHKeyCell.bounds
self.present(optionMenu, animated: true, completion: nil) self.present(optionMenu, animated: true, completion: nil)
} }
private func requestGitPassword(credential: GitCredential.Credential, lastPassword: String?) -> String? { private func requestGitPassword(credential: GitCredential.Credential, lastPassword: String?) -> String? {
let sem = DispatchSemaphore(value: 0) let sem = DispatchSemaphore(value: 0)
var password: String? var password: String?
@ -277,7 +277,7 @@ class GitServerSettingTableViewController: UITableViewController {
case .ssh: case .ssh:
message = "Please fill in the passphrase of your SSH key." message = "Please fill in the passphrase of your SSH key."
} }
DispatchQueue.main.async { DispatchQueue.main.async {
SVProgressHUD.dismiss() SVProgressHUD.dismiss()
let alert = UIAlertController(title: "Password", message: message, preferredStyle: UIAlertControllerStyle.alert) let alert = UIAlertController(title: "Password", message: message, preferredStyle: UIAlertControllerStyle.alert)
@ -295,7 +295,7 @@ class GitServerSettingTableViewController: UITableViewController {
}) })
self.present(alert, animated: true, completion: nil) self.present(alert, animated: true, completion: nil)
} }
let _ = sem.wait(timeout: .distantFuture) let _ = sem.wait(timeout: .distantFuture)
return password return password
} }

View file

@ -33,7 +33,7 @@ class OpenSourceComponentsTableViewController: BasicStaticTableViewController {
"https://github.com/SVProgressHUD/SVProgressHUD", "https://github.com/SVProgressHUD/SVProgressHUD",
"https://github.com/SVProgressHUD/SVProgressHUD/blob/master/LICENSE.txt"], "https://github.com/SVProgressHUD/SVProgressHUD/blob/master/LICENSE.txt"],
] ]
override func viewDidLoad() { override func viewDidLoad() {
tableData.append([]) tableData.append([])
for item in openSourceComponents { for item in openSourceComponents {
@ -43,7 +43,7 @@ class OpenSourceComponentsTableViewController: BasicStaticTableViewController {
} }
super.viewDidLoad() super.viewDidLoad()
} }
@objc func actOnDetailDisclosureButton(_ sender: Any?) { @objc func actOnDetailDisclosureButton(_ sender: Any?) {
if let link = sender as? String { if let link = sender as? String {
let svc = SFSafariViewController(url: URL(string: link)!, entersReaderIfAvailable: false) let svc = SFSafariViewController(url: URL(string: link)!, entersReaderIfAvailable: false)

View file

@ -14,10 +14,10 @@ class PGPKeyArmorSettingTableViewController: UITableViewController, UITextViewDe
@IBOutlet weak var armorPrivateKeyTextView: UITextView! @IBOutlet weak var armorPrivateKeyTextView: UITextView!
@IBOutlet weak var scanPublicKeyCell: UITableViewCell! @IBOutlet weak var scanPublicKeyCell: UITableViewCell!
@IBOutlet weak var scanPrivateKeyCell: UITableViewCell! @IBOutlet weak var scanPrivateKeyCell: UITableViewCell!
var pgpPassphrase: String? var pgpPassphrase: String?
let passwordStore = PasswordStore.shared let passwordStore = PasswordStore.shared
class ScannedPGPKey { class ScannedPGPKey {
static let maxNumberOfGif = 100 static let maxNumberOfGif = 100
enum KeyType { enum KeyType {
@ -30,7 +30,7 @@ class PGPKeyArmorSettingTableViewController: UITableViewController, UITextViewDe
var message = "" var message = ""
var hasStarted = false var hasStarted = false
var isDone = false var isDone = false
func reset(keytype: KeyType) { func reset(keytype: KeyType) {
self.keyType = keytype self.keyType = keytype
numberOfSegments = 0 numberOfSegments = 0
@ -40,14 +40,14 @@ class PGPKeyArmorSettingTableViewController: UITableViewController, UITextViewDe
hasStarted = false hasStarted = false
isDone = false isDone = false
} }
func addSegment(segment: String) { func addSegment(segment: String) {
// skip duplicated segments // skip duplicated segments
guard segment != previousSegment else { guard segment != previousSegment else {
return return
} }
previousSegment = segment previousSegment = segment
// check whether we have found the first block // check whether we have found the first block
if hasStarted == false { if hasStarted == false {
let findPublic = segment.contains("-----BEGIN PGP PUBLIC KEY BLOCK-----") let findPublic = segment.contains("-----BEGIN PGP PUBLIC KEY BLOCK-----")
@ -68,32 +68,32 @@ class PGPKeyArmorSettingTableViewController: UITableViewController, UITextViewDe
guard hasStarted == true else { guard hasStarted == true else {
return return
} }
// check the number of segments // check the number of segments
numberOfSegments = numberOfSegments + 1 numberOfSegments = numberOfSegments + 1
guard numberOfSegments <= ScannedPGPKey.maxNumberOfGif else { guard numberOfSegments <= ScannedPGPKey.maxNumberOfGif else {
key = "Too many QR codes" key = "Too many QR codes"
return return
} }
// update full text and check whether we are done // update full text and check whether we are done
key.append(segment) key.append(segment)
if key.contains("-----END PGP PUBLIC KEY BLOCK-----") || key.contains("-----END PGP PRIVATE KEY BLOCK-----") { if key.contains("-----END PGP PUBLIC KEY BLOCK-----") || key.contains("-----END PGP PRIVATE KEY BLOCK-----") {
isDone = true isDone = true
} }
// update message // update message
message = "\(numberOfSegments) scanned QR codes." message = "\(numberOfSegments) scanned QR codes."
} }
} }
var scanned = ScannedPGPKey() var scanned = ScannedPGPKey()
override func viewDidLoad() { override func viewDidLoad() {
super.viewDidLoad() super.viewDidLoad()
armorPublicKeyTextView.text = SharedDefaults[.pgpPublicKeyArmor] armorPublicKeyTextView.text = SharedDefaults[.pgpPublicKeyArmor]
armorPrivateKeyTextView.text = SharedDefaults[.pgpPrivateKeyArmor] armorPrivateKeyTextView.text = SharedDefaults[.pgpPrivateKeyArmor]
pgpPassphrase = passwordStore.pgpKeyPassphrase pgpPassphrase = passwordStore.pgpKeyPassphrase
scanPublicKeyCell?.textLabel?.text = "Scan Public Key QR Codes" scanPublicKeyCell?.textLabel?.text = "Scan Public Key QR Codes"
scanPublicKeyCell?.textLabel?.textColor = Globals.blue scanPublicKeyCell?.textLabel?.textColor = Globals.blue
scanPublicKeyCell?.selectionStyle = .default scanPublicKeyCell?.selectionStyle = .default
@ -104,7 +104,7 @@ class PGPKeyArmorSettingTableViewController: UITableViewController, UITextViewDe
scanPrivateKeyCell?.selectionStyle = .default scanPrivateKeyCell?.selectionStyle = .default
scanPrivateKeyCell?.accessoryType = .disclosureIndicator scanPrivateKeyCell?.accessoryType = .disclosureIndicator
} }
@IBAction func save(_ sender: Any) { @IBAction func save(_ sender: Any) {
guard armorPublicKeyTextView.text.isEmpty == false else { guard armorPublicKeyTextView.text.isEmpty == false else {
Utils.alert(title: "Cannot Save", message: "Please set public key first.", controller: self, completion: nil) Utils.alert(title: "Cannot Save", message: "Please set public key first.", controller: self, completion: nil)
@ -138,7 +138,7 @@ class PGPKeyArmorSettingTableViewController: UITableViewController, UITextViewDe
}) })
self.present(savePassphraseAlert, animated: true, completion: nil) self.present(savePassphraseAlert, animated: true, completion: nil)
} }
func textView(_ textView: UITextView, shouldChangeTextIn range: NSRange, replacementText text: String) -> Bool { func textView(_ textView: UITextView, shouldChangeTextIn range: NSRange, replacementText text: String) -> Bool {
if text == UIPasteboard.general.string { if text == UIPasteboard.general.string {
// user pastes something, do the copy here again and clear the pasteboard in 45s // user pastes something, do the copy here again and clear the pasteboard in 45s
@ -146,7 +146,7 @@ class PGPKeyArmorSettingTableViewController: UITableViewController, UITextViewDe
} }
return true return true
} }
override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) { override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
let selectedCell = tableView.cellForRow(at: indexPath) let selectedCell = tableView.cellForRow(at: indexPath)
if selectedCell == scanPublicKeyCell { if selectedCell == scanPublicKeyCell {
@ -158,7 +158,7 @@ class PGPKeyArmorSettingTableViewController: UITableViewController, UITextViewDe
} }
tableView.deselectRow(at: indexPath, animated: true) tableView.deselectRow(at: indexPath, animated: true)
} }
// MARK: - QRScannerControllerDelegate Methods // MARK: - QRScannerControllerDelegate Methods
func checkScannedOutput(line: String) -> (accept: Bool, message: String) { func checkScannedOutput(line: String) -> (accept: Bool, message: String) {
scanned.addSegment(segment: line) scanned.addSegment(segment: line)
@ -168,7 +168,7 @@ class PGPKeyArmorSettingTableViewController: UITableViewController, UITextViewDe
return (accept: false, message: scanned.message) return (accept: false, message: scanned.message)
} }
} }
// MARK: - QRScannerControllerDelegate Methods // MARK: - QRScannerControllerDelegate Methods
func handleScannedOutput(line: String) { func handleScannedOutput(line: String) {
switch scanned.keyType { switch scanned.keyType {
@ -178,7 +178,7 @@ class PGPKeyArmorSettingTableViewController: UITableViewController, UITextViewDe
armorPrivateKeyTextView.text = scanned.key armorPrivateKeyTextView.text = scanned.key
} }
} }
override func prepare(for segue: UIStoryboardSegue, sender: Any?) { override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == "showPGPScannerSegue" { if segue.identifier == "showPGPScannerSegue" {
if let navController = segue.destination as? UINavigationController { if let navController = segue.destination as? UINavigationController {

View file

@ -15,7 +15,7 @@ class PGPKeySettingTableViewController: UITableViewController {
@IBOutlet weak var pgpPrivateKeyURLTextField: UITextField! @IBOutlet weak var pgpPrivateKeyURLTextField: UITextField!
var pgpPassphrase: String? var pgpPassphrase: String?
let passwordStore = PasswordStore.shared let passwordStore = PasswordStore.shared
override func viewDidLoad() { override func viewDidLoad() {
super.viewDidLoad() super.viewDidLoad()
tableView.rowHeight = UITableViewAutomaticDimension tableView.rowHeight = UITableViewAutomaticDimension
@ -23,7 +23,7 @@ class PGPKeySettingTableViewController: UITableViewController {
pgpPrivateKeyURLTextField.text = SharedDefaults[.pgpPrivateKeyURL]?.absoluteString pgpPrivateKeyURLTextField.text = SharedDefaults[.pgpPrivateKeyURL]?.absoluteString
pgpPassphrase = passwordStore.pgpKeyPassphrase pgpPassphrase = passwordStore.pgpKeyPassphrase
} }
private func validatePGPKeyURL(input: String?) -> Bool { private func validatePGPKeyURL(input: String?) -> Bool {
guard let path = input, let url = URL(string: path) else { guard let path = input, let url = URL(string: path) else {
Utils.alert(title: "Cannot Save PGP Key", message: "Please set PGP Key URL first.", controller: self, completion: nil) Utils.alert(title: "Cannot Save PGP Key", message: "Please set PGP Key URL first.", controller: self, completion: nil)
@ -35,7 +35,7 @@ class PGPKeySettingTableViewController: UITableViewController {
} }
return true return true
} }
@IBAction func save(_ sender: Any) { @IBAction func save(_ sender: Any) {
guard validatePGPKeyURL(input: pgpPublicKeyURLTextField.text) == true, guard validatePGPKeyURL(input: pgpPublicKeyURLTextField.text) == true,
validatePGPKeyURL(input: pgpPrivateKeyURLTextField.text) == true else { validatePGPKeyURL(input: pgpPrivateKeyURLTextField.text) == true else {
@ -65,6 +65,6 @@ class PGPKeySettingTableViewController: UITableViewController {
}) })
self.present(savePassphraseAlert, animated: true, completion: nil) self.present(savePassphraseAlert, animated: true, completion: nil)
} }
} }

View file

@ -18,7 +18,7 @@ class PasswordDetailTableViewController: UITableViewController, UIGestureRecogni
private var oneTimePasswordIndexPath : IndexPath? private var oneTimePasswordIndexPath : IndexPath?
private var shouldPopCurrentView = false private var shouldPopCurrentView = false
private let passwordStore = PasswordStore.shared private let passwordStore = PasswordStore.shared
private lazy var editUIBarButtonItem: UIBarButtonItem = { private lazy var editUIBarButtonItem: UIBarButtonItem = {
let uiBarButtonItem = UIBarButtonItem(barButtonSystemItem: .edit, target: self, action: #selector(pressEdit(_:))) let uiBarButtonItem = UIBarButtonItem(barButtonSystemItem: .edit, target: self, action: #selector(pressEdit(_:)))
return uiBarButtonItem return uiBarButtonItem
@ -31,18 +31,18 @@ class PasswordDetailTableViewController: UITableViewController, UIGestureRecogni
title = "" title = ""
content = "" content = ""
} }
init(title: String) { init(title: String) {
self.title = title self.title = title
self.content = "" self.content = ""
} }
init(title: String, content: String) { init(title: String, content: String) {
self.title = title self.title = title
self.content = content self.content = content
} }
} }
private struct TableSection { private struct TableSection {
var type: PasswordDetailTableViewControllerSectionType var type: PasswordDetailTableViewControllerSectionType
var header: String? var header: String?
@ -52,56 +52,56 @@ class PasswordDetailTableViewController: UITableViewController, UIGestureRecogni
header = nil header = nil
item = [TableCell]() item = [TableCell]()
} }
init(type: PasswordDetailTableViewControllerSectionType, header: String) { init(type: PasswordDetailTableViewControllerSectionType, header: String) {
self.init(type: type) self.init(type: type)
self.header = header self.header = header
} }
} }
private var tableData = Array<TableSection>() private var tableData = Array<TableSection>()
private enum PasswordDetailTableViewControllerSectionType { private enum PasswordDetailTableViewControllerSectionType {
case name, main, addition, misc case name, main, addition, misc
} }
override func viewDidLoad() { override func viewDidLoad() {
super.viewDidLoad() super.viewDidLoad()
tableView.register(UINib(nibName: "LabelTableViewCell", bundle: nil), forCellReuseIdentifier: "labelCell") tableView.register(UINib(nibName: "LabelTableViewCell", bundle: nil), forCellReuseIdentifier: "labelCell")
tableView.register(UINib(nibName: "PasswordDetailTitleTableViewCell", bundle: nil), forCellReuseIdentifier: "passwordDetailTitleTableViewCell") tableView.register(UINib(nibName: "PasswordDetailTitleTableViewCell", bundle: nil), forCellReuseIdentifier: "passwordDetailTitleTableViewCell")
let tapGesture = UITapGestureRecognizer(target: self, action: #selector(PasswordDetailTableViewController.tapMenu(recognizer:))) let tapGesture = UITapGestureRecognizer(target: self, action: #selector(PasswordDetailTableViewController.tapMenu(recognizer:)))
tapGesture.cancelsTouchesInView = false tapGesture.cancelsTouchesInView = false
tableView.addGestureRecognizer(tapGesture) tableView.addGestureRecognizer(tapGesture)
tapGesture.delegate = self tapGesture.delegate = self
tableView.contentInset = UIEdgeInsetsMake(-36, 0, 44, 0); tableView.contentInset = UIEdgeInsetsMake(-36, 0, 44, 0);
tableView.rowHeight = UITableViewAutomaticDimension tableView.rowHeight = UITableViewAutomaticDimension
tableView.estimatedRowHeight = 52 tableView.estimatedRowHeight = 52
editUIBarButtonItem.isEnabled = false editUIBarButtonItem.isEnabled = false
navigationItem.rightBarButtonItem = editUIBarButtonItem navigationItem.rightBarButtonItem = editUIBarButtonItem
if #available(iOS 11.0, *) { if #available(iOS 11.0, *) {
navigationItem.largeTitleDisplayMode = .never navigationItem.largeTitleDisplayMode = .never
} }
if let imageData = passwordEntity?.getImage() { if let imageData = passwordEntity?.getImage() {
let image = UIImage(data: imageData as Data) let image = UIImage(data: imageData as Data)
passwordImage = image passwordImage = image
} }
self.decryptThenShowPassword() self.decryptThenShowPassword()
self.setupOneTimePasswordAutoRefresh() self.setupOneTimePasswordAutoRefresh()
// pop the current view because this password might be "discarded" // pop the current view because this password might be "discarded"
NotificationCenter.default.addObserver(self, selector: #selector(setShouldPopCurrentView), name: .passwordStoreChangeDiscarded, object: nil) NotificationCenter.default.addObserver(self, selector: #selector(setShouldPopCurrentView), name: .passwordStoreChangeDiscarded, object: nil)
// reset the data table if some password (maybe another one) has been updated // reset the data table if some password (maybe another one) has been updated
NotificationCenter.default.addObserver(self, selector: #selector(decryptThenShowPassword), name: .passwordStoreUpdated, object: nil) NotificationCenter.default.addObserver(self, selector: #selector(decryptThenShowPassword), name: .passwordStoreUpdated, object: nil)
// reset the data table if the disaply settings have been changed // reset the data table if the disaply settings have been changed
NotificationCenter.default.addObserver(self, selector: #selector(decryptThenShowPassword), name: .passwordDetailDisplaySettingChanged, object: nil) NotificationCenter.default.addObserver(self, selector: #selector(decryptThenShowPassword), name: .passwordDetailDisplaySettingChanged, object: nil)
} }
override func viewDidAppear(_ animated: Bool) { override func viewDidAppear(_ animated: Bool) {
super.viewWillAppear(animated) super.viewWillAppear(animated)
if self.shouldPopCurrentView { if self.shouldPopCurrentView {
@ -112,7 +112,7 @@ class PasswordDetailTableViewController: UITableViewController, UIGestureRecogni
self.present(alert, animated: true, completion: nil) self.present(alert, animated: true, completion: nil)
} }
} }
private func requestPGPKeyPassphrase() -> String { private func requestPGPKeyPassphrase() -> String {
let sem = DispatchSemaphore(value: 0) let sem = DispatchSemaphore(value: 0)
var passphrase = "" var passphrase = ""
@ -134,7 +134,7 @@ class PasswordDetailTableViewController: UITableViewController, UIGestureRecogni
} }
return passphrase return passphrase
} }
@objc private func decryptThenShowPassword() { @objc private func decryptThenShowPassword() {
guard let passwordEntity = passwordEntity else { guard let passwordEntity = passwordEntity else {
Utils.alert(title: "Cannot Show Password", message: "The password does not exist.", controller: self, handler: {(UIAlertAction) -> Void in Utils.alert(title: "Cannot Show Password", message: "The password does not exist.", controller: self, handler: {(UIAlertAction) -> Void in
@ -166,7 +166,7 @@ class PasswordDetailTableViewController: UITableViewController, UIGestureRecogni
self.showPassword() self.showPassword()
} }
} }
private func showPassword() { private func showPassword() {
DispatchQueue.main.async { [weak self] in DispatchQueue.main.async { [weak self] in
self?.setTableData() self?.setTableData()
@ -179,7 +179,7 @@ class PasswordDetailTableViewController: UITableViewController, UIGestureRecogni
} }
} }
} }
private func setupOneTimePasswordAutoRefresh() { private func setupOneTimePasswordAutoRefresh() {
Timer.scheduledTimer(withTimeInterval: 1, repeats: true) { Timer.scheduledTimer(withTimeInterval: 1, repeats: true) {
[weak self] timer in [weak self] timer in
@ -204,19 +204,19 @@ class PasswordDetailTableViewController: UITableViewController, UIGestureRecogni
} }
} }
} }
@objc private func pressEdit(_ sender: Any?) { @objc private func pressEdit(_ sender: Any?) {
performSegue(withIdentifier: "editPasswordSegue", sender: self) performSegue(withIdentifier: "editPasswordSegue", sender: self)
} }
@objc private func setShouldPopCurrentView() { @objc private func setShouldPopCurrentView() {
self.shouldPopCurrentView = true self.shouldPopCurrentView = true
} }
@IBAction private func cancelEditPassword(segue: UIStoryboardSegue) { @IBAction private func cancelEditPassword(segue: UIStoryboardSegue) {
} }
@IBAction private func saveEditPassword(segue: UIStoryboardSegue) { @IBAction private func saveEditPassword(segue: UIStoryboardSegue) {
if self.password!.changed != 0 { if self.password!.changed != 0 {
SVProgressHUD.show(withStatus: "Saving") SVProgressHUD.show(withStatus: "Saving")
@ -231,7 +231,7 @@ class PasswordDetailTableViewController: UITableViewController, UIGestureRecogni
SVProgressHUD.dismiss(withDelay: 1) SVProgressHUD.dismiss(withDelay: 1)
} }
} }
@IBAction private func deletePassword(segue: UIStoryboardSegue) { @IBAction private func deletePassword(segue: UIStoryboardSegue) {
do { do {
try passwordStore.delete(passwordEntity: passwordEntity!) try passwordStore.delete(passwordEntity: passwordEntity!)
@ -243,7 +243,7 @@ class PasswordDetailTableViewController: UITableViewController, UIGestureRecogni
private func setTableData() { private func setTableData() {
self.tableData = Array<TableSection>() self.tableData = Array<TableSection>()
// name section // name section
var section = TableSection(type: .name) var section = TableSection(type: .name)
section.item.append(TableCell()) section.item.append(TableCell())
@ -261,9 +261,9 @@ class PasswordDetailTableViewController: UITableViewController, UIGestureRecogni
section.item.append(TableCell(title: "password", content: password.password)) section.item.append(TableCell(title: "password", content: password.password))
tableData.append(section) tableData.append(section)
// addition section // addition section
// show one time password // show one time password
if password.otpType != .none { if password.otpType != .none {
if let (title, otp) = self.password?.getOtpStrings() { if let (title, otp) = self.password?.getOtpStrings() {
@ -273,7 +273,7 @@ class PasswordDetailTableViewController: UITableViewController, UIGestureRecogni
oneTimePasswordIndexPath = IndexPath(row: 0, section: tableData.count - 1) oneTimePasswordIndexPath = IndexPath(row: 0, section: tableData.count - 1)
} }
} }
// show additional information // show additional information
let filteredAdditionKeys = password.getFilteredAdditions() let filteredAdditionKeys = password.getFilteredAdditions()
if filteredAdditionKeys.count > 0 { if filteredAdditionKeys.count > 0 {
@ -283,14 +283,14 @@ class PasswordDetailTableViewController: UITableViewController, UIGestureRecogni
}) })
tableData.append(section) tableData.append(section)
} }
// misc section // misc section
section = TableSection(type: .misc) section = TableSection(type: .misc)
section.item.append(TableCell(title: "Show Raw")) section.item.append(TableCell(title: "Show Raw"))
tableData.append(section) tableData.append(section)
} }
override func prepare(for segue: UIStoryboardSegue, sender: Any?) { override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == "editPasswordSegue" { if segue.identifier == "editPasswordSegue" {
if let controller = segue.destination as? UINavigationController { if let controller = segue.destination as? UINavigationController {
@ -306,7 +306,7 @@ class PasswordDetailTableViewController: UITableViewController, UIGestureRecogni
} }
} }
} }
private func updatePasswordImage(urlString: String) { private func updatePasswordImage(urlString: String) {
var newUrlString = urlString var newUrlString = urlString
if urlString.lowercased().hasPrefix("http://") { if urlString.lowercased().hasPrefix("http://") {
@ -321,7 +321,7 @@ class PasswordDetailTableViewController: UITableViewController, UIGestureRecogni
// if a url does not start with http or https, try to add https // if a url does not start with http or https, try to add https
newUrlString = "https://\(urlString)" newUrlString = "https://\(urlString)"
} }
try? FavIcon.downloadPreferred(newUrlString) { [weak self] result in try? FavIcon.downloadPreferred(newUrlString) { [weak self] result in
if case let .success(image) = result { if case let .success(image) = result {
let indexPath = IndexPath(row: 0, section: 0) let indexPath = IndexPath(row: 0, section: 0)
@ -334,7 +334,7 @@ class PasswordDetailTableViewController: UITableViewController, UIGestureRecogni
} }
} }
} }
@objc private func tapMenu(recognizer: UITapGestureRecognizer) { @objc private func tapMenu(recognizer: UITapGestureRecognizer) {
if recognizer.state == UIGestureRecognizerState.ended { if recognizer.state == UIGestureRecognizerState.ended {
let tapLocation = recognizer.location(in: self.tableView) let tapLocation = recognizer.location(in: self.tableView)
@ -353,17 +353,17 @@ class PasswordDetailTableViewController: UITableViewController, UIGestureRecogni
} }
} }
} }
func gestureRecognizer(_ gestureRecognizer: UIGestureRecognizer, shouldReceive touch: UITouch) -> Bool { func gestureRecognizer(_ gestureRecognizer: UIGestureRecognizer, shouldReceive touch: UITouch) -> Bool {
if touch.view!.isKind(of: UIButton.classForCoder()) { if touch.view!.isKind(of: UIButton.classForCoder()) {
return false return false
} }
return true return true
} }
@IBAction func back(segue:UIStoryboardSegue) { @IBAction func back(segue:UIStoryboardSegue) {
} }
func getNextHOTP() { func getNextHOTP() {
guard password != nil, passwordEntity != nil, password?.otpType == .hotp else { guard password != nil, passwordEntity != nil, password?.otpType == .hotp else {
DispatchQueue.main.async { DispatchQueue.main.async {
@ -371,12 +371,12 @@ class PasswordDetailTableViewController: UITableViewController, UIGestureRecogni
} }
return; return;
} }
// copy HOTP to pasteboard (will update counter) // copy HOTP to pasteboard (will update counter)
if let plainPassword = password!.getNextHotp() { if let plainPassword = password!.getNextHotp() {
SecurePasteboard.shared.copy(textToCopy: plainPassword) SecurePasteboard.shared.copy(textToCopy: plainPassword)
} }
// commit the change of HOTP counter // commit the change of HOTP counter
if password!.changed != 0 { if password!.changed != 0 {
do { do {
@ -465,11 +465,11 @@ class PasswordDetailTableViewController: UITableViewController, UIGestureRecogni
detailTextLabel.textColor = .gray detailTextLabel.textColor = .gray
detailTextLabel.text = "\(numberOfHiddenFields) hidden field\(numberOfHiddenFields > 1 ? "s" : "")" detailTextLabel.text = "\(numberOfHiddenFields) hidden field\(numberOfHiddenFields > 1 ? "s" : "")"
} }
override func tableView(_ tableView: UITableView, titleForHeaderInSection section: Int) -> String? { override func tableView(_ tableView: UITableView, titleForHeaderInSection section: Int) -> String? {
return tableData[section].header return tableData[section].header
} }
override func tableView(_ tableView: UITableView, viewForFooterInSection section: Int) -> UIView? { override func tableView(_ tableView: UITableView, viewForFooterInSection section: Int) -> UIView? {
if section == tableData.count - 1 { if section == tableData.count - 1 {
let view = UIView() let view = UIView()
@ -484,13 +484,13 @@ class PasswordDetailTableViewController: UITableViewController, UIGestureRecogni
} }
return nil return nil
} }
override func tableView(_ tableView: UITableView, performAction action: Selector, forRowAt indexPath: IndexPath, withSender sender: Any?) { override func tableView(_ tableView: UITableView, performAction action: Selector, forRowAt indexPath: IndexPath, withSender sender: Any?) {
if action == #selector(copy(_:)) { if action == #selector(copy(_:)) {
SecurePasteboard.shared.copy(textToCopy: tableData[indexPath.section].item[indexPath.row].content) SecurePasteboard.shared.copy(textToCopy: tableData[indexPath.section].item[indexPath.row].content)
} }
} }
override func tableView(_ tableView: UITableView, canPerformAction action: Selector, forRowAt indexPath: IndexPath, withSender sender: Any?) -> Bool { override func tableView(_ tableView: UITableView, canPerformAction action: Selector, forRowAt indexPath: IndexPath, withSender sender: Any?) -> Bool {
let section = tableData[indexPath.section] let section = tableData[indexPath.section]
switch(section.type) { switch(section.type) {
@ -500,11 +500,11 @@ class PasswordDetailTableViewController: UITableViewController, UIGestureRecogni
return false return false
} }
} }
override func tableView(_ tableView: UITableView, shouldShowMenuForRowAt indexPath: IndexPath) -> Bool { override func tableView(_ tableView: UITableView, shouldShowMenuForRowAt indexPath: IndexPath) -> Bool {
return true return true
} }
override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) { override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
let section = tableData[indexPath.section] let section = tableData[indexPath.section]
if section.type == .misc { if section.type == .misc {

View file

@ -20,21 +20,21 @@ enum PasswordEditorCellKey {
} }
class PasswordEditorTableViewController: UITableViewController, FillPasswordTableViewCellDelegate, PasswordSettingSliderTableViewCellDelegate, QRScannerControllerDelegate, UITextFieldDelegate, UITextViewDelegate, SFSafariViewControllerDelegate { class PasswordEditorTableViewController: UITableViewController, FillPasswordTableViewCellDelegate, PasswordSettingSliderTableViewCellDelegate, QRScannerControllerDelegate, UITextFieldDelegate, UITextViewDelegate, SFSafariViewControllerDelegate {
var tableData = [ var tableData = [
[Dictionary<PasswordEditorCellKey, Any>] [Dictionary<PasswordEditorCellKey, Any>]
]() ]()
var password: Password? var password: Password?
private var navigationItemTitle: String? private var navigationItemTitle: String?
private var sectionHeaderTitles = ["name", "password", "additions",""].map {$0.uppercased()} private var sectionHeaderTitles = ["name", "password", "additions",""].map {$0.uppercased()}
private var sectionFooterTitles = ["", "", "Use \"key: value\" format for additional fields.", ""] private var sectionFooterTitles = ["", "", "Use \"key: value\" format for additional fields.", ""]
private let nameSection = 0 private let nameSection = 0
private let passwordSection = 1 private let passwordSection = 1
private let additionsSection = 2 private let additionsSection = 2
private var hidePasswordSettings = true private var hidePasswordSettings = true
var nameCell: TextFieldTableViewCell? var nameCell: TextFieldTableViewCell?
var fillPasswordCell: FillPasswordTableViewCell? var fillPasswordCell: FillPasswordTableViewCell?
private var passwordLengthCell: SliderTableViewCell? private var passwordLengthCell: SliderTableViewCell?
@ -42,39 +42,39 @@ class PasswordEditorTableViewController: UITableViewController, FillPasswordTabl
private var deletePasswordCell: UITableViewCell? private var deletePasswordCell: UITableViewCell?
private var scanQRCodeCell: UITableViewCell? private var scanQRCodeCell: UITableViewCell?
private var memorablePasswordGeneratorCell: UITableViewCell? private var memorablePasswordGeneratorCell: UITableViewCell?
override func loadView() { override func loadView() {
super.loadView() super.loadView()
deletePasswordCell = UITableViewCell(style: .default, reuseIdentifier: "default") deletePasswordCell = UITableViewCell(style: .default, reuseIdentifier: "default")
deletePasswordCell!.textLabel?.text = "Delete Password" deletePasswordCell!.textLabel?.text = "Delete Password"
deletePasswordCell!.textLabel?.textColor = Globals.red deletePasswordCell!.textLabel?.textColor = Globals.red
deletePasswordCell?.selectionStyle = .default deletePasswordCell?.selectionStyle = .default
scanQRCodeCell = UITableViewCell(style: .default, reuseIdentifier: "default") scanQRCodeCell = UITableViewCell(style: .default, reuseIdentifier: "default")
scanQRCodeCell?.textLabel?.text = "Add One-Time Password" scanQRCodeCell?.textLabel?.text = "Add One-Time Password"
scanQRCodeCell?.textLabel?.textColor = Globals.blue scanQRCodeCell?.textLabel?.textColor = Globals.blue
scanQRCodeCell?.selectionStyle = .default scanQRCodeCell?.selectionStyle = .default
scanQRCodeCell?.accessoryType = .disclosureIndicator scanQRCodeCell?.accessoryType = .disclosureIndicator
memorablePasswordGeneratorCell = UITableViewCell(style: .default, reuseIdentifier: "default") memorablePasswordGeneratorCell = UITableViewCell(style: .default, reuseIdentifier: "default")
memorablePasswordGeneratorCell?.textLabel?.text = "Get a Memorable One: xkpasswd" memorablePasswordGeneratorCell?.textLabel?.text = "Get a Memorable One: xkpasswd"
memorablePasswordGeneratorCell?.textLabel?.textColor = Globals.blue memorablePasswordGeneratorCell?.textLabel?.textColor = Globals.blue
memorablePasswordGeneratorCell?.selectionStyle = .default memorablePasswordGeneratorCell?.selectionStyle = .default
memorablePasswordGeneratorCell?.accessoryType = .disclosureIndicator memorablePasswordGeneratorCell?.accessoryType = .disclosureIndicator
} }
override func viewDidLoad() { override func viewDidLoad() {
super.viewDidLoad() super.viewDidLoad()
if navigationItemTitle != nil { if navigationItemTitle != nil {
navigationItem.title = navigationItemTitle navigationItem.title = navigationItemTitle
} }
tableView.register(UINib(nibName: "TextFieldTableViewCell", bundle: nil), forCellReuseIdentifier: "textFieldCell") tableView.register(UINib(nibName: "TextFieldTableViewCell", bundle: nil), forCellReuseIdentifier: "textFieldCell")
tableView.register(UINib(nibName: "TextViewTableViewCell", bundle: nil), forCellReuseIdentifier: "textViewCell") tableView.register(UINib(nibName: "TextViewTableViewCell", bundle: nil), forCellReuseIdentifier: "textViewCell")
tableView.register(UINib(nibName: "FillPasswordTableViewCell", bundle: nil), forCellReuseIdentifier: "fillPasswordCell") tableView.register(UINib(nibName: "FillPasswordTableViewCell", bundle: nil), forCellReuseIdentifier: "fillPasswordCell")
tableView.register(UINib(nibName: "SliderTableViewCell", bundle: nil), forCellReuseIdentifier: "passwordLengthCell") tableView.register(UINib(nibName: "SliderTableViewCell", bundle: nil), forCellReuseIdentifier: "passwordLengthCell")
tableView.rowHeight = UITableViewAutomaticDimension tableView.rowHeight = UITableViewAutomaticDimension
tableView.estimatedRowHeight = 48 tableView.estimatedRowHeight = 48
self.tableView.sectionFooterHeight = UITableViewAutomaticDimension; self.tableView.sectionFooterHeight = UITableViewAutomaticDimension;
@ -83,10 +83,10 @@ class PasswordEditorTableViewController: UITableViewController, FillPasswordTabl
override func viewDidLayoutSubviews() { override func viewDidLayoutSubviews() {
additionsCell?.contentTextView.setContentOffset(.zero, animated: false) additionsCell?.contentTextView.setContentOffset(.zero, animated: false)
} }
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cellData = tableData[indexPath.section][indexPath.row] let cellData = tableData[indexPath.section][indexPath.row]
switch cellData[PasswordEditorCellKey.type] as! PasswordEditorCellType { switch cellData[PasswordEditorCellKey.type] as! PasswordEditorCellType {
case .nameCell: case .nameCell:
nameCell = tableView.dequeueReusableCell(withIdentifier: "textFieldCell", for: indexPath) as? TextFieldTableViewCell nameCell = tableView.dequeueReusableCell(withIdentifier: "textFieldCell", for: indexPath) as? TextFieldTableViewCell
@ -132,7 +132,7 @@ class PasswordEditorTableViewController: UITableViewController, FillPasswordTabl
return scanQRCodeCell! return scanQRCodeCell!
} }
} }
override func tableView(_ tableView: UITableView, heightForHeaderInSection section: Int) -> CGFloat { override func tableView(_ tableView: UITableView, heightForHeaderInSection section: Int) -> CGFloat {
return 44 return 44
} }
@ -153,11 +153,11 @@ class PasswordEditorTableViewController: UITableViewController, FillPasswordTabl
override func tableView(_ tableView: UITableView, titleForHeaderInSection section: Int) -> String? { override func tableView(_ tableView: UITableView, titleForHeaderInSection section: Int) -> String? {
return sectionHeaderTitles[section] return sectionHeaderTitles[section]
} }
override func tableView(_ tableView: UITableView, titleForFooterInSection section: Int) -> String? { override func tableView(_ tableView: UITableView, titleForFooterInSection section: Int) -> String? {
return sectionFooterTitles[section] return sectionFooterTitles[section]
} }
override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) { override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
let selectedCell = tableView.cellForRow(at: indexPath) let selectedCell = tableView.cellForRow(at: indexPath)
if selectedCell == deletePasswordCell { if selectedCell == deletePasswordCell {
@ -175,12 +175,12 @@ class PasswordEditorTableViewController: UITableViewController, FillPasswordTabl
let vc = SFSafariViewController(url: url, entersReaderIfAvailable: false) let vc = SFSafariViewController(url: url, entersReaderIfAvailable: false)
vc.delegate = self vc.delegate = self
present(vc, animated: true) present(vc, animated: true)
} }
} }
tableView.deselectRow(at: indexPath, animated: true) tableView.deselectRow(at: indexPath, animated: true)
} }
// generate password, copy to pasteboard, and set the cell // generate password, copy to pasteboard, and set the cell
// check whether the current password looks like an OTP field // check whether the current password looks like an OTP field
func generateAndCopyPassword() { func generateAndCopyPassword() {
@ -195,23 +195,23 @@ class PasswordEditorTableViewController: UITableViewController, FillPasswordTabl
self.generateAndCopyPasswordNoOtpCheck() self.generateAndCopyPasswordNoOtpCheck()
} }
} }
// generate the password, don't care whether the original line is otp // generate the password, don't care whether the original line is otp
func generateAndCopyPasswordNoOtpCheck() { func generateAndCopyPasswordNoOtpCheck() {
// show password settings (e.g., the length slider) // show password settings (e.g., the length slider)
showPasswordSettings() showPasswordSettings()
let length = passwordLengthCell?.roundedValue ?? 0 let length = passwordLengthCell?.roundedValue ?? 0
let plainPassword = PasswordGeneratorFlavour.from(SharedDefaults[.passwordGeneratorFlavor]).generatePassword(length: length) let plainPassword = PasswordGeneratorFlavour.from(SharedDefaults[.passwordGeneratorFlavor]).generatePassword(length: length)
SecurePasteboard.shared.copy(textToCopy: plainPassword) SecurePasteboard.shared.copy(textToCopy: plainPassword)
// update tableData so to make sure reloadData() works correctly // update tableData so to make sure reloadData() works correctly
tableData[passwordSection][0][PasswordEditorCellKey.content] = plainPassword tableData[passwordSection][0][PasswordEditorCellKey.content] = plainPassword
// update cell manually, no need to call reloadData() // update cell manually, no need to call reloadData()
fillPasswordCell?.setContent(content: plainPassword) fillPasswordCell?.setContent(content: plainPassword)
} }
// show password settings (e.g., the length slider) // show password settings (e.g., the length slider)
func showPasswordSettings() { func showPasswordSettings() {
if hidePasswordSettings == true { if hidePasswordSettings == true {
@ -219,13 +219,13 @@ class PasswordEditorTableViewController: UITableViewController, FillPasswordTabl
tableView.reloadSections([passwordSection], with: .fade) tableView.reloadSections([passwordSection], with: .fade)
} }
} }
// show/hide password settings (e.g., the length slider) // show/hide password settings (e.g., the length slider)
func showHidePasswordSettings() { func showHidePasswordSettings() {
hidePasswordSettings = !hidePasswordSettings hidePasswordSettings = !hidePasswordSettings
tableView.reloadSections([passwordSection], with: .fade) tableView.reloadSections([passwordSection], with: .fade)
} }
func insertScannedOTPFields(_ otpauth: String) { func insertScannedOTPFields(_ otpauth: String) {
// update tableData // update tableData
var additionsString = "" var additionsString = ""
@ -235,11 +235,11 @@ class PasswordEditorTableViewController: UITableViewController, FillPasswordTabl
additionsString = otpauth additionsString = otpauth
} }
tableData[additionsSection][0][PasswordEditorCellKey.content] = additionsString tableData[additionsSection][0][PasswordEditorCellKey.content] = additionsString
// reload the additions cell // reload the additions cell
additionsCell?.setContent(content: additionsString) additionsCell?.setContent(content: additionsString)
} }
// MARK: - QRScannerControllerDelegate Methods // MARK: - QRScannerControllerDelegate Methods
func checkScannedOutput(line: String) -> (accept: Bool, message: String) { func checkScannedOutput(line: String) -> (accept: Bool, message: String) {
if let url = URL(string: line), let _ = Token(url: url) { if let url = URL(string: line), let _ = Token(url: url) {
@ -248,12 +248,12 @@ class PasswordEditorTableViewController: UITableViewController, FillPasswordTabl
return (accept: false, message: "Invalid token URL") return (accept: false, message: "Invalid token URL")
} }
} }
// MARK: - QRScannerControllerDelegate Methods // MARK: - QRScannerControllerDelegate Methods
func handleScannedOutput(line: String) { func handleScannedOutput(line: String) {
insertScannedOTPFields(line) insertScannedOTPFields(line)
} }
override func prepare(for segue: UIStoryboardSegue, sender: Any?) { override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == "showQRScannerSegue" { if segue.identifier == "showQRScannerSegue" {
if let navController = segue.destination as? UINavigationController { if let navController = segue.destination as? UINavigationController {
@ -265,7 +265,7 @@ class PasswordEditorTableViewController: UITableViewController, FillPasswordTabl
} }
} }
} }
// update tableData so to make sure reloadData() works correctly // update tableData so to make sure reloadData() works correctly
func textFieldDidEndEditing(_ textField: UITextField) { func textFieldDidEndEditing(_ textField: UITextField) {
if textField == nameCell?.contentTextField { if textField == nameCell?.contentTextField {
@ -277,48 +277,48 @@ class PasswordEditorTableViewController: UITableViewController, FillPasswordTabl
} }
} }
} }
// update tableData so to make sure reloadData() works correctly // update tableData so to make sure reloadData() works correctly
func textViewDidEndEditing(_ textView: UITextView) { func textViewDidEndEditing(_ textView: UITextView) {
if textView == additionsCell?.contentTextView { if textView == additionsCell?.contentTextView {
tableData[additionsSection][0][PasswordEditorCellKey.content] = additionsCell?.getContent() tableData[additionsSection][0][PasswordEditorCellKey.content] = additionsCell?.getContent()
} }
} }
func textFieldDidBeginEditing(_ textField: UITextField) { func textFieldDidBeginEditing(_ textField: UITextField) {
if textField == fillPasswordCell?.contentTextField { if textField == fillPasswordCell?.contentTextField {
// show password generation settings automatically // show password generation settings automatically
showPasswordSettings() showPasswordSettings()
} }
} }
func getNameURL() -> (String, URL) { func getNameURL() -> (String, URL) {
let encodedName = (nameCell?.getContent()?.stringByAddingPercentEncodingForRFC3986())! let encodedName = (nameCell?.getContent()?.stringByAddingPercentEncodingForRFC3986())!
let name = URL(string: encodedName)!.lastPathComponent let name = URL(string: encodedName)!.lastPathComponent
let url = URL(string: encodedName)!.appendingPathExtension("gpg") let url = URL(string: encodedName)!.appendingPathExtension("gpg")
return (name, url) return (name, url)
} }
func checkName() -> Bool { func checkName() -> Bool {
// the name field should not be empty // the name field should not be empty
guard let name = nameCell?.getContent(), name.isEmpty == false else { guard let name = nameCell?.getContent(), name.isEmpty == false else {
Utils.alert(title: "Cannot Save", message: "Please fill in the name.", controller: self, completion: nil) Utils.alert(title: "Cannot Save", message: "Please fill in the name.", controller: self, completion: nil)
return false return false
} }
// the name should not start with / // the name should not start with /
guard name.hasPrefix("/") == false else { guard name.hasPrefix("/") == false else {
Utils.alert(title: "Cannot Save", message: "Please remove the prefix \"/\" from your password name.", controller: self, completion: nil) Utils.alert(title: "Cannot Save", message: "Please remove the prefix \"/\" from your password name.", controller: self, completion: nil)
return false return false
} }
// the name field should be a valid url // the name field should be a valid url
guard let path = name.stringByAddingPercentEncodingForRFC3986(), guard let path = name.stringByAddingPercentEncodingForRFC3986(),
var passwordURL = URL(string: path) else { var passwordURL = URL(string: path) else {
Utils.alert(title: "Cannot Save", message: "Password name is invalid.", controller: self, completion: nil) Utils.alert(title: "Cannot Save", message: "Password name is invalid.", controller: self, completion: nil)
return false return false
} }
// check whether we can parse the filename (be consistent with PasswordStore::addPasswordEntities) // check whether we can parse the filename (be consistent with PasswordStore::addPasswordEntities)
var previousPathLength = Int.max var previousPathLength = Int.max
while passwordURL.path != "." { while passwordURL.path != "." {
@ -329,10 +329,10 @@ class PasswordEditorTableViewController: UITableViewController, FillPasswordTabl
} }
previousPathLength = passwordURL.path.count previousPathLength = passwordURL.path.count
} }
return true return true
} }
func safariViewControllerDidFinish(_ controller: SFSafariViewController) { func safariViewControllerDidFinish(_ controller: SFSafariViewController) {
let copiedLinesSplit = UIPasteboard.general.string?.components(separatedBy: CharacterSet.whitespacesAndNewlines).filter({ !$0.isEmpty }) let copiedLinesSplit = UIPasteboard.general.string?.components(separatedBy: CharacterSet.whitespacesAndNewlines).filter({ !$0.isEmpty })
if copiedLinesSplit?.count ?? 0 > 0 { if copiedLinesSplit?.count ?? 0 > 0 {
@ -353,5 +353,5 @@ class PasswordEditorTableViewController: UITableViewController, FillPasswordTabl
self.present(alert, animated: true, completion: nil) self.present(alert, animated: true, completion: nil)
} }
} }
} }

View file

@ -27,13 +27,13 @@ class PasswordsViewController: UIViewController, UITableViewDataSource, UITableV
private var filteredPasswordsTableEntries: [PasswordsTableEntry] = [] private var filteredPasswordsTableEntries: [PasswordsTableEntry] = []
private var parentPasswordEntity: PasswordEntity? = nil private var parentPasswordEntity: PasswordEntity? = nil
private let passwordStore = PasswordStore.shared private let passwordStore = PasswordStore.shared
private var tapTabBarTime: TimeInterval = 0 private var tapTabBarTime: TimeInterval = 0
private var sections = [(title: String, entries: [PasswordsTableEntry])]() private var sections = [(title: String, entries: [PasswordsTableEntry])]()
private var searchActive : Bool = false private var searchActive : Bool = false
private lazy var searchController: UISearchController = { private lazy var searchController: UISearchController = {
let uiSearchController = UISearchController(searchResultsController: nil) let uiSearchController = UISearchController(searchResultsController: nil)
uiSearchController.searchResultsUpdater = self uiSearchController.searchResultsUpdater = self
@ -59,7 +59,7 @@ class PasswordsViewController: UIViewController, UITableViewDataSource, UITableV
let backUIBarButtonItem = UIBarButtonItem(title: "Back", style: .plain, target: self, action: #selector(self.backAction(_:))) let backUIBarButtonItem = UIBarButtonItem(title: "Back", style: .plain, target: self, action: #selector(self.backAction(_:)))
return backUIBarButtonItem return backUIBarButtonItem
}() }()
private lazy var transitionFromRight: CATransition = { private lazy var transitionFromRight: CATransition = {
let transition = CATransition() let transition = CATransition()
transition.type = kCATransitionPush transition.type = kCATransitionPush
@ -69,7 +69,7 @@ class PasswordsViewController: UIViewController, UITableViewDataSource, UITableV
transition.subtype = kCATransitionFromRight transition.subtype = kCATransitionFromRight
return transition return transition
}() }()
private lazy var transitionFromLeft: CATransition = { private lazy var transitionFromLeft: CATransition = {
let transition = CATransition() let transition = CATransition()
transition.type = kCATransitionPush transition.type = kCATransitionPush
@ -81,7 +81,7 @@ class PasswordsViewController: UIViewController, UITableViewDataSource, UITableV
}() }()
@IBOutlet weak var tableView: UITableView! @IBOutlet weak var tableView: UITableView!
private func initPasswordsTableEntries(parent: PasswordEntity?) { private func initPasswordsTableEntries(parent: PasswordEntity?) {
passwordsTableEntries.removeAll() passwordsTableEntries.removeAll()
passwordsTableAllEntries.removeAll() passwordsTableAllEntries.removeAll()
@ -102,9 +102,9 @@ class PasswordsViewController: UIViewController, UITableViewDataSource, UITableV
} }
parentPasswordEntity = parent parentPasswordEntity = parent
} }
@IBAction func cancelAddPassword(segue: UIStoryboardSegue) { @IBAction func cancelAddPassword(segue: UIStoryboardSegue) {
} }
@IBAction func saveAddPassword(segue: UIStoryboardSegue) { @IBAction func saveAddPassword(segue: UIStoryboardSegue) {
if let controller = segue.source as? AddPasswordTableViewController { if let controller = segue.source as? AddPasswordTableViewController {
@ -127,7 +127,7 @@ class PasswordsViewController: UIViewController, UITableViewDataSource, UITableV
} }
} }
} }
private func syncPasswords() { private func syncPasswords() {
guard passwordStore.repositoryExisted() else { guard passwordStore.repositoryExisted() else {
DispatchQueue.main.asyncAfter(deadline: .now() + .milliseconds(800)) { DispatchQueue.main.asyncAfter(deadline: .now() + .milliseconds(800)) {
@ -190,17 +190,17 @@ class PasswordsViewController: UIViewController, UITableViewDataSource, UITableV
} }
} }
} }
override func viewDidAppear(_ animated: Bool) { override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated) super.viewDidAppear(animated)
if SharedDefaults[.isShowFolderOn] { if SharedDefaults[.isShowFolderOn] {
searchController.searchBar.scopeButtonTitles = ["Current", "All"] searchController.searchBar.scopeButtonTitles = ["Current", "All"]
} else { } else {
searchController.searchBar.scopeButtonTitles = nil searchController.searchBar.scopeButtonTitles = nil
} }
} }
override func viewDidLoad() { override func viewDidLoad() {
super.viewDidLoad() super.viewDidLoad()
searchController.searchBar.delegate = self searchController.searchBar.delegate = self
@ -220,22 +220,22 @@ class PasswordsViewController: UIViewController, UITableViewDataSource, UITableV
tableView.refreshControl = syncControl tableView.refreshControl = syncControl
SVProgressHUD.setDefaultMaskType(.black) SVProgressHUD.setDefaultMaskType(.black)
tableView.register(UINib(nibName: "PasswordWithFolderTableViewCell", bundle: nil), forCellReuseIdentifier: "passwordWithFolderTableViewCell") tableView.register(UINib(nibName: "PasswordWithFolderTableViewCell", bundle: nil), forCellReuseIdentifier: "passwordWithFolderTableViewCell")
// initialize the password table // initialize the password table
reloadTableView(parent: nil) reloadTableView(parent: nil)
// reset the data table if some password (maybe another one) has been updated // reset the data table if some password (maybe another one) has been updated
NotificationCenter.default.addObserver(self, selector: #selector(actOnReloadTableViewRelatedNotification), name: .passwordStoreUpdated, object: nil) NotificationCenter.default.addObserver(self, selector: #selector(actOnReloadTableViewRelatedNotification), name: .passwordStoreUpdated, object: nil)
// reset the data table if the disaply settings have been changed // reset the data table if the disaply settings have been changed
NotificationCenter.default.addObserver(self, selector: #selector(actOnReloadTableViewRelatedNotification), name: .passwordDisplaySettingChanged, object: nil) NotificationCenter.default.addObserver(self, selector: #selector(actOnReloadTableViewRelatedNotification), name: .passwordDisplaySettingChanged, object: nil)
NotificationCenter.default.addObserver(self, selector: #selector(actOnSearchNotification), name: .passwordSearch, object: nil) NotificationCenter.default.addObserver(self, selector: #selector(actOnSearchNotification), name: .passwordSearch, object: nil)
// listen to the swipe back guesture // listen to the swipe back guesture
let swipeRight = UISwipeGestureRecognizer(target: self, action: #selector(self.respondToSwipeGesture)) let swipeRight = UISwipeGestureRecognizer(target: self, action: #selector(self.respondToSwipeGesture))
swipeRight.direction = UISwipeGestureRecognizerDirection.right swipeRight.direction = UISwipeGestureRecognizerDirection.right
self.view.addGestureRecognizer(swipeRight) self.view.addGestureRecognizer(swipeRight)
} }
override func viewWillAppear(_ animated: Bool) { override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated) super.viewWillAppear(animated)
tabBarController!.delegate = self tabBarController!.delegate = self
@ -243,7 +243,7 @@ class PasswordsViewController: UIViewController, UITableViewDataSource, UITableV
tableView.deselectRow(at: path, animated: false) tableView.deselectRow(at: path, animated: false)
} }
} }
override func viewWillLayoutSubviews() { override func viewWillLayoutSubviews() {
super.viewWillLayoutSubviews() super.viewWillLayoutSubviews()
guard #available(iOS 11, *) else { guard #available(iOS 11, *) else {
@ -252,21 +252,21 @@ class PasswordsViewController: UIViewController, UITableViewDataSource, UITableV
return return
} }
} }
func numberOfSections(in tableView: UITableView) -> Int { func numberOfSections(in tableView: UITableView) -> Int {
return sections.count return sections.count
} }
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return sections[section].entries.count return sections[section].entries.count
} }
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let longPressGestureRecognizer = UILongPressGestureRecognizer(target: self, action: #selector(longPressAction(_:))) let longPressGestureRecognizer = UILongPressGestureRecognizer(target: self, action: #selector(longPressAction(_:)))
longPressGestureRecognizer.minimumPressDuration = 0.6 longPressGestureRecognizer.minimumPressDuration = 0.6
if SharedDefaults[.isShowFolderOn] && searchController.searchBar.selectedScopeButtonIndex == 0{ if SharedDefaults[.isShowFolderOn] && searchController.searchBar.selectedScopeButtonIndex == 0{
let cell = tableView.dequeueReusableCell(withIdentifier: "passwordTableViewCell", for: indexPath) let cell = tableView.dequeueReusableCell(withIdentifier: "passwordTableViewCell", for: indexPath)
let entry = getPasswordEntry(by: indexPath) let entry = getPasswordEntry(by: indexPath)
if entry.passwordEntity!.synced { if entry.passwordEntity!.synced {
cell.textLabel?.text = entry.title cell.textLabel?.text = entry.title
@ -299,11 +299,11 @@ class PasswordsViewController: UIViewController, UITableViewDataSource, UITableV
} }
} }
private func getPasswordEntry(by indexPath: IndexPath) -> PasswordsTableEntry { private func getPasswordEntry(by indexPath: IndexPath) -> PasswordsTableEntry {
return sections[indexPath.section].entries[indexPath.row] return sections[indexPath.section].entries[indexPath.row]
} }
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) { func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
let entry = getPasswordEntry(by: indexPath) let entry = getPasswordEntry(by: indexPath)
if !entry.isDir { if !entry.isDir {
@ -318,7 +318,7 @@ class PasswordsViewController: UIViewController, UITableViewDataSource, UITableV
reloadTableView(parent: entry.passwordEntity, anim: transitionFromRight) reloadTableView(parent: entry.passwordEntity, anim: transitionFromRight)
} }
} }
@objc func respondToSwipeGesture(gesture: UIGestureRecognizer) { @objc func respondToSwipeGesture(gesture: UIGestureRecognizer) {
if let swipeGesture = gesture as? UISwipeGestureRecognizer { if let swipeGesture = gesture as? UISwipeGestureRecognizer {
// swipe right -> swipe back // swipe right -> swipe back
@ -327,7 +327,7 @@ class PasswordsViewController: UIViewController, UITableViewDataSource, UITableV
} }
} }
} }
@objc func backAction(_ sender: Any?) { @objc func backAction(_ sender: Any?) {
guard SharedDefaults[.isShowFolderOn] else { return } guard SharedDefaults[.isShowFolderOn] else { return }
var anim: CATransition? = transitionFromLeft var anim: CATransition? = transitionFromLeft
@ -336,7 +336,7 @@ class PasswordsViewController: UIViewController, UITableViewDataSource, UITableV
} }
reloadTableView(parent: parentPasswordEntity?.parent, anim: anim) reloadTableView(parent: parentPasswordEntity?.parent, anim: anim)
} }
@objc func longPressAction(_ gesture: UILongPressGestureRecognizer) { @objc func longPressAction(_ gesture: UILongPressGestureRecognizer) {
if gesture.state == UIGestureRecognizerState.began { if gesture.state == UIGestureRecognizerState.began {
let touchPoint = gesture.location(in: tableView) let touchPoint = gesture.location(in: tableView)
@ -345,7 +345,7 @@ class PasswordsViewController: UIViewController, UITableViewDataSource, UITableV
} }
} }
} }
func tableView(_ tableView: UITableView, titleForHeaderInSection section: Int) -> String? { func tableView(_ tableView: UITableView, titleForHeaderInSection section: Int) -> String? {
return sections[section].title return sections[section].title
} }
@ -357,11 +357,11 @@ class PasswordsViewController: UIViewController, UITableViewDataSource, UITableV
func tableView(_ tableView: UITableView, sectionForSectionIndexTitle title: String, at index: Int) -> Int { func tableView(_ tableView: UITableView, sectionForSectionIndexTitle title: String, at index: Int) -> Int {
return index return index
} }
func tableView(_ tableView: UITableView, accessoryButtonTappedForRowWith indexPath: IndexPath) { func tableView(_ tableView: UITableView, accessoryButtonTappedForRowWith indexPath: IndexPath) {
decryptThenCopyPassword(from: indexPath) decryptThenCopyPassword(from: indexPath)
} }
private func requestPGPKeyPassphrase() -> String { private func requestPGPKeyPassphrase() -> String {
let sem = DispatchSemaphore(value: 0) let sem = DispatchSemaphore(value: 0)
var passphrase = "" var passphrase = ""
@ -389,7 +389,7 @@ class PasswordsViewController: UIViewController, UITableViewDataSource, UITableV
} }
return passphrase return passphrase
} }
private func decryptThenCopyPassword(from indexPath: IndexPath) { private func decryptThenCopyPassword(from indexPath: IndexPath) {
guard self.passwordStore.privateKey != 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) Utils.alert(title: "Cannot Copy Password", message: "PGP Key is not set. Please set your PGP Key first.", controller: self, completion: nil)
@ -418,39 +418,39 @@ class PasswordsViewController: UIViewController, UITableViewDataSource, UITableV
} }
} }
} }
private func generateSections(item: [PasswordsTableEntry]) { private func generateSections(item: [PasswordsTableEntry]) {
let collation = UILocalizedIndexedCollation.current() let collation = UILocalizedIndexedCollation.current()
let sectionTitles = collation.sectionIndexTitles let sectionTitles = collation.sectionIndexTitles
var newSections = [(title: String, entries: [PasswordsTableEntry])]() var newSections = [(title: String, entries: [PasswordsTableEntry])]()
// initialize all sections // initialize all sections
for i in 0..<sectionTitles.count { for i in 0..<sectionTitles.count {
newSections.append((title: sectionTitles[i], entries: [PasswordsTableEntry]())) newSections.append((title: sectionTitles[i], entries: [PasswordsTableEntry]()))
} }
// put entries into sections // put entries into sections
for entry in item { for entry in item {
let sectionNumber = collation.section(for: entry, collationStringSelector: #selector(getter: PasswordsTableEntry.title)) let sectionNumber = collation.section(for: entry, collationStringSelector: #selector(getter: PasswordsTableEntry.title))
newSections[sectionNumber].entries.append(entry) newSections[sectionNumber].entries.append(entry)
} }
// sort each list and set sectionTitles // sort each list and set sectionTitles
for i in 0..<sectionTitles.count { for i in 0..<sectionTitles.count {
let entriesToSort = newSections[i].entries let entriesToSort = newSections[i].entries
let sortedEntries = collation.sortedArray(from: entriesToSort, collationStringSelector: #selector(getter: PasswordsTableEntry.title)) let sortedEntries = collation.sortedArray(from: entriesToSort, collationStringSelector: #selector(getter: PasswordsTableEntry.title))
newSections[i].entries = sortedEntries as! [PasswordsTableEntry] newSections[i].entries = sortedEntries as! [PasswordsTableEntry]
} }
// only keep non-empty sections // only keep non-empty sections
sections = newSections.filter {$0.entries.count > 0} sections = newSections.filter {$0.entries.count > 0}
} }
@objc func actOnSearchNotification() { @objc func actOnSearchNotification() {
searchController.searchBar.becomeFirstResponder() searchController.searchBar.becomeFirstResponder()
} }
override func shouldPerformSegue(withIdentifier identifier: String, sender: Any?) -> Bool { override func shouldPerformSegue(withIdentifier identifier: String, sender: Any?) -> Bool {
if identifier == "showPasswordDetail" { if identifier == "showPasswordDetail" {
guard self.passwordStore.privateKey != nil else { guard self.passwordStore.privateKey != nil else {
@ -487,7 +487,7 @@ class PasswordsViewController: UIViewController, UITableViewDataSource, UITableV
} }
} }
} }
func filterContentForSearchText(searchText: String, scope: String = "All") { func filterContentForSearchText(searchText: String, scope: String = "All") {
switch scope { switch scope {
case "All": case "All":
@ -512,10 +512,10 @@ class PasswordsViewController: UIViewController, UITableViewDataSource, UITableV
default: default:
break break
} }
} }
private func reloadTableView(data: [PasswordsTableEntry], anim: CAAnimation? = nil) { private func reloadTableView(data: [PasswordsTableEntry], anim: CAAnimation? = nil) {
// set navigation item // set navigation item
let numberOfLocalCommits = self.passwordStore.numberOfLocalCommits let numberOfLocalCommits = self.passwordStore.numberOfLocalCommits
@ -529,7 +529,7 @@ class PasswordsViewController: UIViewController, UITableViewDataSource, UITableV
} else { } else {
navigationItem.leftBarButtonItem = nil navigationItem.leftBarButtonItem = nil
} }
// set the password table // set the password table
generateSections(item: data) generateSections(item: data)
if anim != nil { if anim != nil {
@ -537,7 +537,7 @@ class PasswordsViewController: UIViewController, UITableViewDataSource, UITableV
} }
tableView.reloadData() tableView.reloadData()
self.tableView.layer.removeAnimation(forKey: "UITableViewReloadDataAnimationKey") self.tableView.layer.removeAnimation(forKey: "UITableViewReloadDataAnimationKey")
// set the sync control title // set the sync control title
let atribbutedTitle = "Last Synced: \(lastSyncedTimeString())" let atribbutedTitle = "Last Synced: \(lastSyncedTimeString())"
syncControl.attributedTitle = NSAttributedString(string: atribbutedTitle) syncControl.attributedTitle = NSAttributedString(string: atribbutedTitle)
@ -552,12 +552,12 @@ class PasswordsViewController: UIViewController, UITableViewDataSource, UITableV
formatter.timeStyle = .short formatter.timeStyle = .short
return formatter.string(from: date) return formatter.string(from: date)
} }
private func reloadTableView(parent: PasswordEntity?, anim: CAAnimation? = nil) { private func reloadTableView(parent: PasswordEntity?, anim: CAAnimation? = nil) {
initPasswordsTableEntries(parent: parent) initPasswordsTableEntries(parent: parent)
reloadTableView(data: passwordsTableEntries, anim: anim) reloadTableView(data: passwordsTableEntries, anim: anim)
} }
@objc func actOnReloadTableViewRelatedNotification() { @objc func actOnReloadTableViewRelatedNotification() {
DispatchQueue.main.async { [weak weakSelf = self] in DispatchQueue.main.async { [weak weakSelf = self] in
guard let strongSelf = weakSelf else { return } guard let strongSelf = weakSelf else { return }
@ -565,11 +565,11 @@ class PasswordsViewController: UIViewController, UITableViewDataSource, UITableV
strongSelf.reloadTableView(data: strongSelf.passwordsTableEntries) strongSelf.reloadTableView(data: strongSelf.passwordsTableEntries)
} }
} }
@objc func handleRefresh(_ syncControl: UIRefreshControl) { @objc func handleRefresh(_ syncControl: UIRefreshControl) {
syncPasswords() syncPasswords()
} }
func tabBarController(_ tabBarController: UITabBarController, didSelect viewController: UIViewController) { func tabBarController(_ tabBarController: UITabBarController, didSelect viewController: UIViewController) {
if viewController == self.navigationController { if viewController == self.navigationController {
let currentTime = Date().timeIntervalSince1970 let currentTime = Date().timeIntervalSince1970
@ -586,14 +586,14 @@ class PasswordsViewController: UIViewController, UITableViewDataSource, UITableV
backAction(self) backAction(self)
} }
} }
func searchBar(_ searchBar: UISearchBar, selectedScopeButtonIndexDidChange selectedScope: Int) { func searchBar(_ searchBar: UISearchBar, selectedScopeButtonIndexDidChange selectedScope: Int) {
// update the default search scope // update the default search scope
SharedDefaults[.isSearchDefaultAll] = searchController.searchBar.scopeButtonTitles![selectedScope] == "All" SharedDefaults[.isSearchDefaultAll] = searchController.searchBar.scopeButtonTitles![selectedScope] == "All"
updateSearchResults(for: searchController) updateSearchResults(for: searchController)
} }
func searchBarShouldBeginEditing(_ searchBar: UISearchBar) -> Bool { func searchBarShouldBeginEditing(_ searchBar: UISearchBar) -> Bool {
// set the default search scope to "all" // set the default search scope to "all"
if SharedDefaults[.isShowFolderOn] && SharedDefaults[.isSearchDefaultAll] { if SharedDefaults[.isShowFolderOn] && SharedDefaults[.isSearchDefaultAll] {
@ -603,14 +603,14 @@ class PasswordsViewController: UIViewController, UITableViewDataSource, UITableV
} }
return true return true
} }
func searchBarShouldEndEditing(_ searchBar: UISearchBar) -> Bool { func searchBarShouldEndEditing(_ searchBar: UISearchBar) -> Bool {
// set the default search scope to "current" // set the default search scope to "current"
searchController.searchBar.selectedScopeButtonIndex = 0 searchController.searchBar.selectedScopeButtonIndex = 0
updateSearchResults(for: searchController) updateSearchResults(for: searchController)
return true return true
} }
private func requestGitPassword(credential: GitCredential.Credential, lastPassword: String?) -> String? { private func requestGitPassword(credential: GitCredential.Credential, lastPassword: String?) -> String? {
let sem = DispatchSemaphore(value: 0) let sem = DispatchSemaphore(value: 0)
var password: String? var password: String?
@ -621,7 +621,7 @@ class PasswordsViewController: UIViewController, UITableViewDataSource, UITableV
case .ssh: case .ssh:
message = "Please fill in the password of your SSH key." message = "Please fill in the password of your SSH key."
} }
DispatchQueue.main.async { DispatchQueue.main.async {
SVProgressHUD.dismiss() SVProgressHUD.dismiss()
let alert = UIAlertController(title: "Password", message: message, preferredStyle: UIAlertControllerStyle.alert) let alert = UIAlertController(title: "Password", message: message, preferredStyle: UIAlertControllerStyle.alert)
@ -639,7 +639,7 @@ class PasswordsViewController: UIViewController, UITableViewDataSource, UITableV
}) })
self.present(alert, animated: true, completion: nil) self.present(alert, animated: true, completion: nil)
} }
let _ = sem.wait(timeout: .distantFuture) let _ = sem.wait(timeout: .distantFuture)
return password return password
} }

View file

@ -17,90 +17,90 @@ protocol QRScannerControllerDelegate {
} }
class QRScannerController: UIViewController, AVCaptureMetadataOutputObjectsDelegate { class QRScannerController: UIViewController, AVCaptureMetadataOutputObjectsDelegate {
@IBOutlet weak var scannerOutput: UILabel! @IBOutlet weak var scannerOutput: UILabel!
var captureSession: AVCaptureSession? var captureSession: AVCaptureSession?
var videoPreviewLayer: AVCaptureVideoPreviewLayer? var videoPreviewLayer: AVCaptureVideoPreviewLayer?
var qrCodeFrameView: UIView? var qrCodeFrameView: UIView?
let supportedCodeTypes = [AVMetadataObject.ObjectType.qr] let supportedCodeTypes = [AVMetadataObject.ObjectType.qr]
var delegate: QRScannerControllerDelegate? var delegate: QRScannerControllerDelegate?
override func viewDidLoad() { override func viewDidLoad() {
super.viewDidLoad() super.viewDidLoad()
if AVCaptureDevice.authorizationStatus(for: .video) == .denied { if AVCaptureDevice.authorizationStatus(for: .video) == .denied {
presentCameraSettings() presentCameraSettings()
} }
// Get an instance of the AVCaptureDevice class to initialize a device object and provide the video as the media type parameter. // Get an instance of the AVCaptureDevice class to initialize a device object and provide the video as the media type parameter.
let captureDevice = AVCaptureDevice.default(for: AVMediaType.video) let captureDevice = AVCaptureDevice.default(for: AVMediaType.video)
do { do {
// Get an instance of the AVCaptureDeviceInput class using the previous device object. // Get an instance of the AVCaptureDeviceInput class using the previous device object.
let input = try AVCaptureDeviceInput(device: captureDevice!) let input = try AVCaptureDeviceInput(device: captureDevice!)
// Initialize the captureSession object. // Initialize the captureSession object.
captureSession = AVCaptureSession() captureSession = AVCaptureSession()
// Set the input device on the capture session. // Set the input device on the capture session.
captureSession?.addInput(input) captureSession?.addInput(input)
// Initialize a AVCaptureMetadataOutput object and set it as the output device to the capture session. // Initialize a AVCaptureMetadataOutput object and set it as the output device to the capture session.
let captureMetadataOutput = AVCaptureMetadataOutput() let captureMetadataOutput = AVCaptureMetadataOutput()
captureSession?.addOutput(captureMetadataOutput) captureSession?.addOutput(captureMetadataOutput)
// Set delegate and use the default dispatch queue to execute the call back // Set delegate and use the default dispatch queue to execute the call back
captureMetadataOutput.setMetadataObjectsDelegate(self, queue: DispatchQueue.main) captureMetadataOutput.setMetadataObjectsDelegate(self, queue: DispatchQueue.main)
captureMetadataOutput.metadataObjectTypes = supportedCodeTypes captureMetadataOutput.metadataObjectTypes = supportedCodeTypes
// Initialize the video preview layer and add it as a sublayer to the viewPreview view's layer. // Initialize the video preview layer and add it as a sublayer to the viewPreview view's layer.
videoPreviewLayer = AVCaptureVideoPreviewLayer(session: captureSession!) videoPreviewLayer = AVCaptureVideoPreviewLayer(session: captureSession!)
videoPreviewLayer?.videoGravity = AVLayerVideoGravity.resizeAspectFill videoPreviewLayer?.videoGravity = AVLayerVideoGravity.resizeAspectFill
videoPreviewLayer?.frame = view.layer.bounds videoPreviewLayer?.frame = view.layer.bounds
view.layer.addSublayer(videoPreviewLayer!) view.layer.addSublayer(videoPreviewLayer!)
// Start video capture. // Start video capture.
captureSession?.startRunning() captureSession?.startRunning()
// Move the message label to the front // Move the message label to the front
scannerOutput.layer.cornerRadius = 10 scannerOutput.layer.cornerRadius = 10
scannerOutput.text = "No QR code detected" scannerOutput.text = "No QR code detected"
view.bringSubview(toFront: scannerOutput) view.bringSubview(toFront: scannerOutput)
// Initialize QR Code Frame to highlight the QR code // Initialize QR Code Frame to highlight the QR code
qrCodeFrameView = UIView() qrCodeFrameView = UIView()
if let qrCodeFrameView = qrCodeFrameView { if let qrCodeFrameView = qrCodeFrameView {
qrCodeFrameView.layer.borderColor = UIColor.green.cgColor qrCodeFrameView.layer.borderColor = UIColor.green.cgColor
qrCodeFrameView.layer.borderWidth = 2 qrCodeFrameView.layer.borderWidth = 2
view.addSubview(qrCodeFrameView) view.addSubview(qrCodeFrameView)
view.bringSubview(toFront: qrCodeFrameView) view.bringSubview(toFront: qrCodeFrameView)
} }
} catch { } catch {
scannerOutput.text = error.localizedDescription scannerOutput.text = error.localizedDescription
} }
} }
override func didReceiveMemoryWarning() { override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning() super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated. // Dispose of any resources that can be recreated.
} }
// MARK: - AVCaptureMetadataOutputObjectsDelegate Methods // MARK: - AVCaptureMetadataOutputObjectsDelegate Methods
func metadataOutput(_ captureOutput: AVCaptureMetadataOutput, didOutput metadataObjects: [AVMetadataObject], from connection: AVCaptureConnection) { func metadataOutput(_ captureOutput: AVCaptureMetadataOutput, didOutput metadataObjects: [AVMetadataObject], from connection: AVCaptureConnection) {
if let metadataObj = metadataObjects.first as? AVMetadataMachineReadableCodeObject, if let metadataObj = metadataObjects.first as? AVMetadataMachineReadableCodeObject,
supportedCodeTypes.contains(metadataObj.type), supportedCodeTypes.contains(metadataObj.type),
let barCodeObject = videoPreviewLayer?.transformedMetadataObject(for: metadataObj) { let barCodeObject = videoPreviewLayer?.transformedMetadataObject(for: metadataObj) {
// draw a bounds on the found QR code // draw a bounds on the found QR code
qrCodeFrameView?.frame = barCodeObject.bounds qrCodeFrameView?.frame = barCodeObject.bounds
// check whether it is a valid result // check whether it is a valid result
if let scanned = metadataObj.stringValue { if let scanned = metadataObj.stringValue {
if let (accept, message) = delegate?.checkScannedOutput(line: scanned) { if let (accept, message) = delegate?.checkScannedOutput(line: scanned) {
@ -121,13 +121,13 @@ class QRScannerController: UIViewController, AVCaptureMetadataOutputObjectsDeleg
} else { } else {
scannerOutput.text = "No string value" scannerOutput.text = "No string value"
} }
} else { } else {
qrCodeFrameView?.frame = CGRect.zero qrCodeFrameView?.frame = CGRect.zero
scannerOutput.text = "No QR code detected" scannerOutput.text = "No QR code detected"
} }
} }
func presentCameraSettings() { func presentCameraSettings() {
let alertController = UIAlertController(title: "Error", let alertController = UIAlertController(title: "Error",
message: "Camera access denied.\nWARNING: Toggle the camera permission resets the app! Save your changes.", message: "Camera access denied.\nWARNING: Toggle the camera permission resets the app! Save your changes.",
@ -140,7 +140,7 @@ class QRScannerController: UIViewController, AVCaptureMetadataOutputObjectsDeleg
}) })
} }
}) })
present(alertController, animated: true) present(alertController, animated: true)
} }
} }

View file

@ -13,7 +13,7 @@ class RawPasswordViewController: UIViewController {
@IBOutlet weak var rawPasswordTextView: UITextView! @IBOutlet weak var rawPasswordTextView: UITextView!
var password: Password? var password: Password?
override func viewDidLoad() { override func viewDidLoad() {
super.viewDidLoad() super.viewDidLoad()
navigationItem.title = password?.name navigationItem.title = password?.name

View file

@ -14,21 +14,21 @@ class SSHKeySettingTableViewController: UITableViewController {
@IBOutlet weak var privateKeyURLTextField: UITextField! @IBOutlet weak var privateKeyURLTextField: UITextField!
let passwordStore = PasswordStore.shared let passwordStore = PasswordStore.shared
override func viewDidLoad() { override func viewDidLoad() {
super.viewDidLoad() super.viewDidLoad()
privateKeyURLTextField.text = SharedDefaults[.gitSSHPrivateKeyURL]?.absoluteString privateKeyURLTextField.text = SharedDefaults[.gitSSHPrivateKeyURL]?.absoluteString
} }
@IBAction func doneButtonTapped(_ sender: UIButton) { @IBAction func doneButtonTapped(_ sender: UIButton) {
guard let privateKeyURL = URL(string: privateKeyURLTextField.text!.trimmed) else { guard let privateKeyURL = URL(string: privateKeyURLTextField.text!.trimmed) else {
Utils.alert(title: "Cannot Save", message: "Please set Private Key URL first.", controller: self, completion: nil) Utils.alert(title: "Cannot Save", message: "Please set Private Key URL first.", controller: self, completion: nil)
return return
} }
SharedDefaults[.gitSSHPrivateKeyURL] = privateKeyURL SharedDefaults[.gitSSHPrivateKeyURL] = privateKeyURL
do { do {
try Data(contentsOf: privateKeyURL).write(to: URL(fileURLWithPath: Globals.gitSSHPrivateKeyPath), options: .atomic) try Data(contentsOf: privateKeyURL).write(to: URL(fileURLWithPath: Globals.gitSSHPrivateKeyPath), options: .atomic)
} catch { } catch {

View file

@ -13,7 +13,7 @@ class SettingsSplitViewController: UISplitViewController, UISplitViewControllerD
self.delegate = self self.delegate = self
self.preferredDisplayMode = .allVisible self.preferredDisplayMode = .allVisible
} }
func splitViewController( func splitViewController(
_ splitViewController: UISplitViewController, _ splitViewController: UISplitViewController,
collapseSecondary secondaryViewController: UIViewController, collapseSecondary secondaryViewController: UIViewController,

View file

@ -16,14 +16,14 @@ class SettingsTableViewController: UITableViewController, UITabBarControllerDele
@IBOutlet weak var passcodeTableViewCell: UITableViewCell! @IBOutlet weak var passcodeTableViewCell: UITableViewCell!
@IBOutlet weak var passwordRepositoryTableViewCell: UITableViewCell! @IBOutlet weak var passwordRepositoryTableViewCell: UITableViewCell!
var setPasscodeLockAlert: UIAlertController? var setPasscodeLockAlert: UIAlertController?
let passwordStore = PasswordStore.shared let passwordStore = PasswordStore.shared
var passcodeLock = PasscodeLock.shared var passcodeLock = PasscodeLock.shared
func tabBarController(_ tabBarController: UITabBarController, didSelect viewController: UIViewController) { func tabBarController(_ tabBarController: UITabBarController, didSelect viewController: UIViewController) {
navigationController?.popViewController(animated: true) navigationController?.popViewController(animated: true)
} }
@IBAction func savePGPKey(segue: UIStoryboardSegue) { @IBAction func savePGPKey(segue: UIStoryboardSegue) {
if let controller = segue.source as? PGPKeySettingTableViewController { if let controller = segue.source as? PGPKeySettingTableViewController {
SharedDefaults[.pgpPrivateKeyURL] = URL(string: controller.pgpPrivateKeyURLTextField.text!.trimmed) SharedDefaults[.pgpPrivateKeyURL] = URL(string: controller.pgpPrivateKeyURLTextField.text!.trimmed)
@ -32,7 +32,7 @@ class SettingsTableViewController: UITableViewController, UITabBarControllerDele
self.passwordStore.pgpKeyPassphrase = controller.pgpPassphrase self.passwordStore.pgpKeyPassphrase = controller.pgpPassphrase
} }
SharedDefaults[.pgpKeySource] = "url" SharedDefaults[.pgpKeySource] = "url"
SVProgressHUD.setDefaultMaskType(.black) SVProgressHUD.setDefaultMaskType(.black)
SVProgressHUD.setDefaultStyle(.light) SVProgressHUD.setDefaultStyle(.light)
SVProgressHUD.show(withStatus: "Fetching PGP Key") SVProgressHUD.show(withStatus: "Fetching PGP Key")
@ -53,7 +53,7 @@ class SettingsTableViewController: UITableViewController, UITabBarControllerDele
} }
} }
} }
} else if let controller = segue.source as? PGPKeyArmorSettingTableViewController { } else if let controller = segue.source as? PGPKeyArmorSettingTableViewController {
SharedDefaults[.pgpKeySource] = "armor" SharedDefaults[.pgpKeySource] = "armor"
if SharedDefaults[.isRememberPGPPassphraseOn] { if SharedDefaults[.isRememberPGPPassphraseOn] {
@ -62,7 +62,7 @@ class SettingsTableViewController: UITableViewController, UITabBarControllerDele
SharedDefaults[.pgpPublicKeyArmor] = controller.armorPublicKeyTextView.text! SharedDefaults[.pgpPublicKeyArmor] = controller.armorPublicKeyTextView.text!
SharedDefaults[.pgpPrivateKeyArmor] = controller.armorPrivateKeyTextView.text! SharedDefaults[.pgpPrivateKeyArmor] = controller.armorPrivateKeyTextView.text!
SVProgressHUD.setDefaultMaskType(.black) SVProgressHUD.setDefaultMaskType(.black)
SVProgressHUD.setDefaultStyle(.light) SVProgressHUD.setDefaultStyle(.light)
SVProgressHUD.show(withStatus: "Fetching PGP Key") SVProgressHUD.show(withStatus: "Fetching PGP Key")
@ -84,7 +84,7 @@ class SettingsTableViewController: UITableViewController, UITabBarControllerDele
} }
} }
} }
private func saveImportedPGPKey() { private func saveImportedPGPKey() {
// load keys // load keys
SharedDefaults[.pgpKeySource] = "file" SharedDefaults[.pgpKeySource] = "file"
@ -108,11 +108,11 @@ class SettingsTableViewController: UITableViewController, UITabBarControllerDele
} }
} }
} }
@IBAction func saveGitServerSetting(segue: UIStoryboardSegue) { @IBAction func saveGitServerSetting(segue: UIStoryboardSegue) {
self.passwordRepositoryTableViewCell.detailTextLabel?.text = SharedDefaults[.gitURL]?.host self.passwordRepositoryTableViewCell.detailTextLabel?.text = SharedDefaults[.gitURL]?.host
} }
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return super.tableView(tableView, numberOfRowsInSection: section) return super.tableView(tableView, numberOfRowsInSection: section)
} }
@ -125,12 +125,12 @@ class SettingsTableViewController: UITableViewController, UITabBarControllerDele
setPasswordRepositoryTableViewCellDetailText() setPasswordRepositoryTableViewCellDetailText()
setPasscodeLockCell() setPasscodeLockCell()
} }
override func viewWillAppear(_ animated: Bool) { override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(true) super.viewWillAppear(true)
tabBarController!.delegate = self tabBarController!.delegate = self
} }
private func setPasscodeLockCell() { private func setPasscodeLockCell() {
if passcodeLock.hasPasscode { if passcodeLock.hasPasscode {
self.passcodeTableViewCell.detailTextLabel?.text = "On" self.passcodeTableViewCell.detailTextLabel?.text = "On"
@ -138,7 +138,7 @@ class SettingsTableViewController: UITableViewController, UITabBarControllerDele
self.passcodeTableViewCell.detailTextLabel?.text = "Off" self.passcodeTableViewCell.detailTextLabel?.text = "Off"
} }
} }
private func setPGPKeyTableViewCellDetailText() { private func setPGPKeyTableViewCellDetailText() {
if let pgpKeyID = self.passwordStore.pgpKeyID { if let pgpKeyID = self.passwordStore.pgpKeyID {
pgpKeyTableViewCell.detailTextLabel?.text = pgpKeyID pgpKeyTableViewCell.detailTextLabel?.text = pgpKeyID
@ -146,7 +146,7 @@ class SettingsTableViewController: UITableViewController, UITabBarControllerDele
pgpKeyTableViewCell.detailTextLabel?.text = "Not Set" pgpKeyTableViewCell.detailTextLabel?.text = "Not Set"
} }
} }
private func setPasswordRepositoryTableViewCellDetailText() { private func setPasswordRepositoryTableViewCellDetailText() {
if SharedDefaults[.gitURL] == nil { if SharedDefaults[.gitURL] == nil {
passwordRepositoryTableViewCell.detailTextLabel?.text = "Not Set" passwordRepositoryTableViewCell.detailTextLabel?.text = "Not Set"
@ -154,13 +154,13 @@ class SettingsTableViewController: UITableViewController, UITabBarControllerDele
passwordRepositoryTableViewCell.detailTextLabel?.text = SharedDefaults[.gitURL]!.host passwordRepositoryTableViewCell.detailTextLabel?.text = SharedDefaults[.gitURL]!.host
} }
} }
@objc func actOnPasswordStoreErasedNotification() { @objc func actOnPasswordStoreErasedNotification() {
setPGPKeyTableViewCellDetailText() setPGPKeyTableViewCellDetailText()
setPasswordRepositoryTableViewCellDetailText() setPasswordRepositoryTableViewCellDetailText()
setPasscodeLockCell() setPasscodeLockCell()
} }
override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) { override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
if tableView.cellForRow(at: indexPath) == passcodeTableViewCell { if tableView.cellForRow(at: indexPath) == passcodeTableViewCell {
if SharedDefaults[.passcodeKey] != nil{ if SharedDefaults[.passcodeKey] != nil{
@ -173,13 +173,13 @@ class SettingsTableViewController: UITableViewController, UITabBarControllerDele
} }
tableView.deselectRow(at: indexPath, animated: true) tableView.deselectRow(at: indexPath, animated: true)
} }
func showPGPKeyActionSheet() { func showPGPKeyActionSheet() {
let optionMenu = UIAlertController(title: nil, message: nil, preferredStyle: .actionSheet) let optionMenu = UIAlertController(title: nil, message: nil, preferredStyle: .actionSheet)
var urlActionTitle = "Download from URL" var urlActionTitle = "Download from URL"
var armorActionTitle = "ASCII-Armor Encrypted Key" var armorActionTitle = "ASCII-Armor Encrypted Key"
var fileActionTitle = "iTunes File Sharing" var fileActionTitle = "iTunes File Sharing"
if SharedDefaults[.pgpKeySource] == "url" { if SharedDefaults[.pgpKeySource] == "url" {
urlActionTitle = "\(urlActionTitle)" urlActionTitle = "\(urlActionTitle)"
} else if SharedDefaults[.pgpKeySource] == "armor" { } else if SharedDefaults[.pgpKeySource] == "armor" {
@ -235,8 +235,8 @@ class SettingsTableViewController: UITableViewController, UITabBarControllerDele
} }
optionMenu.addAction(fileAction) optionMenu.addAction(fileAction)
} }
if SharedDefaults[.pgpKeySource] != nil { if SharedDefaults[.pgpKeySource] != nil {
let deleteAction = UIAlertAction(title: "Remove PGP Keys", style: .destructive) { _ in let deleteAction = UIAlertAction(title: "Remove PGP Keys", style: .destructive) { _ in
self.passwordStore.removePGPKeys() self.passwordStore.removePGPKeys()
@ -249,12 +249,12 @@ class SettingsTableViewController: UITableViewController, UITabBarControllerDele
optionMenu.popoverPresentationController?.sourceRect = pgpKeyTableViewCell.bounds optionMenu.popoverPresentationController?.sourceRect = pgpKeyTableViewCell.bounds
self.present(optionMenu, animated: true, completion: nil) self.present(optionMenu, animated: true, completion: nil)
} }
func showPasscodeActionSheet() { func showPasscodeActionSheet() {
let optionMenu = UIAlertController(title: nil, message: nil, preferredStyle: .actionSheet) let optionMenu = UIAlertController(title: nil, message: nil, preferredStyle: .actionSheet)
let passcodeRemoveViewController = PasscodeLockViewController() let passcodeRemoveViewController = PasscodeLockViewController()
let removePasscodeAction = UIAlertAction(title: "Remove Passcode", style: .destructive) { [weak self] _ in let removePasscodeAction = UIAlertAction(title: "Remove Passcode", style: .destructive) { [weak self] _ in
passcodeRemoveViewController.successCallback = { passcodeRemoveViewController.successCallback = {
self?.passcodeLock.delete() self?.passcodeLock.delete()
@ -262,11 +262,11 @@ class SettingsTableViewController: UITableViewController, UITabBarControllerDele
} }
self?.present(passcodeRemoveViewController, animated: true, completion: nil) self?.present(passcodeRemoveViewController, animated: true, completion: nil)
} }
let changePasscodeAction = UIAlertAction(title: "Change Passcode", style: .default) { [weak self] _ in let changePasscodeAction = UIAlertAction(title: "Change Passcode", style: .default) { [weak self] _ in
self?.setPasscodeLock() self?.setPasscodeLock()
} }
let cancelAction = UIAlertAction(title: "Cancel", style: .cancel, handler: nil) let cancelAction = UIAlertAction(title: "Cancel", style: .cancel, handler: nil)
optionMenu.addAction(removePasscodeAction) optionMenu.addAction(removePasscodeAction)
optionMenu.addAction(changePasscodeAction) optionMenu.addAction(changePasscodeAction)
@ -289,7 +289,7 @@ class SettingsTableViewController: UITableViewController, UITabBarControllerDele
} }
} }
} }
func setPasscodeLock() { func setPasscodeLock() {
// prepare the alert for setting the passcode // prepare the alert for setting the passcode
setPasscodeLockAlert = UIAlertController(title: "Set passcode", message: "Fill in your passcode for Pass (at least 4 characters)", preferredStyle: .alert) setPasscodeLockAlert = UIAlertController(title: "Set passcode", message: "Fill in your passcode for Pass (at least 4 characters)", preferredStyle: .alert)
@ -303,7 +303,7 @@ class SettingsTableViewController: UITableViewController, UITabBarControllerDele
textField.isSecureTextEntry = true textField.isSecureTextEntry = true
textField.addTarget(self, action: #selector(self.alertTextFieldDidChange(_:)), for: UIControlEvents.editingChanged) textField.addTarget(self, action: #selector(self.alertTextFieldDidChange(_:)), for: UIControlEvents.editingChanged)
}) })
// save action // save action
let saveAction = UIAlertAction(title: "Save", style: .default) { (action:UIAlertAction) -> Void in let saveAction = UIAlertAction(title: "Save", style: .default) { (action:UIAlertAction) -> Void in
let passcode: String = self.setPasscodeLockAlert!.textFields![0].text! let passcode: String = self.setPasscodeLockAlert!.textFields![0].text!
@ -312,10 +312,10 @@ class SettingsTableViewController: UITableViewController, UITabBarControllerDele
self.setPasscodeLockCell() self.setPasscodeLockCell()
} }
saveAction.isEnabled = false // disable the Save button by default saveAction.isEnabled = false // disable the Save button by default
// cancel action // cancel action
let cancelAction = UIAlertAction(title: "Cancel", style: .cancel, handler: nil) let cancelAction = UIAlertAction(title: "Cancel", style: .cancel, handler: nil)
// present // present
setPasscodeLockAlert?.addAction(saveAction) setPasscodeLockAlert?.addAction(saveAction)
setPasscodeLockAlert?.addAction(cancelAction) setPasscodeLockAlert?.addAction(cancelAction)

View file

@ -19,7 +19,7 @@ class SpecialThanksTableViewController: BasicStaticTableViewController {
["FlatIcon", ["FlatIcon",
"https://www.flaticon.com"], "https://www.flaticon.com"],
] ]
override func viewDidLoad() { override func viewDidLoad() {
tableData.append([]) tableData.append([])
for item in openSourceComponents { for item in openSourceComponents {

View file

@ -12,33 +12,33 @@ import UIKit
class SecurePasteboard { class SecurePasteboard {
public static let shared = SecurePasteboard() public static let shared = SecurePasteboard()
private var backgroundTaskID = UIBackgroundTaskInvalid private var backgroundTaskID = UIBackgroundTaskInvalid
func copy(textToCopy: String?, expirationTime: Double = 45) { func copy(textToCopy: String?, expirationTime: Double = 45) {
// copy to the pasteboard // copy to the pasteboard
UIPasteboard.general.string = textToCopy ?? "" UIPasteboard.general.string = textToCopy ?? ""
// clean the pasteboard after expirationTime // clean the pasteboard after expirationTime
guard expirationTime > 0 else { guard expirationTime > 0 else {
return return
} }
// exit the existing background task, if any // exit the existing background task, if any
if backgroundTaskID != UIBackgroundTaskInvalid { if backgroundTaskID != UIBackgroundTaskInvalid {
UIApplication.shared.endBackgroundTask(UIBackgroundTaskInvalid) UIApplication.shared.endBackgroundTask(UIBackgroundTaskInvalid)
self.backgroundTaskID = UIBackgroundTaskInvalid self.backgroundTaskID = UIBackgroundTaskInvalid
} }
backgroundTaskID = UIApplication.shared.beginBackgroundTask(expirationHandler: { [weak self] in backgroundTaskID = UIApplication.shared.beginBackgroundTask(expirationHandler: { [weak self] in
UIPasteboard.general.string = "" UIPasteboard.general.string = ""
UIApplication.shared.endBackgroundTask(UIBackgroundTaskInvalid) UIApplication.shared.endBackgroundTask(UIBackgroundTaskInvalid)
self?.backgroundTaskID = UIBackgroundTaskInvalid self?.backgroundTaskID = UIBackgroundTaskInvalid
}) })
DispatchQueue.global(qos: .utility).asyncAfter(deadline: .now() + expirationTime) { [weak self] in DispatchQueue.global(qos: .utility).asyncAfter(deadline: .now() + expirationTime) { [weak self] in
UIPasteboard.general.string = "" UIPasteboard.general.string = ""
UIApplication.shared.endBackgroundTask(UIBackgroundTaskInvalid) UIApplication.shared.endBackgroundTask(UIBackgroundTaskInvalid)
self?.backgroundTaskID = UIBackgroundTaskInvalid self?.backgroundTaskID = UIBackgroundTaskInvalid
} }
} }
} }

View file

@ -18,11 +18,11 @@ class ContentTableViewCell: UITableViewCell {
override func setSelected(_ selected: Bool, animated: Bool) { override func setSelected(_ selected: Bool, animated: Bool) {
super.setSelected(selected, animated: animated) super.setSelected(selected, animated: animated)
} }
func getContent() -> String? { func getContent() -> String? {
return nil return nil
} }
func setContent(content: String?) { } func setContent(content: String?) { }
} }

View file

@ -18,15 +18,15 @@ class FillPasswordTableViewCell: ContentTableViewCell {
@IBOutlet weak var contentTextField: UITextField! @IBOutlet weak var contentTextField: UITextField!
var delegate: FillPasswordTableViewCellDelegate? var delegate: FillPasswordTableViewCellDelegate?
@IBOutlet weak var settingButton: UIButton! @IBOutlet weak var settingButton: UIButton!
@IBOutlet weak var generateButton: UIButton! @IBOutlet weak var generateButton: UIButton!
override func awakeFromNib() { override func awakeFromNib() {
super.awakeFromNib() super.awakeFromNib()
// Initialization code // Initialization code
contentTextField.font = Globals.passwordFont contentTextField.font = Globals.passwordFont
// Force aspect ratio of button images // Force aspect ratio of button images
settingButton.imageView?.contentMode = .scaleAspectFit settingButton.imageView?.contentMode = .scaleAspectFit
generateButton.imageView?.contentMode = .scaleAspectFit generateButton.imageView?.contentMode = .scaleAspectFit
@ -37,24 +37,24 @@ class FillPasswordTableViewCell: ContentTableViewCell {
// Configure the view for the selected state // Configure the view for the selected state
} }
@IBAction func generatePassword(_ sender: UIButton) { @IBAction func generatePassword(_ sender: UIButton) {
self.delegate?.generateAndCopyPassword() self.delegate?.generateAndCopyPassword()
} }
@IBAction func showHidePasswordSettings() { @IBAction func showHidePasswordSettings() {
self.delegate?.showHidePasswordSettings() self.delegate?.showHidePasswordSettings()
} }
// re-color // re-color
@IBAction func textFieldDidChange(_ sender: UITextField) { @IBAction func textFieldDidChange(_ sender: UITextField) {
contentTextField.attributedText = Utils.attributedPassword(plainPassword: sender.text ?? "") contentTextField.attributedText = Utils.attributedPassword(plainPassword: sender.text ?? "")
} }
override func getContent() -> String? { override func getContent() -> String? {
return contentTextField.attributedText?.string return contentTextField.attributedText?.string
} }
override func setContent(content: String?) { override func setContent(content: String?) {
contentTextField.attributedText = Utils.attributedPassword(plainPassword: content ?? "") contentTextField.attributedText = Utils.attributedPassword(plainPassword: content ?? "")
} }

View file

@ -19,19 +19,19 @@ class LabelTableViewCell: UITableViewCell {
@IBOutlet weak var contentLabel: UILabel! @IBOutlet weak var contentLabel: UILabel!
@IBOutlet weak var titleLabel: UILabel! @IBOutlet weak var titleLabel: UILabel!
private enum CellType { private enum CellType {
case password, URL, HOTP, other case password, URL, HOTP, other
} }
private var type = CellType.other private var type = CellType.other
private var isReveal = false private var isReveal = false
weak var delegatePasswordTableView : PasswordDetailTableViewController? weak var delegatePasswordTableView : PasswordDetailTableViewController?
private var passwordDisplayButton: UIButton? private var passwordDisplayButton: UIButton?
private var buttons: UIView? private var buttons: UIView?
var cellData: LabelTableViewCellData? { var cellData: LabelTableViewCellData? {
didSet { didSet {
guard let title = cellData?.title, let content = cellData?.content else { guard let title = cellData?.title, let content = cellData?.content else {
@ -72,7 +72,7 @@ class LabelTableViewCell: UITableViewCell {
updateButtons() updateButtons()
} }
} }
override var canBecomeFirstResponder: Bool { override var canBecomeFirstResponder: Bool {
get { get {
return true return true
@ -103,7 +103,7 @@ class LabelTableViewCell: UITableViewCell {
override func copy(_ sender: Any?) { override func copy(_ sender: Any?) {
SecurePasteboard.shared.copy(textToCopy: cellData?.content) SecurePasteboard.shared.copy(textToCopy: cellData?.content)
} }
@objc func revealPassword(_ sender: Any?) { @objc func revealPassword(_ sender: Any?) {
let plainPassword = cellData?.content ?? "" let plainPassword = cellData?.content ?? ""
if type == .password { if type == .password {
@ -114,7 +114,7 @@ class LabelTableViewCell: UITableViewCell {
isReveal = true isReveal = true
passwordDisplayButton?.setImage(#imageLiteral(resourceName: "Invisible"), for: .normal) passwordDisplayButton?.setImage(#imageLiteral(resourceName: "Invisible"), for: .normal)
} }
@objc func concealPassword(_ sender: Any?) { @objc func concealPassword(_ sender: Any?) {
if type == .password { if type == .password {
if cellData?.content.isEmpty == false { if cellData?.content.isEmpty == false {
@ -128,7 +128,7 @@ class LabelTableViewCell: UITableViewCell {
isReveal = false isReveal = false
passwordDisplayButton?.setImage(#imageLiteral(resourceName: "Visible"), for: .normal) passwordDisplayButton?.setImage(#imageLiteral(resourceName: "Visible"), for: .normal)
} }
@objc func reversePasswordDisplay(_ sender: Any?) { @objc func reversePasswordDisplay(_ sender: Any?) {
if isReveal { if isReveal {
// conceal // conceal
@ -143,21 +143,21 @@ class LabelTableViewCell: UITableViewCell {
// if isURLCell, passwordTableView should not be nil // if isURLCell, passwordTableView should not be nil
delegatePasswordTableView!.openLink(to: cellData?.content) delegatePasswordTableView!.openLink(to: cellData?.content)
} }
@objc func getNextHOTP(_ sender: Any?) { @objc func getNextHOTP(_ sender: Any?) {
// if isHOTPCell, passwordTableView should not be nil // if isHOTPCell, passwordTableView should not be nil
delegatePasswordTableView!.getNextHOTP() delegatePasswordTableView!.getNextHOTP()
} }
private func updateButtons() { private func updateButtons() {
// total width and height of a button // total width and height of a button
let height = min(self.bounds.height, 36.0) let height = min(self.bounds.height, 36.0)
let width = max(height * 0.8, Globals.tableCellButtonSize) let width = max(height * 0.8, Globals.tableCellButtonSize)
// margins (between button boundary and icon) // margins (between button boundary and icon)
let marginY = max((height - Globals.tableCellButtonSize) / 2, 0.0) let marginY = max((height - Globals.tableCellButtonSize) / 2, 0.0)
let marginX = max((width - Globals.tableCellButtonSize) / 2, 0.0) let marginX = max((width - Globals.tableCellButtonSize) / 2, 0.0)
switch type { switch type {
case .password: case .password:
if let content = cellData?.content, content != "" { if let content = cellData?.content, content != "" {
@ -178,7 +178,7 @@ class LabelTableViewCell: UITableViewCell {
nextButton.imageView?.contentMode = .scaleAspectFit nextButton.imageView?.contentMode = .scaleAspectFit
nextButton.contentEdgeInsets = UIEdgeInsetsMake(marginY, marginX, marginY, marginX) nextButton.contentEdgeInsets = UIEdgeInsetsMake(marginY, marginX, marginY, marginX)
nextButton.addTarget(self, action: #selector(getNextHOTP), for: UIControlEvents.touchUpInside) nextButton.addTarget(self, action: #selector(getNextHOTP), for: UIControlEvents.touchUpInside)
// password button // password button
passwordDisplayButton = UIButton(type: .system) passwordDisplayButton = UIButton(type: .system)
passwordDisplayButton!.frame = CGRect(x: width, y: 0, width: width, height: height) passwordDisplayButton!.frame = CGRect(x: width, y: 0, width: width, height: height)
@ -187,7 +187,7 @@ class LabelTableViewCell: UITableViewCell {
passwordDisplayButton!.imageView?.contentMode = .scaleAspectFit passwordDisplayButton!.imageView?.contentMode = .scaleAspectFit
passwordDisplayButton!.contentEdgeInsets = UIEdgeInsetsMake(marginY, marginX, marginY, marginX) passwordDisplayButton!.contentEdgeInsets = UIEdgeInsetsMake(marginY, marginX, marginY, marginX)
passwordDisplayButton!.addTarget(self, action: #selector(reversePasswordDisplay), for: UIControlEvents.touchUpInside) passwordDisplayButton!.addTarget(self, action: #selector(reversePasswordDisplay), for: UIControlEvents.touchUpInside)
buttons = UIView() buttons = UIView()
buttons!.frame = CGRect(x: 0, y: 0, width: width * 2, height: height) buttons!.frame = CGRect(x: 0, y: 0, width: width * 2, height: height)
buttons!.addSubview(nextButton) buttons!.addSubview(nextButton)

View file

@ -22,5 +22,5 @@ class PasswordDetailTitleTableViewCell: UITableViewCell {
// Configure the view for the selected state // Configure the view for the selected state
} }
} }

View file

@ -18,15 +18,15 @@ class SliderTableViewCell: ContentTableViewCell {
@IBOutlet weak var titleLabel: UILabel! @IBOutlet weak var titleLabel: UILabel!
@IBOutlet weak var valueLabel: UILabel! @IBOutlet weak var valueLabel: UILabel!
@IBOutlet weak var slider: UISlider! @IBOutlet weak var slider: UISlider!
var delegate: UITableViewController? var delegate: UITableViewController?
var roundedValue: Int { var roundedValue: Int {
get { get {
return Int(valueLabel.text!)! return Int(valueLabel.text!)!
} }
} }
override func awakeFromNib() { override func awakeFromNib() {
super.awakeFromNib() super.awakeFromNib()
// Initialization code // Initialization code
@ -37,7 +37,7 @@ class SliderTableViewCell: ContentTableViewCell {
// Configure the view for the selected state // Configure the view for the selected state
} }
@IBAction func handleSliderValueChange(_ sender: UISlider) { @IBAction func handleSliderValueChange(_ sender: UISlider) {
let oldRoundedValue = self.roundedValue let oldRoundedValue = self.roundedValue
let newRoundedValue = Int(sender.value) let newRoundedValue = Int(sender.value)
@ -51,14 +51,14 @@ class SliderTableViewCell: ContentTableViewCell {
delegate.generateAndCopyPassword() delegate.generateAndCopyPassword()
} }
} }
func reset(title: String, minimumValue: Int, maximumValue: Int, defaultValue: Int) { func reset(title: String, minimumValue: Int, maximumValue: Int, defaultValue: Int) {
titleLabel.text = title titleLabel.text = title
slider.minimumValue = Float(minimumValue) slider.minimumValue = Float(minimumValue)
slider.maximumValue = Float(maximumValue) slider.maximumValue = Float(maximumValue)
slider.value = Float(defaultValue) slider.value = Float(defaultValue)
valueLabel.text = String(defaultValue) valueLabel.text = String(defaultValue)
// "not editable" // "not editable"
if minimumValue == maximumValue { if minimumValue == maximumValue {
titleLabel.textColor = UIColor.gray titleLabel.textColor = UIColor.gray
@ -66,5 +66,5 @@ class SliderTableViewCell: ContentTableViewCell {
slider.isUserInteractionEnabled = false slider.isUserInteractionEnabled = false
} }
} }
} }

View file

@ -11,15 +11,15 @@ import UIKit
class TextFieldTableViewCell: ContentTableViewCell { class TextFieldTableViewCell: ContentTableViewCell {
@IBOutlet weak var contentTextField: UITextField! @IBOutlet weak var contentTextField: UITextField!
override func awakeFromNib() { override func awakeFromNib() {
super.awakeFromNib() super.awakeFromNib()
} }
override func setSelected(_ selected: Bool, animated: Bool) { override func setSelected(_ selected: Bool, animated: Bool) {
super.setSelected(selected, animated: animated) super.setSelected(selected, animated: animated)
} }
override func getContent() -> String? { override func getContent() -> String? {
return contentTextField.text return contentTextField.text
} }

View file

@ -19,11 +19,11 @@ class TextViewTableViewCell: ContentTableViewCell {
override func setSelected(_ selected: Bool, animated: Bool) { override func setSelected(_ selected: Bool, animated: Bool) {
super.setSelected(selected, animated: animated) super.setSelected(selected, animated: animated)
} }
override func getContent() -> String? { override func getContent() -> String? {
return contentTextView.text return contentTextView.text
} }
override func setContent(content: String?) { override func setContent(content: String?) {
contentTextView.text = content contentTextView.text = content
} }

View file

@ -12,7 +12,7 @@ class TitleTextFieldTableViewCell: UITableViewCell {
@IBOutlet weak var titleLabel: UILabel! @IBOutlet weak var titleLabel: UILabel!
@IBOutlet weak var contentTextField: UITextField! @IBOutlet weak var contentTextField: UITextField!
override func awakeFromNib() { override func awakeFromNib() {
super.awakeFromNib() super.awakeFromNib()
// Initialization code // Initialization code
@ -20,7 +20,7 @@ class TitleTextFieldTableViewCell: UITableViewCell {
let tapGestureRecognizer = UITapGestureRecognizer(target: self, action: #selector(tap(_:))) let tapGestureRecognizer = UITapGestureRecognizer(target: self, action: #selector(tap(_:)))
titleLabel.addGestureRecognizer(tapGestureRecognizer) titleLabel.addGestureRecognizer(tapGestureRecognizer)
} }
@objc func tap(_ sender: Any?) { @objc func tap(_ sender: Any?) {
contentTextField.becomeFirstResponder() contentTextField.becomeFirstResponder()
} }
@ -28,5 +28,5 @@ class TitleTextFieldTableViewCell: UITableViewCell {
override func setSelected(_ selected: Bool, animated: Bool) { override func setSelected(_ selected: Bool, animated: Bool) {
super.setSelected(selected, animated: animated) super.setSelected(selected, animated: animated)
} }
} }

View file

@ -25,18 +25,18 @@ fileprivate class PasswordsTableEntry : NSObject {
class CredentialProviderViewController: ASCredentialProviderViewController, UITableViewDataSource, UITableViewDelegate, UISearchBarDelegate { class CredentialProviderViewController: ASCredentialProviderViewController, UITableViewDataSource, UITableViewDelegate, UISearchBarDelegate {
@IBOutlet weak var searchBar: UISearchBar! @IBOutlet weak var searchBar: UISearchBar!
@IBOutlet weak var tableView: UITableView! @IBOutlet weak var tableView: UITableView!
private let passwordStore = PasswordStore.shared private let passwordStore = PasswordStore.shared
private var searchActive = false private var searchActive = false
private var passwordsTableEntries: [PasswordsTableEntry] = [] private var passwordsTableEntries: [PasswordsTableEntry] = []
private var filteredPasswordsTableEntries: [PasswordsTableEntry] = [] private var filteredPasswordsTableEntries: [PasswordsTableEntry] = []
private lazy var passcodelock: PasscodeExtensionDisplay = { private lazy var passcodelock: PasscodeExtensionDisplay = {
let passcodelock = PasscodeExtensionDisplay(extensionContext: self.extensionContext) let passcodelock = PasscodeExtensionDisplay(extensionContext: self.extensionContext)
return passcodelock return passcodelock
}() }()
/* /*
Prepare your UI to list available credentials for the user to choose from. The items in Prepare your UI to list available credentials for the user to choose from. The items in
'serviceIdentifiers' describe the service the user is logging in to, so your extension can 'serviceIdentifiers' describe the service the user is logging in to, so your extension can
@ -50,14 +50,14 @@ class CredentialProviderViewController: ASCredentialProviderViewController, UITa
searchBarSearchButtonClicked(searchBar) searchBarSearchButtonClicked(searchBar)
return return
} }
// get the domain // get the domain
var identifier = serviceIdentifiers[0].identifier var identifier = serviceIdentifiers[0].identifier
if !identifier.hasPrefix("http://") && !identifier.hasPrefix("https://") { if !identifier.hasPrefix("http://") && !identifier.hasPrefix("https://") {
identifier = "http://" + identifier identifier = "http://" + identifier
} }
let url = URL(string: identifier)?.host ?? "" let url = URL(string: identifier)?.host ?? ""
// "click" search // "click" search
searchBar.text = url searchBar.text = url
searchBar.becomeFirstResponder() searchBar.becomeFirstResponder()
@ -102,20 +102,20 @@ class CredentialProviderViewController: ASCredentialProviderViewController, UITa
super.viewWillAppear(animated) super.viewWillAppear(animated)
passcodelock.presentPasscodeLockIfNeeded(self) passcodelock.presentPasscodeLockIfNeeded(self)
} }
override func viewDidLoad() { override func viewDidLoad() {
super.viewDidLoad() super.viewDidLoad()
// prepare // prepare
searchBar.delegate = self searchBar.delegate = self
tableView.delegate = self tableView.delegate = self
tableView.dataSource = self tableView.dataSource = self
tableView.register(UINib(nibName: "PasswordWithFolderTableViewCell", bundle: nil), forCellReuseIdentifier: "passwordWithFolderTableViewCell") tableView.register(UINib(nibName: "PasswordWithFolderTableViewCell", bundle: nil), forCellReuseIdentifier: "passwordWithFolderTableViewCell")
// initialize table entries // initialize table entries
initPasswordsTableEntries() initPasswordsTableEntries()
} }
private func initPasswordsTableEntries() { private func initPasswordsTableEntries() {
passwordsTableEntries.removeAll() passwordsTableEntries.removeAll()
filteredPasswordsTableEntries.removeAll() filteredPasswordsTableEntries.removeAll()
@ -125,7 +125,7 @@ class CredentialProviderViewController: ASCredentialProviderViewController, UITa
PasswordsTableEntry($0) PasswordsTableEntry($0)
} }
} }
// define cell contents, and set long press action // define cell contents, and set long press action
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "passwordTableViewCell", for: indexPath) let cell = tableView.dequeueReusableCell(withIdentifier: "passwordTableViewCell", for: indexPath)
@ -140,16 +140,16 @@ class CredentialProviderViewController: ASCredentialProviderViewController, UITa
cell.detailTextLabel?.text = entry.categoryText cell.detailTextLabel?.text = entry.categoryText
return cell return cell
} }
// select row -> extension returns (with username and password) // select row -> extension returns (with username and password)
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) { func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
let entry = getPasswordEntry(by: indexPath) let entry = getPasswordEntry(by: indexPath)
guard self.passwordStore.privateKey != 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) Utils.alert(title: "Cannot Copy Password", message: "PGP Key is not set. Please set your PGP Key first.", controller: self, completion: nil)
return return
} }
let passwordEntity = entry.passwordEntity! let passwordEntity = entry.passwordEntity!
UIImpactFeedbackGenerator(style: .medium).impactOccurred() UIImpactFeedbackGenerator(style: .medium).impactOccurred()
DispatchQueue.global(qos: .userInteractive).async { DispatchQueue.global(qos: .userInteractive).async {
@ -171,18 +171,18 @@ class CredentialProviderViewController: ASCredentialProviderViewController, UITa
} }
} }
} }
func numberOfSectionsInTableView(tableView: UITableView) -> Int { func numberOfSectionsInTableView(tableView: UITableView) -> Int {
return 1 return 1
} }
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
if searchActive { if searchActive {
return filteredPasswordsTableEntries.count return filteredPasswordsTableEntries.count
} }
return passwordsTableEntries.count; return passwordsTableEntries.count;
} }
private func requestPGPKeyPassphrase() -> String { private func requestPGPKeyPassphrase() -> String {
let sem = DispatchSemaphore(value: 0) let sem = DispatchSemaphore(value: 0)
var passphrase = "" var passphrase = ""
@ -204,13 +204,13 @@ class CredentialProviderViewController: ASCredentialProviderViewController, UITa
} }
return passphrase return passphrase
} }
func searchBarCancelButtonClicked(_ searchBar: UISearchBar) { func searchBarCancelButtonClicked(_ searchBar: UISearchBar) {
searchBar.text = "" searchBar.text = ""
searchActive = false searchActive = false
self.tableView.reloadData() self.tableView.reloadData()
} }
func searchBarSearchButtonClicked(_ searchBar: UISearchBar) { func searchBarSearchButtonClicked(_ searchBar: UISearchBar) {
if let searchText = searchBar.text, searchText.isEmpty == false { if let searchText = searchBar.text, searchText.isEmpty == false {
filteredPasswordsTableEntries = passwordsTableEntries.filter { entry in filteredPasswordsTableEntries = passwordsTableEntries.filter { entry in
@ -229,11 +229,11 @@ class CredentialProviderViewController: ASCredentialProviderViewController, UITa
} }
self.tableView.reloadData() self.tableView.reloadData()
} }
func searchBar(_ searchBar: UISearchBar, textDidChange searchText: String) { func searchBar(_ searchBar: UISearchBar, textDidChange searchText: String) {
searchBarSearchButtonClicked(searchBar) searchBarSearchButtonClicked(searchBar)
} }
private func getPasswordEntry(by indexPath: IndexPath) -> PasswordsTableEntry { private func getPasswordEntry(by indexPath: IndexPath) -> PasswordsTableEntry {
if searchActive { if searchActive {
return filteredPasswordsTableEntries[indexPath.row] return filteredPasswordsTableEntries[indexPath.row]

View file

@ -31,7 +31,7 @@ class PasscodeExtensionDisplay {
private var isPasscodePresented = false private var isPasscodePresented = false
private let passcodeLockVC: PasscodeLockViewControllerForExtension private let passcodeLockVC: PasscodeLockViewControllerForExtension
private let extensionContext: ASCredentialProviderExtensionContext? private let extensionContext: ASCredentialProviderExtensionContext?
public init(extensionContext: ASCredentialProviderExtensionContext?) { public init(extensionContext: ASCredentialProviderExtensionContext?) {
self.extensionContext = extensionContext self.extensionContext = extensionContext
passcodeLockVC = PasscodeLockViewControllerForExtension(extensionContext: extensionContext) passcodeLockVC = PasscodeLockViewControllerForExtension(extensionContext: extensionContext)
@ -40,7 +40,7 @@ class PasscodeExtensionDisplay {
} }
passcodeLockVC.setCancellable(true) passcodeLockVC.setCancellable(true)
} }
// present the passcode lock view if passcode is set and the view controller is not presented // present the passcode lock view if passcode is set and the view controller is not presented
public func presentPasscodeLockIfNeeded(_ extensionVC: UIViewController) { public func presentPasscodeLockIfNeeded(_ extensionVC: UIViewController) {
guard PasscodeLock.shared.hasPasscode && !isPasscodePresented == true else { guard PasscodeLock.shared.hasPasscode && !isPasscodePresented == true else {
@ -49,7 +49,7 @@ class PasscodeExtensionDisplay {
isPasscodePresented = true isPasscodePresented = true
extensionVC.present(passcodeLockVC, animated: true, completion: nil) extensionVC.present(passcodeLockVC, animated: true, completion: nil)
} }
public func dismiss(animated: Bool = true) { public func dismiss(animated: Bool = true) {
isPasscodePresented = false isPasscodePresented = false
} }

View file

@ -26,24 +26,24 @@ fileprivate class PasswordsTableEntry : NSObject {
class ExtensionViewController: UIViewController, UITableViewDataSource, UITableViewDelegate, UISearchBarDelegate, UINavigationBarDelegate { class ExtensionViewController: UIViewController, UITableViewDataSource, UITableViewDelegate, UISearchBarDelegate, UINavigationBarDelegate {
@IBOutlet weak var searchBar: UISearchBar! @IBOutlet weak var searchBar: UISearchBar!
@IBOutlet weak var tableView: UITableView! @IBOutlet weak var tableView: UITableView!
private let passwordStore = PasswordStore.shared private let passwordStore = PasswordStore.shared
private var searchActive = false private var searchActive = false
private var passwordsTableEntries: [PasswordsTableEntry] = [] private var passwordsTableEntries: [PasswordsTableEntry] = []
private var filteredPasswordsTableEntries: [PasswordsTableEntry] = [] private var filteredPasswordsTableEntries: [PasswordsTableEntry] = []
enum Action { enum Action {
case findLogin, fillBrowser, unknown case findLogin, fillBrowser, unknown
} }
private var extensionAction = Action.unknown private var extensionAction = Action.unknown
private lazy var passcodelock: PasscodeExtensionDisplay = { private lazy var passcodelock: PasscodeExtensionDisplay = {
let passcodelock = PasscodeExtensionDisplay(extensionContext: self.extensionContext) let passcodelock = PasscodeExtensionDisplay(extensionContext: self.extensionContext)
return passcodelock return passcodelock
}() }()
private func initPasswordsTableEntries() { private func initPasswordsTableEntries() {
passwordsTableEntries.removeAll() passwordsTableEntries.removeAll()
filteredPasswordsTableEntries.removeAll() filteredPasswordsTableEntries.removeAll()
@ -53,12 +53,12 @@ class ExtensionViewController: UIViewController, UITableViewDataSource, UITableV
PasswordsTableEntry($0) PasswordsTableEntry($0)
} }
} }
override func viewWillAppear(_ animated: Bool) { override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated) super.viewWillAppear(animated)
passcodelock.presentPasscodeLockIfNeeded(self) passcodelock.presentPasscodeLockIfNeeded(self)
} }
override func viewDidLoad() { override func viewDidLoad() {
super.viewDidLoad() super.viewDidLoad()
// prepare // prepare
@ -66,15 +66,15 @@ class ExtensionViewController: UIViewController, UITableViewDataSource, UITableV
tableView.delegate = self tableView.delegate = self
tableView.dataSource = self tableView.dataSource = self
tableView.register(UINib(nibName: "PasswordWithFolderTableViewCell", bundle: nil), forCellReuseIdentifier: "passwordWithFolderTableViewCell") tableView.register(UINib(nibName: "PasswordWithFolderTableViewCell", bundle: nil), forCellReuseIdentifier: "passwordWithFolderTableViewCell")
// initialize table entries // initialize table entries
initPasswordsTableEntries() initPasswordsTableEntries()
// get the provider // get the provider
guard let extensionItems = extensionContext?.inputItems as? [NSExtensionItem] else { guard let extensionItems = extensionContext?.inputItems as? [NSExtensionItem] else {
return return
} }
for extensionItem in extensionItems { for extensionItem in extensionItems {
if let itemProviders = extensionItem.attachments as? [NSItemProvider] { if let itemProviders = extensionItem.attachments as? [NSItemProvider] {
for provider in itemProviders { for provider in itemProviders {
@ -133,7 +133,7 @@ class ExtensionViewController: UIViewController, UITableViewDataSource, UITableV
} }
} }
} }
// define cell contents, and set long press action // define cell contents, and set long press action
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "passwordTableViewCell", for: indexPath) let cell = tableView.dequeueReusableCell(withIdentifier: "passwordTableViewCell", for: indexPath)
@ -148,16 +148,16 @@ class ExtensionViewController: UIViewController, UITableViewDataSource, UITableV
cell.detailTextLabel?.text = entry.categoryText cell.detailTextLabel?.text = entry.categoryText
return cell return cell
} }
// select row -> extension returns (with username and password) // select row -> extension returns (with username and password)
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) { func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
let entry = getPasswordEntry(by: indexPath) let entry = getPasswordEntry(by: indexPath)
guard self.passwordStore.privateKey != 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) Utils.alert(title: "Cannot Copy Password", message: "PGP Key is not set. Please set your PGP Key first.", controller: self, completion: nil)
return return
} }
let passwordEntity = entry.passwordEntity! let passwordEntity = entry.passwordEntity!
UIImpactFeedbackGenerator(style: .medium).impactOccurred() UIImpactFeedbackGenerator(style: .medium).impactOccurred()
DispatchQueue.global(qos: .userInteractive).async { DispatchQueue.global(qos: .userInteractive).async {
@ -197,19 +197,19 @@ class ExtensionViewController: UIViewController, UITableViewDataSource, UITableV
} }
} }
} }
func numberOfSectionsInTableView(tableView: UITableView) -> Int { func numberOfSectionsInTableView(tableView: UITableView) -> Int {
return 1 return 1
} }
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
if searchActive{ if searchActive{
return filteredPasswordsTableEntries.count return filteredPasswordsTableEntries.count
} }
return passwordsTableEntries.count; return passwordsTableEntries.count;
} }
private func requestPGPKeyPassphrase() -> String { private func requestPGPKeyPassphrase() -> String {
let sem = DispatchSemaphore(value: 0) let sem = DispatchSemaphore(value: 0)
var passphrase = "" var passphrase = ""
@ -231,17 +231,17 @@ class ExtensionViewController: UIViewController, UITableViewDataSource, UITableV
} }
return passphrase return passphrase
} }
@IBAction func cancelExtension(_ sender: Any) { @IBAction func cancelExtension(_ sender: Any) {
extensionContext!.completeRequest(returningItems: [], completionHandler: nil) extensionContext!.completeRequest(returningItems: [], completionHandler: nil)
} }
func searchBarCancelButtonClicked(_ searchBar: UISearchBar) { func searchBarCancelButtonClicked(_ searchBar: UISearchBar) {
searchBar.text = "" searchBar.text = ""
searchActive = false searchActive = false
self.tableView.reloadData() self.tableView.reloadData()
} }
func searchBarSearchButtonClicked(_ searchBar: UISearchBar) { func searchBarSearchButtonClicked(_ searchBar: UISearchBar) {
if let searchText = searchBar.text, searchText.isEmpty == false { if let searchText = searchBar.text, searchText.isEmpty == false {
filteredPasswordsTableEntries = passwordsTableEntries.filter { entry in filteredPasswordsTableEntries = passwordsTableEntries.filter { entry in
@ -260,11 +260,11 @@ class ExtensionViewController: UIViewController, UITableViewDataSource, UITableV
} }
self.tableView.reloadData() self.tableView.reloadData()
} }
func searchBar(_ searchBar: UISearchBar, textDidChange searchText: String) { func searchBar(_ searchBar: UISearchBar, textDidChange searchText: String) {
searchBarSearchButtonClicked(searchBar) searchBarSearchButtonClicked(searchBar)
} }
private func getPasswordEntry(by indexPath: IndexPath) -> PasswordsTableEntry { private func getPasswordEntry(by indexPath: IndexPath) -> PasswordsTableEntry {
if searchActive { if searchActive {
return filteredPasswordsTableEntries[indexPath.row] return filteredPasswordsTableEntries[indexPath.row]

View file

@ -28,7 +28,7 @@ class OnePasswordExtensionKey {
static let returnedFieldsKey = "returned_fields" static let returnedFieldsKey = "returned_fields"
static let oldPasswordKey = "old_password" static let oldPasswordKey = "old_password"
static let passwordGeneratorOptionsKey = "password_generator_options" static let passwordGeneratorOptionsKey = "password_generator_options"
// Password Generator options - Used to set the 1Password Password Generator options when saving a new Login or when changing the password for for an existing Login // Password Generator options - Used to set the 1Password Password Generator options when saving a new Login or when changing the password for for an existing Login
static let generatedPasswordMinLengthKey = "password_min_length" static let generatedPasswordMinLengthKey = "password_min_length"
static let generatedPasswordMaxLengthKey = "password_max_length" static let generatedPasswordMaxLengthKey = "password_max_length"

View file

@ -30,7 +30,7 @@ class PasscodeExtensionDisplay {
private var isPasscodePresented = false private var isPasscodePresented = false
private let passcodeLockVC: PasscodeLockViewControllerForExtension private let passcodeLockVC: PasscodeLockViewControllerForExtension
private let extensionContext: NSExtensionContext? private let extensionContext: NSExtensionContext?
public init(extensionContext: NSExtensionContext?) { public init(extensionContext: NSExtensionContext?) {
self.extensionContext = extensionContext self.extensionContext = extensionContext
passcodeLockVC = PasscodeLockViewControllerForExtension(extensionContext: extensionContext) passcodeLockVC = PasscodeLockViewControllerForExtension(extensionContext: extensionContext)
@ -39,7 +39,7 @@ class PasscodeExtensionDisplay {
} }
passcodeLockVC.setCancellable(true) passcodeLockVC.setCancellable(true)
} }
// present the passcode lock view if passcode is set and the view controller is not presented // present the passcode lock view if passcode is set and the view controller is not presented
public func presentPasscodeLockIfNeeded(_ extensionVC: UIViewController) { public func presentPasscodeLockIfNeeded(_ extensionVC: UIViewController) {
guard PasscodeLock.shared.hasPasscode && !isPasscodePresented == true else { guard PasscodeLock.shared.hasPasscode && !isPasscodePresented == true else {
@ -48,7 +48,7 @@ class PasscodeExtensionDisplay {
isPasscodePresented = true isPasscodePresented = true
extensionVC.present(passcodeLockVC, animated: true, completion: nil) extensionVC.present(passcodeLockVC, animated: true, completion: nil)
} }
public func dismiss(animated: Bool = true) { public func dismiss(animated: Bool = true) {
isPasscodePresented = false isPasscodePresented = false
} }

View file

@ -12,7 +12,7 @@ run: function(arguments) {
arguments.completionFunction({"url_string": url, "error": error}); arguments.completionFunction({"url_string": url, "error": error});
} }
}, },
finalize: function(arguments) { finalize: function(arguments) {
if (arguments["password"]) { if (arguments["password"]) {
var passwordElement = document.querySelector("input[type=password]") var passwordElement = document.querySelector("input[type=password]")
@ -21,7 +21,7 @@ finalize: function(arguments) {
passwordElement.value = arguments["password"] passwordElement.value = arguments["password"]
} }
} }
if (arguments["username"]) { if (arguments["username"]) {
var usernameElement = document.querySelector("input[type=email], input[type=text]") var usernameElement = document.querySelector("input[type=email], input[type=text]")
if (usernameElement) { if (usernameElement) {

View file

@ -11,7 +11,7 @@
import UIKit import UIKit
open class PasscodeLockPresenter { open class PasscodeLockPresenter {
fileprivate var mainWindow: UIWindow? fileprivate var mainWindow: UIWindow?
fileprivate var passcodeLockWindow: UIWindow? fileprivate var passcodeLockWindow: UIWindow?
@ -21,16 +21,16 @@ open class PasscodeLockPresenter {
open func present(windowLevel: CGFloat?) { open func present(windowLevel: CGFloat?) {
guard PasscodeLock.shared.hasPasscode else { return } guard PasscodeLock.shared.hasPasscode else { return }
// dismiss the original window // dismiss the original window
dismiss() dismiss()
// new window // new window
mainWindow?.endEditing(true) mainWindow?.endEditing(true)
passcodeLockWindow = UIWindow(frame: self.mainWindow!.frame) passcodeLockWindow = UIWindow(frame: self.mainWindow!.frame)
moveWindowsToFront(windowLevel: windowLevel) moveWindowsToFront(windowLevel: windowLevel)
passcodeLockWindow?.isHidden = false passcodeLockWindow?.isHidden = false
// new vc // new vc
let passcodeLockVC = PasscodeLockViewController() let passcodeLockVC = PasscodeLockViewController()
let userDismissCompletionCallback = passcodeLockVC.dismissCompletionCallback let userDismissCompletionCallback = passcodeLockVC.dismissCompletionCallback

View file

@ -12,23 +12,23 @@ import UIKit
import LocalAuthentication import LocalAuthentication
open class PasscodeLockViewController: UIViewController, UITextFieldDelegate { open class PasscodeLockViewController: UIViewController, UITextFieldDelegate {
open var dismissCompletionCallback: (()->Void)? open var dismissCompletionCallback: (()->Void)?
open var successCallback: (()->Void)? open var successCallback: (()->Void)?
open var cancelCallback: (()->Void)? open var cancelCallback: (()->Void)?
weak var passcodeLabel: UILabel? weak var passcodeLabel: UILabel?
weak var passcodeWrongAttemptsLabel: UILabel? weak var passcodeWrongAttemptsLabel: UILabel?
weak var passcodeTextField: UITextField? weak var passcodeTextField: UITextField?
weak var biometryAuthButton: UIButton? weak var biometryAuthButton: UIButton?
open weak var cancelButton: UIButton? open weak var cancelButton: UIButton?
var passcodeFailedAttempts = 0 var passcodeFailedAttempts = 0
var isCancellable: Bool = false var isCancellable: Bool = false
open override func loadView() { open override func loadView() {
super.loadView() super.loadView()
let passcodeLabel = UILabel(frame: CGRect(x: 0, y: 0, width: 300, height: 40)) let passcodeLabel = UILabel(frame: CGRect(x: 0, y: 0, width: 300, height: 40))
passcodeLabel.text = "Enter passcode for Pass" passcodeLabel.text = "Enter passcode for Pass"
passcodeLabel.font = UIFont.boldSystemFont(ofSize: 18) passcodeLabel.font = UIFont.boldSystemFont(ofSize: 18)
@ -37,7 +37,7 @@ open class PasscodeLockViewController: UIViewController, UITextFieldDelegate {
passcodeLabel.translatesAutoresizingMaskIntoConstraints = false passcodeLabel.translatesAutoresizingMaskIntoConstraints = false
self.view.addSubview(passcodeLabel) self.view.addSubview(passcodeLabel)
self.passcodeLabel = passcodeLabel self.passcodeLabel = passcodeLabel
let passcodeWrongAttemptsLabel = UILabel(frame: CGRect(x: 0, y: 0, width: 300, height: 40)) let passcodeWrongAttemptsLabel = UILabel(frame: CGRect(x: 0, y: 0, width: 300, height: 40))
passcodeWrongAttemptsLabel.text = "" passcodeWrongAttemptsLabel.text = ""
passcodeWrongAttemptsLabel.textColor = UIColor.red passcodeWrongAttemptsLabel.textColor = UIColor.red
@ -45,7 +45,7 @@ open class PasscodeLockViewController: UIViewController, UITextFieldDelegate {
passcodeWrongAttemptsLabel.translatesAutoresizingMaskIntoConstraints = false passcodeWrongAttemptsLabel.translatesAutoresizingMaskIntoConstraints = false
self.view.addSubview(passcodeWrongAttemptsLabel) self.view.addSubview(passcodeWrongAttemptsLabel)
self.passcodeWrongAttemptsLabel = passcodeWrongAttemptsLabel self.passcodeWrongAttemptsLabel = passcodeWrongAttemptsLabel
let passcodeTextField = UITextField(frame: CGRect(x: 0, y: 0, width: 300, height: 40)) let passcodeTextField = UITextField(frame: CGRect(x: 0, y: 0, width: 300, height: 40))
passcodeTextField.borderStyle = UITextBorderStyle.roundedRect passcodeTextField.borderStyle = UITextBorderStyle.roundedRect
passcodeTextField.placeholder = "passcode" passcodeTextField.placeholder = "passcode"
@ -57,7 +57,7 @@ open class PasscodeLockViewController: UIViewController, UITextFieldDelegate {
passcodeTextField.translatesAutoresizingMaskIntoConstraints = false passcodeTextField.translatesAutoresizingMaskIntoConstraints = false
self.view.addSubview(passcodeTextField) self.view.addSubview(passcodeTextField)
self.passcodeTextField = passcodeTextField self.passcodeTextField = passcodeTextField
let biometryAuthButton = UIButton(type: .custom) let biometryAuthButton = UIButton(type: .custom)
biometryAuthButton.setTitle("", for: .normal) biometryAuthButton.setTitle("", for: .normal)
biometryAuthButton.setTitleColor(Globals.blue, for: .normal) biometryAuthButton.setTitleColor(Globals.blue, for: .normal)
@ -66,7 +66,7 @@ open class PasscodeLockViewController: UIViewController, UITextFieldDelegate {
biometryAuthButton.translatesAutoresizingMaskIntoConstraints = false biometryAuthButton.translatesAutoresizingMaskIntoConstraints = false
self.view.addSubview(biometryAuthButton) self.view.addSubview(biometryAuthButton)
self.biometryAuthButton = biometryAuthButton self.biometryAuthButton = biometryAuthButton
let myContext = LAContext() let myContext = LAContext()
var authError: NSError? var authError: NSError?
if #available(iOS 8.0, macOS 10.12.1, *) { if #available(iOS 8.0, macOS 10.12.1, *) {
@ -81,7 +81,7 @@ open class PasscodeLockViewController: UIViewController, UITextFieldDelegate {
biometryAuthButton.isHidden = false biometryAuthButton.isHidden = false
} }
} }
let cancelButton = UIButton(type: .custom) let cancelButton = UIButton(type: .custom)
cancelButton.setTitle("Cancel", for: .normal) cancelButton.setTitle("Cancel", for: .normal)
cancelButton.setTitleColor(Globals.blue, for: .normal) cancelButton.setTitleColor(Globals.blue, for: .normal)
@ -91,7 +91,7 @@ open class PasscodeLockViewController: UIViewController, UITextFieldDelegate {
cancelButton.contentHorizontalAlignment = UIControlContentHorizontalAlignment.left cancelButton.contentHorizontalAlignment = UIControlContentHorizontalAlignment.left
self.view.addSubview(cancelButton) self.view.addSubview(cancelButton)
self.cancelButton = cancelButton self.cancelButton = cancelButton
NSLayoutConstraint.activate([ NSLayoutConstraint.activate([
passcodeTextField.widthAnchor.constraint(equalToConstant: 300), passcodeTextField.widthAnchor.constraint(equalToConstant: 300),
passcodeTextField.heightAnchor.constraint(equalToConstant: 40), passcodeTextField.heightAnchor.constraint(equalToConstant: 40),
@ -118,13 +118,13 @@ open class PasscodeLockViewController: UIViewController, UITextFieldDelegate {
cancelButton.topAnchor.constraint(equalTo: self.view.safeTopAnchor), cancelButton.topAnchor.constraint(equalTo: self.view.safeTopAnchor),
cancelButton.leftAnchor.constraint(equalTo: self.view.safeLeftAnchor, constant: 20) cancelButton.leftAnchor.constraint(equalTo: self.view.safeLeftAnchor, constant: 20)
]) ])
} }
open override func viewDidLoad() { open override func viewDidLoad() {
super.viewDidLoad() super.viewDidLoad()
} }
open override func viewDidAppear(_ animated: Bool) { open override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated) super.viewDidAppear(animated)
if let biometryAuthButton = biometryAuthButton { if let biometryAuthButton = biometryAuthButton {
@ -137,7 +137,7 @@ open class PasscodeLockViewController: UIViewController, UITextFieldDelegate {
DispatchQueue.main.async { DispatchQueue.main.async {
self.passcodeTextField?.text = "" self.passcodeTextField?.text = ""
} }
// pop // pop
if presentingViewController?.presentedViewController == self { if presentingViewController?.presentedViewController == self {
// if presented as modal // if presented as modal
@ -160,16 +160,16 @@ open class PasscodeLockViewController: UIViewController, UITextFieldDelegate {
passcodeWrongAttemptsLabel?.text = "" passcodeWrongAttemptsLabel?.text = ""
dismissPasscodeLock(completionHandler: successCallback) dismissPasscodeLock(completionHandler: successCallback)
} }
@objc func passcodeLockDidCancel() { @objc func passcodeLockDidCancel() {
dismissPasscodeLock(completionHandler: cancelCallback) dismissPasscodeLock(completionHandler: cancelCallback)
} }
@objc func bioButtonPressedAction(_ uiButton: UIButton) { @objc func bioButtonPressedAction(_ uiButton: UIButton) {
let myContext = LAContext() let myContext = LAContext()
let myLocalizedReasonString = "Authentication is needed to access Pass." let myLocalizedReasonString = "Authentication is needed to access Pass."
var authError: NSError? var authError: NSError?
if #available(iOS 8.0, *) { if #available(iOS 8.0, *) {
if myContext.canEvaluatePolicy(.deviceOwnerAuthenticationWithBiometrics, error: &authError) { if myContext.canEvaluatePolicy(.deviceOwnerAuthenticationWithBiometrics, error: &authError) {
myContext.evaluatePolicy(.deviceOwnerAuthenticationWithBiometrics, localizedReason: myLocalizedReasonString) { success, evaluateError in myContext.evaluatePolicy(.deviceOwnerAuthenticationWithBiometrics, localizedReason: myLocalizedReasonString) { success, evaluateError in
@ -183,7 +183,7 @@ open class PasscodeLockViewController: UIViewController, UITextFieldDelegate {
} }
} }
} }
public override func textFieldShouldReturn(_ textField: UITextField) -> Bool { public override func textFieldShouldReturn(_ textField: UITextField) -> Bool {
if textField == passcodeTextField { if textField == passcodeTextField {
if !PasscodeLock.shared.check(passcode: textField.text ?? "") { if !PasscodeLock.shared.check(passcode: textField.text ?? "") {
@ -198,13 +198,13 @@ open class PasscodeLockViewController: UIViewController, UITextFieldDelegate {
textField.resignFirstResponder() textField.resignFirstResponder()
return true return true
} }
@objc func passcodeTextFieldDidChange(_ textField: UITextField) { @objc func passcodeTextFieldDidChange(_ textField: UITextField) {
if PasscodeLock.shared.check(passcode: textField.text ?? "") { if PasscodeLock.shared.check(passcode: textField.text ?? "") {
self.passcodeLockDidSucceed() self.passcodeLockDidSucceed()
} }
} }
public func setCancellable(_ isCancellable: Bool) { public func setCancellable(_ isCancellable: Bool) {
self.isCancellable = isCancellable self.isCancellable = isCancellable
cancelButton?.isHidden = !isCancellable cancelButton?.isHidden = !isCancellable

View file

@ -15,10 +15,10 @@ public extension DefaultsKeys {
static let pgpKeySource = DefaultsKey<String?>("pgpKeySource") static let pgpKeySource = DefaultsKey<String?>("pgpKeySource")
static let pgpPublicKeyURL = DefaultsKey<URL?>("pgpPublicKeyURL") static let pgpPublicKeyURL = DefaultsKey<URL?>("pgpPublicKeyURL")
static let pgpPrivateKeyURL = DefaultsKey<URL?>("pgpPrivateKeyURL") static let pgpPrivateKeyURL = DefaultsKey<URL?>("pgpPrivateKeyURL")
static let pgpPublicKeyArmor = DefaultsKey<String?>("pgpPublicKeyArmor") static let pgpPublicKeyArmor = DefaultsKey<String?>("pgpPublicKeyArmor")
static let pgpPrivateKeyArmor = DefaultsKey<String?>("pgpPrivateKeyArmor") static let pgpPrivateKeyArmor = DefaultsKey<String?>("pgpPrivateKeyArmor")
static let gitURL = DefaultsKey<URL?>("gitURL") static let gitURL = DefaultsKey<URL?>("gitURL")
static let gitAuthenticationMethod = DefaultsKey<String?>("gitAuthenticationMethod") static let gitAuthenticationMethod = DefaultsKey<String?>("gitAuthenticationMethod")
static let gitUsername = DefaultsKey<String?>("gitUsername") static let gitUsername = DefaultsKey<String?>("gitUsername")
@ -29,10 +29,10 @@ public extension DefaultsKeys {
static let gitSignatureEmail = DefaultsKey<String?>("gitSignatureEmail") static let gitSignatureEmail = DefaultsKey<String?>("gitSignatureEmail")
static let lastSyncedTime = DefaultsKey<Date?>("lastSyncedTime") static let lastSyncedTime = DefaultsKey<Date?>("lastSyncedTime")
static let isTouchIDOn = DefaultsKey<Bool>("isTouchIDOn") static let isTouchIDOn = DefaultsKey<Bool>("isTouchIDOn")
static let passcodeKey = DefaultsKey<String?>("passcodeKey") static let passcodeKey = DefaultsKey<String?>("passcodeKey")
static let isHideUnknownOn = DefaultsKey<Bool>("isHideUnknownOn") static let isHideUnknownOn = DefaultsKey<Bool>("isHideUnknownOn")
static let isHideOTPOn = DefaultsKey<Bool>("isHideOTPOn") static let isHideOTPOn = DefaultsKey<Bool>("isHideOTPOn")
static let isRememberPGPPassphraseOn = DefaultsKey<Bool>("isRememberPGPPassphraseOn") static let isRememberPGPPassphraseOn = DefaultsKey<Bool>("isRememberPGPPassphraseOn")
@ -40,6 +40,6 @@ public extension DefaultsKeys {
static let isShowFolderOn = DefaultsKey<Bool>("isShowFolderOn") static let isShowFolderOn = DefaultsKey<Bool>("isShowFolderOn")
static let isSearchDefaultAll = DefaultsKey<Bool>("isSearchDefaultAll") static let isSearchDefaultAll = DefaultsKey<Bool>("isSearchDefaultAll")
static let passwordGeneratorFlavor = DefaultsKey<String>("passwordGeneratorFlavor") static let passwordGeneratorFlavor = DefaultsKey<String>("passwordGeneratorFlavor")
static let encryptInArmored = DefaultsKey<Bool>("encryptInArmored") static let encryptInArmored = DefaultsKey<Bool>("encryptInArmored")
} }

View file

@ -10,7 +10,7 @@ import Foundation
// https://gist.github.com/NikolaiRuhe/eeb135d20c84a7097516 // https://gist.github.com/NikolaiRuhe/eeb135d20c84a7097516
public extension FileManager { public extension FileManager {
/// This method calculates the accumulated size of a directory on the volume in bytes. /// This method calculates the accumulated size of a directory on the volume in bytes.
/// ///
/// As there's no simple way to get this information from the file system it has to crawl the entire hierarchy, /// As there's no simple way to get this information from the file system it has to crawl the entire hierarchy,
@ -20,73 +20,73 @@ public extension FileManager {
/// - note: There are a couple of oddities that are not taken into account (like symbolic links, meta data of /// - note: There are a couple of oddities that are not taken into account (like symbolic links, meta data of
/// directories, hard links, ...). /// directories, hard links, ...).
func allocatedSizeOfDirectoryAtURL(directoryURL : URL) throws -> UInt64 { func allocatedSizeOfDirectoryAtURL(directoryURL : URL) throws -> UInt64 {
// We'll sum up content size here: // We'll sum up content size here:
var accumulatedSize = UInt64(0) var accumulatedSize = UInt64(0)
// prefetching some properties during traversal will speed up things a bit. // prefetching some properties during traversal will speed up things a bit.
let prefetchedProperties = [ let prefetchedProperties = [
URLResourceKey.isRegularFileKey, URLResourceKey.isRegularFileKey,
URLResourceKey.fileAllocatedSizeKey, URLResourceKey.fileAllocatedSizeKey,
URLResourceKey.totalFileAllocatedSizeKey, URLResourceKey.totalFileAllocatedSizeKey,
] ]
// The error handler simply signals errors to outside code. // The error handler simply signals errors to outside code.
var errorDidOccur: Error? var errorDidOccur: Error?
let errorHandler: (URL, Error) -> Bool = { _, error in let errorHandler: (URL, Error) -> Bool = { _, error in
errorDidOccur = error errorDidOccur = error
return false return false
} }
// We have to enumerate all directory contents, including subdirectories. // We have to enumerate all directory contents, including subdirectories.
let enumerator = self.enumerator(at: directoryURL, let enumerator = self.enumerator(at: directoryURL,
includingPropertiesForKeys: prefetchedProperties, includingPropertiesForKeys: prefetchedProperties,
options: FileManager.DirectoryEnumerationOptions(), options: FileManager.DirectoryEnumerationOptions(),
errorHandler: errorHandler) errorHandler: errorHandler)
precondition(enumerator != nil) precondition(enumerator != nil)
// Start the traversal: // Start the traversal:
for item in enumerator! { for item in enumerator! {
let contentItemURL = item as! NSURL let contentItemURL = item as! NSURL
// Bail out on errors from the errorHandler. // Bail out on errors from the errorHandler.
if let error = errorDidOccur { throw error } if let error = errorDidOccur { throw error }
let resourceValueForKey: (URLResourceKey) throws -> NSNumber? = { key in let resourceValueForKey: (URLResourceKey) throws -> NSNumber? = { key in
var value: AnyObject? var value: AnyObject?
try contentItemURL.getResourceValue(&value, forKey: key) try contentItemURL.getResourceValue(&value, forKey: key)
return value as? NSNumber return value as? NSNumber
} }
// Get the type of this item, making sure we only sum up sizes of regular files. // Get the type of this item, making sure we only sum up sizes of regular files.
guard let isRegularFile = try resourceValueForKey(URLResourceKey.isRegularFileKey) else { guard let isRegularFile = try resourceValueForKey(URLResourceKey.isRegularFileKey) else {
preconditionFailure() preconditionFailure()
} }
guard isRegularFile.boolValue else { guard isRegularFile.boolValue else {
continue continue
} }
// To get the file's size we first try the most comprehensive value in terms of what the file may use on disk. // To get the file's size we first try the most comprehensive value in terms of what the file may use on disk.
// This includes metadata, compression (on file system level) and block size. // This includes metadata, compression (on file system level) and block size.
var fileSize = try resourceValueForKey(URLResourceKey.totalFileAllocatedSizeKey) var fileSize = try resourceValueForKey(URLResourceKey.totalFileAllocatedSizeKey)
// In case the value is unavailable we use the fallback value (excluding meta data and compression) // In case the value is unavailable we use the fallback value (excluding meta data and compression)
// This value should always be available. // This value should always be available.
fileSize = try fileSize ?? resourceValueForKey(URLResourceKey.fileAllocatedSizeKey) fileSize = try fileSize ?? resourceValueForKey(URLResourceKey.fileAllocatedSizeKey)
guard let size = fileSize else { guard let size = fileSize else {
preconditionFailure("huh? NSURLFileAllocatedSizeKey should always return a value") preconditionFailure("huh? NSURLFileAllocatedSizeKey should always return a value")
} }
// We're good, add up the value. // We're good, add up the value.
accumulatedSize += size.uint64Value accumulatedSize += size.uint64Value
} }
// Bail out on errors from the errorHandler. // Bail out on errors from the errorHandler.
if let error = errorDidOccur { throw error } if let error = errorDidOccur { throw error }
// We finally got it. // We finally got it.
return accumulatedSize return accumulatedSize
} }

View file

@ -10,7 +10,7 @@ import Foundation
import UIKit import UIKit
public class Globals { public class Globals {
// Legacy paths (not shared) // Legacy paths (not shared)
public static let documentPathLegacy = NSSearchPathForDirectoriesInDomains(.documentDirectory, .userDomainMask, true)[0]; public static let documentPathLegacy = NSSearchPathForDirectoriesInDomains(.documentDirectory, .userDomainMask, true)[0];
public static let libraryPathLegacy = NSSearchPathForDirectoriesInDomains(.libraryDirectory, .userDomainMask, true)[0]; public static let libraryPathLegacy = NSSearchPathForDirectoriesInDomains(.libraryDirectory, .userDomainMask, true)[0];
@ -19,11 +19,11 @@ public class Globals {
public static let gitSSHPrivateKeyPathLegacy = "\(documentPathLegacy)/ssh_key" public static let gitSSHPrivateKeyPathLegacy = "\(documentPathLegacy)/ssh_key"
public static let gitSSHPrivateKeyURLLegacy = URL(fileURLWithPath: gitSSHPrivateKeyPathLegacy) public static let gitSSHPrivateKeyURLLegacy = URL(fileURLWithPath: gitSSHPrivateKeyPathLegacy)
public static let repositoryPathLegacy = "\(libraryPathLegacy)/password-store" public static let repositoryPathLegacy = "\(libraryPathLegacy)/password-store"
public static let bundleIdentifier = "me.mssun.passforios" public static let bundleIdentifier = "me.mssun.passforios"
public static let groupIdentifier = "group." + bundleIdentifier public static let groupIdentifier = "group." + bundleIdentifier
public static let passKitBundleIdentifier = bundleIdentifier + ".passKit" public static let passKitBundleIdentifier = bundleIdentifier + ".passKit"
public static let sharedContainerURL = FileManager.default.containerURL(forSecurityApplicationGroupIdentifier: groupIdentifier)! public static let sharedContainerURL = FileManager.default.containerURL(forSecurityApplicationGroupIdentifier: groupIdentifier)!
public static let documentPath = sharedContainerURL.appendingPathComponent("Documents").path public static let documentPath = sharedContainerURL.appendingPathComponent("Documents").path
public static let libraryPath = sharedContainerURL.appendingPathComponent("Library").path public static let libraryPath = sharedContainerURL.appendingPathComponent("Library").path
@ -33,19 +33,19 @@ public class Globals {
public static let gitSSHPrivateKeyURL = URL(fileURLWithPath: gitSSHPrivateKeyPath) public static let gitSSHPrivateKeyURL = URL(fileURLWithPath: gitSSHPrivateKeyPath)
public static let repositoryPath = libraryPath + "/password-store" public static let repositoryPath = libraryPath + "/password-store"
public static let dbPath = documentPath + "/pass.sqlite" public static let dbPath = documentPath + "/pass.sqlite"
public static let iTunesFileSharingPath = NSSearchPathForDirectoriesInDomains(.documentDirectory, .userDomainMask, true)[0] public static let iTunesFileSharingPath = NSSearchPathForDirectoriesInDomains(.documentDirectory, .userDomainMask, true)[0]
public static let iTunesFileSharingPGPPublic = iTunesFileSharingPath + "/gpg_key.pub" public static let iTunesFileSharingPGPPublic = iTunesFileSharingPath + "/gpg_key.pub"
public static let iTunesFileSharingPGPPrivate = iTunesFileSharingPath + "/gpg_key" public static let iTunesFileSharingPGPPrivate = iTunesFileSharingPath + "/gpg_key"
public static let iTunesFileSharingSSHPrivate = iTunesFileSharingPath + "/ssh_key" public static let iTunesFileSharingSSHPrivate = iTunesFileSharingPath + "/ssh_key"
public static let gitSignatureDefaultName = "Pass for iOS" public static let gitSignatureDefaultName = "Pass for iOS"
public static let gitSignatureDefaultEmail = "user@passforios" public static let gitSignatureDefaultEmail = "user@passforios"
public static let passwordDots = "••••••••••••" public static let passwordDots = "••••••••••••"
public static let oneTimePasswordDots = "••••••" public static let oneTimePasswordDots = "••••••"
public static let passwordFont = UIFont(name: "Courier-Bold", size: UIFont.labelFontSize - 1) public static let passwordFont = UIFont(name: "Courier-Bold", size: UIFont.labelFontSize - 1)
// UI related // UI related
public static let red = UIColor(red:1.00, green:0.23, blue:0.19, alpha:1.0) public static let red = UIColor(red:1.00, green:0.23, blue:0.19, alpha:1.0)
public static let blue = UIColor(red:0.00, green:0.48, blue:1.00, alpha:1.0) public static let blue = UIColor(red:0.00, green:0.48, blue:1.00, alpha:1.0)
@ -53,7 +53,7 @@ public class Globals {
public static let symbolColor = UIColor(red:200/255.0, green:40/255.0, blue:41/255.0, alpha:1.0) public static let symbolColor = UIColor(red:200/255.0, green:40/255.0, blue:41/255.0, alpha:1.0)
public static let digitColor = UIColor(red:66/255.0, green:113/255.0, blue:174/255.0, alpha:1.0) public static let digitColor = UIColor(red:66/255.0, green:113/255.0, blue:174/255.0, alpha:1.0)
public static let tableCellButtonSize = CGFloat(20.0) public static let tableCellButtonSize = CGFloat(20.0)
private init() { } private init() { }
} }

View file

@ -13,7 +13,7 @@ public extension Notification.Name {
static let passwordStoreErased = Notification.Name("passwordStoreErased") static let passwordStoreErased = Notification.Name("passwordStoreErased")
static let passwordStoreChangeDiscarded = Notification.Name("passwordStoreChangeDiscarded") static let passwordStoreChangeDiscarded = Notification.Name("passwordStoreChangeDiscarded")
static let passwordSearch = Notification.Name("passwordSearch") static let passwordSearch = Notification.Name("passwordSearch")
static let passwordDisplaySettingChanged = Notification.Name("passwordDisplaySettingChanged") static let passwordDisplaySettingChanged = Notification.Name("passwordDisplaySettingChanged")
static let passwordDetailDisplaySettingChanged = Notification.Name("passwordDetailDisplaySettingChanged") static let passwordDetailDisplaySettingChanged = Notification.Name("passwordDetailDisplaySettingChanged")
} }

View file

@ -9,7 +9,7 @@
import Foundation import Foundation
extension UIView { extension UIView {
// Save anchors: https://stackoverflow.com/questions/46317061/use-safe-area-layout-programmatically // Save anchors: https://stackoverflow.com/questions/46317061/use-safe-area-layout-programmatically
var safeTopAnchor: NSLayoutYAxisAnchor { var safeTopAnchor: NSLayoutYAxisAnchor {
if #available(iOS 11.0, *) { if #available(iOS 11.0, *) {
@ -18,7 +18,7 @@ extension UIView {
return self.topAnchor return self.topAnchor
} }
} }
var safeLeftAnchor: NSLayoutXAxisAnchor { var safeLeftAnchor: NSLayoutXAxisAnchor {
if #available(iOS 11.0, *){ if #available(iOS 11.0, *){
return self.safeAreaLayoutGuide.leftAnchor return self.safeAreaLayoutGuide.leftAnchor
@ -26,7 +26,7 @@ extension UIView {
return self.leftAnchor return self.leftAnchor
} }
} }
var safeRightAnchor: NSLayoutXAxisAnchor { var safeRightAnchor: NSLayoutXAxisAnchor {
if #available(iOS 11.0, *){ if #available(iOS 11.0, *){
return self.safeAreaLayoutGuide.rightAnchor return self.safeAreaLayoutGuide.rightAnchor
@ -34,7 +34,7 @@ extension UIView {
return self.rightAnchor return self.rightAnchor
} }
} }
var safeBottomAnchor: NSLayoutYAxisAnchor { var safeBottomAnchor: NSLayoutYAxisAnchor {
if #available(iOS 11.0, *) { if #available(iOS 11.0, *) {
return self.safeAreaLayoutGuide.bottomAnchor return self.safeAreaLayoutGuide.bottomAnchor

View file

@ -15,17 +15,17 @@ public class Utils {
let keychain = Keychain(service: Globals.bundleIdentifier, accessGroup: Globals.groupIdentifier) let keychain = Keychain(service: Globals.bundleIdentifier, accessGroup: Globals.groupIdentifier)
return (try? keychain.getString(name)) ?? nil return (try? keychain.getString(name)) ?? nil
} }
public static func addPasswordToKeychain(name: String, password: String?) { public static func addPasswordToKeychain(name: String, password: String?) {
let keychain = Keychain(service: Globals.bundleIdentifier, accessGroup: Globals.groupIdentifier) let keychain = Keychain(service: Globals.bundleIdentifier, accessGroup: Globals.groupIdentifier)
keychain[name] = password keychain[name] = password
} }
public static func removeKeychain(name: String) { public static func removeKeychain(name: String) {
let keychain = Keychain(service: Globals.bundleIdentifier, accessGroup: Globals.groupIdentifier) let keychain = Keychain(service: Globals.bundleIdentifier, accessGroup: Globals.groupIdentifier)
try? keychain.remove(name) try? keychain.remove(name)
} }
public static func removeAllKeychain() { public static func removeAllKeychain() {
let keychain = Keychain(service: Globals.bundleIdentifier, accessGroup: Globals.groupIdentifier) let keychain = Keychain(service: Globals.bundleIdentifier, accessGroup: Globals.groupIdentifier)
try? keychain.removeAll() try? keychain.removeAll()
@ -53,7 +53,7 @@ public class Utils {
} }
return attributedPassword return attributedPassword
} }
public static func alert(title: String, message: String, controller: UIViewController, handler: ((UIAlertAction) -> Void)? = nil, completion: (() -> Void)? = nil) { public static func alert(title: String, message: String, controller: UIViewController, handler: ((UIAlertAction) -> Void)? = nil, completion: (() -> Void)? = nil) {
let alert = UIAlertController(title: title, message: message, preferredStyle: UIAlertControllerStyle.alert) let alert = UIAlertController(title: title, message: message, preferredStyle: UIAlertControllerStyle.alert)
alert.addAction(UIAlertAction(title: "OK", style: UIAlertActionStyle.default, handler: handler)) alert.addAction(UIAlertAction(title: "OK", style: UIAlertActionStyle.default, handler: handler))

View file

@ -14,21 +14,21 @@ import ObjectiveGit
public struct GitCredential { public struct GitCredential {
private var credential: Credential private var credential: Credential
private let passwordStore = PasswordStore.shared private let passwordStore = PasswordStore.shared
public enum Credential { public enum Credential {
case http(userName: String) case http(userName: String)
case ssh(userName: String, privateKeyFile: URL) case ssh(userName: String, privateKeyFile: URL)
} }
public init(credential: Credential) { public init(credential: Credential) {
self.credential = credential self.credential = credential
} }
public func credentialProvider(requestGitPassword: @escaping (Credential, String?) -> String?) throws -> GTCredentialProvider { public func credentialProvider(requestGitPassword: @escaping (Credential, String?) -> String?) throws -> GTCredentialProvider {
var attempts = 0 var attempts = 0
return GTCredentialProvider { (_, _, _) -> (GTCredential?) in return GTCredentialProvider { (_, _, _) -> (GTCredential?) in
var credential: GTCredential? = nil var credential: GTCredential? = nil
switch self.credential { switch self.credential {
case let .http(userName): case let .http(userName):
var lastPassword = self.passwordStore.gitPassword var lastPassword = self.passwordStore.gitPassword
@ -63,7 +63,7 @@ public struct GitCredential {
return credential return credential
} }
} }
public func delete() { public func delete() {
switch credential { switch credential {
case .http: case .http:

View file

@ -11,24 +11,24 @@ import LocalAuthentication
open class PasscodeLock { open class PasscodeLock {
public static let shared = PasscodeLock() public static let shared = PasscodeLock()
fileprivate let passcodeKey = "passcode.lock.passcode" fileprivate let passcodeKey = "passcode.lock.passcode"
fileprivate var passcode: String? { fileprivate var passcode: String? {
return SharedDefaults[.passcodeKey] return SharedDefaults[.passcodeKey]
} }
public var hasPasscode: Bool { public var hasPasscode: Bool {
return passcode != nil return passcode != nil
} }
public func save(passcode: String) { public func save(passcode: String) {
SharedDefaults[.passcodeKey] = passcode SharedDefaults[.passcodeKey] = passcode
} }
public func check(passcode: String) -> Bool { public func check(passcode: String) -> Bool {
return self.passcode == passcode return self.passcode == passcode
} }
public func delete() { public func delete() {
SharedDefaults[.passcodeKey] = nil SharedDefaults[.passcodeKey] = nil
} }

View file

@ -10,7 +10,7 @@ import OneTimePassword
import Base32 import Base32
public class Password { public class Password {
public var name: String public var name: String
public var url: URL public var url: URL
public var plainText: String public var plainText: String
@ -50,7 +50,7 @@ public class Password {
public var login: String? { public var login: String? {
return getAdditionValue(withKey: Constants.LOGIN_KEYWORD) return getAdditionValue(withKey: Constants.LOGIN_KEYWORD)
} }
public var urlString: String? { public var urlString: String? {
return getAdditionValue(withKey: Constants.URL_KEYWORD) return getAdditionValue(withKey: Constants.URL_KEYWORD)
} }
@ -73,7 +73,7 @@ public class Password {
self.plainText = plainText self.plainText = plainText
initEverything() initEverything()
} }
public func updatePassword(name: String, url: URL, plainText: String) { public func updatePassword(name: String, url: URL, plainText: String) {
guard self.plainText != plainText || self.url != url else { guard self.plainText != plainText || self.url != url else {
@ -129,7 +129,7 @@ public class Password {
let toLowercase = { (string: String) -> String in caseSensitive ? string : string.lowercased() } let toLowercase = { (string: String) -> String in caseSensitive ? string : string.lowercased() }
return additions.first(where: { toLowercase($0.title) == toLowercase(key) })?.content return additions.first(where: { toLowercase($0.title) == toLowercase(key) })?.content
} }
/// Set the OTP token if we are able to construct a valid one. /// Set the OTP token if we are able to construct a valid one.
/// ///
/// Example of TOTP otpauth: /// Example of TOTP otpauth:
@ -164,7 +164,7 @@ public class Password {
.usingCounter(getAdditionValue(withKey: Constants.OTP_COUNTER)) .usingCounter(getAdditionValue(withKey: Constants.OTP_COUNTER))
.build() .build()
} }
/// Get the OTP description and the current password. /// Get the OTP description and the current password.
public func getOtpStrings() -> (description: String, otp: String)? { public func getOtpStrings() -> (description: String, otp: String)? {
guard otpToken != nil else { guard otpToken != nil else {
@ -178,18 +178,18 @@ public class Password {
} }
return (description, otpToken!.currentPassword ?? "error") return (description, otpToken!.currentPassword ?? "error")
} }
// return the password strings // return the password strings
// it is guaranteed that it is a HOTP password when we call this // it is guaranteed that it is a HOTP password when we call this
public func getNextHotp() -> String? { public func getNextHotp() -> String? {
// increase the counter // increase the counter
otpToken = otpToken?.updatedToken() otpToken = otpToken?.updatedToken()
// replace old HOTP settings with the new otpauth // replace old HOTP settings with the new otpauth
var newOtpauth = try! otpToken?.toURL().absoluteString var newOtpauth = try! otpToken?.toURL().absoluteString
newOtpauth?.append("&secret=") newOtpauth?.append("&secret=")
newOtpauth?.append(MF_Base32Codec.base32String(from: otpToken?.generator.secret)) newOtpauth?.append(MF_Base32Codec.base32String(from: otpToken?.generator.secret))
var lines : [String] = [] var lines : [String] = []
self.plainText.enumerateLines() { line, _ in self.plainText.enumerateLines() { line, _ in
let (key, _) = Parser.getKeyValuePair(from: line) let (key, _) = Parser.getKeyValuePair(from: line)
@ -205,7 +205,7 @@ public class Password {
lines.append(newOtpauth!) lines.append(newOtpauth!)
} }
self.updatePassword(name: self.name, url: self.url, plainText: lines.joined(separator: "\n")) self.updatePassword(name: self.name, url: self.url, plainText: lines.joined(separator: "\n"))
// get and return the password // get and return the password
return self.otpToken?.currentPassword return self.otpToken?.currentPassword
} }

View file

@ -10,7 +10,7 @@ import Foundation
import SwiftyUserDefaults import SwiftyUserDefaults
extension PasswordEntity { extension PasswordEntity {
public var nameWithCategory: String { public var nameWithCategory: String {
get { get {
if let p = path, p.hasSuffix(".gpg") { if let p = path, p.hasSuffix(".gpg") {
@ -20,11 +20,11 @@ extension PasswordEntity {
} }
} }
} }
public func getCategoryText() -> String { public func getCategoryText() -> String {
return getCategoryArray().joined(separator: " > ") return getCategoryArray().joined(separator: " > ")
} }
public func getCategoryArray() -> [String] { public func getCategoryArray() -> [String] {
var parentEntity = parent var parentEntity = parent
var passwordCategoryArray: [String] = [] var passwordCategoryArray: [String] = []
@ -35,7 +35,7 @@ extension PasswordEntity {
passwordCategoryArray.reverse() passwordCategoryArray.reverse()
return passwordCategoryArray return passwordCategoryArray
} }
public func getURL() -> URL? { public func getURL() -> URL? {
if let p = getPath().stringByAddingPercentEncodingForRFC3986() { if let p = getPath().stringByAddingPercentEncodingForRFC3986() {
return URL(string: p) return URL(string: p)

View file

@ -18,7 +18,7 @@ public class PasswordStore {
public static let shared = PasswordStore() public static let shared = PasswordStore()
public let storeURL = URL(fileURLWithPath: "\(Globals.repositoryPath)") public let storeURL = URL(fileURLWithPath: "\(Globals.repositoryPath)")
public let tempStoreURL = URL(fileURLWithPath: "\(Globals.repositoryPath)-temp") public let tempStoreURL = URL(fileURLWithPath: "\(Globals.repositoryPath)-temp")
public var storeRepository: GTRepository? public var storeRepository: GTRepository?
public var pgpKeyID: String? public var pgpKeyID: String?
public var publicKey: Key? { public var publicKey: Key? {
@ -31,7 +31,7 @@ public class PasswordStore {
} }
} }
public var privateKey: Key? public var privateKey: Key?
public var gitSignatureForNow: GTSignature { public var gitSignatureForNow: GTSignature {
get { get {
let gitSignatureName = SharedDefaults[.gitSignatureName] ?? Globals.gitSignatureDefaultName let gitSignatureName = SharedDefaults[.gitSignatureName] ?? Globals.gitSignatureDefaultName
@ -39,9 +39,9 @@ public class PasswordStore {
return GTSignature(name: gitSignatureName, email: gitSignatureEmail, time: Date())! return GTSignature(name: gitSignatureName, email: gitSignatureEmail, time: Date())!
} }
} }
public let keyring = ObjectivePGP.defaultKeyring public let keyring = ObjectivePGP.defaultKeyring
public var pgpKeyPassphrase: String? { public var pgpKeyPassphrase: String? {
set { set {
Utils.addPasswordToKeychain(name: "pgpKeyPassphrase", password: newValue) Utils.addPasswordToKeychain(name: "pgpKeyPassphrase", password: newValue)
@ -50,7 +50,7 @@ public class PasswordStore {
return Utils.getPasswordFromKeychain(name: "pgpKeyPassphrase") return Utils.getPasswordFromKeychain(name: "pgpKeyPassphrase")
} }
} }
public var gitPassword: String? { public var gitPassword: String? {
set { set {
Utils.addPasswordToKeychain(name: "gitPassword", password: newValue) Utils.addPasswordToKeychain(name: "gitPassword", password: newValue)
@ -59,7 +59,7 @@ public class PasswordStore {
return Utils.getPasswordFromKeychain(name: "gitPassword") return Utils.getPasswordFromKeychain(name: "gitPassword")
} }
} }
public var gitSSHPrivateKeyPassphrase: String? { public var gitSSHPrivateKeyPassphrase: String? {
set { set {
Utils.addPasswordToKeychain(name: "gitSSHPrivateKeyPassphrase", password: newValue) Utils.addPasswordToKeychain(name: "gitSSHPrivateKeyPassphrase", password: newValue)
@ -68,7 +68,7 @@ public class PasswordStore {
return Utils.getPasswordFromKeychain(name: "gitSSHPrivateKeyPassphrase") return Utils.getPasswordFromKeychain(name: "gitSSHPrivateKeyPassphrase")
} }
} }
private let fm = FileManager.default private let fm = FileManager.default
lazy private var context: NSManagedObjectContext = { lazy private var context: NSManagedObjectContext = {
let modelURL = Bundle(identifier: Globals.passKitBundleIdentifier)!.url(forResource: "pass", withExtension: "momd")! let modelURL = Bundle(identifier: Globals.passKitBundleIdentifier)!.url(forResource: "pass", withExtension: "momd")!
@ -82,7 +82,7 @@ public class PasswordStore {
if let error = error as NSError? { if let error = error as NSError? {
// Replace this implementation with code to handle the error appropriately. // Replace this implementation with code to handle the error appropriately.
// fatalError() causes the application to generate a crash log and terminate. You should not use this function in a shipping application, although it may be useful during development. // fatalError() causes the application to generate a crash log and terminate. You should not use this function in a shipping application, although it may be useful during development.
/* /*
Typical reasons for an error here include: Typical reasons for an error here include:
* The parent directory does not exist, cannot be created, or disallows writing. * The parent directory does not exist, cannot be created, or disallows writing.
@ -96,11 +96,11 @@ public class PasswordStore {
}) })
return container.viewContext return container.viewContext
}() }()
public var numberOfPasswords : Int { public var numberOfPasswords : Int {
return self.fetchPasswordEntityCoreData(withDir: false).count return self.fetchPasswordEntityCoreData(withDir: false).count
} }
public var sizeOfRepositoryByteCount : UInt64 { public var sizeOfRepositoryByteCount : UInt64 {
return (try? fm.allocatedSizeOfDirectoryAtURL(directoryURL: self.storeURL)) ?? 0 return (try? fm.allocatedSizeOfDirectoryAtURL(directoryURL: self.storeURL)) ?? 0
} }
@ -112,12 +112,12 @@ public class PasswordStore {
public var lastSyncedTime: Date? { public var lastSyncedTime: Date? {
return SharedDefaults[.lastSyncedTime] return SharedDefaults[.lastSyncedTime]
} }
private init() { private init() {
// File migration to group // File migration to group
migrateIfNeeded() migrateIfNeeded()
backwardCompatibility() backwardCompatibility()
do { do {
if fm.fileExists(atPath: storeURL.path) { if fm.fileExists(atPath: storeURL.path) {
try storeRepository = GTRepository.init(url: storeURL) try storeRepository = GTRepository.init(url: storeURL)
@ -127,14 +127,14 @@ public class PasswordStore {
print(error) print(error)
} }
} }
private func migrateIfNeeded() { private func migrateIfNeeded() {
// migrate happens only if the repository was cloned and pgp keys were set up using earlier versions // migrate happens only if the repository was cloned and pgp keys were set up using earlier versions
let needMigration = !pgpKeyExists() && !gitSSHKeyExists() && !fm.fileExists(atPath: Globals.repositoryPath) && fm.fileExists(atPath: Globals.repositoryPathLegacy) let needMigration = !pgpKeyExists() && !gitSSHKeyExists() && !fm.fileExists(atPath: Globals.repositoryPath) && fm.fileExists(atPath: Globals.repositoryPathLegacy)
guard needMigration == true else { guard needMigration == true else {
return return
} }
do { do {
// migrate Defaults // migrate Defaults
let userDefaults = UserDefaults() let userDefaults = UserDefaults()
@ -143,7 +143,7 @@ public class PasswordStore {
SharedDefaults.setValue(userDefaults.value(forKey: key), forKey: key) SharedDefaults.setValue(userDefaults.value(forKey: key), forKey: key)
} }
} }
// migrate files // migrate files
try fm.createDirectory(atPath: Globals.documentPath, withIntermediateDirectories: true, attributes: nil) try fm.createDirectory(atPath: Globals.documentPath, withIntermediateDirectories: true, attributes: nil)
try fm.createDirectory(atPath: Globals.libraryPath, withIntermediateDirectories: true, attributes: nil) try fm.createDirectory(atPath: Globals.libraryPath, withIntermediateDirectories: true, attributes: nil)
@ -162,7 +162,7 @@ public class PasswordStore {
} }
updatePasswordEntityCoreData() updatePasswordEntityCoreData()
} }
private func backwardCompatibility() { private func backwardCompatibility() {
// For the newly-introduced isRememberGitCredentialPassphraseOn (20171008) // For the newly-introduced isRememberGitCredentialPassphraseOn (20171008)
if (self.gitPassword != nil || self.gitSSHPrivateKeyPassphrase != nil) && SharedDefaults[.isRememberGitCredentialPassphraseOn] == false { if (self.gitPassword != nil || self.gitSSHPrivateKeyPassphrase != nil) && SharedDefaults[.isRememberGitCredentialPassphraseOn] == false {
@ -173,21 +173,21 @@ public class PasswordStore {
SharedDefaults[.isRememberPGPPassphraseOn] = true SharedDefaults[.isRememberPGPPassphraseOn] = true
} }
} }
enum SSHKeyType { enum SSHKeyType {
case `public`, secret case `public`, secret
} }
public func initGitSSHKey(with armorKey: String) throws { public func initGitSSHKey(with armorKey: String) throws {
let keyPath = Globals.gitSSHPrivateKeyPath let keyPath = Globals.gitSSHPrivateKeyPath
try armorKey.write(toFile: keyPath, atomically: true, encoding: .ascii) try armorKey.write(toFile: keyPath, atomically: true, encoding: .ascii)
} }
public func initPGPKeys() throws { public func initPGPKeys() throws {
try initPGPKey(.public) try initPGPKey(.public)
try initPGPKey(.secret) try initPGPKey(.secret)
} }
public func initPGPKey(_ keyType: PGPKeyType) throws { public func initPGPKey(_ keyType: PGPKeyType) throws {
switch keyType { switch keyType {
case .public: case .public:
@ -206,7 +206,7 @@ public class PasswordStore {
throw AppError.UnknownError throw AppError.UnknownError
} }
} }
public func initPGPKey(from url: URL, keyType: PGPKeyType) throws { public func initPGPKey(from url: URL, keyType: PGPKeyType) throws {
var pgpKeyLocalPath = "" var pgpKeyLocalPath = ""
if keyType == .public { if keyType == .public {
@ -218,7 +218,7 @@ public class PasswordStore {
try pgpKeyData.write(to: URL(fileURLWithPath: pgpKeyLocalPath), options: .atomic) try pgpKeyData.write(to: URL(fileURLWithPath: pgpKeyLocalPath), options: .atomic)
try initPGPKey(keyType) try initPGPKey(keyType)
} }
public func initPGPKey(with armorKey: String, keyType: PGPKeyType) throws { public func initPGPKey(with armorKey: String, keyType: PGPKeyType) throws {
var pgpKeyLocalPath = "" var pgpKeyLocalPath = ""
if keyType == .public { if keyType == .public {
@ -229,8 +229,8 @@ public class PasswordStore {
try armorKey.write(toFile: pgpKeyLocalPath, atomically: true, encoding: .ascii) try armorKey.write(toFile: pgpKeyLocalPath, atomically: true, encoding: .ascii)
try initPGPKey(keyType) try initPGPKey(keyType)
} }
private func importKey(from keyPath: String) -> Key? { private func importKey(from keyPath: String) -> Key? {
if fm.fileExists(atPath: keyPath) { if fm.fileExists(atPath: keyPath) {
let keys = try! ObjectivePGP.readKeys(fromPath: keyPath) let keys = try! ObjectivePGP.readKeys(fromPath: keyPath)
@ -245,12 +245,12 @@ public class PasswordStore {
public func getPgpPrivateKey() -> Key { public func getPgpPrivateKey() -> Key {
return keyring.keys.filter({$0.secretKey != nil})[0] return keyring.keys.filter({$0.secretKey != nil})[0]
} }
public func repositoryExisted() -> Bool { public func repositoryExisted() -> Bool {
let fm = FileManager() let fm = FileManager()
return fm.fileExists(atPath: Globals.repositoryPath) return fm.fileExists(atPath: Globals.repositoryPath)
} }
public func passwordExisted(password: Password) -> Bool { public func passwordExisted(password: Password) -> Bool {
let passwordEntityFetchRequest = NSFetchRequest<NSFetchRequestResult>(entityName: "PasswordEntity") let passwordEntityFetchRequest = NSFetchRequest<NSFetchRequestResult>(entityName: "PasswordEntity")
do { do {
@ -266,7 +266,7 @@ public class PasswordStore {
} }
return true return true
} }
public func passwordEntityExisted(path: String) -> Bool { public func passwordEntityExisted(path: String) -> Bool {
let passwordEntityFetchRequest = NSFetchRequest<NSFetchRequestResult>(entityName: "PasswordEntity") let passwordEntityFetchRequest = NSFetchRequest<NSFetchRequestResult>(entityName: "PasswordEntity")
do { do {
@ -282,7 +282,7 @@ public class PasswordStore {
} }
return true return true
} }
public func getPasswordEntity(by path: String, isDir: Bool) -> PasswordEntity? { public func getPasswordEntity(by path: String, isDir: Bool) -> PasswordEntity? {
let passwordEntityFetchRequest = NSFetchRequest<NSFetchRequestResult>(entityName: "PasswordEntity") let passwordEntityFetchRequest = NSFetchRequest<NSFetchRequestResult>(entityName: "PasswordEntity")
do { do {
@ -292,7 +292,7 @@ public class PasswordStore {
fatalError("Failed to fetch password entities: \(error)") fatalError("Failed to fetch password entities: \(error)")
} }
} }
public func cloneRepository(remoteRepoURL: URL, public func cloneRepository(remoteRepoURL: URL,
credential: GitCredential, credential: GitCredential,
requestGitPassword: @escaping (GitCredential.Credential, String?) -> String?, requestGitPassword: @escaping (GitCredential.Credential, String?) -> String?,
@ -323,7 +323,7 @@ public class PasswordStore {
NotificationCenter.default.post(name: .passwordStoreUpdated, object: nil) NotificationCenter.default.post(name: .passwordStoreUpdated, object: nil)
} }
} }
public func pullRepository(credential: GitCredential, requestGitPassword: @escaping (GitCredential.Credential, String?) -> String?, transferProgressBlock: @escaping (UnsafePointer<git_transfer_progress>, UnsafeMutablePointer<ObjCBool>) -> Void) throws { public func pullRepository(credential: GitCredential, requestGitPassword: @escaping (GitCredential.Credential, String?) -> String?, transferProgressBlock: @escaping (UnsafePointer<git_transfer_progress>, UnsafeMutablePointer<ObjCBool>) -> Void) throws {
guard let storeRepository = storeRepository else { guard let storeRepository = storeRepository else {
throw AppError.RepositoryNotSetError throw AppError.RepositoryNotSetError
@ -339,7 +339,7 @@ public class PasswordStore {
NotificationCenter.default.post(name: .passwordStoreUpdated, object: nil) NotificationCenter.default.post(name: .passwordStoreUpdated, object: nil)
} }
} }
private func updatePasswordEntityCoreData() { private func updatePasswordEntityCoreData() {
deleteCoreData(entityName: "PasswordEntity") deleteCoreData(entityName: "PasswordEntity")
do { do {
@ -393,7 +393,7 @@ public class PasswordStore {
print("Error with save: \(error)") print("Error with save: \(error)")
} }
} }
public func getRecentCommits(count: Int) throws -> [GTCommit] { public func getRecentCommits(count: Int) throws -> [GTCommit] {
guard let storeRepository = storeRepository else { guard let storeRepository = storeRepository else {
return [] return []
@ -410,7 +410,7 @@ public class PasswordStore {
} }
return commits return commits
} }
public func fetchPasswordEntityCoreData(parent: PasswordEntity?) -> [PasswordEntity] { public func fetchPasswordEntityCoreData(parent: PasswordEntity?) -> [PasswordEntity] {
let passwordEntityFetch = NSFetchRequest<NSFetchRequestResult>(entityName: "PasswordEntity") let passwordEntityFetch = NSFetchRequest<NSFetchRequestResult>(entityName: "PasswordEntity")
do { do {
@ -421,7 +421,7 @@ public class PasswordStore {
fatalError("Failed to fetch passwords: \(error)") fatalError("Failed to fetch passwords: \(error)")
} }
} }
public func fetchPasswordEntityCoreData(withDir: Bool) -> [PasswordEntity] { public func fetchPasswordEntityCoreData(withDir: Bool) -> [PasswordEntity] {
let passwordEntityFetch = NSFetchRequest<NSFetchRequestResult>(entityName: "PasswordEntity") let passwordEntityFetch = NSFetchRequest<NSFetchRequestResult>(entityName: "PasswordEntity")
do { do {
@ -434,8 +434,8 @@ public class PasswordStore {
fatalError("Failed to fetch passwords: \(error)") fatalError("Failed to fetch passwords: \(error)")
} }
} }
public func fetchUnsyncedPasswords() -> [PasswordEntity] { public func fetchUnsyncedPasswords() -> [PasswordEntity] {
let passwordEntityFetchRequest = NSFetchRequest<NSFetchRequestResult>(entityName: "PasswordEntity") let passwordEntityFetchRequest = NSFetchRequest<NSFetchRequestResult>(entityName: "PasswordEntity")
passwordEntityFetchRequest.predicate = NSPredicate(format: "synced = %i", 0) passwordEntityFetchRequest.predicate = NSPredicate(format: "synced = %i", 0)
@ -446,7 +446,7 @@ public class PasswordStore {
fatalError("Failed to fetch passwords: \(error)") fatalError("Failed to fetch passwords: \(error)")
} }
} }
public func setAllSynced() { public func setAllSynced() {
let passwordEntities = fetchUnsyncedPasswords() let passwordEntities = fetchUnsyncedPasswords()
for passwordEntity in passwordEntities { for passwordEntity in passwordEntities {
@ -460,7 +460,7 @@ public class PasswordStore {
fatalError("Failed to save: \(error)") fatalError("Failed to save: \(error)")
} }
} }
public func getLatestUpdateInfo(filename: String) -> String { public func getLatestUpdateInfo(filename: String) -> String {
guard let storeRepository = storeRepository else { guard let storeRepository = storeRepository else {
return "Unknown" return "Unknown"
@ -486,10 +486,10 @@ public class PasswordStore {
} }
return autoFormattedDifference return autoFormattedDifference
} }
public func updateRemoteRepo() { public func updateRemoteRepo() {
} }
private func gitAdd(path: String) throws { private func gitAdd(path: String) throws {
guard let storeRepository = storeRepository else { guard let storeRepository = storeRepository else {
throw AppError.RepositoryNotSetError throw AppError.RepositoryNotSetError
@ -497,7 +497,7 @@ public class PasswordStore {
try storeRepository.index().addFile(path) try storeRepository.index().addFile(path)
try storeRepository.index().write() try storeRepository.index().write()
} }
private func gitRm(path: String) throws { private func gitRm(path: String) throws {
guard let storeRepository = storeRepository else { guard let storeRepository = storeRepository else {
throw AppError.RepositoryNotSetError throw AppError.RepositoryNotSetError
@ -509,7 +509,7 @@ public class PasswordStore {
try storeRepository.index().removeFile(path) try storeRepository.index().removeFile(path)
try storeRepository.index().write() try storeRepository.index().write()
} }
private func deleteDirectoryTree(at url: URL) throws { private func deleteDirectoryTree(at url: URL) throws {
var tempURL = storeURL.appendingPathComponent(url.deletingLastPathComponent().path) var tempURL = storeURL.appendingPathComponent(url.deletingLastPathComponent().path)
var count = try fm.contentsOfDirectory(atPath: tempURL.path).count var count = try fm.contentsOfDirectory(atPath: tempURL.path).count
@ -519,12 +519,12 @@ public class PasswordStore {
count = try fm.contentsOfDirectory(atPath: tempURL.path).count count = try fm.contentsOfDirectory(atPath: tempURL.path).count
} }
} }
private func createDirectoryTree(at url: URL) throws { private func createDirectoryTree(at url: URL) throws {
let tempURL = storeURL.appendingPathComponent(url.deletingLastPathComponent().path) let tempURL = storeURL.appendingPathComponent(url.deletingLastPathComponent().path)
try fm.createDirectory(at: tempURL, withIntermediateDirectories: true, attributes: nil) try fm.createDirectory(at: tempURL, withIntermediateDirectories: true, attributes: nil)
} }
private func gitMv(from: String, to: String) throws { private func gitMv(from: String, to: String) throws {
let fromURL = storeURL.appendingPathComponent(from) let fromURL = storeURL.appendingPathComponent(from)
let toURL = storeURL.appendingPathComponent(to) let toURL = storeURL.appendingPathComponent(to)
@ -532,7 +532,7 @@ public class PasswordStore {
try gitAdd(path: to) try gitAdd(path: to)
try gitRm(path: from) try gitRm(path: from)
} }
private func gitCommit(message: String) throws -> GTCommit? { private func gitCommit(message: String) throws -> GTCommit? {
guard let storeRepository = storeRepository else { guard let storeRepository = storeRepository else {
throw AppError.RepositoryNotSetError throw AppError.RepositoryNotSetError
@ -546,7 +546,7 @@ public class PasswordStore {
let commit = try storeRepository.createCommit(with: newTree, message: message, author: signature, committer: signature, parents: [parent], updatingReferenceNamed: headReference.name) let commit = try storeRepository.createCommit(with: newTree, message: message, author: signature, committer: signature, parents: [parent], updatingReferenceNamed: headReference.name)
return commit return commit
} }
private func getLocalBranch(withName branchName: String) throws -> GTBranch? { private func getLocalBranch(withName branchName: String) throws -> GTBranch? {
guard let storeRepository = storeRepository else { guard let storeRepository = storeRepository else {
throw AppError.RepositoryNotSetError throw AppError.RepositoryNotSetError
@ -555,7 +555,7 @@ public class PasswordStore {
let branches = try storeRepository.branches(withPrefix: reference) let branches = try storeRepository.branches(withPrefix: reference)
return branches.first return branches.first
} }
public func pushRepository(credential: GitCredential, requestGitPassword: @escaping (GitCredential.Credential, String?) -> String?, transferProgressBlock: @escaping (UInt32, UInt32, Int, UnsafeMutablePointer<ObjCBool>) -> Void) throws { public func pushRepository(credential: GitCredential, requestGitPassword: @escaping (GitCredential.Credential, String?) -> String?, transferProgressBlock: @escaping (UInt32, UInt32, Int, UnsafeMutablePointer<ObjCBool>) -> Void) throws {
guard let storeRepository = storeRepository else { guard let storeRepository = storeRepository else {
throw AppError.RepositoryNotSetError throw AppError.RepositoryNotSetError
@ -571,12 +571,12 @@ public class PasswordStore {
throw(error) throw(error)
} }
} }
private func addPasswordEntities(password: Password) throws -> PasswordEntity? { private func addPasswordEntities(password: Password) throws -> PasswordEntity? {
guard !passwordExisted(password: password) else { guard !passwordExisted(password: password) else {
throw AppError.PasswordDuplicatedError throw AppError.PasswordDuplicatedError
} }
var passwordURL = password.url var passwordURL = password.url
var previousPathLength = Int.max var previousPathLength = Int.max
var paths: [String] = [] var paths: [String] = []
@ -606,7 +606,7 @@ public class PasswordStore {
} }
return nil return nil
} }
private func insertPasswordEntity(name: String, path: String, parent: PasswordEntity?, synced: Bool = false, isDir: Bool = false) -> PasswordEntity? { private func insertPasswordEntity(name: String, path: String, parent: PasswordEntity?, synced: Bool = false, isDir: Bool = false) -> PasswordEntity? {
var ret: PasswordEntity? = nil var ret: PasswordEntity? = nil
if let passwordEntity = NSEntityDescription.insertNewObject(forEntityName: "PasswordEntity", into: self.context) as? PasswordEntity { if let passwordEntity = NSEntityDescription.insertNewObject(forEntityName: "PasswordEntity", into: self.context) as? PasswordEntity {
@ -624,7 +624,7 @@ public class PasswordStore {
} }
return ret return ret
} }
public func add(password: Password) throws -> PasswordEntity? { public func add(password: Password) throws -> PasswordEntity? {
try createDirectoryTree(at: password.url) try createDirectoryTree(at: password.url)
let newPasswordEntity = try addPasswordEntities(password: password) let newPasswordEntity = try addPasswordEntities(password: password)
@ -635,7 +635,7 @@ public class PasswordStore {
NotificationCenter.default.post(name: .passwordStoreUpdated, object: nil) NotificationCenter.default.post(name: .passwordStoreUpdated, object: nil)
return newPasswordEntity return newPasswordEntity
} }
public func delete(passwordEntity: PasswordEntity) throws { public func delete(passwordEntity: PasswordEntity) throws {
let deletedFileURL = passwordEntity.getURL()! let deletedFileURL = passwordEntity.getURL()!
try gitRm(path: deletedFileURL.path) try gitRm(path: deletedFileURL.path)
@ -644,7 +644,7 @@ public class PasswordStore {
let _ = try gitCommit(message: "Remove \(deletedFileURL.deletingPathExtension().path.removingPercentEncoding!) from store using Pass for iOS.") let _ = try gitCommit(message: "Remove \(deletedFileURL.deletingPathExtension().path.removingPercentEncoding!) from store using Pass for iOS.")
NotificationCenter.default.post(name: .passwordStoreUpdated, object: nil) NotificationCenter.default.post(name: .passwordStoreUpdated, object: nil)
} }
public func edit(passwordEntity: PasswordEntity, password: Password) throws -> PasswordEntity? { public func edit(passwordEntity: PasswordEntity, password: Password) throws -> PasswordEntity? {
var newPasswordEntity: PasswordEntity? = passwordEntity var newPasswordEntity: PasswordEntity? = passwordEntity
@ -656,16 +656,16 @@ public class PasswordStore {
newPasswordEntity = passwordEntity newPasswordEntity = passwordEntity
newPasswordEntity?.synced = false newPasswordEntity?.synced = false
} }
if password.changed&PasswordChange.path.rawValue != 0 { if password.changed&PasswordChange.path.rawValue != 0 {
let deletedFileURL = passwordEntity.getURL()! let deletedFileURL = passwordEntity.getURL()!
// add // add
try createDirectoryTree(at: password.url) try createDirectoryTree(at: password.url)
newPasswordEntity = try addPasswordEntities(password: password) newPasswordEntity = try addPasswordEntities(password: password)
// mv // mv
try gitMv(from: deletedFileURL.path, to: password.url.path) try gitMv(from: deletedFileURL.path, to: password.url.path)
// delete // delete
try deleteDirectoryTree(at: deletedFileURL) try deleteDirectoryTree(at: deletedFileURL)
try deletePasswordEntities(passwordEntity: passwordEntity) try deletePasswordEntities(passwordEntity: passwordEntity)
@ -675,7 +675,7 @@ public class PasswordStore {
NotificationCenter.default.post(name: .passwordStoreUpdated, object: nil) NotificationCenter.default.post(name: .passwordStoreUpdated, object: nil)
return newPasswordEntity return newPasswordEntity
} }
private func deletePasswordEntities(passwordEntity: PasswordEntity) throws { private func deletePasswordEntities(passwordEntity: PasswordEntity) throws {
var current: PasswordEntity? = passwordEntity var current: PasswordEntity? = passwordEntity
while current != nil && (current!.children!.count == 0 || !current!.isDir) { while current != nil && (current!.children!.count == 0 || !current!.isDir) {
@ -689,7 +689,7 @@ public class PasswordStore {
} }
} }
} }
public func saveUpdated(passwordEntity: PasswordEntity) { public func saveUpdated(passwordEntity: PasswordEntity) {
do { do {
try context.save() try context.save()
@ -697,11 +697,11 @@ public class PasswordStore {
fatalError("Failed to save a PasswordEntity: \(error)") fatalError("Failed to save a PasswordEntity: \(error)")
} }
} }
public func deleteCoreData(entityName: String) { public func deleteCoreData(entityName: String) {
let deleteFetchRequest = NSFetchRequest<NSFetchRequestResult>(entityName: entityName) let deleteFetchRequest = NSFetchRequest<NSFetchRequestResult>(entityName: entityName)
let deleteRequest = NSBatchDeleteRequest(fetchRequest: deleteFetchRequest) let deleteRequest = NSBatchDeleteRequest(fetchRequest: deleteFetchRequest)
do { do {
try context.execute(deleteRequest) try context.execute(deleteRequest)
try context.save() try context.save()
@ -710,7 +710,7 @@ public class PasswordStore {
print(error) print(error)
} }
} }
public func updateImage(passwordEntity: PasswordEntity, image: Data?) { public func updateImage(passwordEntity: PasswordEntity, image: Data?) {
guard let image = image else { guard let image = image else {
return return
@ -733,7 +733,7 @@ public class PasswordStore {
} }
} }
} }
public func erase() { public func erase() {
publicKey = nil publicKey = nil
privateKey = nil privateKey = nil
@ -743,19 +743,19 @@ public class PasswordStore {
try? fm.removeItem(atPath: Globals.pgpPublicKeyPath) try? fm.removeItem(atPath: Globals.pgpPublicKeyPath)
try? fm.removeItem(atPath: Globals.pgpPrivateKeyPath) try? fm.removeItem(atPath: Globals.pgpPrivateKeyPath)
try? fm.removeItem(atPath: Globals.gitSSHPrivateKeyPath) try? fm.removeItem(atPath: Globals.gitSSHPrivateKeyPath)
Utils.removeAllKeychain() Utils.removeAllKeychain()
deleteCoreData(entityName: "PasswordEntity") deleteCoreData(entityName: "PasswordEntity")
SharedDefaults.removeAll() SharedDefaults.removeAll()
storeRepository = nil storeRepository = nil
NotificationCenter.default.post(name: .passwordStoreUpdated, object: nil) NotificationCenter.default.post(name: .passwordStoreUpdated, object: nil)
NotificationCenter.default.post(name: .passwordStoreErased, object: nil) NotificationCenter.default.post(name: .passwordStoreErased, object: nil)
} }
// return the number of discarded commits // return the number of discarded commits
public func reset() throws -> Int { public func reset() throws -> Int {
guard let storeRepository = storeRepository else { guard let storeRepository = storeRepository else {
throw AppError.RepositoryNotSetError throw AppError.RepositoryNotSetError
@ -772,7 +772,7 @@ public class PasswordStore {
try storeRepository.reset(to: newHead, resetType: .hard) try storeRepository.reset(to: newHead, resetType: .hard)
self.setAllSynced() self.setAllSynced()
self.updatePasswordEntityCoreData() self.updatePasswordEntityCoreData()
NotificationCenter.default.post(name: .passwordStoreUpdated, object: nil) NotificationCenter.default.post(name: .passwordStoreUpdated, object: nil)
NotificationCenter.default.post(name: .passwordStoreChangeDiscarded, object: nil) NotificationCenter.default.post(name: .passwordStoreChangeDiscarded, object: nil)
return localCommits.count return localCommits.count
@ -780,8 +780,8 @@ public class PasswordStore {
return 0 // no new commit return 0 // no new commit
} }
} }
private func getLocalCommits() throws -> [GTCommit]? { private func getLocalCommits() throws -> [GTCommit]? {
guard let storeRepository = storeRepository else { guard let storeRepository = storeRepository else {
throw AppError.RepositoryNotSetError throw AppError.RepositoryNotSetError
@ -791,18 +791,18 @@ public class PasswordStore {
throw AppError.RepositoryRemoteMasterNotFoundError throw AppError.RepositoryRemoteMasterNotFoundError
} }
let remoteMasterBranch = try storeRepository.remoteBranches()[index] let remoteMasterBranch = try storeRepository.remoteBranches()[index]
// check oid before calling localCommitsRelative // check oid before calling localCommitsRelative
guard remoteMasterBranch.oid != nil else { guard remoteMasterBranch.oid != nil else {
throw AppError.RepositoryRemoteMasterNotFoundError throw AppError.RepositoryRemoteMasterNotFoundError
} }
// get a list of local commits // get a list of local commits
return try storeRepository.localCommitsRelative(toRemoteBranch: remoteMasterBranch) return try storeRepository.localCommitsRelative(toRemoteBranch: remoteMasterBranch)
} }
public func decrypt(passwordEntity: PasswordEntity, requestPGPKeyPassphrase: () -> String) throws -> Password? { public func decrypt(passwordEntity: PasswordEntity, requestPGPKeyPassphrase: () -> String) throws -> Password? {
let encryptedDataPath = storeURL.appendingPathComponent(passwordEntity.getPath()) let encryptedDataPath = storeURL.appendingPathComponent(passwordEntity.getPath())
let encryptedData = try Data(contentsOf: encryptedDataPath) let encryptedData = try Data(contentsOf: encryptedDataPath)
@ -817,7 +817,7 @@ public class PasswordStore {
} }
return Password(name: passwordEntity.getName(), url: url, plainText: plainText) return Password(name: passwordEntity.getName(), url: url, plainText: plainText)
} }
public func encrypt(password: Password) throws -> Data { public func encrypt(password: Password) throws -> Data {
guard keyring.keys.count > 0 else { guard keyring.keys.count > 0 else {
throw AppError.PGPPublicKeyNotExistError throw AppError.PGPPublicKeyNotExistError
@ -830,7 +830,7 @@ public class PasswordStore {
return encryptedData return encryptedData
} }
} }
public func removePGPKeys() { public func removePGPKeys() {
try? fm.removeItem(atPath: Globals.pgpPublicKeyPath) try? fm.removeItem(atPath: Globals.pgpPublicKeyPath)
try? fm.removeItem(atPath: Globals.pgpPrivateKeyPath) try? fm.removeItem(atPath: Globals.pgpPrivateKeyPath)
@ -844,14 +844,14 @@ public class PasswordStore {
publicKey = nil publicKey = nil
privateKey = nil privateKey = nil
} }
public func removeGitSSHKeys() { public func removeGitSSHKeys() {
try? fm.removeItem(atPath: Globals.gitSSHPrivateKeyPath) try? fm.removeItem(atPath: Globals.gitSSHPrivateKeyPath)
Defaults.remove(.gitSSHPrivateKeyArmor) Defaults.remove(.gitSSHPrivateKeyArmor)
Defaults.remove(.gitSSHPrivateKeyURL) Defaults.remove(.gitSSHPrivateKeyURL)
self.gitSSHPrivateKeyPassphrase = nil self.gitSSHPrivateKeyPassphrase = nil
} }
public func gitSSHKeyExists(inFileSharing: Bool = false) -> Bool { public func gitSSHKeyExists(inFileSharing: Bool = false) -> Bool {
if inFileSharing == false { if inFileSharing == false {
return fm.fileExists(atPath: Globals.gitSSHPrivateKeyPath) return fm.fileExists(atPath: Globals.gitSSHPrivateKeyPath)
@ -859,7 +859,7 @@ public class PasswordStore {
return fm.fileExists(atPath: Globals.iTunesFileSharingSSHPrivate) return fm.fileExists(atPath: Globals.iTunesFileSharingSSHPrivate)
} }
} }
public func pgpKeyExists(inFileSharing: Bool = false) -> Bool { public func pgpKeyExists(inFileSharing: Bool = false) -> Bool {
if inFileSharing == false { if inFileSharing == false {
return fm.fileExists(atPath: Globals.pgpPublicKeyPath) && fm.fileExists(atPath: Globals.pgpPrivateKeyPath) return fm.fileExists(atPath: Globals.pgpPublicKeyPath) && fm.fileExists(atPath: Globals.pgpPrivateKeyPath)
@ -867,7 +867,7 @@ public class PasswordStore {
return fm.fileExists(atPath: Globals.iTunesFileSharingPGPPublic) && fm.fileExists(atPath: Globals.iTunesFileSharingPGPPrivate) return fm.fileExists(atPath: Globals.iTunesFileSharingPGPPublic) && fm.fileExists(atPath: Globals.iTunesFileSharingPGPPrivate)
} }
} }
public func gitSSHKeyImportFromFileSharing() throws { public func gitSSHKeyImportFromFileSharing() throws {
try fm.moveItem(atPath: Globals.iTunesFileSharingSSHPrivate, toPath: Globals.gitSSHPrivateKeyPath) try fm.moveItem(atPath: Globals.iTunesFileSharingSSHPrivate, toPath: Globals.gitSSHPrivateKeyPath)
} }

View file

@ -35,7 +35,7 @@ extension AdditionField {
} }
extension AdditionField: Equatable { extension AdditionField: Equatable {
public static func == (first: AdditionField, second: AdditionField) -> Bool { public static func == (first: AdditionField, second: AdditionField) -> Bool {
return first.asTuple == second.asTuple return first.asTuple == second.asTuple
} }

View file

@ -16,7 +16,7 @@ public enum OtpType: String {
var description: String { var description: String {
return rawValue return rawValue
} }
init(token: Token?) { init(token: Token?) {
switch token?.generator.factor { switch token?.generator.factor {
case .some(.counter): case .some(.counter):

View file

@ -14,7 +14,7 @@ class PasswordTest: XCTestCase {
func testUrl() { func testUrl() {
let password = getPasswordObjectWith(content: "") let password = getPasswordObjectWith(content: "")
XCTAssertEqual(password.url, PASSWORD_URL) XCTAssertEqual(password.url, PASSWORD_URL)
XCTAssertEqual(password.namePath, PASSWORD_PATH) XCTAssertEqual(password.namePath, PASSWORD_PATH)
} }

View file

@ -10,27 +10,27 @@ import XCTest
@testable import passKit @testable import passKit
class passKitTests: XCTestCase { class passKitTests: XCTestCase {
override func setUp() { override func setUp() {
super.setUp() super.setUp()
// Put setup code here. This method is called before the invocation of each test method in the class. // Put setup code here. This method is called before the invocation of each test method in the class.
} }
override func tearDown() { override func tearDown() {
// Put teardown code here. This method is called after the invocation of each test method in the class. // Put teardown code here. This method is called after the invocation of each test method in the class.
super.tearDown() super.tearDown()
} }
func testExample() { func testExample() {
// This is an example of a functional test case. // This is an example of a functional test case.
// Use XCTAssert and related functions to verify your tests produce the correct results. // Use XCTAssert and related functions to verify your tests produce the correct results.
} }
func testPerformanceExample() { func testPerformanceExample() {
// This is an example of a performance test case. // This is an example of a performance test case.
self.measure { self.measure {
// Put the code you want to measure the time of here. // Put the code you want to measure the time of here.
} }
} }
} }

View file

@ -9,27 +9,27 @@
import XCTest import XCTest
class passTests: XCTestCase { class passTests: XCTestCase {
override func setUp() { override func setUp() {
super.setUp() super.setUp()
// Put setup code here. This method is called before the invocation of each test method in the class. // Put setup code here. This method is called before the invocation of each test method in the class.
} }
override func tearDown() { override func tearDown() {
// Put teardown code here. This method is called after the invocation of each test method in the class. // Put teardown code here. This method is called after the invocation of each test method in the class.
super.tearDown() super.tearDown()
} }
func testExample() { func testExample() {
// This is an example of a functional test case. // This is an example of a functional test case.
// Use XCTAssert and related functions to verify your tests produce the correct results. // Use XCTAssert and related functions to verify your tests produce the correct results.
} }
func testPerformanceExample() { func testPerformanceExample() {
// This is an example of a performance test case. // This is an example of a performance test case.
self.measure { self.measure {
// Put the code you want to measure the time of here. // Put the code you want to measure the time of here.
} }
} }
} }