This commit is contained in:
Antelle 2016-03-12 19:49:52 +03:00
parent 9725f8bb61
commit 3cdc6b2cb9
9 changed files with 180 additions and 45 deletions

View File

@ -301,7 +301,7 @@ var AppModel = Backbone.Model.extend({
var storage = Storage[params.storage];
var storageLoad = function() {
logger.info('Load from storage');
storage.load(params.path, function(err, data, stat) {
storage.load(params.path, null, function(err, data, stat) {
if (err) {
if (fileInfo) {
logger.info('Open file from cache because of storage load error', err);
@ -321,7 +321,7 @@ var AppModel = Backbone.Model.extend({
var cacheRev = fileInfo && fileInfo.get('rev') || null;
if (cacheRev && storage.stat) {
logger.info('Stat file');
storage.stat(params.path, function(err, stat) {
storage.stat(params.path, null, function(err, stat) {
if (fileInfo && (err || stat && stat.rev === cacheRev)) {
logger.info('Open file from cache because ' + (err ? 'stat error' : 'it is latest'), err);
that.openFileFromCache(params, callback, fileInfo);
@ -341,7 +341,7 @@ var AppModel = Backbone.Model.extend({
openFileFromCache: function(params, callback, fileInfo) {
var that = this;
Storage.cache.load(fileInfo.id, function(err, data) {
Storage.cache.load(fileInfo.id, null, function(err, data) {
new Logger('open', params.name).info('Loaded file from cache', err);
if (err) {
callback(err);
@ -387,7 +387,7 @@ var AppModel = Backbone.Model.extend({
file.set('cacheId', cacheId);
if (updateCacheOnSuccess) {
logger.info('Save loaded file to cache');
Storage.cache.save(cacheId, params.fileData);
Storage.cache.save(cacheId, null, params.fileData);
}
var rev = params.rev || fileInfo && fileInfo.get('rev');
that.addToLastOpenFiles(file, rev);
@ -535,7 +535,7 @@ var AppModel = Backbone.Model.extend({
logger.info('Local, save to cache');
file.getData(function(data, err) {
if (err) { return complete(err); }
Storage.cache.save(fileInfo.id, data, function(err) {
Storage.cache.save(fileInfo.id, null, data, function(err) {
logger.info('Saved to cache', err || 'no error');
complete(err);
});
@ -547,7 +547,7 @@ var AppModel = Backbone.Model.extend({
return complete('Too many load attempts');
}
logger.info('Load from storage, attempt ' + loadLoops);
Storage[storage].load(path, function(err, data, stat) {
Storage[storage].load(path, null, function(err, data, stat) {
logger.info('Load from storage', stat, err || 'no error');
if (err) { return complete(err); }
file.mergeOrUpdate(data, options.remoteKey, function(err) {
@ -570,7 +570,7 @@ var AppModel = Backbone.Model.extend({
saveToCacheAndStorage();
} else if (file.get('dirty')) {
logger.info('Saving not modified dirty file to cache');
Storage.cache.save(fileInfo.id, data, function (err) {
Storage.cache.save(fileInfo.id, null, data, function (err) {
if (err) { return complete(err); }
file.set('dirty', false);
logger.info('Complete, remove dirty flag');
@ -592,7 +592,7 @@ var AppModel = Backbone.Model.extend({
saveToStorage(data);
} else {
logger.info('Saving to cache');
Storage.cache.save(fileInfo.id, data, function (err) {
Storage.cache.save(fileInfo.id, null, data, function (err) {
if (err) { return complete(err); }
file.set('dirty', false);
logger.info('Saved to cache, saving to storage');
@ -603,7 +603,7 @@ var AppModel = Backbone.Model.extend({
};
var saveToStorage = function(data) {
logger.info('Save data to storage');
Storage[storage].save(path, data, function(err, stat) {
Storage[storage].save(path, null, data, function(err, stat) {
if (err && err.revConflict) {
logger.info('Save rev conflict, reloading from storage');
loadFromStorageAndMerge();
@ -622,7 +622,7 @@ var AppModel = Backbone.Model.extend({
}, fileInfo.get('rev'));
};
logger.info('Stat file');
Storage[storage].stat(path, function (err, stat) {
Storage[storage].stat(path, null, function (err, stat) {
if (err) {
if (err.notFound) {
logger.info('File does not exist in storage, creating');
@ -631,7 +631,7 @@ var AppModel = Backbone.Model.extend({
logger.info('Stat error, dirty, save to cache', err || 'no error');
file.getData(function (data) {
if (data) {
Storage.cache.save(fileInfo.id, data, function (e) {
Storage.cache.save(fileInfo.id, null, data, function (e) {
if (!e) {
file.set('dirty', false);
}

View File

@ -38,7 +38,7 @@ var StorageCache = {
}
},
save: function(id, data, callback) {
save: function(id, opts, data, callback) {
logger.debug('Save', id);
this.init((function(err) {
if (err) {
@ -62,7 +62,7 @@ var StorageCache = {
}).bind(this));
},
load: function(id, callback) {
load: function(id, opts, callback) {
logger.debug('Load', id);
this.init((function(err) {
if (err) {
@ -86,7 +86,7 @@ var StorageCache = {
}).bind(this));
},
remove: function(id, callback) {
remove: function(id, opts, callback) {
logger.debug('Remove', id);
this.init((function(err) {
if (err) {

View File

@ -27,7 +27,7 @@ var StorageDropbox = {
return '/' + fileName + '.kdbx';
},
load: function(path, callback) {
load: function(path, opts, callback) {
logger.debug('Load', path);
var ts = logger.ts();
DropboxLink.openFile(path, function(err, data, stat) {
@ -37,7 +37,7 @@ var StorageDropbox = {
}, _.noop);
},
stat: function(path, callback) {
stat: function(path, opts, callback) {
logger.debug('Stat', path);
var ts = logger.ts();
DropboxLink.stat(path, function(err, stat) {
@ -51,7 +51,7 @@ var StorageDropbox = {
}, _.noop);
},
save: function(path, data, callback, rev) {
save: function(path, opts, data, callback, rev) {
logger.debug('Save', path, rev);
var ts = logger.ts();
DropboxLink.saveFile(path, data, rev, function(err, stat) {

View File

@ -33,7 +33,7 @@ var StorageFileCache = {
}
},
save: function(id, data, callback) {
save: function(id, opts, data, callback) {
logger.debug('Save', id);
this.init((function(err) {
if (err) {
@ -51,7 +51,7 @@ var StorageFileCache = {
}).bind(this));
},
load: function(id, callback) {
load: function(id, opts, callback) {
logger.debug('Load', id);
this.init((function(err) {
if (err) {
@ -69,7 +69,7 @@ var StorageFileCache = {
}).bind(this));
},
remove: function(id, callback) {
remove: function(id, opts, callback) {
logger.debug('Remove', id);
this.init((function(err) {
if (err) {

View File

@ -12,7 +12,7 @@ var StorageFile = {
icon: 'hdd-o',
enabled: !!Launcher,
load: function(path, callback) {
load: function(path, opts, callback) {
logger.debug('Load', path);
var ts = logger.ts();
try {
@ -26,7 +26,7 @@ var StorageFile = {
}
},
stat: function(path, callback) {
stat: function(path, opts, callback) {
logger.debug('Stat', path);
var ts = logger.ts();
try {
@ -42,7 +42,7 @@ var StorageFile = {
}
},
save: function(path, data, callback, rev) {
save: function(path, opts, data, callback, rev) {
logger.debug('Save', path, rev);
var ts = logger.ts();
try {

View File

@ -10,32 +10,132 @@ var StorageDropbox = {
enabled: true,
openFields: [
{ id: 'url', title: 'openUrl', desc: 'openUrlDesc', type: 'text', required: true },
{ id: 'user', title: 'openUser', placeholder: 'openUserPlaceholder', type: 'text' },
{ id: 'password', title: 'openPass', placeholder: 'openPassPlaceholder', type: 'password' }
{ id: 'path', title: 'openUrl', desc: 'openUrlDesc', type: 'text', required: true },
{ id: 'user', title: 'openUser', desc: 'openUserDesc', placeholder: 'openUserPlaceholder', type: 'text' },
{ id: 'password', title: 'openPass', desc: 'openPassDesc', placeholder: 'openPassPlaceholder', type: 'password' }
],
load: function(path, callback) {
load: function(path, opts, callback) {
logger.debug('Load', path);
var ts = logger.ts();
var stat = {};
logger.debug('Loaded', path, stat ? stat.versionTag : null, logger.ts(ts));
callback('not implemented');
this._request({
op: 'Load',
method: 'GET',
path: path,
user: opts ? opts.user : null,
password: opts ? opts.password : null
}, callback ? function(err, xhr, stat) {
callback(err, xhr.response, stat);
} : null);
},
stat: function(path, callback) {
stat: function(path, opts, callback) {
logger.debug('Stat', path);
var ts = logger.ts();
var stat = {};
logger.debug('Stated', path, stat ? stat.versionTag : null, logger.ts(ts));
callback('not implemented');
this._request({
op: 'Stat',
method: 'HEAD',
path: path,
user: opts ? opts.user : null,
password: opts ? opts.password : null
}, callback ? function(err, xhr, stat) {
callback(err, stat);
} : null);
},
save: function(path, data, callback, rev) {
save: function(path, opts, data, callback, rev) {
logger.debug('Save', path, rev);
var etag, lastModified;
if (rev && rev.charAt(0) === 'E') {
etag = rev.substr(1);
} else if (rev && rev.charAt(0) === 'T') {
lastModified = rev.substr(1);
}
var cb = callback ? function(err, xhr, stat) {
callback(err, stat);
} : null;
var saveOpts = {
op: 'Save',
method: 'POST',
path: path,
user: opts ? opts.user : null,
password: opts ? opts.password : null,
data: data,
etag: etag
};
if (lastModified) {
logger.debug('Stat before save', path, rev);
this.stat(path, opts, function(err, stat) {
if (err) { return cb(err); }
if (stat.rev !== rev) {
logger.debug('Save error', path, 'rev conflict', stat.rev, rev);
return cb({ revConflict: true });
}
this._request(saveOpts, cb);
});
} else {
this._request(saveOpts, cb);
}
},
_request: function(config, callback) {
var ts = logger.ts();
logger.debug('Saved', path, logger.ts(ts));
callback('not implemented');
var xhr = new XMLHttpRequest();
xhr.addEventListener('load', function() {
if (xhr.status !== 200) {
logger.debug(config.op + ' error', config.path, xhr.status, logger.ts(ts));
var err;
switch (xhr.status) {
case 404:
err = { notFound: true };
break;
case 412:
err = { revConflict: true };
break;
default:
err = 'HTTP status ' + xhr.status;
break;
}
if (callback) { callback(err); callback = null; }
return;
}
var rev = xhr.getResponseHeader('ETag');
if (rev) {
rev = 'E' + rev;
} else {
rev = xhr.getResponseHeader('Last-Modified');
if (rev) {
rev = 'T' + rev;
}
}
if (!rev) {
logger.debug(config.op + ' error', config.path, 'no headers', logger.ts(ts));
if (callback) { callback('No header ETag or Last-Modified'); callback = null; }
return;
}
logger.debug(config.op + 'ed', config.path, rev, logger.ts(ts));
if (callback) { callback(null, xhr, rev ? { rev: rev } : null); callback = null; }
});
xhr.addEventListener('error', function() {
logger.debug(config.op + ' error', config.path, logger.ts(ts));
if (callback) { callback('network error'); callback = null; }
});
xhr.addEventListener('abort', function() {
logger.debug(config.op + ' error', config.path, 'aborted', logger.ts(ts));
if (callback) { callback('aborted'); callback = null; }
});
xhr.responseType = 'arraybuffer';
xhr.open(config.method, config.path);
if (config.user) {
xhr.setRequestHeader('Authorization', 'Basic ' + btoa(config.user + ':' + config.password));
}
if (config.etag) {
xhr.setRequestHeader('If-Match', config.etag);
}
if (config.data) {
var blob = new Blob([config.data], {type: 'application/octet-stream'});
xhr.send(blob);
} else {
xhr.send();
}
}
};

View File

@ -135,8 +135,10 @@ var Locale = {
openUrl: 'URL',
openUrlDesc: 'https://server/path/file.kdbx, or just file.kdbx',
openUser: 'Username',
openUserDesc: 'WebDAV server username (if required)',
openUserPlaceholder: 'no username',
openPass: 'Password',
openPassDesc: 'WebDAV server password (this is not your file password)',
openPassPlaceholder: 'no password',
openConfigError: 'Error: {}',

View File

@ -0,0 +1,14 @@
'use strict';
var UrlUtil = {
getDataFileName: function(url) {
var ix = url.lastIndexOf('/');
if (ix >= 0) {
url = url.substr(ix + 1);
}
url = url.replace(/\?.*/, '').replace(/\.kdbx/i, '');
return url;
}
};
module.exports = UrlUtil;

View File

@ -9,6 +9,7 @@ var Backbone = require('backbone'),
DropboxLink = require('../comp/dropbox-link'),
Logger = require('../util/logger'),
Locale = require('../util/locale'),
UrlUtil = require('../util/url-util'),
Storage = require('../storage');
var logger = new Logger('open-view');
@ -398,7 +399,7 @@ var OpenView = Backbone.View.extend({
var allDropboxFiles = {};
filesStat.forEach(function(file) {
if (!file.isFolder && !file.isRemoved) {
var fileName = file.name.replace(/\.kdbx/i, '');
var fileName = UrlUtil.getDataFileName(file.name);
buttons.push({ result: file.path, title: fileName });
allDropboxFiles[file.path] = file;
}
@ -438,7 +439,7 @@ var OpenView = Backbone.View.extend({
this.params.id = null;
this.params.storage = 'dropbox';
this.params.path = fileStat.path;
this.params.name = fileStat.name.replace(/\.kdbx/i, '');
this.params.name = UrlUtil.getDataFileName(fileStat.name);
this.params.rev = fileStat.versionTag;
this.params.fileData = null;
this.displayOpenFile();
@ -577,18 +578,36 @@ var OpenView = Backbone.View.extend({
this.views.openConfig.setDisabled(true);
var storage = Storage[config.storage];
this.storageWaitId = Math.random();
storage.stat(config, this.storageStatComplete.bind(this, this.storageWaitId));
var path = config.path;
var opts = _.omit(config, 'path');
var req = {
waitId: this.storageWaitId,
storage: config.storage,
path: path,
opts: opts
};
storage.stat(path, opts, this.storageStatComplete.bind(this, req));
},
storageStatComplete: function(waitId, err) {
if (this.storageWaitId !== waitId) {
storageStatComplete: function(req, err, stat) {
if (this.storageWaitId !== req.waitId) {
return;
}
this.storageWaitId = null;
this.busy = false;
this.views.openConfig.setDisabled(false);
if (err) {
this.views.openConfig.setDisabled(false);
this.views.openConfig.setError(err);
} else {
this.closeConfig();
this.params.id = null;
this.params.storage = req.storage;
this.params.path = req.path;
this.params.opts = req.opts;
this.params.name = UrlUtil.getDataFileName(req.path);
this.params.rev = stat.rev;
this.params.fileData = null;
this.displayOpenFile();
}
}
});