2015-10-17 23:49:24 +02:00
|
|
|
'use strict';
|
|
|
|
|
|
|
|
var Backbone = require('backbone'),
|
|
|
|
GroupCollection = require('../collections/group-collection'),
|
|
|
|
GroupModel = require('./group-model'),
|
2015-11-21 23:15:51 +01:00
|
|
|
IconUrl = require('../util/icon-url'),
|
2015-12-12 09:53:50 +01:00
|
|
|
Logger = require('../util/logger'),
|
2015-10-17 23:49:24 +02:00
|
|
|
kdbxweb = require('kdbxweb'),
|
|
|
|
demoFileData = require('base64!../../resources/Demo.kdbx');
|
|
|
|
|
2015-12-12 09:53:50 +01:00
|
|
|
var logger = new Logger('file');
|
|
|
|
|
2015-10-17 23:49:24 +02:00
|
|
|
var FileModel = Backbone.Model.extend({
|
|
|
|
defaults: {
|
2015-12-02 21:50:31 +01:00
|
|
|
id: '',
|
2015-10-17 23:49:24 +02:00
|
|
|
name: '',
|
|
|
|
keyFileName: '',
|
2015-10-21 22:17:53 +02:00
|
|
|
passwordLength: 0,
|
2015-10-17 23:49:24 +02:00
|
|
|
path: '',
|
2015-10-25 17:27:34 +01:00
|
|
|
storage: null,
|
2015-10-17 23:49:24 +02:00
|
|
|
modified: false,
|
2015-12-11 18:50:44 +01:00
|
|
|
dirty: false,
|
2015-10-17 23:49:24 +02:00
|
|
|
open: false,
|
|
|
|
created: false,
|
|
|
|
demo: false,
|
2015-10-24 18:43:30 +02:00
|
|
|
groups: null,
|
|
|
|
oldPasswordLength: 0,
|
|
|
|
oldKeyFileName: '',
|
|
|
|
passwordChanged: false,
|
2015-10-25 17:27:34 +01:00
|
|
|
keyFileChanged: false,
|
2015-12-10 22:31:47 +01:00
|
|
|
syncing: false,
|
2015-12-11 21:51:16 +01:00
|
|
|
syncError: null,
|
2015-12-12 16:43:43 +01:00
|
|
|
syncDate: null,
|
|
|
|
cacheId: null
|
2015-10-17 23:49:24 +02:00
|
|
|
},
|
|
|
|
|
|
|
|
db: null,
|
2015-12-05 14:04:09 +01:00
|
|
|
entryMap: null,
|
|
|
|
groupMap: null,
|
2015-10-17 23:49:24 +02:00
|
|
|
|
|
|
|
initialize: function() {
|
2015-12-05 14:04:09 +01:00
|
|
|
this.entryMap = {};
|
|
|
|
this.groupMap = {};
|
2015-10-17 23:49:24 +02:00
|
|
|
},
|
|
|
|
|
2015-12-06 21:32:41 +01:00
|
|
|
open: function(password, fileData, keyFileData, callback) {
|
2015-10-17 23:49:24 +02:00
|
|
|
try {
|
|
|
|
var credentials = new kdbxweb.Credentials(password, keyFileData);
|
2015-12-12 09:53:50 +01:00
|
|
|
var ts = logger.ts();
|
2015-11-03 22:59:57 +01:00
|
|
|
kdbxweb.Kdbx.load(fileData, credentials, (function(db, err) {
|
|
|
|
if (err) {
|
2015-12-12 09:53:50 +01:00
|
|
|
logger.error('Error opening file', err.code, err.message, err);
|
2015-12-06 21:32:41 +01:00
|
|
|
callback(err);
|
2015-11-03 22:59:57 +01:00
|
|
|
} else {
|
|
|
|
this.db = db;
|
2015-12-13 22:27:26 +01:00
|
|
|
this.readModel();
|
2016-01-16 21:32:50 +01:00
|
|
|
this.setOpenFile({ passwordLength: password.textLength });
|
2015-11-06 21:30:36 +01:00
|
|
|
if (keyFileData) {
|
|
|
|
kdbxweb.ByteUtils.zeroBuffer(keyFileData);
|
|
|
|
}
|
2015-12-12 09:53:50 +01:00
|
|
|
logger.info('Opened file ' + this.get('name') + ': ' + logger.ts(ts) + ', ' +
|
2015-11-06 21:30:36 +01:00
|
|
|
db.header.keyEncryptionRounds + ' rounds, ' + Math.round(fileData.byteLength / 1024) + ' kB');
|
2015-12-06 21:32:41 +01:00
|
|
|
callback();
|
2015-11-03 22:59:57 +01:00
|
|
|
}
|
|
|
|
}).bind(this));
|
2015-10-17 23:49:24 +02:00
|
|
|
} catch (e) {
|
2015-12-12 09:53:50 +01:00
|
|
|
logger.error('Error opening file', e, e.code, e.message, e);
|
2015-12-06 21:32:41 +01:00
|
|
|
callback(e);
|
2015-10-17 23:49:24 +02:00
|
|
|
}
|
|
|
|
},
|
|
|
|
|
|
|
|
create: function(name) {
|
|
|
|
var password = kdbxweb.ProtectedValue.fromString('');
|
|
|
|
var credentials = new kdbxweb.Credentials(password);
|
|
|
|
this.db = kdbxweb.Kdbx.create(credentials, name);
|
2015-12-13 22:27:26 +01:00
|
|
|
this.set('name', name);
|
2015-10-17 23:49:24 +02:00
|
|
|
this.readModel();
|
2015-12-07 20:07:56 +01:00
|
|
|
this.set({ open: true, created: true, name: name });
|
2015-10-17 23:49:24 +02:00
|
|
|
},
|
|
|
|
|
2015-12-06 21:32:41 +01:00
|
|
|
openDemo: function(callback) {
|
2015-10-17 23:49:24 +02:00
|
|
|
var password = kdbxweb.ProtectedValue.fromString('demo');
|
|
|
|
var credentials = new kdbxweb.Credentials(password);
|
|
|
|
var demoFile = kdbxweb.ByteUtils.arrayToBuffer(kdbxweb.ByteUtils.base64ToBytes(demoFileData));
|
2015-11-03 22:59:57 +01:00
|
|
|
kdbxweb.Kdbx.load(demoFile, credentials, (function(db) {
|
|
|
|
this.db = db;
|
2015-12-13 22:27:26 +01:00
|
|
|
this.set('name', 'Demo');
|
2015-11-03 22:59:57 +01:00
|
|
|
this.readModel();
|
2015-12-13 22:27:26 +01:00
|
|
|
this.setOpenFile({passwordLength: 4, demo: true});
|
2015-12-06 21:32:41 +01:00
|
|
|
callback();
|
2015-11-03 22:59:57 +01:00
|
|
|
}).bind(this));
|
2015-10-24 18:43:30 +02:00
|
|
|
},
|
|
|
|
|
|
|
|
setOpenFile: function(props) {
|
|
|
|
_.extend(props, {
|
|
|
|
open: true,
|
|
|
|
oldKeyFileName: this.get('keyFileName'),
|
2015-10-24 21:06:44 +02:00
|
|
|
oldPasswordLength: props.passwordLength,
|
|
|
|
passwordChanged: false,
|
|
|
|
keyFileChanged: false
|
2015-10-24 18:43:30 +02:00
|
|
|
});
|
|
|
|
this.set(props);
|
2015-10-24 21:06:44 +02:00
|
|
|
this._oldPasswordHash = this.db.credentials.passwordHash;
|
|
|
|
this._oldKeyFileHash = this.db.credentials.keyFileHash;
|
|
|
|
this._oldKeyChangeDate = this.db.meta.keyChanged;
|
2015-10-17 23:49:24 +02:00
|
|
|
},
|
|
|
|
|
2015-12-13 22:27:26 +01:00
|
|
|
readModel: function() {
|
2015-10-17 23:49:24 +02:00
|
|
|
var groups = new GroupCollection();
|
2015-10-22 22:39:30 +02:00
|
|
|
this.set({
|
2015-12-02 21:50:31 +01:00
|
|
|
id: this.db.getDefaultGroup().uuid.toString(),
|
2015-10-22 22:39:30 +02:00
|
|
|
groups: groups,
|
|
|
|
defaultUser: this.db.meta.defaultUser,
|
|
|
|
recycleBinEnabled: this.db.meta.recycleBinEnabled,
|
|
|
|
historyMaxItems: this.db.meta.historyMaxItems,
|
|
|
|
historyMaxSize: this.db.meta.historyMaxSize,
|
|
|
|
keyEncryptionRounds: this.db.header.keyEncryptionRounds
|
|
|
|
}, { silent: true });
|
2015-12-05 14:04:09 +01:00
|
|
|
this.db.groups.forEach(function(group) {
|
|
|
|
var groupModel = this.getGroup(group.uuid.id);
|
|
|
|
if (groupModel) {
|
|
|
|
groupModel.setGroup(group, this);
|
|
|
|
} else {
|
|
|
|
groupModel = GroupModel.fromGroup(group, this);
|
|
|
|
}
|
2015-12-13 22:27:26 +01:00
|
|
|
groupModel.set({title: this.get('name')});
|
2015-10-17 23:49:24 +02:00
|
|
|
groups.add(groupModel);
|
|
|
|
}, this);
|
2015-12-05 14:04:09 +01:00
|
|
|
this.buildObjectMap();
|
|
|
|
},
|
|
|
|
|
|
|
|
buildObjectMap: function() {
|
|
|
|
var entryMap = {};
|
|
|
|
var groupMap = {};
|
|
|
|
this.forEachGroup(function(group) {
|
|
|
|
groupMap[group.id] = group;
|
|
|
|
group.forEachOwnEntry(null, function(entry) {
|
|
|
|
entryMap[entry.id] = entry;
|
|
|
|
});
|
|
|
|
}, true);
|
|
|
|
this.entryMap = entryMap;
|
|
|
|
this.groupMap = groupMap;
|
|
|
|
},
|
|
|
|
|
|
|
|
reload: function() {
|
|
|
|
this.buildObjectMap();
|
2015-12-13 22:27:26 +01:00
|
|
|
this.readModel();
|
2015-12-05 14:04:09 +01:00
|
|
|
this.trigger('reload', this);
|
2015-10-17 23:49:24 +02:00
|
|
|
},
|
|
|
|
|
2016-01-16 21:32:50 +01:00
|
|
|
mergeOrUpdate: function(fileData, remoteKey, callback) {
|
|
|
|
var credentials;
|
|
|
|
if (remoteKey) {
|
|
|
|
credentials = new kdbxweb.Credentials(kdbxweb.ProtectedValue.fromString(''));
|
|
|
|
if (remoteKey.password) {
|
|
|
|
credentials.setPassword(remoteKey.password);
|
|
|
|
} else {
|
|
|
|
credentials.passwordHash = this.db.credentials.passwordHash;
|
|
|
|
}
|
|
|
|
if (remoteKey.keyFileName) {
|
|
|
|
if (remoteKey.keyFileData) {
|
|
|
|
credentials.setKeyFile(remoteKey.keyFileData);
|
|
|
|
} else {
|
|
|
|
credentials.keyFileHash = this.db.credentials.keyFileHash;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
credentials = this.db.credentials;
|
|
|
|
}
|
|
|
|
kdbxweb.Kdbx.load(fileData, credentials, (function(remoteDb, err) {
|
2015-12-10 20:44:02 +01:00
|
|
|
if (err) {
|
2015-12-12 09:53:50 +01:00
|
|
|
logger.error('Error opening file to merge', err.code, err.message, err);
|
2015-12-10 20:44:02 +01:00
|
|
|
} else {
|
2015-12-10 22:31:47 +01:00
|
|
|
if (this.get('modified')) {
|
|
|
|
try {
|
2016-01-16 21:32:50 +01:00
|
|
|
if (remoteKey && remoteDb.meta.keyChanged > this.db.meta.keyChanged) {
|
|
|
|
this.db.credentials = remoteDb.credentials;
|
|
|
|
this.set('keyFileName', remoteKey.keyFileName || '');
|
|
|
|
if (remoteKey.password) {
|
|
|
|
this.set('passwordLength', remoteKey.password.textLength);
|
|
|
|
}
|
|
|
|
}
|
2015-12-10 22:31:47 +01:00
|
|
|
this.db.merge(remoteDb);
|
|
|
|
} catch (e) {
|
2015-12-12 09:53:50 +01:00
|
|
|
logger.error('File merge error', e);
|
2015-12-10 22:31:47 +01:00
|
|
|
return callback(e);
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
this.db = remoteDb;
|
|
|
|
}
|
2015-12-12 16:56:16 +01:00
|
|
|
this.set('dirty', true);
|
|
|
|
this.reload();
|
2015-12-10 20:44:02 +01:00
|
|
|
}
|
|
|
|
callback(err);
|
|
|
|
}).bind(this));
|
|
|
|
},
|
|
|
|
|
2015-12-10 22:31:47 +01:00
|
|
|
getLocalEditState: function() {
|
|
|
|
return this.db.getLocalEditState();
|
|
|
|
},
|
|
|
|
|
|
|
|
setLocalEditState: function(editState) {
|
|
|
|
this.db.setLocalEditState(editState);
|
|
|
|
},
|
|
|
|
|
2015-11-17 21:57:32 +01:00
|
|
|
close: function() {
|
|
|
|
this.set({
|
|
|
|
keyFileName: '',
|
|
|
|
passwordLength: 0,
|
|
|
|
modified: false,
|
2015-12-11 18:50:44 +01:00
|
|
|
dirty: false,
|
2015-11-17 21:57:32 +01:00
|
|
|
open: false,
|
|
|
|
created: false,
|
|
|
|
groups: null,
|
|
|
|
passwordChanged: false,
|
|
|
|
keyFileChanged: false,
|
|
|
|
syncing: false
|
|
|
|
});
|
|
|
|
},
|
|
|
|
|
2015-12-05 14:04:09 +01:00
|
|
|
getEntry: function(id) {
|
|
|
|
return this.entryMap[id];
|
|
|
|
},
|
|
|
|
|
2015-10-17 23:49:24 +02:00
|
|
|
getGroup: function(id) {
|
2015-12-05 14:04:09 +01:00
|
|
|
return this.groupMap[id];
|
2015-10-17 23:49:24 +02:00
|
|
|
},
|
|
|
|
|
|
|
|
forEachEntry: function(filter, callback) {
|
|
|
|
var top = this;
|
|
|
|
if (filter.trash) {
|
|
|
|
top = this.getGroup(this.db.meta.recycleBinUuid ? this.db.meta.recycleBinUuid.id : null);
|
|
|
|
} else if (filter.group) {
|
|
|
|
top = this.getGroup(filter.group);
|
|
|
|
}
|
|
|
|
if (top) {
|
|
|
|
if (top.forEachOwnEntry) {
|
|
|
|
top.forEachOwnEntry(filter, callback);
|
|
|
|
}
|
2015-11-06 21:42:37 +01:00
|
|
|
if (!filter.group || filter.subGroups) {
|
|
|
|
top.forEachGroup(function (group) {
|
|
|
|
group.forEachOwnEntry(filter, callback);
|
|
|
|
});
|
|
|
|
}
|
2015-10-17 23:49:24 +02:00
|
|
|
}
|
|
|
|
},
|
|
|
|
|
|
|
|
forEachGroup: function(callback, includeDisabled) {
|
|
|
|
this.get('groups').forEach(function(group) {
|
|
|
|
if (callback(group) !== false) {
|
|
|
|
group.forEachGroup(callback, includeDisabled);
|
|
|
|
}
|
|
|
|
});
|
|
|
|
},
|
|
|
|
|
|
|
|
getTrashGroup: function() {
|
|
|
|
return this.db.meta.recycleBinEnabled ? this.getGroup(this.db.meta.recycleBinUuid.id) : null;
|
|
|
|
},
|
|
|
|
|
|
|
|
setModified: function() {
|
|
|
|
if (!this.get('demo')) {
|
2015-12-11 18:50:44 +01:00
|
|
|
this.set({ modified: true, dirty: true });
|
2015-10-17 23:49:24 +02:00
|
|
|
}
|
2015-10-18 16:02:00 +02:00
|
|
|
},
|
|
|
|
|
2015-11-03 22:59:57 +01:00
|
|
|
getData: function(cb) {
|
2015-11-22 09:45:40 +01:00
|
|
|
this.db.cleanup({
|
|
|
|
historyRules: true,
|
2015-12-17 21:22:36 +01:00
|
|
|
customIcons: true,
|
|
|
|
binaries: true
|
2015-11-22 09:45:40 +01:00
|
|
|
});
|
2015-12-10 20:44:02 +01:00
|
|
|
var that = this;
|
2015-12-17 21:22:36 +01:00
|
|
|
this.db.cleanup({ binaries: true });
|
2015-12-10 20:44:02 +01:00
|
|
|
this.db.save(function(data, err) {
|
|
|
|
if (err) {
|
2015-12-12 09:53:50 +01:00
|
|
|
logger.error('Error saving file', that.get('name'), err);
|
2015-12-10 20:44:02 +01:00
|
|
|
}
|
|
|
|
cb(data, err);
|
|
|
|
});
|
2015-10-18 16:02:00 +02:00
|
|
|
},
|
|
|
|
|
2015-11-03 22:59:57 +01:00
|
|
|
getXml: function(cb) {
|
|
|
|
this.db.saveXml(cb);
|
2015-10-24 18:43:30 +02:00
|
|
|
},
|
|
|
|
|
2015-12-10 22:31:47 +01:00
|
|
|
setSyncProgress: function() {
|
|
|
|
this.set({ syncing: true });
|
|
|
|
},
|
|
|
|
|
2015-12-11 18:50:44 +01:00
|
|
|
setSyncComplete: function(path, storage, error, savedToCache) {
|
|
|
|
if (!error) {
|
|
|
|
this.db.removeLocalEditState();
|
|
|
|
}
|
2015-12-10 22:31:47 +01:00
|
|
|
var modified = this.get('modified') && !!error;
|
2015-12-11 18:50:44 +01:00
|
|
|
var dirty = this.get('dirty') && !savedToCache;
|
2015-12-10 22:31:47 +01:00
|
|
|
this.set({
|
|
|
|
created: false,
|
|
|
|
path: path || this.get('path'),
|
|
|
|
storage: storage || this.get('storage'),
|
|
|
|
modified: modified,
|
2015-12-11 18:50:44 +01:00
|
|
|
dirty: dirty,
|
2015-12-10 22:31:47 +01:00
|
|
|
syncing: false,
|
|
|
|
syncError: error
|
|
|
|
});
|
2015-10-24 21:06:44 +02:00
|
|
|
this.setOpenFile({ passwordLength: this.get('passwordLength') });
|
|
|
|
this.forEachEntry({}, function(entry) {
|
|
|
|
entry.unsaved = false;
|
|
|
|
});
|
|
|
|
},
|
|
|
|
|
2015-10-24 18:43:30 +02:00
|
|
|
setPassword: function(password) {
|
|
|
|
this.db.credentials.setPassword(password);
|
|
|
|
this.db.meta.keyChanged = new Date();
|
2016-01-16 15:19:33 +01:00
|
|
|
this.set({ passwordLength: password.textLength, passwordChanged: true });
|
2015-10-24 18:43:30 +02:00
|
|
|
this.setModified();
|
|
|
|
},
|
|
|
|
|
|
|
|
resetPassword: function() {
|
|
|
|
this.db.credentials.passwordHash = this._oldPasswordHash;
|
|
|
|
if (this.db.credentials.keyFileHash === this._oldKeyFileHash) {
|
|
|
|
this.db.meta.keyChanged = this._oldKeyChangeDate;
|
|
|
|
}
|
|
|
|
this.set({ passwordLength: this.get('oldPasswordLength'), passwordChanged: false });
|
|
|
|
},
|
|
|
|
|
|
|
|
setKeyFile: function(keyFile, keyFileName) {
|
|
|
|
this.db.credentials.setKeyFile(keyFile);
|
|
|
|
this.db.meta.keyChanged = new Date();
|
|
|
|
this.set({ keyFileName: keyFileName, keyFileChanged: true });
|
|
|
|
this.setModified();
|
|
|
|
},
|
|
|
|
|
|
|
|
generateAndSetKeyFile: function() {
|
|
|
|
var keyFile = kdbxweb.Credentials.createRandomKeyFile();
|
|
|
|
var keyFileName = 'Generated';
|
|
|
|
this.setKeyFile(keyFile, keyFileName);
|
|
|
|
return keyFile;
|
|
|
|
},
|
|
|
|
|
|
|
|
resetKeyFile: function() {
|
|
|
|
this.db.credentials.keyFileHash = this._oldKeyFileHash;
|
|
|
|
if (this.db.credentials.passwordHash === this._oldPasswordHash) {
|
|
|
|
this.db.meta.keyChanged = this._oldKeyChangeDate;
|
|
|
|
}
|
|
|
|
this.set({ keyFileName: this.get('oldKeyFileName'), keyFileChanged: false });
|
|
|
|
},
|
|
|
|
|
|
|
|
removeKeyFile: function() {
|
|
|
|
this.db.credentials.keyFileHash = null;
|
|
|
|
var changed = !!this._oldKeyFileHash;
|
|
|
|
if (!changed && this.db.credentials.passwordHash === this._oldPasswordHash) {
|
|
|
|
this.db.meta.keyChanged = this._oldKeyChangeDate;
|
|
|
|
}
|
|
|
|
this.set({ keyFileName: '', keyFileChanged: changed });
|
|
|
|
},
|
|
|
|
|
|
|
|
setName: function(name) {
|
|
|
|
this.db.meta.name = name;
|
|
|
|
this.db.meta.nameChanged = new Date();
|
|
|
|
this.set('name', name);
|
2015-10-31 20:09:32 +01:00
|
|
|
this.get('groups').first().setName(name);
|
2015-10-24 18:43:30 +02:00
|
|
|
this.setModified();
|
2015-12-13 15:59:41 +01:00
|
|
|
this.reload();
|
2015-10-24 18:43:30 +02:00
|
|
|
},
|
|
|
|
|
|
|
|
setDefaultUser: function(defaultUser) {
|
|
|
|
this.db.meta.defaultUser = defaultUser;
|
|
|
|
this.db.meta.defaultUserChanged = new Date();
|
|
|
|
this.set('defaultUser', defaultUser);
|
|
|
|
this.setModified();
|
|
|
|
},
|
|
|
|
|
|
|
|
setRecycleBinEnabled: function(enabled) {
|
|
|
|
enabled = !!enabled;
|
|
|
|
this.db.meta.recycleBinEnabled = enabled;
|
|
|
|
if (enabled) {
|
|
|
|
this.db.createRecycleBin();
|
|
|
|
}
|
|
|
|
this.set('setRecycleBinEnabled', enabled);
|
|
|
|
this.setModified();
|
|
|
|
},
|
|
|
|
|
|
|
|
setHistoryMaxItems: function(count) {
|
|
|
|
this.db.meta.historyMaxItems = count;
|
|
|
|
this.set('historyMaxItems', count);
|
|
|
|
this.setModified();
|
|
|
|
},
|
|
|
|
|
|
|
|
setHistoryMaxSize: function(size) {
|
|
|
|
this.db.meta.historyMaxSize = size;
|
|
|
|
this.set('historyMaxSize', size);
|
|
|
|
this.setModified();
|
|
|
|
},
|
|
|
|
|
|
|
|
setKeyEncryptionRounds: function(rounds) {
|
|
|
|
this.db.header.keyEncryptionRounds = rounds;
|
|
|
|
this.set('keyEncryptionRounds', rounds);
|
|
|
|
this.setModified();
|
2015-11-08 22:25:00 +01:00
|
|
|
},
|
|
|
|
|
|
|
|
emptyTrash: function() {
|
|
|
|
var trashGroup = this.getTrashGroup();
|
|
|
|
if (trashGroup) {
|
|
|
|
trashGroup.getOwnSubGroups().slice().forEach(function(group) {
|
|
|
|
this.db.move(group, null);
|
|
|
|
}, this);
|
|
|
|
trashGroup.group.entries.forEach(function(entry) {
|
|
|
|
this.db.move(entry, null);
|
|
|
|
}, this);
|
|
|
|
trashGroup.get('entries').reset();
|
|
|
|
}
|
2015-11-22 09:21:12 +01:00
|
|
|
},
|
|
|
|
|
|
|
|
getCustomIcons: function() {
|
|
|
|
return _.mapObject(this.db.meta.customIcons, function(customIcon) {
|
|
|
|
return IconUrl.toDataUrl(customIcon);
|
|
|
|
});
|
|
|
|
},
|
|
|
|
|
|
|
|
addCustomIcon: function(iconData) {
|
2015-11-22 11:59:13 +01:00
|
|
|
var id = kdbxweb.KdbxUuid.random();
|
2015-11-22 09:21:12 +01:00
|
|
|
this.db.meta.customIcons[id] = kdbxweb.ByteUtils.arrayToBuffer(kdbxweb.ByteUtils.base64ToBytes(iconData));
|
|
|
|
return id.toString();
|
2015-10-17 23:49:24 +02:00
|
|
|
}
|
|
|
|
});
|
|
|
|
|
|
|
|
module.exports = FileModel;
|