Merge branch 'release-1.5'

This commit is contained in:
antelle 2017-05-23 21:11:41 +02:00
commit daba40e51a
19 changed files with 171 additions and 38 deletions

View File

@ -666,12 +666,12 @@ module.exports = function(grunt) {
grunt.registerTask('build-desktop-executables', [
'electron',
'codesign:app',
'sign-exe:win32-build-x64',
'sign-exe:win32-build-ia32',
'copy:desktop-darwin-helper-x64',
'copy:desktop-windows-helper-ia32',
'copy:desktop-windows-helper-x64'
'copy:desktop-windows-helper-x64',
'codesign:app'
]);
grunt.registerTask('build-desktop-archives', [

View File

@ -3,7 +3,8 @@ const AppSettingsModel = require('../models/app-settings-model');
const ExportApi = {
settings: {
get: function(key) { return key ? AppSettingsModel.instance.get(key) : AppSettingsModel.instance.toJSON(); },
set: function(key, value) { AppSettingsModel.instance.set(key, value); }
set: function(key, value) { AppSettingsModel.instance.set(key, value); },
del: function(key) { AppSettingsModel.instance.unset(key); }
}
};

View File

@ -6,6 +6,7 @@ const AppSettingsModel = require('../models/app-settings-model');
const UpdateModel = require('../models/update-model');
const Transport = require('../comp/transport');
const Logger = require('../util/logger');
const SemVer = require('../util/semver');
const publicKey = require('raw-loader!../../resources/public-key.pem');
const logger = new Logger('updater');
@ -113,7 +114,7 @@ const Updater = {
}
if (!startedByUser && this.getAutoUpdateType() === 'install') {
this.update(startedByUser);
} else if (this.compareVersions(UpdateModel.instance.get('lastVersion'), RuntimeInfo.version) > 0) {
} else if (SemVer.compareVersions(UpdateModel.instance.get('lastVersion'), RuntimeInfo.version) > 0) {
UpdateModel.instance.set('updateStatus', 'found');
}
},
@ -133,7 +134,7 @@ const Updater = {
canAutoUpdate: function() {
const minLauncherVersion = UpdateModel.instance.get('lastCheckUpdMin');
if (minLauncherVersion) {
const cmp = this.compareVersions(Launcher.version, minLauncherVersion);
const cmp = SemVer.compareVersions(Launcher.version, minLauncherVersion);
if (cmp < 0) {
UpdateModel.instance.set({ updateStatus: 'ready', updateManual: true });
return false;
@ -142,29 +143,13 @@ const Updater = {
return true;
},
compareVersions: function(left, right) {
left = left.split('.');
right = right.split('.');
for (let num = 0; num < left.length; num++) {
const partLeft = left[num] | 0;
const partRight = right[num] | 0;
if (partLeft < partRight) {
return -1;
}
if (partLeft > partRight) {
return 1;
}
}
return 0;
},
update: function(startedByUser, successCallback) {
const ver = UpdateModel.instance.get('lastVersion');
if (!this.enabled) {
logger.info('Updater is disabled');
return;
}
if (this.compareVersions(RuntimeInfo.version, ver) >= 0) {
if (SemVer.compareVersions(RuntimeInfo.version, ver) >= 0) {
logger.info('You are using the latest version');
return;
}

View File

@ -496,6 +496,7 @@
"setPlDevelopStart": "Hier entlang",
"setPlTranslate": "Oder {}",
"setPlTranslateLink": "übersetzen Sie die App in Ihre Sprache",
"setPlAutoUpdate": "Automatisch aktualisieren",
"setAboutTitle": "Über",
"setAboutBuilt": "Diese App wurde mit den folgenden Werkzeugen erstellt",
"setAboutLic": "Lizenz",

View File

@ -43,6 +43,8 @@ const AppModel = Backbone.Model.extend({
this.listenTo(Backbone, 'select-entry', this.selectEntry);
this.appLogger = new Logger('app');
AppModel.instance = this;
},
prepare: function() {

View File

@ -8,6 +8,8 @@ const IoCache = require('../storage/io-cache');
const AppSettingsModel = require('../models/app-settings-model');
const BaseLocale = require('../locales/base.json');
const SignatureVerifier = require('../util/signature-verifier');
const SemVer = require('../util/semver');
const RuntimeInfo = require('../comp/runtime-info');
const commonLogger = new Logger('plugin');
const io = new IoCache({
@ -111,6 +113,25 @@ const Plugin = Backbone.Model.extend(_.extend({}, PluginStatus, {
(!manifest.locale || !manifest.locale.title || !/^[a-z]{2}(-[A-Z]{2})?$/.test(manifest.locale.name))) {
return 'Bad plugin locale';
}
if (manifest.desktop && !RuntimeInfo.launcher) {
return 'Desktop plugin';
}
if (manifest.versionMin) {
if (!/^\d+\.\d+\.\d+$/.test(manifest.versionMin)) {
return 'Invalid versionMin';
}
if (SemVer.compareVersions(manifest.versionMin, RuntimeInfo.version) > 0) {
return `Required min app version is ${manifest.versionMin}, actual ${RuntimeInfo.version}`;
}
}
if (manifest.versionMax) {
if (!/^\d+\.\d+\.\d+$/.test(manifest.versionMax)) {
return 'Invalid versionMin';
}
if (SemVer.compareVersions(manifest.versionMax, RuntimeInfo.version) < 0) {
return `Required max app version is ${manifest.versionMax}, actual ${RuntimeInfo.version}`;
}
}
},
validateUpdatedManifest(newManifest) {

View File

@ -168,6 +168,10 @@ const StorageDropbox = StorageBase.extend({
return '/' + fileName + '.kdbx';
},
_encodeJsonHttpHeader(json) {
return json.replace(/[\u007f-\uffff]/g, c => '\\u' + ('000' + c.charCodeAt(0).toString(16)).slice(-4));
},
_apiCall: function(args) {
this._oauthAuthorize(err => {
if (err) {
@ -177,7 +181,7 @@ const StorageDropbox = StorageBase.extend({
let headers;
let data = args.data;
if (args.apiArg) {
headers = { 'Dropbox-API-Arg': JSON.stringify(args.apiArg) };
headers = { 'Dropbox-API-Arg': this._encodeJsonHttpHeader(JSON.stringify(args.apiArg)) };
if (args.data) {
headers['Content-Type'] = 'application/octet-stream';
}

View File

@ -36,8 +36,8 @@ const FeatureDetector = {
return this.isMac;
},
ensureCanRun: function() {
if (/MSIE |Trident/.test(navigator.userAgent)) {
throw 'IE detected';
if (!window.crypto) {
throw 'WebCrypto not available';
}
if (!localStorage.length) {
try {

View File

@ -0,0 +1,19 @@
const SemVer = {
compareVersions(left, right) {
left = left.split('.');
right = right.split('.');
for (let num = 0; num < left.length; num++) {
const partLeft = left[num] | 0;
const partRight = right[num] | 0;
if (partLeft < partRight) {
return -1;
}
if (partLeft > partRight) {
return 1;
}
}
return 0;
}
};
module.exports = SemVer;

View File

@ -57,13 +57,13 @@ const SettingsFileView = Backbone.View.extend({
let canBackup = false;
Object.keys(Storage).forEach(name => {
const prv = Storage[name];
if (!canBackup && prv.backup && prv.enabled) {
canBackup = true;
}
if (!prv.system && prv.enabled) {
storageProviders.push({
name: prv.name, icon: prv.icon, iconSvg: prv.iconSvg, own: name === fileStorage, backup: prv.backup
});
if (!canBackup && prv.backup) {
canBackup = true;
}
}
});
storageProviders.sort((x, y) => (x.uipos || Infinity) - (y.uipos || Infinity));

View File

@ -12,6 +12,7 @@ const SettingsManager = require('../../comp/settings-manager');
const Storage = require('../../storage');
const FeatureDetector = require('../../util/feature-detector');
const Locale = require('../../util/locale');
const SemVer = require('../../util/semver');
const Links = require('../../const/links');
const AutoType = require('../../auto-type');
@ -131,7 +132,7 @@ const SettingsGeneralView = Backbone.View.extend({
return errMsg;
case 'ok':
let msg = Locale.setGenCheckedAt + ' ' + Format.dtStr(UpdateModel.instance.get('lastCheckDate')) + ': ';
const cmp = Updater.compareVersions(RuntimeInfo.version, UpdateModel.instance.get('lastVersion'));
const cmp = SemVer.compareVersions(RuntimeInfo.version, UpdateModel.instance.get('lastVersion'));
if (cmp >= 0) {
msg += Locale.setGenLatestVer;
} else {

View File

@ -8,6 +8,8 @@ const Format = require('../../util/format');
const SettingsManager = require('../../comp/settings-manager');
const SignatureVerifier = require('../../util/signature-verifier');
const FeatureDetector = require('../../util/feature-detector');
const SemVer = require('../../util/semver');
const RuntimeInfo = require('../../comp/runtime-info');
const Links = require('../../const/links');
const SettingsPluginsView = Backbone.View.extend({
@ -86,11 +88,26 @@ const SettingsPluginsView = Backbone.View.extend({
installError: this.installErrors[pl.url],
official: pl.manifest.publicKey === publicKey
}))
.filter(pl => !plugins.get(pl.manifest.name) &&
(!pl.manifest.locale || !SettingsManager.allLocales[pl.manifest.locale.name]))
.filter(pl => !plugins.get(pl.manifest.name) && this.canInstallPlugin(pl))
.sort((x, y) => x.manifest.name.localeCompare(y.manifest.name));
},
canInstallPlugin(plugin) {
if (plugin.manifest.locale && SettingsManager.allLocales[plugin.manifest.locale.name]) {
return false;
}
if (plugin.manifest.desktop && !RuntimeInfo.launcher) {
return false;
}
if (plugin.manifest.versionMin && SemVer.compareVersions(plugin.manufest.versionMin, RuntimeInfo.version) > 0) {
return false;
}
if (plugin.manifest.versionMax && SemVer.compareVersions(plugin.manufest.versionMax, RuntimeInfo.version) > 0) {
return false;
}
return true;
},
installClick() {
const installBtn = this.$el.find('.settings_plugins-install-btn');
const urlTextBox = this.$el.find('#settings__plugins-install-url');

View File

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

View File

@ -1,8 +1,9 @@
{
"name": "keeweb",
"version": "1.5.0",
"version": "1.5.1",
"description": "Free cross-platform password manager compatible with KeePass",
"main": "Gruntfile.js",
"private": true,
"homepage": "https://keeweb.info",
"repository": {
"type": "git",

View File

@ -44,7 +44,7 @@ RUN wget https://github.com/keeweb/keeweb-plugins/archive/master.zip; \
unzip master.zip; \
rm master.zip; \
mv keeweb-plugins-master/docs keeweb/plugins; \
rm -rf keeweb-plugins-master
rm -rf keeweb-plugins-master \
rm keeweb/plugins/CNAME
ENTRYPOINT ["/opt/entrypoint.sh"]

View File

@ -16,6 +16,8 @@ const pkg = require('./package.json');
const op = args.shift();
const bumpVersion = args.some(arg => arg === '--bump-version');
showBanner();
switch (op) {
@ -47,6 +49,7 @@ function signPlugin(packageName) {
}
const manifest = JSON.parse(fs.readFileSync(path.join(packageName, 'manifest.json')));
const privateKey = fs.readFileSync(path.join(packageName, 'private_key.pem'), 'binary');
let changed = false;
for (const res of Object.keys(manifest.resources)) {
console.log(`Signing ${res}...`);
let fileName;
@ -65,10 +68,21 @@ function signPlugin(packageName) {
const sign = crypto.createSign('RSA-SHA256');
sign.write(fs.readFileSync(fileName));
sign.end();
manifest.resources[res] = sign.sign(privateKey).toString('base64');
const signature = sign.sign(privateKey).toString('base64');
if (manifest.resources[res] !== signature) {
manifest.resources[res] = signature;
changed = true;
}
}
if (changed) {
if (bumpVersion) {
manifest.version = manifest.version.replace(/\d+$/, v => +v + 1);
}
fs.writeFileSync(path.join(packageName, 'manifest.json'), JSON.stringify(manifest, null, 2));
console.log('Done, package manifest updated');
} else {
console.log('No changes');
}
fs.writeFileSync(path.join(packageName, 'manifest.json'), JSON.stringify(manifest, null, 2));
console.log('Done, package manifest updated');
}
function watchSignPlugin() {

View File

@ -1,6 +1,6 @@
{
"name": "keeweb-plugin",
"version": "0.1.4",
"version": "0.1.5",
"description": "KeeWeb plugin utils",
"main": "keeweb-plugin.js",
"scripts": {

View File

@ -1,5 +1,10 @@
Release notes
-------------
##### v1.5.1 (2017-05-23)
`-` fix #631: unicode characters in Dropbox files
`-` fix backups in desktop
`+` plugin API improvements
##### v1.5.0 (2017-05-20)
`+` plugins
`*` translations are available only as plugins

62
util/release-stats.js Normal file
View File

@ -0,0 +1,62 @@
/* eslint-disable no-console */
const fs = require('fs');
const path = require('path');
const ps = require('child_process');
const cwd = path.resolve(__dirname, 'keeweb');
if (!fs.existsSync(cwd)) {
console.log('Cloning...');
ps.spawnSync('git', ['clone', 'git@github.com:keeweb/keeweb.git', '-b', 'gh-pages'], {cwd: __dirname});
}
console.log('Getting log...');
ps.spawnSync('git', ['reset', '--hard'], {cwd});
ps.spawnSync('git', ['checkout', 'gh-pages'], {cwd});
const gitLog = ps.spawnSync('git', ['log'], {cwd}).stdout.toString();
console.log('Gettings tags...');
const tags = ps.spawnSync('git', ['tag', '-l'], {cwd})
.stdout.toString()
.split('\n')
.filter(tag => tag);
console.log(`Found ${tags.length} tags`);
const stats = [];
for (const tag of tags) {
console.log(`Tag: ${tag}`);
const match = new RegExp(`commit (\\w+)\\nAuthor[^\\n]+\\nDate:\\s*([^\\n]+)\\n\\n\\s*${tag}`).exec(gitLog);
const [, rev, date] = match;
const dt = new Date(date).getTime();
const checkoutRes = ps.spawnSync('git', ['checkout', rev], {cwd});
if (checkoutRes.error) {
console.error('Checkout error', checkoutRes.error);
throw 'Checkout error';
}
const size = {};
const data = fs.readFileSync(path.join(cwd, 'index.html'));
const html = data.toString();
size.total = data.byteLength;
size.favicons = /<link rel="shortcut icon"[^>]+>/.exec(html)[0].length +
(/<link rel="apple-touch-icon"[^>]+>/.exec(html) || [''])[0].length;
size.css = /<style>.*?<\/style>/.exec(html)[0].length;
size.jsVendor = /<\/style><script>[\s\S]*?<\/script>/.exec(html)[0].length;
size.jsApp = /<\/script><script>[\s\S]*?<\/script>/.exec(html)[0].length;
stats.push({ tag, rev, dt, size });
}
// https://api.github.com/repos/keeweb/keeweb/issues?state=all&sort=created&direction=asc
console.log('Saving stats...');
fs.writeFileSync(path.resolve(__dirname, 'release-stats.json'), JSON.stringify(stats, null, 2));
console.log('Done');