Merge branch 'release/0.2.5'
|
|
@ -10,8 +10,8 @@ Pass is an iOS client compatible with [ZX2C4's Pass command line
|
||||||
application](http://www.passwordstore.org/). It is a password manager using
|
application](http://www.passwordstore.org/). It is a password manager using
|
||||||
GPG for encryption and Git for version control.
|
GPG for encryption and Git for version control.
|
||||||
|
|
||||||
Pass for iOS is under *TestFlight external testing*. Drop me an email for
|
Pass for iOS is under *TestFlight external testing*. Drop an email to
|
||||||
testing. Thank you.
|
`developer@passforios.mssun.me` for testing. Thank you.
|
||||||
|
|
||||||
## Features
|
## Features
|
||||||
|
|
||||||
|
|
@ -20,7 +20,7 @@ testing. Thank you.
|
||||||
- Encrypt and decrypt password entries by PGP keys
|
- Encrypt and decrypt password entries by PGP keys
|
||||||
- Synchronize with your password Git repository
|
- Synchronize with your password Git repository
|
||||||
- User-friendly interface: search, long press to copy, copy and open link, etc.
|
- User-friendly interface: search, long press to copy, copy and open link, etc.
|
||||||
- Support one-time password (OTP) tokens
|
- Support one-time password (OTP) tokens (QR code and otpauth URI)
|
||||||
- Written in Swift
|
- Written in Swift
|
||||||
- No need to jailbreak your devices
|
- No need to jailbreak your devices
|
||||||
- Get from App Store (stay tuned)
|
- Get from App Store (stay tuned)
|
||||||
|
|
|
||||||
|
|
@ -9,6 +9,7 @@
|
||||||
/* Begin PBXBuildFile section */
|
/* Begin PBXBuildFile section */
|
||||||
94BA784B85E071D25EE89B59 /* libPods-pass.a in Frameworks */ = {isa = PBXBuildFile; fileRef = ADCE7A5C3CCC67D7D21BB3C4 /* libPods-pass.a */; };
|
94BA784B85E071D25EE89B59 /* libPods-pass.a in Frameworks */ = {isa = PBXBuildFile; fileRef = ADCE7A5C3CCC67D7D21BB3C4 /* libPods-pass.a */; };
|
||||||
A217ACE21E9AB17C00A1A6CF /* OTPScannerController.swift in Sources */ = {isa = PBXBuildFile; fileRef = A217ACE11E9AB17C00A1A6CF /* OTPScannerController.swift */; };
|
A217ACE21E9AB17C00A1A6CF /* OTPScannerController.swift in Sources */ = {isa = PBXBuildFile; fileRef = A217ACE11E9AB17C00A1A6CF /* OTPScannerController.swift */; };
|
||||||
|
A217ACE41E9BBBBD00A1A6CF /* GitConfigSettingTableViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = A217ACE31E9BBBBD00A1A6CF /* GitConfigSettingTableViewController.swift */; };
|
||||||
A262A58D1E68749C006B0890 /* Base32.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = A262A58C1E68749C006B0890 /* Base32.framework */; };
|
A262A58D1E68749C006B0890 /* Base32.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = A262A58C1E68749C006B0890 /* Base32.framework */; };
|
||||||
A27424D91E7C35960093F436 /* NotificationNames.swift in Sources */ = {isa = PBXBuildFile; fileRef = A27424D81E7C35960093F436 /* NotificationNames.swift */; };
|
A27424D91E7C35960093F436 /* NotificationNames.swift in Sources */ = {isa = PBXBuildFile; fileRef = A27424D81E7C35960093F436 /* NotificationNames.swift */; };
|
||||||
A2802BF91E70813A00879216 /* SliderTableViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = A2802BF71E70813A00879216 /* SliderTableViewCell.swift */; };
|
A2802BF91E70813A00879216 /* SliderTableViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = A2802BF71E70813A00879216 /* SliderTableViewCell.swift */; };
|
||||||
|
|
@ -84,6 +85,7 @@
|
||||||
/* Begin PBXFileReference section */
|
/* Begin PBXFileReference section */
|
||||||
274CCFCF32444A2FF46BE7F4 /* Pods-pass.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-pass.debug.xcconfig"; path = "Pods/Target Support Files/Pods-pass/Pods-pass.debug.xcconfig"; sourceTree = "<group>"; };
|
274CCFCF32444A2FF46BE7F4 /* Pods-pass.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-pass.debug.xcconfig"; path = "Pods/Target Support Files/Pods-pass/Pods-pass.debug.xcconfig"; sourceTree = "<group>"; };
|
||||||
A217ACE11E9AB17C00A1A6CF /* OTPScannerController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = OTPScannerController.swift; sourceTree = "<group>"; };
|
A217ACE11E9AB17C00A1A6CF /* OTPScannerController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = OTPScannerController.swift; sourceTree = "<group>"; };
|
||||||
|
A217ACE31E9BBBBD00A1A6CF /* GitConfigSettingTableViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; lineEnding = 0; path = GitConfigSettingTableViewController.swift; sourceTree = "<group>"; xcLanguageSpecificationIdentifier = xcode.lang.swift; };
|
||||||
A262A58C1E68749C006B0890 /* Base32.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Base32.framework; path = Carthage/Build/iOS/Base32.framework; sourceTree = "<group>"; };
|
A262A58C1E68749C006B0890 /* Base32.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Base32.framework; path = Carthage/Build/iOS/Base32.framework; sourceTree = "<group>"; };
|
||||||
A27424D81E7C35960093F436 /* NotificationNames.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NotificationNames.swift; sourceTree = "<group>"; };
|
A27424D81E7C35960093F436 /* NotificationNames.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NotificationNames.swift; sourceTree = "<group>"; };
|
||||||
A2802BF71E70813A00879216 /* SliderTableViewCell.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SliderTableViewCell.swift; sourceTree = "<group>"; };
|
A2802BF71E70813A00879216 /* SliderTableViewCell.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SliderTableViewCell.swift; sourceTree = "<group>"; };
|
||||||
|
|
@ -107,7 +109,7 @@
|
||||||
DC13B14E1E8640810097803F /* passTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = passTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; };
|
DC13B14E1E8640810097803F /* passTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = passTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||||
DC13B1501E8640810097803F /* passTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = passTests.swift; sourceTree = "<group>"; };
|
DC13B1501E8640810097803F /* passTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = passTests.swift; sourceTree = "<group>"; };
|
||||||
DC13B1521E8640810097803F /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
|
DC13B1521E8640810097803F /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
|
||||||
DC193FF91E49B4430077E0A3 /* AdvancedSettingsTableViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AdvancedSettingsTableViewController.swift; sourceTree = "<group>"; };
|
DC193FF91E49B4430077E0A3 /* AdvancedSettingsTableViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; lineEnding = 0; path = AdvancedSettingsTableViewController.swift; sourceTree = "<group>"; xcLanguageSpecificationIdentifier = xcode.lang.swift; };
|
||||||
DC193FFB1E49E0340077E0A3 /* PasscodeLock.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = PasscodeLock.framework; path = Carthage/Build/iOS/PasscodeLock.framework; sourceTree = "<group>"; };
|
DC193FFB1E49E0340077E0A3 /* PasscodeLock.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = PasscodeLock.framework; path = Carthage/Build/iOS/PasscodeLock.framework; sourceTree = "<group>"; };
|
||||||
DC193FFD1E49E0760077E0A3 /* PasscodeLockRepository.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PasscodeLockRepository.swift; sourceTree = "<group>"; };
|
DC193FFD1E49E0760077E0A3 /* PasscodeLockRepository.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PasscodeLockRepository.swift; sourceTree = "<group>"; };
|
||||||
DC193FFF1E49E1A60077E0A3 /* PasscodeLockConfiguration.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PasscodeLockConfiguration.swift; sourceTree = "<group>"; };
|
DC193FFF1E49E1A60077E0A3 /* PasscodeLockConfiguration.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PasscodeLockConfiguration.swift; sourceTree = "<group>"; };
|
||||||
|
|
@ -128,14 +130,14 @@
|
||||||
DC917BE21E2E8231000FDF54 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
|
DC917BE21E2E8231000FDF54 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
|
||||||
DC962CDE1E4B62C10033B5D8 /* AboutTableViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AboutTableViewController.swift; sourceTree = "<group>"; };
|
DC962CDE1E4B62C10033B5D8 /* AboutTableViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AboutTableViewController.swift; sourceTree = "<group>"; };
|
||||||
DCA049951E3357E000522E8F /* SwiftyUserDefaults.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = SwiftyUserDefaults.framework; path = Carthage/Build/iOS/SwiftyUserDefaults.framework; sourceTree = "<group>"; };
|
DCA049951E3357E000522E8F /* SwiftyUserDefaults.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = SwiftyUserDefaults.framework; path = Carthage/Build/iOS/SwiftyUserDefaults.framework; sourceTree = "<group>"; };
|
||||||
DCA049971E33586A00522E8F /* DefaultsKeys.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DefaultsKeys.swift; sourceTree = "<group>"; };
|
DCA049971E33586A00522E8F /* DefaultsKeys.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; lineEnding = 0; path = DefaultsKeys.swift; sourceTree = "<group>"; xcLanguageSpecificationIdentifier = xcode.lang.swift; };
|
||||||
DCA049991E335CC800522E8F /* GitServerSettingTableViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = GitServerSettingTableViewController.swift; sourceTree = "<group>"; };
|
DCA049991E335CC800522E8F /* GitServerSettingTableViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = GitServerSettingTableViewController.swift; sourceTree = "<group>"; };
|
||||||
DCA0499B1E3362F400522E8F /* PGPKeySettingTableViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PGPKeySettingTableViewController.swift; sourceTree = "<group>"; };
|
DCA0499B1E3362F400522E8F /* PGPKeySettingTableViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PGPKeySettingTableViewController.swift; sourceTree = "<group>"; };
|
||||||
DCA0499D1E33BAC100522E8F /* Globals.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Globals.swift; sourceTree = "<group>"; };
|
DCA0499D1E33BAC100522E8F /* Globals.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Globals.swift; sourceTree = "<group>"; };
|
||||||
DCA671DE1E7A73B100D3ABE1 /* OneTimePassword.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = OneTimePassword.framework; path = Carthage/Build/iOS/OneTimePassword.framework; sourceTree = "<group>"; };
|
DCA671DE1E7A73B100D3ABE1 /* OneTimePassword.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = OneTimePassword.framework; path = Carthage/Build/iOS/OneTimePassword.framework; sourceTree = "<group>"; };
|
||||||
DCA742D91E599ED400D54E16 /* KeychainAccess.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = KeychainAccess.framework; path = Carthage/Build/iOS/KeychainAccess.framework; sourceTree = "<group>"; };
|
DCA742D91E599ED400D54E16 /* KeychainAccess.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = KeychainAccess.framework; path = Carthage/Build/iOS/KeychainAccess.framework; sourceTree = "<group>"; };
|
||||||
DCAAF7441E2FA66800AB94BC /* SettingsTableViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SettingsTableViewController.swift; sourceTree = "<group>"; };
|
DCAAF7441E2FA66800AB94BC /* SettingsTableViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SettingsTableViewController.swift; sourceTree = "<group>"; };
|
||||||
DCC408A31E2FCC9E00F29B0E /* PasswordStore.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PasswordStore.swift; sourceTree = "<group>"; };
|
DCC408A31E2FCC9E00F29B0E /* PasswordStore.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; lineEnding = 0; path = PasswordStore.swift; sourceTree = "<group>"; xcLanguageSpecificationIdentifier = xcode.lang.swift; };
|
||||||
DCC408C61E307DBB00F29B0E /* SVProgressHUD.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = SVProgressHUD.framework; path = Carthage/Build/iOS/SVProgressHUD.framework; sourceTree = "<group>"; };
|
DCC408C61E307DBB00F29B0E /* SVProgressHUD.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = SVProgressHUD.framework; path = Carthage/Build/iOS/SVProgressHUD.framework; sourceTree = "<group>"; };
|
||||||
DCC408C91E30BA1300F29B0E /* pass.xcdatamodel */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcdatamodel; path = pass.xcdatamodel; sourceTree = "<group>"; };
|
DCC408C91E30BA1300F29B0E /* pass.xcdatamodel */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcdatamodel; path = pass.xcdatamodel; sourceTree = "<group>"; };
|
||||||
DCC441511E8F6C06008A90C4 /* RawPasswordViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RawPasswordViewController.swift; sourceTree = "<group>"; };
|
DCC441511E8F6C06008A90C4 /* RawPasswordViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RawPasswordViewController.swift; sourceTree = "<group>"; };
|
||||||
|
|
@ -223,6 +225,7 @@
|
||||||
DCC441531E916382008A90C4 /* GitSSHKeyArmorSettingTableViewController.swift */,
|
DCC441531E916382008A90C4 /* GitSSHKeyArmorSettingTableViewController.swift */,
|
||||||
A2A7813E1E97DBD9001311F5 /* QRScannerController.swift */,
|
A2A7813E1E97DBD9001311F5 /* QRScannerController.swift */,
|
||||||
A217ACE11E9AB17C00A1A6CF /* OTPScannerController.swift */,
|
A217ACE11E9AB17C00A1A6CF /* OTPScannerController.swift */,
|
||||||
|
A217ACE31E9BBBBD00A1A6CF /* GitConfigSettingTableViewController.swift */,
|
||||||
);
|
);
|
||||||
path = Controllers;
|
path = Controllers;
|
||||||
sourceTree = "<group>";
|
sourceTree = "<group>";
|
||||||
|
|
@ -549,6 +552,7 @@
|
||||||
DCFB779E1E4F40C7008DE471 /* FillPasswordTableViewCell.swift in Sources */,
|
DCFB779E1E4F40C7008DE471 /* FillPasswordTableViewCell.swift in Sources */,
|
||||||
A2802BF91E70813A00879216 /* SliderTableViewCell.swift in Sources */,
|
A2802BF91E70813A00879216 /* SliderTableViewCell.swift in Sources */,
|
||||||
DC037CB21E4CAB1700609409 /* AboutRepositoryTableViewController.swift in Sources */,
|
DC037CB21E4CAB1700609409 /* AboutRepositoryTableViewController.swift in Sources */,
|
||||||
|
A217ACE41E9BBBBD00A1A6CF /* GitConfigSettingTableViewController.swift in Sources */,
|
||||||
DC037CB01E4CA51F00609409 /* GeneralSettingsTableViewController.swift in Sources */,
|
DC037CB01E4CA51F00609409 /* GeneralSettingsTableViewController.swift in Sources */,
|
||||||
DC037CB81E4DD1A500609409 /* AddPasswordTableViewController.swift in Sources */,
|
DC037CB81E4DD1A500609409 /* AddPasswordTableViewController.swift in Sources */,
|
||||||
DCC441521E8F6C06008A90C4 /* RawPasswordViewController.swift in Sources */,
|
DCC441521E8F6C06008A90C4 /* RawPasswordViewController.swift in Sources */,
|
||||||
|
|
|
||||||
|
|
@ -1,11 +1,11 @@
|
||||||
<?xml version="1.0" encoding="UTF-8"?>
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="12118" systemVersion="16E195" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" colorMatched="YES" initialViewController="YoR-iB-XAd">
|
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="12120" systemVersion="16F60a" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" colorMatched="YES" initialViewController="YoR-iB-XAd">
|
||||||
<device id="retina5_5" orientation="portrait">
|
<device id="retina5_5" orientation="portrait">
|
||||||
<adaptation id="fullscreen"/>
|
<adaptation id="fullscreen"/>
|
||||||
</device>
|
</device>
|
||||||
<dependencies>
|
<dependencies>
|
||||||
<deployment identifier="iOS"/>
|
<deployment identifier="iOS"/>
|
||||||
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="12086"/>
|
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="12088"/>
|
||||||
<capability name="Constraints to layout margins" minToolsVersion="6.0"/>
|
<capability name="Constraints to layout margins" minToolsVersion="6.0"/>
|
||||||
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
|
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
|
||||||
</dependencies>
|
</dependencies>
|
||||||
|
|
@ -89,11 +89,11 @@
|
||||||
<rect key="frame" x="0.0" y="35" width="414" height="44"/>
|
<rect key="frame" x="0.0" y="35" width="414" height="44"/>
|
||||||
<autoresizingMask key="autoresizingMask"/>
|
<autoresizingMask key="autoresizingMask"/>
|
||||||
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" tableViewCell="55g-T3-9ak" id="dKn-cO-EJa">
|
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" tableViewCell="55g-T3-9ak" id="dKn-cO-EJa">
|
||||||
<rect key="frame" x="0.0" y="0.0" width="381" height="43.666666666666664"/>
|
<rect key="frame" x="0.0" y="0.0" width="376" height="43.666666666666664"/>
|
||||||
<autoresizingMask key="autoresizingMask"/>
|
<autoresizingMask key="autoresizingMask"/>
|
||||||
<subviews>
|
<subviews>
|
||||||
<label opaque="NO" multipleTouchEnabled="YES" contentMode="left" text="General" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" id="dOt-Rj-vWD">
|
<label opaque="NO" multipleTouchEnabled="YES" contentMode="left" text="General" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" id="dOt-Rj-vWD">
|
||||||
<rect key="frame" x="15" y="0.0" width="364" height="43.666666666666664"/>
|
<rect key="frame" x="20" y="0.0" width="356" height="43.666666666666664"/>
|
||||||
<autoresizingMask key="autoresizingMask"/>
|
<autoresizingMask key="autoresizingMask"/>
|
||||||
<fontDescription key="fontDescription" type="system" pointSize="17"/>
|
<fontDescription key="fontDescription" type="system" pointSize="17"/>
|
||||||
<nil key="textColor"/>
|
<nil key="textColor"/>
|
||||||
|
|
@ -109,18 +109,18 @@
|
||||||
<rect key="frame" x="0.0" y="79" width="414" height="44"/>
|
<rect key="frame" x="0.0" y="79" width="414" height="44"/>
|
||||||
<autoresizingMask key="autoresizingMask"/>
|
<autoresizingMask key="autoresizingMask"/>
|
||||||
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" tableViewCell="2rc-ZW-XKd" id="CpT-zb-QEP">
|
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" tableViewCell="2rc-ZW-XKd" id="CpT-zb-QEP">
|
||||||
<rect key="frame" x="0.0" y="0.0" width="381" height="43.666666666666664"/>
|
<rect key="frame" x="0.0" y="0.0" width="376" height="43.666666666666664"/>
|
||||||
<autoresizingMask key="autoresizingMask"/>
|
<autoresizingMask key="autoresizingMask"/>
|
||||||
<subviews>
|
<subviews>
|
||||||
<label opaque="NO" multipleTouchEnabled="YES" contentMode="left" text="Password Repository" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" id="gWn-ib-STb">
|
<label opaque="NO" multipleTouchEnabled="YES" contentMode="left" text="Password Repository" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" id="gWn-ib-STb">
|
||||||
<rect key="frame" x="15" y="11.999999999999998" width="160.33333333333334" height="20.333333333333332"/>
|
<rect key="frame" x="20" y="11.999999999999998" width="160.33333333333334" height="20.333333333333332"/>
|
||||||
<autoresizingMask key="autoresizingMask"/>
|
<autoresizingMask key="autoresizingMask"/>
|
||||||
<fontDescription key="fontDescription" type="system" pointSize="17"/>
|
<fontDescription key="fontDescription" type="system" pointSize="17"/>
|
||||||
<nil key="textColor"/>
|
<nil key="textColor"/>
|
||||||
<nil key="highlightedColor"/>
|
<nil key="highlightedColor"/>
|
||||||
</label>
|
</label>
|
||||||
<label opaque="NO" multipleTouchEnabled="YES" contentMode="left" text="Not Set" textAlignment="right" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" id="Myq-fV-riz">
|
<label opaque="NO" multipleTouchEnabled="YES" contentMode="left" text="Not Set" textAlignment="right" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" id="Myq-fV-riz">
|
||||||
<rect key="frame" x="321.33333333333331" y="11.999999999999998" width="57.666666666666664" height="20.333333333333332"/>
|
<rect key="frame" x="318.33333333333331" y="11.999999999999998" width="57.666666666666664" height="20.333333333333332"/>
|
||||||
<autoresizingMask key="autoresizingMask"/>
|
<autoresizingMask key="autoresizingMask"/>
|
||||||
<fontDescription key="fontDescription" type="system" pointSize="17"/>
|
<fontDescription key="fontDescription" type="system" pointSize="17"/>
|
||||||
<color key="textColor" red="0.5568627451" green="0.5568627451" blue="0.57647058819999997" alpha="1" colorSpace="calibratedRGB"/>
|
<color key="textColor" red="0.5568627451" green="0.5568627451" blue="0.57647058819999997" alpha="1" colorSpace="calibratedRGB"/>
|
||||||
|
|
@ -136,18 +136,18 @@
|
||||||
<rect key="frame" x="0.0" y="123" width="414" height="44"/>
|
<rect key="frame" x="0.0" y="123" width="414" height="44"/>
|
||||||
<autoresizingMask key="autoresizingMask"/>
|
<autoresizingMask key="autoresizingMask"/>
|
||||||
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" tableViewCell="1ze-MS-Xbj" id="W7U-oL-hOh">
|
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" tableViewCell="1ze-MS-Xbj" id="W7U-oL-hOh">
|
||||||
<rect key="frame" x="0.0" y="0.0" width="381" height="43.666666666666664"/>
|
<rect key="frame" x="0.0" y="0.0" width="376" height="43.666666666666664"/>
|
||||||
<autoresizingMask key="autoresizingMask"/>
|
<autoresizingMask key="autoresizingMask"/>
|
||||||
<subviews>
|
<subviews>
|
||||||
<label opaque="NO" multipleTouchEnabled="YES" contentMode="left" text="PGP Key" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" id="RR9-xr-9ko">
|
<label opaque="NO" multipleTouchEnabled="YES" contentMode="left" text="PGP Key" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" id="RR9-xr-9ko">
|
||||||
<rect key="frame" x="15" y="11.999999999999998" width="66" height="20.333333333333332"/>
|
<rect key="frame" x="20" y="11.999999999999998" width="66" height="20.333333333333332"/>
|
||||||
<autoresizingMask key="autoresizingMask"/>
|
<autoresizingMask key="autoresizingMask"/>
|
||||||
<fontDescription key="fontDescription" type="system" pointSize="17"/>
|
<fontDescription key="fontDescription" type="system" pointSize="17"/>
|
||||||
<nil key="textColor"/>
|
<nil key="textColor"/>
|
||||||
<nil key="highlightedColor"/>
|
<nil key="highlightedColor"/>
|
||||||
</label>
|
</label>
|
||||||
<label opaque="NO" multipleTouchEnabled="YES" contentMode="left" text="Not Set" textAlignment="right" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" id="7lc-Vh-G9W">
|
<label opaque="NO" multipleTouchEnabled="YES" contentMode="left" text="Not Set" textAlignment="right" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" id="7lc-Vh-G9W">
|
||||||
<rect key="frame" x="321.33333333333331" y="11.999999999999998" width="57.666666666666664" height="20.333333333333332"/>
|
<rect key="frame" x="318.33333333333331" y="11.999999999999998" width="57.666666666666664" height="20.333333333333332"/>
|
||||||
<autoresizingMask key="autoresizingMask"/>
|
<autoresizingMask key="autoresizingMask"/>
|
||||||
<fontDescription key="fontDescription" type="system" pointSize="17"/>
|
<fontDescription key="fontDescription" type="system" pointSize="17"/>
|
||||||
<color key="textColor" red="0.55686274509803924" green="0.55686274509803924" blue="0.57647058823529407" alpha="1" colorSpace="calibratedRGB"/>
|
<color key="textColor" red="0.55686274509803924" green="0.55686274509803924" blue="0.57647058823529407" alpha="1" colorSpace="calibratedRGB"/>
|
||||||
|
|
@ -164,18 +164,18 @@
|
||||||
<rect key="frame" x="0.0" y="223" width="414" height="44"/>
|
<rect key="frame" x="0.0" y="223" width="414" height="44"/>
|
||||||
<autoresizingMask key="autoresizingMask"/>
|
<autoresizingMask key="autoresizingMask"/>
|
||||||
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" tableViewCell="6Y0-mj-qhA" id="qlv-tQ-Xmc">
|
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" tableViewCell="6Y0-mj-qhA" id="qlv-tQ-Xmc">
|
||||||
<rect key="frame" x="0.0" y="0.0" width="381" height="43.666666666666664"/>
|
<rect key="frame" x="0.0" y="0.0" width="376" height="43.666666666666664"/>
|
||||||
<autoresizingMask key="autoresizingMask"/>
|
<autoresizingMask key="autoresizingMask"/>
|
||||||
<subviews>
|
<subviews>
|
||||||
<label opaque="NO" multipleTouchEnabled="YES" contentMode="left" text="Passcode Lock" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" id="RaZ-6t-0CU">
|
<label opaque="NO" multipleTouchEnabled="YES" contentMode="left" text="Passcode Lock" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" id="RaZ-6t-0CU">
|
||||||
<rect key="frame" x="15" y="11.999999999999998" width="115" height="20.333333333333332"/>
|
<rect key="frame" x="20" y="11.999999999999998" width="115" height="20.333333333333332"/>
|
||||||
<autoresizingMask key="autoresizingMask"/>
|
<autoresizingMask key="autoresizingMask"/>
|
||||||
<fontDescription key="fontDescription" type="system" pointSize="17"/>
|
<fontDescription key="fontDescription" type="system" pointSize="17"/>
|
||||||
<nil key="textColor"/>
|
<nil key="textColor"/>
|
||||||
<nil key="highlightedColor"/>
|
<nil key="highlightedColor"/>
|
||||||
</label>
|
</label>
|
||||||
<label opaque="NO" multipleTouchEnabled="YES" contentMode="left" text="Off" textAlignment="right" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" id="HXb-ZX-HUv">
|
<label opaque="NO" multipleTouchEnabled="YES" contentMode="left" text="Off" textAlignment="right" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" id="HXb-ZX-HUv">
|
||||||
<rect key="frame" x="354.66666666666669" y="11.999999999999998" width="24.333333333333332" height="20.333333333333332"/>
|
<rect key="frame" x="351.66666666666669" y="11.999999999999998" width="24.333333333333332" height="20.333333333333332"/>
|
||||||
<autoresizingMask key="autoresizingMask"/>
|
<autoresizingMask key="autoresizingMask"/>
|
||||||
<fontDescription key="fontDescription" type="system" pointSize="17"/>
|
<fontDescription key="fontDescription" type="system" pointSize="17"/>
|
||||||
<color key="textColor" red="0.5568627451" green="0.5568627451" blue="0.57647058819999997" alpha="1" colorSpace="calibratedRGB"/>
|
<color key="textColor" red="0.5568627451" green="0.5568627451" blue="0.57647058819999997" alpha="1" colorSpace="calibratedRGB"/>
|
||||||
|
|
@ -192,7 +192,7 @@
|
||||||
<autoresizingMask key="autoresizingMask"/>
|
<autoresizingMask key="autoresizingMask"/>
|
||||||
<subviews>
|
<subviews>
|
||||||
<label opaque="NO" multipleTouchEnabled="YES" contentMode="left" text="Touch ID" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" id="H2E-hP-Gyf">
|
<label opaque="NO" multipleTouchEnabled="YES" contentMode="left" text="Touch ID" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" id="H2E-hP-Gyf">
|
||||||
<rect key="frame" x="15" y="0.0" width="384" height="43.666666666666664"/>
|
<rect key="frame" x="20" y="0.0" width="379" height="43.666666666666664"/>
|
||||||
<autoresizingMask key="autoresizingMask"/>
|
<autoresizingMask key="autoresizingMask"/>
|
||||||
<fontDescription key="fontDescription" type="system" pointSize="17"/>
|
<fontDescription key="fontDescription" type="system" pointSize="17"/>
|
||||||
<nil key="textColor"/>
|
<nil key="textColor"/>
|
||||||
|
|
@ -209,11 +209,11 @@
|
||||||
<rect key="frame" x="0.0" y="347" width="414" height="44"/>
|
<rect key="frame" x="0.0" y="347" width="414" height="44"/>
|
||||||
<autoresizingMask key="autoresizingMask"/>
|
<autoresizingMask key="autoresizingMask"/>
|
||||||
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" tableViewCell="tQN-gu-iRe" id="Xs0-LN-r43">
|
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" tableViewCell="tQN-gu-iRe" id="Xs0-LN-r43">
|
||||||
<rect key="frame" x="0.0" y="0.0" width="381" height="43.666666666666664"/>
|
<rect key="frame" x="0.0" y="0.0" width="376" height="43.666666666666664"/>
|
||||||
<autoresizingMask key="autoresizingMask"/>
|
<autoresizingMask key="autoresizingMask"/>
|
||||||
<subviews>
|
<subviews>
|
||||||
<label opaque="NO" multipleTouchEnabled="YES" contentMode="left" text="Advanced" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" id="MKj-d0-8q3">
|
<label opaque="NO" multipleTouchEnabled="YES" contentMode="left" text="Advanced" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" id="MKj-d0-8q3">
|
||||||
<rect key="frame" x="15" y="0.0" width="364" height="43.666666666666664"/>
|
<rect key="frame" x="20" y="0.0" width="356" height="43.666666666666664"/>
|
||||||
<autoresizingMask key="autoresizingMask"/>
|
<autoresizingMask key="autoresizingMask"/>
|
||||||
<fontDescription key="fontDescription" type="system" pointSize="17"/>
|
<fontDescription key="fontDescription" type="system" pointSize="17"/>
|
||||||
<color key="textColor" red="0.0" green="0.0" blue="0.0" alpha="1" colorSpace="calibratedRGB"/>
|
<color key="textColor" red="0.0" green="0.0" blue="0.0" alpha="1" colorSpace="calibratedRGB"/>
|
||||||
|
|
@ -233,11 +233,11 @@
|
||||||
<rect key="frame" x="0.0" y="427" width="414" height="44"/>
|
<rect key="frame" x="0.0" y="427" width="414" height="44"/>
|
||||||
<autoresizingMask key="autoresizingMask"/>
|
<autoresizingMask key="autoresizingMask"/>
|
||||||
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" tableViewCell="osS-xk-WRP" id="G6j-ij-rNr">
|
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" tableViewCell="osS-xk-WRP" id="G6j-ij-rNr">
|
||||||
<rect key="frame" x="0.0" y="0.0" width="381" height="43.666666666666664"/>
|
<rect key="frame" x="0.0" y="0.0" width="376" height="43.666666666666664"/>
|
||||||
<autoresizingMask key="autoresizingMask"/>
|
<autoresizingMask key="autoresizingMask"/>
|
||||||
<subviews>
|
<subviews>
|
||||||
<label opaque="NO" multipleTouchEnabled="YES" contentMode="left" text="About" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" id="oqz-Hr-RAl">
|
<label opaque="NO" multipleTouchEnabled="YES" contentMode="left" text="About" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" id="oqz-Hr-RAl">
|
||||||
<rect key="frame" x="15" y="0.0" width="364" height="43.666666666666664"/>
|
<rect key="frame" x="20" y="0.0" width="356" height="43.666666666666664"/>
|
||||||
<autoresizingMask key="autoresizingMask"/>
|
<autoresizingMask key="autoresizingMask"/>
|
||||||
<fontDescription key="fontDescription" type="system" pointSize="17"/>
|
<fontDescription key="fontDescription" type="system" pointSize="17"/>
|
||||||
<color key="textColor" red="0.0" green="0.0" blue="0.0" alpha="1" colorSpace="calibratedRGB"/>
|
<color key="textColor" red="0.0" green="0.0" blue="0.0" alpha="1" colorSpace="calibratedRGB"/>
|
||||||
|
|
@ -288,22 +288,22 @@
|
||||||
<rect key="frame" x="0.0" y="28" width="414" height="54"/>
|
<rect key="frame" x="0.0" y="28" width="414" height="54"/>
|
||||||
<autoresizingMask key="autoresizingMask"/>
|
<autoresizingMask key="autoresizingMask"/>
|
||||||
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" preservesSuperviewLayoutMargins="YES" tableViewCell="OfB-1N-1Am" id="fh0-au-C6q">
|
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" preservesSuperviewLayoutMargins="YES" tableViewCell="OfB-1N-1Am" id="fh0-au-C6q">
|
||||||
<rect key="frame" x="0.0" y="0.0" width="414" height="54"/>
|
<rect key="frame" x="0.0" y="0.0" width="414" height="53.5"/>
|
||||||
<autoresizingMask key="autoresizingMask"/>
|
<autoresizingMask key="autoresizingMask"/>
|
||||||
<subviews>
|
<subviews>
|
||||||
<label opaque="NO" userInteractionEnabled="NO" tag="101" contentMode="left" verticalHuggingPriority="251" preservesSuperviewLayoutMargins="YES" text="2017/04/04" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="GLC-qL-55P">
|
<label opaque="NO" userInteractionEnabled="NO" tag="201" contentMode="left" verticalHuggingPriority="251" preservesSuperviewLayoutMargins="YES" text="2017/04/04" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="GLC-qL-55P">
|
||||||
<rect key="frame" x="87.333333333333343" y="10.999999999999998" width="311.66666666666663" height="14.33333333333333"/>
|
<rect key="frame" x="87.333333333333343" y="10.999999999999998" width="311.66666666666663" height="14.33333333333333"/>
|
||||||
<fontDescription key="fontDescription" name=".AppleSystemUIFont" family=".AppleSystemUIFont" pointSize="12"/>
|
<fontDescription key="fontDescription" name=".AppleSystemUIFont" family=".AppleSystemUIFont" pointSize="12"/>
|
||||||
<color key="textColor" white="0.33333333333333331" alpha="1" colorSpace="calibratedWhite"/>
|
<color key="textColor" white="0.33333333333333331" alpha="1" colorSpace="calibratedWhite"/>
|
||||||
<nil key="highlightedColor"/>
|
<nil key="highlightedColor"/>
|
||||||
</label>
|
</label>
|
||||||
<label opaque="NO" userInteractionEnabled="NO" tag="102" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" preservesSuperviewLayoutMargins="YES" text="Edit password for baidu.com using Pass for iOS." textAlignment="natural" lineBreakMode="tailTruncation" numberOfLines="0" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="L1p-Dm-Mnh">
|
<label opaque="NO" userInteractionEnabled="NO" tag="202" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" preservesSuperviewLayoutMargins="YES" text="Edit password for baidu.com using Pass for iOS." textAlignment="natural" lineBreakMode="tailTruncation" numberOfLines="0" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="L1p-Dm-Mnh">
|
||||||
<rect key="frame" x="15" y="28.666666666666671" width="384" height="14.333333333333329"/>
|
<rect key="frame" x="15" y="28.666666666666671" width="384" height="14.333333333333329"/>
|
||||||
<fontDescription key="fontDescription" type="system" pointSize="14"/>
|
<fontDescription key="fontDescription" type="system" pointSize="14"/>
|
||||||
<nil key="textColor"/>
|
<nil key="textColor"/>
|
||||||
<nil key="highlightedColor"/>
|
<nil key="highlightedColor"/>
|
||||||
</label>
|
</label>
|
||||||
<label opaque="NO" userInteractionEnabled="NO" tag="100" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" preservesSuperviewLayoutMargins="YES" text="Bob Sun" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="8Dc-U9-AVf">
|
<label opaque="NO" userInteractionEnabled="NO" tag="200" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" preservesSuperviewLayoutMargins="YES" text="Bob Sun" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="8Dc-U9-AVf">
|
||||||
<rect key="frame" x="15" y="11.000000000000002" width="62.333333333333329" height="14.66666666666667"/>
|
<rect key="frame" x="15" y="11.000000000000002" width="62.333333333333329" height="14.66666666666667"/>
|
||||||
<fontDescription key="fontDescription" type="system" weight="medium" pointSize="16"/>
|
<fontDescription key="fontDescription" type="system" weight="medium" pointSize="16"/>
|
||||||
<nil key="textColor"/>
|
<nil key="textColor"/>
|
||||||
|
|
@ -348,10 +348,10 @@
|
||||||
<tableViewSection headerTitle="Git Repository URL" id="pbe-W6-w4V">
|
<tableViewSection headerTitle="Git Repository URL" id="pbe-W6-w4V">
|
||||||
<cells>
|
<cells>
|
||||||
<tableViewCell clipsSubviews="YES" contentMode="scaleToFill" preservesSuperviewLayoutMargins="YES" selectionStyle="none" indentationWidth="10" reuseIdentifier="gitRepositoryURLTabelViewCell" rowHeight="52" id="FRr-pf-aPO">
|
<tableViewCell clipsSubviews="YES" contentMode="scaleToFill" preservesSuperviewLayoutMargins="YES" selectionStyle="none" indentationWidth="10" reuseIdentifier="gitRepositoryURLTabelViewCell" rowHeight="52" id="FRr-pf-aPO">
|
||||||
<rect key="frame" x="0.0" y="55" width="414" height="52"/>
|
<rect key="frame" x="0.0" y="55.5" width="414" height="52"/>
|
||||||
<autoresizingMask key="autoresizingMask"/>
|
<autoresizingMask key="autoresizingMask"/>
|
||||||
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" preservesSuperviewLayoutMargins="YES" tableViewCell="FRr-pf-aPO" id="60A-PS-qGe">
|
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" preservesSuperviewLayoutMargins="YES" tableViewCell="FRr-pf-aPO" id="60A-PS-qGe">
|
||||||
<rect key="frame" x="0.0" y="0.0" width="414" height="51"/>
|
<rect key="frame" x="0.0" y="0.0" width="414" height="52"/>
|
||||||
<autoresizingMask key="autoresizingMask"/>
|
<autoresizingMask key="autoresizingMask"/>
|
||||||
<subviews>
|
<subviews>
|
||||||
<textField opaque="NO" clipsSubviews="YES" contentMode="scaleToFill" preservesSuperviewLayoutMargins="YES" contentHorizontalAlignment="left" contentVerticalAlignment="center" placeholder="Git Repository URL" textAlignment="natural" minimumFontSize="17" clearButtonMode="whileEditing" translatesAutoresizingMaskIntoConstraints="NO" id="EVT-VU-sCi">
|
<textField opaque="NO" clipsSubviews="YES" contentMode="scaleToFill" preservesSuperviewLayoutMargins="YES" contentHorizontalAlignment="left" contentVerticalAlignment="center" placeholder="Git Repository URL" textAlignment="natural" minimumFontSize="17" clearButtonMode="whileEditing" translatesAutoresizingMaskIntoConstraints="NO" id="EVT-VU-sCi">
|
||||||
|
|
@ -378,10 +378,10 @@
|
||||||
<tableViewSection headerTitle="Username" id="fRu-A2-SCk">
|
<tableViewSection headerTitle="Username" id="fRu-A2-SCk">
|
||||||
<cells>
|
<cells>
|
||||||
<tableViewCell clipsSubviews="YES" contentMode="scaleToFill" preservesSuperviewLayoutMargins="YES" selectionStyle="none" indentationWidth="10" reuseIdentifier="usernameTableVIewCell" rowHeight="52" id="tnj-5U-kMB">
|
<tableViewCell clipsSubviews="YES" contentMode="scaleToFill" preservesSuperviewLayoutMargins="YES" selectionStyle="none" indentationWidth="10" reuseIdentifier="usernameTableVIewCell" rowHeight="52" id="tnj-5U-kMB">
|
||||||
<rect key="frame" x="0.0" y="163" width="414" height="52"/>
|
<rect key="frame" x="0.0" y="163.5" width="414" height="52"/>
|
||||||
<autoresizingMask key="autoresizingMask"/>
|
<autoresizingMask key="autoresizingMask"/>
|
||||||
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" preservesSuperviewLayoutMargins="YES" tableViewCell="tnj-5U-kMB" id="f0c-pI-MSJ">
|
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" preservesSuperviewLayoutMargins="YES" tableViewCell="tnj-5U-kMB" id="f0c-pI-MSJ">
|
||||||
<rect key="frame" x="0.0" y="0.0" width="414" height="51"/>
|
<rect key="frame" x="0.0" y="0.0" width="414" height="52"/>
|
||||||
<autoresizingMask key="autoresizingMask"/>
|
<autoresizingMask key="autoresizingMask"/>
|
||||||
<subviews>
|
<subviews>
|
||||||
<textField opaque="NO" clipsSubviews="YES" contentMode="scaleToFill" preservesSuperviewLayoutMargins="YES" contentHorizontalAlignment="left" contentVerticalAlignment="center" placeholder="Username" textAlignment="natural" minimumFontSize="17" clearButtonMode="whileEditing" translatesAutoresizingMaskIntoConstraints="NO" id="TMg-Gk-7nG">
|
<textField opaque="NO" clipsSubviews="YES" contentMode="scaleToFill" preservesSuperviewLayoutMargins="YES" contentHorizontalAlignment="left" contentVerticalAlignment="center" placeholder="Username" textAlignment="natural" minimumFontSize="17" clearButtonMode="whileEditing" translatesAutoresizingMaskIntoConstraints="NO" id="TMg-Gk-7nG">
|
||||||
|
|
@ -407,10 +407,10 @@
|
||||||
<tableViewSection headerTitle="Authentication Method" id="h0N-tI-shZ">
|
<tableViewSection headerTitle="Authentication Method" id="h0N-tI-shZ">
|
||||||
<cells>
|
<cells>
|
||||||
<tableViewCell clipsSubviews="YES" contentMode="scaleToFill" selectionStyle="blue" hidesAccessoryWhenEditing="NO" indentationLevel="2" indentationWidth="0.0" shouldIndentWhileEditing="NO" id="KrP-nb-haa">
|
<tableViewCell clipsSubviews="YES" contentMode="scaleToFill" selectionStyle="blue" hidesAccessoryWhenEditing="NO" indentationLevel="2" indentationWidth="0.0" shouldIndentWhileEditing="NO" id="KrP-nb-haa">
|
||||||
<rect key="frame" x="0.0" y="271" width="414" height="44"/>
|
<rect key="frame" x="0.0" y="271.5" width="414" height="44"/>
|
||||||
<autoresizingMask key="autoresizingMask"/>
|
<autoresizingMask key="autoresizingMask"/>
|
||||||
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" tableViewCell="KrP-nb-haa" id="1uB-oE-kfI">
|
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" tableViewCell="KrP-nb-haa" id="1uB-oE-kfI">
|
||||||
<rect key="frame" x="0.0" y="0.0" width="414" height="43"/>
|
<rect key="frame" x="0.0" y="0.0" width="414" height="44"/>
|
||||||
<autoresizingMask key="autoresizingMask"/>
|
<autoresizingMask key="autoresizingMask"/>
|
||||||
<subviews>
|
<subviews>
|
||||||
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Password" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="LfQ-Af-j2O">
|
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Password" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="LfQ-Af-j2O">
|
||||||
|
|
@ -438,10 +438,10 @@
|
||||||
<inset key="separatorInset" minX="62" minY="0.0" maxX="0.0" maxY="0.0"/>
|
<inset key="separatorInset" minX="62" minY="0.0" maxX="0.0" maxY="0.0"/>
|
||||||
</tableViewCell>
|
</tableViewCell>
|
||||||
<tableViewCell clipsSubviews="YES" contentMode="scaleToFill" selectionStyle="blue" accessoryType="detailButton" hidesAccessoryWhenEditing="NO" indentationLevel="2" indentationWidth="0.0" shouldIndentWhileEditing="NO" id="Qmt-bo-CuJ">
|
<tableViewCell clipsSubviews="YES" contentMode="scaleToFill" selectionStyle="blue" accessoryType="detailButton" hidesAccessoryWhenEditing="NO" indentationLevel="2" indentationWidth="0.0" shouldIndentWhileEditing="NO" id="Qmt-bo-CuJ">
|
||||||
<rect key="frame" x="0.0" y="315" width="414" height="44"/>
|
<rect key="frame" x="0.0" y="315.5" width="414" height="44"/>
|
||||||
<autoresizingMask key="autoresizingMask"/>
|
<autoresizingMask key="autoresizingMask"/>
|
||||||
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" tableViewCell="Qmt-bo-CuJ" id="p3u-8b-h3U">
|
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" tableViewCell="Qmt-bo-CuJ" id="p3u-8b-h3U">
|
||||||
<rect key="frame" x="0.0" y="0.0" width="367" height="43"/>
|
<rect key="frame" x="0.0" y="0.0" width="367" height="44"/>
|
||||||
<autoresizingMask key="autoresizingMask"/>
|
<autoresizingMask key="autoresizingMask"/>
|
||||||
<subviews>
|
<subviews>
|
||||||
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="SSH Key" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="Ezz-76-a53">
|
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="SSH Key" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="Ezz-76-a53">
|
||||||
|
|
@ -482,7 +482,7 @@
|
||||||
<segue destination="7K9-cE-9qq" kind="unwind" unwindAction="cancelGitServerSettingWithSegue:" id="SGr-tc-vDL"/>
|
<segue destination="7K9-cE-9qq" kind="unwind" unwindAction="cancelGitServerSettingWithSegue:" id="SGr-tc-vDL"/>
|
||||||
</connections>
|
</connections>
|
||||||
</barButtonItem>
|
</barButtonItem>
|
||||||
<barButtonItem key="rightBarButtonItem" style="done" systemItem="save" id="sgQ-zB-rxv">
|
<barButtonItem key="rightBarButtonItem" title="Clone" style="done" id="sgQ-zB-rxv">
|
||||||
<connections>
|
<connections>
|
||||||
<action selector="save:" destination="ynQ-64-MfA" id="HNL-Da-fXT"/>
|
<action selector="save:" destination="ynQ-64-MfA" id="HNL-Da-fXT"/>
|
||||||
</connections>
|
</connections>
|
||||||
|
|
@ -519,7 +519,7 @@
|
||||||
<rect key="frame" x="0.0" y="35" width="414" height="52"/>
|
<rect key="frame" x="0.0" y="35" width="414" height="52"/>
|
||||||
<autoresizingMask key="autoresizingMask"/>
|
<autoresizingMask key="autoresizingMask"/>
|
||||||
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" tableViewCell="BYZ-9g-xZy" id="Zfn-rK-sN1">
|
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" tableViewCell="BYZ-9g-xZy" id="Zfn-rK-sN1">
|
||||||
<rect key="frame" x="0.0" y="0.0" width="414" height="52"/>
|
<rect key="frame" x="0.0" y="0.0" width="414" height="51.5"/>
|
||||||
<autoresizingMask key="autoresizingMask"/>
|
<autoresizingMask key="autoresizingMask"/>
|
||||||
<subviews>
|
<subviews>
|
||||||
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Public Key URL" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="dWi-eh-7Eq">
|
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Public Key URL" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="dWi-eh-7Eq">
|
||||||
|
|
@ -553,7 +553,7 @@
|
||||||
<rect key="frame" x="0.0" y="87" width="414" height="52"/>
|
<rect key="frame" x="0.0" y="87" width="414" height="52"/>
|
||||||
<autoresizingMask key="autoresizingMask"/>
|
<autoresizingMask key="autoresizingMask"/>
|
||||||
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" tableViewCell="vpk-J8-j7t" id="1td-qT-6ts">
|
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" tableViewCell="vpk-J8-j7t" id="1td-qT-6ts">
|
||||||
<rect key="frame" x="0.0" y="0.0" width="414" height="52"/>
|
<rect key="frame" x="0.0" y="0.0" width="414" height="51.5"/>
|
||||||
<autoresizingMask key="autoresizingMask"/>
|
<autoresizingMask key="autoresizingMask"/>
|
||||||
<subviews>
|
<subviews>
|
||||||
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Private Key URL" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="Qht-RC-Yeg">
|
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Private Key URL" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="Qht-RC-Yeg">
|
||||||
|
|
@ -640,7 +640,7 @@
|
||||||
</barButtonItem>
|
</barButtonItem>
|
||||||
</navigationItem>
|
</navigationItem>
|
||||||
<connections>
|
<connections>
|
||||||
<segue destination="lY3-ru-aoM" kind="show" identifier="showQRScannerSegue" id="mPU-tp-J0X"/>
|
<segue destination="A9p-Qb-WmU" kind="show" identifier="showQRScannerSegue" id="yyD-4H-pLE"/>
|
||||||
</connections>
|
</connections>
|
||||||
</tableViewController>
|
</tableViewController>
|
||||||
<placeholder placeholderIdentifier="IBFirstResponder" id="6R0-BP-wo8" userLabel="First Responder" sceneMemberID="firstResponder"/>
|
<placeholder placeholderIdentifier="IBFirstResponder" id="6R0-BP-wo8" userLabel="First Responder" sceneMemberID="firstResponder"/>
|
||||||
|
|
@ -684,11 +684,6 @@
|
||||||
<segue destination="0gD-ix-2NF" kind="unwind" unwindAction="cancelOTPScannerWithSegue:" id="nZe-B6-MNt"/>
|
<segue destination="0gD-ix-2NF" kind="unwind" unwindAction="cancelOTPScannerWithSegue:" id="nZe-B6-MNt"/>
|
||||||
</connections>
|
</connections>
|
||||||
</barButtonItem>
|
</barButtonItem>
|
||||||
<barButtonItem key="rightBarButtonItem" systemItem="save" id="4kF-IA-Obv">
|
|
||||||
<connections>
|
|
||||||
<segue destination="0gD-ix-2NF" kind="unwind" unwindAction="saveScannedOTPWithSegue:" id="CWK-sE-Ic4"/>
|
|
||||||
</connections>
|
|
||||||
</barButtonItem>
|
|
||||||
</navigationItem>
|
</navigationItem>
|
||||||
<connections>
|
<connections>
|
||||||
<outlet property="scannerOutput" destination="lOI-p4-BGb" id="LZa-eC-1Lc"/>
|
<outlet property="scannerOutput" destination="lOI-p4-BGb" id="LZa-eC-1Lc"/>
|
||||||
|
|
@ -697,7 +692,7 @@
|
||||||
<placeholder placeholderIdentifier="IBFirstResponder" id="rqh-SR-bIq" userLabel="First Responder" sceneMemberID="firstResponder"/>
|
<placeholder placeholderIdentifier="IBFirstResponder" id="rqh-SR-bIq" userLabel="First Responder" sceneMemberID="firstResponder"/>
|
||||||
<exit id="0gD-ix-2NF" userLabel="Exit" sceneMemberID="exit"/>
|
<exit id="0gD-ix-2NF" userLabel="Exit" sceneMemberID="exit"/>
|
||||||
</objects>
|
</objects>
|
||||||
<point key="canvasLocation" x="7994.202898550725" y="-1008.4239130434784"/>
|
<point key="canvasLocation" x="7059" y="-1012"/>
|
||||||
</scene>
|
</scene>
|
||||||
<!--Password Detail Table View Controller-->
|
<!--Password Detail Table View Controller-->
|
||||||
<scene sceneID="9wY-d0-fB1">
|
<scene sceneID="9wY-d0-fB1">
|
||||||
|
|
@ -807,7 +802,7 @@
|
||||||
</navigationItem>
|
</navigationItem>
|
||||||
<connections>
|
<connections>
|
||||||
<segue destination="HB6-Yu-Y3J" kind="unwind" identifier="deletePasswordSegue" unwindAction="deletePasswordWithSegue:" id="L1Z-64-EZh"/>
|
<segue destination="HB6-Yu-Y3J" kind="unwind" identifier="deletePasswordSegue" unwindAction="deletePasswordWithSegue:" id="L1Z-64-EZh"/>
|
||||||
<segue destination="lY3-ru-aoM" kind="show" identifier="showQRScannerSegue" id="Cpl-XA-cZ9"/>
|
<segue destination="A9p-Qb-WmU" kind="show" identifier="showQRScannerSegue" id="UfP-k3-XeR"/>
|
||||||
</connections>
|
</connections>
|
||||||
</tableViewController>
|
</tableViewController>
|
||||||
<placeholder placeholderIdentifier="IBFirstResponder" id="HlX-6r-eOU" userLabel="First Responder" sceneMemberID="firstResponder"/>
|
<placeholder placeholderIdentifier="IBFirstResponder" id="HlX-6r-eOU" userLabel="First Responder" sceneMemberID="firstResponder"/>
|
||||||
|
|
@ -921,7 +916,7 @@ Phone Support PIN #: 84719</string>
|
||||||
<rect key="frame" x="0.0" y="35" width="414" height="52"/>
|
<rect key="frame" x="0.0" y="35" width="414" height="52"/>
|
||||||
<autoresizingMask key="autoresizingMask"/>
|
<autoresizingMask key="autoresizingMask"/>
|
||||||
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" tableViewCell="Jy1-4S-Lvf" id="tJE-ww-okf">
|
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" tableViewCell="Jy1-4S-Lvf" id="tJE-ww-okf">
|
||||||
<rect key="frame" x="0.0" y="0.0" width="414" height="52"/>
|
<rect key="frame" x="0.0" y="0.0" width="414" height="51.5"/>
|
||||||
<autoresizingMask key="autoresizingMask"/>
|
<autoresizingMask key="autoresizingMask"/>
|
||||||
<subviews>
|
<subviews>
|
||||||
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Public Key URL" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="Oys-xP-ZrB">
|
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Public Key URL" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="Oys-xP-ZrB">
|
||||||
|
|
@ -955,7 +950,7 @@ Phone Support PIN #: 84719</string>
|
||||||
<rect key="frame" x="0.0" y="87" width="414" height="52"/>
|
<rect key="frame" x="0.0" y="87" width="414" height="52"/>
|
||||||
<autoresizingMask key="autoresizingMask"/>
|
<autoresizingMask key="autoresizingMask"/>
|
||||||
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" tableViewCell="MA5-lE-8dT" id="pTv-Wj-psC">
|
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" tableViewCell="MA5-lE-8dT" id="pTv-Wj-psC">
|
||||||
<rect key="frame" x="0.0" y="0.0" width="414" height="52"/>
|
<rect key="frame" x="0.0" y="0.0" width="414" height="51.5"/>
|
||||||
<autoresizingMask key="autoresizingMask"/>
|
<autoresizingMask key="autoresizingMask"/>
|
||||||
<subviews>
|
<subviews>
|
||||||
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Private Key URL" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="C2w-dd-roS">
|
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Private Key URL" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="C2w-dd-roS">
|
||||||
|
|
@ -1058,17 +1053,17 @@ Phone Support PIN #: 84719</string>
|
||||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
||||||
<color key="backgroundColor" cocoaTouchSystemColor="groupTableViewBackgroundColor"/>
|
<color key="backgroundColor" cocoaTouchSystemColor="groupTableViewBackgroundColor"/>
|
||||||
<sections>
|
<sections>
|
||||||
<tableViewSection id="ugP-R2-9M7">
|
<tableViewSection headerTitle="GPG Configuration" id="ugP-R2-9M7">
|
||||||
<cells>
|
<cells>
|
||||||
<tableViewCell clipsSubviews="YES" contentMode="scaleToFill" selectionStyle="default" indentationWidth="10" textLabel="Jwg-mt-woS" style="IBUITableViewCellStyleDefault" id="tHt-Ro-0HF" userLabel="Discard Changes Table View Cell">
|
<tableViewCell clipsSubviews="YES" contentMode="scaleToFill" selectionStyle="default" indentationWidth="10" textLabel="Jwg-mt-woS" style="IBUITableViewCellStyleDefault" id="tHt-Ro-0HF" userLabel="Encrypt in ASCII-Armored">
|
||||||
<rect key="frame" x="0.0" y="35" width="414" height="44"/>
|
<rect key="frame" x="0.0" y="55.5" width="414" height="44"/>
|
||||||
<autoresizingMask key="autoresizingMask"/>
|
<autoresizingMask key="autoresizingMask"/>
|
||||||
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" tableViewCell="tHt-Ro-0HF" id="Epj-ei-NtS">
|
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" tableViewCell="tHt-Ro-0HF" id="Epj-ei-NtS">
|
||||||
<rect key="frame" x="0.0" y="0.0" width="414" height="43.666666666666664"/>
|
<rect key="frame" x="0.0" y="0.0" width="414" height="43.666666666666664"/>
|
||||||
<autoresizingMask key="autoresizingMask"/>
|
<autoresizingMask key="autoresizingMask"/>
|
||||||
<subviews>
|
<subviews>
|
||||||
<label opaque="NO" multipleTouchEnabled="YES" contentMode="left" text="Encrypt in ASCII-Armored" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" id="Jwg-mt-woS">
|
<label opaque="NO" multipleTouchEnabled="YES" contentMode="left" text="Encrypt in ASCII-Armored" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" id="Jwg-mt-woS">
|
||||||
<rect key="frame" x="15" y="0.0" width="384" height="43.666666666666664"/>
|
<rect key="frame" x="20" y="0.0" width="379" height="43.666666666666664"/>
|
||||||
<autoresizingMask key="autoresizingMask"/>
|
<autoresizingMask key="autoresizingMask"/>
|
||||||
<fontDescription key="fontDescription" type="system" pointSize="17"/>
|
<fontDescription key="fontDescription" type="system" pointSize="17"/>
|
||||||
<nil key="textColor"/>
|
<nil key="textColor"/>
|
||||||
|
|
@ -1079,17 +1074,48 @@ Phone Support PIN #: 84719</string>
|
||||||
</tableViewCell>
|
</tableViewCell>
|
||||||
</cells>
|
</cells>
|
||||||
</tableViewSection>
|
</tableViewSection>
|
||||||
<tableViewSection id="aVR-FE-jMg">
|
<tableViewSection headerTitle="Git Configuration" id="ihT-OG-HTv">
|
||||||
|
<cells>
|
||||||
|
<tableViewCell clipsSubviews="YES" contentMode="scaleToFill" selectionStyle="default" accessoryType="disclosureIndicator" indentationWidth="10" textLabel="87a-xY-AbR" detailTextLabel="2qr-d7-0SK" style="IBUITableViewCellStyleValue1" id="SVj-jD-qPT" userLabel="Git Signature">
|
||||||
|
<rect key="frame" x="0.0" y="155.5" width="414" height="44"/>
|
||||||
|
<autoresizingMask key="autoresizingMask"/>
|
||||||
|
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" tableViewCell="SVj-jD-qPT" id="HaO-5w-qZt">
|
||||||
|
<rect key="frame" x="0.0" y="0.0" width="376" height="43.666666666666664"/>
|
||||||
|
<autoresizingMask key="autoresizingMask"/>
|
||||||
|
<subviews>
|
||||||
|
<label opaque="NO" multipleTouchEnabled="YES" contentMode="left" text="Git Signature" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" id="87a-xY-AbR">
|
||||||
|
<rect key="frame" x="20.000000000000007" y="11.999999999999998" width="99.666666666666671" height="20.333333333333332"/>
|
||||||
|
<autoresizingMask key="autoresizingMask"/>
|
||||||
|
<fontDescription key="fontDescription" type="system" pointSize="17"/>
|
||||||
|
<nil key="textColor"/>
|
||||||
|
<nil key="highlightedColor"/>
|
||||||
|
</label>
|
||||||
|
<label opaque="NO" multipleTouchEnabled="YES" contentMode="left" text="Not Set" textAlignment="right" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" id="2qr-d7-0SK">
|
||||||
|
<rect key="frame" x="318.33333333333331" y="11.999999999999998" width="57.666666666666664" height="20.333333333333332"/>
|
||||||
|
<autoresizingMask key="autoresizingMask"/>
|
||||||
|
<fontDescription key="fontDescription" type="system" pointSize="17"/>
|
||||||
|
<color key="textColor" red="0.5568627451" green="0.5568627451" blue="0.57647058819999997" alpha="1" colorSpace="calibratedRGB"/>
|
||||||
|
<nil key="highlightedColor"/>
|
||||||
|
</label>
|
||||||
|
</subviews>
|
||||||
|
</tableViewCellContentView>
|
||||||
|
<connections>
|
||||||
|
<segue destination="RHo-mb-ayc" kind="show" id="YZO-oX-Umt"/>
|
||||||
|
</connections>
|
||||||
|
</tableViewCell>
|
||||||
|
</cells>
|
||||||
|
</tableViewSection>
|
||||||
|
<tableViewSection headerTitle="PasswordStore Data" id="aVR-FE-jMg">
|
||||||
<cells>
|
<cells>
|
||||||
<tableViewCell clipsSubviews="YES" contentMode="scaleToFill" selectionStyle="default" indentationWidth="10" textLabel="zrl-v3-fxg" style="IBUITableViewCellStyleDefault" id="Jm8-B5-wKx" userLabel="Discard Changes Table View Cell">
|
<tableViewCell clipsSubviews="YES" contentMode="scaleToFill" selectionStyle="default" indentationWidth="10" textLabel="zrl-v3-fxg" style="IBUITableViewCellStyleDefault" id="Jm8-B5-wKx" userLabel="Discard Changes Table View Cell">
|
||||||
<rect key="frame" x="0.0" y="115" width="414" height="44"/>
|
<rect key="frame" x="0.0" y="255.5" width="414" height="44"/>
|
||||||
<autoresizingMask key="autoresizingMask"/>
|
<autoresizingMask key="autoresizingMask"/>
|
||||||
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" tableViewCell="Jm8-B5-wKx" id="tjS-Q6-y2M">
|
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" tableViewCell="Jm8-B5-wKx" id="tjS-Q6-y2M">
|
||||||
<rect key="frame" x="0.0" y="0.0" width="414" height="43.666666666666664"/>
|
<rect key="frame" x="0.0" y="0.0" width="414" height="43.666666666666664"/>
|
||||||
<autoresizingMask key="autoresizingMask"/>
|
<autoresizingMask key="autoresizingMask"/>
|
||||||
<subviews>
|
<subviews>
|
||||||
<label opaque="NO" multipleTouchEnabled="YES" contentMode="left" text="Discard All Local Changes" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" id="zrl-v3-fxg">
|
<label opaque="NO" multipleTouchEnabled="YES" contentMode="left" text="Discard All Local Changes" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" id="zrl-v3-fxg">
|
||||||
<rect key="frame" x="15" y="0.0" width="384" height="43.666666666666664"/>
|
<rect key="frame" x="20" y="0.0" width="379" height="43.666666666666664"/>
|
||||||
<autoresizingMask key="autoresizingMask"/>
|
<autoresizingMask key="autoresizingMask"/>
|
||||||
<fontDescription key="fontDescription" type="system" pointSize="17"/>
|
<fontDescription key="fontDescription" type="system" pointSize="17"/>
|
||||||
<color key="textColor" red="0.50196081400000003" green="0.0" blue="0.0" alpha="1" colorSpace="calibratedRGB"/>
|
<color key="textColor" red="0.50196081400000003" green="0.0" blue="0.0" alpha="1" colorSpace="calibratedRGB"/>
|
||||||
|
|
@ -1098,19 +1124,15 @@ Phone Support PIN #: 84719</string>
|
||||||
</subviews>
|
</subviews>
|
||||||
</tableViewCellContentView>
|
</tableViewCellContentView>
|
||||||
</tableViewCell>
|
</tableViewCell>
|
||||||
</cells>
|
|
||||||
</tableViewSection>
|
|
||||||
<tableViewSection id="ujw-Wl-vs1">
|
|
||||||
<cells>
|
|
||||||
<tableViewCell clipsSubviews="YES" contentMode="scaleToFill" selectionStyle="default" indentationWidth="10" textLabel="K2K-Bx-g7Z" style="IBUITableViewCellStyleDefault" id="NI1-Kd-hyH">
|
<tableViewCell clipsSubviews="YES" contentMode="scaleToFill" selectionStyle="default" indentationWidth="10" textLabel="K2K-Bx-g7Z" style="IBUITableViewCellStyleDefault" id="NI1-Kd-hyH">
|
||||||
<rect key="frame" x="0.0" y="195" width="414" height="44"/>
|
<rect key="frame" x="0.0" y="299.5" width="414" height="44"/>
|
||||||
<autoresizingMask key="autoresizingMask"/>
|
<autoresizingMask key="autoresizingMask"/>
|
||||||
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" tableViewCell="NI1-Kd-hyH" id="yLe-T2-TWF">
|
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" tableViewCell="NI1-Kd-hyH" id="yLe-T2-TWF">
|
||||||
<rect key="frame" x="0.0" y="0.0" width="414" height="43.666666666666664"/>
|
<rect key="frame" x="0.0" y="0.0" width="414" height="43.666666666666664"/>
|
||||||
<autoresizingMask key="autoresizingMask"/>
|
<autoresizingMask key="autoresizingMask"/>
|
||||||
<subviews>
|
<subviews>
|
||||||
<label opaque="NO" multipleTouchEnabled="YES" contentMode="left" text="Erase All Password Store Data" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" id="K2K-Bx-g7Z">
|
<label opaque="NO" multipleTouchEnabled="YES" contentMode="left" text="Erase All Password Store Data" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" id="K2K-Bx-g7Z">
|
||||||
<rect key="frame" x="15" y="0.0" width="384" height="43.666666666666664"/>
|
<rect key="frame" x="20" y="0.0" width="379" height="43.666666666666664"/>
|
||||||
<autoresizingMask key="autoresizingMask"/>
|
<autoresizingMask key="autoresizingMask"/>
|
||||||
<fontDescription key="fontDescription" type="system" pointSize="17"/>
|
<fontDescription key="fontDescription" type="system" pointSize="17"/>
|
||||||
<color key="textColor" red="0.50196081400000003" green="0.0" blue="0.0" alpha="1" colorSpace="calibratedRGB"/>
|
<color key="textColor" red="0.50196081400000003" green="0.0" blue="0.0" alpha="1" colorSpace="calibratedRGB"/>
|
||||||
|
|
@ -1132,6 +1154,7 @@ Phone Support PIN #: 84719</string>
|
||||||
<outlet property="discardChangesTableViewCell" destination="Jm8-B5-wKx" id="rfA-G3-2OE"/>
|
<outlet property="discardChangesTableViewCell" destination="Jm8-B5-wKx" id="rfA-G3-2OE"/>
|
||||||
<outlet property="encryptInASCIIArmoredTableViewCell" destination="tHt-Ro-0HF" id="tOi-Sj-mLJ"/>
|
<outlet property="encryptInASCIIArmoredTableViewCell" destination="tHt-Ro-0HF" id="tOi-Sj-mLJ"/>
|
||||||
<outlet property="eraseDataTableViewCell" destination="NI1-Kd-hyH" id="NtJ-f4-oxb"/>
|
<outlet property="eraseDataTableViewCell" destination="NI1-Kd-hyH" id="NtJ-f4-oxb"/>
|
||||||
|
<outlet property="gitSignatureTableViewCell" destination="SVj-jD-qPT" id="4PE-oy-KPR"/>
|
||||||
</connections>
|
</connections>
|
||||||
</tableViewController>
|
</tableViewController>
|
||||||
<placeholder placeholderIdentifier="IBFirstResponder" id="adh-5o-YYB" userLabel="First Responder" sceneMemberID="firstResponder"/>
|
<placeholder placeholderIdentifier="IBFirstResponder" id="adh-5o-YYB" userLabel="First Responder" sceneMemberID="firstResponder"/>
|
||||||
|
|
@ -1257,14 +1280,14 @@ Phone Support PIN #: 84719</string>
|
||||||
<autoresizingMask key="autoresizingMask"/>
|
<autoresizingMask key="autoresizingMask"/>
|
||||||
<subviews>
|
<subviews>
|
||||||
<label opaque="NO" multipleTouchEnabled="YES" contentMode="left" text="ASCII-Armor Keys" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" id="M32-yr-IfE">
|
<label opaque="NO" multipleTouchEnabled="YES" contentMode="left" text="ASCII-Armor Keys" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" id="M32-yr-IfE">
|
||||||
<rect key="frame" x="15.000000000000007" y="17" width="121.66666666666667" height="17"/>
|
<rect key="frame" x="20.000000000000007" y="17" width="121.66666666666667" height="17"/>
|
||||||
<autoresizingMask key="autoresizingMask"/>
|
<autoresizingMask key="autoresizingMask"/>
|
||||||
<fontDescription key="fontDescription" name="HelveticaNeue-Bold" family="Helvetica Neue" pointSize="14"/>
|
<fontDescription key="fontDescription" name="HelveticaNeue-Bold" family="Helvetica Neue" pointSize="14"/>
|
||||||
<nil key="textColor"/>
|
<nil key="textColor"/>
|
||||||
<nil key="highlightedColor"/>
|
<nil key="highlightedColor"/>
|
||||||
</label>
|
</label>
|
||||||
<label opaque="NO" multipleTouchEnabled="YES" contentMode="left" usesAttributedText="YES" lineBreakMode="wordWrap" numberOfLines="0" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" id="sMx-qd-MTJ">
|
<label opaque="NO" multipleTouchEnabled="YES" contentMode="left" usesAttributedText="YES" lineBreakMode="wordWrap" numberOfLines="0" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" id="sMx-qd-MTJ">
|
||||||
<rect key="frame" x="15" y="34" width="384" height="119"/>
|
<rect key="frame" x="20" y="34" width="363.66666666666669" height="119"/>
|
||||||
<autoresizingMask key="autoresizingMask"/>
|
<autoresizingMask key="autoresizingMask"/>
|
||||||
<attributedString key="attributedText">
|
<attributedString key="attributedText">
|
||||||
<fragment>
|
<fragment>
|
||||||
|
|
@ -1317,7 +1340,7 @@ Cgo
|
||||||
<rect key="frame" x="0.0" y="244" width="414" height="160"/>
|
<rect key="frame" x="0.0" y="244" width="414" height="160"/>
|
||||||
<autoresizingMask key="autoresizingMask"/>
|
<autoresizingMask key="autoresizingMask"/>
|
||||||
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" tableViewCell="Lom-iT-l16" id="eya-Tv-r0q">
|
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" tableViewCell="Lom-iT-l16" id="eya-Tv-r0q">
|
||||||
<rect key="frame" x="0.0" y="0.0" width="414" height="160"/>
|
<rect key="frame" x="0.0" y="0.0" width="414" height="159.5"/>
|
||||||
<autoresizingMask key="autoresizingMask"/>
|
<autoresizingMask key="autoresizingMask"/>
|
||||||
<subviews>
|
<subviews>
|
||||||
<textView clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="oyB-oI-1fS">
|
<textView clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="oyB-oI-1fS">
|
||||||
|
|
@ -1347,7 +1370,7 @@ Cgo
|
||||||
<rect key="frame" x="0.0" y="443" width="414" height="160"/>
|
<rect key="frame" x="0.0" y="443" width="414" height="160"/>
|
||||||
<autoresizingMask key="autoresizingMask"/>
|
<autoresizingMask key="autoresizingMask"/>
|
||||||
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" tableViewCell="J8U-ev-FRQ" id="eb0-vb-Fcc">
|
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" tableViewCell="J8U-ev-FRQ" id="eb0-vb-Fcc">
|
||||||
<rect key="frame" x="0.0" y="0.0" width="414" height="160"/>
|
<rect key="frame" x="0.0" y="0.0" width="414" height="159.5"/>
|
||||||
<autoresizingMask key="autoresizingMask"/>
|
<autoresizingMask key="autoresizingMask"/>
|
||||||
<subviews>
|
<subviews>
|
||||||
<textView clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="lrQ-Ln-ZOv">
|
<textView clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="lrQ-Ln-ZOv">
|
||||||
|
|
@ -1455,14 +1478,14 @@ Cgo
|
||||||
<autoresizingMask key="autoresizingMask"/>
|
<autoresizingMask key="autoresizingMask"/>
|
||||||
<subviews>
|
<subviews>
|
||||||
<label opaque="NO" multipleTouchEnabled="YES" contentMode="left" text="ASCII-Armor Keys" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" id="i78-t7-fP9">
|
<label opaque="NO" multipleTouchEnabled="YES" contentMode="left" text="ASCII-Armor Keys" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" id="i78-t7-fP9">
|
||||||
<rect key="frame" x="15.000000000000007" y="37" width="121.66666666666667" height="17"/>
|
<rect key="frame" x="20.000000000000007" y="37" width="121.66666666666667" height="17"/>
|
||||||
<autoresizingMask key="autoresizingMask"/>
|
<autoresizingMask key="autoresizingMask"/>
|
||||||
<fontDescription key="fontDescription" name="HelveticaNeue-Bold" family="Helvetica Neue" pointSize="14"/>
|
<fontDescription key="fontDescription" name="HelveticaNeue-Bold" family="Helvetica Neue" pointSize="14"/>
|
||||||
<nil key="textColor"/>
|
<nil key="textColor"/>
|
||||||
<nil key="highlightedColor"/>
|
<nil key="highlightedColor"/>
|
||||||
</label>
|
</label>
|
||||||
<label opaque="NO" multipleTouchEnabled="YES" contentMode="left" usesAttributedText="YES" lineBreakMode="tailTruncation" numberOfLines="0" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" id="Oit-sd-wa5">
|
<label opaque="NO" multipleTouchEnabled="YES" contentMode="left" usesAttributedText="YES" lineBreakMode="tailTruncation" numberOfLines="0" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" id="Oit-sd-wa5">
|
||||||
<rect key="frame" x="15" y="53.999999999999993" width="344" height="79.333333333333329"/>
|
<rect key="frame" x="20" y="53.999999999999993" width="344" height="79.333333333333329"/>
|
||||||
<autoresizingMask key="autoresizingMask"/>
|
<autoresizingMask key="autoresizingMask"/>
|
||||||
<attributedString key="attributedText">
|
<attributedString key="attributedText">
|
||||||
<fragment>
|
<fragment>
|
||||||
|
|
@ -1498,7 +1521,7 @@ Cgo
|
||||||
<rect key="frame" x="0.0" y="244" width="414" height="160"/>
|
<rect key="frame" x="0.0" y="244" width="414" height="160"/>
|
||||||
<autoresizingMask key="autoresizingMask"/>
|
<autoresizingMask key="autoresizingMask"/>
|
||||||
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" tableViewCell="3po-uS-Nch" id="pet-if-EHU">
|
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" tableViewCell="3po-uS-Nch" id="pet-if-EHU">
|
||||||
<rect key="frame" x="0.0" y="0.0" width="414" height="160"/>
|
<rect key="frame" x="0.0" y="0.0" width="414" height="159.5"/>
|
||||||
<autoresizingMask key="autoresizingMask"/>
|
<autoresizingMask key="autoresizingMask"/>
|
||||||
<subviews>
|
<subviews>
|
||||||
<textView clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="Y9t-DD-6uH">
|
<textView clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="Y9t-DD-6uH">
|
||||||
|
|
@ -1525,7 +1548,7 @@ Cgo
|
||||||
<rect key="frame" x="0.0" y="443" width="414" height="160"/>
|
<rect key="frame" x="0.0" y="443" width="414" height="160"/>
|
||||||
<autoresizingMask key="autoresizingMask"/>
|
<autoresizingMask key="autoresizingMask"/>
|
||||||
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" tableViewCell="nmc-vy-Ab5" id="TQD-GC-YOY">
|
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" tableViewCell="nmc-vy-Ab5" id="TQD-GC-YOY">
|
||||||
<rect key="frame" x="0.0" y="0.0" width="414" height="160"/>
|
<rect key="frame" x="0.0" y="0.0" width="414" height="159.5"/>
|
||||||
<autoresizingMask key="autoresizingMask"/>
|
<autoresizingMask key="autoresizingMask"/>
|
||||||
<subviews>
|
<subviews>
|
||||||
<textView clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="23o-MP-wQY">
|
<textView clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="23o-MP-wQY">
|
||||||
|
|
@ -1568,23 +1591,130 @@ Cgo
|
||||||
</objects>
|
</objects>
|
||||||
<point key="canvasLocation" x="6083" y="2895"/>
|
<point key="canvasLocation" x="6083" y="2895"/>
|
||||||
</scene>
|
</scene>
|
||||||
<!--Navigation Controller-->
|
<!--Git Signature-->
|
||||||
<scene sceneID="E7j-qe-O6d">
|
<scene sceneID="eTh-B3-0rv">
|
||||||
<objects>
|
<objects>
|
||||||
<navigationController automaticallyAdjustsScrollViewInsets="NO" id="lY3-ru-aoM" sceneMemberID="viewController">
|
<tableViewController id="zDo-XK-ymp" customClass="GitConfigSettingTableViewController" customModule="pass" customModuleProvider="target" sceneMemberID="viewController">
|
||||||
|
<tableView key="view" clipsSubviews="YES" contentMode="scaleToFill" alwaysBounceVertical="YES" dataMode="static" style="grouped" separatorStyle="default" allowsSelection="NO" rowHeight="44" sectionHeaderHeight="18" sectionFooterHeight="18" id="9r8-pk-GBf">
|
||||||
|
<rect key="frame" x="0.0" y="0.0" width="414" height="736"/>
|
||||||
|
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
||||||
|
<color key="backgroundColor" cocoaTouchSystemColor="groupTableViewBackgroundColor"/>
|
||||||
|
<sections>
|
||||||
|
<tableViewSection id="TCt-zE-c6t">
|
||||||
|
<cells>
|
||||||
|
<tableViewCell clipsSubviews="YES" contentMode="scaleToFill" selectionStyle="none" indentationWidth="10" reuseIdentifier="pgpKeyURLTableViewCell" rowHeight="52" id="4Bh-1D-w5T">
|
||||||
|
<rect key="frame" x="0.0" y="35" width="414" height="52"/>
|
||||||
|
<autoresizingMask key="autoresizingMask"/>
|
||||||
|
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" tableViewCell="4Bh-1D-w5T" id="mIL-ig-tbp">
|
||||||
|
<rect key="frame" x="0.0" y="0.0" width="414" height="51.5"/>
|
||||||
|
<autoresizingMask key="autoresizingMask"/>
|
||||||
|
<subviews>
|
||||||
|
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Name" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="Hlb-Zh-ega">
|
||||||
|
<rect key="frame" x="15" y="8" width="391" height="15"/>
|
||||||
|
<fontDescription key="fontDescription" style="UICTFontTextStyleCaption1"/>
|
||||||
|
<color key="textColor" white="0.33333333333333331" alpha="1" colorSpace="calibratedWhite"/>
|
||||||
|
<nil key="highlightedColor"/>
|
||||||
|
</label>
|
||||||
|
<textField opaque="NO" clipsSubviews="YES" contentMode="scaleToFill" contentHorizontalAlignment="left" contentVerticalAlignment="center" placeholder="name" minimumFontSize="17" clearButtonMode="whileEditing" translatesAutoresizingMaskIntoConstraints="NO" id="fa8-Vc-w8F">
|
||||||
|
<rect key="frame" x="15" y="23" width="391" height="21"/>
|
||||||
|
<nil key="textColor"/>
|
||||||
|
<fontDescription key="fontDescription" style="UICTFontTextStyleBody"/>
|
||||||
|
<textInputTraits key="textInputTraits" autocorrectionType="no" spellCheckingType="no" returnKeyType="next"/>
|
||||||
|
<connections>
|
||||||
|
<outlet property="delegate" destination="zDo-XK-ymp" id="KRG-7d-AfM"/>
|
||||||
|
</connections>
|
||||||
|
</textField>
|
||||||
|
</subviews>
|
||||||
|
<constraints>
|
||||||
|
<constraint firstItem="fa8-Vc-w8F" firstAttribute="top" secondItem="Hlb-Zh-ega" secondAttribute="bottom" id="7dX-q0-fio"/>
|
||||||
|
<constraint firstItem="fa8-Vc-w8F" firstAttribute="leading" secondItem="Hlb-Zh-ega" secondAttribute="leading" id="N0c-Yi-SOw"/>
|
||||||
|
<constraint firstAttribute="trailingMargin" secondItem="Hlb-Zh-ega" secondAttribute="trailing" id="QuW-Id-nJ3"/>
|
||||||
|
<constraint firstAttribute="topMargin" secondItem="Hlb-Zh-ega" secondAttribute="top" id="Wk8-N9-29h"/>
|
||||||
|
<constraint firstAttribute="trailingMargin" secondItem="fa8-Vc-w8F" secondAttribute="trailing" id="dWA-Zk-8Xs"/>
|
||||||
|
<constraint firstItem="Hlb-Zh-ega" firstAttribute="leading" secondItem="mIL-ig-tbp" secondAttribute="leadingMargin" constant="7" id="rEw-IA-Jqp"/>
|
||||||
|
</constraints>
|
||||||
|
</tableViewCellContentView>
|
||||||
|
</tableViewCell>
|
||||||
|
<tableViewCell clipsSubviews="YES" contentMode="scaleToFill" selectionStyle="none" indentationWidth="10" reuseIdentifier="pgpKeyURLTableViewCell" rowHeight="52" id="JRY-pY-XtS">
|
||||||
|
<rect key="frame" x="0.0" y="87" width="414" height="52"/>
|
||||||
|
<autoresizingMask key="autoresizingMask"/>
|
||||||
|
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" tableViewCell="JRY-pY-XtS" id="KrQ-Ih-dk8">
|
||||||
|
<rect key="frame" x="0.0" y="0.0" width="414" height="51.5"/>
|
||||||
|
<autoresizingMask key="autoresizingMask"/>
|
||||||
|
<subviews>
|
||||||
|
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Email" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="m25-Qy-XwU">
|
||||||
|
<rect key="frame" x="15" y="8" width="391" height="15"/>
|
||||||
|
<fontDescription key="fontDescription" style="UICTFontTextStyleCaption1"/>
|
||||||
|
<color key="textColor" white="0.33333333333333331" alpha="1" colorSpace="calibratedWhite"/>
|
||||||
|
<nil key="highlightedColor"/>
|
||||||
|
</label>
|
||||||
|
<textField opaque="NO" clipsSubviews="YES" contentMode="scaleToFill" contentHorizontalAlignment="left" contentVerticalAlignment="center" placeholder="email" minimumFontSize="17" clearButtonMode="whileEditing" translatesAutoresizingMaskIntoConstraints="NO" id="YY9-za-MNV">
|
||||||
|
<rect key="frame" x="15" y="23" width="391" height="21"/>
|
||||||
|
<nil key="textColor"/>
|
||||||
|
<fontDescription key="fontDescription" style="UICTFontTextStyleBody"/>
|
||||||
|
<textInputTraits key="textInputTraits" autocorrectionType="no" spellCheckingType="no" keyboardType="emailAddress" returnKeyType="done"/>
|
||||||
|
<connections>
|
||||||
|
<outlet property="delegate" destination="zDo-XK-ymp" id="HX8-SE-uSw"/>
|
||||||
|
</connections>
|
||||||
|
</textField>
|
||||||
|
</subviews>
|
||||||
|
<constraints>
|
||||||
|
<constraint firstAttribute="topMargin" secondItem="m25-Qy-XwU" secondAttribute="top" id="2Ht-xl-rR3"/>
|
||||||
|
<constraint firstItem="YY9-za-MNV" firstAttribute="top" secondItem="m25-Qy-XwU" secondAttribute="bottom" id="EL6-fb-eg3"/>
|
||||||
|
<constraint firstAttribute="trailingMargin" secondItem="YY9-za-MNV" secondAttribute="trailing" id="KpQ-1j-faY"/>
|
||||||
|
<constraint firstItem="YY9-za-MNV" firstAttribute="leading" secondItem="m25-Qy-XwU" secondAttribute="leading" id="MJG-cs-oIQ"/>
|
||||||
|
<constraint firstAttribute="trailingMargin" secondItem="m25-Qy-XwU" secondAttribute="trailing" id="brX-cL-blr"/>
|
||||||
|
<constraint firstItem="m25-Qy-XwU" firstAttribute="leading" secondItem="KrQ-Ih-dk8" secondAttribute="leadingMargin" constant="7" id="dNf-iF-vSC"/>
|
||||||
|
</constraints>
|
||||||
|
</tableViewCellContentView>
|
||||||
|
</tableViewCell>
|
||||||
|
</cells>
|
||||||
|
</tableViewSection>
|
||||||
|
</sections>
|
||||||
|
<connections>
|
||||||
|
<outlet property="dataSource" destination="zDo-XK-ymp" id="Q18-Fj-bhC"/>
|
||||||
|
<outlet property="delegate" destination="zDo-XK-ymp" id="uMh-l3-IeX"/>
|
||||||
|
</connections>
|
||||||
|
</tableView>
|
||||||
|
<navigationItem key="navigationItem" title="Git Signature" id="pPi-jd-x5U">
|
||||||
|
<barButtonItem key="leftBarButtonItem" systemItem="cancel" id="fQD-Bc-Mcd">
|
||||||
|
<connections>
|
||||||
|
<segue destination="M5P-Ab-cIc" kind="unwind" identifier="cancelGitConfigSettingSegue" unwindAction="cancelGitConfigSettingWithSegue:" id="Z2N-nJ-04S"/>
|
||||||
|
</connections>
|
||||||
|
</barButtonItem>
|
||||||
|
<barButtonItem key="rightBarButtonItem" systemItem="save" id="kMD-4J-wBb">
|
||||||
|
<connections>
|
||||||
|
<segue destination="M5P-Ab-cIc" kind="unwind" identifier="saveGitConfigSettingSegue" unwindAction="saveGitConfigSettingWithSegue:" id="RGN-ff-tuP"/>
|
||||||
|
</connections>
|
||||||
|
</barButtonItem>
|
||||||
|
</navigationItem>
|
||||||
|
<connections>
|
||||||
|
<outlet property="emailTextField" destination="YY9-za-MNV" id="bpt-uh-1iB"/>
|
||||||
|
<outlet property="nameTextField" destination="fa8-Vc-w8F" id="UQ7-gG-Qe4"/>
|
||||||
|
</connections>
|
||||||
|
</tableViewController>
|
||||||
|
<placeholder placeholderIdentifier="IBFirstResponder" id="FmV-2I-aov" userLabel="First Responder" sceneMemberID="firstResponder"/>
|
||||||
|
<exit id="M5P-Ab-cIc" userLabel="Exit" sceneMemberID="exit"/>
|
||||||
|
</objects>
|
||||||
|
<point key="canvasLocation" x="6109" y="5333"/>
|
||||||
|
</scene>
|
||||||
|
<!--Navigation Controller-->
|
||||||
|
<scene sceneID="9zM-kx-OZU">
|
||||||
|
<objects>
|
||||||
|
<navigationController automaticallyAdjustsScrollViewInsets="NO" id="RHo-mb-ayc" sceneMemberID="viewController">
|
||||||
<toolbarItems/>
|
<toolbarItems/>
|
||||||
<navigationBar key="navigationBar" contentMode="scaleToFill" id="6YB-px-rnU">
|
<navigationBar key="navigationBar" contentMode="scaleToFill" id="ch6-Ko-kuU">
|
||||||
<rect key="frame" x="0.0" y="0.0" width="375" height="44"/>
|
<rect key="frame" x="0.0" y="0.0" width="375" height="44"/>
|
||||||
<autoresizingMask key="autoresizingMask"/>
|
<autoresizingMask key="autoresizingMask"/>
|
||||||
</navigationBar>
|
</navigationBar>
|
||||||
<nil name="viewControllers"/>
|
<nil name="viewControllers"/>
|
||||||
<connections>
|
<connections>
|
||||||
<segue destination="A9p-Qb-WmU" kind="relationship" relationship="rootViewController" id="und-S5-p1v"/>
|
<segue destination="zDo-XK-ymp" kind="relationship" relationship="rootViewController" id="vT1-I7-q3N"/>
|
||||||
</connections>
|
</connections>
|
||||||
</navigationController>
|
</navigationController>
|
||||||
<placeholder placeholderIdentifier="IBFirstResponder" id="D45-tZ-3fu" userLabel="First Responder" sceneMemberID="firstResponder"/>
|
<placeholder placeholderIdentifier="IBFirstResponder" id="6nC-pe-hyF" userLabel="First Responder" sceneMemberID="firstResponder"/>
|
||||||
</objects>
|
</objects>
|
||||||
<point key="canvasLocation" x="7086.9565217391309" y="-1008.4239130434784"/>
|
<point key="canvasLocation" x="4967" y="5333"/>
|
||||||
</scene>
|
</scene>
|
||||||
</scenes>
|
</scenes>
|
||||||
<resources>
|
<resources>
|
||||||
|
|
@ -1592,6 +1722,6 @@ Cgo
|
||||||
<image name="Settings" width="25" height="25"/>
|
<image name="Settings" width="25" height="25"/>
|
||||||
</resources>
|
</resources>
|
||||||
<inferredMetricsTieBreakers>
|
<inferredMetricsTieBreakers>
|
||||||
<segue reference="Cpl-XA-cZ9"/>
|
<segue reference="UfP-k3-XeR"/>
|
||||||
</inferredMetricsTieBreakers>
|
</inferredMetricsTieBreakers>
|
||||||
</document>
|
</document>
|
||||||
|
|
|
||||||
|
|
@ -15,10 +15,10 @@ class AddPasswordTableViewController: PasswordEditorTableViewController {
|
||||||
|
|
||||||
override func viewDidLoad() {
|
override func viewDidLoad() {
|
||||||
tableData = [
|
tableData = [
|
||||||
[[.type: PasswordEditorCellType.textFieldCell, .title: "name"]],
|
[[.type: PasswordEditorCellType.nameCell, .title: "name"]],
|
||||||
[[.type: PasswordEditorCellType.fillPasswordCell, .title: "password"],
|
[[.type: PasswordEditorCellType.fillPasswordCell, .title: "password"],
|
||||||
[.type: PasswordEditorCellType.passwordLengthCell, .title: "passwordlength"]],
|
[.type: PasswordEditorCellType.passwordLengthCell, .title: "passwordlength"]],
|
||||||
[[.type: PasswordEditorCellType.textViewCell, .title: "additions"]],
|
[[.type: PasswordEditorCellType.additionsCell, .title: "additions"]],
|
||||||
[[.type: PasswordEditorCellType.scanQRCodeCell]]
|
[[.type: PasswordEditorCellType.scanQRCodeCell]]
|
||||||
]
|
]
|
||||||
super.viewDidLoad()
|
super.viewDidLoad()
|
||||||
|
|
@ -35,21 +35,12 @@ class AddPasswordTableViewController: PasswordEditorTableViewController {
|
||||||
}
|
}
|
||||||
|
|
||||||
// check name
|
// check name
|
||||||
let nameCell = tableView.cellForRow(at: IndexPath(row: 0, section: 0)) as! TextFieldTableViewCell
|
guard nameCell?.getContent()?.isEmpty == false else {
|
||||||
guard nameCell.getContent()!.isEmpty == false else {
|
|
||||||
let alertTitle = "Cannot Add Password"
|
let alertTitle = "Cannot Add Password"
|
||||||
let alertMessage = "Please fill in the name."
|
let alertMessage = "Please fill in the name."
|
||||||
Utils.alert(title: alertTitle, message: alertMessage, controller: self, completion: nil)
|
Utils.alert(title: alertTitle, message: alertMessage, controller: self, completion: nil)
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
// check "/"
|
|
||||||
guard nameCell.getContent()!.contains("/") == false else {
|
|
||||||
let alertTitle = "Cannot Add Password"
|
|
||||||
let alertMessage = "Illegal character."
|
|
||||||
Utils.alert(title: alertTitle, message: alertMessage, controller: self, completion: nil)
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
@ -57,23 +48,15 @@ class AddPasswordTableViewController: PasswordEditorTableViewController {
|
||||||
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
|
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
|
||||||
super.prepare(for: segue, sender: sender)
|
super.prepare(for: segue, sender: sender)
|
||||||
if segue.identifier == "saveAddPasswordSegue" {
|
if segue.identifier == "saveAddPasswordSegue" {
|
||||||
let cells = tableView.visibleCells
|
var plainText = (fillPasswordCell?.getContent())!
|
||||||
var cellContents = [String: String]()
|
if let additionsString = additionsCell?.getContent(), additionsString.isEmpty == false {
|
||||||
for cell in cells {
|
plainText.append("\n")
|
||||||
if let indexPath = tableView.indexPath(for: cell),
|
plainText.append(additionsString)
|
||||||
let contentCell = cell as? ContentTableViewCell,
|
|
||||||
let cellTitle = tableData[indexPath.section][indexPath.row][.title] as? String,
|
|
||||||
let cellContent = contentCell.getContent() {
|
|
||||||
cellContents[cellTitle] = cellContent
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
var plainText = ""
|
let encodedName = (nameCell?.getContent()?.stringByAddingPercentEncodingForRFC3986())!
|
||||||
if cellContents["additions"]! != "" {
|
let name = URL(string: encodedName)!.lastPathComponent
|
||||||
plainText = "\(cellContents["password"]!)\n\(cellContents["additions"]!)"
|
let url = URL(string: encodedName)!.appendingPathExtension("gpg")
|
||||||
} else {
|
password = Password(name: name, url: url, plainText: plainText)
|
||||||
plainText = "\(cellContents["password"]!)"
|
|
||||||
}
|
|
||||||
password = Password(name: cellContents["name"]!, plainText: plainText)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -13,6 +13,7 @@ import SwiftyUserDefaults
|
||||||
class AdvancedSettingsTableViewController: UITableViewController {
|
class AdvancedSettingsTableViewController: UITableViewController {
|
||||||
|
|
||||||
@IBOutlet weak var encryptInASCIIArmoredTableViewCell: UITableViewCell!
|
@IBOutlet weak var encryptInASCIIArmoredTableViewCell: UITableViewCell!
|
||||||
|
@IBOutlet weak var gitSignatureTableViewCell: UITableViewCell!
|
||||||
@IBOutlet weak var eraseDataTableViewCell: UITableViewCell!
|
@IBOutlet weak var eraseDataTableViewCell: UITableViewCell!
|
||||||
@IBOutlet weak var discardChangesTableViewCell: UITableViewCell!
|
@IBOutlet weak var discardChangesTableViewCell: UITableViewCell!
|
||||||
let passwordStore = PasswordStore.shared
|
let passwordStore = PasswordStore.shared
|
||||||
|
|
@ -30,6 +31,18 @@ class AdvancedSettingsTableViewController: UITableViewController {
|
||||||
encryptInASCIIArmoredSwitch.isOn = Defaults[.encryptInArmored]
|
encryptInASCIIArmoredSwitch.isOn = Defaults[.encryptInArmored]
|
||||||
encryptInASCIIArmoredTableViewCell.accessoryView = encryptInASCIIArmoredSwitch
|
encryptInASCIIArmoredTableViewCell.accessoryView = encryptInASCIIArmoredSwitch
|
||||||
encryptInASCIIArmoredTableViewCell.selectionStyle = .none
|
encryptInASCIIArmoredTableViewCell.selectionStyle = .none
|
||||||
|
setGitSignatureText()
|
||||||
|
}
|
||||||
|
|
||||||
|
private func setGitSignatureText() {
|
||||||
|
let gitSignatureName = passwordStore.gitSignatureForNow.name!
|
||||||
|
let gitSignatureEmail = passwordStore.gitSignatureForNow.email!
|
||||||
|
self.gitSignatureTableViewCell.detailTextLabel?.font = UIFont.systemFont(ofSize: 14)
|
||||||
|
self.gitSignatureTableViewCell.detailTextLabel?.text = "\(gitSignatureName) <\(gitSignatureEmail)>"
|
||||||
|
if Defaults[.gitSignatureName] == nil && Defaults[.gitSignatureEmail] == nil {
|
||||||
|
self.gitSignatureTableViewCell.detailTextLabel?.font = UIFont.systemFont(ofSize: 17)
|
||||||
|
gitSignatureTableViewCell.detailTextLabel?.text = "Not Set"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
|
override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
|
||||||
|
|
@ -48,27 +61,23 @@ class AdvancedSettingsTableViewController: UITableViewController {
|
||||||
} else if tableView.cellForRow(at: indexPath) == discardChangesTableViewCell {
|
} else if tableView.cellForRow(at: indexPath) == discardChangesTableViewCell {
|
||||||
let alert = UIAlertController(title: "Discard All Changes?", message: "Do you want to permanently discard all changes to the local copy of your password data? You cannot undo this action.", preferredStyle: UIAlertControllerStyle.alert)
|
let alert = UIAlertController(title: "Discard All Changes?", message: "Do you want to permanently discard all changes to the local copy of your password data? You cannot undo this action.", preferredStyle: UIAlertControllerStyle.alert)
|
||||||
alert.addAction(UIAlertAction(title: "Discard All Changes", style: UIAlertActionStyle.destructive, handler: {[unowned self] (action) -> Void in
|
alert.addAction(UIAlertAction(title: "Discard All Changes", style: UIAlertActionStyle.destructive, handler: {[unowned self] (action) -> Void in
|
||||||
DispatchQueue.global(qos: .userInitiated).async {
|
SVProgressHUD.show(withStatus: "Resetting ...")
|
||||||
SVProgressHUD.show(withStatus: "Resetting ...")
|
do {
|
||||||
DispatchQueue.main.async {
|
let numberDiscarded = try self.passwordStore.reset()
|
||||||
do {
|
self.navigationController!.popViewController(animated: true)
|
||||||
let numberDiscarded = try self.passwordStore.reset()
|
switch numberDiscarded {
|
||||||
self.navigationController!.popViewController(animated: true)
|
case 0:
|
||||||
switch numberDiscarded {
|
SVProgressHUD.showSuccess(withStatus: "No local commits")
|
||||||
case 0:
|
case 1:
|
||||||
SVProgressHUD.showSuccess(withStatus: "No local commits")
|
SVProgressHUD.showSuccess(withStatus: "Discarded 1 commit")
|
||||||
case 1:
|
default:
|
||||||
SVProgressHUD.showSuccess(withStatus: "Discarded 1 commit")
|
SVProgressHUD.showSuccess(withStatus: "Discarded \(numberDiscarded) commits")
|
||||||
default:
|
|
||||||
SVProgressHUD.showSuccess(withStatus: "Discarded \(numberDiscarded) commits")
|
|
||||||
}
|
|
||||||
SVProgressHUD.dismiss(withDelay: 1)
|
|
||||||
} catch {
|
|
||||||
Utils.alert(title: "Error", message: error.localizedDescription, controller: self, completion: nil)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
SVProgressHUD.dismiss(withDelay: 1)
|
||||||
|
} catch {
|
||||||
|
Utils.alert(title: "Error", message: error.localizedDescription, controller: self, completion: nil)
|
||||||
}
|
}
|
||||||
|
|
||||||
}))
|
}))
|
||||||
alert.addAction(UIAlertAction(title: "Dismiss", style: UIAlertActionStyle.cancel, handler:nil))
|
alert.addAction(UIAlertAction(title: "Dismiss", style: UIAlertActionStyle.cancel, handler:nil))
|
||||||
self.present(alert, animated: true, completion: nil)
|
self.present(alert, animated: true, completion: nil)
|
||||||
|
|
@ -78,5 +87,19 @@ class AdvancedSettingsTableViewController: UITableViewController {
|
||||||
func encryptInASCIIArmoredAction(_ sender: Any?) {
|
func encryptInASCIIArmoredAction(_ sender: Any?) {
|
||||||
Defaults[.encryptInArmored] = encryptInASCIIArmoredSwitch.isOn
|
Defaults[.encryptInArmored] = encryptInASCIIArmoredSwitch.isOn
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@IBAction func cancelGitConfigSetting(segue: UIStoryboardSegue) {
|
||||||
|
}
|
||||||
|
|
||||||
|
@IBAction func saveGitConfigSetting(segue: UIStoryboardSegue) {
|
||||||
|
if let controller = segue.source as? GitConfigSettingTableViewController {
|
||||||
|
if let gitSignatureName = controller.nameTextField.text,
|
||||||
|
let gitSignatureEmail = controller.emailTextField.text {
|
||||||
|
Defaults[.gitSignatureName] = gitSignatureName.isEmpty ? nil : gitSignatureName
|
||||||
|
Defaults[.gitSignatureEmail] = gitSignatureEmail.isEmpty ? nil : gitSignatureEmail
|
||||||
|
}
|
||||||
|
setGitSignatureText()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -15,6 +15,7 @@ class CommitLogsTableViewController: UITableViewController {
|
||||||
|
|
||||||
override func viewDidLoad() {
|
override func viewDidLoad() {
|
||||||
super.viewDidLoad()
|
super.viewDidLoad()
|
||||||
|
NotificationCenter.default.addObserver(self, selector: #selector(updateCommitLogs), name: .passwordStoreUpdated, object: nil)
|
||||||
commits = passwordStore.getRecentCommits(count: 20)
|
commits = passwordStore.getRecentCommits(count: 20)
|
||||||
self.tableView.estimatedRowHeight = 50
|
self.tableView.estimatedRowHeight = 50
|
||||||
self.tableView.rowHeight = UITableViewAutomaticDimension
|
self.tableView.rowHeight = UITableViewAutomaticDimension
|
||||||
|
|
@ -31,12 +32,17 @@ class CommitLogsTableViewController: UITableViewController {
|
||||||
formatter.timeStyle = .medium
|
formatter.timeStyle = .medium
|
||||||
let dateString = formatter.string(from: commits[indexPath.row].commitDate)
|
let dateString = formatter.string(from: commits[indexPath.row].commitDate)
|
||||||
|
|
||||||
let author = cell.contentView.viewWithTag(100) as? UILabel
|
let author = cell.contentView.viewWithTag(200) as? UILabel
|
||||||
let dateLabel = cell.contentView.viewWithTag(101) as? UILabel
|
let dateLabel = cell.contentView.viewWithTag(201) as? UILabel
|
||||||
let messageLabel = cell.contentView.viewWithTag(102) as? UILabel
|
let messageLabel = cell.contentView.viewWithTag(202) as? UILabel
|
||||||
author?.text = commits[indexPath.row].author?.name
|
author?.text = commits[indexPath.row].author?.name
|
||||||
dateLabel?.text = dateString
|
dateLabel?.text = dateString
|
||||||
messageLabel?.text = commits[indexPath.row].message?.trimmingCharacters(in: .whitespacesAndNewlines)
|
messageLabel?.text = commits[indexPath.row].message?.trimmingCharacters(in: .whitespacesAndNewlines)
|
||||||
return cell
|
return cell
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func updateCommitLogs () {
|
||||||
|
commits = passwordStore.getRecentCommits(count: 20)
|
||||||
|
tableView.reloadData()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -11,10 +11,10 @@ import UIKit
|
||||||
class EditPasswordTableViewController: PasswordEditorTableViewController {
|
class EditPasswordTableViewController: PasswordEditorTableViewController {
|
||||||
override func viewDidLoad() {
|
override func viewDidLoad() {
|
||||||
tableData = [
|
tableData = [
|
||||||
[[.type: PasswordEditorCellType.textFieldCell, .title: "name", .content: password!.name]],
|
[[.type: PasswordEditorCellType.nameCell, .title: "name", .content: password!.namePath]],
|
||||||
[[.type: PasswordEditorCellType.fillPasswordCell, .title: "password", .content: password!.password],
|
[[.type: PasswordEditorCellType.fillPasswordCell, .title: "password", .content: password!.password],
|
||||||
[.type: PasswordEditorCellType.passwordLengthCell, .title: "passwordlength"]],
|
[.type: PasswordEditorCellType.passwordLengthCell, .title: "passwordlength"]],
|
||||||
[[.type: PasswordEditorCellType.textViewCell, .title: "additions", .content: password!.getAdditionsPlainText()]],
|
[[.type: PasswordEditorCellType.additionsCell, .title: "additions", .content: password!.getAdditionsPlainText()]],
|
||||||
[[.type: PasswordEditorCellType.scanQRCodeCell],
|
[[.type: PasswordEditorCellType.scanQRCodeCell],
|
||||||
[.type: PasswordEditorCellType.deletePasswordCell]]
|
[.type: PasswordEditorCellType.deletePasswordCell]]
|
||||||
]
|
]
|
||||||
|
|
@ -23,15 +23,13 @@ class EditPasswordTableViewController: PasswordEditorTableViewController {
|
||||||
|
|
||||||
override func shouldPerformSegue(withIdentifier identifier: String, sender: Any?) -> Bool {
|
override func shouldPerformSegue(withIdentifier identifier: String, sender: Any?) -> Bool {
|
||||||
if identifier == "saveEditPasswordSegue" {
|
if identifier == "saveEditPasswordSegue" {
|
||||||
if let nameCell = tableView.cellForRow(at: IndexPath(row: 0, section: 0)) as? ContentTableViewCell {
|
if let name = nameCell?.getContent(),
|
||||||
if nameCell.getContent() != password?.name {
|
let path = name.stringByAddingPercentEncodingForRFC3986(),
|
||||||
let alertTitle = "Cannot Save Edit"
|
let _ = URL(string: path) {
|
||||||
let alertMessage = "Editing name is not supported."
|
return true
|
||||||
Utils.alert(title: alertTitle, message: alertMessage, controller: self) {
|
} else {
|
||||||
nameCell.setContent(content: self.password!.name)
|
Utils.alert(title: "Cannot Save", message: "Password name is invalid.", controller: self, completion: nil)
|
||||||
}
|
return false
|
||||||
return false
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return true
|
return true
|
||||||
|
|
@ -40,23 +38,17 @@ class EditPasswordTableViewController: PasswordEditorTableViewController {
|
||||||
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
|
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
|
||||||
super.prepare(for: segue, sender: sender)
|
super.prepare(for: segue, sender: sender)
|
||||||
if segue.identifier == "saveEditPasswordSegue" {
|
if segue.identifier == "saveEditPasswordSegue" {
|
||||||
let cells = tableView.visibleCells
|
var plainText = (fillPasswordCell?.getContent())!
|
||||||
var cellContents = [String: String]()
|
if let additionsString = additionsCell?.getContent(), additionsString.isEmpty == false {
|
||||||
for cell in cells {
|
plainText.append("\n")
|
||||||
if let indexPath = tableView.indexPath(for: cell),
|
plainText.append(additionsString)
|
||||||
let contentCell = cell as? ContentTableViewCell,
|
|
||||||
let cellTitle = tableData[indexPath.section][indexPath.row][.title] as? String,
|
|
||||||
let cellContent = contentCell.getContent() {
|
|
||||||
cellContents[cellTitle] = cellContent
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
var plainText = ""
|
let encodedName = (nameCell?.getContent()?.stringByAddingPercentEncodingForRFC3986())!
|
||||||
if cellContents["additions"]! != "" {
|
let name = URL(string: encodedName)!.lastPathComponent
|
||||||
plainText = "\(cellContents["password"]!)\n\(cellContents["additions"]!)"
|
let url = URL(string: encodedName)!.appendingPathExtension("gpg")
|
||||||
} else {
|
if password!.plainText != plainText || password!.url!.path != url.path {
|
||||||
plainText = "\(cellContents["password"]!)"
|
password!.updatePassword(name: name, url: url, plainText: plainText)
|
||||||
}
|
}
|
||||||
password!.updatePassword(name: cellContents["name"]!, plainText: plainText)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
41
pass/Controllers/GitConfigSettingTableViewController.swift
Normal file
|
|
@ -0,0 +1,41 @@
|
||||||
|
//
|
||||||
|
// GitConfigSettingTableViewController.swift
|
||||||
|
// pass
|
||||||
|
//
|
||||||
|
// Created by Yishi Lin on 10/4/17.
|
||||||
|
// Copyright © 2017 Yishi Lin. All rights reserved.
|
||||||
|
//
|
||||||
|
|
||||||
|
import UIKit
|
||||||
|
import SwiftyUserDefaults
|
||||||
|
|
||||||
|
class GitConfigSettingTableViewController: UITableViewController {
|
||||||
|
let passwordStore = PasswordStore.shared
|
||||||
|
|
||||||
|
@IBOutlet weak var nameTextField: UITextField!
|
||||||
|
@IBOutlet weak var emailTextField: UITextField!
|
||||||
|
|
||||||
|
override func viewDidLoad() {
|
||||||
|
super.viewDidLoad()
|
||||||
|
tableView.rowHeight = UITableViewAutomaticDimension
|
||||||
|
|
||||||
|
let signature = passwordStore.gitSignatureForNow
|
||||||
|
nameTextField.placeholder = signature.name
|
||||||
|
emailTextField.placeholder = signature.email
|
||||||
|
nameTextField.text = Defaults[.gitSignatureName]
|
||||||
|
emailTextField.text = Defaults[.gitSignatureEmail]
|
||||||
|
}
|
||||||
|
|
||||||
|
override func shouldPerformSegue(withIdentifier identifier: String, sender: Any?) -> Bool {
|
||||||
|
if identifier == "saveGitConfigSettingSegue" {
|
||||||
|
let name = nameTextField.text!.isEmpty ? Globals.gitSignatureDefaultName : nameTextField.text!
|
||||||
|
let email = emailTextField.text!.isEmpty ? Globals.gitSignatureDefaultEmail : nameTextField.text!
|
||||||
|
guard GTSignature(name: name, email: email, time: nil) != nil else {
|
||||||
|
Utils.alert(title: "Error", message: "Invalid name or email.", controller: self, completion: nil)
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
@ -21,28 +21,10 @@ class GitSSHKeyArmorSettingTableViewController: UITableViewController, UITextVie
|
||||||
super.viewDidLoad()
|
super.viewDidLoad()
|
||||||
armorPublicKeyTextView.text = Defaults[.gitSSHPublicKeyArmor]
|
armorPublicKeyTextView.text = Defaults[.gitSSHPublicKeyArmor]
|
||||||
armorPrivateKeyTextView.text = Defaults[.gitSSHPrivateKeyArmor]
|
armorPrivateKeyTextView.text = Defaults[.gitSSHPrivateKeyArmor]
|
||||||
gitSSHPrivateKeyPassphrase = passwordStore.gitSSHPrivateKeyPassphrase
|
|
||||||
|
|
||||||
armorPublicKeyTextView.delegate = self
|
armorPublicKeyTextView.delegate = self
|
||||||
armorPrivateKeyTextView.delegate = self
|
armorPrivateKeyTextView.delegate = self
|
||||||
}
|
}
|
||||||
|
|
||||||
private func createSavePassphraseAlert() -> UIAlertController {
|
|
||||||
let savePassphraseAlert = UIAlertController(title: "Passphrase", message: "Do you want to save the passphrase for later sync?", preferredStyle: UIAlertControllerStyle.alert)
|
|
||||||
savePassphraseAlert.addAction(UIAlertAction(title: "No", style: UIAlertActionStyle.default) { _ in
|
|
||||||
Defaults[.isRememberPassphraseOn] = false
|
|
||||||
Defaults[.gitSSHKeySource] = "armor"
|
|
||||||
self.navigationController!.popViewController(animated: true)
|
|
||||||
})
|
|
||||||
savePassphraseAlert.addAction(UIAlertAction(title: "Save", style: UIAlertActionStyle.destructive) {_ in
|
|
||||||
Defaults[.isRememberPassphraseOn] = true
|
|
||||||
self.passwordStore.gitSSHPrivateKeyPassphrase = self.gitSSHPrivateKeyPassphrase
|
|
||||||
Defaults[.gitSSHKeySource] = "armor"
|
|
||||||
self.navigationController!.popViewController(animated: true)
|
|
||||||
})
|
|
||||||
return savePassphraseAlert
|
|
||||||
}
|
|
||||||
|
|
||||||
@IBAction func doneButtonTapped(_ sender: Any) {
|
@IBAction func doneButtonTapped(_ sender: Any) {
|
||||||
Defaults[.gitSSHPublicKeyArmor] = armorPublicKeyTextView.text
|
Defaults[.gitSSHPublicKeyArmor] = armorPublicKeyTextView.text
|
||||||
Defaults[.gitSSHPrivateKeyArmor] = armorPrivateKeyTextView.text
|
Defaults[.gitSSHPrivateKeyArmor] = armorPrivateKeyTextView.text
|
||||||
|
|
@ -52,22 +34,13 @@ class GitSSHKeyArmorSettingTableViewController: UITableViewController, UITextVie
|
||||||
} 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)
|
||||||
}
|
}
|
||||||
let alert = UIAlertController(title: "Passphrase", message: "Please fill in the passphrase of your SSH secret key.", preferredStyle: UIAlertControllerStyle.alert)
|
Defaults[.gitSSHKeySource] = "armor"
|
||||||
alert.addAction(UIAlertAction(title: "OK", style: UIAlertActionStyle.default, handler: {_ in
|
self.navigationController!.popViewController(animated: true)
|
||||||
self.gitSSHPrivateKeyPassphrase = alert.textFields?.first?.text
|
|
||||||
let savePassphraseAlert = self.createSavePassphraseAlert()
|
|
||||||
self.present(savePassphraseAlert, animated: true, completion: nil)
|
|
||||||
}))
|
|
||||||
alert.addTextField(configurationHandler: {(textField: UITextField!) in
|
|
||||||
textField.text = self.gitSSHPrivateKeyPassphrase
|
|
||||||
textField.isSecureTextEntry = true
|
|
||||||
})
|
|
||||||
self.present(alert, animated: true, completion: nil)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func textView(_ textView: UITextView, shouldChangeTextIn range: NSRange, replacementText text: String) -> Bool {
|
func textView(_ textView: UITextView, shouldChangeTextIn range: NSRange, replacementText text: String) -> Bool {
|
||||||
if text == UIPasteboard.general.string {
|
if text == UIPasteboard.general.string {
|
||||||
// user pastes somethint, get ready to clear in 10s
|
// user pastes something, get ready to clear in 10s
|
||||||
recentPastedText = text
|
recentPastedText = text
|
||||||
DispatchQueue.global(qos: .background).asyncAfter(deadline: DispatchTime.now() + 10) { [weak weakSelf = self] in
|
DispatchQueue.global(qos: .background).asyncAfter(deadline: DispatchTime.now() + 10) { [weak weakSelf = self] in
|
||||||
if let pasteboardString = UIPasteboard.general.string,
|
if let pasteboardString = UIPasteboard.general.string,
|
||||||
|
|
|
||||||
|
|
@ -17,8 +17,8 @@ class GitServerSettingTableViewController: UITableViewController {
|
||||||
@IBOutlet weak var authSSHKeyCell: UITableViewCell!
|
@IBOutlet weak var authSSHKeyCell: UITableViewCell!
|
||||||
@IBOutlet weak var authPasswordCell: UITableViewCell!
|
@IBOutlet weak var authPasswordCell: UITableViewCell!
|
||||||
let passwordStore = PasswordStore.shared
|
let passwordStore = PasswordStore.shared
|
||||||
var password: String?
|
var sshLabel: UILabel? = nil
|
||||||
|
|
||||||
var authenticationMethod = Defaults[.gitAuthenticationMethod]
|
var authenticationMethod = Defaults[.gitAuthenticationMethod]
|
||||||
|
|
||||||
private func checkAuthenticationMethod(method: String) {
|
private func checkAuthenticationMethod(method: String) {
|
||||||
|
|
@ -37,24 +37,22 @@ class GitServerSettingTableViewController: UITableViewController {
|
||||||
sshKeyCheckView.isHidden = true
|
sshKeyCheckView.isHidden = true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override func viewWillAppear(_ animated: Bool) {
|
||||||
|
super.viewWillAppear(animated)
|
||||||
|
// Grey out ssh option if ssh_key and ssh_key.pub are not present
|
||||||
|
sshLabel = authSSHKeyCell.subviews[0].subviews[0] as? UILabel
|
||||||
|
sshLabel!.isEnabled = gitSSHKeyExists()
|
||||||
|
|
||||||
|
}
|
||||||
override func viewDidLoad() {
|
override func viewDidLoad() {
|
||||||
super.viewDidLoad()
|
super.viewDidLoad()
|
||||||
if let url = Defaults[.gitURL] {
|
if let url = Defaults[.gitURL] {
|
||||||
gitURLTextField.text = url.absoluteString
|
gitURLTextField.text = url.absoluteString
|
||||||
}
|
}
|
||||||
usernameTextField.text = Defaults[.gitUsername]
|
usernameTextField.text = Defaults[.gitUsername]
|
||||||
password = passwordStore.gitPassword
|
|
||||||
authenticationMethod = Defaults[.gitAuthenticationMethod]
|
authenticationMethod = Defaults[.gitAuthenticationMethod]
|
||||||
|
|
||||||
// Grey out ssh option if ssh_key and ssh_key.pub are not present
|
|
||||||
let sshLabel = authSSHKeyCell.subviews[0].subviews[0] as! UILabel
|
|
||||||
|
|
||||||
sshLabel.isEnabled = gitSSHKeyExists()
|
|
||||||
|
|
||||||
if authenticationMethod == nil || !sshLabel.isEnabled {
|
|
||||||
authenticationMethod = "Password"
|
|
||||||
}
|
|
||||||
|
|
||||||
checkAuthenticationMethod(method: authenticationMethod!)
|
checkAuthenticationMethod(method: authenticationMethod!)
|
||||||
authSSHKeyCell.accessoryType = .detailButton
|
authSSHKeyCell.accessoryType = .detailButton
|
||||||
}
|
}
|
||||||
|
|
@ -107,24 +105,22 @@ class GitServerSettingTableViewController: UITableViewController {
|
||||||
tableView.deselectRow(at: indexPath, animated: true)
|
tableView.deselectRow(at: indexPath, animated: true)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private func doClone() {
|
||||||
|
if self.shouldPerformSegue(withIdentifier: "saveGitServerSettingSegue", sender: self) {
|
||||||
|
self.performSegue(withIdentifier: "saveGitServerSettingSegue", sender: self)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@IBAction func save(_ sender: Any) {
|
@IBAction func save(_ sender: Any) {
|
||||||
if authenticationMethod == "Password" {
|
if passwordStore.repositoryExisted() {
|
||||||
let alert = UIAlertController(title: "Password", message: "Please fill in the password of your Git account.", preferredStyle: UIAlertControllerStyle.alert)
|
let alert = UIAlertController(title: "Erase Current Password Store Data?", message: "A cloned password store exists. This operation will erase all local data. Data on your remote server will not be affected.", preferredStyle: UIAlertControllerStyle.alert)
|
||||||
alert.addAction(UIAlertAction(title: "OK", style: UIAlertActionStyle.default, handler: {_ in
|
alert.addAction(UIAlertAction(title: "Erase", style: UIAlertActionStyle.destructive, handler: { _ in
|
||||||
self.password = alert.textFields!.first!.text
|
self.doClone()
|
||||||
if self.shouldPerformSegue(withIdentifier: "saveGitServerSettingSegue", sender: self) {
|
|
||||||
self.performSegue(withIdentifier: "saveGitServerSettingSegue", sender: self)
|
|
||||||
}
|
|
||||||
}))
|
}))
|
||||||
alert.addTextField(configurationHandler: {(textField: UITextField!) in
|
alert.addAction(UIAlertAction(title: "Cancel", style: UIAlertActionStyle.cancel, handler: nil))
|
||||||
textField.text = self.password
|
|
||||||
textField.isSecureTextEntry = true
|
|
||||||
})
|
|
||||||
self.present(alert, animated: true, completion: nil)
|
self.present(alert, animated: true, completion: nil)
|
||||||
} else {
|
} else {
|
||||||
if self.shouldPerformSegue(withIdentifier: "saveGitServerSettingSegue", sender: self) {
|
doClone()
|
||||||
self.performSegue(withIdentifier: "saveGitServerSettingSegue", sender: self)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -158,30 +154,8 @@ class GitServerSettingTableViewController: UITableViewController {
|
||||||
|
|
||||||
if (gitSSHKeyExists()) {
|
if (gitSSHKeyExists()) {
|
||||||
let fileAction = UIAlertAction(title: fileActionTitle, style: .default) { _ in
|
let fileAction = UIAlertAction(title: fileActionTitle, style: .default) { _ in
|
||||||
let alert = UIAlertController(
|
Defaults[.gitSSHKeySource] = "file"
|
||||||
title: "SSH Key Passphrase",
|
|
||||||
message: "Please fill in the passphrase for your Git Repository SSH key.",
|
|
||||||
preferredStyle: UIAlertControllerStyle.alert
|
|
||||||
)
|
|
||||||
|
|
||||||
alert.addAction(
|
|
||||||
UIAlertAction(
|
|
||||||
title: "OK",
|
|
||||||
style: UIAlertActionStyle.default,
|
|
||||||
handler: {_ in
|
|
||||||
self.passwordStore.gitSSHPrivateKeyPassphrase = alert.textFields!.first!.text!
|
|
||||||
}
|
|
||||||
)
|
|
||||||
)
|
|
||||||
|
|
||||||
alert.addTextField(
|
|
||||||
configurationHandler: {(textField: UITextField!) in
|
|
||||||
textField.text = self.passwordStore.gitSSHPrivateKeyPassphrase
|
|
||||||
textField.isSecureTextEntry = true
|
|
||||||
}
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
Defaults[.gitSSHKeySource] = "file"
|
|
||||||
optionMenu.addAction(fileAction)
|
optionMenu.addAction(fileAction)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -189,6 +163,7 @@ class GitServerSettingTableViewController: UITableViewController {
|
||||||
let deleteAction = UIAlertAction(title: "Remove Git SSH Keys", style: .destructive) { _ in
|
let deleteAction = UIAlertAction(title: "Remove Git SSH Keys", style: .destructive) { _ in
|
||||||
Utils.removeGitSSHKeys()
|
Utils.removeGitSSHKeys()
|
||||||
Defaults[.gitSSHKeySource] = nil
|
Defaults[.gitSSHKeySource] = nil
|
||||||
|
self.sshLabel!.isEnabled = false
|
||||||
}
|
}
|
||||||
optionMenu.addAction(deleteAction)
|
optionMenu.addAction(deleteAction)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -11,7 +11,6 @@ import AVFoundation
|
||||||
|
|
||||||
class OTPScannerController: QRScannerController {
|
class OTPScannerController: QRScannerController {
|
||||||
|
|
||||||
var tempPassword: Password?
|
|
||||||
var scannedOTP: String?
|
var scannedOTP: String?
|
||||||
|
|
||||||
// MARK: - AVCaptureMetadataOutputObjectsDelegate Methods
|
// MARK: - AVCaptureMetadataOutputObjectsDelegate Methods
|
||||||
|
|
@ -27,14 +26,11 @@ class OTPScannerController: QRScannerController {
|
||||||
// check whether it is a valid result
|
// check whether it is a valid result
|
||||||
if let scannedString = metadataObj.stringValue {
|
if let scannedString = metadataObj.stringValue {
|
||||||
if let (accept, message) = delegate?.checkScannedOutput(line: scannedString) {
|
if let (accept, message) = delegate?.checkScannedOutput(line: scannedString) {
|
||||||
|
scannerOutput.text = message
|
||||||
if accept == true {
|
if accept == true {
|
||||||
captureSession?.stopRunning()
|
captureSession?.stopRunning()
|
||||||
scannedOTP = scannedString
|
scannedOTP = scannedString
|
||||||
tempPassword = Password(name: "empty", plainText: scannedString)
|
presentSaveAlert()
|
||||||
// set scannerOutput
|
|
||||||
setupOneTimePasswordMessage()
|
|
||||||
} else {
|
|
||||||
scannerOutput.text = message
|
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// no delegate, show the scanned result
|
// no delegate, show the scanned result
|
||||||
|
|
@ -50,34 +46,28 @@ class OTPScannerController: QRScannerController {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private func setupOneTimePasswordMessage() {
|
private func presentSaveAlert() {
|
||||||
if let password = tempPassword {
|
// initialize alert
|
||||||
if password.otpType == .hotp {
|
let password = Password(name: "empty", url: nil, plainText: scannedOTP!)
|
||||||
// hotp, no need to refresh
|
let (title, content) = password.getOtpStrings()!
|
||||||
let (title, content) = password.getOtpStrings()!
|
let alert = UIAlertController(title: "Success", message: "\(title): \(content)", preferredStyle: UIAlertControllerStyle.alert)
|
||||||
scannerOutput.text = "\(title):\(content)"
|
alert.addAction(UIAlertAction(title: "Save", style: UIAlertActionStyle.default, handler: {[unowned self] (action) -> Void in
|
||||||
} else if password.otpType == .totp {
|
self.delegate?.handleScannedOutput(line: self.scannedOTP!)
|
||||||
// totp, refresh
|
self.navigationController?.popViewController(animated: true)
|
||||||
Timer.scheduledTimer(withTimeInterval: 1, repeats: true) {
|
}))
|
||||||
[weak weakSelf = self] timer in
|
|
||||||
|
if password.otpType == .hotp {
|
||||||
|
// hotp, no need to refresh
|
||||||
|
self.present(alert, animated: true, completion: nil)
|
||||||
|
} else if password.otpType == .totp {
|
||||||
|
// totp, refresh otp
|
||||||
|
self.present(alert, animated: true) {
|
||||||
|
let alertController = self.presentedViewController as! UIAlertController
|
||||||
|
Timer.scheduledTimer(withTimeInterval: 1, repeats: true) {_ in
|
||||||
let (title, content) = password.getOtpStrings()!
|
let (title, content) = password.getOtpStrings()!
|
||||||
weakSelf?.scannerOutput.text = "\(title):\(content)"
|
alertController.message = "\(title): \(content)"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override func shouldPerformSegue(withIdentifier identifier: String, sender: Any?) -> Bool {
|
|
||||||
if identifier == "saveAddScannedOTPSegue" {
|
|
||||||
return tempPassword != nil
|
|
||||||
}
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
// override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
|
|
||||||
// super.prepare(for: segue, sender: sender)
|
|
||||||
// if segue.identifier == "saveAddScannedOTPSegue" {
|
|
||||||
// delegate?.handleScannedOutput(line: scannedOTP)
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -28,6 +28,7 @@ class PGPKeyArmorSettingTableViewController: UITableViewController, UITextViewDe
|
||||||
let savePassphraseAlert = UIAlertController(title: "Passphrase", message: "Do you want to save the passphrase for later decryption?", preferredStyle: UIAlertControllerStyle.alert)
|
let savePassphraseAlert = UIAlertController(title: "Passphrase", message: "Do you want to save the passphrase for later decryption?", preferredStyle: UIAlertControllerStyle.alert)
|
||||||
savePassphraseAlert.addAction(UIAlertAction(title: "No", style: UIAlertActionStyle.default) { _ in
|
savePassphraseAlert.addAction(UIAlertAction(title: "No", style: UIAlertActionStyle.default) { _ in
|
||||||
Defaults[.isRememberPassphraseOn] = false
|
Defaults[.isRememberPassphraseOn] = false
|
||||||
|
self.pgpPassphrase = nil
|
||||||
self.performSegue(withIdentifier: "savePGPKeySegue", sender: self)
|
self.performSegue(withIdentifier: "savePGPKeySegue", sender: self)
|
||||||
})
|
})
|
||||||
savePassphraseAlert.addAction(UIAlertAction(title: "Save", style: UIAlertActionStyle.destructive) {_ in
|
savePassphraseAlert.addAction(UIAlertAction(title: "Save", style: UIAlertActionStyle.destructive) {_ in
|
||||||
|
|
|
||||||
|
|
@ -14,7 +14,6 @@ import SVProgressHUD
|
||||||
class PasswordDetailTableViewController: UITableViewController, UIGestureRecognizerDelegate {
|
class PasswordDetailTableViewController: UITableViewController, UIGestureRecognizerDelegate {
|
||||||
var passwordEntity: PasswordEntity?
|
var passwordEntity: PasswordEntity?
|
||||||
private var password: Password?
|
private var password: Password?
|
||||||
private var passwordCategoryText = ""
|
|
||||||
private var passwordImage: UIImage?
|
private var passwordImage: UIImage?
|
||||||
private var oneTimePasswordIndexPath : IndexPath?
|
private var oneTimePasswordIndexPath : IndexPath?
|
||||||
private var shouldPopCurrentView = false
|
private var shouldPopCurrentView = false
|
||||||
|
|
@ -77,7 +76,6 @@ class PasswordDetailTableViewController: UITableViewController, UIGestureRecogni
|
||||||
tableView.register(UINib(nibName: "LabelTableViewCell", bundle: nil), forCellReuseIdentifier: "labelCell")
|
tableView.register(UINib(nibName: "LabelTableViewCell", bundle: nil), forCellReuseIdentifier: "labelCell")
|
||||||
tableView.register(UINib(nibName: "PasswordDetailTitleTableViewCell", bundle: nil), forCellReuseIdentifier: "passwordDetailTitleTableViewCell")
|
tableView.register(UINib(nibName: "PasswordDetailTitleTableViewCell", bundle: nil), forCellReuseIdentifier: "passwordDetailTitleTableViewCell")
|
||||||
|
|
||||||
passwordCategoryText = passwordEntity!.getCategoryText()
|
|
||||||
let tapGesture = UITapGestureRecognizer(target: self, action: #selector(PasswordDetailTableViewController.tapMenu(recognizer:)))
|
let tapGesture = UITapGestureRecognizer(target: self, action: #selector(PasswordDetailTableViewController.tapMenu(recognizer:)))
|
||||||
tapGesture.cancelsTouchesInView = false
|
tapGesture.cancelsTouchesInView = false
|
||||||
tableView.addGestureRecognizer(tapGesture)
|
tableView.addGestureRecognizer(tapGesture)
|
||||||
|
|
@ -96,24 +94,7 @@ class PasswordDetailTableViewController: UITableViewController, UIGestureRecogni
|
||||||
let image = UIImage(data: imageData as Data)
|
let image = UIImage(data: imageData as Data)
|
||||||
passwordImage = image
|
passwordImage = image
|
||||||
}
|
}
|
||||||
|
self.decryptThenShowPassword()
|
||||||
var passphrase = ""
|
|
||||||
if Defaults[.isRememberPassphraseOn] && self.passwordStore.pgpKeyPassphrase != nil {
|
|
||||||
passphrase = self.passwordStore.pgpKeyPassphrase!
|
|
||||||
self.decryptThenShowPassword(passphrase: passphrase)
|
|
||||||
} else {
|
|
||||||
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
|
|
||||||
passphrase = alert.textFields!.first!.text!
|
|
||||||
self.decryptThenShowPassword(passphrase: passphrase)
|
|
||||||
}))
|
|
||||||
alert.addTextField(configurationHandler: {(textField: UITextField!) in
|
|
||||||
textField.text = ""
|
|
||||||
textField.isSecureTextEntry = true
|
|
||||||
})
|
|
||||||
self.present(alert, animated: true, completion: nil)
|
|
||||||
}
|
|
||||||
|
|
||||||
self.setupOneTimePasswordAutoRefresh()
|
self.setupOneTimePasswordAutoRefresh()
|
||||||
|
|
||||||
// pop the current view because this password might be "discarded"
|
// pop the current view because this password might be "discarded"
|
||||||
|
|
@ -137,14 +118,33 @@ class PasswordDetailTableViewController: UITableViewController, UIGestureRecogni
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private func decryptThenShowPassword(passphrase: String) {
|
private func requestPGPKeyPassphrase() -> String {
|
||||||
|
let sem = DispatchSemaphore(value: 0)
|
||||||
|
var passphrase = ""
|
||||||
|
DispatchQueue.main.async {
|
||||||
|
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
|
||||||
|
passphrase = alert.textFields!.first!.text!
|
||||||
|
sem.signal()
|
||||||
|
}))
|
||||||
|
alert.addTextField(configurationHandler: {(textField: UITextField!) in
|
||||||
|
textField.text = ""
|
||||||
|
textField.isSecureTextEntry = true
|
||||||
|
})
|
||||||
|
self.present(alert, animated: true, completion: nil)
|
||||||
|
}
|
||||||
|
let _ = sem.wait(timeout: DispatchTime.distantFuture)
|
||||||
if Defaults[.isRememberPassphraseOn] {
|
if Defaults[.isRememberPassphraseOn] {
|
||||||
self.passwordStore.pgpKeyPassphrase = passphrase
|
self.passwordStore.pgpKeyPassphrase = passphrase
|
||||||
}
|
}
|
||||||
|
return passphrase
|
||||||
|
}
|
||||||
|
|
||||||
|
private func decryptThenShowPassword() {
|
||||||
DispatchQueue.global(qos: .userInitiated).async {
|
DispatchQueue.global(qos: .userInitiated).async {
|
||||||
// decrypt password
|
// decrypt password
|
||||||
do {
|
do {
|
||||||
self.password = try self.passwordEntity!.decrypt(passphrase: passphrase)!
|
self.password = try self.passwordStore.decrypt(passwordEntity: self.passwordEntity!, requestPGPKeyPassphrase: self.requestPGPKeyPassphrase)
|
||||||
} catch {
|
} catch {
|
||||||
DispatchQueue.main.async {
|
DispatchQueue.main.async {
|
||||||
let alert = UIAlertController(title: "Cannot Show Password", message: error.localizedDescription, preferredStyle: UIAlertControllerStyle.alert)
|
let alert = UIAlertController(title: "Cannot Show Password", message: error.localizedDescription, preferredStyle: UIAlertControllerStyle.alert)
|
||||||
|
|
@ -164,11 +164,7 @@ class PasswordDetailTableViewController: UITableViewController, UIGestureRecogni
|
||||||
DispatchQueue.main.async { [weak self] in
|
DispatchQueue.main.async { [weak self] in
|
||||||
self?.indicator.stopAnimating()
|
self?.indicator.stopAnimating()
|
||||||
self?.setTableData()
|
self?.setTableData()
|
||||||
UIView.performWithoutAnimation {
|
self?.tableView.reloadData()
|
||||||
self?.tableView.reloadData()
|
|
||||||
// add layoutIfNeeded solves the "flickering problem" during refresh
|
|
||||||
self?.tableView.layoutIfNeeded()
|
|
||||||
}
|
|
||||||
self?.editUIBarButtonItem.isEnabled = true
|
self?.editUIBarButtonItem.isEnabled = true
|
||||||
if let urlString = self?.password?.getURLString() {
|
if let urlString = self?.password?.getURLString() {
|
||||||
if self?.passwordEntity?.image == nil {
|
if self?.passwordEntity?.image == nil {
|
||||||
|
|
@ -216,29 +212,26 @@ class PasswordDetailTableViewController: UITableViewController, UIGestureRecogni
|
||||||
}
|
}
|
||||||
|
|
||||||
@IBAction private func saveEditPassword(segue: UIStoryboardSegue) {
|
@IBAction private func saveEditPassword(segue: UIStoryboardSegue) {
|
||||||
if self.password!.changed {
|
if self.password!.changed != 0 {
|
||||||
SVProgressHUD.show(withStatus: "Saving")
|
SVProgressHUD.show(withStatus: "Saving")
|
||||||
DispatchQueue.global(qos: .userInitiated).async {
|
do {
|
||||||
self.passwordStore.update(passwordEntity: self.passwordEntity!, password: self.password!, progressBlock: { progress in
|
self.passwordEntity = try self.passwordStore.edit(passwordEntity: self.passwordEntity!, password: self.password!)
|
||||||
DispatchQueue.main.async {
|
} catch {
|
||||||
SVProgressHUD.showProgress(progress, status: "Encrypting")
|
Utils.alert(title: "Error", message: error.localizedDescription, controller: self, completion: nil)
|
||||||
}
|
|
||||||
})
|
|
||||||
DispatchQueue.main.async {
|
|
||||||
self.passwordEntity!.synced = false
|
|
||||||
self.passwordStore.saveUpdated(passwordEntity: self.passwordEntity!)
|
|
||||||
self.setTableData()
|
|
||||||
self.tableView.reloadData()
|
|
||||||
SVProgressHUD.showSuccess(withStatus: "Success")
|
|
||||||
SVProgressHUD.dismiss(withDelay: 1)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
self.setTableData()
|
||||||
|
self.tableView.reloadData()
|
||||||
|
SVProgressHUD.showSuccess(withStatus: "Success")
|
||||||
|
SVProgressHUD.dismiss(withDelay: 1)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@IBAction private func deletePassword(segue: UIStoryboardSegue) {
|
@IBAction private func deletePassword(segue: UIStoryboardSegue) {
|
||||||
print("delete")
|
do {
|
||||||
passwordStore.delete(passwordEntity: passwordEntity!)
|
try passwordStore.delete(passwordEntity: passwordEntity!)
|
||||||
|
} catch {
|
||||||
|
Utils.alert(title: "Error", message: error.localizedDescription, controller: self, completion: nil)
|
||||||
|
}
|
||||||
let _ = navigationController?.popViewController(animated: true)
|
let _ = navigationController?.popViewController(animated: true)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -392,16 +385,14 @@ class PasswordDetailTableViewController: UITableViewController, UIGestureRecogni
|
||||||
}
|
}
|
||||||
|
|
||||||
// commit the change of HOTP counter
|
// commit the change of HOTP counter
|
||||||
if password!.changed {
|
if password!.changed != 0 {
|
||||||
DispatchQueue.global(qos: .userInitiated).async {
|
do {
|
||||||
self.passwordStore.update(passwordEntity: self.passwordEntity!, password: self.password!, progressBlock: {_ in })
|
self.passwordEntity = try self.passwordStore.edit(passwordEntity: self.passwordEntity!, password: self.password!)
|
||||||
DispatchQueue.main.async {
|
} catch {
|
||||||
self.passwordEntity!.synced = false
|
Utils.alert(title: "Error", message: error.localizedDescription, controller: self, completion: nil)
|
||||||
self.passwordStore.saveUpdated(passwordEntity: self.passwordEntity!)
|
|
||||||
SVProgressHUD.showSuccess(withStatus: "Password Copied\nCounter Updated")
|
|
||||||
SVProgressHUD.dismiss(withDelay: 1)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
SVProgressHUD.showSuccess(withStatus: "Password Copied\nCounter Updated")
|
||||||
|
SVProgressHUD.dismiss(withDelay: 1)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -432,12 +423,14 @@ class PasswordDetailTableViewController: UITableViewController, UIGestureRecogni
|
||||||
case .name:
|
case .name:
|
||||||
let cell = tableView.dequeueReusableCell(withIdentifier: "passwordDetailTitleTableViewCell", for: indexPath) as! PasswordDetailTitleTableViewCell
|
let cell = tableView.dequeueReusableCell(withIdentifier: "passwordDetailTitleTableViewCell", for: indexPath) as! PasswordDetailTitleTableViewCell
|
||||||
cell.passwordImageImageView.image = passwordImage ?? #imageLiteral(resourceName: "PasswordImagePlaceHolder")
|
cell.passwordImageImageView.image = passwordImage ?? #imageLiteral(resourceName: "PasswordImagePlaceHolder")
|
||||||
var passwordName = passwordEntity!.name!
|
if let passwordName = passwordEntity!.name {
|
||||||
if passwordEntity!.synced == false {
|
if passwordEntity!.synced == false {
|
||||||
passwordName = "\(passwordName) ↻"
|
cell.nameLabel.text = "\(passwordName) ↻"
|
||||||
|
} else {
|
||||||
|
cell.nameLabel.text = passwordName
|
||||||
|
}
|
||||||
}
|
}
|
||||||
cell.nameLabel.text = passwordName
|
cell.categoryLabel.text = passwordEntity!.getCategoryText()
|
||||||
cell.categoryLabel.text = passwordCategoryText
|
|
||||||
cell.selectionStyle = .none
|
cell.selectionStyle = .none
|
||||||
return cell
|
return cell
|
||||||
case .main, .addition:
|
case .main, .addition:
|
||||||
|
|
@ -467,7 +460,7 @@ class PasswordDetailTableViewController: UITableViewController, UIGestureRecogni
|
||||||
footerLabel.numberOfLines = 0
|
footerLabel.numberOfLines = 0
|
||||||
footerLabel.font = UIFont.preferredFont(forTextStyle: .footnote)
|
footerLabel.font = UIFont.preferredFont(forTextStyle: .footnote)
|
||||||
footerLabel.textColor = UIColor.gray
|
footerLabel.textColor = UIColor.gray
|
||||||
let dateString = self.passwordStore.getLatestUpdateInfo(filename: (passwordEntity?.path)!)
|
let dateString = self.passwordStore.getLatestUpdateInfo(filename: password!.url!.path)
|
||||||
footerLabel.text = "Last Updated: \(dateString)"
|
footerLabel.text = "Last Updated: \(dateString)"
|
||||||
view.addSubview(footerLabel)
|
view.addSubview(footerLabel)
|
||||||
return view
|
return view
|
||||||
|
|
|
||||||
|
|
@ -11,14 +11,14 @@ import SwiftyUserDefaults
|
||||||
import OneTimePassword
|
import OneTimePassword
|
||||||
|
|
||||||
enum PasswordEditorCellType {
|
enum PasswordEditorCellType {
|
||||||
case textFieldCell, textViewCell, fillPasswordCell, passwordLengthCell, deletePasswordCell, scanQRCodeCell
|
case nameCell, fillPasswordCell, passwordLengthCell, additionsCell, deletePasswordCell, scanQRCodeCell
|
||||||
}
|
}
|
||||||
|
|
||||||
enum PasswordEditorCellKey {
|
enum PasswordEditorCellKey {
|
||||||
case type, title, content, placeholders
|
case type, title, content, placeholders
|
||||||
}
|
}
|
||||||
|
|
||||||
class PasswordEditorTableViewController: UITableViewController, FillPasswordTableViewCellDelegate, PasswordSettingSliderTableViewCellDelegate, QRScannerControllerDelegate {
|
class PasswordEditorTableViewController: UITableViewController, FillPasswordTableViewCellDelegate, PasswordSettingSliderTableViewCellDelegate, QRScannerControllerDelegate, UITextFieldDelegate, UITextViewDelegate {
|
||||||
|
|
||||||
var tableData = [
|
var tableData = [
|
||||||
[Dictionary<PasswordEditorCellKey, Any>]
|
[Dictionary<PasswordEditorCellKey, Any>]
|
||||||
|
|
@ -29,12 +29,15 @@ class PasswordEditorTableViewController: UITableViewController, FillPasswordTabl
|
||||||
|
|
||||||
private var sectionHeaderTitles = ["name", "password", "additions",""].map {$0.uppercased()}
|
private var sectionHeaderTitles = ["name", "password", "additions",""].map {$0.uppercased()}
|
||||||
private var sectionFooterTitles = ["", "", "Use \"key: value\" format for additional fields.", ""]
|
private var sectionFooterTitles = ["", "", "Use \"key: value\" format for additional fields.", ""]
|
||||||
|
private let nameSection = 0
|
||||||
private let passwordSection = 1
|
private let passwordSection = 1
|
||||||
private let additionsSection = 2
|
private let additionsSection = 2
|
||||||
private var hidePasswordSettings = true
|
private var hidePasswordSettings = true
|
||||||
|
|
||||||
private var fillPasswordCell: FillPasswordTableViewCell?
|
var nameCell: TextFieldTableViewCell?
|
||||||
|
var fillPasswordCell: FillPasswordTableViewCell?
|
||||||
private var passwordLengthCell: SliderTableViewCell?
|
private var passwordLengthCell: SliderTableViewCell?
|
||||||
|
var additionsCell: TextViewTableViewCell?
|
||||||
private var deletePasswordCell: UITableViewCell?
|
private var deletePasswordCell: UITableViewCell?
|
||||||
private var scanQRCodeCell: UITableViewCell?
|
private var scanQRCodeCell: UITableViewCell?
|
||||||
|
|
||||||
|
|
@ -50,6 +53,7 @@ class PasswordEditorTableViewController: UITableViewController, FillPasswordTabl
|
||||||
scanQRCodeCell?.textLabel?.text = "Add One-Time Password"
|
scanQRCodeCell?.textLabel?.text = "Add One-Time Password"
|
||||||
scanQRCodeCell?.textLabel?.textColor = Globals.blue
|
scanQRCodeCell?.textLabel?.textColor = Globals.blue
|
||||||
scanQRCodeCell?.selectionStyle = .default
|
scanQRCodeCell?.selectionStyle = .default
|
||||||
|
scanQRCodeCell?.accessoryType = .disclosureIndicator
|
||||||
// scanQRCodeCell?.imageView?.image = #imageLiteral(resourceName: "Camera").withRenderingMode(.alwaysTemplate)
|
// scanQRCodeCell?.imageView?.image = #imageLiteral(resourceName: "Camera").withRenderingMode(.alwaysTemplate)
|
||||||
// scanQRCodeCell?.imageView?.tintColor = Globals.blue
|
// scanQRCodeCell?.imageView?.tintColor = Globals.blue
|
||||||
// scanQRCodeCell?.imageView?.contentMode = .scaleAspectFit
|
// scanQRCodeCell?.imageView?.contentMode = .scaleAspectFit
|
||||||
|
|
@ -76,10 +80,11 @@ class PasswordEditorTableViewController: UITableViewController, FillPasswordTabl
|
||||||
let cellData = tableData[indexPath.section][indexPath.row]
|
let cellData = tableData[indexPath.section][indexPath.row]
|
||||||
|
|
||||||
switch cellData[PasswordEditorCellKey.type] as! PasswordEditorCellType {
|
switch cellData[PasswordEditorCellKey.type] as! PasswordEditorCellType {
|
||||||
case .textViewCell:
|
case .nameCell:
|
||||||
let cell = tableView.dequeueReusableCell(withIdentifier: "textViewCell", for: indexPath) as! ContentTableViewCell
|
nameCell = tableView.dequeueReusableCell(withIdentifier: "textFieldCell", for: indexPath) as? TextFieldTableViewCell
|
||||||
cell.setContent(content: cellData[PasswordEditorCellKey.content] as? String)
|
nameCell?.contentTextField.delegate = self
|
||||||
return cell
|
nameCell?.setContent(content: cellData[PasswordEditorCellKey.content] as? String)
|
||||||
|
return nameCell!
|
||||||
case .fillPasswordCell:
|
case .fillPasswordCell:
|
||||||
fillPasswordCell = tableView.dequeueReusableCell(withIdentifier: "fillPasswordCell", for: indexPath) as? FillPasswordTableViewCell
|
fillPasswordCell = tableView.dequeueReusableCell(withIdentifier: "fillPasswordCell", for: indexPath) as? FillPasswordTableViewCell
|
||||||
fillPasswordCell?.delegate = self
|
fillPasswordCell?.delegate = self
|
||||||
|
|
@ -95,14 +100,15 @@ class PasswordEditorTableViewController: UITableViewController, FillPasswordTabl
|
||||||
defaultValue: lengthSetting?.def ?? 0)
|
defaultValue: lengthSetting?.def ?? 0)
|
||||||
passwordLengthCell?.delegate = self
|
passwordLengthCell?.delegate = self
|
||||||
return passwordLengthCell!
|
return passwordLengthCell!
|
||||||
|
case .additionsCell:
|
||||||
|
additionsCell = tableView.dequeueReusableCell(withIdentifier: "textViewCell", for: indexPath) as?TextViewTableViewCell
|
||||||
|
additionsCell?.contentTextView.delegate = self
|
||||||
|
additionsCell?.setContent(content: cellData[PasswordEditorCellKey.content] as? String)
|
||||||
|
return additionsCell!
|
||||||
case .deletePasswordCell:
|
case .deletePasswordCell:
|
||||||
return deletePasswordCell!
|
return deletePasswordCell!
|
||||||
case .scanQRCodeCell:
|
case .scanQRCodeCell:
|
||||||
return scanQRCodeCell!
|
return scanQRCodeCell!
|
||||||
default:
|
|
||||||
let cell = tableView.dequeueReusableCell(withIdentifier: "textFieldCell", for: indexPath) as! ContentTableViewCell
|
|
||||||
cell.setContent(content: cellData[PasswordEditorCellKey.content] as? String)
|
|
||||||
return cell
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -187,13 +193,16 @@ class PasswordEditorTableViewController: UITableViewController, FillPasswordTabl
|
||||||
|
|
||||||
func insertScannedOTPFields(_ otpauth: String) {
|
func insertScannedOTPFields(_ otpauth: String) {
|
||||||
// update tableData
|
// update tableData
|
||||||
|
var additionsString = ""
|
||||||
if let additionsPlainText = (tableData[additionsSection][0][PasswordEditorCellKey.content] as? String)?.trimmingCharacters(in: .whitespacesAndNewlines), additionsPlainText != "" {
|
if let additionsPlainText = (tableData[additionsSection][0][PasswordEditorCellKey.content] as? String)?.trimmingCharacters(in: .whitespacesAndNewlines), additionsPlainText != "" {
|
||||||
tableData[additionsSection][0][PasswordEditorCellKey.content] = additionsPlainText + "\n" + otpauth
|
additionsString = additionsPlainText + "\n" + otpauth
|
||||||
} else {
|
} else {
|
||||||
tableData[additionsSection][0][PasswordEditorCellKey.content] = otpauth
|
additionsString = otpauth
|
||||||
}
|
}
|
||||||
// reload
|
tableData[additionsSection][0][PasswordEditorCellKey.content] = additionsString
|
||||||
tableView.reloadSections([additionsSection], with: .none)
|
|
||||||
|
// reload the additions cell
|
||||||
|
additionsCell?.setContent(content: additionsString)
|
||||||
}
|
}
|
||||||
|
|
||||||
// MARK: - QRScannerControllerDelegate Methods
|
// MARK: - QRScannerControllerDelegate Methods
|
||||||
|
|
@ -226,11 +235,17 @@ class PasswordEditorTableViewController: UITableViewController, FillPasswordTabl
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@IBAction func saveScannedOTP(segue: UIStoryboardSegue) {
|
// update the data table after editing
|
||||||
if let controller = segue.source as? OTPScannerController {
|
func textFieldDidEndEditing(_ textField: UITextField) {
|
||||||
if let scannedOTP = controller.scannedOTP {
|
if textField == nameCell?.contentTextField {
|
||||||
insertScannedOTPFields(scannedOTP)
|
tableData[nameSection][0][PasswordEditorCellKey.content] = nameCell?.getContent()
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// update the data table after editing
|
||||||
|
func textViewDidEndEditing(_ textView: UITextView) {
|
||||||
|
if textView == additionsCell?.contentTextView {
|
||||||
|
tableData[additionsSection][0][PasswordEditorCellKey.content] = additionsCell?.getContent()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -112,11 +112,7 @@ class PasswordsViewController: UIViewController, UITableViewDataSource, UITableV
|
||||||
SVProgressHUD.show(withStatus: "Saving")
|
SVProgressHUD.show(withStatus: "Saving")
|
||||||
DispatchQueue.global(qos: .userInitiated).async {
|
DispatchQueue.global(qos: .userInitiated).async {
|
||||||
do {
|
do {
|
||||||
try self.passwordStore.add(password: controller.password!, progressBlock: { progress in
|
let _ = try self.passwordStore.add(password: controller.password!)
|
||||||
DispatchQueue.main.async {
|
|
||||||
SVProgressHUD.showProgress(progress, status: "Encrypting")
|
|
||||||
}
|
|
||||||
})
|
|
||||||
DispatchQueue.main.async {
|
DispatchQueue.main.async {
|
||||||
// will trigger reloadTableView() by a notification
|
// will trigger reloadTableView() by a notification
|
||||||
SVProgressHUD.showSuccess(withStatus: "Done")
|
SVProgressHUD.showSuccess(withStatus: "Done")
|
||||||
|
|
@ -132,19 +128,38 @@ class PasswordsViewController: UIViewController, UITableViewDataSource, UITableV
|
||||||
}
|
}
|
||||||
|
|
||||||
private func syncPasswords() {
|
private func syncPasswords() {
|
||||||
|
guard passwordStore.repositoryExisted() else {
|
||||||
|
DispatchQueue.main.asyncAfter(deadline: .now() + .milliseconds(800)) {
|
||||||
|
Utils.alert(title: "Error", message: "There is no password store right now.", controller: self, completion: nil)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
SVProgressHUD.setDefaultMaskType(.black)
|
SVProgressHUD.setDefaultMaskType(.black)
|
||||||
SVProgressHUD.setDefaultStyle(.light)
|
SVProgressHUD.setDefaultStyle(.light)
|
||||||
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
|
||||||
|
if Defaults[.gitAuthenticationMethod] == "Password" {
|
||||||
|
gitCredential = GitCredential(credential: GitCredential.Credential.http(userName: Defaults[.gitUsername]!, controller: self))
|
||||||
|
} else {
|
||||||
|
gitCredential = GitCredential(
|
||||||
|
credential: GitCredential.Credential.ssh(
|
||||||
|
userName: Defaults[.gitUsername]!,
|
||||||
|
publicKeyFile: Globals.gitSSHPublicKeyURL,
|
||||||
|
privateKeyFile: Globals.gitSSHPrivateKeyURL,
|
||||||
|
controller: self
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
DispatchQueue.global(qos: .userInitiated).async { [unowned self] in
|
DispatchQueue.global(qos: .userInitiated).async { [unowned self] in
|
||||||
do {
|
do {
|
||||||
try self.passwordStore.pullRepository(transferProgressBlock: {(git_transfer_progress, stop) in
|
try self.passwordStore.pullRepository(credential: gitCredential, transferProgressBlock: {(git_transfer_progress, stop) in
|
||||||
DispatchQueue.main.async {
|
DispatchQueue.main.async {
|
||||||
SVProgressHUD.showProgress(Float(git_transfer_progress.pointee.received_objects)/Float(git_transfer_progress.pointee.total_objects), status: "Pull Remote Repository")
|
SVProgressHUD.showProgress(Float(git_transfer_progress.pointee.received_objects)/Float(git_transfer_progress.pointee.total_objects), status: "Pull Remote Repository")
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
if numberOfLocalCommits > 0 {
|
if numberOfLocalCommits > 0 {
|
||||||
try self.passwordStore.pushRepository(transferProgressBlock: {(current, total, bytes, stop) in
|
try self.passwordStore.pushRepository(credential: gitCredential, transferProgressBlock: {(current, total, bytes, stop) in
|
||||||
DispatchQueue.main.async {
|
DispatchQueue.main.async {
|
||||||
SVProgressHUD.showProgress(Float(current)/Float(total), status: "Push Remote Repository")
|
SVProgressHUD.showProgress(Float(current)/Float(total), status: "Push Remote Repository")
|
||||||
}
|
}
|
||||||
|
|
@ -152,7 +167,6 @@ class PasswordsViewController: UIViewController, UITableViewDataSource, UITableV
|
||||||
}
|
}
|
||||||
DispatchQueue.main.async {
|
DispatchQueue.main.async {
|
||||||
self.reloadTableView(parent: nil)
|
self.reloadTableView(parent: nil)
|
||||||
Defaults[.gitPasswordAttempts] = 0
|
|
||||||
SVProgressHUD.showSuccess(withStatus: "Done")
|
SVProgressHUD.showSuccess(withStatus: "Done")
|
||||||
SVProgressHUD.dismiss(withDelay: 1)
|
SVProgressHUD.dismiss(withDelay: 1)
|
||||||
}
|
}
|
||||||
|
|
@ -323,15 +337,19 @@ class PasswordsViewController: UIViewController, UITableViewDataSource, UITableV
|
||||||
}
|
}
|
||||||
let password = getPasswordEntry(by: indexPath).passwordEntity!
|
let password = getPasswordEntry(by: indexPath).passwordEntity!
|
||||||
UIImpactFeedbackGenerator(style: .medium).impactOccurred()
|
UIImpactFeedbackGenerator(style: .medium).impactOccurred()
|
||||||
|
decryptThenCopyPassword(passwordEntity: password)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
private func requestPGPKeyPassphrase() -> String {
|
||||||
|
let sem = DispatchSemaphore(value: 0)
|
||||||
var passphrase = ""
|
var passphrase = ""
|
||||||
if Defaults[.isRememberPassphraseOn] && self.passwordStore.pgpKeyPassphrase != nil {
|
DispatchQueue.main.async {
|
||||||
passphrase = self.passwordStore.pgpKeyPassphrase!
|
|
||||||
self.decryptThenCopyPassword(passwordEntity: password, passphrase: passphrase)
|
|
||||||
} else {
|
|
||||||
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
|
||||||
passphrase = alert.textFields!.first!.text!
|
passphrase = alert.textFields!.first!.text!
|
||||||
self.decryptThenCopyPassword(passwordEntity: password, passphrase: passphrase)
|
sem.signal()
|
||||||
}))
|
}))
|
||||||
alert.addTextField(configurationHandler: {(textField: UITextField!) in
|
alert.addTextField(configurationHandler: {(textField: UITextField!) in
|
||||||
textField.text = ""
|
textField.text = ""
|
||||||
|
|
@ -339,17 +357,21 @@ class PasswordsViewController: UIViewController, UITableViewDataSource, UITableV
|
||||||
})
|
})
|
||||||
self.present(alert, animated: true, completion: nil)
|
self.present(alert, animated: true, completion: nil)
|
||||||
}
|
}
|
||||||
|
let _ = sem.wait(timeout: DispatchTime.distantFuture)
|
||||||
|
if Defaults[.isRememberPassphraseOn] {
|
||||||
|
self.passwordStore.pgpKeyPassphrase = passphrase
|
||||||
|
}
|
||||||
|
return passphrase
|
||||||
}
|
}
|
||||||
|
|
||||||
private func decryptThenCopyPassword(passwordEntity: PasswordEntity, passphrase: String) {
|
private func decryptThenCopyPassword(passwordEntity: PasswordEntity) {
|
||||||
SVProgressHUD.setDefaultMaskType(.black)
|
SVProgressHUD.setDefaultMaskType(.black)
|
||||||
SVProgressHUD.setDefaultStyle(.dark)
|
SVProgressHUD.setDefaultStyle(.dark)
|
||||||
SVProgressHUD.show(withStatus: "Decrypting")
|
SVProgressHUD.show(withStatus: "Decrypting")
|
||||||
DispatchQueue.global(qos: .userInteractive).async {
|
DispatchQueue.global(qos: .userInteractive).async {
|
||||||
var decryptedPassword: Password?
|
var decryptedPassword: Password?
|
||||||
do {
|
do {
|
||||||
decryptedPassword = try passwordEntity.decrypt(passphrase: passphrase)!
|
decryptedPassword = try self.passwordStore.decrypt(passwordEntity: passwordEntity, requestPGPKeyPassphrase: self.requestPGPKeyPassphrase)
|
||||||
DispatchQueue.main.async {
|
DispatchQueue.main.async {
|
||||||
Utils.copyToPasteboard(textToCopy: decryptedPassword?.password)
|
Utils.copyToPasteboard(textToCopy: decryptedPassword?.password)
|
||||||
SVProgressHUD.showSuccess(withStatus: "Password copied, and will be cleared in 45 seconds.")
|
SVProgressHUD.showSuccess(withStatus: "Password copied, and will be cleared in 45 seconds.")
|
||||||
|
|
@ -486,9 +508,9 @@ class PasswordsViewController: UIViewController, UITableViewDataSource, UITableV
|
||||||
}
|
}
|
||||||
|
|
||||||
func actOnReloadTableViewRelatedNotification() {
|
func actOnReloadTableViewRelatedNotification() {
|
||||||
initPasswordsTableEntries(parent: nil)
|
|
||||||
DispatchQueue.main.async { [weak weakSelf = self] in
|
DispatchQueue.main.async { [weak weakSelf = self] in
|
||||||
guard let strongSelf = weakSelf else { return }
|
guard let strongSelf = weakSelf else { return }
|
||||||
|
strongSelf.initPasswordsTableEntries(parent: nil)
|
||||||
strongSelf.reloadTableView(data: strongSelf.passwordsTableEntries)
|
strongSelf.reloadTableView(data: strongSelf.passwordsTableEntries)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -43,32 +43,7 @@ class SSHKeySettingTableViewController: UITableViewController {
|
||||||
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"
|
Defaults[.gitSSHKeySource] = "url"
|
||||||
let alert = UIAlertController(
|
self.navigationController!.popViewController(animated: true)
|
||||||
title: "PGP Passphrase",
|
|
||||||
message: "Please fill in the passphrase for your Git Repository SSH key.",
|
|
||||||
preferredStyle: UIAlertControllerStyle.alert
|
|
||||||
)
|
|
||||||
|
|
||||||
alert.addAction(
|
|
||||||
UIAlertAction(
|
|
||||||
title: "OK",
|
|
||||||
style: UIAlertActionStyle.default,
|
|
||||||
handler: {_ in
|
|
||||||
Utils.addPasswordToKeychain(
|
|
||||||
name: "gitSSHPrivateKeyPassphrase",
|
|
||||||
password: alert.textFields!.first!.text!
|
|
||||||
)
|
|
||||||
self.navigationController!.popViewController(animated: true)
|
|
||||||
}
|
|
||||||
)
|
|
||||||
)
|
|
||||||
|
|
||||||
alert.addTextField(
|
|
||||||
configurationHandler: {(textField: UITextField!) in
|
|
||||||
textField.text = self.passwordStore.gitSSHPrivateKeyPassphrase
|
|
||||||
textField.isSecureTextEntry = true
|
|
||||||
})
|
|
||||||
self.present(alert, animated: true, completion: nil)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -34,7 +34,9 @@ class SettingsTableViewController: UITableViewController {
|
||||||
if let controller = segue.source as? PGPKeySettingTableViewController {
|
if let controller = segue.source as? PGPKeySettingTableViewController {
|
||||||
Defaults[.pgpPrivateKeyURL] = URL(string: controller.pgpPrivateKeyURLTextField.text!)
|
Defaults[.pgpPrivateKeyURL] = URL(string: controller.pgpPrivateKeyURLTextField.text!)
|
||||||
Defaults[.pgpPublicKeyURL] = URL(string: controller.pgpPublicKeyURLTextField.text!)
|
Defaults[.pgpPublicKeyURL] = URL(string: controller.pgpPublicKeyURLTextField.text!)
|
||||||
self.passwordStore.pgpKeyPassphrase = controller.pgpPassphrase
|
if Defaults[.isRememberPassphraseOn] {
|
||||||
|
self.passwordStore.pgpKeyPassphrase = controller.pgpPassphrase
|
||||||
|
}
|
||||||
Defaults[.pgpKeySource] = "url"
|
Defaults[.pgpKeySource] = "url"
|
||||||
|
|
||||||
SVProgressHUD.setDefaultMaskType(.black)
|
SVProgressHUD.setDefaultMaskType(.black)
|
||||||
|
|
@ -48,7 +50,7 @@ class SettingsTableViewController: UITableViewController {
|
||||||
self.pgpKeyTableViewCell.detailTextLabel?.text = self.passwordStore.pgpKeyID
|
self.pgpKeyTableViewCell.detailTextLabel?.text = self.passwordStore.pgpKeyID
|
||||||
SVProgressHUD.showSuccess(withStatus: "Success")
|
SVProgressHUD.showSuccess(withStatus: "Success")
|
||||||
SVProgressHUD.dismiss(withDelay: 1)
|
SVProgressHUD.dismiss(withDelay: 1)
|
||||||
Utils.alert(title: "Rememver to Remove the Key", message: "Remember to remove the key from the server.", controller: self, completion: nil)
|
Utils.alert(title: "Remember to Remove the Key", message: "Remember to remove the key from the server.", controller: self, completion: nil)
|
||||||
}
|
}
|
||||||
} catch {
|
} catch {
|
||||||
DispatchQueue.main.async {
|
DispatchQueue.main.async {
|
||||||
|
|
@ -60,9 +62,8 @@ class SettingsTableViewController: UITableViewController {
|
||||||
|
|
||||||
} else if let controller = segue.source as? PGPKeyArmorSettingTableViewController {
|
} else if let controller = segue.source as? PGPKeyArmorSettingTableViewController {
|
||||||
Defaults[.pgpKeySource] = "armor"
|
Defaults[.pgpKeySource] = "armor"
|
||||||
self.passwordStore.pgpKeyPassphrase = controller.pgpPassphrase
|
|
||||||
if Defaults[.isRememberPassphraseOn] {
|
if Defaults[.isRememberPassphraseOn] {
|
||||||
Utils.addPasswordToKeychain(name: "pgpKeyPassphrase", password: controller.pgpPassphrase!)
|
self.passwordStore.pgpKeyPassphrase = controller.pgpPassphrase
|
||||||
}
|
}
|
||||||
|
|
||||||
Defaults[.pgpPublicKeyArmor] = controller.armorPublicKeyTextView.text!
|
Defaults[.pgpPublicKeyArmor] = controller.armorPublicKeyTextView.text!
|
||||||
|
|
@ -97,64 +98,53 @@ class SettingsTableViewController: UITableViewController {
|
||||||
if let controller = segue.source as? GitServerSettingTableViewController {
|
if let controller = segue.source as? GitServerSettingTableViewController {
|
||||||
let gitRepostiroyURL = controller.gitURLTextField.text!
|
let gitRepostiroyURL = controller.gitURLTextField.text!
|
||||||
let username = controller.usernameTextField.text!
|
let username = controller.usernameTextField.text!
|
||||||
let password = controller.password
|
|
||||||
let auth = controller.authenticationMethod
|
let auth = controller.authenticationMethod
|
||||||
|
|
||||||
if Defaults[.gitURL] == nil ||
|
SVProgressHUD.setDefaultMaskType(.black)
|
||||||
Defaults[.gitURL]!.absoluteString != gitRepostiroyURL ||
|
SVProgressHUD.setDefaultStyle(.light)
|
||||||
auth != Defaults[.gitAuthenticationMethod] ||
|
SVProgressHUD.show(withStatus: "Prepare Repository")
|
||||||
username != Defaults[.gitUsername] ||
|
var gitCredential: GitCredential
|
||||||
password != self.passwordStore.gitPassword ||
|
if auth == "Password" {
|
||||||
self.passwordStore.repositoryExisted() == false {
|
gitCredential = GitCredential(credential: GitCredential.Credential.http(userName: username, controller: self))
|
||||||
|
} else {
|
||||||
SVProgressHUD.setDefaultMaskType(.black)
|
gitCredential = GitCredential(
|
||||||
SVProgressHUD.setDefaultStyle(.light)
|
credential: GitCredential.Credential.ssh(
|
||||||
SVProgressHUD.show(withStatus: "Prepare Repository")
|
userName: username,
|
||||||
var gitCredential: GitCredential
|
publicKeyFile: Globals.gitSSHPublicKeyURL,
|
||||||
if auth == "Password" {
|
privateKeyFile: Globals.gitSSHPrivateKeyURL,
|
||||||
gitCredential = GitCredential(credential: GitCredential.Credential.http(userName: username, password: password!))
|
controller: self
|
||||||
} else {
|
|
||||||
gitCredential = GitCredential(
|
|
||||||
credential: GitCredential.Credential.ssh(
|
|
||||||
userName: username,
|
|
||||||
password: Utils.getPasswordFromKeychain(name: "gitSSHPrivateKeyPassphrase") ?? "",
|
|
||||||
publicKeyFile: Globals.gitSSHPublicKeyURL,
|
|
||||||
privateKeyFile: Globals.gitSSHPrivateKeyURL,
|
|
||||||
passwordNotSetCallback: self.requestSshKeyPassword
|
|
||||||
)
|
|
||||||
)
|
)
|
||||||
}
|
)
|
||||||
let dispatchQueue = DispatchQueue.global(qos: .userInitiated)
|
}
|
||||||
dispatchQueue.async {
|
let dispatchQueue = DispatchQueue.global(qos: .userInitiated)
|
||||||
do {
|
dispatchQueue.async {
|
||||||
try self.passwordStore.cloneRepository(remoteRepoURL: URL(string: gitRepostiroyURL)!,
|
do {
|
||||||
credential: gitCredential,
|
try self.passwordStore.cloneRepository(remoteRepoURL: URL(string: gitRepostiroyURL)!,
|
||||||
transferProgressBlock:{ (git_transfer_progress, stop) in
|
credential: gitCredential,
|
||||||
DispatchQueue.main.async {
|
transferProgressBlock:{ (git_transfer_progress, stop) in
|
||||||
SVProgressHUD.showProgress(Float(git_transfer_progress.pointee.received_objects)/Float(git_transfer_progress.pointee.total_objects), status: "Clone Remote Repository")
|
DispatchQueue.main.async {
|
||||||
}
|
SVProgressHUD.showProgress(Float(git_transfer_progress.pointee.received_objects)/Float(git_transfer_progress.pointee.total_objects), status: "Clone Remote Repository")
|
||||||
},
|
}
|
||||||
checkoutProgressBlock: { (path, completedSteps, totalSteps) in
|
},
|
||||||
DispatchQueue.main.async {
|
checkoutProgressBlock: { (path, completedSteps, totalSteps) in
|
||||||
SVProgressHUD.showProgress(Float(completedSteps)/Float(totalSteps), status: "Checkout Master Branch")
|
DispatchQueue.main.async {
|
||||||
}
|
SVProgressHUD.showProgress(Float(completedSteps)/Float(totalSteps), status: "Checkout Master Branch")
|
||||||
})
|
}
|
||||||
DispatchQueue.main.async {
|
})
|
||||||
Defaults[.gitURL] = URL(string: gitRepostiroyURL)
|
DispatchQueue.main.async {
|
||||||
Defaults[.gitUsername] = username
|
Defaults[.gitURL] = URL(string: gitRepostiroyURL)
|
||||||
Defaults[.gitAuthenticationMethod] = auth
|
Defaults[.gitUsername] = username
|
||||||
Defaults[.gitPasswordAttempts] = 0
|
Defaults[.gitAuthenticationMethod] = auth
|
||||||
self.passwordRepositoryTableViewCell.detailTextLabel?.text = Defaults[.gitURL]?.host
|
self.passwordRepositoryTableViewCell.detailTextLabel?.text = Defaults[.gitURL]?.host
|
||||||
SVProgressHUD.showSuccess(withStatus: "Done")
|
SVProgressHUD.showSuccess(withStatus: "Done")
|
||||||
SVProgressHUD.dismiss(withDelay: 1)
|
SVProgressHUD.dismiss(withDelay: 1)
|
||||||
}
|
}
|
||||||
} catch {
|
} catch {
|
||||||
DispatchQueue.main.async {
|
DispatchQueue.main.async {
|
||||||
Utils.alert(title: "Error", message: error.localizedDescription, controller: self, completion: nil)
|
Utils.alert(title: "Error", message: error.localizedDescription, controller: self, completion: nil)
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -203,32 +193,7 @@ class SettingsTableViewController: UITableViewController {
|
||||||
touchIDSwitch.isOn = false
|
touchIDSwitch.isOn = false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func requestSshKeyPassword() -> String {
|
|
||||||
let sem = DispatchSemaphore(value: 0)
|
|
||||||
var newPassword = ""
|
|
||||||
|
|
||||||
DispatchQueue.main.async {
|
|
||||||
SVProgressHUD.dismiss()
|
|
||||||
let alert = UIAlertController(title: "Password", message: "Please fill in the password of your SSH key.", preferredStyle: UIAlertControllerStyle.alert)
|
|
||||||
|
|
||||||
alert.addAction(UIAlertAction(title: "OK", style: UIAlertActionStyle.default, handler: {_ in
|
|
||||||
newPassword = alert.textFields!.first!.text!
|
|
||||||
sem.signal()
|
|
||||||
}))
|
|
||||||
|
|
||||||
alert.addTextField(configurationHandler: {(textField: UITextField!) in
|
|
||||||
textField.text = self.passwordStore.gitPassword
|
|
||||||
textField.isSecureTextEntry = true
|
|
||||||
})
|
|
||||||
|
|
||||||
self.present(alert, animated: true, completion: nil)
|
|
||||||
}
|
|
||||||
|
|
||||||
let _ = sem.wait(timeout: DispatchTime.distantFuture)
|
|
||||||
return newPassword
|
|
||||||
}
|
|
||||||
|
|
||||||
func actOnPasswordStoreErasedNotification() {
|
func actOnPasswordStoreErasedNotification() {
|
||||||
setPGPKeyTableViewCellDetailText()
|
setPGPKeyTableViewCellDetailText()
|
||||||
setPasswordRepositoryTableViewCellDetailText()
|
setPasswordRepositoryTableViewCellDetailText()
|
||||||
|
|
|
||||||
|
|
@ -20,14 +20,13 @@ extension DefaultsKeys {
|
||||||
static let gitURL = DefaultsKey<URL?>("gitURL")
|
static let gitURL = DefaultsKey<URL?>("gitURL")
|
||||||
static let gitAuthenticationMethod = DefaultsKey<String?>("gitAuthenticationMethod")
|
static let gitAuthenticationMethod = DefaultsKey<String?>("gitAuthenticationMethod")
|
||||||
static let gitUsername = DefaultsKey<String?>("gitUsername")
|
static let gitUsername = DefaultsKey<String?>("gitUsername")
|
||||||
static let gitPasswordAttempts = DefaultsKey<Int>("gitPasswordAttempts")
|
|
||||||
static let gitSSHPublicKeyURL = DefaultsKey<URL?>("gitSSHPublicKeyURL")
|
static let gitSSHPublicKeyURL = DefaultsKey<URL?>("gitSSHPublicKeyURL")
|
||||||
static let gitSSHPrivateKeyURL = DefaultsKey<URL?>("gitSSHPrivateKeyURL")
|
static let gitSSHPrivateKeyURL = DefaultsKey<URL?>("gitSSHPrivateKeyURL")
|
||||||
static let gitSSHKeySource = DefaultsKey<String?>("gitSSHKeySource")
|
static let gitSSHKeySource = DefaultsKey<String?>("gitSSHKeySource")
|
||||||
|
|
||||||
static let gitSSHPublicKeyArmor = DefaultsKey<String?>("gitSSHPublicKeyArmor")
|
static let gitSSHPublicKeyArmor = DefaultsKey<String?>("gitSSHPublicKeyArmor")
|
||||||
static let gitSSHPrivateKeyArmor = DefaultsKey<String?>("gitSSHPrivateKeyArmor")
|
static let gitSSHPrivateKeyArmor = DefaultsKey<String?>("gitSSHPrivateKeyArmor")
|
||||||
|
static let gitSignatureName = DefaultsKey<String?>("gitSignatureName")
|
||||||
|
static let gitSignatureEmail = DefaultsKey<String?>("gitSignatureEmail")
|
||||||
|
|
||||||
static let lastSyncedTime = DefaultsKey<Date?>("lastSyncedTime")
|
static let lastSyncedTime = DefaultsKey<Date?>("lastSyncedTime")
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -26,6 +26,9 @@ class Globals {
|
||||||
static let passwordDefaultLength = ["Random": (min: 6, max: 24, def: 16),
|
static let passwordDefaultLength = ["Random": (min: 6, max: 24, def: 16),
|
||||||
"Apple": (min: 15, max: 15, def: 15)]
|
"Apple": (min: 15, max: 15, def: 15)]
|
||||||
|
|
||||||
|
static let gitSignatureDefaultName = "Pass for iOS"
|
||||||
|
static let gitSignatureDefaultEmail = "user@passforios"
|
||||||
|
|
||||||
static let passwordDots = "••••••••••••"
|
static let passwordDots = "••••••••••••"
|
||||||
static let oneTimePasswordDots = "••••••"
|
static let oneTimePasswordDots = "••••••"
|
||||||
static let passwordFonts = "Menlo"
|
static let passwordFonts = "Menlo"
|
||||||
|
|
|
||||||
|
|
@ -238,3 +238,12 @@ extension FileManager {
|
||||||
return accumulatedSize
|
return accumulatedSize
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
extension String {
|
||||||
|
func stringByAddingPercentEncodingForRFC3986() -> String? {
|
||||||
|
let unreserved = "-._~/?"
|
||||||
|
var allowed = CharacterSet.alphanumerics
|
||||||
|
allowed.insert(charactersIn: unreserved)
|
||||||
|
return addingPercentEncoding(withAllowedCharacters: allowed)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -17,7 +17,7 @@
|
||||||
<key>CFBundlePackageType</key>
|
<key>CFBundlePackageType</key>
|
||||||
<string>APPL</string>
|
<string>APPL</string>
|
||||||
<key>CFBundleShortVersionString</key>
|
<key>CFBundleShortVersionString</key>
|
||||||
<string>0.2.4</string>
|
<string>0.2.5</string>
|
||||||
<key>CFBundleVersion</key>
|
<key>CFBundleVersion</key>
|
||||||
<string>1</string>
|
<string>1</string>
|
||||||
<key>ITSAppUsesNonExemptEncryption</key>
|
<key>ITSAppUsesNonExemptEncryption</key>
|
||||||
|
|
|
||||||
|
|
@ -16,14 +16,29 @@ struct AdditionField {
|
||||||
var content: String
|
var content: String
|
||||||
}
|
}
|
||||||
|
|
||||||
|
enum PasswordChange: Int {
|
||||||
|
case path = 0x01
|
||||||
|
case content = 0x02
|
||||||
|
case none = 0x00
|
||||||
|
}
|
||||||
|
|
||||||
class Password {
|
class Password {
|
||||||
static let otpKeywords = ["otp_secret", "otp_type", "otp_algorithm", "otp_period", "otp_digits", "otp_counter", "otpauth"]
|
static let otpKeywords = ["otp_secret", "otp_type", "otp_algorithm", "otp_period", "otp_digits", "otp_counter", "otpauth"]
|
||||||
|
|
||||||
var name = ""
|
var name = ""
|
||||||
|
var url: URL?
|
||||||
|
var namePath: String {
|
||||||
|
get {
|
||||||
|
if url == nil {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
return url!.deletingPathExtension().path
|
||||||
|
}
|
||||||
|
}
|
||||||
var password = ""
|
var password = ""
|
||||||
var additions = [String: String]()
|
var additions = [String: String]()
|
||||||
var additionKeys = [String]()
|
var additionKeys = [String]()
|
||||||
var changed = false
|
var changed: Int = 0
|
||||||
var plainText = ""
|
var plainText = ""
|
||||||
|
|
||||||
private var firstLineIsOTPField = false
|
private var firstLineIsOTPField = false
|
||||||
|
|
@ -47,19 +62,25 @@ class Password {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
init(name: String, plainText: String) {
|
init(name: String, url: URL?, plainText: String) {
|
||||||
self.initEverything(name: name, plainText: plainText)
|
self.initEverything(name: name, url: url, plainText: plainText)
|
||||||
}
|
}
|
||||||
|
|
||||||
func updatePassword(name: String, plainText: String) {
|
func updatePassword(name: String, url: URL?, plainText: String) {
|
||||||
if self.plainText != plainText {
|
if self.plainText != plainText || self.url != url {
|
||||||
self.initEverything(name: name, plainText: plainText)
|
if self.plainText != plainText {
|
||||||
changed = true
|
changed = changed|PasswordChange.content.rawValue
|
||||||
|
}
|
||||||
|
if self.url != url {
|
||||||
|
changed = changed|PasswordChange.path.rawValue
|
||||||
|
}
|
||||||
|
self.initEverything(name: name, url: url, plainText: plainText)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private func initEverything(name: String, plainText: String) {
|
private func initEverything(name: String, url: URL?, plainText: String) {
|
||||||
self.name = name
|
self.name = name
|
||||||
|
self.url = url
|
||||||
self.plainText = plainText
|
self.plainText = plainText
|
||||||
self.additions.removeAll()
|
self.additions.removeAll()
|
||||||
self.additionKeys.removeAll()
|
self.additionKeys.removeAll()
|
||||||
|
|
@ -322,7 +343,7 @@ class Password {
|
||||||
if newOtpauth != nil {
|
if newOtpauth != nil {
|
||||||
lines.append(newOtpauth!)
|
lines.append(newOtpauth!)
|
||||||
}
|
}
|
||||||
self.updatePassword(name: self.name, plainText: lines.joined(separator: "\n"))
|
self.updatePassword(name: self.name, url: self.url, plainText: lines.joined(separator: "\n"))
|
||||||
|
|
||||||
// get and return the password
|
// get and return the password
|
||||||
return self.otpToken?.currentPassword
|
return self.otpToken?.currentPassword
|
||||||
|
|
|
||||||
|
|
@ -21,24 +21,6 @@ extension PasswordEntity {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func decrypt(passphrase: String) throws -> Password? {
|
|
||||||
var password: Password?
|
|
||||||
let encryptedDataPath = URL(fileURLWithPath: "\(Globals.repositoryPath)/\(path!)")
|
|
||||||
let encryptedData = try Data(contentsOf: encryptedDataPath)
|
|
||||||
let decryptedData = try PasswordStore.shared.pgp.decryptData(encryptedData, passphrase: passphrase)
|
|
||||||
let plainText = String(data: decryptedData, encoding: .utf8) ?? ""
|
|
||||||
password = Password(name: name!, plainText: plainText)
|
|
||||||
return password
|
|
||||||
}
|
|
||||||
|
|
||||||
func encrypt(password: Password) throws -> Data {
|
|
||||||
name = password.name
|
|
||||||
let plainData = password.getPlainData()
|
|
||||||
let pgp = PasswordStore.shared.pgp
|
|
||||||
let encryptedData = try pgp.encryptData(plainData, usingPublicKey: pgp.getKeysOf(.public)[0], armored: Defaults[.encryptInArmored])
|
|
||||||
return encryptedData
|
|
||||||
}
|
|
||||||
|
|
||||||
func getCategoryText() -> String {
|
func getCategoryText() -> String {
|
||||||
var parentEntity = parent
|
var parentEntity = parent
|
||||||
var passwordCategoryArray: [String] = []
|
var passwordCategoryArray: [String] = []
|
||||||
|
|
@ -49,4 +31,11 @@ extension PasswordEntity {
|
||||||
passwordCategoryArray.reverse()
|
passwordCategoryArray.reverse()
|
||||||
return passwordCategoryArray.joined(separator: " > ")
|
return passwordCategoryArray.joined(separator: " > ")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func getURL() -> URL? {
|
||||||
|
if let p = path {
|
||||||
|
return URL(string: p.stringByAddingPercentEncodingForRFC3986()!)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -14,81 +14,96 @@ import ObjectiveGit
|
||||||
import SVProgressHUD
|
import SVProgressHUD
|
||||||
|
|
||||||
struct GitCredential {
|
struct GitCredential {
|
||||||
|
var credential: Credential
|
||||||
|
|
||||||
enum Credential {
|
enum Credential {
|
||||||
case http(userName: String, password: String)
|
case http(userName: String, controller: UIViewController)
|
||||||
case ssh(userName: String, password: String, publicKeyFile: URL, privateKeyFile: URL, passwordNotSetCallback: (() -> String)? )
|
case ssh(userName: String, publicKeyFile: URL, privateKeyFile: URL, controller: UIViewController)
|
||||||
|
}
|
||||||
|
|
||||||
|
init(credential: Credential) {
|
||||||
|
self.credential = credential
|
||||||
}
|
}
|
||||||
|
|
||||||
var credential: Credential
|
|
||||||
|
|
||||||
func credentialProvider() throws -> GTCredentialProvider {
|
func credentialProvider() throws -> GTCredentialProvider {
|
||||||
|
var attempts = 0
|
||||||
|
var lastPassword: String? = nil
|
||||||
return GTCredentialProvider { (_, _, _) -> (GTCredential?) in
|
return GTCredentialProvider { (_, _, _) -> (GTCredential?) in
|
||||||
var credential: GTCredential? = nil
|
var credential: GTCredential? = nil
|
||||||
|
|
||||||
switch self.credential {
|
switch self.credential {
|
||||||
case let .http(userName, password):
|
case let .http(userName, controller):
|
||||||
print(Defaults[.gitPasswordAttempts])
|
var newPassword = Utils.getPasswordFromKeychain(name: "gitPassword")
|
||||||
var newPassword: String = password
|
if newPassword == nil || attempts != 0 {
|
||||||
if Defaults[.gitPasswordAttempts] != 0 {
|
if let requestedPassword = self.requestGitPassword(controller, lastPassword) {
|
||||||
let sem = DispatchSemaphore(value: 0)
|
newPassword = requestedPassword
|
||||||
DispatchQueue.main.async {
|
Utils.addPasswordToKeychain(name: "gitPassword", password: newPassword)
|
||||||
SVProgressHUD.dismiss()
|
} else {
|
||||||
if var topController = UIApplication.shared.keyWindow?.rootViewController {
|
return nil
|
||||||
while let presentedViewController = topController.presentedViewController {
|
|
||||||
topController = presentedViewController
|
|
||||||
}
|
|
||||||
let alert = UIAlertController(title: "Password", message: "Please fill in the password of your Git account.", preferredStyle: UIAlertControllerStyle.alert)
|
|
||||||
alert.addAction(UIAlertAction(title: "OK", style: UIAlertActionStyle.default, handler: {_ in
|
|
||||||
newPassword = alert.textFields!.first!.text!
|
|
||||||
PasswordStore.shared.gitPassword = newPassword
|
|
||||||
sem.signal()
|
|
||||||
}))
|
|
||||||
alert.addAction(UIAlertAction(title: "Cancel", style: .cancel) { _ in
|
|
||||||
Defaults[.gitPasswordAttempts] = -1
|
|
||||||
sem.signal()
|
|
||||||
})
|
|
||||||
alert.addTextField(configurationHandler: {(textField: UITextField!) in
|
|
||||||
textField.text = PasswordStore.shared.gitPassword
|
|
||||||
textField.isSecureTextEntry = true
|
|
||||||
})
|
|
||||||
topController.present(alert, animated: true, completion: nil)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
let _ = sem.wait(timeout: DispatchTime.distantFuture)
|
|
||||||
}
|
}
|
||||||
if Defaults[.gitPasswordAttempts] == -1 {
|
attempts += 1
|
||||||
Defaults[.gitPasswordAttempts] = 0
|
lastPassword = newPassword
|
||||||
return nil
|
credential = try? GTCredential(userName: userName, password: newPassword!)
|
||||||
|
case let .ssh(userName, publicKeyFile, privateKeyFile, controller):
|
||||||
|
var newPassword = Utils.getPasswordFromKeychain(name: "gitSSHKeyPassphrase")
|
||||||
|
if newPassword == nil || attempts != 0 {
|
||||||
|
if let requestedPassword = self.requestGitPassword(controller, lastPassword) {
|
||||||
|
newPassword = requestedPassword
|
||||||
|
Utils.addPasswordToKeychain(name: "gitSSHKeyPassphrase", password: newPassword)
|
||||||
|
} else {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
}
|
}
|
||||||
Defaults[.gitPasswordAttempts] += 1
|
attempts += 1
|
||||||
PasswordStore.shared.gitPassword = newPassword
|
lastPassword = newPassword
|
||||||
credential = try? GTCredential(userName: userName, password: newPassword)
|
credential = try? GTCredential(userName: userName, publicKeyURL: publicKeyFile, privateKeyURL: privateKeyFile, passphrase: newPassword!)
|
||||||
case let .ssh(userName, password, publicKeyFile, privateKeyFile, passwordNotSetCallback):
|
|
||||||
|
|
||||||
var newPassword:String? = password
|
|
||||||
|
|
||||||
// Check if the private key is encrypted
|
|
||||||
let encrypted = try? String(contentsOf: privateKeyFile).contains("ENCRYPTED")
|
|
||||||
|
|
||||||
// Request password if not already set
|
|
||||||
if encrypted! && password == "" {
|
|
||||||
newPassword = passwordNotSetCallback!()
|
|
||||||
}
|
|
||||||
|
|
||||||
// Save password for the future
|
|
||||||
Utils.addPasswordToKeychain(name: "gitSSHPrivateKeyPassphrase", password: newPassword!)
|
|
||||||
|
|
||||||
// nil is expected in case of empty password
|
|
||||||
if newPassword == "" {
|
|
||||||
newPassword = nil
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
credential = try? GTCredential(userName: userName, publicKeyURL: publicKeyFile, privateKeyURL: privateKeyFile, passphrase: newPassword)
|
|
||||||
}
|
}
|
||||||
return credential
|
return credential
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func delete() {
|
||||||
|
switch credential {
|
||||||
|
case .http:
|
||||||
|
Utils.removeKeychain(name: "gitPassword")
|
||||||
|
case .ssh:
|
||||||
|
Utils.removeKeychain(name: "gitSSHKeyPassphrase")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private func requestGitPassword(_ controller: UIViewController, _ lastPassword: String?) -> String? {
|
||||||
|
let sem = DispatchSemaphore(value: 0)
|
||||||
|
var password: String?
|
||||||
|
var message = ""
|
||||||
|
switch credential {
|
||||||
|
case .http:
|
||||||
|
message = "Please fill in the password of your Git account."
|
||||||
|
case .ssh:
|
||||||
|
message = "Please fill in the password of your SSH key."
|
||||||
|
}
|
||||||
|
|
||||||
|
DispatchQueue.main.async {
|
||||||
|
SVProgressHUD.dismiss()
|
||||||
|
let alert = UIAlertController(title: "Password", message: message, preferredStyle: UIAlertControllerStyle.alert)
|
||||||
|
alert.addTextField(configurationHandler: {(textField: UITextField!) in
|
||||||
|
textField.text = lastPassword ?? ""
|
||||||
|
textField.isSecureTextEntry = true
|
||||||
|
})
|
||||||
|
alert.addAction(UIAlertAction(title: "OK", style: UIAlertActionStyle.default, handler: {_ in
|
||||||
|
password = alert.textFields!.first!.text
|
||||||
|
sem.signal()
|
||||||
|
}))
|
||||||
|
alert.addAction(UIAlertAction(title: "Cancel", style: .cancel) { _ in
|
||||||
|
password = nil
|
||||||
|
sem.signal()
|
||||||
|
})
|
||||||
|
controller.present(alert, animated: true, completion: nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
let _ = sem.wait(timeout: .distantFuture)
|
||||||
|
return password
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class PasswordStore {
|
class PasswordStore {
|
||||||
|
|
@ -97,7 +112,6 @@ class PasswordStore {
|
||||||
let tempStoreURL = URL(fileURLWithPath: "\(Globals.repositoryPath)-temp")
|
let tempStoreURL = URL(fileURLWithPath: "\(Globals.repositoryPath)-temp")
|
||||||
|
|
||||||
var storeRepository: GTRepository?
|
var storeRepository: GTRepository?
|
||||||
var gitCredential: GitCredential?
|
|
||||||
var pgpKeyID: String?
|
var pgpKeyID: String?
|
||||||
var publicKey: PGPKey? {
|
var publicKey: PGPKey? {
|
||||||
didSet {
|
didSet {
|
||||||
|
|
@ -112,7 +126,9 @@ class PasswordStore {
|
||||||
|
|
||||||
var gitSignatureForNow: GTSignature {
|
var gitSignatureForNow: GTSignature {
|
||||||
get {
|
get {
|
||||||
return GTSignature(name: Defaults[.gitUsername]!, email: Defaults[.gitUsername]!+"@passforios", time: Date())!
|
let gitSignatureName = Defaults[.gitSignatureName] ?? Globals.gitSignatureDefaultName
|
||||||
|
let gitSignatureEmail = Defaults[.gitSignatureEmail] ?? Globals.gitSignatureDefaultEmail
|
||||||
|
return GTSignature(name: gitSignatureName, email: gitSignatureEmail, time: Date())!
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -126,6 +142,7 @@ class PasswordStore {
|
||||||
return Utils.getPasswordFromKeychain(name: "pgpKeyPassphrase")
|
return Utils.getPasswordFromKeychain(name: "pgpKeyPassphrase")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
var gitPassword: String? {
|
var gitPassword: String? {
|
||||||
set {
|
set {
|
||||||
Utils.addPasswordToKeychain(name: "gitPassword", password: newValue)
|
Utils.addPasswordToKeychain(name: "gitPassword", password: newValue)
|
||||||
|
|
@ -140,7 +157,7 @@ class PasswordStore {
|
||||||
Utils.addPasswordToKeychain(name: "gitSSHPrivateKeyPassphrase", password: newValue)
|
Utils.addPasswordToKeychain(name: "gitSSHPrivateKeyPassphrase", password: newValue)
|
||||||
}
|
}
|
||||||
get {
|
get {
|
||||||
return Utils.getPasswordFromKeychain(name: "gitSSHPrivateKeyPassphrase") ?? ""
|
return Utils.getPasswordFromKeychain(name: "gitSSHPrivateKeyPassphrase")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -173,31 +190,12 @@ class PasswordStore {
|
||||||
print(error)
|
print(error)
|
||||||
}
|
}
|
||||||
initPGPKeys()
|
initPGPKeys()
|
||||||
initGitCredential()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
enum SSHKeyType {
|
enum SSHKeyType {
|
||||||
case `public`, secret
|
case `public`, secret
|
||||||
}
|
}
|
||||||
|
|
||||||
public func initGitCredential() {
|
|
||||||
if Defaults[.gitAuthenticationMethod] == "Password" {
|
|
||||||
gitCredential = GitCredential(credential: GitCredential.Credential.http(userName: Defaults[.gitUsername]!, password: Utils.getPasswordFromKeychain(name: "gitPassword") ?? ""))
|
|
||||||
} else if Defaults[.gitAuthenticationMethod] == "SSH Key"{
|
|
||||||
gitCredential = GitCredential(
|
|
||||||
credential: GitCredential.Credential.ssh(
|
|
||||||
userName: Defaults[.gitUsername]!,
|
|
||||||
password: gitSSHPrivateKeyPassphrase ?? "",
|
|
||||||
publicKeyFile: Globals.gitSSHPublicKeyURL,
|
|
||||||
privateKeyFile: Globals.gitSSHPrivateKeyURL,
|
|
||||||
passwordNotSetCallback: nil
|
|
||||||
)
|
|
||||||
)
|
|
||||||
} else {
|
|
||||||
gitCredential = nil
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public func initGitSSHKey(with armorKey: String, _ keyType: SSHKeyType) throws {
|
public func initGitSSHKey(with armorKey: String, _ keyType: SSHKeyType) throws {
|
||||||
var keyPath = ""
|
var keyPath = ""
|
||||||
switch keyType {
|
switch keyType {
|
||||||
|
|
@ -282,10 +280,9 @@ class PasswordStore {
|
||||||
}
|
}
|
||||||
|
|
||||||
func passwordExisted(password: Password) -> Bool {
|
func passwordExisted(password: Password) -> Bool {
|
||||||
print(password.name)
|
|
||||||
let passwordEntityFetchRequest = NSFetchRequest<NSFetchRequestResult>(entityName: "PasswordEntity")
|
let passwordEntityFetchRequest = NSFetchRequest<NSFetchRequestResult>(entityName: "PasswordEntity")
|
||||||
do {
|
do {
|
||||||
passwordEntityFetchRequest.predicate = NSPredicate(format: "name = %@", password.name)
|
passwordEntityFetchRequest.predicate = NSPredicate(format: "name = %@ and path = %@", password.name, password.url!.path)
|
||||||
let count = try context.count(for: passwordEntityFetchRequest)
|
let count = try context.count(for: passwordEntityFetchRequest)
|
||||||
if count > 0 {
|
if count > 0 {
|
||||||
return true
|
return true
|
||||||
|
|
@ -298,49 +295,79 @@ class PasswordStore {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func passwordEntityExisted(path: String) -> Bool {
|
||||||
|
let passwordEntityFetchRequest = NSFetchRequest<NSFetchRequestResult>(entityName: "PasswordEntity")
|
||||||
|
do {
|
||||||
|
passwordEntityFetchRequest.predicate = NSPredicate(format: "path = %@", path)
|
||||||
|
let count = try context.count(for: passwordEntityFetchRequest)
|
||||||
|
if count > 0 {
|
||||||
|
return true
|
||||||
|
} else {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
} catch {
|
||||||
|
fatalError("Failed to fetch password entities: \(error)")
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
func getPasswordEntity(by path: String, isDir: Bool) -> PasswordEntity? {
|
||||||
|
let passwordEntityFetchRequest = NSFetchRequest<NSFetchRequestResult>(entityName: "PasswordEntity")
|
||||||
|
do {
|
||||||
|
passwordEntityFetchRequest.predicate = NSPredicate(format: "path = %@ and isDir = %@", path, isDir.description)
|
||||||
|
return try context.fetch(passwordEntityFetchRequest).first as? PasswordEntity
|
||||||
|
} catch {
|
||||||
|
fatalError("Failed to fetch password entities: \(error)")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func cloneRepository(remoteRepoURL: URL,
|
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 {
|
||||||
Utils.removeFileIfExists(at: storeURL)
|
Utils.removeFileIfExists(at: storeURL)
|
||||||
Utils.removeFileIfExists(at: tempStoreURL)
|
Utils.removeFileIfExists(at: tempStoreURL)
|
||||||
|
|
||||||
let credentialProvider = try credential.credentialProvider()
|
|
||||||
let options: [String: Any] = [
|
|
||||||
GTRepositoryCloneOptionsCredentialProvider: credentialProvider,
|
|
||||||
]
|
|
||||||
storeRepository = try GTRepository.clone(from: remoteRepoURL, toWorkingDirectory: tempStoreURL, options: options, transferProgressBlock:transferProgressBlock)
|
|
||||||
let fm = FileManager.default
|
|
||||||
do {
|
do {
|
||||||
|
let credentialProvider = try credential.credentialProvider()
|
||||||
|
let options: [String: Any] = [
|
||||||
|
GTRepositoryCloneOptionsCredentialProvider: credentialProvider,
|
||||||
|
]
|
||||||
|
storeRepository = try GTRepository.clone(from: remoteRepoURL, toWorkingDirectory: tempStoreURL, options: options, transferProgressBlock:transferProgressBlock)
|
||||||
|
let fm = FileManager.default
|
||||||
if fm.fileExists(atPath: storeURL.path) {
|
if fm.fileExists(atPath: storeURL.path) {
|
||||||
try fm.removeItem(at: storeURL)
|
try fm.removeItem(at: storeURL)
|
||||||
}
|
}
|
||||||
try fm.copyItem(at: tempStoreURL, to: storeURL)
|
try fm.copyItem(at: tempStoreURL, to: storeURL)
|
||||||
try fm.removeItem(at: tempStoreURL)
|
try fm.removeItem(at: tempStoreURL)
|
||||||
|
storeRepository = try GTRepository(url: storeURL)
|
||||||
} catch {
|
} catch {
|
||||||
print(error)
|
credential.delete()
|
||||||
|
throw(error)
|
||||||
}
|
}
|
||||||
storeRepository = try GTRepository(url: storeURL)
|
|
||||||
gitCredential = credential
|
|
||||||
Defaults[.lastSyncedTime] = Date()
|
|
||||||
DispatchQueue.main.async {
|
DispatchQueue.main.async {
|
||||||
|
Defaults[.lastSyncedTime] = Date()
|
||||||
self.updatePasswordEntityCoreData()
|
self.updatePasswordEntityCoreData()
|
||||||
NotificationCenter.default.post(name: .passwordStoreUpdated, object: nil)
|
NotificationCenter.default.post(name: .passwordStoreUpdated, object: nil)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func pullRepository(transferProgressBlock: @escaping (UnsafePointer<git_transfer_progress>, UnsafeMutablePointer<ObjCBool>) -> Void) throws {
|
func pullRepository(credential: GitCredential, transferProgressBlock: @escaping (UnsafePointer<git_transfer_progress>, UnsafeMutablePointer<ObjCBool>) -> Void) throws {
|
||||||
if gitCredential == nil {
|
if storeRepository == nil {
|
||||||
throw NSError(domain: "me.mssun.pass.error", code: 1, userInfo: [NSLocalizedDescriptionKey: "Git Repository is not set."])
|
throw NSError(domain: "me.mssun.pass.error", code: 1, userInfo: [NSLocalizedDescriptionKey: "Git Repository is not set."])
|
||||||
}
|
}
|
||||||
let credentialProvider = try gitCredential!.credentialProvider()
|
do {
|
||||||
let options: [String: Any] = [
|
let credentialProvider = try credential.credentialProvider()
|
||||||
GTRepositoryRemoteOptionsCredentialProvider: credentialProvider
|
let options: [String: Any] = [
|
||||||
]
|
GTRepositoryRemoteOptionsCredentialProvider: credentialProvider
|
||||||
let remote = try GTRemote(name: "origin", in: storeRepository!)
|
]
|
||||||
try storeRepository?.pull((storeRepository?.currentBranch())!, from: remote, withOptions: options, progress: transferProgressBlock)
|
let remote = try GTRemote(name: "origin", in: storeRepository!)
|
||||||
Defaults[.lastSyncedTime] = Date()
|
try storeRepository!.pull((storeRepository?.currentBranch())!, from: remote, withOptions: options, progress: transferProgressBlock)
|
||||||
|
} catch {
|
||||||
|
credential.delete()
|
||||||
|
throw(error)
|
||||||
|
}
|
||||||
DispatchQueue.main.async {
|
DispatchQueue.main.async {
|
||||||
|
Defaults[.lastSyncedTime] = Date()
|
||||||
self.setAllSynced()
|
self.setAllSynced()
|
||||||
self.updatePasswordEntityCoreData()
|
self.updatePasswordEntityCoreData()
|
||||||
NotificationCenter.default.post(name: .passwordStoreUpdated, object: nil)
|
NotificationCenter.default.post(name: .passwordStoreUpdated, object: nil)
|
||||||
|
|
@ -509,45 +536,69 @@ class PasswordStore {
|
||||||
func updateRemoteRepo() {
|
func updateRemoteRepo() {
|
||||||
}
|
}
|
||||||
|
|
||||||
func createAddCommitInRepository(message: String, fileData: Data, filename: String, progressBlock: (_ progress: Float) -> Void) -> GTCommit? {
|
private func gitAdd(path: String) throws {
|
||||||
|
if let repo = storeRepository {
|
||||||
|
try repo.index().addFile(path)
|
||||||
|
try repo.index().write()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private func gitRm(path: String) throws {
|
||||||
|
if let repo = storeRepository {
|
||||||
|
if FileManager.default.fileExists(atPath: storeURL.appendingPathComponent(path).path) {
|
||||||
|
try FileManager.default.removeItem(at: storeURL.appendingPathComponent(path))
|
||||||
|
}
|
||||||
|
try repo.index().removeFile(path)
|
||||||
|
try repo.index().write()
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
private func deleteDirectoryTree(at url: URL) throws {
|
||||||
|
var tempURL = storeURL.appendingPathComponent(url.deletingLastPathComponent().path)
|
||||||
|
let fm = FileManager.default
|
||||||
|
var count = try fm.contentsOfDirectory(atPath: tempURL.path).count
|
||||||
|
while count == 0 {
|
||||||
|
try fm.removeItem(at: tempURL)
|
||||||
|
tempURL.deleteLastPathComponent()
|
||||||
|
count = try fm.contentsOfDirectory(atPath: tempURL.path).count
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private func createDirectoryTree(at url: URL) throws {
|
||||||
|
let tempURL = storeURL.appendingPathComponent(url.deletingLastPathComponent().path)
|
||||||
|
try FileManager.default.createDirectory(at: tempURL, withIntermediateDirectories: true, attributes: nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
private func gitMv(from: String, to: String) throws {
|
||||||
|
let fm = FileManager.default
|
||||||
do {
|
do {
|
||||||
try storeRepository?.index().add(fileData, withPath: filename)
|
guard fm.fileExists(atPath: storeURL.appendingPathComponent(from).path) else {
|
||||||
try storeRepository?.index().write()
|
print("\(from) not exist")
|
||||||
let newTree = try storeRepository!.index().writeTree()
|
return
|
||||||
let headReference = try storeRepository!.headReference()
|
}
|
||||||
let commitEnum = try GTEnumerator(repository: storeRepository!)
|
try fm.moveItem(at: storeURL.appendingPathComponent(from), to: storeURL.appendingPathComponent(to))
|
||||||
try commitEnum.pushSHA(headReference.targetOID.sha!)
|
|
||||||
let parent = commitEnum.nextObject() as! GTCommit
|
|
||||||
progressBlock(0.5)
|
|
||||||
let signature = gitSignatureForNow
|
|
||||||
let commit = try storeRepository!.createCommit(with: newTree, message: message, author: signature, committer: signature, parents: [parent], updatingReferenceNamed: headReference.name)
|
|
||||||
progressBlock(0.7)
|
|
||||||
return commit
|
|
||||||
} catch {
|
} catch {
|
||||||
print(error)
|
print(error)
|
||||||
}
|
}
|
||||||
return nil
|
try gitAdd(path: to)
|
||||||
|
try gitRm(path: from)
|
||||||
}
|
}
|
||||||
|
|
||||||
func createRemoveCommitInRepository(message: String, path: String) -> GTCommit? {
|
private func gitCommit(message: String) throws -> GTCommit? {
|
||||||
do {
|
if let repo = storeRepository {
|
||||||
try storeRepository?.index().removeFile(path)
|
let newTree = try repo.index().writeTree()
|
||||||
try storeRepository?.index().write()
|
let headReference = try repo.headReference()
|
||||||
let newTree = try storeRepository!.index().writeTree()
|
let commitEnum = try GTEnumerator(repository: repo)
|
||||||
let headReference = try storeRepository!.headReference()
|
|
||||||
let commitEnum = try GTEnumerator(repository: storeRepository!)
|
|
||||||
try commitEnum.pushSHA(headReference.targetOID.sha!)
|
try commitEnum.pushSHA(headReference.targetOID.sha!)
|
||||||
let parent = commitEnum.nextObject() as! GTCommit
|
let parent = commitEnum.nextObject() as! GTCommit
|
||||||
let signature = gitSignatureForNow
|
let signature = gitSignatureForNow
|
||||||
let commit = try storeRepository!.createCommit(with: newTree, message: message, author: signature, committer: signature, parents: [parent], updatingReferenceNamed: headReference.name)
|
let commit = try repo.createCommit(with: newTree, message: message, author: signature, committer: signature, parents: [parent], updatingReferenceNamed: headReference.name)
|
||||||
return commit
|
return commit
|
||||||
} catch {
|
|
||||||
print(error)
|
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
private func getLocalBranch(withName branchName: String) -> GTBranch? {
|
private func getLocalBranch(withName branchName: String) -> GTBranch? {
|
||||||
do {
|
do {
|
||||||
let reference = GTBranch.localNamePrefix().appending(branchName)
|
let reference = GTBranch.localNamePrefix().appending(branchName)
|
||||||
|
|
@ -559,67 +610,133 @@ class PasswordStore {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func pushRepository(transferProgressBlock: @escaping (UInt32, UInt32, Int, UnsafeMutablePointer<ObjCBool>) -> Void) throws {
|
func pushRepository(credential: GitCredential, transferProgressBlock: @escaping (UInt32, UInt32, Int, UnsafeMutablePointer<ObjCBool>) -> Void) throws {
|
||||||
let credentialProvider = try gitCredential!.credentialProvider()
|
do {
|
||||||
let options: [String: Any] = [
|
let credentialProvider = try credential.credentialProvider()
|
||||||
GTRepositoryRemoteOptionsCredentialProvider: credentialProvider,
|
let options: [String: Any] = [
|
||||||
]
|
GTRepositoryRemoteOptionsCredentialProvider: credentialProvider,
|
||||||
let masterBranch = getLocalBranch(withName: "master")!
|
]
|
||||||
let remote = try GTRemote(name: "origin", in: storeRepository!)
|
let masterBranch = getLocalBranch(withName: "master")!
|
||||||
try storeRepository?.push(masterBranch, to: remote, withOptions: options, progress: transferProgressBlock)
|
let remote = try GTRemote(name: "origin", in: storeRepository!)
|
||||||
|
try storeRepository?.push(masterBranch, to: remote, withOptions: options, progress: transferProgressBlock)
|
||||||
|
} catch {
|
||||||
|
credential.delete()
|
||||||
|
throw(error)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func add(password: Password, progressBlock: (_ progress: Float) -> Void) throws {
|
private func addPasswordEntities(password: Password) throws -> PasswordEntity? {
|
||||||
progressBlock(0.0)
|
|
||||||
guard !passwordExisted(password: password) else {
|
guard !passwordExisted(password: password) else {
|
||||||
throw NSError(domain: "me.mssun.pass.error", code: 2, userInfo: [NSLocalizedDescriptionKey: "Cannot add password: password duplicated."])
|
throw NSError(domain: "me.mssun.pass.error", code: 2, userInfo: [NSLocalizedDescriptionKey: "Cannot add password: password duplicated."])
|
||||||
}
|
}
|
||||||
let passwordEntity = NSEntityDescription.insertNewObject(forEntityName: "PasswordEntity", into: context) as! PasswordEntity
|
|
||||||
do {
|
var passwordURL = password.url!
|
||||||
let encryptedData = try passwordEntity.encrypt(password: password)
|
var paths: [String] = []
|
||||||
progressBlock(0.3)
|
while passwordURL.path != "." {
|
||||||
let saveURL = storeURL.appendingPathComponent("\(password.name).gpg")
|
paths.append(passwordURL.path)
|
||||||
try encryptedData.write(to: saveURL)
|
passwordURL = passwordURL.deletingLastPathComponent()
|
||||||
passwordEntity.name = password.name
|
|
||||||
passwordEntity.path = "\(password.name).gpg"
|
|
||||||
passwordEntity.parent = nil
|
|
||||||
passwordEntity.synced = false
|
|
||||||
passwordEntity.isDir = false
|
|
||||||
try context.save()
|
|
||||||
print(saveURL.path)
|
|
||||||
let _ = createAddCommitInRepository(message: "Add password for \(passwordEntity.nameWithCategory) to store using Pass for iOS.", fileData: encryptedData, filename: saveURL.lastPathComponent, progressBlock: progressBlock)
|
|
||||||
progressBlock(1.0)
|
|
||||||
NotificationCenter.default.post(name: .passwordStoreUpdated, object: nil)
|
|
||||||
} catch {
|
|
||||||
print(error)
|
|
||||||
}
|
}
|
||||||
|
paths.reverse()
|
||||||
|
var parentPasswordEntity: PasswordEntity? = nil
|
||||||
|
for path in paths {
|
||||||
|
let isDir = !path.hasSuffix(".gpg")
|
||||||
|
if let passwordEntity = getPasswordEntity(by: path, isDir: isDir) {
|
||||||
|
print(passwordEntity.path!)
|
||||||
|
parentPasswordEntity = passwordEntity
|
||||||
|
} else {
|
||||||
|
if !isDir {
|
||||||
|
return insertPasswordEntity(name: URL(string: path.stringByAddingPercentEncodingForRFC3986()!)!.deletingPathExtension().lastPathComponent, path: path, parent: parentPasswordEntity, synced: false, isDir: false)
|
||||||
|
} else {
|
||||||
|
parentPasswordEntity = insertPasswordEntity(name: URL(string: path.stringByAddingPercentEncodingForRFC3986()!)!.lastPathComponent, path: path, parent: parentPasswordEntity, synced: false, isDir: true)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func update(passwordEntity: PasswordEntity, password: Password, progressBlock: (_ progress: Float) -> Void) {
|
private func insertPasswordEntity(name: String, path: String, parent: PasswordEntity?, synced: Bool = false, isDir: Bool = false) -> PasswordEntity? {
|
||||||
progressBlock(0.0)
|
var ret: PasswordEntity? = nil
|
||||||
do {
|
if let passwordEntity = NSEntityDescription.insertNewObject(forEntityName: "PasswordEntity", into: self.context) as? PasswordEntity {
|
||||||
let encryptedData = try passwordEntity.encrypt(password: password)
|
passwordEntity.name = name
|
||||||
let saveURL = storeURL.appendingPathComponent(passwordEntity.path!)
|
passwordEntity.path = path
|
||||||
try encryptedData.write(to: saveURL)
|
passwordEntity.parent = parent
|
||||||
progressBlock(0.3)
|
passwordEntity.synced = synced
|
||||||
let _ = createAddCommitInRepository(message: "Edit password for \(passwordEntity.nameWithCategory) using Pass for iOS.", fileData: encryptedData, filename: saveURL.lastPathComponent, progressBlock: progressBlock)
|
passwordEntity.isDir = isDir
|
||||||
progressBlock(1.0)
|
do {
|
||||||
NotificationCenter.default.post(name: .passwordStoreUpdated, object: nil)
|
try self.context.save()
|
||||||
} catch {
|
ret = passwordEntity
|
||||||
print(error)
|
} catch {
|
||||||
|
fatalError("Failed to insert a PasswordEntity: \(error)")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
return ret
|
||||||
}
|
}
|
||||||
|
|
||||||
public func delete(passwordEntity: PasswordEntity) {
|
func add(password: Password) throws -> PasswordEntity? {
|
||||||
Utils.removeFileIfExists(at: storeURL.appendingPathComponent(passwordEntity.path!))
|
try createDirectoryTree(at: password.url!)
|
||||||
let _ = createRemoveCommitInRepository(message: "Remove \(passwordEntity.nameWithCategory) from store using Pass for iOS", path: passwordEntity.path!)
|
let newPasswordEntity = try addPasswordEntities(password: password)
|
||||||
context.delete(passwordEntity)
|
let saveURL = storeURL.appendingPathComponent(password.url!.path)
|
||||||
do {
|
try self.encrypt(password: password).write(to: saveURL)
|
||||||
try context.save()
|
try gitAdd(path: password.url!.path)
|
||||||
} catch {
|
let _ = try gitCommit(message: "Add password for \(password.url!.deletingPathExtension().path) to store using Pass for iOS.")
|
||||||
fatalError("Failed to delete a PasswordEntity: \(error)")
|
NotificationCenter.default.post(name: .passwordStoreUpdated, object: nil)
|
||||||
|
return newPasswordEntity
|
||||||
|
}
|
||||||
|
|
||||||
|
public func delete(passwordEntity: PasswordEntity) throws {
|
||||||
|
let deletedFileURL = passwordEntity.getURL()!
|
||||||
|
try deleteDirectoryTree(at: passwordEntity.getURL()!)
|
||||||
|
try deletePasswordEntities(passwordEntity: passwordEntity)
|
||||||
|
try gitRm(path: deletedFileURL.path)
|
||||||
|
let _ = try gitCommit(message: "Remove \(deletedFileURL.deletingPathExtension().path.removingPercentEncoding!) from store using Pass for iOS.")
|
||||||
|
NotificationCenter.default.post(name: .passwordStoreUpdated, object: nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
func edit(passwordEntity: PasswordEntity, password: Password) throws -> PasswordEntity? {
|
||||||
|
var newPasswordEntity: PasswordEntity? = passwordEntity
|
||||||
|
|
||||||
|
if password.changed&PasswordChange.content.rawValue != 0 {
|
||||||
|
print("chagne content")
|
||||||
|
let saveURL = storeURL.appendingPathComponent(passwordEntity.getURL()!.path)
|
||||||
|
try self.encrypt(password: password).write(to: saveURL)
|
||||||
|
try gitAdd(path: passwordEntity.getURL()!.path)
|
||||||
|
let _ = try gitCommit(message: "Edit password for \(passwordEntity.getURL()!.deletingPathExtension().path.removingPercentEncoding!) to store using Pass for iOS.")
|
||||||
|
newPasswordEntity = passwordEntity
|
||||||
|
}
|
||||||
|
|
||||||
|
if password.changed&PasswordChange.path.rawValue != 0 {
|
||||||
|
print("change path")
|
||||||
|
let deletedFileURL = passwordEntity.getURL()!
|
||||||
|
// add
|
||||||
|
try createDirectoryTree(at: password.url!)
|
||||||
|
newPasswordEntity = try addPasswordEntities(password: password)
|
||||||
|
|
||||||
|
// mv
|
||||||
|
try gitMv(from: deletedFileURL.path, to: password.url!.path)
|
||||||
|
|
||||||
|
// delete
|
||||||
|
try deleteDirectoryTree(at: deletedFileURL)
|
||||||
|
try deletePasswordEntities(passwordEntity: passwordEntity)
|
||||||
|
let _ = try gitCommit(message: "Rename \(deletedFileURL.deletingPathExtension().path.removingPercentEncoding!) to \(password.url!.deletingPathExtension().path.removingPercentEncoding!) using Pass for iOS.")
|
||||||
|
|
||||||
}
|
}
|
||||||
NotificationCenter.default.post(name: .passwordStoreUpdated, object: nil)
|
NotificationCenter.default.post(name: .passwordStoreUpdated, object: nil)
|
||||||
|
return newPasswordEntity
|
||||||
|
}
|
||||||
|
|
||||||
|
private func deletePasswordEntities(passwordEntity: PasswordEntity) throws {
|
||||||
|
var current: PasswordEntity? = passwordEntity
|
||||||
|
print(passwordEntity.path!)
|
||||||
|
while current != nil && (current!.children!.count == 0 || !current!.isDir) {
|
||||||
|
let parent = current!.parent
|
||||||
|
self.context.delete(current!)
|
||||||
|
current = parent
|
||||||
|
do {
|
||||||
|
try self.context.save()
|
||||||
|
} catch {
|
||||||
|
fatalError("Failed to delete a PasswordEntity: \(error)")
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func saveUpdated(passwordEntity: PasswordEntity) {
|
func saveUpdated(passwordEntity: PasswordEntity) {
|
||||||
|
|
@ -703,7 +820,6 @@ class PasswordStore {
|
||||||
try self.storeRepository?.reset(to: newHead, resetType: GTRepositoryResetType.hard)
|
try self.storeRepository?.reset(to: newHead, resetType: GTRepositoryResetType.hard)
|
||||||
self.setAllSynced()
|
self.setAllSynced()
|
||||||
self.updatePasswordEntityCoreData()
|
self.updatePasswordEntityCoreData()
|
||||||
Defaults[.lastSyncedTime] = nil
|
|
||||||
|
|
||||||
NotificationCenter.default.post(name: .passwordStoreUpdated, object: nil)
|
NotificationCenter.default.post(name: .passwordStoreUpdated, object: nil)
|
||||||
NotificationCenter.default.post(name: .passwordStoreChangeDiscarded, object: nil)
|
NotificationCenter.default.post(name: .passwordStoreChangeDiscarded, object: nil)
|
||||||
|
|
@ -739,4 +855,28 @@ class PasswordStore {
|
||||||
// get a list of local commits
|
// get a list of local commits
|
||||||
return try storeRepository?.localCommitsRelative(toRemoteBranch: remoteMasterBranch)
|
return try storeRepository?.localCommitsRelative(toRemoteBranch: remoteMasterBranch)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
func decrypt(passwordEntity: PasswordEntity, requestPGPKeyPassphrase: () -> String) throws -> Password? {
|
||||||
|
var password: Password?
|
||||||
|
let encryptedDataPath = URL(fileURLWithPath: "\(Globals.repositoryPath)/\(passwordEntity.path!)")
|
||||||
|
let encryptedData = try Data(contentsOf: encryptedDataPath)
|
||||||
|
var passphrase = self.pgpKeyPassphrase
|
||||||
|
if passphrase == nil {
|
||||||
|
passphrase = requestPGPKeyPassphrase()
|
||||||
|
}
|
||||||
|
let decryptedData = try PasswordStore.shared.pgp.decryptData(encryptedData, passphrase: passphrase)
|
||||||
|
let plainText = String(data: decryptedData, encoding: .utf8) ?? ""
|
||||||
|
let escapedPath = passwordEntity.path!.stringByAddingPercentEncodingForRFC3986() ?? ""
|
||||||
|
password = Password(name: passwordEntity.name!, url: URL(string: escapedPath), plainText: plainText)
|
||||||
|
return password
|
||||||
|
}
|
||||||
|
|
||||||
|
func encrypt(password: Password) throws -> Data {
|
||||||
|
let plainData = password.getPlainData()
|
||||||
|
let pgp = PasswordStore.shared.pgp
|
||||||
|
let encryptedData = try pgp.encryptData(plainData, usingPublicKey: pgp.getKeysOf(.public)[0], armored: Defaults[.encryptInArmored])
|
||||||
|
return encryptedData
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
Before Width: | Height: | Size: 126 KiB After Width: | Height: | Size: 150 KiB |
|
Before Width: | Height: | Size: 182 KiB After Width: | Height: | Size: 206 KiB |
|
Before Width: | Height: | Size: 99 KiB After Width: | Height: | Size: 137 KiB |
|
Before Width: | Height: | Size: 170 KiB After Width: | Height: | Size: 210 KiB |
|
Before Width: | Height: | Size: 144 KiB After Width: | Height: | Size: 152 KiB |