lint: delete trailing whitespaces
This commit is contained in:
parent
2ba6917710
commit
ed387069a4
59 changed files with 624 additions and 623 deletions
|
|
@ -1,4 +1,4 @@
|
|||
<img src="icon/icon_round.png" width="76"/>
|
||||
<img src="icon/icon_round.png" width="76"/>
|
||||
|
||||
# Pass
|
||||
[](https://github.com/mssun/passforios/releases)
|
||||
|
|
|
|||
|
|
@ -1 +1,2 @@
|
|||
theme: jekyll-theme-minimal
|
||||
theme: jekyll-theme-minimal
|
||||
|
||||
|
|
|
|||
|
|
@ -23,7 +23,7 @@ class AppDelegate: UIResponder, UIApplicationDelegate {
|
|||
let presenter = PasscodeLockPresenter(mainWindow: self.window)
|
||||
return presenter
|
||||
}()
|
||||
|
||||
|
||||
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {
|
||||
// Override point for customization after application launch.
|
||||
SVProgressHUD.setMinimumSize(CGSize(width: 150, height: 100))
|
||||
|
|
@ -35,18 +35,18 @@ class AppDelegate: UIResponder, UIApplicationDelegate {
|
|||
}
|
||||
return true
|
||||
}
|
||||
|
||||
|
||||
func application(_ application: UIApplication, supportedInterfaceOrientationsFor window: UIWindow?) -> UIInterfaceOrientationMask {
|
||||
if let _ = window?.rootViewController as? PasscodeLockViewController {
|
||||
window?.frame = UIScreen.main.bounds
|
||||
}
|
||||
return .all
|
||||
}
|
||||
|
||||
|
||||
@objc func postSearchNotification() {
|
||||
NotificationCenter.default.post(name: .passwordSearch, object: nil)
|
||||
}
|
||||
|
||||
|
||||
func application(_ application: UIApplication, performActionFor shortcutItem: UIApplicationShortcutItem, completionHandler: @escaping (Bool) -> Void) {
|
||||
if shortcutItem.type == Globals.bundleIdentifier + ".search" {
|
||||
let tabBarController = self.window!.rootViewController as! UITabBarController
|
||||
|
|
@ -60,7 +60,7 @@ class AppDelegate: UIResponder, UIApplicationDelegate {
|
|||
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.
|
||||
// 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
|
||||
let blurEffect = UIBlurEffect(style: UIBlurEffectStyle.light)
|
||||
let blurEffectView = UIVisualEffectView(effect: blurEffect)
|
||||
|
|
@ -68,7 +68,7 @@ class AppDelegate: UIResponder, UIApplicationDelegate {
|
|||
blurEffectView.tag = ViewTag.blur.rawValue
|
||||
blurEffectView.autoresizingMask = [.flexibleWidth, .flexibleHeight]
|
||||
self.window?.addSubview(blurEffectView)
|
||||
|
||||
|
||||
// Display the Pass icon in the middle of the screen
|
||||
let iconsDictionary = Bundle.main.infoDictionary?["CFBundleIcons"] as? NSDictionary
|
||||
let primaryIconsDictionary = iconsDictionary?["CFBundlePrimaryIcon"] as? NSDictionary
|
||||
|
|
@ -85,7 +85,7 @@ class AppDelegate: UIResponder, UIApplicationDelegate {
|
|||
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.
|
||||
// If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits.
|
||||
|
||||
|
||||
}
|
||||
|
||||
func applicationWillEnterForeground(_ application: UIApplication) {
|
||||
|
|
@ -95,7 +95,7 @@ class AppDelegate: UIResponder, UIApplicationDelegate {
|
|||
|
||||
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.
|
||||
|
||||
|
||||
self.window?.viewWithTag(ViewTag.appicon.rawValue)?.removeFromSuperview()
|
||||
self.window?.viewWithTag(ViewTag.blur.rawValue)?.removeFromSuperview()
|
||||
}
|
||||
|
|
@ -107,7 +107,7 @@ class AppDelegate: UIResponder, UIApplicationDelegate {
|
|||
}
|
||||
|
||||
// MARK: - Core Data stack
|
||||
|
||||
|
||||
lazy var persistentContainer: NSPersistentContainer = {
|
||||
/*
|
||||
The persistent container for the application. This implementation
|
||||
|
|
@ -126,7 +126,7 @@ class AppDelegate: UIResponder, UIApplicationDelegate {
|
|||
if let error = error as NSError? {
|
||||
// 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.
|
||||
|
||||
|
||||
/*
|
||||
Typical reasons for an error here include:
|
||||
* The parent directory does not exist, cannot be created, or disallows writing.
|
||||
|
|
@ -140,9 +140,9 @@ class AppDelegate: UIResponder, UIApplicationDelegate {
|
|||
})
|
||||
return container
|
||||
}()
|
||||
|
||||
|
||||
// MARK: - Core Data Saving support
|
||||
|
||||
|
||||
func saveContext () {
|
||||
let context = persistentContainer.viewContext
|
||||
if context.hasChanges {
|
||||
|
|
|
|||
|
|
@ -10,7 +10,7 @@ import UIKit
|
|||
import passKit
|
||||
|
||||
class AboutRepositoryTableViewController: BasicStaticTableViewController {
|
||||
|
||||
|
||||
private var needRefresh = false
|
||||
private var indicator: UIActivityIndicatorView = {
|
||||
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)
|
||||
tableView.addSubview(indicator)
|
||||
|
||||
|
||||
setTableData()
|
||||
|
||||
|
||||
// all password store updates (including erase, discard) will trigger the refresh
|
||||
NotificationCenter.default.addObserver(self, selector: #selector(setNeedRefresh), name: .passwordStoreUpdated, object: nil)
|
||||
}
|
||||
|
||||
|
||||
override func viewWillAppear(_ animated: Bool) {
|
||||
super.viewWillAppear(animated)
|
||||
if needRefresh {
|
||||
|
|
@ -37,14 +37,14 @@ class AboutRepositoryTableViewController: BasicStaticTableViewController {
|
|||
needRefresh = false
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private func setTableData() {
|
||||
|
||||
|
||||
// clear current contents (if any)
|
||||
self.tableData.removeAll(keepingCapacity: true)
|
||||
self.tableView.reloadData()
|
||||
indicator.startAnimating()
|
||||
|
||||
|
||||
// reload the table
|
||||
DispatchQueue.global(qos: .userInitiated).async {
|
||||
let passwords = self.numberOfPasswordsString()
|
||||
|
|
@ -52,7 +52,7 @@ class AboutRepositoryTableViewController: BasicStaticTableViewController {
|
|||
let localCommits = self.numberOfLocalCommitsString()
|
||||
let lastSynced = self.lastSyncedTimeString()
|
||||
let commits = self.numberOfPasswordsString()
|
||||
|
||||
|
||||
DispatchQueue.main.async { [weak self] in
|
||||
guard let strongSelf = self else {
|
||||
return
|
||||
|
|
@ -97,7 +97,7 @@ class AboutRepositoryTableViewController: BasicStaticTableViewController {
|
|||
formatter.timeStyle = .short
|
||||
return formatter.string(from: date)
|
||||
}
|
||||
|
||||
|
||||
@objc func setNeedRefresh() {
|
||||
needRefresh = true
|
||||
}
|
||||
|
|
|
|||
|
|
@ -9,21 +9,21 @@
|
|||
import UIKit
|
||||
|
||||
class AboutTableViewController: BasicStaticTableViewController {
|
||||
|
||||
|
||||
override func viewDidLoad() {
|
||||
tableData = [
|
||||
// section 0
|
||||
[[.title: "Website", .action: "link", .link: "https://github.com/mssun/pass-ios.git"],
|
||||
[.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"],],
|
||||
|
||||
|
||||
// section 1,
|
||||
[[.title: "Open Source Components", .action: "segue", .link: "showOpenSourceComponentsSegue"],
|
||||
[.title: "Special Thanks", .action: "segue", .link: "showSpecialThanksSegue"],],
|
||||
]
|
||||
super.viewDidLoad()
|
||||
}
|
||||
|
||||
|
||||
override func tableView(_ tableView: UITableView, viewForFooterInSection section: Int) -> UIView? {
|
||||
if section == tableData.count - 1 {
|
||||
let view = UIView()
|
||||
|
|
@ -38,7 +38,7 @@ class AboutTableViewController: BasicStaticTableViewController {
|
|||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
|
||||
override func tableView(_ tableView: UITableView, titleForHeaderInSection section: Int) -> String? {
|
||||
if section == 1 {
|
||||
return "Acknowledgements".uppercased()
|
||||
|
|
|
|||
|
|
@ -27,7 +27,7 @@ class AddPasswordTableViewController: PasswordEditorTableViewController {
|
|||
tableData[0][0][PasswordEditorCellKey.content] = defaultDirPrefix
|
||||
super.viewDidLoad()
|
||||
}
|
||||
|
||||
|
||||
override func shouldPerformSegue(withIdentifier identifier: String, sender: Any?) -> Bool {
|
||||
if identifier == "saveAddPasswordSegue" {
|
||||
// check PGP key
|
||||
|
|
@ -37,7 +37,7 @@ class AddPasswordTableViewController: PasswordEditorTableViewController {
|
|||
Utils.alert(title: alertTitle, message: alertMessage, controller: self, completion: nil)
|
||||
return false
|
||||
}
|
||||
|
||||
|
||||
// check name
|
||||
guard checkName() == true else {
|
||||
return false
|
||||
|
|
@ -45,7 +45,7 @@ class AddPasswordTableViewController: PasswordEditorTableViewController {
|
|||
}
|
||||
return true
|
||||
}
|
||||
|
||||
|
||||
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
|
||||
super.prepare(for: segue, sender: sender)
|
||||
if segue.identifier == "saveAddPasswordSegue" {
|
||||
|
|
|
|||
|
|
@ -17,7 +17,7 @@ class AdvancedSettingsTableViewController: UITableViewController {
|
|||
@IBOutlet weak var eraseDataTableViewCell: UITableViewCell!
|
||||
@IBOutlet weak var discardChangesTableViewCell: UITableViewCell!
|
||||
let passwordStore = PasswordStore.shared
|
||||
|
||||
|
||||
let encryptInASCIIArmoredSwitch: UISwitch = {
|
||||
let uiSwitch = UISwitch()
|
||||
uiSwitch.onTintColor = Globals.blue
|
||||
|
|
@ -33,7 +33,7 @@ class AdvancedSettingsTableViewController: UITableViewController {
|
|||
encryptInASCIIArmoredTableViewCell.selectionStyle = .none
|
||||
setGitSignatureText()
|
||||
}
|
||||
|
||||
|
||||
private func setGitSignatureText() {
|
||||
let gitSignatureName = passwordStore.gitSignatureForNow.name!
|
||||
let gitSignatureEmail = passwordStore.gitSignatureForNow.email!
|
||||
|
|
@ -77,17 +77,17 @@ class AdvancedSettingsTableViewController: UITableViewController {
|
|||
} catch {
|
||||
Utils.alert(title: "Error", message: error.localizedDescription, controller: self, completion: nil)
|
||||
}
|
||||
|
||||
|
||||
}))
|
||||
alert.addAction(UIAlertAction(title: "Dismiss", style: UIAlertActionStyle.cancel, handler:nil))
|
||||
self.present(alert, animated: true, completion: nil)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@objc func encryptInASCIIArmoredAction(_ sender: Any?) {
|
||||
SharedDefaults[.encryptInArmored] = encryptInASCIIArmoredSwitch.isOn
|
||||
}
|
||||
|
||||
|
||||
@IBAction func saveGitConfigSetting(segue: UIStoryboardSegue) {
|
||||
if let controller = segue.source as? GitConfigSettingTableViewController {
|
||||
if let gitSignatureName = controller.nameTextField.text,
|
||||
|
|
|
|||
|
|
@ -27,18 +27,18 @@ enum CellDataKey {
|
|||
class BasicStaticTableViewController: UITableViewController, MFMailComposeViewControllerDelegate {
|
||||
var tableData = [[Dictionary<CellDataKey, Any>]]()
|
||||
var navigationItemTitle: String?
|
||||
|
||||
|
||||
override func viewDidLoad() {
|
||||
super.viewDidLoad()
|
||||
if navigationItemTitle != nil {
|
||||
navigationItem.title = navigationItemTitle
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
override func numberOfSections(in tableView: UITableView) -> Int {
|
||||
return tableData.count
|
||||
}
|
||||
|
||||
|
||||
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
|
||||
return tableData[section].count
|
||||
}
|
||||
|
|
@ -47,13 +47,13 @@ class BasicStaticTableViewController: UITableViewController, MFMailComposeViewCo
|
|||
super.didReceiveMemoryWarning()
|
||||
// Dispose of any resources that can be recreated.
|
||||
}
|
||||
|
||||
|
||||
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
|
||||
|
||||
|
||||
let cellData = tableData[indexPath.section][indexPath.row]
|
||||
let cellDataStyle = cellData[CellDataKey.style] as? CellDataStyle
|
||||
var cell: UITableViewCell?
|
||||
|
||||
|
||||
switch cellDataStyle ?? .defaultStyle {
|
||||
case .value1:
|
||||
cell = UITableViewCell(style: .value1, reuseIdentifier: "value1 cell")
|
||||
|
|
@ -61,7 +61,7 @@ class BasicStaticTableViewController: UITableViewController, MFMailComposeViewCo
|
|||
default:
|
||||
cell = UITableViewCell(style: .default, reuseIdentifier: "default cell")
|
||||
}
|
||||
|
||||
|
||||
if let detailText = cellData[CellDataKey.detailText] as? String {
|
||||
cell?.detailTextLabel?.text = detailText
|
||||
}
|
||||
|
|
@ -71,17 +71,17 @@ class BasicStaticTableViewController: UITableViewController, MFMailComposeViewCo
|
|||
cell?.accessoryType = .disclosureIndicator
|
||||
cell?.selectionStyle = .default
|
||||
}
|
||||
|
||||
|
||||
cell?.textLabel?.text = cellData[CellDataKey.title] as? String
|
||||
return cell ?? UITableViewCell()
|
||||
}
|
||||
|
||||
|
||||
override func tableView(_ tableView: UITableView, accessoryButtonTappedForRowWith indexPath: IndexPath) {
|
||||
let cellData = tableData[indexPath.section][indexPath.row]
|
||||
let selector = cellData[CellDataKey.detailDisclosureAction] as? Selector
|
||||
perform(selector, with: cellData[CellDataKey.detailDisclosureData])
|
||||
}
|
||||
|
||||
|
||||
override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
|
||||
tableView.deselectRow(at: indexPath, animated: true)
|
||||
let cellData = tableData[indexPath.section][indexPath.row]
|
||||
|
|
@ -116,7 +116,7 @@ class BasicStaticTableViewController: UITableViewController, MFMailComposeViewCo
|
|||
break
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
func sendEmail(toRecipients recipients: [String], subject: String) {
|
||||
let mailVC = MFMailComposeViewController()
|
||||
mailVC.mailComposeDelegate = self
|
||||
|
|
@ -125,7 +125,7 @@ class BasicStaticTableViewController: UITableViewController, MFMailComposeViewCo
|
|||
mailVC.setMessageBody("", isHTML: false)
|
||||
self.present(mailVC, animated: true, completion: nil)
|
||||
}
|
||||
|
||||
|
||||
func mailComposeController(_ controller: MFMailComposeViewController, didFinishWith result: MFMailComposeResult, error: Error?) {
|
||||
controller.dismiss(animated: true)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -13,7 +13,7 @@ import passKit
|
|||
class CommitLogsTableViewController: UITableViewController {
|
||||
var commits: [GTCommit] = []
|
||||
let passwordStore = PasswordStore.shared
|
||||
|
||||
|
||||
override func viewDidLoad() {
|
||||
super.viewDidLoad()
|
||||
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 {
|
||||
return commits.count
|
||||
}
|
||||
|
||||
|
||||
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
|
||||
let cell = tableView.dequeueReusableCell(withIdentifier: "commitLogCell", for: indexPath)
|
||||
let formatter = DateFormatter()
|
||||
formatter.dateStyle = DateFormatter.Style.medium
|
||||
formatter.timeStyle = .medium
|
||||
let dateString = formatter.string(from: commits[indexPath.row].commitDate)
|
||||
|
||||
|
||||
let author = cell.contentView.viewWithTag(200) as? UILabel
|
||||
let dateLabel = cell.contentView.viewWithTag(201) as? UILabel
|
||||
let messageLabel = cell.contentView.viewWithTag(202) as? UILabel
|
||||
|
|
@ -41,12 +41,12 @@ class CommitLogsTableViewController: UITableViewController {
|
|||
messageLabel?.text = commits[indexPath.row].message?.trimmed
|
||||
return cell
|
||||
}
|
||||
|
||||
|
||||
@objc func updateCommitLogs() {
|
||||
commits = getCommitLogs()
|
||||
tableView.reloadData()
|
||||
}
|
||||
|
||||
|
||||
private func getCommitLogs() -> [GTCommit] {
|
||||
do {
|
||||
return try passwordStore.getRecentCommits(count: 20)
|
||||
|
|
|
|||
|
|
@ -24,7 +24,7 @@ class EditPasswordTableViewController: PasswordEditorTableViewController {
|
|||
tableData[1].append([.type: PasswordEditorCellType.memorablePasswordGeneratorCell])
|
||||
super.viewDidLoad()
|
||||
}
|
||||
|
||||
|
||||
override func shouldPerformSegue(withIdentifier identifier: String, sender: Any?) -> Bool {
|
||||
if identifier == "saveEditPasswordSegue" {
|
||||
// check name
|
||||
|
|
@ -34,7 +34,7 @@ class EditPasswordTableViewController: PasswordEditorTableViewController {
|
|||
}
|
||||
return true
|
||||
}
|
||||
|
||||
|
||||
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
|
||||
super.prepare(for: segue, sender: sender)
|
||||
if segue.identifier == "saveEditPasswordSegue" {
|
||||
|
|
|
|||
|
|
@ -19,7 +19,7 @@ class GeneralSettingsTableViewController: BasicStaticTableViewController {
|
|||
uiSwitch.addTarget(self, action: #selector(hideUnknownSwitchAction(_:)), for: UIControlEvents.valueChanged)
|
||||
return uiSwitch
|
||||
}()
|
||||
|
||||
|
||||
let hideOTPSwitch: UISwitch = {
|
||||
let uiSwitch = UISwitch()
|
||||
uiSwitch.onTintColor = Globals.blue
|
||||
|
|
@ -27,7 +27,7 @@ class GeneralSettingsTableViewController: BasicStaticTableViewController {
|
|||
uiSwitch.addTarget(self, action: #selector(hideOTPSwitchAction(_:)), for: UIControlEvents.valueChanged)
|
||||
return uiSwitch
|
||||
}()
|
||||
|
||||
|
||||
let rememberPGPPassphraseSwitch: UISwitch = {
|
||||
let uiSwitch = UISwitch()
|
||||
uiSwitch.onTintColor = Globals.blue
|
||||
|
|
@ -36,7 +36,7 @@ class GeneralSettingsTableViewController: BasicStaticTableViewController {
|
|||
uiSwitch.isOn = SharedDefaults[.isRememberPGPPassphraseOn]
|
||||
return uiSwitch
|
||||
}()
|
||||
|
||||
|
||||
let rememberGitCredentialPassphraseSwitch: UISwitch = {
|
||||
let uiSwitch = UISwitch()
|
||||
uiSwitch.onTintColor = Globals.blue
|
||||
|
|
@ -45,7 +45,7 @@ class GeneralSettingsTableViewController: BasicStaticTableViewController {
|
|||
uiSwitch.isOn = SharedDefaults[.isRememberGitCredentialPassphraseOn]
|
||||
return uiSwitch
|
||||
}()
|
||||
|
||||
|
||||
let showFolderSwitch: UISwitch = {
|
||||
let uiSwitch = UISwitch()
|
||||
uiSwitch.onTintColor = Globals.blue
|
||||
|
|
@ -59,12 +59,12 @@ class GeneralSettingsTableViewController: BasicStaticTableViewController {
|
|||
tableData = [
|
||||
// section 0
|
||||
[[.title: "About Repository", .action: "segue", .link: "showAboutRepositorySegue"],],
|
||||
|
||||
|
||||
// section 1
|
||||
[
|
||||
[.title: "Password Generator Flavor", .action: "none", .style: CellDataStyle.value1],
|
||||
],
|
||||
|
||||
|
||||
// section 2
|
||||
[
|
||||
[.title: "Remember PGP Key Passphrase", .action: "none",],
|
||||
|
|
@ -78,9 +78,9 @@ class GeneralSettingsTableViewController: BasicStaticTableViewController {
|
|||
|
||||
]
|
||||
super.viewDidLoad()
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
|
||||
let cell = super.tableView(tableView, cellForRowAt: indexPath)
|
||||
switch cell.textLabel!.text! {
|
||||
|
|
@ -127,7 +127,7 @@ class GeneralSettingsTableViewController: BasicStaticTableViewController {
|
|||
}
|
||||
return cell
|
||||
}
|
||||
|
||||
|
||||
override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
|
||||
super.tableView(tableView, didSelectRowAt: indexPath)
|
||||
let cell = tableView.cellForRow(at: indexPath)!
|
||||
|
|
@ -136,7 +136,7 @@ class GeneralSettingsTableViewController: BasicStaticTableViewController {
|
|||
showPasswordGeneratorFlavorActionSheet(sourceCell: cell)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
func showPasswordGeneratorFlavorActionSheet(sourceCell: UITableViewCell) {
|
||||
let optionMenu = UIAlertController(title: nil, message: nil, preferredStyle: .actionSheet)
|
||||
var randomFlavorActionTitle = ""
|
||||
|
|
@ -152,12 +152,12 @@ class GeneralSettingsTableViewController: BasicStaticTableViewController {
|
|||
SharedDefaults[.passwordGeneratorFlavor] = "Random"
|
||||
sourceCell.detailTextLabel?.text = "Random"
|
||||
}
|
||||
|
||||
|
||||
let appleFlavorAction = UIAlertAction(title: appleFlavorActionTitle, style: .default) { _ in
|
||||
SharedDefaults[.passwordGeneratorFlavor] = "Apple"
|
||||
sourceCell.detailTextLabel?.text = "Apple"
|
||||
}
|
||||
|
||||
|
||||
let cancelAction = UIAlertAction(title: "Cancel", style: .cancel, handler: nil)
|
||||
optionMenu.addAction(randomFlavorAction)
|
||||
optionMenu.addAction(appleFlavorAction)
|
||||
|
|
@ -166,37 +166,37 @@ class GeneralSettingsTableViewController: BasicStaticTableViewController {
|
|||
optionMenu.popoverPresentationController?.sourceRect = sourceCell.bounds
|
||||
self.present(optionMenu, animated: true, completion: nil)
|
||||
}
|
||||
|
||||
|
||||
@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 alertTitle = "Hide Unknown Fields"
|
||||
Utils.alert(title: alertTitle, message: alertMessage, controller: self, completion: nil)
|
||||
}
|
||||
|
||||
|
||||
@objc func tapHideOTPSwitchDetailButton(_ sender: Any?) {
|
||||
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 alertTitle = "Hide One Time Password Fields"
|
||||
Utils.alert(title: alertTitle, message: alertMessage, controller: self, completion: nil)
|
||||
}
|
||||
|
||||
|
||||
@objc func hideUnknownSwitchAction(_ sender: Any?) {
|
||||
SharedDefaults[.isHideUnknownOn] = hideUnknownSwitch.isOn
|
||||
NotificationCenter.default.post(name: .passwordDetailDisplaySettingChanged, object: nil)
|
||||
}
|
||||
|
||||
|
||||
@objc func hideOTPSwitchAction(_ sender: Any?) {
|
||||
SharedDefaults[.isHideOTPOn] = hideOTPSwitch.isOn
|
||||
NotificationCenter.default.post(name: .passwordDetailDisplaySettingChanged, object: nil)
|
||||
}
|
||||
|
||||
|
||||
@objc func rememberPGPPassphraseSwitchAction(_ sender: Any?) {
|
||||
SharedDefaults[.isRememberPGPPassphraseOn] = rememberPGPPassphraseSwitch.isOn
|
||||
if rememberPGPPassphraseSwitch.isOn == false {
|
||||
passwordStore.pgpKeyPassphrase = nil
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@objc func rememberGitCredentialPassphraseSwitchAction(_ sender: Any?) {
|
||||
SharedDefaults[.isRememberGitCredentialPassphraseOn] = rememberGitCredentialPassphraseSwitch.isOn
|
||||
if rememberGitCredentialPassphraseSwitch.isOn == false {
|
||||
|
|
@ -204,10 +204,10 @@ class GeneralSettingsTableViewController: BasicStaticTableViewController {
|
|||
passwordStore.gitPassword = nil
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@objc func showFolderSwitchAction(_ sender: Any?) {
|
||||
SharedDefaults[.isShowFolderOn] = showFolderSwitch.isOn
|
||||
NotificationCenter.default.post(name: .passwordDisplaySettingChanged, object: nil)
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -12,21 +12,21 @@ import passKit
|
|||
|
||||
class GitConfigSettingTableViewController: UITableViewController {
|
||||
let passwordStore = PasswordStore.shared
|
||||
|
||||
|
||||
@IBOutlet weak var nameTextField: UITextField!
|
||||
@IBOutlet weak var emailTextField: UITextField!
|
||||
|
||||
|
||||
override func viewDidLoad() {
|
||||
super.viewDidLoad()
|
||||
tableView.rowHeight = UITableViewAutomaticDimension
|
||||
|
||||
|
||||
let signature = passwordStore.gitSignatureForNow
|
||||
nameTextField.placeholder = signature.name
|
||||
emailTextField.placeholder = signature.email
|
||||
nameTextField.text = SharedDefaults[.gitSignatureName]
|
||||
emailTextField.text = SharedDefaults[.gitSignatureEmail]
|
||||
}
|
||||
|
||||
|
||||
override func shouldPerformSegue(withIdentifier identifier: String, sender: Any?) -> Bool {
|
||||
if identifier == "saveGitConfigSettingSegue" {
|
||||
let name = nameTextField.text!.isEmpty ? Globals.gitSignatureDefaultName : nameTextField.text!
|
||||
|
|
|
|||
|
|
@ -12,10 +12,10 @@ import passKit
|
|||
class GitSSHKeyArmorSettingTableViewController: UITableViewController, UITextViewDelegate, QRScannerControllerDelegate {
|
||||
@IBOutlet weak var armorPrivateKeyTextView: UITextView!
|
||||
@IBOutlet weak var scanPrivateKeyCell: UITableViewCell!
|
||||
|
||||
|
||||
var gitSSHPrivateKeyPassphrase: String?
|
||||
let passwordStore = PasswordStore.shared
|
||||
|
||||
|
||||
class ScannedSSHKey {
|
||||
static let maxNumberOfGif = 100
|
||||
var numberOfSegments = 0
|
||||
|
|
@ -24,7 +24,7 @@ class GitSSHKeyArmorSettingTableViewController: UITableViewController, UITextVie
|
|||
var message = ""
|
||||
var hasStarted = false
|
||||
var isDone = false
|
||||
|
||||
|
||||
func reset() {
|
||||
numberOfSegments = 0
|
||||
previousSegment = ""
|
||||
|
|
@ -33,14 +33,14 @@ class GitSSHKeyArmorSettingTableViewController: UITableViewController, UITextVie
|
|||
hasStarted = false
|
||||
isDone = false
|
||||
}
|
||||
|
||||
|
||||
func addSegment(segment: String) {
|
||||
// skip duplicated segments
|
||||
guard segment != previousSegment else {
|
||||
return
|
||||
}
|
||||
previousSegment = segment
|
||||
|
||||
|
||||
// check whether we have found the first block
|
||||
if hasStarted == false {
|
||||
hasStarted = segment.contains("-----BEGIN")
|
||||
|
|
@ -48,38 +48,38 @@ class GitSSHKeyArmorSettingTableViewController: UITableViewController, UITextVie
|
|||
guard hasStarted == true else {
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
// check the number of segments
|
||||
numberOfSegments = numberOfSegments + 1
|
||||
guard numberOfSegments <= ScannedSSHKey.maxNumberOfGif else {
|
||||
key = "Too many QR codes"
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
// update full text and check whether we are done
|
||||
key.append(segment)
|
||||
if let index1 = key.range(of: "-----END")?.lowerBound,
|
||||
let _ = key.suffix(from: index1).range(of: "KEY-----")?.lowerBound {
|
||||
isDone = true
|
||||
}
|
||||
|
||||
|
||||
// update message
|
||||
message = "\(numberOfSegments) scanned QR codes."
|
||||
}
|
||||
}
|
||||
var scanned = ScannedSSHKey()
|
||||
|
||||
|
||||
override func viewDidLoad() {
|
||||
super.viewDidLoad()
|
||||
armorPrivateKeyTextView.text = SharedDefaults[.gitSSHPrivateKeyArmor]
|
||||
armorPrivateKeyTextView.delegate = self
|
||||
|
||||
|
||||
scanPrivateKeyCell?.textLabel?.text = "Scan Private Key QR Codes"
|
||||
scanPrivateKeyCell?.textLabel?.textColor = Globals.blue
|
||||
scanPrivateKeyCell?.selectionStyle = .default
|
||||
scanPrivateKeyCell?.accessoryType = .disclosureIndicator
|
||||
}
|
||||
|
||||
|
||||
@IBAction func doneButtonTapped(_ sender: Any) {
|
||||
SharedDefaults[.gitSSHPrivateKeyArmor] = armorPrivateKeyTextView.text
|
||||
do {
|
||||
|
|
@ -90,7 +90,7 @@ class GitSSHKeyArmorSettingTableViewController: UITableViewController, UITextVie
|
|||
SharedDefaults[.gitSSHKeySource] = "armor"
|
||||
self.navigationController!.popViewController(animated: true)
|
||||
}
|
||||
|
||||
|
||||
func textView(_ textView: UITextView, shouldChangeTextIn range: NSRange, replacementText text: String) -> Bool {
|
||||
if text == UIPasteboard.general.string {
|
||||
// user pastes something, do the copy here again and clear the pasteboard in 45s
|
||||
|
|
@ -98,7 +98,7 @@ class GitSSHKeyArmorSettingTableViewController: UITableViewController, UITextVie
|
|||
}
|
||||
return true
|
||||
}
|
||||
|
||||
|
||||
override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
|
||||
let selectedCell = tableView.cellForRow(at: indexPath)
|
||||
if selectedCell == scanPrivateKeyCell {
|
||||
|
|
@ -107,8 +107,8 @@ class GitSSHKeyArmorSettingTableViewController: UITableViewController, UITextVie
|
|||
}
|
||||
tableView.deselectRow(at: indexPath, animated: true)
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
// MARK: - QRScannerControllerDelegate Methods
|
||||
func checkScannedOutput(line: String) -> (accept: Bool, message: String) {
|
||||
scanned.addSegment(segment: line)
|
||||
|
|
@ -118,12 +118,12 @@ class GitSSHKeyArmorSettingTableViewController: UITableViewController, UITextVie
|
|||
return (accept: false, message: scanned.message)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// MARK: - QRScannerControllerDelegate Methods
|
||||
func handleScannedOutput(line: String) {
|
||||
armorPrivateKeyTextView.text = scanned.key
|
||||
}
|
||||
|
||||
|
||||
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
|
||||
if segue.identifier == "showSSHScannerSegue" {
|
||||
if let navController = segue.destination as? UINavigationController {
|
||||
|
|
@ -135,9 +135,9 @@ class GitSSHKeyArmorSettingTableViewController: UITableViewController, UITextVie
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@IBAction private func cancelSSHScanner(segue: UIStoryboardSegue) {
|
||||
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -37,7 +37,7 @@ class GitServerSettingTableViewController: UITableViewController {
|
|||
sshKeyCheckView.isHidden = true
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
override func viewWillAppear(_ animated: Bool) {
|
||||
super.viewWillAppear(animated)
|
||||
// Grey out ssh option if ssh_key is not present
|
||||
|
|
@ -55,30 +55,30 @@ class GitServerSettingTableViewController: UITableViewController {
|
|||
checkAuthenticationMethod(method: authenticationMethod)
|
||||
authSSHKeyCell.accessoryType = .detailButton
|
||||
}
|
||||
|
||||
|
||||
override func tableView(_ tableView: UITableView, accessoryButtonTappedForRowWith indexPath: IndexPath) {
|
||||
let cell = tableView.cellForRow(at: indexPath)
|
||||
if cell == authSSHKeyCell {
|
||||
showSSHKeyActionSheet()
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
override func viewDidAppear(_ animated: Bool) {
|
||||
super.viewDidAppear(animated)
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
override func viewWillDisappear(_ animated: Bool) {
|
||||
super.viewWillDisappear(animated)
|
||||
view.endEditing(true)
|
||||
}
|
||||
|
||||
|
||||
private func cloneAndSegueIfSuccess() {
|
||||
// try to clone
|
||||
let gitRepostiroyURL = gitURLTextField.text!.trimmed
|
||||
let username = usernameTextField.text!
|
||||
let auth = authenticationMethod
|
||||
|
||||
|
||||
SVProgressHUD.setDefaultMaskType(.black)
|
||||
SVProgressHUD.setDefaultStyle(.light)
|
||||
SVProgressHUD.show(withStatus: "Prepare Repository")
|
||||
|
|
@ -160,15 +160,15 @@ class GitServerSettingTableViewController: UITableViewController {
|
|||
checkAuthenticationMethod(method: authenticationMethod)
|
||||
tableView.deselectRow(at: indexPath, animated: true)
|
||||
}
|
||||
|
||||
|
||||
@IBAction func save(_ sender: Any) {
|
||||
|
||||
|
||||
// some sanity checks
|
||||
guard let gitURL = URL(string: gitURLTextField.text!) else {
|
||||
Utils.alert(title: "Cannot Save", message: "Please set the Git repository URL.", controller: self, completion: nil)
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
switch gitURL.scheme {
|
||||
case let val where val == "https":
|
||||
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)
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
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)
|
||||
alert.addAction(UIAlertAction(title: "Overwrite", style: UIAlertActionStyle.destructive, handler: { _ in
|
||||
|
|
@ -202,13 +202,13 @@ class GitServerSettingTableViewController: UITableViewController {
|
|||
cloneAndSegueIfSuccess()
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
func showSSHKeyActionSheet() {
|
||||
let optionMenu = UIAlertController(title: nil, message: nil, preferredStyle: .actionSheet)
|
||||
var urlActionTitle = "Download from URL"
|
||||
var armorActionTitle = "ASCII-Armor Encrypted Key"
|
||||
var fileActionTitle = "iTunes File Sharing"
|
||||
|
||||
|
||||
if SharedDefaults[.gitSSHKeySource] == "url" {
|
||||
urlActionTitle = "✓ \(urlActionTitle)"
|
||||
} else if SharedDefaults[.gitSSHKeySource] == "armor" {
|
||||
|
|
@ -225,7 +225,7 @@ class GitServerSettingTableViewController: UITableViewController {
|
|||
let cancelAction = UIAlertAction(title: "Cancel", style: .cancel, handler: nil)
|
||||
optionMenu.addAction(urlAction)
|
||||
optionMenu.addAction(armorAction)
|
||||
|
||||
|
||||
if passwordStore.gitSSHKeyExists(inFileSharing: true) {
|
||||
// might keys updated via iTunes, or downloaded/pasted inside the app
|
||||
fileActionTitle.append(" (Import)")
|
||||
|
|
@ -249,7 +249,7 @@ class GitServerSettingTableViewController: UITableViewController {
|
|||
}
|
||||
optionMenu.addAction(fileAction)
|
||||
}
|
||||
|
||||
|
||||
if SharedDefaults[.gitSSHKeySource] != nil {
|
||||
let deleteAction = UIAlertAction(title: "Remove Git SSH Keys", style: .destructive) { _ in
|
||||
self.passwordStore.removeGitSSHKeys()
|
||||
|
|
@ -266,7 +266,7 @@ class GitServerSettingTableViewController: UITableViewController {
|
|||
optionMenu.popoverPresentationController?.sourceRect = authSSHKeyCell.bounds
|
||||
self.present(optionMenu, animated: true, completion: nil)
|
||||
}
|
||||
|
||||
|
||||
private func requestGitPassword(credential: GitCredential.Credential, lastPassword: String?) -> String? {
|
||||
let sem = DispatchSemaphore(value: 0)
|
||||
var password: String?
|
||||
|
|
@ -277,7 +277,7 @@ class GitServerSettingTableViewController: UITableViewController {
|
|||
case .ssh:
|
||||
message = "Please fill in the passphrase of your SSH key."
|
||||
}
|
||||
|
||||
|
||||
DispatchQueue.main.async {
|
||||
SVProgressHUD.dismiss()
|
||||
let alert = UIAlertController(title: "Password", message: message, preferredStyle: UIAlertControllerStyle.alert)
|
||||
|
|
@ -295,7 +295,7 @@ class GitServerSettingTableViewController: UITableViewController {
|
|||
})
|
||||
self.present(alert, animated: true, completion: nil)
|
||||
}
|
||||
|
||||
|
||||
let _ = sem.wait(timeout: .distantFuture)
|
||||
return password
|
||||
}
|
||||
|
|
|
|||
|
|
@ -33,7 +33,7 @@ class OpenSourceComponentsTableViewController: BasicStaticTableViewController {
|
|||
"https://github.com/SVProgressHUD/SVProgressHUD",
|
||||
"https://github.com/SVProgressHUD/SVProgressHUD/blob/master/LICENSE.txt"],
|
||||
]
|
||||
|
||||
|
||||
override func viewDidLoad() {
|
||||
tableData.append([])
|
||||
for item in openSourceComponents {
|
||||
|
|
@ -43,7 +43,7 @@ class OpenSourceComponentsTableViewController: BasicStaticTableViewController {
|
|||
}
|
||||
super.viewDidLoad()
|
||||
}
|
||||
|
||||
|
||||
@objc func actOnDetailDisclosureButton(_ sender: Any?) {
|
||||
if let link = sender as? String {
|
||||
let svc = SFSafariViewController(url: URL(string: link)!, entersReaderIfAvailable: false)
|
||||
|
|
|
|||
|
|
@ -14,10 +14,10 @@ class PGPKeyArmorSettingTableViewController: UITableViewController, UITextViewDe
|
|||
@IBOutlet weak var armorPrivateKeyTextView: UITextView!
|
||||
@IBOutlet weak var scanPublicKeyCell: UITableViewCell!
|
||||
@IBOutlet weak var scanPrivateKeyCell: UITableViewCell!
|
||||
|
||||
|
||||
var pgpPassphrase: String?
|
||||
let passwordStore = PasswordStore.shared
|
||||
|
||||
|
||||
class ScannedPGPKey {
|
||||
static let maxNumberOfGif = 100
|
||||
enum KeyType {
|
||||
|
|
@ -30,7 +30,7 @@ class PGPKeyArmorSettingTableViewController: UITableViewController, UITextViewDe
|
|||
var message = ""
|
||||
var hasStarted = false
|
||||
var isDone = false
|
||||
|
||||
|
||||
func reset(keytype: KeyType) {
|
||||
self.keyType = keytype
|
||||
numberOfSegments = 0
|
||||
|
|
@ -40,14 +40,14 @@ class PGPKeyArmorSettingTableViewController: UITableViewController, UITextViewDe
|
|||
hasStarted = false
|
||||
isDone = false
|
||||
}
|
||||
|
||||
|
||||
func addSegment(segment: String) {
|
||||
// skip duplicated segments
|
||||
guard segment != previousSegment else {
|
||||
return
|
||||
}
|
||||
previousSegment = segment
|
||||
|
||||
|
||||
// check whether we have found the first block
|
||||
if hasStarted == false {
|
||||
let findPublic = segment.contains("-----BEGIN PGP PUBLIC KEY BLOCK-----")
|
||||
|
|
@ -68,32 +68,32 @@ class PGPKeyArmorSettingTableViewController: UITableViewController, UITextViewDe
|
|||
guard hasStarted == true else {
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
// check the number of segments
|
||||
numberOfSegments = numberOfSegments + 1
|
||||
guard numberOfSegments <= ScannedPGPKey.maxNumberOfGif else {
|
||||
key = "Too many QR codes"
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
// update full text and check whether we are done
|
||||
key.append(segment)
|
||||
if key.contains("-----END PGP PUBLIC KEY BLOCK-----") || key.contains("-----END PGP PRIVATE KEY BLOCK-----") {
|
||||
isDone = true
|
||||
}
|
||||
|
||||
|
||||
// update message
|
||||
message = "\(numberOfSegments) scanned QR codes."
|
||||
}
|
||||
}
|
||||
var scanned = ScannedPGPKey()
|
||||
|
||||
|
||||
override func viewDidLoad() {
|
||||
super.viewDidLoad()
|
||||
armorPublicKeyTextView.text = SharedDefaults[.pgpPublicKeyArmor]
|
||||
armorPrivateKeyTextView.text = SharedDefaults[.pgpPrivateKeyArmor]
|
||||
pgpPassphrase = passwordStore.pgpKeyPassphrase
|
||||
|
||||
|
||||
scanPublicKeyCell?.textLabel?.text = "Scan Public Key QR Codes"
|
||||
scanPublicKeyCell?.textLabel?.textColor = Globals.blue
|
||||
scanPublicKeyCell?.selectionStyle = .default
|
||||
|
|
@ -104,7 +104,7 @@ class PGPKeyArmorSettingTableViewController: UITableViewController, UITextViewDe
|
|||
scanPrivateKeyCell?.selectionStyle = .default
|
||||
scanPrivateKeyCell?.accessoryType = .disclosureIndicator
|
||||
}
|
||||
|
||||
|
||||
@IBAction func save(_ sender: Any) {
|
||||
guard armorPublicKeyTextView.text.isEmpty == false else {
|
||||
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)
|
||||
}
|
||||
|
||||
|
||||
func textView(_ textView: UITextView, shouldChangeTextIn range: NSRange, replacementText text: String) -> Bool {
|
||||
if text == UIPasteboard.general.string {
|
||||
// user pastes something, do the copy here again and clear the pasteboard in 45s
|
||||
|
|
@ -146,7 +146,7 @@ class PGPKeyArmorSettingTableViewController: UITableViewController, UITextViewDe
|
|||
}
|
||||
return true
|
||||
}
|
||||
|
||||
|
||||
override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
|
||||
let selectedCell = tableView.cellForRow(at: indexPath)
|
||||
if selectedCell == scanPublicKeyCell {
|
||||
|
|
@ -158,7 +158,7 @@ class PGPKeyArmorSettingTableViewController: UITableViewController, UITextViewDe
|
|||
}
|
||||
tableView.deselectRow(at: indexPath, animated: true)
|
||||
}
|
||||
|
||||
|
||||
// MARK: - QRScannerControllerDelegate Methods
|
||||
func checkScannedOutput(line: String) -> (accept: Bool, message: String) {
|
||||
scanned.addSegment(segment: line)
|
||||
|
|
@ -168,7 +168,7 @@ class PGPKeyArmorSettingTableViewController: UITableViewController, UITextViewDe
|
|||
return (accept: false, message: scanned.message)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// MARK: - QRScannerControllerDelegate Methods
|
||||
func handleScannedOutput(line: String) {
|
||||
switch scanned.keyType {
|
||||
|
|
@ -178,7 +178,7 @@ class PGPKeyArmorSettingTableViewController: UITableViewController, UITextViewDe
|
|||
armorPrivateKeyTextView.text = scanned.key
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
|
||||
if segue.identifier == "showPGPScannerSegue" {
|
||||
if let navController = segue.destination as? UINavigationController {
|
||||
|
|
|
|||
|
|
@ -15,7 +15,7 @@ class PGPKeySettingTableViewController: UITableViewController {
|
|||
@IBOutlet weak var pgpPrivateKeyURLTextField: UITextField!
|
||||
var pgpPassphrase: String?
|
||||
let passwordStore = PasswordStore.shared
|
||||
|
||||
|
||||
override func viewDidLoad() {
|
||||
super.viewDidLoad()
|
||||
tableView.rowHeight = UITableViewAutomaticDimension
|
||||
|
|
@ -23,7 +23,7 @@ class PGPKeySettingTableViewController: UITableViewController {
|
|||
pgpPrivateKeyURLTextField.text = SharedDefaults[.pgpPrivateKeyURL]?.absoluteString
|
||||
pgpPassphrase = passwordStore.pgpKeyPassphrase
|
||||
}
|
||||
|
||||
|
||||
private func validatePGPKeyURL(input: String?) -> Bool {
|
||||
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)
|
||||
|
|
@ -35,7 +35,7 @@ class PGPKeySettingTableViewController: UITableViewController {
|
|||
}
|
||||
return true
|
||||
}
|
||||
|
||||
|
||||
@IBAction func save(_ sender: Any) {
|
||||
guard validatePGPKeyURL(input: pgpPublicKeyURLTextField.text) == true,
|
||||
validatePGPKeyURL(input: pgpPrivateKeyURLTextField.text) == true else {
|
||||
|
|
@ -65,6 +65,6 @@ class PGPKeySettingTableViewController: UITableViewController {
|
|||
})
|
||||
self.present(savePassphraseAlert, animated: true, completion: nil)
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -18,7 +18,7 @@ class PasswordDetailTableViewController: UITableViewController, UIGestureRecogni
|
|||
private var oneTimePasswordIndexPath : IndexPath?
|
||||
private var shouldPopCurrentView = false
|
||||
private let passwordStore = PasswordStore.shared
|
||||
|
||||
|
||||
private lazy var editUIBarButtonItem: UIBarButtonItem = {
|
||||
let uiBarButtonItem = UIBarButtonItem(barButtonSystemItem: .edit, target: self, action: #selector(pressEdit(_:)))
|
||||
return uiBarButtonItem
|
||||
|
|
@ -31,18 +31,18 @@ class PasswordDetailTableViewController: UITableViewController, UIGestureRecogni
|
|||
title = ""
|
||||
content = ""
|
||||
}
|
||||
|
||||
|
||||
init(title: String) {
|
||||
self.title = title
|
||||
self.content = ""
|
||||
}
|
||||
|
||||
|
||||
init(title: String, content: String) {
|
||||
self.title = title
|
||||
self.content = content
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private struct TableSection {
|
||||
var type: PasswordDetailTableViewControllerSectionType
|
||||
var header: String?
|
||||
|
|
@ -52,56 +52,56 @@ class PasswordDetailTableViewController: UITableViewController, UIGestureRecogni
|
|||
header = nil
|
||||
item = [TableCell]()
|
||||
}
|
||||
|
||||
|
||||
init(type: PasswordDetailTableViewControllerSectionType, header: String) {
|
||||
self.init(type: type)
|
||||
self.header = header
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private var tableData = Array<TableSection>()
|
||||
|
||||
|
||||
private enum PasswordDetailTableViewControllerSectionType {
|
||||
case name, main, addition, misc
|
||||
}
|
||||
|
||||
|
||||
override func viewDidLoad() {
|
||||
super.viewDidLoad()
|
||||
tableView.register(UINib(nibName: "LabelTableViewCell", bundle: nil), forCellReuseIdentifier: "labelCell")
|
||||
tableView.register(UINib(nibName: "PasswordDetailTitleTableViewCell", bundle: nil), forCellReuseIdentifier: "passwordDetailTitleTableViewCell")
|
||||
|
||||
|
||||
let tapGesture = UITapGestureRecognizer(target: self, action: #selector(PasswordDetailTableViewController.tapMenu(recognizer:)))
|
||||
tapGesture.cancelsTouchesInView = false
|
||||
tableView.addGestureRecognizer(tapGesture)
|
||||
tapGesture.delegate = self
|
||||
|
||||
|
||||
tableView.contentInset = UIEdgeInsetsMake(-36, 0, 44, 0);
|
||||
tableView.rowHeight = UITableViewAutomaticDimension
|
||||
tableView.estimatedRowHeight = 52
|
||||
|
||||
|
||||
editUIBarButtonItem.isEnabled = false
|
||||
navigationItem.rightBarButtonItem = editUIBarButtonItem
|
||||
if #available(iOS 11.0, *) {
|
||||
navigationItem.largeTitleDisplayMode = .never
|
||||
}
|
||||
|
||||
|
||||
if let imageData = passwordEntity?.getImage() {
|
||||
let image = UIImage(data: imageData as Data)
|
||||
passwordImage = image
|
||||
}
|
||||
self.decryptThenShowPassword()
|
||||
self.setupOneTimePasswordAutoRefresh()
|
||||
|
||||
|
||||
// pop the current view because this password might be "discarded"
|
||||
NotificationCenter.default.addObserver(self, selector: #selector(setShouldPopCurrentView), name: .passwordStoreChangeDiscarded, object: nil)
|
||||
|
||||
|
||||
// reset the data table if some password (maybe another one) has been updated
|
||||
NotificationCenter.default.addObserver(self, selector: #selector(decryptThenShowPassword), name: .passwordStoreUpdated, object: nil)
|
||||
|
||||
|
||||
// reset the data table if the disaply settings have been changed
|
||||
NotificationCenter.default.addObserver(self, selector: #selector(decryptThenShowPassword), name: .passwordDetailDisplaySettingChanged, object: nil)
|
||||
}
|
||||
|
||||
|
||||
override func viewDidAppear(_ animated: Bool) {
|
||||
super.viewWillAppear(animated)
|
||||
if self.shouldPopCurrentView {
|
||||
|
|
@ -112,7 +112,7 @@ class PasswordDetailTableViewController: UITableViewController, UIGestureRecogni
|
|||
self.present(alert, animated: true, completion: nil)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private func requestPGPKeyPassphrase() -> String {
|
||||
let sem = DispatchSemaphore(value: 0)
|
||||
var passphrase = ""
|
||||
|
|
@ -134,7 +134,7 @@ class PasswordDetailTableViewController: UITableViewController, UIGestureRecogni
|
|||
}
|
||||
return passphrase
|
||||
}
|
||||
|
||||
|
||||
@objc private func decryptThenShowPassword() {
|
||||
guard let passwordEntity = passwordEntity else {
|
||||
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()
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private func showPassword() {
|
||||
DispatchQueue.main.async { [weak self] in
|
||||
self?.setTableData()
|
||||
|
|
@ -179,7 +179,7 @@ class PasswordDetailTableViewController: UITableViewController, UIGestureRecogni
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private func setupOneTimePasswordAutoRefresh() {
|
||||
Timer.scheduledTimer(withTimeInterval: 1, repeats: true) {
|
||||
[weak self] timer in
|
||||
|
|
@ -204,19 +204,19 @@ class PasswordDetailTableViewController: UITableViewController, UIGestureRecogni
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@objc private func pressEdit(_ sender: Any?) {
|
||||
performSegue(withIdentifier: "editPasswordSegue", sender: self)
|
||||
}
|
||||
|
||||
|
||||
@objc private func setShouldPopCurrentView() {
|
||||
self.shouldPopCurrentView = true
|
||||
}
|
||||
|
||||
|
||||
@IBAction private func cancelEditPassword(segue: UIStoryboardSegue) {
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
@IBAction private func saveEditPassword(segue: UIStoryboardSegue) {
|
||||
if self.password!.changed != 0 {
|
||||
SVProgressHUD.show(withStatus: "Saving")
|
||||
|
|
@ -231,7 +231,7 @@ class PasswordDetailTableViewController: UITableViewController, UIGestureRecogni
|
|||
SVProgressHUD.dismiss(withDelay: 1)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@IBAction private func deletePassword(segue: UIStoryboardSegue) {
|
||||
do {
|
||||
try passwordStore.delete(passwordEntity: passwordEntity!)
|
||||
|
|
@ -243,7 +243,7 @@ class PasswordDetailTableViewController: UITableViewController, UIGestureRecogni
|
|||
|
||||
private func setTableData() {
|
||||
self.tableData = Array<TableSection>()
|
||||
|
||||
|
||||
// name section
|
||||
var section = TableSection(type: .name)
|
||||
section.item.append(TableCell())
|
||||
|
|
@ -261,9 +261,9 @@ class PasswordDetailTableViewController: UITableViewController, UIGestureRecogni
|
|||
section.item.append(TableCell(title: "password", content: password.password))
|
||||
tableData.append(section)
|
||||
|
||||
|
||||
|
||||
// addition section
|
||||
|
||||
|
||||
// show one time password
|
||||
if password.otpType != .none {
|
||||
if let (title, otp) = self.password?.getOtpStrings() {
|
||||
|
|
@ -273,7 +273,7 @@ class PasswordDetailTableViewController: UITableViewController, UIGestureRecogni
|
|||
oneTimePasswordIndexPath = IndexPath(row: 0, section: tableData.count - 1)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// show additional information
|
||||
let filteredAdditionKeys = password.getFilteredAdditions()
|
||||
if filteredAdditionKeys.count > 0 {
|
||||
|
|
@ -283,14 +283,14 @@ class PasswordDetailTableViewController: UITableViewController, UIGestureRecogni
|
|||
})
|
||||
tableData.append(section)
|
||||
}
|
||||
|
||||
|
||||
// misc section
|
||||
section = TableSection(type: .misc)
|
||||
section.item.append(TableCell(title: "Show Raw"))
|
||||
tableData.append(section)
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
|
||||
if segue.identifier == "editPasswordSegue" {
|
||||
if let controller = segue.destination as? UINavigationController {
|
||||
|
|
@ -306,7 +306,7 @@ class PasswordDetailTableViewController: UITableViewController, UIGestureRecogni
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private func updatePasswordImage(urlString: String) {
|
||||
var newUrlString = urlString
|
||||
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
|
||||
newUrlString = "https://\(urlString)"
|
||||
}
|
||||
|
||||
|
||||
try? FavIcon.downloadPreferred(newUrlString) { [weak self] result in
|
||||
if case let .success(image) = result {
|
||||
let indexPath = IndexPath(row: 0, section: 0)
|
||||
|
|
@ -334,7 +334,7 @@ class PasswordDetailTableViewController: UITableViewController, UIGestureRecogni
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@objc private func tapMenu(recognizer: UITapGestureRecognizer) {
|
||||
if recognizer.state == UIGestureRecognizerState.ended {
|
||||
let tapLocation = recognizer.location(in: self.tableView)
|
||||
|
|
@ -353,17 +353,17 @@ class PasswordDetailTableViewController: UITableViewController, UIGestureRecogni
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
func gestureRecognizer(_ gestureRecognizer: UIGestureRecognizer, shouldReceive touch: UITouch) -> Bool {
|
||||
if touch.view!.isKind(of: UIButton.classForCoder()) {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
|
||||
@IBAction func back(segue:UIStoryboardSegue) {
|
||||
}
|
||||
|
||||
|
||||
func getNextHOTP() {
|
||||
guard password != nil, passwordEntity != nil, password?.otpType == .hotp else {
|
||||
DispatchQueue.main.async {
|
||||
|
|
@ -371,12 +371,12 @@ class PasswordDetailTableViewController: UITableViewController, UIGestureRecogni
|
|||
}
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
// copy HOTP to pasteboard (will update counter)
|
||||
if let plainPassword = password!.getNextHotp() {
|
||||
SecurePasteboard.shared.copy(textToCopy: plainPassword)
|
||||
}
|
||||
|
||||
|
||||
// commit the change of HOTP counter
|
||||
if password!.changed != 0 {
|
||||
do {
|
||||
|
|
@ -465,11 +465,11 @@ class PasswordDetailTableViewController: UITableViewController, UIGestureRecogni
|
|||
detailTextLabel.textColor = .gray
|
||||
detailTextLabel.text = "\(numberOfHiddenFields) hidden field\(numberOfHiddenFields > 1 ? "s" : "")"
|
||||
}
|
||||
|
||||
|
||||
override func tableView(_ tableView: UITableView, titleForHeaderInSection section: Int) -> String? {
|
||||
return tableData[section].header
|
||||
}
|
||||
|
||||
|
||||
override func tableView(_ tableView: UITableView, viewForFooterInSection section: Int) -> UIView? {
|
||||
if section == tableData.count - 1 {
|
||||
let view = UIView()
|
||||
|
|
@ -484,13 +484,13 @@ class PasswordDetailTableViewController: UITableViewController, UIGestureRecogni
|
|||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
|
||||
override func tableView(_ tableView: UITableView, performAction action: Selector, forRowAt indexPath: IndexPath, withSender sender: Any?) {
|
||||
if action == #selector(copy(_:)) {
|
||||
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 {
|
||||
let section = tableData[indexPath.section]
|
||||
switch(section.type) {
|
||||
|
|
@ -500,11 +500,11 @@ class PasswordDetailTableViewController: UITableViewController, UIGestureRecogni
|
|||
return false
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
override func tableView(_ tableView: UITableView, shouldShowMenuForRowAt indexPath: IndexPath) -> Bool {
|
||||
return true
|
||||
}
|
||||
|
||||
|
||||
override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
|
||||
let section = tableData[indexPath.section]
|
||||
if section.type == .misc {
|
||||
|
|
|
|||
|
|
@ -20,21 +20,21 @@ enum PasswordEditorCellKey {
|
|||
}
|
||||
|
||||
class PasswordEditorTableViewController: UITableViewController, FillPasswordTableViewCellDelegate, PasswordSettingSliderTableViewCellDelegate, QRScannerControllerDelegate, UITextFieldDelegate, UITextViewDelegate, SFSafariViewControllerDelegate {
|
||||
|
||||
|
||||
var tableData = [
|
||||
[Dictionary<PasswordEditorCellKey, Any>]
|
||||
]()
|
||||
var password: Password?
|
||||
|
||||
|
||||
private var navigationItemTitle: String?
|
||||
|
||||
|
||||
private var sectionHeaderTitles = ["name", "password", "additions",""].map {$0.uppercased()}
|
||||
private var sectionFooterTitles = ["", "", "Use \"key: value\" format for additional fields.", ""]
|
||||
private let nameSection = 0
|
||||
private let passwordSection = 1
|
||||
private let additionsSection = 2
|
||||
private var hidePasswordSettings = true
|
||||
|
||||
|
||||
var nameCell: TextFieldTableViewCell?
|
||||
var fillPasswordCell: FillPasswordTableViewCell?
|
||||
private var passwordLengthCell: SliderTableViewCell?
|
||||
|
|
@ -42,39 +42,39 @@ class PasswordEditorTableViewController: UITableViewController, FillPasswordTabl
|
|||
private var deletePasswordCell: UITableViewCell?
|
||||
private var scanQRCodeCell: UITableViewCell?
|
||||
private var memorablePasswordGeneratorCell: UITableViewCell?
|
||||
|
||||
|
||||
override func loadView() {
|
||||
super.loadView()
|
||||
|
||||
|
||||
deletePasswordCell = UITableViewCell(style: .default, reuseIdentifier: "default")
|
||||
deletePasswordCell!.textLabel?.text = "Delete Password"
|
||||
deletePasswordCell!.textLabel?.textColor = Globals.red
|
||||
deletePasswordCell?.selectionStyle = .default
|
||||
|
||||
|
||||
scanQRCodeCell = UITableViewCell(style: .default, reuseIdentifier: "default")
|
||||
scanQRCodeCell?.textLabel?.text = "Add One-Time Password"
|
||||
scanQRCodeCell?.textLabel?.textColor = Globals.blue
|
||||
scanQRCodeCell?.selectionStyle = .default
|
||||
scanQRCodeCell?.accessoryType = .disclosureIndicator
|
||||
|
||||
|
||||
memorablePasswordGeneratorCell = UITableViewCell(style: .default, reuseIdentifier: "default")
|
||||
memorablePasswordGeneratorCell?.textLabel?.text = "Get a Memorable One: xkpasswd"
|
||||
memorablePasswordGeneratorCell?.textLabel?.textColor = Globals.blue
|
||||
memorablePasswordGeneratorCell?.selectionStyle = .default
|
||||
memorablePasswordGeneratorCell?.accessoryType = .disclosureIndicator
|
||||
}
|
||||
|
||||
|
||||
override func viewDidLoad() {
|
||||
super.viewDidLoad()
|
||||
if navigationItemTitle != nil {
|
||||
navigationItem.title = navigationItemTitle
|
||||
}
|
||||
|
||||
|
||||
tableView.register(UINib(nibName: "TextFieldTableViewCell", bundle: nil), forCellReuseIdentifier: "textFieldCell")
|
||||
tableView.register(UINib(nibName: "TextViewTableViewCell", bundle: nil), forCellReuseIdentifier: "textViewCell")
|
||||
tableView.register(UINib(nibName: "FillPasswordTableViewCell", bundle: nil), forCellReuseIdentifier: "fillPasswordCell")
|
||||
tableView.register(UINib(nibName: "SliderTableViewCell", bundle: nil), forCellReuseIdentifier: "passwordLengthCell")
|
||||
|
||||
|
||||
tableView.rowHeight = UITableViewAutomaticDimension
|
||||
tableView.estimatedRowHeight = 48
|
||||
self.tableView.sectionFooterHeight = UITableViewAutomaticDimension;
|
||||
|
|
@ -83,10 +83,10 @@ class PasswordEditorTableViewController: UITableViewController, FillPasswordTabl
|
|||
override func viewDidLayoutSubviews() {
|
||||
additionsCell?.contentTextView.setContentOffset(.zero, animated: false)
|
||||
}
|
||||
|
||||
|
||||
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
|
||||
let cellData = tableData[indexPath.section][indexPath.row]
|
||||
|
||||
|
||||
switch cellData[PasswordEditorCellKey.type] as! PasswordEditorCellType {
|
||||
case .nameCell:
|
||||
nameCell = tableView.dequeueReusableCell(withIdentifier: "textFieldCell", for: indexPath) as? TextFieldTableViewCell
|
||||
|
|
@ -132,7 +132,7 @@ class PasswordEditorTableViewController: UITableViewController, FillPasswordTabl
|
|||
return scanQRCodeCell!
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
override func tableView(_ tableView: UITableView, heightForHeaderInSection section: Int) -> CGFloat {
|
||||
return 44
|
||||
}
|
||||
|
|
@ -153,11 +153,11 @@ class PasswordEditorTableViewController: UITableViewController, FillPasswordTabl
|
|||
override func tableView(_ tableView: UITableView, titleForHeaderInSection section: Int) -> String? {
|
||||
return sectionHeaderTitles[section]
|
||||
}
|
||||
|
||||
|
||||
override func tableView(_ tableView: UITableView, titleForFooterInSection section: Int) -> String? {
|
||||
return sectionFooterTitles[section]
|
||||
}
|
||||
|
||||
|
||||
override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
|
||||
let selectedCell = tableView.cellForRow(at: indexPath)
|
||||
if selectedCell == deletePasswordCell {
|
||||
|
|
@ -175,12 +175,12 @@ class PasswordEditorTableViewController: UITableViewController, FillPasswordTabl
|
|||
let vc = SFSafariViewController(url: url, entersReaderIfAvailable: false)
|
||||
vc.delegate = self
|
||||
present(vc, animated: true)
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
tableView.deselectRow(at: indexPath, animated: true)
|
||||
}
|
||||
|
||||
|
||||
// generate password, copy to pasteboard, and set the cell
|
||||
// check whether the current password looks like an OTP field
|
||||
func generateAndCopyPassword() {
|
||||
|
|
@ -195,23 +195,23 @@ class PasswordEditorTableViewController: UITableViewController, FillPasswordTabl
|
|||
self.generateAndCopyPasswordNoOtpCheck()
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// generate the password, don't care whether the original line is otp
|
||||
func generateAndCopyPasswordNoOtpCheck() {
|
||||
// show password settings (e.g., the length slider)
|
||||
showPasswordSettings()
|
||||
|
||||
|
||||
let length = passwordLengthCell?.roundedValue ?? 0
|
||||
let plainPassword = PasswordGeneratorFlavour.from(SharedDefaults[.passwordGeneratorFlavor]).generatePassword(length: length)
|
||||
SecurePasteboard.shared.copy(textToCopy: plainPassword)
|
||||
|
||||
|
||||
// update tableData so to make sure reloadData() works correctly
|
||||
tableData[passwordSection][0][PasswordEditorCellKey.content] = plainPassword
|
||||
|
||||
|
||||
// update cell manually, no need to call reloadData()
|
||||
fillPasswordCell?.setContent(content: plainPassword)
|
||||
}
|
||||
|
||||
|
||||
// show password settings (e.g., the length slider)
|
||||
func showPasswordSettings() {
|
||||
if hidePasswordSettings == true {
|
||||
|
|
@ -219,13 +219,13 @@ class PasswordEditorTableViewController: UITableViewController, FillPasswordTabl
|
|||
tableView.reloadSections([passwordSection], with: .fade)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// show/hide password settings (e.g., the length slider)
|
||||
func showHidePasswordSettings() {
|
||||
hidePasswordSettings = !hidePasswordSettings
|
||||
tableView.reloadSections([passwordSection], with: .fade)
|
||||
}
|
||||
|
||||
|
||||
func insertScannedOTPFields(_ otpauth: String) {
|
||||
// update tableData
|
||||
var additionsString = ""
|
||||
|
|
@ -235,11 +235,11 @@ class PasswordEditorTableViewController: UITableViewController, FillPasswordTabl
|
|||
additionsString = otpauth
|
||||
}
|
||||
tableData[additionsSection][0][PasswordEditorCellKey.content] = additionsString
|
||||
|
||||
|
||||
// reload the additions cell
|
||||
additionsCell?.setContent(content: additionsString)
|
||||
}
|
||||
|
||||
|
||||
// MARK: - QRScannerControllerDelegate Methods
|
||||
func checkScannedOutput(line: String) -> (accept: Bool, message: String) {
|
||||
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")
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// MARK: - QRScannerControllerDelegate Methods
|
||||
func handleScannedOutput(line: String) {
|
||||
insertScannedOTPFields(line)
|
||||
}
|
||||
|
||||
|
||||
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
|
||||
if segue.identifier == "showQRScannerSegue" {
|
||||
if let navController = segue.destination as? UINavigationController {
|
||||
|
|
@ -265,7 +265,7 @@ class PasswordEditorTableViewController: UITableViewController, FillPasswordTabl
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// update tableData so to make sure reloadData() works correctly
|
||||
func textFieldDidEndEditing(_ textField: UITextField) {
|
||||
if textField == nameCell?.contentTextField {
|
||||
|
|
@ -277,48 +277,48 @@ class PasswordEditorTableViewController: UITableViewController, FillPasswordTabl
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// update tableData so to make sure reloadData() works correctly
|
||||
func textViewDidEndEditing(_ textView: UITextView) {
|
||||
if textView == additionsCell?.contentTextView {
|
||||
tableData[additionsSection][0][PasswordEditorCellKey.content] = additionsCell?.getContent()
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
func textFieldDidBeginEditing(_ textField: UITextField) {
|
||||
if textField == fillPasswordCell?.contentTextField {
|
||||
// show password generation settings automatically
|
||||
showPasswordSettings()
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
func getNameURL() -> (String, URL) {
|
||||
let encodedName = (nameCell?.getContent()?.stringByAddingPercentEncodingForRFC3986())!
|
||||
let name = URL(string: encodedName)!.lastPathComponent
|
||||
let url = URL(string: encodedName)!.appendingPathExtension("gpg")
|
||||
return (name, url)
|
||||
}
|
||||
|
||||
|
||||
func checkName() -> Bool {
|
||||
// the name field should not be empty
|
||||
guard let name = nameCell?.getContent(), name.isEmpty == false else {
|
||||
Utils.alert(title: "Cannot Save", message: "Please fill in the name.", controller: self, completion: nil)
|
||||
return false
|
||||
}
|
||||
|
||||
|
||||
// the name should not start with /
|
||||
guard name.hasPrefix("/") == false else {
|
||||
Utils.alert(title: "Cannot Save", message: "Please remove the prefix \"/\" from your password name.", controller: self, completion: nil)
|
||||
return false
|
||||
}
|
||||
|
||||
|
||||
// the name field should be a valid url
|
||||
guard let path = name.stringByAddingPercentEncodingForRFC3986(),
|
||||
var passwordURL = URL(string: path) else {
|
||||
Utils.alert(title: "Cannot Save", message: "Password name is invalid.", controller: self, completion: nil)
|
||||
return false
|
||||
}
|
||||
|
||||
|
||||
// check whether we can parse the filename (be consistent with PasswordStore::addPasswordEntities)
|
||||
var previousPathLength = Int.max
|
||||
while passwordURL.path != "." {
|
||||
|
|
@ -329,10 +329,10 @@ class PasswordEditorTableViewController: UITableViewController, FillPasswordTabl
|
|||
}
|
||||
previousPathLength = passwordURL.path.count
|
||||
}
|
||||
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
|
||||
func safariViewControllerDidFinish(_ controller: SFSafariViewController) {
|
||||
let copiedLinesSplit = UIPasteboard.general.string?.components(separatedBy: CharacterSet.whitespacesAndNewlines).filter({ !$0.isEmpty })
|
||||
if copiedLinesSplit?.count ?? 0 > 0 {
|
||||
|
|
@ -353,5 +353,5 @@ class PasswordEditorTableViewController: UITableViewController, FillPasswordTabl
|
|||
self.present(alert, animated: true, completion: nil)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -27,13 +27,13 @@ class PasswordsViewController: UIViewController, UITableViewDataSource, UITableV
|
|||
private var filteredPasswordsTableEntries: [PasswordsTableEntry] = []
|
||||
private var parentPasswordEntity: PasswordEntity? = nil
|
||||
private let passwordStore = PasswordStore.shared
|
||||
|
||||
|
||||
private var tapTabBarTime: TimeInterval = 0
|
||||
|
||||
private var sections = [(title: String, entries: [PasswordsTableEntry])]()
|
||||
|
||||
|
||||
private var searchActive : Bool = false
|
||||
|
||||
|
||||
private lazy var searchController: UISearchController = {
|
||||
let uiSearchController = UISearchController(searchResultsController: nil)
|
||||
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(_:)))
|
||||
return backUIBarButtonItem
|
||||
}()
|
||||
|
||||
|
||||
private lazy var transitionFromRight: CATransition = {
|
||||
let transition = CATransition()
|
||||
transition.type = kCATransitionPush
|
||||
|
|
@ -69,7 +69,7 @@ class PasswordsViewController: UIViewController, UITableViewDataSource, UITableV
|
|||
transition.subtype = kCATransitionFromRight
|
||||
return transition
|
||||
}()
|
||||
|
||||
|
||||
private lazy var transitionFromLeft: CATransition = {
|
||||
let transition = CATransition()
|
||||
transition.type = kCATransitionPush
|
||||
|
|
@ -81,7 +81,7 @@ class PasswordsViewController: UIViewController, UITableViewDataSource, UITableV
|
|||
}()
|
||||
|
||||
@IBOutlet weak var tableView: UITableView!
|
||||
|
||||
|
||||
private func initPasswordsTableEntries(parent: PasswordEntity?) {
|
||||
passwordsTableEntries.removeAll()
|
||||
passwordsTableAllEntries.removeAll()
|
||||
|
|
@ -102,9 +102,9 @@ class PasswordsViewController: UIViewController, UITableViewDataSource, UITableV
|
|||
}
|
||||
parentPasswordEntity = parent
|
||||
}
|
||||
|
||||
|
||||
@IBAction func cancelAddPassword(segue: UIStoryboardSegue) {
|
||||
|
||||
|
||||
}
|
||||
@IBAction func saveAddPassword(segue: UIStoryboardSegue) {
|
||||
if let controller = segue.source as? AddPasswordTableViewController {
|
||||
|
|
@ -127,7 +127,7 @@ class PasswordsViewController: UIViewController, UITableViewDataSource, UITableV
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private func syncPasswords() {
|
||||
guard passwordStore.repositoryExisted() else {
|
||||
DispatchQueue.main.asyncAfter(deadline: .now() + .milliseconds(800)) {
|
||||
|
|
@ -190,17 +190,17 @@ class PasswordsViewController: UIViewController, UITableViewDataSource, UITableV
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
override func viewDidAppear(_ animated: Bool) {
|
||||
super.viewDidAppear(animated)
|
||||
|
||||
|
||||
if SharedDefaults[.isShowFolderOn] {
|
||||
searchController.searchBar.scopeButtonTitles = ["Current", "All"]
|
||||
} else {
|
||||
searchController.searchBar.scopeButtonTitles = nil
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
override func viewDidLoad() {
|
||||
super.viewDidLoad()
|
||||
searchController.searchBar.delegate = self
|
||||
|
|
@ -220,22 +220,22 @@ class PasswordsViewController: UIViewController, UITableViewDataSource, UITableV
|
|||
tableView.refreshControl = syncControl
|
||||
SVProgressHUD.setDefaultMaskType(.black)
|
||||
tableView.register(UINib(nibName: "PasswordWithFolderTableViewCell", bundle: nil), forCellReuseIdentifier: "passwordWithFolderTableViewCell")
|
||||
|
||||
|
||||
// initialize the password table
|
||||
reloadTableView(parent: nil)
|
||||
|
||||
|
||||
// reset the data table if some password (maybe another one) has been updated
|
||||
NotificationCenter.default.addObserver(self, selector: #selector(actOnReloadTableViewRelatedNotification), name: .passwordStoreUpdated, object: nil)
|
||||
// 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(actOnSearchNotification), name: .passwordSearch, object: nil)
|
||||
|
||||
|
||||
// listen to the swipe back guesture
|
||||
let swipeRight = UISwipeGestureRecognizer(target: self, action: #selector(self.respondToSwipeGesture))
|
||||
swipeRight.direction = UISwipeGestureRecognizerDirection.right
|
||||
self.view.addGestureRecognizer(swipeRight)
|
||||
}
|
||||
|
||||
|
||||
override func viewWillAppear(_ animated: Bool) {
|
||||
super.viewWillAppear(animated)
|
||||
tabBarController!.delegate = self
|
||||
|
|
@ -243,7 +243,7 @@ class PasswordsViewController: UIViewController, UITableViewDataSource, UITableV
|
|||
tableView.deselectRow(at: path, animated: false)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
override func viewWillLayoutSubviews() {
|
||||
super.viewWillLayoutSubviews()
|
||||
guard #available(iOS 11, *) else {
|
||||
|
|
@ -252,21 +252,21 @@ class PasswordsViewController: UIViewController, UITableViewDataSource, UITableV
|
|||
return
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
func numberOfSections(in tableView: UITableView) -> Int {
|
||||
return sections.count
|
||||
}
|
||||
|
||||
|
||||
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
|
||||
return sections[section].entries.count
|
||||
}
|
||||
|
||||
|
||||
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
|
||||
let longPressGestureRecognizer = UILongPressGestureRecognizer(target: self, action: #selector(longPressAction(_:)))
|
||||
longPressGestureRecognizer.minimumPressDuration = 0.6
|
||||
if SharedDefaults[.isShowFolderOn] && searchController.searchBar.selectedScopeButtonIndex == 0{
|
||||
let cell = tableView.dequeueReusableCell(withIdentifier: "passwordTableViewCell", for: indexPath)
|
||||
|
||||
|
||||
let entry = getPasswordEntry(by: indexPath)
|
||||
if entry.passwordEntity!.synced {
|
||||
cell.textLabel?.text = entry.title
|
||||
|
|
@ -299,11 +299,11 @@ class PasswordsViewController: UIViewController, UITableViewDataSource, UITableV
|
|||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
private func getPasswordEntry(by indexPath: IndexPath) -> PasswordsTableEntry {
|
||||
return sections[indexPath.section].entries[indexPath.row]
|
||||
}
|
||||
|
||||
|
||||
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
|
||||
let entry = getPasswordEntry(by: indexPath)
|
||||
if !entry.isDir {
|
||||
|
|
@ -318,7 +318,7 @@ class PasswordsViewController: UIViewController, UITableViewDataSource, UITableV
|
|||
reloadTableView(parent: entry.passwordEntity, anim: transitionFromRight)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@objc func respondToSwipeGesture(gesture: UIGestureRecognizer) {
|
||||
if let swipeGesture = gesture as? UISwipeGestureRecognizer {
|
||||
// swipe right -> swipe back
|
||||
|
|
@ -327,7 +327,7 @@ class PasswordsViewController: UIViewController, UITableViewDataSource, UITableV
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@objc func backAction(_ sender: Any?) {
|
||||
guard SharedDefaults[.isShowFolderOn] else { return }
|
||||
var anim: CATransition? = transitionFromLeft
|
||||
|
|
@ -336,7 +336,7 @@ class PasswordsViewController: UIViewController, UITableViewDataSource, UITableV
|
|||
}
|
||||
reloadTableView(parent: parentPasswordEntity?.parent, anim: anim)
|
||||
}
|
||||
|
||||
|
||||
@objc func longPressAction(_ gesture: UILongPressGestureRecognizer) {
|
||||
if gesture.state == UIGestureRecognizerState.began {
|
||||
let touchPoint = gesture.location(in: tableView)
|
||||
|
|
@ -345,7 +345,7 @@ class PasswordsViewController: UIViewController, UITableViewDataSource, UITableV
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
func tableView(_ tableView: UITableView, titleForHeaderInSection section: Int) -> String? {
|
||||
return sections[section].title
|
||||
}
|
||||
|
|
@ -357,11 +357,11 @@ class PasswordsViewController: UIViewController, UITableViewDataSource, UITableV
|
|||
func tableView(_ tableView: UITableView, sectionForSectionIndexTitle title: String, at index: Int) -> Int {
|
||||
return index
|
||||
}
|
||||
|
||||
|
||||
func tableView(_ tableView: UITableView, accessoryButtonTappedForRowWith indexPath: IndexPath) {
|
||||
decryptThenCopyPassword(from: indexPath)
|
||||
}
|
||||
|
||||
|
||||
private func requestPGPKeyPassphrase() -> String {
|
||||
let sem = DispatchSemaphore(value: 0)
|
||||
var passphrase = ""
|
||||
|
|
@ -389,7 +389,7 @@ class PasswordsViewController: UIViewController, UITableViewDataSource, UITableV
|
|||
}
|
||||
return passphrase
|
||||
}
|
||||
|
||||
|
||||
private func decryptThenCopyPassword(from indexPath: IndexPath) {
|
||||
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)
|
||||
|
|
@ -418,39 +418,39 @@ class PasswordsViewController: UIViewController, UITableViewDataSource, UITableV
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private func generateSections(item: [PasswordsTableEntry]) {
|
||||
let collation = UILocalizedIndexedCollation.current()
|
||||
let sectionTitles = collation.sectionIndexTitles
|
||||
var newSections = [(title: String, entries: [PasswordsTableEntry])]()
|
||||
|
||||
|
||||
// initialize all sections
|
||||
for i in 0..<sectionTitles.count {
|
||||
newSections.append((title: sectionTitles[i], entries: [PasswordsTableEntry]()))
|
||||
}
|
||||
|
||||
|
||||
// put entries into sections
|
||||
for entry in item {
|
||||
let sectionNumber = collation.section(for: entry, collationStringSelector: #selector(getter: PasswordsTableEntry.title))
|
||||
newSections[sectionNumber].entries.append(entry)
|
||||
}
|
||||
|
||||
|
||||
// sort each list and set sectionTitles
|
||||
for i in 0..<sectionTitles.count {
|
||||
let entriesToSort = newSections[i].entries
|
||||
let sortedEntries = collation.sortedArray(from: entriesToSort, collationStringSelector: #selector(getter: PasswordsTableEntry.title))
|
||||
newSections[i].entries = sortedEntries as! [PasswordsTableEntry]
|
||||
}
|
||||
|
||||
|
||||
// only keep non-empty sections
|
||||
sections = newSections.filter {$0.entries.count > 0}
|
||||
}
|
||||
|
||||
|
||||
@objc func actOnSearchNotification() {
|
||||
searchController.searchBar.becomeFirstResponder()
|
||||
}
|
||||
|
||||
|
||||
|
||||
override func shouldPerformSegue(withIdentifier identifier: String, sender: Any?) -> Bool {
|
||||
if identifier == "showPasswordDetail" {
|
||||
guard self.passwordStore.privateKey != nil else {
|
||||
|
|
@ -487,7 +487,7 @@ class PasswordsViewController: UIViewController, UITableViewDataSource, UITableV
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
func filterContentForSearchText(searchText: String, scope: String = "All") {
|
||||
switch scope {
|
||||
case "All":
|
||||
|
|
@ -512,10 +512,10 @@ class PasswordsViewController: UIViewController, UITableViewDataSource, UITableV
|
|||
default:
|
||||
break
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
private func reloadTableView(data: [PasswordsTableEntry], anim: CAAnimation? = nil) {
|
||||
// set navigation item
|
||||
let numberOfLocalCommits = self.passwordStore.numberOfLocalCommits
|
||||
|
|
@ -529,7 +529,7 @@ class PasswordsViewController: UIViewController, UITableViewDataSource, UITableV
|
|||
} else {
|
||||
navigationItem.leftBarButtonItem = nil
|
||||
}
|
||||
|
||||
|
||||
// set the password table
|
||||
generateSections(item: data)
|
||||
if anim != nil {
|
||||
|
|
@ -537,7 +537,7 @@ class PasswordsViewController: UIViewController, UITableViewDataSource, UITableV
|
|||
}
|
||||
tableView.reloadData()
|
||||
self.tableView.layer.removeAnimation(forKey: "UITableViewReloadDataAnimationKey")
|
||||
|
||||
|
||||
// set the sync control title
|
||||
let atribbutedTitle = "Last Synced: \(lastSyncedTimeString())"
|
||||
syncControl.attributedTitle = NSAttributedString(string: atribbutedTitle)
|
||||
|
|
@ -552,12 +552,12 @@ class PasswordsViewController: UIViewController, UITableViewDataSource, UITableV
|
|||
formatter.timeStyle = .short
|
||||
return formatter.string(from: date)
|
||||
}
|
||||
|
||||
|
||||
private func reloadTableView(parent: PasswordEntity?, anim: CAAnimation? = nil) {
|
||||
initPasswordsTableEntries(parent: parent)
|
||||
reloadTableView(data: passwordsTableEntries, anim: anim)
|
||||
}
|
||||
|
||||
|
||||
@objc func actOnReloadTableViewRelatedNotification() {
|
||||
DispatchQueue.main.async { [weak weakSelf = self] in
|
||||
guard let strongSelf = weakSelf else { return }
|
||||
|
|
@ -565,11 +565,11 @@ class PasswordsViewController: UIViewController, UITableViewDataSource, UITableV
|
|||
strongSelf.reloadTableView(data: strongSelf.passwordsTableEntries)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@objc func handleRefresh(_ syncControl: UIRefreshControl) {
|
||||
syncPasswords()
|
||||
}
|
||||
|
||||
|
||||
func tabBarController(_ tabBarController: UITabBarController, didSelect viewController: UIViewController) {
|
||||
if viewController == self.navigationController {
|
||||
let currentTime = Date().timeIntervalSince1970
|
||||
|
|
@ -586,14 +586,14 @@ class PasswordsViewController: UIViewController, UITableViewDataSource, UITableV
|
|||
backAction(self)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
func searchBar(_ searchBar: UISearchBar, selectedScopeButtonIndexDidChange selectedScope: Int) {
|
||||
// update the default search scope
|
||||
SharedDefaults[.isSearchDefaultAll] = searchController.searchBar.scopeButtonTitles![selectedScope] == "All"
|
||||
updateSearchResults(for: searchController)
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
func searchBarShouldBeginEditing(_ searchBar: UISearchBar) -> Bool {
|
||||
// set the default search scope to "all"
|
||||
if SharedDefaults[.isShowFolderOn] && SharedDefaults[.isSearchDefaultAll] {
|
||||
|
|
@ -603,14 +603,14 @@ class PasswordsViewController: UIViewController, UITableViewDataSource, UITableV
|
|||
}
|
||||
return true
|
||||
}
|
||||
|
||||
|
||||
func searchBarShouldEndEditing(_ searchBar: UISearchBar) -> Bool {
|
||||
// set the default search scope to "current"
|
||||
searchController.searchBar.selectedScopeButtonIndex = 0
|
||||
updateSearchResults(for: searchController)
|
||||
return true
|
||||
}
|
||||
|
||||
|
||||
private func requestGitPassword(credential: GitCredential.Credential, lastPassword: String?) -> String? {
|
||||
let sem = DispatchSemaphore(value: 0)
|
||||
var password: String?
|
||||
|
|
@ -621,7 +621,7 @@ class PasswordsViewController: UIViewController, UITableViewDataSource, UITableV
|
|||
case .ssh:
|
||||
message = "Please fill in the password of your SSH key."
|
||||
}
|
||||
|
||||
|
||||
DispatchQueue.main.async {
|
||||
SVProgressHUD.dismiss()
|
||||
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)
|
||||
}
|
||||
|
||||
|
||||
let _ = sem.wait(timeout: .distantFuture)
|
||||
return password
|
||||
}
|
||||
|
|
|
|||
|
|
@ -17,90 +17,90 @@ protocol QRScannerControllerDelegate {
|
|||
}
|
||||
|
||||
class QRScannerController: UIViewController, AVCaptureMetadataOutputObjectsDelegate {
|
||||
|
||||
|
||||
@IBOutlet weak var scannerOutput: UILabel!
|
||||
|
||||
|
||||
var captureSession: AVCaptureSession?
|
||||
var videoPreviewLayer: AVCaptureVideoPreviewLayer?
|
||||
var qrCodeFrameView: UIView?
|
||||
|
||||
|
||||
let supportedCodeTypes = [AVMetadataObject.ObjectType.qr]
|
||||
|
||||
|
||||
var delegate: QRScannerControllerDelegate?
|
||||
|
||||
|
||||
override func viewDidLoad() {
|
||||
super.viewDidLoad()
|
||||
|
||||
|
||||
if AVCaptureDevice.authorizationStatus(for: .video) == .denied {
|
||||
presentCameraSettings()
|
||||
}
|
||||
|
||||
|
||||
// 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)
|
||||
do {
|
||||
// Get an instance of the AVCaptureDeviceInput class using the previous device object.
|
||||
let input = try AVCaptureDeviceInput(device: captureDevice!)
|
||||
|
||||
|
||||
// Initialize the captureSession object.
|
||||
captureSession = AVCaptureSession()
|
||||
|
||||
// Set the input device on the capture session.
|
||||
captureSession?.addInput(input)
|
||||
|
||||
|
||||
// Initialize a AVCaptureMetadataOutput object and set it as the output device to the capture session.
|
||||
let captureMetadataOutput = AVCaptureMetadataOutput()
|
||||
captureSession?.addOutput(captureMetadataOutput)
|
||||
|
||||
|
||||
// Set delegate and use the default dispatch queue to execute the call back
|
||||
captureMetadataOutput.setMetadataObjectsDelegate(self, queue: DispatchQueue.main)
|
||||
captureMetadataOutput.metadataObjectTypes = supportedCodeTypes
|
||||
|
||||
|
||||
// Initialize the video preview layer and add it as a sublayer to the viewPreview view's layer.
|
||||
videoPreviewLayer = AVCaptureVideoPreviewLayer(session: captureSession!)
|
||||
videoPreviewLayer?.videoGravity = AVLayerVideoGravity.resizeAspectFill
|
||||
videoPreviewLayer?.frame = view.layer.bounds
|
||||
view.layer.addSublayer(videoPreviewLayer!)
|
||||
|
||||
|
||||
// Start video capture.
|
||||
captureSession?.startRunning()
|
||||
|
||||
|
||||
// Move the message label to the front
|
||||
scannerOutput.layer.cornerRadius = 10
|
||||
scannerOutput.text = "No QR code detected"
|
||||
view.bringSubview(toFront: scannerOutput)
|
||||
|
||||
|
||||
// Initialize QR Code Frame to highlight the QR code
|
||||
qrCodeFrameView = UIView()
|
||||
|
||||
|
||||
if let qrCodeFrameView = qrCodeFrameView {
|
||||
qrCodeFrameView.layer.borderColor = UIColor.green.cgColor
|
||||
qrCodeFrameView.layer.borderWidth = 2
|
||||
view.addSubview(qrCodeFrameView)
|
||||
view.bringSubview(toFront: qrCodeFrameView)
|
||||
}
|
||||
|
||||
|
||||
} catch {
|
||||
scannerOutput.text = error.localizedDescription
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
override func didReceiveMemoryWarning() {
|
||||
super.didReceiveMemoryWarning()
|
||||
// Dispose of any resources that can be recreated.
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
// MARK: - AVCaptureMetadataOutputObjectsDelegate Methods
|
||||
|
||||
|
||||
func metadataOutput(_ captureOutput: AVCaptureMetadataOutput, didOutput metadataObjects: [AVMetadataObject], from connection: AVCaptureConnection) {
|
||||
|
||||
|
||||
if let metadataObj = metadataObjects.first as? AVMetadataMachineReadableCodeObject,
|
||||
supportedCodeTypes.contains(metadataObj.type),
|
||||
let barCodeObject = videoPreviewLayer?.transformedMetadataObject(for: metadataObj) {
|
||||
|
||||
|
||||
// draw a bounds on the found QR code
|
||||
qrCodeFrameView?.frame = barCodeObject.bounds
|
||||
|
||||
|
||||
// check whether it is a valid result
|
||||
if let scanned = metadataObj.stringValue {
|
||||
if let (accept, message) = delegate?.checkScannedOutput(line: scanned) {
|
||||
|
|
@ -121,13 +121,13 @@ class QRScannerController: UIViewController, AVCaptureMetadataOutputObjectsDeleg
|
|||
} else {
|
||||
scannerOutput.text = "No string value"
|
||||
}
|
||||
|
||||
|
||||
} else {
|
||||
qrCodeFrameView?.frame = CGRect.zero
|
||||
scannerOutput.text = "No QR code detected"
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
func presentCameraSettings() {
|
||||
let alertController = UIAlertController(title: "Error",
|
||||
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)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -13,7 +13,7 @@ class RawPasswordViewController: UIViewController {
|
|||
|
||||
@IBOutlet weak var rawPasswordTextView: UITextView!
|
||||
var password: Password?
|
||||
|
||||
|
||||
override func viewDidLoad() {
|
||||
super.viewDidLoad()
|
||||
navigationItem.title = password?.name
|
||||
|
|
|
|||
|
|
@ -14,21 +14,21 @@ class SSHKeySettingTableViewController: UITableViewController {
|
|||
|
||||
@IBOutlet weak var privateKeyURLTextField: UITextField!
|
||||
let passwordStore = PasswordStore.shared
|
||||
|
||||
|
||||
override func viewDidLoad() {
|
||||
super.viewDidLoad()
|
||||
privateKeyURLTextField.text = SharedDefaults[.gitSSHPrivateKeyURL]?.absoluteString
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
@IBAction func doneButtonTapped(_ sender: UIButton) {
|
||||
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)
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
SharedDefaults[.gitSSHPrivateKeyURL] = privateKeyURL
|
||||
|
||||
|
||||
do {
|
||||
try Data(contentsOf: privateKeyURL).write(to: URL(fileURLWithPath: Globals.gitSSHPrivateKeyPath), options: .atomic)
|
||||
} catch {
|
||||
|
|
|
|||
|
|
@ -13,7 +13,7 @@ class SettingsSplitViewController: UISplitViewController, UISplitViewControllerD
|
|||
self.delegate = self
|
||||
self.preferredDisplayMode = .allVisible
|
||||
}
|
||||
|
||||
|
||||
func splitViewController(
|
||||
_ splitViewController: UISplitViewController,
|
||||
collapseSecondary secondaryViewController: UIViewController,
|
||||
|
|
|
|||
|
|
@ -16,14 +16,14 @@ class SettingsTableViewController: UITableViewController, UITabBarControllerDele
|
|||
@IBOutlet weak var passcodeTableViewCell: UITableViewCell!
|
||||
@IBOutlet weak var passwordRepositoryTableViewCell: UITableViewCell!
|
||||
var setPasscodeLockAlert: UIAlertController?
|
||||
|
||||
|
||||
let passwordStore = PasswordStore.shared
|
||||
var passcodeLock = PasscodeLock.shared
|
||||
|
||||
|
||||
func tabBarController(_ tabBarController: UITabBarController, didSelect viewController: UIViewController) {
|
||||
navigationController?.popViewController(animated: true)
|
||||
}
|
||||
|
||||
|
||||
@IBAction func savePGPKey(segue: UIStoryboardSegue) {
|
||||
if let controller = segue.source as? PGPKeySettingTableViewController {
|
||||
SharedDefaults[.pgpPrivateKeyURL] = URL(string: controller.pgpPrivateKeyURLTextField.text!.trimmed)
|
||||
|
|
@ -32,7 +32,7 @@ class SettingsTableViewController: UITableViewController, UITabBarControllerDele
|
|||
self.passwordStore.pgpKeyPassphrase = controller.pgpPassphrase
|
||||
}
|
||||
SharedDefaults[.pgpKeySource] = "url"
|
||||
|
||||
|
||||
SVProgressHUD.setDefaultMaskType(.black)
|
||||
SVProgressHUD.setDefaultStyle(.light)
|
||||
SVProgressHUD.show(withStatus: "Fetching PGP Key")
|
||||
|
|
@ -53,7 +53,7 @@ class SettingsTableViewController: UITableViewController, UITabBarControllerDele
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
} else if let controller = segue.source as? PGPKeyArmorSettingTableViewController {
|
||||
SharedDefaults[.pgpKeySource] = "armor"
|
||||
if SharedDefaults[.isRememberPGPPassphraseOn] {
|
||||
|
|
@ -62,7 +62,7 @@ class SettingsTableViewController: UITableViewController, UITabBarControllerDele
|
|||
|
||||
SharedDefaults[.pgpPublicKeyArmor] = controller.armorPublicKeyTextView.text!
|
||||
SharedDefaults[.pgpPrivateKeyArmor] = controller.armorPrivateKeyTextView.text!
|
||||
|
||||
|
||||
SVProgressHUD.setDefaultMaskType(.black)
|
||||
SVProgressHUD.setDefaultStyle(.light)
|
||||
SVProgressHUD.show(withStatus: "Fetching PGP Key")
|
||||
|
|
@ -84,7 +84,7 @@ class SettingsTableViewController: UITableViewController, UITabBarControllerDele
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private func saveImportedPGPKey() {
|
||||
// load keys
|
||||
SharedDefaults[.pgpKeySource] = "file"
|
||||
|
|
@ -108,11 +108,11 @@ class SettingsTableViewController: UITableViewController, UITabBarControllerDele
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@IBAction func saveGitServerSetting(segue: UIStoryboardSegue) {
|
||||
self.passwordRepositoryTableViewCell.detailTextLabel?.text = SharedDefaults[.gitURL]?.host
|
||||
}
|
||||
|
||||
|
||||
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
|
||||
return super.tableView(tableView, numberOfRowsInSection: section)
|
||||
}
|
||||
|
|
@ -125,12 +125,12 @@ class SettingsTableViewController: UITableViewController, UITabBarControllerDele
|
|||
setPasswordRepositoryTableViewCellDetailText()
|
||||
setPasscodeLockCell()
|
||||
}
|
||||
|
||||
|
||||
override func viewWillAppear(_ animated: Bool) {
|
||||
super.viewWillAppear(true)
|
||||
tabBarController!.delegate = self
|
||||
}
|
||||
|
||||
|
||||
private func setPasscodeLockCell() {
|
||||
if passcodeLock.hasPasscode {
|
||||
self.passcodeTableViewCell.detailTextLabel?.text = "On"
|
||||
|
|
@ -138,7 +138,7 @@ class SettingsTableViewController: UITableViewController, UITabBarControllerDele
|
|||
self.passcodeTableViewCell.detailTextLabel?.text = "Off"
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private func setPGPKeyTableViewCellDetailText() {
|
||||
if let pgpKeyID = self.passwordStore.pgpKeyID {
|
||||
pgpKeyTableViewCell.detailTextLabel?.text = pgpKeyID
|
||||
|
|
@ -146,7 +146,7 @@ class SettingsTableViewController: UITableViewController, UITabBarControllerDele
|
|||
pgpKeyTableViewCell.detailTextLabel?.text = "Not Set"
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private func setPasswordRepositoryTableViewCellDetailText() {
|
||||
if SharedDefaults[.gitURL] == nil {
|
||||
passwordRepositoryTableViewCell.detailTextLabel?.text = "Not Set"
|
||||
|
|
@ -154,13 +154,13 @@ class SettingsTableViewController: UITableViewController, UITabBarControllerDele
|
|||
passwordRepositoryTableViewCell.detailTextLabel?.text = SharedDefaults[.gitURL]!.host
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@objc func actOnPasswordStoreErasedNotification() {
|
||||
setPGPKeyTableViewCellDetailText()
|
||||
setPasswordRepositoryTableViewCellDetailText()
|
||||
setPasscodeLockCell()
|
||||
}
|
||||
|
||||
|
||||
override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
|
||||
if tableView.cellForRow(at: indexPath) == passcodeTableViewCell {
|
||||
if SharedDefaults[.passcodeKey] != nil{
|
||||
|
|
@ -173,13 +173,13 @@ class SettingsTableViewController: UITableViewController, UITabBarControllerDele
|
|||
}
|
||||
tableView.deselectRow(at: indexPath, animated: true)
|
||||
}
|
||||
|
||||
|
||||
func showPGPKeyActionSheet() {
|
||||
let optionMenu = UIAlertController(title: nil, message: nil, preferredStyle: .actionSheet)
|
||||
var urlActionTitle = "Download from URL"
|
||||
var armorActionTitle = "ASCII-Armor Encrypted Key"
|
||||
var fileActionTitle = "iTunes File Sharing"
|
||||
|
||||
|
||||
if SharedDefaults[.pgpKeySource] == "url" {
|
||||
urlActionTitle = "✓ \(urlActionTitle)"
|
||||
} else if SharedDefaults[.pgpKeySource] == "armor" {
|
||||
|
|
@ -235,8 +235,8 @@ class SettingsTableViewController: UITableViewController, UITabBarControllerDele
|
|||
}
|
||||
optionMenu.addAction(fileAction)
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
if SharedDefaults[.pgpKeySource] != nil {
|
||||
let deleteAction = UIAlertAction(title: "Remove PGP Keys", style: .destructive) { _ in
|
||||
self.passwordStore.removePGPKeys()
|
||||
|
|
@ -249,12 +249,12 @@ class SettingsTableViewController: UITableViewController, UITabBarControllerDele
|
|||
optionMenu.popoverPresentationController?.sourceRect = pgpKeyTableViewCell.bounds
|
||||
self.present(optionMenu, animated: true, completion: nil)
|
||||
}
|
||||
|
||||
|
||||
func showPasscodeActionSheet() {
|
||||
let optionMenu = UIAlertController(title: nil, message: nil, preferredStyle: .actionSheet)
|
||||
let passcodeRemoveViewController = PasscodeLockViewController()
|
||||
|
||||
|
||||
|
||||
|
||||
let removePasscodeAction = UIAlertAction(title: "Remove Passcode", style: .destructive) { [weak self] _ in
|
||||
passcodeRemoveViewController.successCallback = {
|
||||
self?.passcodeLock.delete()
|
||||
|
|
@ -262,11 +262,11 @@ class SettingsTableViewController: UITableViewController, UITabBarControllerDele
|
|||
}
|
||||
self?.present(passcodeRemoveViewController, animated: true, completion: nil)
|
||||
}
|
||||
|
||||
|
||||
let changePasscodeAction = UIAlertAction(title: "Change Passcode", style: .default) { [weak self] _ in
|
||||
self?.setPasscodeLock()
|
||||
}
|
||||
|
||||
|
||||
let cancelAction = UIAlertAction(title: "Cancel", style: .cancel, handler: nil)
|
||||
optionMenu.addAction(removePasscodeAction)
|
||||
optionMenu.addAction(changePasscodeAction)
|
||||
|
|
@ -289,7 +289,7 @@ class SettingsTableViewController: UITableViewController, UITabBarControllerDele
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
func setPasscodeLock() {
|
||||
// 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)
|
||||
|
|
@ -303,7 +303,7 @@ class SettingsTableViewController: UITableViewController, UITabBarControllerDele
|
|||
textField.isSecureTextEntry = true
|
||||
textField.addTarget(self, action: #selector(self.alertTextFieldDidChange(_:)), for: UIControlEvents.editingChanged)
|
||||
})
|
||||
|
||||
|
||||
// save action
|
||||
let saveAction = UIAlertAction(title: "Save", style: .default) { (action:UIAlertAction) -> Void in
|
||||
let passcode: String = self.setPasscodeLockAlert!.textFields![0].text!
|
||||
|
|
@ -312,10 +312,10 @@ class SettingsTableViewController: UITableViewController, UITabBarControllerDele
|
|||
self.setPasscodeLockCell()
|
||||
}
|
||||
saveAction.isEnabled = false // disable the Save button by default
|
||||
|
||||
|
||||
// cancel action
|
||||
let cancelAction = UIAlertAction(title: "Cancel", style: .cancel, handler: nil)
|
||||
|
||||
|
||||
// present
|
||||
setPasscodeLockAlert?.addAction(saveAction)
|
||||
setPasscodeLockAlert?.addAction(cancelAction)
|
||||
|
|
|
|||
|
|
@ -19,7 +19,7 @@ class SpecialThanksTableViewController: BasicStaticTableViewController {
|
|||
["FlatIcon",
|
||||
"https://www.flaticon.com"],
|
||||
]
|
||||
|
||||
|
||||
override func viewDidLoad() {
|
||||
tableData.append([])
|
||||
for item in openSourceComponents {
|
||||
|
|
|
|||
|
|
@ -12,33 +12,33 @@ import UIKit
|
|||
class SecurePasteboard {
|
||||
public static let shared = SecurePasteboard()
|
||||
private var backgroundTaskID = UIBackgroundTaskInvalid
|
||||
|
||||
|
||||
func copy(textToCopy: String?, expirationTime: Double = 45) {
|
||||
// copy to the pasteboard
|
||||
UIPasteboard.general.string = textToCopy ?? ""
|
||||
|
||||
|
||||
// clean the pasteboard after expirationTime
|
||||
guard expirationTime > 0 else {
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
// exit the existing background task, if any
|
||||
if backgroundTaskID != UIBackgroundTaskInvalid {
|
||||
UIApplication.shared.endBackgroundTask(UIBackgroundTaskInvalid)
|
||||
self.backgroundTaskID = UIBackgroundTaskInvalid
|
||||
}
|
||||
|
||||
|
||||
backgroundTaskID = UIApplication.shared.beginBackgroundTask(expirationHandler: { [weak self] in
|
||||
UIPasteboard.general.string = ""
|
||||
UIApplication.shared.endBackgroundTask(UIBackgroundTaskInvalid)
|
||||
self?.backgroundTaskID = UIBackgroundTaskInvalid
|
||||
})
|
||||
|
||||
|
||||
DispatchQueue.global(qos: .utility).asyncAfter(deadline: .now() + expirationTime) { [weak self] in
|
||||
UIPasteboard.general.string = ""
|
||||
UIApplication.shared.endBackgroundTask(UIBackgroundTaskInvalid)
|
||||
self?.backgroundTaskID = UIBackgroundTaskInvalid
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -18,11 +18,11 @@ class ContentTableViewCell: UITableViewCell {
|
|||
override func setSelected(_ selected: Bool, animated: Bool) {
|
||||
super.setSelected(selected, animated: animated)
|
||||
}
|
||||
|
||||
|
||||
func getContent() -> String? {
|
||||
return nil
|
||||
}
|
||||
|
||||
|
||||
func setContent(content: String?) { }
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -18,15 +18,15 @@ class FillPasswordTableViewCell: ContentTableViewCell {
|
|||
|
||||
@IBOutlet weak var contentTextField: UITextField!
|
||||
var delegate: FillPasswordTableViewCellDelegate?
|
||||
|
||||
|
||||
@IBOutlet weak var settingButton: UIButton!
|
||||
@IBOutlet weak var generateButton: UIButton!
|
||||
|
||||
|
||||
override func awakeFromNib() {
|
||||
super.awakeFromNib()
|
||||
// Initialization code
|
||||
contentTextField.font = Globals.passwordFont
|
||||
|
||||
|
||||
// Force aspect ratio of button images
|
||||
settingButton.imageView?.contentMode = .scaleAspectFit
|
||||
generateButton.imageView?.contentMode = .scaleAspectFit
|
||||
|
|
@ -37,24 +37,24 @@ class FillPasswordTableViewCell: ContentTableViewCell {
|
|||
|
||||
// Configure the view for the selected state
|
||||
}
|
||||
|
||||
|
||||
@IBAction func generatePassword(_ sender: UIButton) {
|
||||
self.delegate?.generateAndCopyPassword()
|
||||
}
|
||||
|
||||
|
||||
@IBAction func showHidePasswordSettings() {
|
||||
self.delegate?.showHidePasswordSettings()
|
||||
}
|
||||
|
||||
|
||||
// re-color
|
||||
@IBAction func textFieldDidChange(_ sender: UITextField) {
|
||||
contentTextField.attributedText = Utils.attributedPassword(plainPassword: sender.text ?? "")
|
||||
}
|
||||
|
||||
|
||||
override func getContent() -> String? {
|
||||
return contentTextField.attributedText?.string
|
||||
}
|
||||
|
||||
|
||||
override func setContent(content: String?) {
|
||||
contentTextField.attributedText = Utils.attributedPassword(plainPassword: content ?? "")
|
||||
}
|
||||
|
|
|
|||
|
|
@ -19,19 +19,19 @@ class LabelTableViewCell: UITableViewCell {
|
|||
|
||||
@IBOutlet weak var contentLabel: UILabel!
|
||||
@IBOutlet weak var titleLabel: UILabel!
|
||||
|
||||
|
||||
private enum CellType {
|
||||
case password, URL, HOTP, other
|
||||
}
|
||||
|
||||
private var type = CellType.other
|
||||
private var isReveal = false
|
||||
|
||||
|
||||
weak var delegatePasswordTableView : PasswordDetailTableViewController?
|
||||
|
||||
|
||||
private var passwordDisplayButton: UIButton?
|
||||
private var buttons: UIView?
|
||||
|
||||
|
||||
var cellData: LabelTableViewCellData? {
|
||||
didSet {
|
||||
guard let title = cellData?.title, let content = cellData?.content else {
|
||||
|
|
@ -72,7 +72,7 @@ class LabelTableViewCell: UITableViewCell {
|
|||
updateButtons()
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
override var canBecomeFirstResponder: Bool {
|
||||
get {
|
||||
return true
|
||||
|
|
@ -103,7 +103,7 @@ class LabelTableViewCell: UITableViewCell {
|
|||
override func copy(_ sender: Any?) {
|
||||
SecurePasteboard.shared.copy(textToCopy: cellData?.content)
|
||||
}
|
||||
|
||||
|
||||
@objc func revealPassword(_ sender: Any?) {
|
||||
let plainPassword = cellData?.content ?? ""
|
||||
if type == .password {
|
||||
|
|
@ -114,7 +114,7 @@ class LabelTableViewCell: UITableViewCell {
|
|||
isReveal = true
|
||||
passwordDisplayButton?.setImage(#imageLiteral(resourceName: "Invisible"), for: .normal)
|
||||
}
|
||||
|
||||
|
||||
@objc func concealPassword(_ sender: Any?) {
|
||||
if type == .password {
|
||||
if cellData?.content.isEmpty == false {
|
||||
|
|
@ -128,7 +128,7 @@ class LabelTableViewCell: UITableViewCell {
|
|||
isReveal = false
|
||||
passwordDisplayButton?.setImage(#imageLiteral(resourceName: "Visible"), for: .normal)
|
||||
}
|
||||
|
||||
|
||||
@objc func reversePasswordDisplay(_ sender: Any?) {
|
||||
if isReveal {
|
||||
// conceal
|
||||
|
|
@ -143,21 +143,21 @@ class LabelTableViewCell: UITableViewCell {
|
|||
// if isURLCell, passwordTableView should not be nil
|
||||
delegatePasswordTableView!.openLink(to: cellData?.content)
|
||||
}
|
||||
|
||||
|
||||
@objc func getNextHOTP(_ sender: Any?) {
|
||||
// if isHOTPCell, passwordTableView should not be nil
|
||||
delegatePasswordTableView!.getNextHOTP()
|
||||
}
|
||||
|
||||
|
||||
private func updateButtons() {
|
||||
// total width and height of a button
|
||||
let height = min(self.bounds.height, 36.0)
|
||||
let width = max(height * 0.8, Globals.tableCellButtonSize)
|
||||
|
||||
|
||||
// margins (between button boundary and icon)
|
||||
let marginY = max((height - Globals.tableCellButtonSize) / 2, 0.0)
|
||||
let marginX = max((width - Globals.tableCellButtonSize) / 2, 0.0)
|
||||
|
||||
|
||||
switch type {
|
||||
case .password:
|
||||
if let content = cellData?.content, content != "" {
|
||||
|
|
@ -178,7 +178,7 @@ class LabelTableViewCell: UITableViewCell {
|
|||
nextButton.imageView?.contentMode = .scaleAspectFit
|
||||
nextButton.contentEdgeInsets = UIEdgeInsetsMake(marginY, marginX, marginY, marginX)
|
||||
nextButton.addTarget(self, action: #selector(getNextHOTP), for: UIControlEvents.touchUpInside)
|
||||
|
||||
|
||||
// password button
|
||||
passwordDisplayButton = UIButton(type: .system)
|
||||
passwordDisplayButton!.frame = CGRect(x: width, y: 0, width: width, height: height)
|
||||
|
|
@ -187,7 +187,7 @@ class LabelTableViewCell: UITableViewCell {
|
|||
passwordDisplayButton!.imageView?.contentMode = .scaleAspectFit
|
||||
passwordDisplayButton!.contentEdgeInsets = UIEdgeInsetsMake(marginY, marginX, marginY, marginX)
|
||||
passwordDisplayButton!.addTarget(self, action: #selector(reversePasswordDisplay), for: UIControlEvents.touchUpInside)
|
||||
|
||||
|
||||
buttons = UIView()
|
||||
buttons!.frame = CGRect(x: 0, y: 0, width: width * 2, height: height)
|
||||
buttons!.addSubview(nextButton)
|
||||
|
|
|
|||
|
|
@ -22,5 +22,5 @@ class PasswordDetailTitleTableViewCell: UITableViewCell {
|
|||
|
||||
// Configure the view for the selected state
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -18,15 +18,15 @@ class SliderTableViewCell: ContentTableViewCell {
|
|||
@IBOutlet weak var titleLabel: UILabel!
|
||||
@IBOutlet weak var valueLabel: UILabel!
|
||||
@IBOutlet weak var slider: UISlider!
|
||||
|
||||
|
||||
var delegate: UITableViewController?
|
||||
|
||||
|
||||
var roundedValue: Int {
|
||||
get {
|
||||
return Int(valueLabel.text!)!
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
override func awakeFromNib() {
|
||||
super.awakeFromNib()
|
||||
// Initialization code
|
||||
|
|
@ -37,7 +37,7 @@ class SliderTableViewCell: ContentTableViewCell {
|
|||
|
||||
// Configure the view for the selected state
|
||||
}
|
||||
|
||||
|
||||
@IBAction func handleSliderValueChange(_ sender: UISlider) {
|
||||
let oldRoundedValue = self.roundedValue
|
||||
let newRoundedValue = Int(sender.value)
|
||||
|
|
@ -51,14 +51,14 @@ class SliderTableViewCell: ContentTableViewCell {
|
|||
delegate.generateAndCopyPassword()
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
func reset(title: String, minimumValue: Int, maximumValue: Int, defaultValue: Int) {
|
||||
titleLabel.text = title
|
||||
slider.minimumValue = Float(minimumValue)
|
||||
slider.maximumValue = Float(maximumValue)
|
||||
slider.value = Float(defaultValue)
|
||||
valueLabel.text = String(defaultValue)
|
||||
|
||||
|
||||
// "not editable"
|
||||
if minimumValue == maximumValue {
|
||||
titleLabel.textColor = UIColor.gray
|
||||
|
|
@ -66,5 +66,5 @@ class SliderTableViewCell: ContentTableViewCell {
|
|||
slider.isUserInteractionEnabled = false
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -11,15 +11,15 @@ import UIKit
|
|||
class TextFieldTableViewCell: ContentTableViewCell {
|
||||
|
||||
@IBOutlet weak var contentTextField: UITextField!
|
||||
|
||||
|
||||
override func awakeFromNib() {
|
||||
super.awakeFromNib()
|
||||
}
|
||||
|
||||
|
||||
override func setSelected(_ selected: Bool, animated: Bool) {
|
||||
super.setSelected(selected, animated: animated)
|
||||
}
|
||||
|
||||
|
||||
override func getContent() -> String? {
|
||||
return contentTextField.text
|
||||
}
|
||||
|
|
|
|||
|
|
@ -19,11 +19,11 @@ class TextViewTableViewCell: ContentTableViewCell {
|
|||
override func setSelected(_ selected: Bool, animated: Bool) {
|
||||
super.setSelected(selected, animated: animated)
|
||||
}
|
||||
|
||||
|
||||
override func getContent() -> String? {
|
||||
return contentTextView.text
|
||||
}
|
||||
|
||||
|
||||
override func setContent(content: String?) {
|
||||
contentTextView.text = content
|
||||
}
|
||||
|
|
|
|||
|
|
@ -12,7 +12,7 @@ class TitleTextFieldTableViewCell: UITableViewCell {
|
|||
|
||||
@IBOutlet weak var titleLabel: UILabel!
|
||||
@IBOutlet weak var contentTextField: UITextField!
|
||||
|
||||
|
||||
override func awakeFromNib() {
|
||||
super.awakeFromNib()
|
||||
// Initialization code
|
||||
|
|
@ -20,7 +20,7 @@ class TitleTextFieldTableViewCell: UITableViewCell {
|
|||
let tapGestureRecognizer = UITapGestureRecognizer(target: self, action: #selector(tap(_:)))
|
||||
titleLabel.addGestureRecognizer(tapGestureRecognizer)
|
||||
}
|
||||
|
||||
|
||||
@objc func tap(_ sender: Any?) {
|
||||
contentTextField.becomeFirstResponder()
|
||||
}
|
||||
|
|
@ -28,5 +28,5 @@ class TitleTextFieldTableViewCell: UITableViewCell {
|
|||
override func setSelected(_ selected: Bool, animated: Bool) {
|
||||
super.setSelected(selected, animated: animated)
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -25,18 +25,18 @@ fileprivate class PasswordsTableEntry : NSObject {
|
|||
class CredentialProviderViewController: ASCredentialProviderViewController, UITableViewDataSource, UITableViewDelegate, UISearchBarDelegate {
|
||||
@IBOutlet weak var searchBar: UISearchBar!
|
||||
@IBOutlet weak var tableView: UITableView!
|
||||
|
||||
|
||||
private let passwordStore = PasswordStore.shared
|
||||
|
||||
|
||||
private var searchActive = false
|
||||
private var passwordsTableEntries: [PasswordsTableEntry] = []
|
||||
private var filteredPasswordsTableEntries: [PasswordsTableEntry] = []
|
||||
|
||||
|
||||
private lazy var passcodelock: PasscodeExtensionDisplay = {
|
||||
let passcodelock = PasscodeExtensionDisplay(extensionContext: self.extensionContext)
|
||||
return passcodelock
|
||||
}()
|
||||
|
||||
|
||||
/*
|
||||
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
|
||||
|
|
@ -50,14 +50,14 @@ class CredentialProviderViewController: ASCredentialProviderViewController, UITa
|
|||
searchBarSearchButtonClicked(searchBar)
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
// get the domain
|
||||
var identifier = serviceIdentifiers[0].identifier
|
||||
if !identifier.hasPrefix("http://") && !identifier.hasPrefix("https://") {
|
||||
identifier = "http://" + identifier
|
||||
}
|
||||
let url = URL(string: identifier)?.host ?? ""
|
||||
|
||||
|
||||
// "click" search
|
||||
searchBar.text = url
|
||||
searchBar.becomeFirstResponder()
|
||||
|
|
@ -102,20 +102,20 @@ class CredentialProviderViewController: ASCredentialProviderViewController, UITa
|
|||
super.viewWillAppear(animated)
|
||||
passcodelock.presentPasscodeLockIfNeeded(self)
|
||||
}
|
||||
|
||||
|
||||
override func viewDidLoad() {
|
||||
super.viewDidLoad()
|
||||
|
||||
|
||||
// prepare
|
||||
searchBar.delegate = self
|
||||
tableView.delegate = self
|
||||
tableView.dataSource = self
|
||||
tableView.register(UINib(nibName: "PasswordWithFolderTableViewCell", bundle: nil), forCellReuseIdentifier: "passwordWithFolderTableViewCell")
|
||||
|
||||
|
||||
// initialize table entries
|
||||
initPasswordsTableEntries()
|
||||
}
|
||||
|
||||
|
||||
private func initPasswordsTableEntries() {
|
||||
passwordsTableEntries.removeAll()
|
||||
filteredPasswordsTableEntries.removeAll()
|
||||
|
|
@ -125,7 +125,7 @@ class CredentialProviderViewController: ASCredentialProviderViewController, UITa
|
|||
PasswordsTableEntry($0)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// define cell contents, and set long press action
|
||||
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
|
||||
let cell = tableView.dequeueReusableCell(withIdentifier: "passwordTableViewCell", for: indexPath)
|
||||
|
|
@ -140,16 +140,16 @@ class CredentialProviderViewController: ASCredentialProviderViewController, UITa
|
|||
cell.detailTextLabel?.text = entry.categoryText
|
||||
return cell
|
||||
}
|
||||
|
||||
|
||||
// select row -> extension returns (with username and password)
|
||||
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
|
||||
let entry = getPasswordEntry(by: indexPath)
|
||||
|
||||
|
||||
guard self.passwordStore.privateKey != nil else {
|
||||
Utils.alert(title: "Cannot Copy Password", message: "PGP Key is not set. Please set your PGP Key first.", controller: self, completion: nil)
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
let passwordEntity = entry.passwordEntity!
|
||||
UIImpactFeedbackGenerator(style: .medium).impactOccurred()
|
||||
DispatchQueue.global(qos: .userInteractive).async {
|
||||
|
|
@ -171,18 +171,18 @@ class CredentialProviderViewController: ASCredentialProviderViewController, UITa
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
func numberOfSectionsInTableView(tableView: UITableView) -> Int {
|
||||
return 1
|
||||
}
|
||||
|
||||
|
||||
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
|
||||
if searchActive {
|
||||
return filteredPasswordsTableEntries.count
|
||||
}
|
||||
return passwordsTableEntries.count;
|
||||
}
|
||||
|
||||
|
||||
private func requestPGPKeyPassphrase() -> String {
|
||||
let sem = DispatchSemaphore(value: 0)
|
||||
var passphrase = ""
|
||||
|
|
@ -204,13 +204,13 @@ class CredentialProviderViewController: ASCredentialProviderViewController, UITa
|
|||
}
|
||||
return passphrase
|
||||
}
|
||||
|
||||
|
||||
func searchBarCancelButtonClicked(_ searchBar: UISearchBar) {
|
||||
searchBar.text = ""
|
||||
searchActive = false
|
||||
self.tableView.reloadData()
|
||||
}
|
||||
|
||||
|
||||
func searchBarSearchButtonClicked(_ searchBar: UISearchBar) {
|
||||
if let searchText = searchBar.text, searchText.isEmpty == false {
|
||||
filteredPasswordsTableEntries = passwordsTableEntries.filter { entry in
|
||||
|
|
@ -229,11 +229,11 @@ class CredentialProviderViewController: ASCredentialProviderViewController, UITa
|
|||
}
|
||||
self.tableView.reloadData()
|
||||
}
|
||||
|
||||
|
||||
func searchBar(_ searchBar: UISearchBar, textDidChange searchText: String) {
|
||||
searchBarSearchButtonClicked(searchBar)
|
||||
}
|
||||
|
||||
|
||||
private func getPasswordEntry(by indexPath: IndexPath) -> PasswordsTableEntry {
|
||||
if searchActive {
|
||||
return filteredPasswordsTableEntries[indexPath.row]
|
||||
|
|
|
|||
|
|
@ -31,7 +31,7 @@ class PasscodeExtensionDisplay {
|
|||
private var isPasscodePresented = false
|
||||
private let passcodeLockVC: PasscodeLockViewControllerForExtension
|
||||
private let extensionContext: ASCredentialProviderExtensionContext?
|
||||
|
||||
|
||||
public init(extensionContext: ASCredentialProviderExtensionContext?) {
|
||||
self.extensionContext = extensionContext
|
||||
passcodeLockVC = PasscodeLockViewControllerForExtension(extensionContext: extensionContext)
|
||||
|
|
@ -40,7 +40,7 @@ class PasscodeExtensionDisplay {
|
|||
}
|
||||
passcodeLockVC.setCancellable(true)
|
||||
}
|
||||
|
||||
|
||||
// present the passcode lock view if passcode is set and the view controller is not presented
|
||||
public func presentPasscodeLockIfNeeded(_ extensionVC: UIViewController) {
|
||||
guard PasscodeLock.shared.hasPasscode && !isPasscodePresented == true else {
|
||||
|
|
@ -49,7 +49,7 @@ class PasscodeExtensionDisplay {
|
|||
isPasscodePresented = true
|
||||
extensionVC.present(passcodeLockVC, animated: true, completion: nil)
|
||||
}
|
||||
|
||||
|
||||
public func dismiss(animated: Bool = true) {
|
||||
isPasscodePresented = false
|
||||
}
|
||||
|
|
|
|||
|
|
@ -26,24 +26,24 @@ fileprivate class PasswordsTableEntry : NSObject {
|
|||
class ExtensionViewController: UIViewController, UITableViewDataSource, UITableViewDelegate, UISearchBarDelegate, UINavigationBarDelegate {
|
||||
@IBOutlet weak var searchBar: UISearchBar!
|
||||
@IBOutlet weak var tableView: UITableView!
|
||||
|
||||
|
||||
private let passwordStore = PasswordStore.shared
|
||||
|
||||
|
||||
private var searchActive = false
|
||||
private var passwordsTableEntries: [PasswordsTableEntry] = []
|
||||
private var filteredPasswordsTableEntries: [PasswordsTableEntry] = []
|
||||
|
||||
|
||||
enum Action {
|
||||
case findLogin, fillBrowser, unknown
|
||||
}
|
||||
|
||||
|
||||
private var extensionAction = Action.unknown
|
||||
|
||||
|
||||
private lazy var passcodelock: PasscodeExtensionDisplay = {
|
||||
let passcodelock = PasscodeExtensionDisplay(extensionContext: self.extensionContext)
|
||||
return passcodelock
|
||||
}()
|
||||
|
||||
|
||||
private func initPasswordsTableEntries() {
|
||||
passwordsTableEntries.removeAll()
|
||||
filteredPasswordsTableEntries.removeAll()
|
||||
|
|
@ -53,12 +53,12 @@ class ExtensionViewController: UIViewController, UITableViewDataSource, UITableV
|
|||
PasswordsTableEntry($0)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
override func viewWillAppear(_ animated: Bool) {
|
||||
super.viewWillAppear(animated)
|
||||
passcodelock.presentPasscodeLockIfNeeded(self)
|
||||
}
|
||||
|
||||
|
||||
override func viewDidLoad() {
|
||||
super.viewDidLoad()
|
||||
// prepare
|
||||
|
|
@ -66,15 +66,15 @@ class ExtensionViewController: UIViewController, UITableViewDataSource, UITableV
|
|||
tableView.delegate = self
|
||||
tableView.dataSource = self
|
||||
tableView.register(UINib(nibName: "PasswordWithFolderTableViewCell", bundle: nil), forCellReuseIdentifier: "passwordWithFolderTableViewCell")
|
||||
|
||||
|
||||
// initialize table entries
|
||||
initPasswordsTableEntries()
|
||||
|
||||
|
||||
// get the provider
|
||||
guard let extensionItems = extensionContext?.inputItems as? [NSExtensionItem] else {
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
for extensionItem in extensionItems {
|
||||
if let itemProviders = extensionItem.attachments as? [NSItemProvider] {
|
||||
for provider in itemProviders {
|
||||
|
|
@ -133,7 +133,7 @@ class ExtensionViewController: UIViewController, UITableViewDataSource, UITableV
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// define cell contents, and set long press action
|
||||
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
|
||||
let cell = tableView.dequeueReusableCell(withIdentifier: "passwordTableViewCell", for: indexPath)
|
||||
|
|
@ -148,16 +148,16 @@ class ExtensionViewController: UIViewController, UITableViewDataSource, UITableV
|
|||
cell.detailTextLabel?.text = entry.categoryText
|
||||
return cell
|
||||
}
|
||||
|
||||
|
||||
// select row -> extension returns (with username and password)
|
||||
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
|
||||
let entry = getPasswordEntry(by: indexPath)
|
||||
|
||||
|
||||
guard self.passwordStore.privateKey != nil else {
|
||||
Utils.alert(title: "Cannot Copy Password", message: "PGP Key is not set. Please set your PGP Key first.", controller: self, completion: nil)
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
let passwordEntity = entry.passwordEntity!
|
||||
UIImpactFeedbackGenerator(style: .medium).impactOccurred()
|
||||
DispatchQueue.global(qos: .userInteractive).async {
|
||||
|
|
@ -197,19 +197,19 @@ class ExtensionViewController: UIViewController, UITableViewDataSource, UITableV
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
func numberOfSectionsInTableView(tableView: UITableView) -> Int {
|
||||
return 1
|
||||
}
|
||||
|
||||
|
||||
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
|
||||
if searchActive{
|
||||
return filteredPasswordsTableEntries.count
|
||||
}
|
||||
return passwordsTableEntries.count;
|
||||
}
|
||||
|
||||
|
||||
private func requestPGPKeyPassphrase() -> String {
|
||||
let sem = DispatchSemaphore(value: 0)
|
||||
var passphrase = ""
|
||||
|
|
@ -231,17 +231,17 @@ class ExtensionViewController: UIViewController, UITableViewDataSource, UITableV
|
|||
}
|
||||
return passphrase
|
||||
}
|
||||
|
||||
|
||||
@IBAction func cancelExtension(_ sender: Any) {
|
||||
extensionContext!.completeRequest(returningItems: [], completionHandler: nil)
|
||||
}
|
||||
|
||||
|
||||
func searchBarCancelButtonClicked(_ searchBar: UISearchBar) {
|
||||
searchBar.text = ""
|
||||
searchActive = false
|
||||
self.tableView.reloadData()
|
||||
}
|
||||
|
||||
|
||||
func searchBarSearchButtonClicked(_ searchBar: UISearchBar) {
|
||||
if let searchText = searchBar.text, searchText.isEmpty == false {
|
||||
filteredPasswordsTableEntries = passwordsTableEntries.filter { entry in
|
||||
|
|
@ -260,11 +260,11 @@ class ExtensionViewController: UIViewController, UITableViewDataSource, UITableV
|
|||
}
|
||||
self.tableView.reloadData()
|
||||
}
|
||||
|
||||
|
||||
func searchBar(_ searchBar: UISearchBar, textDidChange searchText: String) {
|
||||
searchBarSearchButtonClicked(searchBar)
|
||||
}
|
||||
|
||||
|
||||
private func getPasswordEntry(by indexPath: IndexPath) -> PasswordsTableEntry {
|
||||
if searchActive {
|
||||
return filteredPasswordsTableEntries[indexPath.row]
|
||||
|
|
|
|||
|
|
@ -28,7 +28,7 @@ class OnePasswordExtensionKey {
|
|||
static let returnedFieldsKey = "returned_fields"
|
||||
static let oldPasswordKey = "old_password"
|
||||
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
|
||||
static let generatedPasswordMinLengthKey = "password_min_length"
|
||||
static let generatedPasswordMaxLengthKey = "password_max_length"
|
||||
|
|
|
|||
|
|
@ -30,7 +30,7 @@ class PasscodeExtensionDisplay {
|
|||
private var isPasscodePresented = false
|
||||
private let passcodeLockVC: PasscodeLockViewControllerForExtension
|
||||
private let extensionContext: NSExtensionContext?
|
||||
|
||||
|
||||
public init(extensionContext: NSExtensionContext?) {
|
||||
self.extensionContext = extensionContext
|
||||
passcodeLockVC = PasscodeLockViewControllerForExtension(extensionContext: extensionContext)
|
||||
|
|
@ -39,7 +39,7 @@ class PasscodeExtensionDisplay {
|
|||
}
|
||||
passcodeLockVC.setCancellable(true)
|
||||
}
|
||||
|
||||
|
||||
// present the passcode lock view if passcode is set and the view controller is not presented
|
||||
public func presentPasscodeLockIfNeeded(_ extensionVC: UIViewController) {
|
||||
guard PasscodeLock.shared.hasPasscode && !isPasscodePresented == true else {
|
||||
|
|
@ -48,7 +48,7 @@ class PasscodeExtensionDisplay {
|
|||
isPasscodePresented = true
|
||||
extensionVC.present(passcodeLockVC, animated: true, completion: nil)
|
||||
}
|
||||
|
||||
|
||||
public func dismiss(animated: Bool = true) {
|
||||
isPasscodePresented = false
|
||||
}
|
||||
|
|
|
|||
|
|
@ -12,7 +12,7 @@ run: function(arguments) {
|
|||
arguments.completionFunction({"url_string": url, "error": error});
|
||||
}
|
||||
},
|
||||
|
||||
|
||||
finalize: function(arguments) {
|
||||
if (arguments["password"]) {
|
||||
var passwordElement = document.querySelector("input[type=password]")
|
||||
|
|
@ -21,7 +21,7 @@ finalize: function(arguments) {
|
|||
passwordElement.value = arguments["password"]
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if (arguments["username"]) {
|
||||
var usernameElement = document.querySelector("input[type=email], input[type=text]")
|
||||
if (usernameElement) {
|
||||
|
|
|
|||
|
|
@ -11,7 +11,7 @@
|
|||
import UIKit
|
||||
|
||||
open class PasscodeLockPresenter {
|
||||
|
||||
|
||||
fileprivate var mainWindow: UIWindow?
|
||||
fileprivate var passcodeLockWindow: UIWindow?
|
||||
|
||||
|
|
@ -21,16 +21,16 @@ open class PasscodeLockPresenter {
|
|||
|
||||
open func present(windowLevel: CGFloat?) {
|
||||
guard PasscodeLock.shared.hasPasscode else { return }
|
||||
|
||||
|
||||
// dismiss the original window
|
||||
dismiss()
|
||||
|
||||
|
||||
// new window
|
||||
mainWindow?.endEditing(true)
|
||||
passcodeLockWindow = UIWindow(frame: self.mainWindow!.frame)
|
||||
moveWindowsToFront(windowLevel: windowLevel)
|
||||
passcodeLockWindow?.isHidden = false
|
||||
|
||||
|
||||
// new vc
|
||||
let passcodeLockVC = PasscodeLockViewController()
|
||||
let userDismissCompletionCallback = passcodeLockVC.dismissCompletionCallback
|
||||
|
|
|
|||
|
|
@ -12,23 +12,23 @@ import UIKit
|
|||
import LocalAuthentication
|
||||
|
||||
open class PasscodeLockViewController: UIViewController, UITextFieldDelegate {
|
||||
|
||||
|
||||
open var dismissCompletionCallback: (()->Void)?
|
||||
open var successCallback: (()->Void)?
|
||||
open var cancelCallback: (()->Void)?
|
||||
|
||||
|
||||
weak var passcodeLabel: UILabel?
|
||||
weak var passcodeWrongAttemptsLabel: UILabel?
|
||||
weak var passcodeTextField: UITextField?
|
||||
weak var biometryAuthButton: UIButton?
|
||||
open weak var cancelButton: UIButton?
|
||||
|
||||
|
||||
var passcodeFailedAttempts = 0
|
||||
var isCancellable: Bool = false
|
||||
|
||||
|
||||
open override func loadView() {
|
||||
super.loadView()
|
||||
|
||||
|
||||
let passcodeLabel = UILabel(frame: CGRect(x: 0, y: 0, width: 300, height: 40))
|
||||
passcodeLabel.text = "Enter passcode for Pass"
|
||||
passcodeLabel.font = UIFont.boldSystemFont(ofSize: 18)
|
||||
|
|
@ -37,7 +37,7 @@ open class PasscodeLockViewController: UIViewController, UITextFieldDelegate {
|
|||
passcodeLabel.translatesAutoresizingMaskIntoConstraints = false
|
||||
self.view.addSubview(passcodeLabel)
|
||||
self.passcodeLabel = passcodeLabel
|
||||
|
||||
|
||||
let passcodeWrongAttemptsLabel = UILabel(frame: CGRect(x: 0, y: 0, width: 300, height: 40))
|
||||
passcodeWrongAttemptsLabel.text = ""
|
||||
passcodeWrongAttemptsLabel.textColor = UIColor.red
|
||||
|
|
@ -45,7 +45,7 @@ open class PasscodeLockViewController: UIViewController, UITextFieldDelegate {
|
|||
passcodeWrongAttemptsLabel.translatesAutoresizingMaskIntoConstraints = false
|
||||
self.view.addSubview(passcodeWrongAttemptsLabel)
|
||||
self.passcodeWrongAttemptsLabel = passcodeWrongAttemptsLabel
|
||||
|
||||
|
||||
let passcodeTextField = UITextField(frame: CGRect(x: 0, y: 0, width: 300, height: 40))
|
||||
passcodeTextField.borderStyle = UITextBorderStyle.roundedRect
|
||||
passcodeTextField.placeholder = "passcode"
|
||||
|
|
@ -57,7 +57,7 @@ open class PasscodeLockViewController: UIViewController, UITextFieldDelegate {
|
|||
passcodeTextField.translatesAutoresizingMaskIntoConstraints = false
|
||||
self.view.addSubview(passcodeTextField)
|
||||
self.passcodeTextField = passcodeTextField
|
||||
|
||||
|
||||
let biometryAuthButton = UIButton(type: .custom)
|
||||
biometryAuthButton.setTitle("", for: .normal)
|
||||
biometryAuthButton.setTitleColor(Globals.blue, for: .normal)
|
||||
|
|
@ -66,7 +66,7 @@ open class PasscodeLockViewController: UIViewController, UITextFieldDelegate {
|
|||
biometryAuthButton.translatesAutoresizingMaskIntoConstraints = false
|
||||
self.view.addSubview(biometryAuthButton)
|
||||
self.biometryAuthButton = biometryAuthButton
|
||||
|
||||
|
||||
let myContext = LAContext()
|
||||
var authError: NSError?
|
||||
if #available(iOS 8.0, macOS 10.12.1, *) {
|
||||
|
|
@ -81,7 +81,7 @@ open class PasscodeLockViewController: UIViewController, UITextFieldDelegate {
|
|||
biometryAuthButton.isHidden = false
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
let cancelButton = UIButton(type: .custom)
|
||||
cancelButton.setTitle("Cancel", for: .normal)
|
||||
cancelButton.setTitleColor(Globals.blue, for: .normal)
|
||||
|
|
@ -91,7 +91,7 @@ open class PasscodeLockViewController: UIViewController, UITextFieldDelegate {
|
|||
cancelButton.contentHorizontalAlignment = UIControlContentHorizontalAlignment.left
|
||||
self.view.addSubview(cancelButton)
|
||||
self.cancelButton = cancelButton
|
||||
|
||||
|
||||
NSLayoutConstraint.activate([
|
||||
passcodeTextField.widthAnchor.constraint(equalToConstant: 300),
|
||||
passcodeTextField.heightAnchor.constraint(equalToConstant: 40),
|
||||
|
|
@ -118,13 +118,13 @@ open class PasscodeLockViewController: UIViewController, UITextFieldDelegate {
|
|||
cancelButton.topAnchor.constraint(equalTo: self.view.safeTopAnchor),
|
||||
cancelButton.leftAnchor.constraint(equalTo: self.view.safeLeftAnchor, constant: 20)
|
||||
])
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
open override func viewDidLoad() {
|
||||
super.viewDidLoad()
|
||||
}
|
||||
|
||||
|
||||
open override func viewDidAppear(_ animated: Bool) {
|
||||
super.viewDidAppear(animated)
|
||||
if let biometryAuthButton = biometryAuthButton {
|
||||
|
|
@ -137,7 +137,7 @@ open class PasscodeLockViewController: UIViewController, UITextFieldDelegate {
|
|||
DispatchQueue.main.async {
|
||||
self.passcodeTextField?.text = ""
|
||||
}
|
||||
|
||||
|
||||
// pop
|
||||
if presentingViewController?.presentedViewController == self {
|
||||
// if presented as modal
|
||||
|
|
@ -160,16 +160,16 @@ open class PasscodeLockViewController: UIViewController, UITextFieldDelegate {
|
|||
passcodeWrongAttemptsLabel?.text = ""
|
||||
dismissPasscodeLock(completionHandler: successCallback)
|
||||
}
|
||||
|
||||
|
||||
@objc func passcodeLockDidCancel() {
|
||||
dismissPasscodeLock(completionHandler: cancelCallback)
|
||||
}
|
||||
|
||||
|
||||
@objc func bioButtonPressedAction(_ uiButton: UIButton) {
|
||||
let myContext = LAContext()
|
||||
let myLocalizedReasonString = "Authentication is needed to access Pass."
|
||||
var authError: NSError?
|
||||
|
||||
|
||||
if #available(iOS 8.0, *) {
|
||||
if myContext.canEvaluatePolicy(.deviceOwnerAuthenticationWithBiometrics, error: &authError) {
|
||||
myContext.evaluatePolicy(.deviceOwnerAuthenticationWithBiometrics, localizedReason: myLocalizedReasonString) { success, evaluateError in
|
||||
|
|
@ -183,7 +183,7 @@ open class PasscodeLockViewController: UIViewController, UITextFieldDelegate {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public override func textFieldShouldReturn(_ textField: UITextField) -> Bool {
|
||||
if textField == passcodeTextField {
|
||||
if !PasscodeLock.shared.check(passcode: textField.text ?? "") {
|
||||
|
|
@ -198,13 +198,13 @@ open class PasscodeLockViewController: UIViewController, UITextFieldDelegate {
|
|||
textField.resignFirstResponder()
|
||||
return true
|
||||
}
|
||||
|
||||
|
||||
@objc func passcodeTextFieldDidChange(_ textField: UITextField) {
|
||||
if PasscodeLock.shared.check(passcode: textField.text ?? "") {
|
||||
self.passcodeLockDidSucceed()
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public func setCancellable(_ isCancellable: Bool) {
|
||||
self.isCancellable = isCancellable
|
||||
cancelButton?.isHidden = !isCancellable
|
||||
|
|
|
|||
|
|
@ -15,10 +15,10 @@ public extension DefaultsKeys {
|
|||
static let pgpKeySource = DefaultsKey<String?>("pgpKeySource")
|
||||
static let pgpPublicKeyURL = DefaultsKey<URL?>("pgpPublicKeyURL")
|
||||
static let pgpPrivateKeyURL = DefaultsKey<URL?>("pgpPrivateKeyURL")
|
||||
|
||||
|
||||
static let pgpPublicKeyArmor = DefaultsKey<String?>("pgpPublicKeyArmor")
|
||||
static let pgpPrivateKeyArmor = DefaultsKey<String?>("pgpPrivateKeyArmor")
|
||||
|
||||
|
||||
static let gitURL = DefaultsKey<URL?>("gitURL")
|
||||
static let gitAuthenticationMethod = DefaultsKey<String?>("gitAuthenticationMethod")
|
||||
static let gitUsername = DefaultsKey<String?>("gitUsername")
|
||||
|
|
@ -29,10 +29,10 @@ public extension DefaultsKeys {
|
|||
static let gitSignatureEmail = DefaultsKey<String?>("gitSignatureEmail")
|
||||
|
||||
static let lastSyncedTime = DefaultsKey<Date?>("lastSyncedTime")
|
||||
|
||||
|
||||
static let isTouchIDOn = DefaultsKey<Bool>("isTouchIDOn")
|
||||
static let passcodeKey = DefaultsKey<String?>("passcodeKey")
|
||||
|
||||
|
||||
static let isHideUnknownOn = DefaultsKey<Bool>("isHideUnknownOn")
|
||||
static let isHideOTPOn = DefaultsKey<Bool>("isHideOTPOn")
|
||||
static let isRememberPGPPassphraseOn = DefaultsKey<Bool>("isRememberPGPPassphraseOn")
|
||||
|
|
@ -40,6 +40,6 @@ public extension DefaultsKeys {
|
|||
static let isShowFolderOn = DefaultsKey<Bool>("isShowFolderOn")
|
||||
static let isSearchDefaultAll = DefaultsKey<Bool>("isSearchDefaultAll")
|
||||
static let passwordGeneratorFlavor = DefaultsKey<String>("passwordGeneratorFlavor")
|
||||
|
||||
|
||||
static let encryptInArmored = DefaultsKey<Bool>("encryptInArmored")
|
||||
}
|
||||
|
|
|
|||
|
|
@ -10,7 +10,7 @@ import Foundation
|
|||
|
||||
// https://gist.github.com/NikolaiRuhe/eeb135d20c84a7097516
|
||||
public extension FileManager {
|
||||
|
||||
|
||||
/// 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,
|
||||
|
|
@ -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
|
||||
/// directories, hard links, ...).
|
||||
func allocatedSizeOfDirectoryAtURL(directoryURL : URL) throws -> UInt64 {
|
||||
|
||||
|
||||
// We'll sum up content size here:
|
||||
var accumulatedSize = UInt64(0)
|
||||
|
||||
|
||||
// prefetching some properties during traversal will speed up things a bit.
|
||||
let prefetchedProperties = [
|
||||
URLResourceKey.isRegularFileKey,
|
||||
URLResourceKey.fileAllocatedSizeKey,
|
||||
URLResourceKey.totalFileAllocatedSizeKey,
|
||||
]
|
||||
|
||||
|
||||
// The error handler simply signals errors to outside code.
|
||||
var errorDidOccur: Error?
|
||||
let errorHandler: (URL, Error) -> Bool = { _, error in
|
||||
errorDidOccur = error
|
||||
return false
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
// We have to enumerate all directory contents, including subdirectories.
|
||||
let enumerator = self.enumerator(at: directoryURL,
|
||||
includingPropertiesForKeys: prefetchedProperties,
|
||||
options: FileManager.DirectoryEnumerationOptions(),
|
||||
errorHandler: errorHandler)
|
||||
precondition(enumerator != nil)
|
||||
|
||||
|
||||
// Start the traversal:
|
||||
for item in enumerator! {
|
||||
let contentItemURL = item as! NSURL
|
||||
|
||||
|
||||
// Bail out on errors from the errorHandler.
|
||||
if let error = errorDidOccur { throw error }
|
||||
|
||||
|
||||
let resourceValueForKey: (URLResourceKey) throws -> NSNumber? = { key in
|
||||
var value: AnyObject?
|
||||
try contentItemURL.getResourceValue(&value, forKey: key)
|
||||
return value as? NSNumber
|
||||
}
|
||||
|
||||
|
||||
// Get the type of this item, making sure we only sum up sizes of regular files.
|
||||
guard let isRegularFile = try resourceValueForKey(URLResourceKey.isRegularFileKey) else {
|
||||
preconditionFailure()
|
||||
}
|
||||
|
||||
|
||||
guard isRegularFile.boolValue else {
|
||||
continue
|
||||
}
|
||||
|
||||
|
||||
// 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.
|
||||
var fileSize = try resourceValueForKey(URLResourceKey.totalFileAllocatedSizeKey)
|
||||
|
||||
|
||||
// In case the value is unavailable we use the fallback value (excluding meta data and compression)
|
||||
// This value should always be available.
|
||||
fileSize = try fileSize ?? resourceValueForKey(URLResourceKey.fileAllocatedSizeKey)
|
||||
|
||||
|
||||
guard let size = fileSize else {
|
||||
preconditionFailure("huh? NSURLFileAllocatedSizeKey should always return a value")
|
||||
}
|
||||
|
||||
|
||||
// We're good, add up the value.
|
||||
accumulatedSize += size.uint64Value
|
||||
}
|
||||
|
||||
|
||||
// Bail out on errors from the errorHandler.
|
||||
if let error = errorDidOccur { throw error }
|
||||
|
||||
|
||||
// We finally got it.
|
||||
return accumulatedSize
|
||||
}
|
||||
|
|
|
|||
|
|
@ -10,7 +10,7 @@ import Foundation
|
|||
import UIKit
|
||||
|
||||
public class Globals {
|
||||
|
||||
|
||||
// Legacy paths (not shared)
|
||||
public static let documentPathLegacy = NSSearchPathForDirectoriesInDomains(.documentDirectory, .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 gitSSHPrivateKeyURLLegacy = URL(fileURLWithPath: gitSSHPrivateKeyPathLegacy)
|
||||
public static let repositoryPathLegacy = "\(libraryPathLegacy)/password-store"
|
||||
|
||||
|
||||
public static let bundleIdentifier = "me.mssun.passforios"
|
||||
public static let groupIdentifier = "group." + bundleIdentifier
|
||||
public static let passKitBundleIdentifier = bundleIdentifier + ".passKit"
|
||||
|
||||
|
||||
public static let sharedContainerURL = FileManager.default.containerURL(forSecurityApplicationGroupIdentifier: groupIdentifier)!
|
||||
public static let documentPath = sharedContainerURL.appendingPathComponent("Documents").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 repositoryPath = libraryPath + "/password-store"
|
||||
public static let dbPath = documentPath + "/pass.sqlite"
|
||||
|
||||
|
||||
public static let iTunesFileSharingPath = NSSearchPathForDirectoriesInDomains(.documentDirectory, .userDomainMask, true)[0]
|
||||
public static let iTunesFileSharingPGPPublic = iTunesFileSharingPath + "/gpg_key.pub"
|
||||
public static let iTunesFileSharingPGPPrivate = iTunesFileSharingPath + "/gpg_key"
|
||||
public static let iTunesFileSharingSSHPrivate = iTunesFileSharingPath + "/ssh_key"
|
||||
|
||||
|
||||
public static let gitSignatureDefaultName = "Pass for iOS"
|
||||
public static let gitSignatureDefaultEmail = "user@passforios"
|
||||
|
||||
|
||||
public static let passwordDots = "••••••••••••"
|
||||
public static let oneTimePasswordDots = "••••••"
|
||||
public static let passwordFont = UIFont(name: "Courier-Bold", size: UIFont.labelFontSize - 1)
|
||||
|
||||
|
||||
// UI related
|
||||
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)
|
||||
|
|
@ -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 digitColor = UIColor(red:66/255.0, green:113/255.0, blue:174/255.0, alpha:1.0)
|
||||
public static let tableCellButtonSize = CGFloat(20.0)
|
||||
|
||||
|
||||
private init() { }
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -13,7 +13,7 @@ public extension Notification.Name {
|
|||
static let passwordStoreErased = Notification.Name("passwordStoreErased")
|
||||
static let passwordStoreChangeDiscarded = Notification.Name("passwordStoreChangeDiscarded")
|
||||
static let passwordSearch = Notification.Name("passwordSearch")
|
||||
|
||||
|
||||
static let passwordDisplaySettingChanged = Notification.Name("passwordDisplaySettingChanged")
|
||||
static let passwordDetailDisplaySettingChanged = Notification.Name("passwordDetailDisplaySettingChanged")
|
||||
}
|
||||
|
|
|
|||
|
|
@ -9,7 +9,7 @@
|
|||
import Foundation
|
||||
|
||||
extension UIView {
|
||||
|
||||
|
||||
// Save anchors: https://stackoverflow.com/questions/46317061/use-safe-area-layout-programmatically
|
||||
var safeTopAnchor: NSLayoutYAxisAnchor {
|
||||
if #available(iOS 11.0, *) {
|
||||
|
|
@ -18,7 +18,7 @@ extension UIView {
|
|||
return self.topAnchor
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
var safeLeftAnchor: NSLayoutXAxisAnchor {
|
||||
if #available(iOS 11.0, *){
|
||||
return self.safeAreaLayoutGuide.leftAnchor
|
||||
|
|
@ -26,7 +26,7 @@ extension UIView {
|
|||
return self.leftAnchor
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
var safeRightAnchor: NSLayoutXAxisAnchor {
|
||||
if #available(iOS 11.0, *){
|
||||
return self.safeAreaLayoutGuide.rightAnchor
|
||||
|
|
@ -34,7 +34,7 @@ extension UIView {
|
|||
return self.rightAnchor
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
var safeBottomAnchor: NSLayoutYAxisAnchor {
|
||||
if #available(iOS 11.0, *) {
|
||||
return self.safeAreaLayoutGuide.bottomAnchor
|
||||
|
|
|
|||
|
|
@ -15,17 +15,17 @@ public class Utils {
|
|||
let keychain = Keychain(service: Globals.bundleIdentifier, accessGroup: Globals.groupIdentifier)
|
||||
return (try? keychain.getString(name)) ?? nil
|
||||
}
|
||||
|
||||
|
||||
public static func addPasswordToKeychain(name: String, password: String?) {
|
||||
let keychain = Keychain(service: Globals.bundleIdentifier, accessGroup: Globals.groupIdentifier)
|
||||
keychain[name] = password
|
||||
}
|
||||
|
||||
|
||||
public static func removeKeychain(name: String) {
|
||||
let keychain = Keychain(service: Globals.bundleIdentifier, accessGroup: Globals.groupIdentifier)
|
||||
try? keychain.remove(name)
|
||||
}
|
||||
|
||||
|
||||
public static func removeAllKeychain() {
|
||||
let keychain = Keychain(service: Globals.bundleIdentifier, accessGroup: Globals.groupIdentifier)
|
||||
try? keychain.removeAll()
|
||||
|
|
@ -53,7 +53,7 @@ public class Utils {
|
|||
}
|
||||
return attributedPassword
|
||||
}
|
||||
|
||||
|
||||
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)
|
||||
alert.addAction(UIAlertAction(title: "OK", style: UIAlertActionStyle.default, handler: handler))
|
||||
|
|
|
|||
|
|
@ -14,21 +14,21 @@ import ObjectiveGit
|
|||
public struct GitCredential {
|
||||
private var credential: Credential
|
||||
private let passwordStore = PasswordStore.shared
|
||||
|
||||
|
||||
public enum Credential {
|
||||
case http(userName: String)
|
||||
case ssh(userName: String, privateKeyFile: URL)
|
||||
}
|
||||
|
||||
|
||||
public init(credential: Credential) {
|
||||
self.credential = credential
|
||||
}
|
||||
|
||||
|
||||
public func credentialProvider(requestGitPassword: @escaping (Credential, String?) -> String?) throws -> GTCredentialProvider {
|
||||
var attempts = 0
|
||||
return GTCredentialProvider { (_, _, _) -> (GTCredential?) in
|
||||
var credential: GTCredential? = nil
|
||||
|
||||
|
||||
switch self.credential {
|
||||
case let .http(userName):
|
||||
var lastPassword = self.passwordStore.gitPassword
|
||||
|
|
@ -63,7 +63,7 @@ public struct GitCredential {
|
|||
return credential
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public func delete() {
|
||||
switch credential {
|
||||
case .http:
|
||||
|
|
|
|||
|
|
@ -11,24 +11,24 @@ import LocalAuthentication
|
|||
|
||||
open class PasscodeLock {
|
||||
public static let shared = PasscodeLock()
|
||||
|
||||
|
||||
fileprivate let passcodeKey = "passcode.lock.passcode"
|
||||
fileprivate var passcode: String? {
|
||||
return SharedDefaults[.passcodeKey]
|
||||
}
|
||||
|
||||
|
||||
public var hasPasscode: Bool {
|
||||
return passcode != nil
|
||||
}
|
||||
|
||||
|
||||
public func save(passcode: String) {
|
||||
SharedDefaults[.passcodeKey] = passcode
|
||||
}
|
||||
|
||||
|
||||
public func check(passcode: String) -> Bool {
|
||||
return self.passcode == passcode
|
||||
}
|
||||
|
||||
|
||||
public func delete() {
|
||||
SharedDefaults[.passcodeKey] = nil
|
||||
}
|
||||
|
|
|
|||
|
|
@ -10,7 +10,7 @@ import OneTimePassword
|
|||
import Base32
|
||||
|
||||
public class Password {
|
||||
|
||||
|
||||
public var name: String
|
||||
public var url: URL
|
||||
public var plainText: String
|
||||
|
|
@ -50,7 +50,7 @@ public class Password {
|
|||
public var login: String? {
|
||||
return getAdditionValue(withKey: Constants.LOGIN_KEYWORD)
|
||||
}
|
||||
|
||||
|
||||
public var urlString: String? {
|
||||
return getAdditionValue(withKey: Constants.URL_KEYWORD)
|
||||
}
|
||||
|
|
@ -73,7 +73,7 @@ public class Password {
|
|||
self.plainText = plainText
|
||||
initEverything()
|
||||
}
|
||||
|
||||
|
||||
|
||||
public func updatePassword(name: String, url: URL, plainText: String) {
|
||||
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() }
|
||||
return additions.first(where: { toLowercase($0.title) == toLowercase(key) })?.content
|
||||
}
|
||||
|
||||
|
||||
/// Set the OTP token if we are able to construct a valid one.
|
||||
///
|
||||
/// Example of TOTP otpauth:
|
||||
|
|
@ -164,7 +164,7 @@ public class Password {
|
|||
.usingCounter(getAdditionValue(withKey: Constants.OTP_COUNTER))
|
||||
.build()
|
||||
}
|
||||
|
||||
|
||||
/// Get the OTP description and the current password.
|
||||
public func getOtpStrings() -> (description: String, otp: String)? {
|
||||
guard otpToken != nil else {
|
||||
|
|
@ -178,18 +178,18 @@ public class Password {
|
|||
}
|
||||
return (description, otpToken!.currentPassword ?? "error")
|
||||
}
|
||||
|
||||
|
||||
// return the password strings
|
||||
// it is guaranteed that it is a HOTP password when we call this
|
||||
public func getNextHotp() -> String? {
|
||||
// increase the counter
|
||||
otpToken = otpToken?.updatedToken()
|
||||
|
||||
|
||||
// replace old HOTP settings with the new otpauth
|
||||
var newOtpauth = try! otpToken?.toURL().absoluteString
|
||||
newOtpauth?.append("&secret=")
|
||||
newOtpauth?.append(MF_Base32Codec.base32String(from: otpToken?.generator.secret))
|
||||
|
||||
|
||||
var lines : [String] = []
|
||||
self.plainText.enumerateLines() { line, _ in
|
||||
let (key, _) = Parser.getKeyValuePair(from: line)
|
||||
|
|
@ -205,7 +205,7 @@ public class Password {
|
|||
lines.append(newOtpauth!)
|
||||
}
|
||||
self.updatePassword(name: self.name, url: self.url, plainText: lines.joined(separator: "\n"))
|
||||
|
||||
|
||||
// get and return the password
|
||||
return self.otpToken?.currentPassword
|
||||
}
|
||||
|
|
|
|||
|
|
@ -10,7 +10,7 @@ import Foundation
|
|||
import SwiftyUserDefaults
|
||||
|
||||
extension PasswordEntity {
|
||||
|
||||
|
||||
public var nameWithCategory: String {
|
||||
get {
|
||||
if let p = path, p.hasSuffix(".gpg") {
|
||||
|
|
@ -20,11 +20,11 @@ extension PasswordEntity {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public func getCategoryText() -> String {
|
||||
return getCategoryArray().joined(separator: " > ")
|
||||
}
|
||||
|
||||
|
||||
public func getCategoryArray() -> [String] {
|
||||
var parentEntity = parent
|
||||
var passwordCategoryArray: [String] = []
|
||||
|
|
@ -35,7 +35,7 @@ extension PasswordEntity {
|
|||
passwordCategoryArray.reverse()
|
||||
return passwordCategoryArray
|
||||
}
|
||||
|
||||
|
||||
public func getURL() -> URL? {
|
||||
if let p = getPath().stringByAddingPercentEncodingForRFC3986() {
|
||||
return URL(string: p)
|
||||
|
|
|
|||
|
|
@ -18,7 +18,7 @@ public class PasswordStore {
|
|||
public static let shared = PasswordStore()
|
||||
public let storeURL = URL(fileURLWithPath: "\(Globals.repositoryPath)")
|
||||
public let tempStoreURL = URL(fileURLWithPath: "\(Globals.repositoryPath)-temp")
|
||||
|
||||
|
||||
public var storeRepository: GTRepository?
|
||||
public var pgpKeyID: String?
|
||||
public var publicKey: Key? {
|
||||
|
|
@ -31,7 +31,7 @@ public class PasswordStore {
|
|||
}
|
||||
}
|
||||
public var privateKey: Key?
|
||||
|
||||
|
||||
public var gitSignatureForNow: GTSignature {
|
||||
get {
|
||||
let gitSignatureName = SharedDefaults[.gitSignatureName] ?? Globals.gitSignatureDefaultName
|
||||
|
|
@ -39,9 +39,9 @@ public class PasswordStore {
|
|||
return GTSignature(name: gitSignatureName, email: gitSignatureEmail, time: Date())!
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public let keyring = ObjectivePGP.defaultKeyring
|
||||
|
||||
|
||||
public var pgpKeyPassphrase: String? {
|
||||
set {
|
||||
Utils.addPasswordToKeychain(name: "pgpKeyPassphrase", password: newValue)
|
||||
|
|
@ -50,7 +50,7 @@ public class PasswordStore {
|
|||
return Utils.getPasswordFromKeychain(name: "pgpKeyPassphrase")
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public var gitPassword: String? {
|
||||
set {
|
||||
Utils.addPasswordToKeychain(name: "gitPassword", password: newValue)
|
||||
|
|
@ -59,7 +59,7 @@ public class PasswordStore {
|
|||
return Utils.getPasswordFromKeychain(name: "gitPassword")
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public var gitSSHPrivateKeyPassphrase: String? {
|
||||
set {
|
||||
Utils.addPasswordToKeychain(name: "gitSSHPrivateKeyPassphrase", password: newValue)
|
||||
|
|
@ -68,7 +68,7 @@ public class PasswordStore {
|
|||
return Utils.getPasswordFromKeychain(name: "gitSSHPrivateKeyPassphrase")
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private let fm = FileManager.default
|
||||
lazy private var context: NSManagedObjectContext = {
|
||||
let modelURL = Bundle(identifier: Globals.passKitBundleIdentifier)!.url(forResource: "pass", withExtension: "momd")!
|
||||
|
|
@ -82,7 +82,7 @@ public class PasswordStore {
|
|||
if let error = error as NSError? {
|
||||
// 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.
|
||||
|
||||
|
||||
/*
|
||||
Typical reasons for an error here include:
|
||||
* The parent directory does not exist, cannot be created, or disallows writing.
|
||||
|
|
@ -96,11 +96,11 @@ public class PasswordStore {
|
|||
})
|
||||
return container.viewContext
|
||||
}()
|
||||
|
||||
|
||||
public var numberOfPasswords : Int {
|
||||
return self.fetchPasswordEntityCoreData(withDir: false).count
|
||||
return self.fetchPasswordEntityCoreData(withDir: false).count
|
||||
}
|
||||
|
||||
|
||||
public var sizeOfRepositoryByteCount : UInt64 {
|
||||
return (try? fm.allocatedSizeOfDirectoryAtURL(directoryURL: self.storeURL)) ?? 0
|
||||
}
|
||||
|
|
@ -112,12 +112,12 @@ public class PasswordStore {
|
|||
public var lastSyncedTime: Date? {
|
||||
return SharedDefaults[.lastSyncedTime]
|
||||
}
|
||||
|
||||
|
||||
private init() {
|
||||
// File migration to group
|
||||
migrateIfNeeded()
|
||||
backwardCompatibility()
|
||||
|
||||
|
||||
do {
|
||||
if fm.fileExists(atPath: storeURL.path) {
|
||||
try storeRepository = GTRepository.init(url: storeURL)
|
||||
|
|
@ -127,14 +127,14 @@ public class PasswordStore {
|
|||
print(error)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private func migrateIfNeeded() {
|
||||
// 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)
|
||||
guard needMigration == true else {
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
do {
|
||||
// migrate Defaults
|
||||
let userDefaults = UserDefaults()
|
||||
|
|
@ -143,7 +143,7 @@ public class PasswordStore {
|
|||
SharedDefaults.setValue(userDefaults.value(forKey: key), forKey: key)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// migrate files
|
||||
try fm.createDirectory(atPath: Globals.documentPath, withIntermediateDirectories: true, attributes: nil)
|
||||
try fm.createDirectory(atPath: Globals.libraryPath, withIntermediateDirectories: true, attributes: nil)
|
||||
|
|
@ -162,7 +162,7 @@ public class PasswordStore {
|
|||
}
|
||||
updatePasswordEntityCoreData()
|
||||
}
|
||||
|
||||
|
||||
private func backwardCompatibility() {
|
||||
// For the newly-introduced isRememberGitCredentialPassphraseOn (20171008)
|
||||
if (self.gitPassword != nil || self.gitSSHPrivateKeyPassphrase != nil) && SharedDefaults[.isRememberGitCredentialPassphraseOn] == false {
|
||||
|
|
@ -173,21 +173,21 @@ public class PasswordStore {
|
|||
SharedDefaults[.isRememberPGPPassphraseOn] = true
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
enum SSHKeyType {
|
||||
case `public`, secret
|
||||
}
|
||||
|
||||
|
||||
public func initGitSSHKey(with armorKey: String) throws {
|
||||
let keyPath = Globals.gitSSHPrivateKeyPath
|
||||
try armorKey.write(toFile: keyPath, atomically: true, encoding: .ascii)
|
||||
}
|
||||
|
||||
|
||||
public func initPGPKeys() throws {
|
||||
try initPGPKey(.public)
|
||||
try initPGPKey(.secret)
|
||||
}
|
||||
|
||||
|
||||
public func initPGPKey(_ keyType: PGPKeyType) throws {
|
||||
switch keyType {
|
||||
case .public:
|
||||
|
|
@ -206,7 +206,7 @@ public class PasswordStore {
|
|||
throw AppError.UnknownError
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public func initPGPKey(from url: URL, keyType: PGPKeyType) throws {
|
||||
var pgpKeyLocalPath = ""
|
||||
if keyType == .public {
|
||||
|
|
@ -218,7 +218,7 @@ public class PasswordStore {
|
|||
try pgpKeyData.write(to: URL(fileURLWithPath: pgpKeyLocalPath), options: .atomic)
|
||||
try initPGPKey(keyType)
|
||||
}
|
||||
|
||||
|
||||
public func initPGPKey(with armorKey: String, keyType: PGPKeyType) throws {
|
||||
var pgpKeyLocalPath = ""
|
||||
if keyType == .public {
|
||||
|
|
@ -229,8 +229,8 @@ public class PasswordStore {
|
|||
try armorKey.write(toFile: pgpKeyLocalPath, atomically: true, encoding: .ascii)
|
||||
try initPGPKey(keyType)
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
private func importKey(from keyPath: String) -> Key? {
|
||||
if fm.fileExists(atPath: keyPath) {
|
||||
let keys = try! ObjectivePGP.readKeys(fromPath: keyPath)
|
||||
|
|
@ -245,12 +245,12 @@ public class PasswordStore {
|
|||
public func getPgpPrivateKey() -> Key {
|
||||
return keyring.keys.filter({$0.secretKey != nil})[0]
|
||||
}
|
||||
|
||||
|
||||
public func repositoryExisted() -> Bool {
|
||||
let fm = FileManager()
|
||||
return fm.fileExists(atPath: Globals.repositoryPath)
|
||||
}
|
||||
|
||||
|
||||
public func passwordExisted(password: Password) -> Bool {
|
||||
let passwordEntityFetchRequest = NSFetchRequest<NSFetchRequestResult>(entityName: "PasswordEntity")
|
||||
do {
|
||||
|
|
@ -266,7 +266,7 @@ public class PasswordStore {
|
|||
}
|
||||
return true
|
||||
}
|
||||
|
||||
|
||||
public func passwordEntityExisted(path: String) -> Bool {
|
||||
let passwordEntityFetchRequest = NSFetchRequest<NSFetchRequestResult>(entityName: "PasswordEntity")
|
||||
do {
|
||||
|
|
@ -282,7 +282,7 @@ public class PasswordStore {
|
|||
}
|
||||
return true
|
||||
}
|
||||
|
||||
|
||||
public func getPasswordEntity(by path: String, isDir: Bool) -> PasswordEntity? {
|
||||
let passwordEntityFetchRequest = NSFetchRequest<NSFetchRequestResult>(entityName: "PasswordEntity")
|
||||
do {
|
||||
|
|
@ -292,7 +292,7 @@ public class PasswordStore {
|
|||
fatalError("Failed to fetch password entities: \(error)")
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public func cloneRepository(remoteRepoURL: URL,
|
||||
credential: GitCredential,
|
||||
requestGitPassword: @escaping (GitCredential.Credential, String?) -> String?,
|
||||
|
|
@ -323,7 +323,7 @@ public class PasswordStore {
|
|||
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 {
|
||||
guard let storeRepository = storeRepository else {
|
||||
throw AppError.RepositoryNotSetError
|
||||
|
|
@ -339,7 +339,7 @@ public class PasswordStore {
|
|||
NotificationCenter.default.post(name: .passwordStoreUpdated, object: nil)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private func updatePasswordEntityCoreData() {
|
||||
deleteCoreData(entityName: "PasswordEntity")
|
||||
do {
|
||||
|
|
@ -393,7 +393,7 @@ public class PasswordStore {
|
|||
print("Error with save: \(error)")
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public func getRecentCommits(count: Int) throws -> [GTCommit] {
|
||||
guard let storeRepository = storeRepository else {
|
||||
return []
|
||||
|
|
@ -410,7 +410,7 @@ public class PasswordStore {
|
|||
}
|
||||
return commits
|
||||
}
|
||||
|
||||
|
||||
public func fetchPasswordEntityCoreData(parent: PasswordEntity?) -> [PasswordEntity] {
|
||||
let passwordEntityFetch = NSFetchRequest<NSFetchRequestResult>(entityName: "PasswordEntity")
|
||||
do {
|
||||
|
|
@ -421,7 +421,7 @@ public class PasswordStore {
|
|||
fatalError("Failed to fetch passwords: \(error)")
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public func fetchPasswordEntityCoreData(withDir: Bool) -> [PasswordEntity] {
|
||||
let passwordEntityFetch = NSFetchRequest<NSFetchRequestResult>(entityName: "PasswordEntity")
|
||||
do {
|
||||
|
|
@ -434,8 +434,8 @@ public class PasswordStore {
|
|||
fatalError("Failed to fetch passwords: \(error)")
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
public func fetchUnsyncedPasswords() -> [PasswordEntity] {
|
||||
let passwordEntityFetchRequest = NSFetchRequest<NSFetchRequestResult>(entityName: "PasswordEntity")
|
||||
passwordEntityFetchRequest.predicate = NSPredicate(format: "synced = %i", 0)
|
||||
|
|
@ -446,7 +446,7 @@ public class PasswordStore {
|
|||
fatalError("Failed to fetch passwords: \(error)")
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public func setAllSynced() {
|
||||
let passwordEntities = fetchUnsyncedPasswords()
|
||||
for passwordEntity in passwordEntities {
|
||||
|
|
@ -460,7 +460,7 @@ public class PasswordStore {
|
|||
fatalError("Failed to save: \(error)")
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public func getLatestUpdateInfo(filename: String) -> String {
|
||||
guard let storeRepository = storeRepository else {
|
||||
return "Unknown"
|
||||
|
|
@ -486,10 +486,10 @@ public class PasswordStore {
|
|||
}
|
||||
return autoFormattedDifference
|
||||
}
|
||||
|
||||
|
||||
public func updateRemoteRepo() {
|
||||
}
|
||||
|
||||
|
||||
private func gitAdd(path: String) throws {
|
||||
guard let storeRepository = storeRepository else {
|
||||
throw AppError.RepositoryNotSetError
|
||||
|
|
@ -497,7 +497,7 @@ public class PasswordStore {
|
|||
try storeRepository.index().addFile(path)
|
||||
try storeRepository.index().write()
|
||||
}
|
||||
|
||||
|
||||
private func gitRm(path: String) throws {
|
||||
guard let storeRepository = storeRepository else {
|
||||
throw AppError.RepositoryNotSetError
|
||||
|
|
@ -509,7 +509,7 @@ public class PasswordStore {
|
|||
try storeRepository.index().removeFile(path)
|
||||
try storeRepository.index().write()
|
||||
}
|
||||
|
||||
|
||||
private func deleteDirectoryTree(at url: URL) throws {
|
||||
var tempURL = storeURL.appendingPathComponent(url.deletingLastPathComponent().path)
|
||||
var count = try fm.contentsOfDirectory(atPath: tempURL.path).count
|
||||
|
|
@ -519,12 +519,12 @@ public class PasswordStore {
|
|||
count = try fm.contentsOfDirectory(atPath: tempURL.path).count
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private func createDirectoryTree(at url: URL) throws {
|
||||
let tempURL = storeURL.appendingPathComponent(url.deletingLastPathComponent().path)
|
||||
try fm.createDirectory(at: tempURL, withIntermediateDirectories: true, attributes: nil)
|
||||
}
|
||||
|
||||
|
||||
private func gitMv(from: String, to: String) throws {
|
||||
let fromURL = storeURL.appendingPathComponent(from)
|
||||
let toURL = storeURL.appendingPathComponent(to)
|
||||
|
|
@ -532,7 +532,7 @@ public class PasswordStore {
|
|||
try gitAdd(path: to)
|
||||
try gitRm(path: from)
|
||||
}
|
||||
|
||||
|
||||
private func gitCommit(message: String) throws -> GTCommit? {
|
||||
guard let storeRepository = storeRepository else {
|
||||
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)
|
||||
return commit
|
||||
}
|
||||
|
||||
|
||||
private func getLocalBranch(withName branchName: String) throws -> GTBranch? {
|
||||
guard let storeRepository = storeRepository else {
|
||||
throw AppError.RepositoryNotSetError
|
||||
|
|
@ -555,7 +555,7 @@ public class PasswordStore {
|
|||
let branches = try storeRepository.branches(withPrefix: reference)
|
||||
return branches.first
|
||||
}
|
||||
|
||||
|
||||
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 {
|
||||
throw AppError.RepositoryNotSetError
|
||||
|
|
@ -571,12 +571,12 @@ public class PasswordStore {
|
|||
throw(error)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private func addPasswordEntities(password: Password) throws -> PasswordEntity? {
|
||||
guard !passwordExisted(password: password) else {
|
||||
throw AppError.PasswordDuplicatedError
|
||||
}
|
||||
|
||||
|
||||
var passwordURL = password.url
|
||||
var previousPathLength = Int.max
|
||||
var paths: [String] = []
|
||||
|
|
@ -606,7 +606,7 @@ public class PasswordStore {
|
|||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
|
||||
private func insertPasswordEntity(name: String, path: String, parent: PasswordEntity?, synced: Bool = false, isDir: Bool = false) -> PasswordEntity? {
|
||||
var ret: PasswordEntity? = nil
|
||||
if let passwordEntity = NSEntityDescription.insertNewObject(forEntityName: "PasswordEntity", into: self.context) as? PasswordEntity {
|
||||
|
|
@ -624,7 +624,7 @@ public class PasswordStore {
|
|||
}
|
||||
return ret
|
||||
}
|
||||
|
||||
|
||||
public func add(password: Password) throws -> PasswordEntity? {
|
||||
try createDirectoryTree(at: password.url)
|
||||
let newPasswordEntity = try addPasswordEntities(password: password)
|
||||
|
|
@ -635,7 +635,7 @@ public class PasswordStore {
|
|||
NotificationCenter.default.post(name: .passwordStoreUpdated, object: nil)
|
||||
return newPasswordEntity
|
||||
}
|
||||
|
||||
|
||||
public func delete(passwordEntity: PasswordEntity) throws {
|
||||
let deletedFileURL = passwordEntity.getURL()!
|
||||
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.")
|
||||
NotificationCenter.default.post(name: .passwordStoreUpdated, object: nil)
|
||||
}
|
||||
|
||||
|
||||
public func edit(passwordEntity: PasswordEntity, password: Password) throws -> PasswordEntity? {
|
||||
var newPasswordEntity: PasswordEntity? = passwordEntity
|
||||
|
||||
|
|
@ -656,16 +656,16 @@ public class PasswordStore {
|
|||
newPasswordEntity = passwordEntity
|
||||
newPasswordEntity?.synced = false
|
||||
}
|
||||
|
||||
|
||||
if password.changed&PasswordChange.path.rawValue != 0 {
|
||||
let deletedFileURL = passwordEntity.getURL()!
|
||||
// add
|
||||
try createDirectoryTree(at: password.url)
|
||||
newPasswordEntity = try addPasswordEntities(password: password)
|
||||
|
||||
|
||||
// mv
|
||||
try gitMv(from: deletedFileURL.path, to: password.url.path)
|
||||
|
||||
|
||||
// delete
|
||||
try deleteDirectoryTree(at: deletedFileURL)
|
||||
try deletePasswordEntities(passwordEntity: passwordEntity)
|
||||
|
|
@ -675,7 +675,7 @@ public class PasswordStore {
|
|||
NotificationCenter.default.post(name: .passwordStoreUpdated, object: nil)
|
||||
return newPasswordEntity
|
||||
}
|
||||
|
||||
|
||||
private func deletePasswordEntities(passwordEntity: PasswordEntity) throws {
|
||||
var current: PasswordEntity? = passwordEntity
|
||||
while current != nil && (current!.children!.count == 0 || !current!.isDir) {
|
||||
|
|
@ -689,7 +689,7 @@ public class PasswordStore {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public func saveUpdated(passwordEntity: PasswordEntity) {
|
||||
do {
|
||||
try context.save()
|
||||
|
|
@ -697,11 +697,11 @@ public class PasswordStore {
|
|||
fatalError("Failed to save a PasswordEntity: \(error)")
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public func deleteCoreData(entityName: String) {
|
||||
let deleteFetchRequest = NSFetchRequest<NSFetchRequestResult>(entityName: entityName)
|
||||
let deleteRequest = NSBatchDeleteRequest(fetchRequest: deleteFetchRequest)
|
||||
|
||||
|
||||
do {
|
||||
try context.execute(deleteRequest)
|
||||
try context.save()
|
||||
|
|
@ -710,7 +710,7 @@ public class PasswordStore {
|
|||
print(error)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public func updateImage(passwordEntity: PasswordEntity, image: Data?) {
|
||||
guard let image = image else {
|
||||
return
|
||||
|
|
@ -733,7 +733,7 @@ public class PasswordStore {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public func erase() {
|
||||
publicKey = nil
|
||||
privateKey = nil
|
||||
|
|
@ -743,19 +743,19 @@ public class PasswordStore {
|
|||
try? fm.removeItem(atPath: Globals.pgpPublicKeyPath)
|
||||
try? fm.removeItem(atPath: Globals.pgpPrivateKeyPath)
|
||||
try? fm.removeItem(atPath: Globals.gitSSHPrivateKeyPath)
|
||||
|
||||
|
||||
Utils.removeAllKeychain()
|
||||
|
||||
deleteCoreData(entityName: "PasswordEntity")
|
||||
|
||||
|
||||
SharedDefaults.removeAll()
|
||||
storeRepository = nil
|
||||
|
||||
|
||||
NotificationCenter.default.post(name: .passwordStoreUpdated, 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 {
|
||||
guard let storeRepository = storeRepository else {
|
||||
throw AppError.RepositoryNotSetError
|
||||
|
|
@ -772,7 +772,7 @@ public class PasswordStore {
|
|||
try storeRepository.reset(to: newHead, resetType: .hard)
|
||||
self.setAllSynced()
|
||||
self.updatePasswordEntityCoreData()
|
||||
|
||||
|
||||
NotificationCenter.default.post(name: .passwordStoreUpdated, object: nil)
|
||||
NotificationCenter.default.post(name: .passwordStoreChangeDiscarded, object: nil)
|
||||
return localCommits.count
|
||||
|
|
@ -780,8 +780,8 @@ public class PasswordStore {
|
|||
return 0 // no new commit
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
private func getLocalCommits() throws -> [GTCommit]? {
|
||||
guard let storeRepository = storeRepository else {
|
||||
throw AppError.RepositoryNotSetError
|
||||
|
|
@ -791,18 +791,18 @@ public class PasswordStore {
|
|||
throw AppError.RepositoryRemoteMasterNotFoundError
|
||||
}
|
||||
let remoteMasterBranch = try storeRepository.remoteBranches()[index]
|
||||
|
||||
|
||||
// check oid before calling localCommitsRelative
|
||||
guard remoteMasterBranch.oid != nil else {
|
||||
throw AppError.RepositoryRemoteMasterNotFoundError
|
||||
}
|
||||
|
||||
|
||||
// get a list of local commits
|
||||
return try storeRepository.localCommitsRelative(toRemoteBranch: remoteMasterBranch)
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
public func decrypt(passwordEntity: PasswordEntity, requestPGPKeyPassphrase: () -> String) throws -> Password? {
|
||||
let encryptedDataPath = storeURL.appendingPathComponent(passwordEntity.getPath())
|
||||
let encryptedData = try Data(contentsOf: encryptedDataPath)
|
||||
|
|
@ -817,7 +817,7 @@ public class PasswordStore {
|
|||
}
|
||||
return Password(name: passwordEntity.getName(), url: url, plainText: plainText)
|
||||
}
|
||||
|
||||
|
||||
public func encrypt(password: Password) throws -> Data {
|
||||
guard keyring.keys.count > 0 else {
|
||||
throw AppError.PGPPublicKeyNotExistError
|
||||
|
|
@ -830,7 +830,7 @@ public class PasswordStore {
|
|||
return encryptedData
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public func removePGPKeys() {
|
||||
try? fm.removeItem(atPath: Globals.pgpPublicKeyPath)
|
||||
try? fm.removeItem(atPath: Globals.pgpPrivateKeyPath)
|
||||
|
|
@ -844,14 +844,14 @@ public class PasswordStore {
|
|||
publicKey = nil
|
||||
privateKey = nil
|
||||
}
|
||||
|
||||
|
||||
public func removeGitSSHKeys() {
|
||||
try? fm.removeItem(atPath: Globals.gitSSHPrivateKeyPath)
|
||||
Defaults.remove(.gitSSHPrivateKeyArmor)
|
||||
Defaults.remove(.gitSSHPrivateKeyURL)
|
||||
self.gitSSHPrivateKeyPassphrase = nil
|
||||
}
|
||||
|
||||
|
||||
public func gitSSHKeyExists(inFileSharing: Bool = false) -> Bool {
|
||||
if inFileSharing == false {
|
||||
return fm.fileExists(atPath: Globals.gitSSHPrivateKeyPath)
|
||||
|
|
@ -859,7 +859,7 @@ public class PasswordStore {
|
|||
return fm.fileExists(atPath: Globals.iTunesFileSharingSSHPrivate)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public func pgpKeyExists(inFileSharing: Bool = false) -> Bool {
|
||||
if inFileSharing == false {
|
||||
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)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public func gitSSHKeyImportFromFileSharing() throws {
|
||||
try fm.moveItem(atPath: Globals.iTunesFileSharingSSHPrivate, toPath: Globals.gitSSHPrivateKeyPath)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -35,7 +35,7 @@ extension AdditionField {
|
|||
}
|
||||
|
||||
extension AdditionField: Equatable {
|
||||
|
||||
|
||||
public static func == (first: AdditionField, second: AdditionField) -> Bool {
|
||||
return first.asTuple == second.asTuple
|
||||
}
|
||||
|
|
|
|||
|
|
@ -16,7 +16,7 @@ public enum OtpType: String {
|
|||
var description: String {
|
||||
return rawValue
|
||||
}
|
||||
|
||||
|
||||
init(token: Token?) {
|
||||
switch token?.generator.factor {
|
||||
case .some(.counter):
|
||||
|
|
|
|||
|
|
@ -14,7 +14,7 @@ class PasswordTest: XCTestCase {
|
|||
|
||||
func testUrl() {
|
||||
let password = getPasswordObjectWith(content: "")
|
||||
|
||||
|
||||
XCTAssertEqual(password.url, PASSWORD_URL)
|
||||
XCTAssertEqual(password.namePath, PASSWORD_PATH)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -10,27 +10,27 @@ import XCTest
|
|||
@testable import passKit
|
||||
|
||||
class passKitTests: XCTestCase {
|
||||
|
||||
|
||||
override func setUp() {
|
||||
super.setUp()
|
||||
// Put setup code here. This method is called before the invocation of each test method in the class.
|
||||
}
|
||||
|
||||
|
||||
override func tearDown() {
|
||||
// Put teardown code here. This method is called after the invocation of each test method in the class.
|
||||
super.tearDown()
|
||||
}
|
||||
|
||||
|
||||
func testExample() {
|
||||
// This is an example of a functional test case.
|
||||
// Use XCTAssert and related functions to verify your tests produce the correct results.
|
||||
}
|
||||
|
||||
|
||||
func testPerformanceExample() {
|
||||
// This is an example of a performance test case.
|
||||
self.measure {
|
||||
// Put the code you want to measure the time of here.
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -9,27 +9,27 @@
|
|||
import XCTest
|
||||
|
||||
class passTests: XCTestCase {
|
||||
|
||||
|
||||
override func setUp() {
|
||||
super.setUp()
|
||||
// Put setup code here. This method is called before the invocation of each test method in the class.
|
||||
}
|
||||
|
||||
|
||||
override func tearDown() {
|
||||
// Put teardown code here. This method is called after the invocation of each test method in the class.
|
||||
super.tearDown()
|
||||
}
|
||||
|
||||
|
||||
func testExample() {
|
||||
// This is an example of a functional test case.
|
||||
// Use XCTAssert and related functions to verify your tests produce the correct results.
|
||||
}
|
||||
|
||||
|
||||
func testPerformanceExample() {
|
||||
// This is an example of a performance test case.
|
||||
self.measure {
|
||||
// Put the code you want to measure the time of here.
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue