Merge branch 'master' into develop

This commit is contained in:
antelle 2019-09-30 19:33:21 +02:00
commit 7e0565df3b
17 changed files with 113 additions and 72 deletions

View File

@ -20,7 +20,7 @@ The app is quite stable now. Basic stuff, as well as more advanced operations, s
# Self-hosting
Everything you need to host this app on your server is any static file server. The app is a single HTML file + cache manifest (optionally; for offline access).
Everything you need to host this app on your server is any static file server. The app is a single HTML file + a service worker (optionally; for offline access).
You can download the latest distribution files from [gh-pages](https://github.com/keeweb/keeweb/archive/gh-pages.zip) branch.
If you are using Docker:

View File

@ -9,7 +9,7 @@ AutoTypeHelper.prototype.getActiveWindowInfo = function(callback) {
complete(err, out) {
let windowInfo;
if (out) {
const [id, title] = out.trim().split('\n');
const [title, id] = out.trim().split('\n');
windowInfo = {
id,
title

View File

@ -28,6 +28,7 @@ const AutoType = {
Events.on('auto-type', e => this.handleEvent(e));
Events.on('main-window-blur', e => this.resetPendingEvent(e));
Events.on('main-window-will-close', e => this.resetPendingEvent(e));
appModel.files.on('change', () => this.processPendingEvent());
},
handleEvent(e) {
@ -209,7 +210,6 @@ const AutoType = {
const evt = { filter, windowInfo };
if (!this.appModel.files.hasOpenFiles()) {
this.pendingEvent = evt;
this.appModel.files.once('change', this.processPendingEvent, this);
logger.debug('auto-type event delayed');
this.focusMainWindow();
} else {
@ -262,7 +262,6 @@ const AutoType = {
resetPendingEvent() {
if (this.pendingEvent) {
this.pendingEvent = null;
this.appModel.files.off('change', this.processPendingEvent, this);
logger.debug('auto-type event cancelled');
}
},
@ -273,7 +272,6 @@ const AutoType = {
}
logger.debug('processing pending auto-type event');
const evt = this.pendingEvent;
this.appModel.files.off('change', this.processPendingEvent, this);
this.pendingEvent = null;
this.processEventWithFilter(evt);
}

View File

@ -1,9 +1,8 @@
import { Storage } from 'storage';
import { Features } from 'util/features';
const AuthReceiver = {
receive() {
if (!Features.isPopup && !Features.isStandalone) {
if (!Features.isPopup) {
return false;
}
const opener = window.opener || window.parent;
@ -12,16 +11,9 @@ const AuthReceiver = {
if (!hasKeys) {
return false;
}
if (Features.isStandalone) {
if (sessionStorage.authStorage) {
Storage[sessionStorage.authStorage].handleOAuthReturnMessage(message);
}
return false;
} else {
opener.postMessage(message, window.location.origin);
window.close();
return true;
}
opener.postMessage(message, window.location.origin);
window.close();
return true;
},
urlArgsToMessage(url) {

View File

@ -4,9 +4,11 @@ import { Tip } from 'util/ui/tip';
import { KeyHandler } from 'comp/browser/key-handler';
import { Logger } from 'util/logger';
const OnlyDirectEvents = {
const DoesNotBubble = {
mouseenter: true,
mouseleave: true
mouseleave: true,
blur: true,
focus: true
};
class View extends EventEmitter {
@ -19,6 +21,7 @@ class View extends EventEmitter {
hidden = false;
removed = false;
eventListeners = {};
elementEventListeners = [];
debugLogger = localStorage.debugViews ? new Logger('view', this.constructor.name) : undefined;
constructor(model = undefined, options = {}) {
@ -59,6 +62,7 @@ class View extends EventEmitter {
if (this.el) {
const mountRoot = this.options.ownParent ? this.el.firstChild : this.el;
morphdom(mountRoot, html);
this.bindElementEvents();
} else {
let parent = this.options.parent || this.parent;
if (parent) {
@ -102,11 +106,9 @@ class View extends EventEmitter {
if (spaceIx > 0) {
event = eventDef.substr(0, spaceIx);
selector = eventDef.substr(spaceIx + 1);
if (OnlyDirectEvents[event]) {
throw new Error(
`Event listener ${eventDef} defined in ${this.constructor.name} ` +
`can be installed only on the view itself`
);
if (DoesNotBubble[event]) {
this.elementEventListeners.push({ event, selector, method, els: [] });
continue;
}
} else {
event = eventDef;
@ -117,17 +119,48 @@ class View extends EventEmitter {
eventsMap[event].push({ selector, method });
}
for (const [event, handlers] of Object.entries(eventsMap)) {
this.debugLogger && this.debugLogger.debug('Bind', event, handlers);
this.debugLogger && this.debugLogger.debug('Bind', 'view', event, handlers);
const listener = e => this.eventListener(e, handlers);
this.eventListeners[event] = listener;
this.el.addEventListener(event, listener);
}
this.bindElementEvents();
}
unbindEvents() {
for (const [event, listener] of Object.entries(this.eventListeners)) {
this.el.removeEventListener(event, listener);
}
this.unbindElementEvents();
}
bindElementEvents() {
if (!this.elementEventListeners.length) {
return;
}
this.unbindElementEvents();
for (const cfg of this.elementEventListeners) {
const els = this.el.querySelectorAll(cfg.selector);
this.debugLogger &&
this.debugLogger.debug('Bind', 'element', cfg.event, cfg.selector, els.length);
cfg.listener = e => this.eventListener(e, [cfg]);
for (const el of els) {
el.addEventListener(cfg.event, cfg.listener);
cfg.els.push(el);
}
}
}
unbindElementEvents() {
if (!this.elementEventListeners.length) {
return;
}
for (const cfg of this.elementEventListeners) {
for (const el of cfg.els) {
el.removeEventListener(cfg.event, cfg.listener);
}
cfg.els = [];
}
}
eventListener(e, handlers) {

View File

@ -24,6 +24,7 @@ FileInfoModel.defineModelProperties({
openDate: null,
keyFileName: null,
keyFileHash: null,
keyFilePath: null,
opts: null,
backup: null,
fingerprint: null

View File

@ -33,22 +33,33 @@ const PluginGallery = {
this.logger.error('Network error loading plugins');
resolve();
});
}).then(data => {
return this.verifySignature(data).then(gallery => {
})
.then(data => {
this.loading = false;
this.loadError = !gallery;
if (gallery) {
this.logger.debug(
`Loaded ${gallery.plugins.length} plugins`,
this.logger.ts(ts)
);
this.gallery = gallery;
this.saveGallery(gallery);
if (!data) {
this.loadError = true;
Events.emit('plugin-gallery-load-complete');
return;
}
return this.verifySignature(data).then(gallery => {
this.loadError = !gallery;
if (gallery) {
this.logger.debug(
`Loaded ${gallery.plugins.length} plugins`,
this.logger.ts(ts)
);
this.gallery = gallery;
this.saveGallery(gallery);
}
Events.emit('plugin-gallery-load-complete');
return gallery;
});
})
.catch(e => {
this.loadError = true;
this.logger.error('Error loading plugin gallery', e);
Events.emit('plugin-gallery-load-complete');
return gallery;
});
});
},
verifySignature(gallery) {

View File

@ -64,7 +64,7 @@ class PluginManager extends Model {
}
installIfNew(url, expectedManifest, skipSignatureValidation) {
const plugin = this.plugins.find({ url });
const plugin = this.plugins.find(p => p.url === url);
if (plugin && plugin.status !== 'invalid') {
return Promise.resolve();
}

View File

@ -2,7 +2,6 @@ import { Events } from 'framework/events';
import { Links } from 'const/links';
import { AppSettingsModel } from 'models/app-settings-model';
import { RuntimeDataModel } from 'models/runtime-data-model';
import { Features } from 'util/features';
import { Logger } from 'util/logger';
const MaxRequestRetries = 3;
@ -30,18 +29,6 @@ class StorageBase {
}
}
this.logger = new Logger('storage-' + this.name);
if (this._oauthReturnMessage) {
this.logger.debug('OAuth return message', this._oauthReturnMessage);
this._oauthProcessReturn(this._oauthReturnMessage);
delete this._oauthReturnMessage;
delete sessionStorage.authStorage;
if (Features.isStandalone) {
const [url, urlParams] = location.href.split(/[?#]/);
if (urlParams) {
location.href = url;
}
}
}
return this;
}
@ -49,10 +36,6 @@ class StorageBase {
this.enabled = enabled;
}
handleOAuthReturnMessage(message) {
this._oauthReturnMessage = message;
}
_xhr(config) {
const xhr = new XMLHttpRequest();
if (config.responseType) {
@ -94,8 +77,10 @@ class StorageBase {
if (this._oauthToken && !config.skipAuth) {
xhr.setRequestHeader('Authorization', 'Bearer ' + this._oauthToken.accessToken);
}
for (const [key, value] of Object.entries(config.headers)) {
xhr.setRequestHeader(key, value);
if (config.headers) {
for (const [key, value] of Object.entries(config.headers)) {
xhr.setRequestHeader(key, value);
}
}
let data = config.data;
if (data instanceof ArrayBuffer) {
@ -135,9 +120,6 @@ class StorageBase {
settings = Object.keys(settings)
.map(key => key + '=' + settings[key])
.join(',');
if (Features.isStandalone) {
sessionStorage.authStorage = this.name;
}
return window.open(url, title, settings);
}

View File

@ -115,7 +115,8 @@ const KdbxwebInit = {
});
},
workerPostRun() {
// eslint-disable-next-line object-shorthand
workerPostRun: function() {
self.postMessage({ op: 'postRun' });
self.onmessage = e => {
try {
@ -128,7 +129,8 @@ const KdbxwebInit = {
};
},
calcHash(Module, args) {
// eslint-disable-next-line object-shorthand
calcHash: function(Module, args) {
let { password, salt } = args;
const { memory, iterations, length, parallelism, type, version } = args;
const passwordLen = password.byteLength;

View File

@ -235,7 +235,7 @@ class SettingsPluginsView extends View {
pluginMatchesFilter(plugin) {
const searchStr = this.searchStr;
const manifest = plugin.manifest;
return (
return !!(
!searchStr ||
manifest.name.toLowerCase().indexOf(searchStr) >= 0 ||
(manifest.description && manifest.description.toLowerCase().indexOf(searchStr) >= 0) ||

View File

@ -50,6 +50,8 @@ app.setPath('userData', path.join(tempUserDataPath, tempUserDataPathRand));
setEnv();
restorePreferences();
const appSettings = readAppSettings() || {};
app.on('window-all-closed', () => {
if (restartPending) {
app.relaunch();
@ -62,11 +64,10 @@ app.on('window-all-closed', () => {
});
app.on('ready', () => {
appReady = true;
const appSettings = readAppSettings() || {};
setAppOptions();
setSystemAppearance();
createMainWindow(appSettings);
setGlobalShortcuts(appSettings);
createMainWindow();
setGlobalShortcuts();
subscribePowerEvents();
deleteOldTempFiles();
hookRequestHeaders();
@ -154,7 +155,7 @@ function setSystemAppearance() {
}
}
function createMainWindow(appSettings) {
function createMainWindow() {
const windowOptions = {
show: false,
width: 1000,
@ -394,7 +395,7 @@ function notifyOpenFile() {
}
}
function setGlobalShortcuts(appSettings) {
function setGlobalShortcuts() {
const defaultShortcutModifiers = process.platform === 'darwin' ? 'Ctrl+Alt+' : 'Shift+Alt+';
const defaultShortcuts = {
CopyPassword: { shortcut: defaultShortcutModifiers + 'C', event: 'copy-password' },

View File

@ -1,6 +1,6 @@
{
"name": "KeeWeb",
"version": "1.11.0",
"version": "1.11.5",
"lockfileVersion": 1,
"requires": true,
"dependencies": {

View File

@ -1,6 +1,6 @@
{
"name": "KeeWeb",
"version": "1.11.0",
"version": "1.11.5",
"description": "Free cross-platform password manager compatible with KeePass",
"main": "main.js",
"homepage": "https://keeweb.info",

2
package-lock.json generated
View File

@ -1,6 +1,6 @@
{
"name": "keeweb",
"version": "1.11.0",
"version": "1.11.5",
"lockfileVersion": 1,
"requires": true,
"dependencies": {

View File

@ -1,6 +1,6 @@
{
"name": "keeweb",
"version": "1.11.0",
"version": "1.11.5",
"description": "Free cross-platform password manager compatible with KeePass",
"main": "Gruntfile.js",
"private": true,

View File

@ -1,5 +1,26 @@
Release notes
-------------
##### v1.11.5 (2019-09-29)
`-` fix #1279: error opening files with saved keyfiles
##### v1.11.4 (2019-09-29)
`-` fix #1277: auto-type window matching on Linux
`-` fix #1278: entry selection auto-type window issues
`-` fixed displaying errors on the plugins page
##### v1.11.3 (2019-09-29)
`-` fix #1275: starting the app after closing on macOS
`-` fix #1276 GDrive connection issues
##### v1.11.2 (2019-09-29)
`-` fix #1272: Argon2 error
`-` fixed Dropbox connection on iOS 13 homescreen
`-` fixed plugin search filtering
##### v1.11.1 (2019-09-28)
`-` fix #1270: password change control focus
`-` fix #1271: loading custom plugins from config
##### v1.11.0 (2019-09-28)
`+` #1125: field actions: copy, reveal, auto-type
`+` #107: multiline custom fields support