diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..5737441 --- /dev/null +++ b/.gitignore @@ -0,0 +1,11 @@ +.idea + +*.swp +*~ +*.tmp + +*.zip +*.crx +.bz +.bz2 +.xz diff --git a/CHANGELOG b/CHANGELOG old mode 100644 new mode 100755 index 348c786..69175e5 --- a/CHANGELOG +++ b/CHANGELOG @@ -1,3 +1,42 @@ +1.0.0 (27-02-2018) +========================= +- First stable release + +0.5.2 (02-02-2018) +========================= +- Choose own credential fields saves the full URL instead of host +- HTTP Auth no longer gets stuck when there are no credentials available +- Added option to show notifications (enabled by default) +- Added notifications for HTTP Auth and saving new credentialsList (new permission needed) + +0.5.1 (23-01-2018) +========================= +- Fixed unnecessary credential retrieving when input fields are not available + +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 +- Exclude XSD files from content scripts +- Switched default keyboard shortcuts to Alt+Shift+U and Alt+Shift+P + +0.4.5 (28-12-2017) +========================= +- Added support for aria-hidden attribute when checking input field visibility +- Fixed a bug in redetecting credentials +- Small CSS fixes + 0.4.4 (17-12-2017) ========================= - Added support for OTP codes via context menu diff --git a/README.md b/README.md index 7837d47..82fcde0 100644 --- a/README.md +++ b/README.md @@ -1,81 +1,25 @@ -# keepassxc-browser +# KeePassXC-Browser Browser extension for [KeePassXC](https://keepassxc.org/) with Native Messaging. -This is a heavily forked version of [pfn](https://github.com/pfn)'s [chromeIPass](https://github.com/pfn/passifox). -Some changes merged also from [smorks'](https://github.com/smorks/keepasshttp-connector) KeePassHttp-Connector fork. -For testing purposes, please use only the following unofficial KeePassXC [release's](https://github.com/varjolintu/keepassxc/releases). +Based on [pfn](https://github.com/pfn)'s [chromeIPass](https://github.com/pfn/passifox). +Some changes merged also from [smorks'](https://github.com/smorks/keepasshttp-connector) KeePassHttp-Connector. -Get the extension for [Firefox](https://addons.mozilla.org/en-US/firefox/addon/keepassxc-browser/) or [Chrome/Chromium](https://chrome.google.com/webstore/detail/keepassxc-browser/iopaggbpplllidnfmcghoonnokmjoicf). +Get the extension for [Firefox](https://addons.mozilla.org/en-US/firefox/addon/keepassxc-browser/) or [Chrome/Chromium](https://chrome.google.com/webstore/detail/keepassxc-browser/oboonakemofpalcgghocfoadofidjkkk). -Please see this [wiki page](https://github.com/varjolintu/keepassxc-browser/wiki/Connecting-the-database-with-keepassxc-browser) for instructions how to configure this KeePassXC fork in order to connect the database correctly. +Please see this [document](https://keepassxc.org/docs/keepassxc-browser-migration/) for instructions how to configure KeePassXC in order to connect the database correctly. ## How it works -There are two methods which you can use keepassxc-browser to connect to KeePassXC: +There are two methods which you can use KeePassXC-Browser to connect to KeePassXC: -1. keepassxc-browser communicated with KeePassXC through keepassxc-proxy. The proxy handles listening stdin/stdout +1. KeePassXC-Browser communicates with KeePassXC through keepassxc-proxy. The proxy handles listening to STDIN/STDOUT and transfers these messages through Unix domain sockets / named pipes to KeePassXC. This means KeePassXC can be used and started normally without inteference from -Native Messaging API. keepassxc-browser starts only the proxy application and there's no risk of shutting down KeePassXC or losing any unsaved changes. You don't need to install keepassxc-proxy separately. It is included in the latest KeePassXC fork. Alternatively you can use +Native Messaging API. KeePassXC-Browser starts only the proxy application and there's no risk of shutting down KeePassXC or losing any unsaved changes. You don't need to install keepassxc-proxy separately. It is included in the latest KeePassXC fork. Alternatively you can use [keepassxc-proxy-rust](https://github.com/varjolintu/keepassxc-proxy-rust) as a proxy if you prefer a non-Qt solution. There's also Python and C++ versions available at [keepassxc-proxy](https://github.com/varjolintu/keepassxc-proxy). -2. keepassxc-browser communicates directly with KeePassXC via stdin/stdout. Using native messaging directly is a more secure as it ensures the traffic between KeePassXC and keepassxc-browser is direct. This method launches KeePassXC every time you start the browser and closes when you exit. +2. KeePassXC-Browser communicates directly with KeePassXC via stdin/stdout. Using native messaging directly is a more secure as it ensures the traffic between KeePassXC and KeePassXC-Browser is direct. This method launches KeePassXC every time you start the browser and closes when you exit. This can cause unsaved changes not to be saved. If you use this method it's important to enable `Automatically save after every change` from KeePassXC's preferences. Because this option is not preferred as default it's good to test this feature with your OS and ensure KeePassXC asks to confirm any unsaved changes before exit. -## Improvements -The following improvements and features have been made after the fork. At this point some features are only available with the KeePassXC fork: -- Real-time detection of database status (locked/unlocked) -- Credentials on a page are cleared or received automatically again if database is locked or changed to another -- It is possible to lock the active database from the popup (using the red lock icon) -- Input forms are detected even if the login div has been hidden or is created after the page was loaded -- It is possible to use the active database from multiple browsers at the same time with keepassxc-proxy option -- Deprecated JavaScript functions are removed and everything is asynchronous -- Updated Bootstrap to version 3.3.7 and jQuery to version 3.2.1 -- New buttons, icons and settings page graphics -- Redesigned password generator dialog -- Password generator supports diceware passphrases and extended ASCII characters -- Autocomplete works also when only password fields are visible -- Supports TOTP with custom KHP placeholders (`KPH: {TOPT}`) - ## Protocol The details about the messaging protocol used with the browser extension and KeePassXC can be found [here](keepassxc-protocol.md). - -## Licenses - -``` -keepassxc-browser Copyright (C) 2017 Sami Vänttinen -This program is free software: you can redistribute it and/or modify -it under the terms of the GNU General Public License as published by -the Free Software Foundation, version 3 of the License. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program. If not, see . -``` - -``` -PassIFox & ChromeIPass Copyright © 2010-2017 Perry Nguyen -This program is free software: you can redistribute it and/or modify -it under the terms of the GNU General Public License as published by -the Free Software Foundation, version 3 of the License. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program. If not, see . -``` - -## Donations - -Feel free to support this project: -- Donate via [PayPal](https://paypal.me/varjolintu) -- Donate via Bitcoin: 1LHbD69CcmpLW5hjUXs2MGJhw3GxwqLdw3 - -Also consider donating to [KeePassXC](https://flattr.com/submit/auto?fid=x7yqz0&url=https%3A%2F%2Fkeepassxc.org) and [KeePassHttp-Connector](https://github.com/smorks/keepasshttp-connector) teams. They are doing great job. diff --git a/keepassxc-browser/background/browserAction.js b/keepassxc-browser/background/browserAction.js old mode 100644 new mode 100755 index b2cdd80..7b72292 --- a/keepassxc-browser/background/browserAction.js +++ b/keepassxc-browser/background/browserAction.js @@ -5,261 +5,267 @@ 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)); + browser.storage.local.get({'settings': {}}).then(function(item) { + const settings = item.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 = { - 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' - }; + 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' + }; - 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}); + + if (page.settings.showNotifications) { + showNotification('Create or modify the credentials by clicking on the extension icon.'); + } + }); }; 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 old mode 100644 new mode 100755 index a2c9f90..c730af6 --- 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,274 @@ 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(); +kpxcEvent.pageClearLogins = function(callback, tab, alreadyCalled) { + if (!alreadyCalled) { + 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 old mode 100644 new mode 100755 index 7a53fdd..5457caa --- a/keepassxc-browser/background/httpauth.js +++ b/keepassxc-browser/background/httpauth.js @@ -4,82 +4,92 @@ 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; - } +httpAuth.retrieveCredentials = function(tabId, url, submitUrl, forceCallback) { + return new Promise((resolve, reject) => { + keepass.retrieveCredentials((logins) => { + resolve(logins); + }, tabId, url, submitUrl, forceCallback); + }); +}; - httpAuth.requests.push(details.requestId); +httpAuth.processPendingCallbacks = async function(details, resolve, reject) { + if (httpAuth.requests.indexOf(details.requestId) >= 0 || !page.tabs[details.tabId]) { + reject({cancel: false}); + return; + } - if (details.challenger) { - details.proxyUrl = details.challenger.host; - } + httpAuth.requests.push(details.requestId); - details.searchUrl = (details.isProxy && details.proxyUrl) ? details.proxyUrl : details.url; + if (details.challenger) { + details.proxyUrl = details.challenger.host; + } - keepass.retrieveCredentials((logins) => { - httpAuth.loginOrShowCredentials(logins, details, resolve, reject); - }, { "id": details.tabId }, details.searchUrl, details.searchUrl, true); + details.searchUrl = (details.isProxy && details.proxyUrl) ? details.proxyUrl : details.url; + + const logins = await httpAuth.retrieveCredentials({ 'id': details.tabId }, details.searchUrl, details.searchUrl, true); + httpAuth.loginOrShowCredentials(logins, details, resolve, reject); }; 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 { + if (page.settings.showNotifications) { + showNotification('Multiple credentials detected. Click on the extension icon to choose the correct one.'); + } + kpxcEvent.onHTTPAuthPopup(null, { 'id': details.tabId }, { 'logins': logins, 'url': details.searchUrl, 'resolve': resolve }); + } + } + // no logins found + else { + reject({cancel: false}); + } }; 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 old mode 100644 new mode 100755 index b924fe7..e734ca1 --- a/keepassxc-browser/background/keepass.js +++ b/keepassxc-browser/background/keepass.js @@ -12,7 +12,7 @@ keepass.isKeePassXCAvailable = false; keepass.isEncryptionKeyUnrecognized = false; keepass.currentKeePassXC = {'version': 0, 'versionParsed': 0}; keepass.requiredKeePassXC = 220; -keepass.nativeHostName = 'com.varjolintu.keepassxc_browser'; +keepass.nativeHostName = 'org.keepassxc.keepassxc_browser'; keepass.nativePort = null; keepass.keySize = 24; keepass.latestVersionUrl = 'https://api.github.com/repos/keepassxreboot/keepassxc/releases/latest'; @@ -24,941 +24,975 @@ 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 or support for your browser 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); + callback([]); + } + else { + browserAction.showDefault(null, tab); + callback([]); + } + }); + }, 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 old mode 100644 new mode 100755 index a806ed1..f82b2ea --- a/keepassxc-browser/background/page.js +++ b/keepassxc-browser/background/page.js @@ -1,10 +1,11 @@ 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, + showNotifications: true }; var page = {}; @@ -13,72 +14,75 @@ 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; + } + if (!('showNotifications' in page.settings)) { + page.settings.showNotifications = defaultSettings.showNotifications; + } + 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 +94,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 +145,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 old mode 100644 new mode 100755 index 9833a33..558dc9d --- a/keepassxc-browser/global.js +++ b/keepassxc-browser/global.js @@ -1,6 +1,15 @@ 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; +}; + +var showNotification = function(message) { + browser.notifications.create({ + 'type': 'basic', + 'iconUrl': browser.extension.getURL('icons/keepassxc_64x64.png'), + 'title': 'KeePassXC-Browser', + 'message': message + }); }; diff --git a/keepassxc-browser/jquery-3.2.1.min.js b/keepassxc-browser/jquery-3.2.1.min.js deleted file mode 100644 index 644d35e..0000000 --- a/keepassxc-browser/jquery-3.2.1.min.js +++ /dev/null @@ -1,4 +0,0 @@ -/*! jQuery v3.2.1 | (c) JS Foundation and other contributors | jquery.org/license */ -!function(a,b){"use strict";"object"==typeof module&&"object"==typeof module.exports?module.exports=a.document?b(a,!0):function(a){if(!a.document)throw new Error("jQuery requires a window with a document");return b(a)}:b(a)}("undefined"!=typeof window?window:this,function(a,b){"use strict";var c=[],d=a.document,e=Object.getPrototypeOf,f=c.slice,g=c.concat,h=c.push,i=c.indexOf,j={},k=j.toString,l=j.hasOwnProperty,m=l.toString,n=m.call(Object),o={};function p(a,b){b=b||d;var c=b.createElement("script");c.text=a,b.head.appendChild(c).parentNode.removeChild(c)}var q="3.2.1",r=function(a,b){return new r.fn.init(a,b)},s=/^[\s\uFEFF\xA0]+|[\s\uFEFF\xA0]+$/g,t=/^-ms-/,u=/-([a-z])/g,v=function(a,b){return b.toUpperCase()};r.fn=r.prototype={jquery:q,constructor:r,length:0,toArray:function(){return f.call(this)},get:function(a){return null==a?f.call(this):a<0?this[a+this.length]:this[a]},pushStack:function(a){var b=r.merge(this.constructor(),a);return b.prevObject=this,b},each:function(a){return r.each(this,a)},map:function(a){return this.pushStack(r.map(this,function(b,c){return a.call(b,c,b)}))},slice:function(){return this.pushStack(f.apply(this,arguments))},first:function(){return this.eq(0)},last:function(){return this.eq(-1)},eq:function(a){var b=this.length,c=+a+(a<0?b:0);return this.pushStack(c>=0&&c0&&b-1 in a)}var x=function(a){var b,c,d,e,f,g,h,i,j,k,l,m,n,o,p,q,r,s,t,u="sizzle"+1*new Date,v=a.document,w=0,x=0,y=ha(),z=ha(),A=ha(),B=function(a,b){return a===b&&(l=!0),0},C={}.hasOwnProperty,D=[],E=D.pop,F=D.push,G=D.push,H=D.slice,I=function(a,b){for(var c=0,d=a.length;c+~]|"+K+")"+K+"*"),S=new RegExp("="+K+"*([^\\]'\"]*?)"+K+"*\\]","g"),T=new RegExp(N),U=new RegExp("^"+L+"$"),V={ID:new RegExp("^#("+L+")"),CLASS:new RegExp("^\\.("+L+")"),TAG:new RegExp("^("+L+"|[*])"),ATTR:new RegExp("^"+M),PSEUDO:new RegExp("^"+N),CHILD:new RegExp("^:(only|first|last|nth|nth-last)-(child|of-type)(?:\\("+K+"*(even|odd|(([+-]|)(\\d*)n|)"+K+"*(?:([+-]|)"+K+"*(\\d+)|))"+K+"*\\)|)","i"),bool:new RegExp("^(?:"+J+")$","i"),needsContext:new RegExp("^"+K+"*[>+~]|:(even|odd|eq|gt|lt|nth|first|last)(?:\\("+K+"*((?:-\\d)?\\d*)"+K+"*\\)|)(?=[^-]|$)","i")},W=/^(?:input|select|textarea|button)$/i,X=/^h\d$/i,Y=/^[^{]+\{\s*\[native \w/,Z=/^(?:#([\w-]+)|(\w+)|\.([\w-]+))$/,$=/[+~]/,_=new RegExp("\\\\([\\da-f]{1,6}"+K+"?|("+K+")|.)","ig"),aa=function(a,b,c){var d="0x"+b-65536;return d!==d||c?b:d<0?String.fromCharCode(d+65536):String.fromCharCode(d>>10|55296,1023&d|56320)},ba=/([\0-\x1f\x7f]|^-?\d)|^-$|[^\0-\x1f\x7f-\uFFFF\w-]/g,ca=function(a,b){return b?"\0"===a?"\ufffd":a.slice(0,-1)+"\\"+a.charCodeAt(a.length-1).toString(16)+" ":"\\"+a},da=function(){m()},ea=ta(function(a){return a.disabled===!0&&("form"in a||"label"in a)},{dir:"parentNode",next:"legend"});try{G.apply(D=H.call(v.childNodes),v.childNodes),D[v.childNodes.length].nodeType}catch(fa){G={apply:D.length?function(a,b){F.apply(a,H.call(b))}:function(a,b){var c=a.length,d=0;while(a[c++]=b[d++]);a.length=c-1}}}function ga(a,b,d,e){var f,h,j,k,l,o,r,s=b&&b.ownerDocument,w=b?b.nodeType:9;if(d=d||[],"string"!=typeof a||!a||1!==w&&9!==w&&11!==w)return d;if(!e&&((b?b.ownerDocument||b:v)!==n&&m(b),b=b||n,p)){if(11!==w&&(l=Z.exec(a)))if(f=l[1]){if(9===w){if(!(j=b.getElementById(f)))return d;if(j.id===f)return d.push(j),d}else if(s&&(j=s.getElementById(f))&&t(b,j)&&j.id===f)return d.push(j),d}else{if(l[2])return G.apply(d,b.getElementsByTagName(a)),d;if((f=l[3])&&c.getElementsByClassName&&b.getElementsByClassName)return G.apply(d,b.getElementsByClassName(f)),d}if(c.qsa&&!A[a+" "]&&(!q||!q.test(a))){if(1!==w)s=b,r=a;else if("object"!==b.nodeName.toLowerCase()){(k=b.getAttribute("id"))?k=k.replace(ba,ca):b.setAttribute("id",k=u),o=g(a),h=o.length;while(h--)o[h]="#"+k+" "+sa(o[h]);r=o.join(","),s=$.test(a)&&qa(b.parentNode)||b}if(r)try{return G.apply(d,s.querySelectorAll(r)),d}catch(x){}finally{k===u&&b.removeAttribute("id")}}}return i(a.replace(P,"$1"),b,d,e)}function ha(){var a=[];function b(c,e){return a.push(c+" ")>d.cacheLength&&delete b[a.shift()],b[c+" "]=e}return b}function ia(a){return a[u]=!0,a}function ja(a){var b=n.createElement("fieldset");try{return!!a(b)}catch(c){return!1}finally{b.parentNode&&b.parentNode.removeChild(b),b=null}}function ka(a,b){var c=a.split("|"),e=c.length;while(e--)d.attrHandle[c[e]]=b}function la(a,b){var c=b&&a,d=c&&1===a.nodeType&&1===b.nodeType&&a.sourceIndex-b.sourceIndex;if(d)return d;if(c)while(c=c.nextSibling)if(c===b)return-1;return a?1:-1}function ma(a){return function(b){var c=b.nodeName.toLowerCase();return"input"===c&&b.type===a}}function na(a){return function(b){var c=b.nodeName.toLowerCase();return("input"===c||"button"===c)&&b.type===a}}function oa(a){return function(b){return"form"in b?b.parentNode&&b.disabled===!1?"label"in b?"label"in b.parentNode?b.parentNode.disabled===a:b.disabled===a:b.isDisabled===a||b.isDisabled!==!a&&ea(b)===a:b.disabled===a:"label"in b&&b.disabled===a}}function pa(a){return ia(function(b){return b=+b,ia(function(c,d){var e,f=a([],c.length,b),g=f.length;while(g--)c[e=f[g]]&&(c[e]=!(d[e]=c[e]))})})}function qa(a){return a&&"undefined"!=typeof a.getElementsByTagName&&a}c=ga.support={},f=ga.isXML=function(a){var b=a&&(a.ownerDocument||a).documentElement;return!!b&&"HTML"!==b.nodeName},m=ga.setDocument=function(a){var b,e,g=a?a.ownerDocument||a:v;return g!==n&&9===g.nodeType&&g.documentElement?(n=g,o=n.documentElement,p=!f(n),v!==n&&(e=n.defaultView)&&e.top!==e&&(e.addEventListener?e.addEventListener("unload",da,!1):e.attachEvent&&e.attachEvent("onunload",da)),c.attributes=ja(function(a){return a.className="i",!a.getAttribute("className")}),c.getElementsByTagName=ja(function(a){return a.appendChild(n.createComment("")),!a.getElementsByTagName("*").length}),c.getElementsByClassName=Y.test(n.getElementsByClassName),c.getById=ja(function(a){return o.appendChild(a).id=u,!n.getElementsByName||!n.getElementsByName(u).length}),c.getById?(d.filter.ID=function(a){var b=a.replace(_,aa);return function(a){return a.getAttribute("id")===b}},d.find.ID=function(a,b){if("undefined"!=typeof b.getElementById&&p){var c=b.getElementById(a);return c?[c]:[]}}):(d.filter.ID=function(a){var b=a.replace(_,aa);return function(a){var c="undefined"!=typeof a.getAttributeNode&&a.getAttributeNode("id");return c&&c.value===b}},d.find.ID=function(a,b){if("undefined"!=typeof b.getElementById&&p){var c,d,e,f=b.getElementById(a);if(f){if(c=f.getAttributeNode("id"),c&&c.value===a)return[f];e=b.getElementsByName(a),d=0;while(f=e[d++])if(c=f.getAttributeNode("id"),c&&c.value===a)return[f]}return[]}}),d.find.TAG=c.getElementsByTagName?function(a,b){return"undefined"!=typeof b.getElementsByTagName?b.getElementsByTagName(a):c.qsa?b.querySelectorAll(a):void 0}:function(a,b){var c,d=[],e=0,f=b.getElementsByTagName(a);if("*"===a){while(c=f[e++])1===c.nodeType&&d.push(c);return d}return f},d.find.CLASS=c.getElementsByClassName&&function(a,b){if("undefined"!=typeof b.getElementsByClassName&&p)return b.getElementsByClassName(a)},r=[],q=[],(c.qsa=Y.test(n.querySelectorAll))&&(ja(function(a){o.appendChild(a).innerHTML="",a.querySelectorAll("[msallowcapture^='']").length&&q.push("[*^$]="+K+"*(?:''|\"\")"),a.querySelectorAll("[selected]").length||q.push("\\["+K+"*(?:value|"+J+")"),a.querySelectorAll("[id~="+u+"-]").length||q.push("~="),a.querySelectorAll(":checked").length||q.push(":checked"),a.querySelectorAll("a#"+u+"+*").length||q.push(".#.+[+~]")}),ja(function(a){a.innerHTML="";var b=n.createElement("input");b.setAttribute("type","hidden"),a.appendChild(b).setAttribute("name","D"),a.querySelectorAll("[name=d]").length&&q.push("name"+K+"*[*^$|!~]?="),2!==a.querySelectorAll(":enabled").length&&q.push(":enabled",":disabled"),o.appendChild(a).disabled=!0,2!==a.querySelectorAll(":disabled").length&&q.push(":enabled",":disabled"),a.querySelectorAll("*,:x"),q.push(",.*:")})),(c.matchesSelector=Y.test(s=o.matches||o.webkitMatchesSelector||o.mozMatchesSelector||o.oMatchesSelector||o.msMatchesSelector))&&ja(function(a){c.disconnectedMatch=s.call(a,"*"),s.call(a,"[s!='']:x"),r.push("!=",N)}),q=q.length&&new RegExp(q.join("|")),r=r.length&&new RegExp(r.join("|")),b=Y.test(o.compareDocumentPosition),t=b||Y.test(o.contains)?function(a,b){var c=9===a.nodeType?a.documentElement:a,d=b&&b.parentNode;return a===d||!(!d||1!==d.nodeType||!(c.contains?c.contains(d):a.compareDocumentPosition&&16&a.compareDocumentPosition(d)))}:function(a,b){if(b)while(b=b.parentNode)if(b===a)return!0;return!1},B=b?function(a,b){if(a===b)return l=!0,0;var d=!a.compareDocumentPosition-!b.compareDocumentPosition;return d?d:(d=(a.ownerDocument||a)===(b.ownerDocument||b)?a.compareDocumentPosition(b):1,1&d||!c.sortDetached&&b.compareDocumentPosition(a)===d?a===n||a.ownerDocument===v&&t(v,a)?-1:b===n||b.ownerDocument===v&&t(v,b)?1:k?I(k,a)-I(k,b):0:4&d?-1:1)}:function(a,b){if(a===b)return l=!0,0;var c,d=0,e=a.parentNode,f=b.parentNode,g=[a],h=[b];if(!e||!f)return a===n?-1:b===n?1:e?-1:f?1:k?I(k,a)-I(k,b):0;if(e===f)return la(a,b);c=a;while(c=c.parentNode)g.unshift(c);c=b;while(c=c.parentNode)h.unshift(c);while(g[d]===h[d])d++;return d?la(g[d],h[d]):g[d]===v?-1:h[d]===v?1:0},n):n},ga.matches=function(a,b){return ga(a,null,null,b)},ga.matchesSelector=function(a,b){if((a.ownerDocument||a)!==n&&m(a),b=b.replace(S,"='$1']"),c.matchesSelector&&p&&!A[b+" "]&&(!r||!r.test(b))&&(!q||!q.test(b)))try{var d=s.call(a,b);if(d||c.disconnectedMatch||a.document&&11!==a.document.nodeType)return d}catch(e){}return ga(b,n,null,[a]).length>0},ga.contains=function(a,b){return(a.ownerDocument||a)!==n&&m(a),t(a,b)},ga.attr=function(a,b){(a.ownerDocument||a)!==n&&m(a);var e=d.attrHandle[b.toLowerCase()],f=e&&C.call(d.attrHandle,b.toLowerCase())?e(a,b,!p):void 0;return void 0!==f?f:c.attributes||!p?a.getAttribute(b):(f=a.getAttributeNode(b))&&f.specified?f.value:null},ga.escape=function(a){return(a+"").replace(ba,ca)},ga.error=function(a){throw new Error("Syntax error, unrecognized expression: "+a)},ga.uniqueSort=function(a){var b,d=[],e=0,f=0;if(l=!c.detectDuplicates,k=!c.sortStable&&a.slice(0),a.sort(B),l){while(b=a[f++])b===a[f]&&(e=d.push(f));while(e--)a.splice(d[e],1)}return k=null,a},e=ga.getText=function(a){var b,c="",d=0,f=a.nodeType;if(f){if(1===f||9===f||11===f){if("string"==typeof a.textContent)return a.textContent;for(a=a.firstChild;a;a=a.nextSibling)c+=e(a)}else if(3===f||4===f)return a.nodeValue}else while(b=a[d++])c+=e(b);return c},d=ga.selectors={cacheLength:50,createPseudo:ia,match:V,attrHandle:{},find:{},relative:{">":{dir:"parentNode",first:!0}," ":{dir:"parentNode"},"+":{dir:"previousSibling",first:!0},"~":{dir:"previousSibling"}},preFilter:{ATTR:function(a){return a[1]=a[1].replace(_,aa),a[3]=(a[3]||a[4]||a[5]||"").replace(_,aa),"~="===a[2]&&(a[3]=" "+a[3]+" "),a.slice(0,4)},CHILD:function(a){return a[1]=a[1].toLowerCase(),"nth"===a[1].slice(0,3)?(a[3]||ga.error(a[0]),a[4]=+(a[4]?a[5]+(a[6]||1):2*("even"===a[3]||"odd"===a[3])),a[5]=+(a[7]+a[8]||"odd"===a[3])):a[3]&&ga.error(a[0]),a},PSEUDO:function(a){var b,c=!a[6]&&a[2];return V.CHILD.test(a[0])?null:(a[3]?a[2]=a[4]||a[5]||"":c&&T.test(c)&&(b=g(c,!0))&&(b=c.indexOf(")",c.length-b)-c.length)&&(a[0]=a[0].slice(0,b),a[2]=c.slice(0,b)),a.slice(0,3))}},filter:{TAG:function(a){var b=a.replace(_,aa).toLowerCase();return"*"===a?function(){return!0}:function(a){return a.nodeName&&a.nodeName.toLowerCase()===b}},CLASS:function(a){var b=y[a+" "];return b||(b=new RegExp("(^|"+K+")"+a+"("+K+"|$)"))&&y(a,function(a){return b.test("string"==typeof a.className&&a.className||"undefined"!=typeof a.getAttribute&&a.getAttribute("class")||"")})},ATTR:function(a,b,c){return function(d){var e=ga.attr(d,a);return null==e?"!="===b:!b||(e+="","="===b?e===c:"!="===b?e!==c:"^="===b?c&&0===e.indexOf(c):"*="===b?c&&e.indexOf(c)>-1:"$="===b?c&&e.slice(-c.length)===c:"~="===b?(" "+e.replace(O," ")+" ").indexOf(c)>-1:"|="===b&&(e===c||e.slice(0,c.length+1)===c+"-"))}},CHILD:function(a,b,c,d,e){var f="nth"!==a.slice(0,3),g="last"!==a.slice(-4),h="of-type"===b;return 1===d&&0===e?function(a){return!!a.parentNode}:function(b,c,i){var j,k,l,m,n,o,p=f!==g?"nextSibling":"previousSibling",q=b.parentNode,r=h&&b.nodeName.toLowerCase(),s=!i&&!h,t=!1;if(q){if(f){while(p){m=b;while(m=m[p])if(h?m.nodeName.toLowerCase()===r:1===m.nodeType)return!1;o=p="only"===a&&!o&&"nextSibling"}return!0}if(o=[g?q.firstChild:q.lastChild],g&&s){m=q,l=m[u]||(m[u]={}),k=l[m.uniqueID]||(l[m.uniqueID]={}),j=k[a]||[],n=j[0]===w&&j[1],t=n&&j[2],m=n&&q.childNodes[n];while(m=++n&&m&&m[p]||(t=n=0)||o.pop())if(1===m.nodeType&&++t&&m===b){k[a]=[w,n,t];break}}else if(s&&(m=b,l=m[u]||(m[u]={}),k=l[m.uniqueID]||(l[m.uniqueID]={}),j=k[a]||[],n=j[0]===w&&j[1],t=n),t===!1)while(m=++n&&m&&m[p]||(t=n=0)||o.pop())if((h?m.nodeName.toLowerCase()===r:1===m.nodeType)&&++t&&(s&&(l=m[u]||(m[u]={}),k=l[m.uniqueID]||(l[m.uniqueID]={}),k[a]=[w,t]),m===b))break;return t-=e,t===d||t%d===0&&t/d>=0}}},PSEUDO:function(a,b){var c,e=d.pseudos[a]||d.setFilters[a.toLowerCase()]||ga.error("unsupported pseudo: "+a);return e[u]?e(b):e.length>1?(c=[a,a,"",b],d.setFilters.hasOwnProperty(a.toLowerCase())?ia(function(a,c){var d,f=e(a,b),g=f.length;while(g--)d=I(a,f[g]),a[d]=!(c[d]=f[g])}):function(a){return e(a,0,c)}):e}},pseudos:{not:ia(function(a){var b=[],c=[],d=h(a.replace(P,"$1"));return d[u]?ia(function(a,b,c,e){var f,g=d(a,null,e,[]),h=a.length;while(h--)(f=g[h])&&(a[h]=!(b[h]=f))}):function(a,e,f){return b[0]=a,d(b,null,f,c),b[0]=null,!c.pop()}}),has:ia(function(a){return function(b){return ga(a,b).length>0}}),contains:ia(function(a){return a=a.replace(_,aa),function(b){return(b.textContent||b.innerText||e(b)).indexOf(a)>-1}}),lang:ia(function(a){return U.test(a||"")||ga.error("unsupported lang: "+a),a=a.replace(_,aa).toLowerCase(),function(b){var c;do if(c=p?b.lang:b.getAttribute("xml:lang")||b.getAttribute("lang"))return c=c.toLowerCase(),c===a||0===c.indexOf(a+"-");while((b=b.parentNode)&&1===b.nodeType);return!1}}),target:function(b){var c=a.location&&a.location.hash;return c&&c.slice(1)===b.id},root:function(a){return a===o},focus:function(a){return a===n.activeElement&&(!n.hasFocus||n.hasFocus())&&!!(a.type||a.href||~a.tabIndex)},enabled:oa(!1),disabled:oa(!0),checked:function(a){var b=a.nodeName.toLowerCase();return"input"===b&&!!a.checked||"option"===b&&!!a.selected},selected:function(a){return a.parentNode&&a.parentNode.selectedIndex,a.selected===!0},empty:function(a){for(a=a.firstChild;a;a=a.nextSibling)if(a.nodeType<6)return!1;return!0},parent:function(a){return!d.pseudos.empty(a)},header:function(a){return X.test(a.nodeName)},input:function(a){return W.test(a.nodeName)},button:function(a){var b=a.nodeName.toLowerCase();return"input"===b&&"button"===a.type||"button"===b},text:function(a){var b;return"input"===a.nodeName.toLowerCase()&&"text"===a.type&&(null==(b=a.getAttribute("type"))||"text"===b.toLowerCase())},first:pa(function(){return[0]}),last:pa(function(a,b){return[b-1]}),eq:pa(function(a,b,c){return[c<0?c+b:c]}),even:pa(function(a,b){for(var c=0;c=0;)a.push(d);return a}),gt:pa(function(a,b,c){for(var d=c<0?c+b:c;++d1?function(b,c,d){var e=a.length;while(e--)if(!a[e](b,c,d))return!1;return!0}:a[0]}function va(a,b,c){for(var d=0,e=b.length;d-1&&(f[j]=!(g[j]=l))}}else r=wa(r===g?r.splice(o,r.length):r),e?e(null,g,r,i):G.apply(g,r)})}function ya(a){for(var b,c,e,f=a.length,g=d.relative[a[0].type],h=g||d.relative[" "],i=g?1:0,k=ta(function(a){return a===b},h,!0),l=ta(function(a){return I(b,a)>-1},h,!0),m=[function(a,c,d){var e=!g&&(d||c!==j)||((b=c).nodeType?k(a,c,d):l(a,c,d));return b=null,e}];i1&&ua(m),i>1&&sa(a.slice(0,i-1).concat({value:" "===a[i-2].type?"*":""})).replace(P,"$1"),c,i0,e=a.length>0,f=function(f,g,h,i,k){var l,o,q,r=0,s="0",t=f&&[],u=[],v=j,x=f||e&&d.find.TAG("*",k),y=w+=null==v?1:Math.random()||.1,z=x.length;for(k&&(j=g===n||g||k);s!==z&&null!=(l=x[s]);s++){if(e&&l){o=0,g||l.ownerDocument===n||(m(l),h=!p);while(q=a[o++])if(q(l,g||n,h)){i.push(l);break}k&&(w=y)}c&&((l=!q&&l)&&r--,f&&t.push(l))}if(r+=s,c&&s!==r){o=0;while(q=b[o++])q(t,u,g,h);if(f){if(r>0)while(s--)t[s]||u[s]||(u[s]=E.call(i));u=wa(u)}G.apply(i,u),k&&!f&&u.length>0&&r+b.length>1&&ga.uniqueSort(i)}return k&&(w=y,j=v),t};return c?ia(f):f}return h=ga.compile=function(a,b){var c,d=[],e=[],f=A[a+" "];if(!f){b||(b=g(a)),c=b.length;while(c--)f=ya(b[c]),f[u]?d.push(f):e.push(f);f=A(a,za(e,d)),f.selector=a}return f},i=ga.select=function(a,b,c,e){var f,i,j,k,l,m="function"==typeof a&&a,n=!e&&g(a=m.selector||a);if(c=c||[],1===n.length){if(i=n[0]=n[0].slice(0),i.length>2&&"ID"===(j=i[0]).type&&9===b.nodeType&&p&&d.relative[i[1].type]){if(b=(d.find.ID(j.matches[0].replace(_,aa),b)||[])[0],!b)return c;m&&(b=b.parentNode),a=a.slice(i.shift().value.length)}f=V.needsContext.test(a)?0:i.length;while(f--){if(j=i[f],d.relative[k=j.type])break;if((l=d.find[k])&&(e=l(j.matches[0].replace(_,aa),$.test(i[0].type)&&qa(b.parentNode)||b))){if(i.splice(f,1),a=e.length&&sa(i),!a)return G.apply(c,e),c;break}}}return(m||h(a,n))(e,b,!p,c,!b||$.test(a)&&qa(b.parentNode)||b),c},c.sortStable=u.split("").sort(B).join("")===u,c.detectDuplicates=!!l,m(),c.sortDetached=ja(function(a){return 1&a.compareDocumentPosition(n.createElement("fieldset"))}),ja(function(a){return a.innerHTML="","#"===a.firstChild.getAttribute("href")})||ka("type|href|height|width",function(a,b,c){if(!c)return a.getAttribute(b,"type"===b.toLowerCase()?1:2)}),c.attributes&&ja(function(a){return a.innerHTML="",a.firstChild.setAttribute("value",""),""===a.firstChild.getAttribute("value")})||ka("value",function(a,b,c){if(!c&&"input"===a.nodeName.toLowerCase())return a.defaultValue}),ja(function(a){return null==a.getAttribute("disabled")})||ka(J,function(a,b,c){var d;if(!c)return a[b]===!0?b.toLowerCase():(d=a.getAttributeNode(b))&&d.specified?d.value:null}),ga}(a);r.find=x,r.expr=x.selectors,r.expr[":"]=r.expr.pseudos,r.uniqueSort=r.unique=x.uniqueSort,r.text=x.getText,r.isXMLDoc=x.isXML,r.contains=x.contains,r.escapeSelector=x.escape;var y=function(a,b,c){var d=[],e=void 0!==c;while((a=a[b])&&9!==a.nodeType)if(1===a.nodeType){if(e&&r(a).is(c))break;d.push(a)}return d},z=function(a,b){for(var c=[];a;a=a.nextSibling)1===a.nodeType&&a!==b&&c.push(a);return c},A=r.expr.match.needsContext;function B(a,b){return a.nodeName&&a.nodeName.toLowerCase()===b.toLowerCase()}var C=/^<([a-z][^\/\0>:\x20\t\r\n\f]*)[\x20\t\r\n\f]*\/?>(?:<\/\1>|)$/i,D=/^.[^:#\[\.,]*$/;function E(a,b,c){return r.isFunction(b)?r.grep(a,function(a,d){return!!b.call(a,d,a)!==c}):b.nodeType?r.grep(a,function(a){return a===b!==c}):"string"!=typeof b?r.grep(a,function(a){return i.call(b,a)>-1!==c}):D.test(b)?r.filter(b,a,c):(b=r.filter(b,a),r.grep(a,function(a){return i.call(b,a)>-1!==c&&1===a.nodeType}))}r.filter=function(a,b,c){var d=b[0];return c&&(a=":not("+a+")"),1===b.length&&1===d.nodeType?r.find.matchesSelector(d,a)?[d]:[]:r.find.matches(a,r.grep(b,function(a){return 1===a.nodeType}))},r.fn.extend({find:function(a){var b,c,d=this.length,e=this;if("string"!=typeof a)return this.pushStack(r(a).filter(function(){for(b=0;b1?r.uniqueSort(c):c},filter:function(a){return this.pushStack(E(this,a||[],!1))},not:function(a){return this.pushStack(E(this,a||[],!0))},is:function(a){return!!E(this,"string"==typeof a&&A.test(a)?r(a):a||[],!1).length}});var F,G=/^(?:\s*(<[\w\W]+>)[^>]*|#([\w-]+))$/,H=r.fn.init=function(a,b,c){var e,f;if(!a)return this;if(c=c||F,"string"==typeof a){if(e="<"===a[0]&&">"===a[a.length-1]&&a.length>=3?[null,a,null]:G.exec(a),!e||!e[1]&&b)return!b||b.jquery?(b||c).find(a):this.constructor(b).find(a);if(e[1]){if(b=b instanceof r?b[0]:b,r.merge(this,r.parseHTML(e[1],b&&b.nodeType?b.ownerDocument||b:d,!0)),C.test(e[1])&&r.isPlainObject(b))for(e in b)r.isFunction(this[e])?this[e](b[e]):this.attr(e,b[e]);return this}return f=d.getElementById(e[2]),f&&(this[0]=f,this.length=1),this}return a.nodeType?(this[0]=a,this.length=1,this):r.isFunction(a)?void 0!==c.ready?c.ready(a):a(r):r.makeArray(a,this)};H.prototype=r.fn,F=r(d);var I=/^(?:parents|prev(?:Until|All))/,J={children:!0,contents:!0,next:!0,prev:!0};r.fn.extend({has:function(a){var b=r(a,this),c=b.length;return this.filter(function(){for(var a=0;a-1:1===c.nodeType&&r.find.matchesSelector(c,a))){f.push(c);break}return this.pushStack(f.length>1?r.uniqueSort(f):f)},index:function(a){return a?"string"==typeof a?i.call(r(a),this[0]):i.call(this,a.jquery?a[0]:a):this[0]&&this[0].parentNode?this.first().prevAll().length:-1},add:function(a,b){return this.pushStack(r.uniqueSort(r.merge(this.get(),r(a,b))))},addBack:function(a){return this.add(null==a?this.prevObject:this.prevObject.filter(a))}});function K(a,b){while((a=a[b])&&1!==a.nodeType);return a}r.each({parent:function(a){var b=a.parentNode;return b&&11!==b.nodeType?b:null},parents:function(a){return y(a,"parentNode")},parentsUntil:function(a,b,c){return y(a,"parentNode",c)},next:function(a){return K(a,"nextSibling")},prev:function(a){return K(a,"previousSibling")},nextAll:function(a){return y(a,"nextSibling")},prevAll:function(a){return y(a,"previousSibling")},nextUntil:function(a,b,c){return y(a,"nextSibling",c)},prevUntil:function(a,b,c){return y(a,"previousSibling",c)},siblings:function(a){return z((a.parentNode||{}).firstChild,a)},children:function(a){return z(a.firstChild)},contents:function(a){return B(a,"iframe")?a.contentDocument:(B(a,"template")&&(a=a.content||a),r.merge([],a.childNodes))}},function(a,b){r.fn[a]=function(c,d){var e=r.map(this,b,c);return"Until"!==a.slice(-5)&&(d=c),d&&"string"==typeof d&&(e=r.filter(d,e)),this.length>1&&(J[a]||r.uniqueSort(e),I.test(a)&&e.reverse()),this.pushStack(e)}});var L=/[^\x20\t\r\n\f]+/g;function M(a){var b={};return r.each(a.match(L)||[],function(a,c){b[c]=!0}),b}r.Callbacks=function(a){a="string"==typeof a?M(a):r.extend({},a);var b,c,d,e,f=[],g=[],h=-1,i=function(){for(e=e||a.once,d=b=!0;g.length;h=-1){c=g.shift();while(++h-1)f.splice(c,1),c<=h&&h--}),this},has:function(a){return a?r.inArray(a,f)>-1:f.length>0},empty:function(){return f&&(f=[]),this},disable:function(){return e=g=[],f=c="",this},disabled:function(){return!f},lock:function(){return e=g=[],c||b||(f=c=""),this},locked:function(){return!!e},fireWith:function(a,c){return e||(c=c||[],c=[a,c.slice?c.slice():c],g.push(c),b||i()),this},fire:function(){return j.fireWith(this,arguments),this},fired:function(){return!!d}};return j};function N(a){return a}function O(a){throw a}function P(a,b,c,d){var e;try{a&&r.isFunction(e=a.promise)?e.call(a).done(b).fail(c):a&&r.isFunction(e=a.then)?e.call(a,b,c):b.apply(void 0,[a].slice(d))}catch(a){c.apply(void 0,[a])}}r.extend({Deferred:function(b){var c=[["notify","progress",r.Callbacks("memory"),r.Callbacks("memory"),2],["resolve","done",r.Callbacks("once memory"),r.Callbacks("once memory"),0,"resolved"],["reject","fail",r.Callbacks("once memory"),r.Callbacks("once memory"),1,"rejected"]],d="pending",e={state:function(){return d},always:function(){return f.done(arguments).fail(arguments),this},"catch":function(a){return e.then(null,a)},pipe:function(){var a=arguments;return r.Deferred(function(b){r.each(c,function(c,d){var e=r.isFunction(a[d[4]])&&a[d[4]];f[d[1]](function(){var a=e&&e.apply(this,arguments);a&&r.isFunction(a.promise)?a.promise().progress(b.notify).done(b.resolve).fail(b.reject):b[d[0]+"With"](this,e?[a]:arguments)})}),a=null}).promise()},then:function(b,d,e){var f=0;function g(b,c,d,e){return function(){var h=this,i=arguments,j=function(){var a,j;if(!(b=f&&(d!==O&&(h=void 0,i=[a]),c.rejectWith(h,i))}};b?k():(r.Deferred.getStackHook&&(k.stackTrace=r.Deferred.getStackHook()),a.setTimeout(k))}}return r.Deferred(function(a){c[0][3].add(g(0,a,r.isFunction(e)?e:N,a.notifyWith)),c[1][3].add(g(0,a,r.isFunction(b)?b:N)),c[2][3].add(g(0,a,r.isFunction(d)?d:O))}).promise()},promise:function(a){return null!=a?r.extend(a,e):e}},f={};return r.each(c,function(a,b){var g=b[2],h=b[5];e[b[1]]=g.add,h&&g.add(function(){d=h},c[3-a][2].disable,c[0][2].lock),g.add(b[3].fire),f[b[0]]=function(){return f[b[0]+"With"](this===f?void 0:this,arguments),this},f[b[0]+"With"]=g.fireWith}),e.promise(f),b&&b.call(f,f),f},when:function(a){var b=arguments.length,c=b,d=Array(c),e=f.call(arguments),g=r.Deferred(),h=function(a){return function(c){d[a]=this,e[a]=arguments.length>1?f.call(arguments):c,--b||g.resolveWith(d,e)}};if(b<=1&&(P(a,g.done(h(c)).resolve,g.reject,!b),"pending"===g.state()||r.isFunction(e[c]&&e[c].then)))return g.then();while(c--)P(e[c],h(c),g.reject);return g.promise()}});var Q=/^(Eval|Internal|Range|Reference|Syntax|Type|URI)Error$/;r.Deferred.exceptionHook=function(b,c){a.console&&a.console.warn&&b&&Q.test(b.name)&&a.console.warn("jQuery.Deferred exception: "+b.message,b.stack,c)},r.readyException=function(b){a.setTimeout(function(){throw b})};var R=r.Deferred();r.fn.ready=function(a){return R.then(a)["catch"](function(a){r.readyException(a)}),this},r.extend({isReady:!1,readyWait:1,ready:function(a){(a===!0?--r.readyWait:r.isReady)||(r.isReady=!0,a!==!0&&--r.readyWait>0||R.resolveWith(d,[r]))}}),r.ready.then=R.then;function S(){d.removeEventListener("DOMContentLoaded",S), -a.removeEventListener("load",S),r.ready()}"complete"===d.readyState||"loading"!==d.readyState&&!d.documentElement.doScroll?a.setTimeout(r.ready):(d.addEventListener("DOMContentLoaded",S),a.addEventListener("load",S));var T=function(a,b,c,d,e,f,g){var h=0,i=a.length,j=null==c;if("object"===r.type(c)){e=!0;for(h in c)T(a,b,h,c[h],!0,f,g)}else if(void 0!==d&&(e=!0,r.isFunction(d)||(g=!0),j&&(g?(b.call(a,d),b=null):(j=b,b=function(a,b,c){return j.call(r(a),c)})),b))for(;h1,null,!0)},removeData:function(a){return this.each(function(){X.remove(this,a)})}}),r.extend({queue:function(a,b,c){var d;if(a)return b=(b||"fx")+"queue",d=W.get(a,b),c&&(!d||Array.isArray(c)?d=W.access(a,b,r.makeArray(c)):d.push(c)),d||[]},dequeue:function(a,b){b=b||"fx";var c=r.queue(a,b),d=c.length,e=c.shift(),f=r._queueHooks(a,b),g=function(){r.dequeue(a,b)};"inprogress"===e&&(e=c.shift(),d--),e&&("fx"===b&&c.unshift("inprogress"),delete f.stop,e.call(a,g,f)),!d&&f&&f.empty.fire()},_queueHooks:function(a,b){var c=b+"queueHooks";return W.get(a,c)||W.access(a,c,{empty:r.Callbacks("once memory").add(function(){W.remove(a,[b+"queue",c])})})}}),r.fn.extend({queue:function(a,b){var c=2;return"string"!=typeof a&&(b=a,a="fx",c--),arguments.length\x20\t\r\n\f]+)/i,la=/^$|\/(?:java|ecma)script/i,ma={option:[1,""],thead:[1,"","
"],col:[2,"","
"],tr:[2,"","
"],td:[3,"","
"],_default:[0,"",""]};ma.optgroup=ma.option,ma.tbody=ma.tfoot=ma.colgroup=ma.caption=ma.thead,ma.th=ma.td;function na(a,b){var c;return c="undefined"!=typeof a.getElementsByTagName?a.getElementsByTagName(b||"*"):"undefined"!=typeof a.querySelectorAll?a.querySelectorAll(b||"*"):[],void 0===b||b&&B(a,b)?r.merge([a],c):c}function oa(a,b){for(var c=0,d=a.length;c-1)e&&e.push(f);else if(j=r.contains(f.ownerDocument,f),g=na(l.appendChild(f),"script"),j&&oa(g),c){k=0;while(f=g[k++])la.test(f.type||"")&&c.push(f)}return l}!function(){var a=d.createDocumentFragment(),b=a.appendChild(d.createElement("div")),c=d.createElement("input");c.setAttribute("type","radio"),c.setAttribute("checked","checked"),c.setAttribute("name","t"),b.appendChild(c),o.checkClone=b.cloneNode(!0).cloneNode(!0).lastChild.checked,b.innerHTML="",o.noCloneChecked=!!b.cloneNode(!0).lastChild.defaultValue}();var ra=d.documentElement,sa=/^key/,ta=/^(?:mouse|pointer|contextmenu|drag|drop)|click/,ua=/^([^.]*)(?:\.(.+)|)/;function va(){return!0}function wa(){return!1}function xa(){try{return d.activeElement}catch(a){}}function ya(a,b,c,d,e,f){var g,h;if("object"==typeof b){"string"!=typeof c&&(d=d||c,c=void 0);for(h in b)ya(a,h,c,d,b[h],f);return a}if(null==d&&null==e?(e=c,d=c=void 0):null==e&&("string"==typeof c?(e=d,d=void 0):(e=d,d=c,c=void 0)),e===!1)e=wa;else if(!e)return a;return 1===f&&(g=e,e=function(a){return r().off(a),g.apply(this,arguments)},e.guid=g.guid||(g.guid=r.guid++)),a.each(function(){r.event.add(this,b,e,d,c)})}r.event={global:{},add:function(a,b,c,d,e){var f,g,h,i,j,k,l,m,n,o,p,q=W.get(a);if(q){c.handler&&(f=c,c=f.handler,e=f.selector),e&&r.find.matchesSelector(ra,e),c.guid||(c.guid=r.guid++),(i=q.events)||(i=q.events={}),(g=q.handle)||(g=q.handle=function(b){return"undefined"!=typeof r&&r.event.triggered!==b.type?r.event.dispatch.apply(a,arguments):void 0}),b=(b||"").match(L)||[""],j=b.length;while(j--)h=ua.exec(b[j])||[],n=p=h[1],o=(h[2]||"").split(".").sort(),n&&(l=r.event.special[n]||{},n=(e?l.delegateType:l.bindType)||n,l=r.event.special[n]||{},k=r.extend({type:n,origType:p,data:d,handler:c,guid:c.guid,selector:e,needsContext:e&&r.expr.match.needsContext.test(e),namespace:o.join(".")},f),(m=i[n])||(m=i[n]=[],m.delegateCount=0,l.setup&&l.setup.call(a,d,o,g)!==!1||a.addEventListener&&a.addEventListener(n,g)),l.add&&(l.add.call(a,k),k.handler.guid||(k.handler.guid=c.guid)),e?m.splice(m.delegateCount++,0,k):m.push(k),r.event.global[n]=!0)}},remove:function(a,b,c,d,e){var f,g,h,i,j,k,l,m,n,o,p,q=W.hasData(a)&&W.get(a);if(q&&(i=q.events)){b=(b||"").match(L)||[""],j=b.length;while(j--)if(h=ua.exec(b[j])||[],n=p=h[1],o=(h[2]||"").split(".").sort(),n){l=r.event.special[n]||{},n=(d?l.delegateType:l.bindType)||n,m=i[n]||[],h=h[2]&&new RegExp("(^|\\.)"+o.join("\\.(?:.*\\.|)")+"(\\.|$)"),g=f=m.length;while(f--)k=m[f],!e&&p!==k.origType||c&&c.guid!==k.guid||h&&!h.test(k.namespace)||d&&d!==k.selector&&("**"!==d||!k.selector)||(m.splice(f,1),k.selector&&m.delegateCount--,l.remove&&l.remove.call(a,k));g&&!m.length&&(l.teardown&&l.teardown.call(a,o,q.handle)!==!1||r.removeEvent(a,n,q.handle),delete i[n])}else for(n in i)r.event.remove(a,n+b[j],c,d,!0);r.isEmptyObject(i)&&W.remove(a,"handle events")}},dispatch:function(a){var b=r.event.fix(a),c,d,e,f,g,h,i=new Array(arguments.length),j=(W.get(this,"events")||{})[b.type]||[],k=r.event.special[b.type]||{};for(i[0]=b,c=1;c=1))for(;j!==this;j=j.parentNode||this)if(1===j.nodeType&&("click"!==a.type||j.disabled!==!0)){for(f=[],g={},c=0;c-1:r.find(e,this,null,[j]).length),g[e]&&f.push(d);f.length&&h.push({elem:j,handlers:f})}return j=this,i\x20\t\r\n\f]*)[^>]*)\/>/gi,Aa=/\s*$/g;function Ea(a,b){return B(a,"table")&&B(11!==b.nodeType?b:b.firstChild,"tr")?r(">tbody",a)[0]||a:a}function Fa(a){return a.type=(null!==a.getAttribute("type"))+"/"+a.type,a}function Ga(a){var b=Ca.exec(a.type);return b?a.type=b[1]:a.removeAttribute("type"),a}function Ha(a,b){var c,d,e,f,g,h,i,j;if(1===b.nodeType){if(W.hasData(a)&&(f=W.access(a),g=W.set(b,f),j=f.events)){delete g.handle,g.events={};for(e in j)for(c=0,d=j[e].length;c1&&"string"==typeof q&&!o.checkClone&&Ba.test(q))return a.each(function(e){var f=a.eq(e);s&&(b[0]=q.call(this,e,f.html())),Ja(f,b,c,d)});if(m&&(e=qa(b,a[0].ownerDocument,!1,a,d),f=e.firstChild,1===e.childNodes.length&&(e=f),f||d)){for(h=r.map(na(e,"script"),Fa),i=h.length;l")},clone:function(a,b,c){var d,e,f,g,h=a.cloneNode(!0),i=r.contains(a.ownerDocument,a);if(!(o.noCloneChecked||1!==a.nodeType&&11!==a.nodeType||r.isXMLDoc(a)))for(g=na(h),f=na(a),d=0,e=f.length;d0&&oa(g,!i&&na(a,"script")),h},cleanData:function(a){for(var b,c,d,e=r.event.special,f=0;void 0!==(c=a[f]);f++)if(U(c)){if(b=c[W.expando]){if(b.events)for(d in b.events)e[d]?r.event.remove(c,d):r.removeEvent(c,d,b.handle);c[W.expando]=void 0}c[X.expando]&&(c[X.expando]=void 0)}}}),r.fn.extend({detach:function(a){return Ka(this,a,!0)},remove:function(a){return Ka(this,a)},text:function(a){return T(this,function(a){return void 0===a?r.text(this):this.empty().each(function(){1!==this.nodeType&&11!==this.nodeType&&9!==this.nodeType||(this.textContent=a)})},null,a,arguments.length)},append:function(){return Ja(this,arguments,function(a){if(1===this.nodeType||11===this.nodeType||9===this.nodeType){var b=Ea(this,a);b.appendChild(a)}})},prepend:function(){return Ja(this,arguments,function(a){if(1===this.nodeType||11===this.nodeType||9===this.nodeType){var b=Ea(this,a);b.insertBefore(a,b.firstChild)}})},before:function(){return Ja(this,arguments,function(a){this.parentNode&&this.parentNode.insertBefore(a,this)})},after:function(){return Ja(this,arguments,function(a){this.parentNode&&this.parentNode.insertBefore(a,this.nextSibling)})},empty:function(){for(var a,b=0;null!=(a=this[b]);b++)1===a.nodeType&&(r.cleanData(na(a,!1)),a.textContent="");return this},clone:function(a,b){return a=null!=a&&a,b=null==b?a:b,this.map(function(){return r.clone(this,a,b)})},html:function(a){return T(this,function(a){var b=this[0]||{},c=0,d=this.length;if(void 0===a&&1===b.nodeType)return b.innerHTML;if("string"==typeof a&&!Aa.test(a)&&!ma[(ka.exec(a)||["",""])[1].toLowerCase()]){a=r.htmlPrefilter(a);try{for(;c1)}});function _a(a,b,c,d,e){return new _a.prototype.init(a,b,c,d,e)}r.Tween=_a,_a.prototype={constructor:_a,init:function(a,b,c,d,e,f){this.elem=a,this.prop=c,this.easing=e||r.easing._default,this.options=b,this.start=this.now=this.cur(),this.end=d,this.unit=f||(r.cssNumber[c]?"":"px")},cur:function(){var a=_a.propHooks[this.prop];return a&&a.get?a.get(this):_a.propHooks._default.get(this)},run:function(a){var b,c=_a.propHooks[this.prop];return this.options.duration?this.pos=b=r.easing[this.easing](a,this.options.duration*a,0,1,this.options.duration):this.pos=b=a,this.now=(this.end-this.start)*b+this.start,this.options.step&&this.options.step.call(this.elem,this.now,this),c&&c.set?c.set(this):_a.propHooks._default.set(this),this}},_a.prototype.init.prototype=_a.prototype,_a.propHooks={_default:{get:function(a){var b;return 1!==a.elem.nodeType||null!=a.elem[a.prop]&&null==a.elem.style[a.prop]?a.elem[a.prop]:(b=r.css(a.elem,a.prop,""),b&&"auto"!==b?b:0)},set:function(a){r.fx.step[a.prop]?r.fx.step[a.prop](a):1!==a.elem.nodeType||null==a.elem.style[r.cssProps[a.prop]]&&!r.cssHooks[a.prop]?a.elem[a.prop]=a.now:r.style(a.elem,a.prop,a.now+a.unit)}}},_a.propHooks.scrollTop=_a.propHooks.scrollLeft={set:function(a){a.elem.nodeType&&a.elem.parentNode&&(a.elem[a.prop]=a.now)}},r.easing={linear:function(a){return a},swing:function(a){return.5-Math.cos(a*Math.PI)/2},_default:"swing"},r.fx=_a.prototype.init,r.fx.step={};var ab,bb,cb=/^(?:toggle|show|hide)$/,db=/queueHooks$/;function eb(){bb&&(d.hidden===!1&&a.requestAnimationFrame?a.requestAnimationFrame(eb):a.setTimeout(eb,r.fx.interval),r.fx.tick())}function fb(){return a.setTimeout(function(){ab=void 0}),ab=r.now()}function gb(a,b){var c,d=0,e={height:a};for(b=b?1:0;d<4;d+=2-b)c=ca[d],e["margin"+c]=e["padding"+c]=a;return b&&(e.opacity=e.width=a),e}function hb(a,b,c){for(var d,e=(kb.tweeners[b]||[]).concat(kb.tweeners["*"]),f=0,g=e.length;f1)},removeAttr:function(a){return this.each(function(){r.removeAttr(this,a)})}}),r.extend({attr:function(a,b,c){var d,e,f=a.nodeType;if(3!==f&&8!==f&&2!==f)return"undefined"==typeof a.getAttribute?r.prop(a,b,c):(1===f&&r.isXMLDoc(a)||(e=r.attrHooks[b.toLowerCase()]||(r.expr.match.bool.test(b)?lb:void 0)),void 0!==c?null===c?void r.removeAttr(a,b):e&&"set"in e&&void 0!==(d=e.set(a,c,b))?d:(a.setAttribute(b,c+""),c):e&&"get"in e&&null!==(d=e.get(a,b))?d:(d=r.find.attr(a,b), -null==d?void 0:d))},attrHooks:{type:{set:function(a,b){if(!o.radioValue&&"radio"===b&&B(a,"input")){var c=a.value;return a.setAttribute("type",b),c&&(a.value=c),b}}}},removeAttr:function(a,b){var c,d=0,e=b&&b.match(L);if(e&&1===a.nodeType)while(c=e[d++])a.removeAttribute(c)}}),lb={set:function(a,b,c){return b===!1?r.removeAttr(a,c):a.setAttribute(c,c),c}},r.each(r.expr.match.bool.source.match(/\w+/g),function(a,b){var c=mb[b]||r.find.attr;mb[b]=function(a,b,d){var e,f,g=b.toLowerCase();return d||(f=mb[g],mb[g]=e,e=null!=c(a,b,d)?g:null,mb[g]=f),e}});var nb=/^(?:input|select|textarea|button)$/i,ob=/^(?:a|area)$/i;r.fn.extend({prop:function(a,b){return T(this,r.prop,a,b,arguments.length>1)},removeProp:function(a){return this.each(function(){delete this[r.propFix[a]||a]})}}),r.extend({prop:function(a,b,c){var d,e,f=a.nodeType;if(3!==f&&8!==f&&2!==f)return 1===f&&r.isXMLDoc(a)||(b=r.propFix[b]||b,e=r.propHooks[b]),void 0!==c?e&&"set"in e&&void 0!==(d=e.set(a,c,b))?d:a[b]=c:e&&"get"in e&&null!==(d=e.get(a,b))?d:a[b]},propHooks:{tabIndex:{get:function(a){var b=r.find.attr(a,"tabindex");return b?parseInt(b,10):nb.test(a.nodeName)||ob.test(a.nodeName)&&a.href?0:-1}}},propFix:{"for":"htmlFor","class":"className"}}),o.optSelected||(r.propHooks.selected={get:function(a){var b=a.parentNode;return b&&b.parentNode&&b.parentNode.selectedIndex,null},set:function(a){var b=a.parentNode;b&&(b.selectedIndex,b.parentNode&&b.parentNode.selectedIndex)}}),r.each(["tabIndex","readOnly","maxLength","cellSpacing","cellPadding","rowSpan","colSpan","useMap","frameBorder","contentEditable"],function(){r.propFix[this.toLowerCase()]=this});function pb(a){var b=a.match(L)||[];return b.join(" ")}function qb(a){return a.getAttribute&&a.getAttribute("class")||""}r.fn.extend({addClass:function(a){var b,c,d,e,f,g,h,i=0;if(r.isFunction(a))return this.each(function(b){r(this).addClass(a.call(this,b,qb(this)))});if("string"==typeof a&&a){b=a.match(L)||[];while(c=this[i++])if(e=qb(c),d=1===c.nodeType&&" "+pb(e)+" "){g=0;while(f=b[g++])d.indexOf(" "+f+" ")<0&&(d+=f+" ");h=pb(d),e!==h&&c.setAttribute("class",h)}}return this},removeClass:function(a){var b,c,d,e,f,g,h,i=0;if(r.isFunction(a))return this.each(function(b){r(this).removeClass(a.call(this,b,qb(this)))});if(!arguments.length)return this.attr("class","");if("string"==typeof a&&a){b=a.match(L)||[];while(c=this[i++])if(e=qb(c),d=1===c.nodeType&&" "+pb(e)+" "){g=0;while(f=b[g++])while(d.indexOf(" "+f+" ")>-1)d=d.replace(" "+f+" "," ");h=pb(d),e!==h&&c.setAttribute("class",h)}}return this},toggleClass:function(a,b){var c=typeof a;return"boolean"==typeof b&&"string"===c?b?this.addClass(a):this.removeClass(a):r.isFunction(a)?this.each(function(c){r(this).toggleClass(a.call(this,c,qb(this),b),b)}):this.each(function(){var b,d,e,f;if("string"===c){d=0,e=r(this),f=a.match(L)||[];while(b=f[d++])e.hasClass(b)?e.removeClass(b):e.addClass(b)}else void 0!==a&&"boolean"!==c||(b=qb(this),b&&W.set(this,"__className__",b),this.setAttribute&&this.setAttribute("class",b||a===!1?"":W.get(this,"__className__")||""))})},hasClass:function(a){var b,c,d=0;b=" "+a+" ";while(c=this[d++])if(1===c.nodeType&&(" "+pb(qb(c))+" ").indexOf(b)>-1)return!0;return!1}});var rb=/\r/g;r.fn.extend({val:function(a){var b,c,d,e=this[0];{if(arguments.length)return d=r.isFunction(a),this.each(function(c){var e;1===this.nodeType&&(e=d?a.call(this,c,r(this).val()):a,null==e?e="":"number"==typeof e?e+="":Array.isArray(e)&&(e=r.map(e,function(a){return null==a?"":a+""})),b=r.valHooks[this.type]||r.valHooks[this.nodeName.toLowerCase()],b&&"set"in b&&void 0!==b.set(this,e,"value")||(this.value=e))});if(e)return b=r.valHooks[e.type]||r.valHooks[e.nodeName.toLowerCase()],b&&"get"in b&&void 0!==(c=b.get(e,"value"))?c:(c=e.value,"string"==typeof c?c.replace(rb,""):null==c?"":c)}}}),r.extend({valHooks:{option:{get:function(a){var b=r.find.attr(a,"value");return null!=b?b:pb(r.text(a))}},select:{get:function(a){var b,c,d,e=a.options,f=a.selectedIndex,g="select-one"===a.type,h=g?null:[],i=g?f+1:e.length;for(d=f<0?i:g?f:0;d-1)&&(c=!0);return c||(a.selectedIndex=-1),f}}}}),r.each(["radio","checkbox"],function(){r.valHooks[this]={set:function(a,b){if(Array.isArray(b))return a.checked=r.inArray(r(a).val(),b)>-1}},o.checkOn||(r.valHooks[this].get=function(a){return null===a.getAttribute("value")?"on":a.value})});var sb=/^(?:focusinfocus|focusoutblur)$/;r.extend(r.event,{trigger:function(b,c,e,f){var g,h,i,j,k,m,n,o=[e||d],p=l.call(b,"type")?b.type:b,q=l.call(b,"namespace")?b.namespace.split("."):[];if(h=i=e=e||d,3!==e.nodeType&&8!==e.nodeType&&!sb.test(p+r.event.triggered)&&(p.indexOf(".")>-1&&(q=p.split("."),p=q.shift(),q.sort()),k=p.indexOf(":")<0&&"on"+p,b=b[r.expando]?b:new r.Event(p,"object"==typeof b&&b),b.isTrigger=f?2:3,b.namespace=q.join("."),b.rnamespace=b.namespace?new RegExp("(^|\\.)"+q.join("\\.(?:.*\\.|)")+"(\\.|$)"):null,b.result=void 0,b.target||(b.target=e),c=null==c?[b]:r.makeArray(c,[b]),n=r.event.special[p]||{},f||!n.trigger||n.trigger.apply(e,c)!==!1)){if(!f&&!n.noBubble&&!r.isWindow(e)){for(j=n.delegateType||p,sb.test(j+p)||(h=h.parentNode);h;h=h.parentNode)o.push(h),i=h;i===(e.ownerDocument||d)&&o.push(i.defaultView||i.parentWindow||a)}g=0;while((h=o[g++])&&!b.isPropagationStopped())b.type=g>1?j:n.bindType||p,m=(W.get(h,"events")||{})[b.type]&&W.get(h,"handle"),m&&m.apply(h,c),m=k&&h[k],m&&m.apply&&U(h)&&(b.result=m.apply(h,c),b.result===!1&&b.preventDefault());return b.type=p,f||b.isDefaultPrevented()||n._default&&n._default.apply(o.pop(),c)!==!1||!U(e)||k&&r.isFunction(e[p])&&!r.isWindow(e)&&(i=e[k],i&&(e[k]=null),r.event.triggered=p,e[p](),r.event.triggered=void 0,i&&(e[k]=i)),b.result}},simulate:function(a,b,c){var d=r.extend(new r.Event,c,{type:a,isSimulated:!0});r.event.trigger(d,null,b)}}),r.fn.extend({trigger:function(a,b){return this.each(function(){r.event.trigger(a,b,this)})},triggerHandler:function(a,b){var c=this[0];if(c)return r.event.trigger(a,b,c,!0)}}),r.each("blur focus focusin focusout resize scroll click dblclick mousedown mouseup mousemove mouseover mouseout mouseenter mouseleave change select submit keydown keypress keyup contextmenu".split(" "),function(a,b){r.fn[b]=function(a,c){return arguments.length>0?this.on(b,null,a,c):this.trigger(b)}}),r.fn.extend({hover:function(a,b){return this.mouseenter(a).mouseleave(b||a)}}),o.focusin="onfocusin"in a,o.focusin||r.each({focus:"focusin",blur:"focusout"},function(a,b){var c=function(a){r.event.simulate(b,a.target,r.event.fix(a))};r.event.special[b]={setup:function(){var d=this.ownerDocument||this,e=W.access(d,b);e||d.addEventListener(a,c,!0),W.access(d,b,(e||0)+1)},teardown:function(){var d=this.ownerDocument||this,e=W.access(d,b)-1;e?W.access(d,b,e):(d.removeEventListener(a,c,!0),W.remove(d,b))}}});var tb=a.location,ub=r.now(),vb=/\?/;r.parseXML=function(b){var c;if(!b||"string"!=typeof b)return null;try{c=(new a.DOMParser).parseFromString(b,"text/xml")}catch(d){c=void 0}return c&&!c.getElementsByTagName("parsererror").length||r.error("Invalid XML: "+b),c};var wb=/\[\]$/,xb=/\r?\n/g,yb=/^(?:submit|button|image|reset|file)$/i,zb=/^(?:input|select|textarea|keygen)/i;function Ab(a,b,c,d){var e;if(Array.isArray(b))r.each(b,function(b,e){c||wb.test(a)?d(a,e):Ab(a+"["+("object"==typeof e&&null!=e?b:"")+"]",e,c,d)});else if(c||"object"!==r.type(b))d(a,b);else for(e in b)Ab(a+"["+e+"]",b[e],c,d)}r.param=function(a,b){var c,d=[],e=function(a,b){var c=r.isFunction(b)?b():b;d[d.length]=encodeURIComponent(a)+"="+encodeURIComponent(null==c?"":c)};if(Array.isArray(a)||a.jquery&&!r.isPlainObject(a))r.each(a,function(){e(this.name,this.value)});else for(c in a)Ab(c,a[c],b,e);return d.join("&")},r.fn.extend({serialize:function(){return r.param(this.serializeArray())},serializeArray:function(){return this.map(function(){var a=r.prop(this,"elements");return a?r.makeArray(a):this}).filter(function(){var a=this.type;return this.name&&!r(this).is(":disabled")&&zb.test(this.nodeName)&&!yb.test(a)&&(this.checked||!ja.test(a))}).map(function(a,b){var c=r(this).val();return null==c?null:Array.isArray(c)?r.map(c,function(a){return{name:b.name,value:a.replace(xb,"\r\n")}}):{name:b.name,value:c.replace(xb,"\r\n")}}).get()}});var Bb=/%20/g,Cb=/#.*$/,Db=/([?&])_=[^&]*/,Eb=/^(.*?):[ \t]*([^\r\n]*)$/gm,Fb=/^(?:about|app|app-storage|.+-extension|file|res|widget):$/,Gb=/^(?:GET|HEAD)$/,Hb=/^\/\//,Ib={},Jb={},Kb="*/".concat("*"),Lb=d.createElement("a");Lb.href=tb.href;function Mb(a){return function(b,c){"string"!=typeof b&&(c=b,b="*");var d,e=0,f=b.toLowerCase().match(L)||[];if(r.isFunction(c))while(d=f[e++])"+"===d[0]?(d=d.slice(1)||"*",(a[d]=a[d]||[]).unshift(c)):(a[d]=a[d]||[]).push(c)}}function Nb(a,b,c,d){var e={},f=a===Jb;function g(h){var i;return e[h]=!0,r.each(a[h]||[],function(a,h){var j=h(b,c,d);return"string"!=typeof j||f||e[j]?f?!(i=j):void 0:(b.dataTypes.unshift(j),g(j),!1)}),i}return g(b.dataTypes[0])||!e["*"]&&g("*")}function Ob(a,b){var c,d,e=r.ajaxSettings.flatOptions||{};for(c in b)void 0!==b[c]&&((e[c]?a:d||(d={}))[c]=b[c]);return d&&r.extend(!0,a,d),a}function Pb(a,b,c){var d,e,f,g,h=a.contents,i=a.dataTypes;while("*"===i[0])i.shift(),void 0===d&&(d=a.mimeType||b.getResponseHeader("Content-Type"));if(d)for(e in h)if(h[e]&&h[e].test(d)){i.unshift(e);break}if(i[0]in c)f=i[0];else{for(e in c){if(!i[0]||a.converters[e+" "+i[0]]){f=e;break}g||(g=e)}f=f||g}if(f)return f!==i[0]&&i.unshift(f),c[f]}function Qb(a,b,c,d){var e,f,g,h,i,j={},k=a.dataTypes.slice();if(k[1])for(g in a.converters)j[g.toLowerCase()]=a.converters[g];f=k.shift();while(f)if(a.responseFields[f]&&(c[a.responseFields[f]]=b),!i&&d&&a.dataFilter&&(b=a.dataFilter(b,a.dataType)),i=f,f=k.shift())if("*"===f)f=i;else if("*"!==i&&i!==f){if(g=j[i+" "+f]||j["* "+f],!g)for(e in j)if(h=e.split(" "),h[1]===f&&(g=j[i+" "+h[0]]||j["* "+h[0]])){g===!0?g=j[e]:j[e]!==!0&&(f=h[0],k.unshift(h[1]));break}if(g!==!0)if(g&&a["throws"])b=g(b);else try{b=g(b)}catch(l){return{state:"parsererror",error:g?l:"No conversion from "+i+" to "+f}}}return{state:"success",data:b}}r.extend({active:0,lastModified:{},etag:{},ajaxSettings:{url:tb.href,type:"GET",isLocal:Fb.test(tb.protocol),global:!0,processData:!0,async:!0,contentType:"application/x-www-form-urlencoded; charset=UTF-8",accepts:{"*":Kb,text:"text/plain",html:"text/html",xml:"application/xml, text/xml",json:"application/json, text/javascript"},contents:{xml:/\bxml\b/,html:/\bhtml/,json:/\bjson\b/},responseFields:{xml:"responseXML",text:"responseText",json:"responseJSON"},converters:{"* text":String,"text html":!0,"text json":JSON.parse,"text xml":r.parseXML},flatOptions:{url:!0,context:!0}},ajaxSetup:function(a,b){return b?Ob(Ob(a,r.ajaxSettings),b):Ob(r.ajaxSettings,a)},ajaxPrefilter:Mb(Ib),ajaxTransport:Mb(Jb),ajax:function(b,c){"object"==typeof b&&(c=b,b=void 0),c=c||{};var e,f,g,h,i,j,k,l,m,n,o=r.ajaxSetup({},c),p=o.context||o,q=o.context&&(p.nodeType||p.jquery)?r(p):r.event,s=r.Deferred(),t=r.Callbacks("once memory"),u=o.statusCode||{},v={},w={},x="canceled",y={readyState:0,getResponseHeader:function(a){var b;if(k){if(!h){h={};while(b=Eb.exec(g))h[b[1].toLowerCase()]=b[2]}b=h[a.toLowerCase()]}return null==b?null:b},getAllResponseHeaders:function(){return k?g:null},setRequestHeader:function(a,b){return null==k&&(a=w[a.toLowerCase()]=w[a.toLowerCase()]||a,v[a]=b),this},overrideMimeType:function(a){return null==k&&(o.mimeType=a),this},statusCode:function(a){var b;if(a)if(k)y.always(a[y.status]);else for(b in a)u[b]=[u[b],a[b]];return this},abort:function(a){var b=a||x;return e&&e.abort(b),A(0,b),this}};if(s.promise(y),o.url=((b||o.url||tb.href)+"").replace(Hb,tb.protocol+"//"),o.type=c.method||c.type||o.method||o.type,o.dataTypes=(o.dataType||"*").toLowerCase().match(L)||[""],null==o.crossDomain){j=d.createElement("a");try{j.href=o.url,j.href=j.href,o.crossDomain=Lb.protocol+"//"+Lb.host!=j.protocol+"//"+j.host}catch(z){o.crossDomain=!0}}if(o.data&&o.processData&&"string"!=typeof o.data&&(o.data=r.param(o.data,o.traditional)),Nb(Ib,o,c,y),k)return y;l=r.event&&o.global,l&&0===r.active++&&r.event.trigger("ajaxStart"),o.type=o.type.toUpperCase(),o.hasContent=!Gb.test(o.type),f=o.url.replace(Cb,""),o.hasContent?o.data&&o.processData&&0===(o.contentType||"").indexOf("application/x-www-form-urlencoded")&&(o.data=o.data.replace(Bb,"+")):(n=o.url.slice(f.length),o.data&&(f+=(vb.test(f)?"&":"?")+o.data,delete o.data),o.cache===!1&&(f=f.replace(Db,"$1"),n=(vb.test(f)?"&":"?")+"_="+ub++ +n),o.url=f+n),o.ifModified&&(r.lastModified[f]&&y.setRequestHeader("If-Modified-Since",r.lastModified[f]),r.etag[f]&&y.setRequestHeader("If-None-Match",r.etag[f])),(o.data&&o.hasContent&&o.contentType!==!1||c.contentType)&&y.setRequestHeader("Content-Type",o.contentType),y.setRequestHeader("Accept",o.dataTypes[0]&&o.accepts[o.dataTypes[0]]?o.accepts[o.dataTypes[0]]+("*"!==o.dataTypes[0]?", "+Kb+"; q=0.01":""):o.accepts["*"]);for(m in o.headers)y.setRequestHeader(m,o.headers[m]);if(o.beforeSend&&(o.beforeSend.call(p,y,o)===!1||k))return y.abort();if(x="abort",t.add(o.complete),y.done(o.success),y.fail(o.error),e=Nb(Jb,o,c,y)){if(y.readyState=1,l&&q.trigger("ajaxSend",[y,o]),k)return y;o.async&&o.timeout>0&&(i=a.setTimeout(function(){y.abort("timeout")},o.timeout));try{k=!1,e.send(v,A)}catch(z){if(k)throw z;A(-1,z)}}else A(-1,"No Transport");function A(b,c,d,h){var j,m,n,v,w,x=c;k||(k=!0,i&&a.clearTimeout(i),e=void 0,g=h||"",y.readyState=b>0?4:0,j=b>=200&&b<300||304===b,d&&(v=Pb(o,y,d)),v=Qb(o,v,y,j),j?(o.ifModified&&(w=y.getResponseHeader("Last-Modified"),w&&(r.lastModified[f]=w),w=y.getResponseHeader("etag"),w&&(r.etag[f]=w)),204===b||"HEAD"===o.type?x="nocontent":304===b?x="notmodified":(x=v.state,m=v.data,n=v.error,j=!n)):(n=x,!b&&x||(x="error",b<0&&(b=0))),y.status=b,y.statusText=(c||x)+"",j?s.resolveWith(p,[m,x,y]):s.rejectWith(p,[y,x,n]),y.statusCode(u),u=void 0,l&&q.trigger(j?"ajaxSuccess":"ajaxError",[y,o,j?m:n]),t.fireWith(p,[y,x]),l&&(q.trigger("ajaxComplete",[y,o]),--r.active||r.event.trigger("ajaxStop")))}return y},getJSON:function(a,b,c){return r.get(a,b,c,"json")},getScript:function(a,b){return r.get(a,void 0,b,"script")}}),r.each(["get","post"],function(a,b){r[b]=function(a,c,d,e){return r.isFunction(c)&&(e=e||d,d=c,c=void 0),r.ajax(r.extend({url:a,type:b,dataType:e,data:c,success:d},r.isPlainObject(a)&&a))}}),r._evalUrl=function(a){return r.ajax({url:a,type:"GET",dataType:"script",cache:!0,async:!1,global:!1,"throws":!0})},r.fn.extend({wrapAll:function(a){var b;return this[0]&&(r.isFunction(a)&&(a=a.call(this[0])),b=r(a,this[0].ownerDocument).eq(0).clone(!0),this[0].parentNode&&b.insertBefore(this[0]),b.map(function(){var a=this;while(a.firstElementChild)a=a.firstElementChild;return a}).append(this)),this},wrapInner:function(a){return r.isFunction(a)?this.each(function(b){r(this).wrapInner(a.call(this,b))}):this.each(function(){var b=r(this),c=b.contents();c.length?c.wrapAll(a):b.append(a)})},wrap:function(a){var b=r.isFunction(a);return this.each(function(c){r(this).wrapAll(b?a.call(this,c):a)})},unwrap:function(a){return this.parent(a).not("body").each(function(){r(this).replaceWith(this.childNodes)}),this}}),r.expr.pseudos.hidden=function(a){return!r.expr.pseudos.visible(a)},r.expr.pseudos.visible=function(a){return!!(a.offsetWidth||a.offsetHeight||a.getClientRects().length)},r.ajaxSettings.xhr=function(){try{return new a.XMLHttpRequest}catch(b){}};var Rb={0:200,1223:204},Sb=r.ajaxSettings.xhr();o.cors=!!Sb&&"withCredentials"in Sb,o.ajax=Sb=!!Sb,r.ajaxTransport(function(b){var c,d;if(o.cors||Sb&&!b.crossDomain)return{send:function(e,f){var g,h=b.xhr();if(h.open(b.type,b.url,b.async,b.username,b.password),b.xhrFields)for(g in b.xhrFields)h[g]=b.xhrFields[g];b.mimeType&&h.overrideMimeType&&h.overrideMimeType(b.mimeType),b.crossDomain||e["X-Requested-With"]||(e["X-Requested-With"]="XMLHttpRequest");for(g in e)h.setRequestHeader(g,e[g]);c=function(a){return function(){c&&(c=d=h.onload=h.onerror=h.onabort=h.onreadystatechange=null,"abort"===a?h.abort():"error"===a?"number"!=typeof h.status?f(0,"error"):f(h.status,h.statusText):f(Rb[h.status]||h.status,h.statusText,"text"!==(h.responseType||"text")||"string"!=typeof h.responseText?{binary:h.response}:{text:h.responseText},h.getAllResponseHeaders()))}},h.onload=c(),d=h.onerror=c("error"),void 0!==h.onabort?h.onabort=d:h.onreadystatechange=function(){4===h.readyState&&a.setTimeout(function(){c&&d()})},c=c("abort");try{h.send(b.hasContent&&b.data||null)}catch(i){if(c)throw i}},abort:function(){c&&c()}}}),r.ajaxPrefilter(function(a){a.crossDomain&&(a.contents.script=!1)}),r.ajaxSetup({accepts:{script:"text/javascript, application/javascript, application/ecmascript, application/x-ecmascript"},contents:{script:/\b(?:java|ecma)script\b/},converters:{"text script":function(a){return r.globalEval(a),a}}}),r.ajaxPrefilter("script",function(a){void 0===a.cache&&(a.cache=!1),a.crossDomain&&(a.type="GET")}),r.ajaxTransport("script",function(a){if(a.crossDomain){var b,c;return{send:function(e,f){b=r(" - - - - - - -
-

logo keepassxc-browser

+ + Settings | KeePassXC-Browser + + + + + + + + + + +
+

logo KeePassXC-Browser

- + - -
-

General Settings

-
-

- If you just want to insert username + password into the fields where your focus is, press Ctrl + Shift + U. -
- If you only want to insert the password, just press Ctrl + Shift + P. - You can customize these shortcuts on chrome://extensions/configureCommands page -

-

-

- -
-
- - -
-
- - What is the maximum time (ms) the icon should blink after detecting new credentials -
- Default: 7500 -
-
-

-

-

- -
-
- - -
-
- - What is the minimum time (ms) the icon should blink before deactivating due to page redirects. -
- -1 to only use Blink Time ignoring Redirect Allowance (old behavior) -
- Default: -1, Recommended: 2000 -
-
-

-

-

- -
-
- - -
-
- - How many pages should the tab cycle through after the redirect offset before deactivating the icon -
- Default: 1 -
-
-

-
-

-

- - For all password-fields there will be an icon added to generate a new password.
- It is generated by KeePassXC with the profile for automatically generated passwords for new entries.
-
-

-
-

-

- - keepassxc-browser will immediately retrieve the credentials when the tab is activated. -
-

-
-

-

- - If keepassxc-browser does only receive a single entry from KeePassXC it automatically fills this credentials into the found credential fields. - Warning! Using auto-fill is not safe. Use at your own risk. -
-

-
-

-

- - For all username fields on a page a dropdown list appears which contains all available credentials. -
-

-
-

- Check for updates of KeePassXC: -
- - - - -

-
- keepassxc-browser needs KeePassXC to retrieve credentials. -
- You can download the latest stable version from here: https://keepassxc.org/ -
-
-
- Your running version of KeePassXC: -
-
- Latest available version of KeePassXC: - -
-
-
-

-

- - - If credentials are found for a page and the login-type is an HTTP Auth request, keepassxc-browser tries to login with the first given credentials. -
- An HTTP Auth dialog looks like this: -
-
- http-auth-dialog -

-
+ +
+

General Settings

+
+

+ If you just want to insert username + password into the fields where your focus is, press Alt + Shift + U. +
+ If you only want to insert the password, just press Alt + Shift + P. + You can customize these shortcuts on chrome://extensions/configureCommands page +

+

+

+ +
+
+ + +
+
+ + What is the maximum time (ms) the icon should blink after detecting new credentials +
+ Default: 7500 +
+
+

+

+

+ +
+
+ + +
+
+ + What is the minimum time (ms) the icon should blink before deactivating due to page redirects. +
+ -1 to only use Blink Time ignoring Redirect Allowance (old behavior) +
+ Default: -1, Recommended: 2000 +
+
+

+

+

+ +
+
+ + +
+
+ + How many pages should the tab cycle through after the redirect offset before deactivating the icon +
+ Default: 1 +
+
+

+
+

+

+ + + For all password-fields there will be an icon added to generate a new password. +
+ It is generated by KeePassXC with the profile for automatically generated passwords for new entries.
+
+

+
+

+

+ + KeePassXC-Browser will immediately retrieve the credentials when the tab is activated. +
+

+
+

+

+ + If KeePassXC-Browser does only receive a single entry from KeePassXC it automatically fills this credentials into the found credential fields. + Warning! Using auto-fill is not safe. Use at your own risk. +
+

+
+

+

+ + For all username fields on a page a dropdown list appears which contains all available credentials. +
+

+
+

+

+ + Allow KeePassXC-Browser to show notifications when user interaction is needed from the extension icon. +
+

+
+

+ Check for updates of KeePassXC: +
+ + + + +

+
+ KeePassXC-Browser needs KeePassXC to retrieve credentials. +
+ You can download the latest stable version from here: https://keepassxc.org/ +
+
+
+ You are running KeePassXC version: +
+
+ Latest available version of KeePassXC: + +
+
+
+

+

+ + + If credentials are found for a page and the login-type is an HTTP Auth request, KeePassXC-Browser tries to login with the first given credentials. +
+ An HTTP Auth dialog looks like this: +
+
+ http-auth-dialog +

+
- -
-

Connected Databases

-
-

- The following KeePassXC databases are connected to keepassxc-browser. -

- - - - - - - - - - - - - - - - - - - - - - -
IdentifierKeyLast usedCreatedDelete
No connected databases found.
-
- -
+ +
+

Connected Databases

+
+

+ The following KeePassXC databases are connected to KeePassXC-Browser. +

+ + + + + + + + + + + + + + + + + + + + + + +
IdentifierKeyLast usedCreatedDelete
No connected databases found.
+
+ +
- -
+ +
- -
-

Specified credential fields

-
-

- If keepassxc-browser detects the wrong credential fields, you are able to specify the correct fields by yourself. -
- Just go to the page and click on the keepassxc-browser-Icon, now select Choose own credential fields for this page. -
- On this page you can manage theses specified credential fields. -

- - - - - - - - - - - - - - - - -
Page URLDelete
No specified credential fields found.
- -
+ +
+

Specified credential fields

+
+

+ If KeePassXC-Browser detects the wrong credential fields, you are able to specify the correct fields by yourself. +
+ Just go to the page and click on the KeePassXC-Browser-Icon, now select Choose own credential fields for this page. +
+ On this page you can manage theses specified credential fields. +

+ + + + + + + + + + + + + + + + +
Page URLDelete
No specified credential fields found.
+ +
- -
-

About

-
-

- Developed by Perry Nguyen, Lukas Schulze and - Sami Vänttinen -

-

- Visit extension's page in Chrome Web store -

-

- Visit extension's page in Mozilla's Add-ons page -

-

- Visit project's page in GitHub. -

-
-

- keepassxc-browser Version: -

-

- KeePassXC Version: -

-
-
- -
- + +
+

About

+
+

+ Visit extension's page in Chrome Web store +

+

+ Visit extension's page in Mozilla's Add-ons page +

+

+ Visit project's page in GitHub. +

+
+

+ KeePassXC-Browser Version: +

+

+ KeePassXC Version: +

+
+

+ Contributors:
+ Perry Nguyen, Lukas Schulze, + Andy Brandt, Sami Vänttinen + and KeePassXC Team. +

+
+
+ +
+ diff --git a/keepassxc-browser/options/options.js b/keepassxc-browser/options/options.js index f0b275b..b4d826a 100644 --- a/keepassxc-browser/options/options.js +++ b/keepassxc-browser/options/options.js @@ -1,264 +1,264 @@ if (jQuery) { - var $ = jQuery.noConflict(true); + var $ = jQuery.noConflict(true); } $(function() { - browser.runtime.sendMessage({ action: 'load_settings' }).then((settings) => { - options.settings = settings; - browser.runtime.sendMessage({ action: 'load_keyring' }).then((keyRing) => { - options.keyRing = keyRing; - options.initMenu(); - options.initGeneralSettings(); - options.initConnectedDatabases(); - options.initSpecifiedCredentialFields(); - options.initAbout(); - }); - }); + browser.runtime.sendMessage({ action: 'load_settings' }).then((settings) => { + options.settings = settings; + browser.runtime.sendMessage({ action: 'load_keyring' }).then((keyRing) => { + options.keyRing = keyRing; + options.initMenu(); + options.initGeneralSettings(); + options.initConnectedDatabases(); + options.initSpecifiedCredentialFields(); + options.initAbout(); + }); + }); }); var options = options || {}; options.initMenu = function() { - $('.navbar:first ul.nav:first li a').click(function(e) { - e.preventDefault(); - $('.navbar:first ul.nav:first li').removeClass('active'); - $(this).parent('li').addClass('active'); - $('div.tab').hide(); - $('div.tab#tab-' + $(this).attr('href').substring(1)).fadeIn(); - }); + $('.navbar:first ul.nav:first li a').click(function(e) { + e.preventDefault(); + $('.navbar:first ul.nav:first li').removeClass('active'); + $(this).parent('li').addClass('active'); + $('div.tab').hide(); + $('div.tab#tab-' + $(this).attr('href').substring(1)).fadeIn(); + }); - $('div.tab:first').show(); + $('div.tab:first').show(); }; options.saveSettingsPromise = function() { - return new Promise((resolve, reject) => { - browser.storage.local.set({'settings': options.settings}).then((item) => { - browser.runtime.sendMessage({ - action: 'load_settings' - }).then((settings) => { - resolve(settings); - }); - }); - }); + return new Promise((resolve, reject) => { + browser.storage.local.set({'settings': options.settings}).then((item) => { + browser.runtime.sendMessage({ + action: 'load_settings' + }).then((settings) => { + resolve(settings); + }); + }); + }); } options.saveSetting = function(name) { - const $id = '#' + name; - $($id).closest('.control-group').removeClass('error').addClass('success'); - setTimeout(() => { $($id).closest('.control-group').removeClass('success'); }, 2500); + const $id = '#' + name; + $($id).closest('.control-group').removeClass('error').addClass('success'); + setTimeout(() => { $($id).closest('.control-group').removeClass('success'); }, 2500); - browser.storage.local.set({'settings': options.settings}); - browser.runtime.sendMessage({ - action: 'load_settings' - }); + browser.storage.local.set({'settings': options.settings}); + browser.runtime.sendMessage({ + action: 'load_settings' + }); }; options.saveSettings = function() { - browser.storage.local.set({'settings': options.settings}); - browser.runtime.sendMessage({ - action: 'load_settings' - }); + browser.storage.local.set({'settings': options.settings}); + browser.runtime.sendMessage({ + action: 'load_settings' + }); }; options.saveKeyRing = function() { - browser.storage.local.set({'keyRing': options.keyRing}); - browser.runtime.sendMessage({ - action: 'load_keyring' - }); + browser.storage.local.set({'keyRing': options.keyRing}); + browser.runtime.sendMessage({ + action: 'load_keyring' + }); }; options.initGeneralSettings = function() { - $('#tab-general-settings input[type=checkbox]').each(function() { - $(this).attr('checked', options.settings[$(this).attr('name')]); - }); + $('#tab-general-settings input[type=checkbox]').each(function() { + $(this).attr('checked', options.settings[$(this).attr('name')]); + }); - $('#tab-general-settings input[type=checkbox]').change(function() { - const name = $(this).attr('name'); - options.settings[name] = $(this).is(':checked'); - options.saveSettingsPromise().then((x) => { - if (name === 'autoFillAndSend') { - browser.runtime.sendMessage({action: 'init_http_auth'}); - } - }); - }); + $('#tab-general-settings input[type=checkbox]').change(function() { + const name = $(this).attr('name'); + options.settings[name] = $(this).is(':checked'); + options.saveSettingsPromise().then((x) => { + if (name === 'autoFillAndSend') { + browser.runtime.sendMessage({action: 'init_http_auth'}); + } + }); + }); - $('#tab-general-settings input[type=radio]').each(function() { - if ($(this).val() === options.settings[$(this).attr('name')]) { - $(this).attr('checked', options.settings[$(this).attr('name')]); - } - }); + $('#tab-general-settings input[type=radio]').each(function() { + if ($(this).val() === options.settings[$(this).attr('name')]) { + $(this).attr('checked', options.settings[$(this).attr('name')]); + } + }); - $('#tab-general-settings input[type=radio]').change(function() { - options.settings[$(this).attr('name')] = $(this).val(); - options.saveSettings(); - }); + $('#tab-general-settings input[type=radio]').change(function() { + options.settings[$(this).attr('name')] = $(this).val(); + options.saveSettings(); + }); - browser.runtime.sendMessage({ - action: 'get_keepassxc_versions' - }).then(options.showKeePassXCVersions); + browser.runtime.sendMessage({ + action: 'get_keepassxc_versions' + }).then(options.showKeePassXCVersions); - $('#tab-general-settings button.checkUpdateKeePassXC:first').click(function(e) { - e.preventDefault(); - $(this).attr('disabled', true); - browser.runtime.sendMessage({ - action: 'check_update_keepassxc' - }).then(options.showKeePassXCVersions); - }); + $('#tab-general-settings button.checkUpdateKeePassXC:first').click(function(e) { + e.preventDefault(); + $(this).attr('disabled', true); + browser.runtime.sendMessage({ + action: 'check_update_keepassxc' + }).then(options.showKeePassXCVersions); + }); - $('#blinkTimeout').val(options.settings['blinkTimeout']); - $('#blinkMinTimeout').val(options.settings['blinkMinTimeout']); - $('#allowedRedirect').val(options.settings['allowedRedirect']); + $('#blinkTimeout').val(options.settings['blinkTimeout']); + $('#blinkMinTimeout').val(options.settings['blinkMinTimeout']); + $('#allowedRedirect').val(options.settings['allowedRedirect']); - $('#blinkTimeoutButton').click(function(){ - const blinkTimeout = $.trim($('#blinkTimeout').val()); - const blinkTimeoutval = Number(blinkTimeout); + $('#blinkTimeoutButton').click(function(){ + const blinkTimeout = $.trim($('#blinkTimeout').val()); + const blinkTimeoutval = Number(blinkTimeout); options.settings['blinkTimeout'] = String(blinkTimeoutval); - options.saveSetting('blinkTimeout'); - }); + options.saveSetting('blinkTimeout'); + }); - $('#blinkMinTimeoutButton').click(function(){ - const blinkMinTimeout = $.trim($('#blinkMinTimeout').val()); - const blinkMinTimeoutval = Number(blinkMinTimeout); + $('#blinkMinTimeoutButton').click(function(){ + const blinkMinTimeout = $.trim($('#blinkMinTimeout').val()); + const blinkMinTimeoutval = Number(blinkMinTimeout); options.settings['blinkMinTimeout'] = String(blinkMinTimeoutval); - options.saveSetting('blinkMinTimeout'); - }); + options.saveSetting('blinkMinTimeout'); + }); - $('#allowedRedirectButton').click(function(){ - const allowedRedirect = $.trim($('#allowedRedirect').val()); - const allowedRedirectval = Number(allowedRedirect); + $('#allowedRedirectButton').click(function(){ + const allowedRedirect = $.trim($('#allowedRedirect').val()); + const allowedRedirectval = Number(allowedRedirect); options.settings['allowedRedirect'] = String(allowedRedirectval); - options.saveSetting('allowedRedirect'); - }); + options.saveSetting('allowedRedirect'); + }); }; options.showKeePassXCVersions = function(response) { - if (response.current <= 0) { - response.current = 'unknown'; - } - if (response.latest <= 0) { - response.latest = 'unknown'; - } - $('#tab-general-settings .kphVersion:first em.yourVersion:first').text(response.current); - $('#tab-general-settings .kphVersion:first em.latestVersion:first').text(response.latest); - $('#tab-about em.versionKPH').text(response.current); - $('#tab-general-settings button.checkUpdateKeePassXC:first').attr('disabled', false); + if (response.current <= 0) { + response.current = 'unknown'; + } + if (response.latest <= 0) { + response.latest = 'unknown'; + } + $('#tab-general-settings .kphVersion:first em.yourVersion:first').text(response.current); + $('#tab-general-settings .kphVersion:first em.latestVersion:first').text(response.latest); + $('#tab-about em.versionKPH').text(response.current); + $('#tab-general-settings button.checkUpdateKeePassXC:first').attr('disabled', false); }; options.initConnectedDatabases = function() { - $('#dialogDeleteConnectedDatabase').modal({keyboard: true, show: false, backdrop: true}); - $('#tab-connected-databases tr.clone:first button.delete:first').click(function(e) { - e.preventDefault(); - $('#dialogDeleteConnectedDatabase').data('hash', $(this).closest('tr').data('hash')); - $('#dialogDeleteConnectedDatabase .modal-body:first span:first').text($(this).closest('tr').children('td:first').text()); - $('#dialogDeleteConnectedDatabase').modal('show'); - }); + $('#dialogDeleteConnectedDatabase').modal({keyboard: true, show: false, backdrop: true}); + $('#tab-connected-databases tr.clone:first button.delete:first').click(function(e) { + e.preventDefault(); + $('#dialogDeleteConnectedDatabase').data('hash', $(this).closest('tr').data('hash')); + $('#dialogDeleteConnectedDatabase .modal-body:first span:first').text($(this).closest('tr').children('td:first').text()); + $('#dialogDeleteConnectedDatabase').modal('show'); + }); - $('#dialogDeleteConnectedDatabase .modal-footer:first button.yes:first').click(function(e) { - $('#dialogDeleteConnectedDatabase').modal('hide'); + $('#dialogDeleteConnectedDatabase .modal-footer:first button.yes:first').click(function(e) { + $('#dialogDeleteConnectedDatabase').modal('hide'); - const $hash = $('#dialogDeleteConnectedDatabase').data('hash'); - $('#tab-connected-databases #tr-cd-' + $hash).remove(); + const $hash = $('#dialogDeleteConnectedDatabase').data('hash'); + $('#tab-connected-databases #tr-cd-' + $hash).remove(); - delete options.keyRing[$hash]; - options.saveKeyRing(); + delete options.keyRing[$hash]; + options.saveKeyRing(); - if ($('#tab-connected-databases table tbody:first tr').length > 2) { - $('#tab-connected-databases table tbody:first tr.empty:first').hide(); - } - else { - $('#tab-connected-databases table tbody:first tr.empty:first').show(); - } - }); + if ($('#tab-connected-databases table tbody:first tr').length > 2) { + $('#tab-connected-databases table tbody:first tr.empty:first').hide(); + } + else { + $('#tab-connected-databases table tbody:first tr.empty:first').show(); + } + }); - $('#tab-connected-databases tr.clone:first .dropdown-menu:first').width('230px'); + $('#tab-connected-databases tr.clone:first .dropdown-menu:first').width('230px'); - const $trClone = $('#tab-connected-databases table tr.clone:first').clone(true); - $trClone.removeClass('clone'); - for (let hash in options.keyRing) { - const $tr = $trClone.clone(true); - $tr.data('hash', hash); - $tr.attr('id', 'tr-cd-' + hash); + const $trClone = $('#tab-connected-databases table tr.clone:first').clone(true); + $trClone.removeClass('clone'); + for (let hash in options.keyRing) { + const $tr = $trClone.clone(true); + $tr.data('hash', hash); + $tr.attr('id', 'tr-cd-' + hash); - $('a.dropdown-toggle:first img:first', $tr).attr('src', '/icons/19x19/icon_normal_19x19.png'); + $('a.dropdown-toggle:first img:first', $tr).attr('src', '/icons/19x19/icon_normal_19x19.png'); - $tr.children('td:first').text(options.keyRing[hash].id); - $tr.children('td:eq(1)').text(options.keyRing[hash].key); - const lastUsed = (options.keyRing[hash].lastUsed) ? new Date(options.keyRing[hash].lastUsed).toLocaleString() : 'unknown'; - $tr.children('td:eq(2)').text(lastUsed); - const date = (options.keyRing[hash].created) ? new Date(options.keyRing[hash].created).toLocaleDateString() : 'unknown'; - $tr.children('td:eq(3)').text(date); - $('#tab-connected-databases table tbody:first').append($tr); - } + $tr.children('td:first').text(options.keyRing[hash].id); + $tr.children('td:eq(1)').text(options.keyRing[hash].key); + const lastUsed = (options.keyRing[hash].lastUsed) ? new Date(options.keyRing[hash].lastUsed).toLocaleString() : 'unknown'; + $tr.children('td:eq(2)').text(lastUsed); + const date = (options.keyRing[hash].created) ? new Date(options.keyRing[hash].created).toLocaleDateString() : 'unknown'; + $tr.children('td:eq(3)').text(date); + $('#tab-connected-databases table tbody:first').append($tr); + } - if ($('#tab-connected-databases table tbody:first tr').length > 2) { - $('#tab-connected-databases table tbody:first tr.empty:first').hide(); - } - else { - $('#tab-connected-databases table tbody:first tr.empty:first').show(); - } + if ($('#tab-connected-databases table tbody:first tr').length > 2) { + $('#tab-connected-databases table tbody:first tr.empty:first').hide(); + } + else { + $('#tab-connected-databases table tbody:first tr.empty:first').show(); + } - $('#connect-button').click(function() { - browser.runtime.sendMessage({ - action: 'associate' - }); - }); + $('#connect-button').click(function() { + browser.runtime.sendMessage({ + action: 'associate' + }); + }); }; options.initSpecifiedCredentialFields = function() { - $('#dialogDeleteSpecifiedCredentialFields').modal({keyboard: true, show: false, backdrop: true}); - $('#tab-specified-fields tr.clone:first button.delete:first').click(function(e) { - e.preventDefault(); - $('#dialogDeleteSpecifiedCredentialFields').data('url', $(this).closest('tr').data('url')); - $('#dialogDeleteSpecifiedCredentialFields').data('tr-id', $(this).closest('tr').attr('id')); - $('#dialogDeleteSpecifiedCredentialFields .modal-body:first strong:first').text($(this).closest('tr').children('td:first').text()); - $('#dialogDeleteSpecifiedCredentialFields').modal('show'); - }); + $('#dialogDeleteSpecifiedCredentialFields').modal({keyboard: true, show: false, backdrop: true}); + $('#tab-specified-fields tr.clone:first button.delete:first').click(function(e) { + e.preventDefault(); + $('#dialogDeleteSpecifiedCredentialFields').data('url', $(this).closest('tr').data('url')); + $('#dialogDeleteSpecifiedCredentialFields').data('tr-id', $(this).closest('tr').attr('id')); + $('#dialogDeleteSpecifiedCredentialFields .modal-body:first strong:first').text($(this).closest('tr').children('td:first').text()); + $('#dialogDeleteSpecifiedCredentialFields').modal('show'); + }); - $('#dialogDeleteSpecifiedCredentialFields .modal-footer:first button.yes:first').click(function(e) { - $('#dialogDeleteSpecifiedCredentialFields').modal('hide'); + $('#dialogDeleteSpecifiedCredentialFields .modal-footer:first button.yes:first').click(function(e) { + $('#dialogDeleteSpecifiedCredentialFields').modal('hide'); - const $url = $('#dialogDeleteSpecifiedCredentialFields').data('url'); - const $trId = $('#dialogDeleteSpecifiedCredentialFields').data('tr-id'); - $('#tab-specified-fields #' + $trId).remove(); + const $url = $('#dialogDeleteSpecifiedCredentialFields').data('url'); + const $trId = $('#dialogDeleteSpecifiedCredentialFields').data('tr-id'); + $('#tab-specified-fields #' + $trId).remove(); - delete options.settings['defined-credential-fields'][$url]; - options.saveSettings(); + delete options.settings['defined-credential-fields'][$url]; + options.saveSettings(); - if ($('#tab-specified-fields table tbody:first tr').length > 2) { - $('#tab-specified-fields table tbody:first tr.empty:first').hide(); - } - else { - $('#tab-specified-fields table tbody:first tr.empty:first').show(); - } - }); + if ($('#tab-specified-fields table tbody:first tr').length > 2) { + $('#tab-specified-fields table tbody:first tr.empty:first').hide(); + } + else { + $('#tab-specified-fields table tbody:first tr.empty:first').show(); + } + }); - const $trClone = $('#tab-specified-fields table tr.clone:first').clone(true); - $trClone.removeClass('clone'); - let counter = 1; - for (let url in options.settings['defined-credential-fields']) { - const $tr = $trClone.clone(true); - $tr.data('url', url); - $tr.attr('id', 'tr-scf' + counter); - counter += 1; + const $trClone = $('#tab-specified-fields table tr.clone:first').clone(true); + $trClone.removeClass('clone'); + let counter = 1; + for (let url in options.settings['defined-credential-fields']) { + const $tr = $trClone.clone(true); + $tr.data('url', url); + $tr.attr('id', 'tr-scf' + counter); + counter += 1; - $tr.children('td:first').text(url); - $('#tab-specified-fields table tbody:first').append($tr); - } + $tr.children('td:first').text(url); + $('#tab-specified-fields table tbody:first').append($tr); + } - if ($('#tab-specified-fields table tbody:first tr').length > 2) { - $('#tab-specified-fields table tbody:first tr.empty:first').hide(); - } - else { - $('#tab-specified-fields table tbody:first tr.empty:first').show(); - } + if ($('#tab-specified-fields table tbody:first tr').length > 2) { + $('#tab-specified-fields table tbody:first tr.empty:first').hide(); + } + else { + $('#tab-specified-fields table tbody:first tr.empty:first').show(); + } }; options.initAbout = function() { - $('#tab-about em.versionCIP').text(browser.runtime.getManifest().version); - if (isFirefox()) { - $('#chrome-only').remove(); - } + $('#tab-about em.versionCIP').text(browser.runtime.getManifest().version); + if (isFirefox()) { + $('#chrome-only').remove(); + } }; diff --git a/keepassxc-browser/popups/popup.css b/keepassxc-browser/popups/popup.css index 0dd0120..baf8215 100644 --- a/keepassxc-browser/popups/popup.css +++ b/keepassxc-browser/popups/popup.css @@ -1,74 +1,75 @@ body { - font-family: sans-serif; - min-width: 440px; - max-width: 440px; - overflow-x: hidden; - background-color: #eee; - font-size: 15px; - padding: 8px; + font-family: sans-serif; + min-width: 440px; + max-width: 440px; + overflow-x: hidden; + background-color: #eee; + font-size: 15px; + padding: 8px; } .credentials ul { - margin-left: 0; - margin-right: 0; - padding-left: 0; - padding-right: 0; - list-style-position: inside; + margin-left: 0; + margin-right: 0; + padding-left: 0; + padding-right: 0; + list-style-position: inside; } .credentials li { - list-style: none; - padding-top: 1px; - padding-bottom: 1px; - padding-left: 5px; - padding-right: 5px; - margin-left: 10px; - margin-right: 5px; + list-style: none; + padding-top: 1px; + padding-bottom: 1px; + padding-left: 5px; + padding-right: 5px; + margin-left: 10px; + margin-right: 5px; } .credentials a { - color: #333; + color: #333; } .credentials a:hover { - text-decoration: none; + text-decoration: none; } .credentials li:hover { - cursor: pointer; - background: #ddd; + cursor: pointer; + background: #ddd; } .settings { - padding-bottom: 10px; - margin-bottom: 10px; - border-bottom: 1px solid #333; - font-size: 90%; - text-align: left; + padding-bottom: 10px; + margin-bottom: 10px; + border-bottom: 1px solid #333; + font-size: 90%; + text-align: left; } .settings label { - display: block; + display: block; } .settings label input { - vertical-align: middle; + vertical-align: middle; } .settings .description { - margin-top: 3px; - font-size: 80%; - color: #787878; + margin-top: 3px; + font-size: 80%; + color: #787878; } .small { - margin-top: 3px; - margin-left: 12px; - font-size: 80%; - color: #787878; + margin-top: 3px; + margin-left: 12px; + font-size: 80%; + color: #787878; } #update-available { - padding-top: 10px; - margin-top: 10px; - padding-bottom: 0px; - margin-bottom: 0px; - text-align: left; + padding-top: 10px; + margin-top: 10px; + padding-bottom: 0px; + margin-bottom: 0px; + text-align: left; } #update-available a:hover, #update-available a:active { - text-decoration: underline; + text-decoration: underline; } #lock-database-button { - float: right; + float: right; + height: 30px; } diff --git a/keepassxc-browser/popups/popup.html b/keepassxc-browser/popups/popup.html index 76647c9..83340d8 100644 --- a/keepassxc-browser/popups/popup.html +++ b/keepassxc-browser/popups/popup.html @@ -1,99 +1,99 @@ - - KeePassXC - Popup - - - - - - - - - - - -
-
- - - + + KeePassXC - Popup + + + + + + + + + + + +
+
+ + + -
- You use an old version of KeePassXC. -
- Please download the latest version from keepassxc.org. -
-
+
+ You use an old version of KeePassXC. +
+ Please download the latest version from keepassxc.org. +
+
-
-

Checking status...

-
+
+

Checking status...

+
- + - + - + - + - + - -
- + +
+ diff --git a/keepassxc-browser/popups/popup.js b/keepassxc-browser/popups/popup.js index 276f8e8..473d06e 100644 --- a/keepassxc-browser/popups/popup.js +++ b/keepassxc-browser/popups/popup.js @@ -1,88 +1,90 @@ function status_response(r) { - $('#initial-state').hide(); - $('#error-encountered').hide(); - $('#need-reconfigure').hide(); - $('#not-configured').hide(); - $('#configured-and-associated').hide(); - $('#configured-not-associated').hide(); - $('#lock-database-button').hide(); + $('#initial-state').hide(); + $('#error-encountered').hide(); + $('#need-reconfigure').hide(); + $('#not-configured').hide(); + $('#configured-and-associated').hide(); + $('#configured-not-associated').hide(); + $('#lock-database-button').hide(); - if (!r.keePassXCAvailable) { - $('#error-message').html(r.error); - $('#error-encountered').show(); - } - else if (r.keePassXCAvailable && r.databaseClosed) { - $('#database-error-message').html(r.error); - $('#database-not-opened').show(); - } - else if (!r.configured) { - $('#not-configured').show(); - } - else if (r.encryptionKeyUnrecognized) { - $('#need-reconfigure').show(); - $('#need-reconfigure-message').html(r.error); - } - else if (!r.associated) { - $('#need-reconfigure').show(); - $('#need-reconfigure-message').html(r.error); - } - else if (r.error !== null) { - $('#error-encountered').show(); - $('#error-message').html(r.error); - } - else { - $('#configured-and-associated').show(); - $('#associated-identifier').html(r.identifier); - $('#lock-database-button').show(); - } + if (!r.keePassXCAvailable) { + $('#error-message').html(r.error); + $('#error-encountered').show(); + } + else if (r.keePassXCAvailable && r.databaseClosed) { + $('#database-error-message').html(r.error); + $('#database-not-opened').show(); + } + else if (!r.configured) { + $('#not-configured').show(); + } + else if (r.encryptionKeyUnrecognized) { + $('#need-reconfigure').show(); + $('#need-reconfigure-message').html(r.error); + } + else if (!r.associated) { + $('#need-reconfigure').show(); + $('#need-reconfigure-message').html(r.error); + } + else if (r.error !== null) { + $('#error-encountered').show(); + $('#error-message').html(r.error); + } + else { + $('#configured-and-associated').show(); + $('#associated-identifier').html(r.identifier); + $('#lock-database-button').show(); + } } $(function() { - $('#connect-button').click(function() { - browser.runtime.sendMessage({ - action: 'associate' - }); - close(); - }); + $('#connect-button').click(function() { + browser.runtime.sendMessage({ + action: 'associate' + }); + close(); + }); - $('#reconnect-button').click(function() { - browser.runtime.sendMessage({ - action: 'associate' - }); - close(); - }); + $('#reconnect-button').click(function() { + browser.runtime.sendMessage({ + action: 'associate' + }); + close(); + }); - $('#reload-status-button').click(function() { - browser.runtime.sendMessage({ - action: 'reconnect' - }).then(status_response); - }); + $('#reload-status-button').click(function() { + browser.runtime.sendMessage({ + action: 'reconnect' + }).then(status_response); + }); - $('#reopen-database-button').click(function() { - browser.runtime.sendMessage({ - action: 'get_status' - }).then(status_response); - }); + $('#reopen-database-button').click(function() { + browser.runtime.sendMessage({ + action: 'get_status', + args: [ false, true ] // Set forcePopup to true + }).then(status_response); + }); - $('#redetect-fields-button').click(function() { - browser.tabs.query({"active": true, "currentWindow": true}).then(function(tabs) { - if (tabs.length === 0) - return; // For example: only the background devtools or a popup are opened - let tab = tabs[0]; + $('#redetect-fields-button').click(function() { + browser.tabs.query({"active": true, "currentWindow": true}).then(function(tabs) { + if (tabs.length === 0) { + return; // For example: only the background devtools or a popup are opened + } + let tab = tabs[0]; - browser.tabs.sendMessage(tab.id, { - action: 'redetect_fields' - }); - }); - }); + browser.tabs.sendMessage(tab.id, { + action: 'redetect_fields' + }); + }); + }); - $('#lock-database-button').click(function() { - browser.runtime.sendMessage({ - action: 'lock-database' - }).then(status_response); - }); + $('#lock-database-button').click(function() { + browser.runtime.sendMessage({ + action: 'lock-database' + }).then(status_response); + }); - browser.runtime.sendMessage({ - action: "get_status" - }).then(status_response); + browser.runtime.sendMessage({ + action: "get_status" + }).then(status_response); }); diff --git a/keepassxc-browser/popups/popup_functions.js b/keepassxc-browser/popups/popup_functions.js index 3417757..f78182c 100644 --- a/keepassxc-browser/popups/popup_functions.js +++ b/keepassxc-browser/popups/popup_functions.js @@ -1,34 +1,34 @@ var $ = jQuery.noConflict(true); function updateAvailableResponse(available) { - if (available) { - $('#update-available').show(); - } - else { - $('#update-available').hide(); - } + if (available) { + $('#update-available').show(); + } + else { + $('#update-available').hide(); + } } function initSettings() { - $ ('#settings #btn-options').click(function() { - browser.runtime.openOptionsPage().then(close()); - }); + $('#settings #btn-options').click(function() { + browser.runtime.openOptionsPage().then(close()); + }); - $ ('#settings #btn-choose-credential-fields').click(function() { - browser.runtime.getBackgroundPage().then((global) => { - browser.tabs.sendMessage(global.page.currentTabId, { - action: 'choose_credential_fields' - }); - close(); - }); - }); + $('#settings #btn-choose-credential-fields').click(function() { + browser.runtime.getBackgroundPage().then((global) => { + browser.tabs.sendMessage(global.page.currentTabId, { + action: 'choose_credential_fields' + }); + close(); + }); + }); } $(function() { - initSettings(); + initSettings(); - browser.runtime.sendMessage({ - action: 'update_available_keepassxc' - }).then(updateAvailableResponse); + browser.runtime.sendMessage({ + action: 'update_available_keepassxc' + }).then(updateAvailableResponse); }); diff --git a/keepassxc-browser/popups/popup_httpauth.html b/keepassxc-browser/popups/popup_httpauth.html index d9ef4a9..2f43070 100644 --- a/keepassxc-browser/popups/popup_httpauth.html +++ b/keepassxc-browser/popups/popup_httpauth.html @@ -1,36 +1,36 @@ - - keepassxc-browser - Popup - - - - - - - - - - - -
-
- - - + + KeePassXC-Browser - Popup + + + + + + + + + + + +
+
+ + + -
- You use an old version of KeePassXC. -
- Please download the latest version from keepassxc.org. -
-
+
+ You use an old version of KeePassXC. +
+ Please download the latest version from keepassxc.org. +
+
-
-

- Select the login information you would like to get logged in with: -

-
    -
    -
    - +
    +

    + Select the login information you would like to get logged in with: +

    +
      +
      +
      + diff --git a/keepassxc-browser/popups/popup_httpauth.js b/keepassxc-browser/popups/popup_httpauth.js index 7aca550..9ce9271 100644 --- a/keepassxc-browser/popups/popup_httpauth.js +++ b/keepassxc-browser/popups/popup_httpauth.js @@ -1,35 +1,35 @@ $(function() { - browser.runtime.getBackgroundPage().then((global) => { - browser.tabs.query({'active': true, 'currentWindow': true}).then((tabs) => { - let tab = tabs[0]; - const data = global.page.tabs[tab.id].loginList; - let ul = document.getElementById('login-list'); - for (let i = 0; i < data.logins.length; i++) { - const li = document.createElement('li'); - const a = document.createElement('a'); - a.textContent = data.logins[i].login + " (" + data.logins[i].name + ")"; - li.appendChild(a); - $(a).data('creds', data.logins[i]); - $(a).click(function () { - if (data.resolve) { - const creds = $(this).data('creds'); - data.resolve({ - authCredentials: { - username: creds.login, - password: creds.password - } - }); - } - close(); - }); - ul.appendChild(li); - } - }); - }); + browser.runtime.getBackgroundPage().then((global) => { + browser.tabs.query({'active': true, 'currentWindow': true}).then((tabs) => { + let tab = tabs[0]; + const data = global.page.tabs[tab.id].loginList; + let ul = document.getElementById('login-list'); + for (let i = 0; i < data.logins.length; i++) { + const li = document.createElement('li'); + const a = document.createElement('a'); + a.textContent = data.logins[i].login + " (" + data.logins[i].name + ")"; + li.appendChild(a); + $(a).data('creds', data.logins[i]); + $(a).click(function () { + if (data.resolve) { + const creds = $(this).data('creds'); + data.resolve({ + authCredentials: { + username: creds.login, + password: creds.password + } + }); + } + close(); + }); + ul.appendChild(li); + } + }); + }); - $('#lock-database-button').click(function() { - browser.runtime.sendMessage({ - action: 'lock-database' - }).then(status_response); + $('#lock-database-button').click(function() { + browser.runtime.sendMessage({ + action: 'lock-database' + }).then(status_response); }); }); diff --git a/keepassxc-browser/popups/popup_login.html b/keepassxc-browser/popups/popup_login.html index 51d616f..14d4584 100644 --- a/keepassxc-browser/popups/popup_login.html +++ b/keepassxc-browser/popups/popup_login.html @@ -1,36 +1,36 @@ - - KeePassXC - Popup - - - - - - - - - - - -
      -
      - - - + + KeePassXC - Popup + + + + + + + + + + + +
      +
      + + + -
      - You use an old version of KeePassXC. -
      - Please download the latest version from keepassxc.org. -
      -
      +
      + You use an old version of KeePassXC. +
      + Please download the latest version from keepassxc.org. +
      +
      -
      -

      - Select the login information you would like to get entered into the page: -

      -
        -
        -
        - +
        +

        + Select the login information you would like to get entered into the page: +

        +
          +
          +
          + diff --git a/keepassxc-browser/popups/popup_login.js b/keepassxc-browser/popups/popup_login.js index d23c0d2..eae62d3 100644 --- a/keepassxc-browser/popups/popup_login.js +++ b/keepassxc-browser/popups/popup_login.js @@ -1,36 +1,36 @@ $(function() { - browser.runtime.getBackgroundPage().then((global) => { - browser.tabs.query({"active": true, "currentWindow": true}).then((tabs) => { - if (tabs.length === 0) { - return; // For example: only the background devtools or a popup are opened - } - const tab = tabs[0]; + browser.runtime.getBackgroundPage().then((global) => { + browser.tabs.query({'active': true, 'currentWindow': true}).then((tabs) => { + if (tabs.length === 0) { + return; // For example: only the background devtools or a popup are opened + } + const tab = tabs[0]; - const logins = global.page.tabs[tab.id].loginList; - let ul = document.getElementById('login-list'); - for (let i = 0; i < logins.length; i++) { - const li = document.createElement('li'); - const a = document.createElement('a'); - a.textContent = logins[i]; - li.setAttribute('class', 'list-group-item'); - li.appendChild(a); - a.setAttribute('id', '' + i); - a.addEventListener('click', (e) => { - const id = e.target.id; - browser.tabs.sendMessage(tab.id, { - action: 'fill_user_pass_with_specific_login', - id: id - }); - close(); - }); - ul.appendChild(li); - } - }); - }); + const logins = global.page.tabs[tab.id].loginList; + let ul = document.getElementById('login-list'); + for (let i = 0; i < logins.length; i++) { + const li = document.createElement('li'); + const a = document.createElement('a'); + a.textContent = logins[i]; + li.setAttribute('class', 'list-group-item'); + li.appendChild(a); + a.setAttribute('id', '' + i); + a.addEventListener('click', (e) => { + const id = e.target.id; + browser.tabs.sendMessage(tab.id, { + action: 'fill_user_pass_with_specific_login', + id: id + }); + close(); + }); + ul.appendChild(li); + } + }); + }); - $('#lock-database-button').click(function() { - browser.runtime.sendMessage({ - action: 'lock-database' - }); - }); + $('#lock-database-button').click(function() { + browser.runtime.sendMessage({ + action: 'lock-database' + }); + }); }); diff --git a/keepassxc-browser/popups/popup_multiple-fields.html b/keepassxc-browser/popups/popup_multiple-fields.html index 5b41f35..a457820 100644 --- a/keepassxc-browser/popups/popup_multiple-fields.html +++ b/keepassxc-browser/popups/popup_multiple-fields.html @@ -1,36 +1,36 @@ - - keepassxc-browser - Popup - - - - - - - - - - -
          -
          - - - + + KeePassXC-Browser - Popup + + + + + + + + + + +
          +
          + + + -
          - You use an old version of KeePassXC. -
          - Please download the latest version from keepassxc.org. -
          -
          - -
          -

          - keepassxc-browser found more than one password field on this page. To enter your - logins, right-click on one of the password fields, and choose either the - "Fill User + Pass" or "Fill Pass Only" command. -

          -
          +
          + You use an old version of KeePassXC. +
          + Please download the latest version from keepassxc.org.
          - +
          + +
          +

          + KeePassXC-Browser found more than one password field on this page. To enter your + logins, right-click on one of the password fields, and choose either the + "Fill User + Pass" or "Fill Pass Only" command. +

          +
          +
          + diff --git a/keepassxc-browser/popups/popup_remember.html b/keepassxc-browser/popups/popup_remember.html index 82d9153..86ad865 100644 --- a/keepassxc-browser/popups/popup_remember.html +++ b/keepassxc-browser/popups/popup_remember.html @@ -1,51 +1,51 @@ - - keepassxc-browser - Popup - - - - - - - - - - - - -
          -

          - Username or password changed! Save it? -
          - Url: -
          - Username: -

          -

          - - - -

          -
          + + KeePassXC-Browser - Popup + + + + + + + + + + + + +
          +

          + Username or password changed! Save it? +
          + Url: +
          + Username: +

          +

          + + + +

          +
          -
          -

          Credentials will be saved in connected database with identifier .

          -
          +
          +

          Credentials will be saved in connected database with identifier .

          +
          -
          -

          The used username is currently not saved!

          -

          The credentials with the used username are marked bold.

          -

          - Please choose the credentials you want to update: -

          -
            -
            - +
            +

            The used username is currently not saved!

            +

            The credentials with the used username are marked bold.

            +

            + Please choose the credentials you want to update: +

            +
              +
              + diff --git a/keepassxc-browser/popups/popup_remember.js b/keepassxc-browser/popups/popup_remember.js index fca0779..be79328 100644 --- a/keepassxc-browser/popups/popup_remember.js +++ b/keepassxc-browser/popups/popup_remember.js @@ -1,124 +1,124 @@ var _tab; function _initialize(tab) { - _tab = tab; + _tab = tab; - // no credentials set or credentials already cleared - if (!_tab.credentials.username) { - _close(); - return; - } + // no credentials set or credentials already cleared + if (!_tab.credentials.username) { + _close(); + return; + } - // no existing credentials to update --> disable update-button - if (_tab.credentials.list.length === 0) { - $('#btn-update').attr('disabled', true).removeClass('btn-warning'); - } + // no existing credentials to update --> disable update-button + if (_tab.credentials.list.length === 0) { + $('#btn-update').attr('disabled', true).removeClass('btn-warning'); + } - let url = _tab.credentials.url; - url = (url.length > 50) ? url.substring(0, 50) + '...' : url; - $('.information-url:first span:first').text(url); - $('.information-username:first span:first').text(_tab.credentials.username); + let url = _tab.credentials.url; + url = (url.length > 50) ? url.substring(0, 50) + '...' : url; + $('.information-url:first span:first').text(url); + $('.information-username:first span:first').text(_tab.credentials.username); - $('#btn-new').click(function(e) { - browser.runtime.sendMessage({ - action: 'add_credentials', - args: [_tab.credentials.username, _tab.credentials.password, _tab.credentials.url] - }).then(_verifyResult); - }); + $('#btn-new').click(function(e) { + browser.runtime.sendMessage({ + action: 'add_credentials', + args: [_tab.credentials.username, _tab.credentials.password, _tab.credentials.url] + }).then(_verifyResult); + }); - $('#btn-update').click(function(e) { - e.preventDefault(); + $('#btn-update').click(function(e) { + e.preventDefault(); - // only one entry which could be updated - if(_tab.credentials.list.length === 1) { - browser.runtime.sendMessage({ - action: 'update_credentials', - args: [_tab.credentials.list[0].uuid, _tab.credentials.username, _tab.credentials.password, _tab.credentials.url] - }).then(_verifyResult); - } - else { - $('.credentials:first .username-new:first strong:first').text(_tab.credentials.username); - $('.credentials:first .username-exists:first strong:first').text(_tab.credentials.username); + // only one entry which could be updated + if(_tab.credentials.list.length === 1) { + browser.runtime.sendMessage({ + action: 'update_credentials', + args: [_tab.credentials.list[0].uuid, _tab.credentials.username, _tab.credentials.password, _tab.credentials.url] + }).then(_verifyResult); + } + else { + $('.credentials:first .username-new:first strong:first').text(_tab.credentials.username); + $('.credentials:first .username-exists:first strong:first').text(_tab.credentials.username); - if (_tab.credentials.usernameExists) { - $('.credentials:first .username-new:first').hide(); - $('.credentials:first .username-exists:first').show(); - } - else { - $('.credentials:first .username-new:first').show(); - $('.credentials:first .username-exists:first').hide(); - } + if (_tab.credentials.usernameExists) { + $('.credentials:first .username-new:first').hide(); + $('.credentials:first .username-exists:first').show(); + } + else { + $('.credentials:first .username-new:first').show(); + $('.credentials:first .username-exists:first').hide(); + } - for (let i = 0; i < _tab.credentials.list.length; i++) { - let $a = $('') - .attr('href', '#') - .text(_tab.credentials.list[i].login + ' (' + _tab.credentials.list[i].name + ')') - .data('entryId', i) - .click(function(e) { - e.preventDefault(); - browser.runtime.sendMessage({ - action: 'update_credentials', - args: [_tab.credentials.list[$(this).data('entryId')].uuid, _tab.credentials.username, _tab.credentials.password, _tab.credentials.url] - }).then(_verifyResult); - }); + for (let i = 0; i < _tab.credentials.list.length; i++) { + let $a = $('') + .attr('href', '#') + .text(_tab.credentials.list[i].login + ' (' + _tab.credentials.list[i].name + ')') + .data('entryId', i) + .click(function(e) { + e.preventDefault(); + browser.runtime.sendMessage({ + action: 'update_credentials', + args: [_tab.credentials.list[$(this).data('entryId')].uuid, _tab.credentials.username, _tab.credentials.password, _tab.credentials.url] + }).then(_verifyResult); + }); - if (_tab.credentials.usernameExists && _tab.credentials.username === _tab.credentials.list[i].login) { - $a.css('font-weight', 'bold'); - } + if (_tab.credentials.usernameExists && _tab.credentials.username === _tab.credentials.list[i].login) { + $a.css('font-weight', 'bold'); + } - const $li = $('
            • ').append($a); - $('ul#list').append($li); - } + const $li = $('
            • ').append($a); + $('ul#list').append($li); + } - $('.credentials').show(); - } - }); + $('.credentials').show(); + } + }); - $('#btn-dismiss').click(function(e) { - e.preventDefault(); - _close(); - }); + $('#btn-dismiss').click(function(e) { + e.preventDefault(); + _close(); + }); } function _connected_database(db) { - if (db.count > 1 && db.identifier) { - $('.connected-database:first em:first').text(db.identifier); - $('.connected-database:first').show(); - } - else { - $('.connected-database:first').hide(); - } + if (db.count > 1 && db.identifier) { + $('.connected-database:first em:first').text(db.identifier); + $('.connected-database:first').show(); + } + else { + $('.connected-database:first').hide(); + } } function _verifyResult(code) { - if (code === 'success') { - _close(); - } + if (code === 'success') { + _close(); + } } function _close() { - browser.runtime.sendMessage({ - action: 'remove_credentials_from_tab_information' - }); + browser.runtime.sendMessage({ + action: 'remove_credentials_from_tab_information' + }); - browser.runtime.sendMessage({ - action: 'pop_stack' - }); + browser.runtime.sendMessage({ + action: 'pop_stack' + }); - close(); + close(); } $(function() { - browser.runtime.sendMessage({ - action: 'stack_add', - args: ['icon_remember_red_background_19x19.png', 'popup_remember.html', 10, true, 0] - }); + browser.runtime.sendMessage({ + action: 'stack_add', + args: ['icon_remember_red_background_19x19.png', 'popup_remember.html', 10, true, 0] + }); - browser.runtime.sendMessage({ - action: 'get_tab_information' - }).then(_initialize); + browser.runtime.sendMessage({ + action: 'get_tab_information' + }).then(_initialize); - browser.runtime.sendMessage({ - action: 'get_connected_database' - }).then(_connected_database); + browser.runtime.sendMessage({ + action: 'get_connected_database' + }).then(_connected_database); });