Move codes to an embed framework

- Move bundle/group identifiers to passKit/Global
- Fix Core Data
- Change Defaults to SharedDefaults
This commit is contained in:
Yishi Lin 2017-06-13 11:42:49 +08:00
parent 850dc75820
commit d2ba620ae4
45 changed files with 1062 additions and 523 deletions

22
PassKitTests/Info.plist Normal file
View file

@ -0,0 +1,22 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>CFBundleDevelopmentRegion</key>
<string>en</string>
<key>CFBundleExecutable</key>
<string>$(EXECUTABLE_NAME)</string>
<key>CFBundleIdentifier</key>
<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
<key>CFBundleInfoDictionaryVersion</key>
<string>6.0</string>
<key>CFBundleName</key>
<string>$(PRODUCT_NAME)</string>
<key>CFBundlePackageType</key>
<string>BNDL</string>
<key>CFBundleShortVersionString</key>
<string>1.0</string>
<key>CFBundleVersion</key>
<string>1</string>
</dict>
</plist>

View file

@ -0,0 +1,36 @@
//
// passKitTests.swift
// passKitTests
//
// Created by Yishi Lin on 11/6/17.
// Copyright © 2017 Bob Sun. All rights reserved.
//
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.
}
}
}

44
Podfile
View file

@ -1,3 +1,47 @@
def generate_modulemap(name, path)
f = File.new(File.join("#{path}/module.modulemap"), "w+")
module_name = "#{name}"
while(module_name["+"])
module_name["+"] = "_"
end
f.puts("module #{module_name} {")
f.puts(" umbrella header \"#{name}_umbrella.h\"")
f.puts(" export *")
f.puts("}")
end
def generate_umbrella(name, path)
f = File.new(File.join("#{path}/#{name}_umbrella.h"), "w+")
f.puts("#import <Foundation/Foundation.h>")
Dir.chdir(path) {
Dir.glob("**/*.h").map {
|filename| f.puts("#import \"#{filename}\"")
}
}
end
post_install do |installer|
require "fileutils"
headers_path = "#{Dir::pwd}/Pods/Headers/Public/"
installer.pods_project.targets.each do |target|
target_header_path = "#{headers_path}#{target.product_name}"
if File.exist?(target_header_path)
filename = target.product_name
if filename != "." and filename != ".."
generate_umbrella(filename, target_header_path)
generate_modulemap(filename, target_header_path)
end
end
end
end
target 'pass' do target 'pass' do
pod 'ObjectivePGP', :git => 'https://github.com/mssun/ObjectivePGP.git' pod 'ObjectivePGP', :git => 'https://github.com/mssun/ObjectivePGP.git'
target 'passKit' do
inherit! :search_paths
end
target 'passextension' do
inherit! :search_paths
end
end end

File diff suppressed because it is too large Load diff

View file

@ -1,108 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
LastUpgradeVersion = "0820"
version = "1.3">
<BuildAction
parallelizeBuildables = "YES"
buildImplicitDependencies = "YES">
<BuildActionEntries>
<BuildActionEntry
buildForTesting = "YES"
buildForRunning = "YES"
buildForProfiling = "YES"
buildForArchiving = "YES"
buildForAnalyzing = "YES">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "DC917BD21E2E8231000FDF54"
BuildableName = "pass.app"
BlueprintName = "pass"
ReferencedContainer = "container:pass.xcodeproj">
</BuildableReference>
</BuildActionEntry>
</BuildActionEntries>
</BuildAction>
<TestAction
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
shouldUseLaunchSchemeArgsEnv = "YES">
<Testables>
<TestableReference
skipped = "NO">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "DC13B14D1E8640810097803F"
BuildableName = "passTests.xctest"
BlueprintName = "passTests"
ReferencedContainer = "container:pass.xcodeproj">
</BuildableReference>
</TestableReference>
</Testables>
<MacroExpansion>
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "DC917BD21E2E8231000FDF54"
BuildableName = "pass.app"
BlueprintName = "pass"
ReferencedContainer = "container:pass.xcodeproj">
</BuildableReference>
</MacroExpansion>
<AdditionalOptions>
</AdditionalOptions>
</TestAction>
<LaunchAction
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
launchStyle = "0"
useCustomWorkingDirectory = "NO"
ignoresPersistentStateOnLaunch = "NO"
debugDocumentVersioning = "YES"
debugServiceExtension = "internal"
allowLocationSimulation = "YES">
<BuildableProductRunnable
runnableDebuggingMode = "0">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "DC917BD21E2E8231000FDF54"
BuildableName = "pass.app"
BlueprintName = "pass"
ReferencedContainer = "container:pass.xcodeproj">
</BuildableReference>
</BuildableProductRunnable>
<EnvironmentVariables>
<EnvironmentVariable
key = "OS_ACTIVITY_MODE"
value = "disable"
isEnabled = "YES">
</EnvironmentVariable>
</EnvironmentVariables>
<AdditionalOptions>
</AdditionalOptions>
</LaunchAction>
<ProfileAction
buildConfiguration = "Release"
shouldUseLaunchSchemeArgsEnv = "YES"
savedToolIdentifier = ""
useCustomWorkingDirectory = "NO"
debugDocumentVersioning = "YES">
<BuildableProductRunnable
runnableDebuggingMode = "0">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "DC917BD21E2E8231000FDF54"
BuildableName = "pass.app"
BlueprintName = "pass"
ReferencedContainer = "container:pass.xcodeproj">
</BuildableReference>
</BuildableProductRunnable>
</ProfileAction>
<AnalyzeAction
buildConfiguration = "Debug">
</AnalyzeAction>
<ArchiveAction
buildConfiguration = "Release"
revealArchiveInOrganizer = "YES">
</ArchiveAction>
</Scheme>

View file

