Files
cryptpad/www/common/cryptpad-common.js
2020-01-09 17:30:15 +01:00

2158 lines
80 KiB
JavaScript
Raw Blame History

This file contains invisible Unicode characters
This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
define([
'/api/config',
'/customize/messages.js',
'/common/common-util.js',
'/common/common-hash.js',
'/common/common-messaging.js',
'/common/common-constants.js',
'/common/common-feedback.js',
'/common/userObject.js',
'/common/outer/local-store.js',
'/common/outer/worker-channel.js',
'/common/outer/login-block.js',
'/customize/application_config.js',
'/bower_components/nthen/index.js',
], function (Config, Messages, Util, Hash,
Messaging, Constants, Feedback, UserObject, LocalStore, Channel, Block,
AppConfig, Nthen) {
/* This file exposes functionality which is specific to Cryptpad, but not to
any particular pad type. This includes functions for committing metadata
about pads to your local storage for future use and improved usability.
Additionally, there is some basic functionality for import/export.
*/
var urlArgs = Util.find(Config, ['requireConf', 'urlArgs']) || '';
var postMessage = function (/*cmd, data, cb*/) {
/*setTimeout(function () {
AStore.query(cmd, data, cb);
});*/
console.error('NOT_READY');
};
var tryParsing = function (x) {
try { return JSON.parse(x); }
catch (e) {
console.error(e);
return null;
}
};
// Upgrade and donate URLs duplicated in pages.js
var origin = encodeURIComponent(window.location.hostname);
var common = window.Cryptpad = {
Messages: Messages,
//donateURL: 'https://accounts.cryptpad.fr/#/donate?on=' + origin,
donateURL: "https://opencollective.com/cryptpad/",
upgradeURL: 'https://accounts.cryptpad.fr/#/?on=' + origin,
account: {},
};
// COMMON
common.getLanguage = function () {
return Messages._languageUsed;
};
common.setLanguage = function (l, cb) {
var LS_LANG = "CRYPTPAD_LANG";
localStorage.setItem(LS_LANG, l);
cb();
};
common.makeNetwork = function (cb) {
require([
'/bower_components/netflux-websocket/netflux-client.js',
'/common/outer/network-config.js'
], function (Netflux, NetConfig) {
var wsUrl = NetConfig.getWebsocketURL();
Netflux.connect(wsUrl).then(function (network) {
cb(null, network);
}, function (err) {
cb(err);
});
});
};
// RESTRICTED
// Settings only
common.resetDrive = function (cb) {
postMessage("RESET_DRIVE", null, function (obj) {
if (obj && obj.error) { return void cb(obj.error); }
cb();
});
};
common.logoutFromAll = function (cb) {
var token = Math.floor(Math.random()*Number.MAX_SAFE_INTEGER);
localStorage.setItem(Constants.tokenKey, token);
postMessage("SET", {
key: [Constants.tokenKey],
value: token
}, function (obj) {
if (obj && obj.error) { return void cb(obj.error); }
cb();
});
};
// Settings and drive and auth
common.getUserObject = function (teamId, cb) {
postMessage("GET", {
teamId: teamId,
key: []
}, function (obj) {
cb(obj);
});
};
common.getSharedFolder = function (data, cb) {
postMessage("GET_SHARED_FOLDER", data, function (obj) {
cb(obj);
});
};
common.loadSharedFolder = function (id, data, cb) {
postMessage("LOAD_SHARED_FOLDER", {
id: id,
data: data
}, cb);
};
common.getEdPublic = function (teamId, cb) {
postMessage("GET", {
key: teamId ? ['teams', teamId, 'keys', 'drive', 'edPublic'] : ['edPublic']
}, function (obj) {
cb(obj);
});
};
// Settings and ready
common.mergeAnonDrive = function (cb) {
var data = {
anonHash: LocalStore.getFSHash()
};
postMessage("MIGRATE_ANON_DRIVE", data, cb);
};
// Settings
common.deleteAccount = function (cb) {
postMessage("DELETE_ACCOUNT", null, function (obj) {
if (obj.state) {
Feedback.send('DELETE_ACCOUNT_AUTOMATIC');
} else {
Feedback.send('DELETE_ACCOUNT_MANUAL');
}
cb(obj);
});
};
// Drive
common.userObjectCommand = function (data, cb) {
postMessage("DRIVE_USEROBJECT", data, cb);
};
common.restoreDrive = function (data, cb) {
if (data.sfId) { // Shared folder ID
postMessage('RESTORE_SHARED_FOLDER', data, cb, {
timeout: 5 * 60 * 1000
});
return;
}
postMessage("SET", {
teamId: data.teamId,
key:['drive'],
value: data.drive
}, function (obj) {
cb(obj);
}, {
timeout: 5 * 60 * 1000
});
};
common.addSharedFolder = function (teamId, secret, cb) {
var href = (secret.keys && secret.keys.editKeyStr) ? '/drive/#' + Hash.getEditHashFromKeys(secret) : undefined;
postMessage("ADD_SHARED_FOLDER", {
teamId: teamId,
path: ['root'],
folderData: {
href: href,
roHref: '/drive/#' + Hash.getViewHashFromKeys(secret),
channel: secret.channel,
password: secret.password,
ctime: +new Date()
}
}, cb);
};
common.drive = {};
common.drive.onLog = Util.mkEvent();
common.drive.onChange = Util.mkEvent();
common.drive.onRemove = Util.mkEvent();
// Profile
common.getProfileEditUrl = function (cb) {
postMessage("GET", { key: ['profile', 'edit'] }, function (obj) {
cb(obj);
});
};
common.setNewProfile = function (profile) {
postMessage("SET", {
key: ['profile'],
value: profile
}, function () {});
};
common.setAvatar = function (data, cb) {
var postData = {
key: ['profile', 'avatar']
};
// If we don't have "data", it means we want to remove the avatar and we should not have a
// "postData.value", even set to undefined (JSON.stringify transforms undefined to null)
if (data) { postData.value = data; }
postMessage("SET", postData, cb);
};
// Todo
common.getTodoHash = function (cb) {
postMessage("GET", { key: ['todo'] }, function (obj) {
cb(obj);
});
};
common.setTodoHash = function (hash) {
postMessage("SET", {
key: ['todo'],
value: hash
}, function () {});
};
// RPC
common.pinPads = function (pads, cb, teamId) {
var data = {
teamId: teamId,
pads: pads
};
postMessage("PIN_PADS", data, function (obj) {
if (obj && obj.error) { return void cb(obj.error); }
cb(null, obj.hash);
});
};
common.unpinPads = function (pads, cb, teamId) {
var data = {
teamId: teamId,
pads: pads
};
postMessage("UNPIN_PADS", data, function (obj) {
if (obj && obj.error) { return void cb(obj.error); }
cb(null, obj.hash);
});
};
common.getPinnedUsage = function (data, cb) {
postMessage("GET_PINNED_USAGE", data, function (obj) {
if (obj.error) { return void cb(obj.error); }
cb(null, obj.bytes);
});
};
common.updatePinLimit = function (cb) {
postMessage("UPDATE_PIN_LIMIT", null, function (obj) {
if (obj.error) { return void cb(obj.error); }
cb(undefined, obj.limit, obj.plan, obj.note);
});
};
common.getPinLimit = function (data, cb) {
postMessage("GET_PIN_LIMIT", data, function (obj) {
if (obj.error) { return void cb(obj.error); }
cb(undefined, obj.limit, obj.plan, obj.note);
});
};
common.isOverPinLimit = function (teamId, cb) {
if (!LocalStore.isLoggedIn()) { return void cb(null, false); }
var usage;
var andThen = function (e, limit, plan) {
if (e) { return void cb(e); }
var data = {usage: usage, limit: limit, plan: plan};
if (usage > limit) {
return void cb (null, true, data);
}
return void cb (null, false, data);
};
var todo = function (e, used) {
if (e) { return void cb(e); }
usage = used;
common.getPinLimit({
teamId: teamId
}, andThen);
};
common.getPinnedUsage({
teamId: teamId
}, todo);
};
common.clearOwnedChannel = function (channel, cb) {
postMessage("CLEAR_OWNED_CHANNEL", channel, cb);
};
// "force" allows you to delete your drive ID
common.removeOwnedChannel = function (data, cb) {
postMessage("REMOVE_OWNED_CHANNEL", data, cb);
};
common.getDeletedPads = function (data, cb) {
postMessage("GET_DELETED_PADS", data, function (obj) {
if (obj && obj.error) { return void cb(obj.error); }
cb(null, obj);
});
};
common.uploadComplete = function (teamId, id, owned, cb) {
postMessage("UPLOAD_COMPLETE", {teamId: teamId, id: id, owned: owned}, function (obj) {
if (obj && obj.error) { return void cb(obj.error); }
cb(null, obj);
});
};
common.uploadStatus = function (teamId, size, cb) {
postMessage("UPLOAD_STATUS", {teamId: teamId, size: size}, function (obj) {
if (obj && obj.error) { return void cb(obj.error); }
cb(null, obj);
});
};
common.uploadCancel = function (teamId, size, cb) {
postMessage("UPLOAD_CANCEL", {teamId: teamId, size: size}, function (obj) {
if (obj && obj.error) { return void cb(obj.error); }
cb(null, obj);
});
};
common.uploadChunk = function (teamId, data, cb) {
postMessage("UPLOAD_CHUNK", {teamId: teamId, chunk: data}, function (obj) {
if (obj && obj.error) { return void cb(obj.error); }
cb(null, obj);
});
};
common.writeLoginBlock = function (data, cb) {
postMessage('WRITE_LOGIN_BLOCK', data, function (obj) {
cb(obj);
});
};
common.removeLoginBlock = function (data, cb) {
postMessage('REMOVE_LOGIN_BLOCK', data, function (obj) {
cb(obj);
});
};
// ANON RPC
// SFRAME: talk to anon_rpc from the iframe
common.anonRpcMsg = function (msg, data, cb) {
if (!msg) { return; }
postMessage("ANON_RPC_MESSAGE", {
msg: msg,
data: data
}, function (obj) {
if (obj && obj.error) { return void cb(obj.error); }
cb(null, obj);
});
};
common.getFileSize = function (href, password, cb) {
postMessage("GET_FILE_SIZE", {href: href, password: password}, function (obj) {
if (obj && obj.error) { return void cb(obj.error); }
cb(undefined, obj.size);
});
};
common.getMultipleFileSize = function (files, cb) {
postMessage("GET_MULTIPLE_FILE_SIZE", {files:files}, function (obj) {
if (obj.error) { return void cb(obj.error); }
cb(undefined, obj.size);
});
};
common.isNewChannel = function (href, password, cb) {
postMessage('IS_NEW_CHANNEL', {href: href, password: password}, function (obj) {
if (obj.error) { return void cb(obj.error); }
if (!obj) { return void cb('INVALID_RESPONSE'); }
cb(undefined, obj.isNew);
});
};
// Store
common.getMetadata = function (cb) {
var parsed = Hash.parsePadUrl(window.location.href);
postMessage("GET_METADATA", parsed && parsed.type, function (obj) {
if (obj && obj.error) { return void cb(obj.error); }
cb(null, obj);
});
};
common.isOnlyInSharedFolder = function (data, cb) {
postMessage("IS_ONLY_IN_SHARED_FOLDER", data, function (obj) {
if (obj && obj.error) { return void cb(obj.error); }
cb(null, obj);
});
};
common.setDisplayName = function (value, cb) {
postMessage("SET_DISPLAY_NAME", value, cb);
};
common.setPadAttribute = function (attr, value, cb, href) {
cb = cb || function () {};
href = Hash.getRelativeHref(href || window.location.href);
postMessage("SET_PAD_ATTRIBUTE", {
href: href,
attr: attr,
value: value
}, function (obj) {
if (obj && obj.error) { return void cb(obj.error); }
cb();
});
};
common.getPadAttribute = function (attr, cb, href) {
href = Hash.getRelativeHref(href || window.location.href);
if (!href) {
return void cb('E404');
}
postMessage("GET_PAD_ATTRIBUTE", {
href: href,
attr: attr,
}, function (obj) {
if (obj && obj.error) { return void cb(obj.error); }
cb(null, obj);
});
};
common.setAttribute = function (attr, value, cb) {
cb = cb || function () {};
postMessage("SET_ATTRIBUTE", {
attr: attr,
value: value
}, function (obj) {
if (obj && obj.error) { return void cb(obj.error); }
cb();
});
};
common.getAttribute = function (attr, cb) {
postMessage("GET_ATTRIBUTE", {
attr: attr
}, function (obj) {
if (obj && obj.error) { return void cb(obj.error); }
cb(null, obj);
});
};
// Tags
common.resetTags = function (href, tags, cb) {
// set pad attribute
cb = cb || function () {};
if (!Array.isArray(tags)) { return void cb('INVALID_TAGS'); }
common.setPadAttribute('tags', tags.slice(), cb, href);
};
common.tagPad = function (href, tag, cb) {
if (typeof(cb) !== 'function') {
return void console.error('EXPECTED_CALLBACK');
}
if (typeof(tag) !== 'string') { return void cb('INVALID_TAG'); }
common.getPadAttribute('tags', function (e, tags) {
if (e) { return void cb(e); }
var newTags;
if (!tags) {
newTags = [tag];
} else if (tags.indexOf(tag) === -1) {
newTags = tags.slice();
newTags.push(tag);
}
common.setPadAttribute('tags', newTags, cb, href);
}, href);
};
common.untagPad = function (href, tag, cb) {
if (typeof(cb) !== 'function') {
return void console.error('EXPECTED_CALLBACK');
}
if (typeof(tag) !== 'string') { return void cb('INVALID_TAG'); }
common.getPadAttribute('tags', function (e, tags) {
if (e) { return void cb(e); }
if (!tags) { return void cb(); }
var idx = tags.indexOf(tag);
if (idx === -1) { return void cb(); }
var newTags = tags.slice();
newTags.splice(idx, 1);
common.setPadAttribute('tags', newTags, cb, href);
}, href);
};
common.getPadTags = function (href, cb) {
if (typeof(cb) !== 'function') { return; }
common.getPadAttribute('tags', function (e, tags) {
if (e) { return void cb(e); }
cb(void 0, tags ? tags.slice() : []);
}, href);
};
common.listAllTags = function (cb) {
postMessage("LIST_ALL_TAGS", null, function (obj) {
if (obj && obj.error) { return void cb(obj.error); }
cb(void 0, obj);
});
};
// STORAGE - TEMPLATES
common.listTemplates = function (type, cb) {
postMessage("GET_TEMPLATES", null, function (obj) {
if (obj && obj.error) { return void cb(obj.error); }
if (!Array.isArray(obj)) { return void cb ('NOT_AN_ARRAY'); }
if (!type) { return void cb(null, obj); }
var templates = obj.filter(function (f) {
var parsed = Hash.parsePadUrl(f.href);
return parsed.type === type;
});
cb(null, templates);
});
};
common.saveAsTemplate = function (Cryptput, data, cb) {
var p = Hash.parsePadUrl(window.location.href);
if (!p.type) { return; }
// PPP: password for the new template?
var hash = Hash.createRandomHash(p.type);
var href = '/' + p.type + '/#' + hash;
var optsPut = {};
if (p.type === 'poll') { optsPut.initialState = '{}'; }
// PPP: add password as cryptput option
Cryptput(hash, data.toSave, function (e) {
if (e) { throw new Error(e); }
postMessage("ADD_PAD", {
teamId: data.teamId,
href: href,
title: data.title,
path: ['template']
}, function (obj) {
if (obj && obj.error) { return void cb(obj.error); }
cb();
});
}, optsPut);
};
common.isTemplate = function (href, cb) {
var rhref = Hash.getRelativeHref(href);
common.listTemplates(null, function (err, templates) {
cb(void 0, templates.some(function (t) {
return t.href === rhref;
}));
});
};
common.useTemplate = function (data, Crypt, cb, optsPut) {
// opts is used to overrides options for chainpad-netflux in cryptput
// it allows us to add owners and expiration time if it is a new file
var href = data.href;
var parsed = Hash.parsePadUrl(href);
var parsed2 = Hash.parsePadUrl(window.location.href);
if(!parsed) { throw new Error("Cannot get template hash"); }
postMessage("INCREMENT_TEMPLATE_USE", href);
optsPut = optsPut || {};
var optsGet = {};
if (parsed.type === 'poll') { optsGet.initialState = '{}'; }
if (parsed2.type === 'poll') { optsPut.initialState = '{}'; }
Nthen(function (waitFor) {
if (parsed.hashData && parsed.hashData.password) {
common.getPadAttribute('password', waitFor(function (err, password) {
optsGet.password = password;
}), href);
}
if (parsed2.hashData && parsed2.hashData.password && !optsPut.password) {
common.getPadAttribute('password', waitFor(function (err, password) {
optsPut.password = password;
}));
}
}).nThen(function () {
Crypt.get(parsed.hash, function (err, val) {
if (err) { throw new Error(err); }
try {
// Try to fix the title before importing the template
var parsed = JSON.parse(val);
var meta;
if (Array.isArray(parsed) && typeof(parsed[3]) === "object") {
meta = parsed[3].metadata; // pad
} else if (parsed.info) {
meta = parsed.info; // poll
} else {
meta = parsed.metadata;
}
if (typeof(meta) === "object") {
meta.defaultTitle = meta.title || meta.defaultTitle;
meta.title = "";
delete meta.users;
delete meta.chat2;
delete meta.chat;
delete meta.cursor;
if (data.chat) { meta.chat2 = data.chat; }
if (data.cursor) { meta.cursor = data.cursor; }
}
val = JSON.stringify(parsed);
} catch (e) {
console.log("Can't fix template title", e);
}
Crypt.put(parsed2.hash, val, cb, optsPut);
}, optsGet);
});
};
common.useFile = function (Crypt, cb, optsPut) {
var fileHost = Config.fileHost || window.location.origin;
var data = common.fromFileData;
var parsed = Hash.parsePadUrl(data.href);
var parsed2 = Hash.parsePadUrl(window.location.href);
var hash = parsed.hash;
var name = data.title;
var secret = Hash.getSecrets('file', hash, data.password);
var src = fileHost + Hash.getBlobPathFromHex(secret.channel);
var key = secret.keys && secret.keys.cryptKey;
var u8;
var res;
var mode;
var val;
Nthen(function(waitFor) {
Util.fetch(src, waitFor(function (err, _u8) {
if (err) { return void waitFor.abort(); }
u8 = _u8;
}));
}).nThen(function (waitFor) {
require(["/file/file-crypto.js"], waitFor(function (FileCrypto) {
FileCrypto.decrypt(u8, key, waitFor(function (err, _res) {
if (err || !_res.content) { return void waitFor.abort(); }
res = _res;
}));
}));
}).nThen(function (waitFor) {
var ext = Util.parseFilename(data.title).ext;
if (!ext) {
mode = "text";
return;
}
require(["/common/modes.js"], waitFor(function (Modes) {
Modes.list.some(function (fType) {
if (fType.ext === ext) {
mode = fType.mode;
return true;
}
});
}));
}).nThen(function (waitFor) {
var reader = new FileReader();
reader.addEventListener('loadend', waitFor(function (e) {
val = {
content: e.srcElement.result,
highlightMode: mode,
metadata: {
defaultTitle: name,
title: name,
type: "code",
},
};
}));
reader.readAsText(res.content);
}).nThen(function () {
Crypt.put(parsed2.hash, JSON.stringify(val), cb, optsPut);
});
};
// Forget button
common.moveToTrash = function (cb, href) {
href = href || window.location.href;
postMessage("MOVE_TO_TRASH", { href: href }, cb);
};
// When opening a new pad or renaming it, store the new title
common.setPadTitle = function (data, cb) {
if (!data || typeof (data) !== "object") { return cb ('Data is not an object'); }
var href = data.href || window.location.href;
var parsed = Hash.parsePadUrl(href);
if (!parsed.hash) { return cb ('Invalid hash'); }
data.href = parsed.getUrl({present: parsed.present});
if (typeof (data.title) !== "string") { return cb('Missing title'); }
if (common.initialTeam) {
// If the value is -1, it means the user drive was selected from the pad creation screen
// If the value is a positive Integer, force save in the team with the selected ID
if (common.initialTeam !== -1) {
// Team selected from the PCS or pad created from a team drive
data.teamId = common.initialTeam;
}
data.forceSave = 1;
delete common.initialTeam;
}
if (common.initialPath) {
if (!data.path) {
data.path = Array.isArray(common.initialPath) ? common.initialPath
: decodeURIComponent(common.initialPath).split(',');
delete common.initialPath;
}
}
postMessage("SET_PAD_TITLE", data, function (obj) {
if (obj && obj.error) {
if (obj.error !== "EAUTH") { console.log("unable to set pad title"); }
return void cb(obj.error);
}
cb();
});
};
common.storeInTeam = function (data, cb) {
if (!data.href) { return void cb({error: 'EINVAL'}); }
var parsed = Hash.parsePadUrl(data.href);
var secret = Hash.getSecrets(parsed.type, parsed.hash, data.password);
if (!secret || !secret.channel) { return void cb ({error: 'EINVAL'}); }
if (parsed.type === 'drive') {
// Shared folder
var teamId = data.teamId === -1 ? undefined : data.teamId;
common.addSharedFolder(teamId, secret, cb);
return;
}
Nthen(function (waitFor) {
if (parsed.hashData.type !== 'pad') { return; }
// Set the correct owner and expiration time if we can find them
postMessage('GET_PAD_METADATA', {
channel: secret.channel
}, waitFor(function (obj) {
if (!obj || obj.error) { return; }
data.owners = obj.owners;
data.expire = +obj.expire;
}));
}).nThen(function () {
postMessage("SET_PAD_TITLE", {
teamId: data.teamId,
href: Hash.getRelativeHref(data.href),
title: data.title,
password: data.password,
channel: secret.channel,
path: data.path,
owners: data.owners,
expire: data.expire,
forceSave: 1
}, function (obj) {
if (obj && obj.error) { return void cb(obj.error); }
cb();
});
});
};
// Needed for the secure filepicker app
common.getSecureFilesList = function (query, cb) {
postMessage("GET_SECURE_FILES_LIST", query, function (list) {
cb(void 0, list);
});
};
// Get a template href from its id
common.getPadData = function (id, cb) {
postMessage("GET_PAD_DATA", id, function (data) {
cb(void 0, data);
});
};
// Admin
common.adminRpc = function (data, cb) {
postMessage("ADMIN_RPC", data, cb);
};
common.addAdminMailbox = function (data, cb) {
postMessage("ADMIN_ADD_MAILBOX", data, cb);
};
// Network
common.onNetworkDisconnect = Util.mkEvent();
common.onNetworkReconnect = Util.mkEvent();
common.onNewVersionReconnect = Util.mkEvent();
// Messaging (friend requests)
var messaging = common.messaging = {};
messaging.answerFriendRequest = function (data, cb) {
postMessage("ANSWER_FRIEND_REQUEST", data, cb);
};
messaging.sendFriendRequest = function (data, cb) {
postMessage("SEND_FRIEND_REQUEST", data, cb);
};
// Team
common.anonGetPreviewContent = function (data, cb) {
postMessage("ANON_GET_PREVIEW_CONTENT", data, cb);
};
// Onlyoffice
var onlyoffice = common.onlyoffice = {};
onlyoffice.execCommand = function (data, cb) {
postMessage("OO_COMMAND", data, cb);
};
onlyoffice.onEvent = Util.mkEvent();
// Cursor
var cursor = common.cursor = {};
cursor.execCommand = function (data, cb) {
postMessage("CURSOR_COMMAND", data, cb);
};
cursor.onEvent = Util.mkEvent();
// Mailbox
var mailbox = common.mailbox = {};
mailbox.execCommand = function (data, cb) {
postMessage("MAILBOX_COMMAND", data, cb);
};
mailbox.onEvent = Util.mkEvent();
// Universal
var universal = common.universal = {};
universal.execCommand = function (data, cb) {
postMessage("UNIVERSAL_COMMAND", data, cb);
};
universal.onEvent = Util.mkEvent();
// Pad RPC
var pad = common.padRpc = {};
pad.joinPad = function (data) {
postMessage("JOIN_PAD", data);
};
pad.leavePad = function (data, cb) {
postMessage("LEAVE_PAD", data, cb);
};
pad.sendPadMsg = function (data, cb) {
postMessage("SEND_PAD_MSG", data, cb);
};
pad.onReadyEvent = Util.mkEvent();
pad.onMessageEvent = Util.mkEvent();
pad.onJoinEvent = Util.mkEvent();
pad.onLeaveEvent = Util.mkEvent();
pad.onDisconnectEvent = Util.mkEvent();
pad.onConnectEvent = Util.mkEvent();
pad.onErrorEvent = Util.mkEvent();
pad.onMetadataEvent = Util.mkEvent();
pad.requestAccess = function (data, cb) {
postMessage("REQUEST_PAD_ACCESS", data, cb);
};
pad.giveAccess = function (data, cb) {
postMessage("GIVE_PAD_ACCESS", data, cb);
};
common.setPadMetadata = function (data, cb) {
postMessage('SET_PAD_METADATA', data, cb);
};
common.getPadMetadata = function (data, cb) {
postMessage('GET_PAD_METADATA', data, cb);
};
common.burnPad = function (data) {
postMessage('BURN_PAD', data);
};
common.changePadPassword = function (Crypt, Crypto, data, cb) {
var href = data.href;
var newPassword = data.password;
var teamId = data.teamId;
if (!href) { return void cb({ error: 'EINVAL_HREF' }); }
var parsed = Hash.parsePadUrl(href);
if (!parsed.hash) { return void cb({ error: 'EINVAL_HREF' }); }
var warning = false;
var newHash, newRoHref;
var oldChannel;
var oldSecret;
var oldMetadata;
var newSecret;
var privateData;
if (parsed.hashData.version >= 2) {
newSecret = Hash.getSecrets(parsed.type, parsed.hash, newPassword);
if (!(newSecret.keys && newSecret.keys.editKeyStr)) {
return void cb({error: 'EAUTH'});
}
newHash = Hash.getEditHashFromKeys(newSecret);
} else {
newHash = Hash.createRandomHash(parsed.type, newPassword);
newSecret = Hash.getSecrets(parsed.type, newHash, newPassword);
}
var newHref = '/' + parsed.type + '/#' + newHash;
var isSharedFolder = parsed.type === 'drive';
var optsGet = {};
var optsPut = {
password: newPassword,
metadata: {},
initialState: isSharedFolder ? '{}' : undefined
};
var cryptgetVal;
Nthen(function (waitFor) {
if (parsed.hashData && parsed.hashData.password) {
common.getPadAttribute('password', waitFor(function (err, password) {
optsGet.password = password;
}), href);
}
}).nThen(function (waitFor) {
oldSecret = Hash.getSecrets(parsed.type, parsed.hash, optsGet.password);
oldChannel = oldSecret.channel;
common.getPadMetadata({channel: oldChannel}, waitFor(function (metadata) {
oldMetadata = metadata;
}));
common.getMetadata(waitFor(function (err, data) {
if (err) {
waitFor.abort();
return void cb({ error: err });
}
privateData = data.priv;
}));
}).nThen(function (waitFor) {
// Get owners, mailbox and expiration time
var owners = oldMetadata.owners;
optsPut.metadata.owners = owners;
// Check if we're allowed to change the password
var edPublic = teamId ? (privateData.teams[teamId] || {}).edPublic : privateData.edPublic;
var isOwner = Array.isArray(owners) && edPublic && owners.indexOf(edPublic) !== -1;
if (!isOwner) {
// We're not an owner, we shouldn't be able to change the password!
waitFor.abort();
return void cb({ error: 'EPERM' });
}
var mailbox = oldMetadata.mailbox;
if (mailbox) {
// Create the encryptors to be able to decrypt and re-encrypt the mailboxes
var oldCrypto = Crypto.createEncryptor(oldSecret.keys);
var newCrypto = Crypto.createEncryptor(newSecret.keys);
var m;
if (typeof(mailbox) === "string") {
try {
m = newCrypto.encrypt(oldCrypto.decrypt(mailbox, true, true));
} catch (e) {}
} else if (mailbox && typeof(mailbox) === "object") {
m = {};
Object.keys(mailbox).forEach(function (ed) {
console.log(mailbox[ed]);
try {
m[ed] = newCrypto.encrypt(oldCrypto.decrypt(mailbox[ed], true, true));
} catch (e) {
console.error(e);
}
});
}
optsPut.metadata.mailbox = m;
}
var expire = oldMetadata.expire;
if (expire) {
optsPut.metadata.expire = (expire - (+new Date())) / 1000; // Lifetime in seconds
}
}).nThen(function (waitFor) {
Crypt.get(parsed.hash, waitFor(function (err, val) {
if (err) {
waitFor.abort();
return void cb({ error: err });
}
cryptgetVal = val;
if (isSharedFolder) {
var parsed = JSON.parse(val || '{}');
var oldKey = parsed.version === 2 && oldSecret.keys.secondaryKey;
var newKey = newSecret.keys.secondaryKey;
UserObject.reencrypt(oldKey, newKey, parsed);
cryptgetVal = JSON.stringify(parsed);
}
}), optsGet);
}).nThen(function (waitFor) {
Crypt.