Format code with SwiftFormat automatically in every build

This commit is contained in:
Danny Moesch 2020-06-28 21:25:40 +02:00 committed by Mingshen Sun
parent f167ab7549
commit 7f9f0e43b2
100 changed files with 1124 additions and 1063 deletions

View file

@ -11,9 +11,8 @@
import UIKit
open class PasscodeLockPresenter {
fileprivate var mainWindow: UIWindow?
fileprivate var passcodeLockWindow: UIWindow?
private var mainWindow: UIWindow?
private var passcodeLockWindow: UIWindow?
public init(mainWindow window: UIWindow?) {
self.mainWindow = window
@ -27,7 +26,7 @@ open class PasscodeLockPresenter {
// new window
mainWindow?.endEditing(true)
passcodeLockWindow = UIWindow(frame: self.mainWindow!.frame)
passcodeLockWindow = UIWindow(frame: mainWindow!.frame)
moveWindowsToFront(windowLevel: windowLevel)
passcodeLockWindow?.isHidden = false
@ -46,9 +45,9 @@ open class PasscodeLockPresenter {
passcodeLockWindow?.rootViewController = nil
}
fileprivate func moveWindowsToFront(windowLevel: CGFloat?) {
private func moveWindowsToFront(windowLevel: CGFloat?) {
let windowLevel = windowLevel ?? UIWindow.Level.normal.rawValue
let maxWinLevel = max(windowLevel, UIWindow.Level.normal.rawValue)
passcodeLockWindow?.windowLevel = UIWindow.Level(rawValue: maxWinLevel + 1)
passcodeLockWindow?.windowLevel = UIWindow.Level(rawValue: maxWinLevel + 1)
}
}

View file

@ -8,14 +8,13 @@
// Inspired by SwiftPasscodeLock created by Yanko Dimitrov.
//
import UIKit
import LocalAuthentication
import UIKit
open class PasscodeLockViewController: UIViewController, UITextFieldDelegate {
open var dismissCompletionCallback: (()->Void)?
open var successCallback: (()->Void)?
open var cancelCallback: (()->Void)?
open var dismissCompletionCallback: (() -> Void)?
open var successCallback: (() -> Void)?
open var cancelCallback: (() -> Void)?
weak var passcodeTextField: UITextField?
weak var biometryAuthButton: UIButton?
@ -23,21 +22,21 @@ open class PasscodeLockViewController: UIViewController, UITextFieldDelegate {
open weak var cancelButton: UIButton?
var isCancellable: Bool = false
private let passwordStore = PasswordStore.shared
open override func loadView() {
override open func loadView() {
super.loadView()
let passcodeTextField = UITextField()
let passcodeTextField = UITextField()
passcodeTextField.borderStyle = UITextField.BorderStyle.roundedRect
passcodeTextField.placeholder = "EnterPasscode".localize()
passcodeTextField.isSecureTextEntry = true
passcodeTextField.clearButtonMode = UITextField.ViewMode.whileEditing
passcodeTextField.delegate = self
passcodeTextField.addTarget(self, action: #selector(self.passcodeTextFieldDidChange(_:)), for: UIControl.Event.editingChanged)
passcodeTextField.addTarget(self, action: #selector(passcodeTextFieldDidChange(_:)), for: UIControl.Event.editingChanged)
passcodeTextField.translatesAutoresizingMaskIntoConstraints = false
self.view.addSubview(passcodeTextField)
view.addSubview(passcodeTextField)
self.passcodeTextField = passcodeTextField
view.backgroundColor = Colors.systemBackground
@ -50,7 +49,7 @@ open class PasscodeLockViewController: UIViewController, UITextFieldDelegate {
biometryAuthButton.addTarget(self, action: #selector(bioButtonPressedAction(_:)), for: .touchUpInside)
biometryAuthButton.isHidden = true
biometryAuthButton.translatesAutoresizingMaskIntoConstraints = false
self.view.addSubview(biometryAuthButton)
view.addSubview(biometryAuthButton)
self.biometryAuthButton = biometryAuthButton
let myContext = LAContext()
@ -71,21 +70,21 @@ open class PasscodeLockViewController: UIViewController, UITextFieldDelegate {
forgotPasscodeButton.setTitleColor(Colors.systemBlue, for: .normal)
forgotPasscodeButton.addTarget(self, action: #selector(forgotPasscodeButtonPressedAction(_:)), for: .touchUpInside)
// hide the forgotPasscodeButton if the native app is running
forgotPasscodeButton.isHidden = self.isCancellable
forgotPasscodeButton.isHidden = isCancellable
forgotPasscodeButton.translatesAutoresizingMaskIntoConstraints = false
self.view.addSubview(forgotPasscodeButton)
view.addSubview(forgotPasscodeButton)
self.forgotPasscodeButton = forgotPasscodeButton
let cancelButton = UIButton(type: .custom)
cancelButton.setTitle("Cancel".localize(), for: .normal)
cancelButton.setTitleColor(Colors.systemBlue, for: .normal)
cancelButton.addTarget(self, action: #selector(passcodeLockDidCancel), for: .touchUpInside)
cancelButton.isHidden = !self.isCancellable
cancelButton.isHidden = !isCancellable
cancelButton.translatesAutoresizingMaskIntoConstraints = false
cancelButton.contentHorizontalAlignment = UIControl.ContentHorizontalAlignment.left
self.view.addSubview(cancelButton)
view.addSubview(cancelButton)
self.cancelButton = cancelButton
// Display the Pass icon in the middle of the screen
let bundle = Bundle(for: PasscodeLockViewController.self)
let appIcon = UIImage(named: "PasscodeLockViewIcon", in: bundle, compatibleWith: nil)
@ -94,49 +93,48 @@ open class PasscodeLockViewController: UIViewController, UITextFieldDelegate {
appIconView.translatesAutoresizingMaskIntoConstraints = false
appIconView.layer.cornerRadius = appIconSize / 5
appIconView.layer.masksToBounds = true
self.view?.addSubview(appIconView)
view?.addSubview(appIconView)
NSLayoutConstraint.activate([
passcodeTextField.widthAnchor.constraint(equalToConstant: 250),
passcodeTextField.heightAnchor.constraint(equalToConstant: 40),
passcodeTextField.centerXAnchor.constraint(equalTo: self.view.centerXAnchor),
passcodeTextField.centerYAnchor.constraint(equalTo: self.view.centerYAnchor, constant: -20),
passcodeTextField.centerXAnchor.constraint(equalTo: view.centerXAnchor),
passcodeTextField.centerYAnchor.constraint(equalTo: view.centerYAnchor, constant: -20),
// above passocde
appIconView.widthAnchor.constraint(equalToConstant: appIconSize),
appIconView.heightAnchor.constraint(equalToConstant: appIconSize),
appIconView.centerXAnchor.constraint(equalTo: self.view.centerXAnchor),
appIconView.centerXAnchor.constraint(equalTo: view.centerXAnchor),
appIconView.bottomAnchor.constraint(equalTo: passcodeTextField.topAnchor, constant: -appIconSize),
// below passcode
biometryAuthButton.widthAnchor.constraint(equalToConstant: 250),
biometryAuthButton.heightAnchor.constraint(equalToConstant: 40),
biometryAuthButton.centerXAnchor.constraint(equalTo: self.view.centerXAnchor),
biometryAuthButton.centerXAnchor.constraint(equalTo: view.centerXAnchor),
biometryAuthButton.topAnchor.constraint(equalTo: passcodeTextField.bottomAnchor),
// cancel (top-left of the screen)
cancelButton.widthAnchor.constraint(equalToConstant: 150),
cancelButton.heightAnchor.constraint(equalToConstant: 40),
cancelButton.topAnchor.constraint(equalTo: self.view.safeTopAnchor),
cancelButton.leftAnchor.constraint(equalTo: self.view.safeLeftAnchor, constant: 20),
cancelButton.topAnchor.constraint(equalTo: view.safeTopAnchor),
cancelButton.leftAnchor.constraint(equalTo: view.safeLeftAnchor, constant: 20),
// bottom of the screen
forgotPasscodeButton.widthAnchor.constraint(equalToConstant: 250),
forgotPasscodeButton.heightAnchor.constraint(equalToConstant: 40),
forgotPasscodeButton.centerXAnchor.constraint(equalTo: self.view.centerXAnchor),
forgotPasscodeButton.bottomAnchor.constraint(equalTo: self.view.safeBottomAnchor, constant: -40)
forgotPasscodeButton.centerXAnchor.constraint(equalTo: view.centerXAnchor),
forgotPasscodeButton.bottomAnchor.constraint(equalTo: view.safeBottomAnchor, constant: -40),
])
// dismiss keyboard when tapping anywhere
let tap = UITapGestureRecognizer(target: self.view, action: #selector(UIView.endEditing))
let tap = UITapGestureRecognizer(target: view, action: #selector(UIView.endEditing))
view.addGestureRecognizer(tap)
}
open override func viewDidLoad() {
override open func viewDidLoad() {
super.viewDidLoad()
}
open override func viewDidAppear(_ animated: Bool) {
override open func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
if let biometryAuthButton = biometryAuthButton {
self.bioButtonPressedAction(biometryAuthButton)
bioButtonPressedAction(biometryAuthButton)
}
}
@ -167,16 +165,18 @@ open class PasscodeLockViewController: UIViewController, UITextFieldDelegate {
dismissPasscodeLock(completionHandler: successCallback)
}
@objc func passcodeLockDidCancel() {
@objc
func passcodeLockDidCancel() {
dismissPasscodeLock(completionHandler: cancelCallback)
}
@objc func bioButtonPressedAction(_ uiButton: UIButton) {
@objc
func bioButtonPressedAction(_: UIButton) {
let myContext = LAContext()
let myLocalizedReasonString = "AuthenticationNeeded.".localize()
var authError: NSError?
if myContext.canEvaluatePolicy(.deviceOwnerAuthenticationWithBiometrics, error: &authError) {
myContext.evaluatePolicy(.deviceOwnerAuthenticationWithBiometrics, localizedReason: myLocalizedReasonString) { success, evaluateError in
myContext.evaluatePolicy(.deviceOwnerAuthenticationWithBiometrics, localizedReason: myLocalizedReasonString) { success, _ in
if success {
DispatchQueue.main.async {
// user authenticated successfully, take appropriate action
@ -187,9 +187,10 @@ open class PasscodeLockViewController: UIViewController, UITextFieldDelegate {
}
}
@objc func forgotPasscodeButtonPressedAction(_ uiButton: UIButton) {
@objc
func forgotPasscodeButtonPressedAction(_: UIButton) {
let alert = UIAlertController(title: "ResetPass".localize(), message: "ResetPassExplanation.".localize(), preferredStyle: UIAlertController.Style.alert)
alert.addAction(UIAlertAction(title: "ErasePasswordStoreData".localize(), style: UIAlertAction.Style.destructive, handler: {[unowned self] (action) -> Void in
alert.addAction(UIAlertAction(title: "ErasePasswordStoreData".localize(), style: UIAlertAction.Style.destructive, handler: { [unowned self] (_) -> Void in
let myContext = LAContext()
var error: NSError?
// If the device passcode is not set, reset the app.
@ -199,7 +200,7 @@ open class PasscodeLockViewController: UIViewController, UITextFieldDelegate {
return
}
// If the device passcode is set, authentication is required.
myContext.evaluatePolicy(.deviceOwnerAuthentication, localizedReason: "ErasePasswordStoreData".localize()) { (success, error) in
myContext.evaluatePolicy(.deviceOwnerAuthentication, localizedReason: "ErasePasswordStoreData".localize()) { success, error in
if success {
DispatchQueue.main.async {
// User authenticated successfully, take appropriate action
@ -214,25 +215,26 @@ open class PasscodeLockViewController: UIViewController, UITextFieldDelegate {
}
}))
alert.addAction(UIAlertAction.dismiss())
self.present(alert, animated: true, completion: nil)
present(alert, animated: true, completion: nil)
}
public override func textFieldShouldReturn(_ textField: UITextField) -> Bool {
override public func textFieldShouldReturn(_ textField: UITextField) -> Bool {
if textField == passcodeTextField {
if !PasscodeLock.shared.check(passcode: textField.text ?? "") {
self.passcodeTextField?.placeholder =
passcodeTextField?.placeholder =
"TryAgain".localize()
self.passcodeTextField?.text = ""
self.passcodeTextField?.shake()
passcodeTextField?.text = ""
passcodeTextField?.shake()
}
}
textField.resignFirstResponder()
return true
}
@objc func passcodeTextFieldDidChange(_ textField: UITextField) {
@objc
func passcodeTextFieldDidChange(_ textField: UITextField) {
if PasscodeLock.shared.check(passcode: textField.text ?? "") {
self.passcodeLockDidSucceed()
passcodeLockDidSucceed()
}
}

View file

@ -7,7 +7,6 @@
//
extension Array {
func slices(count: UInt) -> [ArraySlice<Element>] {
guard count != 0 else {
return []

View file

@ -8,18 +8,18 @@
extension String {
public func localize() -> String {
return NSLocalizedString(self, value: "#\(self)#", comment: "")
NSLocalizedString(self, value: "#\(self)#", comment: "")
}
public func localize(_ firstValue: CVarArg) -> String {
return String(format: localize(), firstValue)
String(format: localize(), firstValue)
}
public func localize(_ firstValue: CVarArg, _ secondValue: CVarArg) -> String {
return String(format: localize(), firstValue, secondValue)
String(format: localize(), firstValue, secondValue)
}
public func localize(_ error: Error) -> String {
return localize(error.localizedDescription)
localize(error.localizedDescription)
}
}

View file

@ -8,7 +8,7 @@
extension String {
public var trimmed: String {
return trimmingCharacters(in: .whitespacesAndNewlines)
trimmingCharacters(in: .whitespacesAndNewlines)
}
public func stringByAddingPercentEncodingForRFC3986() -> String? {
@ -19,12 +19,12 @@ extension String {
}
public func splitByNewline() -> [String] {
return split(omittingEmptySubsequences: false) { $0 == "\n" || $0 == "\r\n" }.map(String.init)
split(omittingEmptySubsequences: false) { $0 == "\n" || $0 == "\r\n" }.map(String.init)
}
}
extension String {
public static func | (left: String, right: String) -> String {
return right.isEmpty ? left : left + "\n" + right
right.isEmpty ? left : left + "\n" + right
}
}

View file

@ -6,12 +6,12 @@
// Copyright © 2020 Bob Sun. All rights reserved.
//
import UIKit
import Foundation
import UIKit
extension UIAlertAction {
public static func cancelAndPopView(controller: UIViewController) -> UIAlertAction {
return cancel() { _ in
cancel { _ in
controller.navigationController?.popViewController(animated: true)
}
}
@ -24,7 +24,7 @@ extension UIAlertAction {
cancel(with: "Dismiss", handler: handler)
}
public static func cancel(with title: String, handler: ((UIAlertAction) -> Void)? = nil) -> UIAlertAction {
public static func cancel(with _: String, handler: ((UIAlertAction) -> Void)? = nil) -> UIAlertAction {
UIAlertAction(title: "Cancel".localize(), style: .cancel, handler: handler)
}
@ -33,7 +33,7 @@ extension UIAlertAction {
}
public static func okAndPopView(controller: UIViewController) -> UIAlertAction {
return ok() { _ in
ok { _ in
controller.navigationController?.popViewController(animated: true)
}
}
@ -41,13 +41,12 @@ extension UIAlertAction {
public static func selectKey(controller: UIViewController, handler: ((UIAlertAction) -> Void)?) -> UIAlertAction {
UIAlertAction(title: "Select Key", style: .default) { _ in
let selectKeyAlert = UIAlertController(title: "Select from imported keys", message: nil, preferredStyle: .actionSheet)
try? PGPAgent.shared.getShortKeyID().forEach({ k in
try? PGPAgent.shared.getShortKeyID().forEach { k in
let action = UIAlertAction(title: k, style: .default, handler: handler)
selectKeyAlert.addAction(action)
})
}
selectKeyAlert.addAction(UIAlertAction.cancelAndPopView(controller: controller))
controller.present(selectKeyAlert, animated: true, completion: nil)
}
}
}

View file

@ -14,18 +14,18 @@ private var kAssociationKeyNextField: UInt8 = 0
extension UITextField {
@IBOutlet var nextField: UITextField? {
get {
return objc_getAssociatedObject(self, &kAssociationKeyNextField) as? UITextField
objc_getAssociatedObject(self, &kAssociationKeyNextField) as? UITextField
}
set(newField) {
objc_setAssociatedObject(self, &kAssociationKeyNextField, newField, .OBJC_ASSOCIATION_RETAIN)
}
}
func shake() {
let animation = CAKeyframeAnimation(keyPath: "transform.translation.x")
animation.timingFunction = CAMediaTimingFunction(name: CAMediaTimingFunctionName.linear)
animation.repeatCount = 3
animation.duration = 0.2/TimeInterval(animation.repeatCount)
animation.duration = 0.2 / TimeInterval(animation.repeatCount)
animation.autoreverses = true
animation.values = [3, -3]
layer.add(animation, forKey: "shake")

View file

@ -7,7 +7,8 @@
//
extension UIViewController {
@objc public func textFieldShouldReturn(_ textField: UITextField) -> Bool {
@objc
public func textFieldShouldReturn(_ textField: UITextField) -> Bool {
if textField.nextField != nil {
textField.nextField?.becomeFirstResponder()
} else {

View file

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

View file

@ -9,9 +9,8 @@
import KeychainAccess
public class AppKeychain: KeyStore {
public static let shared = AppKeychain()
private let keychain = Keychain(service: Globals.bundleIdentifier, accessGroup: Globals.groupIdentifier)
.accessibility(.whenUnlockedThisDeviceOnly)
.synchronizable(false)
@ -25,15 +24,15 @@ public class AppKeychain: KeyStore {
}
public func contains(key: String) -> Bool {
return (try? keychain.contains(key)) ?? false
(try? keychain.contains(key)) ?? false
}
public func get(for key: String) -> Data? {
return try? keychain.getData(key)
try? keychain.getData(key)
}
public func get(for key: String) -> String? {
return try? keychain.getString(key)
try? keychain.getString(key)
}
public func removeContent(for key: String) {

View file

@ -10,7 +10,6 @@ import Foundation
// https://gist.github.com/NikolaiRuhe/eeb135d20c84a7097516
public extension FileManager {
/// This method calculates the accumulated size of a directory on the volume in bytes.
///
/// As there's no simple way to get this information from the file system it has to crawl the entire hierarchy,
@ -19,8 +18,7 @@ public extension FileManager {
///
/// - note: There are a couple of oddities that are not taken into account (like symbolic links, meta data of
/// directories, hard links, ...).
func allocatedSizeOfDirectoryAtURL(directoryURL : URL) throws -> UInt64 {
func allocatedSizeOfDirectoryAtURL(directoryURL: URL) throws -> UInt64 {
// We'll sum up content size here:
var accumulatedSize = UInt64(0)
@ -29,7 +27,7 @@ public extension FileManager {
URLResourceKey.isRegularFileKey,
URLResourceKey.fileAllocatedSizeKey,
URLResourceKey.totalFileAllocatedSizeKey,
]
]
// The error handler simply signals errors to outside code.
var errorDidOccur: Error?
@ -38,7 +36,6 @@ public extension FileManager {
return false
}
// We have to enumerate all directory contents, including subdirectories.
let enumerator = self.enumerator(at: directoryURL,
includingPropertiesForKeys: prefetchedProperties,
@ -91,4 +88,3 @@ public extension FileManager {
return accumulatedSize
}
}

View file

@ -32,7 +32,7 @@ public final class Globals {
public static let gitPassword = "gitPassword"
public static let gitSSHPrivateKeyPassphrase = "gitSSHPrivateKeyPassphrase"
public static let pgpKeyPassphrase = "pgpKeyPassphrase"
public static let gitSignatureDefaultName = "Pass for iOS"
public static let gitSignatureDefaultEmail = "user@passforios"
@ -43,14 +43,15 @@ public final class Globals {
// UI related
public static let tableCellButtonSize = CGFloat(20.0)
private init() { }
private init() {}
}
public extension Bundle {
var releaseVersionNumber: String? {
return infoDictionary?["CFBundleShortVersionString"] as? String
infoDictionary?["CFBundleShortVersionString"] as? String
}
var buildVersionNumber: String? {
return infoDictionary?["CFBundleVersion"] as? String
infoDictionary?["CFBundleVersion"] as? String
}
}

View file

@ -45,6 +45,6 @@ public class KeyFileManager {
}
public func doesKeyFileExist() -> Bool {
return FileManager.default.fileExists(atPath: keyPath)
FileManager.default.fileExists(atPath: keyPath)
}
}

View file

@ -7,7 +7,6 @@
//
public class Utils {
public static func copyToPasteboard(textToCopy: String?) {
guard textToCopy != nil else {
return
@ -15,8 +14,8 @@ public class Utils {
UIPasteboard.general.string = textToCopy
}
public static func attributedPassword(plainPassword: String) -> NSAttributedString{
let attributedPassword = NSMutableAttributedString.init(string: plainPassword)
public static func attributedPassword(plainPassword: String) -> NSAttributedString {
let attributedPassword = NSMutableAttributedString(string: plainPassword)
// draw all digits in the password into red
// draw all punctuation characters in the password into blue
for (index, element) in plainPassword.unicodeScalars.enumerated() {
@ -40,24 +39,24 @@ public class Utils {
}
public static func createRequestPGPKeyPassphraseHandler(controller: UIViewController) -> (String) -> String {
return { keyID in
{ keyID in
let sem = DispatchSemaphore(value: 0)
var passphrase = ""
DispatchQueue.main.async {
let title = "Passphrase".localize() + " (\(keyID.suffix(8)))"
let message = "FillInPgpPassphrase.".localize()
let alert = UIAlertController(title: title, message: message, preferredStyle: .alert)
alert.addAction(UIAlertAction.ok() { _ in
alert.addAction(UIAlertAction.ok { _ in
passphrase = alert.textFields?.first?.text ?? ""
sem.signal()
})
alert.addTextField() { textField in
alert.addTextField { textField in
textField.text = AppKeychain.shared.get(for: AppKeychain.getPGPKeyPassphraseKey(keyID: keyID)) ?? ""
textField.isSecureTextEntry = true
}
controller.present(alert, animated: true)
}
let _ = sem.wait(timeout: DispatchTime.distantFuture)
_ = sem.wait(timeout: DispatchTime.distantFuture)
if Defaults.isRememberPGPPassphraseOn {
AppKeychain.shared.add(string: passphrase, for: AppKeychain.getPGPKeyPassphraseKey(keyID: keyID))
}
@ -65,4 +64,3 @@ public class Utils {
}
}
}

View file

@ -25,7 +25,7 @@ public struct GitCredential {
public func credentialProvider(requestCredentialPassword: @escaping (Credential, String?) -> String?) throws -> GTCredentialProvider {
var attempts = 0
return GTCredentialProvider { (_, _, _) -> (GTCredential?) in
var credential: GTCredential? = nil
var credential: GTCredential?
switch self.credential {
case let .http(userName):
@ -52,7 +52,7 @@ public struct GitCredential {
return nil
}
var lastPassword = self.passwordStore.gitSSHPrivateKeyPassphrase
if lastPassword == nil || attempts != 0 {
if lastPassword == nil || attempts != 0 {
if let requestedPassword = requestCredentialPassword(self.credential, lastPassword) {
if Defaults.isRememberGitCredentialPassphraseOn {
self.passwordStore.gitSSHPrivateKeyPassphrase = requestedPassword
@ -72,10 +72,9 @@ public struct GitCredential {
public func delete() {
switch credential {
case .http:
self.passwordStore.gitPassword = nil
passwordStore.gitPassword = nil
case .ssh:
self.passwordStore.gitSSHPrivateKeyPassphrase = nil
passwordStore.gitSSHPrivateKeyPassphrase = nil
}
}
}

View file

@ -23,7 +23,7 @@ public class PasscodeLock {
}
public var hasPasscode: Bool {
return passcode != nil
passcode != nil
}
public func save(passcode: String) {
@ -32,7 +32,7 @@ public class PasscodeLock {
}
public func check(passcode: String) -> Bool {
return self.passcode == passcode
self.passcode == passcode
}
public func delete() {

View file

@ -6,11 +6,10 @@
// Copyright © 2017 Bob Sun. All rights reserved.
//
import OneTimePassword
import Base32
import OneTimePassword
public class Password {
public var name: String
public var url: URL
public var plainText: String
@ -29,47 +28,47 @@ public class Password {
}
public var namePath: String {
return url.deletingPathExtension().path
url.deletingPathExtension().path
}
public var nameFromPath: String? {
return url.deletingPathExtension().path.split(separator: "/").last.map { String($0) }
url.deletingPathExtension().path.split(separator: "/").last.map { String($0) }
}
public var password: String {
return parser.firstLine
parser.firstLine
}
public var plainData: Data {
return plainText.data(using: .utf8)!
plainText.data(using: .utf8)!
}
public var additionsPlainText: String {
return parser.additionsSection
parser.additionsSection
}
public var username: String? {
return getAdditionValue(withKey: Constants.USERNAME_KEYWORD)
getAdditionValue(withKey: Constants.USERNAME_KEYWORD)
}
public var login: String? {
return getAdditionValue(withKey: Constants.LOGIN_KEYWORD)
getAdditionValue(withKey: Constants.LOGIN_KEYWORD)
}
public var urlString: String? {
return getAdditionValue(withKey: Constants.URL_KEYWORD)
getAdditionValue(withKey: Constants.URL_KEYWORD)
}
public var currentOtp: String? {
return otpToken?.currentPassword
otpToken?.currentPassword
}
public var numberOfUnknowns: Int {
return additions.map { $0.title }.filter(Constants.isUnknown).count
additions.map(\.title).filter(Constants.isUnknown).count
}
public var numberOfOtpRelated: Int {
return additions.map { $0.title }.filter(Constants.isOtpKeyword).count - (firstLineIsOTPField ? 1 : 0)
additions.map(\.title).filter(Constants.isOtpKeyword).count - (firstLineIsOTPField ? 1 : 0)
}
public init(name: String, url: URL, plainText: String) {
@ -119,7 +118,7 @@ public class Password {
}
public func getFilteredAdditions() -> [AdditionField] {
return additions.filter { field in
additions.filter { field in
let title = field.title.lowercased()
return title != Constants.USERNAME_KEYWORD
&& title != Constants.LOGIN_KEYWORD
@ -194,12 +193,12 @@ public class Password {
newOtpauth?.append("&secret=")
newOtpauth?.append(MF_Base32Codec.base32String(from: otpToken?.generator.secret))
var lines : [String] = []
self.plainText.enumerateLines() { line, _ in
var lines: [String] = []
plainText.enumerateLines { line, _ in
let (key, _) = Parser.getKeyValuePair(from: line)
if !Constants.OTP_KEYWORDS.contains(key ?? "") {
lines.append(line)
} else if key == Constants.OTPAUTH && newOtpauth != nil {
} else if key == Constants.OTPAUTH, newOtpauth != nil {
lines.append(newOtpauth!)
// set to nil to prevent duplication
newOtpauth = nil
@ -208,10 +207,10 @@ public class Password {
if newOtpauth != nil {
lines.append(newOtpauth!)
}
self.updatePassword(name: self.name, url: self.url, plainText: lines.joined(separator: "\n"))
updatePassword(name: name, url: url, plainText: lines.joined(separator: "\n"))
// get and return the password
return self.otpToken?.currentPassword
return otpToken?.currentPassword
}
public func getUsernameForCompletion() -> String {

View file

@ -10,7 +10,6 @@ import Foundation
import SwiftyUserDefaults
extension PasswordEntity {
public var nameWithCategory: String {
if let p = path, p.hasSuffix(".gpg") {
return String(p.prefix(upTo: p.index(p.endIndex, offsetBy: -4)))
@ -19,7 +18,7 @@ extension PasswordEntity {
}
public func getCategoryText() -> String {
return getCategoryArray().joined(separator: " > ")
getCategoryArray().joined(separator: " > ")
}
public func getCategoryArray() -> [String] {
@ -44,17 +43,16 @@ extension PasswordEntity {
// manually write models instead auto generation.
public func getImage() -> Data? {
return image
image
}
public func getName() -> String {
// unwrap non-optional core data
return name ?? ""
name ?? ""
}
public func getPath() -> String {
// unwrap non-optional core data
return path ?? ""
path ?? ""
}
}

View file

@ -6,12 +6,12 @@
// Copyright © 2017 Bob Sun. All rights reserved.
//
import Foundation
import CoreData
import UIKit
import SwiftyUserDefaults
import ObjectiveGit
import Foundation
import KeychainAccess
import ObjectiveGit
import SwiftyUserDefaults
import UIKit
public class PasswordStore {
public static let shared = PasswordStore()
@ -21,43 +21,40 @@ public class PasswordStore {
dateFormatter.timeStyle = .short
return dateFormatter
}()
public var storeURL: URL
public var tempStoreURL: URL {
get {
URL(fileURLWithPath: "\(storeURL.path)-temp")
}
URL(fileURLWithPath: "\(storeURL.path)-temp")
}
public var storeRepository: GTRepository?
public var gitSignatureForNow: GTSignature? {
get {
let gitSignatureName = Defaults.gitSignatureName ?? Globals.gitSignatureDefaultName
let gitSignatureEmail = Defaults.gitSignatureEmail ?? Globals.gitSignatureDefaultEmail
return GTSignature(name: gitSignatureName, email: gitSignatureEmail, time: Date())
}
let gitSignatureName = Defaults.gitSignatureName ?? Globals.gitSignatureDefaultName
let gitSignatureEmail = Defaults.gitSignatureEmail ?? Globals.gitSignatureDefaultEmail
return GTSignature(name: gitSignatureName, email: gitSignatureEmail, time: Date())
}
public var gitPassword: String? {
set {
AppKeychain.shared.add(string: newValue, for: Globals.gitPassword)
}
get {
return AppKeychain.shared.get(for: Globals.gitPassword)
AppKeychain.shared.get(for: Globals.gitPassword)
}
}
public var gitSSHPrivateKeyPassphrase: String? {
set {
AppKeychain.shared.add(string: newValue, for: Globals.gitSSHPrivateKeyPassphrase)
}
get {
return AppKeychain.shared.get(for: Globals.gitSSHPrivateKeyPassphrase)
AppKeychain.shared.get(for: Globals.gitSSHPrivateKeyPassphrase)
}
}
private let fm = FileManager.default
lazy private var context: NSManagedObjectContext = {
private lazy var context: NSManagedObjectContext = {
let modelURL = Bundle(identifier: Globals.passKitBundleIdentifier)!.url(forResource: "pass", withExtension: "momd")!
let managedObjectModel = NSManagedObjectModel(contentsOf: modelURL)
let container = NSPersistentContainer(name: "pass", managedObjectModel: managedObjectModel!)
@ -65,11 +62,11 @@ public class PasswordStore {
try! FileManager.default.createDirectory(atPath: Globals.documentPath, withIntermediateDirectories: true, attributes: nil)
}
container.persistentStoreDescriptions = [NSPersistentStoreDescription(url: URL(fileURLWithPath: Globals.dbPath))]
container.loadPersistentStores(completionHandler: { (storeDescription, error) in
container.loadPersistentStores(completionHandler: { _, error in
if let error = error as NSError? {
// Replace this implementation with code to handle the error appropriately.
// fatalError() causes the application to generate a crash log and terminate. You should not use this function in a shipping application, although it may be useful during development.
/*
Typical reasons for an error here include:
* The parent directory does not exist, cannot be created, or disallows writing.
@ -83,36 +80,36 @@ public class PasswordStore {
})
return container.viewContext
}()
public var numberOfPasswords : Int {
return self.fetchPasswordEntityCoreData(withDir: false).count
public var numberOfPasswords: Int {
fetchPasswordEntityCoreData(withDir: false).count
}
public var sizeOfRepositoryByteCount : UInt64 {
return (try? fm.allocatedSizeOfDirectoryAtURL(directoryURL: self.storeURL)) ?? 0
public var sizeOfRepositoryByteCount: UInt64 {
(try? fm.allocatedSizeOfDirectoryAtURL(directoryURL: storeURL)) ?? 0
}
public var numberOfLocalCommits: Int {
return (try? getLocalCommits())?.flatMap { $0.count } ?? 0
(try? getLocalCommits()).map(\.count) ?? 0
}
public var lastSyncedTime: Date? {
return Defaults.lastSyncedTime
Defaults.lastSyncedTime
}
public var numberOfCommits: UInt? {
return storeRepository?.numberOfCommits(inCurrentBranch: nil)
storeRepository?.numberOfCommits(inCurrentBranch: nil)
}
init(url: URL = URL(fileURLWithPath: "\(Globals.repositoryPath)")) {
storeURL = url
self.storeURL = url
// Migration
importExistingKeysIntoKeychain()
do {
if fm.fileExists(atPath: storeURL.path) {
try storeRepository = GTRepository.init(url: storeURL)
try self.storeRepository = GTRepository(url: storeURL)
}
} catch {
print(error)
@ -128,12 +125,12 @@ public class PasswordStore {
Defaults.remove(\.pgpPrivateKeyArmor)
Defaults.remove(\.gitSSHPrivateKeyArmor)
}
public func repositoryExists() -> Bool {
let fm = FileManager()
return fm.fileExists(atPath: Globals.repositoryPath)
}
public func passwordExisted(password: Password) -> Bool {
let passwordEntityFetchRequest = NSFetchRequest<NSFetchRequestResult>(entityName: "PasswordEntity")
do {
@ -148,7 +145,7 @@ public class PasswordStore {
fatalError("FailedToFetchPasswordEntities".localize(error))
}
}
public func passwordEntityExisted(path: String) -> Bool {
let passwordEntityFetchRequest = NSFetchRequest<NSFetchRequestResult>(entityName: "PasswordEntity")
do {
@ -163,7 +160,7 @@ public class PasswordStore {
fatalError("FailedToFetchPasswordEntities".localize(error))
}
}
public func getPasswordEntity(by path: String, isDir: Bool) -> PasswordEntity? {
let passwordEntityFetchRequest = NSFetchRequest<NSFetchRequestResult>(entityName: "PasswordEntity")
do {
@ -173,7 +170,7 @@ public class PasswordStore {
fatalError("FailedToFetchPasswordEntities".localize(error))
}
}
public func cloneRepository(remoteRepoURL: URL,
credential: GitCredential,
branchName: String,
@ -183,23 +180,23 @@ public class PasswordStore {
do {
let credentialProvider = try credential.credentialProvider(requestCredentialPassword: requestCredentialPassword)
let options = [GTRepositoryCloneOptionsCredentialProvider: credentialProvider]
try self.cloneRepository(remoteRepoURL: remoteRepoURL, options: options, branchName: branchName, transferProgressBlock: transferProgressBlock, checkoutProgressBlock: checkoutProgressBlock)
try cloneRepository(remoteRepoURL: remoteRepoURL, options: options, branchName: branchName, transferProgressBlock: transferProgressBlock, checkoutProgressBlock: checkoutProgressBlock)
} catch {
credential.delete()
throw(error)
throw (error)
}
}
public func cloneRepository(remoteRepoURL: URL,
options: [AnyHashable : Any]? = nil,
options: [AnyHashable: Any]? = nil,
branchName: String,
transferProgressBlock: @escaping (UnsafePointer<git_transfer_progress>, UnsafeMutablePointer<ObjCBool>) -> Void,
checkoutProgressBlock: @escaping (String, UInt, UInt) -> Void,
completion: @escaping () -> Void = {}) throws {
try? fm.removeItem(at: storeURL)
try? fm.removeItem(at: tempStoreURL)
self.gitPassword = nil
self.gitSSHPrivateKeyPassphrase = nil
gitPassword = nil
gitSSHPrivateKeyPassphrase = nil
do {
storeRepository = try GTRepository.clone(from: remoteRepoURL,
toWorkingDirectory: tempStoreURL,
@ -216,7 +213,7 @@ public class PasswordStore {
self.deleteCoreData(entityName: "PasswordEntity")
NotificationCenter.default.post(name: .passwordStoreUpdated, object: nil)
}
throw(error)
throw (error)
}
Defaults.lastSyncedTime = Date()
DispatchQueue.main.async {
@ -225,7 +222,7 @@ public class PasswordStore {
completion()
}
}
private func checkoutAndChangeBranch(withName localBranchName: String, progressBlock: @escaping (String, UInt, UInt) -> Void) throws {
guard let storeRepository = storeRepository else {
throw AppError.RepositoryNotSet
@ -241,7 +238,7 @@ public class PasswordStore {
try storeRepository.checkoutReference(localBranch.reference, options: checkoutOptions)
try storeRepository.moveHEAD(to: localBranch.reference)
}
public func pullRepository(credential: GitCredential,
requestCredentialPassword: @escaping (GitCredential.Credential, String?) -> String?,
progressBlock: @escaping (UnsafePointer<git_transfer_progress>, UnsafeMutablePointer<ObjCBool>) -> Void) throws {
@ -253,30 +250,30 @@ public class PasswordStore {
let remote = try GTRemote(name: "origin", in: storeRepository)
try storeRepository.pull(storeRepository.currentBranch(), from: remote, withOptions: options, progress: progressBlock)
Defaults.lastSyncedTime = Date()
self.setAllSynced()
setAllSynced()
DispatchQueue.main.async {
self.updatePasswordEntityCoreData()
NotificationCenter.default.post(name: .passwordStoreUpdated, object: nil)
}
}
private func updatePasswordEntityCoreData() {
deleteCoreData(entityName: "PasswordEntity")
do {
var q = try fm.contentsOfDirectory(atPath: self.storeURL.path).filter {
var q = try fm.contentsOfDirectory(atPath: storeURL.path).filter {
!$0.hasPrefix(".")
}.map { (filename) -> PasswordEntity in
let passwordEntity = NSEntityDescription.insertNewObject(forEntityName: "PasswordEntity", into: context) as! PasswordEntity
if filename.hasSuffix(".gpg") {
passwordEntity.name = String(filename.prefix(upTo: filename.index(filename.endIndex, offsetBy: -4)))
} else {
passwordEntity.name = filename
}
passwordEntity.path = filename
passwordEntity.parent = nil
return passwordEntity
}.map { (filename) -> PasswordEntity in
let passwordEntity = NSEntityDescription.insertNewObject(forEntityName: "PasswordEntity", into: context) as! PasswordEntity
if filename.hasSuffix(".gpg") {
passwordEntity.name = String(filename.prefix(upTo: filename.index(filename.endIndex, offsetBy: -4)))
} else {
passwordEntity.name = filename
}
passwordEntity.path = filename
passwordEntity.parent = nil
return passwordEntity
}
while q.count > 0 {
while !q.isEmpty {
let e = q.first!
q.remove(at: 0)
guard !e.name!.hasPrefix(".") else {
@ -309,9 +306,9 @@ public class PasswordStore {
} catch {
print(error)
}
self.saveUpdatedContext()
saveUpdatedContext()
}
public func getRecentCommits(count: Int) throws -> [GTCommit] {
guard let storeRepository = storeRepository else {
return []
@ -328,7 +325,7 @@ public class PasswordStore {
}
return commits
}
public func fetchPasswordEntityCoreData(parent: PasswordEntity?) -> [PasswordEntity] {
let passwordEntityFetch = NSFetchRequest<NSFetchRequestResult>(entityName: "PasswordEntity")
do {
@ -339,7 +336,7 @@ public class PasswordStore {
fatalError("FailedToFetchPasswords".localize(error))
}
}
public func fetchPasswordEntityCoreData(withDir: Bool) -> [PasswordEntity] {
let passwordEntityFetch = NSFetchRequest<NSFetchRequestResult>(entityName: "PasswordEntity")
do {
@ -352,8 +349,7 @@ public class PasswordStore {
fatalError("FailedToFetchPasswords".localize(error))
}
}
public func fetchUnsyncedPasswords() -> [PasswordEntity] {
let passwordEntityFetchRequest = NSFetchRequest<NSFetchRequestResult>(entityName: "PasswordEntity")
passwordEntityFetchRequest.predicate = NSPredicate(format: "synced = %i", 0)
@ -364,17 +360,17 @@ public class PasswordStore {
fatalError("FailedToFetchPasswords".localize(error))
}
}
public func setAllSynced() {
let passwordEntities = fetchUnsyncedPasswords()
if passwordEntities.count > 0 {
if !passwordEntities.isEmpty {
for passwordEntity in passwordEntities {
passwordEntity.synced = true
}
self.saveUpdatedContext()
saveUpdatedContext()
}
}
public func getLatestUpdateInfo(filename: String) -> String {
guard let storeRepository = storeRepository else {
return "Unknown".localize()
@ -383,7 +379,7 @@ public class PasswordStore {
let latestCommitTime = blameHunks.map({
$0.finalSignature?.time?.timeIntervalSince1970 ?? 0
}).max() else {
return "Unknown".localize()
return "Unknown".localize()
}
let lastCommitDate = Date(timeIntervalSince1970: latestCommitTime)
if Date().timeIntervalSince(lastCommitDate) <= 60 {
@ -391,10 +387,9 @@ public class PasswordStore {
}
return PasswordStore.dateFormatter.string(from: lastCommitDate)
}
public func updateRemoteRepo() {
}
public func updateRemoteRepo() {}
private func gitAdd(path: String) throws {
guard let storeRepository = storeRepository else {
throw AppError.RepositoryNotSet
@ -402,7 +397,7 @@ public class PasswordStore {
try storeRepository.index().addFile(path)
try storeRepository.index().write()
}
private func gitRm(path: String) throws {
guard let storeRepository = storeRepository else {
throw AppError.RepositoryNotSet
@ -414,7 +409,7 @@ public class PasswordStore {
try storeRepository.index().removeFile(path)
try storeRepository.index().write()
}
private func deleteDirectoryTree(at url: URL) throws {
var tempURL = storeURL.appendingPathComponent(url.deletingLastPathComponent().path)
var count = try fm.contentsOfDirectory(atPath: tempURL.path).count
@ -424,12 +419,12 @@ public class PasswordStore {
count = try fm.contentsOfDirectory(atPath: tempURL.path).count
}
}
private func createDirectoryTree(at url: URL) throws {
let tempURL = storeURL.appendingPathComponent(url.deletingLastPathComponent().path)
try fm.createDirectory(at: tempURL, withIntermediateDirectories: true, attributes: nil)
}
private func gitMv(from: String, to: String) throws {
let fromURL = storeURL.appendingPathComponent(from)
let toURL = storeURL.appendingPathComponent(to)
@ -437,7 +432,7 @@ public class PasswordStore {
try gitAdd(path: to)
try gitRm(path: from)
}
private func gitCommit(message: String) throws -> GTCommit? {
guard let storeRepository = storeRepository else {
throw AppError.RepositoryNotSet
@ -453,7 +448,7 @@ public class PasswordStore {
let commit = try storeRepository.createCommit(with: newTree, message: message, author: signature, committer: signature, parents: [parent], updatingReferenceNamed: headReference.name)
return commit
}
private func getLocalBranch(withName branchName: String) throws -> GTBranch? {
guard let storeRepository = storeRepository else {
throw AppError.RepositoryNotSet
@ -462,7 +457,7 @@ public class PasswordStore {
let branches = try storeRepository.branches(withPrefix: reference)
return branches.first
}
public func pushRepository(credential: GitCredential, requestCredentialPassword: @escaping (GitCredential.Credential, String?) -> String?, transferProgressBlock: @escaping (UInt32, UInt32, Int, UnsafeMutablePointer<ObjCBool>) -> Void) throws {
guard let storeRepository = storeRepository else {
throw AppError.RepositoryNotSet
@ -477,12 +472,12 @@ public class PasswordStore {
throw AppError.GitPushNotSuccessful
}
}
private func addPasswordEntities(password: Password) throws -> PasswordEntity? {
guard !passwordExisted(password: password) else {
throw AppError.PasswordDuplicated
}
var passwordURL = password.url
var previousPathLength = Int.max
var paths: [String] = []
@ -490,20 +485,20 @@ public class PasswordStore {
paths.append(passwordURL.path)
passwordURL = passwordURL.deletingLastPathComponent()
// better identify errors before saving a new password
if passwordURL.path != "." && passwordURL.path.count >= previousPathLength {
if passwordURL.path != ".", passwordURL.path.count >= previousPathLength {
throw AppError.WrongPasswordFilename
}
previousPathLength = passwordURL.path.count
}
paths.reverse()
var parentPasswordEntity: PasswordEntity? = nil
var parentPasswordEntity: PasswordEntity?
for path in paths {
let isDir = !path.hasSuffix(".gpg")
if let passwordEntity = getPasswordEntity(by: path, isDir: isDir) {
passwordEntity.synced = false
parentPasswordEntity = passwordEntity
} else {
let passwordEntity = NSEntityDescription.insertNewObject(forEntityName: "PasswordEntity", into: self.context) as! PasswordEntity
let passwordEntity = NSEntityDescription.insertNewObject(forEntityName: "PasswordEntity", into: context) as! PasswordEntity
let pathURL = URL(string: path.stringByAddingPercentEncodingForRFC3986()!)!
if isDir {
passwordEntity.name = pathURL.lastPathComponent
@ -518,72 +513,73 @@ public class PasswordStore {
}
}
self.saveUpdatedContext()
saveUpdatedContext()
return parentPasswordEntity
}
public func add(password: Password, keyID: String? = nil) throws -> PasswordEntity? {
try createDirectoryTree(at: password.url)
let saveURL = storeURL.appendingPathComponent(password.url.path)
try self.encrypt(password: password, keyID: keyID).write(to: saveURL)
try encrypt(password: password, keyID: keyID).write(to: saveURL)
try gitAdd(path: password.url.path)
let _ = try gitCommit(message: "AddPassword.".localize(password.url.deletingPathExtension().path))
_ = try gitCommit(message: "AddPassword.".localize(password.url.deletingPathExtension().path))
let newPasswordEntity = try addPasswordEntities(password: password)
NotificationCenter.default.post(name: .passwordStoreUpdated, object: nil)
return newPasswordEntity
}
public func delete(passwordEntity: PasswordEntity) throws {
let deletedFileURL = try passwordEntity.getURL()
try gitRm(path: deletedFileURL.path)
try deletePasswordEntities(passwordEntity: passwordEntity)
try deleteDirectoryTree(at: deletedFileURL)
let _ = try gitCommit(message: "RemovePassword.".localize(deletedFileURL.deletingPathExtension().path.removingPercentEncoding!))
_ = try gitCommit(message: "RemovePassword.".localize(deletedFileURL.deletingPathExtension().path.removingPercentEncoding!))
NotificationCenter.default.post(name: .passwordStoreUpdated, object: nil)
}
public func edit(passwordEntity: PasswordEntity, password: Password, keyID: String? = nil) throws -> PasswordEntity? {
var newPasswordEntity: PasswordEntity? = passwordEntity
let url = try passwordEntity.getURL()
if password.changed&PasswordChange.content.rawValue != 0 {
if password.changed & PasswordChange.content.rawValue != 0 {
let saveURL = storeURL.appendingPathComponent(url.path)
try self.encrypt(password: password, keyID: keyID).write(to: saveURL)
try encrypt(password: password, keyID: keyID).write(to: saveURL)
try gitAdd(path: url.path)
let _ = try gitCommit(message: "EditPassword.".localize(url.deletingPathExtension().path.removingPercentEncoding!))
_ = try gitCommit(message: "EditPassword.".localize(url.deletingPathExtension().path.removingPercentEncoding!))
newPasswordEntity = passwordEntity
newPasswordEntity?.synced = false
self.saveUpdatedContext()
saveUpdatedContext()
}
if password.changed&PasswordChange.path.rawValue != 0 {
if password.changed & PasswordChange.path.rawValue != 0 {
let deletedFileURL = url
// add
try createDirectoryTree(at: password.url)
newPasswordEntity = try addPasswordEntities(password: password)
// mv
try gitMv(from: deletedFileURL.path, to: password.url.path)
// delete
try deleteDirectoryTree(at: deletedFileURL)
try deletePasswordEntities(passwordEntity: passwordEntity)
let _ = try gitCommit(message: "RenamePassword.".localize(deletedFileURL.deletingPathExtension().path.removingPercentEncoding!, password.url.deletingPathExtension().path.removingPercentEncoding!))
_ = try gitCommit(message: "RenamePassword.".localize(deletedFileURL.deletingPathExtension().path.removingPercentEncoding!, password.url.deletingPathExtension().path.removingPercentEncoding!))
}
NotificationCenter.default.post(name: .passwordStoreUpdated, object: nil)
return newPasswordEntity
}
private func deletePasswordEntities(passwordEntity: PasswordEntity) throws {
var current: PasswordEntity? = passwordEntity
while current != nil && (current!.children!.count == 0 || !current!.isDir) {
// swiftformat:disable:next isEmpty
while current != nil, current!.children!.count == 0 || !current!.isDir {
let parent = current!.parent
self.context.delete(current!)
context.delete(current!)
current = parent
}
self.saveUpdatedContext()
saveUpdatedContext()
}
public func saveUpdatedContext() {
do {
if context.hasChanges {
@ -593,11 +589,11 @@ public class PasswordStore {
fatalError("FailureToSaveContext".localize(error))
}
}
public func deleteCoreData(entityName: String) {
let deleteFetchRequest = NSFetchRequest<NSFetchRequestResult>(entityName: entityName)
let deleteRequest = NSBatchDeleteRequest(fetchRequest: deleteFetchRequest)
do {
try context.execute(deleteRequest)
try context.save()
@ -606,7 +602,7 @@ public class PasswordStore {
print(error)
}
}
public func updateImage(passwordEntity: PasswordEntity, image: Data?) {
guard let image = image else {
return
@ -625,33 +621,33 @@ public class PasswordStore {
}
}
}
public func erase() {
// Delete files.
try? fm.removeItem(at: storeURL)
try? fm.removeItem(at: tempStoreURL)
// Delete PGP key, SSH key and other secrets from the keychain.
AppKeychain.shared.removeAllContent()
// Delete core data.
deleteCoreData(entityName: "PasswordEntity")
// Delete default settings.
Defaults.removeAll()
// Clean up variables inside PasswordStore.
storeRepository = nil
// Delete cache explicitly.
PasscodeLock.shared.delete()
PGPAgent.shared.uninitKeys()
// Broadcast.
NotificationCenter.default.post(name: .passwordStoreUpdated, object: nil)
NotificationCenter.default.post(name: .passwordStoreErased, object: nil)
}
// return the number of discarded commits
public func reset() throws -> Int {
guard let storeRepository = storeRepository else {
@ -659,26 +655,25 @@ public class PasswordStore {
}
// get a list of local commits
if let localCommits = try getLocalCommits(),
localCommits.count > 0 {
!localCommits.isEmpty {
// get the oldest local commit
guard let firstLocalCommit = localCommits.last,
firstLocalCommit.parents.count == 1,
let newHead = firstLocalCommit.parents.first else {
throw AppError.GitReset
throw AppError.GitReset
}
try storeRepository.reset(to: newHead, resetType: .hard)
self.setAllSynced()
self.updatePasswordEntityCoreData()
setAllSynced()
updatePasswordEntityCoreData()
NotificationCenter.default.post(name: .passwordStoreUpdated, object: nil)
NotificationCenter.default.post(name: .passwordStoreChangeDiscarded, object: nil)
return localCommits.count
} else {
return 0 // no new commit
return 0 // no new commit
}
}
private func getLocalCommits() throws -> [GTCommit]? {
guard let storeRepository = storeRepository else {
throw AppError.RepositoryNotSet
@ -692,7 +687,7 @@ public class PasswordStore {
guard remoteBranch.oid != nil else {
throw AppError.RepositoryRemoteBranchNotFound(remoteBranchName)
}
// get a list of local commits
return try storeRepository.localCommitsRelative(toRemoteBranch: remoteBranch)
}
@ -708,13 +703,13 @@ public class PasswordStore {
let url = try passwordEntity.getURL()
return Password(name: passwordEntity.getName(), url: url, plainText: plainText)
}
public func encrypt(password: Password, keyID: String? = nil) throws -> Data {
let encryptedDataPath = storeURL.appendingPathComponent(password.url.path)
let keyID = keyID ?? findGPGID(from: encryptedDataPath)
return try PGPAgent.shared.encrypt(plainData: password.plainData, keyID: keyID)
}
public func removeGitSSHKeys() {
try? fm.removeItem(atPath: Globals.gitSSHPrivateKeyPath)
Defaults.remove(\.gitSSHKeySource)
@ -727,8 +722,8 @@ public class PasswordStore {
public func findGPGID(from url: URL) -> String {
var path = url
while !FileManager.default.fileExists(atPath: path.appendingPathComponent(".gpg-id").path)
&& path.path != "file:///" {
while !FileManager.default.fileExists(atPath: path.appendingPathComponent(".gpg-id").path),
path.path != "file:///" {
path = path.deletingLastPathComponent()
}
path = path.appendingPathComponent(".gpg-id")

View file

@ -14,7 +14,7 @@ public class PasswordTableEntry: NSObject {
public let isDir: Bool
public let synced: Bool
public let categoryText: String
public init(_ entity: PasswordEntity) {
self.passwordEntity = entity
self.title = entity.name!
@ -22,23 +22,22 @@ public class PasswordTableEntry: NSObject {
self.synced = entity.synced
self.categoryText = entity.getCategoryText()
}
public func match(_ searchText: String) -> Bool {
return PasswordTableEntry.match(nameWithCategory: passwordEntity.nameWithCategory, searchText: searchText)
PasswordTableEntry.match(nameWithCategory: passwordEntity.nameWithCategory, searchText: searchText)
}
public static func match(nameWithCategory: String, searchText: String) -> Bool {
let titleSplit = nameWithCategory.split{ !($0.isLetter || $0.isNumber || $0 == ".") }
let titleSplit = nameWithCategory.split { !($0.isLetter || $0.isNumber || $0 == ".") }
for str in titleSplit {
if (str.localizedCaseInsensitiveContains(searchText)) {
if str.localizedCaseInsensitiveContains(searchText) {
return true
}
if (searchText.localizedCaseInsensitiveContains(str)) {
if searchText.localizedCaseInsensitiveContains(str) {
return true
}
}
return false
}
}

View file

@ -7,7 +7,6 @@
//
public struct AdditionField: Hashable {
public let title: String, content: String
public init(title: String = "", content: String = "") {
@ -16,7 +15,7 @@ public struct AdditionField: Hashable {
}
var asString: String {
return title.isEmpty ? content : title + ": " + content
title.isEmpty ? content : title + ": " + content
}
var asTuple: (String, String) {
@ -25,21 +24,20 @@ public struct AdditionField: Hashable {
}
extension AdditionField {
static func | (left: String, right: AdditionField) -> String {
return left | right.asString
left | right.asString
}
static func | (left: AdditionField, right: String) -> String {
return left.asString | right
left.asString | right
}
static func | (left: AdditionField, right: AdditionField) -> String {
return left.asString | right
left.asString | right
}
}
infix operator =>: MultiplicationPrecedence
public func => (key: String, value: String) -> AdditionField {
return AdditionField(title: key, content: value)
AdditionField(title: key, content: value)
}

View file

@ -7,7 +7,6 @@
//
public struct Constants {
static let OTP_SECRET = "otp_secret"
static let OTP_TYPE = "otp_type"
static let OTP_ALGORITHM = "otp_algorithm"
@ -54,7 +53,7 @@ public struct Constants {
}
static func isOtpKeyword(_ keyword: String) -> Bool {
return OTP_KEYWORDS.contains(keyword.lowercased())
OTP_KEYWORDS.contains(keyword.lowercased())
}
static func isUnknown(_ string: String) -> Bool {
@ -63,10 +62,10 @@ public struct Constants {
}
static func unknown(_ number: UInt) -> String {
return "\(UNKNOWN) \(number)"
"\(UNKNOWN) \(number)"
}
static func getSeparator(breakingLines: Bool) -> String {
return breakingLines ? MULTILINE_WITH_LINE_BREAK_SEPARATOR : MULTILINE_WITHOUT_LINE_BREAK_SEPARATOR
breakingLines ? MULTILINE_WITH_LINE_BREAK_SEPARATOR : MULTILINE_WITHOUT_LINE_BREAK_SEPARATOR
}
}

View file

@ -14,7 +14,7 @@ public enum OTPType: String {
case none = "None"
var description: String {
return rawValue.localize()
rawValue.localize()
}
init(token: Token?) {

View file

@ -7,7 +7,6 @@
//
class Parser {
let firstLine: String
let additionsSection: String
let purgedAdditionalLines: [String]
@ -19,8 +18,8 @@ class Parser {
let splittedPlainText = plainText.splitByNewline()
firstLine = splittedPlainText.first!
additionsSection = splittedPlainText[1...].joined(separator: "\n")
purgedAdditionalLines = splittedPlainText[1...].filter { !$0.isEmpty }
self.additionsSection = splittedPlainText[1...].joined(separator: "\n")
self.purgedAdditionalLines = splittedPlainText[1...].filter { !$0.isEmpty }
}
private func getAdditionFields() -> [AdditionField] {
@ -57,7 +56,7 @@ class Parser {
}
let initialBlanks = String(repeating: Constants.BLANK, count: numberInitialBlanks)
while i < purgedAdditionalLines.count && purgedAdditionalLines[i].starts(with: initialBlanks) {
while i < purgedAdditionalLines.count, purgedAdditionalLines[i].starts(with: initialBlanks) {
result.append(String(purgedAdditionalLines[i].dropFirst(numberInitialBlanks)))
result.append(Constants.getSeparator(breakingLines: !removingLineBreaks))
i += 1

View file

@ -27,7 +27,6 @@ import OneTimePassword
/// * digits: `6` (default: `6`, optional)
///
class TokenBuilder {
private var name: String = ""
private var secret: Data?
private var type: OTPType = .totp
@ -80,7 +79,6 @@ class TokenBuilder {
return self
}
func build() -> Token? {
guard secret != nil, digits != nil else {
return nil

View file

@ -7,7 +7,6 @@
//
public struct PasswordGenerator: Codable {
private static let digits = "0123456789"
private static let letters = "abcdefghijklmnopqrstuvwxyz"
private static let capitalLetters = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
@ -114,7 +113,7 @@ public struct PasswordGenerator: Codable {
}
private func selectRandomly(count: Int, from string: String) -> [Character] {
return (0 ..< count).map { _ in string.randomElement()! }
(0 ..< count).map { _ in string.randomElement()! }
}
}

View file

@ -13,7 +13,7 @@ public enum PasswordGeneratorFlavor: String {
case xkcd = "XKCD"
public var localized: String {
return rawValue.localize()
rawValue.localize()
}
public var longNameLocalized: String {