From ff03e5fc04c0cd52cd3edd2f51c3816e39b27804 Mon Sep 17 00:00:00 2001 From: varjolintu Date: Mon, 22 Jan 2018 15:20:55 +0200 Subject: [PATCH] Version 0.5.0 --- CHANGELOG | 12 + keepassxc-browser/background/browserAction.js | 370 +-- keepassxc-browser/background/event.js | 398 +-- keepassxc-browser/background/httpauth.js | 110 +- keepassxc-browser/background/init.js | 138 +- keepassxc-browser/background/keepass.js | 1512 ++++----- keepassxc-browser/background/page.js | 188 +- keepassxc-browser/global.js | 8 +- keepassxc-browser/keepassxc-browser.css | 232 +- keepassxc-browser/keepassxc-browser.js | 2694 +++++++++-------- keepassxc-browser/manifest.json | 19 +- keepassxc-browser/options/options.css | 111 +- keepassxc-browser/options/options.html | 579 ++-- keepassxc-browser/options/options.js | 386 +-- keepassxc-browser/popups/popup.css | 90 +- keepassxc-browser/popups/popup.html | 178 +- keepassxc-browser/popups/popup.js | 153 +- keepassxc-browser/popups/popup_functions.js | 42 +- keepassxc-browser/popups/popup_httpauth.html | 64 +- keepassxc-browser/popups/popup_httpauth.js | 62 +- keepassxc-browser/popups/popup_login.html | 64 +- keepassxc-browser/popups/popup_login.js | 64 +- .../popups/popup_multiple-fields.html | 64 +- keepassxc-browser/popups/popup_remember.html | 94 +- keepassxc-browser/popups/popup_remember.js | 184 +- 25 files changed, 3955 insertions(+), 3861 deletions(-) diff --git a/CHANGELOG b/CHANGELOG index dbee36f..5e8a773 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -1,3 +1,15 @@ +0.5.0 (22-01-2018) +========================= +- Fixed an error when filling only a password +- Credential retrieval is allowed when only one input field is visible (TOTP) +- Asynchronous receiveCredentialsIfNecessary() +- Send triggerUnlock with request that need to popup KeePassXC to front +- Added verifyDatabaseResponse to get_databasehash +- Renamed keepassxc-browser to KeePassXC-Browser +- Removed duplicate retrieve_credentials requests +- Fixed identation +- Added support for credential filling through user interaction when database is closed + 0.4.8 (06-01-2018) ========================= - Changed native messaging host name to org.keepassxc.keepassxc_browser diff --git a/keepassxc-browser/background/browserAction.js b/keepassxc-browser/background/browserAction.js index b2cdd80..c2a78fc 100644 --- a/keepassxc-browser/background/browserAction.js +++ b/keepassxc-browser/background/browserAction.js @@ -5,261 +5,261 @@ const BLINK_TIMEOUT_REDIRECT_THRESHOLD_TIME_DEFAULT = -1; const BLINK_TIMEOUT_REDIRECT_COUNT_DEFAULT = 1; browserAction.show = function(callback, tab) { - let data = {}; - if (!page.tabs[tab.id] || page.tabs[tab.id].stack.length == 0) { - browserAction.showDefault(callback, tab); - return; - } - else { - data = page.tabs[tab.id].stack[page.tabs[tab.id].stack.length - 1]; - } + let data = {}; + if (!page.tabs[tab.id] || page.tabs[tab.id].stack.length == 0) { + browserAction.showDefault(callback, tab); + return; + } + else { + data = page.tabs[tab.id].stack[page.tabs[tab.id].stack.length - 1]; + } - browser.browserAction.setIcon({ - tabId: tab.id, - path: '/icons/19x19/' + browserAction.generateIconName(data.iconType, data.icon) - }); + browser.browserAction.setIcon({ + tabId: tab.id, + path: '/icons/19x19/' + browserAction.generateIconName(data.iconType, data.icon) + }); - if (data.popup) { - browser.browserAction.setPopup({ - tabId: tab.id, - popup: 'popups/' + data.popup - }); - } + if (data.popup) { + browser.browserAction.setPopup({ + tabId: tab.id, + popup: 'popups/' + data.popup + }); + } }; browserAction.update = function(interval) { - if (!page.tabs[page.currentTabId] || page.tabs[page.currentTabId].stack.length === 0) { - return; - } + if (!page.tabs[page.currentTabId] || page.tabs[page.currentTabId].stack.length === 0) { + return; + } - let data = page.tabs[page.currentTabId].stack[page.tabs[page.currentTabId].stack.length - 1]; + let data = page.tabs[page.currentTabId].stack[page.tabs[page.currentTabId].stack.length - 1]; if (typeof data.visibleForMilliSeconds !== 'undefined') { - if (data.visibleForMilliSeconds <= 0) { - browserAction.stackPop(page.currentTabId); - browserAction.show(null, {'id': page.currentTabId}); - page.clearCredentials(page.currentTabId); + if (data.visibleForMilliSeconds <= 0) { + browserAction.stackPop(page.currentTabId); + browserAction.show(null, {'id': page.currentTabId}); + page.clearCredentials(page.currentTabId); return; - } - data.visibleForMilliSeconds -= interval; - } + } + data.visibleForMilliSeconds -= interval; + } - if (data.intervalIcon) { - data.intervalIcon.counter += 1; - if (data.intervalIcon.counter < data.intervalIcon.max) { - return; - } + if (data.intervalIcon) { + data.intervalIcon.counter += 1; + if (data.intervalIcon.counter < data.intervalIcon.max) { + return; + } - data.intervalIcon.counter = 0; - data.intervalIcon.index += 1; + data.intervalIcon.counter = 0; + data.intervalIcon.index += 1; - if (data.intervalIcon.index > data.intervalIcon.icons.length - 1) { - data.intervalIcon.index = 0; - } + if (data.intervalIcon.index > data.intervalIcon.icons.length - 1) { + data.intervalIcon.index = 0; + } - browser.browserAction.setIcon({ - tabId: page.currentTabId, - path: '/icons/19x19/' + browserAction.generateIconName(null, data.intervalIcon.icons[data.intervalIcon.index]) - }); - } + browser.browserAction.setIcon({ + tabId: page.currentTabId, + path: '/icons/19x19/' + browserAction.generateIconName(null, data.intervalIcon.icons[data.intervalIcon.index]) + }); + } }; browserAction.showDefault = function(callback, tab) { - let stackData = { - level: 1, - iconType: 'normal', - popup: 'popup.html' - }; - keepass.isConfigured().then((response) => { - if (!response || keepass.isDatabaseClosed || !keepass.isKeePassXCAvailable || page.tabs[tab.id].errorMessage) { - stackData.iconType = 'cross'; - } + let stackData = { + level: 1, + iconType: 'normal', + popup: 'popup.html' + }; + keepass.isConfigured().then((response) => { + if (!response || keepass.isDatabaseClosed || !keepass.isKeePassXCAvailable || page.tabs[tab.id].errorMessage) { + stackData.iconType = 'cross'; + } - if (page.tabs[tab.id].loginList.length > 0) { - stackData.iconType = 'questionmark'; - stackData.popup = 'popup_login.html'; - } + if (page.tabs[tab.id].loginList.length > 0) { + stackData.iconType = 'questionmark'; + stackData.popup = 'popup_login.html'; + } - browserAction.stackUnshift(stackData, tab.id); - browserAction.show(null, tab); - }); + browserAction.stackUnshift(stackData, tab.id); + browserAction.show(null, tab); + }); }; browserAction.stackAdd = function(callback, tab, icon, popup, level, push, visibleForMilliSeconds, visibleForPageUpdates, redirectOffset, dontShow) { - const id = tab.id || page.currentTabId; + const id = tab.id || page.currentTabId; - if (!level) { - level = 1; - } + if (!level) { + level = 1; + } - let stackData = { - level: level, - icon: icon - }; + let stackData = { + level: level, + icon: icon + }; - if (popup) { - stackData.popup = popup; - } + if (popup) { + stackData.popup = popup; + } - if (visibleForMilliSeconds) { - stackData.visibleForMilliSeconds = visibleForMilliSeconds; - } + if (visibleForMilliSeconds) { + stackData.visibleForMilliSeconds = visibleForMilliSeconds; + } - if (visibleForPageUpdates) { - stackData.visibleForPageUpdates = visibleForPageUpdates; - } + if (visibleForPageUpdates) { + stackData.visibleForPageUpdates = visibleForPageUpdates; + } - if (redirectOffset) { - stackData.redirectOffset = redirectOffset; - } + if (redirectOffset) { + stackData.redirectOffset = redirectOffset; + } - if (push) { - browserAction.stackPush(stackData, id); - } - else { - browserAction.stackUnshift(stackData, id); - } + if (push) { + browserAction.stackPush(stackData, id); + } + else { + browserAction.stackUnshift(stackData, id); + } - if (!dontShow) { - browserAction.show(null, {'id': id}); - } + if (!dontShow) { + browserAction.show(null, {'id': id}); + } }; browserAction.removeLevelFromStack = function(callback, tab, level, type, dontShow) { - if (!page.tabs[tab.id]) { - return; - } + if (!page.tabs[tab.id]) { + return; + } - if (!type) { - type = '<='; - } + if (!type) { + type = '<='; + } - let newStack = []; - for (const i of page.tabs[tab.id].stack) { - if ( - (type == '<' && i.level >= level) || - (type == '<=' && i.level > level) || - (type == '=' && i.level != level) || - (type == '==' && i.level != level) || - (type == '!=' && i.level == level) || - (type == '>' && i.level <= level) || - (type == '>=' && i.level < level) - ) { - newStack.push(i); - } - } + let newStack = []; + for (const i of page.tabs[tab.id].stack) { + if ( + (type == '<' && i.level >= level) || + (type == '<=' && i.level > level) || + (type == '=' && i.level != level) || + (type == '==' && i.level != level) || + (type == '!=' && i.level == level) || + (type == '>' && i.level <= level) || + (type == '>=' && i.level < level) + ) { + newStack.push(i); + } + } - page.tabs[tab.id].stack = newStack; + page.tabs[tab.id].stack = newStack; - if (!dontShow) { - browserAction.show(callback, tab); - } + if (!dontShow) { + browserAction.show(callback, tab); + } }; browserAction.stackPop = function(tabId) { - const id = tabId || page.currentTabId; - page.tabs[id].stack.pop(); + const id = tabId || page.currentTabId; + page.tabs[id].stack.pop(); }; browserAction.stackPush = function(data, tabId) { - const id = tabId || page.currentTabId; - browserAction.removeLevelFromStack(null, {'id': id}, data.level, '<=', true); - page.tabs[id].stack.push(data); + const id = tabId || page.currentTabId; + browserAction.removeLevelFromStack(null, {'id': id}, data.level, '<=', true); + page.tabs[id].stack.push(data); }; browserAction.stackUnshift = function(data, tabId) { - const id = tabId || page.currentTabId; - browserAction.removeLevelFromStack(null, {'id': id}, data.level, '<=', true); - page.tabs[id].stack.unshift(data); + const id = tabId || page.currentTabId; + browserAction.removeLevelFromStack(null, {'id': id}, data.level, '<=', true); + page.tabs[id].stack.unshift(data); }; browserAction.removeRememberPopup = function(callback, tab, removeImmediately) { - if (!page.tabs[tab.id]) { - return; - } + if (!page.tabs[tab.id]) { + return; + } - if( page.tabs[tab.id].stack.length == 0) { + if( page.tabs[tab.id].stack.length == 0) { page.clearCredentials(tab.id); - return; - } - const data = page.tabs[tab.id].stack[page.tabs[tab.id].stack.length - 1]; + return; + } + const data = page.tabs[tab.id].stack[page.tabs[tab.id].stack.length - 1]; if (removeImmediately || !isNaN(data.visibleForPageUpdates)) { - const currentMS = Date.now(); - if (removeImmediately || (data.visibleForPageUpdates <= 0 && data.redirectOffset > 0)) { - browserAction.stackPop(tab.id); - browserAction.show(null, {"id": tab.id}); - page.clearCredentials(tab.id); - return; - } - else if (!isNaN(data.visibleForPageUpdates) && data.redirectOffset > 0 && currentMS >= data.redirectOffset) { - data.visibleForPageUpdates = data.visibleForPageUpdates - 1; - } + const currentMS = Date.now(); + if (removeImmediately || (data.visibleForPageUpdates <= 0 && data.redirectOffset > 0)) { + browserAction.stackPop(tab.id); + browserAction.show(null, {"id": tab.id}); + page.clearCredentials(tab.id); + return; + } + else if (!isNaN(data.visibleForPageUpdates) && data.redirectOffset > 0 && currentMS >= data.redirectOffset) { + data.visibleForPageUpdates = data.visibleForPageUpdates - 1; + } } }; browserAction.setRememberPopup = function(tabId, username, password, url, usernameExists, credentialsList) { - const settings = typeof(localStorage.settings) === 'undefined' ? {} : JSON.parse(localStorage.settings); - const id = tabId || page.currentTabId; - let timeoutMinMillis = Number(getValueOrDefault(settings, 'blinkMinTimeout', BLINK_TIMEOUT_REDIRECT_THRESHOLD_TIME_DEFAULT, 0)); + const settings = typeof(localStorage.settings) === 'undefined' ? {} : JSON.parse(localStorage.settings); + const id = tabId || page.currentTabId; + let timeoutMinMillis = Number(getValueOrDefault(settings, 'blinkMinTimeout', BLINK_TIMEOUT_REDIRECT_THRESHOLD_TIME_DEFAULT, 0)); - if (timeoutMinMillis > 0) { - timeoutMinMillis += Date.now(); - } + if (timeoutMinMillis > 0) { + timeoutMinMillis += Date.now(); + } - const blinkTimeout = getValueOrDefault(settings, 'blinkTimeout', BLINK_TIMEOUT_DEFAULT, 0); - const pageUpdateAllowance = getValueOrDefault(settings, 'allowedRedirect', BLINK_TIMEOUT_REDIRECT_COUNT_DEFAULT, 0); + const blinkTimeout = getValueOrDefault(settings, 'blinkTimeout', BLINK_TIMEOUT_DEFAULT, 0); + const pageUpdateAllowance = getValueOrDefault(settings, 'allowedRedirect', BLINK_TIMEOUT_REDIRECT_COUNT_DEFAULT, 0); - const stackData = { + const stackData = { visibleForMilliSeconds: blinkTimeout, - visibleForPageUpdates: pageUpdateAllowance, - redirectOffset: timeoutMinMillis, - level: 10, - intervalIcon: { - index: 0, - counter: 0, - max: 2, - icons: ['icon_remember_red_background_19x19.png', 'icon_remember_red_lock_19x19.png'] - }, - icon: 'icon_remember_red_background_19x19.png', - popup: 'popup_remember.html' - }; + visibleForPageUpdates: pageUpdateAllowance, + redirectOffset: timeoutMinMillis, + level: 10, + intervalIcon: { + index: 0, + counter: 0, + max: 2, + icons: ['icon_remember_red_background_19x19.png', 'icon_remember_red_lock_19x19.png'] + }, + icon: 'icon_remember_red_background_19x19.png', + popup: 'popup_remember.html' + }; - browserAction.stackPush(stackData, id); + browserAction.stackPush(stackData, id); - page.tabs[id].credentials = { - username: username, - password: password, - url: url, - usernameExists: usernameExists, - list: credentialsList - }; + page.tabs[id].credentials = { + username: username, + password: password, + url: url, + usernameExists: usernameExists, + list: credentialsList + }; - browserAction.show(null, {'id': id}); + browserAction.show(null, {'id': id}); }; function getValueOrDefault(settings, key, defaultVal, min) { - try { - let val = settings[key]; - if (isNaN(val) || val < min) { - val = defaultVal; - } - return val; - } catch(e) { - return defaultVal; - } + try { + let val = settings[key]; + if (isNaN(val) || val < min) { + val = defaultVal; + } + return val; + } catch(e) { + return defaultVal; + } } browserAction.generateIconName = function(iconType, icon) { - if (icon) { - return icon; - } + if (icon) { + return icon; + } - let name = 'icon_'; - name += (keepass.keePassXCUpdateAvailable()) ? 'new_' : ''; - name += (!iconType || iconType === 'normal') ? 'normal' : iconType; - name += '_19x19.png'; + let name = 'icon_'; + name += (keepass.keePassXCUpdateAvailable()) ? 'new_' : ''; + name += (!iconType || iconType === 'normal') ? 'normal' : iconType; + name += '_19x19.png'; - return name; + return name; }; diff --git a/keepassxc-browser/background/event.js b/keepassxc-browser/background/event.js index a2c9f90..f87ad10 100644 --- a/keepassxc-browser/background/event.js +++ b/keepassxc-browser/background/event.js @@ -1,21 +1,21 @@ const kpxcEvent = {}; kpxcEvent.onMessage = function(request, sender, callback) { - if (request.action in kpxcEvent.messageHandlers) { - //console.log('onMessage(' + request.action + ') for #' + sender.tab.id); - if (!sender.hasOwnProperty('tab') || sender.tab.id < 1) { - sender.tab = {}; - sender.tab.id = page.currentTabId; - } + if (request.action in kpxcEvent.messageHandlers) { + //console.log('onMessage(' + request.action + ') for #' + sender.tab.id); + if (!sender.hasOwnProperty('tab') || sender.tab.id < 1) { + sender.tab = {}; + sender.tab.id = page.currentTabId; + } - kpxcEvent.invoke(kpxcEvent.messageHandlers[request.action], callback, sender.tab.id, request.args); + kpxcEvent.invoke(kpxcEvent.messageHandlers[request.action], callback, sender.tab.id, request.args); - // onMessage closes channel for callback automatically - // if this method does not return true - if (callback) { - return true; - } - } + // onMessage closes channel for callback automatically + // if this method does not return true + if (callback !== undefined) { + return true; + } + } }; /** @@ -30,272 +30,272 @@ kpxcEvent.onMessage = function(request, sender, callback) { * @returns null (asynchronous) */ kpxcEvent.invoke = function(handler, callback, senderTabId, args, secondTime) { - if (senderTabId < 1) { - return; - } + if (senderTabId < 1) { + return; + } - if (!page.tabs[senderTabId]) { - page.createTabEntry(senderTabId); - } + if (!page.tabs[senderTabId]) { + page.createTabEntry(senderTabId); + } - // remove information from no longer existing tabs - page.removePageInformationFromNotExistingTabs(); + // remove information from no longer existing tabs + page.removePageInformationFromNotExistingTabs(); - browser.tabs.get(senderTabId).then((tab) => { - if (!tab) { - return; - } + browser.tabs.get(senderTabId).then((tab) => { + if (!tab) { + return; + } - if (!tab.url) { - // Issue 6877: tab URL is not set directly after you opened a window - // using window.open() - if (!secondTime) { - window.setTimeout(function() { - kpxcEvent.invoke(handler, callback, senderTabId, args, true); - }, 250); - } - return; - } + if (!tab.url) { + // Issue 6877: tab URL is not set directly after you opened a window + // using window.open() + if (!secondTime) { + window.setTimeout(function() { + kpxcEvent.invoke(handler, callback, senderTabId, args, true); + }, 250); + } + return; + } - if (!page.tabs[tab.id]) { - page.createTabEntry(tab.id); - } + if (!page.tabs[tab.id]) { + page.createTabEntry(tab.id); + } - args = args || []; + args = args || []; - args.unshift(tab); - args.unshift(callback); + args.unshift(tab); + args.unshift(callback); - if (handler) { - handler.apply(this, args); - } - else { - console.log('undefined handler for tab ' + tab.id); - } - }).catch((e) => {console.log(e);}); + if (handler) { + handler.apply(this, args); + } + else { + console.log('undefined handler for tab ' + tab.id); + } + }).catch((e) => {console.log(e);}); }; kpxcEvent.onShowAlert = function(callback, tab, message) { - if (page.settings.supressAlerts) { console.log(message); } - else { alert(message); } + if (page.settings.supressAlerts) { console.log(message); } + else { alert(message); } }; kpxcEvent.showStatus = function(configured, tab, callback) { - let keyId = null; - if (configured) { - keyId = keepass.keyRing[keepass.databaseHash].id; - } + let keyId = null; + if (configured) { + keyId = keepass.keyRing[keepass.databaseHash].id; + } - browserAction.showDefault(null, tab); - const errorMessage = page.tabs[tab.id].errorMessage; - callback({ - identifier: keyId, - configured: configured, - databaseClosed: keepass.isDatabaseClosed, - keePassXCAvailable: keepass.isKeePassXCAvailable, - encryptionKeyUnrecognized: keepass.isEncryptionKeyUnrecognized, - associated: keepass.isAssociated(), - error: errorMessage ? errorMessage : null - }); + browserAction.showDefault(null, tab); + const errorMessage = page.tabs[tab.id].errorMessage; + callback({ + identifier: keyId, + configured: configured, + databaseClosed: keepass.isDatabaseClosed, + keePassXCAvailable: keepass.isKeePassXCAvailable, + encryptionKeyUnrecognized: keepass.isEncryptionKeyUnrecognized, + associated: keepass.isAssociated(), + error: errorMessage ? errorMessage : null + }); }; kpxcEvent.onLoadSettings = function(callback, tab) { - page.initSettings().then((settings) => { - callback(settings); - }, (err) => { - console.log('error loading settings: ' + err); - }); + page.initSettings().then((settings) => { + callback(settings); + }, (err) => { + console.log('error loading settings: ' + err); + }); }; kpxcEvent.onLoadKeyRing = function(callback, tab) { - browser.storage.local.get({'keyRing': {}}).then(function(item) { - keepass.keyRing = item.keyRing; - if (keepass.isAssociated() && !keepass.keyRing[keepass.associated.hash]) { - keepass.associated = { - "value": false, - "hash": null - }; - } - callback(item.keyRing); - }, (err) => { - console.log('error loading keyRing: ' + err); - }); + browser.storage.local.get({'keyRing': {}}).then(function(item) { + keepass.keyRing = item.keyRing; + if (keepass.isAssociated() && !keepass.keyRing[keepass.associated.hash]) { + keepass.associated = { + "value": false, + "hash": null + }; + } + callback(item.keyRing); + }, (err) => { + console.log('error loading keyRing: ' + err); + }); }; kpxcEvent.onSaveSettings = function(callback, tab, settings) { - browser.storage.local.set({'settings': settings}).then(function() { - kpxcEvent.onLoadSettings(callback, tab); - }); + browser.storage.local.set({'settings': settings}).then(function() { + kpxcEvent.onLoadSettings(callback, tab); + }); }; -kpxcEvent.onGetStatus = function(callback, tab, internalPoll = false) { - // When internalPoll is true the event is triggered from content script in intervals -> don't poll KeePassXC - if (!internalPoll) { - keepass.testAssociation((response) => { - if (!response) { - kpxcEvent.showStatus(false, tab, callback); - return; - } +kpxcEvent.onGetStatus = function(callback, tab, internalPoll = false, triggerUnlock = false) { + // When internalPoll is true the event is triggered from content script in intervals -> don't poll KeePassXC + if (!internalPoll) { + keepass.testAssociation((response) => { + if (!response) { + kpxcEvent.showStatus(false, tab, callback); + return; + } - keepass.isConfigured().then((configured) => { - kpxcEvent.showStatus(configured, tab, callback); - }); - }, tab, true); - } else { - keepass.isConfigured().then((configured) => { - kpxcEvent.showStatus(configured, tab, callback); - }); - } + keepass.isConfigured().then((configured) => { + kpxcEvent.showStatus(configured, tab, callback); + }); + }, tab, true, triggerUnlock); + } else { + keepass.isConfigured().then((configured) => { + kpxcEvent.showStatus(configured, tab, callback); + }); + } }; kpxcEvent.onReconnect = function(callback, tab) { - keepass.connectToNative(); + keepass.connectToNative(); - // Add a small timeout after reconnecting. Just to make sure. It's not pretty, I know :( - setTimeout(() => { - keepass.generateNewKeyPair(); - keepass.changePublicKeys(tab).then((pkRes) => { - keepass.getDatabaseHash((gdRes) => { - if (gdRes) { - keepass.testAssociation((response) => { - keepass.isConfigured().then((configured) => { - kpxcEvent.showStatus(configured, tab, callback); - }).catch((e) => {console.log(e);}); - }, tab); - } - }, null); - }); - }, 2000); + // Add a small timeout after reconnecting. Just to make sure. It's not pretty, I know :( + setTimeout(() => { + keepass.generateNewKeyPair(); + keepass.changePublicKeys(tab).then((pkRes) => { + keepass.getDatabaseHash((gdRes) => { + if (gdRes) { + keepass.testAssociation((response) => { + keepass.isConfigured().then((configured) => { + kpxcEvent.showStatus(configured, tab, callback); + }).catch((e) => {console.log(e);}); + }, tab); + } + }, null); + }); + }, 2000); }; kpxcEvent.lockDatabase = function(callback, tab) { - keepass.lockDatabase(tab).then((response => { - kpxcEvent.showStatus(true, tab, callback); - })); + keepass.lockDatabase(tab).then((response => { + kpxcEvent.showStatus(true, tab, callback); + })); }; kpxcEvent.onPopStack = function(callback, tab) { - browserAction.stackPop(tab.id); - browserAction.show(null, tab); + browserAction.stackPop(tab.id); + browserAction.show(null, tab); }; kpxcEvent.onGetTabInformation = function(callback, tab) { - const id = tab.id || page.currentTabId; - callback(page.tabs[id]); + const id = tab.id || page.currentTabId; + callback(page.tabs[id]); }; kpxcEvent.onGetConnectedDatabase = function(callback, tab) { - callback({ - count: Object.keys(keepass.keyRing).length, - identifier: (keepass.keyRing[keepass.associated.hash]) ? keepass.keyRing[keepass.associated.hash].id : null - }); + callback({ + count: Object.keys(keepass.keyRing).length, + identifier: (keepass.keyRing[keepass.associated.hash]) ? keepass.keyRing[keepass.associated.hash].id : null + }); }; kpxcEvent.onGetKeePassXCVersions = function(callback, tab) { - if(keepass.currentKeePassXC.version == 0) { - keepass.getDatabaseHash((res) => { - callback({"current": keepass.currentKeePassXC.version, "latest": keepass.currentKeePassXC.version}); - }, tab); - } else { - callback({"current": keepass.currentKeePassXC.version, "latest": keepass.currentKeePassXC.version}); + if(keepass.currentKeePassXC.version == 0) { + keepass.getDatabaseHash((res) => { + callback({"current": keepass.currentKeePassXC.version, "latest": keepass.currentKeePassXC.version}); + }, tab); + } else { + callback({"current": keepass.currentKeePassXC.version, "latest": keepass.currentKeePassXC.version}); } }; kpxcEvent.onCheckUpdateKeePassXC = function(callback, tab) { - keepass.checkForNewKeePassXCVersion(); - callback({current: keepass.currentKeePassXC.version, latest: keepass.latestKeePassXC.version}); + keepass.checkForNewKeePassXCVersion(); + callback({current: keepass.currentKeePassXC.version, latest: keepass.latestKeePassXC.version}); }; kpxcEvent.onUpdateAvailableKeePassXC = function(callback, tab) { - callback(keepass.keePassXCUpdateAvailable()); + callback(keepass.keePassXCUpdateAvailable()); }; kpxcEvent.onRemoveCredentialsFromTabInformation = function(callback, tab) { - const id = tab.id || page.currentTabId; - page.clearCredentials(id); + const id = tab.id || page.currentTabId; + page.clearCredentials(id); }; kpxcEvent.onSetRememberPopup = function(callback, tab, username, password, url, usernameExists, credentialsList) { - browserAction.setRememberPopup(tab.id, username, password, url, usernameExists, credentialsList); + browserAction.setRememberPopup(tab.id, username, password, url, usernameExists, credentialsList); }; kpxcEvent.onLoginPopup = function(callback, tab, logins) { - let stackData = { - level: 1, - iconType: 'questionmark', - popup: 'popup_login.html' - }; - browserAction.stackUnshift(stackData, tab.id); - page.tabs[tab.id].loginList = logins; - browserAction.show(null, tab); + let stackData = { + level: 1, + iconType: 'questionmark', + popup: 'popup_login.html' + }; + browserAction.stackUnshift(stackData, tab.id); + page.tabs[tab.id].loginList = logins; + browserAction.show(null, tab); }; kpxcEvent.initHttpAuth = function(callback) { - httpAuth.init(); - callback(); + httpAuth.init(); + callback(); } kpxcEvent.onHTTPAuthPopup = function(callback, tab, data) { - let stackData = { - level: 1, - iconType: 'questionmark', - popup: 'popup_httpauth.html' - }; - browserAction.stackUnshift(stackData, tab.id); - page.tabs[tab.id].loginList = data; - browserAction.show(null, tab); + let stackData = { + level: 1, + iconType: 'questionmark', + popup: 'popup_httpauth.html' + }; + browserAction.stackUnshift(stackData, tab.id); + page.tabs[tab.id].loginList = data; + browserAction.show(null, tab); }; kpxcEvent.onMultipleFieldsPopup = function(callback, tab) { - let stackData = { - level: 1, - iconType: 'normal', - popup: 'popup_multiple-fields.html' - }; - browserAction.stackUnshift(stackData, tab.id); - browserAction.show(null, tab); + let stackData = { + level: 1, + iconType: 'normal', + popup: 'popup_multiple-fields.html' + }; + browserAction.stackUnshift(stackData, tab.id); + browserAction.show(null, tab); }; kpxcEvent.pageClearLogins = function(callback, tab) { - page.clearLogins(tab.id); - callback(); + page.clearLogins(tab.id); + callback(); }; kpxcEvent.oldDatabaseHash = 'no-hash'; kpxcEvent.checkDatabaseHash = function(callback, tab) { - keepass.checkDatabaseHash((response) => { - callback({old: kpxcEvent.oldDatabaseHash, new: response}); - kpxcEvent.oldDatabaseHash = response; - }); + keepass.checkDatabaseHash((response) => { + callback({old: kpxcEvent.oldDatabaseHash, new: response}); + kpxcEvent.oldDatabaseHash = response; + }); }; // all methods named in this object have to be declared BEFORE this! kpxcEvent.messageHandlers = { - 'add_credentials': keepass.addCredentials, - 'alert': kpxcEvent.onShowAlert, - 'associate': keepass.associate, - 'check_update_keepassxc': kpxcEvent.onCheckUpdateKeePassXC, - 'get_connected_database': kpxcEvent.onGetConnectedDatabase, - 'get_keepassxc_versions': kpxcEvent.onGetKeePassXCVersions, - 'get_status': kpxcEvent.onGetStatus, - 'get_tab_information': kpxcEvent.onGetTabInformation, - 'init_http_auth': kpxcEvent.initHttpAuth, - 'load_keyring': kpxcEvent.onLoadKeyRing, - 'load_settings': kpxcEvent.onLoadSettings, - 'page_clear_logins': kpxcEvent.pageClearLogins, - 'pop_stack': kpxcEvent.onPopStack, - 'popup_login': kpxcEvent.onLoginPopup, - 'popup_multiple-fields': kpxcEvent.onMultipleFieldsPopup, - 'remove_credentials_from_tab_information': kpxcEvent.onRemoveCredentialsFromTabInformation, - 'retrieve_credentials': keepass.retrieveCredentials, - 'show_default_browseraction': browserAction.showDefault, - 'update_credentials': keepass.updateCredentials, - 'save_settings': kpxcEvent.onSaveSettings, - 'set_remember_credentials': kpxcEvent.onSetRememberPopup, - 'stack_add': browserAction.stackAdd, - 'update_available_keepassxc': kpxcEvent.onUpdateAvailableKeePassXC, - 'generate_password': keepass.generatePassword, - 'reconnect': kpxcEvent.onReconnect, - 'lock-database': kpxcEvent.lockDatabase, - 'check_databasehash': kpxcEvent.checkDatabaseHash + 'add_credentials': keepass.addCredentials, + 'alert': kpxcEvent.onShowAlert, + 'associate': keepass.associate, + 'check_databasehash': kpxcEvent.checkDatabaseHash, + 'check_update_keepassxc': kpxcEvent.onCheckUpdateKeePassXC, + 'generate_password': keepass.generatePassword, + 'get_connected_database': kpxcEvent.onGetConnectedDatabase, + 'get_keepassxc_versions': kpxcEvent.onGetKeePassXCVersions, + 'get_status': kpxcEvent.onGetStatus, + 'get_tab_information': kpxcEvent.onGetTabInformation, + 'init_http_auth': kpxcEvent.initHttpAuth, + 'load_keyring': kpxcEvent.onLoadKeyRing, + 'load_settings': kpxcEvent.onLoadSettings, + 'lock-database': kpxcEvent.lockDatabase, + 'page_clear_logins': kpxcEvent.pageClearLogins, + 'pop_stack': kpxcEvent.onPopStack, + 'popup_login': kpxcEvent.onLoginPopup, + 'popup_multiple-fields': kpxcEvent.onMultipleFieldsPopup, + 'reconnect': kpxcEvent.onReconnect, + 'remove_credentials_from_tab_information': kpxcEvent.onRemoveCredentialsFromTabInformation, + 'retrieve_credentials': keepass.retrieveCredentials, + 'show_default_browseraction': browserAction.showDefault, + 'update_credentials': keepass.updateCredentials, + 'save_settings': kpxcEvent.onSaveSettings, + 'set_remember_credentials': kpxcEvent.onSetRememberPopup, + 'stack_add': browserAction.stackAdd, + 'update_available_keepassxc': kpxcEvent.onUpdateAvailableKeePassXC }; diff --git a/keepassxc-browser/background/httpauth.js b/keepassxc-browser/background/httpauth.js index 7a53fdd..d6eede0 100644 --- a/keepassxc-browser/background/httpauth.js +++ b/keepassxc-browser/background/httpauth.js @@ -4,82 +4,82 @@ httpAuth.requests = []; httpAuth.pendingCallbacks = []; httpAuth.init = function() { - let handleReq = httpAuth.handleRequestPromise; - let reqType = 'blocking'; + let handleReq = httpAuth.handleRequestPromise; + let reqType = 'blocking'; - if (!isFirefox()) { - handleReq = httpAuth.handleRequestCallback; - reqType = 'asyncBlocking'; - } + if (!isFirefox()) { + handleReq = httpAuth.handleRequestCallback; + reqType = 'asyncBlocking'; + } - if (browser.webRequest.onAuthRequired.hasListener(handleReq)) { - browser.webRequest.onAuthRequired.removeListener(handleReq); - browser.webRequest.onCompleted.removeListener(httpAuth.requestCompleted); - browser.webRequest.onErrorOccurred.removeListener(httpAuth.requestCompleted); - } + if (browser.webRequest.onAuthRequired.hasListener(handleReq)) { + browser.webRequest.onAuthRequired.removeListener(handleReq); + browser.webRequest.onCompleted.removeListener(httpAuth.requestCompleted); + browser.webRequest.onErrorOccurred.removeListener(httpAuth.requestCompleted); + } - // only intercept http auth requests if the option is turned on. - if (page.settings.autoFillAndSend) { - const opts = { urls: [''] }; + // only intercept http auth requests if the option is turned on. + if (page.settings.autoFillAndSend) { + const opts = { urls: [''] }; - browser.webRequest.onAuthRequired.addListener(handleReq, opts, [reqType]); - browser.webRequest.onCompleted.addListener(httpAuth.requestCompleted, opts); - browser.webRequest.onErrorOccurred.addListener(httpAuth.requestCompleted, opts); - } + browser.webRequest.onAuthRequired.addListener(handleReq, opts, [reqType]); + browser.webRequest.onCompleted.addListener(httpAuth.requestCompleted, opts); + browser.webRequest.onErrorOccurred.addListener(httpAuth.requestCompleted, opts); + } }; httpAuth.requestCompleted = function(details) { - let index = httpAuth.requests.indexOf(details.requestId); - if (index >= 0) { - httpAuth.requests.splice(index, 1); - } + let index = httpAuth.requests.indexOf(details.requestId); + if (index >= 0) { + httpAuth.requests.splice(index, 1); + } }; httpAuth.handleRequestPromise = function(details) { - return new Promise((resolve, reject) => { - httpAuth.processPendingCallbacks(details, resolve, reject); - }); + return new Promise((resolve, reject) => { + httpAuth.processPendingCallbacks(details, resolve, reject); + }); }; httpAuth.handleRequestCallback = function(details, callback) { - httpAuth.processPendingCallbacks(details, callback, callback); + httpAuth.processPendingCallbacks(details, callback, callback); }; httpAuth.processPendingCallbacks = function(details, resolve, reject) { - if (httpAuth.requests.indexOf(details.requestId) >= 0 || !page.tabs[details.tabId]) { - reject({}); - return; - } + if (httpAuth.requests.indexOf(details.requestId) >= 0 || !page.tabs[details.tabId]) { + reject({}); + return; + } - httpAuth.requests.push(details.requestId); + httpAuth.requests.push(details.requestId); - if (details.challenger) { - details.proxyUrl = details.challenger.host; - } + if (details.challenger) { + details.proxyUrl = details.challenger.host; + } - details.searchUrl = (details.isProxy && details.proxyUrl) ? details.proxyUrl : details.url; + details.searchUrl = (details.isProxy && details.proxyUrl) ? details.proxyUrl : details.url; - keepass.retrieveCredentials((logins) => { - httpAuth.loginOrShowCredentials(logins, details, resolve, reject); - }, { "id": details.tabId }, details.searchUrl, details.searchUrl, true); + keepass.retrieveCredentials((logins) => { + httpAuth.loginOrShowCredentials(logins, details, resolve, reject); + }, { "id": details.tabId }, details.searchUrl, details.searchUrl, true); }; httpAuth.loginOrShowCredentials = function(logins, details, resolve, reject) { - // at least one login found --> use first to login - if (logins.length > 0 && page.settings.autoFillAndSend) { - if (logins.length === 1) { - resolve({ - authCredentials: { - username: logins[0].login, - password: logins[0].password - } - }); - } else { - kpxcEvent.onHTTPAuthPopup(null, { 'id': details.tabId }, { 'logins': logins, 'url': details.searchUrl, 'resolve': resolve }); - } - } - // no logins found - else { - reject({}); - } + // at least one login found --> use first to login + if (logins.length > 0 && page.settings.autoFillAndSend) { + if (logins.length === 1) { + resolve({ + authCredentials: { + username: logins[0].login, + password: logins[0].password + } + }); + } else { + kpxcEvent.onHTTPAuthPopup(null, { 'id': details.tabId }, { 'logins': logins, 'url': details.searchUrl, 'resolve': resolve }); + } + } + // no logins found + else { + reject({}); + } }; diff --git a/keepassxc-browser/background/init.js b/keepassxc-browser/background/init.js index 4279020..4f6c6af 100644 --- a/keepassxc-browser/background/init.js +++ b/keepassxc-browser/background/init.js @@ -1,14 +1,14 @@ keepass.migrateKeyRing().then(() => { - page.initSettings().then(() => { - page.initOpenedTabs().then(() => { - httpAuth.init(); - keepass.connectToNative(); - keepass.generateNewKeyPair(); - keepass.changePublicKeys(null).then((pkRes) => { - keepass.getDatabaseHash((gdRes) => {}, null); - }); - }); - }); + page.initSettings().then(() => { + page.initOpenedTabs().then(() => { + httpAuth.init(); + keepass.connectToNative(); + keepass.generateNewKeyPair(); + keepass.changePublicKeys(null).then((pkRes) => { + keepass.getDatabaseHash((gdRes) => {}, null); + }); + }); + }); }); // Milliseconds for intervall (e.g. to update browserAction) @@ -21,13 +21,13 @@ const _interval = 250; * @param {object} tab */ browser.tabs.onCreated.addListener((tab) => { - if (tab.id > 0) { - //console.log('browser.tabs.onCreated(' + tab.id+ ')'); - if (tab.selected) { - page.currentTabId = tab.id; - kpxcEvent.invoke(page.switchTab, null, tab.id, []); - } - } + if (tab.id > 0) { + //console.log('browser.tabs.onCreated(' + tab.id+ ')'); + if (tab.selected) { + page.currentTabId = tab.id; + kpxcEvent.invoke(page.switchTab, null, tab.id, []); + } + } }); /** @@ -36,10 +36,10 @@ browser.tabs.onCreated.addListener((tab) => { * @param {object} removeInfo */ browser.tabs.onRemoved.addListener((tabId, removeInfo) => { - delete page.tabs[tabId]; - if (page.currentTabId === tabId) { - page.currentTabId = -1; - } + delete page.tabs[tabId]; + if (page.currentTabId === tabId) { + page.currentTabId = -1; + } }); /** @@ -48,19 +48,19 @@ browser.tabs.onRemoved.addListener((tabId, removeInfo) => { * @param {object} activeInfo */ browser.tabs.onActivated.addListener((activeInfo) => { - // remove possible credentials from old tab information + // remove possible credentials from old tab information page.clearCredentials(page.currentTabId, true); - browserAction.removeRememberPopup(null, {'id': page.currentTabId}, true); + browserAction.removeRememberPopup(null, {'id': page.currentTabId}, true); - browser.tabs.get(activeInfo.tabId).then((info) => { - if (info && info.id) { - page.currentTabId = info.id; - if (info.status === 'complete') { - //console.log('kpxcEvent.invoke(page.switchTab, null, '+info.id + ', []);'); - kpxcEvent.invoke(page.switchTab, null, info.id, []); - } - } - }); + browser.tabs.get(activeInfo.tabId).then((info) => { + if (info && info.id) { + page.currentTabId = info.id; + if (info.status === 'complete') { + //console.log('kpxcEvent.invoke(page.switchTab, null, '+info.id + ', []);'); + kpxcEvent.invoke(page.switchTab, null, info.id, []); + } + } + }); }); /** @@ -69,60 +69,68 @@ browser.tabs.onActivated.addListener((activeInfo) => { * @param {object} changeInfo */ browser.tabs.onUpdated.addListener((tabId, changeInfo, tab) => { - if (changeInfo.status === 'complete') { - kpxcEvent.invoke(browserAction.removeRememberPopup, null, tabId, []); - } + if (changeInfo.status === 'complete') { + kpxcEvent.invoke(browserAction.removeRememberPopup, null, tabId, []); + } }); browser.runtime.onMessage.addListener(kpxcEvent.onMessage); const contextMenuItems = [ - {title: 'Fill User + Pass', action: 'fill_user_pass'}, - {title: 'Fill Pass Only', action: 'fill_pass_only'}, - {title: 'Fill TOTP', action: 'fill_totp'}, - {title: 'Show Password Generator Icons', action: 'activate_password_generator'}, - {title: 'Save credentials', action: 'remember_credentials'} + {title: 'Fill User + Pass', action: 'fill_user_pass'}, + {title: 'Fill Pass Only', action: 'fill_pass_only'}, + {title: 'Fill TOTP', action: 'fill_totp'}, + {title: 'Show Password Generator Icons', action: 'activate_password_generator'}, + {title: 'Save credentials', action: 'remember_credentials'} ]; let menuContexts = ['editable']; if (isFirefox()) { - menuContexts.push('password'); + menuContexts.push('password'); } // Create context menu items for (const item of contextMenuItems) { - browser.contextMenus.create({ - title: item.title, - contexts: menuContexts, - onclick: (info, tab) => { - browser.tabs.sendMessage(tab.id, { - action: item.action - }).catch((e) => {console.log(e);}); - } - }); + browser.contextMenus.create({ + title: item.title, + contexts: menuContexts, + onclick: (info, tab) => { + browser.tabs.sendMessage(tab.id, { + action: item.action + }).catch((e) => {console.log(e);}); + } + }); } // Listen for keyboard shortcuts specified by user browser.commands.onCommand.addListener((command) => { - if (command === 'fill-username-password') { - browser.tabs.query({ active: true, currentWindow: true }).then((tabs) => { - if (tabs.length) { - browser.tabs.sendMessage(tabs[0].id, { action: 'fill_user_pass' }); - } - }); - } + if (command === 'fill-username-password') { + browser.tabs.query({ active: true, currentWindow: true }).then((tabs) => { + if (tabs.length) { + browser.tabs.sendMessage(tabs[0].id, { action: 'fill_user_pass' }); + } + }); + } - if (command === 'fill-password') { - browser.tabs.query({ active: true, currentWindow: true }).then((tabs) => { - if (tabs.length) { - browser.tabs.sendMessage(tabs[0].id, { action: 'fill_pass_only' }); - } - }); - } + if (command === 'fill-password') { + browser.tabs.query({ active: true, currentWindow: true }).then((tabs) => { + if (tabs.length) { + browser.tabs.sendMessage(tabs[0].id, { action: 'fill_pass_only' }); + } + }); + } + + if (command === 'fill-totp') { + browser.tabs.query({ active: true, currentWindow: true }).then((tabs) => { + if (tabs.length) { + browser.tabs.sendMessage(tabs[0].id, { action: 'fill_totp' }); + } + }); + } }); // Interval which updates the browserAction (e.g. blinking icon) window.setInterval(function() { - browserAction.update(_interval); + browserAction.update(_interval); }, _interval); diff --git a/keepassxc-browser/background/keepass.js b/keepassxc-browser/background/keepass.js index 3ab6fd5..95fcb00 100644 --- a/keepassxc-browser/background/keepass.js +++ b/keepassxc-browser/background/keepass.js @@ -11,7 +11,7 @@ keepass.isDatabaseClosed = false; keepass.isKeePassXCAvailable = false; keepass.isEncryptionKeyUnrecognized = false; keepass.currentKeePassXC = {'version': 0, 'versionParsed': 0}; -keepass.requiredKeePassXC = 224; // TODO: Change to 230 when the official release is done +keepass.requiredKeePassXC = 220; keepass.nativeHostName = 'org.keepassxc.keepassxc_browser'; keepass.nativePort = null; keepass.keySize = 24; @@ -24,941 +24,973 @@ keepass.messageTimeout = 500; // milliseconds keepass.nonce = nacl.util.encodeBase64(nacl.randomBytes(keepass.keySize)); const kpActions = { - SET_LOGIN: 'set-login', - GET_LOGINS: 'get-logins', - GENERATE_PASSWORD: 'generate-password', - ASSOCIATE: 'associate', - TEST_ASSOCIATE: 'test-associate', - GET_DATABASE_HASH: 'get-databasehash', - CHANGE_PUBLIC_KEYS: 'change-public-keys', - LOCK_DATABASE: 'lock-database', - DATABASE_LOCKED: 'database-locked', - DATABASE_UNLOCKED: 'database-unlocked' + SET_LOGIN: 'set-login', + GET_LOGINS: 'get-logins', + GENERATE_PASSWORD: 'generate-password', + ASSOCIATE: 'associate', + TEST_ASSOCIATE: 'test-associate', + GET_DATABASE_HASH: 'get-databasehash', + CHANGE_PUBLIC_KEYS: 'change-public-keys', + LOCK_DATABASE: 'lock-database', + DATABASE_LOCKED: 'database-locked', + DATABASE_UNLOCKED: 'database-unlocked' }; const kpErrors = { - UNKNOWN_ERROR: 0, - DATABASE_NOT_OPENED: 1, - DATABASE_HASH_NOT_RECEIVED: 2, - CLIENT_PUBLIC_KEY_NOT_RECEIVED: 3, - CANNOT_DECRYPT_MESSAGE: 4, - TIMEOUT_OR_NOT_CONNECTED: 5, - ACTION_CANCELLED_OR_DENIED: 6, - PUBLIC_KEY_NOT_FOUND: 7, - ASSOCIATION_FAILED: 8, - KEY_CHANGE_FAILED: 9, - ENCRYPTION_KEY_UNRECOGNIZED: 10, - NO_SAVED_DATABASES_FOUND: 11, - INCORRECT_ACTION: 12, - EMPTY_MESSAGE_RECEIVED: 13, - NO_URL_PROVIDED: 14, + UNKNOWN_ERROR: 0, + DATABASE_NOT_OPENED: 1, + DATABASE_HASH_NOT_RECEIVED: 2, + CLIENT_PUBLIC_KEY_NOT_RECEIVED: 3, + CANNOT_DECRYPT_MESSAGE: 4, + TIMEOUT_OR_NOT_CONNECTED: 5, + ACTION_CANCELLED_OR_DENIED: 6, + PUBLIC_KEY_NOT_FOUND: 7, + ASSOCIATION_FAILED: 8, + KEY_CHANGE_FAILED: 9, + ENCRYPTION_KEY_UNRECOGNIZED: 10, + NO_SAVED_DATABASES_FOUND: 11, + INCORRECT_ACTION: 12, + EMPTY_MESSAGE_RECEIVED: 13, + NO_URL_PROVIDED: 14, + NO_LOGINS_FOUND: 15, errorMessages : { - 0: { msg: 'Unknown error' }, - 1: { msg: 'Database not opened' }, - 2: { msg: 'Database hash not received' }, - 3: { msg: 'Client public key not reveiced' }, - 4: { msg: 'Cannot decrypt message' }, - 5: { msg: 'Timeout or not connected to KeePassXC' }, - 6: { msg: 'Action cancelled or denied' }, - 7: { msg: 'Cannot encrypt message or public key not found. Is Native Messaging enabled in KeePassXC?' }, - 8: { msg: 'KeePassXC association failed, try again.' }, - 9: { msg: 'Key change was not successful.' }, - 10: { msg: 'Encryption key is not recognized' }, - 11: { msg: 'No saved databases found.' }, - 12: { msg: 'Incorrect action.' }, - 13: { msg: 'Empty message received.' }, - 14: { msg: 'No URL provided.' } - }, + 0: { msg: 'Unknown error' }, + 1: { msg: 'Database not opened' }, + 2: { msg: 'Database hash not received' }, + 3: { msg: 'Client public key not reveiced' }, + 4: { msg: 'Cannot decrypt message' }, + 5: { msg: 'Timeout or not connected to KeePassXC' }, + 6: { msg: 'Action cancelled or denied' }, + 7: { msg: 'Cannot encrypt message or public key not found. Is Native Messaging enabled in KeePassXC?' }, + 8: { msg: 'KeePassXC association failed, try again.' }, + 9: { msg: 'Key change was not successful.' }, + 10: { msg: 'Encryption key is not recognized' }, + 11: { msg: 'No saved databases found.' }, + 12: { msg: 'Incorrect action.' }, + 13: { msg: 'Empty message received.' }, + 14: { msg: 'No URL provided.' }, + 15: { msg: 'No logins found.' } + }, - getError(errorCode) { - return this.errorMessages[errorCode].msg; - } + getError(errorCode) { + return this.errorMessages[errorCode].msg; + } }; browser.storage.local.get({ - 'latestKeePassXC': {'version': 0, 'versionParsed': 0, 'lastChecked': null}, - 'keyRing': {}}).then((item) => { - keepass.latestKeePassXC = item.latestKeePassXC; - keepass.keyRing = item.keyRing; + 'latestKeePassXC': {'version': 0, 'versionParsed': 0, 'lastChecked': null}, + 'keyRing': {}}).then((item) => { + keepass.latestKeePassXC = item.latestKeePassXC; + keepass.keyRing = item.keyRing; }); keepass.sendNativeMessage = function(request, enableTimeout = false) { - return new Promise((resolve, reject) => { - let timeout; - let action = request.action; - let ev = keepass.nativePort.onMessage; + return new Promise((resolve, reject) => { + let timeout; + let action = request.action; + let ev = keepass.nativePort.onMessage; - let listener = ((port, action) => { - let handler = (msg) => { - if (msg && msg.action === action) { - port.removeListener(handler); - if (enableTimeout) { - clearTimeout(timeout); - } - resolve(msg); - } - }; - return handler; - })(ev, action); - ev.addListener(listener); + let listener = ((port, action) => { + let handler = (msg) => { + if (msg && msg.action === action) { + port.removeListener(handler); + if (enableTimeout) { + clearTimeout(timeout); + } + resolve(msg); + } + }; + return handler; + })(ev, action); + ev.addListener(listener); - // Handle timeouts - if (enableTimeout) { - timeout = setTimeout(() => { - const errorMessage = { - action: action, - error: kpErrors.getError(kpErrors.TIMEOUT_OR_NOT_CONNECTED), - errorCode: kpErrors.TIMEOUT_OR_NOT_CONNECTED - }; - keepass.isKeePassXCAvailable = false; - ev.removeListener(listener.handler); - resolve(errorMessage); - }, keepass.messageTimeout); - } + // Handle timeouts + if (enableTimeout) { + timeout = setTimeout(() => { + const errorMessage = { + action: action, + error: kpErrors.getError(kpErrors.TIMEOUT_OR_NOT_CONNECTED), + errorCode: kpErrors.TIMEOUT_OR_NOT_CONNECTED + }; + keepass.isKeePassXCAvailable = false; + ev.removeListener(listener.handler); + resolve(errorMessage); + }, keepass.messageTimeout); + } - // Send the request - if (keepass.nativePort) { - keepass.nativePort.postMessage(request); - } - }); + // Send the request + if (keepass.nativePort) { + keepass.nativePort.postMessage(request); + } + }); }; keepass.addCredentials = function(callback, tab, username, password, url) { - keepass.updateCredentials(callback, tab, null, username, password, url); + keepass.updateCredentials(callback, tab, null, username, password, url); }; keepass.updateCredentials = function(callback, tab, entryId, username, password, url) { - page.debug('keepass.updateCredentials(callback, {1}, {2}, {3}, [password], {4})', tab.id, entryId, username, url); - if (tab && page.tabs[tab.id]) { - page.tabs[tab.id].errorMessage = null; - } + page.debug('keepass.updateCredentials(callback, {1}, {2}, {3}, [password], {4})', tab.id, entryId, username, url); + if (tab && page.tabs[tab.id]) { + page.tabs[tab.id].errorMessage = null; + } - keepass.testAssociation((response) => { - if (!response) { - browserAction.showDefault(null, tab); - callback([]); - return; - } + keepass.testAssociation((response) => { + if (!response) { + browserAction.showDefault(null, tab); + callback([]); + return; + } - const kpAction = kpActions.SET_LOGIN; - const {dbid} = keepass.getCryptoKey(); - const nonce = keepass.getNonce(); - const incrementedNonce = keepass.incrementedNonce(nonce); + const kpAction = kpActions.SET_LOGIN; + const {dbid} = keepass.getCryptoKey(); + const nonce = keepass.getNonce(); + const incrementedNonce = keepass.incrementedNonce(nonce); - let messageData = { - action: kpAction, - id: dbid, - login: username, - password: password, - url: url, - submitUrl: url - }; + let messageData = { + action: kpAction, + id: dbid, + login: username, + password: password, + url: url, + submitUrl: url + }; - if (entryId) { - messageData.uuid = entryId; - } + if (entryId) { + messageData.uuid = entryId; + } - const request = { - action: kpAction, - message: keepass.encrypt(messageData, nonce), - nonce: nonce, - clientID: keepass.clientID - }; + const request = { + action: kpAction, + message: keepass.encrypt(messageData, nonce), + nonce: nonce, + clientID: keepass.clientID + }; - keepass.sendNativeMessage(request).then((response) => { - if (response.message && response.nonce) { - const res = keepass.decrypt(response.message, response.nonce); - if (!res) { - keepass.handleError(tab, kpErrors.CANNOT_DECRYPT_MESSAGE); - callback('error'); - return; - } + keepass.sendNativeMessage(request).then((response) => { + if (response.message && response.nonce) { + const res = keepass.decrypt(response.message, response.nonce); + if (!res) { + keepass.handleError(tab, kpErrors.CANNOT_DECRYPT_MESSAGE); + callback('error'); + return; + } - const message = nacl.util.encodeUTF8(res); - const parsed = JSON.parse(message); - callback(keepass.verifyResponse(parsed, incrementedNonce) ? 'success' : 'error'); - } - else if (response.error && response.errorCode) { - keepass.handleError(tab, response.errorCode, response.error); - } - else { - browserAction.showDefault(null, tab); - } - }); - }); + const message = nacl.util.encodeUTF8(res); + const parsed = JSON.parse(message); + callback(keepass.verifyResponse(parsed, incrementedNonce) ? 'success' : 'error'); + } + else if (response.error && response.errorCode) { + keepass.handleError(tab, response.errorCode, response.error); + } + else { + browserAction.showDefault(null, tab); + } + }); + }); }; -keepass.retrieveCredentials = function(callback, tab, url, submiturl, forceCallback) { - page.debug('keepass.retrieveCredentials(callback, {1}, {2}, {3}, {4})', tab.id, url, submiturl, forceCallback); +keepass.retrieveCredentials = function(callback, tab, url, submiturl, forceCallback, triggerUnlock = false) { + page.debug('keepass.retrieveCredentials(callback, {1}, {2}, {3}, {4})', tab.id, url, submiturl, forceCallback); - keepass.testAssociation((response) => { - if (!response) { - browserAction.showDefault(null, tab); - if (forceCallback) { - callback([]); - } - return; - } + keepass.testAssociation((response) => { + if (!response) { + browserAction.showDefault(null, tab); + if (forceCallback) { + callback([]); + } + return; + } - if (tab && page.tabs[tab.id]) { - page.tabs[tab.id].errorMessage = null; - } + if (tab && page.tabs[tab.id]) { + page.tabs[tab.id].errorMessage = null; + } - if (!keepass.isConnected) { - callback([]); - return; - } + if (!keepass.isConnected) { + callback([]); + return; + } - let entries = []; - const kpAction = kpActions.GET_LOGINS; - const nonce = keepass.getNonce(); - const incrementedNonce = keepass.incrementedNonce(nonce); - const {dbid} = keepass.getCryptoKey(); + let entries = []; + const kpAction = kpActions.GET_LOGINS; + const nonce = keepass.getNonce(); + const incrementedNonce = keepass.incrementedNonce(nonce); + const {dbid} = keepass.getCryptoKey(); - let messageData = { - action: kpAction, - id: dbid, - url: url - }; + let messageData = { + action: kpAction, + id: dbid, + url: url + }; - if (submiturl) { - messageData.submitUrl = submiturl; - } + if (submiturl) { + messageData.submitUrl = submiturl; + } - const request = { - action: kpAction, - message: keepass.encrypt(messageData, nonce), - nonce: nonce, - clientID: keepass.clientID - }; + const request = { + action: kpAction, + message: keepass.encrypt(messageData, nonce), + nonce: nonce, + clientID: keepass.clientID + }; - keepass.sendNativeMessage(request).then((response) => { - if (response.message && response.nonce) { - const res = keepass.decrypt(response.message, response.nonce); - if (!res) { - keepass.handleError(tab, kpErrors.CANNOT_DECRYPT_MESSAGE); - callback([]); - return; - } + keepass.sendNativeMessage(request).then((response) => { + if (response.message && response.nonce) { + const res = keepass.decrypt(response.message, response.nonce); + if (!res) { + keepass.handleError(tab, kpErrors.CANNOT_DECRYPT_MESSAGE); + callback([]); + return; + } - const message = nacl.util.encodeUTF8(res); - const parsed = JSON.parse(message); - keepass.setcurrentKeePassXCVersion(parsed.version); + const message = nacl.util.encodeUTF8(res); + const parsed = JSON.parse(message); + keepass.setcurrentKeePassXCVersion(parsed.version); - if (keepass.verifyResponse(parsed, incrementedNonce)) { - entries = parsed.entries; - keepass.updateLastUsed(keepass.databaseHash); - if (entries.length === 0) { - // questionmark-icon is not triggered, so we have to trigger for the normal symbol - browserAction.showDefault(null, tab); - } - callback(entries); - } - else { - console.log('RetrieveCredentials for ' + url + ' rejected'); - } - page.debug('keepass.retrieveCredentials() => entries.length = {1}', entries.length); - } - else if (response.error && response.errorCode) { - keepass.handleError(tab, response.errorCode, response.error); - } - else { - browserAction.showDefault(null, tab); - } - }); - }, tab); + if (keepass.verifyResponse(parsed, incrementedNonce)) { + entries = parsed.entries; + keepass.updateLastUsed(keepass.databaseHash); + if (entries.length === 0) { + // questionmark-icon is not triggered, so we have to trigger for the normal symbol + browserAction.showDefault(null, tab); + } + callback(entries); + } + else { + console.log('RetrieveCredentials for ' + url + ' rejected'); + } + page.debug('keepass.retrieveCredentials() => entries.length = {1}', entries.length); + } + else if (response.error && response.errorCode) { + keepass.handleError(tab, response.errorCode, response.error); + } + else { + browserAction.showDefault(null, tab); + } + }); + }, tab, false, triggerUnlock); }; keepass.generatePassword = function(callback, tab, forceCallback) { - if (!keepass.isConnected) { - callback([]); - return; - } + if (!keepass.isConnected) { + callback([]); + return; + } - keepass.testAssociation((taresponse) => { - if (!taresponse) { - browserAction.showDefault(null, tab); - if (forceCallback) { - callback([]); - } - return; - } + keepass.testAssociation((taresponse) => { + if (!taresponse) { + browserAction.showDefault(null, tab); + if (forceCallback) { + callback([]); + } + return; + } - if (keepass.currentKeePassXC.versionParsed < keepass.requiredKeePassXC) { - callback([]); - return; - } + if (keepass.currentKeePassXC.versionParsed < keepass.requiredKeePassXC) { + callback([]); + return; + } - let passwords = []; - const kpAction = kpActions.GENERATE_PASSWORD; - const nonce = keepass.getNonce(); - const incrementedNonce = keepass.incrementedNonce(nonce); + let passwords = []; + const kpAction = kpActions.GENERATE_PASSWORD; + const nonce = keepass.getNonce(); + const incrementedNonce = keepass.incrementedNonce(nonce); - const request = { - action: kpAction, - nonce: nonce, - clientID: keepass.clientID - }; + const request = { + action: kpAction, + nonce: nonce, + clientID: keepass.clientID + }; - keepass.sendNativeMessage(request).then((response) => { - if (response.message && response.nonce) { - const res = keepass.decrypt(response.message, response.nonce); - if (!res) { - keepass.handleError(tab, kpErrors.CANNOT_DECRYPT_MESSAGE); - callback([]); - return; - } + keepass.sendNativeMessage(request).then((response) => { + if (response.message && response.nonce) { + const res = keepass.decrypt(response.message, response.nonce); + if (!res) { + keepass.handleError(tab, kpErrors.CANNOT_DECRYPT_MESSAGE); + callback([]); + return; + } - const message = nacl.util.encodeUTF8(res); - const parsed = JSON.parse(message); - keepass.setcurrentKeePassXCVersion(parsed.version); + const message = nacl.util.encodeUTF8(res); + const parsed = JSON.parse(message); + keepass.setcurrentKeePassXCVersion(parsed.version); - if (keepass.verifyResponse(parsed, incrementedNonce)) { - if (parsed.entries) { - passwords = parsed.entries; - keepass.updateLastUsed(keepass.databaseHash); - } - else { - console.log('No entries returned. Is KeePassXC up-to-date?'); - } - } - else { - console.log('GeneratePassword rejected'); - } - callback(passwords); - } - else if (response.error && response.errorCode) { - keepass.handleError(tab, response.errorCode, response.error); - } - }); - }, tab); + if (keepass.verifyResponse(parsed, incrementedNonce)) { + if (parsed.entries) { + passwords = parsed.entries; + keepass.updateLastUsed(keepass.databaseHash); + } + else { + console.log('No entries returned. Is KeePassXC up-to-date?'); + } + } + else { + console.log('GeneratePassword rejected'); + } + callback(passwords); + } + else if (response.error && response.errorCode) { + keepass.handleError(tab, response.errorCode, response.error); + } + }); + }, tab); }; keepass.associate = function(callback, tab) { - if (keepass.isAssociated()) { - callback([]); - return; - } + if (keepass.isAssociated()) { + callback([]); + return; + } - keepass.getDatabaseHash((res) => { - if (keepass.isDatabaseClosed || !keepass.isKeePassXCAvailable) { - callback([]); - return; - } + keepass.getDatabaseHash((res) => { + if (keepass.isDatabaseClosed || !keepass.isKeePassXCAvailable) { + callback([]); + return; + } - if (tab && page.tabs[tab.id]) { - page.tabs[tab.id].errorMessage = null; - } + if (tab && page.tabs[tab.id]) { + page.tabs[tab.id].errorMessage = null; + } - const kpAction = kpActions.ASSOCIATE; - const key = nacl.util.encodeBase64(keepass.keyPair.publicKey); - const nonce = keepass.getNonce(); - const incrementedNonce = keepass.incrementedNonce(nonce); + const kpAction = kpActions.ASSOCIATE; + const key = nacl.util.encodeBase64(keepass.keyPair.publicKey); + const nonce = keepass.getNonce(); + const incrementedNonce = keepass.incrementedNonce(nonce); - const messageData = { - action: kpAction, - key: key - }; + const messageData = { + action: kpAction, + key: key + }; - const request = { - action: kpAction, - message: keepass.encrypt(messageData, nonce), - nonce: nonce, - clientID: keepass.clientID - }; + const request = { + action: kpAction, + message: keepass.encrypt(messageData, nonce), + nonce: nonce, + clientID: keepass.clientID, + triggerUnlock: 'true' + }; - keepass.sendNativeMessage(request).then((response) => { - if (response.message && response.nonce) { - const res = keepass.decrypt(response.message, response.nonce); - if (!res) { - keepass.handleError(tab, kpErrors.CANNOT_DECRYPT_MESSAGE); - return; - } + keepass.sendNativeMessage(request).then((response) => { + if (response.message && response.nonce) { + const res = keepass.decrypt(response.message, response.nonce); + if (!res) { + keepass.handleError(tab, kpErrors.CANNOT_DECRYPT_MESSAGE); + return; + } - const message = nacl.util.encodeUTF8(res); - const parsed = JSON.parse(message); - keepass.setcurrentKeePassXCVersion(parsed.version); - const id = parsed.id; + const message = nacl.util.encodeUTF8(res); + const parsed = JSON.parse(message); + keepass.setcurrentKeePassXCVersion(parsed.version); + const id = parsed.id; - if (!keepass.verifyResponse(parsed, incrementedNonce)) { - keepass.handleError(tab, kpErrors.ASSOCIATION_FAILED); - } - else { - keepass.setCryptoKey(id, key); // Save the current public key as id key for the database - keepass.associated.value = true; - keepass.associated.hash = parsed.hash || 0; - } + if (!keepass.verifyResponse(parsed, incrementedNonce)) { + keepass.handleError(tab, kpErrors.ASSOCIATION_FAILED); + } + else { + keepass.setCryptoKey(id, key); // Save the current public key as id key for the database + keepass.associated.value = true; + keepass.associated.hash = parsed.hash || 0; + } - browserAction.show(callback, tab); - } - else if (response.error && response.errorCode) { - keepass.handleError(tab, response.errorCode, response.error); - } - }); - }, tab); + browserAction.show(callback, tab); + } + else if (response.error && response.errorCode) { + keepass.handleError(tab, response.errorCode, response.error); + } + }); + }, tab); }; -keepass.testAssociation = function(callback, tab, enableTimeout = false) { - if (tab && page.tabs[tab.id]) { - page.tabs[tab.id].errorMessage = null; - } +keepass.testAssociation = function(callback, tab, enableTimeout = false, triggerUnlock = false) { + if (tab && page.tabs[tab.id]) { + page.tabs[tab.id].errorMessage = null; + } - keepass.getDatabaseHash((dbHash) => { - if (!dbHash) { - callback(false); - return false; - } + keepass.getDatabaseHash((dbHash) => { + if (!dbHash) { + callback(false); + return false; + } - if (keepass.isDatabaseClosed || !keepass.isKeePassXCAvailable) { - callback(false); - return false; - } + if (keepass.isDatabaseClosed || !keepass.isKeePassXCAvailable) { + callback(false); + return false; + } - if (keepass.isAssociated()) { - callback(true); - return true; - } + if (keepass.isAssociated()) { + callback(true); + return true; + } - if (!keepass.serverPublicKey) { - if (tab && page.tabs[tab.id]) { - keepass.handleError(tab, kpErrors.PUBLIC_KEY_NOT_FOUND); - } - callback(false); - return false; - } + if (!keepass.serverPublicKey) { + if (tab && page.tabs[tab.id]) { + keepass.handleError(tab, kpErrors.PUBLIC_KEY_NOT_FOUND); + } + callback(false); + return false; + } - const kpAction = kpActions.TEST_ASSOCIATE; - const nonce = keepass.getNonce(); - const incrementedNonce = keepass.incrementedNonce(nonce); - const {dbid, dbkey} = keepass.getCryptoKey(); + const kpAction = kpActions.TEST_ASSOCIATE; + const nonce = keepass.getNonce(); + const incrementedNonce = keepass.incrementedNonce(nonce); + const {dbid, dbkey} = keepass.getCryptoKey(); - if (dbkey === null || dbid === null) { - if (tab && page.tabs[tab.id]) { - keepass.handleError(tab, kpErrors.NO_SAVED_DATABASES_FOUND); - } - callback(false); - return false; - } + if (dbkey === null || dbid === null) { + if (tab && page.tabs[tab.id]) { + keepass.handleError(tab, kpErrors.NO_SAVED_DATABASES_FOUND); + } + callback(false); + return false; + } - const messageData = { - action: kpAction, - id: dbid, - key: dbkey - }; + const messageData = { + action: kpAction, + id: dbid, + key: dbkey + }; - const request = { - action: kpAction, - message: keepass.encrypt(messageData, nonce), - nonce: nonce, - clientID: keepass.clientID - }; + const request = { + action: kpAction, + message: keepass.encrypt(messageData, nonce), + nonce: nonce, + clientID: keepass.clientID + }; - keepass.sendNativeMessage(request, enableTimeout).then((response) => { - if (response.message && response.nonce) { - const res = keepass.decrypt(response.message, response.nonce); - if (!res) { - keepass.handleError(tab, kpErrors.CANNOT_DECRYPT_MESSAGE); - callback(false); - return; - } + keepass.sendNativeMessage(request, enableTimeout).then((response) => { + if (response.message && response.nonce) { + const res = keepass.decrypt(response.message, response.nonce); + if (!res) { + keepass.handleError(tab, kpErrors.CANNOT_DECRYPT_MESSAGE); + callback(false); + return; + } - const message = nacl.util.encodeUTF8(res); - const parsed = JSON.parse(message); - keepass.setcurrentKeePassXCVersion(parsed.version); - keepass.isEncryptionKeyUnrecognized = false; + const message = nacl.util.encodeUTF8(res); + const parsed = JSON.parse(message); + keepass.setcurrentKeePassXCVersion(parsed.version); + keepass.isEncryptionKeyUnrecognized = false; - if (!keepass.verifyResponse(parsed, incrementedNonce)) { - const hash = response.hash || 0; - keepass.deleteKey(hash); - keepass.isEncryptionKeyUnrecognized = true; - keepass.handleError(tab, kpErrors.ENCRYPTION_KEY_UNRECOGNIZED); - keepass.associated.value = false; - keepass.associated.hash = null; - } - else if (!keepass.isAssociated()) { - keepass.handleError(tab, kpErrors.ASSOCIATION_FAILED); - } - else { - if (tab && page.tabs[tab.id]) { - delete page.tabs[tab.id].errorMessage; - } - } - } - else if (response.error && response.errorCode) { - keepass.handleError(tab, response.errorCode, response.error); - } - callback(keepass.isAssociated()); - }); - }, tab, enableTimeout); + if (!keepass.verifyResponse(parsed, incrementedNonce)) { + const hash = response.hash || 0; + keepass.deleteKey(hash); + keepass.isEncryptionKeyUnrecognized = true; + keepass.handleError(tab, kpErrors.ENCRYPTION_KEY_UNRECOGNIZED); + keepass.associated.value = false; + keepass.associated.hash = null; + } + else if (!keepass.isAssociated()) { + keepass.handleError(tab, kpErrors.ASSOCIATION_FAILED); + } + else { + if (tab && page.tabs[tab.id]) { + delete page.tabs[tab.id].errorMessage; + } + } + } + else if (response.error && response.errorCode) { + keepass.handleError(tab, response.errorCode, response.error); + } + callback(keepass.isAssociated()); + }); + }, tab, enableTimeout, triggerUnlock); }; -keepass.getDatabaseHash = function(callback, tab, enableTimeout = false) { - if (!keepass.isConnected) { - keepass.handleError(tab, kpErrors.TIMEOUT_OR_NOT_CONNECTED); - callback([]); - return; - } +keepass.getDatabaseHash = function(callback, tab, enableTimeout = false, triggerUnlock = false) { + if (!keepass.isConnected) { + keepass.handleError(tab, kpErrors.TIMEOUT_OR_NOT_CONNECTED); + callback([]); + return; + } - if (!keepass.serverPublicKey) { - keepass.changePublicKeys(tab); - } + if (!keepass.serverPublicKey) { + keepass.changePublicKeys(tab); + } - const kpAction = kpActions.GET_DATABASE_HASH; - const nonce = keepass.getNonce(); - const incrementedNonce = keepass.incrementedNonce(nonce); + const kpAction = kpActions.GET_DATABASE_HASH; + const nonce = keepass.getNonce(); + const incrementedNonce = keepass.incrementedNonce(nonce); - const messageData = { - action: kpAction - }; + const messageData = { + action: kpAction + }; - const encrypted = keepass.encrypt(messageData, nonce); - if (encrypted.length <= 0) { - keepass.handleError(tab, kpErrors.PUBLIC_KEY_NOT_FOUND); - callback(keepass.databaseHash); - return; - } + const encrypted = keepass.encrypt(messageData, nonce); + if (encrypted.length <= 0) { + keepass.handleError(tab, kpErrors.PUBLIC_KEY_NOT_FOUND); + callback(keepass.databaseHash); + return; + } - const request = { - action: kpAction, - message: encrypted, - nonce: nonce, - clientID: keepass.clientID - }; + const request = { + action: kpAction, + message: encrypted, + nonce: nonce, + clientID: keepass.clientID + }; - keepass.sendNativeMessage(request, enableTimeout).then((response) => { - if (response.message && response.nonce) { - const res = keepass.decrypt(response.message, response.nonce); - if (!res) { - keepass.handleError(tab, kpErrors.CANNOT_DECRYPT_MESSAGE); - callback('no-hash'); - return; - } + if (triggerUnlock === true) { + request.triggerUnlock = 'true'; + } - const message = nacl.util.encodeUTF8(res); - const parsed = JSON.parse(message); - if (parsed.hash) { - const oldDatabaseHash = keepass.databaseHash; - keepass.setcurrentKeePassXCVersion(parsed.version); - keepass.databaseHash = parsed.hash || 'no-hash'; + keepass.sendNativeMessage(request, enableTimeout).then((response) => { + if (response.message && response.nonce) { + const res = keepass.decrypt(response.message, response.nonce); + if (!res) { + keepass.handleError(tab, kpErrors.CANNOT_DECRYPT_MESSAGE); + callback('no-hash'); + return; + } - if (oldDatabaseHash && oldDatabaseHash != keepass.databaseHash) { - keepass.associated.value = false; - keepass.associated.hash = null; - } + const message = nacl.util.encodeUTF8(res); + const parsed = JSON.parse(message); + if (keepass.verifyDatabaseResponse(parsed, incrementedNonce) && parsed.hash) { + const oldDatabaseHash = keepass.databaseHash; + keepass.setcurrentKeePassXCVersion(parsed.version); + keepass.databaseHash = parsed.hash || 'no-hash'; - keepass.isDatabaseClosed = false; - keepass.isKeePassXCAvailable = true; - callback(parsed.hash); - return; - } - else if (parsed.errorCode) { - keepass.databaseHash = 'no-hash'; - keepass.isDatabaseClosed = true; - keepass.handleError(tab, kpErrors.DATABASE_NOT_OPENED); - callback(keepass.databaseHash); - return; - } - } - else { - keepass.databaseHash = 'no-hash'; - keepass.isDatabaseClosed = true; - if (response.message === '') { - keepass.isKeePassXCAvailable = false; - keepass.handleError(tab, kpErrors.TIMEOUT_OR_NOT_CONNECTED); - } - else { - keepass.handleError(tab, response.errorCode, response.error); - } - callback(keepass.databaseHash); - return; - } - }); + if (oldDatabaseHash && oldDatabaseHash != keepass.databaseHash) { + keepass.associated.value = false; + keepass.associated.hash = null; + } + + keepass.isDatabaseClosed = false; + keepass.isKeePassXCAvailable = true; + callback(parsed.hash); + return; + } + else if (parsed.errorCode) { + keepass.databaseHash = 'no-hash'; + keepass.isDatabaseClosed = true; + keepass.handleError(tab, kpErrors.DATABASE_NOT_OPENED); + callback(keepass.databaseHash); + return; + } + } + else { + keepass.databaseHash = 'no-hash'; + keepass.isDatabaseClosed = true; + if (response.message && response.message === '') { + keepass.isKeePassXCAvailable = false; + keepass.handleError(tab, kpErrors.TIMEOUT_OR_NOT_CONNECTED); + } + else { + keepass.handleError(tab, response.errorCode, response.error); + } + callback(keepass.databaseHash); + return; + } + }); }; keepass.changePublicKeys = function(tab, enableTimeout = false) { - return new Promise((resolve, reject) => { - if (!keepass.isConnected) { - keepass.handleError(tab, kpErrors.TIMEOUT_OR_NOT_CONNECTED); - reject(false); - } + return new Promise((resolve, reject) => { + if (!keepass.isConnected) { + keepass.handleError(tab, kpErrors.TIMEOUT_OR_NOT_CONNECTED); + reject(false); + } - const kpAction = kpActions.CHANGE_PUBLIC_KEYS; - const key = nacl.util.encodeBase64(keepass.keyPair.publicKey); - const nonce = keepass.getNonce(); - const incrementedNonce = keepass.incrementedNonce(nonce); - keepass.clientID = nacl.util.encodeBase64(nacl.randomBytes(keepass.keySize)); + const kpAction = kpActions.CHANGE_PUBLIC_KEYS; + const key = nacl.util.encodeBase64(keepass.keyPair.publicKey); + const nonce = keepass.getNonce(); + const incrementedNonce = keepass.incrementedNonce(nonce); + keepass.clientID = nacl.util.encodeBase64(nacl.randomBytes(keepass.keySize)); - const request = { - action: kpAction, - publicKey: key, - nonce: nonce, - clientID: keepass.clientID - }; + const request = { + action: kpAction, + publicKey: key, + nonce: nonce, + clientID: keepass.clientID + }; - keepass.sendNativeMessage(request, enableTimeout).then((response) => { - keepass.setcurrentKeePassXCVersion(response.version); + keepass.sendNativeMessage(request, enableTimeout).then((response) => { + keepass.setcurrentKeePassXCVersion(response.version); - if (!keepass.verifyKeyResponse(response, key, incrementedNonce)) { - if (tab && page.tabs[tab.id]) { - keepass.handleError(tab, kpErrors.KEY_CHANGE_FAILED); - reject(false); - } - } - else { - keepass.isKeePassXCAvailable = true; - console.log('Server public key: ' + nacl.util.encodeBase64(keepass.serverPublicKey)); - } - resolve(true); - }); - }); + if (!keepass.verifyKeyResponse(response, key, incrementedNonce)) { + if (tab && page.tabs[tab.id]) { + keepass.handleError(tab, kpErrors.KEY_CHANGE_FAILED); + reject(false); + } + } + else { + keepass.isKeePassXCAvailable = true; + console.log('Server public key: ' + nacl.util.encodeBase64(keepass.serverPublicKey)); + } + resolve(true); + }); + }); }; keepass.lockDatabase = function(tab) { - return new Promise((resolve, reject) => { - if (!keepass.isConnected) { - keepass.handleError(tab, kpErrors.TIMEOUT_OR_NOT_CONNECTED); - reject(false); - } + return new Promise((resolve, reject) => { + if (!keepass.isConnected) { + keepass.handleError(tab, kpErrors.TIMEOUT_OR_NOT_CONNECTED); + reject(false); + } - const kpAction = kpActions.LOCK_DATABASE; - const nonce = keepass.getNonce(); - const incrementedNonce = keepass.incrementedNonce(nonce); + const kpAction = kpActions.LOCK_DATABASE; + const nonce = keepass.getNonce(); + const incrementedNonce = keepass.incrementedNonce(nonce); - const messageData = { - action: kpAction - }; + const messageData = { + action: kpAction + }; - const request = { - action: kpAction, - message: keepass.encrypt(messageData, nonce), - nonce: nonce, - clientID: keepass.clientID - }; + const request = { + action: kpAction, + message: keepass.encrypt(messageData, nonce), + nonce: nonce, + clientID: keepass.clientID + }; - keepass.sendNativeMessage(request).then((response) => { - if (response.message && response.nonce) { - const res = keepass.decrypt(response.message, response.nonce); - if (!res) { - keepass.handleError(tab, kpErrors.CANNOT_DECRYPT_MESSAGE); - resolve(false); - return; - } + keepass.sendNativeMessage(request).then((response) => { + if (response.message && response.nonce) { + const res = keepass.decrypt(response.message, response.nonce); + if (!res) { + keepass.handleError(tab, kpErrors.CANNOT_DECRYPT_MESSAGE); + resolve(false); + return; + } - const message = nacl.util.encodeUTF8(res); - const parsed = JSON.parse(message); - keepass.setcurrentKeePassXCVersion(parsed.version); + const message = nacl.util.encodeUTF8(res); + const parsed = JSON.parse(message); + keepass.setcurrentKeePassXCVersion(parsed.version); - if (keepass.verifyResponse(parsed, incrementedNonce)) { - keepass.isDatabaseClosed = true; + if (keepass.verifyResponse(parsed, incrementedNonce)) { + keepass.isDatabaseClosed = true; - // Display error message in the popup - keepass.handleError(tab, kpErrors.DATABASE_NOT_OPENED); - resolve(true); - } - } - else if (response.error && response.errorCode) { - keepass.isDatabaseClosed = true; - keepass.handleError(tab, response.errorCode, response.error); - } - resolve(false); - }); - }); + // Display error message in the popup + keepass.handleError(tab, kpErrors.DATABASE_NOT_OPENED); + resolve(true); + } + } + else if (response.error && response.errorCode) { + keepass.isDatabaseClosed = true; + keepass.handleError(tab, response.errorCode, response.error); + } + resolve(false); + }); + }); }; keepass.generateNewKeyPair = function() { - keepass.keyPair = nacl.box.keyPair(); - //console.log(nacl.util.encodeBase64(keepass.keyPair.publicKey) + ' ' + nacl.util.encodeBase64(keepass.keyPair.secretKey)); + keepass.keyPair = nacl.box.keyPair(); + //console.log(nacl.util.encodeBase64(keepass.keyPair.publicKey) + ' ' + nacl.util.encodeBase64(keepass.keyPair.secretKey)); }; keepass.isConfigured = function() { - return new Promise((resolve, reject) => { - if (typeof(keepass.databaseHash) === 'undefined') { - keepass.getDatabaseHash((hash) => { - resolve(hash in keepass.keyRing); - }); - } else { - resolve(keepass.databaseHash in keepass.keyRing); - } - }); + return new Promise((resolve, reject) => { + if (typeof(keepass.databaseHash) === 'undefined') { + keepass.getDatabaseHash((hash) => { + resolve(hash in keepass.keyRing); + }); + } else { + resolve(keepass.databaseHash in keepass.keyRing); + } + }); }; keepass.checkDatabaseHash = function(callback, tab) { - callback(keepass.databaseHash); + callback(keepass.databaseHash); }; keepass.isAssociated = function() { - return (keepass.associated.value && keepass.associated.hash && keepass.associated.hash === keepass.databaseHash); + return (keepass.associated.value && keepass.associated.hash && keepass.associated.hash === keepass.databaseHash); }; keepass.migrateKeyRing = function() { - return new Promise((resolve, reject) => { - browser.storage.local.get('keyRing').then((item) => { - const keyring = item.keyRing; - // Change dates to numbers, for compatibilty with Chromium based browsers - if (keyring) { - let num = 0; - for (let keyHash in keyring) { - let key = keyring[keyHash]; - ['created', 'lastUsed'].forEach((fld) => { - let v = key[fld]; - if (v instanceof Date && v.valueOf() >= 0) { - key[fld] = v.valueOf(); - num++; - } else if (typeof v !== 'number') { - key[fld] = Date.now().valueOf(); - num++; - } - }); - } - if (num > 0) { - browser.storage.local.set({ keyRing: keyring }); - } - } - resolve(); - }); - }); + return new Promise((resolve, reject) => { + browser.storage.local.get('keyRing').then((item) => { + const keyring = item.keyRing; + // Change dates to numbers, for compatibilty with Chromium based browsers + if (keyring) { + let num = 0; + for (let keyHash in keyring) { + let key = keyring[keyHash]; + ['created', 'lastUsed'].forEach((fld) => { + let v = key[fld]; + if (v instanceof Date && v.valueOf() >= 0) { + key[fld] = v.valueOf(); + num++; + } else if (typeof v !== 'number') { + key[fld] = Date.now().valueOf(); + num++; + } + }); + } + if (num > 0) { + browser.storage.local.set({ keyRing: keyring }); + } + } + resolve(); + }); + }); }; keepass.saveKey = function(hash, id, key) { - if (!(hash in keepass.keyRing)) { - keepass.keyRing[hash] = { - id: id, - key: key, - hash: hash, - created: new Date().valueOf(), - lastUsed: new Date().valueOf() - }; - } - else { - keepass.keyRing[hash].id = id; - keepass.keyRing[hash].key = key; - keepass.keyRing[hash].hash = hash; - } - browser.storage.local.set({'keyRing': keepass.keyRing}); + if (!(hash in keepass.keyRing)) { + keepass.keyRing[hash] = { + id: id, + key: key, + hash: hash, + created: new Date().valueOf(), + lastUsed: new Date().valueOf() + }; + } + else { + keepass.keyRing[hash].id = id; + keepass.keyRing[hash].key = key; + keepass.keyRing[hash].hash = hash; + } + browser.storage.local.set({'keyRing': keepass.keyRing}); }; keepass.updateLastUsed = function(hash) { - if ((hash in keepass.keyRing)) { - keepass.keyRing[hash].lastUsed = new Date().valueOf(); - browser.storage.local.set({'keyRing': keepass.keyRing}); - } + if ((hash in keepass.keyRing)) { + keepass.keyRing[hash].lastUsed = new Date().valueOf(); + browser.storage.local.set({'keyRing': keepass.keyRing}); + } }; keepass.deleteKey = function(hash) { - delete keepass.keyRing[hash]; - browser.storage.local.set({'keyRing': keepass.keyRing}); + delete keepass.keyRing[hash]; + browser.storage.local.set({'keyRing': keepass.keyRing}); }; keepass.setcurrentKeePassXCVersion = function(version) { - if (version) { - keepass.currentKeePassXC = { - version: version, - versionParsed: Number(version.replace(/\./g, '')) - }; - } + if (version) { + keepass.currentKeePassXC = { + version: version, + versionParsed: Number(version.replace(/\./g, '')) + }; + } }; keepass.keePassXCUpdateAvailable = function() { - if (page.settings.checkUpdateKeePassXC && page.settings.checkUpdateKeePassXC > 0) { - const lastChecked = (keepass.latestKeePassXC.lastChecked) ? new Date(keepass.latestKeePassXC.lastChecked) : new Date(1986, 11, 21); - const daysSinceLastCheck = Math.floor(((new Date()).getTime()-lastChecked.getTime())/86400000); - if (daysSinceLastCheck >= page.settings.checkUpdateKeePassXC) { - keepass.checkForNewKeePassXCVersion(); - } - } + if (page.settings.checkUpdateKeePassXC && page.settings.checkUpdateKeePassXC > 0) { + const lastChecked = (keepass.latestKeePassXC.lastChecked) ? new Date(keepass.latestKeePassXC.lastChecked) : new Date(1986, 11, 21); + const daysSinceLastCheck = Math.floor(((new Date()).getTime()-lastChecked.getTime())/86400000); + if (daysSinceLastCheck >= page.settings.checkUpdateKeePassXC) { + keepass.checkForNewKeePassXCVersion(); + } + } - return (keepass.currentKeePassXC.versionParsed > 0 && keepass.currentKeePassXC.versionParsed < keepass.latestKeePassXC.versionParsed); + return (keepass.currentKeePassXC.versionParsed > 0 && keepass.currentKeePassXC.versionParsed < keepass.latestKeePassXC.versionParsed); }; keepass.checkForNewKeePassXCVersion = function() { - let xhr = new XMLHttpRequest(); - let version = -1; + let xhr = new XMLHttpRequest(); + let version = -1; - xhr.onload = function(e) { - if (xhr.readyState === 4 && xhr.status === 200) { - const json = JSON.parse(xhr.responseText); - if (json.tag_name) { - version = json.tag_name; - keepass.latestKeePassXC.version = version; - keepass.latestKeePassXC.versionParsed = Number(version.replace(/\./g, '')); - } - } + xhr.onload = function(e) { + if (xhr.readyState === 4 && xhr.status === 200) { + const json = JSON.parse(xhr.responseText); + if (json.tag_name) { + version = json.tag_name; + keepass.latestKeePassXC.version = version; + keepass.latestKeePassXC.versionParsed = Number(version.replace(/\./g, '')); + } + } - if (version !== -1) { - browser.storage.local.set({'latestKeePassXC': keepass.latestKeePassXC}); - } - }; + if (version !== -1) { + browser.storage.local.set({'latestKeePassXC': keepass.latestKeePassXC}); + } + }; - xhr.onerror = function(e) { - console.log('checkForNewKeePassXCVersion error:' + e); - }; + xhr.onerror = function(e) { + console.log('checkForNewKeePassXCVersion error:' + e); + }; - try { - xhr.open('GET', keepass.latestVersionUrl, true); - xhr.send(); - } - catch (ex) { - console.log(ex); - } - keepass.latestKeePassXC.lastChecked = new Date().valueOf(); + try { + xhr.open('GET', keepass.latestVersionUrl, true); + xhr.send(); + } + catch (ex) { + console.log(ex); + } + keepass.latestKeePassXC.lastChecked = new Date().valueOf(); }; keepass.connectToNative = function() { - if (keepass.nativePort) { - keepass.nativePort.disconnect(); - } - keepass.nativeConnect(); + if (keepass.nativePort) { + keepass.nativePort.disconnect(); + } + keepass.nativeConnect(); }; keepass.onNativeMessage = function(response) { - //console.log('Received message: ' + JSON.stringify(response)); + //console.log('Received message: ' + JSON.stringify(response)); - // Handle database lock/unlock status - if (response.action === kpActions.DATABASE_LOCKED || response.action === kpActions.DATABASE_UNLOCKED) { - keepass.testAssociation((response) => { - keepass.isConfigured().then((configured) => { - let data = page.tabs[page.currentTabId].stack[page.tabs[page.currentTabId].stack.length - 1]; - data.iconType = configured ? 'normal' : 'cross'; - browserAction.show(null, {'id': page.currentTabId}); - }); - }, null); - } + // Handle database lock/unlock status + if (response.action === kpActions.DATABASE_LOCKED || response.action === kpActions.DATABASE_UNLOCKED) { + keepass.testAssociation((response) => { + keepass.isConfigured().then((configured) => { + let data = page.tabs[page.currentTabId].stack[page.tabs[page.currentTabId].stack.length - 1]; + data.iconType = configured ? 'normal' : 'cross'; + browserAction.show(null, {'id': page.currentTabId}); + }); + }, null); + } }; function onDisconnected() { - keepass.nativePort = null; - keepass.isConnected = false; - keepass.isDatabaseClosed = true; - keepass.isKeePassXCAvailable = false; - keepass.associated.value = false; - keepass.associated.hash = null; - console.log('Failed to connect: ' + (browser.runtime.lastError === null ? 'Unknown error' : browser.runtime.lastError.message)); + keepass.nativePort = null; + keepass.isConnected = false; + keepass.isDatabaseClosed = true; + keepass.isKeePassXCAvailable = false; + keepass.associated.value = false; + keepass.associated.hash = null; + console.log('Failed to connect: ' + (browser.runtime.lastError === null ? 'Unknown error' : browser.runtime.lastError.message)); } keepass.getNonce = function() { - return nacl.util.encodeBase64(nacl.randomBytes(keepass.keySize)); + return nacl.util.encodeBase64(nacl.randomBytes(keepass.keySize)); }; keepass.incrementedNonce = function(nonce) { - const oldNonce = nacl.util.decodeBase64(nonce); - let newNonce = oldNonce.slice(0); + const oldNonce = nacl.util.decodeBase64(nonce); + let newNonce = oldNonce.slice(0); - // from libsodium/utils.c - let i = 0; - let c = 1; - for (; i < newNonce.length; ++i) { - c += newNonce[i]; - newNonce[i] = c; - c >>= 8; - } + // from libsodium/utils.c + let i = 0; + let c = 1; + for (; i < newNonce.length; ++i) { + c += newNonce[i]; + newNonce[i] = c; + c >>= 8; + } - return nacl.util.encodeBase64(newNonce); + return nacl.util.encodeBase64(newNonce); }; keepass.nativeConnect = function() { - console.log('Connecting to native messaging host ' + keepass.nativeHostName); - keepass.nativePort = browser.runtime.connectNative(keepass.nativeHostName); - keepass.nativePort.onMessage.addListener(keepass.onNativeMessage); - keepass.nativePort.onDisconnect.addListener(onDisconnected); - keepass.isConnected = true; + console.log('Connecting to native messaging host ' + keepass.nativeHostName); + keepass.nativePort = browser.runtime.connectNative(keepass.nativeHostName); + keepass.nativePort.onMessage.addListener(keepass.onNativeMessage); + keepass.nativePort.onDisconnect.addListener(onDisconnected); + keepass.isConnected = true; }; keepass.verifyKeyResponse = function(response, key, nonce) { - if (!response.success || !response.publicKey) { - keepass.associated.hash = null; - return false; - } + if (!response.success || !response.publicKey) { + keepass.associated.hash = null; + return false; + } - let reply = false; - if (nacl.util.decodeBase64(nonce).length !== nacl.secretbox.nonceLength) { - return false; - } + let reply = false; + if (!keepass.checkNonceLength(response.nonce)) { + console.log('Error: Invalid nonce length'); + return false; + } - reply = (response.nonce === nonce); + reply = (response.nonce === nonce); - if (response.publicKey) { - keepass.serverPublicKey = nacl.util.decodeBase64(response.publicKey); - reply = true; - } + if (response.publicKey) { + keepass.serverPublicKey = nacl.util.decodeBase64(response.publicKey); + reply = true; + } - return reply; + return reply; }; keepass.verifyResponse = function(response, nonce, id) { - keepass.associated.value = response.success; - if (response.success !== 'true') { - keepass.associated.hash = null; - return false; - } + keepass.associated.value = response.success; + if (response.success !== 'true') { + keepass.associated.hash = null; + return false; + } - keepass.associated.hash = keepass.databaseHash; + keepass.associated.hash = keepass.databaseHash; - if (nacl.util.decodeBase64(response.nonce).length !== nacl.secretbox.nonceLength) { - return false; - } + if (!keepass.checkNonceLength(response.nonce)) { + return false; + } - keepass.associated.value = (response.nonce === nonce); - if (keepass.associated.value === false) { - console.log("Compare failed"); - } + keepass.associated.value = (response.nonce === nonce); + if (keepass.associated.value === false) { + console.log('Error: Nonce compare failed'); + return false; + } - if (id) { - keepass.associated.value = (keepass.associated.value && id === response.id); - } + if (id) { + keepass.associated.value = (keepass.associated.value && id === response.id); + } - keepass.associated.hash = (keepass.associated.value) ? keepass.databaseHash : null; + keepass.associated.hash = (keepass.associated.value) ? keepass.databaseHash : null; + return keepass.isAssociated(); +}; - return keepass.isAssociated(); +keepass.verifyDatabaseResponse = function(response, nonce) { + if (response.success !== 'true') { + keepass.associated.hash = null; + return false; + } + + if (!keepass.checkNonceLength(response.nonce)) { + console.log('Error: Invalid nonce length'); + return false; + } + + if (response.nonce !== nonce) { + console.log('Error: Nonce compare failed'); + return false; + } + + keepass.associated.hash = response.hash; + return response.hash !== 'no-hash' && response.success === 'true'; +}; + +keepass.checkNonceLength = function(nonce) { + return nacl.util.decodeBase64(nonce).length === nacl.secretbox.nonceLength; }; keepass.handleError = function(tab, errorCode, errorMessage = '') { - if (errorMessage.length === 0) { - errorMessage = kpErrors.getError(errorCode); - } - console.log('Error ' + errorCode + ': ' + errorMessage); - if (tab && page.tabs[tab.id]) { - page.tabs[tab.id].errorMessage = errorMessage; - } + if (errorMessage.length === 0) { + errorMessage = kpErrors.getError(errorCode); + } + console.log('Error ' + errorCode + ': ' + errorMessage); + if (tab && page.tabs[tab.id]) { + page.tabs[tab.id].errorMessage = errorMessage; + } }; keepass.getCryptoKey = function() { - let dbkey = null; - let dbid = null; - if (!(keepass.databaseHash in keepass.keyRing)) { - return {dbid, dbkey}; - } + let dbkey = null; + let dbid = null; + if (!(keepass.databaseHash in keepass.keyRing)) { + return {dbid, dbkey}; + } - dbid = keepass.keyRing[keepass.databaseHash].id; + dbid = keepass.keyRing[keepass.databaseHash].id; - if (dbid) { - dbkey = keepass.keyRing[keepass.databaseHash].key; - } + if (dbid) { + dbkey = keepass.keyRing[keepass.databaseHash].key; + } - return {dbid, dbkey}; + return {dbid, dbkey}; }; keepass.setCryptoKey = function(id, key) { - keepass.saveKey(keepass.databaseHash, id, key); + keepass.saveKey(keepass.databaseHash, id, key); }; keepass.encrypt = function(input, nonce) { - const messageData = nacl.util.decodeUTF8(JSON.stringify(input)); - const messageNonce = nacl.util.decodeBase64(nonce); + const messageData = nacl.util.decodeUTF8(JSON.stringify(input)); + const messageNonce = nacl.util.decodeBase64(nonce); - if (keepass.serverPublicKey) { - const message = nacl.box(messageData, messageNonce, keepass.serverPublicKey, keepass.keyPair.secretKey); - if (message) { - return nacl.util.encodeBase64(message); - } - } - return ''; + if (keepass.serverPublicKey) { + const message = nacl.box(messageData, messageNonce, keepass.serverPublicKey, keepass.keyPair.secretKey); + if (message) { + return nacl.util.encodeBase64(message); + } + } + return ''; }; keepass.decrypt = function(input, nonce) { - const m = nacl.util.decodeBase64(input); - const n = nacl.util.decodeBase64(nonce); - const res = nacl.box.open(m, n, keepass.serverPublicKey, keepass.keyPair.secretKey); - return res; + const m = nacl.util.decodeBase64(input); + const n = nacl.util.decodeBase64(nonce); + const res = nacl.box.open(m, n, keepass.serverPublicKey, keepass.keyPair.secretKey); + return res; }; diff --git a/keepassxc-browser/background/page.js b/keepassxc-browser/background/page.js index fb33b3f..bcc126b 100644 --- a/keepassxc-browser/background/page.js +++ b/keepassxc-browser/background/page.js @@ -1,10 +1,10 @@ const defaultSettings = { - checkUpdateKeePassXC: 3, - autoCompleteUsernames: true, - autoFillAndSend: true, - usePasswordGenerator: true, - autoFillSingleEntry: false, - autoRetrieveCredentials: true + checkUpdateKeePassXC: 3, + autoCompleteUsernames: true, + autoFillAndSend: true, + usePasswordGenerator: true, + autoFillSingleEntry: false, + autoRetrieveCredentials: true }; var page = {}; @@ -13,72 +13,72 @@ page.currentTabId = -1; page.blockedTabs = {}; page.initSettings = function() { - return new Promise((resolve, reject) => { - browser.storage.local.get({'settings': {}}).then((item) => { - page.settings = item.settings; - if (!('checkUpdateKeePassXC' in page.settings)) { - page.settings.checkUpdateKeePassXC = defaultSettings.checkUpdateKeePassXC; - } - if (!('autoCompleteUsernames' in page.settings)) { - page.settings.autoCompleteUsernames = defaultSettings.autoCompleteUsernames; - } - if (!('autoFillAndSend' in page.settings)) { - page.settings.autoFillAndSend = defaultSettings.autoFillAndSend; - } - if (!('usePasswordGenerator' in page.settings)) { - page.settings.usePasswordGenerator = defaultSettings.usePasswordGenerator; - } - if (!('autoFillSingleEntry' in page.settings)) { - page.settings.autoFillSingleEntry = defaultSettings.autoFillSingleEntry; - } - if (!('autoRetrieveCredentials' in page.settings)) { - page.settings.autoRetrieveCredentials = defaultSettings.autoRetrieveCredentials; - } - browser.storage.local.set({'settings': page.settings}); - resolve(page.settings); - }); - }); + return new Promise((resolve, reject) => { + browser.storage.local.get({'settings': {}}).then((item) => { + page.settings = item.settings; + if (!('checkUpdateKeePassXC' in page.settings)) { + page.settings.checkUpdateKeePassXC = defaultSettings.checkUpdateKeePassXC; + } + if (!('autoCompleteUsernames' in page.settings)) { + page.settings.autoCompleteUsernames = defaultSettings.autoCompleteUsernames; + } + if (!('autoFillAndSend' in page.settings)) { + page.settings.autoFillAndSend = defaultSettings.autoFillAndSend; + } + if (!('usePasswordGenerator' in page.settings)) { + page.settings.usePasswordGenerator = defaultSettings.usePasswordGenerator; + } + if (!('autoFillSingleEntry' in page.settings)) { + page.settings.autoFillSingleEntry = defaultSettings.autoFillSingleEntry; + } + if (!('autoRetrieveCredentials' in page.settings)) { + page.settings.autoRetrieveCredentials = defaultSettings.autoRetrieveCredentials; + } + browser.storage.local.set({'settings': page.settings}); + resolve(page.settings); + }); + }); }; page.initOpenedTabs = function() { - return new Promise((resolve, reject) => { - browser.tabs.query({}).then((tabs) => { - for (const i of tabs) { - page.createTabEntry(i.id); - } + return new Promise((resolve, reject) => { + browser.tabs.query({}).then((tabs) => { + for (const i of tabs) { + page.createTabEntry(i.id); + } - // set initial tab-ID - browser.tabs.query({ 'active': true, 'currentWindow': true }).then((tabs) => { - if (tabs.length === 0) { - resolve(); - return; // For example: only the background devtools or a popup are opened - } - page.currentTabId = tabs[0].id; - browserAction.show(null, tabs[0]); - resolve(); - }); - }); - }); + // set initial tab-ID + browser.tabs.query({ 'active': true, 'currentWindow': true }).then((tabs) => { + if (tabs.length === 0) { + resolve(); + return; // For example: only the background devtools or a popup are opened + } + page.currentTabId = tabs[0].id; + browserAction.show(null, tabs[0]); + resolve(); + }); + }); + }); }; page.isValidProtocol = function(url) { - let protocol = url.substring(0, url.indexOf(':')); - protocol = protocol.toLowerCase(); - return !(url.indexOf('.') === -1 || (protocol !== 'http' && protocol !== 'https' && protocol !== 'ftp' && protocol !== 'sftp')); + let protocol = url.substring(0, url.indexOf(':')); + protocol = protocol.toLowerCase(); + return !(url.indexOf('.') === -1 || (protocol !== 'http' && protocol !== 'https' && protocol !== 'ftp' && protocol !== 'sftp')); }; page.switchTab = function(callback, tab) { - browserAction.showDefault(null, tab); - browser.tabs.sendMessage(tab.id, {action: 'activated_tab'}).catch((e) => {}); + browserAction.showDefault(null, tab); + browser.tabs.sendMessage(tab.id, {action: 'activated_tab'}).catch((e) => {}); }; page.clearCredentials = function(tabId, complete) { - if (!page.tabs[tabId]) { - return; - } + if (!page.tabs[tabId]) { + return; + } - page.tabs[tabId].credentials = {}; - delete page.tabs[tabId].credentials; + page.tabs[tabId].credentials = {}; + delete page.tabs[tabId].credentials; if (complete) { page.clearLogins(tabId); @@ -90,48 +90,48 @@ page.clearCredentials = function(tabId, complete) { }; page.clearLogins = function(tabId) { - page.tabs[tabId].loginList = []; + page.tabs[tabId].loginList = []; }; page.createTabEntry = function(tabId) { - page.tabs[tabId] = { - 'stack': [], - 'errorMessage': null, - 'loginList': {} - }; + page.tabs[tabId] = { + 'stack': [], + 'errorMessage': null, + 'loginList': {} + }; }; page.removePageInformationFromNotExistingTabs = function() { - let rand = Math.floor(Math.random()*1001); - if (rand === 28) { - browser.tabs.query({}).then(function(tabs) { - let $tabIds = []; - const $infoIds = Object.keys(page.tabs); + let rand = Math.floor(Math.random()*1001); + if (rand === 28) { + browser.tabs.query({}).then(function(tabs) { + let $tabIds = []; + const $infoIds = Object.keys(page.tabs); - for (const t of tabs) { - $tabIds[t.id] = true; - } + for (const t of tabs) { + $tabIds[t.id] = true; + } - for (const i of $infoIds) { - if (!(i in $tabIds)) { - delete page.tabs[i]; - } - } - }); - } + for (const i of $infoIds) { + if (!(i in $tabIds)) { + delete page.tabs[i]; + } + } + }); + } }; page.debugConsole = function() { - if (arguments.length > 1) { - console.log(page.sprintf(arguments[0], arguments)); - } - else { - console.log(arguments[0]); - } + if (arguments.length > 1) { + console.log(page.sprintf(arguments[0], arguments)); + } + else { + console.log(arguments[0]); + } }; page.sprintf = function(input, args) { - return input.replace(/{(\d+)}/g, (match, number) => { + return input.replace(/{(\d+)}/g, (match, number) => { return typeof args[number] !== 'undefined' ? (typeof args[number] === 'object' ? JSON.stringify(args[number]) : args[number]) : match; }); }; @@ -141,12 +141,12 @@ page.debugDummy = function() {}; page.debug = page.debugDummy; page.setDebug = function(bool) { - if (bool) { - page.debug = page.debugConsole; - return 'Debug mode enabled'; - } - else { - page.debug = page.debugDummy; - return 'Debug mode disabled'; - } + if (bool) { + page.debug = page.debugConsole; + return 'Debug mode enabled'; + } + else { + page.debug = page.debugDummy; + return 'Debug mode disabled'; + } }; diff --git a/keepassxc-browser/global.js b/keepassxc-browser/global.js index 9833a33..8f38453 100644 --- a/keepassxc-browser/global.js +++ b/keepassxc-browser/global.js @@ -1,6 +1,6 @@ var isFirefox = function() { - if (!(/Chrome/.test(navigator.userAgent) && /Google/.test(navigator.vendor))) { - return true; - } - return false; + if (!(/Chrome/.test(navigator.userAgent) && /Google/.test(navigator.vendor))) { + return true; + } + return false; }; diff --git a/keepassxc-browser/keepassxc-browser.css b/keepassxc-browser/keepassxc-browser.css index 66dca5d..1899a21 100644 --- a/keepassxc-browser/keepassxc-browser.css +++ b/keepassxc-browser/keepassxc-browser.css @@ -1,22 +1,22 @@ .kpxc .ui-autocomplete li.ui-menu-item { - text-align: left !important; - font-size: .9em !important; - font-weight: normal !important; - font-style: normal !important; - font-family: Verdana, Arial, sans-serif !important; - color: #222222 !important; + text-align: left !important; + font-size: .9em !important; + font-weight: normal !important; + font-style: normal !important; + font-family: Verdana, Arial, sans-serif !important; + color: #222222 !important; } .ui-helper-hidden-accessible { - display: none !important; + display: none !important; } .kpxc .ui-dialog-titlebar-close { - visibility: hidden !important; + visibility: hidden !important; } .kpxc .ui-dialog { - font-size: 12px !important; + font-size: 12px !important; } .kpxc .ui-widget-overlay { @@ -24,12 +24,12 @@ } .kpxc .dialog-form .ui-dialog-content .ui-widget-content { - max-height: 80px !important; + max-height: 80px !important; } .kpxc .ui-dialog-titlebar { - background-color: #3a8233; - color: #fff; + background-color: #3a8233; + color: #fff; } .kpxc .ui-dialog .ui-dialog-buttonpane { @@ -41,164 +41,164 @@ } .kpxc .ui-button .ui-button-text .ui-button { - font-size: .10em !important; + font-size: .10em !important; } input.genpw-text { - font-size: .9em !important; - padding: .4em; - border-radius: 4px; - border-top-right-radius: 0; - border-bottom-right-radius: 0; - border-collapse: separate; - width: 100%; + font-size: .9em !important; + padding: .4em; + border-radius: 4px; + border-top-right-radius: 0; + border-bottom-right-radius: 0; + border-collapse: separate; + width: 100%; } .genpw-input-group-addon { - font-size: inherit !important; - background-color: #eee; - border: 1px solid #ccc; - padding: .4em; - border-radius: 4px; - border-top-left-radius: 0; - border-bottom-left-radius: 0; - white-space: nowrap; - vertical-align: middle; - display: table-cell; - border-collapse: separate; + font-size: inherit !important; + background-color: #eee; + border: 1px solid #ccc; + padding: .4em; + border-radius: 4px; + border-top-left-radius: 0; + border-bottom-left-radius: 0; + white-space: nowrap; + vertical-align: middle; + display: table-cell; + border-collapse: separate; } .kpxc .genpw-input-group { - position: relative; - display: table; - border-collapse: separate; - width: 100%; + position: relative; + display: table; + border-collapse: separate; + width: 100%; } .cip-genpw-icon { - position: absolute; - cursor: pointer; + position: absolute; + cursor: pointer; } .cip-genpw-icon.key { - background: url('chrome-extension://__MSG_@@extension_id__/icons/key.png') right no-repeat; - background-size: contain; + background: url('chrome-extension://__MSG_@@extension_id__/icons/key.png') right no-repeat; + background-size: contain; } .cip-genpw-icon.key-moz { - background: url('moz-extension://__MSG_@@extension_id__/icons/key.png') right no-repeat; - background-size: contain; + background: url('moz-extension://__MSG_@@extension_id__/icons/key.png') right no-repeat; + background-size: contain; } .kpxc .cip-genpw-checkbox { - -webkit-appearance: checkbox !important; - -moz-appearance: checkbox !important; - appearance: checkbox !important; + -webkit-appearance: checkbox !important; + -moz-appearance: checkbox !important; + appearance: checkbox !important; } #cip-genpw-btn-fillin { - margin-right: 5px; + margin-right: 5px; } .b2c-modal-backdrop { - position: fixed; - top: 0; - right: 0; - bottom: 0; - left: 0; - z-index: 2147483645; + position: fixed; + top: 0; + right: 0; + bottom: 0; + left: 0; + z-index: 2147483645; } .b2c-modal-backdrop:after { - content:''; - position: fixed; - top: 0; - right: 0; - bottom: 0; - left: 0; - background-color: #000000; - opacity: 0.8; - filter: alpha(opacity=80); + content:''; + position: fixed; + top: 0; + right: 0; + bottom: 0; + left: 0; + background-color: #000000; + opacity: 0.8; + filter: alpha(opacity=80); } #b2c-cipDefine-fields { - z-index: 2147483646; + z-index: 2147483646; } #b2c-cipDefine-description { - z-index: 2147483646; - color: #efefef; - border: 2px solid #555555; - padding: 7px 5px; - position: absolute; - top: 100px; - left: 150px; - text-align: left; - cursor: pointer; - background-color:rgba(255,255,255,0.3); - font-size: 15px; + z-index: 2147483646; + color: #efefef; + border: 2px solid #555555; + padding: 7px 5px; + position: absolute; + top: 100px; + left: 150px; + text-align: left; + cursor: pointer; + background-color:rgba(255,255,255,0.3); + font-size: 15px; } #b2c-cipDefine-description div:first-of-type { - margin-top: 0; - padding-top: 0; - text-align: left; - color: #efefef; - padding-bottom: 8px; - font-weight: bold; - font-size: 160%; + margin-top: 0; + padding-top: 0; + text-align: left; + color: #efefef; + padding-bottom: 8px; + font-weight: bold; + font-size: 160%; } #b2c-cipDefine-description p { - margin-top: 10px; - padding-top: 10px; - color: #efefef; - border-top: 2px solid #666666; - line-height: 110%; + margin-top: 10px; + padding-top: 10px; + color: #efefef; + border-top: 2px solid #666666; + line-height: 110%; } #b2c-help { - margin-bottom: 3px; + margin-bottom: 3px; } .b2c-fixed-field { - position: absolute; - border: 2px solid #efefef; - cursor: pointer; - z-index: 2147483646; - text-align: left; - font-weight: bold; - background-color:rgba(239,239,239,0.4); + position: absolute; + border: 2px solid #efefef; + cursor: pointer; + z-index: 2147483646; + text-align: left; + font-weight: bold; + background-color:rgba(239,239,239,0.4); } .b2c-fixed-hover-field { - border: 2px solid orange; - background-color:rgba(255,165,239,0.4); + border: 2px solid orange; + background-color:rgba(255,165,239,0.4); } .b2c-fixed-password-field { - border: 2px solid red; - color: #efefef; - background-color:rgba(255,0,0,0.4); + border: 2px solid red; + color: #efefef; + background-color:rgba(255,0,0,0.4); } .b2c-fixed-username-field { - border: 2px solid limegreen; - color: #efefef; - background-color:rgba(50,205,50,0.4); + border: 2px solid limegreen; + color: #efefef; + background-color:rgba(50,205,50,0.4); } .b2c-fixed-string-field { - border: 2px solid deepskyblue; - color: #efefef; - background-color:rgba(30,144,255,0.4); + border: 2px solid deepskyblue; + color: #efefef; + background-color:rgba(30,144,255,0.4); } .b2c-input-append { - display: inline-block; - margin-bottom: 10px; - white-space: nowrap; - vertical-align: middle; + display: inline-block; + margin-bottom: 10px; + white-space: nowrap; + vertical-align: middle; } .b2c-input-append input, @@ -206,23 +206,23 @@ input.genpw-text { .b2c-input-append .b2c-uneditable-input, .b2c-input-append .b2c-dropdown-menu, .b2c-input-append .b2c-popover { - font-size: 14px; + font-size: 14px; } .b2c-input-append input, .b2c-input-append select, .b2c-input-append .b2c-uneditable-input { - position: relative; - margin-bottom: 0; - *margin-left: 0; - vertical-align: top; - -webkit-border-radius: 0 4px 4px 0; - -moz-border-radius: 0 4px 4px 0; - border-radius: 0 4px 4px 0; + position: relative; + margin-bottom: 0; + *margin-left: 0; + vertical-align: top; + -webkit-border-radius: 0 4px 4px 0; + -moz-border-radius: 0 4px 4px 0; + border-radius: 0 4px 4px 0; } .b2c-input-append input:focus, .b2c-input-append select:focus, .b2c-input-append .b2c-b2c-uneditable-input:focus { - z-index: 2; + z-index: 2; } diff --git a/keepassxc-browser/keepassxc-browser.js b/keepassxc-browser/keepassxc-browser.js index 559790d..2cbec96 100644 --- a/keepassxc-browser/keepassxc-browser.js +++ b/keepassxc-browser/keepassxc-browser.js @@ -1,83 +1,85 @@ -// Initialize autocomplete feature -$(this.target).find('input').autocomplete(); - // contains already called method names var _called = {}; +_called.retrieveCredentials = false; +_called.manualFillRequested = 'none'; // Count of detected form fields on the page var _detectedFields = 0; browser.runtime.onMessage.addListener(function(req, sender, callback) { - if ('action' in req) { - if (req.action === 'fill_user_pass_with_specific_login') { - if (cip.credentials[req.id]) { - let combination = null; - if (cip.u) { - cip.setValueWithChange(cip.u, cip.credentials[req.id].login); - combination = cipFields.getCombination('username', cip.u); - cip.u.focus(); - } - if (cip.p) { - cip.setValueWithChange(cip.p, cip.credentials[req.id].password); - combination = cipFields.getCombination('password', cip.p); - } + if ('action' in req) { + if (req.action === 'fill_user_pass_with_specific_login') { + if (cip.credentials[req.id]) { + let combination = null; + if (cip.u) { + cip.setValueWithChange(cip.u, cip.credentials[req.id].login); + combination = cipFields.getCombination('username', cip.u); + cip.u.focus(); + } + if (cip.p) { + cip.setValueWithChange(cip.p, cip.credentials[req.id].password); + combination = cipFields.getCombination('password', cip.p); + } let list = []; - if (cip.fillInStringFields(combination.fields, cip.credentials[req.id].stringFields, list)) { + if (cip.fillInStringFields(combination.fields, cip.credentials[req.id].stringFields, list)) { cipForm.destroy(false, {'password': list.list[0], 'username': list.list[1]}); } - } - // wish I could clear out _logins and _u, but a subsequent - // selection may be requested. - } - else if (req.action === 'fill_user_pass') { - cip.receiveCredentialsIfNecessary(); - cip.fillInFromActiveElement(false); - } - else if (req.action === 'fill_pass_only') { - cip.receiveCredentialsIfNecessary(); - cip.fillInFromActiveElement(false, true); - } - else if (req.action === 'fill_totp') { - cip.receiveCredentialsIfNecessary(); - cip.fillInFromActiveElementTOTPOnly(false); - } - else if (req.action === 'activate_password_generator') { - cip.initPasswordGenerator(cipFields.getAllFields()); - } - else if (req.action === 'remember_credentials') { - cip.contextMenuRememberCredentials(); - } - else if (req.action === 'choose_credential_fields') { - cipDefine.init(); - } - else if (req.action === 'clear_credentials') { - cipEvents.clearCredentials(); - callback(); - } - else if (req.action === 'activated_tab') { - cipEvents.triggerActivatedTab(); - callback(); - } - else if (req.action === 'redetect_fields') { - browser.runtime.sendMessage({ - action: 'load_settings', - }).then((response) => { - cip.settings = response; - cip.initCredentialFields(true); - }); - } - } + } + } + else if (req.action === 'fill_user_pass') { + _called.manualFillRequested = 'both'; + cip.receiveCredentialsIfNecessary().then((response) => { + cip.fillInFromActiveElement(false); + }); + } + else if (req.action === 'fill_pass_only') { + _called.manualFillRequested = 'pass'; + cip.receiveCredentialsIfNecessary().then((response) => { + cip.fillInFromActiveElement(false, true); // passOnly to true + }); + } + else if (req.action === 'fill_totp') { + cip.receiveCredentialsIfNecessary().then((response) => { + cip.fillInFromActiveElementTOTPOnly(false); + }); + } + else if (req.action === 'activate_password_generator') { + cip.initPasswordGenerator(cipFields.getAllFields()); + } + else if (req.action === 'remember_credentials') { + cip.contextMenuRememberCredentials(); + } + else if (req.action === 'choose_credential_fields') { + cipDefine.init(); + } + else if (req.action === 'clear_credentials') { + cipEvents.clearCredentials(); + callback(); + } + else if (req.action === 'activated_tab') { + cipEvents.triggerActivatedTab(); + callback(); + } + else if (req.action === 'redetect_fields') { + browser.runtime.sendMessage({ + action: 'load_settings', + }).then((response) => { + cip.settings = response; + cip.initCredentialFields(true); + }); + } + } }); function _f(fieldId) { - const field = (fieldId) ? jQuery('input[data-cip-id=\''+fieldId+'\']:first') : []; - return (field.length > 0) ? field : null; + const field = (fieldId) ? jQuery('input[data-cip-id=\''+fieldId+'\']:first') : []; + return (field.length > 0) ? field : null; } function _fs(fieldId) { - const field = (fieldId) ? jQuery('input[data-cip-id=\''+fieldId+'\']:first,select[data-cip-id=\''+fieldId+'\']:first').first() : []; - return (field.length > 0) ? field : null; + const field = (fieldId) ? jQuery('input[data-cip-id=\''+fieldId+'\']:first,select[data-cip-id=\''+fieldId+'\']:first').first() : []; + return (field.length > 0) ? field : null; } @@ -87,76 +89,76 @@ var cipAutocomplete = {}; cipAutocomplete.elements = []; cipAutocomplete.init = function(field) { - if (cip.settings.autoFillSingleEntry && cip.credentials.length === 1 && field.hasClass('ui-autocomplete-input')) { - field.autocomplete('destroy'); - } + if (cip.settings.autoFillSingleEntry && cip.credentials.length === 1 && field.hasClass('ui-autocomplete-input')) { + field.autocomplete('destroy'); + } - let acMenu = jQuery('#kpxc-ac-menu'); - if (acMenu.length == 0) { - jQuery('
').appendTo('body'); - } + let acMenu = jQuery('#kpxc-ac-menu'); + if (acMenu.length == 0) { + jQuery('
').appendTo('body'); + } - field - .addClass('kpxc') - .autocomplete({ - appendTo: '#kpxc-ac-menu', - minLength: 0, - source: cipAutocomplete.onSource, - select: cipAutocomplete.onSelect, - open: cipAutocomplete.onOpen - }); - field - .click(cipAutocomplete.onClick) - .blur(cipAutocomplete.onBlur) - .focus(cipAutocomplete.onFocus); + field + .addClass('kpxc') + .autocomplete({ + appendTo: '#kpxc-ac-menu', + minLength: 0, + source: cipAutocomplete.onSource, + select: cipAutocomplete.onSelect, + open: cipAutocomplete.onOpen + }); + field + .click(cipAutocomplete.onClick) + .blur(cipAutocomplete.onBlur) + .focus(cipAutocomplete.onFocus); }; cipAutocomplete.onClick = function() { - jQuery(this).autocomplete('search', jQuery(this).val()); + jQuery(this).autocomplete('search', jQuery(this).val()); }; cipAutocomplete.onOpen = function(event, ui) { - jQuery('ul.ui-autocomplete.ui-menu').css('z-index', 2147483636); + jQuery('ul.ui-autocomplete.ui-menu').css('z-index', 2147483636); }; cipAutocomplete.onSource = function(request, callback) { - const matches = jQuery.map(cipAutocomplete.elements, (tag) => { - if (tag.label.toUpperCase().indexOf(request.term.toUpperCase()) === 0) { - return tag; - } - }); - callback(matches); + const matches = jQuery.map(cipAutocomplete.elements, (tag) => { + if (tag.label.toUpperCase().indexOf(request.term.toUpperCase()) === 0) { + return tag; + } + }); + callback(matches); }; cipAutocomplete.onSelect = function(e, ui) { - e.preventDefault(); - cip.setValueWithChange(jQuery(this), ui.item.value); - const fieldId = cipFields.prepareId(jQuery(this).attr('data-cip-id')); - const combination = cipFields.getCombination('username', fieldId); - combination.loginId = ui.item.loginId; - cip.fillInCredentials(combination, true, false); - jQuery(this).data('fetched', true); + e.preventDefault(); + cip.setValueWithChange(jQuery(this), ui.item.value); + const fieldId = cipFields.prepareId(jQuery(this).attr('data-cip-id')); + const combination = cipFields.getCombination('username', fieldId); + combination.loginId = ui.item.loginId; + cip.fillInCredentials(combination, true, false); + jQuery(this).data('fetched', true); }; cipAutocomplete.onBlur = function() { - if (jQuery(this).data('fetched') === true) { - jQuery(this).data('fetched', false); - } - else { - const fieldId = cipFields.prepareId(jQuery(this).attr('data-cip-id')); - const fields = cipFields.getCombination('username', fieldId); - if (_f(fields.password) && _f(fields.password).data('unchanged') !== true && jQuery(this).val() !== '') { - cip.fillInCredentials(fields, true, true); - } - } + if (jQuery(this).data('fetched') === true) { + jQuery(this).data('fetched', false); + } + else { + const fieldId = cipFields.prepareId(jQuery(this).attr('data-cip-id')); + const fields = cipFields.getCombination('username', fieldId); + if (_f(fields.password) && _f(fields.password).data('unchanged') !== true && jQuery(this).val() !== '') { + cip.fillInCredentials(fields, true, true); + } + } }; cipAutocomplete.onFocus = function() { - cip.u = jQuery(this); + cip.u = jQuery(this); - if (jQuery(this).val() === '') { - jQuery(this).autocomplete('search', ''); - } + if (jQuery(this).val() === '') { + jQuery(this).autocomplete('search', ''); + } }; var cipPassword = {}; @@ -164,316 +166,318 @@ cipPassword.observedIcons = []; cipPassword.observingLock = false; cipPassword.init = function() { - if ('initPasswordGenerator' in _called) { - return; - } + if ('initPasswordGenerator' in _called) { + return; + } - _called.initPasswordGenerator = true; + _called.initPasswordGenerator = true; - window.setInterval(function() { - cipPassword.checkObservedElements(); - }, 400); + window.setInterval(function() { + cipPassword.checkObservedElements(); + }, 400); }; cipPassword.initField = function(field, inputs, pos) { - if (!field || field.length !== 1) { - return; - } - if (field.data('cip-password-generator')) { - return; - } + if (!field || field.length !== 1) { + return; + } + if (field.data('cip-password-generator')) { + return; + } - field.data('cip-password-generator', true); + field.data('cip-password-generator', true); - cipPassword.createIcon(field); + cipPassword.createIcon(field); - let $found = false; - if (inputs) { - for (let i = pos + 1; i < inputs.length; i++) { - if (inputs[i] && inputs[i].attr('type') && inputs[i].attr('type').toLowerCase() === 'password') { - field.data('cip-genpw-next-field-id', inputs[i].data('cip-id')); - field.data('cip-genpw-next-is-password-field', (i === 0)); - $found = true; - break; - } - } - } + let $found = false; + if (inputs) { + for (let i = pos + 1; i < inputs.length; i++) { + if (inputs[i] && inputs[i].attr('type') && inputs[i].attr('type').toLowerCase() === 'password') { + field.data('cip-genpw-next-field-id', inputs[i].data('cip-id')); + field.data('cip-genpw-next-is-password-field', (i === 0)); + $found = true; + break; + } + } + } - field.data('cip-genpw-next-field-exists', $found); + field.data('cip-genpw-next-field-exists', $found); }; cipPassword.createDialog = function() { - if ('passwordCreateDialog' in _called) { - return; - } + if ('passwordCreateDialog' in _called) { + return; + } - _called.passwordCreateDialog = true; + _called.passwordCreateDialog = true; - const $dialog = jQuery('
') - .addClass('dialog-form') - .attr('id', 'cip-genpw-dialog'); + const $dialog = jQuery('
') + .addClass('dialog-form') + .attr('id', 'cip-genpw-dialog'); - const $inputDiv = jQuery('
').addClass('form-group'); - const $inputGroup = jQuery('
').addClass('genpw-input-group'); - const $textfieldPassword = jQuery('') - .attr('id', 'cip-genpw-textfield-password') - .attr('type', 'text') - .attr('aria-describedby', 'cip-genpw-quality') - .attr('placeholder', 'Generated password') - .addClass('genpw-text ui-widget-content ui-corner-all') - .on('change keypress paste textInput input', function() { - jQuery('#cip-genpw-btn-clipboard:first').removeClass('btn-success'); - }); - const $quality = jQuery('') - .addClass('genpw-input-group-addon') - .addClass('b2c-add-on') - .attr('id', 'cip-genpw-quality') - .text('123 Bits'); - $inputGroup.append($textfieldPassword).append($quality); + const $inputDiv = jQuery('
').addClass('form-group'); + const $inputGroup = jQuery('
').addClass('genpw-input-group'); + const $textfieldPassword = jQuery('') + .attr('id', 'cip-genpw-textfield-password') + .attr('type', 'text') + .attr('aria-describedby', 'cip-genpw-quality') + .attr('placeholder', 'Generated password') + .addClass('genpw-text ui-widget-content ui-corner-all') + .on('change keypress paste textInput input', function() { + jQuery('#cip-genpw-btn-clipboard:first').removeClass('btn-success'); + }); + const $quality = jQuery('') + .addClass('genpw-input-group-addon') + .addClass('b2c-add-on') + .attr('id', 'cip-genpw-quality') + .text('123 Bits'); + $inputGroup.append($textfieldPassword).append($quality); - const $checkGroup = jQuery('
').addClass('genpw-input-group'); - const $checkboxNextField = jQuery('') - .attr('id', 'cip-genpw-checkbox-next-field') - .attr('type', 'checkbox') - .addClass('cip-genpw-checkbox'); - const $labelNextField = jQuery('