Merge and resolve conflit

This commit is contained in:
Bob Sun 2017-03-21 13:34:26 -07:00
commit c21502a10f
No known key found for this signature in database
GPG key ID: 1F86BA2052FED3B4
5 changed files with 139 additions and 111 deletions

View file

@ -13,26 +13,25 @@ import SVProgressHUD
class PasswordDetailTableViewController: UITableViewController, UIGestureRecognizerDelegate {
var passwordEntity: PasswordEntity?
var passwordCategoryText = ""
var password: Password?
var passwordImage: UIImage?
var oneTimePasswordIndexPath : IndexPath?
var shouldPopCurrentView = false
let passwordStore = PasswordStore.shared
private var password: Password?
private var passwordCategoryText = ""
private var passwordImage: UIImage?
private var oneTimePasswordIndexPath : IndexPath?
private var shouldPopCurrentView = false
private let passwordStore = PasswordStore.shared
let indicator: UIActivityIndicatorView = {
private let indicator: UIActivityIndicatorView = {
let indicator = UIActivityIndicatorView(activityIndicatorStyle: .gray)
indicator.center = CGPoint(x: UIScreen.main.bounds.width / 2, y: UIScreen.main.bounds.height * 0.382)
return indicator
}()
lazy var editUIBarButtonItem: UIBarButtonItem = {
private lazy var editUIBarButtonItem: UIBarButtonItem = {
let uiBarButtonItem = UIBarButtonItem(barButtonSystemItem: .edit, target: self, action: #selector(pressEdit(_:)))
return uiBarButtonItem
}()
struct TableCell {
private struct TableCell {
var title: String
var content: String
init() {
@ -46,12 +45,12 @@ class PasswordDetailTableViewController: UITableViewController, UIGestureRecogni
}
}
struct TableSection {
private struct TableSection {
var title: String
var item: Array<TableCell>
}
var tableData = Array<TableSection>()
private var tableData = Array<TableSection>()
override func viewDidLoad() {
super.viewDidLoad()
@ -68,7 +67,6 @@ class PasswordDetailTableViewController: UITableViewController, UIGestureRecogni
tableView.rowHeight = UITableViewAutomaticDimension
tableView.estimatedRowHeight = 52
indicator.startAnimating()
tableView.addSubview(indicator)
editUIBarButtonItem.isEnabled = false
@ -96,16 +94,29 @@ class PasswordDetailTableViewController: UITableViewController, UIGestureRecogni
self.present(alert, animated: true, completion: nil)
}
self.setupUpdateOneTimePassword()
self.addNotificationObservers()
self.setupOneTimePasswordAutoRefresh()
NotificationCenter.default.addObserver(self, selector: #selector(setShouldPopCurrentView), name: .passwordStoreChangeDiscarded, object: nil)
NotificationCenter.default.addObserver(self, selector: #selector(showPassword), name: .passwordStoreUpdated, object: nil)
}
func decryptThenShowPassword(passphrase: String) {
override func viewDidAppear(_ animated: Bool) {
super.viewWillAppear(animated)
if self.shouldPopCurrentView {
let alert = UIAlertController(title: "Notice", message: "All previous local changes have been discarded. Your current Password Store will be shown.", preferredStyle: UIAlertControllerStyle.alert)
alert.addAction(UIAlertAction(title: "OK", style: UIAlertActionStyle.default, handler: {_ in
_ = self.navigationController?.popViewController(animated: true)
}))
self.present(alert, animated: true, completion: nil)
}
}
private func decryptThenShowPassword(passphrase: String) {
if Defaults[.isRememberPassphraseOn] {
self.passwordStore.pgpKeyPassphrase = passphrase
}
DispatchQueue.global(qos: .userInitiated).async {
// decrypt password
do {
self.password = try self.passwordEntity!.decrypt(passphrase: passphrase)!
} catch {
@ -118,27 +129,30 @@ class PasswordDetailTableViewController: UITableViewController, UIGestureRecogni
}
return
}
// display password
self.showPassword()
}
}
let password = self.password!
@objc private func showPassword() {
DispatchQueue.main.async { [weak self] in
self?.showPassword(password: password)
self?.indicator.stopAnimating()
self?.setTableData()
UIView.performWithoutAnimation {
self?.tableView.reloadData()
// add layoutIfNeeded solves the "flickering problem" during refresh
self?.tableView.layoutIfNeeded()
}
self?.editUIBarButtonItem.isEnabled = true
if let urlString = self?.password?.getURLString() {
if self?.passwordEntity?.image == nil {
self?.updatePasswordImage(urlString: urlString)
}
}
}
}
func showPassword(password: Password) {
setTableData()
self.tableView.reloadData()
indicator.stopAnimating()
editUIBarButtonItem.isEnabled = true
if let urlString = password.getURLString() {
if self.passwordEntity?.image == nil{
self.updatePasswordImage(urlString: urlString)
}
}
}
func setupUpdateOneTimePassword() {
private func setupOneTimePasswordAutoRefresh() {
Timer.scheduledTimer(withTimeInterval: 1, repeats: true) {
[weak self] timer in
// bail out of the timer code if the object has been freed
@ -163,15 +177,19 @@ class PasswordDetailTableViewController: UITableViewController, UIGestureRecogni
}
}
func pressEdit(_ sender: Any?) {
@objc private func pressEdit(_ sender: Any?) {
performSegue(withIdentifier: "editPasswordSegue", sender: self)
}
@IBAction func cancelEditPassword(segue: UIStoryboardSegue) {
@objc private func setShouldPopCurrentView() {
self.shouldPopCurrentView = true
}
@IBAction private func cancelEditPassword(segue: UIStoryboardSegue) {
}
@IBAction func saveEditPassword(segue: UIStoryboardSegue) {
@IBAction private func saveEditPassword(segue: UIStoryboardSegue) {
if self.password!.changed {
SVProgressHUD.show(withStatus: "Saving")
DispatchQueue.global(qos: .userInitiated).async {
@ -192,13 +210,13 @@ class PasswordDetailTableViewController: UITableViewController, UIGestureRecogni
}
}
@IBAction func deletePassword(segue: UIStoryboardSegue) {
@IBAction private func deletePassword(segue: UIStoryboardSegue) {
print("delete")
passwordStore.delete(passwordEntity: passwordEntity!)
navigationController?.popViewController(animated: true)
}
func setTableData() {
private func setTableData() {
self.tableData = Array<TableSection>()
tableData.append(TableSection(title: "", item: []))
tableData[0].item.append(TableCell())
@ -260,7 +278,7 @@ class PasswordDetailTableViewController: UITableViewController, UIGestureRecogni
}
}
func updatePasswordImage(urlString: String) {
private func updatePasswordImage(urlString: String) {
var newUrlString = urlString
if urlString.lowercased().hasPrefix("http://") {
// try to replace http url to https url
@ -299,7 +317,7 @@ class PasswordDetailTableViewController: UITableViewController, UIGestureRecogni
}
}
func tapMenu(recognizer: UITapGestureRecognizer) {
@objc private func tapMenu(recognizer: UITapGestureRecognizer) {
if recognizer.state == UIGestureRecognizerState.ended {
let tapLocation = recognizer.location(in: self.tableView)
if let tapIndexPath = self.tableView.indexPathForRow(at: tapLocation) {
@ -308,9 +326,9 @@ class PasswordDetailTableViewController: UITableViewController, UIGestureRecogni
let menuController = UIMenuController.shared
let revealItem = UIMenuItem(title: "Reveal", action: #selector(LabelTableViewCell.revealPassword(_:)))
let concealItem = UIMenuItem(title: "Conceal", action: #selector(LabelTableViewCell.concealPassword(_:)))
let nextPasswordItem = UIMenuItem(title: "Next Password", action: #selector(LabelTableViewCell.nextPassword(_:)))
let nextHOTPItem = UIMenuItem(title: "Next Password", action: #selector(LabelTableViewCell.getNextHOTP(_:)))
let openURLItem = UIMenuItem(title: "Copy Password & Open Link", action: #selector(LabelTableViewCell.openLink(_:)))
menuController.menuItems = [revealItem, concealItem, nextPasswordItem, openURLItem]
menuController.menuItems = [revealItem, concealItem, nextHOTPItem, openURLItem]
menuController.setTargetRect(tappedCell.contentLabel.frame, in: tappedCell.contentLabel.superview!)
menuController.setMenuVisible(true, animated: true)
}
@ -318,6 +336,46 @@ class PasswordDetailTableViewController: UITableViewController, UIGestureRecogni
}
}
func getNextHOTP() {
guard password != nil, passwordEntity != nil, password?.otpType == .hotp else {
DispatchQueue.main.async {
Utils.alert(title: "Error", message: "Get next password of a non-HOTP entry.", controller: self, completion: nil)
}
return;
}
// increase HOTP counter
password!.increaseHotpCounter()
// copy HOTP to pasteboard
if let plainPassword = password!.otpToken?.currentPassword {
Utils.copyToPasteboard(textToCopy: plainPassword)
}
// commit the change of HOTP counter
if password!.changed {
DispatchQueue.global(qos: .userInitiated).async {
self.passwordStore.update(passwordEntity: self.passwordEntity!, password: self.password!, progressBlock: {_ in })
DispatchQueue.main.async {
self.passwordEntity!.synced = false
self.passwordStore.saveUpdated(passwordEntity: self.passwordEntity!)
SVProgressHUD.showSuccess(withStatus: "Password Copied\nCounter Updated")
SVProgressHUD.dismiss(withDelay: 1)
}
}
}
}
func openLink() {
guard let urlString = self.password?.getURLString(), let url = URL(string: urlString) else {
DispatchQueue.main.async {
Utils.alert(title: "Error", message: "Cannot find a valid URL", controller: self, completion: nil)
}
return;
}
Utils.copyToPasteboard(textToCopy: password?.password)
UIApplication.shared.open(url, options: [:], completionHandler: nil)
}
override func numberOfSections(in tableView: UITableView) -> Int {
return tableData.count
@ -345,7 +403,7 @@ class PasswordDetailTableViewController: UITableViewController, UIGestureRecogni
let cell = tableView.dequeueReusableCell(withIdentifier: "labelCell", for: indexPath) as! LabelTableViewCell
let titleData = tableData[sectionIndex].item[rowIndex].title
let contentData = tableData[sectionIndex].item[rowIndex].content
cell.passwordTableView = self
cell.delegatePasswordTableView = self
cell.isPasswordCell = (titleData.lowercased() == "password" ? true : false)
cell.isURLCell = (titleData.lowercased() == "url" ? true : false)
cell.isHOTPCell = (titleData == "HMAC-based" ? true : false)
@ -386,23 +444,4 @@ class PasswordDetailTableViewController: UITableViewController, UIGestureRecogni
override func tableView(_ tableView: UITableView, shouldShowMenuForRowAt indexPath: IndexPath) -> Bool {
return true
}
private func addNotificationObservers() {
NotificationCenter.default.addObserver(self, selector: #selector(setShouldPopCurrentView), name: .passwordStoreChangeDiscarded, object: nil)
}
func setShouldPopCurrentView() {
self.shouldPopCurrentView = true
}
override func viewDidAppear(_ animated: Bool) {
super.viewWillAppear(animated)
if self.shouldPopCurrentView {
let alert = UIAlertController(title: "Notice", message: "All previous local changes have been discarded. Your current Password Store will be shown.", preferredStyle: UIAlertControllerStyle.alert)
alert.addAction(UIAlertAction(title: "OK", style: UIAlertActionStyle.default, handler: {_ in
_ = self.navigationController?.popViewController(animated: true)
}))
self.present(alert, animated: true, completion: nil)
}
}
}

View file

@ -28,6 +28,9 @@ class Globals {
static let passwordMaximumLength = 24
static let passwordDefaultLength = 16
static let passwordDots = "••••••••••••"
static let passwordFonts = "Menlo"
private init() { }
}

View file

@ -28,6 +28,24 @@ class Password {
var firstLineIsOTPField = false
var otpToken: Token?
enum OtpType {
case totp, hotp, none
}
var otpType: OtpType {
get {
guard let token = self.otpToken else {
return OtpType.none
}
switch token.generator.factor {
case .counter:
return OtpType.hotp
case .timer:
return OtpType.totp
}
}
}
init(name: String, plainText: String) {
self.initEverything(name: name, plainText: plainText)
}

View file

@ -20,6 +20,7 @@ class FillPasswordTableViewCell: ContentTableViewCell {
override func awakeFromNib() {
super.awakeFromNib()
// Initialization code
contentTextField.font = UIFont(name: Globals.passwordFonts, size: (contentTextField.font?.pointSize)!)
}
override func setSelected(_ selected: Bool, animated: Bool) {
@ -30,7 +31,7 @@ class FillPasswordTableViewCell: ContentTableViewCell {
@IBAction func generatePassword(_ sender: UIButton) {
let plainPassword = self.delegate?.generatePassword() ?? Utils.generatePassword(length: 16)
contentTextField.attributedText = Utils.attributedPassword(plainPassword: plainPassword)
self.setContent(content: plainPassword)
Utils.copyToPasteboard(textToCopy: plainPassword)
}

View file

@ -19,15 +19,13 @@ class LabelTableViewCell: UITableViewCell {
@IBOutlet weak var contentLabel: UILabel!
@IBOutlet weak var titleLabel: UILabel!
let passwordStore = PasswordStore.shared
var isPasswordCell = false
var isURLCell = false
var isReveal = false
var isHOTPCell = false
let passwordDots = "••••••••••••"
weak var passwordTableView : PasswordDetailTableViewController?
weak var delegatePasswordTableView : PasswordDetailTableViewController?
var cellData: LabelTableViewCellData? {
didSet {
@ -36,14 +34,14 @@ class LabelTableViewCell: UITableViewCell {
if isReveal {
contentLabel.attributedText = Utils.attributedPassword(plainPassword: cellData?.content ?? "")
} else {
contentLabel.text = passwordDots
contentLabel.text = Globals.passwordDots
}
contentLabel.font = UIFont(name: "Menlo", size: contentLabel.font.pointSize)
contentLabel.font = UIFont(name: Globals.passwordFonts, size: contentLabel.font.pointSize)
} else if isHOTPCell {
if isReveal {
contentLabel.text = cellData?.content ?? ""
} else {
contentLabel.text = passwordDots
contentLabel.text = Globals.passwordDots
}
} else {
contentLabel.text = cellData?.content
@ -78,9 +76,9 @@ class LabelTableViewCell: UITableViewCell {
}
if isHOTPCell {
if isReveal {
return action == #selector(copy(_:)) || action == #selector(LabelTableViewCell.concealPassword(_:)) || action == #selector(LabelTableViewCell.nextPassword(_:))
return action == #selector(copy(_:)) || action == #selector(LabelTableViewCell.concealPassword(_:)) || action == #selector(LabelTableViewCell.getNextHOTP(_:))
} else {
return action == #selector(copy(_:)) || action == #selector(LabelTableViewCell.revealPassword(_:)) || action == #selector(LabelTableViewCell.nextPassword(_:))
return action == #selector(copy(_:)) || action == #selector(LabelTableViewCell.revealPassword(_:)) || action == #selector(LabelTableViewCell.getNextHOTP(_:))
}
}
return action == #selector(copy(_:))
@ -104,48 +102,17 @@ class LabelTableViewCell: UITableViewCell {
}
func concealPassword(_ sender: Any?) {
contentLabel.text = passwordDots
contentLabel.text = Globals.passwordDots
isReveal = false
}
func nextPassword(_ sender: Any?) {
guard let password = passwordTableView?.password,
let passwordEntity = passwordTableView?.passwordEntity else {
print("Cannot find password/passwordEntity of a cell")
return;
}
// increase HOTP counter
password.increaseHotpCounter()
// only the HOTP password needs update
if let plainPassword = password.otpToken?.currentPassword {
cellData?.content = plainPassword
// contentLabel will be updated automatically
}
// commit
if password.changed {
DispatchQueue.global(qos: .userInitiated).async {
self.passwordStore.update(passwordEntity: passwordEntity, password: password, progressBlock: {_ in })
DispatchQueue.main.async {
passwordEntity.synced = false
self.passwordStore.saveUpdated(passwordEntity: passwordEntity)
// reload so that the "unsynced" symbol could be added
self.passwordTableView?.tableView.reloadRows(at: [IndexPath(row: 0, section: 0)], with: UITableViewRowAnimation.automatic)
SVProgressHUD.showSuccess(withStatus: "Password Copied\nCounter Updated")
SVProgressHUD.dismiss(withDelay: 1)
}
}
}
}
func openLink(_ sender: Any?) {
guard let password = passwordTableView?.password else {
print("Cannot find password of a cell")
return;
// if isURLCell, passwordTableView should not be nil
delegatePasswordTableView!.openLink()
}
Utils.copyToPasteboard(textToCopy: password.password)
UIApplication.shared.open(URL(string: cellData!.content)!, options: [:], completionHandler: nil)
func getNextHOTP(_ sender: Any?) {
// if isHOTPCell, passwordTableView should not be nil
delegatePasswordTableView!.getNextHOTP()
}
}