@ -10,6 +10,7 @@ import UIKit
import CoreData import CoreData
import PasscodeLock import PasscodeLock
import SVProgressHUD import SVProgressHUD
import passKit
@UIApplicationMain @UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate { class AppDelegate: UIResponder, UIApplicationDelegate {
@ -20,7 +21,7 @@ class AppDelegate: UIResponder, UIApplicationDelegate {
} }
lazy var passcodeLockPresenter: PasscodeLockPresenter = { lazy var passcodeLockPresenter: PasscodeLockPresenter = {
let presenter = PasscodeLockPresenter(mainWindow: self.window, configuration: Globals.passcodeConfiguration) let presenter = PasscodeLockPresenter(mainWindow: self.window, configuration: PasscodeLockConfiguration.shared)
return presenter return presenter
}() }()
@ -29,9 +30,7 @@ class AppDelegate: UIResponder, UIApplicationDelegate {
SVProgressHUD.setMinimumSize(CGSize(width: 150, height: 100)) SVProgressHUD.setMinimumSize(CGSize(width: 150, height: 100))
passcodeLockPresenter.present() passcodeLockPresenter.present()
if let shortcutItem = launchOptions?[UIApplicationLaunchOptionsKey.shortcutItem] as? UIApplicationShortcutItem { if let shortcutItem = launchOptions?[UIApplicationLaunchOptionsKey.shortcutItem] as? UIApplicationShortcutItem {
var searchType = Bundle.main.bundleIdentifier! if shortcutItem.type == Globals.bundleIdentifier + ".search" {
searchType.append(".search")
if shortcutItem.type == searchType {
self.perform(#selector(postSearchNotification), with: nil, afterDelay: 0.4) self.perform(#selector(postSearchNotification), with: nil, afterDelay: 0.4)
} }
} }
@ -44,9 +43,7 @@ class AppDelegate: UIResponder, UIApplicationDelegate {
} }
func application(_ application: UIApplication, performActionFor shortcutItem: UIApplicationShortcutItem, completionHandler: @escaping (Bool) -> Void) { func application(_ application: UIApplication, performActionFor shortcutItem: UIApplicationShortcutItem, completionHandler: @escaping (Bool) -> Void) {
var searchType = Bundle.main.bundleIdentifier! if shortcutItem.type == Globals.bundleIdentifier + ".search" {
searchType.append(".search")
if shortcutItem.type == searchType {
let tabBarController = self.window!.rootViewController as! UITabBarController let tabBarController = self.window!.rootViewController as! UITabBarController
tabBarController.selectedIndex = 0 tabBarController.selectedIndex = 0
let navigationController = tabBarController.selectedViewController as! UINavigationController let navigationController = tabBarController.selectedViewController as! UINavigationController
@ -112,9 +109,11 @@ class AppDelegate: UIResponder, UIApplicationDelegate {
application to it. This property is optional since there are legitimate application to it. This property is optional since there are legitimate
error conditions that could cause the creation of the store to fail. error conditions that could cause the creation of the store to fail.
*/ */
let container = NSPersistentContainer(name: "pass") let modelURL = Bundle(identifier: Globals.passKitBundleIdentifier)!.url(forResource: "pass", withExtension: "momd")!
let description = NSPersistentStoreDescription(url: Globals.sharedContainerURL) let managedObjectModel = NSManagedObjectModel(contentsOf: modelURL)
container.loadPersistentStores(completionHandler: { (description, error) in let container = NSPersistentContainer(name: "pass", managedObjectModel: managedObjectModel!)
container.persistentStoreDescriptions = [NSPersistentStoreDescription(url: Globals.sharedContainerURL.appendingPathComponent("Documents/pass.sqlite"))]
container.loadPersistentStores(completionHandler: { (storeDescription, error) in
if let error = error as NSError? { if let error = error as NSError? {
// Replace this implementation with code to handle the error appropriately. // Replace this implementation with code to handle the error appropriately.
// fatalError() causes the application to generate a crash log and terminate. You should not use this function in a shipping application, although it may be useful during development. // fatalError() causes the application to generate a crash log and terminate. You should not use this function in a shipping application, although it may be useful during development.

View file

@ -7,6 +7,7 @@
// //
import UIKit import UIKit
import passKit
class AboutRepositoryTableViewController: BasicStaticTableViewController { class AboutRepositoryTableViewController: BasicStaticTableViewController {

View file

@ -7,7 +7,7 @@
// //
import UIKit import UIKit
import SwiftyUserDefaults import passKit
class AddPasswordTableViewController: PasswordEditorTableViewController { class AddPasswordTableViewController: PasswordEditorTableViewController {
var tempContent: String = "" var tempContent: String = ""
@ -20,7 +20,7 @@ class AddPasswordTableViewController: PasswordEditorTableViewController {
[[.type: PasswordEditorCellType.additionsCell, .title: "additions"]], [[.type: PasswordEditorCellType.additionsCell, .title: "additions"]],
[[.type: PasswordEditorCellType.scanQRCodeCell]] [[.type: PasswordEditorCellType.scanQRCodeCell]]
] ]
if let lengthSetting = Globals.passwordDefaultLength[Defaults[.passwordGeneratorFlavor]], if let lengthSetting = Globals.passwordDefaultLength[SharedDefaults[.passwordGeneratorFlavor]],
lengthSetting.max > lengthSetting.min { lengthSetting.max > lengthSetting.min {
tableData[1].append([.type: PasswordEditorCellType.passwordLengthCell, .title: "passwordlength"]) tableData[1].append([.type: PasswordEditorCellType.passwordLengthCell, .title: "passwordlength"])
} }

View file

@ -8,7 +8,7 @@
import UIKit import UIKit
import SVProgressHUD import SVProgressHUD
import SwiftyUserDefaults import passKit
class AdvancedSettingsTableViewController: UITableViewController { class AdvancedSettingsTableViewController: UITableViewController {
@ -28,7 +28,7 @@ class AdvancedSettingsTableViewController: UITableViewController {
override func viewDidLoad() { override func viewDidLoad() {
super.viewDidLoad() super.viewDidLoad()
encryptInASCIIArmoredSwitch.isOn = Defaults[.encryptInArmored] encryptInASCIIArmoredSwitch.isOn = SharedDefaults[.encryptInArmored]
encryptInASCIIArmoredTableViewCell.accessoryView = encryptInASCIIArmoredSwitch encryptInASCIIArmoredTableViewCell.accessoryView = encryptInASCIIArmoredSwitch
encryptInASCIIArmoredTableViewCell.selectionStyle = .none encryptInASCIIArmoredTableViewCell.selectionStyle = .none
setGitSignatureText() setGitSignatureText()
@ -39,7 +39,7 @@ class AdvancedSettingsTableViewController: UITableViewController {
let gitSignatureEmail = passwordStore.gitSignatureForNow.email! let gitSignatureEmail = passwordStore.gitSignatureForNow.email!
self.gitSignatureTableViewCell.detailTextLabel?.font = UIFont.systemFont(ofSize: 14) self.gitSignatureTableViewCell.detailTextLabel?.font = UIFont.systemFont(ofSize: 14)
self.gitSignatureTableViewCell.detailTextLabel?.text = "\(gitSignatureName) <\(gitSignatureEmail)>" self.gitSignatureTableViewCell.detailTextLabel?.text = "\(gitSignatureName) <\(gitSignatureEmail)>"
if Defaults[.gitSignatureName] == nil && Defaults[.gitSignatureEmail] == nil { if SharedDefaults[.gitSignatureName] == nil && SharedDefaults[.gitSignatureEmail] == nil {
self.gitSignatureTableViewCell.detailTextLabel?.font = UIFont.systemFont(ofSize: 17) self.gitSignatureTableViewCell.detailTextLabel?.font = UIFont.systemFont(ofSize: 17)
gitSignatureTableViewCell.detailTextLabel?.text = "Not Set" gitSignatureTableViewCell.detailTextLabel?.text = "Not Set"
} }
@ -85,7 +85,7 @@ class AdvancedSettingsTableViewController: UITableViewController {
} }
func encryptInASCIIArmoredAction(_ sender: Any?) { func encryptInASCIIArmoredAction(_ sender: Any?) {
Defaults[.encryptInArmored] = encryptInASCIIArmoredSwitch.isOn SharedDefaults[.encryptInArmored] = encryptInASCIIArmoredSwitch.isOn
} }
@IBAction func cancelGitConfigSetting(segue: UIStoryboardSegue) { @IBAction func cancelGitConfigSetting(segue: UIStoryboardSegue) {
@ -95,8 +95,8 @@ class AdvancedSettingsTableViewController: UITableViewController {
if let controller = segue.source as? GitConfigSettingTableViewController { if let controller = segue.source as? GitConfigSettingTableViewController {
if let gitSignatureName = controller.nameTextField.text, if let gitSignatureName = controller.nameTextField.text,
let gitSignatureEmail = controller.emailTextField.text { let gitSignatureEmail = controller.emailTextField.text {
Defaults[.gitSignatureName] = gitSignatureName.isEmpty ? nil : gitSignatureName SharedDefaults[.gitSignatureName] = gitSignatureName.isEmpty ? nil : gitSignatureName
Defaults[.gitSignatureEmail] = gitSignatureEmail.isEmpty ? nil : gitSignatureEmail SharedDefaults[.gitSignatureEmail] = gitSignatureEmail.isEmpty ? nil : gitSignatureEmail
} }
setGitSignatureText() setGitSignatureText()
} }

View file

@ -9,6 +9,7 @@
import UIKit import UIKit
import SafariServices import SafariServices
import MessageUI import MessageUI
import passKit
enum CellDataType { enum CellDataType {

View file

@ -8,6 +8,7 @@
import UIKit import UIKit
import ObjectiveGit import ObjectiveGit
import passKit
class CommitLogsTableViewController: UITableViewController { class CommitLogsTableViewController: UITableViewController {
var commits: [GTCommit] = [] var commits: [GTCommit] = []

View file

@ -7,7 +7,7 @@
// //
import UIKit import UIKit
import SwiftyUserDefaults import passKit
class EditPasswordTableViewController: PasswordEditorTableViewController { class EditPasswordTableViewController: PasswordEditorTableViewController {
override func viewDidLoad() { override func viewDidLoad() {
@ -18,7 +18,7 @@ class EditPasswordTableViewController: PasswordEditorTableViewController {
[[.type: PasswordEditorCellType.scanQRCodeCell], [[.type: PasswordEditorCellType.scanQRCodeCell],
[.type: PasswordEditorCellType.deletePasswordCell]] [.type: PasswordEditorCellType.deletePasswordCell]]
] ]
if let lengthSetting = Globals.passwordDefaultLength[Defaults[.passwordGeneratorFlavor]], if let lengthSetting = Globals.passwordDefaultLength[SharedDefaults[.passwordGeneratorFlavor]],
lengthSetting.max > lengthSetting.min { lengthSetting.max > lengthSetting.min {
tableData[1].append([.type: PasswordEditorCellType.passwordLengthCell, .title: "passwordlength"]) tableData[1].append([.type: PasswordEditorCellType.passwordLengthCell, .title: "passwordlength"])
} }

View file

@ -7,7 +7,7 @@
// //
import UIKit import UIKit
import SwiftyUserDefaults import passKit
class GeneralSettingsTableViewController: BasicStaticTableViewController { class GeneralSettingsTableViewController: BasicStaticTableViewController {
let passwordStore = PasswordStore.shared let passwordStore = PasswordStore.shared
@ -33,7 +33,7 @@ class GeneralSettingsTableViewController: BasicStaticTableViewController {
uiSwitch.onTintColor = Globals.blue uiSwitch.onTintColor = Globals.blue
uiSwitch.sizeToFit() uiSwitch.sizeToFit()
uiSwitch.addTarget(self, action: #selector(rememberPassphraseSwitchAction(_:)), for: UIControlEvents.valueChanged) uiSwitch.addTarget(self, action: #selector(rememberPassphraseSwitchAction(_:)), for: UIControlEvents.valueChanged)
uiSwitch.isOn = Defaults[.isRememberPassphraseOn] uiSwitch.isOn = SharedDefaults[.isRememberPassphraseOn]
return uiSwitch return uiSwitch
}() }()
@ -42,7 +42,7 @@ class GeneralSettingsTableViewController: BasicStaticTableViewController {
uiSwitch.onTintColor = Globals.blue uiSwitch.onTintColor = Globals.blue
uiSwitch.sizeToFit() uiSwitch.sizeToFit()
uiSwitch.addTarget(self, action: #selector(showFolderSwitchAction(_:)), for: UIControlEvents.valueChanged) uiSwitch.addTarget(self, action: #selector(showFolderSwitchAction(_:)), for: UIControlEvents.valueChanged)
uiSwitch.isOn = Defaults[.isShowFolderOn] uiSwitch.isOn = SharedDefaults[.isShowFolderOn]
return uiSwitch return uiSwitch
}() }()
@ -85,7 +85,7 @@ class GeneralSettingsTableViewController: BasicStaticTableViewController {
accessoryView.addSubview(hideUnknownSwitch) accessoryView.addSubview(hideUnknownSwitch)
cell.accessoryView = accessoryView cell.accessoryView = accessoryView
cell.selectionStyle = .none cell.selectionStyle = .none
hideUnknownSwitch.isOn = Defaults[.isHideUnknownOn] hideUnknownSwitch.isOn = SharedDefaults[.isHideUnknownOn]
case "Hide OTP Fields": case "Hide OTP Fields":
cell.accessoryType = .none cell.accessoryType = .none
let detailButton = UIButton(type: .detailDisclosure) let detailButton = UIButton(type: .detailDisclosure)
@ -97,7 +97,7 @@ class GeneralSettingsTableViewController: BasicStaticTableViewController {
accessoryView.addSubview(hideOTPSwitch) accessoryView.addSubview(hideOTPSwitch)
cell.accessoryView = accessoryView cell.accessoryView = accessoryView
cell.selectionStyle = .none cell.selectionStyle = .none
hideOTPSwitch.isOn = Defaults[.isHideOTPOn] hideOTPSwitch.isOn = SharedDefaults[.isHideOTPOn]
case "Remember Passphrase": case "Remember Passphrase":
cell.accessoryType = .none cell.accessoryType = .none
cell.selectionStyle = .none cell.selectionStyle = .none
@ -108,7 +108,7 @@ class GeneralSettingsTableViewController: BasicStaticTableViewController {
cell.accessoryView = showFolderSwitch cell.accessoryView = showFolderSwitch
case "Password Generator Flavor": case "Password Generator Flavor":
cell.accessoryType = .disclosureIndicator cell.accessoryType = .disclosureIndicator
cell.detailTextLabel?.text = Defaults[.passwordGeneratorFlavor] cell.detailTextLabel?.text = SharedDefaults[.passwordGeneratorFlavor]
default: break default: break
} }
return cell return cell
@ -127,7 +127,7 @@ class GeneralSettingsTableViewController: BasicStaticTableViewController {
let optionMenu = UIAlertController(title: nil, message: nil, preferredStyle: .actionSheet) let optionMenu = UIAlertController(title: nil, message: nil, preferredStyle: .actionSheet)
var randomFlavorActionTitle = "" var randomFlavorActionTitle = ""
var appleFlavorActionTitle = "" var appleFlavorActionTitle = ""
if Defaults[.passwordGeneratorFlavor] == "Random" { if SharedDefaults[.passwordGeneratorFlavor] == "Random" {
randomFlavorActionTitle = "✓ Random String" randomFlavorActionTitle = "✓ Random String"
appleFlavorActionTitle = "Apple's Keychain Style" appleFlavorActionTitle = "Apple's Keychain Style"
} else { } else {
@ -135,12 +135,12 @@ class GeneralSettingsTableViewController: BasicStaticTableViewController {
appleFlavorActionTitle = "✓ Apple's Keychain Style" appleFlavorActionTitle = "✓ Apple's Keychain Style"
} }
let randomFlavorAction = UIAlertAction(title: randomFlavorActionTitle, style: .default) { _ in let randomFlavorAction = UIAlertAction(title: randomFlavorActionTitle, style: .default) { _ in
Defaults[.passwordGeneratorFlavor] = "Random" SharedDefaults[.passwordGeneratorFlavor] = "Random"
sourceCell.detailTextLabel?.text = "Random" sourceCell.detailTextLabel?.text = "Random"
} }
let appleFlavorAction = UIAlertAction(title: appleFlavorActionTitle, style: .default) { _ in let appleFlavorAction = UIAlertAction(title: appleFlavorActionTitle, style: .default) { _ in
Defaults[.passwordGeneratorFlavor] = "Apple" SharedDefaults[.passwordGeneratorFlavor] = "Apple"
sourceCell.detailTextLabel?.text = "Apple" sourceCell.detailTextLabel?.text = "Apple"
} }
@ -167,24 +167,24 @@ class GeneralSettingsTableViewController: BasicStaticTableViewController {
} }
func hideUnknownSwitchAction(_ sender: Any?) { func hideUnknownSwitchAction(_ sender: Any?) {
Defaults[.isHideUnknownOn] = hideUnknownSwitch.isOn SharedDefaults[.isHideUnknownOn] = hideUnknownSwitch.isOn
NotificationCenter.default.post(name: .passwordDetailDisplaySettingChanged, object: nil) NotificationCenter.default.post(name: .passwordDetailDisplaySettingChanged, object: nil)
} }
func hideOTPSwitchAction(_ sender: Any?) { func hideOTPSwitchAction(_ sender: Any?) {
Defaults[.isHideOTPOn] = hideOTPSwitch.isOn SharedDefaults[.isHideOTPOn] = hideOTPSwitch.isOn
NotificationCenter.default.post(name: .passwordDetailDisplaySettingChanged, object: nil) NotificationCenter.default.post(name: .passwordDetailDisplaySettingChanged, object: nil)
} }
func rememberPassphraseSwitchAction(_ sender: Any?) { func rememberPassphraseSwitchAction(_ sender: Any?) {
Defaults[.isRememberPassphraseOn] = rememberPassphraseSwitch.isOn SharedDefaults[.isRememberPassphraseOn] = rememberPassphraseSwitch.isOn
if rememberPassphraseSwitch.isOn == false { if rememberPassphraseSwitch.isOn == false {
passwordStore.pgpKeyPassphrase = nil passwordStore.pgpKeyPassphrase = nil
} }
} }
func showFolderSwitchAction(_ sender: Any?) { func showFolderSwitchAction(_ sender: Any?) {
Defaults[.isShowFolderOn] = showFolderSwitch.isOn SharedDefaults[.isShowFolderOn] = showFolderSwitch.isOn
NotificationCenter.default.post(name: .passwordDisplaySettingChanged, object: nil) NotificationCenter.default.post(name: .passwordDisplaySettingChanged, object: nil)
} }

View file

@ -8,6 +8,7 @@
import UIKit import UIKit
import SwiftyUserDefaults import SwiftyUserDefaults
import passKit
class GitConfigSettingTableViewController: UITableViewController { class GitConfigSettingTableViewController: UITableViewController {
let passwordStore = PasswordStore.shared let passwordStore = PasswordStore.shared
@ -22,8 +23,8 @@ class GitConfigSettingTableViewController: UITableViewController {
let signature = passwordStore.gitSignatureForNow let signature = passwordStore.gitSignatureForNow
nameTextField.placeholder = signature.name nameTextField.placeholder = signature.name
emailTextField.placeholder = signature.email emailTextField.placeholder = signature.email
nameTextField.text = Defaults[.gitSignatureName] nameTextField.text = SharedDefaults[.gitSignatureName]
emailTextField.text = Defaults[.gitSignatureEmail] emailTextField.text = SharedDefaults[.gitSignatureEmail]
} }
override func shouldPerformSegue(withIdentifier identifier: String, sender: Any?) -> Bool { override func shouldPerformSegue(withIdentifier identifier: String, sender: Any?) -> Bool {

View file

@ -7,7 +7,7 @@
// //
import UIKit import UIKit
import SwiftyUserDefaults import passKit
class GitSSHKeyArmorSettingTableViewController: UITableViewController, UITextViewDelegate, QRScannerControllerDelegate { class GitSSHKeyArmorSettingTableViewController: UITableViewController, UITextViewDelegate, QRScannerControllerDelegate {
@IBOutlet weak var armorPrivateKeyTextView: UITextView! @IBOutlet weak var armorPrivateKeyTextView: UITextView!
@ -73,7 +73,7 @@ class GitSSHKeyArmorSettingTableViewController: UITableViewController, UITextVie
override func viewDidLoad() { override func viewDidLoad() {
super.viewDidLoad() super.viewDidLoad()
armorPrivateKeyTextView.text = Defaults[.gitSSHPrivateKeyArmor] armorPrivateKeyTextView.text = SharedDefaults[.gitSSHPrivateKeyArmor]
armorPrivateKeyTextView.delegate = self armorPrivateKeyTextView.delegate = self
scanPrivateKeyCell?.textLabel?.text = "Scan Private Key QR Codes" scanPrivateKeyCell?.textLabel?.text = "Scan Private Key QR Codes"
@ -83,13 +83,13 @@ class GitSSHKeyArmorSettingTableViewController: UITableViewController, UITextVie
} }
@IBAction func doneButtonTapped(_ sender: Any) { @IBAction func doneButtonTapped(_ sender: Any) {
Defaults[.gitSSHPrivateKeyArmor] = armorPrivateKeyTextView.text SharedDefaults[.gitSSHPrivateKeyArmor] = armorPrivateKeyTextView.text
do { do {
try passwordStore.initGitSSHKey(with: armorPrivateKeyTextView.text, .secret) try passwordStore.initGitSSHKey(with: armorPrivateKeyTextView.text)
} catch { } catch {
Utils.alert(title: "Cannot Save", message: "Cannot Save SSH Key", controller: self, completion: nil) Utils.alert(title: "Cannot Save", message: "Cannot Save SSH Key", controller: self, completion: nil)
} }
Defaults[.gitSSHKeySource] = "armor" SharedDefaults[.gitSSHKeySource] = "armor"
self.navigationController!.popViewController(animated: true) self.navigationController!.popViewController(animated: true)
} }

View file

@ -7,8 +7,8 @@
// //
import UIKit import UIKit
import SwiftyUserDefaults
import SVProgressHUD import SVProgressHUD
import passKit
class GitServerSettingTableViewController: UITableViewController { class GitServerSettingTableViewController: UITableViewController {
@ -19,7 +19,7 @@ class GitServerSettingTableViewController: UITableViewController {
let passwordStore = PasswordStore.shared let passwordStore = PasswordStore.shared
var sshLabel: UILabel? = nil var sshLabel: UILabel? = nil
var authenticationMethod = Defaults[.gitAuthenticationMethod] ?? "Password" var authenticationMethod = SharedDefaults[.gitAuthenticationMethod] ?? "Password"
private func checkAuthenticationMethod(method: String) { private func checkAuthenticationMethod(method: String) {
let passwordCheckView = authPasswordCell.viewWithTag(1001)! let passwordCheckView = authPasswordCell.viewWithTag(1001)!
@ -47,10 +47,10 @@ class GitServerSettingTableViewController: UITableViewController {
} }
override func viewDidLoad() { override func viewDidLoad() {
super.viewDidLoad() super.viewDidLoad()
if let url = Defaults[.gitURL] { if let url = SharedDefaults[.gitURL] {
gitURLTextField.text = url.absoluteString gitURLTextField.text = url.absoluteString
} }
usernameTextField.text = Defaults[.gitUsername] usernameTextField.text = SharedDefaults[.gitUsername]
sshLabel = authSSHKeyCell.subviews[0].subviews[0] as? UILabel sshLabel = authSSHKeyCell.subviews[0].subviews[0] as? UILabel
checkAuthenticationMethod(method: authenticationMethod) checkAuthenticationMethod(method: authenticationMethod)
authSSHKeyCell.accessoryType = .detailButton authSSHKeyCell.accessoryType = .detailButton
@ -110,9 +110,9 @@ class GitServerSettingTableViewController: UITableViewController {
} }
}) })
DispatchQueue.main.async { DispatchQueue.main.async {
Defaults[.gitURL] = URL(string: gitRepostiroyURL) SharedDefaults[.gitURL] = URL(string: gitRepostiroyURL)
Defaults[.gitUsername] = username SharedDefaults[.gitUsername] = username
Defaults[.gitAuthenticationMethod] = auth SharedDefaults[.gitAuthenticationMethod] = auth
SVProgressHUD.showSuccess(withStatus: "Done") SVProgressHUD.showSuccess(withStatus: "Done")
SVProgressHUD.dismiss(withDelay: 1) SVProgressHUD.dismiss(withDelay: 1)
self.performSegue(withIdentifier: "saveGitServerSettingSegue", sender: self) self.performSegue(withIdentifier: "saveGitServerSettingSegue", sender: self)
@ -167,11 +167,11 @@ class GitServerSettingTableViewController: UITableViewController {
var armorActionTitle = "ASCII-Armor Encrypted Key" var armorActionTitle = "ASCII-Armor Encrypted Key"
var fileActionTitle = "Use Imported Keys" var fileActionTitle = "Use Imported Keys"
if Defaults[.gitSSHKeySource] == "url" { if SharedDefaults[.gitSSHKeySource] == "url" {
urlActionTitle = "\(urlActionTitle)" urlActionTitle = "\(urlActionTitle)"
} else if Defaults[.gitSSHKeySource] == "armor" { } else if SharedDefaults[.gitSSHKeySource] == "armor" {
armorActionTitle = "\(armorActionTitle)" armorActionTitle = "\(armorActionTitle)"
} else if Defaults[.gitSSHKeySource] == "file" { } else if SharedDefaults[.gitSSHKeySource] == "file" {
fileActionTitle = "\(fileActionTitle)" fileActionTitle = "\(fileActionTitle)"
} }
let urlAction = UIAlertAction(title: urlActionTitle, style: .default) { _ in let urlAction = UIAlertAction(title: urlActionTitle, style: .default) { _ in
@ -187,7 +187,7 @@ class GitServerSettingTableViewController: UITableViewController {
if passwordStore.gitSSHKeyExists() { if passwordStore.gitSSHKeyExists() {
// might keys updated via iTunes, or downloaded/pasted inside the app // might keys updated via iTunes, or downloaded/pasted inside the app
let fileAction = UIAlertAction(title: fileActionTitle, style: .default) { _ in let fileAction = UIAlertAction(title: fileActionTitle, style: .default) { _ in
Defaults[.gitSSHKeySource] = "file" SharedDefaults[.gitSSHKeySource] = "file"
} }
optionMenu.addAction(fileAction) optionMenu.addAction(fileAction)
} else { } else {
@ -199,10 +199,10 @@ class GitServerSettingTableViewController: UITableViewController {
optionMenu.addAction(fileAction) optionMenu.addAction(fileAction)
} }
if Defaults[.gitSSHKeySource] != nil { if SharedDefaults[.gitSSHKeySource] != nil {
let deleteAction = UIAlertAction(title: "Remove Git SSH Keys", style: .destructive) { _ in let deleteAction = UIAlertAction(title: "Remove Git SSH Keys", style: .destructive) { _ in
self.passwordStore.removeGitSSHKeys() self.passwordStore.removeGitSSHKeys()
Defaults[.gitSSHKeySource] = nil SharedDefaults[.gitSSHKeySource] = nil
if let sshLabel = self.sshLabel { if let sshLabel = self.sshLabel {
sshLabel.isEnabled = false sshLabel.isEnabled = false
self.checkAuthenticationMethod(method: "Password") self.checkAuthenticationMethod(method: "Password")

View file

@ -8,6 +8,7 @@
import UIKit import UIKit
import AVFoundation import AVFoundation
import passKit
class OTPScannerController: QRScannerController { class OTPScannerController: QRScannerController {

View file

@ -7,7 +7,7 @@
// //
import UIKit import UIKit
import SwiftyUserDefaults import passKit
class PGPKeyArmorSettingTableViewController: UITableViewController, UITextViewDelegate, QRScannerControllerDelegate { class PGPKeyArmorSettingTableViewController: UITableViewController, UITextViewDelegate, QRScannerControllerDelegate {
@IBOutlet weak var armorPublicKeyTextView: UITextView! @IBOutlet weak var armorPublicKeyTextView: UITextView!
@ -92,8 +92,8 @@ class PGPKeyArmorSettingTableViewController: UITableViewController, UITextViewDe
override func viewDidLoad() { override func viewDidLoad() {
super.viewDidLoad() super.viewDidLoad()
armorPublicKeyTextView.text = Defaults[.pgpPublicKeyArmor] armorPublicKeyTextView.text = SharedDefaults[.pgpPublicKeyArmor]
armorPrivateKeyTextView.text = Defaults[.pgpPrivateKeyArmor] armorPrivateKeyTextView.text = SharedDefaults[.pgpPrivateKeyArmor]
pgpPassphrase = passwordStore.pgpKeyPassphrase pgpPassphrase = passwordStore.pgpKeyPassphrase
scanPublicKeyCell?.textLabel?.text = "Scan Public Key QR Codes" scanPublicKeyCell?.textLabel?.text = "Scan Public Key QR Codes"
@ -126,7 +126,7 @@ class PGPKeyArmorSettingTableViewController: UITableViewController, UITextViewDe
// no // no
savePassphraseAlert.addAction(UIAlertAction(title: "No", style: UIAlertActionStyle.default) { _ in savePassphraseAlert.addAction(UIAlertAction(title: "No", style: UIAlertActionStyle.default) { _ in
self.pgpPassphrase = nil self.pgpPassphrase = nil
Defaults[.isRememberPassphraseOn] = false SharedDefaults[.isRememberPassphraseOn] = false
self.performSegue(withIdentifier: "savePGPKeySegue", sender: self) self.performSegue(withIdentifier: "savePGPKeySegue", sender: self)
}) })
// yes // yes
@ -135,7 +135,7 @@ class PGPKeyArmorSettingTableViewController: UITableViewController, UITextViewDe
let alert = UIAlertController(title: "Passphrase", message: "Please fill in the passphrase of your PGP secret key.", preferredStyle: UIAlertControllerStyle.alert) let alert = UIAlertController(title: "Passphrase", message: "Please fill in the passphrase of your PGP secret key.", preferredStyle: UIAlertControllerStyle.alert)
alert.addAction(UIAlertAction(title: "OK", style: UIAlertActionStyle.default, handler: {_ in alert.addAction(UIAlertAction(title: "OK", style: UIAlertActionStyle.default, handler: {_ in
self.pgpPassphrase = alert.textFields?.first?.text self.pgpPassphrase = alert.textFields?.first?.text
Defaults[.isRememberPassphraseOn] = true SharedDefaults[.isRememberPassphraseOn] = true
self.performSegue(withIdentifier: "savePGPKeySegue", sender: self) self.performSegue(withIdentifier: "savePGPKeySegue", sender: self)
})) }))
alert.addTextField(configurationHandler: {(textField: UITextField!) in alert.addTextField(configurationHandler: {(textField: UITextField!) in

View file

@ -7,7 +7,7 @@
// //
import UIKit import UIKit
import SwiftyUserDefaults import passKit
class PGPKeySettingTableViewController: UITableViewController { class PGPKeySettingTableViewController: UITableViewController {
@ -19,8 +19,8 @@ class PGPKeySettingTableViewController: UITableViewController {
override func viewDidLoad() { override func viewDidLoad() {
super.viewDidLoad() super.viewDidLoad()
tableView.rowHeight = UITableViewAutomaticDimension tableView.rowHeight = UITableViewAutomaticDimension
pgpPublicKeyURLTextField.text = Defaults[.pgpPublicKeyURL]?.absoluteString pgpPublicKeyURLTextField.text = SharedDefaults[.pgpPublicKeyURL]?.absoluteString
pgpPrivateKeyURLTextField.text = Defaults[.pgpPrivateKeyURL]?.absoluteString pgpPrivateKeyURLTextField.text = SharedDefaults[.pgpPrivateKeyURL]?.absoluteString
pgpPassphrase = passwordStore.pgpKeyPassphrase pgpPassphrase = passwordStore.pgpKeyPassphrase
} }
@ -51,7 +51,7 @@ class PGPKeySettingTableViewController: UITableViewController {
// no // no
savePassphraseAlert.addAction(UIAlertAction(title: "No", style: UIAlertActionStyle.default) { _ in savePassphraseAlert.addAction(UIAlertAction(title: "No", style: UIAlertActionStyle.default) { _ in
self.pgpPassphrase = nil self.pgpPassphrase = nil
Defaults[.isRememberPassphraseOn] = false SharedDefaults[.isRememberPassphraseOn] = false
self.performSegue(withIdentifier: "savePGPKeySegue", sender: self) self.performSegue(withIdentifier: "savePGPKeySegue", sender: self)
}) })
// yes // yes
@ -60,7 +60,7 @@ class PGPKeySettingTableViewController: UITableViewController {
let alert = UIAlertController(title: "Passphrase", message: "Please fill in the passphrase of your PGP secret key.", preferredStyle: UIAlertControllerStyle.alert) let alert = UIAlertController(title: "Passphrase", message: "Please fill in the passphrase of your PGP secret key.", preferredStyle: UIAlertControllerStyle.alert)
alert.addAction(UIAlertAction(title: "OK", style: UIAlertActionStyle.default, handler: {_ in alert.addAction(UIAlertAction(title: "OK", style: UIAlertActionStyle.default, handler: {_ in
self.pgpPassphrase = alert.textFields?.first?.text self.pgpPassphrase = alert.textFields?.first?.text
Defaults[.isRememberPassphraseOn] = true SharedDefaults[.isRememberPassphraseOn] = true
self.performSegue(withIdentifier: "savePGPKeySegue", sender: self) self.performSegue(withIdentifier: "savePGPKeySegue", sender: self)
})) }))
alert.addTextField(configurationHandler: {(textField: UITextField!) in alert.addTextField(configurationHandler: {(textField: UITextField!) in

View file

@ -8,8 +8,8 @@
import UIKit import UIKit
import FavIcon import FavIcon
import SwiftyUserDefaults
import SVProgressHUD import SVProgressHUD
import passKit
class PasswordDetailTableViewController: UITableViewController, UIGestureRecognizerDelegate { class PasswordDetailTableViewController: UITableViewController, UIGestureRecognizerDelegate {
var passwordEntity: PasswordEntity? var passwordEntity: PasswordEntity?
@ -134,7 +134,7 @@ class PasswordDetailTableViewController: UITableViewController, UIGestureRecogni
self.present(alert, animated: true, completion: nil) self.present(alert, animated: true, completion: nil)
} }
let _ = sem.wait(timeout: DispatchTime.distantFuture) let _ = sem.wait(timeout: DispatchTime.distantFuture)
if Defaults[.isRememberPassphraseOn] { if SharedDefaults[.isRememberPassphraseOn] {
self.passwordStore.pgpKeyPassphrase = passphrase self.passwordStore.pgpKeyPassphrase = passphrase
} }
return passphrase return passphrase
@ -281,8 +281,8 @@ class PasswordDetailTableViewController: UITableViewController, UIGestureRecogni
let filteredAdditionKeys = password.additionKeys.filter { let filteredAdditionKeys = password.additionKeys.filter {
$0.lowercased() != "username" && $0.lowercased() != "username" &&
$0.lowercased() != "password" && $0.lowercased() != "password" &&
(!$0.hasPrefix("unknown") || !Defaults[.isHideUnknownOn]) && (!$0.hasPrefix("unknown") || !SharedDefaults[.isHideUnknownOn]) &&
(!Password.otpKeywords.contains($0) || !Defaults[.isHideOTPOn]) } (!Password.otpKeywords.contains($0) || !SharedDefaults[.isHideOTPOn]) }
if filteredAdditionKeys.count > 0 { if filteredAdditionKeys.count > 0 {
section = TableSection(type: .addition, header: "additions") section = TableSection(type: .addition, header: "additions")

View file

@ -7,8 +7,8 @@
// //
import UIKit import UIKit
import SwiftyUserDefaults
import OneTimePassword import OneTimePassword
import passKit
enum PasswordEditorCellType { enum PasswordEditorCellType {
case nameCell, fillPasswordCell, passwordLengthCell, additionsCell, deletePasswordCell, scanQRCodeCell case nameCell, fillPasswordCell, passwordLengthCell, additionsCell, deletePasswordCell, scanQRCodeCell
@ -95,7 +95,7 @@ class PasswordEditorTableViewController: UITableViewController, FillPasswordTabl
return fillPasswordCell! return fillPasswordCell!
case .passwordLengthCell: case .passwordLengthCell:
passwordLengthCell = tableView.dequeueReusableCell(withIdentifier: "passwordLengthCell", for: indexPath) as? SliderTableViewCell passwordLengthCell = tableView.dequeueReusableCell(withIdentifier: "passwordLengthCell", for: indexPath) as? SliderTableViewCell
let lengthSetting = Globals.passwordDefaultLength[Defaults[.passwordGeneratorFlavor]] ?? let lengthSetting = Globals.passwordDefaultLength[SharedDefaults[.passwordGeneratorFlavor]] ??
Globals.passwordDefaultLength["Random"] Globals.passwordDefaultLength["Random"]
passwordLengthCell?.reset(title: "Length", passwordLengthCell?.reset(title: "Length",
minimumValue: lengthSetting?.min ?? 0, minimumValue: lengthSetting?.min ?? 0,

View file

@ -8,8 +8,7 @@
import UIKit import UIKit
import SVProgressHUD import SVProgressHUD
import SwiftyUserDefaults import passKit
import PasscodeLock
fileprivate class PasswordsTableEntry : NSObject { fileprivate class PasswordsTableEntry : NSObject {
var title: String var title: String
@ -87,7 +86,7 @@ class PasswordsViewController: UIViewController, UITableViewDataSource, UITableV
filteredPasswordsTableEntries.removeAll() filteredPasswordsTableEntries.removeAll()
var passwordEntities = [PasswordEntity]() var passwordEntities = [PasswordEntity]()
var passwordAllEntities = [PasswordEntity]() var passwordAllEntities = [PasswordEntity]()
if Defaults[.isShowFolderOn] { if SharedDefaults[.isShowFolderOn] {
passwordEntities = self.passwordStore.fetchPasswordEntityCoreData(parent: parent) passwordEntities = self.passwordStore.fetchPasswordEntityCoreData(parent: parent)
} else { } else {
passwordEntities = self.passwordStore.fetchPasswordEntityCoreData(withDir: false) passwordEntities = self.passwordStore.fetchPasswordEntityCoreData(withDir: false)
@ -139,12 +138,12 @@ class PasswordsViewController: UIViewController, UITableViewDataSource, UITableV
SVProgressHUD.show(withStatus: "Sync Password Store") SVProgressHUD.show(withStatus: "Sync Password Store")
let numberOfLocalCommits = self.passwordStore.numberOfLocalCommits() let numberOfLocalCommits = self.passwordStore.numberOfLocalCommits()
var gitCredential: GitCredential var gitCredential: GitCredential
if Defaults[.gitAuthenticationMethod] == "Password" { if SharedDefaults[.gitAuthenticationMethod] == "Password" {
gitCredential = GitCredential(credential: GitCredential.Credential.http(userName: Defaults[.gitUsername]!, controller: self)) gitCredential = GitCredential(credential: GitCredential.Credential.http(userName: SharedDefaults[.gitUsername]!, controller: self))
} else { } else {
gitCredential = GitCredential( gitCredential = GitCredential(
credential: GitCredential.Credential.ssh( credential: GitCredential.Credential.ssh(
userName: Defaults[.gitUsername]!, userName: SharedDefaults[.gitUsername]!,
privateKeyFile: Globals.gitSSHPrivateKeyURL, privateKeyFile: Globals.gitSSHPrivateKeyURL,
controller: self controller: self
) )
@ -184,7 +183,7 @@ class PasswordsViewController: UIViewController, UITableViewDataSource, UITableV
override func viewDidAppear(_ animated: Bool) { override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated) super.viewDidAppear(animated)
if Defaults[.isShowFolderOn] { if SharedDefaults[.isShowFolderOn] {
searchController.searchBar.scopeButtonTitles = ["Current", "All"] searchController.searchBar.scopeButtonTitles = ["Current", "All"]
} else { } else {
searchController.searchBar.scopeButtonTitles = nil searchController.searchBar.scopeButtonTitles = nil
@ -238,7 +237,7 @@ class PasswordsViewController: UIViewController, UITableViewDataSource, UITableV
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let longPressGestureRecognizer = UILongPressGestureRecognizer(target: self, action: #selector(longPressAction(_:))) let longPressGestureRecognizer = UILongPressGestureRecognizer(target: self, action: #selector(longPressAction(_:)))
longPressGestureRecognizer.minimumPressDuration = 0.6 longPressGestureRecognizer.minimumPressDuration = 0.6
if Defaults[.isShowFolderOn] && searchController.searchBar.selectedScopeButtonIndex == 0{ if SharedDefaults[.isShowFolderOn] && searchController.searchBar.selectedScopeButtonIndex == 0{
let cell = tableView.dequeueReusableCell(withIdentifier: "passwordTableViewCell", for: indexPath) let cell = tableView.dequeueReusableCell(withIdentifier: "passwordTableViewCell", for: indexPath)
let entry = getPasswordEntry(by: indexPath) let entry = getPasswordEntry(by: indexPath)
@ -296,7 +295,7 @@ class PasswordsViewController: UIViewController, UITableViewDataSource, UITableV
} }
func backAction(_ sender: Any?) { func backAction(_ sender: Any?) {
guard Defaults[.isShowFolderOn] else { return } guard SharedDefaults[.isShowFolderOn] else { return }
var anim: CATransition? = transitionFromLeft var anim: CATransition? = transitionFromLeft
if parentPasswordEntity == nil { if parentPasswordEntity == nil {
anim = nil anim = nil
@ -363,7 +362,7 @@ class PasswordsViewController: UIViewController, UITableViewDataSource, UITableV
// bring back // bring back
SVProgressHUD.show(withStatus: "Decrypting") SVProgressHUD.show(withStatus: "Decrypting")
} }
if Defaults[.isRememberPassphraseOn] { if SharedDefaults[.isRememberPassphraseOn] {
self.passwordStore.pgpKeyPassphrase = passphrase self.passwordStore.pgpKeyPassphrase = passphrase
} }
return passphrase return passphrase

View file

@ -7,6 +7,7 @@
// //
import UIKit import UIKit
import passKit
class RawPasswordViewController: UIViewController { class RawPasswordViewController: UIViewController {

View file

@ -7,8 +7,8 @@
// //
import UIKit import UIKit
import SwiftyUserDefaults
import SVProgressHUD import SVProgressHUD
import passKit
class SSHKeySettingTableViewController: UITableViewController { class SSHKeySettingTableViewController: UITableViewController {
@ -17,7 +17,7 @@ class SSHKeySettingTableViewController: UITableViewController {
override func viewDidLoad() { override func viewDidLoad() {
super.viewDidLoad() super.viewDidLoad()
privateKeyURLTextField.text = Defaults[.gitSSHPrivateKeyURL]?.absoluteString privateKeyURLTextField.text = SharedDefaults[.gitSSHPrivateKeyURL]?.absoluteString
} }
@ -27,14 +27,14 @@ class SSHKeySettingTableViewController: UITableViewController {
return return
} }
Defaults[.gitSSHPrivateKeyURL] = privateKeyURL SharedDefaults[.gitSSHPrivateKeyURL] = privateKeyURL
do { do {
try Data(contentsOf: privateKeyURL).write(to: URL(fileURLWithPath: Globals.gitSSHPrivateKeyPath), options: .atomic) try Data(contentsOf: privateKeyURL).write(to: URL(fileURLWithPath: Globals.gitSSHPrivateKeyPath), options: .atomic)
} catch { } catch {
Utils.alert(title: "Error", message: error.localizedDescription, controller: self, completion: nil) Utils.alert(title: "Error", message: error.localizedDescription, controller: self, completion: nil)
} }
Defaults[.gitSSHKeySource] = "url" SharedDefaults[.gitSSHKeySource] = "url"
self.navigationController!.popViewController(animated: true) self.navigationController!.popViewController(animated: true)
} }

View file

@ -9,9 +9,9 @@
import UIKit import UIKit
import SVProgressHUD import SVProgressHUD
import CoreData import CoreData
import SwiftyUserDefaults
import PasscodeLock import PasscodeLock
import LocalAuthentication import LocalAuthentication
import passKit
class SettingsTableViewController: UITableViewController { class SettingsTableViewController: UITableViewController {
@ -27,26 +27,27 @@ class SettingsTableViewController: UITableViewController {
@IBOutlet weak var passcodeTableViewCell: UITableViewCell! @IBOutlet weak var passcodeTableViewCell: UITableViewCell!
@IBOutlet weak var passwordRepositoryTableViewCell: UITableViewCell! @IBOutlet weak var passwordRepositoryTableViewCell: UITableViewCell!
let passwordStore = PasswordStore.shared let passwordStore = PasswordStore.shared
var passcodeLockConfig = PasscodeLockConfiguration.shared
@IBAction func cancelPGPKey(segue: UIStoryboardSegue) { @IBAction func cancelPGPKey(segue: UIStoryboardSegue) {
} }
@IBAction func savePGPKey(segue: UIStoryboardSegue) { @IBAction func savePGPKey(segue: UIStoryboardSegue) {
if let controller = segue.source as? PGPKeySettingTableViewController { if let controller = segue.source as? PGPKeySettingTableViewController {
Defaults[.pgpPrivateKeyURL] = URL(string: controller.pgpPrivateKeyURLTextField.text!) SharedDefaults[.pgpPrivateKeyURL] = URL(string: controller.pgpPrivateKeyURLTextField.text!)
Defaults[.pgpPublicKeyURL] = URL(string: controller.pgpPublicKeyURLTextField.text!) SharedDefaults[.pgpPublicKeyURL] = URL(string: controller.pgpPublicKeyURLTextField.text!)
if Defaults[.isRememberPassphraseOn] { if SharedDefaults[.isRememberPassphraseOn] {
self.passwordStore.pgpKeyPassphrase = controller.pgpPassphrase self.passwordStore.pgpKeyPassphrase = controller.pgpPassphrase
} }
Defaults[.pgpKeySource] = "url" SharedDefaults[.pgpKeySource] = "url"
SVProgressHUD.setDefaultMaskType(.black) SVProgressHUD.setDefaultMaskType(.black)
SVProgressHUD.setDefaultStyle(.light) SVProgressHUD.setDefaultStyle(.light)
SVProgressHUD.show(withStatus: "Fetching PGP Key") SVProgressHUD.show(withStatus: "Fetching PGP Key")
DispatchQueue.global(qos: .userInitiated).async { [unowned self] in DispatchQueue.global(qos: .userInitiated).async { [unowned self] in
do { do {
try self.passwordStore.initPGPKey(from: Defaults[.pgpPublicKeyURL]!, keyType: .public) try self.passwordStore.initPGPKey(from: SharedDefaults[.pgpPublicKeyURL]!, keyType: .public)
try self.passwordStore.initPGPKey(from: Defaults[.pgpPrivateKeyURL]!, keyType: .secret) try self.passwordStore.initPGPKey(from: SharedDefaults[.pgpPrivateKeyURL]!, keyType: .secret)
DispatchQueue.main.async { DispatchQueue.main.async {
self.pgpKeyTableViewCell.detailTextLabel?.text = self.passwordStore.pgpKeyID self.pgpKeyTableViewCell.detailTextLabel?.text = self.passwordStore.pgpKeyID
SVProgressHUD.showSuccess(withStatus: "Success") SVProgressHUD.showSuccess(withStatus: "Success")
@ -62,13 +63,13 @@ class SettingsTableViewController: UITableViewController {
} }
} else if let controller = segue.source as? PGPKeyArmorSettingTableViewController { } else if let controller = segue.source as? PGPKeyArmorSettingTableViewController {
Defaults[.pgpKeySource] = "armor" SharedDefaults[.pgpKeySource] = "armor"
if Defaults[.isRememberPassphraseOn] { if SharedDefaults[.isRememberPassphraseOn] {
self.passwordStore.pgpKeyPassphrase = controller.pgpPassphrase self.passwordStore.pgpKeyPassphrase = controller.pgpPassphrase
} }
Defaults[.pgpPublicKeyArmor] = controller.armorPublicKeyTextView.text! SharedDefaults[.pgpPublicKeyArmor] = controller.armorPublicKeyTextView.text!
Defaults[.pgpPrivateKeyArmor] = controller.armorPrivateKeyTextView.text! SharedDefaults[.pgpPrivateKeyArmor] = controller.armorPrivateKeyTextView.text!
SVProgressHUD.setDefaultMaskType(.black) SVProgressHUD.setDefaultMaskType(.black)
SVProgressHUD.setDefaultStyle(.light) SVProgressHUD.setDefaultStyle(.light)
@ -94,7 +95,7 @@ class SettingsTableViewController: UITableViewController {
private func saveImportedPGPKey() { private func saveImportedPGPKey() {
// load keys // load keys
Defaults[.pgpKeySource] = "file" SharedDefaults[.pgpKeySource] = "file"
SVProgressHUD.setDefaultMaskType(.black) SVProgressHUD.setDefaultMaskType(.black)
SVProgressHUD.setDefaultStyle(.light) SVProgressHUD.setDefaultStyle(.light)
@ -120,7 +121,7 @@ class SettingsTableViewController: UITableViewController {
} }
@IBAction func saveGitServerSetting(segue: UIStoryboardSegue) { @IBAction func saveGitServerSetting(segue: UIStoryboardSegue) {
self.passwordRepositoryTableViewCell.detailTextLabel?.text = Defaults[.gitURL]?.host self.passwordRepositoryTableViewCell.detailTextLabel?.text = SharedDefaults[.gitURL]?.host
} }
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
@ -138,7 +139,7 @@ class SettingsTableViewController: UITableViewController {
override func viewDidLoad() { override func viewDidLoad() {
super.viewDidLoad() super.viewDidLoad()
NotificationCenter.default.addObserver(self, selector: #selector(SettingsTableViewController.actOnPasswordStoreErasedNotification), name: .passwordStoreErased, object: nil) NotificationCenter.default.addObserver(self, selector: #selector(SettingsTableViewController.actOnPasswordStoreErasedNotification), name: .passwordStoreErased, object: nil)
self.passwordRepositoryTableViewCell.detailTextLabel?.text = Defaults[.gitURL]?.host self.passwordRepositoryTableViewCell.detailTextLabel?.text = SharedDefaults[.gitURL]?.host
touchIDTableViewCell.accessoryView = touchIDSwitch touchIDTableViewCell.accessoryView = touchIDSwitch
setPGPKeyTableViewCellDetailText() setPGPKeyTableViewCellDetailText()
setPasswordRepositoryTableViewCellDetailText() setPasswordRepositoryTableViewCellDetailText()
@ -170,13 +171,13 @@ class SettingsTableViewController: UITableViewController {
private func setPasscodeLockTouchIDCells() { private func setPasscodeLockTouchIDCells() {
if PasscodeLockRepository().hasPasscode { if PasscodeLockRepository().hasPasscode {
self.passcodeTableViewCell.detailTextLabel?.text = "On" self.passcodeTableViewCell.detailTextLabel?.text = "On"
Globals.passcodeConfiguration.isTouchIDAllowed = true passcodeLockConfig.isTouchIDAllowed = true
touchIDSwitch.isOn = Defaults[.isTouchIDOn] touchIDSwitch.isOn = SharedDefaults[.isTouchIDOn]
} else { } else {
self.passcodeTableViewCell.detailTextLabel?.text = "Off" self.passcodeTableViewCell.detailTextLabel?.text = "Off"
Globals.passcodeConfiguration.isTouchIDAllowed = false passcodeLockConfig.isTouchIDAllowed = false
Defaults[.isTouchIDOn] = false SharedDefaults[.isTouchIDOn] = false
touchIDSwitch.isOn = Defaults[.isTouchIDOn] touchIDSwitch.isOn = SharedDefaults[.isTouchIDOn]
} }
} }
@ -189,10 +190,10 @@ class SettingsTableViewController: UITableViewController {
} }
private func setPasswordRepositoryTableViewCellDetailText() { private func setPasswordRepositoryTableViewCellDetailText() {
if Defaults[.gitURL] == nil { if SharedDefaults[.gitURL] == nil {
passwordRepositoryTableViewCell.detailTextLabel?.text = "Not Set" passwordRepositoryTableViewCell.detailTextLabel?.text = "Not Set"
} else { } else {
passwordRepositoryTableViewCell.detailTextLabel?.text = Defaults[.gitURL]!.host passwordRepositoryTableViewCell.detailTextLabel?.text = SharedDefaults[.gitURL]!.host
} }
} }
@ -202,12 +203,12 @@ class SettingsTableViewController: UITableViewController {
setPasscodeLockTouchIDCells() setPasscodeLockTouchIDCells()
let appDelegate = UIApplication.shared.delegate as! AppDelegate let appDelegate = UIApplication.shared.delegate as! AppDelegate
appDelegate.passcodeLockPresenter = PasscodeLockPresenter(mainWindow: appDelegate.window, configuration: Globals.passcodeConfiguration) appDelegate.passcodeLockPresenter = PasscodeLockPresenter(mainWindow: appDelegate.window, configuration: passcodeLockConfig)
} }
override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) { override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
if tableView.cellForRow(at: indexPath) == passcodeTableViewCell { if tableView.cellForRow(at: indexPath) == passcodeTableViewCell {
if Defaults[.passcodeKey] != nil{ if SharedDefaults[.passcodeKey] != nil{
showPasscodeActionSheet() showPasscodeActionSheet()
} else { } else {
setPasscodeLock() setPasscodeLock()
@ -219,17 +220,17 @@ class SettingsTableViewController: UITableViewController {
} }
func touchIDSwitchAction(uiSwitch: UISwitch) { func touchIDSwitchAction(uiSwitch: UISwitch) {
if !Globals.passcodeConfiguration.isTouchIDAllowed || !isTouchIDEnabled() { if !passcodeLockConfig.isTouchIDAllowed || !isTouchIDEnabled() {
// switch off // switch off
DispatchQueue.main.asyncAfter(deadline: .now() + .milliseconds(500)) { DispatchQueue.main.asyncAfter(deadline: .now() + .milliseconds(500)) {
uiSwitch.isOn = Defaults[.isTouchIDOn] // false uiSwitch.isOn = SharedDefaults[.isTouchIDOn] // false
Utils.alert(title: "Notice", message: "Please enable Touch ID and set the passcode lock first.", controller: self, completion: nil) Utils.alert(title: "Notice", message: "Please enable Touch ID and set the passcode lock first.", controller: self, completion: nil)
} }
} else { } else {
Defaults[.isTouchIDOn] = uiSwitch.isOn SharedDefaults[.isTouchIDOn] = uiSwitch.isOn
} }
let appDelegate = UIApplication.shared.delegate as! AppDelegate let appDelegate = UIApplication.shared.delegate as! AppDelegate
appDelegate.passcodeLockPresenter = PasscodeLockPresenter(mainWindow: appDelegate.window, configuration: Globals.passcodeConfiguration) appDelegate.passcodeLockPresenter = PasscodeLockPresenter(mainWindow: appDelegate.window, configuration: passcodeLockConfig)
} }
func showPGPKeyActionSheet() { func showPGPKeyActionSheet() {
@ -238,11 +239,11 @@ class SettingsTableViewController: UITableViewController {
var armorActionTitle = "ASCII-Armor Encrypted Key" var armorActionTitle = "ASCII-Armor Encrypted Key"
var fileActionTitle = "Use Imported Keys" var fileActionTitle = "Use Imported Keys"
if Defaults[.pgpKeySource] == "url" { if SharedDefaults[.pgpKeySource] == "url" {
urlActionTitle = "\(urlActionTitle)" urlActionTitle = "\(urlActionTitle)"
} else if Defaults[.pgpKeySource] == "armor" { } else if SharedDefaults[.pgpKeySource] == "armor" {
armorActionTitle = "\(armorActionTitle)" armorActionTitle = "\(armorActionTitle)"
} else if Defaults[.pgpKeySource] == "file" { } else if SharedDefaults[.pgpKeySource] == "file" {
fileActionTitle = "\(fileActionTitle)" fileActionTitle = "\(fileActionTitle)"
} }
let urlAction = UIAlertAction(title: urlActionTitle, style: .default) { _ in let urlAction = UIAlertAction(title: urlActionTitle, style: .default) { _ in
@ -262,7 +263,7 @@ class SettingsTableViewController: UITableViewController {
// no // no
savePassphraseAlert.addAction(UIAlertAction(title: "No", style: UIAlertActionStyle.default) { _ in savePassphraseAlert.addAction(UIAlertAction(title: "No", style: UIAlertActionStyle.default) { _ in
self.passwordStore.pgpKeyPassphrase = nil self.passwordStore.pgpKeyPassphrase = nil
Defaults[.isRememberPassphraseOn] = false SharedDefaults[.isRememberPassphraseOn] = false
self.saveImportedPGPKey() self.saveImportedPGPKey()
}) })
// yes // yes
@ -271,7 +272,7 @@ class SettingsTableViewController: UITableViewController {
let alert = UIAlertController(title: "Passphrase", message: "Please fill in the passphrase of your PGP secret key.", preferredStyle: UIAlertControllerStyle.alert) let alert = UIAlertController(title: "Passphrase", message: "Please fill in the passphrase of your PGP secret key.", preferredStyle: UIAlertControllerStyle.alert)
alert.addAction(UIAlertAction(title: "OK", style: UIAlertActionStyle.default, handler: {_ in alert.addAction(UIAlertAction(title: "OK", style: UIAlertActionStyle.default, handler: {_ in
self.passwordStore.pgpKeyPassphrase = alert.textFields?.first?.text self.passwordStore.pgpKeyPassphrase = alert.textFields?.first?.text
Defaults[.isRememberPassphraseOn] = true SharedDefaults[.isRememberPassphraseOn] = true
self.saveImportedPGPKey() self.saveImportedPGPKey()
})) }))
alert.addTextField(configurationHandler: {(textField: UITextField!) in alert.addTextField(configurationHandler: {(textField: UITextField!) in
@ -293,7 +294,7 @@ class SettingsTableViewController: UITableViewController {
} }
if Defaults[.pgpKeySource] != nil { if SharedDefaults[.pgpKeySource] != nil {
let deleteAction = UIAlertAction(title: "Remove PGP Keys", style: .destructive) { _ in let deleteAction = UIAlertAction(title: "Remove PGP Keys", style: .destructive) { _ in
self.passwordStore.removePGPKeys() self.passwordStore.removePGPKeys()
self.pgpKeyTableViewCell.detailTextLabel?.text = "Not Set" self.pgpKeyTableViewCell.detailTextLabel?.text = "Not Set"
@ -307,15 +308,15 @@ class SettingsTableViewController: UITableViewController {
} }
func showPasscodeActionSheet() { func showPasscodeActionSheet() {
let passcodeChangeViewController = PasscodeLockViewController(state: .change, configuration: Globals.passcodeConfiguration) let passcodeChangeViewController = PasscodeLockViewController(state: .change, configuration: passcodeLockConfig)
let passcodeRemoveViewController = PasscodeLockViewController(state: .remove, configuration: Globals.passcodeConfiguration) let passcodeRemoveViewController = PasscodeLockViewController(state: .remove, configuration: passcodeLockConfig)
let optionMenu = UIAlertController(title: nil, message: nil, preferredStyle: .actionSheet) let optionMenu = UIAlertController(title: nil, message: nil, preferredStyle: .actionSheet)
let removePasscodeAction = UIAlertAction(title: "Remove Passcode", style: .destructive) { [weak self] _ in let removePasscodeAction = UIAlertAction(title: "Remove Passcode", style: .destructive) { [weak self] _ in
passcodeRemoveViewController.successCallback = { _ in passcodeRemoveViewController.successCallback = { _ in
self?.setPasscodeLockTouchIDCells() self?.setPasscodeLockTouchIDCells()
let appDelegate = UIApplication.shared.delegate as! AppDelegate let appDelegate = UIApplication.shared.delegate as! AppDelegate
appDelegate.passcodeLockPresenter = PasscodeLockPresenter(mainWindow: appDelegate.window, configuration: Globals.passcodeConfiguration) appDelegate.passcodeLockPresenter = PasscodeLockPresenter(mainWindow: appDelegate.window, configuration: (self?.passcodeLockConfig)!)
} }
self?.present(passcodeRemoveViewController, animated: true, completion: nil) self?.present(passcodeRemoveViewController, animated: true, completion: nil)
} }
@ -334,7 +335,7 @@ class SettingsTableViewController: UITableViewController {
} }
func setPasscodeLock() { func setPasscodeLock() {
let passcodeSetViewController = PasscodeLockViewController(state: .set, configuration: Globals.passcodeConfiguration) let passcodeSetViewController = PasscodeLockViewController(state: .set, configuration: passcodeLockConfig)
passcodeSetViewController.successCallback = { _ in passcodeSetViewController.successCallback = { _ in
self.setPasscodeLockTouchIDCells() self.setPasscodeLockTouchIDCells()
} }

View file

@ -1,60 +0,0 @@
//
// Globals.swift
// pass
//
// Created by Mingshen Sun on 21/1/2017.
// Copyright © 2017 Bob Sun. All rights reserved.
//
import Foundation
import UIKit
class Globals {
// Legacy paths (not shared)
static let documentPathLegacy = NSSearchPathForDirectoriesInDomains(.documentDirectory, .userDomainMask, true)[0];
static let libraryPathLegacy = NSSearchPathForDirectoriesInDomains(.libraryDirectory, .userDomainMask, true)[0];
static let pgpPublicKeyPathLegacy = "\(documentPathLegacy)/gpg_key.pub"
static let pgpPrivateKeyPathLegacy = "\(documentPathLegacy)/gpg_key"
static let gitSSHPrivateKeyPathLegacy = "\(documentPathLegacy)/ssh_key"
static let gitSSHPrivateKeyURLLegacy = URL(fileURLWithPath: gitSSHPrivateKeyPathLegacy)
static let repositoryPathLegacy = "\(libraryPathLegacy)/password-store"
static let groupIdentifier = "group." + Bundle.main.bundleIdentifier!
static let sharedContainerURL = FileManager.default.containerURL(forSecurityApplicationGroupIdentifier: groupIdentifier)!
static let documentPath = sharedContainerURL.appendingPathComponent("Keys").path
static let libraryPath = sharedContainerURL.appendingPathComponent("Repository").path
static let pgpPublicKeyPath = documentPath + "/gpg_key.pub"
static let pgpPrivateKeyPath = documentPath + "/gpg_key"
static let gitSSHPrivateKeyPath = documentPath + "/ssh_key"
static let gitSSHPrivateKeyURL = URL(fileURLWithPath: gitSSHPrivateKeyPath)
static let repositoryPath = libraryPath + "/password-store"
static var passcodeConfiguration = PasscodeLockConfiguration()
static let passwordDefaultLength = ["Random": (min: 4, max: 64, def: 16),
"Apple": (min: 15, max: 15, def: 15)]
static let gitSignatureDefaultName = "Pass for iOS"
static let gitSignatureDefaultEmail = "user@passforios"
static let passwordDots = "••••••••••••"
static let oneTimePasswordDots = "••••••"
static let passwordFonts = "Menlo"
// UI related
static let red = UIColor(red:1.00, green:0.23, blue:0.19, alpha:1.0)
static let blue = UIColor(red:0.00, green:0.48, blue:1.00, alpha:1.0)
static let tableCellButtonSize = CGFloat(20.0)
private init() { }
}
extension Bundle {
var releaseVersionNumber: String? {
return infoDictionary?["CFBundleShortVersionString"] as? String
}
var buildVersionNumber: String? {
return infoDictionary?["CFBundleVersion"] as? String
}
}

View file

@ -9,7 +9,7 @@
#ifndef Objective_CBridgingHeader_h #ifndef Objective_CBridgingHeader_h
#define Objective_CBridgingHeader_h #define Objective_CBridgingHeader_h
#import <ObjectivePGP/ObjectivePGP.h> // #import <ObjectivePGP/ObjectivePGP.h>
#import <ObjectiveGit/ObjectiveGit.h> #import <ObjectiveGit/ObjectiveGit.h>
#endif /* Objective_CBridgingHeader_h */ #endif /* Objective_CBridgingHeader_h */

View file

@ -8,13 +8,15 @@
import Foundation import Foundation
import PasscodeLock import PasscodeLock
import SwiftyUserDefaults import passKit
struct PasscodeLockConfiguration: PasscodeLockConfigurationType { struct PasscodeLockConfiguration: PasscodeLockConfigurationType {
static let shared = PasscodeLockConfiguration()
let repository: PasscodeRepositoryType let repository: PasscodeRepositoryType
let passcodeLength = 4 let passcodeLength = 4
var isTouchIDAllowed = Defaults[.isTouchIDOn] var isTouchIDAllowed = SharedDefaults[.isTouchIDOn]
let shouldRequestTouchIDImmediately = true let shouldRequestTouchIDImmediately = true
let maximumInccorectPasscodeAttempts = 3 let maximumInccorectPasscodeAttempts = 3

View file

@ -8,7 +8,7 @@
import Foundation import Foundation
import PasscodeLock import PasscodeLock
import SwiftyUserDefaults import passKit
public class PasscodeLockRepository: PasscodeRepositoryType { public class PasscodeLockRepository: PasscodeRepositoryType {
private let passcodeKey = "passcode.lock.passcode" private let passcodeKey = "passcode.lock.passcode"
@ -23,11 +23,11 @@ public class PasscodeLockRepository: PasscodeRepositoryType {
} }
private var passcode: String? { private var passcode: String? {
return Defaults[.passcodeKey] return SharedDefaults[.passcodeKey]
} }
public func save(passcode: String) { public func save(passcode: String) {
Defaults[.passcodeKey] = passcode SharedDefaults[.passcodeKey] = passcode
} }
public func check(passcode: String) -> Bool { public func check(passcode: String) -> Bool {
@ -35,6 +35,6 @@ public class PasscodeLockRepository: PasscodeRepositoryType {
} }
public func delete() { public func delete() {
Defaults[.passcodeKey] = nil SharedDefaults[.passcodeKey] = nil
} }
} }

View file

@ -7,6 +7,7 @@
// //
import UIKit import UIKit
import passKit
protocol FillPasswordTableViewCellDelegate { protocol FillPasswordTableViewCellDelegate {
func generateAndCopyPassword() func generateAndCopyPassword()

View file

@ -8,6 +8,7 @@
import UIKit import UIKit
import SVProgressHUD import SVProgressHUD
import passKit
struct LabelTableViewCellData { struct LabelTableViewCellData {
var title: String var title: String

View file

@ -9,9 +9,9 @@
import Foundation import Foundation
import SwiftyUserDefaults import SwiftyUserDefaults
var Defaults = UserDefaults(suiteName: Globals.groupIdentifier)! public var SharedDefaults = UserDefaults(suiteName: Globals.groupIdentifier)!
extension DefaultsKeys { public extension DefaultsKeys {
static let pgpKeySource = DefaultsKey<String?>("pgpKeySource") static let pgpKeySource = DefaultsKey<String?>("pgpKeySource")
static let pgpPublicKeyURL = DefaultsKey<URL?>("pgpPublicKeyURL") static let pgpPublicKeyURL = DefaultsKey<URL?>("pgpPublicKeyURL")
static let pgpPrivateKeyURL = DefaultsKey<URL?>("pgpPrivateKeyURL") static let pgpPrivateKeyURL = DefaultsKey<URL?>("pgpPrivateKeyURL")

View file

@ -0,0 +1,61 @@
//
// Globals.swift
// pass
//
// Created by Mingshen Sun on 21/1/2017.
// Copyright © 2017 Bob Sun. All rights reserved.
//
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];
public static let pgpPublicKeyPathLegacy = "\(documentPathLegacy)/gpg_key.pub"
public static let pgpPrivateKeyPathLegacy = "\(documentPathLegacy)/gpg_key"
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
public static let pgpPublicKeyPath = documentPath + "/gpg_key.pub"
public static let pgpPrivateKeyPath = documentPath + "/gpg_key"
public static let gitSSHPrivateKeyPath = documentPath + "/ssh_key"
public static let gitSSHPrivateKeyURL = URL(fileURLWithPath: gitSSHPrivateKeyPath)
public static let repositoryPath = libraryPath + "/password-store"
public static let passwordDefaultLength = ["Random": (min: 4, max: 64, def: 16),
"Apple": (min: 15, max: 15, def: 15)]
public static let gitSignatureDefaultName = "Pass for iOS"
public static let gitSignatureDefaultEmail = "user@passforios"
public static let passwordDots = "••••••••••••"
public static let oneTimePasswordDots = "••••••"
public static let passwordFonts = "Menlo"
// 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)
public static let tableCellButtonSize = CGFloat(20.0)
private init() { }
}
public extension Bundle {
var releaseVersionNumber: String? {
return infoDictionary?["CFBundleShortVersionString"] as? String
}
var buildVersionNumber: String? {
return infoDictionary?["CFBundleVersion"] as? String
}
}

View file

@ -8,7 +8,7 @@
import Foundation import Foundation
extension Notification.Name { public extension Notification.Name {
static let passwordStoreUpdated = Notification.Name("passwordStoreUpdated") static let passwordStoreUpdated = Notification.Name("passwordStoreUpdated")
static let passwordStoreErased = Notification.Name("passwordStoreErased") static let passwordStoreErased = Notification.Name("passwordStoreErased")
static let passwordStoreChangeDiscarded = Notification.Name("passwordStoreChangeDiscarded") static let passwordStoreChangeDiscarded = Notification.Name("passwordStoreChangeDiscarded")

View file

@ -12,8 +12,8 @@ import KeychainAccess
import UIKit import UIKit
import SVProgressHUD import SVProgressHUD
class Utils { public class Utils {
static func removeFileIfExists(atPath path: String) { public static func removeFileIfExists(atPath path: String) {
let fm = FileManager.default let fm = FileManager.default
do { do {
if fm.fileExists(atPath: path) { if fm.fileExists(atPath: path) {
@ -23,12 +23,12 @@ class Utils {
print(error) print(error)
} }
} }
static func removeFileIfExists(at url: URL) { public static func removeFileIfExists(at url: URL) {
removeFileIfExists(atPath: url.path) removeFileIfExists(atPath: url.path)
} }
static func getLastSyncedTimeString() -> String { public static func getLastSyncedTimeString() -> String {
guard let lastSyncedTime = Defaults[.lastSyncedTime] else { guard let lastSyncedTime = SharedDefaults[.lastSyncedTime] else {
return "Oops! Sync again?" return "Oops! Sync again?"
} }
let formatter = DateFormatter() let formatter = DateFormatter()
@ -37,8 +37,8 @@ class Utils {
return formatter.string(from: lastSyncedTime) return formatter.string(from: lastSyncedTime)
} }
static func generatePassword(length: Int) -> String{ public static func generatePassword(length: Int) -> String{
switch Defaults[.passwordGeneratorFlavor] { switch SharedDefaults[.passwordGeneratorFlavor] {
case "Random": case "Random":
return randomString(length: length) return randomString(length: length)
case "Apple": case "Apple":
@ -48,7 +48,7 @@ class Utils {
} }
} }
static func randomString(length: Int) -> String { public static func randomString(length: Int) -> String {
let letters : NSString = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!@#$%^&*_+-=" let letters : NSString = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!@#$%^&*_+-="
let len = UInt32(letters.length) let len = UInt32(letters.length)
@ -64,15 +64,15 @@ class Utils {
return randomString return randomString
} }
static func alert(title: String, message: String, controller: UIViewController, handler: ((UIAlertAction) -> Void)? = nil, completion: (() -> Void)? = nil) { public static func alert(title: String, message: String, controller: UIViewController, handler: ((UIAlertAction) -> Void)? = nil, completion: (() -> Void)? = nil) {
SVProgressHUD.dismiss() SVProgressHUD.dismiss()
let alert = UIAlertController(title: title, message: message, preferredStyle: UIAlertControllerStyle.alert) let alert = UIAlertController(title: title, message: message, preferredStyle: UIAlertControllerStyle.alert)
alert.addAction(UIAlertAction(title: "OK", style: UIAlertActionStyle.default, handler: handler)) alert.addAction(UIAlertAction(title: "OK", style: UIAlertActionStyle.default, handler: handler))
controller.present(alert, animated: true, completion: completion) controller.present(alert, animated: true, completion: completion)
} }
static func getPasswordFromKeychain(name: String) -> String? { public static func getPasswordFromKeychain(name: String) -> String? {
let keychain = Keychain(service: Bundle.main.bundleIdentifier!) let keychain = Keychain(service: Globals.bundleIdentifier)
do { do {
return try keychain.getString(name) return try keychain.getString(name)
} catch { } catch {
@ -81,27 +81,27 @@ class Utils {
return nil return nil
} }
static func addPasswordToKeychain(name: String, password: String?) { public static func addPasswordToKeychain(name: String, password: String?) {
let keychain = Keychain(service: Bundle.main.bundleIdentifier!) let keychain = Keychain(service: Globals.bundleIdentifier)
keychain[name] = password keychain[name] = password
} }
static func removeKeychain(name: String) { public static func removeKeychain(name: String) {
let keychain = Keychain(service: Bundle.main.bundleIdentifier!) let keychain = Keychain(service: Globals.bundleIdentifier)
do { do {
try keychain.remove(name) try keychain.remove(name)
} catch { } catch {
print(error) print(error)
} }
} }
static func removeAllKeychain() { public static func removeAllKeychain() {
let keychain = Keychain(service: Bundle.main.bundleIdentifier!) let keychain = Keychain(service: Globals.bundleIdentifier)
do { do {
try keychain.removeAll() try keychain.removeAll()
} catch { } catch {
print(error) print(error)
} }
} }
static func copyToPasteboard(textToCopy: String?, expirationTime: Double = 45) { public static func copyToPasteboard(textToCopy: String?, expirationTime: Double = 45) {
guard textToCopy != nil else { guard textToCopy != nil else {
return return
} }
@ -113,7 +113,7 @@ class Utils {
} }
} }
} }
static func attributedPassword(plainPassword: String) -> NSAttributedString{ public static func attributedPassword(plainPassword: String) -> NSAttributedString{
let attributedPassword = NSMutableAttributedString.init(string: plainPassword) let attributedPassword = NSMutableAttributedString.init(string: plainPassword)
// draw all digits in the password into red // draw all digits in the password into red
// draw all punctuation characters in the password into blue // draw all punctuation characters in the password into blue
@ -126,15 +126,15 @@ class Utils {
} }
return attributedPassword return attributedPassword
} }
static func initDefaultKeys() { public static func initDefaultKeys() {
if Defaults[.passwordGeneratorFlavor] == "" { if SharedDefaults[.passwordGeneratorFlavor] == "" {
Defaults[.passwordGeneratorFlavor] = "Random" SharedDefaults[.passwordGeneratorFlavor] = "Random"
} }
} }
} }
// https://gist.github.com/NikolaiRuhe/eeb135d20c84a7097516 // https://gist.github.com/NikolaiRuhe/eeb135d20c84a7097516
extension FileManager { public extension FileManager {
/// This method calculates the accumulated size of a directory on the volume in bytes. /// This method calculates the accumulated size of a directory on the volume in bytes.
/// ///
@ -217,7 +217,7 @@ extension FileManager {
} }
} }
extension String { public extension String {
func stringByAddingPercentEncodingForRFC3986() -> String? { func stringByAddingPercentEncodingForRFC3986() -> String? {
let unreserved = "-._~/?" let unreserved = "-._~/?"
var allowed = CharacterSet.alphanumerics var allowed = CharacterSet.alphanumerics

26
passKit/Info.plist Normal file
View file

@ -0,0 +1,26 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>CFBundleDevelopmentRegion</key>
<string>en</string>
<key>CFBundleDisplayName</key>
<string>PassKit</string>
<key>CFBundleExecutable</key>
<string>$(EXECUTABLE_NAME)</string>
<key>CFBundleIdentifier</key>
<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
<key>CFBundleInfoDictionaryVersion</key>
<string>6.0</string>
<key>CFBundleName</key>
<string>$(PRODUCT_NAME)</string>
<key>CFBundlePackageType</key>
<string>FMWK</string>
<key>CFBundleShortVersionString</key>
<string>1.0</string>
<key>CFBundleVersion</key>
<string>$(CURRENT_PROJECT_VERSION)</string>
<key>NSPrincipalClass</key>
<string></string>
</dict>
</plist>

View file

@ -12,19 +12,19 @@ import SwiftyUserDefaults
import ObjectiveGit import ObjectiveGit
import SVProgressHUD import SVProgressHUD
struct GitCredential { public struct GitCredential {
var credential: Credential public var credential: Credential
enum Credential { public enum Credential {
case http(userName: String, controller: UIViewController) case http(userName: String, controller: UIViewController)
case ssh(userName: String, privateKeyFile: URL, controller: UIViewController) case ssh(userName: String, privateKeyFile: URL, controller: UIViewController)
} }
init(credential: Credential) { public init(credential: Credential) {
self.credential = credential self.credential = credential
} }
func credentialProvider() throws -> GTCredentialProvider { public func credentialProvider() throws -> GTCredentialProvider {
var attempts = 0 var attempts = 0
var lastPassword: String? = nil var lastPassword: String? = nil
return GTCredentialProvider { (_, _, _) -> (GTCredential?) in return GTCredentialProvider { (_, _, _) -> (GTCredential?) in
@ -63,7 +63,7 @@ struct GitCredential {
} }
} }
func delete() { public func delete() {
switch credential { switch credential {
case .http: case .http:
Utils.removeKeychain(name: "gitPassword") Utils.removeKeychain(name: "gitPassword")

View file

@ -22,12 +22,12 @@ enum PasswordChange: Int {
case none = 0x00 case none = 0x00
} }
class Password { public class Password {
static let otpKeywords = ["otp_secret", "otp_type", "otp_algorithm", "otp_period", "otp_digits", "otp_counter", "otpauth"] public static let otpKeywords = ["otp_secret", "otp_type", "otp_algorithm", "otp_period", "otp_digits", "otp_counter", "otpauth"]
var name = "" public var name = ""
var url: URL? public var url: URL?
var namePath: String { public var namePath: String {
get { get {
if url == nil { if url == nil {
return "" return ""
@ -35,20 +35,20 @@ class Password {
return url!.deletingPathExtension().path return url!.deletingPathExtension().path
} }
} }
var password = "" public var password = ""
var additions = [String: String]() public var additions = [String: String]()
var additionKeys = [String]() public var additionKeys = [String]()
var changed: Int = 0 public var changed: Int = 0
var plainText = "" public var plainText = ""
private var firstLineIsOTPField = false private var firstLineIsOTPField = false
private var otpToken: Token? private var otpToken: Token?
enum OtpType { public enum OtpType {
case totp, hotp, none case totp, hotp, none
} }
var otpType: OtpType { public var otpType: OtpType {
get { get {
guard let token = self.otpToken else { guard let token = self.otpToken else {
return OtpType.none return OtpType.none
@ -62,11 +62,11 @@ class Password {
} }
} }
init(name: String, url: URL?, plainText: String) { public init(name: String, url: URL?, plainText: String) {
self.initEverything(name: name, url: url, plainText: plainText) self.initEverything(name: name, url: url, plainText: plainText)
} }
func updatePassword(name: String, url: URL?, plainText: String) { public func updatePassword(name: String, url: URL?, plainText: String) {
if self.plainText != plainText || self.url != url { if self.plainText != plainText || self.url != url {
if self.plainText != plainText { if self.plainText != plainText {
changed = changed|PasswordChange.content.rawValue changed = changed|PasswordChange.content.rawValue
@ -108,17 +108,17 @@ class Password {
self.updateOtpToken() self.updateOtpToken()
} }
func getUsername() -> String? { public func getUsername() -> String? {
return getAdditionValue(withKey: "Username") ?? getAdditionValue(withKey: "username") return getAdditionValue(withKey: "Username") ?? getAdditionValue(withKey: "username")
} }
func getURLString() -> String? { public func getURLString() -> String? {
return getAdditionValue(withKey: "URL") ?? getAdditionValue(withKey: "url") ?? getAdditionValue(withKey: "Url") return getAdditionValue(withKey: "URL") ?? getAdditionValue(withKey: "url") ?? getAdditionValue(withKey: "Url")
} }
// return a key-value pair from the line // return a key-value pair from the line
// key might be nil, if there is no ":" in the line // key might be nil, if there is no ":" in the line
static private func getKeyValuePair(from line: String) -> (key: String?, value: String) { private static func getKeyValuePair(from line: String) -> (key: String?, value: String) {
let items = line.components(separatedBy: ": ").map{String($0).trimmingCharacters(in: .whitespaces)} let items = line.components(separatedBy: ": ").map{String($0).trimmingCharacters(in: .whitespaces)}
var key : String? = nil var key : String? = nil
var value = "" var value = ""
@ -138,7 +138,7 @@ class Password {
return (key, value) return (key, value)
} }
static private func getAdditionFields(from additionFieldsPlainText: String) -> ([String: String], [String]){ private static func getAdditionFields(from additionFieldsPlainText: String) -> ([String: String], [String]){
var additions = [String: String]() var additions = [String: String]()
var additionKeys = [String]() var additionKeys = [String]()
var unknownIndex = 0 var unknownIndex = 0
@ -159,7 +159,7 @@ class Password {
return (additions, additionKeys) return (additions, additionKeys)
} }
func getAdditionsPlainText() -> String { public func getAdditionsPlainText() -> String {
// lines starting from the second // lines starting from the second
let plainTextSplit = plainText.characters.split(maxSplits: 1, omittingEmptySubsequences: false) { let plainTextSplit = plainText.characters.split(maxSplits: 1, omittingEmptySubsequences: false) {
$0 == "\n" || $0 == "\r\n" $0 == "\n" || $0 == "\r\n"
@ -175,7 +175,7 @@ class Password {
return self.plainText return self.plainText
} }
func getPlainData() -> Data { public func getPlainData() -> Data {
return getPlainText().data(using: .utf8)! return getPlainText().data(using: .utf8)!
} }
@ -290,7 +290,7 @@ class Password {
} }
// return the description and the password strings // return the description and the password strings
func getOtpStrings() -> (description: String, otp: String)? { public func getOtpStrings() -> (description: String, otp: String)? {
guard let token = self.otpToken else { guard let token = self.otpToken else {
return nil return nil
} }
@ -310,7 +310,7 @@ class Password {
} }
// return the password strings // return the password strings
func getOtp() -> String? { public func getOtp() -> String? {
if let otp = self.otpToken?.currentPassword { if let otp = self.otpToken?.currentPassword {
return otp return otp
} else { } else {
@ -320,7 +320,7 @@ class Password {
// return the password strings // return the password strings
// it is guaranteed that it is a HOTP password when we call this // it is guaranteed that it is a HOTP password when we call this
func getNextHotp() -> String? { public func getNextHotp() -> String? {
// increase the counter // increase the counter
otpToken = otpToken?.updatedToken() otpToken = otpToken?.updatedToken()
@ -349,7 +349,7 @@ class Password {
return self.otpToken?.currentPassword return self.otpToken?.currentPassword
} }
static func LooksLikeOTP(line: String) -> Bool { public static func LooksLikeOTP(line: String) -> Bool {
let (key, _) = getKeyValuePair(from: line) let (key, _) = getKeyValuePair(from: line)
return Password.otpKeywords.contains(key ?? "") return Password.otpKeywords.contains(key ?? "")
} }

View file

@ -11,7 +11,7 @@ import SwiftyUserDefaults
extension PasswordEntity { extension PasswordEntity {
var nameWithCategory: String { public var nameWithCategory: String {
get { get {
if let p = path, p.hasSuffix(".gpg") { if let p = path, p.hasSuffix(".gpg") {
return p.substring(to: p.index(p.endIndex, offsetBy: -4)) return p.substring(to: p.index(p.endIndex, offsetBy: -4))
@ -21,7 +21,7 @@ extension PasswordEntity {
} }
} }
func getCategoryText() -> String { public func getCategoryText() -> String {
var parentEntity = parent var parentEntity = parent
var passwordCategoryArray: [String] = [] var passwordCategoryArray: [String] = []
while parentEntity != nil { while parentEntity != nil {
@ -32,7 +32,7 @@ extension PasswordEntity {
return passwordCategoryArray.joined(separator: " > ") return passwordCategoryArray.joined(separator: " > ")
} }
func getURL() -> URL? { public func getURL() -> URL? {
if let p = path { if let p = path {
return URL(string: p.stringByAddingPercentEncodingForRFC3986()!) return URL(string: p.stringByAddingPercentEncodingForRFC3986()!)
} }

View file

@ -12,15 +12,16 @@ import UIKit
import SwiftyUserDefaults import SwiftyUserDefaults
import ObjectiveGit import ObjectiveGit
import SVProgressHUD import SVProgressHUD
import ObjectivePGP
class PasswordStore { public class PasswordStore {
static let shared = PasswordStore() public static let shared = PasswordStore()
let storeURL = URL(fileURLWithPath: "\(Globals.repositoryPath)") public let storeURL = URL(fileURLWithPath: "\(Globals.repositoryPath)")
let tempStoreURL = URL(fileURLWithPath: "\(Globals.repositoryPath)-temp") public let tempStoreURL = URL(fileURLWithPath: "\(Globals.repositoryPath)-temp")
var storeRepository: GTRepository? public var storeRepository: GTRepository?
var pgpKeyID: String? public var pgpKeyID: String?
var publicKey: PGPKey? { public var publicKey: PGPKey? {
didSet { didSet {
if publicKey != nil { if publicKey != nil {
pgpKeyID = publicKey!.keyID!.shortKeyString pgpKeyID = publicKey!.keyID!.shortKeyString
@ -29,19 +30,19 @@ class PasswordStore {
} }
} }
} }
var privateKey: PGPKey? public var privateKey: PGPKey?
var gitSignatureForNow: GTSignature { public var gitSignatureForNow: GTSignature {
get { get {
let gitSignatureName = Defaults[.gitSignatureName] ?? Globals.gitSignatureDefaultName let gitSignatureName = SharedDefaults[.gitSignatureName] ?? Globals.gitSignatureDefaultName
let gitSignatureEmail = Defaults[.gitSignatureEmail] ?? Globals.gitSignatureDefaultEmail let gitSignatureEmail = SharedDefaults[.gitSignatureEmail] ?? Globals.gitSignatureDefaultEmail
return GTSignature(name: gitSignatureName, email: gitSignatureEmail, time: Date())! return GTSignature(name: gitSignatureName, email: gitSignatureEmail, time: Date())!
} }
} }
var pgp: ObjectivePGP = ObjectivePGP() public var pgp: ObjectivePGP = ObjectivePGP()
var pgpKeyPassphrase: String? { public var pgpKeyPassphrase: String? {
set { set {
Utils.addPasswordToKeychain(name: "pgpKeyPassphrase", password: newValue) Utils.addPasswordToKeychain(name: "pgpKeyPassphrase", password: newValue)
} }
@ -50,7 +51,7 @@ class PasswordStore {
} }
} }
var gitPassword: String? { public var gitPassword: String? {
set { set {
Utils.addPasswordToKeychain(name: "gitPassword", password: newValue) Utils.addPasswordToKeychain(name: "gitPassword", password: newValue)
} }
@ -59,7 +60,7 @@ class PasswordStore {
} }
} }
var gitSSHPrivateKeyPassphrase: String? { public var gitSSHPrivateKeyPassphrase: String? {
set { set {
Utils.addPasswordToKeychain(name: "gitSSHPrivateKeyPassphrase", password: newValue) Utils.addPasswordToKeychain(name: "gitSSHPrivateKeyPassphrase", password: newValue)
} }
@ -68,17 +69,13 @@ class PasswordStore {
} }
} }
let fm = FileManager.default private let fm = FileManager.default
lazy var context: NSManagedObjectContext = { lazy private var context: NSManagedObjectContext = {
/* let modelURL = Bundle(identifier: Globals.passKitBundleIdentifier)!.url(forResource: "pass", withExtension: "momd")!
The persistent container for the application. This implementation let managedObjectModel = NSManagedObjectModel(contentsOf: modelURL)
creates and returns a container, having loaded the store for the let container = NSPersistentContainer(name: "pass", managedObjectModel: managedObjectModel!)
application to it. This property is optional since there are legitimate container.persistentStoreDescriptions = [NSPersistentStoreDescription(url: Globals.sharedContainerURL.appendingPathComponent("Documents/pass.sqlite"))]
error conditions that could cause the creation of the store to fail. container.loadPersistentStores(completionHandler: { (storeDescription, error) in
*/
let container = NSPersistentContainer(name: "pass")
let description = NSPersistentStoreDescription(url: Globals.sharedContainerURL)
container.loadPersistentStores(completionHandler: { (description, error) in
if let error = error as NSError? { if let error = error as NSError? {
// Replace this implementation with code to handle the error appropriately. // Replace this implementation with code to handle the error appropriately.
// fatalError() causes the application to generate a crash log and terminate. You should not use this function in a shipping application, although it may be useful during development. // fatalError() causes the application to generate a crash log and terminate. You should not use this function in a shipping application, although it may be useful during development.
@ -97,11 +94,11 @@ class PasswordStore {
return container.viewContext return container.viewContext
}() }()
var numberOfPasswords : Int { public var numberOfPasswords : Int {
return self.fetchPasswordEntityCoreData(withDir: false).count return self.fetchPasswordEntityCoreData(withDir: false).count
} }
var sizeOfRepositoryByteCount : UInt64 { public var sizeOfRepositoryByteCount : UInt64 {
var size = UInt64(0) var size = UInt64(0)
do { do {
if fm.fileExists(atPath: self.storeURL.path) { if fm.fileExists(atPath: self.storeURL.path) {
@ -116,8 +113,11 @@ class PasswordStore {
private init() { private init() {
// File migration to group // File migration to group
print(Globals.documentPath)
print(Globals.libraryPath)
print(Globals.documentPathLegacy)
print(Globals.libraryPathLegacy)
migration() migration()
do { do {
if fm.fileExists(atPath: storeURL.path) { if fm.fileExists(atPath: storeURL.path) {
try storeRepository = GTRepository.init(url: storeURL) try storeRepository = GTRepository.init(url: storeURL)
@ -134,8 +134,9 @@ class PasswordStore {
return return
} }
do { do {
try fm.copyItem(atPath: Globals.documentPathLegacy, toPath: Globals.documentPath) try fm.moveItem(atPath: Globals.documentPathLegacy, toPath: Globals.documentPath)
try fm.copyItem(atPath: Globals.libraryPathLegacy, toPath: Globals.libraryPath) try fm.moveItem(atPath: Globals.libraryPathLegacy, toPath: Globals.libraryPath)
SharedDefaults = Defaults
} catch { } catch {
print("Cannot migrate: \(error)") print("Cannot migrate: \(error)")
} }
@ -146,10 +147,7 @@ class PasswordStore {
case `public`, secret case `public`, secret
} }
public func initGitSSHKey(with armorKey: String, _ keyType: SSHKeyType) throws { public func initGitSSHKey(with armorKey: String) throws {
guard keyType == .secret else {
return
}
let keyPath = Globals.gitSSHPrivateKeyPath let keyPath = Globals.gitSSHPrivateKeyPath
try armorKey.write(toFile: keyPath, atomically: true, encoding: .ascii) try armorKey.write(toFile: keyPath, atomically: true, encoding: .ascii)
} }
@ -211,16 +209,16 @@ class PasswordStore {
return nil return nil
} }
func getPgpPrivateKey() -> PGPKey { public func getPgpPrivateKey() -> PGPKey {
return pgp.getKeysOf(.secret)[0] return pgp.getKeysOf(.secret)[0]
} }
func repositoryExisted() -> Bool { public func repositoryExisted() -> Bool {
let fm = FileManager() let fm = FileManager()
return fm.fileExists(atPath: Globals.repositoryPath) return fm.fileExists(atPath: Globals.repositoryPath)
} }
func passwordExisted(password: Password) -> Bool { public func passwordExisted(password: Password) -> Bool {
let passwordEntityFetchRequest = NSFetchRequest<NSFetchRequestResult>(entityName: "PasswordEntity") let passwordEntityFetchRequest = NSFetchRequest<NSFetchRequestResult>(entityName: "PasswordEntity")
do { do {
passwordEntityFetchRequest.predicate = NSPredicate(format: "name = %@ and path = %@", password.name, password.url!.path) passwordEntityFetchRequest.predicate = NSPredicate(format: "name = %@ and path = %@", password.name, password.url!.path)
@ -236,7 +234,7 @@ class PasswordStore {
return true return true
} }
func passwordEntityExisted(path: String) -> Bool { public func passwordEntityExisted(path: String) -> Bool {
let passwordEntityFetchRequest = NSFetchRequest<NSFetchRequestResult>(entityName: "PasswordEntity") let passwordEntityFetchRequest = NSFetchRequest<NSFetchRequestResult>(entityName: "PasswordEntity")
do { do {
passwordEntityFetchRequest.predicate = NSPredicate(format: "path = %@", path) passwordEntityFetchRequest.predicate = NSPredicate(format: "path = %@", path)
@ -252,7 +250,7 @@ class PasswordStore {
return true return true
} }
func getPasswordEntity(by path: String, isDir: Bool) -> PasswordEntity? { public func getPasswordEntity(by path: String, isDir: Bool) -> PasswordEntity? {
let passwordEntityFetchRequest = NSFetchRequest<NSFetchRequestResult>(entityName: "PasswordEntity") let passwordEntityFetchRequest = NSFetchRequest<NSFetchRequestResult>(entityName: "PasswordEntity")
do { do {
passwordEntityFetchRequest.predicate = NSPredicate(format: "path = %@ and isDir = %@", path, isDir.description) passwordEntityFetchRequest.predicate = NSPredicate(format: "path = %@ and isDir = %@", path, isDir.description)
@ -262,7 +260,7 @@ class PasswordStore {
} }
} }
func cloneRepository(remoteRepoURL: URL, public func cloneRepository(remoteRepoURL: URL,
credential: GitCredential, credential: GitCredential,
transferProgressBlock: @escaping (UnsafePointer<git_transfer_progress>, UnsafeMutablePointer<ObjCBool>) -> Void, transferProgressBlock: @escaping (UnsafePointer<git_transfer_progress>, UnsafeMutablePointer<ObjCBool>) -> Void,
checkoutProgressBlock: @escaping (String?, UInt, UInt) -> Void) throws { checkoutProgressBlock: @escaping (String?, UInt, UInt) -> Void) throws {
@ -283,13 +281,13 @@ class PasswordStore {
throw(error) throw(error)
} }
DispatchQueue.main.async { DispatchQueue.main.async {
Defaults[.lastSyncedTime] = Date() SharedDefaults[.lastSyncedTime] = Date()
self.updatePasswordEntityCoreData() self.updatePasswordEntityCoreData()
NotificationCenter.default.post(name: .passwordStoreUpdated, object: nil) NotificationCenter.default.post(name: .passwordStoreUpdated, object: nil)
} }
} }
func pullRepository(credential: GitCredential, transferProgressBlock: @escaping (UnsafePointer<git_transfer_progress>, UnsafeMutablePointer<ObjCBool>) -> Void) throws { public func pullRepository(credential: GitCredential, transferProgressBlock: @escaping (UnsafePointer<git_transfer_progress>, UnsafeMutablePointer<ObjCBool>) -> Void) throws {
guard let storeRepository = storeRepository else { guard let storeRepository = storeRepository else {
throw AppError.RepositoryNotSetError throw AppError.RepositoryNotSetError
} }
@ -303,7 +301,7 @@ class PasswordStore {
throw(error) throw(error)
} }
DispatchQueue.main.async { DispatchQueue.main.async {
Defaults[.lastSyncedTime] = Date() SharedDefaults[.lastSyncedTime] = Date()
self.setAllSynced() self.setAllSynced()
self.updatePasswordEntityCoreData() self.updatePasswordEntityCoreData()
NotificationCenter.default.post(name: .passwordStoreUpdated, object: nil) NotificationCenter.default.post(name: .passwordStoreUpdated, object: nil)
@ -364,7 +362,7 @@ class PasswordStore {
} }
} }
func getRecentCommits(count: Int) throws -> [GTCommit] { public func getRecentCommits(count: Int) throws -> [GTCommit] {
guard let storeRepository = storeRepository else { guard let storeRepository = storeRepository else {
return [] return []
} }
@ -380,7 +378,7 @@ class PasswordStore {
return commits return commits
} }
func fetchPasswordEntityCoreData(parent: PasswordEntity?) -> [PasswordEntity] { public func fetchPasswordEntityCoreData(parent: PasswordEntity?) -> [PasswordEntity] {
let passwordEntityFetch = NSFetchRequest<NSFetchRequestResult>(entityName: "PasswordEntity") let passwordEntityFetch = NSFetchRequest<NSFetchRequestResult>(entityName: "PasswordEntity")
do { do {
passwordEntityFetch.predicate = NSPredicate(format: "parent = %@", parent ?? 0) passwordEntityFetch.predicate = NSPredicate(format: "parent = %@", parent ?? 0)
@ -391,7 +389,7 @@ class PasswordStore {
} }
} }
func fetchPasswordEntityCoreData(withDir: Bool) -> [PasswordEntity] { public func fetchPasswordEntityCoreData(withDir: Bool) -> [PasswordEntity] {
let passwordEntityFetch = NSFetchRequest<NSFetchRequestResult>(entityName: "PasswordEntity") let passwordEntityFetch = NSFetchRequest<NSFetchRequestResult>(entityName: "PasswordEntity")
do { do {
if !withDir { if !withDir {
@ -405,7 +403,7 @@ class PasswordStore {
} }
func fetchUnsyncedPasswords() -> [PasswordEntity] { public func fetchUnsyncedPasswords() -> [PasswordEntity] {
let passwordEntityFetchRequest = NSFetchRequest<NSFetchRequestResult>(entityName: "PasswordEntity") let passwordEntityFetchRequest = NSFetchRequest<NSFetchRequestResult>(entityName: "PasswordEntity")
passwordEntityFetchRequest.predicate = NSPredicate(format: "synced = %i", 0) passwordEntityFetchRequest.predicate = NSPredicate(format: "synced = %i", 0)
do { do {
@ -416,7 +414,7 @@ class PasswordStore {
} }
} }
func setAllSynced() { public func setAllSynced() {
let passwordEntities = fetchUnsyncedPasswords() let passwordEntities = fetchUnsyncedPasswords()
for passwordEntity in passwordEntities { for passwordEntity in passwordEntities {
passwordEntity.synced = true passwordEntity.synced = true
@ -430,7 +428,7 @@ class PasswordStore {
} }
} }
func getNumberOfUnsyncedPasswords() -> Int { public func getNumberOfUnsyncedPasswords() -> Int {
let passwordEntityFetchRequest = NSFetchRequest<NSFetchRequestResult>(entityName: "PasswordEntity") let passwordEntityFetchRequest = NSFetchRequest<NSFetchRequestResult>(entityName: "PasswordEntity")
do { do {
passwordEntityFetchRequest.predicate = NSPredicate(format: "synced = %i", 0) passwordEntityFetchRequest.predicate = NSPredicate(format: "synced = %i", 0)
@ -441,7 +439,7 @@ class PasswordStore {
} }
func getLatestUpdateInfo(filename: String) -> String { public func getLatestUpdateInfo(filename: String) -> String {
guard let storeRepository = storeRepository else { guard let storeRepository = storeRepository else {
return "Unknown" return "Unknown"
} }
@ -467,7 +465,7 @@ class PasswordStore {
return autoFormattedDifference return autoFormattedDifference
} }
func updateRemoteRepo() { public func updateRemoteRepo() {
} }
private func gitAdd(path: String) throws { private func gitAdd(path: String) throws {
@ -540,7 +538,7 @@ class PasswordStore {
return branches.first return branches.first
} }
func pushRepository(credential: GitCredential, transferProgressBlock: @escaping (UInt32, UInt32, Int, UnsafeMutablePointer<ObjCBool>) -> Void) throws { public func pushRepository(credential: GitCredential, transferProgressBlock: @escaping (UInt32, UInt32, Int, UnsafeMutablePointer<ObjCBool>) -> Void) throws {
guard let storeRepository = storeRepository else { guard let storeRepository = storeRepository else {
throw AppError.RepositoryNotSetError throw AppError.RepositoryNotSetError
} }
@ -604,7 +602,7 @@ class PasswordStore {
return ret return ret
} }
func add(password: Password) throws -> PasswordEntity? { public func add(password: Password) throws -> PasswordEntity? {
try createDirectoryTree(at: password.url!) try createDirectoryTree(at: password.url!)
let newPasswordEntity = try addPasswordEntities(password: password) let newPasswordEntity = try addPasswordEntities(password: password)
let saveURL = storeURL.appendingPathComponent(password.url!.path) let saveURL = storeURL.appendingPathComponent(password.url!.path)
@ -624,7 +622,7 @@ class PasswordStore {
NotificationCenter.default.post(name: .passwordStoreUpdated, object: nil) NotificationCenter.default.post(name: .passwordStoreUpdated, object: nil)
} }
func edit(passwordEntity: PasswordEntity, password: Password) throws -> PasswordEntity? { public func edit(passwordEntity: PasswordEntity, password: Password) throws -> PasswordEntity? {
var newPasswordEntity: PasswordEntity? = passwordEntity var newPasswordEntity: PasswordEntity? = passwordEntity
if password.changed&PasswordChange.content.rawValue != 0 { if password.changed&PasswordChange.content.rawValue != 0 {
@ -670,7 +668,7 @@ class PasswordStore {
} }
} }
func saveUpdated(passwordEntity: PasswordEntity) { public func saveUpdated(passwordEntity: PasswordEntity) {
do { do {
try context.save() try context.save()
} catch { } catch {
@ -678,7 +676,7 @@ class PasswordStore {
} }
} }
func deleteCoreData(entityName: String) { public func deleteCoreData(entityName: String) {
let deleteFetchRequest = NSFetchRequest<NSFetchRequestResult>(entityName: entityName) let deleteFetchRequest = NSFetchRequest<NSFetchRequestResult>(entityName: entityName)
let deleteRequest = NSBatchDeleteRequest(fetchRequest: deleteFetchRequest) let deleteRequest = NSBatchDeleteRequest(fetchRequest: deleteFetchRequest)
@ -691,7 +689,7 @@ class PasswordStore {
} }
} }
func updateImage(passwordEntity: PasswordEntity, image: Data?) { public func updateImage(passwordEntity: PasswordEntity, image: Data?) {
guard let image = image else { guard let image = image else {
return return
} }
@ -714,7 +712,7 @@ class PasswordStore {
} }
} }
func erase() { public func erase() {
publicKey = nil publicKey = nil
privateKey = nil privateKey = nil
Utils.removeFileIfExists(at: storeURL) Utils.removeFileIfExists(at: storeURL)
@ -736,7 +734,7 @@ class PasswordStore {
} }
// return the number of discarded commits // return the number of discarded commits
func reset() throws -> Int { public func reset() throws -> Int {
guard let storeRepository = storeRepository else { guard let storeRepository = storeRepository else {
throw AppError.RepositoryNotSetError throw AppError.RepositoryNotSetError
} }
@ -761,7 +759,7 @@ class PasswordStore {
} }
} }
func numberOfLocalCommits() -> Int { public func numberOfLocalCommits() -> Int {
do { do {
if let localCommits = try getLocalCommits() { if let localCommits = try getLocalCommits() {
return localCommits.count return localCommits.count
@ -795,7 +793,7 @@ class PasswordStore {
func decrypt(passwordEntity: PasswordEntity, requestPGPKeyPassphrase: () -> String) throws -> Password? { public func decrypt(passwordEntity: PasswordEntity, requestPGPKeyPassphrase: () -> String) throws -> Password? {
let encryptedDataPath = storeURL.appendingPathComponent(passwordEntity.path!) let encryptedDataPath = storeURL.appendingPathComponent(passwordEntity.path!)
let encryptedData = try Data(contentsOf: encryptedDataPath) let encryptedData = try Data(contentsOf: encryptedDataPath)
var passphrase = self.pgpKeyPassphrase var passphrase = self.pgpKeyPassphrase
@ -808,16 +806,16 @@ class PasswordStore {
return Password(name: passwordEntity.name!, url: URL(string: escapedPath), plainText: plainText) return Password(name: passwordEntity.name!, url: URL(string: escapedPath), plainText: plainText)
} }
func encrypt(password: Password) throws -> Data { public func encrypt(password: Password) throws -> Data {
guard let publicKey = pgp.getKeysOf(.public).first else { guard let publicKey = pgp.getKeysOf(.public).first else {
throw AppError.PGPPublicKeyNotExistError throw AppError.PGPPublicKeyNotExistError
} }
let plainData = password.getPlainData() let plainData = password.getPlainData()
let encryptedData = try pgp.encryptData(plainData, usingPublicKey: publicKey, armored: Defaults[.encryptInArmored]) let encryptedData = try pgp.encryptData(plainData, usingPublicKey: publicKey, armored: SharedDefaults[.encryptInArmored])
return encryptedData return encryptedData
} }
func removePGPKeys() { public func removePGPKeys() {
Utils.removeFileIfExists(atPath: Globals.pgpPublicKeyPath) Utils.removeFileIfExists(atPath: Globals.pgpPublicKeyPath)
Utils.removeFileIfExists(atPath: Globals.pgpPrivateKeyPath) Utils.removeFileIfExists(atPath: Globals.pgpPrivateKeyPath)
Defaults.remove(.pgpKeySource) Defaults.remove(.pgpKeySource)
@ -831,18 +829,18 @@ class PasswordStore {
privateKey = nil privateKey = nil
} }
func removeGitSSHKeys() { public func removeGitSSHKeys() {
Utils.removeFileIfExists(atPath: Globals.gitSSHPrivateKeyPath) Utils.removeFileIfExists(atPath: Globals.gitSSHPrivateKeyPath)
Defaults.remove(.gitSSHPrivateKeyArmor) Defaults.remove(.gitSSHPrivateKeyArmor)
Defaults.remove(.gitSSHPrivateKeyURL) Defaults.remove(.gitSSHPrivateKeyURL)
Utils.removeKeychain(name: ".gitSSHPrivateKeyPassphrase") Utils.removeKeychain(name: ".gitSSHPrivateKeyPassphrase")
} }
func gitSSHKeyExists() -> Bool { public func gitSSHKeyExists() -> Bool {
return fm.fileExists(atPath: Globals.gitSSHPrivateKeyPath) return fm.fileExists(atPath: Globals.gitSSHPrivateKeyPath)
} }
func pgpKeyExists() -> Bool { public func pgpKeyExists() -> Bool {
return fm.fileExists(atPath: Globals.pgpPublicKeyPath) && fm.fileExists(atPath: Globals.pgpPrivateKeyPath) return fm.fileExists(atPath: Globals.pgpPublicKeyPath) && fm.fileExists(atPath: Globals.pgpPrivateKeyPath)
} }
} }

View file

@ -1,5 +1,5 @@
<?xml version="1.0" encoding="UTF-8" standalone="yes"?> <?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<model type="com.apple.IDECoreDataModeler.DataModel" documentVersion="1.0" lastSavedToolsVersion="12124.1" systemVersion="16E175b" minimumToolsVersion="Automatic" sourceLanguage="Swift" userDefinedModelVersionIdentifier=""> <model type="com.apple.IDECoreDataModeler.DataModel" documentVersion="1.0" lastSavedToolsVersion="12141" systemVersion="16F73" minimumToolsVersion="Automatic" sourceLanguage="Swift" userDefinedModelVersionIdentifier="">
<entity name="PasswordEntity" representedClassName="PasswordEntity" syncable="YES" codeGenerationType="class"> <entity name="PasswordEntity" representedClassName="PasswordEntity" syncable="YES" codeGenerationType="class">
<attribute name="image" optional="YES" attributeType="Binary" allowsExternalBinaryDataStorage="YES" syncable="YES"/> <attribute name="image" optional="YES" attributeType="Binary" allowsExternalBinaryDataStorage="YES" syncable="YES"/>
<attribute name="isDir" attributeType="Boolean" defaultValueString="NO" usesScalarValueType="YES" syncable="YES"/> <attribute name="isDir" attributeType="Boolean" defaultValueString="NO" usesScalarValueType="YES" syncable="YES"/>

15
passKit/passKit.h Normal file
View file

@ -0,0 +1,15 @@
//
// passKit.h
// passKit
//
// Created by Yishi Lin on 11/6/17.
// Copyright © 2017年 Bob Sun. All rights reserved.
//
#import <UIKit/UIKit.h>
//! Project version number for passKit.
FOUNDATION_EXPORT double passKitVersionNumber;
//! Project version string for passKit.
FOUNDATION_EXPORT const unsigned char passKitVersionString[];

View file

@ -8,10 +8,12 @@
import UIKit import UIKit
import MobileCoreServices import MobileCoreServices
import passKit
class ActionViewController: UIViewController { class ActionViewController: UIViewController {
@IBOutlet weak var textView: UITextView! @IBOutlet weak var textView: UITextView!
let passwordStore = PasswordStore.shared
override func viewDidLoad() { override func viewDidLoad() {
super.viewDidLoad() super.viewDidLoad()
@ -23,9 +25,32 @@ class ActionViewController: UIViewController {
provider.loadItem(forTypeIdentifier: propertyList, options: nil, completionHandler: { (item, error) -> Void in provider.loadItem(forTypeIdentifier: propertyList, options: nil, completionHandler: { (item, error) -> Void in
let dictionary = item as! NSDictionary let dictionary = item as! NSDictionary
let results = dictionary[NSExtensionJavaScriptPreprocessingResultsKey] as! NSDictionary let results = dictionary[NSExtensionJavaScriptPreprocessingResultsKey] as! NSDictionary
let url = results["url"] as? String let url = URL(string: (results["url"] as? String)!)?.host
let numberFormatter = NumberFormatter()
numberFormatter.numberStyle = NumberFormatter.Style.decimal
let numberOfPasswordsString = "Number of password:" + numberFormatter.string(from: NSNumber(value: self.passwordStore.numberOfPasswords))!
let sizeOfRepositoryString = "Size of repo:" + ByteCountFormatter.string(fromByteCount: Int64(self.passwordStore.sizeOfRepositoryByteCount), countStyle: ByteCountFormatter.CountStyle.file)
var numberOfCommits: UInt = 0
do {
if let _ = try self.passwordStore.storeRepository?.currentBranch().oid {
numberOfCommits = self.passwordStore.storeRepository?.numberOfCommits(inCurrentBranch: NSErrorPointer(nilLiteral: ())) ?? 0
}
} catch {
print(error)
}
let numberOfCommitsString = "Number of commits:" + numberFormatter.string(from: NSNumber(value: numberOfCommits))!
let gitURL = SharedDefaults[.gitURL]!
DispatchQueue.main.async { [weak self] in DispatchQueue.main.async { [weak self] in
self?.textView.text = url self?.textView.text = url!
print(numberOfPasswordsString)
print(numberOfCommitsString)
print(sizeOfRepositoryString)
print(gitURL)
} }
}) })
} else { } else {