2017-03-07 13:58:08 +03:00
|
|
|
import QtQuick 2.5
|
2017-01-27 15:55:38 +03:00
|
|
|
import QtQuick.Controls 1.4
|
|
|
|
import QtQuick.Layouts 1.1
|
2017-02-02 16:16:52 +03:00
|
|
|
import QtQuick.Controls.Styles 1.4
|
2017-01-27 15:55:38 +03:00
|
|
|
import QtQuick.Dialogs 1.2
|
2017-05-11 16:25:50 +03:00
|
|
|
import QtQuick.Window 2.2
|
2017-02-20 15:47:39 +03:00
|
|
|
import Qt.labs.settings 1.0
|
2017-01-27 15:55:38 +03:00
|
|
|
|
|
|
|
ApplicationWindow {
|
2017-02-06 17:00:11 +03:00
|
|
|
id: appWindow
|
2017-02-02 16:16:52 +03:00
|
|
|
width: 300
|
|
|
|
height: 400
|
2017-02-04 00:28:57 +03:00
|
|
|
minimumHeight: 400
|
|
|
|
minimumWidth: 300
|
2017-03-16 17:10:15 +03:00
|
|
|
title: getTitle()
|
2017-02-07 12:12:22 +03:00
|
|
|
property var device: yk
|
2017-11-07 12:34:42 +03:00
|
|
|
property var credentials: device.entries
|
2017-02-07 16:17:54 +03:00
|
|
|
property bool hasDevice: device.hasDevice
|
2017-03-17 15:15:48 +03:00
|
|
|
property bool canShowCredentials: device.hasDevice && modeAndKeyMatch
|
|
|
|
&& device.validated
|
2017-03-16 17:29:50 +03:00
|
|
|
property bool modeAndKeyMatch: slotModeMatch || ccidModeMatch
|
|
|
|
property bool slotModeMatch: (settings.slotMode && device.hasOTP)
|
|
|
|
property bool ccidModeMatch: (!settings.slotMode && device.hasCCID)
|
2017-02-17 16:09:55 +03:00
|
|
|
property var hotpCoolDowns: []
|
|
|
|
|
2017-11-29 16:45:53 +03:00
|
|
|
property var selectedKey: null
|
2017-03-30 11:23:04 +03:00
|
|
|
|
2017-03-01 15:17:47 +03:00
|
|
|
// Don't refresh credentials when window is minimized or hidden
|
|
|
|
// See http://doc.qt.io/qt-5/qwindow.html#Visibility-enum
|
|
|
|
property bool shouldRefresh: visibility != 3 && visibility != 0
|
|
|
|
|
2017-11-16 20:15:16 +03:00
|
|
|
property bool hideOnLaunch: settings.closeToTray && settings.hideOnLaunch
|
2017-10-23 20:02:33 +03:00
|
|
|
|
2017-11-17 21:22:01 +03:00
|
|
|
property bool displayTimersRunning: device.hasDevice && appWindow.visible
|
|
|
|
|
2017-03-22 16:45:33 +03:00
|
|
|
signal copy
|
2017-07-31 14:52:06 +03:00
|
|
|
signal generate(bool copyAfterGenerate)
|
2017-03-22 16:45:33 +03:00
|
|
|
signal deleteCredential
|
|
|
|
|
|
|
|
onDeleteCredential: confirmDeleteCredential.open()
|
2017-11-29 16:45:53 +03:00
|
|
|
onGenerate: handleGenerate(getSelected(), copyAfterGenerate)
|
|
|
|
onCopy: clipboard.setClipboard(getSelected().code.value)
|
2017-03-22 16:45:33 +03:00
|
|
|
|
2017-03-17 11:07:41 +03:00
|
|
|
onHasDeviceChanged: handleNewDevice()
|
|
|
|
|
2017-03-16 17:41:26 +03:00
|
|
|
menuBar: MainMenuBar {
|
|
|
|
slotMode: settings.slotMode
|
|
|
|
hasDevice: device.hasDevice
|
2017-11-29 16:45:53 +03:00
|
|
|
enableGenerate: enableManualGenerate(getSelected())
|
2017-03-16 17:41:26 +03:00
|
|
|
onOpenAddCredential: openClearAddCredential()
|
|
|
|
onOpenSetPassword: setPassword.open()
|
|
|
|
onOpenReset: reset.open()
|
|
|
|
onOpenSettings: settingsDialog.open()
|
|
|
|
onOpenAbout: aboutPage.open()
|
|
|
|
}
|
|
|
|
|
2017-03-30 14:14:24 +03:00
|
|
|
Component.onCompleted: {
|
2017-11-07 12:34:42 +03:00
|
|
|
settings.savedPasswords = "" //No longer used.
|
2017-03-30 14:14:24 +03:00
|
|
|
updateTrayVisability()
|
2017-05-12 11:02:13 +03:00
|
|
|
ensureValidWindowPosition()
|
2017-03-30 14:14:24 +03:00
|
|
|
}
|
|
|
|
|
2017-05-11 16:25:50 +03:00
|
|
|
Component.onDestruction: saveScreenLayout()
|
|
|
|
|
2017-03-22 12:02:07 +03:00
|
|
|
Shortcut {
|
|
|
|
sequence: StandardKey.Close
|
|
|
|
onActivated: close()
|
|
|
|
}
|
|
|
|
|
2017-02-28 14:08:42 +03:00
|
|
|
SystemPalette {
|
|
|
|
id: palette
|
|
|
|
}
|
2017-02-20 15:47:39 +03:00
|
|
|
|
2017-09-12 17:21:38 +03:00
|
|
|
BusyIndicator {
|
|
|
|
id: busy
|
|
|
|
z: 1
|
|
|
|
running: false
|
|
|
|
anchors.centerIn: parent
|
|
|
|
}
|
|
|
|
|
2017-03-16 17:41:26 +03:00
|
|
|
// This information is stored in the system registry on Windows,
|
|
|
|
// and in XML preferences files on macOS. On other Unix systems,
|
|
|
|
// in the absence of a standard, INI text files are used.
|
|
|
|
// See http://doc.qt.io/qt-5/qml-qt-labs-settings-settings.html#details
|
2017-02-20 15:47:39 +03:00
|
|
|
Settings {
|
|
|
|
id: settings
|
|
|
|
property bool slotMode
|
|
|
|
property bool slot1
|
|
|
|
property bool slot2
|
|
|
|
property var slot1digits
|
|
|
|
property var slot2digits
|
2017-03-01 13:57:40 +03:00
|
|
|
property string savedPasswords
|
2017-03-30 14:14:24 +03:00
|
|
|
property bool closeToTray
|
2017-10-23 20:02:33 +03:00
|
|
|
property bool hideOnLaunch
|
2017-03-22 14:24:02 +03:00
|
|
|
|
2017-05-11 16:25:50 +03:00
|
|
|
// Keep track of window and desktop dimensions.
|
2017-03-22 14:24:02 +03:00
|
|
|
property alias width: appWindow.width
|
|
|
|
property alias height: appWindow.height
|
2017-05-11 16:25:50 +03:00
|
|
|
property alias x: appWindow.x
|
|
|
|
property alias y: appWindow.y
|
|
|
|
property var desktopAvailableWidth
|
|
|
|
property var desktopAvailableHeight
|
2017-03-30 14:14:24 +03:00
|
|
|
|
|
|
|
onCloseToTrayChanged: {
|
|
|
|
updateTrayVisability()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-01-27 15:55:38 +03:00
|
|
|
AboutPage {
|
|
|
|
id: aboutPage
|
2017-11-20 17:30:26 +03:00
|
|
|
device: yk
|
|
|
|
slotMode: settings.slotMode
|
2017-01-27 15:55:38 +03:00
|
|
|
}
|
|
|
|
|
2017-02-03 11:58:17 +03:00
|
|
|
AddCredential {
|
|
|
|
id: addCredential
|
2017-03-07 17:16:10 +03:00
|
|
|
device: yk
|
|
|
|
}
|
|
|
|
|
|
|
|
AddCredentialSlot {
|
|
|
|
id: addCredentialSlot
|
2017-03-01 16:38:02 +03:00
|
|
|
settings: settings
|
|
|
|
device: yk
|
2017-02-03 11:58:17 +03:00
|
|
|
}
|
|
|
|
|
2017-02-20 15:47:39 +03:00
|
|
|
SettingsDialog {
|
|
|
|
id: settingsDialog
|
|
|
|
settings: settings
|
|
|
|
onAccepted: {
|
2017-03-16 17:40:51 +03:00
|
|
|
saveSettings()
|
2017-02-23 14:18:20 +03:00
|
|
|
refreshDependingOnMode(true)
|
2017-02-20 15:47:39 +03:00
|
|
|
}
|
2017-02-20 14:27:10 +03:00
|
|
|
}
|
|
|
|
|
2017-02-10 14:44:10 +03:00
|
|
|
SetPassword {
|
|
|
|
id: setPassword
|
2017-02-16 17:22:20 +03:00
|
|
|
onAccepted: {
|
2017-03-16 18:34:00 +03:00
|
|
|
trySetPassword()
|
|
|
|
passwordUpdated.open()
|
2017-03-18 20:27:40 +03:00
|
|
|
setPassword.clear()
|
2017-02-16 17:22:20 +03:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-03-17 10:48:38 +03:00
|
|
|
PasswordSetConfirmation {
|
2017-02-10 16:01:49 +03:00
|
|
|
id: passwordUpdated
|
|
|
|
}
|
|
|
|
|
2017-03-17 10:57:30 +03:00
|
|
|
Reset {
|
2017-02-17 10:52:34 +03:00
|
|
|
id: reset
|
|
|
|
onAccepted: {
|
|
|
|
device.reset()
|
2017-03-08 10:56:40 +03:00
|
|
|
device.refreshCCIDCredentials(true)
|
2017-03-18 20:43:11 +03:00
|
|
|
resetConfirmation.open()
|
2017-02-17 10:52:34 +03:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-03-18 20:43:11 +03:00
|
|
|
ResetConfirmation {
|
|
|
|
id: resetConfirmation
|
|
|
|
}
|
|
|
|
|
2017-02-17 12:50:17 +03:00
|
|
|
PasswordPrompt {
|
|
|
|
id: passwordPrompt
|
2017-03-17 13:01:59 +03:00
|
|
|
onAccepted: handlePasswordEntered()
|
2017-03-01 13:57:40 +03:00
|
|
|
}
|
|
|
|
|
2017-02-17 12:50:17 +03:00
|
|
|
// @disable-check M301
|
|
|
|
YubiKey {
|
|
|
|
id: yk
|
2017-03-27 14:36:40 +03:00
|
|
|
onError: console.log(traceback)
|
2017-03-17 11:36:50 +03:00
|
|
|
onWrongPassword: passwordPrompt.open()
|
2017-03-30 10:48:59 +03:00
|
|
|
onCredentialsRefreshed: {
|
2017-11-07 12:34:42 +03:00
|
|
|
credentials = device.entries
|
2017-03-30 10:48:59 +03:00
|
|
|
flickable.restoreScrollPosition()
|
|
|
|
hotpTouchTimer.stop()
|
|
|
|
touchYourYubikey.close()
|
|
|
|
}
|
2017-02-17 12:50:17 +03:00
|
|
|
}
|
|
|
|
|
2017-03-17 11:36:50 +03:00
|
|
|
NoLoadedDeviceMessage {
|
2017-02-17 12:50:17 +03:00
|
|
|
id: noLoadedDeviceMessage
|
2017-03-17 11:36:50 +03:00
|
|
|
device: yk
|
2017-02-17 12:50:17 +03:00
|
|
|
}
|
|
|
|
|
2017-03-17 12:19:06 +03:00
|
|
|
LoadedDeviceMessage {
|
|
|
|
id: loadedDeviceMessage
|
|
|
|
device: yk
|
|
|
|
nCredentials: filteredCredentials(credentials).length
|
|
|
|
settings: settings
|
2017-02-28 11:32:48 +03:00
|
|
|
}
|
|
|
|
|
2017-03-17 12:24:32 +03:00
|
|
|
ClipBoard {
|
2017-02-17 12:50:17 +03:00
|
|
|
id: clipboard
|
2017-02-03 18:23:28 +03:00
|
|
|
}
|
2017-02-03 11:58:17 +03:00
|
|
|
|
2017-03-17 13:01:59 +03:00
|
|
|
CredentialMenu {
|
2017-02-03 15:58:22 +03:00
|
|
|
id: credentialMenu
|
2017-11-29 16:45:53 +03:00
|
|
|
credential: getSelected()
|
|
|
|
showGenerate: allowManualGenerate(credential)
|
|
|
|
enableGenerate: enableManualGenerate(credential)
|
2017-02-03 18:23:28 +03:00
|
|
|
}
|
2017-02-03 11:58:17 +03:00
|
|
|
|
2017-03-17 13:13:14 +03:00
|
|
|
DeleteCredentialConfirmation {
|
2017-02-17 12:50:17 +03:00
|
|
|
id: confirmDeleteCredential
|
|
|
|
onAccepted: {
|
2017-03-17 13:13:14 +03:00
|
|
|
deleteSelectedCredential()
|
2017-02-23 18:00:17 +03:00
|
|
|
refreshDependingOnMode(true)
|
2017-02-17 12:50:17 +03:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-03-16 16:13:54 +03:00
|
|
|
TouchYubiKey {
|
2017-02-17 12:50:17 +03:00
|
|
|
id: touchYourYubikey
|
2017-02-17 12:14:37 +03:00
|
|
|
}
|
|
|
|
|
2017-03-17 14:21:31 +03:00
|
|
|
ArrowKeysSelecter {
|
2017-02-16 13:07:41 +03:00
|
|
|
id: arrowKeys
|
2017-11-29 20:35:47 +03:00
|
|
|
credentials: repeater.model
|
2017-03-21 17:51:37 +03:00
|
|
|
KeyNavigation.tab: search
|
2017-02-16 13:07:41 +03:00
|
|
|
}
|
|
|
|
|
2017-02-06 17:00:11 +03:00
|
|
|
ColumnLayout {
|
2017-02-06 15:42:17 +03:00
|
|
|
anchors.fill: parent
|
2017-03-21 15:16:06 +03:00
|
|
|
spacing: 0
|
2017-02-06 17:00:11 +03:00
|
|
|
|
2017-03-17 14:45:43 +03:00
|
|
|
TimeLeftBar {
|
|
|
|
id: timeLeftBar
|
2017-07-07 09:48:34 +03:00
|
|
|
visible: canShowCredentials && device.hasAnyCredentials()
|
2017-02-06 17:00:11 +03:00
|
|
|
}
|
2017-09-12 17:21:38 +03:00
|
|
|
|
2017-02-06 17:00:11 +03:00
|
|
|
ScrollView {
|
|
|
|
id: scrollView
|
|
|
|
Layout.fillHeight: true
|
|
|
|
Layout.fillWidth: true
|
2017-03-29 13:08:19 +03:00
|
|
|
|
2017-07-06 08:39:18 +03:00
|
|
|
MouseArea {
|
|
|
|
// A mouse area to allow a click
|
|
|
|
// outside search bar to remove focus from it.
|
|
|
|
anchors.fill: parent
|
2017-08-15 10:25:10 +03:00
|
|
|
onClicked: {
|
|
|
|
deselectCredential()
|
|
|
|
}
|
2017-07-06 08:39:18 +03:00
|
|
|
}
|
|
|
|
|
2017-03-28 15:27:49 +03:00
|
|
|
Flickable {
|
|
|
|
id: flickable
|
2017-03-29 13:08:19 +03:00
|
|
|
property double savedScrollPosition
|
2017-03-28 15:27:49 +03:00
|
|
|
Layout.fillHeight: true
|
|
|
|
Layout.fillWidth: true
|
2017-04-07 13:32:51 +03:00
|
|
|
contentWidth: credentialsColumn.width
|
2017-03-28 15:27:49 +03:00
|
|
|
contentHeight: credentialsColumn.height
|
|
|
|
clip: true
|
|
|
|
boundsBehavior: Flickable.StopAtBounds
|
|
|
|
|
2017-03-29 13:08:19 +03:00
|
|
|
function restoreScrollPosition() {
|
|
|
|
contentY = flickable.savedScrollPosition
|
|
|
|
}
|
|
|
|
|
|
|
|
function saveScrollPosition() {
|
|
|
|
savedScrollPosition = flickable.contentY
|
|
|
|
}
|
|
|
|
|
2017-03-28 15:27:49 +03:00
|
|
|
ColumnLayout {
|
|
|
|
width: flickable.width
|
|
|
|
id: credentialsColumn
|
2017-04-07 13:32:51 +03:00
|
|
|
visible: device.hasDevice && (ccidModeMatch
|
|
|
|
|| slotModeMatch)
|
2017-03-28 15:27:49 +03:00
|
|
|
anchors.right: appWindow.right
|
|
|
|
anchors.left: appWindow.left
|
|
|
|
anchors.top: appWindow.top
|
|
|
|
spacing: 0
|
|
|
|
|
|
|
|
Repeater {
|
|
|
|
id: repeater
|
|
|
|
model: filteredCredentials(credentials)
|
2017-03-30 11:23:04 +03:00
|
|
|
|
2017-11-21 13:04:38 +03:00
|
|
|
CredentialItem {
|
2017-11-21 18:33:48 +03:00
|
|
|
code: modelData.code
|
|
|
|
credential: modelData.credential
|
2017-11-21 18:48:59 +03:00
|
|
|
isExpired: appWindow.isExpired(modelData)
|
2017-11-29 13:53:50 +03:00
|
|
|
isSelected: appWindow.isSelected(modelData.credential)
|
2017-11-21 17:51:31 +03:00
|
|
|
timerRunning: displayTimersRunning
|
2017-11-21 18:39:41 +03:00
|
|
|
unselectedColor: (index % 2 == 0
|
|
|
|
? palette.window
|
|
|
|
: palette.midlight
|
|
|
|
)
|
2017-11-21 13:05:04 +03:00
|
|
|
|
2017-11-21 15:39:22 +03:00
|
|
|
onDoubleClick: {
|
|
|
|
// A double-click should select the credential,
|
|
|
|
// then generate if needed and copy the code.
|
2017-11-29 16:45:53 +03:00
|
|
|
selectCredential(modelData)
|
2017-11-21 15:39:22 +03:00
|
|
|
generateOrCopy()
|
|
|
|
}
|
|
|
|
|
2017-11-21 17:55:51 +03:00
|
|
|
onRefresh: refreshDependingOnMode(force)
|
|
|
|
|
2017-11-21 15:39:22 +03:00
|
|
|
onSingleClick: {
|
|
|
|
// Left click, select or deselect credential.
|
|
|
|
if (mouse.button & Qt.LeftButton) {
|
2017-11-29 13:53:50 +03:00
|
|
|
if (appWindow.isSelected(modelData.credential)) {
|
2017-11-21 15:39:22 +03:00
|
|
|
deselectCredential()
|
|
|
|
} else {
|
2017-11-29 16:45:53 +03:00
|
|
|
selectCredential(modelData)
|
2017-11-21 15:39:22 +03:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Right-click, select credential and open popup menu.
|
|
|
|
if (mouse.button & Qt.RightButton) {
|
2017-11-29 16:45:53 +03:00
|
|
|
selectCredential(modelData)
|
2017-11-21 15:39:22 +03:00
|
|
|
credentialMenu.popup()
|
|
|
|
}
|
|
|
|
}
|
2017-02-06 15:42:17 +03:00
|
|
|
}
|
2017-02-02 16:16:52 +03:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2017-02-06 17:00:11 +03:00
|
|
|
|
2017-02-06 17:07:43 +03:00
|
|
|
TextField {
|
|
|
|
id: search
|
2017-11-29 18:34:18 +03:00
|
|
|
focus: true
|
2017-03-13 16:51:14 +03:00
|
|
|
visible: canShowCredentials && device.hasAnyCredentials()
|
2017-03-16 16:04:50 +03:00
|
|
|
placeholderText: qsTr("Search...")
|
2017-02-06 17:07:43 +03:00
|
|
|
Layout.fillWidth: true
|
2017-03-19 00:20:52 +03:00
|
|
|
KeyNavigation.tab: arrowKeys
|
2017-03-22 11:29:58 +03:00
|
|
|
Shortcut {
|
|
|
|
sequence: StandardKey.Find
|
2017-11-29 18:34:18 +03:00
|
|
|
onActivated: search.forceActiveFocus()
|
2017-03-22 11:29:58 +03:00
|
|
|
}
|
2017-11-29 15:45:27 +03:00
|
|
|
onTextChanged: selectFirstSearchResult()
|
2017-08-01 17:29:31 +03:00
|
|
|
Keys.onEscapePressed: {
|
2017-11-29 20:20:07 +03:00
|
|
|
search.clear()
|
2017-08-15 10:25:10 +03:00
|
|
|
deselectCredential()
|
2017-08-01 17:29:31 +03:00
|
|
|
}
|
|
|
|
Keys.onReturnPressed: generateOrCopy()
|
|
|
|
Keys.onEnterPressed: generateOrCopy()
|
2017-11-07 05:40:06 +03:00
|
|
|
Keys.onDownPressed: arrowKeys.goDown()
|
|
|
|
Keys.onUpPressed: arrowKeys.goUp()
|
2017-02-06 17:00:11 +03:00
|
|
|
}
|
2017-02-02 16:16:52 +03:00
|
|
|
}
|
2017-02-06 17:57:32 +03:00
|
|
|
|
2017-01-27 15:55:38 +03:00
|
|
|
Timer {
|
2017-02-02 16:16:52 +03:00
|
|
|
id: ykTimer
|
2017-01-27 15:55:38 +03:00
|
|
|
triggeredOnStart: true
|
|
|
|
interval: 500
|
|
|
|
repeat: true
|
2017-11-17 21:22:01 +03:00
|
|
|
running: appWindow.visible
|
2017-11-07 12:34:42 +03:00
|
|
|
onTriggered: device.refresh(settings.slotMode, refreshDependingOnMode)
|
2017-01-27 15:55:38 +03:00
|
|
|
}
|
|
|
|
|
2017-02-02 16:16:52 +03:00
|
|
|
Timer {
|
2017-03-17 15:52:13 +03:00
|
|
|
id: timeLeftTimer
|
2017-02-02 16:16:52 +03:00
|
|
|
interval: 100
|
|
|
|
repeat: true
|
2017-11-17 21:22:01 +03:00
|
|
|
running: displayTimersRunning
|
2017-02-02 16:16:52 +03:00
|
|
|
triggeredOnStart: true
|
2017-03-17 15:52:13 +03:00
|
|
|
onTriggered: checkTimeLeft()
|
2017-01-27 15:55:38 +03:00
|
|
|
}
|
|
|
|
|
2017-02-17 12:50:17 +03:00
|
|
|
Timer {
|
2017-02-17 16:09:55 +03:00
|
|
|
id: hotpCoolDownTimer
|
2017-02-17 12:50:17 +03:00
|
|
|
interval: 5000
|
2017-02-17 16:09:55 +03:00
|
|
|
onTriggered: hotpCoolDowns = []
|
|
|
|
}
|
|
|
|
|
|
|
|
Timer {
|
|
|
|
id: hotpTouchTimer
|
|
|
|
interval: 500
|
|
|
|
onTriggered: touchYourYubikey.open()
|
2017-01-27 15:55:38 +03:00
|
|
|
}
|
|
|
|
|
2017-09-12 17:21:38 +03:00
|
|
|
NoQrDialog {
|
|
|
|
id: noQr
|
|
|
|
}
|
|
|
|
|
2017-11-29 16:45:53 +03:00
|
|
|
function selectCredential(entry) {
|
|
|
|
selectedKey = entry.credential.key
|
2017-11-21 18:06:04 +03:00
|
|
|
}
|
|
|
|
|
2017-08-15 10:25:10 +03:00
|
|
|
function deselectCredential() {
|
2017-11-29 16:45:53 +03:00
|
|
|
selectedKey = null
|
|
|
|
}
|
|
|
|
|
|
|
|
function getSelected() {
|
|
|
|
return credentials.find(function(entry) {
|
2017-11-29 20:23:37 +03:00
|
|
|
return isSelected(entry.credential)
|
2017-11-29 16:45:53 +03:00
|
|
|
}) || null
|
2017-08-15 10:25:10 +03:00
|
|
|
}
|
|
|
|
|
2017-11-29 13:53:50 +03:00
|
|
|
function isSelected(credential) {
|
2017-11-29 20:23:37 +03:00
|
|
|
return credential.key === selectedKey
|
2017-11-29 13:53:50 +03:00
|
|
|
}
|
|
|
|
|
2017-05-11 16:25:50 +03:00
|
|
|
function saveScreenLayout() {
|
|
|
|
settings.desktopAvailableWidth = Screen.desktopAvailableWidth
|
|
|
|
settings.desktopAvailableHeight = Screen.desktopAvailableHeight
|
|
|
|
}
|
|
|
|
|
2017-05-12 11:02:13 +03:00
|
|
|
function ensureValidWindowPosition() {
|
|
|
|
// If we have the same desktop dimensions as last time, use the saved position.
|
2017-05-11 16:25:50 +03:00
|
|
|
// If not, put the window in the middle of the screen.
|
|
|
|
var savedScreenLayout = (settings.desktopAvailableWidth === Screen.desktopAvailableWidth)
|
|
|
|
&& (settings.desktopAvailableHeight === Screen.desktopAvailableHeight)
|
|
|
|
appWindow.x = (savedScreenLayout) ? settings.x : Screen.width / 2 - appWindow.width / 2
|
|
|
|
appWindow.y = (savedScreenLayout) ? settings.y : Screen.height / 2 - appWindow.height / 2
|
|
|
|
}
|
|
|
|
|
2017-03-17 15:52:13 +03:00
|
|
|
function checkTimeLeft() {
|
|
|
|
var timeLeft = device.expiration - (Date.now() / 1000)
|
|
|
|
if (timeLeft <= 0 && timeLeftBar.value > 0) {
|
|
|
|
refreshDependingOnMode(true)
|
|
|
|
}
|
|
|
|
timeLeftBar.value = timeLeft
|
|
|
|
}
|
|
|
|
|
2017-11-07 12:34:42 +03:00
|
|
|
function allowManualGenerate(entry) {
|
|
|
|
var cred = entry && entry.credential
|
|
|
|
return cred != null && (cred.oath_type === "HOTP" || cred.touch)
|
2017-03-09 12:49:42 +03:00
|
|
|
}
|
|
|
|
|
2017-11-07 12:34:42 +03:00
|
|
|
function enableManualGenerate(entry) {
|
|
|
|
var cred = entry && entry.credential
|
|
|
|
if (allowManualGenerate(entry)) {
|
|
|
|
if (cred.oath_type !== "HOTP") {
|
|
|
|
return entry.code === null || isExpired(entry)
|
2017-03-17 13:01:59 +03:00
|
|
|
} else {
|
2017-11-07 12:34:42 +03:00
|
|
|
return !isInCoolDown(cred.key)
|
2017-03-17 13:01:59 +03:00
|
|
|
}
|
2017-03-09 12:49:42 +03:00
|
|
|
} else {
|
2017-03-17 13:01:59 +03:00
|
|
|
return false
|
2017-03-09 12:49:42 +03:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-11-07 12:34:42 +03:00
|
|
|
function isExpired(entry) {
|
|
|
|
return entry !== null && entry.code !== null && (entry.credential.oath_type !== "HOTP")
|
|
|
|
&& (entry.code.valid_to - (Date.now() / 1000) <= 0)
|
2017-03-08 15:58:42 +03:00
|
|
|
}
|
|
|
|
|
2017-02-23 14:18:20 +03:00
|
|
|
function refreshDependingOnMode(force) {
|
2017-03-01 15:17:47 +03:00
|
|
|
if (hasDevice && shouldRefresh) {
|
2017-11-07 12:34:42 +03:00
|
|
|
flickable.saveScrollPosition()
|
2017-02-28 12:57:10 +03:00
|
|
|
if (settings.slotMode && device.hasOTP) {
|
2017-07-05 14:46:08 +03:00
|
|
|
device.validated = true // Slot side has no password function
|
2017-02-28 14:08:42 +03:00
|
|
|
device.refreshSlotCredentials([settings.slot1, settings.slot2],
|
|
|
|
getSlotDigitsSettings(), force)
|
2017-02-28 12:57:10 +03:00
|
|
|
} else if (!settings.slotMode && device.hasCCID) {
|
2017-02-23 14:18:20 +03:00
|
|
|
device.refreshCCIDCredentials(force)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
function getSlotDigitsSettings() {
|
2017-09-12 17:21:38 +03:00
|
|
|
var slot1digits = 6
|
|
|
|
if (settings.slot1digits === 1) {
|
|
|
|
slot1digits = 7
|
|
|
|
}
|
|
|
|
if (settings.slot1digits === 2) {
|
|
|
|
slot1digits = 8
|
|
|
|
}
|
|
|
|
var slot2digits = 6
|
|
|
|
if (settings.slot2digits === 1) {
|
|
|
|
slot2digits = 7
|
|
|
|
}
|
|
|
|
if (settings.slot2digits === 2) {
|
|
|
|
slot2digits = 8
|
|
|
|
}
|
2017-02-23 14:18:20 +03:00
|
|
|
return [slot1digits, slot2digits]
|
|
|
|
}
|
|
|
|
|
2017-11-07 12:34:42 +03:00
|
|
|
function filteredCredentials(entries) {
|
2017-09-13 11:12:27 +03:00
|
|
|
var searchResult = []
|
2017-11-07 12:34:42 +03:00
|
|
|
if (entries !== null) {
|
|
|
|
for (var i = 0; i < entries.length; i++) {
|
|
|
|
var entry = entries[i]
|
|
|
|
if (entry.credential.key.toLowerCase().indexOf(search.text.toLowerCase(
|
2017-02-06 17:57:32 +03:00
|
|
|
)) !== -1) {
|
2017-11-07 12:34:42 +03:00
|
|
|
searchResult.push(entry)
|
2017-02-06 17:57:32 +03:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2017-03-28 12:02:27 +03:00
|
|
|
|
2017-09-13 11:12:27 +03:00
|
|
|
return searchResult
|
2017-02-06 17:57:32 +03:00
|
|
|
}
|
|
|
|
|
2017-11-29 15:45:27 +03:00
|
|
|
function selectFirstSearchResult() {
|
|
|
|
var searchResult = filteredCredentials(credentials)
|
|
|
|
if (search.text.length > 0) {
|
|
|
|
if (searchResult[0] != null) {
|
|
|
|
if (false === searchResult.some(function(entry) { return isSelected(entry.credential) })) {
|
|
|
|
// If search does not include current selection,
|
|
|
|
// reset selected to avoid hidden selected creds.
|
|
|
|
deselectCredential()
|
|
|
|
}
|
2017-11-29 16:45:53 +03:00
|
|
|
if (selectedKey === null) {
|
2017-11-29 15:45:27 +03:00
|
|
|
// If the search gave some results, and nothing is currently selected,
|
|
|
|
// the top credential should be selected.
|
2017-11-29 16:45:53 +03:00
|
|
|
selectCredential(searchResult[0])
|
2017-11-29 15:45:27 +03:00
|
|
|
}
|
|
|
|
} else {
|
|
|
|
// If search was started but no result,
|
|
|
|
// reset selected to avoid hidden selected creds.
|
|
|
|
deselectCredential()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-09-14 13:27:02 +03:00
|
|
|
function isInCoolDown(longName) {
|
|
|
|
return hotpCoolDowns.indexOf(longName) !== -1
|
2017-02-17 12:14:37 +03:00
|
|
|
}
|
2017-03-09 12:49:42 +03:00
|
|
|
|
2017-11-07 12:34:42 +03:00
|
|
|
function calculateCredential(entry, copyAfterUpdate) {
|
2017-03-30 10:48:59 +03:00
|
|
|
flickable.saveScrollPosition()
|
|
|
|
|
2017-02-23 15:51:44 +03:00
|
|
|
if (settings.slotMode) {
|
2017-11-07 12:34:42 +03:00
|
|
|
var slot = getSlot(entry.credential.name)
|
2017-02-23 15:51:44 +03:00
|
|
|
var digits = getDigits(slot)
|
2017-07-31 14:52:06 +03:00
|
|
|
device.calculateSlotMode(slot, digits, copyAfterUpdate)
|
2017-02-23 15:51:44 +03:00
|
|
|
} else {
|
2017-11-07 12:34:42 +03:00
|
|
|
device.calculate(entry, copyAfterUpdate)
|
2017-02-23 15:51:44 +03:00
|
|
|
}
|
2017-11-07 12:34:42 +03:00
|
|
|
if (entry.credential.oath_type === "HOTP") {
|
2017-02-17 16:09:55 +03:00
|
|
|
hotpTouchTimer.restart()
|
|
|
|
}
|
2017-11-07 12:34:42 +03:00
|
|
|
if (entry.credential.touch) {
|
2017-02-02 16:16:52 +03:00
|
|
|
touchYourYubikey.open()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-02-23 18:00:17 +03:00
|
|
|
function getSlot(name) {
|
|
|
|
if (name.indexOf('1') !== -1) {
|
2017-02-23 15:51:44 +03:00
|
|
|
return 1
|
|
|
|
}
|
2017-02-23 18:00:17 +03:00
|
|
|
if (name.indexOf('2') !== -1) {
|
2017-02-23 15:51:44 +03:00
|
|
|
return 2
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
function getDigits(slot) {
|
2017-02-28 14:08:42 +03:00
|
|
|
return getSlotDigitsSettings()[slot - 1]
|
2017-02-23 15:51:44 +03:00
|
|
|
}
|
2017-03-16 16:59:30 +03:00
|
|
|
|
|
|
|
function openClearAddCredential() {
|
|
|
|
if (settings.slotMode) {
|
|
|
|
addCredentialSlot.clear()
|
|
|
|
device.getSlotStatus(addCredentialSlot.open)
|
|
|
|
} else {
|
|
|
|
addCredential.clear()
|
|
|
|
addCredential.open()
|
|
|
|
}
|
|
|
|
}
|
2017-03-16 17:10:15 +03:00
|
|
|
|
|
|
|
function getTitle() {
|
|
|
|
return qsTr("Yubico Authenticator") + (settings.slotMode ? qsTr(" [Slot mode]") : '')
|
|
|
|
}
|
2017-03-16 18:34:00 +03:00
|
|
|
|
|
|
|
function saveSettings() {
|
|
|
|
settings.slotMode = settingsDialog.slotMode
|
|
|
|
settings.slot1 = settingsDialog.slot1
|
|
|
|
settings.slot2 = settingsDialog.slot2
|
|
|
|
settings.slot1digits = settingsDialog.slot1digits
|
|
|
|
settings.slot2digits = settingsDialog.slot2digits
|
2017-03-30 14:14:24 +03:00
|
|
|
settings.closeToTray = settingsDialog.closeToTray
|
2017-11-16 20:15:16 +03:00
|
|
|
settings.hideOnLaunch = settingsDialog.closeToTray && settingsDialog.hideOnLaunch
|
2017-03-16 18:34:00 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
function trySetPassword() {
|
|
|
|
if (setPassword.newPassword.length > 0) {
|
2017-11-07 12:34:42 +03:00
|
|
|
device.setPassword(setPassword.newPassword, setPassword.remember)
|
2017-03-16 18:34:00 +03:00
|
|
|
} else {
|
2017-11-07 12:34:42 +03:00
|
|
|
device.setPassword(null, true)
|
2017-03-16 18:34:00 +03:00
|
|
|
}
|
|
|
|
}
|
2017-03-17 11:07:41 +03:00
|
|
|
|
|
|
|
function handleNewDevice() {
|
|
|
|
if (device.hasDevice && ccidModeMatch) {
|
2017-04-10 10:17:27 +03:00
|
|
|
device.promptOrSkip(passwordPrompt)
|
2017-03-17 11:07:41 +03:00
|
|
|
} else {
|
|
|
|
passwordPrompt.close()
|
|
|
|
setPassword.close()
|
|
|
|
addCredential.close()
|
|
|
|
addCredentialSlot.close()
|
|
|
|
}
|
|
|
|
}
|
2017-03-17 13:01:59 +03:00
|
|
|
|
2017-11-07 12:34:42 +03:00
|
|
|
function handleGenerate(entry, copyAfterUpdate) {
|
|
|
|
if (!isInCoolDown(entry.credential.key)) {
|
|
|
|
calculateCredential(entry, copyAfterUpdate)
|
|
|
|
if (entry.credential.oath_type === "HOTP") {
|
|
|
|
hotpCoolDowns.push(entry.credential.key)
|
2017-03-17 13:01:59 +03:00
|
|
|
hotpCoolDownTimer.restart()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
function handlePasswordEntered() {
|
2017-11-07 12:34:42 +03:00
|
|
|
device.validate(passwordPrompt.password, passwordPrompt.remember)
|
2017-03-17 13:01:59 +03:00
|
|
|
}
|
2017-03-17 13:13:14 +03:00
|
|
|
|
|
|
|
function deleteSelectedCredential() {
|
|
|
|
if (settings.slotMode) {
|
2017-11-29 16:45:53 +03:00
|
|
|
device.deleteSlotCredential(getSlot(getSelected().credential.name))
|
2017-03-17 13:13:14 +03:00
|
|
|
} else {
|
2017-11-29 16:45:53 +03:00
|
|
|
device.deleteCredential(getSelected().credential)
|
2017-03-17 13:13:14 +03:00
|
|
|
}
|
|
|
|
}
|
2017-03-17 14:52:34 +03:00
|
|
|
|
2017-08-01 17:29:31 +03:00
|
|
|
function generateOrCopy() {
|
2017-11-29 16:45:53 +03:00
|
|
|
var selected = getSelected()
|
2017-11-07 12:34:42 +03:00
|
|
|
if (selected.code == null || isExpired(selected) || selected.credential.oath_type === 'HOTP') {
|
2017-07-31 14:52:06 +03:00
|
|
|
generate(true)
|
2017-07-07 11:03:50 +03:00
|
|
|
} else {
|
|
|
|
copy()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-03-31 12:14:22 +03:00
|
|
|
function updateTrayVisability() {
|
|
|
|
SysTrayIcon.visible = settings.closeToTray
|
|
|
|
// When the tray option is enabled, closing the last window
|
|
|
|
// doesn't actually close the application.
|
|
|
|
app.quitOnLastWindowClosed = !settings.closeToTray
|
|
|
|
}
|
2017-04-10 10:17:27 +03:00
|
|
|
|
2017-09-12 17:21:38 +03:00
|
|
|
function scanQr() {
|
|
|
|
busy.running = true
|
|
|
|
device.parseQr(ScreenShot.capture(), function (uri) {
|
|
|
|
busy.running = false
|
|
|
|
if (settings.slotMode && uri) {
|
|
|
|
addCredentialSlot.updateForm(uri)
|
|
|
|
device.getSlotStatus(addCredentialSlot.open)
|
|
|
|
} else if (!settings.slotMode && uri) {
|
|
|
|
addCredential.updateForm(uri)
|
|
|
|
addCredential.open()
|
|
|
|
} else {
|
|
|
|
noQr.open()
|
|
|
|
}
|
|
|
|
})
|
|
|
|
}
|
2017-01-27 15:55:38 +03:00
|
|
|
}
|