Format code with SwiftFormat automatically in every build
This commit is contained in:
parent
f167ab7549
commit
7f9f0e43b2
100 changed files with 1124 additions and 1063 deletions
|
|
@ -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
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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() {
|
||||
|
|
|
|||
|
|
@ -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 {
|
||||
|
|
|
|||
|
|
@ -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 ?? ""
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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")
|
||||
|
|
|
|||
|
|
@ -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
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue